Merge commit 'AU_LINUX_ANDROID_ICS.04.00.04.00.126' into msm-3.4

AU_LINUX_ANDROID_ICS.04.00.04.00.126 from msm-3.0.
First parent is from google/android-3.4.

* commit 'AU_LINUX_ANDROID_ICS.04.00.04.00.126': (8712 commits)
  PRNG: Device tree entry for qrng device.
  vidc:1080p: Set video core timeout value for Thumbnail mode
  msm: sps: improve the debugging support in SPS driver
  board-8064 msm: Overlap secure and non secure video firmware heaps.
  msm: clock: Add handoff ops for 7x30 and copper XO clocks
  msm_fb: display: Wait for external vsync before DTV IOMMU unmap
  msm: Fix ciruclar dependency in debug UART settings
  msm: gdsc: Add GDSC regulator driver for msm-copper
  defconfig: Enable Mobicore Driver.
  mobicore: Add mobicore driver.
  mobicore: rename variable to lower case.
  mobicore: rename folder.
  mobicore: add makefiles
  mobicore: initial import of kernel driver
  ASoC: msm: Add SLIMBUS_2_RX CPU DAI
  board-8064-gpio: Update FUNC for EPM SPI CS
  msm_fb: display: Remove chicken bit config during video playback
  mmc: msm_sdcc: enable the sanitize capability
  msm-fb: display: lm2 writeback support on mpq platfroms
  msm_fb: display: Disable LVDS phy & pll during panel off
  ...

Signed-off-by: Steve Muckle <smuckle@codeaurora.org>
diff --git a/drivers/Kconfig b/drivers/Kconfig
index a765f40..286a4d4 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -50,6 +50,10 @@
 
 source "drivers/spi/Kconfig"
 
+source "drivers/spmi/Kconfig"
+
+source "drivers/slimbus/Kconfig"
+
 source "drivers/hsi/Kconfig"
 
 source "drivers/pps/Kconfig"
@@ -142,4 +146,6 @@
 
 source "drivers/devfreq/Kconfig"
 
+source "drivers/gud/Kconfig"
+
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index b5d2823..bea505c 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -53,6 +53,8 @@
 obj-$(CONFIG_TARGET_CORE)	+= target/
 obj-$(CONFIG_MTD)		+= mtd/
 obj-$(CONFIG_SPI)		+= spi/
+obj-$(CONFIG_SPMI)		+= spmi/
+obj-$(CONFIG_SLIMBUS)		+= slimbus/
 obj-y				+= hsi/
 obj-y				+= net/
 obj-$(CONFIG_ATM)		+= atm/
@@ -135,3 +137,6 @@
 obj-$(CONFIG_HYPERV)		+= hv/
 
 obj-$(CONFIG_PM_DEVFREQ)	+= devfreq/
+
+#MobiCore
+obj-$(CONFIG_MOBICORE_SUPPORT)  += gud/
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index 1131dd7..4201aba 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -192,6 +192,20 @@
 	  APIs extension; the file's descriptor can then be passed on to other
 	  driver.
 
+config GENLOCK
+	bool "Enable a generic cross-process locking mechanism"
+	depends on ANON_INODES
+	help
+	  Enable a generic cross-process locking API to provide protection
+	  for shared memory objects such as graphics buffers.
+
+config GENLOCK_MISCDEVICE
+	bool "Enable a misc-device for userspace to access the genlock engine"
+	depends on GENLOCK
+	help
+	  Create a miscdevice for the purposes of allowing userspace to create
+	  and interact with locks created using genlock.
+
 config SYNC
 	bool "Synchronization framework"
 	default n
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index 0e4d3da..f81ab90 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -4,12 +4,13 @@
 			   driver.o class.o platform.o \
 			   cpu.o firmware.o init.o map.o devres.o \
 			   attribute_container.o transport_class.o \
-			   topology.o
+			   topology.o sys.o
 obj-$(CONFIG_DEVTMPFS)	+= devtmpfs.o
 obj-y			+= power/
 obj-$(CONFIG_HAS_DMA)	+= dma-mapping.o
 obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o
 obj-$(CONFIG_DMA_SHARED_BUFFER) += dma-buf.o
+obj-$(CONFIG_GENLOCK) += genlock.o
 obj-$(CONFIG_ISA)	+= isa.o
 obj-$(CONFIG_FW_LOADER)	+= firmware_class.o
 obj-$(CONFIG_NUMA)	+= node.o
diff --git a/drivers/base/base.h b/drivers/base/base.h
index 6ee17bb..f372baf 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -99,6 +99,7 @@
 static inline int hypervisor_init(void) { return 0; }
 #endif
 extern int platform_bus_init(void);
+extern int system_bus_init(void);
 extern void cpu_dev_init(void);
 
 extern int bus_add_device(struct device *dev);
diff --git a/drivers/base/core.c b/drivers/base/core.c
index e28ce98..8e01c94 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -649,6 +649,7 @@
 {
 	dev->kobj.kset = devices_kset;
 	kobject_init(&dev->kobj, &device_ktype);
+	INIT_LIST_HEAD(&dev->deferred_probe);
 	INIT_LIST_HEAD(&dev->dma_pools);
 	mutex_init(&dev->mutex);
 	lockdep_set_novalidate_class(&dev->mutex);
diff --git a/drivers/base/genlock.c b/drivers/base/genlock.c
new file mode 100644
index 0000000..5e1d7af
--- /dev/null
+++ b/drivers/base/genlock.c
@@ -0,0 +1,819 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/fb.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/file.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+#include <linux/anon_inodes.h>
+#include <linux/miscdevice.h>
+#include <linux/genlock.h>
+
+/* Lock states - can either be unlocked, held as an exclusive write lock or a
+ * shared read lock
+ */
+
+#define _UNLOCKED 0
+#define _RDLOCK  GENLOCK_RDLOCK
+#define _WRLOCK GENLOCK_WRLOCK
+
+#define GENLOCK_LOG_ERR(fmt, args...) \
+pr_err("genlock: %s: " fmt, __func__, ##args)
+
+struct genlock {
+	struct list_head active;  /* List of handles holding lock */
+	spinlock_t lock;          /* Spinlock to protect the lock internals */
+	wait_queue_head_t queue;  /* Holding pen for processes pending lock */
+	struct file *file;        /* File structure for exported lock */
+	int state;                /* Current state of the lock */
+	struct kref refcount;
+};
+
+struct genlock_handle {
+	struct genlock *lock;     /* Lock currently attached to the handle */
+	struct list_head entry;   /* List node for attaching to a lock */
+	struct file *file;        /* File structure associated with handle */
+	int active;		  /* Number of times the active lock has been
+				     taken */
+};
+
+/*
+ * Create a spinlock to protect against a race condition when a lock gets
+ * released while another process tries to attach it
+ */
+
+static DEFINE_SPINLOCK(genlock_file_lock);
+
+static void genlock_destroy(struct kref *kref)
+{
+	struct genlock *lock = container_of(kref, struct genlock,
+			refcount);
+
+	/*
+	 * Clear the private data for the file descriptor in case the fd is
+	 * still active after the lock gets released
+	 */
+
+	spin_lock(&genlock_file_lock);
+	if (lock->file)
+		lock->file->private_data = NULL;
+	spin_unlock(&genlock_file_lock);
+
+	kfree(lock);
+}
+
+/*
+ * Release the genlock object. Called when all the references to
+ * the genlock file descriptor are released
+ */
+
+static int genlock_release(struct inode *inodep, struct file *file)
+{
+	struct genlock *lock = file->private_data;
+	/*
+	 * Clear the refrence back to this file structure to avoid
+	 * somehow reusing the lock after the file has been destroyed
+	 */
+
+	if (lock)
+		lock->file = NULL;
+
+	return 0;
+}
+
+static const struct file_operations genlock_fops = {
+	.release = genlock_release,
+};
+
+/**
+ * genlock_create_lock - Create a new lock
+ * @handle - genlock handle to attach the lock to
+ *
+ * Returns: a pointer to the genlock
+ */
+
+struct genlock *genlock_create_lock(struct genlock_handle *handle)
+{
+	struct genlock *lock;
+
+	if (IS_ERR_OR_NULL(handle)) {
+		GENLOCK_LOG_ERR("Invalid handle\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	if (handle->lock != NULL) {
+		GENLOCK_LOG_ERR("Handle already has a lock attached\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	lock = kzalloc(sizeof(*lock), GFP_KERNEL);
+	if (lock == NULL) {
+		GENLOCK_LOG_ERR("Unable to allocate memory for a lock\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	INIT_LIST_HEAD(&lock->active);
+	init_waitqueue_head(&lock->queue);
+	spin_lock_init(&lock->lock);
+
+	lock->state = _UNLOCKED;
+
+	/*
+	 * Create an anonyonmous inode for the object that can exported to
+	 * other processes
+	 */
+
+	lock->file = anon_inode_getfile("genlock", &genlock_fops,
+		lock, O_RDWR);
+
+	/* Attach the new lock to the handle */
+	handle->lock = lock;
+	kref_init(&lock->refcount);
+
+	return lock;
+}
+EXPORT_SYMBOL(genlock_create_lock);
+
+/*
+ * Get a file descriptor reference to a lock suitable for sharing with
+ * other processes
+ */
+
+static int genlock_get_fd(struct genlock *lock)
+{
+	int ret;
+
+	if (!lock->file) {
+		GENLOCK_LOG_ERR("No file attached to the lock\n");
+		return -EINVAL;
+	}
+
+	ret = get_unused_fd_flags(0);
+	if (ret < 0)
+		return ret;
+	fd_install(ret, lock->file);
+	return ret;
+}
+
+/**
+ * genlock_attach_lock - Attach an existing lock to a handle
+ * @handle - Pointer to a genlock handle to attach the lock to
+ * @fd - file descriptor for the exported lock
+ *
+ * Returns: A pointer to the attached lock structure
+ */
+
+struct genlock *genlock_attach_lock(struct genlock_handle *handle, int fd)
+{
+	struct file *file;
+	struct genlock *lock;
+
+	if (IS_ERR_OR_NULL(handle)) {
+		GENLOCK_LOG_ERR("Invalid handle\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	if (handle->lock != NULL) {
+		GENLOCK_LOG_ERR("Handle already has a lock attached\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	file = fget(fd);
+	if (file == NULL) {
+		GENLOCK_LOG_ERR("Bad file descriptor\n");
+		return ERR_PTR(-EBADF);
+	}
+
+	/*
+	 * take a spinlock to avoid a race condition if the lock is
+	 * released and then attached
+	 */
+
+	spin_lock(&genlock_file_lock);
+	lock = file->private_data;
+	spin_unlock(&genlock_file_lock);
+
+	fput(file);
+
+	if (lock == NULL) {
+		GENLOCK_LOG_ERR("File descriptor is invalid\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	handle->lock = lock;
+	kref_get(&lock->refcount);
+
+	return lock;
+}
+EXPORT_SYMBOL(genlock_attach_lock);
+
+/* Helper function that returns 1 if the specified handle holds the lock */
+
+static int handle_has_lock(struct genlock *lock, struct genlock_handle *handle)
+{
+	struct genlock_handle *h;
+
+	list_for_each_entry(h, &lock->active, entry) {
+		if (h == handle)
+			return 1;
+	}
+
+	return 0;
+}
+
+/* If the lock just became available, signal the next entity waiting for it */
+
+static void _genlock_signal(struct genlock *lock)
+{
+	if (list_empty(&lock->active)) {
+		/* If the list is empty, then the lock is free */
+		lock->state = _UNLOCKED;
+		/* Wake up the first process sitting in the queue */
+		wake_up(&lock->queue);
+	}
+}
+
+/* Attempt to release the handle's ownership of the lock */
+
+static int _genlock_unlock(struct genlock *lock, struct genlock_handle *handle)
+{
+	int ret = -EINVAL;
+	unsigned long irqflags;
+
+	spin_lock_irqsave(&lock->lock, irqflags);
+
+	if (lock->state == _UNLOCKED) {
+		GENLOCK_LOG_ERR("Trying to unlock an unlocked handle\n");
+		goto done;
+	}
+
+	/* Make sure this handle is an owner of the lock */
+	if (!handle_has_lock(lock, handle)) {
+		GENLOCK_LOG_ERR("handle does not have lock attached to it\n");
+		goto done;
+	}
+	/* If the handle holds no more references to the lock then
+	   release it (maybe) */
+
+	if (--handle->active == 0) {
+		list_del(&handle->entry);
+		_genlock_signal(lock);
+	}
+
+	ret = 0;
+
+done:
+	spin_unlock_irqrestore(&lock->lock, irqflags);
+	return ret;
+}
+
+/* Attempt to acquire the lock for the handle */
+
+static int _genlock_lock(struct genlock *lock, struct genlock_handle *handle,
+	int op, int flags, uint32_t timeout)
+{
+	unsigned long irqflags;
+	int ret = 0;
+	unsigned long ticks = msecs_to_jiffies(timeout);
+
+	spin_lock_irqsave(&lock->lock, irqflags);
+
+	/* Sanity check - no blocking locks in a debug context. Even if it
+	 * succeed to not block, the mere idea is too dangerous to continue
+	 */
+
+	if (in_interrupt() && !(flags & GENLOCK_NOBLOCK))
+		BUG();
+
+	/* Fast path - the lock is unlocked, so go do the needful */
+
+	if (lock->state == _UNLOCKED)
+		goto dolock;
+
+	if (handle_has_lock(lock, handle)) {
+
+		/*
+		 * If the handle already holds the lock and the lock type is
+		 * a read lock then just increment the active pointer. This
+		 * allows the handle to do recursive read locks. Recursive
+		 * write locks are not allowed in order to support
+		 * synchronization within a process using a single gralloc
+		 * handle.
+		 */
+
+		if (lock->state == _RDLOCK && op == _RDLOCK) {
+			handle->active++;
+			goto done;
+		}
+
+		/*
+		 * If the handle holds a write lock then the owner can switch
+		 * to a read lock if they want. Do the transition atomically
+		 * then wake up any pending waiters in case they want a read
+		 * lock too. In order to support synchronization within a
+		 * process the caller must explicity request to convert the
+		 * lock type with the GENLOCK_WRITE_TO_READ flag.
+		 */
+
+		if (flags & GENLOCK_WRITE_TO_READ) {
+			if (lock->state == _WRLOCK && op == _RDLOCK) {
+				lock->state = _RDLOCK;
+				wake_up(&lock->queue);
+				goto done;
+			} else {
+				GENLOCK_LOG_ERR("Invalid state to convert"
+					"write to read\n");
+				ret = -EINVAL;
+				goto done;
+			}
+		}
+	} else {
+
+		/*
+		 * Check to ensure the caller has not attempted to convert a
+		 * write to a read without holding the lock.
+		 */
+
+		if (flags & GENLOCK_WRITE_TO_READ) {
+			GENLOCK_LOG_ERR("Handle must have lock to convert"
+				"write to read\n");
+			ret = -EINVAL;
+			goto done;
+		}
+
+		/*
+		 * If we request a read and the lock is held by a read, then go
+		 * ahead and share the lock
+		 */
+
+		if (op == GENLOCK_RDLOCK && lock->state == _RDLOCK)
+			goto dolock;
+	}
+
+	/* Treat timeout 0 just like a NOBLOCK flag and return if the
+	   lock cannot be aquired without blocking */
+
+	if (flags & GENLOCK_NOBLOCK || timeout == 0) {
+		ret = -EAGAIN;
+		goto done;
+	}
+
+	/*
+	 * Wait while the lock remains in an incompatible state
+	 * state    op    wait
+	 * -------------------
+	 * unlocked n/a   no
+	 * read     read  no
+	 * read     write yes
+	 * write    n/a   yes
+	 */
+
+	while ((lock->state == _RDLOCK && op == _WRLOCK) ||
+			lock->state == _WRLOCK) {
+		signed long elapsed;
+
+		spin_unlock_irqrestore(&lock->lock, irqflags);
+
+		elapsed = wait_event_interruptible_timeout(lock->queue,
+			lock->state == _UNLOCKED ||
+			(lock->state == _RDLOCK && op == _RDLOCK),
+			ticks);
+
+		spin_lock_irqsave(&lock->lock, irqflags);
+
+		if (elapsed <= 0) {
+			ret = (elapsed < 0) ? elapsed : -ETIMEDOUT;
+			goto done;
+		}
+
+		ticks = (unsigned long) elapsed;
+	}
+
+dolock:
+	/* We can now get the lock, add ourselves to the list of owners */
+
+	list_add_tail(&handle->entry, &lock->active);
+	lock->state = op;
+	handle->active++;
+
+done:
+	spin_unlock_irqrestore(&lock->lock, irqflags);
+	return ret;
+
+}
+
+/**
+ * genlock_lock - Acquire or release a lock (depreciated)
+ * @handle - pointer to the genlock handle that is requesting the lock
+ * @op - the operation to perform (RDLOCK, WRLOCK, UNLOCK)
+ * @flags - flags to control the operation
+ * @timeout - optional timeout to wait for the lock to come free
+ *
+ * Returns: 0 on success or error code on failure
+ */
+
+int genlock_lock(struct genlock_handle *handle, int op, int flags,
+	uint32_t timeout)
+{
+	struct genlock *lock;
+	unsigned long irqflags;
+
+	int ret = 0;
+
+	if (IS_ERR_OR_NULL(handle)) {
+		GENLOCK_LOG_ERR("Invalid handle\n");
+		return -EINVAL;
+	}
+
+	lock = handle->lock;
+
+	if (lock == NULL) {
+		GENLOCK_LOG_ERR("Handle does not have a lock attached\n");
+		return -EINVAL;
+	}
+
+	switch (op) {
+	case GENLOCK_UNLOCK:
+		ret = _genlock_unlock(lock, handle);
+		break;
+	case GENLOCK_RDLOCK:
+		spin_lock_irqsave(&lock->lock, irqflags);
+		if (handle_has_lock(lock, handle)) {
+			/* request the WRITE_TO_READ flag for compatibility */
+			flags |= GENLOCK_WRITE_TO_READ;
+		}
+		spin_unlock_irqrestore(&lock->lock, irqflags);
+		/* fall through to take lock */
+	case GENLOCK_WRLOCK:
+		ret = _genlock_lock(lock, handle, op, flags, timeout);
+		break;
+	default:
+		GENLOCK_LOG_ERR("Invalid lock operation\n");
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(genlock_lock);
+
+/**
+ * genlock_dreadlock - Acquire or release a lock
+ * @handle - pointer to the genlock handle that is requesting the lock
+ * @op - the operation to perform (RDLOCK, WRLOCK, UNLOCK)
+ * @flags - flags to control the operation
+ * @timeout - optional timeout to wait for the lock to come free
+ *
+ * Returns: 0 on success or error code on failure
+ */
+
+int genlock_dreadlock(struct genlock_handle *handle, int op, int flags,
+	uint32_t timeout)
+{
+	struct genlock *lock;
+
+	int ret = 0;
+
+	if (IS_ERR_OR_NULL(handle)) {
+		GENLOCK_LOG_ERR("Invalid handle\n");
+		return -EINVAL;
+	}
+
+	lock = handle->lock;
+
+	if (lock == NULL) {
+		GENLOCK_LOG_ERR("Handle does not have a lock attached\n");
+		return -EINVAL;
+	}
+
+	switch (op) {
+	case GENLOCK_UNLOCK:
+		ret = _genlock_unlock(lock, handle);
+		break;
+	case GENLOCK_RDLOCK:
+	case GENLOCK_WRLOCK:
+		ret = _genlock_lock(lock, handle, op, flags, timeout);
+		break;
+	default:
+		GENLOCK_LOG_ERR("Invalid lock operation\n");
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(genlock_dreadlock);
+
+/**
+ * genlock_wait - Wait for the lock to be released
+ * @handle - pointer to the genlock handle that is waiting for the lock
+ * @timeout - optional timeout to wait for the lock to get released
+ */
+
+int genlock_wait(struct genlock_handle *handle, uint32_t timeout)
+{
+	struct genlock *lock;
+	unsigned long irqflags;
+	int ret = 0;
+	unsigned long ticks = msecs_to_jiffies(timeout);
+
+	if (IS_ERR_OR_NULL(handle)) {
+		GENLOCK_LOG_ERR("Invalid handle\n");
+		return -EINVAL;
+	}
+
+	lock = handle->lock;
+
+	if (lock == NULL) {
+		GENLOCK_LOG_ERR("Handle does not have a lock attached\n");
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&lock->lock, irqflags);
+
+	/*
+	 * if timeout is 0 and the lock is already unlocked, then success
+	 * otherwise return -EAGAIN
+	 */
+
+	if (timeout == 0) {
+		ret = (lock->state == _UNLOCKED) ? 0 : -EAGAIN;
+		goto done;
+	}
+
+	while (lock->state != _UNLOCKED) {
+		signed long elapsed;
+
+		spin_unlock_irqrestore(&lock->lock, irqflags);
+
+		elapsed = wait_event_interruptible_timeout(lock->queue,
+			lock->state == _UNLOCKED, ticks);
+
+		spin_lock_irqsave(&lock->lock, irqflags);
+
+		if (elapsed <= 0) {
+			ret = (elapsed < 0) ? elapsed : -ETIMEDOUT;
+			break;
+		}
+
+		ticks = (unsigned long) elapsed;
+	}
+
+done:
+	spin_unlock_irqrestore(&lock->lock, irqflags);
+	return ret;
+}
+
+static void genlock_release_lock(struct genlock_handle *handle)
+{
+	unsigned long flags;
+
+	if (handle == NULL || handle->lock == NULL)
+		return;
+
+	spin_lock_irqsave(&handle->lock->lock, flags);
+
+	/* If the handle is holding the lock, then force it closed */
+
+	if (handle_has_lock(handle->lock, handle)) {
+		list_del(&handle->entry);
+		_genlock_signal(handle->lock);
+	}
+	spin_unlock_irqrestore(&handle->lock->lock, flags);
+
+	kref_put(&handle->lock->refcount, genlock_destroy);
+	handle->lock = NULL;
+	handle->active = 0;
+}
+
+/*
+ * Release function called when all references to a handle are released
+ */
+
+static int genlock_handle_release(struct inode *inodep, struct file *file)
+{
+	struct genlock_handle *handle = file->private_data;
+
+	genlock_release_lock(handle);
+	kfree(handle);
+
+	return 0;
+}
+
+static const struct file_operations genlock_handle_fops = {
+	.release = genlock_handle_release
+};
+
+/*
+ * Allocate a new genlock handle
+ */
+
+static struct genlock_handle *_genlock_get_handle(void)
+{
+	struct genlock_handle *handle = kzalloc(sizeof(*handle), GFP_KERNEL);
+	if (handle == NULL) {
+		GENLOCK_LOG_ERR("Unable to allocate memory for the handle\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	return handle;
+}
+
+/**
+ * genlock_get_handle - Create a new genlock handle
+ *
+ * Returns: A pointer to a new genlock handle
+ */
+
+struct genlock_handle *genlock_get_handle(void)
+{
+	struct genlock_handle *handle = _genlock_get_handle();
+	if (IS_ERR(handle))
+		return handle;
+
+	handle->file = anon_inode_getfile("genlock-handle",
+		&genlock_handle_fops, handle, O_RDWR);
+
+	return handle;
+}
+EXPORT_SYMBOL(genlock_get_handle);
+
+/**
+ * genlock_put_handle - release a reference to a genlock handle
+ * @handle - A pointer to the handle to release
+ */
+
+void genlock_put_handle(struct genlock_handle *handle)
+{
+	if (handle)
+		fput(handle->file);
+}
+EXPORT_SYMBOL(genlock_put_handle);
+
+/**
+ * genlock_get_handle_fd - Get a handle reference from a file descriptor
+ * @fd - The file descriptor for a genlock handle
+ */
+
+struct genlock_handle *genlock_get_handle_fd(int fd)
+{
+	struct file *file = fget(fd);
+
+	if (file == NULL)
+		return ERR_PTR(-EINVAL);
+
+	return file->private_data;
+}
+EXPORT_SYMBOL(genlock_get_handle_fd);
+
+#ifdef CONFIG_GENLOCK_MISCDEVICE
+
+static long genlock_dev_ioctl(struct file *filep, unsigned int cmd,
+	unsigned long arg)
+{
+	struct genlock_lock param;
+	struct genlock_handle *handle = filep->private_data;
+	struct genlock *lock;
+	int ret;
+
+	if (IS_ERR_OR_NULL(handle))
+		return -EINVAL;
+
+	switch (cmd) {
+	case GENLOCK_IOC_NEW: {
+		lock = genlock_create_lock(handle);
+		if (IS_ERR(lock))
+			return PTR_ERR(lock);
+
+		return 0;
+	}
+	case GENLOCK_IOC_EXPORT: {
+		if (handle->lock == NULL) {
+			GENLOCK_LOG_ERR("Handle does not have a lock"
+					"attached\n");
+			return -EINVAL;
+		}
+
+		ret = genlock_get_fd(handle->lock);
+		if (ret < 0)
+			return ret;
+
+		param.fd = ret;
+
+		if (copy_to_user((void __user *) arg, &param,
+			sizeof(param)))
+			return -EFAULT;
+
+		return 0;
+		}
+	case GENLOCK_IOC_ATTACH: {
+		if (copy_from_user(&param, (void __user *) arg,
+			sizeof(param)))
+			return -EFAULT;
+
+		lock = genlock_attach_lock(handle, param.fd);
+		if (IS_ERR(lock))
+			return PTR_ERR(lock);
+
+		return 0;
+	}
+	case GENLOCK_IOC_LOCK: {
+		if (copy_from_user(&param, (void __user *) arg,
+		sizeof(param)))
+			return -EFAULT;
+
+		return genlock_lock(handle, param.op, param.flags,
+			param.timeout);
+	}
+	case GENLOCK_IOC_DREADLOCK: {
+		if (copy_from_user(&param, (void __user *) arg,
+		sizeof(param)))
+			return -EFAULT;
+
+		return genlock_dreadlock(handle, param.op, param.flags,
+			param.timeout);
+	}
+	case GENLOCK_IOC_WAIT: {
+		if (copy_from_user(&param, (void __user *) arg,
+		sizeof(param)))
+			return -EFAULT;
+
+		return genlock_wait(handle, param.timeout);
+	}
+	case GENLOCK_IOC_RELEASE: {
+		/*
+		 * Return error - this ioctl has been deprecated.
+		 * Locks should only be released when the handle is
+		 * destroyed
+		 */
+		GENLOCK_LOG_ERR("Deprecated RELEASE ioctl called\n");
+		return -EINVAL;
+	}
+	default:
+		GENLOCK_LOG_ERR("Invalid ioctl\n");
+		return -EINVAL;
+	}
+}
+
+static int genlock_dev_release(struct inode *inodep, struct file *file)
+{
+	struct genlock_handle *handle = file->private_data;
+
+	genlock_release_lock(handle);
+	kfree(handle);
+
+	return 0;
+}
+
+static int genlock_dev_open(struct inode *inodep, struct file *file)
+{
+	struct genlock_handle *handle = _genlock_get_handle();
+	if (IS_ERR(handle))
+		return PTR_ERR(handle);
+
+	handle->file = file;
+	file->private_data = handle;
+	return 0;
+}
+
+static const struct file_operations genlock_dev_fops = {
+	.open = genlock_dev_open,
+	.release = genlock_dev_release,
+	.unlocked_ioctl = genlock_dev_ioctl,
+};
+
+static struct miscdevice genlock_dev;
+
+static int genlock_dev_init(void)
+{
+	genlock_dev.minor = MISC_DYNAMIC_MINOR;
+	genlock_dev.name = "genlock";
+	genlock_dev.fops = &genlock_dev_fops;
+	genlock_dev.parent = NULL;
+
+	return misc_register(&genlock_dev);
+}
+
+static void genlock_dev_close(void)
+{
+	misc_deregister(&genlock_dev);
+}
+
+module_init(genlock_dev_init);
+module_exit(genlock_dev_close);
+
+#endif
diff --git a/drivers/base/sys.c b/drivers/base/sys.c
new file mode 100644
index 0000000..35e09f0
--- /dev/null
+++ b/drivers/base/sys.c
@@ -0,0 +1,382 @@
+/*
+ * sys.c - pseudo-bus for system 'devices' (cpus, PICs, timers, etc)
+ *
+ * Copyright (c) 2002-3 Patrick Mochel
+ *               2002-3 Open Source Development Lab
+ *
+ * This file is released under the GPLv2
+ *
+ * This exports a 'system' bus type.
+ * By default, a 'sys' bus gets added to the root of the system. There will
+ * always be core system devices. Devices can use sysdev_register() to
+ * add themselves as children of the system bus.
+ */
+
+#include <linux/sysdev.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/pm.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+
+#include "base.h"
+
+#define to_sysdev(k) container_of(k, struct sys_device, kobj)
+#define to_sysdev_attr(a) container_of(a, struct sysdev_attribute, attr)
+
+extern struct kset *system_kset;
+
+static ssize_t
+sysdev_show(struct kobject *kobj, struct attribute *attr, char *buffer)
+{
+	struct sys_device *sysdev = to_sysdev(kobj);
+	struct sysdev_attribute *sysdev_attr = to_sysdev_attr(attr);
+
+	if (sysdev_attr->show)
+		return sysdev_attr->show(sysdev, sysdev_attr, buffer);
+	return -EIO;
+}
+
+
+static ssize_t
+sysdev_store(struct kobject *kobj, struct attribute *attr,
+	     const char *buffer, size_t count)
+{
+	struct sys_device *sysdev = to_sysdev(kobj);
+	struct sysdev_attribute *sysdev_attr = to_sysdev_attr(attr);
+
+	if (sysdev_attr->store)
+		return sysdev_attr->store(sysdev, sysdev_attr, buffer, count);
+	return -EIO;
+}
+
+static const struct sysfs_ops sysfs_ops = {
+	.show	= sysdev_show,
+	.store	= sysdev_store,
+};
+
+static struct kobj_type ktype_sysdev = {
+	.sysfs_ops	= &sysfs_ops,
+};
+
+
+int sysdev_create_file(struct sys_device *s, struct sysdev_attribute *a)
+{
+	return sysfs_create_file(&s->kobj, &a->attr);
+}
+
+
+void sysdev_remove_file(struct sys_device *s, struct sysdev_attribute *a)
+{
+	sysfs_remove_file(&s->kobj, &a->attr);
+}
+
+EXPORT_SYMBOL_GPL(sysdev_create_file);
+EXPORT_SYMBOL_GPL(sysdev_remove_file);
+
+#define to_sysdev_class(k) container_of(k, struct sysdev_class, kset.kobj)
+#define to_sysdev_class_attr(a) container_of(a, \
+	struct sysdev_class_attribute, attr)
+
+static ssize_t sysdev_class_show(struct kobject *kobj, struct attribute *attr,
+				 char *buffer)
+{
+	struct sysdev_class *class = to_sysdev_class(kobj);
+	struct sysdev_class_attribute *class_attr = to_sysdev_class_attr(attr);
+
+	if (class_attr->show)
+		return class_attr->show(class, class_attr, buffer);
+	return -EIO;
+}
+
+static ssize_t sysdev_class_store(struct kobject *kobj, struct attribute *attr,
+				  const char *buffer, size_t count)
+{
+	struct sysdev_class *class = to_sysdev_class(kobj);
+	struct sysdev_class_attribute *class_attr = to_sysdev_class_attr(attr);
+
+	if (class_attr->store)
+		return class_attr->store(class, class_attr, buffer, count);
+	return -EIO;
+}
+
+static const struct sysfs_ops sysfs_class_ops = {
+	.show	= sysdev_class_show,
+	.store	= sysdev_class_store,
+};
+
+static struct kobj_type ktype_sysdev_class = {
+	.sysfs_ops	= &sysfs_class_ops,
+};
+
+int sysdev_class_create_file(struct sysdev_class *c,
+			     struct sysdev_class_attribute *a)
+{
+	return sysfs_create_file(&c->kset.kobj, &a->attr);
+}
+EXPORT_SYMBOL_GPL(sysdev_class_create_file);
+
+void sysdev_class_remove_file(struct sysdev_class *c,
+			      struct sysdev_class_attribute *a)
+{
+	sysfs_remove_file(&c->kset.kobj, &a->attr);
+}
+EXPORT_SYMBOL_GPL(sysdev_class_remove_file);
+
+int sysdev_class_register(struct sysdev_class *cls)
+{
+	int retval;
+
+	pr_debug("Registering sysdev class '%s'\n", cls->name);
+
+	INIT_LIST_HEAD(&cls->drivers);
+	memset(&cls->kset.kobj, 0x00, sizeof(struct kobject));
+	cls->kset.kobj.parent = &system_kset->kobj;
+	cls->kset.kobj.ktype = &ktype_sysdev_class;
+	cls->kset.kobj.kset = system_kset;
+
+	retval = kobject_set_name(&cls->kset.kobj, "%s", cls->name);
+	if (retval)
+		return retval;
+
+	retval = kset_register(&cls->kset);
+	if (!retval && cls->attrs)
+		retval = sysfs_create_files(&cls->kset.kobj,
+					    (const struct attribute **)cls->attrs);
+	return retval;
+}
+
+void sysdev_class_unregister(struct sysdev_class *cls)
+{
+	pr_debug("Unregistering sysdev class '%s'\n",
+		 kobject_name(&cls->kset.kobj));
+	if (cls->attrs)
+		sysfs_remove_files(&cls->kset.kobj,
+				   (const struct attribute **)cls->attrs);
+	kset_unregister(&cls->kset);
+}
+
+EXPORT_SYMBOL_GPL(sysdev_class_register);
+EXPORT_SYMBOL_GPL(sysdev_class_unregister);
+
+static DEFINE_MUTEX(sysdev_drivers_lock);
+
+/*
+ * @dev != NULL means that we're unwinding because some drv->add()
+ * failed for some reason. You need to grab sysdev_drivers_lock before
+ * calling this.
+ */
+static void __sysdev_driver_remove(struct sysdev_class *cls,
+				   struct sysdev_driver *drv,
+				   struct sys_device *from_dev)
+{
+	struct sys_device *dev = from_dev;
+
+	list_del_init(&drv->entry);
+	if (!cls)
+		return;
+
+	if (!drv->remove)
+		goto kset_put;
+
+	if (dev)
+		list_for_each_entry_continue_reverse(dev, &cls->kset.list,
+						     kobj.entry)
+			drv->remove(dev);
+	else
+		list_for_each_entry(dev, &cls->kset.list, kobj.entry)
+			drv->remove(dev);
+
+kset_put:
+	kset_put(&cls->kset);
+}
+
+/**
+ *	sysdev_driver_register - Register auxiliary driver
+ *	@cls:	Device class driver belongs to.
+ *	@drv:	Driver.
+ *
+ *	@drv is inserted into @cls->drivers to be
+ *	called on each operation on devices of that class. The refcount
+ *	of @cls is incremented.
+ */
+int sysdev_driver_register(struct sysdev_class *cls, struct sysdev_driver *drv)
+{
+	struct sys_device *dev = NULL;
+	int err = 0;
+
+	if (!cls) {
+		WARN(1, KERN_WARNING "sysdev: invalid class passed to %s!\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	/* Check whether this driver has already been added to a class. */
+	if (drv->entry.next && !list_empty(&drv->entry))
+		WARN(1, KERN_WARNING "sysdev: class %s: driver (%p) has already"
+			" been registered to a class, something is wrong, but "
+			"will forge on!\n", cls->name, drv);
+
+	mutex_lock(&sysdev_drivers_lock);
+	if (cls && kset_get(&cls->kset)) {
+		list_add_tail(&drv->entry, &cls->drivers);
+
+		/* If devices of this class already exist, tell the driver */
+		if (drv->add) {
+			list_for_each_entry(dev, &cls->kset.list, kobj.entry) {
+				err = drv->add(dev);
+				if (err)
+					goto unwind;
+			}
+		}
+	} else {
+		err = -EINVAL;
+		WARN(1, KERN_ERR "%s: invalid device class\n", __func__);
+	}
+
+	goto unlock;
+
+unwind:
+	__sysdev_driver_remove(cls, drv, dev);
+
+unlock:
+	mutex_unlock(&sysdev_drivers_lock);
+	return err;
+}
+
+/**
+ *	sysdev_driver_unregister - Remove an auxiliary driver.
+ *	@cls:	Class driver belongs to.
+ *	@drv:	Driver.
+ */
+void sysdev_driver_unregister(struct sysdev_class *cls,
+			      struct sysdev_driver *drv)
+{
+	mutex_lock(&sysdev_drivers_lock);
+	__sysdev_driver_remove(cls, drv, NULL);
+	mutex_unlock(&sysdev_drivers_lock);
+}
+EXPORT_SYMBOL_GPL(sysdev_driver_register);
+EXPORT_SYMBOL_GPL(sysdev_driver_unregister);
+
+/**
+ *	sysdev_register - add a system device to the tree
+ *	@sysdev:	device in question
+ *
+ */
+int sysdev_register(struct sys_device *sysdev)
+{
+	int error;
+	struct sysdev_class *cls = sysdev->cls;
+
+	if (!cls)
+		return -EINVAL;
+
+	pr_debug("Registering sys device of class '%s'\n",
+		 kobject_name(&cls->kset.kobj));
+
+	/* initialize the kobject to 0, in case it had previously been used */
+	memset(&sysdev->kobj, 0x00, sizeof(struct kobject));
+
+	/* Make sure the kset is set */
+	sysdev->kobj.kset = &cls->kset;
+
+	/* Register the object */
+	error = kobject_init_and_add(&sysdev->kobj, &ktype_sysdev, NULL,
+				     "%s%d", kobject_name(&cls->kset.kobj),
+				     sysdev->id);
+
+	if (!error) {
+		struct sysdev_driver *drv;
+
+		pr_debug("Registering sys device '%s'\n",
+			 kobject_name(&sysdev->kobj));
+
+		mutex_lock(&sysdev_drivers_lock);
+		/* Generic notification is implicit, because it's that
+		 * code that should have called us.
+		 */
+
+		/* Notify class auxiliary drivers */
+		list_for_each_entry(drv, &cls->drivers, entry) {
+			if (drv->add)
+				drv->add(sysdev);
+		}
+		mutex_unlock(&sysdev_drivers_lock);
+		kobject_uevent(&sysdev->kobj, KOBJ_ADD);
+	}
+
+	return error;
+}
+
+void sysdev_unregister(struct sys_device *sysdev)
+{
+	struct sysdev_driver *drv;
+
+	mutex_lock(&sysdev_drivers_lock);
+	list_for_each_entry(drv, &sysdev->cls->drivers, entry) {
+		if (drv->remove)
+			drv->remove(sysdev);
+	}
+	mutex_unlock(&sysdev_drivers_lock);
+
+	kobject_put(&sysdev->kobj);
+}
+
+EXPORT_SYMBOL_GPL(sysdev_register);
+EXPORT_SYMBOL_GPL(sysdev_unregister);
+
+#define to_ext_attr(x) container_of(x, struct sysdev_ext_attribute, attr)
+
+ssize_t sysdev_store_ulong(struct sys_device *sysdev,
+			   struct sysdev_attribute *attr,
+			   const char *buf, size_t size)
+{
+	struct sysdev_ext_attribute *ea = to_ext_attr(attr);
+	char *end;
+	unsigned long new = simple_strtoul(buf, &end, 0);
+	if (end == buf)
+		return -EINVAL;
+	*(unsigned long *)(ea->var) = new;
+	/* Always return full write size even if we didn't consume all */
+	return size;
+}
+EXPORT_SYMBOL_GPL(sysdev_store_ulong);
+
+ssize_t sysdev_show_ulong(struct sys_device *sysdev,
+			  struct sysdev_attribute *attr,
+			  char *buf)
+{
+	struct sysdev_ext_attribute *ea = to_ext_attr(attr);
+	return snprintf(buf, PAGE_SIZE, "%lx\n", *(unsigned long *)(ea->var));
+}
+EXPORT_SYMBOL_GPL(sysdev_show_ulong);
+
+ssize_t sysdev_store_int(struct sys_device *sysdev,
+			   struct sysdev_attribute *attr,
+			   const char *buf, size_t size)
+{
+	struct sysdev_ext_attribute *ea = to_ext_attr(attr);
+	char *end;
+	long new = simple_strtol(buf, &end, 0);
+	if (end == buf || new > INT_MAX || new < INT_MIN)
+		return -EINVAL;
+	*(int *)(ea->var) = new;
+	/* Always return full write size even if we didn't consume all */
+	return size;
+}
+EXPORT_SYMBOL_GPL(sysdev_store_int);
+
+ssize_t sysdev_show_int(struct sys_device *sysdev,
+			  struct sysdev_attribute *attr,
+			  char *buf)
+{
+	struct sysdev_ext_attribute *ea = to_ext_attr(attr);
+	return snprintf(buf, PAGE_SIZE, "%d\n", *(int *)(ea->var));
+}
+EXPORT_SYMBOL_GPL(sysdev_show_int);
+
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 5ccf142..ea1c27a 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -2,6 +2,16 @@
 menu "Bluetooth device drivers"
 	depends on BT
 
+config BT_HCISMD
+	tristate "HCI SMD driver"
+	help
+	  Bluetooth HCI SMD driver.
+          This driver is required if you want to use Bluetoth device with
+	  SMD interface.
+
+	  Say Y here to compile support for Bluetooth USB devices into the
+          kernel or say M to compile is as a module (hci_smd).
+
 config BT_HCIBTUSB
 	tristate "HCI USB driver"
 	depends on USB
@@ -81,6 +91,17 @@
 
 	  Say Y here to compile support for HCILL protocol.
 
+config BT_HCIUART_IBS
+	bool "HCI_IBS protocol support"
+	depends on BT_HCIUART
+	default n
+	help
+	  HCI_IBS (HCI In-Band Sleep) is a serial protocol for communication
+	  between Bluetooth device and host. This protocol is required for
+	  UART clock control for some Qualcomm Bluetooth devices.
+
+	  Say Y here to compile support for HCI_IBS protocol.
+
 config BT_HCIBCM203X
 	tristate "HCI BCM203x USB driver"
 	depends on USB
@@ -104,6 +125,14 @@
 	  Say Y here to compile support for HCI BPA10x devices into the
 	  kernel or say M to compile it as module (bpa10x).
 
+config BT_MSM_SLEEP
+	tristate "MSM Bluesleep driver"
+	depends on BT && SERIAL_MSM_HS
+	default n
+	help
+	  Bluetooth MSM bluesleep driver.
+	  This driver provides support for BTS sleep.
+
 config BT_HCIBFUSB
 	tristate "HCI BlueFRITZ! USB driver"
 	depends on USB
@@ -188,7 +217,7 @@
 	  The core driver to support Marvell Bluetooth devices.
 
 	  This driver is required if you want to support
-	  Marvell Bluetooth devices, such as 8688/8787/8797.
+	  Marvell Bluetooth devices, such as 8688/8787.
 
 	  Say Y here to compile Marvell Bluetooth driver
 	  into the kernel or say M to compile it as module.
@@ -201,12 +230,20 @@
 	  The driver for Marvell Bluetooth chipsets with SDIO interface.
 
 	  This driver is required if you want to use Marvell Bluetooth
-	  devices with SDIO interface. Currently SD8688/SD8787/SD8797
-	  chipsets are supported.
+	  devices with SDIO interface. Currently SD8688/SD8787 chipsets are
+	  supported.
 
 	  Say Y here to compile support for Marvell BT-over-SDIO driver
 	  into the kernel or say M to compile it as module.
 
+config MSM_BT_POWER
+	tristate "MSM Bluetooth Power Control"
+	depends on ARCH_MSM && RFKILL
+	default m
+	help
+	  Provides a parameter to switch on/off power from PMIC
+	  to Bluetooth device.
+
 config BT_ATH3K
 	tristate "Atheros firmware download driver"
 	depends on BT_HCIBTUSB
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index f4460f4..a20a056 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -2,6 +2,7 @@
 # Makefile for the Linux Bluetooth HCI device drivers.
 #
 
+obj-$(CONFIG_BT_HCISMD)		+= hci_smd.o
 obj-$(CONFIG_BT_HCIVHCI)	+= hci_vhci.o
 obj-$(CONFIG_BT_HCIUART)	+= hci_uart.o
 obj-$(CONFIG_BT_HCIBCM203X)	+= bcm203x.o
@@ -28,4 +29,8 @@
 hci_uart-$(CONFIG_BT_HCIUART_BCSP)	+= hci_bcsp.o
 hci_uart-$(CONFIG_BT_HCIUART_LL)	+= hci_ll.o
 hci_uart-$(CONFIG_BT_HCIUART_ATH3K)	+= hci_ath.o
+hci_uart-$(CONFIG_BT_HCIUART_IBS)	+= hci_ibs.o
 hci_uart-objs				:= $(hci_uart-y)
+obj-$(CONFIG_BT_MSM_SLEEP)              += msm_bt_sleep.o
+msm_bt_sleep-objs                       := bluesleep.o
+obj-$(CONFIG_MSM_BT_POWER)		+= bluetooth-power.o
diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c
index 57fd867..55bc2fa 100644
--- a/drivers/bluetooth/ath3k.c
+++ b/drivers/bluetooth/ath3k.c
@@ -30,7 +30,6 @@
 #include <net/bluetooth/bluetooth.h>
 
 #define VERSION "1.0"
-#define ATH3K_FIRMWARE	"ath3k-1.fw"
 
 #define ATH3K_DNLOAD				0x01
 #define ATH3K_GETSTATE				0x05
@@ -63,20 +62,12 @@
 
 	/* Atheros AR3011 with sflash firmware*/
 	{ USB_DEVICE(0x0CF3, 0x3002) },
-	{ USB_DEVICE(0x13d3, 0x3304) },
-	{ USB_DEVICE(0x0930, 0x0215) },
-	{ USB_DEVICE(0x0489, 0xE03D) },
 
 	/* Atheros AR9285 Malbec with sflash firmware */
 	{ USB_DEVICE(0x03F0, 0x311D) },
 
 	/* Atheros AR3012 with sflash firmware*/
 	{ USB_DEVICE(0x0CF3, 0x3004) },
-	{ USB_DEVICE(0x0CF3, 0x311D) },
-	{ USB_DEVICE(0x13d3, 0x3375) },
-	{ USB_DEVICE(0x04CA, 0x3005) },
-	{ USB_DEVICE(0x13d3, 0x3362) },
-	{ USB_DEVICE(0x0CF3, 0xE004) },
 
 	/* Atheros AR5BBU12 with sflash firmware */
 	{ USB_DEVICE(0x0489, 0xE02C) },
@@ -93,11 +84,6 @@
 
 	/* Atheros AR3012 with sflash firmware*/
 	{ USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 },
-	{ USB_DEVICE(0x0cf3, 0x311D), .driver_info = BTUSB_ATH3012 },
-	{ USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
-	{ USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 },
-	{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
-	{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
 
 	{ }	/* Terminating entry */
 };
@@ -117,7 +103,7 @@
 
 	pipe = usb_sndctrlpipe(udev, 0);
 
-	send_buf = kmalloc(BULK_SIZE, GFP_KERNEL);
+	send_buf = kmalloc(BULK_SIZE, GFP_ATOMIC);
 	if (!send_buf) {
 		BT_ERR("Can't allocate memory chunk for firmware");
 		return -ENOMEM;
@@ -188,7 +174,7 @@
 
 	count = firmware->size;
 
-	send_buf = kmalloc(BULK_SIZE, GFP_KERNEL);
+	send_buf = kmalloc(BULK_SIZE, GFP_ATOMIC);
 	if (!send_buf) {
 		BT_ERR("Can't allocate memory chunk for firmware");
 		return -ENOMEM;
@@ -412,15 +398,9 @@
 		return 0;
 	}
 
-	ret = request_firmware(&firmware, ATH3K_FIRMWARE, &udev->dev);
-	if (ret < 0) {
-		if (ret == -ENOENT)
-			BT_ERR("Firmware file \"%s\" not found",
-							ATH3K_FIRMWARE);
-		else
-			BT_ERR("Firmware file \"%s\" request failed (err=%d)",
-							ATH3K_FIRMWARE, ret);
-		return ret;
+	if (request_firmware(&firmware, "ath3k-1.fw", &udev->dev) < 0) {
+		BT_ERR("Error loading firmware");
+		return -EIO;
 	}
 
 	ret = ath3k_load_firmware(udev, firmware);
@@ -441,10 +421,22 @@
 	.id_table	= ath3k_table,
 };
 
-module_usb_driver(ath3k_driver);
+static int __init ath3k_init(void)
+{
+	BT_INFO("Atheros AR30xx firmware driver ver %s", VERSION);
+	return usb_register(&ath3k_driver);
+}
+
+static void __exit ath3k_exit(void)
+{
+	usb_deregister(&ath3k_driver);
+}
+
+module_init(ath3k_init);
+module_exit(ath3k_exit);
 
 MODULE_AUTHOR("Atheros Communications");
 MODULE_DESCRIPTION("Atheros AR30xx firmware driver");
 MODULE_VERSION(VERSION);
 MODULE_LICENSE("GPL");
-MODULE_FIRMWARE(ATH3K_FIRMWARE);
+MODULE_FIRMWARE("ath3k-1.fw");
diff --git a/drivers/bluetooth/bcm203x.c b/drivers/bluetooth/bcm203x.c
index 1e742a5..8b1b643 100644
--- a/drivers/bluetooth/bcm203x.c
+++ b/drivers/bluetooth/bcm203x.c
@@ -24,7 +24,6 @@
 
 #include <linux/module.h>
 
-#include <linux/atomic.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/slab.h>
@@ -66,7 +65,6 @@
 	unsigned long		state;
 
 	struct work_struct	work;
-	atomic_t		shutdown;
 
 	struct urb		*urb;
 	unsigned char		*buffer;
@@ -99,7 +97,6 @@
 
 		data->state = BCM203X_SELECT_MEMORY;
 
-		/* use workqueue to have a small delay */
 		schedule_work(&data->work);
 		break;
 
@@ -158,10 +155,7 @@
 	struct bcm203x_data *data =
 		container_of(work, struct bcm203x_data, work);
 
-	if (atomic_read(&data->shutdown))
-		return;
-
-	if (usb_submit_urb(data->urb, GFP_KERNEL) < 0)
+	if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0)
 		BT_ERR("Can't submit URB");
 }
 
@@ -249,7 +243,6 @@
 
 	usb_set_intfdata(intf, data);
 
-	/* use workqueue to have a small delay */
 	schedule_work(&data->work);
 
 	return 0;
@@ -261,9 +254,6 @@
 
 	BT_DBG("intf %p", intf);
 
-	atomic_inc(&data->shutdown);
-	cancel_work_sync(&data->work);
-
 	usb_kill_urb(data->urb);
 
 	usb_set_intfdata(intf, NULL);
@@ -281,7 +271,26 @@
 	.id_table	= bcm203x_table,
 };
 
-module_usb_driver(bcm203x_driver);
+static int __init bcm203x_init(void)
+{
+	int err;
+
+	BT_INFO("Broadcom Blutonium firmware driver ver %s", VERSION);
+
+	err = usb_register(&bcm203x_driver);
+	if (err < 0)
+		BT_ERR("Failed to register USB driver");
+
+	return err;
+}
+
+static void __exit bcm203x_exit(void)
+{
+	usb_deregister(&bcm203x_driver);
+}
+
+module_init(bcm203x_init);
+module_exit(bcm203x_exit);
 
 MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
 MODULE_DESCRIPTION("Broadcom Blutonium firmware driver ver " VERSION);
diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c
index b8ac1c5..005919a 100644
--- a/drivers/bluetooth/bfusb.c
+++ b/drivers/bluetooth/bfusb.c
@@ -411,7 +411,7 @@
 
 static int bfusb_open(struct hci_dev *hdev)
 {
-	struct bfusb_data *data = hci_get_drvdata(hdev);
+	struct bfusb_data *data = hdev->driver_data;
 	unsigned long flags;
 	int i, err;
 
@@ -437,7 +437,7 @@
 
 static int bfusb_flush(struct hci_dev *hdev)
 {
-	struct bfusb_data *data = hci_get_drvdata(hdev);
+	struct bfusb_data *data = hdev->driver_data;
 
 	BT_DBG("hdev %p bfusb %p", hdev, data);
 
@@ -448,7 +448,7 @@
 
 static int bfusb_close(struct hci_dev *hdev)
 {
-	struct bfusb_data *data = hci_get_drvdata(hdev);
+	struct bfusb_data *data = hdev->driver_data;
 	unsigned long flags;
 
 	BT_DBG("hdev %p bfusb %p", hdev, data);
@@ -483,7 +483,7 @@
 	if (!test_bit(HCI_RUNNING, &hdev->flags))
 		return -EBUSY;
 
-	data = hci_get_drvdata(hdev);
+	data = hdev->driver_data;
 
 	switch (bt_cb(skb)->pkt_type) {
 	case HCI_COMMAND_PKT:
@@ -544,6 +544,15 @@
 	return 0;
 }
 
+static void bfusb_destruct(struct hci_dev *hdev)
+{
+	struct bfusb_data *data = hdev->driver_data;
+
+	BT_DBG("hdev %p bfusb %p", hdev, data);
+
+	kfree(data);
+}
+
 static int bfusb_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
 {
 	return -ENOIOCTLCMD;
@@ -559,23 +568,22 @@
 
 	BT_INFO("BlueFRITZ! USB loading firmware");
 
-	buf = kmalloc(BFUSB_MAX_BLOCK_SIZE + 3, GFP_KERNEL);
-	if (!buf) {
-		BT_ERR("Can't allocate memory chunk for firmware");
-		return -ENOMEM;
-	}
-
 	pipe = usb_sndctrlpipe(data->udev, 0);
 
 	if (usb_control_msg(data->udev, pipe, USB_REQ_SET_CONFIGURATION,
 				0, 1, 0, NULL, 0, USB_CTRL_SET_TIMEOUT) < 0) {
 		BT_ERR("Can't change to loading configuration");
-		kfree(buf);
 		return -EBUSY;
 	}
 
 	data->udev->toggle[0] = data->udev->toggle[1] = 0;
 
+	buf = kmalloc(BFUSB_MAX_BLOCK_SIZE + 3, GFP_ATOMIC);
+	if (!buf) {
+		BT_ERR("Can't allocate memory chunk for firmware");
+		return -ENOMEM;
+	}
+
 	pipe = usb_sndbulkpipe(data->udev, data->bulk_out_ep);
 
 	while (count) {
@@ -696,15 +704,18 @@
 	data->hdev = hdev;
 
 	hdev->bus = HCI_USB;
-	hci_set_drvdata(hdev, data);
+	hdev->driver_data = data;
 	SET_HCIDEV_DEV(hdev, &intf->dev);
 
 	hdev->open     = bfusb_open;
 	hdev->close    = bfusb_close;
 	hdev->flush    = bfusb_flush;
 	hdev->send     = bfusb_send_frame;
+	hdev->destruct = bfusb_destruct;
 	hdev->ioctl    = bfusb_ioctl;
 
+	hdev->owner = THIS_MODULE;
+
 	if (hci_register_dev(hdev) < 0) {
 		BT_ERR("Can't register HCI device");
 		hci_free_dev(hdev);
@@ -739,9 +750,10 @@
 
 	bfusb_close(hdev);
 
-	hci_unregister_dev(hdev);
+	if (hci_unregister_dev(hdev) < 0)
+		BT_ERR("Can't unregister HCI device %s", hdev->name);
+
 	hci_free_dev(hdev);
-	kfree(data);
 }
 
 static struct usb_driver bfusb_driver = {
@@ -751,7 +763,26 @@
 	.id_table	= bfusb_table,
 };
 
-module_usb_driver(bfusb_driver);
+static int __init bfusb_init(void)
+{
+	int err;
+
+	BT_INFO("BlueFRITZ! USB driver ver %s", VERSION);
+
+	err = usb_register(&bfusb_driver);
+	if (err < 0)
+		BT_ERR("Failed to register BlueFRITZ! USB driver");
+
+	return err;
+}
+
+static void __exit bfusb_exit(void)
+{
+	usb_deregister(&bfusb_driver);
+}
+
+module_init(bfusb_init);
+module_exit(bfusb_exit);
 
 MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
 MODULE_DESCRIPTION("BlueFRITZ! USB driver ver " VERSION);
diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c
index 1fcd923..4104b7f 100644
--- a/drivers/bluetooth/bluecard_cs.c
+++ b/drivers/bluetooth/bluecard_cs.c
@@ -561,7 +561,7 @@
 
 static int bluecard_hci_set_baud_rate(struct hci_dev *hdev, int baud)
 {
-	bluecard_info_t *info = hci_get_drvdata(hdev);
+	bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
 	struct sk_buff *skb;
 
 	/* Ericsson baud rate command */
@@ -609,7 +609,7 @@
 
 static int bluecard_hci_flush(struct hci_dev *hdev)
 {
-	bluecard_info_t *info = hci_get_drvdata(hdev);
+	bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
 
 	/* Drop TX queue */
 	skb_queue_purge(&(info->txq));
@@ -620,7 +620,7 @@
 
 static int bluecard_hci_open(struct hci_dev *hdev)
 {
-	bluecard_info_t *info = hci_get_drvdata(hdev);
+	bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
 	unsigned int iobase = info->p_dev->resource[0]->start;
 
 	if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state)))
@@ -640,7 +640,7 @@
 
 static int bluecard_hci_close(struct hci_dev *hdev)
 {
-	bluecard_info_t *info = hci_get_drvdata(hdev);
+	bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
 	unsigned int iobase = info->p_dev->resource[0]->start;
 
 	if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
@@ -667,7 +667,7 @@
 		return -ENODEV;
 	}
 
-	info = hci_get_drvdata(hdev);
+	info = (bluecard_info_t *)(hdev->driver_data);
 
 	switch (bt_cb(skb)->pkt_type) {
 	case HCI_COMMAND_PKT:
@@ -691,6 +691,11 @@
 }
 
 
+static void bluecard_hci_destruct(struct hci_dev *hdev)
+{
+}
+
+
 static int bluecard_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
 {
 	return -ENOIOCTLCMD;
@@ -729,15 +734,18 @@
 	info->hdev = hdev;
 
 	hdev->bus = HCI_PCCARD;
-	hci_set_drvdata(hdev, info);
+	hdev->driver_data = info;
 	SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
 
 	hdev->open     = bluecard_hci_open;
 	hdev->close    = bluecard_hci_close;
 	hdev->flush    = bluecard_hci_flush;
 	hdev->send     = bluecard_hci_send_frame;
+	hdev->destruct = bluecard_hci_destruct;
 	hdev->ioctl    = bluecard_hci_ioctl;
 
+	hdev->owner = THIS_MODULE;
+
 	id = inb(iobase + 0x30);
 
 	if ((id & 0x0f) == 0x02)
@@ -836,7 +844,9 @@
 	/* Turn FPGA off */
 	outb(0x80, iobase + 0x30);
 
-	hci_unregister_dev(hdev);
+	if (hci_unregister_dev(hdev) < 0)
+		BT_ERR("Can't unregister HCI device %s", hdev->name);
+
 	hci_free_dev(hdev);
 
 	return 0;
@@ -920,7 +930,7 @@
 	pcmcia_disable_device(link);
 }
 
-static const struct pcmcia_device_id bluecard_ids[] = {
+static struct pcmcia_device_id bluecard_ids[] = {
 	PCMCIA_DEVICE_PROD_ID12("BlueCard", "LSE041", 0xbaf16fbf, 0x657cc15e),
 	PCMCIA_DEVICE_PROD_ID12("BTCFCARD", "LSE139", 0xe3987764, 0x2524b59c),
 	PCMCIA_DEVICE_PROD_ID12("WSS", "LSE039", 0x0a0736ec, 0x24e6dfab),
diff --git a/drivers/bluetooth/bluesleep.c b/drivers/bluetooth/bluesleep.c
new file mode 100644
index 0000000..0d11141
--- /dev/null
+++ b/drivers/bluetooth/bluesleep.c
@@ -0,0 +1,757 @@
+/*
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation.
+
+   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+   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.
+
+
+   Copyright (C) 2006-2007 - Motorola
+   Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
+
+   Date         Author           Comment
+   -----------  --------------   --------------------------------
+   2006-Apr-28	Motorola	 The kernel module for running the Bluetooth(R)
+				 Sleep-Mode Protocol from the Host side
+   2006-Sep-08  Motorola         Added workqueue for handling sleep work.
+   2007-Jan-24  Motorola         Added mbm_handle_ioi() call to ISR.
+
+*/
+
+#include <linux/module.h>	/* kernel module definitions */
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/notifier.h>
+#include <linux/proc_fs.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <linux/uaccess.h>
+#include <linux/version.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+
+#include <linux/irq.h>
+#include <linux/param.h>
+#include <linux/bitops.h>
+#include <linux/termios.h>
+#include <mach/gpio.h>
+#include <mach/msm_serial_hs.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h> /* event notifications */
+#include "hci_uart.h"
+
+#define BT_SLEEP_DBG
+#ifndef BT_SLEEP_DBG
+#define BT_DBG(fmt, arg...)
+#endif
+/*
+ * Defines
+ */
+
+#define VERSION		"1.1"
+#define PROC_DIR	"bluetooth/sleep"
+
+struct bluesleep_info {
+	unsigned host_wake;
+	unsigned ext_wake;
+	unsigned host_wake_irq;
+	struct uart_port *uport;
+};
+
+/* work function */
+static void bluesleep_sleep_work(struct work_struct *work);
+
+/* work queue */
+DECLARE_DELAYED_WORK(sleep_workqueue, bluesleep_sleep_work);
+
+/* Macros for handling sleep work */
+#define bluesleep_rx_busy()     schedule_delayed_work(&sleep_workqueue, 0)
+#define bluesleep_tx_busy()     schedule_delayed_work(&sleep_workqueue, 0)
+#define bluesleep_rx_idle()     schedule_delayed_work(&sleep_workqueue, 0)
+#define bluesleep_tx_idle()     schedule_delayed_work(&sleep_workqueue, 0)
+
+/* 1 second timeout */
+#define TX_TIMER_INTERVAL	1
+
+/* state variable names and bit positions */
+#define BT_PROTO	0x01
+#define BT_TXDATA	0x02
+#define BT_ASLEEP	0x04
+
+/* global pointer to a single hci device. */
+static struct hci_dev *bluesleep_hdev;
+
+static struct bluesleep_info *bsi;
+
+/* module usage */
+static atomic_t open_count = ATOMIC_INIT(1);
+
+/*
+ * Local function prototypes
+ */
+
+static int bluesleep_hci_event(struct notifier_block *this,
+			    unsigned long event, void *data);
+
+/*
+ * Global variables
+ */
+
+/** Global state flags */
+static unsigned long flags;
+
+/** Tasklet to respond to change in hostwake line */
+static struct tasklet_struct hostwake_task;
+
+/** Transmission timer */
+static struct timer_list tx_timer;
+
+/** Lock for state transitions */
+static spinlock_t rw_lock;
+
+/** Notifier block for HCI events */
+struct notifier_block hci_event_nblock = {
+	.notifier_call = bluesleep_hci_event,
+};
+
+struct proc_dir_entry *bluetooth_dir, *sleep_dir;
+
+/*
+ * Local functions
+ */
+
+static void hsuart_power(int on)
+{
+	if (on) {
+		msm_hs_request_clock_on(bsi->uport);
+		msm_hs_set_mctrl(bsi->uport, TIOCM_RTS);
+	} else {
+		msm_hs_set_mctrl(bsi->uport, 0);
+		msm_hs_request_clock_off(bsi->uport);
+	}
+}
+
+
+/**
+ * @return 1 if the Host can go to sleep, 0 otherwise.
+ */
+static inline int bluesleep_can_sleep(void)
+{
+	/* check if MSM_WAKE_BT_GPIO and BT_WAKE_MSM_GPIO are both deasserted */
+	return gpio_get_value(bsi->ext_wake) &&
+		gpio_get_value(bsi->host_wake) &&
+		(bsi->uport != NULL);
+}
+
+void bluesleep_sleep_wakeup(void)
+{
+	if (test_bit(BT_ASLEEP, &flags)) {
+		BT_DBG("waking up...");
+		/* Start the timer */
+		mod_timer(&tx_timer, jiffies + (TX_TIMER_INTERVAL * HZ));
+		gpio_set_value(bsi->ext_wake, 0);
+		clear_bit(BT_ASLEEP, &flags);
+		/*Activating UART */
+		hsuart_power(1);
+	}
+}
+
+/**
+ * @brief@  main sleep work handling function which update the flags
+ * and activate and deactivate UART ,check FIFO.
+ */
+static void bluesleep_sleep_work(struct work_struct *work)
+{
+	if (bluesleep_can_sleep()) {
+		/* already asleep, this is an error case */
+		if (test_bit(BT_ASLEEP, &flags)) {
+			BT_DBG("already asleep");
+			return;
+		}
+
+		if (msm_hs_tx_empty(bsi->uport)) {
+			BT_DBG("going to sleep...");
+			set_bit(BT_ASLEEP, &flags);
+			/*Deactivating UART */
+			hsuart_power(0);
+		} else {
+
+		  mod_timer(&tx_timer, jiffies + (TX_TIMER_INTERVAL * HZ));
+			return;
+		}
+	} else {
+		bluesleep_sleep_wakeup();
+	}
+}
+
+/**
+ * A tasklet function that runs in tasklet context and reads the value
+ * of the HOST_WAKE GPIO pin and further defer the work.
+ * @param data Not used.
+ */
+static void bluesleep_hostwake_task(unsigned long data)
+{
+	BT_DBG("hostwake line change");
+
+	spin_lock(&rw_lock);
+
+	if (gpio_get_value(bsi->host_wake))
+		bluesleep_rx_busy();
+	else
+		bluesleep_rx_idle();
+
+	spin_unlock(&rw_lock);
+}
+
+/**
+ * Handles proper timer action when outgoing data is delivered to the
+ * HCI line discipline. Sets BT_TXDATA.
+ */
+static void bluesleep_outgoing_data(void)
+{
+	unsigned long irq_flags;
+
+	spin_lock_irqsave(&rw_lock, irq_flags);
+
+	/* log data passing by */
+	set_bit(BT_TXDATA, &flags);
+
+	/* if the tx side is sleeping... */
+	if (gpio_get_value(bsi->ext_wake)) {
+
+		BT_DBG("tx was sleeping");
+		bluesleep_sleep_wakeup();
+	}
+
+	spin_unlock_irqrestore(&rw_lock, irq_flags);
+}
+
+/**
+ * Handles HCI device events.
+ * @param this Not used.
+ * @param event The event that occurred.
+ * @param data The HCI device associated with the event.
+ * @return <code>NOTIFY_DONE</code>.
+ */
+static int bluesleep_hci_event(struct notifier_block *this,
+				unsigned long event, void *data)
+{
+	struct hci_dev *hdev = (struct hci_dev *) data;
+	struct hci_uart *hu;
+	struct uart_state *state;
+
+	if (!hdev)
+		return NOTIFY_DONE;
+
+	switch (event) {
+	case HCI_DEV_REG:
+		if (!bluesleep_hdev) {
+			bluesleep_hdev = hdev;
+			hu  = (struct hci_uart *) hdev->driver_data;
+			state = (struct uart_state *) hu->tty->driver_data;
+			bsi->uport = state->uart_port;
+		}
+		break;
+	case HCI_DEV_UNREG:
+		bluesleep_hdev = NULL;
+		bsi->uport = NULL;
+		break;
+	case HCI_DEV_WRITE:
+		bluesleep_outgoing_data();
+		break;
+	}
+
+	return NOTIFY_DONE;
+}
+
+/**
+ * Handles transmission timer expiration.
+ * @param data Not used.
+ */
+static void bluesleep_tx_timer_expire(unsigned long data)
+{
+	unsigned long irq_flags;
+
+	spin_lock_irqsave(&rw_lock, irq_flags);
+
+	BT_DBG("Tx timer expired");
+
+	/* were we silent during the last timeout? */
+	if (!test_bit(BT_TXDATA, &flags)) {
+		BT_DBG("Tx has been idle");
+		gpio_set_value(bsi->ext_wake, 1);
+		bluesleep_tx_idle();
+	} else {
+		BT_DBG("Tx data during last period");
+		mod_timer(&tx_timer, jiffies + (TX_TIMER_INTERVAL*HZ));
+	}
+
+	/* clear the incoming data flag */
+	clear_bit(BT_TXDATA, &flags);
+
+	spin_unlock_irqrestore(&rw_lock, irq_flags);
+}
+
+/**
+ * Schedules a tasklet to run when receiving an interrupt on the
+ * <code>HOST_WAKE</code> GPIO pin.
+ * @param irq Not used.
+ * @param dev_id Not used.
+ */
+static irqreturn_t bluesleep_hostwake_isr(int irq, void *dev_id)
+{
+	/* schedule a tasklet to handle the change in the host wake line */
+	tasklet_schedule(&hostwake_task);
+	return IRQ_HANDLED;
+}
+
+/**
+ * Starts the Sleep-Mode Protocol on the Host.
+ * @return On success, 0. On error, -1, and <code>errno</code> is set
+ * appropriately.
+ */
+static int bluesleep_start(void)
+{
+	int retval;
+	unsigned long irq_flags;
+
+	spin_lock_irqsave(&rw_lock, irq_flags);
+
+	if (test_bit(BT_PROTO, &flags)) {
+		spin_unlock_irqrestore(&rw_lock, irq_flags);
+		return 0;
+	}
+
+	spin_unlock_irqrestore(&rw_lock, irq_flags);
+
+	if (!atomic_dec_and_test(&open_count)) {
+		atomic_inc(&open_count);
+		return -EBUSY;
+	}
+
+	/* start the timer */
+
+	mod_timer(&tx_timer, jiffies + (TX_TIMER_INTERVAL*HZ));
+
+	/* assert BT_WAKE */
+	gpio_set_value(bsi->ext_wake, 0);
+	retval = request_irq(bsi->host_wake_irq, bluesleep_hostwake_isr,
+				IRQF_DISABLED | IRQF_TRIGGER_FALLING,
+				"bluetooth hostwake", NULL);
+	if (retval  < 0) {
+		BT_ERR("Couldn't acquire BT_HOST_WAKE IRQ");
+		goto fail;
+	}
+
+	retval = enable_irq_wake(bsi->host_wake_irq);
+	if (retval < 0) {
+		BT_ERR("Couldn't enable BT_HOST_WAKE as wakeup interrupt");
+		free_irq(bsi->host_wake_irq, NULL);
+		goto fail;
+	}
+
+	set_bit(BT_PROTO, &flags);
+	return 0;
+fail:
+	del_timer(&tx_timer);
+	atomic_inc(&open_count);
+
+	return retval;
+}
+
+/**
+ * Stops the Sleep-Mode Protocol on the Host.
+ */
+static void bluesleep_stop(void)
+{
+	unsigned long irq_flags;
+
+	spin_lock_irqsave(&rw_lock, irq_flags);
+
+	if (!test_bit(BT_PROTO, &flags)) {
+		spin_unlock_irqrestore(&rw_lock, irq_flags);
+		return;
+	}
+
+	/* assert BT_WAKE */
+	gpio_set_value(bsi->ext_wake, 0);
+	del_timer(&tx_timer);
+	clear_bit(BT_PROTO, &flags);
+
+	if (test_bit(BT_ASLEEP, &flags)) {
+		clear_bit(BT_ASLEEP, &flags);
+		hsuart_power(1);
+	}
+
+	atomic_inc(&open_count);
+
+	spin_unlock_irqrestore(&rw_lock, irq_flags);
+	if (disable_irq_wake(bsi->host_wake_irq))
+		BT_ERR("Couldn't disable hostwake IRQ wakeup mode\n");
+	free_irq(bsi->host_wake_irq, NULL);
+}
+/**
+ * Read the <code>BT_WAKE</code> GPIO pin value via the proc interface.
+ * When this function returns, <code>page</code> will contain a 1 if the
+ * pin is high, 0 otherwise.
+ * @param page Buffer for writing data.
+ * @param start Not used.
+ * @param offset Not used.
+ * @param count Not used.
+ * @param eof Whether or not there is more data to be read.
+ * @param data Not used.
+ * @return The number of bytes written.
+ */
+static int bluepower_read_proc_btwake(char *page, char **start, off_t offset,
+					int count, int *eof, void *data)
+{
+	*eof = 1;
+	return sprintf(page, "btwake:%u\n", gpio_get_value(bsi->ext_wake));
+}
+
+/**
+ * Write the <code>BT_WAKE</code> GPIO pin value via the proc interface.
+ * @param file Not used.
+ * @param buffer The buffer to read from.
+ * @param count The number of bytes to be written.
+ * @param data Not used.
+ * @return On success, the number of bytes written. On error, -1, and
+ * <code>errno</code> is set appropriately.
+ */
+static int bluepower_write_proc_btwake(struct file *file, const char *buffer,
+					unsigned long count, void *data)
+{
+	char *buf;
+
+	if (count < 1)
+		return -EINVAL;
+
+	buf = kmalloc(count, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	if (copy_from_user(buf, buffer, count)) {
+		kfree(buf);
+		return -EFAULT;
+	}
+
+	if (buf[0] == '0') {
+		gpio_set_value(bsi->ext_wake, 0);
+	} else if (buf[0] == '1') {
+		gpio_set_value(bsi->ext_wake, 1);
+	} else {
+		kfree(buf);
+		return -EINVAL;
+	}
+
+	kfree(buf);
+	return count;
+}
+
+/**
+ * Read the <code>BT_HOST_WAKE</code> GPIO pin value via the proc interface.
+ * When this function returns, <code>page</code> will contain a 1 if the pin
+ * is high, 0 otherwise.
+ * @param page Buffer for writing data.
+ * @param start Not used.
+ * @param offset Not used.
+ * @param count Not used.
+ * @param eof Whether or not there is more data to be read.
+ * @param data Not used.
+ * @return The number of bytes written.
+ */
+static int bluepower_read_proc_hostwake(char *page, char **start, off_t offset,
+					int count, int *eof, void *data)
+{
+	*eof = 1;
+	return sprintf(page, "hostwake: %u \n", gpio_get_value(bsi->host_wake));
+}
+
+
+/**
+ * Read the low-power status of the Host via the proc interface.
+ * When this function returns, <code>page</code> contains a 1 if the Host
+ * is asleep, 0 otherwise.
+ * @param page Buffer for writing data.
+ * @param start Not used.
+ * @param offset Not used.
+ * @param count Not used.
+ * @param eof Whether or not there is more data to be read.
+ * @param data Not used.
+ * @return The number of bytes written.
+ */
+static int bluesleep_read_proc_asleep(char *page, char **start, off_t offset,
+					int count, int *eof, void *data)
+{
+	unsigned int asleep;
+
+	asleep = test_bit(BT_ASLEEP, &flags) ? 1 : 0;
+	*eof = 1;
+	return sprintf(page, "asleep: %u\n", asleep);
+}
+
+/**
+ * Read the low-power protocol being used by the Host via the proc interface.
+ * When this function returns, <code>page</code> will contain a 1 if the Host
+ * is using the Sleep Mode Protocol, 0 otherwise.
+ * @param page Buffer for writing data.
+ * @param start Not used.
+ * @param offset Not used.
+ * @param count Not used.
+ * @param eof Whether or not there is more data to be read.
+ * @param data Not used.
+ * @return The number of bytes written.
+ */
+static int bluesleep_read_proc_proto(char *page, char **start, off_t offset,
+					int count, int *eof, void *data)
+{
+	unsigned int proto;
+
+	proto = test_bit(BT_PROTO, &flags) ? 1 : 0;
+	*eof = 1;
+	return sprintf(page, "proto: %u\n", proto);
+}
+
+/**
+ * Modify the low-power protocol used by the Host via the proc interface.
+ * @param file Not used.
+ * @param buffer The buffer to read from.
+ * @param count The number of bytes to be written.
+ * @param data Not used.
+ * @return On success, the number of bytes written. On error, -1, and
+ * <code>errno</code> is set appropriately.
+ */
+static int bluesleep_write_proc_proto(struct file *file, const char *buffer,
+					unsigned long count, void *data)
+{
+	char proto;
+
+	if (count < 1)
+		return -EINVAL;
+
+	if (copy_from_user(&proto, buffer, 1))
+		return -EFAULT;
+
+	if (proto == '0')
+		bluesleep_stop();
+	else
+		bluesleep_start();
+
+	/* claim that we wrote everything */
+	return count;
+}
+
+static int __init bluesleep_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct resource *res;
+
+	bsi = kzalloc(sizeof(struct bluesleep_info), GFP_KERNEL);
+	if (!bsi)
+		return -ENOMEM;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IO,
+				"gpio_host_wake");
+	if (!res) {
+		BT_ERR("couldn't find host_wake gpio\n");
+		ret = -ENODEV;
+		goto free_bsi;
+	}
+	bsi->host_wake = res->start;
+
+	ret = gpio_request(bsi->host_wake, "bt_host_wake");
+	if (ret)
+		goto free_bsi;
+	ret = gpio_direction_input(bsi->host_wake);
+	if (ret)
+		goto free_bt_host_wake;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IO,
+				"gpio_ext_wake");
+	if (!res) {
+		BT_ERR("couldn't find ext_wake gpio\n");
+		ret = -ENODEV;
+		goto free_bt_host_wake;
+	}
+	bsi->ext_wake = res->start;
+
+	ret = gpio_request(bsi->ext_wake, "bt_ext_wake");
+	if (ret)
+		goto free_bt_host_wake;
+	/* assert bt wake */
+	ret = gpio_direction_output(bsi->ext_wake, 0);
+	if (ret)
+		goto free_bt_ext_wake;
+
+	bsi->host_wake_irq = platform_get_irq_byname(pdev, "host_wake");
+	if (bsi->host_wake_irq < 0) {
+		BT_ERR("couldn't find host_wake irq\n");
+		ret = -ENODEV;
+		goto free_bt_ext_wake;
+	}
+
+
+	return 0;
+
+free_bt_ext_wake:
+	gpio_free(bsi->ext_wake);
+free_bt_host_wake:
+	gpio_free(bsi->host_wake);
+free_bsi:
+	kfree(bsi);
+	return ret;
+}
+
+static int bluesleep_remove(struct platform_device *pdev)
+{
+	/* assert bt wake */
+	gpio_set_value(bsi->ext_wake, 0);
+	if (test_bit(BT_PROTO, &flags)) {
+		if (disable_irq_wake(bsi->host_wake_irq))
+			BT_ERR("Couldn't disable hostwake IRQ wakeup mode \n");
+		free_irq(bsi->host_wake_irq, NULL);
+		del_timer(&tx_timer);
+		if (test_bit(BT_ASLEEP, &flags))
+			hsuart_power(1);
+	}
+
+	gpio_free(bsi->host_wake);
+	gpio_free(bsi->ext_wake);
+	kfree(bsi);
+	return 0;
+}
+
+static struct platform_driver bluesleep_driver = {
+	.remove = bluesleep_remove,
+	.driver = {
+		.name = "bluesleep",
+		.owner = THIS_MODULE,
+	},
+};
+/**
+ * Initializes the module.
+ * @return On success, 0. On error, -1, and <code>errno</code> is set
+ * appropriately.
+ */
+static int __init bluesleep_init(void)
+{
+	int retval;
+	struct proc_dir_entry *ent;
+
+	BT_INFO("MSM Sleep Mode Driver Ver %s", VERSION);
+
+	retval = platform_driver_probe(&bluesleep_driver, bluesleep_probe);
+	if (retval)
+		return retval;
+
+	bluesleep_hdev = NULL;
+
+	bluetooth_dir = proc_mkdir("bluetooth", NULL);
+	if (bluetooth_dir == NULL) {
+		BT_ERR("Unable to create /proc/bluetooth directory");
+		return -ENOMEM;
+	}
+
+	sleep_dir = proc_mkdir("sleep", bluetooth_dir);
+	if (sleep_dir == NULL) {
+		BT_ERR("Unable to create /proc/%s directory", PROC_DIR);
+		return -ENOMEM;
+	}
+
+	/* Creating read/write "btwake" entry */
+	ent = create_proc_entry("btwake", 0, sleep_dir);
+	if (ent == NULL) {
+		BT_ERR("Unable to create /proc/%s/btwake entry", PROC_DIR);
+		retval = -ENOMEM;
+		goto fail;
+	}
+	ent->read_proc = bluepower_read_proc_btwake;
+	ent->write_proc = bluepower_write_proc_btwake;
+
+	/* read only proc entries */
+	if (create_proc_read_entry("hostwake", 0, sleep_dir,
+				bluepower_read_proc_hostwake, NULL) == NULL) {
+		BT_ERR("Unable to create /proc/%s/hostwake entry", PROC_DIR);
+		retval = -ENOMEM;
+		goto fail;
+	}
+
+	/* read/write proc entries */
+	ent = create_proc_entry("proto", 0, sleep_dir);
+	if (ent == NULL) {
+		BT_ERR("Unable to create /proc/%s/proto entry", PROC_DIR);
+		retval = -ENOMEM;
+		goto fail;
+	}
+	ent->read_proc = bluesleep_read_proc_proto;
+	ent->write_proc = bluesleep_write_proc_proto;
+
+	/* read only proc entries */
+	if (create_proc_read_entry("asleep", 0,
+			sleep_dir, bluesleep_read_proc_asleep, NULL) == NULL) {
+		BT_ERR("Unable to create /proc/%s/asleep entry", PROC_DIR);
+		retval = -ENOMEM;
+		goto fail;
+	}
+
+	flags = 0; /* clear all status bits */
+
+	/* Initialize spinlock. */
+	spin_lock_init(&rw_lock);
+
+	/* Initialize timer */
+	init_timer(&tx_timer);
+	tx_timer.function = bluesleep_tx_timer_expire;
+	tx_timer.data = 0;
+
+	/* initialize host wake tasklet */
+	tasklet_init(&hostwake_task, bluesleep_hostwake_task, 0);
+
+	hci_register_notifier(&hci_event_nblock);
+
+	return 0;
+
+fail:
+	remove_proc_entry("asleep", sleep_dir);
+	remove_proc_entry("proto", sleep_dir);
+	remove_proc_entry("hostwake", sleep_dir);
+	remove_proc_entry("btwake", sleep_dir);
+	remove_proc_entry("sleep", bluetooth_dir);
+	remove_proc_entry("bluetooth", 0);
+	return retval;
+}
+
+/**
+ * Cleans up the module.
+ */
+static void __exit bluesleep_exit(void)
+{
+	hci_unregister_notifier(&hci_event_nblock);
+	platform_driver_unregister(&bluesleep_driver);
+
+	remove_proc_entry("asleep", sleep_dir);
+	remove_proc_entry("proto", sleep_dir);
+	remove_proc_entry("hostwake", sleep_dir);
+	remove_proc_entry("btwake", sleep_dir);
+	remove_proc_entry("sleep", bluetooth_dir);
+	remove_proc_entry("bluetooth", 0);
+}
+
+module_init(bluesleep_init);
+module_exit(bluesleep_exit);
+
+MODULE_DESCRIPTION("Bluetooth Sleep Mode Driver ver %s " VERSION);
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/bluetooth/bluetooth-power.c b/drivers/bluetooth/bluetooth-power.c
new file mode 100644
index 0000000..3bf49d1
--- /dev/null
+++ b/drivers/bluetooth/bluetooth-power.c
@@ -0,0 +1,138 @@
+/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+/*
+ * Bluetooth Power Switch Module
+ * controls power to external Bluetooth device
+ * with interface to power management device
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/rfkill.h>
+
+static bool previous;
+
+static int bluetooth_toggle_radio(void *data, bool blocked)
+{
+	int ret = 0;
+	int (*power_control)(int enable);
+
+	power_control = data;
+	if (previous != blocked)
+		ret = (*power_control)(!blocked);
+	if (!ret)
+		previous = blocked;
+	return ret;
+}
+
+static const struct rfkill_ops bluetooth_power_rfkill_ops = {
+	.set_block = bluetooth_toggle_radio,
+};
+
+static int bluetooth_power_rfkill_probe(struct platform_device *pdev)
+{
+	struct rfkill *rfkill;
+	int ret;
+
+	rfkill = rfkill_alloc("bt_power", &pdev->dev, RFKILL_TYPE_BLUETOOTH,
+			      &bluetooth_power_rfkill_ops,
+			      pdev->dev.platform_data);
+
+	if (!rfkill) {
+		dev_err(&pdev->dev, "rfkill allocate failed\n");
+		return -ENOMEM;
+	}
+
+	/* force Bluetooth off during init to allow for user control */
+	rfkill_init_sw_state(rfkill, 1);
+	previous = 1;
+
+	ret = rfkill_register(rfkill);
+	if (ret) {
+		dev_err(&pdev->dev, "rfkill register failed=%d\n", ret);
+		rfkill_destroy(rfkill);
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, rfkill);
+
+	return 0;
+}
+
+static void bluetooth_power_rfkill_remove(struct platform_device *pdev)
+{
+	struct rfkill *rfkill;
+
+	dev_dbg(&pdev->dev, "%s\n", __func__);
+
+	rfkill = platform_get_drvdata(pdev);
+	if (rfkill)
+		rfkill_unregister(rfkill);
+	rfkill_destroy(rfkill);
+	platform_set_drvdata(pdev, NULL);
+}
+
+static int __devinit bt_power_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+
+	dev_dbg(&pdev->dev, "%s\n", __func__);
+
+	if (!pdev->dev.platform_data) {
+		dev_err(&pdev->dev, "platform data not initialized\n");
+		return -ENOSYS;
+	}
+
+	ret = bluetooth_power_rfkill_probe(pdev);
+
+	return ret;
+}
+
+static int __devexit bt_power_remove(struct platform_device *pdev)
+{
+	dev_dbg(&pdev->dev, "%s\n", __func__);
+
+	bluetooth_power_rfkill_remove(pdev);
+
+	return 0;
+}
+
+static struct platform_driver bt_power_driver = {
+	.probe = bt_power_probe,
+	.remove = __devexit_p(bt_power_remove),
+	.driver = {
+		.name = "bt_power",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init bluetooth_power_init(void)
+{
+	int ret;
+
+	ret = platform_driver_register(&bt_power_driver);
+	return ret;
+}
+
+static void __exit bluetooth_power_exit(void)
+{
+	platform_driver_unregister(&bt_power_driver);
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MSM Bluetooth power control driver");
+MODULE_VERSION("1.40");
+
+module_init(bluetooth_power_init);
+module_exit(bluetooth_power_exit);
diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c
index d894340..751b338 100644
--- a/drivers/bluetooth/bpa10x.c
+++ b/drivers/bluetooth/bpa10x.c
@@ -66,7 +66,7 @@
 
 static int bpa10x_recv(struct hci_dev *hdev, int queue, void *buf, int count)
 {
-	struct bpa10x_data *data = hci_get_drvdata(hdev);
+	struct bpa10x_data *data = hdev->driver_data;
 
 	BT_DBG("%s queue %d buffer %p count %d", hdev->name,
 							queue, buf, count);
@@ -189,7 +189,7 @@
 static void bpa10x_rx_complete(struct urb *urb)
 {
 	struct hci_dev *hdev = urb->context;
-	struct bpa10x_data *data = hci_get_drvdata(hdev);
+	struct bpa10x_data *data = hdev->driver_data;
 	int err;
 
 	BT_DBG("%s urb %p status %d count %d", hdev->name,
@@ -219,7 +219,7 @@
 
 static inline int bpa10x_submit_intr_urb(struct hci_dev *hdev)
 {
-	struct bpa10x_data *data = hci_get_drvdata(hdev);
+	struct bpa10x_data *data = hdev->driver_data;
 	struct urb *urb;
 	unsigned char *buf;
 	unsigned int pipe;
@@ -260,7 +260,7 @@
 
 static inline int bpa10x_submit_bulk_urb(struct hci_dev *hdev)
 {
-	struct bpa10x_data *data = hci_get_drvdata(hdev);
+	struct bpa10x_data *data = hdev->driver_data;
 	struct urb *urb;
 	unsigned char *buf;
 	unsigned int pipe;
@@ -301,7 +301,7 @@
 
 static int bpa10x_open(struct hci_dev *hdev)
 {
-	struct bpa10x_data *data = hci_get_drvdata(hdev);
+	struct bpa10x_data *data = hdev->driver_data;
 	int err;
 
 	BT_DBG("%s", hdev->name);
@@ -329,7 +329,7 @@
 
 static int bpa10x_close(struct hci_dev *hdev)
 {
-	struct bpa10x_data *data = hci_get_drvdata(hdev);
+	struct bpa10x_data *data = hdev->driver_data;
 
 	BT_DBG("%s", hdev->name);
 
@@ -343,7 +343,7 @@
 
 static int bpa10x_flush(struct hci_dev *hdev)
 {
-	struct bpa10x_data *data = hci_get_drvdata(hdev);
+	struct bpa10x_data *data = hdev->driver_data;
 
 	BT_DBG("%s", hdev->name);
 
@@ -355,7 +355,7 @@
 static int bpa10x_send_frame(struct sk_buff *skb)
 {
 	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
-	struct bpa10x_data *data = hci_get_drvdata(hdev);
+	struct bpa10x_data *data = hdev->driver_data;
 	struct usb_ctrlrequest *dr;
 	struct urb *urb;
 	unsigned int pipe;
@@ -432,6 +432,17 @@
 	return 0;
 }
 
+static void bpa10x_destruct(struct hci_dev *hdev)
+{
+	struct bpa10x_data *data = hdev->driver_data;
+
+	BT_DBG("%s", hdev->name);
+
+	kfree_skb(data->rx_skb[0]);
+	kfree_skb(data->rx_skb[1]);
+	kfree(data);
+}
+
 static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id *id)
 {
 	struct bpa10x_data *data;
@@ -459,7 +470,7 @@
 	}
 
 	hdev->bus = HCI_USB;
-	hci_set_drvdata(hdev, data);
+	hdev->driver_data = data;
 
 	data->hdev = hdev;
 
@@ -469,6 +480,9 @@
 	hdev->close    = bpa10x_close;
 	hdev->flush    = bpa10x_flush;
 	hdev->send     = bpa10x_send_frame;
+	hdev->destruct = bpa10x_destruct;
+
+	hdev->owner = THIS_MODULE;
 
 	set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks);
 
@@ -498,9 +512,6 @@
 	hci_unregister_dev(data->hdev);
 
 	hci_free_dev(data->hdev);
-	kfree_skb(data->rx_skb[0]);
-	kfree_skb(data->rx_skb[1]);
-	kfree(data);
 }
 
 static struct usb_driver bpa10x_driver = {
@@ -510,7 +521,20 @@
 	.id_table	= bpa10x_table,
 };
 
-module_usb_driver(bpa10x_driver);
+static int __init bpa10x_init(void)
+{
+	BT_INFO("Digianswer Bluetooth USB driver ver %s", VERSION);
+
+	return usb_register(&bpa10x_driver);
+}
+
+static void __exit bpa10x_exit(void)
+{
+	usb_deregister(&bpa10x_driver);
+}
+
+module_init(bpa10x_init);
+module_exit(bpa10x_exit);
 
 MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
 MODULE_DESCRIPTION("Digianswer Bluetooth USB driver ver " VERSION);
diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c
index 308c859..0c8a655 100644
--- a/drivers/bluetooth/bt3c_cs.c
+++ b/drivers/bluetooth/bt3c_cs.c
@@ -39,6 +39,7 @@
 #include <linux/serial.h>
 #include <linux/serial_reg.h>
 #include <linux/bitops.h>
+#include <asm/system.h>
 #include <asm/io.h>
 
 #include <linux/device.h>
@@ -388,7 +389,7 @@
 
 static int bt3c_hci_flush(struct hci_dev *hdev)
 {
-	bt3c_info_t *info = hci_get_drvdata(hdev);
+	bt3c_info_t *info = (bt3c_info_t *)(hdev->driver_data);
 
 	/* Drop TX queue */
 	skb_queue_purge(&(info->txq));
@@ -427,7 +428,7 @@
 		return -ENODEV;
 	}
 
-	info = hci_get_drvdata(hdev);
+	info = (bt3c_info_t *) (hdev->driver_data);
 
 	switch (bt_cb(skb)->pkt_type) {
 	case HCI_COMMAND_PKT:
@@ -455,6 +456,11 @@
 }
 
 
+static void bt3c_hci_destruct(struct hci_dev *hdev)
+{
+}
+
+
 static int bt3c_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
 {
 	return -ENOIOCTLCMD;
@@ -574,15 +580,18 @@
 	info->hdev = hdev;
 
 	hdev->bus = HCI_PCCARD;
-	hci_set_drvdata(hdev, info);
+	hdev->driver_data = info;
 	SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
 
 	hdev->open     = bt3c_hci_open;
 	hdev->close    = bt3c_hci_close;
 	hdev->flush    = bt3c_hci_flush;
 	hdev->send     = bt3c_hci_send_frame;
+	hdev->destruct = bt3c_hci_destruct;
 	hdev->ioctl    = bt3c_hci_ioctl;
 
+	hdev->owner = THIS_MODULE;
+
 	/* Load firmware */
 	err = request_firmware(&firmware, "BT3CPCC.bin", &info->p_dev->dev);
 	if (err < 0) {
@@ -627,7 +636,9 @@
 
 	bt3c_hci_close(hdev);
 
-	hci_unregister_dev(hdev);
+	if (hci_unregister_dev(hdev) < 0)
+		BT_ERR("Can't unregister HCI device %s", hdev->name);
+
 	hci_free_dev(hdev);
 
 	return 0;
@@ -750,7 +761,7 @@
 }
 
 
-static const struct pcmcia_device_id bt3c_ids[] = {
+static struct pcmcia_device_id bt3c_ids[] = {
 	PCMCIA_DEVICE_PROD_ID13("3COM", "Bluetooth PC Card", 0xefce0a31, 0xd4ce9b02),
 	PCMCIA_DEVICE_NULL
 };
diff --git a/drivers/bluetooth/btmrvl_debugfs.c b/drivers/bluetooth/btmrvl_debugfs.c
index 428dbb7..fd6305b 100644
--- a/drivers/bluetooth/btmrvl_debugfs.c
+++ b/drivers/bluetooth/btmrvl_debugfs.c
@@ -45,6 +45,12 @@
 	struct dentry *txdnldready;
 };
 
+static int btmrvl_open_generic(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
 static ssize_t btmrvl_hscfgcmd_write(struct file *file,
 			const char __user *ubuf, size_t count, loff_t *ppos)
 {
@@ -58,8 +64,6 @@
 		return -EFAULT;
 
 	ret = strict_strtol(buf, 10, &result);
-	if (ret)
-		return ret;
 
 	priv->btmrvl_dev.hscfgcmd = result;
 
@@ -87,7 +91,7 @@
 static const struct file_operations btmrvl_hscfgcmd_fops = {
 	.read	= btmrvl_hscfgcmd_read,
 	.write	= btmrvl_hscfgcmd_write,
-	.open	= simple_open,
+	.open	= btmrvl_open_generic,
 	.llseek = default_llseek,
 };
 
@@ -104,8 +108,6 @@
 		return -EFAULT;
 
 	ret = strict_strtol(buf, 10, &result);
-	if (ret)
-		return ret;
 
 	priv->btmrvl_dev.psmode = result;
 
@@ -128,7 +130,7 @@
 static const struct file_operations btmrvl_psmode_fops = {
 	.read	= btmrvl_psmode_read,
 	.write	= btmrvl_psmode_write,
-	.open	= simple_open,
+	.open	= btmrvl_open_generic,
 	.llseek = default_llseek,
 };
 
@@ -145,8 +147,6 @@
 		return -EFAULT;
 
 	ret = strict_strtol(buf, 10, &result);
-	if (ret)
-		return ret;
 
 	priv->btmrvl_dev.pscmd = result;
 
@@ -174,7 +174,7 @@
 static const struct file_operations btmrvl_pscmd_fops = {
 	.read = btmrvl_pscmd_read,
 	.write = btmrvl_pscmd_write,
-	.open = simple_open,
+	.open = btmrvl_open_generic,
 	.llseek = default_llseek,
 };
 
@@ -191,8 +191,6 @@
 		return -EFAULT;
 
 	ret = strict_strtol(buf, 16, &result);
-	if (ret)
-		return ret;
 
 	priv->btmrvl_dev.gpio_gap = result;
 
@@ -215,7 +213,7 @@
 static const struct file_operations btmrvl_gpiogap_fops = {
 	.read	= btmrvl_gpiogap_read,
 	.write	= btmrvl_gpiogap_write,
-	.open	= simple_open,
+	.open	= btmrvl_open_generic,
 	.llseek = default_llseek,
 };
 
@@ -232,8 +230,6 @@
 		return -EFAULT;
 
 	ret = strict_strtol(buf, 10, &result);
-	if (ret)
-		return ret;
 
 	priv->btmrvl_dev.hscmd = result;
 	if (priv->btmrvl_dev.hscmd) {
@@ -259,7 +255,7 @@
 static const struct file_operations btmrvl_hscmd_fops = {
 	.read	= btmrvl_hscmd_read,
 	.write	= btmrvl_hscmd_write,
-	.open	= simple_open,
+	.open	= btmrvl_open_generic,
 	.llseek = default_llseek,
 };
 
@@ -276,8 +272,6 @@
 		return -EFAULT;
 
 	ret = strict_strtol(buf, 10, &result);
-	if (ret)
-		return ret;
 
 	priv->btmrvl_dev.hsmode = result;
 
@@ -299,7 +293,7 @@
 static const struct file_operations btmrvl_hsmode_fops = {
 	.read	= btmrvl_hsmode_read,
 	.write	= btmrvl_hsmode_write,
-	.open	= simple_open,
+	.open	= btmrvl_open_generic,
 	.llseek = default_llseek,
 };
 
@@ -317,7 +311,7 @@
 
 static const struct file_operations btmrvl_curpsmode_fops = {
 	.read	= btmrvl_curpsmode_read,
-	.open	= simple_open,
+	.open	= btmrvl_open_generic,
 	.llseek = default_llseek,
 };
 
@@ -335,7 +329,7 @@
 
 static const struct file_operations btmrvl_psstate_fops = {
 	.read	= btmrvl_psstate_read,
-	.open	= simple_open,
+	.open	= btmrvl_open_generic,
 	.llseek = default_llseek,
 };
 
@@ -353,7 +347,7 @@
 
 static const struct file_operations btmrvl_hsstate_fops = {
 	.read	= btmrvl_hsstate_read,
-	.open	= simple_open,
+	.open	= btmrvl_open_generic,
 	.llseek = default_llseek,
 };
 
@@ -372,13 +366,13 @@
 
 static const struct file_operations btmrvl_txdnldready_fops = {
 	.read	= btmrvl_txdnldready_read,
-	.open	= simple_open,
+	.open	= btmrvl_open_generic,
 	.llseek = default_llseek,
 };
 
 void btmrvl_debugfs_init(struct hci_dev *hdev)
 {
-	struct btmrvl_private *priv = hci_get_drvdata(hdev);
+	struct btmrvl_private *priv = hdev->driver_data;
 	struct btmrvl_debugfs_data *dbg;
 
 	if (!hdev->debugfs)
@@ -395,34 +389,36 @@
 	dbg->config_dir = debugfs_create_dir("config", hdev->debugfs);
 
 	dbg->psmode = debugfs_create_file("psmode", 0644, dbg->config_dir,
-					  priv, &btmrvl_psmode_fops);
+				hdev->driver_data, &btmrvl_psmode_fops);
 	dbg->pscmd = debugfs_create_file("pscmd", 0644, dbg->config_dir,
-					 priv, &btmrvl_pscmd_fops);
+				hdev->driver_data, &btmrvl_pscmd_fops);
 	dbg->gpiogap = debugfs_create_file("gpiogap", 0644, dbg->config_dir,
-					   priv, &btmrvl_gpiogap_fops);
+				hdev->driver_data, &btmrvl_gpiogap_fops);
 	dbg->hsmode =  debugfs_create_file("hsmode", 0644, dbg->config_dir,
-					   priv, &btmrvl_hsmode_fops);
+				hdev->driver_data, &btmrvl_hsmode_fops);
 	dbg->hscmd = debugfs_create_file("hscmd", 0644, dbg->config_dir,
-					 priv, &btmrvl_hscmd_fops);
+				hdev->driver_data, &btmrvl_hscmd_fops);
 	dbg->hscfgcmd = debugfs_create_file("hscfgcmd", 0644, dbg->config_dir,
-					    priv, &btmrvl_hscfgcmd_fops);
+				hdev->driver_data, &btmrvl_hscfgcmd_fops);
 
 	dbg->status_dir = debugfs_create_dir("status", hdev->debugfs);
 	dbg->curpsmode = debugfs_create_file("curpsmode", 0444,
-					     dbg->status_dir, priv,
-					     &btmrvl_curpsmode_fops);
+						dbg->status_dir,
+						hdev->driver_data,
+						&btmrvl_curpsmode_fops);
 	dbg->psstate = debugfs_create_file("psstate", 0444, dbg->status_dir,
-					   priv, &btmrvl_psstate_fops);
+				hdev->driver_data, &btmrvl_psstate_fops);
 	dbg->hsstate = debugfs_create_file("hsstate", 0444, dbg->status_dir,
-					   priv, &btmrvl_hsstate_fops);
+				hdev->driver_data, &btmrvl_hsstate_fops);
 	dbg->txdnldready = debugfs_create_file("txdnldready", 0444,
-					       dbg->status_dir, priv,
-					       &btmrvl_txdnldready_fops);
+						dbg->status_dir,
+						hdev->driver_data,
+						&btmrvl_txdnldready_fops);
 }
 
 void btmrvl_debugfs_remove(struct hci_dev *hdev)
 {
-	struct btmrvl_private *priv = hci_get_drvdata(hdev);
+	struct btmrvl_private *priv = hdev->driver_data;
 	struct btmrvl_debugfs_data *dbg = priv->debugfs_data;
 
 	if (!dbg)
diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c
index d1209ad..548d1d9 100644
--- a/drivers/bluetooth/btmrvl_main.c
+++ b/drivers/bluetooth/btmrvl_main.c
@@ -18,8 +18,6 @@
  * this warranty disclaimer.
  **/
 
-#include <linux/module.h>
-
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 
@@ -387,6 +385,10 @@
 	return -ENOIOCTLCMD;
 }
 
+static void btmrvl_destruct(struct hci_dev *hdev)
+{
+}
+
 static int btmrvl_send_frame(struct sk_buff *skb)
 {
 	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
@@ -394,13 +396,12 @@
 
 	BT_DBG("type=%d, len=%d", skb->pkt_type, skb->len);
 
-	if (!hdev) {
+	if (!hdev || !hdev->driver_data) {
 		BT_ERR("Frame for unknown HCI device");
 		return -ENODEV;
 	}
 
-	priv = hci_get_drvdata(hdev);
-
+	priv = (struct btmrvl_private *) hdev->driver_data;
 	if (!test_bit(HCI_RUNNING, &hdev->flags)) {
 		BT_ERR("Failed testing HCI_RUNING, flags=%lx", hdev->flags);
 		print_hex_dump_bytes("data: ", DUMP_PREFIX_OFFSET,
@@ -431,7 +432,7 @@
 
 static int btmrvl_flush(struct hci_dev *hdev)
 {
-	struct btmrvl_private *priv = hci_get_drvdata(hdev);
+	struct btmrvl_private *priv = hdev->driver_data;
 
 	skb_queue_purge(&priv->adapter->tx_queue);
 
@@ -440,7 +441,7 @@
 
 static int btmrvl_close(struct hci_dev *hdev)
 {
-	struct btmrvl_private *priv = hci_get_drvdata(hdev);
+	struct btmrvl_private *priv = hdev->driver_data;
 
 	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
 		return 0;
@@ -472,6 +473,8 @@
 
 	init_waitqueue_entry(&wait, current);
 
+	current->flags |= PF_NOFREEZE;
+
 	for (;;) {
 		add_wait_queue(&thread->wait_q, &wait);
 
@@ -543,14 +546,16 @@
 	}
 
 	priv->btmrvl_dev.hcidev = hdev;
-	hci_set_drvdata(hdev, priv);
+	hdev->driver_data = priv;
 
 	hdev->bus = HCI_SDIO;
 	hdev->open = btmrvl_open;
 	hdev->close = btmrvl_close;
 	hdev->flush = btmrvl_flush;
 	hdev->send = btmrvl_send_frame;
+	hdev->destruct = btmrvl_destruct;
 	hdev->ioctl = btmrvl_ioctl;
+	hdev->owner = THIS_MODULE;
 
 	btmrvl_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ);
 
diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c
index 27b74b0..7f521d4 100644
--- a/drivers/bluetooth/btmrvl_sdio.c
+++ b/drivers/bluetooth/btmrvl_sdio.c
@@ -23,7 +23,6 @@
 
 #include <linux/mmc/sdio_ids.h>
 #include <linux/mmc/sdio_func.h>
-#include <linux/module.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
@@ -65,7 +64,7 @@
 	.io_port_1 = 0x01,
 	.io_port_2 = 0x02,
 };
-static const struct btmrvl_sdio_card_reg btmrvl_reg_87xx = {
+static const struct btmrvl_sdio_card_reg btmrvl_reg_8787 = {
 	.cfg = 0x00,
 	.host_int_mask = 0x02,
 	.host_intstatus = 0x03,
@@ -82,7 +81,7 @@
 	.io_port_2 = 0x7a,
 };
 
-static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
+static const struct btmrvl_sdio_device btmrvl_sdio_sd6888 = {
 	.helper		= "sd8688_helper.bin",
 	.firmware	= "sd8688.bin",
 	.reg		= &btmrvl_reg_8688,
@@ -92,27 +91,17 @@
 static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = {
 	.helper		= NULL,
 	.firmware	= "mrvl/sd8787_uapsta.bin",
-	.reg		= &btmrvl_reg_87xx,
-	.sd_blksz_fw_dl	= 256,
-};
-
-static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = {
-	.helper		= NULL,
-	.firmware	= "mrvl/sd8797_uapsta.bin",
-	.reg		= &btmrvl_reg_87xx,
+	.reg		= &btmrvl_reg_8787,
 	.sd_blksz_fw_dl	= 256,
 };
 
 static const struct sdio_device_id btmrvl_sdio_ids[] = {
 	/* Marvell SD8688 Bluetooth device */
 	{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9105),
-			.driver_data = (unsigned long) &btmrvl_sdio_sd8688 },
+			.driver_data = (unsigned long) &btmrvl_sdio_sd6888 },
 	/* Marvell SD8787 Bluetooth device */
 	{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911A),
 			.driver_data = (unsigned long) &btmrvl_sdio_sd8787 },
-	/* Marvell SD8797 Bluetooth device */
-	{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x912A),
-			.driver_data = (unsigned long) &btmrvl_sdio_sd8797 },
 
 	{ }	/* Terminating entry */
 };
@@ -1086,4 +1075,3 @@
 MODULE_FIRMWARE("sd8688_helper.bin");
 MODULE_FIRMWARE("sd8688.bin");
 MODULE_FIRMWARE("mrvl/sd8787_uapsta.bin");
-MODULE_FIRMWARE("mrvl/sd8797_uapsta.bin");
diff --git a/drivers/bluetooth/btsdio.c b/drivers/bluetooth/btsdio.c
index e10ea03..792e32d 100644
--- a/drivers/bluetooth/btsdio.c
+++ b/drivers/bluetooth/btsdio.c
@@ -189,7 +189,7 @@
 
 static int btsdio_open(struct hci_dev *hdev)
 {
-	struct btsdio_data *data = hci_get_drvdata(hdev);
+	struct btsdio_data *data = hdev->driver_data;
 	int err;
 
 	BT_DBG("%s", hdev->name);
@@ -225,7 +225,7 @@
 
 static int btsdio_close(struct hci_dev *hdev)
 {
-	struct btsdio_data *data = hci_get_drvdata(hdev);
+	struct btsdio_data *data = hdev->driver_data;
 
 	BT_DBG("%s", hdev->name);
 
@@ -246,7 +246,7 @@
 
 static int btsdio_flush(struct hci_dev *hdev)
 {
-	struct btsdio_data *data = hci_get_drvdata(hdev);
+	struct btsdio_data *data = hdev->driver_data;
 
 	BT_DBG("%s", hdev->name);
 
@@ -258,7 +258,7 @@
 static int btsdio_send_frame(struct sk_buff *skb)
 {
 	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
-	struct btsdio_data *data = hci_get_drvdata(hdev);
+	struct btsdio_data *data = hdev->driver_data;
 
 	BT_DBG("%s", hdev->name);
 
@@ -289,6 +289,15 @@
 	return 0;
 }
 
+static void btsdio_destruct(struct hci_dev *hdev)
+{
+	struct btsdio_data *data = hdev->driver_data;
+
+	BT_DBG("%s", hdev->name);
+
+	kfree(data);
+}
+
 static int btsdio_probe(struct sdio_func *func,
 				const struct sdio_device_id *id)
 {
@@ -321,7 +330,7 @@
 	}
 
 	hdev->bus = HCI_SDIO;
-	hci_set_drvdata(hdev, data);
+	hdev->driver_data = data;
 
 	if (id->class == SDIO_CLASS_BT_AMP)
 		hdev->dev_type = HCI_AMP;
@@ -336,6 +345,9 @@
 	hdev->close    = btsdio_close;
 	hdev->flush    = btsdio_flush;
 	hdev->send     = btsdio_send_frame;
+	hdev->destruct = btsdio_destruct;
+
+	hdev->owner = THIS_MODULE;
 
 	err = hci_register_dev(hdev);
 	if (err < 0) {
@@ -366,7 +378,6 @@
 	hci_unregister_dev(hdev);
 
 	hci_free_dev(hdev);
-	kfree(data);
 }
 
 static struct sdio_driver btsdio_driver = {
diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c
index c4fc2f3..f8a0708 100644
--- a/drivers/bluetooth/btuart_cs.c
+++ b/drivers/bluetooth/btuart_cs.c
@@ -38,6 +38,7 @@
 #include <linux/serial.h>
 #include <linux/serial_reg.h>
 #include <linux/bitops.h>
+#include <asm/system.h>
 #include <asm/io.h>
 
 #include <pcmcia/cistpl.h>
@@ -396,7 +397,7 @@
 
 static int btuart_hci_flush(struct hci_dev *hdev)
 {
-	btuart_info_t *info = hci_get_drvdata(hdev);
+	btuart_info_t *info = (btuart_info_t *)(hdev->driver_data);
 
 	/* Drop TX queue */
 	skb_queue_purge(&(info->txq));
@@ -434,7 +435,7 @@
 		return -ENODEV;
 	}
 
-	info = hci_get_drvdata(hdev);
+	info = (btuart_info_t *)(hdev->driver_data);
 
 	switch (bt_cb(skb)->pkt_type) {
 	case HCI_COMMAND_PKT:
@@ -458,6 +459,11 @@
 }
 
 
+static void btuart_hci_destruct(struct hci_dev *hdev)
+{
+}
+
+
 static int btuart_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
 {
 	return -ENOIOCTLCMD;
@@ -492,15 +498,18 @@
 	info->hdev = hdev;
 
 	hdev->bus = HCI_PCCARD;
-	hci_set_drvdata(hdev, info);
+	hdev->driver_data = info;
 	SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
 
 	hdev->open     = btuart_hci_open;
 	hdev->close    = btuart_hci_close;
 	hdev->flush    = btuart_hci_flush;
 	hdev->send     = btuart_hci_send_frame;
+	hdev->destruct = btuart_hci_destruct;
 	hdev->ioctl    = btuart_hci_ioctl;
 
+	hdev->owner = THIS_MODULE;
+
 	spin_lock_irqsave(&(info->lock), flags);
 
 	/* Reset UART */
@@ -556,7 +565,9 @@
 
 	spin_unlock_irqrestore(&(info->lock), flags);
 
-	hci_unregister_dev(hdev);
+	if (hci_unregister_dev(hdev) < 0)
+		BT_ERR("Can't unregister HCI device %s", hdev->name);
+
 	hci_free_dev(hdev);
 
 	return 0;
@@ -678,7 +689,7 @@
 	pcmcia_disable_device(link);
 }
 
-static const struct pcmcia_device_id btuart_ids[] = {
+static struct pcmcia_device_id btuart_ids[] = {
 	/* don't use this driver. Use serial_cs + hci_uart instead */
 	PCMCIA_DEVICE_NULL
 };
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 9217121..e4b6b6b 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -37,13 +37,13 @@
 
 #define VERSION "0.6"
 
-static bool ignore_dga;
-static bool ignore_csr;
-static bool ignore_sniffer;
-static bool disable_scofix;
-static bool force_scofix;
+static int ignore_dga;
+static int ignore_csr;
+static int ignore_sniffer;
+static int disable_scofix;
+static int force_scofix;
 
-static bool reset = 1;
+static int reset = 1;
 
 static struct usb_driver btusb_driver;
 
@@ -61,7 +61,7 @@
 	{ USB_DEVICE_INFO(0xe0, 0x01, 0x01) },
 
 	/* Broadcom SoftSailing reporting vendor specific */
-	{ USB_DEVICE(0x0a5c, 0x21e1) },
+	{ USB_DEVICE(0x05ac, 0x21e1) },
 
 	/* Apple MacBookPro 7,1 */
 	{ USB_DEVICE(0x05ac, 0x8213) },
@@ -100,17 +100,6 @@
 	/* Canyon CN-BTU1 with HID interfaces */
 	{ USB_DEVICE(0x0c10, 0x0000) },
 
-	/* Broadcom BCM20702A0 */
-	{ USB_DEVICE(0x0489, 0xe042) },
-	{ USB_DEVICE(0x0a5c, 0x21e3) },
-	{ USB_DEVICE(0x0a5c, 0x21e6) },
-	{ USB_DEVICE(0x0a5c, 0x21e8) },
-	{ USB_DEVICE(0x0a5c, 0x21f3) },
-	{ USB_DEVICE(0x413c, 0x8197) },
-
-	/* Foxconn - Hon Hai */
-	{ USB_DEVICE(0x0489, 0xe033) },
-
 	{ }	/* Terminating entry */
 };
 
@@ -125,20 +114,12 @@
 
 	/* Atheros 3011 with sflash firmware */
 	{ USB_DEVICE(0x0cf3, 0x3002), .driver_info = BTUSB_IGNORE },
-	{ USB_DEVICE(0x13d3, 0x3304), .driver_info = BTUSB_IGNORE },
-	{ USB_DEVICE(0x0930, 0x0215), .driver_info = BTUSB_IGNORE },
-	{ USB_DEVICE(0x0489, 0xe03d), .driver_info = BTUSB_IGNORE },
 
 	/* Atheros AR9285 Malbec with sflash firmware */
 	{ USB_DEVICE(0x03f0, 0x311d), .driver_info = BTUSB_IGNORE },
 
 	/* Atheros 3012 with sflash firmware */
 	{ USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 },
-	{ USB_DEVICE(0x0cf3, 0x311d), .driver_info = BTUSB_ATH3012 },
-	{ USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
-	{ USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 },
-	{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
-	{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
 
 	/* Atheros AR5BBU12 with sflash firmware */
 	{ USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE },
@@ -255,7 +236,7 @@
 static void btusb_intr_complete(struct urb *urb)
 {
 	struct hci_dev *hdev = urb->context;
-	struct btusb_data *data = hci_get_drvdata(hdev);
+	struct btusb_data *data = hdev->driver_data;
 	int err;
 
 	BT_DBG("%s urb %p status %d count %d", hdev->name,
@@ -283,9 +264,7 @@
 
 	err = usb_submit_urb(urb, GFP_ATOMIC);
 	if (err < 0) {
-		/* -EPERM: urb is being killed;
-		 * -ENODEV: device got disconnected */
-		if (err != -EPERM && err != -ENODEV)
+		if (err != -EPERM)
 			BT_ERR("%s urb %p failed to resubmit (%d)",
 						hdev->name, urb, -err);
 		usb_unanchor_urb(urb);
@@ -294,7 +273,7 @@
 
 static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
 {
-	struct btusb_data *data = hci_get_drvdata(hdev);
+	struct btusb_data *data = hdev->driver_data;
 	struct urb *urb;
 	unsigned char *buf;
 	unsigned int pipe;
@@ -329,8 +308,7 @@
 
 	err = usb_submit_urb(urb, mem_flags);
 	if (err < 0) {
-		if (err != -EPERM && err != -ENODEV)
-			BT_ERR("%s urb %p submission failed (%d)",
+		BT_ERR("%s urb %p submission failed (%d)",
 						hdev->name, urb, -err);
 		usb_unanchor_urb(urb);
 	}
@@ -343,7 +321,7 @@
 static void btusb_bulk_complete(struct urb *urb)
 {
 	struct hci_dev *hdev = urb->context;
-	struct btusb_data *data = hci_get_drvdata(hdev);
+	struct btusb_data *data = hdev->driver_data;
 	int err;
 
 	BT_DBG("%s urb %p status %d count %d", hdev->name,
@@ -371,9 +349,7 @@
 
 	err = usb_submit_urb(urb, GFP_ATOMIC);
 	if (err < 0) {
-		/* -EPERM: urb is being killed;
-		 * -ENODEV: device got disconnected */
-		if (err != -EPERM && err != -ENODEV)
+		if (err != -EPERM)
 			BT_ERR("%s urb %p failed to resubmit (%d)",
 						hdev->name, urb, -err);
 		usb_unanchor_urb(urb);
@@ -382,7 +358,7 @@
 
 static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags)
 {
-	struct btusb_data *data = hci_get_drvdata(hdev);
+	struct btusb_data *data = hdev->driver_data;
 	struct urb *urb;
 	unsigned char *buf;
 	unsigned int pipe;
@@ -415,8 +391,7 @@
 
 	err = usb_submit_urb(urb, mem_flags);
 	if (err < 0) {
-		if (err != -EPERM && err != -ENODEV)
-			BT_ERR("%s urb %p submission failed (%d)",
+		BT_ERR("%s urb %p submission failed (%d)",
 						hdev->name, urb, -err);
 		usb_unanchor_urb(urb);
 	}
@@ -429,7 +404,7 @@
 static void btusb_isoc_complete(struct urb *urb)
 {
 	struct hci_dev *hdev = urb->context;
-	struct btusb_data *data = hci_get_drvdata(hdev);
+	struct btusb_data *data = hdev->driver_data;
 	int i, err;
 
 	BT_DBG("%s urb %p status %d count %d", hdev->name,
@@ -464,16 +439,14 @@
 
 	err = usb_submit_urb(urb, GFP_ATOMIC);
 	if (err < 0) {
-		/* -EPERM: urb is being killed;
-		 * -ENODEV: device got disconnected */
-		if (err != -EPERM && err != -ENODEV)
+		if (err != -EPERM)
 			BT_ERR("%s urb %p failed to resubmit (%d)",
 						hdev->name, urb, -err);
 		usb_unanchor_urb(urb);
 	}
 }
 
-static inline void __fill_isoc_descriptor(struct urb *urb, int len, int mtu)
+static void inline __fill_isoc_descriptor(struct urb *urb, int len, int mtu)
 {
 	int i, offset = 0;
 
@@ -496,7 +469,7 @@
 
 static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
 {
-	struct btusb_data *data = hci_get_drvdata(hdev);
+	struct btusb_data *data = hdev->driver_data;
 	struct urb *urb;
 	unsigned char *buf;
 	unsigned int pipe;
@@ -522,10 +495,15 @@
 
 	pipe = usb_rcvisocpipe(data->udev, data->isoc_rx_ep->bEndpointAddress);
 
-	usb_fill_int_urb(urb, data->udev, pipe, buf, size, btusb_isoc_complete,
-				hdev, data->isoc_rx_ep->bInterval);
+	urb->dev      = data->udev;
+	urb->pipe     = pipe;
+	urb->context  = hdev;
+	urb->complete = btusb_isoc_complete;
+	urb->interval = data->isoc_rx_ep->bInterval;
 
 	urb->transfer_flags  = URB_FREE_BUFFER | URB_ISO_ASAP;
+	urb->transfer_buffer = buf;
+	urb->transfer_buffer_length = size;
 
 	__fill_isoc_descriptor(urb, size,
 			le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize));
@@ -534,8 +512,7 @@
 
 	err = usb_submit_urb(urb, mem_flags);
 	if (err < 0) {
-		if (err != -EPERM && err != -ENODEV)
-			BT_ERR("%s urb %p submission failed (%d)",
+		BT_ERR("%s urb %p submission failed (%d)",
 						hdev->name, urb, -err);
 		usb_unanchor_urb(urb);
 	}
@@ -549,7 +526,7 @@
 {
 	struct sk_buff *skb = urb->context;
 	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
-	struct btusb_data *data = hci_get_drvdata(hdev);
+	struct btusb_data *data = hdev->driver_data;
 
 	BT_DBG("%s urb %p status %d count %d", hdev->name,
 					urb, urb->status, urb->actual_length);
@@ -596,7 +573,7 @@
 
 static int btusb_open(struct hci_dev *hdev)
 {
-	struct btusb_data *data = hci_get_drvdata(hdev);
+	struct btusb_data *data = hdev->driver_data;
 	int err;
 
 	BT_DBG("%s", hdev->name);
@@ -646,7 +623,7 @@
 
 static int btusb_close(struct hci_dev *hdev)
 {
-	struct btusb_data *data = hci_get_drvdata(hdev);
+	struct btusb_data *data = hdev->driver_data;
 	int err;
 
 	BT_DBG("%s", hdev->name);
@@ -676,7 +653,7 @@
 
 static int btusb_flush(struct hci_dev *hdev)
 {
-	struct btusb_data *data = hci_get_drvdata(hdev);
+	struct btusb_data *data = hdev->driver_data;
 
 	BT_DBG("%s", hdev->name);
 
@@ -688,7 +665,7 @@
 static int btusb_send_frame(struct sk_buff *skb)
 {
 	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
-	struct btusb_data *data = hci_get_drvdata(hdev);
+	struct btusb_data *data = hdev->driver_data;
 	struct usb_ctrlrequest *dr;
 	struct urb *urb;
 	unsigned int pipe;
@@ -726,7 +703,8 @@
 		break;
 
 	case HCI_ACLDATA_PKT:
-		if (!data->bulk_tx_ep)
+		if (!data->bulk_tx_ep || (hdev->conn_hash.acl_num < 1 &&
+						hdev->conn_hash.le_num < 1))
 			return -ENODEV;
 
 		urb = usb_alloc_urb(0, GFP_ATOMIC);
@@ -782,23 +760,31 @@
 
 	err = usb_submit_urb(urb, GFP_ATOMIC);
 	if (err < 0) {
-		if (err != -EPERM && err != -ENODEV)
-			BT_ERR("%s urb %p submission failed (%d)",
-						hdev->name, urb, -err);
+		BT_ERR("%s urb %p submission failed", hdev->name, urb);
 		kfree(urb->setup_packet);
 		usb_unanchor_urb(urb);
 	} else {
 		usb_mark_last_busy(data->udev);
 	}
 
-done:
 	usb_free_urb(urb);
+
+done:
 	return err;
 }
 
+static void btusb_destruct(struct hci_dev *hdev)
+{
+	struct btusb_data *data = hdev->driver_data;
+
+	BT_DBG("%s", hdev->name);
+
+	kfree(data);
+}
+
 static void btusb_notify(struct hci_dev *hdev, unsigned int evt)
 {
-	struct btusb_data *data = hci_get_drvdata(hdev);
+	struct btusb_data *data = hdev->driver_data;
 
 	BT_DBG("%s evt %d", hdev->name, evt);
 
@@ -808,9 +794,9 @@
 	}
 }
 
-static inline int __set_isoc_interface(struct hci_dev *hdev, int altsetting)
+static int inline __set_isoc_interface(struct hci_dev *hdev, int altsetting)
 {
-	struct btusb_data *data = hci_get_drvdata(hdev);
+	struct btusb_data *data = hdev->driver_data;
 	struct usb_interface *intf = data->isoc;
 	struct usb_endpoint_descriptor *ep_desc;
 	int i, err;
@@ -998,7 +984,7 @@
 	}
 
 	hdev->bus = HCI_USB;
-	hci_set_drvdata(hdev, data);
+	hdev->driver_data = data;
 
 	data->hdev = hdev;
 
@@ -1008,8 +994,11 @@
 	hdev->close    = btusb_close;
 	hdev->flush    = btusb_flush;
 	hdev->send     = btusb_send_frame;
+	hdev->destruct = btusb_destruct;
 	hdev->notify   = btusb_notify;
 
+	hdev->owner = THIS_MODULE;
+
 	/* Interface numbers are hardcoded in the specification */
 	data->isoc = usb_ifnum_to_if(data->udev, 1);
 
@@ -1091,6 +1080,9 @@
 		return;
 
 	hdev = data->hdev;
+
+	__hci_dev_hold(hdev);
+
 	usb_set_intfdata(data->intf, NULL);
 
 	if (data->isoc)
@@ -1103,8 +1095,9 @@
 	else if (data->isoc)
 		usb_driver_release_interface(&btusb_driver, data->isoc);
 
+	__hci_dev_put(hdev);
+
 	hci_free_dev(hdev);
-	kfree(data);
 }
 
 #ifdef CONFIG_PM
@@ -1118,7 +1111,7 @@
 		return 0;
 
 	spin_lock_irq(&data->txlock);
-	if (!(PMSG_IS_AUTO(message) && data->tx_in_flight)) {
+	if (!((message.event & PM_EVENT_AUTO) && data->tx_in_flight)) {
 		set_bit(BTUSB_SUSPENDING, &data->flags);
 		spin_unlock_irq(&data->txlock);
 	} else {
@@ -1220,7 +1213,20 @@
 	.supports_autosuspend = 1,
 };
 
-module_usb_driver(btusb_driver);
+static int __init btusb_init(void)
+{
+	BT_INFO("Generic Bluetooth USB driver ver %s", VERSION);
+
+	return usb_register(&btusb_driver);
+}
+
+static void __exit btusb_exit(void)
+{
+	usb_deregister(&btusb_driver);
+}
+
+module_init(btusb_init);
+module_exit(btusb_exit);
 
 module_param(ignore_dga, bool, 0644);
 MODULE_PARM_DESC(ignore_dga, "Ignore devices with id 08fd:0001");
diff --git a/drivers/bluetooth/btwilink.c b/drivers/bluetooth/btwilink.c
index 8869469..65d27af 100644
--- a/drivers/bluetooth/btwilink.c
+++ b/drivers/bluetooth/btwilink.c
@@ -29,7 +29,6 @@
 #include <net/bluetooth/hci.h>
 
 #include <linux/ti_wilink_st.h>
-#include <linux/module.h>
 
 /* Bluetooth Driver Version */
 #define VERSION               "1.0"
@@ -126,13 +125,6 @@
 /* protocol structure registered with shared transport */
 static struct st_proto_s ti_st_proto[MAX_BT_CHNL_IDS] = {
 	{
-		.chnl_id = HCI_EVENT_PKT, /* HCI Events */
-		.hdr_len = sizeof(struct hci_event_hdr),
-		.offset_len_in_hdr = offsetof(struct hci_event_hdr, plen),
-		.len_size = 1, /* sizeof(plen) in struct hci_event_hdr */
-		.reserve = 8,
-	},
-	{
 		.chnl_id = HCI_ACLDATA_PKT, /* ACL */
 		.hdr_len = sizeof(struct hci_acl_hdr),
 		.offset_len_in_hdr = offsetof(struct hci_acl_hdr, dlen),
@@ -146,6 +138,13 @@
 		.len_size = 1, /* sizeof(dlen) in struct hci_sco_hdr */
 		.reserve = 8,
 	},
+	{
+		.chnl_id = HCI_EVENT_PKT, /* HCI Events */
+		.hdr_len = sizeof(struct hci_event_hdr),
+		.offset_len_in_hdr = offsetof(struct hci_event_hdr, plen),
+		.len_size = 1, /* sizeof(plen) in struct hci_event_hdr */
+		.reserve = 8,
+	},
 };
 
 /* Called from HCI core to initialize the device */
@@ -161,7 +160,7 @@
 		return -EBUSY;
 
 	/* provide contexts for callbacks from ST */
-	hst = hci_get_drvdata(hdev);
+	hst = hdev->driver_data;
 
 	for (i = 0; i < MAX_BT_CHNL_IDS; i++) {
 		ti_st_proto[i].priv_data = hst;
@@ -236,12 +235,12 @@
 static int ti_st_close(struct hci_dev *hdev)
 {
 	int err, i;
-	struct ti_st *hst = hci_get_drvdata(hdev);
+	struct ti_st *hst = hdev->driver_data;
 
 	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
 		return 0;
 
-	for (i = MAX_BT_CHNL_IDS-1; i >= 0; i--) {
+	for (i = 0; i < MAX_BT_CHNL_IDS; i++) {
 		err = st_unregister(&ti_st_proto[i]);
 		if (err)
 			BT_ERR("st_unregister(%d) failed with error %d",
@@ -264,7 +263,7 @@
 	if (!test_bit(HCI_RUNNING, &hdev->flags))
 		return -EBUSY;
 
-	hst = hci_get_drvdata(hdev);
+	hst = hdev->driver_data;
 
 	/* Prepend skb with frame type */
 	memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
@@ -291,6 +290,14 @@
 	return 0;
 }
 
+static void ti_st_destruct(struct hci_dev *hdev)
+{
+	BT_DBG("%s", hdev->name);
+	/* do nothing here, since platform remove
+	 * would free the hdev->driver_data
+	 */
+}
+
 static int bt_ti_probe(struct platform_device *pdev)
 {
 	static struct ti_st *hst;
@@ -312,11 +319,13 @@
 
 	hst->hdev = hdev;
 	hdev->bus = HCI_UART;
-	hci_set_drvdata(hdev, hst);
+	hdev->driver_data = hst;
 	hdev->open = ti_st_open;
 	hdev->close = ti_st_close;
 	hdev->flush = NULL;
 	hdev->send = ti_st_send_frame;
+	hdev->destruct = ti_st_destruct;
+	hdev->owner = THIS_MODULE;
 
 	err = hci_register_dev(hdev);
 	if (err < 0) {
diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c
index 6e8d961..26ee0cf 100644
--- a/drivers/bluetooth/dtl1_cs.c
+++ b/drivers/bluetooth/dtl1_cs.c
@@ -38,6 +38,7 @@
 #include <linux/serial.h>
 #include <linux/serial_reg.h>
 #include <linux/bitops.h>
+#include <asm/system.h>
 #include <asm/io.h>
 
 #include <pcmcia/cistpl.h>
@@ -82,6 +83,9 @@
 
 
 static int dtl1_config(struct pcmcia_device *link);
+static void dtl1_release(struct pcmcia_device *link);
+
+static void dtl1_detach(struct pcmcia_device *p_dev);
 
 
 /* Transmit states  */
@@ -363,7 +367,7 @@
 
 static int dtl1_hci_flush(struct hci_dev *hdev)
 {
-	dtl1_info_t *info = hci_get_drvdata(hdev);
+	dtl1_info_t *info = (dtl1_info_t *)(hdev->driver_data);
 
 	/* Drop TX queue */
 	skb_queue_purge(&(info->txq));
@@ -395,7 +399,7 @@
 		return -ENODEV;
 	}
 
-	info = hci_get_drvdata(hdev);
+	info = (dtl1_info_t *)(hdev->driver_data);
 
 	switch (bt_cb(skb)->pkt_type) {
 	case HCI_COMMAND_PKT:
@@ -438,6 +442,11 @@
 }
 
 
+static void dtl1_hci_destruct(struct hci_dev *hdev)
+{
+}
+
+
 static int dtl1_hci_ioctl(struct hci_dev *hdev, unsigned int cmd,  unsigned long arg)
 {
 	return -ENOIOCTLCMD;
@@ -474,15 +483,18 @@
 	info->hdev = hdev;
 
 	hdev->bus = HCI_PCCARD;
-	hci_set_drvdata(hdev, info);
+	hdev->driver_data = info;
 	SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
 
 	hdev->open     = dtl1_hci_open;
 	hdev->close    = dtl1_hci_close;
 	hdev->flush    = dtl1_hci_flush;
 	hdev->send     = dtl1_hci_send_frame;
+	hdev->destruct = dtl1_hci_destruct;
 	hdev->ioctl    = dtl1_hci_ioctl;
 
+	hdev->owner = THIS_MODULE;
+
 	spin_lock_irqsave(&(info->lock), flags);
 
 	/* Reset UART */
@@ -539,7 +551,9 @@
 
 	spin_unlock_irqrestore(&(info->lock), flags);
 
-	hci_unregister_dev(hdev);
+	if (hci_unregister_dev(hdev) < 0)
+		BT_ERR("Can't unregister HCI device %s", hdev->name);
+
 	hci_free_dev(hdev);
 
 	return 0;
@@ -567,8 +581,8 @@
 {
 	dtl1_info_t *info = link->priv;
 
-	dtl1_close(info);
-	pcmcia_disable_device(link);
+	dtl1_release(link);
+
 	kfree(info);
 }
 
@@ -607,11 +621,22 @@
 	return 0;
 
 failed:
-	dtl1_detach(link);
+	dtl1_release(link);
 	return -ENODEV;
 }
 
-static const struct pcmcia_device_id dtl1_ids[] = {
+
+static void dtl1_release(struct pcmcia_device *link)
+{
+	dtl1_info_t *info = link->priv;
+
+	dtl1_close(info);
+
+	pcmcia_disable_device(link);
+}
+
+
+static struct pcmcia_device_id dtl1_ids[] = {
 	PCMCIA_DEVICE_PROD_ID12("Nokia Mobile Phones", "DTL-1", 0xe1bfdd64, 0xe168480d),
 	PCMCIA_DEVICE_PROD_ID12("Nokia Mobile Phones", "DTL-4", 0xe1bfdd64, 0x9102bc82),
 	PCMCIA_DEVICE_PROD_ID12("Socket", "CF", 0xb38bcc2e, 0x44ebf863),
diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c
index 12172a6..4093935 100644
--- a/drivers/bluetooth/hci_ath.c
+++ b/drivers/bluetooth/hci_ath.c
@@ -112,7 +112,7 @@
 
 	BT_DBG("hu %p", hu);
 
-	ath = kzalloc(sizeof(*ath), GFP_KERNEL);
+	ath = kzalloc(sizeof(*ath), GFP_ATOMIC);
 	if (!ath)
 		return -ENOMEM;
 
diff --git a/drivers/bluetooth/hci_bcsp.c b/drivers/bluetooth/hci_bcsp.c
index 661a8dc..9c5b2dc 100644
--- a/drivers/bluetooth/hci_bcsp.c
+++ b/drivers/bluetooth/hci_bcsp.c
@@ -49,8 +49,8 @@
 
 #define VERSION "0.3"
 
-static bool txcrc = 1;
-static bool hciextn = 1;
+static int txcrc = 1;
+static int hciextn = 1;
 
 #define BCSP_TXWINSIZE	4
 
@@ -692,7 +692,7 @@
 
 	BT_DBG("hu %p", hu);
 
-	bcsp = kzalloc(sizeof(*bcsp), GFP_KERNEL);
+	bcsp = kzalloc(sizeof(*bcsp), GFP_ATOMIC);
 	if (!bcsp)
 		return -ENOMEM;
 
diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c
index 7483294..2fcd8b3 100644
--- a/drivers/bluetooth/hci_h4.c
+++ b/drivers/bluetooth/hci_h4.c
@@ -69,7 +69,7 @@
 
 	BT_DBG("hu %p", hu);
 
-	h4 = kzalloc(sizeof(*h4), GFP_KERNEL);
+	h4 = kzalloc(sizeof(*h4), GFP_ATOMIC);
 	if (!h4)
 		return -ENOMEM;
 
diff --git a/drivers/bluetooth/hci_ibs.c b/drivers/bluetooth/hci_ibs.c
new file mode 100644
index 0000000..2a6f3f8
--- /dev/null
+++ b/drivers/bluetooth/hci_ibs.c
@@ -0,0 +1,820 @@
+/*
+ *  Qualcomm's Bluetooth Software In-Band Sleep UART protocol
+ *
+ *  HCI_IBS (HCI In-Band Sleep) is Qualcomm's power management
+ *  protocol extension to H4.
+ *
+ *  Copyright (C) 2007 Texas Instruments, Inc.
+ *  Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ *  Acknowledgements:
+ *  This file is based on hci_ll.c, which was...
+ *  Written by Ohad Ben-Cohen <ohad@bencohen.org>
+ *  which was in turn based on hci_h4.c, which was written
+ *  by Maxim Krasnyansky and Marcel Holtmann.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/poll.h>
+
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/signal.h>
+#include <linux/ioctl.h>
+#include <linux/timer.h>
+#include <linux/skbuff.h>
+#include <linux/serial_core.h>
+
+#ifdef CONFIG_SERIAL_MSM_HS
+#include <mach/msm_serial_hs.h>
+#endif
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "hci_uart.h"
+
+/* HCI_IBS protocol messages */
+#define HCI_IBS_SLEEP_IND	0xFE
+#define HCI_IBS_WAKE_IND	0xFD
+#define HCI_IBS_WAKE_ACK	0xFC
+
+/* HCI_IBS receiver States */
+#define HCI_IBS_W4_PACKET_TYPE	0
+#define HCI_IBS_W4_EVENT_HDR	1
+#define HCI_IBS_W4_ACL_HDR	2
+#define HCI_IBS_W4_SCO_HDR	3
+#define HCI_IBS_W4_DATA		4
+
+/* HCI_IBS transmit side sleep protocol states */
+enum tx_ibs_states_e {
+	HCI_IBS_TX_ASLEEP,
+	HCI_IBS_TX_WAKING,
+	HCI_IBS_TX_AWAKE,
+};
+
+/* HCI_IBS receive side sleep protocol states */
+enum rx_states_e {
+	HCI_IBS_RX_ASLEEP,
+	HCI_IBS_RX_AWAKE,
+};
+
+/* HCI_IBS transmit and receive side clock state vote */
+enum hci_ibs_clock_state_vote_e {
+	HCI_IBS_VOTE_STATS_UPDATE,
+	HCI_IBS_TX_VOTE_CLOCK_ON,
+	HCI_IBS_TX_VOTE_CLOCK_OFF,
+	HCI_IBS_RX_VOTE_CLOCK_ON,
+	HCI_IBS_RX_VOTE_CLOCK_OFF,
+};
+
+static unsigned long wake_retrans = 1;
+static unsigned long tx_idle_delay = (HZ * 2);
+
+struct hci_ibs_cmd {
+	u8 cmd;
+} __attribute__((packed));
+
+struct ibs_struct {
+	unsigned long rx_state;
+	unsigned long rx_count;
+	struct sk_buff *rx_skb;
+	struct sk_buff_head txq;
+	struct sk_buff_head tx_wait_q;	/* HCI_IBS wait queue	*/
+	spinlock_t hci_ibs_lock;	/* HCI_IBS state lock	*/
+	unsigned long tx_ibs_state;	/* HCI_IBS transmit side power state */
+	unsigned long rx_ibs_state;	/* HCI_IBS receive side power state */
+	unsigned long tx_vote;		/* clock must be on for TX */
+	unsigned long rx_vote;		/* clock must be on for RX */
+	struct	timer_list tx_idle_timer;
+	struct	timer_list wake_retrans_timer;
+	/* debug */
+	unsigned long ibs_sent_wacks;
+	unsigned long ibs_sent_slps;
+	unsigned long ibs_sent_wakes;
+	unsigned long ibs_recv_wacks;
+	unsigned long ibs_recv_slps;
+	unsigned long ibs_recv_wakes;
+	unsigned long vote_last_jif;
+	unsigned long vote_on_ticks;
+	unsigned long vote_off_ticks;
+	unsigned long tx_votes_on;
+	unsigned long rx_votes_on;
+	unsigned long tx_votes_off;
+	unsigned long rx_votes_off;
+	unsigned long votes_on;
+	unsigned long votes_off;
+};
+
+#ifdef CONFIG_SERIAL_MSM_HS
+static void __ibs_msm_serial_clock_on(struct tty_struct *tty)
+{
+	struct uart_state *state = tty->driver_data;
+	struct uart_port *port = state->uart_port;
+
+	msm_hs_request_clock_on(port);
+}
+
+static void __ibs_msm_serial_clock_request_off(struct tty_struct *tty)
+{
+	struct uart_state *state = tty->driver_data;
+	struct uart_port *port = state->uart_port;
+
+	msm_hs_request_clock_off(port);
+}
+#else
+static inline void __ibs_msm_serial_clock_on(struct tty_struct *tty) {}
+static inline void __ibs_msm_serial_clock_request_off(struct tty_struct *tty) {}
+#endif
+
+/* clock_vote needs to be called with the ibs lock held */
+static void ibs_msm_serial_clock_vote(unsigned long vote, struct hci_uart *hu)
+{
+	struct ibs_struct *ibs = hu->priv;
+
+	unsigned long old_vote = (ibs->tx_vote | ibs->rx_vote);
+	unsigned long new_vote;
+
+	switch (vote) {
+	default: /* error */
+		BT_ERR("voting irregularity");
+		return;
+	case HCI_IBS_VOTE_STATS_UPDATE:
+		if (old_vote)
+			ibs->vote_off_ticks += (jiffies - ibs->vote_last_jif);
+		else
+			ibs->vote_on_ticks += (jiffies - ibs->vote_last_jif);
+		return;
+	case HCI_IBS_TX_VOTE_CLOCK_ON:
+		ibs->tx_vote = 1;
+		ibs->tx_votes_on++;
+		new_vote = 1;
+		break;
+	case HCI_IBS_RX_VOTE_CLOCK_ON:
+		ibs->rx_vote = 1;
+		ibs->rx_votes_on++;
+		new_vote = 1;
+		break;
+	case HCI_IBS_TX_VOTE_CLOCK_OFF:
+		ibs->tx_vote = 0;
+		ibs->tx_votes_off++;
+		new_vote = ibs->rx_vote | ibs->tx_vote;
+		break;
+	case HCI_IBS_RX_VOTE_CLOCK_OFF:
+		ibs->rx_vote = 0;
+		ibs->rx_votes_off++;
+		new_vote = ibs->rx_vote | ibs->tx_vote;
+		break;
+	}
+	if (new_vote != old_vote) {
+		if (new_vote)
+			__ibs_msm_serial_clock_on(hu->tty);
+		else
+			__ibs_msm_serial_clock_request_off(hu->tty);
+
+		BT_DBG("HCIUART_IBS: vote msm_serial_hs clock %lu(%lu)",
+			new_vote, vote);
+		/* debug */
+		if (new_vote) {
+			ibs->votes_on++;
+			ibs->vote_off_ticks += (jiffies - ibs->vote_last_jif);
+		} else {
+			ibs->votes_off++;
+			ibs->vote_on_ticks += (jiffies - ibs->vote_last_jif);
+		}
+		ibs->vote_last_jif = jiffies;
+	}
+}
+
+/*
+ * Builds and sends an HCI_IBS command packet.
+ * These are very simple packets with only 1 cmd byte
+ */
+static int send_hci_ibs_cmd(u8 cmd, struct hci_uart *hu)
+{
+	int err = 0;
+	struct sk_buff *skb = NULL;
+	struct ibs_struct *ibs = hu->priv;
+	struct hci_ibs_cmd *hci_ibs_packet;
+
+	BT_DBG("hu %p cmd 0x%x", hu, cmd);
+
+	/* allocate packet */
+	skb = bt_skb_alloc(1, GFP_ATOMIC);
+	if (!skb) {
+		BT_ERR("cannot allocate memory for HCI_IBS packet");
+		err = -ENOMEM;
+		goto out;
+	}
+
+	/* prepare packet */
+	hci_ibs_packet = (struct hci_ibs_cmd *) skb_put(skb, 1);
+	hci_ibs_packet->cmd = cmd;
+	skb->dev = (void *) hu->hdev;
+
+	/* send packet */
+	skb_queue_tail(&ibs->txq, skb);
+out:
+	return err;
+}
+
+static void hci_ibs_tx_idle_timeout(unsigned long arg)
+{
+	struct hci_uart *hu = (struct hci_uart *) arg;
+	struct ibs_struct *ibs = hu->priv;
+	unsigned long flags;
+	unsigned long vote_tx_sleep = 0;
+
+	BT_DBG("hu %p idle timeout in %lu state", hu, ibs->tx_ibs_state);
+
+	spin_lock_irqsave_nested(&ibs->hci_ibs_lock,
+					flags, SINGLE_DEPTH_NESTING);
+
+	switch (ibs->tx_ibs_state) {
+	default:
+	case HCI_IBS_TX_ASLEEP:
+	case HCI_IBS_TX_WAKING:
+		BT_ERR("spurrious timeout in tx state %ld", ibs->tx_ibs_state);
+		goto out;
+	case HCI_IBS_TX_AWAKE: /* TX_IDLE, go to SLEEP */
+		if (send_hci_ibs_cmd(HCI_IBS_SLEEP_IND, hu) < 0) {
+			BT_ERR("cannot send SLEEP to device");
+			goto out;
+		}
+		ibs->tx_ibs_state = HCI_IBS_TX_ASLEEP;
+		ibs->ibs_sent_slps++; /* debug */
+		vote_tx_sleep = 1;
+		break;
+	}
+
+	spin_unlock_irqrestore(&ibs->hci_ibs_lock, flags);
+
+	hci_uart_tx_wakeup(hu);  /* run HCI tx handling unlocked */
+
+	if (!vote_tx_sleep)
+		return;
+	/* now that message queued to tty driver, vote for tty clocks off */
+	/* It is up to the tty driver to pend the clocks off until tx done. */
+
+	spin_lock_irqsave_nested(&ibs->hci_ibs_lock,
+					flags, SINGLE_DEPTH_NESTING);
+	ibs_msm_serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_OFF, hu);
+out:
+	spin_unlock_irqrestore(&ibs->hci_ibs_lock, flags);
+}
+
+static void hci_ibs_wake_retrans_timeout(unsigned long arg)
+{
+	struct hci_uart *hu = (struct hci_uart *) arg;
+	struct ibs_struct *ibs = hu->priv;
+	unsigned long flags;
+	unsigned long retransmit = 0;
+
+	BT_DBG("hu %p wake retransmit timeout in %lu state",
+		hu, ibs->tx_ibs_state);
+
+	spin_lock_irqsave_nested(&ibs->hci_ibs_lock,
+					flags, SINGLE_DEPTH_NESTING);
+
+	switch (ibs->tx_ibs_state) {
+	default:
+	case HCI_IBS_TX_ASLEEP:
+	case HCI_IBS_TX_AWAKE:
+		BT_ERR("spurrious timeout tx state %ld", ibs->tx_ibs_state);
+		goto out;
+	case HCI_IBS_TX_WAKING: /* No WAKE_ACK, retransmit WAKE */
+		retransmit = 1;
+		if (send_hci_ibs_cmd(HCI_IBS_WAKE_IND, hu) < 0) {
+			BT_ERR("cannot acknowledge device wake up");
+			goto out;
+		}
+		ibs->ibs_sent_wakes++; /* debug */
+		mod_timer(&ibs->wake_retrans_timer, jiffies + wake_retrans);
+		break;
+	}
+out:
+	spin_unlock_irqrestore(&ibs->hci_ibs_lock, flags);
+	if (retransmit)
+		hci_uart_tx_wakeup(hu);
+}
+
+/* Initialize protocol */
+static int ibs_open(struct hci_uart *hu)
+{
+	struct ibs_struct *ibs;
+
+	BT_DBG("hu %p", hu);
+
+	ibs = kzalloc(sizeof(*ibs), GFP_ATOMIC);
+	if (!ibs)
+		return -ENOMEM;
+
+	skb_queue_head_init(&ibs->txq);
+	skb_queue_head_init(&ibs->tx_wait_q);
+	spin_lock_init(&ibs->hci_ibs_lock);
+
+	/* Assume we start with both sides asleep -- extra wakes OK */
+	ibs->tx_ibs_state = HCI_IBS_TX_ASLEEP;
+	ibs->rx_ibs_state = HCI_IBS_RX_ASLEEP;
+	/* clocks actually on, but we start votes off */
+	ibs->tx_vote = 0;
+	ibs->rx_vote = 0;
+
+	/* debug */
+	ibs->ibs_sent_wacks = 0;
+	ibs->ibs_sent_slps = 0;
+	ibs->ibs_sent_wakes = 0;
+	ibs->ibs_recv_wacks = 0;
+	ibs->ibs_recv_slps = 0;
+	ibs->ibs_recv_wakes = 0;
+	ibs->vote_last_jif = jiffies;
+	ibs->vote_on_ticks = 0;
+	ibs->vote_off_ticks = 0;
+	ibs->votes_on = 0;
+	ibs->votes_off = 0;
+	ibs->tx_votes_on = 0;
+	ibs->tx_votes_off = 0;
+	ibs->rx_votes_on = 0;
+	ibs->rx_votes_off = 0;
+
+	hu->priv = ibs;
+
+	init_timer(&ibs->wake_retrans_timer);
+	ibs->wake_retrans_timer.function = hci_ibs_wake_retrans_timeout;
+	ibs->wake_retrans_timer.data     = (u_long) hu;
+
+	init_timer(&ibs->tx_idle_timer);
+	ibs->tx_idle_timer.function = hci_ibs_tx_idle_timeout;
+	ibs->tx_idle_timer.data     = (u_long) hu;
+
+	BT_INFO("HCI_IBS open, tx_idle_delay=%lu, wake_retrans=%lu",
+		tx_idle_delay, wake_retrans);
+
+	return 0;
+}
+
+void ibs_log_local_stats(struct ibs_struct *ibs)
+{
+	BT_INFO("HCI_IBS stats: tx_idle_delay=%lu, wake_retrans=%lu",
+		tx_idle_delay, wake_retrans);
+
+	BT_INFO("HCI_IBS stats: tx_ibs_state=%lu, rx_ibs_state=%lu",
+		ibs->tx_ibs_state, ibs->rx_ibs_state);
+	BT_INFO("HCI_IBS stats: sent: sleep=%lu, wake=%lu, wake_ack=%lu",
+		ibs->ibs_sent_slps, ibs->ibs_sent_wakes, ibs->ibs_sent_wacks);
+	BT_INFO("HCI_IBS stats: recv: sleep=%lu, wake=%lu, wake_ack=%lu",
+		ibs->ibs_recv_slps, ibs->ibs_recv_wakes, ibs->ibs_recv_wacks);
+
+	BT_INFO("HCI_IBS stats: queues: txq=%s, txwaitq=%s",
+		skb_queue_empty(&(ibs->txq)) ? "empty" : "full",
+		skb_queue_empty(&(ibs->tx_wait_q)) ? "empty" : "full");
+
+	BT_INFO("HCI_IBS stats: vote state: tx=%lu, rx=%lu",
+		ibs->tx_vote, ibs->rx_vote);
+	BT_INFO("HCI_IBS stats: tx votes cast: on=%lu, off=%lu",
+		ibs->tx_votes_on, ibs->tx_votes_off);
+	BT_INFO("HCI_IBS stats: rx votes cast: on=%lu, off=%lu",
+		ibs->rx_votes_on, ibs->rx_votes_off);
+	BT_INFO("HCI_IBS stats: msm_clock votes cast: on=%lu, off=%lu",
+		ibs->votes_on, ibs->votes_off);
+	BT_INFO("HCI_IBS stats: vote ticks: on=%lu, off=%lu",
+		ibs->vote_on_ticks, ibs->vote_off_ticks);
+}
+
+/* Flush protocol data */
+static int ibs_flush(struct hci_uart *hu)
+{
+	struct ibs_struct *ibs = hu->priv;
+
+	BT_DBG("hu %p", hu);
+
+	skb_queue_purge(&ibs->tx_wait_q);
+	skb_queue_purge(&ibs->txq);
+
+	return 0;
+}
+
+/* Close protocol */
+static int ibs_close(struct hci_uart *hu)
+{
+	struct ibs_struct *ibs = hu->priv;
+
+	BT_DBG("hu %p", hu);
+
+	ibs_msm_serial_clock_vote(HCI_IBS_VOTE_STATS_UPDATE, hu);
+	ibs_log_local_stats(ibs);
+
+	skb_queue_purge(&ibs->tx_wait_q);
+	skb_queue_purge(&ibs->txq);
+	del_timer(&ibs->tx_idle_timer);
+	del_timer(&ibs->wake_retrans_timer);
+
+	kfree_skb(ibs->rx_skb);
+
+	hu->priv = NULL;
+
+	kfree(ibs);
+
+	return 0;
+}
+
+/*
+ * Called upon a wake-up-indication from the device
+ */
+static void ibs_device_want_to_wakeup(struct hci_uart *hu)
+{
+	unsigned long flags;
+	struct ibs_struct *ibs = hu->priv;
+
+	BT_DBG("hu %p", hu);
+
+	/* lock hci_ibs state */
+	spin_lock_irqsave(&ibs->hci_ibs_lock, flags);
+
+	/* debug */
+	ibs->ibs_recv_wakes++;
+
+	switch (ibs->rx_ibs_state) {
+	case HCI_IBS_RX_ASLEEP:
+		/* Make sure clock is on - we may have turned clock off since
+		 * receiving the wake up indicator
+		 */
+		ibs_msm_serial_clock_vote(HCI_IBS_RX_VOTE_CLOCK_ON, hu);
+		ibs->rx_ibs_state = HCI_IBS_RX_AWAKE;
+		/* deliberate fall-through */
+	case HCI_IBS_RX_AWAKE:
+		/* Always acknowledge device wake up,
+		 * sending IBS message doesn't count as TX ON.
+		 */
+		if (send_hci_ibs_cmd(HCI_IBS_WAKE_ACK, hu) < 0) {
+			BT_ERR("cannot acknowledge device wake up");
+			goto out;
+		}
+		ibs->ibs_sent_wacks++; /* debug */
+		break;
+	default:
+		/* any other state is illegal */
+		BT_ERR("received HCI_IBS_WAKE_IND in rx state %ld",
+			ibs->rx_ibs_state);
+		break;
+	}
+
+out:
+	spin_unlock_irqrestore(&ibs->hci_ibs_lock, flags);
+
+	/* actually send the packets */
+	hci_uart_tx_wakeup(hu);
+}
+
+/*
+ * Called upon a sleep-indication from the device
+ */
+static void ibs_device_want_to_sleep(struct hci_uart *hu)
+{
+	unsigned long flags;
+	struct ibs_struct *ibs = hu->priv;
+
+	BT_DBG("hu %p", hu);
+
+	/* lock hci_ibs state */
+	spin_lock_irqsave(&ibs->hci_ibs_lock, flags);
+
+	/* debug */
+	ibs->ibs_recv_slps++;
+
+	switch (ibs->rx_ibs_state) {
+	case HCI_IBS_RX_AWAKE:
+		/* update state */
+		ibs->rx_ibs_state = HCI_IBS_RX_ASLEEP;
+		ibs_msm_serial_clock_vote(HCI_IBS_RX_VOTE_CLOCK_OFF, hu);
+		break;
+	case HCI_IBS_RX_ASLEEP:
+		/* deliberate fall-through */
+	default:
+		/* any other state is illegal */
+		BT_ERR("received HCI_IBS_SLEEP_IND in rx state %ld",
+			ibs->rx_ibs_state);
+		break;
+	}
+
+	spin_unlock_irqrestore(&ibs->hci_ibs_lock, flags);
+}
+
+/*
+ * Called upon wake-up-acknowledgement from the device
+ */
+static void ibs_device_woke_up(struct hci_uart *hu)
+{
+	unsigned long flags;
+	struct ibs_struct *ibs = hu->priv;
+	struct sk_buff *skb = NULL;
+
+	BT_DBG("hu %p", hu);
+
+	/* lock hci_ibs state */
+	spin_lock_irqsave(&ibs->hci_ibs_lock, flags);
+
+	/* debug */
+	ibs->ibs_recv_wacks++;
+
+	switch (ibs->tx_ibs_state) {
+	case HCI_IBS_TX_ASLEEP:
+		/* This could be spurrious rx wake on the BT chip.
+		 * Send it another SLEEP othwise it will stay awake. */
+	default:
+		BT_ERR("received HCI_IBS_WAKE_ACK in tx state %ld",
+			ibs->tx_ibs_state);
+		break;
+	case HCI_IBS_TX_AWAKE:
+		/* expect one if we send 2 WAKEs */
+		BT_DBG("received HCI_IBS_WAKE_ACK in tx state %ld",
+			ibs->tx_ibs_state);
+		break;
+	case HCI_IBS_TX_WAKING:
+		/* send pending packets */
+		while ((skb = skb_dequeue(&ibs->tx_wait_q)))
+			skb_queue_tail(&ibs->txq, skb);
+		/* switch timers and change state to HCI_IBS_TX_AWAKE */
+		del_timer(&ibs->wake_retrans_timer);
+		mod_timer(&ibs->tx_idle_timer, jiffies + tx_idle_delay);
+		ibs->tx_ibs_state = HCI_IBS_TX_AWAKE;
+	}
+
+	spin_unlock_irqrestore(&ibs->hci_ibs_lock, flags);
+
+	/* actually send the packets */
+	hci_uart_tx_wakeup(hu);
+}
+
+/* Enqueue frame for transmittion (padding, crc, etc) */
+/* may be called from two simultaneous tasklets */
+static int ibs_enqueue(struct hci_uart *hu, struct sk_buff *skb)
+{
+	unsigned long flags = 0;
+	struct ibs_struct *ibs = hu->priv;
+
+	BT_DBG("hu %p skb %p", hu, skb);
+
+	/* Prepend skb with frame type */
+	memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+
+	/* lock hci_ibs state */
+	spin_lock_irqsave(&ibs->hci_ibs_lock, flags);
+
+	/* act according to current state */
+	switch (ibs->tx_ibs_state) {
+	case HCI_IBS_TX_AWAKE:
+		BT_DBG("device awake, sending normally");
+		skb_queue_tail(&ibs->txq, skb);
+		mod_timer(&ibs->tx_idle_timer, jiffies + tx_idle_delay);
+		break;
+
+	case HCI_IBS_TX_ASLEEP:
+		BT_DBG("device asleep, waking up and queueing packet");
+		ibs_msm_serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_ON, hu);
+		/* save packet for later */
+		skb_queue_tail(&ibs->tx_wait_q, skb);
+		/* awake device */
+		if (send_hci_ibs_cmd(HCI_IBS_WAKE_IND, hu) < 0) {
+			BT_ERR("cannot send WAKE to device");
+			break;
+		}
+		ibs->ibs_sent_wakes++; /* debug */
+
+		/* start retransmit timer */
+		mod_timer(&ibs->wake_retrans_timer, jiffies + wake_retrans);
+
+		ibs->tx_ibs_state = HCI_IBS_TX_WAKING;
+		break;
+
+	case HCI_IBS_TX_WAKING:
+		BT_DBG("device waking up, queueing packet");
+		/* transient state; just keep packet for later */
+		skb_queue_tail(&ibs->tx_wait_q, skb);
+		break;
+
+	default:
+		BT_ERR("illegal tx state: %ld (losing packet)",
+			ibs->tx_ibs_state);
+		kfree_skb(skb);
+		break;
+	}
+
+	spin_unlock_irqrestore(&ibs->hci_ibs_lock, flags);
+
+	return 0;
+}
+
+static inline int ibs_check_data_len(struct ibs_struct *ibs, int len)
+{
+	register int room = skb_tailroom(ibs->rx_skb);
+
+	BT_DBG("len %d room %d", len, room);
+
+	if (!len) {
+		hci_recv_frame(ibs->rx_skb);
+	} else if (len > room) {
+		BT_ERR("Data length is too large");
+		kfree_skb(ibs->rx_skb);
+	} else {
+		ibs->rx_state = HCI_IBS_W4_DATA;
+		ibs->rx_count = len;
+		return len;
+	}
+
+	ibs->rx_state = HCI_IBS_W4_PACKET_TYPE;
+	ibs->rx_skb   = NULL;
+	ibs->rx_count = 0;
+
+	return 0;
+}
+
+/* Recv data */
+static int ibs_recv(struct hci_uart *hu, void *data, int count)
+{
+	struct ibs_struct *ibs = hu->priv;
+	register char *ptr;
+	struct hci_event_hdr *eh;
+	struct hci_acl_hdr   *ah;
+	struct hci_sco_hdr   *sh;
+	register int len, type, dlen;
+
+	BT_DBG("hu %p count %d rx_state %ld rx_count %ld",
+			hu, count, ibs->rx_state, ibs->rx_count);
+
+	ptr = data;
+	while (count) {
+		if (ibs->rx_count) {
+			len = min_t(unsigned int, ibs->rx_count, count);
+			memcpy(skb_put(ibs->rx_skb, len), ptr, len);
+			ibs->rx_count -= len; count -= len; ptr += len;
+
+			if (ibs->rx_count)
+				continue;
+
+			switch (ibs->rx_state) {
+			case HCI_IBS_W4_DATA:
+				BT_DBG("Complete data");
+				hci_recv_frame(ibs->rx_skb);
+
+				ibs->rx_state = HCI_IBS_W4_PACKET_TYPE;
+				ibs->rx_skb = NULL;
+				continue;
+
+			case HCI_IBS_W4_EVENT_HDR:
+				eh = (struct hci_event_hdr *) ibs->rx_skb->data;
+
+				BT_DBG("Event header: evt 0x%2.2x plen %d",
+					eh->evt, eh->plen);
+
+				ibs_check_data_len(ibs, eh->plen);
+				continue;
+
+			case HCI_IBS_W4_ACL_HDR:
+				ah = (struct hci_acl_hdr *) ibs->rx_skb->data;
+				dlen = __le16_to_cpu(ah->dlen);
+
+				BT_DBG("ACL header: dlen %d", dlen);
+
+				ibs_check_data_len(ibs, dlen);
+				continue;
+
+			case HCI_IBS_W4_SCO_HDR:
+				sh = (struct hci_sco_hdr *) ibs->rx_skb->data;
+
+				BT_DBG("SCO header: dlen %d", sh->dlen);
+
+				ibs_check_data_len(ibs, sh->dlen);
+				continue;
+			}
+		}
+
+		/* HCI_IBS_W4_PACKET_TYPE */
+		switch (*ptr) {
+		case HCI_EVENT_PKT:
+			BT_DBG("Event packet");
+			ibs->rx_state = HCI_IBS_W4_EVENT_HDR;
+			ibs->rx_count = HCI_EVENT_HDR_SIZE;
+			type = HCI_EVENT_PKT;
+			break;
+
+		case HCI_ACLDATA_PKT:
+			BT_DBG("ACL packet");
+			ibs->rx_state = HCI_IBS_W4_ACL_HDR;
+			ibs->rx_count = HCI_ACL_HDR_SIZE;
+			type = HCI_ACLDATA_PKT;
+			break;
+
+		case HCI_SCODATA_PKT:
+			BT_DBG("SCO packet");
+			ibs->rx_state = HCI_IBS_W4_SCO_HDR;
+			ibs->rx_count = HCI_SCO_HDR_SIZE;
+			type = HCI_SCODATA_PKT;
+			break;
+
+		/* HCI_IBS signals */
+		case HCI_IBS_SLEEP_IND:
+			BT_DBG("HCI_IBS_SLEEP_IND packet");
+			ibs_device_want_to_sleep(hu);
+			ptr++; count--;
+			continue;
+
+		case HCI_IBS_WAKE_IND:
+			BT_DBG("HCI_IBS_WAKE_IND packet");
+			ibs_device_want_to_wakeup(hu);
+			ptr++; count--;
+			continue;
+
+		case HCI_IBS_WAKE_ACK:
+			BT_DBG("HCI_IBS_WAKE_ACK packet");
+			ibs_device_woke_up(hu);
+			ptr++; count--;
+			continue;
+
+		default:
+			BT_ERR("Unknown HCI packet type %2.2x", (__u8)*ptr);
+			hu->hdev->stat.err_rx++;
+			ptr++; count--;
+			continue;
+		};
+
+		ptr++; count--;
+
+		/* Allocate packet */
+		ibs->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
+		if (!ibs->rx_skb) {
+			BT_ERR("Can't allocate mem for new packet");
+			ibs->rx_state = HCI_IBS_W4_PACKET_TYPE;
+			ibs->rx_count = 0;
+			return 0;
+		}
+
+		ibs->rx_skb->dev = (void *) hu->hdev;
+		bt_cb(ibs->rx_skb)->pkt_type = type;
+	}
+
+	return count;
+}
+
+static struct sk_buff *ibs_dequeue(struct hci_uart *hu)
+{
+	struct ibs_struct *ibs = hu->priv;
+	return skb_dequeue(&ibs->txq);
+}
+
+static struct hci_uart_proto ibs_p = {
+	.id		= HCI_UART_IBS,
+	.open		= ibs_open,
+	.close		= ibs_close,
+	.recv		= ibs_recv,
+	.enqueue	= ibs_enqueue,
+	.dequeue	= ibs_dequeue,
+	.flush		= ibs_flush,
+};
+
+int ibs_init(void)
+{
+	int err = hci_uart_register_proto(&ibs_p);
+
+	if (!err)
+		BT_INFO("HCI_IBS protocol initialized");
+	else
+		BT_ERR("HCI_IBS protocol registration failed");
+
+	return err;
+}
+
+int ibs_deinit(void)
+{
+	return hci_uart_unregister_proto(&ibs_p);
+}
+
+module_param(wake_retrans, ulong, 0644);
+MODULE_PARM_DESC(wake_retrans, "Delay (1/HZ) to retransmit WAKE_IND");
+
+module_param(tx_idle_delay, ulong, 0644);
+MODULE_PARM_DESC(tx_idle_delay, "Delay (1/HZ) since last tx for SLEEP_IND");
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index 98a8c05..121bf7c9 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -2,9 +2,9 @@
  *
  *  Bluetooth HCI UART driver
  *
- *  Copyright (C) 2000-2001  Qualcomm Incorporated
  *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
  *  Copyright (C) 2004-2005  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (c) 2000-2001, 2010-2011, Code Aurora Forum. All rights reserved.
  *
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -48,6 +48,8 @@
 
 #define VERSION "2.2"
 
+static bool reset = 0;
+
 static struct hci_uart_proto *hup[HCI_UART_MAX_PROTO];
 
 int hci_uart_register_proto(struct hci_uart_proto *p)
@@ -172,7 +174,7 @@
 /* Reset device */
 static int hci_uart_flush(struct hci_dev *hdev)
 {
-	struct hci_uart *hu  = hci_get_drvdata(hdev);
+	struct hci_uart *hu  = (struct hci_uart *) hdev->driver_data;
 	struct tty_struct *tty = hu->tty;
 
 	BT_DBG("hdev %p tty %p", hdev, tty);
@@ -218,7 +220,7 @@
 	if (!test_bit(HCI_RUNNING, &hdev->flags))
 		return -EBUSY;
 
-	hu = hci_get_drvdata(hdev);
+	hu = (struct hci_uart *) hdev->driver_data;
 
 	BT_DBG("%s: type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len);
 
@@ -229,6 +231,15 @@
 	return 0;
 }
 
+static void hci_uart_destruct(struct hci_dev *hdev)
+{
+	if (!hdev)
+		return;
+
+	BT_DBG("%s", hdev->name);
+	kfree(hdev->driver_data);
+}
+
 /* ------ LDISC part ------ */
 /* hci_uart_tty_open
  * 
@@ -299,14 +310,12 @@
 			hci_uart_close(hdev);
 
 		if (test_and_clear_bit(HCI_UART_PROTO_SET, &hu->flags)) {
+			hu->proto->close(hu);
 			if (hdev) {
 				hci_unregister_dev(hdev);
 				hci_free_dev(hdev);
 			}
-			hu->proto->close(hu);
 		}
-
-		kfree(hu);
 	}
 }
 
@@ -350,6 +359,7 @@
  */
 static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data, char *flags, int count)
 {
+	int ret;
 	struct hci_uart *hu = (void *)tty->disc_data;
 
 	if (!hu || tty != hu->tty)
@@ -359,8 +369,9 @@
 		return;
 
 	spin_lock(&hu->rx_lock);
-	hu->proto->recv(hu, (void *) data, count);
-	hu->hdev->stat.byte_rx += count;
+	ret = hu->proto->recv(hu, (void *) data, count);
+	if (ret > 0)
+		hu->hdev->stat.byte_rx += count;
 	spin_unlock(&hu->rx_lock);
 
 	tty_unthrottle(tty);
@@ -382,25 +393,23 @@
 	hu->hdev = hdev;
 
 	hdev->bus = HCI_UART;
-	hci_set_drvdata(hdev, hu);
+	hdev->driver_data = hu;
 
 	hdev->open  = hci_uart_open;
 	hdev->close = hci_uart_close;
 	hdev->flush = hci_uart_flush;
 	hdev->send  = hci_uart_send_frame;
+	hdev->destruct = hci_uart_destruct;
 	hdev->parent = hu->tty->dev;
 
+	hdev->owner = THIS_MODULE;
+
+	if (!reset)
+		set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks);
+
 	if (test_bit(HCI_UART_RAW_DEVICE, &hu->hdev_flags))
 		set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
 
-	if (!test_bit(HCI_UART_RESET_ON_INIT, &hu->hdev_flags))
-		set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks);
-
-	if (test_bit(HCI_UART_CREATE_AMP, &hu->hdev_flags))
-		hdev->dev_type = HCI_AMP;
-	else
-		hdev->dev_type = HCI_BREDR;
-
 	if (hci_register_dev(hdev) < 0) {
 		BT_ERR("Can't register HCI device");
 		hci_free_dev(hdev);
@@ -461,11 +470,18 @@
 
 	switch (cmd) {
 	case HCIUARTSETPROTO:
-		if (!test_and_set_bit(HCI_UART_PROTO_SET, &hu->flags)) {
+		if (!test_and_set_bit(HCI_UART_PROTO_SET_IN_PROGRESS,
+			&hu->flags) && !test_bit(HCI_UART_PROTO_SET,
+				&hu->flags)) {
 			err = hci_uart_set_proto(hu, arg);
 			if (err) {
-				clear_bit(HCI_UART_PROTO_SET, &hu->flags);
+				clear_bit(HCI_UART_PROTO_SET_IN_PROGRESS,
+						&hu->flags);
 				return err;
+			} else {
+				set_bit(HCI_UART_PROTO_SET, &hu->flags);
+				clear_bit(HCI_UART_PROTO_SET_IN_PROGRESS,
+						&hu->flags);
 			}
 		} else
 			return -EBUSY;
@@ -558,6 +574,9 @@
 #ifdef CONFIG_BT_HCIUART_ATH3K
 	ath_init();
 #endif
+#ifdef CONFIG_BT_HCIUART_IBS
+	ibs_init();
+#endif
 
 	return 0;
 }
@@ -578,6 +597,9 @@
 #ifdef CONFIG_BT_HCIUART_ATH3K
 	ath_deinit();
 #endif
+#ifdef CONFIG_BT_HCIUART_IBS
+	ibs_deinit();
+#endif
 
 	/* Release tty registration of line discipline */
 	if ((err = tty_unregister_ldisc(N_HCI)))
@@ -587,6 +609,9 @@
 module_init(hci_uart_init);
 module_exit(hci_uart_exit);
 
+module_param(reset, bool, 0644);
+MODULE_PARM_DESC(reset, "Send HCI reset command on initialization");
+
 MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
 MODULE_DESCRIPTION("Bluetooth HCI UART driver ver " VERSION);
 MODULE_VERSION(VERSION);
diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c
index b874c0e..38595e7 100644
--- a/drivers/bluetooth/hci_ll.c
+++ b/drivers/bluetooth/hci_ll.c
@@ -125,7 +125,7 @@
 
 	BT_DBG("hu %p", hu);
 
-	ll = kzalloc(sizeof(*ll), GFP_KERNEL);
+	ll = kzalloc(sizeof(*ll), GFP_ATOMIC);
 	if (!ll)
 		return -ENOMEM;
 
@@ -207,7 +207,7 @@
 		/*
 		 * This state means that both the host and the BRF chip
 		 * have simultaneously sent a wake-up-indication packet.
-		 * Traditionally, in this case, receiving a wake-up-indication
+		 * Traditionaly, in this case, receiving a wake-up-indication
 		 * was enough and an additional wake-up-ack wasn't needed.
 		 * This has changed with the BRF6350, which does require an
 		 * explicit wake-up-ack. Other BRF versions, which do not
diff --git a/drivers/bluetooth/hci_smd.c b/drivers/bluetooth/hci_smd.c
new file mode 100644
index 0000000..b6de881
--- /dev/null
+++ b/drivers/bluetooth/hci_smd.c
@@ -0,0 +1,561 @@
+/*
+ *  HCI_SMD (HCI Shared Memory Driver) is Qualcomm's Shared memory driver
+ *  for the BT HCI protocol.
+ *
+ *  Copyright (c) 2000-2001, 2011-2012 Code Aurora Forum. All rights reserved.
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2004-2006  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *  This file is based on drivers/bluetooth/hci_vhci.c
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/skbuff.h>
+#include <linux/wakelock.h>
+#include <linux/workqueue.h>
+#include <linux/uaccess.h>
+#include <linux/interrupt.h>
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/hci.h>
+#include <mach/msm_smd.h>
+
+#define EVENT_CHANNEL		"APPS_RIVA_BT_CMD"
+#define DATA_CHANNEL		"APPS_RIVA_BT_ACL"
+/* release wakelock in 500ms, not immediately, because higher layers
+ * don't always take wakelocks when they should
+ * This is derived from the implementation for UART transport
+ */
+
+#define RX_Q_MONITOR		(500)	/* 500 milli second */
+
+
+static int hcismd_set;
+static DEFINE_MUTEX(hci_smd_enable);
+
+static int hcismd_set_enable(const char *val, struct kernel_param *kp);
+module_param_call(hcismd_set, hcismd_set_enable, NULL, &hcismd_set, 0644);
+
+static void hci_dev_smd_open(struct work_struct *worker);
+static void hci_dev_restart(struct work_struct *worker);
+
+struct hci_smd_data {
+	struct hci_dev *hdev;
+
+	struct smd_channel *event_channel;
+	struct smd_channel *data_channel;
+	struct wake_lock wake_lock_tx;
+	struct wake_lock wake_lock_rx;
+	struct timer_list rx_q_timer;
+	struct tasklet_struct rx_task;
+};
+static struct hci_smd_data hs;
+
+/* Rx queue monitor timer function */
+static int is_rx_q_empty(unsigned long arg)
+{
+	struct hci_dev *hdev = (struct hci_dev *) arg;
+	struct sk_buff_head *list_ = &hdev->rx_q;
+	struct sk_buff *list = ((struct sk_buff *)list_)->next;
+	BT_DBG("%s Rx timer triggered", hdev->name);
+
+	if (list == (struct sk_buff *)list_) {
+		BT_DBG("%s RX queue empty", hdev->name);
+		return 1;
+	} else{
+		BT_DBG("%s RX queue not empty", hdev->name);
+		return 0;
+	}
+}
+
+static void release_lock(void)
+{
+	struct hci_smd_data *hsmd = &hs;
+	BT_DBG("Releasing Rx Lock");
+	if (is_rx_q_empty((unsigned long)hsmd->hdev) &&
+		wake_lock_active(&hs.wake_lock_rx))
+			wake_unlock(&hs.wake_lock_rx);
+}
+
+/* Rx timer callback function */
+static void schedule_timer(unsigned long arg)
+{
+	struct hci_dev *hdev = (struct hci_dev *) arg;
+	struct hci_smd_data *hsmd = &hs;
+	BT_DBG("%s Schedule Rx timer", hdev->name);
+
+	if (is_rx_q_empty(arg) && wake_lock_active(&hs.wake_lock_rx)) {
+		BT_DBG("%s RX queue empty", hdev->name);
+		/*
+		 * Since the queue is empty, its ideal
+		 * to release the wake lock on Rx
+		 */
+		wake_unlock(&hs.wake_lock_rx);
+	} else{
+		BT_DBG("%s RX queue not empty", hdev->name);
+		/*
+		 * Restart the timer to monitor whether the Rx queue is
+		 * empty for releasing the Rx wake lock
+		 */
+		mod_timer(&hsmd->rx_q_timer,
+			jiffies + msecs_to_jiffies(RX_Q_MONITOR));
+	}
+}
+
+static int hci_smd_open(struct hci_dev *hdev)
+{
+	set_bit(HCI_RUNNING, &hdev->flags);
+	return 0;
+}
+
+
+static int hci_smd_close(struct hci_dev *hdev)
+{
+	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+		return 0;
+	else
+		return -EPERM;
+}
+
+
+static void hci_smd_destruct(struct hci_dev *hdev)
+{
+	if (NULL != hdev->driver_data)
+		kfree(hdev->driver_data);
+}
+
+static void hci_smd_recv_data(void)
+{
+	int len = 0;
+	int rc = 0;
+	struct sk_buff *skb = NULL;
+	struct hci_smd_data *hsmd = &hs;
+	wake_lock(&hs.wake_lock_rx);
+
+	len = smd_read_avail(hsmd->data_channel);
+	if (len > HCI_MAX_FRAME_SIZE) {
+		BT_ERR("Frame larger than the allowed size, flushing frame");
+		smd_read(hsmd->data_channel, NULL, len);
+		goto out_data;
+	}
+
+	if (len <= 0)
+		goto out_data;
+
+	skb = bt_skb_alloc(len, GFP_ATOMIC);
+	if (!skb) {
+		BT_ERR("Error in allocating socket buffer");
+		smd_read(hsmd->data_channel, NULL, len);
+		goto out_data;
+	}
+
+	rc = smd_read(hsmd->data_channel, skb_put(skb, len), len);
+	if (rc < len) {
+		BT_ERR("Error in reading from the channel");
+		goto out_data;
+	}
+
+	skb->dev = (void *)hsmd->hdev;
+	bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
+	skb_orphan(skb);
+
+	rc = hci_recv_frame(skb);
+	if (rc < 0) {
+		BT_ERR("Error in passing the packet to HCI Layer");
+		/*
+		 * skb is getting freed in hci_recv_frame, making it
+		 * to null to avoid multiple access
+		 */
+		skb = NULL;
+		goto out_data;
+	}
+
+	/*
+	 * Start the timer to monitor whether the Rx queue is
+	 * empty for releasing the Rx wake lock
+	 */
+	BT_DBG("Rx Timer is starting");
+	mod_timer(&hsmd->rx_q_timer,
+			jiffies + msecs_to_jiffies(RX_Q_MONITOR));
+
+out_data:
+	release_lock();
+	if (rc)
+		kfree_skb(skb);
+}
+
+static void hci_smd_recv_event(void)
+{
+	int len = 0;
+	int rc = 0;
+	struct sk_buff *skb = NULL;
+	struct hci_smd_data *hsmd = &hs;
+	wake_lock(&hs.wake_lock_rx);
+
+	len = smd_read_avail(hsmd->event_channel);
+	if (len > HCI_MAX_FRAME_SIZE) {
+		BT_ERR("Frame larger than the allowed size, flushing frame");
+		rc = smd_read(hsmd->event_channel, NULL, len);
+		goto out_event;
+	}
+
+	while (len > 0) {
+		skb = bt_skb_alloc(len, GFP_ATOMIC);
+		if (!skb) {
+			BT_ERR("Error in allocating socket buffer");
+			smd_read(hsmd->event_channel, NULL, len);
+			goto out_event;
+		}
+
+		rc = smd_read(hsmd->event_channel, skb_put(skb, len), len);
+		if (rc < len) {
+			BT_ERR("Error in reading from the event channel");
+			goto out_event;
+		}
+
+		skb->dev = (void *)hsmd->hdev;
+		bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
+
+		skb_orphan(skb);
+
+		rc = hci_recv_frame(skb);
+		if (rc < 0) {
+			BT_ERR("Error in passing the packet to HCI Layer");
+			/*
+			 * skb is getting freed in hci_recv_frame, making it
+			 *  to null to avoid multiple access
+			 */
+			skb = NULL;
+			goto out_event;
+		}
+
+		len = smd_read_avail(hsmd->event_channel);
+		/*
+		 * Start the timer to monitor whether the Rx queue is
+		 * empty for releasing the Rx wake lock
+		 */
+		BT_DBG("Rx Timer is starting");
+		mod_timer(&hsmd->rx_q_timer,
+				jiffies + msecs_to_jiffies(RX_Q_MONITOR));
+	}
+out_event:
+	release_lock();
+	if (rc)
+		kfree_skb(skb);
+}
+
+static int hci_smd_send_frame(struct sk_buff *skb)
+{
+	int len;
+	int avail;
+	int ret = 0;
+	wake_lock(&hs.wake_lock_tx);
+
+	switch (bt_cb(skb)->pkt_type) {
+	case HCI_COMMAND_PKT:
+		avail = smd_write_avail(hs.event_channel);
+		if (!avail) {
+			BT_ERR("No space available for smd frame");
+			ret =  -ENOSPC;
+		}
+		len = smd_write(hs.event_channel, skb->data, skb->len);
+		if (len < skb->len) {
+			BT_ERR("Failed to write Command %d", len);
+			ret = -ENODEV;
+		}
+		break;
+	case HCI_ACLDATA_PKT:
+	case HCI_SCODATA_PKT:
+		avail = smd_write_avail(hs.data_channel);
+		if (!avail) {
+			BT_ERR("No space available for smd frame");
+			ret = -ENOSPC;
+		}
+		len = smd_write(hs.data_channel, skb->data, skb->len);
+		if (len < skb->len) {
+			BT_ERR("Failed to write Data %d", len);
+			ret = -ENODEV;
+		}
+		break;
+	default:
+		BT_ERR("Uknown packet type");
+		ret = -ENODEV;
+		break;
+	}
+
+	kfree_skb(skb);
+	wake_unlock(&hs.wake_lock_tx);
+	return ret;
+}
+
+static void hci_smd_rx(unsigned long arg)
+{
+	struct hci_smd_data *hsmd = &hs;
+
+	while ((smd_read_avail(hsmd->event_channel) > 0) ||
+				(smd_read_avail(hsmd->data_channel) > 0)) {
+		hci_smd_recv_event();
+		hci_smd_recv_data();
+	}
+}
+
+static void hci_smd_notify_event(void *data, unsigned int event)
+{
+	struct hci_dev *hdev = hs.hdev;
+	struct hci_smd_data *hsmd = &hs;
+	struct work_struct *reset_worker;
+	struct work_struct *open_worker;
+
+	int len = 0;
+
+	if (!hdev) {
+		BT_ERR("Frame for unknown HCI device (hdev=NULL)");
+		return;
+	}
+
+	switch (event) {
+	case SMD_EVENT_DATA:
+		len = smd_read_avail(hsmd->event_channel);
+		if (len > 0)
+			tasklet_hi_schedule(&hs.rx_task);
+		else if (len < 0)
+			BT_ERR("Failed to read event from smd %d", len);
+
+		break;
+	case SMD_EVENT_OPEN:
+		BT_INFO("opening HCI-SMD channel :%s", EVENT_CHANNEL);
+		hci_smd_open(hdev);
+		open_worker = kzalloc(sizeof(*open_worker), GFP_ATOMIC);
+		if (!open_worker) {
+			BT_ERR("Out of memory");
+			break;
+		}
+		INIT_WORK(open_worker, hci_dev_smd_open);
+		schedule_work(open_worker);
+		break;
+	case SMD_EVENT_CLOSE:
+		BT_INFO("Closing HCI-SMD channel :%s", EVENT_CHANNEL);
+		hci_smd_close(hdev);
+		reset_worker = kzalloc(sizeof(*reset_worker), GFP_ATOMIC);
+		if (!reset_worker) {
+			BT_ERR("Out of memory");
+			break;
+		}
+		INIT_WORK(reset_worker, hci_dev_restart);
+		schedule_work(reset_worker);
+		break;
+	default:
+		break;
+	}
+}
+
+static void hci_smd_notify_data(void *data, unsigned int event)
+{
+	struct hci_dev *hdev = hs.hdev;
+	struct hci_smd_data *hsmd = &hs;
+	int len = 0;
+
+	if (!hdev) {
+		BT_ERR("Frame for unknown HCI device (hdev=NULL)");
+		return;
+	}
+
+	switch (event) {
+	case SMD_EVENT_DATA:
+		len = smd_read_avail(hsmd->data_channel);
+		if (len > 0)
+			tasklet_hi_schedule(&hs.rx_task);
+		else if (len < 0)
+			BT_ERR("Failed to read data from smd %d", len);
+		break;
+	case SMD_EVENT_OPEN:
+		BT_INFO("opening HCI-SMD channel :%s", DATA_CHANNEL);
+		hci_smd_open(hdev);
+		break;
+	case SMD_EVENT_CLOSE:
+		BT_INFO("Closing HCI-SMD channel :%s", DATA_CHANNEL);
+		hci_smd_close(hdev);
+		break;
+	default:
+		break;
+	}
+
+}
+
+static int hci_smd_hci_register_dev(struct hci_smd_data *hsmd)
+{
+	struct hci_dev *hdev;
+
+	hdev = hsmd->hdev;
+
+	if (hci_register_dev(hdev) < 0) {
+		BT_ERR("Can't register HCI device");
+		hci_free_dev(hdev);
+		hsmd->hdev = NULL;
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static int hci_smd_register_smd(struct hci_smd_data *hsmd)
+{
+	struct hci_dev *hdev;
+	int rc;
+
+	/* Initialize and register HCI device */
+	hdev = hci_alloc_dev();
+	if (!hdev) {
+		BT_ERR("Can't allocate HCI device");
+		return -ENOMEM;
+	}
+
+	hsmd->hdev = hdev;
+	hdev->bus = HCI_SMD;
+	hdev->driver_data = NULL;
+	hdev->open  = hci_smd_open;
+	hdev->close = hci_smd_close;
+	hdev->send  = hci_smd_send_frame;
+	hdev->destruct = hci_smd_destruct;
+	hdev->owner = THIS_MODULE;
+
+
+	tasklet_init(&hsmd->rx_task,
+			hci_smd_rx, (unsigned long) hsmd);
+	/*
+	 * Setup the timer to monitor whether the Rx queue is empty,
+	 * to control the wake lock release
+	 */
+	setup_timer(&hsmd->rx_q_timer, schedule_timer,
+			(unsigned long) hsmd->hdev);
+
+	/* Open the SMD Channel and device and register the callback function */
+	rc = smd_named_open_on_edge(EVENT_CHANNEL, SMD_APPS_WCNSS,
+			&hsmd->event_channel, hdev, hci_smd_notify_event);
+	if (rc < 0) {
+		BT_ERR("Cannot open the command channel");
+		hci_free_dev(hdev);
+		hsmd->hdev = NULL;
+		return -ENODEV;
+	}
+
+	rc = smd_named_open_on_edge(DATA_CHANNEL, SMD_APPS_WCNSS,
+			&hsmd->data_channel, hdev, hci_smd_notify_data);
+	if (rc < 0) {
+		BT_ERR("Failed to open the Data channel");
+		hci_free_dev(hdev);
+		hsmd->hdev = NULL;
+		return -ENODEV;
+	}
+
+	/* Disable the read interrupts on the channel */
+	smd_disable_read_intr(hsmd->event_channel);
+	smd_disable_read_intr(hsmd->data_channel);
+	return 0;
+}
+
+static void hci_smd_deregister_dev(struct hci_smd_data *hsmd)
+{
+	tasklet_kill(&hs.rx_task);
+
+	if (hsmd->hdev) {
+		if (hci_unregister_dev(hsmd->hdev) < 0)
+			BT_ERR("Can't unregister HCI device %s",
+				hsmd->hdev->name);
+
+		hci_free_dev(hsmd->hdev);
+		hsmd->hdev = NULL;
+	}
+
+	smd_close(hs.event_channel);
+	smd_close(hs.data_channel);
+
+	if (wake_lock_active(&hs.wake_lock_rx))
+		wake_unlock(&hs.wake_lock_rx);
+	if (wake_lock_active(&hs.wake_lock_tx))
+		wake_unlock(&hs.wake_lock_tx);
+
+	/*Destroy the timer used to monitor the Rx queue for emptiness */
+	if (hs.rx_q_timer.function) {
+		del_timer_sync(&hs.rx_q_timer);
+		hs.rx_q_timer.function = NULL;
+		hs.rx_q_timer.data = 0;
+	}
+}
+
+static void hci_dev_restart(struct work_struct *worker)
+{
+	mutex_lock(&hci_smd_enable);
+	hci_smd_deregister_dev(&hs);
+	hci_smd_register_smd(&hs);
+	mutex_unlock(&hci_smd_enable);
+	kfree(worker);
+}
+
+static void hci_dev_smd_open(struct work_struct *worker)
+{
+	mutex_lock(&hci_smd_enable);
+	hci_smd_hci_register_dev(&hs);
+	mutex_unlock(&hci_smd_enable);
+	kfree(worker);
+}
+
+static int hcismd_set_enable(const char *val, struct kernel_param *kp)
+{
+	int ret = 0;
+
+	mutex_lock(&hci_smd_enable);
+
+	ret = param_set_int(val, kp);
+
+	if (ret)
+		goto done;
+
+	switch (hcismd_set) {
+
+	case 1:
+		hci_smd_register_smd(&hs);
+	break;
+	case 0:
+		hci_smd_deregister_dev(&hs);
+	break;
+	default:
+		ret = -EFAULT;
+	}
+
+done:
+	mutex_unlock(&hci_smd_enable);
+	return ret;
+}
+static int  __init hci_smd_init(void)
+{
+	wake_lock_init(&hs.wake_lock_rx, WAKE_LOCK_SUSPEND,
+			 "msm_smd_Rx");
+	wake_lock_init(&hs.wake_lock_tx, WAKE_LOCK_SUSPEND,
+			 "msm_smd_Tx");
+	return 0;
+}
+module_init(hci_smd_init);
+
+static void __exit hci_smd_exit(void)
+{
+	wake_lock_destroy(&hs.wake_lock_rx);
+	wake_lock_destroy(&hs.wake_lock_tx);
+}
+module_exit(hci_smd_exit);
+
+MODULE_AUTHOR("Ankur Nandwani <ankurn@codeaurora.org>");
+MODULE_DESCRIPTION("Bluetooth SMD driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h
index 6cf6ab22..dc48239 100644
--- a/drivers/bluetooth/hci_uart.h
+++ b/drivers/bluetooth/hci_uart.h
@@ -2,9 +2,9 @@
  *
  *  Bluetooth HCI UART driver
  *
- *  Copyright (C) 2000-2001  Qualcomm Incorporated
  *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
  *  Copyright (C) 2004-2005  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved.
  *
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -35,18 +35,17 @@
 #define HCIUARTGETFLAGS		_IOR('U', 204, int)
 
 /* UART protocols */
-#define HCI_UART_MAX_PROTO	6
+#define HCI_UART_MAX_PROTO	7
 
 #define HCI_UART_H4	0
 #define HCI_UART_BCSP	1
 #define HCI_UART_3WIRE	2
 #define HCI_UART_H4DS	3
 #define HCI_UART_LL	4
-#define HCI_UART_ATH3K	5
+#define HCI_UART_IBS	5
+#define HCI_UART_ATH3K	6
 
 #define HCI_UART_RAW_DEVICE	0
-#define HCI_UART_RESET_ON_INIT	1
-#define HCI_UART_CREATE_AMP	2
 
 struct hci_uart;
 
@@ -75,7 +74,8 @@
 };
 
 /* HCI_UART proto flag bits */
-#define HCI_UART_PROTO_SET	0
+#define HCI_UART_PROTO_SET			0
+#define HCI_UART_PROTO_SET_IN_PROGRESS		1
 
 /* TX states  */
 #define HCI_UART_SENDING	1
@@ -104,3 +104,8 @@
 int ath_init(void);
 int ath_deinit(void);
 #endif
+
+#ifdef CONFIG_BT_HCIUART_IBS
+int ibs_init(void);
+int ibs_deinit(void);
+#endif
diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c
index 158bfe5..67c180c 100644
--- a/drivers/bluetooth/hci_vhci.c
+++ b/drivers/bluetooth/hci_vhci.c
@@ -41,8 +41,6 @@
 
 #define VERSION "1.3"
 
-static bool amp;
-
 struct vhci_data {
 	struct hci_dev *hdev;
 
@@ -61,7 +59,7 @@
 
 static int vhci_close_dev(struct hci_dev *hdev)
 {
-	struct vhci_data *data = hci_get_drvdata(hdev);
+	struct vhci_data *data = hdev->driver_data;
 
 	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
 		return 0;
@@ -73,7 +71,7 @@
 
 static int vhci_flush(struct hci_dev *hdev)
 {
-	struct vhci_data *data = hci_get_drvdata(hdev);
+	struct vhci_data *data = hdev->driver_data;
 
 	skb_queue_purge(&data->readq);
 
@@ -93,7 +91,7 @@
 	if (!test_bit(HCI_RUNNING, &hdev->flags))
 		return -EBUSY;
 
-	data = hci_get_drvdata(hdev);
+	data = hdev->driver_data;
 
 	memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
 	skb_queue_tail(&data->readq, skb);
@@ -103,6 +101,11 @@
 	return 0;
 }
 
+static void vhci_destruct(struct hci_dev *hdev)
+{
+	kfree(hdev->driver_data);
+}
+
 static inline ssize_t vhci_get_user(struct vhci_data *data,
 					const char __user *buf, size_t count)
 {
@@ -234,15 +237,15 @@
 	data->hdev = hdev;
 
 	hdev->bus = HCI_VIRTUAL;
-	hci_set_drvdata(hdev, data);
-
-	if (amp)
-		hdev->dev_type = HCI_AMP;
+	hdev->driver_data = data;
 
 	hdev->open     = vhci_open_dev;
 	hdev->close    = vhci_close_dev;
 	hdev->flush    = vhci_flush;
 	hdev->send     = vhci_send_frame;
+	hdev->destruct = vhci_destruct;
+
+	hdev->owner = THIS_MODULE;
 
 	if (hci_register_dev(hdev) < 0) {
 		BT_ERR("Can't register HCI device");
@@ -261,11 +264,13 @@
 	struct vhci_data *data = file->private_data;
 	struct hci_dev *hdev = data->hdev;
 
-	hci_unregister_dev(hdev);
+	if (hci_unregister_dev(hdev) < 0) {
+		BT_ERR("Can't unregister HCI device %s", hdev->name);
+	}
+
 	hci_free_dev(hdev);
 
 	file->private_data = NULL;
-	kfree(data);
 
 	return 0;
 }
@@ -301,9 +306,6 @@
 module_init(vhci_init);
 module_exit(vhci_exit);
 
-module_param(amp, bool, 0644);
-MODULE_PARM_DESC(amp, "Create AMP controller device");
-
 MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
 MODULE_DESCRIPTION("Bluetooth virtual HCI driver ver " VERSION);
 MODULE_VERSION(VERSION);
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index f48cd68..00a07a0 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -64,6 +64,8 @@
 
 source "drivers/tty/serial/Kconfig"
 
+source "drivers/char/diag/Kconfig"
+
 config TTY_PRINTK
 	bool "TTY driver to output user messages via printk"
 	depends on EXPERT
@@ -629,5 +631,46 @@
 	  device appear much like a simple EEPROM, and knows
 	  how to partition a single ROM for multiple purposes.
 
+config MSM_ROTATOR
+        tristate "MSM Offline Image Rotator Driver"
+        depends on (ARCH_MSM7X30 || ARCH_MSM8X60 || ARCH_MSM8960) && ANDROID_PMEM
+        default y
+        help
+          This driver provides support for the image rotator HW block in the
+          MSM 7x30 SoC.
+
+config MSM_ROTATOR_USE_IMEM
+        bool "Enable rotator driver to use iMem"
+        depends on ARCH_MSM7X30 && MSM_ROTATOR
+        default y
+        help
+          This option enables the msm_rotator driver to use the move efficient
+          iMem.  Some MSM platforms may not have iMem available for the rotator
+          block.  Or some systems may want the iMem to be dedicated to a
+          different function.
+
+config MMC_GENERIC_CSDIO
+	tristate "Generic sdio driver"
+	default n
+	help
+	  SDIO function driver that extends SDIO card as character device
+	  in user space.
+
+config CSDIO_VENDOR_ID
+	hex "Card VendorId"
+	depends on MMC_GENERIC_CSDIO
+	default "0"
+	help
+	  Enter vendor id for targeted sdio device, this may be overwritten by
+	  module parameters.
+
+config CSDIO_DEVICE_ID
+	hex "CardDeviceId"
+	depends on MMC_GENERIC_CSDIO
+	default "0"
+	help
+	  Enter device id for targeted sdio device, this may be overwritten by
+	  module parameters.
+.
 endmenu
 
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 8f18891..c38c26c 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -9,7 +9,6 @@
 obj-$(CONFIG_VIRTIO_CONSOLE)	+= virtio_console.o
 obj-$(CONFIG_RAW_DRIVER)	+= raw.o
 obj-$(CONFIG_SGI_SNSC)		+= snsc.o snsc_event.o
-obj-$(CONFIG_MSM_SMD_PKT)	+= msm_smd_pkt.o
 obj-$(CONFIG_MSPEC)		+= mspec.o
 obj-$(CONFIG_MMTIMER)		+= mmtimer.o
 obj-$(CONFIG_UV_MMTIMER)	+= uv_mmtimer.o
@@ -65,3 +64,6 @@
 js-rtc-y = rtc.o
 
 obj-$(CONFIG_TILE_SROM)		+= tile-srom.o
+obj-$(CONFIG_MSM_ROTATOR)	+= msm_rotator.o
+obj-$(CONFIG_MMC_GENERIC_CSDIO)	+= csdio.o
+obj-$(CONFIG_DIAG_CHAR)		+= diag/
\ No newline at end of file
diff --git a/drivers/char/csdio.c b/drivers/char/csdio.c
new file mode 100644
index 0000000..ca7e986
--- /dev/null
+++ b/drivers/char/csdio.c
@@ -0,0 +1,1074 @@
+/*
+ * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/serial_reg.h>
+#include <linux/circ_buf.h>
+#include <linux/gfp.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+
+/* Char device */
+#include <linux/cdev.h>
+#include <linux/fs.h>
+
+/* Sdio device */
+#include <linux/mmc/core.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+
+#include <linux/csdio.h>
+
+#define FALSE   0
+#define TRUE    1
+
+#define VERSION                     "0.5"
+#define CSDIO_NUM_OF_SDIO_FUNCTIONS 7
+#define CSDIO_DEV_NAME              "csdio"
+#define TP_DEV_NAME                 CSDIO_DEV_NAME"f"
+#define CSDIO_DEV_PERMISSIONS       0666
+
+#define CSDIO_SDIO_BUFFER_SIZE      (64*512)
+
+int csdio_major;
+int csdio_minor;
+int csdio_transport_nr_devs = CSDIO_NUM_OF_SDIO_FUNCTIONS;
+static uint csdio_vendor_id;
+static uint csdio_device_id;
+static char *host_name;
+
+static struct csdio_func_t {
+	struct sdio_func   *m_func;
+	int                 m_enabled;
+	struct cdev         m_cdev;      /* char device structure */
+	struct device      *m_device;
+	u32                 m_block_size;
+} *g_csdio_func_table[CSDIO_NUM_OF_SDIO_FUNCTIONS] = {0};
+
+struct csdio_t {
+	struct cdev             m_cdev;
+	struct device          *m_device;
+	struct class           *m_driver_class;
+	struct fasync_struct   *m_async_queue;
+	unsigned char           m_current_irq_mask; /* currently enabled irqs */
+	struct mmc_host        *m_host;
+	unsigned int            m_num_of_func;
+} g_csdio;
+
+struct csdio_file_descriptor {
+	struct csdio_func_t    *m_port;
+	u32                     m_block_mode;/* data tran. byte(0)/block(1) */
+	u32                     m_op_code;   /* address auto increment flag */
+	u32                     m_address;
+};
+
+static void *g_sdio_buffer;
+
+/*
+ * Open and release
+ */
+static int csdio_transport_open(struct inode *inode, struct file *filp)
+{
+	int ret = 0;
+	struct csdio_func_t *port = NULL; /*  device information */
+	struct sdio_func *func = NULL;
+	struct csdio_file_descriptor *descriptor = NULL;
+
+	port = container_of(inode->i_cdev, struct csdio_func_t, m_cdev);
+	func = port->m_func;
+	descriptor = kzalloc(sizeof(struct csdio_file_descriptor), GFP_KERNEL);
+	if (!descriptor) {
+		ret = -ENOMEM;
+		goto exit;
+	}
+
+	pr_info(TP_DEV_NAME"%d: open: func=%p, port=%p\n",
+			func->num, func, port);
+	sdio_claim_host(func);
+	ret = sdio_enable_func(func);
+	if (ret) {
+		pr_err(TP_DEV_NAME"%d:Enable func failed (%d)\n",
+				func->num, ret);
+		ret = -EIO;
+		goto free_descriptor;
+	}
+	descriptor->m_port = port;
+	filp->private_data = descriptor;
+	goto release_host;
+
+free_descriptor:
+	kfree(descriptor);
+release_host:
+	sdio_release_host(func);
+exit:
+	return ret;
+}
+
+static int csdio_transport_release(struct inode *inode, struct file *filp)
+{
+	int ret = 0;
+	struct csdio_file_descriptor *descriptor = filp->private_data;
+	struct csdio_func_t *port = descriptor->m_port;
+	struct sdio_func *func = port->m_func;
+
+	pr_info(TP_DEV_NAME"%d: release\n", func->num);
+	sdio_claim_host(func);
+	ret = sdio_disable_func(func);
+	if (ret) {
+		pr_err(TP_DEV_NAME"%d:Disable func failed(%d)\n",
+				func->num, ret);
+		ret = -EIO;
+	}
+	sdio_release_host(func);
+	kfree(descriptor);
+	return ret;
+}
+
+/*
+ * Data management: read and write
+ */
+static ssize_t csdio_transport_read(struct file *filp,
+		char __user *buf,
+		size_t count,
+		loff_t *f_pos)
+{
+	ssize_t ret = 0;
+	struct csdio_file_descriptor *descriptor = filp->private_data;
+	struct csdio_func_t *port = descriptor->m_port;
+	struct sdio_func *func = port->m_func;
+	size_t t_count = count;
+
+	if (descriptor->m_block_mode) {
+		pr_info(TP_DEV_NAME "%d: CMD53 read, Md:%d, Addr:0x%04X,"
+				" Un:%d (Bl:%d, BlSz:%d)\n", func->num,
+				descriptor->m_block_mode,
+				descriptor->m_address,
+				count*port->m_block_size,
+				count, port->m_block_size);
+		/* recalculate size */
+		count *= port->m_block_size;
+	}
+	sdio_claim_host(func);
+	if (descriptor->m_op_code) {
+		/* auto increment */
+		ret = sdio_memcpy_fromio(func, g_sdio_buffer,
+				descriptor->m_address, count);
+	} else { /* FIFO */
+		ret = sdio_readsb(func, g_sdio_buffer,
+				descriptor->m_address, count);
+	}
+	sdio_release_host(func);
+	if (!ret) {
+		if (copy_to_user(buf, g_sdio_buffer, count))
+			ret = -EFAULT;
+		else
+			ret = t_count;
+	}
+	if (ret < 0) {
+		pr_err(TP_DEV_NAME "%d: CMD53 read failed (%d)"
+				"(Md:%d, Addr:0x%04X, Sz:%d)\n",
+				func->num, ret,
+				descriptor->m_block_mode,
+				descriptor->m_address, count);
+	}
+	return ret;
+}
+
+static ssize_t csdio_transport_write(struct file *filp,
+		const char __user *buf,
+		size_t count,
+		loff_t *f_pos)
+{
+	ssize_t ret = 0;
+	struct csdio_file_descriptor *descriptor = filp->private_data;
+	struct csdio_func_t *port = descriptor->m_port;
+	struct sdio_func *func = port->m_func;
+	size_t t_count = count;
+
+	if (descriptor->m_block_mode)
+		count *= port->m_block_size;
+
+	if (copy_from_user(g_sdio_buffer, buf, count)) {
+		pr_err(TP_DEV_NAME"%d:copy_from_user failed\n", func->num);
+		ret = -EFAULT;
+	} else {
+		sdio_claim_host(func);
+		if (descriptor->m_op_code) {
+			/* auto increment */
+			ret = sdio_memcpy_toio(func, descriptor->m_address,
+					g_sdio_buffer, count);
+		} else {
+			/* FIFO */
+			ret = sdio_writesb(func, descriptor->m_address,
+					g_sdio_buffer, count);
+		}
+		sdio_release_host(func);
+		if (!ret) {
+			ret = t_count;
+		} else {
+			pr_err(TP_DEV_NAME "%d: CMD53 write failed (%d)"
+				"(Md:%d, Addr:0x%04X, Sz:%d)\n",
+				func->num, ret, descriptor->m_block_mode,
+				descriptor->m_address, count);
+		}
+	}
+	return ret;
+}
+
+/* disable interrupt for sdio client */
+static int disable_sdio_client_isr(struct sdio_func *func)
+{
+	int ret;
+
+	/* disable for all functions, to restore interrupts
+	 * use g_csdio.m_current_irq_mask */
+	sdio_f0_writeb(func, 0, SDIO_CCCR_IENx, &ret);
+	if (ret)
+		pr_err(CSDIO_DEV_NAME" Can't sdio_f0_writeb (%d)\n", ret);
+
+	return ret;
+}
+
+/*
+ * This handles the interrupt from SDIO.
+ */
+static void csdio_sdio_irq(struct sdio_func *func)
+{
+	int ret;
+
+	pr_info(CSDIO_DEV_NAME" csdio_sdio_irq: func=%d\n", func->num);
+	ret = disable_sdio_client_isr(func);
+	if (ret) {
+		pr_err(CSDIO_DEV_NAME" Can't disable client isr(%d)\n", ret);
+		return;
+	}
+	/*  signal asynchronous readers */
+	if (g_csdio.m_async_queue)
+		kill_fasync(&g_csdio.m_async_queue, SIGIO, POLL_IN);
+}
+
+/*
+ * The ioctl() implementation
+ */
+static int csdio_transport_ioctl(struct inode *inode,
+		struct file *filp,
+		unsigned int cmd,
+		unsigned long arg)
+{
+	int err = 0;
+	int ret = 0;
+	struct csdio_file_descriptor *descriptor = filp->private_data;
+	struct csdio_func_t *port = descriptor->m_port;
+	struct sdio_func *func = port->m_func;
+
+	/*  extract the type and number bitfields
+	    sanity check: return ENOTTY (inappropriate ioctl) before
+	    access_ok()
+	*/
+	if ((_IOC_TYPE(cmd) != CSDIO_IOC_MAGIC) ||
+			(_IOC_NR(cmd) > CSDIO_IOC_MAXNR)) {
+		pr_err(TP_DEV_NAME "Wrong ioctl command parameters\n");
+		ret = -ENOTTY;
+		goto exit;
+	}
+
+	/*  the direction is a bitmask, and VERIFY_WRITE catches R/W
+	 *  transfers. `Type' is user-oriented, while access_ok is
+	    kernel-oriented, so the concept of "read" and "write" is reversed
+	*/
+	if (_IOC_DIR(cmd) & _IOC_READ) {
+		err = !access_ok(VERIFY_WRITE, (void __user *)arg,
+				_IOC_SIZE(cmd));
+	} else {
+		if (_IOC_DIR(cmd) & _IOC_WRITE) {
+			err =  !access_ok(VERIFY_READ, (void __user *)arg,
+					_IOC_SIZE(cmd));
+		}
+	}
+	if (err) {
+		pr_err(TP_DEV_NAME "Wrong ioctl access direction\n");
+		ret = -EFAULT;
+		goto exit;
+	}
+
+	switch (cmd) {
+	case CSDIO_IOC_SET_OP_CODE:
+		{
+			pr_info(TP_DEV_NAME"%d:SET_OP_CODE=%d\n",
+					func->num, descriptor->m_op_code);
+			ret = get_user(descriptor->m_op_code,
+					(unsigned char __user *)arg);
+			if (ret) {
+				pr_err(TP_DEV_NAME"%d:SET_OP_CODE get data"
+						" from user space failed(%d)\n",
+						func->num, ret);
+				ret = -ENOTTY;
+				break;
+			}
+		}
+		break;
+	case CSDIO_IOC_FUNCTION_SET_BLOCK_SIZE:
+		{
+			unsigned block_size;
+
+			ret = get_user(block_size, (unsigned __user *)arg);
+			if (ret) {
+				pr_err(TP_DEV_NAME"%d:SET_BLOCK_SIZE get data"
+						" from user space failed(%d)\n",
+						func->num, ret);
+				ret = -ENOTTY;
+				break;
+			}
+			pr_info(TP_DEV_NAME"%d:SET_BLOCK_SIZE=%d\n",
+					func->num, block_size);
+			sdio_claim_host(func);
+			ret = sdio_set_block_size(func, block_size);
+			if (!ret) {
+				port->m_block_size = block_size;
+			} else {
+				pr_err(TP_DEV_NAME"%d:SET_BLOCK_SIZE set block"
+						" size to %d failed (%d)\n",
+						func->num, block_size, ret);
+				ret = -ENOTTY;
+				break;
+			}
+			sdio_release_host(func);
+		}
+		break;
+	case CSDIO_IOC_SET_BLOCK_MODE:
+		{
+			pr_info(TP_DEV_NAME"%d:SET_BLOCK_MODE=%d\n",
+					func->num, descriptor->m_block_mode);
+			ret = get_user(descriptor->m_block_mode,
+					(unsigned char __user *)arg);
+			if (ret) {
+				pr_err(TP_DEV_NAME"%d:SET_BLOCK_MODE get data"
+						" from user space failed\n",
+						func->num);
+				ret = -ENOTTY;
+				break;
+			}
+		}
+		break;
+	case CSDIO_IOC_CMD52:
+		{
+			struct csdio_cmd52_ctrl_t cmd52ctrl;
+			int cmd52ret;
+
+			if (copy_from_user(&cmd52ctrl,
+					(const unsigned char __user *)arg,
+					sizeof(cmd52ctrl))) {
+				pr_err(TP_DEV_NAME"%d:IOC_CMD52 get data"
+						" from user space failed\n",
+						func->num);
+				ret = -ENOTTY;
+				break;
+			}
+			sdio_claim_host(func);
+			if (cmd52ctrl.m_write)
+				sdio_writeb(func, cmd52ctrl.m_data,
+						cmd52ctrl.m_address, &cmd52ret);
+			else
+				cmd52ctrl.m_data = sdio_readb(func,
+						cmd52ctrl.m_address, &cmd52ret);
+
+			cmd52ctrl.m_ret = cmd52ret;
+			sdio_release_host(func);
+			if (cmd52ctrl.m_ret)
+				pr_err(TP_DEV_NAME"%d:IOC_CMD52 failed (%d)\n",
+						func->num, cmd52ctrl.m_ret);
+
+			if (copy_to_user((unsigned char __user *)arg,
+						&cmd52ctrl,
+						sizeof(cmd52ctrl))) {
+				pr_err(TP_DEV_NAME"%d:IOC_CMD52 put data"
+						" to user space failed\n",
+						func->num);
+				ret = -ENOTTY;
+				break;
+			}
+		}
+		break;
+	case CSDIO_IOC_CMD53:
+		{
+			struct csdio_cmd53_ctrl_t csdio_cmd53_ctrl;
+
+			if (copy_from_user(&csdio_cmd53_ctrl,
+						(const char __user *)arg,
+						sizeof(csdio_cmd53_ctrl))) {
+				ret = -EPERM;
+				pr_err(TP_DEV_NAME"%d:"
+					"Get data from user space failed\n",
+					func->num);
+				break;
+			}
+			descriptor->m_block_mode =
+				csdio_cmd53_ctrl.m_block_mode;
+			descriptor->m_op_code = csdio_cmd53_ctrl.m_op_code;
+			descriptor->m_address = csdio_cmd53_ctrl.m_address;
+		}
+		break;
+	case CSDIO_IOC_CONNECT_ISR:
+		{
+			pr_info(CSDIO_DEV_NAME" SDIO_CONNECT_ISR"
+				" func=%d, csdio_sdio_irq=%x\n",
+				func->num, (unsigned int)csdio_sdio_irq);
+			sdio_claim_host(func);
+			ret = sdio_claim_irq(func, csdio_sdio_irq);
+			sdio_release_host(func);
+			if (ret) {
+				pr_err(CSDIO_DEV_NAME" SDIO_CONNECT_ISR"
+						" claim irq failed(%d)\n", ret);
+			} else {
+				/* update current irq mask for disable/enable */
+				g_csdio.m_current_irq_mask |= (1 << func->num);
+			}
+		}
+		break;
+	case CSDIO_IOC_DISCONNECT_ISR:
+		{
+			pr_info(CSDIO_DEV_NAME " SDIO_DISCONNECT_ISR func=%d\n",
+					func->num);
+			sdio_claim_host(func);
+			sdio_release_irq(func);
+			sdio_release_host(func);
+			/* update current irq mask for disable/enable */
+			g_csdio.m_current_irq_mask &= ~(1 << func->num);
+		}
+		break;
+	default:  /*  redundant, as cmd was checked against MAXNR */
+		pr_warning(TP_DEV_NAME"%d: Redundant IOCTL\n",
+				func->num);
+		ret = -ENOTTY;
+	}
+exit:
+	return ret;
+}
+
+static const struct file_operations csdio_transport_fops = {
+	.owner =    THIS_MODULE,
+	.read =     csdio_transport_read,
+	.write =    csdio_transport_write,
+	.ioctl =    csdio_transport_ioctl,
+	.open =     csdio_transport_open,
+	.release =  csdio_transport_release,
+};
+
+static void csdio_transport_cleanup(struct csdio_func_t *port)
+{
+	int devno = MKDEV(csdio_major, csdio_minor + port->m_func->num);
+	device_destroy(g_csdio.m_driver_class, devno);
+	port->m_device = NULL;
+	cdev_del(&port->m_cdev);
+}
+
+#if defined(CONFIG_DEVTMPFS)
+static inline int csdio_cdev_update_permissions(
+    const char *devname, int dev_minor)
+{
+	return 0;
+}
+#else
+static int csdio_cdev_update_permissions(
+    const char *devname, int dev_minor)
+{
+	int ret = 0;
+	mm_segment_t fs;
+	struct file *file;
+	struct inode *inode;
+	struct iattr newattrs;
+	int mode = CSDIO_DEV_PERMISSIONS;
+	char dev_file[64];
+
+	fs = get_fs();
+	set_fs(get_ds());
+
+	snprintf(dev_file, sizeof(dev_file), "/dev/%s%d",
+		devname, dev_minor);
+	file = filp_open(dev_file, O_RDWR, 0);
+	if (IS_ERR(file)) {
+		ret = -EFAULT;
+		goto exit;
+	}
+
+	inode = file->f_path.dentry->d_inode;
+
+	mutex_lock(&inode->i_mutex);
+	newattrs.ia_mode =
+		(mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
+	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
+	ret = notify_change(file->f_path.dentry, &newattrs);
+	mutex_unlock(&inode->i_mutex);
+
+	filp_close(file, NULL);
+
+exit:
+	set_fs(fs);
+	return ret;
+}
+#endif
+
+static struct device *csdio_cdev_init(struct cdev *char_dev,
+		const struct file_operations *file_op, int dev_minor,
+		const char *devname, struct device *parent)
+{
+	int ret = 0;
+	struct device *new_device = NULL;
+	dev_t devno = MKDEV(csdio_major, dev_minor);
+
+	/*  Initialize transport device */
+	cdev_init(char_dev, file_op);
+	char_dev->owner = THIS_MODULE;
+	char_dev->ops = file_op;
+	ret = cdev_add(char_dev, devno, 1);
+
+	/*  Fail gracefully if need be */
+	if (ret) {
+		pr_warning("Error %d adding CSDIO char device '%s%d'",
+				ret, devname, dev_minor);
+		goto exit;
+	}
+	pr_info("'%s%d' char driver registered\n", devname, dev_minor);
+
+	/*  create a /dev entry for transport drivers */
+	new_device = device_create(g_csdio.m_driver_class, parent, devno, NULL,
+			"%s%d", devname, dev_minor);
+	if (!new_device) {
+		pr_err("Can't create device node '/dev/%s%d'\n",
+				devname, dev_minor);
+		goto cleanup;
+	}
+	/* no irq attached */
+	g_csdio.m_current_irq_mask = 0;
+
+	if (csdio_cdev_update_permissions(devname, dev_minor)) {
+		pr_warning("%s%d: Unable to update access permissions of the"
+			" '/dev/%s%d'\n",
+			devname, dev_minor, devname, dev_minor);
+	}
+
+	pr_info("%s%d: Device node '/dev/%s%d' created successfully\n",
+			devname, dev_minor, devname, dev_minor);
+	goto exit;
+cleanup:
+	cdev_del(char_dev);
+exit:
+	return new_device;
+}
+
+/* Looks for first non empty function, returns NULL otherwise */
+static struct sdio_func *get_active_func(void)
+{
+	int i;
+
+	for (i = 0; i < CSDIO_NUM_OF_SDIO_FUNCTIONS; i++) {
+		if (g_csdio_func_table[i])
+			return g_csdio_func_table[i]->m_func;
+	}
+	return NULL;
+}
+
+static ssize_t
+show_vdd(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	if (NULL == g_csdio.m_host)
+		return snprintf(buf, PAGE_SIZE, "N/A\n");
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+		g_csdio.m_host->ios.vdd);
+}
+
+static int
+set_vdd_helper(int value)
+{
+	struct mmc_ios *ios = NULL;
+
+	if (NULL == g_csdio.m_host) {
+		pr_err("%s0: Set VDD, no MMC host assigned\n", CSDIO_DEV_NAME);
+		return -ENXIO;
+	}
+
+	mmc_claim_host(g_csdio.m_host);
+	ios = &g_csdio.m_host->ios;
+	ios->vdd = value;
+	g_csdio.m_host->ops->set_ios(g_csdio.m_host, ios);
+	mmc_release_host(g_csdio.m_host);
+	return 0;
+}
+
+static ssize_t
+set_vdd(struct device *dev, struct device_attribute *att,
+	const char *buf, size_t count)
+{
+	int value = 0;
+
+	sscanf(buf, "%d", &value);
+	if (set_vdd_helper(value))
+		return -ENXIO;
+	return count;
+}
+
+static DEVICE_ATTR(vdd, S_IRUGO | S_IWUSR,
+	show_vdd, set_vdd);
+
+static struct attribute *dev_attrs[] = {
+	&dev_attr_vdd.attr,
+	NULL,
+};
+
+static struct attribute_group dev_attr_grp = {
+	.attrs = dev_attrs,
+};
+
+/*
+ * The ioctl() implementation for control device
+ */
+static int csdio_ctrl_ioctl(struct inode *inode, struct file *filp,
+		unsigned int cmd, unsigned long arg)
+{
+	int err = 0;
+	int ret = 0;
+
+	pr_info("CSDIO ctrl ioctl.\n");
+
+	/*  extract the type and number bitfields
+	    sanity check: return ENOTTY (inappropriate ioctl) before
+	    access_ok()
+	*/
+	if ((_IOC_TYPE(cmd) != CSDIO_IOC_MAGIC) ||
+			(_IOC_NR(cmd) > CSDIO_IOC_MAXNR)) {
+		pr_err(CSDIO_DEV_NAME "Wrong ioctl command parameters\n");
+		ret = -ENOTTY;
+		goto exit;
+	}
+
+	/*  the direction is a bitmask, and VERIFY_WRITE catches R/W
+	  transfers. `Type' is user-oriented, while access_ok is
+	  kernel-oriented, so the concept of "read" and "write" is reversed
+	  */
+	if (_IOC_DIR(cmd) & _IOC_READ) {
+		err = !access_ok(VERIFY_WRITE, (void __user *)arg,
+				_IOC_SIZE(cmd));
+	} else {
+		if (_IOC_DIR(cmd) & _IOC_WRITE)
+			err =  !access_ok(VERIFY_READ, (void __user *)arg,
+					_IOC_SIZE(cmd));
+	}
+	if (err) {
+		pr_err(CSDIO_DEV_NAME "Wrong ioctl access direction\n");
+		ret = -EFAULT;
+		goto exit;
+	}
+
+	switch (cmd) {
+	case CSDIO_IOC_ENABLE_HIGHSPEED_MODE:
+		pr_info(CSDIO_DEV_NAME" ENABLE_HIGHSPEED_MODE\n");
+		break;
+	case CSDIO_IOC_SET_DATA_TRANSFER_CLOCKS:
+		{
+			struct mmc_host *host = g_csdio.m_host;
+			struct mmc_ios *ios = NULL;
+
+			if (NULL == host) {
+				pr_err("%s0: "
+					"CSDIO_IOC_SET_DATA_TRANSFER_CLOCKS,"
+					" no MMC host assigned\n",
+					CSDIO_DEV_NAME);
+				ret = -EFAULT;
+				goto exit;
+			}
+			ios = &host->ios;
+
+			mmc_claim_host(host);
+			ret = get_user(host->ios.clock,
+					(unsigned int __user *)arg);
+			if (ret) {
+				pr_err(CSDIO_DEV_NAME
+					" get data from user space failed\n");
+			} else {
+				pr_err(CSDIO_DEV_NAME
+					"SET_DATA_TRANSFER_CLOCKS(%d-%d)(%d)\n",
+					host->f_min, host->f_max,
+					host->ios.clock);
+				host->ops->set_ios(host, ios);
+			}
+			mmc_release_host(host);
+		}
+		break;
+	case CSDIO_IOC_ENABLE_ISR:
+		{
+			int ret;
+			unsigned char reg;
+			struct sdio_func *func = get_active_func();
+
+			if (!func) {
+				pr_err(CSDIO_DEV_NAME " CSDIO_IOC_ENABLE_ISR"
+						" no active sdio function\n");
+				ret = -EFAULT;
+				goto exit;
+			}
+			pr_info(CSDIO_DEV_NAME
+					" CSDIO_IOC_ENABLE_ISR func=%d\n",
+					func->num);
+			reg = g_csdio.m_current_irq_mask | 1;
+
+			sdio_claim_host(func);
+			sdio_f0_writeb(func, reg, SDIO_CCCR_IENx, &ret);
+			sdio_release_host(func);
+			if (ret) {
+				pr_err(CSDIO_DEV_NAME
+						" Can't sdio_f0_writeb (%d)\n",
+						ret);
+				goto exit;
+			}
+		}
+		break;
+	case CSDIO_IOC_DISABLE_ISR:
+		{
+			int ret;
+			struct sdio_func *func = get_active_func();
+			if (!func) {
+				pr_err(CSDIO_DEV_NAME " CSDIO_IOC_ENABLE_ISR"
+						" no active sdio function\n");
+				ret = -EFAULT;
+				goto exit;
+			}
+			pr_info(CSDIO_DEV_NAME
+					" CSDIO_IOC_DISABLE_ISR func=%p\n",
+					func);
+
+			sdio_claim_host(func);
+			ret = disable_sdio_client_isr(func);
+			sdio_release_host(func);
+			if (ret) {
+				pr_err("%s0: Can't disable client isr (%d)\n",
+					CSDIO_DEV_NAME, ret);
+				goto exit;
+			}
+		}
+	break;
+	case CSDIO_IOC_SET_VDD:
+		{
+			unsigned int vdd = 0;
+
+			ret = get_user(vdd, (unsigned int __user *)arg);
+			if (ret) {
+				pr_err("%s0: CSDIO_IOC_SET_VDD,"
+					" get data from user space failed\n",
+					CSDIO_DEV_NAME);
+				goto exit;
+			}
+			pr_info(CSDIO_DEV_NAME" CSDIO_IOC_SET_VDD - %d\n", vdd);
+
+			ret = set_vdd_helper(vdd);
+			if (ret)
+				goto exit;
+		}
+	break;
+	case CSDIO_IOC_GET_VDD:
+		{
+			if (NULL == g_csdio.m_host) {
+				pr_err("%s0: CSDIO_IOC_GET_VDD,"
+					" no MMC host assigned\n",
+					CSDIO_DEV_NAME);
+				ret = -EFAULT;
+				goto exit;
+			}
+			ret = put_user(g_csdio.m_host->ios.vdd,
+				(unsigned short __user *)arg);
+			if (ret) {
+				pr_err("%s0: CSDIO_IOC_GET_VDD, put data"
+					" to user space failed\n",
+					CSDIO_DEV_NAME);
+				goto exit;
+			}
+		}
+	break;
+	default:  /*  redundant, as cmd was checked against MAXNR */
+		pr_warning(CSDIO_DEV_NAME" Redundant IOCTL\n");
+		ret = -ENOTTY;
+	}
+exit:
+	return ret;
+}
+
+static int csdio_ctrl_fasync(int fd, struct file *filp, int mode)
+{
+	pr_info(CSDIO_DEV_NAME
+			" csdio_ctrl_fasync: fd=%d, filp=%p, mode=%d\n",
+			fd, filp, mode);
+	return fasync_helper(fd, filp, mode, &g_csdio.m_async_queue);
+}
+
+/*
+ * Open and close
+ */
+static int csdio_ctrl_open(struct inode *inode, struct file *filp)
+{
+	int ret = 0;
+	struct csdio_t *csdio_ctrl_drv = NULL; /*  device information */
+
+	pr_info("CSDIO ctrl open.\n");
+	csdio_ctrl_drv = container_of(inode->i_cdev, struct csdio_t, m_cdev);
+	filp->private_data = csdio_ctrl_drv; /*  for other methods */
+	return ret;
+}
+
+static int csdio_ctrl_release(struct inode *inode, struct file *filp)
+{
+	pr_info("CSDIO ctrl release.\n");
+	/*  remove this filp from the asynchronously notified filp's */
+	csdio_ctrl_fasync(-1, filp, 0);
+	return 0;
+}
+
+static const struct file_operations csdio_ctrl_fops = {
+	.owner =	THIS_MODULE,
+	.ioctl =	csdio_ctrl_ioctl,
+	.open  =	csdio_ctrl_open,
+	.release =	csdio_ctrl_release,
+	.fasync =	csdio_ctrl_fasync,
+};
+
+static int csdio_probe(struct sdio_func *func,
+		const struct sdio_device_id *id)
+{
+	struct csdio_func_t *port;
+	int ret = 0;
+	struct mmc_host *host = func->card->host;
+
+	if (NULL != g_csdio.m_host && g_csdio.m_host != host) {
+		pr_info("%s: Device is on unexpected host\n",
+			CSDIO_DEV_NAME);
+		ret = -ENODEV;
+		goto exit;
+	}
+
+	/* enforce single instance policy */
+	if (g_csdio_func_table[func->num-1]) {
+		pr_err("%s - only single SDIO device supported",
+				sdio_func_id(func));
+		ret = -EEXIST;
+		goto exit;
+	}
+
+	port = kzalloc(sizeof(struct csdio_func_t), GFP_KERNEL);
+	if (!port) {
+		pr_err("Can't allocate memory\n");
+		ret = -ENOMEM;
+		goto exit;
+	}
+
+	/* initialize SDIO side */
+	port->m_func = func;
+	sdio_set_drvdata(func, port);
+
+	pr_info("%s - SDIO device found. Function %d\n",
+			sdio_func_id(func), func->num);
+
+	port->m_device = csdio_cdev_init(&port->m_cdev, &csdio_transport_fops,
+			csdio_minor + port->m_func->num,
+			TP_DEV_NAME, &port->m_func->dev);
+
+	/* create appropriate char device */
+	if (!port->m_device)
+		goto free;
+
+	if (0 == g_csdio.m_num_of_func && NULL == host_name)
+		g_csdio.m_host = host;
+	g_csdio.m_num_of_func++;
+	g_csdio_func_table[func->num-1] = port;
+	port->m_enabled = TRUE;
+	goto exit;
+free:
+	kfree(port);
+exit:
+	return ret;
+}
+
+static void csdio_remove(struct sdio_func *func)
+{
+	struct csdio_func_t *port = sdio_get_drvdata(func);
+
+	csdio_transport_cleanup(port);
+	sdio_claim_host(func);
+	sdio_release_irq(func);
+	sdio_disable_func(func);
+	sdio_release_host(func);
+	kfree(port);
+	g_csdio_func_table[func->num-1] = NULL;
+	g_csdio.m_num_of_func--;
+	if (0 == g_csdio.m_num_of_func && NULL == host_name)
+		g_csdio.m_host = NULL;
+	pr_info("%s%d: Device removed (%s). Function %d\n",
+		CSDIO_DEV_NAME, func->num, sdio_func_id(func), func->num);
+}
+
+/* CONFIG_CSDIO_VENDOR_ID and CONFIG_CSDIO_DEVICE_ID are defined in Kconfig.
+ * Use kernel configuration to change the values or overwrite them through
+ * module parameters */
+static struct sdio_device_id csdio_ids[] = {
+	{ SDIO_DEVICE(CONFIG_CSDIO_VENDOR_ID, CONFIG_CSDIO_DEVICE_ID) },
+	{ /* end: all zeroes */},
+};
+
+MODULE_DEVICE_TABLE(sdio, csdio_ids);
+
+static struct sdio_driver csdio_driver = {
+	.probe      = csdio_probe,
+	.remove     = csdio_remove,
+	.name       = "csdio",
+	.id_table   = csdio_ids,
+};
+
+static void __exit csdio_exit(void)
+{
+	dev_t devno = MKDEV(csdio_major, csdio_minor);
+
+	sdio_unregister_driver(&csdio_driver);
+	sysfs_remove_group(&g_csdio.m_device->kobj, &dev_attr_grp);
+	kfree(g_sdio_buffer);
+	device_destroy(g_csdio.m_driver_class, devno);
+	cdev_del(&g_csdio.m_cdev);
+	class_destroy(g_csdio.m_driver_class);
+	unregister_chrdev_region(devno, csdio_transport_nr_devs);
+	pr_info("%s: Exit driver module\n", CSDIO_DEV_NAME);
+}
+
+static char *csdio_devnode(struct device *dev, mode_t *mode)
+{
+	*mode = CSDIO_DEV_PERMISSIONS;
+	return NULL;
+}
+
+static int __init csdio_init(void)
+{
+	int ret = 0;
+	dev_t devno = 0;
+
+	pr_info("Init CSDIO driver module.\n");
+
+	/*  Get a range of minor numbers to work with, asking for a dynamic */
+	/*  major unless directed otherwise at load time. */
+	if (csdio_major) {
+		devno = MKDEV(csdio_major, csdio_minor);
+		ret = register_chrdev_region(devno, csdio_transport_nr_devs,
+				CSDIO_DEV_NAME);
+	} else {
+		ret = alloc_chrdev_region(&devno, csdio_minor,
+				csdio_transport_nr_devs, CSDIO_DEV_NAME);
+		csdio_major = MAJOR(devno);
+	}
+	if (ret < 0) {
+		pr_err("CSDIO: can't get major %d\n", csdio_major);
+		goto exit;
+	}
+	pr_info("CSDIO char driver major number is %d\n", csdio_major);
+
+	/* kernel module got parameters: overwrite vendor and device id's */
+	if ((csdio_vendor_id != 0) && (csdio_device_id != 0)) {
+		csdio_ids[0].vendor = (u16)csdio_vendor_id;
+		csdio_ids[0].device = (u16)csdio_device_id;
+	}
+
+	/*  prepare create /dev/... instance */
+	g_csdio.m_driver_class = class_create(THIS_MODULE, CSDIO_DEV_NAME);
+	if (IS_ERR(g_csdio.m_driver_class)) {
+		ret = -ENOMEM;
+		pr_err(CSDIO_DEV_NAME " class_create failed\n");
+		goto unregister_region;
+	}
+	g_csdio.m_driver_class->devnode = csdio_devnode;
+
+	/*  create CSDIO ctrl driver */
+	g_csdio.m_device = csdio_cdev_init(&g_csdio.m_cdev,
+		&csdio_ctrl_fops, csdio_minor, CSDIO_DEV_NAME, NULL);
+	if (!g_csdio.m_device) {
+		pr_err("%s: Unable to create ctrl driver\n",
+			CSDIO_DEV_NAME);
+		goto destroy_class;
+	}
+
+	g_sdio_buffer = kmalloc(CSDIO_SDIO_BUFFER_SIZE, GFP_KERNEL);
+	if (!g_sdio_buffer) {
+		pr_err("Unable to allocate %d bytes\n", CSDIO_SDIO_BUFFER_SIZE);
+		ret = -ENOMEM;
+		goto destroy_cdev;
+	}
+
+	ret = sysfs_create_group(&g_csdio.m_device->kobj, &dev_attr_grp);
+	if (ret) {
+		pr_err("%s: Unable to create device attribute\n",
+			CSDIO_DEV_NAME);
+		goto free_sdio_buff;
+	}
+
+	g_csdio.m_num_of_func = 0;
+	g_csdio.m_host = NULL;
+
+	if (NULL != host_name) {
+		struct device *dev = bus_find_device_by_name(&platform_bus_type,
+			NULL, host_name);
+		if (NULL != dev) {
+			g_csdio.m_host = dev_get_drvdata(dev);
+		} else {
+			pr_err("%s: Host '%s' doesn't exist!\n", CSDIO_DEV_NAME,
+				host_name);
+		}
+	}
+
+	pr_info("%s: Match with VendorId=0x%X, DeviceId=0x%X, Host = %s\n",
+		CSDIO_DEV_NAME, csdio_device_id, csdio_vendor_id,
+		(NULL == host_name) ? "Any" : host_name);
+
+	/* register sdio driver */
+	ret = sdio_register_driver(&csdio_driver);
+	if (ret) {
+		pr_err("%s: Unable to register as SDIO driver\n",
+			CSDIO_DEV_NAME);
+		goto remove_group;
+	}
+
+	goto exit;
+
+remove_group:
+	sysfs_remove_group(&g_csdio.m_device->kobj, &dev_attr_grp);
+free_sdio_buff:
+	kfree(g_sdio_buffer);
+destroy_cdev:
+	cdev_del(&g_csdio.m_cdev);
+destroy_class:
+	class_destroy(g_csdio.m_driver_class);
+unregister_region:
+	unregister_chrdev_region(devno, csdio_transport_nr_devs);
+exit:
+	return ret;
+}
+module_param(csdio_vendor_id, uint, S_IRUGO);
+module_param(csdio_device_id, uint, S_IRUGO);
+module_param(host_name, charp, S_IRUGO);
+
+module_init(csdio_init);
+module_exit(csdio_exit);
+
+MODULE_AUTHOR("Code Aurora Forum");
+MODULE_DESCRIPTION("CSDIO device driver version " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/char/dcc_tty.c b/drivers/char/dcc_tty.c
index a787acc..7643f50 100644
--- a/drivers/char/dcc_tty.c
+++ b/drivers/char/dcc_tty.c
@@ -21,12 +21,13 @@
 #include <linux/tty.h>
 #include <linux/tty_driver.h>
 #include <linux/tty_flip.h>
+#include <linux/spinlock.h>
 
 MODULE_DESCRIPTION("DCC TTY Driver");
 MODULE_LICENSE("GPL");
 MODULE_VERSION("1.0");
 
-static spinlock_t g_dcc_tty_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t g_dcc_tty_lock = __SPIN_LOCK_UNLOCKED(g_dcc_tty_lock);
 static struct hrtimer g_dcc_timer;
 static char g_dcc_buffer[16];
 static int g_dcc_buffer_head;
diff --git a/drivers/char/diag/Kconfig b/drivers/char/diag/Kconfig
new file mode 100644
index 0000000..53df29b
--- /dev/null
+++ b/drivers/char/diag/Kconfig
@@ -0,0 +1,41 @@
+menu "Diag Support"
+
+config DIAG_CHAR
+	tristate "char driver interface and diag forwarding to/from modem"
+	default m
+	depends on USB_G_ANDROID || USB_FUNCTION_DIAG || USB_QCOM_MAEMO
+	depends on ARCH_MSM
+	help
+	 Char driver interface for diag user space and diag-forwarding to modem ARM and back.
+	 This enables diagchar for maemo usb gadget or android usb gadget based on config selected.
+endmenu
+
+menu "DIAG traffic over USB"
+
+config DIAG_OVER_USB
+	bool "Enable DIAG traffic to go over USB"
+        depends on ARCH_MSM
+	default y
+	help
+	 This feature helps segregate code required for DIAG traffic to go over USB.
+endmenu
+
+menu "SDIO support for DIAG"
+
+config DIAG_SDIO_PIPE
+	depends on MSM_SDIO_AL
+	default y
+	bool "Enable 9K DIAG traffic over SDIO"
+	help
+	 SDIO Transport Layer for DIAG Router
+endmenu
+
+menu "HSIC support for DIAG"
+
+config DIAG_HSIC_PIPE
+	depends on USB_QCOM_DIAG_BRIDGE
+	default y
+	bool "Enable 9K DIAG traffic over HSIC"
+	help
+	 HSIC Transport Layer for DIAG Router
+endmenu
diff --git a/drivers/char/diag/Makefile b/drivers/char/diag/Makefile
new file mode 100644
index 0000000..3181d29
--- /dev/null
+++ b/drivers/char/diag/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_DIAG_CHAR) := diagchar.o
+obj-$(CONFIG_DIAG_SDIO_PIPE) += diagfwd_sdio.o
+obj-$(CONFIG_DIAG_HSIC_PIPE) += diagfwd_hsic.o
+diagchar-objs := diagchar_core.o diagchar_hdlc.o diagfwd.o diagmem.o diagfwd_cntl.o diag_dci.o
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
new file mode 100644
index 0000000..5cbf888
--- /dev/null
+++ b/drivers/char/diag/diag_dci.c
@@ -0,0 +1,335 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/uaccess.h>
+#include <linux/diagchar.h>
+#include <linux/sched.h>
+#include <linux/err.h>
+#include <linux/workqueue.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <asm/current.h>
+#ifdef CONFIG_DIAG_OVER_USB
+#include <mach/usbdiag.h>
+#endif
+#include "diagchar_hdlc.h"
+#include "diagmem.h"
+#include "diagchar.h"
+#include "diagfwd.h"
+#include "diag_dci.h"
+
+unsigned int dci_max_reg = 100;
+unsigned int dci_max_clients = 10;
+
+static void diag_smd_dci_send_req(int proc_num)
+{
+	void *buf = NULL;
+	smd_channel_t *smd_ch = NULL;
+	int i, r, found = 1;
+	int cmd_code_len = 1;
+
+	if (driver->in_busy_dci)
+		return;
+
+	if (proc_num == MODEM_PROC) {
+		buf = driver->buf_in_dci;
+		smd_ch = driver->ch_dci;
+	}
+
+	if (!smd_ch || !buf)
+		return;
+
+	r = smd_read_avail(smd_ch);
+	if (r > IN_BUF_SIZE) {
+		if (r < MAX_IN_BUF_SIZE) {
+			pr_err("diag: SMD DCI sending pkt upto %d bytes", r);
+			buf = krealloc(buf, r, GFP_KERNEL);
+		} else {
+			pr_err("diag: DCI pkt > %d bytes", MAX_IN_BUF_SIZE);
+			return;
+		}
+	}
+	if (buf && r > 0) {
+		smd_read(smd_ch, buf, r);
+		pr_debug("diag: data received ---\n");
+		for (i = 0; i < r; i++)
+			pr_debug("\t %x \t", *(((unsigned char *)buf)+i));
+
+		if (*(uint8_t *)(buf+4) != DCI_CMD_CODE)
+			cmd_code_len = 4; /* delayed response */
+		driver->write_ptr_dci->length =
+			 (int)(*(uint16_t *)(buf+2)) - (4+cmd_code_len);
+		pr_debug("diag: len = %d\n", (int)(*(uint16_t *)(buf+2))
+							 - (4+cmd_code_len));
+		/* look up DCI client with tag */
+		for (i = 0; i < dci_max_reg; i++) {
+			if (driver->dci_tbl[i].tag ==
+			    *(int *)(buf+(4+cmd_code_len))) {
+				found = 0;
+				break;
+			}
+		}
+		if (found)
+			pr_alert("diag: No matching PID for DCI data\n");
+		pr_debug("\n diag PID = %d", driver->dci_tbl[i].pid);
+		if (driver->dci_tbl[i].pid == 0)
+			pr_alert("diag: Receiving DCI process deleted\n");
+		*(int *)(buf+4+cmd_code_len) = driver->dci_tbl[i].uid;
+		/* update len after adding UID */
+		driver->write_ptr_dci->length =
+			driver->write_ptr_dci->length + 4;
+		pr_debug("diag: data receivd, wake process\n");
+		driver->in_busy_dci = 1;
+		diag_update_sleeping_process(driver->dci_tbl[i].pid,
+							DCI_DATA_TYPE);
+		/* delete immediate response entry */
+		if (driver->buf_in_dci[8+cmd_code_len] != 0x80)
+			driver->dci_tbl[i].pid = 0;
+		for (i = 0; i < dci_max_reg; i++)
+			if (driver->dci_tbl[i].pid != 0)
+				pr_debug("diag: PID = %d, UID = %d, tag = %d\n",
+				driver->dci_tbl[i].pid, driver->dci_tbl[i].uid,
+				 driver->dci_tbl[i].tag);
+		pr_debug("diag: completed clearing table\n");
+	}
+}
+
+void diag_read_smd_dci_work_fn(struct work_struct *work)
+{
+	diag_smd_dci_send_req(MODEM_PROC);
+}
+
+static void diag_smd_dci_notify(void *ctxt, unsigned event)
+{
+	queue_work(driver->diag_wq, &(driver->diag_read_smd_dci_work));
+}
+
+static int diag_dci_probe(struct platform_device *pdev)
+{
+	int err = 0;
+
+	if (pdev->id == SMD_APPS_MODEM) {
+		err = smd_open("DIAG_2", &driver->ch_dci, driver,
+					    diag_smd_dci_notify);
+		if (err)
+			pr_err("diag: cannot open DCI port, Id = %d, err ="
+				" %d\n", pdev->id, err);
+	}
+	return err;
+}
+
+
+int diag_send_dci_pkt(struct diag_master_table entry, unsigned char *buf,
+					 int len, int index)
+{
+	int i;
+
+	/* remove UID from user space pkt before sending to peripheral */
+	buf = buf + 4;
+	len = len - 4;
+	mutex_lock(&driver->dci_mutex);
+	/* prepare DCI packet */
+	driver->apps_dci_buf[0] = CONTROL_CHAR; /* start */
+	driver->apps_dci_buf[1] = 1; /* version */
+	*(uint16_t *)(driver->apps_dci_buf + 2) = len + 4 + 1; /* length */
+	driver->apps_dci_buf[4] = DCI_CMD_CODE; /* DCI ID */
+	*(int *)(driver->apps_dci_buf + 5) = driver->dci_tbl[index].tag;
+	for (i = 0; i < len; i++)
+		driver->apps_dci_buf[i+9] = *(buf+i);
+	driver->apps_dci_buf[9+len] = CONTROL_CHAR; /* end */
+
+	if (entry.client_id == MODEM_PROC && driver->ch_dci) {
+		smd_write(driver->ch_dci, driver->apps_dci_buf, len + 10);
+		i = DIAG_DCI_NO_ERROR;
+	} else {
+		pr_alert("diag: check DCI channel\n");
+		i = DIAG_DCI_SEND_DATA_FAIL;
+	}
+	mutex_unlock(&driver->dci_mutex);
+	return i;
+}
+
+int diag_register_dci_transaction(int uid)
+{
+	int i, new_dci_client = 1, ret = -1;
+
+	for (i = 0; i < dci_max_reg; i++) {
+		if (driver->dci_tbl[i].pid == current->tgid) {
+			new_dci_client = 0;
+			break;
+		}
+	}
+	mutex_lock(&driver->dci_mutex);
+	if (new_dci_client)
+		driver->num_dci_client++;
+	if (driver->num_dci_client > MAX_DCI_CLIENT) {
+		pr_info("diag: Max DCI Client limit reached\n");
+		driver->num_dci_client--;
+		mutex_unlock(&driver->dci_mutex);
+		return ret;
+	}
+	/* Make an entry in kernel DCI table */
+	driver->dci_tag++;
+	for (i = 0; i < dci_max_reg; i++) {
+		if (driver->dci_tbl[i].pid == 0) {
+			driver->dci_tbl[i].pid = current->tgid;
+			driver->dci_tbl[i].uid = uid;
+			driver->dci_tbl[i].tag = driver->dci_tag;
+			ret = i;
+			break;
+		}
+	}
+	mutex_unlock(&driver->dci_mutex);
+	return ret;
+}
+
+int diag_process_dci_client(unsigned char *buf, int len)
+{
+	unsigned char *temp = buf;
+	uint16_t subsys_cmd_code;
+	int subsys_id, cmd_code, i, ret = -1, index = -1;
+	struct diag_master_table entry;
+
+	/* enter this UID into kernel table and return index */
+	index = diag_register_dci_transaction(*(int *)temp);
+	if (index < 0) {
+		pr_alert("diag: registering new DCI transaction failed\n");
+		return DIAG_DCI_NO_REG;
+	}
+	temp += 4;
+	/* Check for registered peripheral and fwd pkt to apropriate proc */
+	cmd_code = (int)(*(char *)buf);
+	temp++;
+	subsys_id = (int)(*(char *)temp);
+	temp++;
+	subsys_cmd_code = *(uint16_t *)temp;
+	temp += 2;
+	pr_debug("diag: %d %d %d", cmd_code, subsys_id, subsys_cmd_code);
+	for (i = 0; i < diag_max_reg; i++) {
+		entry = driver->table[i];
+		if (entry.process_id != NO_PROCESS) {
+			if (entry.cmd_code == cmd_code && entry.subsys_id ==
+				 subsys_id && entry.cmd_code_lo <=
+							 subsys_cmd_code &&
+				  entry.cmd_code_hi >= subsys_cmd_code) {
+				ret = diag_send_dci_pkt(entry, buf, len, index);
+			} else if (entry.cmd_code == 255
+				  && cmd_code == 75) {
+				if (entry.subsys_id ==
+					subsys_id &&
+				   entry.cmd_code_lo <=
+					subsys_cmd_code &&
+					 entry.cmd_code_hi >=
+					subsys_cmd_code) {
+					ret = diag_send_dci_pkt(entry, buf, len,
+								 index);
+				}
+			} else if (entry.cmd_code == 255 &&
+				  entry.subsys_id == 255) {
+				if (entry.cmd_code_lo <=
+						 cmd_code &&
+						 entry.
+						cmd_code_hi >= cmd_code) {
+					ret = diag_send_dci_pkt(entry, buf, len,
+								 index);
+				}
+			}
+		}
+	}
+	return ret;
+}
+
+static int diag_dci_runtime_suspend(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: suspending...\n");
+	return 0;
+}
+
+static int diag_dci_runtime_resume(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: resuming...\n");
+	return 0;
+}
+
+static const struct dev_pm_ops diag_dci_dev_pm_ops = {
+	.runtime_suspend = diag_dci_runtime_suspend,
+	.runtime_resume = diag_dci_runtime_resume,
+};
+
+struct platform_driver msm_diag_dci_driver = {
+	.probe = diag_dci_probe,
+	.driver = {
+		   .name = "DIAG_2",
+		   .owner = THIS_MODULE,
+		   .pm   = &diag_dci_dev_pm_ops,
+		   },
+};
+
+int diag_dci_init(void)
+{
+	int success = 0;
+
+	driver->dci_tag = 0;
+	driver->dci_client_id = 0;
+	driver->num_dci_client = 0;
+	driver->in_busy_dci = 0;
+	mutex_init(&driver->dci_mutex);
+	if (driver->buf_in_dci == NULL) {
+		driver->buf_in_dci = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
+		if (driver->buf_in_dci == NULL)
+			goto err;
+	}
+	if (driver->write_ptr_dci == NULL) {
+		driver->write_ptr_dci = kzalloc(
+			sizeof(struct diag_write_device), GFP_KERNEL);
+		if (driver->write_ptr_dci == NULL)
+			goto err;
+	}
+	if (driver->dci_tbl == NULL) {
+		driver->dci_tbl = kzalloc(dci_max_reg *
+			sizeof(struct diag_dci_tbl), GFP_KERNEL);
+		if (driver->dci_tbl == NULL)
+			goto err;
+	}
+	if (driver->apps_dci_buf == NULL) {
+		driver->apps_dci_buf = kzalloc(APPS_BUF_SIZE, GFP_KERNEL);
+		if (driver->apps_dci_buf == NULL)
+			goto err;
+	}
+	success = platform_driver_register(&msm_diag_dci_driver);
+	if (success) {
+		pr_err("diag: Could not register DCI driver\n");
+		goto err;
+	}
+	return DIAG_DCI_NO_ERROR;
+err:
+	pr_err("diag: Could not initialize diag DCI buffers");
+	kfree(driver->dci_tbl);
+	kfree(driver->apps_dci_buf);
+	kfree(driver->buf_in_dci);
+	kfree(driver->write_ptr_dci);
+	return DIAG_DCI_NO_REG;
+}
+
+void diag_dci_exit(void)
+{
+	smd_close(driver->ch_dci);
+	driver->ch_dci = 0;
+	platform_driver_unregister(&msm_diag_dci_driver);
+	kfree(driver->dci_tbl);
+	kfree(driver->apps_dci_buf);
+	kfree(driver->buf_in_dci);
+	kfree(driver->write_ptr_dci);
+}
+
diff --git a/drivers/char/diag/diag_dci.h b/drivers/char/diag/diag_dci.h
new file mode 100644
index 0000000..cc6e0cf
--- /dev/null
+++ b/drivers/char/diag/diag_dci.h
@@ -0,0 +1,46 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef DIAG_DCI_H
+#define DIAG_DCI_H
+#define MAX_DCI_CLIENT 10
+#define DCI_CMD_CODE 0x93
+
+extern unsigned int dci_max_reg;
+extern unsigned int dci_max_clients;
+struct diag_dci_tbl {
+	int pid;
+	int uid;
+	int tag;
+};
+
+#define DIAG_CON_APSS (0x0001)	/* Bit mask for APSS */
+#define DIAG_CON_MPSS (0x0002)	/* Bit mask for MPSS */
+#define DIAG_CON_LPASS (0x0004)	/* Bit mask for LPASS */
+#define DIAG_CON_WCNSS (0x0008)	/* Bit mask for WCNSS */
+
+enum {
+	DIAG_DCI_NO_ERROR = 1001,	/* No error */
+	DIAG_DCI_NO_REG,		/* Could not register */
+	DIAG_DCI_NO_MEM,		/* Failed memory allocation */
+	DIAG_DCI_NOT_SUPPORTED,	/* This particular client is not supported */
+	DIAG_DCI_HUGE_PACKET,	/* Request/Response Packet too huge */
+	DIAG_DCI_SEND_DATA_FAIL,/* writing to kernel or peripheral fails */
+	DIAG_DCI_TABLE_ERR	/* Error dealing with registration tables */
+};
+
+int diag_dci_init(void);
+void diag_dci_exit(void);
+void diag_read_smd_dci_work_fn(struct work_struct *);
+int diag_process_dci_client(unsigned char *buf, int len);
+int diag_send_dci_pkt(struct diag_master_table entry, unsigned char *buf,
+							 int len, int index);
+#endif
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
new file mode 100644
index 0000000..49d687d
--- /dev/null
+++ b/drivers/char/diag/diagchar.h
@@ -0,0 +1,283 @@
+/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef DIAGCHAR_H
+#define DIAGCHAR_H
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mempool.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <mach/msm_smd.h>
+#include <asm/atomic.h>
+#include <asm/mach-types.h>
+/* Size of the USB buffers used for read and write*/
+#define USB_MAX_OUT_BUF 4096
+#define APPS_BUF_SIZE	2000
+#define IN_BUF_SIZE		16384
+#define MAX_IN_BUF_SIZE	32768
+#define MAX_SYNC_OBJ_NAME_SIZE	32
+/* Size of the buffer used for deframing a packet
+  reveived from the PC tool*/
+#define HDLC_MAX 4096
+#define HDLC_OUT_BUF_SIZE	8192
+#define POOL_TYPE_COPY		1
+#define POOL_TYPE_HDLC		2
+#define POOL_TYPE_WRITE_STRUCT	4
+#define POOL_TYPE_ALL		7
+#define MODEM_DATA 		1
+#define QDSP_DATA  		2
+#define APPS_DATA  		3
+#define SDIO_DATA		4
+#define WCNSS_DATA		5
+#define HSIC_DATA		6
+#define MODEM_PROC		0
+#define APPS_PROC		1
+#define QDSP_PROC		2
+#define WCNSS_PROC		3
+#define MSG_MASK_SIZE 9500
+#define LOG_MASK_SIZE 8000
+#define EVENT_MASK_SIZE 1000
+#define USER_SPACE_DATA 8000
+#define PKT_SIZE 4096
+#define MAX_EQUIP_ID 15
+#define DIAG_CTRL_MSG_LOG_MASK	9
+#define DIAG_CTRL_MSG_EVENT_MASK	10
+#define DIAG_CTRL_MSG_F3_MASK	11
+#define CONTROL_CHAR	0x7E
+
+/* Maximum number of pkt reg supported at initialization*/
+extern unsigned int diag_max_reg;
+extern unsigned int diag_threshold_reg;
+
+#define APPEND_DEBUG(ch) \
+do {							\
+	diag_debug_buf[diag_debug_buf_idx] = ch; \
+	(diag_debug_buf_idx < 1023) ? \
+	(diag_debug_buf_idx++) : (diag_debug_buf_idx = 0); \
+} while (0)
+
+struct diag_master_table {
+	uint16_t cmd_code;
+	uint16_t subsys_id;
+	uint32_t client_id;
+	uint16_t cmd_code_lo;
+	uint16_t cmd_code_hi;
+	int process_id;
+};
+
+struct bindpkt_params_per_process {
+	/* Name of the synchronization object associated with this proc */
+	char sync_obj_name[MAX_SYNC_OBJ_NAME_SIZE];
+	uint32_t count;	/* Number of entries in this bind */
+	struct bindpkt_params *params; /* first bind params */
+};
+
+struct bindpkt_params {
+	uint16_t cmd_code;
+	uint16_t subsys_id;
+	uint16_t cmd_code_lo;
+	uint16_t cmd_code_hi;
+	/* For Central Routing, used to store Processor number */
+	uint16_t proc_id;
+	uint32_t event_id;
+	uint32_t log_code;
+	/* For Central Routing, used to store SMD channel pointer */
+	uint32_t client_id;
+};
+
+struct diag_write_device {
+	void *buf;
+	int length;
+};
+
+struct diag_client_map {
+	char name[20];
+	int pid;
+};
+
+/* This structure is defined in USB header file */
+#ifndef CONFIG_DIAG_OVER_USB
+struct diag_request {
+	char *buf;
+	int length;
+	int actual;
+	int status;
+	void *context;
+};
+#endif
+
+struct diagchar_dev {
+
+	/* State for the char driver */
+	unsigned int major;
+	unsigned int minor_start;
+	int num;
+	struct cdev *cdev;
+	char *name;
+	int dropped_count;
+	struct class *diagchar_class;
+	int ref_count;
+	struct mutex diagchar_mutex;
+	wait_queue_head_t wait_q;
+	struct diag_client_map *client_map;
+	int *data_ready;
+	int num_clients;
+	int polling_reg_flag;
+	struct diag_write_device *buf_tbl;
+	int use_device_tree;
+	/* DCI related variables */
+	struct diag_dci_tbl *dci_tbl;
+	int dci_tag;
+	int dci_client_id;
+	struct mutex dci_mutex;
+	int num_dci_client;
+	unsigned char *apps_dci_buf;
+	int dci_state;
+	/* Memory pool parameters */
+	unsigned int itemsize;
+	unsigned int poolsize;
+	unsigned int itemsize_hdlc;
+	unsigned int poolsize_hdlc;
+	unsigned int itemsize_write_struct;
+	unsigned int poolsize_write_struct;
+	unsigned int debug_flag;
+	/* State for the mempool for the char driver */
+	mempool_t *diagpool;
+	mempool_t *diag_hdlc_pool;
+	mempool_t *diag_write_struct_pool;
+	struct mutex diagmem_mutex;
+	int count;
+	int count_hdlc_pool;
+	int count_write_struct_pool;
+	int used;
+	/* Buffers for masks */
+	struct mutex diag_cntl_mutex;
+	struct diag_ctrl_event_mask *event_mask;
+	struct diag_ctrl_log_mask *log_mask;
+	struct diag_ctrl_msg_mask *msg_mask;
+	/* State for diag forwarding */
+	unsigned char *buf_in_1;
+	unsigned char *buf_in_2;
+	unsigned char *buf_in_cntl;
+	unsigned char *buf_in_qdsp_1;
+	unsigned char *buf_in_qdsp_2;
+	unsigned char *buf_in_qdsp_cntl;
+	unsigned char *buf_in_wcnss_1;
+	unsigned char *buf_in_wcnss_2;
+	unsigned char *buf_in_wcnss_cntl;
+	unsigned char *buf_in_dci;
+	unsigned char *usb_buf_out;
+	unsigned char *apps_rsp_buf;
+	unsigned char *user_space_data;
+	/* buffer for updating mask to peripherals */
+	unsigned char *buf_msg_mask_update;
+	unsigned char *buf_log_mask_update;
+	unsigned char *buf_event_mask_update;
+	smd_channel_t *ch;
+	smd_channel_t *ch_cntl;
+	smd_channel_t *ch_dci;
+	smd_channel_t *chqdsp;
+	smd_channel_t *chqdsp_cntl;
+	smd_channel_t *ch_wcnss;
+	smd_channel_t *ch_wcnss_cntl;
+	int in_busy_1;
+	int in_busy_2;
+	int in_busy_qdsp_1;
+	int in_busy_qdsp_2;
+	int in_busy_wcnss_1;
+	int in_busy_wcnss_2;
+	int in_busy_dci;
+	int read_len_legacy;
+	unsigned char *hdlc_buf;
+	unsigned hdlc_count;
+	unsigned hdlc_escape;
+#ifdef CONFIG_DIAG_OVER_USB
+	int usb_connected;
+	struct usb_diag_ch *legacy_ch;
+	struct work_struct diag_proc_hdlc_work;
+	struct work_struct diag_read_work;
+#endif
+	struct workqueue_struct *diag_wq;
+	struct work_struct diag_drain_work;
+	struct work_struct diag_read_smd_work;
+	struct work_struct diag_read_smd_cntl_work;
+	struct work_struct diag_read_smd_qdsp_work;
+	struct work_struct diag_read_smd_qdsp_cntl_work;
+	struct work_struct diag_read_smd_wcnss_work;
+	struct work_struct diag_read_smd_wcnss_cntl_work;
+	struct workqueue_struct *diag_cntl_wq;
+	struct work_struct diag_modem_mask_update_work;
+	struct work_struct diag_qdsp_mask_update_work;
+	struct work_struct diag_wcnss_mask_update_work;
+	struct work_struct diag_read_smd_dci_work;
+	uint8_t *msg_masks;
+	uint8_t *log_masks;
+	int log_masks_length;
+	uint8_t *event_masks;
+	struct diag_master_table *table;
+	uint8_t *pkt_buf;
+	int pkt_length;
+	struct diag_request *write_ptr_1;
+	struct diag_request *write_ptr_2;
+	struct diag_request *usb_read_ptr;
+	struct diag_request *write_ptr_svc;
+	struct diag_request *write_ptr_qdsp_1;
+	struct diag_request *write_ptr_qdsp_2;
+	struct diag_request *write_ptr_wcnss_1;
+	struct diag_request *write_ptr_wcnss_2;
+	struct diag_write_device *write_ptr_dci;
+	int logging_mode;
+	int mask_check;
+	int logging_process_id;
+#ifdef CONFIG_DIAG_SDIO_PIPE
+	unsigned char *buf_in_sdio;
+	unsigned char *usb_buf_mdm_out;
+	struct sdio_channel *sdio_ch;
+	int read_len_mdm;
+	int in_busy_sdio;
+	struct usb_diag_ch *mdm_ch;
+	struct work_struct diag_read_mdm_work;
+	struct workqueue_struct *diag_sdio_wq;
+	struct work_struct diag_read_sdio_work;
+	struct work_struct diag_close_sdio_work;
+	struct diag_request *usb_read_mdm_ptr;
+	struct diag_request *write_ptr_mdm;
+#endif
+#ifdef CONFIG_DIAG_HSIC_PIPE
+	unsigned char *buf_in_hsic;
+	unsigned char *usb_buf_mdm_out;
+	int hsic_initialized;
+	int hsic_ch;
+	int hsic_device_enabled;
+	int hsic_device_opened;
+	int hsic_suspend;
+	int read_len_mdm;
+	int in_busy_hsic_read_on_device;
+	int in_busy_hsic_write_on_device;
+	int in_busy_hsic_write;
+	int in_busy_hsic_read;
+	int usb_mdm_connected;
+	struct usb_diag_ch *mdm_ch;
+	struct workqueue_struct *diag_hsic_wq;
+	struct work_struct diag_read_mdm_work;
+	struct work_struct diag_read_hsic_work;
+	struct work_struct diag_disconnect_work;
+	struct work_struct diag_usb_read_complete_work;
+	struct diag_request *usb_read_mdm_ptr;
+	struct diag_request *write_ptr_mdm;
+#endif
+};
+
+extern struct diagchar_dev *driver;
+#endif
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
new file mode 100644
index 0000000..58a8676
--- /dev/null
+++ b/drivers/char/diag/diagchar_core.c
@@ -0,0 +1,1306 @@
+/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/uaccess.h>
+#include <linux/diagchar.h>
+#include <linux/sched.h>
+#ifdef CONFIG_DIAG_OVER_USB
+#include <mach/usbdiag.h>
+#endif
+#include <asm/current.h>
+#include "diagchar_hdlc.h"
+#include "diagmem.h"
+#include "diagchar.h"
+#include "diagfwd.h"
+#include "diagfwd_cntl.h"
+#include "diag_dci.h"
+#ifdef CONFIG_DIAG_SDIO_PIPE
+#include "diagfwd_sdio.h"
+#endif
+#ifdef CONFIG_DIAG_HSIC_PIPE
+#include "diagfwd_hsic.h"
+#endif
+#include <linux/timer.h>
+
+MODULE_DESCRIPTION("Diag Char Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("1.0");
+
+#define INIT	1
+#define EXIT	-1
+struct diagchar_dev *driver;
+struct diagchar_priv {
+	int pid;
+};
+/* The following variables can be specified by module options */
+ /* for copy buffer */
+static unsigned int itemsize = 4096; /*Size of item in the mempool */
+static unsigned int poolsize = 10; /*Number of items in the mempool */
+/* for hdlc buffer */
+static unsigned int itemsize_hdlc = 8192; /*Size of item in the mempool */
+static unsigned int poolsize_hdlc = 8;  /*Number of items in the mempool */
+/* for write structure buffer */
+static unsigned int itemsize_write_struct = 20; /*Size of item in the mempool */
+static unsigned int poolsize_write_struct = 8; /* Num of items in the mempool */
+/* This is the max number of user-space clients supported at initialization*/
+static unsigned int max_clients = 15;
+static unsigned int threshold_client_limit = 30;
+/* This is the maximum number of pkt registrations supported at initialization*/
+unsigned int diag_max_reg = 600;
+unsigned int diag_threshold_reg = 750;
+
+/* Timer variables */
+static struct timer_list drain_timer;
+static int timer_in_progress;
+void *buf_hdlc;
+module_param(itemsize, uint, 0);
+module_param(poolsize, uint, 0);
+module_param(max_clients, uint, 0);
+
+/* delayed_rsp_id 0 represents no delay in the response. Any other number
+    means that the diag packet has a delayed response. */
+static uint16_t delayed_rsp_id = 1;
+#define DIAGPKT_MAX_DELAYED_RSP 0xFFFF
+/* This macro gets the next delayed respose id. Once it reaches
+ DIAGPKT_MAX_DELAYED_RSP, it stays at DIAGPKT_MAX_DELAYED_RSP */
+
+#define DIAGPKT_NEXT_DELAYED_RSP_ID(x) 				\
+((x < DIAGPKT_MAX_DELAYED_RSP) ? x++ : DIAGPKT_MAX_DELAYED_RSP)
+
+#define COPY_USER_SPACE_OR_EXIT(buf, data, length)		\
+do {								\
+	if ((count < ret+length) || (copy_to_user(buf,		\
+			(void *)&data, length))) {		\
+		ret = -EFAULT;					\
+		goto exit;					\
+	}							\
+	ret += length;						\
+} while (0)
+
+static void drain_timer_func(unsigned long data)
+{
+	queue_work(driver->diag_wq , &(driver->diag_drain_work));
+}
+
+void diag_drain_work_fn(struct work_struct *work)
+{
+	int err = 0;
+	timer_in_progress = 0;
+
+	mutex_lock(&driver->diagchar_mutex);
+	if (buf_hdlc) {
+		err = diag_device_write(buf_hdlc, APPS_DATA, NULL);
+		if (err) {
+			/*Free the buffer right away if write failed */
+			diagmem_free(driver, buf_hdlc, POOL_TYPE_HDLC);
+			diagmem_free(driver, (unsigned char *)driver->
+				 write_ptr_svc, POOL_TYPE_WRITE_STRUCT);
+		}
+		buf_hdlc = NULL;
+#ifdef DIAG_DEBUG
+		pr_debug("diag: Number of bytes written "
+				 "from timer is %d ", driver->used);
+#endif
+		driver->used = 0;
+	}
+	mutex_unlock(&driver->diagchar_mutex);
+}
+
+void diag_read_smd_work_fn(struct work_struct *work)
+{
+	__diag_smd_send_req();
+}
+
+void diag_read_smd_qdsp_work_fn(struct work_struct *work)
+{
+	__diag_smd_qdsp_send_req();
+}
+
+void diag_read_smd_wcnss_work_fn(struct work_struct *work)
+{
+	__diag_smd_wcnss_send_req();
+}
+
+void diag_add_client(int i, struct file *file)
+{
+	struct diagchar_priv *diagpriv_data;
+
+	driver->client_map[i].pid = current->tgid;
+	diagpriv_data = kmalloc(sizeof(struct diagchar_priv),
+							GFP_KERNEL);
+	if (diagpriv_data)
+		diagpriv_data->pid = current->tgid;
+	file->private_data = diagpriv_data;
+	strlcpy(driver->client_map[i].name, current->comm, 20);
+	driver->client_map[i].name[19] = '\0';
+}
+
+static int diagchar_open(struct inode *inode, struct file *file)
+{
+	int i = 0;
+	void *temp;
+
+	if (driver) {
+		mutex_lock(&driver->diagchar_mutex);
+
+		for (i = 0; i < driver->num_clients; i++)
+			if (driver->client_map[i].pid == 0)
+				break;
+
+		if (i < driver->num_clients) {
+			diag_add_client(i, file);
+		} else {
+			if (i < threshold_client_limit) {
+				driver->num_clients++;
+				temp = krealloc(driver->client_map
+					, (driver->num_clients) * sizeof(struct
+						 diag_client_map), GFP_KERNEL);
+				if (!temp)
+					goto fail;
+				else
+					driver->client_map = temp;
+				temp = krealloc(driver->data_ready
+					, (driver->num_clients) * sizeof(int),
+							GFP_KERNEL);
+				if (!temp)
+					goto fail;
+				else
+					driver->data_ready = temp;
+				diag_add_client(i, file);
+			} else {
+				mutex_unlock(&driver->diagchar_mutex);
+				pr_alert("Max client limit for DIAG reached\n");
+				pr_info("Cannot open handle %s"
+					   " %d", current->comm, current->tgid);
+				for (i = 0; i < driver->num_clients; i++)
+					pr_debug("%d) %s PID=%d", i, driver->
+						client_map[i].name,
+						driver->client_map[i].pid);
+				return -ENOMEM;
+			}
+		}
+		driver->data_ready[i] = 0x0;
+		driver->data_ready[i] |= MSG_MASKS_TYPE;
+		driver->data_ready[i] |= EVENT_MASKS_TYPE;
+		driver->data_ready[i] |= LOG_MASKS_TYPE;
+
+		if (driver->ref_count == 0)
+			diagmem_init(driver);
+		driver->ref_count++;
+		mutex_unlock(&driver->diagchar_mutex);
+		return 0;
+	}
+	return -ENOMEM;
+
+fail:
+	mutex_unlock(&driver->diagchar_mutex);
+	driver->num_clients--;
+	pr_alert("diag: Insufficient memory for new client");
+	return -ENOMEM;
+}
+
+static int diagchar_close(struct inode *inode, struct file *file)
+{
+	int i = 0;
+	struct diagchar_priv *diagpriv_data = file->private_data;
+
+	if (!(file->private_data)) {
+		pr_alert("diag: Invalid file pointer");
+		return -ENOMEM;
+	}
+
+	/* clean up any DCI registrations for this client
+	* This will specially help in case of ungraceful exit of any DCI client
+	* This call will remove any pending registrations of such client
+	*/
+	diagchar_ioctl(NULL, DIAG_IOCTL_DCI_DEINIT, 0);
+#ifdef CONFIG_DIAG_OVER_USB
+	/* If the SD logging process exits, change logging to USB mode */
+	if (driver->logging_process_id == current->tgid) {
+		driver->logging_mode = USB_MODE;
+		diagfwd_connect();
+#ifdef CONFIG_DIAG_HSIC_PIPE
+		diagfwd_cancel_hsic();
+		diagfwd_connect_hsic(0);
+#endif
+	}
+#endif /* DIAG over USB */
+	/* Delete the pkt response table entry for the exiting process */
+	for (i = 0; i < diag_max_reg; i++)
+			if (driver->table[i].process_id == current->tgid)
+					driver->table[i].process_id = 0;
+
+	if (driver) {
+		mutex_lock(&driver->diagchar_mutex);
+		driver->ref_count--;
+		/* On Client exit, try to destroy all 3 pools */
+		diagmem_exit(driver, POOL_TYPE_COPY);
+		diagmem_exit(driver, POOL_TYPE_HDLC);
+		diagmem_exit(driver, POOL_TYPE_WRITE_STRUCT);
+		for (i = 0; i < driver->num_clients; i++) {
+			if (NULL != diagpriv_data && diagpriv_data->pid ==
+				 driver->client_map[i].pid) {
+				driver->client_map[i].pid = 0;
+				kfree(diagpriv_data);
+				diagpriv_data = NULL;
+				break;
+			}
+		}
+		mutex_unlock(&driver->diagchar_mutex);
+		return 0;
+	}
+	return -ENOMEM;
+}
+
+int diag_find_polling_reg(int i)
+{
+	uint16_t subsys_id, cmd_code_lo, cmd_code_hi;
+
+	subsys_id = driver->table[i].subsys_id;
+	cmd_code_lo = driver->table[i].cmd_code_lo;
+	cmd_code_hi = driver->table[i].cmd_code_hi;
+	if (driver->table[i].cmd_code == 0x0C)
+		return 1;
+	else if (driver->table[i].cmd_code == 0xFF) {
+		if (subsys_id == 0x04 && cmd_code_hi == 0x0E &&
+			 cmd_code_lo == 0x0E)
+			return 1;
+		else if (subsys_id == 0x08 && cmd_code_hi == 0x02 &&
+			 cmd_code_lo == 0x02)
+			return 1;
+		else if (subsys_id == 0x32 && cmd_code_hi == 0x03  &&
+			 cmd_code_lo == 0x03)
+			return 1;
+	}
+	return 0;
+}
+
+void diag_clear_reg(int proc_num)
+{
+	int i;
+
+	mutex_lock(&driver->diagchar_mutex);
+	/* reset polling flag */
+	driver->polling_reg_flag = 0;
+	for (i = 0; i < diag_max_reg; i++) {
+		if (driver->table[i].client_id == proc_num) {
+			driver->table[i].process_id = 0;
+		}
+	}
+	/* re-scan the registration table */
+	for (i = 0; i < diag_max_reg; i++) {
+		if (diag_find_polling_reg(i) == 1) {
+			driver->polling_reg_flag = 1;
+			break;
+		}
+	}
+	mutex_unlock(&driver->diagchar_mutex);
+}
+
+void diag_add_reg(int j, struct bindpkt_params *params,
+					  int *success, int *count_entries)
+{
+	*success = 1;
+	driver->table[j].cmd_code = params->cmd_code;
+	driver->table[j].subsys_id = params->subsys_id;
+	driver->table[j].cmd_code_lo = params->cmd_code_lo;
+	driver->table[j].cmd_code_hi = params->cmd_code_hi;
+
+	/* check if incoming reg is polling & polling is yet not registered */
+	if (driver->polling_reg_flag == 0)
+		if (diag_find_polling_reg(j) == 1)
+			driver->polling_reg_flag = 1;
+	if (params->proc_id == APPS_PROC) {
+		driver->table[j].process_id = current->tgid;
+		driver->table[j].client_id = APPS_PROC;
+	} else {
+		driver->table[j].process_id = NON_APPS_PROC;
+		driver->table[j].client_id = params->client_id;
+	}
+	(*count_entries)++;
+}
+
+long diagchar_ioctl(struct file *filp,
+			   unsigned int iocmd, unsigned long ioarg)
+{
+	int i, j, count_entries = 0, temp;
+	int success = -1;
+	void *temp_buf;
+	uint16_t support_list = 0;
+
+	if (iocmd == DIAG_IOCTL_COMMAND_REG) {
+		struct bindpkt_params_per_process *pkt_params =
+			 (struct bindpkt_params_per_process *) ioarg;
+		mutex_lock(&driver->diagchar_mutex);
+		for (i = 0; i < diag_max_reg; i++) {
+			if (driver->table[i].process_id == 0) {
+				diag_add_reg(i, pkt_params->params,
+						&success, &count_entries);
+				if (pkt_params->count > count_entries) {
+					pkt_params->params++;
+				} else {
+					mutex_unlock(&driver->diagchar_mutex);
+					return success;
+				}
+			}
+		}
+		if (i < diag_threshold_reg) {
+			/* Increase table size by amount required */
+			diag_max_reg += pkt_params->count -
+							 count_entries;
+			/* Make sure size doesnt go beyond threshold */
+			if (diag_max_reg > diag_threshold_reg) {
+				diag_max_reg = diag_threshold_reg;
+				pr_info("diag: best case memory allocation\n");
+			}
+			temp_buf = krealloc(driver->table,
+					 diag_max_reg*sizeof(struct
+					 diag_master_table), GFP_KERNEL);
+			if (!temp_buf) {
+				diag_max_reg -= pkt_params->count -
+							 count_entries;
+				pr_alert("diag: Insufficient memory for reg.");
+				mutex_unlock(&driver->diagchar_mutex);
+				return 0;
+			} else {
+				driver->table = temp_buf;
+			}
+			for (j = i; j < diag_max_reg; j++) {
+				diag_add_reg(j, pkt_params->params,
+						&success, &count_entries);
+				if (pkt_params->count > count_entries) {
+					pkt_params->params++;
+				} else {
+					mutex_unlock(&driver->diagchar_mutex);
+					return success;
+				}
+			}
+			mutex_unlock(&driver->diagchar_mutex);
+		} else {
+			mutex_unlock(&driver->diagchar_mutex);
+			pr_err("Max size reached, Pkt Registration failed for"
+						" Process %d", current->tgid);
+		}
+		success = 0;
+	} else if (iocmd == DIAG_IOCTL_GET_DELAYED_RSP_ID) {
+		struct diagpkt_delay_params *delay_params =
+					(struct diagpkt_delay_params *) ioarg;
+
+		if ((delay_params->rsp_ptr) &&
+		 (delay_params->size == sizeof(delayed_rsp_id)) &&
+				 (delay_params->num_bytes_ptr)) {
+			*((uint16_t *)delay_params->rsp_ptr) =
+				DIAGPKT_NEXT_DELAYED_RSP_ID(delayed_rsp_id);
+			*(delay_params->num_bytes_ptr) = sizeof(delayed_rsp_id);
+			success = 0;
+		}
+	} else if (iocmd == DIAG_IOCTL_DCI_REG) {
+		if (driver->dci_state == DIAG_DCI_NO_REG)
+			return DIAG_DCI_NO_REG;
+		/* use the 'list' later on to notify user space */
+		if (driver->num_dci_client >= MAX_DCI_CLIENT)
+			return DIAG_DCI_NO_REG;
+		mutex_lock(&driver->dci_mutex);
+		driver->num_dci_client++;
+		pr_debug("diag: id = %d\n", driver->dci_client_id);
+		driver->dci_client_id++;
+		mutex_unlock(&driver->dci_mutex);
+		return driver->dci_client_id;
+	} else if (iocmd == DIAG_IOCTL_DCI_DEINIT) {
+		success = -1;
+		/* Delete this process from DCI table */
+		mutex_lock(&driver->dci_mutex);
+		for (i = 0; i < dci_max_reg; i++) {
+			if (driver->dci_tbl[i].pid == current->tgid) {
+				pr_debug("diag: delete %d\n", current->tgid);
+				driver->dci_tbl[i].pid = 0;
+				success = i;
+			}
+		}
+		/* if any registrations were deleted successfully OR a valid
+		   client_id was sent in DEINIT call , then its DCI client */
+		if (success >= 0 || ioarg)
+			driver->num_dci_client--;
+		driver->num_dci_client--;
+		mutex_unlock(&driver->dci_mutex);
+		for (i = 0; i < dci_max_reg; i++)
+			if (driver->dci_tbl[i].pid != 0)
+				pr_debug("diag: PID = %d, UID = %d, tag = %d\n",
+	driver->dci_tbl[i].pid, driver->dci_tbl[i].uid, driver->dci_tbl[i].tag);
+		pr_debug("diag: complete deleting registrations\n");
+		return success;
+	} else if (iocmd == DIAG_IOCTL_DCI_SUPPORT) {
+		if (driver->ch_dci)
+			support_list = support_list | DIAG_CON_MPSS;
+		*(uint16_t *)ioarg = support_list;
+		return DIAG_DCI_NO_ERROR;
+	} else if (iocmd == DIAG_IOCTL_LSM_DEINIT) {
+		for (i = 0; i < driver->num_clients; i++)
+			if (driver->client_map[i].pid == current->tgid)
+				break;
+		if (i == -1)
+			return -EINVAL;
+		driver->data_ready[i] |= DEINIT_TYPE;
+		wake_up_interruptible(&driver->wait_q);
+		success = 1;
+	} else if (iocmd == DIAG_IOCTL_SWITCH_LOGGING) {
+		mutex_lock(&driver->diagchar_mutex);
+		temp = driver->logging_mode;
+		driver->logging_mode = (int)ioarg;
+		if (driver->logging_mode == MEMORY_DEVICE_MODE)
+			driver->mask_check = 1;
+		if (driver->logging_mode == UART_MODE) {
+			driver->mask_check = 0;
+			driver->logging_mode = MEMORY_DEVICE_MODE;
+		}
+		driver->logging_process_id = current->tgid;
+		mutex_unlock(&driver->diagchar_mutex);
+		if (temp == MEMORY_DEVICE_MODE && driver->logging_mode
+							== NO_LOGGING_MODE) {
+			driver->in_busy_1 = 1;
+			driver->in_busy_2 = 1;
+			driver->in_busy_qdsp_1 = 1;
+			driver->in_busy_qdsp_2 = 1;
+			driver->in_busy_wcnss_1 = 1;
+			driver->in_busy_wcnss_2 = 1;
+#ifdef CONFIG_DIAG_SDIO_PIPE
+			driver->in_busy_sdio = 1;
+#endif
+#ifdef CONFIG_DIAG_HSIC_PIPE
+			diagfwd_disconnect_hsic(0);
+#endif
+		} else if (temp == NO_LOGGING_MODE && driver->logging_mode
+							== MEMORY_DEVICE_MODE) {
+			driver->in_busy_1 = 0;
+			driver->in_busy_2 = 0;
+			driver->in_busy_qdsp_1 = 0;
+			driver->in_busy_qdsp_2 = 0;
+			driver->in_busy_wcnss_1 = 0;
+			driver->in_busy_wcnss_2 = 0;
+			/* Poll SMD channels to check for data*/
+			if (driver->ch)
+				queue_work(driver->diag_wq,
+					&(driver->diag_read_smd_work));
+			if (driver->chqdsp)
+				queue_work(driver->diag_wq,
+					&(driver->diag_read_smd_qdsp_work));
+			if (driver->ch_wcnss)
+				queue_work(driver->diag_wq,
+					&(driver->diag_read_smd_wcnss_work));
+#ifdef CONFIG_DIAG_SDIO_PIPE
+			driver->in_busy_sdio = 0;
+			/* Poll SDIO channel to check for data */
+			if (driver->sdio_ch)
+				queue_work(driver->diag_sdio_wq,
+					&(driver->diag_read_sdio_work));
+#endif
+#ifdef CONFIG_DIAG_HSIC_PIPE
+			diagfwd_connect_hsic(0);
+#endif
+		}
+#ifdef CONFIG_DIAG_OVER_USB
+		else if (temp == USB_MODE && driver->logging_mode
+							 == NO_LOGGING_MODE) {
+			diagfwd_disconnect();
+#ifdef CONFIG_DIAG_HSIC_PIPE
+			diagfwd_disconnect_hsic(0);
+#endif
+		} else if (temp == NO_LOGGING_MODE && driver->logging_mode
+								== USB_MODE) {
+			diagfwd_connect();
+#ifdef CONFIG_DIAG_HSIC_PIPE
+			diagfwd_connect_hsic(0);
+#endif
+		} else if (temp == USB_MODE && driver->logging_mode
+							== MEMORY_DEVICE_MODE) {
+			diagfwd_disconnect();
+			driver->in_busy_1 = 0;
+			driver->in_busy_2 = 0;
+			driver->in_busy_qdsp_1 = 0;
+			driver->in_busy_qdsp_2 = 0;
+			driver->in_busy_wcnss_1 = 0;
+			driver->in_busy_wcnss_2 = 0;
+			/* Poll SMD channels to check for data*/
+			if (driver->ch)
+				queue_work(driver->diag_wq,
+					 &(driver->diag_read_smd_work));
+			if (driver->chqdsp)
+				queue_work(driver->diag_wq,
+					&(driver->diag_read_smd_qdsp_work));
+			if (driver->ch_wcnss)
+				queue_work(driver->diag_wq,
+					&(driver->diag_read_smd_wcnss_work));
+#ifdef CONFIG_DIAG_SDIO_PIPE
+			driver->in_busy_sdio = 0;
+			/* Poll SDIO channel to check for data */
+			if (driver->sdio_ch)
+				queue_work(driver->diag_sdio_wq,
+					&(driver->diag_read_sdio_work));
+#endif
+#ifdef CONFIG_DIAG_HSIC_PIPE
+			diagfwd_cancel_hsic();
+			diagfwd_connect_hsic(0);
+#endif
+		} else if (temp == MEMORY_DEVICE_MODE &&
+				 driver->logging_mode == USB_MODE) {
+			diagfwd_connect();
+#ifdef CONFIG_DIAG_HSIC_PIPE
+			diagfwd_cancel_hsic();
+			diagfwd_connect_hsic(0);
+#endif
+		}
+#endif /* DIAG over USB */
+		success = 1;
+	}
+
+	return success;
+}
+
+static int diagchar_read(struct file *file, char __user *buf, size_t count,
+			  loff_t *ppos)
+{
+	int index = -1, i = 0, ret = 0;
+	int num_data = 0, data_type;
+	for (i = 0; i < driver->num_clients; i++)
+		if (driver->client_map[i].pid == current->tgid)
+			index = i;
+
+	if (index == -1) {
+		pr_err("diag: Client PID not found in table");
+		return -EINVAL;
+	}
+
+	wait_event_interruptible(driver->wait_q,
+				  driver->data_ready[index]);
+	mutex_lock(&driver->diagchar_mutex);
+
+	if ((driver->data_ready[index] & USER_SPACE_LOG_TYPE) && (driver->
+					logging_mode == MEMORY_DEVICE_MODE)) {
+		pr_debug("diag: process woken up\n");
+		/*Copy the type of data being passed*/
+		data_type = driver->data_ready[index] & USER_SPACE_LOG_TYPE;
+		COPY_USER_SPACE_OR_EXIT(buf, data_type, 4);
+		/* place holder for number of data field */
+		ret += 4;
+
+		for (i = 0; i < driver->poolsize_write_struct; i++) {
+			if (driver->buf_tbl[i].length > 0) {
+#ifdef DIAG_DEBUG
+				pr_debug("diag: WRITING the buf address "
+				       "and length is %x , %d\n", (unsigned int)
+					(driver->buf_tbl[i].buf),
+					driver->buf_tbl[i].length);
+#endif
+				num_data++;
+				/* Copy the length of data being passed */
+				if (copy_to_user(buf+ret, (void *)&(driver->
+						buf_tbl[i].length), 4)) {
+						num_data--;
+						goto drop;
+				}
+				ret += 4;
+
+				/* Copy the actual data being passed */
+				if (copy_to_user(buf+ret, (void *)driver->
+				buf_tbl[i].buf, driver->buf_tbl[i].length)) {
+					ret -= 4;
+					num_data--;
+					goto drop;
+				}
+				ret += driver->buf_tbl[i].length;
+drop:
+#ifdef DIAG_DEBUG
+				pr_debug("diag: DEQUEUE buf address and"
+				       " length is %x,%d\n", (unsigned int)
+				       (driver->buf_tbl[i].buf), driver->
+				       buf_tbl[i].length);
+#endif
+				diagmem_free(driver, (unsigned char *)
+				(driver->buf_tbl[i].buf), POOL_TYPE_HDLC);
+				driver->buf_tbl[i].length = 0;
+				driver->buf_tbl[i].buf = 0;
+			}
+		}
+
+		/* copy modem data */
+		if (driver->in_busy_1 == 1) {
+			num_data++;
+			/*Copy the length of data being passed*/
+			COPY_USER_SPACE_OR_EXIT(buf+ret,
+					 (driver->write_ptr_1->length), 4);
+			/*Copy the actual data being passed*/
+			COPY_USER_SPACE_OR_EXIT(buf+ret,
+					*(driver->buf_in_1),
+					 driver->write_ptr_1->length);
+			driver->in_busy_1 = 0;
+		}
+		if (driver->in_busy_2 == 1) {
+			num_data++;
+			/*Copy the length of data being passed*/
+			COPY_USER_SPACE_OR_EXIT(buf+ret,
+					 (driver->write_ptr_2->length), 4);
+			/*Copy the actual data being passed*/
+			COPY_USER_SPACE_OR_EXIT(buf+ret,
+					 *(driver->buf_in_2),
+					 driver->write_ptr_2->length);
+			driver->in_busy_2 = 0;
+		}
+		/* copy lpass data */
+		if (driver->in_busy_qdsp_1 == 1) {
+			num_data++;
+			/*Copy the length of data being passed*/
+			COPY_USER_SPACE_OR_EXIT(buf+ret,
+				 (driver->write_ptr_qdsp_1->length), 4);
+			/*Copy the actual data being passed*/
+			COPY_USER_SPACE_OR_EXIT(buf+ret, *(driver->
+							buf_in_qdsp_1),
+					 driver->write_ptr_qdsp_1->length);
+			driver->in_busy_qdsp_1 = 0;
+		}
+		if (driver->in_busy_qdsp_2 == 1) {
+			num_data++;
+			/*Copy the length of data being passed*/
+			COPY_USER_SPACE_OR_EXIT(buf+ret,
+				 (driver->write_ptr_qdsp_2->length), 4);
+			/*Copy the actual data being passed*/
+			COPY_USER_SPACE_OR_EXIT(buf+ret, *(driver->
+				buf_in_qdsp_2), driver->
+					write_ptr_qdsp_2->length);
+			driver->in_busy_qdsp_2 = 0;
+		}
+		/* copy wncss data */
+		if (driver->in_busy_wcnss_1 == 1) {
+			num_data++;
+			/*Copy the length of data being passed*/
+			COPY_USER_SPACE_OR_EXIT(buf+ret,
+				 (driver->write_ptr_wcnss_1->length), 4);
+			/*Copy the actual data being passed*/
+			COPY_USER_SPACE_OR_EXIT(buf+ret, *(driver->
+							buf_in_wcnss_1),
+					 driver->write_ptr_wcnss_1->length);
+			driver->in_busy_wcnss_1 = 0;
+		}
+		if (driver->in_busy_wcnss_2 == 1) {
+			num_data++;
+			/*Copy the length of data being passed*/
+			COPY_USER_SPACE_OR_EXIT(buf+ret,
+				 (driver->write_ptr_wcnss_2->length), 4);
+			/*Copy the actual data being passed*/
+			COPY_USER_SPACE_OR_EXIT(buf+ret, *(driver->
+							buf_in_wcnss_2),
+					 driver->write_ptr_wcnss_2->length);
+			driver->in_busy_wcnss_2 = 0;
+		}
+#ifdef CONFIG_DIAG_SDIO_PIPE
+		/* copy 9K data over SDIO */
+		if (driver->in_busy_sdio == 1) {
+			num_data++;
+			/*Copy the length of data being passed*/
+			COPY_USER_SPACE_OR_EXIT(buf+ret,
+				 (driver->write_ptr_mdm->length), 4);
+			/*Copy the actual data being passed*/
+			COPY_USER_SPACE_OR_EXIT(buf+ret,
+					*(driver->buf_in_sdio),
+					 driver->write_ptr_mdm->length);
+			driver->in_busy_sdio = 0;
+		}
+#endif
+#ifdef CONFIG_DIAG_HSIC_PIPE
+		pr_debug("diag: Copy data to user space %d\n",
+			 driver->in_busy_hsic_write_on_device);
+		if (driver->in_busy_hsic_write_on_device == 1) {
+			num_data++;
+			/*Copy the length of data being passed*/
+			COPY_USER_SPACE_OR_EXIT(buf+ret,
+				 (driver->write_ptr_mdm->length), 4);
+			/*Copy the actual data being passed*/
+			COPY_USER_SPACE_OR_EXIT(buf+ret,
+					*(driver->buf_in_hsic),
+					 driver->write_ptr_mdm->length);
+			pr_debug("diag: data copied\n");
+			/* call the write complete function */
+			diagfwd_write_complete_hsic();
+		}
+#endif
+		/* copy number of data fields */
+		COPY_USER_SPACE_OR_EXIT(buf+4, num_data, 4);
+		ret -= 4;
+		driver->data_ready[index] ^= USER_SPACE_LOG_TYPE;
+		if (driver->ch)
+			queue_work(driver->diag_wq,
+					 &(driver->diag_read_smd_work));
+		if (driver->chqdsp)
+			queue_work(driver->diag_wq,
+					 &(driver->diag_read_smd_qdsp_work));
+		if (driver->ch_wcnss)
+			queue_work(driver->diag_wq,
+					 &(driver->diag_read_smd_wcnss_work));
+#ifdef CONFIG_DIAG_SDIO_PIPE
+		if (driver->sdio_ch)
+			queue_work(driver->diag_sdio_wq,
+					   &(driver->diag_read_sdio_work));
+#endif
+		APPEND_DEBUG('n');
+		goto exit;
+	} else if (driver->data_ready[index] & USER_SPACE_LOG_TYPE) {
+		/* In case, the thread wakes up and the logging mode is
+		not memory device any more, the condition needs to be cleared */
+		driver->data_ready[index] ^= USER_SPACE_LOG_TYPE;
+	}
+
+	if (driver->data_ready[index] & DEINIT_TYPE) {
+		/*Copy the type of data being passed*/
+		data_type = driver->data_ready[index] & DEINIT_TYPE;
+		COPY_USER_SPACE_OR_EXIT(buf, data_type, 4);
+		driver->data_ready[index] ^= DEINIT_TYPE;
+		goto exit;
+	}
+
+	if (driver->data_ready[index] & MSG_MASKS_TYPE) {
+		/*Copy the type of data being passed*/
+		data_type = driver->data_ready[index] & MSG_MASKS_TYPE;
+		COPY_USER_SPACE_OR_EXIT(buf, data_type, 4);
+		COPY_USER_SPACE_OR_EXIT(buf+4, *(driver->msg_masks),
+							 MSG_MASK_SIZE);
+		driver->data_ready[index] ^= MSG_MASKS_TYPE;
+		goto exit;
+	}
+
+	if (driver->data_ready[index] & EVENT_MASKS_TYPE) {
+		/*Copy the type of data being passed*/
+		data_type = driver->data_ready[index] & EVENT_MASKS_TYPE;
+		COPY_USER_SPACE_OR_EXIT(buf, data_type, 4);
+		COPY_USER_SPACE_OR_EXIT(buf+4, *(driver->event_masks),
+							 EVENT_MASK_SIZE);
+		driver->data_ready[index] ^= EVENT_MASKS_TYPE;
+		goto exit;
+	}
+
+	if (driver->data_ready[index] & LOG_MASKS_TYPE) {
+		/*Copy the type of data being passed*/
+		data_type = driver->data_ready[index] & LOG_MASKS_TYPE;
+		COPY_USER_SPACE_OR_EXIT(buf, data_type, 4);
+		COPY_USER_SPACE_OR_EXIT(buf+4, *(driver->log_masks),
+							 LOG_MASK_SIZE);
+		driver->data_ready[index] ^= LOG_MASKS_TYPE;
+		goto exit;
+	}
+
+	if (driver->data_ready[index] & PKT_TYPE) {
+		/*Copy the type of data being passed*/
+		data_type = driver->data_ready[index] & PKT_TYPE;
+		COPY_USER_SPACE_OR_EXIT(buf, data_type, 4);
+		COPY_USER_SPACE_OR_EXIT(buf+4, *(driver->pkt_buf),
+							 driver->pkt_length);
+		driver->data_ready[index] ^= PKT_TYPE;
+		goto exit;
+	}
+
+	if (driver->data_ready[index] & DCI_DATA_TYPE) {
+		/*Copy the type of data being passed*/
+		data_type = driver->data_ready[index] & DCI_DATA_TYPE;
+		COPY_USER_SPACE_OR_EXIT(buf, data_type, 4);
+		COPY_USER_SPACE_OR_EXIT(buf+4,
+			 driver->write_ptr_dci->length, 4);
+		/* check delayed vs immediate response */
+		if (*(uint8_t *)(driver->buf_in_dci+4) == DCI_CMD_CODE)
+			COPY_USER_SPACE_OR_EXIT(buf+8,
+		*(driver->buf_in_dci + 5), driver->write_ptr_dci->length);
+		else
+			COPY_USER_SPACE_OR_EXIT(buf+8,
+		*(driver->buf_in_dci + 8), driver->write_ptr_dci->length);
+		driver->in_busy_dci = 0;
+		driver->data_ready[index] ^= DCI_DATA_TYPE;
+		if (driver->ch_dci)
+			queue_work(driver->diag_wq,
+				 &(driver->diag_read_smd_dci_work));
+		goto exit;
+	}
+exit:
+	mutex_unlock(&driver->diagchar_mutex);
+	return ret;
+}
+
+static int diagchar_write(struct file *file, const char __user *buf,
+			      size_t count, loff_t *ppos)
+{
+	int err, ret = 0, pkt_type;
+#ifdef DIAG_DEBUG
+	int length = 0, i;
+#endif
+	struct diag_send_desc_type send = { NULL, NULL, DIAG_STATE_START, 0 };
+	struct diag_hdlc_dest_type enc = { NULL, NULL, 0 };
+	void *buf_copy = NULL;
+	int payload_size;
+#ifdef CONFIG_DIAG_OVER_USB
+	if (((driver->logging_mode == USB_MODE) && (!driver->usb_connected)) ||
+				(driver->logging_mode == NO_LOGGING_MODE)) {
+		/*Drop the diag payload */
+		return -EIO;
+	}
+#endif /* DIAG over USB */
+	/* Get the packet type F3/log/event/Pkt response */
+	err = copy_from_user((&pkt_type), buf, 4);
+	/* First 4 bytes indicate the type of payload - ignore these */
+	payload_size = count - 4;
+
+	if (pkt_type == DCI_DATA_TYPE) {
+		err = copy_from_user(driver->user_space_data, buf + 4,
+							 payload_size);
+		if (err) {
+			pr_alert("diag: copy failed for DCI data\n");
+			return DIAG_DCI_SEND_DATA_FAIL;
+		}
+		err = diag_process_dci_client(driver->user_space_data,
+							payload_size);
+		return err;
+	}
+	if (pkt_type == USER_SPACE_LOG_TYPE) {
+		err = copy_from_user(driver->user_space_data, buf + 4,
+							 payload_size);
+		/* Check masks for On-Device logging */
+		if (driver->mask_check) {
+			if (!mask_request_validate(driver->user_space_data)) {
+				pr_alert("diag: mask request Invalid\n");
+				return -EFAULT;
+			}
+		}
+		buf = buf + 4;
+#ifdef DIAG_DEBUG
+		pr_debug("diag: user space data %d\n", payload_size);
+		for (i = 0; i < payload_size; i++)
+			pr_debug("\t %x", *((driver->user_space_data)+i));
+#endif
+#ifdef CONFIG_DIAG_SDIO_PIPE
+		/* send masks to 9k too */
+		if (driver->sdio_ch) {
+			wait_event_interruptible(driver->wait_q,
+				 (sdio_write_avail(driver->sdio_ch) >=
+					 payload_size));
+			if (driver->sdio_ch && (payload_size > 0)) {
+				sdio_write(driver->sdio_ch, (void *)
+				   (driver->user_space_data), payload_size);
+			}
+		}
+#endif
+#ifdef CONFIG_DIAG_HSIC_PIPE
+		/* send masks to 9k too */
+		if (driver->hsic_ch && (payload_size > 0)) {
+			/* wait sending mask updates if HSIC ch not ready */
+			if (driver->in_busy_hsic_write)
+				wait_event_interruptible(driver->wait_q,
+					(driver->in_busy_hsic_write != 1));
+			driver->in_busy_hsic_write = 1;
+			driver->in_busy_hsic_read_on_device = 0;
+			err = diag_bridge_write(driver->user_space_data,
+							 payload_size);
+			if (err) {
+				pr_err("diag: err sending mask to MDM: %d\n",
+									 err);
+				/*
+				* If the error is recoverable, then clear
+				* the write flag, so we will resubmit a
+				* write on the next frame.  Otherwise, don't
+				* resubmit a write on the next frame.
+				*/
+				if ((-ESHUTDOWN) != err)
+					driver->in_busy_hsic_write = 0;
+			}
+		}
+#endif
+		/* send masks to 8k now */
+		diag_process_hdlc((void *)(driver->user_space_data),
+							 payload_size);
+		return 0;
+	}
+
+	if (payload_size > itemsize) {
+		pr_err("diag: Dropping packet, packet payload size crosses"
+				"4KB limit. Current payload size %d\n",
+				payload_size);
+		driver->dropped_count++;
+		return -EBADMSG;
+	}
+
+	buf_copy = diagmem_alloc(driver, payload_size, POOL_TYPE_COPY);
+	if (!buf_copy) {
+		driver->dropped_count++;
+		return -ENOMEM;
+	}
+
+	err = copy_from_user(buf_copy, buf + 4, payload_size);
+	if (err) {
+		printk(KERN_INFO "diagchar : copy_from_user failed\n");
+		ret = -EFAULT;
+		goto fail_free_copy;
+	}
+#ifdef DIAG_DEBUG
+	printk(KERN_DEBUG "data is -->\n");
+	for (i = 0; i < payload_size; i++)
+		printk(KERN_DEBUG "\t %x \t", *(((unsigned char *)buf_copy)+i));
+#endif
+	send.state = DIAG_STATE_START;
+	send.pkt = buf_copy;
+	send.last = (void *)(buf_copy + payload_size - 1);
+	send.terminate = 1;
+#ifdef DIAG_DEBUG
+	pr_debug("diag: Already used bytes in buffer %d, and"
+	" incoming payload size is %d\n", driver->used, payload_size);
+	printk(KERN_DEBUG "hdlc encoded data is -->\n");
+	for (i = 0; i < payload_size + 8; i++) {
+		printk(KERN_DEBUG "\t %x \t", *(((unsigned char *)buf_hdlc)+i));
+		if (*(((unsigned char *)buf_hdlc)+i) != 0x7e)
+			length++;
+	}
+#endif
+	mutex_lock(&driver->diagchar_mutex);
+	if (!buf_hdlc)
+		buf_hdlc = diagmem_alloc(driver, HDLC_OUT_BUF_SIZE,
+						 POOL_TYPE_HDLC);
+	if (!buf_hdlc) {
+		ret = -ENOMEM;
+		goto fail_free_hdlc;
+	}
+	if (HDLC_OUT_BUF_SIZE - driver->used <= (2*payload_size) + 3) {
+		err = diag_device_write(buf_hdlc, APPS_DATA, NULL);
+		if (err) {
+			/*Free the buffer right away if write failed */
+			diagmem_free(driver, buf_hdlc, POOL_TYPE_HDLC);
+			diagmem_free(driver, (unsigned char *)driver->
+				 write_ptr_svc, POOL_TYPE_WRITE_STRUCT);
+			ret = -EIO;
+			goto fail_free_hdlc;
+		}
+		buf_hdlc = NULL;
+		driver->used = 0;
+		buf_hdlc = diagmem_alloc(driver, HDLC_OUT_BUF_SIZE,
+							 POOL_TYPE_HDLC);
+		if (!buf_hdlc) {
+			ret = -ENOMEM;
+			goto fail_free_hdlc;
+		}
+	}
+
+	enc.dest = buf_hdlc + driver->used;
+	enc.dest_last = (void *)(buf_hdlc + driver->used + 2*payload_size + 3);
+	diag_hdlc_encode(&send, &enc);
+
+	/* This is to check if after HDLC encoding, we are still within the
+	 limits of aggregation buffer. If not, we write out the current buffer
+	and start aggregation in a newly allocated buffer */
+	if ((unsigned int) enc.dest >=
+		 (unsigned int)(buf_hdlc + HDLC_OUT_BUF_SIZE)) {
+		err = diag_device_write(buf_hdlc, APPS_DATA, NULL);
+		if (err) {
+			/*Free the buffer right away if write failed */
+			diagmem_free(driver, buf_hdlc, POOL_TYPE_HDLC);
+			diagmem_free(driver, (unsigned char *)driver->
+				 write_ptr_svc, POOL_TYPE_WRITE_STRUCT);
+			ret = -EIO;
+			goto fail_free_hdlc;
+		}
+		buf_hdlc = NULL;
+		driver->used = 0;
+		buf_hdlc = diagmem_alloc(driver, HDLC_OUT_BUF_SIZE,
+							 POOL_TYPE_HDLC);
+		if (!buf_hdlc) {
+			ret = -ENOMEM;
+			goto fail_free_hdlc;
+		}
+		enc.dest = buf_hdlc + driver->used;
+		enc.dest_last = (void *)(buf_hdlc + driver->used +
+							 (2*payload_size) + 3);
+		diag_hdlc_encode(&send, &enc);
+	}
+
+	driver->used = (uint32_t) enc.dest - (uint32_t) buf_hdlc;
+	if (pkt_type == DATA_TYPE_RESPONSE) {
+		err = diag_device_write(buf_hdlc, APPS_DATA, NULL);
+		if (err) {
+			/*Free the buffer right away if write failed */
+			diagmem_free(driver, buf_hdlc, POOL_TYPE_HDLC);
+			diagmem_free(driver, (unsigned char *)driver->
+				 write_ptr_svc, POOL_TYPE_WRITE_STRUCT);
+			ret = -EIO;
+			goto fail_free_hdlc;
+		}
+		buf_hdlc = NULL;
+		driver->used = 0;
+	}
+
+	mutex_unlock(&driver->diagchar_mutex);
+	diagmem_free(driver, buf_copy, POOL_TYPE_COPY);
+	if (!timer_in_progress)	{
+		timer_in_progress = 1;
+		ret = mod_timer(&drain_timer, jiffies + msecs_to_jiffies(500));
+	}
+	return 0;
+
+fail_free_hdlc:
+	buf_hdlc = NULL;
+	driver->used = 0;
+	diagmem_free(driver, buf_copy, POOL_TYPE_COPY);
+	mutex_unlock(&driver->diagchar_mutex);
+	return ret;
+
+fail_free_copy:
+	diagmem_free(driver, buf_copy, POOL_TYPE_COPY);
+	return ret;
+}
+
+int mask_request_validate(unsigned char mask_buf[])
+{
+	uint8_t packet_id;
+	uint8_t subsys_id;
+	uint16_t ss_cmd;
+
+	packet_id = mask_buf[0];
+
+	if (packet_id == 0x4B) {
+		subsys_id = mask_buf[1];
+		ss_cmd = *(uint16_t *)(mask_buf + 2);
+		/* Packets with SSID which are allowed */
+		switch (subsys_id) {
+		case 0x04: /* DIAG_SUBSYS_WCDMA */
+			if ((ss_cmd == 0) || (ss_cmd == 0xF))
+				return 1;
+			break;
+		case 0x08: /* DIAG_SUBSYS_GSM */
+			if ((ss_cmd == 0) || (ss_cmd == 0x1))
+				return 1;
+			break;
+		case 0x09: /* DIAG_SUBSYS_UMTS */
+		case 0x0F: /* DIAG_SUBSYS_CM */
+			if (ss_cmd == 0)
+				return 1;
+			break;
+		case 0x0C: /* DIAG_SUBSYS_OS */
+			if ((ss_cmd == 2) || (ss_cmd == 0x100))
+				return 1; /* MPU and APU */
+			break;
+		case 0x12: /* DIAG_SUBSYS_DIAG_SERV */
+			if ((ss_cmd == 0) || (ss_cmd == 0x6) || (ss_cmd == 0x7))
+				return 1;
+			break;
+		case 0x13: /* DIAG_SUBSYS_FS */
+			if ((ss_cmd == 0) || (ss_cmd == 0x1))
+				return 1;
+			break;
+		default:
+			return 0;
+			break;
+		}
+	} else {
+		switch (packet_id) {
+		case 0x00:    /* Version Number */
+		case 0x0C:    /* CDMA status packet */
+		case 0x1C:    /* Diag Version */
+		case 0x1D:    /* Time Stamp */
+		case 0x60:    /* Event Report Control */
+		case 0x63:    /* Status snapshot */
+		case 0x73:    /* Logging Configuration */
+		case 0x7C:    /* Extended build ID */
+		case 0x7D:    /* Extended Message configuration */
+		case 0x81:    /* Event get mask */
+		case 0x82:    /* Set the event mask */
+			return 1;
+			break;
+		default:
+			return 0;
+			break;
+		}
+	}
+	return 0;
+}
+
+static const struct file_operations diagcharfops = {
+	.owner = THIS_MODULE,
+	.read = diagchar_read,
+	.write = diagchar_write,
+	.unlocked_ioctl = diagchar_ioctl,
+	.open = diagchar_open,
+	.release = diagchar_close
+};
+
+static int diagchar_setup_cdev(dev_t devno)
+{
+
+	int err;
+
+	cdev_init(driver->cdev, &diagcharfops);
+
+	driver->cdev->owner = THIS_MODULE;
+	driver->cdev->ops = &diagcharfops;
+
+	err = cdev_add(driver->cdev, devno, 1);
+
+	if (err) {
+		printk(KERN_INFO "diagchar cdev registration failed !\n\n");
+		return -1;
+	}
+
+	driver->diagchar_class = class_create(THIS_MODULE, "diag");
+
+	if (IS_ERR(driver->diagchar_class)) {
+		printk(KERN_ERR "Error creating diagchar class.\n");
+		return -1;
+	}
+
+	device_create(driver->diagchar_class, NULL, devno,
+				  (void *)driver, "diag");
+
+	return 0;
+
+}
+
+static int diagchar_cleanup(void)
+{
+	if (driver) {
+		if (driver->cdev) {
+			/* TODO - Check if device exists before deleting */
+			device_destroy(driver->diagchar_class,
+				       MKDEV(driver->major,
+					     driver->minor_start));
+			cdev_del(driver->cdev);
+		}
+		if (!IS_ERR(driver->diagchar_class))
+			class_destroy(driver->diagchar_class);
+		kfree(driver);
+	}
+	return 0;
+}
+
+#ifdef CONFIG_DIAG_SDIO_PIPE
+void diag_sdio_fn(int type)
+{
+	if (machine_is_msm8x60_fusion() || machine_is_msm8x60_fusn_ffa()) {
+		if (type == INIT)
+			diagfwd_sdio_init();
+		else if (type == EXIT)
+			diagfwd_sdio_exit();
+	}
+}
+#else
+inline void diag_sdio_fn(int type) {}
+#endif
+
+static int __init diagchar_init(void)
+{
+	dev_t dev;
+	int error;
+
+	pr_debug("diagfwd initializing ..\n");
+	driver = kzalloc(sizeof(struct diagchar_dev) + 5, GFP_KERNEL);
+
+	if (driver) {
+		driver->used = 0;
+		timer_in_progress = 0;
+		driver->debug_flag = 1;
+		driver->dci_state = DIAG_DCI_NO_ERROR;
+		setup_timer(&drain_timer, drain_timer_func, 1234);
+		driver->itemsize = itemsize;
+		driver->poolsize = poolsize;
+		driver->itemsize_hdlc = itemsize_hdlc;
+		driver->poolsize_hdlc = poolsize_hdlc;
+		driver->itemsize_write_struct = itemsize_write_struct;
+		driver->poolsize_write_struct = poolsize_write_struct;
+		driver->num_clients = max_clients;
+		driver->logging_mode = USB_MODE;
+		driver->mask_check = 0;
+		mutex_init(&driver->diagchar_mutex);
+		init_waitqueue_head(&driver->wait_q);
+		INIT_WORK(&(driver->diag_drain_work), diag_drain_work_fn);
+		INIT_WORK(&(driver->diag_read_smd_work), diag_read_smd_work_fn);
+		INIT_WORK(&(driver->diag_read_smd_cntl_work),
+						 diag_read_smd_cntl_work_fn);
+		INIT_WORK(&(driver->diag_read_smd_qdsp_work),
+			   diag_read_smd_qdsp_work_fn);
+		INIT_WORK(&(driver->diag_read_smd_qdsp_cntl_work),
+			   diag_read_smd_qdsp_cntl_work_fn);
+		INIT_WORK(&(driver->diag_read_smd_wcnss_work),
+			diag_read_smd_wcnss_work_fn);
+		INIT_WORK(&(driver->diag_read_smd_wcnss_cntl_work),
+			diag_read_smd_wcnss_cntl_work_fn);
+		INIT_WORK(&(driver->diag_read_smd_dci_work),
+						 diag_read_smd_dci_work_fn);
+		diag_debugfs_init();
+		diagfwd_init();
+		diagfwd_cntl_init();
+		driver->dci_state = diag_dci_init();
+		diag_sdio_fn(INIT);
+#ifdef CONFIG_DIAG_HSIC_PIPE
+		diagfwd_hsic_init();
+#endif
+		pr_debug("diagchar initializing ..\n");
+		driver->num = 1;
+		driver->name = ((void *)driver) + sizeof(struct diagchar_dev);
+		strlcpy(driver->name, "diag", 4);
+
+		/* Get major number from kernel and initialize */
+		error = alloc_chrdev_region(&dev, driver->minor_start,
+					    driver->num, driver->name);
+		if (!error) {
+			driver->major = MAJOR(dev);
+			driver->minor_start = MINOR(dev);
+		} else {
+			printk(KERN_INFO "Major number not allocated\n");
+			goto fail;
+		}
+		driver->cdev = cdev_alloc();
+		error = diagchar_setup_cdev(dev);
+		if (error)
+			goto fail;
+	} else {
+		printk(KERN_INFO "kzalloc failed\n");
+		goto fail;
+	}
+
+	pr_info("diagchar initialized now");
+	return 0;
+
+fail:
+	diag_debugfs_cleanup();
+	diagchar_cleanup();
+	diagfwd_exit();
+	diagfwd_cntl_exit();
+	diag_sdio_fn(EXIT);
+#ifdef CONFIG_DIAG_HSIC_PIPE
+	diagfwd_hsic_exit();
+#endif
+	return -1;
+}
+
+static void diagchar_exit(void)
+{
+	printk(KERN_INFO "diagchar exiting ..\n");
+	/* On Driver exit, send special pool type to
+	 ensure no memory leaks */
+	diagmem_exit(driver, POOL_TYPE_ALL);
+	diagfwd_exit();
+	diagfwd_cntl_exit();
+	diag_sdio_fn(EXIT);
+#ifdef CONFIG_DIAG_HSIC_PIPE
+	diagfwd_hsic_exit();
+#endif
+	diag_debugfs_cleanup();
+	diagchar_cleanup();
+	printk(KERN_INFO "done diagchar exit\n");
+}
+
+module_init(diagchar_init);
+module_exit(diagchar_exit);
diff --git a/drivers/char/diag/diagchar_hdlc.c b/drivers/char/diag/diagchar_hdlc.c
new file mode 100644
index 0000000..74dcb6b
--- /dev/null
+++ b/drivers/char/diag/diagchar_hdlc.c
@@ -0,0 +1,224 @@
+/* Copyright (c) 2008-2009, 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/uaccess.h>
+#include <linux/crc-ccitt.h>
+#include "diagchar_hdlc.h"
+#include "diagchar.h"
+
+
+MODULE_LICENSE("GPL v2");
+
+#define CRC_16_L_SEED           0xFFFF
+
+#define CRC_16_L_STEP(xx_crc, xx_c) \
+	crc_ccitt_byte(xx_crc, xx_c)
+
+void diag_hdlc_encode(struct diag_send_desc_type *src_desc,
+		      struct diag_hdlc_dest_type *enc)
+{
+	uint8_t *dest;
+	uint8_t *dest_last;
+	const uint8_t *src;
+	const uint8_t *src_last;
+	uint16_t crc;
+	unsigned char src_byte = 0;
+	enum diag_send_state_enum_type state;
+	unsigned int used = 0;
+
+	if (src_desc && enc) {
+
+		/* Copy parts to local variables. */
+		src = src_desc->pkt;
+		src_last = src_desc->last;
+		state = src_desc->state;
+		dest = enc->dest;
+		dest_last = enc->dest_last;
+
+		if (state == DIAG_STATE_START) {
+			crc = CRC_16_L_SEED;
+			state++;
+		} else {
+			/* Get a local copy of the CRC */
+			crc = enc->crc;
+		}
+
+		/* dest or dest_last may be NULL to trigger a
+		   state transition only */
+		if (dest && dest_last) {
+			/* This condition needs to include the possibility
+			   of 2 dest bytes for an escaped byte */
+			while (src <= src_last && dest <= dest_last) {
+
+				src_byte = *src++;
+
+				if ((src_byte == CONTROL_CHAR) ||
+				    (src_byte == ESC_CHAR)) {
+
+					/* If the escape character is not the
+					   last byte */
+					if (dest != dest_last) {
+						crc = CRC_16_L_STEP(crc,
+								    src_byte);
+
+						*dest++ = ESC_CHAR;
+						used++;
+
+						*dest++ = src_byte
+							  ^ ESC_MASK;
+						used++;
+					} else {
+
+						src--;
+						break;
+					}
+
+				} else {
+					crc = CRC_16_L_STEP(crc, src_byte);
+					*dest++ = src_byte;
+					used++;
+				}
+			}
+
+			if (src > src_last) {
+
+				if (state == DIAG_STATE_BUSY) {
+					if (src_desc->terminate) {
+						crc = ~crc;
+						state++;
+					} else {
+						/* Done with fragment */
+						state = DIAG_STATE_COMPLETE;
+					}
+				}
+
+				while (dest <= dest_last &&
+				       state >= DIAG_STATE_CRC1 &&
+				       state < DIAG_STATE_TERM) {
+					/* Encode a byte of the CRC next */
+					src_byte = crc & 0xFF;
+
+					if ((src_byte == CONTROL_CHAR)
+					    || (src_byte == ESC_CHAR)) {
+
+						if (dest != dest_last) {
+
+							*dest++ = ESC_CHAR;
+							used++;
+							*dest++ = src_byte ^
+								  ESC_MASK;
+							used++;
+
+							crc >>= 8;
+						} else {
+
+							break;
+						}
+					} else {
+
+						crc >>= 8;
+						*dest++ = src_byte;
+						used++;
+					}
+
+					state++;
+				}
+
+				if (state == DIAG_STATE_TERM) {
+					if (dest_last >= dest) {
+						*dest++ = CONTROL_CHAR;
+						used++;
+						state++;	/* Complete */
+					}
+				}
+			}
+		}
+		/* Copy local variables back into the encode structure. */
+
+		enc->dest = dest;
+		enc->dest_last = dest_last;
+		enc->crc = crc;
+		src_desc->pkt = src;
+		src_desc->last = src_last;
+		src_desc->state = state;
+	}
+
+	return;
+}
+
+
+int diag_hdlc_decode(struct diag_hdlc_decode_type *hdlc)
+{
+	uint8_t *src_ptr = NULL, *dest_ptr = NULL;
+	unsigned int src_length = 0, dest_length = 0;
+
+	unsigned int len = 0;
+	unsigned int i;
+	uint8_t src_byte;
+
+	int pkt_bnd = 0;
+
+	if (hdlc && hdlc->src_ptr && hdlc->dest_ptr &&
+	    (hdlc->src_size - hdlc->src_idx > 0) &&
+	    (hdlc->dest_size - hdlc->dest_idx > 0)) {
+
+		src_ptr = hdlc->src_ptr;
+		src_ptr = &src_ptr[hdlc->src_idx];
+		src_length = hdlc->src_size - hdlc->src_idx;
+
+		dest_ptr = hdlc->dest_ptr;
+		dest_ptr = &dest_ptr[hdlc->dest_idx];
+		dest_length = hdlc->dest_size - hdlc->dest_idx;
+
+		for (i = 0; i < src_length; i++) {
+
+			src_byte = src_ptr[i];
+
+			if (hdlc->escaping) {
+				dest_ptr[len++] = src_byte ^ ESC_MASK;
+				hdlc->escaping = 0;
+			} else if (src_byte == ESC_CHAR) {
+				if (i == (src_length - 1)) {
+					hdlc->escaping = 1;
+					i++;
+					break;
+				} else {
+					dest_ptr[len++] = src_ptr[++i]
+							  ^ ESC_MASK;
+				}
+			} else if (src_byte == CONTROL_CHAR) {
+				dest_ptr[len++] = src_byte;
+				pkt_bnd = 1;
+				i++;
+				break;
+			} else {
+				dest_ptr[len++] = src_byte;
+			}
+
+			if (len >= dest_length) {
+				i++;
+				break;
+			}
+		}
+
+		hdlc->src_idx += i;
+		hdlc->dest_idx += len;
+	}
+
+	return pkt_bnd;
+}
diff --git a/drivers/char/diag/diagchar_hdlc.h b/drivers/char/diag/diagchar_hdlc.h
new file mode 100644
index 0000000..116c980
--- /dev/null
+++ b/drivers/char/diag/diagchar_hdlc.h
@@ -0,0 +1,59 @@
+/* Copyright (c) 2008-2009, 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef DIAGCHAR_HDLC
+#define DIAGCHAR_HDLC
+
+enum diag_send_state_enum_type {
+	DIAG_STATE_START,
+	DIAG_STATE_BUSY,
+	DIAG_STATE_CRC1,
+	DIAG_STATE_CRC2,
+	DIAG_STATE_TERM,
+	DIAG_STATE_COMPLETE
+};
+
+struct diag_send_desc_type {
+	const void *pkt;
+	const void *last;	/* Address of last byte to send. */
+	enum diag_send_state_enum_type state;
+	unsigned char terminate;	/* True if this fragment
+					   terminates the packet */
+};
+
+struct diag_hdlc_dest_type {
+	void *dest;
+	void *dest_last;
+	/* Below: internal use only */
+	uint16_t crc;
+};
+
+struct diag_hdlc_decode_type {
+	uint8_t *src_ptr;
+	unsigned int src_idx;
+	unsigned int src_size;
+	uint8_t *dest_ptr;
+	unsigned int dest_idx;
+	unsigned int dest_size;
+	int escaping;
+
+};
+
+void diag_hdlc_encode(struct diag_send_desc_type *src_desc,
+		      struct diag_hdlc_dest_type *enc);
+
+int diag_hdlc_decode(struct diag_hdlc_decode_type *hdlc);
+
+#define ESC_CHAR     0x7D
+#define ESC_MASK     0x20
+
+#endif
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
new file mode 100644
index 0000000..4ac2643
--- /dev/null
+++ b/drivers/char/diag/diagfwd.c
@@ -0,0 +1,2102 @@
+/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/workqueue.h>
+#include <linux/pm_runtime.h>
+#include <linux/diagchar.h>
+#include <linux/delay.h>
+#include <linux/reboot.h>
+#include <linux/of.h>
+#include <linux/kmemleak.h>
+#ifdef CONFIG_DIAG_OVER_USB
+#include <mach/usbdiag.h>
+#endif
+#include <mach/msm_smd.h>
+#include <mach/socinfo.h>
+#include <mach/restart.h>
+#include "diagmem.h"
+#include "diagchar.h"
+#include "diagfwd.h"
+#include "diagfwd_cntl.h"
+#include "diagchar_hdlc.h"
+#ifdef CONFIG_DIAG_SDIO_PIPE
+#include "diagfwd_sdio.h"
+#endif
+#include "diag_dci.h"
+
+#define MODE_CMD		41
+#define RESET_ID		2
+#define ALL_EQUIP_ID		100
+#define ALL_SSID		-1
+#define MAX_SSID_PER_RANGE	100
+
+int diag_debug_buf_idx;
+unsigned char diag_debug_buf[1024];
+static unsigned int buf_tbl_size = 8; /*Number of entries in table of buffers */
+struct diag_master_table entry;
+smd_channel_t *ch_temp, *chqdsp_temp, *ch_wcnss_temp;
+int diag_event_num_bytes;
+int diag_event_config;
+struct diag_send_desc_type send = { NULL, NULL, DIAG_STATE_START, 0 };
+struct diag_hdlc_dest_type enc = { NULL, NULL, 0 };
+struct mask_info {
+	int equip_id;
+	int num_items;
+	int index;
+};
+
+#define CREATE_MSG_MASK_TBL_ROW(XX)					\
+do {									\
+	*(int *)(msg_mask_tbl_ptr) = MSG_SSID_ ## XX;			\
+	msg_mask_tbl_ptr += 4;						\
+	*(int *)(msg_mask_tbl_ptr) = MSG_SSID_ ## XX ## _LAST;		\
+	msg_mask_tbl_ptr += 4;						\
+	/* increment by MAX_SSID_PER_RANGE cells */			\
+	msg_mask_tbl_ptr += MAX_SSID_PER_RANGE * sizeof(int);		\
+} while (0)
+
+#define ENCODE_RSP_AND_SEND(buf_length)				\
+do {									\
+	send.state = DIAG_STATE_START;					\
+	send.pkt = driver->apps_rsp_buf;				\
+	send.last = (void *)(driver->apps_rsp_buf + buf_length);	\
+	send.terminate = 1;						\
+	if (!driver->in_busy_1) {					\
+		enc.dest = driver->buf_in_1;				\
+		enc.dest_last = (void *)(driver->buf_in_1 + APPS_BUF_SIZE - 1);\
+		diag_hdlc_encode(&send, &enc);				\
+		driver->write_ptr_1->buf = driver->buf_in_1;		\
+		driver->write_ptr_1->length = (int)(enc.dest - \
+						(void *)(driver->buf_in_1)); \
+		driver->in_busy_1 = 1;					\
+		diag_device_write(driver->buf_in_1, MODEM_DATA, \
+						 driver->write_ptr_1); \
+		memset(driver->apps_rsp_buf, '\0', APPS_BUF_SIZE);	\
+	}								\
+} while (0)
+
+#define CHK_OVERFLOW(bufStart, start, end, length) \
+((bufStart <= start) && (end - start >= length)) ? 1 : 0
+
+/* Determine if this device uses a device tree */
+#ifdef CONFIG_OF
+static int has_device_tree(void)
+{
+	struct device_node *node;
+
+	node = of_find_node_by_path("/");
+	if (node) {
+		of_node_put(node);
+		return 1;
+	}
+	return 0;
+}
+#else
+static int has_device_tree(void)
+{
+	return 0;
+}
+#endif
+
+int chk_config_get_id(void)
+{
+	/* For all Fusion targets, Modem will always be present */
+	if (machine_is_msm8x60_fusion() || machine_is_msm8x60_fusn_ffa())
+		return 0;
+
+	if (driver->use_device_tree) {
+		if (machine_is_copper())
+			return MSM8974_TOOLS_ID;
+		else
+			return 0;
+	} else {
+		switch (socinfo_get_msm_cpu()) {
+		case MSM_CPU_8X60:
+			return APQ8060_TOOLS_ID;
+		case MSM_CPU_8960:
+			return AO8960_TOOLS_ID;
+		case MSM_CPU_8064:
+			return APQ8064_TOOLS_ID;
+		case MSM_CPU_8930:
+			return MSM8930_TOOLS_ID;
+		case MSM_CPU_COPPER:
+			return MSM8974_TOOLS_ID;
+		case MSM_CPU_8625:
+			return MSM8625_TOOLS_ID;
+		default:
+			return 0;
+		}
+	}
+}
+
+/*
+ * This will return TRUE for targets which support apps only mode and hence SSR.
+ * This applies to 8960 and newer targets.
+ */
+int chk_apps_only(void)
+{
+	if (driver->use_device_tree)
+		return 1;
+
+	switch (socinfo_get_msm_cpu()) {
+	case MSM_CPU_8960:
+	case MSM_CPU_8064:
+	case MSM_CPU_8930:
+	case MSM_CPU_8627:
+	case MSM_CPU_9615:
+	case MSM_CPU_COPPER:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+/*
+ * This will return TRUE for targets which support apps as master.
+ * Thus, SW DLOAD and Mode Reset are supported on apps processor.
+ * This applies to 8960 and newer targets.
+ */
+int chk_apps_master(void)
+{
+	if (driver->use_device_tree)
+		return 1;
+	else if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_msm9615() ||
+		cpu_is_apq8064() || cpu_is_msm8627())
+		return 1;
+	else
+		return 0;
+}
+
+int chk_polling_response(void)
+{
+	if (!(driver->polling_reg_flag) && chk_apps_master())
+		/*
+		 * If the apps processor is master and no other processor
+		 * has registered to respond for polling
+		 */
+		return 1;
+	else if (!(driver->ch) && !(chk_apps_master()))
+		/*
+		 * If the apps processor is not the master and the modem
+		 * is not up
+		 */
+		return 1;
+	else
+		return 0;
+}
+
+void __diag_smd_send_req(void)
+{
+	void *buf = NULL;
+	int *in_busy_ptr = NULL;
+	struct diag_request *write_ptr_modem = NULL;
+
+	if (!driver->in_busy_1) {
+		buf = driver->buf_in_1;
+		write_ptr_modem = driver->write_ptr_1;
+		in_busy_ptr = &(driver->in_busy_1);
+	} else if (!driver->in_busy_2) {
+		buf = driver->buf_in_2;
+		write_ptr_modem = driver->write_ptr_2;
+		in_busy_ptr = &(driver->in_busy_2);
+	}
+
+	if (driver->ch && buf) {
+		int r = smd_read_avail(driver->ch);
+
+		if (r > IN_BUF_SIZE) {
+			if (r < MAX_IN_BUF_SIZE) {
+				pr_err("diag: SMD sending in "
+						   "packets upto %d bytes", r);
+				buf = krealloc(buf, r, GFP_KERNEL);
+			} else {
+				pr_err("diag: SMD sending in "
+				"packets more than %d bytes", MAX_IN_BUF_SIZE);
+				return;
+			}
+		}
+		if (r > 0) {
+			if (!buf)
+				pr_info("Out of diagmem for Modem\n");
+			else {
+				APPEND_DEBUG('i');
+				smd_read(driver->ch, buf, r);
+				APPEND_DEBUG('j');
+				write_ptr_modem->length = r;
+				*in_busy_ptr = 1;
+				diag_device_write(buf, MODEM_DATA,
+							 write_ptr_modem);
+			}
+		}
+	}
+}
+
+int diag_device_write(void *buf, int proc_num, struct diag_request *write_ptr)
+{
+	int i, err = 0;
+
+	if (driver->logging_mode == MEMORY_DEVICE_MODE) {
+		if (proc_num == APPS_DATA) {
+			for (i = 0; i < driver->poolsize_write_struct; i++)
+				if (driver->buf_tbl[i].length == 0) {
+					driver->buf_tbl[i].buf = buf;
+					driver->buf_tbl[i].length =
+								 driver->used;
+#ifdef DIAG_DEBUG
+					pr_debug("diag: ENQUEUE buf ptr"
+						   " and length is %x , %d\n",
+						   (unsigned int)(driver->buf_
+				tbl[i].buf), driver->buf_tbl[i].length);
+#endif
+					break;
+				}
+		}
+		for (i = 0; i < driver->num_clients; i++)
+			if (driver->client_map[i].pid ==
+						 driver->logging_process_id)
+				break;
+		if (i < driver->num_clients) {
+			driver->data_ready[i] |= USER_SPACE_LOG_TYPE;
+			pr_debug("diag: wake up logging process\n");
+			wake_up_interruptible(&driver->wait_q);
+		} else
+			return -EINVAL;
+	} else if (driver->logging_mode == NO_LOGGING_MODE) {
+		if (proc_num == MODEM_DATA) {
+			driver->in_busy_1 = 0;
+			driver->in_busy_2 = 0;
+			queue_work(driver->diag_wq, &(driver->
+							diag_read_smd_work));
+		} else if (proc_num == QDSP_DATA) {
+			driver->in_busy_qdsp_1 = 0;
+			driver->in_busy_qdsp_2 = 0;
+			queue_work(driver->diag_wq, &(driver->
+						diag_read_smd_qdsp_work));
+		}  else if (proc_num == WCNSS_DATA) {
+			driver->in_busy_wcnss_1 = 0;
+			driver->in_busy_wcnss_2 = 0;
+			queue_work(driver->diag_wq, &(driver->
+				diag_read_smd_wcnss_work));
+		}
+#ifdef CONFIG_DIAG_SDIO_PIPE
+		else if (proc_num == SDIO_DATA) {
+			driver->in_busy_sdio = 0;
+			queue_work(driver->diag_sdio_wq,
+				&(driver->diag_read_sdio_work));
+		}
+#endif
+#ifdef CONFIG_DIAG_HSIC_PIPE
+		else if (proc_num == HSIC_DATA) {
+			driver->in_busy_hsic_read = 0;
+			driver->in_busy_hsic_write_on_device = 0;
+			if (driver->hsic_ch)
+				queue_work(driver->diag_hsic_wq,
+					&(driver->diag_read_hsic_work));
+		}
+#endif
+		err = -1;
+	}
+#ifdef CONFIG_DIAG_OVER_USB
+	else if (driver->logging_mode == USB_MODE) {
+		if (proc_num == APPS_DATA) {
+			driver->write_ptr_svc = (struct diag_request *)
+			(diagmem_alloc(driver, sizeof(struct diag_request),
+				 POOL_TYPE_WRITE_STRUCT));
+			if (driver->write_ptr_svc) {
+				driver->write_ptr_svc->length = driver->used;
+				driver->write_ptr_svc->buf = buf;
+				err = usb_diag_write(driver->legacy_ch,
+						driver->write_ptr_svc);
+			} else
+				err = -1;
+		} else if (proc_num == MODEM_DATA) {
+			write_ptr->buf = buf;
+#ifdef DIAG_DEBUG
+			printk(KERN_INFO "writing data to USB,"
+				"pkt length %d\n", write_ptr->length);
+			print_hex_dump(KERN_DEBUG, "Written Packet Data to"
+					   " USB: ", 16, 1, DUMP_PREFIX_ADDRESS,
+					    buf, write_ptr->length, 1);
+#endif /* DIAG DEBUG */
+			err = usb_diag_write(driver->legacy_ch, write_ptr);
+		} else if (proc_num == QDSP_DATA) {
+			write_ptr->buf = buf;
+			err = usb_diag_write(driver->legacy_ch, write_ptr);
+		} else if (proc_num == WCNSS_DATA) {
+			write_ptr->buf = buf;
+			err = usb_diag_write(driver->legacy_ch, write_ptr);
+		}
+#ifdef CONFIG_DIAG_SDIO_PIPE
+		else if (proc_num == SDIO_DATA) {
+			if (machine_is_msm8x60_fusion() ||
+					 machine_is_msm8x60_fusn_ffa()) {
+				write_ptr->buf = buf;
+				err = usb_diag_write(driver->mdm_ch, write_ptr);
+			} else
+				pr_err("diag: Incorrect sdio data "
+						"while USB write\n");
+		}
+#endif
+#ifdef CONFIG_DIAG_HSIC_PIPE
+		else if (proc_num == HSIC_DATA) {
+			if (driver->hsic_device_enabled) {
+				write_ptr->buf = buf;
+				err = usb_diag_write(driver->mdm_ch, write_ptr);
+			} else
+				pr_err("diag: Incorrect hsic data "
+						"while USB write\n");
+		}
+#endif
+		APPEND_DEBUG('d');
+	}
+#endif /* DIAG OVER USB */
+    return err;
+}
+
+void __diag_smd_wcnss_send_req(void)
+{
+	void *buf = NULL;
+	int *in_busy_wcnss_ptr = NULL;
+	struct diag_request *write_ptr_wcnss = NULL;
+
+	if (!driver->in_busy_wcnss_1) {
+		buf = driver->buf_in_wcnss_1;
+		write_ptr_wcnss = driver->write_ptr_wcnss_1;
+		in_busy_wcnss_ptr = &(driver->in_busy_wcnss_1);
+	} else if (!driver->in_busy_wcnss_2) {
+		buf = driver->buf_in_wcnss_2;
+		write_ptr_wcnss = driver->write_ptr_wcnss_2;
+		in_busy_wcnss_ptr = &(driver->in_busy_wcnss_2);
+	}
+
+	if (driver->ch_wcnss && buf) {
+		int r = smd_read_avail(driver->ch_wcnss);
+		if (r > IN_BUF_SIZE) {
+			if (r < MAX_IN_BUF_SIZE) {
+				pr_err("diag: wcnss packets > %d bytes", r);
+				buf = krealloc(buf, r, GFP_KERNEL);
+			} else {
+				pr_err("diag: wcnss pkt > %d", MAX_IN_BUF_SIZE);
+				return;
+			}
+		}
+		if (r > 0) {
+			if (!buf) {
+				pr_err("Out of diagmem for wcnss\n");
+			} else {
+				APPEND_DEBUG('i');
+				smd_read(driver->ch_wcnss, buf, r);
+				APPEND_DEBUG('j');
+				write_ptr_wcnss->length = r;
+				*in_busy_wcnss_ptr = 1;
+				diag_device_write(buf, WCNSS_DATA,
+					 write_ptr_wcnss);
+			}
+		}
+	}
+}
+
+void __diag_smd_qdsp_send_req(void)
+{
+	void *buf = NULL;
+	int *in_busy_qdsp_ptr = NULL;
+	struct diag_request *write_ptr_qdsp = NULL;
+
+	if (!driver->in_busy_qdsp_1) {
+		buf = driver->buf_in_qdsp_1;
+		write_ptr_qdsp = driver->write_ptr_qdsp_1;
+		in_busy_qdsp_ptr = &(driver->in_busy_qdsp_1);
+	} else if (!driver->in_busy_qdsp_2) {
+		buf = driver->buf_in_qdsp_2;
+		write_ptr_qdsp = driver->write_ptr_qdsp_2;
+		in_busy_qdsp_ptr = &(driver->in_busy_qdsp_2);
+	}
+
+	if (driver->chqdsp && buf) {
+		int r = smd_read_avail(driver->chqdsp);
+
+		if (r > IN_BUF_SIZE) {
+			if (r < MAX_IN_BUF_SIZE) {
+				pr_err("diag: SMD sending in "
+						   "packets upto %d bytes", r);
+				buf = krealloc(buf, r, GFP_KERNEL);
+			} else {
+				pr_err("diag: SMD sending in "
+				"packets more than %d bytes", MAX_IN_BUF_SIZE);
+				return;
+			}
+		}
+		if (r > 0) {
+			if (!buf)
+				printk(KERN_INFO "Out of diagmem for QDSP\n");
+			else {
+				APPEND_DEBUG('i');
+				smd_read(driver->chqdsp, buf, r);
+				APPEND_DEBUG('j');
+				write_ptr_qdsp->length = r;
+				*in_busy_qdsp_ptr = 1;
+				diag_device_write(buf, QDSP_DATA,
+							 write_ptr_qdsp);
+			}
+		}
+	}
+}
+
+static void diag_print_mask_table(void)
+{
+/* Enable this to print mask table when updated */
+#ifdef MASK_DEBUG
+	int first;
+	int last;
+	uint8_t *ptr = driver->msg_masks;
+	int i = 0;
+	pr_info("diag: F3 message mask table\n");
+	while (*(uint32_t *)(ptr + 4)) {
+		first = *(uint32_t *)ptr;
+		ptr += 4;
+		last = *(uint32_t *)ptr;
+		ptr += 4;
+		printk(KERN_INFO "SSID %d - %d\n", first, last);
+		for (i = 0 ; i <= last - first ; i++)
+			printk(KERN_INFO "MASK:%x\n", *((uint32_t *)ptr + i));
+		ptr += MAX_SSID_PER_RANGE*4;
+
+	}
+#endif
+}
+
+void diag_create_msg_mask_table(void)
+{
+	uint8_t *msg_mask_tbl_ptr = driver->msg_masks;
+
+	CREATE_MSG_MASK_TBL_ROW(0);
+	CREATE_MSG_MASK_TBL_ROW(1);
+	CREATE_MSG_MASK_TBL_ROW(2);
+	CREATE_MSG_MASK_TBL_ROW(3);
+	CREATE_MSG_MASK_TBL_ROW(4);
+	CREATE_MSG_MASK_TBL_ROW(5);
+	CREATE_MSG_MASK_TBL_ROW(6);
+	CREATE_MSG_MASK_TBL_ROW(7);
+	CREATE_MSG_MASK_TBL_ROW(8);
+	CREATE_MSG_MASK_TBL_ROW(9);
+	CREATE_MSG_MASK_TBL_ROW(10);
+	CREATE_MSG_MASK_TBL_ROW(11);
+	CREATE_MSG_MASK_TBL_ROW(12);
+	CREATE_MSG_MASK_TBL_ROW(13);
+	CREATE_MSG_MASK_TBL_ROW(14);
+	CREATE_MSG_MASK_TBL_ROW(15);
+	CREATE_MSG_MASK_TBL_ROW(16);
+	CREATE_MSG_MASK_TBL_ROW(17);
+	CREATE_MSG_MASK_TBL_ROW(18);
+	CREATE_MSG_MASK_TBL_ROW(19);
+	CREATE_MSG_MASK_TBL_ROW(20);
+	CREATE_MSG_MASK_TBL_ROW(21);
+	CREATE_MSG_MASK_TBL_ROW(22);
+}
+
+static void diag_set_msg_mask(int rt_mask)
+{
+	int first_ssid, last_ssid, i;
+	uint8_t *parse_ptr, *ptr = driver->msg_masks;
+
+	mutex_lock(&driver->diagchar_mutex);
+	while (*(uint32_t *)(ptr + 4)) {
+		first_ssid = *(uint32_t *)ptr;
+		ptr += 4;
+		last_ssid = *(uint32_t *)ptr;
+		ptr += 4;
+		parse_ptr = ptr;
+		pr_debug("diag: updating range %d %d\n", first_ssid, last_ssid);
+		for (i = 0; i < last_ssid - first_ssid + 1; i++) {
+			*(int *)parse_ptr = rt_mask;
+			parse_ptr += 4;
+		}
+		ptr += MAX_SSID_PER_RANGE * 4;
+	}
+	mutex_unlock(&driver->diagchar_mutex);
+}
+
+static void diag_update_msg_mask(int start, int end , uint8_t *buf)
+{
+	int found = 0;
+	int first;
+	int last;
+	uint8_t *ptr = driver->msg_masks;
+	uint8_t *ptr_buffer_start = &(*(driver->msg_masks));
+	uint8_t *ptr_buffer_end = &(*(driver->msg_masks)) + MSG_MASK_SIZE;
+
+	mutex_lock(&driver->diagchar_mutex);
+
+	/* First SSID can be zero : So check that last is non-zero */
+	while (*(uint32_t *)(ptr + 4)) {
+		first = *(uint32_t *)ptr;
+		ptr += 4;
+		last = *(uint32_t *)ptr;
+		ptr += 4;
+		if (start >= first && start <= last) {
+			ptr += (start - first)*4;
+			if (end <= last)
+				if (CHK_OVERFLOW(ptr_buffer_start, ptr,
+						  ptr_buffer_end,
+						  (((end - start)+1)*4))) {
+					pr_debug("diag: update ssid start %d,"
+						 " end %d\n", start, end);
+					memcpy(ptr, buf , ((end - start)+1)*4);
+				} else
+					printk(KERN_CRIT "Not enough"
+							 " buffer space for"
+							 " MSG_MASK\n");
+			else
+				printk(KERN_INFO "Unable to copy"
+						 " mask change\n");
+
+			found = 1;
+			break;
+		} else {
+			ptr += MAX_SSID_PER_RANGE*4;
+		}
+	}
+	/* Entry was not found - add new table */
+	if (!found) {
+		if (CHK_OVERFLOW(ptr_buffer_start, ptr, ptr_buffer_end,
+				  8 + ((end - start) + 1)*4)) {
+			memcpy(ptr, &(start) , 4);
+			ptr += 4;
+			memcpy(ptr, &(end), 4);
+			ptr += 4;
+			pr_debug("diag: adding NEW ssid start %d, end %d\n",
+								 start, end);
+			memcpy(ptr, buf , ((end - start) + 1)*4);
+		} else
+			printk(KERN_CRIT " Not enough buffer"
+					 " space for MSG_MASK\n");
+	}
+	mutex_unlock(&driver->diagchar_mutex);
+	diag_print_mask_table();
+
+}
+
+void diag_toggle_event_mask(int toggle)
+{
+	uint8_t *ptr = driver->event_masks;
+
+	mutex_lock(&driver->diagchar_mutex);
+	if (toggle)
+		memset(ptr, 0xFF, EVENT_MASK_SIZE);
+	else
+		memset(ptr, 0, EVENT_MASK_SIZE);
+	mutex_unlock(&driver->diagchar_mutex);
+}
+
+static void diag_update_event_mask(uint8_t *buf, int toggle, int num_bytes)
+{
+	uint8_t *ptr = driver->event_masks;
+	uint8_t *temp = buf + 2;
+
+	mutex_lock(&driver->diagchar_mutex);
+	if (!toggle)
+		memset(ptr, 0 , EVENT_MASK_SIZE);
+	else
+		if (CHK_OVERFLOW(ptr, ptr,
+				 ptr+EVENT_MASK_SIZE, num_bytes))
+			memcpy(ptr, temp , num_bytes);
+		else
+			printk(KERN_CRIT "Not enough buffer space "
+					 "for EVENT_MASK\n");
+	mutex_unlock(&driver->diagchar_mutex);
+}
+
+static void diag_disable_log_mask(void)
+{
+	int i = 0;
+	struct mask_info *parse_ptr = (struct mask_info *)(driver->log_masks);
+
+	pr_debug("diag: disable log masks\n");
+	mutex_lock(&driver->diagchar_mutex);
+	for (i = 0; i < MAX_EQUIP_ID; i++) {
+		pr_debug("diag: equip id %d\n", parse_ptr->equip_id);
+		if (!(parse_ptr->equip_id)) /* Reached a null entry */
+			break;
+		memset(driver->log_masks + parse_ptr->index, 0,
+			    (parse_ptr->num_items + 7)/8);
+		parse_ptr++;
+	}
+	mutex_unlock(&driver->diagchar_mutex);
+}
+
+static void diag_update_log_mask(int equip_id, uint8_t *buf, int num_items)
+{
+	uint8_t *temp = buf;
+	int i = 0;
+	unsigned char *ptr_data;
+	int offset = (sizeof(struct mask_info))*MAX_EQUIP_ID;
+	struct mask_info *ptr = (struct mask_info *)(driver->log_masks);
+
+	pr_debug("diag: received equip id = %d\n", equip_id);
+	mutex_lock(&driver->diagchar_mutex);
+	/* Check if we already know index of this equipment ID */
+	for (i = 0; i < MAX_EQUIP_ID; i++) {
+		if ((ptr->equip_id == equip_id) && (ptr->index != 0)) {
+			offset = ptr->index;
+			break;
+		}
+		if ((ptr->equip_id == 0) && (ptr->index == 0)) {
+			/* Reached a null entry */
+			ptr->equip_id = equip_id;
+			ptr->num_items = num_items;
+			ptr->index = driver->log_masks_length;
+			offset = driver->log_masks_length;
+			driver->log_masks_length += ((num_items+7)/8);
+			break;
+		}
+		ptr++;
+	}
+	ptr_data = driver->log_masks + offset;
+	if (CHK_OVERFLOW(driver->log_masks, ptr_data, driver->log_masks
+					 + LOG_MASK_SIZE, (num_items+7)/8))
+		memcpy(ptr_data, temp , (num_items+7)/8);
+	else
+		pr_err("diag: Not enough buffer space for LOG_MASK\n");
+	mutex_unlock(&driver->diagchar_mutex);
+}
+
+static void diag_update_pkt_buffer(unsigned char *buf)
+{
+	unsigned char *ptr = driver->pkt_buf;
+	unsigned char *temp = buf;
+
+	mutex_lock(&driver->diagchar_mutex);
+	if (CHK_OVERFLOW(ptr, ptr, ptr + PKT_SIZE, driver->pkt_length))
+		memcpy(ptr, temp , driver->pkt_length);
+	else
+		printk(KERN_CRIT " Not enough buffer space for PKT_RESP\n");
+	mutex_unlock(&driver->diagchar_mutex);
+}
+
+void diag_update_userspace_clients(unsigned int type)
+{
+	int i;
+
+	mutex_lock(&driver->diagchar_mutex);
+	for (i = 0; i < driver->num_clients; i++)
+		if (driver->client_map[i].pid != 0)
+			driver->data_ready[i] |= type;
+	wake_up_interruptible(&driver->wait_q);
+	mutex_unlock(&driver->diagchar_mutex);
+}
+
+void diag_update_sleeping_process(int process_id, int data_type)
+{
+	int i;
+
+	mutex_lock(&driver->diagchar_mutex);
+	for (i = 0; i < driver->num_clients; i++)
+		if (driver->client_map[i].pid == process_id) {
+			driver->data_ready[i] |= data_type;
+			break;
+		}
+	wake_up_interruptible(&driver->wait_q);
+	mutex_unlock(&driver->diagchar_mutex);
+}
+
+void diag_send_data(struct diag_master_table entry, unsigned char *buf,
+					 int len, int type)
+{
+	driver->pkt_length = len;
+	if (entry.process_id != NON_APPS_PROC && type != MODEM_DATA) {
+		diag_update_pkt_buffer(buf);
+		diag_update_sleeping_process(entry.process_id, PKT_TYPE);
+	} else {
+		if (len > 0) {
+			if (entry.client_id == MODEM_PROC && driver->ch) {
+				if (chk_apps_master() &&
+					 (int)(*(char *)buf) == MODE_CMD)
+					if ((int)(*(char *)(buf+1)) ==
+						RESET_ID)
+						return;
+				smd_write(driver->ch, buf, len);
+			} else if (entry.client_id == QDSP_PROC &&
+							 driver->chqdsp) {
+				smd_write(driver->chqdsp, buf, len);
+			} else if (entry.client_id == WCNSS_PROC &&
+							 driver->ch_wcnss) {
+				smd_write(driver->ch_wcnss, buf, len);
+			} else {
+				pr_alert("diag: incorrect channel");
+			}
+		}
+	}
+}
+
+void diag_modem_mask_update_fn(struct work_struct *work)
+{
+	diag_send_msg_mask_update(driver->ch_cntl, ALL_SSID,
+					   ALL_SSID, MODEM_PROC);
+	diag_send_log_mask_update(driver->ch_cntl, ALL_EQUIP_ID);
+	diag_send_event_mask_update(driver->ch_cntl, diag_event_num_bytes);
+}
+
+void diag_qdsp_mask_update_fn(struct work_struct *work)
+{
+	diag_send_msg_mask_update(driver->chqdsp_cntl, ALL_SSID,
+						   ALL_SSID, QDSP_PROC);
+	diag_send_log_mask_update(driver->chqdsp_cntl, ALL_EQUIP_ID);
+	diag_send_event_mask_update(driver->chqdsp_cntl, diag_event_num_bytes);
+}
+
+void diag_wcnss_mask_update_fn(struct work_struct *work)
+{
+	diag_send_msg_mask_update(driver->ch_wcnss_cntl, ALL_SSID,
+						   ALL_SSID, WCNSS_PROC);
+	diag_send_log_mask_update(driver->ch_wcnss_cntl, ALL_EQUIP_ID);
+	diag_send_event_mask_update(driver->ch_wcnss_cntl,
+						 diag_event_num_bytes);
+}
+
+void diag_send_log_mask_update(smd_channel_t *ch, int equip_id)
+{
+	void *buf = driver->buf_log_mask_update;
+	int header_size = sizeof(struct diag_ctrl_log_mask);
+	struct mask_info *ptr = (struct mask_info *)driver->log_masks;
+	int i, size, wr_size = -ENOMEM, retry_count = 0, timer;
+
+	mutex_lock(&driver->diag_cntl_mutex);
+	for (i = 0; i < MAX_EQUIP_ID; i++) {
+		size = (ptr->num_items+7)/8;
+		/* reached null entry */
+		if ((ptr->equip_id == 0) && (ptr->index == 0))
+			break;
+		driver->log_mask->cmd_type = DIAG_CTRL_MSG_LOG_MASK;
+		driver->log_mask->num_items = ptr->num_items;
+		driver->log_mask->data_len  = 11 + size;
+		driver->log_mask->stream_id = 1; /* 2, if dual stream */
+		driver->log_mask->status = 3; /* status for valid mask */
+		driver->log_mask->equip_id = ptr->equip_id;
+		driver->log_mask->log_mask_size = size;
+		/* send only desired update, NOT ALL */
+		if (equip_id == ALL_EQUIP_ID || equip_id ==
+					 driver->log_mask->equip_id) {
+			memcpy(buf, driver->log_mask, header_size);
+			memcpy(buf+header_size, driver->log_masks+ptr->index,
+									 size);
+			if (ch) {
+				while (retry_count < 3) {
+					wr_size = smd_write(ch, buf,
+							 header_size + size);
+					if (wr_size == -ENOMEM) {
+						retry_count++;
+						for (timer = 0; timer < 5;
+								 timer++)
+							udelay(2000);
+					} else
+						break;
+				}
+				if (wr_size != header_size + size)
+					pr_err("diag: log mask update failed"
+				 " %d, tried %d", wr_size, header_size + size);
+				else
+					pr_debug("diag: updated log equip ID %d"
+					",len %d\n", driver->log_mask->equip_id,
+					 driver->log_mask->log_mask_size);
+			} else
+				pr_err("diag: ch not valid for log update\n");
+		}
+		ptr++;
+	}
+	mutex_unlock(&driver->diag_cntl_mutex);
+}
+
+void diag_send_event_mask_update(smd_channel_t *ch, int num_bytes)
+{
+	void *buf = driver->buf_event_mask_update;
+	int header_size = sizeof(struct diag_ctrl_event_mask);
+	int wr_size = -ENOMEM, retry_count = 0, timer;
+
+	mutex_lock(&driver->diag_cntl_mutex);
+	if (num_bytes == 0) {
+		pr_debug("diag: event mask not set yet, so no update\n");
+		mutex_unlock(&driver->diag_cntl_mutex);
+		return;
+	}
+	/* send event mask update */
+	driver->event_mask->cmd_type = DIAG_CTRL_MSG_EVENT_MASK;
+	driver->event_mask->data_len = 7 + num_bytes;
+	driver->event_mask->stream_id = 1; /* 2, if dual stream */
+	driver->event_mask->status = 3; /* status for valid mask */
+	driver->event_mask->event_config = diag_event_config; /* event config */
+	driver->event_mask->event_mask_size = num_bytes;
+	memcpy(buf, driver->event_mask, header_size);
+	memcpy(buf+header_size, driver->event_masks, num_bytes);
+	if (ch) {
+		while (retry_count < 3) {
+			wr_size = smd_write(ch, buf, header_size + num_bytes);
+			if (wr_size == -ENOMEM) {
+				retry_count++;
+				for (timer = 0; timer < 5; timer++)
+					udelay(2000);
+			} else
+				break;
+		}
+		if (wr_size != header_size + num_bytes)
+			pr_err("diag: error writing event mask %d, tried %d\n",
+					 wr_size, header_size + num_bytes);
+	} else
+		pr_err("diag: ch not valid for event update\n");
+	mutex_unlock(&driver->diag_cntl_mutex);
+}
+
+void diag_send_msg_mask_update(smd_channel_t *ch, int updated_ssid_first,
+						int updated_ssid_last, int proc)
+{
+	void *buf = driver->buf_msg_mask_update;
+	int first, last, size = -ENOMEM, retry_count = 0, timer;
+	int header_size = sizeof(struct diag_ctrl_msg_mask);
+	uint8_t *ptr = driver->msg_masks;
+
+	mutex_lock(&driver->diag_cntl_mutex);
+	while (*(uint32_t *)(ptr + 4)) {
+		first = *(uint32_t *)ptr;
+		ptr += 4;
+		last = *(uint32_t *)ptr;
+		ptr += 4;
+		if ((updated_ssid_first >= first && updated_ssid_last <= last)
+					 || (updated_ssid_first == ALL_SSID)) {
+			/* send f3 mask update */
+			driver->msg_mask->cmd_type = DIAG_CTRL_MSG_F3_MASK;
+			driver->msg_mask->msg_mask_size = last - first + 1;
+			driver->msg_mask->data_len = 11 +
+					 4 * (driver->msg_mask->msg_mask_size);
+			driver->msg_mask->stream_id = 1; /* 2, if dual stream */
+			driver->msg_mask->status = 3; /* status valid mask */
+			driver->msg_mask->msg_mode = 0; /* Legcay mode */
+			driver->msg_mask->ssid_first = first;
+			driver->msg_mask->ssid_last = last;
+			memcpy(buf, driver->msg_mask, header_size);
+			memcpy(buf+header_size, ptr,
+				 4 * (driver->msg_mask->msg_mask_size));
+			if (ch) {
+				while (retry_count < 3) {
+					size = smd_write(ch, buf, header_size +
+					 4*(driver->msg_mask->msg_mask_size));
+					if (size == -ENOMEM) {
+						retry_count++;
+						for (timer = 0; timer < 5;
+								 timer++)
+							udelay(2000);
+					} else
+						break;
+				}
+				if (size != header_size +
+					 4*(driver->msg_mask->msg_mask_size))
+					pr_err("diag: proc %d, msg mask update "
+	 "fail %d, tried %d\n", proc, size,
+	 header_size + 4*(driver->msg_mask->msg_mask_size));
+				else
+					pr_debug("diag: sending mask update for"
+		"ssid first %d, last %d on PROC %d\n", first, last, proc);
+			} else
+				pr_err("diag: proc %d, ch invalid msg mask"
+						 "update\n", proc);
+		}
+		ptr += MAX_SSID_PER_RANGE*4;
+	}
+	mutex_unlock(&driver->diag_cntl_mutex);
+}
+
+static int diag_process_apps_pkt(unsigned char *buf, int len)
+{
+	uint16_t subsys_cmd_code;
+	int subsys_id, ssid_first, ssid_last, ssid_range;
+	int packet_type = 1, i, cmd_code, rt_mask;
+	unsigned char *temp = buf;
+	int data_type;
+#if defined(CONFIG_DIAG_OVER_USB)
+	int payload_length;
+	unsigned char *ptr;
+#endif
+
+	/* Set log masks */
+	if (*buf == 0x73 && *(int *)(buf+4) == 3) {
+		buf += 8;
+		/* Read Equip ID and pass as first param below*/
+		diag_update_log_mask(*(int *)buf, buf+8, *(int *)(buf+4));
+		diag_update_userspace_clients(LOG_MASKS_TYPE);
+#if defined(CONFIG_DIAG_OVER_USB)
+		if (chk_apps_only()) {
+			driver->apps_rsp_buf[0] = 0x73;
+			*(int *)(driver->apps_rsp_buf + 4) = 0x3; /* op. ID */
+			*(int *)(driver->apps_rsp_buf + 8) = 0x0; /* success */
+			payload_length = 8 + ((*(int *)(buf + 4)) + 7)/8;
+			for (i = 0; i < payload_length; i++)
+				*(int *)(driver->apps_rsp_buf+12+i) = *(buf+i);
+			if (driver->ch_cntl)
+				diag_send_log_mask_update(driver->ch_cntl,
+								 *(int *)buf);
+			if (driver->chqdsp_cntl)
+				diag_send_log_mask_update(driver->chqdsp_cntl,
+								 *(int *)buf);
+			if (driver->ch_wcnss_cntl)
+				diag_send_log_mask_update(driver->ch_wcnss_cntl,
+								 *(int *)buf);
+			ENCODE_RSP_AND_SEND(12 + payload_length - 1);
+			return 0;
+		} else
+			buf = temp;
+#endif
+	} /* Disable log masks */
+	else if (*buf == 0x73 && *(int *)(buf+4) == 0) {
+		/* Disable mask for each log code */
+		diag_disable_log_mask();
+		diag_update_userspace_clients(LOG_MASKS_TYPE);
+#if defined(CONFIG_DIAG_OVER_USB)
+		if (chk_apps_only()) {
+			driver->apps_rsp_buf[0] = 0x73;
+			driver->apps_rsp_buf[1] = 0x0;
+			driver->apps_rsp_buf[2] = 0x0;
+			driver->apps_rsp_buf[3] = 0x0;
+			*(int *)(driver->apps_rsp_buf + 4) = 0x0;
+			if (driver->ch_cntl)
+				diag_send_log_mask_update(driver->ch_cntl,
+								 ALL_EQUIP_ID);
+			if (driver->chqdsp_cntl)
+				diag_send_log_mask_update(driver->chqdsp_cntl,
+								 ALL_EQUIP_ID);
+			if (driver->ch_wcnss_cntl)
+				diag_send_log_mask_update(driver->ch_wcnss_cntl,
+								 ALL_EQUIP_ID);
+			ENCODE_RSP_AND_SEND(7);
+			return 0;
+		}
+#endif
+	} /* Set runtime message mask  */
+	else if ((*buf == 0x7d) && (*(buf+1) == 0x4)) {
+		ssid_first = *(uint16_t *)(buf + 2);
+		ssid_last = *(uint16_t *)(buf + 4);
+		ssid_range = 4 * (ssid_last - ssid_first + 1);
+		pr_debug("diag: received mask update for ssid_first = %d,"
+				" ssid_last = %d", ssid_first, ssid_last);
+		diag_update_msg_mask(ssid_first, ssid_last , buf + 8);
+		diag_update_userspace_clients(MSG_MASKS_TYPE);
+#if defined(CONFIG_DIAG_OVER_USB)
+		if (chk_apps_only()) {
+			for (i = 0; i < 8 + ssid_range; i++)
+				*(driver->apps_rsp_buf + i) = *(buf+i);
+			*(driver->apps_rsp_buf + 6) = 0x1;
+			if (driver->ch_cntl)
+				diag_send_msg_mask_update(driver->ch_cntl,
+					 ssid_first, ssid_last, MODEM_PROC);
+			if (driver->chqdsp_cntl)
+				diag_send_msg_mask_update(driver->chqdsp_cntl,
+					 ssid_first, ssid_last, QDSP_PROC);
+			if (driver->ch_wcnss_cntl)
+				diag_send_msg_mask_update(driver->ch_wcnss_cntl,
+					 ssid_first, ssid_last, WCNSS_PROC);
+			ENCODE_RSP_AND_SEND(8 + ssid_range - 1);
+			return 0;
+		} else
+			buf = temp;
+#endif
+	} /* Set ALL runtime message mask  */
+	else if ((*buf == 0x7d) && (*(buf+1) == 0x5)) {
+		rt_mask = *(int *)(buf + 4);
+		diag_set_msg_mask(rt_mask);
+		diag_update_userspace_clients(MSG_MASKS_TYPE);
+#if defined(CONFIG_DIAG_OVER_USB)
+		if (chk_apps_only()) {
+			driver->apps_rsp_buf[0] = 0x7d; /* cmd_code */
+			driver->apps_rsp_buf[1] = 0x5; /* set subcommand */
+			driver->apps_rsp_buf[2] = 1; /* success */
+			driver->apps_rsp_buf[3] = 0; /* rsvd */
+			*(int *)(driver->apps_rsp_buf + 4) = rt_mask;
+			/* send msg mask update to peripheral */
+			if (driver->ch_cntl)
+				diag_send_msg_mask_update(driver->ch_cntl,
+					 ALL_SSID, ALL_SSID, MODEM_PROC);
+			if (driver->chqdsp_cntl)
+				diag_send_msg_mask_update(driver->chqdsp_cntl,
+					 ALL_SSID, ALL_SSID, QDSP_PROC);
+			if (driver->ch_wcnss_cntl)
+				diag_send_msg_mask_update(driver->ch_wcnss_cntl,
+					 ALL_SSID, ALL_SSID, WCNSS_PROC);
+			ENCODE_RSP_AND_SEND(7);
+			return 0;
+		} else
+			buf = temp;
+#endif
+	} else if (*buf == 0x82) {	/* event mask change */
+		buf += 4;
+		diag_event_num_bytes =  (*(uint16_t *)buf)/8+1;
+		diag_update_event_mask(buf, 1, (*(uint16_t *)buf)/8+1);
+		diag_update_userspace_clients(EVENT_MASKS_TYPE);
+#if defined(CONFIG_DIAG_OVER_USB)
+		if (chk_apps_only()) {
+			driver->apps_rsp_buf[0] = 0x82;
+			driver->apps_rsp_buf[1] = 0x0;
+			*(uint16_t *)(driver->apps_rsp_buf + 2) = 0x0;
+			*(uint16_t *)(driver->apps_rsp_buf + 4) =
+							EVENT_LAST_ID + 1;
+			memcpy(driver->apps_rsp_buf+6, driver->event_masks,
+							 EVENT_LAST_ID/8+1);
+			if (driver->ch_cntl)
+				diag_send_event_mask_update(driver->ch_cntl,
+							 diag_event_num_bytes);
+			if (driver->chqdsp_cntl)
+				diag_send_event_mask_update(driver->chqdsp_cntl,
+							 diag_event_num_bytes);
+			if (driver->ch_wcnss_cntl)
+				diag_send_event_mask_update(
+				driver->ch_wcnss_cntl, diag_event_num_bytes);
+			ENCODE_RSP_AND_SEND(6 + EVENT_LAST_ID/8);
+			return 0;
+		} else
+			buf = temp;
+#endif
+	} else if (*buf == 0x60) {
+		diag_event_config = *(buf+1);
+		diag_toggle_event_mask(*(buf+1));
+		diag_update_userspace_clients(EVENT_MASKS_TYPE);
+#if defined(CONFIG_DIAG_OVER_USB)
+		if (chk_apps_only()) {
+			driver->apps_rsp_buf[0] = 0x60;
+			driver->apps_rsp_buf[1] = 0x0;
+			driver->apps_rsp_buf[2] = 0x0;
+			if (driver->ch_cntl)
+				diag_send_event_mask_update(driver->ch_cntl,
+							 diag_event_num_bytes);
+			if (driver->chqdsp_cntl)
+				diag_send_event_mask_update(driver->chqdsp_cntl,
+							 diag_event_num_bytes);
+			if (driver->ch_wcnss_cntl)
+				diag_send_event_mask_update(
+				driver->ch_wcnss_cntl, diag_event_num_bytes);
+			ENCODE_RSP_AND_SEND(2);
+			return 0;
+		}
+#endif
+	}
+	/* Check for registered clients and forward packet to apropriate proc */
+	cmd_code = (int)(*(char *)buf);
+	temp++;
+	subsys_id = (int)(*(char *)temp);
+	temp++;
+	subsys_cmd_code = *(uint16_t *)temp;
+	temp += 2;
+	data_type = APPS_DATA;
+	/* Dont send any command other than mode reset */
+	if (chk_apps_master() && cmd_code == MODE_CMD) {
+		if (subsys_id != RESET_ID)
+			data_type = MODEM_DATA;
+	}
+
+	pr_debug("diag: %d %d %d", cmd_code, subsys_id, subsys_cmd_code);
+	for (i = 0; i < diag_max_reg; i++) {
+		entry = driver->table[i];
+		if (entry.process_id != NO_PROCESS) {
+			if (entry.cmd_code == cmd_code && entry.subsys_id ==
+				 subsys_id && entry.cmd_code_lo <=
+							 subsys_cmd_code &&
+				  entry.cmd_code_hi >= subsys_cmd_code) {
+				diag_send_data(entry, buf, len, data_type);
+				packet_type = 0;
+			} else if (entry.cmd_code == 255
+				  && cmd_code == 75) {
+				if (entry.subsys_id ==
+					subsys_id &&
+				   entry.cmd_code_lo <=
+					subsys_cmd_code &&
+					 entry.cmd_code_hi >=
+					subsys_cmd_code) {
+					diag_send_data(entry, buf, len,
+								 data_type);
+					packet_type = 0;
+				}
+			} else if (entry.cmd_code == 255 &&
+				  entry.subsys_id == 255) {
+				if (entry.cmd_code_lo <=
+						 cmd_code &&
+						 entry.
+						cmd_code_hi >= cmd_code) {
+					diag_send_data(entry, buf, len,
+								 data_type);
+					packet_type = 0;
+				}
+			}
+		}
+	}
+#if defined(CONFIG_DIAG_OVER_USB)
+	/* Check for the command/respond msg for the maximum packet length */
+	if ((*buf == 0x4b) && (*(buf+1) == 0x12) &&
+		(*(uint16_t *)(buf+2) == 0x0055)) {
+		for (i = 0; i < 4; i++)
+			*(driver->apps_rsp_buf+i) = *(buf+i);
+		*(uint32_t *)(driver->apps_rsp_buf+4) = PKT_SIZE;
+		ENCODE_RSP_AND_SEND(7);
+		return 0;
+	}
+	/* Check for Apps Only & get event mask request */
+	else if (!(driver->ch) && chk_apps_only() && *buf == 0x81) {
+		driver->apps_rsp_buf[0] = 0x81;
+		driver->apps_rsp_buf[1] = 0x0;
+		*(uint16_t *)(driver->apps_rsp_buf + 2) = 0x0;
+		*(uint16_t *)(driver->apps_rsp_buf + 4) = EVENT_LAST_ID + 1;
+		for (i = 0; i < EVENT_LAST_ID/8 + 1; i++)
+			*(unsigned char *)(driver->apps_rsp_buf + 6 + i) = 0x0;
+		ENCODE_RSP_AND_SEND(6 + EVENT_LAST_ID/8);
+		return 0;
+	}
+	/* Get log ID range & Check for Apps Only */
+	else if (!(driver->ch) && chk_apps_only()
+			  && (*buf == 0x73) && *(int *)(buf+4) == 1) {
+		driver->apps_rsp_buf[0] = 0x73;
+		*(int *)(driver->apps_rsp_buf + 4) = 0x1; /* operation ID */
+		*(int *)(driver->apps_rsp_buf + 8) = 0x0; /* success code */
+		*(int *)(driver->apps_rsp_buf + 12) = LOG_GET_ITEM_NUM(LOG_0);
+		*(int *)(driver->apps_rsp_buf + 16) = LOG_GET_ITEM_NUM(LOG_1);
+		*(int *)(driver->apps_rsp_buf + 20) = LOG_GET_ITEM_NUM(LOG_2);
+		*(int *)(driver->apps_rsp_buf + 24) = LOG_GET_ITEM_NUM(LOG_3);
+		*(int *)(driver->apps_rsp_buf + 28) = LOG_GET_ITEM_NUM(LOG_4);
+		*(int *)(driver->apps_rsp_buf + 32) = LOG_GET_ITEM_NUM(LOG_5);
+		*(int *)(driver->apps_rsp_buf + 36) = LOG_GET_ITEM_NUM(LOG_6);
+		*(int *)(driver->apps_rsp_buf + 40) = LOG_GET_ITEM_NUM(LOG_7);
+		*(int *)(driver->apps_rsp_buf + 44) = LOG_GET_ITEM_NUM(LOG_8);
+		*(int *)(driver->apps_rsp_buf + 48) = LOG_GET_ITEM_NUM(LOG_9);
+		*(int *)(driver->apps_rsp_buf + 52) = LOG_GET_ITEM_NUM(LOG_10);
+		*(int *)(driver->apps_rsp_buf + 56) = LOG_GET_ITEM_NUM(LOG_11);
+		*(int *)(driver->apps_rsp_buf + 60) = LOG_GET_ITEM_NUM(LOG_12);
+		*(int *)(driver->apps_rsp_buf + 64) = LOG_GET_ITEM_NUM(LOG_13);
+		*(int *)(driver->apps_rsp_buf + 68) = LOG_GET_ITEM_NUM(LOG_14);
+		*(int *)(driver->apps_rsp_buf + 72) = LOG_GET_ITEM_NUM(LOG_15);
+		ENCODE_RSP_AND_SEND(75);
+		return 0;
+	}
+	/* Respond to Get SSID Range request message */
+	else if (!(driver->ch) && chk_apps_only()
+			 && (*buf == 0x7d) && (*(buf+1) == 0x1)) {
+		driver->apps_rsp_buf[0] = 0x7d;
+		driver->apps_rsp_buf[1] = 0x1;
+		driver->apps_rsp_buf[2] = 0x1;
+		driver->apps_rsp_buf[3] = 0x0;
+		*(int *)(driver->apps_rsp_buf + 4) = MSG_MASK_TBL_CNT;
+		*(uint16_t *)(driver->apps_rsp_buf + 8) = MSG_SSID_0;
+		*(uint16_t *)(driver->apps_rsp_buf + 10) = MSG_SSID_0_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 12) = MSG_SSID_1;
+		*(uint16_t *)(driver->apps_rsp_buf + 14) = MSG_SSID_1_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 16) = MSG_SSID_2;
+		*(uint16_t *)(driver->apps_rsp_buf + 18) = MSG_SSID_2_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 20) = MSG_SSID_3;
+		*(uint16_t *)(driver->apps_rsp_buf + 22) = MSG_SSID_3_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 24) = MSG_SSID_4;
+		*(uint16_t *)(driver->apps_rsp_buf + 26) = MSG_SSID_4_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 28) = MSG_SSID_5;
+		*(uint16_t *)(driver->apps_rsp_buf + 30) = MSG_SSID_5_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 32) = MSG_SSID_6;
+		*(uint16_t *)(driver->apps_rsp_buf + 34) = MSG_SSID_6_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 36) = MSG_SSID_7;
+		*(uint16_t *)(driver->apps_rsp_buf + 38) = MSG_SSID_7_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 40) = MSG_SSID_8;
+		*(uint16_t *)(driver->apps_rsp_buf + 42) = MSG_SSID_8_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 44) = MSG_SSID_9;
+		*(uint16_t *)(driver->apps_rsp_buf + 46) = MSG_SSID_9_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 48) = MSG_SSID_10;
+		*(uint16_t *)(driver->apps_rsp_buf + 50) = MSG_SSID_10_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 52) = MSG_SSID_11;
+		*(uint16_t *)(driver->apps_rsp_buf + 54) = MSG_SSID_11_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 56) = MSG_SSID_12;
+		*(uint16_t *)(driver->apps_rsp_buf + 58) = MSG_SSID_12_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 60) = MSG_SSID_13;
+		*(uint16_t *)(driver->apps_rsp_buf + 62) = MSG_SSID_13_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 64) = MSG_SSID_14;
+		*(uint16_t *)(driver->apps_rsp_buf + 66) = MSG_SSID_14_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 68) = MSG_SSID_15;
+		*(uint16_t *)(driver->apps_rsp_buf + 70) = MSG_SSID_15_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 72) = MSG_SSID_16;
+		*(uint16_t *)(driver->apps_rsp_buf + 74) = MSG_SSID_16_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 76) = MSG_SSID_17;
+		*(uint16_t *)(driver->apps_rsp_buf + 78) = MSG_SSID_17_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 80) = MSG_SSID_18;
+		*(uint16_t *)(driver->apps_rsp_buf + 82) = MSG_SSID_18_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 84) = MSG_SSID_19;
+		*(uint16_t *)(driver->apps_rsp_buf + 86) = MSG_SSID_19_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 88) = MSG_SSID_20;
+		*(uint16_t *)(driver->apps_rsp_buf + 90) = MSG_SSID_20_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 92) = MSG_SSID_21;
+		*(uint16_t *)(driver->apps_rsp_buf + 94) = MSG_SSID_21_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 96) = MSG_SSID_22;
+		*(uint16_t *)(driver->apps_rsp_buf + 98) = MSG_SSID_22_LAST;
+		ENCODE_RSP_AND_SEND(99);
+		return 0;
+	}
+	/* Check for Apps Only Respond to Get Subsys Build mask */
+	else if (!(driver->ch) && chk_apps_only()
+			 && (*buf == 0x7d) && (*(buf+1) == 0x2)) {
+		ssid_first = *(uint16_t *)(buf + 2);
+		ssid_last = *(uint16_t *)(buf + 4);
+		ssid_range = 4 * (ssid_last - ssid_first + 1);
+		/* frame response */
+		driver->apps_rsp_buf[0] = 0x7d;
+		driver->apps_rsp_buf[1] = 0x2;
+		*(uint16_t *)(driver->apps_rsp_buf + 2) = ssid_first;
+		*(uint16_t *)(driver->apps_rsp_buf + 4) = ssid_last;
+		driver->apps_rsp_buf[6] = 0x1;
+		driver->apps_rsp_buf[7] = 0x0;
+		ptr = driver->apps_rsp_buf + 8;
+		/* bld time masks */
+		switch (ssid_first) {
+		case MSG_SSID_0:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_0[i/4];
+			break;
+		case MSG_SSID_1:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_1[i/4];
+			break;
+		case MSG_SSID_2:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_2[i/4];
+			break;
+		case MSG_SSID_3:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_3[i/4];
+			break;
+		case MSG_SSID_4:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_4[i/4];
+			break;
+		case MSG_SSID_5:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_5[i/4];
+			break;
+		case MSG_SSID_6:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_6[i/4];
+			break;
+		case MSG_SSID_7:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_7[i/4];
+			break;
+		case MSG_SSID_8:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_8[i/4];
+			break;
+		case MSG_SSID_9:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_9[i/4];
+			break;
+		case MSG_SSID_10:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_10[i/4];
+			break;
+		case MSG_SSID_11:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_11[i/4];
+			break;
+		case MSG_SSID_12:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_12[i/4];
+			break;
+		case MSG_SSID_13:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_13[i/4];
+			break;
+		case MSG_SSID_14:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_14[i/4];
+			break;
+		case MSG_SSID_15:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_15[i/4];
+			break;
+		case MSG_SSID_16:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_16[i/4];
+			break;
+		case MSG_SSID_17:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_17[i/4];
+			break;
+		case MSG_SSID_18:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_18[i/4];
+			break;
+		case MSG_SSID_19:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_19[i/4];
+			break;
+		case MSG_SSID_20:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_20[i/4];
+			break;
+		case MSG_SSID_21:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_21[i/4];
+			break;
+		case MSG_SSID_22:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_22[i/4];
+			break;
+		}
+		ENCODE_RSP_AND_SEND(8 + ssid_range - 1);
+		return 0;
+	}
+	/* Check for download command */
+	else if ((cpu_is_msm8x60() || chk_apps_master()) && (*buf == 0x3A)) {
+		/* send response back */
+		driver->apps_rsp_buf[0] = *buf;
+		ENCODE_RSP_AND_SEND(0);
+		msleep(5000);
+		/* call download API */
+		msm_set_restart_mode(RESTART_DLOAD);
+		printk(KERN_CRIT "diag: download mode set, Rebooting SoC..\n");
+		kernel_restart(NULL);
+		/* Not required, represents that command isnt sent to modem */
+		return 0;
+	}
+	/* Check for polling for Apps only DIAG */
+	else if ((*buf == 0x4b) && (*(buf+1) == 0x32) &&
+		(*(buf+2) == 0x03)) {
+		/* If no one has registered for polling */
+		if (chk_polling_response()) {
+			/* Respond to polling for Apps only DIAG */
+			for (i = 0; i < 3; i++)
+				driver->apps_rsp_buf[i] = *(buf+i);
+			for (i = 0; i < 13; i++)
+				driver->apps_rsp_buf[i+3] = 0;
+
+			ENCODE_RSP_AND_SEND(15);
+			return 0;
+		}
+	}
+	 /* Check for ID for NO MODEM present */
+	else if (chk_polling_response()) {
+		/* respond to 0x0 command */
+		if (*buf == 0x00) {
+			for (i = 0; i < 55; i++)
+				driver->apps_rsp_buf[i] = 0;
+
+			ENCODE_RSP_AND_SEND(54);
+			return 0;
+		}
+		/* respond to 0x7c command */
+		else if (*buf == 0x7c) {
+			driver->apps_rsp_buf[0] = 0x7c;
+			for (i = 1; i < 8; i++)
+				driver->apps_rsp_buf[i] = 0;
+			/* Tools ID for APQ 8060 */
+			*(int *)(driver->apps_rsp_buf + 8) =
+							 chk_config_get_id();
+			*(unsigned char *)(driver->apps_rsp_buf + 12) = '\0';
+			*(unsigned char *)(driver->apps_rsp_buf + 13) = '\0';
+			ENCODE_RSP_AND_SEND(13);
+			return 0;
+		}
+	}
+#endif
+		return packet_type;
+}
+
+#ifdef CONFIG_DIAG_OVER_USB
+void diag_send_error_rsp(int index)
+{
+	int i;
+
+	if (index > 490) {
+		pr_err("diag: error response too huge, aborting\n");
+		return;
+	}
+	driver->apps_rsp_buf[0] = 0x13; /* error code 13 */
+	for (i = 0; i < index; i++)
+		driver->apps_rsp_buf[i+1] = *(driver->hdlc_buf+i);
+	ENCODE_RSP_AND_SEND(index - 3);
+}
+#else
+static inline void diag_send_error_rsp(int index) {}
+#endif
+
+void diag_process_hdlc(void *data, unsigned len)
+{
+	struct diag_hdlc_decode_type hdlc;
+	int ret, type = 0;
+	pr_debug("diag: HDLC decode fn, len of data  %d\n", len);
+	hdlc.dest_ptr = driver->hdlc_buf;
+	hdlc.dest_size = USB_MAX_OUT_BUF;
+	hdlc.src_ptr = data;
+	hdlc.src_size = len;
+	hdlc.src_idx = 0;
+	hdlc.dest_idx = 0;
+	hdlc.escaping = 0;
+
+	ret = diag_hdlc_decode(&hdlc);
+
+	if (ret)
+		type = diag_process_apps_pkt(driver->hdlc_buf,
+							  hdlc.dest_idx - 3);
+	else if (driver->debug_flag) {
+		printk(KERN_ERR "Packet dropped due to bad HDLC coding/CRC"
+				" errors or partial packet received, packet"
+				" length = %d\n", len);
+		print_hex_dump(KERN_DEBUG, "Dropped Packet Data: ", 16, 1,
+					   DUMP_PREFIX_ADDRESS, data, len, 1);
+		driver->debug_flag = 0;
+	}
+	/* send error responses from APPS for Central Routing */
+	if (type == 1 && chk_apps_only()) {
+		diag_send_error_rsp(hdlc.dest_idx);
+		type = 0;
+	}
+	/* implies this packet is NOT meant for apps */
+	if (!(driver->ch) && type == 1) {
+		if (chk_apps_only()) {
+			diag_send_error_rsp(hdlc.dest_idx);
+		} else { /* APQ 8060, Let Q6 respond */
+			if (driver->chqdsp)
+				smd_write(driver->chqdsp, driver->hdlc_buf,
+						  hdlc.dest_idx - 3);
+		}
+		type = 0;
+	}
+
+#ifdef DIAG_DEBUG
+	pr_debug("diag: hdlc.dest_idx = %d", hdlc.dest_idx);
+	for (i = 0; i < hdlc.dest_idx; i++)
+		printk(KERN_DEBUG "\t%x", *(((unsigned char *)
+							driver->hdlc_buf)+i));
+#endif /* DIAG DEBUG */
+	/* ignore 2 bytes for CRC, one for 7E and send */
+	if ((driver->ch) && (ret) && (type) && (hdlc.dest_idx > 3)) {
+		APPEND_DEBUG('g');
+		smd_write(driver->ch, driver->hdlc_buf, hdlc.dest_idx - 3);
+		APPEND_DEBUG('h');
+#ifdef DIAG_DEBUG
+		printk(KERN_INFO "writing data to SMD, pkt length %d\n", len);
+		print_hex_dump(KERN_DEBUG, "Written Packet Data to SMD: ", 16,
+			       1, DUMP_PREFIX_ADDRESS, data, len, 1);
+#endif /* DIAG DEBUG */
+	}
+}
+
+#ifdef CONFIG_DIAG_OVER_USB
+/* 2+1 for modem ; 2 for LPASS ; 1 for WCNSS */
+#define N_LEGACY_WRITE	(driver->poolsize + 6)
+#define N_LEGACY_READ	1
+
+int diagfwd_connect(void)
+{
+	int err;
+
+	printk(KERN_DEBUG "diag: USB connected\n");
+	err = usb_diag_alloc_req(driver->legacy_ch, N_LEGACY_WRITE,
+			N_LEGACY_READ);
+	if (err)
+		printk(KERN_ERR "diag: unable to alloc USB req on legacy ch");
+
+	driver->usb_connected = 1;
+	driver->in_busy_1 = 0;
+	driver->in_busy_2 = 0;
+	driver->in_busy_qdsp_1 = 0;
+	driver->in_busy_qdsp_2 = 0;
+	driver->in_busy_wcnss_1 = 0;
+	driver->in_busy_wcnss_2 = 0;
+
+	/* Poll SMD channels to check for data*/
+	queue_work(driver->diag_wq, &(driver->diag_read_smd_work));
+	queue_work(driver->diag_wq, &(driver->diag_read_smd_qdsp_work));
+	queue_work(driver->diag_wq, &(driver->diag_read_smd_wcnss_work));
+	/* Poll SMD CNTL channels to check for data */
+	diag_smd_cntl_notify(NULL, SMD_EVENT_DATA);
+	diag_smd_qdsp_cntl_notify(NULL, SMD_EVENT_DATA);
+	diag_smd_wcnss_cntl_notify(NULL, SMD_EVENT_DATA);
+	/* Poll USB channel to check for data*/
+	queue_work(driver->diag_wq, &(driver->diag_read_work));
+#ifdef CONFIG_DIAG_SDIO_PIPE
+	if (machine_is_msm8x60_fusion() || machine_is_msm8x60_fusn_ffa()) {
+		if (driver->mdm_ch && !IS_ERR(driver->mdm_ch))
+			diagfwd_connect_sdio();
+		else
+			printk(KERN_INFO "diag: No USB MDM ch");
+	}
+#endif
+	return 0;
+}
+
+int diagfwd_disconnect(void)
+{
+	printk(KERN_DEBUG "diag: USB disconnected\n");
+	driver->usb_connected = 0;
+	driver->debug_flag = 1;
+	usb_diag_free_req(driver->legacy_ch);
+	if (driver->logging_mode == USB_MODE) {
+		driver->in_busy_1 = 1;
+		driver->in_busy_2 = 1;
+		driver->in_busy_qdsp_1 = 1;
+		driver->in_busy_qdsp_2 = 1;
+		driver->in_busy_wcnss_1 = 1;
+		driver->in_busy_wcnss_2 = 1;
+	}
+#ifdef CONFIG_DIAG_SDIO_PIPE
+	if (machine_is_msm8x60_fusion() || machine_is_msm8x60_fusn_ffa())
+		if (driver->mdm_ch && !IS_ERR(driver->mdm_ch))
+			diagfwd_disconnect_sdio();
+#endif
+	/* TBD - notify and flow control SMD */
+	return 0;
+}
+
+int diagfwd_write_complete(struct diag_request *diag_write_ptr)
+{
+	unsigned char *buf = diag_write_ptr->buf;
+	/*Determine if the write complete is for data from modem/apps/q6 */
+	/* Need a context variable here instead */
+	if (buf == (void *)driver->buf_in_1) {
+		driver->in_busy_1 = 0;
+		APPEND_DEBUG('o');
+		queue_work(driver->diag_wq, &(driver->diag_read_smd_work));
+	} else if (buf == (void *)driver->buf_in_2) {
+		driver->in_busy_2 = 0;
+		APPEND_DEBUG('O');
+		queue_work(driver->diag_wq, &(driver->diag_read_smd_work));
+	} else if (buf == (void *)driver->buf_in_qdsp_1) {
+		driver->in_busy_qdsp_1 = 0;
+		APPEND_DEBUG('p');
+		queue_work(driver->diag_wq, &(driver->diag_read_smd_qdsp_work));
+	} else if (buf == (void *)driver->buf_in_qdsp_2) {
+		driver->in_busy_qdsp_2 = 0;
+		APPEND_DEBUG('P');
+		queue_work(driver->diag_wq, &(driver->diag_read_smd_qdsp_work));
+	} else if (buf == driver->buf_in_wcnss_1) {
+		driver->in_busy_wcnss_1 = 0;
+		APPEND_DEBUG('r');
+		queue_work(driver->diag_wq,
+			 &(driver->diag_read_smd_wcnss_work));
+	} else if (buf == driver->buf_in_wcnss_2) {
+		driver->in_busy_wcnss_2 = 0;
+		APPEND_DEBUG('R');
+		queue_work(driver->diag_wq,
+			 &(driver->diag_read_smd_wcnss_work));
+	}
+#ifdef CONFIG_DIAG_SDIO_PIPE
+	else if (buf == (void *)driver->buf_in_sdio)
+		if (machine_is_msm8x60_fusion() ||
+			 machine_is_msm8x60_fusn_ffa())
+			diagfwd_write_complete_sdio();
+		else
+			pr_err("diag: Incorrect buffer pointer while WRITE");
+#endif
+	else {
+		diagmem_free(driver, (unsigned char *)buf, POOL_TYPE_HDLC);
+		diagmem_free(driver, (unsigned char *)diag_write_ptr,
+						 POOL_TYPE_WRITE_STRUCT);
+		APPEND_DEBUG('q');
+	}
+	return 0;
+}
+
+int diagfwd_read_complete(struct diag_request *diag_read_ptr)
+{
+	int status = diag_read_ptr->status;
+	unsigned char *buf = diag_read_ptr->buf;
+
+	/* Determine if the read complete is for data on legacy/mdm ch */
+	if (buf == (void *)driver->usb_buf_out) {
+		driver->read_len_legacy = diag_read_ptr->actual;
+		APPEND_DEBUG('s');
+#ifdef DIAG_DEBUG
+		printk(KERN_INFO "read data from USB, pkt length %d",
+		    diag_read_ptr->actual);
+		print_hex_dump(KERN_DEBUG, "Read Packet Data from USB: ", 16, 1,
+		       DUMP_PREFIX_ADDRESS, diag_read_ptr->buf,
+		       diag_read_ptr->actual, 1);
+#endif /* DIAG DEBUG */
+		if (driver->logging_mode == USB_MODE) {
+			if (status != -ECONNRESET && status != -ESHUTDOWN)
+				queue_work(driver->diag_wq,
+					&(driver->diag_proc_hdlc_work));
+			else
+				queue_work(driver->diag_wq,
+						 &(driver->diag_read_work));
+		}
+	}
+#ifdef CONFIG_DIAG_SDIO_PIPE
+	else if (buf == (void *)driver->usb_buf_mdm_out) {
+		if (machine_is_msm8x60_fusion() ||
+				 machine_is_msm8x60_fusn_ffa()) {
+			driver->read_len_mdm = diag_read_ptr->actual;
+			diagfwd_read_complete_sdio();
+		} else
+			pr_err("diag: Incorrect buffer pointer while READ");
+	}
+#endif
+	else
+		printk(KERN_ERR "diag: Unknown buffer ptr from USB");
+
+	return 0;
+}
+
+void diag_read_work_fn(struct work_struct *work)
+{
+	APPEND_DEBUG('d');
+	driver->usb_read_ptr->buf = driver->usb_buf_out;
+	driver->usb_read_ptr->length = USB_MAX_OUT_BUF;
+	usb_diag_read(driver->legacy_ch, driver->usb_read_ptr);
+	APPEND_DEBUG('e');
+}
+
+void diag_process_hdlc_fn(struct work_struct *work)
+{
+	APPEND_DEBUG('D');
+	diag_process_hdlc(driver->usb_buf_out, driver->read_len_legacy);
+	diag_read_work_fn(work);
+	APPEND_DEBUG('E');
+}
+
+void diag_usb_legacy_notifier(void *priv, unsigned event,
+			struct diag_request *d_req)
+{
+	switch (event) {
+	case USB_DIAG_CONNECT:
+		diagfwd_connect();
+		break;
+	case USB_DIAG_DISCONNECT:
+		diagfwd_disconnect();
+		break;
+	case USB_DIAG_READ_DONE:
+		diagfwd_read_complete(d_req);
+		break;
+	case USB_DIAG_WRITE_DONE:
+		diagfwd_write_complete(d_req);
+		break;
+	default:
+		printk(KERN_ERR "Unknown event from USB diag\n");
+		break;
+	}
+}
+
+#endif /* DIAG OVER USB */
+
+static void diag_smd_notify(void *ctxt, unsigned event)
+{
+	if (event == SMD_EVENT_CLOSE) {
+		pr_info("diag: clean modem registration\n");
+		diag_clear_reg(MODEM_PROC);
+		driver->ch = 0;
+		return;
+	} else if (event == SMD_EVENT_OPEN) {
+		driver->ch = ch_temp;
+	}
+	queue_work(driver->diag_wq, &(driver->diag_read_smd_work));
+}
+
+#if defined(CONFIG_MSM_N_WAY_SMD)
+static void diag_smd_qdsp_notify(void *ctxt, unsigned event)
+{
+	if (event == SMD_EVENT_CLOSE) {
+		pr_info("diag: clean lpass registration\n");
+		diag_clear_reg(QDSP_PROC);
+		driver->chqdsp = 0;
+		return;
+	} else if (event == SMD_EVENT_OPEN) {
+		driver->chqdsp = chqdsp_temp;
+	}
+	queue_work(driver->diag_wq, &(driver->diag_read_smd_qdsp_work));
+}
+#endif
+
+static void diag_smd_wcnss_notify(void *ctxt, unsigned event)
+{
+	if (event == SMD_EVENT_CLOSE) {
+		pr_info("diag: clean wcnss registration\n");
+		diag_clear_reg(WCNSS_PROC);
+		driver->ch_wcnss = 0;
+		return;
+	} else if (event == SMD_EVENT_OPEN) {
+		driver->ch_wcnss = ch_wcnss_temp;
+	}
+	queue_work(driver->diag_wq, &(driver->diag_read_smd_wcnss_work));
+}
+
+static int diag_smd_probe(struct platform_device *pdev)
+{
+	int r = 0;
+
+	if (pdev->id == SMD_APPS_MODEM) {
+		r = smd_open("DIAG", &driver->ch, driver, diag_smd_notify);
+		ch_temp = driver->ch;
+	}
+#if defined(CONFIG_MSM_N_WAY_SMD)
+	if (pdev->id == SMD_APPS_QDSP) {
+		r = smd_named_open_on_edge("DIAG", SMD_APPS_QDSP
+			, &driver->chqdsp, driver, diag_smd_qdsp_notify);
+		chqdsp_temp = driver->chqdsp;
+	}
+#endif
+	if (pdev->id == SMD_APPS_WCNSS) {
+		r = smd_named_open_on_edge("APPS_RIVA_DATA", SMD_APPS_WCNSS
+			, &driver->ch_wcnss, driver, diag_smd_wcnss_notify);
+		ch_wcnss_temp = driver->ch_wcnss;
+	}
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+	pr_debug("diag: open SMD port, Id = %d, r = %d\n", pdev->id, r);
+
+	return 0;
+}
+
+static int diagfwd_runtime_suspend(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: suspending...\n");
+	return 0;
+}
+
+static int diagfwd_runtime_resume(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: resuming...\n");
+	return 0;
+}
+
+static const struct dev_pm_ops diagfwd_dev_pm_ops = {
+	.runtime_suspend = diagfwd_runtime_suspend,
+	.runtime_resume = diagfwd_runtime_resume,
+};
+
+static struct platform_driver msm_smd_ch1_driver = {
+
+	.probe = diag_smd_probe,
+	.driver = {
+		   .name = "DIAG",
+		   .owner = THIS_MODULE,
+		   .pm   = &diagfwd_dev_pm_ops,
+		   },
+};
+
+static struct platform_driver diag_smd_lite_driver = {
+
+	.probe = diag_smd_probe,
+	.driver = {
+		   .name = "APPS_RIVA_DATA",
+		   .owner = THIS_MODULE,
+		   .pm   = &diagfwd_dev_pm_ops,
+		   },
+};
+
+void diagfwd_init(void)
+{
+	diag_debug_buf_idx = 0;
+	driver->read_len_legacy = 0;
+	driver->use_device_tree = has_device_tree();
+	mutex_init(&driver->diag_cntl_mutex);
+
+	if (driver->event_mask == NULL) {
+		driver->event_mask = kzalloc(sizeof(
+			struct diag_ctrl_event_mask), GFP_KERNEL);
+		if (driver->event_mask == NULL)
+			goto err;
+		kmemleak_not_leak(driver->event_mask);
+	}
+	if (driver->msg_mask == NULL) {
+		driver->msg_mask = kzalloc(sizeof(
+			struct diag_ctrl_msg_mask), GFP_KERNEL);
+		if (driver->msg_mask == NULL)
+			goto err;
+		kmemleak_not_leak(driver->msg_mask);
+	}
+	if (driver->log_mask == NULL) {
+		driver->log_mask = kzalloc(sizeof(
+			struct diag_ctrl_log_mask), GFP_KERNEL);
+		if (driver->log_mask == NULL)
+			goto err;
+		kmemleak_not_leak(driver->log_mask);
+	}
+	if (driver->buf_in_1 == NULL) {
+		driver->buf_in_1 = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
+		if (driver->buf_in_1 == NULL)
+			goto err;
+		kmemleak_not_leak(driver->buf_in_1);
+	}
+	if (driver->buf_in_2 == NULL) {
+		driver->buf_in_2 = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
+		if (driver->buf_in_2 == NULL)
+			goto err;
+		kmemleak_not_leak(driver->buf_in_2);
+	}
+	if (driver->buf_in_qdsp_1 == NULL) {
+		driver->buf_in_qdsp_1 = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
+		if (driver->buf_in_qdsp_1 == NULL)
+			goto err;
+		kmemleak_not_leak(driver->buf_in_qdsp_1);
+	}
+	if (driver->buf_in_qdsp_2 == NULL) {
+		driver->buf_in_qdsp_2 = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
+		if (driver->buf_in_qdsp_2 == NULL)
+			goto err;
+		kmemleak_not_leak(driver->buf_in_qdsp_2);
+	}
+	if (driver->buf_in_wcnss_1 == NULL) {
+		driver->buf_in_wcnss_1 = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
+		if (driver->buf_in_wcnss_1 == NULL)
+			goto err;
+		kmemleak_not_leak(driver->buf_in_wcnss_1);
+	}
+	if (driver->buf_in_wcnss_2 == NULL) {
+		driver->buf_in_wcnss_2 = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
+		if (driver->buf_in_wcnss_2 == NULL)
+			goto err;
+		kmemleak_not_leak(driver->buf_in_wcnss_2);
+	}
+
+	if (driver->buf_msg_mask_update == NULL) {
+		driver->buf_msg_mask_update = kzalloc(APPS_BUF_SIZE,
+								 GFP_KERNEL);
+		if (driver->buf_msg_mask_update == NULL)
+			goto err;
+		kmemleak_not_leak(driver->buf_msg_mask_update);
+	}
+	if (driver->buf_log_mask_update == NULL) {
+		driver->buf_log_mask_update = kzalloc(APPS_BUF_SIZE,
+								 GFP_KERNEL);
+		if (driver->buf_log_mask_update == NULL)
+			goto err;
+		kmemleak_not_leak(driver->buf_log_mask_update);
+	}
+	if (driver->buf_event_mask_update == NULL) {
+		driver->buf_event_mask_update = kzalloc(APPS_BUF_SIZE,
+								 GFP_KERNEL);
+		if (driver->buf_event_mask_update == NULL)
+			goto err;
+		kmemleak_not_leak(driver->buf_event_mask_update);
+	}
+	if (driver->usb_buf_out  == NULL &&
+	     (driver->usb_buf_out = kzalloc(USB_MAX_OUT_BUF,
+					 GFP_KERNEL)) == NULL)
+		goto err;
+	kmemleak_not_leak(driver->usb_buf_out);
+	if (driver->hdlc_buf == NULL
+	    && (driver->hdlc_buf = kzalloc(HDLC_MAX, GFP_KERNEL)) == NULL)
+		goto err;
+	kmemleak_not_leak(driver->hdlc_buf);
+	if (driver->user_space_data == NULL)
+		driver->user_space_data = kzalloc(USER_SPACE_DATA, GFP_KERNEL);
+		if (driver->user_space_data == NULL)
+			goto err;
+	kmemleak_not_leak(driver->user_space_data);
+	if (driver->msg_masks == NULL
+	    && (driver->msg_masks = kzalloc(MSG_MASK_SIZE,
+					     GFP_KERNEL)) == NULL)
+		goto err;
+	kmemleak_not_leak(driver->msg_masks);
+	diag_create_msg_mask_table();
+	diag_event_num_bytes = 0;
+	if (driver->log_masks == NULL &&
+	    (driver->log_masks = kzalloc(LOG_MASK_SIZE, GFP_KERNEL)) == NULL)
+		goto err;
+	kmemleak_not_leak(driver->log_masks);
+	driver->log_masks_length = (sizeof(struct mask_info))*MAX_EQUIP_ID;
+	if (driver->event_masks == NULL &&
+	    (driver->event_masks = kzalloc(EVENT_MASK_SIZE,
+					    GFP_KERNEL)) == NULL)
+		goto err;
+	kmemleak_not_leak(driver->event_masks);
+	if (driver->client_map == NULL &&
+	    (driver->client_map = kzalloc
+	     ((driver->num_clients) * sizeof(struct diag_client_map),
+		   GFP_KERNEL)) == NULL)
+		goto err;
+	kmemleak_not_leak(driver->client_map);
+	if (driver->buf_tbl == NULL)
+			driver->buf_tbl = kzalloc(buf_tbl_size *
+			  sizeof(struct diag_write_device), GFP_KERNEL);
+	if (driver->buf_tbl == NULL)
+		goto err;
+	kmemleak_not_leak(driver->buf_tbl);
+	if (driver->data_ready == NULL &&
+	     (driver->data_ready = kzalloc(driver->num_clients * sizeof(int)
+							, GFP_KERNEL)) == NULL)
+		goto err;
+	kmemleak_not_leak(driver->data_ready);
+	if (driver->table == NULL &&
+	     (driver->table = kzalloc(diag_max_reg*
+		      sizeof(struct diag_master_table),
+		       GFP_KERNEL)) == NULL)
+		goto err;
+	kmemleak_not_leak(driver->table);
+	if (driver->write_ptr_1 == NULL) {
+		driver->write_ptr_1 = kzalloc(
+			sizeof(struct diag_request), GFP_KERNEL);
+		if (driver->write_ptr_1 == NULL)
+			goto err;
+		kmemleak_not_leak(driver->write_ptr_1);
+	}
+	if (driver->write_ptr_2 == NULL) {
+		driver->write_ptr_2 = kzalloc(
+			sizeof(struct diag_request), GFP_KERNEL);
+		if (driver->write_ptr_2 == NULL)
+			goto err;
+		kmemleak_not_leak(driver->write_ptr_2);
+	}
+	if (driver->write_ptr_qdsp_1 == NULL) {
+		driver->write_ptr_qdsp_1 = kzalloc(
+			sizeof(struct diag_request), GFP_KERNEL);
+		if (driver->write_ptr_qdsp_1 == NULL)
+			goto err;
+		kmemleak_not_leak(driver->write_ptr_qdsp_1);
+	}
+	if (driver->write_ptr_qdsp_2 == NULL) {
+		driver->write_ptr_qdsp_2 = kzalloc(
+			sizeof(struct diag_request), GFP_KERNEL);
+		if (driver->write_ptr_qdsp_2 == NULL)
+			goto err;
+		kmemleak_not_leak(driver->write_ptr_qdsp_2);
+	}
+	if (driver->write_ptr_wcnss_1 == NULL) {
+		driver->write_ptr_wcnss_1 = kzalloc(
+			sizeof(struct diag_request), GFP_KERNEL);
+		if (driver->write_ptr_wcnss_1 == NULL)
+			goto err;
+		kmemleak_not_leak(driver->write_ptr_wcnss_1);
+	}
+	if (driver->write_ptr_wcnss_2 == NULL) {
+		driver->write_ptr_wcnss_2 = kzalloc(
+			sizeof(struct diag_request), GFP_KERNEL);
+		if (driver->write_ptr_wcnss_2 == NULL)
+			goto err;
+		kmemleak_not_leak(driver->write_ptr_wcnss_2);
+	}
+
+	if (driver->usb_read_ptr == NULL) {
+		driver->usb_read_ptr = kzalloc(
+			sizeof(struct diag_request), GFP_KERNEL);
+		if (driver->usb_read_ptr == NULL)
+			goto err;
+		kmemleak_not_leak(driver->usb_read_ptr);
+	}
+	if (driver->pkt_buf == NULL &&
+	     (driver->pkt_buf = kzalloc(PKT_SIZE,
+			 GFP_KERNEL)) == NULL)
+		goto err;
+	kmemleak_not_leak(driver->pkt_buf);
+	if (driver->apps_rsp_buf == NULL) {
+		driver->apps_rsp_buf = kzalloc(APPS_BUF_SIZE, GFP_KERNEL);
+		if (driver->apps_rsp_buf == NULL)
+			goto err;
+		kmemleak_not_leak(driver->apps_rsp_buf);
+	}
+	driver->diag_wq = create_singlethread_workqueue("diag_wq");
+#ifdef CONFIG_DIAG_OVER_USB
+	INIT_WORK(&(driver->diag_proc_hdlc_work), diag_process_hdlc_fn);
+	INIT_WORK(&(driver->diag_read_work), diag_read_work_fn);
+	INIT_WORK(&(driver->diag_modem_mask_update_work),
+						 diag_modem_mask_update_fn);
+	INIT_WORK(&(driver->diag_qdsp_mask_update_work),
+						 diag_qdsp_mask_update_fn);
+	INIT_WORK(&(driver->diag_wcnss_mask_update_work),
+						 diag_wcnss_mask_update_fn);
+	driver->legacy_ch = usb_diag_open(DIAG_LEGACY, driver,
+			diag_usb_legacy_notifier);
+	if (IS_ERR(driver->legacy_ch)) {
+		printk(KERN_ERR "Unable to open USB diag legacy channel\n");
+		goto err;
+	}
+#endif
+	platform_driver_register(&msm_smd_ch1_driver);
+	platform_driver_register(&diag_smd_lite_driver);
+
+	return;
+err:
+		pr_err("diag: Could not initialize diag buffers");
+		kfree(driver->event_mask);
+		kfree(driver->log_mask);
+		kfree(driver->msg_mask);
+		kfree(driver->buf_in_1);
+		kfree(driver->buf_in_2);
+		kfree(driver->buf_in_qdsp_1);
+		kfree(driver->buf_in_qdsp_2);
+		kfree(driver->buf_in_wcnss_1);
+		kfree(driver->buf_in_wcnss_2);
+		kfree(driver->buf_msg_mask_update);
+		kfree(driver->buf_log_mask_update);
+		kfree(driver->buf_event_mask_update);
+		kfree(driver->usb_buf_out);
+		kfree(driver->hdlc_buf);
+		kfree(driver->msg_masks);
+		kfree(driver->log_masks);
+		kfree(driver->event_masks);
+		kfree(driver->client_map);
+		kfree(driver->buf_tbl);
+		kfree(driver->data_ready);
+		kfree(driver->table);
+		kfree(driver->pkt_buf);
+		kfree(driver->write_ptr_1);
+		kfree(driver->write_ptr_2);
+		kfree(driver->write_ptr_qdsp_1);
+		kfree(driver->write_ptr_qdsp_2);
+		kfree(driver->write_ptr_wcnss_1);
+		kfree(driver->write_ptr_wcnss_2);
+		kfree(driver->usb_read_ptr);
+		kfree(driver->apps_rsp_buf);
+		kfree(driver->user_space_data);
+		if (driver->diag_wq)
+			destroy_workqueue(driver->diag_wq);
+}
+
+void diagfwd_exit(void)
+{
+	smd_close(driver->ch);
+	smd_close(driver->chqdsp);
+	smd_close(driver->ch_wcnss);
+	driver->ch = 0;		/* SMD can make this NULL */
+	driver->chqdsp = 0;
+	driver->ch_wcnss = 0;
+#ifdef CONFIG_DIAG_OVER_USB
+	if (driver->usb_connected)
+		usb_diag_free_req(driver->legacy_ch);
+	usb_diag_close(driver->legacy_ch);
+#endif
+	platform_driver_unregister(&msm_smd_ch1_driver);
+	platform_driver_unregister(&msm_diag_dci_driver);
+	platform_driver_unregister(&diag_smd_lite_driver);
+	kfree(driver->event_mask);
+	kfree(driver->log_mask);
+	kfree(driver->msg_mask);
+	kfree(driver->buf_in_1);
+	kfree(driver->buf_in_2);
+	kfree(driver->buf_in_qdsp_1);
+	kfree(driver->buf_in_qdsp_2);
+	kfree(driver->buf_in_wcnss_1);
+	kfree(driver->buf_in_wcnss_2);
+	kfree(driver->buf_msg_mask_update);
+	kfree(driver->buf_log_mask_update);
+	kfree(driver->buf_event_mask_update);
+	kfree(driver->usb_buf_out);
+	kfree(driver->hdlc_buf);
+	kfree(driver->msg_masks);
+	kfree(driver->log_masks);
+	kfree(driver->event_masks);
+	kfree(driver->client_map);
+	kfree(driver->buf_tbl);
+	kfree(driver->data_ready);
+	kfree(driver->table);
+	kfree(driver->pkt_buf);
+	kfree(driver->write_ptr_1);
+	kfree(driver->write_ptr_2);
+	kfree(driver->write_ptr_qdsp_1);
+	kfree(driver->write_ptr_qdsp_2);
+	kfree(driver->write_ptr_wcnss_1);
+	kfree(driver->write_ptr_wcnss_2);
+	kfree(driver->usb_read_ptr);
+	kfree(driver->apps_rsp_buf);
+	kfree(driver->user_space_data);
+	destroy_workqueue(driver->diag_wq);
+}
diff --git a/drivers/char/diag/diagfwd.h b/drivers/char/diag/diagfwd.h
new file mode 100644
index 0000000..f5de2ac
--- /dev/null
+++ b/drivers/char/diag/diagfwd.h
@@ -0,0 +1,48 @@
+/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef DIAGFWD_H
+#define DIAGFWD_H
+
+#define NO_PROCESS	0
+#define NON_APPS_PROC	-1
+
+void diagfwd_init(void);
+void diagfwd_exit(void);
+void diag_process_hdlc(void *data, unsigned len);
+void __diag_smd_send_req(void);
+void __diag_smd_qdsp_send_req(void);
+void __diag_smd_wcnss_send_req(void);
+void diag_usb_legacy_notifier(void *, unsigned, struct diag_request *);
+long diagchar_ioctl(struct file *, unsigned int, unsigned long);
+int diag_device_write(void *, int, struct diag_request *);
+int mask_request_validate(unsigned char mask_buf[]);
+void diag_clear_reg(int);
+int chk_config_get_id(void);
+int chk_apps_only(void);
+int chk_apps_master(void);
+int chk_polling_response(void);
+void diag_send_event_mask_update(smd_channel_t *, int num_bytes);
+void diag_send_msg_mask_update(smd_channel_t *, int ssid_first,
+					 int ssid_last, int proc);
+void diag_send_log_mask_update(smd_channel_t *, int);
+void diag_update_sleeping_process(int process_id, int data_type);
+/* State for diag forwarding */
+#ifdef CONFIG_DIAG_OVER_USB
+int diagfwd_connect(void);
+int diagfwd_disconnect(void);
+#endif
+extern int diag_debug_buf_idx;
+extern unsigned char diag_debug_buf[1024];
+extern int diag_event_num_bytes;
+extern struct platform_driver msm_diag_dci_driver;
+#endif
diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c
new file mode 100644
index 0000000..de1a5b5
--- /dev/null
+++ b/drivers/char/diag/diagfwd_cntl.c
@@ -0,0 +1,604 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include <linux/diagchar.h>
+#include <linux/platform_device.h>
+#include <linux/kmemleak.h>
+#include "diagchar.h"
+#include "diagfwd.h"
+#include "diagfwd_cntl.h"
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#endif
+
+#define HDR_SIZ 8
+
+void diag_smd_cntl_notify(void *ctxt, unsigned event)
+{
+	int r1, r2;
+
+	if (!(driver->ch_cntl))
+		return;
+
+	switch (event) {
+	case SMD_EVENT_DATA:
+		r1 = smd_read_avail(driver->ch_cntl);
+		r2 = smd_cur_packet_size(driver->ch_cntl);
+		if (r1 > 0 && r1 == r2)
+			queue_work(driver->diag_wq,
+				 &(driver->diag_read_smd_cntl_work));
+		else
+			pr_debug("diag: incomplete pkt on Modem CNTL ch\n");
+		break;
+	case SMD_EVENT_OPEN:
+		queue_work(driver->diag_cntl_wq,
+			 &(driver->diag_modem_mask_update_work));
+		break;
+	}
+}
+
+void diag_smd_qdsp_cntl_notify(void *ctxt, unsigned event)
+{
+	int r1, r2;
+
+	if (!(driver->chqdsp_cntl))
+		return;
+
+	switch (event) {
+	case SMD_EVENT_DATA:
+		r1 = smd_read_avail(driver->chqdsp_cntl);
+		r2 = smd_cur_packet_size(driver->chqdsp_cntl);
+		if (r1 > 0 && r1 == r2)
+			queue_work(driver->diag_wq,
+				 &(driver->diag_read_smd_qdsp_cntl_work));
+		else
+			pr_debug("diag: incomplete pkt on LPASS CNTL ch\n");
+		break;
+	case SMD_EVENT_OPEN:
+		queue_work(driver->diag_cntl_wq,
+			 &(driver->diag_qdsp_mask_update_work));
+		break;
+	}
+}
+
+void diag_smd_wcnss_cntl_notify(void *ctxt, unsigned event)
+{
+	int r1, r2;
+
+	if (!(driver->ch_wcnss_cntl))
+		return;
+
+	switch (event) {
+	case SMD_EVENT_DATA:
+		r1 = smd_read_avail(driver->ch_wcnss_cntl);
+		r2 = smd_cur_packet_size(driver->ch_wcnss_cntl);
+		if (r1 > 0 && r1 == r2)
+			queue_work(driver->diag_wq,
+				 &(driver->diag_read_smd_wcnss_cntl_work));
+		else
+			pr_debug("diag: incomplete pkt on WCNSS CNTL ch\n");
+		break;
+	case SMD_EVENT_OPEN:
+		queue_work(driver->diag_cntl_wq,
+			 &(driver->diag_wcnss_mask_update_work));
+		break;
+	}
+}
+
+static void diag_smd_cntl_send_req(int proc_num)
+{
+	int data_len = 0, type = -1, count_bytes = 0, j, r, flag = 0;
+	struct bindpkt_params_per_process *pkt_params =
+		 kzalloc(sizeof(struct bindpkt_params_per_process), GFP_KERNEL);
+	struct diag_ctrl_msg *msg;
+	struct cmd_code_range *range;
+	struct bindpkt_params *temp;
+	void *buf = NULL;
+	smd_channel_t *smd_ch = NULL;
+
+	if (pkt_params == NULL) {
+		pr_alert("diag: Memory allocation failure\n");
+		return;
+	}
+
+	if (proc_num == MODEM_PROC) {
+		buf = driver->buf_in_cntl;
+		smd_ch = driver->ch_cntl;
+	} else if (proc_num == QDSP_PROC) {
+		buf = driver->buf_in_qdsp_cntl;
+		smd_ch = driver->chqdsp_cntl;
+	} else if (proc_num == WCNSS_PROC) {
+		buf = driver->buf_in_wcnss_cntl;
+		smd_ch = driver->ch_wcnss_cntl;
+	}
+
+	if (!smd_ch || !buf) {
+		kfree(pkt_params);
+		return;
+	}
+
+	r = smd_read_avail(smd_ch);
+	if (r > IN_BUF_SIZE) {
+		if (r < MAX_IN_BUF_SIZE) {
+			pr_err("diag: SMD CNTL sending pkt upto %d bytes", r);
+			buf = krealloc(buf, r, GFP_KERNEL);
+		} else {
+			pr_err("diag: CNTL pkt > %d bytes", MAX_IN_BUF_SIZE);
+			kfree(pkt_params);
+			return;
+		}
+	}
+	if (buf && r > 0) {
+		smd_read(smd_ch, buf, r);
+		while (count_bytes + HDR_SIZ <= r) {
+			type = *(uint32_t *)(buf);
+			data_len = *(uint32_t *)(buf + 4);
+			if (type < DIAG_CTRL_MSG_REG ||
+					 type > DIAG_CTRL_MSG_F3_MASK_V2) {
+				pr_alert("diag: Invalid Msg type %d proc %d",
+					 type, proc_num);
+				break;
+			}
+			if (data_len < 0 || data_len > r) {
+				pr_alert("diag: Invalid data len %d proc %d",
+					 data_len, proc_num);
+				break;
+			}
+			count_bytes = count_bytes+HDR_SIZ+data_len;
+			if (type == DIAG_CTRL_MSG_REG && r >= count_bytes) {
+				msg = buf+HDR_SIZ;
+				range = buf+HDR_SIZ+
+						sizeof(struct diag_ctrl_msg);
+				pkt_params->count = msg->count_entries;
+				temp = kzalloc(pkt_params->count * sizeof(struct
+						 bindpkt_params), GFP_KERNEL);
+				if (temp == NULL) {
+					pr_alert("diag: Memory alloc fail\n");
+					kfree(pkt_params);
+					return;
+				}
+				for (j = 0; j < pkt_params->count; j++) {
+					temp->cmd_code = msg->cmd_code;
+					temp->subsys_id = msg->subsysid;
+					temp->client_id = proc_num;
+					temp->proc_id = proc_num;
+					temp->cmd_code_lo = range->cmd_code_lo;
+					temp->cmd_code_hi = range->cmd_code_hi;
+					range++;
+					temp++;
+				}
+				temp -= pkt_params->count;
+				pkt_params->params = temp;
+				flag = 1;
+				diagchar_ioctl(NULL, DIAG_IOCTL_COMMAND_REG,
+						 (unsigned long)pkt_params);
+				kfree(temp);
+			}
+			buf = buf + HDR_SIZ + data_len;
+		}
+	}
+	kfree(pkt_params);
+	if (flag) {
+		/* Poll SMD CNTL channels to check for data */
+		if (proc_num == MODEM_PROC)
+			diag_smd_cntl_notify(NULL, SMD_EVENT_DATA);
+		else if (proc_num == QDSP_PROC)
+			diag_smd_qdsp_cntl_notify(NULL, SMD_EVENT_DATA);
+		else if (proc_num == WCNSS_PROC)
+			diag_smd_wcnss_cntl_notify(NULL, SMD_EVENT_DATA);
+	}
+}
+
+void diag_read_smd_cntl_work_fn(struct work_struct *work)
+{
+	diag_smd_cntl_send_req(MODEM_PROC);
+}
+
+void diag_read_smd_qdsp_cntl_work_fn(struct work_struct *work)
+{
+	diag_smd_cntl_send_req(QDSP_PROC);
+}
+
+void diag_read_smd_wcnss_cntl_work_fn(struct work_struct *work)
+{
+	diag_smd_cntl_send_req(WCNSS_PROC);
+}
+
+static int diag_smd_cntl_probe(struct platform_device *pdev)
+{
+	int r = 0;
+
+	/* open control ports only on 8960 & newer targets */
+	if (chk_apps_only()) {
+		if (pdev->id == SMD_APPS_MODEM)
+			r = smd_open("DIAG_CNTL", &driver->ch_cntl, driver,
+							diag_smd_cntl_notify);
+		if (pdev->id == SMD_APPS_QDSP)
+			r = smd_named_open_on_edge("DIAG_CNTL", SMD_APPS_QDSP
+				, &driver->chqdsp_cntl, driver,
+					 diag_smd_qdsp_cntl_notify);
+		if (pdev->id == SMD_APPS_WCNSS)
+			r = smd_named_open_on_edge("APPS_RIVA_CTRL",
+				SMD_APPS_WCNSS, &driver->ch_wcnss_cntl,
+					driver, diag_smd_wcnss_cntl_notify);
+		pr_debug("diag: open CNTL port, ID = %d,r = %d\n", pdev->id, r);
+	}
+	return 0;
+}
+
+static int diagfwd_cntl_runtime_suspend(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: suspending...\n");
+	return 0;
+}
+
+static int diagfwd_cntl_runtime_resume(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: resuming...\n");
+	return 0;
+}
+
+static const struct dev_pm_ops diagfwd_cntl_dev_pm_ops = {
+	.runtime_suspend = diagfwd_cntl_runtime_suspend,
+	.runtime_resume = diagfwd_cntl_runtime_resume,
+};
+
+static struct platform_driver msm_smd_ch1_cntl_driver = {
+
+	.probe = diag_smd_cntl_probe,
+	.driver = {
+			.name = "DIAG_CNTL",
+			.owner = THIS_MODULE,
+			.pm   = &diagfwd_cntl_dev_pm_ops,
+		   },
+};
+
+static struct platform_driver diag_smd_lite_cntl_driver = {
+
+	.probe = diag_smd_cntl_probe,
+	.driver = {
+			.name = "APPS_RIVA_CTRL",
+			.owner = THIS_MODULE,
+			.pm   = &diagfwd_cntl_dev_pm_ops,
+		   },
+};
+
+void diagfwd_cntl_init(void)
+{
+	driver->polling_reg_flag = 0;
+	driver->diag_cntl_wq = create_singlethread_workqueue("diag_cntl_wq");
+	if (driver->buf_in_cntl == NULL) {
+		driver->buf_in_cntl = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
+		if (driver->buf_in_cntl == NULL)
+			goto err;
+		kmemleak_not_leak(driver->buf_in_cntl);
+	}
+	if (driver->buf_in_qdsp_cntl == NULL) {
+		driver->buf_in_qdsp_cntl = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
+		if (driver->buf_in_qdsp_cntl == NULL)
+			goto err;
+		kmemleak_not_leak(driver->buf_in_qdsp_cntl);
+	}
+	if (driver->buf_in_wcnss_cntl == NULL) {
+		driver->buf_in_wcnss_cntl = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
+		if (driver->buf_in_wcnss_cntl == NULL)
+			goto err;
+		kmemleak_not_leak(driver->buf_in_wcnss_cntl);
+	}
+	platform_driver_register(&msm_smd_ch1_cntl_driver);
+	platform_driver_register(&diag_smd_lite_cntl_driver);
+
+	return;
+err:
+		pr_err("diag: Could not initialize diag buffers");
+		kfree(driver->buf_in_cntl);
+		kfree(driver->buf_in_qdsp_cntl);
+		kfree(driver->buf_in_wcnss_cntl);
+		if (driver->diag_cntl_wq)
+			destroy_workqueue(driver->diag_cntl_wq);
+}
+
+void diagfwd_cntl_exit(void)
+{
+	smd_close(driver->ch_cntl);
+	smd_close(driver->chqdsp_cntl);
+	smd_close(driver->ch_wcnss_cntl);
+	driver->ch_cntl = 0;
+	driver->chqdsp_cntl = 0;
+	driver->ch_wcnss_cntl = 0;
+	destroy_workqueue(driver->diag_cntl_wq);
+	platform_driver_unregister(&msm_smd_ch1_cntl_driver);
+	platform_driver_unregister(&diag_smd_lite_cntl_driver);
+
+	kfree(driver->buf_in_cntl);
+	kfree(driver->buf_in_qdsp_cntl);
+	kfree(driver->buf_in_wcnss_cntl);
+}
+
+#ifdef CONFIG_DEBUG_FS
+#define DEBUG_BUF_SIZE	4096
+static struct dentry *diag_dbgfs_dent;
+static int diag_dbgfs_table_index;
+
+static ssize_t diag_dbgfs_read_status(struct file *file, char __user *ubuf,
+				      size_t count, loff_t *ppos)
+{
+	char *buf;
+	int ret;
+
+	buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+	if (!buf) {
+		pr_err("diag: %s, Error allocating memory\n", __func__);
+		return -ENOMEM;
+	}
+
+	ret = scnprintf(buf, DEBUG_BUF_SIZE,
+		"modem ch: 0x%x\n"
+		"lpass ch: 0x%x\n"
+		"riva ch: 0x%x\n"
+		"dci ch: 0x%x\n"
+		"modem cntl_ch: 0x%x\n"
+		"lpass cntl_ch: 0x%x\n"
+		"riva cntl_ch: 0x%x\n"
+		"CPU Tools id: %d\n"
+		"Apps only: %d\n"
+		"Apps master: %d\n"
+		"Check Polling Response: %d\n"
+		"polling_reg_flag: %d\n"
+		"uses device tree: %d\n"
+		"in_busy_1: %d\n"
+		"in_busy_2: %d\n"
+		"in_busy_qdsp_1: %d\n"
+		"in_busy_qdsp_2: %d\n"
+		"in_busy_wcnss_1: %d\n"
+		"in_busy_wcnss_2: %d\n"
+		"in_busy_dci: %d\n",
+		(unsigned int)driver->ch,
+		(unsigned int)driver->chqdsp,
+		(unsigned int)driver->ch_wcnss,
+		(unsigned int)driver->ch_dci,
+		(unsigned int)driver->ch_cntl,
+		(unsigned int)driver->chqdsp_cntl,
+		(unsigned int)driver->ch_wcnss_cntl,
+		chk_config_get_id(),
+		chk_apps_only(),
+		chk_apps_master(),
+		chk_polling_response(),
+		driver->polling_reg_flag,
+		driver->use_device_tree,
+		driver->in_busy_1,
+		driver->in_busy_2,
+		driver->in_busy_qdsp_1,
+		driver->in_busy_qdsp_2,
+		driver->in_busy_wcnss_1,
+		driver->in_busy_wcnss_2,
+		driver->in_busy_dci);
+
+#ifdef CONFIG_DIAG_OVER_USB
+	ret += scnprintf(buf+ret, DEBUG_BUF_SIZE,
+		"usb_connected: %d\n",
+		driver->usb_connected);
+#endif
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, ret);
+
+	kfree(buf);
+	return ret;
+}
+
+static ssize_t diag_dbgfs_read_workpending(struct file *file,
+				char __user *ubuf, size_t count, loff_t *ppos)
+{
+	char *buf;
+	int ret;
+
+	buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+	if (!buf) {
+		pr_err("diag: %s, Error allocating memory\n", __func__);
+		return -ENOMEM;
+	}
+
+	ret = scnprintf(buf, DEBUG_BUF_SIZE,
+		"Pending status for work_stucts:\n"
+		"diag_drain_work: %d\n"
+		"diag_read_smd_work: %d\n"
+		"diag_read_smd_cntl_work: %d\n"
+		"diag_read_smd_qdsp_work: %d\n"
+		"diag_read_smd_qdsp_cntl_work: %d\n"
+		"diag_read_smd_wcnss_work: %d\n"
+		"diag_read_smd_wcnss_cntl_work: %d\n"
+		"diag_modem_mask_update_work: %d\n"
+		"diag_qdsp_mask_update_work: %d\n"
+		"diag_wcnss_mask_update_work: %d\n"
+		"diag_read_smd_dci_work: %d\n",
+		work_pending(&(driver->diag_drain_work)),
+		work_pending(&(driver->diag_read_smd_work)),
+		work_pending(&(driver->diag_read_smd_cntl_work)),
+		work_pending(&(driver->diag_read_smd_qdsp_work)),
+		work_pending(&(driver->diag_read_smd_qdsp_cntl_work)),
+		work_pending(&(driver->diag_read_smd_wcnss_work)),
+		work_pending(&(driver->diag_read_smd_wcnss_cntl_work)),
+		work_pending(&(driver->diag_modem_mask_update_work)),
+		work_pending(&(driver->diag_qdsp_mask_update_work)),
+		work_pending(&(driver->diag_wcnss_mask_update_work)),
+		work_pending(&(driver->diag_read_smd_dci_work)));
+
+#ifdef CONFIG_DIAG_OVER_USB
+	ret += scnprintf(buf+ret, DEBUG_BUF_SIZE,
+		"diag_proc_hdlc_work: %d\n"
+		"diag_read_work: %d\n",
+		work_pending(&(driver->diag_proc_hdlc_work)),
+		work_pending(&(driver->diag_read_work)));
+#endif
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, ret);
+
+	kfree(buf);
+	return ret;
+}
+
+static ssize_t diag_dbgfs_read_table(struct file *file, char __user *ubuf,
+				     size_t count, loff_t *ppos)
+{
+	char *buf;
+	int ret = 0;
+	int i;
+	int bytes_remaining;
+	int bytes_in_buffer = 0;
+	int bytes_written;
+	int buf_size = (DEBUG_BUF_SIZE < count) ? DEBUG_BUF_SIZE : count;
+
+	if (diag_dbgfs_table_index >= diag_max_reg) {
+		/* Done. Reset to prepare for future requests */
+		diag_dbgfs_table_index = 0;
+		return 0;
+	}
+
+	buf = kzalloc(sizeof(char) * buf_size, GFP_KERNEL);
+	if (!buf) {
+		pr_err("diag: %s, Error allocating memory\n", __func__);
+		return -ENOMEM;
+	}
+
+	bytes_remaining = buf_size;
+	for (i = diag_dbgfs_table_index; i < diag_max_reg; i++) {
+		/* Do not process empty entries in the table */
+		if (driver->table[i].process_id == 0)
+			continue;
+
+		bytes_written = scnprintf(buf+bytes_in_buffer, bytes_remaining,
+			"i: %3d, cmd_code: %4x, subsys_id: %4x, "
+			"client: %2d, cmd_code_lo: %4x, "
+			"cmd_code_hi: %4x, process_id: %5d\n",
+			i,
+			driver->table[i].cmd_code,
+			driver->table[i].subsys_id,
+			driver->table[i].client_id,
+			driver->table[i].cmd_code_lo,
+			driver->table[i].cmd_code_hi,
+			driver->table[i].process_id);
+
+		bytes_in_buffer += bytes_written;
+
+		/* Check if there is room to add another table entry */
+		bytes_remaining = buf_size - bytes_in_buffer;
+		if (bytes_remaining < bytes_written)
+			break;
+	}
+	diag_dbgfs_table_index = i;
+
+	*ppos = 0;
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, bytes_in_buffer);
+
+	kfree(buf);
+	return ret;
+}
+
+#ifdef CONFIG_DIAG_HSIC_PIPE
+static ssize_t diag_dbgfs_read_hsic(struct file *file, char __user *ubuf,
+				    size_t count, loff_t *ppos)
+{
+	char *buf;
+	int ret;
+
+	buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+	if (!buf) {
+		pr_err("diag: %s, Error allocating memory\n", __func__);
+		return -ENOMEM;
+	}
+
+	ret = scnprintf(buf, DEBUG_BUF_SIZE,
+		"hsic initialized: %d\n"
+		"hsic ch: %d\n"
+		"hsic enabled: %d\n"
+		"hsic_opened: %d\n"
+		"hisc_suspend: %d\n"
+		"in_busy_hsic_read_on_mdm: %d\n"
+		"in_busy_hsic_write_on_mdm: %d\n"
+		"in_busy_hsic_write: %d\n"
+		"in_busy_hsic_read: %d\n"
+		"usb_mdm_connected: %d\n"
+		"diag_read_mdm_work: %d\n"
+		"diag_read_hsic_work: %d\n"
+		"diag_disconnect_work: %d\n"
+		"diag_usb_read_complete_work: %d\n",
+		driver->hsic_initialized,
+		driver->hsic_ch,
+		driver->hsic_device_enabled,
+		driver->hsic_device_opened,
+		driver->hsic_suspend,
+		driver->in_busy_hsic_read_on_device,
+		driver->in_busy_hsic_write_on_device,
+		driver->in_busy_hsic_write,
+		driver->in_busy_hsic_read,
+		driver->usb_mdm_connected,
+		work_pending(&(driver->diag_read_mdm_work)),
+		work_pending(&(driver->diag_read_hsic_work)),
+		work_pending(&(driver->diag_disconnect_work)),
+		work_pending(&(driver->diag_usb_read_complete_work)));
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, ret);
+
+	kfree(buf);
+	return ret;
+}
+
+const struct file_operations diag_dbgfs_hsic_ops = {
+	.read = diag_dbgfs_read_hsic,
+};
+#endif
+
+const struct file_operations diag_dbgfs_status_ops = {
+	.read = diag_dbgfs_read_status,
+};
+
+const struct file_operations diag_dbgfs_table_ops = {
+	.read = diag_dbgfs_read_table,
+};
+
+const struct file_operations diag_dbgfs_workpending_ops = {
+	.read = diag_dbgfs_read_workpending,
+};
+
+void diag_debugfs_init(void)
+{
+	diag_dbgfs_dent = debugfs_create_dir("diag", 0);
+	if (IS_ERR(diag_dbgfs_dent))
+		return;
+
+	debugfs_create_file("status", 0444, diag_dbgfs_dent, 0,
+		&diag_dbgfs_status_ops);
+
+	debugfs_create_file("table", 0444, diag_dbgfs_dent, 0,
+		&diag_dbgfs_table_ops);
+
+	debugfs_create_file("work_pending", 0444, diag_dbgfs_dent, 0,
+		&diag_dbgfs_workpending_ops);
+
+#ifdef CONFIG_DIAG_HSIC_PIPE
+	debugfs_create_file("hsic", 0444, diag_dbgfs_dent, 0,
+		&diag_dbgfs_hsic_ops);
+#endif
+
+	diag_dbgfs_table_index = 0;
+}
+
+void diag_debugfs_cleanup(void)
+{
+	if (diag_dbgfs_dent) {
+		debugfs_remove_recursive(diag_dbgfs_dent);
+		diag_dbgfs_dent = NULL;
+	}
+}
+#else
+void diag_debugfs_init(void) { }
+void diag_debugfs_cleanup(void) { }
+#endif
diff --git a/drivers/char/diag/diagfwd_cntl.h b/drivers/char/diag/diagfwd_cntl.h
new file mode 100644
index 0000000..743ddc1
--- /dev/null
+++ b/drivers/char/diag/diagfwd_cntl.h
@@ -0,0 +1,92 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef DIAGFWD_CNTL_H
+#define DIAGFWD_CNTL_H
+
+/* Message registration commands */
+#define DIAG_CTRL_MSG_REG		1
+/* Message passing for DTR events */
+#define DIAG_CTRL_MSG_DTR		2
+/* Control Diag sleep vote, buffering etc */
+#define DIAG_CTRL_MSG_DIAGMODE		3
+/* Diag data based on "light" diag mask */
+#define DIAG_CTRL_MSG_DIAGDATA		4
+/* Send diag internal feature mask 'diag_int_feature_mask' */
+#define DIAG_CTRL_MSG_FEATURE		8
+/* Send Diag log mask for a particular equip id */
+#define DIAG_CTRL_MSG_EQUIP_LOG_MASK	9
+/* Send Diag event mask */
+#define DIAG_CTRL_MSG_EVENT_MASK_V2	10
+/* Send Diag F3 mask */
+#define DIAG_CTRL_MSG_F3_MASK_V2	11
+
+struct cmd_code_range {
+	uint16_t cmd_code_lo;
+	uint16_t cmd_code_hi;
+	uint32_t data;
+};
+
+struct diag_ctrl_msg {
+	uint32_t version;
+	uint16_t cmd_code;
+	uint16_t subsysid;
+	uint16_t count_entries;
+	uint16_t port;
+};
+
+struct diag_ctrl_event_mask {
+	uint32_t cmd_type;
+	uint32_t data_len;
+	uint8_t stream_id;
+	uint8_t status;
+	uint8_t event_config;
+	uint32_t event_mask_size;
+	/* Copy event mask here */
+} __packed;
+
+struct diag_ctrl_log_mask {
+	uint32_t cmd_type;
+	uint32_t data_len;
+	uint8_t stream_id;
+	uint8_t status;
+	uint8_t equip_id;
+	uint32_t num_items; /* Last log code for this equip_id */
+	uint32_t log_mask_size; /* Size of log mask stored in log_mask[] */
+	/* Copy log mask here */
+} __packed;
+
+struct diag_ctrl_msg_mask {
+	uint32_t cmd_type;
+	uint32_t data_len;
+	uint8_t stream_id;
+	uint8_t status;
+	uint8_t msg_mode;
+	uint16_t ssid_first; /* Start of range of supported SSIDs */
+	uint16_t ssid_last; /* Last SSID in range */
+	uint32_t msg_mask_size; /* ssid_last - ssid_first + 1 */
+	/* Copy msg mask here */
+} __packed;
+
+void diagfwd_cntl_init(void);
+void diagfwd_cntl_exit(void);
+void diag_read_smd_cntl_work_fn(struct work_struct *);
+void diag_read_smd_qdsp_cntl_work_fn(struct work_struct *);
+void diag_read_smd_wcnss_cntl_work_fn(struct work_struct *);
+void diag_smd_cntl_notify(void *ctxt, unsigned event);
+void diag_smd_qdsp_cntl_notify(void *ctxt, unsigned event);
+void diag_smd_wcnss_cntl_notify(void *ctxt, unsigned event);
+
+void diag_debugfs_init(void);
+void diag_debugfs_cleanup(void);
+
+#endif
diff --git a/drivers/char/diag/diagfwd_hsic.c b/drivers/char/diag/diagfwd_hsic.c
new file mode 100644
index 0000000..a3c6f26
--- /dev/null
+++ b/drivers/char/diag/diagfwd_hsic.c
@@ -0,0 +1,604 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/uaccess.h>
+#include <linux/diagchar.h>
+#include <linux/sched.h>
+#include <linux/err.h>
+#include <linux/workqueue.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <asm/current.h>
+#ifdef CONFIG_DIAG_OVER_USB
+#include <mach/usbdiag.h>
+#endif
+#include "diagchar_hdlc.h"
+#include "diagmem.h"
+#include "diagchar.h"
+#include "diagfwd.h"
+#include "diagfwd_hsic.h"
+
+static void diag_read_hsic_work_fn(struct work_struct *work)
+{
+	if (!driver->hsic_ch) {
+		pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__);
+		return;
+	}
+
+	/* If there is no hsic data being read from the hsic and there
+	 * is no hsic data being written to the device
+	 */
+	if (!driver->in_busy_hsic_read &&
+			 !driver->in_busy_hsic_write_on_device) {
+		/*
+		 * Initiate the read from the hsic.  The hsic read is
+		 * asynchronous.  Once the read is complete the read
+		 * callback function will be called.
+		 */
+		int err;
+		driver->in_busy_hsic_read = 1;
+		APPEND_DEBUG('i');
+		pr_debug("diag: read from HSIC\n");
+		err = diag_bridge_read((char *)driver->buf_in_hsic,
+					IN_BUF_SIZE);
+		if (err) {
+			pr_err("DIAG: Error initiating HSIC read, err: %d\n",
+				err);
+			/*
+			 * If the error is recoverable, then clear
+			 * the read flag, so we will resubmit a
+			 * read on the next frame.  Otherwise, don't
+			 * resubmit a read on the next frame.
+			 */
+			if ((-ESHUTDOWN) != err)
+				driver->in_busy_hsic_read = 0;
+		}
+	}
+
+	/*
+	 * If for some reason there was no hsic data, set up
+	 * the next read
+	 */
+	if (!driver->in_busy_hsic_read)
+		queue_work(driver->diag_hsic_wq, &driver->diag_read_hsic_work);
+}
+
+static void diag_hsic_read_complete_callback(void *ctxt, char *buf,
+					int buf_size, int actual_size)
+{
+	/* The read of the data from the HSIC bridge is complete */
+	driver->in_busy_hsic_read = 0;
+
+	if (!driver->hsic_ch) {
+		pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__);
+		return;
+	}
+
+	APPEND_DEBUG('j');
+	if (actual_size > 0) {
+		if (!buf) {
+			pr_err("Out of diagmem for HSIC\n");
+		} else {
+			driver->write_ptr_mdm->length = actual_size;
+			/*
+			 * Set flag to denote hsic data is currently
+			 * being written to the usb mdm channel.
+			 * driver->buf_in_hsic was given to
+			 * diag_bridge_read(), so buf here should be
+			 * driver->buf_in_hsic
+			 */
+			driver->in_busy_hsic_write_on_device = 1;
+			pr_debug("diag: write to device\n");
+			diag_device_write((void *)buf, HSIC_DATA,
+						driver->write_ptr_mdm);
+		}
+	} else {
+		pr_debug("%s: actual_size: %d\n", __func__, actual_size);
+	}
+
+	/*
+	 * If for some reason there was no hsic data to write to the
+	 * mdm channel, set up another read
+	 */
+	if (!driver->in_busy_hsic_write_on_device && ((driver->logging_mode
+			== MEMORY_DEVICE_MODE) || (driver->usb_mdm_connected &&
+						    !driver->hsic_suspend)))
+		queue_work(driver->diag_hsic_wq, &driver->diag_read_hsic_work);
+}
+
+static void diag_hsic_write_complete_callback(void *ctxt, char *buf,
+					int buf_size, int actual_size)
+{
+	/* The write of the data to the HSIC bridge is complete */
+	driver->in_busy_hsic_write = 0;
+
+	if (!driver->hsic_ch) {
+		pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__);
+		return;
+	}
+
+	if (actual_size < 0)
+		pr_err("DIAG in %s: actual_size: %d\n", __func__, actual_size);
+
+	if (driver->usb_mdm_connected)
+		queue_work(driver->diag_hsic_wq, &driver->diag_read_mdm_work);
+}
+
+static int diag_hsic_suspend(void *ctxt)
+{
+	pr_debug("diag: hsic_suspend\n");
+	if (driver->in_busy_hsic_write)
+		return -EBUSY;
+
+	/* Don't allow suspend if in MEMORY_DEVICE_MODE */
+	if (driver->logging_mode == MEMORY_DEVICE_MODE)
+		return -EBUSY;
+
+	driver->hsic_suspend = 1;
+
+	return 0;
+}
+
+static void diag_hsic_resume(void *ctxt)
+{
+	pr_debug("diag: hsic_resume\n");
+	driver->hsic_suspend = 0;
+
+	if (!driver->in_busy_hsic_write_on_device && (driver->logging_mode
+			== MEMORY_DEVICE_MODE || driver->usb_mdm_connected))
+		queue_work(driver->diag_hsic_wq, &driver->diag_read_hsic_work);
+}
+
+static struct diag_bridge_ops hsic_diag_bridge_ops = {
+	.ctxt = NULL,
+	.read_complete_cb = diag_hsic_read_complete_callback,
+	.write_complete_cb = diag_hsic_write_complete_callback,
+	.suspend = diag_hsic_suspend,
+	.resume = diag_hsic_resume,
+};
+
+static int diag_hsic_close(void)
+{
+	if (driver->hsic_device_enabled) {
+		driver->hsic_ch = 0;
+		if (driver->hsic_device_opened) {
+			driver->hsic_device_opened = 0;
+			diag_bridge_close();
+		}
+		pr_debug("diag: in %s: closed successfully\n", __func__);
+	} else {
+		pr_debug("diag: in %s: already closed\n", __func__);
+	}
+
+	return 0;
+}
+
+/* diagfwd_cancel_hsic is called to cancel outstanding read/writes */
+int diagfwd_cancel_hsic(void)
+{
+	int err;
+
+	if (driver->hsic_device_enabled) {
+		if (driver->hsic_device_opened) {
+			driver->hsic_ch = 0;
+			driver->hsic_device_opened = 0;
+			diag_bridge_close();
+			err = diag_bridge_open(&hsic_diag_bridge_ops);
+			if (err) {
+				pr_err("DIAG: HSIC channel open error: %d\n",
+					err);
+			} else {
+				pr_debug("DIAG: opened HSIC channel\n");
+				driver->hsic_device_opened = 1;
+				driver->hsic_ch = 1;
+			}
+		}
+	}
+
+	return 0;
+}
+
+/* diagfwd_connect_hsic is called when the USB mdm channel is connected */
+int diagfwd_connect_hsic(int process_cable)
+{
+	int err;
+
+	pr_debug("DIAG in %s\n", __func__);
+
+	/* If the usb cable is being connected */
+	if (process_cable) {
+		err = usb_diag_alloc_req(driver->mdm_ch, N_MDM_WRITE,
+			N_MDM_READ);
+		if (err)
+			pr_err("DIAG: unable to alloc USB req on mdm"
+				" ch err:%d\n", err);
+
+		driver->usb_mdm_connected = 1;
+	}
+
+	driver->in_busy_hsic_write_on_device = 0;
+	driver->in_busy_hsic_read_on_device = 0;
+	driver->in_busy_hsic_write = 0;
+	driver->in_busy_hsic_read = 0;
+
+	/* If the hsic (diag_bridge) platform device is not open */
+	if (driver->hsic_device_enabled) {
+		if (!driver->hsic_device_opened) {
+			err = diag_bridge_open(&hsic_diag_bridge_ops);
+			if (err) {
+				pr_err("DIAG: HSIC channel open error: %d\n",
+					err);
+			} else {
+				pr_debug("DIAG: opened HSIC channel\n");
+				driver->hsic_device_opened = 1;
+			}
+		} else {
+			pr_debug("DIAG: HSIC channel already open\n");
+		}
+
+		/*
+		 * Turn on communication over usb mdm and hsic, if the hsic
+		 * device driver is enabled and opened
+		 */
+		if (driver->hsic_device_opened)
+			driver->hsic_ch = 1;
+
+		/* Poll USB mdm channel to check for data */
+		if (driver->logging_mode == USB_MODE)
+			queue_work(driver->diag_hsic_wq,
+					&driver->diag_read_mdm_work);
+
+		/* Poll HSIC channel to check for data */
+		queue_work(driver->diag_hsic_wq, &driver->diag_read_hsic_work);
+	} else {
+		/* The hsic device driver has not yet been enabled */
+		pr_info("DIAG: HSIC channel not yet enabled\n");
+	}
+
+	return 0;
+}
+
+/*
+ * diagfwd_disconnect_hsic is called when the USB mdm channel
+ * is disconnected
+ */
+int diagfwd_disconnect_hsic(int process_cable)
+{
+	pr_debug("DIAG in %s\n", __func__);
+
+	/* If the usb cable is being disconnected */
+	if (process_cable) {
+		driver->usb_mdm_connected = 0;
+		usb_diag_free_req(driver->mdm_ch);
+	}
+
+	if (driver->logging_mode != MEMORY_DEVICE_MODE) {
+		driver->in_busy_hsic_write_on_device = 1;
+		driver->in_busy_hsic_read_on_device = 1;
+		driver->in_busy_hsic_write = 1;
+		driver->in_busy_hsic_read = 1;
+		/* Turn off communication over usb mdm and hsic */
+		return diag_hsic_close();
+	}
+	return 0;
+}
+
+/*
+ * diagfwd_write_complete_hsic is called after the asynchronous
+ * usb_diag_write() on mdm channel is complete
+ */
+int diagfwd_write_complete_hsic(void)
+{
+	/*
+	 * Clear flag to denote that the write of the hsic data on the
+	 * usb mdm channel is complete
+	 */
+	driver->in_busy_hsic_write_on_device = 0;
+	if (!driver->hsic_ch) {
+		pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__);
+		return 0;
+	}
+
+	APPEND_DEBUG('q');
+
+	/* Read data from the hsic */
+	queue_work(driver->diag_hsic_wq, &driver->diag_read_hsic_work);
+
+	return 0;
+}
+
+/* Called after the asychronous usb_diag_read() on mdm channel is complete */
+static int diagfwd_read_complete_hsic(struct diag_request *diag_read_ptr)
+{
+	/* The read of the usb driver on the mdm (not hsic) has completed */
+	driver->in_busy_hsic_read_on_device = 0;
+	driver->read_len_mdm = diag_read_ptr->actual;
+
+	if (!driver->hsic_ch) {
+		pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__);
+		return 0;
+	}
+
+	/*
+	 * The read of the usb driver on the mdm channel has completed.
+	 * If there is no write on the hsic in progress, check if the
+	 * read has data to pass on to the hsic. If so, pass the usb
+	 * mdm data on to the hsic.
+	 */
+	if (!driver->in_busy_hsic_write && driver->usb_buf_mdm_out &&
+		(driver->read_len_mdm > 0)) {
+
+		/*
+		 * Initiate the hsic write. The hsic write is
+		 * asynchronous. When complete the write
+		 * complete callback function will be called
+		 */
+		int err;
+		driver->in_busy_hsic_write = 1;
+		err = diag_bridge_write(driver->usb_buf_mdm_out,
+					driver->read_len_mdm);
+		if (err) {
+			pr_err("DIAG: mdm data on hsic write err: %d\n", err);
+			/*
+			 * If the error is recoverable, then clear
+			 * the write flag, so we will resubmit a
+			 * write on the next frame.  Otherwise, don't
+			 * resubmit a write on the next frame.
+			 */
+			if ((-ESHUTDOWN) != err)
+				driver->in_busy_hsic_write = 0;
+		}
+	}
+
+	/*
+	 * If there is no write of the usb mdm data on the
+	 * hsic channel
+	 */
+	if (!driver->in_busy_hsic_write)
+		queue_work(driver->diag_hsic_wq, &driver->diag_read_mdm_work);
+
+	return 0;
+}
+
+static void diagfwd_hsic_notifier(void *priv, unsigned event,
+					struct diag_request *d_req)
+{
+	switch (event) {
+	case USB_DIAG_CONNECT:
+		diagfwd_connect_hsic(1);
+		break;
+	case USB_DIAG_DISCONNECT:
+		queue_work(driver->diag_hsic_wq, &driver->diag_disconnect_work);
+		break;
+	case USB_DIAG_READ_DONE:
+		queue_work(driver->diag_hsic_wq,
+				&driver->diag_usb_read_complete_work);
+		break;
+	case USB_DIAG_WRITE_DONE:
+		diagfwd_write_complete_hsic();
+		break;
+	default:
+		pr_err("DIAG in %s: Unknown event from USB diag:%u\n",
+			__func__, event);
+		break;
+	}
+}
+
+static void diag_usb_read_complete_fn(struct work_struct *w)
+{
+	diagfwd_read_complete_hsic(driver->usb_read_mdm_ptr);
+}
+
+static void diag_disconnect_work_fn(struct work_struct *w)
+{
+	diagfwd_disconnect_hsic(1);
+}
+
+static void diag_read_mdm_work_fn(struct work_struct *work)
+{
+	if (!driver->hsic_ch) {
+		pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__);
+		return;
+	}
+
+	/*
+	 * If there is no data being read from the usb mdm channel
+	 * and there is no mdm channel data currently being written
+	 * to the hsic
+	 */
+	if (!driver->in_busy_hsic_read_on_device &&
+				 !driver->in_busy_hsic_write) {
+		APPEND_DEBUG('x');
+
+		/* Setup the next read from usb mdm channel */
+		driver->in_busy_hsic_read_on_device = 1;
+		driver->usb_read_mdm_ptr->buf = driver->usb_buf_mdm_out;
+		driver->usb_read_mdm_ptr->length = USB_MAX_OUT_BUF;
+		usb_diag_read(driver->mdm_ch, driver->usb_read_mdm_ptr);
+		APPEND_DEBUG('y');
+	}
+
+	/*
+	 * If for some reason there was no mdm channel read initiated,
+	 * queue up the reading of data from the mdm channel
+	 */
+	if (!driver->in_busy_hsic_read_on_device)
+		queue_work(driver->diag_hsic_wq, &driver->diag_read_mdm_work);
+}
+
+static int diag_hsic_probe(struct platform_device *pdev)
+{
+	int err = 0;
+	pr_debug("diag: in %s\n", __func__);
+	if (!driver->hsic_device_enabled) {
+		driver->read_len_mdm = 0;
+		if (driver->buf_in_hsic == NULL)
+			driver->buf_in_hsic = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
+		if (driver->buf_in_hsic == NULL)
+			goto err;
+		if (driver->usb_buf_mdm_out  == NULL)
+			driver->usb_buf_mdm_out = kzalloc(USB_MAX_OUT_BUF,
+								 GFP_KERNEL);
+		if (driver->usb_buf_mdm_out == NULL)
+			goto err;
+		if (driver->write_ptr_mdm == NULL)
+			driver->write_ptr_mdm = kzalloc(
+			sizeof(struct diag_request), GFP_KERNEL);
+		if (driver->write_ptr_mdm == NULL)
+			goto err;
+		if (driver->usb_read_mdm_ptr == NULL)
+			driver->usb_read_mdm_ptr = kzalloc(
+			sizeof(struct diag_request), GFP_KERNEL);
+		if (driver->usb_read_mdm_ptr == NULL)
+			goto err;
+#ifdef CONFIG_DIAG_OVER_USB
+		INIT_WORK(&(driver->diag_read_mdm_work), diag_read_mdm_work_fn);
+#endif
+		INIT_WORK(&(driver->diag_read_hsic_work),
+						 diag_read_hsic_work_fn);
+		driver->hsic_device_enabled = 1;
+	}
+
+	/*
+	 * The probe function was called after the usb was connected
+	 * on the legacy channel OR ODL is turned on. Communication over usb
+	 * mdm and hsic needs to be turned on.
+	 */
+	if (driver->usb_mdm_connected || (driver->logging_mode ==
+							 MEMORY_DEVICE_MODE)) {
+		/* The hsic (diag_bridge) platform device driver is enabled */
+		err = diag_bridge_open(&hsic_diag_bridge_ops);
+		if (err) {
+			pr_err("diag: could not open HSIC, err: %d\n", err);
+			driver->hsic_device_opened = 0;
+			return err;
+		}
+
+		pr_info("diag: opened HSIC channel\n");
+		driver->hsic_device_opened = 1;
+		driver->hsic_ch = 1;
+		driver->in_busy_hsic_write_on_device = 0;
+		driver->in_busy_hsic_read_on_device = 0;
+		driver->in_busy_hsic_write = 0;
+		driver->in_busy_hsic_read = 0;
+
+		if (driver->usb_mdm_connected) {
+			/* Poll USB mdm channel to check for data */
+			queue_work(driver->diag_hsic_wq,
+					 &driver->diag_read_mdm_work);
+		}
+
+		/* Poll HSIC channel to check for data */
+		queue_work(driver->diag_hsic_wq, &driver->diag_read_hsic_work);
+	}
+
+	return err;
+err:
+	pr_err("DIAG could not initialize buf for HSIC\n");
+	kfree(driver->buf_in_hsic);
+	kfree(driver->usb_buf_mdm_out);
+	kfree(driver->write_ptr_mdm);
+	kfree(driver->usb_read_mdm_ptr);
+	if (driver->diag_hsic_wq)
+		destroy_workqueue(driver->diag_hsic_wq);
+
+	return -ENOMEM;
+}
+
+static int diag_hsic_remove(struct platform_device *pdev)
+{
+	pr_debug("DIAG: %s called\n", __func__);
+	diag_hsic_close();
+	return 0;
+}
+
+static int diagfwd_hsic_runtime_suspend(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: suspending...\n");
+	return 0;
+}
+
+static int diagfwd_hsic_runtime_resume(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: resuming...\n");
+	return 0;
+}
+
+static const struct dev_pm_ops diagfwd_hsic_dev_pm_ops = {
+	.runtime_suspend = diagfwd_hsic_runtime_suspend,
+	.runtime_resume = diagfwd_hsic_runtime_resume,
+};
+
+static struct platform_driver msm_hsic_ch_driver = {
+	.probe = diag_hsic_probe,
+	.remove = diag_hsic_remove,
+	.driver = {
+		   .name = "diag_bridge",
+		   .owner = THIS_MODULE,
+		   .pm   = &diagfwd_hsic_dev_pm_ops,
+		   },
+};
+
+void diagfwd_hsic_init(void)
+{
+	int ret;
+
+	pr_debug("DIAG in %s\n", __func__);
+
+	driver->diag_hsic_wq = create_singlethread_workqueue("diag_hsic_wq");
+	INIT_WORK(&(driver->diag_disconnect_work), diag_disconnect_work_fn);
+	INIT_WORK(&(driver->diag_usb_read_complete_work),
+			diag_usb_read_complete_fn);
+
+#ifdef CONFIG_DIAG_OVER_USB
+	driver->mdm_ch = usb_diag_open(DIAG_MDM, driver, diagfwd_hsic_notifier);
+	if (IS_ERR(driver->mdm_ch)) {
+		pr_err("DIAG Unable to open USB diag MDM channel\n");
+		goto err;
+	}
+#endif
+	ret = platform_driver_register(&msm_hsic_ch_driver);
+	if (ret)
+		pr_err("DIAG could not register HSIC device, ret: %d\n", ret);
+	else
+		driver->hsic_initialized = 1;
+
+	return;
+err:
+	pr_err("DIAG could not initialize for HSIC execution\n");
+}
+
+void diagfwd_hsic_exit(void)
+{
+	pr_debug("DIAG in %s\n", __func__);
+
+	if (driver->hsic_initialized)
+		diag_hsic_close();
+
+#ifdef CONFIG_DIAG_OVER_USB
+	if (driver->usb_mdm_connected)
+		usb_diag_free_req(driver->mdm_ch);
+#endif
+	platform_driver_unregister(&msm_hsic_ch_driver);
+#ifdef CONFIG_DIAG_OVER_USB
+	usb_diag_close(driver->mdm_ch);
+#endif
+	kfree(driver->buf_in_hsic);
+	kfree(driver->usb_buf_mdm_out);
+	kfree(driver->write_ptr_mdm);
+	kfree(driver->usb_read_mdm_ptr);
+	destroy_workqueue(driver->diag_hsic_wq);
+
+	driver->hsic_device_enabled = 0;
+}
diff --git a/drivers/char/diag/diagfwd_hsic.h b/drivers/char/diag/diagfwd_hsic.h
new file mode 100644
index 0000000..a47ee26
--- /dev/null
+++ b/drivers/char/diag/diagfwd_hsic.h
@@ -0,0 +1,27 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef DIAGFWD_HSIC_H
+#define DIAGFWD_HSIC_H
+
+#include <mach/diag_bridge.h>
+#define N_MDM_WRITE	1 /* Upgrade to 2 with ping pong buffer */
+#define N_MDM_READ	1
+
+void __init diagfwd_hsic_init(void);
+int diagfwd_connect_hsic(int);
+int diagfwd_disconnect_hsic(int);
+int diagfwd_write_complete_hsic(void);
+int diagfwd_cancel_hsic(void);
+void diagfwd_hsic_exit(void);
+
+#endif
diff --git a/drivers/char/diag/diagfwd_sdio.c b/drivers/char/diag/diagfwd_sdio.c
new file mode 100644
index 0000000..a145c06
--- /dev/null
+++ b/drivers/char/diag/diagfwd_sdio.c
@@ -0,0 +1,296 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/uaccess.h>
+#include <linux/diagchar.h>
+#include <linux/sched.h>
+#include <linux/err.h>
+#include <linux/workqueue.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <asm/current.h>
+#ifdef CONFIG_DIAG_OVER_USB
+#include <mach/usbdiag.h>
+#endif
+#include "diagchar_hdlc.h"
+#include "diagmem.h"
+#include "diagchar.h"
+#include "diagfwd.h"
+#include "diagfwd_sdio.h"
+
+void __diag_sdio_send_req(void)
+{
+	int r = 0;
+	void *buf = driver->buf_in_sdio;
+
+	if (driver->sdio_ch && (!driver->in_busy_sdio)) {
+		r = sdio_read_avail(driver->sdio_ch);
+
+		if (r > IN_BUF_SIZE) {
+			if (r < MAX_IN_BUF_SIZE) {
+				pr_err("diag: SDIO sending"
+					  " packets more than %d bytes\n", r);
+				buf = krealloc(buf, r, GFP_KERNEL);
+			} else {
+				pr_err("diag: SDIO sending"
+			  " in packets more than %d bytes\n", MAX_IN_BUF_SIZE);
+				return;
+			}
+		}
+		if (r > 0) {
+			if (!buf)
+				printk(KERN_INFO "Out of diagmem for SDIO\n");
+			else {
+				APPEND_DEBUG('i');
+				sdio_read(driver->sdio_ch, buf, r);
+				if (((!driver->usb_connected) && (driver->
+					logging_mode == USB_MODE)) || (driver->
+					logging_mode == NO_LOGGING_MODE)) {
+					/* Drop the diag payload */
+					driver->in_busy_sdio = 0;
+					return;
+				}
+				APPEND_DEBUG('j');
+				driver->write_ptr_mdm->length = r;
+				driver->in_busy_sdio = 1;
+				diag_device_write(buf, SDIO_DATA,
+						 driver->write_ptr_mdm);
+			}
+		}
+	}
+}
+
+static void diag_read_sdio_work_fn(struct work_struct *work)
+{
+	__diag_sdio_send_req();
+}
+
+static void diag_sdio_notify(void *ctxt, unsigned event)
+{
+	if (event == SDIO_EVENT_DATA_READ_AVAIL)
+		queue_work(driver->diag_sdio_wq,
+				 &(driver->diag_read_sdio_work));
+
+	if (event == SDIO_EVENT_DATA_WRITE_AVAIL)
+		wake_up_interruptible(&driver->wait_q);
+}
+
+static int diag_sdio_close(void)
+{
+	queue_work(driver->diag_sdio_wq, &(driver->diag_close_sdio_work));
+	return 0;
+}
+
+static void diag_close_sdio_work_fn(struct work_struct *work)
+{
+	pr_debug("diag: sdio close called\n");
+	if (sdio_close(driver->sdio_ch))
+		pr_err("diag: could not close SDIO channel\n");
+	else
+		driver->sdio_ch = NULL; /* channel successfully closed */
+}
+
+int diagfwd_connect_sdio(void)
+{
+	int err;
+
+	err = usb_diag_alloc_req(driver->mdm_ch, N_MDM_WRITE,
+							 N_MDM_READ);
+	if (err)
+		pr_err("diag: unable to alloc USB req on mdm ch\n");
+
+	driver->in_busy_sdio = 0;
+	if (!driver->sdio_ch) {
+		err = sdio_open("SDIO_DIAG", &driver->sdio_ch, driver,
+							 diag_sdio_notify);
+		if (err)
+			pr_info("diag: could not open SDIO channel\n");
+		else
+			pr_info("diag: opened SDIO channel\n");
+	} else {
+		pr_info("diag: SDIO channel already open\n");
+	}
+
+	/* Poll USB channel to check for data*/
+	queue_work(driver->diag_sdio_wq, &(driver->diag_read_mdm_work));
+	/* Poll SDIO channel to check for data*/
+	queue_work(driver->diag_sdio_wq, &(driver->diag_read_sdio_work));
+	return 0;
+}
+
+int diagfwd_disconnect_sdio(void)
+{
+	usb_diag_free_req(driver->mdm_ch);
+	if (driver->sdio_ch && (driver->logging_mode == USB_MODE)) {
+		driver->in_busy_sdio = 1;
+		diag_sdio_close();
+	}
+	return 0;
+}
+
+int diagfwd_write_complete_sdio(void)
+{
+	driver->in_busy_sdio = 0;
+	APPEND_DEBUG('q');
+	queue_work(driver->diag_sdio_wq, &(driver->diag_read_sdio_work));
+	return 0;
+}
+
+int diagfwd_read_complete_sdio(void)
+{
+	queue_work(driver->diag_sdio_wq, &(driver->diag_read_mdm_work));
+	return 0;
+}
+
+void diag_read_mdm_work_fn(struct work_struct *work)
+{
+	if (driver->sdio_ch) {
+		wait_event_interruptible(driver->wait_q, ((sdio_write_avail
+			(driver->sdio_ch) >= driver->read_len_mdm) ||
+				 !(driver->sdio_ch)));
+		if (!(driver->sdio_ch)) {
+			pr_alert("diag: sdio channel not valid");
+			return;
+		}
+		if (driver->sdio_ch && driver->usb_buf_mdm_out &&
+						 (driver->read_len_mdm > 0))
+			sdio_write(driver->sdio_ch, driver->usb_buf_mdm_out,
+							 driver->read_len_mdm);
+		APPEND_DEBUG('x');
+		driver->usb_read_mdm_ptr->buf = driver->usb_buf_mdm_out;
+		driver->usb_read_mdm_ptr->length = USB_MAX_OUT_BUF;
+		usb_diag_read(driver->mdm_ch, driver->usb_read_mdm_ptr);
+		APPEND_DEBUG('y');
+	}
+}
+
+static int diag_sdio_probe(struct platform_device *pdev)
+{
+	int err;
+
+	err = sdio_open("SDIO_DIAG", &driver->sdio_ch, driver,
+							 diag_sdio_notify);
+	if (err)
+		printk(KERN_INFO "DIAG could not open SDIO channel");
+	else {
+		printk(KERN_INFO "DIAG opened SDIO channel");
+		queue_work(driver->diag_sdio_wq, &(driver->diag_read_mdm_work));
+	}
+
+	return err;
+}
+
+static int diag_sdio_remove(struct platform_device *pdev)
+{
+	pr_debug("\n diag: sdio remove called");
+	/* Disable SDIO channel to prevent further read/write */
+	driver->sdio_ch = NULL;
+	return 0;
+}
+
+static int diagfwd_sdio_runtime_suspend(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: suspending...\n");
+	return 0;
+}
+
+static int diagfwd_sdio_runtime_resume(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: resuming...\n");
+	return 0;
+}
+
+static const struct dev_pm_ops diagfwd_sdio_dev_pm_ops = {
+	.runtime_suspend = diagfwd_sdio_runtime_suspend,
+	.runtime_resume = diagfwd_sdio_runtime_resume,
+};
+
+static struct platform_driver msm_sdio_ch_driver = {
+	.probe = diag_sdio_probe,
+	.remove = diag_sdio_remove,
+	.driver = {
+		   .name = "SDIO_DIAG",
+		   .owner = THIS_MODULE,
+		   .pm   = &diagfwd_sdio_dev_pm_ops,
+		   },
+};
+
+void diagfwd_sdio_init(void)
+{
+	int ret;
+
+	driver->read_len_mdm = 0;
+	if (driver->buf_in_sdio == NULL)
+		driver->buf_in_sdio = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
+		if (driver->buf_in_sdio == NULL)
+			goto err;
+	if (driver->usb_buf_mdm_out  == NULL)
+		driver->usb_buf_mdm_out = kzalloc(USB_MAX_OUT_BUF, GFP_KERNEL);
+		if (driver->usb_buf_mdm_out == NULL)
+			goto err;
+	if (driver->write_ptr_mdm == NULL)
+		driver->write_ptr_mdm = kzalloc(
+			sizeof(struct diag_request), GFP_KERNEL);
+		if (driver->write_ptr_mdm == NULL)
+			goto err;
+	if (driver->usb_read_mdm_ptr == NULL)
+		driver->usb_read_mdm_ptr = kzalloc(
+			sizeof(struct diag_request), GFP_KERNEL);
+		if (driver->usb_read_mdm_ptr == NULL)
+			goto err;
+	driver->diag_sdio_wq = create_singlethread_workqueue("diag_sdio_wq");
+#ifdef CONFIG_DIAG_OVER_USB
+	driver->mdm_ch = usb_diag_open(DIAG_MDM, driver,
+			diag_usb_legacy_notifier);
+	if (IS_ERR(driver->mdm_ch)) {
+		printk(KERN_ERR "Unable to open USB diag MDM channel\n");
+		goto err;
+	}
+	INIT_WORK(&(driver->diag_read_mdm_work), diag_read_mdm_work_fn);
+#endif
+	INIT_WORK(&(driver->diag_read_sdio_work), diag_read_sdio_work_fn);
+	INIT_WORK(&(driver->diag_close_sdio_work), diag_close_sdio_work_fn);
+	ret = platform_driver_register(&msm_sdio_ch_driver);
+	if (ret)
+		printk(KERN_INFO "DIAG could not register SDIO device");
+	else
+		printk(KERN_INFO "DIAG registered SDIO device");
+
+	return;
+err:
+		printk(KERN_INFO "\n Could not initialize diag buf for SDIO");
+		kfree(driver->buf_in_sdio);
+		kfree(driver->usb_buf_mdm_out);
+		kfree(driver->write_ptr_mdm);
+		kfree(driver->usb_read_mdm_ptr);
+		if (driver->diag_sdio_wq)
+			destroy_workqueue(driver->diag_sdio_wq);
+}
+
+void diagfwd_sdio_exit(void)
+{
+#ifdef CONFIG_DIAG_OVER_USB
+	if (driver->usb_connected)
+		usb_diag_free_req(driver->mdm_ch);
+#endif
+	platform_driver_unregister(&msm_sdio_ch_driver);
+#ifdef CONFIG_DIAG_OVER_USB
+	usb_diag_close(driver->mdm_ch);
+#endif
+	kfree(driver->buf_in_sdio);
+	kfree(driver->usb_buf_mdm_out);
+	kfree(driver->write_ptr_mdm);
+	kfree(driver->usb_read_mdm_ptr);
+	destroy_workqueue(driver->diag_sdio_wq);
+}
diff --git a/drivers/char/diag/diagfwd_sdio.h b/drivers/char/diag/diagfwd_sdio.h
new file mode 100644
index 0000000..40982c3
--- /dev/null
+++ b/drivers/char/diag/diagfwd_sdio.h
@@ -0,0 +1,27 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef DIAGFWD_SDIO_H
+#define DIAGFWD_SDIO_H
+
+#include <mach/sdio_al.h>
+#define N_MDM_WRITE	1 /* Upgrade to 2 with ping pong buffer */
+#define N_MDM_READ	1
+
+void diagfwd_sdio_init(void);
+void diagfwd_sdio_exit(void);
+int diagfwd_connect_sdio(void);
+int diagfwd_disconnect_sdio(void);
+int diagfwd_read_complete_sdio(void);
+int diagfwd_write_complete_sdio(void);
+
+#endif
diff --git a/drivers/char/diag/diagmem.c b/drivers/char/diag/diagmem.c
new file mode 100644
index 0000000..0b5c27a
--- /dev/null
+++ b/drivers/char/diag/diagmem.c
@@ -0,0 +1,145 @@
+/* Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mempool.h>
+#include <linux/mutex.h>
+#include <asm/atomic.h>
+#include "diagchar.h"
+
+void *diagmem_alloc(struct diagchar_dev *driver, int size, int pool_type)
+{
+	void *buf = NULL;
+
+	if (pool_type == POOL_TYPE_COPY) {
+		if (driver->diagpool) {
+			mutex_lock(&driver->diagmem_mutex);
+			if (driver->count < driver->poolsize) {
+				atomic_add(1, (atomic_t *)&driver->count);
+				buf = mempool_alloc(driver->diagpool,
+								 GFP_ATOMIC);
+			}
+			mutex_unlock(&driver->diagmem_mutex);
+		}
+	} else if (pool_type == POOL_TYPE_HDLC) {
+		if (driver->diag_hdlc_pool) {
+			if (driver->count_hdlc_pool < driver->poolsize_hdlc) {
+				atomic_add(1,
+					 (atomic_t *)&driver->count_hdlc_pool);
+				buf = mempool_alloc(driver->diag_hdlc_pool,
+								 GFP_ATOMIC);
+			}
+		}
+	} else if (pool_type == POOL_TYPE_WRITE_STRUCT) {
+		if (driver->diag_write_struct_pool) {
+			if (driver->count_write_struct_pool <
+					 driver->poolsize_write_struct) {
+				atomic_add(1,
+				 (atomic_t *)&driver->count_write_struct_pool);
+				buf = mempool_alloc(
+				driver->diag_write_struct_pool, GFP_ATOMIC);
+			}
+		}
+	}
+	return buf;
+}
+
+void diagmem_exit(struct diagchar_dev *driver, int pool_type)
+{
+	if (driver->diagpool) {
+		if (driver->count == 0 && driver->ref_count == 0) {
+			mempool_destroy(driver->diagpool);
+			driver->diagpool = NULL;
+		} else if (driver->ref_count == 0 && pool_type == POOL_TYPE_ALL)
+			printk(KERN_ALERT "Unable to destroy COPY mempool");
+		}
+
+	if (driver->diag_hdlc_pool) {
+		if (driver->count_hdlc_pool == 0 && driver->ref_count == 0) {
+			mempool_destroy(driver->diag_hdlc_pool);
+			driver->diag_hdlc_pool = NULL;
+		} else if (driver->ref_count == 0 && pool_type == POOL_TYPE_ALL)
+			printk(KERN_ALERT "Unable to destroy HDLC mempool");
+		}
+
+	if (driver->diag_write_struct_pool) {
+		/* Free up struct pool ONLY if there are no outstanding
+		transactions(aggregation buffer) with USB */
+		if (driver->count_write_struct_pool == 0 &&
+		 driver->count_hdlc_pool == 0 && driver->ref_count == 0) {
+			mempool_destroy(driver->diag_write_struct_pool);
+			driver->diag_write_struct_pool = NULL;
+		} else if (driver->ref_count == 0 && pool_type == POOL_TYPE_ALL)
+			printk(KERN_ALERT "Unable to destroy STRUCT mempool");
+		}
+}
+
+void diagmem_free(struct diagchar_dev *driver, void *buf, int pool_type)
+{
+	if (pool_type == POOL_TYPE_COPY) {
+		if (driver->diagpool != NULL && driver->count > 0) {
+			mempool_free(buf, driver->diagpool);
+			atomic_add(-1, (atomic_t *)&driver->count);
+		} else
+			pr_err("diag: Attempt to free up DIAG driver "
+	       "mempool memory which is already free %d", driver->count);
+	} else if (pool_type == POOL_TYPE_HDLC) {
+		if (driver->diag_hdlc_pool != NULL &&
+			 driver->count_hdlc_pool > 0) {
+			mempool_free(buf, driver->diag_hdlc_pool);
+			atomic_add(-1, (atomic_t *)&driver->count_hdlc_pool);
+		} else
+			pr_err("diag: Attempt to free up DIAG driver "
+	"HDLC mempool which is already free %d ", driver->count_hdlc_pool);
+	} else if (pool_type == POOL_TYPE_WRITE_STRUCT) {
+		if (driver->diag_write_struct_pool != NULL &&
+			 driver->count_write_struct_pool > 0) {
+			mempool_free(buf, driver->diag_write_struct_pool);
+			atomic_add(-1,
+				 (atomic_t *)&driver->count_write_struct_pool);
+		} else
+			pr_err("diag: Attempt to free up DIAG driver "
+			   "USB structure mempool which is already free %d ",
+				    driver->count_write_struct_pool);
+	}
+
+	diagmem_exit(driver, pool_type);
+}
+
+void diagmem_init(struct diagchar_dev *driver)
+{
+	mutex_init(&driver->diagmem_mutex);
+
+	if (driver->count == 0)
+		driver->diagpool = mempool_create_kmalloc_pool(
+					driver->poolsize, driver->itemsize);
+
+	if (driver->count_hdlc_pool == 0)
+		driver->diag_hdlc_pool = mempool_create_kmalloc_pool(
+				driver->poolsize_hdlc, driver->itemsize_hdlc);
+
+	if (driver->count_write_struct_pool == 0)
+		driver->diag_write_struct_pool = mempool_create_kmalloc_pool(
+		driver->poolsize_write_struct, driver->itemsize_write_struct);
+
+	if (!driver->diagpool)
+		printk(KERN_INFO "Cannot allocate diag mempool\n");
+
+	if (!driver->diag_hdlc_pool)
+		printk(KERN_INFO "Cannot allocate diag HDLC mempool\n");
+
+	if (!driver->diag_write_struct_pool)
+		printk(KERN_INFO "Cannot allocate diag USB struct mempool\n");
+}
+
diff --git a/drivers/char/diag/diagmem.h b/drivers/char/diag/diagmem.h
new file mode 100644
index 0000000..43829ae
--- /dev/null
+++ b/drivers/char/diag/diagmem.h
@@ -0,0 +1,22 @@
+/* Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef DIAGMEM_H
+#define DIAGMEM_H
+#include "diagchar.h"
+
+void *diagmem_alloc(struct diagchar_dev *driver, int size, int pool_type);
+void diagmem_free(struct diagchar_dev *driver, void *buf, int pool_type);
+void diagmem_init(struct diagchar_dev *driver);
+void diagmem_exit(struct diagchar_dev *driver, int pool_type);
+
+#endif
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig
index 0689bf6..68616b8 100644
--- a/drivers/char/hw_random/Kconfig
+++ b/drivers/char/hw_random/Kconfig
@@ -250,3 +250,16 @@
 	  (check your distro, or download from
 	  http://sourceforge.net/projects/gkernel/).  rngd periodically reads
 	  /dev/hwrng and injects the entropy into /dev/random.
+
+config HW_RANDOM_MSM
+	tristate "Qualcomm MSM Random Number Generator support"
+	depends on HW_RANDOM && ARCH_MSM
+	default n
+	---help---
+	  This driver provides kernel-side support for the Random Number
+	  Generator hardware found on Qualcomm MSM SoCs.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called msm_rng.
+
+	  If unsure, say Y.
diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile
index b2ff526..c24305d 100644
--- a/drivers/char/hw_random/Makefile
+++ b/drivers/char/hw_random/Makefile
@@ -22,3 +22,4 @@
 obj-$(CONFIG_HW_RANDOM_NOMADIK) += nomadik-rng.o
 obj-$(CONFIG_HW_RANDOM_PICOXCELL) += picoxcell-rng.o
 obj-$(CONFIG_HW_RANDOM_PPC4XX) += ppc4xx-rng.o
+obj-$(CONFIG_HW_RANDOM_MSM) += msm_rng.o
diff --git a/drivers/char/hw_random/msm_rng.c b/drivers/char/hw_random/msm_rng.c
new file mode 100644
index 0000000..7e6670d
--- /dev/null
+++ b/drivers/char/hw_random/msm_rng.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/hw_random.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/types.h>
+#include <mach/msm_iomap.h>
+#include <mach/socinfo.h>
+
+#define DRIVER_NAME "msm_rng"
+
+/* Device specific register offsets */
+#define PRNG_DATA_OUT_OFFSET    0x0000
+#define PRNG_STATUS_OFFSET	0x0004
+#define PRNG_LFSR_CFG_OFFSET	0x0100
+#define PRNG_CONFIG_OFFSET	0x0104
+
+/* Device specific register masks and config values */
+#define PRNG_LFSR_CFG_MASK	0xFFFF0000
+#define PRNG_LFSR_CFG_CLOCKS	0x0000DDDD
+#define PRNG_CONFIG_MASK	0xFFFFFFFD
+#define PRNG_HW_ENABLE		0x00000002
+
+#define MAX_HW_FIFO_DEPTH 16                     /* FIFO is 16 words deep */
+#define MAX_HW_FIFO_SIZE (MAX_HW_FIFO_DEPTH * 4) /* FIFO is 32 bits wide  */
+
+
+struct msm_rng_device {
+	struct platform_device *pdev;
+	void __iomem *base;
+	struct clk *prng_clk;
+};
+
+static int msm_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
+{
+	struct msm_rng_device *msm_rng_dev;
+	struct platform_device *pdev;
+	void __iomem *base;
+	size_t maxsize;
+	size_t currsize = 0;
+	unsigned long val;
+	unsigned long *retdata = data;
+	int ret;
+
+	msm_rng_dev = (struct msm_rng_device *)rng->priv;
+	pdev = msm_rng_dev->pdev;
+	base = msm_rng_dev->base;
+
+	/* calculate max size bytes to transfer back to caller */
+	maxsize = min_t(size_t, MAX_HW_FIFO_SIZE, max);
+
+	/* no room for word data */
+	if (maxsize < 4)
+		return 0;
+
+	/* enable PRNG clock */
+	ret = clk_prepare_enable(msm_rng_dev->prng_clk);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to enable clock in callback\n");
+		return 0;
+	}
+
+	/* read random data from h/w */
+	do {
+		/* check status bit if data is available */
+		if (!(readl_relaxed(base + PRNG_STATUS_OFFSET) & 0x00000001))
+			break;	/* no data to read so just bail */
+
+		/* read FIFO */
+		val = readl_relaxed(base + PRNG_DATA_OUT_OFFSET);
+		if (!val)
+			break;	/* no data to read so just bail */
+
+		/* write data back to callers pointer */
+		*(retdata++) = val;
+		currsize += 4;
+
+		/* make sure we stay on 32bit boundary */
+		if ((maxsize - currsize) < 4)
+			break;
+	} while (currsize < maxsize);
+
+	/* vote to turn off clock */
+	clk_disable_unprepare(msm_rng_dev->prng_clk);
+
+	return currsize;
+}
+
+static struct hwrng msm_rng = {
+	.name = DRIVER_NAME,
+	.read = msm_rng_read,
+};
+
+static int __devinit msm_rng_enable_hw(struct msm_rng_device *msm_rng_dev)
+{
+	unsigned long val = 0;
+	unsigned long reg_val = 0;
+	int ret = 0;
+
+	/* Enable the PRNG CLK */
+	ret = clk_prepare_enable(msm_rng_dev->prng_clk);
+	if (ret) {
+		dev_err(&(msm_rng_dev->pdev)->dev,
+				"failed to enable clock in probe\n");
+		return -EPERM;
+	}
+	/* Enable PRNG h/w only if it is NOT ON */
+	val = readl_relaxed(msm_rng_dev->base + PRNG_CONFIG_OFFSET) &
+					PRNG_HW_ENABLE;
+	/* PRNG H/W is not ON */
+	if (val != PRNG_HW_ENABLE) {
+		val = readl_relaxed(msm_rng_dev->base + PRNG_LFSR_CFG_OFFSET) &
+					PRNG_LFSR_CFG_MASK;
+		val |= PRNG_LFSR_CFG_MASK;
+		writel_relaxed(val, msm_rng_dev->base + PRNG_LFSR_CFG_OFFSET);
+
+		/* The PRNG CONFIG register should be first written */
+		mb();
+
+		reg_val = readl_relaxed(msm_rng_dev->base + PRNG_CONFIG_OFFSET)
+						& PRNG_CONFIG_MASK;
+		reg_val |= PRNG_HW_ENABLE;
+		writel_relaxed(reg_val, msm_rng_dev->base + PRNG_CONFIG_OFFSET);
+
+		/* The PRNG clk should be disabled only after we enable the
+		* PRNG h/w by writing to the PRNG CONFIG register.
+		*/
+		mb();
+	}
+
+	clk_disable_unprepare(msm_rng_dev->prng_clk);
+
+	return 0;
+}
+
+static int __devinit msm_rng_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct msm_rng_device *msm_rng_dev = NULL;
+	void __iomem *base = NULL;
+	int error = 0;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "invalid address\n");
+		error = -EFAULT;
+		goto err_exit;
+	}
+
+	msm_rng_dev = kzalloc(sizeof(msm_rng_dev), GFP_KERNEL);
+	if (!msm_rng_dev) {
+		dev_err(&pdev->dev, "cannot allocate memory\n");
+		error = -ENOMEM;
+		goto err_exit;
+	}
+
+	base = ioremap(res->start, resource_size(res));
+	if (!base) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		error = -ENOMEM;
+		goto err_iomap;
+	}
+	msm_rng_dev->base = base;
+
+	/* create a handle for clock control */
+	msm_rng_dev->prng_clk = clk_get(&pdev->dev, "core_clk");
+	if (IS_ERR(msm_rng_dev->prng_clk)) {
+		dev_err(&pdev->dev, "failed to register clock source\n");
+		error = -EPERM;
+		goto err_clk_get;
+	}
+
+	/* save away pdev and register driver data */
+	msm_rng_dev->pdev = pdev;
+	platform_set_drvdata(pdev, msm_rng_dev);
+
+	/* Enable rng h/w */
+	error = msm_rng_enable_hw(msm_rng_dev);
+
+	if (error)
+		goto rollback_clk;
+
+	/* register with hwrng framework */
+	msm_rng.priv = (unsigned long) msm_rng_dev;
+	error = hwrng_register(&msm_rng);
+	if (error) {
+		dev_err(&pdev->dev, "failed to register hwrng\n");
+		error = -EPERM;
+		goto rollback_clk;
+	}
+
+	return 0;
+
+rollback_clk:
+	clk_put(msm_rng_dev->prng_clk);
+err_clk_get:
+	iounmap(msm_rng_dev->base);
+err_iomap:
+	kfree(msm_rng_dev);
+err_exit:
+	return error;
+}
+
+static int __devexit msm_rng_remove(struct platform_device *pdev)
+{
+	struct msm_rng_device *msm_rng_dev = platform_get_drvdata(pdev);
+
+	hwrng_unregister(&msm_rng);
+	clk_put(msm_rng_dev->prng_clk);
+	iounmap(msm_rng_dev->base);
+	platform_set_drvdata(pdev, NULL);
+	kfree(msm_rng_dev);
+	return 0;
+}
+
+static struct of_device_id qrng_match[] = {
+	{	.compatible = "qcom,msm-rng",
+	},
+	{}
+};
+
+static struct platform_driver rng_driver = {
+	.probe      = msm_rng_probe,
+	.remove     = __devexit_p(msm_rng_remove),
+	.driver     = {
+		.name   = DRIVER_NAME,
+		.owner  = THIS_MODULE,
+		.of_match_table = qrng_match,
+	}
+};
+
+static int __init msm_rng_init(void)
+{
+	return platform_driver_register(&rng_driver);
+}
+
+module_init(msm_rng_init);
+
+static void __exit msm_rng_exit(void)
+{
+	platform_driver_unregister(&rng_driver);
+}
+
+module_exit(msm_rng_exit);
+
+MODULE_AUTHOR("Code Aurora Forum");
+MODULE_DESCRIPTION("Qualcomm MSM Random Number Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/char/msm_rotator.c b/drivers/char/msm_rotator.c
new file mode 100644
index 0000000..6cd1806
--- /dev/null
+++ b/drivers/char/msm_rotator.c
@@ -0,0 +1,1802 @@
+/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/platform_device.h>
+#include <linux/cdev.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include <linux/clk.h>
+#include <linux/android_pmem.h>
+#include <linux/msm_rotator.h>
+#include <linux/io.h>
+#include <mach/msm_rotator_imem.h>
+#include <linux/ktime.h>
+#include <linux/workqueue.h>
+#include <linux/file.h>
+#include <linux/major.h>
+#include <linux/regulator/consumer.h>
+#include <linux/ion.h>
+#ifdef CONFIG_MSM_BUS_SCALING
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
+#endif
+#include <mach/msm_subsystem_map.h>
+#include <mach/iommu_domains.h>
+
+#define DRIVER_NAME "msm_rotator"
+
+#define MSM_ROTATOR_BASE (msm_rotator_dev->io_base)
+#define MSM_ROTATOR_INTR_ENABLE			(MSM_ROTATOR_BASE+0x0020)
+#define MSM_ROTATOR_INTR_STATUS			(MSM_ROTATOR_BASE+0x0024)
+#define MSM_ROTATOR_INTR_CLEAR			(MSM_ROTATOR_BASE+0x0028)
+#define MSM_ROTATOR_START			(MSM_ROTATOR_BASE+0x0030)
+#define MSM_ROTATOR_MAX_BURST_SIZE		(MSM_ROTATOR_BASE+0x0050)
+#define MSM_ROTATOR_HW_VERSION			(MSM_ROTATOR_BASE+0x0070)
+#define MSM_ROTATOR_SW_RESET			(MSM_ROTATOR_BASE+0x0074)
+#define MSM_ROTATOR_SRC_SIZE			(MSM_ROTATOR_BASE+0x1108)
+#define MSM_ROTATOR_SRCP0_ADDR			(MSM_ROTATOR_BASE+0x110c)
+#define MSM_ROTATOR_SRCP1_ADDR			(MSM_ROTATOR_BASE+0x1110)
+#define MSM_ROTATOR_SRCP2_ADDR			(MSM_ROTATOR_BASE+0x1114)
+#define MSM_ROTATOR_SRC_YSTRIDE1		(MSM_ROTATOR_BASE+0x111c)
+#define MSM_ROTATOR_SRC_YSTRIDE2		(MSM_ROTATOR_BASE+0x1120)
+#define MSM_ROTATOR_SRC_FORMAT			(MSM_ROTATOR_BASE+0x1124)
+#define MSM_ROTATOR_SRC_UNPACK_PATTERN1		(MSM_ROTATOR_BASE+0x1128)
+#define MSM_ROTATOR_SUB_BLOCK_CFG		(MSM_ROTATOR_BASE+0x1138)
+#define MSM_ROTATOR_OUT_PACK_PATTERN1		(MSM_ROTATOR_BASE+0x1154)
+#define MSM_ROTATOR_OUTP0_ADDR			(MSM_ROTATOR_BASE+0x1168)
+#define MSM_ROTATOR_OUTP1_ADDR			(MSM_ROTATOR_BASE+0x116c)
+#define MSM_ROTATOR_OUTP2_ADDR			(MSM_ROTATOR_BASE+0x1170)
+#define MSM_ROTATOR_OUT_YSTRIDE1		(MSM_ROTATOR_BASE+0x1178)
+#define MSM_ROTATOR_OUT_YSTRIDE2		(MSM_ROTATOR_BASE+0x117c)
+#define MSM_ROTATOR_SRC_XY			(MSM_ROTATOR_BASE+0x1200)
+#define MSM_ROTATOR_SRC_IMAGE_SIZE		(MSM_ROTATOR_BASE+0x1208)
+
+#define MSM_ROTATOR_MAX_ROT	0x07
+#define MSM_ROTATOR_MAX_H	0x1fff
+#define MSM_ROTATOR_MAX_W	0x1fff
+
+/* from lsb to msb */
+#define GET_PACK_PATTERN(a, x, y, z, bit) \
+			(((a)<<((bit)*3))|((x)<<((bit)*2))|((y)<<(bit))|(z))
+#define CLR_G 0x0
+#define CLR_B 0x1
+#define CLR_R 0x2
+#define CLR_ALPHA 0x3
+
+#define CLR_Y  CLR_G
+#define CLR_CB CLR_B
+#define CLR_CR CLR_R
+
+#define ROTATIONS_TO_BITMASK(r) ((((r) & MDP_ROT_90) ? 1 : 0)  | \
+				 (((r) & MDP_FLIP_LR) ? 2 : 0) | \
+				 (((r) & MDP_FLIP_UD) ? 4 : 0))
+
+#define IMEM_NO_OWNER -1;
+
+#define MAX_SESSIONS 16
+#define INVALID_SESSION -1
+#define VERSION_KEY_MASK 0xFFFFFF00
+#define MAX_DOWNSCALE_RATIO 3
+
+#define ROTATOR_REVISION_V0		0
+#define ROTATOR_REVISION_V1		1
+#define ROTATOR_REVISION_V2		2
+#define ROTATOR_REVISION_NONE	0xffffffff
+
+uint32_t rotator_hw_revision;
+
+/*
+ * rotator_hw_revision:
+ * 0 == 7x30
+ * 1 == 8x60
+ * 2 == 8960
+ *
+ */
+struct tile_parm {
+	unsigned int width;  /* tile's width */
+	unsigned int height; /* tile's height */
+	unsigned int row_tile_w; /* tiles per row's width */
+	unsigned int row_tile_h; /* tiles per row's height */
+};
+
+struct msm_rotator_mem_planes {
+	unsigned int num_planes;
+	unsigned int plane_size[4];
+	unsigned int total_size;
+};
+
+#define checkoffset(offset, size, max_size) \
+	((size) > (max_size) || (offset) > ((max_size) - (size)))
+
+struct msm_rotator_fd_info {
+	int pid;
+	int ref_cnt;
+	struct list_head list;
+};
+
+struct msm_rotator_dev {
+	void __iomem *io_base;
+	int irq;
+	struct msm_rotator_img_info *img_info[MAX_SESSIONS];
+	struct clk *core_clk;
+	struct msm_rotator_fd_info *fd_info[MAX_SESSIONS];
+	struct list_head fd_list;
+	struct clk *pclk;
+	int rot_clk_state;
+	struct regulator *regulator;
+	struct delayed_work rot_clk_work;
+	struct clk *imem_clk;
+	int imem_clk_state;
+	struct delayed_work imem_clk_work;
+	struct platform_device *pdev;
+	struct cdev cdev;
+	struct device *device;
+	struct class *class;
+	dev_t dev_num;
+	int processing;
+	int last_session_idx;
+	struct mutex rotator_lock;
+	struct mutex imem_lock;
+	int imem_owner;
+	wait_queue_head_t wq;
+	struct ion_client *client;
+	#ifdef CONFIG_MSM_BUS_SCALING
+	uint32_t bus_client_handle;
+	#endif
+};
+
+#define COMPONENT_5BITS 1
+#define COMPONENT_6BITS 2
+#define COMPONENT_8BITS 3
+
+static struct msm_rotator_dev *msm_rotator_dev;
+
+enum {
+	CLK_EN,
+	CLK_DIS,
+	CLK_SUSPEND,
+};
+
+int msm_rotator_iommu_map_buf(int mem_id, unsigned char src,
+	unsigned long *start, unsigned long *len,
+	struct ion_handle **pihdl)
+{
+	if (!msm_rotator_dev->client)
+		return -EINVAL;
+
+	*pihdl = ion_import_fd(msm_rotator_dev->client, mem_id);
+	if (IS_ERR_OR_NULL(*pihdl)) {
+		pr_err("ion_import_fd() failed\n");
+		return PTR_ERR(*pihdl);
+	}
+	pr_debug("%s(): ion_hdl %p, ion_buf %p\n", __func__, *pihdl,
+		ion_share(msm_rotator_dev->client, *pihdl));
+
+	if (ion_map_iommu(msm_rotator_dev->client,
+		*pihdl,	ROTATOR_DOMAIN, GEN_POOL,
+		SZ_4K, 0, start, len, 0, ION_IOMMU_UNMAP_DELAYED)) {
+		pr_err("ion_map_iommu() failed\n");
+		return -EINVAL;
+	}
+
+	pr_debug("%s(): mem_id %d, start 0x%lx, len 0x%lx\n",
+		__func__, mem_id, *start, *len);
+	return 0;
+}
+
+int msm_rotator_imem_allocate(int requestor)
+{
+	int rc = 0;
+
+#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
+	switch (requestor) {
+	case ROTATOR_REQUEST:
+		if (mutex_trylock(&msm_rotator_dev->imem_lock)) {
+			msm_rotator_dev->imem_owner = ROTATOR_REQUEST;
+			rc = 1;
+		} else
+			rc = 0;
+		break;
+	case JPEG_REQUEST:
+		mutex_lock(&msm_rotator_dev->imem_lock);
+		msm_rotator_dev->imem_owner = JPEG_REQUEST;
+		rc = 1;
+		break;
+	default:
+		rc = 0;
+	}
+#else
+	if (requestor == JPEG_REQUEST)
+		rc = 1;
+#endif
+	if (rc == 1) {
+		cancel_delayed_work(&msm_rotator_dev->imem_clk_work);
+		if (msm_rotator_dev->imem_clk_state != CLK_EN
+			&& msm_rotator_dev->imem_clk) {
+			clk_prepare_enable(msm_rotator_dev->imem_clk);
+			msm_rotator_dev->imem_clk_state = CLK_EN;
+		}
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(msm_rotator_imem_allocate);
+
+void msm_rotator_imem_free(int requestor)
+{
+#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
+	if (msm_rotator_dev->imem_owner == requestor) {
+		schedule_delayed_work(&msm_rotator_dev->imem_clk_work, HZ);
+		mutex_unlock(&msm_rotator_dev->imem_lock);
+	}
+#else
+	if (requestor == JPEG_REQUEST)
+		schedule_delayed_work(&msm_rotator_dev->imem_clk_work, HZ);
+#endif
+}
+EXPORT_SYMBOL(msm_rotator_imem_free);
+
+static void msm_rotator_imem_clk_work_f(struct work_struct *work)
+{
+#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
+	if (mutex_trylock(&msm_rotator_dev->imem_lock)) {
+		if (msm_rotator_dev->imem_clk_state == CLK_EN
+		     && msm_rotator_dev->imem_clk) {
+			clk_disable_unprepare(msm_rotator_dev->imem_clk);
+			msm_rotator_dev->imem_clk_state = CLK_DIS;
+		} else if (msm_rotator_dev->imem_clk_state == CLK_SUSPEND)
+			msm_rotator_dev->imem_clk_state = CLK_DIS;
+		mutex_unlock(&msm_rotator_dev->imem_lock);
+	}
+#endif
+}
+
+/* enable clocks needed by rotator block */
+static void enable_rot_clks(void)
+{
+	if (msm_rotator_dev->regulator)
+		regulator_enable(msm_rotator_dev->regulator);
+	if (msm_rotator_dev->core_clk != NULL)
+		clk_prepare_enable(msm_rotator_dev->core_clk);
+	if (msm_rotator_dev->pclk != NULL)
+		clk_prepare_enable(msm_rotator_dev->pclk);
+}
+
+/* disable clocks needed by rotator block */
+static void disable_rot_clks(void)
+{
+	if (msm_rotator_dev->core_clk != NULL)
+		clk_disable_unprepare(msm_rotator_dev->core_clk);
+	if (msm_rotator_dev->pclk != NULL)
+		clk_disable_unprepare(msm_rotator_dev->pclk);
+	if (msm_rotator_dev->regulator)
+		regulator_disable(msm_rotator_dev->regulator);
+}
+
+static void msm_rotator_rot_clk_work_f(struct work_struct *work)
+{
+	if (mutex_trylock(&msm_rotator_dev->rotator_lock)) {
+		if (msm_rotator_dev->rot_clk_state == CLK_EN) {
+			disable_rot_clks();
+			msm_rotator_dev->rot_clk_state = CLK_DIS;
+		} else if (msm_rotator_dev->rot_clk_state == CLK_SUSPEND)
+			msm_rotator_dev->rot_clk_state = CLK_DIS;
+		mutex_unlock(&msm_rotator_dev->rotator_lock);
+	}
+}
+
+static irqreturn_t msm_rotator_isr(int irq, void *dev_id)
+{
+	if (msm_rotator_dev->processing) {
+		msm_rotator_dev->processing = 0;
+		wake_up(&msm_rotator_dev->wq);
+	} else
+		printk(KERN_WARNING "%s: unexpected interrupt\n", DRIVER_NAME);
+
+	return IRQ_HANDLED;
+}
+
+static unsigned int tile_size(unsigned int src_width,
+		unsigned int src_height,
+		const struct tile_parm *tp)
+{
+	unsigned int tile_w, tile_h;
+	unsigned int row_num_w, row_num_h;
+	tile_w = tp->width * tp->row_tile_w;
+	tile_h = tp->height * tp->row_tile_h;
+	row_num_w = (src_width + tile_w - 1) / tile_w;
+	row_num_h = (src_height + tile_h - 1) / tile_h;
+	return ((row_num_w * row_num_h * tile_w * tile_h) + 8191) & ~8191;
+}
+
+static int get_bpp(int format)
+{
+	switch (format) {
+	case MDP_RGB_565:
+	case MDP_BGR_565:
+		return 2;
+
+	case MDP_XRGB_8888:
+	case MDP_ARGB_8888:
+	case MDP_RGBA_8888:
+	case MDP_BGRA_8888:
+	case MDP_RGBX_8888:
+		return 4;
+
+	case MDP_Y_CBCR_H2V2:
+	case MDP_Y_CRCB_H2V2:
+	case MDP_Y_CB_CR_H2V2:
+	case MDP_Y_CR_CB_H2V2:
+	case MDP_Y_CR_CB_GH2V2:
+	case MDP_Y_CRCB_H2V2_TILE:
+	case MDP_Y_CBCR_H2V2_TILE:
+		return 1;
+
+	case MDP_RGB_888:
+	case MDP_YCBCR_H1V1:
+	case MDP_YCRCB_H1V1:
+		return 3;
+
+	case MDP_YCRYCB_H2V1:
+		return 2;/* YCrYCb interleave */
+
+	case MDP_Y_CRCB_H2V1:
+	case MDP_Y_CBCR_H2V1:
+		return 1;
+
+	default:
+		return -1;
+	}
+
+}
+
+static int msm_rotator_get_plane_sizes(uint32_t format,	uint32_t w, uint32_t h,
+				       struct msm_rotator_mem_planes *p)
+{
+	/*
+	 * each row of samsung tile consists of two tiles in height
+	 * and two tiles in width which means width should align to
+	 * 64 x 2 bytes and height should align to 32 x 2 bytes.
+	 * video decoder generate two tiles in width and one tile
+	 * in height which ends up height align to 32 X 1 bytes.
+	 */
+	const struct tile_parm tile = {64, 32, 2, 1};
+	int i;
+
+	if (p == NULL)
+		return -EINVAL;
+
+	if ((w > MSM_ROTATOR_MAX_W) || (h > MSM_ROTATOR_MAX_H))
+		return -ERANGE;
+
+	memset(p, 0, sizeof(*p));
+
+	switch (format) {
+	case MDP_XRGB_8888:
+	case MDP_ARGB_8888:
+	case MDP_RGBA_8888:
+	case MDP_BGRA_8888:
+	case MDP_RGBX_8888:
+	case MDP_RGB_888:
+	case MDP_RGB_565:
+	case MDP_BGR_565:
+	case MDP_YCRYCB_H2V1:
+	case MDP_YCBCR_H1V1:
+	case MDP_YCRCB_H1V1:
+		p->num_planes = 1;
+		p->plane_size[0] = w * h * get_bpp(format);
+		break;
+	case MDP_Y_CRCB_H2V1:
+	case MDP_Y_CBCR_H2V1:
+		p->num_planes = 2;
+		p->plane_size[0] = w * h;
+		p->plane_size[1] = w * h;
+		break;
+	case MDP_Y_CBCR_H2V2:
+	case MDP_Y_CRCB_H2V2:
+		p->num_planes = 2;
+		p->plane_size[0] = w * h;
+		p->plane_size[1] = w * h / 2;
+		break;
+	case MDP_Y_CRCB_H2V2_TILE:
+	case MDP_Y_CBCR_H2V2_TILE:
+		p->num_planes = 2;
+		p->plane_size[0] = tile_size(w, h, &tile);
+		p->plane_size[1] = tile_size(w, h/2, &tile);
+		break;
+	case MDP_Y_CB_CR_H2V2:
+	case MDP_Y_CR_CB_H2V2:
+		p->num_planes = 3;
+		p->plane_size[0] = w * h;
+		p->plane_size[1] = (w / 2) * (h / 2);
+		p->plane_size[2] = (w / 2) * (h / 2);
+		break;
+	case MDP_Y_CR_CB_GH2V2:
+		p->num_planes = 3;
+		p->plane_size[0] = ALIGN(w, 16) * h;
+		p->plane_size[1] = ALIGN(w / 2, 16) * (h / 2);
+		p->plane_size[2] = ALIGN(w / 2, 16) * (h / 2);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	for (i = 0; i < p->num_planes; i++)
+		p->total_size += p->plane_size[i];
+
+	return 0;
+}
+
+static int msm_rotator_ycxcx_h2v1(struct msm_rotator_img_info *info,
+				  unsigned int in_paddr,
+				  unsigned int out_paddr,
+				  unsigned int use_imem,
+				  int new_session,
+				  unsigned int in_chroma_paddr,
+				  unsigned int out_chroma_paddr)
+{
+	int bpp;
+
+	if (info->src.format != info->dst.format)
+		return -EINVAL;
+
+	bpp = get_bpp(info->src.format);
+	if (bpp < 0)
+		return -ENOTTY;
+
+	iowrite32(in_paddr, MSM_ROTATOR_SRCP0_ADDR);
+	iowrite32(in_chroma_paddr, MSM_ROTATOR_SRCP1_ADDR);
+	iowrite32(out_paddr +
+			((info->dst_y * info->dst.width) + info->dst_x),
+		  MSM_ROTATOR_OUTP0_ADDR);
+	iowrite32(out_chroma_paddr +
+			((info->dst_y * info->dst.width) + info->dst_x),
+		  MSM_ROTATOR_OUTP1_ADDR);
+
+	if (new_session) {
+		iowrite32(info->src.width |
+			  info->src.width << 16,
+			  MSM_ROTATOR_SRC_YSTRIDE1);
+		if (info->rotations & MDP_ROT_90)
+			iowrite32(info->dst.width |
+				  info->dst.width*2 << 16,
+				  MSM_ROTATOR_OUT_YSTRIDE1);
+		else
+			iowrite32(info->dst.width |
+				  info->dst.width << 16,
+				  MSM_ROTATOR_OUT_YSTRIDE1);
+		if (info->src.format == MDP_Y_CBCR_H2V1) {
+			iowrite32(GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8),
+				  MSM_ROTATOR_SRC_UNPACK_PATTERN1);
+			iowrite32(GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8),
+				  MSM_ROTATOR_OUT_PACK_PATTERN1);
+		} else {
+			iowrite32(GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
+				  MSM_ROTATOR_SRC_UNPACK_PATTERN1);
+			iowrite32(GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
+				  MSM_ROTATOR_OUT_PACK_PATTERN1);
+		}
+		iowrite32((1  << 18) | 		/* chroma sampling 1=H2V1 */
+			  (ROTATIONS_TO_BITMASK(info->rotations) << 9) |
+			  1 << 8 |			/* ROT_EN */
+			  info->downscale_ratio << 2 |	/* downscale v ratio */
+			  info->downscale_ratio,	/* downscale h ratio */
+			  MSM_ROTATOR_SUB_BLOCK_CFG);
+		iowrite32(0 << 29 | 		/* frame format 0 = linear */
+			  (use_imem ? 0 : 1) << 22 | /* tile size */
+			  2 << 19 | 		/* fetch planes 2 = pseudo */
+			  0 << 18 | 		/* unpack align */
+			  1 << 17 | 		/* unpack tight */
+			  1 << 13 | 		/* unpack count 0=1 component */
+			  (bpp-1) << 9 |	/* src Bpp 0=1 byte ... */
+			  0 << 8  | 		/* has alpha */
+			  0 << 6  | 		/* alpha bits 3=8bits */
+			  3 << 4  | 		/* R/Cr bits 1=5 2=6 3=8 */
+			  3 << 2  | 		/* B/Cb bits 1=5 2=6 3=8 */
+			  3 << 0,   		/* G/Y  bits 1=5 2=6 3=8 */
+			  MSM_ROTATOR_SRC_FORMAT);
+	}
+
+	return 0;
+}
+
+static int msm_rotator_ycxcx_h2v2(struct msm_rotator_img_info *info,
+				  unsigned int in_paddr,
+				  unsigned int out_paddr,
+				  unsigned int use_imem,
+				  int new_session,
+				  unsigned int in_chroma_paddr,
+				  unsigned int out_chroma_paddr,
+				  unsigned int in_chroma2_paddr)
+{
+	uint32_t dst_format;
+	int is_tile = 0;
+
+	switch (info->src.format) {
+	case MDP_Y_CRCB_H2V2_TILE:
+		is_tile = 1;
+	case MDP_Y_CR_CB_H2V2:
+	case MDP_Y_CR_CB_GH2V2:
+	case MDP_Y_CRCB_H2V2:
+		dst_format = MDP_Y_CRCB_H2V2;
+		break;
+	case MDP_Y_CBCR_H2V2_TILE:
+		is_tile = 1;
+	case MDP_Y_CB_CR_H2V2:
+	case MDP_Y_CBCR_H2V2:
+		dst_format = MDP_Y_CBCR_H2V2;
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (info->dst.format  != dst_format)
+		return -EINVAL;
+
+	/* rotator expects YCbCr for planar input format */
+	if ((info->src.format == MDP_Y_CR_CB_H2V2 ||
+	    info->src.format == MDP_Y_CR_CB_GH2V2) &&
+	    rotator_hw_revision < ROTATOR_REVISION_V2)
+		swap(in_chroma_paddr, in_chroma2_paddr);
+
+	iowrite32(in_paddr, MSM_ROTATOR_SRCP0_ADDR);
+	iowrite32(in_chroma_paddr, MSM_ROTATOR_SRCP1_ADDR);
+	iowrite32(in_chroma2_paddr, MSM_ROTATOR_SRCP2_ADDR);
+
+	iowrite32(out_paddr +
+			((info->dst_y * info->dst.width) + info->dst_x),
+		  MSM_ROTATOR_OUTP0_ADDR);
+	iowrite32(out_chroma_paddr +
+			((info->dst_y * info->dst.width)/2 + info->dst_x),
+		  MSM_ROTATOR_OUTP1_ADDR);
+
+	if (new_session) {
+		if (in_chroma2_paddr) {
+			if (info->src.format == MDP_Y_CR_CB_GH2V2) {
+				iowrite32(ALIGN(info->src.width, 16) |
+					ALIGN((info->src.width / 2), 16) << 16,
+					MSM_ROTATOR_SRC_YSTRIDE1);
+				iowrite32(ALIGN((info->src.width / 2), 16),
+					MSM_ROTATOR_SRC_YSTRIDE2);
+			} else {
+				iowrite32(info->src.width |
+					(info->src.width / 2) << 16,
+					MSM_ROTATOR_SRC_YSTRIDE1);
+				iowrite32((info->src.width / 2),
+					MSM_ROTATOR_SRC_YSTRIDE2);
+			}
+		} else {
+			iowrite32(info->src.width |
+					info->src.width << 16,
+					MSM_ROTATOR_SRC_YSTRIDE1);
+		}
+		iowrite32(info->dst.width |
+				info->dst.width << 16,
+				MSM_ROTATOR_OUT_YSTRIDE1);
+
+		if (dst_format == MDP_Y_CBCR_H2V2) {
+			iowrite32(GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8),
+				  MSM_ROTATOR_SRC_UNPACK_PATTERN1);
+			iowrite32(GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8),
+				  MSM_ROTATOR_OUT_PACK_PATTERN1);
+		} else {
+			iowrite32(GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
+				  MSM_ROTATOR_SRC_UNPACK_PATTERN1);
+			iowrite32(GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
+				  MSM_ROTATOR_OUT_PACK_PATTERN1);
+		}
+		iowrite32((3  << 18) | 		/* chroma sampling 3=4:2:0 */
+			  (ROTATIONS_TO_BITMASK(info->rotations) << 9) |
+			  1 << 8 |			/* ROT_EN */
+			  info->downscale_ratio << 2 |	/* downscale v ratio */
+			  info->downscale_ratio,	/* downscale h ratio */
+			  MSM_ROTATOR_SUB_BLOCK_CFG);
+
+		iowrite32((is_tile ? 2 : 0) << 29 |  /* frame format */
+			  (use_imem ? 0 : 1) << 22 | /* tile size */
+			  (in_chroma2_paddr ? 1 : 2) << 19 | /* fetch planes */
+			  0 << 18 | 		/* unpack align */
+			  1 << 17 | 		/* unpack tight */
+			  1 << 13 | 		/* unpack count 0=1 component */
+			  0 << 9  |		/* src Bpp 0=1 byte ... */
+			  0 << 8  | 		/* has alpha */
+			  0 << 6  | 		/* alpha bits 3=8bits */
+			  3 << 4  | 		/* R/Cr bits 1=5 2=6 3=8 */
+			  3 << 2  | 		/* B/Cb bits 1=5 2=6 3=8 */
+			  3 << 0,   		/* G/Y  bits 1=5 2=6 3=8 */
+			  MSM_ROTATOR_SRC_FORMAT);
+	}
+	return 0;
+}
+
+static int msm_rotator_ycrycb(struct msm_rotator_img_info *info,
+			      unsigned int in_paddr,
+			      unsigned int out_paddr,
+			      unsigned int use_imem,
+			      int new_session,
+			      unsigned int out_chroma_paddr)
+{
+	int bpp;
+	uint32_t dst_format;
+
+	if (info->src.format == MDP_YCRYCB_H2V1)
+		dst_format = MDP_Y_CRCB_H2V1;
+	else
+		return -EINVAL;
+
+	if (info->dst.format != dst_format)
+		return -EINVAL;
+
+	bpp = get_bpp(info->src.format);
+	if (bpp < 0)
+		return -ENOTTY;
+
+	iowrite32(in_paddr, MSM_ROTATOR_SRCP0_ADDR);
+	iowrite32(out_paddr +
+			((info->dst_y * info->dst.width) + info->dst_x),
+		  MSM_ROTATOR_OUTP0_ADDR);
+	iowrite32(out_chroma_paddr +
+			((info->dst_y * info->dst.width)/2 + info->dst_x),
+		  MSM_ROTATOR_OUTP1_ADDR);
+
+	if (new_session) {
+		iowrite32(info->src.width * bpp,
+			  MSM_ROTATOR_SRC_YSTRIDE1);
+		if (info->rotations & MDP_ROT_90)
+			iowrite32(info->dst.width |
+				  (info->dst.width*2) << 16,
+				  MSM_ROTATOR_OUT_YSTRIDE1);
+		else
+			iowrite32(info->dst.width |
+				  (info->dst.width) << 16,
+				  MSM_ROTATOR_OUT_YSTRIDE1);
+
+		iowrite32(GET_PACK_PATTERN(CLR_Y, CLR_CR, CLR_Y, CLR_CB, 8),
+			  MSM_ROTATOR_SRC_UNPACK_PATTERN1);
+		iowrite32(GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
+			  MSM_ROTATOR_OUT_PACK_PATTERN1);
+		iowrite32((1  << 18) | 		/* chroma sampling 1=H2V1 */
+			  (ROTATIONS_TO_BITMASK(info->rotations) << 9) |
+			  1 << 8 |			/* ROT_EN */
+			  info->downscale_ratio << 2 |	/* downscale v ratio */
+			  info->downscale_ratio,	/* downscale h ratio */
+			  MSM_ROTATOR_SUB_BLOCK_CFG);
+		iowrite32(0 << 29 | 		/* frame format 0 = linear */
+			  (use_imem ? 0 : 1) << 22 | /* tile size */
+			  0 << 19 | 		/* fetch planes 0=interleaved */
+			  0 << 18 | 		/* unpack align */
+			  1 << 17 | 		/* unpack tight */
+			  3 << 13 | 		/* unpack count 0=1 component */
+			  (bpp-1) << 9 |	/* src Bpp 0=1 byte ... */
+			  0 << 8  | 		/* has alpha */
+			  0 << 6  | 		/* alpha bits 3=8bits */
+			  3 << 4  | 		/* R/Cr bits 1=5 2=6 3=8 */
+			  3 << 2  | 		/* B/Cb bits 1=5 2=6 3=8 */
+			  3 << 0,   		/* G/Y  bits 1=5 2=6 3=8 */
+			  MSM_ROTATOR_SRC_FORMAT);
+	}
+
+	return 0;
+}
+
+static int msm_rotator_rgb_types(struct msm_rotator_img_info *info,
+				 unsigned int in_paddr,
+				 unsigned int out_paddr,
+				 unsigned int use_imem,
+				 int new_session)
+{
+	int bpp, abits, rbits, gbits, bbits;
+
+	if (info->src.format != info->dst.format)
+		return -EINVAL;
+
+	bpp = get_bpp(info->src.format);
+	if (bpp < 0)
+		return -ENOTTY;
+
+	iowrite32(in_paddr, MSM_ROTATOR_SRCP0_ADDR);
+	iowrite32(out_paddr +
+			((info->dst_y * info->dst.width) + info->dst_x) * bpp,
+		  MSM_ROTATOR_OUTP0_ADDR);
+
+	if (new_session) {
+		iowrite32(info->src.width * bpp, MSM_ROTATOR_SRC_YSTRIDE1);
+		iowrite32(info->dst.width * bpp, MSM_ROTATOR_OUT_YSTRIDE1);
+		iowrite32((0  << 18) | 		/* chroma sampling 0=rgb */
+			  (ROTATIONS_TO_BITMASK(info->rotations) << 9) |
+			  1 << 8 |			/* ROT_EN */
+			  info->downscale_ratio << 2 |	/* downscale v ratio */
+			  info->downscale_ratio,	/* downscale h ratio */
+			  MSM_ROTATOR_SUB_BLOCK_CFG);
+		switch (info->src.format) {
+		case MDP_RGB_565:
+			iowrite32(GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8),
+				  MSM_ROTATOR_SRC_UNPACK_PATTERN1);
+			iowrite32(GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8),
+				  MSM_ROTATOR_OUT_PACK_PATTERN1);
+			abits = 0;
+			rbits = COMPONENT_5BITS;
+			gbits = COMPONENT_6BITS;
+			bbits = COMPONENT_5BITS;
+			break;
+
+		case MDP_BGR_565:
+			iowrite32(GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8),
+				  MSM_ROTATOR_SRC_UNPACK_PATTERN1);
+			iowrite32(GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8),
+				  MSM_ROTATOR_OUT_PACK_PATTERN1);
+			abits = 0;
+			rbits = COMPONENT_5BITS;
+			gbits = COMPONENT_6BITS;
+			bbits = COMPONENT_5BITS;
+			break;
+
+		case MDP_RGB_888:
+		case MDP_YCBCR_H1V1:
+		case MDP_YCRCB_H1V1:
+			iowrite32(GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8),
+				  MSM_ROTATOR_SRC_UNPACK_PATTERN1);
+			iowrite32(GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8),
+				  MSM_ROTATOR_OUT_PACK_PATTERN1);
+			abits = 0;
+			rbits = COMPONENT_8BITS;
+			gbits = COMPONENT_8BITS;
+			bbits = COMPONENT_8BITS;
+			break;
+
+		case MDP_ARGB_8888:
+		case MDP_RGBA_8888:
+		case MDP_XRGB_8888:
+		case MDP_RGBX_8888:
+			iowrite32(GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G,
+						   CLR_B, 8),
+				  MSM_ROTATOR_SRC_UNPACK_PATTERN1);
+			iowrite32(GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G,
+						   CLR_B, 8),
+				  MSM_ROTATOR_OUT_PACK_PATTERN1);
+			abits = COMPONENT_8BITS;
+			rbits = COMPONENT_8BITS;
+			gbits = COMPONENT_8BITS;
+			bbits = COMPONENT_8BITS;
+			break;
+
+		case MDP_BGRA_8888:
+			iowrite32(GET_PACK_PATTERN(CLR_ALPHA, CLR_B, CLR_G,
+						   CLR_R, 8),
+				  MSM_ROTATOR_SRC_UNPACK_PATTERN1);
+			iowrite32(GET_PACK_PATTERN(CLR_ALPHA, CLR_B, CLR_G,
+						   CLR_R, 8),
+				  MSM_ROTATOR_OUT_PACK_PATTERN1);
+			abits = COMPONENT_8BITS;
+			rbits = COMPONENT_8BITS;
+			gbits = COMPONENT_8BITS;
+			bbits = COMPONENT_8BITS;
+			break;
+
+		default:
+			return -EINVAL;
+		}
+		iowrite32(0 << 29 | 		/* frame format 0 = linear */
+			  (use_imem ? 0 : 1) << 22 | /* tile size */
+			  0 << 19 | 		/* fetch planes 0=interleaved */
+			  0 << 18 | 		/* unpack align */
+			  1 << 17 | 		/* unpack tight */
+			  (abits ? 3 : 2) << 13 | /* unpack count 0=1 comp */
+			  (bpp-1) << 9 | 	/* src Bpp 0=1 byte ... */
+			  (abits ? 1 : 0) << 8  | /* has alpha */
+			  abits << 6  | 	/* alpha bits 3=8bits */
+			  rbits << 4  | 	/* R/Cr bits 1=5 2=6 3=8 */
+			  bbits << 2  | 	/* B/Cb bits 1=5 2=6 3=8 */
+			  gbits << 0,   	/* G/Y  bits 1=5 2=6 3=8 */
+			  MSM_ROTATOR_SRC_FORMAT);
+	}
+
+	return 0;
+}
+
+static int get_img(struct msmfb_data *fbd, unsigned char src,
+	unsigned long *start, unsigned long *len, struct file **p_file,
+	int *p_need, struct ion_handle **p_ihdl)
+{
+	int ret = 0;
+#ifdef CONFIG_FB
+	struct file *file = NULL;
+	int put_needed, fb_num;
+#endif
+#ifdef CONFIG_ANDROID_PMEM
+	unsigned long vstart;
+#endif
+
+	*p_need = 0;
+
+#ifdef CONFIG_FB
+	if (fbd->flags & MDP_MEMORY_ID_TYPE_FB) {
+		file = fget_light(fbd->memory_id, &put_needed);
+		if (file == NULL) {
+			pr_err("fget_light returned NULL\n");
+			return -EINVAL;
+		}
+
+		if (MAJOR(file->f_dentry->d_inode->i_rdev) == FB_MAJOR) {
+			fb_num = MINOR(file->f_dentry->d_inode->i_rdev);
+			if (get_fb_phys_info(start, len, fb_num,
+				ROTATOR_SUBSYSTEM_ID)) {
+				pr_err("get_fb_phys_info() failed\n");
+				ret = -1;
+			} else {
+				*p_file = file;
+				*p_need = put_needed;
+			}
+		} else {
+			pr_err("invalid FB_MAJOR failed\n");
+			ret = -1;
+		}
+		if (ret)
+			fput_light(file, put_needed);
+		return ret;
+	}
+#endif
+
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+	return msm_rotator_iommu_map_buf(fbd->memory_id, src, start,
+		len, p_ihdl);
+#endif
+#ifdef CONFIG_ANDROID_PMEM
+	if (!get_pmem_file(fbd->memory_id, start, &vstart, len, p_file))
+		return 0;
+	else
+		return -ENOMEM;
+#endif
+
+}
+
+static void put_img(struct file *p_file, struct ion_handle *p_ihdl)
+{
+#ifdef CONFIG_ANDROID_PMEM
+	if (p_file != NULL)
+		put_pmem_file(p_file);
+#endif
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+	if (!IS_ERR_OR_NULL(p_ihdl)) {
+		pr_debug("%s(): p_ihdl %p\n", __func__, p_ihdl);
+		ion_unmap_iommu(msm_rotator_dev->client,
+			p_ihdl, ROTATOR_DOMAIN, GEN_POOL);
+
+		ion_free(msm_rotator_dev->client, p_ihdl);
+	}
+#endif
+}
+static int msm_rotator_do_rotate(unsigned long arg)
+{
+	unsigned int status, format;
+	struct msm_rotator_data_info info;
+	unsigned int in_paddr, out_paddr;
+	unsigned long src_len, dst_len;
+	int use_imem = 0, rc = 0, s;
+	struct file *srcp0_file = NULL, *dstp0_file = NULL;
+	struct file *srcp1_file = NULL, *dstp1_file = NULL;
+	struct ion_handle *srcp0_ihdl = NULL, *dstp0_ihdl = NULL;
+	struct ion_handle *srcp1_ihdl = NULL, *dstp1_ihdl = NULL;
+	int ps0_need, p_need;
+	unsigned int in_chroma_paddr = 0, out_chroma_paddr = 0;
+	unsigned int in_chroma2_paddr = 0;
+	struct msm_rotator_img_info *img_info;
+	struct msm_rotator_mem_planes src_planes, dst_planes;
+
+	if (copy_from_user(&info, (void __user *)arg, sizeof(info)))
+		return -EFAULT;
+
+	mutex_lock(&msm_rotator_dev->rotator_lock);
+	for (s = 0; s < MAX_SESSIONS; s++)
+		if ((msm_rotator_dev->img_info[s] != NULL) &&
+			(info.session_id ==
+			(unsigned int)msm_rotator_dev->img_info[s]
+			))
+			break;
+
+	if (s == MAX_SESSIONS) {
+		pr_err("%s() : Attempt to use invalid session_id %d\n",
+			__func__, s);
+		rc = -EINVAL;
+		goto do_rotate_unlock_mutex;
+	}
+
+	if (msm_rotator_dev->img_info[s]->enable == 0) {
+		dev_dbg(msm_rotator_dev->device,
+			"%s() : Session_id %d not enabled \n",
+			__func__, s);
+		rc = -EINVAL;
+		goto do_rotate_unlock_mutex;
+	}
+
+	img_info = msm_rotator_dev->img_info[s];
+	if (msm_rotator_get_plane_sizes(img_info->src.format,
+					img_info->src.width,
+					img_info->src.height,
+					&src_planes)) {
+		pr_err("%s: invalid src format\n", __func__);
+		rc = -EINVAL;
+		goto do_rotate_unlock_mutex;
+	}
+	if (msm_rotator_get_plane_sizes(img_info->dst.format,
+					img_info->dst.width,
+					img_info->dst.height,
+					&dst_planes)) {
+		pr_err("%s: invalid dst format\n", __func__);
+		rc = -EINVAL;
+		goto do_rotate_unlock_mutex;
+	}
+
+	rc = get_img(&info.src, 1, (unsigned long *)&in_paddr,
+			(unsigned long *)&src_len, &srcp0_file, &ps0_need,
+			&srcp0_ihdl);
+	if (rc) {
+		pr_err("%s: in get_img() failed id=0x%08x\n",
+			DRIVER_NAME, info.src.memory_id);
+		goto do_rotate_unlock_mutex;
+	}
+
+	rc = get_img(&info.dst, 0, (unsigned long *)&out_paddr,
+			(unsigned long *)&dst_len, &dstp0_file, &p_need,
+			&dstp0_ihdl);
+	if (rc) {
+		pr_err("%s: out get_img() failed id=0x%08x\n",
+		       DRIVER_NAME, info.dst.memory_id);
+		goto do_rotate_unlock_mutex;
+	}
+
+	format = msm_rotator_dev->img_info[s]->src.format;
+	if (((info.version_key & VERSION_KEY_MASK) == 0xA5B4C300) &&
+			((info.version_key & ~VERSION_KEY_MASK) > 0) &&
+			(src_planes.num_planes == 2)) {
+		if (checkoffset(info.src.offset,
+				src_planes.plane_size[0],
+				src_len)) {
+			pr_err("%s: invalid src buffer (len=%lu offset=%x)\n",
+			       __func__, src_len, info.src.offset);
+			rc = -ERANGE;
+			goto do_rotate_unlock_mutex;
+		}
+		if (checkoffset(info.dst.offset,
+				dst_planes.plane_size[0],
+				dst_len)) {
+			pr_err("%s: invalid dst buffer (len=%lu offset=%x)\n",
+			       __func__, dst_len, info.dst.offset);
+			rc = -ERANGE;
+			goto do_rotate_unlock_mutex;
+		}
+
+		rc = get_img(&info.src_chroma, 1,
+				(unsigned long *)&in_chroma_paddr,
+				(unsigned long *)&src_len, &srcp1_file, &p_need,
+				&srcp1_ihdl);
+		if (rc) {
+			pr_err("%s: in chroma get_img() failed id=0x%08x\n",
+				DRIVER_NAME, info.src_chroma.memory_id);
+			goto do_rotate_unlock_mutex;
+		}
+
+		rc = get_img(&info.dst_chroma, 0,
+				(unsigned long *)&out_chroma_paddr,
+				(unsigned long *)&dst_len, &dstp1_file, &p_need,
+				&dstp1_ihdl);
+		if (rc) {
+			pr_err("%s: out chroma get_img() failed id=0x%08x\n",
+				DRIVER_NAME, info.dst_chroma.memory_id);
+			goto do_rotate_unlock_mutex;
+		}
+
+		if (checkoffset(info.src_chroma.offset,
+				src_planes.plane_size[1],
+				src_len)) {
+			pr_err("%s: invalid chr src buf len=%lu offset=%x\n",
+			       __func__, src_len, info.src_chroma.offset);
+			rc = -ERANGE;
+			goto do_rotate_unlock_mutex;
+		}
+
+		if (checkoffset(info.dst_chroma.offset,
+				src_planes.plane_size[1],
+				dst_len)) {
+			pr_err("%s: invalid chr dst buf len=%lu offset=%x\n",
+			       __func__, dst_len, info.dst_chroma.offset);
+			rc = -ERANGE;
+			goto do_rotate_unlock_mutex;
+		}
+
+		in_chroma_paddr += info.src_chroma.offset;
+		out_chroma_paddr += info.dst_chroma.offset;
+	} else {
+		if (checkoffset(info.src.offset,
+				src_planes.total_size,
+				src_len)) {
+			pr_err("%s: invalid src buffer (len=%lu offset=%x)\n",
+			       __func__, src_len, info.src.offset);
+			rc = -ERANGE;
+			goto do_rotate_unlock_mutex;
+		}
+		if (checkoffset(info.dst.offset,
+				dst_planes.total_size,
+				dst_len)) {
+			pr_err("%s: invalid dst buffer (len=%lu offset=%x)\n",
+			       __func__, dst_len, info.dst.offset);
+			rc = -ERANGE;
+			goto do_rotate_unlock_mutex;
+		}
+	}
+
+	in_paddr += info.src.offset;
+	out_paddr += info.dst.offset;
+
+	if (!in_chroma_paddr && src_planes.num_planes >= 2)
+		in_chroma_paddr = in_paddr + src_planes.plane_size[0];
+	if (!out_chroma_paddr && dst_planes.num_planes >= 2)
+		out_chroma_paddr = out_paddr + dst_planes.plane_size[0];
+	if (src_planes.num_planes >= 3)
+		in_chroma2_paddr = in_chroma_paddr + src_planes.plane_size[1];
+
+	cancel_delayed_work(&msm_rotator_dev->rot_clk_work);
+	if (msm_rotator_dev->rot_clk_state != CLK_EN) {
+		enable_rot_clks();
+		msm_rotator_dev->rot_clk_state = CLK_EN;
+	}
+	enable_irq(msm_rotator_dev->irq);
+
+#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
+	use_imem = msm_rotator_imem_allocate(ROTATOR_REQUEST);
+#else
+	use_imem = 0;
+#endif
+	/*
+	 * workaround for a hardware bug. rotator hardware hangs when we
+	 * use write burst beat size 16 on 128X128 tile fetch mode. As a
+	 * temporary fix use 0x42 for BURST_SIZE when imem used.
+	 */
+	if (use_imem)
+		iowrite32(0x42, MSM_ROTATOR_MAX_BURST_SIZE);
+
+	iowrite32(((msm_rotator_dev->img_info[s]->src_rect.h & 0x1fff)
+				<< 16) |
+		  (msm_rotator_dev->img_info[s]->src_rect.w & 0x1fff),
+		  MSM_ROTATOR_SRC_SIZE);
+	iowrite32(((msm_rotator_dev->img_info[s]->src_rect.y & 0x1fff)
+				<< 16) |
+		  (msm_rotator_dev->img_info[s]->src_rect.x & 0x1fff),
+		  MSM_ROTATOR_SRC_XY);
+	iowrite32(((msm_rotator_dev->img_info[s]->src.height & 0x1fff)
+				<< 16) |
+		  (msm_rotator_dev->img_info[s]->src.width & 0x1fff),
+		  MSM_ROTATOR_SRC_IMAGE_SIZE);
+
+	switch (format) {
+	case MDP_RGB_565:
+	case MDP_BGR_565:
+	case MDP_RGB_888:
+	case MDP_ARGB_8888:
+	case MDP_RGBA_8888:
+	case MDP_XRGB_8888:
+	case MDP_BGRA_8888:
+	case MDP_RGBX_8888:
+	case MDP_YCBCR_H1V1:
+	case MDP_YCRCB_H1V1:
+		rc = msm_rotator_rgb_types(msm_rotator_dev->img_info[s],
+					   in_paddr, out_paddr,
+					   use_imem,
+					   msm_rotator_dev->last_session_idx
+								!= s);
+		break;
+	case MDP_Y_CBCR_H2V2:
+	case MDP_Y_CRCB_H2V2:
+	case MDP_Y_CB_CR_H2V2:
+	case MDP_Y_CR_CB_H2V2:
+	case MDP_Y_CR_CB_GH2V2:
+	case MDP_Y_CRCB_H2V2_TILE:
+	case MDP_Y_CBCR_H2V2_TILE:
+		rc = msm_rotator_ycxcx_h2v2(msm_rotator_dev->img_info[s],
+					    in_paddr, out_paddr, use_imem,
+					    msm_rotator_dev->last_session_idx
+								!= s,
+					    in_chroma_paddr,
+					    out_chroma_paddr,
+					    in_chroma2_paddr);
+		break;
+	case MDP_Y_CBCR_H2V1:
+	case MDP_Y_CRCB_H2V1:
+		rc = msm_rotator_ycxcx_h2v1(msm_rotator_dev->img_info[s],
+					    in_paddr, out_paddr, use_imem,
+					    msm_rotator_dev->last_session_idx
+								!= s,
+					    in_chroma_paddr,
+					    out_chroma_paddr);
+		break;
+	case MDP_YCRYCB_H2V1:
+		rc = msm_rotator_ycrycb(msm_rotator_dev->img_info[s],
+				in_paddr, out_paddr, use_imem,
+				msm_rotator_dev->last_session_idx != s,
+				out_chroma_paddr);
+		break;
+	default:
+		rc = -EINVAL;
+		pr_err("%s(): Unsupported format %u\n", __func__, format);
+		goto do_rotate_exit;
+	}
+
+	if (rc != 0) {
+		msm_rotator_dev->last_session_idx = INVALID_SESSION;
+		pr_err("%s(): Invalid session error\n", __func__);
+		goto do_rotate_exit;
+	}
+
+	iowrite32(3, MSM_ROTATOR_INTR_ENABLE);
+
+	msm_rotator_dev->processing = 1;
+	iowrite32(0x1, MSM_ROTATOR_START);
+
+	wait_event(msm_rotator_dev->wq,
+		   (msm_rotator_dev->processing == 0));
+	status = (unsigned char)ioread32(MSM_ROTATOR_INTR_STATUS);
+	if ((status & 0x03) != 0x01) {
+		pr_err("%s(): AXI Bus Error, issuing SW_RESET\n", __func__);
+		iowrite32(0x1, MSM_ROTATOR_SW_RESET);
+		rc = -EFAULT;
+	}
+	iowrite32(0, MSM_ROTATOR_INTR_ENABLE);
+	iowrite32(3, MSM_ROTATOR_INTR_CLEAR);
+
+do_rotate_exit:
+	disable_irq(msm_rotator_dev->irq);
+#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
+	msm_rotator_imem_free(ROTATOR_REQUEST);
+#endif
+	schedule_delayed_work(&msm_rotator_dev->rot_clk_work, HZ);
+do_rotate_unlock_mutex:
+	put_img(dstp1_file, dstp1_ihdl);
+	put_img(srcp1_file, srcp1_ihdl);
+	put_img(dstp0_file, dstp0_ihdl);
+
+	/* only source may use frame buffer */
+	if (info.src.flags & MDP_MEMORY_ID_TYPE_FB)
+		fput_light(srcp0_file, ps0_need);
+	else
+		put_img(srcp0_file, srcp0_ihdl);
+	mutex_unlock(&msm_rotator_dev->rotator_lock);
+	dev_dbg(msm_rotator_dev->device, "%s() returning rc = %d\n",
+		__func__, rc);
+	return rc;
+}
+
+static void msm_rotator_set_perf_level(u32 wh, u32 is_rgb)
+{
+	u32 perf_level;
+
+	if (is_rgb)
+		perf_level = 1;
+	else if (wh <= (640 * 480))
+		perf_level = 2;
+	else if (wh <= (736 * 1280))
+		perf_level = 3;
+	else
+		perf_level = 4;
+
+#ifdef CONFIG_MSM_BUS_SCALING
+	msm_bus_scale_client_update_request(msm_rotator_dev->bus_client_handle,
+		perf_level);
+#endif
+
+}
+
+static int msm_rotator_start(unsigned long arg,
+			     struct msm_rotator_fd_info *fd_info)
+{
+	struct msm_rotator_img_info info;
+	int rc = 0;
+	int s, is_rgb = 0;
+	int first_free_index = INVALID_SESSION;
+	unsigned int dst_w, dst_h;
+
+	if (copy_from_user(&info, (void __user *)arg, sizeof(info)))
+		return -EFAULT;
+
+	if ((info.rotations > MSM_ROTATOR_MAX_ROT) ||
+	    (info.src.height > MSM_ROTATOR_MAX_H) ||
+	    (info.src.width > MSM_ROTATOR_MAX_W) ||
+	    (info.dst.height > MSM_ROTATOR_MAX_H) ||
+	    (info.dst.width > MSM_ROTATOR_MAX_W) ||
+	    (info.downscale_ratio > MAX_DOWNSCALE_RATIO)) {
+		pr_err("%s: Invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	if (info.rotations & MDP_ROT_90) {
+		dst_w = info.src_rect.h >> info.downscale_ratio;
+		dst_h = info.src_rect.w >> info.downscale_ratio;
+	} else {
+		dst_w = info.src_rect.w >> info.downscale_ratio;
+		dst_h = info.src_rect.h >> info.downscale_ratio;
+	}
+
+	if (checkoffset(info.src_rect.x, info.src_rect.w, info.src.width) ||
+	    checkoffset(info.src_rect.y, info.src_rect.h, info.src.height) ||
+	    checkoffset(info.dst_x, dst_w, info.dst.width) ||
+	    checkoffset(info.dst_y, dst_h, info.dst.height)) {
+		pr_err("%s: Invalid src or dst rect\n", __func__);
+		return -ERANGE;
+	}
+
+	switch (info.src.format) {
+	case MDP_RGB_565:
+	case MDP_BGR_565:
+	case MDP_RGB_888:
+	case MDP_ARGB_8888:
+	case MDP_RGBA_8888:
+	case MDP_XRGB_8888:
+	case MDP_RGBX_8888:
+	case MDP_BGRA_8888:
+		is_rgb = 1;
+		info.dst.format = info.src.format;
+		break;
+	case MDP_Y_CBCR_H2V2:
+	case MDP_Y_CRCB_H2V2:
+	case MDP_Y_CBCR_H2V1:
+	case MDP_Y_CRCB_H2V1:
+	case MDP_YCBCR_H1V1:
+	case MDP_YCRCB_H1V1:
+		info.dst.format = info.src.format;
+		break;
+	case MDP_YCRYCB_H2V1:
+		info.dst.format = MDP_Y_CRCB_H2V1;
+		break;
+	case MDP_Y_CB_CR_H2V2:
+	case MDP_Y_CBCR_H2V2_TILE:
+		info.dst.format = MDP_Y_CBCR_H2V2;
+		break;
+	case MDP_Y_CR_CB_H2V2:
+	case MDP_Y_CR_CB_GH2V2:
+	case MDP_Y_CRCB_H2V2_TILE:
+		info.dst.format = MDP_Y_CRCB_H2V2;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	mutex_lock(&msm_rotator_dev->rotator_lock);
+
+	msm_rotator_set_perf_level((info.src.width*info.src.height), is_rgb);
+
+	for (s = 0; s < MAX_SESSIONS; s++) {
+		if ((msm_rotator_dev->img_info[s] != NULL) &&
+			(info.session_id ==
+			(unsigned int)msm_rotator_dev->img_info[s]
+			)) {
+			*(msm_rotator_dev->img_info[s]) = info;
+			msm_rotator_dev->fd_info[s] = fd_info;
+
+			if (msm_rotator_dev->last_session_idx == s)
+				msm_rotator_dev->last_session_idx =
+				INVALID_SESSION;
+			break;
+		}
+
+		if ((msm_rotator_dev->img_info[s] == NULL) &&
+			(first_free_index ==
+			INVALID_SESSION))
+			first_free_index = s;
+	}
+
+	if ((s == MAX_SESSIONS) && (first_free_index != INVALID_SESSION)) {
+		/* allocate a session id */
+		msm_rotator_dev->img_info[first_free_index] =
+			kzalloc(sizeof(struct msm_rotator_img_info),
+					GFP_KERNEL);
+		if (!msm_rotator_dev->img_info[first_free_index]) {
+			printk(KERN_ERR "%s : unable to alloc mem\n",
+					__func__);
+			rc = -ENOMEM;
+			goto rotator_start_exit;
+		}
+		info.session_id = (unsigned int)
+			msm_rotator_dev->img_info[first_free_index];
+		*(msm_rotator_dev->img_info[first_free_index]) = info;
+		msm_rotator_dev->fd_info[first_free_index] = fd_info;
+	} else if (s == MAX_SESSIONS) {
+		dev_dbg(msm_rotator_dev->device, "%s: all sessions in use\n",
+			__func__);
+		rc = -EBUSY;
+	}
+
+	if (rc == 0 && copy_to_user((void __user *)arg, &info, sizeof(info)))
+		rc = -EFAULT;
+
+rotator_start_exit:
+	mutex_unlock(&msm_rotator_dev->rotator_lock);
+
+	return rc;
+}
+
+static int msm_rotator_finish(unsigned long arg)
+{
+	int rc = 0;
+	int s;
+	unsigned int session_id;
+
+	if (copy_from_user(&session_id, (void __user *)arg, sizeof(s)))
+		return -EFAULT;
+
+	mutex_lock(&msm_rotator_dev->rotator_lock);
+	for (s = 0; s < MAX_SESSIONS; s++) {
+		if ((msm_rotator_dev->img_info[s] != NULL) &&
+			(session_id ==
+			(unsigned int)msm_rotator_dev->img_info[s])) {
+			if (msm_rotator_dev->last_session_idx == s)
+				msm_rotator_dev->last_session_idx =
+					INVALID_SESSION;
+			kfree(msm_rotator_dev->img_info[s]);
+			msm_rotator_dev->img_info[s] = NULL;
+			msm_rotator_dev->fd_info[s] = NULL;
+			break;
+		}
+	}
+
+	if (s == MAX_SESSIONS)
+		rc = -EINVAL;
+#ifdef CONFIG_MSM_BUS_SCALING
+	msm_bus_scale_client_update_request(msm_rotator_dev->bus_client_handle,
+		0);
+#endif
+	mutex_unlock(&msm_rotator_dev->rotator_lock);
+	return rc;
+}
+
+static int
+msm_rotator_open(struct inode *inode, struct file *filp)
+{
+	struct msm_rotator_fd_info *tmp, *fd_info = NULL;
+	int i;
+
+	if (filp->private_data)
+		return -EBUSY;
+
+	mutex_lock(&msm_rotator_dev->rotator_lock);
+	for (i = 0; i < MAX_SESSIONS; i++) {
+		if (msm_rotator_dev->fd_info[i] == NULL)
+			break;
+	}
+
+	if (i == MAX_SESSIONS) {
+		mutex_unlock(&msm_rotator_dev->rotator_lock);
+		return -EBUSY;
+	}
+
+	list_for_each_entry(tmp, &msm_rotator_dev->fd_list, list) {
+		if (tmp->pid == current->pid) {
+			fd_info = tmp;
+			break;
+		}
+	}
+
+	if (!fd_info) {
+		fd_info = kzalloc(sizeof(*fd_info), GFP_KERNEL);
+		if (!fd_info) {
+			mutex_unlock(&msm_rotator_dev->rotator_lock);
+			pr_err("%s: insufficient memory to alloc resources\n",
+			       __func__);
+			return -ENOMEM;
+		}
+		list_add(&fd_info->list, &msm_rotator_dev->fd_list);
+		fd_info->pid = current->pid;
+	}
+	fd_info->ref_cnt++;
+	mutex_unlock(&msm_rotator_dev->rotator_lock);
+
+	filp->private_data = fd_info;
+
+	return 0;
+}
+
+static int
+msm_rotator_close(struct inode *inode, struct file *filp)
+{
+	struct msm_rotator_fd_info *fd_info;
+	int s;
+
+	fd_info = (struct msm_rotator_fd_info *)filp->private_data;
+
+	mutex_lock(&msm_rotator_dev->rotator_lock);
+	if (--fd_info->ref_cnt > 0) {
+		mutex_unlock(&msm_rotator_dev->rotator_lock);
+		return 0;
+	}
+
+	for (s = 0; s < MAX_SESSIONS; s++) {
+		if (msm_rotator_dev->img_info[s] != NULL &&
+			msm_rotator_dev->fd_info[s] == fd_info) {
+			pr_debug("%s: freeing rotator session %p (pid %d)\n",
+				 __func__, msm_rotator_dev->img_info[s],
+				 fd_info->pid);
+			kfree(msm_rotator_dev->img_info[s]);
+			msm_rotator_dev->img_info[s] = NULL;
+			msm_rotator_dev->fd_info[s] = NULL;
+			if (msm_rotator_dev->last_session_idx == s)
+				msm_rotator_dev->last_session_idx =
+					INVALID_SESSION;
+		}
+	}
+	list_del(&fd_info->list);
+	kfree(fd_info);
+	mutex_unlock(&msm_rotator_dev->rotator_lock);
+
+	return 0;
+}
+
+static long msm_rotator_ioctl(struct file *file, unsigned cmd,
+						 unsigned long arg)
+{
+	struct msm_rotator_fd_info *fd_info;
+
+	if (_IOC_TYPE(cmd) != MSM_ROTATOR_IOCTL_MAGIC)
+		return -ENOTTY;
+
+	fd_info = (struct msm_rotator_fd_info *)file->private_data;
+
+	switch (cmd) {
+	case MSM_ROTATOR_IOCTL_START:
+		return msm_rotator_start(arg, fd_info);
+	case MSM_ROTATOR_IOCTL_ROTATE:
+		return msm_rotator_do_rotate(arg);
+	case MSM_ROTATOR_IOCTL_FINISH:
+		return msm_rotator_finish(arg);
+
+	default:
+		dev_dbg(msm_rotator_dev->device,
+			"unexpected IOCTL %d\n", cmd);
+		return -ENOTTY;
+	}
+}
+
+static const struct file_operations msm_rotator_fops = {
+	.owner = THIS_MODULE,
+	.open = msm_rotator_open,
+	.release = msm_rotator_close,
+	.unlocked_ioctl = msm_rotator_ioctl,
+};
+
+static int __devinit msm_rotator_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct resource *res;
+	struct msm_rotator_platform_data *pdata = NULL;
+	int i, number_of_clks;
+	uint32_t ver;
+
+	msm_rotator_dev = kzalloc(sizeof(struct msm_rotator_dev), GFP_KERNEL);
+	if (!msm_rotator_dev) {
+		printk(KERN_ERR "%s Unable to allocate memory for struct\n",
+		       __func__);
+		return -ENOMEM;
+	}
+	for (i = 0; i < MAX_SESSIONS; i++)
+		msm_rotator_dev->img_info[i] = NULL;
+	msm_rotator_dev->last_session_idx = INVALID_SESSION;
+
+	pdata = pdev->dev.platform_data;
+	number_of_clks = pdata->number_of_clocks;
+
+	msm_rotator_dev->imem_owner = IMEM_NO_OWNER;
+	mutex_init(&msm_rotator_dev->imem_lock);
+	INIT_LIST_HEAD(&msm_rotator_dev->fd_list);
+	msm_rotator_dev->imem_clk_state = CLK_DIS;
+	INIT_DELAYED_WORK(&msm_rotator_dev->imem_clk_work,
+			  msm_rotator_imem_clk_work_f);
+	msm_rotator_dev->imem_clk = NULL;
+	msm_rotator_dev->pdev = pdev;
+
+	msm_rotator_dev->core_clk = NULL;
+	msm_rotator_dev->pclk = NULL;
+
+#ifdef CONFIG_MSM_BUS_SCALING
+	if (!msm_rotator_dev->bus_client_handle && pdata &&
+		pdata->bus_scale_table) {
+		msm_rotator_dev->bus_client_handle =
+			msm_bus_scale_register_client(
+				pdata->bus_scale_table);
+		if (!msm_rotator_dev->bus_client_handle) {
+			pr_err("%s not able to get bus scale handle\n",
+				__func__);
+		}
+	}
+#endif
+
+	for (i = 0; i < number_of_clks; i++) {
+		if (pdata->rotator_clks[i].clk_type == ROTATOR_IMEM_CLK) {
+			msm_rotator_dev->imem_clk =
+			clk_get(&msm_rotator_dev->pdev->dev,
+				pdata->rotator_clks[i].clk_name);
+			if (IS_ERR(msm_rotator_dev->imem_clk)) {
+				rc = PTR_ERR(msm_rotator_dev->imem_clk);
+				msm_rotator_dev->imem_clk = NULL;
+				printk(KERN_ERR "%s: cannot get imem_clk "
+					"rc=%d\n", DRIVER_NAME, rc);
+				goto error_imem_clk;
+			}
+			if (pdata->rotator_clks[i].clk_rate)
+				clk_set_rate(msm_rotator_dev->imem_clk,
+					pdata->rotator_clks[i].clk_rate);
+		}
+		if (pdata->rotator_clks[i].clk_type == ROTATOR_PCLK) {
+			msm_rotator_dev->pclk =
+			clk_get(&msm_rotator_dev->pdev->dev,
+				pdata->rotator_clks[i].clk_name);
+			if (IS_ERR(msm_rotator_dev->pclk)) {
+				rc = PTR_ERR(msm_rotator_dev->pclk);
+				msm_rotator_dev->pclk = NULL;
+				printk(KERN_ERR "%s: cannot get pclk rc=%d\n",
+					DRIVER_NAME, rc);
+				goto error_pclk;
+			}
+
+			if (pdata->rotator_clks[i].clk_rate)
+				clk_set_rate(msm_rotator_dev->pclk,
+					pdata->rotator_clks[i].clk_rate);
+		}
+
+		if (pdata->rotator_clks[i].clk_type == ROTATOR_CORE_CLK) {
+			msm_rotator_dev->core_clk =
+			clk_get(&msm_rotator_dev->pdev->dev,
+				pdata->rotator_clks[i].clk_name);
+			if (IS_ERR(msm_rotator_dev->core_clk)) {
+				rc = PTR_ERR(msm_rotator_dev->core_clk);
+				msm_rotator_dev->core_clk = NULL;
+				printk(KERN_ERR "%s: cannot get core clk "
+					"rc=%d\n", DRIVER_NAME, rc);
+			goto error_core_clk;
+			}
+
+			if (pdata->rotator_clks[i].clk_rate)
+				clk_set_rate(msm_rotator_dev->core_clk,
+					pdata->rotator_clks[i].clk_rate);
+		}
+	}
+
+	msm_rotator_dev->regulator = regulator_get(&msm_rotator_dev->pdev->dev,
+						   "vdd");
+	if (IS_ERR(msm_rotator_dev->regulator))
+		msm_rotator_dev->regulator = NULL;
+
+	msm_rotator_dev->rot_clk_state = CLK_DIS;
+	INIT_DELAYED_WORK(&msm_rotator_dev->rot_clk_work,
+			  msm_rotator_rot_clk_work_f);
+
+	mutex_init(&msm_rotator_dev->rotator_lock);
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+	msm_rotator_dev->client = msm_ion_client_create(-1, pdev->name);
+#endif
+	platform_set_drvdata(pdev, msm_rotator_dev);
+
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		printk(KERN_ALERT
+		       "%s: could not get IORESOURCE_MEM\n", DRIVER_NAME);
+		rc = -ENODEV;
+		goto error_get_resource;
+	}
+	msm_rotator_dev->io_base = ioremap(res->start,
+					   resource_size(res));
+
+#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
+	if (msm_rotator_dev->imem_clk)
+		clk_prepare_enable(msm_rotator_dev->imem_clk);
+#endif
+	enable_rot_clks();
+	ver = ioread32(MSM_ROTATOR_HW_VERSION);
+	disable_rot_clks();
+
+#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
+	if (msm_rotator_dev->imem_clk)
+		clk_disable_unprepare(msm_rotator_dev->imem_clk);
+#endif
+	if (ver != pdata->hardware_version_number)
+		pr_debug("%s: invalid HW version ver 0x%x\n",
+			DRIVER_NAME, ver);
+
+	rotator_hw_revision = ver;
+	rotator_hw_revision >>= 16;     /* bit 31:16 */
+	rotator_hw_revision &= 0xff;
+
+	pr_info("%s: rotator_hw_revision=%x\n",
+		__func__, rotator_hw_revision);
+
+	msm_rotator_dev->irq = platform_get_irq(pdev, 0);
+	if (msm_rotator_dev->irq < 0) {
+		printk(KERN_ALERT "%s: could not get IORESOURCE_IRQ\n",
+		       DRIVER_NAME);
+		rc = -ENODEV;
+		goto error_get_irq;
+	}
+	rc = request_irq(msm_rotator_dev->irq, msm_rotator_isr,
+			 IRQF_TRIGGER_RISING, DRIVER_NAME, NULL);
+	if (rc) {
+		printk(KERN_ERR "%s: request_irq() failed\n", DRIVER_NAME);
+		goto error_get_irq;
+	}
+	/* we enable the IRQ when we need it in the ioctl */
+	disable_irq(msm_rotator_dev->irq);
+
+	rc = alloc_chrdev_region(&msm_rotator_dev->dev_num, 0, 1, DRIVER_NAME);
+	if (rc < 0) {
+		printk(KERN_ERR "%s: alloc_chrdev_region Failed rc = %d\n",
+		       __func__, rc);
+		goto error_get_irq;
+	}
+
+	msm_rotator_dev->class = class_create(THIS_MODULE, DRIVER_NAME);
+	if (IS_ERR(msm_rotator_dev->class)) {
+		rc = PTR_ERR(msm_rotator_dev->class);
+		printk(KERN_ERR "%s: couldn't create class rc = %d\n",
+		       DRIVER_NAME, rc);
+		goto error_class_create;
+	}
+
+	msm_rotator_dev->device = device_create(msm_rotator_dev->class, NULL,
+						msm_rotator_dev->dev_num, NULL,
+						DRIVER_NAME);
+	if (IS_ERR(msm_rotator_dev->device)) {
+		rc = PTR_ERR(msm_rotator_dev->device);
+		printk(KERN_ERR "%s: device_create failed %d\n",
+		       DRIVER_NAME, rc);
+		goto error_class_device_create;
+	}
+
+	cdev_init(&msm_rotator_dev->cdev, &msm_rotator_fops);
+	rc = cdev_add(&msm_rotator_dev->cdev,
+		      MKDEV(MAJOR(msm_rotator_dev->dev_num), 0),
+		      1);
+	if (rc < 0) {
+		printk(KERN_ERR "%s: cdev_add failed %d\n", __func__, rc);
+		goto error_cdev_add;
+	}
+
+	init_waitqueue_head(&msm_rotator_dev->wq);
+
+	dev_dbg(msm_rotator_dev->device, "probe successful\n");
+	return rc;
+
+error_cdev_add:
+	device_destroy(msm_rotator_dev->class, msm_rotator_dev->dev_num);
+error_class_device_create:
+	class_destroy(msm_rotator_dev->class);
+error_class_create:
+	unregister_chrdev_region(msm_rotator_dev->dev_num, 1);
+error_get_irq:
+	iounmap(msm_rotator_dev->io_base);
+error_get_resource:
+	mutex_destroy(&msm_rotator_dev->rotator_lock);
+	if (msm_rotator_dev->regulator)
+		regulator_put(msm_rotator_dev->regulator);
+	clk_put(msm_rotator_dev->core_clk);
+error_core_clk:
+	clk_put(msm_rotator_dev->pclk);
+error_pclk:
+	if (msm_rotator_dev->imem_clk)
+		clk_put(msm_rotator_dev->imem_clk);
+error_imem_clk:
+	mutex_destroy(&msm_rotator_dev->imem_lock);
+	kfree(msm_rotator_dev);
+	return rc;
+}
+
+static int __devexit msm_rotator_remove(struct platform_device *plat_dev)
+{
+	int i;
+
+#ifdef CONFIG_MSM_BUS_SCALING
+	msm_bus_scale_unregister_client(msm_rotator_dev->bus_client_handle);
+#endif
+	free_irq(msm_rotator_dev->irq, NULL);
+	mutex_destroy(&msm_rotator_dev->rotator_lock);
+	cdev_del(&msm_rotator_dev->cdev);
+	device_destroy(msm_rotator_dev->class, msm_rotator_dev->dev_num);
+	class_destroy(msm_rotator_dev->class);
+	unregister_chrdev_region(msm_rotator_dev->dev_num, 1);
+	iounmap(msm_rotator_dev->io_base);
+	if (msm_rotator_dev->imem_clk) {
+		if (msm_rotator_dev->imem_clk_state == CLK_EN)
+			clk_disable_unprepare(msm_rotator_dev->imem_clk);
+		clk_put(msm_rotator_dev->imem_clk);
+		msm_rotator_dev->imem_clk = NULL;
+	}
+	if (msm_rotator_dev->rot_clk_state == CLK_EN)
+		disable_rot_clks();
+	clk_put(msm_rotator_dev->core_clk);
+	clk_put(msm_rotator_dev->pclk);
+	if (msm_rotator_dev->regulator)
+		regulator_put(msm_rotator_dev->regulator);
+	msm_rotator_dev->core_clk = NULL;
+	msm_rotator_dev->pclk = NULL;
+	mutex_destroy(&msm_rotator_dev->imem_lock);
+	for (i = 0; i < MAX_SESSIONS; i++)
+		if (msm_rotator_dev->img_info[i] != NULL)
+			kfree(msm_rotator_dev->img_info[i]);
+	kfree(msm_rotator_dev);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int msm_rotator_suspend(struct platform_device *dev, pm_message_t state)
+{
+	mutex_lock(&msm_rotator_dev->imem_lock);
+	if (msm_rotator_dev->imem_clk_state == CLK_EN
+		&& msm_rotator_dev->imem_clk) {
+		clk_disable_unprepare(msm_rotator_dev->imem_clk);
+		msm_rotator_dev->imem_clk_state = CLK_SUSPEND;
+	}
+	mutex_unlock(&msm_rotator_dev->imem_lock);
+	mutex_lock(&msm_rotator_dev->rotator_lock);
+	if (msm_rotator_dev->rot_clk_state == CLK_EN) {
+		disable_rot_clks();
+		msm_rotator_dev->rot_clk_state = CLK_SUSPEND;
+	}
+	mutex_unlock(&msm_rotator_dev->rotator_lock);
+	return 0;
+}
+
+static int msm_rotator_resume(struct platform_device *dev)
+{
+	mutex_lock(&msm_rotator_dev->imem_lock);
+	if (msm_rotator_dev->imem_clk_state == CLK_SUSPEND
+		&& msm_rotator_dev->imem_clk) {
+		clk_prepare_enable(msm_rotator_dev->imem_clk);
+		msm_rotator_dev->imem_clk_state = CLK_EN;
+	}
+	mutex_unlock(&msm_rotator_dev->imem_lock);
+	mutex_lock(&msm_rotator_dev->rotator_lock);
+	if (msm_rotator_dev->rot_clk_state == CLK_SUSPEND) {
+		enable_rot_clks();
+		msm_rotator_dev->rot_clk_state = CLK_EN;
+	}
+	mutex_unlock(&msm_rotator_dev->rotator_lock);
+	return 0;
+}
+#endif
+
+static struct platform_driver msm_rotator_platform_driver = {
+	.probe = msm_rotator_probe,
+	.remove = __devexit_p(msm_rotator_remove),
+#ifdef CONFIG_PM
+	.suspend = msm_rotator_suspend,
+	.resume = msm_rotator_resume,
+#endif
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = DRIVER_NAME
+	}
+};
+
+static int __init msm_rotator_init(void)
+{
+	return platform_driver_register(&msm_rotator_platform_driver);
+}
+
+static void __exit msm_rotator_exit(void)
+{
+	return platform_driver_unregister(&msm_rotator_platform_driver);
+}
+
+module_init(msm_rotator_init);
+module_exit(msm_rotator_exit);
+
+MODULE_DESCRIPTION("MSM Offline Image Rotator driver");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/char/msm_smd_pkt.c b/drivers/char/msm_smd_pkt.c
index 8eca55d..adb2926 100644
--- a/drivers/char/msm_smd_pkt.c
+++ b/drivers/char/msm_smd_pkt.c
@@ -9,11 +9,6 @@
  * 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 Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
  */
 /*
  * SMD Packet Driver -- Provides userspace interface to SMD packet ports.
diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
index a048199..8122ed1 100644
--- a/drivers/char/tpm/Kconfig
+++ b/drivers/char/tpm/Kconfig
@@ -62,4 +62,18 @@
 	  Further information on this driver and the supported hardware
 	  can be found at http://www.trust.rub.de/projects/linux-device-driver-infineon-tpm/ 
 
+config TCG_ST_I2C
+	tristate "ST Micro ST19NP18-TPM-I2C TPM interface"
+	depends on I2C
+	default n
+	---help---
+	  If you have a ST19NP18-TPM-I2C TPM security chip from ST Micro
+	  say Yes and it will be accessible from Linux.
+
+config TCG_TPMD_DEV
+	tristate "tpmd_dev TPM Emulator driver"
+	default n
+	---help---
+	  Enables the TPM emulator driver
+
 endif # TCG_TPM
diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
index ea3a1e0..c113cf1 100644
--- a/drivers/char/tpm/Makefile
+++ b/drivers/char/tpm/Makefile
@@ -6,6 +6,8 @@
 	obj-$(CONFIG_TCG_TPM) += tpm_bios.o
 endif
 obj-$(CONFIG_TCG_TIS) += tpm_tis.o
+obj-$(CONFIG_TCG_ST_I2C) += tpm_st_i2c.o
 obj-$(CONFIG_TCG_NSC) += tpm_nsc.o
 obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o
 obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o
+obj-$(CONFIG_TCG_TPMD_DEV) += tpmd_dev/
diff --git a/drivers/char/tpm/tpm_st_i2c.c b/drivers/char/tpm/tpm_st_i2c.c
new file mode 100644
index 0000000..3a6e8c4f
--- /dev/null
+++ b/drivers/char/tpm/tpm_st_i2c.c
@@ -0,0 +1,361 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <mach/gpio.h>
+#include <mach/tpm_st_i2c.h>
+#include <mach/msm_iomap.h>
+#include "tpm.h"
+
+#define DEVICE_NAME "tpm_st_i2c"
+
+#define TPM_HEADER_LEN sizeof(struct tpm_input_header)
+#define TPM_ST_I2C_BLOCK_MAX 40
+
+struct tpm_st_i2c_dev {
+	struct i2c_client *client;
+	struct tpm_st_i2c_platform_data *pd;
+	struct completion com[2];
+};
+
+/* for completion array */
+#define ACCEPT_CMD_INDEX 0
+#define DATA_AVAIL_INDEX 1
+
+static struct tpm_st_i2c_dev *tpm_st_i2c_dev;
+
+#define TPM_ST_I2C_REQ_COMPLETE_MASK 1
+
+static u8 tpm_st_i2c_status(struct tpm_chip *chip)
+{
+	int gpio = tpm_st_i2c_dev->pd->data_avail_gpio;
+	return gpio_get_value(gpio);
+}
+
+static void tpm_st_i2c_cancel(struct tpm_chip *chip)
+{
+	/* not supported */
+	return;
+}
+
+static int tpm_st_i2c_transfer_buf(struct tpm_chip *chip, u8 *buf, size_t count,
+				   int recv)
+{
+	struct i2c_msg msg = {
+		.addr = tpm_st_i2c_dev->client->addr,
+		.flags = 0,
+		.buf = buf,
+		.len = TPM_HEADER_LEN, /* must read/write header first */
+	};
+	int gpio;
+	int irq;
+	struct completion *com;
+	__be32 *native_size;
+	int read_header = 0;
+	int rc = 0;
+	int len = count;
+	uint32_t size = count;
+	int tmp;
+
+	if (recv) {
+		msg.flags |= I2C_M_RD;
+		read_header = 1;
+		gpio = tpm_st_i2c_dev->pd->data_avail_gpio;
+		irq = tpm_st_i2c_dev->pd->data_avail_irq;
+		com = &tpm_st_i2c_dev->com[DATA_AVAIL_INDEX];
+	} else {
+		gpio = tpm_st_i2c_dev->pd->accept_cmd_gpio;
+		irq = tpm_st_i2c_dev->pd->accept_cmd_irq;
+		com = &tpm_st_i2c_dev->com[ACCEPT_CMD_INDEX];
+	}
+
+	if (len < TPM_HEADER_LEN) {
+		dev_dbg(chip->dev, "%s: invalid len\n", __func__);
+		return -EINVAL;
+	}
+
+	do {
+		if (!gpio_get_value(gpio)) {
+			/* reset the completion in case the irq fired
+			 * during the probe
+			 */
+			init_completion(com);
+			enable_irq(irq);
+			tmp = wait_for_completion_interruptible_timeout(
+				com, HZ/2);
+			if (!tmp) {
+				dev_dbg(chip->dev, "%s timeout\n",
+					__func__);
+				return -EBUSY;
+			}
+		}
+		rc = i2c_transfer(tpm_st_i2c_dev->client->adapter,
+				  &msg, 1);
+		if (rc < 0) {
+			dev_dbg(chip->dev, "Error in I2C transfer\n");
+			return rc;
+		}
+		if (read_header) {
+			read_header = 0;
+			native_size = (__force __be32 *) (buf + 2);
+			size = be32_to_cpu(*native_size);
+			if (count < size) {
+				dev_dbg(chip->dev,
+					"%s: invalid count\n",
+					__func__);
+				rc = -EIO;
+			}
+			len = size;
+		}
+		len -= msg.len;
+		if (len) {
+			buf += msg.len;
+			msg.buf = buf;
+			if (len > TPM_ST_I2C_BLOCK_MAX)
+				msg.len = TPM_ST_I2C_BLOCK_MAX;
+			else
+				msg.len = len;
+		}
+	} while (len > 0);
+
+	if (rc >= 0)
+		return size;
+	else
+		return rc;
+}
+
+static int tpm_st_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+	return tpm_st_i2c_transfer_buf(chip, buf, count, 1);
+}
+
+static int tpm_st_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len)
+{
+	return tpm_st_i2c_transfer_buf(chip, buf, len, 0);
+}
+
+#ifdef CONFIG_PM
+static int tpm_st_i2c_suspend(struct i2c_client *client, pm_message_t msg)
+{
+	return tpm_pm_suspend(&client->dev, msg);
+}
+
+static int tpm_st_i2c_resume(struct i2c_client *client)
+{
+	return tpm_pm_resume(&client->dev);
+}
+#endif
+
+static const struct file_operations tpm_st_i2c_fs_ops = {
+	.owner = THIS_MODULE,
+	.llseek = no_llseek,
+	.open = tpm_open,
+	.read = tpm_read,
+	.write = tpm_write,
+	.release = tpm_release,
+};
+
+static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL);
+static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL);
+static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL);
+static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL);
+static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL);
+static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated,
+		   NULL);
+static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL);
+
+static struct attribute *tpm_st_i2c_attrs[] = {
+	&dev_attr_pubek.attr,
+	&dev_attr_pcrs.attr,
+	&dev_attr_enabled.attr,
+	&dev_attr_active.attr,
+	&dev_attr_owned.attr,
+	&dev_attr_temp_deactivated.attr,
+	&dev_attr_caps.attr,
+	NULL,
+};
+
+static struct attribute_group tpm_st_i2c_attr_grp = {
+	.attrs = tpm_st_i2c_attrs
+};
+
+static struct tpm_vendor_specific tpm_st_i2c_vendor = {
+	.status = tpm_st_i2c_status,
+	.recv = tpm_st_i2c_recv,
+	.send = tpm_st_i2c_send,
+	.cancel = tpm_st_i2c_cancel,
+	.req_complete_mask = TPM_ST_I2C_REQ_COMPLETE_MASK,
+	.req_complete_val = TPM_ST_I2C_REQ_COMPLETE_MASK,
+	.req_canceled = 0xff,  /* not supported */
+	.attr_group = &tpm_st_i2c_attr_grp,
+	.miscdev = {
+		    .fops = &tpm_st_i2c_fs_ops,},
+};
+
+static irqreturn_t tpm_st_i2c_isr(int irq, void *dev_id)
+{
+	disable_irq_nosync(irq);
+	if (irq == tpm_st_i2c_dev->pd->accept_cmd_irq)
+		complete(&tpm_st_i2c_dev->com[ACCEPT_CMD_INDEX]);
+	else
+		complete(&tpm_st_i2c_dev->com[DATA_AVAIL_INDEX]);
+	return IRQ_HANDLED;
+}
+
+static int tpm_st_i2c_probe(struct i2c_client *client,
+			    const struct i2c_device_id *id)
+{
+	int rc = 0;
+	struct tpm_st_i2c_platform_data *pd;
+	struct  tpm_chip *chip;
+	int high;
+
+	dev_dbg(&client->dev, "%s()\n", __func__);
+
+	if (!i2c_check_functionality(client->adapter,
+				     I2C_FUNC_SMBUS_BYTE |
+				     I2C_FUNC_SMBUS_I2C_BLOCK |
+				     I2C_FUNC_I2C)) {
+		dev_err(&client->dev, "incompatible adapter\n");
+		return -ENODEV;
+	}
+
+	pd = client->dev.platform_data;
+	if (!pd || !pd->gpio_setup || !pd->gpio_release) {
+		dev_err(&client->dev, "platform data not setup\n");
+		rc = -EFAULT;
+		goto no_platform_data;
+	}
+	rc = pd->gpio_setup();
+	if (rc) {
+		dev_err(&client->dev, "gpio_setup failed\n");
+		goto gpio_setup_fail;
+	}
+
+	gpio_direction_input(pd->accept_cmd_gpio);
+	gpio_direction_input(pd->data_avail_gpio);
+
+	tpm_st_i2c_dev = kzalloc(sizeof(struct tpm_st_i2c_dev), GFP_KERNEL);
+	if (!tpm_st_i2c_dev) {
+		printk(KERN_ERR "%s Unable to allocate memory for struct\n",
+		       __func__);
+		rc = -ENOMEM;
+		goto kzalloc_fail;
+	}
+
+	tpm_st_i2c_dev->client = client;
+	tpm_st_i2c_dev->pd = pd;
+
+	init_completion(&tpm_st_i2c_dev->com[ACCEPT_CMD_INDEX]);
+	init_completion(&tpm_st_i2c_dev->com[DATA_AVAIL_INDEX]);
+	/* This logic allows us to setup irq but not have it enabled, in
+	 * case the lines are already active
+	 */
+	high = gpio_get_value(pd->data_avail_gpio);
+	rc = request_irq(pd->data_avail_irq, tpm_st_i2c_isr, IRQF_TRIGGER_HIGH,
+			 DEVICE_NAME "-data", NULL);
+	if (rc) {
+		dev_err(&client->dev, "request for data irq failed\n");
+		goto data_irq_fail;
+	}
+	if (!high)
+		disable_irq(pd->data_avail_irq);
+	high = gpio_get_value(pd->accept_cmd_gpio);
+	rc = request_irq(pd->accept_cmd_irq, tpm_st_i2c_isr, IRQF_TRIGGER_HIGH,
+			 DEVICE_NAME "-cmd", NULL);
+	if (rc) {
+		dev_err(&client->dev, "request for cmd irq failed\n");
+		goto cmd_irq_fail;
+	}
+	if (!high)
+		disable_irq(pd->accept_cmd_irq);
+
+	tpm_st_i2c_vendor.irq = pd->data_avail_irq;
+
+	chip = tpm_register_hardware(&client->dev, &tpm_st_i2c_vendor);
+	if (!chip) {
+		dev_err(&client->dev, "Could not register tpm hardware\n");
+		rc = -ENODEV;
+		goto tpm_reg_fail;
+	}
+
+	dev_info(&client->dev, "added\n");
+
+	return 0;
+
+tpm_reg_fail:
+	free_irq(pd->accept_cmd_irq, NULL);
+cmd_irq_fail:
+	free_irq(pd->data_avail_irq, NULL);
+data_irq_fail:
+kzalloc_fail:
+	pd->gpio_release();
+gpio_setup_fail:
+no_platform_data:
+
+	return rc;
+}
+
+static int __exit tpm_st_i2c_remove(struct i2c_client *client)
+{
+	free_irq(tpm_st_i2c_dev->pd->accept_cmd_irq, NULL);
+	free_irq(tpm_st_i2c_dev->pd->data_avail_irq, NULL);
+	tpm_remove_hardware(&client->dev);
+	tpm_st_i2c_dev->pd->gpio_release();
+	kfree(tpm_st_i2c_dev);
+
+	return 0;
+}
+
+static const struct i2c_device_id tpm_st_i2c_id[] = {
+	{ DEVICE_NAME, 0 },
+	{ }
+};
+
+static struct i2c_driver tpm_st_i2c_driver = {
+	.driver = {
+		.name = DEVICE_NAME,
+		.owner = THIS_MODULE,
+	},
+	.probe = tpm_st_i2c_probe,
+	.remove =  __exit_p(tpm_st_i2c_remove),
+#ifdef CONFIG_PM
+	.suspend = tpm_st_i2c_suspend,
+	.resume = tpm_st_i2c_resume,
+#endif
+	.id_table = tpm_st_i2c_id,
+};
+
+static int __init tpm_st_i2c_init(void)
+{
+	int ret;
+
+	ret = i2c_add_driver(&tpm_st_i2c_driver);
+	if (ret)
+		printk(KERN_ERR "%s: failed to add i2c driver\n", __func__);
+
+	return ret;
+}
+
+static void __exit tpm_st_i2c_exit(void)
+{
+	i2c_del_driver(&tpm_st_i2c_driver);
+}
+
+module_init(tpm_st_i2c_init);
+module_exit(tpm_st_i2c_exit);
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("1.0");
+MODULE_AUTHOR("Qualcomm Innovation Center, Inc.");
+MODULE_DESCRIPTION("ST19NP18-TPM-I2C driver");
diff --git a/drivers/char/tpm/tpmd_dev/Makefile b/drivers/char/tpm/tpmd_dev/Makefile
new file mode 100644
index 0000000..7d62de4
--- /dev/null
+++ b/drivers/char/tpm/tpmd_dev/Makefile
@@ -0,0 +1,4 @@
+#
+# Makefile for the kernel tpm emulator device driver.
+#
+obj-$(CONFIG_TCG_TPM) += tpmd_dev.o
diff --git a/drivers/char/tpm/tpmd_dev/config.h b/drivers/char/tpm/tpmd_dev/config.h
new file mode 100644
index 0000000..ec8d93e
--- /dev/null
+++ b/drivers/char/tpm/tpmd_dev/config.h
@@ -0,0 +1,32 @@
+/* Software-based Trusted Platform Module (TPM) Emulator
+ * Copyright (C) 2004-2010 Mario Strasser <mast@gmx.net>
+ *
+ * This module 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; either version 2 of the License,
+ * or (at your option) any later version.
+ *
+ * This module 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.
+ *
+ * $Id: config.h.in 426 2010-02-22 17:11:58Z mast $
+ */
+
+#ifndef _CONFIG_H_
+#define _CONFIG_H_
+
+/* project and build version */
+#define VERSION_MAJOR 0
+#define VERSION_MINOR 7
+#define VERSION_BUILD 424
+
+/* TDDL and LKM configuration */
+#define TPM_SOCKET_NAME  "/var/run/tpm/tpmd_socket:0"
+#define TPM_STORAGE_NAME "/var/lib/tpm/tpm_emulator-1_2_0_7"
+#define TPM_DEVICE_NAME  "/dev/tpm"
+#define TPM_LOG_FILE     ""
+#define TPM_CMD_BUF_SIZE 4096
+
+#endif /* _CONFIG_H_ */
diff --git a/drivers/char/tpm/tpmd_dev/tpmd_dev.c b/drivers/char/tpm/tpmd_dev/tpmd_dev.c
new file mode 100644
index 0000000..cbfcbd8
--- /dev/null
+++ b/drivers/char/tpm/tpmd_dev/tpmd_dev.c
@@ -0,0 +1,272 @@
+/* Software-based Trusted Platform Module (TPM) Emulator
+ * Copyright (C) 2004-2010 Mario Strasser <mast@gmx.net>
+ *
+ * This module 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; either version 2 of the License,
+ * or (at your option) any later version.
+ *
+ * This module 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.
+ *
+ * $Id: tpmd_dev.c 426 2010-02-22 17:11:58Z mast $
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+
+#include <linux/socket.h>
+#include <linux/net.h>
+#include <linux/un.h>
+
+#include "config.h"
+
+#define TPM_DEVICE_MINOR  224
+#define TPM_DEVICE_ID     "tpm"
+#define TPM_MODULE_NAME   "tpmd_dev"
+
+#define TPM_STATE_IS_OPEN 0
+
+#ifdef DEBUG
+#define debug(fmt, ...) printk(KERN_DEBUG "%s %s:%d: Debug: " fmt "\n", \
+                        TPM_MODULE_NAME, __FILE__, __LINE__, ## __VA_ARGS__)
+#else
+#define debug(fmt, ...)
+#endif
+#define info(fmt, ...)  printk(KERN_INFO "%s %s:%d: Info: " fmt "\n", \
+                        TPM_MODULE_NAME, __FILE__, __LINE__, ## __VA_ARGS__)
+#define error(fmt, ...) printk(KERN_ERR "%s %s:%d: Error: " fmt "\n", \
+                        TPM_MODULE_NAME, __FILE__, __LINE__, ## __VA_ARGS__)
+#define alert(fmt, ...) printk(KERN_ALERT "%s %s:%d: Alert: " fmt "\n", \
+                        TPM_MODULE_NAME, __FILE__, __LINE__, ## __VA_ARGS__)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mario Strasser <mast@gmx.net>");
+MODULE_DESCRIPTION("Trusted Platform Module (TPM) Emulator");
+MODULE_SUPPORTED_DEVICE(TPM_DEVICE_ID);
+
+/* module parameters */
+char *tpmd_socket_name = TPM_SOCKET_NAME;
+module_param(tpmd_socket_name, charp, 0444);
+MODULE_PARM_DESC(tpmd_socket_name, " Sets the name of the TPM daemon socket.");
+
+/* TPM lock */
+static struct semaphore tpm_mutex;
+
+/* TPM command response */
+static struct {
+  uint8_t *data;
+  uint32_t size;
+} tpm_response;
+
+/* module state */
+static uint32_t module_state;
+static struct socket *tpmd_sock;
+static struct sockaddr_un addr;
+
+static int tpmd_connect(char *socket_name)
+{
+  int res;
+  res = sock_create(PF_UNIX, SOCK_STREAM, 0, &tpmd_sock);
+  if (res != 0) {
+    error("sock_create() failed: %d\n", res);
+    tpmd_sock = NULL;
+    return res;
+  }
+  addr.sun_family = AF_UNIX;
+  strncpy(addr.sun_path, socket_name, sizeof(addr.sun_path));
+  res = tpmd_sock->ops->connect(tpmd_sock, 
+    (struct sockaddr*)&addr, sizeof(struct sockaddr_un), 0);
+  if (res != 0) {
+    error("sock_connect() failed: %d\n", res);
+    tpmd_sock->ops->release(tpmd_sock);
+    tpmd_sock = NULL;
+    return res;
+  }
+  return 0;
+}
+
+static void tpmd_disconnect(void)
+{
+  if (tpmd_sock != NULL) tpmd_sock->ops->release(tpmd_sock);
+  tpmd_sock = NULL;
+}
+
+static int tpmd_handle_command(const uint8_t *in, uint32_t in_size)
+{
+  int res;
+  mm_segment_t oldmm;
+  struct msghdr msg;
+  struct iovec iov;
+  /* send command to tpmd */
+  memset(&msg, 0, sizeof(msg));
+  iov.iov_base = (void*)in;
+  iov.iov_len = in_size;
+  msg.msg_iov = &iov;
+  msg.msg_iovlen = 1;
+  res = sock_sendmsg(tpmd_sock, &msg, in_size);
+  if (res < 0) {
+    error("sock_sendmsg() failed: %d\n", res);
+    return res;
+  }
+  /* receive response from tpmd */
+  tpm_response.size = TPM_CMD_BUF_SIZE;
+  tpm_response.data = kmalloc(tpm_response.size, GFP_KERNEL);
+  if (tpm_response.data == NULL) return -1;
+  memset(&msg, 0, sizeof(msg));
+  iov.iov_base = (void*)tpm_response.data;
+  iov.iov_len = tpm_response.size;
+  msg.msg_iov = &iov;
+  msg.msg_iovlen = 1;
+  oldmm = get_fs();
+  set_fs(KERNEL_DS);
+  res = sock_recvmsg(tpmd_sock, &msg, tpm_response.size, 0);
+  set_fs(oldmm);
+  if (res < 0) {
+    error("sock_recvmsg() failed: %d\n", res);
+    tpm_response.data = NULL;
+    return res;
+  }
+  tpm_response.size = res;
+  return 0;
+}
+
+static int tpm_open(struct inode *inode, struct file *file)
+{
+  int res;
+  debug("%s()", __FUNCTION__);
+  if (test_and_set_bit(TPM_STATE_IS_OPEN, (void*)&module_state)) return -EBUSY;
+  down(&tpm_mutex);
+  res = tpmd_connect(tpmd_socket_name);
+  up(&tpm_mutex);
+  if (res != 0) {
+    clear_bit(TPM_STATE_IS_OPEN, (void*)&module_state);
+    return -EIO;
+  }
+  return 0;
+}
+
+static int tpm_release(struct inode *inode, struct file *file)
+{
+  debug("%s()", __FUNCTION__);
+  down(&tpm_mutex);
+  if (tpm_response.data != NULL) {
+    kfree(tpm_response.data);
+    tpm_response.data = NULL;
+  }
+  tpmd_disconnect();
+  up(&tpm_mutex);
+  clear_bit(TPM_STATE_IS_OPEN, (void*)&module_state);
+  return 0;
+}
+
+static ssize_t tpm_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+  debug("%s(%zd)", __FUNCTION__, count);
+  down(&tpm_mutex);
+  if (tpm_response.data != NULL) {
+    count = min(count, (size_t)tpm_response.size - (size_t)*ppos);
+    count -= copy_to_user(buf, &tpm_response.data[*ppos], count);
+    *ppos += count;
+    if ((size_t)tpm_response.size == (size_t)*ppos) {
+      kfree(tpm_response.data);
+      tpm_response.data = NULL;
+    }
+  } else {
+    count = 0;
+  }
+  up(&tpm_mutex);
+  return count;
+}
+
+static ssize_t tpm_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
+{
+  debug("%s(%zd)", __FUNCTION__, count);
+  down(&tpm_mutex);
+  *ppos = 0;
+  if (tpm_response.data != NULL) {
+    kfree(tpm_response.data);
+    tpm_response.data = NULL;
+  }
+  if (tpmd_handle_command(buf, count) != 0) { 
+    count = -EILSEQ;
+    tpm_response.data = NULL;
+  }
+  up(&tpm_mutex);
+  return count;
+}
+
+#define TPMIOC_CANCEL   _IO('T', 0x00)
+#define TPMIOC_TRANSMIT _IO('T', 0x01)
+
+static int tpm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+  debug("%s(%d, %p)", __FUNCTION__, cmd, (char*)arg);
+  if (cmd == TPMIOC_TRANSMIT) {
+    uint32_t count = ntohl(*(uint32_t*)(arg + 2));
+    down(&tpm_mutex);
+    if (tpm_response.data != NULL) {
+      kfree(tpm_response.data);
+      tpm_response.data = NULL;
+    }
+    if (tpmd_handle_command((char*)arg, count) == 0) {
+      tpm_response.size -= copy_to_user((char*)arg, tpm_response.data, tpm_response.size);
+      kfree(tpm_response.data);
+      tpm_response.data = NULL;
+    } else {
+      tpm_response.size = 0;
+      tpm_response.data = NULL;
+    }
+    up(&tpm_mutex);
+    return tpm_response.size;
+  }
+  return -1;
+}
+
+struct file_operations fops = {
+  .owner   = THIS_MODULE,
+  .open    = tpm_open,
+  .release = tpm_release,
+  .read    = tpm_read,
+  .write   = tpm_write,
+  .ioctl   = tpm_ioctl,
+};
+
+static struct miscdevice tpm_dev = {
+  .minor      = TPM_DEVICE_MINOR, 
+  .name       = TPM_DEVICE_ID, 
+  .fops       = &fops,
+};
+
+int __init init_tpm_module(void)
+{
+  int res = misc_register(&tpm_dev);
+  if (res != 0) {
+    error("misc_register() failed for minor %d\n", TPM_DEVICE_MINOR);
+    return res;
+  }
+  /* initialize variables */
+  sema_init(&tpm_mutex, 1);
+  module_state = 0;
+  tpm_response.data = NULL;
+  tpm_response.size = 0;
+  tpmd_sock = NULL;
+  return 0;
+}
+
+void __exit cleanup_tpm_module(void)
+{
+  misc_deregister(&tpm_dev);
+  tpmd_disconnect();
+  if (tpm_response.data != NULL) kfree(tpm_response.data);
+}
+
+module_init(init_tpm_module);
+module_exit(cleanup_tpm_module);
+
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
new file mode 100644
index 0000000..94bb440
--- /dev/null
+++ b/drivers/char/tty_io.c
@@ -0,0 +1,3154 @@
+/*
+ *  linux/drivers/char/tty_io.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+/*
+ * 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles
+ * or rs-channels. It also implements echoing, cooked mode etc.
+ *
+ * Kill-line thanks to John T Kohl, who also corrected VMIN = VTIME = 0.
+ *
+ * Modified by Theodore Ts'o, 9/14/92, to dynamically allocate the
+ * tty_struct and tty_queue structures.  Previously there was an array
+ * of 256 tty_struct's which was statically allocated, and the
+ * tty_queue structures were allocated at boot time.  Both are now
+ * dynamically allocated only when the tty is open.
+ *
+ * Also restructured routines so that there is more of a separation
+ * between the high-level tty routines (tty_io.c and tty_ioctl.c) and
+ * the low-level tty routines (serial.c, pty.c, console.c).  This
+ * makes for cleaner and more compact code.  -TYT, 9/17/92
+ *
+ * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines
+ * which can be dynamically activated and de-activated by the line
+ * discipline handling modules (like SLIP).
+ *
+ * NOTE: pay no attention to the line discipline code (yet); its
+ * interface is still subject to change in this version...
+ * -- TYT, 1/31/92
+ *
+ * Added functionality to the OPOST tty handling.  No delays, but all
+ * other bits should be there.
+ *	-- Nick Holloway <alfie@dcs.warwick.ac.uk>, 27th May 1993.
+ *
+ * Rewrote canonical mode and added more termios flags.
+ * 	-- julian@uhunix.uhcc.hawaii.edu (J. Cowley), 13Jan94
+ *
+ * Reorganized FASYNC support so mouse code can share it.
+ *	-- ctm@ardi.com, 9Sep95
+ *
+ * New TIOCLINUX variants added.
+ *	-- mj@k332.feld.cvut.cz, 19-Nov-95
+ *
+ * Restrict vt switching via ioctl()
+ *      -- grif@cs.ucr.edu, 5-Dec-95
+ *
+ * Move console and virtual terminal code to more appropriate files,
+ * implement CONFIG_VT and generalize console device interface.
+ *	-- Marko Kohtala <Marko.Kohtala@hut.fi>, March 97
+ *
+ * Rewrote tty_init_dev and tty_release_dev to eliminate races.
+ *	-- Bill Hawes <whawes@star.net>, June 97
+ *
+ * Added devfs support.
+ *      -- C. Scott Ananian <cananian@alumni.princeton.edu>, 13-Jan-1998
+ *
+ * Added support for a Unix98-style ptmx device.
+ *      -- C. Scott Ananian <cananian@alumni.princeton.edu>, 14-Jan-1998
+ *
+ * Reduced memory usage for older ARM systems
+ *      -- Russell King <rmk@arm.linux.org.uk>
+ *
+ * Move do_SAK() into process context.  Less stack use in devfs functions.
+ * alloc_tty_struct() always uses kmalloc()
+ *			 -- Andrew Morton <andrewm@uow.edu.eu> 17Mar01
+ */
+
+#include <linux/types.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/devpts_fs.h>
+#include <linux/file.h>
+#include <linux/fdtable.h>
+#include <linux/console.h>
+#include <linux/timer.h>
+#include <linux/ctype.h>
+#include <linux/kd.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/smp_lock.h>
+#include <linux/device.h>
+#include <linux/wait.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/seq_file.h>
+
+#include <linux/uaccess.h>
+#include <asm/system.h>
+
+#include <linux/kbd_kern.h>
+#include <linux/vt_kern.h>
+#include <linux/selection.h>
+
+#include <linux/kmod.h>
+#include <linux/nsproxy.h>
+
+#undef TTY_DEBUG_HANGUP
+
+#define TTY_PARANOIA_CHECK 1
+#define CHECK_TTY_COUNT 1
+
+struct ktermios tty_std_termios = {	/* for the benefit of tty drivers  */
+	.c_iflag = ICRNL | IXON,
+	.c_oflag = OPOST | ONLCR,
+	.c_cflag = B38400 | CS8 | CREAD | HUPCL,
+	.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |
+		   ECHOCTL | ECHOKE | IEXTEN,
+	.c_cc = INIT_C_CC,
+	.c_ispeed = 38400,
+	.c_ospeed = 38400
+};
+
+EXPORT_SYMBOL(tty_std_termios);
+
+/* This list gets poked at by procfs and various bits of boot up code. This
+   could do with some rationalisation such as pulling the tty proc function
+   into this file */
+
+LIST_HEAD(tty_drivers);			/* linked list of tty drivers */
+
+/* Mutex to protect creating and releasing a tty. This is shared with
+   vt.c for deeply disgusting hack reasons */
+DEFINE_MUTEX(tty_mutex);
+EXPORT_SYMBOL(tty_mutex);
+
+static ssize_t tty_read(struct file *, char __user *, size_t, loff_t *);
+static ssize_t tty_write(struct file *, const char __user *, size_t, loff_t *);
+ssize_t redirected_tty_write(struct file *, const char __user *,
+							size_t, loff_t *);
+static unsigned int tty_poll(struct file *, poll_table *);
+static int tty_open(struct inode *, struct file *);
+long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+#ifdef CONFIG_COMPAT
+static long tty_compat_ioctl(struct file *file, unsigned int cmd,
+				unsigned long arg);
+#else
+#define tty_compat_ioctl NULL
+#endif
+static int tty_fasync(int fd, struct file *filp, int on);
+static void release_tty(struct tty_struct *tty, int idx);
+static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty);
+static void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty);
+
+/**
+ *	alloc_tty_struct	-	allocate a tty object
+ *
+ *	Return a new empty tty structure. The data fields have not
+ *	been initialized in any way but has been zeroed
+ *
+ *	Locking: none
+ */
+
+struct tty_struct *alloc_tty_struct(void)
+{
+	return kzalloc(sizeof(struct tty_struct), GFP_KERNEL);
+}
+
+/**
+ *	free_tty_struct		-	free a disused tty
+ *	@tty: tty struct to free
+ *
+ *	Free the write buffers, tty queue and tty memory itself.
+ *
+ *	Locking: none. Must be called after tty is definitely unused
+ */
+
+void free_tty_struct(struct tty_struct *tty)
+{
+	kfree(tty->write_buf);
+	tty_buffer_free_all(tty);
+	kfree(tty);
+}
+
+#define TTY_NUMBER(tty) ((tty)->index + (tty)->driver->name_base)
+
+/**
+ *	tty_name	-	return tty naming
+ *	@tty: tty structure
+ *	@buf: buffer for output
+ *
+ *	Convert a tty structure into a name. The name reflects the kernel
+ *	naming policy and if udev is in use may not reflect user space
+ *
+ *	Locking: none
+ */
+
+char *tty_name(struct tty_struct *tty, char *buf)
+{
+	if (!tty) /* Hmm.  NULL pointer.  That's fun. */
+		strcpy(buf, "NULL tty");
+	else
+		strcpy(buf, tty->name);
+	return buf;
+}
+
+EXPORT_SYMBOL(tty_name);
+
+int tty_paranoia_check(struct tty_struct *tty, struct inode *inode,
+			      const char *routine)
+{
+#ifdef TTY_PARANOIA_CHECK
+	if (!tty) {
+		printk(KERN_WARNING
+			"null TTY for (%d:%d) in %s\n",
+			imajor(inode), iminor(inode), routine);
+		return 1;
+	}
+	if (tty->magic != TTY_MAGIC) {
+		printk(KERN_WARNING
+			"bad magic number for tty struct (%d:%d) in %s\n",
+			imajor(inode), iminor(inode), routine);
+		return 1;
+	}
+#endif
+	return 0;
+}
+
+static int check_tty_count(struct tty_struct *tty, const char *routine)
+{
+#ifdef CHECK_TTY_COUNT
+	struct list_head *p;
+	int count = 0;
+
+	file_list_lock();
+	list_for_each(p, &tty->tty_files) {
+		count++;
+	}
+	file_list_unlock();
+	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
+	    tty->driver->subtype == PTY_TYPE_SLAVE &&
+	    tty->link && tty->link->count)
+		count++;
+	if (tty->count != count) {
+		printk(KERN_WARNING "Warning: dev (%s) tty->count(%d) "
+				    "!= #fd's(%d) in %s\n",
+		       tty->name, tty->count, count, routine);
+		return count;
+	}
+#endif
+	return 0;
+}
+
+/**
+ *	get_tty_driver		-	find device of a tty
+ *	@dev_t: device identifier
+ *	@index: returns the index of the tty
+ *
+ *	This routine returns a tty driver structure, given a device number
+ *	and also passes back the index number.
+ *
+ *	Locking: caller must hold tty_mutex
+ */
+
+static struct tty_driver *get_tty_driver(dev_t device, int *index)
+{
+	struct tty_driver *p;
+
+	list_for_each_entry(p, &tty_drivers, tty_drivers) {
+		dev_t base = MKDEV(p->major, p->minor_start);
+		if (device < base || device >= base + p->num)
+			continue;
+		*index = device - base;
+		return tty_driver_kref_get(p);
+	}
+	return NULL;
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+
+/**
+ *	tty_find_polling_driver	-	find device of a polled tty
+ *	@name: name string to match
+ *	@line: pointer to resulting tty line nr
+ *
+ *	This routine returns a tty driver structure, given a name
+ *	and the condition that the tty driver is capable of polled
+ *	operation.
+ */
+struct tty_driver *tty_find_polling_driver(char *name, int *line)
+{
+	struct tty_driver *p, *res = NULL;
+	int tty_line = 0;
+	int len;
+	char *str, *stp;
+
+	for (str = name; *str; str++)
+		if ((*str >= '0' && *str <= '9') || *str == ',')
+			break;
+	if (!*str)
+		return NULL;
+
+	len = str - name;
+	tty_line = simple_strtoul(str, &str, 10);
+
+	mutex_lock(&tty_mutex);
+	/* Search through the tty devices to look for a match */
+	list_for_each_entry(p, &tty_drivers, tty_drivers) {
+		if (strncmp(name, p->name, len) != 0)
+			continue;
+		stp = str;
+		if (*stp == ',')
+			stp++;
+		if (*stp == '\0')
+			stp = NULL;
+
+		if (tty_line >= 0 && tty_line <= p->num && p->ops &&
+		    p->ops->poll_init && !p->ops->poll_init(p, tty_line, stp)) {
+			res = tty_driver_kref_get(p);
+			*line = tty_line;
+			break;
+		}
+	}
+	mutex_unlock(&tty_mutex);
+
+	return res;
+}
+EXPORT_SYMBOL_GPL(tty_find_polling_driver);
+#endif
+
+/**
+ *	tty_check_change	-	check for POSIX terminal changes
+ *	@tty: tty to check
+ *
+ *	If we try to write to, or set the state of, a terminal and we're
+ *	not in the foreground, send a SIGTTOU.  If the signal is blocked or
+ *	ignored, go ahead and perform the operation.  (POSIX 7.2)
+ *
+ *	Locking: ctrl_lock
+ */
+
+int tty_check_change(struct tty_struct *tty)
+{
+	unsigned long flags;
+	int ret = 0;
+
+	if (current->signal->tty != tty)
+		return 0;
+
+	spin_lock_irqsave(&tty->ctrl_lock, flags);
+
+	if (!tty->pgrp) {
+		printk(KERN_WARNING "tty_check_change: tty->pgrp == NULL!\n");
+		goto out_unlock;
+	}
+	if (task_pgrp(current) == tty->pgrp)
+		goto out_unlock;
+	spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+	if (is_ignored(SIGTTOU))
+		goto out;
+	if (is_current_pgrp_orphaned()) {
+		ret = -EIO;
+		goto out;
+	}
+	kill_pgrp(task_pgrp(current), SIGTTOU, 1);
+	set_thread_flag(TIF_SIGPENDING);
+	ret = -ERESTARTSYS;
+out:
+	return ret;
+out_unlock:
+	spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+	return ret;
+}
+
+EXPORT_SYMBOL(tty_check_change);
+
+static ssize_t hung_up_tty_read(struct file *file, char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	return 0;
+}
+
+static ssize_t hung_up_tty_write(struct file *file, const char __user *buf,
+				 size_t count, loff_t *ppos)
+{
+	return -EIO;
+}
+
+/* No kernel lock held - none needed ;) */
+static unsigned int hung_up_tty_poll(struct file *filp, poll_table *wait)
+{
+	return POLLIN | POLLOUT | POLLERR | POLLHUP | POLLRDNORM | POLLWRNORM;
+}
+
+static long hung_up_tty_ioctl(struct file *file, unsigned int cmd,
+		unsigned long arg)
+{
+	return cmd == TIOCSPGRP ? -ENOTTY : -EIO;
+}
+
+static long hung_up_tty_compat_ioctl(struct file *file,
+				     unsigned int cmd, unsigned long arg)
+{
+	return cmd == TIOCSPGRP ? -ENOTTY : -EIO;
+}
+
+static const struct file_operations tty_fops = {
+	.llseek		= no_llseek,
+	.read		= tty_read,
+	.write		= tty_write,
+	.poll		= tty_poll,
+	.unlocked_ioctl	= tty_ioctl,
+	.compat_ioctl	= tty_compat_ioctl,
+	.open		= tty_open,
+	.release	= tty_release,
+	.fasync		= tty_fasync,
+};
+
+static const struct file_operations console_fops = {
+	.llseek		= no_llseek,
+	.read		= tty_read,
+	.write		= redirected_tty_write,
+	.poll		= tty_poll,
+	.unlocked_ioctl	= tty_ioctl,
+	.compat_ioctl	= tty_compat_ioctl,
+	.open		= tty_open,
+	.release	= tty_release,
+	.fasync		= tty_fasync,
+};
+
+static const struct file_operations hung_up_tty_fops = {
+	.llseek		= no_llseek,
+	.read		= hung_up_tty_read,
+	.write		= hung_up_tty_write,
+	.poll		= hung_up_tty_poll,
+	.unlocked_ioctl	= hung_up_tty_ioctl,
+	.compat_ioctl	= hung_up_tty_compat_ioctl,
+	.release	= tty_release,
+};
+
+static DEFINE_SPINLOCK(redirect_lock);
+static struct file *redirect;
+
+/**
+ *	tty_wakeup	-	request more data
+ *	@tty: terminal
+ *
+ *	Internal and external helper for wakeups of tty. This function
+ *	informs the line discipline if present that the driver is ready
+ *	to receive more output data.
+ */
+
+void tty_wakeup(struct tty_struct *tty)
+{
+	struct tty_ldisc *ld;
+
+	if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) {
+		ld = tty_ldisc_ref(tty);
+		if (ld) {
+			if (ld->ops->write_wakeup)
+				ld->ops->write_wakeup(tty);
+			tty_ldisc_deref(ld);
+		}
+	}
+	wake_up_interruptible_poll(&tty->write_wait, POLLOUT);
+}
+
+EXPORT_SYMBOL_GPL(tty_wakeup);
+
+/**
+ *	do_tty_hangup		-	actual handler for hangup events
+ *	@work: tty device
+ *
+ *	This can be called by the "eventd" kernel thread.  That is process
+ *	synchronous but doesn't hold any locks, so we need to make sure we
+ *	have the appropriate locks for what we're doing.
+ *
+ *	The hangup event clears any pending redirections onto the hung up
+ *	device. It ensures future writes will error and it does the needed
+ *	line discipline hangup and signal delivery. The tty object itself
+ *	remains intact.
+ *
+ *	Locking:
+ *		BKL
+ *		  redirect lock for undoing redirection
+ *		  file list lock for manipulating list of ttys
+ *		  tty_ldisc_lock from called functions
+ *		  termios_mutex resetting termios data
+ *		  tasklist_lock to walk task list for hangup event
+ *		    ->siglock to protect ->signal/->sighand
+ */
+static void do_tty_hangup(struct work_struct *work)
+{
+	struct tty_struct *tty =
+		container_of(work, struct tty_struct, hangup_work);
+	struct file *cons_filp = NULL;
+	struct file *filp, *f = NULL;
+	struct task_struct *p;
+	int    closecount = 0, n;
+	unsigned long flags;
+	int refs = 0;
+
+	if (!tty)
+		return;
+
+
+	spin_lock(&redirect_lock);
+	if (redirect && redirect->private_data == tty) {
+		f = redirect;
+		redirect = NULL;
+	}
+	spin_unlock(&redirect_lock);
+
+	/* inuse_filps is protected by the single kernel lock */
+	lock_kernel();
+	check_tty_count(tty, "do_tty_hangup");
+
+	file_list_lock();
+	/* This breaks for file handles being sent over AF_UNIX sockets ? */
+	list_for_each_entry(filp, &tty->tty_files, f_u.fu_list) {
+		if (filp->f_op->write == redirected_tty_write)
+			cons_filp = filp;
+		if (filp->f_op->write != tty_write)
+			continue;
+		closecount++;
+		tty_fasync(-1, filp, 0);	/* can't block */
+		filp->f_op = &hung_up_tty_fops;
+	}
+	file_list_unlock();
+
+	tty_ldisc_hangup(tty);
+
+	read_lock(&tasklist_lock);
+	if (tty->session) {
+		do_each_pid_task(tty->session, PIDTYPE_SID, p) {
+			spin_lock_irq(&p->sighand->siglock);
+			if (p->signal->tty == tty) {
+				p->signal->tty = NULL;
+				/* We defer the dereferences outside fo
+				   the tasklist lock */
+				refs++;
+			}
+			if (!p->signal->leader) {
+				spin_unlock_irq(&p->sighand->siglock);
+				continue;
+			}
+			__group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p);
+			__group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p);
+			put_pid(p->signal->tty_old_pgrp);  /* A noop */
+			spin_lock_irqsave(&tty->ctrl_lock, flags);
+			if (tty->pgrp)
+				p->signal->tty_old_pgrp = get_pid(tty->pgrp);
+			spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+			spin_unlock_irq(&p->sighand->siglock);
+		} while_each_pid_task(tty->session, PIDTYPE_SID, p);
+	}
+	read_unlock(&tasklist_lock);
+
+	spin_lock_irqsave(&tty->ctrl_lock, flags);
+	clear_bit(TTY_THROTTLED, &tty->flags);
+	clear_bit(TTY_PUSH, &tty->flags);
+	clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+	put_pid(tty->session);
+	put_pid(tty->pgrp);
+	tty->session = NULL;
+	tty->pgrp = NULL;
+	tty->ctrl_status = 0;
+	set_bit(TTY_HUPPED, &tty->flags);
+	spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+
+	/* Account for the p->signal references we killed */
+	while (refs--)
+		tty_kref_put(tty);
+
+	/*
+	 * If one of the devices matches a console pointer, we
+	 * cannot just call hangup() because that will cause
+	 * tty->count and state->count to go out of sync.
+	 * So we just call close() the right number of times.
+	 */
+	if (cons_filp) {
+		if (tty->ops->close)
+			for (n = 0; n < closecount; n++)
+				tty->ops->close(tty, cons_filp);
+	} else if (tty->ops->hangup)
+		(tty->ops->hangup)(tty);
+	/*
+	 * We don't want to have driver/ldisc interactions beyond
+	 * the ones we did here. The driver layer expects no
+	 * calls after ->hangup() from the ldisc side. However we
+	 * can't yet guarantee all that.
+	 */
+	set_bit(TTY_HUPPED, &tty->flags);
+	tty_ldisc_enable(tty);
+	unlock_kernel();
+	if (f)
+		fput(f);
+}
+
+/**
+ *	tty_hangup		-	trigger a hangup event
+ *	@tty: tty to hangup
+ *
+ *	A carrier loss (virtual or otherwise) has occurred on this like
+ *	schedule a hangup sequence to run after this event.
+ */
+
+void tty_hangup(struct tty_struct *tty)
+{
+#ifdef TTY_DEBUG_HANGUP
+	char	buf[64];
+	printk(KERN_DEBUG "%s hangup...\n", tty_name(tty, buf));
+#endif
+	schedule_work(&tty->hangup_work);
+}
+
+EXPORT_SYMBOL(tty_hangup);
+
+/**
+ *	tty_vhangup		-	process vhangup
+ *	@tty: tty to hangup
+ *
+ *	The user has asked via system call for the terminal to be hung up.
+ *	We do this synchronously so that when the syscall returns the process
+ *	is complete. That guarantee is necessary for security reasons.
+ */
+
+void tty_vhangup(struct tty_struct *tty)
+{
+#ifdef TTY_DEBUG_HANGUP
+	char	buf[64];
+
+	printk(KERN_DEBUG "%s vhangup...\n", tty_name(tty, buf));
+#endif
+	do_tty_hangup(&tty->hangup_work);
+}
+
+EXPORT_SYMBOL(tty_vhangup);
+
+/**
+ *	tty_vhangup_self	-	process vhangup for own ctty
+ *
+ *	Perform a vhangup on the current controlling tty
+ */
+
+void tty_vhangup_self(void)
+{
+	struct tty_struct *tty;
+
+	tty = get_current_tty();
+	if (tty) {
+		tty_vhangup(tty);
+		tty_kref_put(tty);
+	}
+}
+
+/**
+ *	tty_hung_up_p		-	was tty hung up
+ *	@filp: file pointer of tty
+ *
+ *	Return true if the tty has been subject to a vhangup or a carrier
+ *	loss
+ */
+
+int tty_hung_up_p(struct file *filp)
+{
+	return (filp->f_op == &hung_up_tty_fops);
+}
+
+EXPORT_SYMBOL(tty_hung_up_p);
+
+static void session_clear_tty(struct pid *session)
+{
+	struct task_struct *p;
+	do_each_pid_task(session, PIDTYPE_SID, p) {
+		proc_clear_tty(p);
+	} while_each_pid_task(session, PIDTYPE_SID, p);
+}
+
+/**
+ *	disassociate_ctty	-	disconnect controlling tty
+ *	@on_exit: true if exiting so need to "hang up" the session
+ *
+ *	This function is typically called only by the session leader, when
+ *	it wants to disassociate itself from its controlling tty.
+ *
+ *	It performs the following functions:
+ * 	(1)  Sends a SIGHUP and SIGCONT to the foreground process group
+ * 	(2)  Clears the tty from being controlling the session
+ * 	(3)  Clears the controlling tty for all processes in the
+ * 		session group.
+ *
+ *	The argument on_exit is set to 1 if called when a process is
+ *	exiting; it is 0 if called by the ioctl TIOCNOTTY.
+ *
+ *	Locking:
+ *		BKL is taken for hysterical raisins
+ *		  tty_mutex is taken to protect tty
+ *		  ->siglock is taken to protect ->signal/->sighand
+ *		  tasklist_lock is taken to walk process list for sessions
+ *		    ->siglock is taken to protect ->signal/->sighand
+ */
+
+void disassociate_ctty(int on_exit)
+{
+	struct tty_struct *tty;
+	struct pid *tty_pgrp = NULL;
+
+	if (!current->signal->leader)
+		return;
+
+	tty = get_current_tty();
+	if (tty) {
+		tty_pgrp = get_pid(tty->pgrp);
+		lock_kernel();
+		if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY)
+			tty_vhangup(tty);
+		unlock_kernel();
+		tty_kref_put(tty);
+	} else if (on_exit) {
+		struct pid *old_pgrp;
+		spin_lock_irq(&current->sighand->siglock);
+		old_pgrp = current->signal->tty_old_pgrp;
+		current->signal->tty_old_pgrp = NULL;
+		spin_unlock_irq(&current->sighand->siglock);
+		if (old_pgrp) {
+			kill_pgrp(old_pgrp, SIGHUP, on_exit);
+			kill_pgrp(old_pgrp, SIGCONT, on_exit);
+			put_pid(old_pgrp);
+		}
+		return;
+	}
+	if (tty_pgrp) {
+		kill_pgrp(tty_pgrp, SIGHUP, on_exit);
+		if (!on_exit)
+			kill_pgrp(tty_pgrp, SIGCONT, on_exit);
+		put_pid(tty_pgrp);
+	}
+
+	spin_lock_irq(&current->sighand->siglock);
+	put_pid(current->signal->tty_old_pgrp);
+	current->signal->tty_old_pgrp = NULL;
+	spin_unlock_irq(&current->sighand->siglock);
+
+	tty = get_current_tty();
+	if (tty) {
+		unsigned long flags;
+		spin_lock_irqsave(&tty->ctrl_lock, flags);
+		put_pid(tty->session);
+		put_pid(tty->pgrp);
+		tty->session = NULL;
+		tty->pgrp = NULL;
+		spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+		tty_kref_put(tty);
+	} else {
+#ifdef TTY_DEBUG_HANGUP
+		printk(KERN_DEBUG "error attempted to write to tty [0x%p]"
+		       " = NULL", tty);
+#endif
+	}
+
+	/* Now clear signal->tty under the lock */
+	read_lock(&tasklist_lock);
+	session_clear_tty(task_session(current));
+	read_unlock(&tasklist_lock);
+}
+
+/**
+ *
+ *	no_tty	- Ensure the current process does not have a controlling tty
+ */
+void no_tty(void)
+{
+	struct task_struct *tsk = current;
+	lock_kernel();
+	disassociate_ctty(0);
+	unlock_kernel();
+	proc_clear_tty(tsk);
+}
+
+
+/**
+ *	stop_tty	-	propagate flow control
+ *	@tty: tty to stop
+ *
+ *	Perform flow control to the driver. For PTY/TTY pairs we
+ *	must also propagate the TIOCKPKT status. May be called
+ *	on an already stopped device and will not re-call the driver
+ *	method.
+ *
+ *	This functionality is used by both the line disciplines for
+ *	halting incoming flow and by the driver. It may therefore be
+ *	called from any context, may be under the tty atomic_write_lock
+ *	but not always.
+ *
+ *	Locking:
+ *		Uses the tty control lock internally
+ */
+
+void stop_tty(struct tty_struct *tty)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&tty->ctrl_lock, flags);
+	if (tty->stopped) {
+		spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+		return;
+	}
+	tty->stopped = 1;
+	if (tty->link && tty->link->packet) {
+		tty->ctrl_status &= ~TIOCPKT_START;
+		tty->ctrl_status |= TIOCPKT_STOP;
+		wake_up_interruptible_poll(&tty->link->read_wait, POLLIN);
+	}
+	spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+	if (tty->ops->stop)
+		(tty->ops->stop)(tty);
+}
+
+EXPORT_SYMBOL(stop_tty);
+
+/**
+ *	start_tty	-	propagate flow control
+ *	@tty: tty to start
+ *
+ *	Start a tty that has been stopped if at all possible. Perform
+ *	any necessary wakeups and propagate the TIOCPKT status. If this
+ *	is the tty was previous stopped and is being started then the
+ *	driver start method is invoked and the line discipline woken.
+ *
+ *	Locking:
+ *		ctrl_lock
+ */
+
+void start_tty(struct tty_struct *tty)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&tty->ctrl_lock, flags);
+	if (!tty->stopped || tty->flow_stopped) {
+		spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+		return;
+	}
+	tty->stopped = 0;
+	if (tty->link && tty->link->packet) {
+		tty->ctrl_status &= ~TIOCPKT_STOP;
+		tty->ctrl_status |= TIOCPKT_START;
+		wake_up_interruptible_poll(&tty->link->read_wait, POLLIN);
+	}
+	spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+	if (tty->ops->start)
+		(tty->ops->start)(tty);
+	/* If we have a running line discipline it may need kicking */
+	tty_wakeup(tty);
+}
+
+EXPORT_SYMBOL(start_tty);
+
+/**
+ *	tty_read	-	read method for tty device files
+ *	@file: pointer to tty file
+ *	@buf: user buffer
+ *	@count: size of user buffer
+ *	@ppos: unused
+ *
+ *	Perform the read system call function on this terminal device. Checks
+ *	for hung up devices before calling the line discipline method.
+ *
+ *	Locking:
+ *		Locks the line discipline internally while needed. Multiple
+ *	read calls may be outstanding in parallel.
+ */
+
+static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
+			loff_t *ppos)
+{
+	int i;
+	struct tty_struct *tty;
+	struct inode *inode;
+	struct tty_ldisc *ld;
+
+	tty = (struct tty_struct *)file->private_data;
+	inode = file->f_path.dentry->d_inode;
+	if (tty_paranoia_check(tty, inode, "tty_read"))
+		return -EIO;
+	if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags)))
+		return -EIO;
+
+	/* We want to wait for the line discipline to sort out in this
+	   situation */
+	ld = tty_ldisc_ref_wait(tty);
+	if (ld->ops->read)
+		i = (ld->ops->read)(tty, file, buf, count);
+	else
+		i = -EIO;
+	tty_ldisc_deref(ld);
+	if (i > 0)
+		inode->i_atime = current_fs_time(inode->i_sb);
+	return i;
+}
+
+void tty_write_unlock(struct tty_struct *tty)
+{
+	mutex_unlock(&tty->atomic_write_lock);
+	wake_up_interruptible_poll(&tty->write_wait, POLLOUT);
+}
+
+int tty_write_lock(struct tty_struct *tty, int ndelay)
+{
+	if (!mutex_trylock(&tty->atomic_write_lock)) {
+		if (ndelay)
+			return -EAGAIN;
+		if (mutex_lock_interruptible(&tty->atomic_write_lock))
+			return -ERESTARTSYS;
+	}
+	return 0;
+}
+
+/*
+ * Split writes up in sane blocksizes to avoid
+ * denial-of-service type attacks
+ */
+static inline ssize_t do_tty_write(
+	ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t),
+	struct tty_struct *tty,
+	struct file *file,
+	const char __user *buf,
+	size_t count)
+{
+	ssize_t ret, written = 0;
+	unsigned int chunk;
+
+	ret = tty_write_lock(tty, file->f_flags & O_NDELAY);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * We chunk up writes into a temporary buffer. This
+	 * simplifies low-level drivers immensely, since they
+	 * don't have locking issues and user mode accesses.
+	 *
+	 * But if TTY_NO_WRITE_SPLIT is set, we should use a
+	 * big chunk-size..
+	 *
+	 * The default chunk-size is 2kB, because the NTTY
+	 * layer has problems with bigger chunks. It will
+	 * claim to be able to handle more characters than
+	 * it actually does.
+	 *
+	 * FIXME: This can probably go away now except that 64K chunks
+	 * are too likely to fail unless switched to vmalloc...
+	 */
+	chunk = 2048;
+	if (test_bit(TTY_NO_WRITE_SPLIT, &tty->flags))
+		chunk = 65536;
+	if (count < chunk)
+		chunk = count;
+
+	/* write_buf/write_cnt is protected by the atomic_write_lock mutex */
+	if (tty->write_cnt < chunk) {
+		unsigned char *buf_chunk;
+
+		if (chunk < 1024)
+			chunk = 1024;
+
+		buf_chunk = kmalloc(chunk, GFP_KERNEL);
+		if (!buf_chunk) {
+			ret = -ENOMEM;
+			goto out;
+		}
+		kfree(tty->write_buf);
+		tty->write_cnt = chunk;
+		tty->write_buf = buf_chunk;
+	}
+
+	/* Do the write .. */
+	for (;;) {
+		size_t size = count;
+		if (size > chunk)
+			size = chunk;
+		ret = -EFAULT;
+		if (copy_from_user(tty->write_buf, buf, size))
+			break;
+		ret = write(tty, file, tty->write_buf, size);
+		if (ret <= 0)
+			break;
+		written += ret;
+		buf += ret;
+		count -= ret;
+		if (!count)
+			break;
+		ret = -ERESTARTSYS;
+		if (signal_pending(current))
+			break;
+		cond_resched();
+	}
+	if (written) {
+		struct inode *inode = file->f_path.dentry->d_inode;
+		inode->i_mtime = current_fs_time(inode->i_sb);
+		ret = written;
+	}
+out:
+	tty_write_unlock(tty);
+	return ret;
+}
+
+/**
+ * tty_write_message - write a message to a certain tty, not just the console.
+ * @tty: the destination tty_struct
+ * @msg: the message to write
+ *
+ * This is used for messages that need to be redirected to a specific tty.
+ * We don't put it into the syslog queue right now maybe in the future if
+ * really needed.
+ *
+ * We must still hold the BKL and test the CLOSING flag for the moment.
+ */
+
+void tty_write_message(struct tty_struct *tty, char *msg)
+{
+	if (tty) {
+		mutex_lock(&tty->atomic_write_lock);
+		lock_kernel();
+		if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags)) {
+			unlock_kernel();
+			tty->ops->write(tty, msg, strlen(msg));
+		} else
+			unlock_kernel();
+		tty_write_unlock(tty);
+	}
+	return;
+}
+
+
+/**
+ *	tty_write		-	write method for tty device file
+ *	@file: tty file pointer
+ *	@buf: user data to write
+ *	@count: bytes to write
+ *	@ppos: unused
+ *
+ *	Write data to a tty device via the line discipline.
+ *
+ *	Locking:
+ *		Locks the line discipline as required
+ *		Writes to the tty driver are serialized by the atomic_write_lock
+ *	and are then processed in chunks to the device. The line discipline
+ *	write method will not be invoked in parallel for each device.
+ */
+
+static ssize_t tty_write(struct file *file, const char __user *buf,
+						size_t count, loff_t *ppos)
+{
+	struct tty_struct *tty;
+	struct inode *inode = file->f_path.dentry->d_inode;
+	ssize_t ret;
+	struct tty_ldisc *ld;
+
+	tty = (struct tty_struct *)file->private_data;
+	if (tty_paranoia_check(tty, inode, "tty_write"))
+		return -EIO;
+	if (!tty || !tty->ops->write ||
+		(test_bit(TTY_IO_ERROR, &tty->flags)))
+			return -EIO;
+	/* Short term debug to catch buggy drivers */
+	if (tty->ops->write_room == NULL)
+		printk(KERN_ERR "tty driver %s lacks a write_room method.\n",
+			tty->driver->name);
+	ld = tty_ldisc_ref_wait(tty);
+	if (!ld->ops->write)
+		ret = -EIO;
+	else
+		ret = do_tty_write(ld->ops->write, tty, file, buf, count);
+	tty_ldisc_deref(ld);
+	return ret;
+}
+
+ssize_t redirected_tty_write(struct file *file, const char __user *buf,
+						size_t count, loff_t *ppos)
+{
+	struct file *p = NULL;
+
+	spin_lock(&redirect_lock);
+	if (redirect) {
+		get_file(redirect);
+		p = redirect;
+	}
+	spin_unlock(&redirect_lock);
+
+	if (p) {
+		ssize_t res;
+		res = vfs_write(p, buf, count, &p->f_pos);
+		fput(p);
+		return res;
+	}
+	return tty_write(file, buf, count, ppos);
+}
+
+static char ptychar[] = "pqrstuvwxyzabcde";
+
+/**
+ *	pty_line_name	-	generate name for a pty
+ *	@driver: the tty driver in use
+ *	@index: the minor number
+ *	@p: output buffer of at least 6 bytes
+ *
+ *	Generate a name from a driver reference and write it to the output
+ *	buffer.
+ *
+ *	Locking: None
+ */
+static void pty_line_name(struct tty_driver *driver, int index, char *p)
+{
+	int i = index + driver->name_base;
+	/* ->name is initialized to "ttyp", but "tty" is expected */
+	sprintf(p, "%s%c%x",
+		driver->subtype == PTY_TYPE_SLAVE ? "tty" : driver->name,
+		ptychar[i >> 4 & 0xf], i & 0xf);
+}
+
+/**
+ *	tty_line_name	-	generate name for a tty
+ *	@driver: the tty driver in use
+ *	@index: the minor number
+ *	@p: output buffer of at least 7 bytes
+ *
+ *	Generate a name from a driver reference and write it to the output
+ *	buffer.
+ *
+ *	Locking: None
+ */
+static void tty_line_name(struct tty_driver *driver, int index, char *p)
+{
+	sprintf(p, "%s%d", driver->name, index + driver->name_base);
+}
+
+/**
+ *	tty_driver_lookup_tty() - find an existing tty, if any
+ *	@driver: the driver for the tty
+ *	@idx:	 the minor number
+ *
+ *	Return the tty, if found or ERR_PTR() otherwise.
+ *
+ *	Locking: tty_mutex must be held. If tty is found, the mutex must
+ *	be held until the 'fast-open' is also done. Will change once we
+ *	have refcounting in the driver and per driver locking
+ */
+static struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver,
+		struct inode *inode, int idx)
+{
+	struct tty_struct *tty;
+
+	if (driver->ops->lookup)
+		return driver->ops->lookup(driver, inode, idx);
+
+	tty = driver->ttys[idx];
+	return tty;
+}
+
+/**
+ *	tty_init_termios	-  helper for termios setup
+ *	@tty: the tty to set up
+ *
+ *	Initialise the termios structures for this tty. Thus runs under
+ *	the tty_mutex currently so we can be relaxed about ordering.
+ */
+
+int tty_init_termios(struct tty_struct *tty)
+{
+	struct ktermios *tp;
+	int idx = tty->index;
+
+	tp = tty->driver->termios[idx];
+	if (tp == NULL) {
+		tp = kzalloc(sizeof(struct ktermios[2]), GFP_KERNEL);
+		if (tp == NULL)
+			return -ENOMEM;
+		memcpy(tp, &tty->driver->init_termios,
+						sizeof(struct ktermios));
+		tty->driver->termios[idx] = tp;
+	}
+	tty->termios = tp;
+	tty->termios_locked = tp + 1;
+
+	/* Compatibility until drivers always set this */
+	tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios);
+	tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tty_init_termios);
+
+/**
+ *	tty_driver_install_tty() - install a tty entry in the driver
+ *	@driver: the driver for the tty
+ *	@tty: the tty
+ *
+ *	Install a tty object into the driver tables. The tty->index field
+ *	will be set by the time this is called. This method is responsible
+ *	for ensuring any need additional structures are allocated and
+ *	configured.
+ *
+ *	Locking: tty_mutex for now
+ */
+static int tty_driver_install_tty(struct tty_driver *driver,
+						struct tty_struct *tty)
+{
+	int idx = tty->index;
+	int ret;
+
+	if (driver->ops->install) {
+		lock_kernel();
+		ret = driver->ops->install(driver, tty);
+		unlock_kernel();
+		return ret;
+	}
+
+	if (tty_init_termios(tty) == 0) {
+		lock_kernel();
+		tty_driver_kref_get(driver);
+		tty->count++;
+		driver->ttys[idx] = tty;
+		unlock_kernel();
+		return 0;
+	}
+	return -ENOMEM;
+}
+
+/**
+ *	tty_driver_remove_tty() - remove a tty from the driver tables
+ *	@driver: the driver for the tty
+ *	@idx:	 the minor number
+ *
+ *	Remvoe a tty object from the driver tables. The tty->index field
+ *	will be set by the time this is called.
+ *
+ *	Locking: tty_mutex for now
+ */
+static void tty_driver_remove_tty(struct tty_driver *driver,
+						struct tty_struct *tty)
+{
+	if (driver->ops->remove)
+		driver->ops->remove(driver, tty);
+	else
+		driver->ttys[tty->index] = NULL;
+}
+
+/*
+ * 	tty_reopen()	- fast re-open of an open tty
+ * 	@tty	- the tty to open
+ *
+ *	Return 0 on success, -errno on error.
+ *
+ *	Locking: tty_mutex must be held from the time the tty was found
+ *		 till this open completes.
+ */
+static int tty_reopen(struct tty_struct *tty)
+{
+	struct tty_driver *driver = tty->driver;
+
+	if (test_bit(TTY_CLOSING, &tty->flags))
+		return -EIO;
+
+	if (driver->type == TTY_DRIVER_TYPE_PTY &&
+	    driver->subtype == PTY_TYPE_MASTER) {
+		/*
+		 * special case for PTY masters: only one open permitted,
+		 * and the slave side open count is incremented as well.
+		 */
+		if (tty->count)
+			return -EIO;
+
+		tty->link->count++;
+	}
+	tty->count++;
+	tty->driver = driver; /* N.B. why do this every time?? */
+
+	mutex_lock(&tty->ldisc_mutex);
+	WARN_ON(!test_bit(TTY_LDISC, &tty->flags));
+	mutex_unlock(&tty->ldisc_mutex);
+
+	return 0;
+}
+
+/**
+ *	tty_init_dev		-	initialise a tty device
+ *	@driver: tty driver we are opening a device on
+ *	@idx: device index
+ *	@ret_tty: returned tty structure
+ *	@first_ok: ok to open a new device (used by ptmx)
+ *
+ *	Prepare a tty device. This may not be a "new" clean device but
+ *	could also be an active device. The pty drivers require special
+ *	handling because of this.
+ *
+ *	Locking:
+ *		The function is called under the tty_mutex, which
+ *	protects us from the tty struct or driver itself going away.
+ *
+ *	On exit the tty device has the line discipline attached and
+ *	a reference count of 1. If a pair was created for pty/tty use
+ *	and the other was a pty master then it too has a reference count of 1.
+ *
+ * WSH 06/09/97: Rewritten to remove races and properly clean up after a
+ * failed open.  The new code protects the open with a mutex, so it's
+ * really quite straightforward.  The mutex locking can probably be
+ * relaxed for the (most common) case of reopening a tty.
+ */
+
+struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
+								int first_ok)
+{
+	struct tty_struct *tty;
+	int retval;
+
+	lock_kernel();
+	/* Check if pty master is being opened multiple times */
+	if (driver->subtype == PTY_TYPE_MASTER &&
+		(driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok) {
+		unlock_kernel();
+		return ERR_PTR(-EIO);
+	}
+	unlock_kernel();
+
+	/*
+	 * First time open is complex, especially for PTY devices.
+	 * This code guarantees that either everything succeeds and the
+	 * TTY is ready for operation, or else the table slots are vacated
+	 * and the allocated memory released.  (Except that the termios
+	 * and locked termios may be retained.)
+	 */
+
+	if (!try_module_get(driver->owner))
+		return ERR_PTR(-ENODEV);
+
+	tty = alloc_tty_struct();
+	if (!tty)
+		goto fail_no_mem;
+	initialize_tty_struct(tty, driver, idx);
+
+	retval = tty_driver_install_tty(driver, tty);
+	if (retval < 0) {
+		free_tty_struct(tty);
+		module_put(driver->owner);
+		return ERR_PTR(retval);
+	}
+
+	/*
+	 * Structures all installed ... call the ldisc open routines.
+	 * If we fail here just call release_tty to clean up.  No need
+	 * to decrement the use counts, as release_tty doesn't care.
+	 */
+	retval = tty_ldisc_setup(tty, tty->link);
+	if (retval)
+		goto release_mem_out;
+	return tty;
+
+fail_no_mem:
+	module_put(driver->owner);
+	return ERR_PTR(-ENOMEM);
+
+	/* call the tty release_tty routine to clean out this slot */
+release_mem_out:
+	if (printk_ratelimit())
+		printk(KERN_INFO "tty_init_dev: ldisc open failed, "
+				 "clearing slot %d\n", idx);
+	lock_kernel();
+	release_tty(tty, idx);
+	unlock_kernel();
+	return ERR_PTR(retval);
+}
+
+void tty_free_termios(struct tty_struct *tty)
+{
+	struct ktermios *tp;
+	int idx = tty->index;
+	/* Kill this flag and push into drivers for locking etc */
+	if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {
+		/* FIXME: Locking on ->termios array */
+		tp = tty->termios;
+		tty->driver->termios[idx] = NULL;
+		kfree(tp);
+	}
+}
+EXPORT_SYMBOL(tty_free_termios);
+
+void tty_shutdown(struct tty_struct *tty)
+{
+	tty_driver_remove_tty(tty->driver, tty);
+	tty_free_termios(tty);
+}
+EXPORT_SYMBOL(tty_shutdown);
+
+/**
+ *	release_one_tty		-	release tty structure memory
+ *	@kref: kref of tty we are obliterating
+ *
+ *	Releases memory associated with a tty structure, and clears out the
+ *	driver table slots. This function is called when a device is no longer
+ *	in use. It also gets called when setup of a device fails.
+ *
+ *	Locking:
+ *		tty_mutex - sometimes only
+ *		takes the file list lock internally when working on the list
+ *	of ttys that the driver keeps.
+ *
+ *	This method gets called from a work queue so that the driver private
+ *	cleanup ops can sleep (needed for USB at least)
+ */
+static void release_one_tty(struct work_struct *work)
+{
+	struct tty_struct *tty =
+		container_of(work, struct tty_struct, hangup_work);
+	struct tty_driver *driver = tty->driver;
+
+	if (tty->ops->cleanup)
+		tty->ops->cleanup(tty);
+
+	tty->magic = 0;
+	tty_driver_kref_put(driver);
+	module_put(driver->owner);
+
+	file_list_lock();
+	list_del_init(&tty->tty_files);
+	file_list_unlock();
+
+	put_pid(tty->pgrp);
+	put_pid(tty->session);
+	free_tty_struct(tty);
+}
+
+static void queue_release_one_tty(struct kref *kref)
+{
+	struct tty_struct *tty = container_of(kref, struct tty_struct, kref);
+
+	if (tty->ops->shutdown)
+		tty->ops->shutdown(tty);
+	else
+		tty_shutdown(tty);
+
+	/* The hangup queue is now free so we can reuse it rather than
+	   waste a chunk of memory for each port */
+	INIT_WORK(&tty->hangup_work, release_one_tty);
+	schedule_work(&tty->hangup_work);
+}
+
+/**
+ *	tty_kref_put		-	release a tty kref
+ *	@tty: tty device
+ *
+ *	Release a reference to a tty device and if need be let the kref
+ *	layer destruct the object for us
+ */
+
+void tty_kref_put(struct tty_struct *tty)
+{
+	if (tty)
+		kref_put(&tty->kref, queue_release_one_tty);
+}
+EXPORT_SYMBOL(tty_kref_put);
+
+/**
+ *	release_tty		-	release tty structure memory
+ *
+ *	Release both @tty and a possible linked partner (think pty pair),
+ *	and decrement the refcount of the backing module.
+ *
+ *	Locking:
+ *		tty_mutex - sometimes only
+ *		takes the file list lock internally when working on the list
+ *	of ttys that the driver keeps.
+ *		FIXME: should we require tty_mutex is held here ??
+ *
+ */
+static void release_tty(struct tty_struct *tty, int idx)
+{
+	/* This should always be true but check for the moment */
+	WARN_ON(tty->index != idx);
+
+	if (tty->link)
+		tty_kref_put(tty->link);
+	tty_kref_put(tty);
+}
+
+/**
+ *	tty_release		-	vfs callback for close
+ *	@inode: inode of tty
+ *	@filp: file pointer for handle to tty
+ *
+ *	Called the last time each file handle is closed that references
+ *	this tty. There may however be several such references.
+ *
+ *	Locking:
+ *		Takes bkl. See tty_release_dev
+ *
+ * Even releasing the tty structures is a tricky business.. We have
+ * to be very careful that the structures are all released at the
+ * same time, as interrupts might otherwise get the wrong pointers.
+ *
+ * WSH 09/09/97: rewritten to avoid some nasty race conditions that could
+ * lead to double frees or releasing memory still in use.
+ */
+
+int tty_release(struct inode *inode, struct file *filp)
+{
+	struct tty_struct *tty, *o_tty;
+	int	pty_master, tty_closing, o_tty_closing, do_sleep;
+	int	devpts;
+	int	idx;
+	char	buf[64];
+
+	tty = (struct tty_struct *)filp->private_data;
+	if (tty_paranoia_check(tty, inode, "tty_release_dev"))
+		return 0;
+
+	lock_kernel();
+	check_tty_count(tty, "tty_release_dev");
+
+	tty_fasync(-1, filp, 0);
+
+	idx = tty->index;
+	pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
+		      tty->driver->subtype == PTY_TYPE_MASTER);
+	devpts = (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) != 0;
+	o_tty = tty->link;
+
+#ifdef TTY_PARANOIA_CHECK
+	if (idx < 0 || idx >= tty->driver->num) {
+		printk(KERN_DEBUG "tty_release_dev: bad idx when trying to "
+				  "free (%s)\n", tty->name);
+		unlock_kernel();
+		return 0;
+	}
+	if (!devpts) {
+		if (tty != tty->driver->ttys[idx]) {
+			unlock_kernel();
+			printk(KERN_DEBUG "tty_release_dev: driver.table[%d] not tty "
+			       "for (%s)\n", idx, tty->name);
+			return 0;
+		}
+		if (tty->termios != tty->driver->termios[idx]) {
+			unlock_kernel();
+			printk(KERN_DEBUG "tty_release_dev: driver.termios[%d] not termios "
+			       "for (%s)\n",
+			       idx, tty->name);
+			return 0;
+		}
+	}
+#endif
+
+#ifdef TTY_DEBUG_HANGUP
+	printk(KERN_DEBUG "tty_release_dev of %s (tty count=%d)...",
+	       tty_name(tty, buf), tty->count);
+#endif
+
+#ifdef TTY_PARANOIA_CHECK
+	if (tty->driver->other &&
+	     !(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
+		if (o_tty != tty->driver->other->ttys[idx]) {
+			unlock_kernel();
+			printk(KERN_DEBUG "tty_release_dev: other->table[%d] "
+					  "not o_tty for (%s)\n",
+			       idx, tty->name);
+			return 0 ;
+		}
+		if (o_tty->termios != tty->driver->other->termios[idx]) {
+			unlock_kernel();
+			printk(KERN_DEBUG "tty_release_dev: other->termios[%d] "
+					  "not o_termios for (%s)\n",
+			       idx, tty->name);
+			return 0;
+		}
+		if (o_tty->link != tty) {
+			unlock_kernel();
+			printk(KERN_DEBUG "tty_release_dev: bad pty pointers\n");
+			return 0;
+		}
+	}
+#endif
+	if (tty->ops->close)
+		tty->ops->close(tty, filp);
+
+	unlock_kernel();
+	/*
+	 * Sanity check: if tty->count is going to zero, there shouldn't be
+	 * any waiters on tty->read_wait or tty->write_wait.  We test the
+	 * wait queues and kick everyone out _before_ actually starting to
+	 * close.  This ensures that we won't block while releasing the tty
+	 * structure.
+	 *
+	 * The test for the o_tty closing is necessary, since the master and
+	 * slave sides may close in any order.  If the slave side closes out
+	 * first, its count will be one, since the master side holds an open.
+	 * Thus this test wouldn't be triggered at the time the slave closes,
+	 * so we do it now.
+	 *
+	 * Note that it's possible for the tty to be opened again while we're
+	 * flushing out waiters.  By recalculating the closing flags before
+	 * each iteration we avoid any problems.
+	 */
+	while (1) {
+		/* Guard against races with tty->count changes elsewhere and
+		   opens on /dev/tty */
+
+		mutex_lock(&tty_mutex);
+		lock_kernel();
+		tty_closing = tty->count <= 1;
+		o_tty_closing = o_tty &&
+			(o_tty->count <= (pty_master ? 1 : 0));
+		do_sleep = 0;
+
+		if (tty_closing) {
+			if (waitqueue_active(&tty->read_wait)) {
+				wake_up_poll(&tty->read_wait, POLLIN);
+				do_sleep++;
+			}
+			if (waitqueue_active(&tty->write_wait)) {
+				wake_up_poll(&tty->write_wait, POLLOUT);
+				do_sleep++;
+			}
+		}
+		if (o_tty_closing) {
+			if (waitqueue_active(&o_tty->read_wait)) {
+				wake_up_poll(&o_tty->read_wait, POLLIN);
+				do_sleep++;
+			}
+			if (waitqueue_active(&o_tty->write_wait)) {
+				wake_up_poll(&o_tty->write_wait, POLLOUT);
+				do_sleep++;
+			}
+		}
+		if (!do_sleep)
+			break;
+
+		printk(KERN_WARNING "tty_release_dev: %s: read/write wait queue "
+				    "active!\n", tty_name(tty, buf));
+		unlock_kernel();
+		mutex_unlock(&tty_mutex);
+		schedule();
+	}
+
+	/*
+	 * The closing flags are now consistent with the open counts on
+	 * both sides, and we've completed the last operation that could
+	 * block, so it's safe to proceed with closing.
+	 */
+	if (pty_master) {
+		if (--o_tty->count < 0) {
+			printk(KERN_WARNING "tty_release_dev: bad pty slave count "
+					    "(%d) for %s\n",
+			       o_tty->count, tty_name(o_tty, buf));
+			o_tty->count = 0;
+		}
+	}
+	if (--tty->count < 0) {
+		printk(KERN_WARNING "tty_release_dev: bad tty->count (%d) for %s\n",
+		       tty->count, tty_name(tty, buf));
+		tty->count = 0;
+	}
+
+	/*
+	 * We've decremented tty->count, so we need to remove this file
+	 * descriptor off the tty->tty_files list; this serves two
+	 * purposes:
+	 *  - check_tty_count sees the correct number of file descriptors
+	 *    associated with this tty.
+	 *  - do_tty_hangup no longer sees this file descriptor as
+	 *    something that needs to be handled for hangups.
+	 */
+	file_kill(filp);
+	filp->private_data = NULL;
+
+	/*
+	 * Perform some housekeeping before deciding whether to return.
+	 *
+	 * Set the TTY_CLOSING flag if this was the last open.  In the
+	 * case of a pty we may have to wait around for the other side
+	 * to close, and TTY_CLOSING makes sure we can't be reopened.
+	 */
+	if (tty_closing)
+		set_bit(TTY_CLOSING, &tty->flags);
+	if (o_tty_closing)
+		set_bit(TTY_CLOSING, &o_tty->flags);
+
+	/*
+	 * If _either_ side is closing, make sure there aren't any
+	 * processes that still think tty or o_tty is their controlling
+	 * tty.
+	 */
+	if (tty_closing || o_tty_closing) {
+		read_lock(&tasklist_lock);
+		session_clear_tty(tty->session);
+		if (o_tty)
+			session_clear_tty(o_tty->session);
+		read_unlock(&tasklist_lock);
+	}
+
+	mutex_unlock(&tty_mutex);
+
+	/* check whether both sides are closing ... */
+	if (!tty_closing || (o_tty && !o_tty_closing)) {
+		unlock_kernel();
+		return 0;
+	}
+
+#ifdef TTY_DEBUG_HANGUP
+	printk(KERN_DEBUG "freeing tty structure...");
+#endif
+	/*
+	 * Ask the line discipline code to release its structures
+	 */
+	tty_ldisc_release(tty, o_tty);
+	/*
+	 * The release_tty function takes care of the details of clearing
+	 * the slots and preserving the termios structure.
+	 */
+	release_tty(tty, idx);
+
+	/* Make this pty number available for reallocation */
+	if (devpts)
+		devpts_kill_index(inode, idx);
+	unlock_kernel();
+	return 0;
+}
+
+/**
+ *	tty_open		-	open a tty device
+ *	@inode: inode of device file
+ *	@filp: file pointer to tty
+ *
+ *	tty_open and tty_release keep up the tty count that contains the
+ *	number of opens done on a tty. We cannot use the inode-count, as
+ *	different inodes might point to the same tty.
+ *
+ *	Open-counting is needed for pty masters, as well as for keeping
+ *	track of serial lines: DTR is dropped when the last close happens.
+ *	(This is not done solely through tty->count, now.  - Ted 1/27/92)
+ *
+ *	The termios state of a pty is reset on first open so that
+ *	settings don't persist across reuse.
+ *
+ *	Locking: tty_mutex protects tty, get_tty_driver and tty_init_dev work.
+ *		 tty->count should protect the rest.
+ *		 ->siglock protects ->signal/->sighand
+ */
+
+static int tty_open(struct inode *inode, struct file *filp)
+{
+	struct tty_struct *tty = NULL;
+	int noctty, retval;
+	struct tty_driver *driver;
+	int index;
+	dev_t device = inode->i_rdev;
+	unsigned saved_flags = filp->f_flags;
+
+	nonseekable_open(inode, filp);
+
+retry_open:
+	noctty = filp->f_flags & O_NOCTTY;
+	index  = -1;
+	retval = 0;
+
+	mutex_lock(&tty_mutex);
+	lock_kernel();
+
+	if (device == MKDEV(TTYAUX_MAJOR, 0)) {
+		tty = get_current_tty();
+		if (!tty) {
+			unlock_kernel();
+			mutex_unlock(&tty_mutex);
+			return -ENXIO;
+		}
+		driver = tty_driver_kref_get(tty->driver);
+		index = tty->index;
+		filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
+		/* noctty = 1; */
+		/* FIXME: Should we take a driver reference ? */
+		tty_kref_put(tty);
+		goto got_driver;
+	}
+#ifdef CONFIG_VT
+	if (device == MKDEV(TTY_MAJOR, 0)) {
+		extern struct tty_driver *console_driver;
+		driver = tty_driver_kref_get(console_driver);
+		index = fg_console;
+		noctty = 1;
+		goto got_driver;
+	}
+#endif
+	if (device == MKDEV(TTYAUX_MAJOR, 1)) {
+		struct tty_driver *console_driver = console_device(&index);
+		if (console_driver) {
+			driver = tty_driver_kref_get(console_driver);
+			if (driver) {
+				/* Don't let /dev/console block */
+				filp->f_flags |= O_NONBLOCK;
+				noctty = 1;
+				goto got_driver;
+			}
+		}
+		unlock_kernel();
+		mutex_unlock(&tty_mutex);
+		return -ENODEV;
+	}
+
+	driver = get_tty_driver(device, &index);
+	if (!driver) {
+		unlock_kernel();
+		mutex_unlock(&tty_mutex);
+		return -ENODEV;
+	}
+got_driver:
+	if (!tty) {
+		/* check whether we're reopening an existing tty */
+		tty = tty_driver_lookup_tty(driver, inode, index);
+
+		if (IS_ERR(tty)) {
+			unlock_kernel();
+			mutex_unlock(&tty_mutex);
+			return PTR_ERR(tty);
+		}
+	}
+
+	if (tty) {
+		retval = tty_reopen(tty);
+		if (retval)
+			tty = ERR_PTR(retval);
+	} else
+		tty = tty_init_dev(driver, index, 0);
+
+	mutex_unlock(&tty_mutex);
+	tty_driver_kref_put(driver);
+	if (IS_ERR(tty)) {
+		unlock_kernel();
+		return PTR_ERR(tty);
+	}
+
+	filp->private_data = tty;
+	file_move(filp, &tty->tty_files);
+	check_tty_count(tty, "tty_open");
+	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
+	    tty->driver->subtype == PTY_TYPE_MASTER)
+		noctty = 1;
+#ifdef TTY_DEBUG_HANGUP
+	printk(KERN_DEBUG "opening %s...", tty->name);
+#endif
+	if (!retval) {
+		if (tty->ops->open)
+			retval = tty->ops->open(tty, filp);
+		else
+			retval = -ENODEV;
+	}
+	filp->f_flags = saved_flags;
+
+	if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) &&
+						!capable(CAP_SYS_ADMIN))
+		retval = -EBUSY;
+
+	if (retval) {
+#ifdef TTY_DEBUG_HANGUP
+		printk(KERN_DEBUG "error %d in opening %s...", retval,
+		       tty->name);
+#endif
+		tty_release(inode, filp);
+		if (retval != -ERESTARTSYS) {
+			unlock_kernel();
+			return retval;
+		}
+		if (signal_pending(current)) {
+			unlock_kernel();
+			return retval;
+		}
+		schedule();
+		/*
+		 * Need to reset f_op in case a hangup happened.
+		 */
+		if (filp->f_op == &hung_up_tty_fops)
+			filp->f_op = &tty_fops;
+		unlock_kernel();
+		goto retry_open;
+	}
+	unlock_kernel();
+
+
+	mutex_lock(&tty_mutex);
+	lock_kernel();
+	spin_lock_irq(&current->sighand->siglock);
+	if (!noctty &&
+	    current->signal->leader &&
+	    !current->signal->tty &&
+	    tty->session == NULL)
+		__proc_set_tty(current, tty);
+	spin_unlock_irq(&current->sighand->siglock);
+	unlock_kernel();
+	mutex_unlock(&tty_mutex);
+	return 0;
+}
+
+
+
+/**
+ *	tty_poll	-	check tty status
+ *	@filp: file being polled
+ *	@wait: poll wait structures to update
+ *
+ *	Call the line discipline polling method to obtain the poll
+ *	status of the device.
+ *
+ *	Locking: locks called line discipline but ldisc poll method
+ *	may be re-entered freely by other callers.
+ */
+
+static unsigned int tty_poll(struct file *filp, poll_table *wait)
+{
+	struct tty_struct *tty;
+	struct tty_ldisc *ld;
+	int ret = 0;
+
+	tty = (struct tty_struct *)filp->private_data;
+	if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, "tty_poll"))
+		return 0;
+
+	ld = tty_ldisc_ref_wait(tty);
+	if (ld->ops->poll)
+		ret = (ld->ops->poll)(tty, filp, wait);
+	tty_ldisc_deref(ld);
+	return ret;
+}
+
+static int tty_fasync(int fd, struct file *filp, int on)
+{
+	struct tty_struct *tty;
+	unsigned long flags;
+	int retval = 0;
+
+	lock_kernel();
+	tty = (struct tty_struct *)filp->private_data;
+	if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, "tty_fasync"))
+		goto out;
+
+	retval = fasync_helper(fd, filp, on, &tty->fasync);
+	if (retval <= 0)
+		goto out;
+
+	if (on) {
+		enum pid_type type;
+		struct pid *pid;
+		if (!waitqueue_active(&tty->read_wait))
+			tty->minimum_to_wake = 1;
+		spin_lock_irqsave(&tty->ctrl_lock, flags);
+		if (tty->pgrp) {
+			pid = tty->pgrp;
+			type = PIDTYPE_PGID;
+		} else {
+			pid = task_pid(current);
+			type = PIDTYPE_PID;
+		}
+		get_pid(pid);
+		spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+		retval = __f_setown(filp, pid, type, 0);
+		put_pid(pid);
+		if (retval)
+			goto out;
+	} else {
+		if (!tty->fasync && !waitqueue_active(&tty->read_wait))
+			tty->minimum_to_wake = N_TTY_BUF_SIZE;
+	}
+	retval = 0;
+out:
+	unlock_kernel();
+	return retval;
+}
+
+/**
+ *	tiocsti			-	fake input character
+ *	@tty: tty to fake input into
+ *	@p: pointer to character
+ *
+ *	Fake input to a tty device. Does the necessary locking and
+ *	input management.
+ *
+ *	FIXME: does not honour flow control ??
+ *
+ *	Locking:
+ *		Called functions take tty_ldisc_lock
+ *		current->signal->tty check is safe without locks
+ *
+ *	FIXME: may race normal receive processing
+ */
+
+static int tiocsti(struct tty_struct *tty, char __user *p)
+{
+	char ch, mbz = 0;
+	struct tty_ldisc *ld;
+
+	if ((current->signal->tty != tty) && !capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	if (get_user(ch, p))
+		return -EFAULT;
+	tty_audit_tiocsti(tty, ch);
+	ld = tty_ldisc_ref_wait(tty);
+	ld->ops->receive_buf(tty, &ch, &mbz, 1);
+	tty_ldisc_deref(ld);
+	return 0;
+}
+
+/**
+ *	tiocgwinsz		-	implement window query ioctl
+ *	@tty; tty
+ *	@arg: user buffer for result
+ *
+ *	Copies the kernel idea of the window size into the user buffer.
+ *
+ *	Locking: tty->termios_mutex is taken to ensure the winsize data
+ *		is consistent.
+ */
+
+static int tiocgwinsz(struct tty_struct *tty, struct winsize __user *arg)
+{
+	int err;
+
+	mutex_lock(&tty->termios_mutex);
+	err = copy_to_user(arg, &tty->winsize, sizeof(*arg));
+	mutex_unlock(&tty->termios_mutex);
+
+	return err ? -EFAULT: 0;
+}
+
+/**
+ *	tty_do_resize		-	resize event
+ *	@tty: tty being resized
+ *	@rows: rows (character)
+ *	@cols: cols (character)
+ *
+ *	Update the termios variables and send the necessary signals to
+ *	peform a terminal resize correctly
+ */
+
+int tty_do_resize(struct tty_struct *tty, struct winsize *ws)
+{
+	struct pid *pgrp;
+	unsigned long flags;
+
+	/* Lock the tty */
+	mutex_lock(&tty->termios_mutex);
+	if (!memcmp(ws, &tty->winsize, sizeof(*ws)))
+		goto done;
+	/* Get the PID values and reference them so we can
+	   avoid holding the tty ctrl lock while sending signals */
+	spin_lock_irqsave(&tty->ctrl_lock, flags);
+	pgrp = get_pid(tty->pgrp);
+	spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+
+	if (pgrp)
+		kill_pgrp(pgrp, SIGWINCH, 1);
+	put_pid(pgrp);
+
+	tty->winsize = *ws;
+done:
+	mutex_unlock(&tty->termios_mutex);
+	return 0;
+}
+
+/**
+ *	tiocswinsz		-	implement window size set ioctl
+ *	@tty; tty side of tty
+ *	@arg: user buffer for result
+ *
+ *	Copies the user idea of the window size to the kernel. Traditionally
+ *	this is just advisory information but for the Linux console it
+ *	actually has driver level meaning and triggers a VC resize.
+ *
+ *	Locking:
+ *		Driver dependant. The default do_resize method takes the
+ *	tty termios mutex and ctrl_lock. The console takes its own lock
+ *	then calls into the default method.
+ */
+
+static int tiocswinsz(struct tty_struct *tty, struct winsize __user *arg)
+{
+	struct winsize tmp_ws;
+	if (copy_from_user(&tmp_ws, arg, sizeof(*arg)))
+		return -EFAULT;
+
+	if (tty->ops->resize)
+		return tty->ops->resize(tty, &tmp_ws);
+	else
+		return tty_do_resize(tty, &tmp_ws);
+}
+
+/**
+ *	tioccons	-	allow admin to move logical console
+ *	@file: the file to become console
+ *
+ *	Allow the adminstrator to move the redirected console device
+ *
+ *	Locking: uses redirect_lock to guard the redirect information
+ */
+
+static int tioccons(struct file *file)
+{
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	if (file->f_op->write == redirected_tty_write) {
+		struct file *f;
+		spin_lock(&redirect_lock);
+		f = redirect;
+		redirect = NULL;
+		spin_unlock(&redirect_lock);
+		if (f)
+			fput(f);
+		return 0;
+	}
+	spin_lock(&redirect_lock);
+	if (redirect) {
+		spin_unlock(&redirect_lock);
+		return -EBUSY;
+	}
+	get_file(file);
+	redirect = file;
+	spin_unlock(&redirect_lock);
+	return 0;
+}
+
+/**
+ *	fionbio		-	non blocking ioctl
+ *	@file: file to set blocking value
+ *	@p: user parameter
+ *
+ *	Historical tty interfaces had a blocking control ioctl before
+ *	the generic functionality existed. This piece of history is preserved
+ *	in the expected tty API of posix OS's.
+ *
+ *	Locking: none, the open file handle ensures it won't go away.
+ */
+
+static int fionbio(struct file *file, int __user *p)
+{
+	int nonblock;
+
+	if (get_user(nonblock, p))
+		return -EFAULT;
+
+	spin_lock(&file->f_lock);
+	if (nonblock)
+		file->f_flags |= O_NONBLOCK;
+	else
+		file->f_flags &= ~O_NONBLOCK;
+	spin_unlock(&file->f_lock);
+	return 0;
+}
+
+/**
+ *	tiocsctty	-	set controlling tty
+ *	@tty: tty structure
+ *	@arg: user argument
+ *
+ *	This ioctl is used to manage job control. It permits a session
+ *	leader to set this tty as the controlling tty for the session.
+ *
+ *	Locking:
+ *		Takes tty_mutex() to protect tty instance
+ *		Takes tasklist_lock internally to walk sessions
+ *		Takes ->siglock() when updating signal->tty
+ */
+
+static int tiocsctty(struct tty_struct *tty, int arg)
+{
+	int ret = 0;
+	if (current->signal->leader && (task_session(current) == tty->session))
+		return ret;
+
+	mutex_lock(&tty_mutex);
+	/*
+	 * The process must be a session leader and
+	 * not have a controlling tty already.
+	 */
+	if (!current->signal->leader || current->signal->tty) {
+		ret = -EPERM;
+		goto unlock;
+	}
+
+	if (tty->session) {
+		/*
+		 * This tty is already the controlling
+		 * tty for another session group!
+		 */
+		if (arg == 1 && capable(CAP_SYS_ADMIN)) {
+			/*
+			 * Steal it away
+			 */
+			read_lock(&tasklist_lock);
+			session_clear_tty(tty->session);
+			read_unlock(&tasklist_lock);
+		} else {
+			ret = -EPERM;
+			goto unlock;
+		}
+	}
+	proc_set_tty(current, tty);
+unlock:
+	mutex_unlock(&tty_mutex);
+	return ret;
+}
+
+/**
+ *	tty_get_pgrp	-	return a ref counted pgrp pid
+ *	@tty: tty to read
+ *
+ *	Returns a refcounted instance of the pid struct for the process
+ *	group controlling the tty.
+ */
+
+struct pid *tty_get_pgrp(struct tty_struct *tty)
+{
+	unsigned long flags;
+	struct pid *pgrp;
+
+	spin_lock_irqsave(&tty->ctrl_lock, flags);
+	pgrp = get_pid(tty->pgrp);
+	spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+
+	return pgrp;
+}
+EXPORT_SYMBOL_GPL(tty_get_pgrp);
+
+/**
+ *	tiocgpgrp		-	get process group
+ *	@tty: tty passed by user
+ *	@real_tty: tty side of the tty pased by the user if a pty else the tty
+ *	@p: returned pid
+ *
+ *	Obtain the process group of the tty. If there is no process group
+ *	return an error.
+ *
+ *	Locking: none. Reference to current->signal->tty is safe.
+ */
+
+static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
+{
+	struct pid *pid;
+	int ret;
+	/*
+	 * (tty == real_tty) is a cheap way of
+	 * testing if the tty is NOT a master pty.
+	 */
+	if (tty == real_tty && current->signal->tty != real_tty)
+		return -ENOTTY;
+	pid = tty_get_pgrp(real_tty);
+	ret =  put_user(pid_vnr(pid), p);
+	put_pid(pid);
+	return ret;
+}
+
+/**
+ *	tiocspgrp		-	attempt to set process group
+ *	@tty: tty passed by user
+ *	@real_tty: tty side device matching tty passed by user
+ *	@p: pid pointer
+ *
+ *	Set the process group of the tty to the session passed. Only
+ *	permitted where the tty session is our session.
+ *
+ *	Locking: RCU, ctrl lock
+ */
+
+static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
+{
+	struct pid *pgrp;
+	pid_t pgrp_nr;
+	int retval = tty_check_change(real_tty);
+	unsigned long flags;
+
+	if (retval == -EIO)
+		return -ENOTTY;
+	if (retval)
+		return retval;
+	if (!current->signal->tty ||
+	    (current->signal->tty != real_tty) ||
+	    (real_tty->session != task_session(current)))
+		return -ENOTTY;
+	if (get_user(pgrp_nr, p))
+		return -EFAULT;
+	if (pgrp_nr < 0)
+		return -EINVAL;
+	rcu_read_lock();
+	pgrp = find_vpid(pgrp_nr);
+	retval = -ESRCH;
+	if (!pgrp)
+		goto out_unlock;
+	retval = -EPERM;
+	if (session_of_pgrp(pgrp) != task_session(current))
+		goto out_unlock;
+	retval = 0;
+	spin_lock_irqsave(&tty->ctrl_lock, flags);
+	put_pid(real_tty->pgrp);
+	real_tty->pgrp = get_pid(pgrp);
+	spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+out_unlock:
+	rcu_read_unlock();
+	return retval;
+}
+
+/**
+ *	tiocgsid		-	get session id
+ *	@tty: tty passed by user
+ *	@real_tty: tty side of the tty pased by the user if a pty else the tty
+ *	@p: pointer to returned session id
+ *
+ *	Obtain the session id of the tty. If there is no session
+ *	return an error.
+ *
+ *	Locking: none. Reference to current->signal->tty is safe.
+ */
+
+static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
+{
+	/*
+	 * (tty == real_tty) is a cheap way of
+	 * testing if the tty is NOT a master pty.
+	*/
+	if (tty == real_tty && current->signal->tty != real_tty)
+		return -ENOTTY;
+	if (!real_tty->session)
+		return -ENOTTY;
+	return put_user(pid_vnr(real_tty->session), p);
+}
+
+/**
+ *	tiocsetd	-	set line discipline
+ *	@tty: tty device
+ *	@p: pointer to user data
+ *
+ *	Set the line discipline according to user request.
+ *
+ *	Locking: see tty_set_ldisc, this function is just a helper
+ */
+
+static int tiocsetd(struct tty_struct *tty, int __user *p)
+{
+	int ldisc;
+	int ret;
+
+	if (get_user(ldisc, p))
+		return -EFAULT;
+
+	ret = tty_set_ldisc(tty, ldisc);
+
+	return ret;
+}
+
+/**
+ *	send_break	-	performed time break
+ *	@tty: device to break on
+ *	@duration: timeout in mS
+ *
+ *	Perform a timed break on hardware that lacks its own driver level
+ *	timed break functionality.
+ *
+ *	Locking:
+ *		atomic_write_lock serializes
+ *
+ */
+
+static int send_break(struct tty_struct *tty, unsigned int duration)
+{
+	int retval;
+
+	if (tty->ops->break_ctl == NULL)
+		return 0;
+
+	if (tty->driver->flags & TTY_DRIVER_HARDWARE_BREAK)
+		retval = tty->ops->break_ctl(tty, duration);
+	else {
+		/* Do the work ourselves */
+		if (tty_write_lock(tty, 0) < 0)
+			return -EINTR;
+		retval = tty->ops->break_ctl(tty, -1);
+		if (retval)
+			goto out;
+		if (!signal_pending(current))
+			msleep_interruptible(duration);
+		retval = tty->ops->break_ctl(tty, 0);
+out:
+		tty_write_unlock(tty);
+		if (signal_pending(current))
+			retval = -EINTR;
+	}
+	return retval;
+}
+
+/**
+ *	tty_tiocmget		-	get modem status
+ *	@tty: tty device
+ *	@file: user file pointer
+ *	@p: pointer to result
+ *
+ *	Obtain the modem status bits from the tty driver if the feature
+ *	is supported. Return -EINVAL if it is not available.
+ *
+ *	Locking: none (up to the driver)
+ */
+
+static int tty_tiocmget(struct tty_struct *tty, struct file *file, int __user *p)
+{
+	int retval = -EINVAL;
+
+	if (tty->ops->tiocmget) {
+		retval = tty->ops->tiocmget(tty, file);
+
+		if (retval >= 0)
+			retval = put_user(retval, p);
+	}
+	return retval;
+}
+
+/**
+ *	tty_tiocmset		-	set modem status
+ *	@tty: tty device
+ *	@file: user file pointer
+ *	@cmd: command - clear bits, set bits or set all
+ *	@p: pointer to desired bits
+ *
+ *	Set the modem status bits from the tty driver if the feature
+ *	is supported. Return -EINVAL if it is not available.
+ *
+ *	Locking: none (up to the driver)
+ */
+
+static int tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int cmd,
+	     unsigned __user *p)
+{
+	int retval;
+	unsigned int set, clear, val;
+
+	if (tty->ops->tiocmset == NULL)
+		return -EINVAL;
+
+	retval = get_user(val, p);
+	if (retval)
+		return retval;
+	set = clear = 0;
+	switch (cmd) {
+	case TIOCMBIS:
+		set = val;
+		break;
+	case TIOCMBIC:
+		clear = val;
+		break;
+	case TIOCMSET:
+		set = val;
+		clear = ~val;
+		break;
+	}
+	set &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP|TIOCM_CD|
+		TIOCM_RI|TIOCM_DSR|TIOCM_CTS;
+	clear &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP|TIOCM_CD|
+		TIOCM_RI|TIOCM_DSR|TIOCM_CTS;
+	return tty->ops->tiocmset(tty, file, set, clear);
+}
+
+struct tty_struct *tty_pair_get_tty(struct tty_struct *tty)
+{
+	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
+	    tty->driver->subtype == PTY_TYPE_MASTER)
+		tty = tty->link;
+	return tty;
+}
+EXPORT_SYMBOL(tty_pair_get_tty);
+
+struct tty_struct *tty_pair_get_pty(struct tty_struct *tty)
+{
+	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
+	    tty->driver->subtype == PTY_TYPE_MASTER)
+	    return tty;
+	return tty->link;
+}
+EXPORT_SYMBOL(tty_pair_get_pty);
+
+/*
+ * Split this up, as gcc can choke on it otherwise..
+ */
+long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct tty_struct *tty, *real_tty;
+	void __user *p = (void __user *)arg;
+	int retval;
+	struct tty_ldisc *ld;
+	struct inode *inode = file->f_dentry->d_inode;
+
+	tty = (struct tty_struct *)file->private_data;
+	if (tty_paranoia_check(tty, inode, "tty_ioctl"))
+		return -EINVAL;
+
+	real_tty = tty_pair_get_tty(tty);
+
+	/*
+	 * Factor out some common prep work
+	 */
+	switch (cmd) {
+	case TIOCSETD:
+	case TIOCSBRK:
+	case TIOCCBRK:
+	case TCSBRK:
+	case TCSBRKP:
+		retval = tty_check_change(tty);
+		if (retval)
+			return retval;
+		if (cmd != TIOCCBRK) {
+			tty_wait_until_sent(tty, 0);
+			if (signal_pending(current))
+				return -EINTR;
+		}
+		break;
+	}
+
+	/*
+	 *	Now do the stuff.
+	 */
+	switch (cmd) {
+	case TIOCSTI:
+		return tiocsti(tty, p);
+	case TIOCGWINSZ:
+		return tiocgwinsz(real_tty, p);
+	case TIOCSWINSZ:
+		return tiocswinsz(real_tty, p);
+	case TIOCCONS:
+		return real_tty != tty ? -EINVAL : tioccons(file);
+	case FIONBIO:
+		return fionbio(file, p);
+	case TIOCEXCL:
+		set_bit(TTY_EXCLUSIVE, &tty->flags);
+		return 0;
+	case TIOCNXCL:
+		clear_bit(TTY_EXCLUSIVE, &tty->flags);
+		return 0;
+	case TIOCNOTTY:
+		if (current->signal->tty != tty)
+			return -ENOTTY;
+		no_tty();
+		return 0;
+	case TIOCSCTTY:
+		return tiocsctty(tty, arg);
+	case TIOCGPGRP:
+		return tiocgpgrp(tty, real_tty, p);
+	case TIOCSPGRP:
+		return tiocspgrp(tty, real_tty, p);
+	case TIOCGSID:
+		return tiocgsid(tty, real_tty, p);
+	case TIOCGETD:
+		return put_user(tty->ldisc->ops->num, (int __user *)p);
+	case TIOCSETD:
+		return tiocsetd(tty, p);
+	/*
+	 * Break handling
+	 */
+	case TIOCSBRK:	/* Turn break on, unconditionally */
+		if (tty->ops->break_ctl)
+			return tty->ops->break_ctl(tty, -1);
+		return 0;
+	case TIOCCBRK:	/* Turn break off, unconditionally */
+		if (tty->ops->break_ctl)
+			return tty->ops->break_ctl(tty, 0);
+		return 0;
+	case TCSBRK:   /* SVID version: non-zero arg --> no break */
+		/* non-zero arg means wait for all output data
+		 * to be sent (performed above) but don't send break.
+		 * This is used by the tcdrain() termios function.
+		 */
+		if (!arg)
+			return send_break(tty, 250);
+		return 0;
+	case TCSBRKP:	/* support for POSIX tcsendbreak() */
+		return send_break(tty, arg ? arg*100 : 250);
+
+	case TIOCMGET:
+		return tty_tiocmget(tty, file, p);
+	case TIOCMSET:
+	case TIOCMBIC:
+	case TIOCMBIS:
+		return tty_tiocmset(tty, file, cmd, p);
+	case TCFLSH:
+		switch (arg) {
+		case TCIFLUSH:
+		case TCIOFLUSH:
+		/* flush tty buffer and allow ldisc to process ioctl */
+			tty_buffer_flush(tty);
+			break;
+		}
+		break;
+	}
+	if (tty->ops->ioctl) {
+		retval = (tty->ops->ioctl)(tty, file, cmd, arg);
+		if (retval != -ENOIOCTLCMD)
+			return retval;
+	}
+	ld = tty_ldisc_ref_wait(tty);
+	retval = -EINVAL;
+	if (ld->ops->ioctl) {
+		retval = ld->ops->ioctl(tty, file, cmd, arg);
+		if (retval == -ENOIOCTLCMD)
+			retval = -EINVAL;
+	}
+	tty_ldisc_deref(ld);
+	return retval;
+}
+
+#ifdef CONFIG_COMPAT
+static long tty_compat_ioctl(struct file *file, unsigned int cmd,
+				unsigned long arg)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+	struct tty_struct *tty = file->private_data;
+	struct tty_ldisc *ld;
+	int retval = -ENOIOCTLCMD;
+
+	if (tty_paranoia_check(tty, inode, "tty_ioctl"))
+		return -EINVAL;
+
+	if (tty->ops->compat_ioctl) {
+		retval = (tty->ops->compat_ioctl)(tty, file, cmd, arg);
+		if (retval != -ENOIOCTLCMD)
+			return retval;
+	}
+
+	ld = tty_ldisc_ref_wait(tty);
+	if (ld->ops->compat_ioctl)
+		retval = ld->ops->compat_ioctl(tty, file, cmd, arg);
+	tty_ldisc_deref(ld);
+
+	return retval;
+}
+#endif
+
+/*
+ * This implements the "Secure Attention Key" ---  the idea is to
+ * prevent trojan horses by killing all processes associated with this
+ * tty when the user hits the "Secure Attention Key".  Required for
+ * super-paranoid applications --- see the Orange Book for more details.
+ *
+ * This code could be nicer; ideally it should send a HUP, wait a few
+ * seconds, then send a INT, and then a KILL signal.  But you then
+ * have to coordinate with the init process, since all processes associated
+ * with the current tty must be dead before the new getty is allowed
+ * to spawn.
+ *
+ * Now, if it would be correct ;-/ The current code has a nasty hole -
+ * it doesn't catch files in flight. We may send the descriptor to ourselves
+ * via AF_UNIX socket, close it and later fetch from socket. FIXME.
+ *
+ * Nasty bug: do_SAK is being called in interrupt context.  This can
+ * deadlock.  We punt it up to process context.  AKPM - 16Mar2001
+ */
+void __do_SAK(struct tty_struct *tty)
+{
+#ifdef TTY_SOFT_SAK
+	tty_hangup(tty);
+#else
+	struct task_struct *g, *p;
+	struct pid *session;
+	int		i;
+	struct file	*filp;
+	struct fdtable *fdt;
+
+	if (!tty)
+		return;
+	session = tty->session;
+
+	tty_ldisc_flush(tty);
+
+	tty_driver_flush_buffer(tty);
+
+	read_lock(&tasklist_lock);
+	/* Kill the entire session */
+	do_each_pid_task(session, PIDTYPE_SID, p) {
+		printk(KERN_NOTICE "SAK: killed process %d"
+			" (%s): task_session(p)==tty->session\n",
+			task_pid_nr(p), p->comm);
+		send_sig(SIGKILL, p, 1);
+	} while_each_pid_task(session, PIDTYPE_SID, p);
+	/* Now kill any processes that happen to have the
+	 * tty open.
+	 */
+	do_each_thread(g, p) {
+		if (p->signal->tty == tty) {
+			printk(KERN_NOTICE "SAK: killed process %d"
+			    " (%s): task_session(p)==tty->session\n",
+			    task_pid_nr(p), p->comm);
+			send_sig(SIGKILL, p, 1);
+			continue;
+		}
+		task_lock(p);
+		if (p->files) {
+			/*
+			 * We don't take a ref to the file, so we must
+			 * hold ->file_lock instead.
+			 */
+			spin_lock(&p->files->file_lock);
+			fdt = files_fdtable(p->files);
+			for (i = 0; i < fdt->max_fds; i++) {
+				filp = fcheck_files(p->files, i);
+				if (!filp)
+					continue;
+				if (filp->f_op->read == tty_read &&
+				    filp->private_data == tty) {
+					printk(KERN_NOTICE "SAK: killed process %d"
+					    " (%s): fd#%d opened to the tty\n",
+					    task_pid_nr(p), p->comm, i);
+					force_sig(SIGKILL, p);
+					break;
+				}
+			}
+			spin_unlock(&p->files->file_lock);
+		}
+		task_unlock(p);
+	} while_each_thread(g, p);
+	read_unlock(&tasklist_lock);
+#endif
+}
+
+static void do_SAK_work(struct work_struct *work)
+{
+	struct tty_struct *tty =
+		container_of(work, struct tty_struct, SAK_work);
+	__do_SAK(tty);
+}
+
+/*
+ * The tq handling here is a little racy - tty->SAK_work may already be queued.
+ * Fortunately we don't need to worry, because if ->SAK_work is already queued,
+ * the values which we write to it will be identical to the values which it
+ * already has. --akpm
+ */
+void do_SAK(struct tty_struct *tty)
+{
+	if (!tty)
+		return;
+	schedule_work(&tty->SAK_work);
+}
+
+EXPORT_SYMBOL(do_SAK);
+
+/**
+ *	initialize_tty_struct
+ *	@tty: tty to initialize
+ *
+ *	This subroutine initializes a tty structure that has been newly
+ *	allocated.
+ *
+ *	Locking: none - tty in question must not be exposed at this point
+ */
+
+void initialize_tty_struct(struct tty_struct *tty,
+		struct tty_driver *driver, int idx)
+{
+	memset(tty, 0, sizeof(struct tty_struct));
+	kref_init(&tty->kref);
+	tty->magic = TTY_MAGIC;
+	tty_ldisc_init(tty);
+	tty->session = NULL;
+	tty->pgrp = NULL;
+	tty->overrun_time = jiffies;
+	tty->buf.head = tty->buf.tail = NULL;
+	tty_buffer_init(tty);
+	mutex_init(&tty->termios_mutex);
+	mutex_init(&tty->ldisc_mutex);
+	init_waitqueue_head(&tty->write_wait);
+	init_waitqueue_head(&tty->read_wait);
+	INIT_WORK(&tty->hangup_work, do_tty_hangup);
+	mutex_init(&tty->atomic_read_lock);
+	mutex_init(&tty->atomic_write_lock);
+	mutex_init(&tty->output_lock);
+	mutex_init(&tty->echo_lock);
+	spin_lock_init(&tty->read_lock);
+	spin_lock_init(&tty->ctrl_lock);
+	INIT_LIST_HEAD(&tty->tty_files);
+	INIT_WORK(&tty->SAK_work, do_SAK_work);
+
+	tty->driver = driver;
+	tty->ops = driver->ops;
+	tty->index = idx;
+	tty_line_name(driver, idx, tty->name);
+}
+
+/**
+ *	tty_put_char	-	write one character to a tty
+ *	@tty: tty
+ *	@ch: character
+ *
+ *	Write one byte to the tty using the provided put_char method
+ *	if present. Returns the number of characters successfully output.
+ *
+ *	Note: the specific put_char operation in the driver layer may go
+ *	away soon. Don't call it directly, use this method
+ */
+
+int tty_put_char(struct tty_struct *tty, unsigned char ch)
+{
+	if (tty->ops->put_char)
+		return tty->ops->put_char(tty, ch);
+	return tty->ops->write(tty, &ch, 1);
+}
+EXPORT_SYMBOL_GPL(tty_put_char);
+
+struct class *tty_class;
+
+/**
+ *	tty_register_device - register a tty device
+ *	@driver: the tty driver that describes the tty device
+ *	@index: the index in the tty driver for this tty device
+ *	@device: a struct device that is associated with this tty device.
+ *		This field is optional, if there is no known struct device
+ *		for this tty device it can be set to NULL safely.
+ *
+ *	Returns a pointer to the struct device for this tty device
+ *	(or ERR_PTR(-EFOO) on error).
+ *
+ *	This call is required to be made to register an individual tty device
+ *	if the tty driver's flags have the TTY_DRIVER_DYNAMIC_DEV bit set.  If
+ *	that bit is not set, this function should not be called by a tty
+ *	driver.
+ *
+ *	Locking: ??
+ */
+
+struct device *tty_register_device(struct tty_driver *driver, unsigned index,
+				   struct device *device)
+{
+	char name[64];
+	dev_t dev = MKDEV(driver->major, driver->minor_start) + index;
+
+	if (index >= driver->num) {
+		printk(KERN_ERR "Attempt to register invalid tty line number "
+		       " (%d).\n", index);
+		return ERR_PTR(-EINVAL);
+	}
+
+	if (driver->type == TTY_DRIVER_TYPE_PTY)
+		pty_line_name(driver, index, name);
+	else
+		tty_line_name(driver, index, name);
+
+	return device_create(tty_class, device, dev, NULL, name);
+}
+EXPORT_SYMBOL(tty_register_device);
+
+/**
+ * 	tty_unregister_device - unregister a tty device
+ * 	@driver: the tty driver that describes the tty device
+ * 	@index: the index in the tty driver for this tty device
+ *
+ * 	If a tty device is registered with a call to tty_register_device() then
+ *	this function must be called when the tty device is gone.
+ *
+ *	Locking: ??
+ */
+
+void tty_unregister_device(struct tty_driver *driver, unsigned index)
+{
+	device_destroy(tty_class,
+		MKDEV(driver->major, driver->minor_start) + index);
+}
+EXPORT_SYMBOL(tty_unregister_device);
+
+struct tty_driver *alloc_tty_driver(int lines)
+{
+	struct tty_driver *driver;
+
+	driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL);
+	if (driver) {
+		kref_init(&driver->kref);
+		driver->magic = TTY_DRIVER_MAGIC;
+		driver->num = lines;
+		/* later we'll move allocation of tables here */
+	}
+	return driver;
+}
+EXPORT_SYMBOL(alloc_tty_driver);
+
+static void destruct_tty_driver(struct kref *kref)
+{
+	struct tty_driver *driver = container_of(kref, struct tty_driver, kref);
+	int i;
+	struct ktermios *tp;
+	void *p;
+
+	if (driver->flags & TTY_DRIVER_INSTALLED) {
+		/*
+		 * Free the termios and termios_locked structures because
+		 * we don't want to get memory leaks when modular tty
+		 * drivers are removed from the kernel.
+		 */
+		for (i = 0; i < driver->num; i++) {
+			tp = driver->termios[i];
+			if (tp) {
+				driver->termios[i] = NULL;
+				kfree(tp);
+			}
+			if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV))
+				tty_unregister_device(driver, i);
+		}
+		p = driver->ttys;
+		proc_tty_unregister_driver(driver);
+		driver->ttys = NULL;
+		driver->termios = NULL;
+		kfree(p);
+		cdev_del(&driver->cdev);
+	}
+	kfree(driver);
+}
+
+void tty_driver_kref_put(struct tty_driver *driver)
+{
+	kref_put(&driver->kref, destruct_tty_driver);
+}
+EXPORT_SYMBOL(tty_driver_kref_put);
+
+void tty_set_operations(struct tty_driver *driver,
+			const struct tty_operations *op)
+{
+	driver->ops = op;
+};
+EXPORT_SYMBOL(tty_set_operations);
+
+void put_tty_driver(struct tty_driver *d)
+{
+	tty_driver_kref_put(d);
+}
+EXPORT_SYMBOL(put_tty_driver);
+
+/*
+ * Called by a tty driver to register itself.
+ */
+int tty_register_driver(struct tty_driver *driver)
+{
+	int error;
+	int i;
+	dev_t dev;
+	void **p = NULL;
+
+	if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) {
+		p = kzalloc(driver->num * 2 * sizeof(void *), GFP_KERNEL);
+		if (!p)
+			return -ENOMEM;
+	}
+
+	if (!driver->major) {
+		error = alloc_chrdev_region(&dev, driver->minor_start,
+						driver->num, driver->name);
+		if (!error) {
+			driver->major = MAJOR(dev);
+			driver->minor_start = MINOR(dev);
+		}
+	} else {
+		dev = MKDEV(driver->major, driver->minor_start);
+		error = register_chrdev_region(dev, driver->num, driver->name);
+	}
+	if (error < 0) {
+		kfree(p);
+		return error;
+	}
+
+	if (p) {
+		driver->ttys = (struct tty_struct **)p;
+		driver->termios = (struct ktermios **)(p + driver->num);
+	} else {
+		driver->ttys = NULL;
+		driver->termios = NULL;
+	}
+
+	cdev_init(&driver->cdev, &tty_fops);
+	driver->cdev.owner = driver->owner;
+	error = cdev_add(&driver->cdev, dev, driver->num);
+	if (error) {
+		unregister_chrdev_region(dev, driver->num);
+		driver->ttys = NULL;
+		driver->termios = NULL;
+		kfree(p);
+		return error;
+	}
+
+	mutex_lock(&tty_mutex);
+	list_add(&driver->tty_drivers, &tty_drivers);
+	mutex_unlock(&tty_mutex);
+
+	if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {
+		for (i = 0; i < driver->num; i++)
+		    tty_register_device(driver, i, NULL);
+	}
+	proc_tty_register_driver(driver);
+	driver->flags |= TTY_DRIVER_INSTALLED;
+	return 0;
+}
+
+EXPORT_SYMBOL(tty_register_driver);
+
+/*
+ * Called by a tty driver to unregister itself.
+ */
+int tty_unregister_driver(struct tty_driver *driver)
+{
+#if 0
+	/* FIXME */
+	if (driver->refcount)
+		return -EBUSY;
+#endif
+	unregister_chrdev_region(MKDEV(driver->major, driver->minor_start),
+				driver->num);
+	mutex_lock(&tty_mutex);
+	list_del(&driver->tty_drivers);
+	mutex_unlock(&tty_mutex);
+	return 0;
+}
+
+EXPORT_SYMBOL(tty_unregister_driver);
+
+dev_t tty_devnum(struct tty_struct *tty)
+{
+	return MKDEV(tty->driver->major, tty->driver->minor_start) + tty->index;
+}
+EXPORT_SYMBOL(tty_devnum);
+
+void proc_clear_tty(struct task_struct *p)
+{
+	unsigned long flags;
+	struct tty_struct *tty;
+	spin_lock_irqsave(&p->sighand->siglock, flags);
+	tty = p->signal->tty;
+	p->signal->tty = NULL;
+	spin_unlock_irqrestore(&p->sighand->siglock, flags);
+	tty_kref_put(tty);
+}
+
+/* Called under the sighand lock */
+
+static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty)
+{
+	if (tty) {
+		unsigned long flags;
+		/* We should not have a session or pgrp to put here but.... */
+		spin_lock_irqsave(&tty->ctrl_lock, flags);
+		put_pid(tty->session);
+		put_pid(tty->pgrp);
+		tty->pgrp = get_pid(task_pgrp(tsk));
+		spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+		tty->session = get_pid(task_session(tsk));
+		if (tsk->signal->tty) {
+			printk(KERN_DEBUG "tty not NULL!!\n");
+			tty_kref_put(tsk->signal->tty);
+		}
+	}
+	put_pid(tsk->signal->tty_old_pgrp);
+	tsk->signal->tty = tty_kref_get(tty);
+	tsk->signal->tty_old_pgrp = NULL;
+}
+
+static void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty)
+{
+	spin_lock_irq(&tsk->sighand->siglock);
+	__proc_set_tty(tsk, tty);
+	spin_unlock_irq(&tsk->sighand->siglock);
+}
+
+struct tty_struct *get_current_tty(void)
+{
+	struct tty_struct *tty;
+	unsigned long flags;
+
+	spin_lock_irqsave(&current->sighand->siglock, flags);
+	tty = tty_kref_get(current->signal->tty);
+	spin_unlock_irqrestore(&current->sighand->siglock, flags);
+	return tty;
+}
+EXPORT_SYMBOL_GPL(get_current_tty);
+
+void tty_default_fops(struct file_operations *fops)
+{
+	*fops = tty_fops;
+}
+
+/*
+ * Initialize the console device. This is called *early*, so
+ * we can't necessarily depend on lots of kernel help here.
+ * Just do some early initializations, and do the complex setup
+ * later.
+ */
+void __init console_init(void)
+{
+	initcall_t *call;
+
+	/* Setup the default TTY line discipline. */
+	tty_ldisc_begin();
+
+	/*
+	 * set up the console device so that later boot sequences can
+	 * inform about problems etc..
+	 */
+	call = __con_initcall_start;
+	while (call < __con_initcall_end) {
+		(*call)();
+		call++;
+	}
+}
+
+static char *tty_devnode(struct device *dev, mode_t *mode)
+{
+	if (!mode)
+		return NULL;
+	if (dev->devt == MKDEV(TTYAUX_MAJOR, 0) ||
+	    dev->devt == MKDEV(TTYAUX_MAJOR, 2))
+		*mode = 0666;
+	return NULL;
+}
+
+static int __init tty_class_init(void)
+{
+	tty_class = class_create(THIS_MODULE, "tty");
+	if (IS_ERR(tty_class))
+		return PTR_ERR(tty_class);
+	tty_class->devnode = tty_devnode;
+	return 0;
+}
+
+postcore_initcall(tty_class_init);
+
+/* 3/2004 jmc: why do these devices exist? */
+
+static struct cdev tty_cdev, console_cdev;
+
+/*
+ * Ok, now we can initialize the rest of the tty devices and can count
+ * on memory allocations, interrupts etc..
+ */
+int __init tty_init(void)
+{
+	cdev_init(&tty_cdev, &tty_fops);
+	if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||
+	    register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0)
+		panic("Couldn't register /dev/tty driver\n");
+	device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL,
+			      "tty");
+
+	cdev_init(&console_cdev, &console_fops);
+	if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) ||
+	    register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console") < 0)
+		panic("Couldn't register /dev/console driver\n");
+	device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL,
+			      "console");
+
+#ifdef CONFIG_VT
+	vty_init(&console_fops);
+#endif
+	return 0;
+}
+
diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c
index 6db161f..fdff32e 100644
--- a/drivers/clk/clkdev.c
+++ b/drivers/clk/clkdev.c
@@ -83,6 +83,31 @@
 }
 EXPORT_SYMBOL(clk_get);
 
+static void devm_clk_release(struct device *dev, void *res)
+{
+	clk_put(*(struct clk **)res);
+}
+
+struct clk *devm_clk_get(struct device *dev, const char *id)
+{
+	struct clk **ptr, *clk;
+
+	ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr)
+		return ERR_PTR(-ENOMEM);
+
+	clk = clk_get(dev, id);
+	if (!IS_ERR(clk)) {
+		*ptr = clk;
+		devres_add(dev, ptr);
+	} else {
+		devres_free(ptr);
+	}
+
+	return clk;
+}
+EXPORT_SYMBOL(devm_clk_get);
+
 void clk_put(struct clk *clk)
 {
 	__clk_put(clk);
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index 35835b7..46756c5 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -53,3 +53,4 @@
 ##################################################################################
 # PowerPC platform drivers
 obj-$(CONFIG_CPU_FREQ_MAPLE)		+= maple-cpufreq.o
+obj-$(CONFIG_MSM_DCVS)			+= cpufreq_gov_msm.o
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 7f2f149..e9d654b 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -41,7 +41,11 @@
 static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data);
 #ifdef CONFIG_HOTPLUG_CPU
 /* This one keeps track of the previously set governor of a removed CPU */
-static DEFINE_PER_CPU(char[CPUFREQ_NAME_LEN], cpufreq_cpu_governor);
+struct cpufreq_cpu_save_data {
+	char gov[CPUFREQ_NAME_LEN];
+	unsigned int max, min;
+};
+static DEFINE_PER_CPU(struct cpufreq_cpu_save_data, cpufreq_policy_save);
 #endif
 static DEFINE_SPINLOCK(cpufreq_driver_lock);
 
@@ -68,7 +72,7 @@
 static DEFINE_PER_CPU(struct rw_semaphore, cpu_policy_rwsem);
 
 #define lock_policy_rwsem(mode, cpu)					\
-static int lock_policy_rwsem_##mode					\
+int lock_policy_rwsem_##mode						\
 (int cpu)								\
 {									\
 	int policy_cpu = per_cpu(cpufreq_policy_cpu, cpu);		\
@@ -93,7 +97,7 @@
 	up_read(&per_cpu(cpu_policy_rwsem, policy_cpu));
 }
 
-static void unlock_policy_rwsem_write(int cpu)
+void unlock_policy_rwsem_write(int cpu)
 {
 	int policy_cpu = per_cpu(cpufreq_policy_cpu, cpu);
 	BUG_ON(policy_cpu == -1);
@@ -277,14 +281,31 @@
 		trace_cpu_frequency(freqs->new, freqs->cpu);
 		srcu_notifier_call_chain(&cpufreq_transition_notifier_list,
 				CPUFREQ_POSTCHANGE, freqs);
-		if (likely(policy) && likely(policy->cpu == freqs->cpu))
+		if (likely(policy) && likely(policy->cpu == freqs->cpu)) {
 			policy->cur = freqs->new;
+			sysfs_notify(&policy->kobj, NULL, "scaling_cur_freq");
+		}
 		break;
 	}
 }
 EXPORT_SYMBOL_GPL(cpufreq_notify_transition);
+/**
+ * cpufreq_notify_utilization - notify CPU userspace about CPU utilization
+ * change
+ *
+ * This function is called everytime the CPU load is evaluated by the
+ * ondemand governor. It notifies userspace of cpu load changes via sysfs.
+ */
+void cpufreq_notify_utilization(struct cpufreq_policy *policy,
+		unsigned int util)
+{
+	if (policy)
+		policy->util = util;
 
+	if (policy->util >= MIN_CPU_UTIL_NOTIFY)
+		sysfs_notify(&policy->kobj, NULL, "cpu_utilization");
 
+}
 
 /*********************************************************************
  *                          SYSFS INTERFACE                          *
@@ -372,6 +393,7 @@
 show_one(scaling_min_freq, min);
 show_one(scaling_max_freq, max);
 show_one(scaling_cur_freq, cur);
+show_one(cpu_utilization, util);
 
 static int __cpufreq_set_policy(struct cpufreq_policy *data,
 				struct cpufreq_policy *policy);
@@ -461,6 +483,8 @@
 	policy->user_policy.policy = policy->policy;
 	policy->user_policy.governor = policy->governor;
 
+	sysfs_notify(&policy->kobj, NULL, "scaling_governor");
+
 	if (ret)
 		return ret;
 	else
@@ -586,6 +610,7 @@
 cpufreq_freq_attr_ro(bios_limit);
 cpufreq_freq_attr_ro(related_cpus);
 cpufreq_freq_attr_ro(affected_cpus);
+cpufreq_freq_attr_ro(cpu_utilization);
 cpufreq_freq_attr_rw(scaling_min_freq);
 cpufreq_freq_attr_rw(scaling_max_freq);
 cpufreq_freq_attr_rw(scaling_governor);
@@ -598,6 +623,7 @@
 	&scaling_min_freq.attr,
 	&scaling_max_freq.attr,
 	&affected_cpus.attr,
+	&cpu_utilization.attr,
 	&related_cpus.attr,
 	&scaling_governor.attr,
 	&scaling_driver.attr,
@@ -696,12 +722,22 @@
 #ifdef CONFIG_HOTPLUG_CPU
 	struct cpufreq_governor *gov;
 
-	gov = __find_governor(per_cpu(cpufreq_cpu_governor, cpu));
+	gov = __find_governor(per_cpu(cpufreq_policy_save, cpu).gov);
 	if (gov) {
 		policy->governor = gov;
 		pr_debug("Restoring governor %s for cpu %d\n",
 		       policy->governor->name, cpu);
 	}
+	if (per_cpu(cpufreq_policy_save, cpu).min) {
+		policy->min = per_cpu(cpufreq_policy_save, cpu).min;
+		policy->user_policy.min = policy->min;
+	}
+	if (per_cpu(cpufreq_policy_save, cpu).max) {
+		policy->max = per_cpu(cpufreq_policy_save, cpu).max;
+		policy->user_policy.max = policy->max;
+	}
+	pr_debug("Restoring CPU%d min %d and max %d\n",
+		cpu, policy->min, policy->max);
 #endif
 
 	for_each_cpu(j, policy->cpus) {
@@ -1051,8 +1087,12 @@
 #ifdef CONFIG_SMP
 
 #ifdef CONFIG_HOTPLUG_CPU
-	strncpy(per_cpu(cpufreq_cpu_governor, cpu), data->governor->name,
+	strncpy(per_cpu(cpufreq_policy_save, cpu).gov, data->governor->name,
 			CPUFREQ_NAME_LEN);
+	per_cpu(cpufreq_policy_save, cpu).min = data->min;
+	per_cpu(cpufreq_policy_save, cpu).max = data->max;
+	pr_debug("Saving CPU%d policy min %d and max %d\n",
+			cpu, data->min, data->max);
 #endif
 
 	/* if we have other CPUs still registered, we need to unlink them,
@@ -1076,8 +1116,12 @@
 				continue;
 			pr_debug("removing link for cpu %u\n", j);
 #ifdef CONFIG_HOTPLUG_CPU
-			strncpy(per_cpu(cpufreq_cpu_governor, j),
+			strncpy(per_cpu(cpufreq_policy_save, j).gov,
 				data->governor->name, CPUFREQ_NAME_LEN);
+			per_cpu(cpufreq_policy_save, j).min = data->min;
+			per_cpu(cpufreq_policy_save, j).max = data->max;
+			pr_debug("Saving CPU%d policy min %d and max %d\n",
+					j, data->min, data->max);
 #endif
 			cpu_dev = get_cpu_device(j);
 			kobj = &cpu_dev->kobj;
@@ -1594,8 +1638,11 @@
 	for_each_present_cpu(cpu) {
 		if (cpu_online(cpu))
 			continue;
-		if (!strcmp(per_cpu(cpufreq_cpu_governor, cpu), governor->name))
-			strcpy(per_cpu(cpufreq_cpu_governor, cpu), "\0");
+		if (!strcmp(per_cpu(cpufreq_policy_save, cpu).gov,
+					governor->name))
+			strcpy(per_cpu(cpufreq_policy_save, cpu).gov, "\0");
+		per_cpu(cpufreq_policy_save, cpu).min = 0;
+		per_cpu(cpufreq_policy_save, cpu).max = 0;
 	}
 #endif
 
diff --git a/drivers/cpufreq/cpufreq_gov_msm.c b/drivers/cpufreq/cpufreq_gov_msm.c
new file mode 100644
index 0000000..9c49f80
--- /dev/null
+++ b/drivers/cpufreq/cpufreq_gov_msm.c
@@ -0,0 +1,176 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/kobject.h>
+#include <linux/cpufreq.h>
+#include <linux/platform_device.h>
+#include <mach/msm_dcvs.h>
+
+struct msm_gov {
+	int cpu;
+	unsigned int cur_freq;
+	unsigned int min_freq;
+	unsigned int max_freq;
+	struct msm_dcvs_freq gov_notifier;
+	struct cpufreq_policy *policy;
+};
+
+static DEFINE_PER_CPU_SHARED_ALIGNED(struct mutex, gov_mutex);
+static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_gov, msm_gov_info);
+static char core_name[NR_CPUS][10];
+
+static void msm_gov_check_limits(struct cpufreq_policy *policy)
+{
+	struct msm_gov *gov = &per_cpu(msm_gov_info, policy->cpu);
+
+	if (policy->max < gov->cur_freq)
+		__cpufreq_driver_target(policy, policy->max,
+				CPUFREQ_RELATION_H);
+	else if (policy->min > gov->min_freq)
+		__cpufreq_driver_target(policy, policy->min,
+				CPUFREQ_RELATION_L);
+	else
+		__cpufreq_driver_target(policy, gov->cur_freq,
+				CPUFREQ_RELATION_L);
+
+	gov->cur_freq = policy->cur;
+	gov->min_freq = policy->min;
+	gov->max_freq = policy->max;
+}
+
+static int msm_dcvs_freq_set(struct msm_dcvs_freq *self,
+		unsigned int freq)
+{
+	int ret = -EINVAL;
+	struct msm_gov *gov =
+		container_of(self, struct msm_gov, gov_notifier);
+
+	mutex_lock(&per_cpu(gov_mutex, gov->cpu));
+
+	if (freq < gov->min_freq)
+		freq = gov->min_freq;
+	if (freq > gov->max_freq)
+		freq = gov->max_freq;
+
+	ret = __cpufreq_driver_target(gov->policy, freq, CPUFREQ_RELATION_L);
+	gov->cur_freq = gov->policy->cur;
+
+	mutex_unlock(&per_cpu(gov_mutex, gov->cpu));
+
+	if (!ret)
+		return gov->cur_freq;
+
+	return ret;
+}
+
+static unsigned int msm_dcvs_freq_get(struct msm_dcvs_freq *self)
+{
+	struct msm_gov *gov =
+		container_of(self, struct msm_gov, gov_notifier);
+
+	return gov->cur_freq;
+}
+
+static int cpufreq_governor_msm(struct cpufreq_policy *policy,
+		unsigned int event)
+{
+	unsigned int cpu = policy->cpu;
+	int ret = 0;
+	int handle = 0;
+	struct msm_gov *gov = &per_cpu(msm_gov_info, policy->cpu);
+	struct msm_dcvs_freq *dcvs_notifier =
+			&(per_cpu(msm_gov_info, cpu).gov_notifier);
+
+	switch (event) {
+	case CPUFREQ_GOV_START:
+		if (!cpu_online(cpu))
+			return -EINVAL;
+		BUG_ON(!policy->cur);
+		mutex_lock(&per_cpu(gov_mutex, cpu));
+		per_cpu(msm_gov_info, cpu).cpu = cpu;
+		gov->policy = policy;
+		dcvs_notifier->core_name = core_name[cpu];
+		dcvs_notifier->set_frequency = msm_dcvs_freq_set;
+		dcvs_notifier->get_frequency = msm_dcvs_freq_get;
+		handle = msm_dcvs_freq_sink_register(dcvs_notifier);
+		BUG_ON(handle < 0);
+		msm_gov_check_limits(policy);
+		mutex_unlock(&per_cpu(gov_mutex, cpu));
+		break;
+
+	case CPUFREQ_GOV_STOP:
+		mutex_lock(&per_cpu(gov_mutex, cpu));
+		msm_dcvs_freq_sink_unregister(dcvs_notifier);
+		mutex_unlock(&per_cpu(gov_mutex, cpu));
+		break;
+
+	case CPUFREQ_GOV_LIMITS:
+		mutex_lock(&per_cpu(gov_mutex, cpu));
+		msm_gov_check_limits(policy);
+		mutex_unlock(&per_cpu(gov_mutex, cpu));
+		break;
+	};
+
+	return ret;
+}
+
+struct cpufreq_governor cpufreq_gov_msm = {
+	.name = "msm-dcvs",
+	.governor = cpufreq_governor_msm,
+	.owner = THIS_MODULE,
+};
+
+static int __devinit msm_gov_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	int cpu;
+	uint32_t group_id = 0x43505530; /* CPU0 */
+	struct msm_dcvs_core_info *core = NULL;
+
+	core = pdev->dev.platform_data;
+
+	for_each_possible_cpu(cpu) {
+		mutex_init(&per_cpu(gov_mutex, cpu));
+		snprintf(core_name[cpu], 10, "cpu%d", cpu);
+		ret = msm_dcvs_register_core(core_name[cpu], group_id, core);
+		if (ret)
+			pr_err("Unable to register core for %d\n", cpu);
+	}
+
+	return cpufreq_register_governor(&cpufreq_gov_msm);
+}
+
+static int __devexit msm_gov_remove(struct platform_device *pdev)
+{
+	platform_set_drvdata(pdev, NULL);
+	return 0;
+}
+
+static struct platform_driver msm_gov_driver = {
+	.probe = msm_gov_probe,
+	.remove = __devexit_p(msm_gov_remove),
+	.driver = {
+		.name = "msm_dcvs_gov",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init cpufreq_gov_msm_init(void)
+{
+	return platform_driver_register(&msm_gov_driver);
+}
+late_initcall(cpufreq_gov_msm_init);
diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c
index 836e9b0..5997405 100644
--- a/drivers/cpufreq/cpufreq_ondemand.c
+++ b/drivers/cpufreq/cpufreq_ondemand.c
@@ -22,6 +22,9 @@
 #include <linux/tick.h>
 #include <linux/ktime.h>
 #include <linux/sched.h>
+#include <linux/input.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
 
 /*
  * dbs is used in this file as a shortform for demandbased switching
@@ -37,6 +40,7 @@
 #define MICRO_FREQUENCY_MIN_SAMPLE_RATE		(10000)
 #define MIN_FREQUENCY_UP_THRESHOLD		(11)
 #define MAX_FREQUENCY_UP_THRESHOLD		(100)
+#define MIN_FREQUENCY_DOWN_DIFFERENTIAL		(1)
 
 /*
  * The polling frequency of this governor depends on the capability of
@@ -56,6 +60,9 @@
 #define MIN_LATENCY_MULTIPLIER			(100)
 #define TRANSITION_LATENCY_LIMIT		(10 * 1000 * 1000)
 
+#define POWERSAVE_BIAS_MAXLEVEL			(1000)
+#define POWERSAVE_BIAS_MINLEVEL			(-1000)
+
 static void do_dbs_timer(struct work_struct *work);
 static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
 				unsigned int event);
@@ -96,6 +103,9 @@
 };
 static DEFINE_PER_CPU(struct cpu_dbs_info_s, od_cpu_dbs_info);
 
+static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info);
+static inline void dbs_timer_exit(struct cpu_dbs_info_s *dbs_info);
+
 static unsigned int dbs_enable;	/* number of CPUs using this policy */
 
 /*
@@ -103,13 +113,17 @@
  */
 static DEFINE_MUTEX(dbs_mutex);
 
+static struct workqueue_struct *input_wq;
+
+static DEFINE_PER_CPU(struct work_struct, dbs_refresh_work);
+
 static struct dbs_tuners {
 	unsigned int sampling_rate;
 	unsigned int up_threshold;
 	unsigned int down_differential;
 	unsigned int ignore_nice;
 	unsigned int sampling_down_factor;
-	unsigned int powersave_bias;
+	int          powersave_bias;
 	unsigned int io_is_busy;
 } dbs_tuners_ins = {
 	.up_threshold = DEF_FREQUENCY_UP_THRESHOLD,
@@ -172,10 +186,11 @@
 					  unsigned int freq_next,
 					  unsigned int relation)
 {
-	unsigned int freq_req, freq_reduc, freq_avg;
+	unsigned int freq_req, freq_avg;
 	unsigned int freq_hi, freq_lo;
 	unsigned int index = 0;
 	unsigned int jiffies_total, jiffies_hi, jiffies_lo;
+	int freq_reduc;
 	struct cpu_dbs_info_s *dbs_info = &per_cpu(od_cpu_dbs_info,
 						   policy->cpu);
 
@@ -218,6 +233,26 @@
 	return freq_hi;
 }
 
+static int ondemand_powersave_bias_setspeed(struct cpufreq_policy *policy,
+					    struct cpufreq_policy *altpolicy,
+					    int level)
+{
+	if (level == POWERSAVE_BIAS_MAXLEVEL) {
+		/* maximum powersave; set to lowest frequency */
+		__cpufreq_driver_target(policy,
+			(altpolicy) ? altpolicy->min : policy->min,
+			CPUFREQ_RELATION_L);
+		return 1;
+	} else if (level == POWERSAVE_BIAS_MINLEVEL) {
+		/* minimum powersave; set to highest frequency */
+		__cpufreq_driver_target(policy,
+			(altpolicy) ? altpolicy->max : policy->max,
+			CPUFREQ_RELATION_H);
+		return 1;
+	}
+	return 0;
+}
+
 static void ondemand_powersave_bias_init_cpu(int cpu)
 {
 	struct cpu_dbs_info_s *dbs_info = &per_cpu(od_cpu_dbs_info, cpu);
@@ -253,9 +288,15 @@
 show_one(sampling_rate, sampling_rate);
 show_one(io_is_busy, io_is_busy);
 show_one(up_threshold, up_threshold);
+show_one(down_differential, down_differential);
 show_one(sampling_down_factor, sampling_down_factor);
 show_one(ignore_nice_load, ignore_nice);
-show_one(powersave_bias, powersave_bias);
+
+static ssize_t show_powersave_bias
+(struct kobject *kobj, struct attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", dbs_tuners_ins.powersave_bias);
+}
 
 /**
  * update_sampling_rate - update sampling rate effective immediately if needed.
@@ -353,6 +394,23 @@
 	return count;
 }
 
+static ssize_t store_down_differential(struct kobject *a, struct attribute *b,
+		const char *buf, size_t count)
+{
+	unsigned int input;
+	int ret;
+	ret = sscanf(buf, "%u", &input);
+
+	if (ret != 1 || input >= dbs_tuners_ins.up_threshold ||
+			input < MIN_FREQUENCY_DOWN_DIFFERENTIAL) {
+		return -EINVAL;
+	}
+
+	dbs_tuners_ins.down_differential = input;
+
+	return count;
+}
+
 static ssize_t store_sampling_down_factor(struct kobject *a,
 			struct attribute *b, const char *buf, size_t count)
 {
@@ -409,24 +467,112 @@
 static ssize_t store_powersave_bias(struct kobject *a, struct attribute *b,
 				    const char *buf, size_t count)
 {
-	unsigned int input;
-	int ret;
-	ret = sscanf(buf, "%u", &input);
+	int input  = 0;
+	int bypass = 0;
+	int ret, cpu, reenable_timer, j;
+	struct cpu_dbs_info_s *dbs_info;
+
+	struct cpumask cpus_timer_done;
+	cpumask_clear(&cpus_timer_done);
+
+	ret = sscanf(buf, "%d", &input);
 
 	if (ret != 1)
 		return -EINVAL;
 
-	if (input > 1000)
-		input = 1000;
+	if (input >= POWERSAVE_BIAS_MAXLEVEL) {
+		input  = POWERSAVE_BIAS_MAXLEVEL;
+		bypass = 1;
+	} else if (input <= POWERSAVE_BIAS_MINLEVEL) {
+		input  = POWERSAVE_BIAS_MINLEVEL;
+		bypass = 1;
+	}
+
+	if (input == dbs_tuners_ins.powersave_bias) {
+		/* no change */
+		return count;
+	}
+
+	reenable_timer = ((dbs_tuners_ins.powersave_bias ==
+				POWERSAVE_BIAS_MAXLEVEL) ||
+				(dbs_tuners_ins.powersave_bias ==
+				POWERSAVE_BIAS_MINLEVEL));
 
 	dbs_tuners_ins.powersave_bias = input;
-	ondemand_powersave_bias_init();
+	if (!bypass) {
+		if (reenable_timer) {
+			/* reinstate dbs timer */
+			for_each_online_cpu(cpu) {
+				if (lock_policy_rwsem_write(cpu) < 0)
+					continue;
+
+				dbs_info = &per_cpu(od_cpu_dbs_info, cpu);
+
+				for_each_cpu(j, &cpus_timer_done) {
+					if (!dbs_info->cur_policy) {
+						pr_err("Dbs policy is NULL\n");
+						goto skip_this_cpu;
+					}
+					if (cpumask_test_cpu(j, dbs_info->
+							cur_policy->cpus))
+						goto skip_this_cpu;
+				}
+
+				cpumask_set_cpu(cpu, &cpus_timer_done);
+				if (dbs_info->cur_policy) {
+					/* restart dbs timer */
+					dbs_timer_init(dbs_info);
+				}
+skip_this_cpu:
+				unlock_policy_rwsem_write(cpu);
+			}
+		}
+		ondemand_powersave_bias_init();
+	} else {
+		/* running at maximum or minimum frequencies; cancel
+		   dbs timer as periodic load sampling is not necessary */
+		for_each_online_cpu(cpu) {
+			if (lock_policy_rwsem_write(cpu) < 0)
+				continue;
+
+			dbs_info = &per_cpu(od_cpu_dbs_info, cpu);
+
+			for_each_cpu(j, &cpus_timer_done) {
+				if (!dbs_info->cur_policy) {
+					pr_err("Dbs policy is NULL\n");
+					goto skip_this_cpu_bypass;
+				}
+				if (cpumask_test_cpu(j, dbs_info->
+							cur_policy->cpus))
+					goto skip_this_cpu_bypass;
+			}
+
+			cpumask_set_cpu(cpu, &cpus_timer_done);
+
+			if (dbs_info->cur_policy) {
+				/* cpu using ondemand, cancel dbs timer */
+				mutex_lock(&dbs_info->timer_mutex);
+				dbs_timer_exit(dbs_info);
+
+				ondemand_powersave_bias_setspeed(
+					dbs_info->cur_policy,
+					NULL,
+					input);
+
+				mutex_unlock(&dbs_info->timer_mutex);
+			}
+skip_this_cpu_bypass:
+			unlock_policy_rwsem_write(cpu);
+		}
+	}
+
 	return count;
 }
 
 define_one_global_rw(sampling_rate);
 define_one_global_rw(io_is_busy);
 define_one_global_rw(up_threshold);
+define_one_global_rw(down_differential);
 define_one_global_rw(sampling_down_factor);
 define_one_global_rw(ignore_nice_load);
 define_one_global_rw(powersave_bias);
@@ -435,6 +581,7 @@
 	&sampling_rate_min.attr,
 	&sampling_rate.attr,
 	&up_threshold.attr,
+	&down_differential.attr,
 	&sampling_down_factor.attr,
 	&ignore_nice_load.attr,
 	&powersave_bias.attr,
@@ -462,7 +609,11 @@
 
 static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info)
 {
+	/* Extrapolated load of this CPU */
+	unsigned int load_at_max_freq = 0;
 	unsigned int max_load_freq;
+	/* Current load across this CPU */
+	unsigned int cur_load = 0;
 
 	struct cpufreq_policy *policy;
 	unsigned int j;
@@ -489,7 +640,7 @@
 		struct cpu_dbs_info_s *j_dbs_info;
 		cputime64_t cur_wall_time, cur_idle_time, cur_iowait_time;
 		unsigned int idle_time, wall_time, iowait_time;
-		unsigned int load, load_freq;
+		unsigned int load_freq;
 		int freq_avg;
 
 		j_dbs_info = &per_cpu(od_cpu_dbs_info, j);
@@ -539,16 +690,20 @@
 		if (unlikely(!wall_time || wall_time < idle_time))
 			continue;
 
-		load = 100 * (wall_time - idle_time) / wall_time;
+		cur_load = 100 * (wall_time - idle_time) / wall_time;
 
 		freq_avg = __cpufreq_driver_getavg(policy, j);
 		if (freq_avg <= 0)
 			freq_avg = policy->cur;
 
-		load_freq = load * freq_avg;
+		load_freq = cur_load * freq_avg;
 		if (load_freq > max_load_freq)
 			max_load_freq = load_freq;
 	}
+	/* calculate the scaled load across CPU */
+	load_at_max_freq = (cur_load * policy->cur)/policy->cpuinfo.max_freq;
+
+	cpufreq_notify_utilization(policy, load_at_max_freq);
 
 	/* Check for frequency increase */
 	if (max_load_freq > dbs_tuners_ins.up_threshold * policy->cur) {
@@ -676,6 +831,100 @@
 	return 0;
 }
 
+static void dbs_refresh_callback(struct work_struct *unused)
+{
+	struct cpufreq_policy *policy;
+	struct cpu_dbs_info_s *this_dbs_info;
+	unsigned int cpu = smp_processor_id();
+
+	if (lock_policy_rwsem_write(cpu) < 0)
+		return;
+
+	this_dbs_info = &per_cpu(od_cpu_dbs_info, cpu);
+	policy = this_dbs_info->cur_policy;
+	if (!policy) {
+		/* CPU not using ondemand governor */
+		unlock_policy_rwsem_write(cpu);
+		return;
+	}
+
+	if (policy->cur < policy->max) {
+		policy->cur = policy->max;
+
+		__cpufreq_driver_target(policy, policy->max,
+					CPUFREQ_RELATION_L);
+		this_dbs_info->prev_cpu_idle = get_cpu_idle_time(cpu,
+				&this_dbs_info->prev_cpu_wall);
+	}
+	unlock_policy_rwsem_write(cpu);
+}
+
+static void dbs_input_event(struct input_handle *handle, unsigned int type,
+		unsigned int code, int value)
+{
+	int i;
+
+	if ((dbs_tuners_ins.powersave_bias == POWERSAVE_BIAS_MAXLEVEL) ||
+		(dbs_tuners_ins.powersave_bias == POWERSAVE_BIAS_MINLEVEL)) {
+		/* nothing to do */
+		return;
+	}
+
+	for_each_online_cpu(i) {
+		queue_work_on(i, input_wq, &per_cpu(dbs_refresh_work, i));
+	}
+}
+
+static int dbs_input_connect(struct input_handler *handler,
+		struct input_dev *dev, const struct input_device_id *id)
+{
+	struct input_handle *handle;
+	int error;
+
+	handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
+	if (!handle)
+		return -ENOMEM;
+
+	handle->dev = dev;
+	handle->handler = handler;
+	handle->name = "cpufreq";
+
+	error = input_register_handle(handle);
+	if (error)
+		goto err2;
+
+	error = input_open_device(handle);
+	if (error)
+		goto err1;
+
+	return 0;
+err1:
+	input_unregister_handle(handle);
+err2:
+	kfree(handle);
+	return error;
+}
+
+static void dbs_input_disconnect(struct input_handle *handle)
+{
+	input_close_device(handle);
+	input_unregister_handle(handle);
+	kfree(handle);
+}
+
+static const struct input_device_id dbs_ids[] = {
+	{ .driver_info = 1 },
+	{ },
+};
+
+static struct input_handler dbs_input_handler = {
+	.event		= dbs_input_event,
+	.connect	= dbs_input_connect,
+	.disconnect	= dbs_input_disconnect,
+	.name		= "cpufreq_ond",
+	.id_table	= dbs_ids,
+};
+
 static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
 				   unsigned int event)
 {
@@ -734,10 +983,17 @@
 				    latency * LATENCY_MULTIPLIER);
 			dbs_tuners_ins.io_is_busy = should_io_be_busy();
 		}
+		if (!cpu)
+			rc = input_register_handler(&dbs_input_handler);
 		mutex_unlock(&dbs_mutex);
 
 		mutex_init(&this_dbs_info->timer_mutex);
-		dbs_timer_init(this_dbs_info);
+
+		if (!ondemand_powersave_bias_setspeed(
+					this_dbs_info->cur_policy,
+					NULL,
+					dbs_tuners_ins.powersave_bias))
+			dbs_timer_init(this_dbs_info);
 		break;
 
 	case CPUFREQ_GOV_STOP:
@@ -746,6 +1002,11 @@
 		mutex_lock(&dbs_mutex);
 		mutex_destroy(&this_dbs_info->timer_mutex);
 		dbs_enable--;
+		/* If device is being removed, policy is no longer
+		 * valid. */
+		this_dbs_info->cur_policy = NULL;
+		if (!cpu)
+			input_unregister_handler(&dbs_input_handler);
 		mutex_unlock(&dbs_mutex);
 		if (!dbs_enable)
 			sysfs_remove_group(cpufreq_global_kobject,
@@ -761,6 +1022,11 @@
 		else if (policy->min > this_dbs_info->cur_policy->cur)
 			__cpufreq_driver_target(this_dbs_info->cur_policy,
 				policy->min, CPUFREQ_RELATION_L);
+		else if (dbs_tuners_ins.powersave_bias != 0)
+			ondemand_powersave_bias_setspeed(
+				this_dbs_info->cur_policy,
+				policy,
+				dbs_tuners_ins.powersave_bias);
 		mutex_unlock(&this_dbs_info->timer_mutex);
 		break;
 	}
@@ -770,6 +1036,7 @@
 static int __init cpufreq_gov_dbs_init(void)
 {
 	u64 idle_time;
+	unsigned int i;
 	int cpu = get_cpu();
 
 	idle_time = get_cpu_idle_time_us(cpu, NULL);
@@ -791,12 +1058,22 @@
 			MIN_SAMPLING_RATE_RATIO * jiffies_to_usecs(10);
 	}
 
+	input_wq = create_workqueue("iewq");
+	if (!input_wq) {
+		printk(KERN_ERR "Failed to create iewq workqueue\n");
+		return -EFAULT;
+	}
+	for_each_possible_cpu(i) {
+		INIT_WORK(&per_cpu(dbs_refresh_work, i), dbs_refresh_callback);
+	}
+
 	return cpufreq_register_governor(&cpufreq_gov_ondemand);
 }
 
 static void __exit cpufreq_gov_dbs_exit(void)
 {
 	cpufreq_unregister_governor(&cpufreq_gov_ondemand);
+	destroy_workqueue(input_wq);
 }
 
 
diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c
index 72f0093..1a9a6a5 100644
--- a/drivers/cpufreq/cpufreq_stats.c
+++ b/drivers/cpufreq/cpufreq_stats.c
@@ -349,6 +349,7 @@
 		cpufreq_update_policy(cpu);
 		break;
 	case CPU_DOWN_PREPARE:
+	case CPU_DOWN_PREPARE_FROZEN:
 		cpufreq_stats_free_sysfs(cpu);
 		break;
 	case CPU_DEAD:
diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c
index 746f1e6..3157a86 100644
--- a/drivers/cpuidle/governors/menu.c
+++ b/drivers/cpuidle/governors/menu.c
@@ -126,14 +126,6 @@
 #define LOAD_INT(x) ((x) >> FSHIFT)
 #define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)
 
-static int get_loadavg(void)
-{
-	unsigned long this = this_cpu_load();
-
-
-	return LOAD_INT(this) * 10 + LOAD_FRAC(this) / 10;
-}
-
 static inline int which_bucket(unsigned int duration)
 {
 	int bucket = 0;
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index dd414d9..fb1ffd0 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -285,6 +285,49 @@
 	  Select this to offload Samsung S5PV210 or S5PC110 from AES
 	  algorithms execution.
 
+config CRYPTO_DEV_QCE40
+	bool
+
+config CRYPTO_DEV_QCRYPTO
+	tristate "Qualcomm Crypto accelerator"
+	select CRYPTO_DES
+	select CRYPTO_ALGAPI
+	select CRYPTO_AUTHENC
+	select CRYPTO_BLKCIPHER
+	default n
+	help
+          This driver supports Qualcomm crypto acceleration.
+          To compile this driver as a module, choose M here: the
+          module will be called qcrypto.
+
+config CRYPTO_DEV_QCE
+	tristate "Qualcomm Crypto Engine (QCE) module"
+	select  CRYPTO_DEV_QCE40 if ARCH_MSM8960 || ARCH_MSM9615
+	default n
+	help
+          This driver supports Qualcomm Crypto Engine in MSM7x30, MSM8660
+	  MSM8x55, MSM8960 and MSM9615
+	  To compile this driver as a module, choose M here: the
+	  For MSM7x30 MSM8660 and MSM8x55 the module is called qce
+	  For MSM8960 and MSM9615 the module is called qce40
+
+config CRYPTO_DEV_QCEDEV
+	tristate "QCEDEV Interface to CE module"
+	default n
+	help
+          This driver supports Qualcomm QCEDEV Crypto in MSM7x30, MSM8660,
+          MSM8960 and MSM9615.
+          This exposes the interface to the QCE hardware accelerator via IOCTLs
+	  To compile this driver as a module, choose M here: the
+	  module will be called qcedev.
+
+config CRYPTO_DEV_OTA_CRYPTO
+	tristate "OTA Crypto module"
+	help
+          This driver supports Qualcomm OTA Crypto in the FSM9xxx.
+	  To compile this driver as a module, choose M here: the
+	  module will be called ota_crypto.
+
 config CRYPTO_DEV_TEGRA_AES
 	tristate "Support for TEGRA AES hw engine"
 	depends on ARCH_TEGRA
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index f3e64ea..780620c 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -9,6 +9,7 @@
 obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM) += caam/
 obj-$(CONFIG_CRYPTO_DEV_IXP4XX) += ixp4xx_crypto.o
 obj-$(CONFIG_CRYPTO_DEV_PPC4XX) += amcc/
+obj-$(CONFIG_CRYPTO_DEV_QCE) += msm/
 obj-$(CONFIG_CRYPTO_DEV_OMAP_SHAM) += omap-sham.o
 obj-$(CONFIG_CRYPTO_DEV_OMAP_AES) += omap-aes.o
 obj-$(CONFIG_CRYPTO_DEV_PICOXCELL) += picoxcell_crypto.o
diff --git a/drivers/crypto/msm/Makefile b/drivers/crypto/msm/Makefile
new file mode 100644
index 0000000..61406b9
--- /dev/null
+++ b/drivers/crypto/msm/Makefile
@@ -0,0 +1,8 @@
+obj-$(CONFIG_CRYPTO_DEV_QCEDEV) += qcedev.o
+ifeq ($(CONFIG_CRYPTO_DEV_QCE40), y)
+	obj-$(CONFIG_CRYPTO_DEV_QCE) += qce40.o
+else
+	obj-$(CONFIG_CRYPTO_DEV_QCE) += qce.o
+endif
+obj-$(CONFIG_CRYPTO_DEV_QCRYPTO) += qcrypto.o
+obj-$(CONFIG_CRYPTO_DEV_OTA_CRYPTO) += ota_crypto.o
diff --git a/drivers/crypto/msm/ota_crypto.c b/drivers/crypto/msm/ota_crypto.c
new file mode 100644
index 0000000..b129c05
--- /dev/null
+++ b/drivers/crypto/msm/ota_crypto.c
@@ -0,0 +1,731 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/* Qualcomm Over the Air (OTA) Crypto driver */
+
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/dmapool.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+
+
+#include <linux/qcota.h>
+#include "qce.h"
+#include "qce_ota.h"
+
+enum qce_ota_oper_enum {
+	QCE_OTA_F8_OPER   = 0,
+	QCE_OTA_MPKT_F8_OPER = 1,
+	QCE_OTA_F9_OPER  = 2,
+	QCE_OTA_OPER_LAST
+};
+
+struct ota_dev_control;
+
+struct ota_async_req {
+	struct list_head list;
+	struct completion complete;
+	int err;
+	enum qce_ota_oper_enum op;
+	union {
+		struct qce_f9_req f9_req;
+		struct qce_f8_req f8_req;
+		struct qce_f8_multi_pkt_req f8_mp_req;
+	} req;
+
+	struct ota_dev_control  *podev;
+};
+
+/*
+ * Register ourselves as a misc device to be able to access the ota
+ * from userspace.
+ */
+
+
+#define QCOTA_DEV	"qcota"
+
+
+struct ota_dev_control {
+
+	/* misc device */
+	struct miscdevice miscdevice;
+
+	/* qce handle */
+	void *qce;
+
+	/* platform device */
+	struct platform_device *pdev;
+
+	unsigned magic;
+
+	struct list_head ready_commands;
+	struct ota_async_req *active_command;
+	spinlock_t lock;
+	struct tasklet_struct done_tasklet;
+};
+
+#define OTA_MAGIC 0x4f544143
+
+static long qcota_ioctl(struct file *file,
+			  unsigned cmd, unsigned long arg);
+static int qcota_open(struct inode *inode, struct file *file);
+static int qcota_release(struct inode *inode, struct file *file);
+static int start_req(struct ota_dev_control *podev);
+
+static const struct file_operations qcota_fops = {
+	.owner = THIS_MODULE,
+	.unlocked_ioctl = qcota_ioctl,
+	.open = qcota_open,
+	.release = qcota_release,
+};
+
+static struct ota_dev_control qcota_dev[] = {
+	{
+		.miscdevice = {
+			.minor = MISC_DYNAMIC_MINOR,
+			.name = "qcota0",
+			.fops = &qcota_fops,
+		},
+		.magic = OTA_MAGIC,
+	},
+	{
+		.miscdevice = {
+			.minor = MISC_DYNAMIC_MINOR,
+			.name = "qcota1",
+			.fops = &qcota_fops,
+		},
+		.magic = OTA_MAGIC,
+	},
+	{
+		.miscdevice = {
+			.minor = MISC_DYNAMIC_MINOR,
+			.name = "qcota2",
+			.fops = &qcota_fops,
+		},
+		.magic = OTA_MAGIC,
+	}
+};
+
+#define MAX_OTA_DEVICE ARRAY_SIZE(qcota_dev)
+
+#define DEBUG_MAX_FNAME  16
+#define DEBUG_MAX_RW_BUF 1024
+
+struct qcota_stat {
+	u32 f8_req;
+	u32 f8_mp_req;
+	u32 f9_req;
+	u32 f8_op_success;
+	u32 f8_op_fail;
+	u32 f8_mp_op_success;
+	u32 f8_mp_op_fail;
+	u32 f9_op_success;
+	u32 f9_op_fail;
+};
+static struct qcota_stat _qcota_stat[MAX_OTA_DEVICE];
+static struct dentry *_debug_dent;
+static char _debug_read_buf[DEBUG_MAX_RW_BUF];
+static int _debug_qcota[MAX_OTA_DEVICE];
+
+static struct ota_dev_control *qcota_minor_to_control(unsigned n)
+{
+	int i;
+
+	for (i = 0; i < MAX_OTA_DEVICE; i++) {
+		if (qcota_dev[i].miscdevice.minor == n)
+			return &qcota_dev[i];
+	}
+	return NULL;
+}
+
+static int qcota_open(struct inode *inode, struct file *file)
+{
+	struct ota_dev_control *podev;
+
+	podev = qcota_minor_to_control(MINOR(inode->i_rdev));
+	if (podev == NULL) {
+		pr_err("%s: no such device %d\n", __func__,
+				MINOR(inode->i_rdev));
+		return -ENOENT;
+	}
+
+	file->private_data = podev;
+
+	return 0;
+}
+
+static int qcota_release(struct inode *inode, struct file *file)
+{
+	struct ota_dev_control *podev;
+
+	podev =  file->private_data;
+
+	if (podev != NULL && podev->magic != OTA_MAGIC) {
+		pr_err("%s: invalid handle %p\n",
+			__func__, podev);
+	}
+
+	file->private_data = NULL;
+
+	return 0;
+}
+
+static void req_done(unsigned long data)
+{
+	struct ota_dev_control *podev = (struct ota_dev_control *)data;
+	struct ota_async_req *areq;
+	unsigned long flags;
+	struct ota_async_req *new_req = NULL;
+	int ret = 0;
+
+	spin_lock_irqsave(&podev->lock, flags);
+	areq = podev->active_command;
+	podev->active_command = NULL;
+
+again:
+	if (!list_empty(&podev->ready_commands)) {
+		new_req = container_of(podev->ready_commands.next,
+						struct ota_async_req, list);
+		list_del(&new_req->list);
+		podev->active_command = new_req;
+		new_req->err = 0;
+		ret = start_req(podev);
+	}
+
+	spin_unlock_irqrestore(&podev->lock, flags);
+
+	if (areq)
+		complete(&areq->complete);
+
+	if (new_req && ret) {
+		complete(&new_req->complete);
+		spin_lock_irqsave(&podev->lock, flags);
+		podev->active_command = NULL;
+		areq = NULL;
+		ret = 0;
+		new_req = NULL;
+		goto again;
+	}
+
+	return;
+}
+
+static void f9_cb(void *cookie, unsigned char *icv, unsigned char *iv,
+	int ret)
+{
+	struct ota_async_req *areq = (struct ota_async_req *) cookie;
+	struct ota_dev_control *podev;
+	struct qcota_stat *pstat;
+
+	podev = areq->podev;
+	pstat = &_qcota_stat[podev->pdev->id];
+	areq->req.f9_req.mac_i  = (uint32_t) icv;
+
+	if (ret)
+		areq->err = -ENXIO;
+	else
+		areq->err = 0;
+
+	tasklet_schedule(&podev->done_tasklet);
+};
+
+static void f8_cb(void *cookie, unsigned char *icv, unsigned char *iv,
+	int ret)
+{
+	struct ota_async_req *areq = (struct ota_async_req *) cookie;
+	struct ota_dev_control *podev;
+	struct qcota_stat *pstat;
+
+	podev = areq->podev;
+	pstat = &_qcota_stat[podev->pdev->id];
+
+	if (ret)
+		areq->err = -ENXIO;
+	else
+		areq->err = 0;
+
+	tasklet_schedule(&podev->done_tasklet);
+};
+
+static int start_req(struct ota_dev_control *podev)
+{
+	struct ota_async_req *areq;
+	struct qce_f9_req *pf9;
+	struct qce_f8_multi_pkt_req *p_mp_f8;
+	struct qce_f8_req *pf8;
+	int ret = 0;
+
+	/* start the command on the podev->active_command */
+	areq = podev->active_command;
+	areq->podev = podev;
+
+	switch (areq->op) {
+	case QCE_OTA_F8_OPER:
+		pf8 = &areq->req.f8_req;
+		ret = qce_f8_req(podev->qce, pf8, areq, f8_cb);
+		break;
+	case QCE_OTA_MPKT_F8_OPER:
+		p_mp_f8 = &areq->req.f8_mp_req;
+		ret = qce_f8_multi_pkt_req(podev->qce, p_mp_f8, areq, f8_cb);
+		break;
+
+	case QCE_OTA_F9_OPER:
+		pf9 = &areq->req.f9_req;
+		ret =  qce_f9_req(podev->qce, pf9, areq, f9_cb);
+		break;
+
+	default:
+		ret = -ENOTSUPP;
+		break;
+	};
+	areq->err = ret;
+	return ret;
+};
+
+static int submit_req(struct ota_async_req *areq, struct ota_dev_control *podev)
+{
+	unsigned long flags;
+	int ret = 0;
+	struct qcota_stat *pstat;
+
+	areq->err = 0;
+	spin_lock_irqsave(&podev->lock, flags);
+	if (podev->active_command == NULL) {
+		podev->active_command = areq;
+		ret = start_req(podev);
+	} else {
+		list_add_tail(&areq->list, &podev->ready_commands);
+	}
+
+	if (ret != 0)
+		podev->active_command = NULL;
+	spin_unlock_irqrestore(&podev->lock, flags);
+
+	if (ret == 0)
+		wait_for_completion(&areq->complete);
+
+	pstat = &_qcota_stat[podev->pdev->id];
+	switch (areq->op) {
+	case QCE_OTA_F8_OPER:
+		if (areq->err)
+			pstat->f8_op_fail++;
+		else
+			pstat->f8_op_success++;
+		break;
+
+	case QCE_OTA_MPKT_F8_OPER:
+
+		if (areq->err)
+			pstat->f8_mp_op_fail++;
+		else
+			pstat->f8_mp_op_success++;
+		break;
+
+	case QCE_OTA_F9_OPER:
+	default:
+		if (areq->err)
+			pstat->f9_op_fail++;
+		else
+			pstat->f9_op_success++;
+		break;
+	};
+
+	return areq->err;
+};
+
+static long qcota_ioctl(struct file *file,
+			  unsigned cmd, unsigned long arg)
+{
+	int err = 0;
+	struct ota_dev_control *podev;
+	uint8_t *user_src;
+	uint8_t *user_dst;
+	uint8_t *k_buf = NULL;
+	struct ota_async_req areq;
+	uint32_t total;
+	struct qcota_stat *pstat;
+
+	podev =  file->private_data;
+	if (podev == NULL || podev->magic != OTA_MAGIC) {
+		pr_err("%s: invalid handle %p\n",
+			__func__, podev);
+		return -ENOENT;
+	}
+
+	/* Verify user arguments. */
+	if (_IOC_TYPE(cmd) != QCOTA_IOC_MAGIC)
+		return -ENOTTY;
+
+	init_completion(&areq.complete);
+
+	pstat = &_qcota_stat[podev->pdev->id];
+
+	switch (cmd) {
+	case QCOTA_F9_REQ:
+		if (!access_ok(VERIFY_WRITE, (void __user *)arg,
+			       sizeof(struct qce_f9_req)))
+			return -EFAULT;
+		if (__copy_from_user(&areq.req.f9_req, (void __user *)arg,
+				     sizeof(struct qce_f9_req)))
+			return -EFAULT;
+
+		user_src = areq.req.f9_req.message;
+		if (!access_ok(VERIFY_READ, (void __user *)user_src,
+			       areq.req.f9_req.msize))
+			return -EFAULT;
+
+		k_buf = kmalloc(areq.req.f9_req.msize, GFP_KERNEL);
+		if (k_buf == NULL)
+			return -ENOMEM;
+
+		if (__copy_from_user(k_buf, (void __user *)user_src,
+				areq.req.f9_req.msize)) {
+			kfree(k_buf);
+			return -EFAULT;
+		}
+
+		areq.req.f9_req.message = k_buf;
+		areq.op = QCE_OTA_F9_OPER;
+
+		pstat->f9_req++;
+		err = submit_req(&areq, podev);
+
+		areq.req.f9_req.message = user_src;
+		if (err == 0 && __copy_to_user((void __user *)arg,
+				&areq.req.f9_req, sizeof(struct qce_f9_req))) {
+			err = -EFAULT;
+		}
+		kfree(k_buf);
+		break;
+
+	case QCOTA_F8_REQ:
+		if (!access_ok(VERIFY_WRITE, (void __user *)arg,
+			       sizeof(struct qce_f8_req)))
+			return -EFAULT;
+		if (__copy_from_user(&areq.req.f8_req, (void __user *)arg,
+				     sizeof(struct qce_f8_req)))
+			return -EFAULT;
+		total = areq.req.f8_req.data_len;
+		user_src = areq.req.f8_req.data_in;
+		if (user_src != NULL) {
+			if (!access_ok(VERIFY_READ, (void __user *)
+					user_src, total))
+				return -EFAULT;
+
+		};
+
+		user_dst = areq.req.f8_req.data_out;
+		if (!access_ok(VERIFY_WRITE, (void __user *)
+				user_dst, total))
+			return -EFAULT;
+
+		k_buf = kmalloc(total, GFP_KERNEL);
+		if (k_buf == NULL)
+			return -ENOMEM;
+
+		/* k_buf returned from kmalloc should be cache line aligned */
+		if (user_src && __copy_from_user(k_buf,
+				(void __user *)user_src, total)) {
+			kfree(k_buf);
+			return -EFAULT;
+		}
+
+		if (user_src)
+			areq.req.f8_req.data_in = k_buf;
+		else
+			areq.req.f8_req.data_in = NULL;
+		areq.req.f8_req.data_out = k_buf;
+
+		areq.op = QCE_OTA_F8_OPER;
+
+		pstat->f8_req++;
+		err = submit_req(&areq, podev);
+
+		if (err == 0 && __copy_to_user(user_dst, k_buf, total))
+			err = -EFAULT;
+		kfree(k_buf);
+
+		break;
+
+	case QCOTA_F8_MPKT_REQ:
+		if (!access_ok(VERIFY_WRITE, (void __user *)arg,
+			       sizeof(struct qce_f8_multi_pkt_req)))
+			return -EFAULT;
+		if (__copy_from_user(&areq.req.f8_mp_req, (void __user *)arg,
+				     sizeof(struct qce_f8_multi_pkt_req)))
+			return -EFAULT;
+
+		total = areq.req.f8_mp_req.num_pkt *
+				areq.req.f8_mp_req.qce_f8_req.data_len;
+
+		user_src = areq.req.f8_mp_req.qce_f8_req.data_in;
+		if (!access_ok(VERIFY_READ, (void __user *)
+				user_src, total))
+			return -EFAULT;
+
+		user_dst = areq.req.f8_mp_req.qce_f8_req.data_out;
+		if (!access_ok(VERIFY_WRITE, (void __user *)
+				user_dst, total))
+			return -EFAULT;
+
+		k_buf = kmalloc(total, GFP_KERNEL);
+		if (k_buf == NULL)
+			return -ENOMEM;
+		/* k_buf returned from kmalloc should be cache line aligned */
+		if (__copy_from_user(k_buf, (void __user *)user_src, total)) {
+			kfree(k_buf);
+
+			return -EFAULT;
+		}
+
+		areq.req.f8_mp_req.qce_f8_req.data_out = k_buf;
+		areq.req.f8_mp_req.qce_f8_req.data_in = k_buf;
+
+		areq.op = QCE_OTA_MPKT_F8_OPER;
+
+		pstat->f8_mp_req++;
+		err = submit_req(&areq, podev);
+
+		if (err == 0 && __copy_to_user(user_dst, k_buf, total))
+			err = -EFAULT;
+		kfree(k_buf);
+		break;
+
+	default:
+		return -ENOTTY;
+	}
+
+	return err;
+}
+
+static int qcota_probe(struct platform_device *pdev)
+{
+	void *handle = NULL;
+	int rc = 0;
+	struct ota_dev_control *podev;
+	struct ce_hw_support ce_support;
+
+	if (pdev->id >= MAX_OTA_DEVICE) {
+		pr_err("%s: device id %d  exceeds allowed %d\n",
+			__func__, pdev->id, MAX_OTA_DEVICE);
+		return -ENOENT;
+	}
+
+	podev = &qcota_dev[pdev->id];
+
+	INIT_LIST_HEAD(&podev->ready_commands);
+	podev->active_command = NULL;
+	spin_lock_init(&podev->lock);
+	tasklet_init(&podev->done_tasklet, req_done, (unsigned long)podev);
+
+	/* open qce */
+	handle = qce_open(pdev, &rc);
+	if (handle == NULL) {
+		pr_err("%s: device id %d, can not open qce\n",
+			__func__, pdev->id);
+		platform_set_drvdata(pdev, NULL);
+		return rc;
+	}
+	if (qce_hw_support(handle, &ce_support) < 0 ||
+					ce_support.ota == false) {
+		pr_err("%s: device id %d, qce does not support ota capability\n",
+			__func__, pdev->id);
+		rc = -ENODEV;
+		goto err;
+	}
+	podev->qce = handle;
+	podev->pdev = pdev;
+	platform_set_drvdata(pdev, podev);
+
+	rc = misc_register(&podev->miscdevice);
+	if (rc < 0)
+		goto err;
+
+	return 0;
+err:
+	if (handle)
+		qce_close(handle);
+	platform_set_drvdata(pdev, NULL);
+	podev->qce = NULL;
+	podev->pdev = NULL;
+	return rc;
+};
+
+static int qcota_remove(struct platform_device *pdev)
+{
+	struct ota_dev_control *podev;
+
+	podev = platform_get_drvdata(pdev);
+	if (!podev)
+		return 0;
+	if (podev->qce)
+		qce_close(podev->qce);
+
+	if (podev->miscdevice.minor != MISC_DYNAMIC_MINOR)
+		misc_deregister(&podev->miscdevice);
+	tasklet_kill(&podev->done_tasklet);
+	return 0;
+};
+
+static struct platform_driver qcota_plat_driver = {
+	.probe = qcota_probe,
+	.remove = qcota_remove,
+	.driver = {
+		.name = "qcota",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int _disp_stats(int id)
+{
+	struct qcota_stat *pstat;
+	int len = 0;
+
+	pstat = &_qcota_stat[id];
+	len = snprintf(_debug_read_buf, DEBUG_MAX_RW_BUF - 1,
+			"\nQualcomm OTA crypto accelerator %d Statistics:\n",
+				id + 1);
+
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   F8 request             : %d\n",
+					pstat->f8_req);
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   F8 operation success   : %d\n",
+					pstat->f8_op_success);
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   F8 operation fail      : %d\n",
+					pstat->f8_op_fail);
+
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   F8 MP request          : %d\n",
+					pstat->f8_mp_req);
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   F8 MP operation success: %d\n",
+					pstat->f8_mp_op_success);
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   F8 MP operation fail   : %d\n",
+					pstat->f8_mp_op_fail);
+
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   F9 request             : %d\n",
+					pstat->f9_req);
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   F9 operation success   : %d\n",
+					pstat->f9_op_success);
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   F9 operation fail      : %d\n",
+					pstat->f9_op_fail);
+
+	return len;
+}
+
+static int _debug_stats_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t _debug_stats_read(struct file *file, char __user *buf,
+			size_t count, loff_t *ppos)
+{
+	int rc = -EINVAL;
+	int qcota = *((int *) file->private_data);
+	int len;
+
+	len = _disp_stats(qcota);
+
+	rc = simple_read_from_buffer((void __user *) buf, len,
+			ppos, (void *) _debug_read_buf, len);
+
+	return rc;
+}
+
+static ssize_t _debug_stats_write(struct file *file, const char __user *buf,
+			size_t count, loff_t *ppos)
+{
+
+	int qcota = *((int *) file->private_data);
+
+	memset((char *)&_qcota_stat[qcota], 0, sizeof(struct qcota_stat));
+	return count;
+};
+
+static const struct file_operations _debug_stats_ops = {
+	.open =         _debug_stats_open,
+	.read =         _debug_stats_read,
+	.write =        _debug_stats_write,
+};
+
+static int _qcota_debug_init(void)
+{
+	int rc;
+	char name[DEBUG_MAX_FNAME];
+	int i;
+	struct dentry *dent;
+
+	_debug_dent = debugfs_create_dir("qcota", NULL);
+	if (IS_ERR(_debug_dent)) {
+		pr_err("qcota debugfs_create_dir fail, error %ld\n",
+				PTR_ERR(_debug_dent));
+		return PTR_ERR(_debug_dent);
+	}
+
+	for (i = 0; i < MAX_OTA_DEVICE; i++) {
+		snprintf(name, DEBUG_MAX_FNAME-1, "stats-%d", i+1);
+		_debug_qcota[i] = i;
+		dent = debugfs_create_file(name, 0644, _debug_dent,
+				&_debug_qcota[i], &_debug_stats_ops);
+		if (dent == NULL) {
+			pr_err("qcota debugfs_create_file fail, error %ld\n",
+					PTR_ERR(dent));
+			rc = PTR_ERR(dent);
+			goto err;
+		}
+	}
+	return 0;
+err:
+	debugfs_remove_recursive(_debug_dent);
+	return rc;
+}
+
+static int __init qcota_init(void)
+{
+	int rc;
+
+	rc = _qcota_debug_init();
+	if (rc)
+		return rc;
+	return platform_driver_register(&qcota_plat_driver);
+}
+static void __exit qcota_exit(void)
+{
+	debugfs_remove_recursive(_debug_dent);
+	platform_driver_unregister(&qcota_plat_driver);
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Rohit Vaswani <rvaswani@codeaurora.org>");
+MODULE_DESCRIPTION("Qualcomm Ota Crypto driver");
+MODULE_VERSION("1.01");
+
+module_init(qcota_init);
+module_exit(qcota_exit);
diff --git a/drivers/crypto/msm/qce.c b/drivers/crypto/msm/qce.c
new file mode 100644
index 0000000..55cf651
--- /dev/null
+++ b/drivers/crypto/msm/qce.c
@@ -0,0 +1,2709 @@
+/* Qualcomm Crypto Engine driver.
+ *
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/crypto.h>
+#include <crypto/hash.h>
+#include <crypto/sha.h>
+#include <linux/qcedev.h>
+#include <linux/qcota.h>
+#include <mach/dma.h>
+
+#include "qce.h"
+#include "qcryptohw_30.h"
+#include "qce_ota.h"
+
+/* ADM definitions */
+#define LI_SG_CMD  (1 << 31)    /* last index in the scatter gather cmd */
+#define SRC_INDEX_SG_CMD(index) ((index & 0x3fff) << 16)
+#define DST_INDEX_SG_CMD(index) (index & 0x3fff)
+#define ADM_DESC_LAST  (1 << 31)
+
+/* Data xfer between DM and CE in blocks of 16 bytes */
+#define ADM_CE_BLOCK_SIZE  16
+
+#define QCE_FIFO_SIZE  0x8000
+
+/* Data xfer between DM and CE in blocks of 64 bytes */
+#define ADM_SHA_BLOCK_SIZE  64
+
+#define ADM_DESC_LENGTH_MASK 0xffff
+#define ADM_DESC_LENGTH(x)  (x & ADM_DESC_LENGTH_MASK)
+
+struct dmov_desc {
+	uint32_t addr;
+	uint32_t len;
+};
+
+#define ADM_STATUS_OK 0x80000002
+
+/* Misc definitions */
+
+/* QCE max number of descriptor in a descriptor list */
+#define QCE_MAX_NUM_DESC    128
+
+/* State of DM channel */
+enum qce_chan_st_enum {
+	QCE_CHAN_STATE_IDLE = 0,
+	QCE_CHAN_STATE_IN_PROG = 1,
+	QCE_CHAN_STATE_COMP = 2,
+	QCE_CHAN_STATE_LAST
+};
+
+/*
+ * CE HW device structure.
+ * Each engine has an instance of the structure.
+ * Each engine can only handle one crypto operation at one time. It is up to
+ * the sw above to ensure single threading of operation on an engine.
+ */
+struct qce_device {
+	struct device *pdev;        /* Handle to platform_device structure */
+	unsigned char *coh_vmem;    /* Allocated coherent virtual memory */
+	dma_addr_t coh_pmem;	    /* Allocated coherent physical memory */
+	void __iomem *iobase;	    /* Virtual io base of CE HW  */
+	unsigned int phy_iobase;    /* Physical io base of CE HW    */
+	struct clk *ce_clk;	    /* Handle to CE clk */
+	unsigned int crci_in;	      /* CRCI for CE DM IN Channel   */
+	unsigned int crci_out;	      /* CRCI for CE DM OUT Channel   */
+	unsigned int crci_hash;	      /* CRCI for CE HASH   */
+	unsigned int chan_ce_in;      /* ADM channel used for CE input
+					* and auth result if authentication
+					* only operation. */
+	unsigned int chan_ce_out;     /* ADM channel used for CE output,
+					and icv for esp */
+
+
+	unsigned int *cmd_pointer_list_ce_in;
+	dma_addr_t  phy_cmd_pointer_list_ce_in;
+
+	unsigned int *cmd_pointer_list_ce_out;
+	dma_addr_t  phy_cmd_pointer_list_ce_out;
+
+	unsigned char *cmd_list_ce_in;
+	dma_addr_t  phy_cmd_list_ce_in;
+
+	unsigned char *cmd_list_ce_out;
+	dma_addr_t  phy_cmd_list_ce_out;
+
+	struct dmov_desc *ce_out_src_desc;
+	dma_addr_t  phy_ce_out_src_desc;
+
+	struct dmov_desc *ce_out_dst_desc;
+	dma_addr_t  phy_ce_out_dst_desc;
+
+	struct dmov_desc *ce_in_src_desc;
+	dma_addr_t  phy_ce_in_src_desc;
+
+	struct dmov_desc *ce_in_dst_desc;
+	dma_addr_t  phy_ce_in_dst_desc;
+
+	unsigned char *ce_out_ignore;
+	dma_addr_t phy_ce_out_ignore;
+
+	unsigned char *ce_pad;
+	dma_addr_t phy_ce_pad;
+
+	struct msm_dmov_cmd  *chan_ce_in_cmd;
+	struct msm_dmov_cmd  *chan_ce_out_cmd;
+
+	uint32_t ce_out_ignore_size;
+
+	int ce_out_dst_desc_index;
+	int ce_in_dst_desc_index;
+
+	int ce_out_src_desc_index;
+	int ce_in_src_desc_index;
+
+	enum qce_chan_st_enum chan_ce_in_state;		/* chan ce_in state */
+	enum qce_chan_st_enum chan_ce_out_state;	/* chan ce_out state */
+
+	int chan_ce_in_status;		/* chan ce_in status      */
+	int chan_ce_out_status;		/* chan ce_out status */
+
+
+	unsigned char *dig_result;
+	dma_addr_t phy_dig_result;
+
+	/* cached aes key */
+	uint32_t aeskey[AES256_KEY_SIZE/sizeof(uint32_t)];
+
+	uint32_t aes_key_size;		/* cached aes key size in bytes */
+	int fastaes;			/* ce supports fast aes */
+	int hmac;			/* ce support hmac-sha1 */
+	bool ota;			/* ce support ota */
+
+	qce_comp_func_ptr_t qce_cb;	/* qce callback function pointer */
+
+	int assoc_nents;
+	int src_nents;
+	int dst_nents;
+
+	void *areq;
+	enum qce_cipher_mode_enum mode;
+
+	dma_addr_t phy_iv_in;
+	dma_addr_t phy_ota_src;
+	dma_addr_t phy_ota_dst;
+	unsigned int ota_size;
+	int err;
+};
+
+/* Standard initialization vector for SHA-1, source: FIPS 180-2 */
+static uint32_t  _std_init_vector_sha1[] =   {
+	0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0
+};
+/* Standard initialization vector for SHA-256, source: FIPS 180-2 */
+static uint32_t _std_init_vector_sha256[] = {
+	0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
+	0x510E527F, 0x9B05688C,	0x1F83D9AB, 0x5BE0CD19
+};
+
+/* Source: FIPS 197, Figure 7. S-box: substitution values for the byte xy */
+static const uint32_t _s_box[256] = {
+	0x63, 0x7c, 0x77, 0x7b,   0xf2, 0x6b, 0x6f, 0xc5,
+	0x30, 0x01, 0x67, 0x2b,   0xfe, 0xd7, 0xab, 0x76,
+
+	0xca, 0x82, 0xc9, 0x7d,   0xfa, 0x59, 0x47, 0xf0,
+	0xad, 0xd4, 0xa2, 0xaf,   0x9c, 0xa4, 0x72, 0xc0,
+
+	0xb7, 0xfd, 0x93, 0x26,   0x36, 0x3f, 0xf7, 0xcc,
+	0x34, 0xa5, 0xe5, 0xf1,   0x71, 0xd8, 0x31, 0x15,
+
+	0x04, 0xc7, 0x23, 0xc3,   0x18, 0x96, 0x05, 0x9a,
+	0x07, 0x12, 0x80, 0xe2,   0xeb, 0x27, 0xb2, 0x75,
+
+	0x09, 0x83, 0x2c, 0x1a,   0x1b, 0x6e, 0x5a, 0xa0,
+	0x52, 0x3b, 0xd6, 0xb3,   0x29, 0xe3, 0x2f, 0x84,
+
+	0x53, 0xd1, 0x00, 0xed,   0x20, 0xfc, 0xb1, 0x5b,
+	0x6a, 0xcb, 0xbe, 0x39,   0x4a, 0x4c, 0x58, 0xcf,
+
+	0xd0, 0xef, 0xaa, 0xfb,   0x43, 0x4d, 0x33, 0x85,
+	0x45, 0xf9, 0x02, 0x7f,   0x50, 0x3c, 0x9f, 0xa8,
+
+	0x51, 0xa3, 0x40, 0x8f,   0x92, 0x9d, 0x38, 0xf5,
+	0xbc, 0xb6, 0xda, 0x21,   0x10, 0xff, 0xf3, 0xd2,
+
+	0xcd, 0x0c, 0x13, 0xec,   0x5f, 0x97, 0x44, 0x17,
+	0xc4, 0xa7, 0x7e, 0x3d,   0x64, 0x5d, 0x19, 0x73,
+
+	0x60, 0x81, 0x4f, 0xdc,   0x22, 0x2a, 0x90, 0x88,
+	0x46, 0xee, 0xb8, 0x14,   0xde, 0x5e, 0x0b, 0xdb,
+
+	0xe0, 0x32, 0x3a, 0x0a,   0x49, 0x06, 0x24, 0x5c,
+	0xc2, 0xd3, 0xac, 0x62,   0x91, 0x95, 0xe4, 0x79,
+
+	0xe7, 0xc8, 0x37, 0x6d,   0x8d, 0xd5, 0x4e, 0xa9,
+	0x6c, 0x56, 0xf4, 0xea,   0x65, 0x7a, 0xae, 0x08,
+
+	0xba, 0x78, 0x25, 0x2e,   0x1c, 0xa6, 0xb4, 0xc6,
+	0xe8, 0xdd, 0x74, 0x1f,   0x4b, 0xbd, 0x8b, 0x8a,
+
+	0x70, 0x3e, 0xb5, 0x66,   0x48, 0x03, 0xf6, 0x0e,
+	0x61, 0x35, 0x57, 0xb9,   0x86, 0xc1, 0x1d, 0x9e,
+
+	0xe1, 0xf8, 0x98, 0x11,   0x69, 0xd9, 0x8e, 0x94,
+	0x9b, 0x1e, 0x87, 0xe9,   0xce, 0x55, 0x28, 0xdf,
+
+	0x8c, 0xa1, 0x89, 0x0d,   0xbf, 0xe6, 0x42, 0x68,
+	0x41, 0x99, 0x2d, 0x0f,   0xb0, 0x54, 0xbb, 0x16 };
+
+
+/*
+ *	Source:	FIPS 197, Sec 5.2 Key Expansion, Figure 11. Pseudo Code for Key
+ *		Expansion.
+ */
+static void _aes_expand_key_schedule(uint32_t keysize, uint32_t *AES_KEY,
+		uint32_t *AES_RND_KEY)
+{
+	uint32_t i;
+	uint32_t Nk;
+	uint32_t Nr, rot_data;
+	uint32_t Rcon = 0x01000000;
+	uint32_t temp;
+	uint32_t data_in;
+	uint32_t MSB_store;
+	uint32_t byte_for_sub;
+	uint32_t word_sub[4];
+
+	switch (keysize) {
+	case 192:
+		Nk = 6;
+		Nr = 12;
+		break;
+
+	case 256:
+		Nk = 8;
+		Nr = 14;
+		break;
+
+	case 128:
+	default:  /* default to AES128 */
+		Nk = 4;
+		Nr = 10;
+		break;
+	}
+
+	/* key expansion */
+	i = 0;
+	while (i < Nk) {
+		AES_RND_KEY[i] = AES_KEY[i];
+		i = i + 1;
+	}
+
+	i = Nk;
+	while (i < (4 * (Nr + 1))) {
+		temp = AES_RND_KEY[i-1];
+		if (Nr == 14) {
+			switch (i) {
+			case 8:
+				Rcon = 0x01000000;
+				break;
+
+			case 16:
+				Rcon = 0x02000000;
+				break;
+
+			case 24:
+				Rcon = 0x04000000;
+				break;
+
+			case 32:
+				Rcon = 0x08000000;
+				break;
+
+			case 40:
+				Rcon = 0x10000000;
+				break;
+
+			case 48:
+				Rcon = 0x20000000;
+				break;
+
+			case 56:
+				Rcon = 0x40000000;
+				break;
+			}
+		} else if (Nr == 12) {
+			switch (i) {
+			case  6:
+				Rcon = 0x01000000;
+				break;
+
+			case 12:
+				Rcon = 0x02000000;
+				break;
+
+			case 18:
+				Rcon = 0x04000000;
+				break;
+
+			case 24:
+				Rcon = 0x08000000;
+				break;
+
+			case 30:
+				Rcon = 0x10000000;
+				break;
+
+			case 36:
+				Rcon = 0x20000000;
+				break;
+
+			case 42:
+				Rcon = 0x40000000;
+				break;
+
+			case 48:
+				Rcon = 0x80000000;
+				break;
+			}
+		} else if (Nr == 10) {
+			switch (i) {
+			case 4:
+				Rcon = 0x01000000;
+				break;
+
+			case 8:
+				Rcon = 0x02000000;
+				break;
+
+			case 12:
+				Rcon = 0x04000000;
+				break;
+
+			case 16:
+				Rcon = 0x08000000;
+				break;
+
+			case 20:
+				Rcon = 0x10000000;
+				break;
+
+			case 24:
+				Rcon = 0x20000000;
+				break;
+
+			case 28:
+				Rcon = 0x40000000;
+				break;
+
+			case 32:
+				Rcon = 0x80000000;
+				break;
+
+			case 36:
+				Rcon = 0x1b000000;
+				break;
+
+			case 40:
+				Rcon = 0x36000000;
+				break;
+			}
+		}
+
+		if ((i % Nk) == 0) {
+			data_in   = temp;
+			MSB_store = (data_in >> 24 & 0xff);
+			rot_data  = (data_in << 8) | MSB_store;
+			byte_for_sub = rot_data;
+			word_sub[0] = _s_box[(byte_for_sub & 0xff)];
+			word_sub[1] = (_s_box[((byte_for_sub & 0xff00) >> 8)]
+								<< 8);
+			word_sub[2] = (_s_box[((byte_for_sub & 0xff0000) >> 16)]
+								<< 16);
+			word_sub[3] = (_s_box[((byte_for_sub & 0xff000000)
+								>> 24)] << 24);
+			word_sub[0] =  word_sub[0] | word_sub[1] | word_sub[2] |
+							word_sub[3];
+			temp = word_sub[0] ^ Rcon;
+		} else if ((Nk > 6) && ((i % Nk) == 4)) {
+			byte_for_sub = temp;
+			word_sub[0] = _s_box[(byte_for_sub & 0xff)];
+			word_sub[1] = (_s_box[((byte_for_sub & 0xff00) >> 8)]
+								<< 8);
+			word_sub[2] = (_s_box[((byte_for_sub & 0xff0000) >> 16)]
+								<< 16);
+			word_sub[3] = (_s_box[((byte_for_sub & 0xff000000) >>
+								 24)] << 24);
+			word_sub[0] =  word_sub[0] | word_sub[1] | word_sub[2] |
+						word_sub[3];
+			temp = word_sub[0];
+		}
+
+		AES_RND_KEY[i] = AES_RND_KEY[i-Nk]^temp;
+		i = i+1;
+	}
+}
+
+static void _byte_stream_to_net_words(uint32_t *iv, unsigned char *b,
+		unsigned int len)
+{
+	unsigned n;
+
+	n = len  / sizeof(uint32_t) ;
+	for (; n > 0; n--) {
+		*iv =  ((*b << 24)      & 0xff000000) |
+				(((*(b+1)) << 16) & 0xff0000)   |
+				(((*(b+2)) << 8) & 0xff00)     |
+				(*(b+3)          & 0xff);
+		b += sizeof(uint32_t);
+		iv++;
+	}
+
+	n = len %  sizeof(uint32_t);
+	if (n == 3) {
+		*iv = ((*b << 24) & 0xff000000) |
+				(((*(b+1)) << 16) & 0xff0000)   |
+				(((*(b+2)) << 8) & 0xff00)     ;
+	} else if (n == 2) {
+		*iv = ((*b << 24) & 0xff000000) |
+				(((*(b+1)) << 16) & 0xff0000)   ;
+	} else if (n == 1) {
+		*iv = ((*b << 24) & 0xff000000) ;
+	}
+}
+
+static void _net_words_to_byte_stream(uint32_t *iv, unsigned char *b,
+		unsigned int len)
+{
+	unsigned n = len  / sizeof(uint32_t);
+
+	for (; n > 0; n--) {
+		*b++ = (unsigned char) ((*iv >> 24)   & 0xff);
+		*b++ = (unsigned char) ((*iv >> 16)   & 0xff);
+		*b++ = (unsigned char) ((*iv >> 8)    & 0xff);
+		*b++ = (unsigned char) (*iv           & 0xff);
+		iv++;
+	}
+	n = len % sizeof(uint32_t);
+	if (n == 3) {
+		*b++ = (unsigned char) ((*iv >> 24)   & 0xff);
+		*b++ = (unsigned char) ((*iv >> 16)   & 0xff);
+		*b =   (unsigned char) ((*iv >> 8)    & 0xff);
+	} else if (n == 2) {
+		*b++ = (unsigned char) ((*iv >> 24)   & 0xff);
+		*b =   (unsigned char) ((*iv >> 16)   & 0xff);
+	} else if (n == 1) {
+		*b =   (unsigned char) ((*iv >> 24)   & 0xff);
+	}
+}
+
+static int count_sg(struct scatterlist *sg, int nbytes)
+{
+	int i;
+
+	for (i = 0; nbytes > 0; i++, sg = sg_next(sg))
+		nbytes -= sg->length;
+	return i;
+}
+
+static int dma_map_pmem_sg(struct buf_info *pmem, unsigned entries,
+						struct scatterlist *sg)
+{
+	int i = 0;
+	for (i = 0; i < entries; i++) {
+
+		sg->dma_address = (dma_addr_t)pmem->offset;
+		sg++;
+		pmem++;
+	}
+	return 0;
+}
+
+static int _probe_ce_engine(struct qce_device *pce_dev)
+{
+	unsigned int val;
+	unsigned int rev;
+	unsigned int eng_availability;	/* engine available functions    */
+
+	val = readl_relaxed(pce_dev->iobase + CRYPTO_STATUS_REG);
+	if ((val & 0xfffffff) != 0x0200004) {
+		dev_err(pce_dev->pdev,
+				"unknown Qualcomm crypto device at 0x%x 0x%x\n",
+				pce_dev->phy_iobase, val);
+		return -EIO;
+	};
+	rev = (val & CRYPTO_CORE_REV_MASK) >> CRYPTO_CORE_REV;
+	if (rev == 0x2) {
+		dev_info(pce_dev->pdev,
+				"Qualcomm Crypto 3e device found at 0x%x\n",
+				pce_dev->phy_iobase);
+	} else if (rev == 0x1) {
+		dev_info(pce_dev->pdev,
+				"Qualcomm Crypto 3 device found at 0x%x\n",
+				pce_dev->phy_iobase);
+	} else if (rev == 0x0) {
+		dev_info(pce_dev->pdev,
+				"Qualcomm Crypto 2 device found at 0x%x\n",
+				pce_dev->phy_iobase);
+	} else {
+		dev_err(pce_dev->pdev,
+				"unknown Qualcomm crypto device at 0x%x\n",
+				pce_dev->phy_iobase);
+		return -EIO;
+	}
+
+	eng_availability = readl_relaxed(pce_dev->iobase +
+						CRYPTO_ENGINES_AVAIL);
+
+	if (((eng_availability & CRYPTO_AES_SEL_MASK) >> CRYPTO_AES_SEL)
+			== CRYPTO_AES_SEL_FAST)
+		pce_dev->fastaes = 1;
+	else
+		pce_dev->fastaes = 0;
+
+	if (eng_availability & (1 << CRYPTO_HMAC_SEL))
+		pce_dev->hmac = 1;
+	else
+		pce_dev->hmac = 0;
+
+	if ((eng_availability & (1 << CRYPTO_F9_SEL)) &&
+			(eng_availability & (1 << CRYPTO_F8_SEL)))
+		pce_dev->ota = true;
+	else
+		pce_dev->ota = false;
+
+	pce_dev->aes_key_size = 0;
+
+	return 0;
+};
+
+static int _init_ce_engine(struct qce_device *pce_dev)
+{
+	unsigned int val;
+
+	/* reset qce */
+	writel_relaxed(1 << CRYPTO_SW_RST, pce_dev->iobase + CRYPTO_CONFIG_REG);
+
+	/* Ensure previous instruction (write to reset bit)
+	 * was completed.
+	 */
+	mb();
+	/* configure ce */
+	val = (1 << CRYPTO_MASK_DOUT_INTR) | (1 << CRYPTO_MASK_DIN_INTR) |
+			(1 << CRYPTO_MASK_AUTH_DONE_INTR) |
+					(1 << CRYPTO_MASK_ERR_INTR);
+	writel_relaxed(val, pce_dev->iobase + CRYPTO_CONFIG_REG);
+
+	if (_probe_ce_engine(pce_dev) < 0)
+		return -EIO;
+	if (readl_relaxed(pce_dev->iobase + CRYPTO_CONFIG_REG) != val) {
+		dev_err(pce_dev->pdev,
+				"unknown Qualcomm crypto device at 0x%x\n",
+				pce_dev->phy_iobase);
+		return -EIO;
+	};
+	return 0;
+};
+
+static int _sha_ce_setup(struct qce_device *pce_dev, struct qce_sha_req *sreq)
+{
+	uint32_t auth32[SHA256_DIGEST_SIZE / sizeof(uint32_t)];
+	uint32_t diglen;
+	int rc;
+	int i;
+	uint32_t cfg = 0;
+
+	/* if not the last, the size has to be on the block boundary */
+	if (sreq->last_blk == 0 && (sreq->size % SHA256_BLOCK_SIZE))
+		return -EIO;
+
+	switch (sreq->alg) {
+	case QCE_HASH_SHA1:
+		diglen = SHA1_DIGEST_SIZE;
+		break;
+	case QCE_HASH_SHA256:
+		diglen = SHA256_DIGEST_SIZE;
+		break;
+	default:
+		return -EINVAL;
+	}
+	/*
+	 * write 20/32 bytes, 5/8 words into auth_iv
+	 *  for SHA1/SHA256
+	 */
+
+	if (sreq->first_blk) {
+		if (sreq->alg == QCE_HASH_SHA1) {
+			for (i = 0; i < 5; i++)
+				auth32[i] = _std_init_vector_sha1[i];
+		} else {
+			for (i = 0; i < 8; i++)
+				auth32[i] = _std_init_vector_sha256[i];
+		}
+	} else
+		_byte_stream_to_net_words(auth32, sreq->digest, diglen);
+
+	rc = clk_enable(pce_dev->ce_clk);
+	if (rc)
+		return rc;
+
+	writel_relaxed(auth32[0], pce_dev->iobase + CRYPTO_AUTH_IV0_REG);
+	writel_relaxed(auth32[1], pce_dev->iobase + CRYPTO_AUTH_IV1_REG);
+	writel_relaxed(auth32[2], pce_dev->iobase + CRYPTO_AUTH_IV2_REG);
+	writel_relaxed(auth32[3], pce_dev->iobase + CRYPTO_AUTH_IV3_REG);
+	writel_relaxed(auth32[4], pce_dev->iobase + CRYPTO_AUTH_IV4_REG);
+
+	if (sreq->alg == QCE_HASH_SHA256) {
+		writel_relaxed(auth32[5], pce_dev->iobase +
+							CRYPTO_AUTH_IV5_REG);
+		writel_relaxed(auth32[6], pce_dev->iobase +
+							CRYPTO_AUTH_IV6_REG);
+		writel_relaxed(auth32[7], pce_dev->iobase +
+							CRYPTO_AUTH_IV7_REG);
+	}
+	/* write auth_bytecnt 0/1, start with 0 */
+	writel_relaxed(sreq->auth_data[0], pce_dev->iobase +
+						CRYPTO_AUTH_BYTECNT0_REG);
+	writel_relaxed(sreq->auth_data[1], pce_dev->iobase +
+						CRYPTO_AUTH_BYTECNT1_REG);
+
+	/* write auth_seg_cfg */
+	writel_relaxed(sreq->size << CRYPTO_AUTH_SEG_SIZE,
+			pce_dev->iobase + CRYPTO_AUTH_SEG_CFG_REG);
+
+	/*
+	 * write seg_cfg
+	 */
+
+	if (sreq->alg == QCE_HASH_SHA1)
+		cfg |= (CRYPTO_AUTH_SIZE_SHA1 << CRYPTO_AUTH_SIZE);
+	else
+		cfg = (CRYPTO_AUTH_SIZE_SHA256 << CRYPTO_AUTH_SIZE);
+
+	if (sreq->first_blk)
+		cfg |= 1 << CRYPTO_FIRST;
+	if (sreq->last_blk)
+		cfg |= 1 << CRYPTO_LAST;
+	cfg |= CRYPTO_AUTH_ALG_SHA << CRYPTO_AUTH_ALG;
+	writel_relaxed(cfg, pce_dev->iobase + CRYPTO_SEG_CFG_REG);
+
+	/* write seg_size   */
+	writel_relaxed(sreq->size, pce_dev->iobase + CRYPTO_SEG_SIZE_REG);
+
+	/* issue go to crypto   */
+	writel_relaxed(1 << CRYPTO_GO, pce_dev->iobase + CRYPTO_GOPROC_REG);
+	/* Ensure previous instructions (setting the GO register)
+	 * was completed before issuing a DMA transfer request
+	 */
+	mb();
+
+	return 0;
+}
+
+static int _ce_setup(struct qce_device *pce_dev, struct qce_req *q_req,
+		uint32_t totallen, uint32_t coffset)
+{
+	uint32_t hmackey[HMAC_KEY_SIZE/sizeof(uint32_t)] = {
+			0, 0, 0, 0, 0};
+	uint32_t enckey32[MAX_CIPHER_KEY_SIZE/sizeof(uint32_t)] = {
+			0, 0, 0, 0, 0, 0, 0, 0};
+	uint32_t enciv32[MAX_IV_LENGTH / sizeof(uint32_t)] = {
+			0, 0, 0, 0};
+	uint32_t enck_size_in_word = q_req->encklen / sizeof(uint32_t);
+	int aes_key_chg;
+	int i, rc;
+	uint32_t aes_round_key[CRYPTO_AES_RNDKEYS];
+	uint32_t cfg;
+	uint32_t ivsize = q_req->ivsize;
+
+	rc = clk_enable(pce_dev->ce_clk);
+	if (rc)
+		return rc;
+
+	cfg = (1 << CRYPTO_FIRST) | (1 << CRYPTO_LAST);
+	if (q_req->op == QCE_REQ_AEAD) {
+
+		/* do authentication setup */
+
+		cfg |= (CRYPTO_AUTH_SIZE_HMAC_SHA1 << CRYPTO_AUTH_SIZE)|
+				(CRYPTO_AUTH_ALG_SHA << CRYPTO_AUTH_ALG);
+
+		/* write sha1 init vector */
+		writel_relaxed(_std_init_vector_sha1[0],
+				pce_dev->iobase + CRYPTO_AUTH_IV0_REG);
+		writel_relaxed(_std_init_vector_sha1[1],
+				pce_dev->iobase + CRYPTO_AUTH_IV1_REG);
+		writel_relaxed(_std_init_vector_sha1[2],
+				pce_dev->iobase + CRYPTO_AUTH_IV2_REG);
+		writel_relaxed(_std_init_vector_sha1[3],
+				pce_dev->iobase + CRYPTO_AUTH_IV3_REG);
+		writel_relaxed(_std_init_vector_sha1[4],
+				pce_dev->iobase + CRYPTO_AUTH_IV4_REG);
+		/* write hmac key */
+		_byte_stream_to_net_words(hmackey, q_req->authkey,
+						q_req->authklen);
+		writel_relaxed(hmackey[0], pce_dev->iobase +
+							CRYPTO_AUTH_IV5_REG);
+		writel_relaxed(hmackey[1], pce_dev->iobase +
+							CRYPTO_AUTH_IV6_REG);
+		writel_relaxed(hmackey[2], pce_dev->iobase +
+							CRYPTO_AUTH_IV7_REG);
+		writel_relaxed(hmackey[3], pce_dev->iobase +
+							CRYPTO_AUTH_IV8_REG);
+		writel_relaxed(hmackey[4], pce_dev->iobase +
+							CRYPTO_AUTH_IV9_REG);
+		writel_relaxed(0, pce_dev->iobase + CRYPTO_AUTH_BYTECNT0_REG);
+		writel_relaxed(0, pce_dev->iobase + CRYPTO_AUTH_BYTECNT1_REG);
+
+		/* write auth_seg_cfg */
+		writel_relaxed((totallen << CRYPTO_AUTH_SEG_SIZE) & 0xffff0000,
+				pce_dev->iobase + CRYPTO_AUTH_SEG_CFG_REG);
+
+	}
+
+	_byte_stream_to_net_words(enckey32, q_req->enckey, q_req->encklen);
+
+	switch (q_req->mode) {
+	case QCE_MODE_ECB:
+		cfg |= (CRYPTO_ENCR_MODE_ECB << CRYPTO_ENCR_MODE);
+		break;
+
+	case QCE_MODE_CBC:
+		cfg |= (CRYPTO_ENCR_MODE_CBC << CRYPTO_ENCR_MODE);
+		break;
+
+	case QCE_MODE_CTR:
+	default:
+		cfg |= (CRYPTO_ENCR_MODE_CTR << CRYPTO_ENCR_MODE);
+		break;
+	}
+	pce_dev->mode = q_req->mode;
+
+	switch (q_req->alg) {
+	case CIPHER_ALG_DES:
+		if (q_req->mode !=  QCE_MODE_ECB) {
+			_byte_stream_to_net_words(enciv32, q_req->iv, ivsize);
+			writel_relaxed(enciv32[0], pce_dev->iobase +
+						CRYPTO_CNTR0_IV0_REG);
+			writel_relaxed(enciv32[1], pce_dev->iobase +
+						CRYPTO_CNTR1_IV1_REG);
+		}
+		writel_relaxed(enckey32[0], pce_dev->iobase +
+							CRYPTO_DES_KEY0_REG);
+		writel_relaxed(enckey32[1], pce_dev->iobase +
+							CRYPTO_DES_KEY1_REG);
+		cfg |= ((CRYPTO_ENCR_KEY_SZ_DES << CRYPTO_ENCR_KEY_SZ)  |
+				(CRYPTO_ENCR_ALG_DES << CRYPTO_ENCR_ALG));
+		break;
+
+	case CIPHER_ALG_3DES:
+		if (q_req->mode !=  QCE_MODE_ECB) {
+			_byte_stream_to_net_words(enciv32, q_req->iv, ivsize);
+			writel_relaxed(enciv32[0], pce_dev->iobase +
+						CRYPTO_CNTR0_IV0_REG);
+			writel_relaxed(enciv32[1], pce_dev->iobase +
+						CRYPTO_CNTR1_IV1_REG);
+		}
+		writel_relaxed(enckey32[0], pce_dev->iobase +
+							CRYPTO_DES_KEY0_REG);
+		writel_relaxed(enckey32[1], pce_dev->iobase +
+							CRYPTO_DES_KEY1_REG);
+		writel_relaxed(enckey32[2], pce_dev->iobase +
+							CRYPTO_DES_KEY2_REG);
+		writel_relaxed(enckey32[3], pce_dev->iobase +
+							CRYPTO_DES_KEY3_REG);
+		writel_relaxed(enckey32[4], pce_dev->iobase +
+							CRYPTO_DES_KEY4_REG);
+		writel_relaxed(enckey32[5], pce_dev->iobase +
+							CRYPTO_DES_KEY5_REG);
+		cfg |= ((CRYPTO_ENCR_KEY_SZ_3DES << CRYPTO_ENCR_KEY_SZ)  |
+				(CRYPTO_ENCR_ALG_DES << CRYPTO_ENCR_ALG));
+		break;
+
+	case CIPHER_ALG_AES:
+	default:
+		if (q_req->mode !=  QCE_MODE_ECB) {
+			_byte_stream_to_net_words(enciv32, q_req->iv, ivsize);
+			writel_relaxed(enciv32[0], pce_dev->iobase +
+						CRYPTO_CNTR0_IV0_REG);
+			writel_relaxed(enciv32[1], pce_dev->iobase +
+						CRYPTO_CNTR1_IV1_REG);
+			writel_relaxed(enciv32[2], pce_dev->iobase +
+						CRYPTO_CNTR2_IV2_REG);
+			writel_relaxed(enciv32[3], pce_dev->iobase +
+						CRYPTO_CNTR3_IV3_REG);
+		}
+		/* set number of counter bits */
+		writel_relaxed(0xffff, pce_dev->iobase + CRYPTO_CNTR_MASK_REG);
+
+		if (q_req->op == QCE_REQ_ABLK_CIPHER_NO_KEY) {
+				cfg |= (CRYPTO_ENCR_KEY_SZ_AES128 <<
+						CRYPTO_ENCR_KEY_SZ);
+			cfg |= CRYPTO_ENCR_ALG_AES << CRYPTO_ENCR_ALG;
+		} else {
+			switch (q_req->encklen) {
+			case AES128_KEY_SIZE:
+				cfg |= (CRYPTO_ENCR_KEY_SZ_AES128 <<
+							CRYPTO_ENCR_KEY_SZ);
+				break;
+			case AES192_KEY_SIZE:
+				cfg |= (CRYPTO_ENCR_KEY_SZ_AES192 <<
+							CRYPTO_ENCR_KEY_SZ);
+				break;
+			case AES256_KEY_SIZE:
+			default:
+				cfg |= (CRYPTO_ENCR_KEY_SZ_AES256 <<
+							CRYPTO_ENCR_KEY_SZ);
+
+				/* check for null key. If null, use hw key*/
+				for (i = 0; i < enck_size_in_word; i++) {
+					if (enckey32[i] != 0)
+						break;
+				}
+				if (i == enck_size_in_word)
+					cfg |= 1 << CRYPTO_USE_HW_KEY;
+				break;
+			} /* end of switch (q_req->encklen) */
+
+			cfg |= CRYPTO_ENCR_ALG_AES << CRYPTO_ENCR_ALG;
+			if (pce_dev->aes_key_size !=  q_req->encklen)
+				aes_key_chg = 1;
+			else {
+				for (i = 0; i < enck_size_in_word; i++) {
+					if (enckey32[i] != pce_dev->aeskey[i])
+						break;
+				}
+				aes_key_chg = (i == enck_size_in_word) ? 0 : 1;
+			}
+
+			if (aes_key_chg) {
+				if (pce_dev->fastaes) {
+					for (i = 0; i < enck_size_in_word;
+									i++) {
+						writel_relaxed(enckey32[i],
+							pce_dev->iobase +
+							CRYPTO_AES_RNDKEY0 +
+							(i * sizeof(uint32_t)));
+					}
+				} else {
+					/* size in bit */
+					_aes_expand_key_schedule(
+						q_req->encklen * 8,
+						enckey32, aes_round_key);
+
+					for (i = 0; i < CRYPTO_AES_RNDKEYS;
+									i++) {
+						writel_relaxed(aes_round_key[i],
+							pce_dev->iobase +
+							CRYPTO_AES_RNDKEY0 +
+							(i * sizeof(uint32_t)));
+					}
+				}
+
+				pce_dev->aes_key_size = q_req->encklen;
+				for (i = 0; i < enck_size_in_word; i++)
+					pce_dev->aeskey[i] = enckey32[i];
+			} /*if (aes_key_chg) { */
+		} /* else of if (q_req->op == QCE_REQ_ABLK_CIPHER_NO_KEY) */
+		break;
+	} /* end of switch (q_req->mode)  */
+
+	if (q_req->dir == QCE_ENCRYPT)
+		cfg |= (1 << CRYPTO_AUTH_POS);
+	cfg |= ((q_req->dir == QCE_ENCRYPT) ? 1 : 0) << CRYPTO_ENCODE;
+
+	/* write encr seg cfg */
+	writel_relaxed((q_req->cryptlen << CRYPTO_ENCR_SEG_SIZE) |
+			(coffset & 0xffff),      /* cipher offset */
+			pce_dev->iobase + CRYPTO_ENCR_SEG_CFG_REG);
+
+	/* write seg cfg and size */
+	writel_relaxed(cfg, pce_dev->iobase + CRYPTO_SEG_CFG_REG);
+	writel_relaxed(totallen, pce_dev->iobase + CRYPTO_SEG_SIZE_REG);
+
+	/* issue go to crypto   */
+	writel_relaxed(1 << CRYPTO_GO, pce_dev->iobase + CRYPTO_GOPROC_REG);
+	/* Ensure previous instructions (setting the GO register)
+	 * was completed before issuing a DMA transfer request
+	 */
+	mb();
+	return 0;
+};
+
+static int _aead_complete(struct qce_device *pce_dev)
+{
+	struct aead_request *areq;
+	struct crypto_aead *aead;
+	uint32_t ivsize;
+	uint32_t iv_out[4];
+	unsigned char iv[4 * sizeof(uint32_t)];
+	uint32_t status;
+
+	areq = (struct aead_request *) pce_dev->areq;
+	aead = crypto_aead_reqtfm(areq);
+	ivsize = crypto_aead_ivsize(aead);
+
+	if (areq->src != areq->dst) {
+		dma_unmap_sg(pce_dev->pdev, areq->dst, pce_dev->dst_nents,
+					DMA_FROM_DEVICE);
+	}
+	dma_unmap_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
+			(areq->src == areq->dst) ? DMA_BIDIRECTIONAL :
+							DMA_TO_DEVICE);
+	dma_unmap_single(pce_dev->pdev, pce_dev->phy_iv_in,
+			ivsize, DMA_TO_DEVICE);
+	dma_unmap_sg(pce_dev->pdev, areq->assoc, pce_dev->assoc_nents,
+			DMA_TO_DEVICE);
+
+	/* check ce error status */
+	status = readl_relaxed(pce_dev->iobase + CRYPTO_STATUS_REG);
+	if (status & (1 << CRYPTO_SW_ERR)) {
+		pce_dev->err++;
+		dev_err(pce_dev->pdev,
+			"Qualcomm Crypto Error at 0x%x, status%x\n",
+			pce_dev->phy_iobase, status);
+		_init_ce_engine(pce_dev);
+		clk_disable(pce_dev->ce_clk);
+		pce_dev->qce_cb(areq, pce_dev->dig_result, NULL, -ENXIO);
+		return 0;
+	};
+
+	/* get iv out */
+	if (pce_dev->mode == QCE_MODE_ECB) {
+		clk_disable(pce_dev->ce_clk);
+		pce_dev->qce_cb(areq, pce_dev->dig_result, NULL,
+				pce_dev->chan_ce_in_status |
+				pce_dev->chan_ce_out_status);
+	} else {
+
+		iv_out[0] = readl_relaxed(pce_dev->iobase +
+							CRYPTO_CNTR0_IV0_REG);
+		iv_out[1] = readl_relaxed(pce_dev->iobase +
+							CRYPTO_CNTR1_IV1_REG);
+		iv_out[2] = readl_relaxed(pce_dev->iobase +
+							CRYPTO_CNTR2_IV2_REG);
+		iv_out[3] = readl_relaxed(pce_dev->iobase +
+							CRYPTO_CNTR3_IV3_REG);
+
+		_net_words_to_byte_stream(iv_out, iv, sizeof(iv));
+		clk_disable(pce_dev->ce_clk);
+		pce_dev->qce_cb(areq, pce_dev->dig_result, iv,
+				pce_dev->chan_ce_in_status |
+				pce_dev->chan_ce_out_status);
+	};
+	return 0;
+};
+
+static void _sha_complete(struct qce_device *pce_dev)
+{
+
+	struct ahash_request *areq;
+	uint32_t auth_data[2];
+	uint32_t status;
+
+	areq = (struct ahash_request *) pce_dev->areq;
+	dma_unmap_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
+				DMA_TO_DEVICE);
+
+	/* check ce error status */
+	status = readl_relaxed(pce_dev->iobase + CRYPTO_STATUS_REG);
+	if (status & (1 << CRYPTO_SW_ERR)) {
+		pce_dev->err++;
+		dev_err(pce_dev->pdev,
+			"Qualcomm Crypto Error at 0x%x, status%x\n",
+			pce_dev->phy_iobase, status);
+		_init_ce_engine(pce_dev);
+		clk_disable(pce_dev->ce_clk);
+		pce_dev->qce_cb(areq, pce_dev->dig_result, NULL, -ENXIO);
+		return;
+	};
+
+	auth_data[0] = readl_relaxed(pce_dev->iobase +
+						CRYPTO_AUTH_BYTECNT0_REG);
+	auth_data[1] = readl_relaxed(pce_dev->iobase +
+						CRYPTO_AUTH_BYTECNT1_REG);
+	/* Ensure previous instruction (retriving byte count information)
+	 * was completed before disabling the clk.
+	 */
+	mb();
+	clk_disable(pce_dev->ce_clk);
+	pce_dev->qce_cb(areq,  pce_dev->dig_result, (unsigned char *)auth_data,
+				pce_dev->chan_ce_in_status);
+};
+
+static int _ablk_cipher_complete(struct qce_device *pce_dev)
+{
+	struct ablkcipher_request *areq;
+	uint32_t iv_out[4];
+	unsigned char iv[4 * sizeof(uint32_t)];
+	uint32_t status;
+
+	areq = (struct ablkcipher_request *) pce_dev->areq;
+
+	if (areq->src != areq->dst) {
+		dma_unmap_sg(pce_dev->pdev, areq->dst,
+			pce_dev->dst_nents, DMA_FROM_DEVICE);
+	}
+	dma_unmap_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
+		(areq->src == areq->dst) ? DMA_BIDIRECTIONAL :
+						DMA_TO_DEVICE);
+
+	/* check ce error status */
+	status = readl_relaxed(pce_dev->iobase + CRYPTO_STATUS_REG);
+	if (status & (1 << CRYPTO_SW_ERR)) {
+		pce_dev->err++;
+		dev_err(pce_dev->pdev,
+			"Qualcomm Crypto Error at 0x%x, status%x\n",
+			pce_dev->phy_iobase, status);
+		_init_ce_engine(pce_dev);
+		clk_disable(pce_dev->ce_clk);
+		pce_dev->qce_cb(areq, NULL, NULL, -ENXIO);
+		return 0;
+	};
+
+	/* get iv out */
+	if (pce_dev->mode == QCE_MODE_ECB) {
+		clk_disable(pce_dev->ce_clk);
+		pce_dev->qce_cb(areq, NULL, NULL, pce_dev->chan_ce_in_status |
+					pce_dev->chan_ce_out_status);
+	} else {
+		iv_out[0] = readl_relaxed(pce_dev->iobase +
+							CRYPTO_CNTR0_IV0_REG);
+		iv_out[1] = readl_relaxed(pce_dev->iobase +
+							CRYPTO_CNTR1_IV1_REG);
+		iv_out[2] = readl_relaxed(pce_dev->iobase +
+							CRYPTO_CNTR2_IV2_REG);
+		iv_out[3] = readl_relaxed(pce_dev->iobase +
+							CRYPTO_CNTR3_IV3_REG);
+
+		_net_words_to_byte_stream(iv_out, iv, sizeof(iv));
+		clk_disable(pce_dev->ce_clk);
+		pce_dev->qce_cb(areq, NULL, iv, pce_dev->chan_ce_in_status |
+					pce_dev->chan_ce_out_status);
+	}
+
+	return 0;
+};
+
+static int _ablk_cipher_use_pmem_complete(struct qce_device *pce_dev)
+{
+	struct ablkcipher_request *areq;
+	uint32_t iv_out[4];
+	unsigned char iv[4 * sizeof(uint32_t)];
+	uint32_t status;
+
+	areq = (struct ablkcipher_request *) pce_dev->areq;
+
+	/* check ce error status */
+	status = readl_relaxed(pce_dev->iobase + CRYPTO_STATUS_REG);
+	if (status & (1 << CRYPTO_SW_ERR)) {
+		pce_dev->err++;
+		dev_err(pce_dev->pdev,
+			"Qualcomm Crypto Error at 0x%x, status%x\n",
+			pce_dev->phy_iobase, status);
+		_init_ce_engine(pce_dev);
+		clk_disable(pce_dev->ce_clk);
+		pce_dev->qce_cb(areq, NULL, NULL, -ENXIO);
+		return 0;
+	};
+
+	/* get iv out */
+	if (pce_dev->mode == QCE_MODE_ECB) {
+		clk_disable(pce_dev->ce_clk);
+		pce_dev->qce_cb(areq, NULL, NULL, pce_dev->chan_ce_in_status |
+					pce_dev->chan_ce_out_status);
+	} else {
+		iv_out[0] = readl_relaxed(pce_dev->iobase +
+							CRYPTO_CNTR0_IV0_REG);
+		iv_out[1] = readl_relaxed(pce_dev->iobase +
+							CRYPTO_CNTR1_IV1_REG);
+		iv_out[2] = readl_relaxed(pce_dev->iobase +
+							CRYPTO_CNTR2_IV2_REG);
+		iv_out[3] = readl_relaxed(pce_dev->iobase +
+							CRYPTO_CNTR3_IV3_REG);
+
+		_net_words_to_byte_stream(iv_out, iv, sizeof(iv));
+		clk_disable(pce_dev->ce_clk);
+		pce_dev->qce_cb(areq, NULL, iv, pce_dev->chan_ce_in_status |
+					pce_dev->chan_ce_out_status);
+	}
+
+	return 0;
+};
+
+static int qce_split_and_insert_dm_desc(struct dmov_desc *pdesc,
+			unsigned int plen, unsigned int paddr, int *index)
+{
+	while (plen > QCE_FIFO_SIZE) {
+		pdesc->len = QCE_FIFO_SIZE;
+		if (paddr > 0) {
+			pdesc->addr = paddr;
+			paddr += QCE_FIFO_SIZE;
+		}
+		plen -= pdesc->len;
+		if (plen > 0) {
+			*index = (*index) + 1;
+			if ((*index) >= QCE_MAX_NUM_DESC)
+				return -ENOMEM;
+			pdesc++;
+		}
+	}
+	if ((plen > 0) && (plen <= QCE_FIFO_SIZE)) {
+		pdesc->len = plen;
+		if (paddr > 0)
+			pdesc->addr = paddr;
+	}
+
+	return 0;
+}
+
+static int _chain_sg_buffer_in(struct qce_device *pce_dev,
+		struct scatterlist *sg, unsigned int nbytes)
+{
+	unsigned int len;
+	unsigned int dlen;
+	struct dmov_desc *pdesc;
+
+	pdesc = pce_dev->ce_in_src_desc + pce_dev->ce_in_src_desc_index;
+	/*
+	 * Two consective chunks may be handled by the old
+	 * buffer descriptor.
+	 */
+	while (nbytes > 0) {
+		len = min(nbytes, sg_dma_len(sg));
+		dlen = pdesc->len & ADM_DESC_LENGTH_MASK;
+		nbytes -= len;
+		if (dlen == 0) {
+			pdesc->addr  = sg_dma_address(sg);
+			pdesc->len = len;
+			if (pdesc->len > QCE_FIFO_SIZE) {
+				if (qce_split_and_insert_dm_desc(pdesc,
+						pdesc->len, sg_dma_address(sg),
+						&pce_dev->ce_in_src_desc_index))
+					return -EIO;
+			}
+		} else if (sg_dma_address(sg) == (pdesc->addr + dlen)) {
+			pdesc->len  = dlen + len;
+			if (pdesc->len > QCE_FIFO_SIZE) {
+				if (qce_split_and_insert_dm_desc(pdesc,
+						pdesc->len, pdesc->addr,
+						&pce_dev->ce_in_src_desc_index))
+					return -EIO;
+			}
+		} else {
+			pce_dev->ce_in_src_desc_index++;
+			if (pce_dev->ce_in_src_desc_index >= QCE_MAX_NUM_DESC)
+				return -ENOMEM;
+			pdesc++;
+			pdesc->len = len;
+			pdesc->addr = sg_dma_address(sg);
+			if (pdesc->len > QCE_FIFO_SIZE) {
+				if (qce_split_and_insert_dm_desc(pdesc,
+						pdesc->len, sg_dma_address(sg),
+						&pce_dev->ce_in_src_desc_index))
+					return -EIO;
+			}
+		}
+		if (nbytes > 0)
+			sg = sg_next(sg);
+	}
+	return 0;
+}
+
+static int _chain_pm_buffer_in(struct qce_device *pce_dev,
+		unsigned int pmem, unsigned int nbytes)
+{
+	unsigned int dlen;
+	struct dmov_desc *pdesc;
+
+	pdesc = pce_dev->ce_in_src_desc + pce_dev->ce_in_src_desc_index;
+	dlen = pdesc->len & ADM_DESC_LENGTH_MASK;
+	if (dlen == 0) {
+		pdesc->addr  = pmem;
+		pdesc->len = nbytes;
+	} else if (pmem == (pdesc->addr + dlen)) {
+		pdesc->len  = dlen + nbytes;
+	} else {
+		pce_dev->ce_in_src_desc_index++;
+		if (pce_dev->ce_in_src_desc_index >= QCE_MAX_NUM_DESC)
+			return -ENOMEM;
+		pdesc++;
+		pdesc->len = nbytes;
+		pdesc->addr = pmem;
+	}
+	return 0;
+}
+
+static void _chain_buffer_in_init(struct qce_device *pce_dev)
+{
+	struct dmov_desc *pdesc;
+
+	pce_dev->ce_in_src_desc_index = 0;
+	pce_dev->ce_in_dst_desc_index = 0;
+	pdesc = pce_dev->ce_in_src_desc;
+	pdesc->len = 0;
+}
+
+static void _ce_in_final(struct qce_device *pce_dev, int ncmd, unsigned total)
+{
+	struct dmov_desc *pdesc;
+	dmov_sg *pcmd;
+
+	pdesc = pce_dev->ce_in_src_desc + pce_dev->ce_in_src_desc_index;
+	pdesc->len |= ADM_DESC_LAST;
+
+	pdesc = pce_dev->ce_in_dst_desc;
+	if (total > QCE_FIFO_SIZE) {
+		qce_split_and_insert_dm_desc(pdesc, total, 0,
+				&pce_dev->ce_in_dst_desc_index);
+		pdesc = pce_dev->ce_in_dst_desc + pce_dev->ce_in_dst_desc_index;
+		pdesc->len |= ADM_DESC_LAST;
+	} else
+		pdesc->len = ADM_DESC_LAST | total;
+
+	pcmd = (dmov_sg *) pce_dev->cmd_list_ce_in;
+	if (ncmd == 1)
+		pcmd->cmd |= CMD_LC;
+	else {
+		dmov_s  *pscmd;
+
+		pcmd->cmd &= ~CMD_LC;
+		pcmd++;
+		pscmd = (dmov_s *)pcmd;
+		pscmd->cmd |= CMD_LC;
+	}
+
+#ifdef QCE_DEBUG
+	dev_info(pce_dev->pdev, "_ce_in_final %d\n",
+					pce_dev->ce_in_src_desc_index);
+#endif
+}
+
+#ifdef QCE_DEBUG
+static void _ce_in_dump(struct qce_device *pce_dev)
+{
+	int i;
+	struct dmov_desc *pdesc;
+
+	dev_info(pce_dev->pdev, "_ce_in_dump: src\n");
+	for (i = 0; i <= pce_dev->ce_in_src_desc_index; i++) {
+		pdesc = pce_dev->ce_in_src_desc + i;
+		dev_info(pce_dev->pdev, "%x , %x\n", pdesc->addr,
+				pdesc->len);
+	}
+	dev_info(pce_dev->pdev, "_ce_in_dump: dst\n");
+	for (i = 0; i <= pce_dev->ce_in_dst_desc_index; i++) {
+		pdesc = pce_dev->ce_in_dst_desc + i;
+		dev_info(pce_dev->pdev, "%x , %x\n", pdesc->addr,
+				pdesc->len);
+	}
+};
+
+static void _ce_out_dump(struct qce_device *pce_dev)
+{
+	int i;
+	struct dmov_desc *pdesc;
+
+	dev_info(pce_dev->pdev, "_ce_out_dump: src\n");
+	for (i = 0; i <= pce_dev->ce_out_src_desc_index; i++) {
+		pdesc = pce_dev->ce_out_src_desc + i;
+		dev_info(pce_dev->pdev, "%x , %x\n", pdesc->addr,
+				pdesc->len);
+	}
+
+	dev_info(pce_dev->pdev, "_ce_out_dump: dst\n");
+	for (i = 0; i <= pce_dev->ce_out_dst_desc_index; i++) {
+		pdesc = pce_dev->ce_out_dst_desc + i;
+		dev_info(pce_dev->pdev, "%x , %x\n", pdesc->addr,
+				pdesc->len);
+	}
+};
+#endif
+
+static int _chain_sg_buffer_out(struct qce_device *pce_dev,
+		struct scatterlist *sg, unsigned int nbytes)
+{
+	unsigned int len;
+	unsigned int dlen;
+	struct dmov_desc *pdesc;
+
+	pdesc = pce_dev->ce_out_dst_desc + pce_dev->ce_out_dst_desc_index;
+	/*
+	 * Two consective chunks may be handled by the old
+	 * buffer descriptor.
+	 */
+	while (nbytes > 0) {
+		len = min(nbytes, sg_dma_len(sg));
+		dlen = pdesc->len & ADM_DESC_LENGTH_MASK;
+		nbytes -= len;
+		if (dlen == 0) {
+			pdesc->addr  = sg_dma_address(sg);
+			pdesc->len = len;
+			if (pdesc->len > QCE_FIFO_SIZE) {
+				if (qce_split_and_insert_dm_desc(pdesc,
+					pdesc->len, sg_dma_address(sg),
+					&pce_dev->ce_out_dst_desc_index))
+					return -EIO;
+			}
+		} else if (sg_dma_address(sg) == (pdesc->addr + dlen)) {
+			pdesc->len  = dlen + len;
+			if (pdesc->len > QCE_FIFO_SIZE) {
+				if (qce_split_and_insert_dm_desc(pdesc,
+					pdesc->len, pdesc->addr,
+					&pce_dev->ce_out_dst_desc_index))
+					return -EIO;
+			}
+		} else {
+			pce_dev->ce_out_dst_desc_index++;
+			if (pce_dev->ce_out_dst_desc_index >= QCE_MAX_NUM_DESC)
+				return -EIO;
+			pdesc++;
+			pdesc->len = len;
+			pdesc->addr = sg_dma_address(sg);
+			if (pdesc->len > QCE_FIFO_SIZE) {
+				if (qce_split_and_insert_dm_desc(pdesc,
+					pdesc->len, sg_dma_address(sg),
+					&pce_dev->ce_out_dst_desc_index))
+					return -EIO;
+			}
+		}
+		if (nbytes > 0)
+			sg = sg_next(sg);
+	}
+	return 0;
+}
+
+static int _chain_pm_buffer_out(struct qce_device *pce_dev,
+		unsigned int pmem, unsigned int nbytes)
+{
+	unsigned int dlen;
+	struct dmov_desc *pdesc;
+
+	pdesc = pce_dev->ce_out_dst_desc + pce_dev->ce_out_dst_desc_index;
+	dlen = pdesc->len & ADM_DESC_LENGTH_MASK;
+
+	if (dlen == 0) {
+		pdesc->addr  = pmem;
+		pdesc->len = nbytes;
+	} else if (pmem == (pdesc->addr + dlen)) {
+		pdesc->len  = dlen + nbytes;
+	} else {
+		pce_dev->ce_out_dst_desc_index++;
+		if (pce_dev->ce_out_dst_desc_index >= QCE_MAX_NUM_DESC)
+			return -EIO;
+		pdesc++;
+		pdesc->len = nbytes;
+		pdesc->addr = pmem;
+	}
+	return 0;
+};
+
+static void _chain_buffer_out_init(struct qce_device *pce_dev)
+{
+	struct dmov_desc *pdesc;
+
+	pce_dev->ce_out_dst_desc_index = 0;
+	pce_dev->ce_out_src_desc_index = 0;
+	pdesc = pce_dev->ce_out_dst_desc;
+	pdesc->len = 0;
+};
+
+static void _ce_out_final(struct qce_device *pce_dev, int ncmd, unsigned total)
+{
+	struct dmov_desc *pdesc;
+	dmov_sg *pcmd;
+
+	pdesc = pce_dev->ce_out_dst_desc + pce_dev->ce_out_dst_desc_index;
+	pdesc->len |= ADM_DESC_LAST;
+
+	pdesc = pce_dev->ce_out_src_desc;
+	if (total > QCE_FIFO_SIZE) {
+		qce_split_and_insert_dm_desc(pdesc, total, 0,
+				&pce_dev->ce_out_src_desc_index);
+		pdesc = pce_dev->ce_out_src_desc +
+					pce_dev->ce_out_src_desc_index;
+		pdesc->len |= ADM_DESC_LAST;
+	} else
+		pdesc->len = ADM_DESC_LAST | total;
+
+	pcmd = (dmov_sg *) pce_dev->cmd_list_ce_out;
+	if (ncmd == 1)
+		pcmd->cmd |= CMD_LC;
+	else {
+		dmov_s  *pscmd;
+
+		pcmd->cmd &= ~CMD_LC;
+		pcmd++;
+		pscmd = (dmov_s *)pcmd;
+		pscmd->cmd |= CMD_LC;
+	}
+#ifdef QCE_DEBUG
+	dev_info(pce_dev->pdev, "_ce_out_final %d\n",
+			pce_dev->ce_out_dst_desc_index);
+#endif
+
+};
+
+static void _aead_ce_in_call_back(struct msm_dmov_cmd *cmd_ptr,
+		unsigned int result, struct msm_dmov_errdata *err)
+{
+	struct qce_device *pce_dev;
+
+	pce_dev = (struct qce_device *) cmd_ptr->user;
+	if (result != ADM_STATUS_OK) {
+		dev_err(pce_dev->pdev, "Qualcomm ADM status error %x\n",
+							result);
+		pce_dev->chan_ce_in_status = -1;
+	} else
+		pce_dev->chan_ce_in_status = 0;
+
+	pce_dev->chan_ce_in_state = QCE_CHAN_STATE_COMP;
+	if (pce_dev->chan_ce_out_state == QCE_CHAN_STATE_COMP) {
+		pce_dev->chan_ce_in_state = QCE_CHAN_STATE_IDLE;
+		pce_dev->chan_ce_out_state = QCE_CHAN_STATE_IDLE;
+
+		/* done */
+		_aead_complete(pce_dev);
+	}
+};
+
+static void _aead_ce_out_call_back(struct msm_dmov_cmd *cmd_ptr,
+		unsigned int result, struct msm_dmov_errdata *err)
+{
+	struct qce_device *pce_dev;
+
+	pce_dev = (struct qce_device *) cmd_ptr->user;
+	if (result != ADM_STATUS_OK) {
+		dev_err(pce_dev->pdev, "Qualcomm ADM status error %x\n",
+							result);
+		pce_dev->chan_ce_out_status = -1;
+	} else {
+		pce_dev->chan_ce_out_status = 0;
+	};
+
+	pce_dev->chan_ce_out_state = QCE_CHAN_STATE_COMP;
+	if (pce_dev->chan_ce_in_state == QCE_CHAN_STATE_COMP) {
+		pce_dev->chan_ce_in_state = QCE_CHAN_STATE_IDLE;
+		pce_dev->chan_ce_out_state = QCE_CHAN_STATE_IDLE;
+
+		/* done */
+		_aead_complete(pce_dev);
+	}
+
+};
+
+static void _sha_ce_in_call_back(struct msm_dmov_cmd *cmd_ptr,
+		unsigned int result, struct msm_dmov_errdata *err)
+{
+	struct qce_device *pce_dev;
+
+	pce_dev = (struct qce_device *) cmd_ptr->user;
+	if (result != ADM_STATUS_OK) {
+		dev_err(pce_dev->pdev, "Qualcomm ADM status error %x\n",
+						result);
+		pce_dev->chan_ce_in_status = -1;
+	} else
+		pce_dev->chan_ce_in_status = 0;
+	pce_dev->chan_ce_in_state = QCE_CHAN_STATE_IDLE;
+	_sha_complete(pce_dev);
+};
+
+static void _ablk_cipher_ce_in_call_back(struct msm_dmov_cmd *cmd_ptr,
+		unsigned int result, struct msm_dmov_errdata *err)
+{
+	struct qce_device *pce_dev;
+
+	pce_dev = (struct qce_device *) cmd_ptr->user;
+	if (result != ADM_STATUS_OK) {
+		dev_err(pce_dev->pdev, "Qualcomm ADM status error %x\n",
+						result);
+		pce_dev->chan_ce_in_status = -1;
+	} else
+		pce_dev->chan_ce_in_status = 0;
+
+	pce_dev->chan_ce_in_state = QCE_CHAN_STATE_COMP;
+	if (pce_dev->chan_ce_out_state == QCE_CHAN_STATE_COMP) {
+		pce_dev->chan_ce_in_state = QCE_CHAN_STATE_IDLE;
+		pce_dev->chan_ce_out_state = QCE_CHAN_STATE_IDLE;
+
+		/* done */
+		_ablk_cipher_complete(pce_dev);
+	}
+};
+
+static void _ablk_cipher_ce_out_call_back(struct msm_dmov_cmd *cmd_ptr,
+		unsigned int result, struct msm_dmov_errdata *err)
+{
+	struct qce_device *pce_dev;
+
+	pce_dev = (struct qce_device *) cmd_ptr->user;
+	if (result != ADM_STATUS_OK) {
+		dev_err(pce_dev->pdev, "Qualcomm ADM status error %x\n",
+						result);
+		pce_dev->chan_ce_out_status = -1;
+	} else {
+		pce_dev->chan_ce_out_status = 0;
+	};
+
+	pce_dev->chan_ce_out_state = QCE_CHAN_STATE_COMP;
+	if (pce_dev->chan_ce_in_state == QCE_CHAN_STATE_COMP) {
+		pce_dev->chan_ce_in_state = QCE_CHAN_STATE_IDLE;
+		pce_dev->chan_ce_out_state = QCE_CHAN_STATE_IDLE;
+
+		/* done */
+		_ablk_cipher_complete(pce_dev);
+	}
+};
+
+
+static void _ablk_cipher_ce_in_call_back_pmem(struct msm_dmov_cmd *cmd_ptr,
+		unsigned int result, struct msm_dmov_errdata *err)
+{
+	struct qce_device *pce_dev;
+
+	pce_dev = (struct qce_device *) cmd_ptr->user;
+	if (result != ADM_STATUS_OK) {
+		dev_err(pce_dev->pdev, "Qualcomm ADM status error %x\n",
+						result);
+		pce_dev->chan_ce_in_status = -1;
+	} else
+		pce_dev->chan_ce_in_status = 0;
+
+	pce_dev->chan_ce_in_state = QCE_CHAN_STATE_COMP;
+	if (pce_dev->chan_ce_out_state == QCE_CHAN_STATE_COMP) {
+		pce_dev->chan_ce_in_state = QCE_CHAN_STATE_IDLE;
+		pce_dev->chan_ce_out_state = QCE_CHAN_STATE_IDLE;
+
+		/* done */
+		_ablk_cipher_use_pmem_complete(pce_dev);
+	}
+};
+
+static void _ablk_cipher_ce_out_call_back_pmem(struct msm_dmov_cmd *cmd_ptr,
+		unsigned int result, struct msm_dmov_errdata *err)
+{
+	struct qce_device *pce_dev;
+
+	pce_dev = (struct qce_device *) cmd_ptr->user;
+	if (result != ADM_STATUS_OK) {
+		dev_err(pce_dev->pdev, "Qualcomm ADM status error %x\n",
+						result);
+		pce_dev->chan_ce_out_status = -1;
+	} else {
+		pce_dev->chan_ce_out_status = 0;
+	};
+
+	pce_dev->chan_ce_out_state = QCE_CHAN_STATE_COMP;
+	if (pce_dev->chan_ce_in_state == QCE_CHAN_STATE_COMP) {
+		pce_dev->chan_ce_in_state = QCE_CHAN_STATE_IDLE;
+		pce_dev->chan_ce_out_state = QCE_CHAN_STATE_IDLE;
+
+		/* done */
+		_ablk_cipher_use_pmem_complete(pce_dev);
+	}
+};
+
+static int _setup_cmd_template(struct qce_device *pce_dev)
+{
+	dmov_sg *pcmd;
+	dmov_s  *pscmd;
+	struct dmov_desc *pdesc;
+	unsigned char *vaddr;
+	int i = 0;
+
+	/* Divide up the 4K coherent memory */
+	/* 1. ce_in channel 1st command src descriptors, 128 entries */
+	vaddr = pce_dev->coh_vmem;
+	vaddr = (unsigned char *) ALIGN(((unsigned int)vaddr), 16);
+	pce_dev->ce_in_src_desc = (struct dmov_desc *) vaddr;
+	pce_dev->phy_ce_in_src_desc = pce_dev->coh_pmem +
+			 (vaddr - pce_dev->coh_vmem);
+	vaddr = vaddr + (sizeof(struct dmov_desc) * QCE_MAX_NUM_DESC);
+
+	/* 2. ce_in channel 1st command dst descriptor, 1 entry */
+	vaddr = (unsigned char *) ALIGN(((unsigned int)vaddr), 16);
+	pce_dev->ce_in_dst_desc = (struct dmov_desc *) vaddr;
+	pce_dev->phy_ce_in_dst_desc = pce_dev->coh_pmem +
+			 (vaddr - pce_dev->coh_vmem);
+	vaddr = vaddr + (sizeof(struct dmov_desc) * QCE_MAX_NUM_DESC);
+
+	/*
+	 * 3. ce_in channel command list of one scatter gather command
+	 *    and one simple command.
+	 */
+	pce_dev->cmd_list_ce_in = vaddr;
+	pce_dev->phy_cmd_list_ce_in = pce_dev->coh_pmem
+			 + (vaddr - pce_dev->coh_vmem);
+	vaddr = vaddr + sizeof(dmov_s) + sizeof(dmov_sg);
+
+	/* 4. authentication result. */
+	pce_dev->dig_result = vaddr;
+	pce_dev->phy_dig_result = pce_dev->coh_pmem +
+			(vaddr - pce_dev->coh_vmem);
+	vaddr = vaddr + SHA256_DIGESTSIZE;
+
+	/*
+	 * 5. ce_out channel command list of one scatter gather command
+	 *    and one simple command.
+	 */
+	pce_dev->cmd_list_ce_out = vaddr;
+	pce_dev->phy_cmd_list_ce_out = pce_dev->coh_pmem
+			 + (vaddr - pce_dev->coh_vmem);
+	vaddr = vaddr + sizeof(dmov_s) + sizeof(dmov_sg);
+
+	/* 6. ce_out channel command src descriptors, 1 entry */
+	vaddr = (unsigned char *) ALIGN(((unsigned int)vaddr), 16);
+	pce_dev->ce_out_src_desc = (struct dmov_desc *) vaddr;
+	pce_dev->phy_ce_out_src_desc = pce_dev->coh_pmem
+			 + (vaddr - pce_dev->coh_vmem);
+	vaddr = vaddr + (sizeof(struct dmov_desc) * QCE_MAX_NUM_DESC);
+
+	/* 7. ce_out channel command dst descriptors, 128 entries.  */
+	vaddr = (unsigned char *) ALIGN(((unsigned int)vaddr), 16);
+	pce_dev->ce_out_dst_desc = (struct dmov_desc *) vaddr;
+	pce_dev->phy_ce_out_dst_desc = pce_dev->coh_pmem
+			 + (vaddr - pce_dev->coh_vmem);
+	vaddr = vaddr + (sizeof(struct dmov_desc) * QCE_MAX_NUM_DESC);
+
+	/* 8. pad area. */
+	pce_dev->ce_pad = vaddr;
+	pce_dev->phy_ce_pad = pce_dev->coh_pmem +
+			(vaddr - pce_dev->coh_vmem);
+	vaddr = vaddr + ADM_CE_BLOCK_SIZE;
+
+	/* 9. ce_in channel command pointer list.	 */
+	vaddr = (unsigned char *) ALIGN(((unsigned int) vaddr), 16);
+	pce_dev->cmd_pointer_list_ce_in = (unsigned int *) vaddr;
+	pce_dev->phy_cmd_pointer_list_ce_in = pce_dev->coh_pmem +
+			(vaddr - pce_dev->coh_vmem);
+	vaddr = vaddr + sizeof(unsigned char *);
+
+	/* 10. ce_ou channel command pointer list. */
+	vaddr = (unsigned char *) ALIGN(((unsigned int) vaddr), 16);
+	pce_dev->cmd_pointer_list_ce_out = (unsigned int *) vaddr;
+	pce_dev->phy_cmd_pointer_list_ce_out =  pce_dev->coh_pmem +
+			(vaddr - pce_dev->coh_vmem);
+	vaddr = vaddr + sizeof(unsigned char *);
+
+	/* 11. throw away area to store by-pass data from ce_out. */
+	pce_dev->ce_out_ignore = (unsigned char *) vaddr;
+	pce_dev->phy_ce_out_ignore  = pce_dev->coh_pmem
+			+ (vaddr - pce_dev->coh_vmem);
+	pce_dev->ce_out_ignore_size = (2 * PAGE_SIZE) - (vaddr -
+			pce_dev->coh_vmem);  /* at least 1.5 K of space */
+	/*
+	 * The first command of command list ce_in is for the input of
+	 * concurrent operation of encrypt/decrypt or for the input
+	 * of authentication.
+	 */
+	pcmd = (dmov_sg *) pce_dev->cmd_list_ce_in;
+	/* swap byte and half word , dst crci ,  scatter gather */
+	pcmd->cmd = CMD_DST_SWAP_BYTES | CMD_DST_SWAP_SHORTS |
+			CMD_DST_CRCI(pce_dev->crci_in) | CMD_MODE_SG;
+	pdesc = pce_dev->ce_in_src_desc;
+	pdesc->addr = 0;	/* to be filled in each operation */
+	pdesc->len = 0;		/* to be filled in each operation */
+	pcmd->src_dscr = (unsigned) pce_dev->phy_ce_in_src_desc;
+
+	pdesc = pce_dev->ce_in_dst_desc;
+	for (i = 0; i < QCE_MAX_NUM_DESC; i++) {
+		pdesc->addr = (CRYPTO_DATA_SHADOW0 + pce_dev->phy_iobase);
+		pdesc->len = 0; /* to be filled in each operation */
+		pdesc++;
+	}
+	pcmd->dst_dscr = (unsigned) pce_dev->phy_ce_in_dst_desc;
+	pcmd->_reserved = LI_SG_CMD | SRC_INDEX_SG_CMD(0) |
+						DST_INDEX_SG_CMD(0);
+	pcmd++;
+	/*
+	 * The second command is for the digested data of
+	 * hashing operation only. For others, this command is not used.
+	 */
+	pscmd = (dmov_s *) pcmd;
+	/* last command, swap byte, half word, src crci, single   */
+	pscmd->cmd = CMD_LC | CMD_SRC_SWAP_BYTES | CMD_SRC_SWAP_SHORTS |
+			CMD_SRC_CRCI(pce_dev->crci_hash) | CMD_MODE_SINGLE;
+	pscmd->src = (unsigned) (CRYPTO_AUTH_IV0_REG + pce_dev->phy_iobase);
+	pscmd->len = SHA256_DIGESTSIZE;	/* to be filled.  */
+	pscmd->dst = (unsigned) pce_dev->phy_dig_result;
+	/* setup command pointer list */
+	*(pce_dev->cmd_pointer_list_ce_in) = (CMD_PTR_LP | DMOV_CMD_LIST |
+			DMOV_CMD_ADDR((unsigned int)
+					pce_dev->phy_cmd_list_ce_in));
+	pce_dev->chan_ce_in_cmd->user = (void *) pce_dev;
+	pce_dev->chan_ce_in_cmd->exec_func = NULL;
+	pce_dev->chan_ce_in_cmd->cmdptr = DMOV_CMD_ADDR(
+			(unsigned int) pce_dev->phy_cmd_pointer_list_ce_in);
+	/*
+	 * The first command in the command list ce_out.
+	 * It is for encry/decryp output.
+	 * If hashing only, ce_out is not used.
+	 */
+	pcmd = (dmov_sg *) pce_dev->cmd_list_ce_out;
+	/* swap byte, half word, source crci, scatter gather */
+	pcmd->cmd =   CMD_SRC_SWAP_BYTES | CMD_SRC_SWAP_SHORTS |
+			CMD_SRC_CRCI(pce_dev->crci_out) | CMD_MODE_SG;
+
+	pdesc = pce_dev->ce_out_src_desc;
+	for (i = 0; i < QCE_MAX_NUM_DESC; i++) {
+		pdesc->addr = (CRYPTO_DATA_SHADOW0 + pce_dev->phy_iobase);
+		pdesc->len = 0;  /* to be filled in each operation */
+		pdesc++;
+	}
+	pcmd->src_dscr = (unsigned) pce_dev->phy_ce_out_src_desc;
+
+	pdesc = pce_dev->ce_out_dst_desc;
+	pdesc->addr = 0;  /* to be filled in each operation */
+	pdesc->len = 0;   /* to be filled in each operation */
+	pcmd->dst_dscr = (unsigned) pce_dev->phy_ce_out_dst_desc;
+	pcmd->_reserved = LI_SG_CMD | SRC_INDEX_SG_CMD(0) |
+						DST_INDEX_SG_CMD(0);
+
+	pcmd++;
+	/*
+	 * The second command is for digested data of esp operation.
+	 * For ciphering, this command is not used.
+	 */
+	pscmd = (dmov_s *) pcmd;
+	/* last command, swap byte, half word, src crci, single   */
+	pscmd->cmd = CMD_LC | CMD_SRC_SWAP_BYTES | CMD_SRC_SWAP_SHORTS |
+			CMD_SRC_CRCI(pce_dev->crci_hash) | CMD_MODE_SINGLE;
+	pscmd->src = (CRYPTO_AUTH_IV0_REG + pce_dev->phy_iobase);
+	pscmd->len = SHA1_DIGESTSIZE;     /* we only support hmac(sha1) */
+	pscmd->dst = (unsigned) pce_dev->phy_dig_result;
+	/* setup command pointer list */
+	*(pce_dev->cmd_pointer_list_ce_out) = (CMD_PTR_LP | DMOV_CMD_LIST |
+			DMOV_CMD_ADDR((unsigned int)pce_dev->
+						phy_cmd_list_ce_out));
+
+	pce_dev->chan_ce_out_cmd->user = pce_dev;
+	pce_dev->chan_ce_out_cmd->exec_func = NULL;
+	pce_dev->chan_ce_out_cmd->cmdptr = DMOV_CMD_ADDR(
+			(unsigned int) pce_dev->phy_cmd_pointer_list_ce_out);
+
+
+	return 0;
+};
+
+static int _qce_start_dma(struct qce_device *pce_dev, bool ce_in, bool ce_out)
+{
+
+	if (ce_in)
+		pce_dev->chan_ce_in_state = QCE_CHAN_STATE_IN_PROG;
+	else
+		pce_dev->chan_ce_in_state = QCE_CHAN_STATE_COMP;
+
+	if (ce_out)
+		pce_dev->chan_ce_out_state = QCE_CHAN_STATE_IN_PROG;
+	else
+		pce_dev->chan_ce_out_state = QCE_CHAN_STATE_COMP;
+
+	if (ce_in)
+		msm_dmov_enqueue_cmd(pce_dev->chan_ce_in,
+					pce_dev->chan_ce_in_cmd);
+	if (ce_out)
+		msm_dmov_enqueue_cmd(pce_dev->chan_ce_out,
+					pce_dev->chan_ce_out_cmd);
+
+	return 0;
+};
+
+static void _f9_complete(struct qce_device *pce_dev)
+{
+	uint32_t mac_i;
+	uint32_t status;
+
+	dma_unmap_single(pce_dev->pdev, pce_dev->phy_ota_src,
+				pce_dev->ota_size, DMA_TO_DEVICE);
+
+	/* check ce error status */
+	status = readl_relaxed(pce_dev->iobase + CRYPTO_STATUS_REG);
+	if (status & (1 << CRYPTO_SW_ERR)) {
+		pce_dev->err++;
+		dev_err(pce_dev->pdev,
+			"Qualcomm Crypto Error at 0x%x, status%x\n",
+			pce_dev->phy_iobase, status);
+		_init_ce_engine(pce_dev);
+		pce_dev->qce_cb(pce_dev->areq, NULL, NULL, -ENXIO);
+		return;
+	};
+
+	mac_i = readl_relaxed(pce_dev->iobase + CRYPTO_AUTH_IV0_REG);
+	pce_dev->qce_cb(pce_dev->areq, (void *) mac_i, NULL,
+				pce_dev->chan_ce_in_status);
+};
+
+static void _f8_complete(struct qce_device *pce_dev)
+{
+	uint32_t status;
+
+	if (pce_dev->phy_ota_dst != 0)
+		dma_unmap_single(pce_dev->pdev, pce_dev->phy_ota_dst,
+				pce_dev->ota_size, DMA_FROM_DEVICE);
+	if (pce_dev->phy_ota_src != 0)
+		dma_unmap_single(pce_dev->pdev, pce_dev->phy_ota_src,
+				pce_dev->ota_size, (pce_dev->phy_ota_dst) ?
+				DMA_TO_DEVICE : DMA_BIDIRECTIONAL);
+
+	/* check ce error status */
+	status = readl_relaxed(pce_dev->iobase + CRYPTO_STATUS_REG);
+	if (status & (1 << CRYPTO_SW_ERR)) {
+		pce_dev->err++;
+		dev_err(pce_dev->pdev,
+			"Qualcomm Crypto Error at 0x%x, status%x\n",
+			pce_dev->phy_iobase, status);
+		_init_ce_engine(pce_dev);
+		pce_dev->qce_cb(pce_dev->areq, NULL, NULL, -ENXIO);
+		return;
+	};
+
+	pce_dev->qce_cb(pce_dev->areq, NULL, NULL,
+				pce_dev->chan_ce_in_status |
+					pce_dev->chan_ce_out_status);
+};
+
+
+static void _f9_ce_in_call_back(struct msm_dmov_cmd *cmd_ptr,
+		unsigned int result, struct msm_dmov_errdata *err)
+{
+	struct qce_device *pce_dev;
+
+	pce_dev = (struct qce_device *) cmd_ptr->user;
+	if (result != ADM_STATUS_OK) {
+		dev_err(pce_dev->pdev, "Qualcomm ADM status error %x\n",
+						result);
+		pce_dev->chan_ce_in_status = -1;
+	} else
+		pce_dev->chan_ce_in_status = 0;
+	pce_dev->chan_ce_in_state = QCE_CHAN_STATE_IDLE;
+	_f9_complete(pce_dev);
+};
+
+static void _f8_ce_in_call_back(struct msm_dmov_cmd *cmd_ptr,
+		unsigned int result, struct msm_dmov_errdata *err)
+{
+	struct qce_device *pce_dev;
+
+	pce_dev = (struct qce_device *) cmd_ptr->user;
+	if (result != ADM_STATUS_OK) {
+		dev_err(pce_dev->pdev, "Qualcomm ADM status error %x\n",
+						 result);
+		pce_dev->chan_ce_in_status = -1;
+	} else
+		pce_dev->chan_ce_in_status = 0;
+
+	pce_dev->chan_ce_in_state = QCE_CHAN_STATE_COMP;
+	if (pce_dev->chan_ce_out_state == QCE_CHAN_STATE_COMP) {
+		pce_dev->chan_ce_in_state = QCE_CHAN_STATE_IDLE;
+		pce_dev->chan_ce_out_state = QCE_CHAN_STATE_IDLE;
+
+		/* done */
+		_f8_complete(pce_dev);
+	}
+};
+
+static void _f8_ce_out_call_back(struct msm_dmov_cmd *cmd_ptr,
+		unsigned int result, struct msm_dmov_errdata *err)
+{
+	struct qce_device *pce_dev;
+
+	pce_dev = (struct qce_device *) cmd_ptr->user;
+	if (result != ADM_STATUS_OK) {
+		dev_err(pce_dev->pdev, "Qualcomm ADM status error %x\n",
+						result);
+		pce_dev->chan_ce_out_status = -1;
+	} else {
+		pce_dev->chan_ce_out_status = 0;
+	};
+
+	pce_dev->chan_ce_out_state = QCE_CHAN_STATE_COMP;
+	if (pce_dev->chan_ce_in_state == QCE_CHAN_STATE_COMP) {
+		pce_dev->chan_ce_in_state = QCE_CHAN_STATE_IDLE;
+		pce_dev->chan_ce_out_state = QCE_CHAN_STATE_IDLE;
+
+		/* done */
+		_f8_complete(pce_dev);
+	}
+};
+
+static int _ce_f9_setup(struct qce_device *pce_dev, struct qce_f9_req * req)
+{
+	uint32_t cfg;
+	uint32_t ikey[OTA_KEY_SIZE/sizeof(uint32_t)];
+
+	_byte_stream_to_net_words(ikey, &req->ikey[0], OTA_KEY_SIZE);
+	writel_relaxed(ikey[0], pce_dev->iobase + CRYPTO_AUTH_IV0_REG);
+	writel_relaxed(ikey[1], pce_dev->iobase + CRYPTO_AUTH_IV1_REG);
+	writel_relaxed(ikey[2], pce_dev->iobase + CRYPTO_AUTH_IV2_REG);
+	writel_relaxed(ikey[3], pce_dev->iobase + CRYPTO_AUTH_IV3_REG);
+	writel_relaxed(req->last_bits, pce_dev->iobase + CRYPTO_AUTH_IV4_REG);
+
+	writel_relaxed(req->fresh, pce_dev->iobase + CRYPTO_AUTH_BYTECNT0_REG);
+	writel_relaxed(req->count_i, pce_dev->iobase +
+						CRYPTO_AUTH_BYTECNT1_REG);
+
+	/* write auth_seg_cfg */
+	writel_relaxed((uint32_t)req->msize << CRYPTO_AUTH_SEG_SIZE,
+			pce_dev->iobase + CRYPTO_AUTH_SEG_CFG_REG);
+
+	/* write seg_cfg */
+	cfg = (CRYPTO_AUTH_ALG_F9 << CRYPTO_AUTH_ALG) | (1 << CRYPTO_FIRST) |
+			(1 << CRYPTO_LAST);
+
+	if (req->algorithm == QCE_OTA_ALGO_KASUMI)
+		cfg |= (CRYPTO_AUTH_SIZE_UIA1 << CRYPTO_AUTH_SIZE);
+	else
+		cfg |= (CRYPTO_AUTH_SIZE_UIA2 << CRYPTO_AUTH_SIZE) ;
+
+	if (req->direction == QCE_OTA_DIR_DOWNLINK)
+		cfg |= 1 << CRYPTO_F9_DIRECTION;
+
+	writel_relaxed(cfg, pce_dev->iobase + CRYPTO_SEG_CFG_REG);
+
+	/* write seg_size   */
+	writel_relaxed(req->msize, pce_dev->iobase + CRYPTO_SEG_SIZE_REG);
+
+	/* issue go to crypto   */
+	writel_relaxed(1 << CRYPTO_GO, pce_dev->iobase + CRYPTO_GOPROC_REG);
+
+	/*
+	 * barrier to ensure previous instructions
+	 * (including GO) to CE finish before issue DMA transfer
+	 * request.
+	 */
+	mb();
+	return 0;
+};
+
+static int _ce_f8_setup(struct qce_device *pce_dev, struct qce_f8_req *req,
+		bool key_stream_mode, uint16_t npkts, uint16_t cipher_offset,
+		uint16_t cipher_size)
+{
+	uint32_t cfg;
+	uint32_t ckey[OTA_KEY_SIZE/sizeof(uint32_t)];
+
+	if ((key_stream_mode && (req->data_len & 0xf || npkts > 1)) ||
+				(req->bearer >= QCE_OTA_MAX_BEARER))
+		return -EINVAL;
+
+	/*  write seg_cfg */
+	cfg = (CRYPTO_ENCR_ALG_F8 << CRYPTO_ENCR_ALG) | (1 << CRYPTO_FIRST) |
+				(1 << CRYPTO_LAST);
+	if (req->algorithm == QCE_OTA_ALGO_KASUMI)
+		cfg |= (CRYPTO_ENCR_KEY_SZ_UEA1 << CRYPTO_ENCR_KEY_SZ);
+	else
+		cfg |= (CRYPTO_ENCR_KEY_SZ_UEA2 << CRYPTO_ENCR_KEY_SZ) ;
+	if (key_stream_mode)
+		cfg |= 1 << CRYPTO_F8_KEYSTREAM_ENABLE;
+	if (req->direction == QCE_OTA_DIR_DOWNLINK)
+		cfg |= 1 << CRYPTO_F8_DIRECTION;
+	writel_relaxed(cfg, pce_dev->iobase + CRYPTO_SEG_CFG_REG);
+
+	/* write seg_size   */
+	writel_relaxed(req->data_len, pce_dev->iobase + CRYPTO_SEG_SIZE_REG);
+
+	/* write 0 to auth_size, auth_offset */
+	writel_relaxed(0, pce_dev->iobase + CRYPTO_AUTH_SEG_CFG_REG);
+
+	/* write encr_seg_cfg seg_size, seg_offset */
+	writel_relaxed((((uint32_t) cipher_size) << CRYPTO_ENCR_SEG_SIZE) |
+			(cipher_offset & 0xffff),
+				pce_dev->iobase + CRYPTO_ENCR_SEG_CFG_REG);
+
+	/* write keys */
+	_byte_stream_to_net_words(ckey, &req->ckey[0], OTA_KEY_SIZE);
+	writel_relaxed(ckey[0], pce_dev->iobase + CRYPTO_DES_KEY0_REG);
+	writel_relaxed(ckey[1], pce_dev->iobase + CRYPTO_DES_KEY1_REG);
+	writel_relaxed(ckey[2], pce_dev->iobase + CRYPTO_DES_KEY2_REG);
+	writel_relaxed(ckey[3], pce_dev->iobase + CRYPTO_DES_KEY3_REG);
+
+	/* write cntr0_iv0 for countC */
+	writel_relaxed(req->count_c, pce_dev->iobase + CRYPTO_CNTR0_IV0_REG);
+
+	/* write cntr1_iv1 for nPkts, and bearer */
+	if (npkts == 1)
+		npkts = 0;
+	writel_relaxed(req->bearer << CRYPTO_CNTR1_IV1_REG_F8_BEARER |
+			npkts << CRYPTO_CNTR1_IV1_REG_F8_PKT_CNT,
+				pce_dev->iobase + CRYPTO_CNTR1_IV1_REG);
+
+	/* issue go to crypto   */
+	writel_relaxed(1 << CRYPTO_GO, pce_dev->iobase + CRYPTO_GOPROC_REG);
+
+	/*
+	 * barrier to ensure previous instructions
+	 * (including GO) to CE finish before issue DMA transfer
+	 * request.
+	 */
+	mb();
+	return 0;
+};
+
+int qce_aead_req(void *handle, struct qce_req *q_req)
+{
+	struct qce_device *pce_dev = (struct qce_device *) handle;
+	struct aead_request *areq = (struct aead_request *) q_req->areq;
+	struct crypto_aead *aead = crypto_aead_reqtfm(areq);
+	uint32_t ivsize = crypto_aead_ivsize(aead);
+	uint32_t totallen;
+	uint32_t pad_len;
+	uint32_t authsize = crypto_aead_authsize(aead);
+	int rc = 0;
+
+	q_req->ivsize = ivsize;
+	if (q_req->dir == QCE_ENCRYPT)
+		q_req->cryptlen = areq->cryptlen;
+	else
+		q_req->cryptlen = areq->cryptlen - authsize;
+
+	totallen = q_req->cryptlen + ivsize + areq->assoclen;
+	pad_len = ALIGN(totallen, ADM_CE_BLOCK_SIZE) - totallen;
+
+	_chain_buffer_in_init(pce_dev);
+	_chain_buffer_out_init(pce_dev);
+
+	pce_dev->assoc_nents = 0;
+	pce_dev->phy_iv_in = 0;
+	pce_dev->src_nents = 0;
+	pce_dev->dst_nents = 0;
+
+	pce_dev->assoc_nents = count_sg(areq->assoc, areq->assoclen);
+	dma_map_sg(pce_dev->pdev, areq->assoc, pce_dev->assoc_nents,
+					 DMA_TO_DEVICE);
+	if (_chain_sg_buffer_in(pce_dev, areq->assoc, areq->assoclen) < 0) {
+		rc = -ENOMEM;
+		goto bad;
+	}
+
+	/* cipher iv for input                                 */
+	pce_dev->phy_iv_in = dma_map_single(pce_dev->pdev, q_req->iv,
+			ivsize, DMA_TO_DEVICE);
+	if (_chain_pm_buffer_in(pce_dev, pce_dev->phy_iv_in, ivsize) < 0) {
+		rc = -ENOMEM;
+		goto bad;
+	}
+
+	/* for output, ignore associated data and cipher iv */
+	if (_chain_pm_buffer_out(pce_dev, pce_dev->phy_ce_out_ignore,
+						ivsize + areq->assoclen) < 0) {
+		rc = -ENOMEM;
+		goto bad;
+	}
+
+	/* cipher input       */
+	pce_dev->src_nents = count_sg(areq->src, q_req->cryptlen);
+	dma_map_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
+			(areq->src == areq->dst) ? DMA_BIDIRECTIONAL :
+							DMA_TO_DEVICE);
+	if (_chain_sg_buffer_in(pce_dev, areq->src, q_req->cryptlen) < 0) {
+		rc = -ENOMEM;
+		goto bad;
+	}
+
+	/* cipher output      */
+	if (areq->src != areq->dst) {
+		pce_dev->dst_nents = count_sg(areq->dst, q_req->cryptlen);
+		dma_map_sg(pce_dev->pdev, areq->dst, pce_dev->dst_nents,
+				DMA_FROM_DEVICE);
+	};
+	if (_chain_sg_buffer_out(pce_dev, areq->dst, q_req->cryptlen) < 0) {
+		rc = -ENOMEM;
+		goto bad;
+	}
+
+	/* pad data      */
+	if (pad_len) {
+		if (_chain_pm_buffer_in(pce_dev, pce_dev->phy_ce_pad,
+						pad_len) < 0) {
+			rc = -ENOMEM;
+			goto bad;
+		}
+		if (_chain_pm_buffer_out(pce_dev, pce_dev->phy_ce_pad,
+						pad_len) < 0) {
+			rc = -ENOMEM;
+			goto bad;
+		}
+	}
+
+	/* finalize the ce_in and ce_out channels command lists */
+	_ce_in_final(pce_dev, 1, ALIGN(totallen, ADM_CE_BLOCK_SIZE));
+	_ce_out_final(pce_dev, 2, ALIGN(totallen, ADM_CE_BLOCK_SIZE));
+
+	/* set up crypto device */
+	rc = _ce_setup(pce_dev, q_req, totallen, ivsize + areq->assoclen);
+	if (rc < 0)
+		goto bad;
+
+	/* setup for callback, and issue command to adm */
+	pce_dev->areq = q_req->areq;
+	pce_dev->qce_cb = q_req->qce_cb;
+
+	pce_dev->chan_ce_in_cmd->complete_func = _aead_ce_in_call_back;
+	pce_dev->chan_ce_out_cmd->complete_func = _aead_ce_out_call_back;
+
+	rc = _qce_start_dma(pce_dev, true, true);
+	if (rc == 0)
+		return 0;
+bad:
+	if (pce_dev->assoc_nents) {
+		dma_unmap_sg(pce_dev->pdev, areq->assoc, pce_dev->assoc_nents,
+				DMA_TO_DEVICE);
+	}
+	if (pce_dev->phy_iv_in) {
+		dma_unmap_single(pce_dev->pdev, pce_dev->phy_iv_in,
+				ivsize, DMA_TO_DEVICE);
+	}
+	if (pce_dev->src_nents) {
+		dma_unmap_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
+				(areq->src == areq->dst) ? DMA_BIDIRECTIONAL :
+								DMA_TO_DEVICE);
+	}
+	if (pce_dev->dst_nents) {
+		dma_unmap_sg(pce_dev->pdev, areq->dst, pce_dev->dst_nents,
+				DMA_FROM_DEVICE);
+	}
+	return rc;
+}
+EXPORT_SYMBOL(qce_aead_req);
+
+int qce_ablk_cipher_req(void *handle, struct qce_req *c_req)
+{
+	int rc = 0;
+	struct qce_device *pce_dev = (struct qce_device *) handle;
+	struct ablkcipher_request *areq = (struct ablkcipher_request *)
+						c_req->areq;
+
+	uint32_t pad_len = ALIGN(areq->nbytes, ADM_CE_BLOCK_SIZE)
+						- areq->nbytes;
+
+	_chain_buffer_in_init(pce_dev);
+	_chain_buffer_out_init(pce_dev);
+
+	pce_dev->src_nents = 0;
+	pce_dev->dst_nents = 0;
+	/* cipher input       */
+	pce_dev->src_nents = count_sg(areq->src, areq->nbytes);
+
+	if (c_req->use_pmem != 1)
+		dma_map_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
+			(areq->src == areq->dst) ? DMA_BIDIRECTIONAL :
+								DMA_TO_DEVICE);
+	else
+		dma_map_pmem_sg(&c_req->pmem->src[0], pce_dev->src_nents,
+								areq->src);
+
+	if (_chain_sg_buffer_in(pce_dev, areq->src, areq->nbytes) < 0) {
+		rc = -ENOMEM;
+		goto bad;
+	}
+
+	/* cipher output      */
+	if (areq->src != areq->dst) {
+		pce_dev->dst_nents = count_sg(areq->dst, areq->nbytes);
+		if (c_req->use_pmem != 1)
+			dma_map_sg(pce_dev->pdev, areq->dst, pce_dev->dst_nents,
+							DMA_FROM_DEVICE);
+		else
+			dma_map_pmem_sg(&c_req->pmem->dst[0],
+					pce_dev->dst_nents, areq->dst);
+	};
+	if (_chain_sg_buffer_out(pce_dev, areq->dst, areq->nbytes) < 0) {
+		rc = -ENOMEM;
+		goto bad;
+	}
+
+	/* pad data      */
+	if (pad_len) {
+		if (_chain_pm_buffer_in(pce_dev, pce_dev->phy_ce_pad,
+						pad_len) < 0) {
+			rc = -ENOMEM;
+			goto bad;
+		}
+		if (_chain_pm_buffer_out(pce_dev, pce_dev->phy_ce_pad,
+						pad_len) < 0) {
+			rc = -ENOMEM;
+			goto bad;
+		}
+	}
+
+	/* finalize the ce_in and ce_out channels command lists */
+	_ce_in_final(pce_dev, 1, areq->nbytes + pad_len);
+	_ce_out_final(pce_dev, 1, areq->nbytes + pad_len);
+
+#ifdef QCE_DEBUG
+	_ce_in_dump(pce_dev);
+	_ce_out_dump(pce_dev);
+#endif
+	/* set up crypto device */
+	rc = _ce_setup(pce_dev, c_req, areq->nbytes, 0);
+	if (rc < 0)
+		goto bad;
+
+	/* setup for callback, and issue command to adm */
+	pce_dev->areq = areq;
+	pce_dev->qce_cb = c_req->qce_cb;
+	if (c_req->use_pmem == 1) {
+		pce_dev->chan_ce_in_cmd->complete_func =
+					_ablk_cipher_ce_in_call_back_pmem;
+		pce_dev->chan_ce_out_cmd->complete_func =
+					_ablk_cipher_ce_out_call_back_pmem;
+	} else {
+		pce_dev->chan_ce_in_cmd->complete_func =
+					_ablk_cipher_ce_in_call_back;
+		pce_dev->chan_ce_out_cmd->complete_func =
+					_ablk_cipher_ce_out_call_back;
+	}
+	rc = _qce_start_dma(pce_dev, true, true);
+
+	if (rc == 0)
+		return 0;
+bad:
+	if (c_req->use_pmem != 1) {
+		if (pce_dev->dst_nents) {
+			dma_unmap_sg(pce_dev->pdev, areq->dst,
+				pce_dev->dst_nents, DMA_FROM_DEVICE);
+		}
+		if (pce_dev->src_nents) {
+			dma_unmap_sg(pce_dev->pdev, areq->src,
+					pce_dev->src_nents,
+					(areq->src == areq->dst) ?
+						DMA_BIDIRECTIONAL :
+						DMA_TO_DEVICE);
+		}
+	}
+	return rc;
+}
+EXPORT_SYMBOL(qce_ablk_cipher_req);
+
+int qce_process_sha_req(void *handle, struct qce_sha_req *sreq)
+{
+	struct qce_device *pce_dev = (struct qce_device *) handle;
+	int rc;
+	uint32_t pad_len = ALIGN(sreq->size, ADM_CE_BLOCK_SIZE) - sreq->size;
+	struct ahash_request *areq = (struct ahash_request *)sreq->areq;
+
+	_chain_buffer_in_init(pce_dev);
+	pce_dev->src_nents = count_sg(sreq->src, sreq->size);
+	dma_map_sg(pce_dev->pdev, sreq->src, pce_dev->src_nents,
+							DMA_TO_DEVICE);
+
+	if (_chain_sg_buffer_in(pce_dev, sreq->src, sreq->size) < 0) {
+		rc = -ENOMEM;
+		goto bad;
+	}
+
+	if (pad_len) {
+		if (_chain_pm_buffer_in(pce_dev, pce_dev->phy_ce_pad,
+						pad_len) < 0) {
+				rc = -ENOMEM;
+				goto bad;
+			}
+	}
+	 _ce_in_final(pce_dev, 2, sreq->size + pad_len);
+
+#ifdef QCE_DEBUG
+	_ce_in_dump(pce_dev);
+#endif
+
+	rc =  _sha_ce_setup(pce_dev, sreq);
+
+	if (rc < 0)
+		goto bad;
+
+	pce_dev->areq = areq;
+	pce_dev->qce_cb = sreq->qce_cb;
+	pce_dev->chan_ce_in_cmd->complete_func = _sha_ce_in_call_back;
+
+	rc =  _qce_start_dma(pce_dev, true, false);
+
+	if (rc == 0)
+		return 0;
+bad:
+	if (pce_dev->src_nents) {
+		dma_unmap_sg(pce_dev->pdev, sreq->src,
+				pce_dev->src_nents, DMA_TO_DEVICE);
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(qce_process_sha_req);
+
+/*
+ * crypto engine open function.
+ */
+void *qce_open(struct platform_device *pdev, int *rc)
+{
+	struct qce_device *pce_dev;
+	struct resource *resource;
+	struct clk *ce_clk;
+
+	pce_dev = kzalloc(sizeof(struct qce_device), GFP_KERNEL);
+	if (!pce_dev) {
+		*rc = -ENOMEM;
+		dev_err(&pdev->dev, "Can not allocate memory\n");
+		return NULL;
+	}
+	pce_dev->pdev = &pdev->dev;
+	ce_clk = clk_get(pce_dev->pdev, "core_clk");
+	if (IS_ERR(ce_clk)) {
+		kfree(pce_dev);
+		*rc = PTR_ERR(ce_clk);
+		return NULL;
+	}
+	pce_dev->ce_clk = ce_clk;
+	*rc = clk_enable(pce_dev->ce_clk);
+	if (*rc) {
+		kfree(pce_dev);
+		return NULL;
+	}
+
+	resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!resource) {
+		*rc = -ENXIO;
+		dev_err(pce_dev->pdev, "Missing MEM resource\n");
+		goto err;
+	};
+	pce_dev->phy_iobase = resource->start;
+	pce_dev->iobase = ioremap_nocache(resource->start,
+				resource->end - resource->start + 1);
+	if (!pce_dev->iobase) {
+		*rc = -ENOMEM;
+		dev_err(pce_dev->pdev, "Can not map io memory\n");
+		goto err;
+	}
+
+	pce_dev->chan_ce_in_cmd = kzalloc(sizeof(struct msm_dmov_cmd),
+			GFP_KERNEL);
+	pce_dev->chan_ce_out_cmd = kzalloc(sizeof(struct msm_dmov_cmd),
+			GFP_KERNEL);
+	if (pce_dev->chan_ce_in_cmd == NULL ||
+			pce_dev->chan_ce_out_cmd == NULL) {
+		dev_err(pce_dev->pdev, "Can not allocate memory\n");
+		*rc = -ENOMEM;
+		goto err;
+	}
+
+	resource = platform_get_resource_byname(pdev, IORESOURCE_DMA,
+					"crypto_channels");
+	if (!resource) {
+		*rc = -ENXIO;
+		dev_err(pce_dev->pdev, "Missing DMA channel resource\n");
+		goto err;
+	};
+	pce_dev->chan_ce_in = resource->start;
+	pce_dev->chan_ce_out = resource->end;
+	resource = platform_get_resource_byname(pdev, IORESOURCE_DMA,
+				"crypto_crci_in");
+	if (!resource) {
+		*rc = -ENXIO;
+		dev_err(pce_dev->pdev, "Missing DMA crci in resource\n");
+		goto err;
+	};
+	pce_dev->crci_in = resource->start;
+	resource = platform_get_resource_byname(pdev, IORESOURCE_DMA,
+				"crypto_crci_out");
+	if (!resource) {
+		*rc = -ENXIO;
+		dev_err(pce_dev->pdev, "Missing DMA crci out resource\n");
+		goto err;
+	};
+	pce_dev->crci_out = resource->start;
+	resource = platform_get_resource_byname(pdev, IORESOURCE_DMA,
+				"crypto_crci_hash");
+	if (!resource) {
+		*rc = -ENXIO;
+		dev_err(pce_dev->pdev, "Missing DMA crci hash resource\n");
+		goto err;
+	};
+	pce_dev->crci_hash = resource->start;
+	pce_dev->coh_vmem = dma_alloc_coherent(pce_dev->pdev,
+			2*PAGE_SIZE, &pce_dev->coh_pmem, GFP_KERNEL);
+
+	if (pce_dev->coh_vmem == NULL) {
+		*rc = -ENOMEM;
+		dev_err(pce_dev->pdev, "Can not allocate coherent memory.\n");
+		goto err;
+	}
+	_setup_cmd_template(pce_dev);
+
+	pce_dev->chan_ce_in_state = QCE_CHAN_STATE_IDLE;
+	pce_dev->chan_ce_out_state = QCE_CHAN_STATE_IDLE;
+
+	if (_init_ce_engine(pce_dev)) {
+		*rc = -ENXIO;
+		clk_disable(pce_dev->ce_clk);
+		goto err;
+	}
+	*rc = 0;
+	clk_disable(pce_dev->ce_clk);
+
+	pce_dev->err = 0;
+
+	return pce_dev;
+err:
+	if (pce_dev)
+		qce_close(pce_dev);
+	return NULL;
+}
+EXPORT_SYMBOL(qce_open);
+
+/*
+ * crypto engine close function.
+ */
+int qce_close(void *handle)
+{
+	struct qce_device *pce_dev = (struct qce_device *) handle;
+
+	if (handle == NULL)
+		return -ENODEV;
+	if (pce_dev->iobase)
+		iounmap(pce_dev->iobase);
+
+	if (pce_dev->coh_vmem)
+		dma_free_coherent(pce_dev->pdev, 2*PAGE_SIZE, pce_dev->coh_vmem,
+				pce_dev->coh_pmem);
+	kfree(pce_dev->chan_ce_in_cmd);
+	kfree(pce_dev->chan_ce_out_cmd);
+
+	clk_put(pce_dev->ce_clk);
+	kfree(handle);
+	return 0;
+}
+EXPORT_SYMBOL(qce_close);
+
+int qce_hw_support(void *handle, struct ce_hw_support *ce_support)
+{
+	struct qce_device *pce_dev = (struct qce_device *) handle;
+
+	if (ce_support == NULL)
+		return -EINVAL;
+
+	if (pce_dev->hmac == 1)
+		ce_support->sha1_hmac_20 = true;
+	else
+		ce_support->sha1_hmac_20 = false;
+	ce_support->sha1_hmac = false;
+	ce_support->sha256_hmac = false;
+	ce_support->sha_hmac = false;
+	ce_support->cmac  = false;
+	ce_support->aes_key_192 = true;
+	ce_support->aes_xts  = false;
+	ce_support->aes_ccm  = false;
+	ce_support->ota = pce_dev->ota;
+	return 0;
+}
+EXPORT_SYMBOL(qce_hw_support);
+
+int qce_f8_req(void *handle, struct qce_f8_req *req,
+			void *cookie, qce_comp_func_ptr_t qce_cb)
+{
+	struct qce_device *pce_dev = (struct qce_device *) handle;
+	bool key_stream_mode;
+	dma_addr_t dst;
+	int rc;
+	uint32_t pad_len = ALIGN(req->data_len, ADM_CE_BLOCK_SIZE) -
+						req->data_len;
+
+	_chain_buffer_in_init(pce_dev);
+	_chain_buffer_out_init(pce_dev);
+
+	key_stream_mode = (req->data_in == NULL);
+
+	/* F8 cipher input       */
+	if (key_stream_mode)
+		pce_dev->phy_ota_src = 0;
+	else {
+		pce_dev->phy_ota_src = dma_map_single(pce_dev->pdev,
+					req->data_in, req->data_len,
+					(req->data_in == req->data_out) ?
+					DMA_BIDIRECTIONAL : DMA_TO_DEVICE);
+		if (_chain_pm_buffer_in(pce_dev, pce_dev->phy_ota_src,
+				req->data_len) < 0) {
+			pce_dev->phy_ota_dst = 0;
+			rc =  -ENOMEM;
+			goto bad;
+		}
+	}
+
+	/* F8 cipher output     */
+	if (req->data_in != req->data_out) {
+		dst = dma_map_single(pce_dev->pdev, req->data_out,
+				req->data_len, DMA_FROM_DEVICE);
+		pce_dev->phy_ota_dst = dst;
+	} else {
+		dst = pce_dev->phy_ota_src;
+		pce_dev->phy_ota_dst = 0;
+	}
+	if (_chain_pm_buffer_out(pce_dev, dst, req->data_len) < 0) {
+		rc = -ENOMEM;
+		goto bad;
+	}
+
+	pce_dev->ota_size = req->data_len;
+
+	/* pad data      */
+	if (pad_len) {
+		if (!key_stream_mode && _chain_pm_buffer_in(pce_dev,
+					pce_dev->phy_ce_pad, pad_len) < 0) {
+			rc =  -ENOMEM;
+			goto bad;
+		}
+		if (_chain_pm_buffer_out(pce_dev, pce_dev->phy_ce_pad,
+						pad_len) < 0) {
+			rc =  -ENOMEM;
+			goto bad;
+		}
+	}
+
+	/* finalize the ce_in and ce_out channels command lists */
+	if (!key_stream_mode)
+		_ce_in_final(pce_dev, 1, req->data_len + pad_len);
+	_ce_out_final(pce_dev, 1, req->data_len + pad_len);
+
+	/* set up crypto device */
+	rc = _ce_f8_setup(pce_dev, req, key_stream_mode, 1, 0, req->data_len);
+	if (rc < 0)
+		goto bad;
+
+	/* setup for callback, and issue command to adm */
+	pce_dev->areq = cookie;
+	pce_dev->qce_cb = qce_cb;
+
+	if (!key_stream_mode)
+		pce_dev->chan_ce_in_cmd->complete_func = _f8_ce_in_call_back;
+
+	pce_dev->chan_ce_out_cmd->complete_func = _f8_ce_out_call_back;
+
+	rc =  _qce_start_dma(pce_dev, !(key_stream_mode), true);
+	if (rc == 0)
+		return 0;
+bad:
+	if (pce_dev->phy_ota_dst != 0)
+		dma_unmap_single(pce_dev->pdev, pce_dev->phy_ota_dst,
+				req->data_len, DMA_FROM_DEVICE);
+	if (pce_dev->phy_ota_src != 0)
+		dma_unmap_single(pce_dev->pdev, pce_dev->phy_ota_src,
+				req->data_len,
+				(req->data_in == req->data_out) ?
+					DMA_BIDIRECTIONAL : DMA_TO_DEVICE);
+	return rc;
+}
+EXPORT_SYMBOL(qce_f8_req);
+
+int qce_f8_multi_pkt_req(void *handle, struct qce_f8_multi_pkt_req *mreq,
+			void *cookie, qce_comp_func_ptr_t qce_cb)
+{
+	struct qce_device *pce_dev = (struct qce_device *) handle;
+	uint16_t num_pkt = mreq->num_pkt;
+	uint16_t cipher_start = mreq->cipher_start;
+	uint16_t cipher_size = mreq->cipher_size;
+	struct qce_f8_req *req = &mreq->qce_f8_req;
+	uint32_t total;
+	uint32_t pad_len;
+	dma_addr_t dst = 0;
+	int rc = 0;
+
+	total = num_pkt *  req->data_len;
+	pad_len = ALIGN(total, ADM_CE_BLOCK_SIZE) - total;
+
+	_chain_buffer_in_init(pce_dev);
+	_chain_buffer_out_init(pce_dev);
+
+	/* F8 cipher input       */
+	pce_dev->phy_ota_src = dma_map_single(pce_dev->pdev,
+				req->data_in, total,
+				(req->data_in == req->data_out) ?
+				DMA_BIDIRECTIONAL : DMA_TO_DEVICE);
+	if (_chain_pm_buffer_in(pce_dev, pce_dev->phy_ota_src,
+				total) < 0) {
+		pce_dev->phy_ota_dst = 0;
+		rc = -ENOMEM;
+		goto bad;
+	}
+	/* F8 cipher output      */
+	if (req->data_in != req->data_out) {
+		dst = dma_map_single(pce_dev->pdev, req->data_out, total,
+						DMA_FROM_DEVICE);
+		pce_dev->phy_ota_dst = dst;
+	} else {
+		dst = pce_dev->phy_ota_src;
+		pce_dev->phy_ota_dst = 0;
+	}
+	if (_chain_pm_buffer_out(pce_dev, dst, total) < 0) {
+		rc = -ENOMEM;
+		goto  bad;
+	}
+
+	pce_dev->ota_size = total;
+
+	/* pad data      */
+	if (pad_len) {
+		if (_chain_pm_buffer_in(pce_dev, pce_dev->phy_ce_pad,
+					pad_len) < 0) {
+			rc = -ENOMEM;
+			goto  bad;
+		}
+		if (_chain_pm_buffer_out(pce_dev, pce_dev->phy_ce_pad,
+						pad_len) < 0) {
+			rc = -ENOMEM;
+			goto  bad;
+		}
+	}
+
+	/* finalize the ce_in and ce_out channels command lists */
+	_ce_in_final(pce_dev, 1, total + pad_len);
+	_ce_out_final(pce_dev, 1, total + pad_len);
+
+
+	/* set up crypto device */
+	rc = _ce_f8_setup(pce_dev, req, false, num_pkt, cipher_start,
+			cipher_size);
+	if (rc)
+		goto bad ;
+
+	/* setup for callback, and issue command to adm */
+	pce_dev->areq = cookie;
+	pce_dev->qce_cb = qce_cb;
+
+	pce_dev->chan_ce_in_cmd->complete_func = _f8_ce_in_call_back;
+	pce_dev->chan_ce_out_cmd->complete_func = _f8_ce_out_call_back;
+
+	rc = _qce_start_dma(pce_dev, true, true);
+	if (rc == 0)
+		return 0;
+bad:
+	if (pce_dev->phy_ota_dst)
+		dma_unmap_single(pce_dev->pdev, pce_dev->phy_ota_dst, total,
+				DMA_FROM_DEVICE);
+	dma_unmap_single(pce_dev->pdev, pce_dev->phy_ota_src, total,
+				(req->data_in == req->data_out) ?
+				DMA_BIDIRECTIONAL : DMA_TO_DEVICE);
+	return rc;
+}
+EXPORT_SYMBOL(qce_f8_multi_pkt_req);
+
+int qce_f9_req(void *handle, struct qce_f9_req *req, void *cookie,
+			qce_comp_func_ptr_t qce_cb)
+{
+	struct qce_device *pce_dev = (struct qce_device *) handle;
+	int rc;
+	uint32_t pad_len = ALIGN(req->msize, ADM_CE_BLOCK_SIZE) - req->msize;
+
+	pce_dev->phy_ota_src = dma_map_single(pce_dev->pdev, req->message,
+			req->msize, DMA_TO_DEVICE);
+
+	_chain_buffer_in_init(pce_dev);
+	rc = _chain_pm_buffer_in(pce_dev, pce_dev->phy_ota_src, req->msize);
+	if (rc < 0) {
+		rc =  -ENOMEM;
+		goto bad;
+	}
+
+	pce_dev->ota_size = req->msize;
+	if (pad_len) {
+		rc = _chain_pm_buffer_in(pce_dev, pce_dev->phy_ce_pad,
+				pad_len);
+		if (rc < 0) {
+			rc = -ENOMEM;
+			goto bad;
+		}
+	}
+	_ce_in_final(pce_dev, 2, req->msize + pad_len);
+	rc = _ce_f9_setup(pce_dev, req);
+	if (rc < 0)
+		goto bad;
+
+	/* setup for callback, and issue command to adm */
+	pce_dev->areq = cookie;
+	pce_dev->qce_cb = qce_cb;
+
+	pce_dev->chan_ce_in_cmd->complete_func = _f9_ce_in_call_back;
+
+	rc =  _qce_start_dma(pce_dev, true, false);
+	if (rc == 0)
+		return 0;
+bad:
+	dma_unmap_single(pce_dev->pdev, pce_dev->phy_ota_src,
+				req->msize, DMA_TO_DEVICE);
+	return rc;
+}
+EXPORT_SYMBOL(qce_f9_req);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Mona Hossain <mhossain@codeaurora.org>");
+MODULE_DESCRIPTION("Crypto Engine driver");
+MODULE_VERSION("1.15");
+
diff --git a/drivers/crypto/msm/qce.h b/drivers/crypto/msm/qce.h
new file mode 100644
index 0000000..edd2089
--- /dev/null
+++ b/drivers/crypto/msm/qce.h
@@ -0,0 +1,160 @@
+/* Qualcomm Crypto Engine driver API
+ *
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+#ifndef __CRYPTO_MSM_QCE_H
+#define __CRYPTO_MSM_QCE_H
+
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/crypto.h>
+
+#include <crypto/algapi.h>
+#include <crypto/aes.h>
+#include <crypto/des.h>
+#include <crypto/sha.h>
+#include <crypto/aead.h>
+#include <crypto/authenc.h>
+#include <crypto/scatterwalk.h>
+
+/* SHA digest size  in bytes */
+#define SHA256_DIGESTSIZE		32
+#define SHA1_DIGESTSIZE			20
+
+/* key size in bytes */
+#define HMAC_KEY_SIZE			(SHA1_DIGESTSIZE)    /* hmac-sha1 */
+#define SHA_HMAC_KEY_SIZE		64
+#define DES_KEY_SIZE			8
+#define TRIPLE_DES_KEY_SIZE		24
+#define AES128_KEY_SIZE			16
+#define AES192_KEY_SIZE			24
+#define AES256_KEY_SIZE			32
+#define MAX_CIPHER_KEY_SIZE		AES256_KEY_SIZE
+
+/* iv length in bytes */
+#define AES_IV_LENGTH			16
+#define DES_IV_LENGTH                   8
+#define MAX_IV_LENGTH			AES_IV_LENGTH
+
+/* Maximum number of bytes per transfer */
+#define QCE_MAX_OPER_DATA		0xFF00
+
+/* Maximum Nonce bytes  */
+#define MAX_NONCE  16
+
+typedef void (*qce_comp_func_ptr_t)(void *areq,
+		unsigned char *icv, unsigned char *iv, int ret);
+
+/* Cipher algorithms supported */
+enum qce_cipher_alg_enum {
+	CIPHER_ALG_DES = 0,
+	CIPHER_ALG_3DES = 1,
+	CIPHER_ALG_AES = 2,
+	CIPHER_ALG_LAST
+};
+
+/* Hash and hmac algorithms supported */
+enum qce_hash_alg_enum {
+	QCE_HASH_SHA1   = 0,
+	QCE_HASH_SHA256 = 1,
+	QCE_HASH_SHA1_HMAC   = 2,
+	QCE_HASH_SHA256_HMAC = 3,
+	QCE_HASH_AES_CMAC = 4,
+	QCE_HASH_LAST
+};
+
+/* Cipher encryption/decryption operations */
+enum qce_cipher_dir_enum {
+	QCE_ENCRYPT = 0,
+	QCE_DECRYPT = 1,
+	QCE_CIPHER_DIR_LAST
+};
+
+/* Cipher algorithms modes */
+enum qce_cipher_mode_enum {
+	QCE_MODE_CBC = 0,
+	QCE_MODE_ECB = 1,
+	QCE_MODE_CTR = 2,
+	QCE_MODE_XTS = 3,
+	QCE_MODE_CCM = 4,
+	QCE_CIPHER_MODE_LAST
+};
+
+/* Cipher operation type */
+enum qce_req_op_enum {
+	QCE_REQ_ABLK_CIPHER = 0,
+	QCE_REQ_ABLK_CIPHER_NO_KEY = 1,
+	QCE_REQ_AEAD = 2,
+	QCE_REQ_LAST
+};
+
+/* Algorithms/features supported in CE HW engine */
+struct ce_hw_support {
+	bool sha1_hmac_20; /* Supports 20 bytes of HMAC key*/
+	bool sha1_hmac; /* supports max HMAC key of 64 bytes*/
+	bool sha256_hmac; /* supports max HMAC key of 64 bytes*/
+	bool sha_hmac; /* supports SHA1 and SHA256 MAX HMAC key of 64 bytes*/
+	bool cmac;
+	bool aes_key_192;
+	bool aes_xts;
+	bool aes_ccm;
+	bool ota;
+};
+
+/* Sha operation parameters */
+struct qce_sha_req {
+	qce_comp_func_ptr_t qce_cb;	/* call back */
+	enum qce_hash_alg_enum alg;	/* sha algorithm */
+	unsigned char *digest;		/* sha digest  */
+	struct scatterlist *src;	/* pointer to scatter list entry */
+	uint32_t  auth_data[4];		/* byte count */
+	unsigned char *authkey;		/* auth key */
+	unsigned int  authklen;		/* auth key length */
+	bool first_blk;			/* first block indicator */
+	bool last_blk;			/* last block indicator */
+	unsigned int size;		/* data length in bytes */
+	void *areq;
+};
+
+struct qce_req {
+	enum qce_req_op_enum op;	/* operation type */
+	qce_comp_func_ptr_t qce_cb;	/* call back */
+	void *areq;
+	enum qce_cipher_alg_enum   alg;	/* cipher algorithms*/
+	enum qce_cipher_dir_enum dir;	/* encryption? decryption? */
+	enum qce_cipher_mode_enum mode;	/* algorithm mode */
+	unsigned char *authkey;		/* authentication key  */
+	unsigned int authklen;		/* authentication key kength */
+	unsigned int authsize;		/* authentication key kength */
+	unsigned char  nonce[MAX_NONCE];/* nonce for ccm mode */
+	unsigned char *assoc;		/* Ptr to formatted associated data */
+	unsigned int assoclen;		/* Formatted associated data length  */
+	struct scatterlist *asg;	/* Formatted associated data sg  */
+	unsigned char *enckey;		/* cipher key  */
+	unsigned int encklen;		/* cipher key length */
+	unsigned char *iv;		/* initialization vector */
+	unsigned int ivsize;		/* initialization vector size*/
+	unsigned int cryptlen;		/* data length */
+	unsigned int use_pmem;		/* is source of data PMEM allocated? */
+	struct qcedev_pmem_info *pmem;	/* pointer to pmem_info structure*/
+};
+
+void *qce_open(struct platform_device *pdev, int *rc);
+int qce_close(void *handle);
+int qce_aead_req(void *handle, struct qce_req *req);
+int qce_ablk_cipher_req(void *handle, struct qce_req *req);
+int qce_hw_support(void *handle, struct ce_hw_support *support);
+int qce_process_sha_req(void *handle, struct qce_sha_req *s_req);
+
+#endif /* __CRYPTO_MSM_QCE_H */
diff --git a/drivers/crypto/msm/qce40.c b/drivers/crypto/msm/qce40.c
new file mode 100644
index 0000000..c203fc5
--- /dev/null
+++ b/drivers/crypto/msm/qce40.c
@@ -0,0 +1,2609 @@
+/* Qualcomm Crypto Engine driver.
+ *
+ * Copyright (c) 2011 - 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/crypto.h>
+#include <linux/qcedev.h>
+#include <linux/bitops.h>
+#include <crypto/hash.h>
+#include <crypto/sha.h>
+#include <mach/dma.h>
+#include <mach/clk.h>
+#include <mach/socinfo.h>
+
+#include "qce.h"
+#include "qce40.h"
+#include "qcryptohw_40.h"
+
+/* ADM definitions */
+#define LI_SG_CMD  (1 << 31)    /* last index in the scatter gather cmd */
+#define SRC_INDEX_SG_CMD(index) ((index & 0x3fff) << 16)
+#define DST_INDEX_SG_CMD(index) (index & 0x3fff)
+#define ADM_DESC_LAST  (1 << 31)
+#define QCE_FIFO_SIZE  0x8000
+
+/*
+ * CE HW device structure.
+ * Each engine has an instance of the structure.
+ * Each engine can only handle one crypto operation at one time. It is up to
+ * the sw above to ensure single threading of operation on an engine.
+ */
+struct qce_device {
+	struct device *pdev;        /* Handle to platform_device structure */
+
+	unsigned char *coh_vmem;    /* Allocated coherent virtual memory */
+	dma_addr_t coh_pmem;	    /* Allocated coherent physical memory */
+	int memsize;				/* Memory allocated */
+
+	void __iomem *iobase;	    /* Virtual io base of CE HW  */
+	unsigned int phy_iobase;    /* Physical io base of CE HW    */
+
+	struct clk *ce_core_src_clk;	/* Handle to CE src clk*/
+	struct clk *ce_core_clk;	/* Handle to CE clk */
+	struct clk *ce_clk;		/* Handle to CE clk */
+
+	qce_comp_func_ptr_t qce_cb;	/* qce callback function pointer */
+
+	int assoc_nents;
+	int ivsize;
+	int authsize;
+	int src_nents;
+	int dst_nents;
+
+	void *areq;
+	enum qce_cipher_mode_enum mode;
+	struct ce_dm_data ce_dm;
+};
+
+/* Standard initialization vector for SHA-1, source: FIPS 180-2 */
+static uint8_t  _std_init_vector_sha1_uint8[] =   {
+	0x67, 0x45, 0x23, 0x01, 0xEF, 0xCD, 0xAB, 0x89,
+	0x98, 0xBA, 0xDC, 0xFE, 0x10, 0x32, 0x54, 0x76,
+	0xC3, 0xD2, 0xE1, 0xF0
+};
+
+/* Standard initialization vector for SHA-256, source: FIPS 180-2 */
+static uint8_t _std_init_vector_sha256_uint8[] = {
+	0x6A, 0x09, 0xE6, 0x67, 0xBB, 0x67, 0xAE, 0x85,
+	0x3C, 0x6E, 0xF3, 0x72, 0xA5, 0x4F, 0xF5, 0x3A,
+	0x51, 0x0E, 0x52, 0x7F, 0x9B, 0x05, 0x68, 0x8C,
+	0x1F, 0x83, 0xD9, 0xAB, 0x5B, 0xE0, 0xCD, 0x19
+};
+
+static void _byte_stream_swap_to_net_words(uint32_t *iv, unsigned char *b,
+		unsigned int len)
+{
+	unsigned i, j;
+	unsigned char swap_iv[AES_IV_LENGTH];
+
+	memset(swap_iv, 0, AES_IV_LENGTH);
+	for (i = (AES_IV_LENGTH-len), j = len-1;  i < AES_IV_LENGTH; i++, j--)
+		swap_iv[i] = b[j];
+	memcpy(iv, swap_iv, AES_IV_LENGTH);
+}
+
+static int count_sg(struct scatterlist *sg, int nbytes)
+{
+	int i;
+
+	for (i = 0; nbytes > 0; i++, sg = sg_next(sg))
+		nbytes -= sg->length;
+	return i;
+}
+
+static int dma_map_pmem_sg(struct buf_info *pmem, unsigned entries,
+						struct scatterlist *sg)
+{
+	int i;
+	for (i = 0; i < entries; i++) {
+
+		sg->dma_address = (dma_addr_t)pmem->offset;
+		sg++;
+		pmem++;
+	}
+	return 0;
+}
+
+static int _probe_ce_engine(struct qce_device *pce_dev)
+{
+	unsigned int val;
+	unsigned int rev;
+	unsigned int ret;
+
+	val = (uint32_t)(*((uint32_t *)pce_dev->ce_dm.buffer.version));
+	if (((val & 0xfffffff) != 0x0000043) &&
+			((val & 0xfffffff) != 0x0000042) &&
+			((val & 0xfffffff) != 0x0000040)) {
+		dev_err(pce_dev->pdev,
+				"Unknown Qualcomm crypto device at 0x%x 0x%x\n",
+				pce_dev->phy_iobase, val);
+		return -EIO;
+	};
+	rev = (val & CRYPTO_CORE_REV_MASK);
+	if (rev >= 0x42) {
+		dev_info(pce_dev->pdev,
+				"Qualcomm Crypto 4.2 device found at 0x%x\n",
+				pce_dev->phy_iobase);
+		pce_dev->ce_dm.ce_block_size = 64;
+
+		/* Configure the crypto register to support 64byte CRCI if it
+		 * is not XPU protected and the HW version of device is greater
+		 * than 0x42.
+		 * Crypto config register returns a 0 when it is XPU protected.
+		 */
+
+		ret = readl_relaxed(pce_dev->iobase + CRYPTO_CONFIG_REG);
+		if (ret) {
+			val = BIT(CRYPTO_MASK_DOUT_INTR) |
+					BIT(CRYPTO_MASK_DIN_INTR) |
+					BIT(CRYPTO_MASK_OP_DONE_INTR) |
+					BIT(CRYPTO_MASK_ERR_INTR) |
+					(CRYPTO_REQ_SIZE_ENUM_64_BYTES <<
+						CRYPTO_REQ_SIZE) |
+					(CRYPTO_FIFO_ENUM_64_BYTES <<
+						CRYPTO_FIFO_THRESHOLD);
+
+			writel_relaxed(val, pce_dev->iobase +
+					CRYPTO_CONFIG_REG);
+		} /* end of if (ret) */
+	} else {
+		if (rev == 0x40) {
+			dev_info(pce_dev->pdev,
+				"Qualcomm Crypto 4.0 device found at 0x%x\n",
+							pce_dev->phy_iobase);
+			pce_dev->ce_dm.ce_block_size = 16;
+		}
+	}
+
+	dev_info(pce_dev->pdev,
+			"IO base 0x%x\n, ce_in channel %d     , "
+			"ce_out channel %d\n, "
+			"crci_in %d, crci_out %d\n",
+			(unsigned int) pce_dev->iobase,
+			pce_dev->ce_dm.chan_ce_in, pce_dev->ce_dm.chan_ce_out,
+			pce_dev->ce_dm.crci_in, pce_dev->ce_dm.crci_out);
+
+	return 0;
+};
+
+
+static void _check_probe_done_call_back(struct msm_dmov_cmd *cmd_ptr,
+		unsigned int result, struct msm_dmov_errdata *err)
+{
+	struct qce_device *pce_dev;
+	pce_dev = (struct qce_device *) cmd_ptr->user;
+
+	if (result != ADM_STATUS_OK) {
+		dev_err(pce_dev->pdev, "Qualcomm ADM status error %x\n",
+						result);
+		pce_dev->ce_dm.chan_ce_in_status = -1;
+	} else {
+		_probe_ce_engine(pce_dev);
+		pce_dev->ce_dm.chan_ce_in_status = 0;
+	}
+	pce_dev->ce_dm.chan_ce_in_state = QCE_CHAN_STATE_IDLE;
+};
+
+static int _init_ce_engine(struct qce_device *pce_dev)
+{
+	int status;
+	/* Reset ce */
+	clk_reset(pce_dev->ce_core_clk, CLK_RESET_ASSERT);
+	clk_reset(pce_dev->ce_core_clk, CLK_RESET_DEASSERT);
+
+	/*
+	* Ensure previous instruction (any writes to CLK registers)
+	* to toggle the CLK reset lines was completed before configuring
+	* ce engine. The ce engine configuration settings should not be lost
+	* becasue of clk reset.
+	*/
+	mb();
+
+	/*
+	 * Clear ACCESS_VIOL bit in CRYPTO_STATUS REGISTER
+	*/
+	status = readl_relaxed(pce_dev->iobase + CRYPTO_STATUS_REG);
+	*((uint32_t *)(pce_dev->ce_dm.buffer.status)) = status & (~0x40000);
+	/*
+	* Ensure ce configuration is completed.
+	*/
+	mb();
+
+	pce_dev->ce_dm.chan_ce_in_cmd->complete_func =
+				_check_probe_done_call_back;
+	pce_dev->ce_dm.chan_ce_in_cmd->cmdptr =
+				pce_dev->ce_dm.cmdptrlist.probe_ce_hw;
+	pce_dev->ce_dm.chan_ce_in_state = QCE_CHAN_STATE_IN_PROG;
+	pce_dev->ce_dm.chan_ce_out_state = QCE_CHAN_STATE_COMP;
+	msm_dmov_enqueue_cmd(pce_dev->ce_dm.chan_ce_in,
+					pce_dev->ce_dm.chan_ce_in_cmd);
+
+	return 0;
+};
+
+static int _ce_setup_hash_cmdrptrlist(struct qce_device *pce_dev,
+						struct qce_sha_req *sreq)
+{
+	struct ce_cmdptrlists_ops *cmdptrlist = &pce_dev->ce_dm.cmdptrlist;
+
+	switch (sreq->alg) {
+	case QCE_HASH_SHA1:
+		pce_dev->ce_dm.chan_ce_in_cmd->cmdptr = cmdptrlist->auth_sha1;
+		break;
+
+	case QCE_HASH_SHA256:
+		pce_dev->ce_dm.chan_ce_in_cmd->cmdptr = cmdptrlist->auth_sha256;
+		break;
+	case QCE_HASH_SHA1_HMAC:
+		pce_dev->ce_dm.chan_ce_in_cmd->cmdptr =
+						cmdptrlist->auth_sha1_hmac;
+			break;
+
+	case QCE_HASH_SHA256_HMAC:
+		pce_dev->ce_dm.chan_ce_in_cmd->cmdptr =
+						cmdptrlist->auth_sha256_hmac;
+		break;
+	case QCE_HASH_AES_CMAC:
+		if (sreq->authklen == AES128_KEY_SIZE)
+			pce_dev->ce_dm.chan_ce_in_cmd->cmdptr =
+				cmdptrlist->auth_aes_128_cmac;
+		else
+			pce_dev->ce_dm.chan_ce_in_cmd->cmdptr =
+				cmdptrlist->auth_aes_256_cmac;
+		break;
+
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int _ce_setup_hash(struct qce_device *pce_dev, struct qce_sha_req *sreq)
+{
+	uint32_t diglen;
+	int i;
+	uint32_t auth_cfg = 0;
+	bool sha1 = false;
+
+	if (sreq->alg ==  QCE_HASH_AES_CMAC) {
+
+		memcpy(pce_dev->ce_dm.buffer.auth_key, sreq->authkey,
+						sreq->authklen);
+		auth_cfg |= (1 << CRYPTO_LAST);
+		auth_cfg |= (CRYPTO_AUTH_MODE_CMAC << CRYPTO_AUTH_MODE);
+		auth_cfg |= (CRYPTO_AUTH_SIZE_ENUM_16_BYTES <<
+							CRYPTO_AUTH_SIZE);
+		auth_cfg |= CRYPTO_AUTH_ALG_AES << CRYPTO_AUTH_ALG;
+
+		switch (sreq->authklen) {
+		case AES128_KEY_SIZE:
+			auth_cfg |= (CRYPTO_AUTH_KEY_SZ_AES128 <<
+						CRYPTO_AUTH_KEY_SIZE);
+			break;
+		case AES256_KEY_SIZE:
+			auth_cfg |= (CRYPTO_AUTH_KEY_SZ_AES256 <<
+					CRYPTO_AUTH_KEY_SIZE);
+			break;
+		default:
+			break;
+		}
+
+		goto go_proc;
+	}
+
+	/* if not the last, the size has to be on the block boundary */
+	if (sreq->last_blk == 0 && (sreq->size % SHA256_BLOCK_SIZE))
+		return -EIO;
+
+	switch (sreq->alg) {
+	case QCE_HASH_SHA1:
+	case QCE_HASH_SHA1_HMAC:
+		diglen = SHA1_DIGEST_SIZE;
+		sha1 = true;
+		break;
+	case QCE_HASH_SHA256:
+	case QCE_HASH_SHA256_HMAC:
+		diglen = SHA256_DIGEST_SIZE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if ((sreq->alg == QCE_HASH_SHA1_HMAC) ||
+				(sreq->alg == QCE_HASH_SHA256_HMAC)) {
+
+		memcpy(pce_dev->ce_dm.buffer.auth_key, sreq->authkey,
+						sreq->authklen);
+		auth_cfg |= (CRYPTO_AUTH_MODE_HMAC << CRYPTO_AUTH_MODE);
+	} else {
+		auth_cfg |= (CRYPTO_AUTH_MODE_HASH << CRYPTO_AUTH_MODE);
+	}
+
+	/* write 20/32 bytes, 5/8 words into auth_iv for SHA1/SHA256 */
+	if (sreq->first_blk) {
+		if (sha1)
+			memcpy(pce_dev->ce_dm.buffer.auth_iv,
+				_std_init_vector_sha1_uint8, diglen);
+		else
+			memcpy(pce_dev->ce_dm.buffer.auth_iv,
+				_std_init_vector_sha256_uint8, diglen);
+	} else {
+		memcpy(pce_dev->ce_dm.buffer.auth_iv, sreq->digest,
+								diglen);
+	}
+
+	/* write auth_bytecnt 0/1/2/3, start with 0 */
+	for (i = 0; i < 4; i++)
+		*(((uint32_t *)(pce_dev->ce_dm.buffer.auth_byte_count) + i)) =
+						sreq->auth_data[i];
+
+	/* write seg_cfg */
+	if (sha1)
+		auth_cfg |= (CRYPTO_AUTH_SIZE_SHA1 << CRYPTO_AUTH_SIZE);
+	else
+		auth_cfg |= (CRYPTO_AUTH_SIZE_SHA256 << CRYPTO_AUTH_SIZE);
+
+	if (sreq->last_blk)
+		auth_cfg |= 1 << CRYPTO_LAST;
+
+	auth_cfg |= CRYPTO_AUTH_ALG_SHA << CRYPTO_AUTH_ALG;
+
+go_proc:
+	auth_cfg |= (CRYPTO_AUTH_POS_BEFORE << CRYPTO_AUTH_POS);
+
+	/* write auth seg cfg */
+	*((uint32_t *)(pce_dev->ce_dm.buffer.auth_seg_cfg_size_start)) =
+								auth_cfg;
+	/* write auth seg size */
+	*((uint32_t *)(pce_dev->ce_dm.buffer.auth_seg_cfg_size_start) + 1) =
+								sreq->size;
+
+	/* write auth seg size start*/
+	*((uint32_t *)(pce_dev->ce_dm.buffer.auth_seg_cfg_size_start)+2) = 0;
+
+	/* write seg size */
+	*((uint32_t *)(pce_dev->ce_dm.buffer.seg_size)) = sreq->size;
+
+	_ce_setup_hash_cmdrptrlist(pce_dev, sreq);
+
+	return 0;
+}
+
+static int _ce_setup_cipher_cmdrptrlist(struct qce_device *pce_dev,
+							struct qce_req *creq)
+{
+	struct ce_cmdptrlists_ops *cmdptrlist =
+				&pce_dev->ce_dm.cmdptrlist;
+
+	if (creq->alg != CIPHER_ALG_AES) {
+		switch (creq->alg) {
+		case CIPHER_ALG_DES:
+			if (creq->mode ==  QCE_MODE_ECB) {
+				pce_dev->ce_dm.chan_ce_in_cmd->cmdptr =
+					cmdptrlist->cipher_des_ecb;
+			} else {
+				pce_dev->ce_dm.chan_ce_in_cmd->cmdptr =
+					cmdptrlist->cipher_des_cbc;
+			}
+			break;
+
+		case CIPHER_ALG_3DES:
+			if (creq->mode ==  QCE_MODE_ECB) {
+				pce_dev->ce_dm.chan_ce_in_cmd->cmdptr =
+						cmdptrlist->cipher_3des_ecb;
+			} else {
+				pce_dev->ce_dm.chan_ce_in_cmd->cmdptr =
+						cmdptrlist->cipher_3des_cbc;
+			}
+			break;
+		default:
+			break;
+		}
+	} else {
+		switch (creq->mode) {
+		case QCE_MODE_ECB:
+			if (creq->encklen ==  AES128_KEY_SIZE) {
+				pce_dev->ce_dm.chan_ce_in_cmd->cmdptr =
+						cmdptrlist->cipher_aes_128_ecb;
+			} else {
+				pce_dev->ce_dm.chan_ce_in_cmd->cmdptr =
+						cmdptrlist->cipher_aes_256_ecb;
+			}
+			break;
+
+		case QCE_MODE_CBC:
+			if (creq->encklen ==  AES128_KEY_SIZE) {
+				pce_dev->ce_dm.chan_ce_in_cmd->cmdptr =
+					cmdptrlist->cipher_aes_128_cbc_ctr;
+			} else {
+				pce_dev->ce_dm.chan_ce_in_cmd->cmdptr =
+					cmdptrlist->cipher_aes_256_cbc_ctr;
+			}
+			break;
+
+		case QCE_MODE_CTR:
+			if (creq->encklen ==  AES128_KEY_SIZE) {
+				pce_dev->ce_dm.chan_ce_in_cmd->cmdptr =
+					cmdptrlist->cipher_aes_128_cbc_ctr;
+			} else {
+				pce_dev->ce_dm.chan_ce_in_cmd->cmdptr =
+					cmdptrlist->cipher_aes_256_cbc_ctr;
+			}
+			break;
+
+		case QCE_MODE_XTS:
+			if (creq->encklen ==  AES128_KEY_SIZE) {
+				pce_dev->ce_dm.chan_ce_in_cmd->cmdptr =
+					cmdptrlist->cipher_aes_128_xts;
+			} else {
+				pce_dev->ce_dm.chan_ce_in_cmd->cmdptr =
+					cmdptrlist->cipher_aes_256_xts;
+			}
+			break;
+		case QCE_MODE_CCM:
+			if (creq->encklen ==  AES128_KEY_SIZE) {
+				pce_dev->ce_dm.chan_ce_in_cmd->cmdptr =
+					cmdptrlist->aead_aes_128_ccm;
+			} else {
+				pce_dev->ce_dm.chan_ce_in_cmd->cmdptr =
+					cmdptrlist->aead_aes_256_ccm;
+			}
+			break;
+		default:
+			break;
+		}
+	}
+
+	switch (creq->mode) {
+	case QCE_MODE_CCM:
+		pce_dev->ce_dm.chan_ce_out_cmd->cmdptr =
+					cmdptrlist->aead_ce_out;
+		break;
+	case QCE_MODE_ECB:
+		pce_dev->ce_dm.chan_ce_out_cmd->cmdptr =
+					cmdptrlist->cipher_ce_out;
+		break;
+	default:
+		pce_dev->ce_dm.chan_ce_out_cmd->cmdptr =
+					cmdptrlist->cipher_ce_out_get_iv;
+		break;
+	}
+
+	return 0;
+}
+
+static int _ce_setup_cipher(struct qce_device *pce_dev, struct qce_req *creq,
+		uint32_t totallen_in, uint32_t coffset)
+{
+	uint32_t enck_size_in_word = creq->encklen / sizeof(uint32_t);
+	uint32_t encr_cfg = 0;
+	uint32_t ivsize = creq->ivsize;
+	struct ce_reg_buffer_addr *buffer = &pce_dev->ce_dm.buffer;
+
+	if (creq->mode ==  QCE_MODE_XTS)
+		memcpy(buffer->encr_key, creq->enckey,
+						creq->encklen/2);
+	else
+		memcpy(buffer->encr_key, creq->enckey, creq->encklen);
+
+	if ((creq->op == QCE_REQ_AEAD) && (creq->mode == QCE_MODE_CCM)) {
+		uint32_t noncelen32 = MAX_NONCE/sizeof(uint32_t);
+		uint32_t auth_cfg = 0;
+
+		/* write nonce */
+		memcpy(buffer->auth_nonce_info, creq->nonce, MAX_NONCE);
+		memcpy(buffer->auth_key, creq->enckey, creq->encklen);
+
+		auth_cfg |= (noncelen32 << CRYPTO_AUTH_NONCE_NUM_WORDS);
+		auth_cfg &= ~(1 << CRYPTO_USE_HW_KEY_AUTH);
+		auth_cfg |= (1 << CRYPTO_LAST);
+		if (creq->dir == QCE_ENCRYPT)
+			auth_cfg |= (CRYPTO_AUTH_POS_BEFORE << CRYPTO_AUTH_POS);
+		else
+			auth_cfg |= (CRYPTO_AUTH_POS_AFTER << CRYPTO_AUTH_POS);
+		auth_cfg |= (((creq->authsize >> 1) - 2) << CRYPTO_AUTH_SIZE);
+		auth_cfg |= (CRYPTO_AUTH_MODE_CCM << CRYPTO_AUTH_MODE);
+		if (creq->authklen ==  AES128_KEY_SIZE)
+			auth_cfg |= (CRYPTO_AUTH_KEY_SZ_AES128 <<
+						CRYPTO_AUTH_KEY_SIZE);
+		else {
+			if (creq->authklen ==  AES256_KEY_SIZE)
+				auth_cfg |= (CRYPTO_AUTH_KEY_SZ_AES256 <<
+							CRYPTO_AUTH_KEY_SIZE);
+		}
+		auth_cfg |= (CRYPTO_AUTH_ALG_AES << CRYPTO_AUTH_ALG);
+		*((uint32_t *)(buffer->auth_seg_cfg_size_start)) = auth_cfg;
+
+		if (creq->dir == QCE_ENCRYPT)
+			*((uint32_t *)(buffer->auth_seg_cfg_size_start) + 1) =
+								totallen_in;
+		else
+			*((uint32_t *)(buffer->auth_seg_cfg_size_start) + 1) =
+						(totallen_in - creq->authsize);
+		*((uint32_t *)(buffer->auth_seg_cfg_size_start) + 2) = 0;
+	}
+
+	*((uint32_t *)(buffer->auth_seg_cfg_size_start) + 2) = 0;
+
+	switch (creq->mode) {
+	case QCE_MODE_ECB:
+		encr_cfg |= (CRYPTO_ENCR_MODE_ECB << CRYPTO_ENCR_MODE);
+		break;
+
+	case QCE_MODE_CBC:
+		encr_cfg |= (CRYPTO_ENCR_MODE_CBC << CRYPTO_ENCR_MODE);
+		break;
+
+	case QCE_MODE_XTS:
+		encr_cfg |= (CRYPTO_ENCR_MODE_XTS << CRYPTO_ENCR_MODE);
+		break;
+
+	case QCE_MODE_CCM:
+		encr_cfg |= (CRYPTO_ENCR_MODE_CCM << CRYPTO_ENCR_MODE);
+		break;
+
+	case QCE_MODE_CTR:
+	default:
+		encr_cfg |= (CRYPTO_ENCR_MODE_CTR << CRYPTO_ENCR_MODE);
+		break;
+	}
+	pce_dev->mode = creq->mode;
+
+	switch (creq->alg) {
+	case CIPHER_ALG_DES:
+		if (creq->mode !=  QCE_MODE_ECB)
+			memcpy(buffer->encr_cntr_iv, creq->iv, ivsize);
+
+		encr_cfg |= ((CRYPTO_ENCR_KEY_SZ_DES << CRYPTO_ENCR_KEY_SZ)  |
+				(CRYPTO_ENCR_ALG_DES << CRYPTO_ENCR_ALG));
+		break;
+
+	case CIPHER_ALG_3DES:
+		if (creq->mode !=  QCE_MODE_ECB)
+			memcpy(buffer->encr_cntr_iv, creq->iv, ivsize);
+
+		encr_cfg |= ((CRYPTO_ENCR_KEY_SZ_3DES << CRYPTO_ENCR_KEY_SZ)  |
+				(CRYPTO_ENCR_ALG_DES << CRYPTO_ENCR_ALG));
+		break;
+
+	case CIPHER_ALG_AES:
+	default:
+		if (creq->mode ==  QCE_MODE_XTS) {
+			memcpy(buffer->encr_xts_key, (creq->enckey +
+					creq->encklen/2), creq->encklen/2);
+			*((uint32_t *)(buffer->encr_xts_du_size)) =
+							creq->cryptlen;
+
+		}
+		if (creq->mode !=  QCE_MODE_ECB) {
+			if (creq->mode ==  QCE_MODE_XTS)
+				_byte_stream_swap_to_net_words(
+					(uint32_t *)(buffer->encr_cntr_iv),
+							creq->iv, ivsize);
+			else
+				memcpy(buffer->encr_cntr_iv, creq->iv,
+								ivsize);
+		}
+		/* set number of counter bits */
+		*((uint32_t *)(buffer->encr_mask)) = (uint32_t)0xffffffff;
+
+		if (creq->op == QCE_REQ_ABLK_CIPHER_NO_KEY) {
+				encr_cfg |= (CRYPTO_ENCR_KEY_SZ_AES128 <<
+						CRYPTO_ENCR_KEY_SZ);
+			encr_cfg |= CRYPTO_ENCR_ALG_AES << CRYPTO_ENCR_ALG;
+		} else {
+			uint32_t key_size;
+
+			if (creq->mode == QCE_MODE_XTS) {
+				key_size = creq->encklen/2;
+				enck_size_in_word = key_size/sizeof(uint32_t);
+			} else {
+				key_size = creq->encklen;
+			}
+
+			switch (key_size) {
+			case AES128_KEY_SIZE:
+				encr_cfg |= (CRYPTO_ENCR_KEY_SZ_AES128 <<
+							CRYPTO_ENCR_KEY_SZ);
+				break;
+			case AES256_KEY_SIZE:
+			default:
+				encr_cfg |= (CRYPTO_ENCR_KEY_SZ_AES256 <<
+							CRYPTO_ENCR_KEY_SZ);
+				break;
+			} /* end of switch (creq->encklen) */
+
+			encr_cfg |= CRYPTO_ENCR_ALG_AES << CRYPTO_ENCR_ALG;
+		} /* else of if (creq->op == QCE_REQ_ABLK_CIPHER_NO_KEY) */
+		break;
+	} /* end of switch (creq->mode)  */
+
+	/* write encr seg cfg */
+	encr_cfg |= ((creq->dir == QCE_ENCRYPT) ? 1 : 0) << CRYPTO_ENCODE;
+
+	/* write encr seg cfg */
+	*((uint32_t *)(buffer->encr_seg_cfg_size_start)) = encr_cfg;
+	/* write encr seg size */
+	if ((creq->mode == QCE_MODE_CCM) && (creq->dir == QCE_DECRYPT))
+		*((uint32_t *)(buffer->encr_seg_cfg_size_start) + 1) =
+					(creq->cryptlen + creq->authsize);
+	else
+		*((uint32_t *)(buffer->encr_seg_cfg_size_start) + 1) =
+							creq->cryptlen;
+
+
+	*((uint32_t *)(buffer->encr_seg_cfg_size_start) + 2) =
+						(coffset & 0xffff);
+
+	*((uint32_t *)(buffer->seg_size)) = totallen_in;
+
+	_ce_setup_cipher_cmdrptrlist(pce_dev, creq);
+	return 0;
+};
+
+static int _aead_complete(struct qce_device *pce_dev)
+{
+	struct aead_request *areq;
+
+	areq = (struct aead_request *) pce_dev->areq;
+
+	if (areq->src != areq->dst) {
+		dma_unmap_sg(pce_dev->pdev, areq->dst, pce_dev->dst_nents,
+					DMA_FROM_DEVICE);
+	}
+	dma_unmap_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
+			(areq->src == areq->dst) ? DMA_BIDIRECTIONAL :
+							DMA_TO_DEVICE);
+
+	dma_unmap_sg(pce_dev->pdev, areq->assoc, pce_dev->assoc_nents,
+			DMA_TO_DEVICE);
+
+	/* check MAC */
+	if (pce_dev->mode == QCE_MODE_CCM) {
+		uint32_t result;
+
+		result =
+			(uint32_t)(*((uint32_t *)pce_dev->ce_dm.buffer.status));
+		result &= (1 << CRYPTO_MAC_FAILED);
+		result |= (pce_dev->ce_dm.chan_ce_in_status |
+					pce_dev->ce_dm.chan_ce_out_status);
+		pce_dev->qce_cb(areq, pce_dev->ce_dm.buffer.auth_result, NULL,
+							result);
+	}
+	return 0;
+};
+
+static void _sha_complete(struct qce_device *pce_dev)
+{
+	struct ahash_request *areq;
+
+	areq = (struct ahash_request *) pce_dev->areq;
+	dma_unmap_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
+				DMA_TO_DEVICE);
+
+	pce_dev->qce_cb(areq,  pce_dev->ce_dm.buffer.auth_result,
+				pce_dev->ce_dm.buffer.auth_byte_count,
+				pce_dev->ce_dm.chan_ce_in_status);
+
+};
+
+static int _ablk_cipher_complete(struct qce_device *pce_dev)
+{
+	struct ablkcipher_request *areq;
+
+	areq = (struct ablkcipher_request *) pce_dev->areq;
+
+	if (areq->src != areq->dst) {
+		dma_unmap_sg(pce_dev->pdev, areq->dst,
+			pce_dev->dst_nents, DMA_FROM_DEVICE);
+	}
+	dma_unmap_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
+		(areq->src == areq->dst) ? DMA_BIDIRECTIONAL :
+						DMA_TO_DEVICE);
+
+	if (pce_dev->mode == QCE_MODE_ECB) {
+		pce_dev->qce_cb(areq, NULL, NULL,
+					pce_dev->ce_dm.chan_ce_in_status |
+					pce_dev->ce_dm.chan_ce_out_status);
+	} else {
+
+		pce_dev->qce_cb(areq, NULL, pce_dev->ce_dm.buffer.encr_cntr_iv,
+			pce_dev->ce_dm.chan_ce_in_status |
+			pce_dev->ce_dm.chan_ce_out_status);
+	}
+
+	return 0;
+};
+
+static int _ablk_cipher_use_pmem_complete(struct qce_device *pce_dev)
+{
+	struct ablkcipher_request *areq;
+
+	areq = (struct ablkcipher_request *) pce_dev->areq;
+
+	if (pce_dev->mode == QCE_MODE_ECB) {
+		pce_dev->qce_cb(areq, NULL, NULL,
+					pce_dev->ce_dm.chan_ce_in_status |
+					pce_dev->ce_dm.chan_ce_out_status);
+	} else {
+		pce_dev->qce_cb(areq, NULL, pce_dev->ce_dm.buffer.encr_cntr_iv,
+					pce_dev->ce_dm.chan_ce_in_status |
+					pce_dev->ce_dm.chan_ce_out_status);
+	}
+
+	return 0;
+};
+
+static int qce_split_and_insert_dm_desc(struct dmov_desc *pdesc,
+			unsigned int plen, unsigned int paddr, int *index)
+{
+	while (plen > QCE_FIFO_SIZE) {
+		pdesc->len = QCE_FIFO_SIZE;
+		if (paddr > 0) {
+			pdesc->addr = paddr;
+			paddr += QCE_FIFO_SIZE;
+		}
+		plen -= pdesc->len;
+		if (plen > 0) {
+			*index = (*index) + 1;
+			if ((*index) >= QCE_MAX_NUM_DESC)
+				return -ENOMEM;
+			pdesc++;
+		}
+	}
+	if ((plen > 0) && (plen <= QCE_FIFO_SIZE)) {
+		pdesc->len = plen;
+		if (paddr > 0)
+			pdesc->addr = paddr;
+	}
+
+	return 0;
+}
+
+static int _chain_sg_buffer_in(struct qce_device *pce_dev,
+		struct scatterlist *sg, unsigned int nbytes)
+{
+	unsigned int len;
+	unsigned int dlen;
+	struct dmov_desc *pdesc;
+
+	pdesc = pce_dev->ce_dm.ce_in_src_desc +
+				pce_dev->ce_dm.ce_in_src_desc_index;
+	/*
+	 * Two consective chunks may be handled by the old
+	 * buffer descriptor.
+	 */
+	while (nbytes > 0) {
+		len = min(nbytes, sg_dma_len(sg));
+		dlen = pdesc->len & ADM_DESC_LENGTH_MASK;
+		nbytes -= len;
+		if (dlen == 0) {
+			pdesc->addr  = sg_dma_address(sg);
+			pdesc->len = len;
+			if (pdesc->len > QCE_FIFO_SIZE)
+				qce_split_and_insert_dm_desc(pdesc, pdesc->len,
+					sg_dma_address(sg),
+					&pce_dev->ce_dm.ce_in_src_desc_index);
+		} else if (sg_dma_address(sg) == (pdesc->addr + dlen)) {
+			pdesc->len  = dlen + len;
+			if (pdesc->len > QCE_FIFO_SIZE)
+				qce_split_and_insert_dm_desc(pdesc, pdesc->len,
+					pdesc->addr,
+					&pce_dev->ce_dm.ce_in_src_desc_index);
+		} else {
+			pce_dev->ce_dm.ce_in_src_desc_index++;
+			if (pce_dev->ce_dm.ce_in_src_desc_index >=
+							QCE_MAX_NUM_DESC)
+				return -ENOMEM;
+			pdesc++;
+			pdesc->len = len;
+			pdesc->addr = sg_dma_address(sg);
+			if (pdesc->len > QCE_FIFO_SIZE)
+				qce_split_and_insert_dm_desc(pdesc, pdesc->len,
+					sg_dma_address(sg),
+					&pce_dev->ce_dm.ce_in_src_desc_index);
+		}
+		if (nbytes > 0)
+			sg = sg_next(sg);
+	}
+	return 0;
+}
+
+static int _chain_pm_buffer_in(struct qce_device *pce_dev,
+		unsigned int pmem, unsigned int nbytes)
+{
+	unsigned int dlen;
+	struct dmov_desc *pdesc;
+
+	pdesc = pce_dev->ce_dm.ce_in_src_desc +
+				pce_dev->ce_dm.ce_in_src_desc_index;
+	dlen = pdesc->len & ADM_DESC_LENGTH_MASK;
+	if (dlen == 0) {
+		pdesc->addr  = pmem;
+		pdesc->len = nbytes;
+	} else if (pmem == (pdesc->addr + dlen)) {
+		pdesc->len  = dlen + nbytes;
+	} else {
+		pce_dev->ce_dm.ce_in_src_desc_index++;
+		if (pce_dev->ce_dm.ce_in_src_desc_index >=
+						QCE_MAX_NUM_DESC)
+			return -ENOMEM;
+		pdesc++;
+		pdesc->len = nbytes;
+		pdesc->addr = pmem;
+	}
+	return 0;
+}
+
+static void _chain_buffer_in_init(struct qce_device *pce_dev)
+{
+	struct dmov_desc *pdesc;
+
+	pce_dev->ce_dm.ce_in_src_desc_index = 0;
+	pce_dev->ce_dm.ce_in_dst_desc_index = 0;
+	pdesc = pce_dev->ce_dm.ce_in_src_desc;
+	pdesc->len = 0;
+}
+
+static void _ce_in_final(struct qce_device *pce_dev, unsigned total)
+{
+	struct dmov_desc *pdesc;
+	dmov_sg *pcmd;
+
+	pdesc = pce_dev->ce_dm.ce_in_src_desc +
+				pce_dev->ce_dm.ce_in_src_desc_index;
+	pdesc->len |= ADM_DESC_LAST;
+
+	pdesc = pce_dev->ce_dm.ce_in_dst_desc;
+	if (total > QCE_FIFO_SIZE) {
+		qce_split_and_insert_dm_desc(pdesc, total, 0,
+				&pce_dev->ce_dm.ce_in_dst_desc_index);
+		pdesc = pce_dev->ce_dm.ce_in_dst_desc +
+					pce_dev->ce_dm.ce_in_dst_desc_index;
+		pdesc->len |= ADM_DESC_LAST;
+	} else
+		pdesc->len = ADM_DESC_LAST | total;
+
+	pcmd = (dmov_sg *) pce_dev->ce_dm.cmdlist.ce_data_in;
+	pcmd->cmd |= CMD_LC;
+
+}
+
+#ifdef QCE_DEBUG
+static void _ce_in_dump(struct qce_device *pce_dev)
+{
+	int i;
+	struct dmov_desc *pdesc;
+
+	dev_info(pce_dev->pdev, "_ce_in_dump: src\n");
+	for (i = 0; i <= pce_dev->ce_dm.ce_in_src_desc_index; i++) {
+		pdesc = pce_dev->ce_dm.ce_in_src_desc + i;
+		dev_info(pce_dev->pdev, "%x , %x\n", pdesc->addr,
+				pdesc->len);
+	}
+	dev_info(pce_dev->pdev, "_ce_in_dump: dst\n");
+	for (i = 0; i <= pce_dev->ce_dm.ce_in_dst_desc_index; i++) {
+		pdesc = pce_dev->ce_dm.ce_in_dst_desc + i;
+		dev_info(pce_dev->pdev, "%x , %x\n", pdesc->addr,
+				pdesc->len);
+	}
+};
+
+static void _ce_out_dump(struct qce_device *pce_dev)
+{
+	int i;
+	struct dmov_desc *pdesc;
+
+	dev_info(pce_dev->pdev, "_ce_out_dump: src\n");
+	for (i = 0; i <= pce_dev->ce_dm.ce_out_src_desc_index; i++) {
+		pdesc = pce_dev->ce_dm.ce_out_src_desc + i;
+		dev_info(pce_dev->pdev, "%x , %x\n", pdesc->addr,
+				pdesc->len);
+	}
+
+	dev_info(pce_dev->pdev, "_ce_out_dump: dst\n");
+	for (i = 0; i <= pce_dev->ce_dm.ce_out_dst_desc_index; i++) {
+		pdesc = pce_dev->ce_dm.ce_out_dst_desc + i;
+		dev_info(pce_dev->pdev, "%x , %x\n", pdesc->addr,
+				pdesc->len);
+	}
+};
+
+#else
+
+static void _ce_in_dump(struct qce_device *pce_dev)
+{
+};
+
+static void _ce_out_dump(struct qce_device *pce_dev)
+{
+};
+
+#endif
+
+static int _chain_sg_buffer_out(struct qce_device *pce_dev,
+		struct scatterlist *sg, unsigned int nbytes)
+{
+	unsigned int len;
+	unsigned int dlen;
+	struct dmov_desc *pdesc;
+
+	pdesc = pce_dev->ce_dm.ce_out_dst_desc +
+				pce_dev->ce_dm.ce_out_dst_desc_index;
+	/*
+	 * Two consective chunks may be handled by the old
+	 * buffer descriptor.
+	 */
+	while (nbytes > 0) {
+		len = min(nbytes, sg_dma_len(sg));
+		dlen = pdesc->len & ADM_DESC_LENGTH_MASK;
+		nbytes -= len;
+		if (dlen == 0) {
+			pdesc->addr  = sg_dma_address(sg);
+			pdesc->len = len;
+			if (pdesc->len > QCE_FIFO_SIZE)
+				qce_split_and_insert_dm_desc(pdesc, pdesc->len,
+					sg_dma_address(sg),
+					&pce_dev->ce_dm.ce_out_dst_desc_index);
+		} else if (sg_dma_address(sg) == (pdesc->addr + dlen)) {
+			pdesc->len  = dlen + len;
+			if (pdesc->len > QCE_FIFO_SIZE)
+				qce_split_and_insert_dm_desc(pdesc, pdesc->len,
+					pdesc->addr,
+					&pce_dev->ce_dm.ce_out_dst_desc_index);
+
+		} else {
+			pce_dev->ce_dm.ce_out_dst_desc_index++;
+			if (pce_dev->ce_dm.ce_out_dst_desc_index >=
+							QCE_MAX_NUM_DESC)
+				return -EIO;
+			pdesc++;
+			pdesc->len = len;
+			pdesc->addr = sg_dma_address(sg);
+			if (pdesc->len > QCE_FIFO_SIZE)
+				qce_split_and_insert_dm_desc(pdesc, pdesc->len,
+					sg_dma_address(sg),
+					&pce_dev->ce_dm.ce_out_dst_desc_index);
+
+		}
+		if (nbytes > 0)
+			sg = sg_next(sg);
+	}
+	return 0;
+}
+
+static int _chain_pm_buffer_out(struct qce_device *pce_dev,
+		unsigned int pmem, unsigned int nbytes)
+{
+	unsigned int dlen;
+	struct dmov_desc *pdesc;
+
+	pdesc = pce_dev->ce_dm.ce_out_dst_desc +
+				pce_dev->ce_dm.ce_out_dst_desc_index;
+	dlen = pdesc->len & ADM_DESC_LENGTH_MASK;
+
+	if (dlen == 0) {
+		pdesc->addr  = pmem;
+		pdesc->len = nbytes;
+	} else if (pmem == (pdesc->addr + dlen)) {
+		pdesc->len  = dlen + nbytes;
+	} else {
+		pce_dev->ce_dm.ce_out_dst_desc_index++;
+		if (pce_dev->ce_dm.ce_out_dst_desc_index >= QCE_MAX_NUM_DESC)
+			return -EIO;
+		pdesc++;
+		pdesc->len = nbytes;
+		pdesc->addr = pmem;
+	}
+	return 0;
+};
+
+static void _chain_buffer_out_init(struct qce_device *pce_dev)
+{
+	struct dmov_desc *pdesc;
+
+	pce_dev->ce_dm.ce_out_dst_desc_index = 0;
+	pce_dev->ce_dm.ce_out_src_desc_index = 0;
+	pdesc = pce_dev->ce_dm.ce_out_dst_desc;
+	pdesc->len = 0;
+};
+
+static void _ce_out_final(struct qce_device *pce_dev, unsigned total)
+{
+	struct dmov_desc *pdesc;
+	dmov_sg *pcmd;
+
+	pdesc = pce_dev->ce_dm.ce_out_dst_desc +
+				pce_dev->ce_dm.ce_out_dst_desc_index;
+	pdesc->len |= ADM_DESC_LAST;
+
+	pdesc = pce_dev->ce_dm.ce_out_src_desc +
+				pce_dev->ce_dm.ce_out_src_desc_index;
+	if (total > QCE_FIFO_SIZE) {
+		qce_split_and_insert_dm_desc(pdesc, total, 0,
+				&pce_dev->ce_dm.ce_out_src_desc_index);
+		pdesc = pce_dev->ce_dm.ce_out_src_desc +
+				pce_dev->ce_dm.ce_out_src_desc_index;
+		pdesc->len |= ADM_DESC_LAST;
+	} else
+		pdesc->len = ADM_DESC_LAST | total;
+
+	pcmd = (dmov_sg *) pce_dev->ce_dm.cmdlist.ce_data_out;
+	pcmd->cmd |= CMD_LC;
+};
+
+static void _aead_ce_in_call_back(struct msm_dmov_cmd *cmd_ptr,
+		unsigned int result, struct msm_dmov_errdata *err)
+{
+	struct qce_device *pce_dev;
+
+	pce_dev = (struct qce_device *) cmd_ptr->user;
+	if (result != ADM_STATUS_OK) {
+		dev_err(pce_dev->pdev, "Qualcomm ADM status error %x\n",
+							result);
+		pce_dev->ce_dm.chan_ce_in_status = -1;
+	} else {
+		pce_dev->ce_dm.chan_ce_in_status = 0;
+	}
+
+	pce_dev->ce_dm.chan_ce_in_state = QCE_CHAN_STATE_COMP;
+	if (pce_dev->ce_dm.chan_ce_out_state == QCE_CHAN_STATE_COMP) {
+		pce_dev->ce_dm.chan_ce_in_state = QCE_CHAN_STATE_IDLE;
+		pce_dev->ce_dm.chan_ce_out_state = QCE_CHAN_STATE_IDLE;
+
+		/* done */
+		_aead_complete(pce_dev);
+	}
+};
+
+static void _aead_ce_out_call_back(struct msm_dmov_cmd *cmd_ptr,
+		unsigned int result, struct msm_dmov_errdata *err)
+{
+	struct qce_device *pce_dev;
+
+	pce_dev = (struct qce_device *) cmd_ptr->user;
+	if (result != ADM_STATUS_OK) {
+		dev_err(pce_dev->pdev, "Qualcomm ADM status error %x\n",
+							result);
+		pce_dev->ce_dm.chan_ce_out_status = -1;
+	} else {
+		pce_dev->ce_dm.chan_ce_out_status = 0;
+	};
+
+	pce_dev->ce_dm.chan_ce_out_state = QCE_CHAN_STATE_COMP;
+	if (pce_dev->ce_dm.chan_ce_in_state == QCE_CHAN_STATE_COMP) {
+		pce_dev->ce_dm.chan_ce_in_state = QCE_CHAN_STATE_IDLE;
+		pce_dev->ce_dm.chan_ce_out_state = QCE_CHAN_STATE_IDLE;
+
+		/* done */
+		_aead_complete(pce_dev);
+	}
+
+};
+
+static void _sha_ce_in_call_back(struct msm_dmov_cmd *cmd_ptr,
+		unsigned int result, struct msm_dmov_errdata *err)
+{
+	struct qce_device *pce_dev;
+
+	pce_dev = (struct qce_device *) cmd_ptr->user;
+	if (result != ADM_STATUS_OK) {
+		dev_err(pce_dev->pdev, "Qualcomm ADM status error %x\n",
+						result);
+		pce_dev->ce_dm.chan_ce_in_status = -1;
+	} else {
+		pce_dev->ce_dm.chan_ce_in_status = 0;
+	}
+	pce_dev->ce_dm.chan_ce_in_state = QCE_CHAN_STATE_IDLE;
+	_sha_complete(pce_dev);
+};
+
+static void _ablk_cipher_ce_in_call_back(struct msm_dmov_cmd *cmd_ptr,
+		unsigned int result, struct msm_dmov_errdata *err)
+{
+	struct qce_device *pce_dev;
+
+	pce_dev = (struct qce_device *) cmd_ptr->user;
+	if (result != ADM_STATUS_OK) {
+		dev_err(pce_dev->pdev, "Qualcomm ADM status error %x\n",
+						result);
+		pce_dev->ce_dm.chan_ce_in_status = -1;
+	} else {
+		pce_dev->ce_dm.chan_ce_in_status = 0;
+	}
+
+	pce_dev->ce_dm.chan_ce_in_state = QCE_CHAN_STATE_COMP;
+	if (pce_dev->ce_dm.chan_ce_out_state == QCE_CHAN_STATE_COMP) {
+		pce_dev->ce_dm.chan_ce_in_state = QCE_CHAN_STATE_IDLE;
+		pce_dev->ce_dm.chan_ce_out_state = QCE_CHAN_STATE_IDLE;
+
+		/* done */
+		_ablk_cipher_complete(pce_dev);
+	}
+};
+
+static void _ablk_cipher_ce_out_call_back(struct msm_dmov_cmd *cmd_ptr,
+		unsigned int result, struct msm_dmov_errdata *err)
+{
+	struct qce_device *pce_dev;
+
+	pce_dev = (struct qce_device *) cmd_ptr->user;
+
+	if (result != ADM_STATUS_OK) {
+		dev_err(pce_dev->pdev, "Qualcomm ADM status error %x\n",
+						result);
+		pce_dev->ce_dm.chan_ce_out_status = -1;
+	} else {
+		pce_dev->ce_dm.chan_ce_out_status = 0;
+	};
+
+	pce_dev->ce_dm.chan_ce_out_state = QCE_CHAN_STATE_COMP;
+	if (pce_dev->ce_dm.chan_ce_in_state == QCE_CHAN_STATE_COMP) {
+		pce_dev->ce_dm.chan_ce_in_state = QCE_CHAN_STATE_IDLE;
+		pce_dev->ce_dm.chan_ce_out_state = QCE_CHAN_STATE_IDLE;
+
+		/* done */
+		_ablk_cipher_complete(pce_dev);
+	}
+};
+
+
+static void _ablk_cipher_ce_in_call_back_pmem(struct msm_dmov_cmd *cmd_ptr,
+		unsigned int result, struct msm_dmov_errdata *err)
+{
+	struct qce_device *pce_dev;
+	pce_dev = (struct qce_device *) cmd_ptr->user;
+	if (result != ADM_STATUS_OK) {
+		dev_err(pce_dev->pdev, "Qualcomm ADM status error %x\n",
+						result);
+		pce_dev->ce_dm.chan_ce_in_status = -1;
+	} else {
+		pce_dev->ce_dm.chan_ce_in_status = 0;
+	}
+
+	pce_dev->ce_dm.chan_ce_in_state = QCE_CHAN_STATE_COMP;
+	if (pce_dev->ce_dm.chan_ce_out_state == QCE_CHAN_STATE_COMP) {
+		pce_dev->ce_dm.chan_ce_in_state = QCE_CHAN_STATE_IDLE;
+		pce_dev->ce_dm.chan_ce_out_state = QCE_CHAN_STATE_IDLE;
+
+		/* done */
+		_ablk_cipher_use_pmem_complete(pce_dev);
+	}
+};
+
+static void _ablk_cipher_ce_out_call_back_pmem(struct msm_dmov_cmd *cmd_ptr,
+		unsigned int result, struct msm_dmov_errdata *err)
+{
+	struct qce_device *pce_dev;
+
+	pce_dev = (struct qce_device *) cmd_ptr->user;
+	if (result != ADM_STATUS_OK) {
+		dev_err(pce_dev->pdev, "Qualcomm ADM status error %x\n",
+						result);
+		pce_dev->ce_dm.chan_ce_out_status = -1;
+	} else {
+		pce_dev->ce_dm.chan_ce_out_status = 0;
+	};
+
+	pce_dev->ce_dm.chan_ce_out_state = QCE_CHAN_STATE_COMP;
+	if (pce_dev->ce_dm.chan_ce_in_state == QCE_CHAN_STATE_COMP) {
+		pce_dev->ce_dm.chan_ce_in_state = QCE_CHAN_STATE_IDLE;
+		pce_dev->ce_dm.chan_ce_out_state = QCE_CHAN_STATE_IDLE;
+
+		/* done */
+		_ablk_cipher_use_pmem_complete(pce_dev);
+	}
+};
+
+static int qce_setup_cmd_buffers(struct qce_device *pce_dev,
+						unsigned char **pvaddr)
+{
+	struct ce_reg_buffers *addr = (struct ce_reg_buffers *)(*pvaddr);
+	struct ce_reg_buffer_addr *buffer = &pce_dev->ce_dm.buffer;
+
+	/*
+	 * Designate chunks of the allocated memory to various
+	 * buffer pointers
+	 */
+	buffer->reset_buf_64 = addr->reset_buf_64;
+	buffer->version = addr->version;
+	buffer->encr_seg_cfg_size_start = addr->encr_seg_cfg_size_start;
+	buffer->encr_key = addr->encr_key;
+	buffer->encr_xts_key = addr->encr_xts_key;
+	buffer->encr_xts_du_size = addr->encr_xts_du_size;
+	buffer->encr_cntr_iv = addr->encr_cntr_iv;
+	buffer->encr_mask = addr->encr_mask;
+	buffer->auth_seg_cfg_size_start = addr->auth_seg_cfg_size_start;
+	buffer->auth_key = addr->auth_key;
+	buffer->auth_iv = addr->auth_iv;
+	buffer->auth_result = addr->auth_result;
+	buffer->auth_nonce_info = addr->auth_nonce_info;
+	buffer->auth_byte_count = addr->auth_byte_count;
+	buffer->seg_size = addr->seg_size;
+	buffer->go_proc = addr->go_proc;
+	buffer->status = addr->status;
+	buffer->pad = addr->pad;
+
+	memset(buffer->reset_buf_64, 0, 64);
+	*((uint32_t *)buffer->encr_mask) = (uint32_t)(0xffffffff);
+	*((uint32_t *)buffer->go_proc) = (uint32_t)(1 << CRYPTO_GO);
+
+	*pvaddr += sizeof(struct ce_reg_buffers);
+
+	return 0;
+
+}
+
+static int _setup_cipher_cmdlists(struct qce_device *pce_dev,
+						unsigned char **pvaddr)
+{
+	dmov_s  *pscmd = (dmov_s  *)(*pvaddr);
+
+	/*
+	 * Designate chunks of the allocated memory to various
+	 * command list pointers related to cipher operation
+	 */
+	pce_dev->ce_dm.cmdlist.set_cipher_cfg = pscmd;
+	pscmd->cmd = CMD_LC | CMD_MODE_SINGLE;
+	pscmd->dst = (unsigned) (CRYPTO_ENCR_SEG_CFG_REG +
+					pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE * 3;
+	pscmd->src =
+		GET_PHYS_ADDR(pce_dev->ce_dm.buffer.encr_seg_cfg_size_start);
+	pscmd++;
+
+	pce_dev->ce_dm.cmdlist.set_cipher_aes_128_key = pscmd;
+	pscmd->cmd = CMD_LC | CMD_SRC_SWAP_BYTES |
+				CMD_SRC_SWAP_SHORTS | CMD_MODE_SINGLE;
+	pscmd->dst = (unsigned) (CRYPTO_ENCR_KEY0_REG + pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE * 4;
+	pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.encr_key);
+	pscmd++;
+
+	pce_dev->ce_dm.cmdlist.set_cipher_aes_256_key = pscmd;
+	pscmd->cmd = CMD_LC | CMD_SRC_SWAP_BYTES |
+				CMD_SRC_SWAP_SHORTS | CMD_MODE_SINGLE;
+	pscmd->dst = (unsigned) (CRYPTO_ENCR_KEY0_REG + pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE * 8;
+	pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.encr_key);
+	pscmd++;
+
+	pce_dev->ce_dm.cmdlist.set_cipher_des_key = pscmd;
+	pscmd->cmd = CMD_LC | CMD_SRC_SWAP_BYTES |
+				CMD_SRC_SWAP_SHORTS | CMD_MODE_SINGLE;
+	pscmd->dst = (unsigned) (CRYPTO_ENCR_KEY0_REG + pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE * 2;
+	pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.encr_key);
+	pscmd++;
+
+	pce_dev->ce_dm.cmdlist.set_cipher_3des_key = pscmd;
+	pscmd->cmd = CMD_LC | CMD_SRC_SWAP_BYTES |
+				CMD_SRC_SWAP_SHORTS | CMD_MODE_SINGLE;
+	pscmd->dst = (unsigned) (CRYPTO_ENCR_KEY0_REG + pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE * 6;
+	pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.encr_key);
+	pscmd++;
+
+	pce_dev->ce_dm.cmdlist.set_cipher_aes_128_xts_key = pscmd;
+	pscmd->cmd = CMD_LC | CMD_SRC_SWAP_BYTES |
+				CMD_SRC_SWAP_SHORTS | CMD_MODE_SINGLE;
+	pscmd->dst = (unsigned) (CRYPTO_ENCR_XTS_KEY0_REG +
+							pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE * 4;
+	pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.encr_xts_key);
+	pscmd++;
+
+	pce_dev->ce_dm.cmdlist.set_cipher_aes_256_xts_key = pscmd;
+	pscmd->cmd = CMD_LC | CMD_SRC_SWAP_BYTES |
+					CMD_SRC_SWAP_SHORTS | CMD_MODE_SINGLE;
+	pscmd->dst = (unsigned) (CRYPTO_ENCR_XTS_KEY0_REG +
+							pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE * 8;
+	pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.encr_xts_key);
+	pscmd++;
+
+	pce_dev->ce_dm.cmdlist.set_cipher_xts_du_size = pscmd;
+	pscmd->cmd = CMD_LC | CMD_MODE_SINGLE;
+	pscmd->dst = (unsigned) (CRYPTO_ENCR_XTS_DU_SIZE_REG +
+							pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE * 4;
+	pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.encr_xts_du_size);
+	pscmd++;
+
+	pce_dev->ce_dm.cmdlist.set_cipher_aes_iv = pscmd;
+	pscmd->cmd = CMD_LC | CMD_SRC_SWAP_BYTES |
+					CMD_SRC_SWAP_SHORTS | CMD_MODE_SINGLE;
+	pscmd->dst = (unsigned) (CRYPTO_CNTR0_IV0_REG + pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE * 4;
+	pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.encr_cntr_iv);
+	pscmd++;
+
+	pce_dev->ce_dm.cmdlist.set_cipher_des_iv = pscmd;
+	pscmd->cmd = CMD_LC | CMD_SRC_SWAP_BYTES |
+				CMD_SRC_SWAP_SHORTS | CMD_MODE_SINGLE;
+	pscmd->dst = (unsigned) (CRYPTO_CNTR0_IV0_REG + pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE * 2;
+	pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.encr_cntr_iv);
+	pscmd++;
+
+	pce_dev->ce_dm.cmdlist.get_cipher_iv = pscmd;
+	pscmd->cmd = CMD_LC | CMD_SRC_SWAP_BYTES |
+				CMD_SRC_SWAP_SHORTS | CMD_MODE_SINGLE;
+	pscmd->src = (unsigned) (CRYPTO_CNTR0_IV0_REG + pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE * 4;
+	pscmd->dst = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.encr_cntr_iv);
+	pscmd++;
+
+	pce_dev->ce_dm.cmdlist.set_cipher_mask = pscmd;
+	pscmd->cmd = CMD_LC | CMD_MODE_SINGLE;
+	pscmd->dst = (unsigned) (CRYPTO_CNTR_MASK_REG + pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE;
+	pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.encr_mask);
+	pscmd++;
+
+	/* RESET CIPHER AND AUTH REGISTERS COMMAND LISTS*/
+
+	pce_dev->ce_dm.cmdlist.reset_cipher_key  = pscmd;
+	pscmd->cmd = CMD_LC | CMD_MODE_SINGLE;
+	pscmd->dst = (unsigned) (CRYPTO_ENCR_KEY0_REG + pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE * 8;
+	pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.reset_buf_64);
+	pscmd++;
+
+	pce_dev->ce_dm.cmdlist.reset_cipher_xts_key  = pscmd;
+	pscmd->cmd = CMD_LC | CMD_MODE_SINGLE;
+	pscmd->dst = (unsigned) (CRYPTO_ENCR_XTS_KEY0_REG +
+							pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE * 8;
+	pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.reset_buf_64);
+	pscmd++;
+
+	pce_dev->ce_dm.cmdlist.reset_cipher_iv  = pscmd;
+	pscmd->cmd = CMD_LC | CMD_MODE_SINGLE;
+	pscmd->dst = (unsigned) (CRYPTO_CNTR0_IV0_REG + pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE * 4;
+	pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.reset_buf_64);
+	pscmd++;
+
+	pce_dev->ce_dm.cmdlist.reset_cipher_cfg  = pscmd;
+	pscmd->cmd = CMD_LC | CMD_MODE_SINGLE;
+	pscmd->dst = (unsigned) (CRYPTO_ENCR_SEG_CFG_REG + pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE;
+	pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.reset_buf_64);
+	pscmd++;
+
+	*pvaddr = (unsigned char *) pscmd;
+
+	return 0;
+}
+
+static int _setup_auth_cmdlists(struct qce_device *pce_dev,
+						unsigned char **pvaddr)
+{
+	dmov_s  *pscmd = (dmov_s  *)(*pvaddr);
+
+	/*
+	 * Designate chunks of the allocated memory to various
+	 * command list pointers related to authentication operation
+	 */
+	pce_dev->ce_dm.cmdlist.set_auth_cfg = pscmd;
+	pscmd->cmd = CMD_LC | CMD_MODE_SINGLE;
+	pscmd->dst = (unsigned) (CRYPTO_AUTH_SEG_CFG_REG + pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE * 3;
+	pscmd->src =
+		GET_PHYS_ADDR(pce_dev->ce_dm.buffer.auth_seg_cfg_size_start);
+	pscmd++;
+
+	pce_dev->ce_dm.cmdlist.set_auth_key_128 = pscmd;
+	pscmd->cmd = CMD_LC | CMD_SRC_SWAP_BYTES |
+				CMD_SRC_SWAP_SHORTS | CMD_MODE_SINGLE;
+	pscmd->dst = (unsigned) (CRYPTO_AUTH_KEY0_REG + pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE * 4;
+	pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.auth_key);
+	pscmd++;
+
+	pce_dev->ce_dm.cmdlist.set_auth_key_256 = pscmd;
+	pscmd->cmd = CMD_LC | CMD_SRC_SWAP_BYTES |
+				CMD_SRC_SWAP_SHORTS | CMD_MODE_SINGLE;
+	pscmd->dst = (unsigned) (CRYPTO_AUTH_KEY0_REG + pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE * 8;
+	pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.auth_key);
+	pscmd++;
+
+	pce_dev->ce_dm.cmdlist.set_auth_key_512 = pscmd;
+	pscmd->cmd = CMD_LC | CMD_SRC_SWAP_BYTES |
+				CMD_SRC_SWAP_SHORTS | CMD_MODE_SINGLE;
+	pscmd->dst = (unsigned) (CRYPTO_AUTH_KEY0_REG + pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE * 16;
+	pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.auth_key);
+	pscmd++;
+
+	pce_dev->ce_dm.cmdlist.set_auth_iv_16 = pscmd;
+	pscmd->cmd = CMD_LC | CMD_SRC_SWAP_BYTES |
+				CMD_SRC_SWAP_SHORTS | CMD_MODE_SINGLE;
+	pscmd->dst = (unsigned) (CRYPTO_AUTH_IV0_REG + pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE * 4;
+	pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.auth_iv);
+	pscmd++;
+
+	pce_dev->ce_dm.cmdlist.get_auth_result_16 = pscmd;
+	pscmd->cmd = CMD_LC | CMD_SRC_SWAP_BYTES |
+				CMD_SRC_SWAP_SHORTS | CMD_MODE_SINGLE;
+	pscmd->src = (unsigned) (CRYPTO_AUTH_IV0_REG + pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE * 4;
+	pscmd->dst = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.auth_result);
+	pscmd++;
+
+	pce_dev->ce_dm.cmdlist.set_auth_iv_20 = pscmd;
+	pscmd->cmd = CMD_LC | CMD_SRC_SWAP_BYTES |
+				CMD_SRC_SWAP_SHORTS | CMD_MODE_SINGLE;
+	pscmd->dst = (unsigned) (CRYPTO_AUTH_IV0_REG + pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE * 5;
+	pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.auth_iv);
+	pscmd++;
+
+	pce_dev->ce_dm.cmdlist.get_auth_result_20 = pscmd;
+	pscmd->cmd = CMD_LC | CMD_SRC_SWAP_BYTES |
+				CMD_SRC_SWAP_SHORTS | CMD_MODE_SINGLE;
+	pscmd->src = (unsigned) (CRYPTO_AUTH_IV0_REG + pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE * 5;
+	pscmd->dst = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.auth_result);
+	pscmd++;
+
+	pce_dev->ce_dm.cmdlist.set_auth_iv_32 = pscmd;
+	pscmd->cmd = CMD_LC | CMD_SRC_SWAP_BYTES |
+				CMD_SRC_SWAP_SHORTS | CMD_MODE_SINGLE;
+	pscmd->dst = (unsigned) (CRYPTO_AUTH_IV0_REG + pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE * 8;
+	pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.auth_iv);
+	pscmd++;
+
+
+	pce_dev->ce_dm.cmdlist.get_auth_result_32 = pscmd;
+	pscmd->cmd = CMD_LC | CMD_SRC_SWAP_BYTES |
+				CMD_SRC_SWAP_SHORTS | CMD_MODE_SINGLE;
+	pscmd->src = (unsigned) (CRYPTO_AUTH_IV0_REG + pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE * 8;
+	pscmd->dst = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.auth_result);
+	pscmd++;
+
+	pce_dev->ce_dm.cmdlist.set_auth_byte_count = pscmd;
+	pscmd->cmd = CMD_LC | CMD_MODE_SINGLE;
+	pscmd->dst = (unsigned) (CRYPTO_AUTH_BYTECNT0_REG +
+							pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE * 4;
+	pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.auth_byte_count);
+	pscmd++;
+
+	pce_dev->ce_dm.cmdlist.get_auth_byte_count = pscmd;
+	pscmd->cmd = CMD_LC | CMD_MODE_SINGLE;
+	pscmd->src = (unsigned) (CRYPTO_AUTH_BYTECNT0_REG +
+							pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE * 4;
+	pscmd->dst = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.auth_byte_count);
+	pscmd++;
+
+	pce_dev->ce_dm.cmdlist.set_auth_nonce_info = pscmd;
+	pscmd->cmd = CMD_LC | CMD_SRC_SWAP_BYTES |
+				CMD_SRC_SWAP_SHORTS | CMD_MODE_SINGLE;
+	pscmd->dst = (unsigned) (CRYPTO_AUTH_INFO_NONCE0_REG +
+							pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE * 4;
+	pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.auth_nonce_info);
+	pscmd++;
+
+	/* RESET CIPHER AND AUTH REGISTERS COMMAND LISTS*/
+
+	pce_dev->ce_dm.cmdlist.reset_auth_key = pscmd;
+	pscmd->cmd = CMD_LC | CMD_MODE_SINGLE;
+	pscmd->dst = (unsigned) (CRYPTO_AUTH_KEY0_REG + pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE * 16;
+	pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.reset_buf_64);
+	pscmd++;
+
+	pce_dev->ce_dm.cmdlist.reset_auth_iv = pscmd;
+	pscmd->cmd = CMD_LC | CMD_MODE_SINGLE;
+	pscmd->dst = (unsigned) (CRYPTO_AUTH_IV0_REG + pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE * 16;
+	pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.reset_buf_64);
+	pscmd++;
+
+	pce_dev->ce_dm.cmdlist.reset_auth_cfg = pscmd;
+	pscmd->cmd = CMD_LC | CMD_MODE_SINGLE;
+	pscmd->dst = (unsigned) (CRYPTO_AUTH_SEG_CFG_REG + pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE;
+	pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.reset_buf_64);
+	pscmd++;
+
+
+	pce_dev->ce_dm.cmdlist.reset_auth_byte_count = pscmd;
+	pscmd->cmd = CMD_LC | CMD_MODE_SINGLE;
+	pscmd->dst = (unsigned) (CRYPTO_AUTH_BYTECNT0_REG +
+							pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE * 4;
+	pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.reset_buf_64);
+	pscmd++;
+
+	/* WAIT UNTIL MAC OP IS DONE*/
+
+	pce_dev->ce_dm.cmdlist.get_status_wait = pscmd;
+	pscmd->cmd = CMD_LC | CMD_MODE_SINGLE;
+	pscmd->src = (unsigned) (CRYPTO_STATUS_REG + pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE;
+	pscmd->dst = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.status);
+	pscmd++;
+
+	*pvaddr = (unsigned char *) pscmd;
+
+	return 0;
+}
+
+static int qce_setup_cmdlists(struct qce_device *pce_dev,
+						unsigned char **pvaddr)
+{
+	dmov_sg *pcmd;
+	dmov_s  *pscmd;
+	unsigned char *vaddr = *pvaddr;
+	struct dmov_desc *pdesc;
+	int i = 0;
+
+	/*
+	 * Designate chunks of the allocated memory to various
+	 * command list pointers related to operation define
+	 * in ce_cmdlists structure.
+	 */
+	vaddr = (unsigned char *) ALIGN(((unsigned int)vaddr), 16);
+	*pvaddr = (unsigned char *) vaddr;
+
+	_setup_cipher_cmdlists(pce_dev, pvaddr);
+	_setup_auth_cmdlists(pce_dev, pvaddr);
+
+	pscmd = (dmov_s  *)(*pvaddr);
+
+	/* GET HW VERSION COMMAND LIST */
+	pce_dev->ce_dm.cmdlist.get_hw_version = pscmd;
+	pscmd->cmd = CMD_LC | CMD_MODE_SINGLE | CMD_OCB;
+	pscmd->src = (unsigned) (CRYPTO_VERSION_REG + pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE;
+	pscmd->dst = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.version);
+	pscmd++;
+
+
+	/* SET SEG SIZE REGISTER and OCB COMMAND LIST */
+	pce_dev->ce_dm.cmdlist.set_seg_size_ocb = pscmd;
+	pscmd->cmd = CMD_LC | CMD_MODE_SINGLE | CMD_OCB;
+	pscmd->dst = (unsigned) (CRYPTO_SEG_SIZE_REG + pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE;
+	pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.seg_size);
+	pscmd++;
+
+
+	/* OCU COMMAND LIST */
+	pce_dev->ce_dm.cmdlist.get_status_ocu = pscmd;
+	pscmd->cmd = CMD_LC | CMD_MODE_SINGLE | CMD_OCU;
+	pscmd->src = (unsigned) (CRYPTO_STATUS_REG + pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE;
+	pscmd->dst = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.status);
+	pscmd++;
+
+	/* CLEAR STATUS COMMAND LIST */
+	pce_dev->ce_dm.cmdlist.clear_status = pscmd;
+	pscmd->cmd = CMD_LC | CMD_MODE_SINGLE | CMD_OCU;
+	pscmd->dst = (unsigned) (CRYPTO_STATUS_REG + pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE;
+	pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.status);
+	pscmd++;
+
+	/* SET GO_PROC REGISTERS COMMAND LIST */
+	pce_dev->ce_dm.cmdlist.set_go_proc = pscmd;
+	pscmd->cmd = CMD_LC | CMD_MODE_SINGLE;
+	pscmd->dst = (unsigned) (CRYPTO_GOPROC_REG + pce_dev->phy_iobase);
+	pscmd->len = CRYPTO_REG_SIZE;
+	pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.go_proc);
+	pscmd++;
+
+	pcmd = (dmov_sg  *)pscmd;
+	pce_dev->ce_dm.cmdlist.ce_data_in = pcmd;
+	/* swap byte and half word , dst crci ,  scatter gather */
+	pcmd->cmd = CMD_DST_SWAP_BYTES | CMD_DST_SWAP_SHORTS |
+			CMD_DST_CRCI(pce_dev->ce_dm.crci_in) | CMD_MODE_SG;
+
+	pdesc = pce_dev->ce_dm.ce_in_src_desc;
+	pdesc->addr = 0;	/* to be filled in each operation */
+	pdesc->len = 0;		/* to be filled in each operation */
+
+	pdesc = pce_dev->ce_dm.ce_in_dst_desc;
+	for (i = 0; i < QCE_MAX_NUM_DESC; i++) {
+		pdesc->addr = (CRYPTO_DATA_SHADOW0 + pce_dev->phy_iobase);
+		pdesc->len = 0; /* to be filled in each operation */
+		pdesc++;
+	}
+	pcmd->src_dscr = GET_PHYS_ADDR(pce_dev->ce_dm.ce_in_src_desc);
+	pcmd->dst_dscr = GET_PHYS_ADDR(pce_dev->ce_dm.ce_in_dst_desc);
+	pcmd->_reserved = LI_SG_CMD | SRC_INDEX_SG_CMD(0) |
+						DST_INDEX_SG_CMD(0);
+
+
+	pcmd++;
+	pce_dev->ce_dm.cmdlist.ce_data_out = pcmd;
+	/* swap byte, half word, source crci, scatter gather */
+	pcmd->cmd =   CMD_SRC_SWAP_BYTES | CMD_SRC_SWAP_SHORTS |
+			CMD_SRC_CRCI(pce_dev->ce_dm.crci_out) | CMD_MODE_SG;
+
+	pdesc = pce_dev->ce_dm.ce_out_src_desc;
+	for (i = 0; i < QCE_MAX_NUM_DESC; i++) {
+		pdesc->addr = (CRYPTO_DATA_SHADOW0 + pce_dev->phy_iobase);
+		pdesc->len = 0;  /* to be filled in each operation */
+		pdesc++;
+	}
+
+	pdesc = pce_dev->ce_dm.ce_out_dst_desc;
+	pdesc->addr = 0;  /* to be filled in each operation */
+	pdesc->len = 0;   /* to be filled in each operation */
+
+	pcmd->src_dscr = GET_PHYS_ADDR(pce_dev->ce_dm.ce_out_src_desc);
+	pcmd->dst_dscr = GET_PHYS_ADDR(pce_dev->ce_dm.ce_out_dst_desc);
+	pcmd->_reserved = LI_SG_CMD | SRC_INDEX_SG_CMD(0) |
+						DST_INDEX_SG_CMD(0);
+	pcmd++;
+
+	*pvaddr = (unsigned char *) pcmd;
+
+	return 0;
+}
+
+static int _setup_cipher_cmdptrlists(struct qce_device *pce_dev,
+						unsigned char **pvaddr)
+{
+	uint32_t * cmd_ptr_vaddr = (uint32_t *)(*pvaddr);
+	struct ce_cmdlists *cmdlist = &pce_dev->ce_dm.cmdlist;
+	struct ce_cmdptrlists_ops *cmdptrlist = &pce_dev->ce_dm.cmdptrlist;
+
+	/*
+	 * Designate chunks of the allocated memory to various
+	 * command list pointers related to cipher operations defined
+	 * in ce_cmdptrlists_ops structure.
+	 */
+	cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
+	cmdptrlist->cipher_aes_128_cbc_ctr = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
+
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_128_key);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_iv);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_mask);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_go_proc);
+	*cmd_ptr_vaddr++ = QCE_SET_LAST_CMD_PTR(cmdlist->ce_data_in);
+
+	cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
+	cmdptrlist->cipher_aes_256_cbc_ctr = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
+
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_256_key);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_iv);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_mask);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_go_proc);
+	*cmd_ptr_vaddr++ = QCE_SET_LAST_CMD_PTR(cmdlist->ce_data_in);
+
+	cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
+	cmdptrlist->cipher_aes_128_ecb = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
+
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_128_key);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_mask);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_go_proc);
+	*cmd_ptr_vaddr++ = QCE_SET_LAST_CMD_PTR(cmdlist->ce_data_in);
+
+	cmd_ptr_vaddr = (uint32_t *)ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
+	cmdptrlist->cipher_aes_256_ecb = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
+
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_256_key);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_mask);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_go_proc);
+	*cmd_ptr_vaddr++ = QCE_SET_LAST_CMD_PTR(cmdlist->ce_data_in);
+
+	cmd_ptr_vaddr = (uint32_t *)ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
+	cmdptrlist->cipher_aes_128_xts = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
+
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_128_key);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_128_xts_key);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_iv);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_xts_du_size);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_mask);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_go_proc);
+	*cmd_ptr_vaddr++ = QCE_SET_LAST_CMD_PTR(cmdlist->ce_data_in);
+
+	cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
+	cmdptrlist->cipher_aes_256_xts = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
+
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_256_key);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_256_xts_key);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_iv);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_xts_du_size);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_mask);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_go_proc);
+	*cmd_ptr_vaddr++ = QCE_SET_LAST_CMD_PTR(cmdlist->ce_data_in);
+
+	cmd_ptr_vaddr = (uint32_t *)ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
+	cmdptrlist->cipher_des_cbc = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
+
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_des_key);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_des_iv);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_go_proc);
+	*cmd_ptr_vaddr++ = QCE_SET_LAST_CMD_PTR(cmdlist->ce_data_in);
+
+	cmd_ptr_vaddr = (uint32_t *)ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
+	cmdptrlist->cipher_des_ecb = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
+
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_des_key);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_go_proc);
+	*cmd_ptr_vaddr++ = QCE_SET_LAST_CMD_PTR(cmdlist->ce_data_in);
+
+	cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
+	cmdptrlist->cipher_3des_cbc = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
+
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_3des_key);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_des_iv);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_go_proc);
+	*cmd_ptr_vaddr++ = QCE_SET_LAST_CMD_PTR(cmdlist->ce_data_in);
+
+	cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
+	cmdptrlist->cipher_3des_ecb = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
+
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_3des_key);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_go_proc);
+	*cmd_ptr_vaddr++ = QCE_SET_LAST_CMD_PTR(cmdlist->ce_data_in);
+
+	cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
+	cmdptrlist->cipher_ce_out = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
+
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->ce_data_out);
+	*cmd_ptr_vaddr++ = QCE_SET_LAST_CMD_PTR(cmdlist->get_status_ocu);
+
+	cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
+	cmdptrlist->cipher_ce_out_get_iv = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
+
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->ce_data_out);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_cipher_iv);
+	*cmd_ptr_vaddr++ = QCE_SET_LAST_CMD_PTR(cmdlist->get_status_ocu);
+
+	*pvaddr = (unsigned char *) cmd_ptr_vaddr;
+
+	return 0;
+}
+
+static int _setup_auth_cmdptrlists(struct qce_device *pce_dev,
+						unsigned char **pvaddr)
+{
+	uint32_t * cmd_ptr_vaddr = (uint32_t *)(*pvaddr);
+	struct ce_cmdlists *cmdlist = &pce_dev->ce_dm.cmdlist;
+	struct ce_cmdptrlists_ops *cmdptrlist = &pce_dev->ce_dm.cmdptrlist;
+
+	/*
+	 * Designate chunks of the allocated memory to various
+	 * command list pointers related to authentication operations
+	 * defined in ce_cmdptrlists_ops structure.
+	 */
+	cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
+	cmdptrlist->auth_sha1 = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
+
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_cipher_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_iv_20);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_byte_count);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_go_proc);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->ce_data_in);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_status_wait);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_status_wait);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_auth_byte_count);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_auth_result_20);
+	*cmd_ptr_vaddr++ = QCE_SET_LAST_CMD_PTR(cmdlist->get_status_ocu);
+
+	cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
+	cmdptrlist->auth_sha256 = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
+
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_cipher_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_iv_32);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_byte_count);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_go_proc);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->ce_data_in);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_status_wait);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_status_wait);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_auth_byte_count);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_auth_result_32);
+	*cmd_ptr_vaddr++ = QCE_SET_LAST_CMD_PTR(cmdlist->get_status_ocu);
+
+	cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
+	cmdptrlist->auth_sha1_hmac = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
+
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_cipher_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_key_512);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_iv_20);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_byte_count);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_go_proc);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->ce_data_in);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_status_wait);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_status_wait);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_auth_byte_count);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_auth_result_20);
+	*cmd_ptr_vaddr++ = QCE_SET_LAST_CMD_PTR(cmdlist->get_status_ocu);
+
+	cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
+	cmdptrlist->auth_sha256_hmac = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
+
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_cipher_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_key_512);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_iv_32);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_byte_count);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_go_proc);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->ce_data_in);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_status_wait);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_status_wait);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_auth_byte_count);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_auth_result_32);
+	*cmd_ptr_vaddr++ = QCE_SET_LAST_CMD_PTR(cmdlist->get_status_ocu);
+
+	cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
+	cmdptrlist->auth_aes_128_cmac = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
+
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_cipher_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_iv);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_key);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_byte_count);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_key_128);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_go_proc);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->ce_data_in);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_status_wait);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_status_wait);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_auth_byte_count);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_auth_result_16);
+	*cmd_ptr_vaddr++ = QCE_SET_LAST_CMD_PTR(cmdlist->get_status_ocu);
+
+	cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
+	cmdptrlist->auth_aes_256_cmac = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
+
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_cipher_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_iv);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_key);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_byte_count);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_key_256);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_go_proc);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->ce_data_in);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_status_wait);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_status_wait);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_auth_byte_count);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_auth_result_16);
+	*cmd_ptr_vaddr++ = QCE_SET_LAST_CMD_PTR(cmdlist->get_status_ocu);
+
+	*pvaddr = (unsigned char *) cmd_ptr_vaddr;
+
+	return 0;
+}
+
+static int _setup_aead_cmdptrlists(struct qce_device *pce_dev,
+						unsigned char **pvaddr)
+{
+	uint32_t * cmd_ptr_vaddr = (uint32_t *)(*pvaddr);
+	struct ce_cmdlists *cmdlist = &pce_dev->ce_dm.cmdlist;
+	struct ce_cmdptrlists_ops *cmdptrlist = &pce_dev->ce_dm.cmdptrlist;
+
+	/*
+	 * Designate chunks of the allocated memory to various
+	 * command list pointers related to aead operations
+	 * defined in ce_cmdptrlists_ops structure.
+	 */
+	cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
+	cmdptrlist->aead_aes_128_ccm = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
+
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_iv);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_key);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_byte_count);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_key_128);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_nonce_info);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_128_key);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_iv);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_mask);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_go_proc);
+	*cmd_ptr_vaddr++ = QCE_SET_LAST_CMD_PTR(cmdlist->ce_data_in);
+
+	cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
+	cmdptrlist->aead_aes_256_ccm = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
+
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_iv);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_key);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_byte_count);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_key_256);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_nonce_info);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_cfg);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_256_key);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_iv);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_mask);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_go_proc);
+	*cmd_ptr_vaddr++ = QCE_SET_LAST_CMD_PTR(cmdlist->ce_data_in);
+
+	cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
+	cmdptrlist->aead_ce_out = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
+
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->ce_data_out);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_status_wait);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_status_wait);
+	*cmd_ptr_vaddr++ = QCE_SET_LAST_CMD_PTR(cmdlist->get_status_ocu);
+
+	*pvaddr = (unsigned char *) cmd_ptr_vaddr;
+
+	return 0;
+}
+
+static int qce_setup_cmdptrlists(struct qce_device *pce_dev,
+					unsigned char **pvaddr)
+{
+	uint32_t * cmd_ptr_vaddr = (uint32_t *)(*pvaddr);
+	struct ce_cmdlists *cmdlist = &pce_dev->ce_dm.cmdlist;
+	struct ce_cmdptrlists_ops *cmdptrlist = &pce_dev->ce_dm.cmdptrlist;
+	/*
+	 * Designate chunks of the allocated memory to various
+	 * command list pointers related to operations defined
+	 * in ce_cmdptrlists_ops structure.
+	 */
+	cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
+	cmdptrlist->probe_ce_hw = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
+
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_hw_version);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status);
+	*cmd_ptr_vaddr++ = QCE_SET_LAST_CMD_PTR(cmdlist->get_status_ocu);
+
+	*pvaddr = (unsigned char *) cmd_ptr_vaddr;
+
+	_setup_cipher_cmdptrlists(pce_dev, pvaddr);
+	_setup_auth_cmdptrlists(pce_dev, pvaddr);
+	_setup_aead_cmdptrlists(pce_dev, pvaddr);
+
+	return 0;
+}
+
+
+static int qce_setup_ce_dm_data(struct qce_device *pce_dev)
+{
+	unsigned char *vaddr;
+
+	/* 1. ce_in channel data xfer command src descriptors, 128 entries */
+	vaddr = pce_dev->coh_vmem;
+	vaddr = (unsigned char *) ALIGN(((unsigned int)vaddr),  16);
+	pce_dev->ce_dm.ce_in_src_desc = (struct dmov_desc *) vaddr;
+	vaddr = vaddr + (sizeof(struct dmov_desc) * QCE_MAX_NUM_DESC);
+
+	/* 2. ce_in channel data xfer command dst descriptors, 128 entries */
+	vaddr = (unsigned char *) ALIGN(((unsigned int)vaddr), 16);
+	pce_dev->ce_dm.ce_in_dst_desc = (struct dmov_desc *) vaddr;
+	vaddr = vaddr + (sizeof(struct dmov_desc) * QCE_MAX_NUM_DESC);
+
+
+	/* 3. ce_out channel data xfer command src descriptors, 128 entries */
+	vaddr = (unsigned char *) ALIGN(((unsigned int)vaddr), 16);
+	pce_dev->ce_dm.ce_out_src_desc = (struct dmov_desc *) vaddr;
+	vaddr = vaddr + (sizeof(struct dmov_desc) * QCE_MAX_NUM_DESC);
+
+	/* 4. ce_out channel data xfer command dst descriptors, 128 entries. */
+	vaddr = (unsigned char *) ALIGN(((unsigned int)vaddr), 16);
+	pce_dev->ce_dm.ce_out_dst_desc = (struct dmov_desc *) vaddr;
+	vaddr = vaddr + (sizeof(struct dmov_desc) * QCE_MAX_NUM_DESC);
+
+	qce_setup_cmd_buffers(pce_dev, &vaddr);
+	qce_setup_cmdlists(pce_dev, &vaddr);
+	qce_setup_cmdptrlists(pce_dev, &vaddr);
+
+	pce_dev->ce_dm.buffer.ignore_data = vaddr;
+
+	pce_dev->ce_dm.phy_ce_pad = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.pad);
+	pce_dev->ce_dm.phy_ce_out_ignore  =
+			GET_PHYS_ADDR(pce_dev->ce_dm.buffer.ignore_data);
+
+	pce_dev->ce_dm.chan_ce_in_cmd->user = (void *) pce_dev;
+	pce_dev->ce_dm.chan_ce_in_cmd->exec_func = NULL;
+
+	pce_dev->ce_dm.chan_ce_out_cmd->user = (void *) pce_dev;
+	pce_dev->ce_dm.chan_ce_out_cmd->exec_func = NULL;
+
+	return 0;
+}
+
+static int _qce_start_dma(struct qce_device *pce_dev, bool ce_in, bool ce_out)
+{
+
+	if (ce_in)
+		pce_dev->ce_dm.chan_ce_in_state = QCE_CHAN_STATE_IN_PROG;
+	else
+		pce_dev->ce_dm.chan_ce_in_state = QCE_CHAN_STATE_COMP;
+
+	if (ce_out)
+		pce_dev->ce_dm.chan_ce_out_state = QCE_CHAN_STATE_IN_PROG;
+	else
+		pce_dev->ce_dm.chan_ce_out_state = QCE_CHAN_STATE_COMP;
+
+	if (ce_in)
+		msm_dmov_enqueue_cmd(pce_dev->ce_dm.chan_ce_in,
+					pce_dev->ce_dm.chan_ce_in_cmd);
+	if (ce_out)
+		msm_dmov_enqueue_cmd(pce_dev->ce_dm.chan_ce_out,
+					pce_dev->ce_dm.chan_ce_out_cmd);
+
+	return 0;
+};
+
+int qce_aead_req(void *handle, struct qce_req *q_req)
+{
+	struct qce_device *pce_dev = (struct qce_device *) handle;
+	struct aead_request *areq = (struct aead_request *) q_req->areq;
+	uint32_t authsize = q_req->authsize;
+	uint32_t totallen_in, totallen_out, out_len;
+	uint32_t pad_len_in, pad_len_out;
+	int rc = 0;
+	int ce_block_size;
+
+	ce_block_size = pce_dev->ce_dm.ce_block_size;
+	if (q_req->dir == QCE_ENCRYPT) {
+		uint32_t pad_mac_len_out;
+
+		q_req->cryptlen = areq->cryptlen;
+		totallen_in = q_req->cryptlen + areq->assoclen;
+		pad_len_in = ALIGN(totallen_in, ce_block_size) - totallen_in;
+
+		out_len = areq->cryptlen + authsize;
+		totallen_out = q_req->cryptlen + authsize + areq->assoclen;
+		pad_mac_len_out = ALIGN(authsize, ce_block_size) - authsize;
+		totallen_out += pad_mac_len_out;
+		pad_len_out = ALIGN(totallen_out, ce_block_size) -
+					totallen_out + pad_mac_len_out;
+
+	} else {
+		q_req->cryptlen = areq->cryptlen - authsize;
+		totallen_in = areq->cryptlen + areq->assoclen;
+		pad_len_in = ALIGN(totallen_in, ce_block_size) - totallen_in;
+
+		out_len = q_req->cryptlen;
+		totallen_out = totallen_in;
+		pad_len_out = ALIGN(totallen_out, ce_block_size) - totallen_out;
+		pad_len_out += authsize;
+	}
+
+	_chain_buffer_in_init(pce_dev);
+	_chain_buffer_out_init(pce_dev);
+
+	pce_dev->assoc_nents = 0;
+	pce_dev->src_nents = 0;
+	pce_dev->dst_nents = 0;
+	pce_dev->ivsize = q_req->ivsize;
+	pce_dev->authsize = q_req->authsize;
+
+	/* associated data input */
+	pce_dev->assoc_nents = count_sg(areq->assoc, areq->assoclen);
+	dma_map_sg(pce_dev->pdev, areq->assoc, pce_dev->assoc_nents,
+					 DMA_TO_DEVICE);
+	if (_chain_sg_buffer_in(pce_dev, areq->assoc, areq->assoclen) < 0) {
+		rc = -ENOMEM;
+		goto bad;
+	}
+	/* cipher input */
+	pce_dev->src_nents = count_sg(areq->src, areq->cryptlen);
+	dma_map_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
+			(areq->src == areq->dst) ? DMA_BIDIRECTIONAL :
+							DMA_TO_DEVICE);
+	if (_chain_sg_buffer_in(pce_dev, areq->src, areq->cryptlen) < 0) {
+		rc = -ENOMEM;
+		goto bad;
+	}
+	/* pad data in */
+	if (pad_len_in) {
+		if (_chain_pm_buffer_in(pce_dev, pce_dev->ce_dm.phy_ce_pad,
+						pad_len_in) < 0) {
+			rc = -ENOMEM;
+			goto bad;
+		}
+	}
+
+	/* ignore associated data */
+	if (_chain_pm_buffer_out(pce_dev, pce_dev->ce_dm.phy_ce_out_ignore,
+				areq->assoclen) < 0) {
+		rc = -ENOMEM;
+		goto bad;
+	}
+	/* cipher + mac output  for encryption    */
+	if (areq->src != areq->dst) {
+		pce_dev->dst_nents = count_sg(areq->dst, out_len);
+		dma_map_sg(pce_dev->pdev, areq->dst, pce_dev->dst_nents,
+				DMA_FROM_DEVICE);
+	};
+	if (_chain_sg_buffer_out(pce_dev, areq->dst, out_len) < 0) {
+		rc = -ENOMEM;
+		goto bad;
+	}
+	/* pad data out */
+	if (pad_len_out) {
+		if (_chain_pm_buffer_out(pce_dev, pce_dev->ce_dm.phy_ce_pad,
+						pad_len_out) < 0) {
+			rc = -ENOMEM;
+			goto bad;
+		}
+	}
+
+	/* finalize the ce_in and ce_out channels command lists */
+	_ce_in_final(pce_dev, ALIGN(totallen_in, ce_block_size));
+	_ce_out_final(pce_dev, ALIGN(totallen_out, ce_block_size));
+
+	/* set up crypto device */
+	rc = _ce_setup_cipher(pce_dev, q_req, totallen_in, areq->assoclen);
+	if (rc < 0)
+		goto bad;
+
+	/* setup for callback, and issue command to adm */
+	pce_dev->areq = q_req->areq;
+	pce_dev->qce_cb = q_req->qce_cb;
+
+	pce_dev->ce_dm.chan_ce_in_cmd->complete_func = _aead_ce_in_call_back;
+	pce_dev->ce_dm.chan_ce_out_cmd->complete_func = _aead_ce_out_call_back;
+
+	_ce_in_dump(pce_dev);
+	_ce_out_dump(pce_dev);
+
+	rc = _qce_start_dma(pce_dev, true, true);
+	if (rc == 0)
+		return 0;
+bad:
+	if (pce_dev->assoc_nents) {
+		dma_unmap_sg(pce_dev->pdev, areq->assoc, pce_dev->assoc_nents,
+				DMA_TO_DEVICE);
+	}
+
+	if (pce_dev->src_nents) {
+		dma_unmap_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
+				(areq->src == areq->dst) ? DMA_BIDIRECTIONAL :
+								DMA_TO_DEVICE);
+	}
+	if (pce_dev->dst_nents) {
+		dma_unmap_sg(pce_dev->pdev, areq->dst, pce_dev->dst_nents,
+				DMA_FROM_DEVICE);
+	}
+	return rc;
+}
+EXPORT_SYMBOL(qce_aead_req);
+
+int qce_ablk_cipher_req(void *handle, struct qce_req *c_req)
+{
+	int rc = 0;
+	struct qce_device *pce_dev = (struct qce_device *) handle;
+	struct ablkcipher_request *areq = (struct ablkcipher_request *)
+						c_req->areq;
+
+	uint32_t pad_len = ALIGN(areq->nbytes, pce_dev->ce_dm.ce_block_size)
+						- areq->nbytes;
+
+	_chain_buffer_in_init(pce_dev);
+	_chain_buffer_out_init(pce_dev);
+
+	pce_dev->src_nents = 0;
+	pce_dev->dst_nents = 0;
+
+	/* cipher input */
+	pce_dev->src_nents = count_sg(areq->src, areq->nbytes);
+
+	if (c_req->use_pmem != 1)
+		dma_map_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
+			(areq->src == areq->dst) ? DMA_BIDIRECTIONAL :
+								DMA_TO_DEVICE);
+	else
+		dma_map_pmem_sg(&c_req->pmem->src[0], pce_dev->src_nents,
+								areq->src);
+
+	if (_chain_sg_buffer_in(pce_dev, areq->src, areq->nbytes) < 0) {
+		rc = -ENOMEM;
+		goto bad;
+	}
+
+	/* cipher output */
+	if (areq->src != areq->dst) {
+		pce_dev->dst_nents = count_sg(areq->dst, areq->nbytes);
+		if (c_req->use_pmem != 1)
+			dma_map_sg(pce_dev->pdev, areq->dst, pce_dev->dst_nents,
+							DMA_FROM_DEVICE);
+		else
+			dma_map_pmem_sg(&c_req->pmem->dst[0],
+					pce_dev->dst_nents, areq->dst);
+	};
+	if (_chain_sg_buffer_out(pce_dev, areq->dst, areq->nbytes) < 0) {
+		rc = -ENOMEM;
+		goto bad;
+	}
+
+	/* pad data */
+	if (pad_len) {
+		if (_chain_pm_buffer_in(pce_dev, pce_dev->ce_dm.phy_ce_pad,
+						pad_len) < 0) {
+			rc = -ENOMEM;
+			goto bad;
+		}
+		if (_chain_pm_buffer_out(pce_dev, pce_dev->ce_dm.phy_ce_pad,
+						pad_len) < 0) {
+			rc = -ENOMEM;
+			goto bad;
+		}
+	}
+
+	/* finalize the ce_in and ce_out channels command lists */
+	_ce_in_final(pce_dev, areq->nbytes + pad_len);
+	_ce_out_final(pce_dev, areq->nbytes + pad_len);
+
+	_ce_in_dump(pce_dev);
+	_ce_out_dump(pce_dev);
+
+	/* set up crypto device */
+	rc = _ce_setup_cipher(pce_dev, c_req, areq->nbytes, 0);
+	if (rc < 0)
+		goto bad;
+
+	/* setup for callback, and issue command to adm */
+	pce_dev->areq = areq;
+	pce_dev->qce_cb = c_req->qce_cb;
+	if (c_req->use_pmem == 1) {
+		pce_dev->ce_dm.chan_ce_in_cmd->complete_func =
+					_ablk_cipher_ce_in_call_back_pmem;
+		pce_dev->ce_dm.chan_ce_out_cmd->complete_func =
+					_ablk_cipher_ce_out_call_back_pmem;
+	} else {
+		pce_dev->ce_dm.chan_ce_in_cmd->complete_func =
+					_ablk_cipher_ce_in_call_back;
+		pce_dev->ce_dm.chan_ce_out_cmd->complete_func =
+					_ablk_cipher_ce_out_call_back;
+	}
+	rc = _qce_start_dma(pce_dev, true, true);
+
+	if (rc == 0)
+		return 0;
+bad:
+	if (c_req->use_pmem != 1) {
+			if (pce_dev->dst_nents) {
+				dma_unmap_sg(pce_dev->pdev, areq->dst,
+				pce_dev->dst_nents, DMA_FROM_DEVICE);
+			}
+		if (pce_dev->src_nents) {
+			dma_unmap_sg(pce_dev->pdev, areq->src,
+					pce_dev->src_nents,
+					(areq->src == areq->dst) ?
+						DMA_BIDIRECTIONAL :
+						DMA_TO_DEVICE);
+		}
+	}
+	return rc;
+}
+EXPORT_SYMBOL(qce_ablk_cipher_req);
+
+int qce_process_sha_req(void *handle, struct qce_sha_req *sreq)
+{
+	struct qce_device *pce_dev = (struct qce_device *) handle;
+	int rc;
+	uint32_t pad_len = ALIGN(sreq->size, pce_dev->ce_dm.ce_block_size) -
+								sreq->size;
+	struct ahash_request *areq = (struct ahash_request *)sreq->areq;
+
+	_chain_buffer_in_init(pce_dev);
+	pce_dev->src_nents = count_sg(sreq->src, sreq->size);
+	dma_map_sg(pce_dev->pdev, sreq->src, pce_dev->src_nents,
+							DMA_TO_DEVICE);
+
+	if (_chain_sg_buffer_in(pce_dev, sreq->src, sreq->size) < 0) {
+		rc = -ENOMEM;
+		goto bad;
+	}
+
+	if (pad_len) {
+		if (_chain_pm_buffer_in(pce_dev, pce_dev->ce_dm.phy_ce_pad,
+						pad_len) < 0) {
+				rc = -ENOMEM;
+				goto bad;
+			}
+	}
+	 _ce_in_final(pce_dev, sreq->size + pad_len);
+
+	_ce_in_dump(pce_dev);
+
+	rc = _ce_setup_hash(pce_dev, sreq);
+
+	if (rc < 0)
+		goto bad;
+
+	pce_dev->areq = areq;
+	pce_dev->qce_cb = sreq->qce_cb;
+	pce_dev->ce_dm.chan_ce_in_cmd->complete_func = _sha_ce_in_call_back;
+
+	rc =  _qce_start_dma(pce_dev, true, false);
+
+	if (rc == 0)
+		return 0;
+bad:
+	if (pce_dev->src_nents) {
+		dma_unmap_sg(pce_dev->pdev, sreq->src,
+				pce_dev->src_nents, DMA_TO_DEVICE);
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(qce_process_sha_req);
+
+/* crypto engine open function. */
+void *qce_open(struct platform_device *pdev, int *rc)
+{
+	struct qce_device *pce_dev;
+	struct resource *resource;
+	struct clk *ce_core_clk;
+	struct clk *ce_clk;
+	struct clk *ce_core_src_clk;
+	int ret = 0;
+
+	pce_dev = kzalloc(sizeof(struct qce_device), GFP_KERNEL);
+	if (!pce_dev) {
+		*rc = -ENOMEM;
+		dev_err(&pdev->dev, "Can not allocate memory\n");
+		return NULL;
+	}
+	pce_dev->pdev = &pdev->dev;
+
+	resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!resource) {
+		*rc = -ENXIO;
+		dev_err(pce_dev->pdev, "Missing MEM resource\n");
+		goto err_pce_dev;
+	};
+	pce_dev->phy_iobase = resource->start;
+	pce_dev->iobase = ioremap_nocache(resource->start,
+				resource->end - resource->start + 1);
+	if (!pce_dev->iobase) {
+		*rc = -ENOMEM;
+		dev_err(pce_dev->pdev, "Can not map io memory\n");
+		goto err_pce_dev;
+	}
+
+	pce_dev->ce_dm.chan_ce_in_cmd = kzalloc(sizeof(struct msm_dmov_cmd),
+			GFP_KERNEL);
+	pce_dev->ce_dm.chan_ce_out_cmd = kzalloc(sizeof(struct msm_dmov_cmd),
+			GFP_KERNEL);
+	if (pce_dev->ce_dm.chan_ce_in_cmd == NULL ||
+			pce_dev->ce_dm.chan_ce_out_cmd == NULL) {
+		dev_err(pce_dev->pdev, "Can not allocate memory\n");
+		*rc = -ENOMEM;
+		goto err_dm_chan_cmd;
+	}
+
+	resource = platform_get_resource_byname(pdev, IORESOURCE_DMA,
+					"crypto_channels");
+	if (!resource) {
+		*rc = -ENXIO;
+		dev_err(pce_dev->pdev, "Missing DMA channel resource\n");
+		goto err_dm_chan_cmd;
+	};
+	pce_dev->ce_dm.chan_ce_in = resource->start;
+	pce_dev->ce_dm.chan_ce_out = resource->end;
+	resource = platform_get_resource_byname(pdev, IORESOURCE_DMA,
+				"crypto_crci_in");
+	if (!resource) {
+		*rc = -ENXIO;
+		dev_err(pce_dev->pdev, "Missing DMA crci in resource\n");
+		goto err_dm_chan_cmd;
+	};
+	pce_dev->ce_dm.crci_in = resource->start;
+	resource = platform_get_resource_byname(pdev, IORESOURCE_DMA,
+				"crypto_crci_out");
+	if (!resource) {
+		*rc = -ENXIO;
+		dev_err(pce_dev->pdev, "Missing DMA crci out resource\n");
+		goto err_dm_chan_cmd;
+	};
+	pce_dev->ce_dm.crci_out = resource->start;
+	pce_dev->memsize = 2 * PAGE_SIZE;
+	pce_dev->coh_vmem = dma_alloc_coherent(pce_dev->pdev,
+			pce_dev->memsize, &pce_dev->coh_pmem, GFP_KERNEL);
+
+	if (pce_dev->coh_vmem == NULL) {
+		*rc = -ENOMEM;
+		dev_err(pce_dev->pdev, "Can not allocate coherent memory.\n");
+		goto err;
+	}
+
+	/* Get CE3 src core clk. */
+	ce_core_src_clk = clk_get(pce_dev->pdev, "ce3_core_src_clk");
+	if (!IS_ERR(ce_core_src_clk)) {
+		pce_dev->ce_core_src_clk = ce_core_src_clk;
+
+		/* Set the core src clk @100Mhz */
+		ret = clk_set_rate(pce_dev->ce_core_src_clk, 100000000);
+		if (ret) {
+			clk_put(pce_dev->ce_core_src_clk);
+			goto err;
+		}
+	} else
+		pce_dev->ce_core_src_clk = NULL;
+
+	/* Get CE core clk */
+	ce_core_clk = clk_get(pce_dev->pdev, "core_clk");
+	if (IS_ERR(ce_core_clk)) {
+		*rc = PTR_ERR(ce_core_clk);
+		if (pce_dev->ce_core_src_clk != NULL)
+			clk_put(pce_dev->ce_core_src_clk);
+		goto err;
+	}
+	pce_dev->ce_core_clk = ce_core_clk;
+	/* Get CE clk */
+	ce_clk = clk_get(pce_dev->pdev, "iface_clk");
+	if (IS_ERR(ce_clk)) {
+		*rc = PTR_ERR(ce_clk);
+		if (pce_dev->ce_core_src_clk != NULL)
+			clk_put(pce_dev->ce_core_src_clk);
+		clk_put(pce_dev->ce_core_clk);
+		goto err;
+	}
+	pce_dev->ce_clk = ce_clk;
+
+	/* Enable CE core clk */
+	*rc = clk_prepare_enable(pce_dev->ce_core_clk);
+	if (*rc) {
+		if (pce_dev->ce_core_src_clk != NULL)
+			clk_put(pce_dev->ce_core_src_clk);
+		clk_put(pce_dev->ce_core_clk);
+		clk_put(pce_dev->ce_clk);
+		goto err;
+	} else {
+		/* Enable CE clk */
+		*rc = clk_prepare_enable(pce_dev->ce_clk);
+		if (*rc) {
+			clk_disable_unprepare(pce_dev->ce_core_clk);
+			if (pce_dev->ce_core_src_clk != NULL)
+				clk_put(pce_dev->ce_core_src_clk);
+			clk_put(pce_dev->ce_core_clk);
+			clk_put(pce_dev->ce_clk);
+			goto err;
+
+		}
+	}
+	qce_setup_ce_dm_data(pce_dev);
+
+	pce_dev->ce_dm.chan_ce_in_state = QCE_CHAN_STATE_IDLE;
+	pce_dev->ce_dm.chan_ce_out_state = QCE_CHAN_STATE_IDLE;
+	if (_init_ce_engine(pce_dev)) {
+		*rc = -ENXIO;
+		goto err;
+	}
+	*rc = 0;
+	return pce_dev;
+
+err:
+	if (pce_dev->coh_vmem)
+		dma_free_coherent(pce_dev->pdev, pce_dev->memsize,
+			pce_dev->coh_vmem, pce_dev->coh_pmem);
+err_dm_chan_cmd:
+	kfree(pce_dev->ce_dm.chan_ce_in_cmd);
+	kfree(pce_dev->ce_dm.chan_ce_out_cmd);
+	if (pce_dev->iobase)
+		iounmap(pce_dev->iobase);
+
+err_pce_dev:
+
+	kfree(pce_dev);
+
+	return NULL;
+}
+EXPORT_SYMBOL(qce_open);
+
+/* crypto engine close function. */
+int qce_close(void *handle)
+{
+	struct qce_device *pce_dev = (struct qce_device *) handle;
+
+	if (handle == NULL)
+		return -ENODEV;
+	if (pce_dev->iobase)
+		iounmap(pce_dev->iobase);
+
+	if (pce_dev->coh_vmem)
+		dma_free_coherent(pce_dev->pdev, pce_dev->memsize,
+				pce_dev->coh_vmem, pce_dev->coh_pmem);
+	clk_disable_unprepare(pce_dev->ce_clk);
+	clk_disable_unprepare(pce_dev->ce_core_clk);
+
+	if (pce_dev->ce_core_src_clk != NULL)
+		clk_put(pce_dev->ce_core_src_clk);
+
+	clk_put(pce_dev->ce_clk);
+	clk_put(pce_dev->ce_core_clk);
+
+	kfree(pce_dev->ce_dm.chan_ce_in_cmd);
+	kfree(pce_dev->ce_dm.chan_ce_out_cmd);
+	kfree(handle);
+
+	return 0;
+}
+EXPORT_SYMBOL(qce_close);
+
+int qce_hw_support(void *handle, struct ce_hw_support *ce_support)
+{
+	if (ce_support == NULL)
+		return -EINVAL;
+
+	ce_support->sha1_hmac_20 = false;
+	ce_support->sha1_hmac = false;
+	ce_support->sha256_hmac = false;
+	ce_support->sha_hmac = false;
+	ce_support->cmac  = true;
+	ce_support->aes_key_192 = false;
+	ce_support->aes_xts = true;
+	ce_support->aes_ccm = true;
+	ce_support->ota = false;
+	return 0;
+}
+EXPORT_SYMBOL(qce_hw_support);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Mona Hossain <mhossain@codeaurora.org>");
+MODULE_DESCRIPTION("Crypto Engine driver");
+MODULE_VERSION("2.17");
diff --git a/drivers/crypto/msm/qce40.h b/drivers/crypto/msm/qce40.h
new file mode 100644
index 0000000..809ba7f
--- /dev/null
+++ b/drivers/crypto/msm/qce40.h
@@ -0,0 +1,240 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _DRIVERS_CRYPTO_MSM_QCE40_H_
+#define _DRIVERS_CRYPTO_MSM_QCE40_H_
+
+
+#define GET_VIRT_ADDR(x)  \
+		((uint32_t)pce_dev->coh_vmem +			\
+		((uint32_t)x - pce_dev->coh_pmem))
+#define GET_PHYS_ADDR(x)  \
+		(pce_dev->coh_pmem + ((unsigned char *)x -	\
+		pce_dev->coh_vmem))
+
+/* Sets the adddress of a command list in command pointer list */
+#define QCE_SET_CMD_PTR(x)  \
+		(uint32_t)(DMOV_CMD_ADDR(GET_PHYS_ADDR((unsigned char *)x)))
+
+/* Sets the adddress of the last command list in command pointer list */
+#define SET_LAST_CMD_PTR(x) \
+		((DMOV_CMD_ADDR(x)) | CMD_PTR_LP)
+
+/* Get the adddress of the last command list in command pointer list */
+#define QCE_SET_LAST_CMD_PTR(x) \
+		SET_LAST_CMD_PTR((GET_PHYS_ADDR((unsigned char *)x)))
+
+
+/* MAX Data xfer block size between DM and CE */
+#define MAX_ADM_CE_BLOCK_SIZE  64
+#define ADM_DESC_LENGTH_MASK 0xffff
+#define ADM_DESC_LENGTH(x)  (x & ADM_DESC_LENGTH_MASK)
+
+#define ADM_STATUS_OK 0x80000002
+
+/* QCE max number of descriptor in a descriptor list */
+#define QCE_MAX_NUM_DESC    128
+
+#define CRYPTO_REG_SIZE                 0x4
+
+struct dmov_desc {
+	uint32_t addr;
+	uint32_t len;
+};
+
+/* State of DM channel */
+enum qce_chan_st_enum {
+	QCE_CHAN_STATE_IDLE = 0,
+	QCE_CHAN_STATE_IN_PROG = 1,
+	QCE_CHAN_STATE_COMP = 2,
+	QCE_CHAN_STATE_LAST
+};
+
+/* CE buffers */
+struct ce_reg_buffer_addr {
+
+	unsigned char *reset_buf_64;
+	unsigned char *version;
+
+	unsigned char *encr_seg_cfg_size_start;
+	unsigned char *encr_key;
+	unsigned char *encr_xts_key;
+	unsigned char *encr_cntr_iv;
+	unsigned char *encr_mask;
+	unsigned char *encr_xts_du_size;
+
+	unsigned char *auth_seg_cfg_size_start;
+	unsigned char *auth_key;
+	unsigned char *auth_iv;
+	unsigned char *auth_result;
+	unsigned char *auth_nonce_info;
+	unsigned char *auth_byte_count;
+
+	unsigned char *seg_size;
+	unsigned char *go_proc;
+	unsigned char *status;
+
+	unsigned char *pad;
+	unsigned char *ignore_data;
+};
+
+/* CE buffers */
+struct ce_reg_buffers {
+
+	unsigned char reset_buf_64[64];
+	unsigned char version[CRYPTO_REG_SIZE];
+
+	unsigned char encr_seg_cfg_size_start[3 * CRYPTO_REG_SIZE];
+	unsigned char encr_key[8 * CRYPTO_REG_SIZE];
+	unsigned char encr_xts_key[8 * CRYPTO_REG_SIZE];
+	unsigned char encr_cntr_iv[4 * CRYPTO_REG_SIZE];
+	unsigned char encr_mask[CRYPTO_REG_SIZE];
+	unsigned char encr_xts_du_size[CRYPTO_REG_SIZE];
+
+	unsigned char auth_seg_cfg_size_start[3 * CRYPTO_REG_SIZE];
+	unsigned char auth_key[16 * CRYPTO_REG_SIZE];
+	unsigned char auth_iv[16 * CRYPTO_REG_SIZE];
+	unsigned char auth_result[16 * CRYPTO_REG_SIZE];
+	unsigned char auth_nonce_info[4 * CRYPTO_REG_SIZE];
+	unsigned char auth_byte_count[4 * CRYPTO_REG_SIZE];
+
+	unsigned char seg_size[CRYPTO_REG_SIZE];
+	unsigned char go_proc[CRYPTO_REG_SIZE];
+	unsigned char status[CRYPTO_REG_SIZE];
+
+	unsigned char pad[2 * MAX_ADM_CE_BLOCK_SIZE];
+};
+
+/* CE Command lists */
+struct ce_cmdlists {
+	dmov_s *get_hw_version;
+	dmov_s *clear_status;
+	dmov_s *get_status_ocu;
+
+	dmov_s *set_cipher_cfg;
+
+	dmov_s *set_cipher_aes_128_key;
+	dmov_s *set_cipher_aes_256_key;
+	dmov_s *set_cipher_des_key;
+	dmov_s *set_cipher_3des_key;
+
+	dmov_s *set_cipher_aes_128_xts_key;
+	dmov_s *set_cipher_aes_256_xts_key;
+	dmov_s *set_cipher_xts_du_size;
+
+	dmov_s *set_cipher_aes_iv;
+	dmov_s *set_cipher_aes_xts_iv;
+	dmov_s *set_cipher_des_iv;
+	dmov_s *get_cipher_iv;
+
+	dmov_s *set_cipher_mask;
+
+	dmov_s *set_auth_cfg;
+	dmov_s *set_auth_key_128;
+	dmov_s *set_auth_key_256;
+	dmov_s *set_auth_key_512;
+	dmov_s *set_auth_iv_16;
+	dmov_s *get_auth_result_16;
+	dmov_s *set_auth_iv_20;
+	dmov_s *get_auth_result_20;
+	dmov_s *set_auth_iv_32;
+	dmov_s *get_auth_result_32;
+	dmov_s *set_auth_byte_count;
+	dmov_s *get_auth_byte_count;
+
+	dmov_s *set_auth_nonce_info;
+
+	dmov_s *reset_cipher_key;
+	dmov_s *reset_cipher_xts_key;
+	dmov_s *reset_cipher_iv;
+	dmov_s *reset_cipher_cfg;
+	dmov_s *reset_auth_key;
+	dmov_s *reset_auth_iv;
+	dmov_s *reset_auth_cfg;
+	dmov_s *reset_auth_byte_count;
+
+	dmov_s *set_seg_size_ocb;
+	dmov_s *get_status_wait;
+	dmov_s *set_go_proc;
+
+	dmov_sg *ce_data_in;
+	dmov_sg *ce_data_out;
+};
+
+/* Command pointer lists */
+struct ce_cmdptrlists_ops {
+
+	uint32_t probe_ce_hw;
+	uint32_t cipher_aes_128_cbc_ctr;
+	uint32_t cipher_aes_256_cbc_ctr;
+	uint32_t cipher_aes_128_ecb;
+	uint32_t cipher_aes_256_ecb;
+	uint32_t cipher_aes_128_xts;
+	uint32_t cipher_aes_256_xts;
+	uint32_t cipher_des_cbc;
+	uint32_t cipher_des_ecb;
+	uint32_t cipher_3des_cbc;
+	uint32_t cipher_3des_ecb;
+	uint32_t auth_sha1;
+	uint32_t auth_sha256;
+	uint32_t auth_sha1_hmac;
+	uint32_t auth_sha256_hmac;
+	uint32_t auth_aes_128_cmac;
+	uint32_t auth_aes_256_cmac;
+	uint32_t aead_aes_128_ccm;
+	uint32_t aead_aes_256_ccm;
+
+	uint32_t cipher_ce_out;
+	uint32_t cipher_ce_out_get_iv;
+	uint32_t aead_ce_out;
+};
+
+/* DM data structure with buffers, commandlists & commmand pointer lists */
+struct ce_dm_data {
+	unsigned int chan_ce_in;	/* ADM channel used for CE input
+					 * and auth result if authentication
+					 * only operation. */
+	unsigned int chan_ce_out;	/* ADM channel used for CE output,
+					 * and icv for esp */
+
+	unsigned int crci_in;		/* CRCI for CE DM IN Channel   */
+	unsigned int crci_out;		/* CRCI for CE DM OUT Channel   */
+
+	enum qce_chan_st_enum chan_ce_in_state;		/* chan ce_in state */
+	enum qce_chan_st_enum chan_ce_out_state;	/* chan ce_out state */
+
+	int chan_ce_in_status;				/* chan ce_in status */
+	int chan_ce_out_status;				/* chan ce_out status */
+
+	struct dmov_desc *ce_out_src_desc;
+	struct dmov_desc *ce_out_dst_desc;
+	struct dmov_desc *ce_in_src_desc;
+	struct dmov_desc *ce_in_dst_desc;
+
+	int ce_out_src_desc_index;
+	int ce_out_dst_desc_index;
+	int ce_in_src_desc_index;
+	int ce_in_dst_desc_index;
+
+	int ce_block_size;
+
+	dma_addr_t phy_ce_out_ignore;
+	dma_addr_t phy_ce_pad;
+
+	struct ce_reg_buffer_addr buffer;
+	struct ce_cmdlists cmdlist;
+	struct ce_cmdptrlists_ops cmdptrlist;
+
+	struct msm_dmov_cmd  *chan_ce_in_cmd;
+	struct msm_dmov_cmd  *chan_ce_out_cmd;
+};
+#endif /* _DRIVERS_CRYPTO_MSM_QCE40_H */
diff --git a/drivers/crypto/msm/qce_ota.h b/drivers/crypto/msm/qce_ota.h
new file mode 100644
index 0000000..72af585
--- /dev/null
+++ b/drivers/crypto/msm/qce_ota.h
@@ -0,0 +1,30 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/* Qualcomm Crypto Engine driver OTA APIi */
+
+#ifndef __CRYPTO_MSM_QCE_OTA_H
+#define __CRYPTO_MSM_QCE_OTA_H
+
+#include <linux/platform_device.h>
+#include <linux/qcota.h>
+
+
+int qce_f8_req(void *handle, struct qce_f8_req *req,
+		void *cookie, qce_comp_func_ptr_t qce_cb);
+int qce_f8_multi_pkt_req(void *handle, struct qce_f8_multi_pkt_req *req,
+		void *cookie, qce_comp_func_ptr_t qce_cb);
+int qce_f9_req(void *handle, struct qce_f9_req *req,
+		void *cookie, qce_comp_func_ptr_t qce_cb);
+
+#endif /* __CRYPTO_MSM_QCE_OTA_H */
diff --git a/drivers/crypto/msm/qcedev.c b/drivers/crypto/msm/qcedev.c
new file mode 100644
index 0000000..fff494c
--- /dev/null
+++ b/drivers/crypto/msm/qcedev.c
@@ -0,0 +1,2228 @@
+/* Qualcomm CE device driver.
+ *
+ * Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/mman.h>
+#include <linux/android_pmem.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/dmapool.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/scatterlist.h>
+#include <linux/crypto.h>
+#include <crypto/hash.h>
+#include <linux/platform_data/qcom_crypto_device.h>
+#include <mach/scm.h>
+#include <mach/msm_bus.h>
+#include <linux/qcedev.h>
+#include "qce.h"
+
+
+#define CACHE_LINE_SIZE 32
+#define CE_SHA_BLOCK_SIZE SHA256_BLOCK_SIZE
+
+static uint8_t  _std_init_vector_sha1_uint8[] =   {
+	0x67, 0x45, 0x23, 0x01, 0xEF, 0xCD, 0xAB, 0x89,
+	0x98, 0xBA, 0xDC, 0xFE, 0x10, 0x32, 0x54, 0x76,
+	0xC3, 0xD2, 0xE1, 0xF0
+};
+/* standard initialization vector for SHA-256, source: FIPS 180-2 */
+static uint8_t _std_init_vector_sha256_uint8[] = {
+	0x6A, 0x09, 0xE6, 0x67, 0xBB, 0x67, 0xAE, 0x85,
+	0x3C, 0x6E, 0xF3, 0x72, 0xA5, 0x4F, 0xF5, 0x3A,
+	0x51, 0x0E, 0x52, 0x7F, 0x9B, 0x05, 0x68, 0x8C,
+	0x1F, 0x83, 0xD9, 0xAB, 0x5B, 0xE0, 0xCD, 0x19
+};
+
+enum qcedev_crypto_oper_type {
+  QCEDEV_CRYPTO_OPER_CIPHER	= 0,
+  QCEDEV_CRYPTO_OPER_SHA	= 1,
+  QCEDEV_CRYPTO_OPER_LAST
+};
+
+struct qcedev_handle;
+
+struct qcedev_cipher_req {
+	struct ablkcipher_request creq;
+	void *cookie;
+};
+
+struct qcedev_sha_req {
+	struct ahash_request sreq;
+	void *cookie;
+};
+
+struct	qcedev_sha_ctxt {
+	uint32_t	auth_data[4];
+	uint8_t		digest[QCEDEV_MAX_SHA_DIGEST];
+	uint32_t	diglen;
+	uint8_t		trailing_buf[64];
+	uint32_t	trailing_buf_len;
+	uint8_t		first_blk;
+	uint8_t		last_blk;
+	uint8_t		authkey[QCEDEV_MAX_SHA_BLOCK_SIZE];
+};
+
+struct qcedev_async_req {
+	struct list_head			list;
+	struct completion			complete;
+	enum qcedev_crypto_oper_type		op_type;
+	union {
+		struct qcedev_cipher_op_req	cipher_op_req;
+		struct qcedev_sha_op_req	sha_op_req;
+	};
+	union{
+		struct qcedev_cipher_req	cipher_req;
+		struct qcedev_sha_req		sha_req;
+	};
+	struct qcedev_handle			*handle;
+	int					err;
+};
+
+static DEFINE_MUTEX(send_cmd_lock);
+static DEFINE_MUTEX(sent_bw_req);
+/**********************************************************************
+ * Register ourselves as a misc device to be able to access the dev driver
+ * from userspace. */
+
+
+#define QCEDEV_DEV	"qcedev"
+
+struct qcedev_control{
+
+	/* CE features supported by platform */
+	struct msm_ce_hw_support platform_support;
+
+	uint32_t ce_lock_count;
+	uint32_t high_bw_req_count;
+
+	/* CE features/algorithms supported by HW engine*/
+	struct ce_hw_support ce_support;
+
+	uint32_t  bus_scale_handle;
+
+	/* misc device */
+	struct miscdevice miscdevice;
+
+	/* qce handle */
+	void *qce;
+
+	/* platform device */
+	struct platform_device *pdev;
+
+	unsigned magic;
+
+	struct list_head ready_commands;
+	struct qcedev_async_req *active_command;
+	spinlock_t lock;
+	struct tasklet_struct done_tasklet;
+};
+
+struct qcedev_handle {
+	/* qcedev control handle */
+	struct qcedev_control *cntl;
+	/* qce internal sha context*/
+	struct	qcedev_sha_ctxt sha_ctxt;
+};
+
+/*-------------------------------------------------------------------------
+* Resource Locking Service
+* ------------------------------------------------------------------------*/
+#define QCEDEV_CMD_ID				1
+#define QCEDEV_CE_LOCK_CMD			1
+#define QCEDEV_CE_UNLOCK_CMD			0
+#define NUM_RETRY				1000
+#define CE_BUSY					55
+
+static int qcedev_scm_cmd(int resource, int cmd, int *response)
+{
+#ifdef CONFIG_MSM_SCM
+
+	struct {
+		int resource;
+		int cmd;
+	} cmd_buf;
+
+	cmd_buf.resource = resource;
+	cmd_buf.cmd = cmd;
+
+	return scm_call(SCM_SVC_TZ, QCEDEV_CMD_ID, &cmd_buf,
+		sizeof(cmd_buf), response, sizeof(*response));
+
+#else
+	return 0;
+#endif
+}
+
+static void qcedev_ce_high_bw_req(struct qcedev_control *podev,
+							bool high_bw_req)
+{
+	int ret = 0;
+
+	mutex_lock(&sent_bw_req);
+	if (high_bw_req) {
+		if (podev->high_bw_req_count == 0)
+			ret = msm_bus_scale_client_update_request(
+					podev->bus_scale_handle, 1);
+		if (ret)
+			pr_err("%s Unable to set to high bandwidth\n",
+							__func__);
+		podev->high_bw_req_count++;
+	} else {
+		if (podev->high_bw_req_count == 1)
+			ret = msm_bus_scale_client_update_request(
+					podev->bus_scale_handle, 0);
+		if (ret)
+			pr_err("%s Unable to set to low bandwidth\n",
+							__func__);
+		podev->high_bw_req_count--;
+	}
+	mutex_unlock(&sent_bw_req);
+}
+
+
+static int qcedev_unlock_ce(struct qcedev_control *podev)
+{
+	int ret = 0;
+
+	mutex_lock(&send_cmd_lock);
+	if (podev->ce_lock_count == 1) {
+		int response = 0;
+
+		if (qcedev_scm_cmd(podev->platform_support.shared_ce_resource,
+					QCEDEV_CE_UNLOCK_CMD, &response)) {
+			pr_err("Failed to release CE lock\n");
+			ret = -EIO;
+		}
+	}
+	if (ret == 0) {
+		if (podev->ce_lock_count)
+			podev->ce_lock_count--;
+		else {
+			/* We should never be here */
+			ret = -EIO;
+			pr_err("CE hardware is already unlocked\n");
+		}
+	}
+	mutex_unlock(&send_cmd_lock);
+
+	return ret;
+}
+
+static int qcedev_lock_ce(struct qcedev_control *podev)
+{
+	int ret = 0;
+
+	mutex_lock(&send_cmd_lock);
+	if (podev->ce_lock_count == 0) {
+		int response = -CE_BUSY;
+		int i = 0;
+
+		do {
+			if (qcedev_scm_cmd(
+				podev->platform_support.shared_ce_resource,
+				QCEDEV_CE_LOCK_CMD, &response)) {
+				response = -EINVAL;
+				break;
+			}
+		} while ((response == -CE_BUSY) && (i++ < NUM_RETRY));
+
+		if ((response == -CE_BUSY) && (i >= NUM_RETRY)) {
+			ret = -EUSERS;
+		} else {
+			if (response < 0)
+				ret = -EINVAL;
+		}
+	}
+	if (ret == 0)
+		podev->ce_lock_count++;
+	mutex_unlock(&send_cmd_lock);
+	return ret;
+}
+
+#define QCEDEV_MAGIC 0x56434544 /* "qced" */
+
+static long qcedev_ioctl(struct file *file, unsigned cmd, unsigned long arg);
+static int qcedev_open(struct inode *inode, struct file *file);
+static int qcedev_release(struct inode *inode, struct file *file);
+static int start_cipher_req(struct qcedev_control *podev);
+static int start_sha_req(struct qcedev_control *podev);
+
+static const struct file_operations qcedev_fops = {
+	.owner = THIS_MODULE,
+	.unlocked_ioctl = qcedev_ioctl,
+	.open = qcedev_open,
+	.release = qcedev_release,
+};
+
+static struct qcedev_control qce_dev[] = {
+	{
+		.miscdevice = {
+			.minor = MISC_DYNAMIC_MINOR,
+			.name = "qce",
+			.fops = &qcedev_fops,
+		},
+		.magic = QCEDEV_MAGIC,
+	},
+};
+
+#define MAX_QCE_DEVICE ARRAY_SIZE(qce_dev)
+#define DEBUG_MAX_FNAME  16
+#define DEBUG_MAX_RW_BUF 1024
+
+struct qcedev_stat {
+	u32 qcedev_dec_success;
+	u32 qcedev_dec_fail;
+	u32 qcedev_enc_success;
+	u32 qcedev_enc_fail;
+	u32 qcedev_sha_success;
+	u32 qcedev_sha_fail;
+};
+
+static struct qcedev_stat _qcedev_stat[MAX_QCE_DEVICE];
+static struct dentry *_debug_dent;
+static char _debug_read_buf[DEBUG_MAX_RW_BUF];
+static int _debug_qcedev[MAX_QCE_DEVICE];
+
+static struct qcedev_control *qcedev_minor_to_control(unsigned n)
+{
+	int i;
+
+	for (i = 0; i < MAX_QCE_DEVICE; i++) {
+		if (qce_dev[i].miscdevice.minor == n)
+			return &qce_dev[i];
+	}
+	return NULL;
+}
+
+static int qcedev_open(struct inode *inode, struct file *file)
+{
+	struct qcedev_handle *handle;
+	struct qcedev_control *podev;
+
+	podev = qcedev_minor_to_control(MINOR(inode->i_rdev));
+	if (podev == NULL) {
+		pr_err("%s: no such device %d\n", __func__,
+					MINOR(inode->i_rdev));
+		return -ENOENT;
+	}
+
+	handle = kzalloc(sizeof(struct qcedev_handle), GFP_KERNEL);
+	if (handle == NULL) {
+		pr_err("Failed to allocate memory %ld\n",
+					PTR_ERR(handle));
+		return -ENOMEM;
+	}
+
+	handle->cntl = podev;
+	file->private_data = handle;
+	if (podev->platform_support.bus_scale_table != NULL)
+		qcedev_ce_high_bw_req(podev, true);
+	return 0;
+}
+
+static int qcedev_release(struct inode *inode, struct file *file)
+{
+	struct qcedev_control *podev;
+	struct qcedev_handle *handle;
+
+	handle =  file->private_data;
+	podev =  handle->cntl;
+	if (podev != NULL && podev->magic != QCEDEV_MAGIC) {
+		pr_err("%s: invalid handle %p\n",
+					__func__, podev);
+	}
+	kzfree(handle);
+	file->private_data = NULL;
+	if (podev->platform_support.bus_scale_table != NULL)
+		qcedev_ce_high_bw_req(podev, false);
+	return 0;
+}
+
+static void req_done(unsigned long data)
+{
+	struct qcedev_control *podev = (struct qcedev_control *)data;
+	struct qcedev_async_req *areq;
+	unsigned long flags = 0;
+	struct qcedev_async_req *new_req = NULL;
+	int ret = 0;
+
+	spin_lock_irqsave(&podev->lock, flags);
+	areq = podev->active_command;
+	podev->active_command = NULL;
+
+again:
+	if (!list_empty(&podev->ready_commands)) {
+		new_req = container_of(podev->ready_commands.next,
+						struct qcedev_async_req, list);
+		list_del(&new_req->list);
+		podev->active_command = new_req;
+		new_req->err = 0;
+		if (new_req->op_type == QCEDEV_CRYPTO_OPER_CIPHER)
+			ret = start_cipher_req(podev);
+		else
+			ret = start_sha_req(podev);
+	}
+
+	spin_unlock_irqrestore(&podev->lock, flags);
+
+	if (areq)
+		complete(&areq->complete);
+
+	if (new_req && ret) {
+		complete(&new_req->complete);
+		spin_lock_irqsave(&podev->lock, flags);
+		podev->active_command = NULL;
+		areq = NULL;
+		ret = 0;
+		new_req = NULL;
+		goto again;
+	}
+
+	return;
+}
+
+static void qcedev_sha_req_cb(void *cookie, unsigned char *digest,
+	unsigned char *authdata, int ret)
+{
+	struct qcedev_sha_req *areq;
+	struct qcedev_control *pdev;
+	struct qcedev_handle *handle;
+
+	uint32_t *auth32 = (uint32_t *)authdata;
+
+	areq = (struct qcedev_sha_req *) cookie;
+	handle = (struct qcedev_handle *) areq->cookie;
+	pdev = handle->cntl;
+
+	if (digest)
+		memcpy(&handle->sha_ctxt.digest[0], digest, 32);
+
+	if (authdata) {
+		handle->sha_ctxt.auth_data[0] = auth32[0];
+		handle->sha_ctxt.auth_data[1] = auth32[1];
+		handle->sha_ctxt.auth_data[2] = auth32[2];
+		handle->sha_ctxt.auth_data[3] = auth32[3];
+	}
+
+	tasklet_schedule(&pdev->done_tasklet);
+};
+
+
+static void qcedev_cipher_req_cb(void *cookie, unsigned char *icv,
+	unsigned char *iv, int ret)
+{
+	struct qcedev_cipher_req *areq;
+	struct qcedev_handle *handle;
+	struct qcedev_control *podev;
+	struct qcedev_async_req *qcedev_areq;
+
+	areq = (struct qcedev_cipher_req *) cookie;
+	handle = (struct qcedev_handle *) areq->cookie;
+	podev = handle->cntl;
+	qcedev_areq = podev->active_command;
+
+	if (iv)
+		memcpy(&qcedev_areq->cipher_op_req.iv[0], iv,
+					qcedev_areq->cipher_op_req.ivlen);
+	tasklet_schedule(&podev->done_tasklet);
+};
+
+static int start_cipher_req(struct qcedev_control *podev)
+{
+	struct qcedev_async_req *qcedev_areq;
+	struct qce_req creq;
+	int ret = 0;
+
+	/* start the command on the podev->active_command */
+	qcedev_areq = podev->active_command;
+
+	qcedev_areq->cipher_req.cookie = qcedev_areq->handle;
+	creq.use_pmem = qcedev_areq->cipher_op_req.use_pmem;
+	if (qcedev_areq->cipher_op_req.use_pmem == QCEDEV_USE_PMEM)
+		creq.pmem = &qcedev_areq->cipher_op_req.pmem;
+	else
+		creq.pmem = NULL;
+
+	switch (qcedev_areq->cipher_op_req.alg) {
+	case QCEDEV_ALG_DES:
+		creq.alg = CIPHER_ALG_DES;
+		break;
+	case QCEDEV_ALG_3DES:
+		creq.alg = CIPHER_ALG_3DES;
+		break;
+	case QCEDEV_ALG_AES:
+		creq.alg = CIPHER_ALG_AES;
+		break;
+	default:
+		return -EINVAL;
+	};
+
+	switch (qcedev_areq->cipher_op_req.mode) {
+	case QCEDEV_AES_MODE_CBC:
+	case QCEDEV_DES_MODE_CBC:
+		creq.mode = QCE_MODE_CBC;
+		break;
+	case QCEDEV_AES_MODE_ECB:
+	case QCEDEV_DES_MODE_ECB:
+		creq.mode = QCE_MODE_ECB;
+		break;
+	case QCEDEV_AES_MODE_CTR:
+		creq.mode = QCE_MODE_CTR;
+		break;
+	case QCEDEV_AES_MODE_XTS:
+		creq.mode = QCE_MODE_XTS;
+		break;
+	default:
+		return -EINVAL;
+	};
+
+	if ((creq.alg == CIPHER_ALG_AES) &&
+		(creq.mode == QCE_MODE_CTR)) {
+		creq.dir = QCE_ENCRYPT;
+	} else {
+		if (QCEDEV_OPER_ENC == qcedev_areq->cipher_op_req.op)
+			creq.dir = QCE_ENCRYPT;
+		else
+			creq.dir = QCE_DECRYPT;
+	}
+
+	creq.iv = &qcedev_areq->cipher_op_req.iv[0];
+	creq.ivsize = qcedev_areq->cipher_op_req.ivlen;
+
+	creq.enckey =  &qcedev_areq->cipher_op_req.enckey[0];
+	creq.encklen = qcedev_areq->cipher_op_req.encklen;
+
+	creq.cryptlen = qcedev_areq->cipher_op_req.data_len;
+
+	if (qcedev_areq->cipher_op_req.encklen == 0) {
+		if ((qcedev_areq->cipher_op_req.op == QCEDEV_OPER_ENC_NO_KEY)
+			|| (qcedev_areq->cipher_op_req.op ==
+				QCEDEV_OPER_DEC_NO_KEY))
+			creq.op = QCE_REQ_ABLK_CIPHER_NO_KEY;
+		else {
+			int i;
+
+			for (i = 0; i < QCEDEV_MAX_KEY_SIZE; i++) {
+				if (qcedev_areq->cipher_op_req.enckey[i] != 0)
+					break;
+			}
+
+			if ((podev->platform_support.hw_key_support == 1) &&
+						(i == QCEDEV_MAX_KEY_SIZE))
+				creq.op = QCE_REQ_ABLK_CIPHER;
+			else {
+				ret = -EINVAL;
+				goto unsupported;
+			}
+		}
+	} else {
+		creq.op = QCE_REQ_ABLK_CIPHER;
+	}
+
+	creq.qce_cb = qcedev_cipher_req_cb;
+	creq.areq = (void *)&qcedev_areq->cipher_req;
+
+	ret = qce_ablk_cipher_req(podev->qce, &creq);
+unsupported:
+	if (ret)
+		qcedev_areq->err = -ENXIO;
+	else
+		qcedev_areq->err = 0;
+	return ret;
+};
+
+static int start_sha_req(struct qcedev_control *podev)
+{
+	struct qcedev_async_req *qcedev_areq;
+	struct qce_sha_req sreq;
+	int ret = 0;
+	struct qcedev_handle *handle;
+
+	/* start the command on the podev->active_command */
+	qcedev_areq = podev->active_command;
+	handle = qcedev_areq->handle;
+
+	switch (qcedev_areq->sha_op_req.alg) {
+	case QCEDEV_ALG_SHA1:
+		sreq.alg = QCE_HASH_SHA1;
+		break;
+	case QCEDEV_ALG_SHA256:
+		sreq.alg = QCE_HASH_SHA256;
+		break;
+	case QCEDEV_ALG_SHA1_HMAC:
+		if (podev->ce_support.sha_hmac) {
+			sreq.alg = QCE_HASH_SHA1_HMAC;
+			sreq.authkey = &handle->sha_ctxt.authkey[0];
+
+		} else {
+			sreq.alg = QCE_HASH_SHA1;
+			sreq.authkey = NULL;
+		}
+		break;
+	case QCEDEV_ALG_SHA256_HMAC:
+		if (podev->ce_support.sha_hmac) {
+			sreq.alg = QCE_HASH_SHA256_HMAC;
+			sreq.authkey = &handle->sha_ctxt.authkey[0];
+
+		} else {
+			sreq.alg = QCE_HASH_SHA256;
+			sreq.authkey = NULL;
+		}
+		break;
+	case QCEDEV_ALG_AES_CMAC:
+		sreq.alg = QCE_HASH_AES_CMAC;
+		sreq.authkey = &handle->sha_ctxt.authkey[0];
+		sreq.authklen = qcedev_areq->sha_op_req.authklen;
+		break;
+	default:
+		break;
+	};
+
+	qcedev_areq->sha_req.cookie = handle;
+
+	sreq.qce_cb = qcedev_sha_req_cb;
+	if (qcedev_areq->sha_op_req.alg != QCEDEV_ALG_AES_CMAC) {
+		sreq.auth_data[0] = handle->sha_ctxt.auth_data[0];
+		sreq.auth_data[1] = handle->sha_ctxt.auth_data[1];
+		sreq.auth_data[2] = handle->sha_ctxt.auth_data[2];
+		sreq.auth_data[3] = handle->sha_ctxt.auth_data[3];
+		sreq.digest = &handle->sha_ctxt.digest[0];
+		sreq.first_blk = handle->sha_ctxt.first_blk;
+		sreq.last_blk = handle->sha_ctxt.last_blk;
+	}
+	sreq.size = qcedev_areq->sha_req.sreq.nbytes;
+	sreq.src = qcedev_areq->sha_req.sreq.src;
+	sreq.areq = (void *)&qcedev_areq->sha_req;
+
+	ret = qce_process_sha_req(podev->qce, &sreq);
+
+	if (ret)
+		qcedev_areq->err = -ENXIO;
+	else
+		qcedev_areq->err = 0;
+	return ret;
+};
+
+static int submit_req(struct qcedev_async_req *qcedev_areq,
+					struct qcedev_handle *handle)
+{
+	struct qcedev_control *podev;
+	unsigned long flags = 0;
+	int ret = 0;
+	struct qcedev_stat *pstat;
+
+	qcedev_areq->err = 0;
+	podev = handle->cntl;
+
+	if (podev->platform_support.ce_shared) {
+		ret = qcedev_lock_ce(podev);
+		if (ret)
+			return ret;
+	}
+
+	spin_lock_irqsave(&podev->lock, flags);
+
+	if (podev->active_command == NULL) {
+		podev->active_command = qcedev_areq;
+		if (qcedev_areq->op_type == QCEDEV_CRYPTO_OPER_CIPHER)
+			ret = start_cipher_req(podev);
+		else
+			ret = start_sha_req(podev);
+	} else {
+		list_add_tail(&qcedev_areq->list, &podev->ready_commands);
+	}
+
+	if (ret != 0)
+		podev->active_command = NULL;
+
+	spin_unlock_irqrestore(&podev->lock, flags);
+
+	if (ret == 0)
+		wait_for_completion(&qcedev_areq->complete);
+
+	if (podev->platform_support.ce_shared)
+		ret = qcedev_unlock_ce(podev);
+
+	if (ret)
+		qcedev_areq->err = -EIO;
+
+	pstat = &_qcedev_stat[podev->pdev->id];
+	if (qcedev_areq->op_type == QCEDEV_CRYPTO_OPER_CIPHER) {
+		switch (qcedev_areq->cipher_op_req.op) {
+		case QCEDEV_OPER_DEC:
+			if (qcedev_areq->err)
+				pstat->qcedev_dec_fail++;
+			else
+				pstat->qcedev_dec_success++;
+			break;
+		case QCEDEV_OPER_ENC:
+			if (qcedev_areq->err)
+				pstat->qcedev_enc_fail++;
+			else
+				pstat->qcedev_enc_success++;
+			break;
+		default:
+			break;
+		};
+	} else {
+		if (qcedev_areq->err)
+			pstat->qcedev_sha_fail++;
+		else
+			pstat->qcedev_sha_success++;
+	}
+
+	return qcedev_areq->err;
+}
+
+static int qcedev_sha_init(struct qcedev_async_req *areq,
+				struct qcedev_handle *handle)
+{
+	struct qcedev_sha_ctxt *sha_ctxt = &handle->sha_ctxt;
+
+	memset(sha_ctxt, 0, sizeof(struct qcedev_sha_ctxt));
+	sha_ctxt->first_blk = 1;
+
+	if ((areq->sha_op_req.alg == QCEDEV_ALG_SHA1) ||
+			(areq->sha_op_req.alg == QCEDEV_ALG_SHA1_HMAC)) {
+		memcpy(&sha_ctxt->digest[0],
+			&_std_init_vector_sha1_uint8[0], SHA1_DIGEST_SIZE);
+		sha_ctxt->diglen = SHA1_DIGEST_SIZE;
+	} else {
+		if ((areq->sha_op_req.alg == QCEDEV_ALG_SHA256) ||
+			(areq->sha_op_req.alg == QCEDEV_ALG_SHA256_HMAC)) {
+			memcpy(&sha_ctxt->digest[0],
+					&_std_init_vector_sha256_uint8[0],
+					SHA256_DIGEST_SIZE);
+			sha_ctxt->diglen = SHA256_DIGEST_SIZE;
+		}
+	}
+	return 0;
+}
+
+
+static int qcedev_sha_update_max_xfer(struct qcedev_async_req *qcedev_areq,
+				struct qcedev_handle *handle)
+{
+	int err = 0;
+	int i = 0;
+	struct scatterlist sg_src[2];
+	uint32_t total;
+
+	uint8_t *user_src = NULL;
+	uint8_t *k_src = NULL;
+	uint8_t *k_buf_src = NULL;
+	uint8_t *k_align_src = NULL;
+
+	uint32_t sha_pad_len = 0;
+	uint32_t trailing_buf_len = 0;
+	uint32_t t_buf = handle->sha_ctxt.trailing_buf_len;
+	uint32_t sha_block_size;
+
+	total = qcedev_areq->sha_op_req.data_len + t_buf;
+
+	if (qcedev_areq->sha_op_req.alg == QCEDEV_ALG_SHA1)
+		sha_block_size = SHA1_BLOCK_SIZE;
+	else
+		sha_block_size = SHA256_BLOCK_SIZE;
+
+	if (total <= sha_block_size) {
+		uint32_t len =  qcedev_areq->sha_op_req.data_len;
+
+		i = 0;
+
+		k_src = &handle->sha_ctxt.trailing_buf[t_buf];
+
+		/* Copy data from user src(s) */
+		while (len > 0) {
+			user_src =
+			(void __user *)qcedev_areq->sha_op_req.data[i].vaddr;
+			if (user_src && __copy_from_user(k_src,
+				(void __user *)user_src,
+				qcedev_areq->sha_op_req.data[i].len))
+				return -EFAULT;
+
+			len -= qcedev_areq->sha_op_req.data[i].len;
+			k_src += qcedev_areq->sha_op_req.data[i].len;
+			i++;
+		}
+		handle->sha_ctxt.trailing_buf_len = total;
+
+		return 0;
+	}
+
+
+	k_buf_src = kmalloc(total + CACHE_LINE_SIZE * 2,
+				GFP_KERNEL);
+	if (k_buf_src == NULL) {
+		pr_err("%s: Can't Allocate memory: k_buf_src 0x%x\n",
+			__func__, (uint32_t)k_buf_src);
+		return -ENOMEM;
+	}
+
+	k_align_src = (uint8_t *) ALIGN(((unsigned int)k_buf_src),
+							CACHE_LINE_SIZE);
+	k_src = k_align_src;
+
+	/* check for trailing buffer from previous updates and append it */
+	if (t_buf > 0) {
+		memcpy(k_src, &handle->sha_ctxt.trailing_buf[0],
+								t_buf);
+		k_src += t_buf;
+	}
+
+	/* Copy data from user src(s) */
+	user_src = (void __user *)qcedev_areq->sha_op_req.data[0].vaddr;
+	if (user_src && __copy_from_user(k_src,
+				(void __user *)user_src,
+				qcedev_areq->sha_op_req.data[0].len)) {
+		kfree(k_buf_src);
+		return -EFAULT;
+	}
+	k_src += qcedev_areq->sha_op_req.data[0].len;
+	for (i = 1; i < qcedev_areq->sha_op_req.entries; i++) {
+		user_src = (void __user *)qcedev_areq->sha_op_req.data[i].vaddr;
+		if (user_src && __copy_from_user(k_src,
+					(void __user *)user_src,
+					qcedev_areq->sha_op_req.data[i].len)) {
+			kfree(k_buf_src);
+			return -EFAULT;
+		}
+		k_src += qcedev_areq->sha_op_req.data[i].len;
+	}
+
+	/*  get new trailing buffer */
+	sha_pad_len = ALIGN(total, CE_SHA_BLOCK_SIZE) - total;
+	trailing_buf_len =  CE_SHA_BLOCK_SIZE - sha_pad_len;
+
+	qcedev_areq->sha_req.sreq.src = (struct scatterlist *) &sg_src[0];
+	sg_set_buf(qcedev_areq->sha_req.sreq.src, k_align_src,
+						total-trailing_buf_len);
+	sg_mark_end(qcedev_areq->sha_req.sreq.src);
+
+	qcedev_areq->sha_req.sreq.nbytes = total - trailing_buf_len;
+
+	/*  update sha_ctxt trailing buf content to new trailing buf */
+	if (trailing_buf_len > 0) {
+		memset(&handle->sha_ctxt.trailing_buf[0], 0, 64);
+		memcpy(&handle->sha_ctxt.trailing_buf[0],
+			(k_src - trailing_buf_len),
+			trailing_buf_len);
+	}
+	handle->sha_ctxt.trailing_buf_len = trailing_buf_len;
+
+	err = submit_req(qcedev_areq, handle);
+
+	handle->sha_ctxt.last_blk = 0;
+	handle->sha_ctxt.first_blk = 0;
+
+	kfree(k_buf_src);
+	return err;
+}
+
+static int qcedev_sha_update(struct qcedev_async_req *qcedev_areq,
+				struct qcedev_handle *handle)
+{
+	int err = 0;
+	int i = 0;
+	int j = 0;
+	int k = 0;
+	int num_entries = 0;
+	uint32_t total = 0;
+
+	/* verify address src(s) */
+	for (i = 0; i < qcedev_areq->sha_op_req.entries; i++)
+		if (!access_ok(VERIFY_READ,
+			(void __user *)qcedev_areq->sha_op_req.data[i].vaddr,
+			qcedev_areq->sha_op_req.data[i].len))
+			return -EFAULT;
+
+	if (qcedev_areq->sha_op_req.data_len > QCE_MAX_OPER_DATA) {
+
+		struct	qcedev_sha_op_req *saved_req;
+		struct	qcedev_sha_op_req req;
+		struct	qcedev_sha_op_req *sreq = &qcedev_areq->sha_op_req;
+
+		/* save the original req structure */
+		saved_req =
+			kmalloc(sizeof(struct qcedev_sha_op_req), GFP_KERNEL);
+		if (saved_req == NULL) {
+			pr_err("%s:Can't Allocate mem:saved_req 0x%x\n",
+			__func__, (uint32_t)saved_req);
+			return -ENOMEM;
+		}
+		memcpy(&req, sreq, sizeof(struct qcedev_sha_op_req));
+		memcpy(saved_req, sreq, sizeof(struct qcedev_sha_op_req));
+
+		i = 0;
+		/* Address 32 KB  at a time */
+		while ((i < req.entries) && (err == 0)) {
+			if (sreq->data[i].len > QCE_MAX_OPER_DATA) {
+				sreq->data[0].len = QCE_MAX_OPER_DATA;
+				if (i > 0) {
+					sreq->data[0].vaddr =
+							sreq->data[i].vaddr;
+				}
+
+				sreq->data_len = QCE_MAX_OPER_DATA;
+				sreq->entries = 1;
+
+				err = qcedev_sha_update_max_xfer(qcedev_areq,
+									handle);
+
+				sreq->data[i].len = req.data[i].len -
+							QCE_MAX_OPER_DATA;
+				sreq->data[i].vaddr = req.data[i].vaddr +
+							QCE_MAX_OPER_DATA;
+				req.data[i].vaddr = sreq->data[i].vaddr;
+				req.data[i].len = sreq->data[i].len;
+			} else {
+				total = 0;
+				for (j = i; j < req.entries; j++) {
+					num_entries++;
+					if ((total + sreq->data[j].len) >=
+							QCE_MAX_OPER_DATA) {
+						sreq->data[j].len =
+						(QCE_MAX_OPER_DATA - total);
+						total = QCE_MAX_OPER_DATA;
+						break;
+					}
+					total += sreq->data[j].len;
+				}
+
+				sreq->data_len = total;
+				if (i > 0)
+					for (k = 0; k < num_entries; k++) {
+						sreq->data[k].len =
+							sreq->data[i+k].len;
+						sreq->data[k].vaddr =
+							sreq->data[i+k].vaddr;
+					}
+				sreq->entries = num_entries;
+
+				i = j;
+				err = qcedev_sha_update_max_xfer(qcedev_areq,
+									handle);
+				num_entries = 0;
+
+				sreq->data[i].vaddr = req.data[i].vaddr +
+							sreq->data[i].len;
+				sreq->data[i].len = req.data[i].len -
+							sreq->data[i].len;
+				req.data[i].vaddr = sreq->data[i].vaddr;
+				req.data[i].len = sreq->data[i].len;
+
+				if (sreq->data[i].len == 0)
+					i++;
+			}
+		} /* end of while ((i < req.entries) && (err == 0)) */
+
+		/* Restore the original req structure */
+		for (i = 0; i < saved_req->entries; i++) {
+			sreq->data[i].len = saved_req->data[i].len;
+			sreq->data[i].vaddr = saved_req->data[i].vaddr;
+		}
+		sreq->entries = saved_req->entries;
+		sreq->data_len = saved_req->data_len;
+		kfree(saved_req);
+	} else
+		err = qcedev_sha_update_max_xfer(qcedev_areq, handle);
+
+	return err;
+}
+
+static int qcedev_sha_final(struct qcedev_async_req *qcedev_areq,
+				struct qcedev_handle *handle)
+{
+	int err = 0;
+	struct scatterlist sg_src;
+	uint32_t total;
+
+	uint8_t *k_buf_src = NULL;
+	uint8_t *k_align_src = NULL;
+
+	handle->sha_ctxt.first_blk = 0;
+	handle->sha_ctxt.last_blk = 1;
+
+	total = handle->sha_ctxt.trailing_buf_len;
+
+	if (total) {
+		k_buf_src = kmalloc(total + CACHE_LINE_SIZE * 2,
+					GFP_KERNEL);
+		if (k_buf_src == NULL) {
+			pr_err("%s: Can't Allocate memory: k_buf_src 0x%x\n",
+			__func__, (uint32_t)k_buf_src);
+			return -ENOMEM;
+		}
+
+		k_align_src = (uint8_t *) ALIGN(((unsigned int)k_buf_src),
+							CACHE_LINE_SIZE);
+		memcpy(k_align_src, &handle->sha_ctxt.trailing_buf[0], total);
+	}
+	handle->sha_ctxt.last_blk = 1;
+	handle->sha_ctxt.first_blk = 0;
+
+	qcedev_areq->sha_req.sreq.src = (struct scatterlist *) &sg_src;
+	sg_set_buf(qcedev_areq->sha_req.sreq.src, k_align_src, total);
+	sg_mark_end(qcedev_areq->sha_req.sreq.src);
+
+	qcedev_areq->sha_req.sreq.nbytes = total;
+
+	err = submit_req(qcedev_areq, handle);
+
+	handle->sha_ctxt.first_blk = 0;
+	handle->sha_ctxt.last_blk = 0;
+	handle->sha_ctxt.auth_data[0] = 0;
+	handle->sha_ctxt.auth_data[1] = 0;
+	handle->sha_ctxt.trailing_buf_len = 0;
+	memset(&handle->sha_ctxt.trailing_buf[0], 0, 64);
+
+	kfree(k_buf_src);
+	return err;
+}
+
+static int qcedev_hash_cmac(struct qcedev_async_req *qcedev_areq,
+					struct qcedev_handle *handle)
+{
+	int err = 0;
+	int i = 0;
+	struct scatterlist sg_src[2];
+	uint32_t total;
+
+	uint8_t *user_src = NULL;
+	uint8_t *k_src = NULL;
+	uint8_t *k_buf_src = NULL;
+
+	total = qcedev_areq->sha_op_req.data_len;
+
+	/* verify address src(s) */
+	for (i = 0; i < qcedev_areq->sha_op_req.entries; i++)
+		if (!access_ok(VERIFY_READ,
+			(void __user *)qcedev_areq->sha_op_req.data[i].vaddr,
+			qcedev_areq->sha_op_req.data[i].len))
+			return -EFAULT;
+
+	/* Verify Source Address */
+	if (!access_ok(VERIFY_READ,
+				(void __user *)qcedev_areq->sha_op_req.authkey,
+				qcedev_areq->sha_op_req.authklen))
+			return -EFAULT;
+	if (__copy_from_user(&handle->sha_ctxt.authkey[0],
+				(void __user *)qcedev_areq->sha_op_req.authkey,
+				qcedev_areq->sha_op_req.authklen))
+		return -EFAULT;
+
+
+	k_buf_src = kmalloc(total, GFP_KERNEL);
+	if (k_buf_src == NULL) {
+		pr_err("%s: Can't Allocate memory: k_buf_src 0x%x\n",
+			__func__, (uint32_t)k_buf_src);
+		return -ENOMEM;
+	}
+
+	k_src = k_buf_src;
+
+	/* Copy data from user src(s) */
+	user_src = (void __user *)qcedev_areq->sha_op_req.data[0].vaddr;
+	for (i = 0; i < qcedev_areq->sha_op_req.entries; i++) {
+		user_src =
+			(void __user *)qcedev_areq->sha_op_req.data[i].vaddr;
+		if (user_src && __copy_from_user(k_src, (void __user *)user_src,
+				qcedev_areq->sha_op_req.data[i].len)) {
+			kfree(k_buf_src);
+			return -EFAULT;
+		}
+		k_src += qcedev_areq->sha_op_req.data[i].len;
+	}
+
+	qcedev_areq->sha_req.sreq.src = (struct scatterlist *) &sg_src[0];
+	sg_set_buf(qcedev_areq->sha_req.sreq.src, k_buf_src, total);
+	sg_mark_end(qcedev_areq->sha_req.sreq.src);
+
+	qcedev_areq->sha_req.sreq.nbytes = total;
+	handle->sha_ctxt.diglen = qcedev_areq->sha_op_req.diglen;
+	err = submit_req(qcedev_areq, handle);
+
+	kfree(k_buf_src);
+	return err;
+}
+
+static int qcedev_set_hmac_auth_key(struct qcedev_async_req *areq,
+					struct qcedev_handle *handle)
+{
+	int err = 0;
+
+	if (areq->sha_op_req.authklen <= QCEDEV_MAX_KEY_SIZE) {
+		/* Verify Source Address */
+		if (!access_ok(VERIFY_READ,
+				(void __user *)areq->sha_op_req.authkey,
+				areq->sha_op_req.authklen))
+			return -EFAULT;
+		if (__copy_from_user(&handle->sha_ctxt.authkey[0],
+				(void __user *)areq->sha_op_req.authkey,
+				areq->sha_op_req.authklen))
+			return -EFAULT;
+	} else {
+		struct qcedev_async_req authkey_areq;
+
+		init_completion(&authkey_areq.complete);
+
+		authkey_areq.sha_op_req.entries = 1;
+		authkey_areq.sha_op_req.data[0].vaddr =
+						areq->sha_op_req.authkey;
+		authkey_areq.sha_op_req.data[0].len = areq->sha_op_req.authklen;
+		authkey_areq.sha_op_req.data_len = areq->sha_op_req.authklen;
+		authkey_areq.sha_op_req.diglen = 0;
+		memset(&authkey_areq.sha_op_req.digest[0], 0,
+						QCEDEV_MAX_SHA_DIGEST);
+		if (areq->sha_op_req.alg == QCEDEV_ALG_SHA1_HMAC)
+				authkey_areq.sha_op_req.alg = QCEDEV_ALG_SHA1;
+		if (areq->sha_op_req.alg == QCEDEV_ALG_SHA256_HMAC)
+				authkey_areq.sha_op_req.alg = QCEDEV_ALG_SHA256;
+
+		authkey_areq.op_type = QCEDEV_CRYPTO_OPER_SHA;
+
+		qcedev_sha_init(&authkey_areq, handle);
+		err = qcedev_sha_update(&authkey_areq, handle);
+		if (!err)
+			err = qcedev_sha_final(&authkey_areq, handle);
+		else
+			return err;
+		memcpy(&handle->sha_ctxt.authkey[0],
+				&handle->sha_ctxt.digest[0],
+				handle->sha_ctxt.diglen);
+	}
+	return err;
+}
+
+static int qcedev_hmac_get_ohash(struct qcedev_async_req *qcedev_areq,
+				struct qcedev_handle *handle)
+{
+	int err = 0;
+	struct scatterlist sg_src;
+	uint8_t *k_src = NULL;
+	uint32_t sha_block_size = 0;
+	uint32_t sha_digest_size = 0;
+
+	if (qcedev_areq->sha_op_req.alg == QCEDEV_ALG_SHA1_HMAC) {
+		sha_digest_size = SHA1_DIGEST_SIZE;
+		sha_block_size = SHA1_BLOCK_SIZE;
+	} else {
+		if (qcedev_areq->sha_op_req.alg == QCEDEV_ALG_SHA256_HMAC) {
+			sha_digest_size = SHA256_DIGEST_SIZE;
+			sha_block_size = SHA256_BLOCK_SIZE;
+		}
+	}
+	k_src = kmalloc(sha_block_size, GFP_KERNEL);
+	if (k_src == NULL) {
+		pr_err("%s: Can't Allocate memory: k_src 0x%x\n",
+			__func__, (uint32_t)k_src);
+		return -ENOMEM;
+	}
+
+	/* check for trailing buffer from previous updates and append it */
+	memcpy(k_src, &handle->sha_ctxt.trailing_buf[0],
+			handle->sha_ctxt.trailing_buf_len);
+
+	qcedev_areq->sha_req.sreq.src = (struct scatterlist *) &sg_src;
+	sg_set_buf(qcedev_areq->sha_req.sreq.src, k_src, sha_block_size);
+	sg_mark_end(qcedev_areq->sha_req.sreq.src);
+
+	qcedev_areq->sha_req.sreq.nbytes = sha_block_size;
+	memset(&handle->sha_ctxt.trailing_buf[0], 0, sha_block_size);
+	memcpy(&handle->sha_ctxt.trailing_buf[0], &handle->sha_ctxt.digest[0],
+					sha_digest_size);
+	handle->sha_ctxt.trailing_buf_len = sha_digest_size;
+
+	handle->sha_ctxt.first_blk = 1;
+	handle->sha_ctxt.last_blk = 0;
+	handle->sha_ctxt.auth_data[0] = 0;
+	handle->sha_ctxt.auth_data[1] = 0;
+
+	if (qcedev_areq->sha_op_req.alg == QCEDEV_ALG_SHA1_HMAC) {
+		memcpy(&handle->sha_ctxt.digest[0],
+			&_std_init_vector_sha1_uint8[0], SHA1_DIGEST_SIZE);
+		handle->sha_ctxt.diglen = SHA1_DIGEST_SIZE;
+	}
+
+	if (qcedev_areq->sha_op_req.alg == QCEDEV_ALG_SHA256_HMAC) {
+		memcpy(&handle->sha_ctxt.digest[0],
+			&_std_init_vector_sha256_uint8[0], SHA256_DIGEST_SIZE);
+		handle->sha_ctxt.diglen = SHA256_DIGEST_SIZE;
+	}
+	err = submit_req(qcedev_areq, handle);
+
+	handle->sha_ctxt.last_blk = 0;
+	handle->sha_ctxt.first_blk = 0;
+
+	kfree(k_src);
+	return err;
+}
+
+static int qcedev_hmac_update_iokey(struct qcedev_async_req *areq,
+				struct qcedev_handle *handle, bool ikey)
+{
+	int i;
+	uint32_t constant;
+	uint32_t sha_block_size;
+
+	if (ikey)
+		constant = 0x36;
+	else
+		constant = 0x5c;
+
+	if (areq->sha_op_req.alg == QCEDEV_ALG_SHA1_HMAC)
+		sha_block_size = SHA1_BLOCK_SIZE;
+	else
+		sha_block_size = SHA256_BLOCK_SIZE;
+
+	memset(&handle->sha_ctxt.trailing_buf[0], 0, sha_block_size);
+	for (i = 0; i < sha_block_size; i++)
+		handle->sha_ctxt.trailing_buf[i] =
+				(handle->sha_ctxt.authkey[i] ^ constant);
+
+	handle->sha_ctxt.trailing_buf_len = sha_block_size;
+	return 0;
+}
+
+static int qcedev_hmac_init(struct qcedev_async_req *areq,
+				struct qcedev_handle *handle)
+{
+	int err;
+	struct qcedev_control *podev = handle->cntl;
+
+	qcedev_sha_init(areq, handle);
+	err = qcedev_set_hmac_auth_key(areq, handle);
+	if (err)
+		return err;
+	if (!podev->ce_support.sha_hmac)
+		qcedev_hmac_update_iokey(areq, handle, true);
+	return 0;
+}
+
+static int qcedev_hmac_final(struct qcedev_async_req *areq,
+				struct qcedev_handle *handle)
+{
+	int err;
+	struct qcedev_control *podev = handle->cntl;
+
+	err = qcedev_sha_final(areq, handle);
+	if (podev->ce_support.sha_hmac)
+		return err;
+
+	qcedev_hmac_update_iokey(areq, handle, false);
+	err = qcedev_hmac_get_ohash(areq, handle);
+	if (err)
+		return err;
+	err = qcedev_sha_final(areq, handle);
+
+	return err;
+}
+
+static int qcedev_hash_init(struct qcedev_async_req *areq,
+				struct qcedev_handle *handle)
+{
+	if ((areq->sha_op_req.alg == QCEDEV_ALG_SHA1) ||
+			(areq->sha_op_req.alg == QCEDEV_ALG_SHA256))
+		return qcedev_sha_init(areq, handle);
+	else
+		return qcedev_hmac_init(areq, handle);
+}
+
+static int qcedev_hash_update(struct qcedev_async_req *qcedev_areq,
+				struct qcedev_handle *handle)
+{
+	return qcedev_sha_update(qcedev_areq, handle);
+}
+
+static int qcedev_hash_final(struct qcedev_async_req *areq,
+				struct qcedev_handle *handle)
+{
+	if ((areq->sha_op_req.alg == QCEDEV_ALG_SHA1) ||
+			(areq->sha_op_req.alg == QCEDEV_ALG_SHA256))
+		return qcedev_sha_final(areq, handle);
+	else
+		return qcedev_hmac_final(areq, handle);
+}
+
+#ifdef CONFIG_ANDROID_PMEM
+static int qcedev_pmem_ablk_cipher_max_xfer(struct qcedev_async_req *areq,
+						struct qcedev_handle *handle)
+{
+	int i = 0;
+	int err = 0;
+	struct scatterlist *sg_src = NULL;
+	struct scatterlist *sg_dst = NULL;
+	struct scatterlist *sg_ndex = NULL;
+	struct file *file_src = NULL;
+	struct file *file_dst = NULL;
+	unsigned long paddr;
+	unsigned long kvaddr;
+	unsigned long len;
+
+	sg_src = kmalloc((sizeof(struct scatterlist) *
+				areq->cipher_op_req.entries),	GFP_KERNEL);
+	if (sg_src == NULL) {
+		pr_err("%s: Can't Allocate memory:sg_src 0x%x\n",
+			__func__, (uint32_t)sg_src);
+		return -ENOMEM;
+
+	}
+	memset(sg_src, 0, (sizeof(struct scatterlist) *
+				areq->cipher_op_req.entries));
+	sg_ndex = sg_src;
+	areq->cipher_req.creq.src = sg_src;
+
+	/* address src */
+	get_pmem_file(areq->cipher_op_req.pmem.fd_src, &paddr,
+					&kvaddr, &len, &file_src);
+
+	for (i = 0; i < areq->cipher_op_req.entries; i++) {
+		sg_set_buf(sg_ndex,
+		((uint8_t *)(areq->cipher_op_req.pmem.src[i].offset) + kvaddr),
+		areq->cipher_op_req.pmem.src[i].len);
+		sg_ndex++;
+	}
+	sg_mark_end(--sg_ndex);
+
+	for (i = 0; i < areq->cipher_op_req.entries; i++)
+		areq->cipher_op_req.pmem.src[i].offset += (uint32_t)paddr;
+
+	/* address dst */
+	/* If not place encryption/decryption */
+	if (areq->cipher_op_req.in_place_op != 1) {
+		sg_dst = kmalloc((sizeof(struct scatterlist) *
+				areq->cipher_op_req.entries), GFP_KERNEL);
+		if (sg_dst == NULL) {
+			pr_err("%s: Can't Allocate memory: sg_dst 0x%x\n",
+			__func__, (uint32_t)sg_dst);
+			return -ENOMEM;
+		}
+		memset(sg_dst, 0, (sizeof(struct scatterlist) *
+					areq->cipher_op_req.entries));
+		areq->cipher_req.creq.dst = sg_dst;
+		sg_ndex = sg_dst;
+
+		get_pmem_file(areq->cipher_op_req.pmem.fd_dst, &paddr,
+					&kvaddr, &len, &file_dst);
+		for (i = 0; i < areq->cipher_op_req.entries; i++)
+			sg_set_buf(sg_ndex++,
+			((uint8_t *)(areq->cipher_op_req.pmem.dst[i].offset)
+			+ kvaddr), areq->cipher_op_req.pmem.dst[i].len);
+		sg_mark_end(--sg_ndex);
+
+		for (i = 0; i < areq->cipher_op_req.entries; i++)
+			areq->cipher_op_req.pmem.dst[i].offset +=
+							(uint32_t)paddr;
+	} else {
+		areq->cipher_req.creq.dst = sg_src;
+		for (i = 0; i < areq->cipher_op_req.entries; i++) {
+			areq->cipher_op_req.pmem.dst[i].offset =
+				areq->cipher_op_req.pmem.src[i].offset;
+			areq->cipher_op_req.pmem.dst[i].len =
+				areq->cipher_op_req.pmem.src[i].len;
+		}
+	}
+
+	areq->cipher_req.creq.nbytes = areq->cipher_op_req.data_len;
+	areq->cipher_req.creq.info = areq->cipher_op_req.iv;
+
+	err = submit_req(areq, handle);
+
+	kfree(sg_src);
+	kfree(sg_dst);
+
+	if (file_dst)
+		put_pmem_file(file_dst);
+	if (file_src)
+		put_pmem_file(file_src);
+
+	return err;
+};
+
+
+static int qcedev_pmem_ablk_cipher(struct qcedev_async_req *qcedev_areq,
+						struct qcedev_handle *handle)
+{
+	int err = 0;
+	int i = 0;
+	int j = 0;
+	int k = 0;
+	int num_entries = 0;
+	uint32_t total = 0;
+	struct qcedev_cipher_op_req *saved_req;
+	struct qcedev_cipher_op_req *creq = &qcedev_areq->cipher_op_req;
+
+	saved_req = kmalloc(sizeof(struct qcedev_cipher_op_req), GFP_KERNEL);
+	if (saved_req == NULL) {
+		pr_err(KERN_ERR "%s:Can't Allocate mem:saved_req 0x%x\n",
+		__func__, (uint32_t)saved_req);
+		return -ENOMEM;
+	}
+	memcpy(saved_req, creq, sizeof(struct qcedev_cipher_op_req));
+
+	if (qcedev_areq->cipher_op_req.data_len > QCE_MAX_OPER_DATA) {
+
+		struct qcedev_cipher_op_req req;
+
+		/* save the original req structure */
+		memcpy(&req, creq, sizeof(struct qcedev_cipher_op_req));
+
+		i = 0;
+		/* Address 32 KB  at a time */
+		while ((i < req.entries) && (err == 0)) {
+			if (creq->pmem.src[i].len > QCE_MAX_OPER_DATA) {
+				creq->pmem.src[0].len =	QCE_MAX_OPER_DATA;
+				if (i > 0) {
+					creq->pmem.src[0].offset =
+						creq->pmem.src[i].offset;
+				}
+
+				creq->data_len = QCE_MAX_OPER_DATA;
+				creq->entries = 1;
+
+				err =
+				qcedev_pmem_ablk_cipher_max_xfer(qcedev_areq,
+								handle);
+
+				creq->pmem.src[i].len =	req.pmem.src[i].len -
+							QCE_MAX_OPER_DATA;
+				creq->pmem.src[i].offset =
+						req.pmem.src[i].offset +
+						QCE_MAX_OPER_DATA;
+				req.pmem.src[i].offset =
+						creq->pmem.src[i].offset;
+				req.pmem.src[i].len = creq->pmem.src[i].len;
+			} else {
+				total = 0;
+				for (j = i; j < req.entries; j++) {
+					num_entries++;
+					if ((total + creq->pmem.src[j].len)
+							>= QCE_MAX_OPER_DATA) {
+						creq->pmem.src[j].len =
+						QCE_MAX_OPER_DATA - total;
+						total = QCE_MAX_OPER_DATA;
+						break;
+					}
+					total += creq->pmem.src[j].len;
+				}
+
+				creq->data_len = total;
+				if (i > 0)
+					for (k = 0; k < num_entries; k++) {
+						creq->pmem.src[k].len =
+						creq->pmem.src[i+k].len;
+						creq->pmem.src[k].offset =
+						creq->pmem.src[i+k].offset;
+					}
+				creq->entries =  num_entries;
+
+				i = j;
+				err =
+				qcedev_pmem_ablk_cipher_max_xfer(qcedev_areq,
+								handle);
+				num_entries = 0;
+
+					creq->pmem.src[i].offset =
+						req.pmem.src[i].offset +
+						creq->pmem.src[i].len;
+					creq->pmem.src[i].len =
+						req.pmem.src[i].len -
+						creq->pmem.src[i].len;
+					req.pmem.src[i].offset =
+						creq->pmem.src[i].offset;
+					req.pmem.src[i].len =
+						creq->pmem.src[i].len;
+
+				if (creq->pmem.src[i].len == 0)
+					i++;
+			}
+
+		} /* end of while ((i < req.entries) && (err == 0)) */
+
+	} else
+		err = qcedev_pmem_ablk_cipher_max_xfer(qcedev_areq, handle);
+
+	/* Restore the original req structure */
+	for (i = 0; i < saved_req->entries; i++) {
+		creq->pmem.src[i].len = saved_req->pmem.src[i].len;
+		creq->pmem.src[i].offset = saved_req->pmem.src[i].offset;
+	}
+	creq->entries = saved_req->entries;
+	creq->data_len = saved_req->data_len;
+	kfree(saved_req);
+
+	return err;
+
+}
+#else
+static int qcedev_pmem_ablk_cipher(struct qcedev_async_req *qcedev_areq,
+						struct qcedev_handle *handle)
+{
+	return -EPERM;
+}
+#endif/*CONFIG_ANDROID_PMEM*/
+
+static int qcedev_vbuf_ablk_cipher_max_xfer(struct qcedev_async_req *areq,
+				int *di, struct qcedev_handle *handle,
+				uint8_t *k_align_src)
+{
+	int err = 0;
+	int i = 0;
+	int dst_i = *di;
+	struct scatterlist sg_src;
+	uint32_t byteoffset = 0;
+	uint8_t *user_src = NULL;
+	uint8_t *k_align_dst = k_align_src;
+	struct	qcedev_cipher_op_req *creq = &areq->cipher_op_req;
+
+
+	if (areq->cipher_op_req.mode == QCEDEV_AES_MODE_CTR)
+		byteoffset = areq->cipher_op_req.byteoffset;
+
+	user_src = (void __user *)areq->cipher_op_req.vbuf.src[0].vaddr;
+	if (user_src && __copy_from_user((k_align_src + byteoffset),
+				(void __user *)user_src,
+				areq->cipher_op_req.vbuf.src[0].len))
+		return -EFAULT;
+
+	k_align_src += areq->cipher_op_req.vbuf.src[0].len;
+
+	for (i = 1; i < areq->cipher_op_req.entries; i++) {
+		user_src =
+			(void __user *)areq->cipher_op_req.vbuf.src[i].vaddr;
+		if (user_src && __copy_from_user(k_align_src,
+					(void __user *)user_src,
+					areq->cipher_op_req.vbuf.src[i].len)) {
+			return -EFAULT;
+		}
+		k_align_src += areq->cipher_op_req.vbuf.src[i].len;
+	}
+
+	/* restore src beginning */
+	k_align_src = k_align_dst;
+	areq->cipher_op_req.data_len += byteoffset;
+
+	areq->cipher_req.creq.src = (struct scatterlist *) &sg_src;
+	areq->cipher_req.creq.dst = (struct scatterlist *) &sg_src;
+
+	/* In place encryption/decryption */
+	sg_set_buf(areq->cipher_req.creq.src,
+					k_align_dst,
+					areq->cipher_op_req.data_len);
+	sg_mark_end(areq->cipher_req.creq.src);
+
+	areq->cipher_req.creq.nbytes = areq->cipher_op_req.data_len;
+	areq->cipher_req.creq.info = areq->cipher_op_req.iv;
+	areq->cipher_op_req.entries = 1;
+
+	err = submit_req(areq, handle);
+
+	/* copy data to destination buffer*/
+	creq->data_len -= byteoffset;
+
+	while (creq->data_len > 0) {
+		if (creq->vbuf.dst[dst_i].len <= creq->data_len) {
+			if (err == 0 && __copy_to_user(
+				(void __user *)creq->vbuf.dst[dst_i].vaddr,
+					(k_align_dst + byteoffset),
+					creq->vbuf.dst[dst_i].len))
+					return -EFAULT;
+
+			k_align_dst += creq->vbuf.dst[dst_i].len +
+						byteoffset;
+			creq->data_len -= creq->vbuf.dst[dst_i].len;
+			dst_i++;
+		} else {
+				if (err == 0 && __copy_to_user(
+				(void __user *)creq->vbuf.dst[dst_i].vaddr,
+				(k_align_dst + byteoffset),
+				creq->data_len))
+					return -EFAULT;
+
+			k_align_dst += creq->data_len;
+			creq->vbuf.dst[dst_i].len -= creq->data_len;
+			creq->vbuf.dst[dst_i].vaddr += creq->data_len;
+			creq->data_len = 0;
+		}
+	}
+	*di = dst_i;
+
+	return err;
+};
+
+static int qcedev_vbuf_ablk_cipher(struct qcedev_async_req *areq,
+						struct qcedev_handle *handle)
+{
+	int err = 0;
+	int di = 0;
+	int i = 0;
+	int j = 0;
+	int k = 0;
+	uint32_t byteoffset = 0;
+	int num_entries = 0;
+	uint32_t total = 0;
+	uint32_t len;
+	uint8_t *k_buf_src = NULL;
+	uint8_t *k_align_src = NULL;
+	uint32_t max_data_xfer;
+	struct qcedev_cipher_op_req *saved_req;
+	struct	qcedev_cipher_op_req *creq = &areq->cipher_op_req;
+
+	/* Verify Source Address's */
+	for (i = 0; i < areq->cipher_op_req.entries; i++)
+		if (!access_ok(VERIFY_READ,
+			(void __user *)areq->cipher_op_req.vbuf.src[i].vaddr,
+					areq->cipher_op_req.vbuf.src[i].len))
+			return -EFAULT;
+
+	/* Verify Destination Address's */
+	if (areq->cipher_op_req.in_place_op != 1)
+		for (i = 0; i < areq->cipher_op_req.entries; i++)
+			if (!access_ok(VERIFY_READ,
+			(void __user *)areq->cipher_op_req.vbuf.dst[i].vaddr,
+					areq->cipher_op_req.vbuf.dst[i].len))
+				return -EFAULT;
+
+	if (areq->cipher_op_req.mode == QCEDEV_AES_MODE_CTR)
+		byteoffset = areq->cipher_op_req.byteoffset;
+	k_buf_src = kmalloc(QCE_MAX_OPER_DATA + CACHE_LINE_SIZE * 2,
+				GFP_KERNEL);
+	if (k_buf_src == NULL) {
+		pr_err("%s: Can't Allocate memory: k_buf_src 0x%x\n",
+			__func__, (uint32_t)k_buf_src);
+		return -ENOMEM;
+	}
+	k_align_src = (uint8_t *) ALIGN(((unsigned int)k_buf_src),
+							CACHE_LINE_SIZE);
+	max_data_xfer = QCE_MAX_OPER_DATA - byteoffset;
+
+	saved_req = kmalloc(sizeof(struct qcedev_cipher_op_req), GFP_KERNEL);
+	if (saved_req == NULL) {
+		pr_err("%s: Can't Allocate memory:saved_req 0x%x\n",
+			__func__, (uint32_t)saved_req);
+		kfree(k_buf_src);
+		return -ENOMEM;
+
+	}
+	memcpy(saved_req, creq, sizeof(struct qcedev_cipher_op_req));
+
+	if (areq->cipher_op_req.data_len > max_data_xfer) {
+		struct qcedev_cipher_op_req req;
+
+		/* save the original req structure */
+		memcpy(&req, creq, sizeof(struct qcedev_cipher_op_req));
+
+		i = 0;
+		/* Address 32 KB  at a time */
+		while ((i < req.entries) && (err == 0)) {
+			if (creq->vbuf.src[i].len > max_data_xfer) {
+				creq->vbuf.src[0].len =	max_data_xfer;
+				if (i > 0) {
+					creq->vbuf.src[0].vaddr =
+						creq->vbuf.src[i].vaddr;
+				}
+
+				creq->data_len = max_data_xfer;
+				creq->entries = 1;
+
+				err = qcedev_vbuf_ablk_cipher_max_xfer(areq,
+						&di, handle, k_align_src);
+				if (err < 0) {
+					kfree(k_buf_src);
+					kfree(saved_req);
+					return err;
+				}
+
+				creq->vbuf.src[i].len =	req.vbuf.src[i].len -
+							max_data_xfer;
+				creq->vbuf.src[i].vaddr =
+						req.vbuf.src[i].vaddr +
+						max_data_xfer;
+				req.vbuf.src[i].vaddr =
+						creq->vbuf.src[i].vaddr;
+				req.vbuf.src[i].len = creq->vbuf.src[i].len;
+
+			} else {
+				total = areq->cipher_op_req.byteoffset;
+				for (j = i; j < req.entries; j++) {
+					num_entries++;
+					if ((total + creq->vbuf.src[j].len)
+							>= max_data_xfer) {
+						creq->vbuf.src[j].len =
+						max_data_xfer - total;
+						total = max_data_xfer;
+						break;
+					}
+					total += creq->vbuf.src[j].len;
+				}
+
+				creq->data_len = total;
+				if (i > 0)
+					for (k = 0; k < num_entries; k++) {
+						creq->vbuf.src[k].len =
+						creq->vbuf.src[i+k].len;
+						creq->vbuf.src[k].vaddr =
+						creq->vbuf.src[i+k].vaddr;
+					}
+				creq->entries =  num_entries;
+
+				i = j;
+				err = qcedev_vbuf_ablk_cipher_max_xfer(areq,
+						&di, handle, k_align_src);
+				if (err < 0) {
+					kfree(k_buf_src);
+					kfree(saved_req);
+					return err;
+				}
+
+				num_entries = 0;
+				areq->cipher_op_req.byteoffset = 0;
+
+				creq->vbuf.src[i].vaddr = req.vbuf.src[i].vaddr
+					+ creq->vbuf.src[i].len;
+				creq->vbuf.src[i].len =	req.vbuf.src[i].len -
+							creq->vbuf.src[i].len;
+
+				req.vbuf.src[i].vaddr =
+						creq->vbuf.src[i].vaddr;
+				req.vbuf.src[i].len = creq->vbuf.src[i].len;
+
+				if (creq->vbuf.src[i].len == 0)
+					i++;
+			}
+
+			areq->cipher_op_req.byteoffset = 0;
+			max_data_xfer = QCE_MAX_OPER_DATA;
+			byteoffset = 0;
+
+		} /* end of while ((i < req.entries) && (err == 0)) */
+	} else
+		err = qcedev_vbuf_ablk_cipher_max_xfer(areq, &di, handle,
+								k_align_src);
+
+	/* Restore the original req structure */
+	for (i = 0; i < saved_req->entries; i++) {
+		creq->vbuf.src[i].len = saved_req->vbuf.src[i].len;
+		creq->vbuf.src[i].vaddr = saved_req->vbuf.src[i].vaddr;
+	}
+	for (len = 0, i = 0; len < saved_req->data_len; i++) {
+		creq->vbuf.dst[i].len = saved_req->vbuf.dst[i].len;
+		creq->vbuf.dst[i].vaddr = saved_req->vbuf.dst[i].vaddr;
+		len += saved_req->vbuf.dst[i].len;
+	}
+	creq->entries = saved_req->entries;
+	creq->data_len = saved_req->data_len;
+	creq->byteoffset = saved_req->byteoffset;
+
+	kfree(saved_req);
+	kfree(k_buf_src);
+	return err;
+
+}
+
+static int qcedev_check_cipher_params(struct qcedev_cipher_op_req *req,
+						struct qcedev_control *podev)
+{
+	if ((req->entries == 0) || (req->data_len == 0))
+		goto error;
+	if ((req->alg >= QCEDEV_ALG_LAST) ||
+		(req->mode >= QCEDEV_AES_DES_MODE_LAST))
+		goto error;
+	if (req->alg == QCEDEV_ALG_AES) {
+		if ((req->mode == QCEDEV_AES_MODE_XTS) &&
+					(!podev->ce_support.aes_xts))
+			goto error;
+		/* if intending to use HW key make sure key fields are set
+		 * correctly and HW key is indeed supported in target
+		 */
+		if (req->encklen == 0) {
+			int i;
+			for (i = 0; i < QCEDEV_MAX_KEY_SIZE; i++)
+				if (req->enckey[i])
+					goto error;
+			if ((req->op != QCEDEV_OPER_ENC_NO_KEY) &&
+				(req->op != QCEDEV_OPER_DEC_NO_KEY))
+				if (!podev->platform_support.hw_key_support)
+					goto error;
+		} else {
+			if (req->encklen == QCEDEV_AES_KEY_192) {
+				if (!podev->ce_support.aes_key_192)
+					goto error;
+			} else {
+				/* if not using HW key make sure key
+				 * length is valid
+				 */
+				if (!((req->encklen == QCEDEV_AES_KEY_128) ||
+					(req->encklen == QCEDEV_AES_KEY_256)))
+					goto error;
+			}
+		}
+	}
+	/* if using a byteoffset, make sure it is CTR mode using vbuf */
+	if (req->byteoffset) {
+		if (req->mode != QCEDEV_AES_MODE_CTR)
+			goto error;
+		else { /* if using CTR mode make sure not using Pmem */
+			if (req->use_pmem)
+				goto error;
+		}
+	}
+	/* if using PMEM with non-zero byteoffset, ensure it is in_place_op */
+	if (req->use_pmem) {
+		if (!req->in_place_op)
+			goto error;
+	}
+	/* Ensure zer ivlen for ECB  mode  */
+	if (req->ivlen != 0) {
+		if ((req->mode == QCEDEV_AES_MODE_ECB) ||
+				(req->mode == QCEDEV_DES_MODE_ECB))
+			goto error;
+	} else {
+		if ((req->mode != QCEDEV_AES_MODE_ECB) &&
+				(req->mode != QCEDEV_DES_MODE_ECB))
+			goto error;
+	}
+
+	return 0;
+error:
+	return -EINVAL;
+
+}
+
+static int qcedev_check_sha_params(struct qcedev_sha_op_req *req,
+						struct qcedev_control *podev)
+{
+	if ((req->alg == QCEDEV_ALG_AES_CMAC) &&
+				(!podev->ce_support.cmac))
+		goto sha_error;
+
+	if ((req->entries == 0) || (req->data_len == 0))
+		goto sha_error;
+
+	if (req->alg >= QCEDEV_ALG_SHA_ALG_LAST)
+		goto sha_error;
+
+	return 0;
+sha_error:
+	return -EINVAL;
+}
+
+static long qcedev_ioctl(struct file *file, unsigned cmd, unsigned long arg)
+{
+	int err = 0;
+	struct qcedev_handle *handle;
+	struct qcedev_control *podev;
+	struct qcedev_async_req qcedev_areq;
+	struct qcedev_stat *pstat;
+
+	handle =  file->private_data;
+	podev =  handle->cntl;
+	qcedev_areq.handle = handle;
+	if (podev == NULL || podev->magic != QCEDEV_MAGIC) {
+		pr_err("%s: invalid handle %p\n",
+			__func__, podev);
+		return -ENOENT;
+	}
+
+	/* Verify user arguments. */
+	if (_IOC_TYPE(cmd) != QCEDEV_IOC_MAGIC)
+		return -ENOTTY;
+
+	init_completion(&qcedev_areq.complete);
+	pstat = &_qcedev_stat[podev->pdev->id];
+
+	switch (cmd) {
+	case QCEDEV_IOCTL_LOCK_CE:
+		if (podev->platform_support.ce_shared)
+			err = qcedev_lock_ce(podev);
+		else
+			err = -ENOTTY;
+		break;
+	case QCEDEV_IOCTL_UNLOCK_CE:
+		if (podev->platform_support.ce_shared)
+			err = qcedev_unlock_ce(podev);
+		else
+			err = -ENOTTY;
+		break;
+	case QCEDEV_IOCTL_ENC_REQ:
+	case QCEDEV_IOCTL_DEC_REQ:
+		if (!access_ok(VERIFY_WRITE, (void __user *)arg,
+				sizeof(struct qcedev_cipher_op_req)))
+			return -EFAULT;
+
+		if (__copy_from_user(&qcedev_areq.cipher_op_req,
+				(void __user *)arg,
+				sizeof(struct qcedev_cipher_op_req)))
+			return -EFAULT;
+		qcedev_areq.op_type = QCEDEV_CRYPTO_OPER_CIPHER;
+
+		if (qcedev_check_cipher_params(&qcedev_areq.cipher_op_req,
+				podev))
+			return -EINVAL;
+
+		if (qcedev_areq.cipher_op_req.use_pmem)
+			err = qcedev_pmem_ablk_cipher(&qcedev_areq, handle);
+		else
+			err = qcedev_vbuf_ablk_cipher(&qcedev_areq, handle);
+		if (err)
+			return err;
+		if (__copy_to_user((void __user *)arg,
+					&qcedev_areq.cipher_op_req,
+					sizeof(struct qcedev_cipher_op_req)))
+				return -EFAULT;
+		break;
+
+	case QCEDEV_IOCTL_SHA_INIT_REQ:
+
+		if (!access_ok(VERIFY_WRITE, (void __user *)arg,
+				sizeof(struct qcedev_sha_op_req)))
+			return -EFAULT;
+
+		if (__copy_from_user(&qcedev_areq.sha_op_req,
+					(void __user *)arg,
+					sizeof(struct qcedev_sha_op_req)))
+			return -EFAULT;
+		if (qcedev_check_sha_params(&qcedev_areq.sha_op_req, podev))
+			return -EINVAL;
+		qcedev_areq.op_type = QCEDEV_CRYPTO_OPER_SHA;
+		err = qcedev_hash_init(&qcedev_areq, handle);
+		if (err)
+			return err;
+		if (__copy_to_user((void __user *)arg, &qcedev_areq.sha_op_req,
+					sizeof(struct qcedev_sha_op_req)))
+				return -EFAULT;
+		break;
+	case QCEDEV_IOCTL_GET_CMAC_REQ:
+		if (!podev->ce_support.cmac)
+			return -ENOTTY;
+	case QCEDEV_IOCTL_SHA_UPDATE_REQ:
+		if (!access_ok(VERIFY_WRITE, (void __user *)arg,
+				sizeof(struct qcedev_sha_op_req)))
+			return -EFAULT;
+
+		if (__copy_from_user(&qcedev_areq.sha_op_req,
+					(void __user *)arg,
+					sizeof(struct qcedev_sha_op_req)))
+			return -EFAULT;
+		if (qcedev_check_sha_params(&qcedev_areq.sha_op_req, podev))
+			return -EINVAL;
+		qcedev_areq.op_type = QCEDEV_CRYPTO_OPER_SHA;
+
+		if (qcedev_areq.sha_op_req.alg == QCEDEV_ALG_AES_CMAC) {
+			err = qcedev_hash_cmac(&qcedev_areq, handle);
+			if (err)
+				return err;
+		} else {
+			err = qcedev_hash_update(&qcedev_areq, handle);
+			if (err)
+				return err;
+		}
+
+		memcpy(&qcedev_areq.sha_op_req.digest[0],
+				&handle->sha_ctxt.digest[0],
+				handle->sha_ctxt.diglen);
+		if (__copy_to_user((void __user *)arg, &qcedev_areq.sha_op_req,
+					sizeof(struct qcedev_sha_op_req)))
+			return -EFAULT;
+		break;
+
+	case QCEDEV_IOCTL_SHA_FINAL_REQ:
+
+		if (!access_ok(VERIFY_WRITE, (void __user *)arg,
+				sizeof(struct qcedev_sha_op_req)))
+			return -EFAULT;
+
+		if (__copy_from_user(&qcedev_areq.sha_op_req,
+					(void __user *)arg,
+					sizeof(struct qcedev_sha_op_req)))
+			return -EFAULT;
+		if (qcedev_check_sha_params(&qcedev_areq.sha_op_req, podev))
+			return -EINVAL;
+		qcedev_areq.op_type = QCEDEV_CRYPTO_OPER_SHA;
+		err = qcedev_hash_final(&qcedev_areq, handle);
+		if (err)
+			return err;
+		qcedev_areq.sha_op_req.diglen = handle->sha_ctxt.diglen;
+		memcpy(&qcedev_areq.sha_op_req.digest[0],
+				&handle->sha_ctxt.digest[0],
+				handle->sha_ctxt.diglen);
+		if (__copy_to_user((void __user *)arg, &qcedev_areq.sha_op_req,
+					sizeof(struct qcedev_sha_op_req)))
+			return -EFAULT;
+		break;
+
+	case QCEDEV_IOCTL_GET_SHA_REQ:
+
+		if (!access_ok(VERIFY_WRITE, (void __user *)arg,
+				sizeof(struct qcedev_sha_op_req)))
+			return -EFAULT;
+
+		if (__copy_from_user(&qcedev_areq.sha_op_req,
+					(void __user *)arg,
+					sizeof(struct qcedev_sha_op_req)))
+			return -EFAULT;
+		if (qcedev_check_sha_params(&qcedev_areq.sha_op_req, podev))
+			return -EINVAL;
+		qcedev_areq.op_type = QCEDEV_CRYPTO_OPER_SHA;
+		qcedev_hash_init(&qcedev_areq, handle);
+		err = qcedev_hash_update(&qcedev_areq, handle);
+		if (err)
+			return err;
+		err = qcedev_hash_final(&qcedev_areq, handle);
+		if (err)
+			return err;
+		qcedev_areq.sha_op_req.diglen =	handle->sha_ctxt.diglen;
+		memcpy(&qcedev_areq.sha_op_req.digest[0],
+				&handle->sha_ctxt.digest[0],
+				handle->sha_ctxt.diglen);
+		if (__copy_to_user((void __user *)arg, &qcedev_areq.sha_op_req,
+					sizeof(struct qcedev_sha_op_req)))
+			return -EFAULT;
+		break;
+
+	default:
+		return -ENOTTY;
+	}
+
+	return err;
+}
+
+static int qcedev_probe(struct platform_device *pdev)
+{
+	void *handle = NULL;
+	int rc = 0;
+	struct qcedev_control *podev;
+	struct msm_ce_hw_support *platform_support;
+
+	if (pdev->id >= MAX_QCE_DEVICE) {
+		pr_err("%s: device id %d  exceeds allowed %d\n",
+			__func__, pdev->id, MAX_QCE_DEVICE);
+		return -ENOENT;
+	}
+	podev = &qce_dev[pdev->id];
+
+	platform_support = (struct msm_ce_hw_support *)pdev->dev.platform_data;
+	podev->platform_support.ce_shared = platform_support->ce_shared;
+	podev->platform_support.shared_ce_resource =
+				platform_support->shared_ce_resource;
+	podev->platform_support.hw_key_support =
+				platform_support->hw_key_support;
+	podev->platform_support.bus_scale_table =
+				platform_support->bus_scale_table;
+	podev->ce_lock_count = 0;
+	podev->high_bw_req_count = 0;
+	INIT_LIST_HEAD(&podev->ready_commands);
+	podev->active_command = NULL;
+
+	spin_lock_init(&podev->lock);
+
+	tasklet_init(&podev->done_tasklet, req_done, (unsigned long)podev);
+
+	/* open qce */
+	handle = qce_open(pdev, &rc);
+	if (handle == NULL) {
+		platform_set_drvdata(pdev, NULL);
+		return rc;
+	}
+
+	podev->qce = handle;
+	podev->pdev = pdev;
+	platform_set_drvdata(pdev, podev);
+	qce_hw_support(podev->qce, &podev->ce_support);
+
+	if (podev->platform_support.bus_scale_table != NULL) {
+		podev->bus_scale_handle =
+			msm_bus_scale_register_client(
+				(struct msm_bus_scale_pdata *)
+				podev->platform_support.bus_scale_table);
+		if (!podev->bus_scale_handle) {
+			printk(KERN_ERR "%s not able to get bus scale\n",
+								__func__);
+			rc =  -ENOMEM;
+			goto err;
+		}
+	}
+	rc = misc_register(&podev->miscdevice);
+
+	if (rc >= 0)
+		return 0;
+	else
+		if (podev->platform_support.bus_scale_table != NULL)
+			msm_bus_scale_unregister_client(
+						podev->bus_scale_handle);
+err:
+
+	if (handle)
+		qce_close(handle);
+	platform_set_drvdata(pdev, NULL);
+	podev->qce = NULL;
+	podev->pdev = NULL;
+	return rc;
+};
+
+static int qcedev_remove(struct platform_device *pdev)
+{
+	struct qcedev_control *podev;
+
+	podev = platform_get_drvdata(pdev);
+	if (!podev)
+		return 0;
+	if (podev->qce)
+		qce_close(podev->qce);
+
+	if (podev->platform_support.bus_scale_table != NULL)
+		msm_bus_scale_unregister_client(podev->bus_scale_handle);
+
+	if (podev->miscdevice.minor != MISC_DYNAMIC_MINOR)
+		misc_deregister(&podev->miscdevice);
+	tasklet_kill(&podev->done_tasklet);
+	return 0;
+};
+
+static struct platform_driver qcedev_plat_driver = {
+	.probe = qcedev_probe,
+	.remove = qcedev_remove,
+	.driver = {
+		.name = "qce",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int _disp_stats(int id)
+{
+	struct qcedev_stat *pstat;
+	int len = 0;
+
+	pstat = &_qcedev_stat[id];
+	len = snprintf(_debug_read_buf, DEBUG_MAX_RW_BUF - 1,
+			"\nQualcomm QCE dev driver %d Statistics:\n",
+				id + 1);
+
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   Encryption operation success       : %d\n",
+					pstat->qcedev_enc_success);
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   Encryption operation fail   : %d\n",
+					pstat->qcedev_enc_fail);
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   Decryption operation success     : %d\n",
+					pstat->qcedev_dec_success);
+
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   Encryption operation fail          : %d\n",
+					pstat->qcedev_dec_fail);
+
+	return len;
+}
+
+static int _debug_stats_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t _debug_stats_read(struct file *file, char __user *buf,
+			size_t count, loff_t *ppos)
+{
+	int rc = -EINVAL;
+	int qcedev = *((int *) file->private_data);
+	int len;
+
+	len = _disp_stats(qcedev);
+
+	rc = simple_read_from_buffer((void __user *) buf, len,
+			ppos, (void *) _debug_read_buf, len);
+
+	return rc;
+}
+
+static ssize_t _debug_stats_write(struct file *file, const char __user *buf,
+			size_t count, loff_t *ppos)
+{
+
+	int qcedev = *((int *) file->private_data);
+
+	memset((char *)&_qcedev_stat[qcedev], 0, sizeof(struct qcedev_stat));
+	return count;
+};
+
+static const struct file_operations _debug_stats_ops = {
+	.open =         _debug_stats_open,
+	.read =         _debug_stats_read,
+	.write =        _debug_stats_write,
+};
+
+static int _qcedev_debug_init(void)
+{
+	int rc;
+	char name[DEBUG_MAX_FNAME];
+	int i;
+	struct dentry *dent;
+
+	_debug_dent = debugfs_create_dir("qcedev", NULL);
+	if (IS_ERR(_debug_dent)) {
+		pr_err("qcedev debugfs_create_dir fail, error %ld\n",
+				PTR_ERR(_debug_dent));
+		return PTR_ERR(_debug_dent);
+	}
+
+	for (i = 0; i < MAX_QCE_DEVICE; i++) {
+		snprintf(name, DEBUG_MAX_FNAME-1, "stats-%d", i+1);
+		_debug_qcedev[i] = i;
+		dent = debugfs_create_file(name, 0644, _debug_dent,
+				&_debug_qcedev[i], &_debug_stats_ops);
+		if (dent == NULL) {
+			pr_err("qcedev debugfs_create_file fail, error %ld\n",
+					PTR_ERR(dent));
+			rc = PTR_ERR(dent);
+			goto err;
+		}
+	}
+	return 0;
+err:
+	debugfs_remove_recursive(_debug_dent);
+	return rc;
+}
+
+static int qcedev_init(void)
+{
+	int rc;
+
+	rc = _qcedev_debug_init();
+	if (rc)
+		return rc;
+	return platform_driver_register(&qcedev_plat_driver);
+}
+
+static void qcedev_exit(void)
+{
+	debugfs_remove_recursive(_debug_dent);
+	platform_driver_unregister(&qcedev_plat_driver);
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Mona Hossain <mhossain@codeaurora.org>");
+MODULE_DESCRIPTION("Qualcomm DEV Crypto driver");
+MODULE_VERSION("1.26");
+
+module_init(qcedev_init);
+module_exit(qcedev_exit);
diff --git a/drivers/crypto/msm/qcrypto.c b/drivers/crypto/msm/qcrypto.c
new file mode 100644
index 0000000..168edaa
--- /dev/null
+++ b/drivers/crypto/msm/qcrypto.c
@@ -0,0 +1,3367 @@
+/* Qualcomm Crypto driver
+ *
+ * Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/crypto.h>
+#include <linux/kernel.h>
+#include <linux/rtnetlink.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/debugfs.h>
+
+#include <crypto/ctr.h>
+#include <crypto/des.h>
+#include <crypto/aes.h>
+#include <crypto/sha.h>
+#include <crypto/hash.h>
+#include <crypto/algapi.h>
+#include <crypto/aead.h>
+#include <crypto/authenc.h>
+#include <crypto/scatterwalk.h>
+#include <crypto/internal/hash.h>
+
+#include <mach/scm.h>
+#include <linux/platform_data/qcom_crypto_device.h>
+#include <mach/msm_bus.h>
+#include "qce.h"
+
+
+#define MAX_CRYPTO_DEVICE 3
+#define DEBUG_MAX_FNAME  16
+#define DEBUG_MAX_RW_BUF 1024
+
+struct crypto_stat {
+	u32 aead_sha1_aes_enc;
+	u32 aead_sha1_aes_dec;
+	u32 aead_sha1_des_enc;
+	u32 aead_sha1_des_dec;
+	u32 aead_sha1_3des_enc;
+	u32 aead_sha1_3des_dec;
+	u32 aead_op_success;
+	u32 aead_op_fail;
+	u32 ablk_cipher_aes_enc;
+	u32 ablk_cipher_aes_dec;
+	u32 ablk_cipher_des_enc;
+	u32 ablk_cipher_des_dec;
+	u32 ablk_cipher_3des_enc;
+	u32 ablk_cipher_3des_dec;
+	u32 ablk_cipher_op_success;
+	u32 ablk_cipher_op_fail;
+	u32 sha1_digest;
+	u32 sha256_digest;
+	u32 sha_op_success;
+	u32 sha_op_fail;
+	u32 sha1_hmac_digest;
+	u32 sha256_hmac_digest;
+	u32 sha_hmac_op_success;
+	u32 sha_hmac_op_fail;
+};
+static struct crypto_stat _qcrypto_stat[MAX_CRYPTO_DEVICE];
+static struct dentry *_debug_dent;
+static char _debug_read_buf[DEBUG_MAX_RW_BUF];
+
+struct crypto_priv {
+	/* CE features supported by target device*/
+	struct msm_ce_hw_support platform_support;
+
+	/* CE features/algorithms supported by HW engine*/
+	struct ce_hw_support ce_support;
+
+	uint32_t  bus_scale_handle;
+	/* the lock protects queue and req*/
+	spinlock_t lock;
+
+	/* qce handle */
+	void *qce;
+
+	/* list of  registered algorithms */
+	struct list_head alg_list;
+
+	/* platform device */
+	struct platform_device *pdev;
+
+	/* current active request */
+	struct crypto_async_request *req;
+	int res;
+
+	/* request queue */
+	struct crypto_queue queue;
+
+	uint32_t ce_lock_count;
+	uint32_t high_bw_req_count;
+
+	struct work_struct unlock_ce_ws;
+
+	struct tasklet_struct done_tasklet;
+};
+
+
+/*-------------------------------------------------------------------------
+* Resource Locking Service
+* ------------------------------------------------------------------------*/
+#define QCRYPTO_CMD_ID				1
+#define QCRYPTO_CE_LOCK_CMD			1
+#define QCRYPTO_CE_UNLOCK_CMD			0
+#define NUM_RETRY				1000
+#define CE_BUSY				        55
+
+static DEFINE_MUTEX(sent_bw_req);
+
+static int qcrypto_scm_cmd(int resource, int cmd, int *response)
+{
+#ifdef CONFIG_MSM_SCM
+
+	struct {
+		int resource;
+		int cmd;
+	} cmd_buf;
+
+	cmd_buf.resource = resource;
+	cmd_buf.cmd = cmd;
+
+	return scm_call(SCM_SVC_TZ, QCRYPTO_CMD_ID, &cmd_buf,
+		sizeof(cmd_buf), response, sizeof(*response));
+
+#else
+	return 0;
+#endif
+}
+
+static void qcrypto_unlock_ce(struct work_struct *work)
+{
+	int response = 0;
+	unsigned long flags;
+	struct crypto_priv *cp = container_of(work, struct crypto_priv,
+							unlock_ce_ws);
+	if (cp->ce_lock_count == 1)
+		BUG_ON(qcrypto_scm_cmd(cp->platform_support.shared_ce_resource,
+				QCRYPTO_CE_UNLOCK_CMD, &response) != 0);
+	spin_lock_irqsave(&cp->lock, flags);
+	cp->ce_lock_count--;
+	spin_unlock_irqrestore(&cp->lock, flags);
+}
+
+static int qcrypto_lock_ce(struct crypto_priv *cp)
+{
+	unsigned long flags;
+	int response = -CE_BUSY;
+	int i = 0;
+
+	if (cp->ce_lock_count == 0) {
+		do {
+			if (qcrypto_scm_cmd(
+				cp->platform_support.shared_ce_resource,
+				QCRYPTO_CE_LOCK_CMD, &response)) {
+				response = -EINVAL;
+				break;
+			}
+		} while ((response == -CE_BUSY) && (i++ < NUM_RETRY));
+
+		if ((response == -CE_BUSY) && (i >= NUM_RETRY))
+			return -EUSERS;
+		if (response < 0)
+			return -EINVAL;
+	}
+	spin_lock_irqsave(&cp->lock, flags);
+	cp->ce_lock_count++;
+	spin_unlock_irqrestore(&cp->lock, flags);
+
+
+	return 0;
+}
+
+enum qcrypto_alg_type {
+	QCRYPTO_ALG_CIPHER	= 0,
+	QCRYPTO_ALG_SHA	= 1,
+	QCRYPTO_ALG_LAST
+};
+
+struct qcrypto_alg {
+	struct list_head entry;
+	struct crypto_alg cipher_alg;
+	struct ahash_alg sha_alg;
+	enum qcrypto_alg_type alg_type;
+	struct crypto_priv *cp;
+};
+
+#define QCRYPTO_MAX_KEY_SIZE	64
+/* max of AES_BLOCK_SIZE, DES3_EDE_BLOCK_SIZE */
+#define QCRYPTO_MAX_IV_LENGTH	16
+
+struct qcrypto_cipher_ctx {
+	u8 auth_key[QCRYPTO_MAX_KEY_SIZE];
+	u8 iv[QCRYPTO_MAX_IV_LENGTH];
+
+	u8 enc_key[QCRYPTO_MAX_KEY_SIZE];
+	unsigned int enc_key_len;
+
+	unsigned int authsize;
+	unsigned int auth_key_len;
+
+	struct crypto_priv *cp;
+};
+
+struct qcrypto_cipher_req_ctx {
+	u8 *iv;
+	unsigned int ivsize;
+	int  aead;
+	struct scatterlist asg;		/* Formatted associated data sg  */
+	unsigned char *assoc;		/* Pointer to formatted assoc data */
+	unsigned int assoclen;		/* Save Unformatted assoc data length */
+	struct scatterlist *assoc_sg;	/* Save Unformatted assoc data sg */
+	enum qce_cipher_alg_enum alg;
+	enum qce_cipher_dir_enum dir;
+	enum qce_cipher_mode_enum mode;
+};
+
+#define SHA_MAX_BLOCK_SIZE      SHA256_BLOCK_SIZE
+#define SHA_MAX_STATE_SIZE	(SHA256_DIGEST_SIZE / sizeof(u32))
+#define SHA_MAX_DIGEST_SIZE	 SHA256_DIGEST_SIZE
+
+static uint8_t  _std_init_vector_sha1_uint8[] =   {
+	0x67, 0x45, 0x23, 0x01, 0xEF, 0xCD, 0xAB, 0x89,
+	0x98, 0xBA, 0xDC, 0xFE, 0x10, 0x32, 0x54, 0x76,
+	0xC3, 0xD2, 0xE1, 0xF0
+};
+
+/* standard initialization vector for SHA-256, source: FIPS 180-2 */
+static uint8_t _std_init_vector_sha256_uint8[] = {
+	0x6A, 0x09, 0xE6, 0x67, 0xBB, 0x67, 0xAE, 0x85,
+	0x3C, 0x6E, 0xF3, 0x72, 0xA5, 0x4F, 0xF5, 0x3A,
+	0x51, 0x0E, 0x52, 0x7F, 0x9B, 0x05, 0x68, 0x8C,
+	0x1F, 0x83, 0xD9, 0xAB, 0x5B, 0xE0, 0xCD, 0x19
+};
+
+struct qcrypto_sha_ctx {
+	enum qce_hash_alg_enum  alg;
+	uint32_t		byte_count[4];
+	uint8_t			digest[SHA_MAX_DIGEST_SIZE];
+	uint32_t		diglen;
+	uint8_t			*tmp_tbuf;
+	uint8_t			*trailing_buf;
+	uint8_t			*in_buf;
+	uint32_t		authkey_in_len;
+	uint32_t		trailing_buf_len;
+	uint8_t			first_blk;
+	uint8_t			last_blk;
+	uint8_t			authkey[SHA_MAX_BLOCK_SIZE];
+	struct ahash_request *ahash_req;
+	struct completion ahash_req_complete;
+	struct scatterlist *sg;
+	struct scatterlist tmp_sg;
+	struct crypto_priv *cp;
+};
+
+struct qcrypto_sha_req_ctx {
+	union {
+		struct sha1_state sha1_state_ctx;
+		struct sha256_state sha256_state_ctx;
+	};
+	struct scatterlist *src;
+	uint32_t nbytes;
+};
+
+static void _byte_stream_to_words(uint32_t *iv, unsigned char *b,
+		unsigned int len)
+{
+	unsigned n;
+
+	n = len  / sizeof(uint32_t) ;
+	for (; n > 0; n--) {
+		*iv =  ((*b << 24)      & 0xff000000) |
+				(((*(b+1)) << 16) & 0xff0000)   |
+				(((*(b+2)) << 8) & 0xff00)     |
+				(*(b+3)          & 0xff);
+		b += sizeof(uint32_t);
+		iv++;
+	}
+
+	n = len %  sizeof(uint32_t);
+	if (n == 3) {
+		*iv = ((*b << 24) & 0xff000000) |
+				(((*(b+1)) << 16) & 0xff0000)   |
+				(((*(b+2)) << 8) & 0xff00)     ;
+	} else if (n == 2) {
+		*iv = ((*b << 24) & 0xff000000) |
+				(((*(b+1)) << 16) & 0xff0000)   ;
+	} else if (n == 1) {
+		*iv = ((*b << 24) & 0xff000000) ;
+	}
+}
+
+static void _words_to_byte_stream(uint32_t *iv, unsigned char *b,
+		unsigned int len)
+{
+	unsigned n = len  / sizeof(uint32_t);
+
+	for (; n > 0; n--) {
+		*b++ = (unsigned char) ((*iv >> 24)   & 0xff);
+		*b++ = (unsigned char) ((*iv >> 16)   & 0xff);
+		*b++ = (unsigned char) ((*iv >> 8)    & 0xff);
+		*b++ = (unsigned char) (*iv           & 0xff);
+		iv++;
+	}
+	n = len % sizeof(uint32_t);
+	if (n == 3) {
+		*b++ = (unsigned char) ((*iv >> 24)   & 0xff);
+		*b++ = (unsigned char) ((*iv >> 16)   & 0xff);
+		*b =   (unsigned char) ((*iv >> 8)    & 0xff);
+	} else if (n == 2) {
+		*b++ = (unsigned char) ((*iv >> 24)   & 0xff);
+		*b =   (unsigned char) ((*iv >> 16)   & 0xff);
+	} else if (n == 1) {
+		*b =   (unsigned char) ((*iv >> 24)   & 0xff);
+	}
+}
+
+static void qcrypto_ce_high_bw_req(struct crypto_priv *cp, bool high_bw_req)
+{
+	int ret = 0;
+
+	mutex_lock(&sent_bw_req);
+	if (high_bw_req) {
+		if (cp->high_bw_req_count == 0)
+			ret = msm_bus_scale_client_update_request(
+				cp->bus_scale_handle, 1);
+		if (ret)
+			pr_err("%s Unable to set to high bandwidth\n",
+							__func__);
+		cp->high_bw_req_count++;
+	} else {
+		if (cp->high_bw_req_count == 1)
+			ret = msm_bus_scale_client_update_request(
+				cp->bus_scale_handle, 0);
+		if (ret)
+			pr_err("%s Unable to set to low bandwidth\n",
+							__func__);
+		cp->high_bw_req_count--;
+	}
+	mutex_unlock(&sent_bw_req);
+}
+
+static void _start_qcrypto_process(struct crypto_priv *cp);
+
+static struct qcrypto_alg *_qcrypto_sha_alg_alloc(struct crypto_priv *cp,
+		struct ahash_alg *template)
+{
+	struct qcrypto_alg *q_alg;
+	q_alg = kzalloc(sizeof(struct qcrypto_alg), GFP_KERNEL);
+	if (!q_alg) {
+		pr_err("qcrypto Memory allocation of q_alg FAIL, error %ld\n",
+				PTR_ERR(q_alg));
+		return ERR_PTR(-ENOMEM);
+	}
+
+	q_alg->alg_type = QCRYPTO_ALG_SHA;
+	q_alg->sha_alg = *template;
+	q_alg->cp = cp;
+
+	return q_alg;
+};
+
+static struct qcrypto_alg *_qcrypto_cipher_alg_alloc(struct crypto_priv *cp,
+		struct crypto_alg *template)
+{
+	struct qcrypto_alg *q_alg;
+
+	q_alg = kzalloc(sizeof(struct qcrypto_alg), GFP_KERNEL);
+	if (!q_alg) {
+		pr_err("qcrypto Memory allocation of q_alg FAIL, error %ld\n",
+				PTR_ERR(q_alg));
+		return ERR_PTR(-ENOMEM);
+	}
+
+	q_alg->alg_type = QCRYPTO_ALG_CIPHER;
+	q_alg->cipher_alg = *template;
+	q_alg->cp = cp;
+
+	return q_alg;
+};
+
+static int _qcrypto_cipher_cra_init(struct crypto_tfm *tfm)
+{
+	struct crypto_alg *alg = tfm->__crt_alg;
+	struct qcrypto_alg *q_alg;
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(tfm);
+
+	q_alg = container_of(alg, struct qcrypto_alg, cipher_alg);
+
+	/* update context with ptr to cp */
+	ctx->cp = q_alg->cp;
+
+	/* random first IV */
+	get_random_bytes(ctx->iv, QCRYPTO_MAX_IV_LENGTH);
+	if (ctx->cp->platform_support.bus_scale_table != NULL)
+		qcrypto_ce_high_bw_req(ctx->cp, true);
+
+	return 0;
+};
+
+static int _qcrypto_ahash_cra_init(struct crypto_tfm *tfm)
+{
+	struct crypto_ahash *ahash = __crypto_ahash_cast(tfm);
+	struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(tfm);
+	struct ahash_alg *alg =	container_of(crypto_hash_alg_common(ahash),
+						struct ahash_alg, halg);
+	struct qcrypto_alg *q_alg = container_of(alg, struct qcrypto_alg,
+								sha_alg);
+
+	crypto_ahash_set_reqsize(ahash, sizeof(struct qcrypto_sha_req_ctx));
+	/* update context with ptr to cp */
+	sha_ctx->cp = q_alg->cp;
+	sha_ctx->sg = NULL;
+	sha_ctx->tmp_tbuf = kzalloc(SHA_MAX_BLOCK_SIZE +
+					SHA_MAX_DIGEST_SIZE, GFP_KERNEL);
+	if (sha_ctx->tmp_tbuf == NULL) {
+		pr_err("qcrypto Can't Allocate mem: sha_ctx->tmp_tbuf, error %ld\n",
+			PTR_ERR(sha_ctx->tmp_tbuf));
+		return -ENOMEM;
+	}
+
+	sha_ctx->trailing_buf = kzalloc(SHA_MAX_BLOCK_SIZE, GFP_KERNEL);
+	if (sha_ctx->trailing_buf == NULL) {
+		kfree(sha_ctx->tmp_tbuf);
+		sha_ctx->tmp_tbuf = NULL;
+		pr_err("qcrypto Can't Allocate mem: sha_ctx->trailing_buf, error %ld\n",
+			PTR_ERR(sha_ctx->trailing_buf));
+		return -ENOMEM;
+	}
+
+	sha_ctx->ahash_req = NULL;
+	if (sha_ctx->cp->platform_support.bus_scale_table != NULL)
+		qcrypto_ce_high_bw_req(sha_ctx->cp, true);
+
+	return 0;
+};
+
+static void _qcrypto_ahash_cra_exit(struct crypto_tfm *tfm)
+{
+	struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(tfm);
+
+	kfree(sha_ctx->tmp_tbuf);
+	sha_ctx->tmp_tbuf = NULL;
+	kfree(sha_ctx->trailing_buf);
+	sha_ctx->trailing_buf = NULL;
+	if (sha_ctx->sg != NULL) {
+		kfree(sha_ctx->sg);
+		sha_ctx->sg = NULL;
+	}
+	if (sha_ctx->ahash_req != NULL) {
+		ahash_request_free(sha_ctx->ahash_req);
+		sha_ctx->ahash_req = NULL;
+	}
+	if (sha_ctx->cp->platform_support.bus_scale_table != NULL)
+		qcrypto_ce_high_bw_req(sha_ctx->cp, false);
+};
+
+
+static void _crypto_sha_hmac_ahash_req_complete(
+	struct crypto_async_request *req, int err);
+
+static int _qcrypto_ahash_hmac_cra_init(struct crypto_tfm *tfm)
+{
+	struct crypto_ahash *ahash = __crypto_ahash_cast(tfm);
+	struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(tfm);
+	int ret = 0;
+
+	ret = _qcrypto_ahash_cra_init(tfm);
+	if (ret)
+		return ret;
+	sha_ctx->ahash_req = ahash_request_alloc(ahash, GFP_KERNEL);
+
+	if (sha_ctx->ahash_req == NULL) {
+		_qcrypto_ahash_cra_exit(tfm);
+		return -ENOMEM;
+	}
+
+	init_completion(&sha_ctx->ahash_req_complete);
+	ahash_request_set_callback(sha_ctx->ahash_req,
+				CRYPTO_TFM_REQ_MAY_BACKLOG,
+				_crypto_sha_hmac_ahash_req_complete,
+				&sha_ctx->ahash_req_complete);
+	crypto_ahash_clear_flags(ahash, ~0);
+
+	if (sha_ctx->cp->platform_support.bus_scale_table != NULL)
+		qcrypto_ce_high_bw_req(sha_ctx->cp, true);
+
+	return 0;
+};
+
+static int _qcrypto_cra_ablkcipher_init(struct crypto_tfm *tfm)
+{
+	tfm->crt_ablkcipher.reqsize = sizeof(struct qcrypto_cipher_req_ctx);
+	return _qcrypto_cipher_cra_init(tfm);
+};
+
+static int _qcrypto_cra_aead_init(struct crypto_tfm *tfm)
+{
+	tfm->crt_aead.reqsize = sizeof(struct qcrypto_cipher_req_ctx);
+	return _qcrypto_cipher_cra_init(tfm);
+};
+
+static void _qcrypto_cra_ablkcipher_exit(struct crypto_tfm *tfm)
+{
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(tfm);
+
+	if (ctx->cp->platform_support.bus_scale_table != NULL)
+		qcrypto_ce_high_bw_req(ctx->cp, false);
+};
+
+static void _qcrypto_cra_aead_exit(struct crypto_tfm *tfm)
+{
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(tfm);
+
+	if (ctx->cp->platform_support.bus_scale_table != NULL)
+		qcrypto_ce_high_bw_req(ctx->cp, false);
+};
+
+static int _disp_stats(int id)
+{
+	struct crypto_stat *pstat;
+	int len = 0;
+
+	pstat = &_qcrypto_stat[id];
+	len = snprintf(_debug_read_buf, DEBUG_MAX_RW_BUF - 1,
+			"\nQualcomm crypto accelerator %d Statistics:\n",
+				id + 1);
+
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   ABLK AES CIPHER encryption   : %d\n",
+					pstat->ablk_cipher_aes_enc);
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   ABLK AES CIPHER decryption   : %d\n",
+					pstat->ablk_cipher_aes_dec);
+
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   ABLK DES CIPHER encryption   : %d\n",
+					pstat->ablk_cipher_des_enc);
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   ABLK DES CIPHER decryption   : %d\n",
+					pstat->ablk_cipher_des_dec);
+
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   ABLK 3DES CIPHER encryption  : %d\n",
+					pstat->ablk_cipher_3des_enc);
+
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   ABLK 3DES CIPHER decryption  : %d\n",
+					pstat->ablk_cipher_3des_dec);
+
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   ABLK CIPHER operation success: %d\n",
+					pstat->ablk_cipher_op_success);
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   ABLK CIPHER operation fail   : %d\n",
+					pstat->ablk_cipher_op_fail);
+
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   AEAD SHA1-AES encryption      : %d\n",
+					pstat->aead_sha1_aes_enc);
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   AEAD SHA1-AES decryption      : %d\n",
+					pstat->aead_sha1_aes_dec);
+
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   AEAD SHA1-DES encryption      : %d\n",
+					pstat->aead_sha1_des_enc);
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   AEAD SHA1-DES decryption      : %d\n",
+					pstat->aead_sha1_des_dec);
+
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   AEAD SHA1-3DES encryption     : %d\n",
+					pstat->aead_sha1_3des_enc);
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   AEAD SHA1-3DES decryption     : %d\n",
+					pstat->aead_sha1_3des_dec);
+
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   AEAD operation success       : %d\n",
+					pstat->aead_op_success);
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   AEAD operation fail          : %d\n",
+					pstat->aead_op_fail);
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   SHA1 digest			 : %d\n",
+					pstat->sha1_digest);
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   SHA256 digest		 : %d\n",
+					pstat->sha256_digest);
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   SHA  operation fail          : %d\n",
+					pstat->sha_op_fail);
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   SHA  operation success          : %d\n",
+					pstat->sha_op_success);
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   SHA1 HMAC digest			 : %d\n",
+					pstat->sha1_hmac_digest);
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   SHA256 HMAC digest		 : %d\n",
+					pstat->sha256_hmac_digest);
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   SHA HMAC operation fail          : %d\n",
+					pstat->sha_hmac_op_fail);
+	len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"   SHA HMAC operation success          : %d\n",
+					pstat->sha_hmac_op_success);
+	return len;
+}
+
+static int _qcrypto_remove(struct platform_device *pdev)
+{
+	struct crypto_priv *cp;
+	struct qcrypto_alg *q_alg;
+	struct qcrypto_alg *n;
+
+	cp = platform_get_drvdata(pdev);
+
+	if (!cp)
+		return 0;
+
+	if (cp->platform_support.bus_scale_table != NULL)
+		msm_bus_scale_unregister_client(cp->bus_scale_handle);
+
+	list_for_each_entry_safe(q_alg, n, &cp->alg_list, entry) {
+		if (q_alg->alg_type == QCRYPTO_ALG_CIPHER)
+			crypto_unregister_alg(&q_alg->cipher_alg);
+		if (q_alg->alg_type == QCRYPTO_ALG_SHA)
+			crypto_unregister_ahash(&q_alg->sha_alg);
+		list_del(&q_alg->entry);
+		kfree(q_alg);
+	}
+
+	if (cp->qce)
+		qce_close(cp->qce);
+	tasklet_kill(&cp->done_tasklet);
+	kfree(cp);
+	return 0;
+};
+
+static int _qcrypto_setkey_aes(struct crypto_ablkcipher *cipher, const u8 *key,
+		unsigned int len)
+{
+	struct crypto_tfm *tfm = crypto_ablkcipher_tfm(cipher);
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(tfm);
+	struct crypto_priv *cp = ctx->cp;
+
+	switch (len) {
+	case AES_KEYSIZE_128:
+	case AES_KEYSIZE_256:
+		break;
+	case AES_KEYSIZE_192:
+		if (cp->ce_support.aes_key_192)
+			break;
+	default:
+		crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN);
+		return -EINVAL;
+	};
+	ctx->enc_key_len = len;
+	memcpy(ctx->enc_key, key, len);
+	return 0;
+};
+
+static int _qcrypto_setkey_des(struct crypto_ablkcipher *cipher, const u8 *key,
+		unsigned int len)
+{
+	struct crypto_tfm *tfm = crypto_ablkcipher_tfm(cipher);
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(tfm);
+	u32 tmp[DES_EXPKEY_WORDS];
+	int ret = des_ekey(tmp, key);
+
+	if (len != DES_KEY_SIZE) {
+		crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN);
+		return -EINVAL;
+	};
+
+	if (unlikely(ret == 0) && (tfm->crt_flags & CRYPTO_TFM_REQ_WEAK_KEY)) {
+		tfm->crt_flags |= CRYPTO_TFM_RES_WEAK_KEY;
+		return -EINVAL;
+	}
+
+	ctx->enc_key_len = len;
+	memcpy(ctx->enc_key, key, len);
+	return 0;
+};
+
+static int _qcrypto_setkey_3des(struct crypto_ablkcipher *cipher, const u8 *key,
+		unsigned int len)
+{
+	struct crypto_tfm *tfm = crypto_ablkcipher_tfm(cipher);
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(tfm);
+
+	if (len != DES3_EDE_KEY_SIZE) {
+		crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN);
+		return -EINVAL;
+	};
+	ctx->enc_key_len = len;
+	memcpy(ctx->enc_key, key, len);
+	return 0;
+};
+
+static void req_done(unsigned long data)
+{
+	struct crypto_async_request *areq;
+	struct crypto_priv *cp = (struct crypto_priv *)data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&cp->lock, flags);
+	areq = cp->req;
+	cp->req = NULL;
+	spin_unlock_irqrestore(&cp->lock, flags);
+
+	if (areq)
+		areq->complete(areq, cp->res);
+	_start_qcrypto_process(cp);
+};
+
+static void _update_sha1_ctx(struct ahash_request  *req)
+{
+	struct qcrypto_sha_req_ctx *rctx = ahash_request_ctx(req);
+	struct sha1_state *sha_state_ctx = &rctx->sha1_state_ctx;
+	struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(req->base.tfm);
+
+	if (sha_ctx->last_blk == 1)
+		memset(sha_state_ctx, 0x00, sizeof(struct sha1_state));
+	else {
+		memset(sha_state_ctx->buffer, 0x00, SHA1_BLOCK_SIZE);
+		memcpy(sha_state_ctx->buffer, sha_ctx->trailing_buf,
+						sha_ctx->trailing_buf_len);
+		_byte_stream_to_words(sha_state_ctx->state , sha_ctx->digest,
+					SHA1_DIGEST_SIZE);
+	}
+	return;
+}
+
+static void _update_sha256_ctx(struct ahash_request  *req)
+{
+	struct qcrypto_sha_req_ctx *rctx = ahash_request_ctx(req);
+	struct sha256_state *sha_state_ctx = &rctx->sha256_state_ctx;
+	struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(req->base.tfm);
+
+	if (sha_ctx->last_blk == 1)
+		memset(sha_state_ctx, 0x00, sizeof(struct sha256_state));
+	else {
+		memset(sha_state_ctx->buf, 0x00, SHA256_BLOCK_SIZE);
+		memcpy(sha_state_ctx->buf, sha_ctx->trailing_buf,
+						sha_ctx->trailing_buf_len);
+		_byte_stream_to_words(sha_state_ctx->state, sha_ctx->digest,
+					SHA256_DIGEST_SIZE);
+	}
+	return;
+}
+
+static void _qce_ahash_complete(void *cookie, unsigned char *digest,
+		unsigned char *authdata, int ret)
+{
+	struct ahash_request *areq = (struct ahash_request *) cookie;
+	struct crypto_ahash *ahash = crypto_ahash_reqtfm(areq);
+	struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(areq->base.tfm);
+	struct qcrypto_sha_req_ctx *rctx = ahash_request_ctx(areq);
+	struct crypto_priv *cp = sha_ctx->cp;
+	struct crypto_stat *pstat;
+	uint32_t diglen = crypto_ahash_digestsize(ahash);
+	uint32_t *auth32 = (uint32_t *)authdata;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+#ifdef QCRYPTO_DEBUG
+	dev_info(&cp->pdev->dev, "_qce_ahash_complete: %p ret %d\n",
+				areq, ret);
+#endif
+
+	if (digest) {
+		memcpy(sha_ctx->digest, digest, diglen);
+		memcpy(areq->result, digest, diglen);
+	}
+	if (authdata) {
+		sha_ctx->byte_count[0] = auth32[0];
+		sha_ctx->byte_count[1] = auth32[1];
+		sha_ctx->byte_count[2] = auth32[2];
+		sha_ctx->byte_count[3] = auth32[3];
+	}
+	areq->src = rctx->src;
+	areq->nbytes = rctx->nbytes;
+
+	if (sha_ctx->sg != NULL) {
+		kfree(sha_ctx->sg);
+		sha_ctx->sg = NULL;
+	}
+
+	if (sha_ctx->alg == QCE_HASH_SHA1)
+		_update_sha1_ctx(areq);
+	if (sha_ctx->alg == QCE_HASH_SHA256)
+		_update_sha256_ctx(areq);
+
+	sha_ctx->last_blk = 0;
+	sha_ctx->first_blk = 0;
+
+	if (ret) {
+		cp->res = -ENXIO;
+		pstat->sha_op_fail++;
+	} else {
+		cp->res = 0;
+		pstat->sha_op_success++;
+	}
+
+	if (cp->platform_support.ce_shared)
+		schedule_work(&cp->unlock_ce_ws);
+	tasklet_schedule(&cp->done_tasklet);
+};
+
+static void _qce_ablk_cipher_complete(void *cookie, unsigned char *icb,
+		unsigned char *iv, int ret)
+{
+	struct ablkcipher_request *areq = (struct ablkcipher_request *) cookie;
+	struct crypto_ablkcipher *ablk = crypto_ablkcipher_reqtfm(areq);
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(areq->base.tfm);
+	struct crypto_priv *cp = ctx->cp;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+#ifdef QCRYPTO_DEBUG
+	dev_info(&cp->pdev->dev, "_qce_ablk_cipher_complete: %p ret %d\n",
+				areq, ret);
+#endif
+	if (iv)
+		memcpy(ctx->iv, iv, crypto_ablkcipher_ivsize(ablk));
+
+	if (ret) {
+		cp->res = -ENXIO;
+		pstat->ablk_cipher_op_fail++;
+	} else {
+		cp->res = 0;
+		pstat->ablk_cipher_op_success++;
+	}
+	if (cp->platform_support.ce_shared)
+		schedule_work(&cp->unlock_ce_ws);
+	tasklet_schedule(&cp->done_tasklet);
+};
+
+
+static void _qce_aead_complete(void *cookie, unsigned char *icv,
+				unsigned char *iv, int ret)
+{
+	struct aead_request *areq = (struct aead_request *) cookie;
+	struct crypto_aead *aead = crypto_aead_reqtfm(areq);
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(areq->base.tfm);
+	struct crypto_priv *cp = ctx->cp;
+	struct qcrypto_cipher_req_ctx *rctx;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+	rctx = aead_request_ctx(areq);
+
+	if (rctx->mode == QCE_MODE_CCM) {
+		kzfree(rctx->assoc);
+		areq->assoc = rctx->assoc_sg;
+		areq->assoclen = rctx->assoclen;
+		if (ret) {
+			if (ret == 0x2000000)
+				ret = -EBADMSG;
+			else
+				ret = -ENXIO;
+		}
+	} else {
+		if (ret == 0) {
+			if (rctx->dir  == QCE_ENCRYPT) {
+				/* copy the icv to dst */
+				scatterwalk_map_and_copy(icv, areq->dst,
+						areq->cryptlen,
+						ctx->authsize, 1);
+
+			} else {
+				unsigned char tmp[SHA256_DIGESTSIZE];
+
+				/* compare icv from src */
+				scatterwalk_map_and_copy(tmp,
+					areq->src, areq->cryptlen -
+					ctx->authsize, ctx->authsize, 0);
+				ret = memcmp(icv, tmp, ctx->authsize);
+				if (ret != 0)
+					ret = -EBADMSG;
+
+			}
+		} else {
+			ret = -ENXIO;
+		}
+
+		if (iv)
+			memcpy(ctx->iv, iv, crypto_aead_ivsize(aead));
+	}
+
+	if (ret)
+		pstat->aead_op_fail++;
+	else
+		pstat->aead_op_success++;
+
+	if (cp->platform_support.ce_shared)
+		schedule_work(&cp->unlock_ce_ws);
+	tasklet_schedule(&cp->done_tasklet);
+}
+
+static int aead_ccm_set_msg_len(u8 *block, unsigned int msglen, int csize)
+{
+	__be32 data;
+
+	memset(block, 0, csize);
+	block += csize;
+
+	if (csize >= 4)
+		csize = 4;
+	else if (msglen > (1 << (8 * csize)))
+		return -EOVERFLOW;
+
+	data = cpu_to_be32(msglen);
+	memcpy(block - csize, (u8 *)&data + 4 - csize, csize);
+
+	return 0;
+}
+
+static int qccrypto_set_aead_ccm_nonce(struct qce_req *qreq)
+{
+	struct aead_request *areq = (struct aead_request *) qreq->areq;
+	unsigned int i = ((unsigned int)qreq->iv[0]) + 1;
+
+	memcpy(&qreq->nonce[0] , qreq->iv, qreq->ivsize);
+	/*
+	 * Format control info per RFC 3610 and
+	 * NIST Special Publication 800-38C
+	 */
+	qreq->nonce[0] |= (8 * ((qreq->authsize - 2) / 2));
+	if (areq->assoclen)
+		qreq->nonce[0] |= 64;
+
+	if (i > MAX_NONCE)
+		return -EINVAL;
+
+	return aead_ccm_set_msg_len(qreq->nonce + 16 - i, qreq->cryptlen, i);
+}
+
+static int qcrypto_aead_ccm_format_adata(struct qce_req *qreq, uint32_t alen,
+						struct scatterlist *sg)
+{
+	unsigned char *adata;
+	uint32_t len, l;
+
+	qreq->assoc = kzalloc((alen + 0x64), (GFP_KERNEL | __GFP_DMA));
+	if (!qreq->assoc) {
+		pr_err("qcrypto Memory allocation of adata FAIL, error %ld\n",
+				PTR_ERR(qreq->assoc));
+		return -ENOMEM;
+	}
+	adata = qreq->assoc;
+	/*
+	 * Add control info for associated data
+	 * RFC 3610 and NIST Special Publication 800-38C
+	 */
+	if (alen < 65280) {
+		*(__be16 *)adata = cpu_to_be16(alen);
+		len = 2;
+	} else {
+			if ((alen >= 65280) && (alen <= 0xffffffff)) {
+				*(__be16 *)adata = cpu_to_be16(0xfffe);
+				*(__be32 *)&adata[2] = cpu_to_be32(alen);
+				len = 6;
+		} else {
+				*(__be16 *)adata = cpu_to_be16(0xffff);
+				*(__be32 *)&adata[6] = cpu_to_be32(alen);
+				len = 10;
+		}
+	}
+	adata += len;
+	qreq->assoclen = ALIGN((alen + len), 16);
+	for (l = alen; l > 0; sg = sg_next(sg)) {
+		memcpy(adata, sg_virt(sg), sg->length);
+		l -= sg->length;
+		adata += sg->length;
+	}
+	return 0;
+}
+
+static void _start_qcrypto_process(struct crypto_priv *cp)
+{
+	struct crypto_async_request *async_req = NULL;
+	struct crypto_async_request *backlog = NULL;
+	unsigned long flags;
+	u32 type;
+	struct qce_req qreq;
+	int ret;
+	struct qcrypto_cipher_req_ctx *rctx;
+	struct qcrypto_cipher_ctx *cipher_ctx;
+	struct qcrypto_sha_ctx *sha_ctx;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+again:
+	spin_lock_irqsave(&cp->lock, flags);
+	if (cp->req == NULL) {
+		backlog = crypto_get_backlog(&cp->queue);
+		async_req = crypto_dequeue_request(&cp->queue);
+		cp->req = async_req;
+	}
+	spin_unlock_irqrestore(&cp->lock, flags);
+	if (!async_req)
+		return;
+	if (backlog)
+		backlog->complete(backlog, -EINPROGRESS);
+	type = crypto_tfm_alg_type(async_req->tfm);
+
+	if (type == CRYPTO_ALG_TYPE_ABLKCIPHER) {
+		struct ablkcipher_request *req;
+		struct crypto_ablkcipher *tfm;
+
+		req = container_of(async_req, struct ablkcipher_request, base);
+		cipher_ctx = crypto_tfm_ctx(async_req->tfm);
+		rctx = ablkcipher_request_ctx(req);
+		tfm = crypto_ablkcipher_reqtfm(req);
+
+		qreq.op = QCE_REQ_ABLK_CIPHER;
+		qreq.qce_cb = _qce_ablk_cipher_complete;
+		qreq.areq = req;
+		qreq.alg = rctx->alg;
+		qreq.dir = rctx->dir;
+		qreq.mode = rctx->mode;
+		qreq.enckey = cipher_ctx->enc_key;
+		qreq.encklen = cipher_ctx->enc_key_len;
+		qreq.iv = req->info;
+		qreq.ivsize = crypto_ablkcipher_ivsize(tfm);
+		qreq.cryptlen = req->nbytes;
+		qreq.use_pmem = 0;
+
+		if ((cipher_ctx->enc_key_len == 0) &&
+				(cp->platform_support.hw_key_support == 0))
+			ret = -EINVAL;
+		else
+			ret =  qce_ablk_cipher_req(cp->qce, &qreq);
+	} else {
+		if (type == CRYPTO_ALG_TYPE_AHASH) {
+
+			struct ahash_request *req;
+			struct qce_sha_req sreq;
+
+			req = container_of(async_req,
+						struct ahash_request, base);
+			sha_ctx = crypto_tfm_ctx(async_req->tfm);
+
+			sreq.qce_cb = _qce_ahash_complete;
+			sreq.digest =  &sha_ctx->digest[0];
+			sreq.src = req->src;
+			sreq.auth_data[0] = sha_ctx->byte_count[0];
+			sreq.auth_data[1] = sha_ctx->byte_count[1];
+			sreq.auth_data[2] = sha_ctx->byte_count[2];
+			sreq.auth_data[3] = sha_ctx->byte_count[3];
+			sreq.first_blk = sha_ctx->first_blk;
+			sreq.last_blk = sha_ctx->last_blk;
+			sreq.size = req->nbytes;
+			sreq.areq = req;
+
+			switch (sha_ctx->alg) {
+			case QCE_HASH_SHA1:
+				sreq.alg = QCE_HASH_SHA1;
+				sreq.authkey = NULL;
+				break;
+			case QCE_HASH_SHA256:
+				sreq.alg = QCE_HASH_SHA256;
+				sreq.authkey = NULL;
+				break;
+			case QCE_HASH_SHA1_HMAC:
+				sreq.alg = QCE_HASH_SHA1_HMAC;
+				sreq.authkey = &sha_ctx->authkey[0];
+				break;
+			case QCE_HASH_SHA256_HMAC:
+				sreq.alg = QCE_HASH_SHA256_HMAC;
+				sreq.authkey = &sha_ctx->authkey[0];
+				break;
+			default:
+				break;
+			};
+			ret =  qce_process_sha_req(cp->qce, &sreq);
+
+		} else {
+			struct aead_request *req = container_of(async_req,
+						struct aead_request, base);
+			struct crypto_aead *aead = crypto_aead_reqtfm(req);
+
+			rctx = aead_request_ctx(req);
+			cipher_ctx = crypto_tfm_ctx(async_req->tfm);
+
+			qreq.op = QCE_REQ_AEAD;
+			qreq.qce_cb = _qce_aead_complete;
+
+			qreq.areq = req;
+			qreq.alg = rctx->alg;
+			qreq.dir = rctx->dir;
+			qreq.mode = rctx->mode;
+			qreq.iv = rctx->iv;
+
+			qreq.enckey = cipher_ctx->enc_key;
+			qreq.encklen = cipher_ctx->enc_key_len;
+			qreq.authkey = cipher_ctx->auth_key;
+			qreq.authklen = cipher_ctx->auth_key_len;
+			qreq.authsize = crypto_aead_authsize(aead);
+			qreq.ivsize =  crypto_aead_ivsize(aead);
+			if (qreq.mode == QCE_MODE_CCM) {
+				if (qreq.dir == QCE_ENCRYPT)
+					qreq.cryptlen = req->cryptlen;
+				else
+					qreq.cryptlen = req->cryptlen -
+								qreq.authsize;
+				/* Get NONCE */
+				ret = qccrypto_set_aead_ccm_nonce(&qreq);
+				if (ret)
+					goto done;
+				/* Format Associated data    */
+				ret = qcrypto_aead_ccm_format_adata(&qreq,
+								req->assoclen,
+								req->assoc);
+				if (ret)
+					goto done;
+				/*
+				 * Save the original associated data
+				 * length and sg
+				 */
+				rctx->assoc_sg  = req->assoc;
+				rctx->assoclen  = req->assoclen;
+				rctx->assoc  = qreq.assoc;
+				/*
+				 * update req with new formatted associated
+				 * data info
+				 */
+				req->assoc = &rctx->asg;
+				req->assoclen = qreq.assoclen;
+				sg_set_buf(req->assoc, qreq.assoc,
+							req->assoclen);
+				sg_mark_end(req->assoc);
+			}
+			ret =  qce_aead_req(cp->qce, &qreq);
+		}
+	};
+done:
+	if (ret) {
+
+		spin_lock_irqsave(&cp->lock, flags);
+		cp->req = NULL;
+		spin_unlock_irqrestore(&cp->lock, flags);
+
+		if (type == CRYPTO_ALG_TYPE_ABLKCIPHER)
+			pstat->ablk_cipher_op_fail++;
+		else
+			if (type == CRYPTO_ALG_TYPE_AHASH)
+				pstat->sha_op_fail++;
+			else
+				pstat->aead_op_fail++;
+
+		async_req->complete(async_req, ret);
+		goto again;
+	};
+};
+
+static int _qcrypto_queue_req(struct crypto_priv *cp,
+				struct crypto_async_request *req)
+{
+	int ret;
+	unsigned long flags;
+
+	if (cp->platform_support.ce_shared) {
+		ret = qcrypto_lock_ce(cp);
+		if (ret)
+			return ret;
+	}
+
+	spin_lock_irqsave(&cp->lock, flags);
+	ret = crypto_enqueue_request(&cp->queue, req);
+	spin_unlock_irqrestore(&cp->lock, flags);
+	_start_qcrypto_process(cp);
+
+	return ret;
+}
+
+static int _qcrypto_enc_aes_ecb(struct ablkcipher_request *req)
+{
+	struct qcrypto_cipher_req_ctx *rctx;
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = ctx->cp;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+	BUG_ON(crypto_tfm_alg_type(req->base.tfm) !=
+					CRYPTO_ALG_TYPE_ABLKCIPHER);
+#ifdef QCRYPTO_DEBUG
+	dev_info(&cp->pdev->dev, "_qcrypto_enc_aes_ecb: %p\n", req);
+#endif
+	rctx = ablkcipher_request_ctx(req);
+	rctx->aead = 0;
+	rctx->alg = CIPHER_ALG_AES;
+	rctx->dir = QCE_ENCRYPT;
+	rctx->mode = QCE_MODE_ECB;
+
+	pstat->ablk_cipher_aes_enc++;
+	return _qcrypto_queue_req(cp, &req->base);
+};
+
+static int _qcrypto_enc_aes_cbc(struct ablkcipher_request *req)
+{
+	struct qcrypto_cipher_req_ctx *rctx;
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = ctx->cp;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+	BUG_ON(crypto_tfm_alg_type(req->base.tfm) !=
+					CRYPTO_ALG_TYPE_ABLKCIPHER);
+#ifdef QCRYPTO_DEBUG
+	dev_info(&cp->pdev->dev, "_qcrypto_enc_aes_cbc: %p\n", req);
+#endif
+	rctx = ablkcipher_request_ctx(req);
+	rctx->aead = 0;
+	rctx->alg = CIPHER_ALG_AES;
+	rctx->dir = QCE_ENCRYPT;
+	rctx->mode = QCE_MODE_CBC;
+
+	pstat->ablk_cipher_aes_enc++;
+	return _qcrypto_queue_req(cp, &req->base);
+};
+
+static int _qcrypto_enc_aes_ctr(struct ablkcipher_request *req)
+{
+	struct qcrypto_cipher_req_ctx *rctx;
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = ctx->cp;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+	BUG_ON(crypto_tfm_alg_type(req->base.tfm) !=
+				CRYPTO_ALG_TYPE_ABLKCIPHER);
+#ifdef QCRYPTO_DEBUG
+	dev_info(&cp->pdev->dev, "_qcrypto_enc_aes_ctr: %p\n", req);
+#endif
+	rctx = ablkcipher_request_ctx(req);
+	rctx->aead = 0;
+	rctx->alg = CIPHER_ALG_AES;
+	rctx->dir = QCE_ENCRYPT;
+	rctx->mode = QCE_MODE_CTR;
+
+	pstat->ablk_cipher_aes_enc++;
+	return _qcrypto_queue_req(cp, &req->base);
+};
+
+static int _qcrypto_enc_aes_xts(struct ablkcipher_request *req)
+{
+	struct qcrypto_cipher_req_ctx *rctx;
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = ctx->cp;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+	BUG_ON(crypto_tfm_alg_type(req->base.tfm) !=
+					CRYPTO_ALG_TYPE_ABLKCIPHER);
+	rctx = ablkcipher_request_ctx(req);
+	rctx->aead = 0;
+	rctx->alg = CIPHER_ALG_AES;
+	rctx->dir = QCE_ENCRYPT;
+	rctx->mode = QCE_MODE_XTS;
+
+	pstat->ablk_cipher_aes_enc++;
+	return _qcrypto_queue_req(cp, &req->base);
+};
+
+static int _qcrypto_aead_encrypt_aes_ccm(struct aead_request *req)
+{
+	struct qcrypto_cipher_req_ctx *rctx;
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = ctx->cp;
+	struct crypto_stat *pstat;
+
+	if ((ctx->authsize > 16) || (ctx->authsize < 4) || (ctx->authsize & 1))
+		return  -EINVAL;
+	if ((ctx->auth_key_len != AES_KEYSIZE_128) &&
+		(ctx->auth_key_len != AES_KEYSIZE_256))
+		return  -EINVAL;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+	rctx = aead_request_ctx(req);
+	rctx->aead = 1;
+	rctx->alg = CIPHER_ALG_AES;
+	rctx->dir = QCE_ENCRYPT;
+	rctx->mode = QCE_MODE_CCM;
+	rctx->iv = req->iv;
+
+	pstat->aead_sha1_aes_enc++;
+	return _qcrypto_queue_req(cp, &req->base);
+}
+
+static int _qcrypto_enc_des_ecb(struct ablkcipher_request *req)
+{
+	struct qcrypto_cipher_req_ctx *rctx;
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = ctx->cp;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+	BUG_ON(crypto_tfm_alg_type(req->base.tfm) !=
+					CRYPTO_ALG_TYPE_ABLKCIPHER);
+	rctx = ablkcipher_request_ctx(req);
+	rctx->aead = 0;
+	rctx->alg = CIPHER_ALG_DES;
+	rctx->dir = QCE_ENCRYPT;
+	rctx->mode = QCE_MODE_ECB;
+
+	pstat->ablk_cipher_des_enc++;
+	return _qcrypto_queue_req(cp, &req->base);
+};
+
+static int _qcrypto_enc_des_cbc(struct ablkcipher_request *req)
+{
+	struct qcrypto_cipher_req_ctx *rctx;
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = ctx->cp;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+	BUG_ON(crypto_tfm_alg_type(req->base.tfm) !=
+					CRYPTO_ALG_TYPE_ABLKCIPHER);
+	rctx = ablkcipher_request_ctx(req);
+	rctx->aead = 0;
+	rctx->alg = CIPHER_ALG_DES;
+	rctx->dir = QCE_ENCRYPT;
+	rctx->mode = QCE_MODE_CBC;
+
+	pstat->ablk_cipher_des_enc++;
+	return _qcrypto_queue_req(cp, &req->base);
+};
+
+static int _qcrypto_enc_3des_ecb(struct ablkcipher_request *req)
+{
+	struct qcrypto_cipher_req_ctx *rctx;
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = ctx->cp;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+	BUG_ON(crypto_tfm_alg_type(req->base.tfm) !=
+					CRYPTO_ALG_TYPE_ABLKCIPHER);
+	rctx = ablkcipher_request_ctx(req);
+	rctx->aead = 0;
+	rctx->alg = CIPHER_ALG_3DES;
+	rctx->dir = QCE_ENCRYPT;
+	rctx->mode = QCE_MODE_ECB;
+
+	pstat->ablk_cipher_3des_enc++;
+	return _qcrypto_queue_req(cp, &req->base);
+};
+
+static int _qcrypto_enc_3des_cbc(struct ablkcipher_request *req)
+{
+	struct qcrypto_cipher_req_ctx *rctx;
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = ctx->cp;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+	BUG_ON(crypto_tfm_alg_type(req->base.tfm) !=
+					CRYPTO_ALG_TYPE_ABLKCIPHER);
+	rctx = ablkcipher_request_ctx(req);
+	rctx->aead = 0;
+	rctx->alg = CIPHER_ALG_3DES;
+	rctx->dir = QCE_ENCRYPT;
+	rctx->mode = QCE_MODE_CBC;
+
+	pstat->ablk_cipher_3des_enc++;
+	return _qcrypto_queue_req(cp, &req->base);
+};
+
+static int _qcrypto_dec_aes_ecb(struct ablkcipher_request *req)
+{
+	struct qcrypto_cipher_req_ctx *rctx;
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = ctx->cp;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+	BUG_ON(crypto_tfm_alg_type(req->base.tfm) !=
+				CRYPTO_ALG_TYPE_ABLKCIPHER);
+#ifdef QCRYPTO_DEBUG
+	dev_info(&cp->pdev->dev, "_qcrypto_dec_aes_ecb: %p\n", req);
+#endif
+	rctx = ablkcipher_request_ctx(req);
+	rctx->aead = 0;
+	rctx->alg = CIPHER_ALG_AES;
+	rctx->dir = QCE_DECRYPT;
+	rctx->mode = QCE_MODE_ECB;
+
+	pstat->ablk_cipher_aes_dec++;
+	return _qcrypto_queue_req(cp, &req->base);
+};
+
+static int _qcrypto_dec_aes_cbc(struct ablkcipher_request *req)
+{
+	struct qcrypto_cipher_req_ctx *rctx;
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = ctx->cp;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+	BUG_ON(crypto_tfm_alg_type(req->base.tfm) !=
+				CRYPTO_ALG_TYPE_ABLKCIPHER);
+#ifdef QCRYPTO_DEBUG
+	dev_info(&cp->pdev->dev, "_qcrypto_dec_aes_cbc: %p\n", req);
+#endif
+
+	rctx = ablkcipher_request_ctx(req);
+	rctx->aead = 0;
+	rctx->alg = CIPHER_ALG_AES;
+	rctx->dir = QCE_DECRYPT;
+	rctx->mode = QCE_MODE_CBC;
+
+	pstat->ablk_cipher_aes_dec++;
+	return _qcrypto_queue_req(cp, &req->base);
+};
+
+static int _qcrypto_dec_aes_ctr(struct ablkcipher_request *req)
+{
+	struct qcrypto_cipher_req_ctx *rctx;
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = ctx->cp;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+	BUG_ON(crypto_tfm_alg_type(req->base.tfm) !=
+					CRYPTO_ALG_TYPE_ABLKCIPHER);
+#ifdef QCRYPTO_DEBUG
+	dev_info(&cp->pdev->dev, "_qcrypto_dec_aes_ctr: %p\n", req);
+#endif
+	rctx = ablkcipher_request_ctx(req);
+	rctx->aead = 0;
+	rctx->alg = CIPHER_ALG_AES;
+	rctx->mode = QCE_MODE_CTR;
+
+	/* Note. There is no such thing as aes/counter mode, decrypt */
+	rctx->dir = QCE_ENCRYPT;
+
+	pstat->ablk_cipher_aes_dec++;
+	return _qcrypto_queue_req(cp, &req->base);
+};
+
+static int _qcrypto_dec_des_ecb(struct ablkcipher_request *req)
+{
+	struct qcrypto_cipher_req_ctx *rctx;
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = ctx->cp;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+	BUG_ON(crypto_tfm_alg_type(req->base.tfm) !=
+					CRYPTO_ALG_TYPE_ABLKCIPHER);
+	rctx = ablkcipher_request_ctx(req);
+	rctx->aead = 0;
+	rctx->alg = CIPHER_ALG_DES;
+	rctx->dir = QCE_DECRYPT;
+	rctx->mode = QCE_MODE_ECB;
+
+	pstat->ablk_cipher_des_dec++;
+	return _qcrypto_queue_req(cp, &req->base);
+};
+
+static int _qcrypto_dec_des_cbc(struct ablkcipher_request *req)
+{
+	struct qcrypto_cipher_req_ctx *rctx;
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = ctx->cp;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+	BUG_ON(crypto_tfm_alg_type(req->base.tfm) !=
+					CRYPTO_ALG_TYPE_ABLKCIPHER);
+	rctx = ablkcipher_request_ctx(req);
+	rctx->aead = 0;
+	rctx->alg = CIPHER_ALG_DES;
+	rctx->dir = QCE_DECRYPT;
+	rctx->mode = QCE_MODE_CBC;
+
+	pstat->ablk_cipher_des_dec++;
+	return _qcrypto_queue_req(cp, &req->base);
+};
+
+static int _qcrypto_dec_3des_ecb(struct ablkcipher_request *req)
+{
+	struct qcrypto_cipher_req_ctx *rctx;
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = ctx->cp;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+	BUG_ON(crypto_tfm_alg_type(req->base.tfm) !=
+					CRYPTO_ALG_TYPE_ABLKCIPHER);
+	rctx = ablkcipher_request_ctx(req);
+	rctx->aead = 0;
+	rctx->alg = CIPHER_ALG_3DES;
+	rctx->dir = QCE_DECRYPT;
+	rctx->mode = QCE_MODE_ECB;
+
+	pstat->ablk_cipher_3des_dec++;
+	return _qcrypto_queue_req(cp, &req->base);
+};
+
+static int _qcrypto_dec_3des_cbc(struct ablkcipher_request *req)
+{
+	struct qcrypto_cipher_req_ctx *rctx;
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = ctx->cp;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+	BUG_ON(crypto_tfm_alg_type(req->base.tfm) !=
+					CRYPTO_ALG_TYPE_ABLKCIPHER);
+	rctx = ablkcipher_request_ctx(req);
+	rctx->aead = 0;
+	rctx->alg = CIPHER_ALG_3DES;
+	rctx->dir = QCE_DECRYPT;
+	rctx->mode = QCE_MODE_CBC;
+
+	pstat->ablk_cipher_3des_dec++;
+	return _qcrypto_queue_req(cp, &req->base);
+};
+
+static int _qcrypto_dec_aes_xts(struct ablkcipher_request *req)
+{
+	struct qcrypto_cipher_req_ctx *rctx;
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = ctx->cp;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+	BUG_ON(crypto_tfm_alg_type(req->base.tfm) !=
+					CRYPTO_ALG_TYPE_ABLKCIPHER);
+	rctx = ablkcipher_request_ctx(req);
+	rctx->aead = 0;
+	rctx->alg = CIPHER_ALG_AES;
+	rctx->mode = QCE_MODE_XTS;
+	rctx->dir = QCE_DECRYPT;
+
+	pstat->ablk_cipher_aes_dec++;
+	return _qcrypto_queue_req(cp, &req->base);
+};
+
+
+static int _qcrypto_aead_decrypt_aes_ccm(struct aead_request *req)
+{
+	struct qcrypto_cipher_req_ctx *rctx;
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = ctx->cp;
+	struct crypto_stat *pstat;
+
+	if ((ctx->authsize > 16) || (ctx->authsize < 4) || (ctx->authsize & 1))
+		return  -EINVAL;
+	if ((ctx->auth_key_len != AES_KEYSIZE_128) &&
+		(ctx->auth_key_len != AES_KEYSIZE_256))
+		return  -EINVAL;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+	rctx = aead_request_ctx(req);
+	rctx->aead = 1;
+	rctx->alg = CIPHER_ALG_AES;
+	rctx->dir = QCE_DECRYPT;
+	rctx->mode = QCE_MODE_CCM;
+	rctx->iv = req->iv;
+
+	pstat->aead_sha1_aes_dec++;
+	return _qcrypto_queue_req(cp, &req->base);
+}
+
+static int _qcrypto_aead_setauthsize(struct crypto_aead *authenc,
+				unsigned int authsize)
+{
+	struct qcrypto_cipher_ctx *ctx = crypto_aead_ctx(authenc);
+
+	ctx->authsize = authsize;
+	return 0;
+}
+
+static int _qcrypto_aead_ccm_setauthsize(struct crypto_aead *authenc,
+				  unsigned int authsize)
+{
+	struct qcrypto_cipher_ctx *ctx = crypto_aead_ctx(authenc);
+
+	switch (authsize) {
+	case 4:
+	case 6:
+	case 8:
+	case 10:
+	case 12:
+	case 14:
+	case 16:
+		break;
+	default:
+		return -EINVAL;
+	}
+	ctx->authsize = authsize;
+	return 0;
+}
+
+static int _qcrypto_aead_setkey(struct crypto_aead *tfm, const u8 *key,
+			unsigned int keylen)
+{
+	struct qcrypto_cipher_ctx *ctx = crypto_aead_ctx(tfm);
+	struct rtattr *rta = (struct rtattr *)key;
+	struct crypto_authenc_key_param *param;
+
+	if (!RTA_OK(rta, keylen))
+		goto badkey;
+	if (rta->rta_type != CRYPTO_AUTHENC_KEYA_PARAM)
+		goto badkey;
+	if (RTA_PAYLOAD(rta) < sizeof(*param))
+		goto badkey;
+
+	param = RTA_DATA(rta);
+	ctx->enc_key_len = be32_to_cpu(param->enckeylen);
+
+	key += RTA_ALIGN(rta->rta_len);
+	keylen -= RTA_ALIGN(rta->rta_len);
+
+	if (keylen < ctx->enc_key_len)
+		goto badkey;
+
+	ctx->auth_key_len = keylen - ctx->enc_key_len;
+	if (ctx->enc_key_len >= QCRYPTO_MAX_KEY_SIZE ||
+				ctx->auth_key_len >= QCRYPTO_MAX_KEY_SIZE)
+		goto badkey;
+	memset(ctx->auth_key, 0, QCRYPTO_MAX_KEY_SIZE);
+	memcpy(ctx->enc_key, key + ctx->auth_key_len, ctx->enc_key_len);
+	memcpy(ctx->auth_key, key, ctx->auth_key_len);
+
+	return 0;
+badkey:
+	ctx->enc_key_len = 0;
+	crypto_aead_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
+	return -EINVAL;
+}
+
+static int _qcrypto_aead_ccm_setkey(struct crypto_aead *aead, const u8 *key,
+			unsigned int keylen)
+{
+	struct crypto_tfm *tfm = crypto_aead_tfm(aead);
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(tfm);
+	struct crypto_priv *cp = ctx->cp;
+
+	switch (keylen) {
+	case AES_KEYSIZE_128:
+	case AES_KEYSIZE_256:
+		break;
+	case AES_KEYSIZE_192:
+		if (cp->ce_support.aes_key_192)
+			break;
+	default:
+		ctx->enc_key_len = 0;
+		crypto_aead_set_flags(aead, CRYPTO_TFM_RES_BAD_KEY_LEN);
+		return -EINVAL;
+	};
+	ctx->enc_key_len = keylen;
+	memcpy(ctx->enc_key, key, keylen);
+	ctx->auth_key_len = keylen;
+	memcpy(ctx->auth_key, key, keylen);
+
+	return 0;
+}
+
+static int _qcrypto_aead_encrypt_aes_cbc(struct aead_request *req)
+{
+	struct qcrypto_cipher_req_ctx *rctx;
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = ctx->cp;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+#ifdef QCRYPTO_DEBUG
+	dev_info(&cp->pdev->dev, "_qcrypto_aead_encrypt_aes_cbc: %p\n", req);
+#endif
+
+	rctx = aead_request_ctx(req);
+	rctx->aead = 1;
+	rctx->alg = CIPHER_ALG_AES;
+	rctx->dir = QCE_ENCRYPT;
+	rctx->mode = QCE_MODE_CBC;
+	rctx->iv = req->iv;
+
+	pstat->aead_sha1_aes_enc++;
+	return _qcrypto_queue_req(cp, &req->base);
+}
+
+static int _qcrypto_aead_decrypt_aes_cbc(struct aead_request *req)
+{
+	struct qcrypto_cipher_req_ctx *rctx;
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = ctx->cp;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+#ifdef QCRYPTO_DEBUG
+	dev_info(&cp->pdev->dev, "_qcrypto_aead_decrypt_aes_cbc: %p\n", req);
+#endif
+	rctx = aead_request_ctx(req);
+	rctx->aead = 1;
+	rctx->alg = CIPHER_ALG_AES;
+	rctx->dir = QCE_DECRYPT;
+	rctx->mode = QCE_MODE_CBC;
+	rctx->iv = req->iv;
+
+	pstat->aead_sha1_aes_dec++;
+	return _qcrypto_queue_req(cp, &req->base);
+}
+
+static int _qcrypto_aead_givencrypt_aes_cbc(struct aead_givcrypt_request *req)
+{
+	struct aead_request *areq = &req->areq;
+	struct crypto_aead *authenc = crypto_aead_reqtfm(areq);
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(areq->base.tfm);
+	struct crypto_priv *cp = ctx->cp;
+	struct qcrypto_cipher_req_ctx *rctx;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+	rctx = aead_request_ctx(areq);
+	rctx->aead = 1;
+	rctx->alg = CIPHER_ALG_AES;
+	rctx->dir = QCE_ENCRYPT;
+	rctx->mode = QCE_MODE_CBC;
+	rctx->iv = req->giv;	/* generated iv */
+
+	memcpy(req->giv, ctx->iv, crypto_aead_ivsize(authenc));
+	 /* avoid consecutive packets going out with same IV */
+	*(__be64 *)req->giv ^= cpu_to_be64(req->seq);
+	pstat->aead_sha1_aes_enc++;
+	return _qcrypto_queue_req(cp, &areq->base);
+}
+
+#ifdef QCRYPTO_AEAD_AES_CTR
+static int _qcrypto_aead_encrypt_aes_ctr(struct aead_request *req)
+{
+	struct qcrypto_cipher_req_ctx *rctx;
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = ctx->cp;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+	rctx = aead_request_ctx(req);
+	rctx->aead = 1;
+	rctx->alg = CIPHER_ALG_AES;
+	rctx->dir = QCE_ENCRYPT;
+	rctx->mode = QCE_MODE_CTR;
+	rctx->iv = req->iv;
+
+	pstat->aead_sha1_aes_enc++;
+	return _qcrypto_queue_req(cp, &req->base);
+}
+
+static int _qcrypto_aead_decrypt_aes_ctr(struct aead_request *req)
+{
+	struct qcrypto_cipher_req_ctx *rctx;
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = ctx->cp;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+	rctx = aead_request_ctx(req);
+	rctx->aead = 1;
+	rctx->alg = CIPHER_ALG_AES;
+
+	/* Note. There is no such thing as aes/counter mode, decrypt */
+	rctx->dir = QCE_ENCRYPT;
+
+	rctx->mode = QCE_MODE_CTR;
+	rctx->iv = req->iv;
+
+	pstat->aead_sha1_aes_dec++;
+	return _qcrypto_queue_req(cp, &req->base);
+}
+
+static int _qcrypto_aead_givencrypt_aes_ctr(struct aead_givcrypt_request *req)
+{
+	struct aead_request *areq = &req->areq;
+	struct crypto_aead *authenc = crypto_aead_reqtfm(areq);
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(areq->base.tfm);
+	struct crypto_priv *cp = ctx->cp;
+	struct qcrypto_cipher_req_ctx *rctx;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+	rctx = aead_request_ctx(areq);
+	rctx->aead = 1;
+	rctx->alg = CIPHER_ALG_AES;
+	rctx->dir = QCE_ENCRYPT;
+	rctx->mode = QCE_MODE_CTR;
+	rctx->iv = req->giv;	/* generated iv */
+
+	memcpy(req->giv, ctx->iv, crypto_aead_ivsize(authenc));
+	 /* avoid consecutive packets going out with same IV */
+	*(__be64 *)req->giv ^= cpu_to_be64(req->seq);
+	pstat->aead_sha1_aes_enc++;
+	return _qcrypto_queue_req(cp, &areq->base);
+};
+#endif /* QCRYPTO_AEAD_AES_CTR */
+
+static int _qcrypto_aead_encrypt_des_cbc(struct aead_request *req)
+{
+	struct qcrypto_cipher_req_ctx *rctx;
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = ctx->cp;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+	rctx = aead_request_ctx(req);
+	rctx->aead = 1;
+	rctx->alg = CIPHER_ALG_DES;
+	rctx->dir = QCE_ENCRYPT;
+	rctx->mode = QCE_MODE_CBC;
+	rctx->iv = req->iv;
+
+	pstat->aead_sha1_des_enc++;
+	return _qcrypto_queue_req(cp, &req->base);
+}
+
+static int _qcrypto_aead_decrypt_des_cbc(struct aead_request *req)
+{
+	struct qcrypto_cipher_req_ctx *rctx;
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = ctx->cp;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+	rctx = aead_request_ctx(req);
+	rctx->aead = 1;
+	rctx->alg = CIPHER_ALG_DES;
+	rctx->dir = QCE_DECRYPT;
+	rctx->mode = QCE_MODE_CBC;
+	rctx->iv = req->iv;
+
+	pstat->aead_sha1_des_dec++;
+	return _qcrypto_queue_req(cp, &req->base);
+}
+
+static int _qcrypto_aead_givencrypt_des_cbc(struct aead_givcrypt_request *req)
+{
+	struct aead_request *areq = &req->areq;
+	struct crypto_aead *authenc = crypto_aead_reqtfm(areq);
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(areq->base.tfm);
+	struct crypto_priv *cp = ctx->cp;
+	struct qcrypto_cipher_req_ctx *rctx;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+	rctx = aead_request_ctx(areq);
+	rctx->aead = 1;
+	rctx->alg = CIPHER_ALG_DES;
+	rctx->dir = QCE_ENCRYPT;
+	rctx->mode = QCE_MODE_CBC;
+	rctx->iv = req->giv;	/* generated iv */
+
+	memcpy(req->giv, ctx->iv, crypto_aead_ivsize(authenc));
+	 /* avoid consecutive packets going out with same IV */
+	*(__be64 *)req->giv ^= cpu_to_be64(req->seq);
+	pstat->aead_sha1_des_enc++;
+	return _qcrypto_queue_req(cp, &areq->base);
+}
+
+static int _qcrypto_aead_encrypt_3des_cbc(struct aead_request *req)
+{
+	struct qcrypto_cipher_req_ctx *rctx;
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = ctx->cp;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+	rctx = aead_request_ctx(req);
+	rctx->aead = 1;
+	rctx->alg = CIPHER_ALG_3DES;
+	rctx->dir = QCE_ENCRYPT;
+	rctx->mode = QCE_MODE_CBC;
+	rctx->iv = req->iv;
+
+	pstat->aead_sha1_3des_enc++;
+	return _qcrypto_queue_req(cp, &req->base);
+}
+
+static int _qcrypto_aead_decrypt_3des_cbc(struct aead_request *req)
+{
+	struct qcrypto_cipher_req_ctx *rctx;
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = ctx->cp;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+	rctx = aead_request_ctx(req);
+	rctx->aead = 1;
+	rctx->alg = CIPHER_ALG_3DES;
+	rctx->dir = QCE_DECRYPT;
+	rctx->mode = QCE_MODE_CBC;
+	rctx->iv = req->iv;
+
+	pstat->aead_sha1_3des_dec++;
+	return _qcrypto_queue_req(cp, &req->base);
+}
+
+static int _qcrypto_aead_givencrypt_3des_cbc(struct aead_givcrypt_request *req)
+{
+	struct aead_request *areq = &req->areq;
+	struct crypto_aead *authenc = crypto_aead_reqtfm(areq);
+	struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(areq->base.tfm);
+	struct crypto_priv *cp = ctx->cp;
+	struct qcrypto_cipher_req_ctx *rctx;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+	rctx = aead_request_ctx(areq);
+	rctx->aead = 1;
+	rctx->alg = CIPHER_ALG_3DES;
+	rctx->dir = QCE_ENCRYPT;
+	rctx->mode = QCE_MODE_CBC;
+	rctx->iv = req->giv;	/* generated iv */
+
+	memcpy(req->giv, ctx->iv, crypto_aead_ivsize(authenc));
+	 /* avoid consecutive packets going out with same IV */
+	*(__be64 *)req->giv ^= cpu_to_be64(req->seq);
+	pstat->aead_sha1_3des_enc++;
+	return _qcrypto_queue_req(cp, &areq->base);
+}
+
+static int qcrypto_count_sg(struct scatterlist *sg, int nbytes)
+{
+	int i;
+
+	for (i = 0; nbytes > 0; i++, sg = sg_next(sg))
+		nbytes -= sg->length;
+
+	return i;
+}
+
+static int _sha_init(struct qcrypto_sha_ctx *ctx)
+{
+	ctx->first_blk = 1;
+	ctx->last_blk = 0;
+	ctx->byte_count[0] = 0;
+	ctx->byte_count[1] = 0;
+	ctx->byte_count[2] = 0;
+	ctx->byte_count[3] = 0;
+	ctx->trailing_buf_len = 0;
+
+	return 0;
+};
+
+static int _sha1_init(struct ahash_request *req)
+{
+	struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = sha_ctx->cp;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+	_sha_init(sha_ctx);
+	sha_ctx->alg = QCE_HASH_SHA1;
+
+	memset(&sha_ctx->trailing_buf[0], 0x00, SHA1_BLOCK_SIZE);
+	memcpy(&sha_ctx->digest[0], &_std_init_vector_sha1_uint8[0],
+						SHA1_DIGEST_SIZE);
+	sha_ctx->diglen = SHA1_DIGEST_SIZE;
+	_update_sha1_ctx(req);
+
+	pstat->sha1_digest++;
+	return 0;
+};
+
+static int _sha256_init(struct ahash_request *req)
+{
+	struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = sha_ctx->cp;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+
+	_sha_init(sha_ctx);
+	sha_ctx->alg = QCE_HASH_SHA256;
+
+	memset(&sha_ctx->trailing_buf[0], 0x00, SHA256_BLOCK_SIZE);
+	memcpy(&sha_ctx->digest[0], &_std_init_vector_sha256_uint8[0],
+						SHA256_DIGEST_SIZE);
+	sha_ctx->diglen = SHA256_DIGEST_SIZE;
+	_update_sha256_ctx(req);
+
+	pstat->sha256_digest++;
+	return 0;
+};
+
+
+static int _sha1_export(struct ahash_request  *req, void *out)
+{
+	struct qcrypto_sha_req_ctx *rctx = ahash_request_ctx(req);
+	struct sha1_state *sha_state_ctx = &rctx->sha1_state_ctx;
+	struct sha1_state *out_ctx = (struct sha1_state *)out;
+
+	out_ctx->count = sha_state_ctx->count;
+	memcpy(out_ctx->state, sha_state_ctx->state, sizeof(out_ctx->state));
+	memcpy(out_ctx->buffer, sha_state_ctx->buffer, SHA1_BLOCK_SIZE);
+
+	return 0;
+};
+
+static int _sha1_import(struct ahash_request  *req, const void *in)
+{
+	struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(req->base.tfm);
+	struct qcrypto_sha_req_ctx *rctx = ahash_request_ctx(req);
+	struct sha1_state *sha_state_ctx = &rctx->sha1_state_ctx;
+	struct sha1_state *in_ctx = (struct sha1_state *)in;
+
+	sha_state_ctx->count = in_ctx->count;
+	memcpy(sha_state_ctx->state, in_ctx->state, sizeof(in_ctx->state));
+	memcpy(sha_state_ctx->buffer, in_ctx->buffer, SHA1_BLOCK_SIZE);
+	memcpy(sha_ctx->trailing_buf, in_ctx->buffer, SHA1_BLOCK_SIZE);
+
+	sha_ctx->byte_count[0] =  (uint32_t)(in_ctx->count & 0xFFFFFFC0);
+	sha_ctx->byte_count[1] =  (uint32_t)(in_ctx->count >> 32);
+	_words_to_byte_stream(in_ctx->state, sha_ctx->digest, sha_ctx->diglen);
+
+	sha_ctx->trailing_buf_len = (uint32_t)(in_ctx->count &
+						(SHA1_BLOCK_SIZE-1));
+
+	if (!(in_ctx->count))
+		sha_ctx->first_blk = 1;
+	else
+		sha_ctx->first_blk = 0;
+
+	return 0;
+}
+static int _sha256_export(struct ahash_request  *req, void *out)
+{
+	struct qcrypto_sha_req_ctx *rctx = ahash_request_ctx(req);
+	struct sha256_state *sha_state_ctx = &rctx->sha256_state_ctx;
+	struct sha256_state *out_ctx = (struct sha256_state *)out;
+
+	out_ctx->count = sha_state_ctx->count;
+	memcpy(out_ctx->state, sha_state_ctx->state, sizeof(out_ctx->state));
+	memcpy(out_ctx->buf, sha_state_ctx->buf, SHA256_BLOCK_SIZE);
+
+	return 0;
+};
+
+static int _sha256_import(struct ahash_request  *req, const void *in)
+{
+	struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(req->base.tfm);
+	struct qcrypto_sha_req_ctx *rctx = ahash_request_ctx(req);
+	struct sha256_state *sha_state_ctx = &rctx->sha256_state_ctx;
+	struct sha256_state *in_ctx = (struct sha256_state *)in;
+
+	sha_state_ctx->count = in_ctx->count;
+	memcpy(sha_state_ctx->state, in_ctx->state, sizeof(in_ctx->state));
+	memcpy(sha_state_ctx->buf, in_ctx->buf, SHA256_BLOCK_SIZE);
+	memcpy(sha_ctx->trailing_buf, in_ctx->buf, SHA256_BLOCK_SIZE);
+
+	sha_ctx->byte_count[0] =  (uint32_t)(in_ctx->count & 0xFFFFFFC0);
+	sha_ctx->byte_count[1] =  (uint32_t)(in_ctx->count >> 32);
+	_words_to_byte_stream(in_ctx->state, sha_ctx->digest, sha_ctx->diglen);
+
+	sha_ctx->trailing_buf_len = (uint32_t)(in_ctx->count &
+						(SHA256_BLOCK_SIZE-1));
+
+	if (!(in_ctx->count))
+		sha_ctx->first_blk = 1;
+	else
+		sha_ctx->first_blk = 0;
+
+	return 0;
+}
+
+
+static int _sha_update(struct ahash_request  *req, uint32_t sha_block_size)
+{
+	struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = sha_ctx->cp;
+	struct qcrypto_sha_req_ctx *rctx = ahash_request_ctx(req);
+	uint32_t total, len, i, num_sg;
+	uint8_t *k_src = NULL;
+	uint32_t sha_pad_len = 0;
+	uint32_t end_src = 0;
+	uint32_t trailing_buf_len = 0;
+	uint32_t nbytes, index = 0;
+	uint32_t saved_length = 0;
+	int ret = 0;
+
+	/* check for trailing buffer from previous updates and append it */
+	total = req->nbytes + sha_ctx->trailing_buf_len;
+	len = req->nbytes;
+
+	if (total <= sha_block_size) {
+		i = 0;
+
+		k_src = &sha_ctx->trailing_buf[sha_ctx->trailing_buf_len];
+		while (len > 0) {
+			memcpy(k_src, sg_virt(&req->src[i]),
+							req->src[i].length);
+			len -= req->src[i].length;
+			k_src += req->src[i].length;
+			i++;
+		}
+		sha_ctx->trailing_buf_len = total;
+		if (sha_ctx->alg == QCE_HASH_SHA1)
+			_update_sha1_ctx(req);
+		if (sha_ctx->alg == QCE_HASH_SHA256)
+			_update_sha256_ctx(req);
+		return 0;
+	}
+
+	/* save the original req structure fields*/
+	rctx->src = req->src;
+	rctx->nbytes = req->nbytes;
+
+	memcpy(sha_ctx->tmp_tbuf, sha_ctx->trailing_buf,
+					sha_ctx->trailing_buf_len);
+	k_src = &sha_ctx->trailing_buf[0];
+	/*  get new trailing buffer */
+	sha_pad_len = ALIGN(total, sha_block_size) - total;
+	trailing_buf_len =  sha_block_size - sha_pad_len;
+	nbytes = total - trailing_buf_len;
+	num_sg = qcrypto_count_sg(req->src, req->nbytes);
+
+	len = sha_ctx->trailing_buf_len;
+	i = 0;
+
+	while (len < nbytes) {
+		if ((len + req->src[i].length) > nbytes)
+			break;
+		len += req->src[i].length;
+		i++;
+	}
+
+	end_src = i;
+	if (len < nbytes) {
+		uint32_t remnant = (nbytes - len);
+		memcpy(k_src, (sg_virt(&req->src[i]) + remnant),
+				(req->src[i].length - remnant));
+		k_src += (req->src[i].length - remnant);
+		saved_length = req->src[i].length;
+		index = i;
+		req->src[i].length = remnant;
+		i++;
+	}
+
+	while (i < num_sg) {
+		memcpy(k_src, sg_virt(&req->src[i]), req->src[i].length);
+		k_src += req->src[i].length;
+		i++;
+	}
+
+	if (sha_ctx->trailing_buf_len) {
+		num_sg = end_src + 2;
+		sha_ctx->sg = kzalloc(num_sg * (sizeof(struct scatterlist)),
+								GFP_KERNEL);
+		if (sha_ctx->sg == NULL) {
+			pr_err("qcrypto Can't Allocate mem: sha_ctx->sg, error %ld\n",
+				PTR_ERR(sha_ctx->sg));
+			return -ENOMEM;
+		}
+
+		sg_set_buf(&sha_ctx->sg[0], sha_ctx->tmp_tbuf,
+						sha_ctx->trailing_buf_len);
+		for (i = 1; i < num_sg; i++)
+			sg_set_buf(&sha_ctx->sg[i], sg_virt(&req->src[i-1]),
+							req->src[i-1].length);
+
+		req->src = sha_ctx->sg;
+		sg_mark_end(&sha_ctx->sg[num_sg - 1]);
+	} else
+		sg_mark_end(&req->src[end_src]);
+
+	req->nbytes = nbytes;
+	if (saved_length > 0)
+		rctx->src[index].length = saved_length;
+	sha_ctx->trailing_buf_len = trailing_buf_len;
+
+	ret =  _qcrypto_queue_req(cp, &req->base);
+
+	return ret;
+};
+
+static int _sha1_update(struct ahash_request  *req)
+{
+	struct qcrypto_sha_req_ctx *rctx = ahash_request_ctx(req);
+	struct sha1_state *sha_state_ctx = &rctx->sha1_state_ctx;
+
+	sha_state_ctx->count += req->nbytes;
+	return _sha_update(req, SHA1_BLOCK_SIZE);
+}
+
+static int _sha256_update(struct ahash_request  *req)
+{
+	struct qcrypto_sha_req_ctx *rctx = ahash_request_ctx(req);
+	struct sha256_state *sha_state_ctx = &rctx->sha256_state_ctx;
+
+	sha_state_ctx->count += req->nbytes;
+	return _sha_update(req, SHA256_BLOCK_SIZE);
+}
+
+static int _sha_final(struct ahash_request *req, uint32_t sha_block_size)
+{
+	struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = sha_ctx->cp;
+	struct qcrypto_sha_req_ctx *rctx = ahash_request_ctx(req);
+	int ret = 0;
+
+	sha_ctx->last_blk = 1;
+
+	/* save the original req structure fields*/
+	rctx->src = req->src;
+	rctx->nbytes = req->nbytes;
+
+	sg_set_buf(&sha_ctx->tmp_sg, sha_ctx->trailing_buf,
+					sha_ctx->trailing_buf_len);
+	sg_mark_end(&sha_ctx->tmp_sg);
+
+	req->src = &sha_ctx->tmp_sg;
+	req->nbytes = sha_ctx->trailing_buf_len;
+
+	ret =  _qcrypto_queue_req(cp, &req->base);
+
+	return ret;
+};
+
+static int _sha1_final(struct ahash_request  *req)
+{
+	return _sha_final(req, SHA1_BLOCK_SIZE);
+}
+
+static int _sha256_final(struct ahash_request  *req)
+{
+	return _sha_final(req, SHA256_BLOCK_SIZE);
+}
+
+static int _sha_digest(struct ahash_request *req)
+{
+	struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(req->base.tfm);
+	struct qcrypto_sha_req_ctx *rctx = ahash_request_ctx(req);
+	struct crypto_priv *cp = sha_ctx->cp;
+	int ret = 0;
+
+	/* save the original req structure fields*/
+	rctx->src = req->src;
+	rctx->nbytes = req->nbytes;
+
+	sha_ctx->last_blk = 1;
+	ret =  _qcrypto_queue_req(cp, &req->base);
+
+	return ret;
+}
+
+static int _sha1_digest(struct ahash_request *req)
+{
+	_sha1_init(req);
+	return _sha_digest(req);
+}
+
+static int _sha256_digest(struct ahash_request *req)
+{
+	_sha256_init(req);
+	return _sha_digest(req);
+}
+
+static void _crypto_sha_hmac_ahash_req_complete(
+	struct crypto_async_request *req, int err)
+{
+	struct completion *ahash_req_complete = req->data;
+
+	if (err == -EINPROGRESS)
+		return;
+	complete(ahash_req_complete);
+}
+
+static int _sha_hmac_setkey(struct crypto_ahash *tfm, const u8 *key,
+		unsigned int len)
+{
+	struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(&tfm->base);
+	int ret = 0;
+
+	sha_ctx->in_buf = kzalloc(len, GFP_KERNEL);
+	if (sha_ctx->in_buf == NULL) {
+		pr_err("qcrypto Can't Allocate mem: sha_ctx->in_buf, error %ld\n",
+		PTR_ERR(sha_ctx->in_buf));
+		return -ENOMEM;
+	}
+	memcpy(sha_ctx->in_buf, key, len);
+	sg_set_buf(&sha_ctx->tmp_sg, sha_ctx->in_buf, len);
+	sg_mark_end(&sha_ctx->tmp_sg);
+
+	ahash_request_set_crypt(sha_ctx->ahash_req, &sha_ctx->tmp_sg,
+				&sha_ctx->authkey[0], len);
+
+	ret = _sha_digest(sha_ctx->ahash_req);
+	if (ret == -EINPROGRESS || ret == -EBUSY) {
+		ret =
+			wait_for_completion_interruptible(
+						&sha_ctx->ahash_req_complete);
+		INIT_COMPLETION(sha_ctx->ahash_req_complete);
+	}
+
+	sha_ctx->authkey_in_len = len;
+	kfree(sha_ctx->in_buf);
+	sha_ctx->in_buf = NULL;
+
+	return ret;
+}
+
+static int _sha1_hmac_setkey(struct crypto_ahash *tfm, const u8 *key,
+							unsigned int len)
+{
+	struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(&tfm->base);
+
+	if (len <= SHA1_BLOCK_SIZE)
+		memcpy(&sha_ctx->authkey[0], key, len);
+	else {
+		_sha_init(sha_ctx);
+		sha_ctx->alg = QCE_HASH_SHA1;
+		memcpy(&sha_ctx->digest[0], &_std_init_vector_sha1_uint8[0],
+						SHA1_DIGEST_SIZE);
+		sha_ctx->diglen = SHA1_DIGEST_SIZE;
+		_sha_hmac_setkey(tfm, key, len);
+	}
+	return 0;
+}
+
+static int _sha256_hmac_setkey(struct crypto_ahash *tfm, const u8 *key,
+							unsigned int len)
+{
+	struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(&tfm->base);
+
+	if (len <= SHA256_BLOCK_SIZE)
+		memcpy(&sha_ctx->authkey[0], key, len);
+	else {
+		_sha_init(sha_ctx);
+		sha_ctx->alg = QCE_HASH_SHA256;
+		memcpy(&sha_ctx->digest[0], &_std_init_vector_sha256_uint8[0],
+						SHA256_DIGEST_SIZE);
+		sha_ctx->diglen = SHA256_DIGEST_SIZE;
+		_sha_hmac_setkey(tfm, key, len);
+	}
+
+	return 0;
+}
+
+static int _sha_hmac_init_ihash(struct ahash_request *req,
+						uint32_t sha_block_size)
+{
+	struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(req->base.tfm);
+	int i;
+
+	for (i = 0; i < sha_block_size; i++)
+		sha_ctx->trailing_buf[i] = sha_ctx->authkey[i] ^ 0x36;
+	sha_ctx->trailing_buf_len = sha_block_size;
+
+	return 0;
+}
+
+static int _sha1_hmac_init(struct ahash_request *req)
+{
+	struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = sha_ctx->cp;
+	struct crypto_stat *pstat;
+	int ret = 0;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+	pstat->sha1_hmac_digest++;
+
+	_sha_init(sha_ctx);
+	memset(&sha_ctx->trailing_buf[0], 0x00, SHA1_BLOCK_SIZE);
+	memcpy(&sha_ctx->digest[0], &_std_init_vector_sha1_uint8[0],
+						SHA1_DIGEST_SIZE);
+	sha_ctx->diglen = SHA1_DIGEST_SIZE;
+	_update_sha1_ctx(req);
+
+	if (cp->ce_support.sha_hmac)
+			sha_ctx->alg = QCE_HASH_SHA1_HMAC;
+	else {
+		sha_ctx->alg = QCE_HASH_SHA1;
+		ret = _sha_hmac_init_ihash(req, SHA1_BLOCK_SIZE);
+	}
+
+	return ret;
+}
+
+static int _sha256_hmac_init(struct ahash_request *req)
+{
+	struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = sha_ctx->cp;
+	struct crypto_stat *pstat;
+	int ret = 0;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+	pstat->sha256_hmac_digest++;
+
+	_sha_init(sha_ctx);
+	memset(&sha_ctx->trailing_buf[0], 0x00, SHA256_BLOCK_SIZE);
+	memcpy(&sha_ctx->digest[0], &_std_init_vector_sha256_uint8[0],
+						SHA256_DIGEST_SIZE);
+	sha_ctx->diglen = SHA256_DIGEST_SIZE;
+	_update_sha256_ctx(req);
+
+	if (cp->ce_support.sha_hmac)
+		sha_ctx->alg = QCE_HASH_SHA256_HMAC;
+	else {
+		sha_ctx->alg = QCE_HASH_SHA256;
+		ret = _sha_hmac_init_ihash(req, SHA256_BLOCK_SIZE);
+	}
+
+	return ret;
+}
+
+static int _sha1_hmac_update(struct ahash_request *req)
+{
+	return _sha1_update(req);
+}
+
+static int _sha256_hmac_update(struct ahash_request *req)
+{
+	return _sha256_update(req);
+}
+
+static int _sha_hmac_outer_hash(struct ahash_request *req,
+		uint32_t sha_digest_size, uint32_t sha_block_size)
+{
+	struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(req->base.tfm);
+	struct qcrypto_sha_req_ctx *rctx = ahash_request_ctx(req);
+	struct crypto_priv *cp = sha_ctx->cp;
+	int i;
+
+	for (i = 0; i < sha_block_size; i++)
+		sha_ctx->tmp_tbuf[i] = sha_ctx->authkey[i] ^ 0x5c;
+
+	/* save the original req structure fields*/
+	rctx->src = req->src;
+	rctx->nbytes = req->nbytes;
+
+	memcpy(&sha_ctx->tmp_tbuf[sha_block_size], &sha_ctx->digest[0],
+						 sha_digest_size);
+
+	sg_set_buf(&sha_ctx->tmp_sg, sha_ctx->tmp_tbuf, sha_block_size +
+							sha_digest_size);
+	sg_mark_end(&sha_ctx->tmp_sg);
+	req->src = &sha_ctx->tmp_sg;
+	req->nbytes = sha_block_size + sha_digest_size;
+
+	_sha_init(sha_ctx);
+	if (sha_ctx->alg == QCE_HASH_SHA1) {
+		memcpy(&sha_ctx->digest[0], &_std_init_vector_sha1_uint8[0],
+							SHA1_DIGEST_SIZE);
+		sha_ctx->diglen = SHA1_DIGEST_SIZE;
+	} else {
+		memcpy(&sha_ctx->digest[0], &_std_init_vector_sha256_uint8[0],
+							SHA256_DIGEST_SIZE);
+		sha_ctx->diglen = SHA256_DIGEST_SIZE;
+	}
+
+	sha_ctx->last_blk = 1;
+	return  _qcrypto_queue_req(cp, &req->base);
+}
+
+static int _sha_hmac_inner_hash(struct ahash_request *req,
+			uint32_t sha_digest_size, uint32_t sha_block_size)
+{
+	struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(req->base.tfm);
+	struct ahash_request *areq = sha_ctx->ahash_req;
+	struct crypto_priv *cp = sha_ctx->cp;
+	int ret = 0;
+
+	sha_ctx->last_blk = 1;
+
+	sg_set_buf(&sha_ctx->tmp_sg, sha_ctx->trailing_buf,
+					sha_ctx->trailing_buf_len);
+	sg_mark_end(&sha_ctx->tmp_sg);
+
+	ahash_request_set_crypt(areq, &sha_ctx->tmp_sg, &sha_ctx->digest[0],
+						sha_ctx->trailing_buf_len);
+	sha_ctx->last_blk = 1;
+	ret =  _qcrypto_queue_req(cp, &areq->base);
+
+	if (ret == -EINPROGRESS || ret == -EBUSY) {
+		ret =
+		wait_for_completion_interruptible(&sha_ctx->ahash_req_complete);
+		INIT_COMPLETION(sha_ctx->ahash_req_complete);
+	}
+
+	return ret;
+}
+
+static int _sha1_hmac_final(struct ahash_request *req)
+{
+	struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = sha_ctx->cp;
+	int ret = 0;
+
+	if (cp->ce_support.sha_hmac)
+		return _sha_final(req, SHA1_BLOCK_SIZE);
+	else {
+		ret = _sha_hmac_inner_hash(req, SHA1_DIGEST_SIZE,
+							SHA1_BLOCK_SIZE);
+		if (ret)
+			return ret;
+		return _sha_hmac_outer_hash(req, SHA1_DIGEST_SIZE,
+							SHA1_BLOCK_SIZE);
+	}
+}
+
+static int _sha256_hmac_final(struct ahash_request *req)
+{
+	struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = sha_ctx->cp;
+	int ret = 0;
+
+	if (cp->ce_support.sha_hmac)
+		return _sha_final(req, SHA256_BLOCK_SIZE);
+	else {
+		ret = _sha_hmac_inner_hash(req, SHA256_DIGEST_SIZE,
+							SHA256_BLOCK_SIZE);
+		if (ret)
+			return ret;
+		return _sha_hmac_outer_hash(req, SHA256_DIGEST_SIZE,
+							SHA256_BLOCK_SIZE);
+	}
+	return 0;
+}
+
+
+static int _sha1_hmac_digest(struct ahash_request *req)
+{
+	struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = sha_ctx->cp;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+	pstat->sha1_hmac_digest++;
+
+	_sha_init(sha_ctx);
+	memcpy(&sha_ctx->digest[0], &_std_init_vector_sha1_uint8[0],
+							SHA1_DIGEST_SIZE);
+	sha_ctx->diglen = SHA1_DIGEST_SIZE;
+	sha_ctx->alg = QCE_HASH_SHA1_HMAC;
+
+	return _sha_digest(req);
+}
+
+static int _sha256_hmac_digest(struct ahash_request *req)
+{
+	struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(req->base.tfm);
+	struct crypto_priv *cp = sha_ctx->cp;
+	struct crypto_stat *pstat;
+
+	pstat = &_qcrypto_stat[cp->pdev->id];
+	pstat->sha256_hmac_digest++;
+
+	_sha_init(sha_ctx);
+	memcpy(&sha_ctx->digest[0], &_std_init_vector_sha256_uint8[0],
+						SHA256_DIGEST_SIZE);
+	sha_ctx->diglen = SHA256_DIGEST_SIZE;
+	sha_ctx->alg = QCE_HASH_SHA256_HMAC;
+
+	return _sha_digest(req);
+}
+
+static struct ahash_alg _qcrypto_ahash_algos[] = {
+	{
+		.init		=	_sha1_init,
+		.update		=	_sha1_update,
+		.final		=	_sha1_final,
+		.export		=	_sha1_export,
+		.import		=	_sha1_import,
+		.digest		=	_sha1_digest,
+		.halg		= {
+			.digestsize	= SHA1_DIGEST_SIZE,
+			.statesize	= sizeof(struct sha1_state),
+			.base	= {
+				.cra_name	 = "sha1",
+				.cra_driver_name = "qcrypto-sha1",
+				.cra_priority	 = 300,
+				.cra_flags	 = CRYPTO_ALG_TYPE_AHASH |
+							 CRYPTO_ALG_ASYNC,
+				.cra_blocksize	 = SHA1_BLOCK_SIZE,
+				.cra_ctxsize	 =
+						sizeof(struct qcrypto_sha_ctx),
+				.cra_alignmask	 = 0,
+				.cra_type	 = &crypto_ahash_type,
+				.cra_module	 = THIS_MODULE,
+				.cra_init	 = _qcrypto_ahash_cra_init,
+				.cra_exit	 = _qcrypto_ahash_cra_exit,
+			},
+		},
+	},
+	{
+		.init		=	_sha256_init,
+		.update		=	_sha256_update,
+		.final		=	_sha256_final,
+		.export		=	_sha256_export,
+		.import		=	_sha256_import,
+		.digest		=	_sha256_digest,
+		.halg		= {
+			.digestsize	= SHA256_DIGEST_SIZE,
+			.statesize	= sizeof(struct sha256_state),
+			.base		= {
+				.cra_name	 = "sha256",
+				.cra_driver_name = "qcrypto-sha256",
+				.cra_priority	 = 300,
+				.cra_flags	 = CRYPTO_ALG_TYPE_AHASH |
+							CRYPTO_ALG_ASYNC,
+				.cra_blocksize	 = SHA256_BLOCK_SIZE,
+				.cra_ctxsize	 =
+						sizeof(struct qcrypto_sha_ctx),
+				.cra_alignmask	 = 0,
+				.cra_type	 = &crypto_ahash_type,
+				.cra_module	 = THIS_MODULE,
+				.cra_init	 = _qcrypto_ahash_cra_init,
+				.cra_exit	 = _qcrypto_ahash_cra_exit,
+			},
+		},
+	},
+};
+
+static struct ahash_alg _qcrypto_sha_hmac_algos[] = {
+	{
+		.init		=	_sha1_hmac_init,
+		.update		=	_sha1_hmac_update,
+		.final		=	_sha1_hmac_final,
+		.export		=	_sha1_export,
+		.import		=	_sha1_import,
+		.digest		=	_sha1_hmac_digest,
+		.setkey		=	_sha1_hmac_setkey,
+		.halg		= {
+			.digestsize	= SHA1_DIGEST_SIZE,
+			.statesize	= sizeof(struct sha1_state),
+			.base	= {
+				.cra_name	 = "hmac(sha1)",
+				.cra_driver_name = "qcrypto-hmac-sha1",
+				.cra_priority	 = 300,
+				.cra_flags	 = CRYPTO_ALG_TYPE_AHASH |
+							 CRYPTO_ALG_ASYNC,
+				.cra_blocksize	 = SHA1_BLOCK_SIZE,
+				.cra_ctxsize	 =
+						sizeof(struct qcrypto_sha_ctx),
+				.cra_alignmask	 = 0,
+				.cra_type	 = &crypto_ahash_type,
+				.cra_module	 = THIS_MODULE,
+				.cra_init	 = _qcrypto_ahash_hmac_cra_init,
+				.cra_exit	 = _qcrypto_ahash_cra_exit,
+			},
+		},
+	},
+	{
+		.init		=	_sha256_hmac_init,
+		.update		=	_sha256_hmac_update,
+		.final		=	_sha256_hmac_final,
+		.export		=	_sha256_export,
+		.import		=	_sha256_import,
+		.digest		=	_sha256_hmac_digest,
+		.setkey		=	_sha256_hmac_setkey,
+		.halg		= {
+			.digestsize	= SHA256_DIGEST_SIZE,
+			.statesize	= sizeof(struct sha256_state),
+			.base		= {
+				.cra_name	 = "hmac(sha256)",
+				.cra_driver_name = "qcrypto-hmac-sha256",
+				.cra_priority	 = 300,
+				.cra_flags	 = CRYPTO_ALG_TYPE_AHASH |
+							CRYPTO_ALG_ASYNC,
+				.cra_blocksize	 = SHA256_BLOCK_SIZE,
+				.cra_ctxsize	 =
+						sizeof(struct qcrypto_sha_ctx),
+				.cra_alignmask	 = 0,
+				.cra_type	 = &crypto_ahash_type,
+				.cra_module	 = THIS_MODULE,
+				.cra_init	 = _qcrypto_ahash_hmac_cra_init,
+				.cra_exit	 = _qcrypto_ahash_cra_exit,
+			},
+		},
+	},
+};
+
+static struct crypto_alg _qcrypto_ablk_cipher_algos[] = {
+	{
+		.cra_name		= "ecb(aes)",
+		.cra_driver_name	= "qcrypto-ecb-aes",
+		.cra_priority	= 300,
+		.cra_flags	= CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
+		.cra_blocksize	= AES_BLOCK_SIZE,
+		.cra_ctxsize	= sizeof(struct qcrypto_cipher_ctx),
+		.cra_alignmask	= 0,
+		.cra_type	= &crypto_ablkcipher_type,
+		.cra_module	= THIS_MODULE,
+		.cra_init	= _qcrypto_cra_ablkcipher_init,
+		.cra_exit	= _qcrypto_cra_ablkcipher_exit,
+		.cra_u		= {
+			.ablkcipher = {
+				.min_keysize	= AES_MIN_KEY_SIZE,
+				.max_keysize	= AES_MAX_KEY_SIZE,
+				.setkey		= _qcrypto_setkey_aes,
+				.encrypt	= _qcrypto_enc_aes_ecb,
+				.decrypt	= _qcrypto_dec_aes_ecb,
+			},
+		},
+	},
+	{
+		.cra_name	= "cbc(aes)",
+		.cra_driver_name = "qcrypto-cbc-aes",
+		.cra_priority	= 300,
+		.cra_flags	= CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
+		.cra_blocksize	= AES_BLOCK_SIZE,
+		.cra_ctxsize	= sizeof(struct qcrypto_cipher_ctx),
+		.cra_alignmask	= 0,
+		.cra_type	= &crypto_ablkcipher_type,
+		.cra_module	= THIS_MODULE,
+		.cra_init	= _qcrypto_cra_ablkcipher_init,
+		.cra_exit	= _qcrypto_cra_ablkcipher_exit,
+		.cra_u		= {
+			.ablkcipher = {
+				.ivsize		= AES_BLOCK_SIZE,
+				.min_keysize	= AES_MIN_KEY_SIZE,
+				.max_keysize	= AES_MAX_KEY_SIZE,
+				.setkey		= _qcrypto_setkey_aes,
+				.encrypt	= _qcrypto_enc_aes_cbc,
+				.decrypt	= _qcrypto_dec_aes_cbc,
+			},
+		},
+	},
+	{
+		.cra_name	= "ctr(aes)",
+		.cra_driver_name = "qcrypto-ctr-aes",
+		.cra_priority	= 300,
+		.cra_flags	= CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
+		.cra_blocksize	= AES_BLOCK_SIZE,
+		.cra_ctxsize	= sizeof(struct qcrypto_cipher_ctx),
+		.cra_alignmask	= 0,
+		.cra_type	= &crypto_ablkcipher_type,
+		.cra_module	= THIS_MODULE,
+		.cra_init	= _qcrypto_cra_ablkcipher_init,
+		.cra_exit	= _qcrypto_cra_ablkcipher_exit,
+		.cra_u		= {
+			.ablkcipher = {
+				.ivsize		= AES_BLOCK_SIZE,
+				.min_keysize	= AES_MIN_KEY_SIZE,
+				.max_keysize	= AES_MAX_KEY_SIZE,
+				.setkey		= _qcrypto_setkey_aes,
+				.encrypt	= _qcrypto_enc_aes_ctr,
+				.decrypt	= _qcrypto_dec_aes_ctr,
+			},
+		},
+	},
+	{
+		.cra_name		= "ecb(des)",
+		.cra_driver_name	= "qcrypto-ecb-des",
+		.cra_priority	= 300,
+		.cra_flags	= CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
+		.cra_blocksize	= DES_BLOCK_SIZE,
+		.cra_ctxsize	= sizeof(struct qcrypto_cipher_ctx),
+		.cra_alignmask	= 0,
+		.cra_type	= &crypto_ablkcipher_type,
+		.cra_module	= THIS_MODULE,
+		.cra_init	= _qcrypto_cra_ablkcipher_init,
+		.cra_exit	= _qcrypto_cra_ablkcipher_exit,
+		.cra_u		= {
+			.ablkcipher = {
+				.min_keysize	= DES_KEY_SIZE,
+				.max_keysize	= DES_KEY_SIZE,
+				.setkey		= _qcrypto_setkey_des,
+				.encrypt	= _qcrypto_enc_des_ecb,
+				.decrypt	= _qcrypto_dec_des_ecb,
+			},
+		},
+	},
+	{
+		.cra_name	= "cbc(des)",
+		.cra_driver_name = "qcrypto-cbc-des",
+		.cra_priority	= 300,
+		.cra_flags	= CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
+		.cra_blocksize	= DES_BLOCK_SIZE,
+		.cra_ctxsize	= sizeof(struct qcrypto_cipher_ctx),
+		.cra_alignmask	= 0,
+		.cra_type	= &crypto_ablkcipher_type,
+		.cra_module	= THIS_MODULE,
+		.cra_init	= _qcrypto_cra_ablkcipher_init,
+		.cra_exit	= _qcrypto_cra_ablkcipher_exit,
+		.cra_u		= {
+			.ablkcipher = {
+				.ivsize		= DES_BLOCK_SIZE,
+				.min_keysize	= DES_KEY_SIZE,
+				.max_keysize	= DES_KEY_SIZE,
+				.setkey		= _qcrypto_setkey_des,
+				.encrypt	= _qcrypto_enc_des_cbc,
+				.decrypt	= _qcrypto_dec_des_cbc,
+			},
+		},
+	},
+	{
+		.cra_name		= "ecb(des3_ede)",
+		.cra_driver_name	= "qcrypto-ecb-3des",
+		.cra_priority	= 300,
+		.cra_flags	= CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
+		.cra_blocksize	= DES3_EDE_BLOCK_SIZE,
+		.cra_ctxsize	= sizeof(struct qcrypto_cipher_ctx),
+		.cra_alignmask	= 0,
+		.cra_type	= &crypto_ablkcipher_type,
+		.cra_module	= THIS_MODULE,
+		.cra_init	= _qcrypto_cra_ablkcipher_init,
+		.cra_exit	= _qcrypto_cra_ablkcipher_exit,
+		.cra_u		= {
+			.ablkcipher = {
+				.min_keysize	= DES3_EDE_KEY_SIZE,
+				.max_keysize	= DES3_EDE_KEY_SIZE,
+				.setkey		= _qcrypto_setkey_3des,
+				.encrypt	= _qcrypto_enc_3des_ecb,
+				.decrypt	= _qcrypto_dec_3des_ecb,
+			},
+		},
+	},
+	{
+		.cra_name	= "cbc(des3_ede)",
+		.cra_driver_name = "qcrypto-cbc-3des",
+		.cra_priority	= 300,
+		.cra_flags	= CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
+		.cra_blocksize	= DES3_EDE_BLOCK_SIZE,
+		.cra_ctxsize	= sizeof(struct qcrypto_cipher_ctx),
+		.cra_alignmask	= 0,
+		.cra_type	= &crypto_ablkcipher_type,
+		.cra_module	= THIS_MODULE,
+		.cra_init	= _qcrypto_cra_ablkcipher_init,
+		.cra_exit	= _qcrypto_cra_ablkcipher_exit,
+		.cra_u		= {
+			.ablkcipher = {
+				.ivsize		= DES3_EDE_BLOCK_SIZE,
+				.min_keysize	= DES3_EDE_KEY_SIZE,
+				.max_keysize	= DES3_EDE_KEY_SIZE,
+				.setkey		= _qcrypto_setkey_3des,
+				.encrypt	= _qcrypto_enc_3des_cbc,
+				.decrypt	= _qcrypto_dec_3des_cbc,
+			},
+		},
+	},
+};
+
+static struct crypto_alg _qcrypto_ablk_cipher_xts_algo = {
+	.cra_name	= "xts(aes)",
+	.cra_driver_name = "qcrypto-xts-aes",
+	.cra_priority	= 300,
+	.cra_flags	= CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
+	.cra_blocksize	= AES_BLOCK_SIZE,
+	.cra_ctxsize	= sizeof(struct qcrypto_cipher_ctx),
+	.cra_alignmask	= 0,
+	.cra_type	= &crypto_ablkcipher_type,
+	.cra_module	= THIS_MODULE,
+	.cra_init	= _qcrypto_cra_ablkcipher_init,
+	.cra_exit	= _qcrypto_cra_ablkcipher_exit,
+	.cra_u		= {
+		.ablkcipher = {
+			.ivsize		= AES_BLOCK_SIZE,
+			.min_keysize	= AES_MIN_KEY_SIZE,
+			.max_keysize	= AES_MAX_KEY_SIZE,
+			.setkey		= _qcrypto_setkey_aes,
+			.encrypt	= _qcrypto_enc_aes_xts,
+			.decrypt	= _qcrypto_dec_aes_xts,
+		},
+	},
+};
+
+static struct crypto_alg _qcrypto_aead_sha1_hmac_algos[] = {
+	{
+		.cra_name	= "authenc(hmac(sha1),cbc(aes))",
+		.cra_driver_name = "qcrypto-aead-hmac-sha1-cbc-aes",
+		.cra_priority	= 300,
+		.cra_flags	= CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_ASYNC,
+		.cra_blocksize  = AES_BLOCK_SIZE,
+		.cra_ctxsize	= sizeof(struct qcrypto_cipher_ctx),
+		.cra_alignmask	= 0,
+		.cra_type	= &crypto_aead_type,
+		.cra_module	= THIS_MODULE,
+		.cra_init	= _qcrypto_cra_aead_init,
+		.cra_exit	= _qcrypto_cra_aead_exit,
+		.cra_u		= {
+			.aead = {
+				.ivsize         = AES_BLOCK_SIZE,
+				.maxauthsize    = SHA1_DIGEST_SIZE,
+				.setkey = _qcrypto_aead_setkey,
+				.setauthsize = _qcrypto_aead_setauthsize,
+				.encrypt = _qcrypto_aead_encrypt_aes_cbc,
+				.decrypt = _qcrypto_aead_decrypt_aes_cbc,
+				.givencrypt = _qcrypto_aead_givencrypt_aes_cbc,
+				.geniv = "<built-in>",
+			}
+		}
+	},
+
+#ifdef QCRYPTO_AEAD_AES_CTR
+	{
+		.cra_name	= "authenc(hmac(sha1),ctr(aes))",
+		.cra_driver_name = "qcrypto-aead-hmac-sha1-ctr-aes",
+		.cra_priority	= 300,
+		.cra_flags	= CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_ASYNC,
+		.cra_blocksize  = AES_BLOCK_SIZE,
+		.cra_ctxsize	= sizeof(struct qcrypto_cipher_ctx),
+		.cra_alignmask	= 0,
+		.cra_type	= &crypto_aead_type,
+		.cra_module	= THIS_MODULE,
+		.cra_init	= _qcrypto_cra_aead_init,
+		.cra_exit	= _qcrypto_cra_aead_exit,
+		.cra_u		= {
+			.aead = {
+				.ivsize         = AES_BLOCK_SIZE,
+				.maxauthsize    = SHA1_DIGEST_SIZE,
+				.setkey = _qcrypto_aead_setkey,
+				.setauthsize = _qcrypto_aead_setauthsize,
+				.encrypt = _qcrypto_aead_encrypt_aes_ctr,
+				.decrypt = _qcrypto_aead_decrypt_aes_ctr,
+				.givencrypt = _qcrypto_aead_givencrypt_aes_ctr,
+				.geniv = "<built-in>",
+			}
+		}
+	},
+#endif /* QCRYPTO_AEAD_AES_CTR */
+	{
+		.cra_name	= "authenc(hmac(sha1),cbc(des))",
+		.cra_driver_name = "qcrypto-aead-hmac-sha1-cbc-des",
+		.cra_priority	= 300,
+		.cra_flags	= CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_ASYNC,
+		.cra_blocksize  = DES_BLOCK_SIZE,
+		.cra_ctxsize	= sizeof(struct qcrypto_cipher_ctx),
+		.cra_alignmask	= 0,
+		.cra_type	= &crypto_aead_type,
+		.cra_module	= THIS_MODULE,
+		.cra_init	= _qcrypto_cra_aead_init,
+		.cra_exit	= _qcrypto_cra_aead_exit,
+		.cra_u		= {
+			.aead = {
+				.ivsize         = DES_BLOCK_SIZE,
+				.maxauthsize    = SHA1_DIGEST_SIZE,
+				.setkey = _qcrypto_aead_setkey,
+				.setauthsize = _qcrypto_aead_setauthsize,
+				.encrypt = _qcrypto_aead_encrypt_des_cbc,
+				.decrypt = _qcrypto_aead_decrypt_des_cbc,
+				.givencrypt = _qcrypto_aead_givencrypt_des_cbc,
+				.geniv = "<built-in>",
+			}
+		}
+	},
+	{
+		.cra_name	= "authenc(hmac(sha1),cbc(des3_ede))",
+		.cra_driver_name = "qcrypto-aead-hmac-sha1-cbc-3des",
+		.cra_priority	= 300,
+		.cra_flags	= CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_ASYNC,
+		.cra_blocksize  = DES3_EDE_BLOCK_SIZE,
+		.cra_ctxsize	= sizeof(struct qcrypto_cipher_ctx),
+		.cra_alignmask	= 0,
+		.cra_type	= &crypto_aead_type,
+		.cra_module	= THIS_MODULE,
+		.cra_init	= _qcrypto_cra_aead_init,
+		.cra_exit	= _qcrypto_cra_aead_exit,
+		.cra_u		= {
+			.aead = {
+				.ivsize         = DES3_EDE_BLOCK_SIZE,
+				.maxauthsize    = SHA1_DIGEST_SIZE,
+				.setkey = _qcrypto_aead_setkey,
+				.setauthsize = _qcrypto_aead_setauthsize,
+				.encrypt = _qcrypto_aead_encrypt_3des_cbc,
+				.decrypt = _qcrypto_aead_decrypt_3des_cbc,
+				.givencrypt = _qcrypto_aead_givencrypt_3des_cbc,
+				.geniv = "<built-in>",
+			}
+		}
+	},
+};
+
+static struct crypto_alg _qcrypto_aead_ccm_algo = {
+	.cra_name	= "ccm(aes)",
+	.cra_driver_name = "qcrypto-aes-ccm",
+	.cra_priority	= 300,
+	.cra_flags	= CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_ASYNC,
+	.cra_blocksize  = AES_BLOCK_SIZE,
+	.cra_ctxsize	= sizeof(struct qcrypto_cipher_ctx),
+	.cra_alignmask	= 0,
+	.cra_type	= &crypto_aead_type,
+	.cra_module	= THIS_MODULE,
+	.cra_init	= _qcrypto_cra_aead_init,
+	.cra_exit	= _qcrypto_cra_aead_exit,
+	.cra_u		= {
+		.aead = {
+			.ivsize         = AES_BLOCK_SIZE,
+			.maxauthsize    = SHA1_DIGEST_SIZE,
+			.setkey = _qcrypto_aead_ccm_setkey,
+			.setauthsize = _qcrypto_aead_ccm_setauthsize,
+			.encrypt = _qcrypto_aead_encrypt_aes_ccm,
+			.decrypt = _qcrypto_aead_decrypt_aes_ccm,
+			.geniv = "<built-in>",
+		}
+	}
+};
+
+
+static int  _qcrypto_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	void *handle;
+	struct crypto_priv *cp;
+	int i;
+	struct msm_ce_hw_support *platform_support;
+
+	if (pdev->id >= MAX_CRYPTO_DEVICE) {
+		pr_err("%s: device id %d  exceeds allowed %d\n",
+				__func__, pdev->id, MAX_CRYPTO_DEVICE);
+		return -ENOENT;
+	}
+
+	cp = kzalloc(sizeof(*cp), GFP_KERNEL);
+	if (!cp) {
+		pr_err("qcrypto Memory allocation of q_alg FAIL, error %ld\n",
+				PTR_ERR(cp));
+		return -ENOMEM;
+	}
+
+	/* open qce */
+	handle = qce_open(pdev, &rc);
+	if (handle == NULL) {
+		kfree(cp);
+		platform_set_drvdata(pdev, NULL);
+		return rc;
+	}
+
+	INIT_LIST_HEAD(&cp->alg_list);
+	platform_set_drvdata(pdev, cp);
+	spin_lock_init(&cp->lock);
+	tasklet_init(&cp->done_tasklet, req_done, (unsigned long)cp);
+	crypto_init_queue(&cp->queue, 50);
+	cp->qce = handle;
+	cp->pdev = pdev;
+	qce_hw_support(cp->qce, &cp->ce_support);
+	platform_support = (struct msm_ce_hw_support *)pdev->dev.platform_data;
+	cp->platform_support.ce_shared = platform_support->ce_shared;
+	cp->platform_support.shared_ce_resource =
+				platform_support->shared_ce_resource;
+	cp->platform_support.hw_key_support =
+				platform_support->hw_key_support;
+	cp->platform_support.bus_scale_table =
+				platform_support->bus_scale_table;
+	cp->high_bw_req_count = 0;
+	cp->ce_lock_count = 0;
+	cp->platform_support.sha_hmac = platform_support->sha_hmac;
+
+	if (cp->platform_support.ce_shared)
+		INIT_WORK(&cp->unlock_ce_ws, qcrypto_unlock_ce);
+
+	if (cp->platform_support.bus_scale_table != NULL) {
+		cp->bus_scale_handle =
+			msm_bus_scale_register_client(
+				(struct msm_bus_scale_pdata *)
+					cp->platform_support.bus_scale_table);
+		if (!cp->bus_scale_handle) {
+			printk(KERN_ERR "%s not able to get bus scale\n",
+				__func__);
+			rc =  -ENOMEM;
+			goto err;
+		}
+	}
+
+	/* register crypto cipher algorithms the device supports */
+	for (i = 0; i < ARRAY_SIZE(_qcrypto_ablk_cipher_algos); i++) {
+		struct qcrypto_alg *q_alg;
+
+		q_alg = _qcrypto_cipher_alg_alloc(cp,
+					&_qcrypto_ablk_cipher_algos[i]);
+		if (IS_ERR(q_alg)) {
+			rc = PTR_ERR(q_alg);
+			goto err;
+		}
+		rc = crypto_register_alg(&q_alg->cipher_alg);
+		if (rc) {
+			dev_err(&pdev->dev, "%s alg registration failed\n",
+					q_alg->cipher_alg.cra_driver_name);
+			kfree(q_alg);
+		} else {
+			list_add_tail(&q_alg->entry, &cp->alg_list);
+			dev_info(&pdev->dev, "%s\n",
+					q_alg->cipher_alg.cra_driver_name);
+		}
+	}
+
+	/* register crypto cipher algorithms the device supports */
+	if (cp->ce_support.aes_xts) {
+		struct qcrypto_alg *q_alg;
+
+		q_alg = _qcrypto_cipher_alg_alloc(cp,
+					&_qcrypto_ablk_cipher_xts_algo);
+		if (IS_ERR(q_alg)) {
+			rc = PTR_ERR(q_alg);
+			goto err;
+		}
+		rc = crypto_register_alg(&q_alg->cipher_alg);
+		if (rc) {
+			dev_err(&pdev->dev, "%s alg registration failed\n",
+					q_alg->cipher_alg.cra_driver_name);
+			kfree(q_alg);
+		} else {
+			list_add_tail(&q_alg->entry, &cp->alg_list);
+			dev_info(&pdev->dev, "%s\n",
+					q_alg->cipher_alg.cra_driver_name);
+		}
+	}
+
+	/*
+	 * Register crypto hash (sha1 and sha256) algorithms the
+	 * device supports
+	 */
+	for (i = 0; i < ARRAY_SIZE(_qcrypto_ahash_algos); i++) {
+		struct qcrypto_alg *q_alg = NULL;
+
+		q_alg = _qcrypto_sha_alg_alloc(cp, &_qcrypto_ahash_algos[i]);
+
+		if (IS_ERR(q_alg)) {
+			rc = PTR_ERR(q_alg);
+			goto err;
+		}
+
+		rc = crypto_register_ahash(&q_alg->sha_alg);
+		if (rc) {
+			dev_err(&pdev->dev, "%s alg registration failed\n",
+				q_alg->sha_alg.halg.base.cra_driver_name);
+			kfree(q_alg);
+		} else {
+			list_add_tail(&q_alg->entry, &cp->alg_list);
+			dev_info(&pdev->dev, "%s\n",
+				q_alg->sha_alg.halg.base.cra_driver_name);
+		}
+	}
+
+	/* register crypto aead (hmac-sha1) algorithms the device supports */
+	if (cp->ce_support.sha1_hmac_20 || cp->ce_support.sha1_hmac) {
+		for (i = 0; i < ARRAY_SIZE(_qcrypto_aead_sha1_hmac_algos);
+									i++) {
+			struct qcrypto_alg *q_alg;
+
+			q_alg = _qcrypto_cipher_alg_alloc(cp,
+					&_qcrypto_aead_sha1_hmac_algos[i]);
+			if (IS_ERR(q_alg)) {
+				rc = PTR_ERR(q_alg);
+				goto err;
+			}
+
+			rc = crypto_register_alg(&q_alg->cipher_alg);
+			if (rc) {
+				dev_err(&pdev->dev,
+					"%s alg registration failed\n",
+					q_alg->cipher_alg.cra_driver_name);
+				kfree(q_alg);
+			} else {
+				list_add_tail(&q_alg->entry, &cp->alg_list);
+				dev_info(&pdev->dev, "%s\n",
+					q_alg->cipher_alg.cra_driver_name);
+			}
+		}
+	}
+
+	if ((cp->ce_support.sha_hmac) || (cp->platform_support.sha_hmac)) {
+		/* register crypto hmac algorithms the device supports */
+		for (i = 0; i < ARRAY_SIZE(_qcrypto_sha_hmac_algos); i++) {
+			struct qcrypto_alg *q_alg = NULL;
+
+			q_alg = _qcrypto_sha_alg_alloc(cp,
+						&_qcrypto_sha_hmac_algos[i]);
+
+			if (IS_ERR(q_alg)) {
+				rc = PTR_ERR(q_alg);
+				goto err;
+			}
+
+			rc = crypto_register_ahash(&q_alg->sha_alg);
+			if (rc) {
+				dev_err(&pdev->dev,
+				"%s alg registration failed\n",
+				q_alg->sha_alg.halg.base.cra_driver_name);
+				kfree(q_alg);
+			} else {
+				list_add_tail(&q_alg->entry, &cp->alg_list);
+				dev_info(&pdev->dev, "%s\n",
+				q_alg->sha_alg.halg.base.cra_driver_name);
+			}
+		}
+	}
+	/*
+	 * Register crypto cipher (aes-ccm) algorithms the
+	 * device supports
+	 */
+	if (cp->ce_support.aes_ccm) {
+		struct qcrypto_alg *q_alg;
+
+		q_alg = _qcrypto_cipher_alg_alloc(cp, &_qcrypto_aead_ccm_algo);
+		if (IS_ERR(q_alg)) {
+			rc = PTR_ERR(q_alg);
+			goto err;
+		}
+		rc = crypto_register_alg(&q_alg->cipher_alg);
+		if (rc) {
+			dev_err(&pdev->dev, "%s alg registration failed\n",
+					q_alg->cipher_alg.cra_driver_name);
+			kfree(q_alg);
+		} else {
+			list_add_tail(&q_alg->entry, &cp->alg_list);
+			dev_info(&pdev->dev, "%s\n",
+					q_alg->cipher_alg.cra_driver_name);
+		}
+	}
+
+	return 0;
+err:
+	_qcrypto_remove(pdev);
+	return rc;
+};
+
+static struct platform_driver _qualcomm_crypto = {
+	.probe          = _qcrypto_probe,
+	.remove         = _qcrypto_remove,
+	.driver         = {
+		.owner  = THIS_MODULE,
+		.name   = "qcrypto",
+	},
+};
+
+static int _debug_qcrypto[MAX_CRYPTO_DEVICE];
+
+static int _debug_stats_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t _debug_stats_read(struct file *file, char __user *buf,
+			size_t count, loff_t *ppos)
+{
+	int rc = -EINVAL;
+	int qcrypto = *((int *) file->private_data);
+	int len;
+
+	len = _disp_stats(qcrypto);
+
+	rc = simple_read_from_buffer((void __user *) buf, len,
+			ppos, (void *) _debug_read_buf, len);
+
+	return rc;
+}
+
+static ssize_t _debug_stats_write(struct file *file, const char __user *buf,
+			size_t count, loff_t *ppos)
+{
+
+	int qcrypto = *((int *) file->private_data);
+
+	memset((char *)&_qcrypto_stat[qcrypto], 0, sizeof(struct crypto_stat));
+	return count;
+};
+
+static const struct file_operations _debug_stats_ops = {
+	.open =         _debug_stats_open,
+	.read =         _debug_stats_read,
+	.write =        _debug_stats_write,
+};
+
+static int _qcrypto_debug_init(void)
+{
+	int rc;
+	char name[DEBUG_MAX_FNAME];
+	int i;
+	struct dentry *dent;
+
+	_debug_dent = debugfs_create_dir("qcrypto", NULL);
+	if (IS_ERR(_debug_dent)) {
+		pr_err("qcrypto debugfs_create_dir fail, error %ld\n",
+				PTR_ERR(_debug_dent));
+		return PTR_ERR(_debug_dent);
+	}
+
+	for (i = 0; i < MAX_CRYPTO_DEVICE; i++) {
+		snprintf(name, DEBUG_MAX_FNAME-1, "stats-%d", i+1);
+		_debug_qcrypto[i] = i;
+		dent = debugfs_create_file(name, 0644, _debug_dent,
+				&_debug_qcrypto[i], &_debug_stats_ops);
+		if (dent == NULL) {
+			pr_err("qcrypto debugfs_create_file fail, error %ld\n",
+					PTR_ERR(dent));
+			rc = PTR_ERR(dent);
+			goto err;
+		}
+	}
+	return 0;
+err:
+	debugfs_remove_recursive(_debug_dent);
+	return rc;
+}
+
+static int __init _qcrypto_init(void)
+{
+	int rc;
+
+	rc = _qcrypto_debug_init();
+	if (rc)
+		return rc;
+
+	return platform_driver_register(&_qualcomm_crypto);
+}
+
+static void __exit _qcrypto_exit(void)
+{
+	pr_debug("%s Unregister QCRYPTO\n", __func__);
+	debugfs_remove_recursive(_debug_dent);
+	platform_driver_unregister(&_qualcomm_crypto);
+}
+
+module_init(_qcrypto_init);
+module_exit(_qcrypto_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Mona Hossain <mhossain@codeaurora.org>");
+MODULE_DESCRIPTION("Qualcomm Crypto driver");
+MODULE_VERSION("1.21");
diff --git a/drivers/crypto/msm/qcryptohw_30.h b/drivers/crypto/msm/qcryptohw_30.h
new file mode 100644
index 0000000..edbee71
--- /dev/null
+++ b/drivers/crypto/msm/qcryptohw_30.h
@@ -0,0 +1,308 @@
+/* Copyright (c)2009- 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _DRIVERS_CRYPTO_MSM_QCRYPTOHW_30_H_
+#define _DRIVERS_CRYPTO_MSM_QCRYPTOHW_30_H_
+
+#define QCE_AUTH_REG_BYTE_COUNT 2
+#define CRYPTO_DATA_IN_REG			0x0
+#define CRYPTO_DATA_OUT_REG			0x10
+#define CRYPTO_STATUS_REG			0x20
+#define CRYPTO_CONFIG_REG			0x24
+#define CRYPTO_DEBUG_REG			0x28
+#define CRYPTO_REGISTER_LOCK_REG		0x2C
+#define CRYPTO_SEG_CFG_REG			0x30
+#define CRYPTO_ENCR_SEG_CFG_REG			0x34
+#define CRYPTO_AUTH_SEG_CFG_REG			0x38
+#define CRYPTO_SEG_SIZE_REG			0x3C
+#define CRYPTO_GOPROC_REG			0x40
+#define CRYPTO_ENGINES_AVAIL			0x44
+
+#define CRYPTO_DES_KEY0_REG			0x50
+#define CRYPTO_DES_KEY1_REG			0x54
+#define CRYPTO_DES_KEY2_REG			0x58
+#define CRYPTO_DES_KEY3_REG			0x5C
+#define CRYPTO_DES_KEY4_REG			0x60
+#define CRYPTO_DES_KEY5_REG			0x64
+
+#define CRYPTO_CNTR0_IV0_REG			0x70
+#define CRYPTO_CNTR1_IV1_REG			0x74
+#define CRYPTO_CNTR2_IV2_REG			0x78
+#define CRYPTO_CNTR3_IV3_REG			0x7C
+#define CRYPTO_CNTR_MASK_REG			0x80
+
+#define CRYPTO_AUTH_BYTECNT0_REG		0x90
+#define CRYPTO_AUTH_BYTECNT1_REG		0x94
+#define CRYPTO_AUTH_BYTECNT2_REG		0x98
+#define CRYPTO_AUTH_BYTECNT3_REG		0x9C
+
+#define CRYPTO_AUTH_IV0_REG			0x100
+#define CRYPTO_AUTH_IV1_REG			0x104
+#define CRYPTO_AUTH_IV2_REG			0x108
+#define CRYPTO_AUTH_IV3_REG			0x10C
+#define CRYPTO_AUTH_IV4_REG			0x110
+#define CRYPTO_AUTH_IV5_REG			0x114
+#define CRYPTO_AUTH_IV6_REG			0x118
+#define CRYPTO_AUTH_IV7_REG			0x11C
+#define CRYPTO_AUTH_IV8_REG			0x120
+#define CRYPTO_AUTH_IV9_REG			0x124
+#define CRYPTO_AUTH_IV10_REG			0x128
+#define CRYPTO_AUTH_IV11_REG			0x12C
+#define CRYPTO_AUTH_IV12_REG			0x130
+#define CRYPTO_AUTH_IV13_REG			0x134
+#define CRYPTO_AUTH_IV14_REG			0x138
+#define CRYPTO_AUTH_IV15_REG			0x13C
+
+#define CRYPTO_AES_RNDKEY0			0x200
+#define CRYPTO_AES_RNDKEY1			0x204
+#define CRYPTO_AES_RNDKEY2			0x208
+#define CRYPTO_AES_RNDKEY3			0x20C
+#define CRYPTO_AES_RNDKEY4			0x210
+#define CRYPTO_AES_RNDKEY5			0x214
+#define CRYPTO_AES_RNDKEY6			0x218
+#define CRYPTO_AES_RNDKEY7			0x21C
+#define CRYPTO_AES_RNDKEY8			0x220
+#define CRYPTO_AES_RNDKEY9			0x224
+#define CRYPTO_AES_RNDKEY10			0x228
+#define CRYPTO_AES_RNDKEY11			0x22c
+#define CRYPTO_AES_RNDKEY12			0x230
+#define CRYPTO_AES_RNDKEY13			0x234
+#define CRYPTO_AES_RNDKEY14			0x238
+#define CRYPTO_AES_RNDKEY15			0x23C
+#define CRYPTO_AES_RNDKEY16			0x240
+#define CRYPTO_AES_RNDKEY17			0x244
+#define CRYPTO_AES_RNDKEY18			0x248
+#define CRYPTO_AES_RNDKEY19			0x24C
+#define CRYPTO_AES_RNDKEY20			0x250
+#define CRYPTO_AES_RNDKEY21			0x254
+#define CRYPTO_AES_RNDKEY22			0x258
+#define CRYPTO_AES_RNDKEY23			0x25C
+#define CRYPTO_AES_RNDKEY24			0x260
+#define CRYPTO_AES_RNDKEY25			0x264
+#define CRYPTO_AES_RNDKEY26			0x268
+#define CRYPTO_AES_RNDKEY27			0x26C
+#define CRYPTO_AES_RNDKEY28			0x270
+#define CRYPTO_AES_RNDKEY29			0x274
+#define CRYPTO_AES_RNDKEY30			0x278
+#define CRYPTO_AES_RNDKEY31			0x27C
+#define CRYPTO_AES_RNDKEY32			0x280
+#define CRYPTO_AES_RNDKEY33			0x284
+#define CRYPTO_AES_RNDKEY34			0x288
+#define CRYPTO_AES_RNDKEY35			0x28c
+#define CRYPTO_AES_RNDKEY36			0x290
+#define CRYPTO_AES_RNDKEY37			0x294
+#define CRYPTO_AES_RNDKEY38			0x298
+#define CRYPTO_AES_RNDKEY39			0x29C
+#define CRYPTO_AES_RNDKEY40			0x2A0
+#define CRYPTO_AES_RNDKEY41			0x2A4
+#define CRYPTO_AES_RNDKEY42			0x2A8
+#define CRYPTO_AES_RNDKEY43			0x2AC
+#define CRYPTO_AES_RNDKEY44			0x2B0
+#define CRYPTO_AES_RNDKEY45			0x2B4
+#define CRYPTO_AES_RNDKEY46			0x2B8
+#define CRYPTO_AES_RNDKEY47			0x2BC
+#define CRYPTO_AES_RNDKEY48			0x2C0
+#define CRYPTO_AES_RNDKEY49			0x2C4
+#define CRYPTO_AES_RNDKEY50			0x2C8
+#define CRYPTO_AES_RNDKEY51			0x2CC
+#define CRYPTO_AES_RNDKEY52			0x2D0
+#define CRYPTO_AES_RNDKEY53			0x2D4
+#define CRYPTO_AES_RNDKEY54			0x2D8
+#define CRYPTO_AES_RNDKEY55			0x2DC
+#define CRYPTO_AES_RNDKEY56			0x2E0
+#define CRYPTO_AES_RNDKEY57			0x2E4
+#define CRYPTO_AES_RNDKEY58			0x2E8
+#define CRYPTO_AES_RNDKEY59			0x2EC
+
+#define CRYPTO_DATA_SHADOW0			0x8000
+#define CRYPTO_DATA_SHADOW8191			0x8FFC
+
+/* status reg  */
+#define CRYPTO_CORE_REV				28	/* bit 31-28 */
+#define CRYPTO_CORE_REV_MASK			(0xf << CRYPTO_CORE_REV)
+#define CRYPTO_DOUT_SIZE_AVAIL			22	/* bit 24-22 */
+#define CRYPTO_DOUT_SIZE_AVAIL_MASK		(0x7 << CRYPTO_DOUT_SIZE_AVAIL)
+#define CRYPTO_DIN_SIZE_AVAIL			19	/* bit 21-19 */
+#define CRYPTO_DIN_SIZE_AVAIL_MASK		(0x7 << CRYPTO_DIN_SIZE_AVAIL)
+#define CRYPTO_ACCESS_VIOL			18
+#define CRYPTO_SEG_CHNG_ERR			17
+#define CRYPTO_CFH_CHNG_ERR			16
+#define CRYPTO_DOUT_ERR				15
+#define CRYPTO_DIN_ERR				14
+#define CRYPTO_LOCKED				13
+#define CRYPTO_CRYPTO_STATE			10	/* bit 12-10 */
+#define CRYPTO_CRYPTO_STATE_MASK		(0x7 << CRYPTO_CRYPTO_STATE)
+#define CRYPTO_ENCR_BUSY			9
+#define CRYPTO_AUTH_BUSY			8
+#define CRYPTO_DOUT_INTR			7
+#define CRYPTO_DIN_INTR				6
+#define CRYPTO_AUTH_DONE_INTR			5
+#define CRYPTO_ERR_INTR				4
+#define CRYPTO_DOUT_RDY				3
+#define CRYPTO_DIN_RDY				2
+#define CRYPTO_AUTH_DONE			1
+#define CRYPTO_SW_ERR				0
+
+#define CRYPTO_CRYPTO_STATE_IDLE		0
+#define CRYPTO_CRYPTO_STATE_LOCKED		1
+#define CRYPTO_CRYPTO_STATE_GO			3
+#define CRYPTO_CRYPTO_STATE_PROCESSING		4
+#define CRYPTO_CRYPTO_STATE_FINAL_READ		5
+#define CRYPTO_CRYPTO_STATE_CTXT_CLEARING	6
+#define CRYPTO_CRYPTO_STATE_UNLOCKING		7
+
+/* config reg */
+#define CRYPTO_HIGH_SPD_HASH_EN_N		15
+#define CRYPTO_HIGH_SPD_OUT_EN_N		14
+#define CRYPTO_HIGH_SPD_IN_EN_N			13
+#define CRYPTO_DBG_EN				12
+#define CRYPTO_DBG_SEL				7	/* bit 11:7 */
+#define CRYPTO_DBG_SEL_MASK			(0x1F << CRYPTO_DBG_SEL)
+#define CRYPTO_MASK_DOUT_INTR			6
+#define CRYPTO_MASK_DIN_INTR			5
+#define CRYPTO_MASK_AUTH_DONE_INTR		4
+#define CRYPTO_MASK_ERR_INTR			3
+#define CRYPTO_AUTO_SHUTDOWN_EN			2
+#define CRYPTO_CLK_EN_N				1
+#define CRYPTO_SW_RST				0
+
+/* seg_cfg reg */
+#define CRYPTO_F8_KEYSTREAM_ENABLE		25
+#define CRYPTO_F9_DIRECTION			24
+#define CRYPTO_F8_DIRECTION			23
+#define CRYPTO_USE_HW_KEY			22
+
+#define CRYPTO_CNTR_ALG				20	/* bit 21-20 */
+#define CRYPTO_CNTR_ALG_MASK			(3 << efine CRYPTO_CNTR_ALG)
+
+#define CRYPTO_CLR_CNTXT			19
+#define CRYPTO_LAST				18
+#define CRYPTO_FIRST				17
+#define CRYPTO_ENCODE				16
+
+#define CRYPTO_AUTH_POS				14	/* bit 15-14 */
+#define CRYPTO_AUTH_POS_MASK			(3 << CRYPTO_AUTH_POS)
+
+#define CRYPTO_AUTH_SIZE			11	/* bit 13-11 */
+#define CRYPTO_AUTH_SIZE_MASK			(7 << CRYPTO_AUTH_SIZE)
+
+#define CRYPTO_AUTH_ALG				9	/* bit 10-9 */
+#define CRYPTO_AUTH_ALG_MASK			(3 << CRYPTO_AUTH_ALG)
+
+#define CRYPTO_ENCR_MODE			6	/* bit 8-6 */
+#define CRYPTO_ENCR_MODE_MASK			(7 << CRYPTO_ENCR_MODE)
+
+#define CRYPTO_ENCR_KEY_SZ			3	/* bit 5-3 */
+#define CRYPTO_ENCR_KEY_SZ_MASK			(7 << CRYPTO_ENCR_KEY_SZ)
+
+#define CRYPTO_ENCR_ALG				0	/* bit 2-0 */
+#define CRYPTO_ENCR_ALG_MASK			(7 << CRYPTO_ENCR_ALG)
+
+#define CRYPTO_CNTR_ALG_NIST			0
+#define CRYPTO_CNTR_ALG_UMB			1
+#define CRYPTO_CNTR_ALG_VAR2			2
+
+#define CRYPTO_AUTH_POS_BEFORE			0
+#define CRYPTO_AUTH_POS_AFTER			1
+
+#define CRYPTO_AUTH_SIZE_SHA1			0
+#define CRYPTO_AUTH_SIZE_SHA256			1
+#define CRYPTO_AUTH_SIZE_SHA384			2
+#define CRYPTO_AUTH_SIZE_SHA512			3
+#define CRYPTO_AUTH_SIZE_HMAC_SHA1		4
+
+#define CRYPTO_AUTH_SIZE_UIA1			0
+#define CRYPTO_AUTH_SIZE_UIA2			1
+
+#define CRYPTO_AUTH_ALG_NONE			0
+#define CRYPTO_AUTH_ALG_SHA			1
+#define CRYPTO_AUTH_ALG_F9			2
+#define CRYPTO_AUTH_ALG_RESERVED1		3
+
+#define CRYPTO_ENCR_MODE_ECB			0
+#define CRYPTO_ENCR_MODE_CBC			1
+/* only valid when AES */
+#define CRYPTO_ENCR_MODE_CTR			2
+
+
+#define CRYPTO_ENCR_KEY_SZ_DES			0
+#define CRYPTO_ENCR_KEY_SZ_3DES			1
+
+#define CRYPTO_ENCR_KEY_SZ_AES128		0
+#define CRYPTO_ENCR_KEY_SZ_AES192		1
+#define CRYPTO_ENCR_KEY_SZ_AES256		2
+
+#define CRYPTO_ENCR_KEY_SZ_UEA1			0
+#define CRYPTO_ENCR_KEY_SZ_UEA2			1
+
+#define CRYPTO_ENCR_ALG_NONE			0
+#define CRYPTO_ENCR_ALG_DES			1
+#define CRYPTO_ENCR_ALG_AES			2
+#define CRYPTO_ENCR_ALG_C2			3
+#define CRYPTO_ENCR_ALG_F8			4
+
+/* encr_seg_cfg reg */
+#define CRYPTO_ENCR_SEG_SIZE			16	/* bit 31-16  */
+#define CRYPTO_ENCR_SEG_SIZE_MASK		(0xffff << CRYPTO_ENCR_SEG_SIZE)
+
+#define CRYPTO_ENCR_START			0
+#define CRYPTO_ENCR_START_MASK			(0xffff << CRYPTO_ENCR_START)
+
+/* auth_seg_cfg reg */
+#define CRYPTO_AUTH_SEG_SIZE			16	/* bit 31-16  */
+#define CRYPTO_AUTH_SEG_SIZE_MASK		(0xffff << CRYPTO_AUTH_SEG_SIZE)
+
+#define CRYPTO_AUTH_START			0
+#define CRYPTO_AUTH_START_MASK			(0xffff << CRYPTO_AUTH_START)
+
+
+/* seg_size reg */
+#define CRYPTO_SEG_SIZE				0
+#define CRYPTO_SEG_SIZE_MASK			(0xffff << CRYPTO_SEG_SIZE)
+
+/* goproc reg */
+#define CRYPTO_GO				0
+
+/* engines_avail */
+#define CRYPTO_F9_SEL				8
+#define CRYPTO_F8_SEL				7
+#define CRYPTO_HMAC_SEL				6
+#define CRYPTO_SHA512_SEL			5
+#define CRYPTO_SHA_SEL				4
+#define CRYPTO_DES_SEL				3
+#define CRYPTO_C2_SEL				2
+
+#define CRYPTO_AES_SEL				0	/* bit 1-0 */
+#define CRYPTO_AES_SEL_MASK			(3 <<  CRYPTO_AES_SEL)
+#define CRYPTO_AES_SEL_NO			0
+#define CRYPTO_AES_SEL_SLOW			1
+#define CRYPTO_AES_SEL_FAST			2
+#define CRYPTO_AES_SEL_RESERVED			3
+
+/*  F8 definition of CRYPTO_CNTR1_IV1_REG  */
+#define CRYPTO_CNTR1_IV1_REG_F8_PKT_CNT		16	/* bit 31 - 16 */
+#define CRYPTO_CNTR1_IV1_REG_F8_PKT_CNT_MASK \
+		(0xffff << CRYPTO_CNTR1_IV1_REG_F8_PKT_CNT)
+
+#define CRYPTO_CNTR1_IV1_REG_F8_BEARER		0	/* bit 4 - 0 */
+#define CRYPTO_CNTR1_IV1_REG_F8_BEARER_MASK \
+		(0x1f << CRYPTO_CNTR1_IV1_REG_F8_BEARER)
+
+/* F9 definition of CRYPTO_AUTH_IV4_REG */
+#define CRYPTO_AUTH_IV4_REG_F9_VALID_BIS	0	/* bit 2 - 0 */
+#define CRYPTO_AUTH_IV4_REG_F9_VALID_BIS_MASK \
+		(0x7  << CRYPTO_AUTH_IV4_REG_F9_VALID_BIS)
+
+/* misc  */
+#define CRYPTO_AES_RNDKEYS			60
+
+#endif /* _DRIVERS_CRYPTO_MSM_QCRYPTOHW_30_H_ */
diff --git a/drivers/crypto/msm/qcryptohw_40.h b/drivers/crypto/msm/qcryptohw_40.h
new file mode 100644
index 0000000..367bdaa
--- /dev/null
+++ b/drivers/crypto/msm/qcryptohw_40.h
@@ -0,0 +1,316 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _DRIVERS_CRYPTO_MSM_QCRYPTOHW_40_H_
+#define _DRIVERS_CRYPTO_MSM_QCRYPTOHW_40_H_
+
+
+#define QCE_AUTH_REG_BYTE_COUNT 4
+#define CRYPTO_VERSION_REG			0x0
+#define CRYPTO_DATA_IN_REG			0x008
+#define CRYPTO_DATA_OUT_REG			0x010
+#define CRYPTO_STATUS_REG			0x100
+#define CRYPTO_ENGINES_AVAIL			0x104
+#define CRYPTO3_VERSION_REG			0x108
+#define CRYPTO_SEG_SIZE_REG			0x200
+#define CRYPTO_GOPROC_REG			0x204
+#define CRYPTO_ENCR_SEG_CFG_REG			0x300
+
+#define CRYPTO_ENCR_SEG_SIZE_REG		0x304
+#define CRYPTO_ENCR_SEG_START_REG		0x308
+
+#define CRYPTO_ENCR_KEY0_REG			0x310
+#define CRYPTO_ENCR_KEY1_REG			0x314
+#define CRYPTO_ENCR_KEY2_REG			0x318
+#define CRYPTO_ENCR_KEY3_REG			0x31C
+#define CRYPTO_ENCR_KEY4_REG			0x320
+#define CRYPTO_ENCR_KEY5_REG			0x324
+#define CRYPTO_ENCR_KEY6_REG			0x328
+#define CRYPTO_ENCR_KEY7_REG			0x32C
+
+#define CRYPTO_ENCR_XTS_KEY0_REG		0x330
+#define CRYPTO_ENCR_XTS_KEY1_REG		0x334
+#define CRYPTO_ENCR_XTS_KEY2_REG		0x338
+#define CRYPTO_ENCR_XTS_KEY3_REG		0x33C
+#define CRYPTO_ENCR_XTS_KEY4_REG		0x340
+#define CRYPTO_ENCR_XTS_KEY5_REG		0x344
+#define CRYPTO_ENCR_XTS_KEY6_REG		0x348
+#define CRYPTO_ENCR_XTS_KEY7_REG		0x34C
+
+#define CRYPTO_CNTR0_IV0_REG			0x350
+#define CRYPTO_CNTR1_IV1_REG			0x354
+#define CRYPTO_CNTR2_IV2_REG			0x358
+#define CRYPTO_CNTR3_IV3_REG			0x35C
+
+#define CRYPTO_CNTR_MASK_REG			0x360
+
+#define CRYPTO_ENCR_XTS_DU_SIZE_REG		0x364
+
+#define CRYPTO_AUTH_SEG_CFG_REG			0x400
+#define CRYPTO_AUTH_SEG_SIZE_REG		0x404
+#define CRYPTO_AUTH_SEG_START_REG		0x408
+
+#define CRYPTO_AUTH_KEY0_REG			0x410
+#define CRYPTO_AUTH_KEY1_REG			0x414
+#define CRYPTO_AUTH_KEY2_REG			0x418
+#define CRYPTO_AUTH_KEY3_REG			0x41C
+#define CRYPTO_AUTH_KEY4_REG			0x420
+#define CRYPTO_AUTH_KEY5_REG			0x424
+#define CRYPTO_AUTH_KEY6_REG			0x428
+#define CRYPTO_AUTH_KEY7_REG			0x42C
+#define CRYPTO_AUTH_KEY8_REG			0x430
+#define CRYPTO_AUTH_KEY9_REG			0x434
+#define CRYPTO_AUTH_KEY10_REG			0x438
+#define CRYPTO_AUTH_KEY11_REG			0x43C
+#define CRYPTO_AUTH_KEY12_REG			0x440
+#define CRYPTO_AUTH_KEY13_REG			0x444
+#define CRYPTO_AUTH_KEY14_REG			0x448
+#define CRYPTO_AUTH_KEY15_REG			0x44C
+
+#define CRYPTO_AUTH_IV0_REG			0x450
+#define CRYPTO_AUTH_IV1_REG			0x454
+#define CRYPTO_AUTH_IV2_REG			0x458
+#define CRYPTO_AUTH_IV3_REG			0x45C
+#define CRYPTO_AUTH_IV4_REG			0x460
+#define CRYPTO_AUTH_IV5_REG			0x464
+#define CRYPTO_AUTH_IV6_REG			0x468
+#define CRYPTO_AUTH_IV7_REG			0x46C
+#define CRYPTO_AUTH_IV8_REG			0x470
+#define CRYPTO_AUTH_IV9_REG			0x474
+#define CRYPTO_AUTH_IV10_REG			0x478
+#define CRYPTO_AUTH_IV11_REG			0x47C
+#define CRYPTO_AUTH_IV12_REG			0x480
+#define CRYPTO_AUTH_IV13_REG			0x484
+#define CRYPTO_AUTH_IV14_REG			0x488
+#define CRYPTO_AUTH_IV15_REG			0x48C
+
+#define CRYPTO_AUTH_INFO_NONCE0_REG		0x490
+#define CRYPTO_AUTH_INFO_NONCE1_REG		0x494
+#define CRYPTO_AUTH_INFO_NONCE2_REG		0x498
+#define CRYPTO_AUTH_INFO_NONCE3_REG		0x49C
+
+#define CRYPTO_AUTH_BYTECNT0_REG		0x4A0
+#define CRYPTO_AUTH_BYTECNT1_REG		0x4A4
+#define CRYPTO_AUTH_BYTECNT2_REG		0x4A8
+#define CRYPTO_AUTH_BYTECNT3_REG		0x4AC
+
+#define CRYPTO_AUTH_EXP_MAC0_REG		0x4B0
+#define CRYPTO_AUTH_EXP_MAC1_REG		0x4B4
+#define CRYPTO_AUTH_EXP_MAC2_REG		0x4B8
+#define CRYPTO_AUTH_EXP_MAC3_REG		0x4BC
+#define CRYPTO_AUTH_EXP_MAC4_REG		0x4C0
+#define CRYPTO_AUTH_EXP_MAC5_REG		0x4C4
+#define CRYPTO_AUTH_EXP_MAC6_REG		0x4C8
+#define CRYPTO_AUTH_EXP_MAC7_REG		0x4CC
+
+#define CRYPTO_CONFIG_REG			0x500
+#define CRYPTO_SACR_REG				0x504
+#define CRYPTO_DEBUG_REG			0x508
+
+#define CRYPTO_DATA_SHADOW0			0x8000
+#define CRYPTO_DATA_SHADOW8191			0x8FFC
+
+
+/* Register bits */
+
+#define CRYPTO_CORE_MAJOR_REV			4 /* bit 7-4 */
+#define CRYPTO_CORE_MAJOR_REV_MASK		(0xF << CRYPTO_CORE_MAJOR_REV)
+#define CRYPTO_CORE_MINOR_REV			0 /* bit 3-0 */
+#define CRYPTO_CORE_MINOR_REV_MASK		(0xF << CRYPTO_CORE_MINOR_REV)
+#define CRYPTO_CORE_REV_MASK			0xFF
+
+/* status reg  */
+#define CRYPTO_MAC_FAILED			25
+#define CRYPTO_DOUT_SIZE_AVAIL			22 /* bit 24-22 */
+#define CRYPTO_DOUT_SIZE_AVAIL_MASK		(0x7 << CRYPTO_DOUT_SIZE_AVAIL)
+#define CRYPTO_DIN_SIZE_AVAIL			19 /* bit 21-19 */
+#define CRYPTO_DIN_SIZE_AVAIL_MASK		(0x7 << CRYPTO_DIN_SIZE_AVAIL)
+#define CRYPTO_ACCESS_VIOL			18
+#define CRYPTO_SEG_CHNG_ERR			17
+#define CRYPTO_CFH_CHNG_ERR			16
+#define CRYPTO_DOUT_ERR				15
+#define CRYPTO_DIN_ERR				14
+#define CRYPTO_LOCKED				13
+#define CRYPTO_CRYPTO_STATE			10 /* bit 12-10 */
+#define CRYPTO_CRYPTO_STATE_MASK		(0x7 << CRYPTO_CRYPTO_STATE)
+#define CRYPTO_ENCR_BUSY			9
+#define CRYPTO_AUTH_BUSY			8
+#define CRYPTO_DOUT_INTR			7
+#define CRYPTO_DIN_INTR				6
+#define CRYPTO_OP_DONE_INTR			5
+#define CRYPTO_ERR_INTR				4
+#define CRYPTO_DOUT_RDY				3
+#define CRYPTO_DIN_RDY				2
+#define CRYPTO_OPERATION_DONE			1
+#define CRYPTO_SW_ERR				0
+
+/* config reg */
+#define CRYPTO_REQ_SIZE				30 /* bit 31-30 */
+#define CRYPTO_REQ_SIZE_MASK			(0x3 << CRYPTO_REQ_SIZE)
+#define CRYPTO_REQ_SIZE_ENUM_16_BYTES	0
+#define CRYPTO_REQ_SIZE_ENUM_32_BYTES	1
+#define CRYPTO_REQ_SIZE_ENUM_64_BYTES	2
+
+#define CRYPTO_MAX_QUEUED_REQ			27 /* bit 29-27 */
+#define CRYPTO_MAX_QUEUED_REQ_MASK		(0x7 << CRYPTO_MAX_QUEUED_REQ)
+#define CRYPTO_ENUM1_QUEUED_REQS		0
+#define CRYPTO_ENUM2_QUEUED_REQS		1
+#define CRYPTO_ENUM3_QUEUED_REQS		2
+#define CRYPTO_ENUM4_QUEUED_REQS		3
+
+#define CRYPTO_FIFO_THRESHOLD			24 /* bit 26-24 */
+#define CRYPTO_FIFO_THRESHOLD_MASK		(0x7 << CRYPTO_FIFO_THRESHOLD)
+#define CRYPTO_FIFO_ENUM_16_BYTES		0
+#define CRYPTO_FIFO_ENUM_32_BYTES		1
+#define CRYPTO_FIFO_ENUM_48_BYTES		2
+#define CRYPTO_FIFO_ENUM_64_BYTES		3
+
+#define CRYPTO_IRQ_ENABLES			20	/* bit 23-20 */
+#define CRYPTO_IRQ_ENABLES_MASK			(0xF << CRYPTO_IRQ_ENABLES)
+
+#define CRYPTO_ACR_EN				18
+#define CRYPTO_BAM_MODE				17
+#define CRYPTO_LITTLE_ENDIAN_MODE		16
+#define CRYPTO_HIGH_SPD_OUT_EN_N		14
+#define CRYPTO_HIGH_SPD_IN_EN_N			13
+#define CRYPTO_DBG_EN				12
+
+#define CRYPTO_DBG_SEL				7 /* bit 11:7 */
+#define CRYPTO_DBG_SEL_MASK			(0x1F << CRYPTO_DBG_SEL)
+
+#define CRYPTO_MASK_DOUT_INTR			6
+#define CRYPTO_MASK_DIN_INTR			5
+#define CRYPTO_MASK_OP_DONE_INTR		4
+#define CRYPTO_MASK_ERR_INTR			3
+#define CRYPTO_AUTO_SHUTDOWN_EN			2
+#define CRYPTO_CLK_EN_N				1
+
+/* auth_seg_cfg reg */
+#define CRYPTO_COMP_EXP_MAC			20
+#define CRYPTO_COMP_EXP_MAC_DISABLED		0
+#define CRYPTO_COMP_EXP_MAC_ENABLED		1
+
+#define CRYPTO_F9_DIRECTION			19
+#define CRYPTO_F9_DIRECTION_UPLINK		0
+#define CRYPTO_F9_DIRECTION_DOWNLINK		1
+
+#define CRYPTO_AUTH_NONCE_NUM_WORDS		16
+#define CRYPTO_AUTH_NONCE_NUM_WORDS_MASK \
+					(0x7 << CRYPTO_AUTH_NONCE_NUM_WORDS)
+
+#define CRYPTO_USE_HW_KEY_AUTH			15
+
+#define CRYPTO_LAST				14
+
+#define CRYPTO_AUTH_POS				12 /* bit 13 .. 12*/
+#define CRYPTO_AUTH_POS_MASK			(0x3 << CRYPTO_AUTH_POS)
+#define CRYPTO_AUTH_POS_BEFORE			0
+#define CRYPTO_AUTH_POS_AFTER			1
+
+#define CRYPTO_AUTH_SIZE			9 /* bits 11 .. 9*/
+#define CRYPTO_AUTH_SIZE_MASK			(0x7 << CRYPTO_AUTH_SIZE)
+#define CRYPTO_AUTH_SIZE_SHA1			0
+#define CRYPTO_AUTH_SIZE_SHA256			1
+#define CRYPTO_AUTH_SIZE_ENUM_4_BYTES		0
+#define CRYPTO_AUTH_SIZE_ENUM_6_BYTES		1
+#define CRYPTO_AUTH_SIZE_ENUM_8_BYTES		2
+#define CRYPTO_AUTH_SIZE_ENUM_10_BYTES		3
+#define CRYPTO_AUTH_SIZE_ENUM_12_BYTES		4
+#define CRYPTO_AUTH_SIZE_ENUM_14_BYTES		5
+#define CRYPTO_AUTH_SIZE_ENUM_16_BYTES		6
+
+#define CRYPTO_AUTH_MODE			6 /* bit 8 .. 6*/
+#define CRYPTO_AUTH_MODE_MASK			(0x7 << CRYPTO_AUTH_MODE)
+#define CRYPTO_AUTH_MODE_HASH			0
+#define CRYPTO_AUTH_MODE_HMAC			1
+#define CRYPTO_AUTH_MODE_CCM			0
+#define CRYPTO_AUTH_MODE_CMAC			1
+
+#define CRYPTO_AUTH_KEY_SIZE			3
+#define CRYPTO_AUTH_KEY_SIZE_MASK		(0x7 << CRYPTO_AUTH_KEY_SIZE)
+#define CRYPTO_AUTH_KEY_SZ_AES128		0
+#define CRYPTO_AUTH_KEY_SZ_AES256		2
+
+#define CRYPTO_AUTH_ALG				0 /* bit 2 .. 0*/
+#define CRYPTO_AUTH_ALG_MASK			7
+#define CRYPTO_AUTH_ALG_NONE			0
+#define CRYPTO_AUTH_ALG_SHA			1
+#define CRYPTO_AUTH_ALG_AES			2
+#define CRYPTO_AUTH_ALG_KASUMI			3
+#define CRYPTO_AUTH_ALG_SNOW3G			4
+
+/* encr_xts_du_size reg */
+#define CRYPTO_ENCR_XTS_DU_SIZE			0 /* bit 19-0  */
+#define CRYPTO_ENCR_XTS_DU_SIZE_MASK		0xfffff
+
+/* encr_seg_cfg reg */
+#define CRYPTO_F8_KEYSTREAM_ENABLE		15
+#define CRYPTO_F8_KEYSTREAM_DISABLED		0
+#define CRYPTO_F8_KEYSTREAM_ENABLED		1
+
+#define CRYPTO_F8_DIRECTION			14
+#define CRYPTO_F8_DIRECTION_UPLINK		0
+#define CRYPTO_F8_DIRECTION_DOWNLINK		1
+
+#define CRYPTO_USE_HW_KEY_ENCR			13
+#define CRYPTO_USE_HW_KEY_REG			0
+#define CRYPTO_USE_HW_KEY			1
+
+#define CRYPTO_CNTR_ALG				11 /* bit 12-11 */
+#define CRYPTO_CNTR_ALG_MASK			(3 << CRYPTO_CNTR_ALG)
+#define CRYPTO_CNTR_ALG_NIST			0
+
+#define CRYPTO_ENCODE				10
+
+#define CRYPTO_ENCR_MODE			6 /* bit 9-6 */
+#define CRYPTO_ENCR_MODE_MASK			(0xF << CRYPTO_ENCR_MODE)
+/* only valid when AES */
+#define CRYPTO_ENCR_MODE_ECB			0
+#define CRYPTO_ENCR_MODE_CBC			1
+#define CRYPTO_ENCR_MODE_CTR			2
+#define CRYPTO_ENCR_MODE_XTS			3
+#define CRYPTO_ENCR_MODE_CCM			4
+
+#define CRYPTO_ENCR_KEY_SZ			3 /* bit 5-3 */
+#define CRYPTO_ENCR_KEY_SZ_MASK			(7 << CRYPTO_ENCR_KEY_SZ)
+#define CRYPTO_ENCR_KEY_SZ_DES			0
+#define CRYPTO_ENCR_KEY_SZ_3DES			1
+#define CRYPTO_ENCR_KEY_SZ_AES128		0
+#define CRYPTO_ENCR_KEY_SZ_AES256		2
+#define CRYPTO_ENCR_KEY_SZ_UEA1			0
+#define CRYPTO_ENCR_KEY_SZ_UEA2			1
+
+#define CRYPTO_ENCR_ALG				0 /* bit 2-0 */
+#define CRYPTO_ENCR_ALG_MASK			(7 << CRYPTO_ENCR_ALG)
+#define CRYPTO_ENCR_ALG_NONE			0
+#define CRYPTO_ENCR_ALG_DES			1
+#define CRYPTO_ENCR_ALG_AES			2
+#define CRYPTO_ENCR_ALG_KASUMI			3
+#define CRYPTO_ENCR_ALG_SNOW_3G			5
+
+/* goproc reg */
+#define CRYPTO_GO				0
+#define CRYPTO_CLR_CNTXT			1
+
+/* engines_avail */
+#define CRYPTO_ENCR_AES_SEL			0
+#define CRYPTO_DES_SEL				3
+#define CRYPTO_ENCR_SNOW3G_SEL			4
+#define CRYPTO_ENCR_KASUMI_SEL			5
+#define CRYPTO_SHA_SEL				6
+#define CRYPTO_SHA512_SEL			7
+#define CRYPTO_AUTH_AES_SEL			8
+#define CRYPTO_AUTH_SNOW3G_SEL			9
+#define CRYPTO_AUTH_KASUMI_SEL			10
+#define CRYPTO_BAM_SEL				11
+
+#endif /* _DRIVERS_CRYPTO_MSM_QCRYPTOHW_40_H_ */
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index e03653d..ef077a5 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -124,6 +124,14 @@
 	  Qualcomm MSM chips.  Most of the pins on the MSM can be
 	  selected for GPIO, and are controlled by this driver.
 
+config GPIO_FSM9XXX
+	tristate "Qualcomm FSM GPIO"
+	depends on GPIOLIB && ARCH_MSM
+	help
+	  Say yes here to support the GPIO interface on Qualcomm FSM chips.
+	  Most of the pins on the MSM can be selected for GPIO, and are
+	  controlled by this driver.
+
 config GPIO_MXC
 	def_bool y
 	depends on ARCH_MXC
@@ -514,4 +522,44 @@
 	help
 	  Select this option to enable GPIO driver for the TPS65910
 	  chip family.
+
+config GPIO_PM8XXX
+	tristate "Qualcomm PM8xxx GPIO support"
+	depends on MFD_PM8XXX
+	default y if MFD_PM8XXX
+	help
+	  This option enables support for on-chip GPIO found on Qualcomm PM8xxx
+	  PMICs.
+
+config GPIO_PM8XXX_MPP
+	tristate "Support for Qualcomm PM8xxx MPP features"
+	depends on MFD_PM8XXX
+	default y if MFD_PM8XXX
+	help
+	  This is the multi-purpose pin (MPP) driver for Qualcomm PM 8xxx PMIC
+	  chips.
+
+config GPIO_PM8XXX_RPC
+	tristate "Qualcomm PM8xxx RPC based GPIO support"
+	depends on MSM_SMD
+	help
+	  This option enables support for on-chip GPIO found on Qualcomm PM8xxx
+	  PMICs through RPC.
+
+config GPIO_QPNP
+	depends on ARCH_MSMCOPPER
+	depends on OF_SPMI
+	depends on MSM_QPNP_INT
+	tristate "Qualcomm QPNP GPIO support"
+	help
+	  Say 'y' here to include support for the Qualcomm QPNP gpio
+	  support. QPNP is a SPMI based PMIC implementation.
+
+config GPIO_QPNP_DEBUG
+	depends on GPIO_QPNP
+	depends on DEBUG_FS
+	bool "Qualcomm QPNP GPIO debug support"
+	help
+	  Say 'y' here to include debug support for the Qualcomm
+	  QPNP gpio support
 endif
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 007f54b..babd44d 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -34,6 +34,7 @@
 obj-$(CONFIG_GPIO_MPC8XXX)	+= gpio-mpc8xxx.o
 obj-$(CONFIG_GPIO_MSM_V1)	+= gpio-msm-v1.o
 obj-$(CONFIG_GPIO_MSM_V2)	+= gpio-msm-v2.o
+obj-$(CONFIG_GPIO_FSM9XXX)	+= gpio-fsm9xxx.o
 obj-$(CONFIG_GPIO_MXC)		+= gpio-mxc.o
 obj-$(CONFIG_GPIO_MXS)		+= gpio-mxs.o
 obj-$(CONFIG_PLAT_NOMADIK)	+= gpio-nomadik.o
@@ -42,7 +43,11 @@
 obj-$(CONFIG_GPIO_PCF857X)	+= gpio-pcf857x.o
 obj-$(CONFIG_GPIO_PCH)		+= gpio-pch.o
 obj-$(CONFIG_GPIO_PL061)	+= gpio-pl061.o
+obj-$(CONFIG_GPIO_PM8XXX)	+= pm8xxx-gpio.o
+obj-$(CONFIG_GPIO_PM8XXX_MPP) 	+= pm8xxx-mpp.o
+obj-$(CONFIG_GPIO_PM8XXX_RPC)	+= gpio-pm8xxx-rpc.o
 obj-$(CONFIG_GPIO_PXA)		+= gpio-pxa.o
+obj-$(CONFIG_GPIO_QPNP)		+= qpnp-gpio.o
 obj-$(CONFIG_GPIO_RDC321X)	+= gpio-rdc321x.o
 obj-$(CONFIG_PLAT_SAMSUNG)	+= gpio-samsung.o
 obj-$(CONFIG_ARCH_SA1100)	+= gpio-sa1100.o
diff --git a/drivers/gpio/gpio-fsm9xxx.c b/drivers/gpio/gpio-fsm9xxx.c
new file mode 100644
index 0000000..ad7e1fd
--- /dev/null
+++ b/drivers/gpio/gpio-fsm9xxx.c
@@ -0,0 +1,335 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/bitops.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <mach/gpiomux.h>
+#include <mach/msm_iomap.h>
+
+/* see 80-VA736-2 Rev C pp 695-751
+**
+** These are actually the *shadow* gpio registers, since the
+** real ones (which allow full access) are only available to the
+** ARM9 side of the world.
+**
+** Since the _BASE need to be page-aligned when we're mapping them
+** to virtual addresses, adjust for the additional offset in these
+** macros.
+*/
+
+#if defined(CONFIG_ARCH_FSM9XXX)
+#define MSM_GPIO1_REG(off) (MSM_TLMM_BASE + (off))
+#endif
+
+#if defined(CONFIG_ARCH_FSM9XXX)
+
+/* output value */
+#define MSM_GPIO_OUT_G(group)  MSM_GPIO1_REG(0x00 + (group) * 4)
+#define MSM_GPIO_OUT_N(gpio)   MSM_GPIO_OUT_G((gpio) / 32)
+#define MSM_GPIO_OUT_0         MSM_GPIO_OUT_G(0)   /* gpio  31-0   */
+#define MSM_GPIO_OUT_1         MSM_GPIO_OUT_G(1)   /* gpio  63-32  */
+#define MSM_GPIO_OUT_2         MSM_GPIO_OUT_G(2)   /* gpio  95-64  */
+#define MSM_GPIO_OUT_3         MSM_GPIO_OUT_G(3)   /* gpio 127-96  */
+#define MSM_GPIO_OUT_4         MSM_GPIO_OUT_G(4)   /* gpio 159-128 */
+#define MSM_GPIO_OUT_5         MSM_GPIO_OUT_G(5)   /* gpio 167-160 */
+
+/* same pin map as above, output enable */
+#define MSM_GPIO_OE_G(group)   MSM_GPIO1_REG(0x20 + (group) * 4)
+#define MSM_GPIO_OE_N(gpio)    MSM_GPIO_OE_G((gpio) / 32)
+#define MSM_GPIO_OE_0          MSM_GPIO_OE_G(0)
+#define MSM_GPIO_OE_1          MSM_GPIO_OE_G(1)
+#define MSM_GPIO_OE_2          MSM_GPIO_OE_G(2)
+#define MSM_GPIO_OE_3          MSM_GPIO_OE_G(3)
+#define MSM_GPIO_OE_4          MSM_GPIO_OE_G(4)
+#define MSM_GPIO_OE_5          MSM_GPIO_OE_G(5)
+
+/* same pin map as above, input read */
+#define MSM_GPIO_IN_G(group)   MSM_GPIO1_REG(0x48 + (group) * 4)
+#define MSM_GPIO_IN_N(gpio)    MSM_GPIO_IN_G((gpio) / 32)
+#define MSM_GPIO_IN_0          MSM_GPIO_IN_G(0)
+#define MSM_GPIO_IN_1          MSM_GPIO_IN_G(1)
+#define MSM_GPIO_IN_2          MSM_GPIO_IN_G(2)
+#define MSM_GPIO_IN_3          MSM_GPIO_IN_G(3)
+#define MSM_GPIO_IN_4          MSM_GPIO_IN_G(4)
+#define MSM_GPIO_IN_5          MSM_GPIO_IN_G(5)
+
+/* configuration */
+#define MSM_GPIO_PAGE          MSM_GPIO1_REG(0x40)
+#define MSM_GPIO_CONFIG        MSM_GPIO1_REG(0x44)
+
+#endif /* CONFIG_ARCH_FSM9XXX */
+
+#define MSM_GPIO_BANK(bank, first, last)				\
+	{								\
+		.regs = {						\
+			.out =         MSM_GPIO_OUT_##bank,		\
+			.in =          MSM_GPIO_IN_##bank,		\
+			.oe =          MSM_GPIO_OE_##bank,		\
+		},							\
+		.chip = {						\
+			.base = (first),				\
+			.ngpio = (last) - (first) + 1,			\
+			.get = msm_gpio_get,				\
+			.set = msm_gpio_set,				\
+			.direction_input = msm_gpio_direction_input,	\
+			.direction_output = msm_gpio_direction_output,	\
+			.request = msm_gpio_request,			\
+			.free = msm_gpio_free,				\
+		}							\
+	}
+
+struct msm_gpio_regs {
+	void __iomem *out;
+	void __iomem *in;
+	void __iomem *oe;
+};
+
+struct msm_gpio_chip {
+	spinlock_t		lock;
+	struct gpio_chip	chip;
+	struct msm_gpio_regs	regs;
+};
+
+static int msm_gpio_write(struct msm_gpio_chip *msm_chip,
+			  unsigned offset, unsigned on)
+{
+	unsigned mask = BIT(offset);
+	unsigned val;
+
+	val = __raw_readl(msm_chip->regs.out);
+	if (on)
+		__raw_writel(val | mask, msm_chip->regs.out);
+	else
+		__raw_writel(val & ~mask, msm_chip->regs.out);
+	return 0;
+}
+
+static int msm_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+	struct msm_gpio_chip *msm_chip;
+	unsigned long irq_flags;
+
+	msm_chip = container_of(chip, struct msm_gpio_chip, chip);
+	spin_lock_irqsave(&msm_chip->lock, irq_flags);
+	__raw_writel(__raw_readl(msm_chip->regs.oe) & ~BIT(offset),
+		msm_chip->regs.oe);
+	spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
+	return 0;
+}
+
+static int
+msm_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value)
+{
+	struct msm_gpio_chip *msm_chip;
+	unsigned long irq_flags;
+
+	msm_chip = container_of(chip, struct msm_gpio_chip, chip);
+	spin_lock_irqsave(&msm_chip->lock, irq_flags);
+	msm_gpio_write(msm_chip, offset, value);
+	__raw_writel(__raw_readl(msm_chip->regs.oe) | BIT(offset),
+		msm_chip->regs.oe);
+	spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
+	return 0;
+}
+
+static int msm_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+	struct msm_gpio_chip *msm_chip;
+
+	msm_chip = container_of(chip, struct msm_gpio_chip, chip);
+	return (__raw_readl(msm_chip->regs.in) & (1U << offset)) ? 1 : 0;
+}
+
+static void msm_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+	struct msm_gpio_chip *msm_chip;
+	unsigned long irq_flags;
+
+	msm_chip = container_of(chip, struct msm_gpio_chip, chip);
+	spin_lock_irqsave(&msm_chip->lock, irq_flags);
+	msm_gpio_write(msm_chip, offset, value);
+	spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
+}
+
+#ifdef CONFIG_MSM_GPIOMUX
+static int msm_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+	return msm_gpiomux_get(chip->base + offset);
+}
+
+static void msm_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+	msm_gpiomux_put(chip->base + offset);
+}
+#else
+#define msm_gpio_request NULL
+#define msm_gpio_free NULL
+#endif
+
+struct msm_gpio_chip msm_gpio_chips[] = {
+	MSM_GPIO_BANK(0,   0,  31),
+	MSM_GPIO_BANK(1,  32,  63),
+	MSM_GPIO_BANK(2,  64,  95),
+	MSM_GPIO_BANK(3,  96, 127),
+	MSM_GPIO_BANK(4, 128, 159),
+	MSM_GPIO_BANK(5, 160, 167),
+};
+
+void msm_gpio_enter_sleep(int from_idle)
+{
+	return;
+}
+
+void msm_gpio_exit_sleep(void)
+{
+	return;
+}
+
+static int __init msm_init_gpio(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) {
+		spin_lock_init(&msm_gpio_chips[i].lock);
+		gpiochip_add(&msm_gpio_chips[i].chip);
+	}
+
+	return 0;
+}
+
+postcore_initcall(msm_init_gpio);
+
+int gpio_tlmm_config(unsigned config, unsigned disable)
+{
+	uint32_t flags;
+	unsigned gpio = GPIO_PIN(config);
+
+	if (gpio > NR_MSM_GPIOS)
+		return -EINVAL;
+	flags = ((GPIO_DRVSTR(config) << 6) & (0x7 << 6)) |
+		((GPIO_FUNC(config) << 2) & (0xf << 2)) |
+		((GPIO_PULL(config) & 0x3));
+	dsb();
+	__raw_writel(gpio, MSM_GPIO_PAGE);
+	dsb();
+	__raw_writel(flags, MSM_GPIO_CONFIG);
+
+	return 0;
+}
+EXPORT_SYMBOL(gpio_tlmm_config);
+
+int msm_gpios_request_enable(const struct msm_gpio *table, int size)
+{
+	int rc = msm_gpios_request(table, size);
+	if (rc)
+		return rc;
+	rc = msm_gpios_enable(table, size);
+	if (rc)
+		msm_gpios_free(table, size);
+	return rc;
+}
+EXPORT_SYMBOL(msm_gpios_request_enable);
+
+void msm_gpios_disable_free(const struct msm_gpio *table, int size)
+{
+	msm_gpios_disable(table, size);
+	msm_gpios_free(table, size);
+}
+EXPORT_SYMBOL(msm_gpios_disable_free);
+
+int msm_gpios_request(const struct msm_gpio *table, int size)
+{
+	int rc;
+	int i;
+	const struct msm_gpio *g;
+	for (i = 0; i < size; i++) {
+		g = table + i;
+		rc = gpio_request(GPIO_PIN(g->gpio_cfg), g->label);
+		if (rc) {
+			pr_err("gpio_request(%d) <%s> failed: %d\n",
+			       GPIO_PIN(g->gpio_cfg), g->label ?: "?", rc);
+			goto err;
+		}
+	}
+	return 0;
+err:
+	msm_gpios_free(table, i);
+	return rc;
+}
+EXPORT_SYMBOL(msm_gpios_request);
+
+void msm_gpios_free(const struct msm_gpio *table, int size)
+{
+	int i;
+	const struct msm_gpio *g;
+	for (i = size-1; i >= 0; i--) {
+		g = table + i;
+		gpio_free(GPIO_PIN(g->gpio_cfg));
+	}
+}
+EXPORT_SYMBOL(msm_gpios_free);
+
+int msm_gpios_enable(const struct msm_gpio *table, int size)
+{
+	int rc;
+	int i;
+	const struct msm_gpio *g;
+	for (i = 0; i < size; i++) {
+		g = table + i;
+		rc = gpio_tlmm_config(g->gpio_cfg, GPIO_CFG_ENABLE);
+		if (rc) {
+			pr_err("gpio_tlmm_config(0x%08x, GPIO_CFG_ENABLE)"
+			       " <%s> failed: %d\n",
+			       g->gpio_cfg, g->label ?: "?", rc);
+			pr_err("pin %d func %d dir %d pull %d drvstr %d\n",
+			       GPIO_PIN(g->gpio_cfg), GPIO_FUNC(g->gpio_cfg),
+			       GPIO_DIR(g->gpio_cfg), GPIO_PULL(g->gpio_cfg),
+			       GPIO_DRVSTR(g->gpio_cfg));
+			goto err;
+		}
+	}
+	return 0;
+err:
+	msm_gpios_disable(table, i);
+	return rc;
+}
+EXPORT_SYMBOL(msm_gpios_enable);
+
+int msm_gpios_disable(const struct msm_gpio *table, int size)
+{
+	int rc = 0;
+	int i;
+	const struct msm_gpio *g;
+	for (i = size-1; i >= 0; i--) {
+		int tmp;
+		g = table + i;
+		tmp = gpio_tlmm_config(g->gpio_cfg, GPIO_CFG_DISABLE);
+		if (tmp) {
+			pr_err("gpio_tlmm_config(0x%08x, GPIO_CFG_DISABLE)"
+			       " <%s> failed: %d\n",
+			       g->gpio_cfg, g->label ?: "?", rc);
+			pr_err("pin %d func %d dir %d pull %d drvstr %d\n",
+			       GPIO_PIN(g->gpio_cfg), GPIO_FUNC(g->gpio_cfg),
+			       GPIO_DIR(g->gpio_cfg), GPIO_PULL(g->gpio_cfg),
+			       GPIO_DRVSTR(g->gpio_cfg));
+			if (!rc)
+				rc = tmp;
+		}
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(msm_gpios_disable);
+
diff --git a/drivers/gpio/gpio-msm-v1.c b/drivers/gpio/gpio-msm-v1.c
index 52a4d42..8718c9a 100644
--- a/drivers/gpio/gpio-msm-v1.c
+++ b/drivers/gpio/gpio-msm-v1.c
@@ -1,6 +1,7 @@
-/*
+/* linux/arch/arm/mach-msm/gpio.c
+ *
  * Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -19,9 +20,13 @@
 #include <linux/io.h>
 #include <linux/irq.h>
 #include <linux/module.h>
-#include <mach/cpu.h>
-#include <mach/msm_gpiomux.h>
+#include <linux/platform_device.h>
+#include <asm/mach/irq.h>
+#include <mach/gpiomux.h>
 #include <mach/msm_iomap.h>
+#include <mach/msm_smsm.h>
+#include <mach/proc_comm.h>
+
 
 /* see 80-VA736-2 Rev C pp 695-751
 **
@@ -34,257 +39,187 @@
 ** macros.
 */
 
+#if defined(CONFIG_ARCH_MSM7X30)
 #define MSM_GPIO1_REG(off) (MSM_GPIO1_BASE + (off))
 #define MSM_GPIO2_REG(off) (MSM_GPIO2_BASE + 0x400 + (off))
-#define MSM_GPIO1_SHADOW_REG(off) (MSM_GPIO1_BASE + 0x800 + (off))
-#define MSM_GPIO2_SHADOW_REG(off) (MSM_GPIO2_BASE + 0xC00 + (off))
+#else
+#define MSM_GPIO1_REG(off) (MSM_GPIO1_BASE + 0x800 + (off))
+#define MSM_GPIO2_REG(off) (MSM_GPIO2_BASE + 0xC00 + (off))
+#endif
 
-/*
- * MSM7X00 registers
- */
+#if defined(CONFIG_ARCH_MSM7X00A) || defined(CONFIG_ARCH_MSM7X25) ||\
+    defined(CONFIG_ARCH_MSM7X27)
+
 /* output value */
-#define MSM7X00_GPIO_OUT_0	MSM_GPIO1_SHADOW_REG(0x00)  /* gpio  15-0  */
-#define MSM7X00_GPIO_OUT_1	MSM_GPIO2_SHADOW_REG(0x00)  /* gpio  42-16 */
-#define MSM7X00_GPIO_OUT_2	MSM_GPIO1_SHADOW_REG(0x04)  /* gpio  67-43 */
-#define MSM7X00_GPIO_OUT_3	MSM_GPIO1_SHADOW_REG(0x08)  /* gpio  94-68 */
-#define MSM7X00_GPIO_OUT_4	MSM_GPIO1_SHADOW_REG(0x0C)  /* gpio 106-95 */
-#define MSM7X00_GPIO_OUT_5	MSM_GPIO1_SHADOW_REG(0x50)  /* gpio 107-121 */
+#define MSM_GPIO_OUT_0         MSM_GPIO1_REG(0x00)  /* gpio  15-0  */
+#define MSM_GPIO_OUT_1         MSM_GPIO2_REG(0x00)  /* gpio  42-16 */
+#define MSM_GPIO_OUT_2         MSM_GPIO1_REG(0x04)  /* gpio  67-43 */
+#define MSM_GPIO_OUT_3         MSM_GPIO1_REG(0x08)  /* gpio  94-68 */
+#define MSM_GPIO_OUT_4         MSM_GPIO1_REG(0x0C)  /* gpio 106-95 */
+#define MSM_GPIO_OUT_5         MSM_GPIO1_REG(0x50)  /* gpio 107-121 */
 
 /* same pin map as above, output enable */
-#define MSM7X00_GPIO_OE_0	MSM_GPIO1_SHADOW_REG(0x10)
-#define MSM7X00_GPIO_OE_1	MSM_GPIO2_SHADOW_REG(0x08)
-#define MSM7X00_GPIO_OE_2	MSM_GPIO1_SHADOW_REG(0x14)
-#define MSM7X00_GPIO_OE_3	MSM_GPIO1_SHADOW_REG(0x18)
-#define MSM7X00_GPIO_OE_4	MSM_GPIO1_SHADOW_REG(0x1C)
-#define MSM7X00_GPIO_OE_5	MSM_GPIO1_SHADOW_REG(0x54)
+#define MSM_GPIO_OE_0          MSM_GPIO1_REG(0x10)
+#define MSM_GPIO_OE_1          MSM_GPIO2_REG(0x08)
+#define MSM_GPIO_OE_2          MSM_GPIO1_REG(0x14)
+#define MSM_GPIO_OE_3          MSM_GPIO1_REG(0x18)
+#define MSM_GPIO_OE_4          MSM_GPIO1_REG(0x1C)
+#define MSM_GPIO_OE_5          MSM_GPIO1_REG(0x54)
 
 /* same pin map as above, input read */
-#define MSM7X00_GPIO_IN_0	MSM_GPIO1_SHADOW_REG(0x34)
-#define MSM7X00_GPIO_IN_1	MSM_GPIO2_SHADOW_REG(0x20)
-#define MSM7X00_GPIO_IN_2	MSM_GPIO1_SHADOW_REG(0x38)
-#define MSM7X00_GPIO_IN_3	MSM_GPIO1_SHADOW_REG(0x3C)
-#define MSM7X00_GPIO_IN_4	MSM_GPIO1_SHADOW_REG(0x40)
-#define MSM7X00_GPIO_IN_5	MSM_GPIO1_SHADOW_REG(0x44)
+#define MSM_GPIO_IN_0          MSM_GPIO1_REG(0x34)
+#define MSM_GPIO_IN_1          MSM_GPIO2_REG(0x20)
+#define MSM_GPIO_IN_2          MSM_GPIO1_REG(0x38)
+#define MSM_GPIO_IN_3          MSM_GPIO1_REG(0x3C)
+#define MSM_GPIO_IN_4          MSM_GPIO1_REG(0x40)
+#define MSM_GPIO_IN_5          MSM_GPIO1_REG(0x44)
 
 /* same pin map as above, 1=edge 0=level interrup */
-#define MSM7X00_GPIO_INT_EDGE_0	MSM_GPIO1_SHADOW_REG(0x60)
-#define MSM7X00_GPIO_INT_EDGE_1	MSM_GPIO2_SHADOW_REG(0x50)
-#define MSM7X00_GPIO_INT_EDGE_2	MSM_GPIO1_SHADOW_REG(0x64)
-#define MSM7X00_GPIO_INT_EDGE_3	MSM_GPIO1_SHADOW_REG(0x68)
-#define MSM7X00_GPIO_INT_EDGE_4	MSM_GPIO1_SHADOW_REG(0x6C)
-#define MSM7X00_GPIO_INT_EDGE_5	MSM_GPIO1_SHADOW_REG(0xC0)
+#define MSM_GPIO_INT_EDGE_0    MSM_GPIO1_REG(0x60)
+#define MSM_GPIO_INT_EDGE_1    MSM_GPIO2_REG(0x50)
+#define MSM_GPIO_INT_EDGE_2    MSM_GPIO1_REG(0x64)
+#define MSM_GPIO_INT_EDGE_3    MSM_GPIO1_REG(0x68)
+#define MSM_GPIO_INT_EDGE_4    MSM_GPIO1_REG(0x6C)
+#define MSM_GPIO_INT_EDGE_5    MSM_GPIO1_REG(0xC0)
 
 /* same pin map as above, 1=positive 0=negative */
-#define MSM7X00_GPIO_INT_POS_0	MSM_GPIO1_SHADOW_REG(0x70)
-#define MSM7X00_GPIO_INT_POS_1	MSM_GPIO2_SHADOW_REG(0x58)
-#define MSM7X00_GPIO_INT_POS_2	MSM_GPIO1_SHADOW_REG(0x74)
-#define MSM7X00_GPIO_INT_POS_3	MSM_GPIO1_SHADOW_REG(0x78)
-#define MSM7X00_GPIO_INT_POS_4	MSM_GPIO1_SHADOW_REG(0x7C)
-#define MSM7X00_GPIO_INT_POS_5	MSM_GPIO1_SHADOW_REG(0xBC)
+#define MSM_GPIO_INT_POS_0     MSM_GPIO1_REG(0x70)
+#define MSM_GPIO_INT_POS_1     MSM_GPIO2_REG(0x58)
+#define MSM_GPIO_INT_POS_2     MSM_GPIO1_REG(0x74)
+#define MSM_GPIO_INT_POS_3     MSM_GPIO1_REG(0x78)
+#define MSM_GPIO_INT_POS_4     MSM_GPIO1_REG(0x7C)
+#define MSM_GPIO_INT_POS_5     MSM_GPIO1_REG(0xBC)
 
 /* same pin map as above, interrupt enable */
-#define MSM7X00_GPIO_INT_EN_0	MSM_GPIO1_SHADOW_REG(0x80)
-#define MSM7X00_GPIO_INT_EN_1	MSM_GPIO2_SHADOW_REG(0x60)
-#define MSM7X00_GPIO_INT_EN_2	MSM_GPIO1_SHADOW_REG(0x84)
-#define MSM7X00_GPIO_INT_EN_3	MSM_GPIO1_SHADOW_REG(0x88)
-#define MSM7X00_GPIO_INT_EN_4	MSM_GPIO1_SHADOW_REG(0x8C)
-#define MSM7X00_GPIO_INT_EN_5	MSM_GPIO1_SHADOW_REG(0xB8)
+#define MSM_GPIO_INT_EN_0      MSM_GPIO1_REG(0x80)
+#define MSM_GPIO_INT_EN_1      MSM_GPIO2_REG(0x60)
+#define MSM_GPIO_INT_EN_2      MSM_GPIO1_REG(0x84)
+#define MSM_GPIO_INT_EN_3      MSM_GPIO1_REG(0x88)
+#define MSM_GPIO_INT_EN_4      MSM_GPIO1_REG(0x8C)
+#define MSM_GPIO_INT_EN_5      MSM_GPIO1_REG(0xB8)
 
 /* same pin map as above, write 1 to clear interrupt */
-#define MSM7X00_GPIO_INT_CLEAR_0	MSM_GPIO1_SHADOW_REG(0x90)
-#define MSM7X00_GPIO_INT_CLEAR_1	MSM_GPIO2_SHADOW_REG(0x68)
-#define MSM7X00_GPIO_INT_CLEAR_2	MSM_GPIO1_SHADOW_REG(0x94)
-#define MSM7X00_GPIO_INT_CLEAR_3	MSM_GPIO1_SHADOW_REG(0x98)
-#define MSM7X00_GPIO_INT_CLEAR_4	MSM_GPIO1_SHADOW_REG(0x9C)
-#define MSM7X00_GPIO_INT_CLEAR_5	MSM_GPIO1_SHADOW_REG(0xB4)
+#define MSM_GPIO_INT_CLEAR_0   MSM_GPIO1_REG(0x90)
+#define MSM_GPIO_INT_CLEAR_1   MSM_GPIO2_REG(0x68)
+#define MSM_GPIO_INT_CLEAR_2   MSM_GPIO1_REG(0x94)
+#define MSM_GPIO_INT_CLEAR_3   MSM_GPIO1_REG(0x98)
+#define MSM_GPIO_INT_CLEAR_4   MSM_GPIO1_REG(0x9C)
+#define MSM_GPIO_INT_CLEAR_5   MSM_GPIO1_REG(0xB4)
 
 /* same pin map as above, 1=interrupt pending */
-#define MSM7X00_GPIO_INT_STATUS_0	MSM_GPIO1_SHADOW_REG(0xA0)
-#define MSM7X00_GPIO_INT_STATUS_1	MSM_GPIO2_SHADOW_REG(0x70)
-#define MSM7X00_GPIO_INT_STATUS_2	MSM_GPIO1_SHADOW_REG(0xA4)
-#define MSM7X00_GPIO_INT_STATUS_3	MSM_GPIO1_SHADOW_REG(0xA8)
-#define MSM7X00_GPIO_INT_STATUS_4	MSM_GPIO1_SHADOW_REG(0xAC)
-#define MSM7X00_GPIO_INT_STATUS_5	MSM_GPIO1_SHADOW_REG(0xB0)
+#define MSM_GPIO_INT_STATUS_0  MSM_GPIO1_REG(0xA0)
+#define MSM_GPIO_INT_STATUS_1  MSM_GPIO2_REG(0x70)
+#define MSM_GPIO_INT_STATUS_2  MSM_GPIO1_REG(0xA4)
+#define MSM_GPIO_INT_STATUS_3  MSM_GPIO1_REG(0xA8)
+#define MSM_GPIO_INT_STATUS_4  MSM_GPIO1_REG(0xAC)
+#define MSM_GPIO_INT_STATUS_5  MSM_GPIO1_REG(0xB0)
 
-/*
- * QSD8X50 registers
- */
+#endif
+
+#if defined(CONFIG_ARCH_MSM7X30)
+
 /* output value */
-#define QSD8X50_GPIO_OUT_0	MSM_GPIO1_SHADOW_REG(0x00)  /* gpio  15-0   */
-#define QSD8X50_GPIO_OUT_1	MSM_GPIO2_SHADOW_REG(0x00)  /* gpio  42-16  */
-#define QSD8X50_GPIO_OUT_2	MSM_GPIO1_SHADOW_REG(0x04)  /* gpio  67-43  */
-#define QSD8X50_GPIO_OUT_3	MSM_GPIO1_SHADOW_REG(0x08)  /* gpio  94-68  */
-#define QSD8X50_GPIO_OUT_4	MSM_GPIO1_SHADOW_REG(0x0C)  /* gpio 103-95  */
-#define QSD8X50_GPIO_OUT_5	MSM_GPIO1_SHADOW_REG(0x10)  /* gpio 121-104 */
-#define QSD8X50_GPIO_OUT_6	MSM_GPIO1_SHADOW_REG(0x14)  /* gpio 152-122 */
-#define QSD8X50_GPIO_OUT_7	MSM_GPIO1_SHADOW_REG(0x18)  /* gpio 164-153 */
+#define MSM_GPIO_OUT_0         MSM_GPIO1_REG(0x00)   /* gpio  15-0   */
+#define MSM_GPIO_OUT_1         MSM_GPIO2_REG(0x00)   /* gpio  43-16  */
+#define MSM_GPIO_OUT_2         MSM_GPIO1_REG(0x04)   /* gpio  67-44  */
+#define MSM_GPIO_OUT_3         MSM_GPIO1_REG(0x08)   /* gpio  94-68  */
+#define MSM_GPIO_OUT_4         MSM_GPIO1_REG(0x0C)   /* gpio 106-95  */
+#define MSM_GPIO_OUT_5         MSM_GPIO1_REG(0x50)   /* gpio 133-107 */
+#define MSM_GPIO_OUT_6         MSM_GPIO1_REG(0xC4)   /* gpio 150-134 */
+#define MSM_GPIO_OUT_7         MSM_GPIO1_REG(0x214)  /* gpio 181-151 */
 
 /* same pin map as above, output enable */
-#define QSD8X50_GPIO_OE_0	MSM_GPIO1_SHADOW_REG(0x20)
-#define QSD8X50_GPIO_OE_1	MSM_GPIO2_SHADOW_REG(0x08)
-#define QSD8X50_GPIO_OE_2	MSM_GPIO1_SHADOW_REG(0x24)
-#define QSD8X50_GPIO_OE_3	MSM_GPIO1_SHADOW_REG(0x28)
-#define QSD8X50_GPIO_OE_4	MSM_GPIO1_SHADOW_REG(0x2C)
-#define QSD8X50_GPIO_OE_5	MSM_GPIO1_SHADOW_REG(0x30)
-#define QSD8X50_GPIO_OE_6	MSM_GPIO1_SHADOW_REG(0x34)
-#define QSD8X50_GPIO_OE_7	MSM_GPIO1_SHADOW_REG(0x38)
+#define MSM_GPIO_OE_0          MSM_GPIO1_REG(0x10)
+#define MSM_GPIO_OE_1          MSM_GPIO2_REG(0x08)
+#define MSM_GPIO_OE_2          MSM_GPIO1_REG(0x14)
+#define MSM_GPIO_OE_3          MSM_GPIO1_REG(0x18)
+#define MSM_GPIO_OE_4          MSM_GPIO1_REG(0x1C)
+#define MSM_GPIO_OE_5          MSM_GPIO1_REG(0x54)
+#define MSM_GPIO_OE_6          MSM_GPIO1_REG(0xC8)
+#define MSM_GPIO_OE_7          MSM_GPIO1_REG(0x218)
 
 /* same pin map as above, input read */
-#define QSD8X50_GPIO_IN_0	MSM_GPIO1_SHADOW_REG(0x50)
-#define QSD8X50_GPIO_IN_1	MSM_GPIO2_SHADOW_REG(0x20)
-#define QSD8X50_GPIO_IN_2	MSM_GPIO1_SHADOW_REG(0x54)
-#define QSD8X50_GPIO_IN_3	MSM_GPIO1_SHADOW_REG(0x58)
-#define QSD8X50_GPIO_IN_4	MSM_GPIO1_SHADOW_REG(0x5C)
-#define QSD8X50_GPIO_IN_5	MSM_GPIO1_SHADOW_REG(0x60)
-#define QSD8X50_GPIO_IN_6	MSM_GPIO1_SHADOW_REG(0x64)
-#define QSD8X50_GPIO_IN_7	MSM_GPIO1_SHADOW_REG(0x68)
+#define MSM_GPIO_IN_0          MSM_GPIO1_REG(0x34)
+#define MSM_GPIO_IN_1          MSM_GPIO2_REG(0x20)
+#define MSM_GPIO_IN_2          MSM_GPIO1_REG(0x38)
+#define MSM_GPIO_IN_3          MSM_GPIO1_REG(0x3C)
+#define MSM_GPIO_IN_4          MSM_GPIO1_REG(0x40)
+#define MSM_GPIO_IN_5          MSM_GPIO1_REG(0x44)
+#define MSM_GPIO_IN_6          MSM_GPIO1_REG(0xCC)
+#define MSM_GPIO_IN_7          MSM_GPIO1_REG(0x21C)
 
 /* same pin map as above, 1=edge 0=level interrup */
-#define QSD8X50_GPIO_INT_EDGE_0	MSM_GPIO1_SHADOW_REG(0x70)
-#define QSD8X50_GPIO_INT_EDGE_1	MSM_GPIO2_SHADOW_REG(0x50)
-#define QSD8X50_GPIO_INT_EDGE_2	MSM_GPIO1_SHADOW_REG(0x74)
-#define QSD8X50_GPIO_INT_EDGE_3	MSM_GPIO1_SHADOW_REG(0x78)
-#define QSD8X50_GPIO_INT_EDGE_4	MSM_GPIO1_SHADOW_REG(0x7C)
-#define QSD8X50_GPIO_INT_EDGE_5	MSM_GPIO1_SHADOW_REG(0x80)
-#define QSD8X50_GPIO_INT_EDGE_6	MSM_GPIO1_SHADOW_REG(0x84)
-#define QSD8X50_GPIO_INT_EDGE_7	MSM_GPIO1_SHADOW_REG(0x88)
+#define MSM_GPIO_INT_EDGE_0    MSM_GPIO1_REG(0x60)
+#define MSM_GPIO_INT_EDGE_1    MSM_GPIO2_REG(0x50)
+#define MSM_GPIO_INT_EDGE_2    MSM_GPIO1_REG(0x64)
+#define MSM_GPIO_INT_EDGE_3    MSM_GPIO1_REG(0x68)
+#define MSM_GPIO_INT_EDGE_4    MSM_GPIO1_REG(0x6C)
+#define MSM_GPIO_INT_EDGE_5    MSM_GPIO1_REG(0xC0)
+#define MSM_GPIO_INT_EDGE_6    MSM_GPIO1_REG(0xD0)
+#define MSM_GPIO_INT_EDGE_7    MSM_GPIO1_REG(0x240)
 
 /* same pin map as above, 1=positive 0=negative */
-#define QSD8X50_GPIO_INT_POS_0	MSM_GPIO1_SHADOW_REG(0x90)
-#define QSD8X50_GPIO_INT_POS_1	MSM_GPIO2_SHADOW_REG(0x58)
-#define QSD8X50_GPIO_INT_POS_2	MSM_GPIO1_SHADOW_REG(0x94)
-#define QSD8X50_GPIO_INT_POS_3	MSM_GPIO1_SHADOW_REG(0x98)
-#define QSD8X50_GPIO_INT_POS_4	MSM_GPIO1_SHADOW_REG(0x9C)
-#define QSD8X50_GPIO_INT_POS_5	MSM_GPIO1_SHADOW_REG(0xA0)
-#define QSD8X50_GPIO_INT_POS_6	MSM_GPIO1_SHADOW_REG(0xA4)
-#define QSD8X50_GPIO_INT_POS_7	MSM_GPIO1_SHADOW_REG(0xA8)
+#define MSM_GPIO_INT_POS_0     MSM_GPIO1_REG(0x70)
+#define MSM_GPIO_INT_POS_1     MSM_GPIO2_REG(0x58)
+#define MSM_GPIO_INT_POS_2     MSM_GPIO1_REG(0x74)
+#define MSM_GPIO_INT_POS_3     MSM_GPIO1_REG(0x78)
+#define MSM_GPIO_INT_POS_4     MSM_GPIO1_REG(0x7C)
+#define MSM_GPIO_INT_POS_5     MSM_GPIO1_REG(0xBC)
+#define MSM_GPIO_INT_POS_6     MSM_GPIO1_REG(0xD4)
+#define MSM_GPIO_INT_POS_7     MSM_GPIO1_REG(0x228)
 
 /* same pin map as above, interrupt enable */
-#define QSD8X50_GPIO_INT_EN_0	MSM_GPIO1_SHADOW_REG(0xB0)
-#define QSD8X50_GPIO_INT_EN_1	MSM_GPIO2_SHADOW_REG(0x60)
-#define QSD8X50_GPIO_INT_EN_2	MSM_GPIO1_SHADOW_REG(0xB4)
-#define QSD8X50_GPIO_INT_EN_3	MSM_GPIO1_SHADOW_REG(0xB8)
-#define QSD8X50_GPIO_INT_EN_4	MSM_GPIO1_SHADOW_REG(0xBC)
-#define QSD8X50_GPIO_INT_EN_5	MSM_GPIO1_SHADOW_REG(0xC0)
-#define QSD8X50_GPIO_INT_EN_6	MSM_GPIO1_SHADOW_REG(0xC4)
-#define QSD8X50_GPIO_INT_EN_7	MSM_GPIO1_SHADOW_REG(0xC8)
+#define MSM_GPIO_INT_EN_0      MSM_GPIO1_REG(0x80)
+#define MSM_GPIO_INT_EN_1      MSM_GPIO2_REG(0x60)
+#define MSM_GPIO_INT_EN_2      MSM_GPIO1_REG(0x84)
+#define MSM_GPIO_INT_EN_3      MSM_GPIO1_REG(0x88)
+#define MSM_GPIO_INT_EN_4      MSM_GPIO1_REG(0x8C)
+#define MSM_GPIO_INT_EN_5      MSM_GPIO1_REG(0xB8)
+#define MSM_GPIO_INT_EN_6      MSM_GPIO1_REG(0xD8)
+#define MSM_GPIO_INT_EN_7      MSM_GPIO1_REG(0x22C)
 
 /* same pin map as above, write 1 to clear interrupt */
-#define QSD8X50_GPIO_INT_CLEAR_0	MSM_GPIO1_SHADOW_REG(0xD0)
-#define QSD8X50_GPIO_INT_CLEAR_1	MSM_GPIO2_SHADOW_REG(0x68)
-#define QSD8X50_GPIO_INT_CLEAR_2	MSM_GPIO1_SHADOW_REG(0xD4)
-#define QSD8X50_GPIO_INT_CLEAR_3	MSM_GPIO1_SHADOW_REG(0xD8)
-#define QSD8X50_GPIO_INT_CLEAR_4	MSM_GPIO1_SHADOW_REG(0xDC)
-#define QSD8X50_GPIO_INT_CLEAR_5	MSM_GPIO1_SHADOW_REG(0xE0)
-#define QSD8X50_GPIO_INT_CLEAR_6	MSM_GPIO1_SHADOW_REG(0xE4)
-#define QSD8X50_GPIO_INT_CLEAR_7	MSM_GPIO1_SHADOW_REG(0xE8)
+#define MSM_GPIO_INT_CLEAR_0   MSM_GPIO1_REG(0x90)
+#define MSM_GPIO_INT_CLEAR_1   MSM_GPIO2_REG(0x68)
+#define MSM_GPIO_INT_CLEAR_2   MSM_GPIO1_REG(0x94)
+#define MSM_GPIO_INT_CLEAR_3   MSM_GPIO1_REG(0x98)
+#define MSM_GPIO_INT_CLEAR_4   MSM_GPIO1_REG(0x9C)
+#define MSM_GPIO_INT_CLEAR_5   MSM_GPIO1_REG(0xB4)
+#define MSM_GPIO_INT_CLEAR_6   MSM_GPIO1_REG(0xDC)
+#define MSM_GPIO_INT_CLEAR_7   MSM_GPIO1_REG(0x230)
 
 /* same pin map as above, 1=interrupt pending */
-#define QSD8X50_GPIO_INT_STATUS_0	MSM_GPIO1_SHADOW_REG(0xF0)
-#define QSD8X50_GPIO_INT_STATUS_1	MSM_GPIO2_SHADOW_REG(0x70)
-#define QSD8X50_GPIO_INT_STATUS_2	MSM_GPIO1_SHADOW_REG(0xF4)
-#define QSD8X50_GPIO_INT_STATUS_3	MSM_GPIO1_SHADOW_REG(0xF8)
-#define QSD8X50_GPIO_INT_STATUS_4	MSM_GPIO1_SHADOW_REG(0xFC)
-#define QSD8X50_GPIO_INT_STATUS_5	MSM_GPIO1_SHADOW_REG(0x100)
-#define QSD8X50_GPIO_INT_STATUS_6	MSM_GPIO1_SHADOW_REG(0x104)
-#define QSD8X50_GPIO_INT_STATUS_7	MSM_GPIO1_SHADOW_REG(0x108)
+#define MSM_GPIO_INT_STATUS_0  MSM_GPIO1_REG(0xA0)
+#define MSM_GPIO_INT_STATUS_1  MSM_GPIO2_REG(0x70)
+#define MSM_GPIO_INT_STATUS_2  MSM_GPIO1_REG(0xA4)
+#define MSM_GPIO_INT_STATUS_3  MSM_GPIO1_REG(0xA8)
+#define MSM_GPIO_INT_STATUS_4  MSM_GPIO1_REG(0xAC)
+#define MSM_GPIO_INT_STATUS_5  MSM_GPIO1_REG(0xB0)
+#define MSM_GPIO_INT_STATUS_6  MSM_GPIO1_REG(0xE0)
+#define MSM_GPIO_INT_STATUS_7  MSM_GPIO1_REG(0x234)
 
-/*
- * MSM7X30 registers
- */
-/* output value */
-#define MSM7X30_GPIO_OUT_0	MSM_GPIO1_REG(0x00)   /* gpio  15-0   */
-#define MSM7X30_GPIO_OUT_1	MSM_GPIO2_REG(0x00)   /* gpio  43-16  */
-#define MSM7X30_GPIO_OUT_2	MSM_GPIO1_REG(0x04)   /* gpio  67-44  */
-#define MSM7X30_GPIO_OUT_3	MSM_GPIO1_REG(0x08)   /* gpio  94-68  */
-#define MSM7X30_GPIO_OUT_4	MSM_GPIO1_REG(0x0C)   /* gpio 106-95  */
-#define MSM7X30_GPIO_OUT_5	MSM_GPIO1_REG(0x50)   /* gpio 133-107 */
-#define MSM7X30_GPIO_OUT_6	MSM_GPIO1_REG(0xC4)   /* gpio 150-134 */
-#define MSM7X30_GPIO_OUT_7	MSM_GPIO1_REG(0x214)  /* gpio 181-151 */
+#endif
 
-/* same pin map as above, output enable */
-#define MSM7X30_GPIO_OE_0	MSM_GPIO1_REG(0x10)
-#define MSM7X30_GPIO_OE_1	MSM_GPIO2_REG(0x08)
-#define MSM7X30_GPIO_OE_2	MSM_GPIO1_REG(0x14)
-#define MSM7X30_GPIO_OE_3	MSM_GPIO1_REG(0x18)
-#define MSM7X30_GPIO_OE_4	MSM_GPIO1_REG(0x1C)
-#define MSM7X30_GPIO_OE_5	MSM_GPIO1_REG(0x54)
-#define MSM7X30_GPIO_OE_6	MSM_GPIO1_REG(0xC8)
-#define MSM7X30_GPIO_OE_7	MSM_GPIO1_REG(0x218)
-
-/* same pin map as above, input read */
-#define MSM7X30_GPIO_IN_0	MSM_GPIO1_REG(0x34)
-#define MSM7X30_GPIO_IN_1	MSM_GPIO2_REG(0x20)
-#define MSM7X30_GPIO_IN_2	MSM_GPIO1_REG(0x38)
-#define MSM7X30_GPIO_IN_3	MSM_GPIO1_REG(0x3C)
-#define MSM7X30_GPIO_IN_4	MSM_GPIO1_REG(0x40)
-#define MSM7X30_GPIO_IN_5	MSM_GPIO1_REG(0x44)
-#define MSM7X30_GPIO_IN_6	MSM_GPIO1_REG(0xCC)
-#define MSM7X30_GPIO_IN_7	MSM_GPIO1_REG(0x21C)
-
-/* same pin map as above, 1=edge 0=level interrup */
-#define MSM7X30_GPIO_INT_EDGE_0	MSM_GPIO1_REG(0x60)
-#define MSM7X30_GPIO_INT_EDGE_1	MSM_GPIO2_REG(0x50)
-#define MSM7X30_GPIO_INT_EDGE_2	MSM_GPIO1_REG(0x64)
-#define MSM7X30_GPIO_INT_EDGE_3	MSM_GPIO1_REG(0x68)
-#define MSM7X30_GPIO_INT_EDGE_4	MSM_GPIO1_REG(0x6C)
-#define MSM7X30_GPIO_INT_EDGE_5	MSM_GPIO1_REG(0xC0)
-#define MSM7X30_GPIO_INT_EDGE_6	MSM_GPIO1_REG(0xD0)
-#define MSM7X30_GPIO_INT_EDGE_7	MSM_GPIO1_REG(0x240)
-
-/* same pin map as above, 1=positive 0=negative */
-#define MSM7X30_GPIO_INT_POS_0	MSM_GPIO1_REG(0x70)
-#define MSM7X30_GPIO_INT_POS_1	MSM_GPIO2_REG(0x58)
-#define MSM7X30_GPIO_INT_POS_2	MSM_GPIO1_REG(0x74)
-#define MSM7X30_GPIO_INT_POS_3	MSM_GPIO1_REG(0x78)
-#define MSM7X30_GPIO_INT_POS_4	MSM_GPIO1_REG(0x7C)
-#define MSM7X30_GPIO_INT_POS_5	MSM_GPIO1_REG(0xBC)
-#define MSM7X30_GPIO_INT_POS_6	MSM_GPIO1_REG(0xD4)
-#define MSM7X30_GPIO_INT_POS_7	MSM_GPIO1_REG(0x228)
-
-/* same pin map as above, interrupt enable */
-#define MSM7X30_GPIO_INT_EN_0	MSM_GPIO1_REG(0x80)
-#define MSM7X30_GPIO_INT_EN_1	MSM_GPIO2_REG(0x60)
-#define MSM7X30_GPIO_INT_EN_2	MSM_GPIO1_REG(0x84)
-#define MSM7X30_GPIO_INT_EN_3	MSM_GPIO1_REG(0x88)
-#define MSM7X30_GPIO_INT_EN_4	MSM_GPIO1_REG(0x8C)
-#define MSM7X30_GPIO_INT_EN_5	MSM_GPIO1_REG(0xB8)
-#define MSM7X30_GPIO_INT_EN_6	MSM_GPIO1_REG(0xD8)
-#define MSM7X30_GPIO_INT_EN_7	MSM_GPIO1_REG(0x22C)
-
-/* same pin map as above, write 1 to clear interrupt */
-#define MSM7X30_GPIO_INT_CLEAR_0	MSM_GPIO1_REG(0x90)
-#define MSM7X30_GPIO_INT_CLEAR_1	MSM_GPIO2_REG(0x68)
-#define MSM7X30_GPIO_INT_CLEAR_2	MSM_GPIO1_REG(0x94)
-#define MSM7X30_GPIO_INT_CLEAR_3	MSM_GPIO1_REG(0x98)
-#define MSM7X30_GPIO_INT_CLEAR_4	MSM_GPIO1_REG(0x9C)
-#define MSM7X30_GPIO_INT_CLEAR_5	MSM_GPIO1_REG(0xB4)
-#define MSM7X30_GPIO_INT_CLEAR_6	MSM_GPIO1_REG(0xDC)
-#define MSM7X30_GPIO_INT_CLEAR_7	MSM_GPIO1_REG(0x230)
-
-/* same pin map as above, 1=interrupt pending */
-#define MSM7X30_GPIO_INT_STATUS_0	MSM_GPIO1_REG(0xA0)
-#define MSM7X30_GPIO_INT_STATUS_1	MSM_GPIO2_REG(0x70)
-#define MSM7X30_GPIO_INT_STATUS_2	MSM_GPIO1_REG(0xA4)
-#define MSM7X30_GPIO_INT_STATUS_3	MSM_GPIO1_REG(0xA8)
-#define MSM7X30_GPIO_INT_STATUS_4	MSM_GPIO1_REG(0xAC)
-#define MSM7X30_GPIO_INT_STATUS_5	MSM_GPIO1_REG(0xB0)
-#define MSM7X30_GPIO_INT_STATUS_6	MSM_GPIO1_REG(0xE0)
-#define MSM7X30_GPIO_INT_STATUS_7	MSM_GPIO1_REG(0x234)
+enum {
+	GPIO_DEBUG_SLEEP = 1U << 0,
+};
+static int msm_gpio_debug_mask;
+module_param_named(debug_mask, msm_gpio_debug_mask, int,
+		   S_IRUGO | S_IWUSR | S_IWGRP);
 
 #define FIRST_GPIO_IRQ MSM_GPIO_TO_INT(0)
 
-#define MSM_GPIO_BANK(soc, bank, first, last)				\
+#define MSM_GPIO_BANK(bank, first, last)				\
 	{								\
 		.regs = {						\
-			.out =         soc##_GPIO_OUT_##bank,		\
-			.in =          soc##_GPIO_IN_##bank,		\
-			.int_status =  soc##_GPIO_INT_STATUS_##bank,	\
-			.int_clear =   soc##_GPIO_INT_CLEAR_##bank,	\
-			.int_en =      soc##_GPIO_INT_EN_##bank,	\
-			.int_edge =    soc##_GPIO_INT_EDGE_##bank,	\
-			.int_pos =     soc##_GPIO_INT_POS_##bank,	\
-			.oe =          soc##_GPIO_OE_##bank,		\
+			.out =         MSM_GPIO_OUT_##bank,		\
+			.in =          MSM_GPIO_IN_##bank,		\
+			.int_status =  MSM_GPIO_INT_STATUS_##bank,	\
+			.int_clear =   MSM_GPIO_INT_CLEAR_##bank,	\
+			.int_en =      MSM_GPIO_INT_EN_##bank,		\
+			.int_edge =    MSM_GPIO_INT_EDGE_##bank,	\
+			.int_pos =     MSM_GPIO_INT_POS_##bank,		\
+			.oe =          MSM_GPIO_OE_##bank,		\
 		},							\
 		.chip = {						\
 			.base = (first),				\
@@ -329,11 +264,11 @@
 	unsigned mask = BIT(offset);
 	unsigned val;
 
-	val = readl(msm_chip->regs.out);
+	val = __raw_readl(msm_chip->regs.out);
 	if (on)
-		writel(val | mask, msm_chip->regs.out);
+		__raw_writel(val | mask, msm_chip->regs.out);
 	else
-		writel(val & ~mask, msm_chip->regs.out);
+		__raw_writel(val & ~mask, msm_chip->regs.out);
 	return 0;
 }
 
@@ -342,13 +277,13 @@
 	int loop_limit = 100;
 	unsigned pol, val, val2, intstat;
 	do {
-		val = readl(msm_chip->regs.in);
-		pol = readl(msm_chip->regs.int_pos);
+		val = __raw_readl(msm_chip->regs.in);
+		pol = __raw_readl(msm_chip->regs.int_pos);
 		pol = (pol & ~msm_chip->both_edge_detect) |
 		      (~val & msm_chip->both_edge_detect);
-		writel(pol, msm_chip->regs.int_pos);
-		intstat = readl(msm_chip->regs.int_status);
-		val2 = readl(msm_chip->regs.in);
+		__raw_writel(pol, msm_chip->regs.int_pos);
+		intstat = __raw_readl(msm_chip->regs.int_status);
+		val2 = __raw_readl(msm_chip->regs.in);
 		if (((val ^ val2) & msm_chip->both_edge_detect & ~intstat) == 0)
 			return;
 	} while (loop_limit-- > 0);
@@ -365,10 +300,10 @@
 	/* Save interrupts that already triggered before we loose them. */
 	/* Any interrupt that triggers between the read of int_status */
 	/* and the write to int_clear will still be lost though. */
-	msm_chip->int_status_copy |= readl(msm_chip->regs.int_status);
+	msm_chip->int_status_copy |= __raw_readl(msm_chip->regs.int_status);
 	msm_chip->int_status_copy &= ~bit;
 #endif
-	writel(bit, msm_chip->regs.int_clear);
+	__raw_writel(bit, msm_chip->regs.int_clear);
 	msm_gpio_update_both_edge_detect(msm_chip);
 	return 0;
 }
@@ -380,7 +315,9 @@
 
 	msm_chip = container_of(chip, struct msm_gpio_chip, chip);
 	spin_lock_irqsave(&msm_chip->lock, irq_flags);
-	writel(readl(msm_chip->regs.oe) & ~BIT(offset), msm_chip->regs.oe);
+	__raw_writel(__raw_readl(msm_chip->regs.oe) & ~BIT(offset),
+			msm_chip->regs.oe);
+	mb();
 	spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
 	return 0;
 }
@@ -394,7 +331,9 @@
 	msm_chip = container_of(chip, struct msm_gpio_chip, chip);
 	spin_lock_irqsave(&msm_chip->lock, irq_flags);
 	msm_gpio_write(msm_chip, offset, value);
-	writel(readl(msm_chip->regs.oe) | BIT(offset), msm_chip->regs.oe);
+	__raw_writel(__raw_readl(msm_chip->regs.oe) | BIT(offset),
+			msm_chip->regs.oe);
+	mb();
 	spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
 	return 0;
 }
@@ -402,9 +341,12 @@
 static int msm_gpio_get(struct gpio_chip *chip, unsigned offset)
 {
 	struct msm_gpio_chip *msm_chip;
+	int rc;
 
 	msm_chip = container_of(chip, struct msm_gpio_chip, chip);
-	return (readl(msm_chip->regs.in) & (1U << offset)) ? 1 : 0;
+	rc = (__raw_readl(msm_chip->regs.in) & (1U << offset)) ? 1 : 0;
+	mb();
+	return rc;
 }
 
 static void msm_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
@@ -415,6 +357,7 @@
 	msm_chip = container_of(chip, struct msm_gpio_chip, chip);
 	spin_lock_irqsave(&msm_chip->lock, irq_flags);
 	msm_gpio_write(msm_chip, offset, value);
+	mb();
 	spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
 }
 
@@ -438,84 +381,88 @@
 #define msm_gpio_free NULL
 #endif
 
-static struct msm_gpio_chip *msm_gpio_chips;
-static int msm_gpio_count;
-
-static struct msm_gpio_chip msm_gpio_chips_msm7x01[] = {
-	MSM_GPIO_BANK(MSM7X00, 0,   0,  15),
-	MSM_GPIO_BANK(MSM7X00, 1,  16,  42),
-	MSM_GPIO_BANK(MSM7X00, 2,  43,  67),
-	MSM_GPIO_BANK(MSM7X00, 3,  68,  94),
-	MSM_GPIO_BANK(MSM7X00, 4,  95, 106),
-	MSM_GPIO_BANK(MSM7X00, 5, 107, 121),
-};
-
-static struct msm_gpio_chip msm_gpio_chips_msm7x30[] = {
-	MSM_GPIO_BANK(MSM7X30, 0,   0,  15),
-	MSM_GPIO_BANK(MSM7X30, 1,  16,  43),
-	MSM_GPIO_BANK(MSM7X30, 2,  44,  67),
-	MSM_GPIO_BANK(MSM7X30, 3,  68,  94),
-	MSM_GPIO_BANK(MSM7X30, 4,  95, 106),
-	MSM_GPIO_BANK(MSM7X30, 5, 107, 133),
-	MSM_GPIO_BANK(MSM7X30, 6, 134, 150),
-	MSM_GPIO_BANK(MSM7X30, 7, 151, 181),
-};
-
-static struct msm_gpio_chip msm_gpio_chips_qsd8x50[] = {
-	MSM_GPIO_BANK(QSD8X50, 0,   0,  15),
-	MSM_GPIO_BANK(QSD8X50, 1,  16,  42),
-	MSM_GPIO_BANK(QSD8X50, 2,  43,  67),
-	MSM_GPIO_BANK(QSD8X50, 3,  68,  94),
-	MSM_GPIO_BANK(QSD8X50, 4,  95, 103),
-	MSM_GPIO_BANK(QSD8X50, 5, 104, 121),
-	MSM_GPIO_BANK(QSD8X50, 6, 122, 152),
-	MSM_GPIO_BANK(QSD8X50, 7, 153, 164),
+struct msm_gpio_chip msm_gpio_chips[] = {
+#if defined(CONFIG_ARCH_MSM7X00A)
+	MSM_GPIO_BANK(0,   0,  15),
+	MSM_GPIO_BANK(1,  16,  42),
+	MSM_GPIO_BANK(2,  43,  67),
+	MSM_GPIO_BANK(3,  68,  94),
+	MSM_GPIO_BANK(4,  95, 106),
+	MSM_GPIO_BANK(5, 107, 121),
+#elif defined(CONFIG_ARCH_MSM7X25) || defined(CONFIG_ARCH_MSM7X27)
+	MSM_GPIO_BANK(0,   0,  15),
+	MSM_GPIO_BANK(1,  16,  42),
+	MSM_GPIO_BANK(2,  43,  67),
+	MSM_GPIO_BANK(3,  68,  94),
+	MSM_GPIO_BANK(4,  95, 106),
+	MSM_GPIO_BANK(5, 107, 132),
+#elif defined(CONFIG_ARCH_MSM7X30)
+	MSM_GPIO_BANK(0,   0,  15),
+	MSM_GPIO_BANK(1,  16,  43),
+	MSM_GPIO_BANK(2,  44,  67),
+	MSM_GPIO_BANK(3,  68,  94),
+	MSM_GPIO_BANK(4,  95, 106),
+	MSM_GPIO_BANK(5, 107, 133),
+	MSM_GPIO_BANK(6, 134, 150),
+	MSM_GPIO_BANK(7, 151, 181),
+#elif defined(CONFIG_ARCH_QSD8X50)
+	MSM_GPIO_BANK(0,   0,  15),
+	MSM_GPIO_BANK(1,  16,  42),
+	MSM_GPIO_BANK(2,  43,  67),
+	MSM_GPIO_BANK(3,  68,  94),
+	MSM_GPIO_BANK(4,  95, 103),
+	MSM_GPIO_BANK(5, 104, 121),
+	MSM_GPIO_BANK(6, 122, 152),
+	MSM_GPIO_BANK(7, 153, 164),
+#endif
 };
 
 static void msm_gpio_irq_ack(struct irq_data *d)
 {
 	unsigned long irq_flags;
-	struct msm_gpio_chip *msm_chip = irq_data_get_irq_chip_data(d);
+	struct msm_gpio_chip *msm_chip = irq_get_chip_data(d->irq);
 	spin_lock_irqsave(&msm_chip->lock, irq_flags);
 	msm_gpio_clear_detect_status(msm_chip,
-				     d->irq - gpio_to_irq(msm_chip->chip.base));
+			     d->irq - gpio_to_irq(msm_chip->chip.base));
 	spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
 }
 
 static void msm_gpio_irq_mask(struct irq_data *d)
 {
 	unsigned long irq_flags;
-	struct msm_gpio_chip *msm_chip = irq_data_get_irq_chip_data(d);
+	struct msm_gpio_chip *msm_chip = irq_get_chip_data(d->irq);
 	unsigned offset = d->irq - gpio_to_irq(msm_chip->chip.base);
 
 	spin_lock_irqsave(&msm_chip->lock, irq_flags);
 	/* level triggered interrupts are also latched */
-	if (!(readl(msm_chip->regs.int_edge) & BIT(offset)))
+	if (!(__raw_readl(msm_chip->regs.int_edge) & BIT(offset)))
 		msm_gpio_clear_detect_status(msm_chip, offset);
 	msm_chip->int_enable[0] &= ~BIT(offset);
-	writel(msm_chip->int_enable[0], msm_chip->regs.int_en);
+	__raw_writel(msm_chip->int_enable[0], msm_chip->regs.int_en);
+	mb();
 	spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
 }
 
 static void msm_gpio_irq_unmask(struct irq_data *d)
 {
 	unsigned long irq_flags;
-	struct msm_gpio_chip *msm_chip = irq_data_get_irq_chip_data(d);
+	struct msm_gpio_chip *msm_chip = irq_get_chip_data(d->irq);
 	unsigned offset = d->irq - gpio_to_irq(msm_chip->chip.base);
 
 	spin_lock_irqsave(&msm_chip->lock, irq_flags);
 	/* level triggered interrupts are also latched */
-	if (!(readl(msm_chip->regs.int_edge) & BIT(offset)))
+	if (!(__raw_readl(msm_chip->regs.int_edge) & BIT(offset)))
 		msm_gpio_clear_detect_status(msm_chip, offset);
 	msm_chip->int_enable[0] |= BIT(offset);
-	writel(msm_chip->int_enable[0], msm_chip->regs.int_en);
+	__raw_writel(msm_chip->int_enable[0], msm_chip->regs.int_en);
+	mb();
 	spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
 }
 
 static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
 {
 	unsigned long irq_flags;
-	struct msm_gpio_chip *msm_chip = irq_data_get_irq_chip_data(d);
+	struct msm_gpio_chip *msm_chip = irq_get_chip_data(d->irq);
 	unsigned offset = d->irq - gpio_to_irq(msm_chip->chip.base);
 
 	spin_lock_irqsave(&msm_chip->lock, irq_flags);
@@ -532,17 +479,17 @@
 static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int flow_type)
 {
 	unsigned long irq_flags;
-	struct msm_gpio_chip *msm_chip = irq_data_get_irq_chip_data(d);
+	struct msm_gpio_chip *msm_chip = irq_get_chip_data(d->irq);
 	unsigned offset = d->irq - gpio_to_irq(msm_chip->chip.base);
 	unsigned val, mask = BIT(offset);
 
 	spin_lock_irqsave(&msm_chip->lock, irq_flags);
-	val = readl(msm_chip->regs.int_edge);
+	val = __raw_readl(msm_chip->regs.int_edge);
 	if (flow_type & IRQ_TYPE_EDGE_BOTH) {
-		writel(val | mask, msm_chip->regs.int_edge);
+		__raw_writel(val | mask, msm_chip->regs.int_edge);
 		__irq_set_handler_locked(d->irq, handle_edge_irq);
 	} else {
-		writel(val & ~mask, msm_chip->regs.int_edge);
+		__raw_writel(val & ~mask, msm_chip->regs.int_edge);
 		__irq_set_handler_locked(d->irq, handle_level_irq);
 	}
 	if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) {
@@ -550,12 +497,13 @@
 		msm_gpio_update_both_edge_detect(msm_chip);
 	} else {
 		msm_chip->both_edge_detect &= ~mask;
-		val = readl(msm_chip->regs.int_pos);
+		val = __raw_readl(msm_chip->regs.int_pos);
 		if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_HIGH))
-			writel(val | mask, msm_chip->regs.int_pos);
+			__raw_writel(val | mask, msm_chip->regs.int_pos);
 		else
-			writel(val & ~mask, msm_chip->regs.int_pos);
+			__raw_writel(val & ~mask, msm_chip->regs.int_pos);
 	}
+	mb();
 	spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
 	return 0;
 }
@@ -564,10 +512,13 @@
 {
 	int i, j, mask;
 	unsigned val;
+	struct irq_chip *chip = irq_desc_get_chip(desc);
 
-	for (i = 0; i < msm_gpio_count; i++) {
+	chained_irq_enter(chip, desc);
+
+	for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) {
 		struct msm_gpio_chip *msm_chip = &msm_gpio_chips[i];
-		val = readl(msm_chip->regs.int_status);
+		val = __raw_readl(msm_chip->regs.int_status);
 		val &= msm_chip->int_enable[0];
 		while (val) {
 			mask = val & -val;
@@ -580,34 +531,266 @@
 					   msm_chip->chip.base + j);
 		}
 	}
-	desc->irq_data.chip->irq_ack(&desc->irq_data);
+
+	chained_irq_exit(chip, desc);
 }
 
 static struct irq_chip msm_gpio_irq_chip = {
-	.name          = "msmgpio",
-	.irq_ack       = msm_gpio_irq_ack,
-	.irq_mask      = msm_gpio_irq_mask,
-	.irq_unmask    = msm_gpio_irq_unmask,
-	.irq_set_wake  = msm_gpio_irq_set_wake,
-	.irq_set_type  = msm_gpio_irq_set_type,
+	.name      = "msmgpio",
+	.irq_ack	= msm_gpio_irq_ack,
+	.irq_mask	= msm_gpio_irq_mask,
+	.irq_unmask	= msm_gpio_irq_unmask,
+	.irq_set_wake	= msm_gpio_irq_set_wake,
+	.irq_set_type	= msm_gpio_irq_set_type,
 };
 
-static int __init msm_init_gpio(void)
+#define NUM_GPIO_SMEM_BANKS 6
+#define GPIO_SMEM_NUM_GROUPS 2
+#define GPIO_SMEM_MAX_PC_INTERRUPTS 8
+struct tramp_gpio_smem {
+	uint16_t num_fired[GPIO_SMEM_NUM_GROUPS];
+	uint16_t fired[GPIO_SMEM_NUM_GROUPS][GPIO_SMEM_MAX_PC_INTERRUPTS];
+	uint32_t enabled[NUM_GPIO_SMEM_BANKS];
+	uint32_t detection[NUM_GPIO_SMEM_BANKS];
+	uint32_t polarity[NUM_GPIO_SMEM_BANKS];
+};
+
+static void msm_gpio_sleep_int(unsigned long arg)
+{
+	int i, j;
+	struct tramp_gpio_smem *smem_gpio;
+
+	BUILD_BUG_ON(NR_GPIO_IRQS > NUM_GPIO_SMEM_BANKS * 32);
+
+	smem_gpio = smem_alloc(SMEM_GPIO_INT, sizeof(*smem_gpio));
+	if (smem_gpio == NULL)
+		return;
+
+	local_irq_disable();
+	for (i = 0; i < GPIO_SMEM_NUM_GROUPS; i++) {
+		int count = smem_gpio->num_fired[i];
+		for (j = 0; j < count; j++) {
+			/* TODO: Check mask */
+			generic_handle_irq(
+				MSM_GPIO_TO_INT(smem_gpio->fired[i][j]));
+		}
+	}
+	local_irq_enable();
+}
+
+static DECLARE_TASKLET(msm_gpio_sleep_int_tasklet, msm_gpio_sleep_int, 0);
+
+void msm_gpio_enter_sleep(int from_idle)
+{
+	int i;
+	struct tramp_gpio_smem *smem_gpio;
+
+	smem_gpio = smem_alloc(SMEM_GPIO_INT, sizeof(*smem_gpio));
+
+	if (smem_gpio) {
+		for (i = 0; i < ARRAY_SIZE(smem_gpio->enabled); i++) {
+			smem_gpio->enabled[i] = 0;
+			smem_gpio->detection[i] = 0;
+			smem_gpio->polarity[i] = 0;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) {
+		__raw_writel(msm_gpio_chips[i].int_enable[!from_idle],
+		       msm_gpio_chips[i].regs.int_en);
+		if (smem_gpio) {
+			uint32_t tmp;
+			int start, index, shiftl, shiftr;
+			start = msm_gpio_chips[i].chip.base;
+			index = start / 32;
+			shiftl = start % 32;
+			shiftr = 32 - shiftl;
+			tmp = msm_gpio_chips[i].int_enable[!from_idle];
+			smem_gpio->enabled[index] |= tmp << shiftl;
+			smem_gpio->enabled[index+1] |= tmp >> shiftr;
+			smem_gpio->detection[index] |=
+				__raw_readl(msm_gpio_chips[i].regs.int_edge) <<
+				shiftl;
+			smem_gpio->detection[index+1] |=
+				__raw_readl(msm_gpio_chips[i].regs.int_edge) >>
+				shiftr;
+			smem_gpio->polarity[index] |=
+				__raw_readl(msm_gpio_chips[i].regs.int_pos) <<
+				shiftl;
+			smem_gpio->polarity[index+1] |=
+				__raw_readl(msm_gpio_chips[i].regs.int_pos) >>
+				shiftr;
+		}
+	}
+	mb();
+
+	if (smem_gpio) {
+		if (msm_gpio_debug_mask & GPIO_DEBUG_SLEEP)
+			for (i = 0; i < ARRAY_SIZE(smem_gpio->enabled); i++) {
+				printk("msm_gpio_enter_sleep gpio %d-%d: enable"
+				       " %08x, edge %08x, polarity %08x\n",
+				       i * 32, i * 32 + 31,
+				       smem_gpio->enabled[i],
+				       smem_gpio->detection[i],
+				       smem_gpio->polarity[i]);
+			}
+		for (i = 0; i < GPIO_SMEM_NUM_GROUPS; i++)
+			smem_gpio->num_fired[i] = 0;
+	}
+}
+
+void msm_gpio_exit_sleep(void)
+{
+	int i;
+	struct tramp_gpio_smem *smem_gpio;
+
+	smem_gpio = smem_alloc(SMEM_GPIO_INT, sizeof(*smem_gpio));
+
+	for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) {
+		__raw_writel(msm_gpio_chips[i].int_enable[0],
+		       msm_gpio_chips[i].regs.int_en);
+	}
+	mb();
+
+	if (smem_gpio && (smem_gpio->num_fired[0] || smem_gpio->num_fired[1])) {
+		if (msm_gpio_debug_mask & GPIO_DEBUG_SLEEP)
+			printk(KERN_INFO "gpio: fired %x %x\n",
+			      smem_gpio->num_fired[0], smem_gpio->num_fired[1]);
+		tasklet_schedule(&msm_gpio_sleep_int_tasklet);
+	}
+}
+
+
+int gpio_tlmm_config(unsigned config, unsigned disable)
+{
+	return msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &config, &disable);
+}
+EXPORT_SYMBOL(gpio_tlmm_config);
+
+int msm_gpios_request_enable(const struct msm_gpio *table, int size)
+{
+	int rc = msm_gpios_request(table, size);
+	if (rc)
+		return rc;
+	rc = msm_gpios_enable(table, size);
+	if (rc)
+		msm_gpios_free(table, size);
+	return rc;
+}
+EXPORT_SYMBOL(msm_gpios_request_enable);
+
+void msm_gpios_disable_free(const struct msm_gpio *table, int size)
+{
+	msm_gpios_disable(table, size);
+	msm_gpios_free(table, size);
+}
+EXPORT_SYMBOL(msm_gpios_disable_free);
+
+int msm_gpios_request(const struct msm_gpio *table, int size)
+{
+	int rc;
+	int i;
+	const struct msm_gpio *g;
+	for (i = 0; i < size; i++) {
+		g = table + i;
+		rc = gpio_request(GPIO_PIN(g->gpio_cfg), g->label);
+		if (rc) {
+			pr_err("gpio_request(%d) <%s> failed: %d\n",
+			       GPIO_PIN(g->gpio_cfg), g->label ?: "?", rc);
+			goto err;
+		}
+	}
+	return 0;
+err:
+	msm_gpios_free(table, i);
+	return rc;
+}
+EXPORT_SYMBOL(msm_gpios_request);
+
+void msm_gpios_free(const struct msm_gpio *table, int size)
+{
+	int i;
+	const struct msm_gpio *g;
+	for (i = size-1; i >= 0; i--) {
+		g = table + i;
+		gpio_free(GPIO_PIN(g->gpio_cfg));
+	}
+}
+EXPORT_SYMBOL(msm_gpios_free);
+
+int msm_gpios_enable(const struct msm_gpio *table, int size)
+{
+	int rc;
+	int i;
+	const struct msm_gpio *g;
+	for (i = 0; i < size; i++) {
+		g = table + i;
+		rc = gpio_tlmm_config(g->gpio_cfg, GPIO_CFG_ENABLE);
+		if (rc) {
+			pr_err("gpio_tlmm_config(0x%08x, GPIO_CFG_ENABLE)"
+			       " <%s> failed: %d\n",
+			       g->gpio_cfg, g->label ?: "?", rc);
+			pr_err("pin %d func %d dir %d pull %d drvstr %d\n",
+			       GPIO_PIN(g->gpio_cfg), GPIO_FUNC(g->gpio_cfg),
+			       GPIO_DIR(g->gpio_cfg), GPIO_PULL(g->gpio_cfg),
+			       GPIO_DRVSTR(g->gpio_cfg));
+			goto err;
+		}
+	}
+	return 0;
+err:
+	msm_gpios_disable(table, i);
+	return rc;
+}
+EXPORT_SYMBOL(msm_gpios_enable);
+
+int msm_gpios_disable(const struct msm_gpio *table, int size)
+{
+	int rc = 0;
+	int i;
+	const struct msm_gpio *g;
+	for (i = size-1; i >= 0; i--) {
+		int tmp;
+		g = table + i;
+		tmp = gpio_tlmm_config(g->gpio_cfg, GPIO_CFG_DISABLE);
+		if (tmp) {
+			pr_err("gpio_tlmm_config(0x%08x, GPIO_CFG_DISABLE)"
+			       " <%s> failed: %d\n",
+			       g->gpio_cfg, g->label ?: "?", rc);
+			pr_err("pin %d func %d dir %d pull %d drvstr %d\n",
+			       GPIO_PIN(g->gpio_cfg), GPIO_FUNC(g->gpio_cfg),
+			       GPIO_DIR(g->gpio_cfg), GPIO_PULL(g->gpio_cfg),
+			       GPIO_DRVSTR(g->gpio_cfg));
+			if (!rc)
+				rc = tmp;
+		}
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(msm_gpios_disable);
+
+/* Locate the GPIO_OUT register for the given GPIO and return its address
+ * and the bit position of the gpio's bit within the register.
+ *
+ * This function is used by gpiomux-v1 in order to support output transitions.
+ */
+void msm_gpio_find_out(const unsigned gpio, void __iomem **out,
+	unsigned *offset)
+{
+	struct msm_gpio_chip *msm_chip = msm_gpio_chips;
+
+	while (gpio >= msm_chip->chip.base + msm_chip->chip.ngpio)
+		++msm_chip;
+
+	*out = msm_chip->regs.out;
+	*offset = gpio - msm_chip->chip.base;
+}
+
+static int __devinit msm_gpio_probe(struct platform_device *dev)
 {
 	int i, j = 0;
-
-	if (cpu_is_msm7x01()) {
-		msm_gpio_chips = msm_gpio_chips_msm7x01;
-		msm_gpio_count = ARRAY_SIZE(msm_gpio_chips_msm7x01);
-	} else if (cpu_is_msm7x30()) {
-		msm_gpio_chips = msm_gpio_chips_msm7x30;
-		msm_gpio_count = ARRAY_SIZE(msm_gpio_chips_msm7x30);
-	} else if (cpu_is_qsd8x50()) {
-		msm_gpio_chips = msm_gpio_chips_qsd8x50;
-		msm_gpio_count = ARRAY_SIZE(msm_gpio_chips_qsd8x50);
-	} else {
-		return 0;
-	}
+	int grp_irq;
 
 	for (i = FIRST_GPIO_IRQ; i < FIRST_GPIO_IRQ + NR_GPIO_IRQS; i++) {
 		if (i - FIRST_GPIO_IRQ >=
@@ -620,17 +803,35 @@
 		set_irq_flags(i, IRQF_VALID);
 	}
 
-	for (i = 0; i < msm_gpio_count; i++) {
+	for (i = 0; i < dev->num_resources; i++) {
+		grp_irq = platform_get_irq(dev, i);
+		if (grp_irq < 0)
+			return -ENXIO;
+
+		irq_set_chained_handler(grp_irq, msm_gpio_irq_handler);
+		irq_set_irq_wake(grp_irq, (i + 1));
+	}
+
+	for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) {
 		spin_lock_init(&msm_gpio_chips[i].lock);
-		writel(0, msm_gpio_chips[i].regs.int_en);
+		__raw_writel(0, msm_gpio_chips[i].regs.int_en);
 		gpiochip_add(&msm_gpio_chips[i].chip);
 	}
 
-	irq_set_chained_handler(INT_GPIO_GROUP1, msm_gpio_irq_handler);
-	irq_set_chained_handler(INT_GPIO_GROUP2, msm_gpio_irq_handler);
-	irq_set_irq_wake(INT_GPIO_GROUP1, 1);
-	irq_set_irq_wake(INT_GPIO_GROUP2, 2);
+	mb();
 	return 0;
 }
 
-postcore_initcall(msm_init_gpio);
+static struct platform_driver msm_gpio_driver = {
+	.probe = msm_gpio_probe,
+	.driver = {
+		.name = "msmgpio",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init msm_gpio_init(void)
+{
+	return platform_driver_register(&msm_gpio_driver);
+}
+postcore_initcall(msm_gpio_init);
diff --git a/drivers/gpio/gpio-msm-v2.c b/drivers/gpio/gpio-msm-v2.c
index 5cb1227..ad436e0 100644
--- a/drivers/gpio/gpio-msm-v2.c
+++ b/drivers/gpio/gpio-msm-v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -9,50 +9,121 @@
  * 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 Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
  */
-#define pr_fmt(fmt) "%s: " fmt, __func__
-
 #include <linux/bitmap.h>
 #include <linux/bitops.h>
 #include <linux/gpio.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
-#include <linux/io.h>
 #include <linux/irq.h>
+#include <linux/io.h>
 #include <linux/module.h>
-#include <linux/platform_device.h>
 #include <linux/spinlock.h>
+#include <linux/syscore_ops.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
+#include <linux/err.h>
 
 #include <asm/mach/irq.h>
 
-#include <mach/msm_gpiomux.h>
 #include <mach/msm_iomap.h>
+#include <mach/gpiomux.h>
+#include <mach/mpm.h>
 
 /* Bits of interest in the GPIO_IN_OUT register.
  */
 enum {
-	GPIO_IN  = 0,
-	GPIO_OUT = 1
+	GPIO_IN_BIT  = 0,
+	GPIO_OUT_BIT = 1
 };
 
 /* Bits of interest in the GPIO_INTR_STATUS register.
  */
 enum {
-	INTR_STATUS = 0,
+	INTR_STATUS_BIT = 0,
 };
 
 /* Bits of interest in the GPIO_CFG register.
  */
 enum {
-	GPIO_OE = 9,
+	GPIO_OE_BIT = 9,
 };
 
 /* Bits of interest in the GPIO_INTR_CFG register.
+ */
+enum {
+	INTR_ENABLE_BIT        = 0,
+	INTR_POL_CTL_BIT       = 1,
+	INTR_DECT_CTL_BIT      = 2,
+	INTR_RAW_STATUS_EN_BIT = 3,
+};
+
+/* Codes of interest in GPIO_INTR_CFG_SU.
+ */
+enum {
+	TARGET_PROC_SCORPION = 4,
+	TARGET_PROC_NONE     = 7,
+};
+
+/*
+ * There is no 'DC_POLARITY_LO' because the GIC is incapable
+ * of asserting on falling edge or level-low conditions.  Even though
+ * the registers allow for low-polarity inputs, the case can never arise.
+ */
+enum {
+	DC_POLARITY_HI	= BIT(11),
+	DC_IRQ_ENABLE	= BIT(3),
+};
+
+enum msm_tlmm_register {
+	SDC4_HDRV_PULL_CTL = 0x20a0,
+	SDC3_HDRV_PULL_CTL = 0x20a4,
+	SDC1_HDRV_PULL_CTL = 0x20a0,
+};
+
+struct tlmm_field_cfg {
+	enum msm_tlmm_register reg;
+	u8                     off;
+};
+
+static const struct tlmm_field_cfg tlmm_hdrv_cfgs[] = {
+	{SDC4_HDRV_PULL_CTL, 6}, /* TLMM_HDRV_SDC4_CLK  */
+	{SDC4_HDRV_PULL_CTL, 3}, /* TLMM_HDRV_SDC4_CMD  */
+	{SDC4_HDRV_PULL_CTL, 0}, /* TLMM_HDRV_SDC4_DATA */
+	{SDC3_HDRV_PULL_CTL, 6}, /* TLMM_HDRV_SDC3_CLK  */
+	{SDC3_HDRV_PULL_CTL, 3}, /* TLMM_HDRV_SDC3_CMD  */
+	{SDC3_HDRV_PULL_CTL, 0}, /* TLMM_HDRV_SDC3_DATA */
+	{SDC1_HDRV_PULL_CTL, 6}, /* TLMM_HDRV_SDC1_CLK  */
+	{SDC1_HDRV_PULL_CTL, 3}, /* TLMM_HDRV_SDC1_CMD  */
+	{SDC1_HDRV_PULL_CTL, 0}, /* TLMM_HDRV_SDC1_DATA */
+};
+
+static const struct tlmm_field_cfg tlmm_pull_cfgs[] = {
+	{SDC4_HDRV_PULL_CTL, 11}, /* TLMM_PULL_SDC4_CMD  */
+	{SDC4_HDRV_PULL_CTL, 9},  /* TLMM_PULL_SDC4_DATA */
+	{SDC3_HDRV_PULL_CTL, 14}, /* TLMM_PULL_SDC3_CLK  */
+	{SDC3_HDRV_PULL_CTL, 11}, /* TLMM_PULL_SDC3_CMD  */
+	{SDC3_HDRV_PULL_CTL, 9},  /* TLMM_PULL_SDC3_DATA */
+	{SDC1_HDRV_PULL_CTL, 13}, /* TLMM_PULL_SDC1_CLK  */
+	{SDC1_HDRV_PULL_CTL, 11}, /* TLMM_PULL_SDC1_CMD  */
+	{SDC1_HDRV_PULL_CTL, 9},  /* TLMM_PULL_SDC1_DATA */
+};
+
+/*
+ * Supported arch specific irq extension.
+ * Default make them NULL.
+ */
+struct irq_chip msm_gpio_irq_extn = {
+	.irq_eoi	= NULL,
+	.irq_mask	= NULL,
+	.irq_unmask	= NULL,
+	.irq_retrigger	= NULL,
+	.irq_set_type	= NULL,
+	.irq_set_wake	= NULL,
+	.irq_disable	= NULL,
+};
+
+/*
  * When a GPIO triggers, two separate decisions are made, controlled
  * by two separate flags.
  *
@@ -64,22 +135,13 @@
  * If INTR_ENABLE is set and INTR_RAW_STATUS_EN is NOT set, an interrupt
  * can be triggered but the status register will not reflect it.
  */
-enum {
-	INTR_ENABLE        = 0,
-	INTR_POL_CTL       = 1,
-	INTR_DECT_CTL      = 2,
-	INTR_RAW_STATUS_EN = 3,
-};
-
-/* Codes of interest in GPIO_INTR_CFG_SU.
- */
-enum {
-	TARGET_PROC_SCORPION = 4,
-	TARGET_PROC_NONE     = 7,
-};
-
+#define INTR_RAW_STATUS_EN BIT(INTR_RAW_STATUS_EN_BIT)
+#define INTR_ENABLE        BIT(INTR_ENABLE_BIT)
+#define INTR_DECT_CTL_EDGE BIT(INTR_DECT_CTL_BIT)
+#define INTR_POL_CTL_HI    BIT(INTR_POL_CTL_BIT)
 
 #define GPIO_INTR_CFG_SU(gpio)    (MSM_TLMM_BASE + 0x0400 + (0x04 * (gpio)))
+#define DIR_CONN_INTR_CFG_SU(irq) (MSM_TLMM_BASE + 0x0700 + (0x04 * (irq)))
 #define GPIO_CONFIG(gpio)         (MSM_TLMM_BASE + 0x1000 + (0x10 * (gpio)))
 #define GPIO_IN_OUT(gpio)         (MSM_TLMM_BASE + 0x1004 + (0x10 * (gpio)))
 #define GPIO_INTR_CFG(gpio)       (MSM_TLMM_BASE + 0x1008 + (0x10 * (gpio)))
@@ -90,7 +152,7 @@
  *
  * @enabled_irqs: a bitmap used to optimize the summary-irq handler.  By
  * keeping track of which gpios are unmasked as irq sources, we avoid
- * having to do readl calls on hundreds of iomapped registers each time
+ * having to do __raw_readl calls on hundreds of iomapped registers each time
  * the summary interrupt fires in order to locate the active interrupts.
  *
  * @wake_irqs: a bitmap for tracking which interrupt lines are enabled
@@ -103,9 +165,10 @@
  */
 struct msm_gpio_dev {
 	struct gpio_chip gpio_chip;
-	DECLARE_BITMAP(enabled_irqs, NR_GPIO_IRQS);
-	DECLARE_BITMAP(wake_irqs, NR_GPIO_IRQS);
-	DECLARE_BITMAP(dual_edge_irqs, NR_GPIO_IRQS);
+	DECLARE_BITMAP(enabled_irqs, NR_MSM_GPIOS);
+	DECLARE_BITMAP(wake_irqs, NR_MSM_GPIOS);
+	DECLARE_BITMAP(dual_edge_irqs, NR_MSM_GPIOS);
+	struct irq_domain domain;
 };
 
 static DEFINE_SPINLOCK(tlmm_lock);
@@ -117,22 +180,26 @@
 
 static inline void set_gpio_bits(unsigned n, void __iomem *reg)
 {
-	writel(readl(reg) | n, reg);
+	__raw_writel(__raw_readl(reg) | n, reg);
 }
 
-static inline void clear_gpio_bits(unsigned n, void __iomem *reg)
+static inline void clr_gpio_bits(unsigned n, void __iomem *reg)
 {
-	writel(readl(reg) & ~n, reg);
+	__raw_writel(__raw_readl(reg) & ~n, reg);
 }
 
 static int msm_gpio_get(struct gpio_chip *chip, unsigned offset)
 {
-	return readl(GPIO_IN_OUT(offset)) & BIT(GPIO_IN);
+	int rc;
+	rc = __raw_readl(GPIO_IN_OUT(offset)) & BIT(GPIO_IN_BIT);
+	mb();
+	return rc;
 }
 
 static void msm_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
 {
-	writel(val ? BIT(GPIO_OUT) : 0, GPIO_IN_OUT(offset));
+	__raw_writel(val ? BIT(GPIO_OUT_BIT) : 0, GPIO_IN_OUT(offset));
+	mb();
 }
 
 static int msm_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
@@ -140,7 +207,8 @@
 	unsigned long irq_flags;
 
 	spin_lock_irqsave(&tlmm_lock, irq_flags);
-	clear_gpio_bits(BIT(GPIO_OE), GPIO_CONFIG(offset));
+	clr_gpio_bits(BIT(GPIO_OE_BIT), GPIO_CONFIG(offset));
+	mb();
 	spin_unlock_irqrestore(&tlmm_lock, irq_flags);
 	return 0;
 }
@@ -153,11 +221,38 @@
 
 	spin_lock_irqsave(&tlmm_lock, irq_flags);
 	msm_gpio_set(chip, offset, val);
-	set_gpio_bits(BIT(GPIO_OE), GPIO_CONFIG(offset));
+	set_gpio_bits(BIT(GPIO_OE_BIT), GPIO_CONFIG(offset));
+	mb();
 	spin_unlock_irqrestore(&tlmm_lock, irq_flags);
 	return 0;
 }
 
+#ifdef CONFIG_OF
+static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	struct msm_gpio_dev *g_dev = to_msm_gpio_dev(chip);
+	struct irq_domain *domain = &g_dev->domain;
+	return domain->irq_base + (offset - chip->base);
+}
+
+static inline int msm_irq_to_gpio(struct gpio_chip *chip, unsigned irq)
+{
+	struct msm_gpio_dev *g_dev = to_msm_gpio_dev(chip);
+	struct irq_domain *domain = &g_dev->domain;
+	return irq - domain->irq_base;
+}
+#else
+static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	return MSM_GPIO_TO_INT(offset - chip->base);
+}
+
+static inline int msm_irq_to_gpio(struct gpio_chip *chip, unsigned irq)
+{
+	return irq - MSM_GPIO_TO_INT(chip->base);
+}
+#endif
+
 static int msm_gpio_request(struct gpio_chip *chip, unsigned offset)
 {
 	return msm_gpiomux_get(chip->base + offset);
@@ -168,20 +263,11 @@
 	msm_gpiomux_put(chip->base + offset);
 }
 
-static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
-{
-	return MSM_GPIO_TO_INT(chip->base + offset);
-}
-
-static inline int msm_irq_to_gpio(struct gpio_chip *chip, unsigned irq)
-{
-	return irq - MSM_GPIO_TO_INT(chip->base);
-}
-
 static struct msm_gpio_dev msm_gpio = {
 	.gpio_chip = {
+		.label		  = "msmgpio",
 		.base             = 0,
-		.ngpio            = NR_GPIO_IRQS,
+		.ngpio            = NR_MSM_GPIOS,
 		.direction_input  = msm_gpio_direction_input,
 		.direction_output = msm_gpio_direction_output,
 		.get              = msm_gpio_get,
@@ -192,6 +278,18 @@
 	},
 };
 
+static void switch_mpm_config(struct irq_data *d, unsigned val)
+{
+	/* switch the configuration in the mpm as well */
+	if (!msm_gpio_irq_extn.irq_set_type)
+		return;
+
+	if (val)
+		msm_gpio_irq_extn.irq_set_type(d, IRQF_TRIGGER_FALLING);
+	else
+		msm_gpio_irq_extn.irq_set_type(d, IRQF_TRIGGER_RISING);
+}
+
 /* For dual-edge interrupts in software, since the hardware has no
  * such support:
  *
@@ -212,34 +310,44 @@
  *
  * Algorithm comes from Google's msmgpio driver, see mach-msm/gpio.c.
  */
-static void msm_gpio_update_dual_edge_pos(unsigned gpio)
+static void msm_gpio_update_dual_edge_pos(struct irq_data *d, unsigned gpio)
 {
 	int loop_limit = 100;
 	unsigned val, val2, intstat;
 
 	do {
-		val = readl(GPIO_IN_OUT(gpio)) & BIT(GPIO_IN);
+		val = __raw_readl(GPIO_IN_OUT(gpio)) & BIT(GPIO_IN_BIT);
 		if (val)
-			clear_gpio_bits(BIT(INTR_POL_CTL), GPIO_INTR_CFG(gpio));
+			clr_gpio_bits(INTR_POL_CTL_HI, GPIO_INTR_CFG(gpio));
 		else
-			set_gpio_bits(BIT(INTR_POL_CTL), GPIO_INTR_CFG(gpio));
-		val2 = readl(GPIO_IN_OUT(gpio)) & BIT(GPIO_IN);
-		intstat = readl(GPIO_INTR_STATUS(gpio)) & BIT(INTR_STATUS);
-		if (intstat || val == val2)
+			set_gpio_bits(INTR_POL_CTL_HI, GPIO_INTR_CFG(gpio));
+		val2 = __raw_readl(GPIO_IN_OUT(gpio)) & BIT(GPIO_IN_BIT);
+		intstat = __raw_readl(GPIO_INTR_STATUS(gpio)) &
+					BIT(INTR_STATUS_BIT);
+		if (intstat || val == val2) {
+			switch_mpm_config(d, val);
 			return;
+		}
 	} while (loop_limit-- > 0);
-	pr_err("dual-edge irq failed to stabilize, "
+	pr_err("%s: dual-edge irq failed to stabilize, "
 	       "interrupts dropped. %#08x != %#08x\n",
-	       val, val2);
+	       __func__, val, val2);
 }
 
 static void msm_gpio_irq_ack(struct irq_data *d)
 {
 	int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
 
-	writel(BIT(INTR_STATUS), GPIO_INTR_STATUS(gpio));
+	__raw_writel(BIT(INTR_STATUS_BIT), GPIO_INTR_STATUS(gpio));
 	if (test_bit(gpio, msm_gpio.dual_edge_irqs))
-		msm_gpio_update_dual_edge_pos(gpio);
+		msm_gpio_update_dual_edge_pos(d, gpio);
+	mb();
+}
+
+static void __msm_gpio_irq_mask(unsigned int gpio)
+{
+	__raw_writel(TARGET_PROC_NONE, GPIO_INTR_CFG_SU(gpio));
+	clr_gpio_bits(INTR_RAW_STATUS_EN | INTR_ENABLE, GPIO_INTR_CFG(gpio));
 }
 
 static void msm_gpio_irq_mask(struct irq_data *d)
@@ -248,10 +356,20 @@
 	unsigned long irq_flags;
 
 	spin_lock_irqsave(&tlmm_lock, irq_flags);
-	writel(TARGET_PROC_NONE, GPIO_INTR_CFG_SU(gpio));
-	clear_gpio_bits(INTR_RAW_STATUS_EN | INTR_ENABLE, GPIO_INTR_CFG(gpio));
+	__msm_gpio_irq_mask(gpio);
 	__clear_bit(gpio, msm_gpio.enabled_irqs);
+	mb();
 	spin_unlock_irqrestore(&tlmm_lock, irq_flags);
+
+	if (msm_gpio_irq_extn.irq_mask)
+		msm_gpio_irq_extn.irq_mask(d);
+
+}
+
+static void __msm_gpio_irq_unmask(unsigned int gpio)
+{
+	set_gpio_bits(INTR_RAW_STATUS_EN | INTR_ENABLE, GPIO_INTR_CFG(gpio));
+	__raw_writel(TARGET_PROC_SCORPION, GPIO_INTR_CFG_SU(gpio));
 }
 
 static void msm_gpio_irq_unmask(struct irq_data *d)
@@ -261,9 +379,18 @@
 
 	spin_lock_irqsave(&tlmm_lock, irq_flags);
 	__set_bit(gpio, msm_gpio.enabled_irqs);
-	set_gpio_bits(INTR_RAW_STATUS_EN | INTR_ENABLE, GPIO_INTR_CFG(gpio));
-	writel(TARGET_PROC_SCORPION, GPIO_INTR_CFG_SU(gpio));
+	__msm_gpio_irq_unmask(gpio);
+	mb();
 	spin_unlock_irqrestore(&tlmm_lock, irq_flags);
+
+	if (msm_gpio_irq_extn.irq_mask)
+		msm_gpio_irq_extn.irq_unmask(d);
+}
+
+static void msm_gpio_irq_disable(struct irq_data *d)
+{
+	if (msm_gpio_irq_extn.irq_disable)
+		msm_gpio_irq_extn.irq_disable(d);
 }
 
 static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int flow_type)
@@ -274,33 +401,37 @@
 
 	spin_lock_irqsave(&tlmm_lock, irq_flags);
 
-	bits = readl(GPIO_INTR_CFG(gpio));
+	bits = __raw_readl(GPIO_INTR_CFG(gpio));
 
 	if (flow_type & IRQ_TYPE_EDGE_BOTH) {
-		bits |= BIT(INTR_DECT_CTL);
+		bits |= INTR_DECT_CTL_EDGE;
 		__irq_set_handler_locked(d->irq, handle_edge_irq);
 		if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
 			__set_bit(gpio, msm_gpio.dual_edge_irqs);
 		else
 			__clear_bit(gpio, msm_gpio.dual_edge_irqs);
 	} else {
-		bits &= ~BIT(INTR_DECT_CTL);
+		bits &= ~INTR_DECT_CTL_EDGE;
 		__irq_set_handler_locked(d->irq, handle_level_irq);
 		__clear_bit(gpio, msm_gpio.dual_edge_irqs);
 	}
 
 	if (flow_type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_HIGH))
-		bits |= BIT(INTR_POL_CTL);
+		bits |= INTR_POL_CTL_HI;
 	else
-		bits &= ~BIT(INTR_POL_CTL);
+		bits &= ~INTR_POL_CTL_HI;
 
-	writel(bits, GPIO_INTR_CFG(gpio));
+	__raw_writel(bits, GPIO_INTR_CFG(gpio));
 
 	if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
-		msm_gpio_update_dual_edge_pos(gpio);
+		msm_gpio_update_dual_edge_pos(d, gpio);
 
+	mb();
 	spin_unlock_irqrestore(&tlmm_lock, irq_flags);
 
+	if (msm_gpio_irq_extn.irq_set_type)
+		msm_gpio_irq_extn.irq_set_type(d, flow_type);
+
 	return 0;
 }
 
@@ -310,22 +441,24 @@
  * which have been set as summary IRQ lines and which are triggered,
  * and to call their interrupt handlers.
  */
-static void msm_summary_irq_handler(unsigned int irq, struct irq_desc *desc)
+static irqreturn_t msm_summary_irq_handler(int irq, void *data)
 {
 	unsigned long i;
+	struct irq_desc *desc = irq_to_desc(irq);
 	struct irq_chip *chip = irq_desc_get_chip(desc);
 
 	chained_irq_enter(chip, desc);
 
-	for (i = find_first_bit(msm_gpio.enabled_irqs, NR_GPIO_IRQS);
-	     i < NR_GPIO_IRQS;
-	     i = find_next_bit(msm_gpio.enabled_irqs, NR_GPIO_IRQS, i + 1)) {
-		if (readl(GPIO_INTR_STATUS(i)) & BIT(INTR_STATUS))
+	for (i = find_first_bit(msm_gpio.enabled_irqs, NR_MSM_GPIOS);
+	     i < NR_MSM_GPIOS;
+	     i = find_next_bit(msm_gpio.enabled_irqs, NR_MSM_GPIOS, i + 1)) {
+		if (__raw_readl(GPIO_INTR_STATUS(i)) & BIT(INTR_STATUS_BIT))
 			generic_handle_irq(msm_gpio_to_irq(&msm_gpio.gpio_chip,
 							   i));
 	}
 
 	chained_irq_exit(chip, desc);
+	return IRQ_HANDLED;
 }
 
 static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
@@ -333,15 +466,18 @@
 	int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
 
 	if (on) {
-		if (bitmap_empty(msm_gpio.wake_irqs, NR_GPIO_IRQS))
-			irq_set_irq_wake(TLMM_SCSS_SUMMARY_IRQ, 1);
+		if (bitmap_empty(msm_gpio.wake_irqs, NR_MSM_GPIOS))
+			irq_set_irq_wake(TLMM_MSM_SUMMARY_IRQ, 1);
 		set_bit(gpio, msm_gpio.wake_irqs);
 	} else {
 		clear_bit(gpio, msm_gpio.wake_irqs);
-		if (bitmap_empty(msm_gpio.wake_irqs, NR_GPIO_IRQS))
-			irq_set_irq_wake(TLMM_SCSS_SUMMARY_IRQ, 0);
+		if (bitmap_empty(msm_gpio.wake_irqs, NR_MSM_GPIOS))
+			irq_set_irq_wake(TLMM_MSM_SUMMARY_IRQ, 0);
 	}
 
+	if (msm_gpio_irq_extn.irq_set_wake)
+		msm_gpio_irq_extn.irq_set_wake(d, on);
+
 	return 0;
 }
 
@@ -352,82 +488,270 @@
 	.irq_ack	= msm_gpio_irq_ack,
 	.irq_set_type	= msm_gpio_irq_set_type,
 	.irq_set_wake	= msm_gpio_irq_set_wake,
+	.irq_disable	= msm_gpio_irq_disable,
 };
 
-static int __devinit msm_gpio_probe(struct platform_device *dev)
+/*
+ * This lock class tells lockdep that GPIO irqs are in a different
+ * category than their parent, so it won't report false recursion.
+ */
+static struct lock_class_key msm_gpio_lock_class;
+
+static int __devinit msm_gpio_probe(void)
 {
 	int i, irq, ret;
 
-	bitmap_zero(msm_gpio.enabled_irqs, NR_GPIO_IRQS);
-	bitmap_zero(msm_gpio.wake_irqs, NR_GPIO_IRQS);
-	bitmap_zero(msm_gpio.dual_edge_irqs, NR_GPIO_IRQS);
-	msm_gpio.gpio_chip.label = dev->name;
+	spin_lock_init(&tlmm_lock);
+	bitmap_zero(msm_gpio.enabled_irqs, NR_MSM_GPIOS);
+	bitmap_zero(msm_gpio.wake_irqs, NR_MSM_GPIOS);
+	bitmap_zero(msm_gpio.dual_edge_irqs, NR_MSM_GPIOS);
 	ret = gpiochip_add(&msm_gpio.gpio_chip);
 	if (ret < 0)
 		return ret;
 
 	for (i = 0; i < msm_gpio.gpio_chip.ngpio; ++i) {
 		irq = msm_gpio_to_irq(&msm_gpio.gpio_chip, i);
+		irq_set_lockdep_class(irq, &msm_gpio_lock_class);
 		irq_set_chip_and_handler(irq, &msm_gpio_irq_chip,
 					 handle_level_irq);
 		set_irq_flags(irq, IRQF_VALID);
 	}
 
-	irq_set_chained_handler(TLMM_SCSS_SUMMARY_IRQ,
-				msm_summary_irq_handler);
+	ret = request_irq(TLMM_MSM_SUMMARY_IRQ, msm_summary_irq_handler,
+			IRQF_TRIGGER_HIGH, "msmgpio", NULL);
+	if (ret) {
+		pr_err("Request_irq failed for TLMM_MSM_SUMMARY_IRQ - %d\n",
+				ret);
+		return ret;
+	}
 	return 0;
 }
 
-static int __devexit msm_gpio_remove(struct platform_device *dev)
+static int __devexit msm_gpio_remove(void)
 {
 	int ret = gpiochip_remove(&msm_gpio.gpio_chip);
 
 	if (ret < 0)
 		return ret;
 
-	irq_set_handler(TLMM_SCSS_SUMMARY_IRQ, NULL);
+	irq_set_handler(TLMM_MSM_SUMMARY_IRQ, NULL);
 
 	return 0;
 }
 
-static struct platform_driver msm_gpio_driver = {
-	.probe = msm_gpio_probe,
-	.remove = __devexit_p(msm_gpio_remove),
-	.driver = {
-		.name = "msmgpio",
-		.owner = THIS_MODULE,
-	},
-};
+#ifdef CONFIG_PM
+static int msm_gpio_suspend(void)
+{
+	unsigned long irq_flags;
+	unsigned long i;
 
-static struct platform_device msm_device_gpio = {
-	.name = "msmgpio",
-	.id   = -1,
+	spin_lock_irqsave(&tlmm_lock, irq_flags);
+	for_each_set_bit(i, msm_gpio.enabled_irqs, NR_MSM_GPIOS)
+			__msm_gpio_irq_mask(i);
+
+	for_each_set_bit(i, msm_gpio.wake_irqs, NR_MSM_GPIOS)
+		__msm_gpio_irq_unmask(i);
+	mb();
+	spin_unlock_irqrestore(&tlmm_lock, irq_flags);
+	return 0;
+}
+
+extern int msm_show_resume_irq_mask;
+
+void msm_gpio_show_resume_irq(void)
+{
+	unsigned long irq_flags;
+	int i, irq, intstat;
+
+	if (!msm_show_resume_irq_mask)
+		return;
+
+	spin_lock_irqsave(&tlmm_lock, irq_flags);
+	for_each_set_bit(i, msm_gpio.wake_irqs, NR_MSM_GPIOS) {
+		intstat = __raw_readl(GPIO_INTR_STATUS(i)) &
+					BIT(INTR_STATUS_BIT);
+		if (intstat) {
+			irq = msm_gpio_to_irq(&msm_gpio.gpio_chip, i);
+			pr_warning("%s: %d triggered\n",
+				__func__, irq);
+		}
+	}
+	spin_unlock_irqrestore(&tlmm_lock, irq_flags);
+}
+
+static void msm_gpio_resume(void)
+{
+	unsigned long irq_flags;
+	unsigned long i;
+
+	msm_gpio_show_resume_irq();
+
+	spin_lock_irqsave(&tlmm_lock, irq_flags);
+	for_each_set_bit(i, msm_gpio.wake_irqs, NR_MSM_GPIOS)
+		__msm_gpio_irq_mask(i);
+
+	for_each_set_bit(i, msm_gpio.enabled_irqs, NR_MSM_GPIOS)
+		__msm_gpio_irq_unmask(i);
+	mb();
+	spin_unlock_irqrestore(&tlmm_lock, irq_flags);
+}
+#else
+#define msm_gpio_suspend NULL
+#define msm_gpio_resume NULL
+#endif
+
+static struct syscore_ops msm_gpio_syscore_ops = {
+	.suspend = msm_gpio_suspend,
+	.resume = msm_gpio_resume,
 };
 
 static int __init msm_gpio_init(void)
 {
-	int rc;
-
-	rc = platform_driver_register(&msm_gpio_driver);
-	if (!rc) {
-		rc = platform_device_register(&msm_device_gpio);
-		if (rc)
-			platform_driver_unregister(&msm_gpio_driver);
-	}
-
-	return rc;
+	msm_gpio_probe();
+	register_syscore_ops(&msm_gpio_syscore_ops);
+	return 0;
 }
 
 static void __exit msm_gpio_exit(void)
 {
-	platform_device_unregister(&msm_device_gpio);
-	platform_driver_unregister(&msm_gpio_driver);
+	unregister_syscore_ops(&msm_gpio_syscore_ops);
+	msm_gpio_remove();
 }
 
 postcore_initcall(msm_gpio_init);
 module_exit(msm_gpio_exit);
 
+static void msm_tlmm_set_field(const struct tlmm_field_cfg *configs,
+			       unsigned id, unsigned width, unsigned val)
+{
+	unsigned long irqflags;
+	u32 mask = (1 << width) - 1;
+	u32 __iomem *reg = MSM_TLMM_BASE + configs[id].reg;
+	u32 reg_val;
+
+	spin_lock_irqsave(&tlmm_lock, irqflags);
+	reg_val = __raw_readl(reg);
+	reg_val &= ~(mask << configs[id].off);
+	reg_val |= (val & mask) << configs[id].off;
+	__raw_writel(reg_val, reg);
+	mb();
+	spin_unlock_irqrestore(&tlmm_lock, irqflags);
+}
+
+void msm_tlmm_set_hdrive(enum msm_tlmm_hdrive_tgt tgt, int drv_str)
+{
+	msm_tlmm_set_field(tlmm_hdrv_cfgs, tgt, 3, drv_str);
+}
+EXPORT_SYMBOL(msm_tlmm_set_hdrive);
+
+void msm_tlmm_set_pull(enum msm_tlmm_pull_tgt tgt, int pull)
+{
+	msm_tlmm_set_field(tlmm_pull_cfgs, tgt, 2, pull);
+}
+EXPORT_SYMBOL(msm_tlmm_set_pull);
+
+int gpio_tlmm_config(unsigned config, unsigned disable)
+{
+	uint32_t flags;
+	unsigned gpio = GPIO_PIN(config);
+
+	if (gpio > NR_MSM_GPIOS)
+		return -EINVAL;
+
+	flags = ((GPIO_DIR(config) << 9) & (0x1 << 9)) |
+		((GPIO_DRVSTR(config) << 6) & (0x7 << 6)) |
+		((GPIO_FUNC(config) << 2) & (0xf << 2)) |
+		((GPIO_PULL(config) & 0x3));
+	__raw_writel(flags, GPIO_CONFIG(gpio));
+	mb();
+
+	return 0;
+}
+EXPORT_SYMBOL(gpio_tlmm_config);
+
+int msm_gpio_install_direct_irq(unsigned gpio, unsigned irq,
+					unsigned int input_polarity)
+{
+	unsigned long irq_flags;
+	uint32_t bits;
+
+	if (gpio >= NR_MSM_GPIOS || irq >= NR_TLMM_MSM_DIR_CONN_IRQ)
+		return -EINVAL;
+
+	spin_lock_irqsave(&tlmm_lock, irq_flags);
+
+	__raw_writel(__raw_readl(GPIO_CONFIG(gpio)) | BIT(GPIO_OE_BIT),
+		GPIO_CONFIG(gpio));
+	__raw_writel(__raw_readl(GPIO_INTR_CFG(gpio)) &
+		~(INTR_RAW_STATUS_EN | INTR_ENABLE),
+		GPIO_INTR_CFG(gpio));
+	__raw_writel(DC_IRQ_ENABLE | TARGET_PROC_NONE,
+		GPIO_INTR_CFG_SU(gpio));
+
+	bits = TARGET_PROC_SCORPION | (gpio << 3);
+	if (input_polarity)
+		bits |= DC_POLARITY_HI;
+	__raw_writel(bits, DIR_CONN_INTR_CFG_SU(irq));
+
+	mb();
+	spin_unlock_irqrestore(&tlmm_lock, irq_flags);
+
+	return 0;
+}
+EXPORT_SYMBOL(msm_gpio_install_direct_irq);
+
+#ifdef CONFIG_OF
+static int msm_gpio_domain_dt_translate(struct irq_domain *d,
+					struct device_node *controller,
+					const u32 *intspec,
+					unsigned int intsize,
+					unsigned long *out_hwirq,
+					unsigned int *out_type)
+{
+	if (d->of_node != controller)
+		return -EINVAL;
+	if (intsize != 2)
+		return -EINVAL;
+
+	/* hwirq value */
+	*out_hwirq = intspec[0];
+
+	/* irq flags */
+	*out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
+	return 0;
+}
+
+static struct irq_domain_ops msm_gpio_irq_domain_ops = {
+	.dt_translate = msm_gpio_domain_dt_translate,
+};
+
+int __init msm_gpio_of_init(struct device_node *node,
+			    struct device_node *parent)
+{
+	struct irq_domain *domain = &msm_gpio.domain;
+	int start;
+
+	start = irq_domain_find_free_range(0, NR_MSM_GPIOS);
+	domain->irq_base = irq_alloc_descs(start, 0, NR_MSM_GPIOS,
+							numa_node_id());
+	if (IS_ERR_VALUE(domain->irq_base)) {
+		WARN(1, "Cannot allocate irq_descs @ IRQ%d\n", start);
+		return domain->irq_base;
+	}
+
+	domain->irq_base = irq_domain_find_free_range(0, NR_MSM_GPIOS);
+	domain->nr_irq = NR_MSM_GPIOS;
+	domain->of_node = of_node_get(node);
+	domain->priv = &msm_gpio;
+	domain->ops = &msm_gpio_irq_domain_ops;
+	irq_domain_add(domain);
+	pr_debug("%s: irq_base = %u\n", __func__, domain->irq_base);
+
+	return 0;
+}
+#endif
+
 MODULE_AUTHOR("Gregory Bean <gbean@codeaurora.org>");
 MODULE_DESCRIPTION("Driver for Qualcomm MSM TLMMv2 SoC GPIOs");
 MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:msmgpio");
+MODULE_ALIAS("sysdev:msmgpio");
diff --git a/drivers/gpio/gpio-pm8xxx-rpc.c b/drivers/gpio/gpio-pm8xxx-rpc.c
new file mode 100644
index 0000000..1acc741
--- /dev/null
+++ b/drivers/gpio/gpio-pm8xxx-rpc.c
@@ -0,0 +1,241 @@
+/*
+ * Qualcomm PMIC8XXX GPIO driver based on RPC
+ *
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt)	"%s: " fmt, __func__
+
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/gpio-pm8xxx-rpc.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <mach/pmic.h>
+
+struct pm8xxx_gpio_rpc_chip {
+	struct list_head	link;
+	struct gpio_chip	gpio_chip;
+};
+
+static LIST_HEAD(pm8xxx_gpio_rpc_chips);
+static DEFINE_MUTEX(pm8xxx_gpio_chips_lock);
+
+static int pm8xxx_gpio_rpc_get(struct pm8xxx_gpio_rpc_chip *pm8xxx_gpio_chip,
+								unsigned gpio)
+{
+	int rc;
+
+	if (gpio >= pm8xxx_gpio_chip->gpio_chip.ngpio
+					|| pm8xxx_gpio_chip == NULL)
+		return -EINVAL;
+
+	rc =  pmic_gpio_get_value(gpio);
+
+	return rc;
+}
+
+static int pm8xxx_gpio_rpc_set(struct pm8xxx_gpio_rpc_chip *pm8xxx_gpio_chip,
+						 unsigned gpio, int value)
+{
+	int rc;
+
+	if (gpio >= pm8xxx_gpio_chip->gpio_chip.ngpio ||
+					pm8xxx_gpio_chip == NULL)
+		return -EINVAL;
+
+	rc = pmic_gpio_set_value(gpio, value);
+
+	return rc;
+}
+
+static int pm8xxx_gpio_rpc_set_direction(struct pm8xxx_gpio_rpc_chip
+			*pm8xxx_gpio_chip, unsigned gpio, int direction)
+{
+	int rc = 0;
+
+	if (!direction || pm8xxx_gpio_chip == NULL)
+		return -EINVAL;
+
+	if (direction ==  PM_GPIO_DIR_IN)
+		rc = pmic_gpio_direction_input(gpio);
+	else if (direction == PM_GPIO_DIR_OUT)
+		rc = pmic_gpio_direction_output(gpio);
+
+	return rc;
+}
+
+static int pm8xxx_gpio_rpc_read(struct gpio_chip *gpio_chip, unsigned offset)
+{
+	struct pm8xxx_gpio_rpc_chip *pm8xxx_gpio_chip =
+					dev_get_drvdata(gpio_chip->dev);
+
+	return pm8xxx_gpio_rpc_get(pm8xxx_gpio_chip, offset);
+}
+
+static void pm8xxx_gpio_rpc_write(struct gpio_chip *gpio_chip,
+						unsigned offset, int val)
+{
+	struct pm8xxx_gpio_rpc_chip *pm8xxx_gpio_chip =
+					dev_get_drvdata(gpio_chip->dev);
+
+	pm8xxx_gpio_rpc_set(pm8xxx_gpio_chip, offset, !!val);
+}
+
+static int pm8xxx_gpio_rpc_direction_input(struct gpio_chip *gpio_chip,
+							unsigned offset)
+{
+	struct pm8xxx_gpio_rpc_chip *pm8xxx_gpio_chip =
+					dev_get_drvdata(gpio_chip->dev);
+
+	return pm8xxx_gpio_rpc_set_direction(pm8xxx_gpio_chip, offset,
+							PM_GPIO_DIR_IN);
+}
+
+static int pm8xxx_gpio_rpc_direction_output(struct gpio_chip *gpio_chip,
+						unsigned offset, int val)
+{
+	int ret = 0;
+
+	struct pm8xxx_gpio_rpc_chip *pm8xxx_gpio_chip =
+					dev_get_drvdata(gpio_chip->dev);
+
+	ret = pm8xxx_gpio_rpc_set_direction(pm8xxx_gpio_chip, offset,
+							PM_GPIO_DIR_OUT);
+	if (!ret)
+		ret = pm8xxx_gpio_rpc_set(pm8xxx_gpio_chip, offset, !!val);
+
+	return ret;
+}
+
+static void pm8xxx_gpio_rpc_dbg_show(struct seq_file *s, struct gpio_chip
+								*gpio_chip)
+{
+	struct pm8xxx_gpio_rpc_chip *pmxx_gpio_chip =
+					dev_get_drvdata(gpio_chip->dev);
+	u8 state, mode;
+	const char *label;
+	int i;
+
+	for (i = 0; i < gpio_chip->ngpio; i++) {
+		label = gpiochip_is_requested(gpio_chip, i);
+		state = pm8xxx_gpio_rpc_get(pmxx_gpio_chip, i);
+		mode =  pmic_gpio_get_direction(i);
+		seq_printf(s, "gpio-%-3d (%-12.12s) %s %s",
+				gpio_chip->base + i,
+				label ? label : " ", mode ? "out" : "in",
+				state ? "hi" : "lo");
+		seq_printf(s, "\n");
+	}
+}
+
+static int __devinit pm8xxx_gpio_rpc_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct pm8xxx_gpio_rpc_chip *pm8xxx_gpio_chip;
+	const struct pm8xxx_gpio_rpc_platform_data *pdata =
+					pdev->dev.platform_data;
+
+	if (!pdata) {
+		pr_err("missing platform data\n");
+		return -EINVAL;
+	}
+
+	pm8xxx_gpio_chip = kzalloc(sizeof(struct pm8xxx_gpio_rpc_chip),
+								GFP_KERNEL);
+	if (!pm8xxx_gpio_chip) {
+		pr_err("Cannot allocate pm8xxx_gpio_chip\n");
+		return -ENOMEM;
+	}
+
+	pm8xxx_gpio_chip->gpio_chip.label = "pm8xxx-gpio-rpc";
+	pm8xxx_gpio_chip->gpio_chip.direction_input	=
+					pm8xxx_gpio_rpc_direction_input;
+	pm8xxx_gpio_chip->gpio_chip.direction_output	=
+					pm8xxx_gpio_rpc_direction_output;
+	pm8xxx_gpio_chip->gpio_chip.get		= pm8xxx_gpio_rpc_read;
+	pm8xxx_gpio_chip->gpio_chip.set		= pm8xxx_gpio_rpc_write;
+	pm8xxx_gpio_chip->gpio_chip.dbg_show	= pm8xxx_gpio_rpc_dbg_show;
+	pm8xxx_gpio_chip->gpio_chip.ngpio	= pdata->ngpios;
+	pm8xxx_gpio_chip->gpio_chip.can_sleep	= 1;
+	pm8xxx_gpio_chip->gpio_chip.dev		= &pdev->dev;
+	pm8xxx_gpio_chip->gpio_chip.base	= pdata->gpio_base;
+
+	mutex_lock(&pm8xxx_gpio_chips_lock);
+	list_add(&pm8xxx_gpio_chip->link, &pm8xxx_gpio_rpc_chips);
+	mutex_unlock(&pm8xxx_gpio_chips_lock);
+	platform_set_drvdata(pdev, pm8xxx_gpio_chip);
+
+	ret = gpiochip_add(&pm8xxx_gpio_chip->gpio_chip);
+	if (ret) {
+		pr_err("gpiochip_add failed ret = %d\n", ret);
+		goto reset_drvdata;
+	}
+
+	pr_info("OK: base=%d, ngpio=%d\n", pm8xxx_gpio_chip->gpio_chip.base,
+		pm8xxx_gpio_chip->gpio_chip.ngpio);
+
+	return 0;
+
+reset_drvdata:
+	mutex_lock(&pm8xxx_gpio_chips_lock);
+	list_del(&pm8xxx_gpio_chip->link);
+	mutex_unlock(&pm8xxx_gpio_chips_lock);
+	platform_set_drvdata(pdev, NULL);
+	kfree(pm8xxx_gpio_chip);
+	mutex_destroy(&pm8xxx_gpio_chips_lock);
+	return ret;
+}
+
+static int __devexit pm8xxx_gpio_rpc_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_gpio_rpc_chip *pm8xxx_gpio_chip =
+						platform_get_drvdata(pdev);
+
+	mutex_lock(&pm8xxx_gpio_chips_lock);
+	list_del(&pm8xxx_gpio_chip->link);
+	mutex_unlock(&pm8xxx_gpio_chips_lock);
+	platform_set_drvdata(pdev, NULL);
+	if (gpiochip_remove(&pm8xxx_gpio_chip->gpio_chip))
+		pr_err("failed to remove gpio chip\n");
+	kfree(pm8xxx_gpio_chip);
+	mutex_destroy(&pm8xxx_gpio_chips_lock);
+	return 0;
+}
+
+static struct platform_driver pm8xxx_gpio_rpc_driver = {
+	.probe		= pm8xxx_gpio_rpc_probe,
+	.remove		= __devexit_p(pm8xxx_gpio_rpc_remove),
+	.driver		= {
+		.name	= PM8XXX_GPIO_DEV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init pm8xxx_gpio_rpc_init(void)
+{
+	return platform_driver_register(&pm8xxx_gpio_rpc_driver);
+}
+postcore_initcall(pm8xxx_gpio_rpc_init);
+
+static void __exit pm8xxx_gpio_rpc_exit(void)
+{
+	platform_driver_unregister(&pm8xxx_gpio_rpc_driver);
+}
+module_exit(pm8xxx_gpio_rpc_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC GPIO driver based on RPC");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8XXX_GPIO_DEV_NAME);
diff --git a/drivers/gpio/gpio-sx150x.c b/drivers/gpio/gpio-sx150x.c
index a4f7353..93b94bd 100644
--- a/drivers/gpio/gpio-sx150x.c
+++ b/drivers/gpio/gpio-sx150x.c
@@ -8,11 +8,6 @@
  * 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 Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
  */
 #include <linux/gpio.h>
 #include <linux/i2c.h>
@@ -189,9 +184,9 @@
 	return err;
 }
 
-static void sx150x_set_oscio(struct sx150x_chip *chip, int val)
+static s32 sx150x_set_oscio(struct sx150x_chip *chip, int val)
 {
-	sx150x_i2c_write(chip->client,
+	return sx150x_i2c_write(chip->client,
 			chip->dev_cfg->reg_clock,
 			(val ? 0x1f : 0x10));
 }
@@ -286,11 +281,13 @@
 
 	chip = container_of(gc, struct sx150x_chip, gpio_chip);
 
-	if (!offset_is_oscio(chip, offset)) {
-		mutex_lock(&chip->lock);
+	mutex_lock(&chip->lock);
+	if (offset_is_oscio(chip, offset))
+		status = sx150x_set_oscio(chip, val);
+	else
 		status = sx150x_io_output(chip, offset, val);
-		mutex_unlock(&chip->lock);
-	}
+	mutex_unlock(&chip->lock);
+
 	return status;
 }
 
diff --git a/drivers/gpio/pm8xxx-gpio.c b/drivers/gpio/pm8xxx-gpio.c
new file mode 100644
index 0000000..cb874e8
--- /dev/null
+++ b/drivers/gpio/pm8xxx-gpio.c
@@ -0,0 +1,462 @@
+/*
+ * Qualcomm PMIC8XXX GPIO driver
+ *
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt)	"%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/gpio.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+/* GPIO registers */
+#define	SSBI_REG_ADDR_GPIO_BASE		0x150
+#define	SSBI_REG_ADDR_GPIO(n)		(SSBI_REG_ADDR_GPIO_BASE + n)
+
+/* GPIO */
+#define	PM_GPIO_BANK_MASK		0x70
+#define	PM_GPIO_BANK_SHIFT		4
+#define	PM_GPIO_WRITE			0x80
+
+/* Bank 0 */
+#define	PM_GPIO_VIN_MASK		0x0E
+#define	PM_GPIO_VIN_SHIFT		1
+#define	PM_GPIO_MODE_ENABLE		0x01
+
+/* Bank 1 */
+#define	PM_GPIO_MODE_MASK		0x0C
+#define	PM_GPIO_MODE_SHIFT		2
+#define	PM_GPIO_OUT_BUFFER		0x02
+#define	PM_GPIO_OUT_INVERT		0x01
+
+#define	PM_GPIO_MODE_OFF		3
+#define	PM_GPIO_MODE_OUTPUT		2
+#define	PM_GPIO_MODE_INPUT		0
+#define	PM_GPIO_MODE_BOTH		1
+
+/* Bank 2 */
+#define	PM_GPIO_PULL_MASK		0x0E
+#define	PM_GPIO_PULL_SHIFT		1
+
+/* Bank 3 */
+#define	PM_GPIO_OUT_STRENGTH_MASK	0x0C
+#define	PM_GPIO_OUT_STRENGTH_SHIFT	2
+#define PM_GPIO_PIN_ENABLE		0x00
+#define	PM_GPIO_PIN_DISABLE		0x01
+
+/* Bank 4 */
+#define	PM_GPIO_FUNC_MASK		0x0E
+#define	PM_GPIO_FUNC_SHIFT		1
+
+/* Bank 5 */
+#define	PM_GPIO_NON_INT_POL_INV	0x08
+#define PM_GPIO_BANKS		6
+
+struct pm_gpio_chip {
+	struct list_head	link;
+	struct gpio_chip	gpio_chip;
+	spinlock_t		pm_lock;
+	u8			*bank1;
+	int			irq_base;
+};
+
+static LIST_HEAD(pm_gpio_chips);
+static DEFINE_MUTEX(pm_gpio_chips_lock);
+
+static int pm_gpio_get(struct pm_gpio_chip *pm_gpio_chip, unsigned gpio)
+{
+	int	mode;
+
+	if (gpio >= pm_gpio_chip->gpio_chip.ngpio || pm_gpio_chip == NULL)
+		return -EINVAL;
+
+	/* Get gpio value from config bank 1 if output gpio.
+	   Get gpio value from IRQ RT status register for all other gpio modes.
+	 */
+	mode = (pm_gpio_chip->bank1[gpio] & PM_GPIO_MODE_MASK) >>
+		PM_GPIO_MODE_SHIFT;
+	if (mode == PM_GPIO_MODE_OUTPUT)
+		return pm_gpio_chip->bank1[gpio] & PM_GPIO_OUT_INVERT;
+	else
+		return pm8xxx_read_irq_stat(pm_gpio_chip->gpio_chip.dev->parent,
+				pm_gpio_chip->irq_base + gpio);
+}
+
+static int pm_gpio_set(struct pm_gpio_chip *pm_gpio_chip,
+		unsigned gpio, int value)
+{
+	int	rc;
+	u8	bank1;
+	unsigned long flags;
+
+	if (gpio >= pm_gpio_chip->gpio_chip.ngpio || pm_gpio_chip == NULL)
+		return -EINVAL;
+
+	spin_lock_irqsave(&pm_gpio_chip->pm_lock, flags);
+	bank1 = PM_GPIO_WRITE
+			| (pm_gpio_chip->bank1[gpio] & ~PM_GPIO_OUT_INVERT);
+
+	if (value)
+		bank1 |= PM_GPIO_OUT_INVERT;
+
+	pm_gpio_chip->bank1[gpio] = bank1;
+	rc = pm8xxx_writeb(pm_gpio_chip->gpio_chip.dev->parent,
+				SSBI_REG_ADDR_GPIO(gpio), bank1);
+	spin_unlock_irqrestore(&pm_gpio_chip->pm_lock, flags);
+
+	if (rc)
+		pr_err("FAIL pm8xxx_writeb(): rc=%d. "
+		       "(gpio=%d, value=%d)\n",
+		       rc, gpio, value);
+
+	return rc;
+}
+
+static int dir_map[] = {
+	PM_GPIO_MODE_OFF,
+	PM_GPIO_MODE_OUTPUT,
+	PM_GPIO_MODE_INPUT,
+	PM_GPIO_MODE_BOTH,
+};
+
+static int pm_gpio_set_direction(struct pm_gpio_chip *pm_gpio_chip,
+			      unsigned gpio, int direction)
+{
+	int	rc;
+	u8	bank1;
+	unsigned long flags;
+
+	if (!direction || pm_gpio_chip == NULL)
+		return -EINVAL;
+
+	spin_lock_irqsave(&pm_gpio_chip->pm_lock, flags);
+	bank1 = PM_GPIO_WRITE
+			| (pm_gpio_chip->bank1[gpio] & ~PM_GPIO_MODE_MASK);
+
+	bank1 |= ((dir_map[direction] << PM_GPIO_MODE_SHIFT)
+		  & PM_GPIO_MODE_MASK);
+
+	pm_gpio_chip->bank1[gpio] = bank1;
+	rc = pm8xxx_writeb(pm_gpio_chip->gpio_chip.dev->parent,
+				SSBI_REG_ADDR_GPIO(gpio), bank1);
+	spin_unlock_irqrestore(&pm_gpio_chip->pm_lock, flags);
+
+	if (rc)
+		pr_err("Failed on pm8xxx_writeb(): rc=%d (GPIO config)\n",
+			rc);
+
+	return rc;
+}
+
+static int pm_gpio_init_bank1(struct pm_gpio_chip *pm_gpio_chip)
+{
+	int i, rc;
+	u8 bank;
+
+	for (i = 0; i < pm_gpio_chip->gpio_chip.ngpio; i++) {
+		bank = 1 << PM_GPIO_BANK_SHIFT;
+		rc = pm8xxx_writeb(pm_gpio_chip->gpio_chip.dev->parent,
+				SSBI_REG_ADDR_GPIO(i),
+				bank);
+		if (rc) {
+			pr_err("error setting bank rc=%d\n", rc);
+			return rc;
+		}
+
+		rc = pm8xxx_readb(pm_gpio_chip->gpio_chip.dev->parent,
+				SSBI_REG_ADDR_GPIO(i),
+				&pm_gpio_chip->bank1[i]);
+		if (rc) {
+			pr_err("error reading bank 1 rc=%d\n", rc);
+			return rc;
+		}
+	}
+	return 0;
+}
+
+static int pm_gpio_to_irq(struct gpio_chip *gpio_chip, unsigned offset)
+{
+	struct pm_gpio_chip *pm_gpio_chip = dev_get_drvdata(gpio_chip->dev);
+
+	return pm_gpio_chip->irq_base + offset;
+}
+
+static int pm_gpio_read(struct gpio_chip *gpio_chip, unsigned offset)
+{
+	struct pm_gpio_chip *pm_gpio_chip = dev_get_drvdata(gpio_chip->dev);
+
+	return pm_gpio_get(pm_gpio_chip, offset);
+}
+
+static void pm_gpio_write(struct gpio_chip *gpio_chip,
+		unsigned offset, int val)
+{
+	struct pm_gpio_chip *pm_gpio_chip = dev_get_drvdata(gpio_chip->dev);
+
+	pm_gpio_set(pm_gpio_chip, offset, val);
+}
+
+static int pm_gpio_direction_input(struct gpio_chip *gpio_chip,
+		unsigned offset)
+{
+	struct pm_gpio_chip *pm_gpio_chip = dev_get_drvdata(gpio_chip->dev);
+
+	return pm_gpio_set_direction(pm_gpio_chip, offset, PM_GPIO_DIR_IN);
+}
+
+static int pm_gpio_direction_output(struct gpio_chip *gpio_chip,
+		unsigned offset,
+		int val)
+{
+	int ret;
+	struct pm_gpio_chip *pm_gpio_chip = dev_get_drvdata(gpio_chip->dev);
+
+	ret = pm_gpio_set_direction(pm_gpio_chip, offset, PM_GPIO_DIR_OUT);
+	if (!ret)
+		ret = pm_gpio_set(pm_gpio_chip, offset, val);
+
+	return ret;
+}
+
+static void pm_gpio_dbg_show(struct seq_file *s, struct gpio_chip *gpio_chip)
+{
+	static const char * const cmode[] = { "in", "in/out", "out", "off" };
+	struct pm_gpio_chip *pm_gpio_chip = dev_get_drvdata(gpio_chip->dev);
+	u8 mode, state, bank;
+	const char *label;
+	int i, j;
+
+	for (i = 0; i < gpio_chip->ngpio; i++) {
+		label = gpiochip_is_requested(gpio_chip, i);
+		mode = (pm_gpio_chip->bank1[i] & PM_GPIO_MODE_MASK) >>
+			PM_GPIO_MODE_SHIFT;
+		state = pm_gpio_get(pm_gpio_chip, i);
+		seq_printf(s, "gpio-%-3d (%-12.12s) %-10.10s"
+				" %s",
+				gpio_chip->base + i,
+				label ? label : "--",
+				cmode[mode],
+				state ? "hi" : "lo");
+		for (j = 0; j < PM_GPIO_BANKS; j++) {
+			bank = j << PM_GPIO_BANK_SHIFT;
+			pm8xxx_writeb(gpio_chip->dev->parent,
+					SSBI_REG_ADDR_GPIO(i),
+					bank);
+			pm8xxx_readb(gpio_chip->dev->parent,
+					SSBI_REG_ADDR_GPIO(i),
+					&bank);
+			seq_printf(s, " 0x%02x", bank);
+		}
+		seq_printf(s, "\n");
+	}
+}
+
+static int __devinit pm_gpio_probe(struct platform_device *pdev)
+{
+	int ret;
+	const struct pm8xxx_gpio_platform_data *pdata = pdev->dev.platform_data;
+	struct pm_gpio_chip *pm_gpio_chip;
+
+	if (!pdata) {
+		pr_err("missing platform data\n");
+		return -EINVAL;
+	}
+
+	pm_gpio_chip = kzalloc(sizeof(struct pm_gpio_chip), GFP_KERNEL);
+	if (!pm_gpio_chip) {
+		pr_err("Cannot allocate pm_gpio_chip\n");
+		return -ENOMEM;
+	}
+
+	pm_gpio_chip->bank1 = kzalloc(sizeof(u8) * pdata->gpio_cdata.ngpios,
+					GFP_KERNEL);
+	if (!pm_gpio_chip->bank1) {
+		pr_err("Cannot allocate pm_gpio_chip->bank1\n");
+		ret = -ENOMEM;
+		goto free_chip;
+	}
+
+	spin_lock_init(&pm_gpio_chip->pm_lock);
+	pm_gpio_chip->gpio_chip.label = "pm-gpio";
+	pm_gpio_chip->gpio_chip.direction_input	= pm_gpio_direction_input;
+	pm_gpio_chip->gpio_chip.direction_output = pm_gpio_direction_output;
+	pm_gpio_chip->gpio_chip.to_irq = pm_gpio_to_irq;
+	pm_gpio_chip->gpio_chip.get = pm_gpio_read;
+	pm_gpio_chip->gpio_chip.set = pm_gpio_write;
+	pm_gpio_chip->gpio_chip.dbg_show = pm_gpio_dbg_show;
+	pm_gpio_chip->gpio_chip.ngpio = pdata->gpio_cdata.ngpios;
+	pm_gpio_chip->gpio_chip.can_sleep = 0;
+	pm_gpio_chip->gpio_chip.dev = &pdev->dev;
+	pm_gpio_chip->gpio_chip.base = pdata->gpio_base;
+	pm_gpio_chip->irq_base = platform_get_irq(pdev, 0);
+	mutex_lock(&pm_gpio_chips_lock);
+	list_add(&pm_gpio_chip->link, &pm_gpio_chips);
+	mutex_unlock(&pm_gpio_chips_lock);
+	platform_set_drvdata(pdev, pm_gpio_chip);
+
+	ret = gpiochip_add(&pm_gpio_chip->gpio_chip);
+	if (ret) {
+		pr_err("gpiochip_add failed ret = %d\n", ret);
+		goto reset_drvdata;
+	}
+
+	ret = pm_gpio_init_bank1(pm_gpio_chip);
+	if (ret) {
+		pr_err("gpio init bank failed ret = %d\n", ret);
+		goto remove_chip;
+	}
+
+	pr_info("OK: base=%d, ngpio=%d\n", pm_gpio_chip->gpio_chip.base,
+		pm_gpio_chip->gpio_chip.ngpio);
+
+	return 0;
+
+remove_chip:
+	if (gpiochip_remove(&pm_gpio_chip->gpio_chip))
+		pr_err("failed to remove gpio chip\n");
+reset_drvdata:
+	platform_set_drvdata(pdev, NULL);
+	kfree(pm_gpio_chip->bank1);
+free_chip:
+	kfree(pm_gpio_chip);
+	return ret;
+}
+
+static int __devexit pm_gpio_remove(struct platform_device *pdev)
+{
+	struct pm_gpio_chip *pm_gpio_chip
+		= platform_get_drvdata(pdev);
+
+	mutex_lock(&pm_gpio_chips_lock);
+	list_del(&pm_gpio_chip->link);
+	mutex_unlock(&pm_gpio_chips_lock);
+	platform_set_drvdata(pdev, NULL);
+	if (gpiochip_remove(&pm_gpio_chip->gpio_chip))
+		pr_err("failed to remove gpio chip\n");
+	kfree(pm_gpio_chip->bank1);
+	kfree(pm_gpio_chip);
+	return 0;
+}
+
+int pm8xxx_gpio_config(int gpio, struct pm_gpio *param)
+{
+	int	rc, pm_gpio = -EINVAL;
+	u8	bank[8];
+	unsigned long flags;
+	struct pm_gpio_chip *pm_gpio_chip;
+	struct gpio_chip *gpio_chip;
+
+	if (param == NULL)
+		return -EINVAL;
+
+	mutex_lock(&pm_gpio_chips_lock);
+	list_for_each_entry(pm_gpio_chip, &pm_gpio_chips, link) {
+		gpio_chip = &pm_gpio_chip->gpio_chip;
+		if (gpio >= gpio_chip->base
+			&& gpio < gpio_chip->base + gpio_chip->ngpio) {
+			pm_gpio = gpio - gpio_chip->base;
+			break;
+		}
+	}
+	mutex_unlock(&pm_gpio_chips_lock);
+	if (pm_gpio < 0) {
+		pr_err("called on gpio %d not handled by any pmic\n", gpio);
+		return -EINVAL;
+	}
+
+	/* Select banks and configure the gpio */
+	bank[0] = PM_GPIO_WRITE |
+		((param->vin_sel << PM_GPIO_VIN_SHIFT) &
+			PM_GPIO_VIN_MASK) |
+		PM_GPIO_MODE_ENABLE;
+	bank[1] = PM_GPIO_WRITE |
+		((1 << PM_GPIO_BANK_SHIFT) &
+			PM_GPIO_BANK_MASK) |
+		((dir_map[param->direction] <<
+			PM_GPIO_MODE_SHIFT) &
+			PM_GPIO_MODE_MASK) |
+		((param->direction & PM_GPIO_DIR_OUT) ?
+			((param->output_buffer & 1) ?
+			 PM_GPIO_OUT_BUFFER : 0) : 0) |
+		((param->direction & PM_GPIO_DIR_OUT) ?
+			param->output_value & 0x01 : 0);
+	bank[2] = PM_GPIO_WRITE |
+		((2 << PM_GPIO_BANK_SHIFT) &
+			PM_GPIO_BANK_MASK) |
+		((param->pull << PM_GPIO_PULL_SHIFT) &
+			PM_GPIO_PULL_MASK);
+	bank[3] = PM_GPIO_WRITE |
+		((3 << PM_GPIO_BANK_SHIFT) &
+			PM_GPIO_BANK_MASK) |
+		((param->out_strength <<
+			PM_GPIO_OUT_STRENGTH_SHIFT) &
+			PM_GPIO_OUT_STRENGTH_MASK) |
+		(param->disable_pin ?
+			PM_GPIO_PIN_DISABLE : PM_GPIO_PIN_ENABLE);
+	bank[4] = PM_GPIO_WRITE |
+		((4 << PM_GPIO_BANK_SHIFT) &
+			PM_GPIO_BANK_MASK) |
+		((param->function << PM_GPIO_FUNC_SHIFT) &
+			PM_GPIO_FUNC_MASK);
+	bank[5] = PM_GPIO_WRITE |
+		((5 << PM_GPIO_BANK_SHIFT) & PM_GPIO_BANK_MASK) |
+		(param->inv_int_pol ? 0 : PM_GPIO_NON_INT_POL_INV);
+
+	spin_lock_irqsave(&pm_gpio_chip->pm_lock, flags);
+	/* Remember bank1 for later use */
+	pm_gpio_chip->bank1[pm_gpio] = bank[1];
+	rc = pm8xxx_write_buf(pm_gpio_chip->gpio_chip.dev->parent,
+			SSBI_REG_ADDR_GPIO(pm_gpio), bank, 6);
+	spin_unlock_irqrestore(&pm_gpio_chip->pm_lock, flags);
+
+	if (rc)
+		pr_err("Failed on pm8xxx_write_buf() rc=%d (GPIO config)\n",
+			rc);
+
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_gpio_config);
+
+static struct platform_driver pm_gpio_driver = {
+	.probe		= pm_gpio_probe,
+	.remove		= __devexit_p(pm_gpio_remove),
+	.driver		= {
+		.name	= PM8XXX_GPIO_DEV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init pm_gpio_init(void)
+{
+	return platform_driver_register(&pm_gpio_driver);
+}
+postcore_initcall(pm_gpio_init);
+
+static void __exit pm_gpio_exit(void)
+{
+	platform_driver_unregister(&pm_gpio_driver);
+}
+module_exit(pm_gpio_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC GPIO driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8XXX_GPIO_DEV_NAME);
diff --git a/drivers/gpio/pm8xxx-mpp.c b/drivers/gpio/pm8xxx-mpp.c
new file mode 100644
index 0000000..affe980
--- /dev/null
+++ b/drivers/gpio/pm8xxx-mpp.c
@@ -0,0 +1,335 @@
+/*
+ * Qualcomm PM8XXX Multi-Purpose Pin (MPP) driver
+ *
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/mpp.h>
+
+/* MPP Type */
+#define	PM8XXX_MPP_TYPE_MASK		0xE0
+#define	PM8XXX_MPP_TYPE_SHIFT		5
+
+/* MPP Config Level */
+#define	PM8XXX_MPP_CONFIG_LVL_MASK	0x1C
+#define	PM8XXX_MPP_CONFIG_LVL_SHIFT	2
+
+/* MPP Config Control */
+#define	PM8XXX_MPP_CONFIG_CTRL_MASK	0x03
+#define	PM8XXX_MPP_CONFIG_CTRL_SHIFT	0
+
+struct pm8xxx_mpp_chip {
+	struct list_head	link;
+	struct gpio_chip	gpio_chip;
+	spinlock_t		pm_lock;
+	u8			*ctrl_reg;
+	int			mpp_base;
+	int			irq_base;
+	int			nmpps;
+	u16			base_addr;
+};
+
+static LIST_HEAD(pm8xxx_mpp_chips);
+static DEFINE_MUTEX(pm8xxx_mpp_chips_lock);
+
+static int pm8xxx_mpp_write(struct pm8xxx_mpp_chip *mpp_chip, u16 offset,
+				u8 val, u8 mask)
+{
+	u8 reg;
+	int rc;
+	unsigned long flags;
+
+	spin_lock_irqsave(&mpp_chip->pm_lock, flags);
+
+	reg = (mpp_chip->ctrl_reg[offset] & ~mask) | (val & mask);
+	rc = pm8xxx_writeb(mpp_chip->gpio_chip.dev->parent,
+				mpp_chip->base_addr + offset, reg);
+	if (!rc)
+		mpp_chip->ctrl_reg[offset] = reg;
+
+	spin_unlock_irqrestore(&mpp_chip->pm_lock, flags);
+
+	return rc;
+}
+
+static int pm8xxx_mpp_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	struct pm8xxx_mpp_chip *mpp_chip = dev_get_drvdata(chip->dev);
+
+	return mpp_chip->irq_base + offset;
+}
+
+static int pm8xxx_mpp_get(struct gpio_chip *chip, unsigned offset)
+{
+	struct pm8xxx_mpp_chip *mpp_chip = dev_get_drvdata(chip->dev);
+	int rc;
+
+	if ((mpp_chip->ctrl_reg[offset] & PM8XXX_MPP_TYPE_MASK) >>
+			PM8XXX_MPP_TYPE_SHIFT == PM8XXX_MPP_TYPE_D_OUTPUT)
+		rc = mpp_chip->ctrl_reg[offset] & PM8XXX_MPP_CONFIG_CTRL_MASK;
+	else
+		rc = pm8xxx_read_irq_stat(mpp_chip->gpio_chip.dev->parent,
+				mpp_chip->irq_base + offset);
+
+	return rc;
+}
+
+static void pm8xxx_mpp_set(struct gpio_chip *chip, unsigned offset, int val)
+{
+	struct pm8xxx_mpp_chip *mpp_chip = dev_get_drvdata(chip->dev);
+	u8 reg = val ? PM8XXX_MPP_DOUT_CTRL_HIGH : PM8XXX_MPP_DOUT_CTRL_LOW;
+	int rc;
+
+	rc = pm8xxx_mpp_write(mpp_chip, offset, reg,
+			PM8XXX_MPP_CONFIG_CTRL_MASK);
+	if (rc)
+		pr_err("pm8xxx_mpp_write(): rc=%d\n", rc);
+}
+
+static int pm8xxx_mpp_dir_input(struct gpio_chip *chip, unsigned offset)
+{
+	struct pm8xxx_mpp_chip *mpp_chip = dev_get_drvdata(chip->dev);
+	int rc = pm8xxx_mpp_write(mpp_chip, offset,
+			PM8XXX_MPP_TYPE_D_INPUT << PM8XXX_MPP_TYPE_SHIFT,
+			PM8XXX_MPP_TYPE_MASK);
+
+	if (rc)
+		pr_err("pm8xxx_mpp_write(): rc=%d\n", rc);
+	return rc;
+}
+
+static int pm8xxx_mpp_dir_output(struct gpio_chip *chip,
+		unsigned offset, int val)
+{
+	struct pm8xxx_mpp_chip *mpp_chip = dev_get_drvdata(chip->dev);
+	u8 reg = (PM8XXX_MPP_TYPE_D_OUTPUT << PM8XXX_MPP_TYPE_SHIFT) |
+		(val & PM8XXX_MPP_CONFIG_CTRL_MASK);
+	u8 mask = PM8XXX_MPP_TYPE_MASK | PM8XXX_MPP_CONFIG_CTRL_MASK;
+	int rc = pm8xxx_mpp_write(mpp_chip, offset, reg, mask);
+
+	if (rc)
+		pr_err("pm8xxx_mpp_write(): rc=%d\n", rc);
+	return rc;
+}
+
+static void pm8xxx_mpp_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+	static const char * const ctype[] = {	"d_in", "d_out", "bi_dir",
+						"a_in", "a_out", "sink",
+						"dtest_sink", "dtest_out"
+	};
+	struct pm8xxx_mpp_chip *mpp_chip = dev_get_drvdata(chip->dev);
+	u8 type, state;
+	const char *label;
+	int i;
+
+	for (i = 0; i < mpp_chip->nmpps; i++) {
+		label = gpiochip_is_requested(chip, i);
+		type = (mpp_chip->ctrl_reg[i] & PM8XXX_MPP_TYPE_MASK) >>
+			PM8XXX_MPP_TYPE_SHIFT;
+		state = pm8xxx_mpp_get(chip, i);
+		seq_printf(s, "gpio-%-3d (%-12.12s) %-10.10s"
+				" %s 0x%02x\n",
+				chip->base + i,
+				label ? label : "--",
+				ctype[type],
+				state ? "hi" : "lo",
+				mpp_chip->ctrl_reg[i]);
+	}
+}
+
+int pm8xxx_mpp_config(unsigned mpp, struct pm8xxx_mpp_config_data *config)
+{
+	struct pm8xxx_mpp_chip *mpp_chip;
+	int rc, found = 0;
+	u8 config_reg, mask;
+
+	if (!config) {
+		pr_err("config not specified for MPP %d\n", mpp);
+		return -EINVAL;
+	}
+
+	mutex_lock(&pm8xxx_mpp_chips_lock);
+	list_for_each_entry(mpp_chip, &pm8xxx_mpp_chips, link) {
+		if (mpp >= mpp_chip->mpp_base
+		    && mpp < mpp_chip->mpp_base + mpp_chip->nmpps) {
+			found = 1;
+			break;
+		}
+	}
+	mutex_unlock(&pm8xxx_mpp_chips_lock);
+	if (!found) {
+		pr_err("called on mpp %d not handled by any pmic\n", mpp);
+		return -EINVAL;
+	}
+
+	mask = PM8XXX_MPP_TYPE_MASK | PM8XXX_MPP_CONFIG_LVL_MASK |
+		PM8XXX_MPP_CONFIG_CTRL_MASK;
+	config_reg = (config->type << PM8XXX_MPP_TYPE_SHIFT)
+			& PM8XXX_MPP_TYPE_MASK;
+	config_reg |= (config->level << PM8XXX_MPP_CONFIG_LVL_SHIFT)
+			& PM8XXX_MPP_CONFIG_LVL_MASK;
+	config_reg |= config->control & PM8XXX_MPP_CONFIG_CTRL_MASK;
+
+	rc = pm8xxx_mpp_write(mpp_chip, mpp - mpp_chip->mpp_base, config_reg,
+			      mask);
+
+	if (rc)
+		pr_err("pm8xxx_mpp_write(): rc=%d\n", rc);
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_mpp_config);
+
+static int __devinit pm8xxx_mpp_reg_init(struct pm8xxx_mpp_chip *mpp_chip)
+{
+	int rc, i;
+
+	for (i = 0; i < mpp_chip->nmpps; i++) {
+		rc = pm8xxx_readb(mpp_chip->gpio_chip.dev->parent,
+					mpp_chip->base_addr + i,
+					&mpp_chip->ctrl_reg[i]);
+		if (rc) {
+			pr_err("failed to read register 0x%x rc=%d\n",
+						mpp_chip->base_addr + i, rc);
+			return rc;
+		}
+	}
+	return 0;
+}
+
+static int __devinit pm8xxx_mpp_probe(struct platform_device *pdev)
+{
+	int rc;
+	const struct pm8xxx_mpp_platform_data *pdata = pdev->dev.platform_data;
+	struct pm8xxx_mpp_chip *mpp_chip;
+
+	if (!pdata) {
+		pr_err("missing platform data\n");
+		return -EINVAL;
+	}
+
+	mpp_chip = kzalloc(sizeof(struct pm8xxx_mpp_chip), GFP_KERNEL);
+	if (!mpp_chip) {
+		pr_err("Cannot allocate %d bytes\n",
+			sizeof(struct pm8xxx_mpp_chip));
+		return -ENOMEM;
+	}
+
+	mpp_chip->ctrl_reg = kzalloc(pdata->core_data.nmpps, GFP_KERNEL);
+	if (!mpp_chip->ctrl_reg) {
+		pr_err("Cannot allocate %d bytes\n", pdata->core_data.nmpps);
+		rc = -ENOMEM;
+		goto free_mpp_chip;
+	}
+
+	spin_lock_init(&mpp_chip->pm_lock);
+
+	mpp_chip->gpio_chip.label = PM8XXX_MPP_DEV_NAME;
+	mpp_chip->gpio_chip.direction_input = pm8xxx_mpp_dir_input;
+	mpp_chip->gpio_chip.direction_output = pm8xxx_mpp_dir_output;
+	mpp_chip->gpio_chip.to_irq = pm8xxx_mpp_to_irq;
+	mpp_chip->gpio_chip.get = pm8xxx_mpp_get;
+	mpp_chip->gpio_chip.set = pm8xxx_mpp_set;
+	mpp_chip->gpio_chip.dbg_show = pm8xxx_mpp_dbg_show;
+	mpp_chip->gpio_chip.ngpio = pdata->core_data.nmpps;
+	mpp_chip->gpio_chip.can_sleep = 0;
+	mpp_chip->gpio_chip.dev = &pdev->dev;
+	mpp_chip->gpio_chip.base = pdata->mpp_base;
+	mpp_chip->irq_base = platform_get_irq(pdev, 0);
+	mpp_chip->mpp_base = pdata->mpp_base;
+	mpp_chip->base_addr = pdata->core_data.base_addr;
+	mpp_chip->nmpps = pdata->core_data.nmpps;
+
+	mutex_lock(&pm8xxx_mpp_chips_lock);
+	list_add(&mpp_chip->link, &pm8xxx_mpp_chips);
+	mutex_unlock(&pm8xxx_mpp_chips_lock);
+
+	platform_set_drvdata(pdev, mpp_chip);
+
+	rc = gpiochip_add(&mpp_chip->gpio_chip);
+	if (rc) {
+		pr_err("gpiochip_add failed, rc=%d\n", rc);
+		goto reset_drvdata;
+	}
+
+	rc = pm8xxx_mpp_reg_init(mpp_chip);
+	if (rc) {
+		pr_err("failed to read MPP ctrl registers, rc=%d\n", rc);
+		goto remove_chip;
+	}
+
+	pr_info("OK: base=%d, ngpio=%d\n", mpp_chip->gpio_chip.base,
+		mpp_chip->gpio_chip.ngpio);
+
+	return 0;
+
+remove_chip:
+	if (gpiochip_remove(&mpp_chip->gpio_chip))
+		pr_err("failed to remove gpio chip\n");
+reset_drvdata:
+	platform_set_drvdata(pdev, NULL);
+free_mpp_chip:
+	kfree(mpp_chip);
+	return rc;
+}
+
+static int __devexit pm8xxx_mpp_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_mpp_chip *mpp_chip = platform_get_drvdata(pdev);
+
+	mutex_lock(&pm8xxx_mpp_chips_lock);
+	list_del(&mpp_chip->link);
+	mutex_unlock(&pm8xxx_mpp_chips_lock);
+	platform_set_drvdata(pdev, NULL);
+	if (gpiochip_remove(&mpp_chip->gpio_chip))
+		pr_err("failed to remove gpio chip\n");
+	kfree(mpp_chip->ctrl_reg);
+	kfree(mpp_chip);
+
+	return 0;
+}
+
+static struct platform_driver pm8xxx_mpp_driver = {
+	.probe		= pm8xxx_mpp_probe,
+	.remove		= __devexit_p(pm8xxx_mpp_remove),
+	.driver		= {
+		.name	= PM8XXX_MPP_DEV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init pm8xxx_mpp_init(void)
+{
+	return platform_driver_register(&pm8xxx_mpp_driver);
+}
+postcore_initcall(pm8xxx_mpp_init);
+
+static void __exit pm8xxx_mpp_exit(void)
+{
+	platform_driver_unregister(&pm8xxx_mpp_driver);
+}
+module_exit(pm8xxx_mpp_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PM8XXX MPP driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8XXX_MPP_DEV_NAME);
diff --git a/drivers/gpio/qpnp-gpio.c b/drivers/gpio/qpnp-gpio.c
new file mode 100644
index 0000000..d9a23e1
--- /dev/null
+++ b/drivers/gpio/qpnp-gpio.c
@@ -0,0 +1,1090 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/qpnp/gpio.h>
+#include <linux/export.h>
+
+#include <mach/qpnp.h>
+
+#define Q_REG_ADDR(q_spec, reg_index)	\
+		((q_spec)->offset + reg_index)
+
+#define Q_REG_STATUS1			0x8
+#define Q_NUM_CTL_REGS			7
+
+/* type registers base address offsets */
+#define Q_REG_TYPE			0x10
+#define Q_REG_SUBTYPE			0x11
+
+/* gpio peripheral type and subtype values */
+#define Q_GPIO_TYPE			0x10
+#define Q_GPIO_SUBTYPE_GPIO_4CH		0x1
+#define Q_GPIO_SUBTYPE_GPIOC_4CH	0x5
+#define Q_GPIO_SUBTYPE_GPIO_8CH		0x9
+#define Q_GPIO_SUBTYPE_GPIOC_8CH	0xD
+
+/* control register base address offsets */
+#define Q_REG_MODE_CTL			0x40
+#define Q_REG_DIG_PULL_CTL		0x42
+#define Q_REG_DIG_IN_CTL		0x43
+#define Q_REG_DIG_VIN_CTL		0x44
+#define Q_REG_DIG_OUT_CTL		0x45
+#define Q_REG_EN_CTL			0x46
+
+/* control register regs array indices */
+#define Q_REG_I_MODE_CTL		0
+#define Q_REG_I_DIG_PULL_CTL		2
+#define Q_REG_I_DIG_IN_CTL		3
+#define Q_REG_I_DIG_VIN_CTL		4
+#define Q_REG_I_DIG_OUT_CTL		5
+#define Q_REG_I_EN_CTL			6
+
+/* control reg: mode */
+#define Q_REG_OUT_INVERT_SHIFT		0
+#define Q_REG_OUT_INVERT_MASK		0x1
+#define Q_REG_SRC_SEL_SHIFT		1
+#define Q_REG_SRC_SEL_MASK		0xE
+#define Q_REG_MODE_SEL_SHIFT		4
+#define Q_REG_MODE_SEL_MASK		0x70
+
+/* control reg: dig_vin */
+#define Q_REG_VIN_SHIFT			0
+#define Q_REG_VIN_MASK			0x7
+
+/* control reg: dig_pull */
+#define Q_REG_PULL_SHIFT		0
+#define Q_REG_PULL_MASK			0x7
+
+/* control reg: dig_out */
+#define Q_REG_OUT_STRENGTH_SHIFT	0
+#define Q_REG_OUT_STRENGTH_MASK		0x3
+#define Q_REG_OUT_TYPE_SHIFT		4
+#define Q_REG_OUT_TYPE_MASK		0x30
+
+/* control reg: en */
+#define Q_REG_MASTER_EN_SHIFT		7
+#define Q_REG_MASTER_EN_MASK		0x80
+
+enum qpnp_gpio_param_type {
+	Q_GPIO_CFG_DIRECTION,
+	Q_GPIO_CFG_OUTPUT_TYPE,
+	Q_GPIO_CFG_INVERT,
+	Q_GPIO_CFG_PULL,
+	Q_GPIO_CFG_VIN_SEL,
+	Q_GPIO_CFG_OUT_STRENGTH,
+	Q_GPIO_CFG_SRC_SELECT,
+	Q_GPIO_CFG_MASTER_EN,
+	Q_GPIO_CFG_INVALID,
+};
+
+#define Q_NUM_PARAMS			Q_GPIO_CFG_INVALID
+
+/* param error checking */
+#define QPNP_GPIO_DIR_INVALID		3
+#define QPNP_GPIO_INVERT_INVALID	2
+#define QPNP_GPIO_OUT_BUF_INVALID	3
+#define QPNP_GPIO_VIN_INVALID		8
+#define QPNP_GPIO_PULL_INVALID		6
+#define QPNP_GPIO_OUT_STRENGTH_INVALID	4
+#define QPNP_GPIO_SRC_INVALID		8
+#define QPNP_GPIO_MASTER_INVALID	2
+
+struct qpnp_gpio_spec {
+	uint8_t slave;			/* 0-15 */
+	uint16_t offset;		/* 0-255 */
+	uint32_t gpio_chip_idx;		/* offset from gpio_chip base */
+	uint32_t pmic_gpio;		/* PMIC gpio number */
+	int irq;			/* logical IRQ number */
+	u8 regs[Q_NUM_CTL_REGS];	/* Control regs */
+	u8 type;			/* peripheral type */
+	u8 subtype;			/* peripheral subtype */
+	struct device_node *node;
+	enum qpnp_gpio_param_type params[Q_NUM_PARAMS];
+	struct qpnp_gpio_chip *q_chip;
+};
+
+struct qpnp_gpio_chip {
+	struct gpio_chip	gpio_chip;
+	struct spmi_device	*spmi;
+	struct qpnp_gpio_spec	**pmic_gpios;
+	struct qpnp_gpio_spec	**chip_gpios;
+	uint32_t		pmic_gpio_lowest;
+	uint32_t		pmic_gpio_highest;
+	struct device_node	*int_ctrl;
+	struct list_head	chip_list;
+	struct dentry		*dfs_dir;
+};
+
+static LIST_HEAD(qpnp_gpio_chips);
+static DEFINE_MUTEX(qpnp_gpio_chips_lock);
+
+static inline void qpnp_pmic_gpio_set_spec(struct qpnp_gpio_chip *q_chip,
+					      uint32_t pmic_gpio,
+					      struct qpnp_gpio_spec *spec)
+{
+	q_chip->pmic_gpios[pmic_gpio - q_chip->pmic_gpio_lowest] = spec;
+}
+
+static inline struct qpnp_gpio_spec *qpnp_pmic_gpio_get_spec(
+						struct qpnp_gpio_chip *q_chip,
+						uint32_t pmic_gpio)
+{
+	if (pmic_gpio < q_chip->pmic_gpio_lowest ||
+	    pmic_gpio > q_chip->pmic_gpio_highest)
+		return NULL;
+
+	return q_chip->pmic_gpios[pmic_gpio - q_chip->pmic_gpio_lowest];
+}
+
+static inline struct qpnp_gpio_spec *qpnp_chip_gpio_get_spec(
+						struct qpnp_gpio_chip *q_chip,
+						uint32_t chip_gpio)
+{
+	if (chip_gpio > q_chip->gpio_chip.ngpio)
+		return NULL;
+
+	return q_chip->chip_gpios[chip_gpio];
+}
+
+static inline void qpnp_chip_gpio_set_spec(struct qpnp_gpio_chip *q_chip,
+					      uint32_t chip_gpio,
+					      struct qpnp_gpio_spec *spec)
+{
+	q_chip->chip_gpios[chip_gpio] = spec;
+}
+
+static int qpnp_gpio_check_config(struct qpnp_gpio_spec *q_spec,
+				  struct qpnp_gpio_cfg *param)
+{
+	int gpio = q_spec->pmic_gpio;
+
+	if (param->direction >= QPNP_GPIO_DIR_INVALID)
+		pr_err("invalid direction for gpio %d\n", gpio);
+	else if (param->invert >= QPNP_GPIO_INVERT_INVALID)
+		pr_err("invalid invert polarity for gpio %d\n", gpio);
+	else if (param->src_select >= QPNP_GPIO_SRC_INVALID)
+		pr_err("invalid source select for gpio %d\n", gpio);
+	else if (param->out_strength >= QPNP_GPIO_OUT_STRENGTH_INVALID ||
+		 param->out_strength == 0)
+		pr_err("invalid out strength for gpio %d\n", gpio);
+	else if (param->output_type >= QPNP_GPIO_OUT_BUF_INVALID)
+		pr_err("invalid out type for gpio %d\n", gpio);
+	else if ((param->output_type == QPNP_GPIO_OUT_BUF_OPEN_DRAIN_NMOS ||
+		 param->output_type == QPNP_GPIO_OUT_BUF_OPEN_DRAIN_PMOS) &&
+		 (q_spec->subtype == Q_GPIO_SUBTYPE_GPIOC_4CH ||
+		 (q_spec->subtype == Q_GPIO_SUBTYPE_GPIOC_8CH)))
+		pr_err("invalid out type for gpio %d\n"
+		       "gpioc does not support open-drain\n", gpio);
+	else if (param->vin_sel >= QPNP_GPIO_VIN_INVALID)
+		pr_err("invalid vin select value for gpio %d\n", gpio);
+	else if (param->pull >= QPNP_GPIO_PULL_INVALID)
+		pr_err("invalid pull value for gpio %d\n", gpio);
+	else if (param->master_en >= QPNP_GPIO_MASTER_INVALID)
+		pr_err("invalid master_en value for gpio %d\n", gpio);
+	else
+		return 0;
+
+	return -EINVAL;
+}
+
+static inline u8 q_reg_get(u8 *reg, int shift, int mask)
+{
+	return (*reg & mask) >> shift;
+}
+
+static inline void q_reg_set(u8 *reg, int shift, int mask, int value)
+{
+	*reg |= (value << shift) & mask;
+}
+
+static inline void q_reg_clr_set(u8 *reg, int shift, int mask, int value)
+{
+	*reg &= ~mask;
+	*reg |= (value << shift) & mask;
+}
+
+static int qpnp_gpio_cache_regs(struct qpnp_gpio_chip *q_chip,
+				struct qpnp_gpio_spec *q_spec)
+{
+	int rc;
+	struct device *dev = &q_chip->spmi->dev;
+
+	rc = spmi_ext_register_readl(q_chip->spmi->ctrl, q_spec->slave,
+				     Q_REG_ADDR(q_spec, Q_REG_MODE_CTL),
+				     &q_spec->regs[Q_REG_I_MODE_CTL],
+				     Q_NUM_CTL_REGS);
+	if (rc)
+		dev_err(dev, "%s: unable to read control regs\n", __func__);
+
+	return rc;
+}
+
+static int _qpnp_gpio_config(struct qpnp_gpio_chip *q_chip,
+			     struct qpnp_gpio_spec *q_spec,
+			     struct qpnp_gpio_cfg *param)
+{
+	struct device *dev = &q_chip->spmi->dev;
+	int rc;
+
+	rc = qpnp_gpio_check_config(q_spec, param);
+	if (rc)
+		goto gpio_cfg;
+
+	/* set direction */
+	q_reg_clr_set(&q_spec->regs[Q_REG_I_MODE_CTL],
+			  Q_REG_MODE_SEL_SHIFT, Q_REG_MODE_SEL_MASK,
+			  param->direction);
+
+	/* output specific configuration */
+	q_reg_clr_set(&q_spec->regs[Q_REG_I_MODE_CTL],
+			  Q_REG_OUT_INVERT_SHIFT, Q_REG_OUT_INVERT_MASK,
+			  param->invert);
+	q_reg_clr_set(&q_spec->regs[Q_REG_I_MODE_CTL],
+			  Q_REG_SRC_SEL_SHIFT, Q_REG_SRC_SEL_MASK,
+			  param->src_select);
+	q_reg_clr_set(&q_spec->regs[Q_REG_I_DIG_OUT_CTL],
+			  Q_REG_OUT_STRENGTH_SHIFT, Q_REG_OUT_STRENGTH_MASK,
+			  param->out_strength);
+	q_reg_clr_set(&q_spec->regs[Q_REG_I_DIG_OUT_CTL],
+			  Q_REG_OUT_TYPE_SHIFT, Q_REG_OUT_TYPE_MASK,
+			  param->output_type);
+
+	/* config applicable for both input / output */
+	q_reg_clr_set(&q_spec->regs[Q_REG_I_DIG_VIN_CTL],
+			  Q_REG_VIN_SHIFT, Q_REG_VIN_MASK,
+			  param->vin_sel);
+	q_reg_clr_set(&q_spec->regs[Q_REG_I_DIG_PULL_CTL],
+			  Q_REG_PULL_SHIFT, Q_REG_PULL_MASK,
+			  param->pull);
+	q_reg_clr_set(&q_spec->regs[Q_REG_I_EN_CTL],
+			  Q_REG_MASTER_EN_SHIFT, Q_REG_MASTER_EN_MASK,
+			  param->master_en);
+
+	rc = spmi_ext_register_writel(q_chip->spmi->ctrl, q_spec->slave,
+			      Q_REG_ADDR(q_spec, Q_REG_MODE_CTL),
+			      &q_spec->regs[Q_REG_I_MODE_CTL], Q_NUM_CTL_REGS);
+	if (rc) {
+		dev_err(&q_chip->spmi->dev, "%s: unable to write master"
+						" enable\n", __func__);
+		goto gpio_cfg;
+	}
+
+	return 0;
+
+gpio_cfg:
+	dev_err(dev, "%s: unable to set default config for"
+		     " pmic gpio %d\n", __func__, q_spec->pmic_gpio);
+
+	return rc;
+}
+
+int qpnp_gpio_config(int gpio, struct qpnp_gpio_cfg *param)
+{
+	int rc, chip_offset;
+	struct qpnp_gpio_chip *q_chip;
+	struct qpnp_gpio_spec *q_spec = NULL;
+	struct gpio_chip *gpio_chip;
+
+	if (param == NULL)
+		return -EINVAL;
+
+	mutex_lock(&qpnp_gpio_chips_lock);
+	list_for_each_entry(q_chip, &qpnp_gpio_chips, chip_list) {
+		gpio_chip = &q_chip->gpio_chip;
+		if (gpio >= gpio_chip->base
+				&& gpio < gpio_chip->base + gpio_chip->ngpio) {
+			chip_offset = gpio - gpio_chip->base;
+			q_spec = qpnp_chip_gpio_get_spec(q_chip, chip_offset);
+			if (WARN_ON(!q_spec)) {
+				mutex_unlock(&qpnp_gpio_chips_lock);
+				return -ENODEV;
+			}
+			break;
+		}
+	}
+	mutex_unlock(&qpnp_gpio_chips_lock);
+
+	rc = _qpnp_gpio_config(q_chip, q_spec, param);
+
+	return rc;
+}
+EXPORT_SYMBOL(qpnp_gpio_config);
+
+int qpnp_gpio_map_gpio(uint16_t slave_id, uint32_t pmic_gpio)
+{
+	struct qpnp_gpio_chip *q_chip;
+	struct qpnp_gpio_spec *q_spec = NULL;
+
+	mutex_lock(&qpnp_gpio_chips_lock);
+	list_for_each_entry(q_chip, &qpnp_gpio_chips, chip_list) {
+		if (q_chip->spmi->sid != slave_id)
+			continue;
+		if (q_chip->pmic_gpio_lowest <= pmic_gpio &&
+		    q_chip->pmic_gpio_highest >= pmic_gpio) {
+			q_spec = qpnp_pmic_gpio_get_spec(q_chip, pmic_gpio);
+			mutex_unlock(&qpnp_gpio_chips_lock);
+			if (WARN_ON(!q_spec))
+				return -ENODEV;
+			return q_chip->gpio_chip.base + q_spec->gpio_chip_idx;
+		}
+	}
+	mutex_unlock(&qpnp_gpio_chips_lock);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(qpnp_gpio_map_gpio);
+
+static int qpnp_gpio_to_irq(struct gpio_chip *gpio_chip, unsigned offset)
+{
+	struct qpnp_gpio_chip *q_chip = dev_get_drvdata(gpio_chip->dev);
+	struct qpnp_gpio_spec *q_spec;
+
+	q_spec = qpnp_chip_gpio_get_spec(q_chip, offset);
+	if (!q_spec)
+		return -EINVAL;
+
+	return q_spec->irq;
+}
+
+static int qpnp_gpio_get(struct gpio_chip *gpio_chip, unsigned offset)
+{
+	int rc, ret_val;
+	struct qpnp_gpio_chip *q_chip = dev_get_drvdata(gpio_chip->dev);
+	struct qpnp_gpio_spec *q_spec = NULL;
+	u8 buf[1];
+
+	if (WARN_ON(!q_chip))
+		return -ENODEV;
+
+	q_spec = qpnp_chip_gpio_get_spec(q_chip, offset);
+	if (WARN_ON(!q_spec))
+		return -ENODEV;
+
+	/* gpio val is from RT status iff input is enabled */
+	if ((q_spec->regs[Q_REG_I_MODE_CTL] & Q_REG_MODE_SEL_MASK)
+						== QPNP_GPIO_DIR_IN) {
+		/* INT_RT_STS */
+		rc = spmi_ext_register_readl(q_chip->spmi->ctrl, q_spec->slave,
+				Q_REG_ADDR(q_spec, Q_REG_STATUS1),
+				&buf[0], 1);
+		return buf[0];
+
+	} else {
+		ret_val = (q_spec->regs[Q_REG_I_MODE_CTL] &
+			       Q_REG_OUT_INVERT_MASK) >> Q_REG_OUT_INVERT_SHIFT;
+		return ret_val;
+	}
+
+	return 0;
+}
+
+static int __qpnp_gpio_set(struct qpnp_gpio_chip *q_chip,
+			   struct qpnp_gpio_spec *q_spec, int value)
+{
+	int rc;
+
+	if (!q_chip || !q_spec)
+		return -EINVAL;
+
+	if (value)
+		q_reg_clr_set(&q_spec->regs[Q_REG_I_MODE_CTL],
+			  Q_REG_OUT_INVERT_SHIFT, Q_REG_OUT_INVERT_MASK, 1);
+	else
+		q_reg_clr_set(&q_spec->regs[Q_REG_I_MODE_CTL],
+			  Q_REG_OUT_INVERT_SHIFT, Q_REG_OUT_INVERT_MASK, 0);
+
+	rc = spmi_ext_register_writel(q_chip->spmi->ctrl, q_spec->slave,
+			      Q_REG_ADDR(q_spec, Q_REG_I_MODE_CTL),
+			      &q_spec->regs[Q_REG_I_MODE_CTL], 1);
+	if (rc)
+		dev_err(&q_chip->spmi->dev, "%s: spmi write failed\n",
+								__func__);
+	return rc;
+}
+
+
+static void qpnp_gpio_set(struct gpio_chip *gpio_chip,
+		unsigned offset, int value)
+{
+	struct qpnp_gpio_chip *q_chip = dev_get_drvdata(gpio_chip->dev);
+	struct qpnp_gpio_spec *q_spec;
+
+	if (WARN_ON(!q_chip))
+		return;
+
+	q_spec = qpnp_chip_gpio_get_spec(q_chip, offset);
+	if (WARN_ON(!q_spec))
+		return;
+
+	__qpnp_gpio_set(q_chip, q_spec, value);
+}
+
+static int qpnp_gpio_set_direction(struct qpnp_gpio_chip *q_chip,
+				   struct qpnp_gpio_spec *q_spec, int direction)
+{
+	int rc;
+
+	if (!q_chip || !q_spec)
+		return -EINVAL;
+
+	if (direction >= QPNP_GPIO_DIR_INVALID) {
+		pr_err("invalid direction specification %d\n", direction);
+		return -EINVAL;
+	}
+
+	q_reg_clr_set(&q_spec->regs[Q_REG_I_MODE_CTL],
+			Q_REG_MODE_SEL_SHIFT,
+			Q_REG_MODE_SEL_MASK,
+			direction);
+
+	rc = spmi_ext_register_writel(q_chip->spmi->ctrl, q_spec->slave,
+			      Q_REG_ADDR(q_spec, Q_REG_I_MODE_CTL),
+			      &q_spec->regs[Q_REG_I_MODE_CTL], 1);
+	return rc;
+}
+
+static int qpnp_gpio_direction_input(struct gpio_chip *gpio_chip,
+		unsigned offset)
+{
+	struct qpnp_gpio_chip *q_chip = dev_get_drvdata(gpio_chip->dev);
+	struct qpnp_gpio_spec *q_spec;
+
+	if (WARN_ON(!q_chip))
+		return -ENODEV;
+
+	q_spec = qpnp_chip_gpio_get_spec(q_chip, offset);
+	if (WARN_ON(!q_spec))
+		return -ENODEV;
+
+	return qpnp_gpio_set_direction(q_chip, q_spec, QPNP_GPIO_DIR_IN);
+}
+
+static int qpnp_gpio_direction_output(struct gpio_chip *gpio_chip,
+		unsigned offset,
+		int val)
+{
+	int rc;
+	struct qpnp_gpio_chip *q_chip = dev_get_drvdata(gpio_chip->dev);
+	struct qpnp_gpio_spec *q_spec;
+
+	if (WARN_ON(!q_chip))
+		return -ENODEV;
+
+	q_spec = qpnp_chip_gpio_get_spec(q_chip, offset);
+	if (WARN_ON(!q_spec))
+		return -ENODEV;
+
+	rc = __qpnp_gpio_set(q_chip, q_spec, val);
+	if (rc)
+		return rc;
+
+	rc = qpnp_gpio_set_direction(q_chip, q_spec, QPNP_GPIO_DIR_OUT);
+
+	return rc;
+}
+
+static int qpnp_gpio_of_gpio_xlate(struct gpio_chip *gpio_chip,
+				   struct device_node *np,
+				   const void *gpio_spec, u32 *flags)
+{
+	struct qpnp_gpio_chip *q_chip = dev_get_drvdata(gpio_chip->dev);
+	struct qpnp_gpio_spec *q_spec;
+	const __be32 *gpio = gpio_spec;
+	u32 n = be32_to_cpup(gpio);
+
+	if (WARN_ON(gpio_chip->of_gpio_n_cells < 2)) {
+		pr_err("of_gpio_n_cells < 2\n");
+		return -EINVAL;
+	}
+
+	q_spec = qpnp_pmic_gpio_get_spec(q_chip, n);
+	if (!q_spec) {
+		pr_err("no such PMIC gpio %u in device topology\n", n);
+		return -EINVAL;
+	}
+
+	if (flags)
+		*flags = be32_to_cpu(gpio[1]);
+
+	return q_spec->gpio_chip_idx;
+}
+
+static int qpnp_gpio_apply_config(struct qpnp_gpio_chip *q_chip,
+				  struct qpnp_gpio_spec *q_spec)
+{
+	struct qpnp_gpio_cfg param;
+	struct device_node *node = q_spec->node;
+	int rc;
+
+	param.direction    = q_reg_get(&q_spec->regs[Q_REG_I_MODE_CTL],
+				       Q_REG_MODE_SEL_SHIFT,
+				       Q_REG_MODE_SEL_MASK);
+	param.output_type  = q_reg_get(&q_spec->regs[Q_REG_I_DIG_OUT_CTL],
+				       Q_REG_OUT_TYPE_SHIFT,
+				       Q_REG_OUT_TYPE_MASK);
+	param.invert	   = q_reg_get(&q_spec->regs[Q_REG_I_MODE_CTL],
+				       Q_REG_OUT_INVERT_MASK,
+				       Q_REG_OUT_INVERT_MASK);
+	param.pull	   = q_reg_get(&q_spec->regs[Q_REG_I_MODE_CTL],
+				       Q_REG_PULL_SHIFT, Q_REG_PULL_MASK);
+	param.vin_sel	   = q_reg_get(&q_spec->regs[Q_REG_I_DIG_VIN_CTL],
+				       Q_REG_VIN_SHIFT, Q_REG_VIN_MASK);
+	param.out_strength = q_reg_get(&q_spec->regs[Q_REG_I_DIG_OUT_CTL],
+				       Q_REG_OUT_STRENGTH_SHIFT,
+				       Q_REG_OUT_STRENGTH_MASK);
+	param.src_select   = q_reg_get(&q_spec->regs[Q_REG_I_MODE_CTL],
+				       Q_REG_SRC_SEL_SHIFT, Q_REG_SRC_SEL_MASK);
+	param.master_en    = q_reg_get(&q_spec->regs[Q_REG_I_EN_CTL],
+				       Q_REG_MASTER_EN_SHIFT,
+				       Q_REG_MASTER_EN_MASK);
+
+	of_property_read_u32(node, "qcom,direction",
+		&param.direction);
+	of_property_read_u32(node, "qcom,output-type",
+		&param.output_type);
+	of_property_read_u32(node, "qcom,invert",
+		&param.invert);
+	of_property_read_u32(node, "qcom,pull",
+		&param.pull);
+	of_property_read_u32(node, "qcom,vin-sel",
+		&param.vin_sel);
+	of_property_read_u32(node, "qcom,out-strength",
+		&param.out_strength);
+	of_property_read_u32(node, "qcom,src-select",
+		&param.src_select);
+	rc = of_property_read_u32(node, "qcom,master-en",
+		&param.master_en);
+
+	rc = _qpnp_gpio_config(q_chip, q_spec, &param);
+
+	return rc;
+}
+
+static int qpnp_gpio_free_chip(struct qpnp_gpio_chip *q_chip)
+{
+	struct spmi_device *spmi = q_chip->spmi;
+	int rc, i;
+
+	if (q_chip->chip_gpios)
+		for (i = 0; i < spmi->num_dev_node; i++)
+			kfree(q_chip->chip_gpios[i]);
+
+	mutex_lock(&qpnp_gpio_chips_lock);
+	list_del(&q_chip->chip_list);
+	mutex_unlock(&qpnp_gpio_chips_lock);
+	rc = gpiochip_remove(&q_chip->gpio_chip);
+	if (rc)
+		dev_err(&q_chip->spmi->dev, "%s: unable to remove gpio\n",
+				__func__);
+	kfree(q_chip->chip_gpios);
+	kfree(q_chip->pmic_gpios);
+	kfree(q_chip);
+	return rc;
+}
+
+#ifdef CONFIG_GPIO_QPNP_DEBUG
+struct qpnp_gpio_reg {
+	uint32_t addr;
+	uint32_t idx;
+	uint32_t shift;
+	uint32_t mask;
+};
+
+static struct dentry *driver_dfs_dir;
+
+static int qpnp_gpio_reg_attr(enum qpnp_gpio_param_type type,
+			     struct qpnp_gpio_reg *cfg)
+{
+	switch (type) {
+	case Q_GPIO_CFG_DIRECTION:
+		cfg->addr = Q_REG_MODE_CTL;
+		cfg->idx = Q_REG_I_MODE_CTL;
+		cfg->shift = Q_REG_MODE_SEL_SHIFT;
+		cfg->mask = Q_REG_MODE_SEL_MASK;
+		break;
+	case Q_GPIO_CFG_OUTPUT_TYPE:
+		cfg->addr = Q_REG_DIG_OUT_CTL;
+		cfg->idx = Q_REG_I_DIG_OUT_CTL;
+		cfg->shift = Q_REG_OUT_TYPE_SHIFT;
+		cfg->mask = Q_REG_OUT_TYPE_MASK;
+		break;
+	case Q_GPIO_CFG_INVERT:
+		cfg->addr = Q_REG_MODE_CTL;
+		cfg->idx = Q_REG_I_MODE_CTL;
+		cfg->shift = Q_REG_OUT_INVERT_SHIFT;
+		cfg->mask = Q_REG_OUT_INVERT_MASK;
+		break;
+	case Q_GPIO_CFG_PULL:
+		cfg->addr = Q_REG_DIG_PULL_CTL;
+		cfg->idx = Q_REG_I_DIG_PULL_CTL;
+		cfg->shift = Q_REG_PULL_SHIFT;
+		cfg->mask = Q_REG_PULL_MASK;
+		break;
+	case Q_GPIO_CFG_VIN_SEL:
+		cfg->addr = Q_REG_DIG_VIN_CTL;
+		cfg->idx = Q_REG_I_DIG_VIN_CTL;
+		cfg->shift = Q_REG_VIN_SHIFT;
+		cfg->mask = Q_REG_VIN_MASK;
+		break;
+	case Q_GPIO_CFG_OUT_STRENGTH:
+		cfg->addr = Q_REG_DIG_OUT_CTL;
+		cfg->idx = Q_REG_I_DIG_OUT_CTL;
+		cfg->shift = Q_REG_OUT_STRENGTH_SHIFT;
+		cfg->mask = Q_REG_OUT_STRENGTH_MASK;
+		break;
+	case Q_GPIO_CFG_SRC_SELECT:
+		cfg->addr = Q_REG_MODE_CTL;
+		cfg->idx = Q_REG_I_MODE_CTL;
+		cfg->shift = Q_REG_SRC_SEL_SHIFT;
+		cfg->mask = Q_REG_SRC_SEL_MASK;
+		break;
+	case Q_GPIO_CFG_MASTER_EN:
+		cfg->addr = Q_REG_EN_CTL;
+		cfg->idx = Q_REG_I_EN_CTL;
+		cfg->shift = Q_REG_MASTER_EN_SHIFT;
+		cfg->mask = Q_REG_MASTER_EN_MASK;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int qpnp_gpio_debugfs_get(void *data, u64 *val)
+{
+	enum qpnp_gpio_param_type *idx = data;
+	struct qpnp_gpio_spec *q_spec;
+	struct qpnp_gpio_reg cfg = {};
+	int rc;
+
+	rc = qpnp_gpio_reg_attr(*idx, &cfg);
+	if (rc)
+		return rc;
+	q_spec = container_of(idx, struct qpnp_gpio_spec, params[*idx]);
+	*val = q_reg_get(&q_spec->regs[cfg.idx], cfg.shift, cfg.mask);
+	return 0;
+}
+
+static int qpnp_gpio_check_reg_val(enum qpnp_gpio_param_type idx,
+				   struct qpnp_gpio_spec *q_spec,
+				   uint32_t val)
+{
+	switch (idx) {
+	case Q_GPIO_CFG_DIRECTION:
+		if (val >= QPNP_GPIO_DIR_INVALID)
+			return -EINVAL;
+		break;
+	case Q_GPIO_CFG_OUTPUT_TYPE:
+		if ((val >= QPNP_GPIO_OUT_BUF_INVALID) ||
+		   ((val == QPNP_GPIO_OUT_BUF_OPEN_DRAIN_NMOS ||
+		   val == QPNP_GPIO_OUT_BUF_OPEN_DRAIN_PMOS) &&
+		   (q_spec->subtype == Q_GPIO_SUBTYPE_GPIOC_4CH ||
+		   (q_spec->subtype == Q_GPIO_SUBTYPE_GPIOC_8CH))))
+			return -EINVAL;
+		break;
+	case Q_GPIO_CFG_INVERT:
+		if (val >= QPNP_GPIO_INVERT_INVALID)
+			return -EINVAL;
+		break;
+	case Q_GPIO_CFG_PULL:
+		if (val >= QPNP_GPIO_PULL_INVALID)
+			return -EINVAL;
+		break;
+	case Q_GPIO_CFG_VIN_SEL:
+		if (val >= QPNP_GPIO_VIN_INVALID)
+			return -EINVAL;
+		break;
+	case Q_GPIO_CFG_OUT_STRENGTH:
+		if (val >= QPNP_GPIO_OUT_STRENGTH_INVALID ||
+		    val == 0)
+			return -EINVAL;
+		break;
+	case Q_GPIO_CFG_SRC_SELECT:
+		if (val >= QPNP_GPIO_SRC_INVALID)
+			return -EINVAL;
+		break;
+	case Q_GPIO_CFG_MASTER_EN:
+		if (val >= QPNP_GPIO_MASTER_INVALID)
+			return -EINVAL;
+		break;
+	default:
+		pr_err("invalid param type %u specified\n", idx);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int qpnp_gpio_debugfs_set(void *data, u64 val)
+{
+	enum qpnp_gpio_param_type *idx = data;
+	struct qpnp_gpio_spec *q_spec;
+	struct qpnp_gpio_chip *q_chip;
+	struct qpnp_gpio_reg cfg = {};
+	int rc;
+
+	q_spec = container_of(idx, struct qpnp_gpio_spec, params[*idx]);
+	q_chip = q_spec->q_chip;
+
+	rc = qpnp_gpio_check_reg_val(*idx, q_spec, val);
+	if (rc)
+		return rc;
+
+	rc = qpnp_gpio_reg_attr(*idx, &cfg);
+	if (rc)
+		return rc;
+	q_reg_clr_set(&q_spec->regs[cfg.idx], cfg.shift, cfg.mask, val);
+	rc = spmi_ext_register_writel(q_chip->spmi->ctrl, q_spec->slave,
+				      Q_REG_ADDR(q_spec, cfg.addr),
+				      &q_spec->regs[cfg.idx], 1);
+
+	return rc;
+}
+DEFINE_SIMPLE_ATTRIBUTE(qpnp_gpio_fops, qpnp_gpio_debugfs_get,
+			qpnp_gpio_debugfs_set, "%llu\n");
+
+#define DEBUGFS_BUF_SIZE 11 /* supports 2^32 in decimal */
+
+struct qpnp_gpio_debugfs_args {
+	enum qpnp_gpio_param_type type;
+	const char *filename;
+};
+
+static struct qpnp_gpio_debugfs_args dfs_args[] = {
+	{ Q_GPIO_CFG_DIRECTION, "direction" },
+	{ Q_GPIO_CFG_OUTPUT_TYPE, "output_type" },
+	{ Q_GPIO_CFG_INVERT, "invert" },
+	{ Q_GPIO_CFG_PULL, "pull" },
+	{ Q_GPIO_CFG_VIN_SEL, "vin_sel" },
+	{ Q_GPIO_CFG_OUT_STRENGTH, "out_strength" },
+	{ Q_GPIO_CFG_SRC_SELECT, "src_select" },
+	{ Q_GPIO_CFG_MASTER_EN, "master_en" }
+};
+
+static int qpnp_gpio_debugfs_create(struct qpnp_gpio_chip *q_chip)
+{
+	struct spmi_device *spmi = q_chip->spmi;
+	struct device *dev = &spmi->dev;
+	struct qpnp_gpio_spec *q_spec;
+	enum qpnp_gpio_param_type *params;
+	enum qpnp_gpio_param_type type;
+	char pmic_gpio[DEBUGFS_BUF_SIZE];
+	const char *filename;
+	struct dentry *dfs, *dfs_io_dir;
+	int i, j;
+
+	BUG_ON(Q_NUM_PARAMS != ARRAY_SIZE(dfs_args));
+
+	q_chip->dfs_dir = debugfs_create_dir(dev->of_node->name,
+							driver_dfs_dir);
+	if (q_chip->dfs_dir == NULL) {
+		dev_err(dev, "%s: cannot register chip debugfs directory %s\n",
+						__func__, dev->of_node->name);
+		return -ENODEV;
+	}
+
+	for (i = 0; i < spmi->num_dev_node; i++) {
+		q_spec = qpnp_chip_gpio_get_spec(q_chip, i);
+		params = q_spec->params;
+		snprintf(pmic_gpio, DEBUGFS_BUF_SIZE, "%u", q_spec->pmic_gpio);
+		dfs_io_dir = debugfs_create_dir(pmic_gpio,
+							q_chip->dfs_dir);
+		if (dfs_io_dir == NULL)
+			goto dfs_err;
+
+		for (j = 0; j < Q_NUM_PARAMS; j++) {
+			type = dfs_args[j].type;
+			filename = dfs_args[j].filename;
+
+			params[type] = type;
+			dfs = debugfs_create_file(
+					filename,
+					S_IRUGO | S_IWUSR,
+					dfs_io_dir,
+					&q_spec->params[type],
+					&qpnp_gpio_fops);
+			if (dfs == NULL)
+				goto dfs_err;
+		}
+	}
+	return 0;
+dfs_err:
+	dev_err(dev, "%s: cannot register debugfs for pmic gpio %u on"
+				     " chip %s\n", __func__,
+				     q_spec->pmic_gpio, dev->of_node->name);
+	debugfs_remove_recursive(q_chip->dfs_dir);
+	return -ENFILE;
+}
+#else
+static int qpnp_gpio_debugfs_create(struct qpnp_gpio_chip *q_chip)
+{
+	return 0;
+}
+#endif
+
+static int qpnp_gpio_probe(struct spmi_device *spmi)
+{
+	struct qpnp_gpio_chip *q_chip;
+	struct resource *res;
+	struct qpnp_gpio_spec *q_spec;
+	int i, rc;
+	int lowest_gpio = UINT_MAX, highest_gpio = 0;
+	u32 intspec[3], gpio;
+	char buf[2];
+
+	q_chip = kzalloc(sizeof(*q_chip), GFP_KERNEL);
+	if (!q_chip) {
+		dev_err(&spmi->dev, "%s: Can't allocate gpio_chip\n",
+								__func__);
+		return -ENOMEM;
+	}
+	q_chip->spmi = spmi;
+	dev_set_drvdata(&spmi->dev, q_chip);
+
+	mutex_lock(&qpnp_gpio_chips_lock);
+	list_add(&q_chip->chip_list, &qpnp_gpio_chips);
+	mutex_unlock(&qpnp_gpio_chips_lock);
+
+	/* first scan through nodes to find the range required for allocation */
+	for (i = 0; i < spmi->num_dev_node; i++) {
+		rc = of_property_read_u32(spmi->dev_node[i].of_node,
+							"qcom,gpio-num", &gpio);
+		if (rc) {
+			dev_err(&spmi->dev, "%s: unable to get"
+				" qcom,gpio-num property\n", __func__);
+			goto err_probe;
+		}
+
+		if (gpio < lowest_gpio)
+			lowest_gpio = gpio;
+		if (gpio > highest_gpio)
+			highest_gpio = gpio;
+	}
+
+	if (highest_gpio < lowest_gpio) {
+		dev_err(&spmi->dev, "%s: no device nodes specified in"
+					" topology\n", __func__);
+		rc = -EINVAL;
+		goto err_probe;
+	} else if (lowest_gpio == 0) {
+		dev_err(&spmi->dev, "%s: 0 is not a valid PMIC GPIO\n",
+								__func__);
+		rc = -EINVAL;
+		goto err_probe;
+	}
+
+	q_chip->pmic_gpio_lowest = lowest_gpio;
+	q_chip->pmic_gpio_highest = highest_gpio;
+
+	/* allocate gpio lookup tables */
+	q_chip->pmic_gpios = kzalloc(sizeof(struct qpnp_gpio_spec *) *
+						highest_gpio - lowest_gpio + 1,
+						GFP_KERNEL);
+	q_chip->chip_gpios = kzalloc(sizeof(struct qpnp_gpio_spec *) *
+						spmi->num_dev_node, GFP_KERNEL);
+	if (!q_chip->pmic_gpios || !q_chip->chip_gpios) {
+		dev_err(&spmi->dev, "%s: unable to allocate memory\n",
+								__func__);
+		rc = -ENOMEM;
+		goto err_probe;
+	}
+
+	/* get interrupt controller device_node */
+	q_chip->int_ctrl = of_irq_find_parent(spmi->dev.of_node);
+	if (!q_chip->int_ctrl) {
+		dev_err(&spmi->dev, "%s: Can't find interrupt parent\n",
+								__func__);
+		rc = -EINVAL;
+		goto err_probe;
+	}
+
+	/* now scan through again and populate the lookup table */
+	for (i = 0; i < spmi->num_dev_node; i++) {
+		res = qpnp_get_resource(spmi, i, IORESOURCE_MEM, 0);
+		if (!res) {
+			dev_err(&spmi->dev, "%s: node %s is missing has no"
+				" base address definition\n",
+				__func__, spmi->dev_node[i].of_node->full_name);
+		}
+
+		rc = of_property_read_u32(spmi->dev_node[i].of_node,
+							"qcom,gpio-num", &gpio);
+		if (rc) {
+			dev_err(&spmi->dev, "%s: unable to get"
+				" qcom,gpio-num property\n", __func__);
+			goto err_probe;
+		}
+
+		q_spec = kzalloc(sizeof(struct qpnp_gpio_spec),
+							GFP_KERNEL);
+		if (!q_spec) {
+			dev_err(&spmi->dev, "%s: unable to allocate"
+						" memory\n",
+					__func__);
+			rc = -ENOMEM;
+			goto err_probe;
+		}
+
+		q_spec->slave = spmi->sid;
+		q_spec->offset = res->start;
+		q_spec->gpio_chip_idx = i;
+		q_spec->pmic_gpio = gpio;
+		q_spec->node = spmi->dev_node[i].of_node;
+		q_spec->q_chip = q_chip;
+
+		rc = spmi_ext_register_readl(spmi->ctrl, q_spec->slave,
+				Q_REG_ADDR(q_spec, Q_REG_TYPE), &buf[0], 2);
+		if (rc) {
+			dev_err(&spmi->dev, "%s: unable to read type regs\n",
+						__func__);
+			goto err_probe;
+		}
+		q_spec->type	= buf[0];
+		q_spec->subtype = buf[1];
+
+		/* call into irq_domain to get irq mapping */
+		intspec[0] = q_chip->spmi->sid;
+		intspec[1] = (q_spec->offset >> 8) & 0xFF;
+		intspec[2] = 0;
+		q_spec->irq = irq_create_of_mapping(q_chip->int_ctrl,
+							intspec, 3);
+		if (!q_spec->irq) {
+			dev_err(&spmi->dev, "%s: invalid irq for gpio"
+					" %u\n", __func__, gpio);
+			rc = -EINVAL;
+			goto err_probe;
+		}
+		/* initialize lookup table params */
+		qpnp_pmic_gpio_set_spec(q_chip, gpio, q_spec);
+		qpnp_chip_gpio_set_spec(q_chip, i, q_spec);
+	}
+
+	q_chip->gpio_chip.base = -1;
+	q_chip->gpio_chip.ngpio = spmi->num_dev_node;
+	q_chip->gpio_chip.label = "qpnp-gpio";
+	q_chip->gpio_chip.direction_input = qpnp_gpio_direction_input;
+	q_chip->gpio_chip.direction_output = qpnp_gpio_direction_output;
+	q_chip->gpio_chip.to_irq = qpnp_gpio_to_irq;
+	q_chip->gpio_chip.get = qpnp_gpio_get;
+	q_chip->gpio_chip.set = qpnp_gpio_set;
+	q_chip->gpio_chip.dev = &spmi->dev;
+	q_chip->gpio_chip.of_xlate = qpnp_gpio_of_gpio_xlate;
+	q_chip->gpio_chip.of_gpio_n_cells = 2;
+	q_chip->gpio_chip.can_sleep = 0;
+
+	rc = gpiochip_add(&q_chip->gpio_chip);
+	if (rc) {
+		dev_err(&spmi->dev, "%s: Can't add gpio chip, rc = %d\n",
+								__func__, rc);
+		goto err_probe;
+	}
+
+	/* now configure gpio config defaults if they exist */
+	for (i = 0; i < spmi->num_dev_node; i++) {
+		q_spec = qpnp_chip_gpio_get_spec(q_chip, i);
+		if (WARN_ON(!q_spec)) {
+			rc = -ENODEV;
+			goto err_probe;
+		}
+
+		rc = qpnp_gpio_cache_regs(q_chip, q_spec);
+		if (rc)
+			goto err_probe;
+
+		rc = qpnp_gpio_apply_config(q_chip, q_spec);
+		if (rc)
+			goto err_probe;
+	}
+
+	dev_dbg(&spmi->dev, "%s: gpio_chip registered between %d-%u\n",
+			__func__, q_chip->gpio_chip.base,
+			(q_chip->gpio_chip.base + q_chip->gpio_chip.ngpio) - 1);
+
+	rc = qpnp_gpio_debugfs_create(q_chip);
+	if (rc) {
+		dev_err(&spmi->dev, "%s: debugfs creation failed\n", __func__);
+		goto err_probe;
+	}
+
+	return 0;
+
+err_probe:
+	qpnp_gpio_free_chip(q_chip);
+	return rc;
+}
+
+static int qpnp_gpio_remove(struct spmi_device *spmi)
+{
+	struct qpnp_gpio_chip *q_chip = dev_get_drvdata(&spmi->dev);
+
+	debugfs_remove_recursive(q_chip->dfs_dir);
+
+	return qpnp_gpio_free_chip(q_chip);
+}
+
+static struct of_device_id spmi_match_table[] = {
+	{	.compatible = "qcom,qpnp-gpio",
+	},
+	{}
+};
+
+static const struct spmi_device_id qpnp_gpio_id[] = {
+	{ "qcom,qpnp-gpio", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(spmi, qpnp_gpio_id);
+
+static struct spmi_driver qpnp_gpio_driver = {
+	.driver		= {
+		.name	= "qcom,qpnp-gpio",
+		.of_match_table = spmi_match_table,
+	},
+	.probe		= qpnp_gpio_probe,
+	.remove		= qpnp_gpio_remove,
+	.id_table	= qpnp_gpio_id,
+};
+
+static int __init qpnp_gpio_init(void)
+{
+#ifdef CONFIG_GPIO_QPNP_DEBUG
+	driver_dfs_dir = debugfs_create_dir("qpnp_gpio", NULL);
+	if (driver_dfs_dir == NULL)
+		pr_err("Cannot register top level debugfs directory\n");
+#endif
+
+	return spmi_driver_register(&qpnp_gpio_driver);
+}
+
+static void __exit qpnp_gpio_exit(void)
+{
+#ifdef CONFIG_GPIO_QPNP_DEBUG
+	debugfs_remove_recursive(driver_dfs_dir);
+#endif
+	spmi_driver_unregister(&qpnp_gpio_driver);
+}
+
+MODULE_DESCRIPTION("QPNP PMIC gpio driver");
+MODULE_LICENSE("GPL v2");
+
+module_init(qpnp_gpio_init);
+module_exit(qpnp_gpio_exit);
diff --git a/drivers/gpu/Makefile b/drivers/gpu/Makefile
index ca2d3b3..01cef64 100644
--- a/drivers/gpu/Makefile
+++ b/drivers/gpu/Makefile
@@ -1 +1,2 @@
 obj-y			+= drm/ vga/ stub/ ion/
+obj-$(CONFIG_MSM_KGSL)	+= msm/
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 9d24d65..b114875 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -3737,6 +3737,10 @@
 # define GEN6_RCPBUNIT_CLOCK_GATE_DISABLE		(1 << 12)
 # define GEN6_RCCUNIT_CLOCK_GATE_DISABLE		(1 << 11)
 
+#define GEN6_UCGCTL2				0x9404
+# define GEN6_RCPBUNIT_CLOCK_GATE_DISABLE		(1 << 12)
+# define GEN6_RCCUNIT_CLOCK_GATE_DISABLE		(1 << 11)
+
 #define GEN6_RPNSWREQ				0xA008
 #define   GEN6_TURBO_DISABLE			(1<<31)
 #define   GEN6_FREQUENCY(x)			((x)<<25)
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
index f7eb5d8..48dae40 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -439,6 +439,9 @@
 	struct radeon_i2c_chan *ddc_bus;
 	/* some systems have an hdmi and vga port with a shared ddc line */
 	bool shared_ddc;
+	/* for some Radeon chip families we apply an additional EDID header
+	   check as part of the DDC probe */
+	bool requires_extended_probe;
 	bool use_digital;
 	/* we need to mind the EDID between detect
 	   and get modes due to analog/digital/tvencoder */
@@ -526,7 +529,8 @@
 				u8 val);
 extern void radeon_router_select_ddc_port(struct radeon_connector *radeon_connector);
 extern void radeon_router_select_cd_port(struct radeon_connector *radeon_connector);
-extern bool radeon_ddc_probe(struct radeon_connector *radeon_connector);
+extern bool radeon_ddc_probe(struct radeon_connector *radeon_connector,
+			bool requires_extended_probe);
 extern int radeon_ddc_get_modes(struct radeon_connector *radeon_connector);
 
 extern struct drm_encoder *radeon_best_encoder(struct drm_connector *connector);
diff --git a/drivers/gpu/ion/Kconfig b/drivers/gpu/ion/Kconfig
index b5bfdb4..5bb254b 100644
--- a/drivers/gpu/ion/Kconfig
+++ b/drivers/gpu/ion/Kconfig
@@ -11,3 +11,8 @@
 	help
 	  Choose this option if you wish to use ion on an nVidia Tegra.
 
+config ION_MSM
+	tristate "Ion for MSM"
+	depends on ARCH_MSM && ION
+	help
+	  Choose this option if you wish to use ion on an MSM target.
diff --git a/drivers/gpu/ion/Makefile b/drivers/gpu/ion/Makefile
index 73fe3fa..c9e8a94 100644
--- a/drivers/gpu/ion/Makefile
+++ b/drivers/gpu/ion/Makefile
@@ -1,2 +1,3 @@
-obj-$(CONFIG_ION) +=	ion.o ion_heap.o ion_system_heap.o ion_carveout_heap.o
+obj-$(CONFIG_ION) +=	ion.o ion_heap.o ion_system_heap.o ion_carveout_heap.o ion_iommu_heap.o ion_cp_heap.o
 obj-$(CONFIG_ION_TEGRA) += tegra/
+obj-$(CONFIG_ION_MSM) += msm/
diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c
index f8cb55f..7e84aa7 100644
--- a/drivers/gpu/ion/ion.c
+++ b/drivers/gpu/ion/ion.c
@@ -2,6 +2,7 @@
  * drivers/gpu/ion/ion.c
  *
  * Copyright (C) 2011 Google, Inc.
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -14,15 +15,14 @@
  *
  */
 
+#include <linux/module.h>
 #include <linux/device.h>
 #include <linux/file.h>
 #include <linux/fs.h>
 #include <linux/anon_inodes.h>
 #include <linux/ion.h>
 #include <linux/list.h>
-#include <linux/memblock.h>
 #include <linux/miscdevice.h>
-#include <linux/export.h>
 #include <linux/mm.h>
 #include <linux/mm_types.h>
 #include <linux/rbtree.h>
@@ -31,8 +31,8 @@
 #include <linux/seq_file.h>
 #include <linux/uaccess.h>
 #include <linux/debugfs.h>
-#include <linux/dma-buf.h>
 
+#include <mach/iommu_domains.h>
 #include "ion_priv.h"
 #define DEBUG
 
@@ -51,12 +51,14 @@
 	struct rb_root heaps;
 	long (*custom_ioctl) (struct ion_client *client, unsigned int cmd,
 			      unsigned long arg);
-	struct rb_root clients;
+	struct rb_root user_clients;
+	struct rb_root kernel_clients;
 	struct dentry *debug_root;
 };
 
 /**
  * struct ion_client - a process/hw block local address space
+ * @ref:		for reference counting the client
  * @node:		node in the tree of all clients
  * @dev:		backpointer to ion device
  * @handles:		an rb tree of all the handles in this client
@@ -70,12 +72,13 @@
  * as well as the handles themselves, and should be held while modifying either.
  */
 struct ion_client {
+	struct kref ref;
 	struct rb_node node;
 	struct ion_device *dev;
 	struct rb_root handles;
 	struct mutex lock;
 	unsigned int heap_mask;
-	const char *name;
+	char *name;
 	struct task_struct *task;
 	pid_t pid;
 	struct dentry *debug_root;
@@ -89,6 +92,7 @@
  * @node:		node in the client's handle rbtree
  * @kmap_cnt:		count of times this client has mapped to kernel
  * @dmap_cnt:		count of times this client has mapped for dma
+ * @usermap_cnt:	count of times this client has mapped for userspace
  *
  * Modifications to node, map_cnt or mapping should be protected by the
  * lock in the client.  Other fields are never changed after initialization.
@@ -99,8 +103,31 @@
 	struct ion_buffer *buffer;
 	struct rb_node node;
 	unsigned int kmap_cnt;
+	unsigned int dmap_cnt;
+	unsigned int usermap_cnt;
+	unsigned int iommu_map_cnt;
 };
 
+static void ion_iommu_release(struct kref *kref);
+
+static int ion_validate_buffer_flags(struct ion_buffer *buffer,
+					unsigned long flags)
+{
+	if (buffer->kmap_cnt || buffer->dmap_cnt || buffer->umap_cnt ||
+		buffer->iommu_map_cnt) {
+		if (buffer->flags != flags) {
+			pr_err("%s: buffer was already mapped with flags %lx,"
+				" cannot map with flags %lx\n", __func__,
+				buffer->flags, flags);
+			return 1;
+		}
+
+	} else {
+		buffer->flags = flags;
+	}
+	return 0;
+}
+
 /* this function should only be called while dev->lock is held */
 static void ion_buffer_add(struct ion_device *dev,
 			   struct ion_buffer *buffer)
@@ -127,6 +154,61 @@
 	rb_insert_color(&buffer->node, &dev->buffers);
 }
 
+static void ion_iommu_add(struct ion_buffer *buffer,
+			  struct ion_iommu_map *iommu)
+{
+	struct rb_node **p = &buffer->iommu_maps.rb_node;
+	struct rb_node *parent = NULL;
+	struct ion_iommu_map *entry;
+
+	while (*p) {
+		parent = *p;
+		entry = rb_entry(parent, struct ion_iommu_map, node);
+
+		if (iommu->key < entry->key) {
+			p = &(*p)->rb_left;
+		} else if (iommu->key > entry->key) {
+			p = &(*p)->rb_right;
+		} else {
+			pr_err("%s: buffer %p already has mapping for domain %d"
+				" and partition %d\n", __func__,
+				buffer,
+				iommu_map_domain(iommu),
+				iommu_map_partition(iommu));
+			BUG();
+		}
+	}
+
+	rb_link_node(&iommu->node, parent, p);
+	rb_insert_color(&iommu->node, &buffer->iommu_maps);
+
+}
+
+static struct ion_iommu_map *ion_iommu_lookup(struct ion_buffer *buffer,
+						unsigned int domain_no,
+						unsigned int partition_no)
+{
+	struct rb_node **p = &buffer->iommu_maps.rb_node;
+	struct rb_node *parent = NULL;
+	struct ion_iommu_map *entry;
+	uint64_t key = domain_no;
+	key = key << 32 | partition_no;
+
+	while (*p) {
+		parent = *p;
+		entry = rb_entry(parent, struct ion_iommu_map, node);
+
+		if (key < entry->key)
+			p = &(*p)->rb_left;
+		else if (key > entry->key)
+			p = &(*p)->rb_right;
+		else
+			return entry;
+	}
+
+	return NULL;
+}
+
 /* this function should only be called while dev->lock is held */
 static struct ion_buffer *ion_buffer_create(struct ion_heap *heap,
 				     struct ion_device *dev,
@@ -135,7 +217,6 @@
 				     unsigned long flags)
 {
 	struct ion_buffer *buffer;
-	struct sg_table *table;
 	int ret;
 
 	buffer = kzalloc(sizeof(struct ion_buffer), GFP_KERNEL);
@@ -150,15 +231,6 @@
 		kfree(buffer);
 		return ERR_PTR(ret);
 	}
-
-	table = buffer->heap->ops->map_dma(buffer->heap, buffer);
-	if (IS_ERR_OR_NULL(table)) {
-		heap->ops->free(buffer);
-		kfree(buffer);
-		return ERR_PTR(PTR_ERR(table));
-	}
-	buffer->sg_table = table;
-
 	buffer->dev = dev;
 	buffer->size = len;
 	mutex_init(&buffer->lock);
@@ -166,15 +238,44 @@
 	return buffer;
 }
 
+/**
+ * Check for delayed IOMMU unmapping. Also unmap any outstanding
+ * mappings which would otherwise have been leaked.
+ */
+static void ion_iommu_delayed_unmap(struct ion_buffer *buffer)
+{
+	struct ion_iommu_map *iommu_map;
+	struct rb_node *node;
+	const struct rb_root *rb = &(buffer->iommu_maps);
+	unsigned long ref_count;
+	unsigned int delayed_unmap;
+
+	mutex_lock(&buffer->lock);
+
+	while ((node = rb_first(rb)) != 0) {
+		iommu_map = rb_entry(node, struct ion_iommu_map, node);
+		ref_count = atomic_read(&iommu_map->ref.refcount);
+		delayed_unmap = iommu_map->flags & ION_IOMMU_UNMAP_DELAYED;
+
+		if ((delayed_unmap && ref_count > 1) || !delayed_unmap) {
+			pr_err("%s: Virtual memory address leak in domain %u, partition %u\n",
+				__func__, iommu_map->domain_info[DI_DOMAIN_NUM],
+				iommu_map->domain_info[DI_PARTITION_NUM]);
+		}
+		/* set ref count to 1 to force release */
+		kref_init(&iommu_map->ref);
+		kref_put(&iommu_map->ref, ion_iommu_release);
+	}
+
+	mutex_unlock(&buffer->lock);
+}
+
 static void ion_buffer_destroy(struct kref *kref)
 {
 	struct ion_buffer *buffer = container_of(kref, struct ion_buffer, ref);
 	struct ion_device *dev = buffer->dev;
 
-	if (WARN_ON(buffer->kmap_cnt > 0))
-		buffer->heap->ops->unmap_kernel(buffer->heap, buffer);
-
-	buffer->heap->ops->unmap_dma(buffer->heap, buffer);
+	ion_iommu_delayed_unmap(buffer);
 	buffer->heap->ops->free(buffer);
 	mutex_lock(&dev->lock);
 	rb_erase(&buffer->node, &dev->buffers);
@@ -209,26 +310,17 @@
 	return handle;
 }
 
-static void ion_handle_kmap_put(struct ion_handle *);
-
+/* Client lock must be locked when calling */
 static void ion_handle_destroy(struct kref *kref)
 {
 	struct ion_handle *handle = container_of(kref, struct ion_handle, ref);
-	struct ion_client *client = handle->client;
-	struct ion_buffer *buffer = handle->buffer;
-
-	mutex_lock(&client->lock);
-
-	mutex_lock(&buffer->lock);
-	while (buffer->kmap_cnt)
-		ion_handle_kmap_put(handle);
-	mutex_unlock(&buffer->lock);
-
+	/* XXX Can a handle be destroyed while it's map count is non-zero?:
+	   if (handle->map_cnt) unmap
+	 */
+	WARN_ON(handle->kmap_cnt || handle->dmap_cnt || handle->usermap_cnt);
+	ion_buffer_put(handle->buffer);
 	if (!RB_EMPTY_NODE(&handle->node))
-		rb_erase(&handle->node, &client->handles);
-	mutex_unlock(&client->lock);
-
-	ion_buffer_put(buffer);
+		rb_erase(&handle->node, &handle->client->handles);
 	kfree(handle);
 }
 
@@ -307,6 +399,12 @@
 	struct ion_handle *handle;
 	struct ion_device *dev = client->dev;
 	struct ion_buffer *buffer = NULL;
+	unsigned long secure_allocation = flags & ION_SECURE;
+	const unsigned int MAX_DBG_STR_LEN = 64;
+	char dbg_str[MAX_DBG_STR_LEN];
+	unsigned int dbg_str_idx = 0;
+
+	dbg_str[0] = '\0';
 
 	/*
 	 * traverse the list of heaps available in this system in priority
@@ -314,11 +412,6 @@
 	 * request of the caller allocate from it.  Repeat until allocate has
 	 * succeeded or all heaps have been tried
 	 */
-	if (WARN_ON(!len))
-		return ERR_PTR(-EINVAL);
-
-	len = PAGE_ALIGN(len);
-
 	mutex_lock(&dev->lock);
 	for (n = rb_first(&dev->heaps); n != NULL; n = rb_next(n)) {
 		struct ion_heap *heap = rb_entry(n, struct ion_heap, node);
@@ -328,35 +421,59 @@
 		/* if the caller didn't specify this heap type */
 		if (!((1 << heap->id) & flags))
 			continue;
+		/* Do not allow un-secure heap if secure is specified */
+		if (secure_allocation && (heap->type != ION_HEAP_TYPE_CP))
+			continue;
 		buffer = ion_buffer_create(heap, dev, len, align, flags);
 		if (!IS_ERR_OR_NULL(buffer))
 			break;
+		if (dbg_str_idx < MAX_DBG_STR_LEN) {
+			unsigned int len_left = MAX_DBG_STR_LEN-dbg_str_idx-1;
+			int ret_value = snprintf(&dbg_str[dbg_str_idx],
+						len_left, "%s ", heap->name);
+			if (ret_value >= len_left) {
+				/* overflow */
+				dbg_str[MAX_DBG_STR_LEN-1] = '\0';
+				dbg_str_idx = MAX_DBG_STR_LEN;
+			} else if (ret_value >= 0) {
+				dbg_str_idx += ret_value;
+			} else {
+				/* error */
+				dbg_str[MAX_DBG_STR_LEN-1] = '\0';
+			}
+		}
 	}
 	mutex_unlock(&dev->lock);
 
-	if (buffer == NULL)
-		return ERR_PTR(-ENODEV);
-
-	if (IS_ERR(buffer))
+	if (IS_ERR_OR_NULL(buffer)) {
+		pr_debug("ION is unable to allocate 0x%x bytes (alignment: "
+			 "0x%x) from heap(s) %sfor client %s with heap "
+			 "mask 0x%x\n",
+			len, align, dbg_str, client->name, client->heap_mask);
 		return ERR_PTR(PTR_ERR(buffer));
+	}
 
 	handle = ion_handle_create(client, buffer);
 
+	if (IS_ERR_OR_NULL(handle))
+		goto end;
+
 	/*
 	 * ion_buffer_create will create a buffer with a ref_cnt of 1,
 	 * and ion_handle_create will take a second reference, drop one here
 	 */
 	ion_buffer_put(buffer);
 
-	if (!IS_ERR(handle)) {
-		mutex_lock(&client->lock);
-		ion_handle_add(client, handle);
-		mutex_unlock(&client->lock);
-	}
+	mutex_lock(&client->lock);
+	ion_handle_add(client, handle);
+	mutex_unlock(&client->lock);
+	return handle;
 
-
+end:
+	ion_buffer_put(buffer);
 	return handle;
 }
+EXPORT_SYMBOL(ion_alloc);
 
 void ion_free(struct ion_client *client, struct ion_handle *handle)
 {
@@ -366,13 +483,46 @@
 
 	mutex_lock(&client->lock);
 	valid_handle = ion_handle_validate(client, handle);
-	mutex_unlock(&client->lock);
-
 	if (!valid_handle) {
-		WARN("%s: invalid handle passed to free.\n", __func__);
+		mutex_unlock(&client->lock);
+		WARN(1, "%s: invalid handle passed to free.\n", __func__);
 		return;
 	}
 	ion_handle_put(handle);
+	mutex_unlock(&client->lock);
+}
+EXPORT_SYMBOL(ion_free);
+
+static void ion_client_get(struct ion_client *client);
+static int ion_client_put(struct ion_client *client);
+
+static bool _ion_map(int *buffer_cnt, int *handle_cnt)
+{
+	bool map;
+
+	BUG_ON(*handle_cnt != 0 && *buffer_cnt == 0);
+
+	if (*buffer_cnt)
+		map = false;
+	else
+		map = true;
+	if (*handle_cnt == 0)
+		(*buffer_cnt)++;
+	(*handle_cnt)++;
+	return map;
+}
+
+static bool _ion_unmap(int *buffer_cnt, int *handle_cnt)
+{
+	BUG_ON(*handle_cnt == 0);
+	(*handle_cnt)--;
+	if (*handle_cnt != 0)
+		return false;
+	BUG_ON(*buffer_cnt == 0);
+	(*buffer_cnt)--;
+	if (*buffer_cnt == 0)
+		return true;
+	return false;
 }
 
 int ion_phys(struct ion_client *client, struct ion_handle *handle,
@@ -399,58 +549,10 @@
 	ret = buffer->heap->ops->phys(buffer->heap, buffer, addr, len);
 	return ret;
 }
+EXPORT_SYMBOL(ion_phys);
 
-static void *ion_buffer_kmap_get(struct ion_buffer *buffer)
-{
-	void *vaddr;
-
-	if (buffer->kmap_cnt) {
-		buffer->kmap_cnt++;
-		return buffer->vaddr;
-	}
-	vaddr = buffer->heap->ops->map_kernel(buffer->heap, buffer);
-	if (IS_ERR_OR_NULL(vaddr))
-		return vaddr;
-	buffer->vaddr = vaddr;
-	buffer->kmap_cnt++;
-	return vaddr;
-}
-
-static void *ion_handle_kmap_get(struct ion_handle *handle)
-{
-	struct ion_buffer *buffer = handle->buffer;
-	void *vaddr;
-
-	if (handle->kmap_cnt) {
-		handle->kmap_cnt++;
-		return buffer->vaddr;
-	}
-	vaddr = ion_buffer_kmap_get(buffer);
-	if (IS_ERR_OR_NULL(vaddr))
-		return vaddr;
-	handle->kmap_cnt++;
-	return vaddr;
-}
-
-static void ion_buffer_kmap_put(struct ion_buffer *buffer)
-{
-	buffer->kmap_cnt--;
-	if (!buffer->kmap_cnt) {
-		buffer->heap->ops->unmap_kernel(buffer->heap, buffer);
-		buffer->vaddr = NULL;
-	}
-}
-
-static void ion_handle_kmap_put(struct ion_handle *handle)
-{
-	struct ion_buffer *buffer = handle->buffer;
-
-	handle->kmap_cnt--;
-	if (!handle->kmap_cnt)
-		ion_buffer_kmap_put(buffer);
-}
-
-void *ion_map_kernel(struct ion_client *client, struct ion_handle *handle)
+void *ion_map_kernel(struct ion_client *client, struct ion_handle *handle,
+			unsigned long flags)
 {
 	struct ion_buffer *buffer;
 	void *vaddr;
@@ -464,20 +566,267 @@
 	}
 
 	buffer = handle->buffer;
+	mutex_lock(&buffer->lock);
 
 	if (!handle->buffer->heap->ops->map_kernel) {
 		pr_err("%s: map_kernel is not implemented by this heap.\n",
 		       __func__);
+		mutex_unlock(&buffer->lock);
 		mutex_unlock(&client->lock);
 		return ERR_PTR(-ENODEV);
 	}
 
-	mutex_lock(&buffer->lock);
-	vaddr = ion_handle_kmap_get(handle);
+	if (ion_validate_buffer_flags(buffer, flags)) {
+			vaddr = ERR_PTR(-EEXIST);
+			goto out;
+	}
+
+	if (_ion_map(&buffer->kmap_cnt, &handle->kmap_cnt)) {
+		vaddr = buffer->heap->ops->map_kernel(buffer->heap, buffer,
+							flags);
+		if (IS_ERR_OR_NULL(vaddr))
+			_ion_unmap(&buffer->kmap_cnt, &handle->kmap_cnt);
+		buffer->vaddr = vaddr;
+	} else {
+		vaddr = buffer->vaddr;
+	}
+
+out:
 	mutex_unlock(&buffer->lock);
 	mutex_unlock(&client->lock);
 	return vaddr;
 }
+EXPORT_SYMBOL(ion_map_kernel);
+
+static struct ion_iommu_map *__ion_iommu_map(struct ion_buffer *buffer,
+		int domain_num, int partition_num, unsigned long align,
+		unsigned long iova_length, unsigned long flags,
+		unsigned long *iova)
+{
+	struct ion_iommu_map *data;
+	int ret;
+
+	data = kmalloc(sizeof(*data), GFP_ATOMIC);
+
+	if (!data)
+		return ERR_PTR(-ENOMEM);
+
+	data->buffer = buffer;
+	iommu_map_domain(data) = domain_num;
+	iommu_map_partition(data) = partition_num;
+
+	ret = buffer->heap->ops->map_iommu(buffer, data,
+						domain_num,
+						partition_num,
+						align,
+						iova_length,
+						flags);
+
+	if (ret)
+		goto out;
+
+	kref_init(&data->ref);
+	*iova = data->iova_addr;
+
+	ion_iommu_add(buffer, data);
+
+	return data;
+
+out:
+	kfree(data);
+	return ERR_PTR(ret);
+}
+
+int ion_map_iommu(struct ion_client *client, struct ion_handle *handle,
+			int domain_num, int partition_num, unsigned long align,
+			unsigned long iova_length, unsigned long *iova,
+			unsigned long *buffer_size,
+			unsigned long flags, unsigned long iommu_flags)
+{
+	struct ion_buffer *buffer;
+	struct ion_iommu_map *iommu_map;
+	int ret = 0;
+
+	if (ION_IS_CACHED(flags)) {
+		pr_err("%s: Cannot map iommu as cached.\n", __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&client->lock);
+	if (!ion_handle_validate(client, handle)) {
+		pr_err("%s: invalid handle passed to map_kernel.\n",
+		       __func__);
+		mutex_unlock(&client->lock);
+		return -EINVAL;
+	}
+
+	buffer = handle->buffer;
+	mutex_lock(&buffer->lock);
+
+	if (!handle->buffer->heap->ops->map_iommu) {
+		pr_err("%s: map_iommu is not implemented by this heap.\n",
+		       __func__);
+		ret = -ENODEV;
+		goto out;
+	}
+
+	/*
+	 * If clients don't want a custom iova length, just use whatever
+	 * the buffer size is
+	 */
+	if (!iova_length)
+		iova_length = buffer->size;
+
+	if (buffer->size > iova_length) {
+		pr_debug("%s: iova length %lx is not at least buffer size"
+			" %x\n", __func__, iova_length, buffer->size);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (buffer->size & ~PAGE_MASK) {
+		pr_debug("%s: buffer size %x is not aligned to %lx", __func__,
+			buffer->size, PAGE_SIZE);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (iova_length & ~PAGE_MASK) {
+		pr_debug("%s: iova_length %lx is not aligned to %lx", __func__,
+			iova_length, PAGE_SIZE);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	iommu_map = ion_iommu_lookup(buffer, domain_num, partition_num);
+	_ion_map(&buffer->iommu_map_cnt, &handle->iommu_map_cnt);
+	if (!iommu_map) {
+		iommu_map = __ion_iommu_map(buffer, domain_num, partition_num,
+					    align, iova_length, flags, iova);
+		if (IS_ERR_OR_NULL(iommu_map)) {
+			_ion_unmap(&buffer->iommu_map_cnt,
+				   &handle->iommu_map_cnt);
+		} else {
+			iommu_map->flags = iommu_flags;
+
+			if (iommu_map->flags & ION_IOMMU_UNMAP_DELAYED)
+				kref_get(&iommu_map->ref);
+		}
+	} else {
+		if (iommu_map->flags != iommu_flags) {
+			pr_err("%s: handle %p is already mapped with iommu flags %lx, trying to map with flags %lx\n",
+				__func__, handle,
+				iommu_map->flags, iommu_flags);
+			_ion_unmap(&buffer->iommu_map_cnt,
+				   &handle->iommu_map_cnt);
+			ret = -EINVAL;
+		} else if (iommu_map->mapped_size != iova_length) {
+			pr_err("%s: handle %p is already mapped with length"
+					" %x, trying to map with length %lx\n",
+				__func__, handle, iommu_map->mapped_size,
+				iova_length);
+			_ion_unmap(&buffer->iommu_map_cnt,
+				   &handle->iommu_map_cnt);
+			ret = -EINVAL;
+		} else {
+			kref_get(&iommu_map->ref);
+			*iova = iommu_map->iova_addr;
+		}
+	}
+	*buffer_size = buffer->size;
+out:
+	mutex_unlock(&buffer->lock);
+	mutex_unlock(&client->lock);
+	return ret;
+}
+EXPORT_SYMBOL(ion_map_iommu);
+
+static void ion_iommu_release(struct kref *kref)
+{
+	struct ion_iommu_map *map = container_of(kref, struct ion_iommu_map,
+						ref);
+	struct ion_buffer *buffer = map->buffer;
+
+	rb_erase(&map->node, &buffer->iommu_maps);
+	buffer->heap->ops->unmap_iommu(map);
+	kfree(map);
+}
+
+void ion_unmap_iommu(struct ion_client *client, struct ion_handle *handle,
+			int domain_num, int partition_num)
+{
+	struct ion_iommu_map *iommu_map;
+	struct ion_buffer *buffer;
+
+	mutex_lock(&client->lock);
+	buffer = handle->buffer;
+
+	mutex_lock(&buffer->lock);
+
+	iommu_map = ion_iommu_lookup(buffer, domain_num, partition_num);
+
+	if (!iommu_map) {
+		WARN(1, "%s: (%d,%d) was never mapped for %p\n", __func__,
+				domain_num, partition_num, buffer);
+		goto out;
+	}
+
+	_ion_unmap(&buffer->iommu_map_cnt, &handle->iommu_map_cnt);
+	kref_put(&iommu_map->ref, ion_iommu_release);
+
+out:
+	mutex_unlock(&buffer->lock);
+
+	mutex_unlock(&client->lock);
+
+}
+EXPORT_SYMBOL(ion_unmap_iommu);
+
+struct scatterlist *ion_map_dma(struct ion_client *client,
+				struct ion_handle *handle,
+				unsigned long flags)
+{
+	struct ion_buffer *buffer;
+	struct scatterlist *sglist;
+
+	mutex_lock(&client->lock);
+	if (!ion_handle_validate(client, handle)) {
+		pr_err("%s: invalid handle passed to map_dma.\n",
+		       __func__);
+		mutex_unlock(&client->lock);
+		return ERR_PTR(-EINVAL);
+	}
+	buffer = handle->buffer;
+	mutex_lock(&buffer->lock);
+
+	if (!handle->buffer->heap->ops->map_dma) {
+		pr_err("%s: map_kernel is not implemented by this heap.\n",
+		       __func__);
+		mutex_unlock(&buffer->lock);
+		mutex_unlock(&client->lock);
+		return ERR_PTR(-ENODEV);
+	}
+
+	if (ion_validate_buffer_flags(buffer, flags)) {
+		sglist = ERR_PTR(-EEXIST);
+		goto out;
+	}
+
+	if (_ion_map(&buffer->dmap_cnt, &handle->dmap_cnt)) {
+		sglist = buffer->heap->ops->map_dma(buffer->heap, buffer);
+		if (IS_ERR_OR_NULL(sglist))
+			_ion_unmap(&buffer->dmap_cnt, &handle->dmap_cnt);
+		buffer->sglist = sglist;
+	} else {
+		sglist = buffer->sglist;
+	}
+
+out:
+	mutex_unlock(&buffer->lock);
+	mutex_unlock(&client->lock);
+	return sglist;
+}
+EXPORT_SYMBOL(ion_map_dma);
 
 void ion_unmap_kernel(struct ion_client *client, struct ion_handle *handle)
 {
@@ -486,18 +835,173 @@
 	mutex_lock(&client->lock);
 	buffer = handle->buffer;
 	mutex_lock(&buffer->lock);
-	ion_handle_kmap_put(handle);
+	if (_ion_unmap(&buffer->kmap_cnt, &handle->kmap_cnt)) {
+		buffer->heap->ops->unmap_kernel(buffer->heap, buffer);
+		buffer->vaddr = NULL;
+	}
 	mutex_unlock(&buffer->lock);
 	mutex_unlock(&client->lock);
 }
+EXPORT_SYMBOL(ion_unmap_kernel);
+
+void ion_unmap_dma(struct ion_client *client, struct ion_handle *handle)
+{
+	struct ion_buffer *buffer;
+
+	mutex_lock(&client->lock);
+	buffer = handle->buffer;
+	mutex_lock(&buffer->lock);
+	if (_ion_unmap(&buffer->dmap_cnt, &handle->dmap_cnt)) {
+		buffer->heap->ops->unmap_dma(buffer->heap, buffer);
+		buffer->sglist = NULL;
+	}
+	mutex_unlock(&buffer->lock);
+	mutex_unlock(&client->lock);
+}
+EXPORT_SYMBOL(ion_unmap_dma);
+
+struct ion_buffer *ion_share(struct ion_client *client,
+				 struct ion_handle *handle)
+{
+	bool valid_handle;
+
+	mutex_lock(&client->lock);
+	valid_handle = ion_handle_validate(client, handle);
+	mutex_unlock(&client->lock);
+	if (!valid_handle) {
+		WARN("%s: invalid handle passed to share.\n", __func__);
+		return ERR_PTR(-EINVAL);
+	}
+
+	/* do not take an extra reference here, the burden is on the caller
+	 * to make sure the buffer doesn't go away while it's passing it
+	 * to another client -- ion_free should not be called on this handle
+	 * until the buffer has been imported into the other client
+	 */
+	return handle->buffer;
+}
+EXPORT_SYMBOL(ion_share);
+
+struct ion_handle *ion_import(struct ion_client *client,
+			      struct ion_buffer *buffer)
+{
+	struct ion_handle *handle = NULL;
+
+	mutex_lock(&client->lock);
+	/* if a handle exists for this buffer just take a reference to it */
+	handle = ion_handle_lookup(client, buffer);
+	if (!IS_ERR_OR_NULL(handle)) {
+		ion_handle_get(handle);
+		goto end;
+	}
+	handle = ion_handle_create(client, buffer);
+	if (IS_ERR_OR_NULL(handle))
+		goto end;
+	ion_handle_add(client, handle);
+end:
+	mutex_unlock(&client->lock);
+	return handle;
+}
+EXPORT_SYMBOL(ion_import);
+
+static int check_vaddr_bounds(unsigned long start, unsigned long end)
+{
+	struct mm_struct *mm = current->active_mm;
+	struct vm_area_struct *vma;
+	int ret = 1;
+
+	if (end < start)
+		goto out;
+
+	down_read(&mm->mmap_sem);
+	vma = find_vma(mm, start);
+	if (vma && vma->vm_start < end) {
+		if (start < vma->vm_start)
+			goto out_up;
+		if (end > vma->vm_end)
+			goto out_up;
+		ret = 0;
+	}
+
+out_up:
+	up_read(&mm->mmap_sem);
+out:
+	return ret;
+}
+
+int ion_do_cache_op(struct ion_client *client, struct ion_handle *handle,
+			void *uaddr, unsigned long offset, unsigned long len,
+			unsigned int cmd)
+{
+	struct ion_buffer *buffer;
+	int ret = -EINVAL;
+
+	mutex_lock(&client->lock);
+	if (!ion_handle_validate(client, handle)) {
+		pr_err("%s: invalid handle passed to do_cache_op.\n",
+		       __func__);
+		mutex_unlock(&client->lock);
+		return -EINVAL;
+	}
+	buffer = handle->buffer;
+	mutex_lock(&buffer->lock);
+
+	if (!ION_IS_CACHED(buffer->flags)) {
+		ret = 0;
+		goto out;
+	}
+
+	if (!handle->buffer->heap->ops->cache_op) {
+		pr_err("%s: cache_op is not implemented by this heap.\n",
+		       __func__);
+		ret = -ENODEV;
+		goto out;
+	}
+
+
+	ret = buffer->heap->ops->cache_op(buffer->heap, buffer, uaddr,
+						offset, len, cmd);
+
+out:
+	mutex_unlock(&buffer->lock);
+	mutex_unlock(&client->lock);
+	return ret;
+
+}
+
+static const struct file_operations ion_share_fops;
+
+struct ion_handle *ion_import_fd(struct ion_client *client, int fd)
+{
+	struct file *file = fget(fd);
+	struct ion_handle *handle;
+
+	if (!file) {
+		pr_err("%s: imported fd not found in file table.\n", __func__);
+		return ERR_PTR(-EINVAL);
+	}
+	if (file->f_op != &ion_share_fops) {
+		pr_err("%s: imported file %s is not a shared ion"
+			" file.", __func__, file->f_dentry->d_name.name);
+		handle = ERR_PTR(-EINVAL);
+		goto end;
+	}
+	handle = ion_import(client, file->private_data);
+end:
+	fput(file);
+	return handle;
+}
+EXPORT_SYMBOL(ion_import_fd);
 
 static int ion_debug_client_show(struct seq_file *s, void *unused)
 {
 	struct ion_client *client = s->private;
 	struct rb_node *n;
-	size_t sizes[ION_NUM_HEAPS] = {0};
-	const char *names[ION_NUM_HEAPS] = {0};
-	int i;
+	struct rb_node *n2;
+
+	seq_printf(s, "%16.16s: %16.16s : %16.16s : %12.12s : %12.12s : %s\n",
+			"heap_name", "size_in_bytes", "handle refcount",
+			"buffer", "physical", "[domain,partition] - virt");
 
 	mutex_lock(&client->lock);
 	for (n = rb_first(&client->handles); n; n = rb_next(n)) {
@@ -505,18 +1009,35 @@
 						     node);
 		enum ion_heap_type type = handle->buffer->heap->type;
 
-		if (!names[type])
-			names[type] = handle->buffer->heap->name;
-		sizes[type] += handle->buffer->size;
+		seq_printf(s, "%16.16s: %16x : %16d : %12p",
+				handle->buffer->heap->name,
+				handle->buffer->size,
+				atomic_read(&handle->ref.refcount),
+				handle->buffer);
+
+		if (type == ION_HEAP_TYPE_SYSTEM_CONTIG ||
+			type == ION_HEAP_TYPE_CARVEOUT ||
+			type == ION_HEAP_TYPE_CP)
+			seq_printf(s, " : %12lx", handle->buffer->priv_phys);
+		else
+			seq_printf(s, " : %12s", "N/A");
+
+		for (n2 = rb_first(&handle->buffer->iommu_maps); n2;
+				   n2 = rb_next(n2)) {
+			struct ion_iommu_map *imap =
+				rb_entry(n2, struct ion_iommu_map, node);
+			seq_printf(s, " : [%d,%d] - %8lx",
+					imap->domain_info[DI_DOMAIN_NUM],
+					imap->domain_info[DI_PARTITION_NUM],
+					imap->iova_addr);
+		}
+		seq_printf(s, "\n");
 	}
+
+	seq_printf(s, "%16.16s %d\n", "client refcount:",
+			atomic_read(&client->ref.refcount));
 	mutex_unlock(&client->lock);
 
-	seq_printf(s, "%16.16s: %16.16s\n", "heap_name", "size_in_bytes");
-	for (i = 0; i < ION_NUM_HEAPS; i++) {
-		if (!names[i])
-			continue;
-		seq_printf(s, "%16.16s: %16u\n", names[i], sizes[i]);
-	}
 	return 0;
 }
 
@@ -532,6 +1053,29 @@
 	.release = single_release,
 };
 
+static struct ion_client *ion_client_lookup(struct ion_device *dev,
+					    struct task_struct *task)
+{
+	struct rb_node *n = dev->user_clients.rb_node;
+	struct ion_client *client;
+
+	mutex_lock(&dev->lock);
+	while (n) {
+		client = rb_entry(n, struct ion_client, node);
+		if (task == client->task) {
+			ion_client_get(client);
+			mutex_unlock(&dev->lock);
+			return client;
+		} else if (task < client->task) {
+			n = n->rb_left;
+		} else if (task > client->task) {
+			n = n->rb_right;
+		}
+	}
+	mutex_unlock(&dev->lock);
+	return NULL;
+}
+
 struct ion_client *ion_client_create(struct ion_device *dev,
 				     unsigned int heap_mask,
 				     const char *name)
@@ -541,8 +1085,14 @@
 	struct rb_node **p;
 	struct rb_node *parent = NULL;
 	struct ion_client *entry;
-	char debug_name[64];
 	pid_t pid;
+	unsigned int name_len;
+
+	if (!name) {
+		pr_err("%s: Name cannot be null\n", __func__);
+		return ERR_PTR(-EINVAL);
+	}
+	name_len = strnlen(name, 64);
 
 	get_task_struct(current->group_leader);
 	task_lock(current->group_leader);
@@ -557,37 +1107,71 @@
 	}
 	task_unlock(current->group_leader);
 
+	/* if this isn't a kernel thread, see if a client already
+	   exists */
+	if (task) {
+		client = ion_client_lookup(dev, task);
+		if (!IS_ERR_OR_NULL(client)) {
+			put_task_struct(current->group_leader);
+			return client;
+		}
+	}
+
 	client = kzalloc(sizeof(struct ion_client), GFP_KERNEL);
 	if (!client) {
-		if (task)
-			put_task_struct(current->group_leader);
+		put_task_struct(current->group_leader);
 		return ERR_PTR(-ENOMEM);
 	}
 
 	client->dev = dev;
 	client->handles = RB_ROOT;
 	mutex_init(&client->lock);
-	client->name = name;
+
+	client->name = kzalloc(name_len+1, GFP_KERNEL);
+	if (!client->name) {
+		put_task_struct(current->group_leader);
+		kfree(client);
+		return ERR_PTR(-ENOMEM);
+	} else {
+		strlcpy(client->name, name, name_len+1);
+	}
+
 	client->heap_mask = heap_mask;
 	client->task = task;
 	client->pid = pid;
+	kref_init(&client->ref);
 
 	mutex_lock(&dev->lock);
-	p = &dev->clients.rb_node;
-	while (*p) {
-		parent = *p;
-		entry = rb_entry(parent, struct ion_client, node);
+	if (task) {
+		p = &dev->user_clients.rb_node;
+		while (*p) {
+			parent = *p;
+			entry = rb_entry(parent, struct ion_client, node);
 
-		if (client < entry)
-			p = &(*p)->rb_left;
-		else if (client > entry)
-			p = &(*p)->rb_right;
+			if (task < entry->task)
+				p = &(*p)->rb_left;
+			else if (task > entry->task)
+				p = &(*p)->rb_right;
+		}
+		rb_link_node(&client->node, parent, p);
+		rb_insert_color(&client->node, &dev->user_clients);
+	} else {
+		p = &dev->kernel_clients.rb_node;
+		while (*p) {
+			parent = *p;
+			entry = rb_entry(parent, struct ion_client, node);
+
+			if (client < entry)
+				p = &(*p)->rb_left;
+			else if (client > entry)
+				p = &(*p)->rb_right;
+		}
+		rb_link_node(&client->node, parent, p);
+		rb_insert_color(&client->node, &dev->kernel_clients);
 	}
-	rb_link_node(&client->node, parent, p);
-	rb_insert_color(&client->node, &dev->clients);
 
-	snprintf(debug_name, 64, "%u", client->pid);
-	client->debug_root = debugfs_create_file(debug_name, 0664,
+
+	client->debug_root = debugfs_create_file(name, 0664,
 						 dev->debug_root, client,
 						 &debug_client_fops);
 	mutex_unlock(&dev->lock);
@@ -595,8 +1179,9 @@
 	return client;
 }
 
-void ion_client_destroy(struct ion_client *client)
+static void _ion_client_destroy(struct kref *kref)
 {
+	struct ion_client *client = container_of(kref, struct ion_client, ref);
 	struct ion_device *dev = client->dev;
 	struct rb_node *n;
 
@@ -607,201 +1192,279 @@
 		ion_handle_destroy(&handle->ref);
 	}
 	mutex_lock(&dev->lock);
-	if (client->task)
+	if (client->task) {
+		rb_erase(&client->node, &dev->user_clients);
 		put_task_struct(client->task);
-	rb_erase(&client->node, &dev->clients);
+	} else {
+		rb_erase(&client->node, &dev->kernel_clients);
+	}
 	debugfs_remove_recursive(client->debug_root);
 	mutex_unlock(&dev->lock);
 
+	kfree(client->name);
 	kfree(client);
 }
 
-struct sg_table *ion_sg_table(struct ion_client *client,
-			      struct ion_handle *handle)
+static void ion_client_get(struct ion_client *client)
+{
+	kref_get(&client->ref);
+}
+
+static int ion_client_put(struct ion_client *client)
+{
+	return kref_put(&client->ref, _ion_client_destroy);
+}
+
+void ion_client_destroy(struct ion_client *client)
+{
+	if (client)
+		ion_client_put(client);
+}
+EXPORT_SYMBOL(ion_client_destroy);
+
+int ion_handle_get_flags(struct ion_client *client, struct ion_handle *handle,
+			unsigned long *flags)
 {
 	struct ion_buffer *buffer;
-	struct sg_table *table;
 
 	mutex_lock(&client->lock);
 	if (!ion_handle_validate(client, handle)) {
-		pr_err("%s: invalid handle passed to map_dma.\n",
-		       __func__);
+		pr_err("%s: invalid handle passed to %s.\n",
+		       __func__, __func__);
 		mutex_unlock(&client->lock);
-		return ERR_PTR(-EINVAL);
-	}
-	buffer = handle->buffer;
-	table = buffer->sg_table;
-	mutex_unlock(&client->lock);
-	return table;
-}
-
-static struct sg_table *ion_map_dma_buf(struct dma_buf_attachment *attachment,
-					enum dma_data_direction direction)
-{
-	struct dma_buf *dmabuf = attachment->dmabuf;
-	struct ion_buffer *buffer = dmabuf->priv;
-
-	return buffer->sg_table;
-}
-
-static void ion_unmap_dma_buf(struct dma_buf_attachment *attachment,
-			      struct sg_table *table,
-			      enum dma_data_direction direction)
-{
-}
-
-static int ion_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
-{
-	struct ion_buffer *buffer = dmabuf->priv;
-	int ret;
-
-	if (!buffer->heap->ops->map_user) {
-		pr_err("%s: this heap does not define a method for mapping "
-		       "to userspace\n", __func__);
 		return -EINVAL;
 	}
-
+	buffer = handle->buffer;
 	mutex_lock(&buffer->lock);
-	/* now map it to userspace */
-	ret = buffer->heap->ops->map_user(buffer->heap, buffer, vma);
+	*flags = buffer->flags;
 	mutex_unlock(&buffer->lock);
+	mutex_unlock(&client->lock);
 
-	if (ret)
-		pr_err("%s: failure mapping buffer to userspace\n",
-		       __func__);
-
-	return ret;
+	return 0;
 }
+EXPORT_SYMBOL(ion_handle_get_flags);
 
-static void ion_dma_buf_release(struct dma_buf *dmabuf)
+int ion_handle_get_size(struct ion_client *client, struct ion_handle *handle,
+			unsigned long *size)
 {
-	struct ion_buffer *buffer = dmabuf->priv;
-	ion_buffer_put(buffer);
-}
+	struct ion_buffer *buffer;
 
-static void *ion_dma_buf_kmap(struct dma_buf *dmabuf, unsigned long offset)
-{
-	struct ion_buffer *buffer = dmabuf->priv;
-	return buffer->vaddr + offset;
-}
-
-static void ion_dma_buf_kunmap(struct dma_buf *dmabuf, unsigned long offset,
-			       void *ptr)
-{
-	return;
-}
-
-static int ion_dma_buf_begin_cpu_access(struct dma_buf *dmabuf, size_t start,
-					size_t len,
-					enum dma_data_direction direction)
-{
-	struct ion_buffer *buffer = dmabuf->priv;
-	void *vaddr;
-
-	if (!buffer->heap->ops->map_kernel) {
-		pr_err("%s: map kernel is not implemented by this heap.\n",
-		       __func__);
-		return -ENODEV;
+	mutex_lock(&client->lock);
+	if (!ion_handle_validate(client, handle)) {
+		pr_err("%s: invalid handle passed to %s.\n",
+		       __func__, __func__);
+		mutex_unlock(&client->lock);
+		return -EINVAL;
 	}
-
+	buffer = handle->buffer;
 	mutex_lock(&buffer->lock);
-	vaddr = ion_buffer_kmap_get(buffer);
+	*size = buffer->size;
 	mutex_unlock(&buffer->lock);
-	if (IS_ERR(vaddr))
-		return PTR_ERR(vaddr);
-	if (!vaddr)
-		return -ENOMEM;
+	mutex_unlock(&client->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(ion_handle_get_size);
+
+static int ion_share_release(struct inode *inode, struct file* file)
+{
+	struct ion_buffer *buffer = file->private_data;
+
+	pr_debug("%s: %d\n", __func__, __LINE__);
+	/* drop the reference to the buffer -- this prevents the
+	   buffer from going away because the client holding it exited
+	   while it was being passed */
+	ion_buffer_put(buffer);
 	return 0;
 }
 
-static void ion_dma_buf_end_cpu_access(struct dma_buf *dmabuf, size_t start,
-				       size_t len,
-				       enum dma_data_direction direction)
+static void ion_vma_open(struct vm_area_struct *vma)
 {
-	struct ion_buffer *buffer = dmabuf->priv;
 
+	struct ion_buffer *buffer = vma->vm_file->private_data;
+	struct ion_handle *handle = vma->vm_private_data;
+	struct ion_client *client;
+
+	pr_debug("%s: %d\n", __func__, __LINE__);
+	/* check that the client still exists and take a reference so
+	   it can't go away until this vma is closed */
+	client = ion_client_lookup(buffer->dev, current->group_leader);
+	if (IS_ERR_OR_NULL(client)) {
+		vma->vm_private_data = NULL;
+		return;
+	}
+	ion_handle_get(handle);
 	mutex_lock(&buffer->lock);
-	ion_buffer_kmap_put(buffer);
+	buffer->umap_cnt++;
 	mutex_unlock(&buffer->lock);
+	pr_debug("%s: %d client_cnt %d handle_cnt %d alloc_cnt %d\n",
+		 __func__, __LINE__,
+		 atomic_read(&client->ref.refcount),
+		 atomic_read(&handle->ref.refcount),
+		 atomic_read(&buffer->ref.refcount));
 }
 
-struct dma_buf_ops dma_buf_ops = {
-	.map_dma_buf = ion_map_dma_buf,
-	.unmap_dma_buf = ion_unmap_dma_buf,
-	.mmap = ion_mmap,
-	.release = ion_dma_buf_release,
-	.begin_cpu_access = ion_dma_buf_begin_cpu_access,
-	.end_cpu_access = ion_dma_buf_end_cpu_access,
-	.kmap_atomic = ion_dma_buf_kmap,
-	.kunmap_atomic = ion_dma_buf_kunmap,
-	.kmap = ion_dma_buf_kmap,
-	.kunmap = ion_dma_buf_kunmap,
+static void ion_vma_close(struct vm_area_struct *vma)
+{
+	struct ion_handle *handle = vma->vm_private_data;
+	struct ion_buffer *buffer = vma->vm_file->private_data;
+	struct ion_client *client;
+
+	pr_debug("%s: %d\n", __func__, __LINE__);
+	/* this indicates the client is gone, nothing to do here */
+	if (!handle)
+		return;
+	client = handle->client;
+	mutex_lock(&buffer->lock);
+	buffer->umap_cnt--;
+	mutex_unlock(&buffer->lock);
+
+	if (buffer->heap->ops->unmap_user)
+		buffer->heap->ops->unmap_user(buffer->heap, buffer);
+
+
+	pr_debug("%s: %d client_cnt %d handle_cnt %d alloc_cnt %d\n",
+		 __func__, __LINE__,
+		 atomic_read(&client->ref.refcount),
+		 atomic_read(&handle->ref.refcount),
+		 atomic_read(&buffer->ref.refcount));
+	mutex_lock(&client->lock);
+	ion_handle_put(handle);
+	mutex_unlock(&client->lock);
+	ion_client_put(client);
+	pr_debug("%s: %d client_cnt %d handle_cnt %d alloc_cnt %d\n",
+		 __func__, __LINE__,
+		 atomic_read(&client->ref.refcount),
+		 atomic_read(&handle->ref.refcount),
+		 atomic_read(&buffer->ref.refcount));
+}
+
+static struct vm_operations_struct ion_vm_ops = {
+	.open = ion_vma_open,
+	.close = ion_vma_close,
 };
 
-int ion_share_dma_buf(struct ion_client *client, struct ion_handle *handle)
+static int ion_share_mmap(struct file *file, struct vm_area_struct *vma)
 {
-	struct ion_buffer *buffer;
-	struct dma_buf *dmabuf;
-	bool valid_handle;
-	int fd;
+	struct ion_buffer *buffer = file->private_data;
+	unsigned long size = vma->vm_end - vma->vm_start;
+	struct ion_client *client;
+	struct ion_handle *handle;
+	int ret;
+	unsigned long flags = file->f_flags & O_DSYNC ?
+				ION_SET_CACHE(UNCACHED) :
+				ION_SET_CACHE(CACHED);
 
-	mutex_lock(&client->lock);
-	valid_handle = ion_handle_validate(client, handle);
-	mutex_unlock(&client->lock);
-	if (!valid_handle) {
-		WARN("%s: invalid handle passed to share.\n", __func__);
+
+	pr_debug("%s: %d\n", __func__, __LINE__);
+	/* make sure the client still exists, it's possible for the client to
+	   have gone away but the map/share fd still to be around, take
+	   a reference to it so it can't go away while this mapping exists */
+	client = ion_client_lookup(buffer->dev, current->group_leader);
+	if (IS_ERR_OR_NULL(client)) {
+		pr_err("%s: trying to mmap an ion handle in a process with no "
+		       "ion client\n", __func__);
 		return -EINVAL;
 	}
 
-	buffer = handle->buffer;
-	ion_buffer_get(buffer);
-	dmabuf = dma_buf_export(buffer, &dma_buf_ops, buffer->size, O_RDWR);
-	if (IS_ERR(dmabuf)) {
-		ion_buffer_put(buffer);
-		return PTR_ERR(dmabuf);
+	if ((size > buffer->size) || (size + (vma->vm_pgoff << PAGE_SHIFT) >
+				     buffer->size)) {
+		pr_err("%s: trying to map larger area than handle has available"
+		       "\n", __func__);
+		ret = -EINVAL;
+		goto err;
 	}
-	fd = dma_buf_fd(dmabuf, O_CLOEXEC);
-	if (fd < 0) {
-		dma_buf_put(dmabuf);
-		ion_buffer_put(buffer);
+
+	/* find the handle and take a reference to it */
+	handle = ion_import(client, buffer);
+	if (IS_ERR_OR_NULL(handle)) {
+		ret = -EINVAL;
+		goto err;
 	}
-	return fd;
+
+	if (!handle->buffer->heap->ops->map_user) {
+		pr_err("%s: this heap does not define a method for mapping "
+		       "to userspace\n", __func__);
+		ret = -EINVAL;
+		goto err1;
+	}
+
+	mutex_lock(&buffer->lock);
+
+	if (ion_validate_buffer_flags(buffer, flags)) {
+		ret = -EEXIST;
+		mutex_unlock(&buffer->lock);
+		goto err1;
+	}
+
+	/* now map it to userspace */
+	ret = buffer->heap->ops->map_user(buffer->heap, buffer, vma,
+						flags);
+
+	buffer->umap_cnt++;
+	if (ret) {
+		pr_err("%s: failure mapping buffer to userspace\n",
+		       __func__);
+		goto err2;
+	}
+	mutex_unlock(&buffer->lock);
+
+	vma->vm_ops = &ion_vm_ops;
+	/* move the handle into the vm_private_data so we can access it from
+	   vma_open/close */
+	vma->vm_private_data = handle;
+	pr_debug("%s: %d client_cnt %d handle_cnt %d alloc_cnt %d\n",
+		 __func__, __LINE__,
+		 atomic_read(&client->ref.refcount),
+		 atomic_read(&handle->ref.refcount),
+		 atomic_read(&buffer->ref.refcount));
+	return 0;
+
+err2:
+	buffer->umap_cnt--;
+	mutex_unlock(&buffer->lock);
+	/* drop the reference to the handle */
+err1:
+	mutex_lock(&client->lock);
+	ion_handle_put(handle);
+	mutex_unlock(&client->lock);
+err:
+	/* drop the reference to the client */
+	ion_client_put(client);
+	return ret;
 }
 
-struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd)
+static const struct file_operations ion_share_fops = {
+	.owner		= THIS_MODULE,
+	.release	= ion_share_release,
+	.mmap		= ion_share_mmap,
+};
+
+static int ion_ioctl_share(struct file *parent, struct ion_client *client,
+			   struct ion_handle *handle)
 {
-	struct dma_buf *dmabuf;
-	struct ion_buffer *buffer;
-	struct ion_handle *handle;
+	int fd = get_unused_fd();
+	struct file *file;
 
-	dmabuf = dma_buf_get(fd);
-	if (IS_ERR_OR_NULL(dmabuf))
-		return ERR_PTR(PTR_ERR(dmabuf));
-	/* if this memory came from ion */
+	if (fd < 0)
+		return -ENFILE;
 
-	if (dmabuf->ops != &dma_buf_ops) {
-		pr_err("%s: can not import dmabuf from another exporter\n",
-		       __func__);
-		dma_buf_put(dmabuf);
-		return ERR_PTR(-EINVAL);
-	}
-	buffer = dmabuf->priv;
+	file = anon_inode_getfile("ion_share_fd", &ion_share_fops,
+				  handle->buffer, O_RDWR);
+	if (IS_ERR_OR_NULL(file))
+		goto err;
 
-	mutex_lock(&client->lock);
-	/* if a handle exists for this buffer just take a reference to it */
-	handle = ion_handle_lookup(client, buffer);
-	if (!IS_ERR_OR_NULL(handle)) {
-		ion_handle_get(handle);
-		goto end;
-	}
-	handle = ion_handle_create(client, buffer);
-	if (IS_ERR_OR_NULL(handle))
-		goto end;
-	ion_handle_add(client, handle);
-end:
-	mutex_unlock(&client->lock);
-	dma_buf_put(dmabuf);
-	return handle;
+	if (parent->f_flags & O_DSYNC)
+		file->f_flags |= O_DSYNC;
+
+	ion_buffer_get(handle->buffer);
+	fd_install(fd, file);
+
+	return fd;
+
+err:
+	put_unused_fd(fd);
+	return -ENFILE;
 }
 
 static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
@@ -818,13 +1481,11 @@
 		data.handle = ion_alloc(client, data.len, data.align,
 					     data.flags);
 
-		if (IS_ERR(data.handle))
-			return PTR_ERR(data.handle);
+		if (IS_ERR_OR_NULL(data.handle))
+			return -ENOMEM;
 
-		if (copy_to_user((void __user *)arg, &data, sizeof(data))) {
-			ion_free(client, data.handle);
+		if (copy_to_user((void __user *)arg, &data, sizeof(data)))
 			return -EFAULT;
-		}
 		break;
 	}
 	case ION_IOC_FREE:
@@ -843,29 +1504,46 @@
 		ion_free(client, data.handle);
 		break;
 	}
+	case ION_IOC_MAP:
 	case ION_IOC_SHARE:
 	{
 		struct ion_fd_data data;
 
 		if (copy_from_user(&data, (void __user *)arg, sizeof(data)))
 			return -EFAULT;
-		data.fd = ion_share_dma_buf(client, data.handle);
+		mutex_lock(&client->lock);
+		if (!ion_handle_validate(client, data.handle)) {
+			pr_err("%s: invalid handle passed to share ioctl.\n",
+			       __func__);
+			mutex_unlock(&client->lock);
+			return -EINVAL;
+		}
+		data.fd = ion_ioctl_share(filp, client, data.handle);
+		mutex_unlock(&client->lock);
 		if (copy_to_user((void __user *)arg, &data, sizeof(data)))
 			return -EFAULT;
+		if (data.fd < 0)
+			return data.fd;
 		break;
 	}
 	case ION_IOC_IMPORT:
 	{
 		struct ion_fd_data data;
+		int ret = 0;
 		if (copy_from_user(&data, (void __user *)arg,
 				   sizeof(struct ion_fd_data)))
 			return -EFAULT;
-		data.handle = ion_import_dma_buf(client, data.fd);
-		if (IS_ERR(data.handle))
+
+		data.handle = ion_import_fd(client, data.fd);
+		if (IS_ERR(data.handle)) {
+			ret = PTR_ERR(data.handle);
 			data.handle = NULL;
+		}
 		if (copy_to_user((void __user *)arg, &data,
 				 sizeof(struct ion_fd_data)))
 			return -EFAULT;
+		if (ret < 0)
+			return ret;
 		break;
 	}
 	case ION_IOC_CUSTOM:
@@ -880,6 +1558,66 @@
 			return -EFAULT;
 		return dev->custom_ioctl(client, data.cmd, data.arg);
 	}
+	case ION_IOC_CLEAN_CACHES:
+	case ION_IOC_INV_CACHES:
+	case ION_IOC_CLEAN_INV_CACHES:
+	{
+		struct ion_flush_data data;
+		unsigned long start, end;
+		struct ion_handle *handle = NULL;
+		int ret;
+
+		if (copy_from_user(&data, (void __user *)arg,
+				sizeof(struct ion_flush_data)))
+			return -EFAULT;
+
+		start = (unsigned long) data.vaddr;
+		end = (unsigned long) data.vaddr + data.length;
+
+		if (check_vaddr_bounds(start, end)) {
+			pr_err("%s: virtual address %p is out of bounds\n",
+				__func__, data.vaddr);
+			return -EINVAL;
+		}
+
+		if (!data.handle) {
+			handle = ion_import_fd(client, data.fd);
+			if (IS_ERR_OR_NULL(handle)) {
+				pr_info("%s: Could not import handle: %d\n",
+					__func__, (int)handle);
+				return -EINVAL;
+			}
+		}
+
+		ret = ion_do_cache_op(client,
+					data.handle ? data.handle : handle,
+					data.vaddr, data.offset, data.length,
+					cmd);
+
+		if (!data.handle)
+			ion_free(client, handle);
+
+		if (ret < 0)
+			return ret;
+		break;
+
+	}
+	case ION_IOC_GET_FLAGS:
+	{
+		struct ion_flag_data data;
+		int ret;
+		if (copy_from_user(&data, (void __user *)arg,
+				   sizeof(struct ion_flag_data)))
+			return -EFAULT;
+
+		ret = ion_handle_get_flags(client, data.handle, &data.flags);
+		if (ret < 0)
+			return ret;
+		if (copy_to_user((void __user *)arg, &data,
+				 sizeof(struct ion_flag_data)))
+			return -EFAULT;
+		break;
+	}
 	default:
 		return -ENOTTY;
 	}
@@ -891,7 +1629,7 @@
 	struct ion_client *client = file->private_data;
 
 	pr_debug("%s: %d\n", __func__, __LINE__);
-	ion_client_destroy(client);
+	ion_client_put(client);
 	return 0;
 }
 
@@ -900,9 +1638,11 @@
 	struct miscdevice *miscdev = file->private_data;
 	struct ion_device *dev = container_of(miscdev, struct ion_device, dev);
 	struct ion_client *client;
+	char debug_name[64];
 
 	pr_debug("%s: %d\n", __func__, __LINE__);
-	client = ion_client_create(dev, -1, "user");
+	snprintf(debug_name, 64, "%u", task_pid_nr(current->group_leader));
+	client = ion_client_create(dev, -1, debug_name);
 	if (IS_ERR_OR_NULL(client))
 		return PTR_ERR(client);
 	file->private_data = client;
@@ -918,7 +1658,7 @@
 };
 
 static size_t ion_debug_heap_total(struct ion_client *client,
-				   enum ion_heap_type type)
+				   enum ion_heap_ids id)
 {
 	size_t size = 0;
 	struct rb_node *n;
@@ -928,38 +1668,190 @@
 		struct ion_handle *handle = rb_entry(n,
 						     struct ion_handle,
 						     node);
-		if (handle->buffer->heap->type == type)
+		if (handle->buffer->heap->id == id)
 			size += handle->buffer->size;
 	}
 	mutex_unlock(&client->lock);
 	return size;
 }
 
+/**
+ * Searches through a clients handles to find if the buffer is owned
+ * by this client. Used for debug output.
+ * @param client pointer to candidate owner of buffer
+ * @param buf pointer to buffer that we are trying to find the owner of
+ * @return 1 if found, 0 otherwise
+ */
+static int ion_debug_find_buffer_owner(const struct ion_client *client,
+				       const struct ion_buffer *buf)
+{
+	struct rb_node *n;
+
+	for (n = rb_first(&client->handles); n; n = rb_next(n)) {
+		const struct ion_handle *handle = rb_entry(n,
+						     const struct ion_handle,
+						     node);
+		if (handle->buffer == buf)
+			return 1;
+	}
+	return 0;
+}
+
+/**
+ * Adds mem_map_data pointer to the tree of mem_map
+ * Used for debug output.
+ * @param mem_map The mem_map tree
+ * @param data The new data to add to the tree
+ */
+static void ion_debug_mem_map_add(struct rb_root *mem_map,
+				  struct mem_map_data *data)
+{
+	struct rb_node **p = &mem_map->rb_node;
+	struct rb_node *parent = NULL;
+	struct mem_map_data *entry;
+
+	while (*p) {
+		parent = *p;
+		entry = rb_entry(parent, struct mem_map_data, node);
+
+		if (data->addr < entry->addr) {
+			p = &(*p)->rb_left;
+		} else if (data->addr > entry->addr) {
+			p = &(*p)->rb_right;
+		} else {
+			pr_err("%s: mem_map_data already found.", __func__);
+			BUG();
+		}
+	}
+	rb_link_node(&data->node, parent, p);
+	rb_insert_color(&data->node, mem_map);
+}
+
+/**
+ * Search for an owner of a buffer by iterating over all ION clients.
+ * @param dev ion device containing pointers to all the clients.
+ * @param buffer pointer to buffer we are trying to find the owner of.
+ * @return name of owner.
+ */
+const char *ion_debug_locate_owner(const struct ion_device *dev,
+					 const struct ion_buffer *buffer)
+{
+	struct rb_node *j;
+	const char *client_name = NULL;
+
+	for (j = rb_first(&dev->user_clients); j && !client_name;
+			  j = rb_next(j)) {
+		struct ion_client *client = rb_entry(j, struct ion_client,
+						     node);
+		if (ion_debug_find_buffer_owner(client, buffer))
+			client_name = client->name;
+	}
+	for (j = rb_first(&dev->kernel_clients); j && !client_name;
+			  j = rb_next(j)) {
+		struct ion_client *client = rb_entry(j, struct ion_client,
+						     node);
+		if (ion_debug_find_buffer_owner(client, buffer))
+			client_name = client->name;
+	}
+	return client_name;
+}
+
+/**
+ * Create a mem_map of the heap.
+ * @param s seq_file to log error message to.
+ * @param heap The heap to create mem_map for.
+ * @param mem_map The mem map to be created.
+ */
+void ion_debug_mem_map_create(struct seq_file *s, struct ion_heap *heap,
+			      struct rb_root *mem_map)
+{
+	struct ion_device *dev = heap->dev;
+	struct rb_node *n;
+
+	for (n = rb_first(&dev->buffers); n; n = rb_next(n)) {
+		struct ion_buffer *buffer =
+				rb_entry(n, struct ion_buffer, node);
+		if (buffer->heap->id == heap->id) {
+			struct mem_map_data *data =
+					kzalloc(sizeof(*data), GFP_KERNEL);
+			if (!data) {
+				seq_printf(s, "ERROR: out of memory. "
+					   "Part of memory map will not be logged\n");
+				break;
+			}
+			data->addr = buffer->priv_phys;
+			data->addr_end = buffer->priv_phys + buffer->size-1;
+			data->size = buffer->size;
+			data->client_name = ion_debug_locate_owner(dev, buffer);
+			ion_debug_mem_map_add(mem_map, data);
+		}
+	}
+}
+
+/**
+ * Free the memory allocated by ion_debug_mem_map_create
+ * @param mem_map The mem map to free.
+ */
+static void ion_debug_mem_map_destroy(struct rb_root *mem_map)
+{
+	if (mem_map) {
+		struct rb_node *n;
+		while ((n = rb_first(mem_map)) != 0) {
+			struct mem_map_data *data =
+					rb_entry(n, struct mem_map_data, node);
+			rb_erase(&data->node, mem_map);
+			kfree(data);
+		}
+	}
+}
+
+/**
+ * Print heap debug information.
+ * @param s seq_file to log message to.
+ * @param heap pointer to heap that we will print debug information for.
+ */
+static void ion_heap_print_debug(struct seq_file *s, struct ion_heap *heap)
+{
+	if (heap->ops->print_debug) {
+		struct rb_root mem_map = RB_ROOT;
+		ion_debug_mem_map_create(s, heap, &mem_map);
+		heap->ops->print_debug(heap, s, &mem_map);
+		ion_debug_mem_map_destroy(&mem_map);
+	}
+}
+
 static int ion_debug_heap_show(struct seq_file *s, void *unused)
 {
 	struct ion_heap *heap = s->private;
 	struct ion_device *dev = heap->dev;
 	struct rb_node *n;
 
+	mutex_lock(&dev->lock);
 	seq_printf(s, "%16.s %16.s %16.s\n", "client", "pid", "size");
-
-	for (n = rb_first(&dev->clients); n; n = rb_next(n)) {
+	for (n = rb_first(&dev->user_clients); n; n = rb_next(n)) {
 		struct ion_client *client = rb_entry(n, struct ion_client,
 						     node);
-		size_t size = ion_debug_heap_total(client, heap->type);
+		char task_comm[TASK_COMM_LEN];
+		size_t size = ion_debug_heap_total(client, heap->id);
 		if (!size)
 			continue;
-		if (client->task) {
-			char task_comm[TASK_COMM_LEN];
 
-			get_task_comm(task_comm, client->task);
-			seq_printf(s, "%16.s %16u %16u\n", task_comm,
-				   client->pid, size);
-		} else {
-			seq_printf(s, "%16.s %16u %16u\n", client->name,
-				   client->pid, size);
-		}
+		get_task_comm(task_comm, client->task);
+		seq_printf(s, "%16.s %16u %16x\n", task_comm, client->pid,
+			   size);
 	}
+
+	for (n = rb_first(&dev->kernel_clients); n; n = rb_next(n)) {
+		struct ion_client *client = rb_entry(n, struct ion_client,
+						     node);
+		size_t size = ion_debug_heap_total(client, heap->id);
+		if (!size)
+			continue;
+		seq_printf(s, "%16.s %16u %16x\n", client->name, client->pid,
+			   size);
+	}
+	ion_heap_print_debug(s, heap);
+	mutex_unlock(&dev->lock);
 	return 0;
 }
 
@@ -981,11 +1873,6 @@
 	struct rb_node *parent = NULL;
 	struct ion_heap *entry;
 
-	if (!heap->ops->allocate || !heap->ops->free || !heap->ops->map_dma ||
-	    !heap->ops->unmap_dma)
-		pr_err("%s: can not add heap with invalid ops struct.\n",
-		       __func__);
-
 	heap->dev = dev;
 	mutex_lock(&dev->lock);
 	while (*p) {
@@ -1011,6 +1898,135 @@
 	mutex_unlock(&dev->lock);
 }
 
+int ion_secure_heap(struct ion_device *dev, int heap_id)
+{
+	struct rb_node *n;
+	int ret_val = 0;
+
+	/*
+	 * traverse the list of heaps available in this system
+	 * and find the heap that is specified.
+	 */
+	mutex_lock(&dev->lock);
+	for (n = rb_first(&dev->heaps); n != NULL; n = rb_next(n)) {
+		struct ion_heap *heap = rb_entry(n, struct ion_heap, node);
+		if (heap->type != ION_HEAP_TYPE_CP)
+			continue;
+		if (ION_HEAP(heap->id) != heap_id)
+			continue;
+		if (heap->ops->secure_heap)
+			ret_val = heap->ops->secure_heap(heap);
+		else
+			ret_val = -EINVAL;
+		break;
+	}
+	mutex_unlock(&dev->lock);
+	return ret_val;
+}
+
+int ion_unsecure_heap(struct ion_device *dev, int heap_id)
+{
+	struct rb_node *n;
+	int ret_val = 0;
+
+	/*
+	 * traverse the list of heaps available in this system
+	 * and find the heap that is specified.
+	 */
+	mutex_lock(&dev->lock);
+	for (n = rb_first(&dev->heaps); n != NULL; n = rb_next(n)) {
+		struct ion_heap *heap = rb_entry(n, struct ion_heap, node);
+		if (heap->type != ION_HEAP_TYPE_CP)
+			continue;
+		if (ION_HEAP(heap->id) != heap_id)
+			continue;
+		if (heap->ops->secure_heap)
+			ret_val = heap->ops->unsecure_heap(heap);
+		else
+			ret_val = -EINVAL;
+		break;
+	}
+	mutex_unlock(&dev->lock);
+	return ret_val;
+}
+
+static int ion_debug_leak_show(struct seq_file *s, void *unused)
+{
+	struct ion_device *dev = s->private;
+	struct rb_node *n;
+	struct rb_node *n2;
+
+	/* mark all buffers as 1 */
+	seq_printf(s, "%16.s %16.s %16.s %16.s\n", "buffer", "heap", "size",
+		"ref cnt");
+	mutex_lock(&dev->lock);
+	for (n = rb_first(&dev->buffers); n; n = rb_next(n)) {
+		struct ion_buffer *buf = rb_entry(n, struct ion_buffer,
+						     node);
+
+		buf->marked = 1;
+	}
+
+	/* now see which buffers we can access */
+	for (n = rb_first(&dev->kernel_clients); n; n = rb_next(n)) {
+		struct ion_client *client = rb_entry(n, struct ion_client,
+						     node);
+
+		mutex_lock(&client->lock);
+		for (n2 = rb_first(&client->handles); n2; n2 = rb_next(n2)) {
+			struct ion_handle *handle = rb_entry(n2,
+						struct ion_handle, node);
+
+			handle->buffer->marked = 0;
+
+		}
+		mutex_unlock(&client->lock);
+
+	}
+
+	for (n = rb_first(&dev->user_clients); n; n = rb_next(n)) {
+		struct ion_client *client = rb_entry(n, struct ion_client,
+						     node);
+
+		mutex_lock(&client->lock);
+		for (n2 = rb_first(&client->handles); n2; n2 = rb_next(n2)) {
+			struct ion_handle *handle = rb_entry(n2,
+						struct ion_handle, node);
+
+			handle->buffer->marked = 0;
+
+		}
+		mutex_unlock(&client->lock);
+
+	}
+	/* And anyone still marked as a 1 means a leaked handle somewhere */
+	for (n = rb_first(&dev->buffers); n; n = rb_next(n)) {
+		struct ion_buffer *buf = rb_entry(n, struct ion_buffer,
+						     node);
+
+		if (buf->marked == 1)
+			seq_printf(s, "%16.x %16.s %16.x %16.d\n",
+				(int)buf, buf->heap->name, buf->size,
+				atomic_read(&buf->ref.refcount));
+	}
+	mutex_unlock(&dev->lock);
+	return 0;
+}
+
+static int ion_debug_leak_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, ion_debug_leak_show, inode->i_private);
+}
+
+static const struct file_operations debug_leak_fops = {
+	.open = ion_debug_leak_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+
+
 struct ion_device *ion_device_create(long (*custom_ioctl)
 				     (struct ion_client *client,
 				      unsigned int cmd,
@@ -1041,7 +2057,10 @@
 	idev->buffers = RB_ROOT;
 	mutex_init(&idev->lock);
 	idev->heaps = RB_ROOT;
-	idev->clients = RB_ROOT;
+	idev->user_clients = RB_ROOT;
+	idev->kernel_clients = RB_ROOT;
+	debugfs_create_file("check_leaked_fds", 0664, idev->debug_root, idev,
+			    &debug_leak_fops);
 	return idev;
 }
 
@@ -1051,19 +2070,3 @@
 	/* XXX need to free the heaps and clients ? */
 	kfree(dev);
 }
-
-void __init ion_reserve(struct ion_platform_data *data)
-{
-	int i, ret;
-
-	for (i = 0; i < data->nr; i++) {
-		if (data->heaps[i].size == 0)
-			continue;
-		ret = memblock_reserve(data->heaps[i].base,
-				       data->heaps[i].size);
-		if (ret)
-			pr_err("memblock reserve of %x@%lx failed\n",
-			       data->heaps[i].size,
-			       data->heaps[i].base);
-	}
-}
diff --git a/drivers/gpu/ion/ion_carveout_heap.c b/drivers/gpu/ion/ion_carveout_heap.c
index b4fcb3c..1fdc1f9 100644
--- a/drivers/gpu/ion/ion_carveout_heap.c
+++ b/drivers/gpu/ion/ion_carveout_heap.c
@@ -2,6 +2,7 @@
  * drivers/gpu/ion/ion_carveout_heap.c
  *
  * Copyright (C) 2011 Google, Inc.
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -23,14 +24,25 @@
 #include <linux/scatterlist.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
+#include <linux/iommu.h>
+#include <linux/seq_file.h>
 #include "ion_priv.h"
 
+#include <mach/iommu_domains.h>
 #include <asm/mach/map.h>
+#include <asm/cacheflush.h>
 
 struct ion_carveout_heap {
 	struct ion_heap heap;
 	struct gen_pool *pool;
 	ion_phys_addr_t base;
+	unsigned long allocated_bytes;
+	unsigned long total_size;
+	int (*request_region)(void *);
+	int (*release_region)(void *);
+	atomic_t map_count;
+	void *bus_id;
+	unsigned int has_outer_cache;
 };
 
 ion_phys_addr_t ion_carveout_allocate(struct ion_heap *heap,
@@ -39,11 +51,22 @@
 {
 	struct ion_carveout_heap *carveout_heap =
 		container_of(heap, struct ion_carveout_heap, heap);
-	unsigned long offset = gen_pool_alloc(carveout_heap->pool, size);
+	unsigned long offset = gen_pool_alloc_aligned(carveout_heap->pool,
+							size, ilog2(align));
 
-	if (!offset)
+	if (!offset) {
+		if ((carveout_heap->total_size -
+		      carveout_heap->allocated_bytes) >= size)
+			pr_debug("%s: heap %s has enough memory (%lx) but"
+				" the allocation of size %lx still failed."
+				" Memory is probably fragmented.",
+				__func__, heap->name,
+				carveout_heap->total_size -
+				carveout_heap->allocated_bytes, size);
 		return ION_CARVEOUT_ALLOCATE_FAIL;
+	}
 
+	carveout_heap->allocated_bytes += size;
 	return offset;
 }
 
@@ -56,6 +79,7 @@
 	if (addr == ION_CARVEOUT_ALLOCATE_FAIL)
 		return;
 	gen_pool_free(carveout_heap->pool, addr, size);
+	carveout_heap->allocated_bytes -= size;
 }
 
 static int ion_carveout_heap_phys(struct ion_heap *heap,
@@ -87,37 +111,306 @@
 struct scatterlist *ion_carveout_heap_map_dma(struct ion_heap *heap,
 					      struct ion_buffer *buffer)
 {
-	return ERR_PTR(-EINVAL);
+	struct scatterlist *sglist;
+
+	sglist = vmalloc(sizeof(struct scatterlist));
+	if (!sglist)
+		return ERR_PTR(-ENOMEM);
+
+	sg_init_table(sglist, 1);
+	sglist->length = buffer->size;
+	sglist->offset = 0;
+	sglist->dma_address = buffer->priv_phys;
+
+	return sglist;
 }
 
 void ion_carveout_heap_unmap_dma(struct ion_heap *heap,
 				 struct ion_buffer *buffer)
 {
-	return;
+	if (buffer->sglist)
+		vfree(buffer->sglist);
+}
+
+static int ion_carveout_request_region(struct ion_carveout_heap *carveout_heap)
+{
+	int ret_value = 0;
+	if (atomic_inc_return(&carveout_heap->map_count) == 1) {
+		if (carveout_heap->request_region) {
+			ret_value = carveout_heap->request_region(
+						carveout_heap->bus_id);
+			if (ret_value) {
+				pr_err("Unable to request SMI region");
+				atomic_dec(&carveout_heap->map_count);
+			}
+		}
+	}
+	return ret_value;
+}
+
+static int ion_carveout_release_region(struct ion_carveout_heap *carveout_heap)
+{
+	int ret_value = 0;
+	if (atomic_dec_and_test(&carveout_heap->map_count)) {
+		if (carveout_heap->release_region) {
+			ret_value = carveout_heap->release_region(
+						carveout_heap->bus_id);
+			if (ret_value)
+				pr_err("Unable to release SMI region");
+		}
+	}
+	return ret_value;
 }
 
 void *ion_carveout_heap_map_kernel(struct ion_heap *heap,
-				   struct ion_buffer *buffer)
+				   struct ion_buffer *buffer,
+				   unsigned long flags)
 {
-	return __arm_ioremap(buffer->priv_phys, buffer->size,
-			      MT_MEMORY_NONCACHED);
+	struct ion_carveout_heap *carveout_heap =
+		container_of(heap, struct ion_carveout_heap, heap);
+	void *ret_value;
+
+	if (ion_carveout_request_region(carveout_heap))
+		return NULL;
+
+	if (ION_IS_CACHED(flags))
+		ret_value = ioremap_cached(buffer->priv_phys, buffer->size);
+	else
+		ret_value = ioremap(buffer->priv_phys, buffer->size);
+
+	if (!ret_value)
+		ion_carveout_release_region(carveout_heap);
+	return ret_value;
 }
 
 void ion_carveout_heap_unmap_kernel(struct ion_heap *heap,
 				    struct ion_buffer *buffer)
 {
+	struct ion_carveout_heap *carveout_heap =
+		container_of(heap, struct ion_carveout_heap, heap);
+
 	__arm_iounmap(buffer->vaddr);
 	buffer->vaddr = NULL;
+
+	ion_carveout_release_region(carveout_heap);
 	return;
 }
 
 int ion_carveout_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer,
-			       struct vm_area_struct *vma)
+			       struct vm_area_struct *vma, unsigned long flags)
 {
-	return remap_pfn_range(vma, vma->vm_start,
-			       __phys_to_pfn(buffer->priv_phys) + vma->vm_pgoff,
-			       buffer->size,
-			       pgprot_noncached(vma->vm_page_prot));
+	struct ion_carveout_heap *carveout_heap =
+		container_of(heap, struct ion_carveout_heap, heap);
+	int ret_value = 0;
+
+	if (ion_carveout_request_region(carveout_heap))
+		return -EINVAL;
+
+	if (!ION_IS_CACHED(flags))
+		vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+	ret_value =  remap_pfn_range(vma, vma->vm_start,
+			__phys_to_pfn(buffer->priv_phys) + vma->vm_pgoff,
+			vma->vm_end - vma->vm_start,
+			vma->vm_page_prot);
+
+	if (ret_value)
+		ion_carveout_release_region(carveout_heap);
+	return ret_value;
+}
+
+void ion_carveout_heap_unmap_user(struct ion_heap *heap,
+				    struct ion_buffer *buffer)
+{
+	struct ion_carveout_heap *carveout_heap =
+		container_of(heap, struct ion_carveout_heap, heap);
+	ion_carveout_release_region(carveout_heap);
+}
+
+int ion_carveout_cache_ops(struct ion_heap *heap, struct ion_buffer *buffer,
+			void *vaddr, unsigned int offset, unsigned int length,
+			unsigned int cmd)
+{
+	void (*outer_cache_op)(phys_addr_t, phys_addr_t);
+	struct ion_carveout_heap *carveout_heap =
+	     container_of(heap, struct  ion_carveout_heap, heap);
+
+	switch (cmd) {
+	case ION_IOC_CLEAN_CACHES:
+		dmac_clean_range(vaddr, vaddr + length);
+		outer_cache_op = outer_clean_range;
+		break;
+	case ION_IOC_INV_CACHES:
+		dmac_inv_range(vaddr, vaddr + length);
+		outer_cache_op = outer_inv_range;
+		break;
+	case ION_IOC_CLEAN_INV_CACHES:
+		dmac_flush_range(vaddr, vaddr + length);
+		outer_cache_op = outer_flush_range;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (carveout_heap->has_outer_cache) {
+		unsigned long pstart = buffer->priv_phys + offset;
+		outer_cache_op(pstart, pstart + length);
+	}
+	return 0;
+}
+
+static int ion_carveout_print_debug(struct ion_heap *heap, struct seq_file *s,
+				    const struct rb_root *mem_map)
+{
+	struct ion_carveout_heap *carveout_heap =
+		container_of(heap, struct ion_carveout_heap, heap);
+
+	seq_printf(s, "total bytes currently allocated: %lx\n",
+		carveout_heap->allocated_bytes);
+	seq_printf(s, "total heap size: %lx\n", carveout_heap->total_size);
+
+	if (mem_map) {
+		unsigned long base = carveout_heap->base;
+		unsigned long size = carveout_heap->total_size;
+		unsigned long end = base+size;
+		unsigned long last_end = base;
+		struct rb_node *n;
+
+		seq_printf(s, "\nMemory Map\n");
+		seq_printf(s, "%16.s %14.s %14.s %14.s\n",
+			   "client", "start address", "end address",
+			   "size (hex)");
+
+		for (n = rb_first(mem_map); n; n = rb_next(n)) {
+			struct mem_map_data *data =
+					rb_entry(n, struct mem_map_data, node);
+			const char *client_name = "(null)";
+
+			if (last_end < data->addr) {
+				seq_printf(s, "%16.s %14lx %14lx %14lu (%lx)\n",
+					   "FREE", last_end, data->addr-1,
+					   data->addr-last_end,
+					   data->addr-last_end);
+			}
+
+			if (data->client_name)
+				client_name = data->client_name;
+
+			seq_printf(s, "%16.s %14lx %14lx %14lu (%lx)\n",
+				   client_name, data->addr,
+				   data->addr_end,
+				   data->size, data->size);
+			last_end = data->addr_end+1;
+		}
+		if (last_end < end) {
+			seq_printf(s, "%16.s %14lx %14lx %14lu (%lx)\n", "FREE",
+				last_end, end-1, end-last_end, end-last_end);
+		}
+	}
+	return 0;
+}
+
+int ion_carveout_heap_map_iommu(struct ion_buffer *buffer,
+					struct ion_iommu_map *data,
+					unsigned int domain_num,
+					unsigned int partition_num,
+					unsigned long align,
+					unsigned long iova_length,
+					unsigned long flags)
+{
+	struct iommu_domain *domain;
+	int ret = 0;
+	unsigned long extra;
+	struct scatterlist *sglist = 0;
+	int prot = IOMMU_WRITE | IOMMU_READ;
+	prot |= ION_IS_CACHED(flags) ? IOMMU_CACHE : 0;
+
+	data->mapped_size = iova_length;
+
+	if (!msm_use_iommu()) {
+		data->iova_addr = buffer->priv_phys;
+		return 0;
+	}
+
+	extra = iova_length - buffer->size;
+
+	ret = msm_allocate_iova_address(domain_num, partition_num,
+						data->mapped_size, align,
+						&data->iova_addr);
+
+	if (ret)
+		goto out;
+
+	domain = msm_get_iommu_domain(domain_num);
+
+	if (!domain) {
+		ret = -ENOMEM;
+		goto out1;
+	}
+
+	sglist = vmalloc(sizeof(*sglist));
+	if (!sglist)
+		goto out1;
+
+	sg_init_table(sglist, 1);
+	sglist->length = buffer->size;
+	sglist->offset = 0;
+	sglist->dma_address = buffer->priv_phys;
+
+	ret = iommu_map_range(domain, data->iova_addr, sglist,
+			      buffer->size, prot);
+	if (ret) {
+		pr_err("%s: could not map %lx in domain %p\n",
+			__func__, data->iova_addr, domain);
+		goto out1;
+	}
+
+	if (extra) {
+		unsigned long extra_iova_addr = data->iova_addr + buffer->size;
+		ret = msm_iommu_map_extra(domain, extra_iova_addr, extra,
+					  SZ_4K, prot);
+		if (ret)
+			goto out2;
+	}
+	vfree(sglist);
+	return ret;
+
+out2:
+	iommu_unmap_range(domain, data->iova_addr, buffer->size);
+out1:
+	vfree(sglist);
+	msm_free_iova_address(data->iova_addr, domain_num, partition_num,
+				data->mapped_size);
+
+out:
+
+	return ret;
+}
+
+void ion_carveout_heap_unmap_iommu(struct ion_iommu_map *data)
+{
+	unsigned int domain_num;
+	unsigned int partition_num;
+	struct iommu_domain *domain;
+
+	if (!msm_use_iommu())
+		return;
+
+	domain_num = iommu_map_domain(data);
+	partition_num = iommu_map_partition(data);
+
+	domain = msm_get_iommu_domain(domain_num);
+
+	if (!domain) {
+		WARN(1, "Could not get domain %d. Corruption?\n", domain_num);
+		return;
+	}
+
+	iommu_unmap_range(domain, data->iova_addr, data->mapped_size);
+	msm_free_iova_address(data->iova_addr, domain_num, partition_num,
+				data->mapped_size);
+
+	return;
 }
 
 static struct ion_heap_ops carveout_heap_ops = {
@@ -126,12 +419,20 @@
 	.phys = ion_carveout_heap_phys,
 	.map_user = ion_carveout_heap_map_user,
 	.map_kernel = ion_carveout_heap_map_kernel,
+	.unmap_user = ion_carveout_heap_unmap_user,
 	.unmap_kernel = ion_carveout_heap_unmap_kernel,
+	.map_dma = ion_carveout_heap_map_dma,
+	.unmap_dma = ion_carveout_heap_unmap_dma,
+	.cache_op = ion_carveout_cache_ops,
+	.print_debug = ion_carveout_print_debug,
+	.map_iommu = ion_carveout_heap_map_iommu,
+	.unmap_iommu = ion_carveout_heap_unmap_iommu,
 };
 
 struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *heap_data)
 {
 	struct ion_carveout_heap *carveout_heap;
+	int ret;
 
 	carveout_heap = kzalloc(sizeof(struct ion_carveout_heap), GFP_KERNEL);
 	if (!carveout_heap)
@@ -143,11 +444,32 @@
 		return ERR_PTR(-ENOMEM);
 	}
 	carveout_heap->base = heap_data->base;
-	gen_pool_add(carveout_heap->pool, carveout_heap->base, heap_data->size,
-		     -1);
+	ret = gen_pool_add(carveout_heap->pool, carveout_heap->base,
+			heap_data->size, -1);
+	if (ret < 0) {
+		gen_pool_destroy(carveout_heap->pool);
+		kfree(carveout_heap);
+		return ERR_PTR(-EINVAL);
+	}
 	carveout_heap->heap.ops = &carveout_heap_ops;
 	carveout_heap->heap.type = ION_HEAP_TYPE_CARVEOUT;
+	carveout_heap->allocated_bytes = 0;
+	carveout_heap->total_size = heap_data->size;
+	carveout_heap->has_outer_cache = heap_data->has_outer_cache;
 
+	if (heap_data->extra_data) {
+		struct ion_co_heap_pdata *extra_data =
+				heap_data->extra_data;
+
+		if (extra_data->setup_region)
+			carveout_heap->bus_id = extra_data->setup_region();
+		if (extra_data->request_region)
+			carveout_heap->request_region =
+					extra_data->request_region;
+		if (extra_data->release_region)
+			carveout_heap->release_region =
+					extra_data->release_region;
+	}
 	return &carveout_heap->heap;
 }
 
diff --git a/drivers/gpu/ion/ion_cp_heap.c b/drivers/gpu/ion/ion_cp_heap.c
new file mode 100644
index 0000000..a857988a
--- /dev/null
+++ b/drivers/gpu/ion/ion_cp_heap.c
@@ -0,0 +1,1019 @@
+/*
+ * drivers/gpu/ion/ion_cp_heap.c
+ *
+ * Copyright (C) 2011 Google, Inc.
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * 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/spinlock.h>
+
+#include <linux/err.h>
+#include <linux/genalloc.h>
+#include <linux/io.h>
+#include <linux/ion.h>
+#include <linux/mm.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/memory_alloc.h>
+#include <linux/seq_file.h>
+#include <linux/fmem.h>
+#include <linux/iommu.h>
+
+#include <asm/mach/map.h>
+
+#include <mach/msm_memtypes.h>
+#include <mach/scm.h>
+#include <mach/iommu_domains.h>
+
+#include "ion_priv.h"
+
+#include <asm/mach/map.h>
+#include <asm/cacheflush.h>
+
+/**
+ * struct ion_cp_heap - container for the heap and shared heap data
+
+ * @heap:	the heap information structure
+ * @pool:	memory pool to allocate from.
+ * @base:	the base address of the memory pool.
+ * @permission_type:	Identifier for the memory used by SCM for protecting
+ *			and unprotecting memory.
+ * @secure_base:	Base address used when securing a heap that is shared.
+ * @secure_size:	Size used when securing a heap that is shared.
+ * @lock:	mutex to protect shared access.
+ * @heap_protected:	Indicates whether heap has been protected or not.
+ * @allocated_bytes:	the total number of allocated bytes from the pool.
+ * @total_size:	the total size of the memory pool.
+ * @request_region:	function pointer to call when first mapping of memory
+ *			occurs.
+ * @release_region:	function pointer to call when last mapping of memory
+ *			unmapped.
+ * @bus_id: token used with request/release region.
+ * @kmap_cached_count:	the total number of times this heap has been mapped in
+ *			kernel space (cached).
+ * @kmap_uncached_count:the total number of times this heap has been mapped in
+ *			kernel space (un-cached).
+ * @umap_count:	the total number of times this heap has been mapped in
+ *		user space.
+ * @iommu_iova: saved iova when mapping full heap at once.
+ * @iommu_partition: partition used to map full heap.
+ * @reusable: indicates if the memory should be reused via fmem.
+ * @reserved_vrange: reserved virtual address range for use with fmem
+ * @iommu_map_all:	Indicates whether we should map whole heap into IOMMU.
+ * @iommu_2x_map_domain: Indicates the domain to use for overmapping.
+ * @has_outer_cache:    set to 1 if outer cache is used, 0 otherwise.
+*/
+struct ion_cp_heap {
+	struct ion_heap heap;
+	struct gen_pool *pool;
+	ion_phys_addr_t base;
+	unsigned int permission_type;
+	ion_phys_addr_t secure_base;
+	size_t secure_size;
+	struct mutex lock;
+	unsigned int heap_protected;
+	unsigned long allocated_bytes;
+	unsigned long total_size;
+	int (*request_region)(void *);
+	int (*release_region)(void *);
+	void *bus_id;
+	unsigned long kmap_cached_count;
+	unsigned long kmap_uncached_count;
+	unsigned long umap_count;
+	unsigned long iommu_iova[MAX_DOMAINS];
+	unsigned long iommu_partition[MAX_DOMAINS];
+	int reusable;
+	void *reserved_vrange;
+	int iommu_map_all;
+	int iommu_2x_map_domain;
+	unsigned int has_outer_cache;
+};
+
+enum {
+	HEAP_NOT_PROTECTED = 0,
+	HEAP_PROTECTED = 1,
+};
+
+static int ion_cp_protect_mem(unsigned int phy_base, unsigned int size,
+			unsigned int permission_type);
+
+static int ion_cp_unprotect_mem(unsigned int phy_base, unsigned int size,
+				unsigned int permission_type);
+
+/**
+ * Get the total number of kernel mappings.
+ * Must be called with heap->lock locked.
+ */
+static unsigned long ion_cp_get_total_kmap_count(
+					const struct ion_cp_heap *cp_heap)
+{
+	return cp_heap->kmap_cached_count + cp_heap->kmap_uncached_count;
+}
+
+/**
+ * Protects memory if heap is unsecured heap. Also ensures that we are in
+ * the correct FMEM state if this heap is a reusable heap.
+ * Must be called with heap->lock locked.
+ */
+static int ion_cp_protect(struct ion_heap *heap)
+{
+	struct ion_cp_heap *cp_heap =
+		container_of(heap, struct ion_cp_heap, heap);
+	int ret_value = 0;
+
+	if (cp_heap->heap_protected == HEAP_NOT_PROTECTED) {
+		/* Make sure we are in C state when the heap is protected. */
+		if (cp_heap->reusable && !cp_heap->allocated_bytes) {
+			ret_value = fmem_set_state(FMEM_C_STATE);
+			if (ret_value)
+				goto out;
+		}
+
+		ret_value = ion_cp_protect_mem(cp_heap->secure_base,
+				cp_heap->secure_size, cp_heap->permission_type);
+		if (ret_value) {
+			pr_err("Failed to protect memory for heap %s - "
+				"error code: %d\n", heap->name, ret_value);
+
+			if (cp_heap->reusable && !cp_heap->allocated_bytes) {
+				if (fmem_set_state(FMEM_T_STATE) != 0)
+					pr_err("%s: unable to transition heap to T-state\n",
+						__func__);
+			}
+		} else {
+			cp_heap->heap_protected = HEAP_PROTECTED;
+			pr_debug("Protected heap %s @ 0x%lx\n",
+				heap->name, cp_heap->base);
+		}
+	}
+out:
+	return ret_value;
+}
+
+/**
+ * Unprotects memory if heap is secure heap. Also ensures that we are in
+ * the correct FMEM state if this heap is a reusable heap.
+ * Must be called with heap->lock locked.
+ */
+static void ion_cp_unprotect(struct ion_heap *heap)
+{
+	struct ion_cp_heap *cp_heap =
+		container_of(heap, struct ion_cp_heap, heap);
+
+	if (cp_heap->heap_protected == HEAP_PROTECTED) {
+		int error_code = ion_cp_unprotect_mem(
+			cp_heap->secure_base, cp_heap->secure_size,
+			cp_heap->permission_type);
+		if (error_code) {
+			pr_err("Failed to un-protect memory for heap %s - "
+				"error code: %d\n", heap->name, error_code);
+		} else  {
+			cp_heap->heap_protected = HEAP_NOT_PROTECTED;
+			pr_debug("Un-protected heap %s @ 0x%x\n", heap->name,
+				(unsigned int) cp_heap->base);
+
+			if (cp_heap->reusable && !cp_heap->allocated_bytes) {
+				if (fmem_set_state(FMEM_T_STATE) != 0)
+					pr_err("%s: unable to transition heap to T-state",
+						__func__);
+			}
+		}
+	}
+}
+
+ion_phys_addr_t ion_cp_allocate(struct ion_heap *heap,
+				      unsigned long size,
+				      unsigned long align,
+				      unsigned long flags)
+{
+	unsigned long offset;
+	unsigned long secure_allocation = flags & ION_SECURE;
+
+	struct ion_cp_heap *cp_heap =
+		container_of(heap, struct ion_cp_heap, heap);
+
+	mutex_lock(&cp_heap->lock);
+	if (!secure_allocation && cp_heap->heap_protected == HEAP_PROTECTED) {
+		mutex_unlock(&cp_heap->lock);
+		pr_err("ION cannot allocate un-secure memory from protected"
+			" heap %s\n", heap->name);
+		return ION_CP_ALLOCATE_FAIL;
+	}
+
+	if (secure_allocation &&
+	    (cp_heap->umap_count > 0 || cp_heap->kmap_cached_count > 0)) {
+		mutex_unlock(&cp_heap->lock);
+		pr_err("ION cannot allocate secure memory from heap with "
+			"outstanding mappings: User space: %lu, kernel space "
+			"(cached): %lu\n", cp_heap->umap_count,
+					   cp_heap->kmap_cached_count);
+		return ION_CP_ALLOCATE_FAIL;
+	}
+
+	/*
+	 * if this is the first reusable allocation, transition
+	 * the heap
+	 */
+	if (cp_heap->reusable && !cp_heap->allocated_bytes) {
+		if (fmem_set_state(FMEM_C_STATE) != 0) {
+			mutex_unlock(&cp_heap->lock);
+			return ION_RESERVED_ALLOCATE_FAIL;
+		}
+	}
+
+	cp_heap->allocated_bytes += size;
+	mutex_unlock(&cp_heap->lock);
+
+	offset = gen_pool_alloc_aligned(cp_heap->pool,
+					size, ilog2(align));
+
+	if (!offset) {
+		mutex_lock(&cp_heap->lock);
+		cp_heap->allocated_bytes -= size;
+		if ((cp_heap->total_size -
+		     cp_heap->allocated_bytes) >= size)
+			pr_debug("%s: heap %s has enough memory (%lx) but"
+				" the allocation of size %lx still failed."
+				" Memory is probably fragmented.\n",
+				__func__, heap->name,
+				cp_heap->total_size -
+				cp_heap->allocated_bytes, size);
+
+		if (cp_heap->reusable && !cp_heap->allocated_bytes &&
+		    cp_heap->heap_protected == HEAP_NOT_PROTECTED) {
+			if (fmem_set_state(FMEM_T_STATE) != 0)
+				pr_err("%s: unable to transition heap to T-state\n",
+					__func__);
+		}
+		mutex_unlock(&cp_heap->lock);
+
+		return ION_CP_ALLOCATE_FAIL;
+	}
+
+	return offset;
+}
+
+static void iommu_unmap_all(unsigned long domain_num,
+			    struct ion_cp_heap *cp_heap)
+{
+	unsigned long left_to_unmap = cp_heap->total_size;
+	unsigned long page_size = SZ_64K;
+
+	struct iommu_domain *domain = msm_get_iommu_domain(domain_num);
+	if (domain) {
+		unsigned long temp_iova = cp_heap->iommu_iova[domain_num];
+
+		while (left_to_unmap) {
+			iommu_unmap(domain, temp_iova, page_size);
+			temp_iova += page_size;
+			left_to_unmap -= page_size;
+		}
+		if (domain_num == cp_heap->iommu_2x_map_domain)
+			msm_iommu_unmap_extra(domain, temp_iova,
+					      cp_heap->total_size, SZ_64K);
+	} else {
+		pr_err("Unable to get IOMMU domain %lu\n", domain_num);
+	}
+}
+
+void ion_cp_free(struct ion_heap *heap, ion_phys_addr_t addr,
+		       unsigned long size)
+{
+	struct ion_cp_heap *cp_heap =
+		container_of(heap, struct ion_cp_heap, heap);
+
+	if (addr == ION_CP_ALLOCATE_FAIL)
+		return;
+	gen_pool_free(cp_heap->pool, addr, size);
+
+	mutex_lock(&cp_heap->lock);
+	cp_heap->allocated_bytes -= size;
+
+	if (cp_heap->reusable && !cp_heap->allocated_bytes &&
+	    cp_heap->heap_protected == HEAP_NOT_PROTECTED) {
+		if (fmem_set_state(FMEM_T_STATE) != 0)
+			pr_err("%s: unable to transition heap to T-state\n",
+				__func__);
+	}
+
+	/* Unmap everything if we previously mapped the whole heap at once. */
+	if (!cp_heap->allocated_bytes) {
+		unsigned int i;
+		for (i = 0; i < MAX_DOMAINS; ++i) {
+			if (cp_heap->iommu_iova[i]) {
+				unsigned long vaddr_len = cp_heap->total_size;
+
+				if (i == cp_heap->iommu_2x_map_domain)
+					vaddr_len <<= 1;
+				iommu_unmap_all(i, cp_heap);
+
+				msm_free_iova_address(cp_heap->iommu_iova[i], i,
+						cp_heap->iommu_partition[i],
+						vaddr_len);
+			}
+			cp_heap->iommu_iova[i] = 0;
+			cp_heap->iommu_partition[i] = 0;
+		}
+	}
+	mutex_unlock(&cp_heap->lock);
+}
+
+static int ion_cp_heap_phys(struct ion_heap *heap,
+				  struct ion_buffer *buffer,
+				  ion_phys_addr_t *addr, size_t *len)
+{
+	*addr = buffer->priv_phys;
+	*len = buffer->size;
+	return 0;
+}
+
+static int ion_cp_heap_allocate(struct ion_heap *heap,
+				      struct ion_buffer *buffer,
+				      unsigned long size, unsigned long align,
+				      unsigned long flags)
+{
+	buffer->priv_phys = ion_cp_allocate(heap, size, align, flags);
+	return buffer->priv_phys == ION_CP_ALLOCATE_FAIL ? -ENOMEM : 0;
+}
+
+static void ion_cp_heap_free(struct ion_buffer *buffer)
+{
+	struct ion_heap *heap = buffer->heap;
+
+	ion_cp_free(heap, buffer->priv_phys, buffer->size);
+	buffer->priv_phys = ION_CP_ALLOCATE_FAIL;
+}
+
+struct scatterlist *ion_cp_heap_create_sglist(struct ion_buffer *buffer)
+{
+	struct scatterlist *sglist;
+
+	sglist = vmalloc(sizeof(*sglist));
+	if (!sglist)
+		return ERR_PTR(-ENOMEM);
+
+	sg_init_table(sglist, 1);
+	sglist->length = buffer->size;
+	sglist->offset = 0;
+	sglist->dma_address = buffer->priv_phys;
+
+	return sglist;
+}
+
+struct scatterlist *ion_cp_heap_map_dma(struct ion_heap *heap,
+					      struct ion_buffer *buffer)
+{
+	return ion_cp_heap_create_sglist(buffer);
+}
+
+void ion_cp_heap_unmap_dma(struct ion_heap *heap,
+				 struct ion_buffer *buffer)
+{
+	if (buffer->sglist)
+		vfree(buffer->sglist);
+}
+
+/**
+ * Call request region for SMI memory of this is the first mapping.
+ */
+static int ion_cp_request_region(struct ion_cp_heap *cp_heap)
+{
+	int ret_value = 0;
+	if ((cp_heap->umap_count + ion_cp_get_total_kmap_count(cp_heap)) == 0)
+		if (cp_heap->request_region)
+			ret_value = cp_heap->request_region(cp_heap->bus_id);
+	return ret_value;
+}
+
+/**
+ * Call release region for SMI memory of this is the last un-mapping.
+ */
+static int ion_cp_release_region(struct ion_cp_heap *cp_heap)
+{
+	int ret_value = 0;
+	if ((cp_heap->umap_count + ion_cp_get_total_kmap_count(cp_heap)) == 0)
+		if (cp_heap->release_region)
+			ret_value = cp_heap->release_region(cp_heap->bus_id);
+	return ret_value;
+}
+
+void *ion_map_fmem_buffer(struct ion_buffer *buffer, unsigned long phys_base,
+				void *virt_base, unsigned long flags)
+{
+	int ret;
+	unsigned int offset = buffer->priv_phys - phys_base;
+	unsigned long start = ((unsigned long)virt_base) + offset;
+	const struct mem_type *type = ION_IS_CACHED(flags) ?
+				get_mem_type(MT_DEVICE_CACHED) :
+				get_mem_type(MT_DEVICE);
+
+	if (phys_base > buffer->priv_phys)
+		return NULL;
+
+
+	ret = ioremap_pages(start, buffer->priv_phys, buffer->size, type);
+
+	if (!ret)
+		return (void *)start;
+	else
+		return NULL;
+}
+
+void *ion_cp_heap_map_kernel(struct ion_heap *heap,
+				   struct ion_buffer *buffer,
+				   unsigned long flags)
+{
+	struct ion_cp_heap *cp_heap =
+		container_of(heap, struct ion_cp_heap, heap);
+	void *ret_value = NULL;
+
+	mutex_lock(&cp_heap->lock);
+	if ((cp_heap->heap_protected == HEAP_NOT_PROTECTED) ||
+	    ((cp_heap->heap_protected == HEAP_PROTECTED) &&
+	      !ION_IS_CACHED(flags))) {
+
+		if (ion_cp_request_region(cp_heap)) {
+			mutex_unlock(&cp_heap->lock);
+			return NULL;
+		}
+
+		if (cp_heap->reusable) {
+			ret_value = ion_map_fmem_buffer(buffer, cp_heap->base,
+					cp_heap->reserved_vrange, flags);
+
+		} else {
+			if (ION_IS_CACHED(flags))
+				ret_value = ioremap_cached(buffer->priv_phys,
+							   buffer->size);
+			else
+				ret_value = ioremap(buffer->priv_phys,
+						    buffer->size);
+		}
+
+		if (!ret_value) {
+			ion_cp_release_region(cp_heap);
+		} else {
+			if (ION_IS_CACHED(buffer->flags))
+				++cp_heap->kmap_cached_count;
+			else
+				++cp_heap->kmap_uncached_count;
+		}
+	}
+	mutex_unlock(&cp_heap->lock);
+	return ret_value;
+}
+
+void ion_cp_heap_unmap_kernel(struct ion_heap *heap,
+				    struct ion_buffer *buffer)
+{
+	struct ion_cp_heap *cp_heap =
+		container_of(heap, struct ion_cp_heap, heap);
+
+	if (cp_heap->reusable)
+		unmap_kernel_range((unsigned long)buffer->vaddr, buffer->size);
+	else
+		__arm_iounmap(buffer->vaddr);
+
+	buffer->vaddr = NULL;
+
+	mutex_lock(&cp_heap->lock);
+	if (ION_IS_CACHED(buffer->flags))
+		--cp_heap->kmap_cached_count;
+	else
+		--cp_heap->kmap_uncached_count;
+	ion_cp_release_region(cp_heap);
+	mutex_unlock(&cp_heap->lock);
+
+	return;
+}
+
+int ion_cp_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer,
+			struct vm_area_struct *vma, unsigned long flags)
+{
+	int ret_value = -EAGAIN;
+	struct ion_cp_heap *cp_heap =
+		container_of(heap, struct ion_cp_heap, heap);
+
+	mutex_lock(&cp_heap->lock);
+	if (cp_heap->heap_protected == HEAP_NOT_PROTECTED) {
+		if (ion_cp_request_region(cp_heap)) {
+			mutex_unlock(&cp_heap->lock);
+			return -EINVAL;
+		}
+
+		if (!ION_IS_CACHED(flags))
+			vma->vm_page_prot = pgprot_writecombine(
+							vma->vm_page_prot);
+
+		ret_value =  remap_pfn_range(vma, vma->vm_start,
+			__phys_to_pfn(buffer->priv_phys) + vma->vm_pgoff,
+			vma->vm_end - vma->vm_start,
+			vma->vm_page_prot);
+
+		if (ret_value)
+			ion_cp_release_region(cp_heap);
+		else
+			++cp_heap->umap_count;
+	}
+	mutex_unlock(&cp_heap->lock);
+	return ret_value;
+}
+
+void ion_cp_heap_unmap_user(struct ion_heap *heap,
+			struct ion_buffer *buffer)
+{
+	struct ion_cp_heap *cp_heap =
+			container_of(heap, struct ion_cp_heap, heap);
+
+	mutex_lock(&cp_heap->lock);
+	--cp_heap->umap_count;
+	ion_cp_release_region(cp_heap);
+	mutex_unlock(&cp_heap->lock);
+}
+
+int ion_cp_cache_ops(struct ion_heap *heap, struct ion_buffer *buffer,
+			void *vaddr, unsigned int offset, unsigned int length,
+			unsigned int cmd)
+{
+	void (*outer_cache_op)(phys_addr_t, phys_addr_t);
+	struct ion_cp_heap *cp_heap =
+	     container_of(heap, struct  ion_cp_heap, heap);
+
+	switch (cmd) {
+	case ION_IOC_CLEAN_CACHES:
+		dmac_clean_range(vaddr, vaddr + length);
+		outer_cache_op = outer_clean_range;
+		break;
+	case ION_IOC_INV_CACHES:
+		dmac_inv_range(vaddr, vaddr + length);
+		outer_cache_op = outer_inv_range;
+		break;
+	case ION_IOC_CLEAN_INV_CACHES:
+		dmac_flush_range(vaddr, vaddr + length);
+		outer_cache_op = outer_flush_range;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (cp_heap->has_outer_cache) {
+		unsigned long pstart = buffer->priv_phys + offset;
+		outer_cache_op(pstart, pstart + length);
+	}
+	return 0;
+}
+
+static int ion_cp_print_debug(struct ion_heap *heap, struct seq_file *s,
+			      const struct rb_root *mem_map)
+{
+	unsigned long total_alloc;
+	unsigned long total_size;
+	unsigned long umap_count;
+	unsigned long kmap_count;
+	unsigned long heap_protected;
+	struct ion_cp_heap *cp_heap =
+		container_of(heap, struct ion_cp_heap, heap);
+
+	mutex_lock(&cp_heap->lock);
+	total_alloc = cp_heap->allocated_bytes;
+	total_size = cp_heap->total_size;
+	umap_count = cp_heap->umap_count;
+	kmap_count = ion_cp_get_total_kmap_count(cp_heap);
+	heap_protected = cp_heap->heap_protected == HEAP_PROTECTED;
+	mutex_unlock(&cp_heap->lock);
+
+	seq_printf(s, "total bytes currently allocated: %lx\n", total_alloc);
+	seq_printf(s, "total heap size: %lx\n", total_size);
+	seq_printf(s, "umapping count: %lx\n", umap_count);
+	seq_printf(s, "kmapping count: %lx\n", kmap_count);
+	seq_printf(s, "heap protected: %s\n", heap_protected ? "Yes" : "No");
+	seq_printf(s, "reusable: %s\n", cp_heap->reusable  ? "Yes" : "No");
+
+	if (mem_map) {
+		unsigned long base = cp_heap->base;
+		unsigned long size = cp_heap->total_size;
+		unsigned long end = base+size;
+		unsigned long last_end = base;
+		struct rb_node *n;
+
+		seq_printf(s, "\nMemory Map\n");
+		seq_printf(s, "%16.s %14.s %14.s %14.s\n",
+			   "client", "start address", "end address",
+			   "size (hex)");
+
+		for (n = rb_first(mem_map); n; n = rb_next(n)) {
+			struct mem_map_data *data =
+					rb_entry(n, struct mem_map_data, node);
+			const char *client_name = "(null)";
+
+			if (last_end < data->addr) {
+				seq_printf(s, "%16.s %14lx %14lx %14lu (%lx)\n",
+					   "FREE", last_end, data->addr-1,
+					   data->addr-last_end,
+					   data->addr-last_end);
+			}
+
+			if (data->client_name)
+				client_name = data->client_name;
+
+			seq_printf(s, "%16.s %14lx %14lx %14lu (%lx)\n",
+				   client_name, data->addr,
+				   data->addr_end,
+				   data->size, data->size);
+			last_end = data->addr_end+1;
+		}
+		if (last_end < end) {
+			seq_printf(s, "%16.s %14lx %14lx %14lu (%lx)\n", "FREE",
+				last_end, end-1, end-last_end, end-last_end);
+		}
+	}
+
+	return 0;
+}
+
+int ion_cp_secure_heap(struct ion_heap *heap)
+{
+	int ret_value;
+	struct ion_cp_heap *cp_heap =
+		container_of(heap, struct ion_cp_heap, heap);
+	mutex_lock(&cp_heap->lock);
+	if (cp_heap->umap_count == 0 && cp_heap->kmap_cached_count == 0) {
+		ret_value = ion_cp_protect(heap);
+	} else {
+		pr_err("ION cannot secure heap with outstanding mappings: "
+		       "User space: %lu, kernel space (cached): %lu\n",
+		       cp_heap->umap_count, cp_heap->kmap_cached_count);
+		ret_value = -EINVAL;
+	}
+
+	mutex_unlock(&cp_heap->lock);
+	return ret_value;
+}
+
+int ion_cp_unsecure_heap(struct ion_heap *heap)
+{
+	int ret_value = 0;
+	struct ion_cp_heap *cp_heap =
+		container_of(heap, struct ion_cp_heap, heap);
+	mutex_lock(&cp_heap->lock);
+	ion_cp_unprotect(heap);
+	mutex_unlock(&cp_heap->lock);
+	return ret_value;
+}
+
+static int iommu_map_all(unsigned long domain_num, struct ion_cp_heap *cp_heap,
+			int partition, unsigned long prot)
+{
+	unsigned long left_to_map = cp_heap->total_size;
+	unsigned long page_size = SZ_64K;
+	int ret_value = 0;
+	unsigned long virt_addr_len = cp_heap->total_size;
+	struct iommu_domain *domain = msm_get_iommu_domain(domain_num);
+
+	/* If we are mapping into the video domain we need to map twice the
+	 * size of the heap to account for prefetch issue in video core.
+	 */
+	if (domain_num == cp_heap->iommu_2x_map_domain)
+		virt_addr_len <<= 1;
+
+	if (cp_heap->total_size & (SZ_64K-1)) {
+		pr_err("Heap size is not aligned to 64K, cannot map into IOMMU\n");
+		ret_value = -EINVAL;
+	}
+	if (cp_heap->base & (SZ_64K-1)) {
+		pr_err("Heap physical address is not aligned to 64K, cannot map into IOMMU\n");
+		ret_value = -EINVAL;
+	}
+	if (!ret_value && domain) {
+		unsigned long temp_phys = cp_heap->base;
+		unsigned long temp_iova;
+
+		ret_value = msm_allocate_iova_address(domain_num, partition,
+						virt_addr_len, SZ_64K,
+						&temp_iova);
+
+		if (ret_value) {
+			pr_err("%s: could not allocate iova from domain %lu, partition %d\n",
+				__func__, domain_num, partition);
+			goto out;
+		}
+		cp_heap->iommu_iova[domain_num] = temp_iova;
+
+		while (left_to_map) {
+			int ret = iommu_map(domain, temp_iova, temp_phys,
+					page_size, prot);
+			if (ret) {
+				pr_err("%s: could not map %lx in domain %p, error: %d\n",
+					__func__, temp_iova, domain, ret);
+				ret_value = -EAGAIN;
+				goto free_iova;
+			}
+			temp_iova += page_size;
+			temp_phys += page_size;
+			left_to_map -= page_size;
+		}
+		if (domain_num == cp_heap->iommu_2x_map_domain)
+			ret_value = msm_iommu_map_extra(domain, temp_iova,
+							cp_heap->total_size,
+							SZ_64K, prot);
+		if (ret_value)
+			goto free_iova;
+	} else {
+		pr_err("Unable to get IOMMU domain %lu\n", domain_num);
+		ret_value = -ENOMEM;
+	}
+	goto out;
+
+free_iova:
+	msm_free_iova_address(cp_heap->iommu_iova[domain_num], domain_num,
+			      partition, virt_addr_len);
+out:
+	return ret_value;
+}
+
+static int ion_cp_heap_map_iommu(struct ion_buffer *buffer,
+				struct ion_iommu_map *data,
+				unsigned int domain_num,
+				unsigned int partition_num,
+				unsigned long align,
+				unsigned long iova_length,
+				unsigned long flags)
+{
+	struct iommu_domain *domain;
+	int ret = 0;
+	unsigned long extra;
+	struct scatterlist *sglist = 0;
+	struct ion_cp_heap *cp_heap =
+		container_of(buffer->heap, struct ion_cp_heap, heap);
+	int prot = IOMMU_WRITE | IOMMU_READ;
+	prot |= ION_IS_CACHED(flags) ? IOMMU_CACHE : 0;
+
+	data->mapped_size = iova_length;
+
+	if (!msm_use_iommu()) {
+		data->iova_addr = buffer->priv_phys;
+		return 0;
+	}
+
+	if (cp_heap->iommu_iova[domain_num]) {
+		/* Already mapped. */
+		unsigned long offset = buffer->priv_phys - cp_heap->base;
+		data->iova_addr = cp_heap->iommu_iova[domain_num] + offset;
+		return 0;
+	} else if (cp_heap->iommu_map_all) {
+		ret = iommu_map_all(domain_num, cp_heap, partition_num, prot);
+		if (!ret) {
+			unsigned long offset =
+					buffer->priv_phys - cp_heap->base;
+			data->iova_addr =
+				cp_heap->iommu_iova[domain_num] + offset;
+			cp_heap->iommu_partition[domain_num] = partition_num;
+			/*
+			clear delayed map flag so that we don't interfere
+			with this feature (we are already delaying).
+			*/
+			data->flags &= ~ION_IOMMU_UNMAP_DELAYED;
+			return 0;
+		} else {
+			cp_heap->iommu_iova[domain_num] = 0;
+			cp_heap->iommu_partition[domain_num] = 0;
+			return ret;
+		}
+	}
+
+	extra = iova_length - buffer->size;
+
+	ret = msm_allocate_iova_address(domain_num, partition_num,
+						data->mapped_size, align,
+						&data->iova_addr);
+
+	if (ret)
+		goto out;
+
+	domain = msm_get_iommu_domain(domain_num);
+
+	if (!domain) {
+		ret = -ENOMEM;
+		goto out1;
+	}
+
+	sglist = ion_cp_heap_create_sglist(buffer);
+	if (IS_ERR_OR_NULL(sglist)) {
+		ret = -ENOMEM;
+		goto out1;
+	}
+	ret = iommu_map_range(domain, data->iova_addr, sglist,
+			      buffer->size, prot);
+	if (ret) {
+		pr_err("%s: could not map %lx in domain %p\n",
+			__func__, data->iova_addr, domain);
+		goto out1;
+	}
+
+	if (extra) {
+		unsigned long extra_iova_addr = data->iova_addr + buffer->size;
+		ret = msm_iommu_map_extra(domain, extra_iova_addr, extra,
+					  SZ_4K, prot);
+		if (ret)
+			goto out2;
+	}
+	vfree(sglist);
+	return ret;
+
+out2:
+	iommu_unmap_range(domain, data->iova_addr, buffer->size);
+out1:
+	if (!IS_ERR_OR_NULL(sglist))
+		vfree(sglist);
+	msm_free_iova_address(data->iova_addr, domain_num, partition_num,
+				data->mapped_size);
+out:
+	return ret;
+}
+
+static void ion_cp_heap_unmap_iommu(struct ion_iommu_map *data)
+{
+	unsigned int domain_num;
+	unsigned int partition_num;
+	struct iommu_domain *domain;
+	struct ion_cp_heap *cp_heap =
+		container_of(data->buffer->heap, struct ion_cp_heap, heap);
+
+	if (!msm_use_iommu())
+		return;
+
+
+	domain_num = iommu_map_domain(data);
+
+	/* If we are mapping everything we'll wait to unmap until everything
+	   is freed. */
+	if (cp_heap->iommu_iova[domain_num])
+		return;
+
+	partition_num = iommu_map_partition(data);
+
+	domain = msm_get_iommu_domain(domain_num);
+
+	if (!domain) {
+		WARN(1, "Could not get domain %d. Corruption?\n", domain_num);
+		return;
+	}
+
+	iommu_unmap_range(domain, data->iova_addr, data->mapped_size);
+	msm_free_iova_address(data->iova_addr, domain_num, partition_num,
+				data->mapped_size);
+
+	return;
+}
+
+static struct ion_heap_ops cp_heap_ops = {
+	.allocate = ion_cp_heap_allocate,
+	.free = ion_cp_heap_free,
+	.phys = ion_cp_heap_phys,
+	.map_user = ion_cp_heap_map_user,
+	.unmap_user = ion_cp_heap_unmap_user,
+	.map_kernel = ion_cp_heap_map_kernel,
+	.unmap_kernel = ion_cp_heap_unmap_kernel,
+	.map_dma = ion_cp_heap_map_dma,
+	.unmap_dma = ion_cp_heap_unmap_dma,
+	.cache_op = ion_cp_cache_ops,
+	.print_debug = ion_cp_print_debug,
+	.secure_heap = ion_cp_secure_heap,
+	.unsecure_heap = ion_cp_unsecure_heap,
+	.map_iommu = ion_cp_heap_map_iommu,
+	.unmap_iommu = ion_cp_heap_unmap_iommu,
+};
+
+struct ion_heap *ion_cp_heap_create(struct ion_platform_heap *heap_data)
+{
+	struct ion_cp_heap *cp_heap;
+	int ret;
+
+	cp_heap = kzalloc(sizeof(*cp_heap), GFP_KERNEL);
+	if (!cp_heap)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_init(&cp_heap->lock);
+
+	cp_heap->pool = gen_pool_create(12, -1);
+	if (!cp_heap->pool)
+		goto free_heap;
+
+	cp_heap->base = heap_data->base;
+	ret = gen_pool_add(cp_heap->pool, cp_heap->base, heap_data->size, -1);
+	if (ret < 0)
+		goto destroy_pool;
+
+	cp_heap->allocated_bytes = 0;
+	cp_heap->umap_count = 0;
+	cp_heap->kmap_cached_count = 0;
+	cp_heap->kmap_uncached_count = 0;
+	cp_heap->total_size = heap_data->size;
+	cp_heap->heap.ops = &cp_heap_ops;
+	cp_heap->heap.type = ION_HEAP_TYPE_CP;
+	cp_heap->heap_protected = HEAP_NOT_PROTECTED;
+	cp_heap->secure_base = cp_heap->base;
+	cp_heap->secure_size = heap_data->size;
+	cp_heap->has_outer_cache = heap_data->has_outer_cache;
+	if (heap_data->extra_data) {
+		struct ion_cp_heap_pdata *extra_data =
+				heap_data->extra_data;
+		cp_heap->reusable = extra_data->reusable;
+		cp_heap->reserved_vrange = extra_data->virt_addr;
+		cp_heap->permission_type = extra_data->permission_type;
+		if (extra_data->secure_size) {
+			cp_heap->secure_base = extra_data->secure_base;
+			cp_heap->secure_size = extra_data->secure_size;
+		}
+		if (extra_data->setup_region)
+			cp_heap->bus_id = extra_data->setup_region();
+		if (extra_data->request_region)
+			cp_heap->request_region = extra_data->request_region;
+		if (extra_data->release_region)
+			cp_heap->release_region = extra_data->release_region;
+		cp_heap->iommu_map_all =
+				extra_data->iommu_map_all;
+		cp_heap->iommu_2x_map_domain =
+				extra_data->iommu_2x_map_domain;
+
+	}
+
+	return &cp_heap->heap;
+
+destroy_pool:
+	gen_pool_destroy(cp_heap->pool);
+
+free_heap:
+	kfree(cp_heap);
+
+	return ERR_PTR(-ENOMEM);
+}
+
+void ion_cp_heap_destroy(struct ion_heap *heap)
+{
+	struct ion_cp_heap *cp_heap =
+	     container_of(heap, struct  ion_cp_heap, heap);
+
+	gen_pool_destroy(cp_heap->pool);
+	kfree(cp_heap);
+	cp_heap = NULL;
+}
+
+void ion_cp_heap_get_base(struct ion_heap *heap, unsigned long *base,
+		unsigned long *size) \
+{
+	struct ion_cp_heap *cp_heap =
+	     container_of(heap, struct  ion_cp_heap, heap);
+	*base = cp_heap->base;
+	*size = cp_heap->total_size;
+}
+
+/*  SCM related code for locking down memory for content protection */
+
+#define SCM_CP_LOCK_CMD_ID	0x1
+#define SCM_CP_PROTECT		0x1
+#define SCM_CP_UNPROTECT	0x0
+
+struct cp_lock_msg {
+	unsigned int start;
+	unsigned int end;
+	unsigned int permission_type;
+	unsigned char lock;
+} __attribute__ ((__packed__));
+
+
+static int ion_cp_protect_mem(unsigned int phy_base, unsigned int size,
+			      unsigned int permission_type)
+{
+	struct cp_lock_msg cmd;
+	cmd.start = phy_base;
+	cmd.end = phy_base + size;
+	cmd.permission_type = permission_type;
+	cmd.lock = SCM_CP_PROTECT;
+
+	return scm_call(SCM_SVC_CP, SCM_CP_LOCK_CMD_ID,
+			&cmd, sizeof(cmd), NULL, 0);
+}
+
+static int ion_cp_unprotect_mem(unsigned int phy_base, unsigned int size,
+				unsigned int permission_type)
+{
+	struct cp_lock_msg cmd;
+	cmd.start = phy_base;
+	cmd.end = phy_base + size;
+	cmd.permission_type = permission_type;
+	cmd.lock = SCM_CP_UNPROTECT;
+
+	return scm_call(SCM_SVC_CP, SCM_CP_LOCK_CMD_ID,
+			&cmd, sizeof(cmd), NULL, 0);
+}
diff --git a/drivers/gpu/ion/ion_heap.c b/drivers/gpu/ion/ion_heap.c
index 8ce3c19..6ea49db 100644
--- a/drivers/gpu/ion/ion_heap.c
+++ b/drivers/gpu/ion/ion_heap.c
@@ -2,6 +2,7 @@
  * drivers/gpu/ion/ion_heap.c
  *
  * Copyright (C) 2011 Google, Inc.
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -32,6 +33,12 @@
 	case ION_HEAP_TYPE_CARVEOUT:
 		heap = ion_carveout_heap_create(heap_data);
 		break;
+	case ION_HEAP_TYPE_IOMMU:
+		heap = ion_iommu_heap_create(heap_data);
+		break;
+	case ION_HEAP_TYPE_CP:
+		heap = ion_cp_heap_create(heap_data);
+		break;
 	default:
 		pr_err("%s: Invalid heap type %d\n", __func__,
 		       heap_data->type);
@@ -65,6 +72,12 @@
 	case ION_HEAP_TYPE_CARVEOUT:
 		ion_carveout_heap_destroy(heap);
 		break;
+	case ION_HEAP_TYPE_IOMMU:
+		ion_iommu_heap_destroy(heap);
+		break;
+	case ION_HEAP_TYPE_CP:
+		ion_cp_heap_destroy(heap);
+		break;
 	default:
 		pr_err("%s: Invalid heap type %d\n", __func__,
 		       heap->type);
diff --git a/drivers/gpu/ion/ion_iommu_heap.c b/drivers/gpu/ion/ion_iommu_heap.c
new file mode 100644
index 0000000..621144b
--- /dev/null
+++ b/drivers/gpu/ion/ion_iommu_heap.c
@@ -0,0 +1,349 @@
+/*
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/ion.h>
+#include <linux/mm.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/iommu.h>
+#include <linux/pfn.h>
+#include "ion_priv.h"
+
+#include <asm/mach/map.h>
+#include <asm/page.h>
+#include <asm/cacheflush.h>
+#include <mach/iommu_domains.h>
+
+struct ion_iommu_heap {
+	struct ion_heap heap;
+	unsigned int has_outer_cache;
+};
+
+struct ion_iommu_priv_data {
+	struct page **pages;
+	int nrpages;
+	unsigned long size;
+	struct scatterlist *iommu_sglist;
+};
+
+static int ion_iommu_heap_allocate(struct ion_heap *heap,
+				      struct ion_buffer *buffer,
+				      unsigned long size, unsigned long align,
+				      unsigned long flags)
+{
+	int ret, i;
+	struct ion_iommu_priv_data *data = NULL;
+
+	if (msm_use_iommu()) {
+		data = kmalloc(sizeof(*data), GFP_KERNEL);
+		if (!data)
+			return -ENOMEM;
+
+		data->size = PFN_ALIGN(size);
+		data->nrpages = data->size >> PAGE_SHIFT;
+		data->pages = kzalloc(sizeof(struct page *)*data->nrpages,
+				GFP_KERNEL);
+		if (!data->pages) {
+			ret = -ENOMEM;
+			goto err1;
+		}
+		data->iommu_sglist = vmalloc(sizeof(*data->iommu_sglist) *
+						data->nrpages);
+		if (!data->iommu_sglist) {
+			ret = -ENOMEM;
+			goto err1;
+		}
+
+		sg_init_table(data->iommu_sglist, data->nrpages);
+
+		for (i = 0; i < data->nrpages; i++) {
+			data->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO);
+			if (!data->pages[i])
+				goto err2;
+
+			sg_set_page(&data->iommu_sglist[i], data->pages[i],
+				    PAGE_SIZE, 0);
+		}
+
+
+		buffer->priv_virt = data;
+		return 0;
+
+	} else {
+		return -ENOMEM;
+	}
+
+
+err2:
+	vfree(data->iommu_sglist);
+	data->iommu_sglist = NULL;
+
+	for (i = 0; i < data->nrpages; i++) {
+		if (data->pages[i])
+			__free_page(data->pages[i]);
+	}
+	kfree(data->pages);
+err1:
+	kfree(data);
+	return ret;
+}
+
+static void ion_iommu_heap_free(struct ion_buffer *buffer)
+{
+	struct ion_iommu_priv_data *data = buffer->priv_virt;
+	int i;
+
+	if (!data)
+		return;
+
+	for (i = 0; i < data->nrpages; i++)
+		__free_page(data->pages[i]);
+
+	vfree(data->iommu_sglist);
+	data->iommu_sglist = NULL;
+
+	kfree(data->pages);
+	kfree(data);
+}
+
+void *ion_iommu_heap_map_kernel(struct ion_heap *heap,
+				   struct ion_buffer *buffer,
+				   unsigned long flags)
+{
+	struct ion_iommu_priv_data *data = buffer->priv_virt;
+	pgprot_t page_prot = PAGE_KERNEL;
+
+	if (!data)
+		return NULL;
+
+	if (!ION_IS_CACHED(flags))
+		page_prot = pgprot_noncached(page_prot);
+
+	buffer->vaddr = vmap(data->pages, data->nrpages, VM_IOREMAP, page_prot);
+
+	return buffer->vaddr;
+}
+
+void ion_iommu_heap_unmap_kernel(struct ion_heap *heap,
+				    struct ion_buffer *buffer)
+{
+	if (!buffer->vaddr)
+		return;
+
+	vunmap(buffer->vaddr);
+	buffer->vaddr = NULL;
+}
+
+int ion_iommu_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer,
+			       struct vm_area_struct *vma, unsigned long flags)
+{
+	struct ion_iommu_priv_data *data = buffer->priv_virt;
+	int i;
+	unsigned long curr_addr;
+	if (!data)
+		return -EINVAL;
+
+	if (!ION_IS_CACHED(flags))
+		vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+	curr_addr = vma->vm_start;
+	for (i = 0; i < data->nrpages && curr_addr < vma->vm_end; i++) {
+		if (vm_insert_page(vma, curr_addr, data->pages[i])) {
+			/*
+			 * This will fail the mmap which will
+			 * clean up the vma space properly.
+			 */
+			return -EINVAL;
+		}
+		curr_addr += PAGE_SIZE;
+	}
+	return 0;
+}
+
+int ion_iommu_heap_map_iommu(struct ion_buffer *buffer,
+					struct ion_iommu_map *data,
+					unsigned int domain_num,
+					unsigned int partition_num,
+					unsigned long align,
+					unsigned long iova_length,
+					unsigned long flags)
+{
+	struct iommu_domain *domain;
+	int ret = 0;
+	unsigned long extra;
+	struct ion_iommu_priv_data *buffer_data = buffer->priv_virt;
+	int prot = IOMMU_WRITE | IOMMU_READ;
+	prot |= ION_IS_CACHED(flags) ? IOMMU_CACHE : 0;
+
+	BUG_ON(!msm_use_iommu());
+
+	data->mapped_size = iova_length;
+	extra = iova_length - buffer->size;
+
+	ret = msm_allocate_iova_address(domain_num, partition_num,
+						data->mapped_size, align,
+						&data->iova_addr);
+
+	if (!data->iova_addr)
+		goto out;
+
+	domain = msm_get_iommu_domain(domain_num);
+
+	if (!domain) {
+		ret = -ENOMEM;
+		goto out1;
+	}
+
+	ret = iommu_map_range(domain, data->iova_addr,
+			      buffer_data->iommu_sglist, buffer->size, prot);
+	if (ret) {
+		pr_err("%s: could not map %lx in domain %p\n",
+			__func__, data->iova_addr, domain);
+		goto out1;
+	}
+
+	if (extra) {
+		unsigned long extra_iova_addr = data->iova_addr + buffer->size;
+		ret = msm_iommu_map_extra(domain, extra_iova_addr, extra, SZ_4K,
+					  prot);
+		if (ret)
+			goto out2;
+	}
+	return ret;
+
+out2:
+	iommu_unmap_range(domain, data->iova_addr, buffer->size);
+out1:
+	msm_free_iova_address(data->iova_addr, domain_num, partition_num,
+				buffer->size);
+
+out:
+
+	return ret;
+}
+
+void ion_iommu_heap_unmap_iommu(struct ion_iommu_map *data)
+{
+	unsigned int domain_num;
+	unsigned int partition_num;
+	struct iommu_domain *domain;
+
+	BUG_ON(!msm_use_iommu());
+
+	domain_num = iommu_map_domain(data);
+	partition_num = iommu_map_partition(data);
+
+	domain = msm_get_iommu_domain(domain_num);
+
+	if (!domain) {
+		WARN(1, "Could not get domain %d. Corruption?\n", domain_num);
+		return;
+	}
+
+	iommu_unmap_range(domain, data->iova_addr, data->mapped_size);
+	msm_free_iova_address(data->iova_addr, domain_num, partition_num,
+				data->mapped_size);
+
+	return;
+}
+
+static int ion_iommu_cache_ops(struct ion_heap *heap, struct ion_buffer *buffer,
+			void *vaddr, unsigned int offset, unsigned int length,
+			unsigned int cmd)
+{
+	void (*outer_cache_op)(phys_addr_t, phys_addr_t);
+	struct ion_iommu_heap *iommu_heap =
+	     container_of(heap, struct  ion_iommu_heap, heap);
+
+	switch (cmd) {
+	case ION_IOC_CLEAN_CACHES:
+		dmac_clean_range(vaddr, vaddr + length);
+		outer_cache_op = outer_clean_range;
+		break;
+	case ION_IOC_INV_CACHES:
+		dmac_inv_range(vaddr, vaddr + length);
+		outer_cache_op = outer_inv_range;
+		break;
+	case ION_IOC_CLEAN_INV_CACHES:
+		dmac_flush_range(vaddr, vaddr + length);
+		outer_cache_op = outer_flush_range;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (iommu_heap->has_outer_cache) {
+		unsigned long pstart;
+		unsigned int i;
+		struct ion_iommu_priv_data *data = buffer->priv_virt;
+		if (!data)
+			return -ENOMEM;
+
+		for (i = 0; i < data->nrpages; ++i) {
+			pstart = page_to_phys(data->pages[i]);
+			outer_cache_op(pstart, pstart + PAGE_SIZE);
+		}
+	}
+	return 0;
+}
+
+static struct scatterlist *ion_iommu_heap_map_dma(struct ion_heap *heap,
+					      struct ion_buffer *buffer)
+{
+	struct ion_iommu_priv_data *data = buffer->priv_virt;
+	return data->iommu_sglist;
+}
+
+static void ion_iommu_heap_unmap_dma(struct ion_heap *heap,
+				 struct ion_buffer *buffer)
+{
+}
+
+static struct ion_heap_ops iommu_heap_ops = {
+	.allocate = ion_iommu_heap_allocate,
+	.free = ion_iommu_heap_free,
+	.map_user = ion_iommu_heap_map_user,
+	.map_kernel = ion_iommu_heap_map_kernel,
+	.unmap_kernel = ion_iommu_heap_unmap_kernel,
+	.map_iommu = ion_iommu_heap_map_iommu,
+	.unmap_iommu = ion_iommu_heap_unmap_iommu,
+	.cache_op = ion_iommu_cache_ops,
+	.map_dma = ion_iommu_heap_map_dma,
+	.unmap_dma = ion_iommu_heap_unmap_dma,
+};
+
+struct ion_heap *ion_iommu_heap_create(struct ion_platform_heap *heap_data)
+{
+	struct ion_iommu_heap *iommu_heap;
+
+	iommu_heap = kzalloc(sizeof(struct ion_iommu_heap), GFP_KERNEL);
+	if (!iommu_heap)
+		return ERR_PTR(-ENOMEM);
+
+	iommu_heap->heap.ops = &iommu_heap_ops;
+	iommu_heap->heap.type = ION_HEAP_TYPE_IOMMU;
+	iommu_heap->has_outer_cache = heap_data->has_outer_cache;
+
+	return &iommu_heap->heap;
+}
+
+void ion_iommu_heap_destroy(struct ion_heap *heap)
+{
+	struct ion_iommu_heap *iommu_heap =
+	     container_of(heap, struct  ion_iommu_heap, heap);
+
+	kfree(iommu_heap);
+	iommu_heap = NULL;
+}
diff --git a/drivers/gpu/ion/ion_priv.h b/drivers/gpu/ion/ion_priv.h
index cf4a960..6d636ee 100644
--- a/drivers/gpu/ion/ion_priv.h
+++ b/drivers/gpu/ion/ion_priv.h
@@ -2,6 +2,7 @@
  * drivers/gpu/ion/ion_priv.h
  *
  * Copyright (C) 2011 Google, Inc.
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -22,6 +23,56 @@
 #include <linux/mutex.h>
 #include <linux/rbtree.h>
 #include <linux/ion.h>
+#include <linux/iommu.h>
+#include <linux/seq_file.h>
+
+struct ion_mapping;
+
+struct ion_dma_mapping {
+	struct kref ref;
+	struct scatterlist *sglist;
+};
+
+struct ion_kernel_mapping {
+	struct kref ref;
+	void *vaddr;
+};
+
+enum {
+	DI_PARTITION_NUM = 0,
+	DI_DOMAIN_NUM = 1,
+	DI_MAX,
+};
+
+/**
+ * struct ion_iommu_map - represents a mapping of an ion buffer to an iommu
+ * @iova_addr - iommu virtual address
+ * @node - rb node to exist in the buffer's tree of iommu mappings
+ * @domain_info - contains the partition number and domain number
+ *		domain_info[1] = domain number
+ *		domain_info[0] = partition number
+ * @ref - for reference counting this mapping
+ * @mapped_size - size of the iova space mapped
+ *		(may not be the same as the buffer size)
+ * @flags - iommu domain/partition specific flags.
+ *
+ * Represents a mapping of one ion buffer to a particular iommu domain
+ * and address range. There may exist other mappings of this buffer in
+ * different domains or address ranges. All mappings will have the same
+ * cacheability and security.
+ */
+struct ion_iommu_map {
+	unsigned long iova_addr;
+	struct rb_node node;
+	union {
+		int domain_info[DI_MAX];
+		uint64_t key;
+	};
+	struct ion_buffer *buffer;
+	struct kref ref;
+	int mapped_size;
+	unsigned long flags;
+};
 
 struct ion_buffer *ion_handle_buffer(struct ion_handle *handle);
 
@@ -41,7 +92,7 @@
  * @kmap_cnt:		number of times the buffer is mapped to the kernel
  * @vaddr:		the kenrel mapping if kmap_cnt is not zero
  * @dmap_cnt:		number of times the buffer is mapped for dma
- * @sg_table:		the sg table for the buffer if dmap_cnt is not zero
+ * @sglist:		the scatterlist for the buffer is dmap_cnt is not zero
 */
 struct ion_buffer {
 	struct kref ref;
@@ -58,7 +109,11 @@
 	int kmap_cnt;
 	void *vaddr;
 	int dmap_cnt;
-	struct sg_table *sg_table;
+	struct scatterlist *sglist;
+	int umap_cnt;
+	unsigned int iommu_map_cnt;
+	struct rb_root iommu_maps;
+	int marked;
 };
 
 /**
@@ -72,6 +127,7 @@
  * @map_kernel		map memory to the kernel
  * @unmap_kernel	unmap memory to the kernel
  * @map_user		map memory to userspace
+ * @unmap_user		unmap memory to userspace
  */
 struct ion_heap_ops {
 	int (*allocate) (struct ion_heap *heap,
@@ -80,13 +136,30 @@
 	void (*free) (struct ion_buffer *buffer);
 	int (*phys) (struct ion_heap *heap, struct ion_buffer *buffer,
 		     ion_phys_addr_t *addr, size_t *len);
-	struct sg_table *(*map_dma) (struct ion_heap *heap,
+	struct scatterlist *(*map_dma) (struct ion_heap *heap,
 					struct ion_buffer *buffer);
 	void (*unmap_dma) (struct ion_heap *heap, struct ion_buffer *buffer);
-	void * (*map_kernel) (struct ion_heap *heap, struct ion_buffer *buffer);
+	void * (*map_kernel) (struct ion_heap *heap, struct ion_buffer *buffer,
+				unsigned long flags);
 	void (*unmap_kernel) (struct ion_heap *heap, struct ion_buffer *buffer);
 	int (*map_user) (struct ion_heap *mapper, struct ion_buffer *buffer,
-			 struct vm_area_struct *vma);
+			 struct vm_area_struct *vma, unsigned long flags);
+	void (*unmap_user) (struct ion_heap *mapper, struct ion_buffer *buffer);
+	int (*cache_op)(struct ion_heap *heap, struct ion_buffer *buffer,
+			void *vaddr, unsigned int offset,
+			unsigned int length, unsigned int cmd);
+	int (*map_iommu)(struct ion_buffer *buffer,
+				struct ion_iommu_map *map_data,
+				unsigned int domain_num,
+				unsigned int partition_num,
+				unsigned long align,
+				unsigned long iova_length,
+				unsigned long flags);
+	void (*unmap_iommu)(struct ion_iommu_map *data);
+	int (*print_debug)(struct ion_heap *heap, struct seq_file *s,
+			   const struct rb_root *mem_map);
+	int (*secure_heap)(struct ion_heap *heap);
+	int (*unsecure_heap)(struct ion_heap *heap);
 };
 
 /**
@@ -115,6 +188,26 @@
 };
 
 /**
+ * struct mem_map_data - represents information about the memory map for a heap
+ * @node:		rb node used to store in the tree of mem_map_data
+ * @addr:		start address of memory region.
+ * @addr:		end address of memory region.
+ * @size:		size of memory region
+ * @client_name:		name of the client who owns this buffer.
+ *
+ */
+struct mem_map_data {
+	struct rb_node node;
+	unsigned long addr;
+	unsigned long addr_end;
+	unsigned long size;
+	const char *client_name;
+};
+
+#define iommu_map_domain(__m)		((__m)->domain_info[1])
+#define iommu_map_partition(__m)	((__m)->domain_info[0])
+
+/**
  * ion_device_create - allocates and returns an ion device
  * @custom_ioctl:	arch specific ioctl function if applicable
  *
@@ -155,6 +248,16 @@
 
 struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *);
 void ion_carveout_heap_destroy(struct ion_heap *);
+
+struct ion_heap *ion_iommu_heap_create(struct ion_platform_heap *);
+void ion_iommu_heap_destroy(struct ion_heap *);
+
+struct ion_heap *ion_cp_heap_create(struct ion_platform_heap *);
+void ion_cp_heap_destroy(struct ion_heap *);
+
+struct ion_heap *ion_reusable_heap_create(struct ion_platform_heap *);
+void ion_reusable_heap_destroy(struct ion_heap *);
+
 /**
  * kernel api to allocate/free from carveout -- used when carveout is
  * used to back an architecture specific custom heap
@@ -163,10 +266,58 @@
 				      unsigned long align);
 void ion_carveout_free(struct ion_heap *heap, ion_phys_addr_t addr,
 		       unsigned long size);
+
+
+struct ion_heap *msm_get_contiguous_heap(void);
 /**
- * The carveout heap returns physical addresses, since 0 may be a valid
+ * The carveout/cp heap returns physical addresses, since 0 may be a valid
  * physical address, this is used to indicate allocation failed
  */
 #define ION_CARVEOUT_ALLOCATE_FAIL -1
+#define ION_CP_ALLOCATE_FAIL -1
+
+/**
+ * The reserved heap returns physical addresses, since 0 may be a valid
+ * physical address, this is used to indicate allocation failed
+ */
+#define ION_RESERVED_ALLOCATE_FAIL -1
+
+/**
+ * ion_map_fmem_buffer - map fmem allocated memory into the kernel
+ * @buffer - buffer to map
+ * @phys_base - physical base of the heap
+ * @virt_base - virtual base of the heap
+ * @flags - flags for the heap
+ *
+ * Map fmem allocated memory into the kernel address space. This
+ * is designed to be used by other heaps that need fmem behavior.
+ * The virtual range must be pre-allocated.
+ */
+void *ion_map_fmem_buffer(struct ion_buffer *buffer, unsigned long phys_base,
+				void *virt_base, unsigned long flags);
+
+/**
+ * ion_do_cache_op - do cache operations.
+ *
+ * @client - pointer to ION client.
+ * @handle - pointer to buffer handle.
+ * @uaddr -  virtual address to operate on.
+ * @offset - offset from physical address.
+ * @len - Length of data to do cache operation on.
+ * @cmd - Cache operation to perform:
+ *		ION_IOC_CLEAN_CACHES
+ *		ION_IOC_INV_CACHES
+ *		ION_IOC_CLEAN_INV_CACHES
+ *
+ * Returns 0 on success
+ */
+int ion_do_cache_op(struct ion_client *client, struct ion_handle *handle,
+			void *uaddr, unsigned long offset, unsigned long len,
+			unsigned int cmd);
+
+void ion_cp_heap_get_base(struct ion_heap *heap, unsigned long *base,
+			unsigned long *size);
+
+void ion_mem_map_show(struct ion_heap *heap);
 
 #endif /* _ION_PRIV_H */
diff --git a/drivers/gpu/ion/ion_system_heap.c b/drivers/gpu/ion/ion_system_heap.c
index 116e923..08b271b 100644
--- a/drivers/gpu/ion/ion_system_heap.c
+++ b/drivers/gpu/ion/ion_system_heap.c
@@ -2,6 +2,7 @@
  * drivers/gpu/ion/ion_system_heap.c
  *
  * Copyright (C) 2011 Google, Inc.
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -20,111 +21,291 @@
 #include <linux/scatterlist.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
+#include <linux/iommu.h>
+#include <linux/seq_file.h>
+#include <mach/iommu_domains.h>
 #include "ion_priv.h"
+#include <mach/memory.h>
+#include <asm/cacheflush.h>
+
+static atomic_t system_heap_allocated;
+static atomic_t system_contig_heap_allocated;
+static unsigned int system_heap_has_outer_cache;
+static unsigned int system_heap_contig_has_outer_cache;
 
 static int ion_system_heap_allocate(struct ion_heap *heap,
 				     struct ion_buffer *buffer,
 				     unsigned long size, unsigned long align,
 				     unsigned long flags)
 {
-	struct sg_table *table;
-	struct scatterlist *sg;
-	int i, j;
-	int npages = PAGE_ALIGN(size) / PAGE_SIZE;
-
-	table = kmalloc(sizeof(struct sg_table), GFP_KERNEL);
-	if (!table)
+	buffer->priv_virt = vmalloc_user(size);
+	if (!buffer->priv_virt)
 		return -ENOMEM;
-	i = sg_alloc_table(table, npages, GFP_KERNEL);
-	if (i)
-		goto err0;
-	for_each_sg(table->sgl, sg, table->nents, i) {
-		struct page *page;
-		page = alloc_page(GFP_KERNEL);
-		if (!page)
-			goto err1;
-		sg_set_page(sg, page, PAGE_SIZE, 0);
-	}
-	buffer->priv_virt = table;
+
+	atomic_add(size, &system_heap_allocated);
 	return 0;
-err1:
-	for_each_sg(table->sgl, sg, i, j)
-		__free_page(sg_page(sg));
-	sg_free_table(table);
-err0:
-	kfree(table);
-	return -ENOMEM;
 }
 
 void ion_system_heap_free(struct ion_buffer *buffer)
 {
-	int i;
-	struct scatterlist *sg;
-	struct sg_table *table = buffer->priv_virt;
-
-	for_each_sg(table->sgl, sg, table->nents, i)
-		__free_page(sg_page(sg));
-	if (buffer->sg_table)
-		sg_free_table(buffer->sg_table);
-	kfree(buffer->sg_table);
+	vfree(buffer->priv_virt);
+	atomic_sub(buffer->size, &system_heap_allocated);
 }
 
-struct sg_table *ion_system_heap_map_dma(struct ion_heap *heap,
-					 struct ion_buffer *buffer)
+struct scatterlist *ion_system_heap_map_dma(struct ion_heap *heap,
+					    struct ion_buffer *buffer)
 {
-	return buffer->priv_virt;
+	struct scatterlist *sglist;
+	struct page *page;
+	int i;
+	int npages = PAGE_ALIGN(buffer->size) / PAGE_SIZE;
+	void *vaddr = buffer->priv_virt;
+
+	sglist = vmalloc(npages * sizeof(struct scatterlist));
+	if (!sglist)
+		return ERR_PTR(-ENOMEM);
+	memset(sglist, 0, npages * sizeof(struct scatterlist));
+	sg_init_table(sglist, npages);
+	for (i = 0; i < npages; i++) {
+		page = vmalloc_to_page(vaddr);
+		if (!page)
+			goto end;
+		sg_set_page(&sglist[i], page, PAGE_SIZE, 0);
+		vaddr += PAGE_SIZE;
+	}
+	/* XXX do cache maintenance for dma? */
+	return sglist;
+end:
+	vfree(sglist);
+	return NULL;
 }
 
 void ion_system_heap_unmap_dma(struct ion_heap *heap,
 			       struct ion_buffer *buffer)
 {
-	return;
+	/* XXX undo cache maintenance for dma? */
+	if (buffer->sglist)
+		vfree(buffer->sglist);
 }
 
 void *ion_system_heap_map_kernel(struct ion_heap *heap,
-				 struct ion_buffer *buffer)
+				 struct ion_buffer *buffer,
+				 unsigned long flags)
 {
-	struct scatterlist *sg;
-	int i;
-	void *vaddr;
-	struct sg_table *table = buffer->priv_virt;
-	struct page **pages = kmalloc(sizeof(struct page *) * table->nents,
-				      GFP_KERNEL);
-
-	for_each_sg(table->sgl, sg, table->nents, i)
-		pages[i] = sg_page(sg);
-	vaddr = vmap(pages, table->nents, VM_MAP, PAGE_KERNEL);
-	kfree(pages);
-
-	return vaddr;
+	if (ION_IS_CACHED(flags))
+		return buffer->priv_virt;
+	else {
+		pr_err("%s: cannot map system heap uncached\n", __func__);
+		return ERR_PTR(-EINVAL);
+	}
 }
 
 void ion_system_heap_unmap_kernel(struct ion_heap *heap,
 				  struct ion_buffer *buffer)
 {
-	vunmap(buffer->vaddr);
+}
+
+void ion_system_heap_unmap_iommu(struct ion_iommu_map *data)
+{
+	unsigned int domain_num;
+	unsigned int partition_num;
+	struct iommu_domain *domain;
+
+	if (!msm_use_iommu())
+		return;
+
+	domain_num = iommu_map_domain(data);
+	partition_num = iommu_map_partition(data);
+
+	domain = msm_get_iommu_domain(domain_num);
+
+	if (!domain) {
+		WARN(1, "Could not get domain %d. Corruption?\n", domain_num);
+		return;
+	}
+
+	iommu_unmap_range(domain, data->iova_addr, data->mapped_size);
+	msm_free_iova_address(data->iova_addr, domain_num, partition_num,
+				data->mapped_size);
+
+	return;
 }
 
 int ion_system_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer,
-			     struct vm_area_struct *vma)
+			     struct vm_area_struct *vma, unsigned long flags)
 {
-	struct sg_table *table = buffer->priv_virt;
-	unsigned long addr = vma->vm_start;
-	unsigned long offset = vma->vm_pgoff;
-	struct scatterlist *sg;
-	int i;
+	if (ION_IS_CACHED(flags))
+		return remap_vmalloc_range(vma, buffer->priv_virt,
+						vma->vm_pgoff);
+	else {
+		pr_err("%s: cannot map system heap uncached\n", __func__);
+		return -EINVAL;
+	}
+}
 
-	for_each_sg(table->sgl, sg, table->nents, i) {
-		if (offset) {
-			offset--;
-			continue;
+int ion_system_heap_cache_ops(struct ion_heap *heap, struct ion_buffer *buffer,
+			void *vaddr, unsigned int offset, unsigned int length,
+			unsigned int cmd)
+{
+	void (*outer_cache_op)(phys_addr_t, phys_addr_t);
+
+	switch (cmd) {
+	case ION_IOC_CLEAN_CACHES:
+		dmac_clean_range(vaddr, vaddr + length);
+		outer_cache_op = outer_clean_range;
+		break;
+	case ION_IOC_INV_CACHES:
+		dmac_inv_range(vaddr, vaddr + length);
+		outer_cache_op = outer_inv_range;
+		break;
+	case ION_IOC_CLEAN_INV_CACHES:
+		dmac_flush_range(vaddr, vaddr + length);
+		outer_cache_op = outer_flush_range;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (system_heap_has_outer_cache) {
+		unsigned long pstart;
+		void *vend;
+		void *vtemp;
+		unsigned long ln = 0;
+		vend = buffer->priv_virt + buffer->size;
+		vtemp = buffer->priv_virt + offset;
+
+		if ((vtemp+length) > vend) {
+			pr_err("Trying to flush outside of mapped range.\n");
+			pr_err("End of mapped range: %p, trying to flush to "
+				"address %p\n", vend, vtemp+length);
+			WARN(1, "%s: called with heap name %s, buffer size 0x%x, "
+				"vaddr 0x%p, offset 0x%x, length: 0x%x\n",
+				__func__, heap->name, buffer->size, vaddr,
+				offset, length);
+			return -EINVAL;
 		}
-		vm_insert_page(vma, addr, sg_page(sg));
-		addr += PAGE_SIZE;
+
+		for (; ln < length && vtemp < vend;
+		      vtemp += PAGE_SIZE, ln += PAGE_SIZE) {
+			struct page *page = vmalloc_to_page(vtemp);
+			if (!page) {
+				WARN(1, "Could not find page for virt. address %p\n",
+					vtemp);
+				return -EINVAL;
+			}
+			pstart = page_to_phys(page);
+			/*
+			 * If page -> phys is returning NULL, something
+			 * has really gone wrong...
+			 */
+			if (!pstart) {
+				WARN(1, "Could not translate %p to physical address\n",
+					vtemp);
+				return -EINVAL;
+			}
+
+			outer_cache_op(pstart, pstart + PAGE_SIZE);
+		}
 	}
 	return 0;
 }
 
+static int ion_system_print_debug(struct ion_heap *heap, struct seq_file *s,
+				  const struct rb_root *unused)
+{
+	seq_printf(s, "total bytes currently allocated: %lx\n",
+			(unsigned long) atomic_read(&system_heap_allocated));
+
+	return 0;
+}
+
+int ion_system_heap_map_iommu(struct ion_buffer *buffer,
+				struct ion_iommu_map *data,
+				unsigned int domain_num,
+				unsigned int partition_num,
+				unsigned long align,
+				unsigned long iova_length,
+				unsigned long flags)
+{
+	int ret = 0, i;
+	struct iommu_domain *domain;
+	unsigned long extra;
+	unsigned long extra_iova_addr;
+	struct page *page;
+	int npages = buffer->size >> PAGE_SHIFT;
+	void *vaddr = buffer->priv_virt;
+	struct scatterlist *sglist = 0;
+	int prot = IOMMU_WRITE | IOMMU_READ;
+	prot |= ION_IS_CACHED(flags) ? IOMMU_CACHE : 0;
+
+	if (!ION_IS_CACHED(flags))
+		return -EINVAL;
+
+	if (!msm_use_iommu())
+		return -EINVAL;
+
+	data->mapped_size = iova_length;
+	extra = iova_length - buffer->size;
+
+	ret = msm_allocate_iova_address(domain_num, partition_num,
+						data->mapped_size, align,
+						&data->iova_addr);
+
+	if (ret)
+		goto out;
+
+	domain = msm_get_iommu_domain(domain_num);
+
+	if (!domain) {
+		ret = -ENOMEM;
+		goto out1;
+	}
+
+
+	sglist = vmalloc(sizeof(*sglist) * npages);
+	if (!sglist) {
+		ret = -ENOMEM;
+		goto out1;
+	}
+
+	sg_init_table(sglist, npages);
+	for (i = 0; i < npages; i++) {
+		page = vmalloc_to_page(vaddr);
+		if (!page)
+			goto out1;
+		sg_set_page(&sglist[i], page, PAGE_SIZE, 0);
+		vaddr += PAGE_SIZE;
+	}
+
+	ret = iommu_map_range(domain, data->iova_addr, sglist,
+			      buffer->size, prot);
+
+	if (ret) {
+		pr_err("%s: could not map %lx in domain %p\n",
+			__func__, data->iova_addr, domain);
+		goto out1;
+	}
+
+	extra_iova_addr = data->iova_addr + buffer->size;
+	if (extra) {
+		ret = msm_iommu_map_extra(domain, extra_iova_addr, extra, SZ_4K,
+					  prot);
+		if (ret)
+			goto out2;
+	}
+	vfree(sglist);
+	return ret;
+
+out2:
+	iommu_unmap_range(domain, data->iova_addr, buffer->size);
+out1:
+	vfree(sglist);
+	msm_free_iova_address(data->iova_addr, domain_num, partition_num,
+				data->mapped_size);
+out:
+	return ret;
+}
+
 static struct ion_heap_ops vmalloc_ops = {
 	.allocate = ion_system_heap_allocate,
 	.free = ion_system_heap_free,
@@ -133,9 +314,13 @@
 	.map_kernel = ion_system_heap_map_kernel,
 	.unmap_kernel = ion_system_heap_unmap_kernel,
 	.map_user = ion_system_heap_map_user,
+	.cache_op = ion_system_heap_cache_ops,
+	.print_debug = ion_system_print_debug,
+	.map_iommu = ion_system_heap_map_iommu,
+	.unmap_iommu = ion_system_heap_unmap_iommu,
 };
 
-struct ion_heap *ion_system_heap_create(struct ion_platform_heap *unused)
+struct ion_heap *ion_system_heap_create(struct ion_platform_heap *pheap)
 {
 	struct ion_heap *heap;
 
@@ -144,6 +329,7 @@
 		return ERR_PTR(-ENOMEM);
 	heap->ops = &vmalloc_ops;
 	heap->type = ION_HEAP_TYPE_SYSTEM;
+	system_heap_has_outer_cache = pheap->has_outer_cache;
 	return heap;
 }
 
@@ -161,12 +347,14 @@
 	buffer->priv_virt = kzalloc(len, GFP_KERNEL);
 	if (!buffer->priv_virt)
 		return -ENOMEM;
+	atomic_add(len, &system_contig_heap_allocated);
 	return 0;
 }
 
 void ion_system_contig_heap_free(struct ion_buffer *buffer)
 {
 	kfree(buffer->priv_virt);
+	atomic_sub(buffer->size, &system_contig_heap_allocated);
 }
 
 static int ion_system_contig_heap_phys(struct ion_heap *heap,
@@ -178,34 +366,161 @@
 	return 0;
 }
 
-struct sg_table *ion_system_contig_heap_map_dma(struct ion_heap *heap,
+struct scatterlist *ion_system_contig_heap_map_dma(struct ion_heap *heap,
 						   struct ion_buffer *buffer)
 {
-	struct sg_table *table;
-	int ret;
+	struct scatterlist *sglist;
 
-	table = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
-	if (!table)
+	sglist = vmalloc(sizeof(struct scatterlist));
+	if (!sglist)
 		return ERR_PTR(-ENOMEM);
-	ret = sg_alloc_table(table, 1, GFP_KERNEL);
-	if (ret) {
-		kfree(table);
-		return ERR_PTR(ret);
-	}
-	sg_set_page(table->sgl, virt_to_page(buffer->priv_virt), buffer->size,
-		    0);
-	return table;
+	sg_init_table(sglist, 1);
+	sg_set_page(sglist, virt_to_page(buffer->priv_virt), buffer->size, 0);
+	return sglist;
 }
 
 int ion_system_contig_heap_map_user(struct ion_heap *heap,
 				    struct ion_buffer *buffer,
-				    struct vm_area_struct *vma)
+				    struct vm_area_struct *vma,
+				    unsigned long flags)
 {
 	unsigned long pfn = __phys_to_pfn(virt_to_phys(buffer->priv_virt));
-	return remap_pfn_range(vma, vma->vm_start, pfn + vma->vm_pgoff,
+
+	if (ION_IS_CACHED(flags))
+		return remap_pfn_range(vma, vma->vm_start, pfn + vma->vm_pgoff,
 			       vma->vm_end - vma->vm_start,
 			       vma->vm_page_prot);
+	else {
+		pr_err("%s: cannot map system heap uncached\n", __func__);
+		return -EINVAL;
+	}
+}
 
+int ion_system_contig_heap_cache_ops(struct ion_heap *heap,
+			struct ion_buffer *buffer, void *vaddr,
+			unsigned int offset, unsigned int length,
+			unsigned int cmd)
+{
+	void (*outer_cache_op)(phys_addr_t, phys_addr_t);
+
+	switch (cmd) {
+	case ION_IOC_CLEAN_CACHES:
+		dmac_clean_range(vaddr, vaddr + length);
+		outer_cache_op = outer_clean_range;
+		break;
+	case ION_IOC_INV_CACHES:
+		dmac_inv_range(vaddr, vaddr + length);
+		outer_cache_op = outer_inv_range;
+		break;
+	case ION_IOC_CLEAN_INV_CACHES:
+		dmac_flush_range(vaddr, vaddr + length);
+		outer_cache_op = outer_flush_range;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (system_heap_contig_has_outer_cache) {
+		unsigned long pstart;
+
+		pstart = virt_to_phys(buffer->priv_virt) + offset;
+		if (!pstart) {
+			WARN(1, "Could not do virt to phys translation on %p\n",
+				buffer->priv_virt);
+			return -EINVAL;
+		}
+
+		outer_cache_op(pstart, pstart + PAGE_SIZE);
+	}
+
+	return 0;
+}
+
+static int ion_system_contig_print_debug(struct ion_heap *heap,
+					 struct seq_file *s,
+					 const struct rb_root *unused)
+{
+	seq_printf(s, "total bytes currently allocated: %lx\n",
+		(unsigned long) atomic_read(&system_contig_heap_allocated));
+
+	return 0;
+}
+
+int ion_system_contig_heap_map_iommu(struct ion_buffer *buffer,
+				struct ion_iommu_map *data,
+				unsigned int domain_num,
+				unsigned int partition_num,
+				unsigned long align,
+				unsigned long iova_length,
+				unsigned long flags)
+{
+	int ret = 0;
+	struct iommu_domain *domain;
+	unsigned long extra;
+	struct scatterlist *sglist = 0;
+	struct page *page = 0;
+	int prot = IOMMU_WRITE | IOMMU_READ;
+	prot |= ION_IS_CACHED(flags) ? IOMMU_CACHE : 0;
+
+	if (!ION_IS_CACHED(flags))
+		return -EINVAL;
+
+	if (!msm_use_iommu()) {
+		data->iova_addr = virt_to_phys(buffer->vaddr);
+		return 0;
+	}
+
+	data->mapped_size = iova_length;
+	extra = iova_length - buffer->size;
+
+	ret = msm_allocate_iova_address(domain_num, partition_num,
+						data->mapped_size, align,
+						&data->iova_addr);
+
+	if (ret)
+		goto out;
+
+	domain = msm_get_iommu_domain(domain_num);
+
+	if (!domain) {
+		ret = -ENOMEM;
+		goto out1;
+	}
+	page = virt_to_page(buffer->vaddr);
+
+	sglist = vmalloc(sizeof(*sglist));
+	if (!sglist)
+		goto out1;
+
+	sg_init_table(sglist, 1);
+	sg_set_page(sglist, page, buffer->size, 0);
+
+	ret = iommu_map_range(domain, data->iova_addr, sglist,
+			      buffer->size, prot);
+	if (ret) {
+		pr_err("%s: could not map %lx in domain %p\n",
+			__func__, data->iova_addr, domain);
+		goto out1;
+	}
+
+	if (extra) {
+		unsigned long extra_iova_addr = data->iova_addr + buffer->size;
+		ret = msm_iommu_map_extra(domain, extra_iova_addr, extra, SZ_4K,
+					  prot);
+		if (ret)
+			goto out2;
+	}
+	vfree(sglist);
+	return ret;
+out2:
+	iommu_unmap_range(domain, data->iova_addr, buffer->size);
+
+out1:
+	vfree(sglist);
+	msm_free_iova_address(data->iova_addr, domain_num, partition_num,
+						data->mapped_size);
+out:
+	return ret;
 }
 
 static struct ion_heap_ops kmalloc_ops = {
@@ -217,9 +532,13 @@
 	.map_kernel = ion_system_heap_map_kernel,
 	.unmap_kernel = ion_system_heap_unmap_kernel,
 	.map_user = ion_system_contig_heap_map_user,
+	.cache_op = ion_system_contig_heap_cache_ops,
+	.print_debug = ion_system_contig_print_debug,
+	.map_iommu = ion_system_contig_heap_map_iommu,
+	.unmap_iommu = ion_system_heap_unmap_iommu,
 };
 
-struct ion_heap *ion_system_contig_heap_create(struct ion_platform_heap *unused)
+struct ion_heap *ion_system_contig_heap_create(struct ion_platform_heap *pheap)
 {
 	struct ion_heap *heap;
 
@@ -228,6 +547,7 @@
 		return ERR_PTR(-ENOMEM);
 	heap->ops = &kmalloc_ops;
 	heap->type = ION_HEAP_TYPE_SYSTEM_CONTIG;
+	system_heap_contig_has_outer_cache = pheap->has_outer_cache;
 	return heap;
 }
 
diff --git a/drivers/gpu/ion/msm/Makefile b/drivers/gpu/ion/msm/Makefile
new file mode 100644
index 0000000..bedd8d2
--- /dev/null
+++ b/drivers/gpu/ion/msm/Makefile
@@ -0,0 +1 @@
+obj-y += msm_ion.o
diff --git a/drivers/gpu/ion/msm/msm_ion.c b/drivers/gpu/ion/msm/msm_ion.c
new file mode 100644
index 0000000..f6a4cf4
--- /dev/null
+++ b/drivers/gpu/ion/msm/msm_ion.c
@@ -0,0 +1,347 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/export.h>
+#include <linux/err.h>
+#include <linux/ion.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/memory_alloc.h>
+#include <linux/fmem.h>
+#include <mach/ion.h>
+#include <mach/msm_memtypes.h>
+#include "../ion_priv.h"
+
+static struct ion_device *idev;
+static int num_heaps;
+static struct ion_heap **heaps;
+
+struct ion_client *msm_ion_client_create(unsigned int heap_mask,
+					const char *name)
+{
+	return ion_client_create(idev, heap_mask, name);
+}
+EXPORT_SYMBOL(msm_ion_client_create);
+
+int msm_ion_secure_heap(int heap_id)
+{
+	return ion_secure_heap(idev, heap_id);
+}
+EXPORT_SYMBOL(msm_ion_secure_heap);
+
+int msm_ion_unsecure_heap(int heap_id)
+{
+	return ion_unsecure_heap(idev, heap_id);
+}
+EXPORT_SYMBOL(msm_ion_unsecure_heap);
+
+int msm_ion_do_cache_op(struct ion_client *client, struct ion_handle *handle,
+			void *vaddr, unsigned long len, unsigned int cmd)
+{
+	return ion_do_cache_op(client, handle, vaddr, 0, len, cmd);
+}
+EXPORT_SYMBOL(msm_ion_do_cache_op);
+
+static unsigned long msm_ion_get_base(unsigned long size, int memory_type,
+				    unsigned int align)
+{
+	switch (memory_type) {
+	case ION_EBI_TYPE:
+		return allocate_contiguous_ebi_nomap(size, align);
+		break;
+	case ION_SMI_TYPE:
+		return allocate_contiguous_memory_nomap(size, MEMTYPE_SMI,
+							align);
+		break;
+	default:
+		pr_err("%s: Unknown memory type %d\n", __func__, memory_type);
+		return 0;
+	}
+}
+
+static struct ion_platform_heap *find_heap(const struct ion_platform_heap
+					   heap_data[],
+					   unsigned int nr_heaps,
+					   int heap_id)
+{
+	unsigned int i;
+	for (i = 0; i < nr_heaps; ++i) {
+		const struct ion_platform_heap *heap = &heap_data[i];
+		if (heap->id == heap_id)
+			return (struct ion_platform_heap *) heap;
+	}
+	return 0;
+}
+
+static void ion_set_base_address(struct ion_platform_heap *heap,
+			    struct ion_platform_heap *shared_heap,
+			    struct ion_co_heap_pdata *co_heap_data,
+			    struct ion_cp_heap_pdata *cp_data)
+{
+	if (cp_data->reusable) {
+		const struct fmem_data *fmem_info = fmem_get_info();
+
+		if (!fmem_info) {
+			pr_err("fmem info pointer NULL!\n");
+			BUG();
+		}
+
+		heap->base = fmem_info->phys - fmem_info->reserved_size_low;
+		cp_data->virt_addr = fmem_info->virt;
+		pr_info("ION heap %s using FMEM\n", shared_heap->name);
+	} else {
+		heap->base = msm_ion_get_base(heap->size + shared_heap->size,
+						shared_heap->memory_type,
+						co_heap_data->align);
+	}
+	if (heap->base) {
+		shared_heap->base = heap->base + heap->size;
+		cp_data->secure_base = heap->base;
+		cp_data->secure_size = heap->size + shared_heap->size;
+	} else {
+		pr_err("%s: could not get memory for heap %s (id %x)\n",
+			__func__, heap->name, heap->id);
+	}
+}
+
+static void allocate_co_memory(struct ion_platform_heap *heap,
+			       struct ion_platform_heap heap_data[],
+			       unsigned int nr_heaps)
+{
+	struct ion_co_heap_pdata *co_heap_data =
+		(struct ion_co_heap_pdata *) heap->extra_data;
+
+	if (co_heap_data->adjacent_mem_id != INVALID_HEAP_ID) {
+		struct ion_platform_heap *shared_heap =
+			find_heap(heap_data, nr_heaps,
+				  co_heap_data->adjacent_mem_id);
+		if (shared_heap) {
+			struct ion_cp_heap_pdata *cp_data =
+			   (struct ion_cp_heap_pdata *) shared_heap->extra_data;
+			if (cp_data->fixed_position == FIXED_MIDDLE) {
+				const struct fmem_data *fmem_info =
+					fmem_get_info();
+
+				if (!fmem_info) {
+					pr_err("fmem info pointer NULL!\n");
+					BUG();
+				}
+
+				cp_data->virt_addr = fmem_info->virt;
+				if (!cp_data->secure_base) {
+					cp_data->secure_base = heap->base;
+					cp_data->secure_size =
+						heap->size + shared_heap->size;
+				}
+			} else if (!heap->base) {
+				ion_set_base_address(heap, shared_heap,
+					co_heap_data, cp_data);
+			}
+		}
+	}
+}
+
+/* Fixup heaps in board file to support two heaps being adjacent to each other.
+ * A flag (adjacent_mem_id) in the platform data tells us that the heap phy
+ * memory location must be adjacent to the specified heap. We do this by
+ * carving out memory for both heaps and then splitting up the memory to the
+ * two heaps. The heap specifying the "adjacent_mem_id" get the base of the
+ * memory while heap specified in "adjacent_mem_id" get base+size as its
+ * base address.
+ * Note: Modifies platform data and allocates memory.
+ */
+static void msm_ion_heap_fixup(struct ion_platform_heap heap_data[],
+			       unsigned int nr_heaps)
+{
+	unsigned int i;
+
+	for (i = 0; i < nr_heaps; i++) {
+		struct ion_platform_heap *heap = &heap_data[i];
+		if (heap->type == ION_HEAP_TYPE_CARVEOUT) {
+			if (heap->extra_data)
+				allocate_co_memory(heap, heap_data, nr_heaps);
+		}
+	}
+}
+
+static void msm_ion_allocate(struct ion_platform_heap *heap)
+{
+
+	if (!heap->base && heap->extra_data) {
+		unsigned int align = 0;
+		switch (heap->type) {
+		case ION_HEAP_TYPE_CARVEOUT:
+			align =
+			((struct ion_co_heap_pdata *) heap->extra_data)->align;
+			break;
+		case ION_HEAP_TYPE_CP:
+		{
+			struct ion_cp_heap_pdata *data =
+				(struct ion_cp_heap_pdata *)
+				heap->extra_data;
+			if (data->reusable) {
+				const struct fmem_data *fmem_info =
+					fmem_get_info();
+				heap->base = fmem_info->phys;
+				data->virt_addr = fmem_info->virt;
+				pr_info("ION heap %s using FMEM\n", heap->name);
+			} else if (data->mem_is_fmem) {
+				const struct fmem_data *fmem_info =
+					fmem_get_info();
+				heap->base = fmem_info->phys + fmem_info->size;
+			}
+			align = data->align;
+			break;
+		}
+		default:
+			break;
+		}
+		if (align && !heap->base) {
+			heap->base = msm_ion_get_base(heap->size,
+						      heap->memory_type,
+						      align);
+			if (!heap->base)
+				pr_err("%s: could not get memory for heap %s "
+				   "(id %x)\n", __func__, heap->name, heap->id);
+		}
+	}
+}
+
+static int is_heap_overlapping(const struct ion_platform_heap *heap1,
+				const struct ion_platform_heap *heap2)
+{
+	unsigned long heap1_base = heap1->base;
+	unsigned long heap2_base = heap2->base;
+	unsigned long heap1_end = heap1->base + heap1->size - 1;
+	unsigned long heap2_end = heap2->base + heap2->size - 1;
+
+	if (heap1_base == heap2_base)
+		return 1;
+	if (heap1_base < heap2_base && heap1_end >= heap2_base)
+		return 1;
+	if (heap2_base < heap1_base && heap2_end >= heap1_base)
+		return 1;
+	return 0;
+}
+
+static void check_for_heap_overlap(const struct ion_platform_heap heap_list[],
+				   unsigned long nheaps)
+{
+	unsigned long i;
+	unsigned long j;
+
+	for (i = 0; i < nheaps; ++i) {
+		const struct ion_platform_heap *heap1 = &heap_list[i];
+		if (!heap1->base)
+			continue;
+		for (j = i + 1; j < nheaps; ++j) {
+			const struct ion_platform_heap *heap2 = &heap_list[j];
+			if (!heap2->base)
+				continue;
+			if (is_heap_overlapping(heap1, heap2)) {
+				panic("Memory in heap %s overlaps with heap %s\n",
+					heap1->name, heap2->name);
+			}
+		}
+	}
+}
+
+static int msm_ion_probe(struct platform_device *pdev)
+{
+	struct ion_platform_data *pdata = pdev->dev.platform_data;
+	int err;
+	int i;
+
+	num_heaps = pdata->nr;
+
+	heaps = kcalloc(pdata->nr, sizeof(struct ion_heap *), GFP_KERNEL);
+
+	if (!heaps) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	idev = ion_device_create(NULL);
+	if (IS_ERR_OR_NULL(idev)) {
+		err = PTR_ERR(idev);
+		goto freeheaps;
+	}
+
+	msm_ion_heap_fixup(pdata->heaps, num_heaps);
+
+	/* create the heaps as specified in the board file */
+	for (i = 0; i < num_heaps; i++) {
+		struct ion_platform_heap *heap_data = &pdata->heaps[i];
+		msm_ion_allocate(heap_data);
+
+		heap_data->has_outer_cache = pdata->has_outer_cache;
+		heaps[i] = ion_heap_create(heap_data);
+		if (IS_ERR_OR_NULL(heaps[i])) {
+			heaps[i] = 0;
+			continue;
+		} else {
+			if (heap_data->size)
+				pr_info("ION heap %s created at %lx "
+					"with size %x\n", heap_data->name,
+							  heap_data->base,
+							  heap_data->size);
+			else
+				pr_info("ION heap %s created\n",
+							  heap_data->name);
+		}
+
+		ion_device_add_heap(idev, heaps[i]);
+	}
+
+	check_for_heap_overlap(pdata->heaps, num_heaps);
+	platform_set_drvdata(pdev, idev);
+	return 0;
+
+freeheaps:
+	kfree(heaps);
+out:
+	return err;
+}
+
+static int msm_ion_remove(struct platform_device *pdev)
+{
+	struct ion_device *idev = platform_get_drvdata(pdev);
+	int i;
+
+	for (i = 0; i < num_heaps; i++)
+		ion_heap_destroy(heaps[i]);
+
+	ion_device_destroy(idev);
+	kfree(heaps);
+	return 0;
+}
+
+static struct platform_driver msm_ion_driver = {
+	.probe = msm_ion_probe,
+	.remove = msm_ion_remove,
+	.driver = { .name = "ion-msm" }
+};
+
+static int __init msm_ion_init(void)
+{
+	return platform_driver_register(&msm_ion_driver);
+}
+
+static void __exit msm_ion_exit(void)
+{
+	platform_driver_unregister(&msm_ion_driver);
+}
+
+subsys_initcall(msm_ion_init);
+module_exit(msm_ion_exit);
+
diff --git a/drivers/gpu/msm/Kconfig b/drivers/gpu/msm/Kconfig
new file mode 100644
index 0000000..ba63fbc
--- /dev/null
+++ b/drivers/gpu/msm/Kconfig
@@ -0,0 +1,98 @@
+config MSM_KGSL
+	tristate "MSM 3D Graphics driver"
+	default n
+	depends on ARCH_MSM && !ARCH_MSM7X00A && !ARCH_MSM7X25
+	select GENERIC_ALLOCATOR
+	select FW_LOADER
+	---help---
+	  3D graphics driver. Required to use hardware accelerated
+	  OpenGL ES 2.0 and 1.1.
+
+config MSM_KGSL_CFF_DUMP
+	bool "Enable KGSL Common File Format (CFF) Dump Feature [Use with caution]"
+	default n
+	depends on MSM_KGSL
+	select RELAY
+	---help---
+	  This is an analysis and diagnostic feature only, and should only be
+	  turned on during KGSL GPU diagnostics and will slow down the KGSL
+	  performance sigificantly, hence *do not use in production builds*.
+	  When enabled, CFF Dump is on at boot. It can be turned off at runtime
+	  via 'echo 0 > /d/kgsl/cff_dump'.  The log can be captured via
+	  /d/kgsl-cff/cpu[0|1].
+
+config MSM_KGSL_CFF_DUMP_NO_CONTEXT_MEM_DUMP
+	bool "When selected will disable KGSL CFF Dump for context switches"
+	default n
+	depends on MSM_KGSL_CFF_DUMP
+	---help---
+	  Dumping all the memory for every context switch can produce quite
+	  huge log files, to reduce this, turn this feature on.
+
+config MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL
+	bool "Disable human readable CP_STAT fields in post-mortem dump"
+	default n
+	depends on MSM_KGSL
+	---help---
+	  For a more compact kernel log the human readable output of
+	  CP_STAT can be turned off with this option.
+
+config MSM_KGSL_PSTMRTMDMP_NO_IB_DUMP
+	bool "Disable dumping current IB1 and IB2 in post-mortem dump"
+	default n
+	depends on MSM_KGSL
+	---help---
+	  For a more compact kernel log the IB1 and IB2 embedded dump
+	  can be turned off with this option.  Some IB dumps take up
+	  so much space that vital other information gets cut from the
+	  post-mortem dump.
+
+config MSM_KGSL_PSTMRTMDMP_RB_HEX
+	bool "Use hex version for ring-buffer in post-mortem dump"
+	default n
+	depends on MSM_KGSL
+	---help---
+	  Use hex version for the ring-buffer in the post-mortem dump, instead
+	  of the human readable version.
+
+config MSM_KGSL_2D
+	tristate "MSM 2D graphics driver. Required for OpenVG"
+	default y
+	depends on MSM_KGSL && !ARCH_MSM7X27 && !ARCH_MSM7X27A && !(ARCH_QSD8X50 && !MSM_SOC_REV_A)
+
+config MSM_KGSL_DRM
+	bool "Build a DRM interface for the MSM_KGSL driver"
+	depends on MSM_KGSL && DRM
+
+config KGSL_PER_PROCESS_PAGE_TABLE
+	bool "Enable Per Process page tables for the KGSL driver"
+	default n
+	depends on !MSM_KGSL_DRM
+	---help---
+	  The MMU will use per process pagetables when enabled.
+
+config MSM_KGSL_PAGE_TABLE_SIZE
+	hex "Size of pagetables"
+	default 0xFFF0000
+	---help---
+	  Sets the pagetable size used by the MMU.  The max value
+	  is 0xFFF0000 or (256M - 64K).
+
+config MSM_KGSL_PAGE_TABLE_COUNT
+	int "Minimum of concurrent pagetables to support"
+	default 8
+	depends on KGSL_PER_PROCESS_PAGE_TABLE
+	---help---
+	  Specify the number of pagetables to allocate at init time
+	  This is the number of concurrent processes that are guaranteed to
+	  to run at any time.  Additional processes can be created dynamically
+	  assuming there is enough contiguous memory to allocate the pagetable.
+
+config MSM_KGSL_MMU_PAGE_FAULT
+	bool "Force the GPU MMU to page fault for unmapped regions"
+	default y
+
+config MSM_KGSL_DISABLE_SHADOW_WRITES
+	bool "Disable register shadow writes for context switches"
+	default n
+	depends on MSM_KGSL
diff --git a/drivers/gpu/msm/Makefile b/drivers/gpu/msm/Makefile
new file mode 100644
index 0000000..6cdb5f1
--- /dev/null
+++ b/drivers/gpu/msm/Makefile
@@ -0,0 +1,46 @@
+ccflags-y := -Iinclude/drm -Idrivers/gpu/msm
+
+msm_kgsl_core-y = \
+	kgsl.o \
+	kgsl_trace.o \
+	kgsl_sharedmem.o \
+	kgsl_pwrctrl.o \
+	kgsl_pwrscale.o \
+	kgsl_mmu.o \
+	kgsl_gpummu.o \
+	kgsl_iommu.o \
+	kgsl_snapshot.o
+
+msm_kgsl_core-$(CONFIG_DEBUG_FS) += kgsl_debugfs.o
+msm_kgsl_core-$(CONFIG_MSM_KGSL_CFF_DUMP) += kgsl_cffdump.o
+msm_kgsl_core-$(CONFIG_MSM_KGSL_DRM) += kgsl_drm.o
+msm_kgsl_core-$(CONFIG_MSM_SCM) += kgsl_pwrscale_trustzone.o
+msm_kgsl_core-$(CONFIG_MSM_SLEEP_STATS_DEVICE) += kgsl_pwrscale_idlestats.o
+msm_kgsl_core-$(CONFIG_MSM_DCVS) += kgsl_pwrscale_msm.o
+
+msm_adreno-y += \
+	adreno_ringbuffer.o \
+	adreno_drawctxt.o \
+	adreno_postmortem.o \
+	adreno_snapshot.o \
+	adreno_a2xx.o \
+	adreno_a2xx_trace.o \
+	adreno_a2xx_snapshot.o \
+	adreno_a3xx.o \
+	adreno_a3xx_trace.o \
+	adreno_a3xx_snapshot.o \
+	adreno.o
+
+msm_adreno-$(CONFIG_DEBUG_FS) += adreno_debugfs.o
+
+msm_z180-y += \
+	z180.o \
+	z180_trace.o
+
+msm_kgsl_core-objs = $(msm_kgsl_core-y)
+msm_adreno-objs = $(msm_adreno-y)
+msm_z180-objs = $(msm_z180-y)
+
+obj-$(CONFIG_MSM_KGSL) += msm_kgsl_core.o
+obj-$(CONFIG_MSM_KGSL) += msm_adreno.o
+obj-$(CONFIG_MSM_KGSL_2D) += msm_z180.o
diff --git a/drivers/gpu/msm/a2xx_reg.h b/drivers/gpu/msm/a2xx_reg.h
new file mode 100644
index 0000000..41cb601
--- /dev/null
+++ b/drivers/gpu/msm/a2xx_reg.h
@@ -0,0 +1,438 @@
+/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __A200_REG_H
+#define __A200_REG_H
+
+enum VGT_EVENT_TYPE {
+	VS_DEALLOC = 0,
+	PS_DEALLOC = 1,
+	VS_DONE_TS = 2,
+	PS_DONE_TS = 3,
+	CACHE_FLUSH_TS = 4,
+	CONTEXT_DONE = 5,
+	CACHE_FLUSH = 6,
+	VIZQUERY_START = 7,
+	VIZQUERY_END = 8,
+	SC_WAIT_WC = 9,
+	RST_PIX_CNT = 13,
+	RST_VTX_CNT = 14,
+	TILE_FLUSH = 15,
+	CACHE_FLUSH_AND_INV_TS_EVENT = 20,
+	ZPASS_DONE = 21,
+	CACHE_FLUSH_AND_INV_EVENT = 22,
+	PERFCOUNTER_START = 23,
+	PERFCOUNTER_STOP = 24,
+	VS_FETCH_DONE = 27,
+	FACENESS_FLUSH = 28,
+};
+
+enum COLORFORMATX {
+	COLORX_4_4_4_4 = 0,
+	COLORX_1_5_5_5 = 1,
+	COLORX_5_6_5 = 2,
+	COLORX_8 = 3,
+	COLORX_8_8 = 4,
+	COLORX_8_8_8_8 = 5,
+	COLORX_S8_8_8_8 = 6,
+	COLORX_16_FLOAT = 7,
+	COLORX_16_16_FLOAT = 8,
+	COLORX_16_16_16_16_FLOAT = 9,
+	COLORX_32_FLOAT = 10,
+	COLORX_32_32_FLOAT = 11,
+	COLORX_32_32_32_32_FLOAT = 12,
+	COLORX_2_3_3 = 13,
+	COLORX_8_8_8 = 14,
+};
+
+enum SURFACEFORMAT {
+	FMT_1_REVERSE                  = 0,
+	FMT_1                          = 1,
+	FMT_8                          = 2,
+	FMT_1_5_5_5                    = 3,
+	FMT_5_6_5                      = 4,
+	FMT_6_5_5                      = 5,
+	FMT_8_8_8_8                    = 6,
+	FMT_2_10_10_10                 = 7,
+	FMT_8_A                        = 8,
+	FMT_8_B                        = 9,
+	FMT_8_8                        = 10,
+	FMT_Cr_Y1_Cb_Y0                = 11,
+	FMT_Y1_Cr_Y0_Cb                = 12,
+	FMT_5_5_5_1                    = 13,
+	FMT_8_8_8_8_A                  = 14,
+	FMT_4_4_4_4                    = 15,
+	FMT_10_11_11                   = 16,
+	FMT_11_11_10                   = 17,
+	FMT_DXT1                       = 18,
+	FMT_DXT2_3                     = 19,
+	FMT_DXT4_5                     = 20,
+	FMT_24_8                       = 22,
+	FMT_24_8_FLOAT                 = 23,
+	FMT_16                         = 24,
+	FMT_16_16                      = 25,
+	FMT_16_16_16_16                = 26,
+	FMT_16_EXPAND                  = 27,
+	FMT_16_16_EXPAND               = 28,
+	FMT_16_16_16_16_EXPAND         = 29,
+	FMT_16_FLOAT                   = 30,
+	FMT_16_16_FLOAT                = 31,
+	FMT_16_16_16_16_FLOAT          = 32,
+	FMT_32                         = 33,
+	FMT_32_32                      = 34,
+	FMT_32_32_32_32                = 35,
+	FMT_32_FLOAT                   = 36,
+	FMT_32_32_FLOAT                = 37,
+	FMT_32_32_32_32_FLOAT          = 38,
+	FMT_32_AS_8                    = 39,
+	FMT_32_AS_8_8                  = 40,
+	FMT_16_MPEG                    = 41,
+	FMT_16_16_MPEG                 = 42,
+	FMT_8_INTERLACED               = 43,
+	FMT_32_AS_8_INTERLACED         = 44,
+	FMT_32_AS_8_8_INTERLACED       = 45,
+	FMT_16_INTERLACED              = 46,
+	FMT_16_MPEG_INTERLACED         = 47,
+	FMT_16_16_MPEG_INTERLACED      = 48,
+	FMT_DXN                        = 49,
+	FMT_8_8_8_8_AS_16_16_16_16     = 50,
+	FMT_DXT1_AS_16_16_16_16        = 51,
+	FMT_DXT2_3_AS_16_16_16_16      = 52,
+	FMT_DXT4_5_AS_16_16_16_16      = 53,
+	FMT_2_10_10_10_AS_16_16_16_16  = 54,
+	FMT_10_11_11_AS_16_16_16_16    = 55,
+	FMT_11_11_10_AS_16_16_16_16    = 56,
+	FMT_32_32_32_FLOAT             = 57,
+	FMT_DXT3A                      = 58,
+	FMT_DXT5A                      = 59,
+	FMT_CTX1                       = 60,
+	FMT_DXT3A_AS_1_1_1_1           = 61
+};
+
+#define REG_PERF_MODE_CNT	0x0
+#define REG_PERF_STATE_RESET	0x0
+#define REG_PERF_STATE_ENABLE	0x1
+#define REG_PERF_STATE_FREEZE	0x2
+
+#define RB_EDRAM_INFO_EDRAM_SIZE_SIZE                      4
+#define RB_EDRAM_INFO_EDRAM_MAPPING_MODE_SIZE              2
+#define RB_EDRAM_INFO_UNUSED0_SIZE                         8
+#define RB_EDRAM_INFO_EDRAM_RANGE_SIZE                     18
+
+struct rb_edram_info_t {
+	unsigned int edram_size:RB_EDRAM_INFO_EDRAM_SIZE_SIZE;
+	unsigned int edram_mapping_mode:RB_EDRAM_INFO_EDRAM_MAPPING_MODE_SIZE;
+	unsigned int unused0:RB_EDRAM_INFO_UNUSED0_SIZE;
+	unsigned int edram_range:RB_EDRAM_INFO_EDRAM_RANGE_SIZE;
+};
+
+union reg_rb_edram_info {
+	unsigned int val;
+	struct rb_edram_info_t f;
+};
+
+#define RBBM_READ_ERROR_ADDRESS_MASK	0x0001fffc
+#define RBBM_READ_ERROR_REQUESTER	(1<<30)
+#define RBBM_READ_ERROR_ERROR		(1<<31)
+
+#define CP_RB_CNTL_RB_BUFSZ_SIZE                           6
+#define CP_RB_CNTL_UNUSED0_SIZE                            2
+#define CP_RB_CNTL_RB_BLKSZ_SIZE                           6
+#define CP_RB_CNTL_UNUSED1_SIZE                            2
+#define CP_RB_CNTL_BUF_SWAP_SIZE                           2
+#define CP_RB_CNTL_UNUSED2_SIZE                            2
+#define CP_RB_CNTL_RB_POLL_EN_SIZE                         1
+#define CP_RB_CNTL_UNUSED3_SIZE                            6
+#define CP_RB_CNTL_RB_NO_UPDATE_SIZE                       1
+#define CP_RB_CNTL_UNUSED4_SIZE                            3
+#define CP_RB_CNTL_RB_RPTR_WR_ENA_SIZE                     1
+
+struct cp_rb_cntl_t {
+	unsigned int rb_bufsz:CP_RB_CNTL_RB_BUFSZ_SIZE;
+	unsigned int unused0:CP_RB_CNTL_UNUSED0_SIZE;
+	unsigned int rb_blksz:CP_RB_CNTL_RB_BLKSZ_SIZE;
+	unsigned int unused1:CP_RB_CNTL_UNUSED1_SIZE;
+	unsigned int buf_swap:CP_RB_CNTL_BUF_SWAP_SIZE;
+	unsigned int unused2:CP_RB_CNTL_UNUSED2_SIZE;
+	unsigned int rb_poll_en:CP_RB_CNTL_RB_POLL_EN_SIZE;
+	unsigned int unused3:CP_RB_CNTL_UNUSED3_SIZE;
+	unsigned int rb_no_update:CP_RB_CNTL_RB_NO_UPDATE_SIZE;
+	unsigned int unused4:CP_RB_CNTL_UNUSED4_SIZE;
+	unsigned int rb_rptr_wr_ena:CP_RB_CNTL_RB_RPTR_WR_ENA_SIZE;
+};
+
+union reg_cp_rb_cntl {
+	unsigned int val:32;
+	struct cp_rb_cntl_t f;
+};
+
+#define RB_COLOR_INFO__COLOR_FORMAT_MASK                   0x0000000fL
+#define RB_COPY_DEST_INFO__COPY_DEST_FORMAT__SHIFT         0x00000004
+
+
+#define SQ_INT_CNTL__PS_WATCHDOG_MASK                      0x00000001L
+#define SQ_INT_CNTL__VS_WATCHDOG_MASK                      0x00000002L
+
+#define RBBM_INT_CNTL__RDERR_INT_MASK                      0x00000001L
+#define RBBM_INT_CNTL__DISPLAY_UPDATE_INT_MASK             0x00000002L
+#define RBBM_INT_CNTL__GUI_IDLE_INT_MASK                   0x00080000L
+
+#define RBBM_STATUS__CMDFIFO_AVAIL_MASK                    0x0000001fL
+#define RBBM_STATUS__TC_BUSY_MASK                          0x00000020L
+#define RBBM_STATUS__HIRQ_PENDING_MASK                     0x00000100L
+#define RBBM_STATUS__CPRQ_PENDING_MASK                     0x00000200L
+#define RBBM_STATUS__CFRQ_PENDING_MASK                     0x00000400L
+#define RBBM_STATUS__PFRQ_PENDING_MASK                     0x00000800L
+#define RBBM_STATUS__VGT_BUSY_NO_DMA_MASK                  0x00001000L
+#define RBBM_STATUS__RBBM_WU_BUSY_MASK                     0x00004000L
+#define RBBM_STATUS__CP_NRT_BUSY_MASK                      0x00010000L
+#define RBBM_STATUS__MH_BUSY_MASK                          0x00040000L
+#define RBBM_STATUS__MH_COHERENCY_BUSY_MASK                0x00080000L
+#define RBBM_STATUS__SX_BUSY_MASK                          0x00200000L
+#define RBBM_STATUS__TPC_BUSY_MASK                         0x00400000L
+#define RBBM_STATUS__SC_CNTX_BUSY_MASK                     0x01000000L
+#define RBBM_STATUS__PA_BUSY_MASK                          0x02000000L
+#define RBBM_STATUS__VGT_BUSY_MASK                         0x04000000L
+#define RBBM_STATUS__SQ_CNTX17_BUSY_MASK                   0x08000000L
+#define RBBM_STATUS__SQ_CNTX0_BUSY_MASK                    0x10000000L
+#define RBBM_STATUS__RB_CNTX_BUSY_MASK                     0x40000000L
+#define RBBM_STATUS__GUI_ACTIVE_MASK                       0x80000000L
+
+#define CP_INT_CNTL__SW_INT_MASK                           0x00080000L
+#define CP_INT_CNTL__T0_PACKET_IN_IB_MASK                  0x00800000L
+#define CP_INT_CNTL__OPCODE_ERROR_MASK                     0x01000000L
+#define CP_INT_CNTL__PROTECTED_MODE_ERROR_MASK             0x02000000L
+#define CP_INT_CNTL__RESERVED_BIT_ERROR_MASK               0x04000000L
+#define CP_INT_CNTL__IB_ERROR_MASK                         0x08000000L
+#define CP_INT_CNTL__IB2_INT_MASK                          0x20000000L
+#define CP_INT_CNTL__IB1_INT_MASK                          0x40000000L
+#define CP_INT_CNTL__RB_INT_MASK                           0x80000000L
+
+#define MASTER_INT_SIGNAL__MH_INT_STAT                     0x00000020L
+#define MASTER_INT_SIGNAL__SQ_INT_STAT                     0x04000000L
+#define MASTER_INT_SIGNAL__CP_INT_STAT                     0x40000000L
+#define MASTER_INT_SIGNAL__RBBM_INT_STAT                   0x80000000L
+
+#define RB_EDRAM_INFO__EDRAM_SIZE_MASK                     0x0000000fL
+#define RB_EDRAM_INFO__EDRAM_RANGE_MASK                    0xffffc000L
+
+#define MH_ARBITER_CONFIG__SAME_PAGE_GRANULARITY__SHIFT    0x00000006
+#define MH_ARBITER_CONFIG__L1_ARB_ENABLE__SHIFT            0x00000007
+#define MH_ARBITER_CONFIG__L1_ARB_HOLD_ENABLE__SHIFT       0x00000008
+#define MH_ARBITER_CONFIG__L2_ARB_CONTROL__SHIFT           0x00000009
+#define MH_ARBITER_CONFIG__PAGE_SIZE__SHIFT                0x0000000a
+#define MH_ARBITER_CONFIG__TC_REORDER_ENABLE__SHIFT        0x0000000d
+#define MH_ARBITER_CONFIG__TC_ARB_HOLD_ENABLE__SHIFT       0x0000000e
+#define MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT_ENABLE__SHIFT   0x0000000f
+#define MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT__SHIFT          0x00000010
+#define MH_ARBITER_CONFIG__CP_CLNT_ENABLE__SHIFT           0x00000016
+#define MH_ARBITER_CONFIG__VGT_CLNT_ENABLE__SHIFT          0x00000017
+#define MH_ARBITER_CONFIG__TC_CLNT_ENABLE__SHIFT           0x00000018
+#define MH_ARBITER_CONFIG__RB_CLNT_ENABLE__SHIFT           0x00000019
+#define MH_ARBITER_CONFIG__PA_CLNT_ENABLE__SHIFT           0x0000001a
+
+#define CP_RB_CNTL__RB_BUFSZ__SHIFT                        0x00000000
+#define CP_RB_CNTL__RB_BLKSZ__SHIFT                        0x00000008
+#define CP_RB_CNTL__RB_POLL_EN__SHIFT                      0x00000014
+#define CP_RB_CNTL__RB_NO_UPDATE__SHIFT                    0x0000001b
+
+#define RB_COLOR_INFO__COLOR_FORMAT__SHIFT                 0x00000000
+#define RB_EDRAM_INFO__EDRAM_MAPPING_MODE__SHIFT           0x00000004
+#define RB_EDRAM_INFO__EDRAM_RANGE__SHIFT                  0x0000000e
+
+#define REG_CP_CSQ_IB1_STAT              0x01FE
+#define REG_CP_CSQ_IB2_STAT              0x01FF
+#define REG_CP_CSQ_RB_STAT               0x01FD
+#define REG_CP_DEBUG                     0x01FC
+#define REG_CP_IB1_BASE                  0x0458
+#define REG_CP_IB1_BUFSZ                 0x0459
+#define REG_CP_IB2_BASE                  0x045A
+#define REG_CP_IB2_BUFSZ                 0x045B
+#define REG_CP_INT_ACK                   0x01F4
+#define REG_CP_INT_CNTL                  0x01F2
+#define REG_CP_INT_STATUS                0x01F3
+#define REG_CP_ME_CNTL                   0x01F6
+#define REG_CP_ME_RAM_DATA               0x01FA
+#define REG_CP_ME_RAM_WADDR              0x01F8
+#define REG_CP_ME_RAM_RADDR              0x01F9
+#define REG_CP_ME_STATUS                 0x01F7
+#define REG_CP_PFP_UCODE_ADDR            0x00C0
+#define REG_CP_PFP_UCODE_DATA            0x00C1
+#define REG_CP_QUEUE_THRESHOLDS          0x01D5
+#define REG_CP_RB_BASE                   0x01C0
+#define REG_CP_RB_CNTL                   0x01C1
+#define REG_CP_RB_RPTR                   0x01C4
+#define REG_CP_RB_RPTR_ADDR              0x01C3
+#define REG_CP_RB_RPTR_WR                0x01C7
+#define REG_CP_RB_WPTR                   0x01C5
+#define REG_CP_RB_WPTR_BASE              0x01C8
+#define REG_CP_RB_WPTR_DELAY             0x01C6
+#define REG_CP_STAT                      0x047F
+#define REG_CP_STATE_DEBUG_DATA          0x01ED
+#define REG_CP_STATE_DEBUG_INDEX         0x01EC
+#define REG_CP_ST_BASE                   0x044D
+#define REG_CP_ST_BUFSZ                  0x044E
+
+#define REG_CP_PERFMON_CNTL              0x0444
+#define REG_CP_PERFCOUNTER_SELECT        0x0445
+#define REG_CP_PERFCOUNTER_LO            0x0446
+#define REG_CP_PERFCOUNTER_HI            0x0447
+
+#define REG_RBBM_PERFCOUNTER1_SELECT     0x0395
+#define REG_RBBM_PERFCOUNTER1_HI         0x0398
+#define REG_RBBM_PERFCOUNTER1_LO         0x0397
+
+#define REG_MASTER_INT_SIGNAL            0x03B7
+
+#define REG_PA_CL_VPORT_XSCALE           0x210F
+#define REG_PA_CL_VPORT_ZOFFSET          0x2114
+#define REG_PA_CL_VPORT_ZSCALE           0x2113
+#define REG_PA_CL_CLIP_CNTL              0x2204
+#define REG_PA_CL_VTE_CNTL               0x2206
+#define REG_PA_SC_AA_MASK                0x2312
+#define REG_PA_SC_LINE_CNTL              0x2300
+#define REG_PA_SC_SCREEN_SCISSOR_BR      0x200F
+#define REG_PA_SC_SCREEN_SCISSOR_TL      0x200E
+#define REG_PA_SC_VIZ_QUERY              0x2293
+#define REG_PA_SC_VIZ_QUERY_STATUS       0x0C44
+#define REG_PA_SC_WINDOW_OFFSET          0x2080
+#define REG_PA_SC_WINDOW_SCISSOR_BR      0x2082
+#define REG_PA_SC_WINDOW_SCISSOR_TL      0x2081
+#define REG_PA_SU_FACE_DATA              0x0C86
+#define REG_PA_SU_POINT_SIZE             0x2280
+#define REG_PA_SU_LINE_CNTL              0x2282
+#define REG_PA_SU_POLY_OFFSET_BACK_OFFSET 0x2383
+#define REG_PA_SU_POLY_OFFSET_FRONT_SCALE 0x2380
+#define REG_PA_SU_SC_MODE_CNTL           0x2205
+
+#define REG_PC_INDEX_OFFSET              0x2102
+
+#define REG_RBBM_CNTL                    0x003B
+#define REG_RBBM_INT_ACK                 0x03B6
+#define REG_RBBM_INT_CNTL                0x03B4
+#define REG_RBBM_INT_STATUS              0x03B5
+#define REG_RBBM_PATCH_RELEASE           0x0001
+#define REG_RBBM_PERIPHID1               0x03F9
+#define REG_RBBM_PERIPHID2               0x03FA
+#define REG_RBBM_DEBUG                   0x039B
+#define REG_RBBM_DEBUG_OUT               0x03A0
+#define REG_RBBM_DEBUG_CNTL              0x03A1
+#define REG_RBBM_PM_OVERRIDE1            0x039C
+#define REG_RBBM_PM_OVERRIDE2            0x039D
+#define REG_RBBM_READ_ERROR              0x03B3
+#define REG_RBBM_SOFT_RESET              0x003C
+#define REG_RBBM_STATUS                  0x05D0
+
+#define REG_RB_COLORCONTROL              0x2202
+#define REG_RB_COLOR_DEST_MASK           0x2326
+#define REG_RB_COLOR_MASK                0x2104
+#define REG_RB_COPY_CONTROL              0x2318
+#define REG_RB_DEPTHCONTROL              0x2200
+#define REG_RB_EDRAM_INFO                0x0F02
+#define REG_RB_MODECONTROL               0x2208
+#define REG_RB_SURFACE_INFO              0x2000
+#define REG_RB_SAMPLE_POS                0x220a
+
+#define REG_SCRATCH_ADDR                 0x01DD
+#define REG_SCRATCH_REG0                 0x0578
+#define REG_SCRATCH_REG2                 0x057A
+#define REG_SCRATCH_UMSK                 0x01DC
+
+#define REG_SQ_CF_BOOLEANS               0x4900
+#define REG_SQ_CF_LOOP                   0x4908
+#define REG_SQ_GPR_MANAGEMENT            0x0D00
+#define REG_SQ_FLOW_CONTROL              0x0D01
+#define REG_SQ_INST_STORE_MANAGMENT      0x0D02
+#define REG_SQ_INT_ACK                   0x0D36
+#define REG_SQ_INT_CNTL                  0x0D34
+#define REG_SQ_INT_STATUS                0x0D35
+#define REG_SQ_PROGRAM_CNTL              0x2180
+#define REG_SQ_PS_PROGRAM                0x21F6
+#define REG_SQ_VS_PROGRAM                0x21F7
+#define REG_SQ_WRAPPING_0                0x2183
+#define REG_SQ_WRAPPING_1                0x2184
+
+#define REG_VGT_ENHANCE                  0x2294
+#define REG_VGT_INDX_OFFSET              0x2102
+#define REG_VGT_MAX_VTX_INDX             0x2100
+#define REG_VGT_MIN_VTX_INDX             0x2101
+
+#define REG_TP0_CHICKEN                  0x0E1E
+#define REG_TC_CNTL_STATUS               0x0E00
+#define REG_PA_SC_AA_CONFIG              0x2301
+#define REG_VGT_VERTEX_REUSE_BLOCK_CNTL  0x2316
+#define REG_SQ_INTERPOLATOR_CNTL         0x2182
+#define REG_RB_DEPTH_INFO                0x2002
+#define REG_COHER_DEST_BASE_0            0x2006
+#define REG_RB_FOG_COLOR                 0x2109
+#define REG_RB_STENCILREFMASK_BF         0x210C
+#define REG_PA_SC_LINE_STIPPLE           0x2283
+#define REG_SQ_PS_CONST                  0x2308
+#define REG_RB_DEPTH_CLEAR               0x231D
+#define REG_RB_SAMPLE_COUNT_CTL          0x2324
+#define REG_SQ_CONSTANT_0                0x4000
+#define REG_SQ_FETCH_0                   0x4800
+
+#define REG_COHER_BASE_PM4               0xA2A
+#define REG_COHER_STATUS_PM4             0xA2B
+#define REG_COHER_SIZE_PM4               0xA29
+
+/*registers added in adreno220*/
+#define REG_A220_PC_INDX_OFFSET          REG_VGT_INDX_OFFSET
+#define REG_A220_PC_VERTEX_REUSE_BLOCK_CNTL REG_VGT_VERTEX_REUSE_BLOCK_CNTL
+#define REG_A220_PC_MAX_VTX_INDX         REG_VGT_MAX_VTX_INDX
+#define REG_A220_RB_LRZ_VSC_CONTROL	 0x2209
+#define REG_A220_GRAS_CONTROL            0x2210
+#define REG_A220_VSC_BIN_SIZE            0x0C01
+#define REG_A220_VSC_PIPE_DATA_LENGTH_7  0x0C1D
+
+/*registers added in adreno225*/
+#define REG_A225_RB_COLOR_INFO3          0x2005
+#define REG_A225_PC_MULTI_PRIM_IB_RESET_INDX 0x2103
+#define REG_A225_GRAS_UCP0X              0x2340
+#define REG_A225_GRAS_UCP5W              0x2357
+#define REG_A225_GRAS_UCP_ENABLED        0x2360
+
+/* Debug registers used by snapshot */
+#define REG_PA_SU_DEBUG_CNTL            0x0C80
+#define REG_PA_SU_DEBUG_DATA            0x0C81
+#define REG_RB_DEBUG_CNTL               0x0F26
+#define REG_RB_DEBUG_DATA               0x0F27
+#define REG_PC_DEBUG_CNTL               0x0C38
+#define REG_PC_DEBUG_DATA               0x0C39
+#define REG_GRAS_DEBUG_CNTL             0x0C80
+#define REG_GRAS_DEBUG_DATA             0x0C81
+#define REG_SQ_DEBUG_MISC               0x0D05
+#define REG_SQ_DEBUG_INPUT_FSM          0x0DAE
+#define REG_SQ_DEBUG_CONST_MGR_FSM      0x0DAF
+#define REG_SQ_DEBUG_EXP_ALLOC          0x0DB3
+#define REG_SQ_DEBUG_FSM_ALU_0          0x0DB1
+#define REG_SQ_DEBUG_FSM_ALU_1          0x0DB2
+#define REG_SQ_DEBUG_PTR_BUFF           0x0DB4
+#define REG_SQ_DEBUG_GPR_VTX            0x0DB5
+#define REG_SQ_DEBUG_GPR_PIX            0x0DB6
+#define REG_SQ_DEBUG_TB_STATUS_SEL      0x0DB7
+#define REG_SQ_DEBUG_VTX_TB_0           0x0DB8
+#define REG_SQ_DEBUG_VTX_TB_1           0x0DB9
+#define REG_SQ_DEBUG_VTX_TB_STATE_MEM   0x0DBB
+#define REG_SQ_DEBUG_TP_FSM             0x0DB0
+#define REG_SQ_DEBUG_VTX_TB_STATUS_REG  0x0DBA
+#define REG_SQ_DEBUG_PIX_TB_0           0x0DBC
+#define REG_SQ_DEBUG_PIX_TB_STATUS_REG_0 0x0DBD
+#define REG_SQ_DEBUG_PIX_TB_STATUS_REG_1 0x0DBE
+#define REG_SQ_DEBUG_PIX_TB_STATUS_REG_2 0x0DBF
+#define REG_SQ_DEBUG_PIX_TB_STATUS_REG_3 0x0DC0
+#define REG_SQ_DEBUG_PIX_TB_STATE_MEM   0x0DC1
+#define REG_SQ_DEBUG_MISC_0             0x2309
+#define REG_SQ_DEBUG_MISC_1             0x230A
+
+#endif /* __A200_REG_H */
diff --git a/drivers/gpu/msm/a3xx_reg.h b/drivers/gpu/msm/a3xx_reg.h
new file mode 100644
index 0000000..35af06e
--- /dev/null
+++ b/drivers/gpu/msm/a3xx_reg.h
@@ -0,0 +1,514 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _A300_REG_H
+#define _A300_REG_H
+
+/* Interrupt bit positions within RBBM_INT_0 */
+
+#define A3XX_INT_RBBM_GPU_IDLE 0
+#define A3XX_INT_RBBM_AHB_ERROR 1
+#define A3XX_INT_RBBM_REG_TIMEOUT 2
+#define A3XX_INT_RBBM_ME_MS_TIMEOUT 3
+#define A3XX_INT_RBBM_PFP_MS_TIMEOUT 4
+#define A3XX_INT_RBBM_ATB_BUS_OVERFLOW 5
+#define A3XX_INT_VFD_ERROR 6
+#define A3XX_INT_CP_SW_INT 7
+#define A3XX_INT_CP_T0_PACKET_IN_IB 8
+#define A3XX_INT_CP_OPCODE_ERROR 9
+#define A3XX_INT_CP_RESERVED_BIT_ERROR 10
+#define A3XX_INT_CP_HW_FAULT 11
+#define A3XX_INT_CP_DMA 12
+#define A3XX_INT_CP_IB2_INT 13
+#define A3XX_INT_CP_IB1_INT 14
+#define A3XX_INT_CP_RB_INT 15
+#define A3XX_INT_CP_REG_PROTECT_FAULT 16
+#define A3XX_INT_CP_RB_DONE_TS 17
+#define A3XX_INT_CP_VS_DONE_TS 18
+#define A3XX_INT_CP_PS_DONE_TS 19
+#define A3XX_INT_CACHE_FLUSH_TS 20
+#define A3XX_INT_CP_AHB_ERROR_HALT 21
+#define A3XX_INT_MISC_HANG_DETECT 24
+#define A3XX_INT_UCHE_OOB_ACCESS 25
+
+/* Register definitions */
+
+#define A3XX_RBBM_HW_VERSION 0x000
+#define A3XX_RBBM_HW_RELEASE 0x001
+#define A3XX_RBBM_HW_CONFIGURATION 0x002
+#define A3XX_RBBM_SP_HYST_CNT 0x012
+#define A3XX_RBBM_SW_RESET_CMD 0x018
+#define A3XX_RBBM_AHB_CTL0 0x020
+#define A3XX_RBBM_AHB_CTL1 0x021
+#define A3XX_RBBM_AHB_CMD 0x022
+#define A3XX_RBBM_AHB_ERROR_STATUS 0x027
+#define A3XX_RBBM_GPR0_CTL 0x02E
+/* This the same register as on A2XX, just in a different place */
+#define A3XX_RBBM_STATUS 0x030
+#define A3XX_RBBM_WAIT_IDLE_CLOCKS_CTL 0x33
+#define A3XX_RBBM_INTERFACE_HANG_INT_CTL 0x50
+#define A3XX_RBBM_INTERFACE_HANG_MASK_CTL0 0x51
+#define A3XX_RBBM_INTERFACE_HANG_MASK_CTL1 0x54
+#define A3XX_RBBM_INTERFACE_HANG_MASK_CTL2 0x57
+#define A3XX_RBBM_INTERFACE_HANG_MASK_CTL3 0x5A
+#define A3XX_RBBM_INT_CLEAR_CMD 0x061
+#define A3XX_RBBM_INT_0_MASK 0x063
+#define A3XX_RBBM_INT_0_STATUS 0x064
+#define A3XX_RBBM_GPU_BUSY_MASKED 0x88
+#define A3XX_RBBM_RBBM_CTL 0x100
+#define A3XX_RBBM_RBBM_CTL 0x100
+#define A3XX_RBBM_PERFCTR_PWR_1_LO 0x0EC
+#define A3XX_RBBM_PERFCTR_PWR_1_HI 0x0ED
+#define A3XX_RBBM_DEBUG_BUS_CTL             0x111
+#define A3XX_RBBM_DEBUG_BUS_DATA_STATUS     0x112
+/* Following two are same as on A2XX, just in a different place */
+#define A3XX_CP_PFP_UCODE_ADDR 0x1C9
+#define A3XX_CP_PFP_UCODE_DATA 0x1CA
+#define A3XX_CP_ROQ_ADDR 0x1CC
+#define A3XX_CP_ROQ_DATA 0x1CD
+#define A3XX_CP_MEQ_ADDR 0x1DA
+#define A3XX_CP_MEQ_DATA 0x1DB
+#define A3XX_CP_HW_FAULT  0x45C
+#define A3XX_CP_AHB_FAULT 0x54D
+#define A3XX_CP_PROTECT_CTRL 0x45E
+#define A3XX_CP_PROTECT_STATUS 0x45F
+#define A3XX_CP_PROTECT_REG_0 0x460
+#define A3XX_CP_PROTECT_REG_1 0x461
+#define A3XX_CP_PROTECT_REG_2 0x462
+#define A3XX_CP_PROTECT_REG_3 0x463
+#define A3XX_CP_PROTECT_REG_4 0x464
+#define A3XX_CP_PROTECT_REG_5 0x465
+#define A3XX_CP_PROTECT_REG_6 0x466
+#define A3XX_CP_PROTECT_REG_7 0x467
+#define A3XX_CP_PROTECT_REG_8 0x468
+#define A3XX_CP_PROTECT_REG_9 0x469
+#define A3XX_CP_PROTECT_REG_A 0x46A
+#define A3XX_CP_PROTECT_REG_B 0x46B
+#define A3XX_CP_PROTECT_REG_C 0x46C
+#define A3XX_CP_PROTECT_REG_D 0x46D
+#define A3XX_CP_PROTECT_REG_E 0x46E
+#define A3XX_CP_PROTECT_REG_F 0x46F
+#define A3XX_CP_SCRATCH_REG2 0x57A
+#define A3XX_CP_SCRATCH_REG3 0x57B
+#define A3XX_VSC_BIN_SIZE 0xC01
+#define A3XX_VSC_SIZE_ADDRESS 0xC02
+#define A3XX_VSC_PIPE_CONFIG_0 0xC06
+#define A3XX_VSC_PIPE_DATA_ADDRESS_0 0xC07
+#define A3XX_VSC_PIPE_DATA_LENGTH_0 0xC08
+#define A3XX_VSC_PIPE_CONFIG_1 0xC09
+#define A3XX_VSC_PIPE_DATA_ADDRESS_1 0xC0A
+#define A3XX_VSC_PIPE_DATA_LENGTH_1 0xC0B
+#define A3XX_VSC_PIPE_CONFIG_2 0xC0C
+#define A3XX_VSC_PIPE_DATA_ADDRESS_2 0xC0D
+#define A3XX_VSC_PIPE_DATA_LENGTH_2 0xC0E
+#define A3XX_VSC_PIPE_CONFIG_3 0xC0F
+#define A3XX_VSC_PIPE_DATA_ADDRESS_3 0xC10
+#define A3XX_VSC_PIPE_DATA_LENGTH_3 0xC11
+#define A3XX_VSC_PIPE_CONFIG_4 0xC12
+#define A3XX_VSC_PIPE_DATA_ADDRESS_4 0xC13
+#define A3XX_VSC_PIPE_DATA_LENGTH_4 0xC14
+#define A3XX_VSC_PIPE_CONFIG_5 0xC15
+#define A3XX_VSC_PIPE_DATA_ADDRESS_5 0xC16
+#define A3XX_VSC_PIPE_DATA_LENGTH_5 0xC17
+#define A3XX_VSC_PIPE_CONFIG_6 0xC18
+#define A3XX_VSC_PIPE_DATA_ADDRESS_6 0xC19
+#define A3XX_VSC_PIPE_DATA_LENGTH_6 0xC1A
+#define A3XX_VSC_PIPE_CONFIG_7 0xC1B
+#define A3XX_VSC_PIPE_DATA_ADDRESS_7 0xC1C
+#define A3XX_VSC_PIPE_DATA_LENGTH_7 0xC1D
+#define A3XX_GRAS_CL_USER_PLANE_X0 0xCA0
+#define A3XX_GRAS_CL_USER_PLANE_Y0 0xCA1
+#define A3XX_GRAS_CL_USER_PLANE_Z0 0xCA2
+#define A3XX_GRAS_CL_USER_PLANE_W0 0xCA3
+#define A3XX_GRAS_CL_USER_PLANE_X1 0xCA4
+#define A3XX_GRAS_CL_USER_PLANE_Y1 0xCA5
+#define A3XX_GRAS_CL_USER_PLANE_Z1 0xCA6
+#define A3XX_GRAS_CL_USER_PLANE_W1 0xCA7
+#define A3XX_GRAS_CL_USER_PLANE_X2 0xCA8
+#define A3XX_GRAS_CL_USER_PLANE_Y2 0xCA9
+#define A3XX_GRAS_CL_USER_PLANE_Z2 0xCAA
+#define A3XX_GRAS_CL_USER_PLANE_W2 0xCAB
+#define A3XX_GRAS_CL_USER_PLANE_X3 0xCAC
+#define A3XX_GRAS_CL_USER_PLANE_Y3 0xCAD
+#define A3XX_GRAS_CL_USER_PLANE_Z3 0xCAE
+#define A3XX_GRAS_CL_USER_PLANE_W3 0xCAF
+#define A3XX_GRAS_CL_USER_PLANE_X4 0xCB0
+#define A3XX_GRAS_CL_USER_PLANE_Y4 0xCB1
+#define A3XX_GRAS_CL_USER_PLANE_Z4 0xCB2
+#define A3XX_GRAS_CL_USER_PLANE_W4 0xCB3
+#define A3XX_GRAS_CL_USER_PLANE_X5 0xCB4
+#define A3XX_GRAS_CL_USER_PLANE_Y5 0xCB5
+#define A3XX_GRAS_CL_USER_PLANE_Z5 0xCB6
+#define A3XX_GRAS_CL_USER_PLANE_W5 0xCB7
+#define A3XX_VPC_VPC_DEBUG_RAM_SEL 0xE61
+#define A3XX_VPC_VPC_DEBUG_RAM_READ 0xE62
+#define A3XX_UCHE_CACHE_INVALIDATE0_REG 0xEA0
+#define A3XX_GRAS_CL_CLIP_CNTL 0x2040
+#define A3XX_GRAS_CL_GB_CLIP_ADJ 0x2044
+#define A3XX_GRAS_CL_VPORT_XOFFSET 0x2048
+#define A3XX_GRAS_CL_VPORT_ZOFFSET 0x204C
+#define A3XX_GRAS_CL_VPORT_ZSCALE 0x204D
+#define A3XX_GRAS_SU_POINT_MINMAX 0x2068
+#define A3XX_GRAS_SU_POINT_SIZE 0x2069
+#define A3XX_GRAS_SU_POLY_OFFSET_SCALE 0x206C
+#define A3XX_GRAS_SU_POLY_OFFSET_OFFSET 0x206D
+#define A3XX_GRAS_SU_MODE_CONTROL 0x2070
+#define A3XX_GRAS_SC_CONTROL 0x2072
+#define A3XX_GRAS_SC_SCREEN_SCISSOR_TL 0x2074
+#define A3XX_GRAS_SC_SCREEN_SCISSOR_BR 0x2075
+#define A3XX_GRAS_SC_WINDOW_SCISSOR_TL 0x2079
+#define A3XX_GRAS_SC_WINDOW_SCISSOR_BR 0x207A
+#define A3XX_RB_MODE_CONTROL 0x20C0
+#define A3XX_RB_RENDER_CONTROL 0x20C1
+#define A3XX_RB_MSAA_CONTROL 0x20C2
+#define A3XX_RB_MRT_CONTROL0 0x20C4
+#define A3XX_RB_MRT_BUF_INFO0 0x20C5
+#define A3XX_RB_MRT_BLEND_CONTROL0 0x20C7
+#define A3XX_RB_MRT_BLEND_CONTROL1 0x20CB
+#define A3XX_RB_MRT_BLEND_CONTROL2 0x20CF
+#define A3XX_RB_MRT_BLEND_CONTROL3 0x20D3
+#define A3XX_RB_BLEND_RED 0x20E4
+#define A3XX_RB_COPY_CONTROL 0x20EC
+#define A3XX_RB_COPY_DEST_INFO 0x20EF
+#define A3XX_RB_DEPTH_CONTROL 0x2100
+#define A3XX_RB_STENCIL_CONTROL 0x2104
+#define A3XX_PC_VSTREAM_CONTROL 0x21E4
+#define A3XX_PC_VERTEX_REUSE_BLOCK_CNTL 0x21EA
+#define A3XX_PC_PRIM_VTX_CNTL 0x21EC
+#define A3XX_PC_RESTART_INDEX 0x21ED
+#define A3XX_HLSQ_CONTROL_0_REG 0x2200
+#define A3XX_HLSQ_VS_CONTROL_REG 0x2204
+#define A3XX_HLSQ_CONST_FSPRESV_RANGE_REG 0x2207
+#define A3XX_HLSQ_CL_NDRANGE_0_REG 0x220A
+#define A3XX_HLSQ_CL_NDRANGE_2_REG 0x220C
+#define A3XX_HLSQ_CL_CONTROL_0_REG 0x2211
+#define A3XX_HLSQ_CL_CONTROL_1_REG 0x2212
+#define A3XX_HLSQ_CL_KERNEL_CONST_REG 0x2214
+#define A3XX_HLSQ_CL_KERNEL_GROUP_X_REG 0x2215
+#define A3XX_HLSQ_CL_KERNEL_GROUP_Z_REG 0x2217
+#define A3XX_HLSQ_CL_WG_OFFSET_REG 0x221A
+#define A3XX_VFD_CONTROL_0 0x2240
+#define A3XX_VFD_INDEX_MIN 0x2242
+#define A3XX_VFD_INDEX_MAX 0x2243
+#define A3XX_VFD_FETCH_INSTR_0_0 0x2246
+#define A3XX_VFD_FETCH_INSTR_0_4 0x224E
+#define A3XX_VFD_FETCH_INSTR_1_F 0x2265
+#define A3XX_VFD_DECODE_INSTR_0 0x2266
+#define A3XX_VFD_VS_THREADING_THRESHOLD 0x227E
+#define A3XX_VPC_ATTR 0x2280
+#define A3XX_VPC_VARY_CYLWRAP_ENABLE_1 0x228B
+#define A3XX_SP_SP_CTRL_REG 0x22C0
+#define A3XX_SP_VS_CTRL_REG0 0x22C4
+#define A3XX_SP_VS_CTRL_REG1 0x22C5
+#define A3XX_SP_VS_PARAM_REG 0x22C6
+#define A3XX_SP_VS_OUT_REG_7 0x22CE
+#define A3XX_SP_VS_VPC_DST_REG_0 0x22D0
+#define A3XX_SP_VS_OBJ_OFFSET_REG 0x22D4
+#define A3XX_SP_VS_PVT_MEM_ADDR_REG 0x22D7
+#define A3XX_SP_VS_PVT_MEM_SIZE_REG 0x22D8
+#define A3XX_SP_VS_LENGTH_REG 0x22DF
+#define A3XX_SP_FS_CTRL_REG0 0x22E0
+#define A3XX_SP_FS_CTRL_REG1 0x22E1
+#define A3XX_SP_FS_OBJ_OFFSET_REG 0x22E2
+#define A3XX_SP_FS_PVT_MEM_ADDR_REG 0x22E5
+#define A3XX_SP_FS_PVT_MEM_SIZE_REG 0x22E6
+#define A3XX_SP_FS_FLAT_SHAD_MODE_REG_0 0x22E8
+#define A3XX_SP_FS_FLAT_SHAD_MODE_REG_1 0x22E9
+#define A3XX_SP_FS_OUTPUT_REG 0x22EC
+#define A3XX_SP_FS_MRT_REG_0 0x22F0
+#define A3XX_SP_FS_IMAGE_OUTPUT_REG_0 0x22F4
+#define A3XX_SP_FS_IMAGE_OUTPUT_REG_3 0x22F7
+#define A3XX_SP_FS_LENGTH_REG 0x22FF
+#define A3XX_TPL1_TP_VS_TEX_OFFSET 0x2340
+#define A3XX_TPL1_TP_FS_TEX_OFFSET 0x2342
+#define A3XX_TPL1_TP_FS_BORDER_COLOR_BASE_ADDR 0x2343
+#define A3XX_VBIF_FIXED_SORT_EN 0x300C
+#define A3XX_VBIF_FIXED_SORT_SEL0 0x300D
+#define A3XX_VBIF_FIXED_SORT_SEL1 0x300E
+#define A3XX_VBIF_ABIT_SORT 0x301C
+#define A3XX_VBIF_ABIT_SORT_CONF 0x301D
+#define A3XX_VBIF_GATE_OFF_WRREQ_EN 0x302A
+#define A3XX_VBIF_IN_RD_LIM_CONF0 0x302C
+#define A3XX_VBIF_IN_RD_LIM_CONF1 0x302D
+#define A3XX_VBIF_IN_WR_LIM_CONF0 0x3030
+#define A3XX_VBIF_IN_WR_LIM_CONF1 0x3031
+#define A3XX_VBIF_OUT_RD_LIM_CONF0 0x3034
+#define A3XX_VBIF_OUT_WR_LIM_CONF0 0x3035
+#define A3XX_VBIF_DDR_OUT_MAX_BURST 0x3036
+#define A3XX_VBIF_ARB_CTL 0x303C
+#define A3XX_VBIF_OUT_AXI_AOOO_EN 0x305E
+#define A3XX_VBIF_OUT_AXI_AOOO 0x305F
+#define A3XX_VBIF_ERR_PENDING  0x3064
+#define A3XX_VBIF_ERR_MASK 0x3066
+#define A3XX_VBIF_ERR_CLEAR 0x3067
+#define A3XX_VBIF_ERR_INFO 0x3068
+
+/* Bit flags for RBBM_CTL */
+#define RBBM_RBBM_CTL_RESET_PWR_CTR1  (1 << 1)
+#define RBBM_RBBM_CTL_ENABLE_PWR_CTR1  (1 << 17)
+
+/* Various flags used by the context switch code */
+
+#define SP_MULTI 0
+#define SP_BUFFER_MODE 1
+#define SP_TWO_VTX_QUADS 0
+#define SP_PIXEL_BASED 0
+#define SP_R8G8B8A8_UNORM 8
+#define SP_FOUR_PIX_QUADS 1
+
+#define HLSQ_DIRECT 0
+#define HLSQ_BLOCK_ID_SP_VS 4
+#define HLSQ_SP_VS_INSTR 0
+#define HLSQ_SP_FS_INSTR 0
+#define HLSQ_BLOCK_ID_SP_FS 6
+#define HLSQ_TWO_PIX_QUADS 0
+#define HLSQ_TWO_VTX_QUADS 0
+#define HLSQ_BLOCK_ID_TP_TEX 2
+#define HLSQ_TP_TEX_SAMPLERS 0
+#define HLSQ_TP_TEX_MEMOBJ 1
+#define HLSQ_BLOCK_ID_TP_MIPMAP 3
+#define HLSQ_TP_MIPMAP_BASE 1
+#define HLSQ_FOUR_PIX_QUADS 1
+
+#define RB_FACTOR_ONE 1
+#define RB_BLEND_OP_ADD 0
+#define RB_FACTOR_ZERO 0
+#define RB_DITHER_DISABLE 0
+#define RB_DITHER_ALWAYS 1
+#define RB_FRAG_NEVER 0
+#define RB_ENDIAN_NONE 0
+#define RB_R8G8B8A8_UNORM 8
+#define RB_RESOLVE_PASS 2
+#define RB_CLEAR_MODE_RESOLVE 1
+#define RB_TILINGMODE_LINEAR 0
+#define RB_REF_NEVER 0
+#define RB_STENCIL_KEEP 0
+#define RB_RENDERING_PASS 0
+#define RB_TILINGMODE_32X32 2
+
+#define PC_DRAW_TRIANGLES 2
+#define PC_DI_PT_RECTLIST 8
+#define PC_DI_SRC_SEL_AUTO_INDEX 2
+#define PC_DI_INDEX_SIZE_16_BIT 0
+#define PC_DI_IGNORE_VISIBILITY 0
+#define PC_DI_PT_TRILIST 4
+#define PC_DI_SRC_SEL_IMMEDIATE 1
+#define PC_DI_INDEX_SIZE_32_BIT 1
+
+#define UCHE_ENTIRE_CACHE 1
+#define UCHE_OP_INVALIDATE 1
+
+/*
+ * The following are bit field shifts within some of the registers defined
+ * above. These are used in the context switch code in conjunction with the
+ * _SET macro
+ */
+
+#define GRAS_CL_CLIP_CNTL_CLIP_DISABLE 16
+#define GRAS_CL_CLIP_CNTL_IJ_PERSP_CENTER 12
+#define GRAS_CL_CLIP_CNTL_PERSP_DIVISION_DISABLE 21
+#define GRAS_CL_CLIP_CNTL_VP_CLIP_CODE_IGNORE 19
+#define GRAS_CL_CLIP_CNTL_VP_XFORM_DISABLE 20
+#define GRAS_CL_CLIP_CNTL_ZFAR_CLIP_DISABLE 17
+#define GRAS_CL_VPORT_XSCALE_VPORT_XSCALE 0
+#define GRAS_CL_VPORT_YSCALE_VPORT_YSCALE 0
+#define GRAS_CL_VPORT_ZSCALE_VPORT_ZSCALE 0
+#define GRAS_SC_CONTROL_RASTER_MODE 12
+#define GRAS_SC_CONTROL_RENDER_MODE 4
+#define GRAS_SC_SCREEN_SCISSOR_BR_BR_X 0
+#define GRAS_SC_SCREEN_SCISSOR_BR_BR_Y 16
+#define GRAS_SC_WINDOW_SCISSOR_BR_BR_X 0
+#define GRAS_SC_WINDOW_SCISSOR_BR_BR_Y 16
+#define HLSQ_CONSTFSPRESERVEDRANGEREG_ENDENTRY 16
+#define HLSQ_CONSTFSPRESERVEDRANGEREG_STARTENTRY 0
+#define HLSQ_CTRL0REG_CHUNKDISABLE 26
+#define HLSQ_CTRL0REG_CONSTSWITCHMODE 27
+#define HLSQ_CTRL0REG_FSSUPERTHREADENABLE 6
+#define HLSQ_CTRL0REG_FSTHREADSIZE 4
+#define HLSQ_CTRL0REG_LAZYUPDATEDISABLE 28
+#define HLSQ_CTRL0REG_RESERVED2 10
+#define HLSQ_CTRL0REG_SPCONSTFULLUPDATE 29
+#define HLSQ_CTRL0REG_SPSHADERRESTART 9
+#define HLSQ_CTRL0REG_TPFULLUPDATE 30
+#define HLSQ_CTRL1REG_RESERVED1 9
+#define HLSQ_CTRL1REG_VSSUPERTHREADENABLE 8
+#define HLSQ_CTRL1REG_VSTHREADSIZE 6
+#define HLSQ_CTRL2REG_PRIMALLOCTHRESHOLD 26
+#define HLSQ_FSCTRLREG_FSCONSTLENGTH 0
+#define HLSQ_FSCTRLREG_FSCONSTSTARTOFFSET 12
+#define HLSQ_FSCTRLREG_FSINSTRLENGTH 24
+#define HLSQ_VSCTRLREG_VSINSTRLENGTH 24
+#define PC_PRIM_VTX_CONTROL_POLYMODE_BACK_PTYPE 8
+#define PC_PRIM_VTX_CONTROL_POLYMODE_FRONT_PTYPE 5
+#define PC_PRIM_VTX_CONTROL_PROVOKING_VTX_LAST 25
+#define PC_PRIM_VTX_CONTROL_STRIDE_IN_VPC 0
+#define PC_DRAW_INITIATOR_PRIM_TYPE 0
+#define PC_DRAW_INITIATOR_SOURCE_SELECT 6
+#define PC_DRAW_INITIATOR_VISIBILITY_CULLING_MODE 9
+#define PC_DRAW_INITIATOR_INDEX_SIZE 0x0B
+#define PC_DRAW_INITIATOR_SMALL_INDEX 0x0D
+#define PC_DRAW_INITIATOR_PRE_DRAW_INITIATOR_ENABLE 0x0E
+#define RB_COPYCONTROL_COPY_GMEM_BASE 14
+#define RB_COPYCONTROL_RESOLVE_CLEAR_MODE 4
+#define RB_COPYDESTBASE_COPY_DEST_BASE 4
+#define RB_COPYDESTINFO_COPY_COMPONENT_ENABLE 14
+#define RB_COPYDESTINFO_COPY_DEST_ENDIAN 18
+#define RB_COPYDESTINFO_COPY_DEST_FORMAT 2
+#define RB_COPYDESTINFO_COPY_DEST_TILE 0
+#define RB_COPYDESTPITCH_COPY_DEST_PITCH 0
+#define RB_DEPTHCONTROL_Z_TEST_FUNC 4
+#define RB_MODECONTROL_RENDER_MODE 8
+#define RB_MODECONTROL_MARB_CACHE_SPLIT_MODE 15
+#define RB_MODECONTROL_PACKER_TIMER_ENABLE 16
+#define RB_MRTBLENDCONTROL_ALPHA_BLEND_OPCODE 21
+#define RB_MRTBLENDCONTROL_ALPHA_DEST_FACTOR 24
+#define RB_MRTBLENDCONTROL_ALPHA_SRC_FACTOR 16
+#define RB_MRTBLENDCONTROL_CLAMP_ENABLE 29
+#define RB_MRTBLENDCONTROL_RGB_BLEND_OPCODE 5
+#define RB_MRTBLENDCONTROL_RGB_DEST_FACTOR 8
+#define RB_MRTBLENDCONTROL_RGB_SRC_FACTOR 0
+#define RB_MRTBUFBASE_COLOR_BUF_BASE 4
+#define RB_MRTBUFINFO_COLOR_BUF_PITCH 17
+#define RB_MRTBUFINFO_COLOR_FORMAT 0
+#define RB_MRTBUFINFO_COLOR_TILE_MODE 6
+#define RB_MRTCONTROL_COMPONENT_ENABLE 24
+#define RB_MRTCONTROL_DITHER_MODE 12
+#define RB_MRTCONTROL_READ_DEST_ENABLE 3
+#define RB_MRTCONTROL_ROP_CODE 8
+#define RB_MSAACONTROL_MSAA_DISABLE 10
+#define RB_MSAACONTROL_SAMPLE_MASK 16
+#define RB_RENDERCONTROL_ALPHA_TEST_FUNC 24
+#define RB_RENDERCONTROL_BIN_WIDTH 4
+#define RB_RENDERCONTROL_DISABLE_COLOR_PIPE 12
+#define RB_STENCILCONTROL_STENCIL_FAIL 11
+#define RB_STENCILCONTROL_STENCIL_FAIL_BF 23
+#define RB_STENCILCONTROL_STENCIL_FUNC 8
+#define RB_STENCILCONTROL_STENCIL_FUNC_BF 20
+#define RB_STENCILCONTROL_STENCIL_ZFAIL 17
+#define RB_STENCILCONTROL_STENCIL_ZFAIL_BF 29
+#define RB_STENCILCONTROL_STENCIL_ZPASS 14
+#define RB_STENCILCONTROL_STENCIL_ZPASS_BF 26
+#define SP_FSCTRLREG0_FSFULLREGFOOTPRINT 10
+#define SP_FSCTRLREG0_FSICACHEINVALID 2
+#define SP_FSCTRLREG0_FSINOUTREGOVERLAP 18
+#define SP_FSCTRLREG0_FSINSTRBUFFERMODE 1
+#define SP_FSCTRLREG0_FSLENGTH 24
+#define SP_FSCTRLREG0_FSSUPERTHREADMODE 21
+#define SP_FSCTRLREG0_FSTHREADMODE 0
+#define SP_FSCTRLREG0_FSTHREADSIZE 20
+#define SP_FSCTRLREG0_PIXLODENABLE 22
+#define SP_FSCTRLREG1_FSCONSTLENGTH 0
+#define SP_FSCTRLREG1_FSINITIALOUTSTANDING 20
+#define SP_FSCTRLREG1_HALFPRECVAROFFSET 24
+#define SP_FSMRTREG_REGID 0
+#define SP_FSOUTREG_PAD0 2
+#define SP_IMAGEOUTPUTREG_MRTFORMAT 0
+#define SP_IMAGEOUTPUTREG_PAD0 6
+#define SP_OBJOFFSETREG_CONSTOBJECTSTARTOFFSET 16
+#define SP_OBJOFFSETREG_SHADEROBJOFFSETINIC 25
+#define SP_SHADERLENGTH_LEN 0
+#define SP_SPCTRLREG_CONSTMODE 18
+#define SP_SPCTRLREG_SLEEPMODE 20
+#define SP_VSCTRLREG0_VSFULLREGFOOTPRINT 10
+#define SP_VSCTRLREG0_VSICACHEINVALID 2
+#define SP_VSCTRLREG0_VSINSTRBUFFERMODE 1
+#define SP_VSCTRLREG0_VSLENGTH 24
+#define SP_VSCTRLREG0_VSSUPERTHREADMODE 21
+#define SP_VSCTRLREG0_VSTHREADMODE 0
+#define SP_VSCTRLREG0_VSTHREADSIZE 20
+#define SP_VSCTRLREG1_VSINITIALOUTSTANDING 24
+#define SP_VSOUTREG_COMPMASK0 9
+#define SP_VSPARAMREG_POSREGID 0
+#define SP_VSPARAMREG_PSIZEREGID 8
+#define SP_VSPARAMREG_TOTALVSOUTVAR 20
+#define SP_VSVPCDSTREG_OUTLOC0 0
+#define TPL1_TPTEXOFFSETREG_BASETABLEPTR 16
+#define TPL1_TPTEXOFFSETREG_MEMOBJOFFSET 8
+#define TPL1_TPTEXOFFSETREG_SAMPLEROFFSET 0
+#define UCHE_INVALIDATE1REG_OPCODE 0x1C
+#define UCHE_INVALIDATE1REG_ALLORPORTION 0x1F
+#define VFD_BASEADDR_BASEADDR 0
+#define VFD_CTRLREG0_PACKETSIZE 18
+#define VFD_CTRLREG0_STRMDECINSTRCNT 22
+#define VFD_CTRLREG0_STRMFETCHINSTRCNT 27
+#define VFD_CTRLREG0_TOTALATTRTOVS 0
+#define VFD_CTRLREG1_MAXSTORAGE 0
+#define VFD_CTRLREG1_REGID4INST 24
+#define VFD_CTRLREG1_REGID4VTX 16
+#define VFD_DECODEINSTRUCTIONS_CONSTFILL 4
+#define VFD_DECODEINSTRUCTIONS_FORMAT 6
+#define VFD_DECODEINSTRUCTIONS_LASTCOMPVALID 29
+#define VFD_DECODEINSTRUCTIONS_REGID 12
+#define VFD_DECODEINSTRUCTIONS_SHIFTCNT 24
+#define VFD_DECODEINSTRUCTIONS_SWITCHNEXT 30
+#define VFD_DECODEINSTRUCTIONS_WRITEMASK 0
+#define VFD_FETCHINSTRUCTIONS_BUFSTRIDE 7
+#define VFD_FETCHINSTRUCTIONS_FETCHSIZE 0
+#define VFD_FETCHINSTRUCTIONS_INDEXDECODE 18
+#define VFD_FETCHINSTRUCTIONS_STEPRATE 24
+#define VFD_FETCHINSTRUCTIONS_SWITCHNEXT 17
+#define VFD_THREADINGTHRESHOLD_REGID_VTXCNT 8
+#define VFD_THREADINGTHRESHOLD_RESERVED6 4
+#define VPC_VPCATTR_LMSIZE 28
+#define VPC_VPCATTR_THRHDASSIGN 12
+#define VPC_VPCATTR_TOTALATTR 0
+#define VPC_VPCPACK_NUMFPNONPOSVAR 8
+#define VPC_VPCPACK_NUMNONPOSVSVAR 16
+#define VPC_VPCVARPSREPLMODE_COMPONENT08 0
+#define VPC_VPCVARPSREPLMODE_COMPONENT09 2
+#define VPC_VPCVARPSREPLMODE_COMPONENT0A 4
+#define VPC_VPCVARPSREPLMODE_COMPONENT0B 6
+#define VPC_VPCVARPSREPLMODE_COMPONENT0C 8
+#define VPC_VPCVARPSREPLMODE_COMPONENT0D 10
+#define VPC_VPCVARPSREPLMODE_COMPONENT0E 12
+#define VPC_VPCVARPSREPLMODE_COMPONENT0F 14
+#define VPC_VPCVARPSREPLMODE_COMPONENT10 16
+#define VPC_VPCVARPSREPLMODE_COMPONENT11 18
+#define VPC_VPCVARPSREPLMODE_COMPONENT12 20
+#define VPC_VPCVARPSREPLMODE_COMPONENT13 22
+#define VPC_VPCVARPSREPLMODE_COMPONENT14 24
+#define VPC_VPCVARPSREPLMODE_COMPONENT15 26
+#define VPC_VPCVARPSREPLMODE_COMPONENT16 28
+#define VPC_VPCVARPSREPLMODE_COMPONENT17 30
+
+/* RBBM Debug bus block IDs */
+#define RBBM_BLOCK_ID_NONE             0x0
+#define RBBM_BLOCK_ID_CP               0x1
+#define RBBM_BLOCK_ID_RBBM             0x2
+#define RBBM_BLOCK_ID_VBIF             0x3
+#define RBBM_BLOCK_ID_HLSQ             0x4
+#define RBBM_BLOCK_ID_UCHE             0x5
+#define RBBM_BLOCK_ID_PC               0x8
+#define RBBM_BLOCK_ID_VFD              0x9
+#define RBBM_BLOCK_ID_VPC              0xa
+#define RBBM_BLOCK_ID_TSE              0xb
+#define RBBM_BLOCK_ID_RAS              0xc
+#define RBBM_BLOCK_ID_VSC              0xd
+#define RBBM_BLOCK_ID_SP_0             0x10
+#define RBBM_BLOCK_ID_SP_1             0x11
+#define RBBM_BLOCK_ID_SP_2             0x12
+#define RBBM_BLOCK_ID_SP_3             0x13
+#define RBBM_BLOCK_ID_TPL1_0           0x18
+#define RBBM_BLOCK_ID_TPL1_1           0x19
+#define RBBM_BLOCK_ID_TPL1_2           0x1a
+#define RBBM_BLOCK_ID_TPL1_3           0x1b
+#define RBBM_BLOCK_ID_RB_0             0x20
+#define RBBM_BLOCK_ID_RB_1             0x21
+#define RBBM_BLOCK_ID_RB_2             0x22
+#define RBBM_BLOCK_ID_RB_3             0x23
+#define RBBM_BLOCK_ID_MARB_0           0x28
+#define RBBM_BLOCK_ID_MARB_1           0x29
+#define RBBM_BLOCK_ID_MARB_2           0x2a
+#define RBBM_BLOCK_ID_MARB_3           0x2b
+
+#endif
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
new file mode 100644
index 0000000..66baee1
--- /dev/null
+++ b/drivers/gpu/msm/adreno.c
@@ -0,0 +1,1658 @@
+/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/vmalloc.h>
+#include <linux/ioctl.h>
+#include <linux/sched.h>
+
+#include <mach/socinfo.h>
+
+#include "kgsl.h"
+#include "kgsl_pwrscale.h"
+#include "kgsl_cffdump.h"
+#include "kgsl_sharedmem.h"
+#include "kgsl_iommu.h"
+
+#include "adreno.h"
+#include "adreno_pm4types.h"
+#include "adreno_debugfs.h"
+#include "adreno_postmortem.h"
+
+#include "a2xx_reg.h"
+#include "a3xx_reg.h"
+
+#define DRIVER_VERSION_MAJOR   3
+#define DRIVER_VERSION_MINOR   1
+
+/* Adreno MH arbiter config*/
+#define ADRENO_CFG_MHARB \
+	(0x10 \
+		| (0 << MH_ARBITER_CONFIG__SAME_PAGE_GRANULARITY__SHIFT) \
+		| (1 << MH_ARBITER_CONFIG__L1_ARB_ENABLE__SHIFT) \
+		| (1 << MH_ARBITER_CONFIG__L1_ARB_HOLD_ENABLE__SHIFT) \
+		| (0 << MH_ARBITER_CONFIG__L2_ARB_CONTROL__SHIFT) \
+		| (1 << MH_ARBITER_CONFIG__PAGE_SIZE__SHIFT) \
+		| (1 << MH_ARBITER_CONFIG__TC_REORDER_ENABLE__SHIFT) \
+		| (1 << MH_ARBITER_CONFIG__TC_ARB_HOLD_ENABLE__SHIFT) \
+		| (0 << MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT_ENABLE__SHIFT) \
+		| (0x8 << MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT__SHIFT) \
+		| (1 << MH_ARBITER_CONFIG__CP_CLNT_ENABLE__SHIFT) \
+		| (1 << MH_ARBITER_CONFIG__VGT_CLNT_ENABLE__SHIFT) \
+		| (1 << MH_ARBITER_CONFIG__TC_CLNT_ENABLE__SHIFT) \
+		| (1 << MH_ARBITER_CONFIG__RB_CLNT_ENABLE__SHIFT) \
+		| (1 << MH_ARBITER_CONFIG__PA_CLNT_ENABLE__SHIFT))
+
+#define ADRENO_MMU_CONFIG						\
+	(0x01								\
+	 | (MMU_CONFIG << MH_MMU_CONFIG__RB_W_CLNT_BEHAVIOR__SHIFT)	\
+	 | (MMU_CONFIG << MH_MMU_CONFIG__CP_W_CLNT_BEHAVIOR__SHIFT)	\
+	 | (MMU_CONFIG << MH_MMU_CONFIG__CP_R0_CLNT_BEHAVIOR__SHIFT)	\
+	 | (MMU_CONFIG << MH_MMU_CONFIG__CP_R1_CLNT_BEHAVIOR__SHIFT)	\
+	 | (MMU_CONFIG << MH_MMU_CONFIG__CP_R2_CLNT_BEHAVIOR__SHIFT)	\
+	 | (MMU_CONFIG << MH_MMU_CONFIG__CP_R3_CLNT_BEHAVIOR__SHIFT)	\
+	 | (MMU_CONFIG << MH_MMU_CONFIG__CP_R4_CLNT_BEHAVIOR__SHIFT)	\
+	 | (MMU_CONFIG << MH_MMU_CONFIG__VGT_R0_CLNT_BEHAVIOR__SHIFT)	\
+	 | (MMU_CONFIG << MH_MMU_CONFIG__VGT_R1_CLNT_BEHAVIOR__SHIFT)	\
+	 | (MMU_CONFIG << MH_MMU_CONFIG__TC_R_CLNT_BEHAVIOR__SHIFT)	\
+	 | (MMU_CONFIG << MH_MMU_CONFIG__PA_W_CLNT_BEHAVIOR__SHIFT))
+
+static const struct kgsl_functable adreno_functable;
+
+static struct adreno_device device_3d0 = {
+	.dev = {
+		KGSL_DEVICE_COMMON_INIT(device_3d0.dev),
+		.name = DEVICE_3D0_NAME,
+		.id = KGSL_DEVICE_3D0,
+		.mh = {
+			.mharb  = ADRENO_CFG_MHARB,
+			/* Remove 1k boundary check in z470 to avoid a GPU
+			 * hang.  Notice that this solution won't work if
+			 * both EBI and SMI are used
+			 */
+			.mh_intf_cfg1 = 0x00032f07,
+			/* turn off memory protection unit by setting
+			   acceptable physical address range to include
+			   all pages. */
+			.mpu_base = 0x00000000,
+			.mpu_range =  0xFFFFF000,
+		},
+		.mmu = {
+			.config = ADRENO_MMU_CONFIG,
+		},
+		.pwrctrl = {
+			.irq_name = KGSL_3D0_IRQ,
+		},
+		.iomemname = KGSL_3D0_REG_MEMORY,
+		.ftbl = &adreno_functable,
+#ifdef CONFIG_HAS_EARLYSUSPEND
+		.display_off = {
+			.level = EARLY_SUSPEND_LEVEL_STOP_DRAWING,
+			.suspend = kgsl_early_suspend_driver,
+			.resume = kgsl_late_resume_driver,
+		},
+#endif
+	},
+	.gmem_base = 0,
+	.gmem_size = SZ_256K,
+	.pfp_fw = NULL,
+	.pm4_fw = NULL,
+	.wait_timeout = 10000, /* in milliseconds */
+	.ib_check_level = 0,
+};
+
+
+/*
+ * This is the master list of all GPU cores that are supported by this
+ * driver.
+ */
+
+#define ANY_ID (~0)
+
+static const struct {
+	enum adreno_gpurev gpurev;
+	unsigned int core, major, minor, patchid;
+	const char *pm4fw;
+	const char *pfpfw;
+	struct adreno_gpudev *gpudev;
+	unsigned int istore_size;
+	unsigned int pix_shader_start;
+	unsigned int instruction_size; /* Size of an instruction in dwords */
+	unsigned int gmem_size; /* size of gmem for gpu*/
+} adreno_gpulist[] = {
+	{ ADRENO_REV_A200, 0, 2, ANY_ID, ANY_ID,
+		"yamato_pm4.fw", "yamato_pfp.fw", &adreno_a2xx_gpudev,
+		512, 384, 3, SZ_256K },
+	{ ADRENO_REV_A203, 0, 1, 1, ANY_ID,
+		"yamato_pm4.fw", "yamato_pfp.fw", &adreno_a2xx_gpudev,
+		512, 384, 3, SZ_256K },
+	{ ADRENO_REV_A205, 0, 1, 0, ANY_ID,
+		"yamato_pm4.fw", "yamato_pfp.fw", &adreno_a2xx_gpudev,
+		512, 384, 3, SZ_256K },
+	{ ADRENO_REV_A220, 2, 1, ANY_ID, ANY_ID,
+		"leia_pm4_470.fw", "leia_pfp_470.fw", &adreno_a2xx_gpudev,
+		512, 384, 3, SZ_512K },
+	/*
+	 * patchlevel 5 (8960v2) needs special pm4 firmware to work around
+	 * a hardware problem.
+	 */
+	{ ADRENO_REV_A225, 2, 2, 0, 5,
+		"a225p5_pm4.fw", "a225_pfp.fw", &adreno_a2xx_gpudev,
+		1536, 768, 3, SZ_512K },
+	{ ADRENO_REV_A225, 2, 2, 0, 6,
+		"a225_pm4.fw", "a225_pfp.fw", &adreno_a2xx_gpudev,
+		1536, 768, 3, SZ_512K },
+	{ ADRENO_REV_A225, 2, 2, ANY_ID, ANY_ID,
+		"a225_pm4.fw", "a225_pfp.fw", &adreno_a2xx_gpudev,
+		1536, 768, 3, SZ_512K },
+	/* A3XX doesn't use the pix_shader_start */
+	{ ADRENO_REV_A305, 3, 0, 5, 0,
+		"a300_pm4.fw", "a300_pfp.fw", &adreno_a3xx_gpudev,
+		512, 0, 2, SZ_256K },
+	/* A3XX doesn't use the pix_shader_start */
+	{ ADRENO_REV_A320, 3, 2, 0, ANY_ID,
+		"a300_pm4.fw", "a300_pfp.fw", &adreno_a3xx_gpudev,
+		512, 0, 2, SZ_512K },
+
+};
+
+static irqreturn_t adreno_irq_handler(struct kgsl_device *device)
+{
+	irqreturn_t result;
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
+	result = adreno_dev->gpudev->irq_handler(adreno_dev);
+
+	if (device->requested_state == KGSL_STATE_NONE) {
+		if (device->pwrctrl.nap_allowed == true) {
+			kgsl_pwrctrl_request_state(device, KGSL_STATE_NAP);
+			queue_work(device->work_queue, &device->idle_check_ws);
+		} else if (device->pwrscale.policy != NULL) {
+			queue_work(device->work_queue, &device->idle_check_ws);
+		}
+	}
+
+	/* Reset the time-out in our idle timer */
+	mod_timer_pending(&device->idle_timer,
+		jiffies + device->pwrctrl.interval_timeout);
+	return result;
+}
+
+static void adreno_cleanup_pt(struct kgsl_device *device,
+			struct kgsl_pagetable *pagetable)
+{
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
+
+	kgsl_mmu_unmap(pagetable, &rb->buffer_desc);
+
+	kgsl_mmu_unmap(pagetable, &rb->memptrs_desc);
+
+	kgsl_mmu_unmap(pagetable, &device->memstore);
+
+	kgsl_mmu_unmap(pagetable, &device->mmu.setstate_memory);
+}
+
+static int adreno_setup_pt(struct kgsl_device *device,
+			struct kgsl_pagetable *pagetable)
+{
+	int result = 0;
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
+
+	result = kgsl_mmu_map_global(pagetable, &rb->buffer_desc,
+				     GSL_PT_PAGE_RV);
+	if (result)
+		goto error;
+
+	result = kgsl_mmu_map_global(pagetable, &rb->memptrs_desc,
+				     GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
+	if (result)
+		goto unmap_buffer_desc;
+
+	result = kgsl_mmu_map_global(pagetable, &device->memstore,
+				     GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
+	if (result)
+		goto unmap_memptrs_desc;
+
+	result = kgsl_mmu_map_global(pagetable, &device->mmu.setstate_memory,
+				     GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
+	if (result)
+		goto unmap_memstore_desc;
+
+	return result;
+
+unmap_memstore_desc:
+	kgsl_mmu_unmap(pagetable, &device->memstore);
+
+unmap_memptrs_desc:
+	kgsl_mmu_unmap(pagetable, &rb->memptrs_desc);
+
+unmap_buffer_desc:
+	kgsl_mmu_unmap(pagetable, &rb->buffer_desc);
+
+error:
+	return result;
+}
+
+static void adreno_iommu_setstate(struct kgsl_device *device,
+					uint32_t flags)
+{
+	unsigned int pt_val, reg_pt_val;
+	unsigned int link[200];
+	unsigned int *cmds = &link[0];
+	int sizedwords = 0;
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	struct kgsl_memdesc **reg_map_desc;
+	void *reg_map_array;
+	int num_iommu_units, i;
+
+	if (!adreno_dev->drawctxt_active)
+		return kgsl_mmu_device_setstate(&device->mmu, flags);
+	num_iommu_units = kgsl_mmu_get_reg_map_desc(&device->mmu,
+							&reg_map_array);
+	reg_map_desc = reg_map_array;
+
+	if (kgsl_mmu_enable_clk(&device->mmu,
+				KGSL_IOMMU_CONTEXT_USER))
+		goto done;
+
+	if (adreno_is_a225(adreno_dev))
+		cmds += adreno_add_change_mh_phys_limit_cmds(cmds, 0xFFFFF000,
+					device->mmu.setstate_memory.gpuaddr +
+					KGSL_IOMMU_SETSTATE_NOP_OFFSET);
+	else
+		cmds += adreno_add_bank_change_cmds(cmds,
+					KGSL_IOMMU_CONTEXT_USER,
+					device->mmu.setstate_memory.gpuaddr +
+					KGSL_IOMMU_SETSTATE_NOP_OFFSET);
+
+	if (flags & KGSL_MMUFLAGS_PTUPDATE) {
+		pt_val = kgsl_mmu_pt_get_base_addr(device->mmu.hwpagetable);
+		/*
+		 * We need to perfrom the following operations for all
+		 * IOMMU units
+		 */
+		for (i = 0; i < num_iommu_units; i++) {
+			reg_pt_val = (pt_val &
+				(KGSL_IOMMU_TTBR0_PA_MASK <<
+				KGSL_IOMMU_TTBR0_PA_SHIFT)) +
+				kgsl_mmu_get_pt_lsb(&device->mmu, i,
+					KGSL_IOMMU_CONTEXT_USER);
+			/*
+			 * Set address of the new pagetable by writng to IOMMU
+			 * TTBR0 register
+			 */
+			*cmds++ = cp_type3_packet(CP_MEM_WRITE, 2);
+			*cmds++ = reg_map_desc[i]->gpuaddr +
+				(KGSL_IOMMU_CONTEXT_USER <<
+				KGSL_IOMMU_CTX_SHIFT) + KGSL_IOMMU_TTBR0;
+			*cmds++ = reg_pt_val;
+			*cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
+			*cmds++ = 0x00000000;
+
+			/*
+			 * Read back the ttbr0 register as a barrier to ensure
+			 * above writes have completed
+			 */
+			cmds += adreno_add_read_cmds(device, cmds,
+				reg_map_desc[i]->gpuaddr +
+				(KGSL_IOMMU_CONTEXT_USER <<
+				KGSL_IOMMU_CTX_SHIFT) + KGSL_IOMMU_TTBR0,
+				reg_pt_val,
+				device->mmu.setstate_memory.gpuaddr +
+				KGSL_IOMMU_SETSTATE_NOP_OFFSET);
+
+			/* set the asid */
+			*cmds++ = cp_type3_packet(CP_MEM_WRITE, 2);
+			*cmds++ = reg_map_desc[i]->gpuaddr +
+				(KGSL_IOMMU_CONTEXT_USER <<
+				KGSL_IOMMU_CTX_SHIFT) + KGSL_IOMMU_CONTEXTIDR;
+			*cmds++ = kgsl_mmu_get_hwpagetable_asid(&device->mmu);
+			*cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
+			*cmds++ = 0x00000000;
+
+			/* Read back asid to ensure above write completes */
+			cmds += adreno_add_read_cmds(device, cmds,
+				reg_map_desc[i]->gpuaddr +
+				(KGSL_IOMMU_CONTEXT_USER <<
+				KGSL_IOMMU_CTX_SHIFT) + KGSL_IOMMU_CONTEXTIDR,
+				kgsl_mmu_get_hwpagetable_asid(&device->mmu),
+				device->mmu.setstate_memory.gpuaddr +
+				KGSL_IOMMU_SETSTATE_NOP_OFFSET);
+		}
+		/* invalidate all base pointers */
+		*cmds++ = cp_type3_packet(CP_INVALIDATE_STATE, 1);
+		*cmds++ = 0x7fff;
+
+		if (flags & KGSL_MMUFLAGS_TLBFLUSH)
+			cmds += __adreno_add_idle_indirect_cmds(cmds,
+				device->mmu.setstate_memory.gpuaddr +
+				KGSL_IOMMU_SETSTATE_NOP_OFFSET);
+	}
+	if (flags & KGSL_MMUFLAGS_TLBFLUSH) {
+		/*
+		 * tlb flush based on asid, no need to flush entire tlb
+		 */
+		for (i = 0; i < num_iommu_units; i++) {
+			*cmds++ = cp_type3_packet(CP_MEM_WRITE, 2);
+			*cmds++ = (reg_map_desc[i]->gpuaddr +
+				(KGSL_IOMMU_CONTEXT_USER <<
+				KGSL_IOMMU_CTX_SHIFT) +
+				KGSL_IOMMU_CTX_TLBIASID);
+			*cmds++ = kgsl_mmu_get_hwpagetable_asid(&device->mmu);
+			cmds += adreno_add_read_cmds(device, cmds,
+				reg_map_desc[i]->gpuaddr +
+				(KGSL_IOMMU_CONTEXT_USER <<
+				KGSL_IOMMU_CTX_SHIFT) +
+				KGSL_IOMMU_CONTEXTIDR,
+				kgsl_mmu_get_hwpagetable_asid(&device->mmu),
+				device->mmu.setstate_memory.gpuaddr +
+				KGSL_IOMMU_SETSTATE_NOP_OFFSET);
+		}
+	}
+
+	if (adreno_is_a225(adreno_dev))
+		cmds += adreno_add_change_mh_phys_limit_cmds(cmds,
+			reg_map_desc[num_iommu_units - 1]->gpuaddr - PAGE_SIZE,
+			device->mmu.setstate_memory.gpuaddr +
+			KGSL_IOMMU_SETSTATE_NOP_OFFSET);
+	else
+		cmds += adreno_add_bank_change_cmds(cmds,
+			KGSL_IOMMU_CONTEXT_PRIV,
+			device->mmu.setstate_memory.gpuaddr +
+			KGSL_IOMMU_SETSTATE_NOP_OFFSET);
+
+	sizedwords += (cmds - &link[0]);
+	if (sizedwords)
+		adreno_ringbuffer_issuecmds(device,
+			KGSL_CMD_FLAGS_PMODE, &link[0], sizedwords);
+done:
+	if (num_iommu_units)
+		kfree(reg_map_array);
+}
+
+static void adreno_gpummu_setstate(struct kgsl_device *device,
+					uint32_t flags)
+{
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	unsigned int link[32];
+	unsigned int *cmds = &link[0];
+	int sizedwords = 0;
+	unsigned int mh_mmu_invalidate = 0x00000003; /*invalidate all and tc */
+
+	/*
+	 * If possible, then set the state via the command stream to avoid
+	 * a CPU idle.  Otherwise, use the default setstate which uses register
+	 * writes For CFF dump we must idle and use the registers so that it is
+	 * easier to filter out the mmu accesses from the dump
+	 */
+	if (!kgsl_cff_dump_enable && adreno_dev->drawctxt_active) {
+		if (flags & KGSL_MMUFLAGS_PTUPDATE) {
+			/* wait for graphics pipe to be idle */
+			*cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
+			*cmds++ = 0x00000000;
+
+			/* set page table base */
+			*cmds++ = cp_type0_packet(MH_MMU_PT_BASE, 1);
+			*cmds++ = kgsl_mmu_pt_get_base_addr(
+					device->mmu.hwpagetable);
+			sizedwords += 4;
+		}
+
+		if (flags & KGSL_MMUFLAGS_TLBFLUSH) {
+			if (!(flags & KGSL_MMUFLAGS_PTUPDATE)) {
+				*cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE,
+								1);
+				*cmds++ = 0x00000000;
+				sizedwords += 2;
+			}
+			*cmds++ = cp_type0_packet(MH_MMU_INVALIDATE, 1);
+			*cmds++ = mh_mmu_invalidate;
+			sizedwords += 2;
+		}
+
+		if (flags & KGSL_MMUFLAGS_PTUPDATE &&
+			adreno_is_a20x(adreno_dev)) {
+			/* HW workaround: to resolve MMU page fault interrupts
+			* caused by the VGT.It prevents the CP PFP from filling
+			* the VGT DMA request fifo too early,thereby ensuring
+			* that the VGT will not fetch vertex/bin data until
+			* after the page table base register has been updated.
+			*
+			* Two null DRAW_INDX_BIN packets are inserted right
+			* after the page table base update, followed by a
+			* wait for idle. The null packets will fill up the
+			* VGT DMA request fifo and prevent any further
+			* vertex/bin updates from occurring until the wait
+			* has finished. */
+			*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+			*cmds++ = (0x4 << 16) |
+				(REG_PA_SU_SC_MODE_CNTL - 0x2000);
+			*cmds++ = 0;	  /* disable faceness generation */
+			*cmds++ = cp_type3_packet(CP_SET_BIN_BASE_OFFSET, 1);
+			*cmds++ = device->mmu.setstate_memory.gpuaddr;
+			*cmds++ = cp_type3_packet(CP_DRAW_INDX_BIN, 6);
+			*cmds++ = 0;	  /* viz query info */
+			*cmds++ = 0x0003C004; /* draw indicator */
+			*cmds++ = 0;	  /* bin base */
+			*cmds++ = 3;	  /* bin size */
+			*cmds++ =
+			device->mmu.setstate_memory.gpuaddr; /* dma base */
+			*cmds++ = 6;	  /* dma size */
+			*cmds++ = cp_type3_packet(CP_DRAW_INDX_BIN, 6);
+			*cmds++ = 0;	  /* viz query info */
+			*cmds++ = 0x0003C004; /* draw indicator */
+			*cmds++ = 0;	  /* bin base */
+			*cmds++ = 3;	  /* bin size */
+			/* dma base */
+			*cmds++ = device->mmu.setstate_memory.gpuaddr;
+			*cmds++ = 6;	  /* dma size */
+			*cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
+			*cmds++ = 0x00000000;
+			sizedwords += 21;
+		}
+
+
+		if (flags & (KGSL_MMUFLAGS_PTUPDATE | KGSL_MMUFLAGS_TLBFLUSH)) {
+			*cmds++ = cp_type3_packet(CP_INVALIDATE_STATE, 1);
+			*cmds++ = 0x7fff; /* invalidate all base pointers */
+			sizedwords += 2;
+		}
+
+		adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_PMODE,
+					&link[0], sizedwords);
+	} else {
+		kgsl_mmu_device_setstate(&device->mmu, flags);
+	}
+}
+
+static void adreno_setstate(struct kgsl_device *device,
+			uint32_t flags)
+{
+	/* call the mmu specific handler */
+	if (KGSL_MMU_TYPE_GPU == kgsl_mmu_get_mmutype())
+		return adreno_gpummu_setstate(device, flags);
+	else if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_get_mmutype())
+		return adreno_iommu_setstate(device, flags);
+}
+
+static unsigned int
+a3xx_getchipid(struct kgsl_device *device)
+{
+	unsigned int majorid = 0, minorid = 0, patchid = 0;
+
+	/*
+	 * We could detect the chipID from the hardware but it takes multiple
+	 * registers to find the right combination. Since we traffic exclusively
+	 * in system on chips, we can be (mostly) confident that a SOC version
+	 * will match a GPU (at this juncture at least).  So do the lazy/quick
+	 * thing and set the chip_id based on the SoC
+	 */
+
+	if (cpu_is_apq8064()) {
+		unsigned int version = socinfo_get_version();
+
+		/* A320 */
+		majorid = 2;
+		minorid = 0;
+
+		/*
+		 * V1.1 has some GPU work arounds that we need to communicate
+		 * up to user space via the patchid
+		 */
+
+		if ((SOCINFO_VERSION_MAJOR(version) == 1) &&
+			(SOCINFO_VERSION_MINOR(version) == 1))
+			patchid = 1;
+		else
+			patchid = 0;
+	} else if (cpu_is_msm8930()) {
+		/* A305 */
+		majorid = 0;
+		minorid = 5;
+		patchid = 0;
+	}
+
+	return (0x03 << 24) | (majorid << 16) | (minorid << 8) | patchid;
+}
+
+static unsigned int
+a2xx_getchipid(struct kgsl_device *device)
+{
+	unsigned int chipid = 0;
+	unsigned int coreid, majorid, minorid, patchid, revid;
+	uint32_t soc_platform_version = socinfo_get_version();
+
+	adreno_regread(device, REG_RBBM_PERIPHID1, &coreid);
+	adreno_regread(device, REG_RBBM_PERIPHID2, &majorid);
+	adreno_regread(device, REG_RBBM_PATCH_RELEASE, &revid);
+
+	/*
+	* adreno 22x gpus are indicated by coreid 2,
+	* but REG_RBBM_PERIPHID1 always contains 0 for this field
+	*/
+	if (cpu_is_msm8960() || cpu_is_msm8x60())
+		chipid = 2 << 24;
+	else
+		chipid = (coreid & 0xF) << 24;
+
+	chipid |= ((majorid >> 4) & 0xF) << 16;
+
+	minorid = ((revid >> 0)  & 0xFF);
+
+	patchid = ((revid >> 16) & 0xFF);
+
+	/* 8x50 returns 0 for patch release, but it should be 1 */
+	/* 8960v3 returns 5 for patch release, but it should be 6 */
+	/* 8x25 returns 0 for minor id, but it should be 1 */
+	if (cpu_is_qsd8x50())
+		patchid = 1;
+	else if (cpu_is_msm8960() &&
+			SOCINFO_VERSION_MAJOR(soc_platform_version) == 3)
+		patchid = 6;
+	else if (cpu_is_msm8625() && minorid == 0)
+		minorid = 1;
+
+	chipid |= (minorid << 8) | patchid;
+
+	return chipid;
+}
+
+static unsigned int
+adreno_getchipid(struct kgsl_device *device)
+{
+	if (cpu_is_apq8064() || cpu_is_msm8930())
+		return a3xx_getchipid(device);
+	else
+		return a2xx_getchipid(device);
+}
+
+static inline bool _rev_match(unsigned int id, unsigned int entry)
+{
+	return (entry == ANY_ID || entry == id);
+}
+
+static void
+adreno_identify_gpu(struct adreno_device *adreno_dev)
+{
+	unsigned int i, core, major, minor, patchid;
+
+	adreno_dev->chip_id = adreno_getchipid(&adreno_dev->dev);
+
+	core = (adreno_dev->chip_id >> 24) & 0xff;
+	major = (adreno_dev->chip_id >> 16) & 0xff;
+	minor = (adreno_dev->chip_id >> 8) & 0xff;
+	patchid = (adreno_dev->chip_id & 0xff);
+
+	for (i = 0; i < ARRAY_SIZE(adreno_gpulist); i++) {
+		if (core == adreno_gpulist[i].core &&
+		    _rev_match(major, adreno_gpulist[i].major) &&
+		    _rev_match(minor, adreno_gpulist[i].minor) &&
+		    _rev_match(patchid, adreno_gpulist[i].patchid))
+			break;
+	}
+
+	if (i == ARRAY_SIZE(adreno_gpulist)) {
+		adreno_dev->gpurev = ADRENO_REV_UNKNOWN;
+		return;
+	}
+
+	adreno_dev->gpurev = adreno_gpulist[i].gpurev;
+	adreno_dev->gpudev = adreno_gpulist[i].gpudev;
+	adreno_dev->pfp_fwfile = adreno_gpulist[i].pfpfw;
+	adreno_dev->pm4_fwfile = adreno_gpulist[i].pm4fw;
+	adreno_dev->istore_size = adreno_gpulist[i].istore_size;
+	adreno_dev->pix_shader_start = adreno_gpulist[i].pix_shader_start;
+	adreno_dev->instruction_size = adreno_gpulist[i].instruction_size;
+	adreno_dev->gmem_size = adreno_gpulist[i].gmem_size;
+}
+
+static int __devinit
+adreno_probe(struct platform_device *pdev)
+{
+	struct kgsl_device *device;
+	struct adreno_device *adreno_dev;
+	int status = -EINVAL;
+
+	device = (struct kgsl_device *)pdev->id_entry->driver_data;
+	adreno_dev = ADRENO_DEVICE(device);
+	device->parentdev = &pdev->dev;
+
+	status = adreno_ringbuffer_init(device);
+	if (status != 0)
+		goto error;
+
+	status = kgsl_device_platform_probe(device);
+	if (status)
+		goto error_close_rb;
+
+	adreno_debugfs_init(device);
+
+	kgsl_pwrscale_init(device);
+	kgsl_pwrscale_attach_policy(device, ADRENO_DEFAULT_PWRSCALE_POLICY);
+
+	device->flags &= ~KGSL_FLAGS_SOFT_RESET;
+	return 0;
+
+error_close_rb:
+	adreno_ringbuffer_close(&adreno_dev->ringbuffer);
+error:
+	device->parentdev = NULL;
+	return status;
+}
+
+static int __devexit adreno_remove(struct platform_device *pdev)
+{
+	struct kgsl_device *device;
+	struct adreno_device *adreno_dev;
+
+	device = (struct kgsl_device *)pdev->id_entry->driver_data;
+	adreno_dev = ADRENO_DEVICE(device);
+
+	kgsl_pwrscale_detach_policy(device);
+	kgsl_pwrscale_close(device);
+
+	adreno_ringbuffer_close(&adreno_dev->ringbuffer);
+	kgsl_device_platform_remove(device);
+
+	return 0;
+}
+
+static int adreno_start(struct kgsl_device *device, unsigned int init_ram)
+{
+	int status = -EINVAL;
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
+	kgsl_pwrctrl_set_state(device, KGSL_STATE_INIT);
+
+	/* Power up the device */
+	kgsl_pwrctrl_enable(device);
+
+	/* Identify the specific GPU */
+	adreno_identify_gpu(adreno_dev);
+
+	if (adreno_dev->gpurev == ADRENO_REV_UNKNOWN) {
+		KGSL_DRV_ERR(device, "Unknown chip ID %x\n",
+			adreno_dev->chip_id);
+		goto error_clk_off;
+	}
+
+	/* Set up the MMU */
+	if (adreno_is_a2xx(adreno_dev)) {
+		/*
+		 * the MH_CLNT_INTF_CTRL_CONFIG registers aren't present
+		 * on older gpus
+		 */
+		if (adreno_is_a20x(adreno_dev)) {
+			device->mh.mh_intf_cfg1 = 0;
+			device->mh.mh_intf_cfg2 = 0;
+		}
+
+		kgsl_mh_start(device);
+	}
+
+	status = kgsl_mmu_start(device);
+	if (status)
+		goto error_clk_off;
+
+	/* Start the GPU */
+	adreno_dev->gpudev->start(adreno_dev);
+
+	kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_ON);
+	device->ftbl->irqctrl(device, 1);
+
+	status = adreno_ringbuffer_start(&adreno_dev->ringbuffer, init_ram);
+	if (status == 0) {
+		mod_timer(&device->idle_timer, jiffies + FIRST_TIMEOUT);
+		return 0;
+	}
+
+	kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF);
+	kgsl_mmu_stop(&device->mmu);
+error_clk_off:
+	kgsl_pwrctrl_disable(device);
+
+	return status;
+}
+
+static int adreno_stop(struct kgsl_device *device)
+{
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
+	adreno_dev->drawctxt_active = NULL;
+
+	adreno_ringbuffer_stop(&adreno_dev->ringbuffer);
+
+	kgsl_mmu_stop(&device->mmu);
+
+	device->ftbl->irqctrl(device, 0);
+	kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF);
+	del_timer_sync(&device->idle_timer);
+
+	/* Power down the device */
+	kgsl_pwrctrl_disable(device);
+
+	return 0;
+}
+
+static int
+adreno_recover_hang(struct kgsl_device *device)
+{
+	int ret;
+	unsigned int *rb_buffer;
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
+	unsigned int timestamp;
+	unsigned int num_rb_contents;
+	unsigned int reftimestamp;
+	unsigned int enable_ts;
+	unsigned int soptimestamp;
+	unsigned int eoptimestamp;
+	unsigned int context_id;
+	struct kgsl_context *context;
+	struct adreno_context *adreno_context;
+	int next = 0;
+
+	KGSL_DRV_ERR(device, "Starting recovery from 3D GPU hang....\n");
+	rb_buffer = vmalloc(rb->buffer_desc.size);
+	if (!rb_buffer) {
+		KGSL_MEM_ERR(device,
+			"Failed to allocate memory for recovery: %x\n",
+			rb->buffer_desc.size);
+		return -ENOMEM;
+	}
+	/* Extract valid contents from rb which can stil be executed after
+	 * hang */
+	ret = adreno_ringbuffer_extract(rb, rb_buffer, &num_rb_contents);
+	if (ret)
+		goto done;
+	kgsl_sharedmem_readl(&device->memstore, &context_id,
+				KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
+					current_context));
+	context = idr_find(&device->context_idr, context_id);
+	if (context == NULL) {
+		KGSL_DRV_ERR(device, "Last context unknown id:%d\n",
+				context_id);
+		context_id = KGSL_MEMSTORE_GLOBAL;
+	}
+
+	timestamp = rb->timestamp[KGSL_MEMSTORE_GLOBAL];
+	KGSL_DRV_ERR(device, "Last issued global timestamp: %x\n", timestamp);
+
+	kgsl_sharedmem_readl(&device->memstore, &reftimestamp,
+				KGSL_MEMSTORE_OFFSET(context_id,
+					ref_wait_ts));
+	kgsl_sharedmem_readl(&device->memstore, &enable_ts,
+				KGSL_MEMSTORE_OFFSET(context_id,
+					ts_cmp_enable));
+	kgsl_sharedmem_readl(&device->memstore, &soptimestamp,
+				KGSL_MEMSTORE_OFFSET(context_id,
+					soptimestamp));
+	kgsl_sharedmem_readl(&device->memstore, &eoptimestamp,
+				KGSL_MEMSTORE_OFFSET(context_id,
+					eoptimestamp));
+	/* Make sure memory is synchronized before restarting the GPU */
+	mb();
+	KGSL_CTXT_ERR(device,
+		"Context id that caused a GPU hang: %d\n", context_id);
+	/* restart device */
+	ret = adreno_stop(device);
+	if (ret)
+		goto done;
+	ret = adreno_start(device, true);
+	if (ret)
+		goto done;
+	KGSL_DRV_ERR(device, "Device has been restarted after hang\n");
+	/* Restore timestamp states */
+	kgsl_sharedmem_writel(&device->memstore,
+			KGSL_MEMSTORE_OFFSET(context_id, soptimestamp),
+			soptimestamp);
+	kgsl_sharedmem_writel(&device->memstore,
+			KGSL_MEMSTORE_OFFSET(context_id, eoptimestamp),
+			eoptimestamp);
+
+	if (num_rb_contents) {
+		kgsl_sharedmem_writel(&device->memstore,
+			KGSL_MEMSTORE_OFFSET(context_id, ref_wait_ts),
+			reftimestamp);
+		kgsl_sharedmem_writel(&device->memstore,
+			KGSL_MEMSTORE_OFFSET(context_id, ts_cmp_enable),
+			enable_ts);
+	}
+	/* Make sure all writes are posted before the GPU reads them */
+	wmb();
+	/* Mark the invalid context so no more commands are accepted from
+	 * that context */
+
+	adreno_context = context->devctxt;
+
+	KGSL_CTXT_ERR(device,
+		"Context that caused a GPU hang: %d\n", adreno_context->id);
+
+	adreno_context->flags |= CTXT_FLAGS_GPU_HANG;
+
+	/*
+	 * Set the reset status of all contexts to
+	 * INNOCENT_CONTEXT_RESET_EXT except for the bad context
+	 * since thats the guilty party
+	 */
+	while ((context = idr_get_next(&device->context_idr, &next))) {
+		if (KGSL_CTX_STAT_GUILTY_CONTEXT_RESET_EXT !=
+			context->reset_status) {
+			if (context->id != context_id)
+				context->reset_status =
+				KGSL_CTX_STAT_INNOCENT_CONTEXT_RESET_EXT;
+			else
+				context->reset_status =
+				KGSL_CTX_STAT_GUILTY_CONTEXT_RESET_EXT;
+		}
+		next = next + 1;
+	}
+
+	/* Restore valid commands in ringbuffer */
+	adreno_ringbuffer_restore(rb, rb_buffer, num_rb_contents);
+	rb->timestamp[KGSL_MEMSTORE_GLOBAL] = timestamp;
+done:
+	vfree(rb_buffer);
+	return ret;
+}
+
+static int
+adreno_dump_and_recover(struct kgsl_device *device)
+{
+	int result = -ETIMEDOUT;
+
+	if (device->state == KGSL_STATE_HUNG)
+		goto done;
+	if (device->state == KGSL_STATE_DUMP_AND_RECOVER) {
+		mutex_unlock(&device->mutex);
+		wait_for_completion(&device->recovery_gate);
+		mutex_lock(&device->mutex);
+		if (device->state != KGSL_STATE_HUNG)
+			result = 0;
+	} else {
+		kgsl_pwrctrl_set_state(device, KGSL_STATE_DUMP_AND_RECOVER);
+		INIT_COMPLETION(device->recovery_gate);
+		/* Detected a hang */
+
+
+		/*
+		 * Trigger an automatic dump of the state to
+		 * the console
+		 */
+		adreno_postmortem_dump(device, 0);
+
+		/*
+		 * Make a GPU snapshot.  For now, do it after the PM dump so we
+		 * can at least be sure the PM dump will work as it always has
+		 */
+		kgsl_device_snapshot(device, 1);
+
+		result = adreno_recover_hang(device);
+		if (result)
+			kgsl_pwrctrl_set_state(device, KGSL_STATE_HUNG);
+		else
+			kgsl_pwrctrl_set_state(device, KGSL_STATE_ACTIVE);
+		complete_all(&device->recovery_gate);
+	}
+done:
+	return result;
+}
+
+static int adreno_getproperty(struct kgsl_device *device,
+				enum kgsl_property_type type,
+				void *value,
+				unsigned int sizebytes)
+{
+	int status = -EINVAL;
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
+	switch (type) {
+	case KGSL_PROP_DEVICE_INFO:
+		{
+			struct kgsl_devinfo devinfo;
+
+			if (sizebytes != sizeof(devinfo)) {
+				status = -EINVAL;
+				break;
+			}
+
+			memset(&devinfo, 0, sizeof(devinfo));
+			devinfo.device_id = device->id+1;
+			devinfo.chip_id = adreno_dev->chip_id;
+			devinfo.mmu_enabled = kgsl_mmu_enabled();
+			devinfo.gpu_id = adreno_dev->gpurev;
+			devinfo.gmem_gpubaseaddr = adreno_dev->gmem_base;
+			devinfo.gmem_sizebytes = adreno_dev->gmem_size;
+
+			if (copy_to_user(value, &devinfo, sizeof(devinfo)) !=
+					0) {
+				status = -EFAULT;
+				break;
+			}
+			status = 0;
+		}
+		break;
+	case KGSL_PROP_DEVICE_SHADOW:
+		{
+			struct kgsl_shadowprop shadowprop;
+
+			if (sizebytes != sizeof(shadowprop)) {
+				status = -EINVAL;
+				break;
+			}
+			memset(&shadowprop, 0, sizeof(shadowprop));
+			if (device->memstore.hostptr) {
+				/*NOTE: with mmu enabled, gpuaddr doesn't mean
+				 * anything to mmap().
+				 */
+				shadowprop.gpuaddr = device->memstore.physaddr;
+				shadowprop.size = device->memstore.size;
+				/* GSL needs this to be set, even if it
+				   appears to be meaningless */
+				shadowprop.flags = KGSL_FLAGS_INITIALIZED |
+					KGSL_FLAGS_PER_CONTEXT_TIMESTAMPS;
+			}
+			if (copy_to_user(value, &shadowprop,
+				sizeof(shadowprop))) {
+				status = -EFAULT;
+				break;
+			}
+			status = 0;
+		}
+		break;
+	case KGSL_PROP_MMU_ENABLE:
+		{
+			int mmu_prop = kgsl_mmu_enabled();
+
+			if (sizebytes != sizeof(int)) {
+				status = -EINVAL;
+				break;
+			}
+			if (copy_to_user(value, &mmu_prop, sizeof(mmu_prop))) {
+				status = -EFAULT;
+				break;
+			}
+			status = 0;
+		}
+		break;
+	case KGSL_PROP_INTERRUPT_WAITS:
+		{
+			int int_waits = 1;
+			if (sizebytes != sizeof(int)) {
+				status = -EINVAL;
+				break;
+			}
+			if (copy_to_user(value, &int_waits, sizeof(int))) {
+				status = -EFAULT;
+				break;
+			}
+			status = 0;
+		}
+		break;
+	default:
+		status = -EINVAL;
+	}
+
+	return status;
+}
+
+static int adreno_setproperty(struct kgsl_device *device,
+				enum kgsl_property_type type,
+				void *value,
+				unsigned int sizebytes)
+{
+	int status = -EINVAL;
+
+	switch (type) {
+	case KGSL_PROP_PWRCTRL: {
+			unsigned int enable;
+			struct kgsl_device_platform_data *pdata =
+				kgsl_device_get_drvdata(device);
+
+			if (sizebytes != sizeof(enable))
+				break;
+
+			if (copy_from_user(&enable, (void __user *) value,
+				sizeof(enable))) {
+				status = -EFAULT;
+				break;
+			}
+
+			if (enable) {
+				if (pdata->nap_allowed)
+					device->pwrctrl.nap_allowed = true;
+
+				kgsl_pwrscale_enable(device);
+			} else {
+				device->pwrctrl.nap_allowed = false;
+				kgsl_pwrscale_disable(device);
+			}
+
+			status = 0;
+		}
+		break;
+	default:
+		break;
+	}
+
+	return status;
+}
+
+static inline void adreno_poke(struct kgsl_device *device)
+{
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	adreno_regwrite(device, REG_CP_RB_WPTR, adreno_dev->ringbuffer.wptr);
+}
+
+/* Caller must hold the device mutex. */
+int adreno_idle(struct kgsl_device *device, unsigned int timeout)
+{
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
+	unsigned int rbbm_status;
+	unsigned long wait_timeout =
+		msecs_to_jiffies(adreno_dev->wait_timeout);
+	unsigned long wait_time;
+	unsigned long wait_time_part;
+	unsigned int msecs;
+	unsigned int msecs_first;
+	unsigned int msecs_part;
+
+	kgsl_cffdump_regpoll(device->id,
+		adreno_dev->gpudev->reg_rbbm_status << 2,
+		0x00000000, 0x80000000);
+	/* first, wait until the CP has consumed all the commands in
+	 * the ring buffer
+	 */
+retry:
+	if (rb->flags & KGSL_FLAGS_STARTED) {
+		msecs = adreno_dev->wait_timeout;
+		msecs_first = (msecs <= 100) ? ((msecs + 4) / 5) : 100;
+		msecs_part = (msecs - msecs_first + 3) / 4;
+		wait_time = jiffies + wait_timeout;
+		wait_time_part = jiffies + msecs_to_jiffies(msecs_first);
+		adreno_poke(device);
+		do {
+			if (time_after(jiffies, wait_time_part)) {
+				adreno_poke(device);
+				wait_time_part = jiffies +
+					msecs_to_jiffies(msecs_part);
+			}
+			GSL_RB_GET_READPTR(rb, &rb->rptr);
+			if (time_after(jiffies, wait_time)) {
+				KGSL_DRV_ERR(device, "rptr: %x, wptr: %x\n",
+					rb->rptr, rb->wptr);
+				goto err;
+			}
+		} while (rb->rptr != rb->wptr);
+	}
+
+	/* now, wait for the GPU to finish its operations */
+	wait_time = jiffies + wait_timeout;
+	while (time_before(jiffies, wait_time)) {
+		adreno_regread(device, adreno_dev->gpudev->reg_rbbm_status,
+			&rbbm_status);
+		if (adreno_is_a2xx(adreno_dev)) {
+			if (rbbm_status == 0x110)
+				return 0;
+		} else {
+			if (!(rbbm_status & 0x80000000))
+				return 0;
+		}
+	}
+
+err:
+	KGSL_DRV_ERR(device, "spun too long waiting for RB to idle\n");
+	if (!adreno_dump_and_recover(device)) {
+		wait_time = jiffies + wait_timeout;
+		goto retry;
+	}
+	return -ETIMEDOUT;
+}
+
+static unsigned int adreno_isidle(struct kgsl_device *device)
+{
+	int status = false;
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
+	unsigned int rbbm_status;
+
+	WARN_ON(device->state == KGSL_STATE_INIT);
+	/* If the device isn't active, don't force it on. */
+	if (device->state == KGSL_STATE_ACTIVE) {
+		/* Is the ring buffer is empty? */
+		GSL_RB_GET_READPTR(rb, &rb->rptr);
+		if (!device->active_cnt && (rb->rptr == rb->wptr)) {
+			/* Is the core idle? */
+			adreno_regread(device,
+				adreno_dev->gpudev->reg_rbbm_status,
+				&rbbm_status);
+
+			if (adreno_is_a2xx(adreno_dev)) {
+				if (rbbm_status == 0x110)
+					status = true;
+			} else {
+				if (!(rbbm_status & 0x80000000))
+					status = true;
+			}
+		}
+	} else {
+		status = true;
+	}
+	return status;
+}
+
+/* Caller must hold the device mutex. */
+static int adreno_suspend_context(struct kgsl_device *device)
+{
+	int status = 0;
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
+	/* switch to NULL ctxt */
+	if (adreno_dev->drawctxt_active != NULL) {
+		adreno_drawctxt_switch(adreno_dev, NULL, 0);
+		status = adreno_idle(device, KGSL_TIMEOUT_DEFAULT);
+	}
+
+	return status;
+}
+
+/* Find a memory structure attached to an adreno context */
+
+struct kgsl_memdesc *adreno_find_ctxtmem(struct kgsl_device *device,
+	unsigned int pt_base, unsigned int gpuaddr, unsigned int size)
+{
+	struct kgsl_context *context;
+	struct adreno_context *adreno_context = NULL;
+	int next = 0;
+
+	while (1) {
+		context = idr_get_next(&device->context_idr, &next);
+		if (context == NULL)
+			break;
+
+		adreno_context = (struct adreno_context *)context->devctxt;
+
+		if (kgsl_mmu_pt_equal(adreno_context->pagetable, pt_base)) {
+			struct kgsl_memdesc *desc;
+
+			desc = &adreno_context->gpustate;
+			if (kgsl_gpuaddr_in_memdesc(desc, gpuaddr, size))
+				return desc;
+
+			desc = &adreno_context->context_gmem_shadow.gmemshadow;
+			if (kgsl_gpuaddr_in_memdesc(desc, gpuaddr, size))
+				return desc;
+		}
+		next = next + 1;
+	}
+
+	return NULL;
+}
+
+struct kgsl_memdesc *adreno_find_region(struct kgsl_device *device,
+						unsigned int pt_base,
+						unsigned int gpuaddr,
+						unsigned int size)
+{
+	struct kgsl_mem_entry *entry;
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	struct adreno_ringbuffer *ringbuffer = &adreno_dev->ringbuffer;
+
+	if (kgsl_gpuaddr_in_memdesc(&ringbuffer->buffer_desc, gpuaddr, size))
+		return &ringbuffer->buffer_desc;
+
+	if (kgsl_gpuaddr_in_memdesc(&ringbuffer->memptrs_desc, gpuaddr, size))
+		return &ringbuffer->memptrs_desc;
+
+	if (kgsl_gpuaddr_in_memdesc(&device->memstore, gpuaddr, size))
+		return &device->memstore;
+
+	if (kgsl_gpuaddr_in_memdesc(&device->mmu.setstate_memory, gpuaddr,
+					size))
+		return &device->mmu.setstate_memory;
+
+	entry = kgsl_get_mem_entry(pt_base, gpuaddr, size);
+
+	if (entry)
+		return &entry->memdesc;
+
+	return adreno_find_ctxtmem(device, pt_base, gpuaddr, size);
+}
+
+uint8_t *adreno_convertaddr(struct kgsl_device *device, unsigned int pt_base,
+			    unsigned int gpuaddr, unsigned int size)
+{
+	struct kgsl_memdesc *memdesc;
+
+	memdesc = adreno_find_region(device, pt_base, gpuaddr, size);
+
+	return memdesc ? kgsl_gpuaddr_to_vaddr(memdesc, gpuaddr) : NULL;
+}
+
+void adreno_regread(struct kgsl_device *device, unsigned int offsetwords,
+				unsigned int *value)
+{
+	unsigned int *reg;
+	BUG_ON(offsetwords*sizeof(uint32_t) >= device->reg_len);
+	reg = (unsigned int *)(device->reg_virt + (offsetwords << 2));
+
+	if (!in_interrupt())
+		kgsl_pre_hwaccess(device);
+
+	/*ensure this read finishes before the next one.
+	 * i.e. act like normal readl() */
+	*value = __raw_readl(reg);
+	rmb();
+}
+
+void adreno_regwrite(struct kgsl_device *device, unsigned int offsetwords,
+				unsigned int value)
+{
+	unsigned int *reg;
+
+	BUG_ON(offsetwords*sizeof(uint32_t) >= device->reg_len);
+
+	if (!in_interrupt())
+		kgsl_pre_hwaccess(device);
+
+	kgsl_cffdump_regwrite(device->id, offsetwords << 2, value);
+	reg = (unsigned int *)(device->reg_virt + (offsetwords << 2));
+
+	/*ensure previous writes post before this one,
+	 * i.e. act like normal writel() */
+	wmb();
+	__raw_writel(value, reg);
+}
+
+static unsigned int _get_context_id(struct kgsl_context *k_ctxt)
+{
+	unsigned int context_id = KGSL_MEMSTORE_GLOBAL;
+	if (k_ctxt != NULL) {
+		struct adreno_context *a_ctxt = k_ctxt->devctxt;
+		if (k_ctxt->id == KGSL_CONTEXT_INVALID || a_ctxt == NULL)
+			context_id = KGSL_CONTEXT_INVALID;
+		else if (a_ctxt->flags & CTXT_FLAGS_PER_CONTEXT_TS)
+			context_id = k_ctxt->id;
+	}
+
+	return context_id;
+}
+
+static int kgsl_check_interrupt_timestamp(struct kgsl_device *device,
+		struct kgsl_context *context, unsigned int timestamp)
+{
+	int status;
+	unsigned int ref_ts, enableflag;
+	unsigned int context_id;
+
+	mutex_lock(&device->mutex);
+	context_id = _get_context_id(context);
+	/*
+	 * If the context ID is invalid, we are in a race with
+	 * the context being destroyed by userspace so bail.
+	 */
+	if (context_id == KGSL_CONTEXT_INVALID) {
+		KGSL_DRV_WARN(device, "context was detached");
+		status = -EINVAL;
+		goto unlock;
+	}
+
+	status = kgsl_check_timestamp(device, context, timestamp);
+	if (!status) {
+		kgsl_sharedmem_readl(&device->memstore, &enableflag,
+			KGSL_MEMSTORE_OFFSET(context_id, ts_cmp_enable));
+		mb();
+
+		if (enableflag) {
+			kgsl_sharedmem_readl(&device->memstore, &ref_ts,
+				KGSL_MEMSTORE_OFFSET(context_id,
+					ref_wait_ts));
+			mb();
+			if (timestamp_cmp(ref_ts, timestamp) >= 0) {
+				kgsl_sharedmem_writel(&device->memstore,
+				KGSL_MEMSTORE_OFFSET(context_id,
+					ref_wait_ts), timestamp);
+				wmb();
+			}
+		} else {
+			unsigned int cmds[2];
+			kgsl_sharedmem_writel(&device->memstore,
+				KGSL_MEMSTORE_OFFSET(context_id,
+					ref_wait_ts), timestamp);
+			enableflag = 1;
+			kgsl_sharedmem_writel(&device->memstore,
+				KGSL_MEMSTORE_OFFSET(context_id,
+					ts_cmp_enable), enableflag);
+			wmb();
+			/* submit a dummy packet so that even if all
+			* commands upto timestamp get executed we will still
+			* get an interrupt */
+			cmds[0] = cp_type3_packet(CP_NOP, 1);
+			cmds[1] = 0;
+			adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE,
+				&cmds[0], 2);
+		}
+	}
+unlock:
+	mutex_unlock(&device->mutex);
+
+	return status;
+}
+
+/*
+ wait_event_interruptible_timeout checks for the exit condition before
+ placing a process in wait q. For conditional interrupts we expect the
+ process to already be in its wait q when its exit condition checking
+ function is called.
+*/
+#define kgsl_wait_event_interruptible_timeout(wq, condition, timeout, io)\
+({									\
+	long __ret = timeout;						\
+	if (io)						\
+		__wait_io_event_interruptible_timeout(wq, condition, __ret);\
+	else						\
+		__wait_event_interruptible_timeout(wq, condition, __ret);\
+	__ret;								\
+})
+
+/* MUST be called with the device mutex held */
+static int adreno_waittimestamp(struct kgsl_device *device,
+				struct kgsl_context *context,
+				unsigned int timestamp,
+				unsigned int msecs)
+{
+	long status = 0;
+	uint io = 1;
+	static uint io_cnt;
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+	int retries;
+	unsigned int msecs_first;
+	unsigned int msecs_part;
+	unsigned int ts_issued;
+	unsigned int context_id = _get_context_id(context);
+
+	ts_issued = adreno_dev->ringbuffer.timestamp[context_id];
+
+	/* Don't wait forever, set a max value for now */
+	if (msecs == -1)
+		msecs = adreno_dev->wait_timeout;
+
+	if (timestamp_cmp(timestamp, ts_issued) > 0) {
+		KGSL_DRV_ERR(device, "Cannot wait for invalid ts <%d:0x%x>, "
+			"last issued ts <%d:0x%x>\n",
+			context_id, timestamp, context_id, ts_issued);
+		status = -EINVAL;
+		goto done;
+	}
+
+	/* Keep the first timeout as 100msecs before rewriting
+	 * the WPTR. Less visible impact if the WPTR has not
+	 * been updated properly.
+	 */
+	msecs_first = (msecs <= 100) ? ((msecs + 4) / 5) : 100;
+	msecs_part = (msecs - msecs_first + 3) / 4;
+	for (retries = 0; retries < 5; retries++) {
+		/*
+		 * If the context ID is invalid, we are in a race with
+		 * the context being destroyed by userspace so bail.
+		 */
+		if (context_id == KGSL_CONTEXT_INVALID) {
+			KGSL_DRV_WARN(device, "context was detached");
+			status = -EINVAL;
+			goto done;
+		}
+		if (kgsl_check_timestamp(device, context, timestamp)) {
+			/* if the timestamp happens while we're not
+			 * waiting, there's a chance that an interrupt
+			 * will not be generated and thus the timestamp
+			 * work needs to be queued.
+			 */
+			queue_work(device->work_queue, &device->ts_expired_ws);
+			status = 0;
+			goto done;
+		}
+		adreno_poke(device);
+		io_cnt = (io_cnt + 1) % 100;
+		if (io_cnt <
+		    pwr->pwrlevels[pwr->active_pwrlevel].io_fraction)
+			io = 0;
+		mutex_unlock(&device->mutex);
+		/* We need to make sure that the process is
+		 * placed in wait-q before its condition is called
+		 */
+		status = kgsl_wait_event_interruptible_timeout(
+				device->wait_queue,
+				kgsl_check_interrupt_timestamp(device,
+					context, timestamp),
+				msecs_to_jiffies(retries ?
+					msecs_part : msecs_first), io);
+		mutex_lock(&device->mutex);
+
+		if (status > 0) {
+			/*completed before the wait finished */
+			status = 0;
+			goto done;
+		} else if (status < 0) {
+			/*an error occurred*/
+			goto done;
+		}
+		/*this wait timed out*/
+	}
+	status = -ETIMEDOUT;
+	KGSL_DRV_ERR(device,
+		     "Device hang detected while waiting for timestamp: "
+		     "<%d:0x%x>, last submitted timestamp: <%d:0x%x>, "
+		     "wptr: 0x%x\n",
+		      context_id, timestamp, context_id, ts_issued,
+		      adreno_dev->ringbuffer.wptr);
+	if (!adreno_dump_and_recover(device)) {
+		/* wait for idle after recovery as the
+		 * timestamp that this process wanted
+		 * to wait on may be invalid */
+		if (!adreno_idle(device, KGSL_TIMEOUT_DEFAULT))
+			status = 0;
+	}
+done:
+	return (int)status;
+}
+
+static unsigned int adreno_readtimestamp(struct kgsl_device *device,
+		struct kgsl_context *context, enum kgsl_timestamp_type type)
+{
+	unsigned int timestamp = 0;
+	unsigned int context_id = _get_context_id(context);
+
+	/*
+	 * If the context ID is invalid, we are in a race with
+	 * the context being destroyed by userspace so bail.
+	 */
+	if (context_id == KGSL_CONTEXT_INVALID) {
+		KGSL_DRV_WARN(device, "context was detached");
+		return timestamp;
+	}
+	switch (type) {
+	case KGSL_TIMESTAMP_QUEUED: {
+		struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+		struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
+
+		timestamp = rb->timestamp[context_id];
+		break;
+	}
+	case KGSL_TIMESTAMP_CONSUMED:
+		adreno_regread(device, REG_CP_TIMESTAMP, &timestamp);
+		break;
+	case KGSL_TIMESTAMP_RETIRED:
+		kgsl_sharedmem_readl(&device->memstore, &timestamp,
+			KGSL_MEMSTORE_OFFSET(context_id, eoptimestamp));
+		break;
+	}
+
+	rmb();
+
+	return timestamp;
+}
+
+static long adreno_ioctl(struct kgsl_device_private *dev_priv,
+			      unsigned int cmd, void *data)
+{
+	int result = 0;
+	struct kgsl_drawctxt_set_bin_base_offset *binbase;
+	struct kgsl_context *context;
+
+	switch (cmd) {
+	case IOCTL_KGSL_DRAWCTXT_SET_BIN_BASE_OFFSET:
+		binbase = data;
+
+		context = kgsl_find_context(dev_priv, binbase->drawctxt_id);
+		if (context) {
+			adreno_drawctxt_set_bin_base_offset(
+				dev_priv->device, context, binbase->offset);
+		} else {
+			result = -EINVAL;
+			KGSL_DRV_ERR(dev_priv->device,
+				"invalid drawctxt drawctxt_id %d "
+				"device_id=%d\n",
+				binbase->drawctxt_id, dev_priv->device->id);
+		}
+		break;
+
+	default:
+		KGSL_DRV_INFO(dev_priv->device,
+			"invalid ioctl code %08x\n", cmd);
+		result = -ENOIOCTLCMD;
+		break;
+	}
+	return result;
+
+}
+
+static inline s64 adreno_ticks_to_us(u32 ticks, u32 gpu_freq)
+{
+	gpu_freq /= 1000000;
+	return ticks / gpu_freq;
+}
+
+static void adreno_power_stats(struct kgsl_device *device,
+				struct kgsl_power_stats *stats)
+{
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+	unsigned int cycles;
+
+	/* Get the busy cycles counted since the counter was last reset */
+	/* Calling this function also resets and restarts the counter */
+
+	cycles = adreno_dev->gpudev->busy_cycles(adreno_dev);
+
+	/* In order to calculate idle you have to have run the algorithm *
+	 * at least once to get a start time. */
+	if (pwr->time != 0) {
+		s64 tmp = ktime_to_us(ktime_get());
+		stats->total_time = tmp - pwr->time;
+		pwr->time = tmp;
+		stats->busy_time = adreno_ticks_to_us(cycles, device->pwrctrl.
+				pwrlevels[device->pwrctrl.active_pwrlevel].
+				gpu_freq);
+	} else {
+		stats->total_time = 0;
+		stats->busy_time = 0;
+		pwr->time = ktime_to_us(ktime_get());
+	}
+}
+
+void adreno_irqctrl(struct kgsl_device *device, int state)
+{
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	adreno_dev->gpudev->irq_control(adreno_dev, state);
+}
+
+static unsigned int adreno_gpuid(struct kgsl_device *device)
+{
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
+	/* Standard KGSL gpuid format:
+	 * top word is 0x0002 for 2D or 0x0003 for 3D
+	 * Bottom word is core specific identifer
+	 */
+
+	return (0x0003 << 16) | ((int) adreno_dev->gpurev);
+}
+
+static const struct kgsl_functable adreno_functable = {
+	/* Mandatory functions */
+	.regread = adreno_regread,
+	.regwrite = adreno_regwrite,
+	.idle = adreno_idle,
+	.isidle = adreno_isidle,
+	.suspend_context = adreno_suspend_context,
+	.start = adreno_start,
+	.stop = adreno_stop,
+	.getproperty = adreno_getproperty,
+	.waittimestamp = adreno_waittimestamp,
+	.readtimestamp = adreno_readtimestamp,
+	.issueibcmds = adreno_ringbuffer_issueibcmds,
+	.ioctl = adreno_ioctl,
+	.setup_pt = adreno_setup_pt,
+	.cleanup_pt = adreno_cleanup_pt,
+	.power_stats = adreno_power_stats,
+	.irqctrl = adreno_irqctrl,
+	.gpuid = adreno_gpuid,
+	.snapshot = adreno_snapshot,
+	.irq_handler = adreno_irq_handler,
+	/* Optional functions */
+	.setstate = adreno_setstate,
+	.drawctxt_create = adreno_drawctxt_create,
+	.drawctxt_destroy = adreno_drawctxt_destroy,
+	.setproperty = adreno_setproperty,
+};
+
+static struct platform_device_id adreno_id_table[] = {
+	{ DEVICE_3D0_NAME, (kernel_ulong_t)&device_3d0.dev, },
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, adreno_id_table);
+
+static struct platform_driver adreno_platform_driver = {
+	.probe = adreno_probe,
+	.remove = __devexit_p(adreno_remove),
+	.suspend = kgsl_suspend_driver,
+	.resume = kgsl_resume_driver,
+	.id_table = adreno_id_table,
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = DEVICE_3D_NAME,
+		.pm = &kgsl_pm_ops,
+	}
+};
+
+static int __init kgsl_3d_init(void)
+{
+	return platform_driver_register(&adreno_platform_driver);
+}
+
+static void __exit kgsl_3d_exit(void)
+{
+	platform_driver_unregister(&adreno_platform_driver);
+}
+
+module_init(kgsl_3d_init);
+module_exit(kgsl_3d_exit);
+
+MODULE_DESCRIPTION("3D Graphics driver");
+MODULE_VERSION("1.2");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:kgsl_3d");
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
new file mode 100644
index 0000000..4ce56a4
--- /dev/null
+++ b/drivers/gpu/msm/adreno.h
@@ -0,0 +1,293 @@
+/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __ADRENO_H
+#define __ADRENO_H
+
+#include "kgsl_device.h"
+#include "adreno_drawctxt.h"
+#include "adreno_ringbuffer.h"
+#include "kgsl_iommu.h"
+
+#define DEVICE_3D_NAME "kgsl-3d"
+#define DEVICE_3D0_NAME "kgsl-3d0"
+
+#define ADRENO_DEVICE(device) \
+		KGSL_CONTAINER_OF(device, struct adreno_device, dev)
+
+/* Flags to control command packet settings */
+#define KGSL_CMD_FLAGS_NONE             0x00000000
+#define KGSL_CMD_FLAGS_PMODE		0x00000001
+#define KGSL_CMD_FLAGS_NO_TS_CMP	0x00000002
+#define KGSL_CMD_FLAGS_NOT_KERNEL_CMD	0x00000004
+
+/* Command identifiers */
+#define KGSL_CONTEXT_TO_MEM_IDENTIFIER	0x2EADBEEF
+#define KGSL_CMD_IDENTIFIER		0x2EEDFACE
+#define KGSL_START_OF_IB_IDENTIFIER	0x2EADEABE
+#define KGSL_END_OF_IB_IDENTIFIER	0x2ABEDEAD
+
+#ifdef CONFIG_MSM_SCM
+#define ADRENO_DEFAULT_PWRSCALE_POLICY  (&kgsl_pwrscale_policy_tz)
+#elif defined CONFIG_MSM_SLEEP_STATS_DEVICE
+#define ADRENO_DEFAULT_PWRSCALE_POLICY  (&kgsl_pwrscale_policy_idlestats)
+#else
+#define ADRENO_DEFAULT_PWRSCALE_POLICY  NULL
+#endif
+
+#define ADRENO_ISTORE_START 0x5000 /* Istore offset */
+
+enum adreno_gpurev {
+	ADRENO_REV_UNKNOWN = 0,
+	ADRENO_REV_A200 = 200,
+	ADRENO_REV_A203 = 203,
+	ADRENO_REV_A205 = 205,
+	ADRENO_REV_A220 = 220,
+	ADRENO_REV_A225 = 225,
+	ADRENO_REV_A305 = 305,
+	ADRENO_REV_A320 = 320,
+};
+
+struct adreno_gpudev;
+
+struct adreno_device {
+	struct kgsl_device dev;    /* Must be first field in this struct */
+	unsigned int chip_id;
+	enum adreno_gpurev gpurev;
+	unsigned long gmem_base;
+	unsigned int gmem_size;
+	struct adreno_context *drawctxt_active;
+	const char *pfp_fwfile;
+	unsigned int *pfp_fw;
+	size_t pfp_fw_size;
+	const char *pm4_fwfile;
+	unsigned int *pm4_fw;
+	size_t pm4_fw_size;
+	struct adreno_ringbuffer ringbuffer;
+	unsigned int mharb;
+	struct adreno_gpudev *gpudev;
+	unsigned int wait_timeout;
+	unsigned int istore_size;
+	unsigned int pix_shader_start;
+	unsigned int instruction_size;
+	unsigned int ib_check_level;
+};
+
+struct adreno_gpudev {
+	/*
+	 * These registers are in a different location on A3XX,  so define
+	 * them in the structure and use them as variables.
+	 */
+	unsigned int reg_rbbm_status;
+	unsigned int reg_cp_pfp_ucode_data;
+	unsigned int reg_cp_pfp_ucode_addr;
+
+	/* GPU specific function hooks */
+	int (*ctxt_create)(struct adreno_device *, struct adreno_context *);
+	void (*ctxt_save)(struct adreno_device *, struct adreno_context *);
+	void (*ctxt_restore)(struct adreno_device *, struct adreno_context *);
+	irqreturn_t (*irq_handler)(struct adreno_device *);
+	void (*irq_control)(struct adreno_device *, int);
+	void * (*snapshot)(struct adreno_device *, void *, int *, int);
+	void (*rb_init)(struct adreno_device *, struct adreno_ringbuffer *);
+	void (*start)(struct adreno_device *);
+	unsigned int (*busy_cycles)(struct adreno_device *);
+};
+
+extern struct adreno_gpudev adreno_a2xx_gpudev;
+extern struct adreno_gpudev adreno_a3xx_gpudev;
+
+/* A2XX register sets defined in adreno_a2xx.c */
+extern const unsigned int a200_registers[];
+extern const unsigned int a220_registers[];
+extern const unsigned int a225_registers[];
+extern const unsigned int a200_registers_count;
+extern const unsigned int a220_registers_count;
+extern const unsigned int a225_registers_count;
+
+/* A3XX register set defined in adreno_a3xx.c */
+extern const unsigned int a3xx_registers[];
+extern const unsigned int a3xx_registers_count;
+
+int adreno_idle(struct kgsl_device *device, unsigned int timeout);
+void adreno_regread(struct kgsl_device *device, unsigned int offsetwords,
+				unsigned int *value);
+void adreno_regwrite(struct kgsl_device *device, unsigned int offsetwords,
+				unsigned int value);
+
+struct kgsl_memdesc *adreno_find_region(struct kgsl_device *device,
+						unsigned int pt_base,
+						unsigned int gpuaddr,
+						unsigned int size);
+
+uint8_t *adreno_convertaddr(struct kgsl_device *device,
+	unsigned int pt_base, unsigned int gpuaddr, unsigned int size);
+
+struct kgsl_memdesc *adreno_find_ctxtmem(struct kgsl_device *device,
+	unsigned int pt_base, unsigned int gpuaddr, unsigned int size);
+
+void *adreno_snapshot(struct kgsl_device *device, void *snapshot, int *remain,
+		int hang);
+
+static inline int adreno_is_a200(struct adreno_device *adreno_dev)
+{
+	return (adreno_dev->gpurev == ADRENO_REV_A200);
+}
+
+static inline int adreno_is_a203(struct adreno_device *adreno_dev)
+{
+	return (adreno_dev->gpurev == ADRENO_REV_A203);
+}
+
+static inline int adreno_is_a205(struct adreno_device *adreno_dev)
+{
+	return (adreno_dev->gpurev == ADRENO_REV_A205);
+}
+
+static inline int adreno_is_a20x(struct adreno_device *adreno_dev)
+{
+	return (adreno_dev->gpurev <= 209);
+}
+
+static inline int adreno_is_a220(struct adreno_device *adreno_dev)
+{
+	return (adreno_dev->gpurev == ADRENO_REV_A220);
+}
+
+static inline int adreno_is_a225(struct adreno_device *adreno_dev)
+{
+	return (adreno_dev->gpurev == ADRENO_REV_A225);
+}
+
+static inline int adreno_is_a22x(struct adreno_device *adreno_dev)
+{
+	return (adreno_dev->gpurev  == ADRENO_REV_A220 ||
+		adreno_dev->gpurev == ADRENO_REV_A225);
+}
+
+static inline int adreno_is_a2xx(struct adreno_device *adreno_dev)
+{
+	return (adreno_dev->gpurev <= 299);
+}
+
+static inline int adreno_is_a3xx(struct adreno_device *adreno_dev)
+{
+	return (adreno_dev->gpurev >= 300);
+}
+
+static inline int adreno_is_a305(struct adreno_device *adreno_dev)
+{
+	return (adreno_dev->gpurev == ADRENO_REV_A305);
+}
+
+static inline int adreno_is_a320(struct adreno_device *adreno_dev)
+{
+	return (adreno_dev->gpurev == ADRENO_REV_A320);
+}
+
+static inline int adreno_rb_ctxtswitch(unsigned int *cmd)
+{
+	return (cmd[0] == cp_nop_packet(1) &&
+		cmd[1] == KGSL_CONTEXT_TO_MEM_IDENTIFIER);
+}
+
+/**
+ * adreno_encode_istore_size - encode istore size in CP format
+ * @adreno_dev - The 3D device.
+ *
+ * Encode the istore size into the format expected that the
+ * CP_SET_SHADER_BASES and CP_ME_INIT commands:
+ * bits 31:29 - istore size as encoded by this function
+ * bits 27:16 - vertex shader start offset in instructions
+ * bits 11:0 - pixel shader start offset in instructions.
+ */
+static inline int adreno_encode_istore_size(struct adreno_device *adreno_dev)
+{
+	unsigned int size;
+	/* in a225 the CP microcode multiplies the encoded
+	 * value by 3 while decoding.
+	 */
+	if (adreno_is_a225(adreno_dev))
+		size = adreno_dev->istore_size/3;
+	else
+		size = adreno_dev->istore_size;
+
+	return (ilog2(size) - 5) << 29;
+}
+
+static inline int __adreno_add_idle_indirect_cmds(unsigned int *cmds,
+						unsigned int nop_gpuaddr)
+{
+	/* Adding an indirect buffer ensures that the prefetch stalls until
+	 * the commands in indirect buffer have completed. We need to stall
+	 * prefetch with a nop indirect buffer when updating pagetables
+	 * because it provides stabler synchronization */
+	*cmds++ = CP_HDR_INDIRECT_BUFFER_PFD;
+	*cmds++ = nop_gpuaddr;
+	*cmds++ = 2;
+	*cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
+	*cmds++ = 0x00000000;
+	return 5;
+}
+
+static inline int adreno_add_change_mh_phys_limit_cmds(unsigned int *cmds,
+						unsigned int new_phys_limit,
+						unsigned int nop_gpuaddr)
+{
+	unsigned int *start = cmds;
+
+	cmds += __adreno_add_idle_indirect_cmds(cmds, nop_gpuaddr);
+	*cmds++ = cp_type0_packet(MH_MMU_MPU_END, 1);
+	*cmds++ = new_phys_limit;
+	cmds += __adreno_add_idle_indirect_cmds(cmds, nop_gpuaddr);
+	return cmds - start;
+}
+
+static inline int adreno_add_bank_change_cmds(unsigned int *cmds,
+					int cur_ctx_bank,
+					unsigned int nop_gpuaddr)
+{
+	unsigned int *start = cmds;
+
+	cmds += __adreno_add_idle_indirect_cmds(cmds, nop_gpuaddr);
+	*cmds++ = cp_type0_packet(REG_CP_STATE_DEBUG_INDEX, 1);
+	*cmds++ = (cur_ctx_bank ? 0 : 0x20);
+	cmds += __adreno_add_idle_indirect_cmds(cmds, nop_gpuaddr);
+	return cmds - start;
+}
+
+/*
+ * adreno_read_cmds - Add pm4 packets to perform read
+ * @device - Pointer to device structure
+ * @cmds - Pointer to memory where read commands need to be added
+ * @addr - gpu address of the read
+ * @val - The GPU will wait until the data at address addr becomes
+ * equal to value
+ */
+static inline int adreno_add_read_cmds(struct kgsl_device *device,
+				unsigned int *cmds, unsigned int addr,
+				unsigned int val, unsigned int nop_gpuaddr)
+{
+	unsigned int *start = cmds;
+
+	*cmds++ = cp_type3_packet(CP_WAIT_REG_MEM, 5);
+	/* MEM SPACE = memory, FUNCTION = equals */
+	*cmds++ = 0x13;
+	*cmds++ = addr;
+	*cmds++ = val;
+	*cmds++ = 0xFFFFFFFF;
+	*cmds++ = 0xFFFFFFFF;
+	cmds += __adreno_add_idle_indirect_cmds(cmds, nop_gpuaddr);
+	return cmds - start;
+}
+
+#endif /*__ADRENO_H */
diff --git a/drivers/gpu/msm/adreno_a2xx.c b/drivers/gpu/msm/adreno_a2xx.c
new file mode 100644
index 0000000..7ccfd3f
--- /dev/null
+++ b/drivers/gpu/msm/adreno_a2xx.c
@@ -0,0 +1,1992 @@
+/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <mach/socinfo.h>
+
+#include "kgsl.h"
+#include "kgsl_sharedmem.h"
+#include "kgsl_cffdump.h"
+#include "adreno.h"
+#include "adreno_a2xx_trace.h"
+
+/*
+ * These are the registers that are dumped with GPU snapshot
+ * and postmortem.  The lists are dword offset pairs in the
+ * form of {start offset, end offset} inclusive.
+ */
+
+/* A200, A205 */
+const unsigned int a200_registers[] = {
+	0x0000, 0x0002, 0x0004, 0x000B, 0x003B, 0x003D, 0x0040, 0x0044,
+	0x0046, 0x0047, 0x01C0, 0x01C1, 0x01C3, 0x01C8, 0x01D5, 0x01D9,
+	0x01DC, 0x01DD, 0x01EA, 0x01EA, 0x01EE, 0x01F3, 0x01F6, 0x01F7,
+	0x01FC, 0x01FF, 0x0391, 0x0392, 0x039B, 0x039E, 0x03B2, 0x03B5,
+	0x03B7, 0x03B7, 0x03F8, 0x03FB, 0x0440, 0x0440, 0x0443, 0x0444,
+	0x044B, 0x044B, 0x044D, 0x044F, 0x0452, 0x0452, 0x0454, 0x045B,
+	0x047F, 0x047F, 0x0578, 0x0587, 0x05C9, 0x05C9, 0x05D0, 0x05D0,
+	0x0601, 0x0604, 0x0606, 0x0609, 0x060B, 0x060E, 0x0613, 0x0614,
+	0x0A29, 0x0A2B, 0x0A2F, 0x0A31, 0x0A40, 0x0A43, 0x0A45, 0x0A45,
+	0x0A4E, 0x0A4F, 0x0C2C, 0x0C2C, 0x0C30, 0x0C30, 0x0C38, 0x0C3C,
+	0x0C40, 0x0C40, 0x0C44, 0x0C44, 0x0C80, 0x0C86, 0x0C88, 0x0C94,
+	0x0C99, 0x0C9A, 0x0CA4, 0x0CA5, 0x0D00, 0x0D03, 0x0D06, 0x0D06,
+	0x0D08, 0x0D0B, 0x0D34, 0x0D35, 0x0DAE, 0x0DC1, 0x0DC8, 0x0DD4,
+	0x0DD8, 0x0DD9, 0x0E00, 0x0E00, 0x0E02, 0x0E04, 0x0E17, 0x0E1E,
+	0x0EC0, 0x0EC9, 0x0ECB, 0x0ECC, 0x0ED0, 0x0ED0, 0x0ED4, 0x0ED7,
+	0x0EE0, 0x0EE2, 0x0F01, 0x0F02, 0x0F0C, 0x0F0C, 0x0F0E, 0x0F12,
+	0x0F26, 0x0F2A, 0x0F2C, 0x0F2C, 0x2000, 0x2002, 0x2006, 0x200F,
+	0x2080, 0x2082, 0x2100, 0x2109, 0x210C, 0x2114, 0x2180, 0x2184,
+	0x21F5, 0x21F7, 0x2200, 0x2208, 0x2280, 0x2283, 0x2293, 0x2294,
+	0x2300, 0x2308, 0x2312, 0x2312, 0x2316, 0x231D, 0x2324, 0x2326,
+	0x2380, 0x2383, 0x2400, 0x2402, 0x2406, 0x240F, 0x2480, 0x2482,
+	0x2500, 0x2509, 0x250C, 0x2514, 0x2580, 0x2584, 0x25F5, 0x25F7,
+	0x2600, 0x2608, 0x2680, 0x2683, 0x2693, 0x2694, 0x2700, 0x2708,
+	0x2712, 0x2712, 0x2716, 0x271D, 0x2724, 0x2726, 0x2780, 0x2783,
+	0x4000, 0x4003, 0x4800, 0x4805, 0x4900, 0x4900, 0x4908, 0x4908,
+};
+
+const unsigned int a220_registers[] = {
+	0x0000, 0x0002, 0x0004, 0x000B, 0x003B, 0x003D, 0x0040, 0x0044,
+	0x0046, 0x0047, 0x01C0, 0x01C1, 0x01C3, 0x01C8, 0x01D5, 0x01D9,
+	0x01DC, 0x01DD, 0x01EA, 0x01EA, 0x01EE, 0x01F3, 0x01F6, 0x01F7,
+	0x01FC, 0x01FF, 0x0391, 0x0392, 0x039B, 0x039E, 0x03B2, 0x03B5,
+	0x03B7, 0x03B7, 0x03F8, 0x03FB, 0x0440, 0x0440, 0x0443, 0x0444,
+	0x044B, 0x044B, 0x044D, 0x044F, 0x0452, 0x0452, 0x0454, 0x045B,
+	0x047F, 0x047F, 0x0578, 0x0587, 0x05C9, 0x05C9, 0x05D0, 0x05D0,
+	0x0601, 0x0604, 0x0606, 0x0609, 0x060B, 0x060E, 0x0613, 0x0614,
+	0x0A29, 0x0A2B, 0x0A2F, 0x0A31, 0x0A40, 0x0A40, 0x0A42, 0x0A43,
+	0x0A45, 0x0A45, 0x0A4E, 0x0A4F, 0x0C30, 0x0C30, 0x0C38, 0x0C39,
+	0x0C3C, 0x0C3C, 0x0C80, 0x0C81, 0x0C88, 0x0C93, 0x0D00, 0x0D03,
+	0x0D05, 0x0D06, 0x0D08, 0x0D0B, 0x0D34, 0x0D35, 0x0DAE, 0x0DC1,
+	0x0DC8, 0x0DD4, 0x0DD8, 0x0DD9, 0x0E00, 0x0E00, 0x0E02, 0x0E04,
+	0x0E17, 0x0E1E, 0x0EC0, 0x0EC9, 0x0ECB, 0x0ECC, 0x0ED0, 0x0ED0,
+	0x0ED4, 0x0ED7, 0x0EE0, 0x0EE2, 0x0F01, 0x0F02, 0x2000, 0x2002,
+	0x2006, 0x200F, 0x2080, 0x2082, 0x2100, 0x2102, 0x2104, 0x2109,
+	0x210C, 0x2114, 0x2180, 0x2184, 0x21F5, 0x21F7, 0x2200, 0x2202,
+	0x2204, 0x2204, 0x2208, 0x2208, 0x2280, 0x2282, 0x2294, 0x2294,
+	0x2300, 0x2308, 0x2309, 0x230A, 0x2312, 0x2312, 0x2316, 0x2316,
+	0x2318, 0x231D, 0x2324, 0x2326, 0x2380, 0x2383, 0x2400, 0x2402,
+	0x2406, 0x240F, 0x2480, 0x2482, 0x2500, 0x2502, 0x2504, 0x2509,
+	0x250C, 0x2514, 0x2580, 0x2584, 0x25F5, 0x25F7, 0x2600, 0x2602,
+	0x2604, 0x2606, 0x2608, 0x2608, 0x2680, 0x2682, 0x2694, 0x2694,
+	0x2700, 0x2708, 0x2712, 0x2712, 0x2716, 0x2716, 0x2718, 0x271D,
+	0x2724, 0x2726, 0x2780, 0x2783, 0x4000, 0x4003, 0x4800, 0x4805,
+	0x4900, 0x4900, 0x4908, 0x4908,
+};
+
+const unsigned int a225_registers[] = {
+	0x0000, 0x0002, 0x0004, 0x000B, 0x003B, 0x003D, 0x0040, 0x0044,
+	0x0046, 0x0047, 0x013C, 0x013C, 0x0140, 0x014F, 0x01C0, 0x01C1,
+	0x01C3, 0x01C8, 0x01D5, 0x01D9, 0x01DC, 0x01DD, 0x01EA, 0x01EA,
+	0x01EE, 0x01F3, 0x01F6, 0x01F7, 0x01FC, 0x01FF, 0x0391, 0x0392,
+	0x039B, 0x039E, 0x03B2, 0x03B5, 0x03B7, 0x03B7, 0x03F8, 0x03FB,
+	0x0440, 0x0440, 0x0443, 0x0444, 0x044B, 0x044B, 0x044D, 0x044F,
+	0x0452, 0x0452, 0x0454, 0x045B, 0x047F, 0x047F, 0x0578, 0x0587,
+	0x05C9, 0x05C9, 0x05D0, 0x05D0, 0x0601, 0x0604, 0x0606, 0x0609,
+	0x060B, 0x060E, 0x0613, 0x0614, 0x0A29, 0x0A2B, 0x0A2F, 0x0A31,
+	0x0A40, 0x0A40, 0x0A42, 0x0A43, 0x0A45, 0x0A45, 0x0A4E, 0x0A4F,
+	0x0C01, 0x0C1D, 0x0C30, 0x0C30, 0x0C38, 0x0C39, 0x0C3C, 0x0C3C,
+	0x0C80, 0x0C81, 0x0C88, 0x0C93, 0x0D00, 0x0D03, 0x0D05, 0x0D06,
+	0x0D08, 0x0D0B, 0x0D34, 0x0D35, 0x0DAE, 0x0DC1, 0x0DC8, 0x0DD4,
+	0x0DD8, 0x0DD9, 0x0E00, 0x0E00, 0x0E02, 0x0E04, 0x0E17, 0x0E1E,
+	0x0EC0, 0x0EC9, 0x0ECB, 0x0ECC, 0x0ED0, 0x0ED0, 0x0ED4, 0x0ED7,
+	0x0EE0, 0x0EE2, 0x0F01, 0x0F02, 0x2000, 0x200F, 0x2080, 0x2082,
+	0x2100, 0x2109, 0x210C, 0x2114, 0x2180, 0x2184, 0x21F5, 0x21F7,
+	0x2200, 0x2202, 0x2204, 0x2206, 0x2208, 0x2210, 0x2220, 0x2222,
+	0x2280, 0x2282, 0x2294, 0x2294, 0x2297, 0x2297, 0x2300, 0x230A,
+	0x2312, 0x2312, 0x2315, 0x2316, 0x2318, 0x231D, 0x2324, 0x2326,
+	0x2340, 0x2357, 0x2360, 0x2360, 0x2380, 0x2383, 0x2400, 0x240F,
+	0x2480, 0x2482, 0x2500, 0x2509, 0x250C, 0x2514, 0x2580, 0x2584,
+	0x25F5, 0x25F7, 0x2600, 0x2602, 0x2604, 0x2606, 0x2608, 0x2610,
+	0x2620, 0x2622, 0x2680, 0x2682, 0x2694, 0x2694, 0x2697, 0x2697,
+	0x2700, 0x270A, 0x2712, 0x2712, 0x2715, 0x2716, 0x2718, 0x271D,
+	0x2724, 0x2726, 0x2740, 0x2757, 0x2760, 0x2760, 0x2780, 0x2783,
+	0x4000, 0x4003, 0x4800, 0x4806, 0x4808, 0x4808, 0x4900, 0x4900,
+	0x4908, 0x4908,
+};
+
+const unsigned int a200_registers_count = ARRAY_SIZE(a200_registers) / 2;
+const unsigned int a220_registers_count = ARRAY_SIZE(a220_registers) / 2;
+const unsigned int a225_registers_count = ARRAY_SIZE(a225_registers) / 2;
+
+/*
+ *
+ *  Memory Map for Register, Constant & Instruction Shadow, and Command Buffers
+ *  (34.5KB)
+ *
+ *  +---------------------+------------+-------------+---+---------------------+
+ *  | ALU Constant Shadow | Reg Shadow | C&V Buffers |Tex| Shader Instr Shadow |
+ *  +---------------------+------------+-------------+---+---------------------+
+ *    ________________________________/               \____________________
+ *   /                                                                     |
+ *  +--------------+-----------+------+-----------+------------------------+
+ *  | Restore Regs | Save Regs | Quad | Gmem Save | Gmem Restore | unused  |
+ *  +--------------+-----------+------+-----------+------------------------+
+ *
+ *              8K - ALU Constant Shadow (8K aligned)
+ *              4K - H/W Register Shadow (8K aligned)
+ *              4K - Command and Vertex Buffers
+ *                         - Indirect command buffer : Const/Reg restore
+ *                               - includes Loop & Bool const shadows
+ *                         - Indirect command buffer : Const/Reg save
+ *                         - Quad vertices & texture coordinates
+ *                         - Indirect command buffer : Gmem save
+ *                         - Indirect command buffer : Gmem restore
+ *                         - Unused (padding to 8KB boundary)
+ *             <1K - Texture Constant Shadow (768 bytes) (8K aligned)
+ *       18K - Shader Instruction Shadow
+ *               - 6K vertex (32 byte aligned)
+ *               - 6K pixel  (32 byte aligned)
+ *               - 6K shared (32 byte aligned)
+ *
+ *  Note: Reading constants into a shadow, one at a time using REG_TO_MEM, takes
+ *  3 DWORDS per DWORD transfered, plus 1 DWORD for the shadow, for a total of
+ *  16 bytes per constant.  If the texture constants were transfered this way,
+ *  the Command & Vertex Buffers section would extend past the 16K boundary.
+ *  By moving the texture constant shadow area to start at 16KB boundary, we
+ *  only require approximately 40 bytes more memory, but are able to use the
+ *  LOAD_CONSTANT_CONTEXT shadowing feature for the textures, speeding up
+ *  context switching.
+ *
+ *  [Using LOAD_CONSTANT_CONTEXT shadowing feature for the Loop and/or Bool
+ *  constants would require an additional 8KB each, for alignment.]
+ *
+ */
+
+/* Constants */
+
+#define ALU_CONSTANTS	2048	/* DWORDS */
+#define NUM_REGISTERS	1024	/* DWORDS */
+#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES
+#define CMD_BUFFER_LEN	9216	/* DWORDS */
+#else
+#define CMD_BUFFER_LEN	3072	/* DWORDS */
+#endif
+#define TEX_CONSTANTS		(32*6)	/* DWORDS */
+#define BOOL_CONSTANTS		8	/* DWORDS */
+#define LOOP_CONSTANTS		56	/* DWORDS */
+
+/* LOAD_CONSTANT_CONTEXT shadow size */
+#define LCC_SHADOW_SIZE		0x2000	/* 8KB */
+
+#define ALU_SHADOW_SIZE		LCC_SHADOW_SIZE	/* 8KB */
+#define REG_SHADOW_SIZE		0x1000	/* 4KB */
+#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES
+#define CMD_BUFFER_SIZE		0x9000	/* 36KB */
+#else
+#define CMD_BUFFER_SIZE		0x3000	/* 12KB */
+#endif
+#define TEX_SHADOW_SIZE		(TEX_CONSTANTS*4)	/* 768 bytes */
+
+#define REG_OFFSET		LCC_SHADOW_SIZE
+#define CMD_OFFSET		(REG_OFFSET + REG_SHADOW_SIZE)
+#define TEX_OFFSET		(CMD_OFFSET + CMD_BUFFER_SIZE)
+#define SHADER_OFFSET		((TEX_OFFSET + TEX_SHADOW_SIZE + 32) & ~31)
+
+static inline int _shader_shadow_size(struct adreno_device *adreno_dev)
+{
+	return adreno_dev->istore_size *
+		(adreno_dev->instruction_size * sizeof(unsigned int));
+}
+
+static inline int _context_size(struct adreno_device *adreno_dev)
+{
+	return SHADER_OFFSET + 3*_shader_shadow_size(adreno_dev);
+}
+
+/* A scratchpad used to build commands during context create */
+
+static struct tmp_ctx {
+	unsigned int *start;	/* Command & Vertex buffer start */
+	unsigned int *cmd;	/* Next available dword in C&V buffer */
+
+	/* address of buffers, needed when creating IB1 command buffers. */
+	uint32_t bool_shadow;	/* bool constants */
+	uint32_t loop_shadow;	/* loop constants */
+
+	uint32_t shader_shared;	/* shared shader instruction shadow */
+	uint32_t shader_vertex;	/* vertex shader instruction shadow */
+	uint32_t shader_pixel;	/* pixel shader instruction shadow */
+
+	/* Addresses in command buffer where separately handled registers
+	 * are saved
+	 */
+	uint32_t reg_values[33];
+	uint32_t chicken_restore;
+
+	uint32_t gmem_base;	/* Base gpu address of GMEM */
+
+} tmp_ctx;
+
+/* context save (gmem -> sys) */
+
+/* pre-compiled vertex shader program
+*
+*  attribute vec4  P;
+*  void main(void)
+*  {
+*    gl_Position = P;
+*  }
+*/
+#define GMEM2SYS_VTX_PGM_LEN	0x12
+
+static unsigned int gmem2sys_vtx_pgm[GMEM2SYS_VTX_PGM_LEN] = {
+	0x00011003, 0x00001000, 0xc2000000,
+	0x00001004, 0x00001000, 0xc4000000,
+	0x00001005, 0x00002000, 0x00000000,
+	0x1cb81000, 0x00398a88, 0x00000003,
+	0x140f803e, 0x00000000, 0xe2010100,
+	0x14000000, 0x00000000, 0xe2000000
+};
+
+/* pre-compiled fragment shader program
+*
+*  precision highp float;
+*  uniform   vec4  clear_color;
+*  void main(void)
+*  {
+*     gl_FragColor = clear_color;
+*  }
+*/
+
+#define GMEM2SYS_FRAG_PGM_LEN	0x0c
+
+static unsigned int gmem2sys_frag_pgm[GMEM2SYS_FRAG_PGM_LEN] = {
+	0x00000000, 0x1002c400, 0x10000000,
+	0x00001003, 0x00002000, 0x00000000,
+	0x140f8000, 0x00000000, 0x22000000,
+	0x14000000, 0x00000000, 0xe2000000
+};
+
+/* context restore (sys -> gmem) */
+/* pre-compiled vertex shader program
+*
+*  attribute vec4 position;
+*  attribute vec4 texcoord;
+*  varying   vec4 texcoord0;
+*  void main()
+*  {
+*     gl_Position = position;
+*     texcoord0 = texcoord;
+*  }
+*/
+
+#define SYS2GMEM_VTX_PGM_LEN	0x18
+
+static unsigned int sys2gmem_vtx_pgm[SYS2GMEM_VTX_PGM_LEN] = {
+	0x00052003, 0x00001000, 0xc2000000, 0x00001005,
+	0x00001000, 0xc4000000, 0x00001006, 0x10071000,
+	0x20000000, 0x18981000, 0x0039ba88, 0x00000003,
+	0x12982000, 0x40257b08, 0x00000002, 0x140f803e,
+	0x00000000, 0xe2010100, 0x140f8000, 0x00000000,
+	0xe2020200, 0x14000000, 0x00000000, 0xe2000000
+};
+
+/* pre-compiled fragment shader program
+*
+*  precision mediump   float;
+*  uniform   sampler2D tex0;
+*  varying   vec4      texcoord0;
+*  void main()
+*  {
+*     gl_FragColor = texture2D(tex0, texcoord0.xy);
+*  }
+*/
+
+#define SYS2GMEM_FRAG_PGM_LEN	0x0f
+
+static unsigned int sys2gmem_frag_pgm[SYS2GMEM_FRAG_PGM_LEN] = {
+	0x00011002, 0x00001000, 0xc4000000, 0x00001003,
+	0x10041000, 0x20000000, 0x10000001, 0x1ffff688,
+	0x00000002, 0x140f8000, 0x00000000, 0xe2000000,
+	0x14000000, 0x00000000, 0xe2000000
+};
+
+/* shader texture constants (sysmem -> gmem)  */
+#define SYS2GMEM_TEX_CONST_LEN	6
+
+static unsigned int sys2gmem_tex_const[SYS2GMEM_TEX_CONST_LEN] = {
+	/* Texture, FormatXYZW=Unsigned, ClampXYZ=Wrap/Repeat,
+	 * RFMode=ZeroClamp-1, Dim=1:2d
+	 */
+	0x00000002,		/* Pitch = TBD */
+
+	/* Format=6:8888_WZYX, EndianSwap=0:None, ReqSize=0:256bit, DimHi=0,
+	 * NearestClamp=1:OGL Mode
+	 */
+	0x00000800,		/* Address[31:12] = TBD */
+
+	/* Width, Height, EndianSwap=0:None */
+	0,			/* Width & Height = TBD */
+
+	/* NumFormat=0:RF, DstSelXYZW=XYZW, ExpAdj=0, MagFilt=MinFilt=0:Point,
+	 * Mip=2:BaseMap
+	 */
+	0 << 1 | 1 << 4 | 2 << 7 | 3 << 10 | 2 << 23,
+
+	/* VolMag=VolMin=0:Point, MinMipLvl=0, MaxMipLvl=1, LodBiasH=V=0,
+	 * Dim3d=0
+	 */
+	0,
+
+	/* BorderColor=0:ABGRBlack, ForceBC=0:diable, TriJuice=0, Aniso=0,
+	 * Dim=1:2d, MipPacking=0
+	 */
+	1 << 9			/* Mip Address[31:12] = TBD */
+};
+
+#define NUM_COLOR_FORMATS   13
+
+static enum SURFACEFORMAT surface_format_table[NUM_COLOR_FORMATS] = {
+	FMT_4_4_4_4,		/* COLORX_4_4_4_4 */
+	FMT_1_5_5_5,		/* COLORX_1_5_5_5 */
+	FMT_5_6_5,		/* COLORX_5_6_5 */
+	FMT_8,			/* COLORX_8 */
+	FMT_8_8,		/* COLORX_8_8 */
+	FMT_8_8_8_8,		/* COLORX_8_8_8_8 */
+	FMT_8_8_8_8,		/* COLORX_S8_8_8_8 */
+	FMT_16_FLOAT,		/* COLORX_16_FLOAT */
+	FMT_16_16_FLOAT,	/* COLORX_16_16_FLOAT */
+	FMT_16_16_16_16_FLOAT,	/* COLORX_16_16_16_16_FLOAT */
+	FMT_32_FLOAT,		/* COLORX_32_FLOAT */
+	FMT_32_32_FLOAT,	/* COLORX_32_32_FLOAT */
+	FMT_32_32_32_32_FLOAT,	/* COLORX_32_32_32_32_FLOAT */
+};
+
+static unsigned int format2bytesperpixel[NUM_COLOR_FORMATS] = {
+	2,			/* COLORX_4_4_4_4 */
+	2,			/* COLORX_1_5_5_5 */
+	2,			/* COLORX_5_6_5 */
+	1,			/* COLORX_8 */
+	2,			/* COLORX_8_8 8*/
+	4,			/* COLORX_8_8_8_8 */
+	4,			/* COLORX_S8_8_8_8 */
+	2,			/* COLORX_16_FLOAT */
+	4,			/* COLORX_16_16_FLOAT */
+	8,			/* COLORX_16_16_16_16_FLOAT */
+	4,			/* COLORX_32_FLOAT */
+	8,			/* COLORX_32_32_FLOAT */
+	16,			/* COLORX_32_32_32_32_FLOAT */
+};
+
+/* shader linkage info */
+#define SHADER_CONST_ADDR	(11 * 6 + 3)
+
+
+static unsigned int *program_shader(unsigned int *cmds, int vtxfrag,
+				    unsigned int *shader_pgm, int dwords)
+{
+	/* load the patched vertex shader stream */
+	*cmds++ = cp_type3_packet(CP_IM_LOAD_IMMEDIATE, 2 + dwords);
+	/* 0=vertex shader, 1=fragment shader */
+	*cmds++ = vtxfrag;
+	/* instruction start & size (in 32-bit words) */
+	*cmds++ = ((0 << 16) | dwords);
+
+	memcpy(cmds, shader_pgm, dwords << 2);
+	cmds += dwords;
+
+	return cmds;
+}
+
+static unsigned int *reg_to_mem(unsigned int *cmds, uint32_t dst,
+				uint32_t src, int dwords)
+{
+	while (dwords-- > 0) {
+		*cmds++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+		*cmds++ = src++;
+		*cmds++ = dst;
+		dst += 4;
+	}
+
+	return cmds;
+}
+
+#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES
+
+static void build_reg_to_mem_range(unsigned int start, unsigned int end,
+				   unsigned int **cmd,
+				   struct adreno_context *drawctxt)
+{
+	unsigned int i = start;
+
+	for (i = start; i <= end; i++) {
+		*(*cmd)++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+		*(*cmd)++ = i;
+		*(*cmd)++ =
+		    ((drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000) +
+		    (i - 0x2000) * 4;
+	}
+}
+
+#endif
+
+/* chicken restore */
+static unsigned int *build_chicken_restore_cmds(
+					struct adreno_context *drawctxt)
+{
+	unsigned int *start = tmp_ctx.cmd;
+	unsigned int *cmds = start;
+
+	*cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
+	*cmds++ = 0;
+
+	*cmds++ = cp_type0_packet(REG_TP0_CHICKEN, 1);
+	tmp_ctx.chicken_restore = virt2gpu(cmds, &drawctxt->gpustate);
+	*cmds++ = 0x00000000;
+
+	/* create indirect buffer command for above command sequence */
+	create_ib1(drawctxt, drawctxt->chicken_restore, start, cmds);
+
+	return cmds;
+}
+
+/****************************************************************************/
+/* context save                                                             */
+/****************************************************************************/
+
+static const unsigned int register_ranges_a20x[] = {
+	REG_RB_SURFACE_INFO, REG_RB_DEPTH_INFO,
+	REG_COHER_DEST_BASE_0, REG_PA_SC_SCREEN_SCISSOR_BR,
+	REG_PA_SC_WINDOW_OFFSET, REG_PA_SC_WINDOW_SCISSOR_BR,
+	REG_RB_STENCILREFMASK_BF, REG_PA_CL_VPORT_ZOFFSET,
+	REG_SQ_PROGRAM_CNTL, REG_SQ_WRAPPING_1,
+	REG_PA_SC_LINE_CNTL, REG_SQ_PS_CONST,
+	REG_PA_SC_AA_MASK, REG_PA_SC_AA_MASK,
+	REG_RB_SAMPLE_COUNT_CTL, REG_RB_COLOR_DEST_MASK,
+	REG_PA_SU_POLY_OFFSET_FRONT_SCALE, REG_PA_SU_POLY_OFFSET_BACK_OFFSET,
+	REG_VGT_MAX_VTX_INDX, REG_RB_FOG_COLOR,
+	REG_RB_DEPTHCONTROL, REG_RB_MODECONTROL,
+	REG_PA_SU_POINT_SIZE, REG_PA_SC_LINE_STIPPLE,
+	REG_PA_SC_VIZ_QUERY, REG_PA_SC_VIZ_QUERY,
+	REG_VGT_VERTEX_REUSE_BLOCK_CNTL, REG_RB_DEPTH_CLEAR
+};
+
+static const unsigned int register_ranges_a220[] = {
+	REG_RB_SURFACE_INFO, REG_RB_DEPTH_INFO,
+	REG_COHER_DEST_BASE_0, REG_PA_SC_SCREEN_SCISSOR_BR,
+	REG_PA_SC_WINDOW_OFFSET, REG_PA_SC_WINDOW_SCISSOR_BR,
+	REG_RB_STENCILREFMASK_BF, REG_PA_CL_VPORT_ZOFFSET,
+	REG_SQ_PROGRAM_CNTL, REG_SQ_WRAPPING_1,
+	REG_PA_SC_LINE_CNTL, REG_SQ_PS_CONST,
+	REG_PA_SC_AA_MASK, REG_PA_SC_AA_MASK,
+	REG_RB_SAMPLE_COUNT_CTL, REG_RB_COLOR_DEST_MASK,
+	REG_PA_SU_POLY_OFFSET_FRONT_SCALE, REG_PA_SU_POLY_OFFSET_BACK_OFFSET,
+	REG_A220_PC_MAX_VTX_INDX, REG_A220_PC_INDX_OFFSET,
+	REG_RB_COLOR_MASK, REG_RB_FOG_COLOR,
+	REG_RB_DEPTHCONTROL, REG_RB_COLORCONTROL,
+	REG_PA_CL_CLIP_CNTL, REG_PA_CL_VTE_CNTL,
+	REG_RB_MODECONTROL, REG_RB_SAMPLE_POS,
+	REG_PA_SU_POINT_SIZE, REG_PA_SU_LINE_CNTL,
+	REG_A220_PC_VERTEX_REUSE_BLOCK_CNTL,
+	REG_A220_PC_VERTEX_REUSE_BLOCK_CNTL,
+	REG_RB_COPY_CONTROL, REG_RB_DEPTH_CLEAR
+};
+
+static const unsigned int register_ranges_a225[] = {
+	REG_RB_SURFACE_INFO, REG_A225_RB_COLOR_INFO3,
+	REG_COHER_DEST_BASE_0, REG_PA_SC_SCREEN_SCISSOR_BR,
+	REG_PA_SC_WINDOW_OFFSET, REG_PA_SC_WINDOW_SCISSOR_BR,
+	REG_RB_STENCILREFMASK_BF, REG_PA_CL_VPORT_ZOFFSET,
+	REG_SQ_PROGRAM_CNTL, REG_SQ_WRAPPING_1,
+	REG_PA_SC_LINE_CNTL, REG_SQ_PS_CONST,
+	REG_PA_SC_AA_MASK, REG_PA_SC_AA_MASK,
+	REG_RB_SAMPLE_COUNT_CTL, REG_RB_COLOR_DEST_MASK,
+	REG_PA_SU_POLY_OFFSET_FRONT_SCALE, REG_PA_SU_POLY_OFFSET_BACK_OFFSET,
+	REG_A220_PC_MAX_VTX_INDX, REG_A225_PC_MULTI_PRIM_IB_RESET_INDX,
+	REG_RB_COLOR_MASK, REG_RB_FOG_COLOR,
+	REG_RB_DEPTHCONTROL, REG_RB_COLORCONTROL,
+	REG_PA_CL_CLIP_CNTL, REG_PA_CL_VTE_CNTL,
+	REG_RB_MODECONTROL, REG_RB_SAMPLE_POS,
+	REG_PA_SU_POINT_SIZE, REG_PA_SU_LINE_CNTL,
+	REG_A220_PC_VERTEX_REUSE_BLOCK_CNTL,
+	REG_A220_PC_VERTEX_REUSE_BLOCK_CNTL,
+	REG_RB_COPY_CONTROL, REG_RB_DEPTH_CLEAR,
+	REG_A225_GRAS_UCP0X, REG_A225_GRAS_UCP5W,
+	REG_A225_GRAS_UCP_ENABLED, REG_A225_GRAS_UCP_ENABLED
+};
+
+
+/* save h/w regs, alu constants, texture contants, etc. ...
+*  requires: bool_shadow_gpuaddr, loop_shadow_gpuaddr
+*/
+static void build_regsave_cmds(struct adreno_device *adreno_dev,
+			       struct adreno_context *drawctxt)
+{
+	unsigned int *start = tmp_ctx.cmd;
+	unsigned int *cmd = start;
+
+	*cmd++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
+	*cmd++ = 0;
+
+#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES
+	/* Make sure the HW context has the correct register values
+	 * before reading them. */
+	*cmd++ = cp_type3_packet(CP_CONTEXT_UPDATE, 1);
+	*cmd++ = 0;
+
+	{
+		unsigned int i = 0;
+		unsigned int reg_array_size = 0;
+		const unsigned int *ptr_register_ranges;
+
+		/* Based on chip id choose the register ranges */
+		if (adreno_is_a220(adreno_dev)) {
+			ptr_register_ranges = register_ranges_a220;
+			reg_array_size = ARRAY_SIZE(register_ranges_a220);
+		} else if (adreno_is_a225(adreno_dev)) {
+			ptr_register_ranges = register_ranges_a225;
+			reg_array_size = ARRAY_SIZE(register_ranges_a225);
+		} else {
+			ptr_register_ranges = register_ranges_a20x;
+			reg_array_size = ARRAY_SIZE(register_ranges_a20x);
+		}
+
+
+		/* Write HW registers into shadow */
+		for (i = 0; i < (reg_array_size/2) ; i++) {
+			build_reg_to_mem_range(ptr_register_ranges[i*2],
+					ptr_register_ranges[i*2+1],
+					&cmd, drawctxt);
+		}
+	}
+
+	/* Copy ALU constants */
+	cmd =
+	    reg_to_mem(cmd, (drawctxt->gpustate.gpuaddr) & 0xFFFFE000,
+		       REG_SQ_CONSTANT_0, ALU_CONSTANTS);
+
+	/* Copy Tex constants */
+	cmd =
+	    reg_to_mem(cmd,
+		       (drawctxt->gpustate.gpuaddr + TEX_OFFSET) & 0xFFFFE000,
+		       REG_SQ_FETCH_0, TEX_CONSTANTS);
+#else
+
+	/* Insert a wait for idle packet before reading the registers.
+	 * This is to fix a hang/reset seen during stress testing.  In this
+	 * hang, CP encountered a timeout reading SQ's boolean constant
+	 * register. There is logic in the HW that blocks reading of this
+	 * register when the SQ block is not idle, which we believe is
+	 * contributing to the hang.*/
+	*cmd++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
+	*cmd++ = 0;
+
+	/* H/w registers are already shadowed; just need to disable shadowing
+	 * to prevent corruption.
+	 */
+	*cmd++ = cp_type3_packet(CP_LOAD_CONSTANT_CONTEXT, 3);
+	*cmd++ = (drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000;
+	*cmd++ = 4 << 16;	/* regs, start=0 */
+	*cmd++ = 0x0;		/* count = 0 */
+
+	/* ALU constants are already shadowed; just need to disable shadowing
+	 * to prevent corruption.
+	 */
+	*cmd++ = cp_type3_packet(CP_LOAD_CONSTANT_CONTEXT, 3);
+	*cmd++ = drawctxt->gpustate.gpuaddr & 0xFFFFE000;
+	*cmd++ = 0 << 16;	/* ALU, start=0 */
+	*cmd++ = 0x0;		/* count = 0 */
+
+	/* Tex constants are already shadowed; just need to disable shadowing
+	 *  to prevent corruption.
+	 */
+	*cmd++ = cp_type3_packet(CP_LOAD_CONSTANT_CONTEXT, 3);
+	*cmd++ = (drawctxt->gpustate.gpuaddr + TEX_OFFSET) & 0xFFFFE000;
+	*cmd++ = 1 << 16;	/* Tex, start=0 */
+	*cmd++ = 0x0;		/* count = 0 */
+#endif
+
+	/* Need to handle some of the registers separately */
+	*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+	*cmd++ = REG_SQ_GPR_MANAGEMENT;
+	*cmd++ = tmp_ctx.reg_values[0];
+
+	*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+	*cmd++ = REG_TP0_CHICKEN;
+	*cmd++ = tmp_ctx.reg_values[1];
+
+	if (adreno_is_a22x(adreno_dev)) {
+		unsigned int i;
+		unsigned int j = 2;
+		for (i = REG_A220_VSC_BIN_SIZE; i <=
+				REG_A220_VSC_PIPE_DATA_LENGTH_7; i++) {
+			*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+			*cmd++ = i;
+			*cmd++ = tmp_ctx.reg_values[j];
+			j++;
+		}
+	}
+
+	/* Copy Boolean constants */
+	cmd = reg_to_mem(cmd, tmp_ctx.bool_shadow, REG_SQ_CF_BOOLEANS,
+			 BOOL_CONSTANTS);
+
+	/* Copy Loop constants */
+	cmd = reg_to_mem(cmd, tmp_ctx.loop_shadow,
+		REG_SQ_CF_LOOP, LOOP_CONSTANTS);
+
+	/* create indirect buffer command for above command sequence */
+	create_ib1(drawctxt, drawctxt->reg_save, start, cmd);
+
+	tmp_ctx.cmd = cmd;
+}
+
+/*copy colour, depth, & stencil buffers from graphics memory to system memory*/
+static unsigned int *build_gmem2sys_cmds(struct adreno_device *adreno_dev,
+					 struct adreno_context *drawctxt,
+					 struct gmem_shadow_t *shadow)
+{
+	unsigned int *cmds = shadow->gmem_save_commands;
+	unsigned int *start = cmds;
+	/* Calculate the new offset based on the adjusted base */
+	unsigned int bytesperpixel = format2bytesperpixel[shadow->format];
+	unsigned int addr = shadow->gmemshadow.gpuaddr;
+	unsigned int offset = (addr - (addr & 0xfffff000)) / bytesperpixel;
+
+	if (!(drawctxt->flags & CTXT_FLAGS_PREAMBLE)) {
+		/* Store TP0_CHICKEN register */
+		*cmds++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+		*cmds++ = REG_TP0_CHICKEN;
+
+		*cmds++ = tmp_ctx.chicken_restore;
+
+		*cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
+		*cmds++ = 0;
+	}
+
+	/* Set TP0_CHICKEN to zero */
+	*cmds++ = cp_type0_packet(REG_TP0_CHICKEN, 1);
+	*cmds++ = 0x00000000;
+
+	/* Set PA_SC_AA_CONFIG to 0 */
+	*cmds++ = cp_type0_packet(REG_PA_SC_AA_CONFIG, 1);
+	*cmds++ = 0x00000000;
+
+	/* program shader */
+
+	/* load shader vtx constants ... 5 dwords */
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 4);
+	*cmds++ = (0x1 << 16) | SHADER_CONST_ADDR;
+	*cmds++ = 0;
+	/* valid(?) vtx constant flag & addr */
+	*cmds++ = shadow->quad_vertices.gpuaddr | 0x3;
+	/* limit = 12 dwords */
+	*cmds++ = 0x00000030;
+
+	/* Invalidate L2 cache to make sure vertices are updated */
+	*cmds++ = cp_type0_packet(REG_TC_CNTL_STATUS, 1);
+	*cmds++ = 0x1;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 4);
+	*cmds++ = CP_REG(REG_VGT_MAX_VTX_INDX);
+	*cmds++ = 0x00ffffff;	/* REG_VGT_MAX_VTX_INDX */
+	*cmds++ = 0x0;		/* REG_VGT_MIN_VTX_INDX */
+	*cmds++ = 0x00000000;	/* REG_VGT_INDX_OFFSET */
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(REG_PA_SC_AA_MASK);
+	*cmds++ = 0x0000ffff;	/* REG_PA_SC_AA_MASK */
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(REG_RB_COLORCONTROL);
+	*cmds++ = 0x00000c20;
+
+	/* Repartition shaders */
+	*cmds++ = cp_type0_packet(REG_SQ_INST_STORE_MANAGMENT, 1);
+	*cmds++ = adreno_dev->pix_shader_start;
+
+	/* Invalidate Vertex & Pixel instruction code address and sizes */
+	*cmds++ = cp_type3_packet(CP_INVALIDATE_STATE, 1);
+	*cmds++ = 0x00003F00;
+
+	*cmds++ = cp_type3_packet(CP_SET_SHADER_BASES, 1);
+	*cmds++ = adreno_encode_istore_size(adreno_dev)
+		  | adreno_dev->pix_shader_start;
+
+	/* load the patched vertex shader stream */
+	cmds = program_shader(cmds, 0, gmem2sys_vtx_pgm, GMEM2SYS_VTX_PGM_LEN);
+
+	/* Load the patched fragment shader stream */
+	cmds =
+	    program_shader(cmds, 1, gmem2sys_frag_pgm, GMEM2SYS_FRAG_PGM_LEN);
+
+	/* SQ_PROGRAM_CNTL / SQ_CONTEXT_MISC */
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(REG_SQ_PROGRAM_CNTL);
+	if (adreno_is_a22x(adreno_dev))
+		*cmds++ = 0x10018001;
+	else
+		*cmds++ = 0x10010001;
+	*cmds++ = 0x00000008;
+
+	/* resolve */
+
+	/* PA_CL_VTE_CNTL */
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(REG_PA_CL_VTE_CNTL);
+	/* disable X/Y/Z transforms, X/Y/Z are premultiplied by W */
+	*cmds++ = 0x00000b00;
+
+	/* program surface info */
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(REG_RB_SURFACE_INFO);
+	*cmds++ = shadow->gmem_pitch;	/* pitch, MSAA = 1 */
+
+	/* RB_COLOR_INFO Endian=none, Linear, Format=RGBA8888, Swap=0,
+	 *                Base=gmem_base
+	 */
+	/* gmem base assumed 4K aligned. */
+	BUG_ON(tmp_ctx.gmem_base & 0xFFF);
+	*cmds++ =
+	    (shadow->
+	     format << RB_COLOR_INFO__COLOR_FORMAT__SHIFT) | tmp_ctx.gmem_base;
+
+	/* disable Z */
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(REG_RB_DEPTHCONTROL);
+	if (adreno_is_a22x(adreno_dev))
+		*cmds++ = 0x08;
+	else
+		*cmds++ = 0;
+
+	/* set REG_PA_SU_SC_MODE_CNTL
+	 *              Front_ptype = draw triangles
+	 *              Back_ptype = draw triangles
+	 *              Provoking vertex = last
+	 */
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(REG_PA_SU_SC_MODE_CNTL);
+	*cmds++ = 0x00080240;
+
+	/* Use maximum scissor values -- quad vertices already have the
+	 * correct bounds */
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(REG_PA_SC_SCREEN_SCISSOR_TL);
+	*cmds++ = (0 << 16) | 0;
+	*cmds++ = (0x1fff << 16) | (0x1fff);
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(REG_PA_SC_WINDOW_SCISSOR_TL);
+	*cmds++ = (unsigned int)((1U << 31) | (0 << 16) | 0);
+	*cmds++ = (0x1fff << 16) | (0x1fff);
+
+	/* load the viewport so that z scale = clear depth and
+	 *  z offset = 0.0f
+	 */
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(REG_PA_CL_VPORT_ZSCALE);
+	*cmds++ = 0xbf800000;	/* -1.0f */
+	*cmds++ = 0x0;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(REG_RB_COLOR_MASK);
+	*cmds++ = 0x0000000f;	/* R = G = B = 1:enabled */
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(REG_RB_COLOR_DEST_MASK);
+	*cmds++ = 0xffffffff;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(REG_SQ_WRAPPING_0);
+	*cmds++ = 0x00000000;
+	*cmds++ = 0x00000000;
+
+	/* load the stencil ref value
+	 * $AAM - do this later
+	 */
+
+	/* load the COPY state */
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 6);
+	*cmds++ = CP_REG(REG_RB_COPY_CONTROL);
+	*cmds++ = 0;		/* RB_COPY_CONTROL */
+	*cmds++ = addr & 0xfffff000;	/* RB_COPY_DEST_BASE */
+	*cmds++ = shadow->pitch >> 5;	/* RB_COPY_DEST_PITCH */
+
+	/* Endian=none, Linear, Format=RGBA8888,Swap=0,!Dither,
+	 *  MaskWrite:R=G=B=A=1
+	 */
+	*cmds++ = 0x0003c008 |
+	    (shadow->format << RB_COPY_DEST_INFO__COPY_DEST_FORMAT__SHIFT);
+	/* Make sure we stay in offsetx field. */
+	BUG_ON(offset & 0xfffff000);
+	*cmds++ = offset;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(REG_RB_MODECONTROL);
+	*cmds++ = 0x6;		/* EDRAM copy */
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(REG_PA_CL_CLIP_CNTL);
+	*cmds++ = 0x00010000;
+
+	if (adreno_is_a22x(adreno_dev)) {
+		*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+		*cmds++ = CP_REG(REG_A220_RB_LRZ_VSC_CONTROL);
+		*cmds++ = 0x0000000;
+
+		*cmds++ = cp_type3_packet(CP_DRAW_INDX, 3);
+		*cmds++ = 0;           /* viz query info. */
+		/* PrimType=RectList, SrcSel=AutoIndex, VisCullMode=Ignore*/
+		*cmds++ = 0x00004088;
+		*cmds++ = 3;	       /* NumIndices=3 */
+	} else {
+		/* queue the draw packet */
+		*cmds++ = cp_type3_packet(CP_DRAW_INDX, 2);
+		*cmds++ = 0;		/* viz query info. */
+		/* PrimType=RectList, NumIndices=3, SrcSel=AutoIndex */
+		*cmds++ = 0x00030088;
+	}
+
+	/* create indirect buffer command for above command sequence */
+	create_ib1(drawctxt, shadow->gmem_save, start, cmds);
+
+	return cmds;
+}
+
+/* context restore */
+
+/*copy colour, depth, & stencil buffers from system memory to graphics memory*/
+static unsigned int *build_sys2gmem_cmds(struct adreno_device *adreno_dev,
+					 struct adreno_context *drawctxt,
+					 struct gmem_shadow_t *shadow)
+{
+	unsigned int *cmds = shadow->gmem_restore_commands;
+	unsigned int *start = cmds;
+
+	if (!(drawctxt->flags & CTXT_FLAGS_PREAMBLE)) {
+		/* Store TP0_CHICKEN register */
+		*cmds++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+		*cmds++ = REG_TP0_CHICKEN;
+		*cmds++ = tmp_ctx.chicken_restore;
+
+		*cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
+		*cmds++ = 0;
+	}
+
+	/* Set TP0_CHICKEN to zero */
+	*cmds++ = cp_type0_packet(REG_TP0_CHICKEN, 1);
+	*cmds++ = 0x00000000;
+
+	/* Set PA_SC_AA_CONFIG to 0 */
+	*cmds++ = cp_type0_packet(REG_PA_SC_AA_CONFIG, 1);
+	*cmds++ = 0x00000000;
+	/* shader constants */
+
+	/* vertex buffer constants */
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 7);
+
+	*cmds++ = (0x1 << 16) | (9 * 6);
+	/* valid(?) vtx constant flag & addr */
+	*cmds++ = shadow->quad_vertices.gpuaddr | 0x3;
+	/* limit = 12 dwords */
+	*cmds++ = 0x00000030;
+	/* valid(?) vtx constant flag & addr */
+	*cmds++ = shadow->quad_texcoords.gpuaddr | 0x3;
+	/* limit = 8 dwords */
+	*cmds++ = 0x00000020;
+	*cmds++ = 0;
+	*cmds++ = 0;
+
+	/* Invalidate L2 cache to make sure vertices are updated */
+	*cmds++ = cp_type0_packet(REG_TC_CNTL_STATUS, 1);
+	*cmds++ = 0x1;
+
+	cmds = program_shader(cmds, 0, sys2gmem_vtx_pgm, SYS2GMEM_VTX_PGM_LEN);
+
+	/* Repartition shaders */
+	*cmds++ = cp_type0_packet(REG_SQ_INST_STORE_MANAGMENT, 1);
+	*cmds++ = adreno_dev->pix_shader_start;
+
+	/* Invalidate Vertex & Pixel instruction code address and sizes */
+	*cmds++ = cp_type3_packet(CP_INVALIDATE_STATE, 1);
+	*cmds++ = 0x00000300; /* 0x100 = Vertex, 0x200 = Pixel */
+
+	*cmds++ = cp_type3_packet(CP_SET_SHADER_BASES, 1);
+	*cmds++ = adreno_encode_istore_size(adreno_dev)
+		  | adreno_dev->pix_shader_start;
+
+	/* Load the patched fragment shader stream */
+	cmds =
+	    program_shader(cmds, 1, sys2gmem_frag_pgm, SYS2GMEM_FRAG_PGM_LEN);
+
+	/* SQ_PROGRAM_CNTL / SQ_CONTEXT_MISC */
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(REG_SQ_PROGRAM_CNTL);
+	*cmds++ = 0x10030002;
+	*cmds++ = 0x00000008;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(REG_PA_SC_AA_MASK);
+	*cmds++ = 0x0000ffff;	/* REG_PA_SC_AA_MASK */
+
+	if (!adreno_is_a22x(adreno_dev)) {
+		/* PA_SC_VIZ_QUERY */
+		*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+		*cmds++ = CP_REG(REG_PA_SC_VIZ_QUERY);
+		*cmds++ = 0x0;		/*REG_PA_SC_VIZ_QUERY */
+	}
+
+	/* RB_COLORCONTROL */
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(REG_RB_COLORCONTROL);
+	*cmds++ = 0x00000c20;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 4);
+	*cmds++ = CP_REG(REG_VGT_MAX_VTX_INDX);
+	*cmds++ = 0x00ffffff;	/* mmVGT_MAX_VTX_INDX */
+	*cmds++ = 0x0;		/* mmVGT_MIN_VTX_INDX */
+	*cmds++ = 0x00000000;	/* mmVGT_INDX_OFFSET */
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(REG_VGT_VERTEX_REUSE_BLOCK_CNTL);
+	*cmds++ = 0x00000002;	/* mmVGT_VERTEX_REUSE_BLOCK_CNTL */
+	*cmds++ = 0x00000002;	/* mmVGT_OUT_DEALLOC_CNTL */
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(REG_SQ_INTERPOLATOR_CNTL);
+	*cmds++ = 0xffffffff;	/* mmSQ_INTERPOLATOR_CNTL */
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(REG_PA_SC_AA_CONFIG);
+	*cmds++ = 0x00000000;	/* REG_PA_SC_AA_CONFIG */
+
+	/* set REG_PA_SU_SC_MODE_CNTL
+	 * Front_ptype = draw triangles
+	 * Back_ptype = draw triangles
+	 * Provoking vertex = last
+	 */
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(REG_PA_SU_SC_MODE_CNTL);
+	*cmds++ = 0x00080240;
+
+	/* texture constants */
+	*cmds++ =
+	    cp_type3_packet(CP_SET_CONSTANT, (SYS2GMEM_TEX_CONST_LEN + 1));
+	*cmds++ = (0x1 << 16) | (0 * 6);
+	memcpy(cmds, sys2gmem_tex_const, SYS2GMEM_TEX_CONST_LEN << 2);
+	cmds[0] |= (shadow->pitch >> 5) << 22;
+	cmds[1] |=
+	    shadow->gmemshadow.gpuaddr | surface_format_table[shadow->format];
+	cmds[2] |= (shadow->width - 1) | (shadow->height - 1) << 13;
+	cmds += SYS2GMEM_TEX_CONST_LEN;
+
+	/* program surface info */
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(REG_RB_SURFACE_INFO);
+	*cmds++ = shadow->gmem_pitch;	/* pitch, MSAA = 1 */
+
+	/* RB_COLOR_INFO Endian=none, Linear, Format=RGBA8888, Swap=0,
+	 *                Base=gmem_base
+	 */
+	*cmds++ =
+	    (shadow->
+	     format << RB_COLOR_INFO__COLOR_FORMAT__SHIFT) | tmp_ctx.gmem_base;
+
+	/* RB_DEPTHCONTROL */
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(REG_RB_DEPTHCONTROL);
+
+	if (adreno_is_a22x(adreno_dev))
+		*cmds++ = 8;		/* disable Z */
+	else
+		*cmds++ = 0;		/* disable Z */
+
+	/* Use maximum scissor values -- quad vertices already
+	 * have the correct bounds */
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(REG_PA_SC_SCREEN_SCISSOR_TL);
+	*cmds++ = (0 << 16) | 0;
+	*cmds++ = ((0x1fff) << 16) | 0x1fff;
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(REG_PA_SC_WINDOW_SCISSOR_TL);
+	*cmds++ = (unsigned int)((1U << 31) | (0 << 16) | 0);
+	*cmds++ = ((0x1fff) << 16) | 0x1fff;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(REG_PA_CL_VTE_CNTL);
+	/* disable X/Y/Z transforms, X/Y/Z are premultiplied by W */
+	*cmds++ = 0x00000b00;
+
+	/*load the viewport so that z scale = clear depth and z offset = 0.0f */
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(REG_PA_CL_VPORT_ZSCALE);
+	*cmds++ = 0xbf800000;
+	*cmds++ = 0x0;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(REG_RB_COLOR_MASK);
+	*cmds++ = 0x0000000f;	/* R = G = B = 1:enabled */
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(REG_RB_COLOR_DEST_MASK);
+	*cmds++ = 0xffffffff;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(REG_SQ_WRAPPING_0);
+	*cmds++ = 0x00000000;
+	*cmds++ = 0x00000000;
+
+	/* load the stencil ref value
+	 *  $AAM - do this later
+	 */
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(REG_RB_MODECONTROL);
+	/* draw pixels with color and depth/stencil component */
+	*cmds++ = 0x4;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(REG_PA_CL_CLIP_CNTL);
+	*cmds++ = 0x00010000;
+
+	if (adreno_is_a22x(adreno_dev)) {
+		*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+		*cmds++ = CP_REG(REG_A220_RB_LRZ_VSC_CONTROL);
+		*cmds++ = 0x0000000;
+
+		*cmds++ = cp_type3_packet(CP_DRAW_INDX, 3);
+		*cmds++ = 0;           /* viz query info. */
+		/* PrimType=RectList, SrcSel=AutoIndex, VisCullMode=Ignore*/
+		*cmds++ = 0x00004088;
+		*cmds++ = 3;	       /* NumIndices=3 */
+	} else {
+		/* queue the draw packet */
+		*cmds++ = cp_type3_packet(CP_DRAW_INDX, 2);
+		*cmds++ = 0;		/* viz query info. */
+		/* PrimType=RectList, NumIndices=3, SrcSel=AutoIndex */
+		*cmds++ = 0x00030088;
+	}
+
+	/* create indirect buffer command for above command sequence */
+	create_ib1(drawctxt, shadow->gmem_restore, start, cmds);
+
+	return cmds;
+}
+
+static void build_regrestore_cmds(struct adreno_device *adreno_dev,
+				  struct adreno_context *drawctxt)
+{
+	unsigned int *start = tmp_ctx.cmd;
+	unsigned int *cmd = start;
+
+	unsigned int i = 0;
+	unsigned int reg_array_size = 0;
+	const unsigned int *ptr_register_ranges;
+
+	*cmd++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
+	*cmd++ = 0;
+
+	/* H/W Registers */
+	/* deferred cp_type3_packet(CP_LOAD_CONSTANT_CONTEXT, ???); */
+	cmd++;
+#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES
+	/* Force mismatch */
+	*cmd++ = ((drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000) | 1;
+#else
+	*cmd++ = (drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000;
+#endif
+
+	/* Based on chip id choose the registers ranges*/
+	if (adreno_is_a220(adreno_dev)) {
+		ptr_register_ranges = register_ranges_a220;
+		reg_array_size = ARRAY_SIZE(register_ranges_a220);
+	} else if (adreno_is_a225(adreno_dev)) {
+		ptr_register_ranges = register_ranges_a225;
+		reg_array_size = ARRAY_SIZE(register_ranges_a225);
+	} else {
+		ptr_register_ranges = register_ranges_a20x;
+		reg_array_size = ARRAY_SIZE(register_ranges_a20x);
+	}
+
+
+	for (i = 0; i < (reg_array_size/2); i++) {
+		cmd = reg_range(cmd, ptr_register_ranges[i*2],
+				ptr_register_ranges[i*2+1]);
+	}
+
+	/* Now we know how many register blocks we have, we can compute command
+	 * length
+	 */
+	start[2] =
+	    cp_type3_packet(CP_LOAD_CONSTANT_CONTEXT, (cmd - start) - 3);
+	/* Enable shadowing for the entire register block. */
+#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES
+	start[4] |= (0 << 24) | (4 << 16);	/* Disable shadowing. */
+#else
+	start[4] |= (1 << 24) | (4 << 16);
+#endif
+
+	/* Need to handle some of the registers separately */
+	*cmd++ = cp_type0_packet(REG_SQ_GPR_MANAGEMENT, 1);
+	tmp_ctx.reg_values[0] = virt2gpu(cmd, &drawctxt->gpustate);
+	*cmd++ = 0x00040400;
+
+	*cmd++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
+	*cmd++ = 0;
+	*cmd++ = cp_type0_packet(REG_TP0_CHICKEN, 1);
+	tmp_ctx.reg_values[1] = virt2gpu(cmd, &drawctxt->gpustate);
+	*cmd++ = 0x00000000;
+
+	if (adreno_is_a22x(adreno_dev)) {
+		unsigned int i;
+		unsigned int j = 2;
+		for (i = REG_A220_VSC_BIN_SIZE; i <=
+				REG_A220_VSC_PIPE_DATA_LENGTH_7; i++) {
+			*cmd++ = cp_type0_packet(i, 1);
+			tmp_ctx.reg_values[j] = virt2gpu(cmd,
+				&drawctxt->gpustate);
+			*cmd++ = 0x00000000;
+			j++;
+		}
+	}
+
+	/* ALU Constants */
+	*cmd++ = cp_type3_packet(CP_LOAD_CONSTANT_CONTEXT, 3);
+	*cmd++ = drawctxt->gpustate.gpuaddr & 0xFFFFE000;
+#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES
+	*cmd++ = (0 << 24) | (0 << 16) | 0;	/* Disable shadowing */
+#else
+	*cmd++ = (1 << 24) | (0 << 16) | 0;
+#endif
+	*cmd++ = ALU_CONSTANTS;
+
+	/* Texture Constants */
+	*cmd++ = cp_type3_packet(CP_LOAD_CONSTANT_CONTEXT, 3);
+	*cmd++ = (drawctxt->gpustate.gpuaddr + TEX_OFFSET) & 0xFFFFE000;
+#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES
+	/* Disable shadowing */
+	*cmd++ = (0 << 24) | (1 << 16) | 0;
+#else
+	*cmd++ = (1 << 24) | (1 << 16) | 0;
+#endif
+	*cmd++ = TEX_CONSTANTS;
+
+	/* Boolean Constants */
+	*cmd++ = cp_type3_packet(CP_SET_CONSTANT, 1 + BOOL_CONSTANTS);
+	*cmd++ = (2 << 16) | 0;
+
+	/* the next BOOL_CONSTANT dwords is the shadow area for
+	 *  boolean constants.
+	 */
+	tmp_ctx.bool_shadow = virt2gpu(cmd, &drawctxt->gpustate);
+	cmd += BOOL_CONSTANTS;
+
+	/* Loop Constants */
+	*cmd++ = cp_type3_packet(CP_SET_CONSTANT, 1 + LOOP_CONSTANTS);
+	*cmd++ = (3 << 16) | 0;
+
+	/* the next LOOP_CONSTANTS dwords is the shadow area for
+	 * loop constants.
+	 */
+	tmp_ctx.loop_shadow = virt2gpu(cmd, &drawctxt->gpustate);
+	cmd += LOOP_CONSTANTS;
+
+	/* create indirect buffer command for above command sequence */
+	create_ib1(drawctxt, drawctxt->reg_restore, start, cmd);
+
+	tmp_ctx.cmd = cmd;
+}
+
+static void
+build_shader_save_restore_cmds(struct adreno_device *adreno_dev,
+				struct adreno_context *drawctxt)
+{
+	unsigned int *cmd = tmp_ctx.cmd;
+	unsigned int *save, *restore, *fixup;
+	unsigned int *startSizeVtx, *startSizePix, *startSizeShared;
+	unsigned int *partition1;
+	unsigned int *shaderBases, *partition2;
+
+	/* compute vertex, pixel and shared instruction shadow GPU addresses */
+	tmp_ctx.shader_vertex = drawctxt->gpustate.gpuaddr + SHADER_OFFSET;
+	tmp_ctx.shader_pixel = tmp_ctx.shader_vertex
+				+ _shader_shadow_size(adreno_dev);
+	tmp_ctx.shader_shared = tmp_ctx.shader_pixel
+				+  _shader_shadow_size(adreno_dev);
+
+	/* restore shader partitioning and instructions */
+
+	restore = cmd;		/* start address */
+
+	/* Invalidate Vertex & Pixel instruction code address and sizes */
+	*cmd++ = cp_type3_packet(CP_INVALIDATE_STATE, 1);
+	*cmd++ = 0x00000300;	/* 0x100 = Vertex, 0x200 = Pixel */
+
+	/* Restore previous shader vertex & pixel instruction bases. */
+	*cmd++ = cp_type3_packet(CP_SET_SHADER_BASES, 1);
+	shaderBases = cmd++;	/* TBD #5: shader bases (from fixup) */
+
+	/* write the shader partition information to a scratch register */
+	*cmd++ = cp_type0_packet(REG_SQ_INST_STORE_MANAGMENT, 1);
+	partition1 = cmd++;	/* TBD #4a: partition info (from save) */
+
+	/* load vertex shader instructions from the shadow. */
+	*cmd++ = cp_type3_packet(CP_IM_LOAD, 2);
+	*cmd++ = tmp_ctx.shader_vertex + 0x0;	/* 0x0 = Vertex */
+	startSizeVtx = cmd++;	/* TBD #1: start/size (from save) */
+
+	/* load pixel shader instructions from the shadow. */
+	*cmd++ = cp_type3_packet(CP_IM_LOAD, 2);
+	*cmd++ = tmp_ctx.shader_pixel + 0x1;	/* 0x1 = Pixel */
+	startSizePix = cmd++;	/* TBD #2: start/size (from save) */
+
+	/* load shared shader instructions from the shadow. */
+	*cmd++ = cp_type3_packet(CP_IM_LOAD, 2);
+	*cmd++ = tmp_ctx.shader_shared + 0x2;	/* 0x2 = Shared */
+	startSizeShared = cmd++;	/* TBD #3: start/size (from save) */
+
+	/* create indirect buffer command for above command sequence */
+	create_ib1(drawctxt, drawctxt->shader_restore, restore, cmd);
+
+	/*
+	 *  fixup SET_SHADER_BASES data
+	 *
+	 *  since self-modifying PM4 code is being used here, a seperate
+	 *  command buffer is used for this fixup operation, to ensure the
+	 *  commands are not read by the PM4 engine before the data fields
+	 *  have been written.
+	 */
+
+	fixup = cmd;		/* start address */
+
+	/* write the shader partition information to a scratch register */
+	*cmd++ = cp_type0_packet(REG_SCRATCH_REG2, 1);
+	partition2 = cmd++;	/* TBD #4b: partition info (from save) */
+
+	/* mask off unused bits, then OR with shader instruction memory size */
+	*cmd++ = cp_type3_packet(CP_REG_RMW, 3);
+	*cmd++ = REG_SCRATCH_REG2;
+	/* AND off invalid bits. */
+	*cmd++ = 0x0FFF0FFF;
+	/* OR in instruction memory size.  */
+	*cmd++ = adreno_encode_istore_size(adreno_dev);
+
+	/* write the computed value to the SET_SHADER_BASES data field */
+	*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+	*cmd++ = REG_SCRATCH_REG2;
+	/* TBD #5: shader bases (to restore) */
+	*cmd++ = virt2gpu(shaderBases, &drawctxt->gpustate);
+
+	/* create indirect buffer command for above command sequence */
+	create_ib1(drawctxt, drawctxt->shader_fixup, fixup, cmd);
+
+	/* save shader partitioning and instructions */
+
+	save = cmd;		/* start address */
+
+	*cmd++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
+	*cmd++ = 0;
+
+	/* fetch the SQ_INST_STORE_MANAGMENT register value,
+	 *  store the value in the data fields of the SET_CONSTANT commands
+	 *  above.
+	 */
+	*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+	*cmd++ = REG_SQ_INST_STORE_MANAGMENT;
+	/* TBD #4a: partition info (to restore) */
+	*cmd++ = virt2gpu(partition1, &drawctxt->gpustate);
+	*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+	*cmd++ = REG_SQ_INST_STORE_MANAGMENT;
+	/* TBD #4b: partition info (to fixup) */
+	*cmd++ = virt2gpu(partition2, &drawctxt->gpustate);
+
+
+	/* store the vertex shader instructions */
+	*cmd++ = cp_type3_packet(CP_IM_STORE, 2);
+	*cmd++ = tmp_ctx.shader_vertex + 0x0;	/* 0x0 = Vertex */
+	/* TBD #1: start/size (to restore) */
+	*cmd++ = virt2gpu(startSizeVtx, &drawctxt->gpustate);
+
+	/* store the pixel shader instructions */
+	*cmd++ = cp_type3_packet(CP_IM_STORE, 2);
+	*cmd++ = tmp_ctx.shader_pixel + 0x1;	/* 0x1 = Pixel */
+	/* TBD #2: start/size (to restore) */
+	*cmd++ = virt2gpu(startSizePix, &drawctxt->gpustate);
+
+	/* store the shared shader instructions if vertex base is nonzero */
+
+	*cmd++ = cp_type3_packet(CP_IM_STORE, 2);
+	*cmd++ = tmp_ctx.shader_shared + 0x2;	/* 0x2 = Shared */
+	/* TBD #3: start/size (to restore) */
+	*cmd++ = virt2gpu(startSizeShared, &drawctxt->gpustate);
+
+
+	*cmd++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
+	*cmd++ = 0;
+
+	/* create indirect buffer command for above command sequence */
+	create_ib1(drawctxt, drawctxt->shader_save, save, cmd);
+
+	tmp_ctx.cmd = cmd;
+}
+
+/* create buffers for saving/restoring registers, constants, & GMEM */
+static int a2xx_create_gpustate_shadow(struct adreno_device *adreno_dev,
+			struct adreno_context *drawctxt)
+{
+	drawctxt->flags |= CTXT_FLAGS_STATE_SHADOW;
+
+	/* build indirect command buffers to save & restore regs/constants */
+	build_regrestore_cmds(adreno_dev, drawctxt);
+	build_regsave_cmds(adreno_dev, drawctxt);
+
+	build_shader_save_restore_cmds(adreno_dev, drawctxt);
+
+	return 0;
+}
+
+/* create buffers for saving/restoring registers, constants, & GMEM */
+static int a2xx_create_gmem_shadow(struct adreno_device *adreno_dev,
+			struct adreno_context *drawctxt)
+{
+	int result;
+
+	calc_gmemsize(&drawctxt->context_gmem_shadow, adreno_dev->gmem_size);
+	tmp_ctx.gmem_base = adreno_dev->gmem_base;
+
+	result = kgsl_allocate(&drawctxt->context_gmem_shadow.gmemshadow,
+		drawctxt->pagetable, drawctxt->context_gmem_shadow.size);
+
+	if (result)
+		return result;
+
+	/* set the gmem shadow flag for the context */
+	drawctxt->flags |= CTXT_FLAGS_GMEM_SHADOW;
+
+	/* blank out gmem shadow. */
+	kgsl_sharedmem_set(&drawctxt->context_gmem_shadow.gmemshadow, 0, 0,
+			   drawctxt->context_gmem_shadow.size);
+
+	/* build quad vertex buffer */
+	build_quad_vtxbuff(drawctxt, &drawctxt->context_gmem_shadow,
+		&tmp_ctx.cmd);
+
+	/* build TP0_CHICKEN register restore command buffer */
+	if (!(drawctxt->flags & CTXT_FLAGS_PREAMBLE))
+		tmp_ctx.cmd = build_chicken_restore_cmds(drawctxt);
+
+	/* build indirect command buffers to save & restore gmem */
+	drawctxt->context_gmem_shadow.gmem_save_commands = tmp_ctx.cmd;
+	tmp_ctx.cmd =
+	    build_gmem2sys_cmds(adreno_dev, drawctxt,
+				&drawctxt->context_gmem_shadow);
+	drawctxt->context_gmem_shadow.gmem_restore_commands = tmp_ctx.cmd;
+	tmp_ctx.cmd =
+	    build_sys2gmem_cmds(adreno_dev, drawctxt,
+				&drawctxt->context_gmem_shadow);
+
+	kgsl_cache_range_op(&drawctxt->context_gmem_shadow.gmemshadow,
+			    KGSL_CACHE_OP_FLUSH);
+
+	kgsl_cffdump_syncmem(NULL,
+			&drawctxt->context_gmem_shadow.gmemshadow,
+			drawctxt->context_gmem_shadow.gmemshadow.gpuaddr,
+			drawctxt->context_gmem_shadow.gmemshadow.size, false);
+
+	return 0;
+}
+
+static int a2xx_drawctxt_create(struct adreno_device *adreno_dev,
+	struct adreno_context *drawctxt)
+{
+	int ret;
+
+	/*
+	 * Allocate memory for the GPU state and the context commands.
+	 * Despite the name, this is much more then just storage for
+	 * the gpustate.  This contains command space for gmem save
+	 * and texture and vertex buffer storage too
+	 */
+
+	ret = kgsl_allocate(&drawctxt->gpustate,
+		drawctxt->pagetable, _context_size(adreno_dev));
+
+	if (ret)
+		return ret;
+
+	kgsl_sharedmem_set(&drawctxt->gpustate, 0, 0,
+		_context_size(adreno_dev));
+
+	tmp_ctx.cmd = tmp_ctx.start
+	    = (unsigned int *)((char *)drawctxt->gpustate.hostptr + CMD_OFFSET);
+
+	if (!(drawctxt->flags & CTXT_FLAGS_PREAMBLE)) {
+		ret = a2xx_create_gpustate_shadow(adreno_dev, drawctxt);
+		if (ret)
+			goto done;
+
+		drawctxt->flags |= CTXT_FLAGS_SHADER_SAVE;
+	}
+
+	if (!(drawctxt->flags & CTXT_FLAGS_NOGMEMALLOC)) {
+		ret = a2xx_create_gmem_shadow(adreno_dev, drawctxt);
+		if (ret)
+			goto done;
+	}
+
+	/* Flush and sync the gpustate memory */
+
+	kgsl_cache_range_op(&drawctxt->gpustate,
+			    KGSL_CACHE_OP_FLUSH);
+
+	kgsl_cffdump_syncmem(NULL, &drawctxt->gpustate,
+			drawctxt->gpustate.gpuaddr,
+			drawctxt->gpustate.size, false);
+
+done:
+	if (ret)
+		kgsl_sharedmem_free(&drawctxt->gpustate);
+
+	return ret;
+}
+
+static void a2xx_drawctxt_save(struct adreno_device *adreno_dev,
+			struct adreno_context *context)
+{
+	struct kgsl_device *device = &adreno_dev->dev;
+	unsigned int cmd[22];
+
+	if (context == NULL)
+		return;
+
+	if (context->flags & CTXT_FLAGS_GPU_HANG)
+		KGSL_CTXT_WARN(device,
+			"Current active context has caused gpu hang\n");
+
+	if (!(context->flags & CTXT_FLAGS_PREAMBLE)) {
+
+		/* save registers and constants. */
+		adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE,
+			context->reg_save, 3);
+
+		if (context->flags & CTXT_FLAGS_SHADER_SAVE) {
+			/* save shader partitioning and instructions. */
+			adreno_ringbuffer_issuecmds(device,
+				KGSL_CMD_FLAGS_PMODE,
+				context->shader_save, 3);
+
+			/*
+			 * fixup shader partitioning parameter for
+			 *  SET_SHADER_BASES.
+			 */
+			adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE,
+				context->shader_fixup, 3);
+
+			context->flags |= CTXT_FLAGS_SHADER_RESTORE;
+		}
+	}
+
+	if ((context->flags & CTXT_FLAGS_GMEM_SAVE) &&
+	    (context->flags & CTXT_FLAGS_GMEM_SHADOW)) {
+		/* save gmem.
+		 * (note: changes shader. shader must already be saved.)
+		 */
+		adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_PMODE,
+			context->context_gmem_shadow.gmem_save, 3);
+
+		/* Restore TP0_CHICKEN */
+		if (!(context->flags & CTXT_FLAGS_PREAMBLE)) {
+			adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE,
+				context->chicken_restore, 3);
+		}
+
+		context->flags |= CTXT_FLAGS_GMEM_RESTORE;
+	} else if (adreno_is_a225(adreno_dev)) {
+		unsigned int *cmds = &cmd[0];
+		/*
+		 * Issue an empty draw call to avoid possible hangs due to
+		 * repeated idles without intervening draw calls.
+		 * On adreno 225 the PC block has a cache that is only
+		 * flushed on draw calls and repeated idles can make it
+		 * overflow. The gmem save path contains draw calls so
+		 * this workaround isn't needed there.
+		 */
+		*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+		*cmds++ = (0x4 << 16) | (REG_PA_SU_SC_MODE_CNTL - 0x2000);
+		*cmds++ = 0;
+		*cmds++ = cp_type3_packet(CP_DRAW_INDX, 5);
+		*cmds++ = 0;
+		*cmds++ = 1<<14;
+		*cmds++ = 0;
+		*cmds++ = device->mmu.setstate_memory.gpuaddr;
+		*cmds++ = 0;
+		*cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
+		*cmds++ = 0x00000000;
+
+		adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_PMODE,
+					    &cmd[0], 11);
+	}
+}
+
+static void a2xx_drawctxt_restore(struct adreno_device *adreno_dev,
+			struct adreno_context *context)
+{
+	struct kgsl_device *device = &adreno_dev->dev;
+	unsigned int cmds[5];
+
+	if (context == NULL) {
+		/* No context - set the default apgetable and thats it */
+		kgsl_mmu_setstate(&device->mmu, device->mmu.defaultpagetable);
+		return;
+	}
+
+	KGSL_CTXT_INFO(device, "context flags %08x\n", context->flags);
+
+	cmds[0] = cp_nop_packet(1);
+	cmds[1] = KGSL_CONTEXT_TO_MEM_IDENTIFIER;
+	cmds[2] = cp_type3_packet(CP_MEM_WRITE, 2);
+	cmds[3] = device->memstore.gpuaddr +
+		KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL, current_context);
+	cmds[4] = context->id;
+	adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE, cmds, 5);
+	kgsl_mmu_setstate(&device->mmu, context->pagetable);
+
+#ifndef CONFIG_MSM_KGSL_CFF_DUMP_NO_CONTEXT_MEM_DUMP
+	kgsl_cffdump_syncmem(NULL, &context->gpustate,
+		context->gpustate.gpuaddr, LCC_SHADOW_SIZE +
+		REG_SHADOW_SIZE + CMD_BUFFER_SIZE + TEX_SHADOW_SIZE, false);
+#endif
+
+	/* restore gmem.
+	 *  (note: changes shader. shader must not already be restored.)
+	 */
+	if (context->flags & CTXT_FLAGS_GMEM_RESTORE) {
+		adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_PMODE,
+			context->context_gmem_shadow.gmem_restore, 3);
+
+		if (!(context->flags & CTXT_FLAGS_PREAMBLE)) {
+			/* Restore TP0_CHICKEN */
+			adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE,
+				context->chicken_restore, 3);
+		}
+
+		context->flags &= ~CTXT_FLAGS_GMEM_RESTORE;
+	}
+
+	if (!(context->flags & CTXT_FLAGS_PREAMBLE)) {
+
+		/* restore registers and constants. */
+		adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE,
+			context->reg_restore, 3);
+
+		/* restore shader instructions & partitioning. */
+		if (context->flags & CTXT_FLAGS_SHADER_RESTORE) {
+			adreno_ringbuffer_issuecmds(device,
+				KGSL_CMD_FLAGS_NONE,
+				context->shader_restore, 3);
+		}
+	}
+
+	if (adreno_is_a20x(adreno_dev)) {
+		cmds[0] = cp_type3_packet(CP_SET_BIN_BASE_OFFSET, 1);
+		cmds[1] = context->bin_base_offset;
+		adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE,
+			cmds, 2);
+	}
+}
+
+/*
+ * Interrupt management
+ *
+ * a2xx interrupt control is distributed among the various
+ * hardware components (RB, CP, MMU).  The main interrupt
+ * tells us which component fired the interrupt, but one needs
+ * to go to the individual component to find out why.  The
+ * following functions provide the broken out support for
+ * managing the interrupts
+ */
+
+#define RBBM_INT_MASK RBBM_INT_CNTL__RDERR_INT_MASK
+
+#define CP_INT_MASK \
+	(CP_INT_CNTL__T0_PACKET_IN_IB_MASK | \
+	CP_INT_CNTL__OPCODE_ERROR_MASK | \
+	CP_INT_CNTL__PROTECTED_MODE_ERROR_MASK | \
+	CP_INT_CNTL__RESERVED_BIT_ERROR_MASK | \
+	CP_INT_CNTL__IB_ERROR_MASK | \
+	CP_INT_CNTL__IB1_INT_MASK | \
+	CP_INT_CNTL__RB_INT_MASK)
+
+#define VALID_STATUS_COUNT_MAX	10
+
+static struct {
+	unsigned int mask;
+	const char *message;
+} kgsl_cp_error_irqs[] = {
+	{ CP_INT_CNTL__T0_PACKET_IN_IB_MASK,
+		"ringbuffer TO packet in IB interrupt" },
+	{ CP_INT_CNTL__OPCODE_ERROR_MASK,
+		"ringbuffer opcode error interrupt" },
+	{ CP_INT_CNTL__PROTECTED_MODE_ERROR_MASK,
+		"ringbuffer protected mode error interrupt" },
+	{ CP_INT_CNTL__RESERVED_BIT_ERROR_MASK,
+		"ringbuffer reserved bit error interrupt" },
+	{ CP_INT_CNTL__IB_ERROR_MASK,
+		"ringbuffer IB error interrupt" },
+};
+
+static void a2xx_cp_intrcallback(struct kgsl_device *device)
+{
+	unsigned int status = 0, num_reads = 0, master_status = 0;
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
+	int i;
+
+	adreno_regread(device, REG_MASTER_INT_SIGNAL, &master_status);
+	while (!status && (num_reads < VALID_STATUS_COUNT_MAX) &&
+		(master_status & MASTER_INT_SIGNAL__CP_INT_STAT)) {
+		adreno_regread(device, REG_CP_INT_STATUS, &status);
+		adreno_regread(device, REG_MASTER_INT_SIGNAL,
+					&master_status);
+		num_reads++;
+	}
+	if (num_reads > 1)
+		KGSL_DRV_WARN(device,
+			"Looped %d times to read REG_CP_INT_STATUS\n",
+			num_reads);
+
+	trace_kgsl_a2xx_irq_status(device, master_status, status);
+
+	if (!status) {
+		if (master_status & MASTER_INT_SIGNAL__CP_INT_STAT) {
+			/* This indicates that we could not read CP_INT_STAT.
+			 * As a precaution just wake up processes so
+			 * they can check their timestamps. Since, we
+			 * did not ack any interrupts this interrupt will
+			 * be generated again */
+			KGSL_DRV_WARN(device, "Unable to read CP_INT_STATUS\n");
+			wake_up_interruptible_all(&device->wait_queue);
+		} else
+			KGSL_DRV_WARN(device, "Spurious interrput detected\n");
+		return;
+	}
+
+	if (status & CP_INT_CNTL__RB_INT_MASK) {
+		/* signal intr completion event */
+		unsigned int context_id;
+		kgsl_sharedmem_readl(&device->memstore,
+				&context_id,
+				KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
+					current_context));
+		if (context_id < KGSL_MEMSTORE_MAX) {
+			kgsl_sharedmem_writel(&rb->device->memstore,
+					KGSL_MEMSTORE_OFFSET(context_id,
+						ts_cmp_enable), 0);
+			device->last_expired_ctxt_id = context_id;
+			wmb();
+		}
+		KGSL_CMD_WARN(rb->device, "ringbuffer rb interrupt\n");
+	}
+
+	for (i = 0; i < ARRAY_SIZE(kgsl_cp_error_irqs); i++) {
+		if (status & kgsl_cp_error_irqs[i].mask) {
+			KGSL_CMD_CRIT(rb->device, "%s\n",
+				 kgsl_cp_error_irqs[i].message);
+			/*
+			 * on fatal errors, turn off the interrupts to
+			 * avoid storming. This has the side effect of
+			 * forcing a PM dump when the timestamp times out
+			 */
+
+			kgsl_pwrctrl_irq(rb->device, KGSL_PWRFLAGS_OFF);
+		}
+	}
+
+	/* only ack bits we understand */
+	status &= CP_INT_MASK;
+	adreno_regwrite(device, REG_CP_INT_ACK, status);
+
+	if (status & (CP_INT_CNTL__IB1_INT_MASK | CP_INT_CNTL__RB_INT_MASK)) {
+		KGSL_CMD_WARN(rb->device, "ringbuffer ib1/rb interrupt\n");
+		queue_work(device->work_queue, &device->ts_expired_ws);
+		wake_up_interruptible_all(&device->wait_queue);
+		atomic_notifier_call_chain(&(device->ts_notifier_list),
+					   device->id,
+					   NULL);
+	}
+}
+
+static void a2xx_rbbm_intrcallback(struct kgsl_device *device)
+{
+	unsigned int status = 0;
+	unsigned int rderr = 0;
+	unsigned int addr = 0;
+	const char *source;
+
+	adreno_regread(device, REG_RBBM_INT_STATUS, &status);
+
+	if (status & RBBM_INT_CNTL__RDERR_INT_MASK) {
+		adreno_regread(device, REG_RBBM_READ_ERROR, &rderr);
+		source = (rderr & RBBM_READ_ERROR_REQUESTER)
+			 ? "host" : "cp";
+		/* convert to dword address */
+		addr = (rderr & RBBM_READ_ERROR_ADDRESS_MASK) >> 2;
+
+		/*
+		 * Log CP_INT_STATUS interrupts from the CP at a
+		 * lower level because they can happen frequently
+		 * and are worked around in a2xx_irq_handler.
+		 */
+		if (addr == REG_CP_INT_STATUS &&
+			rderr & RBBM_READ_ERROR_ERROR &&
+			rderr & RBBM_READ_ERROR_REQUESTER)
+			KGSL_DRV_WARN(device,
+				"rbbm read error interrupt: %s reg: %04X\n",
+				source, addr);
+		else
+			KGSL_DRV_CRIT(device,
+				"rbbm read error interrupt: %s reg: %04X\n",
+				source, addr);
+	}
+
+	status &= RBBM_INT_MASK;
+	adreno_regwrite(device, REG_RBBM_INT_ACK, status);
+}
+
+irqreturn_t a2xx_irq_handler(struct adreno_device *adreno_dev)
+{
+	struct kgsl_device *device = &adreno_dev->dev;
+	irqreturn_t result = IRQ_NONE;
+	unsigned int status;
+
+	adreno_regread(device, REG_MASTER_INT_SIGNAL, &status);
+
+	if (status & MASTER_INT_SIGNAL__MH_INT_STAT) {
+		kgsl_mh_intrcallback(device);
+		result = IRQ_HANDLED;
+	}
+
+	if (status & MASTER_INT_SIGNAL__CP_INT_STAT) {
+		a2xx_cp_intrcallback(device);
+		result = IRQ_HANDLED;
+	}
+
+	if (status & MASTER_INT_SIGNAL__RBBM_INT_STAT) {
+		a2xx_rbbm_intrcallback(device);
+		result = IRQ_HANDLED;
+	}
+
+	return result;
+}
+
+static void a2xx_irq_control(struct adreno_device *adreno_dev, int state)
+{
+	struct kgsl_device *device = &adreno_dev->dev;
+
+	if (state) {
+		adreno_regwrite(device, REG_RBBM_INT_CNTL, RBBM_INT_MASK);
+		adreno_regwrite(device, REG_CP_INT_CNTL, CP_INT_MASK);
+		adreno_regwrite(device, MH_INTERRUPT_MASK, KGSL_MMU_INT_MASK);
+	} else {
+		adreno_regwrite(device, REG_RBBM_INT_CNTL, 0);
+		adreno_regwrite(device, REG_CP_INT_CNTL, 0);
+		adreno_regwrite(device, MH_INTERRUPT_MASK, 0);
+	}
+
+	/* Force the writes to post before touching the IRQ line */
+	wmb();
+}
+
+static void a2xx_rb_init(struct adreno_device *adreno_dev,
+			struct adreno_ringbuffer *rb)
+{
+	unsigned int *cmds, cmds_gpu;
+
+	/* ME_INIT */
+	cmds = adreno_ringbuffer_allocspace(rb, 19);
+	cmds_gpu = rb->buffer_desc.gpuaddr + sizeof(uint)*(rb->wptr-19);
+
+	GSL_RB_WRITE(cmds, cmds_gpu, cp_type3_packet(CP_ME_INIT, 18));
+	/* All fields present (bits 9:0) */
+	GSL_RB_WRITE(cmds, cmds_gpu, 0x000003ff);
+	/* Disable/Enable Real-Time Stream processing (present but ignored) */
+	GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000);
+	/* Enable (2D <-> 3D) implicit synchronization (present but ignored) */
+	GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000);
+
+	GSL_RB_WRITE(cmds, cmds_gpu,
+		SUBBLOCK_OFFSET(REG_RB_SURFACE_INFO));
+	GSL_RB_WRITE(cmds, cmds_gpu,
+		SUBBLOCK_OFFSET(REG_PA_SC_WINDOW_OFFSET));
+	GSL_RB_WRITE(cmds, cmds_gpu,
+		SUBBLOCK_OFFSET(REG_VGT_MAX_VTX_INDX));
+	GSL_RB_WRITE(cmds, cmds_gpu,
+		SUBBLOCK_OFFSET(REG_SQ_PROGRAM_CNTL));
+	GSL_RB_WRITE(cmds, cmds_gpu,
+		SUBBLOCK_OFFSET(REG_RB_DEPTHCONTROL));
+	GSL_RB_WRITE(cmds, cmds_gpu,
+		SUBBLOCK_OFFSET(REG_PA_SU_POINT_SIZE));
+	GSL_RB_WRITE(cmds, cmds_gpu,
+		SUBBLOCK_OFFSET(REG_PA_SC_LINE_CNTL));
+	GSL_RB_WRITE(cmds, cmds_gpu,
+		SUBBLOCK_OFFSET(REG_PA_SU_POLY_OFFSET_FRONT_SCALE));
+
+	/* Instruction memory size: */
+	GSL_RB_WRITE(cmds, cmds_gpu,
+		(adreno_encode_istore_size(adreno_dev)
+		| adreno_dev->pix_shader_start));
+	/* Maximum Contexts */
+	GSL_RB_WRITE(cmds, cmds_gpu, 0x00000001);
+	/* Write Confirm Interval and The CP will wait the
+	* wait_interval * 16 clocks between polling  */
+	GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000);
+
+	/* NQ and External Memory Swap */
+	GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000);
+	/* Protected mode error checking
+	 * If iommu is used then protection needs to be turned off
+	 * to enable context bank switching */
+	if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_get_mmutype())
+		GSL_RB_WRITE(cmds, cmds_gpu, 0);
+	else
+		GSL_RB_WRITE(cmds, cmds_gpu, GSL_RB_PROTECTED_MODE_CONTROL);
+	/* Disable header dumping and Header dump address */
+	GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000);
+	/* Header dump size */
+	GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000);
+
+	adreno_ringbuffer_submit(rb);
+}
+
+static unsigned int a2xx_busy_cycles(struct adreno_device *adreno_dev)
+{
+	struct kgsl_device *device = &adreno_dev->dev;
+	unsigned int reg, val;
+
+	/* Freeze the counter */
+	adreno_regwrite(device, REG_CP_PERFMON_CNTL,
+		REG_PERF_MODE_CNT | REG_PERF_STATE_FREEZE);
+
+	/* Get the value */
+	adreno_regread(device, REG_RBBM_PERFCOUNTER1_LO, &val);
+
+	/* Reset the counter */
+	adreno_regwrite(device, REG_CP_PERFMON_CNTL,
+		REG_PERF_MODE_CNT | REG_PERF_STATE_RESET);
+
+	/* Re-Enable the performance monitors */
+	adreno_regread(device, REG_RBBM_PM_OVERRIDE2, &reg);
+	adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, (reg | 0x40));
+	adreno_regwrite(device, REG_RBBM_PERFCOUNTER1_SELECT, 0x1);
+	adreno_regwrite(device, REG_CP_PERFMON_CNTL,
+		REG_PERF_MODE_CNT | REG_PERF_STATE_ENABLE);
+
+	return val;
+}
+
+static void a2xx_gmeminit(struct adreno_device *adreno_dev)
+{
+	struct kgsl_device *device = &adreno_dev->dev;
+	union reg_rb_edram_info rb_edram_info;
+	unsigned int gmem_size;
+	unsigned int edram_value = 0;
+
+	/* get edram_size value equivalent */
+	gmem_size = (adreno_dev->gmem_size >> 14);
+	while (gmem_size >>= 1)
+		edram_value++;
+
+	rb_edram_info.val = 0;
+
+	rb_edram_info.f.edram_size = edram_value;
+	rb_edram_info.f.edram_mapping_mode = 0; /* EDRAM_MAP_UPPER */
+
+	/* must be aligned to size */
+	rb_edram_info.f.edram_range = (adreno_dev->gmem_base >> 14);
+
+	adreno_regwrite(device, REG_RB_EDRAM_INFO, rb_edram_info.val);
+}
+
+static void a2xx_start(struct adreno_device *adreno_dev)
+{
+	struct kgsl_device *device = &adreno_dev->dev;
+
+	/*
+	 * We need to make sure all blocks are powered up and clocked
+	 * before issuing a soft reset.  The overrides will then be
+	 * turned off (set to 0)
+	 */
+	adreno_regwrite(device, REG_RBBM_PM_OVERRIDE1, 0xfffffffe);
+	adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0xffffffff);
+
+	/*
+	 * Only reset CP block if all blocks have previously been
+	 * reset
+	 */
+	if (!(device->flags & KGSL_FLAGS_SOFT_RESET) ||
+		!adreno_is_a22x(adreno_dev)) {
+		adreno_regwrite(device, REG_RBBM_SOFT_RESET,
+			0xFFFFFFFF);
+		device->flags |= KGSL_FLAGS_SOFT_RESET;
+	} else {
+		adreno_regwrite(device, REG_RBBM_SOFT_RESET,
+			0x00000001);
+	}
+	/*
+	 * The core is in an indeterminate state until the reset
+	 * completes after 30ms.
+	 */
+	msleep(30);
+
+	adreno_regwrite(device, REG_RBBM_SOFT_RESET, 0x00000000);
+
+	if (adreno_is_a225(adreno_dev)) {
+		/* Enable large instruction store for A225 */
+		adreno_regwrite(device, REG_SQ_FLOW_CONTROL,
+			0x18000000);
+	}
+
+	adreno_regwrite(device, REG_RBBM_CNTL, 0x00004442);
+
+	adreno_regwrite(device, REG_SQ_VS_PROGRAM, 0x00000000);
+	adreno_regwrite(device, REG_SQ_PS_PROGRAM, 0x00000000);
+
+	if (cpu_is_msm8960())
+		adreno_regwrite(device, REG_RBBM_PM_OVERRIDE1, 0x200);
+	else
+		adreno_regwrite(device, REG_RBBM_PM_OVERRIDE1, 0);
+
+	if (!adreno_is_a22x(adreno_dev))
+		adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0);
+	else
+		adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0x80);
+
+	adreno_regwrite(device, REG_RBBM_DEBUG, 0x00080000);
+
+	/* Make sure interrupts are disabled */
+	adreno_regwrite(device, REG_RBBM_INT_CNTL, 0);
+	adreno_regwrite(device, REG_CP_INT_CNTL, 0);
+	adreno_regwrite(device, REG_SQ_INT_CNTL, 0);
+
+	a2xx_gmeminit(adreno_dev);
+}
+
+/* Defined in adreno_a2xx_snapshot.c */
+void *a2xx_snapshot(struct adreno_device *adreno_dev, void *snapshot,
+	int *remain, int hang);
+
+struct adreno_gpudev adreno_a2xx_gpudev = {
+	.reg_rbbm_status = REG_RBBM_STATUS,
+	.reg_cp_pfp_ucode_addr = REG_CP_PFP_UCODE_ADDR,
+	.reg_cp_pfp_ucode_data = REG_CP_PFP_UCODE_DATA,
+
+	.ctxt_create = a2xx_drawctxt_create,
+	.ctxt_save = a2xx_drawctxt_save,
+	.ctxt_restore = a2xx_drawctxt_restore,
+	.irq_handler = a2xx_irq_handler,
+	.irq_control = a2xx_irq_control,
+	.snapshot = a2xx_snapshot,
+	.rb_init = a2xx_rb_init,
+	.busy_cycles = a2xx_busy_cycles,
+	.start = a2xx_start,
+};
diff --git a/drivers/gpu/msm/adreno_a2xx_snapshot.c b/drivers/gpu/msm/adreno_a2xx_snapshot.c
new file mode 100644
index 0000000..2368264
--- /dev/null
+++ b/drivers/gpu/msm/adreno_a2xx_snapshot.c
@@ -0,0 +1,341 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "kgsl.h"
+#include "adreno.h"
+#include "kgsl_snapshot.h"
+
+#define DEBUG_SECTION_SZ(_dwords) (((_dwords) * sizeof(unsigned int)) \
+		+ sizeof(struct kgsl_snapshot_debug))
+
+/* Dump the SX debug registers into a GPU snapshot debug section */
+
+#define SXDEBUG_COUNT 0x1B
+
+static int a2xx_snapshot_sxdebug(struct kgsl_device *device, void *snapshot,
+	int remain, void *priv)
+{
+	struct kgsl_snapshot_debug *header = snapshot;
+	unsigned int *data = snapshot + sizeof(*header);
+	int i;
+
+	if (remain < DEBUG_SECTION_SZ(SXDEBUG_COUNT)) {
+		SNAPSHOT_ERR_NOMEM(device, "SX DEBUG");
+		return 0;
+	}
+
+	header->type = SNAPSHOT_DEBUG_SX;
+	header->size = SXDEBUG_COUNT;
+
+	for (i = 0; i < SXDEBUG_COUNT; i++) {
+		adreno_regwrite(device, REG_RBBM_DEBUG_CNTL, 0x1B00 | i);
+		adreno_regread(device, REG_RBBM_DEBUG_OUT, &data[i]);
+	}
+
+	adreno_regwrite(device, REG_RBBM_DEBUG_CNTL, 0);
+
+	return DEBUG_SECTION_SZ(SXDEBUG_COUNT);
+}
+
+#define CPDEBUG_COUNT 0x20
+
+static int a2xx_snapshot_cpdebug(struct kgsl_device *device, void *snapshot,
+	int remain, void *priv)
+{
+	struct kgsl_snapshot_debug *header = snapshot;
+	unsigned int *data = snapshot + sizeof(*header);
+	int i;
+
+	if (remain < DEBUG_SECTION_SZ(CPDEBUG_COUNT)) {
+		SNAPSHOT_ERR_NOMEM(device, "CP DEBUG");
+		return 0;
+	}
+
+	header->type = SNAPSHOT_DEBUG_CP;
+	header->size = CPDEBUG_COUNT;
+
+	for (i = 0; i < CPDEBUG_COUNT; i++) {
+		adreno_regwrite(device, REG_RBBM_DEBUG_CNTL, 0x1628);
+		adreno_regread(device, REG_RBBM_DEBUG_OUT, &data[i]);
+	}
+
+	adreno_regwrite(device, REG_RBBM_DEBUG_CNTL, 0);
+
+	return DEBUG_SECTION_SZ(CPDEBUG_COUNT);
+}
+
+/*
+ * The contents of the SQ debug sections are dword pairs:
+ * [register offset]:[value]
+ * This macro writes both dwords for the given register
+ */
+
+#define SQ_DEBUG_WRITE(_device, _reg, _data, _offset) \
+	do { _data[(_offset)++] = (_reg); \
+	adreno_regread(_device, (_reg), &_data[(_offset)++]); } while (0)
+
+#define SQ_DEBUG_BANK_SIZE 23
+
+static int a2xx_snapshot_sqdebug(struct kgsl_device *device, void *snapshot,
+	int remain, void *priv)
+{
+	struct kgsl_snapshot_debug *header = snapshot;
+	unsigned int *data = snapshot + sizeof(*header);
+	int i, offset = 0;
+	int size = SQ_DEBUG_BANK_SIZE * 2 * 2;
+
+	if (remain < DEBUG_SECTION_SZ(size)) {
+		SNAPSHOT_ERR_NOMEM(device, "SQ Debug");
+		return 0;
+	}
+
+	header->type = SNAPSHOT_DEBUG_SQ;
+	header->size = size;
+
+	for (i = 0; i < 2; i++) {
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_CONST_MGR_FSM+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_EXP_ALLOC+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_FSM_ALU_0+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_FSM_ALU_1+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_GPR_PIX+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_GPR_VTX+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_INPUT_FSM+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_MISC+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_MISC_0+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_MISC_1+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_PIX_TB_0+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_PIX_TB_STATE_MEM+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device,
+			REG_SQ_DEBUG_PIX_TB_STATUS_REG_0+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device,
+			REG_SQ_DEBUG_PIX_TB_STATUS_REG_1+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device,
+			REG_SQ_DEBUG_PIX_TB_STATUS_REG_2+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device,
+			REG_SQ_DEBUG_PIX_TB_STATUS_REG_3+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_PTR_BUFF+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_TB_STATUS_SEL+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_TP_FSM+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_VTX_TB_0+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_VTX_TB_1+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_VTX_TB_STATE_MEM+i*0x1000,
+			data, offset);
+	}
+
+	return DEBUG_SECTION_SZ(size);
+}
+
+#define SQ_DEBUG_THREAD_SIZE 7
+
+static int a2xx_snapshot_sqthreaddebug(struct kgsl_device *device,
+	void *snapshot, int remain, void *priv)
+{
+	struct kgsl_snapshot_debug *header = snapshot;
+	unsigned int *data = snapshot + sizeof(*header);
+	int i, offset = 0;
+	int size = SQ_DEBUG_THREAD_SIZE * 2 * 16;
+
+	if (remain < DEBUG_SECTION_SZ(size)) {
+		SNAPSHOT_ERR_NOMEM(device, "SQ THREAD DEBUG");
+		return 0;
+	}
+
+	header->type = SNAPSHOT_DEBUG_SQTHREAD;
+	header->size = size;
+
+	for (i = 0; i < 16; i++) {
+		adreno_regwrite(device, REG_SQ_DEBUG_TB_STATUS_SEL,
+				i | (6<<4) | (i<<7) | (1<<11) | (1<<12)
+				| (i<<16) | (6<<20) | (i<<23));
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_VTX_TB_STATE_MEM,
+			 data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_VTX_TB_STATUS_REG,
+			 data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_PIX_TB_STATE_MEM,
+			 data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_PIX_TB_STATUS_REG_0,
+			 data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_PIX_TB_STATUS_REG_1,
+			 data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_PIX_TB_STATUS_REG_2,
+			 data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_PIX_TB_STATUS_REG_3,
+			 data, offset);
+	}
+
+	return DEBUG_SECTION_SZ(size);
+}
+
+#define MIUDEBUG_COUNT 0x10
+
+static int a2xx_snapshot_miudebug(struct kgsl_device *device, void *snapshot,
+	int remain, void *priv)
+{
+	struct kgsl_snapshot_debug *header = snapshot;
+	unsigned int *data = snapshot + sizeof(*header);
+	int i;
+
+	if (remain < DEBUG_SECTION_SZ(MIUDEBUG_COUNT)) {
+		SNAPSHOT_ERR_NOMEM(device, "MIU DEBUG");
+		return 0;
+	}
+
+	header->type = SNAPSHOT_DEBUG_MIU;
+	header->size = MIUDEBUG_COUNT;
+
+	for (i = 0; i < MIUDEBUG_COUNT; i++) {
+		adreno_regwrite(device, REG_RBBM_DEBUG_CNTL, 0x1600 | i);
+		adreno_regread(device, REG_RBBM_DEBUG_OUT, &data[i]);
+	}
+
+	adreno_regwrite(device, REG_RBBM_DEBUG_CNTL, 0);
+
+	return DEBUG_SECTION_SZ(MIUDEBUG_COUNT);
+}
+
+/* A2XX GPU snapshot function - this is where all of the A2XX specific
+ * bits and pieces are grabbed into the snapshot memory
+ */
+
+void *a2xx_snapshot(struct adreno_device *adreno_dev, void *snapshot,
+	int *remain, int hang)
+{
+	struct kgsl_device *device = &adreno_dev->dev;
+	struct kgsl_snapshot_registers regs;
+	unsigned int pmoverride;
+
+	/* Choose the register set to dump */
+
+	if (adreno_is_a20x(adreno_dev)) {
+		regs.regs = (unsigned int *) a200_registers;
+		regs.count = a200_registers_count;
+	} else if (adreno_is_a220(adreno_dev)) {
+		regs.regs = (unsigned int *) a220_registers;
+		regs.count = a220_registers_count;
+	} else if (adreno_is_a225(adreno_dev)) {
+		regs.regs = (unsigned int *) a225_registers;
+		regs.count = a225_registers_count;
+	}
+
+	/* Master set of (non debug) registers */
+	snapshot = kgsl_snapshot_add_section(device,
+		KGSL_SNAPSHOT_SECTION_REGS, snapshot, remain,
+		kgsl_snapshot_dump_regs, &regs);
+
+	/* CP_STATE_DEBUG indexed registers */
+	snapshot = kgsl_snapshot_indexed_registers(device, snapshot,
+			remain, REG_CP_STATE_DEBUG_INDEX,
+			REG_CP_STATE_DEBUG_DATA, 0x0, 0x14);
+
+	/* CP_ME indexed registers */
+	snapshot = kgsl_snapshot_indexed_registers(device, snapshot,
+			remain, REG_CP_ME_CNTL, REG_CP_ME_STATUS,
+			64, 44);
+
+	/*
+	 * Need to temporarily turn off clock gating for the debug bus to
+	 * work
+	 */
+
+	adreno_regread(device, REG_RBBM_PM_OVERRIDE2, &pmoverride);
+	adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0xFF);
+
+	/* SX debug registers */
+	snapshot = kgsl_snapshot_add_section(device,
+			KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
+			a2xx_snapshot_sxdebug, NULL);
+
+	/* SU debug indexed registers (only for < 470) */
+	if (!adreno_is_a22x(adreno_dev))
+		snapshot = kgsl_snapshot_indexed_registers(device, snapshot,
+				remain, REG_PA_SU_DEBUG_CNTL,
+				REG_PA_SU_DEBUG_DATA,
+				0, 0x1B);
+
+	/* CP debug registers */
+	snapshot = kgsl_snapshot_add_section(device,
+			KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
+			a2xx_snapshot_cpdebug, NULL);
+
+	/* MH debug indexed registers */
+	snapshot = kgsl_snapshot_indexed_registers(device, snapshot,
+			remain, MH_DEBUG_CTRL, MH_DEBUG_DATA, 0x0, 0x40);
+
+	/* Leia only register sets */
+	if (adreno_is_a22x(adreno_dev)) {
+		/* RB DEBUG indexed regisers */
+		snapshot = kgsl_snapshot_indexed_registers(device, snapshot,
+			remain, REG_RB_DEBUG_CNTL, REG_RB_DEBUG_DATA, 0, 8);
+
+		/* RB DEBUG indexed registers bank 2 */
+		snapshot = kgsl_snapshot_indexed_registers(device, snapshot,
+			remain, REG_RB_DEBUG_CNTL, REG_RB_DEBUG_DATA + 0x1000,
+			0, 8);
+
+		/* PC_DEBUG indexed registers */
+		snapshot = kgsl_snapshot_indexed_registers(device, snapshot,
+			remain, REG_PC_DEBUG_CNTL, REG_PC_DEBUG_DATA, 0, 8);
+
+		/* GRAS_DEBUG indexed registers */
+		snapshot = kgsl_snapshot_indexed_registers(device, snapshot,
+			remain, REG_GRAS_DEBUG_CNTL, REG_GRAS_DEBUG_DATA, 0, 4);
+
+		/* MIU debug registers */
+		snapshot = kgsl_snapshot_add_section(device,
+			KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
+			a2xx_snapshot_miudebug, NULL);
+
+		/* SQ DEBUG debug registers */
+		snapshot = kgsl_snapshot_add_section(device,
+			KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
+			a2xx_snapshot_sqdebug, NULL);
+
+		/*
+		 * Reading SQ THREAD causes bad things to happen on a running
+		 * system, so only read it if the GPU is already hung
+		 */
+
+		if (hang) {
+			/* SQ THREAD debug registers */
+			snapshot = kgsl_snapshot_add_section(device,
+				KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
+				a2xx_snapshot_sqthreaddebug, NULL);
+		}
+	}
+
+	/* Reset the clock gating */
+	adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, pmoverride);
+
+	return snapshot;
+}
diff --git a/drivers/gpu/msm/adreno_a2xx_trace.c b/drivers/gpu/msm/adreno_a2xx_trace.c
new file mode 100644
index 0000000..c91d1a0
--- /dev/null
+++ b/drivers/gpu/msm/adreno_a2xx_trace.c
@@ -0,0 +1,19 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "kgsl.h"
+#include "adreno.h"
+
+/* Instantiate tracepoints */
+#define CREATE_TRACE_POINTS
+#include "adreno_a2xx_trace.h"
diff --git a/drivers/gpu/msm/adreno_a2xx_trace.h b/drivers/gpu/msm/adreno_a2xx_trace.h
new file mode 100644
index 0000000..2528e15
--- /dev/null
+++ b/drivers/gpu/msm/adreno_a2xx_trace.h
@@ -0,0 +1,78 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#if !defined(_ADRENO_A2XX_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _ADRENO_A2XX_TRACE_H
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM kgsl
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE adreno_a2xx_trace
+
+#include <linux/tracepoint.h>
+
+struct kgsl_device;
+
+/*
+ * Tracepoint for a2xx irq. Includes status info
+ */
+TRACE_EVENT(kgsl_a2xx_irq_status,
+
+	TP_PROTO(struct kgsl_device *device, unsigned int master_status,
+		 unsigned int status),
+
+	TP_ARGS(device, master_status, status),
+
+	TP_STRUCT__entry(
+		__string(device_name, device->name)
+		__field(unsigned int, master_status)
+		__field(unsigned int, status)
+	),
+
+	TP_fast_assign(
+		__assign_str(device_name, device->name);
+		__entry->master_status = master_status;
+		__entry->status = status;
+	),
+
+	TP_printk(
+		"d_name=%s master=%s status=%s",
+		__get_str(device_name),
+		__entry->master_status ? __print_flags(__entry->master_status,
+			"|",
+			{ MASTER_INT_SIGNAL__MH_INT_STAT, "MH" },
+			{ MASTER_INT_SIGNAL__SQ_INT_STAT, "SQ" },
+			{ MASTER_INT_SIGNAL__CP_INT_STAT, "CP" },
+			{ MASTER_INT_SIGNAL__RBBM_INT_STAT, "RBBM" }) : "None",
+		__entry->status ? __print_flags(__entry->status, "|",
+			{ CP_INT_CNTL__SW_INT_MASK, "SW" },
+			{ CP_INT_CNTL__T0_PACKET_IN_IB_MASK,
+				"T0_PACKET_IN_IB" },
+			{ CP_INT_CNTL__OPCODE_ERROR_MASK, "OPCODE_ERROR" },
+			{ CP_INT_CNTL__PROTECTED_MODE_ERROR_MASK,
+				"PROTECTED_MODE_ERROR" },
+			{ CP_INT_CNTL__RESERVED_BIT_ERROR_MASK,
+				"RESERVED_BIT_ERROR" },
+			{ CP_INT_CNTL__IB_ERROR_MASK, "IB_ERROR" },
+			{ CP_INT_CNTL__IB2_INT_MASK, "IB2" },
+			{ CP_INT_CNTL__IB1_INT_MASK, "IB1" },
+			{ CP_INT_CNTL__RB_INT_MASK, "RB" }) : "None"
+	)
+);
+
+#endif /* _ADRENO_A2XX_TRACE_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c
new file mode 100644
index 0000000..09eae17
--- /dev/null
+++ b/drivers/gpu/msm/adreno_a3xx.c
@@ -0,0 +1,2680 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <mach/socinfo.h>
+
+#include "kgsl.h"
+#include "adreno.h"
+#include "kgsl_sharedmem.h"
+#include "kgsl_cffdump.h"
+#include "a3xx_reg.h"
+#include "adreno_a3xx_trace.h"
+
+/*
+ * Set of registers to dump for A3XX on postmortem and snapshot.
+ * Registers in pairs - first value is the start offset, second
+ * is the stop offset (inclusive)
+ */
+
+const unsigned int a3xx_registers[] = {
+	0x0000, 0x0002, 0x0010, 0x0012, 0x0018, 0x0018, 0x0020, 0x0027,
+	0x0029, 0x002b, 0x002e, 0x0033, 0x0040, 0x0042, 0x0050, 0x005c,
+	0x0060, 0x006c, 0x0080, 0x0082, 0x0084, 0x0088, 0x0090, 0x00e5,
+	0x00ea, 0x00ed, 0x0100, 0x0100, 0x0110, 0x0123, 0x01c0, 0x01c1,
+	0x01c3, 0x01c5, 0x01c7, 0x01c7, 0x01d5, 0x01d9, 0x01dc, 0x01dd,
+	0x01ea, 0x01ea, 0x01ee, 0x01f1, 0x01f5, 0x01f5, 0x01fc, 0x01ff,
+	0x0440, 0x0440, 0x0443, 0x0443, 0x0445, 0x0445, 0x044d, 0x044f,
+	0x0452, 0x0452, 0x0454, 0x046f, 0x047c, 0x047c, 0x047f, 0x047f,
+	0x0578, 0x057f, 0x0600, 0x0602, 0x0605, 0x0607, 0x060a, 0x060e,
+	0x0612, 0x0614, 0x0c01, 0x0c02, 0x0c06, 0x0c1d, 0x0c3d, 0x0c3f,
+	0x0c48, 0x0c4b, 0x0c80, 0x0c80, 0x0c88, 0x0c8b, 0x0ca0, 0x0cb7,
+	0x0cc0, 0x0cc1, 0x0cc6, 0x0cc7, 0x0ce4, 0x0ce5, 0x0e00, 0x0e05,
+	0x0e0c, 0x0e0c, 0x0e22, 0x0e23, 0x0e41, 0x0e45, 0x0e64, 0x0e65,
+	0x0e80, 0x0e82, 0x0e84, 0x0e89, 0x0ea0, 0x0ea1, 0x0ea4, 0x0ea7,
+	0x0ec4, 0x0ecb, 0x0ee0, 0x0ee0, 0x0f00, 0x0f01, 0x0f03, 0x0f09,
+	0x2040, 0x2040, 0x2044, 0x2044, 0x2048, 0x204d, 0x2068, 0x2069,
+	0x206c, 0x206d, 0x2070, 0x2070, 0x2072, 0x2072, 0x2074, 0x2075,
+	0x2079, 0x207a, 0x20c0, 0x20d3, 0x20e4, 0x20ef, 0x2100, 0x2109,
+	0x210c, 0x210c, 0x210e, 0x210e, 0x2110, 0x2111, 0x2114, 0x2115,
+	0x21e4, 0x21e4, 0x21ea, 0x21ea, 0x21ec, 0x21ed, 0x21f0, 0x21f0,
+	0x2200, 0x2212, 0x2214, 0x2217, 0x221a, 0x221a, 0x2240, 0x227e,
+	0x2280, 0x228b, 0x22c0, 0x22c0, 0x22c4, 0x22ce, 0x22d0, 0x22d8,
+	0x22df, 0x22e6, 0x22e8, 0x22e9, 0x22ec, 0x22ec, 0x22f0, 0x22f7,
+	0x22ff, 0x22ff, 0x2340, 0x2343, 0x2348, 0x2349, 0x2350, 0x2356,
+	0x2360, 0x2360, 0x2440, 0x2440, 0x2444, 0x2444, 0x2448, 0x244d,
+	0x2468, 0x2469, 0x246c, 0x246d, 0x2470, 0x2470, 0x2472, 0x2472,
+	0x2474, 0x2475, 0x2479, 0x247a, 0x24c0, 0x24d3, 0x24e4, 0x24ef,
+	0x2500, 0x2509, 0x250c, 0x250c, 0x250e, 0x250e, 0x2510, 0x2511,
+	0x2514, 0x2515, 0x25e4, 0x25e4, 0x25ea, 0x25ea, 0x25ec, 0x25ed,
+	0x25f0, 0x25f0, 0x2600, 0x2612, 0x2614, 0x2617, 0x261a, 0x261a,
+	0x2640, 0x267e, 0x2680, 0x268b, 0x26c0, 0x26c0, 0x26c4, 0x26ce,
+	0x26d0, 0x26d8, 0x26df, 0x26e6, 0x26e8, 0x26e9, 0x26ec, 0x26ec,
+	0x26f0, 0x26f7, 0x26ff, 0x26ff, 0x2740, 0x2743, 0x2748, 0x2749,
+	0x2750, 0x2756, 0x2760, 0x2760, 0x300C, 0x300E, 0x301C, 0x301D,
+	0x302A, 0x302A, 0x302C, 0x302D, 0x3030, 0x3031, 0x3034, 0x3036,
+	0x303C, 0x303C, 0x305E, 0x305F,
+};
+
+const unsigned int a3xx_registers_count = ARRAY_SIZE(a3xx_registers) / 2;
+
+/* Simple macro to facilitate bit setting in the gmem2sys and sys2gmem
+ * functions.
+ */
+
+#define _SET(_shift, _val) ((_val) << (_shift))
+
+/*
+ ****************************************************************************
+ *
+ * Context state shadow structure:
+ *
+ * +---------------------+------------+-------------+---------------------+---+
+ * | ALU Constant Shadow | Reg Shadow | C&V Buffers | Shader Instr Shadow |Tex|
+ * +---------------------+------------+-------------+---------------------+---+
+ *
+ *		 8K - ALU Constant Shadow (8K aligned)
+ *		 4K - H/W Register Shadow (8K aligned)
+ *		 5K - Command and Vertex Buffers
+ *		 8K - Shader Instruction Shadow
+ *		 ~6K - Texture Constant Shadow
+ *
+ *
+ ***************************************************************************
+ */
+
+/* Sizes of all sections in state shadow memory */
+#define ALU_SHADOW_SIZE      (8*1024) /* 8KB */
+#define REG_SHADOW_SIZE      (4*1024) /* 4KB */
+#define CMD_BUFFER_SIZE      (5*1024) /* 5KB */
+#define TEX_SIZE_MEM_OBJECTS 896      /* bytes */
+#define TEX_SIZE_MIPMAP      1936     /* bytes */
+#define TEX_SIZE_SAMPLER_OBJ 256      /* bytes */
+#define TEX_SHADOW_SIZE                            \
+	((TEX_SIZE_MEM_OBJECTS + TEX_SIZE_MIPMAP + \
+	TEX_SIZE_SAMPLER_OBJ)*2) /* ~6KB */
+#define SHADER_SHADOW_SIZE   (8*1024) /* 8KB */
+
+/* Total context size, excluding GMEM shadow */
+#define CONTEXT_SIZE                         \
+	(ALU_SHADOW_SIZE+REG_SHADOW_SIZE +   \
+	CMD_BUFFER_SIZE+SHADER_SHADOW_SIZE + \
+	TEX_SHADOW_SIZE)
+
+/* Offsets to different sections in context shadow memory */
+#define REG_OFFSET ALU_SHADOW_SIZE
+#define CMD_OFFSET (REG_OFFSET+REG_SHADOW_SIZE)
+#define SHADER_OFFSET (CMD_OFFSET+CMD_BUFFER_SIZE)
+#define TEX_OFFSET (SHADER_OFFSET+SHADER_SHADOW_SIZE)
+#define VS_TEX_OFFSET_MEM_OBJECTS TEX_OFFSET
+#define VS_TEX_OFFSET_MIPMAP (VS_TEX_OFFSET_MEM_OBJECTS+TEX_SIZE_MEM_OBJECTS)
+#define VS_TEX_OFFSET_SAMPLER_OBJ (VS_TEX_OFFSET_MIPMAP+TEX_SIZE_MIPMAP)
+#define FS_TEX_OFFSET_MEM_OBJECTS \
+	(VS_TEX_OFFSET_SAMPLER_OBJ+TEX_SIZE_SAMPLER_OBJ)
+#define FS_TEX_OFFSET_MIPMAP (FS_TEX_OFFSET_MEM_OBJECTS+TEX_SIZE_MEM_OBJECTS)
+#define FS_TEX_OFFSET_SAMPLER_OBJ (FS_TEX_OFFSET_MIPMAP+TEX_SIZE_MIPMAP)
+
+/* The offset for fragment shader data in HLSQ context */
+#define SSIZE (16*1024)
+
+#define HLSQ_SAMPLER_OFFSET 0x000
+#define HLSQ_MEMOBJ_OFFSET  0x400
+#define HLSQ_MIPMAP_OFFSET  0x800
+
+/* Use shadow RAM */
+#define HLSQ_SHADOW_BASE		(0x10000+SSIZE*2)
+
+#define REG_TO_MEM_LOOP_COUNT_SHIFT	18
+
+#define BUILD_PC_DRAW_INITIATOR(prim_type, source_select, index_size, \
+	vis_cull_mode) \
+	(((prim_type)      << PC_DRAW_INITIATOR_PRIM_TYPE) | \
+	((source_select)   << PC_DRAW_INITIATOR_SOURCE_SELECT) | \
+	((index_size & 1)  << PC_DRAW_INITIATOR_INDEX_SIZE) | \
+	((index_size >> 1) << PC_DRAW_INITIATOR_SMALL_INDEX) | \
+	((vis_cull_mode)   << PC_DRAW_INITIATOR_VISIBILITY_CULLING_MODE) | \
+	(1                 << PC_DRAW_INITIATOR_PRE_DRAW_INITIATOR_ENABLE))
+
+/*
+ * List of context registers (starting from dword offset 0x2000).
+ * Each line contains start and end of a range of registers.
+ */
+static const unsigned int context_register_ranges[] = {
+	A3XX_GRAS_CL_CLIP_CNTL, A3XX_GRAS_CL_CLIP_CNTL,
+	A3XX_GRAS_CL_GB_CLIP_ADJ, A3XX_GRAS_CL_GB_CLIP_ADJ,
+	A3XX_GRAS_CL_VPORT_XOFFSET, A3XX_GRAS_CL_VPORT_ZSCALE,
+	A3XX_GRAS_SU_POINT_MINMAX, A3XX_GRAS_SU_POINT_SIZE,
+	A3XX_GRAS_SU_POLY_OFFSET_SCALE, A3XX_GRAS_SU_POLY_OFFSET_OFFSET,
+	A3XX_GRAS_SU_MODE_CONTROL, A3XX_GRAS_SU_MODE_CONTROL,
+	A3XX_GRAS_SC_CONTROL, A3XX_GRAS_SC_CONTROL,
+	A3XX_GRAS_SC_SCREEN_SCISSOR_TL, A3XX_GRAS_SC_SCREEN_SCISSOR_BR,
+	A3XX_GRAS_SC_WINDOW_SCISSOR_TL, A3XX_GRAS_SC_WINDOW_SCISSOR_BR,
+	A3XX_RB_MODE_CONTROL, A3XX_RB_MRT_BLEND_CONTROL3,
+	A3XX_RB_BLEND_RED, A3XX_RB_COPY_DEST_INFO,
+	A3XX_RB_DEPTH_CONTROL, A3XX_RB_DEPTH_CONTROL,
+	A3XX_PC_VSTREAM_CONTROL, A3XX_PC_VSTREAM_CONTROL,
+	A3XX_PC_VERTEX_REUSE_BLOCK_CNTL, A3XX_PC_VERTEX_REUSE_BLOCK_CNTL,
+	A3XX_PC_PRIM_VTX_CNTL, A3XX_PC_RESTART_INDEX,
+	A3XX_HLSQ_CONTROL_0_REG, A3XX_HLSQ_CONST_FSPRESV_RANGE_REG,
+	A3XX_HLSQ_CL_NDRANGE_0_REG, A3XX_HLSQ_CL_NDRANGE_0_REG,
+	A3XX_HLSQ_CL_NDRANGE_2_REG, A3XX_HLSQ_CL_CONTROL_1_REG,
+	A3XX_HLSQ_CL_KERNEL_CONST_REG, A3XX_HLSQ_CL_KERNEL_GROUP_Z_REG,
+	A3XX_HLSQ_CL_WG_OFFSET_REG, A3XX_HLSQ_CL_WG_OFFSET_REG,
+	A3XX_VFD_CONTROL_0, A3XX_VFD_VS_THREADING_THRESHOLD,
+	A3XX_SP_SP_CTRL_REG, A3XX_SP_SP_CTRL_REG,
+	A3XX_SP_VS_CTRL_REG0, A3XX_SP_VS_OUT_REG_7,
+	A3XX_SP_VS_VPC_DST_REG_0, A3XX_SP_VS_PVT_MEM_SIZE_REG,
+	A3XX_SP_VS_LENGTH_REG, A3XX_SP_FS_PVT_MEM_SIZE_REG,
+	A3XX_SP_FS_FLAT_SHAD_MODE_REG_0, A3XX_SP_FS_FLAT_SHAD_MODE_REG_1,
+	A3XX_SP_FS_OUTPUT_REG, A3XX_SP_FS_OUTPUT_REG,
+	A3XX_SP_FS_MRT_REG_0, A3XX_SP_FS_IMAGE_OUTPUT_REG_3,
+	A3XX_SP_FS_LENGTH_REG, A3XX_SP_FS_LENGTH_REG,
+	A3XX_TPL1_TP_VS_TEX_OFFSET, A3XX_TPL1_TP_FS_BORDER_COLOR_BASE_ADDR,
+	A3XX_VPC_ATTR, A3XX_VPC_VARY_CYLWRAP_ENABLE_1,
+};
+
+/* Global registers that need to be saved separately */
+static const unsigned int global_registers[] = {
+	A3XX_GRAS_CL_USER_PLANE_X0, A3XX_GRAS_CL_USER_PLANE_Y0,
+	A3XX_GRAS_CL_USER_PLANE_Z0, A3XX_GRAS_CL_USER_PLANE_W0,
+	A3XX_GRAS_CL_USER_PLANE_X1, A3XX_GRAS_CL_USER_PLANE_Y1,
+	A3XX_GRAS_CL_USER_PLANE_Z1, A3XX_GRAS_CL_USER_PLANE_W1,
+	A3XX_GRAS_CL_USER_PLANE_X2, A3XX_GRAS_CL_USER_PLANE_Y2,
+	A3XX_GRAS_CL_USER_PLANE_Z2, A3XX_GRAS_CL_USER_PLANE_W2,
+	A3XX_GRAS_CL_USER_PLANE_X3, A3XX_GRAS_CL_USER_PLANE_Y3,
+	A3XX_GRAS_CL_USER_PLANE_Z3, A3XX_GRAS_CL_USER_PLANE_W3,
+	A3XX_GRAS_CL_USER_PLANE_X4, A3XX_GRAS_CL_USER_PLANE_Y4,
+	A3XX_GRAS_CL_USER_PLANE_Z4, A3XX_GRAS_CL_USER_PLANE_W4,
+	A3XX_GRAS_CL_USER_PLANE_X5, A3XX_GRAS_CL_USER_PLANE_Y5,
+	A3XX_GRAS_CL_USER_PLANE_Z5, A3XX_GRAS_CL_USER_PLANE_W5,
+	A3XX_VSC_BIN_SIZE,
+	A3XX_VSC_PIPE_CONFIG_0, A3XX_VSC_PIPE_CONFIG_1,
+	A3XX_VSC_PIPE_CONFIG_2, A3XX_VSC_PIPE_CONFIG_3,
+	A3XX_VSC_PIPE_CONFIG_4, A3XX_VSC_PIPE_CONFIG_5,
+	A3XX_VSC_PIPE_CONFIG_6, A3XX_VSC_PIPE_CONFIG_7,
+	A3XX_VSC_PIPE_DATA_ADDRESS_0, A3XX_VSC_PIPE_DATA_ADDRESS_1,
+	A3XX_VSC_PIPE_DATA_ADDRESS_2, A3XX_VSC_PIPE_DATA_ADDRESS_3,
+	A3XX_VSC_PIPE_DATA_ADDRESS_4, A3XX_VSC_PIPE_DATA_ADDRESS_5,
+	A3XX_VSC_PIPE_DATA_ADDRESS_6, A3XX_VSC_PIPE_DATA_ADDRESS_7,
+	A3XX_VSC_PIPE_DATA_LENGTH_0, A3XX_VSC_PIPE_DATA_LENGTH_1,
+	A3XX_VSC_PIPE_DATA_LENGTH_2, A3XX_VSC_PIPE_DATA_LENGTH_3,
+	A3XX_VSC_PIPE_DATA_LENGTH_4, A3XX_VSC_PIPE_DATA_LENGTH_5,
+	A3XX_VSC_PIPE_DATA_LENGTH_6, A3XX_VSC_PIPE_DATA_LENGTH_7,
+	A3XX_VSC_SIZE_ADDRESS
+};
+
+#define GLOBAL_REGISTER_COUNT ARRAY_SIZE(global_registers)
+
+/* A scratchpad used to build commands during context create */
+static struct tmp_ctx {
+	unsigned int *cmd; /* Next available dword in C&V buffer */
+
+	/* Addresses in comamnd buffer where registers are saved */
+	uint32_t reg_values[GLOBAL_REGISTER_COUNT];
+	uint32_t gmem_base; /* Base GPU address of GMEM */
+} tmp_ctx;
+
+#ifndef GSL_CONTEXT_SWITCH_CPU_SYNC
+/*
+ * Function for executing dest = ( (reg & and) ROL rol ) | or
+ */
+static unsigned int *rmw_regtomem(unsigned int *cmd,
+				  unsigned int reg, unsigned int and,
+				  unsigned int rol, unsigned int or,
+				  unsigned int dest)
+{
+	/* CP_SCRATCH_REG2 = (CP_SCRATCH_REG2 & 0x00000000) | reg */
+	*cmd++ = cp_type3_packet(CP_REG_RMW, 3);
+	*cmd++ = (1 << 30) | A3XX_CP_SCRATCH_REG2;
+	*cmd++ = 0x00000000;	/* AND value */
+	*cmd++ = reg;		/* OR address */
+
+	/* CP_SCRATCH_REG2 = ( (CP_SCRATCH_REG2 & and) ROL rol ) |  or */
+	*cmd++ = cp_type3_packet(CP_REG_RMW, 3);
+	*cmd++ = (rol << 24) | A3XX_CP_SCRATCH_REG2;
+	*cmd++ = and;		/* AND value */
+	*cmd++ = or;		/* OR value */
+
+	*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+	*cmd++ = A3XX_CP_SCRATCH_REG2;
+	*cmd++ = dest;
+
+	return cmd;
+}
+#endif
+
+static void build_regconstantsave_cmds(struct adreno_device *adreno_dev,
+				       struct adreno_context *drawctxt)
+{
+	unsigned int *cmd = tmp_ctx.cmd;
+	unsigned int *start;
+	unsigned int i;
+
+	drawctxt->constant_save_commands[0].hostptr = cmd;
+	drawctxt->constant_save_commands[0].gpuaddr =
+	    virt2gpu(cmd, &drawctxt->gpustate);
+	cmd++;
+
+	start = cmd;
+
+	*cmd++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
+	*cmd++ = 0;
+
+#ifndef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES
+	/*
+	 * Context registers are already shadowed; just need to
+	 * disable shadowing to prevent corruption.
+	 */
+
+	*cmd++ = cp_type3_packet(CP_LOAD_CONSTANT_CONTEXT, 3);
+	*cmd++ = (drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000;
+	*cmd++ = 4 << 16;	/* regs, start=0 */
+	*cmd++ = 0x0;		/* count = 0 */
+
+#else
+	/*
+	 * Make sure the HW context has the correct register values before
+	 * reading them.
+	 */
+
+	/* Write context registers into shadow */
+	for (i = 0; i < ARRAY_SIZE(context_register_ranges) / 2; i++) {
+		unsigned int start = context_register_ranges[i * 2];
+		unsigned int end = context_register_ranges[i * 2 + 1];
+		*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+		*cmd++ = ((end - start + 1) << REG_TO_MEM_LOOP_COUNT_SHIFT) |
+			start;
+		*cmd++ = ((drawctxt->gpustate.gpuaddr + REG_OFFSET)
+			  & 0xFFFFE000) + (start - 0x2000) * 4;
+	}
+#endif
+
+	/* Need to handle some of the global registers separately */
+	for (i = 0; i < ARRAY_SIZE(global_registers); i++) {
+		*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+		*cmd++ = global_registers[i];
+		*cmd++ = tmp_ctx.reg_values[i];
+	}
+
+	/* Save vertex shader constants */
+	*cmd++ = cp_type3_packet(CP_COND_EXEC, 4);
+	*cmd++ = drawctxt->cond_execs[2].gpuaddr >> 2;
+	*cmd++ = drawctxt->cond_execs[2].gpuaddr >> 2;
+	*cmd++ = 0x0000FFFF;
+	*cmd++ = 3; /* EXEC_COUNT */
+	*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+	drawctxt->constant_save_commands[1].hostptr = cmd;
+	drawctxt->constant_save_commands[1].gpuaddr =
+	    virt2gpu(cmd, &drawctxt->gpustate);
+	/*
+	   From fixup:
+
+	   dwords = SP_VS_CTRL_REG1.VSCONSTLENGTH / 4
+	   src = (HLSQ_SHADOW_BASE + 0x2000) / 4
+
+	   From register spec:
+	   SP_VS_CTRL_REG1.VSCONSTLENGTH [09:00]: 0-512, unit = 128bits.
+	 */
+	*cmd++ = 0;	/* (dwords << REG_TO_MEM_LOOP_COUNT_SHIFT) | src  */
+	/* ALU constant shadow base */
+	*cmd++ = drawctxt->gpustate.gpuaddr & 0xfffffffc;
+
+	/* Save fragment shader constants */
+	*cmd++ = cp_type3_packet(CP_COND_EXEC, 4);
+	*cmd++ = drawctxt->cond_execs[3].gpuaddr >> 2;
+	*cmd++ = drawctxt->cond_execs[3].gpuaddr >> 2;
+	*cmd++ = 0x0000FFFF;
+	*cmd++ = 3; /* EXEC_COUNT */
+	*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+	drawctxt->constant_save_commands[2].hostptr = cmd;
+	drawctxt->constant_save_commands[2].gpuaddr =
+	    virt2gpu(cmd, &drawctxt->gpustate);
+	/*
+	   From fixup:
+
+	   dwords = SP_FS_CTRL_REG1.FSCONSTLENGTH / 4
+	   src = (HLSQ_SHADOW_BASE + 0x2000 + SSIZE) / 4
+
+	   From register spec:
+	   SP_FS_CTRL_REG1.FSCONSTLENGTH [09:00]: 0-512, unit = 128bits.
+	 */
+	*cmd++ = 0;	/* (dwords << REG_TO_MEM_LOOP_COUNT_SHIFT) | src  */
+
+	/*
+	   From fixup:
+
+	   base = drawctxt->gpustate.gpuaddr (ALU constant shadow base)
+	   offset = SP_FS_OBJ_OFFSET_REG.CONSTOBJECTSTARTOFFSET
+
+	   From register spec:
+	   SP_FS_OBJ_OFFSET_REG.CONSTOBJECTSTARTOFFSET [16:24]: Constant object
+	   start offset in on chip RAM,
+	   128bit aligned
+
+	   dst = base + offset
+	   Because of the base alignment we can use
+	   dst = base | offset
+	 */
+	*cmd++ = 0;		/* dst */
+
+	/* Save VS texture memory objects */
+	*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+	*cmd++ =
+	    ((TEX_SIZE_MEM_OBJECTS / 4) << REG_TO_MEM_LOOP_COUNT_SHIFT) |
+		((HLSQ_SHADOW_BASE + HLSQ_MEMOBJ_OFFSET) / 4);
+	*cmd++ =
+	    (drawctxt->gpustate.gpuaddr +
+	     VS_TEX_OFFSET_MEM_OBJECTS) & 0xfffffffc;
+
+	/* Save VS texture mipmap pointers */
+	*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+	*cmd++ =
+	    ((TEX_SIZE_MIPMAP / 4) << REG_TO_MEM_LOOP_COUNT_SHIFT) |
+		((HLSQ_SHADOW_BASE + HLSQ_MIPMAP_OFFSET) / 4);
+	*cmd++ =
+	    (drawctxt->gpustate.gpuaddr + VS_TEX_OFFSET_MIPMAP) & 0xfffffffc;
+
+	/* Save VS texture sampler objects */
+	*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+	*cmd++ = ((TEX_SIZE_SAMPLER_OBJ / 4) << REG_TO_MEM_LOOP_COUNT_SHIFT) |
+		((HLSQ_SHADOW_BASE + HLSQ_SAMPLER_OFFSET) / 4);
+	*cmd++ =
+	    (drawctxt->gpustate.gpuaddr +
+	     VS_TEX_OFFSET_SAMPLER_OBJ) & 0xfffffffc;
+
+	/* Save FS texture memory objects */
+	*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+	*cmd++ =
+	    ((TEX_SIZE_MEM_OBJECTS / 4) << REG_TO_MEM_LOOP_COUNT_SHIFT) |
+		((HLSQ_SHADOW_BASE + HLSQ_MEMOBJ_OFFSET + SSIZE) / 4);
+	*cmd++ =
+	    (drawctxt->gpustate.gpuaddr +
+	     FS_TEX_OFFSET_MEM_OBJECTS) & 0xfffffffc;
+
+	/* Save FS texture mipmap pointers */
+	*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+	*cmd++ =
+	    ((TEX_SIZE_MIPMAP / 4) << REG_TO_MEM_LOOP_COUNT_SHIFT) |
+		((HLSQ_SHADOW_BASE + HLSQ_MIPMAP_OFFSET + SSIZE) / 4);
+	*cmd++ =
+	    (drawctxt->gpustate.gpuaddr + FS_TEX_OFFSET_MIPMAP) & 0xfffffffc;
+
+	/* Save FS texture sampler objects */
+	*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+	*cmd++ =
+	    ((TEX_SIZE_SAMPLER_OBJ / 4) << REG_TO_MEM_LOOP_COUNT_SHIFT) |
+		((HLSQ_SHADOW_BASE + HLSQ_SAMPLER_OFFSET + SSIZE) / 4);
+	*cmd++ =
+	    (drawctxt->gpustate.gpuaddr +
+	     FS_TEX_OFFSET_SAMPLER_OBJ) & 0xfffffffc;
+
+	/* Create indirect buffer command for above command sequence */
+	create_ib1(drawctxt, drawctxt->regconstant_save, start, cmd);
+
+	tmp_ctx.cmd = cmd;
+}
+
+/* Copy GMEM contents to system memory shadow. */
+static unsigned int *build_gmem2sys_cmds(struct adreno_device *adreno_dev,
+					 struct adreno_context *drawctxt,
+					 struct gmem_shadow_t *shadow)
+{
+	unsigned int *cmds = tmp_ctx.cmd;
+	unsigned int *start = cmds;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(A3XX_RB_MODE_CONTROL);
+
+	/* RB_MODE_CONTROL */
+	*cmds++ = _SET(RB_MODECONTROL_RENDER_MODE, RB_RESOLVE_PASS) |
+		_SET(RB_MODECONTROL_MARB_CACHE_SPLIT_MODE, 1) |
+		_SET(RB_MODECONTROL_PACKER_TIMER_ENABLE, 1);
+	/* RB_RENDER_CONTROL */
+	*cmds++ = _SET(RB_RENDERCONTROL_BIN_WIDTH, shadow->width >> 5) |
+		_SET(RB_RENDERCONTROL_DISABLE_COLOR_PIPE, 1);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5);
+	*cmds++ = CP_REG(A3XX_RB_COPY_CONTROL);
+	/* RB_COPY_CONTROL */
+	*cmds++ = _SET(RB_COPYCONTROL_RESOLVE_CLEAR_MODE,
+		RB_CLEAR_MODE_RESOLVE) |
+		_SET(RB_COPYCONTROL_COPY_GMEM_BASE,
+		tmp_ctx.gmem_base >> 14);
+	/* RB_COPY_DEST_BASE */
+	*cmds++ = _SET(RB_COPYDESTBASE_COPY_DEST_BASE,
+		shadow->gmemshadow.gpuaddr >> 5);
+	/* RB_COPY_DEST_PITCH */
+	*cmds++ = _SET(RB_COPYDESTPITCH_COPY_DEST_PITCH,
+		(shadow->pitch * 4) / 32);
+	/* RB_COPY_DEST_INFO */
+	*cmds++ = _SET(RB_COPYDESTINFO_COPY_DEST_TILE,
+		RB_TILINGMODE_LINEAR) |
+		_SET(RB_COPYDESTINFO_COPY_DEST_FORMAT, RB_R8G8B8A8_UNORM) |
+		_SET(RB_COPYDESTINFO_COPY_COMPONENT_ENABLE, 0X0F) |
+		_SET(RB_COPYDESTINFO_COPY_DEST_ENDIAN, RB_ENDIAN_NONE);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_GRAS_SC_CONTROL);
+	/* GRAS_SC_CONTROL */
+	*cmds++ = _SET(GRAS_SC_CONTROL_RENDER_MODE, 2);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(A3XX_VFD_CONTROL_0);
+	/* VFD_CONTROL_0 */
+	*cmds++ = _SET(VFD_CTRLREG0_TOTALATTRTOVS, 4) |
+		_SET(VFD_CTRLREG0_PACKETSIZE, 2) |
+		_SET(VFD_CTRLREG0_STRMDECINSTRCNT, 1) |
+		_SET(VFD_CTRLREG0_STRMFETCHINSTRCNT, 1);
+	/* VFD_CONTROL_1 */
+	*cmds++ = _SET(VFD_CTRLREG1_MAXSTORAGE, 1) |
+		_SET(VFD_CTRLREG1_REGID4VTX,  252) |
+		_SET(VFD_CTRLREG1_REGID4INST,  252);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(A3XX_VFD_FETCH_INSTR_0_0);
+	/* VFD_FETCH_INSTR_0_0 */
+	*cmds++ = _SET(VFD_FETCHINSTRUCTIONS_FETCHSIZE, 11) |
+		_SET(VFD_FETCHINSTRUCTIONS_BUFSTRIDE, 12) |
+		_SET(VFD_FETCHINSTRUCTIONS_STEPRATE, 1);
+	/* VFD_FETCH_INSTR_1_0 */
+	*cmds++ = _SET(VFD_BASEADDR_BASEADDR,
+		shadow->quad_vertices.gpuaddr);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_VFD_DECODE_INSTR_0);
+	/* VFD_DECODE_INSTR_0 */
+	*cmds++ = _SET(VFD_DECODEINSTRUCTIONS_WRITEMASK, 0x0F) |
+		_SET(VFD_DECODEINSTRUCTIONS_CONSTFILL, 1) |
+		_SET(VFD_DECODEINSTRUCTIONS_FORMAT, 2) |
+		_SET(VFD_DECODEINSTRUCTIONS_REGID, 5) |
+		_SET(VFD_DECODEINSTRUCTIONS_SHIFTCNT, 12) |
+		_SET(VFD_DECODEINSTRUCTIONS_LASTCOMPVALID, 1);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5);
+	*cmds++ = CP_REG(A3XX_HLSQ_CONTROL_0_REG);
+	/* HLSQ_CONTROL_0_REG */
+	*cmds++ = _SET(HLSQ_CTRL0REG_FSTHREADSIZE, HLSQ_TWO_PIX_QUADS) |
+		_SET(HLSQ_CTRL0REG_FSSUPERTHREADENABLE, 1) |
+		_SET(HLSQ_CTRL0REG_SPSHADERRESTART, 1) |
+		_SET(HLSQ_CTRL0REG_RESERVED2, 1) |
+		_SET(HLSQ_CTRL0REG_CHUNKDISABLE, 1) |
+		_SET(HLSQ_CTRL0REG_CONSTSWITCHMODE, 1) |
+		_SET(HLSQ_CTRL0REG_LAZYUPDATEDISABLE, 1) |
+		_SET(HLSQ_CTRL0REG_SPCONSTFULLUPDATE, 1) |
+		_SET(HLSQ_CTRL0REG_TPFULLUPDATE, 1);
+	/* HLSQ_CONTROL_1_REG */
+	*cmds++ = _SET(HLSQ_CTRL1REG_VSTHREADSIZE, HLSQ_TWO_VTX_QUADS) |
+		_SET(HLSQ_CTRL1REG_VSSUPERTHREADENABLE, 1) |
+		_SET(HLSQ_CTRL1REG_RESERVED1, 4);
+	/* HLSQ_CONTROL_2_REG */
+	*cmds++ = _SET(HLSQ_CTRL2REG_PRIMALLOCTHRESHOLD, 31);
+	/* HLSQ_CONTROL_3_REG */
+	*cmds++ = 0x00000000;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5);
+	*cmds++ = CP_REG(A3XX_HLSQ_VS_CONTROL_REG);
+	/* HLSQ_VS_CONTROL_REG */
+	*cmds++ = _SET(HLSQ_VSCTRLREG_VSINSTRLENGTH, 1);
+	/* HLSQ_FS_CONTROL_REG */
+	*cmds++ = _SET(HLSQ_FSCTRLREG_FSCONSTLENGTH, 1) |
+		_SET(HLSQ_FSCTRLREG_FSCONSTSTARTOFFSET, 272) |
+		_SET(HLSQ_FSCTRLREG_FSINSTRLENGTH, 1);
+	/* HLSQ_CONST_VSPRESV_RANGE_REG */
+	*cmds++ = 0x00000000;
+	/* HLSQ_CONST_FSPRESV_RANGE_REQ */
+	*cmds++ = _SET(HLSQ_CONSTFSPRESERVEDRANGEREG_STARTENTRY, 32) |
+		_SET(HLSQ_CONSTFSPRESERVEDRANGEREG_ENDENTRY, 32);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_SP_FS_LENGTH_REG);
+	/* SP_FS_LENGTH_REG */
+	*cmds++ = _SET(SP_SHADERLENGTH_LEN, 1);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_SP_SP_CTRL_REG);
+	/* SP_SP_CTRL_REG */
+	*cmds++ = _SET(SP_SPCTRLREG_CONSTMODE, 1) |
+		_SET(SP_SPCTRLREG_SLEEPMODE, 1);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 12);
+	*cmds++ = CP_REG(A3XX_SP_VS_CTRL_REG0);
+	/* SP_VS_CTRL_REG0 */
+	*cmds++ = _SET(SP_VSCTRLREG0_VSTHREADMODE, SP_MULTI) |
+		_SET(SP_VSCTRLREG0_VSINSTRBUFFERMODE, SP_BUFFER_MODE) |
+		_SET(SP_VSCTRLREG0_VSICACHEINVALID, 1) |
+		_SET(SP_VSCTRLREG0_VSFULLREGFOOTPRINT, 3) |
+		_SET(SP_VSCTRLREG0_VSTHREADSIZE, SP_TWO_VTX_QUADS) |
+		_SET(SP_VSCTRLREG0_VSSUPERTHREADMODE, 1) |
+		_SET(SP_VSCTRLREG0_VSLENGTH, 1);
+	/* SP_VS_CTRL_REG1 */
+	*cmds++ = _SET(SP_VSCTRLREG1_VSINITIALOUTSTANDING, 4);
+	/* SP_VS_PARAM_REG */
+	*cmds++ = _SET(SP_VSPARAMREG_POSREGID, 1) |
+		_SET(SP_VSPARAMREG_PSIZEREGID, 252);
+	/* SP_VS_OUT_REG_0 */
+	*cmds++ = 0x00000000;
+	/* SP_VS_OUT_REG_1 */
+	*cmds++ = 0x00000000;
+	/* SP_VS_OUT_REG_2 */
+	*cmds++ = 0x00000000;
+	/* SP_VS_OUT_REG_3 */
+	*cmds++ = 0x00000000;
+	/* SP_VS_OUT_REG_4 */
+	*cmds++ = 0x00000000;
+	/* SP_VS_OUT_REG_5 */
+	*cmds++ = 0x00000000;
+	/* SP_VS_OUT_REG_6 */
+	*cmds++ = 0x00000000;
+	/* SP_VS_OUT_REG_7 */
+	*cmds++ = 0x00000000;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 7);
+	*cmds++ = CP_REG(A3XX_SP_VS_VPC_DST_REG_0);
+	/* SP_VS_VPC_DST_REG_0 */
+	*cmds++ = 0x00000000;
+	/* SP_VS_VPC_DST_REG_1 */
+	*cmds++ = 0x00000000;
+	/* SP_VS_VPC_DST_REG_2 */
+	*cmds++ = 0x00000000;
+	/* SP_VS_VPC_DST_REG_3 */
+	*cmds++ = 0x00000000;
+	/* SP_VS_OBJ_OFFSET_REG */
+	*cmds++ = 0x00000000;
+	/* SP_VS_OBJ_START_REG */
+	*cmds++ = 0x00000000;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 6);
+	*cmds++ = CP_REG(A3XX_SP_VS_LENGTH_REG);
+	/* SP_VS_LENGTH_REG */
+	*cmds++ = _SET(SP_SHADERLENGTH_LEN, 1);
+	/* SP_FS_CTRL_REG0 */
+	*cmds++ = _SET(SP_FSCTRLREG0_FSTHREADMODE, SP_MULTI) |
+		_SET(SP_FSCTRLREG0_FSINSTRBUFFERMODE, SP_BUFFER_MODE) |
+		_SET(SP_FSCTRLREG0_FSICACHEINVALID, 1) |
+		_SET(SP_FSCTRLREG0_FSFULLREGFOOTPRINT, 2) |
+		_SET(SP_FSCTRLREG0_FSINOUTREGOVERLAP, 1) |
+		_SET(SP_FSCTRLREG0_FSTHREADSIZE, SP_TWO_VTX_QUADS) |
+		_SET(SP_FSCTRLREG0_FSSUPERTHREADMODE, 1) |
+		_SET(SP_FSCTRLREG0_FSLENGTH, 1);
+	/* SP_FS_CTRL_REG1 */
+	*cmds++ = _SET(SP_FSCTRLREG1_FSCONSTLENGTH, 1) |
+		_SET(SP_FSCTRLREG1_FSINITIALOUTSTANDING, 2) |
+		_SET(SP_FSCTRLREG1_HALFPRECVAROFFSET, 63);
+	/* SP_FS_OBJ_OFFSET_REG */
+	*cmds++ = _SET(SP_OBJOFFSETREG_CONSTOBJECTSTARTOFFSET, 272) |
+		_SET(SP_OBJOFFSETREG_SHADEROBJOFFSETINIC, 1);
+	/* SP_FS_OBJ_START_REG */
+	*cmds++ = 0x00000000;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(A3XX_SP_FS_FLAT_SHAD_MODE_REG_0);
+	/* SP_FS_FLAT_SHAD_MODE_REG_0 */
+	*cmds++ = 0x00000000;
+	/* SP_FS_FLAT_SHAD_MODE_REG_1 */
+	*cmds++ = 0x00000000;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_SP_FS_OUTPUT_REG);
+	/* SP_FS_OUTPUT_REG */
+	*cmds++ = _SET(SP_IMAGEOUTPUTREG_PAD0, SP_PIXEL_BASED);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5);
+	*cmds++ = CP_REG(A3XX_SP_FS_MRT_REG_0);
+	/* SP_FS_MRT_REG_0 */
+	*cmds++ = _SET(SP_FSMRTREG_REGID, 1);
+	/* SP_FS_MRT_REG_1 */
+	*cmds++ = 0x00000000;
+	/* SP_FS_MRT_REG_2 */
+	*cmds++ = 0x00000000;
+	/* SP_FS_MRT_REG_3 */
+	*cmds++ = 0x00000000;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 11);
+	*cmds++ = CP_REG(A3XX_VPC_ATTR);
+	/* VPC_ATTR */
+	*cmds++ = _SET(VPC_VPCATTR_THRHDASSIGN, 1) |
+		_SET(VPC_VPCATTR_LMSIZE, 1);
+	/* VPC_PACK */
+	*cmds++ = 0x00000000;
+	/* VPC_VARRYING_INTERUPT_MODE_0 */
+	*cmds++ = 0x00000000;
+	/* VPC_VARRYING_INTERUPT_MODE_1 */
+	*cmds++ = 0x00000000;
+	/* VPC_VARRYING_INTERUPT_MODE_2 */
+	*cmds++ = 0x00000000;
+	/* VPC_VARRYING_INTERUPT_MODE_3 */
+	*cmds++ = 0x00000000;
+	/* VPC_VARYING_PS_REPL_MODE_0 */
+	*cmds++ = 0x00000000;
+	/* VPC_VARYING_PS_REPL_MODE_1 */
+	*cmds++ = 0x00000000;
+	/* VPC_VARYING_PS_REPL_MODE_2 */
+	*cmds++ = 0x00000000;
+	/* VPC_VARYING_PS_REPL_MODE_3 */
+	*cmds++ = 0x00000000;
+
+	*cmds++ = cp_type3_packet(CP_LOAD_STATE, 10);
+	*cmds++ = (0 << CP_LOADSTATE_DSTOFFSET_SHIFT)
+		| (HLSQ_DIRECT << CP_LOADSTATE_STATESRC_SHIFT)
+		| (HLSQ_BLOCK_ID_SP_VS << CP_LOADSTATE_STATEBLOCKID_SHIFT)
+		| (1 << CP_LOADSTATE_NUMOFUNITS_SHIFT);
+	*cmds++ = (HLSQ_SP_VS_INSTR << CP_LOADSTATE_STATETYPE_SHIFT)
+		| (0 << CP_LOADSTATE_EXTSRCADDR_SHIFT);
+
+	/* (sy)(rpt3)mov.f32f32 r0.y, (r)r1.y; */
+	*cmds++ = 0x00000005; *cmds++ = 0x30044b01;
+	/* end; */
+	*cmds++ = 0x00000000; *cmds++ = 0x03000000;
+	/* nop; */
+	*cmds++ = 0x00000000; *cmds++ = 0x00000000;
+	/* nop; */
+	*cmds++ = 0x00000000; *cmds++ = 0x00000000;
+
+	*cmds++ = cp_type3_packet(CP_LOAD_STATE, 10);
+	*cmds++ = (0 << CP_LOADSTATE_DSTOFFSET_SHIFT)
+		| (HLSQ_DIRECT << CP_LOADSTATE_STATESRC_SHIFT)
+		| (HLSQ_BLOCK_ID_SP_FS << CP_LOADSTATE_STATEBLOCKID_SHIFT)
+		| (1 << CP_LOADSTATE_NUMOFUNITS_SHIFT);
+	*cmds++ = (HLSQ_SP_FS_INSTR << CP_LOADSTATE_STATETYPE_SHIFT)
+		| (0 << CP_LOADSTATE_EXTSRCADDR_SHIFT);
+
+	/* (sy)(rpt3)mov.f32f32 r0.y, (r)c0.x; */
+	*cmds++ = 0x00000000; *cmds++ = 0x30244b01;
+	/* end; */
+	*cmds++ = 0x00000000; *cmds++ = 0x03000000;
+	/* nop; */
+	*cmds++ = 0x00000000; *cmds++ = 0x00000000;
+	/* nop; */
+	*cmds++ = 0x00000000; *cmds++ = 0x00000000;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_RB_MSAA_CONTROL);
+	/* RB_MSAA_CONTROL */
+	*cmds++ = _SET(RB_MSAACONTROL_MSAA_DISABLE, 1) |
+		_SET(RB_MSAACONTROL_SAMPLE_MASK, 0xFFFF);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_RB_DEPTH_CONTROL);
+	/* RB_DEPTH_CONTROL */
+	*cmds++ = _SET(RB_DEPTHCONTROL_Z_TEST_FUNC, RB_FRAG_NEVER);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_RB_MRT_CONTROL0);
+	/* RB_MRT_CONTROL0 */
+	*cmds++ = _SET(RB_MRTCONTROL_READ_DEST_ENABLE, 1) |
+		_SET(RB_MRTCONTROL_ROP_CODE, 12) |
+		_SET(RB_MRTCONTROL_DITHER_MODE, RB_DITHER_ALWAYS) |
+		_SET(RB_MRTCONTROL_COMPONENT_ENABLE, 0xF);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(A3XX_RB_MRT_BLEND_CONTROL0);
+	/* RB_MRT_BLEND_CONTROL0 */
+	*cmds++ = _SET(RB_MRTBLENDCONTROL_RGB_SRC_FACTOR, RB_FACTOR_ONE) |
+		_SET(RB_MRTBLENDCONTROL_RGB_BLEND_OPCODE, RB_BLEND_OP_ADD) |
+		_SET(RB_MRTBLENDCONTROL_RGB_DEST_FACTOR, RB_FACTOR_ZERO) |
+		_SET(RB_MRTBLENDCONTROL_ALPHA_SRC_FACTOR, RB_FACTOR_ONE) |
+		_SET(RB_MRTBLENDCONTROL_ALPHA_DEST_FACTOR, RB_FACTOR_ZERO) |
+		_SET(RB_MRTBLENDCONTROL_CLAMP_ENABLE, 1);
+	/* RB_MRT_CONTROL1 */
+	*cmds++ = _SET(RB_MRTCONTROL_READ_DEST_ENABLE, 1) |
+		_SET(RB_MRTCONTROL_DITHER_MODE, RB_DITHER_DISABLE) |
+		_SET(RB_MRTCONTROL_COMPONENT_ENABLE, 0xF);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(A3XX_RB_MRT_BLEND_CONTROL1);
+	/* RB_MRT_BLEND_CONTROL1 */
+	*cmds++ = _SET(RB_MRTBLENDCONTROL_RGB_SRC_FACTOR, RB_FACTOR_ONE) |
+		_SET(RB_MRTBLENDCONTROL_RGB_BLEND_OPCODE, RB_BLEND_OP_ADD) |
+		_SET(RB_MRTBLENDCONTROL_RGB_DEST_FACTOR, RB_FACTOR_ZERO) |
+		_SET(RB_MRTBLENDCONTROL_ALPHA_SRC_FACTOR, RB_FACTOR_ONE) |
+		_SET(RB_MRTBLENDCONTROL_ALPHA_DEST_FACTOR, RB_FACTOR_ZERO) |
+		_SET(RB_MRTBLENDCONTROL_CLAMP_ENABLE, 1);
+	/* RB_MRT_CONTROL2 */
+	*cmds++ = _SET(RB_MRTCONTROL_READ_DEST_ENABLE, 1) |
+		_SET(RB_MRTCONTROL_DITHER_MODE, RB_DITHER_DISABLE) |
+		_SET(RB_MRTCONTROL_COMPONENT_ENABLE, 0xF);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(A3XX_RB_MRT_BLEND_CONTROL2);
+	/* RB_MRT_BLEND_CONTROL2 */
+	*cmds++ = _SET(RB_MRTBLENDCONTROL_RGB_SRC_FACTOR, RB_FACTOR_ONE) |
+		_SET(RB_MRTBLENDCONTROL_RGB_BLEND_OPCODE, RB_BLEND_OP_ADD) |
+		_SET(RB_MRTBLENDCONTROL_RGB_DEST_FACTOR, RB_FACTOR_ZERO) |
+		_SET(RB_MRTBLENDCONTROL_ALPHA_SRC_FACTOR, RB_FACTOR_ONE) |
+		_SET(RB_MRTBLENDCONTROL_ALPHA_DEST_FACTOR, RB_FACTOR_ZERO) |
+		_SET(RB_MRTBLENDCONTROL_CLAMP_ENABLE, 1);
+	/* RB_MRT_CONTROL3 */
+	*cmds++ = _SET(RB_MRTCONTROL_READ_DEST_ENABLE, 1) |
+		_SET(RB_MRTCONTROL_DITHER_MODE, RB_DITHER_DISABLE) |
+		_SET(RB_MRTCONTROL_COMPONENT_ENABLE, 0xF);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_RB_MRT_BLEND_CONTROL3);
+	/* RB_MRT_BLEND_CONTROL3 */
+	*cmds++ = _SET(RB_MRTBLENDCONTROL_RGB_SRC_FACTOR, RB_FACTOR_ONE) |
+		_SET(RB_MRTBLENDCONTROL_RGB_BLEND_OPCODE, RB_BLEND_OP_ADD) |
+		_SET(RB_MRTBLENDCONTROL_RGB_DEST_FACTOR, RB_FACTOR_ZERO) |
+		_SET(RB_MRTBLENDCONTROL_ALPHA_SRC_FACTOR, RB_FACTOR_ONE) |
+		_SET(RB_MRTBLENDCONTROL_ALPHA_DEST_FACTOR, RB_FACTOR_ZERO) |
+		_SET(RB_MRTBLENDCONTROL_CLAMP_ENABLE, 1);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5);
+	*cmds++ = CP_REG(A3XX_VFD_INDEX_MIN);
+	/* VFD_INDEX_MIN */
+	*cmds++ = 0x00000000;
+	/* VFD_INDEX_MAX */
+	*cmds++ = 0xFFFFFFFF;
+	/* VFD_INSTANCEID_OFFSET */
+	*cmds++ = 0x00000000;
+	/* VFD_INDEX_OFFSET */
+	*cmds++ = 0x00000000;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_VFD_VS_THREADING_THRESHOLD);
+	/* VFD_VS_THREADING_THRESHOLD */
+	*cmds++ = _SET(VFD_THREADINGTHRESHOLD_RESERVED6, 12) |
+		_SET(VFD_THREADINGTHRESHOLD_REGID_VTXCNT, 252);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_TPL1_TP_VS_TEX_OFFSET);
+	/* TPL1_TP_VS_TEX_OFFSET */
+	*cmds++ = 0;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_TPL1_TP_FS_TEX_OFFSET);
+	/* TPL1_TP_FS_TEX_OFFSET */
+	*cmds++ = _SET(TPL1_TPTEXOFFSETREG_SAMPLEROFFSET, 16) |
+		_SET(TPL1_TPTEXOFFSETREG_MEMOBJOFFSET, 16) |
+		_SET(TPL1_TPTEXOFFSETREG_BASETABLEPTR, 224);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_PC_PRIM_VTX_CNTL);
+	/* PC_PRIM_VTX_CNTL */
+	*cmds++ = _SET(PC_PRIM_VTX_CONTROL_POLYMODE_FRONT_PTYPE,
+		PC_DRAW_TRIANGLES) |
+		_SET(PC_PRIM_VTX_CONTROL_POLYMODE_BACK_PTYPE,
+		PC_DRAW_TRIANGLES) |
+		_SET(PC_PRIM_VTX_CONTROL_PROVOKING_VTX_LAST, 1);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(A3XX_GRAS_SC_WINDOW_SCISSOR_TL);
+	/* GRAS_SC_WINDOW_SCISSOR_TL */
+	*cmds++ = 0x00000000;
+	/* GRAS_SC_WINDOW_SCISSOR_BR */
+	*cmds++ = _SET(GRAS_SC_WINDOW_SCISSOR_BR_BR_X, shadow->width - 1) |
+		_SET(GRAS_SC_WINDOW_SCISSOR_BR_BR_Y, shadow->height - 1);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(A3XX_GRAS_SC_SCREEN_SCISSOR_TL);
+	/* GRAS_SC_SCREEN_SCISSOR_TL */
+	*cmds++ = 0x00000000;
+	/* GRAS_SC_SCREEN_SCISSOR_BR */
+	*cmds++ = _SET(GRAS_SC_SCREEN_SCISSOR_BR_BR_X, shadow->width - 1) |
+		_SET(GRAS_SC_SCREEN_SCISSOR_BR_BR_Y, shadow->height - 1);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5);
+	*cmds++ = CP_REG(A3XX_GRAS_CL_VPORT_XOFFSET);
+	/* GRAS_CL_VPORT_XOFFSET */
+	*cmds++ = 0x00000000;
+	/* GRAS_CL_VPORT_XSCALE */
+	*cmds++ = _SET(GRAS_CL_VPORT_XSCALE_VPORT_XSCALE, 0x3f800000);
+	/* GRAS_CL_VPORT_YOFFSET */
+	*cmds++ = 0x00000000;
+	/* GRAS_CL_VPORT_YSCALE */
+	*cmds++ = _SET(GRAS_CL_VPORT_YSCALE_VPORT_YSCALE, 0x3f800000);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(A3XX_GRAS_CL_VPORT_ZOFFSET);
+	/* GRAS_CL_VPORT_ZOFFSET */
+	*cmds++ = 0x00000000;
+	/* GRAS_CL_VPORT_ZSCALE */
+	*cmds++ = _SET(GRAS_CL_VPORT_ZSCALE_VPORT_ZSCALE, 0x3f800000);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_GRAS_CL_CLIP_CNTL);
+	/* GRAS_CL_CLIP_CNTL */
+	*cmds++ = _SET(GRAS_CL_CLIP_CNTL_CLIP_DISABLE, 1) |
+		_SET(GRAS_CL_CLIP_CNTL_ZFAR_CLIP_DISABLE, 1) |
+		_SET(GRAS_CL_CLIP_CNTL_VP_CLIP_CODE_IGNORE, 1) |
+		_SET(GRAS_CL_CLIP_CNTL_VP_XFORM_DISABLE, 1) |
+		_SET(GRAS_CL_CLIP_CNTL_PERSP_DIVISION_DISABLE, 1);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_GRAS_CL_GB_CLIP_ADJ);
+	/* GRAS_CL_GB_CLIP_ADJ */
+	*cmds++ = 0x00000000;
+
+	*cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
+	*cmds++ = 0x00000000;
+
+	/*
+	 * Resolve using two draw calls with a dummy register
+	 * write in between. This is a HLM workaround
+	 * that should be removed later.
+	 */
+	*cmds++ = cp_type3_packet(CP_DRAW_INDX_2, 6);
+	*cmds++ = 0x00000000; /* Viz query info */
+	*cmds++ = BUILD_PC_DRAW_INITIATOR(PC_DI_PT_TRILIST,
+					  PC_DI_SRC_SEL_IMMEDIATE,
+					  PC_DI_INDEX_SIZE_32_BIT,
+					  PC_DI_IGNORE_VISIBILITY);
+	*cmds++ = 0x00000003; /* Num indices */
+	*cmds++ = 0x00000000; /* Index 0 */
+	*cmds++ = 0x00000001; /* Index 1 */
+	*cmds++ = 0x00000002; /* Index 2 */
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_HLSQ_CL_CONTROL_0_REG);
+	*cmds++ = 0x00000000;
+
+	*cmds++ = cp_type3_packet(CP_DRAW_INDX_2, 6);
+	*cmds++ = 0x00000000; /* Viz query info */
+	*cmds++ = BUILD_PC_DRAW_INITIATOR(PC_DI_PT_TRILIST,
+					  PC_DI_SRC_SEL_IMMEDIATE,
+					  PC_DI_INDEX_SIZE_32_BIT,
+					  PC_DI_IGNORE_VISIBILITY);
+	*cmds++ = 0x00000003; /* Num indices */
+	*cmds++ = 0x00000002; /* Index 0 */
+	*cmds++ = 0x00000001; /* Index 1 */
+	*cmds++ = 0x00000003; /* Index 2 */
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_HLSQ_CL_CONTROL_0_REG);
+	*cmds++ = 0x00000000;
+
+	*cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
+	*cmds++ = 0x00000000;
+
+	/* Create indirect buffer command for above command sequence */
+	create_ib1(drawctxt, shadow->gmem_save, start, cmds);
+
+	return cmds;
+}
+
+static void build_shader_save_cmds(struct adreno_device *adreno_dev,
+				   struct adreno_context *drawctxt)
+{
+	unsigned int *cmd = tmp_ctx.cmd;
+	unsigned int *start;
+
+	/* Reserve space for boolean values used for COND_EXEC packet */
+	drawctxt->cond_execs[0].hostptr = cmd;
+	drawctxt->cond_execs[0].gpuaddr = virt2gpu(cmd, &drawctxt->gpustate);
+	*cmd++ = 0;
+	drawctxt->cond_execs[1].hostptr = cmd;
+	drawctxt->cond_execs[1].gpuaddr = virt2gpu(cmd, &drawctxt->gpustate);
+	*cmd++ = 0;
+
+	drawctxt->shader_save_commands[0].hostptr = cmd;
+	drawctxt->shader_save_commands[0].gpuaddr =
+	    virt2gpu(cmd, &drawctxt->gpustate);
+	*cmd++ = 0;
+	drawctxt->shader_save_commands[1].hostptr = cmd;
+	drawctxt->shader_save_commands[1].gpuaddr =
+	    virt2gpu(cmd, &drawctxt->gpustate);
+	*cmd++ = 0;
+
+	start = cmd;
+
+	/* Save vertex shader */
+
+	*cmd++ = cp_type3_packet(CP_COND_EXEC, 4);
+	*cmd++ = drawctxt->cond_execs[0].gpuaddr >> 2;
+	*cmd++ = drawctxt->cond_execs[0].gpuaddr >> 2;
+	*cmd++ = 0x0000FFFF;
+	*cmd++ = 3;		/* EXEC_COUNT */
+
+	*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+	drawctxt->shader_save_commands[2].hostptr = cmd;
+	drawctxt->shader_save_commands[2].gpuaddr =
+	    virt2gpu(cmd, &drawctxt->gpustate);
+	/*
+	   From fixup:
+
+	   dwords = SP_VS_CTRL_REG0.VS_LENGTH * 8
+
+	   From regspec:
+	   SP_VS_CTRL_REG0.VS_LENGTH [31:24]: VS length, unit = 256bits.
+	   If bit31 is 1, it means overflow
+	   or any long shader.
+
+	   src = (HLSQ_SHADOW_BASE + 0x1000)/4
+	 */
+	*cmd++ = 0;	/*(dwords << REG_TO_MEM_LOOP_COUNT_SHIFT) | src */
+	*cmd++ = (drawctxt->gpustate.gpuaddr + SHADER_OFFSET) & 0xfffffffc;
+
+	/* Save fragment shader */
+	*cmd++ = cp_type3_packet(CP_COND_EXEC, 4);
+	*cmd++ = drawctxt->cond_execs[1].gpuaddr >> 2;
+	*cmd++ = drawctxt->cond_execs[1].gpuaddr >> 2;
+	*cmd++ = 0x0000FFFF;
+	*cmd++ = 3;		/* EXEC_COUNT */
+
+	*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+	drawctxt->shader_save_commands[3].hostptr = cmd;
+	drawctxt->shader_save_commands[3].gpuaddr =
+	    virt2gpu(cmd, &drawctxt->gpustate);
+	/*
+	   From fixup:
+
+	   dwords = SP_FS_CTRL_REG0.FS_LENGTH * 8
+
+	   From regspec:
+	   SP_FS_CTRL_REG0.FS_LENGTH [31:24]: FS length, unit = 256bits.
+	   If bit31 is 1, it means overflow
+	   or any long shader.
+
+	   fs_offset = SP_FS_OBJ_OFFSET_REG.SHADEROBJOFFSETINIC * 32
+	   From regspec:
+
+	   SP_FS_OBJ_OFFSET_REG.SHADEROBJOFFSETINIC [31:25]:
+	   First instruction of the whole shader will be stored from
+	   the offset in instruction cache, unit = 256bits, a cache line.
+	   It can start from 0 if no VS available.
+
+	   src = (HLSQ_SHADOW_BASE + 0x1000 + SSIZE + fs_offset)/4
+	 */
+	*cmd++ = 0;	/*(dwords << REG_TO_MEM_LOOP_COUNT_SHIFT) | src */
+	*cmd++ = (drawctxt->gpustate.gpuaddr + SHADER_OFFSET
+		  + (SHADER_SHADOW_SIZE / 2)) & 0xfffffffc;
+
+	/* Create indirect buffer command for above command sequence */
+	create_ib1(drawctxt, drawctxt->shader_save, start, cmd);
+
+	tmp_ctx.cmd = cmd;
+}
+
+/*
+ * Make an IB to modify context save IBs with the correct shader instruction
+ * and constant sizes and offsets.
+ */
+
+static void build_save_fixup_cmds(struct adreno_device *adreno_dev,
+				  struct adreno_context *drawctxt)
+{
+	unsigned int *cmd = tmp_ctx.cmd;
+	unsigned int *start = cmd;
+
+	/* Flush HLSQ lazy updates */
+	*cmd++ = cp_type3_packet(CP_EVENT_WRITE, 1);
+	*cmd++ = 0x7;		/* HLSQ_FLUSH */
+	*cmd++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
+	*cmd++ = 0;
+
+	*cmd++ = cp_type0_packet(A3XX_UCHE_CACHE_INVALIDATE0_REG, 2);
+	*cmd++ = 0x00000000; /* No start addr for full invalidate */
+	*cmd++ = (unsigned int)
+		UCHE_ENTIRE_CACHE << UCHE_INVALIDATE1REG_ALLORPORTION |
+		UCHE_OP_INVALIDATE << UCHE_INVALIDATE1REG_OPCODE |
+		0; /* No end addr for full invalidate */
+
+	/* Make sure registers are flushed */
+	*cmd++ = cp_type3_packet(CP_CONTEXT_UPDATE, 1);
+	*cmd++ = 0;
+
+#ifdef GSL_CONTEXT_SWITCH_CPU_SYNC
+
+	/* Save shader sizes */
+	*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+	*cmd++ = A3XX_SP_VS_CTRL_REG0;
+	*cmd++ = drawctxt->shader_save_commands[2].gpuaddr;
+
+	*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+	*cmd++ = A3XX_SP_FS_CTRL_REG0;
+	*cmd++ = drawctxt->shader_save_commands[3].gpuaddr;
+
+	/* Save shader offsets */
+	*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+	*cmd++ = A3XX_SP_FS_OBJ_OFFSET_REG;
+	*cmd++ = drawctxt->shader_save_commands[1].gpuaddr;
+
+	/* Save constant sizes */
+	*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+	*cmd++ = A3XX_SP_VS_CTRL_REG1;
+	*cmd++ = drawctxt->constant_save_commands[1].gpuaddr;
+	*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+	*cmd++ = A3XX_SP_FS_CTRL_REG1;
+	*cmd++ = drawctxt->constant_save_commands[2].gpuaddr;
+
+	/* Save FS constant offset */
+	*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+	*cmd++ = A3XX_SP_FS_OBJ_OFFSET_REG;
+	*cmd++ = drawctxt->constant_save_commands[0].gpuaddr;
+
+
+	/* Save VS instruction store mode */
+	*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+	*cmd++ = A3XX_SP_VS_CTRL_REG0;
+	*cmd++ = drawctxt->cond_execs[0].gpuaddr;
+
+	/* Save FS instruction store mode */
+	*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+	*cmd++ = A3XX_SP_FS_CTRL_REG0;
+	*cmd++ = drawctxt->cond_execs[1].gpuaddr;
+#else
+
+	/* Shader save */
+	cmd = rmw_regtomem(cmd, A3XX_SP_VS_CTRL_REG0, 0x7f000000,
+			11+REG_TO_MEM_LOOP_COUNT_SHIFT,
+			(HLSQ_SHADOW_BASE + 0x1000) / 4,
+			drawctxt->shader_save_commands[2].gpuaddr);
+
+	/* CP_SCRATCH_REG2 = (CP_SCRATCH_REG2 & 0x00000000) | SP_FS_CTRL_REG0 */
+	*cmd++ = cp_type3_packet(CP_REG_RMW, 3);
+	*cmd++ = (1 << 30) | A3XX_CP_SCRATCH_REG2;
+	*cmd++ = 0x00000000;	/* AND value */
+	*cmd++ = A3XX_SP_FS_CTRL_REG0;	/* OR address */
+	/* CP_SCRATCH_REG2 = ( (CP_SCRATCH_REG2 & 0x7f000000) >> 21 )
+	   |  ((HLSQ_SHADOW_BASE+0x1000+SSIZE)/4) */
+	*cmd++ = cp_type3_packet(CP_REG_RMW, 3);
+	*cmd++ = ((11 + REG_TO_MEM_LOOP_COUNT_SHIFT) << 24) |
+		A3XX_CP_SCRATCH_REG2;
+	*cmd++ = 0x7f000000;	/* AND value */
+	*cmd++ = (HLSQ_SHADOW_BASE + 0x1000 + SSIZE) / 4;	/* OR value */
+
+	/*
+	 * CP_SCRATCH_REG3 = (CP_SCRATCH_REG3 & 0x00000000) |
+	 * SP_FS_OBJ_OFFSET_REG
+	 */
+
+	*cmd++ = cp_type3_packet(CP_REG_RMW, 3);
+	*cmd++ = (1 << 30) | A3XX_CP_SCRATCH_REG3;
+	*cmd++ = 0x00000000;	/* AND value */
+	*cmd++ = A3XX_SP_FS_OBJ_OFFSET_REG;	/* OR address */
+	/*
+	 * CP_SCRATCH_REG3 = ( (CP_SCRATCH_REG3 & 0xfe000000) >> 25 ) |
+	 * 0x00000000
+	 */
+	*cmd++ = cp_type3_packet(CP_REG_RMW, 3);
+	*cmd++ = A3XX_CP_SCRATCH_REG3;
+	*cmd++ = 0xfe000000;	/* AND value */
+	*cmd++ = 0x00000000;	/* OR value */
+	/*
+	 * CP_SCRATCH_REG2 =  (CP_SCRATCH_REG2 & 0xffffffff) | CP_SCRATCH_REG3
+	 */
+	*cmd++ = cp_type3_packet(CP_REG_RMW, 3);
+	*cmd++ = (1 << 30) | A3XX_CP_SCRATCH_REG2;
+	*cmd++ = 0xffffffff;	/* AND value */
+	*cmd++ = A3XX_CP_SCRATCH_REG3;	/* OR address */
+
+	*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+	*cmd++ = A3XX_CP_SCRATCH_REG2;
+	*cmd++ = drawctxt->shader_save_commands[3].gpuaddr;
+
+	/* Constant save */
+	cmd = rmw_regtomem(cmd, A3XX_SP_VS_CTRL_REG1, 0x000003ff,
+			   2 + REG_TO_MEM_LOOP_COUNT_SHIFT,
+			   (HLSQ_SHADOW_BASE + 0x2000) / 4,
+			   drawctxt->constant_save_commands[1].gpuaddr);
+
+	cmd = rmw_regtomem(cmd, A3XX_SP_FS_CTRL_REG1, 0x000003ff,
+			   2 + REG_TO_MEM_LOOP_COUNT_SHIFT,
+			   (HLSQ_SHADOW_BASE + 0x2000 + SSIZE) / 4,
+			   drawctxt->constant_save_commands[2].gpuaddr);
+
+	cmd = rmw_regtomem(cmd, A3XX_SP_FS_OBJ_OFFSET_REG, 0x00ff0000,
+			   18, drawctxt->gpustate.gpuaddr & 0xfffffe00,
+			   drawctxt->constant_save_commands[2].gpuaddr
+			   + sizeof(unsigned int));
+
+	/* Modify constant save conditionals */
+	cmd = rmw_regtomem(cmd, A3XX_SP_VS_CTRL_REG1, 0x000003ff,
+		0, 0, drawctxt->cond_execs[2].gpuaddr);
+
+	cmd = rmw_regtomem(cmd, A3XX_SP_FS_CTRL_REG1, 0x000003ff,
+		0, 0, drawctxt->cond_execs[3].gpuaddr);
+
+	/* Save VS instruction store mode */
+
+	cmd = rmw_regtomem(cmd, A3XX_SP_VS_CTRL_REG0, 0x00000002,
+			   31, 0, drawctxt->cond_execs[0].gpuaddr);
+
+	/* Save FS instruction store mode */
+	cmd = rmw_regtomem(cmd, A3XX_SP_FS_CTRL_REG0, 0x00000002,
+			   31, 0, drawctxt->cond_execs[1].gpuaddr);
+
+#endif
+
+	create_ib1(drawctxt, drawctxt->save_fixup, start, cmd);
+
+	tmp_ctx.cmd = cmd;
+}
+
+/****************************************************************************/
+/* Functions to build context restore IBs                                   */
+/****************************************************************************/
+
+static unsigned int *build_sys2gmem_cmds(struct adreno_device *adreno_dev,
+					 struct adreno_context *drawctxt,
+					 struct gmem_shadow_t *shadow)
+{
+	unsigned int *cmds = tmp_ctx.cmd;
+	unsigned int *start = cmds;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5);
+	*cmds++ = CP_REG(A3XX_HLSQ_CONTROL_0_REG);
+	/* HLSQ_CONTROL_0_REG */
+	*cmds++ = _SET(HLSQ_CTRL0REG_FSTHREADSIZE, HLSQ_FOUR_PIX_QUADS) |
+		_SET(HLSQ_CTRL0REG_SPSHADERRESTART, 1) |
+		_SET(HLSQ_CTRL0REG_CHUNKDISABLE, 1) |
+		_SET(HLSQ_CTRL0REG_SPCONSTFULLUPDATE, 1) |
+		_SET(HLSQ_CTRL0REG_TPFULLUPDATE, 1);
+	/* HLSQ_CONTROL_1_REG */
+	*cmds++ = _SET(HLSQ_CTRL1REG_VSTHREADSIZE, HLSQ_TWO_VTX_QUADS);
+	/* HLSQ_CONTROL_2_REG */
+	*cmds++ = _SET(HLSQ_CTRL2REG_PRIMALLOCTHRESHOLD, 31);
+	/* HLSQ_CONTROL3_REG */
+	*cmds++ = 0x00000000;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(A3XX_RB_MRT_BUF_INFO0);
+	/* RB_MRT_BUF_INFO0 */
+	*cmds++ = _SET(RB_MRTBUFINFO_COLOR_FORMAT, RB_R8G8B8A8_UNORM) |
+		_SET(RB_MRTBUFINFO_COLOR_TILE_MODE, RB_TILINGMODE_32X32) |
+		_SET(RB_MRTBUFINFO_COLOR_BUF_PITCH,
+		(shadow->gmem_pitch * 4 * 8) / 256);
+	/* RB_MRT_BUF_BASE0 */
+	*cmds++ = _SET(RB_MRTBUFBASE_COLOR_BUF_BASE, tmp_ctx.gmem_base >> 5);
+
+	/* Texture samplers */
+	*cmds++ = cp_type3_packet(CP_LOAD_STATE, 4);
+	*cmds++ = (16 << CP_LOADSTATE_DSTOFFSET_SHIFT)
+		| (HLSQ_DIRECT << CP_LOADSTATE_STATESRC_SHIFT)
+		| (HLSQ_BLOCK_ID_TP_TEX << CP_LOADSTATE_STATEBLOCKID_SHIFT)
+		| (1 << CP_LOADSTATE_NUMOFUNITS_SHIFT);
+	*cmds++ = (HLSQ_TP_TEX_SAMPLERS << CP_LOADSTATE_STATETYPE_SHIFT)
+		| (0 << CP_LOADSTATE_EXTSRCADDR_SHIFT);
+	*cmds++ = 0x00000240;
+	*cmds++ = 0x00000000;
+
+	/* Texture memobjs */
+	*cmds++ = cp_type3_packet(CP_LOAD_STATE, 6);
+	*cmds++ = (16 << CP_LOADSTATE_DSTOFFSET_SHIFT)
+		| (HLSQ_DIRECT << CP_LOADSTATE_STATESRC_SHIFT)
+		| (HLSQ_BLOCK_ID_TP_TEX << CP_LOADSTATE_STATEBLOCKID_SHIFT)
+		| (1 << CP_LOADSTATE_NUMOFUNITS_SHIFT);
+	*cmds++ = (HLSQ_TP_TEX_MEMOBJ << CP_LOADSTATE_STATETYPE_SHIFT)
+		| (0 << CP_LOADSTATE_EXTSRCADDR_SHIFT);
+	*cmds++ = 0x4cc06880;
+	*cmds++ = shadow->height | (shadow->width << 14);
+	*cmds++ = (shadow->pitch*4*8) << 9;
+	*cmds++ = 0x00000000;
+
+	/* Mipmap bases */
+	*cmds++ = cp_type3_packet(CP_LOAD_STATE, 16);
+	*cmds++ = (224 << CP_LOADSTATE_DSTOFFSET_SHIFT)
+		| (HLSQ_DIRECT << CP_LOADSTATE_STATESRC_SHIFT)
+		| (HLSQ_BLOCK_ID_TP_MIPMAP << CP_LOADSTATE_STATEBLOCKID_SHIFT)
+		| (14 << CP_LOADSTATE_NUMOFUNITS_SHIFT);
+	*cmds++ = (HLSQ_TP_MIPMAP_BASE << CP_LOADSTATE_STATETYPE_SHIFT)
+		| (0 << CP_LOADSTATE_EXTSRCADDR_SHIFT);
+	*cmds++ = shadow->gmemshadow.gpuaddr;
+	*cmds++ = 0x00000000;
+	*cmds++ = 0x00000000;
+	*cmds++ = 0x00000000;
+	*cmds++ = 0x00000000;
+	*cmds++ = 0x00000000;
+	*cmds++ = 0x00000000;
+	*cmds++ = 0x00000000;
+	*cmds++ = 0x00000000;
+	*cmds++ = 0x00000000;
+	*cmds++ = 0x00000000;
+	*cmds++ = 0x00000000;
+	*cmds++ = 0x00000000;
+	*cmds++ = 0x00000000;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5);
+	*cmds++ = CP_REG(A3XX_HLSQ_VS_CONTROL_REG);
+	/* HLSQ_VS_CONTROL_REG */
+	*cmds++ = _SET(HLSQ_VSCTRLREG_VSINSTRLENGTH, 1);
+	/* HLSQ_FS_CONTROL_REG */
+	*cmds++ = _SET(HLSQ_FSCTRLREG_FSCONSTLENGTH, 1) |
+		_SET(HLSQ_FSCTRLREG_FSCONSTSTARTOFFSET, 128) |
+		_SET(HLSQ_FSCTRLREG_FSINSTRLENGTH, 2);
+	/* HLSQ_CONST_VSPRESV_RANGE_REG */
+	*cmds++ = 0x00000000;
+	/* HLSQ_CONST_FSPRESV_RANGE_REG */
+	*cmds++ = 0x00000000;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_SP_FS_LENGTH_REG);
+	/* SP_FS_LENGTH_REG */
+	*cmds++ = _SET(SP_SHADERLENGTH_LEN, 2);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 12);
+	*cmds++ = CP_REG(A3XX_SP_VS_CTRL_REG0);
+	/* SP_VS_CTRL_REG0 */
+	*cmds++ = _SET(SP_VSCTRLREG0_VSTHREADMODE, SP_MULTI) |
+		_SET(SP_VSCTRLREG0_VSINSTRBUFFERMODE, SP_BUFFER_MODE) |
+		_SET(SP_VSCTRLREG0_VSICACHEINVALID, 1) |
+		_SET(SP_VSCTRLREG0_VSFULLREGFOOTPRINT, 2) |
+		_SET(SP_VSCTRLREG0_VSTHREADSIZE, SP_TWO_VTX_QUADS) |
+		_SET(SP_VSCTRLREG0_VSLENGTH, 1);
+	/* SP_VS_CTRL_REG1 */
+	*cmds++ = _SET(SP_VSCTRLREG1_VSINITIALOUTSTANDING, 8);
+	/* SP_VS_PARAM_REG */
+	*cmds++ = _SET(SP_VSPARAMREG_POSREGID, 4) |
+		_SET(SP_VSPARAMREG_PSIZEREGID, 252) |
+		_SET(SP_VSPARAMREG_TOTALVSOUTVAR, 1);
+	/* SP_VS_OUT_REG0 */
+	*cmds++ = _SET(SP_VSOUTREG_COMPMASK0, 3);
+	/* SP_VS_OUT_REG1 */
+	*cmds++ = 0x00000000;
+	/* SP_VS_OUT_REG2 */
+	*cmds++ = 0x00000000;
+	/* SP_VS_OUT_REG3 */
+	*cmds++ = 0x00000000;
+	/* SP_VS_OUT_REG4 */
+	*cmds++ = 0x00000000;
+	/* SP_VS_OUT_REG5 */
+	*cmds++ = 0x00000000;
+	/* SP_VS_OUT_REG6 */
+	*cmds++ = 0x00000000;
+	/* SP_VS_OUT_REG7 */
+	*cmds++ = 0x00000000;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 7);
+	*cmds++ = CP_REG(A3XX_SP_VS_VPC_DST_REG_0);
+	/* SP_VS_VPC_DST_REG0 */
+	*cmds++ = _SET(SP_VSVPCDSTREG_OUTLOC0, 8);
+	/* SP_VS_VPC_DST_REG1 */
+	*cmds++ = 0x00000000;
+	/* SP_VS_VPC_DST_REG2 */
+	*cmds++ = 0x00000000;
+	/* SP_VS_VPC_DST_REG3 */
+	*cmds++ = 0x00000000;
+	/* SP_VS_OBJ_OFFSET_REG */
+	*cmds++ = 0x00000000;
+	/* SP_VS_OBJ_START_REG */
+	*cmds++ = 0x00000000;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 6);
+	*cmds++ = CP_REG(A3XX_SP_VS_LENGTH_REG);
+	/* SP_VS_LENGTH_REG */
+	*cmds++ = _SET(SP_SHADERLENGTH_LEN, 1);
+	/* SP_FS_CTRL_REG0 */
+	*cmds++ = _SET(SP_FSCTRLREG0_FSTHREADMODE, SP_MULTI) |
+		_SET(SP_FSCTRLREG0_FSINSTRBUFFERMODE, SP_BUFFER_MODE) |
+		_SET(SP_FSCTRLREG0_FSICACHEINVALID, 1) |
+		_SET(SP_FSCTRLREG0_FSFULLREGFOOTPRINT, 2) |
+		_SET(SP_FSCTRLREG0_FSINOUTREGOVERLAP, 1) |
+		_SET(SP_FSCTRLREG0_FSTHREADSIZE, SP_FOUR_PIX_QUADS) |
+		_SET(SP_FSCTRLREG0_PIXLODENABLE, 1) |
+		_SET(SP_FSCTRLREG0_FSLENGTH, 2);
+	/* SP_FS_CTRL_REG1 */
+	*cmds++ = _SET(SP_FSCTRLREG1_FSCONSTLENGTH, 1) |
+		_SET(SP_FSCTRLREG1_FSINITIALOUTSTANDING, 2) |
+		_SET(SP_FSCTRLREG1_HALFPRECVAROFFSET, 63);
+	/* SP_FS_OBJ_OFFSET_REG */
+	*cmds++ = _SET(SP_OBJOFFSETREG_CONSTOBJECTSTARTOFFSET, 128) |
+		_SET(SP_OBJOFFSETREG_SHADEROBJOFFSETINIC, 1);
+	/* SP_FS_OBJ_START_REG */
+	*cmds++ = 0x00000000;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(A3XX_SP_FS_FLAT_SHAD_MODE_REG_0);
+	/* SP_FS_FLAT_SHAD_MODE_REG0 */
+	*cmds++ = 0x00000000;
+	/* SP_FS_FLAT_SHAD_MODE_REG1 */
+	*cmds++ = 0x00000000;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_SP_FS_OUTPUT_REG);
+	/* SP_FS_OUT_REG */
+	*cmds++ = _SET(SP_FSOUTREG_PAD0, SP_PIXEL_BASED);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5);
+	*cmds++ = CP_REG(A3XX_SP_FS_MRT_REG_0);
+	/* SP_FS_MRT_REG0 */
+	*cmds++ = _SET(SP_FSMRTREG_REGID, 4);
+	/* SP_FS_MRT_REG1 */
+	*cmds++ = 0;
+	/* SP_FS_MRT_REG2 */
+	*cmds++ = 0;
+	/* SP_FS_MRT_REG3 */
+	*cmds++ = 0;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 11);
+	*cmds++ = CP_REG(A3XX_VPC_ATTR);
+	/* VPC_ATTR */
+	*cmds++ = _SET(VPC_VPCATTR_TOTALATTR, 2) |
+		_SET(VPC_VPCATTR_THRHDASSIGN, 1) |
+		_SET(VPC_VPCATTR_LMSIZE, 1);
+	/* VPC_PACK */
+	*cmds++ = _SET(VPC_VPCPACK_NUMFPNONPOSVAR, 2) |
+		_SET(VPC_VPCPACK_NUMNONPOSVSVAR, 2);
+	/* VPC_VARYING_INTERP_MODE_0 */
+	*cmds++ = 0x00000000;
+	/* VPC_VARYING_INTERP_MODE1 */
+	*cmds++ = 0x00000000;
+	/* VPC_VARYING_INTERP_MODE2 */
+	*cmds++ = 0x00000000;
+	/* VPC_VARYING_IINTERP_MODE3 */
+	*cmds++ = 0x00000000;
+	/* VPC_VARRYING_PS_REPL_MODE_0 */
+	*cmds++ = _SET(VPC_VPCVARPSREPLMODE_COMPONENT08, 1) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT09, 2) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT0A,	1) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT0B, 2) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT0C, 1) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT0D, 2) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT0E, 1) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT0F, 2) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT10, 1) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT11, 2) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT12, 1) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT13, 2) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT14, 1) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT15, 2) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT16, 1) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT17, 2);
+	/* VPC_VARRYING_PS_REPL_MODE_1 */
+	*cmds++ = _SET(VPC_VPCVARPSREPLMODE_COMPONENT08, 1) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT09, 2) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT0A,	1) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT0B, 2) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT0C, 1) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT0D, 2) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT0E, 1) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT0F, 2) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT10, 1) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT11, 2) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT12, 1) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT13, 2) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT14, 1) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT15, 2) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT16, 1) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT17, 2);
+	/* VPC_VARRYING_PS_REPL_MODE_2 */
+	*cmds++ = _SET(VPC_VPCVARPSREPLMODE_COMPONENT08, 1) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT09, 2) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT0A,	1) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT0B, 2) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT0C, 1) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT0D, 2) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT0E, 1) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT0F, 2) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT10, 1) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT11, 2) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT12, 1) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT13, 2) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT14, 1) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT15, 2) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT16, 1) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT17, 2);
+	/* VPC_VARRYING_PS_REPL_MODE_3 */
+	*cmds++ = _SET(VPC_VPCVARPSREPLMODE_COMPONENT08, 1) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT09, 2) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT0A,	1) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT0B, 2) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT0C, 1) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT0D, 2) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT0E, 1) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT0F, 2) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT10, 1) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT11, 2) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT12, 1) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT13, 2) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT14, 1) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT15, 2) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT16, 1) |
+		_SET(VPC_VPCVARPSREPLMODE_COMPONENT17, 2);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_SP_SP_CTRL_REG);
+	/* SP_SP_CTRL_REG */
+	*cmds++ = _SET(SP_SPCTRLREG_SLEEPMODE, 1);
+
+	/* Load vertex shader */
+	*cmds++ = cp_type3_packet(CP_LOAD_STATE, 10);
+	*cmds++ = (0 << CP_LOADSTATE_DSTOFFSET_SHIFT)
+		| (HLSQ_DIRECT << CP_LOADSTATE_STATESRC_SHIFT)
+		| (HLSQ_BLOCK_ID_SP_VS << CP_LOADSTATE_STATEBLOCKID_SHIFT)
+		| (1 << CP_LOADSTATE_NUMOFUNITS_SHIFT);
+	*cmds++ = (HLSQ_SP_VS_INSTR << CP_LOADSTATE_STATETYPE_SHIFT)
+		| (0 << CP_LOADSTATE_EXTSRCADDR_SHIFT);
+	/* (sy)end; */
+	*cmds++ = 0x00000000; *cmds++ = 0x13000000;
+	/* nop; */
+	*cmds++ = 0x00000000; *cmds++ = 0x00000000;
+	/* nop; */
+	*cmds++ = 0x00000000; *cmds++ = 0x00000000;
+	/* nop; */
+	*cmds++ = 0x00000000; *cmds++ = 0x00000000;
+
+	/* Load fragment shader */
+	*cmds++ = cp_type3_packet(CP_LOAD_STATE, 18);
+	*cmds++ = (0 << CP_LOADSTATE_DSTOFFSET_SHIFT)
+		| (HLSQ_DIRECT << CP_LOADSTATE_STATESRC_SHIFT)
+		| (HLSQ_BLOCK_ID_SP_FS << CP_LOADSTATE_STATEBLOCKID_SHIFT)
+		| (2 << CP_LOADSTATE_NUMOFUNITS_SHIFT);
+	*cmds++ = (HLSQ_SP_FS_INSTR << CP_LOADSTATE_STATETYPE_SHIFT)
+		| (0 << CP_LOADSTATE_EXTSRCADDR_SHIFT);
+	/* (sy)(rpt1)bary.f (ei)r0.z, (r)0, r0.x; */
+	*cmds++ = 0x00002000; *cmds++ = 0x57368902;
+	/* (rpt5)nop; */
+	*cmds++ = 0x00000000; *cmds++ = 0x00000500;
+	/* sam (f32)r0.xyzw, r0.z, s#0, t#0; */
+	*cmds++ = 0x00000005; *cmds++ = 0xa0c01f00;
+	/* (sy)mov.f32f32 r1.x, r0.x; */
+	*cmds++ = 0x00000000; *cmds++ = 0x30044004;
+	/* mov.f32f32 r1.y, r0.y; */
+	*cmds++ = 0x00000001; *cmds++ = 0x20044005;
+	/* mov.f32f32 r1.z, r0.z; */
+	*cmds++ = 0x00000002; *cmds++ = 0x20044006;
+	/* mov.f32f32 r1.w, r0.w; */
+	*cmds++ = 0x00000003; *cmds++ = 0x20044007;
+	/* end; */
+	*cmds++ = 0x00000000; *cmds++ = 0x03000000;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(A3XX_VFD_CONTROL_0);
+	/* VFD_CONTROL_0 */
+	*cmds++ = _SET(VFD_CTRLREG0_TOTALATTRTOVS, 8) |
+		_SET(VFD_CTRLREG0_PACKETSIZE, 2) |
+		_SET(VFD_CTRLREG0_STRMDECINSTRCNT, 2) |
+		_SET(VFD_CTRLREG0_STRMFETCHINSTRCNT, 2);
+	/* VFD_CONTROL_1 */
+	*cmds++ =  _SET(VFD_CTRLREG1_MAXSTORAGE, 2) |
+		_SET(VFD_CTRLREG1_REGID4VTX, 252) |
+		_SET(VFD_CTRLREG1_REGID4INST, 252);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5);
+	*cmds++ = CP_REG(A3XX_VFD_FETCH_INSTR_0_0);
+	/* VFD_FETCH_INSTR_0_0 */
+	*cmds++ = _SET(VFD_FETCHINSTRUCTIONS_FETCHSIZE, 7) |
+		_SET(VFD_FETCHINSTRUCTIONS_BUFSTRIDE, 8) |
+		_SET(VFD_FETCHINSTRUCTIONS_SWITCHNEXT, 1) |
+		_SET(VFD_FETCHINSTRUCTIONS_STEPRATE, 1);
+	/* VFD_FETCH_INSTR_1_0 */
+	*cmds++ = _SET(VFD_BASEADDR_BASEADDR,
+		shadow->quad_vertices_restore.gpuaddr);
+	/* VFD_FETCH_INSTR_0_1 */
+	*cmds++ = _SET(VFD_FETCHINSTRUCTIONS_FETCHSIZE, 11) |
+		_SET(VFD_FETCHINSTRUCTIONS_BUFSTRIDE, 12) |
+		_SET(VFD_FETCHINSTRUCTIONS_INDEXDECODE, 1) |
+		_SET(VFD_FETCHINSTRUCTIONS_STEPRATE, 1);
+	/* VFD_FETCH_INSTR_1_1 */
+	*cmds++ = _SET(VFD_BASEADDR_BASEADDR,
+		shadow->quad_vertices_restore.gpuaddr + 16);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(A3XX_VFD_DECODE_INSTR_0);
+	/* VFD_DECODE_INSTR_0 */
+	*cmds++ = _SET(VFD_DECODEINSTRUCTIONS_WRITEMASK, 0x0F) |
+		_SET(VFD_DECODEINSTRUCTIONS_CONSTFILL, 1) |
+		_SET(VFD_DECODEINSTRUCTIONS_FORMAT, 1) |
+		_SET(VFD_DECODEINSTRUCTIONS_SHIFTCNT, 8) |
+		_SET(VFD_DECODEINSTRUCTIONS_LASTCOMPVALID, 1) |
+		_SET(VFD_DECODEINSTRUCTIONS_SWITCHNEXT, 1);
+	/* VFD_DECODE_INSTR_1 */
+	*cmds++ = _SET(VFD_DECODEINSTRUCTIONS_WRITEMASK, 0x0F) |
+		_SET(VFD_DECODEINSTRUCTIONS_CONSTFILL, 1) |
+		_SET(VFD_DECODEINSTRUCTIONS_FORMAT, 2) |
+		_SET(VFD_DECODEINSTRUCTIONS_REGID, 4) |
+		_SET(VFD_DECODEINSTRUCTIONS_SHIFTCNT, 12) |
+		_SET(VFD_DECODEINSTRUCTIONS_LASTCOMPVALID, 1);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_RB_DEPTH_CONTROL);
+	/* RB_DEPTH_CONTROL */
+	*cmds++ = _SET(RB_DEPTHCONTROL_Z_TEST_FUNC, RB_FRAG_NEVER);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_RB_STENCIL_CONTROL);
+	/* RB_STENCIL_CONTROL */
+	*cmds++ = _SET(RB_STENCILCONTROL_STENCIL_FUNC, RB_REF_NEVER) |
+		_SET(RB_STENCILCONTROL_STENCIL_FAIL, RB_STENCIL_KEEP) |
+		_SET(RB_STENCILCONTROL_STENCIL_ZPASS, RB_STENCIL_KEEP) |
+		_SET(RB_STENCILCONTROL_STENCIL_ZFAIL, RB_STENCIL_KEEP) |
+		_SET(RB_STENCILCONTROL_STENCIL_FUNC_BF, RB_REF_NEVER) |
+		_SET(RB_STENCILCONTROL_STENCIL_FAIL_BF, RB_STENCIL_KEEP) |
+		_SET(RB_STENCILCONTROL_STENCIL_ZPASS_BF, RB_STENCIL_KEEP) |
+		_SET(RB_STENCILCONTROL_STENCIL_ZFAIL_BF, RB_STENCIL_KEEP);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_RB_MODE_CONTROL);
+	/* RB_MODE_CONTROL */
+	*cmds++ = _SET(RB_MODECONTROL_RENDER_MODE, RB_RENDERING_PASS) |
+		_SET(RB_MODECONTROL_MARB_CACHE_SPLIT_MODE, 1);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_RB_RENDER_CONTROL);
+	/* RB_RENDER_CONTROL */
+	*cmds++ = _SET(RB_RENDERCONTROL_BIN_WIDTH, shadow->width >> 5) |
+		_SET(RB_RENDERCONTROL_ALPHA_TEST_FUNC, 7);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_RB_MSAA_CONTROL);
+	/* RB_MSAA_CONTROL */
+	*cmds++ = _SET(RB_MSAACONTROL_MSAA_DISABLE, 1) |
+		_SET(RB_MSAACONTROL_SAMPLE_MASK, 0xFFFF);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_RB_MRT_CONTROL0);
+	/* RB_MRT_CONTROL0 */
+	*cmds++ = _SET(RB_MRTCONTROL_READ_DEST_ENABLE, 1) |
+		_SET(RB_MRTCONTROL_ROP_CODE, 12) |
+		_SET(RB_MRTCONTROL_DITHER_MODE, RB_DITHER_ALWAYS) |
+		_SET(RB_MRTCONTROL_COMPONENT_ENABLE, 0xF);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(A3XX_RB_MRT_BLEND_CONTROL0);
+	/* RB_MRT_BLENDCONTROL0 */
+	*cmds++ = _SET(RB_MRTBLENDCONTROL_RGB_SRC_FACTOR, RB_FACTOR_ONE) |
+		_SET(RB_MRTBLENDCONTROL_RGB_BLEND_OPCODE, RB_BLEND_OP_ADD) |
+		_SET(RB_MRTBLENDCONTROL_RGB_DEST_FACTOR, RB_FACTOR_ZERO) |
+		_SET(RB_MRTBLENDCONTROL_ALPHA_SRC_FACTOR, RB_FACTOR_ONE) |
+		_SET(RB_MRTBLENDCONTROL_ALPHA_BLEND_OPCODE, RB_BLEND_OP_ADD) |
+		_SET(RB_MRTBLENDCONTROL_ALPHA_DEST_FACTOR, RB_FACTOR_ZERO) |
+		_SET(RB_MRTBLENDCONTROL_CLAMP_ENABLE, 1);
+	/* RB_MRT_CONTROL1 */
+	*cmds++ = _SET(RB_MRTCONTROL_READ_DEST_ENABLE, 1) |
+		_SET(RB_MRTCONTROL_DITHER_MODE, RB_DITHER_DISABLE) |
+		_SET(RB_MRTCONTROL_COMPONENT_ENABLE, 0xF);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(A3XX_RB_MRT_BLEND_CONTROL1);
+	/* RB_MRT_BLENDCONTROL1 */
+	*cmds++ = _SET(RB_MRTBLENDCONTROL_RGB_SRC_FACTOR, RB_FACTOR_ONE) |
+		_SET(RB_MRTBLENDCONTROL_RGB_BLEND_OPCODE, RB_BLEND_OP_ADD) |
+		_SET(RB_MRTBLENDCONTROL_RGB_DEST_FACTOR, RB_FACTOR_ZERO) |
+		_SET(RB_MRTBLENDCONTROL_ALPHA_SRC_FACTOR, RB_FACTOR_ONE) |
+		_SET(RB_MRTBLENDCONTROL_ALPHA_BLEND_OPCODE, RB_BLEND_OP_ADD) |
+		_SET(RB_MRTBLENDCONTROL_ALPHA_DEST_FACTOR, RB_FACTOR_ZERO) |
+		_SET(RB_MRTBLENDCONTROL_CLAMP_ENABLE, 1);
+	/* RB_MRT_CONTROL2 */
+	*cmds++ = _SET(RB_MRTCONTROL_READ_DEST_ENABLE, 1) |
+		_SET(RB_MRTCONTROL_DITHER_MODE, RB_DITHER_DISABLE) |
+		_SET(RB_MRTCONTROL_COMPONENT_ENABLE, 0xF);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(A3XX_RB_MRT_BLEND_CONTROL2);
+	/* RB_MRT_BLENDCONTROL2 */
+	*cmds++ = _SET(RB_MRTBLENDCONTROL_RGB_SRC_FACTOR, RB_FACTOR_ONE) |
+		_SET(RB_MRTBLENDCONTROL_RGB_BLEND_OPCODE, RB_BLEND_OP_ADD) |
+		_SET(RB_MRTBLENDCONTROL_RGB_DEST_FACTOR, RB_FACTOR_ZERO) |
+		_SET(RB_MRTBLENDCONTROL_ALPHA_SRC_FACTOR, RB_FACTOR_ONE) |
+		_SET(RB_MRTBLENDCONTROL_ALPHA_BLEND_OPCODE, RB_BLEND_OP_ADD) |
+		_SET(RB_MRTBLENDCONTROL_ALPHA_DEST_FACTOR, RB_FACTOR_ZERO) |
+		_SET(RB_MRTBLENDCONTROL_CLAMP_ENABLE, 1);
+	/* RB_MRT_CONTROL3 */
+	*cmds++ = _SET(RB_MRTCONTROL_READ_DEST_ENABLE, 1) |
+		_SET(RB_MRTCONTROL_DITHER_MODE, RB_DITHER_DISABLE) |
+		_SET(RB_MRTCONTROL_COMPONENT_ENABLE, 0xF);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_RB_MRT_BLEND_CONTROL3);
+	/* RB_MRT_BLENDCONTROL3 */
+	*cmds++ = _SET(RB_MRTBLENDCONTROL_RGB_SRC_FACTOR, RB_FACTOR_ONE) |
+		_SET(RB_MRTBLENDCONTROL_RGB_BLEND_OPCODE, RB_BLEND_OP_ADD) |
+		_SET(RB_MRTBLENDCONTROL_RGB_DEST_FACTOR, RB_FACTOR_ZERO) |
+		_SET(RB_MRTBLENDCONTROL_ALPHA_SRC_FACTOR, RB_FACTOR_ONE) |
+		_SET(RB_MRTBLENDCONTROL_ALPHA_BLEND_OPCODE, RB_BLEND_OP_ADD) |
+		_SET(RB_MRTBLENDCONTROL_ALPHA_DEST_FACTOR, RB_FACTOR_ZERO) |
+		_SET(RB_MRTBLENDCONTROL_CLAMP_ENABLE, 1);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5);
+	*cmds++ = CP_REG(A3XX_VFD_INDEX_MIN);
+	/* VFD_INDEX_MIN */
+	*cmds++ = 0x00000000;
+	/* VFD_INDEX_MAX */
+	*cmds++ = 0xFFFFFFFF;
+	/* VFD_INDEX_OFFSET */
+	*cmds++ = 0x00000000;
+	/* TPL1_TP_VS_TEX_OFFSET */
+	*cmds++ = 0x00000000;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_VFD_VS_THREADING_THRESHOLD);
+	/* VFD_VS_THREADING_THRESHOLD */
+	*cmds++ = _SET(VFD_THREADINGTHRESHOLD_RESERVED6, 12) |
+		_SET(VFD_THREADINGTHRESHOLD_REGID_VTXCNT, 252);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_TPL1_TP_VS_TEX_OFFSET);
+	/* TPL1_TP_VS_TEX_OFFSET */
+	*cmds++ = 0x00000000;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_TPL1_TP_FS_TEX_OFFSET);
+	/* TPL1_TP_FS_TEX_OFFSET */
+	*cmds++ = _SET(TPL1_TPTEXOFFSETREG_SAMPLEROFFSET, 16) |
+		_SET(TPL1_TPTEXOFFSETREG_MEMOBJOFFSET, 16) |
+		_SET(TPL1_TPTEXOFFSETREG_BASETABLEPTR, 224);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_GRAS_SC_CONTROL);
+	/* GRAS_SC_CONTROL */
+	/*cmds++ = _SET(GRAS_SC_CONTROL_RASTER_MODE, 1);
+		*cmds++ = _SET(GRAS_SC_CONTROL_RASTER_MODE, 1) |*/
+	*cmds++ = 0x04001000;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_GRAS_SU_MODE_CONTROL);
+	/* GRAS_SU_MODE_CONTROL */
+	*cmds++ = 0x00000000;
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(A3XX_GRAS_SC_WINDOW_SCISSOR_TL);
+	/* GRAS_SC_WINDOW_SCISSOR_TL */
+	*cmds++ = 0x00000000;
+	/* GRAS_SC_WINDOW_SCISSOR_BR */
+	*cmds++ = _SET(GRAS_SC_WINDOW_SCISSOR_BR_BR_X, shadow->width - 1) |
+		_SET(GRAS_SC_WINDOW_SCISSOR_BR_BR_Y, shadow->height - 1);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(A3XX_GRAS_SC_SCREEN_SCISSOR_TL);
+	/* GRAS_SC_SCREEN_SCISSOR_TL */
+	*cmds++ = 0x00000000;
+	/* GRAS_SC_SCREEN_SCISSOR_BR */
+	*cmds++ = _SET(GRAS_SC_SCREEN_SCISSOR_BR_BR_X, shadow->width - 1) |
+		_SET(GRAS_SC_SCREEN_SCISSOR_BR_BR_Y, shadow->height - 1);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5);
+	*cmds++ = CP_REG(A3XX_GRAS_CL_VPORT_XOFFSET);
+	/* GRAS_CL_VPORT_XOFFSET */
+	*cmds++ = 0x00000000;
+	/* GRAS_CL_VPORT_XSCALE */
+	*cmds++ = _SET(GRAS_CL_VPORT_XSCALE_VPORT_XSCALE, 0x3F800000);
+	/* GRAS_CL_VPORT_YOFFSET */
+	*cmds++ = 0x00000000;
+	/* GRAS_CL_VPORT_YSCALE */
+	*cmds++ = _SET(GRAS_CL_VPORT_YSCALE_VPORT_YSCALE, 0x3F800000);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
+	*cmds++ = CP_REG(A3XX_GRAS_CL_VPORT_ZOFFSET);
+	/* GRAS_CL_VPORT_ZOFFSET */
+	*cmds++ = 0x00000000;
+	/* GRAS_CL_VPORT_ZSCALE */
+	*cmds++ = _SET(GRAS_CL_VPORT_ZSCALE_VPORT_ZSCALE, 0x3F800000);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_GRAS_CL_CLIP_CNTL);
+	/* GRAS_CL_CLIP_CNTL */
+	*cmds++ = _SET(GRAS_CL_CLIP_CNTL_IJ_PERSP_CENTER, 1);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_SP_FS_IMAGE_OUTPUT_REG_0);
+	/* SP_FS_IMAGE_OUTPUT_REG_0 */
+	*cmds++ = _SET(SP_IMAGEOUTPUTREG_MRTFORMAT, SP_R8G8B8A8_UNORM);
+
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_PC_PRIM_VTX_CNTL);
+	/* PC_PRIM_VTX_CONTROL */
+	*cmds++ = _SET(PC_PRIM_VTX_CONTROL_STRIDE_IN_VPC, 2) |
+		_SET(PC_PRIM_VTX_CONTROL_POLYMODE_FRONT_PTYPE,
+		PC_DRAW_TRIANGLES) |
+		_SET(PC_PRIM_VTX_CONTROL_POLYMODE_BACK_PTYPE,
+		PC_DRAW_TRIANGLES) |
+		_SET(PC_PRIM_VTX_CONTROL_PROVOKING_VTX_LAST, 1);
+
+	*cmds++ = cp_type3_packet(CP_DRAW_INDX, 3);
+	*cmds++ = 0x00000000; /* Viz query info */
+	*cmds++ = BUILD_PC_DRAW_INITIATOR(PC_DI_PT_RECTLIST,
+					  PC_DI_SRC_SEL_AUTO_INDEX,
+					  PC_DI_INDEX_SIZE_16_BIT,
+					  PC_DI_IGNORE_VISIBILITY);
+	*cmds++ = 0x00000002; /* Num indices */
+
+	/* Create indirect buffer command for above command sequence */
+	create_ib1(drawctxt, shadow->gmem_restore, start, cmds);
+
+	return cmds;
+}
+
+static void build_regrestore_cmds(struct adreno_device *adreno_dev,
+				  struct adreno_context *drawctxt)
+{
+	unsigned int *start = tmp_ctx.cmd;
+	unsigned int *cmd = start;
+	unsigned int *lcc_start;
+
+	int i;
+
+	/* Flush HLSQ lazy updates */
+	*cmd++ = cp_type3_packet(CP_EVENT_WRITE, 1);
+	*cmd++ = 0x7;		/* HLSQ_FLUSH */
+	*cmd++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
+	*cmd++ = 0;
+
+	*cmd++ = cp_type0_packet(A3XX_UCHE_CACHE_INVALIDATE0_REG, 2);
+	*cmd++ = 0x00000000;    /* No start addr for full invalidate */
+	*cmd++ = (unsigned int)
+		UCHE_ENTIRE_CACHE << UCHE_INVALIDATE1REG_ALLORPORTION |
+		UCHE_OP_INVALIDATE << UCHE_INVALIDATE1REG_OPCODE |
+		0;  /* No end addr for full invalidate */
+
+	lcc_start = cmd;
+
+	/* deferred cp_type3_packet(CP_LOAD_CONSTANT_CONTEXT, ???); */
+	cmd++;
+
+#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES
+	/* Force mismatch */
+	*cmd++ = ((drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000) | 1;
+#else
+	*cmd++ = (drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000;
+#endif
+
+	for (i = 0; i < ARRAY_SIZE(context_register_ranges) / 2; i++) {
+		cmd = reg_range(cmd, context_register_ranges[i * 2],
+				context_register_ranges[i * 2 + 1]);
+	}
+
+	lcc_start[0] = cp_type3_packet(CP_LOAD_CONSTANT_CONTEXT,
+					(cmd - lcc_start) - 1);
+
+#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES
+	lcc_start[2] |= (0 << 24) | (4 << 16);	/* Disable shadowing. */
+#else
+	lcc_start[2] |= (1 << 24) | (4 << 16);
+#endif
+
+	for (i = 0; i < ARRAY_SIZE(global_registers); i++) {
+		*cmd++ = cp_type0_packet(global_registers[i], 1);
+		tmp_ctx.reg_values[i] = virt2gpu(cmd, &drawctxt->gpustate);
+		*cmd++ = 0x00000000;
+	}
+
+	create_ib1(drawctxt, drawctxt->reg_restore, start, cmd);
+	tmp_ctx.cmd = cmd;
+}
+
+static void build_constantrestore_cmds(struct adreno_device *adreno_dev,
+				       struct adreno_context *drawctxt)
+{
+	unsigned int *cmd = tmp_ctx.cmd;
+	unsigned int *start = cmd;
+	unsigned int mode = 4;	/* Indirect mode */
+	unsigned int stateblock;
+	unsigned int numunits;
+	unsigned int statetype;
+
+	drawctxt->cond_execs[2].hostptr = cmd;
+	drawctxt->cond_execs[2].gpuaddr = virt2gpu(cmd, &drawctxt->gpustate);
+	*cmd++ = 0;
+	drawctxt->cond_execs[3].hostptr = cmd;
+	drawctxt->cond_execs[3].gpuaddr = virt2gpu(cmd, &drawctxt->gpustate);
+	*cmd++ = 0;
+
+#ifndef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES
+	*cmd++ = cp_type3_packet(CP_LOAD_CONSTANT_CONTEXT, 3);
+	*cmd++ = (drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000;
+	*cmd++ = 4 << 16;
+	*cmd++ = 0x0;
+#endif
+	/* HLSQ full update */
+	*cmd++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmd++ = CP_REG(A3XX_HLSQ_CONTROL_0_REG);
+	*cmd++ = 0x68000240;	/* A3XX_HLSQ_CONTROL_0_REG */
+
+#ifndef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES
+	/* Re-enable shadowing */
+	*cmd++ = cp_type3_packet(CP_LOAD_CONSTANT_CONTEXT, 3);
+	*cmd++ = (drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000;
+	*cmd++ = (4 << 16) | (1 << 24);
+	*cmd++ = 0x0;
+#endif
+
+	/* Load vertex shader constants */
+	*cmd++ = cp_type3_packet(CP_COND_EXEC, 4);
+	*cmd++ = drawctxt->cond_execs[2].gpuaddr >> 2;
+	*cmd++ = drawctxt->cond_execs[2].gpuaddr >> 2;
+	*cmd++ = 0x0000ffff;
+	*cmd++ = 3; /* EXEC_COUNT */
+	*cmd++ = cp_type3_packet(CP_LOAD_STATE, 2);
+	drawctxt->constant_load_commands[0].hostptr = cmd;
+	drawctxt->constant_load_commands[0].gpuaddr = virt2gpu(cmd,
+		&drawctxt->gpustate);
+
+	/*
+	   From fixup:
+
+	   mode = 4 (indirect)
+	   stateblock = 4 (Vertex constants)
+	   numunits = SP_VS_CTRL_REG1.VSCONSTLENGTH * 2; (256bit units)
+
+	   From register spec:
+	   SP_VS_CTRL_REG1.VSCONSTLENGTH [09:00]: 0-512, unit = 128bits.
+
+	   ord1 = (numunits<<22) | (stateblock<<19) | (mode<<16);
+	 */
+
+	*cmd++ = 0;		/* ord1 */
+	*cmd++ = ((drawctxt->gpustate.gpuaddr) & 0xfffffffc) | 1;
+
+	/* Load fragment shader constants */
+	*cmd++ = cp_type3_packet(CP_COND_EXEC, 4);
+	*cmd++ = drawctxt->cond_execs[3].gpuaddr >> 2;
+	*cmd++ = drawctxt->cond_execs[3].gpuaddr >> 2;
+	*cmd++ = 0x0000ffff;
+	*cmd++ = 3; /* EXEC_COUNT */
+	*cmd++ = cp_type3_packet(CP_LOAD_STATE, 2);
+	drawctxt->constant_load_commands[1].hostptr = cmd;
+	drawctxt->constant_load_commands[1].gpuaddr =
+	    virt2gpu(cmd, &drawctxt->gpustate);
+	/*
+	   From fixup:
+
+	   mode = 4 (indirect)
+	   stateblock = 6 (Fragment constants)
+	   numunits = SP_FS_CTRL_REG1.FSCONSTLENGTH * 2; (256bit units)
+
+	   From register spec:
+	   SP_FS_CTRL_REG1.FSCONSTLENGTH [09:00]: 0-512, unit = 128bits.
+
+	   ord1 = (numunits<<22) | (stateblock<<19) | (mode<<16);
+	 */
+
+	*cmd++ = 0;		/* ord1 */
+	drawctxt->constant_load_commands[2].hostptr = cmd;
+	drawctxt->constant_load_commands[2].gpuaddr =
+	    virt2gpu(cmd, &drawctxt->gpustate);
+	/*
+	   From fixup:
+	   base = drawctxt->gpustate.gpuaddr (ALU constant shadow base)
+	   offset = SP_FS_OBJ_OFFSET_REG.CONSTOBJECTSTARTOFFSET
+
+	   From register spec:
+	   SP_FS_OBJ_OFFSET_REG.CONSTOBJECTSTARTOFFSET [16:24]: Constant object
+	   start offset in on chip RAM,
+	   128bit aligned
+
+	   ord2 = base + offset | 1
+	   Because of the base alignment we can use
+	   ord2 = base | offset | 1
+	 */
+	*cmd++ = 0;		/* ord2 */
+
+	/* Restore VS texture memory objects */
+	stateblock = 0;
+	statetype = 1;
+	numunits = (TEX_SIZE_MEM_OBJECTS / 7) / 4;
+
+	*cmd++ = cp_type3_packet(CP_LOAD_STATE, 2);
+	*cmd++ = (numunits << 22) | (stateblock << 19) | (mode << 16);
+	*cmd++ = ((drawctxt->gpustate.gpuaddr + VS_TEX_OFFSET_MEM_OBJECTS)
+	    & 0xfffffffc) | statetype;
+
+	/* Restore VS texture mipmap addresses */
+	stateblock = 1;
+	statetype = 1;
+	numunits = TEX_SIZE_MIPMAP / 4;
+	*cmd++ = cp_type3_packet(CP_LOAD_STATE, 2);
+	*cmd++ = (numunits << 22) | (stateblock << 19) | (mode << 16);
+	*cmd++ = ((drawctxt->gpustate.gpuaddr + VS_TEX_OFFSET_MIPMAP)
+	    & 0xfffffffc) | statetype;
+
+	/* Restore VS texture sampler objects */
+	stateblock = 0;
+	statetype = 0;
+	numunits = (TEX_SIZE_SAMPLER_OBJ / 2) / 4;
+	*cmd++ = cp_type3_packet(CP_LOAD_STATE, 2);
+	*cmd++ = (numunits << 22) | (stateblock << 19) | (mode << 16);
+	*cmd++ = ((drawctxt->gpustate.gpuaddr + VS_TEX_OFFSET_SAMPLER_OBJ)
+	    & 0xfffffffc) | statetype;
+
+	/* Restore FS texture memory objects */
+	stateblock = 2;
+	statetype = 1;
+	numunits = (TEX_SIZE_MEM_OBJECTS / 7) / 4;
+	*cmd++ = cp_type3_packet(CP_LOAD_STATE, 2);
+	*cmd++ = (numunits << 22) | (stateblock << 19) | (mode << 16);
+	*cmd++ = ((drawctxt->gpustate.gpuaddr + FS_TEX_OFFSET_MEM_OBJECTS)
+	    & 0xfffffffc) | statetype;
+
+	/* Restore FS texture mipmap addresses */
+	stateblock = 3;
+	statetype = 1;
+	numunits = TEX_SIZE_MIPMAP / 4;
+	*cmd++ = cp_type3_packet(CP_LOAD_STATE, 2);
+	*cmd++ = (numunits << 22) | (stateblock << 19) | (mode << 16);
+	*cmd++ = ((drawctxt->gpustate.gpuaddr + FS_TEX_OFFSET_MIPMAP)
+	    & 0xfffffffc) | statetype;
+
+	/* Restore FS texture sampler objects */
+	stateblock = 2;
+	statetype = 0;
+	numunits = (TEX_SIZE_SAMPLER_OBJ / 2) / 4;
+	*cmd++ = cp_type3_packet(CP_LOAD_STATE, 2);
+	*cmd++ = (numunits << 22) | (stateblock << 19) | (mode << 16);
+	*cmd++ = ((drawctxt->gpustate.gpuaddr + FS_TEX_OFFSET_SAMPLER_OBJ)
+	    & 0xfffffffc) | statetype;
+
+	create_ib1(drawctxt, drawctxt->constant_restore, start, cmd);
+	tmp_ctx.cmd = cmd;
+}
+
+static void build_shader_restore_cmds(struct adreno_device *adreno_dev,
+				      struct adreno_context *drawctxt)
+{
+	unsigned int *cmd = tmp_ctx.cmd;
+	unsigned int *start = cmd;
+
+	/* Vertex shader */
+	*cmd++ = cp_type3_packet(CP_COND_EXEC, 4);
+	*cmd++ = drawctxt->cond_execs[0].gpuaddr >> 2;
+	*cmd++ = drawctxt->cond_execs[0].gpuaddr >> 2;
+	*cmd++ = 1;
+	*cmd++ = 3;		/* EXEC_COUNT */
+
+	*cmd++ = cp_type3_packet(CP_LOAD_STATE, 2);
+	drawctxt->shader_load_commands[0].hostptr = cmd;
+	drawctxt->shader_load_commands[0].gpuaddr =
+	    virt2gpu(cmd, &drawctxt->gpustate);
+	/*
+	   From fixup:
+
+	   mode = 4 (indirect)
+	   stateblock = 4 (Vertex shader)
+	   numunits = SP_VS_CTRL_REG0.VS_LENGTH
+
+	   From regspec:
+	   SP_VS_CTRL_REG0.VS_LENGTH [31:24]: VS length, unit = 256bits.
+	   If bit31 is 1, it means overflow
+	   or any long shader.
+
+	   ord1 = (numunits<<22) | (stateblock<<19) | (mode<<11)
+	 */
+	*cmd++ = 0;		/*ord1 */
+	*cmd++ = (drawctxt->gpustate.gpuaddr + SHADER_OFFSET) & 0xfffffffc;
+
+	/* Fragment shader */
+	*cmd++ = cp_type3_packet(CP_COND_EXEC, 4);
+	*cmd++ = drawctxt->cond_execs[1].gpuaddr >> 2;
+	*cmd++ = drawctxt->cond_execs[1].gpuaddr >> 2;
+	*cmd++ = 1;
+	*cmd++ = 3;		/* EXEC_COUNT */
+
+	*cmd++ = cp_type3_packet(CP_LOAD_STATE, 2);
+	drawctxt->shader_load_commands[1].hostptr = cmd;
+	drawctxt->shader_load_commands[1].gpuaddr =
+	    virt2gpu(cmd, &drawctxt->gpustate);
+	/*
+	   From fixup:
+
+	   mode = 4 (indirect)
+	   stateblock = 6 (Fragment shader)
+	   numunits = SP_FS_CTRL_REG0.FS_LENGTH
+
+	   From regspec:
+	   SP_FS_CTRL_REG0.FS_LENGTH [31:24]: FS length, unit = 256bits.
+	   If bit31 is 1, it means overflow
+	   or any long shader.
+
+	   ord1 = (numunits<<22) | (stateblock<<19) | (mode<<11)
+	 */
+	*cmd++ = 0;		/*ord1 */
+	*cmd++ = (drawctxt->gpustate.gpuaddr + SHADER_OFFSET
+		  + (SHADER_SHADOW_SIZE / 2)) & 0xfffffffc;
+
+	create_ib1(drawctxt, drawctxt->shader_restore, start, cmd);
+	tmp_ctx.cmd = cmd;
+}
+
+static void build_hlsqcontrol_restore_cmds(struct adreno_device *adreno_dev,
+					   struct adreno_context *drawctxt)
+{
+	unsigned int *cmd = tmp_ctx.cmd;
+	unsigned int *start = cmd;
+
+	*cmd++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmd++ = CP_REG(A3XX_HLSQ_CONTROL_0_REG);
+	drawctxt->hlsqcontrol_restore_commands[0].hostptr = cmd;
+	drawctxt->hlsqcontrol_restore_commands[0].gpuaddr
+	    = virt2gpu(cmd, &drawctxt->gpustate);
+	*cmd++ = 0;
+
+	/* Create indirect buffer command for above command sequence */
+	create_ib1(drawctxt, drawctxt->hlsqcontrol_restore, start, cmd);
+
+	tmp_ctx.cmd = cmd;
+}
+
+/* IB that modifies the shader and constant sizes and offsets in restore IBs. */
+static void build_restore_fixup_cmds(struct adreno_device *adreno_dev,
+				     struct adreno_context *drawctxt)
+{
+	unsigned int *cmd = tmp_ctx.cmd;
+	unsigned int *start = cmd;
+
+#ifdef GSL_CONTEXT_SWITCH_CPU_SYNC
+	/* Save shader sizes */
+	*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+	*cmd++ = A3XX_SP_VS_CTRL_REG0;
+	*cmd++ = drawctxt->shader_load_commands[0].gpuaddr;
+
+	*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+	*cmd++ = A3XX_SP_FS_CTRL_REG0;
+	*cmd++ = drawctxt->shader_load_commands[1].gpuaddr;
+
+	/* Save constant sizes */
+	*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+	*cmd++ = A3XX_SP_VS_CTRL_REG1;
+	*cmd++ = drawctxt->constant_load_commands[0].gpuaddr;
+
+	*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+	*cmd++ = A3XX_SP_FS_CTRL_REG1;
+	*cmd++ = drawctxt->constant_load_commands[1].gpuaddr;
+
+	/* Save constant offsets */
+	*cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2);
+	*cmd++ = A3XX_SP_FS_OBJ_OFFSET_REG;
+	*cmd++ = drawctxt->constant_load_commands[2].gpuaddr;
+#else
+	/* Save shader sizes */
+	cmd = rmw_regtomem(cmd, A3XX_SP_VS_CTRL_REG0, 0x7f000000,
+			   30, (4 << 19) | (4 << 16),
+			   drawctxt->shader_load_commands[0].gpuaddr);
+
+	cmd = rmw_regtomem(cmd, A3XX_SP_FS_CTRL_REG0, 0x7f000000,
+			   30, (6 << 19) | (4 << 16),
+			   drawctxt->shader_load_commands[1].gpuaddr);
+
+	/* Save constant sizes */
+	cmd = rmw_regtomem(cmd, A3XX_SP_VS_CTRL_REG1, 0x000003ff,
+			   23, (4 << 19) | (4 << 16),
+			   drawctxt->constant_load_commands[0].gpuaddr);
+
+	cmd = rmw_regtomem(cmd, A3XX_SP_FS_CTRL_REG1, 0x000003ff,
+			   23, (6 << 19) | (4 << 16),
+			   drawctxt->constant_load_commands[1].gpuaddr);
+
+	/* Modify constant restore conditionals */
+	cmd = rmw_regtomem(cmd, A3XX_SP_VS_CTRL_REG1, 0x000003ff,
+			0, 0, drawctxt->cond_execs[2].gpuaddr);
+
+	cmd = rmw_regtomem(cmd, A3XX_SP_FS_CTRL_REG1, 0x000003ff,
+			0, 0, drawctxt->cond_execs[3].gpuaddr);
+
+	/* Save fragment constant shadow offset */
+	cmd = rmw_regtomem(cmd, A3XX_SP_FS_OBJ_OFFSET_REG, 0x00ff0000,
+			   18, (drawctxt->gpustate.gpuaddr & 0xfffffe00) | 1,
+			   drawctxt->constant_load_commands[2].gpuaddr);
+#endif
+
+	/* Use mask value to avoid flushing HLSQ which would cause the HW to
+	   discard all the shader data */
+
+	cmd = rmw_regtomem(cmd,  A3XX_HLSQ_CONTROL_0_REG, 0x9ffffdff,
+		0, 0, drawctxt->hlsqcontrol_restore_commands[0].gpuaddr);
+
+	create_ib1(drawctxt, drawctxt->restore_fixup, start, cmd);
+
+	tmp_ctx.cmd = cmd;
+}
+
+static int a3xx_create_gpustate_shadow(struct adreno_device *adreno_dev,
+				     struct adreno_context *drawctxt)
+{
+	drawctxt->flags |= CTXT_FLAGS_STATE_SHADOW;
+
+	build_regrestore_cmds(adreno_dev, drawctxt);
+	build_constantrestore_cmds(adreno_dev, drawctxt);
+	build_hlsqcontrol_restore_cmds(adreno_dev, drawctxt);
+	build_regconstantsave_cmds(adreno_dev, drawctxt);
+	build_shader_save_cmds(adreno_dev, drawctxt);
+	build_shader_restore_cmds(adreno_dev, drawctxt);
+	build_restore_fixup_cmds(adreno_dev, drawctxt);
+	build_save_fixup_cmds(adreno_dev, drawctxt);
+
+	return 0;
+}
+
+/* create buffers for saving/restoring registers, constants, & GMEM */
+static int a3xx_create_gmem_shadow(struct adreno_device *adreno_dev,
+				 struct adreno_context *drawctxt)
+{
+	int result;
+
+	calc_gmemsize(&drawctxt->context_gmem_shadow, adreno_dev->gmem_size);
+	tmp_ctx.gmem_base = adreno_dev->gmem_base;
+
+	result = kgsl_allocate(&drawctxt->context_gmem_shadow.gmemshadow,
+		drawctxt->pagetable, drawctxt->context_gmem_shadow.size);
+
+	if (result)
+		return result;
+
+	build_quad_vtxbuff(drawctxt, &drawctxt->context_gmem_shadow,
+		&tmp_ctx.cmd);
+
+	/* Dow we need to idle? */
+	/* adreno_idle(&adreno_dev->dev, KGSL_TIMEOUT_DEFAULT); */
+
+	tmp_ctx.cmd = build_gmem2sys_cmds(adreno_dev, drawctxt,
+		&drawctxt->context_gmem_shadow);
+	tmp_ctx.cmd = build_sys2gmem_cmds(adreno_dev, drawctxt,
+		&drawctxt->context_gmem_shadow);
+
+	kgsl_cache_range_op(&drawctxt->context_gmem_shadow.gmemshadow,
+		KGSL_CACHE_OP_FLUSH);
+
+	drawctxt->flags |= CTXT_FLAGS_GMEM_SHADOW;
+
+	return 0;
+}
+
+static int a3xx_drawctxt_create(struct adreno_device *adreno_dev,
+	struct adreno_context *drawctxt)
+{
+	int ret;
+
+	/*
+	 * Allocate memory for the GPU state and the context commands.
+	 * Despite the name, this is much more then just storage for
+	 * the gpustate.  This contains command space for gmem save
+	 * and texture and vertex buffer storage too
+	 */
+
+	ret = kgsl_allocate(&drawctxt->gpustate,
+		drawctxt->pagetable, CONTEXT_SIZE);
+
+	if (ret)
+		return ret;
+
+	kgsl_sharedmem_set(&drawctxt->gpustate, 0, 0, CONTEXT_SIZE);
+	tmp_ctx.cmd = drawctxt->gpustate.hostptr + CMD_OFFSET;
+
+	if (!(drawctxt->flags & CTXT_FLAGS_PREAMBLE)) {
+		ret = a3xx_create_gpustate_shadow(adreno_dev, drawctxt);
+		if (ret)
+			goto done;
+
+		drawctxt->flags |= CTXT_FLAGS_SHADER_SAVE;
+	}
+
+	if (!(drawctxt->flags & CTXT_FLAGS_NOGMEMALLOC))
+		ret = a3xx_create_gmem_shadow(adreno_dev, drawctxt);
+
+done:
+	if (ret)
+		kgsl_sharedmem_free(&drawctxt->gpustate);
+
+	return ret;
+}
+
+static void a3xx_drawctxt_save(struct adreno_device *adreno_dev,
+			   struct adreno_context *context)
+{
+	struct kgsl_device *device = &adreno_dev->dev;
+
+	if (context == NULL)
+		return;
+
+	if (context->flags & CTXT_FLAGS_GPU_HANG)
+		KGSL_CTXT_WARN(device,
+			       "Current active context has caused gpu hang\n");
+
+	if (!(context->flags & CTXT_FLAGS_PREAMBLE)) {
+		/* Fixup self modifying IBs for save operations */
+		adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE,
+			context->save_fixup, 3);
+
+		/* save registers and constants. */
+		adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE,
+			context->regconstant_save, 3);
+
+		if (context->flags & CTXT_FLAGS_SHADER_SAVE) {
+			/* Save shader instructions */
+			adreno_ringbuffer_issuecmds(device,
+				KGSL_CMD_FLAGS_PMODE, context->shader_save, 3);
+
+			context->flags |= CTXT_FLAGS_SHADER_RESTORE;
+		}
+	}
+
+	if ((context->flags & CTXT_FLAGS_GMEM_SAVE) &&
+	    (context->flags & CTXT_FLAGS_GMEM_SHADOW)) {
+		/*
+		 * Save GMEM (note: changes shader. shader must
+		 * already be saved.)
+		 */
+
+		adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_PMODE,
+					    context->context_gmem_shadow.
+					    gmem_save, 3);
+		context->flags |= CTXT_FLAGS_GMEM_RESTORE;
+	}
+}
+
+static void a3xx_drawctxt_restore(struct adreno_device *adreno_dev,
+			      struct adreno_context *context)
+{
+	struct kgsl_device *device = &adreno_dev->dev;
+	unsigned int cmds[5];
+
+	if (context == NULL) {
+		/* No context - set the default pagetable and thats it */
+		kgsl_mmu_setstate(&device->mmu, device->mmu.defaultpagetable);
+		return;
+	}
+
+	KGSL_CTXT_INFO(device, "context flags %08x\n", context->flags);
+
+	cmds[0] = cp_nop_packet(1);
+	cmds[1] = KGSL_CONTEXT_TO_MEM_IDENTIFIER;
+	cmds[2] = cp_type3_packet(CP_MEM_WRITE, 2);
+	cmds[3] = device->memstore.gpuaddr +
+		KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL, current_context);
+	cmds[4] = context->id;
+	adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE, cmds, 5);
+	kgsl_mmu_setstate(&device->mmu, context->pagetable);
+
+	/*
+	 * Restore GMEM.  (note: changes shader.
+	 * Shader must not already be restored.)
+	 */
+
+	if (context->flags & CTXT_FLAGS_GMEM_RESTORE) {
+		adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_PMODE,
+					    context->context_gmem_shadow.
+					    gmem_restore, 3);
+		context->flags &= ~CTXT_FLAGS_GMEM_RESTORE;
+	}
+
+	if (!(context->flags & CTXT_FLAGS_PREAMBLE)) {
+		adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE,
+			context->reg_restore, 3);
+
+		/* Fixup self modifying IBs for restore operations */
+		adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE,
+			context->restore_fixup, 3);
+
+		adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE,
+			context->constant_restore, 3);
+
+		if (context->flags & CTXT_FLAGS_SHADER_RESTORE)
+			adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE,
+				context->shader_restore, 3);
+
+		/* Restore HLSQ_CONTROL_0 register */
+		adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE,
+			context->hlsqcontrol_restore, 3);
+	}
+}
+
+static void a3xx_rb_init(struct adreno_device *adreno_dev,
+			 struct adreno_ringbuffer *rb)
+{
+	unsigned int *cmds, cmds_gpu;
+	cmds = adreno_ringbuffer_allocspace(rb, 18);
+	cmds_gpu = rb->buffer_desc.gpuaddr + sizeof(uint) * (rb->wptr - 18);
+
+	GSL_RB_WRITE(cmds, cmds_gpu, cp_type3_packet(CP_ME_INIT, 17));
+	GSL_RB_WRITE(cmds, cmds_gpu, 0x000003f7);
+	GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000);
+	GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000);
+	GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000);
+	GSL_RB_WRITE(cmds, cmds_gpu, 0x00000080);
+	GSL_RB_WRITE(cmds, cmds_gpu, 0x00000100);
+	GSL_RB_WRITE(cmds, cmds_gpu, 0x00000180);
+	GSL_RB_WRITE(cmds, cmds_gpu, 0x00006600);
+	GSL_RB_WRITE(cmds, cmds_gpu, 0x00000150);
+	GSL_RB_WRITE(cmds, cmds_gpu, 0x0000014e);
+	GSL_RB_WRITE(cmds, cmds_gpu, 0x00000154);
+	GSL_RB_WRITE(cmds, cmds_gpu, 0x00000001);
+	GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000);
+	GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000);
+	/* Protected mode control - turned off for A3XX */
+	GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000);
+	GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000);
+	GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000);
+
+	adreno_ringbuffer_submit(rb);
+}
+
+#define VBIF_MAX_CLIENTS 6
+
+static void a3xx_vbif_callback(struct adreno_device *adreno_dev,
+	unsigned int status)
+{
+	struct kgsl_device *device = &adreno_dev->dev;
+	int i;
+	char str[80], *ptr = str;
+	int slen = sizeof(str) - 1;
+
+	KGSL_DRV_INFO(device, "VBIF error | status=%X\n",
+		status);
+
+	for (i = 0; i < VBIF_MAX_CLIENTS; i++) {
+		if (status & (1 << i)) {
+			unsigned int err;
+			int ret;
+
+			adreno_regwrite(device, A3XX_VBIF_ERR_INFO, i);
+			adreno_regread(device, A3XX_VBIF_ERR_INFO, &err);
+
+			ret = snprintf(ptr, slen, "%d:%8.8X ", i, err);
+			ptr += ret;
+			slen -= ret;
+		}
+	}
+
+	KGSL_DRV_INFO(device, "%s\n", str);
+
+	/* Clear the errors */
+	adreno_regwrite(device, A3XX_VBIF_ERR_CLEAR, status);
+}
+
+static void a3xx_err_callback(struct adreno_device *adreno_dev, int bit)
+{
+	struct kgsl_device *device = &adreno_dev->dev;
+	const char *err = "";
+
+	switch (bit) {
+	case A3XX_INT_RBBM_AHB_ERROR: {
+		unsigned int reg;
+
+		adreno_regread(device, A3XX_RBBM_AHB_ERROR_STATUS, &reg);
+
+		/*
+		 * Return the word address of the erroring register so that it
+		 * matches the register specification
+		 */
+
+		KGSL_DRV_CRIT(device,
+			"RBBM | AHB bus error | %s | addr=%x | ports=%x:%x\n",
+			reg & (1 << 28) ? "WRITE" : "READ",
+			(reg & 0xFFFFF) >> 2, (reg >> 20) & 0x3,
+			(reg >> 24) & 0x3);
+
+		/* Clear the error */
+		adreno_regwrite(device, A3XX_RBBM_AHB_CMD, (1 << 3));
+		return;
+	}
+	case A3XX_INT_RBBM_REG_TIMEOUT:
+		err = "RBBM: AHB register timeout";
+		break;
+	case A3XX_INT_RBBM_ME_MS_TIMEOUT:
+		err = "RBBM: ME master split timeout";
+		break;
+	case A3XX_INT_RBBM_PFP_MS_TIMEOUT:
+		err = "RBBM: PFP master split timeout";
+		break;
+	case A3XX_INT_RBBM_ATB_BUS_OVERFLOW:
+		err = "RBBM: ATB bus oveflow";
+		break;
+	case A3XX_INT_VFD_ERROR:
+		err = "VFD: Out of bounds access";
+		break;
+	case A3XX_INT_CP_T0_PACKET_IN_IB:
+		err = "ringbuffer TO packet in IB interrupt";
+		break;
+	case A3XX_INT_CP_OPCODE_ERROR:
+		err = "ringbuffer opcode error interrupt";
+		break;
+	case A3XX_INT_CP_RESERVED_BIT_ERROR:
+		err = "ringbuffer reserved bit error interrupt";
+		break;
+	case A3XX_INT_CP_HW_FAULT:
+		err = "ringbuffer hardware fault";
+		break;
+	case A3XX_INT_CP_REG_PROTECT_FAULT:
+		err = "ringbuffer protected mode error interrupt";
+		break;
+	case A3XX_INT_CP_AHB_ERROR_HALT:
+		err = "ringbuffer AHB error interrupt";
+		break;
+	case A3XX_INT_MISC_HANG_DETECT:
+		err = "MISC: GPU hang detected";
+		break;
+	case A3XX_INT_UCHE_OOB_ACCESS:
+		err = "UCHE:  Out of bounds access";
+		break;
+	}
+
+	KGSL_DRV_CRIT(device, "%s\n", err);
+	kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF);
+}
+
+static void a3xx_cp_callback(struct adreno_device *adreno_dev, int irq)
+{
+	struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
+
+	if (irq == A3XX_INT_CP_RB_INT) {
+		unsigned int context_id;
+		kgsl_sharedmem_readl(&adreno_dev->dev.memstore,
+				&context_id,
+				KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
+					current_context));
+		if (context_id < KGSL_MEMSTORE_MAX) {
+			kgsl_sharedmem_writel(&rb->device->memstore,
+					KGSL_MEMSTORE_OFFSET(context_id,
+						ts_cmp_enable), 0);
+			wmb();
+		}
+		KGSL_CMD_WARN(rb->device, "ringbuffer rb interrupt\n");
+	}
+
+	wake_up_interruptible_all(&rb->device->wait_queue);
+
+	/* Schedule work to free mem and issue ibs */
+	queue_work(rb->device->work_queue, &rb->device->ts_expired_ws);
+
+	atomic_notifier_call_chain(&rb->device->ts_notifier_list,
+				   rb->device->id, NULL);
+}
+
+#define A3XX_IRQ_CALLBACK(_c) { .func = _c }
+
+#define A3XX_INT_MASK \
+	((1 << A3XX_INT_RBBM_AHB_ERROR) |        \
+	 (1 << A3XX_INT_RBBM_REG_TIMEOUT) |      \
+	 (1 << A3XX_INT_RBBM_ATB_BUS_OVERFLOW) | \
+	 (1 << A3XX_INT_CP_T0_PACKET_IN_IB) |    \
+	 (1 << A3XX_INT_CP_OPCODE_ERROR) |       \
+	 (1 << A3XX_INT_CP_RESERVED_BIT_ERROR) | \
+	 (1 << A3XX_INT_CP_HW_FAULT) |           \
+	 (1 << A3XX_INT_CP_IB1_INT) |            \
+	 (1 << A3XX_INT_CP_IB2_INT) |            \
+	 (1 << A3XX_INT_CP_RB_INT) |             \
+	 (1 << A3XX_INT_CP_REG_PROTECT_FAULT) |  \
+	 (1 << A3XX_INT_CP_AHB_ERROR_HALT) |     \
+	 (1 << A3XX_INT_UCHE_OOB_ACCESS))
+
+static struct {
+	void (*func)(struct adreno_device *, int);
+} a3xx_irq_funcs[] = {
+	A3XX_IRQ_CALLBACK(NULL),               /* 0 - RBBM_GPU_IDLE */
+	A3XX_IRQ_CALLBACK(a3xx_err_callback),  /* 1 - RBBM_AHB_ERROR */
+	A3XX_IRQ_CALLBACK(a3xx_err_callback),  /* 2 - RBBM_REG_TIMEOUT */
+	A3XX_IRQ_CALLBACK(a3xx_err_callback),  /* 3 - RBBM_ME_MS_TIMEOUT */
+	A3XX_IRQ_CALLBACK(a3xx_err_callback),  /* 4 - RBBM_PFP_MS_TIMEOUT */
+	A3XX_IRQ_CALLBACK(a3xx_err_callback),  /* 5 - RBBM_ATB_BUS_OVERFLOW */
+	A3XX_IRQ_CALLBACK(a3xx_err_callback),  /* 6 - RBBM_VFD_ERROR */
+	A3XX_IRQ_CALLBACK(NULL),	       /* 7 - CP_SW */
+	A3XX_IRQ_CALLBACK(a3xx_err_callback),  /* 8 - CP_T0_PACKET_IN_IB */
+	A3XX_IRQ_CALLBACK(a3xx_err_callback),  /* 9 - CP_OPCODE_ERROR */
+	A3XX_IRQ_CALLBACK(a3xx_err_callback),  /* 10 - CP_RESERVED_BIT_ERROR */
+	A3XX_IRQ_CALLBACK(a3xx_err_callback),  /* 11 - CP_HW_FAULT */
+	A3XX_IRQ_CALLBACK(NULL),	       /* 12 - CP_DMA */
+	A3XX_IRQ_CALLBACK(a3xx_cp_callback),   /* 13 - CP_IB2_INT */
+	A3XX_IRQ_CALLBACK(a3xx_cp_callback),   /* 14 - CP_IB1_INT */
+	A3XX_IRQ_CALLBACK(a3xx_cp_callback),   /* 15 - CP_RB_INT */
+	A3XX_IRQ_CALLBACK(a3xx_err_callback),  /* 16 - CP_REG_PROTECT_FAULT */
+	A3XX_IRQ_CALLBACK(NULL),	       /* 17 - CP_RB_DONE_TS */
+	A3XX_IRQ_CALLBACK(NULL),	       /* 18 - CP_VS_DONE_TS */
+	A3XX_IRQ_CALLBACK(NULL),	       /* 19 - CP_PS_DONE_TS */
+	A3XX_IRQ_CALLBACK(NULL),	       /* 20 - CP_CACHE_FLUSH_TS */
+	A3XX_IRQ_CALLBACK(a3xx_err_callback),  /* 21 - CP_AHB_ERROR_FAULT */
+	A3XX_IRQ_CALLBACK(NULL),	       /* 22 - Unused */
+	A3XX_IRQ_CALLBACK(NULL),	       /* 23 - Unused */
+	A3XX_IRQ_CALLBACK(NULL),	       /* 24 - MISC_HANG_DETECT */
+	A3XX_IRQ_CALLBACK(a3xx_err_callback),  /* 25 - UCHE_OOB_ACCESS */
+	/* 26 to 31 - Unused */
+};
+
+static irqreturn_t a3xx_irq_handler(struct adreno_device *adreno_dev)
+{
+	struct kgsl_device *device = &adreno_dev->dev;
+	irqreturn_t ret = IRQ_NONE;
+	unsigned int status, tmp;
+	int i;
+
+	adreno_regread(&adreno_dev->dev, A3XX_RBBM_INT_0_STATUS, &status);
+
+	for (tmp = status, i = 0; tmp && i < ARRAY_SIZE(a3xx_irq_funcs); i++) {
+		if (tmp & 1) {
+			if (a3xx_irq_funcs[i].func != NULL) {
+				a3xx_irq_funcs[i].func(adreno_dev, i);
+				ret = IRQ_HANDLED;
+			} else {
+				KGSL_DRV_CRIT(device,
+					"Unhandled interrupt bit %x\n", i);
+			}
+		}
+
+		tmp >>= 1;
+	}
+
+	trace_kgsl_a3xx_irq_status(device, status);
+
+	if (status)
+		adreno_regwrite(&adreno_dev->dev, A3XX_RBBM_INT_CLEAR_CMD,
+			status);
+
+	/* Check for VBIF errors */
+	adreno_regread(&adreno_dev->dev, A3XX_VBIF_ERR_PENDING, &status);
+
+	if (status) {
+		a3xx_vbif_callback(adreno_dev, status);
+		ret = IRQ_HANDLED;
+	}
+
+	return ret;
+}
+
+static void a3xx_irq_control(struct adreno_device *adreno_dev, int state)
+{
+	struct kgsl_device *device = &adreno_dev->dev;
+
+	if (state) {
+		adreno_regwrite(device, A3XX_RBBM_INT_0_MASK, A3XX_INT_MASK);
+
+		/* Enable VBIF interrupts - write 0 to enable them all */
+		adreno_regwrite(device, A3XX_VBIF_ERR_MASK, 0);
+		/* Clear outstanding VBIF errors */
+		adreno_regwrite(device, A3XX_VBIF_ERR_CLEAR, 0x3F);
+	} else {
+		adreno_regwrite(device, A3XX_RBBM_INT_0_MASK, 0);
+		adreno_regwrite(device, A3XX_VBIF_ERR_MASK, 0xFFFFFFFF);
+	}
+}
+
+static unsigned int a3xx_busy_cycles(struct adreno_device *adreno_dev)
+{
+	struct kgsl_device *device = &adreno_dev->dev;
+	unsigned int reg, val;
+
+	/* Freeze the counter */
+	adreno_regread(device, A3XX_RBBM_RBBM_CTL, &reg);
+	reg &= ~RBBM_RBBM_CTL_ENABLE_PWR_CTR1;
+	adreno_regwrite(device, A3XX_RBBM_RBBM_CTL, reg);
+
+	/* Read the value */
+	adreno_regread(device, A3XX_RBBM_PERFCTR_PWR_1_LO, &val);
+
+	/* Reset the counter */
+	reg |= RBBM_RBBM_CTL_RESET_PWR_CTR1;
+	adreno_regwrite(device, A3XX_RBBM_RBBM_CTL, reg);
+
+	/* Re-enable the counter */
+	reg &= ~RBBM_RBBM_CTL_RESET_PWR_CTR1;
+	reg |= RBBM_RBBM_CTL_ENABLE_PWR_CTR1;
+	adreno_regwrite(device, A3XX_RBBM_RBBM_CTL, reg);
+
+	return val;
+}
+
+static void a3xx_start(struct adreno_device *adreno_dev)
+{
+	struct kgsl_device *device = &adreno_dev->dev;
+
+	/* Reset the core */
+	adreno_regwrite(device, A3XX_RBBM_SW_RESET_CMD,
+		0x00000001);
+	msleep(20);
+
+	/* Set up 16 deep read/write request queues */
+
+	adreno_regwrite(device, A3XX_VBIF_IN_RD_LIM_CONF0, 0x10101010);
+	adreno_regwrite(device, A3XX_VBIF_IN_RD_LIM_CONF1, 0x10101010);
+	adreno_regwrite(device, A3XX_VBIF_OUT_RD_LIM_CONF0, 0x10101010);
+	adreno_regwrite(device, A3XX_VBIF_OUT_WR_LIM_CONF0, 0x10101010);
+	adreno_regwrite(device, A3XX_VBIF_DDR_OUT_MAX_BURST, 0x00000303);
+	adreno_regwrite(device, A3XX_VBIF_IN_WR_LIM_CONF0, 0x10101010);
+	adreno_regwrite(device, A3XX_VBIF_IN_WR_LIM_CONF1, 0x10101010);
+
+	/* Enable WR-REQ */
+	adreno_regwrite(device, A3XX_VBIF_GATE_OFF_WRREQ_EN, 0x000000FF);
+
+	/* Set up round robin arbitration between both AXI ports */
+	adreno_regwrite(device, A3XX_VBIF_ARB_CTL, 0x00000030);
+
+	/* Set up AOOO */
+	adreno_regwrite(device, A3XX_VBIF_OUT_AXI_AOOO_EN, 0x0000003C);
+	adreno_regwrite(device, A3XX_VBIF_OUT_AXI_AOOO, 0x003C003C);
+
+	if (cpu_is_apq8064()) {
+		/* Enable 1K sort */
+		adreno_regwrite(device, A3XX_VBIF_ABIT_SORT, 0x000000FF);
+		adreno_regwrite(device, A3XX_VBIF_ABIT_SORT_CONF, 0x000000A4);
+	}
+	/* Make all blocks contribute to the GPU BUSY perf counter */
+	adreno_regwrite(device, A3XX_RBBM_GPU_BUSY_MASKED, 0xFFFFFFFF);
+
+	/* Tune the hystersis counters for SP and CP idle detection */
+	adreno_regwrite(device, A3XX_RBBM_SP_HYST_CNT, 0x10);
+	adreno_regwrite(device, A3XX_RBBM_WAIT_IDLE_CLOCKS_CTL, 0x10);
+
+	/* Enable the RBBM error reporting bits.  This lets us get
+	   useful information on failure */
+
+	adreno_regwrite(device, A3XX_RBBM_AHB_CTL0, 0x00000001);
+
+	/* Enable AHB error reporting */
+	adreno_regwrite(device, A3XX_RBBM_AHB_CTL1, 0xA6FFFFFF);
+
+	/* Turn on the power counters */
+	adreno_regwrite(device, A3XX_RBBM_RBBM_CTL, 0x00030000);
+
+	/* Turn on hang detection - this spews a lot of useful information
+	 * into the RBBM registers on a hang */
+
+	adreno_regwrite(device, A3XX_RBBM_INTERFACE_HANG_INT_CTL,
+			(1 << 16) | 0xFFF);
+
+}
+
+/* Defined in adreno_a3xx_snapshot.c */
+void *a3xx_snapshot(struct adreno_device *adreno_dev, void *snapshot,
+	int *remain, int hang);
+
+struct adreno_gpudev adreno_a3xx_gpudev = {
+	.reg_rbbm_status = A3XX_RBBM_STATUS,
+	.reg_cp_pfp_ucode_addr = A3XX_CP_PFP_UCODE_ADDR,
+	.reg_cp_pfp_ucode_data = A3XX_CP_PFP_UCODE_DATA,
+
+	.ctxt_create = a3xx_drawctxt_create,
+	.ctxt_save = a3xx_drawctxt_save,
+	.ctxt_restore = a3xx_drawctxt_restore,
+	.rb_init = a3xx_rb_init,
+	.irq_control = a3xx_irq_control,
+	.irq_handler = a3xx_irq_handler,
+	.busy_cycles = a3xx_busy_cycles,
+	.start = a3xx_start,
+	.snapshot = a3xx_snapshot,
+};
diff --git a/drivers/gpu/msm/adreno_a3xx_snapshot.c b/drivers/gpu/msm/adreno_a3xx_snapshot.c
new file mode 100644
index 0000000..c8c7c44
--- /dev/null
+++ b/drivers/gpu/msm/adreno_a3xx_snapshot.c
@@ -0,0 +1,296 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "kgsl.h"
+#include "adreno.h"
+#include "kgsl_snapshot.h"
+#include "a3xx_reg.h"
+
+#define DEBUG_SECTION_SZ(_dwords) (((_dwords) * sizeof(unsigned int)) \
+		+ sizeof(struct kgsl_snapshot_debug))
+
+#define VPC_MEMORY_BANKS 4
+#define VPC_MEMORY_SIZE 512
+
+static int a3xx_snapshot_vpc_memory(struct kgsl_device *device, void *snapshot,
+		int remain, void *priv)
+{
+	struct kgsl_snapshot_debug *header = snapshot;
+	unsigned int *data = snapshot + sizeof(*header);
+	int size = VPC_MEMORY_BANKS * VPC_MEMORY_SIZE;
+	int bank, addr, i = 0;
+
+	if (remain < DEBUG_SECTION_SZ(size)) {
+		SNAPSHOT_ERR_NOMEM(device, "VPC MEMORY");
+		return 0;
+	}
+
+	header->type = SNAPSHOT_DEBUG_VPC_MEMORY;
+	header->size = size;
+
+	for (bank = 0; bank < VPC_MEMORY_BANKS; bank++) {
+		for (addr = 0; addr < VPC_MEMORY_SIZE; addr++) {
+			unsigned int val = bank | (addr << 4);
+			adreno_regwrite(device,
+				A3XX_VPC_VPC_DEBUG_RAM_SEL, val);
+			adreno_regread(device,
+				A3XX_VPC_VPC_DEBUG_RAM_READ, &data[i++]);
+		}
+	}
+
+	return DEBUG_SECTION_SZ(size);
+}
+
+#define CP_MEQ_SIZE 16
+static int a3xx_snapshot_cp_meq(struct kgsl_device *device, void *snapshot,
+		int remain, void *priv)
+{
+	struct kgsl_snapshot_debug *header = snapshot;
+	unsigned int *data = snapshot + sizeof(*header);
+	int i;
+
+	if (remain < DEBUG_SECTION_SZ(CP_MEQ_SIZE)) {
+		SNAPSHOT_ERR_NOMEM(device, "CP MEQ DEBUG");
+		return 0;
+	}
+
+	header->type = SNAPSHOT_DEBUG_CP_MEQ;
+	header->size = CP_MEQ_SIZE;
+
+	adreno_regwrite(device, A3XX_CP_MEQ_ADDR, 0x0);
+	for (i = 0; i < CP_MEQ_SIZE; i++)
+		adreno_regread(device, A3XX_CP_MEQ_DATA, &data[i]);
+
+	return DEBUG_SECTION_SZ(CP_MEQ_SIZE);
+}
+
+static int a3xx_snapshot_cp_pm4_ram(struct kgsl_device *device, void *snapshot,
+		int remain, void *priv)
+{
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	struct kgsl_snapshot_debug *header = snapshot;
+	unsigned int *data = snapshot + sizeof(*header);
+	int i, size = adreno_dev->pm4_fw_size - 1;
+
+	if (remain < DEBUG_SECTION_SZ(size)) {
+		SNAPSHOT_ERR_NOMEM(device, "CP PM4 RAM DEBUG");
+		return 0;
+	}
+
+	header->type = SNAPSHOT_DEBUG_CP_PM4_RAM;
+	header->size = size;
+
+	/*
+	 * Read the firmware from the GPU rather than use our cache in order to
+	 * try to catch mis-programming or corruption in the hardware.  We do
+	 * use the cached version of the size, however, instead of trying to
+	 * maintain always changing hardcoded constants
+	 */
+
+	adreno_regwrite(device, REG_CP_ME_RAM_RADDR, 0x0);
+	for (i = 0; i < size; i++)
+		adreno_regread(device, REG_CP_ME_RAM_DATA, &data[i]);
+
+	return DEBUG_SECTION_SZ(size);
+}
+
+static int a3xx_snapshot_cp_pfp_ram(struct kgsl_device *device, void *snapshot,
+		int remain, void *priv)
+{
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	struct kgsl_snapshot_debug *header = snapshot;
+	unsigned int *data = snapshot + sizeof(*header);
+	int i, size = adreno_dev->pfp_fw_size - 1;
+
+	if (remain < DEBUG_SECTION_SZ(size)) {
+		SNAPSHOT_ERR_NOMEM(device, "CP PFP RAM DEBUG");
+		return 0;
+	}
+
+	header->type = SNAPSHOT_DEBUG_CP_PFP_RAM;
+	header->size = size;
+
+	/*
+	 * Read the firmware from the GPU rather than use our cache in order to
+	 * try to catch mis-programming or corruption in the hardware.  We do
+	 * use the cached version of the size, however, instead of trying to
+	 * maintain always changing hardcoded constants
+	 */
+	kgsl_regwrite(device, A3XX_CP_PFP_UCODE_ADDR, 0x0);
+	for (i = 0; i < size; i++)
+		adreno_regread(device, A3XX_CP_PFP_UCODE_DATA, &data[i]);
+
+	return DEBUG_SECTION_SZ(size);
+}
+
+#define CP_ROQ_SIZE 128
+
+static int a3xx_snapshot_cp_roq(struct kgsl_device *device, void *snapshot,
+		int remain, void *priv)
+{
+	struct kgsl_snapshot_debug *header = snapshot;
+	unsigned int *data = snapshot + sizeof(*header);
+	int i;
+
+	if (remain < DEBUG_SECTION_SZ(CP_ROQ_SIZE)) {
+		SNAPSHOT_ERR_NOMEM(device, "CP ROQ DEBUG");
+		return 0;
+	}
+
+	header->type = SNAPSHOT_DEBUG_CP_ROQ;
+	header->size = CP_ROQ_SIZE;
+
+	adreno_regwrite(device, A3XX_CP_ROQ_ADDR, 0x0);
+	for (i = 0; i < CP_ROQ_SIZE; i++)
+		adreno_regread(device, A3XX_CP_ROQ_DATA, &data[i]);
+
+	return DEBUG_SECTION_SZ(CP_ROQ_SIZE);
+}
+
+#define DEBUGFS_BLOCK_SIZE 0x40
+
+static int a3xx_snapshot_debugbus_block(struct kgsl_device *device,
+	void *snapshot, int remain, void *priv)
+{
+	struct kgsl_snapshot_debugbus *header = snapshot;
+	unsigned int id = (unsigned int) priv;
+	unsigned int val;
+	int i;
+	unsigned int *data = snapshot + sizeof(*header);
+	int size =
+		(DEBUGFS_BLOCK_SIZE * sizeof(unsigned int)) + sizeof(*header);
+
+	if (remain < size) {
+		SNAPSHOT_ERR_NOMEM(device, "DEBUGBUS");
+		return 0;
+	}
+
+	val = (id << 8) | (1 << 16);
+
+	header->id = id;
+	header->count = DEBUGFS_BLOCK_SIZE;
+
+	for (i = 0; i < DEBUGFS_BLOCK_SIZE; i++) {
+		adreno_regwrite(device, A3XX_RBBM_DEBUG_BUS_CTL, val | i);
+		adreno_regread(device, A3XX_RBBM_DEBUG_BUS_DATA_STATUS,
+			&data[i]);
+	}
+
+	return size;
+}
+
+static unsigned int debugbus_blocks[] = {
+	RBBM_BLOCK_ID_CP,
+	RBBM_BLOCK_ID_RBBM,
+	RBBM_BLOCK_ID_VBIF,
+	RBBM_BLOCK_ID_HLSQ,
+	RBBM_BLOCK_ID_UCHE,
+	RBBM_BLOCK_ID_PC,
+	RBBM_BLOCK_ID_VFD,
+	RBBM_BLOCK_ID_VPC,
+	RBBM_BLOCK_ID_TSE,
+	RBBM_BLOCK_ID_RAS,
+	RBBM_BLOCK_ID_VSC,
+	RBBM_BLOCK_ID_SP_0,
+	RBBM_BLOCK_ID_SP_1,
+	RBBM_BLOCK_ID_SP_2,
+	RBBM_BLOCK_ID_SP_3,
+	RBBM_BLOCK_ID_TPL1_0,
+	RBBM_BLOCK_ID_TPL1_1,
+	RBBM_BLOCK_ID_TPL1_2,
+	RBBM_BLOCK_ID_TPL1_3,
+	RBBM_BLOCK_ID_RB_0,
+	RBBM_BLOCK_ID_RB_1,
+	RBBM_BLOCK_ID_RB_2,
+	RBBM_BLOCK_ID_RB_3,
+	RBBM_BLOCK_ID_MARB_0,
+	RBBM_BLOCK_ID_MARB_1,
+	RBBM_BLOCK_ID_MARB_2,
+	RBBM_BLOCK_ID_MARB_3,
+};
+
+static void *a3xx_snapshot_debugbus(struct kgsl_device *device,
+	void *snapshot, int *remain)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(debugbus_blocks); i++) {
+		snapshot = kgsl_snapshot_add_section(device,
+			KGSL_SNAPSHOT_SECTION_DEBUGBUS, snapshot, remain,
+			a3xx_snapshot_debugbus_block,
+			(void *) debugbus_blocks[i]);
+	}
+
+	return snapshot;
+}
+
+/* A3XX GPU snapshot function - this is where all of the A3XX specific
+ * bits and pieces are grabbed into the snapshot memory
+ */
+
+void *a3xx_snapshot(struct adreno_device *adreno_dev, void *snapshot,
+	int *remain, int hang)
+{
+	struct kgsl_device *device = &adreno_dev->dev;
+	struct kgsl_snapshot_registers regs;
+
+	regs.regs = (unsigned int *) a3xx_registers;
+	regs.count = a3xx_registers_count;
+
+	/* Master set of (non debug) registers */
+	snapshot = kgsl_snapshot_add_section(device,
+		KGSL_SNAPSHOT_SECTION_REGS, snapshot, remain,
+		kgsl_snapshot_dump_regs, &regs);
+
+	/* CP_STATE_DEBUG indexed registers */
+	snapshot = kgsl_snapshot_indexed_registers(device, snapshot,
+			remain, REG_CP_STATE_DEBUG_INDEX,
+			REG_CP_STATE_DEBUG_DATA, 0x0, 0x14);
+
+	/* CP_ME indexed registers */
+	snapshot = kgsl_snapshot_indexed_registers(device, snapshot,
+			remain, REG_CP_ME_CNTL, REG_CP_ME_STATUS,
+			64, 44);
+
+	/* VPC memory */
+	snapshot = kgsl_snapshot_add_section(device,
+			KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
+			a3xx_snapshot_vpc_memory, NULL);
+
+	/* CP MEQ */
+	snapshot = kgsl_snapshot_add_section(device,
+			KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
+			a3xx_snapshot_cp_meq, NULL);
+
+	/* CP PFP and PM4 */
+	/* Reading these will hang the GPU if it isn't already hung */
+
+	if (hang) {
+		snapshot = kgsl_snapshot_add_section(device,
+			KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
+			a3xx_snapshot_cp_pfp_ram, NULL);
+
+		snapshot = kgsl_snapshot_add_section(device,
+			KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
+			a3xx_snapshot_cp_pm4_ram, NULL);
+	}
+
+	/* CP ROQ */
+	snapshot = kgsl_snapshot_add_section(device,
+			KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
+			a3xx_snapshot_cp_roq, NULL);
+
+	snapshot = a3xx_snapshot_debugbus(device, snapshot, remain);
+
+	return snapshot;
+}
diff --git a/drivers/gpu/msm/adreno_a3xx_trace.c b/drivers/gpu/msm/adreno_a3xx_trace.c
new file mode 100644
index 0000000..8b4a80d
--- /dev/null
+++ b/drivers/gpu/msm/adreno_a3xx_trace.c
@@ -0,0 +1,20 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "kgsl.h"
+#include "adreno.h"
+
+/* Instantiate tracepoints */
+#define CREATE_TRACE_POINTS
+#include "a3xx_reg.h"
+#include "adreno_a3xx_trace.h"
diff --git a/drivers/gpu/msm/adreno_a3xx_trace.h b/drivers/gpu/msm/adreno_a3xx_trace.h
new file mode 100644
index 0000000..44483a8
--- /dev/null
+++ b/drivers/gpu/msm/adreno_a3xx_trace.h
@@ -0,0 +1,89 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#if !defined(_ADRENO_A3XX_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _ADRENO_A3XX_TRACE_H
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM kgsl
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE adreno_a3xx_trace
+
+#include <linux/tracepoint.h>
+
+struct kgsl_device;
+
+/*
+ * Tracepoint for a3xx irq. Includes status info
+ */
+TRACE_EVENT(kgsl_a3xx_irq_status,
+
+	TP_PROTO(struct kgsl_device *device, unsigned int status),
+
+	TP_ARGS(device, status),
+
+	TP_STRUCT__entry(
+		__string(device_name, device->name)
+		__field(unsigned int, status)
+	),
+
+	TP_fast_assign(
+		__assign_str(device_name, device->name);
+		__entry->status = status;
+	),
+
+	TP_printk(
+		"d_name=%s status=%s",
+		__get_str(device_name),
+		__entry->status ? __print_flags(__entry->status, "|",
+			{ 1 << A3XX_INT_RBBM_AHB_ERROR, "RBBM_GPU_IDLE" },
+			{ 1 << A3XX_INT_RBBM_AHB_ERROR, "RBBM_AHB_ERR" },
+			{ 1 << A3XX_INT_RBBM_REG_TIMEOUT, "RBBM_REG_TIMEOUT" },
+			{ 1 << A3XX_INT_RBBM_ME_MS_TIMEOUT,
+				"RBBM_ME_MS_TIMEOUT" },
+			{ 1 << A3XX_INT_RBBM_PFP_MS_TIMEOUT,
+				"RBBM_PFP_MS_TIMEOUT" },
+			{ 1 << A3XX_INT_RBBM_ATB_BUS_OVERFLOW,
+				"RBBM_ATB_BUS_OVERFLOW" },
+			{ 1 << A3XX_INT_VFD_ERROR, "RBBM_VFD_ERROR" },
+			{ 1 << A3XX_INT_CP_SW_INT, "CP_SW" },
+			{ 1 << A3XX_INT_CP_T0_PACKET_IN_IB,
+				"CP_T0_PACKET_IN_IB" },
+			{ 1 << A3XX_INT_CP_OPCODE_ERROR, "CP_OPCODE_ERROR" },
+			{ 1 << A3XX_INT_CP_RESERVED_BIT_ERROR,
+				"CP_RESERVED_BIT_ERROR" },
+			{ 1 << A3XX_INT_CP_HW_FAULT, "CP_HW_FAULT" },
+			{ 1 << A3XX_INT_CP_DMA, "CP_DMA" },
+			{ 1 << A3XX_INT_CP_IB2_INT, "CP_IB2_INT" },
+			{ 1 << A3XX_INT_CP_IB1_INT, "CP_IB1_INT" },
+			{ 1 << A3XX_INT_CP_RB_INT, "CP_RB_INT" },
+			{ 1 << A3XX_INT_CP_REG_PROTECT_FAULT,
+				"CP_REG_PROTECT_FAULT" },
+			{ 1 << A3XX_INT_CP_RB_DONE_TS, "CP_RB_DONE_TS" },
+			{ 1 << A3XX_INT_CP_VS_DONE_TS, "CP_VS_DONE_TS" },
+			{ 1 << A3XX_INT_CP_PS_DONE_TS, "CP_PS_DONE_TS" },
+			{ 1 << A3XX_INT_CACHE_FLUSH_TS, "CACHE_FLUSH_TS" },
+			{ 1 << A3XX_INT_CP_AHB_ERROR_HALT,
+				"CP_AHB_ERROR_HALT" },
+			{ 1 << A3XX_INT_MISC_HANG_DETECT, "MISC_HANG_DETECT" },
+			{ 1 << A3XX_INT_UCHE_OOB_ACCESS, "UCHE_OOB_ACCESS" })
+		: "None"
+	)
+);
+
+#endif /* _ADRENO_A3XX_TRACE_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/drivers/gpu/msm/adreno_debugfs.c b/drivers/gpu/msm/adreno_debugfs.c
new file mode 100644
index 0000000..822cf14
--- /dev/null
+++ b/drivers/gpu/msm/adreno_debugfs.c
@@ -0,0 +1,133 @@
+/* Copyright (c) 2002,2008-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/export.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include "kgsl.h"
+#include "adreno_postmortem.h"
+#include "adreno.h"
+
+#include "a2xx_reg.h"
+
+unsigned int kgsl_cff_dump_enable;
+int adreno_pm_regs_enabled;
+int adreno_pm_ib_enabled;
+
+static struct dentry *pm_d_debugfs;
+
+static int pm_dump_set(void *data, u64 val)
+{
+	struct kgsl_device *device = data;
+
+	if (val) {
+		mutex_lock(&device->mutex);
+		adreno_postmortem_dump(device, 1);
+		mutex_unlock(&device->mutex);
+	}
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(pm_dump_fops,
+			NULL,
+			pm_dump_set, "%llu\n");
+
+static int pm_regs_enabled_set(void *data, u64 val)
+{
+	adreno_pm_regs_enabled = val ? 1 : 0;
+	return 0;
+}
+
+static int pm_regs_enabled_get(void *data, u64 *val)
+{
+	*val = adreno_pm_regs_enabled;
+	return 0;
+}
+
+static int pm_ib_enabled_set(void *data, u64 val)
+{
+	adreno_pm_ib_enabled = val ? 1 : 0;
+	return 0;
+}
+
+static int pm_ib_enabled_get(void *data, u64 *val)
+{
+	*val = adreno_pm_ib_enabled;
+	return 0;
+}
+
+
+DEFINE_SIMPLE_ATTRIBUTE(pm_regs_enabled_fops,
+			pm_regs_enabled_get,
+			pm_regs_enabled_set, "%llu\n");
+
+DEFINE_SIMPLE_ATTRIBUTE(pm_ib_enabled_fops,
+			pm_ib_enabled_get,
+			pm_ib_enabled_set, "%llu\n");
+
+
+static int kgsl_cff_dump_enable_set(void *data, u64 val)
+{
+#ifdef CONFIG_MSM_KGSL_CFF_DUMP
+	kgsl_cff_dump_enable = (val != 0);
+	return 0;
+#else
+	return -EINVAL;
+#endif
+}
+
+static int kgsl_cff_dump_enable_get(void *data, u64 *val)
+{
+	*val = kgsl_cff_dump_enable;
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(kgsl_cff_dump_enable_fops, kgsl_cff_dump_enable_get,
+			kgsl_cff_dump_enable_set, "%llu\n");
+
+typedef void (*reg_read_init_t)(struct kgsl_device *device);
+typedef void (*reg_read_fill_t)(struct kgsl_device *device, int i,
+	unsigned int *vals, int linec);
+
+void adreno_debugfs_init(struct kgsl_device *device)
+{
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
+	if (!device->d_debugfs || IS_ERR(device->d_debugfs))
+		return;
+
+	debugfs_create_file("cff_dump", 0644, device->d_debugfs, device,
+			    &kgsl_cff_dump_enable_fops);
+	debugfs_create_u32("wait_timeout", 0644, device->d_debugfs,
+		&adreno_dev->wait_timeout);
+	debugfs_create_u32("ib_check", 0644, device->d_debugfs,
+			   &adreno_dev->ib_check_level);
+
+	/* Create post mortem control files */
+
+	pm_d_debugfs = debugfs_create_dir("postmortem", device->d_debugfs);
+
+	if (IS_ERR(pm_d_debugfs))
+		return;
+
+	debugfs_create_file("dump",  0600, pm_d_debugfs, device,
+			    &pm_dump_fops);
+	debugfs_create_file("regs_enabled", 0644, pm_d_debugfs, device,
+			    &pm_regs_enabled_fops);
+	debugfs_create_file("ib_enabled", 0644, pm_d_debugfs, device,
+				    &pm_ib_enabled_fops);
+}
diff --git a/drivers/gpu/msm/adreno_debugfs.h b/drivers/gpu/msm/adreno_debugfs.h
new file mode 100644
index 0000000..5f8d89a
--- /dev/null
+++ b/drivers/gpu/msm/adreno_debugfs.h
@@ -0,0 +1,46 @@
+/* Copyright (c) 2002,2008-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __ADRENO_DEBUGFS_H
+#define __ADRENO_DEBUGFS_H
+
+#ifdef CONFIG_DEBUG_FS
+
+int adreno_debugfs_init(struct kgsl_device *device);
+
+extern int adreno_pm_regs_enabled;
+extern int adreno_pm_ib_enabled;
+
+static inline int is_adreno_pm_regs_enabled(void)
+{
+	return adreno_pm_regs_enabled;
+}
+
+static inline int is_adreno_pm_ib_enabled(void)
+{
+	return adreno_pm_ib_enabled;
+}
+
+#else
+static inline int adreno_debugfs_init(struct kgsl_device *device)
+{
+	return 0;
+}
+
+static inline int kgsl_pmregs_enabled(void)
+{
+	/* If debugfs is turned off, then always print registers */
+	return 1;
+}
+#endif
+
+#endif /* __ADRENO_DEBUGFS_H */
diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c
new file mode 100644
index 0000000..0d15fb9
--- /dev/null
+++ b/drivers/gpu/msm/adreno_drawctxt.c
@@ -0,0 +1,285 @@
+/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/slab.h>
+
+#include "kgsl.h"
+#include "kgsl_sharedmem.h"
+#include "adreno.h"
+
+#define KGSL_INIT_REFTIMESTAMP		0x7FFFFFFF
+
+/* quad for copying GMEM to context shadow */
+#define QUAD_LEN 12
+#define QUAD_RESTORE_LEN 14
+
+static unsigned int gmem_copy_quad[QUAD_LEN] = {
+	0x00000000, 0x00000000, 0x3f800000,
+	0x00000000, 0x00000000, 0x3f800000,
+	0x00000000, 0x00000000, 0x3f800000,
+	0x00000000, 0x00000000, 0x3f800000
+};
+
+static unsigned int gmem_restore_quad[QUAD_RESTORE_LEN] = {
+	0x00000000, 0x3f800000, 0x3f800000,
+	0x00000000, 0x00000000, 0x00000000,
+	0x3f800000, 0x00000000, 0x00000000,
+	0x3f800000, 0x00000000, 0x00000000,
+	0x3f800000, 0x3f800000,
+};
+
+#define TEXCOORD_LEN 8
+
+static unsigned int gmem_copy_texcoord[TEXCOORD_LEN] = {
+	0x00000000, 0x3f800000,
+	0x3f800000, 0x3f800000,
+	0x00000000, 0x00000000,
+	0x3f800000, 0x00000000
+};
+
+/*
+ * Helper functions
+ * These are global helper functions used by the GPUs during context switch
+ */
+
+/**
+ * uint2float - convert a uint to IEEE754 single precision float
+ * @ uintval - value to convert
+ */
+
+unsigned int uint2float(unsigned int uintval)
+{
+	unsigned int exp, frac = 0;
+
+	if (uintval == 0)
+		return 0;
+
+	exp = ilog2(uintval);
+
+	/* Calculate fraction */
+	if (23 > exp)
+		frac = (uintval & (~(1 << exp))) << (23 - exp);
+
+	/* Exp is biased by 127 and shifted 23 bits */
+	exp = (exp + 127) << 23;
+
+	return exp | frac;
+}
+
+static void set_gmem_copy_quad(struct gmem_shadow_t *shadow)
+{
+	/* set vertex buffer values */
+	gmem_copy_quad[1] = uint2float(shadow->height);
+	gmem_copy_quad[3] = uint2float(shadow->width);
+	gmem_copy_quad[4] = uint2float(shadow->height);
+	gmem_copy_quad[9] = uint2float(shadow->width);
+
+	gmem_restore_quad[5] = uint2float(shadow->height);
+	gmem_restore_quad[7] = uint2float(shadow->width);
+
+	memcpy(shadow->quad_vertices.hostptr, gmem_copy_quad, QUAD_LEN << 2);
+	memcpy(shadow->quad_vertices_restore.hostptr, gmem_restore_quad,
+		QUAD_RESTORE_LEN << 2);
+
+	memcpy(shadow->quad_texcoords.hostptr, gmem_copy_texcoord,
+		TEXCOORD_LEN << 2);
+}
+
+/**
+ * build_quad_vtxbuff - Create a quad for saving/restoring GMEM
+ * @ context - Pointer to the context being created
+ * @ shadow - Pointer to the GMEM shadow structure
+ * @ incmd - Pointer to pointer to the temporary command buffer
+ */
+
+/* quad for saving/restoring gmem */
+void build_quad_vtxbuff(struct adreno_context *drawctxt,
+		struct gmem_shadow_t *shadow, unsigned int **incmd)
+{
+	 unsigned int *cmd = *incmd;
+
+	/* quad vertex buffer location (in GPU space) */
+	shadow->quad_vertices.hostptr = cmd;
+	shadow->quad_vertices.gpuaddr = virt2gpu(cmd, &drawctxt->gpustate);
+
+	cmd += QUAD_LEN;
+
+	/* Used by A3XX, but define for both to make the code easier */
+	shadow->quad_vertices_restore.hostptr = cmd;
+	shadow->quad_vertices_restore.gpuaddr =
+		virt2gpu(cmd, &drawctxt->gpustate);
+
+	cmd += QUAD_RESTORE_LEN;
+
+	/* tex coord buffer location (in GPU space) */
+	shadow->quad_texcoords.hostptr = cmd;
+	shadow->quad_texcoords.gpuaddr = virt2gpu(cmd, &drawctxt->gpustate);
+
+	cmd += TEXCOORD_LEN;
+
+	set_gmem_copy_quad(shadow);
+	*incmd = cmd;
+}
+
+/**
+ * adreno_drawctxt_create - create a new adreno draw context
+ * @device - KGSL device to create the context on
+ * @pagetable - Pagetable for the context
+ * @context- Generic KGSL context structure
+ * @flags - flags for the context (passed from user space)
+ *
+ * Create a new draw context for the 3D core.  Return 0 on success,
+ * or error code on failure.
+ */
+int adreno_drawctxt_create(struct kgsl_device *device,
+			struct kgsl_pagetable *pagetable,
+			struct kgsl_context *context, uint32_t flags)
+{
+	struct adreno_context *drawctxt;
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	int ret;
+
+	drawctxt = kzalloc(sizeof(struct adreno_context), GFP_KERNEL);
+
+	if (drawctxt == NULL)
+		return -ENOMEM;
+
+	drawctxt->pagetable = pagetable;
+	drawctxt->bin_base_offset = 0;
+	drawctxt->id = context->id;
+
+	if (flags & KGSL_CONTEXT_PREAMBLE)
+		drawctxt->flags |= CTXT_FLAGS_PREAMBLE;
+
+	if (flags & KGSL_CONTEXT_NO_GMEM_ALLOC)
+		drawctxt->flags |= CTXT_FLAGS_NOGMEMALLOC;
+
+	if (flags & KGSL_CONTEXT_PER_CONTEXT_TS)
+		drawctxt->flags |= CTXT_FLAGS_PER_CONTEXT_TS;
+
+	ret = adreno_dev->gpudev->ctxt_create(adreno_dev, drawctxt);
+	if (ret)
+		goto err;
+
+	kgsl_sharedmem_writel(&device->memstore,
+			KGSL_MEMSTORE_OFFSET(drawctxt->id, ref_wait_ts),
+			KGSL_INIT_REFTIMESTAMP);
+
+	context->devctxt = drawctxt;
+	return 0;
+err:
+	kfree(drawctxt);
+	return ret;
+}
+
+/**
+ * adreno_drawctxt_destroy - destroy a draw context
+ * @device - KGSL device that owns the context
+ * @context- Generic KGSL context container for the context
+ *
+ * Destroy an existing context.  Return 0 on success or error
+ * code on failure.
+ */
+
+/* destroy a drawing context */
+
+void adreno_drawctxt_destroy(struct kgsl_device *device,
+			  struct kgsl_context *context)
+{
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	struct adreno_context *drawctxt;
+
+	if (context == NULL)
+		return;
+
+	drawctxt = context->devctxt;
+	/* deactivate context */
+	if (adreno_dev->drawctxt_active == drawctxt) {
+		/* no need to save GMEM or shader, the context is
+		 * being destroyed.
+		 */
+		drawctxt->flags &= ~(CTXT_FLAGS_GMEM_SAVE |
+				     CTXT_FLAGS_SHADER_SAVE |
+				     CTXT_FLAGS_GMEM_SHADOW |
+				     CTXT_FLAGS_STATE_SHADOW);
+
+		adreno_drawctxt_switch(adreno_dev, NULL, 0);
+	}
+
+	adreno_idle(device, KGSL_TIMEOUT_DEFAULT);
+
+	kgsl_sharedmem_free(&drawctxt->gpustate);
+	kgsl_sharedmem_free(&drawctxt->context_gmem_shadow.gmemshadow);
+
+	kfree(drawctxt);
+	context->devctxt = NULL;
+}
+
+/**
+ * adreno_drawctxt_set_bin_base_offset - set bin base offset for the context
+ * @device - KGSL device that owns the context
+ * @context- Generic KGSL context container for the context
+ * @offset - Offset to set
+ *
+ * Set the bin base offset for A2XX devices.  Not valid for A3XX devices.
+ */
+
+void adreno_drawctxt_set_bin_base_offset(struct kgsl_device *device,
+				      struct kgsl_context *context,
+				      unsigned int offset)
+{
+	struct adreno_context *drawctxt = context->devctxt;
+
+	if (drawctxt)
+		drawctxt->bin_base_offset = offset;
+}
+
+/**
+ * adreno_drawctxt_switch - switch the current draw context
+ * @adreno_dev - The 3D device that owns the context
+ * @drawctxt - the 3D context to switch to
+ * @flags - Flags to accompany the switch (from user space)
+ *
+ * Switch the current draw context
+ */
+
+void adreno_drawctxt_switch(struct adreno_device *adreno_dev,
+				struct adreno_context *drawctxt,
+				unsigned int flags)
+{
+	struct kgsl_device *device = &adreno_dev->dev;
+
+	if (drawctxt) {
+		if (flags & KGSL_CONTEXT_SAVE_GMEM)
+			/* Set the flag in context so that the save is done
+			* when this context is switched out. */
+			drawctxt->flags |= CTXT_FLAGS_GMEM_SAVE;
+		else
+			/* Remove GMEM saving flag from the context */
+			drawctxt->flags &= ~CTXT_FLAGS_GMEM_SAVE;
+	}
+
+	/* already current? */
+	if (adreno_dev->drawctxt_active == drawctxt)
+		return;
+
+	KGSL_CTXT_INFO(device, "from %p to %p flags %d\n",
+			adreno_dev->drawctxt_active, drawctxt, flags);
+
+	/* Save the old context */
+	adreno_dev->gpudev->ctxt_save(adreno_dev, adreno_dev->drawctxt_active);
+
+	/* Set the new context */
+	adreno_dev->gpudev->ctxt_restore(adreno_dev, drawctxt);
+	adreno_dev->drawctxt_active = drawctxt;
+}
diff --git a/drivers/gpu/msm/adreno_drawctxt.h b/drivers/gpu/msm/adreno_drawctxt.h
new file mode 100644
index 0000000..3eb1aba
--- /dev/null
+++ b/drivers/gpu/msm/adreno_drawctxt.h
@@ -0,0 +1,179 @@
+/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __ADRENO_DRAWCTXT_H
+#define __ADRENO_DRAWCTXT_H
+
+#include "adreno_pm4types.h"
+#include "a2xx_reg.h"
+
+/* Flags */
+
+#define CTXT_FLAGS_NOT_IN_USE		0x00000000
+#define CTXT_FLAGS_IN_USE		0x00000001
+
+/* state shadow memory allocated */
+#define CTXT_FLAGS_STATE_SHADOW		0x00000010
+
+/* gmem shadow memory allocated */
+#define CTXT_FLAGS_GMEM_SHADOW		0x00000100
+/* gmem must be copied to shadow */
+#define CTXT_FLAGS_GMEM_SAVE		0x00000200
+/* gmem can be restored from shadow */
+#define CTXT_FLAGS_GMEM_RESTORE		0x00000400
+/* preamble packed in cmdbuffer for context switching */
+#define CTXT_FLAGS_PREAMBLE		0x00000800
+/* shader must be copied to shadow */
+#define CTXT_FLAGS_SHADER_SAVE		0x00002000
+/* shader can be restored from shadow */
+#define CTXT_FLAGS_SHADER_RESTORE	0x00004000
+/* Context has caused a GPU hang */
+#define CTXT_FLAGS_GPU_HANG		0x00008000
+/* Specifies there is no need to save GMEM */
+#define CTXT_FLAGS_NOGMEMALLOC          0x00010000
+/* Trash state for context */
+#define CTXT_FLAGS_TRASHSTATE		0x00020000
+/* per context timestamps enabled */
+#define CTXT_FLAGS_PER_CONTEXT_TS	0x00040000
+
+struct kgsl_device;
+struct adreno_device;
+struct kgsl_device_private;
+struct kgsl_context;
+
+/* draw context */
+struct gmem_shadow_t {
+	struct kgsl_memdesc gmemshadow;	/* Shadow buffer address */
+
+	/*
+	 * 256 KB GMEM surface = 4 bytes-per-pixel x 256 pixels/row x
+	 * 256 rows. Width & height must be multiples of 32 in case tiled
+	 * textures are used
+	*/
+
+	enum COLORFORMATX format; /* Unused on A3XX */
+	unsigned int size;	/* Size of surface used to store GMEM */
+	unsigned int width;	/* Width of surface used to store GMEM */
+	unsigned int height;	/* Height of surface used to store GMEM */
+	unsigned int pitch;	/* Pitch of surface used to store GMEM */
+	unsigned int gmem_pitch;	/* Pitch value used for GMEM */
+	unsigned int *gmem_save_commands;    /* Unused on A3XX */
+	unsigned int *gmem_restore_commands; /* Unused on A3XX */
+	unsigned int gmem_save[3];
+	unsigned int gmem_restore[3];
+	struct kgsl_memdesc quad_vertices;
+	struct kgsl_memdesc quad_texcoords;
+	struct kgsl_memdesc quad_vertices_restore;
+};
+
+struct adreno_context {
+	unsigned int id;
+	uint32_t flags;
+	struct kgsl_pagetable *pagetable;
+	struct kgsl_memdesc gpustate;
+	unsigned int reg_restore[3];
+	unsigned int shader_save[3];
+	unsigned int shader_restore[3];
+
+	/* Information of the GMEM shadow that is created in context create */
+	struct gmem_shadow_t context_gmem_shadow;
+
+	/* A2XX specific items */
+	unsigned int reg_save[3];
+	unsigned int shader_fixup[3];
+	unsigned int chicken_restore[3];
+	unsigned int bin_base_offset;
+
+	/* A3XX specific items */
+	unsigned int regconstant_save[3];
+	unsigned int constant_restore[3];
+	unsigned int hlsqcontrol_restore[3];
+	unsigned int save_fixup[3];
+	unsigned int restore_fixup[3];
+	struct kgsl_memdesc shader_load_commands[2];
+	struct kgsl_memdesc shader_save_commands[4];
+	struct kgsl_memdesc constant_save_commands[3];
+	struct kgsl_memdesc constant_load_commands[3];
+	struct kgsl_memdesc cond_execs[4];
+	struct kgsl_memdesc hlsqcontrol_restore_commands[1];
+};
+
+int adreno_drawctxt_create(struct kgsl_device *device,
+			struct kgsl_pagetable *pagetable,
+			struct kgsl_context *context,
+			uint32_t flags);
+
+void adreno_drawctxt_destroy(struct kgsl_device *device,
+			  struct kgsl_context *context);
+
+void adreno_drawctxt_switch(struct adreno_device *adreno_dev,
+				struct adreno_context *drawctxt,
+				unsigned int flags);
+void adreno_drawctxt_set_bin_base_offset(struct kgsl_device *device,
+					struct kgsl_context *context,
+					unsigned int offset);
+
+/* GPU context switch helper functions */
+
+void build_quad_vtxbuff(struct adreno_context *drawctxt,
+		struct gmem_shadow_t *shadow, unsigned int **incmd);
+
+unsigned int uint2float(unsigned int);
+
+static inline unsigned int virt2gpu(unsigned int *cmd,
+				    struct kgsl_memdesc *memdesc)
+{
+	return memdesc->gpuaddr + ((char *) cmd - (char *) memdesc->hostptr);
+}
+
+static inline void create_ib1(struct adreno_context *drawctxt,
+			      unsigned int *cmd,
+			      unsigned int *start,
+			      unsigned int *end)
+{
+	cmd[0] = CP_HDR_INDIRECT_BUFFER_PFD;
+	cmd[1] = virt2gpu(start, &drawctxt->gpustate);
+	cmd[2] = end - start;
+}
+
+
+static inline unsigned int *reg_range(unsigned int *cmd, unsigned int start,
+	unsigned int end)
+{
+	*cmd++ = CP_REG(start);		/* h/w regs, start addr */
+	*cmd++ = end - start + 1;	/* count */
+	return cmd;
+}
+
+static inline void calc_gmemsize(struct gmem_shadow_t *shadow, int gmem_size)
+{
+	int w = 64, h = 64;
+
+	shadow->format = COLORX_8_8_8_8;
+
+	/* convert from bytes to 32-bit words */
+	gmem_size = (gmem_size + 3) / 4;
+
+	while ((w * h) < gmem_size) {
+		if (w < h)
+			w *= 2;
+		else
+			h *= 2;
+	}
+
+	shadow->pitch = shadow->width = w;
+	shadow->height = h;
+	shadow->gmem_pitch = shadow->pitch;
+	shadow->size = shadow->pitch * shadow->height * 4;
+}
+
+#endif  /* __ADRENO_DRAWCTXT_H */
diff --git a/drivers/gpu/msm/adreno_pm4types.h b/drivers/gpu/msm/adreno_pm4types.h
new file mode 100644
index 0000000..fb44b25
--- /dev/null
+++ b/drivers/gpu/msm/adreno_pm4types.h
@@ -0,0 +1,232 @@
+/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __ADRENO_PM4TYPES_H
+#define __ADRENO_PM4TYPES_H
+
+
+#define CP_PKT_MASK	0xc0000000
+
+#define CP_TYPE0_PKT	((unsigned int)0 << 30)
+#define CP_TYPE1_PKT	((unsigned int)1 << 30)
+#define CP_TYPE2_PKT	((unsigned int)2 << 30)
+#define CP_TYPE3_PKT	((unsigned int)3 << 30)
+
+
+/* type3 packets */
+/* initialize CP's micro-engine */
+#define CP_ME_INIT		0x48
+
+/* skip N 32-bit words to get to the next packet */
+#define CP_NOP			0x10
+
+/* indirect buffer dispatch.  same as IB, but init is pipelined */
+#define CP_INDIRECT_BUFFER_PFD	0x37
+
+/* wait for the IDLE state of the engine */
+#define CP_WAIT_FOR_IDLE	0x26
+
+/* wait until a register or memory location is a specific value */
+#define CP_WAIT_REG_MEM	0x3c
+
+/* wait until a register location is equal to a specific value */
+#define CP_WAIT_REG_EQ		0x52
+
+/* wait until a register location is >= a specific value */
+#define CP_WAT_REG_GTE		0x53
+
+/* wait until a read completes */
+#define CP_WAIT_UNTIL_READ	0x5c
+
+/* wait until all base/size writes from an IB_PFD packet have completed */
+#define CP_WAIT_IB_PFD_COMPLETE 0x5d
+
+/* register read/modify/write */
+#define CP_REG_RMW		0x21
+
+/* Set binning configuration registers */
+#define CP_SET_BIN_DATA             0x2f
+
+/* reads register in chip and writes to memory */
+#define CP_REG_TO_MEM		0x3e
+
+/* write N 32-bit words to memory */
+#define CP_MEM_WRITE		0x3d
+
+/* write CP_PROG_COUNTER value to memory */
+#define CP_MEM_WRITE_CNTR	0x4f
+
+/* conditional execution of a sequence of packets */
+#define CP_COND_EXEC		0x44
+
+/* conditional write to memory or register */
+#define CP_COND_WRITE		0x45
+
+/* generate an event that creates a write to memory when completed */
+#define CP_EVENT_WRITE		0x46
+
+/* generate a VS|PS_done event */
+#define CP_EVENT_WRITE_SHD	0x58
+
+/* generate a cache flush done event */
+#define CP_EVENT_WRITE_CFL	0x59
+
+/* generate a z_pass done event */
+#define CP_EVENT_WRITE_ZPD	0x5b
+
+
+/* initiate fetch of index buffer and draw */
+#define CP_DRAW_INDX		0x22
+
+/* draw using supplied indices in packet */
+#define CP_DRAW_INDX_2		0x36
+
+/* initiate fetch of index buffer and binIDs and draw */
+#define CP_DRAW_INDX_BIN	0x34
+
+/* initiate fetch of bin IDs and draw using supplied indices */
+#define CP_DRAW_INDX_2_BIN	0x35
+
+
+/* begin/end initiator for viz query extent processing */
+#define CP_VIZ_QUERY		0x23
+
+/* fetch state sub-blocks and initiate shader code DMAs */
+#define CP_SET_STATE		0x25
+
+/* load constant into chip and to memory */
+#define CP_SET_CONSTANT	0x2d
+
+/* load sequencer instruction memory (pointer-based) */
+#define CP_IM_LOAD		0x27
+
+/* load sequencer instruction memory (code embedded in packet) */
+#define CP_IM_LOAD_IMMEDIATE	0x2b
+
+/* load constants from a location in memory */
+#define CP_LOAD_CONSTANT_CONTEXT 0x2e
+
+/* (A2x) sets binning configuration registers */
+#define CP_SET_BIN_DATA             0x2f
+
+/* selective invalidation of state pointers */
+#define CP_INVALIDATE_STATE	0x3b
+
+
+/* dynamically changes shader instruction memory partition */
+#define CP_SET_SHADER_BASES	0x4A
+
+/* sets the 64-bit BIN_MASK register in the PFP */
+#define CP_SET_BIN_MASK	0x50
+
+/* sets the 64-bit BIN_SELECT register in the PFP */
+#define CP_SET_BIN_SELECT	0x51
+
+
+/* updates the current context, if needed */
+#define CP_CONTEXT_UPDATE	0x5e
+
+/* generate interrupt from the command stream */
+#define CP_INTERRUPT		0x40
+
+
+/* copy sequencer instruction memory to system memory */
+#define CP_IM_STORE            0x2c
+
+/*
+ * for a20x
+ * program an offset that will added to the BIN_BASE value of
+ * the 3D_DRAW_INDX_BIN packet
+ */
+#define CP_SET_BIN_BASE_OFFSET     0x4B
+
+/*
+ * for a22x
+ * sets draw initiator flags register in PFP, gets bitwise-ORed into
+ * every draw initiator
+ */
+#define CP_SET_DRAW_INIT_FLAGS      0x4B
+
+#define CP_SET_PROTECTED_MODE  0x5f /* sets the register protection mode */
+
+/*
+ * for a3xx
+ */
+
+#define CP_LOAD_STATE 0x30 /* load high level sequencer command */
+
+/* Conditionally load a IB based on a flag */
+#define CP_COND_INDIRECT_BUFFER_PFE 0x3A /* prefetch enabled */
+#define CP_COND_INDIRECT_BUFFER_PFD 0x32 /* prefetch disabled */
+
+/* Load a buffer with pre-fetch enabled */
+#define CP_INDIRECT_BUFFER_PFE 0x3F
+
+#define CP_LOADSTATE_DSTOFFSET_SHIFT 0x00000000
+#define CP_LOADSTATE_STATESRC_SHIFT 0x00000010
+#define CP_LOADSTATE_STATEBLOCKID_SHIFT 0x00000013
+#define CP_LOADSTATE_NUMOFUNITS_SHIFT 0x00000016
+#define CP_LOADSTATE_STATETYPE_SHIFT 0x00000000
+#define CP_LOADSTATE_EXTSRCADDR_SHIFT 0x00000002
+
+/* packet header building macros */
+#define cp_type0_packet(regindx, cnt) \
+	(CP_TYPE0_PKT | (((cnt)-1) << 16) | ((regindx) & 0x7FFF))
+
+#define cp_type0_packet_for_sameregister(regindx, cnt) \
+	((CP_TYPE0_PKT | (((cnt)-1) << 16) | ((1 << 15) | \
+		((regindx) & 0x7FFF)))
+
+#define cp_type1_packet(reg0, reg1) \
+	 (CP_TYPE1_PKT | ((reg1) << 12) | (reg0))
+
+#define cp_type3_packet(opcode, cnt) \
+	 (CP_TYPE3_PKT | (((cnt)-1) << 16) | (((opcode) & 0xFF) << 8))
+
+#define cp_predicated_type3_packet(opcode, cnt) \
+	 (CP_TYPE3_PKT | (((cnt)-1) << 16) | (((opcode) & 0xFF) << 8) | 0x1)
+
+#define cp_nop_packet(cnt) \
+	 (CP_TYPE3_PKT | (((cnt)-1) << 16) | (CP_NOP << 8))
+
+#define pkt_is_type0(pkt) (((pkt) & 0XC0000000) == CP_TYPE0_PKT)
+
+#define type0_pkt_size(pkt) ((((pkt) >> 16) & 0x3FFF) + 1)
+#define type0_pkt_offset(pkt) ((pkt) & 0x7FFF)
+
+#define pkt_is_type3(pkt) (((pkt) & 0xC0000000) == CP_TYPE3_PKT)
+
+#define cp_type3_opcode(pkt) (((pkt) >> 8) & 0xFF)
+#define type3_pkt_size(pkt) ((((pkt) >> 16) & 0x3FFF) + 1)
+
+/* packet headers */
+#define CP_HDR_ME_INIT	cp_type3_packet(CP_ME_INIT, 18)
+#define CP_HDR_INDIRECT_BUFFER_PFD cp_type3_packet(CP_INDIRECT_BUFFER_PFD, 2)
+#define CP_HDR_INDIRECT_BUFFER_PFE cp_type3_packet(CP_INDIRECT_BUFFER_PFE, 2)
+
+/* dword base address of the GFX decode space */
+#define SUBBLOCK_OFFSET(reg) ((unsigned int)((reg) - (0x2000)))
+
+/* gmem command buffer length */
+#define CP_REG(reg) ((0x4 << 16) | (SUBBLOCK_OFFSET(reg)))
+
+
+/* Return 1 if the command is an indirect buffer of any kind */
+static inline int adreno_cmd_is_ib(unsigned int cmd)
+{
+	return (cmd == cp_type3_packet(CP_INDIRECT_BUFFER_PFE, 2) ||
+		cmd == cp_type3_packet(CP_INDIRECT_BUFFER_PFD, 2) ||
+		cmd == cp_type3_packet(CP_COND_INDIRECT_BUFFER_PFE, 2) ||
+		cmd == cp_type3_packet(CP_COND_INDIRECT_BUFFER_PFD, 2));
+}
+
+#endif	/* __ADRENO_PM4TYPES_H */
diff --git a/drivers/gpu/msm/adreno_postmortem.c b/drivers/gpu/msm/adreno_postmortem.c
new file mode 100644
index 0000000..7bb65ca
--- /dev/null
+++ b/drivers/gpu/msm/adreno_postmortem.c
@@ -0,0 +1,956 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/vmalloc.h>
+
+#include "kgsl.h"
+#include "kgsl_sharedmem.h"
+
+#include "adreno.h"
+#include "adreno_pm4types.h"
+#include "adreno_ringbuffer.h"
+#include "adreno_postmortem.h"
+#include "adreno_debugfs.h"
+#include "kgsl_cffdump.h"
+#include "kgsl_pwrctrl.h"
+
+#include "a2xx_reg.h"
+#include "a3xx_reg.h"
+
+#define INVALID_RB_CMD 0xaaaaaaaa
+#define NUM_DWORDS_OF_RINGBUFFER_HISTORY 100
+
+struct pm_id_name {
+	uint32_t id;
+	char name[9];
+};
+
+static const struct pm_id_name pm0_types[] = {
+	{REG_PA_SC_AA_CONFIG,		"RPASCAAC"},
+	{REG_RBBM_PM_OVERRIDE2,		"RRBBPMO2"},
+	{REG_SCRATCH_REG2,		"RSCRTRG2"},
+	{REG_SQ_GPR_MANAGEMENT,		"RSQGPRMN"},
+	{REG_SQ_INST_STORE_MANAGMENT,	"RSQINSTS"},
+	{REG_TC_CNTL_STATUS,		"RTCCNTLS"},
+	{REG_TP0_CHICKEN,		"RTP0CHCK"},
+	{REG_CP_TIMESTAMP,		"CP_TM_ST"},
+};
+
+static const struct pm_id_name pm3_types[] = {
+	{CP_COND_EXEC,			"CND_EXEC"},
+	{CP_CONTEXT_UPDATE,		"CX__UPDT"},
+	{CP_DRAW_INDX,			"DRW_NDX_"},
+	{CP_DRAW_INDX_BIN,		"DRW_NDXB"},
+	{CP_EVENT_WRITE,		"EVENT_WT"},
+	{CP_IM_LOAD,			"IN__LOAD"},
+	{CP_IM_LOAD_IMMEDIATE,		"IM_LOADI"},
+	{CP_IM_STORE,			"IM_STORE"},
+	{CP_INDIRECT_BUFFER_PFE,	"IND_BUF_"},
+	{CP_INDIRECT_BUFFER_PFD,	"IND_BUFP"},
+	{CP_INTERRUPT,			"PM4_INTR"},
+	{CP_INVALIDATE_STATE,		"INV_STAT"},
+	{CP_LOAD_CONSTANT_CONTEXT,	"LD_CN_CX"},
+	{CP_ME_INIT,			"ME__INIT"},
+	{CP_NOP,			"PM4__NOP"},
+	{CP_REG_RMW,			"REG__RMW"},
+	{CP_REG_TO_MEM,		"REG2_MEM"},
+	{CP_SET_BIN_BASE_OFFSET,	"ST_BIN_O"},
+	{CP_SET_CONSTANT,		"ST_CONST"},
+	{CP_SET_PROTECTED_MODE,	"ST_PRT_M"},
+	{CP_SET_SHADER_BASES,		"ST_SHD_B"},
+	{CP_WAIT_FOR_IDLE,		"WAIT4IDL"},
+};
+
+static uint32_t adreno_is_pm4_len(uint32_t word)
+{
+	if (word == INVALID_RB_CMD)
+		return 0;
+
+	return (word >> 16) & 0x3FFF;
+}
+
+static bool adreno_is_pm4_type(uint32_t word)
+{
+	int i;
+
+	if (word == INVALID_RB_CMD)
+		return 1;
+
+	if (adreno_is_pm4_len(word) > 16)
+		return 0;
+
+	if ((word & (3<<30)) == CP_TYPE0_PKT) {
+		for (i = 0; i < ARRAY_SIZE(pm0_types); ++i) {
+			if ((word & 0x7FFF) == pm0_types[i].id)
+				return 1;
+		}
+		return 0;
+	}
+	if ((word & (3<<30)) == CP_TYPE3_PKT) {
+		for (i = 0; i < ARRAY_SIZE(pm3_types); ++i) {
+			if ((word & 0xFFFF) == (pm3_types[i].id << 8))
+				return 1;
+		}
+		return 0;
+	}
+	return 0;
+}
+
+static const char *adreno_pm4_name(uint32_t word)
+{
+	int i;
+
+	if (word == INVALID_RB_CMD)
+		return "--------";
+
+	if ((word & (3<<30)) == CP_TYPE0_PKT) {
+		for (i = 0; i < ARRAY_SIZE(pm0_types); ++i) {
+			if ((word & 0x7FFF) == pm0_types[i].id)
+				return pm0_types[i].name;
+		}
+		return "????????";
+	}
+	if ((word & (3<<30)) == CP_TYPE3_PKT) {
+		for (i = 0; i < ARRAY_SIZE(pm3_types); ++i) {
+			if ((word & 0xFFFF) == (pm3_types[i].id << 8))
+				return pm3_types[i].name;
+		}
+		return "????????";
+	}
+	return "????????";
+}
+
+static void adreno_dump_regs(struct kgsl_device *device,
+			   const int *registers, int size)
+{
+	int range = 0, offset = 0;
+
+	for (range = 0; range < size; range++) {
+		/* start and end are in dword offsets */
+		int start = registers[range * 2];
+		int end = registers[range * 2 + 1];
+
+		unsigned char linebuf[32 * 3 + 2 + 32 + 1];
+		int linelen, i;
+
+		for (offset = start; offset <= end; offset += linelen) {
+			unsigned int regvals[32/4];
+			linelen = min(end+1-offset, 32/4);
+
+			for (i = 0; i < linelen; ++i)
+				kgsl_regread(device, offset+i, regvals+i);
+
+			hex_dump_to_buffer(regvals, linelen*4, 32, 4,
+				linebuf, sizeof(linebuf), 0);
+			KGSL_LOG_DUMP(device,
+				"REG: %5.5X: %s\n", offset, linebuf);
+		}
+	}
+}
+
+static void dump_ib(struct kgsl_device *device, char* buffId, uint32_t pt_base,
+	uint32_t base_offset, uint32_t ib_base, uint32_t ib_size, bool dump)
+{
+	uint8_t *base_addr = adreno_convertaddr(device, pt_base,
+		ib_base, ib_size*sizeof(uint32_t));
+
+	if (base_addr && dump)
+		print_hex_dump(KERN_ERR, buffId, DUMP_PREFIX_OFFSET,
+				 32, 4, base_addr, ib_size*4, 0);
+	else
+		KGSL_LOG_DUMP(device, "%s base:%8.8X  ib_size:%d  "
+			"offset:%5.5X%s\n",
+			buffId, ib_base, ib_size*4, base_offset,
+			base_addr ? "" : " [Invalid]");
+}
+
+#define IB_LIST_SIZE	64
+struct ib_list {
+	int count;
+	uint32_t bases[IB_LIST_SIZE];
+	uint32_t sizes[IB_LIST_SIZE];
+	uint32_t offsets[IB_LIST_SIZE];
+};
+
+static void dump_ib1(struct kgsl_device *device, uint32_t pt_base,
+			uint32_t base_offset,
+			uint32_t ib1_base, uint32_t ib1_size,
+			struct ib_list *ib_list, bool dump)
+{
+	int i, j;
+	uint32_t value;
+	uint32_t *ib1_addr;
+
+	dump_ib(device, "IB1:", pt_base, base_offset, ib1_base,
+		ib1_size, dump);
+
+	/* fetch virtual address for given IB base */
+	ib1_addr = (uint32_t *)adreno_convertaddr(device, pt_base,
+		ib1_base, ib1_size*sizeof(uint32_t));
+	if (!ib1_addr)
+		return;
+
+	for (i = 0; i+3 < ib1_size; ) {
+		value = ib1_addr[i++];
+		if (adreno_cmd_is_ib(value)) {
+			uint32_t ib2_base = ib1_addr[i++];
+			uint32_t ib2_size = ib1_addr[i++];
+
+			/* find previous match */
+			for (j = 0; j < ib_list->count; ++j)
+				if (ib_list->sizes[j] == ib2_size
+					&& ib_list->bases[j] == ib2_base)
+					break;
+
+			if (j < ib_list->count || ib_list->count
+				>= IB_LIST_SIZE)
+				continue;
+
+			/* store match */
+			ib_list->sizes[ib_list->count] = ib2_size;
+			ib_list->bases[ib_list->count] = ib2_base;
+			ib_list->offsets[ib_list->count] = i<<2;
+			++ib_list->count;
+		}
+	}
+}
+
+static void adreno_dump_rb_buffer(const void *buf, size_t len,
+		char *linebuf, size_t linebuflen, int *argp)
+{
+	const u32 *ptr4 = buf;
+	const int ngroups = len;
+	int lx = 0, j;
+	bool nxsp = 1;
+
+	for (j = 0; j < ngroups; j++) {
+		if (*argp < 0) {
+			lx += scnprintf(linebuf + lx, linebuflen - lx, " <");
+			*argp = -*argp;
+		} else if (nxsp)
+			lx += scnprintf(linebuf + lx, linebuflen - lx, "  ");
+		else
+			nxsp = 1;
+		if (!*argp && adreno_is_pm4_type(ptr4[j])) {
+			lx += scnprintf(linebuf + lx, linebuflen - lx,
+				"%s", adreno_pm4_name(ptr4[j]));
+			*argp = -(adreno_is_pm4_len(ptr4[j])+1);
+		} else {
+			lx += scnprintf(linebuf + lx, linebuflen - lx,
+				"%8.8X", ptr4[j]);
+			if (*argp > 1)
+				--*argp;
+			else if (*argp == 1) {
+				*argp = 0;
+				nxsp = 0;
+				lx += scnprintf(linebuf + lx, linebuflen - lx,
+					"> ");
+			}
+		}
+	}
+	linebuf[lx] = '\0';
+}
+
+static bool adreno_rb_use_hex(void)
+{
+#ifdef CONFIG_MSM_KGSL_PSTMRTMDMP_RB_HEX
+	return 1;
+#else
+	return 0;
+#endif
+}
+
+static void adreno_dump_rb(struct kgsl_device *device, const void *buf,
+			 size_t len, int start, int size)
+{
+	const uint32_t *ptr = buf;
+	int i, remaining, args = 0;
+	unsigned char linebuf[32 * 3 + 2 + 32 + 1];
+	const int rowsize = 8;
+
+	len >>= 2;
+	remaining = len;
+	for (i = 0; i < len; i += rowsize) {
+		int linelen = min(remaining, rowsize);
+		remaining -= rowsize;
+
+		if (adreno_rb_use_hex())
+			hex_dump_to_buffer(ptr+i, linelen*4, rowsize*4, 4,
+				linebuf, sizeof(linebuf), 0);
+		else
+			adreno_dump_rb_buffer(ptr+i, linelen, linebuf,
+				sizeof(linebuf), &args);
+		KGSL_LOG_DUMP(device,
+			"RB: %4.4X:%s\n", (start+i)%size, linebuf);
+	}
+}
+
+struct log_field {
+	bool show;
+	const char *display;
+};
+
+static int adreno_dump_fields_line(struct kgsl_device *device,
+				 const char *start, char *str, int slen,
+				 const struct log_field **lines,
+				 int num)
+{
+	const struct log_field *l = *lines;
+	int sptr, count  = 0;
+
+	sptr = snprintf(str, slen, "%s", start);
+
+	for (  ; num && sptr < slen; num--, l++) {
+		int ilen = strlen(l->display);
+
+		if (!l->show)
+			continue;
+
+		if (count)
+			ilen += strlen("  | ");
+
+		if (ilen > (slen - sptr))
+			break;
+
+		if (count++)
+			sptr += snprintf(str + sptr, slen - sptr, " | ");
+
+		sptr += snprintf(str + sptr, slen - sptr, "%s", l->display);
+	}
+
+	KGSL_LOG_DUMP(device, "%s\n", str);
+
+	*lines = l;
+	return num;
+}
+
+static void adreno_dump_fields(struct kgsl_device *device,
+			     const char *start, const struct log_field *lines,
+			     int num)
+{
+	char lb[90];
+	const char *sstr = start;
+
+	lb[sizeof(lb)  - 1] = '\0';
+
+	while (num) {
+		int ret = adreno_dump_fields_line(device, sstr, lb,
+			sizeof(lb) - 1, &lines, num);
+
+		if (ret == num)
+			break;
+
+		num = ret;
+		sstr = "        ";
+	}
+}
+
+static void adreno_dump_a3xx(struct kgsl_device *device)
+{
+	unsigned int r1, r2, r3, rbbm_status;
+	unsigned int cp_stat, rb_count;
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
+	kgsl_regread(device, adreno_dev->gpudev->reg_rbbm_status, &rbbm_status);
+	KGSL_LOG_DUMP(device, "RBBM:   STATUS   = %08X\n", rbbm_status);
+
+	{
+		struct log_field lines[] = {
+			{rbbm_status & BIT(0),  "HI busy     "},
+			{rbbm_status & BIT(1),  "CP ME busy  "},
+			{rbbm_status & BIT(2),  "CP PFP busy "},
+			{rbbm_status & BIT(14), "CP NRT busy "},
+			{rbbm_status & BIT(15), "VBIF busy   "},
+			{rbbm_status & BIT(16), "TSE busy    "},
+			{rbbm_status & BIT(17), "RAS busy    "},
+			{rbbm_status & BIT(18), "RB busy     "},
+			{rbbm_status & BIT(19), "PC DCALL bsy"},
+			{rbbm_status & BIT(20), "PC VSD busy "},
+			{rbbm_status & BIT(21), "VFD busy    "},
+			{rbbm_status & BIT(22), "VPC busy    "},
+			{rbbm_status & BIT(23), "UCHE busy   "},
+			{rbbm_status & BIT(24), "SP busy     "},
+			{rbbm_status & BIT(25), "TPL1 busy   "},
+			{rbbm_status & BIT(26), "MARB busy   "},
+			{rbbm_status & BIT(27), "VSC busy    "},
+			{rbbm_status & BIT(28), "ARB busy    "},
+			{rbbm_status & BIT(29), "HLSQ busy   "},
+			{rbbm_status & BIT(30), "GPU bsy noHC"},
+			{rbbm_status & BIT(31), "GPU busy    "},
+			};
+		adreno_dump_fields(device, " STATUS=", lines,
+				ARRAY_SIZE(lines));
+	}
+
+	kgsl_regread(device, REG_CP_RB_BASE, &r1);
+	kgsl_regread(device, REG_CP_RB_CNTL, &r2);
+	rb_count = 2 << (r2 & (BIT(6) - 1));
+	kgsl_regread(device, REG_CP_RB_RPTR_ADDR, &r3);
+	KGSL_LOG_DUMP(device,
+		"CP_RB:  BASE = %08X | CNTL   = %08X | RPTR_ADDR = %08X"
+		"| rb_count = %08X\n", r1, r2, r3, rb_count);
+
+	kgsl_regread(device, REG_CP_RB_RPTR, &r1);
+	kgsl_regread(device, REG_CP_RB_WPTR, &r2);
+	kgsl_regread(device, REG_CP_RB_RPTR_WR, &r3);
+	KGSL_LOG_DUMP(device,
+		"        RPTR = %08X | WPTR   = %08X | RPTR_WR   = %08X"
+		"\n", r1, r2, r3);
+
+	kgsl_regread(device, REG_CP_IB1_BASE, &r1);
+	kgsl_regread(device, REG_CP_IB1_BUFSZ, &r2);
+	KGSL_LOG_DUMP(device, "CP_IB1: BASE = %08X | BUFSZ  = %d\n", r1, r2);
+
+	kgsl_regread(device, REG_CP_ME_CNTL, &r1);
+	kgsl_regread(device, REG_CP_ME_STATUS, &r2);
+	KGSL_LOG_DUMP(device, "CP_ME:  CNTL = %08X | STATUS = %08X\n", r1, r2);
+
+	kgsl_regread(device, REG_CP_STAT, &cp_stat);
+	KGSL_LOG_DUMP(device, "CP_STAT      = %08X\n", cp_stat);
+#ifndef CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL
+	{
+		struct log_field lns[] = {
+			{cp_stat & BIT(0), "WR_BSY     0"},
+			{cp_stat & BIT(1), "RD_RQ_BSY  1"},
+			{cp_stat & BIT(2), "RD_RTN_BSY 2"},
+		};
+		adreno_dump_fields(device, "    MIU=", lns, ARRAY_SIZE(lns));
+	}
+	{
+		struct log_field lns[] = {
+			{cp_stat & BIT(5), "RING_BUSY  5"},
+			{cp_stat & BIT(6), "NDRCTS_BSY 6"},
+			{cp_stat & BIT(7), "NDRCT2_BSY 7"},
+			{cp_stat & BIT(9), "ST_BUSY    9"},
+			{cp_stat & BIT(10), "BUSY      10"},
+		};
+		adreno_dump_fields(device, "    CSF=", lns, ARRAY_SIZE(lns));
+	}
+	{
+		struct log_field lns[] = {
+			{cp_stat & BIT(11), "RNG_Q_BSY 11"},
+			{cp_stat & BIT(12), "NDRCTS_Q_B12"},
+			{cp_stat & BIT(13), "NDRCT2_Q_B13"},
+			{cp_stat & BIT(16), "ST_QUEUE_B16"},
+			{cp_stat & BIT(17), "PFP_BUSY  17"},
+		};
+		adreno_dump_fields(device, "   RING=", lns, ARRAY_SIZE(lns));
+	}
+	{
+		struct log_field lns[] = {
+			{cp_stat & BIT(3), "RBIU_BUSY  3"},
+			{cp_stat & BIT(4), "RCIU_BUSY  4"},
+			{cp_stat & BIT(8), "EVENT_BUSY 8"},
+			{cp_stat & BIT(18), "MQ_RG_BSY 18"},
+			{cp_stat & BIT(19), "MQ_NDRS_BS19"},
+			{cp_stat & BIT(20), "MQ_NDR2_BS20"},
+			{cp_stat & BIT(21), "MIU_WC_STL21"},
+			{cp_stat & BIT(22), "CP_NRT_BSY22"},
+			{cp_stat & BIT(23), "3D_BUSY   23"},
+			{cp_stat & BIT(26), "ME_BUSY   26"},
+			{cp_stat & BIT(27), "RB_FFO_BSY27"},
+			{cp_stat & BIT(28), "CF_FFO_BSY28"},
+			{cp_stat & BIT(29), "PS_FFO_BSY29"},
+			{cp_stat & BIT(30), "VS_FFO_BSY30"},
+			{cp_stat & BIT(31), "CP_BUSY   31"},
+		};
+		adreno_dump_fields(device, " CP_STT=", lns, ARRAY_SIZE(lns));
+	}
+#endif
+
+	kgsl_regread(device, A3XX_RBBM_INT_0_STATUS, &r1);
+	KGSL_LOG_DUMP(device, "MSTR_INT_SGNL = %08X\n", r1);
+	{
+		struct log_field ints[] = {
+			{r1 & BIT(0),  "RBBM_GPU_IDLE 0"},
+			{r1 & BIT(1),  "RBBM_AHB_ERROR 1"},
+			{r1 & BIT(2),  "RBBM_REG_TIMEOUT 2"},
+			{r1 & BIT(3),  "RBBM_ME_MS_TIMEOUT 3"},
+			{r1 & BIT(4),  "RBBM_PFP_MS_TIMEOUT 4"},
+			{r1 & BIT(5),  "RBBM_ATB_BUS_OVERFLOW 5"},
+			{r1 & BIT(6),  "VFD_ERROR 6"},
+			{r1 & BIT(7),  "CP_SW_INT 7"},
+			{r1 & BIT(8),  "CP_T0_PACKET_IN_IB 8"},
+			{r1 & BIT(9),  "CP_OPCODE_ERROR 9"},
+			{r1 & BIT(10), "CP_RESERVED_BIT_ERROR 10"},
+			{r1 & BIT(11), "CP_HW_FAULT 11"},
+			{r1 & BIT(12), "CP_DMA 12"},
+			{r1 & BIT(13), "CP_IB2_INT 13"},
+			{r1 & BIT(14), "CP_IB1_INT 14"},
+			{r1 & BIT(15), "CP_RB_INT 15"},
+			{r1 & BIT(16), "CP_REG_PROTECT_FAULT 16"},
+			{r1 & BIT(17), "CP_RB_DONE_TS 17"},
+			{r1 & BIT(18), "CP_VS_DONE_TS 18"},
+			{r1 & BIT(19), "CP_PS_DONE_TS 19"},
+			{r1 & BIT(20), "CACHE_FLUSH_TS 20"},
+			{r1 & BIT(21), "CP_AHB_ERROR_HALT 21"},
+			{r1 & BIT(24), "MISC_HANG_DETECT 24"},
+			{r1 & BIT(25), "UCHE_OOB_ACCESS 25"},
+		};
+		adreno_dump_fields(device, "INT_SGNL=", ints, ARRAY_SIZE(ints));
+	}
+}
+
+static void adreno_dump_a2xx(struct kgsl_device *device)
+{
+	unsigned int r1, r2, r3, rbbm_status;
+	unsigned int cp_stat, rb_count;
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
+	kgsl_regread(device, adreno_dev->gpudev->reg_rbbm_status, &rbbm_status);
+
+	kgsl_regread(device, REG_RBBM_PM_OVERRIDE1, &r2);
+	kgsl_regread(device, REG_RBBM_PM_OVERRIDE2, &r3);
+	KGSL_LOG_DUMP(device, "RBBM:   STATUS   = %08X | PM_OVERRIDE1 = %08X | "
+		"PM_OVERRIDE2 = %08X\n", rbbm_status, r2, r3);
+
+	kgsl_regread(device, REG_RBBM_INT_CNTL, &r1);
+	kgsl_regread(device, REG_RBBM_INT_STATUS, &r2);
+	kgsl_regread(device, REG_RBBM_READ_ERROR, &r3);
+	KGSL_LOG_DUMP(device, "        INT_CNTL = %08X | INT_STATUS   = %08X | "
+		"READ_ERROR   = %08X\n", r1, r2, r3);
+
+	{
+		char cmdFifo[16];
+		struct log_field lines[] = {
+			{rbbm_status &  0x001F, cmdFifo},
+			{rbbm_status &  BIT(5), "TC busy     "},
+			{rbbm_status &  BIT(8), "HIRQ pending"},
+			{rbbm_status &  BIT(9), "CPRQ pending"},
+			{rbbm_status & BIT(10), "CFRQ pending"},
+			{rbbm_status & BIT(11), "PFRQ pending"},
+			{rbbm_status & BIT(12), "VGT 0DMA bsy"},
+			{rbbm_status & BIT(14), "RBBM WU busy"},
+			{rbbm_status & BIT(16), "CP NRT busy "},
+			{rbbm_status & BIT(18), "MH busy     "},
+			{rbbm_status & BIT(19), "MH chncy bsy"},
+			{rbbm_status & BIT(21), "SX busy     "},
+			{rbbm_status & BIT(22), "TPC busy    "},
+			{rbbm_status & BIT(24), "SC CNTX busy"},
+			{rbbm_status & BIT(25), "PA busy     "},
+			{rbbm_status & BIT(26), "VGT busy    "},
+			{rbbm_status & BIT(27), "SQ cntx1 bsy"},
+			{rbbm_status & BIT(28), "SQ cntx0 bsy"},
+			{rbbm_status & BIT(30), "RB busy     "},
+			{rbbm_status & BIT(31), "Grphs pp bsy"},
+		};
+		snprintf(cmdFifo, sizeof(cmdFifo), "CMD FIFO=%01X  ",
+			rbbm_status & 0xf);
+		adreno_dump_fields(device, " STATUS=", lines,
+				ARRAY_SIZE(lines));
+	}
+
+	kgsl_regread(device, REG_CP_RB_BASE, &r1);
+	kgsl_regread(device, REG_CP_RB_CNTL, &r2);
+	rb_count = 2 << (r2 & (BIT(6)-1));
+	kgsl_regread(device, REG_CP_RB_RPTR_ADDR, &r3);
+	KGSL_LOG_DUMP(device,
+		"CP_RB:  BASE = %08X | CNTL   = %08X | RPTR_ADDR = %08X"
+		"| rb_count = %08X\n", r1, r2, r3, rb_count);
+	{
+		struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
+		if (rb->sizedwords != rb_count)
+			rb_count = rb->sizedwords;
+	}
+
+	kgsl_regread(device, REG_CP_RB_RPTR, &r1);
+	kgsl_regread(device, REG_CP_RB_WPTR, &r2);
+	kgsl_regread(device, REG_CP_RB_RPTR_WR, &r3);
+	KGSL_LOG_DUMP(device,
+		"        RPTR = %08X | WPTR   = %08X | RPTR_WR   = %08X"
+		"\n", r1, r2, r3);
+
+	kgsl_regread(device, REG_CP_IB1_BASE, &r1);
+	kgsl_regread(device, REG_CP_IB1_BUFSZ, &r2);
+	KGSL_LOG_DUMP(device, "CP_IB1: BASE = %08X | BUFSZ  = %d\n", r1, r2);
+
+	kgsl_regread(device, REG_CP_IB2_BASE, &r1);
+	kgsl_regread(device, REG_CP_IB2_BUFSZ, &r2);
+	KGSL_LOG_DUMP(device, "CP_IB2: BASE = %08X | BUFSZ  = %d\n", r1, r2);
+
+	kgsl_regread(device, REG_CP_INT_CNTL, &r1);
+	kgsl_regread(device, REG_CP_INT_STATUS, &r2);
+	KGSL_LOG_DUMP(device, "CP_INT: CNTL = %08X | STATUS = %08X\n", r1, r2);
+
+	kgsl_regread(device, REG_CP_ME_CNTL, &r1);
+	kgsl_regread(device, REG_CP_ME_STATUS, &r2);
+	kgsl_regread(device, REG_MASTER_INT_SIGNAL, &r3);
+	KGSL_LOG_DUMP(device,
+		"CP_ME:  CNTL = %08X | STATUS = %08X | MSTR_INT_SGNL = "
+		"%08X\n", r1, r2, r3);
+
+	kgsl_regread(device, REG_CP_STAT, &cp_stat);
+	KGSL_LOG_DUMP(device, "CP_STAT      = %08X\n", cp_stat);
+#ifndef CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL
+	{
+		struct log_field lns[] = {
+			{cp_stat &  BIT(0), "WR_BSY     0"},
+			{cp_stat &  BIT(1), "RD_RQ_BSY  1"},
+			{cp_stat &  BIT(2), "RD_RTN_BSY 2"},
+		};
+		adreno_dump_fields(device, "    MIU=", lns, ARRAY_SIZE(lns));
+	}
+	{
+		struct log_field lns[] = {
+			{cp_stat &  BIT(5), "RING_BUSY  5"},
+			{cp_stat &  BIT(6), "NDRCTS_BSY 6"},
+			{cp_stat &  BIT(7), "NDRCT2_BSY 7"},
+			{cp_stat &  BIT(9), "ST_BUSY    9"},
+			{cp_stat & BIT(10), "BUSY      10"},
+		};
+		adreno_dump_fields(device, "    CSF=", lns, ARRAY_SIZE(lns));
+	}
+	{
+		struct log_field lns[] = {
+			{cp_stat & BIT(11), "RNG_Q_BSY 11"},
+			{cp_stat & BIT(12), "NDRCTS_Q_B12"},
+			{cp_stat & BIT(13), "NDRCT2_Q_B13"},
+			{cp_stat & BIT(16), "ST_QUEUE_B16"},
+			{cp_stat & BIT(17), "PFP_BUSY  17"},
+		};
+		adreno_dump_fields(device, "   RING=", lns, ARRAY_SIZE(lns));
+	}
+	{
+		struct log_field lns[] = {
+			{cp_stat &  BIT(3), "RBIU_BUSY  3"},
+			{cp_stat &  BIT(4), "RCIU_BUSY  4"},
+			{cp_stat & BIT(18), "MQ_RG_BSY 18"},
+			{cp_stat & BIT(19), "MQ_NDRS_BS19"},
+			{cp_stat & BIT(20), "MQ_NDR2_BS20"},
+			{cp_stat & BIT(21), "MIU_WC_STL21"},
+			{cp_stat & BIT(22), "CP_NRT_BSY22"},
+			{cp_stat & BIT(23), "3D_BUSY   23"},
+			{cp_stat & BIT(26), "ME_BUSY   26"},
+			{cp_stat & BIT(29), "ME_WC_BSY 29"},
+			{cp_stat & BIT(30), "MIU_FF EM 30"},
+			{cp_stat & BIT(31), "CP_BUSY   31"},
+		};
+		adreno_dump_fields(device, " CP_STT=", lns, ARRAY_SIZE(lns));
+	}
+#endif
+
+	kgsl_regread(device, REG_SCRATCH_REG0, &r1);
+	KGSL_LOG_DUMP(device, "SCRATCH_REG0       = %08X\n", r1);
+
+	kgsl_regread(device, REG_COHER_SIZE_PM4, &r1);
+	kgsl_regread(device, REG_COHER_BASE_PM4, &r2);
+	kgsl_regread(device, REG_COHER_STATUS_PM4, &r3);
+	KGSL_LOG_DUMP(device,
+		"COHER:  SIZE_PM4   = %08X | BASE_PM4 = %08X | STATUS_PM4"
+		" = %08X\n", r1, r2, r3);
+
+	kgsl_regread(device, MH_AXI_ERROR, &r1);
+	KGSL_LOG_DUMP(device, "MH:     AXI_ERROR  = %08X\n", r1);
+
+	kgsl_regread(device, MH_MMU_PAGE_FAULT, &r1);
+	kgsl_regread(device, MH_MMU_CONFIG, &r2);
+	kgsl_regread(device, MH_MMU_MPU_BASE, &r3);
+	KGSL_LOG_DUMP(device,
+		"MH_MMU: PAGE_FAULT = %08X | CONFIG   = %08X | MPU_BASE ="
+		" %08X\n", r1, r2, r3);
+
+	kgsl_regread(device, MH_MMU_MPU_END, &r1);
+	kgsl_regread(device, MH_MMU_VA_RANGE, &r2);
+	r3 = kgsl_mmu_get_current_ptbase(&device->mmu);
+	KGSL_LOG_DUMP(device,
+		"        MPU_END    = %08X | VA_RANGE = %08X | PT_BASE  ="
+		" %08X\n", r1, r2, r3);
+
+	KGSL_LOG_DUMP(device, "PAGETABLE SIZE: %08X ",
+		kgsl_mmu_get_ptsize());
+
+	kgsl_regread(device, MH_MMU_TRAN_ERROR, &r1);
+	KGSL_LOG_DUMP(device, "        TRAN_ERROR = %08X\n", r1);
+
+	kgsl_regread(device, MH_INTERRUPT_MASK, &r1);
+	kgsl_regread(device, MH_INTERRUPT_STATUS, &r2);
+	KGSL_LOG_DUMP(device,
+		"MH_INTERRUPT: MASK = %08X | STATUS   = %08X\n", r1, r2);
+}
+
+static int adreno_dump(struct kgsl_device *device)
+{
+	unsigned int cp_ib1_base, cp_ib1_bufsz;
+	unsigned int cp_ib2_base, cp_ib2_bufsz;
+	unsigned int pt_base, cur_pt_base;
+	unsigned int cp_rb_base, cp_rb_ctrl, rb_count;
+	unsigned int cp_rb_wptr, cp_rb_rptr;
+	unsigned int i;
+	int result = 0;
+	uint32_t *rb_copy;
+	const uint32_t *rb_vaddr;
+	int num_item = 0;
+	int read_idx, write_idx;
+	unsigned int ts_processed = 0xdeaddead;
+	struct kgsl_context *context;
+	unsigned int context_id;
+
+	static struct ib_list ib_list;
+
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
+	mb();
+
+	if (adreno_is_a2xx(adreno_dev))
+		adreno_dump_a2xx(device);
+	else if (adreno_is_a3xx(adreno_dev))
+		adreno_dump_a3xx(device);
+
+	pt_base = kgsl_mmu_get_current_ptbase(&device->mmu);
+	cur_pt_base = pt_base;
+
+	kgsl_regread(device, REG_CP_RB_BASE, &cp_rb_base);
+	kgsl_regread(device, REG_CP_RB_CNTL, &cp_rb_ctrl);
+	rb_count = 2 << (cp_rb_ctrl & (BIT(6) - 1));
+	kgsl_regread(device, REG_CP_RB_RPTR, &cp_rb_rptr);
+	kgsl_regread(device, REG_CP_RB_WPTR, &cp_rb_wptr);
+	kgsl_regread(device, REG_CP_IB1_BASE, &cp_ib1_base);
+	kgsl_regread(device, REG_CP_IB1_BUFSZ, &cp_ib1_bufsz);
+	kgsl_regread(device, REG_CP_IB2_BASE, &cp_ib2_base);
+	kgsl_regread(device, REG_CP_IB2_BUFSZ, &cp_ib2_bufsz);
+
+	kgsl_sharedmem_readl(&device->memstore,
+			(unsigned int *) &context_id,
+			KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
+				current_context));
+	context = idr_find(&device->context_idr, context_id);
+	if (context) {
+		ts_processed = kgsl_readtimestamp(device, context,
+						  KGSL_TIMESTAMP_RETIRED);
+		KGSL_LOG_DUMP(device, "CTXT: %d  TIMESTM RTRD: %08X\n",
+				context->id, ts_processed);
+	} else
+		KGSL_LOG_DUMP(device, "BAD CTXT: %d\n", context_id);
+
+	num_item = adreno_ringbuffer_count(&adreno_dev->ringbuffer,
+						cp_rb_rptr);
+	if (num_item <= 0)
+		KGSL_LOG_POSTMORTEM_WRITE(device, "Ringbuffer is Empty.\n");
+
+	rb_copy = vmalloc(rb_count<<2);
+	if (!rb_copy) {
+		KGSL_LOG_POSTMORTEM_WRITE(device,
+			"vmalloc(%d) failed\n", rb_count << 2);
+		result = -ENOMEM;
+		goto end;
+	}
+
+	KGSL_LOG_DUMP(device, "RB: rd_addr:%8.8x  rb_size:%d  num_item:%d\n",
+		cp_rb_base, rb_count<<2, num_item);
+
+	if (adreno_dev->ringbuffer.buffer_desc.gpuaddr != cp_rb_base)
+		KGSL_LOG_POSTMORTEM_WRITE(device,
+			"rb address mismatch, should be 0x%08x\n",
+			adreno_dev->ringbuffer.buffer_desc.gpuaddr);
+
+	rb_vaddr = adreno_dev->ringbuffer.buffer_desc.hostptr;
+	if (!rb_vaddr) {
+		KGSL_LOG_POSTMORTEM_WRITE(device,
+			"rb has no kernel mapping!\n");
+		goto error_vfree;
+	}
+
+	read_idx = (int)cp_rb_rptr - NUM_DWORDS_OF_RINGBUFFER_HISTORY;
+	if (read_idx < 0)
+		read_idx += rb_count;
+	write_idx = (int)cp_rb_wptr + 16;
+	if (write_idx > rb_count)
+		write_idx -= rb_count;
+	num_item += NUM_DWORDS_OF_RINGBUFFER_HISTORY+16;
+	if (num_item > rb_count)
+		num_item = rb_count;
+	if (write_idx >= read_idx)
+		memcpy(rb_copy, rb_vaddr+read_idx, num_item<<2);
+	else {
+		int part1_c = rb_count-read_idx;
+		memcpy(rb_copy, rb_vaddr+read_idx, part1_c<<2);
+		memcpy(rb_copy+part1_c, rb_vaddr, (num_item-part1_c)<<2);
+	}
+
+	/* extract the latest ib commands from the buffer */
+	ib_list.count = 0;
+	i = 0;
+	for (read_idx = 0; read_idx < num_item; ) {
+		uint32_t this_cmd = rb_copy[read_idx++];
+		if (adreno_cmd_is_ib(this_cmd)) {
+			uint32_t ib_addr = rb_copy[read_idx++];
+			uint32_t ib_size = rb_copy[read_idx++];
+			dump_ib1(device, cur_pt_base, (read_idx-3)<<2, ib_addr,
+				ib_size, &ib_list, 0);
+			for (; i < ib_list.count; ++i)
+				dump_ib(device, "IB2:", cur_pt_base,
+					ib_list.offsets[i],
+					ib_list.bases[i],
+					ib_list.sizes[i], 0);
+		} else if (this_cmd == cp_type0_packet(MH_MMU_PT_BASE, 1)) {
+
+			KGSL_LOG_DUMP(device, "Current pagetable: %x\t"
+				"pagetable base: %x\n",
+				kgsl_mmu_get_ptname_from_ptbase(cur_pt_base),
+				cur_pt_base);
+
+			/* Set cur_pt_base to the new pagetable base */
+			cur_pt_base = rb_copy[read_idx++];
+
+			KGSL_LOG_DUMP(device, "New pagetable: %x\t"
+				"pagetable base: %x\n",
+				kgsl_mmu_get_ptname_from_ptbase(cur_pt_base),
+				cur_pt_base);
+		}
+	}
+
+	/* Restore cur_pt_base back to the pt_base of
+	   the process in whose context the GPU hung */
+	cur_pt_base = pt_base;
+
+	read_idx = (int)cp_rb_rptr - NUM_DWORDS_OF_RINGBUFFER_HISTORY;
+	if (read_idx < 0)
+		read_idx += rb_count;
+	KGSL_LOG_DUMP(device,
+		"RB: addr=%8.8x  window:%4.4x-%4.4x, start:%4.4x\n",
+		cp_rb_base, cp_rb_rptr, cp_rb_wptr, read_idx);
+	adreno_dump_rb(device, rb_copy, num_item<<2, read_idx, rb_count);
+
+	if (is_adreno_pm_ib_enabled()) {
+		for (read_idx = NUM_DWORDS_OF_RINGBUFFER_HISTORY;
+			read_idx >= 0; --read_idx) {
+			uint32_t this_cmd = rb_copy[read_idx];
+			if (adreno_cmd_is_ib(this_cmd)) {
+				uint32_t ib_addr = rb_copy[read_idx+1];
+				uint32_t ib_size = rb_copy[read_idx+2];
+				if (ib_size && cp_ib1_base == ib_addr) {
+					KGSL_LOG_DUMP(device,
+						"IB1: base:%8.8X  "
+						"count:%d\n", ib_addr, ib_size);
+					dump_ib(device, "IB1: ", cur_pt_base,
+						read_idx<<2, ib_addr, ib_size,
+						1);
+				}
+			}
+		}
+		for (i = 0; i < ib_list.count; ++i) {
+			uint32_t ib_size = ib_list.sizes[i];
+			uint32_t ib_offset = ib_list.offsets[i];
+			if (ib_size && cp_ib2_base == ib_list.bases[i]) {
+				KGSL_LOG_DUMP(device,
+					"IB2: base:%8.8X  count:%d\n",
+					cp_ib2_base, ib_size);
+				dump_ib(device, "IB2: ", cur_pt_base, ib_offset,
+					ib_list.bases[i], ib_size, 1);
+			}
+		}
+	}
+
+	/* Dump the registers if the user asked for it */
+	if (is_adreno_pm_regs_enabled()) {
+		if (adreno_is_a20x(adreno_dev))
+			adreno_dump_regs(device, a200_registers,
+					a200_registers_count);
+		else if (adreno_is_a22x(adreno_dev))
+			adreno_dump_regs(device, a220_registers,
+					a220_registers_count);
+		else if (adreno_is_a225(adreno_dev))
+			adreno_dump_regs(device, a225_registers,
+				a225_registers_count);
+		else if (adreno_is_a3xx(adreno_dev))
+			adreno_dump_regs(device, a3xx_registers,
+					a3xx_registers_count);
+	}
+
+error_vfree:
+	vfree(rb_copy);
+end:
+	return result;
+}
+
+/**
+ * adreno_postmortem_dump - Dump the current GPU state
+ * @device - A pointer to the KGSL device to dump
+ * @manual - A flag that indicates if this was a manually triggered
+ *           dump (from debugfs).  If zero, then this is assumed to be a
+ *           dump automaticlaly triggered from a hang
+*/
+
+int adreno_postmortem_dump(struct kgsl_device *device, int manual)
+{
+	bool saved_nap;
+	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+
+	BUG_ON(device == NULL);
+
+	kgsl_cffdump_hang(device->id);
+
+	/* For a manual dump, make sure that the system is idle */
+
+	if (manual) {
+		if (device->active_cnt != 0) {
+			mutex_unlock(&device->mutex);
+			wait_for_completion(&device->suspend_gate);
+			mutex_lock(&device->mutex);
+		}
+
+		if (device->state == KGSL_STATE_ACTIVE)
+			kgsl_idle(device,  KGSL_TIMEOUT_DEFAULT);
+
+	}
+	KGSL_LOG_DUMP(device, "POWER: FLAGS = %08lX | ACTIVE POWERLEVEL = %08X",
+			pwr->power_flags, pwr->active_pwrlevel);
+
+	KGSL_LOG_DUMP(device, "POWER: INTERVAL TIMEOUT = %08X ",
+		pwr->interval_timeout);
+
+	KGSL_LOG_DUMP(device, "GRP_CLK = %lu ",
+				  kgsl_get_clkrate(pwr->grp_clks[0]));
+
+	KGSL_LOG_DUMP(device, "BUS CLK = %lu ",
+		kgsl_get_clkrate(pwr->ebi1_clk));
+
+	/* Disable the idle timer so we don't get interrupted */
+	del_timer_sync(&device->idle_timer);
+	mutex_unlock(&device->mutex);
+	flush_workqueue(device->work_queue);
+	mutex_lock(&device->mutex);
+
+	/* Turn off napping to make sure we have the clocks full
+	   attention through the following process */
+	saved_nap = device->pwrctrl.nap_allowed;
+	device->pwrctrl.nap_allowed = false;
+
+	/* Force on the clocks */
+	kgsl_pwrctrl_wake(device);
+
+	/* Disable the irq */
+	kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF);
+
+	adreno_dump(device);
+
+	/* Restore nap mode */
+	device->pwrctrl.nap_allowed = saved_nap;
+
+	/* On a manual trigger, turn on the interrupts and put
+	   the clocks to sleep.  They will recover themselves
+	   on the next event.  For a hang, leave things as they
+	   are until recovery kicks in. */
+
+	if (manual) {
+		kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_ON);
+
+		/* try to go into a sleep mode until the next event */
+		kgsl_pwrctrl_request_state(device, KGSL_STATE_SLEEP);
+		kgsl_pwrctrl_sleep(device);
+	}
+
+	KGSL_DRV_ERR(device, "Dump Finished\n");
+
+	return 0;
+}
diff --git a/drivers/gpu/msm/adreno_postmortem.h b/drivers/gpu/msm/adreno_postmortem.h
new file mode 100644
index 0000000..b677800
--- /dev/null
+++ b/drivers/gpu/msm/adreno_postmortem.h
@@ -0,0 +1,21 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __ADRENO_POSTMORTEM_H
+#define __ADRENO_POSTMORTEM_H
+
+struct kgsl_device;
+
+int adreno_postmortem_dump(struct kgsl_device *device, int manual);
+
+#endif /* __ADRENO_POSTMORTEM_H */
diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c
new file mode 100644
index 0000000..3d46221
--- /dev/null
+++ b/drivers/gpu/msm/adreno_ringbuffer.c
@@ -0,0 +1,1101 @@
+/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/log2.h>
+
+#include "kgsl.h"
+#include "kgsl_sharedmem.h"
+#include "kgsl_cffdump.h"
+
+#include "adreno.h"
+#include "adreno_pm4types.h"
+#include "adreno_ringbuffer.h"
+#include "adreno_debugfs.h"
+
+#include "a2xx_reg.h"
+#include "a3xx_reg.h"
+
+#define GSL_RB_NOP_SIZEDWORDS				2
+
+void adreno_ringbuffer_submit(struct adreno_ringbuffer *rb)
+{
+	BUG_ON(rb->wptr == 0);
+
+	/* Let the pwrscale policy know that new commands have
+	 been submitted. */
+	kgsl_pwrscale_busy(rb->device);
+
+	/*synchronize memory before informing the hardware of the
+	 *new commands.
+	 */
+	mb();
+
+	adreno_regwrite(rb->device, REG_CP_RB_WPTR, rb->wptr);
+}
+
+static void
+adreno_ringbuffer_waitspace(struct adreno_ringbuffer *rb, unsigned int numcmds,
+			  int wptr_ahead)
+{
+	int nopcount;
+	unsigned int freecmds;
+	unsigned int *cmds;
+	uint cmds_gpu;
+
+	/* if wptr ahead, fill the remaining with NOPs */
+	if (wptr_ahead) {
+		/* -1 for header */
+		nopcount = rb->sizedwords - rb->wptr - 1;
+
+		cmds = (unsigned int *)rb->buffer_desc.hostptr + rb->wptr;
+		cmds_gpu = rb->buffer_desc.gpuaddr + sizeof(uint)*rb->wptr;
+
+		GSL_RB_WRITE(cmds, cmds_gpu, cp_nop_packet(nopcount));
+
+		/* Make sure that rptr is not 0 before submitting
+		 * commands at the end of ringbuffer. We do not
+		 * want the rptr and wptr to become equal when
+		 * the ringbuffer is not empty */
+		do {
+			GSL_RB_GET_READPTR(rb, &rb->rptr);
+		} while (!rb->rptr);
+
+		rb->wptr++;
+
+		adreno_ringbuffer_submit(rb);
+
+		rb->wptr = 0;
+	}
+
+	/* wait for space in ringbuffer */
+	do {
+		GSL_RB_GET_READPTR(rb, &rb->rptr);
+
+		freecmds = rb->rptr - rb->wptr;
+
+	} while ((freecmds != 0) && (freecmds <= numcmds));
+}
+
+unsigned int *adreno_ringbuffer_allocspace(struct adreno_ringbuffer *rb,
+					     unsigned int numcmds)
+{
+	unsigned int	*ptr = NULL;
+
+	BUG_ON(numcmds >= rb->sizedwords);
+
+	GSL_RB_GET_READPTR(rb, &rb->rptr);
+	/* check for available space */
+	if (rb->wptr >= rb->rptr) {
+		/* wptr ahead or equal to rptr */
+		/* reserve dwords for nop packet */
+		if ((rb->wptr + numcmds) > (rb->sizedwords -
+				GSL_RB_NOP_SIZEDWORDS))
+			adreno_ringbuffer_waitspace(rb, numcmds, 1);
+	} else {
+		/* wptr behind rptr */
+		if ((rb->wptr + numcmds) >= rb->rptr)
+			adreno_ringbuffer_waitspace(rb, numcmds, 0);
+		/* check for remaining space */
+		/* reserve dwords for nop packet */
+		if ((rb->wptr + numcmds) > (rb->sizedwords -
+				GSL_RB_NOP_SIZEDWORDS))
+			adreno_ringbuffer_waitspace(rb, numcmds, 1);
+	}
+
+	ptr = (unsigned int *)rb->buffer_desc.hostptr + rb->wptr;
+	rb->wptr += numcmds;
+
+	return ptr;
+}
+
+static int _load_firmware(struct kgsl_device *device, const char *fwfile,
+			  void **data, int *len)
+{
+	const struct firmware *fw = NULL;
+	int ret;
+
+	ret = request_firmware(&fw, fwfile, device->dev);
+
+	if (ret) {
+		KGSL_DRV_ERR(device, "request_firmware(%s) failed: %d\n",
+			     fwfile, ret);
+		return ret;
+	}
+
+	*data = kmalloc(fw->size, GFP_KERNEL);
+
+	if (*data) {
+		memcpy(*data, fw->data, fw->size);
+		*len = fw->size;
+	} else
+		KGSL_MEM_ERR(device, "kmalloc(%d) failed\n", fw->size);
+
+	release_firmware(fw);
+	return (*data != NULL) ? 0 : -ENOMEM;
+}
+
+static int adreno_ringbuffer_load_pm4_ucode(struct kgsl_device *device)
+{
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	int i, ret = 0;
+
+	if (adreno_dev->pm4_fw == NULL) {
+		int len;
+		void *ptr;
+
+		ret = _load_firmware(device, adreno_dev->pm4_fwfile,
+			&ptr, &len);
+
+		if (ret)
+			goto err;
+
+		/* PM4 size is 3 dword aligned plus 1 dword of version */
+		if (len % ((sizeof(uint32_t) * 3)) != sizeof(uint32_t)) {
+			KGSL_DRV_ERR(device, "Bad firmware size: %d\n", len);
+			ret = -EINVAL;
+			kfree(ptr);
+			goto err;
+		}
+
+		adreno_dev->pm4_fw_size = len / sizeof(uint32_t);
+		adreno_dev->pm4_fw = ptr;
+	}
+
+	KGSL_DRV_INFO(device, "loading pm4 ucode version: %d\n",
+		adreno_dev->pm4_fw[0]);
+
+	adreno_regwrite(device, REG_CP_DEBUG, 0x02000000);
+	adreno_regwrite(device, REG_CP_ME_RAM_WADDR, 0);
+	for (i = 1; i < adreno_dev->pm4_fw_size; i++)
+		adreno_regwrite(device, REG_CP_ME_RAM_DATA,
+				     adreno_dev->pm4_fw[i]);
+err:
+	return ret;
+}
+
+static int adreno_ringbuffer_load_pfp_ucode(struct kgsl_device *device)
+{
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	int i, ret = 0;
+
+	if (adreno_dev->pfp_fw == NULL) {
+		int len;
+		void *ptr;
+
+		ret = _load_firmware(device, adreno_dev->pfp_fwfile,
+			&ptr, &len);
+		if (ret)
+			goto err;
+
+		/* PFP size shold be dword aligned */
+		if (len % sizeof(uint32_t) != 0) {
+			KGSL_DRV_ERR(device, "Bad firmware size: %d\n", len);
+			ret = -EINVAL;
+			kfree(ptr);
+			goto err;
+		}
+
+		adreno_dev->pfp_fw_size = len / sizeof(uint32_t);
+		adreno_dev->pfp_fw = ptr;
+	}
+
+	KGSL_DRV_INFO(device, "loading pfp ucode version: %d\n",
+		adreno_dev->pfp_fw[0]);
+
+	adreno_regwrite(device, adreno_dev->gpudev->reg_cp_pfp_ucode_addr, 0);
+	for (i = 1; i < adreno_dev->pfp_fw_size; i++)
+		adreno_regwrite(device,
+			adreno_dev->gpudev->reg_cp_pfp_ucode_data,
+			adreno_dev->pfp_fw[i]);
+err:
+	return ret;
+}
+
+int adreno_ringbuffer_start(struct adreno_ringbuffer *rb, unsigned int init_ram)
+{
+	int status;
+	/*cp_rb_cntl_u cp_rb_cntl; */
+	union reg_cp_rb_cntl cp_rb_cntl;
+	unsigned int rb_cntl;
+	struct kgsl_device *device = rb->device;
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
+	if (rb->flags & KGSL_FLAGS_STARTED)
+		return 0;
+
+	if (init_ram)
+		rb->timestamp[KGSL_MEMSTORE_GLOBAL] = 0;
+
+	kgsl_sharedmem_set(&rb->memptrs_desc, 0, 0,
+			   sizeof(struct kgsl_rbmemptrs));
+
+	kgsl_sharedmem_set(&rb->buffer_desc, 0, 0xAA,
+			   (rb->sizedwords << 2));
+
+	if (adreno_is_a2xx(adreno_dev)) {
+		adreno_regwrite(device, REG_CP_RB_WPTR_BASE,
+			(rb->memptrs_desc.gpuaddr
+			+ GSL_RB_MEMPTRS_WPTRPOLL_OFFSET));
+
+		/* setup WPTR delay */
+		adreno_regwrite(device, REG_CP_RB_WPTR_DELAY,
+			0 /*0x70000010 */);
+	}
+
+	/*setup REG_CP_RB_CNTL */
+	adreno_regread(device, REG_CP_RB_CNTL, &rb_cntl);
+	cp_rb_cntl.val = rb_cntl;
+
+	/*
+	 * The size of the ringbuffer in the hardware is the log2
+	 * representation of the size in quadwords (sizedwords / 2)
+	 */
+	cp_rb_cntl.f.rb_bufsz = ilog2(rb->sizedwords >> 1);
+
+	/*
+	 * Specify the quadwords to read before updating mem RPTR.
+	 * Like above, pass the log2 representation of the blocksize
+	 * in quadwords.
+	*/
+	cp_rb_cntl.f.rb_blksz = ilog2(KGSL_RB_BLKSIZE >> 3);
+
+	if (adreno_is_a2xx(adreno_dev)) {
+		/* WPTR polling */
+		cp_rb_cntl.f.rb_poll_en = GSL_RB_CNTL_POLL_EN;
+	}
+
+	/* mem RPTR writebacks */
+	cp_rb_cntl.f.rb_no_update =  GSL_RB_CNTL_NO_UPDATE;
+
+	adreno_regwrite(device, REG_CP_RB_CNTL, cp_rb_cntl.val);
+
+	adreno_regwrite(device, REG_CP_RB_BASE, rb->buffer_desc.gpuaddr);
+
+	adreno_regwrite(device, REG_CP_RB_RPTR_ADDR,
+			     rb->memptrs_desc.gpuaddr +
+			     GSL_RB_MEMPTRS_RPTR_OFFSET);
+
+	if (adreno_is_a3xx(adreno_dev)) {
+		/* enable access protection to privileged registers */
+		adreno_regwrite(device, A3XX_CP_PROTECT_CTRL, 0x00000007);
+
+		/* RBBM registers */
+		adreno_regwrite(device, A3XX_CP_PROTECT_REG_0, 0x63000040);
+		adreno_regwrite(device, A3XX_CP_PROTECT_REG_1, 0x62000080);
+		adreno_regwrite(device, A3XX_CP_PROTECT_REG_2, 0x600000CC);
+		adreno_regwrite(device, A3XX_CP_PROTECT_REG_3, 0x60000108);
+		adreno_regwrite(device, A3XX_CP_PROTECT_REG_4, 0x64000140);
+		adreno_regwrite(device, A3XX_CP_PROTECT_REG_5, 0x66000400);
+
+		/* CP registers */
+		adreno_regwrite(device, A3XX_CP_PROTECT_REG_6, 0x65000700);
+		adreno_regwrite(device, A3XX_CP_PROTECT_REG_7, 0x610007D8);
+		adreno_regwrite(device, A3XX_CP_PROTECT_REG_8, 0x620007E0);
+		adreno_regwrite(device, A3XX_CP_PROTECT_REG_9, 0x61001178);
+		adreno_regwrite(device, A3XX_CP_PROTECT_REG_A, 0x64001180);
+
+		/* RB registers */
+		adreno_regwrite(device, A3XX_CP_PROTECT_REG_B, 0x60003300);
+
+		/* VBIF registers */
+		adreno_regwrite(device, A3XX_CP_PROTECT_REG_C, 0x6B00C000);
+	}
+
+	if (adreno_is_a2xx(adreno_dev)) {
+		/* explicitly clear all cp interrupts */
+		adreno_regwrite(device, REG_CP_INT_ACK, 0xFFFFFFFF);
+	}
+
+	/* setup scratch/timestamp */
+	adreno_regwrite(device, REG_SCRATCH_ADDR, device->memstore.gpuaddr +
+			     KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
+				     soptimestamp));
+
+	adreno_regwrite(device, REG_SCRATCH_UMSK,
+			     GSL_RB_MEMPTRS_SCRATCH_MASK);
+
+	/* load the CP ucode */
+
+	status = adreno_ringbuffer_load_pm4_ucode(device);
+	if (status != 0)
+		return status;
+
+	/* load the prefetch parser ucode */
+	status = adreno_ringbuffer_load_pfp_ucode(device);
+	if (status != 0)
+		return status;
+
+	/* CP ROQ queue sizes (bytes) - RB:16, ST:16, IB1:32, IB2:64 */
+	if (adreno_is_a305(adreno_dev) || adreno_is_a320(adreno_dev))
+		adreno_regwrite(device, REG_CP_QUEUE_THRESHOLDS, 0x000E0602);
+
+	rb->rptr = 0;
+	rb->wptr = 0;
+
+	/* clear ME_HALT to start micro engine */
+	adreno_regwrite(device, REG_CP_ME_CNTL, 0);
+
+	/* ME init is GPU specific, so jump into the sub-function */
+	adreno_dev->gpudev->rb_init(adreno_dev, rb);
+
+	/* idle device to validate ME INIT */
+	status = adreno_idle(device, KGSL_TIMEOUT_DEFAULT);
+
+	if (status == 0)
+		rb->flags |= KGSL_FLAGS_STARTED;
+
+	return status;
+}
+
+void adreno_ringbuffer_stop(struct adreno_ringbuffer *rb)
+{
+	if (rb->flags & KGSL_FLAGS_STARTED) {
+		/* ME_HALT */
+		adreno_regwrite(rb->device, REG_CP_ME_CNTL, 0x10000000);
+		rb->flags &= ~KGSL_FLAGS_STARTED;
+	}
+}
+
+int adreno_ringbuffer_init(struct kgsl_device *device)
+{
+	int status;
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
+
+	rb->device = device;
+	/*
+	 * It is silly to convert this to words and then back to bytes
+	 * immediately below, but most of the rest of the code deals
+	 * in words, so we might as well only do the math once
+	 */
+	rb->sizedwords = KGSL_RB_SIZE >> 2;
+
+	/* allocate memory for ringbuffer */
+	status = kgsl_allocate_contiguous(&rb->buffer_desc,
+		(rb->sizedwords << 2));
+
+	if (status != 0) {
+		adreno_ringbuffer_close(rb);
+		return status;
+	}
+
+	/* allocate memory for polling and timestamps */
+	/* This really can be at 4 byte alignment boundry but for using MMU
+	 * we need to make it at page boundary */
+	status = kgsl_allocate_contiguous(&rb->memptrs_desc,
+		sizeof(struct kgsl_rbmemptrs));
+
+	if (status != 0) {
+		adreno_ringbuffer_close(rb);
+		return status;
+	}
+
+	/* overlay structure on memptrs memory */
+	rb->memptrs = (struct kgsl_rbmemptrs *) rb->memptrs_desc.hostptr;
+
+	return 0;
+}
+
+void adreno_ringbuffer_close(struct adreno_ringbuffer *rb)
+{
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(rb->device);
+
+	kgsl_sharedmem_free(&rb->buffer_desc);
+	kgsl_sharedmem_free(&rb->memptrs_desc);
+
+	kfree(adreno_dev->pfp_fw);
+	kfree(adreno_dev->pm4_fw);
+
+	adreno_dev->pfp_fw = NULL;
+	adreno_dev->pm4_fw = NULL;
+
+	memset(rb, 0, sizeof(struct adreno_ringbuffer));
+}
+
+static uint32_t
+adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb,
+				struct adreno_context *context,
+				unsigned int flags, unsigned int *cmds,
+				int sizedwords)
+{
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(rb->device);
+	unsigned int *ringcmds;
+	unsigned int timestamp;
+	unsigned int total_sizedwords = sizedwords;
+	unsigned int i;
+	unsigned int rcmd_gpu;
+	unsigned int context_id = KGSL_MEMSTORE_GLOBAL;
+	unsigned int gpuaddr = rb->device->memstore.gpuaddr;
+
+	if (context != NULL) {
+		/*
+		 * if the context was not created with per context timestamp
+		 * support, we must use the global timestamp since issueibcmds
+		 * will be returning that one.
+		 */
+		if (context->flags & CTXT_FLAGS_PER_CONTEXT_TS)
+			context_id = context->id;
+	}
+
+	/* reserve space to temporarily turn off protected mode
+	*  error checking if needed
+	*/
+	total_sizedwords += flags & KGSL_CMD_FLAGS_PMODE ? 4 : 0;
+	total_sizedwords += !(flags & KGSL_CMD_FLAGS_NO_TS_CMP) ? 7 : 0;
+	total_sizedwords += !(flags & KGSL_CMD_FLAGS_NOT_KERNEL_CMD) ? 2 : 0;
+
+	if (adreno_is_a3xx(adreno_dev))
+		total_sizedwords += 7;
+
+	total_sizedwords += 2; /* scratchpad ts for recovery */
+	if (context) {
+		total_sizedwords += 3; /* sop timestamp */
+		total_sizedwords += 4; /* eop timestamp */
+		total_sizedwords += 3; /* global timestamp without cache
+					* flush for non-zero context */
+	} else {
+		total_sizedwords += 4; /* global timestamp for recovery*/
+	}
+
+	ringcmds = adreno_ringbuffer_allocspace(rb, total_sizedwords);
+	rcmd_gpu = rb->buffer_desc.gpuaddr
+		+ sizeof(uint)*(rb->wptr-total_sizedwords);
+
+	if (!(flags & KGSL_CMD_FLAGS_NOT_KERNEL_CMD)) {
+		GSL_RB_WRITE(ringcmds, rcmd_gpu, cp_nop_packet(1));
+		GSL_RB_WRITE(ringcmds, rcmd_gpu, KGSL_CMD_IDENTIFIER);
+	}
+	if (flags & KGSL_CMD_FLAGS_PMODE) {
+		/* disable protected mode error checking */
+		GSL_RB_WRITE(ringcmds, rcmd_gpu,
+			cp_type3_packet(CP_SET_PROTECTED_MODE, 1));
+		GSL_RB_WRITE(ringcmds, rcmd_gpu, 0);
+	}
+
+	for (i = 0; i < sizedwords; i++) {
+		GSL_RB_WRITE(ringcmds, rcmd_gpu, *cmds);
+		cmds++;
+	}
+
+	if (flags & KGSL_CMD_FLAGS_PMODE) {
+		/* re-enable protected mode error checking */
+		GSL_RB_WRITE(ringcmds, rcmd_gpu,
+			cp_type3_packet(CP_SET_PROTECTED_MODE, 1));
+		GSL_RB_WRITE(ringcmds, rcmd_gpu, 1);
+	}
+
+	/* always increment the global timestamp. once. */
+	rb->timestamp[KGSL_MEMSTORE_GLOBAL]++;
+	if (context) {
+		if (context_id == KGSL_MEMSTORE_GLOBAL)
+			rb->timestamp[context_id] =
+				rb->timestamp[KGSL_MEMSTORE_GLOBAL];
+		else
+			rb->timestamp[context_id]++;
+	}
+	timestamp = rb->timestamp[context_id];
+
+	/* scratchpad ts for recovery */
+	GSL_RB_WRITE(ringcmds, rcmd_gpu, cp_type0_packet(REG_CP_TIMESTAMP, 1));
+	GSL_RB_WRITE(ringcmds, rcmd_gpu, rb->timestamp[KGSL_MEMSTORE_GLOBAL]);
+
+	if (adreno_is_a3xx(adreno_dev)) {
+		/*
+		 * FLush HLSQ lazy updates to make sure there are no
+		 * rsources pending for indirect loads after the timestamp
+		 */
+
+		GSL_RB_WRITE(ringcmds, rcmd_gpu,
+			cp_type3_packet(CP_EVENT_WRITE, 1));
+		GSL_RB_WRITE(ringcmds, rcmd_gpu, 0x07); /* HLSQ_FLUSH */
+		GSL_RB_WRITE(ringcmds, rcmd_gpu,
+			cp_type3_packet(CP_WAIT_FOR_IDLE, 1));
+		GSL_RB_WRITE(ringcmds, rcmd_gpu, 0x00);
+	}
+
+	if (context) {
+		/* start-of-pipeline timestamp */
+		GSL_RB_WRITE(ringcmds, rcmd_gpu,
+			cp_type3_packet(CP_MEM_WRITE, 2));
+		GSL_RB_WRITE(ringcmds, rcmd_gpu, (gpuaddr +
+			KGSL_MEMSTORE_OFFSET(context->id, soptimestamp)));
+		GSL_RB_WRITE(ringcmds, rcmd_gpu, timestamp);
+
+		/* end-of-pipeline timestamp */
+		GSL_RB_WRITE(ringcmds, rcmd_gpu,
+			cp_type3_packet(CP_EVENT_WRITE, 3));
+		GSL_RB_WRITE(ringcmds, rcmd_gpu, CACHE_FLUSH_TS);
+		GSL_RB_WRITE(ringcmds, rcmd_gpu, (gpuaddr +
+			KGSL_MEMSTORE_OFFSET(context->id, eoptimestamp)));
+		GSL_RB_WRITE(ringcmds, rcmd_gpu, timestamp);
+
+		GSL_RB_WRITE(ringcmds, rcmd_gpu,
+			cp_type3_packet(CP_MEM_WRITE, 2));
+		GSL_RB_WRITE(ringcmds, rcmd_gpu, (gpuaddr +
+			      KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
+				      eoptimestamp)));
+		GSL_RB_WRITE(ringcmds, rcmd_gpu,
+			rb->timestamp[KGSL_MEMSTORE_GLOBAL]);
+	} else {
+		GSL_RB_WRITE(ringcmds, rcmd_gpu,
+			cp_type3_packet(CP_EVENT_WRITE, 3));
+		GSL_RB_WRITE(ringcmds, rcmd_gpu, CACHE_FLUSH_TS);
+		GSL_RB_WRITE(ringcmds, rcmd_gpu, (gpuaddr +
+			      KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
+				      eoptimestamp)));
+		GSL_RB_WRITE(ringcmds, rcmd_gpu,
+			rb->timestamp[KGSL_MEMSTORE_GLOBAL]);
+	}
+
+	if (!(flags & KGSL_CMD_FLAGS_NO_TS_CMP)) {
+		/* Conditional execution based on memory values */
+		GSL_RB_WRITE(ringcmds, rcmd_gpu,
+			cp_type3_packet(CP_COND_EXEC, 4));
+		GSL_RB_WRITE(ringcmds, rcmd_gpu, (gpuaddr +
+			KGSL_MEMSTORE_OFFSET(
+				context_id, ts_cmp_enable)) >> 2);
+		GSL_RB_WRITE(ringcmds, rcmd_gpu, (gpuaddr +
+			KGSL_MEMSTORE_OFFSET(
+				context_id, ref_wait_ts)) >> 2);
+		GSL_RB_WRITE(ringcmds, rcmd_gpu, timestamp);
+		/* # of conditional command DWORDs */
+		GSL_RB_WRITE(ringcmds, rcmd_gpu, 2);
+		GSL_RB_WRITE(ringcmds, rcmd_gpu,
+			cp_type3_packet(CP_INTERRUPT, 1));
+		GSL_RB_WRITE(ringcmds, rcmd_gpu, CP_INT_CNTL__RB_INT_MASK);
+	}
+
+	if (adreno_is_a3xx(adreno_dev)) {
+		/* Dummy set-constant to trigger context rollover */
+		GSL_RB_WRITE(ringcmds, rcmd_gpu,
+			cp_type3_packet(CP_SET_CONSTANT, 2));
+		GSL_RB_WRITE(ringcmds, rcmd_gpu,
+			(0x4<<16)|(A3XX_HLSQ_CL_KERNEL_GROUP_X_REG - 0x2000));
+		GSL_RB_WRITE(ringcmds, rcmd_gpu, 0);
+	}
+
+	adreno_ringbuffer_submit(rb);
+
+	return timestamp;
+}
+
+void
+adreno_ringbuffer_issuecmds(struct kgsl_device *device,
+						unsigned int flags,
+						unsigned int *cmds,
+						int sizedwords)
+{
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
+
+	if (device->state & KGSL_STATE_HUNG)
+		return;
+	adreno_ringbuffer_addcmds(rb, NULL, flags, cmds, sizedwords);
+}
+
+static bool _parse_ibs(struct kgsl_device_private *dev_priv, uint gpuaddr,
+			   int sizedwords);
+
+static bool
+_handle_type3(struct kgsl_device_private *dev_priv, uint *hostaddr)
+{
+	unsigned int opcode = cp_type3_opcode(*hostaddr);
+	switch (opcode) {
+	case CP_INDIRECT_BUFFER_PFD:
+	case CP_INDIRECT_BUFFER_PFE:
+	case CP_COND_INDIRECT_BUFFER_PFE:
+	case CP_COND_INDIRECT_BUFFER_PFD:
+		return _parse_ibs(dev_priv, hostaddr[1], hostaddr[2]);
+	case CP_NOP:
+	case CP_WAIT_FOR_IDLE:
+	case CP_WAIT_REG_MEM:
+	case CP_WAIT_REG_EQ:
+	case CP_WAT_REG_GTE:
+	case CP_WAIT_UNTIL_READ:
+	case CP_WAIT_IB_PFD_COMPLETE:
+	case CP_REG_RMW:
+	case CP_REG_TO_MEM:
+	case CP_MEM_WRITE:
+	case CP_MEM_WRITE_CNTR:
+	case CP_COND_EXEC:
+	case CP_COND_WRITE:
+	case CP_EVENT_WRITE:
+	case CP_EVENT_WRITE_SHD:
+	case CP_EVENT_WRITE_CFL:
+	case CP_EVENT_WRITE_ZPD:
+	case CP_DRAW_INDX:
+	case CP_DRAW_INDX_2:
+	case CP_DRAW_INDX_BIN:
+	case CP_DRAW_INDX_2_BIN:
+	case CP_VIZ_QUERY:
+	case CP_SET_STATE:
+	case CP_SET_CONSTANT:
+	case CP_IM_LOAD:
+	case CP_IM_LOAD_IMMEDIATE:
+	case CP_LOAD_CONSTANT_CONTEXT:
+	case CP_INVALIDATE_STATE:
+	case CP_SET_SHADER_BASES:
+	case CP_SET_BIN_MASK:
+	case CP_SET_BIN_SELECT:
+	case CP_SET_BIN_BASE_OFFSET:
+	case CP_SET_BIN_DATA:
+	case CP_CONTEXT_UPDATE:
+	case CP_INTERRUPT:
+	case CP_IM_STORE:
+	case CP_LOAD_STATE:
+		break;
+	/* these shouldn't come from userspace */
+	case CP_ME_INIT:
+	case CP_SET_PROTECTED_MODE:
+	default:
+		KGSL_CMD_ERR(dev_priv->device, "bad CP opcode %0x\n", opcode);
+		return false;
+		break;
+	}
+
+	return true;
+}
+
+static bool
+_handle_type0(struct kgsl_device_private *dev_priv, uint *hostaddr)
+{
+	unsigned int reg = type0_pkt_offset(*hostaddr);
+	unsigned int cnt = type0_pkt_size(*hostaddr);
+	if (reg < 0x0192 || (reg + cnt) >= 0x8000) {
+		KGSL_CMD_ERR(dev_priv->device, "bad type0 reg: 0x%0x cnt: %d\n",
+			     reg, cnt);
+		return false;
+	}
+	return true;
+}
+
+/*
+ * Traverse IBs and dump them to test vector. Detect swap by inspecting
+ * register writes, keeping note of the current state, and dump
+ * framebuffer config to test vector
+ */
+static bool _parse_ibs(struct kgsl_device_private *dev_priv,
+			   uint gpuaddr, int sizedwords)
+{
+	static uint level; /* recursion level */
+	bool ret = false;
+	uint *hostaddr, *hoststart;
+	int dwords_left = sizedwords; /* dwords left in the current command
+					 buffer */
+	struct kgsl_mem_entry *entry;
+
+	spin_lock(&dev_priv->process_priv->mem_lock);
+	entry = kgsl_sharedmem_find_region(dev_priv->process_priv,
+					   gpuaddr, sizedwords * sizeof(uint));
+	spin_unlock(&dev_priv->process_priv->mem_lock);
+	if (entry == NULL) {
+		KGSL_CMD_ERR(dev_priv->device,
+			     "no mapping for gpuaddr: 0x%08x\n", gpuaddr);
+		return false;
+	}
+
+	hostaddr = (uint *)kgsl_gpuaddr_to_vaddr(&entry->memdesc, gpuaddr);
+	if (hostaddr == NULL) {
+		KGSL_CMD_ERR(dev_priv->device,
+			     "no mapping for gpuaddr: 0x%08x\n", gpuaddr);
+		return false;
+	}
+
+	hoststart = hostaddr;
+
+	level++;
+
+	KGSL_CMD_INFO(dev_priv->device, "ib: gpuaddr:0x%08x, wc:%d, hptr:%p\n",
+		gpuaddr, sizedwords, hostaddr);
+
+	mb();
+	while (dwords_left > 0) {
+		bool cur_ret = true;
+		int count = 0; /* dword count including packet header */
+
+		switch (*hostaddr >> 30) {
+		case 0x0: /* type-0 */
+			count = (*hostaddr >> 16)+2;
+			cur_ret = _handle_type0(dev_priv, hostaddr);
+			break;
+		case 0x1: /* type-1 */
+			count = 2;
+			break;
+		case 0x3: /* type-3 */
+			count = ((*hostaddr >> 16) & 0x3fff) + 2;
+			cur_ret = _handle_type3(dev_priv, hostaddr);
+			break;
+		default:
+			KGSL_CMD_ERR(dev_priv->device, "unexpected type: "
+				"type:%d, word:0x%08x @ 0x%p, gpu:0x%08x\n",
+				*hostaddr >> 30, *hostaddr, hostaddr,
+				gpuaddr+4*(sizedwords-dwords_left));
+			cur_ret = false;
+			count = dwords_left;
+			break;
+		}
+
+		if (!cur_ret) {
+			KGSL_CMD_ERR(dev_priv->device,
+				"bad sub-type: #:%d/%d, v:0x%08x"
+				" @ 0x%p[gb:0x%08x], level:%d\n",
+				sizedwords-dwords_left, sizedwords, *hostaddr,
+				hostaddr, gpuaddr+4*(sizedwords-dwords_left),
+				level);
+
+			if (ADRENO_DEVICE(dev_priv->device)->ib_check_level
+				>= 2)
+				print_hex_dump(KERN_ERR,
+					level == 1 ? "IB1:" : "IB2:",
+					DUMP_PREFIX_OFFSET, 32, 4, hoststart,
+					sizedwords*4, 0);
+			goto done;
+		}
+
+		/* jump to next packet */
+		dwords_left -= count;
+		hostaddr += count;
+		if (dwords_left < 0) {
+			KGSL_CMD_ERR(dev_priv->device,
+				"bad count: c:%d, #:%d/%d, "
+				"v:0x%08x @ 0x%p[gb:0x%08x], level:%d\n",
+				count, sizedwords-(dwords_left+count),
+				sizedwords, *(hostaddr-count), hostaddr-count,
+				gpuaddr+4*(sizedwords-(dwords_left+count)),
+				level);
+			if (ADRENO_DEVICE(dev_priv->device)->ib_check_level
+				>= 2)
+				print_hex_dump(KERN_ERR,
+					level == 1 ? "IB1:" : "IB2:",
+					DUMP_PREFIX_OFFSET, 32, 4, hoststart,
+					sizedwords*4, 0);
+			goto done;
+		}
+	}
+
+	ret = true;
+done:
+	if (!ret)
+		KGSL_DRV_ERR(dev_priv->device,
+			"parsing failed: gpuaddr:0x%08x, "
+			"host:0x%p, wc:%d\n", gpuaddr, hoststart, sizedwords);
+
+	level--;
+
+	return ret;
+}
+
+int
+adreno_ringbuffer_issueibcmds(struct kgsl_device_private *dev_priv,
+				struct kgsl_context *context,
+				struct kgsl_ibdesc *ibdesc,
+				unsigned int numibs,
+				uint32_t *timestamp,
+				unsigned int flags)
+{
+	struct kgsl_device *device = dev_priv->device;
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	unsigned int *link;
+	unsigned int *cmds;
+	unsigned int i;
+	struct adreno_context *drawctxt;
+	unsigned int start_index = 0;
+
+	if (device->state & KGSL_STATE_HUNG)
+		return -EBUSY;
+	if (!(adreno_dev->ringbuffer.flags & KGSL_FLAGS_STARTED) ||
+	      context == NULL || ibdesc == 0 || numibs == 0)
+		return -EINVAL;
+
+	drawctxt = context->devctxt;
+
+	if (drawctxt->flags & CTXT_FLAGS_GPU_HANG) {
+		KGSL_CTXT_WARN(device, "Context %p caused a gpu hang.."
+			" will not accept commands for context %d\n",
+			drawctxt, drawctxt->id);
+		return -EDEADLK;
+	}
+
+	cmds = link = kzalloc(sizeof(unsigned int) * (numibs * 3 + 4),
+				GFP_KERNEL);
+	if (!link) {
+		KGSL_CORE_ERR("kzalloc(%d) failed\n",
+			sizeof(unsigned int) * (numibs * 3 + 4));
+		return -ENOMEM;
+	}
+
+	/*When preamble is enabled, the preamble buffer with state restoration
+	commands are stored in the first node of the IB chain. We can skip that
+	if a context switch hasn't occured */
+
+	if (drawctxt->flags & CTXT_FLAGS_PREAMBLE &&
+		adreno_dev->drawctxt_active == drawctxt)
+		start_index = 1;
+
+	if (!start_index) {
+		*cmds++ = cp_nop_packet(1);
+		*cmds++ = KGSL_START_OF_IB_IDENTIFIER;
+	} else {
+		*cmds++ = cp_nop_packet(4);
+		*cmds++ = KGSL_START_OF_IB_IDENTIFIER;
+		*cmds++ = CP_HDR_INDIRECT_BUFFER_PFD;
+		*cmds++ = ibdesc[0].gpuaddr;
+		*cmds++ = ibdesc[0].sizedwords;
+	}
+	for (i = start_index; i < numibs; i++) {
+		if (unlikely(adreno_dev->ib_check_level >= 1 &&
+		    !_parse_ibs(dev_priv, ibdesc[i].gpuaddr,
+				ibdesc[i].sizedwords))) {
+			kfree(link);
+			return -EINVAL;
+		}
+		*cmds++ = CP_HDR_INDIRECT_BUFFER_PFD;
+		*cmds++ = ibdesc[i].gpuaddr;
+		*cmds++ = ibdesc[i].sizedwords;
+	}
+
+	*cmds++ = cp_nop_packet(1);
+	*cmds++ = KGSL_END_OF_IB_IDENTIFIER;
+
+	kgsl_setstate(&device->mmu,
+		      kgsl_mmu_pt_get_flags(device->mmu.hwpagetable,
+					device->id));
+
+	adreno_drawctxt_switch(adreno_dev, drawctxt, flags);
+
+	*timestamp = adreno_ringbuffer_addcmds(&adreno_dev->ringbuffer,
+					drawctxt,
+					KGSL_CMD_FLAGS_NOT_KERNEL_CMD,
+					&link[0], (cmds - link));
+
+	KGSL_CMD_INFO(device, "ctxt %d g %08x numibs %d ts %d\n",
+		context->id, (unsigned int)ibdesc, numibs, *timestamp);
+
+	kfree(link);
+
+#ifdef CONFIG_MSM_KGSL_CFF_DUMP
+	/*
+	 * insert wait for idle after every IB1
+	 * this is conservative but works reliably and is ok
+	 * even for performance simulations
+	 */
+	adreno_idle(device, KGSL_TIMEOUT_DEFAULT);
+#endif
+
+	return 0;
+}
+
+int adreno_ringbuffer_extract(struct adreno_ringbuffer *rb,
+				unsigned int *temp_rb_buffer,
+				int *rb_size)
+{
+	struct kgsl_device *device = rb->device;
+	unsigned int rb_rptr;
+	unsigned int retired_timestamp;
+	unsigned int temp_idx = 0;
+	unsigned int value;
+	unsigned int val1;
+	unsigned int val2;
+	unsigned int val3;
+	unsigned int copy_rb_contents = 0;
+	struct kgsl_context *context;
+	unsigned int context_id;
+
+	GSL_RB_GET_READPTR(rb, &rb->rptr);
+
+	/* current_context is the context that is presently active in the
+	 * GPU, i.e the context in which the hang is caused */
+	kgsl_sharedmem_readl(&device->memstore, &context_id,
+		KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
+		current_context));
+	KGSL_DRV_ERR(device, "Last context id: %d\n", context_id);
+	context = idr_find(&device->context_idr, context_id);
+	if (context == NULL) {
+		KGSL_DRV_ERR(device,
+			"GPU recovery from hang not possible because last"
+			" context id is invalid.\n");
+		return -EINVAL;
+	}
+	retired_timestamp = kgsl_readtimestamp(device, context,
+					       KGSL_TIMESTAMP_RETIRED);
+	KGSL_DRV_ERR(device, "GPU successfully executed till ts: %x\n",
+			retired_timestamp);
+	/*
+	 * We need to go back in history by 4 dwords from the current location
+	 * of read pointer as 4 dwords are read to match the end of a command.
+	 * Also, take care of wrap around when moving back
+	 */
+	if (rb->rptr >= 4)
+		rb_rptr = (rb->rptr - 4) * sizeof(unsigned int);
+	else
+		rb_rptr = rb->buffer_desc.size -
+			((4 - rb->rptr) * sizeof(unsigned int));
+	/* Read the rb contents going backwards to locate end of last
+	 * sucessfully executed command */
+	while ((rb_rptr / sizeof(unsigned int)) != rb->wptr) {
+		kgsl_sharedmem_readl(&rb->buffer_desc, &value, rb_rptr);
+		if (value == retired_timestamp) {
+			rb_rptr = adreno_ringbuffer_inc_wrapped(rb_rptr,
+							rb->buffer_desc.size);
+			kgsl_sharedmem_readl(&rb->buffer_desc, &val1, rb_rptr);
+			rb_rptr = adreno_ringbuffer_inc_wrapped(rb_rptr,
+							rb->buffer_desc.size);
+			kgsl_sharedmem_readl(&rb->buffer_desc, &val2, rb_rptr);
+			rb_rptr = adreno_ringbuffer_inc_wrapped(rb_rptr,
+							rb->buffer_desc.size);
+			kgsl_sharedmem_readl(&rb->buffer_desc, &val3, rb_rptr);
+			/* match the pattern found at the end of a command */
+			if ((val1 == 2 &&
+				val2 == cp_type3_packet(CP_INTERRUPT, 1)
+				&& val3 == CP_INT_CNTL__RB_INT_MASK) ||
+				(val1 == cp_type3_packet(CP_EVENT_WRITE, 3)
+				&& val2 == CACHE_FLUSH_TS &&
+				val3 == (rb->device->memstore.gpuaddr +
+				KGSL_MEMSTORE_OFFSET(context_id,
+					eoptimestamp)))) {
+				rb_rptr = adreno_ringbuffer_inc_wrapped(rb_rptr,
+							rb->buffer_desc.size);
+				KGSL_DRV_ERR(device,
+					"Found end of last executed "
+					"command at offset: %x\n",
+					rb_rptr / sizeof(unsigned int));
+				break;
+			} else {
+				if (rb_rptr < (3 * sizeof(unsigned int)))
+					rb_rptr = rb->buffer_desc.size -
+						(3 * sizeof(unsigned int))
+							+ rb_rptr;
+				else
+					rb_rptr -= (3 * sizeof(unsigned int));
+			}
+		}
+
+		if (rb_rptr == 0)
+			rb_rptr = rb->buffer_desc.size - sizeof(unsigned int);
+		else
+			rb_rptr -= sizeof(unsigned int);
+	}
+
+	if ((rb_rptr / sizeof(unsigned int)) == rb->wptr) {
+		KGSL_DRV_ERR(device,
+			"GPU recovery from hang not possible because last"
+			" successful timestamp is overwritten\n");
+		return -EINVAL;
+	}
+	/* rb_rptr is now pointing to the first dword of the command following
+	 * the last sucessfully executed command sequence. Assumption is that
+	 * GPU is hung in the command sequence pointed by rb_rptr */
+	/* make sure the GPU is not hung in a command submitted by kgsl
+	 * itself */
+	kgsl_sharedmem_readl(&rb->buffer_desc, &val1, rb_rptr);
+	kgsl_sharedmem_readl(&rb->buffer_desc, &val2,
+				adreno_ringbuffer_inc_wrapped(rb_rptr,
+							rb->buffer_desc.size));
+	if (val1 == cp_nop_packet(1) && val2 == KGSL_CMD_IDENTIFIER) {
+		KGSL_DRV_ERR(device,
+			"GPU recovery from hang not possible because "
+			"of hang in kgsl command\n");
+		return -EINVAL;
+	}
+
+	while ((rb_rptr / sizeof(unsigned int)) != rb->wptr) {
+		kgsl_sharedmem_readl(&rb->buffer_desc, &value, rb_rptr);
+		rb_rptr = adreno_ringbuffer_inc_wrapped(rb_rptr,
+						rb->buffer_desc.size);
+		/* check for context switch indicator */
+		if (value == KGSL_CONTEXT_TO_MEM_IDENTIFIER) {
+			kgsl_sharedmem_readl(&rb->buffer_desc, &value, rb_rptr);
+			rb_rptr = adreno_ringbuffer_inc_wrapped(rb_rptr,
+							rb->buffer_desc.size);
+			BUG_ON(value != cp_type3_packet(CP_MEM_WRITE, 2));
+			kgsl_sharedmem_readl(&rb->buffer_desc, &val1, rb_rptr);
+			rb_rptr = adreno_ringbuffer_inc_wrapped(rb_rptr,
+							rb->buffer_desc.size);
+			BUG_ON(val1 != (device->memstore.gpuaddr +
+				KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
+					current_context)));
+			kgsl_sharedmem_readl(&rb->buffer_desc, &value, rb_rptr);
+			rb_rptr = adreno_ringbuffer_inc_wrapped(rb_rptr,
+							rb->buffer_desc.size);
+
+			/*
+			 * If other context switches were already lost and
+			 * and the current context is the one that is hanging,
+			 * then we cannot recover.  Print an error message
+			 * and leave.
+			 */
+
+			if ((copy_rb_contents == 0) && (value == context_id)) {
+				KGSL_DRV_ERR(device, "GPU recovery could not "
+					"find the previous context\n");
+				return -EINVAL;
+			}
+
+			/*
+			 * If we were copying the commands and got to this point
+			 * then we need to remove the 3 commands that appear
+			 * before KGSL_CONTEXT_TO_MEM_IDENTIFIER
+			 */
+			if (temp_idx)
+				temp_idx -= 3;
+			/* if context switches to a context that did not cause
+			 * hang then start saving the rb contents as those
+			 * commands can be executed */
+			if (value != context_id) {
+				copy_rb_contents = 1;
+				temp_rb_buffer[temp_idx++] = cp_nop_packet(1);
+				temp_rb_buffer[temp_idx++] =
+						KGSL_CMD_IDENTIFIER;
+				temp_rb_buffer[temp_idx++] = cp_nop_packet(1);
+				temp_rb_buffer[temp_idx++] =
+						KGSL_CONTEXT_TO_MEM_IDENTIFIER;
+				temp_rb_buffer[temp_idx++] =
+					cp_type3_packet(CP_MEM_WRITE, 2);
+				temp_rb_buffer[temp_idx++] = val1;
+				temp_rb_buffer[temp_idx++] = value;
+			} else {
+				copy_rb_contents = 0;
+			}
+		} else if (copy_rb_contents)
+			temp_rb_buffer[temp_idx++] = value;
+	}
+
+	*rb_size = temp_idx;
+	return 0;
+}
+
+void
+adreno_ringbuffer_restore(struct adreno_ringbuffer *rb, unsigned int *rb_buff,
+			int num_rb_contents)
+{
+	int i;
+	unsigned int *ringcmds;
+	unsigned int rcmd_gpu;
+
+	if (!num_rb_contents)
+		return;
+
+	if (num_rb_contents > (rb->buffer_desc.size - rb->wptr)) {
+		adreno_regwrite(rb->device, REG_CP_RB_RPTR, 0);
+		rb->rptr = 0;
+		BUG_ON(num_rb_contents > rb->buffer_desc.size);
+	}
+	ringcmds = (unsigned int *)rb->buffer_desc.hostptr + rb->wptr;
+	rcmd_gpu = rb->buffer_desc.gpuaddr + sizeof(unsigned int) * rb->wptr;
+	for (i = 0; i < num_rb_contents; i++)
+		GSL_RB_WRITE(ringcmds, rcmd_gpu, rb_buff[i]);
+	rb->wptr += num_rb_contents;
+	adreno_ringbuffer_submit(rb);
+}
diff --git a/drivers/gpu/msm/adreno_ringbuffer.h b/drivers/gpu/msm/adreno_ringbuffer.h
new file mode 100644
index 0000000..ae2e4c7
--- /dev/null
+++ b/drivers/gpu/msm/adreno_ringbuffer.h
@@ -0,0 +1,141 @@
+/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __ADRENO_RINGBUFFER_H
+#define __ADRENO_RINGBUFFER_H
+
+/*
+ * Adreno ringbuffer sizes in bytes - these are converted to
+ * the appropriate log2 values in the code
+ */
+
+#define KGSL_RB_SIZE (32 * 1024)
+#define KGSL_RB_BLKSIZE 16
+
+/* CP timestamp register */
+#define	REG_CP_TIMESTAMP		 REG_SCRATCH_REG0
+
+
+struct kgsl_device;
+struct kgsl_device_private;
+
+#define GSL_RB_MEMPTRS_SCRATCH_COUNT	 8
+struct kgsl_rbmemptrs {
+	int  rptr;
+	int  wptr_poll;
+};
+
+#define GSL_RB_MEMPTRS_RPTR_OFFSET \
+	(offsetof(struct kgsl_rbmemptrs, rptr))
+
+#define GSL_RB_MEMPTRS_WPTRPOLL_OFFSET \
+	(offsetof(struct kgsl_rbmemptrs, wptr_poll))
+
+struct adreno_ringbuffer {
+	struct kgsl_device *device;
+	uint32_t flags;
+
+	struct kgsl_memdesc buffer_desc;
+
+	struct kgsl_memdesc memptrs_desc;
+	struct kgsl_rbmemptrs *memptrs;
+
+	/*ringbuffer size */
+	unsigned int sizedwords;
+
+	unsigned int wptr; /* write pointer offset in dwords from baseaddr */
+	unsigned int rptr; /* read pointer offset in dwords from baseaddr */
+
+	unsigned int timestamp[KGSL_MEMSTORE_MAX];
+};
+
+
+#define GSL_RB_WRITE(ring, gpuaddr, data) \
+	do { \
+		*ring = data; \
+		wmb(); \
+		kgsl_cffdump_setmem(gpuaddr, data, 4); \
+		ring++; \
+		gpuaddr += sizeof(uint); \
+	} while (0)
+
+/* enable timestamp (...scratch0) memory shadowing */
+#define GSL_RB_MEMPTRS_SCRATCH_MASK 0x1
+
+/* mem rptr */
+#define GSL_RB_CNTL_NO_UPDATE 0x0 /* enable */
+#define GSL_RB_GET_READPTR(rb, data) \
+	do { \
+		*(data) = rb->memptrs->rptr; \
+	} while (0)
+
+#define GSL_RB_CNTL_POLL_EN 0x0 /* disable */
+
+/*
+ * protected mode error checking below register address 0x800
+ * note: if CP_INTERRUPT packet is used then checking needs
+ * to change to below register address 0x7C8
+ */
+#define GSL_RB_PROTECTED_MODE_CONTROL		0x200001F2
+
+int adreno_ringbuffer_issueibcmds(struct kgsl_device_private *dev_priv,
+				struct kgsl_context *context,
+				struct kgsl_ibdesc *ibdesc,
+				unsigned int numibs,
+				uint32_t *timestamp,
+				unsigned int flags);
+
+int adreno_ringbuffer_init(struct kgsl_device *device);
+
+int adreno_ringbuffer_start(struct adreno_ringbuffer *rb,
+				unsigned int init_ram);
+
+void adreno_ringbuffer_stop(struct adreno_ringbuffer *rb);
+
+void adreno_ringbuffer_close(struct adreno_ringbuffer *rb);
+
+void adreno_ringbuffer_issuecmds(struct kgsl_device *device,
+					unsigned int flags,
+					unsigned int *cmdaddr,
+					int sizedwords);
+
+void adreno_ringbuffer_submit(struct adreno_ringbuffer *rb);
+
+void kgsl_cp_intrcallback(struct kgsl_device *device);
+
+int adreno_ringbuffer_extract(struct adreno_ringbuffer *rb,
+				unsigned int *temp_rb_buffer,
+				int *rb_size);
+
+void
+adreno_ringbuffer_restore(struct adreno_ringbuffer *rb, unsigned int *rb_buff,
+			int num_rb_contents);
+
+unsigned int *adreno_ringbuffer_allocspace(struct adreno_ringbuffer *rb,
+					     unsigned int numcmds);
+
+static inline int adreno_ringbuffer_count(struct adreno_ringbuffer *rb,
+	unsigned int rptr)
+{
+	if (rb->wptr >= rptr)
+		return rb->wptr - rptr;
+	return rb->wptr + rb->sizedwords - rptr;
+}
+
+/* Increment a value by 4 bytes with wrap-around based on size */
+static inline unsigned int adreno_ringbuffer_inc_wrapped(unsigned int val,
+							unsigned int size)
+{
+	return (val + sizeof(unsigned int)) % size;
+}
+
+#endif  /* __ADRENO_RINGBUFFER_H */
diff --git a/drivers/gpu/msm/adreno_snapshot.c b/drivers/gpu/msm/adreno_snapshot.c
new file mode 100644
index 0000000..a0907d7
--- /dev/null
+++ b/drivers/gpu/msm/adreno_snapshot.c
@@ -0,0 +1,870 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "kgsl.h"
+#include "kgsl_sharedmem.h"
+#include "kgsl_snapshot.h"
+
+#include "adreno.h"
+#include "adreno_pm4types.h"
+#include "a2xx_reg.h"
+#include "a3xx_reg.h"
+
+/* Number of dwords of ringbuffer history to record */
+#define NUM_DWORDS_OF_RINGBUFFER_HISTORY 100
+
+/* Maintain a list of the objects we see during parsing */
+
+#define SNAPSHOT_OBJ_BUFSIZE 64
+
+#define SNAPSHOT_OBJ_TYPE_IB 0
+
+/* Keep track of how many bytes are frozen after a snapshot and tell the user */
+static int snapshot_frozen_objsize;
+
+static struct kgsl_snapshot_obj {
+	int type;
+	uint32_t gpuaddr;
+	uint32_t ptbase;
+	void *ptr;
+	int dwords;
+} objbuf[SNAPSHOT_OBJ_BUFSIZE];
+
+/* Pointer to the next open entry in the object list */
+static int objbufptr;
+
+/* Push a new buffer object onto the list */
+static void push_object(struct kgsl_device *device, int type, uint32_t ptbase,
+	uint32_t gpuaddr, int dwords)
+{
+	int index;
+	void *ptr;
+
+	/*
+	 * Sometimes IBs can be reused in the same dump.  Because we parse from
+	 * oldest to newest, if we come across an IB that has already been used,
+	 * assume that it has been reused and update the list with the newest
+	 * size.
+	 */
+
+	for (index = 0; index < objbufptr; index++) {
+		if (objbuf[index].gpuaddr == gpuaddr &&
+			objbuf[index].ptbase == ptbase) {
+				objbuf[index].dwords = dwords;
+				return;
+			}
+	}
+
+	if (objbufptr == SNAPSHOT_OBJ_BUFSIZE) {
+		KGSL_DRV_ERR(device, "snapshot: too many snapshot objects\n");
+		return;
+	}
+
+	/*
+	 * adreno_convertaddr verifies that the IB size is valid - at least in
+	 * the context of it being smaller then the allocated memory space
+	 */
+	ptr = adreno_convertaddr(device, ptbase, gpuaddr, dwords << 2);
+
+	if (ptr == NULL) {
+		KGSL_DRV_ERR(device,
+			"snapshot: Can't find GPU address for %x\n", gpuaddr);
+		return;
+	}
+
+	/* Put it on the list of things to parse */
+	objbuf[objbufptr].type = type;
+	objbuf[objbufptr].gpuaddr = gpuaddr;
+	objbuf[objbufptr].ptbase = ptbase;
+	objbuf[objbufptr].dwords = dwords;
+	objbuf[objbufptr++].ptr = ptr;
+}
+
+/*
+ * Return a 1 if the specified object is already on the list of buffers
+ * to be dumped
+ */
+
+static int find_object(int type, unsigned int gpuaddr, unsigned int ptbase)
+{
+	int index;
+
+	for (index = 0; index < objbufptr; index++) {
+		if (objbuf[index].gpuaddr == gpuaddr &&
+			objbuf[index].ptbase == ptbase &&
+			objbuf[index].type == type)
+			return 1;
+	}
+
+	return 0;
+}
+
+/*
+ * This structure keeps track of type0 writes to VSC_PIPE_DATA_ADDRESS_x and
+ * VSC_PIPE_DATA_LENGTH_x. When a draw initator is called these registers
+ * point to buffers that we need to freeze for a snapshot
+ */
+
+static struct {
+	unsigned int base;
+	unsigned int size;
+} vsc_pipe[8];
+
+/*
+ * This is the cached value of type0 writes to the VSC_SIZE_ADDRESS which
+ * contains the buffer address of the visiblity stream size buffer during a
+ * binning pass
+ */
+
+static unsigned int vsc_size_address;
+
+/*
+ * This struct keeps track of type0 writes to VFD_FETCH_INSTR_0_X and
+ * VFD_FETCH_INSTR_1_X registers. When a draw initator is called the addresses
+ * and sizes in these registers point to VBOs that we need to freeze for a
+ * snapshot
+ */
+
+static struct {
+	unsigned int base;
+	unsigned int stride;
+} vbo[16];
+
+/*
+ * This is the cached value of type0 writes to VFD_INDEX_MAX.  This will be used
+ * to calculate the size of the VBOs when the draw initator is called
+ */
+
+static unsigned int vfd_index_max;
+
+/*
+ * This is the cached value of type0 writes to VFD_CONTROL_0 which tells us how
+ * many VBOs are active when the draw initator is called
+ */
+
+static unsigned int vfd_control_0;
+
+/*
+ * Cached value of type0 writes to SP_VS_PVT_MEM_ADDR and SP_FS_PVT_MEM_ADDR.
+ * This is a buffer that contains private stack information for the shader
+ */
+
+static unsigned int sp_vs_pvt_mem_addr;
+static unsigned int sp_fs_pvt_mem_addr;
+
+static void ib_parse_load_state(struct kgsl_device *device, unsigned int *pkt,
+	unsigned int ptbase)
+{
+	unsigned int block, source, type;
+
+	/*
+	 * The object here is to find indirect shaders i.e - shaders loaded from
+	 * GPU memory instead of directly in the command.  These should be added
+	 * to the list of memory objects to dump. So look at the load state
+	 * call and see if 1) the shader block is a shader (block = 4, 5 or 6)
+	 * 2) that the block is indirect (source = 4). If these all match then
+	 * add the memory address to the list.  The size of the object will
+	 * differ depending on the type.  Type 0 (instructions) are 8 dwords per
+	 * unit and type 1 (constants) are 2 dwords per unit.
+	 */
+
+	if (type3_pkt_size(pkt[0]) < 2)
+		return;
+
+	/*
+	 * pkt[1] 18:16 - source
+	 * pkt[1] 21:19 - state block
+	 * pkt[1] 31:22 - size in units
+	 * pkt[2] 0:1 - type
+	 * pkt[2] 31:2 - GPU memory address
+	 */
+
+	block = (pkt[1] >> 19) & 0x07;
+	source = (pkt[1] >> 16) & 0x07;
+	type = pkt[2] & 0x03;
+
+	if ((block == 4 || block == 5 || block == 6) && source == 4) {
+		int unitsize = (type == 0) ? 8 : 2;
+		int ret;
+
+		/* Freeze the GPU buffer containing the shader */
+
+		ret = kgsl_snapshot_get_object(device, ptbase,
+				pkt[2] & 0xFFFFFFFC,
+				(((pkt[1] >> 22) & 0x03FF) * unitsize) << 2,
+				SNAPSHOT_GPU_OBJECT_SHADER);
+		snapshot_frozen_objsize += ret;
+	}
+}
+
+/*
+ * This opcode sets the base addresses for the visibilty stream buffer and the
+ * visiblity stream size buffer.
+ */
+
+static void ib_parse_set_bin_data(struct kgsl_device *device, unsigned int *pkt,
+	unsigned int ptbase)
+{
+	int ret;
+
+	if (type3_pkt_size(pkt[0]) < 2)
+		return;
+
+	/* Visiblity stream buffer */
+	ret = kgsl_snapshot_get_object(device, ptbase, pkt[1], 0,
+			SNAPSHOT_GPU_OBJECT_GENERIC);
+	snapshot_frozen_objsize += ret;
+
+	/* visiblity stream size buffer (fixed size 8 dwords) */
+	ret = kgsl_snapshot_get_object(device, ptbase, pkt[2], 32,
+			SNAPSHOT_GPU_OBJECT_GENERIC);
+	snapshot_frozen_objsize += ret;
+}
+
+/*
+ * This opcode writes to GPU memory - if the buffer is written to, there is a
+ * good chance that it would be valuable to capture in the snapshot, so mark all
+ * buffers that are written to as frozen
+ */
+
+static void ib_parse_mem_write(struct kgsl_device *device, unsigned int *pkt,
+	unsigned int ptbase)
+{
+	int ret;
+
+	if (type3_pkt_size(pkt[0]) < 1)
+		return;
+
+	/*
+	 * The address is where the data in the rest of this packet is written
+	 * to, but since that might be an offset into the larger buffer we need
+	 * to get the whole thing. Pass a size of 0 kgsl_snapshot_get_object to
+	 * capture the entire buffer.
+	 */
+
+	ret = kgsl_snapshot_get_object(device, ptbase, pkt[1] & 0xFFFFFFFC, 0,
+		SNAPSHOT_GPU_OBJECT_GENERIC);
+
+	snapshot_frozen_objsize += ret;
+}
+
+/*
+ * The DRAW_INDX opcode sends a draw initator which starts a draw operation in
+ * the GPU, so this is the point where all the registers and buffers become
+ * "valid".  The DRAW_INDX may also have an index buffer pointer that should be
+ * frozen with the others
+ */
+
+static void ib_parse_draw_indx(struct kgsl_device *device, unsigned int *pkt,
+	unsigned int ptbase)
+{
+	int ret, i;
+
+	if (type3_pkt_size(pkt[0]) < 3)
+		return;
+
+	/*  DRAW_IDX may have a index buffer pointer */
+
+	if (type3_pkt_size(pkt[0]) > 3) {
+		ret = kgsl_snapshot_get_object(device, ptbase, pkt[4], pkt[5],
+			SNAPSHOT_GPU_OBJECT_GENERIC);
+		snapshot_frozen_objsize += ret;
+	}
+
+	/*
+	 * All of the type0 writes are valid at a draw initiator, so freeze
+	 * the various buffers that we are tracking
+	 */
+
+	/* First up the visiblity stream buffer */
+
+	for (i = 0; i < ARRAY_SIZE(vsc_pipe); i++) {
+		if (vsc_pipe[i].base != 0 && vsc_pipe[i].size != 0) {
+			ret = kgsl_snapshot_get_object(device, ptbase,
+				vsc_pipe[i].base, vsc_pipe[i].size,
+				SNAPSHOT_GPU_OBJECT_GENERIC);
+			snapshot_frozen_objsize += ret;
+		}
+	}
+
+	/* Next the visibility stream size buffer */
+
+	if (vsc_size_address) {
+		ret = kgsl_snapshot_get_object(device, ptbase,
+				vsc_size_address, 32,
+				SNAPSHOT_GPU_OBJECT_GENERIC);
+		snapshot_frozen_objsize += ret;
+	}
+
+	/* Next private shader buffer memory */
+	if (sp_vs_pvt_mem_addr) {
+		ret = kgsl_snapshot_get_object(device, ptbase,
+				sp_vs_pvt_mem_addr, 8192,
+				SNAPSHOT_GPU_OBJECT_GENERIC);
+
+		snapshot_frozen_objsize += ret;
+	}
+
+	if (sp_fs_pvt_mem_addr) {
+		ret = kgsl_snapshot_get_object(device, ptbase,
+				sp_fs_pvt_mem_addr, 8192,
+				SNAPSHOT_GPU_OBJECT_GENERIC);
+		snapshot_frozen_objsize += ret;
+	}
+
+	/* Finally: VBOs */
+
+	/* The number of active VBOs is stored in VFD_CONTROL_O[31:27] */
+	for (i = 0; i < (vfd_control_0) >> 27; i++) {
+		int size;
+
+		/*
+		 * The size of the VBO is the stride stored in
+		 * VFD_FETCH_INSTR_0_X.BUFSTRIDE * VFD_INDEX_MAX. The base
+		 * is stored in VFD_FETCH_INSTR_1_X
+		 */
+
+		if (vbo[i].base != 0) {
+			size = vbo[i].stride * vfd_index_max;
+
+			ret = kgsl_snapshot_get_object(device, ptbase,
+				vbo[i].base,
+				0, SNAPSHOT_GPU_OBJECT_GENERIC);
+			snapshot_frozen_objsize += ret;
+		}
+	}
+}
+
+/*
+ * Parse all the type3 opcode packets that may contain important information,
+ * such as additional GPU buffers to grab or a draw initator
+ */
+
+static void ib_parse_type3(struct kgsl_device *device, unsigned int *ptr,
+	unsigned int ptbase)
+{
+	switch (cp_type3_opcode(*ptr)) {
+	case CP_LOAD_STATE:
+		ib_parse_load_state(device, ptr, ptbase);
+		break;
+	case CP_SET_BIN_DATA:
+		ib_parse_set_bin_data(device, ptr, ptbase);
+		break;
+	case CP_MEM_WRITE:
+		ib_parse_mem_write(device, ptr, ptbase);
+		break;
+	case CP_DRAW_INDX:
+		ib_parse_draw_indx(device, ptr, ptbase);
+		break;
+	}
+}
+
+/*
+ * Parse type0 packets found in the stream.  Some of the registers that are
+ * written are clues for GPU buffers that we need to freeze.  Register writes
+ * are considred valid when a draw initator is called, so just cache the values
+ * here and freeze them when a CP_DRAW_INDX is seen.  This protects against
+ * needlessly caching buffers that won't be used during a draw call
+ */
+
+static void ib_parse_type0(struct kgsl_device *device, unsigned int *ptr,
+	unsigned int ptbase)
+{
+	int size = type0_pkt_size(*ptr);
+	int offset = type0_pkt_offset(*ptr);
+	int i;
+
+	for (i = 0; i < size; i++, offset++) {
+
+		/* Visiblity stream buffer */
+
+		if (offset >= A3XX_VSC_PIPE_DATA_ADDRESS_0 &&
+			offset <= A3XX_VSC_PIPE_DATA_LENGTH_7) {
+			int index = offset - A3XX_VSC_PIPE_DATA_ADDRESS_0;
+
+			/* Each bank of address and length registers are
+			 * interleaved with an empty register:
+			 *
+			 * address 0
+			 * length 0
+			 * empty
+			 * address 1
+			 * length 1
+			 * empty
+			 * ...
+			 */
+
+			if ((index % 3) == 0)
+				vsc_pipe[index / 3].base = ptr[i + 1];
+			else if ((index % 3) == 1)
+				vsc_pipe[index / 3].size = ptr[i + 1];
+		} else if ((offset >= A3XX_VFD_FETCH_INSTR_0_0) &&
+			(offset <= A3XX_VFD_FETCH_INSTR_1_F)) {
+			int index = offset - A3XX_VFD_FETCH_INSTR_0_0;
+
+			/*
+			 * FETCH_INSTR_0_X and FETCH_INSTR_1_X banks are
+			 * interleaved as above but without the empty register
+			 * in between
+			 */
+
+			if ((index % 2) == 0)
+				vbo[index >> 1].stride =
+					(ptr[i + 1] >> 7) & 0x1FF;
+			else
+				vbo[index >> 1].base = ptr[i + 1];
+		} else {
+			/*
+			 * Cache various support registers for calculating
+			 * buffer sizes
+			 */
+
+			switch (offset) {
+			case A3XX_VFD_CONTROL_0:
+				vfd_control_0 = ptr[i + 1];
+				break;
+			case A3XX_VFD_INDEX_MAX:
+				vfd_index_max = ptr[i + 1];
+				break;
+			case A3XX_VSC_SIZE_ADDRESS:
+				vsc_size_address = ptr[i + 1];
+				break;
+			case A3XX_SP_VS_PVT_MEM_ADDR_REG:
+				sp_vs_pvt_mem_addr = ptr[i + 1];
+				break;
+			case A3XX_SP_FS_PVT_MEM_ADDR_REG:
+				sp_fs_pvt_mem_addr = ptr[i + 1];
+				break;
+			}
+		}
+	}
+}
+
+/* Add an IB as a GPU object, but first, parse it to find more goodies within */
+
+static void ib_add_gpu_object(struct kgsl_device *device, unsigned int ptbase,
+		unsigned int gpuaddr, unsigned int dwords)
+{
+	int i, ret, rem = dwords;
+	unsigned int *src = (unsigned int *) adreno_convertaddr(device, ptbase,
+		gpuaddr, dwords << 2);
+
+	if (src == NULL)
+		return;
+
+	for (i = 0; rem != 0; rem--, i++) {
+		int pktsize;
+
+		if (!pkt_is_type0(src[i]) && !pkt_is_type3(src[i]))
+			continue;
+
+		pktsize = type3_pkt_size(src[i]);
+
+		if ((pktsize + 1) > rem)
+			break;
+
+		if (pkt_is_type3(src[i])) {
+			if (adreno_cmd_is_ib(src[i]))
+				ib_add_gpu_object(device, ptbase,
+					src[i + 1], src[i + 2]);
+			else
+				ib_parse_type3(device, &src[i], ptbase);
+		} else if (pkt_is_type0(src[i])) {
+			ib_parse_type0(device, &src[i], ptbase);
+		}
+
+		i += pktsize;
+		rem -= pktsize;
+	}
+
+	ret = kgsl_snapshot_get_object(device, ptbase, gpuaddr, dwords << 2,
+		SNAPSHOT_GPU_OBJECT_IB);
+
+	snapshot_frozen_objsize += ret;
+}
+
+/* Snapshot the istore memory */
+static int snapshot_istore(struct kgsl_device *device, void *snapshot,
+	int remain, void *priv)
+{
+	struct kgsl_snapshot_istore *header = snapshot;
+	unsigned int *data = snapshot + sizeof(*header);
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	int count, i;
+
+	count = adreno_dev->istore_size * adreno_dev->instruction_size;
+
+	if (remain < (count * 4) + sizeof(*header)) {
+		KGSL_DRV_ERR(device,
+			"snapshot: Not enough memory for the istore section");
+		return 0;
+	}
+
+	header->count = adreno_dev->istore_size;
+
+	for (i = 0; i < count; i++)
+		kgsl_regread(device, ADRENO_ISTORE_START + i, &data[i]);
+
+	return (count * 4) + sizeof(*header);
+}
+
+/* Snapshot the ringbuffer memory */
+static int snapshot_rb(struct kgsl_device *device, void *snapshot,
+	int remain, void *priv)
+{
+	struct kgsl_snapshot_rb *header = snapshot;
+	unsigned int *data = snapshot + sizeof(*header);
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
+	unsigned int ptbase, rptr, *rbptr, ibbase;
+	int index, size, i;
+	int parse_ibs = 0, ib_parse_start;
+	int skip_pktsize = 1;
+
+	/* Get the physical address of the MMU pagetable */
+	ptbase = kgsl_mmu_get_current_ptbase(&device->mmu);
+
+	/* Get the current read pointers for the RB */
+	kgsl_regread(device, REG_CP_RB_RPTR, &rptr);
+
+	/* Address of the last processed IB */
+	kgsl_regread(device, REG_CP_IB1_BASE, &ibbase);
+
+	/*
+	 * Figure out the window of ringbuffer data to dump.  First we need to
+	 * find where the last processed IB ws submitted
+	 */
+
+	index = rptr;
+	rbptr = rb->buffer_desc.hostptr;
+
+	while (index != rb->wptr) {
+		index--;
+
+		if (index < 0) {
+			index = rb->sizedwords - 3;
+
+			/* We wrapped without finding what we wanted */
+			if (index < rb->wptr) {
+				index = rb->wptr;
+				break;
+			}
+		}
+
+		if (adreno_cmd_is_ib(rbptr[index]) &&
+			rbptr[index + 1] == ibbase)
+			break;
+	}
+
+	/*
+	 * index points at the last submitted IB. We can only trust that the
+	 * memory between the context switch and the hanging IB is valid, so
+	 * the next step is to find the context switch before the submission
+	 */
+
+	while (index != rb->wptr) {
+		index--;
+
+		if (index < 0) {
+			index = rb->sizedwords - 2;
+
+			/*
+			 * Wrapped without finding the context switch. This is
+			 * harmless - we should still have enough data to dump a
+			 * valid state
+			 */
+
+			if (index < rb->wptr) {
+				index = rb->wptr;
+				break;
+			}
+		}
+
+		/* Break if the current packet is a context switch identifier */
+		if ((rbptr[index] == cp_nop_packet(1)) &&
+			(rbptr[index + 1] == KGSL_CONTEXT_TO_MEM_IDENTIFIER))
+			break;
+	}
+
+	/*
+	 * Index represents the start of the window of interest.  We will try
+	 * to dump all buffers between here and the rptr
+	 */
+
+	ib_parse_start = index;
+
+	/*
+	 * Dump the entire ringbuffer - the parser can choose how much of it to
+	 * process
+	 */
+
+	size = (rb->sizedwords << 2);
+
+	if (remain < size + sizeof(*header)) {
+		KGSL_DRV_ERR(device,
+			"snapshot: Not enough memory for the rb section");
+		return 0;
+	}
+
+	/* Write the sub-header for the section */
+	header->start = rb->wptr;
+	header->end = rb->wptr;
+	header->wptr = rb->wptr;
+	header->rbsize = rb->sizedwords;
+	header->count = rb->sizedwords;
+
+	/*
+	 * Loop through the RB, copying the data and looking for indirect
+	 * buffers and MMU pagetable changes
+	 */
+
+	index = rb->wptr;
+	for (i = 0; i < rb->sizedwords; i++) {
+		*data = rbptr[index];
+
+		/*
+		 * Sometimes the rptr is located in the middle of a packet.
+		 * try to adust for that by modifying the rptr to match a
+		 * packet boundary. Unfortunately for us, it is hard to tell
+		 * which dwords are legitimate type0 header and which are just
+		 * random data so just walk over type0 packets until we get
+		 * to the first type3, and from that point on start checking the
+		 * size of the packet and adjusting accordingly
+		 */
+
+		if (skip_pktsize && pkt_is_type3(rbptr[index]))
+			skip_pktsize = 0;
+
+		if (skip_pktsize == 0) {
+			unsigned int pktsize = type3_pkt_size(rbptr[index]);
+			if (index +  pktsize > rptr)
+				rptr = (index + pktsize) % rb->sizedwords;
+		}
+
+		/*
+		 * Only parse IBs between the start and the rptr or the next
+		 * context switch, whichever comes first
+		 */
+
+		if (index == ib_parse_start)
+			parse_ibs = 1;
+		else if (index == rptr || adreno_rb_ctxtswitch(&rbptr[index]))
+			parse_ibs = 0;
+
+		if (parse_ibs && adreno_cmd_is_ib(rbptr[index])) {
+			unsigned int ibaddr = rbptr[index + 1];
+			unsigned int ibsize = rbptr[index + 2];
+
+			/*
+			 * This will return non NULL if the IB happens to be
+			 * part of the context memory (i.e - context switch
+			 * command buffers)
+			 */
+
+			struct kgsl_memdesc *memdesc =
+				adreno_find_ctxtmem(device, ptbase, ibaddr,
+					ibsize);
+
+			/*
+			 * The IB from CP_IB1_BASE and the IBs for legacy
+			 * context switch go into the snapshot all
+			 * others get marked at GPU objects
+			 */
+
+			if (ibaddr == ibbase || memdesc != NULL)
+				push_object(device, SNAPSHOT_OBJ_TYPE_IB,
+					ptbase, ibaddr, ibsize);
+			else
+				ib_add_gpu_object(device, ptbase, ibaddr,
+					ibsize);
+		}
+
+		index = index + 1;
+
+		if (index == rb->sizedwords)
+			index = 0;
+
+		data++;
+	}
+
+	/* Return the size of the section */
+	return size + sizeof(*header);
+}
+
+/* Snapshot the memory for an indirect buffer */
+static int snapshot_ib(struct kgsl_device *device, void *snapshot,
+	int remain, void *priv)
+{
+	struct kgsl_snapshot_ib *header = snapshot;
+	struct kgsl_snapshot_obj *obj = priv;
+	unsigned int *src = obj->ptr;
+	unsigned int *dst = snapshot + sizeof(*header);
+	int i;
+
+	if (remain < (obj->dwords << 2) + sizeof(*header)) {
+		KGSL_DRV_ERR(device,
+			"snapshot: Not enough memory for the ib section");
+		return 0;
+	}
+
+	/* Write the sub-header for the section */
+	header->gpuaddr = obj->gpuaddr;
+	header->ptbase = obj->ptbase;
+	header->size = obj->dwords;
+
+	/* Write the contents of the ib */
+	for (i = 0; i < obj->dwords; i++, src++, dst++) {
+		*dst = *src;
+
+		if (pkt_is_type3(*src)) {
+			if ((obj->dwords - i) < type3_pkt_size(*src) + 1)
+				continue;
+
+			if (adreno_cmd_is_ib(*src))
+				push_object(device, SNAPSHOT_OBJ_TYPE_IB,
+					obj->ptbase, src[1], src[2]);
+			else
+				ib_parse_type3(device, src, obj->ptbase);
+		}
+	}
+
+	return (obj->dwords << 2) + sizeof(*header);
+}
+
+/* Dump another item on the current pending list */
+static void *dump_object(struct kgsl_device *device, int obj, void *snapshot,
+	int *remain)
+{
+	switch (objbuf[obj].type) {
+	case SNAPSHOT_OBJ_TYPE_IB:
+		snapshot = kgsl_snapshot_add_section(device,
+			KGSL_SNAPSHOT_SECTION_IB, snapshot, remain,
+			snapshot_ib, &objbuf[obj]);
+		break;
+	default:
+		KGSL_DRV_ERR(device,
+			"snapshot: Invalid snapshot object type: %d\n",
+			objbuf[obj].type);
+		break;
+	}
+
+	return snapshot;
+}
+
+/* adreno_snapshot - Snapshot the Adreno GPU state
+ * @device - KGSL device to snapshot
+ * @snapshot - Pointer to the start of memory to write into
+ * @remain - A pointer to how many bytes of memory are remaining in the snapshot
+ * @hang - set if this snapshot was automatically triggered by a GPU hang
+ * This is a hook function called by kgsl_snapshot to snapshot the
+ * Adreno specific information for the GPU snapshot.  In turn, this function
+ * calls the GPU specific snapshot function to get core specific information.
+ */
+
+void *adreno_snapshot(struct kgsl_device *device, void *snapshot, int *remain,
+		int hang)
+{
+	int i;
+	uint32_t ptbase, ibbase, ibsize;
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
+	/* Reset the list of objects */
+	objbufptr = 0;
+
+	snapshot_frozen_objsize = 0;
+
+	/* Clear the caches for the visibilty stream and VBO parsing */
+
+	vfd_control_0 = 0;
+	vfd_index_max = 0;
+	vsc_size_address = 0;
+
+	memset(vsc_pipe, 0, sizeof(vsc_pipe));
+	memset(vbo, 0, sizeof(vbo));
+
+	/* Get the physical address of the MMU pagetable */
+	ptbase = kgsl_mmu_get_current_ptbase(&device->mmu);
+
+	/* Dump the ringbuffer */
+	snapshot = kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_RB,
+		snapshot, remain, snapshot_rb, NULL);
+
+	/*
+	 * Make sure that the last IB1 that was being executed is dumped.
+	 * Since this was the last IB1 that was processed, we should have
+	 * already added it to the list during the ringbuffer parse but we
+	 * want to be double plus sure.
+	 */
+
+	kgsl_regread(device, REG_CP_IB1_BASE, &ibbase);
+	kgsl_regread(device, REG_CP_IB1_BUFSZ, &ibsize);
+
+	/*
+	 * The problem is that IB size from the register is the unprocessed size
+	 * of the buffer not the original size, so if we didn't catch this
+	 * buffer being directly used in the RB, then we might not be able to
+	 * dump the whle thing. Print a warning message so we can try to
+	 * figure how often this really happens.
+	 */
+
+	if (!find_object(SNAPSHOT_OBJ_TYPE_IB, ibbase, ptbase) && ibsize) {
+		push_object(device, SNAPSHOT_OBJ_TYPE_IB, ptbase,
+			ibbase, ibsize);
+		KGSL_DRV_ERR(device, "CP_IB1_BASE not found in the ringbuffer. "
+			"Dumping %x dwords of the buffer.\n", ibsize);
+	}
+
+	kgsl_regread(device, REG_CP_IB2_BASE, &ibbase);
+	kgsl_regread(device, REG_CP_IB2_BUFSZ, &ibsize);
+
+	/*
+	 * Add the last parsed IB2 to the list. The IB2 should be found as we
+	 * parse the objects below, but we try to add it to the list first, so
+	 * it too can be parsed.  Don't print an error message in this case - if
+	 * the IB2 is found during parsing, the list will be updated with the
+	 * correct size.
+	 */
+
+	if (!find_object(SNAPSHOT_OBJ_TYPE_IB, ibbase, ptbase) && ibsize) {
+		push_object(device, SNAPSHOT_OBJ_TYPE_IB, ptbase,
+			ibbase, ibsize);
+	}
+
+	/*
+	 * Go through the list of found objects and dump each one.  As the IBs
+	 * are parsed, more objects might be found, and objbufptr will increase
+	 */
+	for (i = 0; i < objbufptr; i++)
+		snapshot = dump_object(device, i, snapshot, remain);
+
+	/*
+	 * Only dump the istore on a hang - reading it on a running system
+	 * has a non 0 chance of hanging the GPU
+	 */
+
+	if (hang) {
+		snapshot = kgsl_snapshot_add_section(device,
+			KGSL_SNAPSHOT_SECTION_ISTORE, snapshot, remain,
+			snapshot_istore, NULL);
+	}
+
+	/* Add GPU specific sections - registers mainly, but other stuff too */
+	if (adreno_dev->gpudev->snapshot)
+		snapshot = adreno_dev->gpudev->snapshot(adreno_dev, snapshot,
+			remain, hang);
+
+	if (snapshot_frozen_objsize)
+		KGSL_DRV_ERR(device, "GPU snapshot froze %dKb of GPU buffers\n",
+			snapshot_frozen_objsize / 1024);
+
+	return snapshot;
+}
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
new file mode 100644
index 0000000..e918cc2
--- /dev/null
+++ b/drivers/gpu/msm/kgsl.c
@@ -0,0 +1,2697 @@
+/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/module.h>
+#include <linux/fb.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/android_pmem.h>
+#include <linux/vmalloc.h>
+#include <linux/pm_runtime.h>
+#include <linux/genlock.h>
+#include <linux/rbtree.h>
+#include <linux/ashmem.h>
+#include <linux/major.h>
+#include <linux/ion.h>
+#include <linux/io.h>
+#include <mach/socinfo.h>
+
+#include "kgsl.h"
+#include "kgsl_debugfs.h"
+#include "kgsl_cffdump.h"
+#include "kgsl_log.h"
+#include "kgsl_sharedmem.h"
+#include "kgsl_device.h"
+#include "kgsl_trace.h"
+
+#undef MODULE_PARAM_PREFIX
+#define MODULE_PARAM_PREFIX "kgsl."
+
+static int kgsl_pagetable_count = KGSL_PAGETABLE_COUNT;
+static char *ksgl_mmu_type;
+module_param_named(ptcount, kgsl_pagetable_count, int, 0);
+MODULE_PARM_DESC(kgsl_pagetable_count,
+"Minimum number of pagetables for KGSL to allocate at initialization time");
+module_param_named(mmutype, ksgl_mmu_type, charp, 0);
+MODULE_PARM_DESC(ksgl_mmu_type,
+"Type of MMU to be used for graphics. Valid values are 'iommu' or 'gpummu' or 'nommu'");
+
+static struct ion_client *kgsl_ion_client;
+
+/**
+ * kgsl_add_event - Add a new timstamp event for the KGSL device
+ * @device - KGSL device for the new event
+ * @ts - the timestamp to trigger the event on
+ * @cb - callback function to call when the timestamp expires
+ * @priv - private data for the specific event type
+ * @owner - driver instance that owns this event
+ *
+ * @returns - 0 on success or error code on failure
+ */
+
+static int kgsl_add_event(struct kgsl_device *device, u32 id, u32 ts,
+	void (*cb)(struct kgsl_device *, void *, u32, u32), void *priv,
+	struct kgsl_device_private *owner)
+{
+	struct kgsl_event *event;
+	struct list_head *n;
+	unsigned int cur_ts;
+	struct kgsl_context *context = NULL;
+
+	if (cb == NULL)
+		return -EINVAL;
+
+	if (id != KGSL_MEMSTORE_GLOBAL) {
+		context = idr_find(&device->context_idr, id);
+		if (context == NULL)
+			return -EINVAL;
+	}
+	cur_ts = kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_RETIRED);
+
+	/* Check to see if the requested timestamp has already fired */
+
+	if (timestamp_cmp(cur_ts, ts) >= 0) {
+		cb(device, priv, id, cur_ts);
+		return 0;
+	}
+
+	event = kzalloc(sizeof(*event), GFP_KERNEL);
+	if (event == NULL)
+		return -ENOMEM;
+
+	event->context = context;
+	event->timestamp = ts;
+	event->priv = priv;
+	event->func = cb;
+	event->owner = owner;
+
+	/*
+	 * Add the event in order to the list.  Order is by context id
+	 * first and then by timestamp for that context.
+	 */
+
+	for (n = device->events.next ; n != &device->events; n = n->next) {
+		struct kgsl_event *e =
+			list_entry(n, struct kgsl_event, list);
+
+		if (e->context != context)
+			continue;
+
+		if (timestamp_cmp(e->timestamp, ts) > 0) {
+			list_add(&event->list, n->prev);
+			break;
+		}
+	}
+
+	if (n == &device->events)
+		list_add_tail(&event->list, &device->events);
+
+	queue_work(device->work_queue, &device->ts_expired_ws);
+	return 0;
+}
+
+/**
+ * kgsl_cancel_events_ctxt - Cancel all events for a context
+ * @device - KGSL device for the events to cancel
+ * @ctxt - context whose events we want to cancel
+ *
+ */
+static void kgsl_cancel_events_ctxt(struct kgsl_device *device,
+	struct kgsl_context *context)
+{
+	struct kgsl_event *event, *event_tmp;
+	unsigned int id, cur;
+
+	cur = kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_RETIRED);
+	id = context->id;
+
+	list_for_each_entry_safe(event, event_tmp, &device->events, list) {
+		if (event->context != context)
+			continue;
+
+		/*
+		 * "cancel" the events by calling their callback.
+		 * Currently, events are used for lock and memory
+		 * management, so if the process is dying the right
+		 * thing to do is release or free.
+		 */
+		if (event->func)
+			event->func(device, event->priv, id, cur);
+
+		list_del(&event->list);
+		kfree(event);
+	}
+}
+
+/**
+ * kgsl_cancel_events - Cancel all events for a process
+ * @device - KGSL device for the events to cancel
+ * @owner - driver instance that owns the events to cancel
+ *
+ */
+static void kgsl_cancel_events(struct kgsl_device *device,
+	struct kgsl_device_private *owner)
+{
+	struct kgsl_event *event, *event_tmp;
+	unsigned int id, cur;
+
+	list_for_each_entry_safe(event, event_tmp, &device->events, list) {
+		if (event->owner != owner)
+			continue;
+
+		cur = kgsl_readtimestamp(device, event->context,
+					 KGSL_TIMESTAMP_RETIRED);
+
+		id = event->context ? event->context->id : KGSL_MEMSTORE_GLOBAL;
+		/*
+		 * "cancel" the events by calling their callback.
+		 * Currently, events are used for lock and memory
+		 * management, so if the process is dying the right
+		 * thing to do is release or free.
+		 */
+		if (event->func)
+			event->func(device, event->priv, id, cur);
+
+		list_del(&event->list);
+		kfree(event);
+	}
+}
+
+/* kgsl_get_mem_entry - get the mem_entry structure for the specified object
+ * @ptbase - the pagetable base of the object
+ * @gpuaddr - the GPU address of the object
+ * @size - Size of the region to search
+ */
+
+struct kgsl_mem_entry *kgsl_get_mem_entry(unsigned int ptbase,
+	unsigned int gpuaddr, unsigned int size)
+{
+	struct kgsl_process_private *priv;
+	struct kgsl_mem_entry *entry;
+
+	mutex_lock(&kgsl_driver.process_mutex);
+
+	list_for_each_entry(priv, &kgsl_driver.process_list, list) {
+		if (!kgsl_mmu_pt_equal(priv->pagetable, ptbase))
+			continue;
+		spin_lock(&priv->mem_lock);
+		entry = kgsl_sharedmem_find_region(priv, gpuaddr, size);
+
+		if (entry) {
+			spin_unlock(&priv->mem_lock);
+			mutex_unlock(&kgsl_driver.process_mutex);
+			return entry;
+		}
+		spin_unlock(&priv->mem_lock);
+	}
+	mutex_unlock(&kgsl_driver.process_mutex);
+
+	return NULL;
+}
+EXPORT_SYMBOL(kgsl_get_mem_entry);
+
+static inline struct kgsl_mem_entry *
+kgsl_mem_entry_create(void)
+{
+	struct kgsl_mem_entry *entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+
+	if (!entry)
+		KGSL_CORE_ERR("kzalloc(%d) failed\n", sizeof(*entry));
+	else
+		kref_init(&entry->refcount);
+
+	return entry;
+}
+
+void
+kgsl_mem_entry_destroy(struct kref *kref)
+{
+	struct kgsl_mem_entry *entry = container_of(kref,
+						    struct kgsl_mem_entry,
+						    refcount);
+
+	if (entry->memtype != KGSL_MEM_ENTRY_KERNEL)
+		kgsl_driver.stats.mapped -= entry->memdesc.size;
+
+	/*
+	 * Ion takes care of freeing the sglist for us (how nice </sarcasm>) so
+	 * unmap the dma before freeing the sharedmem so kgsl_sharedmem_free
+	 * doesn't try to free it again
+	 */
+
+	if (entry->memtype == KGSL_MEM_ENTRY_ION) {
+		ion_unmap_dma(kgsl_ion_client, entry->priv_data);
+		entry->memdesc.sg = NULL;
+	}
+
+	kgsl_sharedmem_free(&entry->memdesc);
+
+	switch (entry->memtype) {
+	case KGSL_MEM_ENTRY_PMEM:
+	case KGSL_MEM_ENTRY_ASHMEM:
+		if (entry->priv_data)
+			fput(entry->priv_data);
+		break;
+	case KGSL_MEM_ENTRY_ION:
+		ion_free(kgsl_ion_client, entry->priv_data);
+		break;
+	}
+
+	kfree(entry);
+}
+EXPORT_SYMBOL(kgsl_mem_entry_destroy);
+
+static
+void kgsl_mem_entry_attach_process(struct kgsl_mem_entry *entry,
+				   struct kgsl_process_private *process)
+{
+	struct rb_node **node;
+	struct rb_node *parent = NULL;
+
+	spin_lock(&process->mem_lock);
+
+	node = &process->mem_rb.rb_node;
+
+	while (*node) {
+		struct kgsl_mem_entry *cur;
+
+		parent = *node;
+		cur = rb_entry(parent, struct kgsl_mem_entry, node);
+
+		if (entry->memdesc.gpuaddr < cur->memdesc.gpuaddr)
+			node = &parent->rb_left;
+		else
+			node = &parent->rb_right;
+	}
+
+	rb_link_node(&entry->node, parent, node);
+	rb_insert_color(&entry->node, &process->mem_rb);
+
+	spin_unlock(&process->mem_lock);
+
+	entry->priv = process;
+}
+
+/* Detach a memory entry from a process and unmap it from the MMU */
+
+static void kgsl_mem_entry_detach_process(struct kgsl_mem_entry *entry)
+{
+	if (entry == NULL)
+		return;
+
+	entry->priv->stats[entry->memtype].cur -= entry->memdesc.size;
+	entry->priv = NULL;
+
+	kgsl_mmu_unmap(entry->memdesc.pagetable, &entry->memdesc);
+
+	kgsl_mem_entry_put(entry);
+}
+
+/* Allocate a new context id */
+
+static struct kgsl_context *
+kgsl_create_context(struct kgsl_device_private *dev_priv)
+{
+	struct kgsl_context *context;
+	int ret, id;
+
+	context = kzalloc(sizeof(*context), GFP_KERNEL);
+
+	if (context == NULL)
+		return NULL;
+
+	while (1) {
+		if (idr_pre_get(&dev_priv->device->context_idr,
+				GFP_KERNEL) == 0) {
+			kfree(context);
+			return NULL;
+		}
+
+		ret = idr_get_new_above(&dev_priv->device->context_idr,
+				  context, 1, &id);
+
+		if (ret != -EAGAIN)
+			break;
+	}
+
+	if (ret) {
+		kfree(context);
+		return NULL;
+	}
+
+	/* MAX - 1, there is one memdesc in memstore for device info */
+	if (id >= KGSL_MEMSTORE_MAX) {
+		KGSL_DRV_ERR(dev_priv->device, "cannot have more than %d "
+				"ctxts due to memstore limitation\n",
+				KGSL_MEMSTORE_MAX);
+		idr_remove(&dev_priv->device->context_idr, id);
+		kfree(context);
+		return NULL;
+	}
+
+	kref_init(&context->refcount);
+	context->id = id;
+	context->dev_priv = dev_priv;
+
+	return context;
+}
+
+/**
+ * kgsl_context_detach - Release the "master" context reference
+ * @context - The context that will be detached
+ *
+ * This is called when a context becomes unusable, because userspace
+ * has requested for it to be destroyed. The context itself may
+ * exist a bit longer until its reference count goes to zero.
+ * Other code referencing the context can detect that it has been
+ * detached because the context id will be set to KGSL_CONTEXT_INVALID.
+ */
+void
+kgsl_context_detach(struct kgsl_context *context)
+{
+	int id;
+	struct kgsl_device *device;
+	if (context == NULL)
+		return;
+	device = context->dev_priv->device;
+	trace_kgsl_context_detach(device, context);
+	id = context->id;
+
+	if (device->ftbl->drawctxt_destroy)
+		device->ftbl->drawctxt_destroy(device, context);
+	/*device specific drawctxt_destroy MUST clean up devctxt */
+	BUG_ON(context->devctxt);
+	/*
+	 * Cancel events after the device-specific context is
+	 * destroyed, to avoid possibly freeing memory while
+	 * it is still in use by the GPU.
+	 */
+	kgsl_cancel_events_ctxt(device, context);
+	idr_remove(&device->context_idr, id);
+	context->id = KGSL_CONTEXT_INVALID;
+	kgsl_context_put(context);
+}
+
+void
+kgsl_context_destroy(struct kref *kref)
+{
+	struct kgsl_context *context = container_of(kref, struct kgsl_context,
+						    refcount);
+	kfree(context);
+}
+
+void kgsl_timestamp_expired(struct work_struct *work)
+{
+	struct kgsl_device *device = container_of(work, struct kgsl_device,
+		ts_expired_ws);
+	struct kgsl_event *event, *event_tmp;
+	uint32_t ts_processed;
+	unsigned int id;
+
+	mutex_lock(&device->mutex);
+
+	/* Process expired events */
+	list_for_each_entry_safe(event, event_tmp, &device->events, list) {
+		ts_processed = kgsl_readtimestamp(device, event->context,
+						  KGSL_TIMESTAMP_RETIRED);
+		if (timestamp_cmp(ts_processed, event->timestamp) < 0)
+			continue;
+
+		id = event->context ? event->context->id : KGSL_MEMSTORE_GLOBAL;
+
+		if (event->func)
+			event->func(device, event->priv, id, ts_processed);
+
+		list_del(&event->list);
+		kfree(event);
+	}
+
+	device->last_expired_ctxt_id = KGSL_CONTEXT_INVALID;
+
+	mutex_unlock(&device->mutex);
+}
+EXPORT_SYMBOL(kgsl_timestamp_expired);
+
+static void kgsl_check_idle_locked(struct kgsl_device *device)
+{
+	if (device->pwrctrl.nap_allowed == true &&
+	    device->state == KGSL_STATE_ACTIVE &&
+		device->requested_state == KGSL_STATE_NONE) {
+		kgsl_pwrctrl_request_state(device, KGSL_STATE_NAP);
+		if (kgsl_pwrctrl_sleep(device) != 0)
+			mod_timer(&device->idle_timer,
+				  jiffies +
+				  device->pwrctrl.interval_timeout);
+	}
+}
+
+static void kgsl_check_idle(struct kgsl_device *device)
+{
+	mutex_lock(&device->mutex);
+	kgsl_check_idle_locked(device);
+	mutex_unlock(&device->mutex);
+}
+
+struct kgsl_device *kgsl_get_device(int dev_idx)
+{
+	int i;
+	struct kgsl_device *ret = NULL;
+
+	mutex_lock(&kgsl_driver.devlock);
+
+	for (i = 0; i < KGSL_DEVICE_MAX; i++) {
+		if (kgsl_driver.devp[i] && kgsl_driver.devp[i]->id == dev_idx) {
+			ret = kgsl_driver.devp[i];
+			break;
+		}
+	}
+
+	mutex_unlock(&kgsl_driver.devlock);
+	return ret;
+}
+EXPORT_SYMBOL(kgsl_get_device);
+
+static struct kgsl_device *kgsl_get_minor(int minor)
+{
+	struct kgsl_device *ret = NULL;
+
+	if (minor < 0 || minor >= KGSL_DEVICE_MAX)
+		return NULL;
+
+	mutex_lock(&kgsl_driver.devlock);
+	ret = kgsl_driver.devp[minor];
+	mutex_unlock(&kgsl_driver.devlock);
+
+	return ret;
+}
+
+int kgsl_register_ts_notifier(struct kgsl_device *device,
+			      struct notifier_block *nb)
+{
+	BUG_ON(device == NULL);
+	return atomic_notifier_chain_register(&device->ts_notifier_list,
+					      nb);
+}
+EXPORT_SYMBOL(kgsl_register_ts_notifier);
+
+int kgsl_unregister_ts_notifier(struct kgsl_device *device,
+				struct notifier_block *nb)
+{
+	BUG_ON(device == NULL);
+	return atomic_notifier_chain_unregister(&device->ts_notifier_list,
+						nb);
+}
+EXPORT_SYMBOL(kgsl_unregister_ts_notifier);
+
+int kgsl_check_timestamp(struct kgsl_device *device,
+	struct kgsl_context *context, unsigned int timestamp)
+{
+	unsigned int ts_processed;
+
+	ts_processed = kgsl_readtimestamp(device, context,
+					  KGSL_TIMESTAMP_RETIRED);
+
+	return (timestamp_cmp(ts_processed, timestamp) >= 0);
+}
+EXPORT_SYMBOL(kgsl_check_timestamp);
+
+static int kgsl_suspend_device(struct kgsl_device *device, pm_message_t state)
+{
+	int status = -EINVAL;
+	unsigned int nap_allowed_saved;
+	struct kgsl_pwrscale_policy *policy_saved;
+
+	if (!device)
+		return -EINVAL;
+
+	KGSL_PWR_WARN(device, "suspend start\n");
+
+	mutex_lock(&device->mutex);
+	nap_allowed_saved = device->pwrctrl.nap_allowed;
+	device->pwrctrl.nap_allowed = false;
+	policy_saved = device->pwrscale.policy;
+	device->pwrscale.policy = NULL;
+	kgsl_pwrctrl_request_state(device, KGSL_STATE_SUSPEND);
+	/* Make sure no user process is waiting for a timestamp *
+	 * before supending */
+	if (device->active_cnt != 0) {
+		mutex_unlock(&device->mutex);
+		wait_for_completion(&device->suspend_gate);
+		mutex_lock(&device->mutex);
+	}
+	/* Don't let the timer wake us during suspended sleep. */
+	del_timer_sync(&device->idle_timer);
+	switch (device->state) {
+		case KGSL_STATE_INIT:
+			break;
+		case KGSL_STATE_ACTIVE:
+			/* Wait for the device to become idle */
+			device->ftbl->idle(device, KGSL_TIMEOUT_DEFAULT);
+		case KGSL_STATE_NAP:
+		case KGSL_STATE_SLEEP:
+			/* Get the completion ready to be waited upon. */
+			INIT_COMPLETION(device->hwaccess_gate);
+			device->ftbl->suspend_context(device);
+			device->ftbl->stop(device);
+			if (device->idle_wakelock.name)
+				wake_unlock(&device->idle_wakelock);
+			pm_qos_update_request(&device->pm_qos_req_dma,
+						PM_QOS_DEFAULT_VALUE);
+			kgsl_pwrctrl_set_state(device, KGSL_STATE_SUSPEND);
+			break;
+		case KGSL_STATE_SLUMBER:
+			INIT_COMPLETION(device->hwaccess_gate);
+			kgsl_pwrctrl_set_state(device, KGSL_STATE_SUSPEND);
+			break;
+		default:
+			KGSL_PWR_ERR(device, "suspend fail, device %d\n",
+					device->id);
+			goto end;
+	}
+	kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
+	device->pwrctrl.nap_allowed = nap_allowed_saved;
+	device->pwrscale.policy = policy_saved;
+	status = 0;
+
+end:
+	mutex_unlock(&device->mutex);
+	KGSL_PWR_WARN(device, "suspend end\n");
+	return status;
+}
+
+static int kgsl_resume_device(struct kgsl_device *device)
+{
+	int status = -EINVAL;
+
+	if (!device)
+		return -EINVAL;
+
+	KGSL_PWR_WARN(device, "resume start\n");
+	mutex_lock(&device->mutex);
+	if (device->state == KGSL_STATE_SUSPEND) {
+		kgsl_pwrctrl_set_state(device, KGSL_STATE_SLUMBER);
+		status = 0;
+		complete_all(&device->hwaccess_gate);
+	}
+	kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
+
+	mutex_unlock(&device->mutex);
+	KGSL_PWR_WARN(device, "resume end\n");
+	return status;
+}
+
+static int kgsl_suspend(struct device *dev)
+{
+
+	pm_message_t arg = {0};
+	struct kgsl_device *device = dev_get_drvdata(dev);
+	return kgsl_suspend_device(device, arg);
+}
+
+static int kgsl_resume(struct device *dev)
+{
+	struct kgsl_device *device = dev_get_drvdata(dev);
+	return kgsl_resume_device(device);
+}
+
+static int kgsl_runtime_suspend(struct device *dev)
+{
+	return 0;
+}
+
+static int kgsl_runtime_resume(struct device *dev)
+{
+	return 0;
+}
+
+const struct dev_pm_ops kgsl_pm_ops = {
+	.suspend = kgsl_suspend,
+	.resume = kgsl_resume,
+	.runtime_suspend = kgsl_runtime_suspend,
+	.runtime_resume = kgsl_runtime_resume,
+};
+EXPORT_SYMBOL(kgsl_pm_ops);
+
+void kgsl_early_suspend_driver(struct early_suspend *h)
+{
+	struct kgsl_device *device = container_of(h,
+					struct kgsl_device, display_off);
+	KGSL_PWR_WARN(device, "early suspend start\n");
+	mutex_lock(&device->mutex);
+	kgsl_pwrctrl_request_state(device, KGSL_STATE_SLUMBER);
+	kgsl_pwrctrl_sleep(device);
+	mutex_unlock(&device->mutex);
+	KGSL_PWR_WARN(device, "early suspend end\n");
+}
+EXPORT_SYMBOL(kgsl_early_suspend_driver);
+
+int kgsl_suspend_driver(struct platform_device *pdev,
+					pm_message_t state)
+{
+	struct kgsl_device *device = dev_get_drvdata(&pdev->dev);
+	return kgsl_suspend_device(device, state);
+}
+EXPORT_SYMBOL(kgsl_suspend_driver);
+
+int kgsl_resume_driver(struct platform_device *pdev)
+{
+	struct kgsl_device *device = dev_get_drvdata(&pdev->dev);
+	return kgsl_resume_device(device);
+}
+EXPORT_SYMBOL(kgsl_resume_driver);
+
+void kgsl_late_resume_driver(struct early_suspend *h)
+{
+	struct kgsl_device *device = container_of(h,
+					struct kgsl_device, display_off);
+	KGSL_PWR_WARN(device, "late resume start\n");
+	mutex_lock(&device->mutex);
+	device->pwrctrl.restore_slumber = 0;
+	if (device->pwrscale.policy == NULL)
+		kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_TURBO);
+	kgsl_pwrctrl_wake(device);
+	mutex_unlock(&device->mutex);
+	kgsl_check_idle(device);
+	KGSL_PWR_WARN(device, "late resume end\n");
+}
+EXPORT_SYMBOL(kgsl_late_resume_driver);
+
+/* file operations */
+static struct kgsl_process_private *
+kgsl_get_process_private(struct kgsl_device_private *cur_dev_priv)
+{
+	struct kgsl_process_private *private;
+
+	mutex_lock(&kgsl_driver.process_mutex);
+	list_for_each_entry(private, &kgsl_driver.process_list, list) {
+		if (private->pid == task_tgid_nr(current)) {
+			private->refcnt++;
+			goto out;
+		}
+	}
+
+	/* no existing process private found for this dev_priv, create one */
+	private = kzalloc(sizeof(struct kgsl_process_private), GFP_KERNEL);
+	if (private == NULL) {
+		KGSL_DRV_ERR(cur_dev_priv->device, "kzalloc(%d) failed\n",
+			sizeof(struct kgsl_process_private));
+		goto out;
+	}
+
+	spin_lock_init(&private->mem_lock);
+	private->refcnt = 1;
+	private->pid = task_tgid_nr(current);
+	private->mem_rb = RB_ROOT;
+
+	if (kgsl_mmu_enabled())
+	{
+		unsigned long pt_name;
+
+		pt_name = task_tgid_nr(current);
+		private->pagetable = kgsl_mmu_getpagetable(pt_name);
+		if (private->pagetable == NULL) {
+			kfree(private);
+			private = NULL;
+			goto out;
+		}
+	}
+
+	list_add(&private->list, &kgsl_driver.process_list);
+
+	kgsl_process_init_sysfs(private);
+
+out:
+	mutex_unlock(&kgsl_driver.process_mutex);
+	return private;
+}
+
+static void
+kgsl_put_process_private(struct kgsl_device *device,
+			 struct kgsl_process_private *private)
+{
+	struct kgsl_mem_entry *entry = NULL;
+	struct rb_node *node;
+
+	if (!private)
+		return;
+
+	mutex_lock(&kgsl_driver.process_mutex);
+
+	if (--private->refcnt)
+		goto unlock;
+
+	kgsl_process_uninit_sysfs(private);
+
+	list_del(&private->list);
+
+	for (node = rb_first(&private->mem_rb); node; ) {
+		entry = rb_entry(node, struct kgsl_mem_entry, node);
+		node = rb_next(&entry->node);
+
+		rb_erase(&entry->node, &private->mem_rb);
+		kgsl_mem_entry_detach_process(entry);
+	}
+	kgsl_mmu_putpagetable(private->pagetable);
+	kfree(private);
+unlock:
+	mutex_unlock(&kgsl_driver.process_mutex);
+}
+
+static int kgsl_release(struct inode *inodep, struct file *filep)
+{
+	int result = 0;
+	struct kgsl_device_private *dev_priv = filep->private_data;
+	struct kgsl_process_private *private = dev_priv->process_priv;
+	struct kgsl_device *device = dev_priv->device;
+	struct kgsl_context *context;
+	int next = 0;
+
+	filep->private_data = NULL;
+
+	mutex_lock(&device->mutex);
+	kgsl_check_suspended(device);
+
+	while (1) {
+		context = idr_get_next(&device->context_idr, &next);
+		if (context == NULL)
+			break;
+
+		if (context->dev_priv == dev_priv)
+			kgsl_context_detach(context);
+
+		next = next + 1;
+	}
+	/*
+	 * Clean up any to-be-freed entries that belong to this
+	 * process and this device. This is done after the context
+	 * are destroyed to avoid possibly freeing memory while
+	 * it is still in use by the GPU.
+	 */
+	kgsl_cancel_events(device, dev_priv);
+
+	device->open_count--;
+	if (device->open_count == 0) {
+		result = device->ftbl->stop(device);
+		kgsl_pwrctrl_set_state(device, KGSL_STATE_INIT);
+	}
+
+	mutex_unlock(&device->mutex);
+	kfree(dev_priv);
+
+	kgsl_put_process_private(device, private);
+
+	pm_runtime_put(device->parentdev);
+	return result;
+}
+
+static int kgsl_open(struct inode *inodep, struct file *filep)
+{
+	int result;
+	struct kgsl_device_private *dev_priv;
+	struct kgsl_device *device;
+	unsigned int minor = iminor(inodep);
+
+	device = kgsl_get_minor(minor);
+	BUG_ON(device == NULL);
+
+	if (filep->f_flags & O_EXCL) {
+		KGSL_DRV_ERR(device, "O_EXCL not allowed\n");
+		return -EBUSY;
+	}
+
+	result = pm_runtime_get_sync(device->parentdev);
+	if (result < 0) {
+		KGSL_DRV_ERR(device,
+			"Runtime PM: Unable to wake up the device, rc = %d\n",
+			result);
+		return result;
+	}
+	result = 0;
+
+	dev_priv = kzalloc(sizeof(struct kgsl_device_private), GFP_KERNEL);
+	if (dev_priv == NULL) {
+		KGSL_DRV_ERR(device, "kzalloc failed(%d)\n",
+			sizeof(struct kgsl_device_private));
+		result = -ENOMEM;
+		goto err_pmruntime;
+	}
+
+	dev_priv->device = device;
+	filep->private_data = dev_priv;
+
+	/* Get file (per process) private struct */
+	dev_priv->process_priv = kgsl_get_process_private(dev_priv);
+	if (dev_priv->process_priv ==  NULL) {
+		result = -ENOMEM;
+		goto err_freedevpriv;
+	}
+
+	mutex_lock(&device->mutex);
+	kgsl_check_suspended(device);
+
+	if (device->open_count == 0) {
+		kgsl_sharedmem_set(&device->memstore, 0, 0,
+				device->memstore.size);
+
+		result = device->ftbl->start(device, true);
+
+		if (result) {
+			mutex_unlock(&device->mutex);
+			goto err_putprocess;
+		}
+		kgsl_pwrctrl_set_state(device, KGSL_STATE_ACTIVE);
+	}
+	device->open_count++;
+	mutex_unlock(&device->mutex);
+
+	KGSL_DRV_INFO(device, "Initialized %s: mmu=%s pagetable_count=%d\n",
+		device->name, kgsl_mmu_enabled() ? "on" : "off",
+		kgsl_pagetable_count);
+
+	return result;
+
+err_putprocess:
+	kgsl_put_process_private(device, dev_priv->process_priv);
+err_freedevpriv:
+	filep->private_data = NULL;
+	kfree(dev_priv);
+err_pmruntime:
+	pm_runtime_put(device->parentdev);
+	return result;
+}
+
+/*call with private->mem_lock locked */
+struct kgsl_mem_entry *
+kgsl_sharedmem_find_region(struct kgsl_process_private *private,
+	unsigned int gpuaddr, size_t size)
+{
+	struct rb_node *node = private->mem_rb.rb_node;
+
+	while (node != NULL) {
+		struct kgsl_mem_entry *entry;
+
+		entry = rb_entry(node, struct kgsl_mem_entry, node);
+
+
+		if (kgsl_gpuaddr_in_memdesc(&entry->memdesc, gpuaddr, size))
+			return entry;
+
+		if (gpuaddr < entry->memdesc.gpuaddr)
+			node = node->rb_left;
+		else if (gpuaddr >=
+			(entry->memdesc.gpuaddr + entry->memdesc.size))
+			node = node->rb_right;
+		else {
+			return NULL;
+		}
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL(kgsl_sharedmem_find_region);
+
+/*call with private->mem_lock locked */
+static inline struct kgsl_mem_entry *
+kgsl_sharedmem_find(struct kgsl_process_private *private, unsigned int gpuaddr)
+{
+	return kgsl_sharedmem_find_region(private, gpuaddr, 1);
+}
+
+/*call all ioctl sub functions with driver locked*/
+static long kgsl_ioctl_device_getproperty(struct kgsl_device_private *dev_priv,
+					  unsigned int cmd, void *data)
+{
+	int result = 0;
+	struct kgsl_device_getproperty *param = data;
+
+	switch (param->type) {
+	case KGSL_PROP_VERSION:
+	{
+		struct kgsl_version version;
+		if (param->sizebytes != sizeof(version)) {
+			result = -EINVAL;
+			break;
+		}
+
+		version.drv_major = KGSL_VERSION_MAJOR;
+		version.drv_minor = KGSL_VERSION_MINOR;
+		version.dev_major = dev_priv->device->ver_major;
+		version.dev_minor = dev_priv->device->ver_minor;
+
+		if (copy_to_user(param->value, &version, sizeof(version)))
+			result = -EFAULT;
+
+		break;
+	}
+	case KGSL_PROP_GPU_RESET_STAT:
+	{
+		/* Return reset status of given context and clear it */
+		uint32_t id;
+		struct kgsl_context *context;
+
+		if (param->sizebytes != sizeof(unsigned int)) {
+			result = -EINVAL;
+			break;
+		}
+		/* We expect the value passed in to contain the context id */
+		if (copy_from_user(&id, param->value,
+			sizeof(unsigned int))) {
+			result = -EFAULT;
+			break;
+		}
+		context = kgsl_find_context(dev_priv, id);
+		if (!context) {
+			result = -EINVAL;
+			break;
+		}
+		/*
+		 * Copy the reset status to value which also serves as
+		 * the out parameter
+		 */
+		if (copy_to_user(param->value, &(context->reset_status),
+			sizeof(unsigned int))) {
+			result = -EFAULT;
+			break;
+		}
+		/* Clear reset status once its been queried */
+		context->reset_status = KGSL_CTX_STAT_NO_ERROR;
+		break;
+	}
+	default:
+		result = dev_priv->device->ftbl->getproperty(
+					dev_priv->device, param->type,
+					param->value, param->sizebytes);
+	}
+
+
+	return result;
+}
+
+static long kgsl_ioctl_device_setproperty(struct kgsl_device_private *dev_priv,
+					  unsigned int cmd, void *data)
+{
+	int result = 0;
+	/* The getproperty struct is reused for setproperty too */
+	struct kgsl_device_getproperty *param = data;
+
+	if (dev_priv->device->ftbl->setproperty)
+		result = dev_priv->device->ftbl->setproperty(
+			dev_priv->device, param->type,
+			param->value, param->sizebytes);
+
+	return result;
+}
+
+static long _device_waittimestamp(struct kgsl_device_private *dev_priv,
+		struct kgsl_context *context,
+		unsigned int timestamp,
+		unsigned int timeout)
+{
+	int result = 0;
+	struct kgsl_device *device = dev_priv->device;
+	unsigned int context_id = context ? context->id : KGSL_MEMSTORE_GLOBAL;
+
+	/* Set the active count so that suspend doesn't do the wrong thing */
+
+	device->active_cnt++;
+
+	trace_kgsl_waittimestamp_entry(device, context_id,
+				       kgsl_readtimestamp(device, context,
+							KGSL_TIMESTAMP_RETIRED),
+				       timestamp, timeout);
+
+	result = device->ftbl->waittimestamp(dev_priv->device,
+					context, timestamp, timeout);
+
+	trace_kgsl_waittimestamp_exit(device,
+				      kgsl_readtimestamp(device, context,
+							KGSL_TIMESTAMP_RETIRED),
+				      result);
+
+	/* Fire off any pending suspend operations that are in flight */
+
+	INIT_COMPLETION(dev_priv->device->suspend_gate);
+	dev_priv->device->active_cnt--;
+	complete(&dev_priv->device->suspend_gate);
+
+	return result;
+}
+
+static long kgsl_ioctl_device_waittimestamp(struct kgsl_device_private
+						*dev_priv, unsigned int cmd,
+						void *data)
+{
+	struct kgsl_device_waittimestamp *param = data;
+
+	return _device_waittimestamp(dev_priv, NULL,
+			param->timestamp, param->timeout);
+}
+
+static long kgsl_ioctl_device_waittimestamp_ctxtid(struct kgsl_device_private
+						*dev_priv, unsigned int cmd,
+						void *data)
+{
+	struct kgsl_device_waittimestamp_ctxtid *param = data;
+	struct kgsl_context *context;
+	int result;
+
+	context = kgsl_find_context(dev_priv, param->context_id);
+	if (context == NULL) {
+		KGSL_DRV_ERR(dev_priv->device, "invalid context_id %d\n",
+			param->context_id);
+		return -EINVAL;
+	}
+	/*
+	 * A reference count is needed here, because waittimestamp may
+	 * block with the device mutex unlocked and userspace could
+	 * request for the context to be destroyed during that time.
+	 */
+	kgsl_context_get(context);
+	result = _device_waittimestamp(dev_priv, context,
+			param->timestamp, param->timeout);
+	kgsl_context_put(context);
+	return result;
+}
+
+static long kgsl_ioctl_rb_issueibcmds(struct kgsl_device_private *dev_priv,
+				      unsigned int cmd, void *data)
+{
+	int result = 0;
+	struct kgsl_ringbuffer_issueibcmds *param = data;
+	struct kgsl_ibdesc *ibdesc;
+	struct kgsl_context *context;
+
+	context = kgsl_find_context(dev_priv, param->drawctxt_id);
+	if (context == NULL) {
+		result = -EINVAL;
+		KGSL_DRV_ERR(dev_priv->device,
+			"invalid context_id %d\n",
+			param->drawctxt_id);
+		goto done;
+	}
+
+	if (param->flags & KGSL_CONTEXT_SUBMIT_IB_LIST) {
+		KGSL_DRV_INFO(dev_priv->device,
+			"Using IB list mode for ib submission, numibs: %d\n",
+			param->numibs);
+		if (!param->numibs) {
+			KGSL_DRV_ERR(dev_priv->device,
+				"Invalid numibs as parameter: %d\n",
+				 param->numibs);
+			result = -EINVAL;
+			goto done;
+		}
+
+		ibdesc = kzalloc(sizeof(struct kgsl_ibdesc) * param->numibs,
+					GFP_KERNEL);
+		if (!ibdesc) {
+			KGSL_MEM_ERR(dev_priv->device,
+				"kzalloc(%d) failed\n",
+				sizeof(struct kgsl_ibdesc) * param->numibs);
+			result = -ENOMEM;
+			goto done;
+		}
+
+		if (copy_from_user(ibdesc, (void *)param->ibdesc_addr,
+				sizeof(struct kgsl_ibdesc) * param->numibs)) {
+			result = -EFAULT;
+			KGSL_DRV_ERR(dev_priv->device,
+				"copy_from_user failed\n");
+			goto free_ibdesc;
+		}
+	} else {
+		KGSL_DRV_INFO(dev_priv->device,
+			"Using single IB submission mode for ib submission\n");
+		/* If user space driver is still using the old mode of
+		 * submitting single ib then we need to support that as well */
+		ibdesc = kzalloc(sizeof(struct kgsl_ibdesc), GFP_KERNEL);
+		if (!ibdesc) {
+			KGSL_MEM_ERR(dev_priv->device,
+				"kzalloc(%d) failed\n",
+				sizeof(struct kgsl_ibdesc));
+			result = -ENOMEM;
+			goto done;
+		}
+		ibdesc[0].gpuaddr = param->ibdesc_addr;
+		ibdesc[0].sizedwords = param->numibs;
+		param->numibs = 1;
+	}
+
+	result = dev_priv->device->ftbl->issueibcmds(dev_priv,
+					     context,
+					     ibdesc,
+					     param->numibs,
+					     &param->timestamp,
+					     param->flags);
+
+	trace_kgsl_issueibcmds(dev_priv->device, param, ibdesc, result);
+
+free_ibdesc:
+	kfree(ibdesc);
+done:
+
+	return result;
+}
+
+static long _cmdstream_readtimestamp(struct kgsl_device_private *dev_priv,
+		struct kgsl_context *context, unsigned int type,
+		unsigned int *timestamp)
+{
+	*timestamp = kgsl_readtimestamp(dev_priv->device, context, type);
+
+	trace_kgsl_readtimestamp(dev_priv->device,
+			context ? context->id : KGSL_MEMSTORE_GLOBAL,
+			type, *timestamp);
+
+	return 0;
+}
+
+static long kgsl_ioctl_cmdstream_readtimestamp(struct kgsl_device_private
+						*dev_priv, unsigned int cmd,
+						void *data)
+{
+	struct kgsl_cmdstream_readtimestamp *param = data;
+
+	return _cmdstream_readtimestamp(dev_priv, NULL,
+			param->type, &param->timestamp);
+}
+
+static long kgsl_ioctl_cmdstream_readtimestamp_ctxtid(struct kgsl_device_private
+						*dev_priv, unsigned int cmd,
+						void *data)
+{
+	struct kgsl_cmdstream_readtimestamp_ctxtid *param = data;
+	struct kgsl_context *context;
+
+	context = kgsl_find_context(dev_priv, param->context_id);
+	if (context == NULL) {
+		KGSL_DRV_ERR(dev_priv->device, "invalid context_id %d\n",
+			param->context_id);
+		return -EINVAL;
+	}
+
+	return _cmdstream_readtimestamp(dev_priv, context,
+			param->type, &param->timestamp);
+}
+
+static void kgsl_freemem_event_cb(struct kgsl_device *device,
+	void *priv, u32 id, u32 timestamp)
+{
+	struct kgsl_mem_entry *entry = priv;
+	spin_lock(&entry->priv->mem_lock);
+	rb_erase(&entry->node, &entry->priv->mem_rb);
+	spin_unlock(&entry->priv->mem_lock);
+	trace_kgsl_mem_timestamp_free(device, entry, id, timestamp, 0);
+	kgsl_mem_entry_detach_process(entry);
+}
+
+static long _cmdstream_freememontimestamp(struct kgsl_device_private *dev_priv,
+		unsigned int gpuaddr, struct kgsl_context *context,
+		unsigned int timestamp, unsigned int type)
+{
+	int result = 0;
+	struct kgsl_mem_entry *entry = NULL;
+	struct kgsl_device *device = dev_priv->device;
+	unsigned int context_id = context ? context->id : KGSL_MEMSTORE_GLOBAL;
+
+	spin_lock(&dev_priv->process_priv->mem_lock);
+	entry = kgsl_sharedmem_find(dev_priv->process_priv, gpuaddr);
+	spin_unlock(&dev_priv->process_priv->mem_lock);
+
+	if (!entry) {
+		KGSL_DRV_ERR(dev_priv->device,
+				"invalid gpuaddr %08x\n", gpuaddr);
+		result = -EINVAL;
+		goto done;
+	}
+	trace_kgsl_mem_timestamp_queue(device, entry, context_id,
+				       kgsl_readtimestamp(device, context,
+						  KGSL_TIMESTAMP_RETIRED),
+				       timestamp);
+	result = kgsl_add_event(dev_priv->device, context_id, timestamp,
+				kgsl_freemem_event_cb, entry, dev_priv);
+done:
+	return result;
+}
+
+static long kgsl_ioctl_cmdstream_freememontimestamp(struct kgsl_device_private
+						    *dev_priv, unsigned int cmd,
+						    void *data)
+{
+	struct kgsl_cmdstream_freememontimestamp *param = data;
+
+	return _cmdstream_freememontimestamp(dev_priv, param->gpuaddr,
+			NULL, param->timestamp, param->type);
+}
+
+static long kgsl_ioctl_cmdstream_freememontimestamp_ctxtid(
+						struct kgsl_device_private
+						*dev_priv, unsigned int cmd,
+						void *data)
+{
+	struct kgsl_cmdstream_freememontimestamp_ctxtid *param = data;
+	struct kgsl_context *context;
+
+	context = kgsl_find_context(dev_priv, param->context_id);
+	if (context == NULL) {
+		KGSL_DRV_ERR(dev_priv->device,
+			"invalid drawctxt context_id %d\n", param->context_id);
+		return -EINVAL;
+	}
+
+	return _cmdstream_freememontimestamp(dev_priv, param->gpuaddr,
+			context, param->timestamp, param->type);
+}
+
+static long kgsl_ioctl_drawctxt_create(struct kgsl_device_private *dev_priv,
+					unsigned int cmd, void *data)
+{
+	int result = 0;
+	struct kgsl_drawctxt_create *param = data;
+	struct kgsl_context *context = NULL;
+
+	context = kgsl_create_context(dev_priv);
+
+	if (context == NULL) {
+		result = -ENOMEM;
+		goto done;
+	}
+
+	if (dev_priv->device->ftbl->drawctxt_create) {
+		result = dev_priv->device->ftbl->drawctxt_create(
+			dev_priv->device, dev_priv->process_priv->pagetable,
+			context, param->flags);
+		if (result)
+			goto done;
+	}
+	trace_kgsl_context_create(dev_priv->device, context, param->flags);
+	param->drawctxt_id = context->id;
+done:
+	if (result && context)
+		kgsl_context_detach(context);
+
+	return result;
+}
+
+static long kgsl_ioctl_drawctxt_destroy(struct kgsl_device_private *dev_priv,
+					unsigned int cmd, void *data)
+{
+	int result = 0;
+	struct kgsl_drawctxt_destroy *param = data;
+	struct kgsl_context *context;
+
+	context = kgsl_find_context(dev_priv, param->drawctxt_id);
+
+	if (context == NULL) {
+		result = -EINVAL;
+		goto done;
+	}
+
+	kgsl_context_detach(context);
+done:
+	return result;
+}
+
+static long kgsl_ioctl_sharedmem_free(struct kgsl_device_private *dev_priv,
+					unsigned int cmd, void *data)
+{
+	int result = 0;
+	struct kgsl_sharedmem_free *param = data;
+	struct kgsl_process_private *private = dev_priv->process_priv;
+	struct kgsl_mem_entry *entry = NULL;
+
+	spin_lock(&private->mem_lock);
+	entry = kgsl_sharedmem_find(private, param->gpuaddr);
+	if (entry)
+		rb_erase(&entry->node, &private->mem_rb);
+
+	spin_unlock(&private->mem_lock);
+
+	if (entry) {
+		trace_kgsl_mem_free(entry);
+		kgsl_mem_entry_detach_process(entry);
+	} else {
+		KGSL_CORE_ERR("invalid gpuaddr %08x\n", param->gpuaddr);
+		result = -EINVAL;
+	}
+
+	return result;
+}
+
+static struct vm_area_struct *kgsl_get_vma_from_start_addr(unsigned int addr)
+{
+	struct vm_area_struct *vma;
+
+	down_read(&current->mm->mmap_sem);
+	vma = find_vma(current->mm, addr);
+	up_read(&current->mm->mmap_sem);
+	if (!vma)
+		KGSL_CORE_ERR("find_vma(%x) failed\n", addr);
+
+	return vma;
+}
+
+static long
+kgsl_ioctl_sharedmem_from_vmalloc(struct kgsl_device_private *dev_priv,
+				unsigned int cmd, void *data)
+{
+	int result = 0, len = 0;
+	struct kgsl_process_private *private = dev_priv->process_priv;
+	struct kgsl_sharedmem_from_vmalloc *param = data;
+	struct kgsl_mem_entry *entry = NULL;
+	struct vm_area_struct *vma;
+
+	KGSL_DEV_ERR_ONCE(dev_priv->device, "IOCTL_KGSL_SHAREDMEM_FROM_VMALLOC"
+			" is deprecated\n");
+	if (!kgsl_mmu_enabled())
+		return -ENODEV;
+
+	if (!param->hostptr) {
+		KGSL_CORE_ERR("invalid hostptr %x\n", param->hostptr);
+		result = -EINVAL;
+		goto error;
+	}
+
+	vma = kgsl_get_vma_from_start_addr(param->hostptr);
+	if (!vma) {
+		result = -EINVAL;
+		goto error;
+	}
+
+	/*
+	 * If the user specified a length, use it, otherwise try to
+	 * infer the length if the vma region
+	 */
+	if (param->gpuaddr != 0) {
+		len = param->gpuaddr;
+	} else {
+		/*
+		 * For this to work, we have to assume the VMA region is only
+		 * for this single allocation.  If it isn't, then bail out
+		 */
+		if (vma->vm_pgoff || (param->hostptr != vma->vm_start)) {
+			KGSL_CORE_ERR("VMA region does not match hostaddr\n");
+			result = -EINVAL;
+			goto error;
+		}
+
+		len = vma->vm_end - vma->vm_start;
+	}
+
+	/* Make sure it fits */
+	if (len == 0 || param->hostptr + len > vma->vm_end) {
+		KGSL_CORE_ERR("Invalid memory allocation length %d\n", len);
+		result = -EINVAL;
+		goto error;
+	}
+
+	entry = kgsl_mem_entry_create();
+	if (entry == NULL) {
+		result = -ENOMEM;
+		goto error;
+	}
+
+	result = kgsl_sharedmem_page_alloc_user(&entry->memdesc,
+					     private->pagetable, len,
+					     param->flags);
+	if (result != 0)
+		goto error_free_entry;
+
+	vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+	result = kgsl_sharedmem_map_vma(vma, &entry->memdesc);
+	if (result) {
+		KGSL_CORE_ERR("kgsl_sharedmem_map_vma failed: %d\n", result);
+		goto error_free_alloc;
+	}
+
+	param->gpuaddr = entry->memdesc.gpuaddr;
+
+	entry->memtype = KGSL_MEM_ENTRY_KERNEL;
+
+	kgsl_mem_entry_attach_process(entry, private);
+
+	trace_kgsl_mem_alloc(entry);
+	/* Process specific statistics */
+	kgsl_process_add_stats(private, entry->memtype, len);
+
+	kgsl_check_idle(dev_priv->device);
+	return 0;
+
+error_free_alloc:
+	kgsl_sharedmem_free(&entry->memdesc);
+
+error_free_entry:
+	kfree(entry);
+
+error:
+	kgsl_check_idle(dev_priv->device);
+	return result;
+}
+
+static inline int _check_region(unsigned long start, unsigned long size,
+				uint64_t len)
+{
+	uint64_t end = ((uint64_t) start) + size;
+	return (end > len);
+}
+
+static int kgsl_get_phys_file(int fd, unsigned long *start, unsigned long *len,
+			      unsigned long *vstart, struct file **filep)
+{
+	struct file *fbfile;
+	int ret = 0;
+	dev_t rdev;
+	struct fb_info *info;
+
+	*filep = NULL;
+#ifdef CONFIG_ANDROID_PMEM
+	if (!get_pmem_file(fd, start, vstart, len, filep))
+		return 0;
+#endif
+
+	fbfile = fget(fd);
+	if (fbfile == NULL) {
+		KGSL_CORE_ERR("fget_light failed\n");
+		return -1;
+	}
+
+	rdev = fbfile->f_dentry->d_inode->i_rdev;
+	info = MAJOR(rdev) == FB_MAJOR ? registered_fb[MINOR(rdev)] : NULL;
+	if (info) {
+		*start = info->fix.smem_start;
+		*len = info->fix.smem_len;
+		*vstart = (unsigned long)__va(info->fix.smem_start);
+		ret = 0;
+	} else {
+		KGSL_CORE_ERR("framebuffer minor %d not found\n",
+			      MINOR(rdev));
+		ret = -1;
+	}
+
+	fput(fbfile);
+
+	return ret;
+}
+
+static int kgsl_setup_phys_file(struct kgsl_mem_entry *entry,
+				struct kgsl_pagetable *pagetable,
+				unsigned int fd, unsigned int offset,
+				size_t size)
+{
+	int ret;
+	unsigned long phys, virt, len;
+	struct file *filep;
+
+	ret = kgsl_get_phys_file(fd, &phys, &len, &virt, &filep);
+	if (ret)
+		return ret;
+
+	if (phys == 0) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	if (offset >= len) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	if (size == 0)
+		size = len;
+
+	/* Adjust the size of the region to account for the offset */
+	size += offset & ~PAGE_MASK;
+
+	size = ALIGN(size, PAGE_SIZE);
+
+	if (_check_region(offset & PAGE_MASK, size, len)) {
+		KGSL_CORE_ERR("Offset (%ld) + size (%d) is larger"
+			      "than pmem region length %ld\n",
+			      offset & PAGE_MASK, size, len);
+		ret = -EINVAL;
+		goto err;
+
+	}
+
+	entry->priv_data = filep;
+
+	entry->memdesc.pagetable = pagetable;
+	entry->memdesc.size = size;
+	entry->memdesc.physaddr = phys + (offset & PAGE_MASK);
+	entry->memdesc.hostptr = (void *) (virt + (offset & PAGE_MASK));
+
+	ret = memdesc_sg_phys(&entry->memdesc,
+		phys + (offset & PAGE_MASK), size);
+	if (ret)
+		goto err;
+
+	return 0;
+err:
+#ifdef CONFIG_ANDROID_PMEM
+	put_pmem_file(filep);
+#endif
+	return ret;
+}
+
+static int memdesc_sg_virt(struct kgsl_memdesc *memdesc,
+	void *addr, int size)
+{
+	int i;
+	int sglen = PAGE_ALIGN(size) / PAGE_SIZE;
+	unsigned long paddr = (unsigned long) addr;
+
+	memdesc->sg = kgsl_sg_alloc(sglen);
+
+	if (memdesc->sg == NULL)
+		return -ENOMEM;
+
+	memdesc->sglen = sglen;
+	sg_init_table(memdesc->sg, sglen);
+
+	spin_lock(&current->mm->page_table_lock);
+
+	for (i = 0; i < sglen; i++, paddr += PAGE_SIZE) {
+		struct page *page;
+		pmd_t *ppmd;
+		pte_t *ppte;
+		pgd_t *ppgd = pgd_offset(current->mm, paddr);
+
+		if (pgd_none(*ppgd) || pgd_bad(*ppgd))
+			goto err;
+
+		ppmd = pmd_offset(pud_offset(ppgd, paddr), paddr);
+		if (pmd_none(*ppmd) || pmd_bad(*ppmd))
+			goto err;
+
+		ppte = pte_offset_map(ppmd, paddr);
+		if (ppte == NULL)
+			goto err;
+
+		page = pfn_to_page(pte_pfn(*ppte));
+		if (!page)
+			goto err;
+
+		sg_set_page(&memdesc->sg[i], page, PAGE_SIZE, 0);
+		pte_unmap(ppte);
+	}
+
+	spin_unlock(&current->mm->page_table_lock);
+
+	return 0;
+
+err:
+	spin_unlock(&current->mm->page_table_lock);
+	kgsl_sg_free(memdesc->sg,  sglen);
+	memdesc->sg = NULL;
+
+	return -EINVAL;
+}
+
+static int kgsl_setup_hostptr(struct kgsl_mem_entry *entry,
+			      struct kgsl_pagetable *pagetable,
+			      void *hostptr, unsigned int offset,
+			      size_t size)
+{
+	struct vm_area_struct *vma;
+	unsigned int len;
+
+	down_read(&current->mm->mmap_sem);
+	vma = find_vma(current->mm, (unsigned int) hostptr);
+	up_read(&current->mm->mmap_sem);
+
+	if (!vma) {
+		KGSL_CORE_ERR("find_vma(%p) failed\n", hostptr);
+		return -EINVAL;
+	}
+
+	/* We don't necessarily start at vma->vm_start */
+	len = vma->vm_end - (unsigned long) hostptr;
+
+	if (offset >= len)
+		return -EINVAL;
+
+	if (!KGSL_IS_PAGE_ALIGNED((unsigned long) hostptr) ||
+	    !KGSL_IS_PAGE_ALIGNED(len)) {
+		KGSL_CORE_ERR("user address len(%u)"
+			      "and start(%p) must be page"
+			      "aligned\n", len, hostptr);
+		return -EINVAL;
+	}
+
+	if (size == 0)
+		size = len;
+
+	/* Adjust the size of the region to account for the offset */
+	size += offset & ~PAGE_MASK;
+
+	size = ALIGN(size, PAGE_SIZE);
+
+	if (_check_region(offset & PAGE_MASK, size, len)) {
+		KGSL_CORE_ERR("Offset (%ld) + size (%d) is larger"
+			      "than region length %d\n",
+			      offset & PAGE_MASK, size, len);
+		return -EINVAL;
+	}
+
+	entry->memdesc.pagetable = pagetable;
+	entry->memdesc.size = size;
+	entry->memdesc.hostptr = hostptr + (offset & PAGE_MASK);
+
+	return memdesc_sg_virt(&entry->memdesc,
+		hostptr + (offset & PAGE_MASK), size);
+}
+
+#ifdef CONFIG_ASHMEM
+static int kgsl_setup_ashmem(struct kgsl_mem_entry *entry,
+			     struct kgsl_pagetable *pagetable,
+			     int fd, void *hostptr, size_t size)
+{
+	int ret;
+	struct vm_area_struct *vma;
+	struct file *filep, *vmfile;
+	unsigned long len;
+	unsigned int hostaddr = (unsigned int) hostptr;
+
+	vma = kgsl_get_vma_from_start_addr(hostaddr);
+	if (vma == NULL)
+		return -EINVAL;
+
+	if (vma->vm_pgoff || vma->vm_start != hostaddr) {
+		KGSL_CORE_ERR("Invalid vma region\n");
+		return -EINVAL;
+	}
+
+	len = vma->vm_end - vma->vm_start;
+
+	if (size == 0)
+		size = len;
+
+	if (size != len) {
+		KGSL_CORE_ERR("Invalid size %d for vma region %p\n",
+			      size, hostptr);
+		return -EINVAL;
+	}
+
+	ret = get_ashmem_file(fd, &filep, &vmfile, &len);
+
+	if (ret) {
+		KGSL_CORE_ERR("get_ashmem_file failed\n");
+		return ret;
+	}
+
+	if (vmfile != vma->vm_file) {
+		KGSL_CORE_ERR("ashmem shmem file does not match vma\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	entry->priv_data = filep;
+	entry->memdesc.pagetable = pagetable;
+	entry->memdesc.size = ALIGN(size, PAGE_SIZE);
+	entry->memdesc.hostptr = hostptr;
+
+	ret = memdesc_sg_virt(&entry->memdesc, hostptr, size);
+	if (ret)
+		goto err;
+
+	return 0;
+
+err:
+	put_ashmem_file(filep);
+	return ret;
+}
+#else
+static int kgsl_setup_ashmem(struct kgsl_mem_entry *entry,
+			     struct kgsl_pagetable *pagetable,
+			     int fd, void *hostptr, size_t size)
+{
+	return -EINVAL;
+}
+#endif
+
+static int kgsl_setup_ion(struct kgsl_mem_entry *entry,
+		struct kgsl_pagetable *pagetable, int fd)
+{
+	struct ion_handle *handle;
+	struct scatterlist *s;
+	unsigned long flags;
+
+	if (IS_ERR_OR_NULL(kgsl_ion_client))
+		return -ENODEV;
+
+	handle = ion_import_fd(kgsl_ion_client, fd);
+	if (IS_ERR_OR_NULL(handle))
+		return PTR_ERR(handle);
+
+	entry->memtype = KGSL_MEM_ENTRY_ION;
+	entry->priv_data = handle;
+	entry->memdesc.pagetable = pagetable;
+	entry->memdesc.size = 0;
+
+	if (ion_handle_get_flags(kgsl_ion_client, handle, &flags))
+		goto err;
+
+	entry->memdesc.sg = ion_map_dma(kgsl_ion_client, handle, flags);
+
+	if (IS_ERR_OR_NULL(entry->memdesc.sg))
+		goto err;
+
+	/* Calculate the size of the memdesc from the sglist */
+
+	entry->memdesc.sglen = 0;
+
+	for (s = entry->memdesc.sg; s != NULL; s = sg_next(s)) {
+		entry->memdesc.size += s->length;
+		entry->memdesc.sglen++;
+	}
+
+	return 0;
+err:
+	ion_free(kgsl_ion_client, handle);
+	return -ENOMEM;
+}
+
+static long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv,
+				     unsigned int cmd, void *data)
+{
+	int result = -EINVAL;
+	struct kgsl_map_user_mem *param = data;
+	struct kgsl_mem_entry *entry = NULL;
+	struct kgsl_process_private *private = dev_priv->process_priv;
+	enum kgsl_user_mem_type memtype;
+
+	entry = kgsl_mem_entry_create();
+
+	if (entry == NULL)
+		return -ENOMEM;
+
+	if (_IOC_SIZE(cmd) == sizeof(struct kgsl_sharedmem_from_pmem))
+		memtype = KGSL_USER_MEM_TYPE_PMEM;
+	else
+		memtype = param->memtype;
+
+	switch (memtype) {
+	case KGSL_USER_MEM_TYPE_PMEM:
+		if (param->fd == 0 || param->len == 0)
+			break;
+
+		result = kgsl_setup_phys_file(entry, private->pagetable,
+					      param->fd, param->offset,
+					      param->len);
+		entry->memtype = KGSL_MEM_ENTRY_PMEM;
+		break;
+
+	case KGSL_USER_MEM_TYPE_ADDR:
+		KGSL_DEV_ERR_ONCE(dev_priv->device, "User mem type "
+				"KGSL_USER_MEM_TYPE_ADDR is deprecated\n");
+		if (!kgsl_mmu_enabled()) {
+			KGSL_DRV_ERR(dev_priv->device,
+				"Cannot map paged memory with the "
+				"MMU disabled\n");
+			break;
+		}
+
+		if (param->hostptr == 0)
+			break;
+
+		result = kgsl_setup_hostptr(entry, private->pagetable,
+					    (void *) param->hostptr,
+					    param->offset, param->len);
+		entry->memtype = KGSL_MEM_ENTRY_USER;
+		break;
+
+	case KGSL_USER_MEM_TYPE_ASHMEM:
+		if (!kgsl_mmu_enabled()) {
+			KGSL_DRV_ERR(dev_priv->device,
+				"Cannot map paged memory with the "
+				"MMU disabled\n");
+			break;
+		}
+
+		if (param->hostptr == 0)
+			break;
+
+		result = kgsl_setup_ashmem(entry, private->pagetable,
+					   param->fd, (void *) param->hostptr,
+					   param->len);
+
+		entry->memtype = KGSL_MEM_ENTRY_ASHMEM;
+		break;
+	case KGSL_USER_MEM_TYPE_ION:
+		result = kgsl_setup_ion(entry, private->pagetable,
+			param->fd);
+		break;
+	default:
+		KGSL_CORE_ERR("Invalid memory type: %x\n", memtype);
+		break;
+	}
+
+	if (result)
+		goto error;
+
+	result = kgsl_mmu_map(private->pagetable,
+			      &entry->memdesc,
+			      GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
+
+	if (result)
+		goto error_put_file_ptr;
+
+	/* Adjust the returned value for a non 4k aligned offset */
+	param->gpuaddr = entry->memdesc.gpuaddr + (param->offset & ~PAGE_MASK);
+
+	KGSL_STATS_ADD(param->len, kgsl_driver.stats.mapped,
+		kgsl_driver.stats.mapped_max);
+
+	kgsl_process_add_stats(private, entry->memtype, param->len);
+
+	kgsl_mem_entry_attach_process(entry, private);
+	trace_kgsl_mem_map(entry, param->fd);
+
+	kgsl_check_idle(dev_priv->device);
+	return result;
+
+error_put_file_ptr:
+	switch (entry->memtype) {
+	case KGSL_MEM_ENTRY_PMEM:
+	case KGSL_MEM_ENTRY_ASHMEM:
+		if (entry->priv_data)
+			fput(entry->priv_data);
+		break;
+	case KGSL_MEM_ENTRY_ION:
+		ion_unmap_dma(kgsl_ion_client, entry->priv_data);
+		ion_free(kgsl_ion_client, entry->priv_data);
+		break;
+	default:
+		break;
+	}
+error:
+	kfree(entry);
+	kgsl_check_idle(dev_priv->device);
+	return result;
+}
+
+/*This function flushes a graphics memory allocation from CPU cache
+ *when caching is enabled with MMU*/
+static long
+kgsl_ioctl_sharedmem_flush_cache(struct kgsl_device_private *dev_priv,
+				 unsigned int cmd, void *data)
+{
+	int result = 0;
+	struct kgsl_mem_entry *entry;
+	struct kgsl_sharedmem_free *param = data;
+	struct kgsl_process_private *private = dev_priv->process_priv;
+
+	spin_lock(&private->mem_lock);
+	entry = kgsl_sharedmem_find(private, param->gpuaddr);
+	if (!entry) {
+		KGSL_CORE_ERR("invalid gpuaddr %08x\n", param->gpuaddr);
+		result = -EINVAL;
+		goto done;
+	}
+	if (!entry->memdesc.hostptr) {
+		KGSL_CORE_ERR("invalid hostptr with gpuaddr %08x\n",
+			param->gpuaddr);
+			goto done;
+	}
+
+	kgsl_cache_range_op(&entry->memdesc, KGSL_CACHE_OP_CLEAN);
+done:
+	spin_unlock(&private->mem_lock);
+	return result;
+}
+
+static long
+kgsl_ioctl_gpumem_alloc(struct kgsl_device_private *dev_priv,
+			unsigned int cmd, void *data)
+{
+	struct kgsl_process_private *private = dev_priv->process_priv;
+	struct kgsl_gpumem_alloc *param = data;
+	struct kgsl_mem_entry *entry;
+	int result;
+
+	entry = kgsl_mem_entry_create();
+	if (entry == NULL)
+		return -ENOMEM;
+
+	result = kgsl_allocate_user(&entry->memdesc, private->pagetable,
+		param->size, param->flags);
+
+	if (result == 0) {
+		entry->memtype = KGSL_MEM_ENTRY_KERNEL;
+		kgsl_mem_entry_attach_process(entry, private);
+		param->gpuaddr = entry->memdesc.gpuaddr;
+
+		kgsl_process_add_stats(private, entry->memtype, param->size);
+		trace_kgsl_mem_alloc(entry);
+	} else
+		kfree(entry);
+
+	kgsl_check_idle(dev_priv->device);
+	return result;
+}
+static long kgsl_ioctl_cff_syncmem(struct kgsl_device_private *dev_priv,
+					unsigned int cmd, void *data)
+{
+	int result = 0;
+	struct kgsl_cff_syncmem *param = data;
+	struct kgsl_process_private *private = dev_priv->process_priv;
+	struct kgsl_mem_entry *entry = NULL;
+
+	spin_lock(&private->mem_lock);
+	entry = kgsl_sharedmem_find_region(private, param->gpuaddr, param->len);
+	if (entry)
+		kgsl_cffdump_syncmem(dev_priv, &entry->memdesc, param->gpuaddr,
+				     param->len, true);
+	else
+		result = -EINVAL;
+	spin_unlock(&private->mem_lock);
+	return result;
+}
+
+static long kgsl_ioctl_cff_user_event(struct kgsl_device_private *dev_priv,
+		unsigned int cmd, void *data)
+{
+	int result = 0;
+	struct kgsl_cff_user_event *param = data;
+
+	kgsl_cffdump_user_event(param->cff_opcode, param->op1, param->op2,
+			param->op3, param->op4, param->op5);
+
+	return result;
+}
+
+#ifdef CONFIG_GENLOCK
+struct kgsl_genlock_event_priv {
+	struct genlock_handle *handle;
+	struct genlock *lock;
+};
+
+/**
+ * kgsl_genlock_event_cb - Event callback for a genlock timestamp event
+ * @device - The KGSL device that expired the timestamp
+ * @priv - private data for the event
+ * @context_id - the context id that goes with the timestamp
+ * @timestamp - the timestamp that triggered the event
+ *
+ * Release a genlock lock following the expiration of a timestamp
+ */
+
+static void kgsl_genlock_event_cb(struct kgsl_device *device,
+	void *priv, u32 context_id, u32 timestamp)
+{
+	struct kgsl_genlock_event_priv *ev = priv;
+	int ret;
+
+	ret = genlock_lock(ev->handle, GENLOCK_UNLOCK, 0, 0);
+	if (ret)
+		KGSL_CORE_ERR("Error while unlocking genlock: %d\n", ret);
+
+	genlock_put_handle(ev->handle);
+
+	kfree(ev);
+}
+
+/**
+ * kgsl_add_genlock-event - Create a new genlock event
+ * @device - KGSL device to create the event on
+ * @timestamp - Timestamp to trigger the event
+ * @data - User space buffer containing struct kgsl_genlock_event_priv
+ * @len - length of the userspace buffer
+ * @owner - driver instance that owns this event
+ * @returns 0 on success or error code on error
+ *
+ * Attack to a genlock handle and register an event to release the
+ * genlock lock when the timestamp expires
+ */
+
+static int kgsl_add_genlock_event(struct kgsl_device *device,
+	u32 context_id, u32 timestamp, void __user *data, int len,
+	struct kgsl_device_private *owner)
+{
+	struct kgsl_genlock_event_priv *event;
+	struct kgsl_timestamp_event_genlock priv;
+	int ret;
+
+	if (len !=  sizeof(priv))
+		return -EINVAL;
+
+	if (copy_from_user(&priv, data, sizeof(priv)))
+		return -EFAULT;
+
+	event = kzalloc(sizeof(*event), GFP_KERNEL);
+
+	if (event == NULL)
+		return -ENOMEM;
+
+	event->handle = genlock_get_handle_fd(priv.handle);
+
+	if (IS_ERR(event->handle)) {
+		int ret = PTR_ERR(event->handle);
+		kfree(event);
+		return ret;
+	}
+
+	ret = kgsl_add_event(device, context_id, timestamp,
+			kgsl_genlock_event_cb, event, owner);
+	if (ret)
+		kfree(event);
+
+	return ret;
+}
+#else
+static long kgsl_add_genlock_event(struct kgsl_device *device,
+	u32 context_id, u32 timestamp, void __user *data, int len,
+	struct kgsl_device_private *owner)
+{
+	return -EINVAL;
+}
+#endif
+
+/**
+ * kgsl_ioctl_timestamp_event - Register a new timestamp event from userspace
+ * @dev_priv - pointer to the private device structure
+ * @cmd - the ioctl cmd passed from kgsl_ioctl
+ * @data - the user data buffer from kgsl_ioctl
+ * @returns 0 on success or error code on failure
+ */
+
+static long kgsl_ioctl_timestamp_event(struct kgsl_device_private *dev_priv,
+		unsigned int cmd, void *data)
+{
+	struct kgsl_timestamp_event *param = data;
+	int ret;
+
+	switch (param->type) {
+	case KGSL_TIMESTAMP_EVENT_GENLOCK:
+		ret = kgsl_add_genlock_event(dev_priv->device,
+			param->context_id, param->timestamp, param->priv,
+			param->len, dev_priv);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+typedef long (*kgsl_ioctl_func_t)(struct kgsl_device_private *,
+	unsigned int, void *);
+
+#define KGSL_IOCTL_FUNC(_cmd, _func, _lock) \
+	[_IOC_NR(_cmd)] = { .cmd = _cmd, .func = _func, .lock = _lock }
+
+static const struct {
+	unsigned int cmd;
+	kgsl_ioctl_func_t func;
+	int lock;
+} kgsl_ioctl_funcs[] = {
+	KGSL_IOCTL_FUNC(IOCTL_KGSL_DEVICE_GETPROPERTY,
+			kgsl_ioctl_device_getproperty, 1),
+	KGSL_IOCTL_FUNC(IOCTL_KGSL_DEVICE_WAITTIMESTAMP,
+			kgsl_ioctl_device_waittimestamp, 1),
+	KGSL_IOCTL_FUNC(IOCTL_KGSL_DEVICE_WAITTIMESTAMP_CTXTID,
+			kgsl_ioctl_device_waittimestamp_ctxtid, 1),
+	KGSL_IOCTL_FUNC(IOCTL_KGSL_RINGBUFFER_ISSUEIBCMDS,
+			kgsl_ioctl_rb_issueibcmds, 1),
+	KGSL_IOCTL_FUNC(IOCTL_KGSL_CMDSTREAM_READTIMESTAMP,
+			kgsl_ioctl_cmdstream_readtimestamp, 1),
+	KGSL_IOCTL_FUNC(IOCTL_KGSL_CMDSTREAM_READTIMESTAMP_CTXTID,
+			kgsl_ioctl_cmdstream_readtimestamp_ctxtid, 1),
+	KGSL_IOCTL_FUNC(IOCTL_KGSL_CMDSTREAM_FREEMEMONTIMESTAMP,
+			kgsl_ioctl_cmdstream_freememontimestamp, 1),
+	KGSL_IOCTL_FUNC(IOCTL_KGSL_CMDSTREAM_FREEMEMONTIMESTAMP_CTXTID,
+			kgsl_ioctl_cmdstream_freememontimestamp_ctxtid, 1),
+	KGSL_IOCTL_FUNC(IOCTL_KGSL_DRAWCTXT_CREATE,
+			kgsl_ioctl_drawctxt_create, 1),
+	KGSL_IOCTL_FUNC(IOCTL_KGSL_DRAWCTXT_DESTROY,
+			kgsl_ioctl_drawctxt_destroy, 1),
+	KGSL_IOCTL_FUNC(IOCTL_KGSL_MAP_USER_MEM,
+			kgsl_ioctl_map_user_mem, 0),
+	KGSL_IOCTL_FUNC(IOCTL_KGSL_SHAREDMEM_FROM_PMEM,
+			kgsl_ioctl_map_user_mem, 0),
+	KGSL_IOCTL_FUNC(IOCTL_KGSL_SHAREDMEM_FREE,
+			kgsl_ioctl_sharedmem_free, 0),
+	KGSL_IOCTL_FUNC(IOCTL_KGSL_SHAREDMEM_FROM_VMALLOC,
+			kgsl_ioctl_sharedmem_from_vmalloc, 0),
+	KGSL_IOCTL_FUNC(IOCTL_KGSL_SHAREDMEM_FLUSH_CACHE,
+			kgsl_ioctl_sharedmem_flush_cache, 0),
+	KGSL_IOCTL_FUNC(IOCTL_KGSL_GPUMEM_ALLOC,
+			kgsl_ioctl_gpumem_alloc, 0),
+	KGSL_IOCTL_FUNC(IOCTL_KGSL_CFF_SYNCMEM,
+			kgsl_ioctl_cff_syncmem, 0),
+	KGSL_IOCTL_FUNC(IOCTL_KGSL_CFF_USER_EVENT,
+			kgsl_ioctl_cff_user_event, 0),
+	KGSL_IOCTL_FUNC(IOCTL_KGSL_TIMESTAMP_EVENT,
+			kgsl_ioctl_timestamp_event, 1),
+	KGSL_IOCTL_FUNC(IOCTL_KGSL_SETPROPERTY,
+			kgsl_ioctl_device_setproperty, 1),
+};
+
+static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
+{
+	struct kgsl_device_private *dev_priv = filep->private_data;
+	unsigned int nr = _IOC_NR(cmd);
+	kgsl_ioctl_func_t func;
+	int lock, ret;
+	char ustack[64];
+	void *uptr = NULL;
+
+	BUG_ON(dev_priv == NULL);
+
+	/* Workaround for an previously incorrectly defined ioctl code.
+	   This helps ensure binary compatability */
+
+	if (cmd == IOCTL_KGSL_CMDSTREAM_FREEMEMONTIMESTAMP_OLD)
+		cmd = IOCTL_KGSL_CMDSTREAM_FREEMEMONTIMESTAMP;
+	else if (cmd == IOCTL_KGSL_CMDSTREAM_READTIMESTAMP_OLD)
+		cmd = IOCTL_KGSL_CMDSTREAM_READTIMESTAMP;
+
+	if (cmd & (IOC_IN | IOC_OUT)) {
+		if (_IOC_SIZE(cmd) < sizeof(ustack))
+			uptr = ustack;
+		else {
+			uptr = kzalloc(_IOC_SIZE(cmd), GFP_KERNEL);
+			if (uptr == NULL) {
+				KGSL_MEM_ERR(dev_priv->device,
+					"kzalloc(%d) failed\n", _IOC_SIZE(cmd));
+				ret = -ENOMEM;
+				goto done;
+			}
+		}
+
+		if (cmd & IOC_IN) {
+			if (copy_from_user(uptr, (void __user *) arg,
+				_IOC_SIZE(cmd))) {
+				ret = -EFAULT;
+				goto done;
+			}
+		} else
+			memset(uptr, 0, _IOC_SIZE(cmd));
+	}
+
+	if (nr < ARRAY_SIZE(kgsl_ioctl_funcs) &&
+	    kgsl_ioctl_funcs[nr].func != NULL) {
+		func = kgsl_ioctl_funcs[nr].func;
+		lock = kgsl_ioctl_funcs[nr].lock;
+	} else {
+		func = dev_priv->device->ftbl->ioctl;
+		if (!func) {
+			KGSL_DRV_INFO(dev_priv->device,
+				      "invalid ioctl code %08x\n", cmd);
+			ret = -ENOIOCTLCMD;
+			goto done;
+		}
+		lock = 1;
+	}
+
+	if (lock) {
+		mutex_lock(&dev_priv->device->mutex);
+		kgsl_check_suspended(dev_priv->device);
+	}
+
+	ret = func(dev_priv, cmd, uptr);
+
+	if (lock) {
+		kgsl_check_idle_locked(dev_priv->device);
+		mutex_unlock(&dev_priv->device->mutex);
+	}
+
+	if (ret == 0 && (cmd & IOC_OUT)) {
+		if (copy_to_user((void __user *) arg, uptr, _IOC_SIZE(cmd)))
+			ret = -EFAULT;
+	}
+
+done:
+	if (_IOC_SIZE(cmd) >= sizeof(ustack))
+		kfree(uptr);
+
+	return ret;
+}
+
+static int
+kgsl_mmap_memstore(struct kgsl_device *device, struct vm_area_struct *vma)
+{
+	struct kgsl_memdesc *memdesc = &device->memstore;
+	int result;
+	unsigned int vma_size = vma->vm_end - vma->vm_start;
+
+	/* The memstore can only be mapped as read only */
+
+	if (vma->vm_flags & VM_WRITE)
+		return -EPERM;
+
+	if (memdesc->size  !=  vma_size) {
+		KGSL_MEM_ERR(device, "memstore bad size: %d should be %d\n",
+			     vma_size, memdesc->size);
+		return -EINVAL;
+	}
+
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+	result = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+				 vma_size, vma->vm_page_prot);
+	if (result != 0)
+		KGSL_MEM_ERR(device, "remap_pfn_range failed: %d\n",
+			     result);
+
+	return result;
+}
+
+/*
+ * kgsl_gpumem_vm_open is called whenever a vma region is copied or split.
+ * Increase the refcount to make sure that the accounting stays correct
+ */
+
+static void kgsl_gpumem_vm_open(struct vm_area_struct *vma)
+{
+	struct kgsl_mem_entry *entry = vma->vm_private_data;
+	kgsl_mem_entry_get(entry);
+}
+
+static int
+kgsl_gpumem_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+	struct kgsl_mem_entry *entry = vma->vm_private_data;
+
+	if (!entry->memdesc.ops || !entry->memdesc.ops->vmfault)
+		return VM_FAULT_SIGBUS;
+
+	return entry->memdesc.ops->vmfault(&entry->memdesc, vma, vmf);
+}
+
+static void
+kgsl_gpumem_vm_close(struct vm_area_struct *vma)
+{
+	struct kgsl_mem_entry *entry  = vma->vm_private_data;
+	kgsl_mem_entry_put(entry);
+}
+
+static struct vm_operations_struct kgsl_gpumem_vm_ops = {
+	.open  = kgsl_gpumem_vm_open,
+	.fault = kgsl_gpumem_vm_fault,
+	.close = kgsl_gpumem_vm_close,
+};
+
+static int kgsl_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	unsigned long vma_offset = vma->vm_pgoff << PAGE_SHIFT;
+	struct kgsl_device_private *dev_priv = file->private_data;
+	struct kgsl_process_private *private = dev_priv->process_priv;
+	struct kgsl_mem_entry *entry = NULL;
+	struct kgsl_device *device = dev_priv->device;
+
+	/* Handle leagacy behavior for memstore */
+
+	if (vma_offset == device->memstore.physaddr)
+		return kgsl_mmap_memstore(device, vma);
+
+	/* Find a chunk of GPU memory */
+
+	spin_lock(&private->mem_lock);
+	entry = kgsl_sharedmem_find(private, vma_offset);
+
+	if (entry)
+		kgsl_mem_entry_get(entry);
+
+	spin_unlock(&private->mem_lock);
+
+	if (entry == NULL)
+		return -EINVAL;
+
+	if (!entry->memdesc.ops ||
+		!entry->memdesc.ops->vmflags ||
+		!entry->memdesc.ops->vmfault)
+		return -EINVAL;
+
+	vma->vm_flags |= entry->memdesc.ops->vmflags(&entry->memdesc);
+
+	vma->vm_private_data = entry;
+	vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+	vma->vm_ops = &kgsl_gpumem_vm_ops;
+	vma->vm_file = file;
+
+	return 0;
+}
+
+static irqreturn_t kgsl_irq_handler(int irq, void *data)
+{
+	struct kgsl_device *device = data;
+
+	return device->ftbl->irq_handler(device);
+
+}
+
+static const struct file_operations kgsl_fops = {
+	.owner = THIS_MODULE,
+	.release = kgsl_release,
+	.open = kgsl_open,
+	.mmap = kgsl_mmap,
+	.unlocked_ioctl = kgsl_ioctl,
+};
+
+struct kgsl_driver kgsl_driver  = {
+	.process_mutex = __MUTEX_INITIALIZER(kgsl_driver.process_mutex),
+	.ptlock = __SPIN_LOCK_UNLOCKED(kgsl_driver.ptlock),
+	.devlock = __MUTEX_INITIALIZER(kgsl_driver.devlock),
+};
+EXPORT_SYMBOL(kgsl_driver);
+
+static void _unregister_device(struct kgsl_device *device)
+{
+	int minor;
+
+	mutex_lock(&kgsl_driver.devlock);
+	for (minor = 0; minor < KGSL_DEVICE_MAX; minor++) {
+		if (device == kgsl_driver.devp[minor])
+			break;
+	}
+	if (minor != KGSL_DEVICE_MAX) {
+		device_destroy(kgsl_driver.class,
+				MKDEV(MAJOR(kgsl_driver.major), minor));
+		kgsl_driver.devp[minor] = NULL;
+	}
+	mutex_unlock(&kgsl_driver.devlock);
+}
+
+static int _register_device(struct kgsl_device *device)
+{
+	int minor, ret;
+	dev_t dev;
+
+	/* Find a minor for the device */
+
+	mutex_lock(&kgsl_driver.devlock);
+	for (minor = 0; minor < KGSL_DEVICE_MAX; minor++) {
+		if (kgsl_driver.devp[minor] == NULL) {
+			kgsl_driver.devp[minor] = device;
+			break;
+		}
+	}
+	mutex_unlock(&kgsl_driver.devlock);
+
+	if (minor == KGSL_DEVICE_MAX) {
+		KGSL_CORE_ERR("minor devices exhausted\n");
+		return -ENODEV;
+	}
+
+	/* Create the device */
+	dev = MKDEV(MAJOR(kgsl_driver.major), minor);
+	device->dev = device_create(kgsl_driver.class,
+				    device->parentdev,
+				    dev, device,
+				    device->name);
+
+	if (IS_ERR(device->dev)) {
+		mutex_lock(&kgsl_driver.devlock);
+		kgsl_driver.devp[minor] = NULL;
+		mutex_unlock(&kgsl_driver.devlock);
+		ret = PTR_ERR(device->dev);
+		KGSL_CORE_ERR("device_create(%s): %d\n", device->name, ret);
+		return ret;
+	}
+
+	dev_set_drvdata(device->parentdev, device);
+	return 0;
+}
+
+int kgsl_device_platform_probe(struct kgsl_device *device)
+{
+	int result;
+	int status = -EINVAL;
+	struct resource *res;
+	struct platform_device *pdev =
+		container_of(device->parentdev, struct platform_device, dev);
+
+	status = _register_device(device);
+	if (status)
+		return status;
+
+	/* Initialize logging first, so that failures below actually print. */
+	kgsl_device_debugfs_init(device);
+
+	status = kgsl_pwrctrl_init(device);
+	if (status)
+		goto error;
+
+	kgsl_ion_client = msm_ion_client_create(UINT_MAX, KGSL_NAME);
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+					   device->iomemname);
+	if (res == NULL) {
+		KGSL_DRV_ERR(device, "platform_get_resource_byname failed\n");
+		status = -EINVAL;
+		goto error_pwrctrl_close;
+	}
+	if (res->start == 0 || resource_size(res) == 0) {
+		KGSL_DRV_ERR(device, "dev %d invalid register region\n",
+			device->id);
+		status = -EINVAL;
+		goto error_pwrctrl_close;
+	}
+
+	device->reg_phys = res->start;
+	device->reg_len = resource_size(res);
+
+	if (!devm_request_mem_region(device->dev, device->reg_phys,
+				device->reg_len, device->name)) {
+		KGSL_DRV_ERR(device, "request_mem_region failed\n");
+		status = -ENODEV;
+		goto error_pwrctrl_close;
+	}
+
+	device->reg_virt = devm_ioremap(device->dev, device->reg_phys,
+					device->reg_len);
+
+	if (device->reg_virt == NULL) {
+		KGSL_DRV_ERR(device, "ioremap failed\n");
+		status = -ENODEV;
+		goto error_pwrctrl_close;
+	}
+	/*acquire interrupt */
+	device->pwrctrl.interrupt_num =
+		platform_get_irq_byname(pdev, device->pwrctrl.irq_name);
+
+	if (device->pwrctrl.interrupt_num <= 0) {
+		KGSL_DRV_ERR(device, "platform_get_irq_byname failed: %d\n",
+					 device->pwrctrl.interrupt_num);
+		status = -EINVAL;
+		goto error_pwrctrl_close;
+	}
+
+	status = devm_request_irq(device->dev, device->pwrctrl.interrupt_num,
+				  kgsl_irq_handler, IRQF_TRIGGER_HIGH,
+				  device->name, device);
+	if (status) {
+		KGSL_DRV_ERR(device, "request_irq(%d) failed: %d\n",
+			      device->pwrctrl.interrupt_num, status);
+		goto error_pwrctrl_close;
+	}
+	disable_irq(device->pwrctrl.interrupt_num);
+
+	KGSL_DRV_INFO(device,
+		"dev_id %d regs phys 0x%08lx size 0x%08x virt %p\n",
+		device->id, device->reg_phys, device->reg_len,
+		device->reg_virt);
+
+	result = kgsl_drm_init(pdev);
+	if (result)
+		goto error_pwrctrl_close;
+
+	kgsl_cffdump_open(device->id);
+
+	setup_timer(&device->idle_timer, kgsl_timer, (unsigned long) device);
+	status = kgsl_create_device_workqueue(device);
+	if (status)
+		goto error_pwrctrl_close;
+
+	status = kgsl_mmu_init(device);
+	if (status != 0) {
+		KGSL_DRV_ERR(device, "kgsl_mmu_init failed %d\n", status);
+		goto error_dest_work_q;
+	}
+
+	status = kgsl_allocate_contiguous(&device->memstore,
+		sizeof(struct kgsl_devmemstore));
+
+	if (status != 0) {
+		KGSL_DRV_ERR(device, "kgsl_allocate_contiguous failed %d\n",
+				status);
+		goto error_close_mmu;
+	}
+
+	wake_lock_init(&device->idle_wakelock, WAKE_LOCK_IDLE, device->name);
+	pm_qos_add_request(&device->pm_qos_req_dma, PM_QOS_CPU_DMA_LATENCY,
+				PM_QOS_DEFAULT_VALUE);
+
+	/* Initalize the snapshot engine */
+	kgsl_device_snapshot_init(device);
+
+	/* Initialize common sysfs entries */
+	kgsl_pwrctrl_init_sysfs(device);
+
+	return 0;
+
+error_close_mmu:
+	kgsl_mmu_close(device);
+error_dest_work_q:
+	destroy_workqueue(device->work_queue);
+	device->work_queue = NULL;
+error_pwrctrl_close:
+	kgsl_pwrctrl_close(device);
+error:
+	_unregister_device(device);
+	return status;
+}
+EXPORT_SYMBOL(kgsl_device_platform_probe);
+
+void kgsl_device_platform_remove(struct kgsl_device *device)
+{
+	kgsl_device_snapshot_close(device);
+
+	kgsl_cffdump_close(device->id);
+	kgsl_pwrctrl_uninit_sysfs(device);
+
+	wake_lock_destroy(&device->idle_wakelock);
+	pm_qos_remove_request(&device->pm_qos_req_dma);
+
+	idr_destroy(&device->context_idr);
+
+	kgsl_sharedmem_free(&device->memstore);
+
+	kgsl_mmu_close(device);
+
+	if (device->work_queue) {
+		destroy_workqueue(device->work_queue);
+		device->work_queue = NULL;
+	}
+	kgsl_pwrctrl_close(device);
+
+	_unregister_device(device);
+}
+EXPORT_SYMBOL(kgsl_device_platform_remove);
+
+static int __devinit
+kgsl_ptdata_init(void)
+{
+	kgsl_driver.ptpool = kgsl_mmu_ptpool_init(kgsl_pagetable_count);
+
+	if (!kgsl_driver.ptpool)
+		return -ENOMEM;
+	return 0;
+}
+
+static void kgsl_core_exit(void)
+{
+	kgsl_mmu_ptpool_destroy(kgsl_driver.ptpool);
+	kgsl_driver.ptpool = NULL;
+
+	kgsl_drm_exit();
+	kgsl_cffdump_destroy();
+	kgsl_core_debugfs_close();
+	kgsl_sharedmem_uninit_sysfs();
+
+	device_unregister(&kgsl_driver.virtdev);
+
+	if (kgsl_driver.class) {
+		class_destroy(kgsl_driver.class);
+		kgsl_driver.class = NULL;
+	}
+
+	unregister_chrdev_region(kgsl_driver.major, KGSL_DEVICE_MAX);
+}
+
+static int __init kgsl_core_init(void)
+{
+	int result = 0;
+	/* alloc major and minor device numbers */
+	result = alloc_chrdev_region(&kgsl_driver.major, 0, KGSL_DEVICE_MAX,
+				  KGSL_NAME);
+	if (result < 0) {
+		KGSL_CORE_ERR("alloc_chrdev_region failed err = %d\n", result);
+		goto err;
+	}
+
+	cdev_init(&kgsl_driver.cdev, &kgsl_fops);
+	kgsl_driver.cdev.owner = THIS_MODULE;
+	kgsl_driver.cdev.ops = &kgsl_fops;
+	result = cdev_add(&kgsl_driver.cdev, MKDEV(MAJOR(kgsl_driver.major), 0),
+		       KGSL_DEVICE_MAX);
+
+	if (result) {
+		KGSL_CORE_ERR("kgsl: cdev_add() failed, dev_num= %d,"
+			     " result= %d\n", kgsl_driver.major, result);
+		goto err;
+	}
+
+	kgsl_driver.class = class_create(THIS_MODULE, KGSL_NAME);
+
+	if (IS_ERR(kgsl_driver.class)) {
+		result = PTR_ERR(kgsl_driver.class);
+		KGSL_CORE_ERR("failed to create class %s", KGSL_NAME);
+		goto err;
+	}
+
+	/* Make a virtual device for managing core related things
+	   in sysfs */
+	kgsl_driver.virtdev.class = kgsl_driver.class;
+	dev_set_name(&kgsl_driver.virtdev, "kgsl");
+	result = device_register(&kgsl_driver.virtdev);
+	if (result) {
+		KGSL_CORE_ERR("driver_register failed\n");
+		goto err;
+	}
+
+	/* Make kobjects in the virtual device for storing statistics */
+
+	kgsl_driver.ptkobj =
+	  kobject_create_and_add("pagetables",
+				 &kgsl_driver.virtdev.kobj);
+
+	kgsl_driver.prockobj =
+		kobject_create_and_add("proc",
+				       &kgsl_driver.virtdev.kobj);
+
+	kgsl_core_debugfs_init();
+
+	kgsl_sharedmem_init_sysfs();
+	kgsl_cffdump_init();
+
+	INIT_LIST_HEAD(&kgsl_driver.process_list);
+
+	INIT_LIST_HEAD(&kgsl_driver.pagetable_list);
+
+	kgsl_mmu_set_mmutype(ksgl_mmu_type);
+
+	if (KGSL_MMU_TYPE_GPU == kgsl_mmu_get_mmutype()) {
+		result = kgsl_ptdata_init();
+		if (result)
+			goto err;
+	}
+
+	return 0;
+
+err:
+	kgsl_core_exit();
+	return result;
+}
+
+module_init(kgsl_core_init);
+module_exit(kgsl_core_exit);
+
+MODULE_AUTHOR("Qualcomm Innovation Center, Inc.");
+MODULE_DESCRIPTION("MSM GPU driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h
new file mode 100644
index 0000000..b67f460
--- /dev/null
+++ b/drivers/gpu/msm/kgsl.h
@@ -0,0 +1,266 @@
+/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __KGSL_H
+#define __KGSL_H
+
+#include <linux/types.h>
+#include <linux/msm_kgsl.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/cdev.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mm.h>
+
+#define KGSL_NAME "kgsl"
+
+/* The number of memstore arrays limits the number of contexts allowed.
+ * If more contexts are needed, update multiple for MEMSTORE_SIZE
+ */
+#define KGSL_MEMSTORE_SIZE	((int)(PAGE_SIZE * 2))
+#define KGSL_MEMSTORE_GLOBAL	(0)
+#define KGSL_MEMSTORE_MAX	(KGSL_MEMSTORE_SIZE / \
+		sizeof(struct kgsl_devmemstore) - 1)
+
+/* Timestamp window used to detect rollovers (half of integer range) */
+#define KGSL_TIMESTAMP_WINDOW 0x80000000
+
+/*cache coherency ops */
+#define DRM_KGSL_GEM_CACHE_OP_TO_DEV	0x0001
+#define DRM_KGSL_GEM_CACHE_OP_FROM_DEV	0x0002
+
+/* The size of each entry in a page table */
+#define KGSL_PAGETABLE_ENTRY_SIZE  4
+
+/* Pagetable Virtual Address base */
+#define KGSL_PAGETABLE_BASE	0x10000000
+
+/* Extra accounting entries needed in the pagetable */
+#define KGSL_PT_EXTRA_ENTRIES      16
+
+#define KGSL_PAGETABLE_ENTRIES(_sz) (((_sz) >> PAGE_SHIFT) + \
+				     KGSL_PT_EXTRA_ENTRIES)
+
+#ifdef CONFIG_KGSL_PER_PROCESS_PAGE_TABLE
+#define KGSL_PAGETABLE_COUNT (CONFIG_MSM_KGSL_PAGE_TABLE_COUNT)
+#else
+#define KGSL_PAGETABLE_COUNT 1
+#endif
+
+/* Casting using container_of() for structures that kgsl owns. */
+#define KGSL_CONTAINER_OF(ptr, type, member) \
+		container_of(ptr, type, member)
+
+/* A macro for memory statistics - add the new size to the stat and if
+   the statisic is greater then _max, set _max
+*/
+
+#define KGSL_STATS_ADD(_size, _stat, _max) \
+	do { _stat += (_size); if (_stat > _max) _max = _stat; } while (0)
+
+struct kgsl_device;
+
+struct kgsl_driver {
+	struct cdev cdev;
+	dev_t major;
+	struct class *class;
+	/* Virtual device for managing the core */
+	struct device virtdev;
+	/* Kobjects for storing pagetable and process statistics */
+	struct kobject *ptkobj;
+	struct kobject *prockobj;
+	struct kgsl_device *devp[KGSL_DEVICE_MAX];
+
+	/* Global lilst of open processes */
+	struct list_head process_list;
+	/* Global list of pagetables */
+	struct list_head pagetable_list;
+	/* Spinlock for accessing the pagetable list */
+	spinlock_t ptlock;
+	/* Mutex for accessing the process list */
+	struct mutex process_mutex;
+
+	/* Mutex for protecting the device list */
+	struct mutex devlock;
+
+	void *ptpool;
+
+	struct {
+		unsigned int vmalloc;
+		unsigned int vmalloc_max;
+		unsigned int page_alloc;
+		unsigned int page_alloc_max;
+		unsigned int coherent;
+		unsigned int coherent_max;
+		unsigned int mapped;
+		unsigned int mapped_max;
+		unsigned int histogram[16];
+	} stats;
+};
+
+extern struct kgsl_driver kgsl_driver;
+
+struct kgsl_pagetable;
+struct kgsl_memdesc;
+
+struct kgsl_memdesc_ops {
+	int (*vmflags)(struct kgsl_memdesc *);
+	int (*vmfault)(struct kgsl_memdesc *, struct vm_area_struct *,
+		       struct vm_fault *);
+	void (*free)(struct kgsl_memdesc *memdesc);
+	int (*map_kernel_mem)(struct kgsl_memdesc *);
+};
+
+#define KGSL_MEMDESC_GUARD_PAGE BIT(0)
+
+/* shared memory allocation */
+struct kgsl_memdesc {
+	struct kgsl_pagetable *pagetable;
+	void *hostptr;
+	unsigned int gpuaddr;
+	unsigned int physaddr;
+	unsigned int size;
+	unsigned int priv;
+	struct scatterlist *sg;
+	unsigned int sglen;
+	struct kgsl_memdesc_ops *ops;
+	int flags;
+};
+
+/* List of different memory entry types */
+
+#define KGSL_MEM_ENTRY_KERNEL 0
+#define KGSL_MEM_ENTRY_PMEM   1
+#define KGSL_MEM_ENTRY_ASHMEM 2
+#define KGSL_MEM_ENTRY_USER   3
+#define KGSL_MEM_ENTRY_ION    4
+#define KGSL_MEM_ENTRY_MAX    5
+
+/* List of flags */
+
+#define KGSL_MEM_ENTRY_FROZEN (1 << 0)
+
+struct kgsl_mem_entry {
+	struct kref refcount;
+	struct kgsl_memdesc memdesc;
+	int memtype;
+	int flags;
+	void *priv_data;
+	struct rb_node node;
+	unsigned int context_id;
+	/* back pointer to private structure under whose context this
+	* allocation is made */
+	struct kgsl_process_private *priv;
+};
+
+#ifdef CONFIG_MSM_KGSL_MMU_PAGE_FAULT
+#define MMU_CONFIG 2
+#else
+#define MMU_CONFIG 1
+#endif
+
+void kgsl_mem_entry_destroy(struct kref *kref);
+
+struct kgsl_mem_entry *kgsl_get_mem_entry(unsigned int ptbase,
+		unsigned int gpuaddr, unsigned int size);
+
+struct kgsl_mem_entry *kgsl_sharedmem_find_region(
+	struct kgsl_process_private *private, unsigned int gpuaddr,
+	size_t size);
+
+extern const struct dev_pm_ops kgsl_pm_ops;
+
+struct early_suspend;
+int kgsl_suspend_driver(struct platform_device *pdev, pm_message_t state);
+int kgsl_resume_driver(struct platform_device *pdev);
+void kgsl_early_suspend_driver(struct early_suspend *h);
+void kgsl_late_resume_driver(struct early_suspend *h);
+
+#ifdef CONFIG_MSM_KGSL_DRM
+extern int kgsl_drm_init(struct platform_device *dev);
+extern void kgsl_drm_exit(void);
+#else
+static inline int kgsl_drm_init(struct platform_device *dev)
+{
+	return 0;
+}
+
+static inline void kgsl_drm_exit(void)
+{
+}
+#endif
+
+static inline int kgsl_gpuaddr_in_memdesc(const struct kgsl_memdesc *memdesc,
+				unsigned int gpuaddr, unsigned int size)
+{
+	if (gpuaddr >= memdesc->gpuaddr &&
+	    ((gpuaddr + size) <= (memdesc->gpuaddr + memdesc->size))) {
+		return 1;
+	}
+	return 0;
+}
+
+static inline void *kgsl_memdesc_map(struct kgsl_memdesc *memdesc)
+{
+	if (memdesc->hostptr == NULL && memdesc->ops->map_kernel_mem)
+		memdesc->ops->map_kernel_mem(memdesc);
+
+	return memdesc->hostptr;
+}
+
+static inline uint8_t *kgsl_gpuaddr_to_vaddr(struct kgsl_memdesc *memdesc,
+					     unsigned int gpuaddr)
+{
+	void *hostptr = NULL;
+
+	if ((gpuaddr >= memdesc->gpuaddr) &&
+		(gpuaddr < (memdesc->gpuaddr + memdesc->size)))
+		hostptr = kgsl_memdesc_map(memdesc);
+
+	return hostptr != NULL ? hostptr + (gpuaddr - memdesc->gpuaddr) : NULL;
+}
+
+static inline int timestamp_cmp(unsigned int a, unsigned int b)
+{
+	/* check for equal */
+	if (a == b)
+		return 0;
+
+	/* check for greater-than for non-rollover case */
+	if ((a > b) && (a - b < KGSL_TIMESTAMP_WINDOW))
+		return 1;
+
+	/* check for greater-than for rollover case
+	 * note that <= is required to ensure that consistent
+	 * results are returned for values whose difference is
+	 * equal to the window size
+	 */
+	a += KGSL_TIMESTAMP_WINDOW;
+	b += KGSL_TIMESTAMP_WINDOW;
+	return ((a > b) && (a - b <= KGSL_TIMESTAMP_WINDOW)) ? 1 : -1;
+}
+
+static inline void
+kgsl_mem_entry_get(struct kgsl_mem_entry *entry)
+{
+	kref_get(&entry->refcount);
+}
+
+static inline void
+kgsl_mem_entry_put(struct kgsl_mem_entry *entry)
+{
+	kref_put(&entry->refcount, kgsl_mem_entry_destroy);
+}
+
+#endif /* __KGSL_H */
diff --git a/drivers/gpu/msm/kgsl_cffdump.c b/drivers/gpu/msm/kgsl_cffdump.c
new file mode 100644
index 0000000..4e354d0
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_cffdump.c
@@ -0,0 +1,591 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/* #define DEBUG */
+#define ALIGN_CPU
+
+#include <linux/spinlock.h>
+#include <linux/debugfs.h>
+#include <linux/relay.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/sched.h>
+#include <mach/socinfo.h>
+
+#include "kgsl.h"
+#include "kgsl_cffdump.h"
+#include "kgsl_debugfs.h"
+#include "kgsl_log.h"
+#include "kgsl_sharedmem.h"
+#include "adreno_pm4types.h"
+
+static struct rchan	*chan;
+static struct dentry	*dir;
+static int		suspended;
+static size_t		dropped;
+static size_t		subbuf_size = 256*1024;
+static size_t		n_subbufs = 64;
+
+/* forward declarations */
+static void destroy_channel(void);
+static struct rchan *create_channel(unsigned subbuf_size, unsigned n_subbufs);
+
+static spinlock_t cffdump_lock;
+static ulong serial_nr;
+static ulong total_bytes;
+static ulong total_syncmem;
+static long last_sec;
+
+#define MEMBUF_SIZE	64
+
+#define CFF_OP_WRITE_REG        0x00000002
+struct cff_op_write_reg {
+	unsigned char op;
+	uint addr;
+	uint value;
+} __packed;
+
+#define CFF_OP_POLL_REG         0x00000004
+struct cff_op_poll_reg {
+	unsigned char op;
+	uint addr;
+	uint value;
+	uint mask;
+} __packed;
+
+#define CFF_OP_WAIT_IRQ         0x00000005
+struct cff_op_wait_irq {
+	unsigned char op;
+} __packed;
+
+#define CFF_OP_RMW              0x0000000a
+
+#define CFF_OP_WRITE_MEM        0x0000000b
+struct cff_op_write_mem {
+	unsigned char op;
+	uint addr;
+	uint value;
+} __packed;
+
+#define CFF_OP_WRITE_MEMBUF     0x0000000c
+struct cff_op_write_membuf {
+	unsigned char op;
+	uint addr;
+	ushort count;
+	uint buffer[MEMBUF_SIZE];
+} __packed;
+
+#define CFF_OP_MEMORY_BASE	0x0000000d
+struct cff_op_memory_base {
+	unsigned char op;
+	uint base;
+	uint size;
+	uint gmemsize;
+} __packed;
+
+#define CFF_OP_HANG		0x0000000e
+struct cff_op_hang {
+	unsigned char op;
+} __packed;
+
+#define CFF_OP_EOF              0xffffffff
+struct cff_op_eof {
+	unsigned char op;
+} __packed;
+
+#define CFF_OP_VERIFY_MEM_FILE  0x00000007
+#define CFF_OP_WRITE_SURFACE_PARAMS 0x00000011
+struct cff_op_user_event {
+	unsigned char op;
+	unsigned int op1;
+	unsigned int op2;
+	unsigned int op3;
+	unsigned int op4;
+	unsigned int op5;
+} __packed;
+
+
+static void b64_encodeblock(unsigned char in[3], unsigned char out[4], int len)
+{
+	static const char tob64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmno"
+		"pqrstuvwxyz0123456789+/";
+
+	out[0] = tob64[in[0] >> 2];
+	out[1] = tob64[((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4)];
+	out[2] = (unsigned char) (len > 1 ? tob64[((in[1] & 0x0f) << 2)
+		| ((in[2] & 0xc0) >> 6)] : '=');
+	out[3] = (unsigned char) (len > 2 ? tob64[in[2] & 0x3f] : '=');
+}
+
+static void b64_encode(const unsigned char *in_buf, int in_size,
+	unsigned char *out_buf, int out_bufsize, int *out_size)
+{
+	unsigned char in[3], out[4];
+	int i, len;
+
+	*out_size = 0;
+	while (in_size > 0) {
+		len = 0;
+		for (i = 0; i < 3; ++i) {
+			if (in_size-- > 0) {
+				in[i] = *in_buf++;
+				++len;
+			} else
+				in[i] = 0;
+		}
+		if (len) {
+			b64_encodeblock(in, out, len);
+			if (out_bufsize < 4) {
+				pr_warn("kgsl: cffdump: %s: out of buffer\n",
+					__func__);
+				return;
+			}
+			for (i = 0; i < 4; ++i)
+				*out_buf++ = out[i];
+			*out_size += 4;
+			out_bufsize -= 4;
+		}
+	}
+}
+
+#define KLOG_TMPBUF_SIZE (1024)
+static void klog_printk(const char *fmt, ...)
+{
+	/* per-cpu klog formatting temporary buffer */
+	static char klog_buf[NR_CPUS][KLOG_TMPBUF_SIZE];
+
+	va_list args;
+	int len;
+	char *cbuf;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	cbuf = klog_buf[smp_processor_id()];
+	va_start(args, fmt);
+	len = vsnprintf(cbuf, KLOG_TMPBUF_SIZE, fmt, args);
+	total_bytes += len;
+	va_end(args);
+	relay_write(chan, cbuf, len);
+	local_irq_restore(flags);
+}
+
+static struct cff_op_write_membuf cff_op_write_membuf;
+static void cffdump_membuf(int id, unsigned char *out_buf, int out_bufsize)
+{
+	void *data;
+	int len, out_size;
+	struct cff_op_write_mem cff_op_write_mem;
+
+	uint addr = cff_op_write_membuf.addr
+		- sizeof(uint)*cff_op_write_membuf.count;
+
+	if (!cff_op_write_membuf.count) {
+		pr_warn("kgsl: cffdump: membuf: count == 0, skipping");
+		return;
+	}
+
+	if (cff_op_write_membuf.count != 1) {
+		cff_op_write_membuf.op = CFF_OP_WRITE_MEMBUF;
+		cff_op_write_membuf.addr = addr;
+		len = sizeof(cff_op_write_membuf) -
+			sizeof(uint)*(MEMBUF_SIZE - cff_op_write_membuf.count);
+		data = &cff_op_write_membuf;
+	} else {
+		cff_op_write_mem.op = CFF_OP_WRITE_MEM;
+		cff_op_write_mem.addr = addr;
+		cff_op_write_mem.value = cff_op_write_membuf.buffer[0];
+		data = &cff_op_write_mem;
+		len = sizeof(cff_op_write_mem);
+	}
+	b64_encode(data, len, out_buf, out_bufsize, &out_size);
+	out_buf[out_size] = 0;
+	klog_printk("%ld:%d;%s\n", ++serial_nr, id, out_buf);
+	cff_op_write_membuf.count = 0;
+	cff_op_write_membuf.addr = 0;
+}
+
+static void cffdump_printline(int id, uint opcode, uint op1, uint op2,
+	uint op3, uint op4, uint op5)
+{
+	struct cff_op_write_reg cff_op_write_reg;
+	struct cff_op_poll_reg cff_op_poll_reg;
+	struct cff_op_wait_irq cff_op_wait_irq;
+	struct cff_op_memory_base cff_op_memory_base;
+	struct cff_op_hang cff_op_hang;
+	struct cff_op_eof cff_op_eof;
+	struct cff_op_user_event cff_op_user_event;
+	unsigned char out_buf[sizeof(cff_op_write_membuf)/3*4 + 16];
+	void *data;
+	int len = 0, out_size;
+	long cur_secs;
+
+	spin_lock(&cffdump_lock);
+	if (opcode == CFF_OP_WRITE_MEM) {
+		if ((cff_op_write_membuf.addr != op1 &&
+			cff_op_write_membuf.count)
+			|| (cff_op_write_membuf.count == MEMBUF_SIZE))
+			cffdump_membuf(id, out_buf, sizeof(out_buf));
+
+		cff_op_write_membuf.buffer[cff_op_write_membuf.count++] = op2;
+		cff_op_write_membuf.addr = op1 + sizeof(uint);
+		spin_unlock(&cffdump_lock);
+		return;
+	} else if (cff_op_write_membuf.count)
+		cffdump_membuf(id, out_buf, sizeof(out_buf));
+	spin_unlock(&cffdump_lock);
+
+	switch (opcode) {
+	case CFF_OP_WRITE_REG:
+		cff_op_write_reg.op = opcode;
+		cff_op_write_reg.addr = op1;
+		cff_op_write_reg.value = op2;
+		data = &cff_op_write_reg;
+		len = sizeof(cff_op_write_reg);
+		break;
+
+	case CFF_OP_POLL_REG:
+		cff_op_poll_reg.op = opcode;
+		cff_op_poll_reg.addr = op1;
+		cff_op_poll_reg.value = op2;
+		cff_op_poll_reg.mask = op3;
+		data = &cff_op_poll_reg;
+		len = sizeof(cff_op_poll_reg);
+		break;
+
+	case CFF_OP_WAIT_IRQ:
+		cff_op_wait_irq.op = opcode;
+		data = &cff_op_wait_irq;
+		len = sizeof(cff_op_wait_irq);
+		break;
+
+	case CFF_OP_MEMORY_BASE:
+		cff_op_memory_base.op = opcode;
+		cff_op_memory_base.base = op1;
+		cff_op_memory_base.size = op2;
+		cff_op_memory_base.gmemsize = op3;
+		data = &cff_op_memory_base;
+		len = sizeof(cff_op_memory_base);
+		break;
+
+	case CFF_OP_HANG:
+		cff_op_hang.op = opcode;
+		data = &cff_op_hang;
+		len = sizeof(cff_op_hang);
+		break;
+
+	case CFF_OP_EOF:
+		cff_op_eof.op = opcode;
+		data = &cff_op_eof;
+		len = sizeof(cff_op_eof);
+		break;
+
+	case CFF_OP_WRITE_SURFACE_PARAMS:
+	case CFF_OP_VERIFY_MEM_FILE:
+		cff_op_user_event.op = opcode;
+		cff_op_user_event.op1 = op1;
+		cff_op_user_event.op2 = op2;
+		cff_op_user_event.op3 = op3;
+		cff_op_user_event.op4 = op4;
+		cff_op_user_event.op5 = op5;
+		data = &cff_op_user_event;
+		len = sizeof(cff_op_user_event);
+		break;
+	}
+
+	if (len) {
+		b64_encode(data, len, out_buf, sizeof(out_buf), &out_size);
+		out_buf[out_size] = 0;
+		klog_printk("%ld:%d;%s\n", ++serial_nr, id, out_buf);
+	} else
+		pr_warn("kgsl: cffdump: unhandled opcode: %d\n", opcode);
+
+	cur_secs = get_seconds();
+	if ((cur_secs - last_sec) > 10 || (last_sec - cur_secs) > 10) {
+		pr_info("kgsl: cffdump: total [bytes:%lu kB, syncmem:%lu kB], "
+			"seq#: %lu\n", total_bytes/1024, total_syncmem/1024,
+			serial_nr);
+		last_sec = cur_secs;
+	}
+}
+
+void kgsl_cffdump_init()
+{
+	struct dentry *debugfs_dir = kgsl_get_debugfs_dir();
+
+#ifdef ALIGN_CPU
+	cpumask_t mask;
+
+	cpumask_clear(&mask);
+	cpumask_set_cpu(0, &mask);
+	sched_setaffinity(0, &mask);
+#endif
+	if (!debugfs_dir || IS_ERR(debugfs_dir)) {
+		KGSL_CORE_ERR("Debugfs directory is bad\n");
+		return;
+	}
+
+	kgsl_cff_dump_enable = 1;
+
+	spin_lock_init(&cffdump_lock);
+
+	dir = debugfs_create_dir("cff", debugfs_dir);
+	if (!dir) {
+		KGSL_CORE_ERR("debugfs_create_dir failed\n");
+		return;
+	}
+
+	chan = create_channel(subbuf_size, n_subbufs);
+}
+
+void kgsl_cffdump_destroy()
+{
+	if (chan)
+		relay_flush(chan);
+	destroy_channel();
+	if (dir)
+		debugfs_remove(dir);
+}
+
+void kgsl_cffdump_open(enum kgsl_deviceid device_id)
+{
+	kgsl_cffdump_memory_base(device_id, KGSL_PAGETABLE_BASE,
+			kgsl_mmu_get_ptsize(), SZ_256K);
+}
+
+void kgsl_cffdump_memory_base(enum kgsl_deviceid device_id, unsigned int base,
+			      unsigned int range, unsigned gmemsize)
+{
+	cffdump_printline(device_id, CFF_OP_MEMORY_BASE, base,
+			range, gmemsize, 0, 0);
+}
+
+void kgsl_cffdump_hang(enum kgsl_deviceid device_id)
+{
+	cffdump_printline(device_id, CFF_OP_HANG, 0, 0, 0, 0, 0);
+}
+
+void kgsl_cffdump_close(enum kgsl_deviceid device_id)
+{
+	cffdump_printline(device_id, CFF_OP_EOF, 0, 0, 0, 0, 0);
+}
+
+void kgsl_cffdump_user_event(unsigned int cff_opcode, unsigned int op1,
+		unsigned int op2, unsigned int op3,
+		unsigned int op4, unsigned int op5)
+{
+	cffdump_printline(-1, cff_opcode, op1, op2, op3, op4, op5);
+}
+
+void kgsl_cffdump_syncmem(struct kgsl_device_private *dev_priv,
+	const struct kgsl_memdesc *memdesc, uint gpuaddr, uint sizebytes,
+	bool clean_cache)
+{
+	const void *src;
+
+	if (!kgsl_cff_dump_enable)
+		return;
+
+	total_syncmem += sizebytes;
+
+	if (memdesc == NULL) {
+		struct kgsl_mem_entry *entry;
+		spin_lock(&dev_priv->process_priv->mem_lock);
+		entry = kgsl_sharedmem_find_region(dev_priv->process_priv,
+			gpuaddr, sizebytes);
+		spin_unlock(&dev_priv->process_priv->mem_lock);
+		if (entry == NULL) {
+			KGSL_CORE_ERR("did not find mapping "
+				"for gpuaddr: 0x%08x\n", gpuaddr);
+			return;
+		}
+		memdesc = &entry->memdesc;
+	}
+	src = (uint *)kgsl_gpuaddr_to_vaddr(memdesc, gpuaddr);
+	if (memdesc->hostptr == NULL) {
+		KGSL_CORE_ERR("no kernel mapping for "
+			"gpuaddr: 0x%08x, m->host: 0x%p, phys: 0x%08x\n",
+			gpuaddr, memdesc->hostptr, memdesc->physaddr);
+		return;
+	}
+
+	if (clean_cache) {
+		/* Ensure that this memory region is not read from the
+		 * cache but fetched fresh */
+
+		mb();
+
+		kgsl_cache_range_op((struct kgsl_memdesc *)memdesc,
+				KGSL_CACHE_OP_INV);
+	}
+
+	while (sizebytes > 3) {
+		cffdump_printline(-1, CFF_OP_WRITE_MEM, gpuaddr, *(uint *)src,
+			0, 0, 0);
+		gpuaddr += 4;
+		src += 4;
+		sizebytes -= 4;
+	}
+	if (sizebytes > 0)
+		cffdump_printline(-1, CFF_OP_WRITE_MEM, gpuaddr, *(uint *)src,
+			0, 0, 0);
+}
+
+void kgsl_cffdump_setmem(uint addr, uint value, uint sizebytes)
+{
+	if (!kgsl_cff_dump_enable)
+		return;
+
+	while (sizebytes > 3) {
+		/* Use 32bit memory writes as long as there's at least
+		 * 4 bytes left */
+		cffdump_printline(-1, CFF_OP_WRITE_MEM, addr, value,
+				0, 0, 0);
+		addr += 4;
+		sizebytes -= 4;
+	}
+	if (sizebytes > 0)
+		cffdump_printline(-1, CFF_OP_WRITE_MEM, addr, value,
+				0, 0, 0);
+}
+
+void kgsl_cffdump_regwrite(enum kgsl_deviceid device_id, uint addr,
+	uint value)
+{
+	if (!kgsl_cff_dump_enable)
+		return;
+
+	cffdump_printline(device_id, CFF_OP_WRITE_REG, addr, value,
+			0, 0, 0);
+}
+
+void kgsl_cffdump_regpoll(enum kgsl_deviceid device_id, uint addr,
+	uint value, uint mask)
+{
+	if (!kgsl_cff_dump_enable)
+		return;
+
+	cffdump_printline(device_id, CFF_OP_POLL_REG, addr, value,
+			mask, 0, 0);
+}
+
+void kgsl_cffdump_slavewrite(uint addr, uint value)
+{
+	if (!kgsl_cff_dump_enable)
+		return;
+
+	cffdump_printline(-1, CFF_OP_WRITE_REG, addr, value, 0, 0, 0);
+}
+
+int kgsl_cffdump_waitirq(void)
+{
+	if (!kgsl_cff_dump_enable)
+		return 0;
+
+	cffdump_printline(-1, CFF_OP_WAIT_IRQ, 0, 0, 0, 0, 0);
+
+	return 1;
+}
+EXPORT_SYMBOL(kgsl_cffdump_waitirq);
+
+static int subbuf_start_handler(struct rchan_buf *buf,
+	void *subbuf, void *prev_subbuf, uint prev_padding)
+{
+	pr_debug("kgsl: cffdump: subbuf_start_handler(subbuf=%p, prev_subbuf"
+		"=%p, prev_padding=%08x)\n", subbuf, prev_subbuf, prev_padding);
+
+	if (relay_buf_full(buf)) {
+		if (!suspended) {
+			suspended = 1;
+			pr_warn("kgsl: cffdump: relay: cpu %d buffer full!!!\n",
+				smp_processor_id());
+		}
+		dropped++;
+		return 0;
+	} else if (suspended) {
+		suspended = 0;
+		pr_warn("kgsl: cffdump: relay: cpu %d buffer no longer full.\n",
+			smp_processor_id());
+	}
+
+	subbuf_start_reserve(buf, 0);
+	return 1;
+}
+
+static struct dentry *create_buf_file_handler(const char *filename,
+	struct dentry *parent, int mode, struct rchan_buf *buf,
+	int *is_global)
+{
+	return debugfs_create_file(filename, mode, parent, buf,
+				       &relay_file_operations);
+}
+
+/*
+ * file_remove() default callback.  Removes relay file in debugfs.
+ */
+static int remove_buf_file_handler(struct dentry *dentry)
+{
+	pr_info("kgsl: cffdump: %s()\n", __func__);
+	debugfs_remove(dentry);
+	return 0;
+}
+
+/*
+ * relay callbacks
+ */
+static struct rchan_callbacks relay_callbacks = {
+	.subbuf_start = subbuf_start_handler,
+	.create_buf_file = create_buf_file_handler,
+	.remove_buf_file = remove_buf_file_handler,
+};
+
+/**
+ *	create_channel - creates channel /debug/klog/cpuXXX
+ *
+ *	Creates channel along with associated produced/consumed control files
+ *
+ *	Returns channel on success, NULL otherwise
+ */
+static struct rchan *create_channel(unsigned subbuf_size, unsigned n_subbufs)
+{
+	struct rchan *chan;
+
+	pr_info("kgsl: cffdump: relay: create_channel: subbuf_size %u, "
+		"n_subbufs %u, dir 0x%p\n", subbuf_size, n_subbufs, dir);
+
+	chan = relay_open("cpu", dir, subbuf_size,
+			  n_subbufs, &relay_callbacks, NULL);
+	if (!chan) {
+		KGSL_CORE_ERR("relay_open failed\n");
+		return NULL;
+	}
+
+	suspended = 0;
+	dropped = 0;
+
+	return chan;
+}
+
+/**
+ *	destroy_channel - destroys channel /debug/kgsl/cff/cpuXXX
+ *
+ *	Destroys channel along with associated produced/consumed control files
+ */
+static void destroy_channel(void)
+{
+	pr_info("kgsl: cffdump: relay: destroy_channel\n");
+	if (chan) {
+		relay_close(chan);
+		chan = NULL;
+	}
+}
+
diff --git a/drivers/gpu/msm/kgsl_cffdump.h b/drivers/gpu/msm/kgsl_cffdump.h
new file mode 100644
index 0000000..140e486
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_cffdump.h
@@ -0,0 +1,69 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __KGSL_CFFDUMP_H
+#define __KGSL_CFFDUMP_H
+
+#ifdef CONFIG_MSM_KGSL_CFF_DUMP
+
+#include <linux/types.h>
+
+#include "kgsl_device.h"
+
+void kgsl_cffdump_init(void);
+void kgsl_cffdump_destroy(void);
+void kgsl_cffdump_open(enum kgsl_deviceid device_id);
+void kgsl_cffdump_close(enum kgsl_deviceid device_id);
+void kgsl_cffdump_syncmem(struct kgsl_device_private *dev_priv,
+	const struct kgsl_memdesc *memdesc, uint physaddr, uint sizebytes,
+	bool clean_cache);
+void kgsl_cffdump_setmem(uint addr, uint value, uint sizebytes);
+void kgsl_cffdump_regwrite(enum kgsl_deviceid device_id, uint addr,
+	uint value);
+void kgsl_cffdump_regpoll(enum kgsl_deviceid device_id, uint addr,
+	uint value, uint mask);
+bool kgsl_cffdump_parse_ibs(struct kgsl_device_private *dev_priv,
+	const struct kgsl_memdesc *memdesc, uint gpuaddr, int sizedwords,
+	bool check_only);
+void kgsl_cffdump_user_event(unsigned int cff_opcode, unsigned int op1,
+		unsigned int op2, unsigned int op3,
+		unsigned int op4, unsigned int op5);
+static inline bool kgsl_cffdump_flags_no_memzero(void) { return true; }
+
+void kgsl_cffdump_memory_base(enum kgsl_deviceid device_id, unsigned int base,
+			      unsigned int range, unsigned int gmemsize);
+
+void kgsl_cffdump_hang(enum kgsl_deviceid device_id);
+
+#else
+
+#define kgsl_cffdump_init()					(void)0
+#define kgsl_cffdump_destroy()					(void)0
+#define kgsl_cffdump_open(device_id)				(void)0
+#define kgsl_cffdump_close(device_id)				(void)0
+#define kgsl_cffdump_syncmem(dev_priv, memdesc, addr, sizebytes, clean_cache) \
+	(void) 0
+#define kgsl_cffdump_setmem(addr, value, sizebytes)		(void)0
+#define kgsl_cffdump_regwrite(device_id, addr, value)		(void)0
+#define kgsl_cffdump_regpoll(device_id, addr, value, mask)	(void)0
+#define kgsl_cffdump_parse_ibs(dev_priv, memdesc, gpuaddr, \
+	sizedwords, check_only)					true
+#define kgsl_cffdump_flags_no_memzero()				true
+#define kgsl_cffdump_memory_base(base, range, gmemsize)		(void)0
+#define kgsl_cffdump_hang(device_id)				(void)0
+#define kgsl_cffdump_user_event(cff_opcode, op1, op2, op3, op4, op5) \
+	(void)param
+
+#endif /* CONFIG_MSM_KGSL_CFF_DUMP */
+
+#endif /* __KGSL_CFFDUMP_H */
diff --git a/drivers/gpu/msm/kgsl_debugfs.c b/drivers/gpu/msm/kgsl_debugfs.c
new file mode 100644
index 0000000..328dd95
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_debugfs.c
@@ -0,0 +1,88 @@
+/* Copyright (c) 2002,2008-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/debugfs.h>
+
+#include "kgsl.h"
+#include "kgsl_device.h"
+
+/*default log levels is error for everything*/
+#define KGSL_LOG_LEVEL_DEFAULT 3
+#define KGSL_LOG_LEVEL_MAX     7
+
+struct dentry *kgsl_debugfs_dir;
+
+static inline int kgsl_log_set(unsigned int *log_val, void *data, u64 val)
+{
+	*log_val = min((unsigned int)val, (unsigned int)KGSL_LOG_LEVEL_MAX);
+	return 0;
+}
+
+#define KGSL_DEBUGFS_LOG(__log)                         \
+static int __log ## _set(void *data, u64 val)           \
+{                                                       \
+	struct kgsl_device *device = data;              \
+	return kgsl_log_set(&device->__log, data, val); \
+}                                                       \
+static int __log ## _get(void *data, u64 *val)	        \
+{                                                       \
+	struct kgsl_device *device = data;              \
+	*val = device->__log;                           \
+	return 0;                                       \
+}                                                       \
+DEFINE_SIMPLE_ATTRIBUTE(__log ## _fops,                 \
+__log ## _get, __log ## _set, "%llu\n");                \
+
+KGSL_DEBUGFS_LOG(drv_log);
+KGSL_DEBUGFS_LOG(cmd_log);
+KGSL_DEBUGFS_LOG(ctxt_log);
+KGSL_DEBUGFS_LOG(mem_log);
+KGSL_DEBUGFS_LOG(pwr_log);
+
+void kgsl_device_debugfs_init(struct kgsl_device *device)
+{
+	if (kgsl_debugfs_dir && !IS_ERR(kgsl_debugfs_dir))
+		device->d_debugfs = debugfs_create_dir(device->name,
+						       kgsl_debugfs_dir);
+
+	if (!device->d_debugfs || IS_ERR(device->d_debugfs))
+		return;
+
+	device->cmd_log = KGSL_LOG_LEVEL_DEFAULT;
+	device->ctxt_log = KGSL_LOG_LEVEL_DEFAULT;
+	device->drv_log = KGSL_LOG_LEVEL_DEFAULT;
+	device->mem_log = KGSL_LOG_LEVEL_DEFAULT;
+	device->pwr_log = KGSL_LOG_LEVEL_DEFAULT;
+
+	debugfs_create_file("log_level_cmd", 0644, device->d_debugfs, device,
+			    &cmd_log_fops);
+	debugfs_create_file("log_level_ctxt", 0644, device->d_debugfs, device,
+			    &ctxt_log_fops);
+	debugfs_create_file("log_level_drv", 0644, device->d_debugfs, device,
+			    &drv_log_fops);
+	debugfs_create_file("log_level_mem", 0644, device->d_debugfs, device,
+				&mem_log_fops);
+	debugfs_create_file("log_level_pwr", 0644, device->d_debugfs, device,
+				&pwr_log_fops);
+}
+
+void kgsl_core_debugfs_init(void)
+{
+	kgsl_debugfs_dir = debugfs_create_dir("kgsl", 0);
+}
+
+void kgsl_core_debugfs_close(void)
+{
+	debugfs_remove_recursive(kgsl_debugfs_dir);
+}
diff --git a/drivers/gpu/msm/kgsl_debugfs.h b/drivers/gpu/msm/kgsl_debugfs.h
new file mode 100644
index 0000000..5e10988
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_debugfs.h
@@ -0,0 +1,39 @@
+/* Copyright (c) 2002,2008-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _KGSL_DEBUGFS_H
+#define _KGSL_DEBUGFS_H
+
+struct kgsl_device;
+
+#ifdef CONFIG_DEBUG_FS
+void kgsl_core_debugfs_init(void);
+void kgsl_core_debugfs_close(void);
+
+void kgsl_device_debugfs_init(struct kgsl_device *device);
+
+extern struct dentry *kgsl_debugfs_dir;
+static inline struct dentry *kgsl_get_debugfs_dir(void)
+{
+	return kgsl_debugfs_dir;
+}
+
+#else
+static inline void kgsl_core_debugfs_init(void) { }
+static inline void kgsl_device_debugfs_init(struct kgsl_device *device) { }
+static inline void kgsl_core_debugfs_close(void) { }
+static inline struct dentry *kgsl_get_debugfs_dir(void) { return NULL; }
+
+#endif
+
+#endif
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
new file mode 100644
index 0000000..203f0c9
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -0,0 +1,419 @@
+/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __KGSL_DEVICE_H
+#define __KGSL_DEVICE_H
+
+#include <linux/idr.h>
+#include <linux/wakelock.h>
+#include <linux/pm_qos.h>
+#include <linux/earlysuspend.h>
+
+#include "kgsl.h"
+#include "kgsl_mmu.h"
+#include "kgsl_pwrctrl.h"
+#include "kgsl_log.h"
+#include "kgsl_pwrscale.h"
+
+#define KGSL_TIMEOUT_NONE       0
+#define KGSL_TIMEOUT_DEFAULT    0xFFFFFFFF
+
+#define FIRST_TIMEOUT (HZ / 2)
+
+
+/* KGSL device state is initialized to INIT when platform_probe		*
+ * sucessfully initialized the device.  Once a device has been opened	*
+ * (started) it becomes active.  NAP implies that only low latency	*
+ * resources (for now clocks on some platforms) are off.  SLEEP implies	*
+ * that the KGSL module believes a device is idle (has been inactive	*
+ * past its timer) and all system resources are released.  SUSPEND is	*
+ * requested by the kernel and will be enforced upon all open devices.	*/
+
+#define KGSL_STATE_NONE		0x00000000
+#define KGSL_STATE_INIT		0x00000001
+#define KGSL_STATE_ACTIVE	0x00000002
+#define KGSL_STATE_NAP		0x00000004
+#define KGSL_STATE_SLEEP	0x00000008
+#define KGSL_STATE_SUSPEND	0x00000010
+#define KGSL_STATE_HUNG		0x00000020
+#define KGSL_STATE_DUMP_AND_RECOVER	0x00000040
+#define KGSL_STATE_SLUMBER	0x00000080
+
+#define KGSL_GRAPHICS_MEMORY_LOW_WATERMARK  0x1000000
+
+#define KGSL_IS_PAGE_ALIGNED(addr) (!((addr) & (~PAGE_MASK)))
+
+struct kgsl_device;
+struct platform_device;
+struct kgsl_device_private;
+struct kgsl_context;
+struct kgsl_power_stats;
+
+struct kgsl_functable {
+	/* Mandatory functions - these functions must be implemented
+	   by the client device.  The driver will not check for a NULL
+	   pointer before calling the hook.
+	 */
+	void (*regread) (struct kgsl_device *device,
+		unsigned int offsetwords, unsigned int *value);
+	void (*regwrite) (struct kgsl_device *device,
+		unsigned int offsetwords, unsigned int value);
+	int (*idle) (struct kgsl_device *device, unsigned int timeout);
+	unsigned int (*isidle) (struct kgsl_device *device);
+	int (*suspend_context) (struct kgsl_device *device);
+	int (*start) (struct kgsl_device *device, unsigned int init_ram);
+	int (*stop) (struct kgsl_device *device);
+	int (*getproperty) (struct kgsl_device *device,
+		enum kgsl_property_type type, void *value,
+		unsigned int sizebytes);
+	int (*waittimestamp) (struct kgsl_device *device,
+		struct kgsl_context *context, unsigned int timestamp,
+		unsigned int msecs);
+	unsigned int (*readtimestamp) (struct kgsl_device *device,
+		struct kgsl_context *context, enum kgsl_timestamp_type type);
+	int (*issueibcmds) (struct kgsl_device_private *dev_priv,
+		struct kgsl_context *context, struct kgsl_ibdesc *ibdesc,
+		unsigned int sizedwords, uint32_t *timestamp,
+		unsigned int flags);
+	int (*setup_pt)(struct kgsl_device *device,
+		struct kgsl_pagetable *pagetable);
+	void (*cleanup_pt)(struct kgsl_device *device,
+		struct kgsl_pagetable *pagetable);
+	void (*power_stats)(struct kgsl_device *device,
+		struct kgsl_power_stats *stats);
+	void (*irqctrl)(struct kgsl_device *device, int state);
+	unsigned int (*gpuid)(struct kgsl_device *device);
+	void * (*snapshot)(struct kgsl_device *device, void *snapshot,
+		int *remain, int hang);
+	irqreturn_t (*irq_handler)(struct kgsl_device *device);
+	/* Optional functions - these functions are not mandatory.  The
+	   driver will check that the function pointer is not NULL before
+	   calling the hook */
+	void (*setstate) (struct kgsl_device *device, uint32_t flags);
+	int (*drawctxt_create) (struct kgsl_device *device,
+		struct kgsl_pagetable *pagetable, struct kgsl_context *context,
+		uint32_t flags);
+	void (*drawctxt_destroy) (struct kgsl_device *device,
+		struct kgsl_context *context);
+	long (*ioctl) (struct kgsl_device_private *dev_priv,
+		unsigned int cmd, void *data);
+	int (*setproperty) (struct kgsl_device *device,
+		enum kgsl_property_type type, void *value,
+		unsigned int sizebytes);
+};
+
+/* MH register values */
+struct kgsl_mh {
+	unsigned int     mharb;
+	unsigned int     mh_intf_cfg1;
+	unsigned int     mh_intf_cfg2;
+	uint32_t         mpu_base;
+	int              mpu_range;
+};
+
+struct kgsl_event {
+	struct kgsl_context *context;
+	uint32_t timestamp;
+	void (*func)(struct kgsl_device *, void *, u32, u32);
+	void *priv;
+	struct list_head list;
+	struct kgsl_device_private *owner;
+};
+
+
+struct kgsl_device {
+	struct device *dev;
+	const char *name;
+	unsigned int ver_major;
+	unsigned int ver_minor;
+	uint32_t flags;
+	enum kgsl_deviceid id;
+	unsigned long reg_phys;
+	void *reg_virt;
+	unsigned int reg_len;
+	struct kgsl_memdesc memstore;
+	const char *iomemname;
+
+	struct kgsl_mh mh;
+	struct kgsl_mmu mmu;
+	struct completion hwaccess_gate;
+	const struct kgsl_functable *ftbl;
+	struct work_struct idle_check_ws;
+	struct timer_list idle_timer;
+	struct kgsl_pwrctrl pwrctrl;
+	int open_count;
+
+	struct atomic_notifier_head ts_notifier_list;
+	struct mutex mutex;
+	uint32_t state;
+	uint32_t requested_state;
+
+	unsigned int last_expired_ctxt_id;
+	unsigned int active_cnt;
+	struct completion suspend_gate;
+
+	wait_queue_head_t wait_queue;
+	struct workqueue_struct *work_queue;
+	struct device *parentdev;
+	struct completion recovery_gate;
+	struct dentry *d_debugfs;
+	struct idr context_idr;
+	struct early_suspend display_off;
+
+	void *snapshot;		/* Pointer to the snapshot memory region */
+	int snapshot_maxsize;   /* Max size of the snapshot region */
+	int snapshot_size;      /* Current size of the snapshot region */
+	u32 snapshot_timestamp;	/* Timestamp of the last valid snapshot */
+	int snapshot_frozen;	/* 1 if the snapshot output is frozen until
+				   it gets read by the user.  This avoids
+				   losing the output on multiple hangs  */
+	struct kobject snapshot_kobj;
+
+	/*
+	 * List of GPU buffers that have been frozen in memory until they can be
+	 * dumped
+	 */
+	struct list_head snapshot_obj_list;
+
+	/* Logging levels */
+	int cmd_log;
+	int ctxt_log;
+	int drv_log;
+	int mem_log;
+	int pwr_log;
+	struct wake_lock idle_wakelock;
+	struct kgsl_pwrscale pwrscale;
+	struct kobject pwrscale_kobj;
+	struct pm_qos_request pm_qos_req_dma;
+	struct work_struct ts_expired_ws;
+	struct list_head events;
+	s64 on_time;
+};
+
+void kgsl_timestamp_expired(struct work_struct *work);
+
+#define KGSL_DEVICE_COMMON_INIT(_dev) \
+	.hwaccess_gate = COMPLETION_INITIALIZER((_dev).hwaccess_gate),\
+	.suspend_gate = COMPLETION_INITIALIZER((_dev).suspend_gate),\
+	.recovery_gate = COMPLETION_INITIALIZER((_dev).recovery_gate),\
+	.ts_notifier_list = ATOMIC_NOTIFIER_INIT((_dev).ts_notifier_list),\
+	.idle_check_ws = __WORK_INITIALIZER((_dev).idle_check_ws,\
+			kgsl_idle_check),\
+	.ts_expired_ws  = __WORK_INITIALIZER((_dev).ts_expired_ws,\
+			kgsl_timestamp_expired),\
+	.context_idr = IDR_INIT((_dev).context_idr),\
+	.events = LIST_HEAD_INIT((_dev).events),\
+	.wait_queue = __WAIT_QUEUE_HEAD_INITIALIZER((_dev).wait_queue),\
+	.mutex = __MUTEX_INITIALIZER((_dev).mutex),\
+	.state = KGSL_STATE_INIT,\
+	.ver_major = DRIVER_VERSION_MAJOR,\
+	.ver_minor = DRIVER_VERSION_MINOR,\
+	.last_expired_ctxt_id = KGSL_CONTEXT_INVALID
+
+struct kgsl_context {
+	struct kref refcount;
+	uint32_t id;
+
+	/* Pointer to the owning device instance */
+	struct kgsl_device_private *dev_priv;
+
+	/* Pointer to the device specific context information */
+	void *devctxt;
+	/*
+	 * Status indicating whether a gpu reset occurred and whether this
+	 * context was responsible for causing it
+	 */
+	unsigned int reset_status;
+};
+
+struct kgsl_process_private {
+	unsigned int refcnt;
+	pid_t pid;
+	spinlock_t mem_lock;
+	struct rb_root mem_rb;
+	struct kgsl_pagetable *pagetable;
+	struct list_head list;
+	struct kobject kobj;
+
+	struct {
+		unsigned int cur;
+		unsigned int max;
+	} stats[KGSL_MEM_ENTRY_MAX];
+};
+
+struct kgsl_device_private {
+	struct kgsl_device *device;
+	struct kgsl_process_private *process_priv;
+};
+
+struct kgsl_power_stats {
+	s64 total_time;
+	s64 busy_time;
+};
+
+struct kgsl_device *kgsl_get_device(int dev_idx);
+
+static inline void kgsl_process_add_stats(struct kgsl_process_private *priv,
+	unsigned int type, size_t size)
+{
+	priv->stats[type].cur += size;
+	if (priv->stats[type].max < priv->stats[type].cur)
+		priv->stats[type].max = priv->stats[type].cur;
+}
+
+static inline void kgsl_regread(struct kgsl_device *device,
+				unsigned int offsetwords,
+				unsigned int *value)
+{
+	device->ftbl->regread(device, offsetwords, value);
+}
+
+static inline void kgsl_regwrite(struct kgsl_device *device,
+				 unsigned int offsetwords,
+				 unsigned int value)
+{
+	device->ftbl->regwrite(device, offsetwords, value);
+}
+
+static inline int kgsl_idle(struct kgsl_device *device, unsigned int timeout)
+{
+	return device->ftbl->idle(device, timeout);
+}
+
+static inline unsigned int kgsl_gpuid(struct kgsl_device *device)
+{
+	return device->ftbl->gpuid(device);
+}
+
+static inline unsigned int kgsl_readtimestamp(struct kgsl_device *device,
+					      struct kgsl_context *context,
+					      enum kgsl_timestamp_type type)
+{
+	return device->ftbl->readtimestamp(device, context, type);
+}
+
+static inline int kgsl_create_device_sysfs_files(struct device *root,
+	const struct device_attribute **list)
+{
+	int ret = 0, i;
+	for (i = 0; list[i] != NULL; i++)
+		ret |= device_create_file(root, list[i]);
+	return ret;
+}
+
+static inline void kgsl_remove_device_sysfs_files(struct device *root,
+	const struct device_attribute **list)
+{
+	int i;
+	for (i = 0; list[i] != NULL; i++)
+		device_remove_file(root, list[i]);
+}
+
+static inline struct kgsl_mmu *
+kgsl_get_mmu(struct kgsl_device *device)
+{
+	return (struct kgsl_mmu *) (device ? &device->mmu : NULL);
+}
+
+static inline struct kgsl_device *kgsl_device_from_dev(struct device *dev)
+{
+	int i;
+
+	for (i = 0; i < KGSL_DEVICE_MAX; i++) {
+		if (kgsl_driver.devp[i] && kgsl_driver.devp[i]->dev == dev)
+			return kgsl_driver.devp[i];
+	}
+
+	return NULL;
+}
+
+static inline int kgsl_create_device_workqueue(struct kgsl_device *device)
+{
+	device->work_queue = create_singlethread_workqueue(device->name);
+	if (!device->work_queue) {
+		KGSL_DRV_ERR(device,
+			     "create_singlethread_workqueue(%s) failed\n",
+			     device->name);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static inline struct kgsl_context *
+kgsl_find_context(struct kgsl_device_private *dev_priv, uint32_t id)
+{
+	struct kgsl_context *ctxt =
+		idr_find(&dev_priv->device->context_idr, id);
+
+	/* Make sure that the context belongs to the current instance so
+	   that other processes can't guess context IDs and mess things up */
+
+	return  (ctxt && ctxt->dev_priv == dev_priv) ? ctxt : NULL;
+}
+
+int kgsl_check_timestamp(struct kgsl_device *device,
+		struct kgsl_context *context, unsigned int timestamp);
+
+int kgsl_register_ts_notifier(struct kgsl_device *device,
+			      struct notifier_block *nb);
+
+int kgsl_unregister_ts_notifier(struct kgsl_device *device,
+				struct notifier_block *nb);
+
+int kgsl_device_platform_probe(struct kgsl_device *device);
+
+void kgsl_device_platform_remove(struct kgsl_device *device);
+
+const char *kgsl_pwrstate_to_str(unsigned int state);
+
+int kgsl_device_snapshot_init(struct kgsl_device *device);
+int kgsl_device_snapshot(struct kgsl_device *device, int hang);
+void kgsl_device_snapshot_close(struct kgsl_device *device);
+
+static inline struct kgsl_device_platform_data *
+kgsl_device_get_drvdata(struct kgsl_device *dev)
+{
+	struct platform_device *pdev =
+		container_of(dev->parentdev, struct platform_device, dev);
+
+	return pdev->dev.platform_data;
+}
+
+/**
+ * kgsl_context_get - Get context reference count
+ * @context
+ *
+ * Asynchronous code that holds a pointer to a context
+ * must hold a reference count on it. The kgsl device
+ * mutex must be held while the context reference count
+ * is changed.
+ */
+static inline void
+kgsl_context_get(struct kgsl_context *context)
+{
+	kref_get(&context->refcount);
+}
+
+void kgsl_context_destroy(struct kref *kref);
+
+/**
+ * kgsl_context_put - Release context reference count
+ * @context
+ *
+ */
+static inline void
+kgsl_context_put(struct kgsl_context *context)
+{
+	kref_put(&context->refcount, kgsl_context_destroy);
+}
+
+#endif  /* __KGSL_DEVICE_H */
diff --git a/drivers/gpu/msm/kgsl_drm.c b/drivers/gpu/msm/kgsl_drm.c
new file mode 100644
index 0000000..66ac08f
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_drm.c
@@ -0,0 +1,1508 @@
+/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/* Implements an interface between KGSL and the DRM subsystem.  For now this
+ * is pretty simple, but it will take on more of the workload as time goes
+ * on
+ */
+#include "drmP.h"
+#include "drm.h"
+#include <linux/android_pmem.h>
+
+#include "kgsl.h"
+#include "kgsl_device.h"
+#include "kgsl_drm.h"
+#include "kgsl_mmu.h"
+#include "kgsl_sharedmem.h"
+
+#define DRIVER_AUTHOR           "Qualcomm"
+#define DRIVER_NAME             "kgsl"
+#define DRIVER_DESC             "KGSL DRM"
+#define DRIVER_DATE             "20100127"
+
+#define DRIVER_MAJOR            2
+#define DRIVER_MINOR            1
+#define DRIVER_PATCHLEVEL       1
+
+#define DRM_KGSL_GEM_FLAG_MAPPED (1 << 0)
+
+#define ENTRY_EMPTY -1
+#define ENTRY_NEEDS_CLEANUP -2
+
+#define DRM_KGSL_NOT_INITED -1
+#define DRM_KGSL_INITED   1
+
+#define DRM_KGSL_NUM_FENCE_ENTRIES (DRM_KGSL_HANDLE_WAIT_ENTRIES << 2)
+#define DRM_KGSL_HANDLE_WAIT_ENTRIES 5
+
+/* Returns true if the memory type is in PMEM */
+
+#ifdef CONFIG_KERNEL_PMEM_SMI_REGION
+#define TYPE_IS_PMEM(_t) \
+  (((_t & DRM_KGSL_GEM_TYPE_MEM_MASK) == DRM_KGSL_GEM_TYPE_EBI) || \
+   ((_t & DRM_KGSL_GEM_TYPE_MEM_MASK) == DRM_KGSL_GEM_TYPE_SMI) || \
+   ((_t) & DRM_KGSL_GEM_TYPE_PMEM))
+#else
+#define TYPE_IS_PMEM(_t) \
+  (((_t & DRM_KGSL_GEM_TYPE_MEM_MASK) == DRM_KGSL_GEM_TYPE_EBI) || \
+   ((_t) & (DRM_KGSL_GEM_TYPE_PMEM | DRM_KGSL_GEM_PMEM_EBI)))
+#endif
+
+/* Returns true if the memory type is regular */
+
+#define TYPE_IS_MEM(_t) \
+  (((_t & DRM_KGSL_GEM_TYPE_MEM_MASK) == DRM_KGSL_GEM_TYPE_KMEM) || \
+   ((_t & DRM_KGSL_GEM_TYPE_MEM_MASK) == DRM_KGSL_GEM_TYPE_KMEM_NOCACHE) || \
+   ((_t) & DRM_KGSL_GEM_TYPE_MEM))
+
+#define TYPE_IS_FD(_t) ((_t) & DRM_KGSL_GEM_TYPE_FD_MASK)
+
+/* Returns true if KMEM region is uncached */
+
+#define IS_MEM_UNCACHED(_t) \
+  ((_t == DRM_KGSL_GEM_TYPE_KMEM_NOCACHE) || \
+   (_t == DRM_KGSL_GEM_TYPE_KMEM) || \
+   (TYPE_IS_MEM(_t) && (_t & DRM_KGSL_GEM_CACHE_WCOMBINE)))
+
+struct drm_kgsl_gem_object_wait_list_entry {
+	struct list_head list;
+	int pid;
+	int in_use;
+	wait_queue_head_t process_wait_q;
+};
+
+struct drm_kgsl_gem_object_fence {
+	int32_t fence_id;
+	unsigned int num_buffers;
+	int ts_valid;
+	unsigned int timestamp;
+	int ts_device;
+	int lockpid;
+	struct list_head buffers_in_fence;
+};
+
+struct drm_kgsl_gem_object_fence_list_entry {
+	struct list_head list;
+	int in_use;
+	struct drm_gem_object *gem_obj;
+};
+
+static int32_t fence_id = 0x1;
+
+static struct drm_kgsl_gem_object_fence
+			  gem_buf_fence[DRM_KGSL_NUM_FENCE_ENTRIES];
+
+struct drm_kgsl_gem_object {
+	struct drm_gem_object *obj;
+	uint32_t type;
+	struct kgsl_memdesc memdesc;
+	struct kgsl_pagetable *pagetable;
+	uint64_t mmap_offset;
+	int bufcount;
+	int flags;
+	struct list_head list;
+	int active;
+
+	struct {
+		uint32_t offset;
+		uint32_t gpuaddr;
+	} bufs[DRM_KGSL_GEM_MAX_BUFFERS];
+
+	int bound;
+	int lockpid;
+	/* Put these here to avoid allocing all the time */
+	struct drm_kgsl_gem_object_wait_list_entry
+	wait_entries[DRM_KGSL_HANDLE_WAIT_ENTRIES];
+	/* Each object can only appear in a single fence */
+	struct drm_kgsl_gem_object_fence_list_entry
+	fence_entries[DRM_KGSL_NUM_FENCE_ENTRIES];
+
+	struct list_head wait_list;
+};
+
+static int kgsl_drm_inited = DRM_KGSL_NOT_INITED;
+
+/* This is a global list of all the memory currently mapped in the MMU */
+static struct list_head kgsl_mem_list;
+
+static void kgsl_gem_mem_flush(struct kgsl_memdesc *memdesc, int type, int op)
+{
+	int cacheop = 0;
+
+	switch (op) {
+	case DRM_KGSL_GEM_CACHE_OP_TO_DEV:
+		if (type & (DRM_KGSL_GEM_CACHE_WBACK |
+			    DRM_KGSL_GEM_CACHE_WBACKWA))
+			cacheop = KGSL_CACHE_OP_CLEAN;
+
+		break;
+
+	case DRM_KGSL_GEM_CACHE_OP_FROM_DEV:
+		if (type & (DRM_KGSL_GEM_CACHE_WBACK |
+			    DRM_KGSL_GEM_CACHE_WBACKWA |
+			    DRM_KGSL_GEM_CACHE_WTHROUGH))
+			cacheop = KGSL_CACHE_OP_INV;
+	}
+
+	kgsl_cache_range_op(memdesc, cacheop);
+}
+
+/* TODO:
+ * Add vsync wait */
+
+static int kgsl_drm_load(struct drm_device *dev, unsigned long flags)
+{
+	return 0;
+}
+
+static int kgsl_drm_unload(struct drm_device *dev)
+{
+	return 0;
+}
+
+struct kgsl_drm_device_priv {
+	struct kgsl_device *device[KGSL_DEVICE_MAX];
+	struct kgsl_device_private *devpriv[KGSL_DEVICE_MAX];
+};
+
+void kgsl_drm_preclose(struct drm_device *dev, struct drm_file *file_priv)
+{
+}
+
+static int kgsl_drm_suspend(struct drm_device *dev, pm_message_t state)
+{
+	return 0;
+}
+
+static int kgsl_drm_resume(struct drm_device *dev)
+{
+	return 0;
+}
+
+static void
+kgsl_gem_free_mmap_offset(struct drm_gem_object *obj)
+{
+	struct drm_device *dev = obj->dev;
+	struct drm_gem_mm *mm = dev->mm_private;
+	struct drm_kgsl_gem_object *priv = obj->driver_private;
+	struct drm_map_list *list;
+
+	list = &obj->map_list;
+	drm_ht_remove_item(&mm->offset_hash, &list->hash);
+	if (list->file_offset_node) {
+		drm_mm_put_block(list->file_offset_node);
+		list->file_offset_node = NULL;
+	}
+
+	kfree(list->map);
+	list->map = NULL;
+
+	priv->mmap_offset = 0;
+}
+
+static int
+kgsl_gem_memory_allocated(struct drm_gem_object *obj)
+{
+	struct drm_kgsl_gem_object *priv = obj->driver_private;
+	return priv->memdesc.size ? 1 : 0;
+}
+
+static int
+kgsl_gem_alloc_memory(struct drm_gem_object *obj)
+{
+	struct drm_kgsl_gem_object *priv = obj->driver_private;
+	int index;
+	int result = 0;
+
+	/* Return if the memory is already allocated */
+
+	if (kgsl_gem_memory_allocated(obj) || TYPE_IS_FD(priv->type))
+		return 0;
+
+	if (priv->pagetable == NULL) {
+		priv->pagetable = kgsl_mmu_getpagetable(KGSL_MMU_GLOBAL_PT);
+
+		if (priv->pagetable == NULL) {
+			DRM_ERROR("Unable to get the GPU MMU pagetable\n");
+			return -EINVAL;
+		}
+	}
+
+	if (TYPE_IS_PMEM(priv->type)) {
+		int type;
+
+		if (priv->type == DRM_KGSL_GEM_TYPE_EBI ||
+		    priv->type & DRM_KGSL_GEM_PMEM_EBI) {
+				type = PMEM_MEMTYPE_EBI1;
+				result = kgsl_sharedmem_ebimem_user(
+						&priv->memdesc,
+						priv->pagetable,
+						obj->size * priv->bufcount,
+						0);
+				if (result) {
+					DRM_ERROR(
+					"Unable to allocate PMEM memory\n");
+					return result;
+				}
+		}
+		else
+			return -EINVAL;
+
+	} else if (TYPE_IS_MEM(priv->type)) {
+
+		if (priv->type == DRM_KGSL_GEM_TYPE_KMEM ||
+			priv->type & DRM_KGSL_GEM_CACHE_MASK)
+				list_add(&priv->list, &kgsl_mem_list);
+
+		result = kgsl_sharedmem_page_alloc_user(&priv->memdesc,
+					priv->pagetable,
+					obj->size * priv->bufcount, 0);
+
+		if (result != 0) {
+				DRM_ERROR(
+				"Unable to allocate Vmalloc user memory\n");
+				return result;
+		}
+	} else
+		return -EINVAL;
+
+	for (index = 0; index < priv->bufcount; index++) {
+		priv->bufs[index].offset = index * obj->size;
+		priv->bufs[index].gpuaddr =
+			priv->memdesc.gpuaddr +
+			priv->bufs[index].offset;
+	}
+	priv->flags |= DRM_KGSL_GEM_FLAG_MAPPED;
+
+	return 0;
+}
+
+static void
+kgsl_gem_free_memory(struct drm_gem_object *obj)
+{
+	struct drm_kgsl_gem_object *priv = obj->driver_private;
+
+	if (!kgsl_gem_memory_allocated(obj) || TYPE_IS_FD(priv->type))
+		return;
+
+	kgsl_gem_mem_flush(&priv->memdesc,  priv->type,
+			   DRM_KGSL_GEM_CACHE_OP_FROM_DEV);
+
+	kgsl_sharedmem_free(&priv->memdesc);
+
+	kgsl_mmu_putpagetable(priv->pagetable);
+	priv->pagetable = NULL;
+
+	if ((priv->type == DRM_KGSL_GEM_TYPE_KMEM) ||
+	    (priv->type & DRM_KGSL_GEM_CACHE_MASK))
+		list_del(&priv->list);
+
+	priv->flags &= ~DRM_KGSL_GEM_FLAG_MAPPED;
+
+}
+
+int
+kgsl_gem_init_object(struct drm_gem_object *obj)
+{
+	struct drm_kgsl_gem_object *priv;
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (priv == NULL) {
+		DRM_ERROR("Unable to create GEM object\n");
+		return -ENOMEM;
+	}
+
+	obj->driver_private = priv;
+	priv->obj = obj;
+
+	return 0;
+}
+
+void
+kgsl_gem_free_object(struct drm_gem_object *obj)
+{
+	kgsl_gem_free_memory(obj);
+	kgsl_gem_free_mmap_offset(obj);
+	drm_gem_object_release(obj);
+	kfree(obj->driver_private);
+}
+
+static int
+kgsl_gem_create_mmap_offset(struct drm_gem_object *obj)
+{
+	struct drm_device *dev = obj->dev;
+	struct drm_gem_mm *mm = dev->mm_private;
+	struct drm_kgsl_gem_object *priv = obj->driver_private;
+	struct drm_map_list *list;
+	int msize;
+
+	list = &obj->map_list;
+	list->map = kzalloc(sizeof(struct drm_map_list), GFP_KERNEL);
+	if (list->map == NULL) {
+		DRM_ERROR("Unable to allocate drm_map_list\n");
+		return -ENOMEM;
+	}
+
+	msize = obj->size * priv->bufcount;
+
+	list->map->type = _DRM_GEM;
+	list->map->size = msize;
+	list->map->handle = obj;
+
+	/* Allocate a mmap offset */
+	list->file_offset_node = drm_mm_search_free(&mm->offset_manager,
+						    msize / PAGE_SIZE,
+						    0, 0);
+
+	if (!list->file_offset_node) {
+		DRM_ERROR("Failed to allocate offset for %d\n", obj->name);
+		kfree(list->map);
+		return -ENOMEM;
+	}
+
+	list->file_offset_node = drm_mm_get_block(list->file_offset_node,
+						  msize / PAGE_SIZE, 0);
+
+	if (!list->file_offset_node) {
+		DRM_ERROR("Unable to create the file_offset_node\n");
+		kfree(list->map);
+		return -ENOMEM;
+	}
+
+	list->hash.key = list->file_offset_node->start;
+	if (drm_ht_insert_item(&mm->offset_hash, &list->hash)) {
+		DRM_ERROR("Failed to add to map hash\n");
+		drm_mm_put_block(list->file_offset_node);
+		kfree(list->map);
+		return -ENOMEM;
+	}
+
+	priv->mmap_offset = ((uint64_t) list->hash.key) << PAGE_SHIFT;
+
+	return 0;
+}
+
+int
+kgsl_gem_obj_addr(int drm_fd, int handle, unsigned long *start,
+			unsigned long *len)
+{
+	struct file *filp;
+	struct drm_device *dev;
+	struct drm_file *file_priv;
+	struct drm_gem_object *obj;
+	struct drm_kgsl_gem_object *priv;
+	int ret = 0;
+
+	filp = fget(drm_fd);
+	if (unlikely(filp == NULL)) {
+		DRM_ERROR("Unable to get the DRM file descriptor\n");
+		return -EINVAL;
+	}
+	file_priv = filp->private_data;
+	if (unlikely(file_priv == NULL)) {
+		DRM_ERROR("Unable to get the file private data\n");
+		fput(filp);
+		return -EINVAL;
+	}
+	dev = file_priv->minor->dev;
+	if (unlikely(dev == NULL)) {
+		DRM_ERROR("Unable to get the minor device\n");
+		fput(filp);
+		return -EINVAL;
+	}
+
+	obj = drm_gem_object_lookup(dev, file_priv, handle);
+	if (unlikely(obj == NULL)) {
+		DRM_ERROR("Invalid GEM handle %x\n", handle);
+		fput(filp);
+		return -EBADF;
+	}
+
+	mutex_lock(&dev->struct_mutex);
+	priv = obj->driver_private;
+
+	/* We can only use the MDP for PMEM regions */
+
+	if (TYPE_IS_PMEM(priv->type)) {
+		*start = priv->memdesc.physaddr +
+			priv->bufs[priv->active].offset;
+
+		*len = priv->memdesc.size;
+
+		kgsl_gem_mem_flush(&priv->memdesc,
+				   priv->type, DRM_KGSL_GEM_CACHE_OP_TO_DEV);
+	} else {
+		*start = 0;
+		*len = 0;
+		ret = -EINVAL;
+	}
+
+	drm_gem_object_unreference(obj);
+	mutex_unlock(&dev->struct_mutex);
+
+	fput(filp);
+	return ret;
+}
+
+static int
+kgsl_gem_init_obj(struct drm_device *dev,
+		  struct drm_file *file_priv,
+		  struct drm_gem_object *obj,
+		  int *handle)
+{
+	struct drm_kgsl_gem_object *priv;
+	int ret, i;
+
+	mutex_lock(&dev->struct_mutex);
+	priv = obj->driver_private;
+
+	memset(&priv->memdesc, 0, sizeof(priv->memdesc));
+	priv->bufcount = 1;
+	priv->active = 0;
+	priv->bound = 0;
+
+	/* To preserve backwards compatability, the default memory source
+	   is EBI */
+
+	priv->type = DRM_KGSL_GEM_TYPE_PMEM | DRM_KGSL_GEM_PMEM_EBI;
+
+	ret = drm_gem_handle_create(file_priv, obj, handle);
+
+	drm_gem_object_unreference(obj);
+	INIT_LIST_HEAD(&priv->wait_list);
+
+	for (i = 0; i < DRM_KGSL_HANDLE_WAIT_ENTRIES; i++) {
+		INIT_LIST_HEAD((struct list_head *) &priv->wait_entries[i]);
+		priv->wait_entries[i].pid = 0;
+		init_waitqueue_head(&priv->wait_entries[i].process_wait_q);
+	}
+
+	for (i = 0; i < DRM_KGSL_NUM_FENCE_ENTRIES; i++) {
+		INIT_LIST_HEAD((struct list_head *) &priv->fence_entries[i]);
+		priv->fence_entries[i].in_use = 0;
+		priv->fence_entries[i].gem_obj = obj;
+	}
+
+	mutex_unlock(&dev->struct_mutex);
+	return ret;
+}
+
+int
+kgsl_gem_create_ioctl(struct drm_device *dev, void *data,
+		      struct drm_file *file_priv)
+{
+	struct drm_kgsl_gem_create *create = data;
+	struct drm_gem_object *obj;
+	int ret, handle;
+
+	/* Page align the size so we can allocate multiple buffers */
+	create->size = ALIGN(create->size, 4096);
+
+	obj = drm_gem_object_alloc(dev, create->size);
+
+	if (obj == NULL) {
+		DRM_ERROR("Unable to allocate the GEM object\n");
+		return -ENOMEM;
+	}
+
+	ret = kgsl_gem_init_obj(dev, file_priv, obj, &handle);
+	if (ret)
+		return ret;
+
+	create->handle = handle;
+	return 0;
+}
+
+int
+kgsl_gem_create_fd_ioctl(struct drm_device *dev, void *data,
+			      struct drm_file *file_priv)
+{
+	struct drm_kgsl_gem_create_fd *args = data;
+	struct file *file;
+	dev_t rdev;
+	struct fb_info *info;
+	struct drm_gem_object *obj;
+	struct drm_kgsl_gem_object *priv;
+	int ret, put_needed, handle;
+
+	file = fget_light(args->fd, &put_needed);
+
+	if (file == NULL) {
+		DRM_ERROR("Unable to get the file object\n");
+		return -EBADF;
+	}
+
+	rdev = file->f_dentry->d_inode->i_rdev;
+
+	/* Only framebuffer objects are supported ATM */
+
+	if (MAJOR(rdev) != FB_MAJOR) {
+		DRM_ERROR("File descriptor is not a framebuffer\n");
+		ret = -EBADF;
+		goto error_fput;
+	}
+
+	info = registered_fb[MINOR(rdev)];
+
+	if (info == NULL) {
+		DRM_ERROR("Framebuffer minor %d is not registered\n",
+			  MINOR(rdev));
+		ret = -EBADF;
+		goto error_fput;
+	}
+
+	obj = drm_gem_object_alloc(dev, info->fix.smem_len);
+
+	if (obj == NULL) {
+		DRM_ERROR("Unable to allocate GEM object\n");
+		ret = -ENOMEM;
+		goto error_fput;
+	}
+
+	ret = kgsl_gem_init_obj(dev, file_priv, obj, &handle);
+
+	if (ret)
+		goto error_fput;
+
+	mutex_lock(&dev->struct_mutex);
+
+	priv = obj->driver_private;
+	priv->memdesc.physaddr = info->fix.smem_start;
+	priv->type = DRM_KGSL_GEM_TYPE_FD_FBMEM;
+
+	mutex_unlock(&dev->struct_mutex);
+	args->handle = handle;
+
+error_fput:
+	fput_light(file, put_needed);
+
+	return ret;
+}
+
+int
+kgsl_gem_setmemtype_ioctl(struct drm_device *dev, void *data,
+			struct drm_file *file_priv)
+{
+	struct drm_kgsl_gem_memtype *args = data;
+	struct drm_gem_object *obj;
+	struct drm_kgsl_gem_object *priv;
+	int ret = 0;
+
+	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+
+	if (obj == NULL) {
+		DRM_ERROR("Invalid GEM handle %x\n", args->handle);
+		return -EBADF;
+	}
+
+	mutex_lock(&dev->struct_mutex);
+	priv = obj->driver_private;
+
+	if (TYPE_IS_FD(priv->type))
+		ret = -EINVAL;
+	else {
+		if (TYPE_IS_PMEM(args->type) || TYPE_IS_MEM(args->type))
+			priv->type = args->type;
+		else
+			ret = -EINVAL;
+	}
+
+	drm_gem_object_unreference(obj);
+	mutex_unlock(&dev->struct_mutex);
+
+	return ret;
+}
+
+int
+kgsl_gem_getmemtype_ioctl(struct drm_device *dev, void *data,
+			  struct drm_file *file_priv)
+{
+	struct drm_kgsl_gem_memtype *args = data;
+	struct drm_gem_object *obj;
+	struct drm_kgsl_gem_object *priv;
+
+	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+
+	if (obj == NULL) {
+		DRM_ERROR("Invalid GEM handle %x\n", args->handle);
+		return -EBADF;
+	}
+
+	mutex_lock(&dev->struct_mutex);
+	priv = obj->driver_private;
+
+	args->type = priv->type;
+
+	drm_gem_object_unreference(obj);
+	mutex_unlock(&dev->struct_mutex);
+
+	return 0;
+}
+
+int
+kgsl_gem_unbind_gpu_ioctl(struct drm_device *dev, void *data,
+			struct drm_file *file_priv)
+{
+	return 0;
+}
+
+int
+kgsl_gem_bind_gpu_ioctl(struct drm_device *dev, void *data,
+			struct drm_file *file_priv)
+{
+	return 0;
+}
+
+/* Allocate the memory and prepare it for CPU mapping */
+
+int
+kgsl_gem_alloc_ioctl(struct drm_device *dev, void *data,
+		    struct drm_file *file_priv)
+{
+	struct drm_kgsl_gem_alloc *args = data;
+	struct drm_gem_object *obj;
+	struct drm_kgsl_gem_object *priv;
+	int ret;
+
+	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+
+	if (obj == NULL) {
+		DRM_ERROR("Invalid GEM handle %x\n", args->handle);
+		return -EBADF;
+	}
+
+	mutex_lock(&dev->struct_mutex);
+	priv = obj->driver_private;
+
+	ret = kgsl_gem_alloc_memory(obj);
+
+	if (ret) {
+		DRM_ERROR("Unable to allocate object memory\n");
+	} else if (!priv->mmap_offset) {
+		ret = kgsl_gem_create_mmap_offset(obj);
+		if (ret)
+			DRM_ERROR("Unable to create a mmap offset\n");
+	}
+
+	args->offset = priv->mmap_offset;
+
+	drm_gem_object_unreference(obj);
+	mutex_unlock(&dev->struct_mutex);
+
+	return ret;
+}
+
+int
+kgsl_gem_mmap_ioctl(struct drm_device *dev, void *data,
+			struct drm_file *file_priv)
+{
+	struct drm_kgsl_gem_mmap *args = data;
+	struct drm_gem_object *obj;
+	unsigned long addr;
+
+	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+
+	if (obj == NULL) {
+		DRM_ERROR("Invalid GEM handle %x\n", args->handle);
+		return -EBADF;
+	}
+
+	down_write(&current->mm->mmap_sem);
+
+	addr = do_mmap(obj->filp, 0, args->size,
+		       PROT_READ | PROT_WRITE, MAP_SHARED,
+		       args->offset);
+
+	up_write(&current->mm->mmap_sem);
+
+	mutex_lock(&dev->struct_mutex);
+	drm_gem_object_unreference(obj);
+	mutex_unlock(&dev->struct_mutex);
+
+	if (IS_ERR((void *) addr))
+		return addr;
+
+	args->hostptr = (uint32_t) addr;
+	return 0;
+}
+
+/* This function is deprecated */
+
+int
+kgsl_gem_prep_ioctl(struct drm_device *dev, void *data,
+			struct drm_file *file_priv)
+{
+	struct drm_kgsl_gem_prep *args = data;
+	struct drm_gem_object *obj;
+	struct drm_kgsl_gem_object *priv;
+	int ret;
+
+	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+
+	if (obj == NULL) {
+		DRM_ERROR("Invalid GEM handle %x\n", args->handle);
+		return -EBADF;
+	}
+
+	mutex_lock(&dev->struct_mutex);
+	priv = obj->driver_private;
+
+	ret = kgsl_gem_alloc_memory(obj);
+	if (ret) {
+		DRM_ERROR("Unable to allocate object memory\n");
+		drm_gem_object_unreference(obj);
+		mutex_unlock(&dev->struct_mutex);
+		return ret;
+	}
+
+	if (priv->mmap_offset == 0) {
+		ret = kgsl_gem_create_mmap_offset(obj);
+		if (ret) {
+			drm_gem_object_unreference(obj);
+			mutex_unlock(&dev->struct_mutex);
+			return ret;
+		}
+	}
+
+	args->offset = priv->mmap_offset;
+	args->phys = priv->memdesc.physaddr;
+
+	drm_gem_object_unreference(obj);
+	mutex_unlock(&dev->struct_mutex);
+
+	return 0;
+}
+
+int
+kgsl_gem_get_bufinfo_ioctl(struct drm_device *dev, void *data,
+			   struct drm_file *file_priv)
+{
+	struct drm_kgsl_gem_bufinfo *args = data;
+	struct drm_gem_object *obj;
+	struct drm_kgsl_gem_object *priv;
+	int ret = -EINVAL;
+	int index;
+
+	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+
+	if (obj == NULL) {
+		DRM_ERROR("Invalid GEM handle %x\n", args->handle);
+		return -EBADF;
+	}
+
+	mutex_lock(&dev->struct_mutex);
+	priv = obj->driver_private;
+
+	if (!kgsl_gem_memory_allocated(obj)) {
+		DRM_ERROR("Memory not allocated for this object\n");
+		goto out;
+	}
+
+	for (index = 0; index < priv->bufcount; index++) {
+		args->offset[index] = priv->bufs[index].offset;
+		args->gpuaddr[index] = priv->bufs[index].gpuaddr;
+	}
+
+	args->count = priv->bufcount;
+	args->active = priv->active;
+
+	ret = 0;
+
+out:
+	drm_gem_object_unreference(obj);
+	mutex_unlock(&dev->struct_mutex);
+
+	return ret;
+}
+
+int
+kgsl_gem_set_bufcount_ioctl(struct drm_device *dev, void *data,
+			  struct drm_file *file_priv)
+{
+	struct drm_kgsl_gem_bufcount *args = data;
+	struct drm_gem_object *obj;
+	struct drm_kgsl_gem_object *priv;
+	int ret = -EINVAL;
+
+	if (args->bufcount < 1 || args->bufcount > DRM_KGSL_GEM_MAX_BUFFERS)
+		return -EINVAL;
+
+	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+
+	if (obj == NULL) {
+		DRM_ERROR("Invalid GEM handle %x\n", args->handle);
+		return -EBADF;
+	}
+
+	mutex_lock(&dev->struct_mutex);
+	priv = obj->driver_private;
+
+	/* It is too much math to worry about what happens if we are already
+	   allocated, so just bail if we are */
+
+	if (kgsl_gem_memory_allocated(obj)) {
+		DRM_ERROR("Memory already allocated - cannot change"
+			  "number of buffers\n");
+		goto out;
+	}
+
+	priv->bufcount = args->bufcount;
+	ret = 0;
+
+out:
+	drm_gem_object_unreference(obj);
+	mutex_unlock(&dev->struct_mutex);
+
+	return ret;
+}
+
+int
+kgsl_gem_set_active_ioctl(struct drm_device *dev, void *data,
+			  struct drm_file *file_priv)
+{
+	struct drm_kgsl_gem_active *args = data;
+	struct drm_gem_object *obj;
+	struct drm_kgsl_gem_object *priv;
+	int ret = -EINVAL;
+
+	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+
+	if (obj == NULL) {
+		DRM_ERROR("Invalid GEM handle %x\n", args->handle);
+		return -EBADF;
+	}
+
+	mutex_lock(&dev->struct_mutex);
+	priv = obj->driver_private;
+
+	if (args->active < 0 || args->active >= priv->bufcount) {
+		DRM_ERROR("Invalid active buffer %d\n", args->active);
+		goto out;
+	}
+
+	priv->active = args->active;
+	ret = 0;
+
+out:
+	drm_gem_object_unreference(obj);
+	mutex_unlock(&dev->struct_mutex);
+
+	return ret;
+}
+
+int kgsl_gem_kmem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+	struct drm_gem_object *obj = vma->vm_private_data;
+	struct drm_device *dev = obj->dev;
+	struct drm_kgsl_gem_object *priv;
+	unsigned long offset;
+	struct page *page;
+	int i;
+
+	mutex_lock(&dev->struct_mutex);
+
+	priv = obj->driver_private;
+
+	offset = (unsigned long) vmf->virtual_address - vma->vm_start;
+	i = offset >> PAGE_SHIFT;
+	page = sg_page(&(priv->memdesc.sg[i]));
+
+	if (!page) {
+		mutex_unlock(&dev->struct_mutex);
+		return VM_FAULT_SIGBUS;
+	}
+
+	get_page(page);
+	vmf->page = page;
+
+	mutex_unlock(&dev->struct_mutex);
+	return 0;
+}
+
+int kgsl_gem_phys_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+	struct drm_gem_object *obj = vma->vm_private_data;
+	struct drm_device *dev = obj->dev;
+	struct drm_kgsl_gem_object *priv;
+	unsigned long offset, pfn;
+	int ret = 0;
+
+	offset = ((unsigned long) vmf->virtual_address - vma->vm_start) >>
+		PAGE_SHIFT;
+
+	mutex_lock(&dev->struct_mutex);
+
+	priv = obj->driver_private;
+
+	pfn = (priv->memdesc.physaddr >> PAGE_SHIFT) + offset;
+	ret = vm_insert_pfn(vma,
+			    (unsigned long) vmf->virtual_address, pfn);
+	mutex_unlock(&dev->struct_mutex);
+
+	switch (ret) {
+	case -ENOMEM:
+	case -EAGAIN:
+		return VM_FAULT_OOM;
+	case -EFAULT:
+		return VM_FAULT_SIGBUS;
+	default:
+		return VM_FAULT_NOPAGE;
+	}
+}
+
+static struct vm_operations_struct kgsl_gem_kmem_vm_ops = {
+	.fault = kgsl_gem_kmem_fault,
+	.open = drm_gem_vm_open,
+	.close = drm_gem_vm_close,
+};
+
+static struct vm_operations_struct kgsl_gem_phys_vm_ops = {
+	.fault = kgsl_gem_phys_fault,
+	.open = drm_gem_vm_open,
+	.close = drm_gem_vm_close,
+};
+
+/* This is a clone of the standard drm_gem_mmap function modified to allow
+   us to properly map KMEM regions as well as the PMEM regions */
+
+int msm_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	struct drm_file *priv = filp->private_data;
+	struct drm_device *dev = priv->minor->dev;
+	struct drm_gem_mm *mm = dev->mm_private;
+	struct drm_local_map *map = NULL;
+	struct drm_gem_object *obj;
+	struct drm_hash_item *hash;
+	struct drm_kgsl_gem_object *gpriv;
+	int ret = 0;
+
+	mutex_lock(&dev->struct_mutex);
+
+	if (drm_ht_find_item(&mm->offset_hash, vma->vm_pgoff, &hash)) {
+		mutex_unlock(&dev->struct_mutex);
+		return drm_mmap(filp, vma);
+	}
+
+	map = drm_hash_entry(hash, struct drm_map_list, hash)->map;
+	if (!map ||
+	    ((map->flags & _DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN))) {
+		ret =  -EPERM;
+		goto out_unlock;
+	}
+
+	/* Check for valid size. */
+	if (map->size < vma->vm_end - vma->vm_start) {
+		ret = -EINVAL;
+		goto out_unlock;
+	}
+
+	obj = map->handle;
+
+	gpriv = obj->driver_private;
+
+	/* VM_PFNMAP is only for memory that doesn't use struct page
+	 * in other words, not "normal" memory.  If you try to use it
+	 * with "normal" memory then the mappings don't get flushed. */
+
+	if (TYPE_IS_MEM(gpriv->type)) {
+		vma->vm_flags |= VM_RESERVED | VM_DONTEXPAND;
+		vma->vm_ops = &kgsl_gem_kmem_vm_ops;
+	} else {
+		vma->vm_flags |= VM_RESERVED | VM_IO | VM_PFNMAP |
+			VM_DONTEXPAND;
+		vma->vm_ops = &kgsl_gem_phys_vm_ops;
+	}
+
+	vma->vm_private_data = map->handle;
+
+
+	/* Take care of requested caching policy */
+	if (gpriv->type == DRM_KGSL_GEM_TYPE_KMEM ||
+	    gpriv->type & DRM_KGSL_GEM_CACHE_MASK) {
+		if (gpriv->type & DRM_KGSL_GEM_CACHE_WBACKWA)
+			vma->vm_page_prot =
+			pgprot_writebackwacache(vma->vm_page_prot);
+		else if (gpriv->type & DRM_KGSL_GEM_CACHE_WBACK)
+				vma->vm_page_prot =
+				pgprot_writebackcache(vma->vm_page_prot);
+		else if (gpriv->type & DRM_KGSL_GEM_CACHE_WTHROUGH)
+				vma->vm_page_prot =
+				pgprot_writethroughcache(vma->vm_page_prot);
+		else
+			vma->vm_page_prot =
+			pgprot_writecombine(vma->vm_page_prot);
+	} else {
+		if (gpriv->type == DRM_KGSL_GEM_TYPE_KMEM_NOCACHE)
+			vma->vm_page_prot =
+			pgprot_noncached(vma->vm_page_prot);
+		else
+			/* default pmem is WC */
+			vma->vm_page_prot =
+			pgprot_writecombine(vma->vm_page_prot);
+	}
+
+	/* flush out existing KMEM cached mappings if new ones are
+	 * of uncached type */
+	if (IS_MEM_UNCACHED(gpriv->type))
+		kgsl_cache_range_op(&gpriv->memdesc,
+				    KGSL_CACHE_OP_FLUSH);
+
+	/* Add the other memory types here */
+
+	/* Take a ref for this mapping of the object, so that the fault
+	 * handler can dereference the mmap offset's pointer to the object.
+	 * This reference is cleaned up by the corresponding vm_close
+	 * (which should happen whether the vma was created by this call, or
+	 * by a vm_open due to mremap or partial unmap or whatever).
+	 */
+	drm_gem_object_reference(obj);
+
+	vma->vm_file = filp;	/* Needed for drm_vm_open() */
+	drm_vm_open_locked(vma);
+
+out_unlock:
+	mutex_unlock(&dev->struct_mutex);
+
+	return ret;
+}
+
+void
+cleanup_fence(struct drm_kgsl_gem_object_fence *fence, int check_waiting)
+{
+	int j;
+	struct drm_kgsl_gem_object_fence_list_entry *this_fence_entry = NULL;
+	struct drm_kgsl_gem_object *unlock_obj;
+	struct drm_gem_object *obj;
+	struct drm_kgsl_gem_object_wait_list_entry *lock_next;
+
+	fence->ts_valid = 0;
+	fence->timestamp = -1;
+	fence->ts_device = -1;
+
+	/* Walk the list of buffers in this fence and clean up the */
+	/* references. Note that this can cause memory allocations */
+	/* to be freed */
+	for (j = fence->num_buffers; j > 0; j--) {
+		this_fence_entry =
+				(struct drm_kgsl_gem_object_fence_list_entry *)
+				fence->buffers_in_fence.prev;
+
+		this_fence_entry->in_use = 0;
+		obj = this_fence_entry->gem_obj;
+		unlock_obj = obj->driver_private;
+
+		/* Delete it from the list */
+
+		list_del(&this_fence_entry->list);
+
+		/* we are unlocking - see if there are other pids waiting */
+		if (check_waiting) {
+			if (!list_empty(&unlock_obj->wait_list)) {
+				lock_next =
+				(struct drm_kgsl_gem_object_wait_list_entry *)
+					unlock_obj->wait_list.prev;
+
+				list_del((struct list_head *)&lock_next->list);
+
+				unlock_obj->lockpid = 0;
+				wake_up_interruptible(
+						&lock_next->process_wait_q);
+				lock_next->pid = 0;
+
+			} else {
+				/* List is empty so set pid to 0 */
+				unlock_obj->lockpid = 0;
+			}
+		}
+
+		drm_gem_object_unreference(obj);
+	}
+	/* here all the buffers in the fence are released */
+	/* clear the fence entry */
+	fence->fence_id = ENTRY_EMPTY;
+}
+
+int
+find_empty_fence(void)
+{
+	int i;
+
+	for (i = 0; i < DRM_KGSL_NUM_FENCE_ENTRIES; i++) {
+		if (gem_buf_fence[i].fence_id == ENTRY_EMPTY) {
+			gem_buf_fence[i].fence_id = fence_id++;
+			gem_buf_fence[i].ts_valid = 0;
+			INIT_LIST_HEAD(&(gem_buf_fence[i].buffers_in_fence));
+			if (fence_id == 0xFFFFFFF0)
+				fence_id = 1;
+			return i;
+		} else {
+
+			/* Look for entries to be cleaned up */
+			if (gem_buf_fence[i].fence_id == ENTRY_NEEDS_CLEANUP)
+				cleanup_fence(&gem_buf_fence[i], 0);
+		}
+	}
+
+	return ENTRY_EMPTY;
+}
+
+int
+find_fence(int index)
+{
+	int i;
+
+	for (i = 0; i < DRM_KGSL_NUM_FENCE_ENTRIES; i++) {
+		if (gem_buf_fence[i].fence_id == index)
+			return i;
+	}
+
+	return ENTRY_EMPTY;
+}
+
+void
+wakeup_fence_entries(struct drm_kgsl_gem_object_fence *fence)
+{
+    struct drm_kgsl_gem_object_fence_list_entry *this_fence_entry = NULL;
+	struct drm_kgsl_gem_object_wait_list_entry *lock_next;
+	struct drm_kgsl_gem_object *unlock_obj;
+	struct drm_gem_object *obj;
+
+	/* TS has expired when we get here */
+	fence->ts_valid = 0;
+	fence->timestamp = -1;
+	fence->ts_device = -1;
+
+	list_for_each_entry(this_fence_entry, &fence->buffers_in_fence, list) {
+		obj = this_fence_entry->gem_obj;
+		unlock_obj = obj->driver_private;
+
+		if (!list_empty(&unlock_obj->wait_list)) {
+			lock_next =
+				(struct drm_kgsl_gem_object_wait_list_entry *)
+					unlock_obj->wait_list.prev;
+
+			/* Unblock the pid */
+			lock_next->pid = 0;
+
+			/* Delete it from the list */
+			list_del((struct list_head *)&lock_next->list);
+
+			unlock_obj->lockpid = 0;
+			wake_up_interruptible(&lock_next->process_wait_q);
+
+		} else {
+			/* List is empty so set pid to 0 */
+			unlock_obj->lockpid = 0;
+		}
+	}
+	fence->fence_id = ENTRY_NEEDS_CLEANUP;  /* Mark it as needing cleanup */
+}
+
+int
+kgsl_gem_lock_handle_ioctl(struct drm_device *dev, void *data,
+						   struct drm_file *file_priv)
+{
+	/* The purpose of this function is to lock a given set of handles. */
+	/* The driver will maintain a list of locked handles. */
+	/* If a request comes in for a handle that's locked the thread will */
+	/* block until it's no longer in use. */
+
+	struct drm_kgsl_gem_lock_handles *args = data;
+	struct drm_gem_object *obj;
+	struct drm_kgsl_gem_object *priv;
+	struct drm_kgsl_gem_object_fence_list_entry *this_fence_entry = NULL;
+	struct drm_kgsl_gem_object_fence *fence;
+	struct drm_kgsl_gem_object_wait_list_entry *lock_item;
+	int i, j;
+	int result = 0;
+	uint32_t *lock_list;
+	uint32_t *work_list = NULL;
+	int32_t fence_index;
+
+	/* copy in the data from user space */
+	lock_list = kzalloc(sizeof(uint32_t) * args->num_handles, GFP_KERNEL);
+	if (!lock_list) {
+		DRM_ERROR("Unable allocate memory for lock list\n");
+		result = -ENOMEM;
+		goto error;
+	}
+
+	if (copy_from_user(lock_list, args->handle_list,
+			   sizeof(uint32_t) * args->num_handles)) {
+		DRM_ERROR("Unable to copy the lock list from the user\n");
+		result = -EFAULT;
+		goto free_handle_list;
+	}
+
+
+	work_list = lock_list;
+	mutex_lock(&dev->struct_mutex);
+
+	/* build the fence for this group of handles */
+	fence_index = find_empty_fence();
+	if (fence_index == ENTRY_EMPTY) {
+		DRM_ERROR("Unable to find a empty fence\n");
+		args->lock_id = 0xDEADBEEF;
+		result = -EFAULT;
+		goto out_unlock;
+	}
+
+	fence = &gem_buf_fence[fence_index];
+	gem_buf_fence[fence_index].num_buffers = args->num_handles;
+	args->lock_id = gem_buf_fence[fence_index].fence_id;
+
+	for (j = args->num_handles; j > 0; j--, lock_list++) {
+		obj = drm_gem_object_lookup(dev, file_priv, *lock_list);
+
+		if (obj == NULL) {
+			DRM_ERROR("Invalid GEM handle %x\n", *lock_list);
+			result = -EBADF;
+			goto out_unlock;
+		}
+
+		priv = obj->driver_private;
+		this_fence_entry = NULL;
+
+		/* get a fence entry to hook into the fence */
+		for (i = 0; i < DRM_KGSL_NUM_FENCE_ENTRIES; i++) {
+			if (!priv->fence_entries[i].in_use) {
+				this_fence_entry = &priv->fence_entries[i];
+				this_fence_entry->in_use = 1;
+				break;
+			}
+		}
+
+		if (this_fence_entry == NULL) {
+			fence->num_buffers = 0;
+			fence->fence_id = ENTRY_EMPTY;
+			args->lock_id = 0xDEADBEAD;
+			result = -EFAULT;
+			drm_gem_object_unreference(obj);
+			goto out_unlock;
+		}
+
+		/* We're trying to lock - add to a fence */
+		list_add((struct list_head *)this_fence_entry,
+				 &gem_buf_fence[fence_index].buffers_in_fence);
+		if (priv->lockpid) {
+
+			if (priv->lockpid == args->pid) {
+				/* now that things are running async this  */
+				/* happens when an op isn't done */
+				/* so it's already locked by the calling pid */
+					continue;
+			}
+
+
+			/* if a pid already had it locked */
+			/* create and add to wait list */
+			for (i = 0; i < DRM_KGSL_HANDLE_WAIT_ENTRIES; i++) {
+				if (priv->wait_entries[i].in_use == 0) {
+					/* this one is empty */
+					lock_item = &priv->wait_entries[i];
+				    lock_item->in_use = 1;
+					lock_item->pid = args->pid;
+					INIT_LIST_HEAD((struct list_head *)
+						&priv->wait_entries[i]);
+					break;
+				}
+			}
+
+			if (i == DRM_KGSL_HANDLE_WAIT_ENTRIES) {
+
+				result =  -EFAULT;
+				drm_gem_object_unreference(obj);
+				goto out_unlock;
+			}
+
+			list_add_tail((struct list_head *)&lock_item->list,
+							&priv->wait_list);
+			mutex_unlock(&dev->struct_mutex);
+			/* here we need to block */
+			wait_event_interruptible_timeout(
+					priv->wait_entries[i].process_wait_q,
+					(priv->lockpid == 0),
+					msecs_to_jiffies(64));
+			mutex_lock(&dev->struct_mutex);
+			lock_item->in_use = 0;
+		}
+
+		/* Getting here means no one currently holds the lock */
+		priv->lockpid = args->pid;
+
+		args->lock_id = gem_buf_fence[fence_index].fence_id;
+	}
+	fence->lockpid = args->pid;
+
+out_unlock:
+	mutex_unlock(&dev->struct_mutex);
+
+free_handle_list:
+	kfree(work_list);
+
+error:
+	return result;
+}
+
+int
+kgsl_gem_unlock_handle_ioctl(struct drm_device *dev, void *data,
+			 struct drm_file *file_priv)
+{
+	struct drm_kgsl_gem_unlock_handles *args = data;
+	int result = 0;
+	int32_t fence_index;
+
+	mutex_lock(&dev->struct_mutex);
+	fence_index = find_fence(args->lock_id);
+	if (fence_index == ENTRY_EMPTY) {
+		DRM_ERROR("Invalid lock ID: %x\n", args->lock_id);
+		result = -EFAULT;
+		goto out_unlock;
+	}
+
+	cleanup_fence(&gem_buf_fence[fence_index], 1);
+
+out_unlock:
+	mutex_unlock(&dev->struct_mutex);
+
+	return result;
+}
+
+
+int
+kgsl_gem_unlock_on_ts_ioctl(struct drm_device *dev, void *data,
+			struct drm_file *file_priv)
+{
+	struct drm_kgsl_gem_unlock_on_ts *args = data;
+	int result = 0;
+	int ts_done = 0;
+	int32_t fence_index, ts_device;
+	struct drm_kgsl_gem_object_fence *fence;
+	struct kgsl_device *device;
+
+	if (args->type == DRM_KGSL_GEM_TS_3D)
+		ts_device = KGSL_DEVICE_3D0;
+	else if (args->type == DRM_KGSL_GEM_TS_2D)
+		ts_device = KGSL_DEVICE_2D0;
+	else {
+		result = -EINVAL;
+		goto error;
+	}
+
+	device = kgsl_get_device(ts_device);
+	ts_done = kgsl_check_timestamp(device, NULL, args->timestamp);
+
+	mutex_lock(&dev->struct_mutex);
+
+	fence_index = find_fence(args->lock_id);
+	if (fence_index == ENTRY_EMPTY) {
+		DRM_ERROR("Invalid lock ID: %x\n", args->lock_id);
+		result = -EFAULT;
+		goto out_unlock;
+	}
+
+	fence = &gem_buf_fence[fence_index];
+	fence->ts_device = ts_device;
+
+	if (!ts_done)
+		fence->ts_valid = 1;
+	else
+		cleanup_fence(fence, 1);
+
+
+out_unlock:
+	mutex_unlock(&dev->struct_mutex);
+
+error:
+	return result;
+}
+
+struct drm_ioctl_desc kgsl_drm_ioctls[] = {
+	DRM_IOCTL_DEF_DRV(KGSL_GEM_CREATE, kgsl_gem_create_ioctl, 0),
+	DRM_IOCTL_DEF_DRV(KGSL_GEM_PREP, kgsl_gem_prep_ioctl, 0),
+	DRM_IOCTL_DEF_DRV(KGSL_GEM_SETMEMTYPE, kgsl_gem_setmemtype_ioctl, 0),
+	DRM_IOCTL_DEF_DRV(KGSL_GEM_GETMEMTYPE, kgsl_gem_getmemtype_ioctl, 0),
+	DRM_IOCTL_DEF_DRV(KGSL_GEM_BIND_GPU, kgsl_gem_bind_gpu_ioctl, 0),
+	DRM_IOCTL_DEF_DRV(KGSL_GEM_UNBIND_GPU, kgsl_gem_unbind_gpu_ioctl, 0),
+	DRM_IOCTL_DEF_DRV(KGSL_GEM_ALLOC, kgsl_gem_alloc_ioctl, 0),
+	DRM_IOCTL_DEF_DRV(KGSL_GEM_MMAP, kgsl_gem_mmap_ioctl, 0),
+	DRM_IOCTL_DEF_DRV(KGSL_GEM_GET_BUFINFO, kgsl_gem_get_bufinfo_ioctl, 0),
+	DRM_IOCTL_DEF_DRV(KGSL_GEM_SET_BUFCOUNT,
+		      kgsl_gem_set_bufcount_ioctl, 0),
+	DRM_IOCTL_DEF_DRV(KGSL_GEM_SET_ACTIVE, kgsl_gem_set_active_ioctl, 0),
+	DRM_IOCTL_DEF_DRV(KGSL_GEM_LOCK_HANDLE,
+				  kgsl_gem_lock_handle_ioctl, 0),
+	DRM_IOCTL_DEF_DRV(KGSL_GEM_UNLOCK_HANDLE,
+				  kgsl_gem_unlock_handle_ioctl, 0),
+	DRM_IOCTL_DEF_DRV(KGSL_GEM_UNLOCK_ON_TS,
+				  kgsl_gem_unlock_on_ts_ioctl, 0),
+	DRM_IOCTL_DEF_DRV(KGSL_GEM_CREATE_FD, kgsl_gem_create_fd_ioctl,
+		      DRM_MASTER),
+};
+
+static struct drm_driver driver = {
+	.driver_features = DRIVER_GEM,
+	.load = kgsl_drm_load,
+	.unload = kgsl_drm_unload,
+	.preclose = kgsl_drm_preclose,
+	.suspend = kgsl_drm_suspend,
+	.resume = kgsl_drm_resume,
+	.reclaim_buffers = drm_core_reclaim_buffers,
+	.gem_init_object = kgsl_gem_init_object,
+	.gem_free_object = kgsl_gem_free_object,
+	.ioctls = kgsl_drm_ioctls,
+
+	.fops = {
+		 .owner = THIS_MODULE,
+		 .open = drm_open,
+		 .release = drm_release,
+		 .unlocked_ioctl = drm_ioctl,
+		 .mmap = msm_drm_gem_mmap,
+		 .poll = drm_poll,
+		 .fasync = drm_fasync,
+		 },
+
+	.name = DRIVER_NAME,
+	.desc = DRIVER_DESC,
+	.date = DRIVER_DATE,
+	.major = DRIVER_MAJOR,
+	.minor = DRIVER_MINOR,
+	.patchlevel = DRIVER_PATCHLEVEL,
+};
+
+int kgsl_drm_init(struct platform_device *dev)
+{
+	int i;
+
+	/* Only initialize once */
+	if (kgsl_drm_inited == DRM_KGSL_INITED)
+		return 0;
+
+	kgsl_drm_inited = DRM_KGSL_INITED;
+
+	driver.num_ioctls = DRM_ARRAY_SIZE(kgsl_drm_ioctls);
+
+	INIT_LIST_HEAD(&kgsl_mem_list);
+
+	for (i = 0; i < DRM_KGSL_NUM_FENCE_ENTRIES; i++) {
+		gem_buf_fence[i].num_buffers = 0;
+		gem_buf_fence[i].ts_valid = 0;
+		gem_buf_fence[i].fence_id = ENTRY_EMPTY;
+	}
+
+	return drm_platform_init(&driver, dev);
+}
+
+void kgsl_drm_exit(void)
+{
+	kgsl_drm_inited = DRM_KGSL_NOT_INITED;
+	drm_platform_exit(&driver, driver.kdriver.platform_device);
+}
diff --git a/drivers/gpu/msm/kgsl_gpummu.c b/drivers/gpu/msm/kgsl_gpummu.c
new file mode 100644
index 0000000..429d035
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_gpummu.c
@@ -0,0 +1,745 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/genalloc.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+
+#include "kgsl.h"
+#include "kgsl_mmu.h"
+#include "kgsl_device.h"
+#include "kgsl_sharedmem.h"
+#include "kgsl_trace.h"
+
+#define KGSL_PAGETABLE_SIZE \
+	ALIGN(KGSL_PAGETABLE_ENTRIES(CONFIG_MSM_KGSL_PAGE_TABLE_SIZE) * \
+	KGSL_PAGETABLE_ENTRY_SIZE, PAGE_SIZE)
+
+static ssize_t
+sysfs_show_ptpool_entries(struct kobject *kobj,
+			  struct kobj_attribute *attr,
+			  char *buf)
+{
+	struct kgsl_ptpool *pool = (struct kgsl_ptpool *)
+					kgsl_driver.ptpool;
+	return snprintf(buf, PAGE_SIZE, "%d\n", pool->entries);
+}
+
+static ssize_t
+sysfs_show_ptpool_min(struct kobject *kobj,
+			 struct kobj_attribute *attr,
+			 char *buf)
+{
+	struct kgsl_ptpool *pool = (struct kgsl_ptpool *)
+					kgsl_driver.ptpool;
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			pool->static_entries);
+}
+
+static ssize_t
+sysfs_show_ptpool_chunks(struct kobject *kobj,
+			 struct kobj_attribute *attr,
+			 char *buf)
+{
+	struct kgsl_ptpool *pool = (struct kgsl_ptpool *)
+					kgsl_driver.ptpool;
+	return snprintf(buf, PAGE_SIZE, "%d\n", pool->chunks);
+}
+
+static ssize_t
+sysfs_show_ptpool_ptsize(struct kobject *kobj,
+			 struct kobj_attribute *attr,
+			 char *buf)
+{
+	struct kgsl_ptpool *pool = (struct kgsl_ptpool *)
+					kgsl_driver.ptpool;
+	return snprintf(buf, PAGE_SIZE, "%d\n", pool->ptsize);
+}
+
+static struct kobj_attribute attr_ptpool_entries = {
+	.attr = { .name = "ptpool_entries", .mode = 0444 },
+	.show = sysfs_show_ptpool_entries,
+	.store = NULL,
+};
+
+static struct kobj_attribute attr_ptpool_min = {
+	.attr = { .name = "ptpool_min", .mode = 0444 },
+	.show = sysfs_show_ptpool_min,
+	.store = NULL,
+};
+
+static struct kobj_attribute attr_ptpool_chunks = {
+	.attr = { .name = "ptpool_chunks", .mode = 0444 },
+	.show = sysfs_show_ptpool_chunks,
+	.store = NULL,
+};
+
+static struct kobj_attribute attr_ptpool_ptsize = {
+	.attr = { .name = "ptpool_ptsize", .mode = 0444 },
+	.show = sysfs_show_ptpool_ptsize,
+	.store = NULL,
+};
+
+static struct attribute *ptpool_attrs[] = {
+	&attr_ptpool_entries.attr,
+	&attr_ptpool_min.attr,
+	&attr_ptpool_chunks.attr,
+	&attr_ptpool_ptsize.attr,
+	NULL,
+};
+
+static struct attribute_group ptpool_attr_group = {
+	.attrs = ptpool_attrs,
+};
+
+static int
+_kgsl_ptpool_add_entries(struct kgsl_ptpool *pool, int count, int dynamic)
+{
+	struct kgsl_ptpool_chunk *chunk;
+	size_t size = ALIGN(count * pool->ptsize, PAGE_SIZE);
+
+	BUG_ON(count == 0);
+
+	if (get_order(size) >= MAX_ORDER) {
+		KGSL_CORE_ERR("ptpool allocation is too big: %d\n", size);
+		return -EINVAL;
+	}
+
+	chunk = kzalloc(sizeof(*chunk), GFP_KERNEL);
+	if (chunk == NULL) {
+		KGSL_CORE_ERR("kzalloc(%d) failed\n", sizeof(*chunk));
+		return -ENOMEM;
+	}
+
+	chunk->size = size;
+	chunk->count = count;
+	chunk->dynamic = dynamic;
+
+	chunk->data = dma_alloc_coherent(NULL, size,
+					 &chunk->phys, GFP_KERNEL);
+
+	if (chunk->data == NULL) {
+		KGSL_CORE_ERR("dma_alloc_coherent(%d) failed\n", size);
+		goto err;
+	}
+
+	chunk->bitmap = kzalloc(BITS_TO_LONGS(count) * 4, GFP_KERNEL);
+
+	if (chunk->bitmap == NULL) {
+		KGSL_CORE_ERR("kzalloc(%d) failed\n",
+			BITS_TO_LONGS(count) * 4);
+		goto err_dma;
+	}
+
+	list_add_tail(&chunk->list, &pool->list);
+
+	pool->chunks++;
+	pool->entries += count;
+
+	if (!dynamic)
+		pool->static_entries += count;
+
+	return 0;
+
+err_dma:
+	dma_free_coherent(NULL, chunk->size, chunk->data, chunk->phys);
+err:
+	kfree(chunk);
+	return -ENOMEM;
+}
+
+static void *
+_kgsl_ptpool_get_entry(struct kgsl_ptpool *pool, unsigned int *physaddr)
+{
+	struct kgsl_ptpool_chunk *chunk;
+
+	list_for_each_entry(chunk, &pool->list, list) {
+		int bit = find_first_zero_bit(chunk->bitmap, chunk->count);
+
+		if (bit >= chunk->count)
+			continue;
+
+		set_bit(bit, chunk->bitmap);
+		*physaddr = chunk->phys + (bit * pool->ptsize);
+
+		return chunk->data + (bit * pool->ptsize);
+	}
+
+	return NULL;
+}
+
+/**
+ * kgsl_ptpool_add
+ * @pool:  A pointer to a ptpool structure
+ * @entries: Number of entries to add
+ *
+ * Add static entries to the pagetable pool.
+ */
+
+static int
+kgsl_ptpool_add(struct kgsl_ptpool *pool, int count)
+{
+	int ret = 0;
+	BUG_ON(count == 0);
+
+	mutex_lock(&pool->lock);
+
+	/* Only 4MB can be allocated in one chunk, so larger allocations
+	   need to be split into multiple sections */
+
+	while (count) {
+		int entries = ((count * pool->ptsize) > SZ_4M) ?
+			SZ_4M / pool->ptsize : count;
+
+		/* Add the entries as static, i.e. they don't ever stand
+		   a chance of being removed */
+
+		ret =  _kgsl_ptpool_add_entries(pool, entries, 0);
+		if (ret)
+			break;
+
+		count -= entries;
+	}
+
+	mutex_unlock(&pool->lock);
+	return ret;
+}
+
+/**
+ * kgsl_ptpool_alloc
+ * @pool:  A pointer to a ptpool structure
+ * @addr: A pointer to store the physical address of the chunk
+ *
+ * Allocate a pagetable from the pool.  Returns the virtual address
+ * of the pagetable, the physical address is returned in physaddr
+ */
+
+static void *kgsl_ptpool_alloc(struct kgsl_ptpool *pool,
+				unsigned int *physaddr)
+{
+	void *addr = NULL;
+	int ret;
+
+	mutex_lock(&pool->lock);
+	addr = _kgsl_ptpool_get_entry(pool, physaddr);
+	if (addr)
+		goto done;
+
+	/* Add a chunk for 1 more pagetable and mark it as dynamic */
+	ret = _kgsl_ptpool_add_entries(pool, 1, 1);
+
+	if (ret)
+		goto done;
+
+	addr = _kgsl_ptpool_get_entry(pool, physaddr);
+done:
+	mutex_unlock(&pool->lock);
+	return addr;
+}
+
+static inline void _kgsl_ptpool_rm_chunk(struct kgsl_ptpool_chunk *chunk)
+{
+	list_del(&chunk->list);
+
+	if (chunk->data)
+		dma_free_coherent(NULL, chunk->size, chunk->data,
+			chunk->phys);
+	kfree(chunk->bitmap);
+	kfree(chunk);
+}
+
+/**
+ * kgsl_ptpool_free
+ * @pool:  A pointer to a ptpool structure
+ * @addr: A pointer to the virtual address to free
+ *
+ * Free a pagetable allocated from the pool
+ */
+
+static void kgsl_ptpool_free(struct kgsl_ptpool *pool, void *addr)
+{
+	struct kgsl_ptpool_chunk *chunk, *tmp;
+
+	if (pool == NULL || addr == NULL)
+		return;
+
+	mutex_lock(&pool->lock);
+	list_for_each_entry_safe(chunk, tmp, &pool->list, list)  {
+		if (addr >=  chunk->data &&
+		    addr < chunk->data + chunk->size) {
+			int bit = ((unsigned long) (addr - chunk->data)) /
+				pool->ptsize;
+
+			clear_bit(bit, chunk->bitmap);
+			memset(addr, 0, pool->ptsize);
+
+			if (chunk->dynamic &&
+				bitmap_empty(chunk->bitmap, chunk->count))
+				_kgsl_ptpool_rm_chunk(chunk);
+
+			break;
+		}
+	}
+
+	mutex_unlock(&pool->lock);
+}
+
+void kgsl_gpummu_ptpool_destroy(void *ptpool)
+{
+	struct kgsl_ptpool *pool = (struct kgsl_ptpool *)ptpool;
+	struct kgsl_ptpool_chunk *chunk, *tmp;
+
+	if (pool == NULL)
+		return;
+
+	mutex_lock(&pool->lock);
+	list_for_each_entry_safe(chunk, tmp, &pool->list, list)
+		_kgsl_ptpool_rm_chunk(chunk);
+	mutex_unlock(&pool->lock);
+
+	kfree(pool);
+}
+
+/**
+ * kgsl_ptpool_init
+ * @pool:  A pointer to a ptpool structure to initialize
+ * @entries:  The number of inital entries to add to the pool
+ *
+ * Initalize a pool and allocate an initial chunk of entries.
+ */
+void *kgsl_gpummu_ptpool_init(int entries)
+{
+	int ptsize = KGSL_PAGETABLE_SIZE;
+	struct kgsl_ptpool *pool;
+	int ret = 0;
+
+	pool = kzalloc(sizeof(struct kgsl_ptpool), GFP_KERNEL);
+	if (!pool) {
+		KGSL_CORE_ERR("Failed to allocate memory "
+				"for ptpool\n");
+		return NULL;
+	}
+
+	pool->ptsize = ptsize;
+	mutex_init(&pool->lock);
+	INIT_LIST_HEAD(&pool->list);
+
+	if (entries) {
+		ret = kgsl_ptpool_add(pool, entries);
+		if (ret)
+			goto err_ptpool_remove;
+	}
+
+	ret = sysfs_create_group(kgsl_driver.ptkobj, &ptpool_attr_group);
+	if (ret) {
+		KGSL_CORE_ERR("sysfs_create_group failed for ptpool "
+				"statistics: %d\n", ret);
+		goto err_ptpool_remove;
+	}
+	return (void *)pool;
+
+err_ptpool_remove:
+	kgsl_gpummu_ptpool_destroy(pool);
+	return NULL;
+}
+
+int kgsl_gpummu_pt_equal(struct kgsl_pagetable *pt,
+					unsigned int pt_base)
+{
+	struct kgsl_gpummu_pt *gpummu_pt = pt ? pt->priv : NULL;
+	return gpummu_pt && pt_base && (gpummu_pt->base.gpuaddr == pt_base);
+}
+
+void kgsl_gpummu_destroy_pagetable(void *mmu_specific_pt)
+{
+	struct kgsl_gpummu_pt *gpummu_pt = (struct kgsl_gpummu_pt *)
+						mmu_specific_pt;
+	kgsl_ptpool_free((struct kgsl_ptpool *)kgsl_driver.ptpool,
+				gpummu_pt->base.hostptr);
+
+	kgsl_driver.stats.coherent -= KGSL_PAGETABLE_SIZE;
+
+	kfree(gpummu_pt->tlbflushfilter.base);
+
+	kfree(gpummu_pt);
+}
+
+static inline uint32_t
+kgsl_pt_entry_get(unsigned int va_base, uint32_t va)
+{
+	return (va - va_base) >> PAGE_SHIFT;
+}
+
+static inline void
+kgsl_pt_map_set(struct kgsl_gpummu_pt *pt, uint32_t pte, uint32_t val)
+{
+	uint32_t *baseptr = (uint32_t *)pt->base.hostptr;
+	BUG_ON(pte*sizeof(uint32_t) >= pt->base.size);
+	baseptr[pte] = val;
+}
+
+static inline uint32_t
+kgsl_pt_map_get(struct kgsl_gpummu_pt *pt, uint32_t pte)
+{
+	uint32_t *baseptr = (uint32_t *)pt->base.hostptr;
+	BUG_ON(pte*sizeof(uint32_t) >= pt->base.size);
+	return baseptr[pte] & GSL_PT_PAGE_ADDR_MASK;
+}
+
+static void kgsl_gpummu_pagefault(struct kgsl_mmu *mmu)
+{
+	unsigned int reg;
+	unsigned int ptbase;
+
+	kgsl_regread(mmu->device, MH_MMU_PAGE_FAULT, &reg);
+	kgsl_regread(mmu->device, MH_MMU_PT_BASE, &ptbase);
+
+	KGSL_MEM_CRIT(mmu->device,
+			"mmu page fault: page=0x%lx pt=%d op=%s axi=%d\n",
+			reg & ~(PAGE_SIZE - 1),
+			kgsl_mmu_get_ptname_from_ptbase(ptbase),
+			reg & 0x02 ? "WRITE" : "READ", (reg >> 4) & 0xF);
+	trace_kgsl_mmu_pagefault(mmu->device, reg & ~(PAGE_SIZE - 1),
+			kgsl_mmu_get_ptname_from_ptbase(ptbase),
+			reg & 0x02 ? "WRITE" : "READ");
+}
+
+static void *kgsl_gpummu_create_pagetable(void)
+{
+	struct kgsl_gpummu_pt *gpummu_pt;
+
+	gpummu_pt = kzalloc(sizeof(struct kgsl_gpummu_pt),
+				GFP_KERNEL);
+	if (!gpummu_pt)
+		return NULL;
+
+	gpummu_pt->last_superpte = 0;
+
+	gpummu_pt->tlbflushfilter.size = (CONFIG_MSM_KGSL_PAGE_TABLE_SIZE /
+				(PAGE_SIZE * GSL_PT_SUPER_PTE * 8)) + 1;
+	gpummu_pt->tlbflushfilter.base = (unsigned int *)
+			kzalloc(gpummu_pt->tlbflushfilter.size, GFP_KERNEL);
+	if (!gpummu_pt->tlbflushfilter.base) {
+		KGSL_CORE_ERR("kzalloc(%d) failed\n",
+			gpummu_pt->tlbflushfilter.size);
+		goto err_free_gpummu;
+	}
+	GSL_TLBFLUSH_FILTER_RESET();
+
+	gpummu_pt->base.hostptr = kgsl_ptpool_alloc((struct kgsl_ptpool *)
+						kgsl_driver.ptpool,
+						&gpummu_pt->base.physaddr);
+
+	if (gpummu_pt->base.hostptr == NULL)
+		goto err_flushfilter;
+
+	/* ptpool allocations are from coherent memory, so update the
+	   device statistics acordingly */
+
+	KGSL_STATS_ADD(KGSL_PAGETABLE_SIZE, kgsl_driver.stats.coherent,
+		       kgsl_driver.stats.coherent_max);
+
+	gpummu_pt->base.gpuaddr = gpummu_pt->base.physaddr;
+	gpummu_pt->base.size = KGSL_PAGETABLE_SIZE;
+
+	return (void *)gpummu_pt;
+
+err_flushfilter:
+	kfree(gpummu_pt->tlbflushfilter.base);
+err_free_gpummu:
+	kfree(gpummu_pt);
+
+	return NULL;
+}
+
+static void kgsl_gpummu_default_setstate(struct kgsl_mmu *mmu,
+					uint32_t flags)
+{
+	struct kgsl_gpummu_pt *gpummu_pt;
+	if (!kgsl_mmu_enabled())
+		return;
+
+	if (flags & KGSL_MMUFLAGS_PTUPDATE) {
+		kgsl_idle(mmu->device, KGSL_TIMEOUT_DEFAULT);
+		gpummu_pt = mmu->hwpagetable->priv;
+		kgsl_regwrite(mmu->device, MH_MMU_PT_BASE,
+			gpummu_pt->base.gpuaddr);
+	}
+
+	if (flags & KGSL_MMUFLAGS_TLBFLUSH) {
+		/* Invalidate all and tc */
+		kgsl_regwrite(mmu->device, MH_MMU_INVALIDATE,  0x00000003);
+	}
+}
+
+static void kgsl_gpummu_setstate(struct kgsl_mmu *mmu,
+				struct kgsl_pagetable *pagetable)
+{
+	if (mmu->flags & KGSL_FLAGS_STARTED) {
+		/* page table not current, then setup mmu to use new
+		 *  specified page table
+		 */
+		if (mmu->hwpagetable != pagetable) {
+			mmu->hwpagetable = pagetable;
+			/* Since we do a TLB flush the tlb_flags should
+			 * be cleared by calling kgsl_mmu_pt_get_flags
+			 */
+			kgsl_mmu_pt_get_flags(pagetable, mmu->device->id);
+
+			/* call device specific set page table */
+			kgsl_setstate(mmu, KGSL_MMUFLAGS_TLBFLUSH |
+				KGSL_MMUFLAGS_PTUPDATE);
+		}
+	}
+}
+
+static int kgsl_gpummu_init(struct kgsl_mmu *mmu)
+{
+	/*
+	 * intialize device mmu
+	 *
+	 * call this with the global lock held
+	 */
+	int status = 0;
+
+	/* sub-client MMU lookups require address translation */
+	if ((mmu->config & ~0x1) > 0) {
+		/*make sure virtual address range is a multiple of 64Kb */
+		if (CONFIG_MSM_KGSL_PAGE_TABLE_SIZE & ((1 << 16) - 1)) {
+			KGSL_CORE_ERR("Invalid pagetable size requested "
+			"for GPUMMU: %x\n", CONFIG_MSM_KGSL_PAGE_TABLE_SIZE);
+			return -EINVAL;
+		}
+	}
+
+	dev_info(mmu->device->dev, "|%s| MMU type set for device is GPUMMU\n",
+		__func__);
+	return status;
+}
+
+static int kgsl_gpummu_start(struct kgsl_mmu *mmu)
+{
+	/*
+	 * intialize device mmu
+	 *
+	 * call this with the global lock held
+	 */
+
+	struct kgsl_device *device = mmu->device;
+	struct kgsl_gpummu_pt *gpummu_pt;
+
+	if (mmu->flags & KGSL_FLAGS_STARTED)
+		return 0;
+
+	/* MMU not enabled */
+	if ((mmu->config & 0x1) == 0)
+		return 0;
+
+	/* setup MMU and sub-client behavior */
+	kgsl_regwrite(device, MH_MMU_CONFIG, mmu->config);
+
+	/* idle device */
+	kgsl_idle(device,  KGSL_TIMEOUT_DEFAULT);
+
+	/* enable axi interrupts */
+	kgsl_regwrite(device, MH_INTERRUPT_MASK,
+			GSL_MMU_INT_MASK | MH_INTERRUPT_MASK__MMU_PAGE_FAULT);
+
+	kgsl_sharedmem_set(&mmu->setstate_memory, 0, 0,
+			   mmu->setstate_memory.size);
+
+	/* TRAN_ERROR needs a 32 byte (32 byte aligned) chunk of memory
+	 * to complete transactions in case of an MMU fault. Note that
+	 * we'll leave the bottom 32 bytes of the setstate_memory for other
+	 * purposes (e.g. use it when dummy read cycles are needed
+	 * for other blocks) */
+	kgsl_regwrite(device, MH_MMU_TRAN_ERROR,
+		mmu->setstate_memory.physaddr + 32);
+
+	if (mmu->defaultpagetable == NULL)
+		mmu->defaultpagetable =
+			kgsl_mmu_getpagetable(KGSL_MMU_GLOBAL_PT);
+
+	/* Return error if the default pagetable doesn't exist */
+	if (mmu->defaultpagetable == NULL)
+		return -ENOMEM;
+
+	mmu->hwpagetable = mmu->defaultpagetable;
+	gpummu_pt = mmu->hwpagetable->priv;
+	kgsl_regwrite(mmu->device, MH_MMU_PT_BASE,
+		      gpummu_pt->base.gpuaddr);
+	kgsl_regwrite(mmu->device, MH_MMU_VA_RANGE,
+		      (KGSL_PAGETABLE_BASE |
+		      (CONFIG_MSM_KGSL_PAGE_TABLE_SIZE >> 16)));
+	kgsl_setstate(mmu, KGSL_MMUFLAGS_TLBFLUSH);
+	mmu->flags |= KGSL_FLAGS_STARTED;
+
+	return 0;
+}
+
+static int
+kgsl_gpummu_unmap(void *mmu_specific_pt,
+		struct kgsl_memdesc *memdesc)
+{
+	unsigned int numpages;
+	unsigned int pte, ptefirst, ptelast, superpte;
+	unsigned int range = kgsl_sg_size(memdesc->sg, memdesc->sglen);
+	struct kgsl_gpummu_pt *gpummu_pt = mmu_specific_pt;
+
+	/* All GPU addresses as assigned are page aligned, but some
+	   functions purturb the gpuaddr with an offset, so apply the
+	   mask here to make sure we have the right address */
+
+	unsigned int gpuaddr = memdesc->gpuaddr &  KGSL_MMU_ALIGN_MASK;
+
+	numpages = (range >> PAGE_SHIFT);
+	if (range & (PAGE_SIZE - 1))
+		numpages++;
+
+	ptefirst = kgsl_pt_entry_get(KGSL_PAGETABLE_BASE, gpuaddr);
+	ptelast = ptefirst + numpages;
+
+	superpte = ptefirst - (ptefirst & (GSL_PT_SUPER_PTE-1));
+	GSL_TLBFLUSH_FILTER_SETDIRTY(superpte / GSL_PT_SUPER_PTE);
+	for (pte = ptefirst; pte < ptelast; pte++) {
+#ifdef VERBOSE_DEBUG
+		/* check if PTE exists */
+		if (!kgsl_pt_map_get(gpummu_pt, pte))
+			KGSL_CORE_ERR("pt entry %x is already "
+			"unmapped for pagetable %p\n", pte, gpummu_pt);
+#endif
+		kgsl_pt_map_set(gpummu_pt, pte, GSL_PT_PAGE_DIRTY);
+		superpte = pte - (pte & (GSL_PT_SUPER_PTE - 1));
+		if (pte == superpte)
+			GSL_TLBFLUSH_FILTER_SETDIRTY(superpte /
+				GSL_PT_SUPER_PTE);
+	}
+
+	/* Post all writes to the pagetable */
+	wmb();
+
+	return 0;
+}
+
+#define SUPERPTE_IS_DIRTY(_p) \
+(((_p) & (GSL_PT_SUPER_PTE - 1)) == 0 && \
+GSL_TLBFLUSH_FILTER_ISDIRTY((_p) / GSL_PT_SUPER_PTE))
+
+static int
+kgsl_gpummu_map(void *mmu_specific_pt,
+		struct kgsl_memdesc *memdesc,
+		unsigned int protflags,
+		unsigned int *tlb_flags)
+{
+	unsigned int pte;
+	struct kgsl_gpummu_pt *gpummu_pt = mmu_specific_pt;
+	struct scatterlist *s;
+	int flushtlb = 0;
+	int i;
+
+	pte = kgsl_pt_entry_get(KGSL_PAGETABLE_BASE, memdesc->gpuaddr);
+
+	/* Flush the TLB if the first PTE isn't at the superpte boundary */
+	if (pte & (GSL_PT_SUPER_PTE - 1))
+		flushtlb = 1;
+
+	for_each_sg(memdesc->sg, s, memdesc->sglen, i) {
+		unsigned int paddr = kgsl_get_sg_pa(s);
+		unsigned int j;
+
+		/* Each sg entry might be multiple pages long */
+		for (j = paddr; j < paddr + s->length; pte++, j += PAGE_SIZE) {
+			if (SUPERPTE_IS_DIRTY(pte))
+				flushtlb = 1;
+			kgsl_pt_map_set(gpummu_pt, pte, j | protflags);
+		}
+	}
+
+	/* Flush the TLB if the last PTE isn't at the superpte boundary */
+	if ((pte + 1) & (GSL_PT_SUPER_PTE - 1))
+		flushtlb = 1;
+
+	wmb();
+
+	if (flushtlb) {
+		/*set all devices as needing flushing*/
+		*tlb_flags = UINT_MAX;
+		GSL_TLBFLUSH_FILTER_RESET();
+	}
+
+	return 0;
+}
+
+static void kgsl_gpummu_stop(struct kgsl_mmu *mmu)
+{
+	kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000000);
+	mmu->flags &= ~KGSL_FLAGS_STARTED;
+}
+
+static int kgsl_gpummu_close(struct kgsl_mmu *mmu)
+{
+	/*
+	 *  close device mmu
+	 *
+	 *  call this with the global lock held
+	 */
+	if (mmu->setstate_memory.gpuaddr)
+		kgsl_sharedmem_free(&mmu->setstate_memory);
+
+	if (mmu->defaultpagetable)
+		kgsl_mmu_putpagetable(mmu->defaultpagetable);
+
+	return 0;
+}
+
+static unsigned int
+kgsl_gpummu_get_current_ptbase(struct kgsl_mmu *mmu)
+{
+	unsigned int ptbase;
+	kgsl_regread(mmu->device, MH_MMU_PT_BASE, &ptbase);
+	return ptbase;
+}
+
+static unsigned int
+kgsl_gpummu_pt_get_base_addr(struct kgsl_pagetable *pt)
+{
+	struct kgsl_gpummu_pt *gpummu_pt = pt->priv;
+	return gpummu_pt->base.gpuaddr;
+}
+
+struct kgsl_mmu_ops gpummu_ops = {
+	.mmu_init = kgsl_gpummu_init,
+	.mmu_close = kgsl_gpummu_close,
+	.mmu_start = kgsl_gpummu_start,
+	.mmu_stop = kgsl_gpummu_stop,
+	.mmu_setstate = kgsl_gpummu_setstate,
+	.mmu_device_setstate = kgsl_gpummu_default_setstate,
+	.mmu_pagefault = kgsl_gpummu_pagefault,
+	.mmu_get_current_ptbase = kgsl_gpummu_get_current_ptbase,
+	.mmu_enable_clk = NULL,
+	.mmu_disable_clk = NULL,
+	.mmu_get_hwpagetable_asid = NULL,
+	.mmu_get_pt_lsb = NULL,
+	.mmu_get_reg_map_desc = NULL,
+};
+
+struct kgsl_mmu_pt_ops gpummu_pt_ops = {
+	.mmu_map = kgsl_gpummu_map,
+	.mmu_unmap = kgsl_gpummu_unmap,
+	.mmu_create_pagetable = kgsl_gpummu_create_pagetable,
+	.mmu_destroy_pagetable = kgsl_gpummu_destroy_pagetable,
+	.mmu_pt_equal = kgsl_gpummu_pt_equal,
+	.mmu_pt_get_base_addr = kgsl_gpummu_pt_get_base_addr,
+};
diff --git a/drivers/gpu/msm/kgsl_gpummu.h b/drivers/gpu/msm/kgsl_gpummu.h
new file mode 100644
index 0000000..caa5df1
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_gpummu.h
@@ -0,0 +1,78 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __KGSL_GPUMMU_H
+#define __KGSL_GPUMMU_H
+
+#define GSL_PT_PAGE_BITS_MASK	0x00000007
+#define GSL_PT_PAGE_ADDR_MASK	PAGE_MASK
+
+#define GSL_MMU_INT_MASK \
+	(MH_INTERRUPT_MASK__AXI_READ_ERROR | \
+	 MH_INTERRUPT_MASK__AXI_WRITE_ERROR)
+
+/* Macros to manage TLB flushing */
+#define GSL_TLBFLUSH_FILTER_ENTRY_NUMBITS     (sizeof(unsigned char) * 8)
+#define GSL_TLBFLUSH_FILTER_GET(superpte)			     \
+	      (*((unsigned char *)				    \
+	      (((unsigned int)gpummu_pt->tlbflushfilter.base)    \
+	      + (superpte / GSL_TLBFLUSH_FILTER_ENTRY_NUMBITS))))
+#define GSL_TLBFLUSH_FILTER_SETDIRTY(superpte)				\
+	      (GSL_TLBFLUSH_FILTER_GET((superpte)) |= 1 <<	    \
+	      (superpte % GSL_TLBFLUSH_FILTER_ENTRY_NUMBITS))
+#define GSL_TLBFLUSH_FILTER_ISDIRTY(superpte)			 \
+	      (GSL_TLBFLUSH_FILTER_GET((superpte)) &		  \
+	      (1 << (superpte % GSL_TLBFLUSH_FILTER_ENTRY_NUMBITS)))
+#define GSL_TLBFLUSH_FILTER_RESET() memset(gpummu_pt->tlbflushfilter.base,\
+				      0, gpummu_pt->tlbflushfilter.size)
+
+extern struct kgsl_mmu_ops gpummu_ops;
+extern struct kgsl_mmu_pt_ops gpummu_pt_ops;
+
+struct kgsl_tlbflushfilter {
+	unsigned int *base;
+	unsigned int size;
+};
+
+struct kgsl_gpummu_pt {
+	struct kgsl_memdesc  base;
+	unsigned int   last_superpte;
+	/* Maintain filter to manage tlb flushing */
+	struct kgsl_tlbflushfilter tlbflushfilter;
+};
+
+struct kgsl_ptpool_chunk {
+	size_t size;
+	unsigned int count;
+	int dynamic;
+
+	void *data;
+	unsigned int phys;
+
+	unsigned long *bitmap;
+	struct list_head list;
+};
+
+struct kgsl_ptpool {
+	size_t ptsize;
+	struct mutex lock;
+	struct list_head list;
+	int entries;
+	int static_entries;
+	int chunks;
+};
+
+void *kgsl_gpummu_ptpool_init(int entries);
+void kgsl_gpummu_ptpool_destroy(void *ptpool);
+
+#endif /* __KGSL_GPUMMU_H */
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
new file mode 100644
index 0000000..febb265
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -0,0 +1,1020 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/genalloc.h>
+#include <linux/slab.h>
+#include <linux/iommu.h>
+#include <linux/msm_kgsl.h>
+
+#include "kgsl.h"
+#include "kgsl_device.h"
+#include "kgsl_mmu.h"
+#include "kgsl_sharedmem.h"
+#include "kgsl_iommu.h"
+#include "adreno_pm4types.h"
+#include "adreno.h"
+#include "kgsl_trace.h"
+
+static struct kgsl_iommu_unit *get_iommu_unit(struct device *dev)
+{
+	int i, j, k;
+
+	for (i = 0; i < KGSL_DEVICE_MAX; i++) {
+		struct kgsl_mmu *mmu;
+		struct kgsl_iommu *iommu;
+
+		if (kgsl_driver.devp[i] == NULL)
+			continue;
+
+		mmu = kgsl_get_mmu(kgsl_driver.devp[i]);
+		if (mmu == NULL || mmu->priv == NULL)
+			continue;
+
+		iommu = mmu->priv;
+
+		for (j = 0; j < iommu->unit_count; j++) {
+			struct kgsl_iommu_unit *iommu_unit =
+				&iommu->iommu_units[j];
+			for (k = 0; k < iommu_unit->dev_count; k++) {
+				if (iommu_unit->dev[k].dev == dev)
+					return iommu_unit;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+static struct kgsl_iommu_device *get_iommu_device(struct kgsl_iommu_unit *unit,
+		struct device *dev)
+{
+	int k;
+
+	for (k = 0; unit && k < unit->dev_count; k++) {
+		if (unit->dev[k].dev == dev)
+			return &(unit->dev[k]);
+	}
+
+	return NULL;
+}
+
+static int kgsl_iommu_fault_handler(struct iommu_domain *domain,
+	struct device *dev, unsigned long addr, int flags)
+{
+	struct kgsl_iommu_unit *iommu_unit = get_iommu_unit(dev);
+	struct kgsl_iommu_device *iommu_dev = get_iommu_device(iommu_unit, dev);
+	unsigned int ptbase, fsr;
+
+	if (!iommu_dev) {
+		KGSL_CORE_ERR("Invalid IOMMU device %p\n", dev);
+		return -ENOSYS;
+	}
+
+	ptbase = iommu_get_pt_base_addr(domain);
+
+	fsr = KGSL_IOMMU_GET_IOMMU_REG(iommu_unit->reg_map.hostptr,
+		iommu_dev->ctx_id, FSR);
+
+	KGSL_MEM_CRIT(iommu_dev->kgsldev,
+		"GPU PAGE FAULT: addr = %lX pid = %d\n",
+		addr, kgsl_mmu_get_ptname_from_ptbase(ptbase));
+	KGSL_MEM_CRIT(iommu_dev->kgsldev, "context = %d FSR = %X\n",
+		iommu_dev->ctx_id, fsr);
+
+	trace_kgsl_mmu_pagefault(iommu_dev->kgsldev, addr,
+			kgsl_mmu_get_ptname_from_ptbase(ptbase), 0);
+
+	return 0;
+}
+
+/*
+ * kgsl_iommu_disable_clk - Disable iommu clocks
+ * @mmu - Pointer to mmu structure
+ *
+ * Disables iommu clocks
+ * Return - void
+ */
+static void kgsl_iommu_disable_clk(struct kgsl_mmu *mmu)
+{
+	struct kgsl_iommu *iommu = mmu->priv;
+	struct msm_iommu_drvdata *iommu_drvdata;
+	int i, j;
+
+	for (i = 0; i < iommu->unit_count; i++) {
+		struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
+		for (j = 0; j < iommu_unit->dev_count; j++) {
+			if (!iommu_unit->dev[j].clk_enabled)
+				continue;
+			iommu_drvdata = dev_get_drvdata(
+					iommu_unit->dev[j].dev->parent);
+			if (iommu_drvdata->clk)
+				clk_disable_unprepare(iommu_drvdata->clk);
+			clk_disable_unprepare(iommu_drvdata->pclk);
+			iommu_unit->dev[j].clk_enabled = false;
+		}
+	}
+}
+
+/*
+ * kgsl_iommu_enable_clk - Enable iommu clocks
+ * @mmu - Pointer to mmu structure
+ * @ctx_id - The context bank whose clocks are to be turned on
+ *
+ * Enables iommu clocks of a given context
+ * Return: 0 on success else error code
+ */
+static int kgsl_iommu_enable_clk(struct kgsl_mmu *mmu,
+				int ctx_id)
+{
+	int ret = 0;
+	int i, j;
+	struct kgsl_iommu *iommu = mmu->priv;
+	struct msm_iommu_drvdata *iommu_drvdata;
+
+	for (i = 0; i < iommu->unit_count; i++) {
+		struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
+		for (j = 0; j < iommu_unit->dev_count; j++) {
+			if (iommu_unit->dev[j].clk_enabled ||
+				ctx_id != iommu_unit->dev[j].ctx_id)
+				continue;
+			iommu_drvdata =
+			dev_get_drvdata(iommu_unit->dev[j].dev->parent);
+			ret = clk_prepare_enable(iommu_drvdata->pclk);
+			if (ret)
+				goto done;
+			if (iommu_drvdata->clk) {
+				ret = clk_prepare_enable(iommu_drvdata->clk);
+				if (ret) {
+					clk_disable_unprepare(
+						iommu_drvdata->pclk);
+					goto done;
+				}
+			}
+			iommu_unit->dev[j].clk_enabled = true;
+		}
+	}
+done:
+	if (ret)
+		kgsl_iommu_disable_clk(mmu);
+	return ret;
+}
+
+/*
+ * kgsl_iommu_pt_equal - Check if pagetables are equal
+ * @pt - Pointer to pagetable
+ * @pt_base - Address of a pagetable that the IOMMU register is
+ * programmed with
+ *
+ * Checks whether the pt_base is equal to the base address of
+ * the pagetable which is contained in the pt structure
+ * Return - Non-zero if the pagetable addresses are equal else 0
+ */
+static int kgsl_iommu_pt_equal(struct kgsl_pagetable *pt,
+					unsigned int pt_base)
+{
+	struct kgsl_iommu_pt *iommu_pt = pt ? pt->priv : NULL;
+	unsigned int domain_ptbase = iommu_pt ?
+				iommu_get_pt_base_addr(iommu_pt->domain) : 0;
+	/* Only compare the valid address bits of the pt_base */
+	domain_ptbase &= (KGSL_IOMMU_TTBR0_PA_MASK <<
+				KGSL_IOMMU_TTBR0_PA_SHIFT);
+	pt_base &= (KGSL_IOMMU_TTBR0_PA_MASK <<
+				KGSL_IOMMU_TTBR0_PA_SHIFT);
+	return domain_ptbase && pt_base &&
+		(domain_ptbase == pt_base);
+}
+
+/*
+ * kgsl_iommu_destroy_pagetable - Free up reaources help by a pagetable
+ * @mmu_specific_pt - Pointer to pagetable which is to be freed
+ *
+ * Return - void
+ */
+static void kgsl_iommu_destroy_pagetable(void *mmu_specific_pt)
+{
+	struct kgsl_iommu_pt *iommu_pt = mmu_specific_pt;
+	if (iommu_pt->domain)
+		iommu_domain_free(iommu_pt->domain);
+	if (iommu_pt->iommu) {
+		if ((KGSL_IOMMU_ASID_REUSE == iommu_pt->asid) &&
+			iommu_pt->iommu->asid_reuse)
+			iommu_pt->iommu->asid_reuse--;
+		if (!iommu_pt->iommu->asid_reuse ||
+			(KGSL_IOMMU_ASID_REUSE != iommu_pt->asid))
+			clear_bit(iommu_pt->asid, iommu_pt->iommu->asids);
+	}
+	kfree(iommu_pt);
+}
+
+/*
+ * kgsl_iommu_create_pagetable - Create a IOMMU pagetable
+ *
+ * Allocate memory to hold a pagetable and allocate the IOMMU
+ * domain which is the actual IOMMU pagetable
+ * Return - void
+ */
+void *kgsl_iommu_create_pagetable(void)
+{
+	struct kgsl_iommu_pt *iommu_pt;
+
+	iommu_pt = kzalloc(sizeof(struct kgsl_iommu_pt), GFP_KERNEL);
+	if (!iommu_pt) {
+		KGSL_CORE_ERR("kzalloc(%d) failed\n",
+				sizeof(struct kgsl_iommu_pt));
+		return NULL;
+	}
+	iommu_pt->domain = iommu_domain_alloc(&platform_bus_type,
+										  MSM_IOMMU_DOMAIN_PT_CACHEABLE);
+	if (!iommu_pt->domain) {
+		KGSL_CORE_ERR("Failed to create iommu domain\n");
+		kfree(iommu_pt);
+		return NULL;
+	} else {
+		iommu_set_fault_handler(iommu_pt->domain,
+			kgsl_iommu_fault_handler);
+	}
+
+	return iommu_pt;
+}
+
+/*
+ * kgsl_detach_pagetable_iommu_domain - Detach the IOMMU unit from a
+ * pagetable
+ * @mmu - Pointer to the device mmu structure
+ * @priv - Flag indicating whether the private or user context is to be
+ * detached
+ *
+ * Detach the IOMMU unit with the domain that is contained in the
+ * hwpagetable of the given mmu. After detaching the IOMMU unit is not
+ * in use because the PTBR will not be set after a detach
+ * Return - void
+ */
+static void kgsl_detach_pagetable_iommu_domain(struct kgsl_mmu *mmu)
+{
+	struct kgsl_iommu_pt *iommu_pt;
+	struct kgsl_iommu *iommu = mmu->priv;
+	int i, j;
+
+	BUG_ON(mmu->hwpagetable == NULL);
+	BUG_ON(mmu->hwpagetable->priv == NULL);
+
+	iommu_pt = mmu->hwpagetable->priv;
+
+	for (i = 0; i < iommu->unit_count; i++) {
+		struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
+		for (j = 0; j < iommu_unit->dev_count; j++) {
+			if (iommu_unit->dev[j].attached) {
+				iommu_detach_device(iommu_pt->domain,
+						iommu_unit->dev[j].dev);
+				iommu_unit->dev[j].attached = false;
+				KGSL_MEM_INFO(mmu->device, "iommu %p detached "
+					"from user dev of MMU: %p\n",
+					iommu_pt->domain, mmu);
+			}
+		}
+	}
+}
+
+/*
+ * kgsl_attach_pagetable_iommu_domain - Attach the IOMMU unit to a
+ * pagetable, i.e set the IOMMU's PTBR to the pagetable address and
+ * setup other IOMMU registers for the device so that it becomes
+ * active
+ * @mmu - Pointer to the device mmu structure
+ * @priv - Flag indicating whether the private or user context is to be
+ * attached
+ *
+ * Attach the IOMMU unit with the domain that is contained in the
+ * hwpagetable of the given mmu.
+ * Return - 0 on success else error code
+ */
+static int kgsl_attach_pagetable_iommu_domain(struct kgsl_mmu *mmu)
+{
+	struct kgsl_iommu_pt *iommu_pt;
+	struct kgsl_iommu *iommu = mmu->priv;
+	int i, j, ret = 0;
+
+	BUG_ON(mmu->hwpagetable == NULL);
+	BUG_ON(mmu->hwpagetable->priv == NULL);
+
+	iommu_pt = mmu->hwpagetable->priv;
+
+	/*
+	 * Loop through all the iommu devcies under all iommu units and
+	 * attach the domain
+	 */
+	for (i = 0; i < iommu->unit_count; i++) {
+		struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
+		for (j = 0; j < iommu_unit->dev_count; j++) {
+			if (!iommu_unit->dev[j].attached) {
+				ret = iommu_attach_device(iommu_pt->domain,
+							iommu_unit->dev[j].dev);
+				if (ret) {
+					KGSL_MEM_ERR(mmu->device,
+						"Failed to attach device, err %d\n",
+						ret);
+					goto done;
+				}
+				iommu_unit->dev[j].attached = true;
+				KGSL_MEM_INFO(mmu->device,
+				"iommu pt %p attached to dev %p, ctx_id %d\n",
+				iommu_pt->domain, iommu_unit->dev[j].dev,
+				iommu_unit->dev[j].ctx_id);
+			}
+		}
+	}
+done:
+	return ret;
+}
+
+/*
+ * _get_iommu_ctxs - Get device pointer to IOMMU contexts
+ * @mmu - Pointer to mmu device
+ * data - Pointer to the platform data containing information about
+ * iommu devices for one iommu unit
+ * unit_id - The IOMMU unit number. This is not a specific ID but just
+ * a serial number. The serial numbers are treated as ID's of the
+ * IOMMU units
+ *
+ * Return - 0 on success else error code
+ */
+static int _get_iommu_ctxs(struct kgsl_mmu *mmu,
+	struct kgsl_device_iommu_data *data, unsigned int unit_id)
+{
+	struct kgsl_iommu *iommu = mmu->priv;
+	struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[unit_id];
+	int i;
+
+	if (data->iommu_ctx_count > KGSL_IOMMU_MAX_DEVS_PER_UNIT) {
+		KGSL_CORE_ERR("Too many iommu devices defined for an "
+				"IOMMU unit\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < data->iommu_ctx_count; i++) {
+		if (!data->iommu_ctxs[i].iommu_ctx_name)
+			continue;
+
+		iommu_unit->dev[iommu_unit->dev_count].dev =
+			msm_iommu_get_ctx(data->iommu_ctxs[i].iommu_ctx_name);
+		if (iommu_unit->dev[iommu_unit->dev_count].dev == NULL) {
+			KGSL_CORE_ERR("Failed to get iommu dev handle for "
+			"device %s\n", data->iommu_ctxs[i].iommu_ctx_name);
+			return -EINVAL;
+		}
+		if (KGSL_IOMMU_CONTEXT_USER != data->iommu_ctxs[i].ctx_id &&
+			KGSL_IOMMU_CONTEXT_PRIV != data->iommu_ctxs[i].ctx_id) {
+			KGSL_CORE_ERR("Invalid context ID defined: %d\n",
+					data->iommu_ctxs[i].ctx_id);
+			return -EINVAL;
+		}
+		iommu_unit->dev[iommu_unit->dev_count].ctx_id =
+						data->iommu_ctxs[i].ctx_id;
+		iommu_unit->dev[iommu_unit->dev_count].kgsldev = mmu->device;
+
+		KGSL_DRV_INFO(mmu->device,
+				"Obtained dev handle %p for iommu context %s\n",
+				iommu_unit->dev[iommu_unit->dev_count].dev,
+				data->iommu_ctxs[i].iommu_ctx_name);
+
+		iommu_unit->dev_count++;
+	}
+
+	return 0;
+}
+
+/*
+ * kgsl_get_iommu_ctxt - Get device pointer to IOMMU contexts
+ * @mmu - Pointer to mmu device
+ *
+ * Get the device pointers for the IOMMU user and priv contexts of the
+ * kgsl device
+ * Return - 0 on success else error code
+ */
+static int kgsl_get_iommu_ctxt(struct kgsl_mmu *mmu)
+{
+	struct platform_device *pdev =
+		container_of(mmu->device->parentdev, struct platform_device,
+				dev);
+	struct kgsl_device_platform_data *pdata_dev = pdev->dev.platform_data;
+	struct kgsl_iommu *iommu = mmu->device->mmu.priv;
+	int i, ret = 0;
+
+	/* Go through the IOMMU data and get all the context devices */
+	if (KGSL_IOMMU_MAX_UNITS < pdata_dev->iommu_count) {
+		KGSL_CORE_ERR("Too many IOMMU units defined\n");
+		ret = -EINVAL;
+		goto  done;
+	}
+
+	for (i = 0; i < pdata_dev->iommu_count; i++) {
+		ret = _get_iommu_ctxs(mmu, &pdata_dev->iommu_data[i], i);
+		if (ret)
+			break;
+	}
+	iommu->unit_count = pdata_dev->iommu_count;
+done:
+	return ret;
+}
+
+/*
+ * kgsl_set_register_map - Map the IOMMU regsiters in the memory descriptors
+ * of the respective iommu units
+ * @mmu - Pointer to mmu structure
+ *
+ * Return - 0 on success else error code
+ */
+static int kgsl_set_register_map(struct kgsl_mmu *mmu)
+{
+	struct platform_device *pdev =
+		container_of(mmu->device->parentdev, struct platform_device,
+				dev);
+	struct kgsl_device_platform_data *pdata_dev = pdev->dev.platform_data;
+	struct kgsl_iommu *iommu = mmu->device->mmu.priv;
+	struct kgsl_iommu_unit *iommu_unit;
+	int i = 0, ret = 0;
+
+	for (; i < pdata_dev->iommu_count; i++) {
+		struct kgsl_device_iommu_data data = pdata_dev->iommu_data[i];
+		iommu_unit = &iommu->iommu_units[i];
+		/* set up the IOMMU register map for the given IOMMU unit */
+		if (!data.physstart || !data.physend) {
+			KGSL_CORE_ERR("The register range for IOMMU unit not"
+					" specified\n");
+			ret = -EINVAL;
+			goto err;
+		}
+		iommu_unit->reg_map.hostptr = ioremap(data.physstart,
+					data.physend - data.physstart + 1);
+		if (!iommu_unit->reg_map.hostptr) {
+			KGSL_CORE_ERR("Failed to map SMMU register address "
+				"space from %x to %x\n", data.physstart,
+				data.physend - data.physstart + 1);
+			ret = -ENOMEM;
+			i--;
+			goto err;
+		}
+		iommu_unit->reg_map.size = data.physend - data.physstart + 1;
+		iommu_unit->reg_map.physaddr = data.physstart;
+		memdesc_sg_phys(&iommu_unit->reg_map, data.physstart,
+				iommu_unit->reg_map.size);
+	}
+	iommu->unit_count = pdata_dev->iommu_count;
+	return ret;
+err:
+	/* Unmap any mapped IOMMU regions */
+	for (; i >= 0; i--) {
+		iommu_unit = &iommu->iommu_units[i];
+		iounmap(iommu_unit->reg_map.hostptr);
+		iommu_unit->reg_map.size = 0;
+		iommu_unit->reg_map.physaddr = 0;
+	}
+	return ret;
+}
+
+/*
+ * kgsl_iommu_pt_get_base_addr - Get the address of the pagetable that the
+ * IOMMU ttbr0 register is programmed with
+ * @pt - kgsl pagetable pointer that contains the IOMMU domain pointer
+ *
+ * Return - actual pagetable address that the ttbr0 register is programmed
+ * with
+ */
+static unsigned int kgsl_iommu_pt_get_base_addr(struct kgsl_pagetable *pt)
+{
+	struct kgsl_iommu_pt *iommu_pt = pt->priv;
+	return iommu_get_pt_base_addr(iommu_pt->domain);
+}
+
+/*
+ * kgsl_iommu_get_pt_lsb - Return the lsb of the ttbr0 IOMMU register
+ * @mmu - Pointer to mmu structure
+ * @hostptr - Pointer to the IOMMU register map. This is used to match
+ * the iommu device whose lsb value is to be returned
+ * @ctx_id - The context bank whose lsb valus is to be returned
+ * Return - returns the lsb which is the last 14 bits of the ttbr0 IOMMU
+ * register. ttbr0 is the actual PTBR for of the IOMMU. The last 14 bits
+ * are only programmed once in the beginning when a domain is attached
+ * does not change.
+ */
+static int kgsl_iommu_get_pt_lsb(struct kgsl_mmu *mmu,
+				unsigned int unit_id,
+				enum kgsl_iommu_context_id ctx_id)
+{
+	struct kgsl_iommu *iommu = mmu->priv;
+	int i, j;
+	for (i = 0; i < iommu->unit_count; i++) {
+		struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
+		for (j = 0; j < iommu_unit->dev_count; j++)
+			if (unit_id == i &&
+				ctx_id == iommu_unit->dev[j].ctx_id)
+				return iommu_unit->dev[j].pt_lsb;
+	}
+	return 0;
+}
+
+static void kgsl_iommu_setstate(struct kgsl_mmu *mmu,
+				struct kgsl_pagetable *pagetable)
+{
+	if (mmu->flags & KGSL_FLAGS_STARTED) {
+		struct kgsl_iommu *iommu = mmu->priv;
+		struct kgsl_iommu_pt *iommu_pt = pagetable->priv;
+		/* page table not current, then setup mmu to use new
+		 *  specified page table
+		 */
+		if (mmu->hwpagetable != pagetable) {
+			unsigned int flags = 0;
+			mmu->hwpagetable = pagetable;
+			/* force tlb flush if asid is reused */
+			if (iommu->asid_reuse &&
+				(KGSL_IOMMU_ASID_REUSE == iommu_pt->asid))
+				flags |= KGSL_MMUFLAGS_TLBFLUSH;
+			flags |= kgsl_mmu_pt_get_flags(mmu->hwpagetable,
+							mmu->device->id);
+			kgsl_setstate(mmu, KGSL_MMUFLAGS_PTUPDATE | flags);
+		}
+	}
+}
+
+static int kgsl_iommu_init(struct kgsl_mmu *mmu)
+{
+	/*
+	 * intialize device mmu
+	 *
+	 * call this with the global lock held
+	 */
+	int status = 0;
+	struct kgsl_iommu *iommu;
+
+	iommu = kzalloc(sizeof(struct kgsl_iommu), GFP_KERNEL);
+	if (!iommu) {
+		KGSL_CORE_ERR("kzalloc(%d) failed\n",
+				sizeof(struct kgsl_iommu));
+		return -ENOMEM;
+	}
+	iommu->asids = kzalloc(BITS_TO_LONGS(KGSL_IOMMU_MAX_ASIDS) *
+				sizeof(unsigned long), GFP_KERNEL);
+	if (!iommu->asids) {
+		KGSL_CORE_ERR("kzalloc(%d) failed\n",
+				sizeof(struct kgsl_iommu));
+		status = -ENOMEM;
+		goto done;
+	}
+
+	mmu->priv = iommu;
+	status = kgsl_get_iommu_ctxt(mmu);
+	if (status)
+		goto done;
+	status = kgsl_set_register_map(mmu);
+	if (status)
+		goto done;
+
+	/* A nop is required in an indirect buffer when switching
+	 * pagetables in-stream */
+	kgsl_sharedmem_writel(&mmu->setstate_memory,
+				KGSL_IOMMU_SETSTATE_NOP_OFFSET,
+				cp_nop_packet(1));
+
+	dev_info(mmu->device->dev, "|%s| MMU type set for device is IOMMU\n",
+			__func__);
+done:
+	if (status) {
+		kfree(iommu->asids);
+		kfree(iommu);
+		mmu->priv = NULL;
+	}
+	return status;
+}
+
+/*
+ * kgsl_iommu_setup_defaultpagetable - Setup the initial defualtpagetable
+ * for iommu. This function is only called once during first start, successive
+ * start do not call this funciton.
+ * @mmu - Pointer to mmu structure
+ *
+ * Create the  initial defaultpagetable and setup the iommu mappings to it
+ * Return - 0 on success else error code
+ */
+static int kgsl_iommu_setup_defaultpagetable(struct kgsl_mmu *mmu)
+{
+	int status = 0;
+	int i = 0;
+	struct kgsl_iommu *iommu = mmu->priv;
+	struct kgsl_iommu_pt *iommu_pt;
+
+	mmu->defaultpagetable = kgsl_mmu_getpagetable(KGSL_MMU_GLOBAL_PT);
+	/* Return error if the default pagetable doesn't exist */
+	if (mmu->defaultpagetable == NULL) {
+		status = -ENOMEM;
+		goto err;
+	}
+	/* Map the IOMMU regsiters to only defaultpagetable */
+	for (i = 0; i < iommu->unit_count; i++) {
+		iommu->iommu_units[i].reg_map.priv |= KGSL_MEMFLAGS_GLOBAL;
+		status = kgsl_mmu_map(mmu->defaultpagetable,
+			&(iommu->iommu_units[i].reg_map),
+			GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
+		if (status) {
+			iommu->iommu_units[i].reg_map.priv &=
+							~KGSL_MEMFLAGS_GLOBAL;
+			goto err;
+		}
+	}
+	/*
+	 * The dafault pagetable always has asid 0 assigned by the iommu driver
+	 * and asid 1 is assigned to the private context.
+	 */
+	iommu_pt = mmu->defaultpagetable->priv;
+	iommu_pt->asid = 0;
+	set_bit(0, iommu->asids);
+	set_bit(1, iommu->asids);
+	return status;
+err:
+	for (i--; i >= 0; i--) {
+		kgsl_mmu_unmap(mmu->defaultpagetable,
+				&(iommu->iommu_units[i].reg_map));
+		iommu->iommu_units[i].reg_map.priv &= ~KGSL_MEMFLAGS_GLOBAL;
+	}
+	if (mmu->defaultpagetable) {
+		kgsl_mmu_putpagetable(mmu->defaultpagetable);
+		mmu->defaultpagetable = NULL;
+	}
+	return status;
+}
+
+static int kgsl_iommu_start(struct kgsl_mmu *mmu)
+{
+	int status;
+	struct kgsl_iommu *iommu = mmu->priv;
+	int i, j;
+
+	if (mmu->flags & KGSL_FLAGS_STARTED)
+		return 0;
+
+	if (mmu->defaultpagetable == NULL) {
+		status = kgsl_iommu_setup_defaultpagetable(mmu);
+		if (status)
+			return -ENOMEM;
+	}
+	/* We use the GPU MMU to control access to IOMMU registers on a225,
+	 * hence we still keep the MMU active on a225 */
+	if (adreno_is_a225(ADRENO_DEVICE(mmu->device))) {
+		struct kgsl_mh *mh = &(mmu->device->mh);
+		kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000001);
+		kgsl_regwrite(mmu->device, MH_MMU_MPU_END,
+			mh->mpu_base +
+			iommu->iommu_units
+				[iommu->unit_count - 1].reg_map.gpuaddr -
+				PAGE_SIZE);
+	} else {
+		kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000000);
+	}
+
+	mmu->hwpagetable = mmu->defaultpagetable;
+
+	status = kgsl_attach_pagetable_iommu_domain(mmu);
+	if (status) {
+		mmu->hwpagetable = NULL;
+		goto done;
+	}
+	status = kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_USER);
+	if (status) {
+		KGSL_CORE_ERR("clk enable failed\n");
+		goto done;
+	}
+	status = kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_PRIV);
+	if (status) {
+		KGSL_CORE_ERR("clk enable failed\n");
+		goto done;
+	}
+	/* Get the lsb value of pagetables set in the IOMMU ttbr0 register as
+	 * that value should not change when we change pagetables, so while
+	 * changing pagetables we can use this lsb value of the pagetable w/o
+	 * having to read it again
+	 */
+	for (i = 0; i < iommu->unit_count; i++) {
+		struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
+		for (j = 0; j < iommu_unit->dev_count; j++)
+			iommu_unit->dev[j].pt_lsb = KGSL_IOMMMU_PT_LSB(
+						KGSL_IOMMU_GET_IOMMU_REG(
+						iommu_unit->reg_map.hostptr,
+						iommu_unit->dev[j].ctx_id,
+						TTBR0));
+	}
+	iommu->asid = KGSL_IOMMU_GET_IOMMU_REG(
+				iommu->iommu_units[0].reg_map.hostptr,
+				KGSL_IOMMU_CONTEXT_USER,
+				CONTEXTIDR);
+
+	kgsl_iommu_disable_clk(mmu);
+	mmu->flags |= KGSL_FLAGS_STARTED;
+
+done:
+	if (status) {
+		kgsl_iommu_disable_clk(mmu);
+		kgsl_detach_pagetable_iommu_domain(mmu);
+	}
+	return status;
+}
+
+static int
+kgsl_iommu_unmap(void *mmu_specific_pt,
+		struct kgsl_memdesc *memdesc)
+{
+	int ret;
+	unsigned int range = kgsl_sg_size(memdesc->sg, memdesc->sglen);
+	struct kgsl_iommu_pt *iommu_pt = mmu_specific_pt;
+
+	/* All GPU addresses as assigned are page aligned, but some
+	   functions purturb the gpuaddr with an offset, so apply the
+	   mask here to make sure we have the right address */
+
+	unsigned int gpuaddr = memdesc->gpuaddr &  KGSL_MMU_ALIGN_MASK;
+
+	if (range == 0 || gpuaddr == 0)
+		return 0;
+
+	ret = iommu_unmap_range(iommu_pt->domain, gpuaddr, range);
+	if (ret)
+		KGSL_CORE_ERR("iommu_unmap_range(%p, %x, %d) failed "
+			"with err: %d\n", iommu_pt->domain, gpuaddr,
+			range, ret);
+
+	return 0;
+}
+
+static int
+kgsl_iommu_map(void *mmu_specific_pt,
+			struct kgsl_memdesc *memdesc,
+			unsigned int protflags,
+			unsigned int *tlb_flags)
+{
+	int ret;
+	unsigned int iommu_virt_addr;
+	struct kgsl_iommu_pt *iommu_pt = mmu_specific_pt;
+	int size = kgsl_sg_size(memdesc->sg, memdesc->sglen);
+
+	BUG_ON(NULL == iommu_pt);
+
+
+	iommu_virt_addr = memdesc->gpuaddr;
+
+	ret = iommu_map_range(iommu_pt->domain, iommu_virt_addr, memdesc->sg,
+				size, (IOMMU_READ | IOMMU_WRITE));
+	if (ret) {
+		KGSL_CORE_ERR("iommu_map_range(%p, %x, %p, %d, %d) "
+				"failed with err: %d\n", iommu_pt->domain,
+				iommu_virt_addr, memdesc->sg, size,
+				(IOMMU_READ | IOMMU_WRITE), ret);
+		return ret;
+	}
+
+#ifdef CONFIG_KGSL_PER_PROCESS_PAGE_TABLE
+	/*
+	 * Flushing only required if per process pagetables are used. With
+	 * global case, flushing will happen inside iommu_map function
+	 */
+	if (!ret)
+		*tlb_flags = UINT_MAX;
+#endif
+	return ret;
+}
+
+static void kgsl_iommu_stop(struct kgsl_mmu *mmu)
+{
+	/*
+	 *  stop device mmu
+	 *
+	 *  call this with the global lock held
+	 */
+
+	if (mmu->flags & KGSL_FLAGS_STARTED) {
+		kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000000);
+		/* detach iommu attachment */
+		kgsl_detach_pagetable_iommu_domain(mmu);
+		mmu->hwpagetable = NULL;
+
+		mmu->flags &= ~KGSL_FLAGS_STARTED;
+	}
+}
+
+static int kgsl_iommu_close(struct kgsl_mmu *mmu)
+{
+	struct kgsl_iommu *iommu = mmu->priv;
+	int i;
+	for (i = 0; i < iommu->unit_count; i++) {
+		if (iommu->iommu_units[i].reg_map.gpuaddr)
+			kgsl_mmu_unmap(mmu->defaultpagetable,
+			&(iommu->iommu_units[i].reg_map));
+		if (iommu->iommu_units[i].reg_map.hostptr)
+			iounmap(iommu->iommu_units[i].reg_map.hostptr);
+		kgsl_sg_free(iommu->iommu_units[i].reg_map.sg,
+				iommu->iommu_units[i].reg_map.sglen);
+	}
+	if (mmu->defaultpagetable)
+		kgsl_mmu_putpagetable(mmu->defaultpagetable);
+	kfree(iommu->asids);
+	kfree(iommu);
+
+	return 0;
+}
+
+static unsigned int
+kgsl_iommu_get_current_ptbase(struct kgsl_mmu *mmu)
+{
+	unsigned int pt_base;
+	struct kgsl_iommu *iommu = mmu->priv;
+	/* Return the current pt base by reading IOMMU pt_base register */
+	kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_USER);
+	pt_base = readl_relaxed(iommu->iommu_units[0].reg_map.hostptr +
+			(KGSL_IOMMU_CONTEXT_USER << KGSL_IOMMU_CTX_SHIFT) +
+			KGSL_IOMMU_TTBR0);
+	kgsl_iommu_disable_clk(mmu);
+	return pt_base & (KGSL_IOMMU_TTBR0_PA_MASK <<
+				KGSL_IOMMU_TTBR0_PA_SHIFT);
+}
+
+/*
+ * kgsl_iommu_get_hwpagetable_asid - Returns asid(application space ID) for a
+ * pagetable
+ * @mmu - Pointer to mmu structure
+ *
+ * Allocates an asid to a IOMMU domain if it does not already have one. asid's
+ * are unique identifiers for pagetable that can be used to selectively flush
+ * tlb entries of the IOMMU unit.
+ * Return - asid to be used with the IOMMU domain
+ */
+static int kgsl_iommu_get_hwpagetable_asid(struct kgsl_mmu *mmu)
+{
+	struct kgsl_iommu *iommu = mmu->priv;
+	struct kgsl_iommu_pt *iommu_pt = mmu->hwpagetable->priv;
+
+	/*
+	 * If the iommu pagetable does not have any asid assigned and is not the
+	 * default pagetable then assign asid.
+	 */
+	if (!iommu_pt->asid && iommu_pt != mmu->defaultpagetable->priv) {
+		iommu_pt->asid = find_first_zero_bit(iommu->asids,
+							KGSL_IOMMU_MAX_ASIDS);
+		/* No free bits means reuse asid */
+		if (iommu_pt->asid >= KGSL_IOMMU_MAX_ASIDS) {
+			iommu_pt->asid = KGSL_IOMMU_ASID_REUSE;
+			iommu->asid_reuse++;
+		}
+		set_bit(iommu_pt->asid, iommu->asids);
+		/*
+		 * Store pointer to asids list so that during pagetable destroy
+		 * the asid assigned to this pagetable may be cleared
+		 */
+		iommu_pt->iommu = iommu;
+	}
+	/* Return the asid + the constant part of asid that never changes */
+	return (iommu_pt->asid & (KGSL_IOMMU_CONTEXTIDR_ASID_MASK <<
+				KGSL_IOMMU_CONTEXTIDR_ASID_SHIFT)) +
+		(iommu->asid & ~(KGSL_IOMMU_CONTEXTIDR_ASID_MASK <<
+				KGSL_IOMMU_CONTEXTIDR_ASID_SHIFT));
+}
+
+/*
+ * kgsl_iommu_default_setstate - Change the IOMMU pagetable or flush IOMMU tlb
+ * of the primary context bank
+ * @mmu - Pointer to mmu structure
+ * @flags - Flags indicating whether pagetable has to chnage or tlb is to be
+ * flushed or both
+ *
+ * Based on flags set the new pagetable fo the IOMMU unit or flush it's tlb or
+ * do both by doing direct register writes to the IOMMu registers through the
+ * cpu
+ * Return - void
+ */
+static void kgsl_iommu_default_setstate(struct kgsl_mmu *mmu,
+					uint32_t flags)
+{
+	struct kgsl_iommu *iommu = mmu->priv;
+	int temp;
+	int i;
+	unsigned int pt_base = kgsl_iommu_pt_get_base_addr(
+					mmu->hwpagetable);
+	unsigned int pt_val;
+
+	if (kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_USER)) {
+		KGSL_DRV_ERR(mmu->device, "Failed to enable iommu clocks\n");
+		return;
+	}
+	/* Mask off the lsb of the pt base address since lsb will not change */
+	pt_base &= (KGSL_IOMMU_TTBR0_PA_MASK << KGSL_IOMMU_TTBR0_PA_SHIFT);
+	if (flags & KGSL_MMUFLAGS_PTUPDATE) {
+		kgsl_idle(mmu->device, KGSL_TIMEOUT_DEFAULT);
+		for (i = 0; i < iommu->unit_count; i++) {
+			/* get the lsb value which should not change when
+			 * changing ttbr0 */
+			pt_val = kgsl_iommu_get_pt_lsb(mmu, i,
+						KGSL_IOMMU_CONTEXT_USER);
+			pt_val += pt_base;
+
+			KGSL_IOMMU_SET_IOMMU_REG(
+				iommu->iommu_units[i].reg_map.hostptr,
+				KGSL_IOMMU_CONTEXT_USER, TTBR0, pt_val);
+
+			mb();
+			temp = KGSL_IOMMU_GET_IOMMU_REG(
+				iommu->iommu_units[i].reg_map.hostptr,
+				KGSL_IOMMU_CONTEXT_USER, TTBR0);
+			/* Set asid */
+			KGSL_IOMMU_SET_IOMMU_REG(
+				iommu->iommu_units[i].reg_map.hostptr,
+				KGSL_IOMMU_CONTEXT_USER, CONTEXTIDR,
+				kgsl_iommu_get_hwpagetable_asid(mmu));
+			mb();
+			temp = KGSL_IOMMU_GET_IOMMU_REG(
+					iommu->iommu_units[i].reg_map.hostptr,
+					KGSL_IOMMU_CONTEXT_USER, CONTEXTIDR);
+		}
+	}
+	/* Flush tlb */
+	if (flags & KGSL_MMUFLAGS_TLBFLUSH) {
+		for (i = 0; i < iommu->unit_count; i++) {
+			KGSL_IOMMU_SET_IOMMU_REG(
+				iommu->iommu_units[i].reg_map.hostptr,
+				KGSL_IOMMU_CONTEXT_USER, CTX_TLBIASID,
+				kgsl_iommu_get_hwpagetable_asid(mmu));
+			mb();
+		}
+	}
+	/* Disable smmu clock */
+	kgsl_iommu_disable_clk(mmu);
+}
+
+/*
+ * kgsl_iommu_get_reg_map_desc - Returns an array of pointers that contain
+ * the address of memory descriptors which map the IOMMU registers
+ * @mmu - Pointer to mmu structure
+ * @reg_map_desc - Out parameter in which the address of the array containing
+ * pointers to register map descriptors is returned. The caller is supposed
+ * to free this array
+ *
+ * Return - The number of iommu units which is also the number of register
+ * mapped descriptor arrays which the out parameter will have
+ */
+static int kgsl_iommu_get_reg_map_desc(struct kgsl_mmu *mmu,
+					void **reg_map_desc)
+{
+	struct kgsl_iommu *iommu = mmu->priv;
+	void **reg_desc_ptr;
+	int i;
+
+	/*
+	 * Alocate array of pointers that will hold address of the register map
+	 * descriptors
+	 */
+	reg_desc_ptr = kmalloc(iommu->unit_count *
+			sizeof(struct kgsl_memdesc *), GFP_KERNEL);
+	if (!reg_desc_ptr) {
+		KGSL_CORE_ERR("Failed to kmalloc(%d)\n",
+			iommu->unit_count * sizeof(struct kgsl_memdesc *));
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < iommu->unit_count; i++)
+		reg_desc_ptr[i] = &(iommu->iommu_units[i].reg_map);
+
+	*reg_map_desc = reg_desc_ptr;
+	return i;
+}
+
+struct kgsl_mmu_ops iommu_ops = {
+	.mmu_init = kgsl_iommu_init,
+	.mmu_close = kgsl_iommu_close,
+	.mmu_start = kgsl_iommu_start,
+	.mmu_stop = kgsl_iommu_stop,
+	.mmu_setstate = kgsl_iommu_setstate,
+	.mmu_device_setstate = kgsl_iommu_default_setstate,
+	.mmu_pagefault = NULL,
+	.mmu_get_current_ptbase = kgsl_iommu_get_current_ptbase,
+	.mmu_enable_clk = kgsl_iommu_enable_clk,
+	.mmu_disable_clk = kgsl_iommu_disable_clk,
+	.mmu_get_hwpagetable_asid = kgsl_iommu_get_hwpagetable_asid,
+	.mmu_get_pt_lsb = kgsl_iommu_get_pt_lsb,
+	.mmu_get_reg_map_desc = kgsl_iommu_get_reg_map_desc,
+};
+
+struct kgsl_mmu_pt_ops iommu_pt_ops = {
+	.mmu_map = kgsl_iommu_map,
+	.mmu_unmap = kgsl_iommu_unmap,
+	.mmu_create_pagetable = kgsl_iommu_create_pagetable,
+	.mmu_destroy_pagetable = kgsl_iommu_destroy_pagetable,
+	.mmu_pt_equal = kgsl_iommu_pt_equal,
+	.mmu_pt_get_base_addr = kgsl_iommu_pt_get_base_addr,
+};
diff --git a/drivers/gpu/msm/kgsl_iommu.h b/drivers/gpu/msm/kgsl_iommu.h
new file mode 100644
index 0000000..efc3d9c
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_iommu.h
@@ -0,0 +1,134 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __KGSL_IOMMU_H
+#define __KGSL_IOMMU_H
+
+#include <mach/iommu.h>
+
+/* IOMMU registers and masks */
+#define KGSL_IOMMU_TTBR0			0x10
+#define KGSL_IOMMU_TTBR1			0x14
+#define KGSL_IOMMU_FSR				0x20
+
+#define KGSL_IOMMU_TTBR0_PA_MASK		0x0003FFFF
+#define KGSL_IOMMU_TTBR0_PA_SHIFT		14
+#define KGSL_IOMMU_CTX_TLBIALL			0x800
+#define KGSL_IOMMU_CONTEXTIDR			0x8
+#define KGSL_IOMMU_CONTEXTIDR_ASID_MASK		0xFF
+#define KGSL_IOMMU_CONTEXTIDR_ASID_SHIFT	0
+#define KGSL_IOMMU_CTX_TLBIASID			0x804
+#define KGSL_IOMMU_CTX_SHIFT			12
+
+#define KGSL_IOMMU_MAX_ASIDS			256
+#define KGSL_IOMMU_ASID_REUSE			2
+
+/*
+ * Max number of iommu units that the gpu core can have
+ * On APQ8064, KGSL can control a maximum of 2 IOMMU units.
+ */
+#define KGSL_IOMMU_MAX_UNITS 2
+
+/* Max number of iommu contexts per IOMMU unit */
+#define KGSL_IOMMU_MAX_DEVS_PER_UNIT 2
+
+/* Macros to read/write IOMMU registers */
+#define KGSL_IOMMU_SET_IOMMU_REG(base_addr, ctx, REG, val)		\
+		writel_relaxed(val, base_addr +				\
+				(ctx << KGSL_IOMMU_CTX_SHIFT) +		\
+				KGSL_IOMMU_##REG)
+
+#define KGSL_IOMMU_GET_IOMMU_REG(base_addr, ctx, REG)			\
+		readl_relaxed(base_addr +				\
+			(ctx << KGSL_IOMMU_CTX_SHIFT) +			\
+			KGSL_IOMMU_##REG)
+
+/* Gets the lsb value of pagetable */
+#define KGSL_IOMMMU_PT_LSB(pt_val)					\
+		(pt_val & ~(KGSL_IOMMU_TTBR0_PA_MASK <<			\
+				KGSL_IOMMU_TTBR0_PA_SHIFT))
+
+/* offset at which a nop command is placed in setstate_memory */
+#define KGSL_IOMMU_SETSTATE_NOP_OFFSET	1024
+
+/*
+ * struct kgsl_iommu_device - Structure holding data about iommu contexts
+ * @dev: Device pointer to iommu context
+ * @attached: Indicates whether this iommu context is presently attached to
+ * a pagetable/domain or not
+ * @pt_lsb: The LSB of IOMMU_TTBR0 register which is the pagetable
+ * register
+ * @ctx_id: This iommu units context id. It can be either 0 or 1
+ * @clk_enabled: If set indicates that iommu clocks of this iommu context
+ * are on, else the clocks are off
+ */
+struct kgsl_iommu_device {
+	struct device *dev;
+	bool attached;
+	unsigned int pt_lsb;
+	enum kgsl_iommu_context_id ctx_id;
+	bool clk_enabled;
+	struct kgsl_device *kgsldev;
+};
+
+/*
+ * struct kgsl_iommu_unit - Structure holding data about iommu units. An IOMMU
+ * units is basically a separte IOMMU h/w block with it's own IOMMU contexts
+ * @dev: Pointer to array of struct kgsl_iommu_device which has information
+ * about the IOMMU contexts under this IOMMU unit
+ * @dev_count: Number of IOMMU contexts that are valid in the previous feild
+ * @reg_map: Memory descriptor which holds the mapped address of this IOMMU
+ * units register range
+ */
+struct kgsl_iommu_unit {
+	struct kgsl_iommu_device dev[KGSL_IOMMU_MAX_DEVS_PER_UNIT];
+	unsigned int dev_count;
+	struct kgsl_memdesc reg_map;
+};
+
+/*
+ * struct kgsl_iommu - Structure holding iommu data for kgsl driver
+ * @dev: Array of kgsl_iommu_device which contain information about
+ * iommu contexts owned by graphics cores
+ * @unit_count: Number of IOMMU units that are available for this
+ * instance of the IOMMU driver
+ * @iommu_last_cmd_ts: The timestamp of last command submitted that
+ * aceeses iommu registers
+ * @device: Pointer to kgsl device
+ * @asids: A bit structure indicating which id's are presently used
+ * @asid: Contains the initial value of IOMMU_CONTEXTIDR when a domain
+ * is first attached
+ * asid_reuse: Holds the number of times the reuse asid is reused
+ */
+struct kgsl_iommu {
+	struct kgsl_iommu_unit iommu_units[KGSL_IOMMU_MAX_UNITS];
+	unsigned int unit_count;
+	unsigned int iommu_last_cmd_ts;
+	struct kgsl_device *device;
+	unsigned long *asids;
+	unsigned int asid;
+	unsigned int asid_reuse;
+};
+
+/*
+ * struct kgsl_iommu_pt - Iommu pagetable structure private to kgsl driver
+ * @domain: Pointer to the iommu domain that contains the iommu pagetable
+ * @iommu: Pointer to iommu structure
+ * @asid: The asid assigned to this domain
+ */
+struct kgsl_iommu_pt {
+	struct iommu_domain *domain;
+	struct kgsl_iommu *iommu;
+	unsigned int asid;
+};
+
+#endif
diff --git a/drivers/gpu/msm/kgsl_log.h b/drivers/gpu/msm/kgsl_log.h
new file mode 100644
index 0000000..6fd28ab
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_log.h
@@ -0,0 +1,112 @@
+/* Copyright (c) 2002,2008-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __KGSL_LOG_H
+#define __KGSL_LOG_H
+
+extern unsigned int kgsl_cff_dump_enable;
+
+#define KGSL_LOG_INFO(dev, lvl, fmt, args...) \
+	do { \
+		if ((lvl) >= 6)  \
+			dev_info(dev, "|%s| " fmt, \
+					__func__, ##args);\
+	} while (0)
+
+#define KGSL_LOG_WARN(dev, lvl, fmt, args...) \
+	do { \
+		if ((lvl) >= 4)  \
+			dev_warn(dev, "|%s| " fmt, \
+					__func__, ##args);\
+	} while (0)
+
+#define KGSL_LOG_ERR(dev, lvl, fmt, args...) \
+	do { \
+		if ((lvl) >= 3)  \
+			dev_err(dev, "|%s| " fmt, \
+					__func__, ##args);\
+	} while (0)
+
+#define KGSL_LOG_CRIT(dev, lvl, fmt, args...) \
+	do { \
+		if ((lvl) >= 2) \
+			dev_crit(dev, "|%s| " fmt, \
+					__func__, ##args);\
+	} while (0)
+
+#define KGSL_LOG_POSTMORTEM_WRITE(_dev, fmt, args...) \
+	do { dev_crit(_dev->dev, fmt, ##args); } while (0)
+
+#define KGSL_LOG_DUMP(_dev, fmt, args...)	dev_err(_dev->dev, fmt, ##args)
+
+#define KGSL_DEV_ERR_ONCE(_dev, fmt, args...) \
+({ \
+	static bool kgsl_dev_err_once; \
+							\
+	if (!kgsl_dev_err_once) { \
+		kgsl_dev_err_once = true; \
+		dev_crit(_dev->dev, "|%s| " fmt, __func__, ##args); \
+	} \
+})
+
+#define KGSL_DRV_INFO(_dev, fmt, args...) \
+KGSL_LOG_INFO(_dev->dev, _dev->drv_log, fmt, ##args)
+#define KGSL_DRV_WARN(_dev, fmt, args...) \
+KGSL_LOG_WARN(_dev->dev, _dev->drv_log, fmt, ##args)
+#define KGSL_DRV_ERR(_dev, fmt, args...)  \
+KGSL_LOG_ERR(_dev->dev, _dev->drv_log, fmt, ##args)
+#define KGSL_DRV_CRIT(_dev, fmt, args...) \
+KGSL_LOG_CRIT(_dev->dev, _dev->drv_log, fmt, ##args)
+
+#define KGSL_CMD_INFO(_dev, fmt, args...) \
+KGSL_LOG_INFO(_dev->dev, _dev->cmd_log, fmt, ##args)
+#define KGSL_CMD_WARN(_dev, fmt, args...) \
+KGSL_LOG_WARN(_dev->dev, _dev->cmd_log, fmt, ##args)
+#define KGSL_CMD_ERR(_dev, fmt, args...) \
+KGSL_LOG_ERR(_dev->dev, _dev->cmd_log, fmt, ##args)
+#define KGSL_CMD_CRIT(_dev, fmt, args...) \
+KGSL_LOG_CRIT(_dev->dev, _dev->cmd_log, fmt, ##args)
+
+#define KGSL_CTXT_INFO(_dev, fmt, args...) \
+KGSL_LOG_INFO(_dev->dev, _dev->ctxt_log, fmt, ##args)
+#define KGSL_CTXT_WARN(_dev, fmt, args...) \
+KGSL_LOG_WARN(_dev->dev, _dev->ctxt_log, fmt, ##args)
+#define KGSL_CTXT_ERR(_dev, fmt, args...)  \
+KGSL_LOG_ERR(_dev->dev, _dev->ctxt_log, fmt, ##args)
+#define KGSL_CTXT_CRIT(_dev, fmt, args...) \
+KGSL_LOG_CRIT(_dev->dev, _dev->ctxt_log, fmt, ##args)
+
+#define KGSL_MEM_INFO(_dev, fmt, args...) \
+KGSL_LOG_INFO(_dev->dev, _dev->mem_log, fmt, ##args)
+#define KGSL_MEM_WARN(_dev, fmt, args...) \
+KGSL_LOG_WARN(_dev->dev, _dev->mem_log, fmt, ##args)
+#define KGSL_MEM_ERR(_dev, fmt, args...)  \
+KGSL_LOG_ERR(_dev->dev, _dev->mem_log, fmt, ##args)
+#define KGSL_MEM_CRIT(_dev, fmt, args...) \
+KGSL_LOG_CRIT(_dev->dev, _dev->mem_log, fmt, ##args)
+
+#define KGSL_PWR_INFO(_dev, fmt, args...) \
+KGSL_LOG_INFO(_dev->dev, _dev->pwr_log, fmt, ##args)
+#define KGSL_PWR_WARN(_dev, fmt, args...) \
+KGSL_LOG_WARN(_dev->dev, _dev->pwr_log, fmt, ##args)
+#define KGSL_PWR_ERR(_dev, fmt, args...) \
+KGSL_LOG_ERR(_dev->dev, _dev->pwr_log, fmt, ##args)
+#define KGSL_PWR_CRIT(_dev, fmt, args...) \
+KGSL_LOG_CRIT(_dev->dev, _dev->pwr_log, fmt, ##args)
+
+/* Core error messages - these are for core KGSL functions that have
+   no device associated with them (such as memory) */
+
+#define KGSL_CORE_ERR(fmt, args...) \
+pr_err("kgsl: %s: " fmt, __func__, ##args)
+
+#endif /* __KGSL_LOG_H */
diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c
new file mode 100644
index 0000000..ff9f0b8
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_mmu.c
@@ -0,0 +1,823 @@
+/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/export.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/genalloc.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/iommu.h>
+#include <mach/socinfo.h>
+
+#include "kgsl.h"
+#include "kgsl_mmu.h"
+#include "kgsl_device.h"
+#include "kgsl_sharedmem.h"
+#include "adreno_postmortem.h"
+
+#define KGSL_MMU_ALIGN_SHIFT    13
+#define KGSL_MMU_ALIGN_MASK     (~((1 << KGSL_MMU_ALIGN_SHIFT) - 1))
+
+static enum kgsl_mmutype kgsl_mmu_type;
+
+static void pagetable_remove_sysfs_objects(struct kgsl_pagetable *pagetable);
+
+static int kgsl_cleanup_pt(struct kgsl_pagetable *pt)
+{
+	int i;
+	/* For IOMMU only unmap the global structures to global pt */
+	if ((KGSL_MMU_TYPE_NONE != kgsl_mmu_type) &&
+		(KGSL_MMU_TYPE_IOMMU == kgsl_mmu_type) &&
+		(KGSL_MMU_GLOBAL_PT !=  pt->name))
+		return 0;
+	for (i = 0; i < KGSL_DEVICE_MAX; i++) {
+		struct kgsl_device *device = kgsl_driver.devp[i];
+		if (device)
+			device->ftbl->cleanup_pt(device, pt);
+	}
+	return 0;
+}
+
+
+static int kgsl_setup_pt(struct kgsl_pagetable *pt)
+{
+	int i = 0;
+	int status = 0;
+
+	/* For IOMMU only map the global structures to global pt */
+	if ((KGSL_MMU_TYPE_NONE != kgsl_mmu_type) &&
+		(KGSL_MMU_TYPE_IOMMU == kgsl_mmu_type) &&
+		(KGSL_MMU_GLOBAL_PT !=  pt->name))
+		return 0;
+	for (i = 0; i < KGSL_DEVICE_MAX; i++) {
+		struct kgsl_device *device = kgsl_driver.devp[i];
+		if (device) {
+			status = device->ftbl->setup_pt(device, pt);
+			if (status)
+				goto error_pt;
+		}
+	}
+	return status;
+error_pt:
+	while (i >= 0) {
+		struct kgsl_device *device = kgsl_driver.devp[i];
+		if (device)
+			device->ftbl->cleanup_pt(device, pt);
+		i--;
+	}
+	return status;
+}
+
+static void kgsl_destroy_pagetable(struct kref *kref)
+{
+	struct kgsl_pagetable *pagetable = container_of(kref,
+		struct kgsl_pagetable, refcount);
+	unsigned long flags;
+
+	spin_lock_irqsave(&kgsl_driver.ptlock, flags);
+	list_del(&pagetable->list);
+	spin_unlock_irqrestore(&kgsl_driver.ptlock, flags);
+
+	pagetable_remove_sysfs_objects(pagetable);
+
+	kgsl_cleanup_pt(pagetable);
+
+	if (pagetable->kgsl_pool)
+		gen_pool_destroy(pagetable->kgsl_pool);
+	if (pagetable->pool)
+		gen_pool_destroy(pagetable->pool);
+
+	pagetable->pt_ops->mmu_destroy_pagetable(pagetable->priv);
+
+	kfree(pagetable);
+}
+
+static inline void kgsl_put_pagetable(struct kgsl_pagetable *pagetable)
+{
+	if (pagetable)
+		kref_put(&pagetable->refcount, kgsl_destroy_pagetable);
+}
+
+static struct kgsl_pagetable *
+kgsl_get_pagetable(unsigned long name)
+{
+	struct kgsl_pagetable *pt, *ret = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&kgsl_driver.ptlock, flags);
+	list_for_each_entry(pt, &kgsl_driver.pagetable_list, list) {
+		if (pt->name == name) {
+			ret = pt;
+			kref_get(&ret->refcount);
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&kgsl_driver.ptlock, flags);
+	return ret;
+}
+
+static struct kgsl_pagetable *
+_get_pt_from_kobj(struct kobject *kobj)
+{
+	unsigned long ptname;
+
+	if (!kobj)
+		return NULL;
+
+	if (sscanf(kobj->name, "%ld", &ptname) != 1)
+		return NULL;
+
+	return kgsl_get_pagetable(ptname);
+}
+
+static ssize_t
+sysfs_show_entries(struct kobject *kobj,
+		   struct kobj_attribute *attr,
+		   char *buf)
+{
+	struct kgsl_pagetable *pt;
+	int ret = 0;
+
+	pt = _get_pt_from_kobj(kobj);
+
+	if (pt)
+		ret += snprintf(buf, PAGE_SIZE, "%d\n", pt->stats.entries);
+
+	kgsl_put_pagetable(pt);
+	return ret;
+}
+
+static ssize_t
+sysfs_show_mapped(struct kobject *kobj,
+		  struct kobj_attribute *attr,
+		  char *buf)
+{
+	struct kgsl_pagetable *pt;
+	int ret = 0;
+
+	pt = _get_pt_from_kobj(kobj);
+
+	if (pt)
+		ret += snprintf(buf, PAGE_SIZE, "%d\n", pt->stats.mapped);
+
+	kgsl_put_pagetable(pt);
+	return ret;
+}
+
+static ssize_t
+sysfs_show_va_range(struct kobject *kobj,
+		    struct kobj_attribute *attr,
+		    char *buf)
+{
+	struct kgsl_pagetable *pt;
+	int ret = 0;
+
+	pt = _get_pt_from_kobj(kobj);
+
+	if (pt) {
+		ret += snprintf(buf, PAGE_SIZE, "0x%x\n",
+			kgsl_mmu_get_ptsize());
+	}
+
+	kgsl_put_pagetable(pt);
+	return ret;
+}
+
+static ssize_t
+sysfs_show_max_mapped(struct kobject *kobj,
+		      struct kobj_attribute *attr,
+		      char *buf)
+{
+	struct kgsl_pagetable *pt;
+	int ret = 0;
+
+	pt = _get_pt_from_kobj(kobj);
+
+	if (pt)
+		ret += snprintf(buf, PAGE_SIZE, "%d\n", pt->stats.max_mapped);
+
+	kgsl_put_pagetable(pt);
+	return ret;
+}
+
+static ssize_t
+sysfs_show_max_entries(struct kobject *kobj,
+		       struct kobj_attribute *attr,
+		       char *buf)
+{
+	struct kgsl_pagetable *pt;
+	int ret = 0;
+
+	pt = _get_pt_from_kobj(kobj);
+
+	if (pt)
+		ret += snprintf(buf, PAGE_SIZE, "%d\n", pt->stats.max_entries);
+
+	kgsl_put_pagetable(pt);
+	return ret;
+}
+
+static struct kobj_attribute attr_entries = {
+	.attr = { .name = "entries", .mode = 0444 },
+	.show = sysfs_show_entries,
+	.store = NULL,
+};
+
+static struct kobj_attribute attr_mapped = {
+	.attr = { .name = "mapped", .mode = 0444 },
+	.show = sysfs_show_mapped,
+	.store = NULL,
+};
+
+static struct kobj_attribute attr_va_range = {
+	.attr = { .name = "va_range", .mode = 0444 },
+	.show = sysfs_show_va_range,
+	.store = NULL,
+};
+
+static struct kobj_attribute attr_max_mapped = {
+	.attr = { .name = "max_mapped", .mode = 0444 },
+	.show = sysfs_show_max_mapped,
+	.store = NULL,
+};
+
+static struct kobj_attribute attr_max_entries = {
+	.attr = { .name = "max_entries", .mode = 0444 },
+	.show = sysfs_show_max_entries,
+	.store = NULL,
+};
+
+static struct attribute *pagetable_attrs[] = {
+	&attr_entries.attr,
+	&attr_mapped.attr,
+	&attr_va_range.attr,
+	&attr_max_mapped.attr,
+	&attr_max_entries.attr,
+	NULL,
+};
+
+static struct attribute_group pagetable_attr_group = {
+	.attrs = pagetable_attrs,
+};
+
+static void
+pagetable_remove_sysfs_objects(struct kgsl_pagetable *pagetable)
+{
+	if (pagetable->kobj)
+		sysfs_remove_group(pagetable->kobj,
+				   &pagetable_attr_group);
+
+	kobject_put(pagetable->kobj);
+}
+
+static int
+pagetable_add_sysfs_objects(struct kgsl_pagetable *pagetable)
+{
+	char ptname[16];
+	int ret = -ENOMEM;
+
+	snprintf(ptname, sizeof(ptname), "%d", pagetable->name);
+	pagetable->kobj = kobject_create_and_add(ptname,
+						 kgsl_driver.ptkobj);
+	if (pagetable->kobj == NULL)
+		goto err;
+
+	ret = sysfs_create_group(pagetable->kobj, &pagetable_attr_group);
+
+err:
+	if (ret) {
+		if (pagetable->kobj)
+			kobject_put(pagetable->kobj);
+
+		pagetable->kobj = NULL;
+	}
+
+	return ret;
+}
+
+unsigned int kgsl_mmu_get_ptsize(void)
+{
+	/*
+	 * For IOMMU, we could do up to 4G virtual range if we wanted to, but
+	 * it makes more sense to return a smaller range and leave the rest of
+	 * the virtual range for future improvements
+	 */
+
+	if (KGSL_MMU_TYPE_GPU == kgsl_mmu_type)
+		return CONFIG_MSM_KGSL_PAGE_TABLE_SIZE;
+	else if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_type)
+		return SZ_2G;
+	else
+		return 0;
+}
+
+int
+kgsl_mmu_get_ptname_from_ptbase(unsigned int pt_base)
+{
+	struct kgsl_pagetable *pt;
+	int ptid = -1;
+
+	spin_lock(&kgsl_driver.ptlock);
+	list_for_each_entry(pt, &kgsl_driver.pagetable_list, list) {
+		if (pt->pt_ops->mmu_pt_equal(pt, pt_base)) {
+			ptid = (int) pt->name;
+			break;
+		}
+	}
+	spin_unlock(&kgsl_driver.ptlock);
+
+	return ptid;
+}
+EXPORT_SYMBOL(kgsl_mmu_get_ptname_from_ptbase);
+
+int kgsl_mmu_init(struct kgsl_device *device)
+{
+	int status = 0;
+	struct kgsl_mmu *mmu = &device->mmu;
+
+	mmu->device = device;
+	status = kgsl_allocate_contiguous(&mmu->setstate_memory, PAGE_SIZE);
+	if (status)
+		return status;
+	kgsl_sharedmem_set(&mmu->setstate_memory, 0, 0,
+				mmu->setstate_memory.size);
+
+	if (KGSL_MMU_TYPE_NONE == kgsl_mmu_type) {
+		dev_info(device->dev, "|%s| MMU type set for device is "
+				"NOMMU\n", __func__);
+		goto done;
+	} else if (KGSL_MMU_TYPE_GPU == kgsl_mmu_type)
+		mmu->mmu_ops = &gpummu_ops;
+	else if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_type)
+		mmu->mmu_ops = &iommu_ops;
+
+	status =  mmu->mmu_ops->mmu_init(mmu);
+done:
+	if (status)
+		kgsl_sharedmem_free(&mmu->setstate_memory);
+	return status;
+}
+EXPORT_SYMBOL(kgsl_mmu_init);
+
+int kgsl_mmu_start(struct kgsl_device *device)
+{
+	struct kgsl_mmu *mmu = &device->mmu;
+
+	if (kgsl_mmu_type == KGSL_MMU_TYPE_NONE) {
+		kgsl_regwrite(device, MH_MMU_CONFIG, 0);
+		/* Setup gpuaddr of global mappings */
+		if (!mmu->setstate_memory.gpuaddr)
+			kgsl_setup_pt(NULL);
+		return 0;
+	} else {
+		return mmu->mmu_ops->mmu_start(mmu);
+	}
+}
+EXPORT_SYMBOL(kgsl_mmu_start);
+
+static void mh_axi_error(struct kgsl_device *device, const char* type)
+{
+	unsigned int reg, gpu_err, phys_err, pt_base;
+
+	kgsl_regread(device, MH_AXI_ERROR, &reg);
+	pt_base = kgsl_mmu_get_current_ptbase(&device->mmu);
+	/*
+	 * Read gpu virtual and physical addresses that
+	 * caused the error from the debug data.
+	 */
+	kgsl_regwrite(device, MH_DEBUG_CTRL, 44);
+	kgsl_regread(device, MH_DEBUG_DATA, &gpu_err);
+	kgsl_regwrite(device, MH_DEBUG_CTRL, 45);
+	kgsl_regread(device, MH_DEBUG_DATA, &phys_err);
+	KGSL_MEM_CRIT(device,
+			"axi %s error: %08x pt %08x gpu %08x phys %08x\n",
+			type, reg, pt_base, gpu_err, phys_err);
+}
+
+void kgsl_mh_intrcallback(struct kgsl_device *device)
+{
+	unsigned int status = 0;
+
+	kgsl_regread(device, MH_INTERRUPT_STATUS, &status);
+
+	if (status & MH_INTERRUPT_MASK__AXI_READ_ERROR)
+		mh_axi_error(device, "read");
+	if (status & MH_INTERRUPT_MASK__AXI_WRITE_ERROR)
+		mh_axi_error(device, "write");
+	if (status & MH_INTERRUPT_MASK__MMU_PAGE_FAULT)
+		device->mmu.mmu_ops->mmu_pagefault(&device->mmu);
+
+	status &= KGSL_MMU_INT_MASK;
+	kgsl_regwrite(device, MH_INTERRUPT_CLEAR, status);
+}
+EXPORT_SYMBOL(kgsl_mh_intrcallback);
+
+static struct kgsl_pagetable *kgsl_mmu_createpagetableobject(
+				unsigned int name)
+{
+	int status = 0;
+	struct kgsl_pagetable *pagetable = NULL;
+	unsigned long flags;
+	unsigned int ptsize;
+
+	pagetable = kzalloc(sizeof(struct kgsl_pagetable), GFP_KERNEL);
+	if (pagetable == NULL) {
+		KGSL_CORE_ERR("kzalloc(%d) failed\n",
+			sizeof(struct kgsl_pagetable));
+		return NULL;
+	}
+
+	kref_init(&pagetable->refcount);
+
+	spin_lock_init(&pagetable->lock);
+
+	ptsize = kgsl_mmu_get_ptsize();
+
+	pagetable->name = name;
+	pagetable->max_entries = KGSL_PAGETABLE_ENTRIES(ptsize);
+
+	/*
+	 * create a separate kgsl pool for IOMMU, global mappings can be mapped
+	 * just once from this pool of the defaultpagetable
+	 */
+	if ((KGSL_MMU_TYPE_IOMMU == kgsl_mmu_get_mmutype()) &&
+		(KGSL_MMU_GLOBAL_PT == name)) {
+		pagetable->kgsl_pool = gen_pool_create(KGSL_MMU_ALIGN_SHIFT,
+						       -1);
+		if (pagetable->kgsl_pool == NULL) {
+			KGSL_CORE_ERR("gen_pool_create(%d) failed\n",
+					KGSL_MMU_ALIGN_SHIFT);
+			goto err_alloc;
+		}
+		if (gen_pool_add(pagetable->kgsl_pool,
+			KGSL_IOMMU_GLOBAL_MEM_BASE,
+			KGSL_IOMMU_GLOBAL_MEM_SIZE, -1)) {
+			KGSL_CORE_ERR("gen_pool_add failed\n");
+			goto err_kgsl_pool;
+		}
+	}
+
+	pagetable->pool = gen_pool_create(KGSL_MMU_ALIGN_SHIFT, -1);
+	if (pagetable->pool == NULL) {
+		KGSL_CORE_ERR("gen_pool_create(%d) failed\n",
+			      KGSL_MMU_ALIGN_SHIFT);
+		goto err_kgsl_pool;
+	}
+
+	if (gen_pool_add(pagetable->pool, KGSL_PAGETABLE_BASE,
+				ptsize, -1)) {
+		KGSL_CORE_ERR("gen_pool_add failed\n");
+		goto err_pool;
+	}
+
+	if (KGSL_MMU_TYPE_GPU == kgsl_mmu_type)
+		pagetable->pt_ops = &gpummu_pt_ops;
+	else if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_type)
+		pagetable->pt_ops = &iommu_pt_ops;
+
+	pagetable->priv = pagetable->pt_ops->mmu_create_pagetable();
+	if (!pagetable->priv)
+		goto err_pool;
+
+	status = kgsl_setup_pt(pagetable);
+	if (status)
+		goto err_mmu_create;
+
+	spin_lock_irqsave(&kgsl_driver.ptlock, flags);
+	list_add(&pagetable->list, &kgsl_driver.pagetable_list);
+	spin_unlock_irqrestore(&kgsl_driver.ptlock, flags);
+
+	/* Create the sysfs entries */
+	pagetable_add_sysfs_objects(pagetable);
+
+	return pagetable;
+
+err_mmu_create:
+	pagetable->pt_ops->mmu_destroy_pagetable(pagetable->priv);
+err_pool:
+	gen_pool_destroy(pagetable->pool);
+err_kgsl_pool:
+	if (pagetable->kgsl_pool)
+		gen_pool_destroy(pagetable->kgsl_pool);
+err_alloc:
+	kfree(pagetable);
+
+	return NULL;
+}
+
+struct kgsl_pagetable *kgsl_mmu_getpagetable(unsigned long name)
+{
+	struct kgsl_pagetable *pt;
+
+	if (KGSL_MMU_TYPE_NONE == kgsl_mmu_type)
+		return (void *)(-1);
+
+#ifndef CONFIG_KGSL_PER_PROCESS_PAGE_TABLE
+	name = KGSL_MMU_GLOBAL_PT;
+#endif
+	pt = kgsl_get_pagetable(name);
+
+	if (pt == NULL)
+		pt = kgsl_mmu_createpagetableobject(name);
+
+	return pt;
+}
+
+void kgsl_mmu_putpagetable(struct kgsl_pagetable *pagetable)
+{
+	kgsl_put_pagetable(pagetable);
+}
+EXPORT_SYMBOL(kgsl_mmu_putpagetable);
+
+void kgsl_setstate(struct kgsl_mmu *mmu, uint32_t flags)
+{
+	struct kgsl_device *device = mmu->device;
+	if (KGSL_MMU_TYPE_NONE == kgsl_mmu_type)
+		return;
+	else if (device->ftbl->setstate)
+		device->ftbl->setstate(device, flags);
+	else if (mmu->mmu_ops->mmu_device_setstate)
+		mmu->mmu_ops->mmu_device_setstate(mmu, flags);
+}
+EXPORT_SYMBOL(kgsl_setstate);
+
+void kgsl_mh_start(struct kgsl_device *device)
+{
+	struct kgsl_mh *mh = &device->mh;
+	/* force mmu off to for now*/
+	kgsl_regwrite(device, MH_MMU_CONFIG, 0);
+	kgsl_idle(device,  KGSL_TIMEOUT_DEFAULT);
+
+	/* define physical memory range accessible by the core */
+	kgsl_regwrite(device, MH_MMU_MPU_BASE, mh->mpu_base);
+	kgsl_regwrite(device, MH_MMU_MPU_END,
+			mh->mpu_base + mh->mpu_range);
+	kgsl_regwrite(device, MH_ARBITER_CONFIG, mh->mharb);
+
+	if (mh->mh_intf_cfg1 != 0)
+		kgsl_regwrite(device, MH_CLNT_INTF_CTRL_CONFIG1,
+				mh->mh_intf_cfg1);
+
+	if (mh->mh_intf_cfg2 != 0)
+		kgsl_regwrite(device, MH_CLNT_INTF_CTRL_CONFIG2,
+				mh->mh_intf_cfg2);
+
+	/*
+	 * Interrupts are enabled on a per-device level when
+	 * kgsl_pwrctrl_irq() is called
+	 */
+}
+
+static inline struct gen_pool *
+_get_pool(struct kgsl_pagetable *pagetable, unsigned int flags)
+{
+	if (pagetable->kgsl_pool &&
+		(KGSL_MEMFLAGS_GLOBAL & flags))
+		return pagetable->kgsl_pool;
+	return pagetable->pool;
+}
+
+int
+kgsl_mmu_map(struct kgsl_pagetable *pagetable,
+				struct kgsl_memdesc *memdesc,
+				unsigned int protflags)
+{
+	int ret;
+	struct gen_pool *pool;
+	int size;
+
+	if (kgsl_mmu_type == KGSL_MMU_TYPE_NONE) {
+		if (memdesc->sglen == 1) {
+			memdesc->gpuaddr = sg_dma_address(memdesc->sg);
+			if (!memdesc->gpuaddr)
+				memdesc->gpuaddr = sg_phys(memdesc->sg);
+			if (!memdesc->gpuaddr) {
+				KGSL_CORE_ERR("Unable to get a valid physical "
+					"address for memdesc\n");
+				return -EINVAL;
+			}
+			return 0;
+		} else {
+			KGSL_CORE_ERR("Memory is not contigious "
+					"(sglen = %d)\n", memdesc->sglen);
+			return -EINVAL;
+		}
+	}
+
+	size = kgsl_sg_size(memdesc->sg, memdesc->sglen);
+
+	/* Allocate from kgsl pool if it exists for global mappings */
+	pool = _get_pool(pagetable, memdesc->priv);
+
+	memdesc->gpuaddr = gen_pool_alloc(pool, size);
+	if (memdesc->gpuaddr == 0) {
+		KGSL_CORE_ERR("gen_pool_alloc(%d) failed from pool: %s\n",
+			size,
+			(pool == pagetable->kgsl_pool) ?
+			"kgsl_pool" : "general_pool");
+		KGSL_CORE_ERR(" [%d] allocated=%d, entries=%d\n",
+				pagetable->name, pagetable->stats.mapped,
+				pagetable->stats.entries);
+		return -ENOMEM;
+	}
+
+	if (KGSL_MMU_TYPE_IOMMU != kgsl_mmu_get_mmutype())
+		spin_lock(&pagetable->lock);
+	ret = pagetable->pt_ops->mmu_map(pagetable->priv, memdesc, protflags,
+						&pagetable->tlb_flags);
+	if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_get_mmutype())
+		spin_lock(&pagetable->lock);
+
+	if (ret)
+		goto err_free_gpuaddr;
+
+	/* Keep track of the statistics for the sysfs files */
+
+	KGSL_STATS_ADD(1, pagetable->stats.entries,
+		       pagetable->stats.max_entries);
+
+	KGSL_STATS_ADD(size, pagetable->stats.mapped,
+		       pagetable->stats.max_mapped);
+
+	spin_unlock(&pagetable->lock);
+
+	return 0;
+
+err_free_gpuaddr:
+	spin_unlock(&pagetable->lock);
+	gen_pool_free(pool, memdesc->gpuaddr, size);
+	memdesc->gpuaddr = 0;
+	return ret;
+}
+EXPORT_SYMBOL(kgsl_mmu_map);
+
+int
+kgsl_mmu_unmap(struct kgsl_pagetable *pagetable,
+		struct kgsl_memdesc *memdesc)
+{
+	struct gen_pool *pool;
+	int size;
+
+	if (memdesc->size == 0 || memdesc->gpuaddr == 0)
+		return 0;
+
+	if (kgsl_mmu_type == KGSL_MMU_TYPE_NONE) {
+		memdesc->gpuaddr = 0;
+		return 0;
+	}
+
+	size = kgsl_sg_size(memdesc->sg, memdesc->sglen);
+
+	if (KGSL_MMU_TYPE_IOMMU != kgsl_mmu_get_mmutype())
+		spin_lock(&pagetable->lock);
+	pagetable->pt_ops->mmu_unmap(pagetable->priv, memdesc);
+	if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_get_mmutype())
+		spin_lock(&pagetable->lock);
+	/* Remove the statistics */
+	pagetable->stats.entries--;
+	pagetable->stats.mapped -= size;
+
+	spin_unlock(&pagetable->lock);
+
+	pool = _get_pool(pagetable, memdesc->priv);
+	gen_pool_free(pool, memdesc->gpuaddr, size);
+
+	/*
+	 * Don't clear the gpuaddr on global mappings because they
+	 * may be in use by other pagetables
+	 */
+	if (!(memdesc->priv & KGSL_MEMFLAGS_GLOBAL))
+		memdesc->gpuaddr = 0;
+	return 0;
+}
+EXPORT_SYMBOL(kgsl_mmu_unmap);
+
+int kgsl_mmu_map_global(struct kgsl_pagetable *pagetable,
+			struct kgsl_memdesc *memdesc, unsigned int protflags)
+{
+	int result = -EINVAL;
+	unsigned int gpuaddr = 0;
+
+	if (memdesc == NULL) {
+		KGSL_CORE_ERR("invalid memdesc\n");
+		goto error;
+	}
+	/* Not all global mappings are needed for all MMU types */
+	if (!memdesc->size)
+		return 0;
+
+	gpuaddr = memdesc->gpuaddr;
+	memdesc->priv |= KGSL_MEMFLAGS_GLOBAL;
+
+	result = kgsl_mmu_map(pagetable, memdesc, protflags);
+	if (result)
+		goto error;
+
+	/*global mappings must have the same gpu address in all pagetables*/
+	if (gpuaddr && gpuaddr != memdesc->gpuaddr) {
+		KGSL_CORE_ERR("pt %p addr mismatch phys 0x%08x"
+			"gpu 0x%0x 0x%08x", pagetable, memdesc->physaddr,
+			gpuaddr, memdesc->gpuaddr);
+		goto error_unmap;
+	}
+	return result;
+error_unmap:
+	kgsl_mmu_unmap(pagetable, memdesc);
+error:
+	return result;
+}
+EXPORT_SYMBOL(kgsl_mmu_map_global);
+
+int kgsl_mmu_close(struct kgsl_device *device)
+{
+	struct kgsl_mmu *mmu = &device->mmu;
+
+	kgsl_sharedmem_free(&mmu->setstate_memory);
+	if (kgsl_mmu_type == KGSL_MMU_TYPE_NONE)
+		return 0;
+	else
+		return mmu->mmu_ops->mmu_close(mmu);
+}
+EXPORT_SYMBOL(kgsl_mmu_close);
+
+int kgsl_mmu_pt_get_flags(struct kgsl_pagetable *pt,
+			enum kgsl_deviceid id)
+{
+	unsigned int result = 0;
+
+	if (pt == NULL)
+		return 0;
+
+	spin_lock(&pt->lock);
+	if (pt->tlb_flags && (1<<id)) {
+		result = KGSL_MMUFLAGS_TLBFLUSH;
+		pt->tlb_flags &= ~(1<<id);
+	}
+	spin_unlock(&pt->lock);
+	return result;
+}
+EXPORT_SYMBOL(kgsl_mmu_pt_get_flags);
+
+void kgsl_mmu_ptpool_destroy(void *ptpool)
+{
+	if (KGSL_MMU_TYPE_GPU == kgsl_mmu_type)
+		kgsl_gpummu_ptpool_destroy(ptpool);
+	ptpool = 0;
+}
+EXPORT_SYMBOL(kgsl_mmu_ptpool_destroy);
+
+void *kgsl_mmu_ptpool_init(int entries)
+{
+	if (KGSL_MMU_TYPE_GPU == kgsl_mmu_type)
+		return kgsl_gpummu_ptpool_init(entries);
+	else
+		return (void *)(-1);
+}
+EXPORT_SYMBOL(kgsl_mmu_ptpool_init);
+
+int kgsl_mmu_enabled(void)
+{
+	if (KGSL_MMU_TYPE_NONE != kgsl_mmu_type)
+		return 1;
+	else
+		return 0;
+}
+EXPORT_SYMBOL(kgsl_mmu_enabled);
+
+enum kgsl_mmutype kgsl_mmu_get_mmutype(void)
+{
+	return kgsl_mmu_type;
+}
+EXPORT_SYMBOL(kgsl_mmu_get_mmutype);
+
+void kgsl_mmu_set_mmutype(char *mmutype)
+{
+	/* Set the default MMU - GPU on <=8960 and nothing on >= 8064 */
+	kgsl_mmu_type =
+		cpu_is_apq8064() ? KGSL_MMU_TYPE_NONE : KGSL_MMU_TYPE_GPU;
+
+	/* Use the IOMMU if it is found */
+	if (iommu_present(&platform_bus_type))
+		kgsl_mmu_type = KGSL_MMU_TYPE_IOMMU;
+
+	if (mmutype && !strncmp(mmutype, "gpummu", 6))
+		kgsl_mmu_type = KGSL_MMU_TYPE_GPU;
+	if (iommu_present(&platform_bus_type) && mmutype &&
+	    !strncmp(mmutype, "iommu", 5))
+		kgsl_mmu_type = KGSL_MMU_TYPE_IOMMU;
+	if (mmutype && !strncmp(mmutype, "nommu", 5))
+		kgsl_mmu_type = KGSL_MMU_TYPE_NONE;
+}
+EXPORT_SYMBOL(kgsl_mmu_set_mmutype);
diff --git a/drivers/gpu/msm/kgsl_mmu.h b/drivers/gpu/msm/kgsl_mmu.h
new file mode 100644
index 0000000..2db327b
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_mmu.h
@@ -0,0 +1,297 @@
+/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __KGSL_MMU_H
+#define __KGSL_MMU_H
+
+/*
+ * These defines control the split between ttbr1 and ttbr0 pagetables of IOMMU
+ * and what ranges of memory we map to them
+ */
+#define KGSL_IOMMU_GLOBAL_MEM_BASE	0xC0000000
+#define KGSL_IOMMU_GLOBAL_MEM_SIZE	SZ_4M
+#define KGSL_IOMMU_TTBR1_SPLIT		2
+
+#define KGSL_MMU_ALIGN_SHIFT    13
+#define KGSL_MMU_ALIGN_MASK     (~((1 << KGSL_MMU_ALIGN_SHIFT) - 1))
+
+/* Identifier for the global page table */
+/* Per process page tables will probably pass in the thread group
+   as an identifier */
+
+#define KGSL_MMU_GLOBAL_PT 0
+
+struct kgsl_device;
+
+#define GSL_PT_SUPER_PTE 8
+#define GSL_PT_PAGE_WV		0x00000001
+#define GSL_PT_PAGE_RV		0x00000002
+#define GSL_PT_PAGE_DIRTY	0x00000004
+
+/* MMU registers - the register locations for all cores are the
+   same.  The method for getting to those locations differs between
+   2D and 3D, but the 2D and 3D register functions do that magic
+   for us */
+
+#define MH_MMU_CONFIG                0x0040
+#define MH_MMU_VA_RANGE              0x0041
+#define MH_MMU_PT_BASE               0x0042
+#define MH_MMU_PAGE_FAULT            0x0043
+#define MH_MMU_TRAN_ERROR            0x0044
+#define MH_MMU_INVALIDATE            0x0045
+#define MH_MMU_MPU_BASE              0x0046
+#define MH_MMU_MPU_END               0x0047
+
+#define MH_INTERRUPT_MASK            0x0A42
+#define MH_INTERRUPT_STATUS          0x0A43
+#define MH_INTERRUPT_CLEAR           0x0A44
+#define MH_AXI_ERROR                 0x0A45
+#define MH_ARBITER_CONFIG            0x0A40
+#define MH_DEBUG_CTRL                0x0A4E
+#define MH_DEBUG_DATA                0x0A4F
+#define MH_AXI_HALT_CONTROL          0x0A50
+#define MH_CLNT_INTF_CTRL_CONFIG1    0x0A54
+#define MH_CLNT_INTF_CTRL_CONFIG2    0x0A55
+
+/* MH_MMU_CONFIG bit definitions */
+
+#define MH_MMU_CONFIG__RB_W_CLNT_BEHAVIOR__SHIFT           0x00000004
+#define MH_MMU_CONFIG__CP_W_CLNT_BEHAVIOR__SHIFT           0x00000006
+#define MH_MMU_CONFIG__CP_R0_CLNT_BEHAVIOR__SHIFT          0x00000008
+#define MH_MMU_CONFIG__CP_R1_CLNT_BEHAVIOR__SHIFT          0x0000000a
+#define MH_MMU_CONFIG__CP_R2_CLNT_BEHAVIOR__SHIFT          0x0000000c
+#define MH_MMU_CONFIG__CP_R3_CLNT_BEHAVIOR__SHIFT          0x0000000e
+#define MH_MMU_CONFIG__CP_R4_CLNT_BEHAVIOR__SHIFT          0x00000010
+#define MH_MMU_CONFIG__VGT_R0_CLNT_BEHAVIOR__SHIFT         0x00000012
+#define MH_MMU_CONFIG__VGT_R1_CLNT_BEHAVIOR__SHIFT         0x00000014
+#define MH_MMU_CONFIG__TC_R_CLNT_BEHAVIOR__SHIFT           0x00000016
+#define MH_MMU_CONFIG__PA_W_CLNT_BEHAVIOR__SHIFT           0x00000018
+
+/* MMU Flags */
+#define KGSL_MMUFLAGS_TLBFLUSH         0x10000000
+#define KGSL_MMUFLAGS_PTUPDATE         0x20000000
+
+#define MH_INTERRUPT_MASK__AXI_READ_ERROR                  0x00000001L
+#define MH_INTERRUPT_MASK__AXI_WRITE_ERROR                 0x00000002L
+#define MH_INTERRUPT_MASK__MMU_PAGE_FAULT                  0x00000004L
+
+#define KGSL_MMU_INT_MASK \
+	(MH_INTERRUPT_MASK__AXI_READ_ERROR | \
+	 MH_INTERRUPT_MASK__AXI_WRITE_ERROR | \
+	 MH_INTERRUPT_MASK__MMU_PAGE_FAULT)
+
+enum kgsl_mmutype {
+	KGSL_MMU_TYPE_GPU = 0,
+	KGSL_MMU_TYPE_IOMMU,
+	KGSL_MMU_TYPE_NONE
+};
+
+struct kgsl_pagetable {
+	spinlock_t lock;
+	struct kref refcount;
+	unsigned int   max_entries;
+	struct gen_pool *pool;
+	struct gen_pool *kgsl_pool;
+	struct list_head list;
+	unsigned int name;
+	struct kobject *kobj;
+
+	struct {
+		unsigned int entries;
+		unsigned int mapped;
+		unsigned int max_mapped;
+		unsigned int max_entries;
+	} stats;
+	const struct kgsl_mmu_pt_ops *pt_ops;
+	unsigned int tlb_flags;
+	void *priv;
+};
+
+struct kgsl_mmu;
+
+struct kgsl_mmu_ops {
+	int (*mmu_init) (struct kgsl_mmu *mmu);
+	int (*mmu_close) (struct kgsl_mmu *mmu);
+	int (*mmu_start) (struct kgsl_mmu *mmu);
+	void (*mmu_stop) (struct kgsl_mmu *mmu);
+	void (*mmu_setstate) (struct kgsl_mmu *mmu,
+		struct kgsl_pagetable *pagetable);
+	void (*mmu_device_setstate) (struct kgsl_mmu *mmu,
+					uint32_t flags);
+	void (*mmu_pagefault) (struct kgsl_mmu *mmu);
+	unsigned int (*mmu_get_current_ptbase)
+			(struct kgsl_mmu *mmu);
+	void (*mmu_disable_clk)
+		(struct kgsl_mmu *mmu);
+	int (*mmu_enable_clk)
+		(struct kgsl_mmu *mmu, int ctx_id);
+	int (*mmu_get_hwpagetable_asid)(struct kgsl_mmu *mmu);
+	int (*mmu_get_pt_lsb)(struct kgsl_mmu *mmu,
+				unsigned int unit_id,
+				enum kgsl_iommu_context_id ctx_id);
+	int (*mmu_get_reg_map_desc)(struct kgsl_mmu *mmu,
+				void **reg_map_desc);
+};
+
+struct kgsl_mmu_pt_ops {
+	int (*mmu_map) (void *mmu_pt,
+			struct kgsl_memdesc *memdesc,
+			unsigned int protflags,
+			unsigned int *tlb_flags);
+	int (*mmu_unmap) (void *mmu_pt,
+			struct kgsl_memdesc *memdesc);
+	void *(*mmu_create_pagetable) (void);
+	void (*mmu_destroy_pagetable) (void *pt);
+	int (*mmu_pt_equal) (struct kgsl_pagetable *pt,
+			unsigned int pt_base);
+	unsigned int (*mmu_pt_get_base_addr)
+			(struct kgsl_pagetable *pt);
+};
+
+struct kgsl_mmu {
+	unsigned int     refcnt;
+	uint32_t      flags;
+	struct kgsl_device     *device;
+	unsigned int     config;
+	struct kgsl_memdesc    setstate_memory;
+	/* current page table object being used by device mmu */
+	struct kgsl_pagetable  *defaultpagetable;
+	struct kgsl_pagetable  *hwpagetable;
+	const struct kgsl_mmu_ops *mmu_ops;
+	void *priv;
+};
+
+#include "kgsl_gpummu.h"
+
+extern struct kgsl_mmu_ops iommu_ops;
+extern struct kgsl_mmu_pt_ops iommu_pt_ops;
+
+struct kgsl_pagetable *kgsl_mmu_getpagetable(unsigned long name);
+void kgsl_mmu_putpagetable(struct kgsl_pagetable *pagetable);
+void kgsl_mh_start(struct kgsl_device *device);
+void kgsl_mh_intrcallback(struct kgsl_device *device);
+int kgsl_mmu_init(struct kgsl_device *device);
+int kgsl_mmu_start(struct kgsl_device *device);
+int kgsl_mmu_close(struct kgsl_device *device);
+int kgsl_mmu_map(struct kgsl_pagetable *pagetable,
+		 struct kgsl_memdesc *memdesc,
+		 unsigned int protflags);
+int kgsl_mmu_map_global(struct kgsl_pagetable *pagetable,
+			struct kgsl_memdesc *memdesc, unsigned int protflags);
+int kgsl_mmu_unmap(struct kgsl_pagetable *pagetable,
+		    struct kgsl_memdesc *memdesc);
+unsigned int kgsl_virtaddr_to_physaddr(void *virtaddr);
+void kgsl_setstate(struct kgsl_mmu *mmu, uint32_t flags);
+int kgsl_mmu_get_ptname_from_ptbase(unsigned int pt_base);
+int kgsl_mmu_pt_get_flags(struct kgsl_pagetable *pt,
+			enum kgsl_deviceid id);
+void kgsl_mmu_ptpool_destroy(void *ptpool);
+void *kgsl_mmu_ptpool_init(int entries);
+int kgsl_mmu_enabled(void);
+void kgsl_mmu_set_mmutype(char *mmutype);
+enum kgsl_mmutype kgsl_mmu_get_mmutype(void);
+unsigned int kgsl_mmu_get_ptsize(void);
+
+/*
+ * Static inline functions of MMU that simply call the SMMU specific
+ * function using a function pointer. These functions can be thought
+ * of as wrappers around the actual function
+ */
+
+static inline unsigned int kgsl_mmu_get_current_ptbase(struct kgsl_mmu *mmu)
+{
+	if (mmu->mmu_ops && mmu->mmu_ops->mmu_get_current_ptbase)
+		return mmu->mmu_ops->mmu_get_current_ptbase(mmu);
+	else
+		return 0;
+}
+
+static inline void kgsl_mmu_setstate(struct kgsl_mmu *mmu,
+			struct kgsl_pagetable *pagetable)
+{
+	if (mmu->mmu_ops && mmu->mmu_ops->mmu_setstate)
+		mmu->mmu_ops->mmu_setstate(mmu, pagetable);
+}
+
+static inline void kgsl_mmu_device_setstate(struct kgsl_mmu *mmu,
+						uint32_t flags)
+{
+	if (mmu->mmu_ops && mmu->mmu_ops->mmu_device_setstate)
+		mmu->mmu_ops->mmu_device_setstate(mmu, flags);
+}
+
+static inline void kgsl_mmu_stop(struct kgsl_mmu *mmu)
+{
+	if (mmu->mmu_ops && mmu->mmu_ops->mmu_stop)
+		mmu->mmu_ops->mmu_stop(mmu);
+}
+
+static inline int kgsl_mmu_pt_equal(struct kgsl_pagetable *pt,
+			unsigned int pt_base)
+{
+	if (KGSL_MMU_TYPE_NONE == kgsl_mmu_get_mmutype())
+		return 1;
+	else
+		return pt->pt_ops->mmu_pt_equal(pt, pt_base);
+}
+
+static inline unsigned int kgsl_mmu_pt_get_base_addr(struct kgsl_pagetable *pt)
+{
+	if (KGSL_MMU_TYPE_NONE == kgsl_mmu_get_mmutype())
+		return 0;
+	else
+		return pt->pt_ops->mmu_pt_get_base_addr(pt);
+}
+
+static inline int kgsl_mmu_get_reg_map_desc(struct kgsl_mmu *mmu,
+						void **reg_map_desc)
+{
+	if (mmu->mmu_ops && mmu->mmu_ops->mmu_get_reg_map_desc)
+		return mmu->mmu_ops->mmu_get_reg_map_desc(mmu, reg_map_desc);
+	else
+		return 0;
+}
+
+static inline int kgsl_mmu_get_pt_lsb(struct kgsl_mmu *mmu,
+					unsigned int unit_id,
+					enum kgsl_iommu_context_id ctx_id)
+{
+	if (mmu->mmu_ops && mmu->mmu_ops->mmu_get_pt_lsb)
+		return mmu->mmu_ops->mmu_get_pt_lsb(mmu, unit_id, ctx_id);
+	else
+		return 0;
+}
+
+static inline int kgsl_mmu_get_hwpagetable_asid(struct kgsl_mmu *mmu)
+{
+	if (mmu->mmu_ops && mmu->mmu_ops->mmu_get_hwpagetable_asid)
+		return mmu->mmu_ops->mmu_get_hwpagetable_asid(mmu);
+	else
+		return 0;
+}
+
+static inline int kgsl_mmu_enable_clk(struct kgsl_mmu *mmu,
+					int ctx_id)
+{
+	if (mmu->mmu_ops && mmu->mmu_ops->mmu_enable_clk)
+		return mmu->mmu_ops->mmu_enable_clk(mmu, ctx_id);
+	else
+		return 0;
+}
+
+static inline void kgsl_mmu_disable_clk(struct kgsl_mmu *mmu)
+{
+	if (mmu->mmu_ops && mmu->mmu_ops->mmu_disable_clk)
+		mmu->mmu_ops->mmu_disable_clk(mmu);
+}
+
+#endif /* __KGSL_MMU_H */
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
new file mode 100644
index 0000000..8701b67
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -0,0 +1,948 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/export.h>
+#include <linux/interrupt.h>
+#include <asm/page.h>
+#include <linux/pm_runtime.h>
+#include <mach/msm_iomap.h>
+#include <mach/msm_bus.h>
+
+#include "kgsl.h"
+#include "kgsl_pwrscale.h"
+#include "kgsl_device.h"
+#include "kgsl_trace.h"
+
+#define KGSL_PWRFLAGS_POWER_ON 0
+#define KGSL_PWRFLAGS_CLK_ON   1
+#define KGSL_PWRFLAGS_AXI_ON   2
+#define KGSL_PWRFLAGS_IRQ_ON   3
+
+#define GPU_SWFI_LATENCY	3
+#define UPDATE_BUSY_VAL		1000000
+#define UPDATE_BUSY		50
+
+struct clk_pair {
+	const char *name;
+	uint map;
+};
+
+struct clk_pair clks[KGSL_MAX_CLKS] = {
+	{
+		.name = "src_clk",
+		.map = KGSL_CLK_SRC,
+	},
+	{
+		.name = "core_clk",
+		.map = KGSL_CLK_CORE,
+	},
+	{
+		.name = "iface_clk",
+		.map = KGSL_CLK_IFACE,
+	},
+	{
+		.name = "mem_clk",
+		.map = KGSL_CLK_MEM,
+	},
+	{
+		.name = "mem_iface_clk",
+		.map = KGSL_CLK_MEM_IFACE,
+	},
+};
+
+void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device,
+				unsigned int new_level)
+{
+	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+	if (new_level < (pwr->num_pwrlevels - 1) &&
+		new_level >= pwr->thermal_pwrlevel &&
+		new_level != pwr->active_pwrlevel) {
+		struct kgsl_pwrlevel *pwrlevel = &pwr->pwrlevels[new_level];
+		int diff = new_level - pwr->active_pwrlevel;
+		int d = (diff > 0) ? 1 : -1;
+		int level = pwr->active_pwrlevel;
+		pwr->active_pwrlevel = new_level;
+		if ((test_bit(KGSL_PWRFLAGS_CLK_ON, &pwr->power_flags)) ||
+			(device->state == KGSL_STATE_NAP)) {
+			/*
+			 * On some platforms, instability is caused on
+			 * changing clock freq when the core is busy.
+			 * Idle the gpu core before changing the clock freq.
+			 */
+			if (pwr->idle_needed == true)
+				device->ftbl->idle(device,
+						KGSL_TIMEOUT_DEFAULT);
+			/* Don't shift by more than one level at a time to
+			 * avoid glitches.
+			 */
+			while (level != new_level) {
+				level += d;
+				clk_set_rate(pwr->grp_clks[0],
+						pwr->pwrlevels[level].gpu_freq);
+			}
+		}
+		if (test_bit(KGSL_PWRFLAGS_AXI_ON, &pwr->power_flags)) {
+			if (pwr->pcl)
+				msm_bus_scale_client_update_request(pwr->pcl,
+					pwrlevel->bus_freq);
+			else if (pwr->ebi1_clk)
+				clk_set_rate(pwr->ebi1_clk, pwrlevel->bus_freq);
+		}
+		trace_kgsl_pwrlevel(device, pwr->active_pwrlevel,
+				    pwrlevel->gpu_freq);
+	}
+}
+EXPORT_SYMBOL(kgsl_pwrctrl_pwrlevel_change);
+
+static int __gpuclk_store(int max, struct device *dev,
+						  struct device_attribute *attr,
+						  const char *buf, size_t count)
+{	int ret, i, delta = 5000000;
+	unsigned long val;
+	struct kgsl_device *device = kgsl_device_from_dev(dev);
+	struct kgsl_pwrctrl *pwr;
+
+	if (device == NULL)
+		return 0;
+	pwr = &device->pwrctrl;
+
+	ret = sscanf(buf, "%ld", &val);
+	if (ret != 1)
+		return count;
+
+	mutex_lock(&device->mutex);
+	for (i = 0; i < pwr->num_pwrlevels; i++) {
+		if (abs(pwr->pwrlevels[i].gpu_freq - val) < delta) {
+			if (max)
+				pwr->thermal_pwrlevel = i;
+			break;
+		}
+	}
+
+	if (i == pwr->num_pwrlevels)
+		goto done;
+
+	/*
+	 * If the current or requested clock speed is greater than the
+	 * thermal limit, bump down immediately.
+	 */
+
+	if (pwr->pwrlevels[pwr->active_pwrlevel].gpu_freq >
+	    pwr->pwrlevels[pwr->thermal_pwrlevel].gpu_freq)
+		kgsl_pwrctrl_pwrlevel_change(device, pwr->thermal_pwrlevel);
+	else if (!max)
+		kgsl_pwrctrl_pwrlevel_change(device, i);
+
+done:
+	mutex_unlock(&device->mutex);
+	return count;
+}
+
+static int kgsl_pwrctrl_max_gpuclk_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+	return __gpuclk_store(1, dev, attr, buf, count);
+}
+
+static int kgsl_pwrctrl_max_gpuclk_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct kgsl_device *device = kgsl_device_from_dev(dev);
+	struct kgsl_pwrctrl *pwr;
+	if (device == NULL)
+		return 0;
+	pwr = &device->pwrctrl;
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			pwr->pwrlevels[pwr->thermal_pwrlevel].gpu_freq);
+}
+
+static int kgsl_pwrctrl_gpuclk_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	return __gpuclk_store(0, dev, attr, buf, count);
+}
+
+static int kgsl_pwrctrl_gpuclk_show(struct device *dev,
+				    struct device_attribute *attr,
+				    char *buf)
+{
+	struct kgsl_device *device = kgsl_device_from_dev(dev);
+	struct kgsl_pwrctrl *pwr;
+	if (device == NULL)
+		return 0;
+	pwr = &device->pwrctrl;
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			pwr->pwrlevels[pwr->active_pwrlevel].gpu_freq);
+}
+
+static int kgsl_pwrctrl_pwrnap_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	char temp[20];
+	unsigned long val;
+	struct kgsl_device *device = kgsl_device_from_dev(dev);
+	struct kgsl_pwrctrl *pwr;
+	int rc;
+
+	if (device == NULL)
+		return 0;
+	pwr = &device->pwrctrl;
+
+	snprintf(temp, sizeof(temp), "%.*s",
+			 (int)min(count, sizeof(temp) - 1), buf);
+	rc = strict_strtoul(temp, 0, &val);
+	if (rc)
+		return rc;
+
+	mutex_lock(&device->mutex);
+
+	if (val == 1)
+		pwr->nap_allowed = true;
+	else if (val == 0)
+		pwr->nap_allowed = false;
+
+	mutex_unlock(&device->mutex);
+
+	return count;
+}
+
+static int kgsl_pwrctrl_pwrnap_show(struct device *dev,
+				    struct device_attribute *attr,
+				    char *buf)
+{
+	struct kgsl_device *device = kgsl_device_from_dev(dev);
+	if (device == NULL)
+		return 0;
+	return snprintf(buf, PAGE_SIZE, "%d\n", device->pwrctrl.nap_allowed);
+}
+
+
+static int kgsl_pwrctrl_idle_timer_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	char temp[20];
+	unsigned long val;
+	struct kgsl_device *device = kgsl_device_from_dev(dev);
+	struct kgsl_pwrctrl *pwr;
+	const long div = 1000/HZ;
+	static unsigned int org_interval_timeout = 1;
+	int rc;
+
+	if (device == NULL)
+		return 0;
+	pwr = &device->pwrctrl;
+
+	snprintf(temp, sizeof(temp), "%.*s",
+			 (int)min(count, sizeof(temp) - 1), buf);
+	rc = strict_strtoul(temp, 0, &val);
+	if (rc)
+		return rc;
+
+	if (org_interval_timeout == 1)
+		org_interval_timeout = pwr->interval_timeout;
+
+	mutex_lock(&device->mutex);
+
+	/* Let the timeout be requested in ms, but convert to jiffies. */
+	val /= div;
+	if (val >= org_interval_timeout)
+		pwr->interval_timeout = val;
+
+	mutex_unlock(&device->mutex);
+
+	return count;
+}
+
+static int kgsl_pwrctrl_idle_timer_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct kgsl_device *device = kgsl_device_from_dev(dev);
+	if (device == NULL)
+		return 0;
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+		device->pwrctrl.interval_timeout);
+}
+
+static int kgsl_pwrctrl_gpubusy_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	int ret;
+	struct kgsl_device *device = kgsl_device_from_dev(dev);
+	struct kgsl_busy *b = &device->pwrctrl.busy;
+	ret = snprintf(buf, 17, "%7d %7d\n",
+				   b->on_time_old, b->time_old);
+	if (!test_bit(KGSL_PWRFLAGS_AXI_ON, &device->pwrctrl.power_flags)) {
+		b->on_time_old = 0;
+		b->time_old = 0;
+	}
+	return ret;
+}
+
+DEVICE_ATTR(gpuclk, 0644, kgsl_pwrctrl_gpuclk_show, kgsl_pwrctrl_gpuclk_store);
+DEVICE_ATTR(max_gpuclk, 0644, kgsl_pwrctrl_max_gpuclk_show,
+	kgsl_pwrctrl_max_gpuclk_store);
+DEVICE_ATTR(pwrnap, 0664, kgsl_pwrctrl_pwrnap_show, kgsl_pwrctrl_pwrnap_store);
+DEVICE_ATTR(idle_timer, 0644, kgsl_pwrctrl_idle_timer_show,
+	kgsl_pwrctrl_idle_timer_store);
+DEVICE_ATTR(gpubusy, 0644, kgsl_pwrctrl_gpubusy_show,
+	NULL);
+
+static const struct device_attribute *pwrctrl_attr_list[] = {
+	&dev_attr_gpuclk,
+	&dev_attr_max_gpuclk,
+	&dev_attr_pwrnap,
+	&dev_attr_idle_timer,
+	&dev_attr_gpubusy,
+	NULL
+};
+
+int kgsl_pwrctrl_init_sysfs(struct kgsl_device *device)
+{
+	return kgsl_create_device_sysfs_files(device->dev, pwrctrl_attr_list);
+}
+
+void kgsl_pwrctrl_uninit_sysfs(struct kgsl_device *device)
+{
+	kgsl_remove_device_sysfs_files(device->dev, pwrctrl_attr_list);
+}
+
+/* Track the amount of time the gpu is on vs the total system time. *
+ * Regularly update the percentage of busy time displayed by sysfs. */
+static void kgsl_pwrctrl_busy_time(struct kgsl_device *device, bool on_time)
+{
+	struct kgsl_busy *b = &device->pwrctrl.busy;
+	int elapsed;
+	if (b->start.tv_sec == 0)
+		do_gettimeofday(&(b->start));
+	do_gettimeofday(&(b->stop));
+	elapsed = (b->stop.tv_sec - b->start.tv_sec) * 1000000;
+	elapsed += b->stop.tv_usec - b->start.tv_usec;
+	b->time += elapsed;
+	if (on_time)
+		b->on_time += elapsed;
+	/* Update the output regularly and reset the counters. */
+	if ((b->time > UPDATE_BUSY_VAL) ||
+		!test_bit(KGSL_PWRFLAGS_AXI_ON, &device->pwrctrl.power_flags)) {
+		b->on_time_old = b->on_time;
+		b->time_old = b->time;
+		b->on_time = 0;
+		b->time = 0;
+	}
+	do_gettimeofday(&(b->start));
+}
+
+void kgsl_pwrctrl_clk(struct kgsl_device *device, int state,
+					  int requested_state)
+{
+	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+	int i = 0;
+	if (state == KGSL_PWRFLAGS_OFF) {
+		if (test_and_clear_bit(KGSL_PWRFLAGS_CLK_ON,
+			&pwr->power_flags)) {
+			trace_kgsl_clk(device, state);
+			for (i = KGSL_MAX_CLKS - 1; i > 0; i--)
+				if (pwr->grp_clks[i])
+					clk_disable(pwr->grp_clks[i]);
+			/* High latency clock maintenance. */
+			if ((pwr->pwrlevels[0].gpu_freq > 0) &&
+				(requested_state != KGSL_STATE_NAP)) {
+				clk_set_rate(pwr->grp_clks[0],
+					pwr->pwrlevels[pwr->num_pwrlevels - 1].
+					gpu_freq);
+				for (i = KGSL_MAX_CLKS - 1; i > 0; i--)
+					if (pwr->grp_clks[i])
+						clk_unprepare(pwr->grp_clks[i]);
+			}
+			kgsl_pwrctrl_busy_time(device, true);
+		}
+	} else if (state == KGSL_PWRFLAGS_ON) {
+		if (!test_and_set_bit(KGSL_PWRFLAGS_CLK_ON,
+			&pwr->power_flags)) {
+			trace_kgsl_clk(device, state);
+			/* High latency clock maintenance. */
+			if ((pwr->pwrlevels[0].gpu_freq > 0) &&
+				(device->state != KGSL_STATE_NAP)) {
+				for (i = KGSL_MAX_CLKS - 1; i > 0; i--)
+					if (pwr->grp_clks[i])
+						clk_prepare(pwr->grp_clks[i]);
+				clk_set_rate(pwr->grp_clks[0],
+					pwr->pwrlevels[pwr->active_pwrlevel].
+						gpu_freq);
+			}
+
+			/* as last step, enable grp_clk
+			   this is to let GPU interrupt to come */
+			for (i = KGSL_MAX_CLKS - 1; i > 0; i--)
+				if (pwr->grp_clks[i])
+					clk_enable(pwr->grp_clks[i]);
+			kgsl_pwrctrl_busy_time(device, false);
+		}
+	}
+}
+
+void kgsl_pwrctrl_axi(struct kgsl_device *device, int state)
+{
+	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+
+	if (state == KGSL_PWRFLAGS_OFF) {
+		if (test_and_clear_bit(KGSL_PWRFLAGS_AXI_ON,
+			&pwr->power_flags)) {
+			trace_kgsl_bus(device, state);
+			if (pwr->ebi1_clk) {
+				clk_set_rate(pwr->ebi1_clk, 0);
+				clk_disable_unprepare(pwr->ebi1_clk);
+			}
+			if (pwr->pcl)
+				msm_bus_scale_client_update_request(pwr->pcl,
+								    0);
+		}
+	} else if (state == KGSL_PWRFLAGS_ON) {
+		if (!test_and_set_bit(KGSL_PWRFLAGS_AXI_ON,
+			&pwr->power_flags)) {
+			trace_kgsl_bus(device, state);
+			if (pwr->ebi1_clk) {
+				clk_prepare_enable(pwr->ebi1_clk);
+				clk_set_rate(pwr->ebi1_clk,
+					pwr->pwrlevels[pwr->active_pwrlevel].
+					bus_freq);
+			}
+			if (pwr->pcl)
+				msm_bus_scale_client_update_request(pwr->pcl,
+					pwr->pwrlevels[pwr->active_pwrlevel].
+						bus_freq);
+		}
+	}
+}
+
+void kgsl_pwrctrl_pwrrail(struct kgsl_device *device, int state)
+{
+	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+
+	if (state == KGSL_PWRFLAGS_OFF) {
+		if (test_and_clear_bit(KGSL_PWRFLAGS_POWER_ON,
+			&pwr->power_flags)) {
+			trace_kgsl_rail(device, state);
+			if (pwr->gpu_reg)
+				regulator_disable(pwr->gpu_reg);
+		}
+	} else if (state == KGSL_PWRFLAGS_ON) {
+		if (!test_and_set_bit(KGSL_PWRFLAGS_POWER_ON,
+			&pwr->power_flags)) {
+			trace_kgsl_rail(device, state);
+			if (pwr->gpu_reg) {
+				int status = regulator_enable(pwr->gpu_reg);
+				if (status)
+					KGSL_DRV_ERR(device, "regulator_enable "
+							"failed: %d\n", status);
+			}
+		}
+	}
+}
+
+void kgsl_pwrctrl_irq(struct kgsl_device *device, int state)
+{
+	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+
+	if (state == KGSL_PWRFLAGS_ON) {
+		if (!test_and_set_bit(KGSL_PWRFLAGS_IRQ_ON,
+			&pwr->power_flags)) {
+			trace_kgsl_irq(device, state);
+			enable_irq(pwr->interrupt_num);
+		}
+	} else if (state == KGSL_PWRFLAGS_OFF) {
+		if (test_and_clear_bit(KGSL_PWRFLAGS_IRQ_ON,
+			&pwr->power_flags)) {
+			trace_kgsl_irq(device, state);
+			if (in_interrupt())
+				disable_irq_nosync(pwr->interrupt_num);
+			else
+				disable_irq(pwr->interrupt_num);
+		}
+	}
+}
+EXPORT_SYMBOL(kgsl_pwrctrl_irq);
+
+int kgsl_pwrctrl_init(struct kgsl_device *device)
+{
+	int i, result = 0;
+	struct clk *clk;
+	struct platform_device *pdev =
+		container_of(device->parentdev, struct platform_device, dev);
+	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+	struct kgsl_device_platform_data *pdata = pdev->dev.platform_data;
+
+	/*acquire clocks */
+	for (i = 0; i < KGSL_MAX_CLKS; i++) {
+		if (pdata->clk_map & clks[i].map) {
+			clk = clk_get(&pdev->dev, clks[i].name);
+			if (IS_ERR(clk))
+				goto clk_err;
+			pwr->grp_clks[i] = clk;
+		}
+	}
+	/* Make sure we have a source clk for freq setting */
+	if (pwr->grp_clks[0] == NULL)
+		pwr->grp_clks[0] = pwr->grp_clks[1];
+
+	/* put the AXI bus into asynchronous mode with the graphics cores */
+	if (pdata->set_grp_async != NULL)
+		pdata->set_grp_async();
+
+	if (pdata->num_levels > KGSL_MAX_PWRLEVELS) {
+		KGSL_PWR_ERR(device, "invalid power level count: %d\n",
+					 pdata->num_levels);
+		result = -EINVAL;
+		goto done;
+	}
+	pwr->num_pwrlevels = pdata->num_levels;
+	pwr->active_pwrlevel = pdata->init_level;
+	pwr->default_pwrlevel = pdata->init_level;
+	for (i = 0; i < pdata->num_levels; i++) {
+		pwr->pwrlevels[i].gpu_freq =
+		(pdata->pwrlevel[i].gpu_freq > 0) ?
+		clk_round_rate(pwr->grp_clks[0],
+					   pdata->pwrlevel[i].
+					   gpu_freq) : 0;
+		pwr->pwrlevels[i].bus_freq =
+			pdata->pwrlevel[i].bus_freq;
+		pwr->pwrlevels[i].io_fraction =
+			pdata->pwrlevel[i].io_fraction;
+	}
+	/* Do not set_rate for targets in sync with AXI */
+	if (pwr->pwrlevels[0].gpu_freq > 0)
+		clk_set_rate(pwr->grp_clks[0], pwr->
+				pwrlevels[pwr->num_pwrlevels - 1].gpu_freq);
+
+	pwr->gpu_reg = regulator_get(&pdev->dev, "vdd");
+	if (IS_ERR(pwr->gpu_reg))
+		pwr->gpu_reg = NULL;
+
+	pwr->power_flags = 0;
+
+	pwr->nap_allowed = pdata->nap_allowed;
+	pwr->idle_needed = pdata->idle_needed;
+	pwr->interval_timeout = pdata->idle_timeout;
+	pwr->strtstp_sleepwake = pdata->strtstp_sleepwake;
+	pwr->ebi1_clk = clk_get(&pdev->dev, "bus_clk");
+	if (IS_ERR(pwr->ebi1_clk))
+		pwr->ebi1_clk = NULL;
+	else
+		clk_set_rate(pwr->ebi1_clk,
+					 pwr->pwrlevels[pwr->active_pwrlevel].
+						bus_freq);
+	if (pdata->bus_scale_table != NULL) {
+		pwr->pcl = msm_bus_scale_register_client(pdata->
+							bus_scale_table);
+		if (!pwr->pcl) {
+			KGSL_PWR_ERR(device,
+					"msm_bus_scale_register_client failed: "
+					"id %d table %p", device->id,
+					pdata->bus_scale_table);
+			result = -EINVAL;
+			goto done;
+		}
+	}
+
+
+	pm_runtime_enable(device->parentdev);
+	register_early_suspend(&device->display_off);
+	return result;
+
+clk_err:
+	result = PTR_ERR(clk);
+	KGSL_PWR_ERR(device, "clk_get(%s) failed: %d\n",
+				 clks[i].name, result);
+
+done:
+	return result;
+}
+
+void kgsl_pwrctrl_close(struct kgsl_device *device)
+{
+	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+	int i;
+
+	KGSL_PWR_INFO(device, "close device %d\n", device->id);
+
+	pm_runtime_disable(device->parentdev);
+	unregister_early_suspend(&device->display_off);
+
+	clk_put(pwr->ebi1_clk);
+
+	if (pwr->pcl)
+		msm_bus_scale_unregister_client(pwr->pcl);
+
+	pwr->pcl = 0;
+
+	if (pwr->gpu_reg) {
+		regulator_put(pwr->gpu_reg);
+		pwr->gpu_reg = NULL;
+	}
+
+	for (i = 1; i < KGSL_MAX_CLKS; i++)
+		if (pwr->grp_clks[i]) {
+			clk_put(pwr->grp_clks[i]);
+			pwr->grp_clks[i] = NULL;
+		}
+
+	pwr->grp_clks[0] = NULL;
+	pwr->power_flags = 0;
+}
+
+void kgsl_idle_check(struct work_struct *work)
+{
+	struct kgsl_device *device = container_of(work, struct kgsl_device,
+							idle_check_ws);
+	WARN_ON(device == NULL);
+	if (device == NULL)
+		return;
+
+	mutex_lock(&device->mutex);
+	if (device->state & (KGSL_STATE_ACTIVE | KGSL_STATE_NAP)) {
+		kgsl_pwrscale_idle(device);
+
+		if (kgsl_pwrctrl_sleep(device) != 0) {
+			mod_timer(&device->idle_timer,
+					jiffies +
+					device->pwrctrl.interval_timeout);
+			/* If the GPU has been too busy to sleep, make sure *
+			 * that is acurately reflected in the % busy numbers. */
+			device->pwrctrl.busy.no_nap_cnt++;
+			if (device->pwrctrl.busy.no_nap_cnt > UPDATE_BUSY) {
+				kgsl_pwrctrl_busy_time(device, true);
+				device->pwrctrl.busy.no_nap_cnt = 0;
+			}
+		}
+	} else if (device->state & (KGSL_STATE_HUNG |
+					KGSL_STATE_DUMP_AND_RECOVER)) {
+		kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
+	}
+
+	mutex_unlock(&device->mutex);
+}
+
+void kgsl_timer(unsigned long data)
+{
+	struct kgsl_device *device = (struct kgsl_device *) data;
+
+	KGSL_PWR_INFO(device, "idle timer expired device %d\n", device->id);
+	if (device->requested_state != KGSL_STATE_SUSPEND) {
+		if (device->pwrctrl.restore_slumber ||
+					device->pwrctrl.strtstp_sleepwake)
+			kgsl_pwrctrl_request_state(device, KGSL_STATE_SLUMBER);
+		else
+			kgsl_pwrctrl_request_state(device, KGSL_STATE_SLEEP);
+		/* Have work run in a non-interrupt context. */
+		queue_work(device->work_queue, &device->idle_check_ws);
+	}
+}
+
+void kgsl_pre_hwaccess(struct kgsl_device *device)
+{
+	BUG_ON(!mutex_is_locked(&device->mutex));
+	switch (device->state) {
+	case KGSL_STATE_ACTIVE:
+		return;
+	case KGSL_STATE_NAP:
+	case KGSL_STATE_SLEEP:
+	case KGSL_STATE_SLUMBER:
+		kgsl_pwrctrl_wake(device);
+		break;
+	case KGSL_STATE_SUSPEND:
+		kgsl_check_suspended(device);
+		break;
+	case KGSL_STATE_INIT:
+	case KGSL_STATE_HUNG:
+	case KGSL_STATE_DUMP_AND_RECOVER:
+		if (test_bit(KGSL_PWRFLAGS_CLK_ON,
+					 &device->pwrctrl.power_flags))
+			break;
+		else
+			KGSL_PWR_ERR(device,
+					"hw access while clocks off from state %d\n",
+					device->state);
+		break;
+	default:
+		KGSL_PWR_ERR(device, "hw access while in unknown state %d\n",
+					 device->state);
+		break;
+	}
+}
+EXPORT_SYMBOL(kgsl_pre_hwaccess);
+
+void kgsl_check_suspended(struct kgsl_device *device)
+{
+	if (device->requested_state == KGSL_STATE_SUSPEND ||
+				device->state == KGSL_STATE_SUSPEND) {
+		mutex_unlock(&device->mutex);
+		wait_for_completion(&device->hwaccess_gate);
+		mutex_lock(&device->mutex);
+	} else if (device->state == KGSL_STATE_DUMP_AND_RECOVER) {
+		mutex_unlock(&device->mutex);
+		wait_for_completion(&device->recovery_gate);
+		mutex_lock(&device->mutex);
+	} else if (device->state == KGSL_STATE_SLUMBER)
+		kgsl_pwrctrl_wake(device);
+}
+
+static int
+_nap(struct kgsl_device *device)
+{
+	switch (device->state) {
+	case KGSL_STATE_ACTIVE:
+		if (!device->ftbl->isidle(device)) {
+			kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
+			return -EBUSY;
+		}
+		kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF);
+		kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_OFF, KGSL_STATE_NAP);
+		kgsl_mmu_disable_clk(&device->mmu);
+		kgsl_pwrctrl_set_state(device, KGSL_STATE_NAP);
+		if (device->idle_wakelock.name)
+			wake_unlock(&device->idle_wakelock);
+	case KGSL_STATE_NAP:
+	case KGSL_STATE_SLEEP:
+	case KGSL_STATE_SLUMBER:
+		break;
+	default:
+		kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
+		break;
+	}
+	return 0;
+}
+
+static void
+_sleep_accounting(struct kgsl_device *device)
+{
+	kgsl_pwrctrl_busy_time(device, false);
+	device->pwrctrl.busy.start.tv_sec = 0;
+	device->pwrctrl.time = 0;
+	kgsl_pwrscale_sleep(device);
+}
+
+static int
+_sleep(struct kgsl_device *device)
+{
+	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+	switch (device->state) {
+	case KGSL_STATE_ACTIVE:
+		if (!device->ftbl->isidle(device)) {
+			kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
+			return -EBUSY;
+		}
+		/* fall through */
+	case KGSL_STATE_NAP:
+		kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF);
+		kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_OFF);
+		if (pwr->pwrlevels[0].gpu_freq > 0)
+			clk_set_rate(pwr->grp_clks[0],
+				pwr->pwrlevels[pwr->num_pwrlevels - 1].
+				gpu_freq);
+		_sleep_accounting(device);
+		kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_OFF, KGSL_STATE_SLEEP);
+		kgsl_mmu_disable_clk(&device->mmu);
+		kgsl_pwrctrl_set_state(device, KGSL_STATE_SLEEP);
+		wake_unlock(&device->idle_wakelock);
+		pm_qos_update_request(&device->pm_qos_req_dma,
+					PM_QOS_DEFAULT_VALUE);
+		break;
+	case KGSL_STATE_SLEEP:
+	case KGSL_STATE_SLUMBER:
+		break;
+	default:
+		KGSL_PWR_WARN(device, "unhandled state %s\n",
+				kgsl_pwrstate_to_str(device->state));
+		break;
+	}
+	return 0;
+}
+
+static int
+_slumber(struct kgsl_device *device)
+{
+	switch (device->state) {
+	case KGSL_STATE_ACTIVE:
+		if (!device->ftbl->isidle(device)) {
+			kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
+			device->pwrctrl.restore_slumber = true;
+			return -EBUSY;
+		}
+		/* fall through */
+	case KGSL_STATE_NAP:
+	case KGSL_STATE_SLEEP:
+		del_timer_sync(&device->idle_timer);
+		if (!device->pwrctrl.strtstp_sleepwake)
+			kgsl_pwrctrl_pwrlevel_change(device,
+					KGSL_PWRLEVEL_NOMINAL);
+		device->pwrctrl.restore_slumber = true;
+		device->ftbl->suspend_context(device);
+		device->ftbl->stop(device);
+		_sleep_accounting(device);
+		kgsl_pwrctrl_set_state(device, KGSL_STATE_SLUMBER);
+		if (device->idle_wakelock.name)
+			wake_unlock(&device->idle_wakelock);
+		pm_qos_update_request(&device->pm_qos_req_dma,
+						PM_QOS_DEFAULT_VALUE);
+		break;
+	case KGSL_STATE_SLUMBER:
+		break;
+	default:
+		KGSL_PWR_WARN(device, "unhandled state %s\n",
+				kgsl_pwrstate_to_str(device->state));
+		break;
+	}
+	return 0;
+}
+
+/******************************************************************/
+/* Caller must hold the device mutex. */
+int kgsl_pwrctrl_sleep(struct kgsl_device *device)
+{
+	int status = 0;
+	KGSL_PWR_INFO(device, "sleep device %d\n", device->id);
+
+	/* Work through the legal state transitions */
+	switch (device->requested_state) {
+	case KGSL_STATE_NAP:
+		status = _nap(device);
+		break;
+	case KGSL_STATE_SLEEP:
+		status = _sleep(device);
+		break;
+	case KGSL_STATE_SLUMBER:
+		status = _slumber(device);
+		break;
+	default:
+		KGSL_PWR_INFO(device, "bad state request 0x%x\n",
+				device->requested_state);
+		kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
+		status = -EINVAL;
+		break;
+	}
+	return status;
+}
+EXPORT_SYMBOL(kgsl_pwrctrl_sleep);
+
+/******************************************************************/
+/* Caller must hold the device mutex. */
+void kgsl_pwrctrl_wake(struct kgsl_device *device)
+{
+	int status;
+	kgsl_pwrctrl_request_state(device, KGSL_STATE_ACTIVE);
+	switch (device->state) {
+	case KGSL_STATE_SLUMBER:
+		status = device->ftbl->start(device, 0);
+		if (status) {
+			kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
+			KGSL_DRV_ERR(device, "start failed %d\n", status);
+			break;
+		}
+		/* fall through */
+	case KGSL_STATE_SLEEP:
+		kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_ON);
+		kgsl_pwrscale_wake(device);
+		/* fall through */
+	case KGSL_STATE_NAP:
+		/* Turn on the core clocks */
+		kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_ON, KGSL_STATE_ACTIVE);
+		/* Enable state before turning on irq */
+		kgsl_pwrctrl_set_state(device, KGSL_STATE_ACTIVE);
+		kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_ON);
+		/* Re-enable HW access */
+		mod_timer(&device->idle_timer,
+				jiffies + device->pwrctrl.interval_timeout);
+		wake_lock(&device->idle_wakelock);
+		if (device->pwrctrl.restore_slumber == false)
+			pm_qos_update_request(&device->pm_qos_req_dma,
+						GPU_SWFI_LATENCY);
+	case KGSL_STATE_ACTIVE:
+		break;
+	default:
+		KGSL_PWR_WARN(device, "unhandled state %s\n",
+				kgsl_pwrstate_to_str(device->state));
+		kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
+		break;
+	}
+}
+EXPORT_SYMBOL(kgsl_pwrctrl_wake);
+
+void kgsl_pwrctrl_enable(struct kgsl_device *device)
+{
+	/* Order pwrrail/clk sequence based upon platform */
+	kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_ON);
+	kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_ON, KGSL_STATE_ACTIVE);
+	kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_ON);
+}
+EXPORT_SYMBOL(kgsl_pwrctrl_enable);
+
+void kgsl_pwrctrl_disable(struct kgsl_device *device)
+{
+	/* Order pwrrail/clk sequence based upon platform */
+	kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_OFF);
+	kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_OFF, KGSL_STATE_SLEEP);
+	kgsl_mmu_disable_clk(&device->mmu);
+	kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_OFF);
+}
+EXPORT_SYMBOL(kgsl_pwrctrl_disable);
+
+void kgsl_pwrctrl_set_state(struct kgsl_device *device, unsigned int state)
+{
+	trace_kgsl_pwr_set_state(device, state);
+	device->state = state;
+	device->requested_state = KGSL_STATE_NONE;
+}
+EXPORT_SYMBOL(kgsl_pwrctrl_set_state);
+
+void kgsl_pwrctrl_request_state(struct kgsl_device *device, unsigned int state)
+{
+	if (state != KGSL_STATE_NONE && state != device->requested_state)
+		trace_kgsl_pwr_request_state(device, state);
+	device->requested_state = state;
+}
+EXPORT_SYMBOL(kgsl_pwrctrl_request_state);
+
+const char *kgsl_pwrstate_to_str(unsigned int state)
+{
+	switch (state) {
+	case KGSL_STATE_NONE:
+		return "NONE";
+	case KGSL_STATE_INIT:
+		return "INIT";
+	case KGSL_STATE_ACTIVE:
+		return "ACTIVE";
+	case KGSL_STATE_NAP:
+		return "NAP";
+	case KGSL_STATE_SLEEP:
+		return "SLEEP";
+	case KGSL_STATE_SUSPEND:
+		return "SUSPEND";
+	case KGSL_STATE_HUNG:
+		return "HUNG";
+	case KGSL_STATE_DUMP_AND_RECOVER:
+		return "DNR";
+	case KGSL_STATE_SLUMBER:
+		return "SLUMBER";
+	default:
+		break;
+	}
+	return "UNKNOWN";
+}
+EXPORT_SYMBOL(kgsl_pwrstate_to_str);
+
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h
new file mode 100644
index 0000000..1e5c21c
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_pwrctrl.h
@@ -0,0 +1,84 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __KGSL_PWRCTRL_H
+#define __KGSL_PWRCTRL_H
+
+/*****************************************************************************
+** power flags
+*****************************************************************************/
+#define KGSL_PWRFLAGS_ON   1
+#define KGSL_PWRFLAGS_OFF  0
+
+#define KGSL_PWRLEVEL_TURBO 0
+#define KGSL_PWRLEVEL_NOMINAL 1
+#define KGSL_PWRLEVEL_LAST_OFFSET 2
+
+#define KGSL_MAX_CLKS 5
+
+struct platform_device;
+
+struct kgsl_busy {
+	struct timeval start;
+	struct timeval stop;
+	int on_time;
+	int time;
+	int on_time_old;
+	int time_old;
+	unsigned int no_nap_cnt;
+};
+
+struct kgsl_pwrctrl {
+	int interrupt_num;
+	struct clk *ebi1_clk;
+	struct clk *grp_clks[KGSL_MAX_CLKS];
+	unsigned long power_flags;
+	struct kgsl_pwrlevel pwrlevels[KGSL_MAX_PWRLEVELS];
+	unsigned int active_pwrlevel;
+	int thermal_pwrlevel;
+	unsigned int default_pwrlevel;
+	unsigned int num_pwrlevels;
+	unsigned int interval_timeout;
+	bool strtstp_sleepwake;
+	struct regulator *gpu_reg;
+	uint32_t pcl;
+	unsigned int nap_allowed;
+	unsigned int idle_needed;
+	const char *irq_name;
+	s64 time;
+	struct kgsl_busy busy;
+	unsigned int restore_slumber;
+};
+
+void kgsl_pwrctrl_irq(struct kgsl_device *device, int state);
+int kgsl_pwrctrl_init(struct kgsl_device *device);
+void kgsl_pwrctrl_close(struct kgsl_device *device);
+void kgsl_timer(unsigned long data);
+void kgsl_idle_check(struct work_struct *work);
+void kgsl_pre_hwaccess(struct kgsl_device *device);
+void kgsl_check_suspended(struct kgsl_device *device);
+int kgsl_pwrctrl_sleep(struct kgsl_device *device);
+void kgsl_pwrctrl_wake(struct kgsl_device *device);
+void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device,
+	unsigned int level);
+int kgsl_pwrctrl_init_sysfs(struct kgsl_device *device);
+void kgsl_pwrctrl_uninit_sysfs(struct kgsl_device *device);
+void kgsl_pwrctrl_enable(struct kgsl_device *device);
+void kgsl_pwrctrl_disable(struct kgsl_device *device);
+static inline unsigned long kgsl_get_clkrate(struct clk *clk)
+{
+	return (clk != NULL) ? clk_get_rate(clk) : 0;
+}
+
+void kgsl_pwrctrl_set_state(struct kgsl_device *device, unsigned int state);
+void kgsl_pwrctrl_request_state(struct kgsl_device *device, unsigned int state);
+#endif /* __KGSL_PWRCTRL_H */
diff --git a/drivers/gpu/msm/kgsl_pwrscale.c b/drivers/gpu/msm/kgsl_pwrscale.c
new file mode 100644
index 0000000..6fb9326
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_pwrscale.c
@@ -0,0 +1,374 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/export.h>
+#include <linux/kernel.h>
+
+#include <asm/page.h>
+
+#include "kgsl.h"
+#include "kgsl_pwrscale.h"
+#include "kgsl_device.h"
+
+struct kgsl_pwrscale_attribute {
+	struct attribute attr;
+	ssize_t (*show)(struct kgsl_device *device, char *buf);
+	ssize_t (*store)(struct kgsl_device *device, const char *buf,
+			 size_t count);
+};
+
+#define to_pwrscale(k) container_of(k, struct kgsl_pwrscale, kobj)
+#define pwrscale_to_device(p) container_of(p, struct kgsl_device, pwrscale)
+#define to_device(k) container_of(k, struct kgsl_device, pwrscale_kobj)
+#define to_pwrscale_attr(a) \
+container_of(a, struct kgsl_pwrscale_attribute, attr)
+#define to_policy_attr(a) \
+container_of(a, struct kgsl_pwrscale_policy_attribute, attr)
+
+#define PWRSCALE_ATTR(_name, _mode, _show, _store) \
+struct kgsl_pwrscale_attribute pwrscale_attr_##_name = \
+__ATTR(_name, _mode, _show, _store)
+
+/* Master list of available policies */
+
+static struct kgsl_pwrscale_policy *kgsl_pwrscale_policies[] = {
+#ifdef CONFIG_MSM_SCM
+	&kgsl_pwrscale_policy_tz,
+#endif
+#ifdef CONFIG_MSM_SLEEP_STATS_DEVICE
+	&kgsl_pwrscale_policy_idlestats,
+#endif
+#ifdef CONFIG_MSM_DCVS
+	&kgsl_pwrscale_policy_msm,
+#endif
+	NULL
+};
+
+static ssize_t pwrscale_policy_store(struct kgsl_device *device,
+				     const char *buf, size_t count)
+{
+	int i;
+	struct kgsl_pwrscale_policy *policy = NULL;
+
+	/* The special keyword none allows the user to detach all
+	   policies */
+	if (!strncmp("none", buf, 4)) {
+		kgsl_pwrscale_detach_policy(device);
+		return count;
+	}
+
+	for (i = 0; kgsl_pwrscale_policies[i]; i++) {
+		if (!strncmp(kgsl_pwrscale_policies[i]->name, buf,
+			     strnlen(kgsl_pwrscale_policies[i]->name,
+				PAGE_SIZE))) {
+			policy = kgsl_pwrscale_policies[i];
+			break;
+		}
+	}
+
+	if (policy)
+		if (kgsl_pwrscale_attach_policy(device, policy))
+			return -EIO;
+
+	return count;
+}
+
+static ssize_t pwrscale_policy_show(struct kgsl_device *device, char *buf)
+{
+	int ret;
+
+	if (device->pwrscale.policy) {
+		ret = snprintf(buf, PAGE_SIZE, "%s",
+			       device->pwrscale.policy->name);
+		if (device->pwrscale.enabled == 0)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				" (disabled)");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+	} else
+		ret = snprintf(buf, PAGE_SIZE, "none\n");
+
+	return ret;
+}
+
+PWRSCALE_ATTR(policy, 0664, pwrscale_policy_show, pwrscale_policy_store);
+
+static ssize_t pwrscale_avail_policies_show(struct kgsl_device *device,
+					    char *buf)
+{
+	int i, ret = 0;
+
+	for (i = 0; kgsl_pwrscale_policies[i]; i++) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s ",
+				kgsl_pwrscale_policies[i]->name);
+	}
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "none\n");
+	return ret;
+}
+PWRSCALE_ATTR(avail_policies, 0444, pwrscale_avail_policies_show, NULL);
+
+static struct attribute *pwrscale_attrs[] = {
+	&pwrscale_attr_policy.attr,
+	&pwrscale_attr_avail_policies.attr,
+	NULL
+};
+
+static ssize_t policy_sysfs_show(struct kobject *kobj,
+				   struct attribute *attr, char *buf)
+{
+	struct kgsl_pwrscale *pwrscale = to_pwrscale(kobj);
+	struct kgsl_device *device = pwrscale_to_device(pwrscale);
+	struct kgsl_pwrscale_policy_attribute *pattr = to_policy_attr(attr);
+	ssize_t ret;
+
+	if (pattr->show)
+		ret = pattr->show(device, pwrscale, buf);
+	else
+		ret = -EIO;
+
+	return ret;
+}
+
+static ssize_t policy_sysfs_store(struct kobject *kobj,
+				    struct attribute *attr,
+				    const char *buf, size_t count)
+{
+	struct kgsl_pwrscale *pwrscale = to_pwrscale(kobj);
+	struct kgsl_device *device = pwrscale_to_device(pwrscale);
+	struct kgsl_pwrscale_policy_attribute *pattr = to_policy_attr(attr);
+	ssize_t ret;
+
+	if (pattr->store)
+		ret = pattr->store(device, pwrscale, buf, count);
+	else
+		ret = -EIO;
+
+	return ret;
+}
+
+static void policy_sysfs_release(struct kobject *kobj)
+{
+}
+
+static ssize_t pwrscale_sysfs_show(struct kobject *kobj,
+				   struct attribute *attr, char *buf)
+{
+	struct kgsl_device *device = to_device(kobj);
+	struct kgsl_pwrscale_attribute *pattr = to_pwrscale_attr(attr);
+	ssize_t ret;
+
+	if (pattr->show)
+		ret = pattr->show(device, buf);
+	else
+		ret = -EIO;
+
+	return ret;
+}
+
+static ssize_t pwrscale_sysfs_store(struct kobject *kobj,
+				    struct attribute *attr,
+				    const char *buf, size_t count)
+{
+	struct kgsl_device *device = to_device(kobj);
+	struct kgsl_pwrscale_attribute *pattr = to_pwrscale_attr(attr);
+	ssize_t ret;
+
+	if (pattr->store)
+		ret = pattr->store(device, buf, count);
+	else
+		ret = -EIO;
+
+	return ret;
+}
+
+static void pwrscale_sysfs_release(struct kobject *kobj)
+{
+}
+
+static const struct sysfs_ops policy_sysfs_ops = {
+	.show = policy_sysfs_show,
+	.store = policy_sysfs_store
+};
+
+static const struct sysfs_ops pwrscale_sysfs_ops = {
+	.show = pwrscale_sysfs_show,
+	.store = pwrscale_sysfs_store
+};
+
+static struct kobj_type ktype_pwrscale_policy = {
+	.sysfs_ops = &policy_sysfs_ops,
+	.default_attrs = NULL,
+	.release = policy_sysfs_release
+};
+
+static struct kobj_type ktype_pwrscale = {
+	.sysfs_ops = &pwrscale_sysfs_ops,
+	.default_attrs = pwrscale_attrs,
+	.release = pwrscale_sysfs_release
+};
+
+#define PWRSCALE_ACTIVE(_d) \
+	((_d)->pwrscale.policy && (_d)->pwrscale.enabled)
+
+void kgsl_pwrscale_sleep(struct kgsl_device *device)
+{
+	if (PWRSCALE_ACTIVE(device) && device->pwrscale.policy->sleep)
+		device->pwrscale.policy->sleep(device, &device->pwrscale);
+}
+EXPORT_SYMBOL(kgsl_pwrscale_sleep);
+
+void kgsl_pwrscale_wake(struct kgsl_device *device)
+{
+	if (PWRSCALE_ACTIVE(device) && device->pwrscale.policy->wake)
+		device->pwrscale.policy->wake(device, &device->pwrscale);
+}
+EXPORT_SYMBOL(kgsl_pwrscale_wake);
+
+void kgsl_pwrscale_busy(struct kgsl_device *device)
+{
+	if (PWRSCALE_ACTIVE(device) && device->pwrscale.policy->busy)
+		if ((!device->pwrscale.gpu_busy) &&
+			(device->requested_state != KGSL_STATE_SLUMBER))
+			device->pwrscale.policy->busy(device,
+					&device->pwrscale);
+	device->pwrscale.gpu_busy = 1;
+}
+
+void kgsl_pwrscale_idle(struct kgsl_device *device)
+{
+	if (PWRSCALE_ACTIVE(device) && device->pwrscale.policy->idle)
+		if (device->requested_state != KGSL_STATE_SLUMBER &&
+			device->requested_state != KGSL_STATE_SLEEP)
+			device->pwrscale.policy->idle(device,
+					&device->pwrscale);
+	device->pwrscale.gpu_busy = 0;
+}
+EXPORT_SYMBOL(kgsl_pwrscale_idle);
+
+void kgsl_pwrscale_disable(struct kgsl_device *device)
+{
+	device->pwrscale.enabled = 0;
+}
+EXPORT_SYMBOL(kgsl_pwrscale_disable);
+
+void kgsl_pwrscale_enable(struct kgsl_device *device)
+{
+	device->pwrscale.enabled = 1;
+}
+EXPORT_SYMBOL(kgsl_pwrscale_enable);
+
+int kgsl_pwrscale_policy_add_files(struct kgsl_device *device,
+				   struct kgsl_pwrscale *pwrscale,
+				   struct attribute_group *attr_group)
+{
+	int ret;
+
+	ret = kobject_add(&pwrscale->kobj, &device->pwrscale_kobj,
+		"%s", pwrscale->policy->name);
+
+	if (ret)
+		return ret;
+
+	ret = sysfs_create_group(&pwrscale->kobj, attr_group);
+
+	if (ret) {
+		kobject_del(&pwrscale->kobj);
+		kobject_put(&pwrscale->kobj);
+	}
+
+	return ret;
+}
+
+void kgsl_pwrscale_policy_remove_files(struct kgsl_device *device,
+				       struct kgsl_pwrscale *pwrscale,
+				       struct attribute_group *attr_group)
+{
+	sysfs_remove_group(&pwrscale->kobj, attr_group);
+	kobject_del(&pwrscale->kobj);
+	kobject_put(&pwrscale->kobj);
+}
+
+static void _kgsl_pwrscale_detach_policy(struct kgsl_device *device)
+{
+	if (device->pwrscale.policy != NULL) {
+		device->pwrscale.policy->close(device, &device->pwrscale);
+		kgsl_pwrctrl_pwrlevel_change(device,
+				device->pwrctrl.thermal_pwrlevel);
+	}
+	device->pwrscale.policy = NULL;
+}
+
+void kgsl_pwrscale_detach_policy(struct kgsl_device *device)
+{
+	mutex_lock(&device->mutex);
+	_kgsl_pwrscale_detach_policy(device);
+	mutex_unlock(&device->mutex);
+}
+EXPORT_SYMBOL(kgsl_pwrscale_detach_policy);
+
+int kgsl_pwrscale_attach_policy(struct kgsl_device *device,
+				struct kgsl_pwrscale_policy *policy)
+{
+	int ret = 0;
+
+	mutex_lock(&device->mutex);
+
+	if (device->pwrscale.policy == policy)
+		goto done;
+
+	if (device->pwrctrl.num_pwrlevels < 3) {
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (device->pwrscale.policy != NULL)
+		_kgsl_pwrscale_detach_policy(device);
+
+	device->pwrscale.policy = policy;
+
+	/* Pwrscale is enabled by default at attach time */
+	kgsl_pwrscale_enable(device);
+
+	if (policy) {
+		ret = device->pwrscale.policy->init(device, &device->pwrscale);
+		if (ret)
+			device->pwrscale.policy = NULL;
+	}
+
+done:
+	mutex_unlock(&device->mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL(kgsl_pwrscale_attach_policy);
+
+int kgsl_pwrscale_init(struct kgsl_device *device)
+{
+	int ret;
+
+	ret = kobject_init_and_add(&device->pwrscale_kobj, &ktype_pwrscale,
+		&device->dev->kobj, "pwrscale");
+
+	if (ret)
+		return ret;
+
+	kobject_init(&device->pwrscale.kobj, &ktype_pwrscale_policy);
+	return ret;
+}
+EXPORT_SYMBOL(kgsl_pwrscale_init);
+
+void kgsl_pwrscale_close(struct kgsl_device *device)
+{
+	kobject_put(&device->pwrscale_kobj);
+}
+EXPORT_SYMBOL(kgsl_pwrscale_close);
diff --git a/drivers/gpu/msm/kgsl_pwrscale.h b/drivers/gpu/msm/kgsl_pwrscale.h
new file mode 100644
index 0000000..34698cd
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_pwrscale.h
@@ -0,0 +1,82 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __KGSL_PWRSCALE_H
+#define __KGSL_PWRSCALE_H
+
+struct kgsl_pwrscale;
+
+struct kgsl_pwrscale_policy  {
+	const char *name;
+	int (*init)(struct kgsl_device *device,
+		struct kgsl_pwrscale *pwrscale);
+	void (*close)(struct kgsl_device *device,
+		struct kgsl_pwrscale *pwrscale);
+	void (*idle)(struct kgsl_device *device,
+		struct kgsl_pwrscale *pwrscale);
+	void (*busy)(struct kgsl_device *device,
+		struct kgsl_pwrscale *pwrscale);
+	void (*sleep)(struct kgsl_device *device,
+		struct kgsl_pwrscale *pwrscale);
+	void (*wake)(struct kgsl_device *device,
+		struct kgsl_pwrscale *pwrscale);
+};
+
+struct kgsl_pwrscale {
+	struct kgsl_pwrscale_policy *policy;
+	struct kobject kobj;
+	void *priv;
+	int gpu_busy;
+	int enabled;
+};
+
+struct kgsl_pwrscale_policy_attribute {
+	struct attribute attr;
+	ssize_t (*show)(struct kgsl_device *device,
+			struct kgsl_pwrscale *pwrscale, char *buf);
+	ssize_t (*store)(struct kgsl_device *device,
+			 struct kgsl_pwrscale *pwrscale, const char *buf,
+			 size_t count);
+};
+
+#define PWRSCALE_POLICY_ATTR(_name, _mode, _show, _store)          \
+	struct kgsl_pwrscale_policy_attribute policy_attr_##_name = \
+		__ATTR(_name, _mode, _show, _store)
+
+extern struct kgsl_pwrscale_policy kgsl_pwrscale_policy_tz;
+extern struct kgsl_pwrscale_policy kgsl_pwrscale_policy_idlestats;
+extern struct kgsl_pwrscale_policy kgsl_pwrscale_policy_msm;
+
+int kgsl_pwrscale_init(struct kgsl_device *device);
+void kgsl_pwrscale_close(struct kgsl_device *device);
+
+int kgsl_pwrscale_attach_policy(struct kgsl_device *device,
+	struct kgsl_pwrscale_policy *policy);
+void kgsl_pwrscale_detach_policy(struct kgsl_device *device);
+
+void kgsl_pwrscale_idle(struct kgsl_device *device);
+void kgsl_pwrscale_busy(struct kgsl_device *device);
+void kgsl_pwrscale_sleep(struct kgsl_device *device);
+void kgsl_pwrscale_wake(struct kgsl_device *device);
+
+void kgsl_pwrscale_enable(struct kgsl_device *device);
+void kgsl_pwrscale_disable(struct kgsl_device *device);
+
+int kgsl_pwrscale_policy_add_files(struct kgsl_device *device,
+				   struct kgsl_pwrscale *pwrscale,
+				   struct attribute_group *attr_group);
+
+void kgsl_pwrscale_policy_remove_files(struct kgsl_device *device,
+				       struct kgsl_pwrscale *pwrscale,
+				       struct attribute_group *attr_group);
+#endif
diff --git a/drivers/gpu/msm/kgsl_pwrscale_idlestats.c b/drivers/gpu/msm/kgsl_pwrscale_idlestats.c
new file mode 100644
index 0000000..71af893
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_pwrscale_idlestats.c
@@ -0,0 +1,231 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/idle_stats_device.h>
+#include <linux/cpufreq.h>
+#include <linux/notifier.h>
+#include <linux/cpumask.h>
+#include <linux/tick.h>
+
+#include "kgsl.h"
+#include "kgsl_pwrscale.h"
+#include "kgsl_device.h"
+
+#define MAX_CORES 4
+struct _cpu_info {
+	spinlock_t lock;
+	struct notifier_block cpu_nb;
+	u64 start[MAX_CORES];
+	u64 end[MAX_CORES];
+	int curr_freq[MAX_CORES];
+	int max_freq[MAX_CORES];
+};
+
+struct idlestats_priv {
+	char name[32];
+	struct msm_idle_stats_device idledev;
+	struct kgsl_device *device;
+	struct msm_idle_pulse pulse;
+	struct _cpu_info cpu_info;
+};
+
+static int idlestats_cpufreq_notifier(
+				struct notifier_block *nb,
+				unsigned long val, void *data)
+{
+	struct _cpu_info *cpu = container_of(nb,
+						struct _cpu_info, cpu_nb);
+	struct cpufreq_freqs *freq = data;
+
+	if (val != CPUFREQ_POSTCHANGE)
+		return 0;
+
+	spin_lock(&cpu->lock);
+	if (freq->cpu < num_possible_cpus())
+		cpu->curr_freq[freq->cpu] = freq->new / 1000;
+	spin_unlock(&cpu->lock);
+
+	return 0;
+}
+
+static void idlestats_get_sample(struct msm_idle_stats_device *idledev,
+	struct msm_idle_pulse *pulse)
+{
+	struct kgsl_power_stats stats;
+	struct idlestats_priv *priv = container_of(idledev,
+		struct idlestats_priv, idledev);
+	struct kgsl_device *device = priv->device;
+	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+
+	mutex_lock(&device->mutex);
+	/* If the GPU is asleep, don't wake it up - assume that we
+	   are idle */
+
+	if (device->state == KGSL_STATE_ACTIVE) {
+		device->ftbl->power_stats(device, &stats);
+		pulse->busy_start_time = pwr->time - stats.busy_time;
+		pulse->busy_interval = stats.busy_time;
+	} else {
+		pulse->busy_start_time = pwr->time;
+		pulse->busy_interval = 0;
+	}
+	pulse->wait_interval = 0;
+	mutex_unlock(&device->mutex);
+}
+
+static void idlestats_busy(struct kgsl_device *device,
+			struct kgsl_pwrscale *pwrscale)
+{
+	struct idlestats_priv *priv = pwrscale->priv;
+	int i, busy, nr_cpu = 1;
+
+	if (priv->pulse.busy_start_time != 0) {
+		priv->pulse.wait_interval = 0;
+		/* Calculate the total CPU busy time for this GPU pulse */
+		for (i = 0; i < num_possible_cpus(); i++) {
+			spin_lock(&priv->cpu_info.lock);
+			if (cpu_online(i)) {
+				priv->cpu_info.end[i] =
+						(u64)ktime_to_us(ktime_get()) -
+						get_cpu_idle_time_us(i, NULL);
+				busy = priv->cpu_info.end[i] -
+						priv->cpu_info.start[i];
+				/* Normalize the busy time by frequency */
+				busy = priv->cpu_info.curr_freq[i] *
+					(busy / priv->cpu_info.max_freq[i]);
+				priv->pulse.wait_interval += busy;
+				nr_cpu++;
+			}
+			spin_unlock(&priv->cpu_info.lock);
+		}
+		priv->pulse.wait_interval /= nr_cpu;
+		msm_idle_stats_idle_end(&priv->idledev, &priv->pulse);
+	}
+	priv->pulse.busy_start_time = ktime_to_us(ktime_get());
+}
+
+static void idlestats_idle(struct kgsl_device *device,
+			struct kgsl_pwrscale *pwrscale)
+{
+	int i, nr_cpu;
+	struct kgsl_power_stats stats;
+	struct idlestats_priv *priv = pwrscale->priv;
+
+	/* This is called from within a mutex protected function, so
+	   no additional locking required */
+	device->ftbl->power_stats(device, &stats);
+
+	/* If total_time is zero, then we don't have
+	   any interesting statistics to store */
+	if (stats.total_time == 0) {
+		priv->pulse.busy_start_time = 0;
+		return;
+	}
+
+	priv->pulse.busy_interval   = stats.busy_time;
+	nr_cpu = num_possible_cpus();
+	for (i = 0; i < nr_cpu; i++)
+		if (cpu_online(i))
+			priv->cpu_info.start[i] =
+					(u64)ktime_to_us(ktime_get()) -
+					get_cpu_idle_time_us(i, NULL);
+
+	msm_idle_stats_idle_start(&priv->idledev);
+}
+
+static void idlestats_sleep(struct kgsl_device *device,
+			struct kgsl_pwrscale *pwrscale)
+{
+	struct idlestats_priv *priv = pwrscale->priv;
+	msm_idle_stats_update_event(&priv->idledev,
+		MSM_IDLE_STATS_EVENT_IDLE_TIMER_EXPIRED);
+}
+
+static void idlestats_wake(struct kgsl_device *device,
+			struct kgsl_pwrscale *pwrscale)
+{
+	/* Use highest perf level on wake-up from
+	   sleep for better performance */
+	kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_TURBO);
+}
+
+static int idlestats_init(struct kgsl_device *device,
+		     struct kgsl_pwrscale *pwrscale)
+{
+	struct idlestats_priv *priv;
+	struct cpufreq_policy cpu_policy;
+	int ret, i;
+
+	priv = pwrscale->priv = kzalloc(sizeof(struct idlestats_priv),
+		GFP_KERNEL);
+	if (pwrscale->priv == NULL)
+		return -ENOMEM;
+
+	snprintf(priv->name, sizeof(priv->name), "idle_stats_%s",
+		 device->name);
+
+	priv->device = device;
+
+	priv->idledev.name = (const char *) priv->name;
+	priv->idledev.get_sample = idlestats_get_sample;
+
+	spin_lock_init(&priv->cpu_info.lock);
+	priv->cpu_info.cpu_nb.notifier_call =
+			idlestats_cpufreq_notifier;
+	ret = cpufreq_register_notifier(&priv->cpu_info.cpu_nb,
+				CPUFREQ_TRANSITION_NOTIFIER);
+	if (ret)
+		goto err;
+	for (i = 0; i < num_possible_cpus(); i++) {
+		cpufreq_frequency_table_cpuinfo(&cpu_policy,
+					cpufreq_frequency_get_table(i));
+		priv->cpu_info.max_freq[i] = cpu_policy.max / 1000;
+		priv->cpu_info.curr_freq[i] = cpu_policy.max / 1000;
+	}
+	ret = msm_idle_stats_register_device(&priv->idledev);
+err:
+	if (ret) {
+		kfree(pwrscale->priv);
+		pwrscale->priv = NULL;
+	}
+
+	return ret;
+}
+
+static void idlestats_close(struct kgsl_device *device,
+		      struct kgsl_pwrscale *pwrscale)
+{
+	struct idlestats_priv *priv = pwrscale->priv;
+
+	if (pwrscale->priv == NULL)
+		return;
+
+	cpufreq_unregister_notifier(&priv->cpu_info.cpu_nb,
+						CPUFREQ_TRANSITION_NOTIFIER);
+	msm_idle_stats_deregister_device(&priv->idledev);
+
+	kfree(pwrscale->priv);
+	pwrscale->priv = NULL;
+}
+
+struct kgsl_pwrscale_policy kgsl_pwrscale_policy_idlestats = {
+	.name = "idlestats",
+	.init = idlestats_init,
+	.idle = idlestats_idle,
+	.busy = idlestats_busy,
+	.sleep = idlestats_sleep,
+	.wake = idlestats_wake,
+	.close = idlestats_close
+};
diff --git a/drivers/gpu/msm/kgsl_pwrscale_msm.c b/drivers/gpu/msm/kgsl_pwrscale_msm.c
new file mode 100644
index 0000000..61d4b2d
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_pwrscale_msm.c
@@ -0,0 +1,200 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/slab.h>
+#include <mach/msm_dcvs.h>
+#include "kgsl.h"
+#include "kgsl_pwrscale.h"
+#include "kgsl_device.h"
+#include "a2xx_reg.h"
+
+struct msm_priv {
+	struct kgsl_device *device;
+	int enabled;
+	int handle;
+	unsigned int cur_freq;
+	struct msm_dcvs_idle idle_source;
+	struct msm_dcvs_freq freq_sink;
+	struct msm_dcvs_core_info *core_info;
+};
+
+static int msm_idle_enable(struct msm_dcvs_idle *self,
+					enum msm_core_control_event event)
+{
+	struct msm_priv *priv = container_of(self, struct msm_priv,
+								idle_source);
+
+	switch (event) {
+	case MSM_DCVS_ENABLE_IDLE_PULSE:
+		priv->enabled = true;
+		break;
+	case MSM_DCVS_DISABLE_IDLE_PULSE:
+		priv->enabled = false;
+		break;
+	case MSM_DCVS_ENABLE_HIGH_LATENCY_MODES:
+	case MSM_DCVS_DISABLE_HIGH_LATENCY_MODES:
+		break;
+	}
+	return 0;
+}
+
+/* Set the requested frequency if it is within 5MHz (delta) of a
+ * supported frequency.
+ */
+static int msm_set_freq(struct msm_dcvs_freq *self,
+						unsigned int freq)
+{
+	int i, delta = 5000000;
+	struct msm_priv *priv = container_of(self, struct msm_priv,
+								freq_sink);
+	struct kgsl_device *device = priv->device;
+	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+
+	/* msm_dcvs manager uses frequencies in kHz */
+	freq *= 1000;
+	for (i = 0; i < pwr->num_pwrlevels; i++)
+		if (abs(pwr->pwrlevels[i].gpu_freq - freq) < delta)
+			break;
+	if (i == pwr->num_pwrlevels)
+		return 0;
+
+	mutex_lock(&device->mutex);
+	kgsl_pwrctrl_pwrlevel_change(device, i);
+	priv->cur_freq = pwr->pwrlevels[pwr->active_pwrlevel].gpu_freq;
+	mutex_unlock(&device->mutex);
+
+	/* return current frequency in kHz */
+	return priv->cur_freq / 1000;
+}
+
+static unsigned int msm_get_freq(struct msm_dcvs_freq *self)
+{
+	struct msm_priv *priv = container_of(self, struct msm_priv,
+								freq_sink);
+	/* return current frequency in kHz */
+	return priv->cur_freq / 1000;
+}
+
+static void msm_busy(struct kgsl_device *device,
+			struct kgsl_pwrscale *pwrscale)
+{
+	struct msm_priv *priv = pwrscale->priv;
+	if (priv->enabled)
+		msm_dcvs_idle(priv->handle, MSM_DCVS_IDLE_EXIT, 0);
+	return;
+}
+
+static void msm_idle(struct kgsl_device *device,
+			struct kgsl_pwrscale *pwrscale)
+{
+	struct msm_priv *priv = pwrscale->priv;
+	unsigned int rb_rptr, rb_wptr;
+	kgsl_regread(device, REG_CP_RB_RPTR, &rb_rptr);
+	kgsl_regread(device, REG_CP_RB_WPTR, &rb_wptr);
+
+	if (priv->enabled && (rb_rptr == rb_wptr))
+		msm_dcvs_idle(priv->handle, MSM_DCVS_IDLE_ENTER, 0);
+
+	return;
+}
+
+static void msm_sleep(struct kgsl_device *device,
+			struct kgsl_pwrscale *pwrscale)
+{
+	/* do we need to reset any parameters here? */
+}
+
+static int msm_init(struct kgsl_device *device,
+		     struct kgsl_pwrscale *pwrscale)
+{
+	struct msm_priv *priv;
+	struct msm_dcvs_freq_entry *tbl;
+	int i, ret, low_level;
+	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+	struct platform_device *pdev =
+		container_of(device->parentdev, struct platform_device, dev);
+	struct kgsl_device_platform_data *pdata = pdev->dev.platform_data;
+
+	priv = pwrscale->priv = kzalloc(sizeof(struct msm_priv),
+		GFP_KERNEL);
+	if (pwrscale->priv == NULL)
+		return -ENOMEM;
+
+	priv->core_info = pdata->core_info;
+	tbl = priv->core_info->freq_tbl;
+	/* Fill in frequency table from low to high, reversing order. */
+	low_level = pwr->num_pwrlevels - KGSL_PWRLEVEL_LAST_OFFSET;
+	for (i = 0; i <= low_level; i++)
+		tbl[i].freq =
+			pwr->pwrlevels[low_level - i].gpu_freq / 1000;
+	ret = msm_dcvs_register_core(device->name, 0, priv->core_info);
+	if (ret) {
+		KGSL_PWR_ERR(device, "msm_dcvs_register_core failed");
+		goto err;
+	}
+
+	priv->device = device;
+	priv->idle_source.enable = msm_idle_enable;
+	priv->idle_source.core_name = device->name;
+	priv->handle = msm_dcvs_idle_source_register(&priv->idle_source);
+	if (priv->handle < 0) {
+		ret = priv->handle;
+		KGSL_PWR_ERR(device, "msm_dcvs_idle_source_register failed\n");
+		goto err;
+	}
+
+	priv->freq_sink.core_name = device->name;
+	priv->freq_sink.set_frequency = msm_set_freq;
+	priv->freq_sink.get_frequency = msm_get_freq;
+	ret = msm_dcvs_freq_sink_register(&priv->freq_sink);
+	if (ret >= 0) {
+		if (device->ftbl->isidle(device)) {
+			device->pwrscale.gpu_busy = 0;
+			msm_dcvs_idle(priv->handle, MSM_DCVS_IDLE_ENTER, 0);
+		} else {
+			device->pwrscale.gpu_busy = 1;
+		}
+		return 0;
+	}
+
+	KGSL_PWR_ERR(device, "msm_dcvs_freq_sink_register failed\n");
+	msm_dcvs_idle_source_unregister(&priv->idle_source);
+
+err:
+	kfree(pwrscale->priv);
+	pwrscale->priv = NULL;
+
+	return ret;
+}
+
+static void msm_close(struct kgsl_device *device,
+		      struct kgsl_pwrscale *pwrscale)
+{
+	struct msm_priv *priv = pwrscale->priv;
+
+	if (pwrscale->priv == NULL)
+		return;
+	msm_dcvs_idle_source_unregister(&priv->idle_source);
+	msm_dcvs_freq_sink_unregister(&priv->freq_sink);
+	kfree(pwrscale->priv);
+	pwrscale->priv = NULL;
+}
+
+struct kgsl_pwrscale_policy kgsl_pwrscale_policy_msm = {
+	.name = "msm",
+	.init = msm_init,
+	.idle = msm_idle,
+	.busy = msm_busy,
+	.sleep = msm_sleep,
+	.close = msm_close,
+};
diff --git a/drivers/gpu/msm/kgsl_pwrscale_trustzone.c b/drivers/gpu/msm/kgsl_pwrscale_trustzone.c
new file mode 100644
index 0000000..ad1e7ed
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_pwrscale_trustzone.c
@@ -0,0 +1,215 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+#include <mach/socinfo.h>
+#include <mach/scm.h>
+
+#include "kgsl.h"
+#include "kgsl_pwrscale.h"
+#include "kgsl_device.h"
+
+#define TZ_GOVERNOR_PERFORMANCE 0
+#define TZ_GOVERNOR_ONDEMAND    1
+
+struct tz_priv {
+	int governor;
+	unsigned int no_switch_cnt;
+	unsigned int skip_cnt;
+};
+spinlock_t tz_lock;
+
+#define SWITCH_OFF		200
+#define SWITCH_OFF_RESET_TH	40
+#define SKIP_COUNTER		500
+#define TZ_RESET_ID		0x3
+#define TZ_UPDATE_ID		0x4
+
+#ifdef CONFIG_MSM_SCM
+/* Trap into the TrustZone, and call funcs there. */
+static int __secure_tz_entry(u32 cmd, u32 val, u32 id)
+{
+	int ret;
+	spin_lock(&tz_lock);
+	__iowmb();
+	ret = scm_call_atomic2(SCM_SVC_IO, cmd, val, id);
+	spin_unlock(&tz_lock);
+	return ret;
+}
+#else
+static int __secure_tz_entry(u32 cmd, u32 val, u32 id)
+{
+	return 0;
+}
+#endif /* CONFIG_MSM_SCM */
+
+static ssize_t tz_governor_show(struct kgsl_device *device,
+				struct kgsl_pwrscale *pwrscale,
+				char *buf)
+{
+	struct tz_priv *priv = pwrscale->priv;
+	int ret;
+
+	if (priv->governor == TZ_GOVERNOR_ONDEMAND)
+		ret = snprintf(buf, 10, "ondemand\n");
+	else
+		ret = snprintf(buf, 13, "performance\n");
+
+	return ret;
+}
+
+static ssize_t tz_governor_store(struct kgsl_device *device,
+				struct kgsl_pwrscale *pwrscale,
+				 const char *buf, size_t count)
+{
+	char str[20];
+	struct tz_priv *priv = pwrscale->priv;
+	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+	int ret;
+
+	ret = sscanf(buf, "%20s", str);
+	if (ret != 1)
+		return -EINVAL;
+
+	mutex_lock(&device->mutex);
+
+	if (!strncmp(str, "ondemand", 8))
+		priv->governor = TZ_GOVERNOR_ONDEMAND;
+	else if (!strncmp(str, "performance", 11))
+		priv->governor = TZ_GOVERNOR_PERFORMANCE;
+
+	if (priv->governor == TZ_GOVERNOR_PERFORMANCE)
+		kgsl_pwrctrl_pwrlevel_change(device, pwr->thermal_pwrlevel);
+
+	mutex_unlock(&device->mutex);
+	return count;
+}
+
+PWRSCALE_POLICY_ATTR(governor, 0644, tz_governor_show, tz_governor_store);
+
+static struct attribute *tz_attrs[] = {
+	&policy_attr_governor.attr,
+	NULL
+};
+
+static struct attribute_group tz_attr_group = {
+	.attrs = tz_attrs,
+};
+
+static void tz_wake(struct kgsl_device *device, struct kgsl_pwrscale *pwrscale)
+{
+	struct tz_priv *priv = pwrscale->priv;
+	if (device->state != KGSL_STATE_NAP &&
+		priv->governor == TZ_GOVERNOR_ONDEMAND &&
+		device->pwrctrl.restore_slumber == 0)
+		kgsl_pwrctrl_pwrlevel_change(device,
+					device->pwrctrl.default_pwrlevel);
+}
+
+static void tz_idle(struct kgsl_device *device, struct kgsl_pwrscale *pwrscale)
+{
+	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+	struct tz_priv *priv = pwrscale->priv;
+	struct kgsl_power_stats stats;
+	int val, idle;
+
+	/* In "performance" mode the clock speed always stays
+	   the same */
+
+	if (priv->governor == TZ_GOVERNOR_PERFORMANCE)
+		return;
+
+	device->ftbl->power_stats(device, &stats);
+	if (stats.total_time == 0)
+		return;
+
+	/* If the GPU has stayed in turbo mode for a while, *
+	 * stop writing out values. */
+	if (pwr->active_pwrlevel == 0) {
+		if (priv->no_switch_cnt > SWITCH_OFF) {
+			priv->skip_cnt++;
+			if (priv->skip_cnt > SKIP_COUNTER) {
+				priv->no_switch_cnt -= SWITCH_OFF_RESET_TH;
+				priv->skip_cnt = 0;
+			}
+			return;
+		}
+		priv->no_switch_cnt++;
+	} else {
+		priv->no_switch_cnt = 0;
+	}
+
+	idle = stats.total_time - stats.busy_time;
+	idle = (idle > 0) ? idle : 0;
+	val = __secure_tz_entry(TZ_UPDATE_ID, idle, device->id);
+	if (val)
+		kgsl_pwrctrl_pwrlevel_change(device,
+					     pwr->active_pwrlevel + val);
+}
+
+static void tz_busy(struct kgsl_device *device,
+	struct kgsl_pwrscale *pwrscale)
+{
+	device->on_time = ktime_to_us(ktime_get());
+}
+
+static void tz_sleep(struct kgsl_device *device,
+	struct kgsl_pwrscale *pwrscale)
+{
+	struct tz_priv *priv = pwrscale->priv;
+
+	__secure_tz_entry(TZ_RESET_ID, 0, device->id);
+	priv->no_switch_cnt = 0;
+}
+
+static int tz_init(struct kgsl_device *device, struct kgsl_pwrscale *pwrscale)
+{
+	struct tz_priv *priv;
+
+	/* Trustzone is only valid for some SOCs */
+	if (!(cpu_is_msm8x60() || cpu_is_msm8960() || cpu_is_apq8064() ||
+		cpu_is_msm8930()))
+		return -EINVAL;
+
+	priv = pwrscale->priv = kzalloc(sizeof(struct tz_priv), GFP_KERNEL);
+	if (pwrscale->priv == NULL)
+		return -ENOMEM;
+
+	priv->governor = TZ_GOVERNOR_ONDEMAND;
+	spin_lock_init(&tz_lock);
+	kgsl_pwrscale_policy_add_files(device, pwrscale, &tz_attr_group);
+
+	return 0;
+}
+
+static void tz_close(struct kgsl_device *device, struct kgsl_pwrscale *pwrscale)
+{
+	kgsl_pwrscale_policy_remove_files(device, pwrscale, &tz_attr_group);
+	kfree(pwrscale->priv);
+	pwrscale->priv = NULL;
+}
+
+struct kgsl_pwrscale_policy kgsl_pwrscale_policy_tz = {
+	.name = "trustzone",
+	.init = tz_init,
+	.busy = tz_busy,
+	.idle = tz_idle,
+	.sleep = tz_sleep,
+	.wake = tz_wake,
+	.close = tz_close
+};
+EXPORT_SYMBOL(kgsl_pwrscale_policy_tz);
diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c
new file mode 100644
index 0000000..a2dd649
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_sharedmem.c
@@ -0,0 +1,817 @@
+/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/export.h>
+#include <linux/vmalloc.h>
+#include <linux/memory_alloc.h>
+#include <asm/cacheflush.h>
+#include <linux/slab.h>
+#include <linux/kmemleak.h>
+
+#include "kgsl.h"
+#include "kgsl_sharedmem.h"
+#include "kgsl_cffdump.h"
+#include "kgsl_device.h"
+
+/* An attribute for showing per-process memory statistics */
+struct kgsl_mem_entry_attribute {
+	struct attribute attr;
+	int memtype;
+	ssize_t (*show)(struct kgsl_process_private *priv,
+		int type, char *buf);
+};
+
+#define to_mem_entry_attr(a) \
+container_of(a, struct kgsl_mem_entry_attribute, attr)
+
+#define __MEM_ENTRY_ATTR(_type, _name, _show) \
+{ \
+	.attr = { .name = __stringify(_name), .mode = 0444 }, \
+	.memtype = _type, \
+	.show = _show, \
+}
+
+/*
+ * A structure to hold the attributes for a particular memory type.
+ * For each memory type in each process we store the current and maximum
+ * memory usage and display the counts in sysfs.  This structure and
+ * the following macro allow us to simplify the definition for those
+ * adding new memory types
+ */
+
+struct mem_entry_stats {
+	int memtype;
+	struct kgsl_mem_entry_attribute attr;
+	struct kgsl_mem_entry_attribute max_attr;
+};
+
+
+#define MEM_ENTRY_STAT(_type, _name) \
+{ \
+	.memtype = _type, \
+	.attr = __MEM_ENTRY_ATTR(_type, _name, mem_entry_show), \
+	.max_attr = __MEM_ENTRY_ATTR(_type, _name##_max, \
+		mem_entry_max_show), \
+}
+
+
+/*
+ * One page allocation for a guard region to protect against over-zealous
+ * GPU pre-fetch
+ */
+
+static struct page *kgsl_guard_page;
+
+/**
+ * Given a kobj, find the process structure attached to it
+ */
+
+static struct kgsl_process_private *
+_get_priv_from_kobj(struct kobject *kobj)
+{
+	struct kgsl_process_private *private;
+	unsigned long name;
+
+	if (!kobj)
+		return NULL;
+
+	if (sscanf(kobj->name, "%ld", &name) != 1)
+		return NULL;
+
+	list_for_each_entry(private, &kgsl_driver.process_list, list) {
+		if (private->pid == name)
+			return private;
+	}
+
+	return NULL;
+}
+
+/**
+ * Show the current amount of memory allocated for the given memtype
+ */
+
+static ssize_t
+mem_entry_show(struct kgsl_process_private *priv, int type, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", priv->stats[type].cur);
+}
+
+/**
+ * Show the maximum memory allocated for the given memtype through the life of
+ * the process
+ */
+
+static ssize_t
+mem_entry_max_show(struct kgsl_process_private *priv, int type, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", priv->stats[type].max);
+}
+
+
+static void mem_entry_sysfs_release(struct kobject *kobj)
+{
+}
+
+static ssize_t mem_entry_sysfs_show(struct kobject *kobj,
+	struct attribute *attr, char *buf)
+{
+	struct kgsl_mem_entry_attribute *pattr = to_mem_entry_attr(attr);
+	struct kgsl_process_private *priv;
+	ssize_t ret;
+
+	mutex_lock(&kgsl_driver.process_mutex);
+	priv = _get_priv_from_kobj(kobj);
+
+	if (priv && pattr->show)
+		ret = pattr->show(priv, pattr->memtype, buf);
+	else
+		ret = -EIO;
+
+	mutex_unlock(&kgsl_driver.process_mutex);
+	return ret;
+}
+
+static const struct sysfs_ops mem_entry_sysfs_ops = {
+	.show = mem_entry_sysfs_show,
+};
+
+static struct kobj_type ktype_mem_entry = {
+	.sysfs_ops = &mem_entry_sysfs_ops,
+	.default_attrs = NULL,
+	.release = mem_entry_sysfs_release
+};
+
+static struct mem_entry_stats mem_stats[] = {
+	MEM_ENTRY_STAT(KGSL_MEM_ENTRY_KERNEL, kernel),
+#ifdef CONFIG_ANDROID_PMEM
+	MEM_ENTRY_STAT(KGSL_MEM_ENTRY_PMEM, pmem),
+#endif
+#ifdef CONFIG_ASHMEM
+	MEM_ENTRY_STAT(KGSL_MEM_ENTRY_ASHMEM, ashmem),
+#endif
+	MEM_ENTRY_STAT(KGSL_MEM_ENTRY_USER, user),
+#ifdef CONFIG_ION
+	MEM_ENTRY_STAT(KGSL_MEM_ENTRY_ION, ion),
+#endif
+};
+
+void
+kgsl_process_uninit_sysfs(struct kgsl_process_private *private)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(mem_stats); i++) {
+		sysfs_remove_file(&private->kobj, &mem_stats[i].attr.attr);
+		sysfs_remove_file(&private->kobj,
+			&mem_stats[i].max_attr.attr);
+	}
+
+	kobject_put(&private->kobj);
+}
+
+void
+kgsl_process_init_sysfs(struct kgsl_process_private *private)
+{
+	unsigned char name[16];
+	int i, ret;
+
+	snprintf(name, sizeof(name), "%d", private->pid);
+
+	if (kobject_init_and_add(&private->kobj, &ktype_mem_entry,
+		kgsl_driver.prockobj, name))
+		return;
+
+	for (i = 0; i < ARRAY_SIZE(mem_stats); i++) {
+		/* We need to check the value of sysfs_create_file, but we
+		 * don't really care if it passed or not */
+
+		ret = sysfs_create_file(&private->kobj,
+			&mem_stats[i].attr.attr);
+		ret = sysfs_create_file(&private->kobj,
+			&mem_stats[i].max_attr.attr);
+	}
+}
+
+static int kgsl_drv_memstat_show(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	unsigned int val = 0;
+
+	if (!strncmp(attr->attr.name, "vmalloc", 7))
+		val = kgsl_driver.stats.vmalloc;
+	else if (!strncmp(attr->attr.name, "vmalloc_max", 11))
+		val = kgsl_driver.stats.vmalloc_max;
+	else if (!strncmp(attr->attr.name, "page_alloc", 10))
+		val = kgsl_driver.stats.page_alloc;
+	else if (!strncmp(attr->attr.name, "page_alloc_max", 14))
+		val = kgsl_driver.stats.page_alloc_max;
+	else if (!strncmp(attr->attr.name, "coherent", 8))
+		val = kgsl_driver.stats.coherent;
+	else if (!strncmp(attr->attr.name, "coherent_max", 12))
+		val = kgsl_driver.stats.coherent_max;
+	else if (!strncmp(attr->attr.name, "mapped", 6))
+		val = kgsl_driver.stats.mapped;
+	else if (!strncmp(attr->attr.name, "mapped_max", 10))
+		val = kgsl_driver.stats.mapped_max;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", val);
+}
+
+static int kgsl_drv_histogram_show(struct device *dev,
+				   struct device_attribute *attr,
+				   char *buf)
+{
+	int len = 0;
+	int i;
+
+	for (i = 0; i < 16; i++)
+		len += snprintf(buf + len, PAGE_SIZE - len, "%d ",
+			kgsl_driver.stats.histogram[i]);
+
+	len += snprintf(buf + len, PAGE_SIZE - len, "\n");
+	return len;
+}
+
+DEVICE_ATTR(vmalloc, 0444, kgsl_drv_memstat_show, NULL);
+DEVICE_ATTR(vmalloc_max, 0444, kgsl_drv_memstat_show, NULL);
+DEVICE_ATTR(page_alloc, 0444, kgsl_drv_memstat_show, NULL);
+DEVICE_ATTR(page_alloc_max, 0444, kgsl_drv_memstat_show, NULL);
+DEVICE_ATTR(coherent, 0444, kgsl_drv_memstat_show, NULL);
+DEVICE_ATTR(coherent_max, 0444, kgsl_drv_memstat_show, NULL);
+DEVICE_ATTR(mapped, 0444, kgsl_drv_memstat_show, NULL);
+DEVICE_ATTR(mapped_max, 0444, kgsl_drv_memstat_show, NULL);
+DEVICE_ATTR(histogram, 0444, kgsl_drv_histogram_show, NULL);
+
+static const struct device_attribute *drv_attr_list[] = {
+	&dev_attr_vmalloc,
+	&dev_attr_vmalloc_max,
+	&dev_attr_page_alloc,
+	&dev_attr_page_alloc_max,
+	&dev_attr_coherent,
+	&dev_attr_coherent_max,
+	&dev_attr_mapped,
+	&dev_attr_mapped_max,
+	&dev_attr_histogram,
+	NULL
+};
+
+void
+kgsl_sharedmem_uninit_sysfs(void)
+{
+	kgsl_remove_device_sysfs_files(&kgsl_driver.virtdev, drv_attr_list);
+}
+
+int
+kgsl_sharedmem_init_sysfs(void)
+{
+	return kgsl_create_device_sysfs_files(&kgsl_driver.virtdev,
+		drv_attr_list);
+}
+
+#ifdef CONFIG_OUTER_CACHE
+static void _outer_cache_range_op(int op, unsigned long addr, size_t size)
+{
+	switch (op) {
+	case KGSL_CACHE_OP_FLUSH:
+		outer_flush_range(addr, addr + size);
+		break;
+	case KGSL_CACHE_OP_CLEAN:
+		outer_clean_range(addr, addr + size);
+		break;
+	case KGSL_CACHE_OP_INV:
+		outer_inv_range(addr, addr + size);
+		break;
+	}
+}
+
+static void outer_cache_range_op_sg(struct scatterlist *sg, int sglen, int op)
+{
+	struct scatterlist *s;
+	int i;
+
+	for_each_sg(sg, s, sglen, i) {
+		unsigned int paddr = kgsl_get_sg_pa(s);
+		_outer_cache_range_op(op, paddr, s->length);
+	}
+}
+
+#else
+static void outer_cache_range_op_sg(struct scatterlist *sg, int sglen, int op)
+{
+}
+#endif
+
+static int kgsl_page_alloc_vmfault(struct kgsl_memdesc *memdesc,
+				struct vm_area_struct *vma,
+				struct vm_fault *vmf)
+{
+	unsigned long offset;
+	struct page *page;
+	int i;
+
+	offset = (unsigned long) vmf->virtual_address - vma->vm_start;
+
+	i = offset >> PAGE_SHIFT;
+	page = sg_page(&memdesc->sg[i]);
+	if (page == NULL)
+		return VM_FAULT_SIGBUS;
+
+	get_page(page);
+
+	vmf->page = page;
+	return 0;
+}
+
+static int kgsl_page_alloc_vmflags(struct kgsl_memdesc *memdesc)
+{
+	return VM_RESERVED | VM_DONTEXPAND;
+}
+
+static void kgsl_page_alloc_free(struct kgsl_memdesc *memdesc)
+{
+	int i = 0;
+	struct scatterlist *sg;
+	int sglen = memdesc->sglen;
+
+	/* Don't free the guard page if it was used */
+	if (memdesc->flags & KGSL_MEMDESC_GUARD_PAGE)
+		sglen--;
+
+	kgsl_driver.stats.page_alloc -= memdesc->size;
+
+	if (memdesc->hostptr) {
+		vunmap(memdesc->hostptr);
+		kgsl_driver.stats.vmalloc -= memdesc->size;
+	}
+	if (memdesc->sg)
+		for_each_sg(memdesc->sg, sg, sglen, i)
+			__free_page(sg_page(sg));
+}
+
+static int kgsl_contiguous_vmflags(struct kgsl_memdesc *memdesc)
+{
+	return VM_RESERVED | VM_IO | VM_PFNMAP | VM_DONTEXPAND;
+}
+
+/*
+ * kgsl_page_alloc_map_kernel - Map the memory in memdesc to kernel address
+ * space
+ *
+ * @memdesc - The memory descriptor which contains information about the memory
+ *
+ * Return: 0 on success else error code
+ */
+static int kgsl_page_alloc_map_kernel(struct kgsl_memdesc *memdesc)
+{
+	if (!memdesc->hostptr) {
+		pgprot_t page_prot = pgprot_writecombine(PAGE_KERNEL);
+		struct page **pages = NULL;
+		struct scatterlist *sg;
+		int sglen = memdesc->sglen;
+		int i;
+
+		/* Don't map the guard page if it exists */
+		if (memdesc->flags & KGSL_MEMDESC_GUARD_PAGE)
+			sglen--;
+
+		/* create a list of pages to call vmap */
+		pages = vmalloc(sglen * sizeof(struct page *));
+		if (!pages) {
+			KGSL_CORE_ERR("vmalloc(%d) failed\n",
+				sglen * sizeof(struct page *));
+			return -ENOMEM;
+		}
+		for_each_sg(memdesc->sg, sg, sglen, i)
+			pages[i] = sg_page(sg);
+		memdesc->hostptr = vmap(pages, sglen,
+					VM_IOREMAP, page_prot);
+		KGSL_STATS_ADD(memdesc->size, kgsl_driver.stats.vmalloc,
+				kgsl_driver.stats.vmalloc_max);
+		vfree(pages);
+	}
+	if (!memdesc->hostptr)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int kgsl_contiguous_vmfault(struct kgsl_memdesc *memdesc,
+				struct vm_area_struct *vma,
+				struct vm_fault *vmf)
+{
+	unsigned long offset, pfn;
+	int ret;
+
+	offset = ((unsigned long) vmf->virtual_address - vma->vm_start) >>
+		PAGE_SHIFT;
+
+	pfn = (memdesc->physaddr >> PAGE_SHIFT) + offset;
+	ret = vm_insert_pfn(vma, (unsigned long) vmf->virtual_address, pfn);
+
+	if (ret == -ENOMEM || ret == -EAGAIN)
+		return VM_FAULT_OOM;
+	else if (ret == -EFAULT)
+		return VM_FAULT_SIGBUS;
+
+	return VM_FAULT_NOPAGE;
+}
+
+static void kgsl_ebimem_free(struct kgsl_memdesc *memdesc)
+
+{
+	kgsl_driver.stats.coherent -= memdesc->size;
+	if (memdesc->hostptr)
+		iounmap(memdesc->hostptr);
+
+	free_contiguous_memory_by_paddr(memdesc->physaddr);
+}
+
+static void kgsl_coherent_free(struct kgsl_memdesc *memdesc)
+{
+	kgsl_driver.stats.coherent -= memdesc->size;
+	dma_free_coherent(NULL, memdesc->size,
+			  memdesc->hostptr, memdesc->physaddr);
+}
+
+/* Global - also used by kgsl_drm.c */
+struct kgsl_memdesc_ops kgsl_page_alloc_ops = {
+	.free = kgsl_page_alloc_free,
+	.vmflags = kgsl_page_alloc_vmflags,
+	.vmfault = kgsl_page_alloc_vmfault,
+	.map_kernel_mem = kgsl_page_alloc_map_kernel,
+};
+EXPORT_SYMBOL(kgsl_page_alloc_ops);
+
+static struct kgsl_memdesc_ops kgsl_ebimem_ops = {
+	.free = kgsl_ebimem_free,
+	.vmflags = kgsl_contiguous_vmflags,
+	.vmfault = kgsl_contiguous_vmfault,
+};
+
+static struct kgsl_memdesc_ops kgsl_coherent_ops = {
+	.free = kgsl_coherent_free,
+};
+
+void kgsl_cache_range_op(struct kgsl_memdesc *memdesc, int op)
+{
+	void *addr = memdesc->hostptr;
+	int size = memdesc->size;
+
+	switch (op) {
+	case KGSL_CACHE_OP_FLUSH:
+		dmac_flush_range(addr, addr + size);
+		break;
+	case KGSL_CACHE_OP_CLEAN:
+		dmac_clean_range(addr, addr + size);
+		break;
+	case KGSL_CACHE_OP_INV:
+		dmac_inv_range(addr, addr + size);
+		break;
+	}
+
+	outer_cache_range_op_sg(memdesc->sg, memdesc->sglen, op);
+}
+EXPORT_SYMBOL(kgsl_cache_range_op);
+
+static int
+_kgsl_sharedmem_page_alloc(struct kgsl_memdesc *memdesc,
+			struct kgsl_pagetable *pagetable,
+			size_t size, unsigned int protflags)
+{
+	int order, ret = 0;
+	int sglen = PAGE_ALIGN(size) / PAGE_SIZE;
+	int i;
+
+	/*
+	 * Add guard page to the end of the allocation when the
+	 * IOMMU is in use.
+	 */
+
+	if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_IOMMU)
+		sglen++;
+
+	memdesc->size = size;
+	memdesc->pagetable = pagetable;
+	memdesc->priv = KGSL_MEMFLAGS_CACHED;
+	memdesc->ops = &kgsl_page_alloc_ops;
+
+	memdesc->sg = kgsl_sg_alloc(sglen);
+
+	if (memdesc->sg == NULL) {
+		KGSL_CORE_ERR("vmalloc(%d) failed\n",
+			sglen * sizeof(struct scatterlist));
+		ret = -ENOMEM;
+		goto done;
+	}
+
+	kmemleak_not_leak(memdesc->sg);
+
+	memdesc->sglen = sglen;
+	sg_init_table(memdesc->sg, sglen);
+
+	for (i = 0; i < PAGE_ALIGN(size) / PAGE_SIZE; i++) {
+		struct page *page = alloc_page(GFP_KERNEL | __GFP_ZERO |
+						__GFP_HIGHMEM);
+		if (!page) {
+			ret = -ENOMEM;
+			memdesc->sglen = i;
+			goto done;
+		}
+		flush_dcache_page(page);
+		sg_set_page(&memdesc->sg[i], page, PAGE_SIZE, 0);
+	}
+
+	/* ADd the guard page to the end of the sglist */
+
+	if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_IOMMU) {
+		if (kgsl_guard_page == NULL)
+			kgsl_guard_page = alloc_page(GFP_KERNEL | __GFP_ZERO |
+				__GFP_HIGHMEM);
+
+		if (kgsl_guard_page != NULL) {
+			sg_set_page(&memdesc->sg[sglen - 1], kgsl_guard_page,
+				PAGE_SIZE, 0);
+			memdesc->flags |= KGSL_MEMDESC_GUARD_PAGE;
+		} else
+			memdesc->sglen--;
+	}
+
+	outer_cache_range_op_sg(memdesc->sg, memdesc->sglen,
+				KGSL_CACHE_OP_FLUSH);
+
+	ret = kgsl_mmu_map(pagetable, memdesc, protflags);
+
+	if (ret)
+		goto done;
+
+	KGSL_STATS_ADD(size, kgsl_driver.stats.page_alloc,
+		kgsl_driver.stats.page_alloc_max);
+
+	order = get_order(size);
+
+	if (order < 16)
+		kgsl_driver.stats.histogram[order]++;
+
+done:
+	if (ret)
+		kgsl_sharedmem_free(memdesc);
+
+	return ret;
+}
+
+int
+kgsl_sharedmem_page_alloc(struct kgsl_memdesc *memdesc,
+		       struct kgsl_pagetable *pagetable, size_t size)
+{
+	int ret = 0;
+	BUG_ON(size == 0);
+
+	size = ALIGN(size, PAGE_SIZE * 2);
+
+	ret =  _kgsl_sharedmem_page_alloc(memdesc, pagetable, size,
+		GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
+	if (!ret)
+		ret = kgsl_page_alloc_map_kernel(memdesc);
+	if (ret)
+		kgsl_sharedmem_free(memdesc);
+	return ret;
+}
+EXPORT_SYMBOL(kgsl_sharedmem_page_alloc);
+
+int
+kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
+			    struct kgsl_pagetable *pagetable,
+			    size_t size, int flags)
+{
+	unsigned int protflags;
+
+	BUG_ON(size == 0);
+
+	protflags = GSL_PT_PAGE_RV;
+	if (!(flags & KGSL_MEMFLAGS_GPUREADONLY))
+		protflags |= GSL_PT_PAGE_WV;
+
+	return _kgsl_sharedmem_page_alloc(memdesc, pagetable, size,
+		protflags);
+}
+EXPORT_SYMBOL(kgsl_sharedmem_page_alloc_user);
+
+int
+kgsl_sharedmem_alloc_coherent(struct kgsl_memdesc *memdesc, size_t size)
+{
+	int result = 0;
+
+	size = ALIGN(size, PAGE_SIZE);
+
+	memdesc->size = size;
+	memdesc->ops = &kgsl_coherent_ops;
+
+	memdesc->hostptr = dma_alloc_coherent(NULL, size, &memdesc->physaddr,
+					      GFP_KERNEL);
+	if (memdesc->hostptr == NULL) {
+		KGSL_CORE_ERR("dma_alloc_coherent(%d) failed\n", size);
+		result = -ENOMEM;
+		goto err;
+	}
+
+	result = memdesc_sg_phys(memdesc, memdesc->physaddr, size);
+	if (result)
+		goto err;
+
+	/* Record statistics */
+
+	KGSL_STATS_ADD(size, kgsl_driver.stats.coherent,
+		       kgsl_driver.stats.coherent_max);
+
+err:
+	if (result)
+		kgsl_sharedmem_free(memdesc);
+
+	return result;
+}
+EXPORT_SYMBOL(kgsl_sharedmem_alloc_coherent);
+
+void kgsl_sharedmem_free(struct kgsl_memdesc *memdesc)
+{
+	if (memdesc == NULL || memdesc->size == 0)
+		return;
+
+	if (memdesc->gpuaddr)
+		kgsl_mmu_unmap(memdesc->pagetable, memdesc);
+
+	if (memdesc->ops && memdesc->ops->free)
+		memdesc->ops->free(memdesc);
+
+	kgsl_sg_free(memdesc->sg, memdesc->sglen);
+
+	memset(memdesc, 0, sizeof(*memdesc));
+}
+EXPORT_SYMBOL(kgsl_sharedmem_free);
+
+static int
+_kgsl_sharedmem_ebimem(struct kgsl_memdesc *memdesc,
+			struct kgsl_pagetable *pagetable, size_t size)
+{
+	int result = 0;
+
+	memdesc->size = size;
+	memdesc->pagetable = pagetable;
+	memdesc->ops = &kgsl_ebimem_ops;
+	memdesc->physaddr = allocate_contiguous_ebi_nomap(size, SZ_8K);
+
+	if (memdesc->physaddr == 0) {
+		KGSL_CORE_ERR("allocate_contiguous_ebi_nomap(%d) failed\n",
+			size);
+		return -ENOMEM;
+	}
+
+	result = memdesc_sg_phys(memdesc, memdesc->physaddr, size);
+
+	if (result)
+		goto err;
+
+	result = kgsl_mmu_map(pagetable, memdesc,
+		GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
+
+	if (result)
+		goto err;
+
+	KGSL_STATS_ADD(size, kgsl_driver.stats.coherent,
+		kgsl_driver.stats.coherent_max);
+
+err:
+	if (result)
+		kgsl_sharedmem_free(memdesc);
+
+	return result;
+}
+
+int
+kgsl_sharedmem_ebimem_user(struct kgsl_memdesc *memdesc,
+			struct kgsl_pagetable *pagetable,
+			size_t size, int flags)
+{
+	size = ALIGN(size, PAGE_SIZE);
+	return _kgsl_sharedmem_ebimem(memdesc, pagetable, size);
+}
+EXPORT_SYMBOL(kgsl_sharedmem_ebimem_user);
+
+int
+kgsl_sharedmem_ebimem(struct kgsl_memdesc *memdesc,
+		struct kgsl_pagetable *pagetable, size_t size)
+{
+	int result;
+	size = ALIGN(size, 8192);
+	result = _kgsl_sharedmem_ebimem(memdesc, pagetable, size);
+
+	if (result)
+		return result;
+
+	memdesc->hostptr = ioremap(memdesc->physaddr, size);
+
+	if (memdesc->hostptr == NULL) {
+		KGSL_CORE_ERR("ioremap failed\n");
+		kgsl_sharedmem_free(memdesc);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(kgsl_sharedmem_ebimem);
+
+int
+kgsl_sharedmem_readl(const struct kgsl_memdesc *memdesc,
+			uint32_t *dst,
+			unsigned int offsetbytes)
+{
+	uint32_t *src;
+	BUG_ON(memdesc == NULL || memdesc->hostptr == NULL || dst == NULL);
+	WARN_ON(offsetbytes % sizeof(uint32_t) != 0);
+	if (offsetbytes % sizeof(uint32_t) != 0)
+		return -EINVAL;
+
+	WARN_ON(offsetbytes + sizeof(uint32_t) > memdesc->size);
+	if (offsetbytes + sizeof(uint32_t) > memdesc->size)
+		return -ERANGE;
+	src = (uint32_t *)(memdesc->hostptr + offsetbytes);
+	*dst = *src;
+	return 0;
+}
+EXPORT_SYMBOL(kgsl_sharedmem_readl);
+
+int
+kgsl_sharedmem_writel(const struct kgsl_memdesc *memdesc,
+			unsigned int offsetbytes,
+			uint32_t src)
+{
+	uint32_t *dst;
+	BUG_ON(memdesc == NULL || memdesc->hostptr == NULL);
+	WARN_ON(offsetbytes % sizeof(uint32_t) != 0);
+	if (offsetbytes % sizeof(uint32_t) != 0)
+		return -EINVAL;
+
+	WARN_ON(offsetbytes + sizeof(uint32_t) > memdesc->size);
+	if (offsetbytes + sizeof(uint32_t) > memdesc->size)
+		return -ERANGE;
+	kgsl_cffdump_setmem(memdesc->gpuaddr + offsetbytes,
+		src, sizeof(uint32_t));
+	dst = (uint32_t *)(memdesc->hostptr + offsetbytes);
+	*dst = src;
+	return 0;
+}
+EXPORT_SYMBOL(kgsl_sharedmem_writel);
+
+int
+kgsl_sharedmem_set(const struct kgsl_memdesc *memdesc, unsigned int offsetbytes,
+			unsigned int value, unsigned int sizebytes)
+{
+	BUG_ON(memdesc == NULL || memdesc->hostptr == NULL);
+	BUG_ON(offsetbytes + sizebytes > memdesc->size);
+
+	kgsl_cffdump_setmem(memdesc->gpuaddr + offsetbytes, value,
+			    sizebytes);
+	memset(memdesc->hostptr + offsetbytes, value, sizebytes);
+	return 0;
+}
+EXPORT_SYMBOL(kgsl_sharedmem_set);
+
+/*
+ * kgsl_sharedmem_map_vma - Map a user vma to physical memory
+ *
+ * @vma - The user vma to map
+ * @memdesc - The memory descriptor which contains information about the
+ * physical memory
+ *
+ * Return: 0 on success else error code
+ */
+int
+kgsl_sharedmem_map_vma(struct vm_area_struct *vma,
+			const struct kgsl_memdesc *memdesc)
+{
+	unsigned long addr = vma->vm_start;
+	unsigned long size = vma->vm_end - vma->vm_start;
+	int ret, i = 0;
+
+	if (!memdesc->sg || (size != memdesc->size) ||
+		(memdesc->sglen != (size / PAGE_SIZE)))
+		return -EINVAL;
+
+	for (; addr < vma->vm_end; addr += PAGE_SIZE, i++) {
+		ret = vm_insert_page(vma, addr, sg_page(&memdesc->sg[i]));
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+EXPORT_SYMBOL(kgsl_sharedmem_map_vma);
diff --git a/drivers/gpu/msm/kgsl_sharedmem.h b/drivers/gpu/msm/kgsl_sharedmem.h
new file mode 100644
index 0000000..034ade4
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_sharedmem.h
@@ -0,0 +1,171 @@
+/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __KGSL_SHAREDMEM_H
+#define __KGSL_SHAREDMEM_H
+
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
+#include "kgsl_mmu.h"
+#include <linux/slab.h>
+#include <linux/kmemleak.h>
+
+struct kgsl_device;
+struct kgsl_process_private;
+
+#define KGSL_CACHE_OP_INV       0x01
+#define KGSL_CACHE_OP_FLUSH     0x02
+#define KGSL_CACHE_OP_CLEAN     0x03
+
+/** Set if the memdesc describes cached memory */
+#define KGSL_MEMFLAGS_CACHED    0x00000001
+/** Set if the memdesc is mapped into all pagetables */
+#define KGSL_MEMFLAGS_GLOBAL    0x00000002
+
+extern struct kgsl_memdesc_ops kgsl_page_alloc_ops;
+
+int kgsl_sharedmem_page_alloc(struct kgsl_memdesc *memdesc,
+			   struct kgsl_pagetable *pagetable, size_t size);
+
+int kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
+				struct kgsl_pagetable *pagetable,
+				size_t size, int flags);
+
+int kgsl_sharedmem_alloc_coherent(struct kgsl_memdesc *memdesc, size_t size);
+
+int kgsl_sharedmem_ebimem_user(struct kgsl_memdesc *memdesc,
+			     struct kgsl_pagetable *pagetable,
+			     size_t size, int flags);
+
+int kgsl_sharedmem_ebimem(struct kgsl_memdesc *memdesc,
+			struct kgsl_pagetable *pagetable,
+			size_t size);
+
+void kgsl_sharedmem_free(struct kgsl_memdesc *memdesc);
+
+int kgsl_sharedmem_readl(const struct kgsl_memdesc *memdesc,
+			uint32_t *dst,
+			unsigned int offsetbytes);
+
+int kgsl_sharedmem_writel(const struct kgsl_memdesc *memdesc,
+			unsigned int offsetbytes,
+			uint32_t src);
+
+int kgsl_sharedmem_set(const struct kgsl_memdesc *memdesc,
+			unsigned int offsetbytes, unsigned int value,
+			unsigned int sizebytes);
+
+void kgsl_cache_range_op(struct kgsl_memdesc *memdesc, int op);
+
+void kgsl_process_init_sysfs(struct kgsl_process_private *private);
+void kgsl_process_uninit_sysfs(struct kgsl_process_private *private);
+
+int kgsl_sharedmem_init_sysfs(void);
+void kgsl_sharedmem_uninit_sysfs(void);
+
+static inline unsigned int kgsl_get_sg_pa(struct scatterlist *sg)
+{
+	/*
+	 * Try sg_dma_address first to support ion carveout
+	 * regions which do not work with sg_phys().
+	 */
+	unsigned int pa = sg_dma_address(sg);
+	if (pa == 0)
+		pa = sg_phys(sg);
+	return pa;
+}
+
+int
+kgsl_sharedmem_map_vma(struct vm_area_struct *vma,
+			const struct kgsl_memdesc *memdesc);
+
+/*
+ * For relatively small sglists, it is preferable to use kzalloc
+ * rather than going down the vmalloc rat hole.  If the size of
+ * the sglist is < PAGE_SIZE use kzalloc otherwise fallback to
+ * vmalloc
+ */
+
+static inline void *kgsl_sg_alloc(unsigned int sglen)
+{
+	if ((sglen * sizeof(struct scatterlist)) <  PAGE_SIZE)
+		return kzalloc(sglen * sizeof(struct scatterlist), GFP_KERNEL);
+	else
+		return vmalloc(sglen * sizeof(struct scatterlist));
+}
+
+static inline void kgsl_sg_free(void *ptr, unsigned int sglen)
+{
+	if ((sglen * sizeof(struct scatterlist)) < PAGE_SIZE)
+		kfree(ptr);
+	else
+		vfree(ptr);
+}
+
+static inline int
+memdesc_sg_phys(struct kgsl_memdesc *memdesc,
+		unsigned int physaddr, unsigned int size)
+{
+	memdesc->sg = kgsl_sg_alloc(1);
+
+	kmemleak_not_leak(memdesc->sg);
+
+	memdesc->sglen = 1;
+	sg_init_table(memdesc->sg, 1);
+	memdesc->sg[0].length = size;
+	memdesc->sg[0].offset = 0;
+	memdesc->sg[0].dma_address = physaddr;
+	return 0;
+}
+
+static inline int
+kgsl_allocate(struct kgsl_memdesc *memdesc,
+		struct kgsl_pagetable *pagetable, size_t size)
+{
+	if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_NONE)
+		return kgsl_sharedmem_ebimem(memdesc, pagetable, size);
+	return kgsl_sharedmem_page_alloc(memdesc, pagetable, size);
+}
+
+static inline int
+kgsl_allocate_user(struct kgsl_memdesc *memdesc,
+		struct kgsl_pagetable *pagetable,
+		size_t size, unsigned int flags)
+{
+	if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_NONE)
+		return kgsl_sharedmem_ebimem_user(memdesc, pagetable, size,
+						  flags);
+	return kgsl_sharedmem_page_alloc_user(memdesc, pagetable, size, flags);
+}
+
+static inline int
+kgsl_allocate_contiguous(struct kgsl_memdesc *memdesc, size_t size)
+{
+	int ret  = kgsl_sharedmem_alloc_coherent(memdesc, size);
+	if (!ret && (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_NONE))
+		memdesc->gpuaddr = memdesc->physaddr;
+	return ret;
+}
+
+static inline int kgsl_sg_size(struct scatterlist *sg, int sglen)
+{
+	int i, size = 0;
+	struct scatterlist *s;
+
+	for_each_sg(sg, s, sglen, i) {
+		size += s->length;
+	}
+
+	return size;
+}
+#endif /* __KGSL_SHAREDMEM_H */
diff --git a/drivers/gpu/msm/kgsl_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c
new file mode 100644
index 0000000..8935b29
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_snapshot.c
@@ -0,0 +1,774 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/export.h>
+#include <linux/time.h>
+#include <linux/sysfs.h>
+#include <linux/utsname.h>
+#include <linux/sched.h>
+#include <linux/idr.h>
+
+#include "kgsl.h"
+#include "kgsl_log.h"
+#include "kgsl_device.h"
+#include "kgsl_sharedmem.h"
+#include "kgsl_snapshot.h"
+
+/* Placeholder for the list of memory objects frozen after a hang */
+
+struct kgsl_snapshot_object {
+	unsigned int gpuaddr;
+	unsigned int ptbase;
+	unsigned int size;
+	unsigned int offset;
+	int type;
+	struct kgsl_mem_entry *entry;
+	struct list_head node;
+};
+
+/* idr_for_each function to count the number of contexts */
+
+static int snapshot_context_count(int id, void *ptr, void *data)
+{
+	int *count = data;
+	*count = *count + 1;
+
+	return 0;
+}
+
+/*
+ * To simplify the iterator loop use a global pointer instead of trying
+ * to pass around double star references to the snapshot data
+ */
+
+static void *_ctxtptr;
+
+static int snapshot_context_info(int id, void *ptr, void *data)
+{
+	struct kgsl_snapshot_linux_context *header = _ctxtptr;
+	struct kgsl_context *context = ptr;
+	struct kgsl_device *device = context->dev_priv->device;
+
+	header->id = id;
+
+	/* Future-proof for per-context timestamps - for now, just
+	 * return the global timestamp for all contexts
+	 */
+
+	header->timestamp_queued = kgsl_readtimestamp(device, context,
+						      KGSL_TIMESTAMP_QUEUED);
+	header->timestamp_retired = kgsl_readtimestamp(device, context,
+						       KGSL_TIMESTAMP_RETIRED);
+
+	_ctxtptr += sizeof(struct kgsl_snapshot_linux_context);
+
+	return 0;
+}
+
+/* Snapshot the Linux specific information */
+static int snapshot_os(struct kgsl_device *device,
+	void *snapshot, int remain, void *priv)
+{
+	struct kgsl_snapshot_linux *header = snapshot;
+	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+	struct task_struct *task;
+	pid_t pid;
+	int hang = (int) priv;
+	int ctxtcount = 0;
+	int size = sizeof(*header);
+
+	/* Figure out how many active contexts there are - these will
+	 * be appended on the end of the structure */
+
+	idr_for_each(&device->context_idr, snapshot_context_count, &ctxtcount);
+
+	size += ctxtcount * sizeof(struct kgsl_snapshot_linux_context);
+
+	/* Make sure there is enough room for the data */
+	if (remain < size) {
+		SNAPSHOT_ERR_NOMEM(device, "OS");
+		return 0;
+	}
+
+	memset(header, 0, sizeof(*header));
+
+	header->osid = KGSL_SNAPSHOT_OS_LINUX;
+
+	header->state = hang ? SNAPSHOT_STATE_HUNG : SNAPSHOT_STATE_RUNNING;
+
+	/* Get the kernel build information */
+	strlcpy(header->release, utsname()->release, sizeof(header->release));
+	strlcpy(header->version, utsname()->version, sizeof(header->version));
+
+	/* Get the Unix time for the timestamp */
+	header->seconds = get_seconds();
+
+	/* Remember the power information */
+	header->power_flags = pwr->power_flags;
+	header->power_level = pwr->active_pwrlevel;
+	header->power_interval_timeout = pwr->interval_timeout;
+	header->grpclk = kgsl_get_clkrate(pwr->grp_clks[0]);
+	header->busclk = kgsl_get_clkrate(pwr->ebi1_clk);
+
+	/* Future proof for per-context timestamps */
+	header->current_context = -1;
+
+	/* Get the current PT base */
+	header->ptbase = kgsl_mmu_get_current_ptbase(&device->mmu);
+	/* And the PID for the task leader */
+	pid = header->pid = kgsl_mmu_get_ptname_from_ptbase(header->ptbase);
+
+	task = find_task_by_vpid(pid);
+
+	if (task)
+		get_task_comm(header->comm, task);
+
+	header->ctxtcount = ctxtcount;
+
+	/* append information for each context */
+	_ctxtptr = snapshot + sizeof(*header);
+	idr_for_each(&device->context_idr, snapshot_context_info, NULL);
+
+	/* Return the size of the data segment */
+	return size;
+}
+/*
+ * kgsl_snapshot_dump_indexed_regs - helper function to dump indexed registers
+ * @device - the device to dump registers from
+ * @snapshot - pointer to the start of the region of memory for the snapshot
+ * @remain - a pointer to the number of bytes remaining in the snapshot
+ * @priv - A pointer to the kgsl_snapshot_indexed_registers data
+ *
+ * Given a indexed register cmd/data pair and a count, dump each indexed
+ * register
+ */
+
+static int kgsl_snapshot_dump_indexed_regs(struct kgsl_device *device,
+	void *snapshot, int remain, void *priv)
+{
+	struct kgsl_snapshot_indexed_registers *iregs = priv;
+	struct kgsl_snapshot_indexed_regs *header = snapshot;
+	unsigned int *data = snapshot + sizeof(*header);
+	int i;
+
+	if (remain < (iregs->count * 4) + sizeof(*header)) {
+		SNAPSHOT_ERR_NOMEM(device, "INDEXED REGS");
+		return 0;
+	}
+
+	header->index_reg = iregs->index;
+	header->data_reg = iregs->data;
+	header->count = iregs->count;
+	header->start = iregs->start;
+
+	for (i = 0; i < iregs->count; i++) {
+		kgsl_regwrite(device, iregs->index, iregs->start + i);
+		kgsl_regread(device, iregs->data, &data[i]);
+	}
+
+	return (iregs->count * 4) + sizeof(*header);
+}
+
+#define GPU_OBJ_HEADER_SZ \
+	(sizeof(struct kgsl_snapshot_section_header) + \
+	 sizeof(struct kgsl_snapshot_gpu_object))
+
+#define GPU_OBJ_SECTION_SIZE(_o) \
+	(GPU_OBJ_HEADER_SZ + ((_o)->size))
+
+static int kgsl_snapshot_dump_object(struct kgsl_device *device,
+		struct kgsl_snapshot_object *obj, void *buf,
+		unsigned int off, unsigned int count)
+{
+	unsigned char headers[GPU_OBJ_HEADER_SZ];
+	struct kgsl_snapshot_section_header *sect =
+		(struct kgsl_snapshot_section_header *) headers;
+	struct kgsl_snapshot_gpu_object *header =
+		(struct kgsl_snapshot_gpu_object *) (headers + sizeof(*sect));
+	int ret = 0;
+
+	/* Construct a local copy of the headers */
+
+	sect->magic = SNAPSHOT_SECTION_MAGIC;
+	sect->id = KGSL_SNAPSHOT_SECTION_GPU_OBJECT;
+	sect->size = GPU_OBJ_SECTION_SIZE(obj);
+
+	header->type = obj->type;
+
+	/* Header size is in dwords, object size is in bytes */
+	header->size = obj->size >> 2;
+	header->gpuaddr = obj->gpuaddr;
+	header->ptbase = obj->ptbase;
+
+	/* Copy out any part of the header block that is needed */
+
+	if (off < GPU_OBJ_HEADER_SZ) {
+		int size = count < GPU_OBJ_HEADER_SZ - off ?
+			count : GPU_OBJ_HEADER_SZ - off;
+
+		memcpy(buf, headers + off, size);
+
+		count -= size;
+		ret += size;
+	}
+
+	/* Now copy whatever part of the data is needed */
+
+	if (off < (GPU_OBJ_HEADER_SZ + obj->size)) {
+		int offset;
+		int size = count < obj->size ? count : obj->size;
+
+		/*
+		 * If the desired gpuaddr isn't at the beginning of the region,
+		 * then offset the source pointer
+		 */
+
+		offset = obj->offset;
+
+		/*
+		 * Then  adjust it to account for the offset for the output
+		 * buffer.
+		 */
+
+		if (off > GPU_OBJ_HEADER_SZ) {
+			int loff = (off - GPU_OBJ_HEADER_SZ);
+
+			/* Adjust the size so we don't walk off the end */
+
+			if ((loff + size) > obj->size)
+				size = obj->size - loff;
+
+			offset += loff;
+		}
+
+		memcpy(buf + ret, obj->entry->memdesc.hostptr + offset, size);
+		ret += size;
+	}
+
+	return ret;
+}
+
+static void kgsl_snapshot_put_object(struct kgsl_device *device,
+	struct kgsl_snapshot_object *obj)
+{
+	list_del(&obj->node);
+
+	obj->entry->flags &= ~KGSL_MEM_ENTRY_FROZEN;
+	kgsl_mem_entry_put(obj->entry);
+
+	kfree(obj);
+}
+
+/* kgsl_snapshot_get_object - Mark a GPU buffer to be frozen
+ * @device - the device that is being snapshotted
+ * @ptbase - the pagetable base of the object to freeze
+ * @gpuaddr - The gpu address of the object to freeze
+ * @size - the size of the object (may not always be the size of the region)
+ * @type - the type of object being saved (shader, vbo, etc)
+ *
+ * Mark and freeze a GPU buffer object.  This will prevent it from being
+ * freed until it can be copied out as part of the snapshot dump.  Returns the
+ * size of the object being frozen
+ */
+
+int kgsl_snapshot_get_object(struct kgsl_device *device, unsigned int ptbase,
+	unsigned int gpuaddr, unsigned int size, unsigned int type)
+{
+	struct kgsl_mem_entry *entry;
+	struct kgsl_snapshot_object *obj;
+	int offset;
+
+	entry = kgsl_get_mem_entry(ptbase, gpuaddr, size);
+
+	if (entry == NULL) {
+		KGSL_DRV_ERR(device, "Unable to find GPU buffer %8.8X\n",
+				gpuaddr);
+		return 0;
+	}
+
+	/* We can't freeze external memory, because we don't own it */
+	if (entry->memtype != KGSL_MEM_ENTRY_KERNEL) {
+		KGSL_DRV_ERR(device,
+			"Only internal GPU buffers can be frozen\n");
+		return 0;
+	}
+
+	/*
+	 * size indicates the number of bytes in the region to save. This might
+	 * not always be the entire size of the region because some buffers are
+	 * sub-allocated from a larger region.  However, if size 0 was passed
+	 * thats a flag that the caller wants to capture the entire buffer
+	 */
+
+	if (size == 0) {
+		size = entry->memdesc.size;
+		offset = 0;
+
+		/* Adjust the gpuaddr to the start of the object */
+		gpuaddr = entry->memdesc.gpuaddr;
+	} else {
+		offset = gpuaddr - entry->memdesc.gpuaddr;
+	}
+
+	if (size + offset > entry->memdesc.size) {
+		KGSL_DRV_ERR(device, "Invalid size for GPU buffer %8.8X\n",
+				gpuaddr);
+		return 0;
+	}
+
+	/* If the buffer is already on the list, skip it */
+	list_for_each_entry(obj, &device->snapshot_obj_list, node) {
+		if (obj->gpuaddr == gpuaddr && obj->ptbase == ptbase) {
+			/* If the size is different, use the new size */
+			if (obj->size != size)
+				obj->size = size;
+
+			return 0;
+		}
+	}
+
+	if (kgsl_memdesc_map(&entry->memdesc) == NULL) {
+		KGSL_DRV_ERR(device, "Unable to map GPU buffer %X\n",
+				gpuaddr);
+		return 0;
+	}
+
+	obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+
+	if (obj == NULL) {
+		KGSL_DRV_ERR(device, "Unable to allocate memory\n");
+		return 0;
+	}
+
+	/* Ref count the mem entry */
+	kgsl_mem_entry_get(entry);
+
+	obj->type = type;
+	obj->entry = entry;
+	obj->gpuaddr = gpuaddr;
+	obj->ptbase = ptbase;
+	obj->size = size;
+	obj->offset = offset;
+
+	list_add(&obj->node, &device->snapshot_obj_list);
+
+	/*
+	 * Return the size of the entire mem entry that was frozen - this gets
+	 * used for tracking how much memory is frozen for a hang.  Also, mark
+	 * the memory entry as frozen. If the entry was already marked as
+	 * frozen, then another buffer already got to it.  In that case, return
+	 * 0 so it doesn't get counted twice
+	 */
+
+	if (entry->flags & KGSL_MEM_ENTRY_FROZEN)
+		return 0;
+
+	entry->flags |= KGSL_MEM_ENTRY_FROZEN;
+
+	return entry->memdesc.size;
+}
+EXPORT_SYMBOL(kgsl_snapshot_get_object);
+
+/*
+ * kgsl_snapshot_dump_regs - helper function to dump device registers
+ * @device - the device to dump registers from
+ * @snapshot - pointer to the start of the region of memory for the snapshot
+ * @remain - a pointer to the number of bytes remaining in the snapshot
+ * @priv - A pointer to the kgsl_snapshot_registers data
+ *
+ * Given an array of register ranges pairs (start,end [inclusive]), dump the
+ * registers into a snapshot register section.  The snapshot region stores a
+ * part of dwords for each register - the word address of the register, and
+ * the value.
+ */
+int kgsl_snapshot_dump_regs(struct kgsl_device *device, void *snapshot,
+	int remain, void *priv)
+{
+	struct kgsl_snapshot_regs *header = snapshot;
+	struct kgsl_snapshot_registers *regs = priv;
+	unsigned int *data = snapshot + sizeof(*header);
+	int count = 0, i, j;
+
+	/* Figure out how many registers we are going to dump */
+
+	for (i = 0; i < regs->count; i++) {
+		int start = regs->regs[i * 2];
+		int end = regs->regs[i * 2 + 1];
+
+		count += (end - start + 1);
+	}
+
+	if (remain < (count * 8) + sizeof(*header)) {
+		SNAPSHOT_ERR_NOMEM(device, "REGISTERS");
+		return 0;
+	}
+
+	for (i = 0; i < regs->count; i++) {
+		unsigned int start = regs->regs[i * 2];
+		unsigned int end = regs->regs[i * 2 + 1];
+
+		for (j = start; j <= end; j++) {
+			unsigned int val;
+
+			kgsl_regread(device, j, &val);
+			*data++ = j;
+			*data++ = val;
+		}
+	}
+
+	header->count = count;
+
+	/* Return the size of the section */
+	return (count * 8) + sizeof(*header);
+}
+EXPORT_SYMBOL(kgsl_snapshot_dump_regs);
+
+void *kgsl_snapshot_indexed_registers(struct kgsl_device *device,
+		void *snapshot, int *remain,
+		unsigned int index, unsigned int data, unsigned int start,
+		unsigned int count)
+{
+	struct kgsl_snapshot_indexed_registers iregs;
+	iregs.index = index;
+	iregs.data = data;
+	iregs.start = start;
+	iregs.count = count;
+
+	return kgsl_snapshot_add_section(device,
+		 KGSL_SNAPSHOT_SECTION_INDEXED_REGS, snapshot,
+		 remain, kgsl_snapshot_dump_indexed_regs, &iregs);
+}
+EXPORT_SYMBOL(kgsl_snapshot_indexed_registers);
+
+/*
+ * kgsl_snapshot - construct a device snapshot
+ * @device - device to snapshot
+ * @hang - set to 1 if the snapshot was triggered following a hnag
+ * Given a device, construct a binary snapshot dump of the current device state
+ * and store it in the device snapshot memory.
+ */
+int kgsl_device_snapshot(struct kgsl_device *device, int hang)
+{
+	struct kgsl_snapshot_header *header = device->snapshot;
+	int remain = device->snapshot_maxsize - sizeof(*header);
+	void *snapshot;
+
+	/*
+	 * The first hang is always the one we are interested in. To
+	 * avoid a subsequent hang blowing away the first, the snapshot
+	 * is frozen until it is dumped via sysfs.
+	 *
+	 * Note that triggered snapshots are always taken regardless
+	 * of the state and never frozen.
+	 */
+
+	if (hang && device->snapshot_frozen == 1)
+		return 0;
+
+	if (device->snapshot == NULL) {
+		KGSL_DRV_ERR(device,
+			"snapshot: No snapshot memory available\n");
+		return -ENOMEM;
+	}
+
+	if (remain < sizeof(*header)) {
+		KGSL_DRV_ERR(device,
+			"snapshot: Not enough memory for the header\n");
+		return -ENOMEM;
+	}
+
+	header->magic = SNAPSHOT_MAGIC;
+
+	header->gpuid = kgsl_gpuid(device);
+
+	/* Get a pointer to the first section (right after the header) */
+	snapshot = ((void *) device->snapshot) + sizeof(*header);
+
+	/* Build the Linux specific header */
+	snapshot = kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_OS,
+		snapshot, &remain, snapshot_os, (void *) hang);
+
+	/* Get the device specific sections */
+	if (device->ftbl->snapshot)
+		snapshot = device->ftbl->snapshot(device, snapshot, &remain,
+			hang);
+
+	device->snapshot_timestamp = get_seconds();
+	device->snapshot_size = (int) (snapshot - device->snapshot);
+
+	/* Freeze the snapshot on a hang until it gets read */
+	device->snapshot_frozen = (hang) ? 1 : 0;
+
+	/* log buffer info to aid in ramdump recovery */
+	KGSL_DRV_ERR(device, "snapshot created at va %p pa %lx size %d\n",
+			device->snapshot, __pa(device->snapshot),
+			device->snapshot_size);
+	if (hang)
+		sysfs_notify(&device->snapshot_kobj, NULL, "timestamp");
+	return 0;
+}
+EXPORT_SYMBOL(kgsl_device_snapshot);
+
+/* An attribute for showing snapshot details */
+struct kgsl_snapshot_attribute {
+	struct attribute attr;
+	ssize_t (*show)(struct kgsl_device *device, char *buf);
+	ssize_t (*store)(struct kgsl_device *device, const char *buf,
+		size_t count);
+};
+
+#define to_snapshot_attr(a) \
+container_of(a, struct kgsl_snapshot_attribute, attr)
+
+#define kobj_to_device(a) \
+container_of(a, struct kgsl_device, snapshot_kobj)
+
+/* Dump the sysfs binary data to the user */
+static ssize_t snapshot_show(struct file *filep, struct kobject *kobj,
+	struct bin_attribute *attr, char *buf, loff_t off,
+	size_t count)
+{
+	struct kgsl_device *device = kobj_to_device(kobj);
+	struct kgsl_snapshot_object *obj, *tmp;
+	unsigned int size, src, dst = 0;
+
+	if (device == NULL)
+		return 0;
+
+	/* Return nothing if we haven't taken a snapshot yet */
+	if (device->snapshot_timestamp == 0)
+		return 0;
+
+	/* Get the mutex to keep things from changing while we are dumping */
+	mutex_lock(&device->mutex);
+
+	if (off < device->snapshot_size) {
+		size = count < (device->snapshot_size - off) ?
+			count : device->snapshot_size - off;
+
+		memcpy(buf, device->snapshot + off, size);
+
+		count -= size;
+		dst += size;
+	}
+
+	if (count == 0)
+		goto done;
+
+	src = device->snapshot_size;
+
+	list_for_each_entry(obj, &device->snapshot_obj_list, node) {
+
+		int objsize = GPU_OBJ_SECTION_SIZE(obj);
+		int offset;
+
+		/* If the offset is beyond this object, then move on */
+
+		if (off >= (src + objsize)) {
+			src += objsize;
+			continue;
+		}
+
+		/* Adjust the offset to be relative to the object */
+		offset = (off >= src) ? (off - src) : 0;
+
+		size = kgsl_snapshot_dump_object(device, obj, buf + dst,
+			offset, count);
+
+		count -= size;
+		dst += size;
+
+		if (count == 0)
+			goto done;
+
+		/* Move on to the next object - update src accordingly */
+		src += objsize;
+	}
+
+	/* Add the end section */
+
+	if (off < (src + sizeof(struct kgsl_snapshot_section_header))) {
+		if (count >= sizeof(struct kgsl_snapshot_section_header)) {
+			struct kgsl_snapshot_section_header *head =
+				(void *) (buf + dst);
+
+			head->magic = SNAPSHOT_SECTION_MAGIC;
+			head->id = KGSL_SNAPSHOT_SECTION_END;
+			head->size = sizeof(*head);
+
+			dst += sizeof(*head);
+		} else {
+			goto done;
+		}
+	}
+
+	/* Release the buffers and unfreeze the snapshot */
+
+	list_for_each_entry_safe(obj, tmp, &device->snapshot_obj_list, node)
+		kgsl_snapshot_put_object(device, obj);
+
+	if (device->snapshot_frozen)
+		KGSL_DRV_ERR(device, "Snapshot objects released\n");
+
+	device->snapshot_frozen = 0;
+
+done:
+	mutex_unlock(&device->mutex);
+
+	return dst;
+}
+
+/* Show the timestamp of the last collected snapshot */
+static ssize_t timestamp_show(struct kgsl_device *device, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%x\n", device->snapshot_timestamp);
+}
+
+/* manually trigger a new snapshot to be collected */
+static ssize_t trigger_store(struct kgsl_device *device, const char *buf,
+	size_t count)
+{
+	if (device && count > 0) {
+		mutex_lock(&device->mutex);
+		kgsl_device_snapshot(device, 0);
+		mutex_unlock(&device->mutex);
+	}
+
+	return count;
+}
+
+static struct bin_attribute snapshot_attr = {
+	.attr.name = "dump",
+	.attr.mode = 0444,
+	.size = 0,
+	.read = snapshot_show
+};
+
+#define SNAPSHOT_ATTR(_name, _mode, _show, _store) \
+struct kgsl_snapshot_attribute attr_##_name = { \
+	.attr = { .name = __stringify(_name), .mode = _mode }, \
+	.show = _show, \
+	.store = _store, \
+}
+
+SNAPSHOT_ATTR(trigger, 0600, NULL, trigger_store);
+SNAPSHOT_ATTR(timestamp, 0444, timestamp_show, NULL);
+
+static void snapshot_sysfs_release(struct kobject *kobj)
+{
+}
+
+static ssize_t snapshot_sysfs_show(struct kobject *kobj,
+	struct attribute *attr, char *buf)
+{
+	struct kgsl_snapshot_attribute *pattr = to_snapshot_attr(attr);
+	struct kgsl_device *device = kobj_to_device(kobj);
+	ssize_t ret;
+
+	if (device && pattr->show)
+		ret = pattr->show(device, buf);
+	else
+		ret = -EIO;
+
+	return ret;
+}
+
+static ssize_t snapshot_sysfs_store(struct kobject *kobj,
+	struct attribute *attr, const char *buf, size_t count)
+{
+	struct kgsl_snapshot_attribute *pattr = to_snapshot_attr(attr);
+	struct kgsl_device *device = kobj_to_device(kobj);
+	ssize_t ret;
+
+	if (device && pattr->store)
+		ret = pattr->store(device, buf, count);
+	else
+		ret = -EIO;
+
+	return ret;
+}
+
+static const struct sysfs_ops snapshot_sysfs_ops = {
+	.show = snapshot_sysfs_show,
+	.store = snapshot_sysfs_store,
+};
+
+static struct kobj_type ktype_snapshot = {
+	.sysfs_ops = &snapshot_sysfs_ops,
+	.default_attrs = NULL,
+	.release = snapshot_sysfs_release,
+};
+
+/* kgsl_device_snapshot_init - Add resources for the device GPU snapshot
+ * @device - The device to initalize
+ *
+ * Allocate memory for a GPU snapshot for the specified device,
+ * and create the sysfs files to manage it
+ */
+
+int kgsl_device_snapshot_init(struct kgsl_device *device)
+{
+	int ret;
+
+	if (device->snapshot == NULL)
+		device->snapshot = kzalloc(KGSL_SNAPSHOT_MEMSIZE, GFP_KERNEL);
+
+	if (device->snapshot == NULL)
+		return -ENOMEM;
+
+	device->snapshot_maxsize = KGSL_SNAPSHOT_MEMSIZE;
+	device->snapshot_timestamp = 0;
+
+	INIT_LIST_HEAD(&device->snapshot_obj_list);
+
+	ret = kobject_init_and_add(&device->snapshot_kobj, &ktype_snapshot,
+		&device->dev->kobj, "snapshot");
+	if (ret)
+		goto done;
+
+	ret = sysfs_create_bin_file(&device->snapshot_kobj, &snapshot_attr);
+	if (ret)
+		goto done;
+
+	ret  = sysfs_create_file(&device->snapshot_kobj, &attr_trigger.attr);
+	if (ret)
+		goto done;
+
+	ret  = sysfs_create_file(&device->snapshot_kobj, &attr_timestamp.attr);
+
+done:
+	return ret;
+}
+EXPORT_SYMBOL(kgsl_device_snapshot_init);
+
+/* kgsl_device_snapshot_close - Take down snapshot memory for a device
+ * @device - Pointer to the kgsl_device
+ *
+ * Remove the sysfs files and free the memory allocated for the GPU
+ * snapshot
+ */
+
+void kgsl_device_snapshot_close(struct kgsl_device *device)
+{
+	sysfs_remove_bin_file(&device->snapshot_kobj, &snapshot_attr);
+	sysfs_remove_file(&device->snapshot_kobj, &attr_trigger.attr);
+	sysfs_remove_file(&device->snapshot_kobj, &attr_timestamp.attr);
+
+	kobject_put(&device->snapshot_kobj);
+
+	kfree(device->snapshot);
+
+	device->snapshot = NULL;
+	device->snapshot_maxsize = 0;
+	device->snapshot_timestamp = 0;
+}
+EXPORT_SYMBOL(kgsl_device_snapshot_close);
diff --git a/drivers/gpu/msm/kgsl_snapshot.h b/drivers/gpu/msm/kgsl_snapshot.h
new file mode 100644
index 0000000..304f4bb
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_snapshot.h
@@ -0,0 +1,293 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _KGSL_SNAPSHOT_H_
+#define _KGSL_SNAPSHOT_H_
+
+#include <linux/types.h>
+
+/* Snapshot header */
+
+#define SNAPSHOT_MAGIC 0x504D0001
+
+/* GPU ID scheme:
+ * [16:31] - core identifer (0x0002 for 2D or 0x0003 for 3D)
+ * [00:16] - GPU specific identifier
+ */
+
+struct kgsl_snapshot_header {
+	__u32 magic; /* Magic identifier */
+	__u32 gpuid; /* GPU ID - see above */
+} __packed;
+
+/* Section header */
+#define SNAPSHOT_SECTION_MAGIC 0xABCD
+
+struct kgsl_snapshot_section_header {
+	__u16 magic; /* Magic identifier */
+	__u16 id;    /* Type of section */
+	__u32 size;  /* Size of the section including this header */
+} __packed;
+
+/* Section identifiers */
+#define KGSL_SNAPSHOT_SECTION_OS           0x0101
+#define KGSL_SNAPSHOT_SECTION_REGS         0x0201
+#define KGSL_SNAPSHOT_SECTION_RB           0x0301
+#define KGSL_SNAPSHOT_SECTION_IB           0x0401
+#define KGSL_SNAPSHOT_SECTION_INDEXED_REGS 0x0501
+#define KGSL_SNAPSHOT_SECTION_ISTORE       0x0801
+#define KGSL_SNAPSHOT_SECTION_DEBUG        0x0901
+#define KGSL_SNAPSHOT_SECTION_DEBUGBUS     0x0A01
+#define KGSL_SNAPSHOT_SECTION_GPU_OBJECT   0x0B01
+
+#define KGSL_SNAPSHOT_SECTION_END          0xFFFF
+
+/* OS sub-section header */
+#define KGSL_SNAPSHOT_OS_LINUX             0x0001
+
+/* Linux OS specific information */
+
+#define SNAPSHOT_STATE_HUNG 0
+#define SNAPSHOT_STATE_RUNNING 1
+
+struct kgsl_snapshot_linux {
+	int osid;                   /* subsection OS identifier */
+	int state;		    /* 1 if the thread is running, 0 for hung */
+	__u32 seconds;		    /* Unix timestamp for the snapshot */
+	__u32 power_flags;            /* Current power flags */
+	__u32 power_level;            /* Current power level */
+	__u32 power_interval_timeout; /* Power interval timeout */
+	__u32 grpclk;                 /* Current GP clock value */
+	__u32 busclk;		    /* Current busclk value */
+	__u32 ptbase;		    /* Current ptbase */
+	__u32 pid;		    /* PID of the process that owns the PT */
+	__u32 current_context;	    /* ID of the current context */
+	__u32 ctxtcount;	    /* Number of contexts appended to section */
+	unsigned char release[32];  /* kernel release */
+	unsigned char version[32];  /* kernel version */
+	unsigned char comm[16];	    /* Name of the process that owns the PT */
+} __packed;
+
+/*
+ * This structure contains a record of an active context.
+ * These are appended one after another in the OS section below
+ * the header above
+ */
+
+struct kgsl_snapshot_linux_context {
+	__u32 id;			/* The context ID */
+	__u32 timestamp_queued;		/* The last queued timestamp */
+	__u32 timestamp_retired;	/* The last timestamp retired by HW */
+};
+
+/* Ringbuffer sub-section header */
+struct kgsl_snapshot_rb {
+	int start;  /* dword at the start of the dump */
+	int end;    /* dword at the end of the dump */
+	int rbsize; /* Size (in dwords) of the ringbuffer */
+	int wptr;   /* Current index of the CPU write pointer */
+	int rptr;   /* Current index of the GPU read pointer */
+	int count;  /* Number of dwords in the dump */
+} __packed;
+
+/* Indirect buffer sub-section header */
+struct kgsl_snapshot_ib {
+	__u32 gpuaddr; /* GPU address of the the IB */
+	__u32 ptbase;  /* Base for the pagetable the GPU address is valid in */
+	int size;    /* Size of the IB */
+} __packed;
+
+/* Register sub-section header */
+struct kgsl_snapshot_regs {
+	__u32 count; /* Number of register pairs in the section */
+} __packed;
+
+/* Indexed register sub-section header */
+struct kgsl_snapshot_indexed_regs {
+	__u32 index_reg; /* Offset of the index register for this section */
+	__u32 data_reg;  /* Offset of the data register for this section */
+	int start;     /* Starting index */
+	int count;     /* Number of dwords in the data */
+} __packed;
+
+/* Istore sub-section header */
+struct kgsl_snapshot_istore {
+	int count;   /* Number of instructions in the istore */
+} __packed;
+
+/* Debug data sub-section header */
+
+/* A2XX debug sections */
+#define SNAPSHOT_DEBUG_SX         1
+#define SNAPSHOT_DEBUG_CP         2
+#define SNAPSHOT_DEBUG_SQ         3
+#define SNAPSHOT_DEBUG_SQTHREAD   4
+#define SNAPSHOT_DEBUG_MIU        5
+
+/* A3XX debug sections */
+#define SNAPSHOT_DEBUG_VPC_MEMORY 6
+#define SNAPSHOT_DEBUG_CP_MEQ     7
+#define SNAPSHOT_DEBUG_CP_PM4_RAM 8
+#define SNAPSHOT_DEBUG_CP_PFP_RAM 9
+#define SNAPSHOT_DEBUG_CP_ROQ     10
+
+struct kgsl_snapshot_debug {
+	int type;    /* Type identifier for the attached tata */
+	int size;   /* Size of the section in dwords */
+} __packed;
+
+struct kgsl_snapshot_debugbus {
+	int id;	   /* Debug bus ID */
+	int count; /* Number of dwords in the dump */
+} __packed;
+
+#define SNAPSHOT_GPU_OBJECT_SHADER  1
+#define SNAPSHOT_GPU_OBJECT_IB      2
+#define SNAPSHOT_GPU_OBJECT_GENERIC 3
+
+struct kgsl_snapshot_gpu_object {
+	int type;      /* Type of GPU object */
+	__u32 gpuaddr; /* GPU address of the the object */
+	__u32 ptbase;  /* Base for the pagetable the GPU address is valid in */
+	int size;    /* Size of the object (in dwords) */
+};
+
+#ifdef __KERNEL__
+
+/* Allocate 512K for each device snapshot */
+#define KGSL_SNAPSHOT_MEMSIZE (512 * 1024)
+
+struct kgsl_device;
+/*
+ * A helper macro to print out "not enough memory functions" - this
+ * makes it easy to standardize the messages as well as cut down on
+ * the number of strings in the binary
+ */
+
+#define SNAPSHOT_ERR_NOMEM(_d, _s) \
+	KGSL_DRV_ERR((_d), \
+	"snapshot: not enough snapshot memory for section %s\n", (_s))
+
+/*
+ * kgsl_snapshot_add_section - Add a new section to the GPU snapshot
+ * @device - the KGSL device being snapshotted
+ * @id - the section id
+ * @snapshot - pointer to the memory for the snapshot
+ * @remain - pointer to the number of bytes left in the snapshot region
+ * @func - Function pointer to fill the section
+ * @priv - Priv pointer to pass to the function
+ *
+ * Set up a KGSL snapshot header by filling the memory with the callback
+ * function and adding the standard section header
+ */
+
+static inline void *kgsl_snapshot_add_section(struct kgsl_device *device,
+	u16 id, void *snapshot, int *remain,
+	int (*func)(struct kgsl_device *, void *, int, void *), void *priv)
+{
+	struct kgsl_snapshot_section_header *header = snapshot;
+	void *data = snapshot + sizeof(*header);
+	int ret = 0;
+
+	/*
+	 * Sanity check to make sure there is enough for the header.  The
+	 * callback will check to make sure there is enough for the rest
+	 * of the data.  If there isn't enough room then don't advance the
+	 * pointer.
+	 */
+
+	if (*remain < sizeof(*header))
+		return snapshot;
+
+	/* It is legal to have no function (i.e. - make an empty section) */
+
+	if (func) {
+		ret = func(device, data, *remain, priv);
+
+		/*
+		 * If there wasn't enough room for the data then don't bother
+		 * setting up the header.
+		 */
+
+		if (ret == 0)
+			return snapshot;
+	}
+
+	header->magic = SNAPSHOT_SECTION_MAGIC;
+	header->id = id;
+	header->size = ret + sizeof(*header);
+
+	/* Decrement the room left in the snapshot region */
+	*remain -= header->size;
+	/* Advance the pointer to the end of the next function */
+	return snapshot + header->size;
+}
+
+/* A common helper function to dump a range of registers.  This will be used in
+ * the GPU specific devices like this:
+ *
+ * struct kgsl_snapshot_registers priv;
+ * priv.regs = registers_array;;
+ * priv.count = num_registers;
+ *
+ * kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_REGS, snapshot,
+ *	remain, kgsl_snapshot_dump_regs, &priv).
+ *
+ * Pass in an array of register range pairs in the form of:
+ * start reg, stop reg
+ * All the registers between start and stop inclusive will be dumped
+ */
+
+struct kgsl_snapshot_registers {
+	unsigned int *regs;  /* Pointer to the array of register ranges */
+	int count;	     /* Number of entries in the array */
+};
+
+int kgsl_snapshot_dump_regs(struct kgsl_device *device, void *snapshot,
+	int remain, void *priv);
+
+/*
+ * A common helper function to dump a set of indexed registers. Use it
+ * like this:
+ *
+ * struct kgsl_snapshot_indexed_registers priv;
+ * priv.index = REG_INDEX;
+ * priv.data = REG_DATA;
+ * priv.count = num_registers
+ *
+ * kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_INDEXED_REGS,
+ *	snapshot, remain, kgsl_snapshot_dump_indexed_regs, &priv).
+ *
+ * The callback function will write an index from 0 to priv.count to
+ * the index register and read the data from the data register.
+ */
+
+struct kgsl_snapshot_indexed_registers {
+	unsigned int index; /* Offset of the index register */
+	unsigned int data;  /* Offset of the data register */
+	unsigned int start;	/* Index to start with */
+	unsigned int count; /* Number of values to read from the pair */
+};
+
+/* Helper function to snapshot a section of indexed registers */
+
+void *kgsl_snapshot_indexed_registers(struct kgsl_device *device,
+	void *snapshot, int *remain, unsigned int index,
+	unsigned int data, unsigned int start, unsigned int count);
+
+/* Freeze a GPU buffer so it can be dumped in the snapshot */
+int kgsl_snapshot_get_object(struct kgsl_device *device, unsigned int ptbase,
+	unsigned int gpuaddr, unsigned int size, unsigned int type);
+
+#endif
+#endif
diff --git a/drivers/gpu/msm/kgsl_trace.c b/drivers/gpu/msm/kgsl_trace.c
new file mode 100644
index 0000000..2bcca15
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_trace.c
@@ -0,0 +1,19 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "kgsl.h"
+#include "kgsl_device.h"
+
+/* Instantiate tracepoints */
+#define CREATE_TRACE_POINTS
+#include "kgsl_trace.h"
diff --git a/drivers/gpu/msm/kgsl_trace.h b/drivers/gpu/msm/kgsl_trace.h
new file mode 100644
index 0000000..60231f6
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_trace.h
@@ -0,0 +1,496 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#if !defined(_KGSL_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _KGSL_TRACE_H
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM kgsl
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE kgsl_trace
+
+#include <linux/tracepoint.h>
+#include "kgsl_device.h"
+
+struct kgsl_device;
+struct kgsl_ringbuffer_issueibcmds;
+struct kgsl_device_waittimestamp;
+
+/*
+ * Tracepoint for kgsl issue ib commands
+ */
+TRACE_EVENT(kgsl_issueibcmds,
+
+	TP_PROTO(struct kgsl_device *device,
+			struct kgsl_ringbuffer_issueibcmds *cmd,
+			struct kgsl_ibdesc *ibdesc,
+			int result),
+
+	TP_ARGS(device, cmd, ibdesc, result),
+
+	TP_STRUCT__entry(
+		__string(device_name, device->name)
+		__field(unsigned int, drawctxt_id)
+		__field(unsigned int, ibdesc_addr)
+		__field(unsigned int, numibs)
+		__field(unsigned int, timestamp)
+		__field(unsigned int, flags)
+		__field(int, result)
+	),
+
+	TP_fast_assign(
+		__assign_str(device_name, device->name);
+		__entry->drawctxt_id = cmd->drawctxt_id;
+		__entry->ibdesc_addr = ibdesc[0].gpuaddr;
+		__entry->numibs = cmd->numibs;
+		__entry->timestamp = cmd->timestamp;
+		__entry->flags = cmd->flags;
+		__entry->result = result;
+	),
+
+	TP_printk(
+		"d_name=%s ctx=%u ib=0x%u numibs=%u timestamp=0x%x "
+		"flags=0x%x(%s) result=%d",
+		__get_str(device_name),
+		__entry->drawctxt_id,
+		__entry->ibdesc_addr,
+		__entry->numibs,
+		__entry->timestamp,
+		__entry->flags,
+		__entry->flags ? __print_flags(__entry->flags, "|",
+			{ KGSL_CONTEXT_SAVE_GMEM, "SAVE_GMEM" },
+			{ KGSL_CONTEXT_SUBMIT_IB_LIST, "IB_LIST" },
+			{ KGSL_CONTEXT_CTX_SWITCH, "CTX_SWITCH" })
+			: "None",
+		__entry->result
+	)
+);
+
+/*
+ * Tracepoint for kgsl readtimestamp
+ */
+TRACE_EVENT(kgsl_readtimestamp,
+
+	TP_PROTO(struct kgsl_device *device,
+			unsigned int context_id,
+			unsigned int type,
+			unsigned int timestamp),
+
+	TP_ARGS(device, context_id, type, timestamp),
+
+	TP_STRUCT__entry(
+		__string(device_name, device->name)
+		__field(unsigned int, context_id)
+		__field(unsigned int, type)
+		__field(unsigned int, timestamp)
+	),
+
+	TP_fast_assign(
+		__assign_str(device_name, device->name);
+		__entry->context_id = context_id;
+		__entry->type = type;
+		__entry->timestamp = timestamp;
+	),
+
+	TP_printk(
+		"d_name=%s context_id=%u type=%u timestamp=0x%x",
+		__get_str(device_name),
+		__entry->context_id,
+		__entry->type,
+		__entry->timestamp
+	)
+);
+
+/*
+ * Tracepoint for kgsl waittimestamp entry
+ */
+TRACE_EVENT(kgsl_waittimestamp_entry,
+
+	TP_PROTO(struct kgsl_device *device,
+			unsigned int context_id,
+			unsigned int curr_ts,
+			unsigned int wait_ts,
+			unsigned int timeout),
+
+	TP_ARGS(device, context_id, curr_ts, wait_ts, timeout),
+
+	TP_STRUCT__entry(
+		__string(device_name, device->name)
+		__field(unsigned int, context_id)
+		__field(unsigned int, curr_ts)
+		__field(unsigned int, wait_ts)
+		__field(unsigned int, timeout)
+	),
+
+	TP_fast_assign(
+		__assign_str(device_name, device->name);
+		__entry->context_id = context_id;
+		__entry->curr_ts = curr_ts;
+		__entry->wait_ts = wait_ts;
+		__entry->timeout = timeout;
+	),
+
+	TP_printk(
+		"d_name=%s context_id=%u curr_ts=%u timestamp=0x%x timeout=%u",
+		__get_str(device_name),
+		__entry->context_id,
+		__entry->curr_ts,
+		__entry->wait_ts,
+		__entry->timeout
+	)
+);
+
+/*
+ * Tracepoint for kgsl waittimestamp exit
+ */
+TRACE_EVENT(kgsl_waittimestamp_exit,
+
+	TP_PROTO(struct kgsl_device *device, unsigned int curr_ts,
+		 int result),
+
+	TP_ARGS(device, curr_ts, result),
+
+	TP_STRUCT__entry(
+		__string(device_name, device->name)
+		__field(unsigned int, curr_ts)
+		__field(int, result)
+	),
+
+	TP_fast_assign(
+		__assign_str(device_name, device->name);
+		__entry->curr_ts = curr_ts;
+		__entry->result = result;
+	),
+
+	TP_printk(
+		"d_name=%s curr_ts=%u result=%d",
+		__get_str(device_name),
+		__entry->curr_ts,
+		__entry->result
+	)
+);
+
+DECLARE_EVENT_CLASS(kgsl_pwr_template,
+	TP_PROTO(struct kgsl_device *device, int on),
+
+	TP_ARGS(device, on),
+
+	TP_STRUCT__entry(
+		__string(device_name, device->name)
+		__field(int, on)
+	),
+
+	TP_fast_assign(
+		__assign_str(device_name, device->name);
+		__entry->on = on;
+	),
+
+	TP_printk(
+		"d_name=%s %s",
+		__get_str(device_name),
+		__entry->on ? "on" : "off"
+	)
+);
+
+DEFINE_EVENT(kgsl_pwr_template, kgsl_clk,
+	TP_PROTO(struct kgsl_device *device, int on),
+	TP_ARGS(device, on)
+);
+
+DEFINE_EVENT(kgsl_pwr_template, kgsl_irq,
+	TP_PROTO(struct kgsl_device *device, int on),
+	TP_ARGS(device, on)
+);
+
+DEFINE_EVENT(kgsl_pwr_template, kgsl_bus,
+	TP_PROTO(struct kgsl_device *device, int on),
+	TP_ARGS(device, on)
+);
+
+DEFINE_EVENT(kgsl_pwr_template, kgsl_rail,
+	TP_PROTO(struct kgsl_device *device, int on),
+	TP_ARGS(device, on)
+);
+
+TRACE_EVENT(kgsl_pwrlevel,
+
+	TP_PROTO(struct kgsl_device *device, unsigned int pwrlevel,
+		 unsigned int freq),
+
+	TP_ARGS(device, pwrlevel, freq),
+
+	TP_STRUCT__entry(
+		__string(device_name, device->name)
+		__field(unsigned int, pwrlevel)
+		__field(unsigned int, freq)
+	),
+
+	TP_fast_assign(
+		__assign_str(device_name, device->name);
+		__entry->pwrlevel = pwrlevel;
+		__entry->freq = freq;
+	),
+
+	TP_printk(
+		"d_name=%s pwrlevel=%d freq=%d",
+		__get_str(device_name),
+		__entry->pwrlevel,
+		__entry->freq
+	)
+);
+
+DECLARE_EVENT_CLASS(kgsl_pwrstate_template,
+	TP_PROTO(struct kgsl_device *device, unsigned int state),
+
+	TP_ARGS(device, state),
+
+	TP_STRUCT__entry(
+		__string(device_name, device->name)
+		__field(unsigned int, state)
+	),
+
+	TP_fast_assign(
+		__assign_str(device_name, device->name);
+		__entry->state = state;
+	),
+
+	TP_printk(
+		"d_name=%s %s",
+		__get_str(device_name),
+		kgsl_pwrstate_to_str(__entry->state)
+	)
+);
+
+DEFINE_EVENT(kgsl_pwrstate_template, kgsl_pwr_set_state,
+	TP_PROTO(struct kgsl_device *device, unsigned int state),
+	TP_ARGS(device, state)
+);
+
+DEFINE_EVENT(kgsl_pwrstate_template, kgsl_pwr_request_state,
+	TP_PROTO(struct kgsl_device *device, unsigned int state),
+	TP_ARGS(device, state)
+);
+
+TRACE_EVENT(kgsl_mem_alloc,
+
+	TP_PROTO(struct kgsl_mem_entry *mem_entry),
+
+	TP_ARGS(mem_entry),
+
+	TP_STRUCT__entry(
+		__field(unsigned int, gpuaddr)
+		__field(unsigned int, size)
+	),
+
+	TP_fast_assign(
+		__entry->gpuaddr = mem_entry->memdesc.gpuaddr;
+		__entry->size = mem_entry->memdesc.size;
+	),
+
+	TP_printk(
+		"gpuaddr=0x%08x size=%d",
+		__entry->gpuaddr, __entry->size
+	)
+);
+
+TRACE_EVENT(kgsl_mem_map,
+
+	TP_PROTO(struct kgsl_mem_entry *mem_entry, int fd),
+
+	TP_ARGS(mem_entry, fd),
+
+	TP_STRUCT__entry(
+		__field(unsigned int, gpuaddr)
+		__field(unsigned int, size)
+		__field(int, fd)
+		__field(int, type)
+	),
+
+	TP_fast_assign(
+		__entry->gpuaddr = mem_entry->memdesc.gpuaddr;
+		__entry->size = mem_entry->memdesc.size;
+		__entry->fd = fd;
+		__entry->type = mem_entry->memtype;
+	),
+
+	TP_printk(
+		"gpuaddr=0x%08x size=%d type=%d fd=%d",
+		__entry->gpuaddr, __entry->size,
+		__entry->type, __entry->fd
+	)
+);
+
+TRACE_EVENT(kgsl_mem_free,
+
+	TP_PROTO(struct kgsl_mem_entry *mem_entry),
+
+	TP_ARGS(mem_entry),
+
+	TP_STRUCT__entry(
+		__field(unsigned int, gpuaddr)
+		__field(unsigned int, size)
+		__field(int, type)
+		__field(int, fd)
+	),
+
+	TP_fast_assign(
+		__entry->gpuaddr = mem_entry->memdesc.gpuaddr;
+		__entry->size = mem_entry->memdesc.size;
+		__entry->type = mem_entry->memtype;
+	),
+
+	TP_printk(
+		"gpuaddr=0x%08x size=%d type=%d",
+		__entry->gpuaddr, __entry->size, __entry->type
+	)
+);
+
+DECLARE_EVENT_CLASS(kgsl_mem_timestamp_template,
+
+	TP_PROTO(struct kgsl_device *device, struct kgsl_mem_entry *mem_entry,
+		unsigned int id, unsigned int curr_ts, unsigned int free_ts),
+
+	TP_ARGS(device, mem_entry, id, curr_ts, free_ts),
+
+	TP_STRUCT__entry(
+		__string(device_name, device->name)
+		__field(unsigned int, gpuaddr)
+		__field(unsigned int, size)
+		__field(int, type)
+		__field(unsigned int, drawctxt_id)
+		__field(unsigned int, curr_ts)
+		__field(unsigned int, free_ts)
+	),
+
+	TP_fast_assign(
+		__assign_str(device_name, device->name);
+		__entry->gpuaddr = mem_entry->memdesc.gpuaddr;
+		__entry->size = mem_entry->memdesc.size;
+		__entry->drawctxt_id = id;
+		__entry->type = mem_entry->memtype;
+		__entry->curr_ts = curr_ts;
+		__entry->free_ts = free_ts;
+	),
+
+	TP_printk(
+		"d_name=%s gpuaddr=0x%08x size=%d type=%d ctx=%u"
+		" curr_ts=0x%08x free_ts=0x%08x",
+		__get_str(device_name),
+		__entry->gpuaddr,
+		__entry->size,
+		__entry->type,
+		__entry->drawctxt_id,
+		__entry->curr_ts,
+		__entry->free_ts
+	)
+);
+
+DEFINE_EVENT(kgsl_mem_timestamp_template, kgsl_mem_timestamp_queue,
+	TP_PROTO(struct kgsl_device *device, struct kgsl_mem_entry *mem_entry,
+		unsigned int id, unsigned int curr_ts, unsigned int free_ts),
+	TP_ARGS(device, mem_entry, id, curr_ts, free_ts)
+);
+
+DEFINE_EVENT(kgsl_mem_timestamp_template, kgsl_mem_timestamp_free,
+	TP_PROTO(struct kgsl_device *device, struct kgsl_mem_entry *mem_entry,
+		unsigned int id, unsigned int curr_ts, unsigned int free_ts),
+	TP_ARGS(device, mem_entry, id, curr_ts, free_ts)
+);
+
+TRACE_EVENT(kgsl_context_create,
+
+	TP_PROTO(struct kgsl_device *device, struct kgsl_context *context,
+		 unsigned int flags),
+
+	TP_ARGS(device, context, flags),
+
+	TP_STRUCT__entry(
+		__string(device_name, device->name)
+		__field(unsigned int, id)
+		__field(unsigned int, flags)
+	),
+
+	TP_fast_assign(
+		__assign_str(device_name, device->name);
+		__entry->id = context->id;
+		__entry->flags = flags;
+	),
+
+	TP_printk(
+		"d_name=%s ctx=%u flags=0x%x %s",
+		__get_str(device_name), __entry->id, __entry->flags,
+		__entry->flags ? __print_flags(__entry->flags, "|",
+			{ KGSL_CONTEXT_NO_GMEM_ALLOC , "NO_GMEM_ALLOC" },
+			{ KGSL_CONTEXT_PREAMBLE, "PREAMBLE" },
+			{ KGSL_CONTEXT_TRASH_STATE, "TRASH_STATE" },
+			{ KGSL_CONTEXT_PER_CONTEXT_TS, "PER_CONTEXT_TS" })
+			: "None"
+	)
+);
+
+TRACE_EVENT(kgsl_context_detach,
+
+	TP_PROTO(struct kgsl_device *device, struct kgsl_context *context),
+
+	TP_ARGS(device, context),
+
+	TP_STRUCT__entry(
+		__string(device_name, device->name)
+		__field(unsigned int, id)
+	),
+
+	TP_fast_assign(
+		__assign_str(device_name, device->name);
+		__entry->id = context->id;
+	),
+
+	TP_printk(
+		"d_name=%s ctx=%u",
+		__get_str(device_name), __entry->id
+	)
+);
+
+TRACE_EVENT(kgsl_mmu_pagefault,
+
+	TP_PROTO(struct kgsl_device *device, unsigned int page,
+		 unsigned int pt, const char *op),
+
+	TP_ARGS(device, page, pt, op),
+
+	TP_STRUCT__entry(
+		__string(device_name, device->name)
+		__field(unsigned int, page)
+		__field(unsigned int, pt)
+		__string(op, op)
+	),
+
+	TP_fast_assign(
+		__assign_str(device_name, device->name);
+		__entry->page = page;
+		__entry->pt = pt;
+		__assign_str(op, op);
+	),
+
+	TP_printk(
+		"d_name=%s page=0x%08x pt=%d op=%s\n",
+		__get_str(device_name), __entry->page, __entry->pt,
+		__get_str(op)
+	)
+);
+
+#endif /* _KGSL_TRACE_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/drivers/gpu/msm/z180.c b/drivers/gpu/msm/z180.c
new file mode 100644
index 0000000..f4bbf69
--- /dev/null
+++ b/drivers/gpu/msm/z180.c
@@ -0,0 +1,970 @@
+/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+
+#include "kgsl.h"
+#include "kgsl_cffdump.h"
+#include "kgsl_sharedmem.h"
+
+#include "z180.h"
+#include "z180_reg.h"
+#include "z180_trace.h"
+
+#define DRIVER_VERSION_MAJOR   3
+#define DRIVER_VERSION_MINOR   1
+
+#define Z180_DEVICE(device) \
+		KGSL_CONTAINER_OF(device, struct z180_device, dev)
+
+#define GSL_VGC_INT_MASK \
+	 (REG_VGC_IRQSTATUS__MH_MASK | \
+	  REG_VGC_IRQSTATUS__G2D_MASK | \
+	  REG_VGC_IRQSTATUS__FIFO_MASK)
+
+#define VGV3_NEXTCMD_JUMP        0x01
+
+#define VGV3_NEXTCMD_NEXTCMD_FSHIFT 12
+#define VGV3_NEXTCMD_NEXTCMD_FMASK 0x7
+
+#define VGV3_CONTROL_MARKADD_FSHIFT 0
+#define VGV3_CONTROL_MARKADD_FMASK 0xfff
+
+#define Z180_PACKET_SIZE 15
+#define Z180_MARKER_SIZE 10
+#define Z180_CALL_CMD     0x1000
+#define Z180_MARKER_CMD   0x8000
+#define Z180_STREAM_END_CMD 0x9000
+#define Z180_STREAM_PACKET 0x7C000176
+#define Z180_STREAM_PACKET_CALL 0x7C000275
+#define Z180_PACKET_COUNT 8
+#define Z180_RB_SIZE (Z180_PACKET_SIZE*Z180_PACKET_COUNT \
+			  *sizeof(uint32_t))
+
+#define NUMTEXUNITS             4
+#define TEXUNITREGCOUNT         25
+#define VG_REGCOUNT             0x39
+
+#define PACKETSIZE_BEGIN        3
+#define PACKETSIZE_G2DCOLOR     2
+#define PACKETSIZE_TEXUNIT      (TEXUNITREGCOUNT * 2)
+#define PACKETSIZE_REG          (VG_REGCOUNT * 2)
+#define PACKETSIZE_STATE        (PACKETSIZE_TEXUNIT * NUMTEXUNITS + \
+				 PACKETSIZE_REG + PACKETSIZE_BEGIN + \
+				 PACKETSIZE_G2DCOLOR)
+#define PACKETSIZE_STATESTREAM  (ALIGN((PACKETSIZE_STATE * \
+				 sizeof(unsigned int)), 32) / \
+				 sizeof(unsigned int))
+
+#define Z180_INVALID_CONTEXT UINT_MAX
+
+/* z180 MH arbiter config*/
+#define Z180_CFG_MHARB \
+	(0x10 \
+		| (0 << MH_ARBITER_CONFIG__SAME_PAGE_GRANULARITY__SHIFT) \
+		| (1 << MH_ARBITER_CONFIG__L1_ARB_ENABLE__SHIFT) \
+		| (1 << MH_ARBITER_CONFIG__L1_ARB_HOLD_ENABLE__SHIFT) \
+		| (0 << MH_ARBITER_CONFIG__L2_ARB_CONTROL__SHIFT) \
+		| (1 << MH_ARBITER_CONFIG__PAGE_SIZE__SHIFT) \
+		| (1 << MH_ARBITER_CONFIG__TC_REORDER_ENABLE__SHIFT) \
+		| (1 << MH_ARBITER_CONFIG__TC_ARB_HOLD_ENABLE__SHIFT) \
+		| (0 << MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT_ENABLE__SHIFT) \
+		| (0x8 << MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT__SHIFT) \
+		| (1 << MH_ARBITER_CONFIG__CP_CLNT_ENABLE__SHIFT) \
+		| (1 << MH_ARBITER_CONFIG__VGT_CLNT_ENABLE__SHIFT) \
+		| (1 << MH_ARBITER_CONFIG__TC_CLNT_ENABLE__SHIFT) \
+		| (1 << MH_ARBITER_CONFIG__RB_CLNT_ENABLE__SHIFT) \
+		| (1 << MH_ARBITER_CONFIG__PA_CLNT_ENABLE__SHIFT))
+
+#define Z180_TIMESTAMP_EPSILON 20000
+#define Z180_IDLE_COUNT_MAX 1000000
+
+enum z180_cmdwindow_type {
+	Z180_CMDWINDOW_2D = 0x00000000,
+	Z180_CMDWINDOW_MMU = 0x00000002,
+};
+
+#define Z180_CMDWINDOW_TARGET_MASK		0x000000FF
+#define Z180_CMDWINDOW_ADDR_MASK		0x00FFFF00
+#define Z180_CMDWINDOW_TARGET_SHIFT		0
+#define Z180_CMDWINDOW_ADDR_SHIFT		8
+
+static int z180_start(struct kgsl_device *device, unsigned int init_ram);
+static int z180_stop(struct kgsl_device *device);
+static int z180_wait(struct kgsl_device *device,
+				struct kgsl_context *context,
+				unsigned int timestamp,
+				unsigned int msecs);
+static void z180_regread(struct kgsl_device *device,
+				unsigned int offsetwords,
+				unsigned int *value);
+static void z180_regwrite(struct kgsl_device *device,
+				unsigned int offsetwords,
+				unsigned int value);
+static void z180_cmdwindow_write(struct kgsl_device *device,
+				unsigned int addr,
+				unsigned int data);
+
+#define Z180_MMU_CONFIG					     \
+	(0x01							     \
+	| (MMU_CONFIG << MH_MMU_CONFIG__RB_W_CLNT_BEHAVIOR__SHIFT)   \
+	| (MMU_CONFIG << MH_MMU_CONFIG__CP_W_CLNT_BEHAVIOR__SHIFT)   \
+	| (MMU_CONFIG << MH_MMU_CONFIG__CP_R0_CLNT_BEHAVIOR__SHIFT)  \
+	| (MMU_CONFIG << MH_MMU_CONFIG__CP_R1_CLNT_BEHAVIOR__SHIFT)  \
+	| (MMU_CONFIG << MH_MMU_CONFIG__CP_R2_CLNT_BEHAVIOR__SHIFT)  \
+	| (MMU_CONFIG << MH_MMU_CONFIG__CP_R3_CLNT_BEHAVIOR__SHIFT)  \
+	| (MMU_CONFIG << MH_MMU_CONFIG__CP_R4_CLNT_BEHAVIOR__SHIFT)  \
+	| (MMU_CONFIG << MH_MMU_CONFIG__VGT_R0_CLNT_BEHAVIOR__SHIFT) \
+	| (MMU_CONFIG << MH_MMU_CONFIG__VGT_R1_CLNT_BEHAVIOR__SHIFT) \
+	| (MMU_CONFIG << MH_MMU_CONFIG__TC_R_CLNT_BEHAVIOR__SHIFT)   \
+	| (MMU_CONFIG << MH_MMU_CONFIG__PA_W_CLNT_BEHAVIOR__SHIFT))
+
+static const struct kgsl_functable z180_functable;
+
+static struct z180_device device_2d0 = {
+	.dev = {
+		KGSL_DEVICE_COMMON_INIT(device_2d0.dev),
+		.name = DEVICE_2D0_NAME,
+		.id = KGSL_DEVICE_2D0,
+		.mh = {
+			.mharb = Z180_CFG_MHARB,
+			.mh_intf_cfg1 = 0x00032f07,
+			.mh_intf_cfg2 = 0x004b274f,
+			/* turn off memory protection unit by setting
+			   acceptable physical address range to include
+			   all pages. */
+			.mpu_base = 0x00000000,
+			.mpu_range =  0xFFFFF000,
+		},
+		.mmu = {
+			.config = Z180_MMU_CONFIG,
+		},
+		.pwrctrl = {
+			.irq_name = KGSL_2D0_IRQ,
+		},
+		.iomemname = KGSL_2D0_REG_MEMORY,
+		.ftbl = &z180_functable,
+	},
+	.cmdwin_lock = __SPIN_LOCK_INITIALIZER(device_2d1.cmdwin_lock),
+};
+
+static struct z180_device device_2d1 = {
+	.dev = {
+		KGSL_DEVICE_COMMON_INIT(device_2d1.dev),
+		.name = DEVICE_2D1_NAME,
+		.id = KGSL_DEVICE_2D1,
+		.mh = {
+			.mharb = Z180_CFG_MHARB,
+			.mh_intf_cfg1 = 0x00032f07,
+			.mh_intf_cfg2 = 0x004b274f,
+			/* turn off memory protection unit by setting
+			   acceptable physical address range to include
+			   all pages. */
+			.mpu_base = 0x00000000,
+			.mpu_range =  0xFFFFF000,
+		},
+		.mmu = {
+			.config = Z180_MMU_CONFIG,
+		},
+		.pwrctrl = {
+			.irq_name = KGSL_2D1_IRQ,
+		},
+		.iomemname = KGSL_2D1_REG_MEMORY,
+		.ftbl = &z180_functable,
+	},
+	.cmdwin_lock = __SPIN_LOCK_INITIALIZER(device_2d1.cmdwin_lock),
+};
+
+static irqreturn_t z180_irq_handler(struct kgsl_device *device)
+{
+	irqreturn_t result = IRQ_NONE;
+	unsigned int status;
+	struct z180_device *z180_dev = Z180_DEVICE(device);
+
+	z180_regread(device, ADDR_VGC_IRQSTATUS >> 2, &status);
+
+	trace_kgsl_z180_irq_status(device, status);
+
+	if (status & GSL_VGC_INT_MASK) {
+		z180_regwrite(device,
+			ADDR_VGC_IRQSTATUS >> 2, status & GSL_VGC_INT_MASK);
+
+		result = IRQ_HANDLED;
+
+		if (status & REG_VGC_IRQSTATUS__FIFO_MASK)
+			KGSL_DRV_ERR(device, "z180 fifo interrupt\n");
+		if (status & REG_VGC_IRQSTATUS__MH_MASK)
+			kgsl_mh_intrcallback(device);
+		if (status & REG_VGC_IRQSTATUS__G2D_MASK) {
+			int count;
+
+			z180_regread(device,
+					 ADDR_VGC_IRQ_ACTIVE_CNT >> 2,
+					 &count);
+
+			count >>= 8;
+			count &= 255;
+			z180_dev->timestamp += count;
+
+			queue_work(device->work_queue, &device->ts_expired_ws);
+			wake_up_interruptible(&device->wait_queue);
+
+			atomic_notifier_call_chain(
+				&(device->ts_notifier_list),
+				device->id, NULL);
+		}
+	}
+
+	if ((device->pwrctrl.nap_allowed == true) &&
+		(device->requested_state == KGSL_STATE_NONE)) {
+		kgsl_pwrctrl_request_state(device, KGSL_STATE_NAP);
+		queue_work(device->work_queue, &device->idle_check_ws);
+	}
+	mod_timer_pending(&device->idle_timer,
+			jiffies + device->pwrctrl.interval_timeout);
+
+	return result;
+}
+
+static void z180_cleanup_pt(struct kgsl_device *device,
+			       struct kgsl_pagetable *pagetable)
+{
+	struct z180_device *z180_dev = Z180_DEVICE(device);
+
+	kgsl_mmu_unmap(pagetable, &device->mmu.setstate_memory);
+
+	kgsl_mmu_unmap(pagetable, &device->memstore);
+
+	kgsl_mmu_unmap(pagetable, &z180_dev->ringbuffer.cmdbufdesc);
+}
+
+static int z180_setup_pt(struct kgsl_device *device,
+			     struct kgsl_pagetable *pagetable)
+{
+	int result = 0;
+	struct z180_device *z180_dev = Z180_DEVICE(device);
+
+	result = kgsl_mmu_map_global(pagetable, &device->mmu.setstate_memory,
+				     GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
+
+	if (result)
+		goto error;
+
+	result = kgsl_mmu_map_global(pagetable, &device->memstore,
+				     GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
+	if (result)
+		goto error_unmap_dummy;
+
+	result = kgsl_mmu_map_global(pagetable,
+				     &z180_dev->ringbuffer.cmdbufdesc,
+				     GSL_PT_PAGE_RV);
+	if (result)
+		goto error_unmap_memstore;
+	return result;
+
+error_unmap_dummy:
+	kgsl_mmu_unmap(pagetable, &device->mmu.setstate_memory);
+
+error_unmap_memstore:
+	kgsl_mmu_unmap(pagetable, &device->memstore);
+
+error:
+	return result;
+}
+
+static inline unsigned int rb_offset(unsigned int timestamp)
+{
+	return (timestamp % Z180_PACKET_COUNT)
+		*sizeof(unsigned int)*(Z180_PACKET_SIZE);
+}
+
+static inline unsigned int rb_gpuaddr(struct z180_device *z180_dev,
+					unsigned int timestamp)
+{
+	return z180_dev->ringbuffer.cmdbufdesc.gpuaddr + rb_offset(timestamp);
+}
+
+static void addmarker(struct z180_ringbuffer *rb, unsigned int timestamp)
+{
+	char *ptr = (char *)(rb->cmdbufdesc.hostptr);
+	unsigned int *p = (unsigned int *)(ptr + rb_offset(timestamp));
+
+	*p++ = Z180_STREAM_PACKET;
+	*p++ = (Z180_MARKER_CMD | 5);
+	*p++ = ADDR_VGV3_LAST << 24;
+	*p++ = ADDR_VGV3_LAST << 24;
+	*p++ = ADDR_VGV3_LAST << 24;
+	*p++ = Z180_STREAM_PACKET;
+	*p++ = 5;
+	*p++ = ADDR_VGV3_LAST << 24;
+	*p++ = ADDR_VGV3_LAST << 24;
+	*p++ = ADDR_VGV3_LAST << 24;
+}
+
+static void addcmd(struct z180_ringbuffer *rb, unsigned int timestamp,
+			unsigned int cmd, unsigned int nextcnt)
+{
+	char * ptr = (char *)(rb->cmdbufdesc.hostptr);
+	unsigned int *p = (unsigned int *)(ptr + (rb_offset(timestamp)
+			   + (Z180_MARKER_SIZE * sizeof(unsigned int))));
+
+	*p++ = Z180_STREAM_PACKET_CALL;
+	*p++ = cmd;
+	*p++ = Z180_CALL_CMD | nextcnt;
+	*p++ = ADDR_VGV3_LAST << 24;
+	*p++ = ADDR_VGV3_LAST << 24;
+}
+
+static void z180_cmdstream_start(struct kgsl_device *device, int init_ram)
+{
+	struct z180_device *z180_dev = Z180_DEVICE(device);
+	unsigned int cmd = VGV3_NEXTCMD_JUMP << VGV3_NEXTCMD_NEXTCMD_FSHIFT;
+
+	if (init_ram) {
+		z180_dev->timestamp = 0;
+		z180_dev->current_timestamp = 0;
+	}
+
+	addmarker(&z180_dev->ringbuffer, 0);
+
+	z180_cmdwindow_write(device, ADDR_VGV3_MODE, 4);
+
+	z180_cmdwindow_write(device, ADDR_VGV3_NEXTADDR,
+			     rb_gpuaddr(z180_dev, z180_dev->current_timestamp));
+
+	z180_cmdwindow_write(device, ADDR_VGV3_NEXTCMD, cmd | 5);
+
+	z180_cmdwindow_write(device, ADDR_VGV3_WRITEADDR,
+			device->memstore.gpuaddr);
+
+	cmd = (int)(((1) & VGV3_CONTROL_MARKADD_FMASK)
+			<< VGV3_CONTROL_MARKADD_FSHIFT);
+
+	z180_cmdwindow_write(device, ADDR_VGV3_CONTROL, cmd);
+
+	z180_cmdwindow_write(device, ADDR_VGV3_CONTROL, 0);
+}
+
+static int room_in_rb(struct z180_device *device)
+{
+	int ts_diff;
+
+	ts_diff = device->current_timestamp - device->timestamp;
+
+	return ts_diff < Z180_PACKET_COUNT;
+}
+
+static int z180_idle(struct kgsl_device *device, unsigned int timeout)
+{
+	int status = 0;
+	struct z180_device *z180_dev = Z180_DEVICE(device);
+
+	if (timestamp_cmp(z180_dev->current_timestamp,
+		z180_dev->timestamp) > 0)
+		status = z180_wait(device, NULL,
+				z180_dev->current_timestamp, timeout);
+
+	if (status)
+		KGSL_DRV_ERR(device, "z180_waittimestamp() timed out\n");
+
+	return status;
+}
+
+int
+z180_cmdstream_issueibcmds(struct kgsl_device_private *dev_priv,
+			struct kgsl_context *context,
+			struct kgsl_ibdesc *ibdesc,
+			unsigned int numibs,
+			uint32_t *timestamp,
+			unsigned int ctrl)
+{
+	long result = 0;
+	unsigned int ofs        = PACKETSIZE_STATESTREAM * sizeof(unsigned int);
+	unsigned int cnt        = 5;
+	unsigned int old_timestamp = 0;
+	unsigned int nextcnt    = Z180_STREAM_END_CMD | 5;
+	struct kgsl_mem_entry *entry = NULL;
+	unsigned int cmd;
+	struct kgsl_device *device = dev_priv->device;
+	struct kgsl_pagetable *pagetable = dev_priv->process_priv->pagetable;
+	struct z180_device *z180_dev = Z180_DEVICE(device);
+	unsigned int sizedwords;
+
+	if (device->state & KGSL_STATE_HUNG) {
+		result = -EINVAL;
+		goto error;
+	}
+	if (numibs != 1) {
+		KGSL_DRV_ERR(device, "Invalid number of ibs: %d\n", numibs);
+		result = -EINVAL;
+		goto error;
+	}
+	cmd = ibdesc[0].gpuaddr;
+	sizedwords = ibdesc[0].sizedwords;
+	/*
+	 * Get a kernel mapping to the IB for monkey patching.
+	 * See the end of this function.
+	 */
+	entry = kgsl_sharedmem_find_region(dev_priv->process_priv, cmd,
+		sizedwords);
+	if (entry == NULL) {
+		KGSL_DRV_ERR(device, "Bad ibdesc: gpuaddr 0x%x size %d\n",
+			     cmd, sizedwords);
+		result = -EINVAL;
+		goto error;
+	}
+	/*
+	 * This will only map memory if it exists, otherwise it will reuse the
+	 * mapping. And the 2d userspace reuses IBs so we likely won't create
+	 * too many mappings.
+	 */
+	if (kgsl_gpuaddr_to_vaddr(&entry->memdesc, cmd) == NULL) {
+		KGSL_DRV_ERR(device,
+			     "Cannot make kernel mapping for gpuaddr 0x%x\n",
+			     cmd);
+		result = -EINVAL;
+		goto error;
+	}
+
+	KGSL_CMD_INFO(device, "ctxt %d ibaddr 0x%08x sizedwords %d\n",
+		context->id, cmd, sizedwords);
+	/* context switch */
+	if ((context->id != (int)z180_dev->ringbuffer.prevctx) ||
+	    (ctrl & KGSL_CONTEXT_CTX_SWITCH)) {
+		KGSL_CMD_INFO(device, "context switch %d -> %d\n",
+			context->id, z180_dev->ringbuffer.prevctx);
+		kgsl_mmu_setstate(&device->mmu, pagetable);
+		cnt = PACKETSIZE_STATESTREAM;
+		ofs = 0;
+	}
+	kgsl_setstate(&device->mmu,
+			kgsl_mmu_pt_get_flags(device->mmu.hwpagetable,
+			device->id));
+
+	result = wait_event_interruptible_timeout(device->wait_queue,
+				  room_in_rb(z180_dev),
+				  msecs_to_jiffies(KGSL_TIMEOUT_DEFAULT));
+	if (result < 0) {
+		KGSL_CMD_ERR(device, "wait_event_interruptible_timeout "
+			"failed: %ld\n", result);
+		goto error;
+	}
+	result = 0;
+
+	old_timestamp = z180_dev->current_timestamp;
+	z180_dev->current_timestamp++;
+	*timestamp = z180_dev->current_timestamp;
+
+	z180_dev->ringbuffer.prevctx = context->id;
+
+	addcmd(&z180_dev->ringbuffer, old_timestamp, cmd + ofs, cnt);
+	kgsl_pwrscale_busy(device);
+
+	/* Make sure the next ringbuffer entry has a marker */
+	addmarker(&z180_dev->ringbuffer, z180_dev->current_timestamp);
+
+	/* monkey patch the IB so that it jumps back to the ringbuffer */
+	kgsl_sharedmem_writel(&entry->memdesc,
+		      ((sizedwords + 1) * sizeof(unsigned int)),
+		      rb_gpuaddr(z180_dev, z180_dev->current_timestamp));
+	kgsl_sharedmem_writel(&entry->memdesc,
+			      ((sizedwords + 2) * sizeof(unsigned int)),
+			      nextcnt);
+
+	/* sync memory before activating the hardware for the new command*/
+	mb();
+
+	cmd = (int)(((2) & VGV3_CONTROL_MARKADD_FMASK)
+		<< VGV3_CONTROL_MARKADD_FSHIFT);
+
+	z180_cmdwindow_write(device, ADDR_VGV3_CONTROL, cmd);
+	z180_cmdwindow_write(device, ADDR_VGV3_CONTROL, 0);
+error:
+	return (int)result;
+}
+
+static int z180_ringbuffer_init(struct kgsl_device *device)
+{
+	struct z180_device *z180_dev = Z180_DEVICE(device);
+	memset(&z180_dev->ringbuffer, 0, sizeof(struct z180_ringbuffer));
+	z180_dev->ringbuffer.prevctx = Z180_INVALID_CONTEXT;
+	return kgsl_allocate_contiguous(&z180_dev->ringbuffer.cmdbufdesc,
+		Z180_RB_SIZE);
+}
+
+static void z180_ringbuffer_close(struct kgsl_device *device)
+{
+	struct z180_device *z180_dev = Z180_DEVICE(device);
+	kgsl_sharedmem_free(&z180_dev->ringbuffer.cmdbufdesc);
+	memset(&z180_dev->ringbuffer, 0, sizeof(struct z180_ringbuffer));
+}
+
+static int __devinit z180_probe(struct platform_device *pdev)
+{
+	int status = -EINVAL;
+	struct kgsl_device *device = NULL;
+	struct z180_device *z180_dev;
+
+	device = (struct kgsl_device *)pdev->id_entry->driver_data;
+	device->parentdev = &pdev->dev;
+
+	z180_dev = Z180_DEVICE(device);
+
+	status = z180_ringbuffer_init(device);
+	if (status != 0)
+		goto error;
+
+	status = kgsl_device_platform_probe(device);
+	if (status)
+		goto error_close_ringbuffer;
+
+	kgsl_pwrscale_init(device);
+	kgsl_pwrscale_attach_policy(device, Z180_DEFAULT_PWRSCALE_POLICY);
+
+	return status;
+
+error_close_ringbuffer:
+	z180_ringbuffer_close(device);
+error:
+	device->parentdev = NULL;
+	return status;
+}
+
+static int __devexit z180_remove(struct platform_device *pdev)
+{
+	struct kgsl_device *device = NULL;
+
+	device = (struct kgsl_device *)pdev->id_entry->driver_data;
+
+	kgsl_pwrscale_close(device);
+	kgsl_device_platform_remove(device);
+
+	z180_ringbuffer_close(device);
+
+	return 0;
+}
+
+static int z180_start(struct kgsl_device *device, unsigned int init_ram)
+{
+	int status = 0;
+
+	kgsl_pwrctrl_set_state(device, KGSL_STATE_INIT);
+
+	kgsl_pwrctrl_enable(device);
+
+	/* Set interrupts to 0 to ensure a good state */
+	z180_regwrite(device, (ADDR_VGC_IRQENABLE >> 2), 0x0);
+
+	kgsl_mh_start(device);
+
+	status = kgsl_mmu_start(device);
+	if (status)
+		goto error_clk_off;
+
+	z180_cmdstream_start(device, init_ram);
+
+	mod_timer(&device->idle_timer, jiffies + FIRST_TIMEOUT);
+	kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_ON);
+	device->ftbl->irqctrl(device, 1);
+	return 0;
+
+error_clk_off:
+	z180_regwrite(device, (ADDR_VGC_IRQENABLE >> 2), 0);
+	kgsl_pwrctrl_disable(device);
+	return status;
+}
+
+static int z180_stop(struct kgsl_device *device)
+{
+	device->ftbl->irqctrl(device, 0);
+	z180_idle(device, KGSL_TIMEOUT_DEFAULT);
+
+	del_timer_sync(&device->idle_timer);
+
+	kgsl_mmu_stop(&device->mmu);
+
+	/* Disable the clocks before the power rail. */
+	kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF);
+
+	kgsl_pwrctrl_disable(device);
+
+	return 0;
+}
+
+static int z180_getproperty(struct kgsl_device *device,
+				enum kgsl_property_type type,
+				void *value,
+				unsigned int sizebytes)
+{
+	int status = -EINVAL;
+
+	switch (type) {
+	case KGSL_PROP_DEVICE_INFO:
+	{
+		struct kgsl_devinfo devinfo;
+
+		if (sizebytes != sizeof(devinfo)) {
+			status = -EINVAL;
+			break;
+		}
+
+		memset(&devinfo, 0, sizeof(devinfo));
+		devinfo.device_id = device->id+1;
+		devinfo.chip_id = 0;
+		devinfo.mmu_enabled = kgsl_mmu_enabled();
+
+		if (copy_to_user(value, &devinfo, sizeof(devinfo)) !=
+				0) {
+			status = -EFAULT;
+			break;
+		}
+		status = 0;
+	}
+	break;
+	case KGSL_PROP_MMU_ENABLE:
+		{
+			int mmu_prop = kgsl_mmu_enabled();
+			if (sizebytes != sizeof(int)) {
+				status = -EINVAL;
+				break;
+			}
+			if (copy_to_user(value, &mmu_prop, sizeof(mmu_prop))) {
+				status = -EFAULT;
+				break;
+			}
+			status = 0;
+		}
+		break;
+
+	default:
+		KGSL_DRV_ERR(device, "invalid property: %d\n", type);
+		status = -EINVAL;
+	}
+	return status;
+}
+
+static unsigned int z180_isidle(struct kgsl_device *device)
+{
+	struct z180_device *z180_dev = Z180_DEVICE(device);
+
+	return (timestamp_cmp(z180_dev->timestamp,
+		z180_dev->current_timestamp) == 0) ? true : false;
+}
+
+static int z180_suspend_context(struct kgsl_device *device)
+{
+	struct z180_device *z180_dev = Z180_DEVICE(device);
+
+	z180_dev->ringbuffer.prevctx = Z180_INVALID_CONTEXT;
+
+	return 0;
+}
+
+/* Not all Z180 registers are directly accessible.
+ * The _z180_(read|write)_simple functions below handle the ones that are.
+ */
+static void _z180_regread_simple(struct kgsl_device *device,
+				unsigned int offsetwords,
+				unsigned int *value)
+{
+	unsigned int *reg;
+
+	BUG_ON(offsetwords * sizeof(uint32_t) >= device->reg_len);
+
+	reg = (unsigned int *)(device->reg_virt + (offsetwords << 2));
+
+	/*ensure this read finishes before the next one.
+	 * i.e. act like normal readl() */
+	*value = __raw_readl(reg);
+	rmb();
+
+}
+
+static void _z180_regwrite_simple(struct kgsl_device *device,
+				 unsigned int offsetwords,
+				 unsigned int value)
+{
+	unsigned int *reg;
+
+	BUG_ON(offsetwords*sizeof(uint32_t) >= device->reg_len);
+
+	reg = (unsigned int *)(device->reg_virt + (offsetwords << 2));
+	kgsl_cffdump_regwrite(device->id, offsetwords << 2, value);
+	/*ensure previous writes post before this one,
+	 * i.e. act like normal writel() */
+	wmb();
+	__raw_writel(value, reg);
+}
+
+
+/* The MH registers must be accessed through via a 2 step write, (read|write)
+ * process. These registers may be accessed from interrupt context during
+ * the handling of MH or MMU error interrupts. Therefore a spin lock is used
+ * to ensure that the 2 step sequence is not interrupted.
+ */
+static void _z180_regread_mmu(struct kgsl_device *device,
+			     unsigned int offsetwords,
+			     unsigned int *value)
+{
+	struct z180_device *z180_dev = Z180_DEVICE(device);
+	unsigned long flags;
+
+	spin_lock_irqsave(&z180_dev->cmdwin_lock, flags);
+	_z180_regwrite_simple(device, (ADDR_VGC_MH_READ_ADDR >> 2),
+				offsetwords);
+	_z180_regread_simple(device, (ADDR_VGC_MH_DATA_ADDR >> 2), value);
+	spin_unlock_irqrestore(&z180_dev->cmdwin_lock, flags);
+}
+
+
+static void _z180_regwrite_mmu(struct kgsl_device *device,
+			      unsigned int offsetwords,
+			      unsigned int value)
+{
+	struct z180_device *z180_dev = Z180_DEVICE(device);
+	unsigned int cmdwinaddr;
+	unsigned long flags;
+
+	cmdwinaddr = ((Z180_CMDWINDOW_MMU << Z180_CMDWINDOW_TARGET_SHIFT) &
+			Z180_CMDWINDOW_TARGET_MASK);
+	cmdwinaddr |= ((offsetwords << Z180_CMDWINDOW_ADDR_SHIFT) &
+			Z180_CMDWINDOW_ADDR_MASK);
+
+	spin_lock_irqsave(&z180_dev->cmdwin_lock, flags);
+	_z180_regwrite_simple(device, ADDR_VGC_MMUCOMMANDSTREAM >> 2,
+			     cmdwinaddr);
+	_z180_regwrite_simple(device, ADDR_VGC_MMUCOMMANDSTREAM >> 2, value);
+	spin_unlock_irqrestore(&z180_dev->cmdwin_lock, flags);
+}
+
+/* the rest of the code doesn't want to think about if it is writing mmu
+ * registers or normal registers so handle it here
+ */
+static void z180_regread(struct kgsl_device *device,
+			unsigned int offsetwords,
+			unsigned int *value)
+{
+	if (!in_interrupt())
+		kgsl_pre_hwaccess(device);
+
+	if ((offsetwords >= MH_ARBITER_CONFIG &&
+	     offsetwords <= MH_AXI_HALT_CONTROL) ||
+	    (offsetwords >= MH_MMU_CONFIG &&
+	     offsetwords <= MH_MMU_MPU_END)) {
+		_z180_regread_mmu(device, offsetwords, value);
+	} else {
+		_z180_regread_simple(device, offsetwords, value);
+	}
+}
+
+static void z180_regwrite(struct kgsl_device *device,
+				unsigned int offsetwords,
+				unsigned int value)
+{
+	if (!in_interrupt())
+		kgsl_pre_hwaccess(device);
+
+	if ((offsetwords >= MH_ARBITER_CONFIG &&
+	     offsetwords <= MH_CLNT_INTF_CTRL_CONFIG2) ||
+	    (offsetwords >= MH_MMU_CONFIG &&
+	     offsetwords <= MH_MMU_MPU_END)) {
+		_z180_regwrite_mmu(device, offsetwords, value);
+	} else {
+		_z180_regwrite_simple(device, offsetwords, value);
+	}
+}
+
+static void z180_cmdwindow_write(struct kgsl_device *device,
+		unsigned int addr, unsigned int data)
+{
+	unsigned int cmdwinaddr;
+
+	cmdwinaddr = ((Z180_CMDWINDOW_2D << Z180_CMDWINDOW_TARGET_SHIFT) &
+			Z180_CMDWINDOW_TARGET_MASK);
+	cmdwinaddr |= ((addr << Z180_CMDWINDOW_ADDR_SHIFT) &
+			Z180_CMDWINDOW_ADDR_MASK);
+
+	z180_regwrite(device, ADDR_VGC_COMMANDSTREAM >> 2, cmdwinaddr);
+	z180_regwrite(device, ADDR_VGC_COMMANDSTREAM >> 2, data);
+}
+
+static unsigned int z180_readtimestamp(struct kgsl_device *device,
+		struct kgsl_context *context, enum kgsl_timestamp_type type)
+{
+	struct z180_device *z180_dev = Z180_DEVICE(device);
+	(void)context;
+	/* get current EOP timestamp */
+	return z180_dev->timestamp;
+}
+
+static int z180_waittimestamp(struct kgsl_device *device,
+				struct kgsl_context *context,
+				unsigned int timestamp,
+				unsigned int msecs)
+{
+	int status = -EINVAL;
+
+	/* Don't wait forever, set a max (10 sec) value for now */
+	if (msecs == -1)
+		msecs = 10 * MSEC_PER_SEC;
+
+	mutex_unlock(&device->mutex);
+	status = z180_wait(device, context, timestamp, msecs);
+	mutex_lock(&device->mutex);
+
+	return status;
+}
+
+static int z180_wait(struct kgsl_device *device,
+				struct kgsl_context *context,
+				unsigned int timestamp,
+				unsigned int msecs)
+{
+	int status = -EINVAL;
+	long timeout = 0;
+
+	timeout = wait_io_event_interruptible_timeout(
+			device->wait_queue,
+			kgsl_check_timestamp(device, context, timestamp),
+			msecs_to_jiffies(msecs));
+
+	if (timeout > 0)
+		status = 0;
+	else if (timeout == 0) {
+		status = -ETIMEDOUT;
+		kgsl_pwrctrl_set_state(device, KGSL_STATE_HUNG);
+	} else
+		status = timeout;
+
+	return status;
+}
+
+static void
+z180_drawctxt_destroy(struct kgsl_device *device,
+			  struct kgsl_context *context)
+{
+	struct z180_device *z180_dev = Z180_DEVICE(device);
+
+	z180_idle(device, KGSL_TIMEOUT_DEFAULT);
+
+	if (z180_dev->ringbuffer.prevctx == context->id) {
+		z180_dev->ringbuffer.prevctx = Z180_INVALID_CONTEXT;
+		device->mmu.hwpagetable = device->mmu.defaultpagetable;
+		kgsl_setstate(&device->mmu, KGSL_MMUFLAGS_PTUPDATE);
+	}
+}
+
+static void z180_power_stats(struct kgsl_device *device,
+			    struct kgsl_power_stats *stats)
+{
+	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+	s64 tmp = ktime_to_us(ktime_get());
+
+	if (pwr->time == 0) {
+		pwr->time = tmp;
+		stats->total_time = 0;
+		stats->busy_time = 0;
+	} else {
+		stats->total_time = tmp - pwr->time;
+		pwr->time = tmp;
+		stats->busy_time = tmp - device->on_time;
+		device->on_time = tmp;
+	}
+}
+
+static void z180_irqctrl(struct kgsl_device *device, int state)
+{
+	/* Control interrupts for Z180 and the Z180 MMU */
+
+	if (state) {
+		z180_regwrite(device, (ADDR_VGC_IRQENABLE >> 2), 3);
+		z180_regwrite(device, MH_INTERRUPT_MASK, KGSL_MMU_INT_MASK);
+	} else {
+		z180_regwrite(device, (ADDR_VGC_IRQENABLE >> 2), 0);
+		z180_regwrite(device, MH_INTERRUPT_MASK, 0);
+	}
+}
+
+static unsigned int z180_gpuid(struct kgsl_device *device)
+{
+	/* Standard KGSL gpuid format:
+	 * top word is 0x0002 for 2D or 0x0003 for 3D
+	 * Bottom word is core specific identifer
+	 */
+
+	return (0x0002 << 16) | 180;
+}
+
+static const struct kgsl_functable z180_functable = {
+	/* Mandatory functions */
+	.regread = z180_regread,
+	.regwrite = z180_regwrite,
+	.idle = z180_idle,
+	.isidle = z180_isidle,
+	.suspend_context = z180_suspend_context,
+	.start = z180_start,
+	.stop = z180_stop,
+	.getproperty = z180_getproperty,
+	.waittimestamp = z180_waittimestamp,
+	.readtimestamp = z180_readtimestamp,
+	.issueibcmds = z180_cmdstream_issueibcmds,
+	.setup_pt = z180_setup_pt,
+	.cleanup_pt = z180_cleanup_pt,
+	.power_stats = z180_power_stats,
+	.irqctrl = z180_irqctrl,
+	.gpuid = z180_gpuid,
+	.irq_handler = z180_irq_handler,
+	/* Optional functions */
+	.drawctxt_create = NULL,
+	.drawctxt_destroy = z180_drawctxt_destroy,
+	.ioctl = NULL,
+};
+
+static struct platform_device_id z180_id_table[] = {
+	{ DEVICE_2D0_NAME, (kernel_ulong_t)&device_2d0.dev, },
+	{ DEVICE_2D1_NAME, (kernel_ulong_t)&device_2d1.dev, },
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, z180_id_table);
+
+static struct platform_driver z180_platform_driver = {
+	.probe = z180_probe,
+	.remove = __devexit_p(z180_remove),
+	.suspend = kgsl_suspend_driver,
+	.resume = kgsl_resume_driver,
+	.id_table = z180_id_table,
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = DEVICE_2D_NAME,
+		.pm = &kgsl_pm_ops,
+	}
+};
+
+static int __init kgsl_2d_init(void)
+{
+	return platform_driver_register(&z180_platform_driver);
+}
+
+static void __exit kgsl_2d_exit(void)
+{
+	platform_driver_unregister(&z180_platform_driver);
+}
+
+module_init(kgsl_2d_init);
+module_exit(kgsl_2d_exit);
+
+MODULE_DESCRIPTION("2D Graphics driver");
+MODULE_VERSION("1.2");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:kgsl_2d");
diff --git a/drivers/gpu/msm/z180.h b/drivers/gpu/msm/z180.h
new file mode 100644
index 0000000..e5c5ef3
--- /dev/null
+++ b/drivers/gpu/msm/z180.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __Z180_H
+#define __Z180_H
+
+#include "kgsl_device.h"
+
+#define DEVICE_2D_NAME "kgsl-2d"
+#define DEVICE_2D0_NAME "kgsl-2d0"
+#define DEVICE_2D1_NAME "kgsl-2d1"
+
+#define Z180_DEFAULT_PWRSCALE_POLICY  NULL
+
+struct z180_ringbuffer {
+	unsigned int prevctx;
+	struct kgsl_memdesc      cmdbufdesc;
+};
+
+struct z180_device {
+	struct kgsl_device dev;    /* Must be first field in this struct */
+	int current_timestamp;
+	int timestamp;
+	struct z180_ringbuffer ringbuffer;
+	spinlock_t cmdwin_lock;
+};
+
+#endif /* __Z180_H */
diff --git a/drivers/gpu/msm/z180_reg.h b/drivers/gpu/msm/z180_reg.h
new file mode 100644
index 0000000..5b6c001
--- /dev/null
+++ b/drivers/gpu/msm/z180_reg.h
@@ -0,0 +1,49 @@
+/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __Z80_REG_H
+#define __Z80_REG_H
+
+#define REG_VGC_IRQSTATUS__MH_MASK                         0x00000001L
+#define REG_VGC_IRQSTATUS__G2D_MASK                        0x00000002L
+#define REG_VGC_IRQSTATUS__FIFO_MASK                       0x00000004L
+
+#define	MH_ARBITER_CONFIG__SAME_PAGE_GRANULARITY__SHIFT    0x00000006
+#define	MH_ARBITER_CONFIG__L1_ARB_ENABLE__SHIFT            0x00000007
+#define	MH_ARBITER_CONFIG__L1_ARB_HOLD_ENABLE__SHIFT       0x00000008
+#define	MH_ARBITER_CONFIG__L2_ARB_CONTROL__SHIFT           0x00000009
+#define	MH_ARBITER_CONFIG__PAGE_SIZE__SHIFT                0x0000000a
+#define	MH_ARBITER_CONFIG__TC_REORDER_ENABLE__SHIFT        0x0000000d
+#define	MH_ARBITER_CONFIG__TC_ARB_HOLD_ENABLE__SHIFT       0x0000000e
+#define	MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT_ENABLE__SHIFT   0x0000000f
+#define	MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT__SHIFT          0x00000010
+#define	MH_ARBITER_CONFIG__CP_CLNT_ENABLE__SHIFT           0x00000016
+#define	MH_ARBITER_CONFIG__VGT_CLNT_ENABLE__SHIFT          0x00000017
+#define	MH_ARBITER_CONFIG__TC_CLNT_ENABLE__SHIFT           0x00000018
+#define	MH_ARBITER_CONFIG__RB_CLNT_ENABLE__SHIFT           0x00000019
+#define	MH_ARBITER_CONFIG__PA_CLNT_ENABLE__SHIFT           0x0000001a
+
+#define ADDR_VGC_MH_READ_ADDR            0x0510
+#define ADDR_VGC_MH_DATA_ADDR            0x0518
+#define ADDR_VGC_COMMANDSTREAM           0x0000
+#define ADDR_VGC_IRQENABLE               0x0438
+#define ADDR_VGC_IRQSTATUS               0x0418
+#define ADDR_VGC_IRQ_ACTIVE_CNT          0x04E0
+#define ADDR_VGC_MMUCOMMANDSTREAM        0x03FC
+#define ADDR_VGV3_CONTROL                0x0070
+#define ADDR_VGV3_LAST                   0x007F
+#define ADDR_VGV3_MODE                   0x0071
+#define ADDR_VGV3_NEXTADDR               0x0075
+#define ADDR_VGV3_NEXTCMD                0x0076
+#define ADDR_VGV3_WRITEADDR              0x0072
+
+#endif /* __Z180_REG_H */
diff --git a/drivers/gpu/msm/z180_trace.c b/drivers/gpu/msm/z180_trace.c
new file mode 100644
index 0000000..29b519c
--- /dev/null
+++ b/drivers/gpu/msm/z180_trace.c
@@ -0,0 +1,20 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "kgsl.h"
+#include "z180.h"
+#include "z180_reg.h"
+
+/* Instantiate tracepoints */
+#define CREATE_TRACE_POINTS
+#include "z180_trace.h"
diff --git a/drivers/gpu/msm/z180_trace.h b/drivers/gpu/msm/z180_trace.h
new file mode 100644
index 0000000..fbe1fe5
--- /dev/null
+++ b/drivers/gpu/msm/z180_trace.h
@@ -0,0 +1,60 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#if !defined(_Z180_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _Z180_TRACE_H
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM kgsl
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE z180_trace
+
+#include <linux/tracepoint.h>
+
+struct kgsl_device;
+
+/*
+ * Tracepoint for z180 irq. Includes status info
+ */
+TRACE_EVENT(kgsl_z180_irq_status,
+
+	TP_PROTO(struct kgsl_device *device, unsigned int status),
+
+	TP_ARGS(device, status),
+
+	TP_STRUCT__entry(
+		__string(device_name, device->name)
+		__field(unsigned int, status)
+	),
+
+	TP_fast_assign(
+		__assign_str(device_name, device->name);
+		__entry->status = status;
+	),
+
+	TP_printk(
+		"d_name=%s status=%s",
+		__get_str(device_name),
+		__entry->status ? __print_flags(__entry->status, "|",
+			{ REG_VGC_IRQSTATUS__MH_MASK, "MH" },
+			{ REG_VGC_IRQSTATUS__G2D_MASK, "G2D" },
+			{ REG_VGC_IRQSTATUS__FIFO_MASK, "FIFO" }) : "None"
+	)
+);
+
+#endif /* _Z180_TRACE_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/drivers/gud/Kconfig b/drivers/gud/Kconfig
new file mode 100644
index 0000000..3a241b7
--- /dev/null
+++ b/drivers/gud/Kconfig
@@ -0,0 +1,32 @@
+#
+# MobiCore configuration
+#
+config MOBICORE_SUPPORT
+	tristate "Linux MobiCore Support"
+	#depends on ARM_TRUSTZONE
+	---help---
+	  Enable Linux Kernel MobiCore Support
+
+config MOBICORE_DEBUG
+    bool "MobiCore Module debug mode"
+    depends on MOBICORE_SUPPORT
+    ---help---
+      Enable Debug mode in the MobiCore Driver.
+      It enables printing information about mobicore operations
+
+config MOBICORE_VERBOSE
+    bool "MobiCore Module verbose debug mode"
+    depends on MOBICORE_DEBUG
+    ---help---
+      Enable Verbose Debug mode in the MobiCore Driver.
+      It enables printing extra information about mobicore operations
+      Beware: this is only useful for debuging deep in the driver because
+      it prints too much logs
+
+
+config MOBICORE_API
+    tristate "Linux MobiCore API"
+    depends on MOBICORE_SUPPORT
+    ---help---
+      Enable Linux Kernel MobiCore API
+
diff --git a/drivers/gud/Makefile b/drivers/gud/Makefile
new file mode 100644
index 0000000..ea212c5
--- /dev/null
+++ b/drivers/gud/Makefile
@@ -0,0 +1,31 @@
+#
+# Makefile for the kernel mobicore drivers
+#
+GUD_ROOT_FOLDER := drivers/gud
+# add our modules to kernel.
+obj-$(CONFIG_MOBICORE_API) += mckernelapi.o
+obj-$(CONFIG_MOBICORE_SUPPORT) += mcdrvmodule.o
+
+mcdrvmodule-objs := mobicore_driver/logging.o mobicore_driver/main.o
+
+mckernelapi-objs := mobicore_kernelapi/main.o \
+		mobicore_kernelapi/clientlib.o \
+		mobicore_kernelapi/device.o \
+		mobicore_kernelapi/session.o \
+		mobicore_kernelapi/connection.o
+
+# Release mode by default
+ccflags-y := -DNDEBUG
+ccflags-y += -Wno-declaration-after-statement
+
+ccflags-$(CONFIG_MOBICORE_DEBUG) += -DDEBUG
+ccflags-$(CONFIG_MOBICORE_VERBOSE) += -DDEBUG_VERBOSE
+
+# Choose one platform from the folder
+MOBICORE_PLATFORM := $(shell (ls -1 $(PWD)/$(GUD_ROOT_FOLDER)/mobicore_driver/platforms | tail -1) )
+# Use the available platform folder
+ccflags-y += -I$(GUD_ROOT_FOLDER)/mobicore_driver/platforms/$(MOBICORE_PLATFORM)
+
+
+ccflags-y += -I$(GUD_ROOT_FOLDER)/mobicore_driver/public
+ccflags-y += -I$(GUD_ROOT_FOLDER)/mobicore_kernelapi/include
diff --git a/drivers/gud/README b/drivers/gud/README
new file mode 100644
index 0000000..c6d62a1
--- /dev/null
+++ b/drivers/gud/README
@@ -0,0 +1,6 @@
+MobiCore is an operating system being shipped with TZBSP
+on msm chipsets. MobiCore consists of several components in
+the secure world(TrustZone) and non-secure world(linux
+kernel, Android user space). The MobiCore driver
+communicates with the MobiCore kernel that exists in
+TrustZone.
diff --git a/drivers/gud/mobicore_driver/build_tag.h b/drivers/gud/mobicore_driver/build_tag.h
new file mode 100644
index 0000000..43541bb
--- /dev/null
+++ b/drivers/gud/mobicore_driver/build_tag.h
@@ -0,0 +1,29 @@
+/**
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2012-2012 -->
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *	notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *	notice, this list of conditions and the following disclaimer in the
+ *	documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *	products derived from this software without specific prior
+ *	written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#define MOBICORE_COMPONENT_BUILD_TAG "*** GC_MSM8960_Release_V010 ###"
diff --git a/drivers/gud/mobicore_driver/logging.c b/drivers/gud/mobicore_driver/logging.c
new file mode 100644
index 0000000..eb44c8a
--- /dev/null
+++ b/drivers/gud/mobicore_driver/logging.c
@@ -0,0 +1,336 @@
+/** MobiCore driver module.(interface to the secure world SWD)
+ * @addtogroup MCD_MCDIMPL_KMOD_LOGGING MobiCore Driver Logging Subsystem.
+ * @ingroup  MCD_MCDIMPL_KMOD
+ * @{
+ * @file
+ * MobiCore Driver Logging Subsystem.
+ * The logging subsytem provides the interface between the Mobicore trace
+ * buffer and the Linux log
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include "mc_drv_module.h"
+#include "mc_drv_module_linux_api.h"
+#include "mc_drv_module_fastcalls.h"
+
+/* Default len of the log ring buffer 256KB*/
+#define LOG_BUF_SIZE	(64 * PAGE_SIZE)
+
+/* Max Len of a log line for printing */
+#define LOG_LINE_SIZE	256
+
+static uint32_t log_size = LOG_BUF_SIZE;
+module_param(log_size, uint, 0);
+MODULE_PARM_DESC(log_size, " Size of the MobiCore log ringbuffer "
+						"(or 256KB default).");
+
+/*----------------------------------------------------------------------------*/
+/* Definitions for log version 2 */
+#define LOG_TYPE_MASK				(0x0007)
+#define LOG_TYPE_CHAR				0
+#define LOG_TYPE_INTEGER			1
+/* Field length */
+#define LOG_LENGTH_MASK				(0x00F8)
+#define LOG_LENGTH_SHIFT			3
+/* Extra attributes */
+#define LOG_EOL					(0x0100)
+#define LOG_INTEGER_DECIMAL			(0x0200)
+#define LOG_INTEGER_SIGNED			(0x0400)
+
+struct logmsg_struct {
+	/* Type and format of data */
+	uint16_t ctrl;
+	/* Unique value for each event source */
+	uint16_t source;
+	/* Value, if any */
+	uint32_t log_data;
+};
+
+/** MobiCore log previous position */
+static uint32_t log_pos;
+/** MobiCore log buffer structure */
+static struct mc_trace_buf *log_buf;
+/** Log Thread task structure */
+struct task_struct *log_thread;
+/** Log Line buffer */
+static char *log_line;
+
+static void log_msg(struct logmsg_struct *msg);
+
+/*----------------------------------------------------------------------------*/
+static void log_eol(void)
+{
+	if (!strnlen(log_line, LOG_LINE_SIZE))
+		return;
+	printk(KERN_INFO "%s\n", log_line);
+	log_line[0] = 0;
+}
+/*----------------------------------------------------------------------------*/
+/**
+ * Put a char to the log line if there is enough space if not then also
+ * output the line. Assume nobody else is updating the line! */
+static void log_char(char ch)
+{
+	uint32_t len;
+	if (ch == '\n' || ch == '\r') {
+		log_eol();
+		return;
+	}
+
+	if (strnlen(log_line, LOG_LINE_SIZE) >= LOG_LINE_SIZE - 1) {
+		printk(KERN_INFO "%s\n", log_line);
+		log_line[0] = 0;
+	}
+
+	len = strnlen(log_line, LOG_LINE_SIZE);
+	log_line[len] = ch;
+	log_line[len + 1] = 0;
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ * Put a string to the log line if there is enough space if not then also
+ * output the line. Assume nobody else is updating the line! */
+static void log_str(const char *s)
+{
+	int i;
+	for (i = 0; i < strnlen(s, LOG_LINE_SIZE); i++)
+		log_char(s[i]);
+}
+
+/*----------------------------------------------------------------------------*/
+static uint32_t process_v1log(void)
+{
+	char *last_char = log_buf->buff + log_buf->write_pos;
+	char *buff = log_buf->buff + log_pos;
+	while (buff != last_char) {
+		log_char(*(buff++));
+		/* Wrap around */
+		if (buff - (char *)log_buf >= log_size)
+			buff = log_buf->buff;
+	}
+	return buff - log_buf->buff;
+}
+
+/*----------------------------------------------------------------------------*/
+static uint32_t process_v2log(void)
+{
+	char *last_msg = log_buf->buff + log_buf->write_pos;
+	char *buff = log_buf->buff + log_pos;
+	while (buff != last_msg) {
+		log_msg((struct logmsg_struct *)buff);
+		buff += sizeof(struct logmsg_struct);
+		/* Wrap around */
+		if (buff + sizeof(struct logmsg_struct) >
+			(char *)log_buf + log_size)
+			buff = log_buf->buff;
+	}
+	return buff - log_buf->buff;
+}
+
+static const uint8_t HEX2ASCII[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
+				'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+/*----------------------------------------------------------------------------*/
+static void dbg_raw_nro(uint32_t format, uint32_t value)
+{
+	int digits = 1;
+	uint32_t base = (format & LOG_INTEGER_DECIMAL) ? 10 : 16;
+	int width = (format & LOG_LENGTH_MASK) >> LOG_LENGTH_SHIFT;
+	int negative = FALSE;
+	uint32_t digit_base = 1;
+
+	if ((format & LOG_INTEGER_SIGNED) != 0 && ((signed int)value) < 0) {
+			negative = TRUE;
+			value = (uint32_t)(-(signed int)value);
+			width--;
+	}
+
+	/* Find length and divider to get largest digit */
+	while (value / digit_base >= base) {
+			digit_base *= base;
+			digits++;
+	}
+
+	if (width > digits) {
+		char ch = (base == 10) ? ' ' : '0';
+		while (width > digits) {
+			log_char(ch);
+			width--;
+		}
+	}
+
+	if (negative)
+		log_char('-');
+
+	while (digits-- > 0) {
+		uint32_t d = value / digit_base;
+		log_char(HEX2ASCII[d]);
+		value = value - d * digit_base;
+		digit_base /= base;
+	}
+}
+
+/*----------------------------------------------------------------------------*/
+static void log_msg(struct logmsg_struct *msg)
+{
+	unsigned char msgtxt[5];
+	int mpos = 0;
+	switch (msg->ctrl & LOG_TYPE_MASK) {
+	case LOG_TYPE_CHAR: {
+		uint32_t ch;
+		ch = msg->log_data;
+		while (ch != 0) {
+			msgtxt[mpos++] = ch&0xFF;
+			ch >>= 8;
+		}
+		msgtxt[mpos] = 0;
+		log_str(msgtxt);
+		break;
+	}
+	case LOG_TYPE_INTEGER: {
+		dbg_raw_nro(msg->ctrl, msg->log_data);
+		break;
+	}
+	default:
+		break;
+	}
+	if (msg->ctrl & LOG_EOL)
+		log_eol();
+}
+
+/*----------------------------------------------------------------------------*/
+static int log_worker(void *p)
+{
+	if (log_buf == NULL)
+		return -EFAULT;
+
+	/* The thread should have never started */
+	if (log_buf == NULL)
+		return -EFAULT;
+
+	while (!kthread_should_stop()) {
+		if (log_buf->write_pos == log_pos)
+			schedule_timeout_interruptible(MAX_SCHEDULE_TIMEOUT);
+
+		switch (log_buf->version) {
+		case 1:
+			log_pos = process_v1log();
+			break;
+		case 2:
+			log_pos = process_v2log();
+			break;
+		default:
+			MCDRV_DBG_ERROR("Unknown Mobicore log data "
+				"version %d logging disabled.",
+				log_buf->version);
+			log_pos = log_buf->write_pos;
+			/* Stop the thread as we have no idea what
+			 * happens next */
+			return -EFAULT;
+		}
+	}
+	MCDRV_DBG("Logging thread stopped!");
+	return 0;
+}
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * Wakeup the log reader thread
+ * This should be called from the places where calls into MobiCore have
+ * generated some logs(eg, yield, SIQ...)
+ */
+void mobicore_log_read(void)
+{
+	if (log_thread == NULL || IS_ERR(log_thread))
+		return;
+
+	wake_up_process(log_thread);
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ * Setup mobicore kernel log. It assumes it's running on CORE 0!
+ * The fastcall will complain is that is not the case!
+ */
+long mobicore_log_setup(void *data)
+{
+	unsigned long phys_log_buf;
+	union fc_generic fc_log;
+
+	log_pos = 0;
+	log_buf = NULL;
+	log_thread = NULL;
+	log_line = NULL;
+
+	/* Sanity check for the log size */
+	if (log_size < PAGE_SIZE)
+		return -EFAULT;
+	else
+		log_size =
+			get_nr_of_pages_for_buffer(NULL, log_size) * PAGE_SIZE;
+
+	log_line = kzalloc(LOG_LINE_SIZE, GFP_KERNEL);
+	if (IS_ERR(log_line)) {
+		MCDRV_DBG_ERROR("failed to allocate log line!");
+		return -ENOMEM;
+	}
+
+	log_thread = kthread_create(log_worker, NULL, "mobicore_log");
+	if (IS_ERR(log_thread)) {
+		MCDRV_DBG_ERROR("mobicore log thread creation failed!");
+		return -EFAULT;
+	}
+
+	log_pos = 0;
+	log_buf = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
+					size_to_order(log_size));
+	if (!log_buf) {
+		MCDRV_DBG_ERROR("Failed to get page for logger!");
+		return -ENOMEM;
+	}
+	phys_log_buf = virt_to_phys(log_buf);
+
+	memset(&fc_log, 0, sizeof(fc_log));
+	fc_log.as_in.cmd      = MC_FC_NWD_TRACE;
+	fc_log.as_in.param[0] = phys_log_buf;
+	fc_log.as_in.param[1] = log_size;
+
+	MCDRV_DBG("fc_log virt=%p phys=%p ", log_buf, (void *)phys_log_buf);
+	mc_fastcall(&fc_log);
+	MCDRV_DBG("fc_log out ret=0x%08x", fc_log.as_out.ret);
+	/* If the setup failed we must free the memory allocated */
+	if (fc_log.as_out.ret) {
+		MCDRV_DBG_ERROR("MobiCore shared traces setup failed!");
+		kthread_stop(log_thread);
+		free_pages((unsigned long)log_buf, size_to_order(log_size));
+
+		log_buf = NULL;
+		log_thread = NULL;
+		return -EIO;
+	}
+
+	MCDRV_DBG("fc_log Logger version %u\n", log_buf->version);
+	return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ * Free kernel log componenets.
+ * ATTN: We can't free the log buffer because it's also in use by MobiCore and
+ * even if the module is unloaded MobiCore is still running.
+ */
+void mobicore_log_free(void)
+{
+	if (log_thread && !IS_ERR(log_thread)) {
+		/* We don't really care what the thread returns for exit */
+		kthread_stop(log_thread);
+	}
+
+	kfree(log_line);
+}
diff --git a/drivers/gud/mobicore_driver/main.c b/drivers/gud/mobicore_driver/main.c
new file mode 100644
index 0000000..8a8ea1e
--- /dev/null
+++ b/drivers/gud/mobicore_driver/main.c
@@ -0,0 +1,2869 @@
+/** MobiCore driver module.(interface to the secure world SWD)
+ * @addtogroup MCD_MCDIMPL_KMOD_IMPL
+ * @{
+ * @file
+ * MobiCore Driver Kernel Module.
+ * This module is written as a Linux device driver.
+ * This driver represents the command proxy on the lowest layer, from the
+ * secure world to the non secure world, and vice versa.
+ * This driver is located in the non secure world (Linux).
+ * This driver offers IOCTL commands, for access to the secure world, and has
+ * the interface from the secure world to the normal world.
+ * The access to the driver is possible with a file descriptor,
+ * which has to be created by the fd = open(/dev/mobicore) command.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include "mc_drv_module.h"
+#include "mc_drv_module_linux_api.h"
+#include "mc_drv_module_android.h"
+#include "mc_drv_module_fastcalls.h"
+#include "public/mc_kernel_api.h"
+
+/* Initial value for the daemon sempahore signaling */
+#define DAEMON_SEM_VAL 0
+
+/** MobiCore interrupt context data */
+static struct mc_drv_kmod_ctx	mc_drv_kmod_ctx;
+
+/** MobiCore MCI information */
+static uint32_t mci_base;
+/*
+#############################################################################
+##
+## Convenience functions for Linux API functions
+##
+#############################################################################*/
+static int goto_cpu0(void);
+static int goto_all_cpu(void) __attribute__ ((unused));
+
+
+/*----------------------------------------------------------------------------*/
+static void init_and_add_to_list(
+	struct list_head *item,
+	struct list_head *list_head
+)
+{
+	INIT_LIST_HEAD(item);
+
+	list_add(item, list_head);
+}
+
+/*----------------------------------------------------------------------------*/
+/** check if CPU supports the ARM TrustZone Security Extensions
+ *	@return int TRUE or FALSE */
+static int has_security_extensions(
+	void
+)
+{
+	u32 fea = 0;
+	asm volatile(
+		"mrc p15, 0, %[fea], cr0, cr1, 0" :
+		[fea]"=r" (fea));
+
+	MCDRV_DBG_VERBOSE("CPU Features: 0x%X", fea);
+
+	/* If the CPU features ID has 0 for security features then the CPU
+	 * doesn't support TrustZone at all!
+	 */
+	if ((fea & ARM_SECURITY_EXTENSION_MASK) == 0)
+		return 0;
+
+	return 1;
+}
+
+/*----------------------------------------------------------------------------*/
+/** check if running in secure mode
+ *	@return int TRUE or FALSE */
+static int is_secure_mode(
+	void
+)
+{
+	u32 cpsr = 0, nsacr = 0;
+	asm volatile(
+		"mrc	p15, 0, %[nsacr], cr1, cr1, 2\n"
+		"mrs %[cpsr], cpsr\n" :
+		[nsacr]"=r" (nsacr),
+		[cpsr]"=r"(cpsr));
+
+	MCDRV_DBG_VERBOSE("CPRS.M = set to 0x%X\n", cpsr & ARM_CPSR_MASK);
+	MCDRV_DBG_VERBOSE("SCR.NS = set to 0x%X\n", nsacr);
+
+	/* If the NSACR contains the reset value(=0) then most likely we are
+	 * running in Secure MODE.
+	 * If the cpsr mode is set to monitor mode then we cannot load!
+	 */
+	if (nsacr == 0 || ((cpsr & ARM_CPSR_MASK) == ARM_MONITOR_MODE))
+		return 1;
+
+	return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+/** check if userland caller is privileged (aka has "root" access rights).
+	@return int TRUE or FALSE */
+static int is_userland_caller_privileged(
+	void
+) {
+	/* For some platforms we cannot run the Daemon as root - for Android
+	 * compliance tests it is not allowed, thus we assume the daemon is ran
+	 * as the system user.
+	 * In Android the system user for daemons has no particular capabilities
+	 * other than a fixed UID: AID_SYSTEM 1000
+	 * The actual number is guaranteed to be the same in all Android systems
+	 * so we will take it for granted: see android_filesystem_config.h in
+	 * the Android source tree for all UIDs and their meaning:
+	 * http://android-dls.com/wiki/index.php?title=Android_UIDs_and_GIDs
+	 */
+#ifdef MC_ANDROID_UID_CHECK
+	return current_euid() <= AID_SYSTEM;
+#else
+	/* capable should cover all possibilities, root or sudo, uid checking
+	 * was not very reliable */
+	return capable(CAP_SYS_ADMIN);
+#endif
+}
+
+
+
+/*----------------------------------------------------------------------------*/
+static void unlock_page_from_used_l2_table(
+	struct page *page
+){
+	/* REV axh: check if we should do this. */
+	SetPageDirty(page);
+
+	/* release page, old api was page_cache_release() */
+	ClearPageReserved(page);
+	put_page(page);
+}
+
+/*----------------------------------------------------------------------------*/
+/* convert L2 PTE to page pointer */
+static struct page *l2_pte_to_page(
+	pte_t pte
+) {
+	void *phys_page_addr	= (void *)((unsigned int)pte & PAGE_MASK);
+	unsigned int pfn	= addr_to_pfn(phys_page_addr);
+	struct page *page	= pfn_to_page(pfn);
+	return page;
+}
+
+/*----------------------------------------------------------------------------*/
+/* convert page pointer to L2 PTE */
+static pte_t page_to_l2_pte(
+	struct page *page
+)
+{
+	unsigned int pfn	= page_to_pfn(page);
+	void *phys_addr		= pfn_to_addr(pfn);
+	pte_t pte		= (pte_t)((unsigned int)phys_addr & PAGE_MASK);
+	return pte;
+}
+
+
+/*----------------------------------------------------------------------------*/
+static inline int lock_user_pages(
+	struct task_struct	*task,
+	void			*virt_start_page_addr,
+	int			nr_of_pages,
+	struct page		**pages
+)
+{
+	int		ret = 0;
+	int		locked_pages = 0;
+	unsigned int	i;
+
+	do {
+
+		/* lock user pages, must hold the mmap_sem to do this. */
+		down_read(&(task->mm->mmap_sem));
+		locked_pages = get_user_pages(
+					  task,
+					  task->mm,
+					  (unsigned long)virt_start_page_addr,
+					  nr_of_pages,
+					  1, /* write access */
+					  0, /* they say drivers should always
+						pass 0 here..... */
+					  pages,
+					  NULL); /* we don't need the VMAs */
+		up_read(&(task->mm->mmap_sem));
+
+		/* could as lock all pages? */
+		if (locked_pages != nr_of_pages) {
+			MCDRV_DBG_ERROR(
+				"get_user_pages() failed, "
+				"locked_pages=%d\n",
+				locked_pages);
+			ret = -ENOMEM;
+			/* check if an error has been returned. */
+			if (locked_pages < 0) {
+				ret = locked_pages;
+				locked_pages = 0;
+			}
+			break;
+		}
+
+		/* do cache maintenance on locked pages. */
+		for (i = 0; i < nr_of_pages; i++)
+			flush_dcache_page(pages[i]);
+
+	} while (FALSE);
+
+
+	if (ret != 0) {
+		/* release all locked pages. */
+		MCDRV_ASSERT(locked_pages >= 0);
+		for (i = 0; i < locked_pages; i++)
+			put_page(pages[i]);
+	}
+
+	return ret;
+
+}
+
+/*
+#############################################################################
+##
+## Driver implementation functions
+##
+#############################################################################*/
+/*----------------------------------------------------------------------------*/
+/* check if caller is MobiCore Daemon */
+static unsigned int is_caller_mc_daemon(
+	struct mc_instance *instance
+)
+{
+	return ((instance != NULL)
+		&& (mc_drv_kmod_ctx.daemon_inst == instance));
+}
+
+
+/*----------------------------------------------------------------------------*/
+/* Get process context from file pointer */
+static struct mc_instance *get_instance(
+	struct file *file
+) {
+	MCDRV_ASSERT(file != NULL);
+
+	return (struct mc_instance *)(file->private_data);
+}
+
+
+/*----------------------------------------------------------------------------*/
+/* Get a unique ID */
+static unsigned int get_mc_kmod_unique_id(
+	void
+)
+{
+	return (unsigned int)atomic_inc_return(
+			   &(mc_drv_kmod_ctx.unique_counter));
+}
+
+
+/*----------------------------------------------------------------------------*/
+/* Get kernel pointer to shared L2 table given a per-process reference */
+static struct l2table *get_l2_table_kernel_virt(
+	struct mc_used_l2_table	*used_l2table
+)
+{
+	MCDRV_ASSERT(used_l2table != NULL);
+	MCDRV_ASSERT(used_l2table->set != NULL);
+	MCDRV_ASSERT(used_l2table->set->kernel_virt != NULL);
+	return &(used_l2table->set->kernel_virt->table[used_l2table->idx]);
+}
+
+/*----------------------------------------------------------------------------*/
+/* Get physical address of a shared L2 table given a per-process reference */
+static struct l2table *get_l2_table_phys(
+	struct mc_used_l2_table  *used_l2table
+)
+{
+	MCDRV_ASSERT(used_l2table != NULL);
+	MCDRV_ASSERT(used_l2table->set != NULL);
+	MCDRV_ASSERT(used_l2table->set->phys != NULL);
+	return &(used_l2table->set->phys->table[used_l2table->idx]);
+}
+
+/*----------------------------------------------------------------------------*/
+static unsigned int is_in_use_used_l2_table(
+	struct mc_used_l2_table	*used_l2table
+)
+{
+	return ((used_l2table->flags &
+			  (MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_APP
+			   | MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC)) != 0);
+}
+
+
+
+/*----------------------------------------------------------------------------*/
+static struct mc_used_l2_table *find_used_l2_table_by_handle(
+	unsigned int	handle
+) {
+	struct mc_used_l2_table  *used_l2table;
+	struct mc_used_l2_table  *used_l2table_with_handle = NULL;
+
+	list_for_each_entry(
+		used_l2table,
+		&(mc_drv_kmod_ctx.mc_used_l2_tables),
+		list
+	) {
+		if (handle == used_l2table->handle) {
+			used_l2table_with_handle = used_l2table;
+			break;
+		}
+	}
+
+	return used_l2table_with_handle;
+}
+
+/*
+#############################################################################
+##
+## L2 Table Pool
+##
+#############################################################################*/
+
+/*----------------------------------------------------------------------------*/
+static struct mc_used_l2_table *allocate_used_l2_table(
+	struct mc_instance	*instance
+) {
+	int				ret = 0;
+	struct mc_l2_table_store	*l2table_store = NULL;
+	struct mc_l2_tables_set		*l2table_set = NULL;
+	struct mc_used_l2_table		*used_l2table = NULL;
+	struct page			*page;
+	unsigned int			i = 0;
+
+	do {
+		/* allocate a WSM L2 descriptor */
+		used_l2table  = kmalloc(sizeof(*used_l2table), GFP_KERNEL);
+		if (used_l2table == NULL) {
+			ret = -ENOMEM;
+			MCDRV_DBG_ERROR("out of memory\n");
+			break;
+		}
+		/* clean */
+		memset(used_l2table, 0, sizeof(*used_l2table));
+		used_l2table->handle = get_mc_kmod_unique_id();
+		used_l2table->owner = instance;
+
+		/* add to global list. */
+		init_and_add_to_list(
+			&(used_l2table->list),
+			&(mc_drv_kmod_ctx.mc_used_l2_tables));
+
+		/* walk though list to find free set. */
+		list_for_each_entry(
+			l2table_set,
+			&(mc_drv_kmod_ctx.mc_l2_tables_sets),
+			list
+		) {
+			for (i = 0; i < MC_DRV_KMOD_L2_TABLE_PER_PAGES; i++) {
+				if ((l2table_set->usage_bitmap & (1U << i))
+					== 0) {
+					/* found a set,
+						l2table_set and i are set. */
+					l2table_store =
+						l2table_set->kernel_virt;
+					break;
+				}
+			}
+			if (l2table_store != NULL)
+				break;
+		} /* end while */
+
+		if (l2table_store == NULL) {
+			l2table_store = (struct mc_l2_table_store *)
+						get_zeroed_page(GFP_KERNEL);
+			if (l2table_store == NULL) {
+				ret = -ENOMEM;
+				break;
+			}
+
+			/* Actually, locking is not necessary, because kernel
+				memory is not supposed to get swapped out. But
+				we play safe.... */
+			page = virt_to_page(l2table_store);
+			SetPageReserved(page);
+
+			/* allocate a descriptor */
+			l2table_set = kmalloc(sizeof(*l2table_set), GFP_KERNEL);
+			if (l2table_set == NULL) {
+				kfree(l2table_store);
+				ret = -ENOMEM;
+				break;
+			}
+			/* initialize */
+			memset(l2table_set, 0, sizeof(*l2table_set));
+
+			l2table_set->kernel_virt = l2table_store;
+			l2table_set->page = page;
+			l2table_set->phys = (void *)virt_to_phys(l2table_store);
+
+			/* init add to list. */
+			init_and_add_to_list(
+				&(l2table_set->list),
+				&(mc_drv_kmod_ctx.mc_l2_tables_sets));
+
+			/* use first table */
+			i = 0;
+		}
+
+		/* set set usage */
+		l2table_set->usage_bitmap |= (1U << i);
+
+		/* set set reference */
+		used_l2table->set = l2table_set;
+		used_l2table->idx = i;
+
+		MCDRV_DBG_VERBOSE(
+			"chunkPhys=%p,idx=%d\n",
+			l2table_set->phys, i);
+
+	} while (FALSE);
+
+	if (ret != 0) {
+		if (used_l2table != NULL) {
+			/* remove from list */
+			list_del(&(l2table_set->list));
+			/* free memory */
+			kfree(used_l2table);
+			used_l2table = NULL;
+		}
+	}
+
+	return used_l2table;
+}
+
+/*----------------------------------------------------------------------------*/
+static void free_used_l2_table(
+	struct mc_used_l2_table *used_l2table
+)
+{
+	struct mc_l2_tables_set	*l2table_set;
+	unsigned int		idx;
+
+	MCDRV_ASSERT(used_l2table != NULL);
+
+	l2table_set = used_l2table->set;
+	MCDRV_ASSERT(l2table_set != NULL);
+
+	/* clean usage flag */
+	idx = used_l2table->idx;
+	MCDRV_ASSERT(idx < MC_DRV_KMOD_L2_TABLE_PER_PAGES);
+	l2table_set->usage_bitmap &= ~(1U << idx);
+
+	/* if nobody uses this set, we can release it. */
+	if (l2table_set->usage_bitmap == 0) {
+		MCDRV_ASSERT(l2table_set->page != NULL);
+		ClearPageReserved(l2table_set->page);
+
+		MCDRV_ASSERT(l2table_set->kernel_virt != NULL);
+		free_page((unsigned long)l2table_set->kernel_virt);
+
+		/* remove from list */
+		list_del(&(l2table_set->list));
+
+		/* free memory */
+		kfree(l2table_set);
+	}
+
+	return;
+}
+
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * Create a L2 table in a WSM container that has been allocates previously.
+ *
+ * @param task		pointer to task owning WSM
+ * @param wsm_buffer	user space WSM start
+ * @param wsm_len	WSM length
+ * @param used_l2table	Pointer to L2 table details
+ */
+static int map_buffer_into_used_l2_table(
+	struct task_struct	*task,
+	void			*wsm_buffer,
+	unsigned int		wsm_len,
+	struct mc_used_l2_table	*used_l2table
+)
+{
+	int		ret = 0;
+	unsigned int	i, nr_of_pages;
+	void		*virt_addr_page;
+	struct page	*page;
+	struct l2table	*l2table;
+	struct page	**l2table_as_array_of_pointers_to_page;
+
+	/* task can be null when called from kernel space */
+	MCDRV_ASSERT(wsm_buffer != NULL);
+	MCDRV_ASSERT(wsm_len != 0);
+	MCDRV_ASSERT(used_l2table != NULL);
+
+	MCDRV_DBG_VERBOSE("WSM addr=0x%p, len=0x%08x\n", wsm_buffer, wsm_len);
+
+	/* Check if called from kernel space wsm_buffer is actually
+	 * vmalloced or not */
+	if (task == NULL && !is_vmalloc_addr(wsm_buffer)) {
+		MCDRV_DBG_ERROR("WSM addr is not a vmalloc address");
+		return -EINVAL;
+	}
+
+	l2table = get_l2_table_kernel_virt(used_l2table);
+	/* We use the memory for the L2 table to hold the pointer
+	and convert them later. This works, as everything comes
+	down to a 32 bit value. */
+	l2table_as_array_of_pointers_to_page = (struct page **)l2table;
+
+	do {
+
+		/* no size > 1Mib supported */
+		if (wsm_len > SZ_1M) {
+			MCDRV_DBG_ERROR("size > 1 MiB\n");
+			ret = -EINVAL;
+			break;
+		}
+
+		/* calculate page usage */
+		virt_addr_page = get_page_start(wsm_buffer);
+		nr_of_pages  = get_nr_of_pages_for_buffer(wsm_buffer, wsm_len);
+
+
+		MCDRV_DBG_VERBOSE("virt addr pageStart=0x%p,pages=%d\n",
+				  virt_addr_page,
+				  nr_of_pages);
+
+		/* L2 table can hold max 1MiB in 256 pages. */
+		if ((nr_of_pages*PAGE_SIZE) > SZ_1M) {
+			MCDRV_DBG_ERROR("WSM paged exceed 1 MiB\n");
+			ret = -EINVAL;
+			break;
+		}
+
+		/* Request comes from user space */
+		if (task != NULL) {
+			/* lock user page in memory, so they do not get swapped
+			* out.
+			* REV axh:
+			* Kernel 2.6.27 added a new get_user_pages_fast()
+			* function, maybe it is called fast_gup() in some
+			* versions.
+			* handle user process doing a fork().
+			* Child should not get things.
+			* http://osdir.com/ml/linux-media/2009-07/msg00813.html
+			* http://lwn.net/Articles/275808/ */
+
+			ret = lock_user_pages(
+					  task,
+					  virt_addr_page,
+					  nr_of_pages,
+					  l2table_as_array_of_pointers_to_page);
+			if (ret != 0) {
+				MCDRV_DBG_ERROR("lock_user_pages() failed\n");
+				break;
+			}
+		}
+		/* Request comes from kernel space(vmalloc buffer) */
+		else {
+			void *uaddr = wsm_buffer;
+			for (i = 0; i < nr_of_pages; i++) {
+				page = vmalloc_to_page(uaddr);
+				if (!page) {
+					MCDRV_DBG_ERROR(
+						"vmalloc_to_Page()"
+						" failed to map address\n");
+					ret = -EINVAL;
+					break;
+				}
+				get_page(page);
+				/* Lock the page in memory, it can't be swapped
+				 * out */
+				SetPageReserved(page);
+				l2table_as_array_of_pointers_to_page[i] = page;
+				uaddr += PAGE_SIZE;
+			}
+		}
+
+		used_l2table->nr_of_pages = nr_of_pages;
+		used_l2table->flags |= MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_APP;
+
+		/* create L2 Table entries. used_l2table->table contains a list
+		of page pointers here. For a proper cleanup we have to ensure
+		that the following code either works and used_l2table contains
+		a valid L2 table - or fails and used_l2table->table contains the
+		list of page pointers. Any mixed contents will make cleanup
+		difficult.*/
+
+		for (i = 0; i < nr_of_pages; i++) {
+			pte_t pte;
+			page = l2table_as_array_of_pointers_to_page[i];
+
+			/* create L2 table entry, see ARM MMU docu for details
+			about flags stored in the lowest 12 bits. As a side
+			reference, the Article "ARM's multiply-mapped memory
+			mess" found in the collection at at
+			http://lwn.net/Articles/409032/ is also worth reading.*/
+			pte = page_to_l2_pte(page)
+					| L2_FLAG_AP1 | L2_FLAG_AP0
+					| L2_FLAG_C | L2_FLAG_B
+					| L2_FLAG_SMALL | L2_FLAG_SMALL_XN
+			/* Linux uses different mappings for SMP systems(the
+			 * sharing flag is set for the pte. In order not to
+			 * confuse things too much in Mobicore make sure the
+			 * shared buffers have the same flags.
+			 * This should also be done in SWD side
+			 */
+#ifdef CONFIG_SMP
+					| L2_FLAG_S | L2_FLAG_SMALL_TEX0
+#endif
+				  ;
+
+			l2table->table_entries[i] = pte;
+			MCDRV_DBG_VERBOSE("L2 entry %d:  0x%08x\n", i,
+					  (unsigned int)(pte));
+		}
+
+		/* ensure rest of table is empty */
+		while (i < 255)
+			l2table->table_entries[i++] = (pte_t)0;
+
+	} while (FALSE);
+
+	return ret;
+}
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * Remove a L2 table in a WSM container. Afterwards the container may be
+ * released.
+ *
+ * @param used_l2table	Pointer to L2 table details
+ */
+
+static void unmap_buffers_from_used_l2_table(
+	struct mc_used_l2_table	*used_l2table
+)
+{
+	unsigned int	i;
+	struct l2table	*l2table;
+
+	MCDRV_ASSERT(used_l2table != NULL);
+	/* this should not happen, as we have no empty tables. */
+	MCDRV_ASSERT(!is_in_use_used_l2_table(used_l2table));
+
+	/* found the table, now release the resources. */
+	MCDRV_DBG_VERBOSE("clear L2 table, phys_base=%p, nr_of_pages=%d\n",
+			  get_l2_table_phys(used_l2table),
+			  used_l2table->nr_of_pages);
+
+	l2table = get_l2_table_kernel_virt(used_l2table);
+
+	/* release all locked user space pages */
+	for (i = 0; i < used_l2table->nr_of_pages; i++) {
+		/* convert physical entries from L2 table to page pointers */
+		pte_t pte = get_l2_table_kernel_virt(used_l2table)->
+							table_entries[i];
+		struct page *page = l2_pte_to_page(pte);
+		unlock_page_from_used_l2_table(page);
+	}
+
+	/* remember that all pages have been freed */
+	used_l2table->nr_of_pages = 0;
+
+	return;
+}
+
+
+/*
+#############################################################################
+##
+## Helper functions
+##
+#############################################################################*/
+/*----------------------------------------------------------------------------*/
+#define FREE_FROM_SWD	TRUE
+#define FREE_FROM_NWD	FALSE
+/** Delete a used l2 table. */
+static void delete_used_l2_table(
+	struct mc_used_l2_table	*used_l2table,
+	unsigned int		is_swd
+)
+{
+	if (is_swd) {
+		used_l2table->flags &=
+			~MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC;
+	} else {
+		used_l2table->flags &=
+			~MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_APP;
+		used_l2table->owner = NULL;
+	}
+
+	/* release if Nwd and Swd/MC do no longer use it. */
+	if (is_in_use_used_l2_table(used_l2table)) {
+		MCDRV_DBG_WARN(
+			"WSM L2 table still in use: physBase=%p, "
+			"nr_of_pages=%d\n",
+			get_l2_table_phys(used_l2table),
+			used_l2table->nr_of_pages);
+	} else {
+		unmap_buffers_from_used_l2_table(used_l2table);
+		free_used_l2_table(used_l2table);
+
+		list_del(&(used_l2table->list));
+
+		kfree(used_l2table);
+	}
+	return;
+}
+
+/*----------------------------------------------------------------------------*/
+/** Allocate L2 table and map buffer into it. That is, create respective table
+	entries. Must hold Semaphore mc_drv_kmod_ctx.wsm_l2_sem */
+static struct mc_used_l2_table *new_used_l2_table(
+	struct mc_instance	*instance,
+	struct task_struct	*task,
+	void			*wsm_buffer,
+	unsigned int		wsm_len
+) {
+	int			ret = 0;
+	struct mc_used_l2_table	*used_l2table;
+
+	do {
+		used_l2table = allocate_used_l2_table(instance);
+		if (used_l2table == NULL) {
+			MCDRV_DBG_ERROR(
+				"allocate_used_l2_table() failed\n");
+			break;
+		}
+
+		/* create the L2 page for the WSM */
+		ret = map_buffer_into_used_l2_table(
+				  task,
+				  wsm_buffer,
+				  wsm_len,
+				  used_l2table);
+		if (ret != 0) {
+			MCDRV_DBG_ERROR(
+				"map_buffer_into_used_l2_table() failed\n");
+			delete_used_l2_table(used_l2table, FREE_FROM_NWD);
+			used_l2table = NULL;
+			break;
+		}
+
+	} while (FALSE);
+
+
+	return used_l2table;
+}
+
+/*
+#############################################################################
+##
+## IoCtl handler
+##
+#############################################################################*/
+
+/**
+ * Map a virtual memory buffer structure to Mobicore
+ * @param instance
+ * @param addr		address of the buffer(NB it must be kernel virtual!)
+ * @param len		buffer length
+ * @param handle	pointer to handle
+ * @param phys_wsm_l2_table	pointer to physical L2 table(?)
+ *
+ * @return 0 if no error
+ *
+ */
+/*----------------------------------------------------------------------------*/
+int mobicore_map_vmem(
+	struct mc_instance	*instance,
+	void			*addr,
+	uint32_t		len,
+	uint32_t		*handle,
+	void			**phys_wsm_l2_table
+)
+{
+	int ret = 0;
+	struct mc_used_l2_table *used_l2table = NULL;
+	MCDRV_ASSERT(instance != NULL);
+
+	MCDRV_DBG_VERBOSE("enter\n");
+
+	do {
+		if (len == 0) {
+			MCDRV_DBG_ERROR("len=0 is not supported!\n");
+			ret = -EINVAL;
+			break;
+		}
+
+		/* try to get the semaphore */
+		ret = down_interruptible(&(mc_drv_kmod_ctx.wsm_l2_sem));
+		if (ret != 0) {
+			MCDRV_DBG_ERROR("down_interruptible() failed with %d\n",
+					ret);
+			ret = -ERESTARTSYS;
+			break;
+		}
+
+		do {
+			used_l2table = new_used_l2_table(
+						   instance,
+						   NULL,
+						   addr,
+						   len);
+
+			if (used_l2table == NULL) {
+				MCDRV_DBG_ERROR("new_used_l2_table() failed\n");
+				ret = -EINVAL;
+				break;
+			}
+
+			/* set response */
+			*handle = used_l2table->handle;
+			*phys_wsm_l2_table =
+				(void *)get_l2_table_phys(used_l2table);
+			MCDRV_DBG_VERBOSE("handle: %d, phys=%p\n",
+					  *handle,
+					  (void *)(*phys_wsm_l2_table));
+
+		} while (FALSE);
+
+		/* release semaphore */
+		up(&(mc_drv_kmod_ctx.wsm_l2_sem));
+
+	} while (FALSE);
+
+	MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(mobicore_map_vmem);
+/*----------------------------------------------------------------------------*/
+/**
+ *
+ * @param instance
+ * @param arg
+ *
+ * @return 0 if no error
+ *
+ */
+static int handle_ioctl_app_register_wsm_l2(
+	struct mc_instance			*instance,
+	union mc_ioctl_app_reg_wsm_l2_params	*user_params
+)
+{
+	int					ret = 0;
+	union mc_ioctl_app_reg_wsm_l2_params	params;
+	struct mc_used_l2_table			*used_l2table = NULL;
+	struct pid				*pid_struct = NULL;
+	struct task_struct			*task = current;
+
+	MCDRV_ASSERT(instance != NULL);
+
+	MCDRV_DBG_VERBOSE("enter\n");
+
+	do {
+		/* get use parameters */
+		ret = copy_from_user(
+				  &(params.in),
+				  &(user_params->in),
+				  sizeof(params.in));
+		if (ret != 0) {
+			MCDRV_DBG_ERROR("copy_from_user() failed\n");
+			break;
+		}
+
+		/* daemon can do this for another task. */
+		if (params.in.pid != 0) {
+			MCDRV_DBG_ERROR("pid != 0 unsupported\n");
+			ret = -EINVAL;
+			break;
+		}
+		if (params.in.len == 0) {
+			MCDRV_DBG_ERROR("len=0 is not supported!\n");
+			ret = -EINVAL;
+			break;
+		}
+
+		/* try to get the semaphore */
+		ret = down_interruptible(&(mc_drv_kmod_ctx.wsm_l2_sem));
+		if (ret != 0) {
+			MCDRV_DBG_ERROR("down_interruptible() failed with %d\n",
+					ret);
+			ret = -ERESTARTSYS;
+			break;
+		}
+
+		do {
+			used_l2table = new_used_l2_table(
+						   instance,
+						   task,
+						   (void *)(params.in.buffer),
+						   params.in.len);
+
+			if (used_l2table == NULL) {
+				MCDRV_DBG_ERROR("new_used_l2_table() failed\n");
+				ret = -EINVAL;
+				break;
+			}
+
+			/* if the daemon does this, we set the MC lock */
+			if (is_caller_mc_daemon(instance))
+				used_l2table->flags |=
+					MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC;
+
+			/* set response */
+			memset(&params.out, 0, sizeof(params.out));
+			params.out.handle = used_l2table->handle;
+			/* TODO: return the physical address for daemon only,
+				otherwise set NULL */
+			params.out.phys_wsm_l2_table =
+				(uint32_t)get_l2_table_phys(used_l2table);
+
+			MCDRV_DBG_VERBOSE("handle: %d, phys=%p\n",
+					params.out.handle,
+					(void *)(params.out.phys_wsm_l2_table));
+
+
+			/* copy L2Table to user space */
+			ret = copy_to_user(
+					  &(user_params->out),
+					  &(params.out),
+					  sizeof(params.out));
+			if (ret != 0) {
+				MCDRV_DBG_ERROR("copy_to_user() failed\n");
+
+				/* free the table again, as app does not know
+					about anything. */
+				if (is_caller_mc_daemon(instance)) {
+					used_l2table->flags &=
+					~MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC;
+				}
+				delete_used_l2_table(used_l2table,
+						FREE_FROM_NWD);
+				used_l2table = NULL;
+				break;
+			}
+
+		} while (FALSE);
+
+		/* release semaphore */
+		up(&(mc_drv_kmod_ctx.wsm_l2_sem));
+
+	} while (FALSE);
+
+
+
+	/* release PID struct reference */
+	if (pid_struct != NULL)
+		put_pid(pid_struct);
+
+
+	MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+	return ret;
+}
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * Unmap a virtual memory buffer from mobicore
+ * @param instance
+ * @param handle
+ *
+ * @return 0 if no error
+ *
+ */
+int mobicore_unmap_vmem(
+	struct mc_instance	*instance,
+	uint32_t		handle
+)
+{
+	int ret = 0;
+	struct mc_used_l2_table *used_l2table = NULL;
+
+	MCDRV_ASSERT(instance != NULL);
+	MCDRV_DBG_VERBOSE("enter\n");
+
+	do {
+		/* try to get the semaphore */
+		ret = down_interruptible(&(mc_drv_kmod_ctx.wsm_l2_sem));
+		if (ret != 0) {
+			MCDRV_DBG_ERROR("processOpenSession() failed with %d\n",
+					ret);
+			ret = -ERESTARTSYS;
+			break;
+		}
+
+		do {
+			used_l2table = find_used_l2_table_by_handle(handle);
+			if (used_l2table == NULL) {
+				ret = -EINVAL;
+				MCDRV_DBG_ERROR("entry not found\n");
+				break;
+			}
+
+			if (instance != used_l2table->owner) {
+				ret = -EINVAL;
+				MCDRV_DBG_ERROR("instance does no own it\n");
+				break;
+			}
+
+			/* free table (if no further locks exist) */
+			delete_used_l2_table(used_l2table, FREE_FROM_NWD);
+			used_l2table = NULL;
+			/* there are no out parameters */
+		} while (FALSE);
+		/* release semaphore */
+		up(&(mc_drv_kmod_ctx.wsm_l2_sem));
+
+	} while (FALSE);
+
+	MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(mobicore_unmap_vmem);
+/*----------------------------------------------------------------------------*/
+/**
+ *
+ * @param instance
+ * @param arg
+ *
+ * @return 0 if no error
+ *
+ */
+static int handle_ioctl_app_unregister_wsm_l2(
+	struct mc_instance			*instance,
+	struct mc_ioctl_app_unreg_wsm_l2_params	*user_params
+)
+{
+	int					ret = 0;
+	struct mc_ioctl_app_unreg_wsm_l2_params	params;
+	struct mc_used_l2_table			*used_l2table = NULL;
+
+	MCDRV_ASSERT(instance != NULL);
+	MCDRV_DBG_VERBOSE("enter\n");
+
+	do {
+		ret = copy_from_user(
+				  &(params.in),
+				  &(user_params->in),
+				  sizeof(params.in));
+
+		if (ret != 0) {
+			MCDRV_DBG_ERROR("copy_from_user\n");
+			break;
+		}
+
+		/* try to get the semaphore */
+		ret = down_interruptible(&(mc_drv_kmod_ctx.wsm_l2_sem));
+		if (ret != 0) {
+			MCDRV_DBG_ERROR("down_interruptible() failed with %d\n",
+					ret);
+			ret = -ERESTARTSYS;
+			break;
+		}
+
+		do {
+			/* daemon can do this for another task. */
+			if (params.in.pid != 0) {
+				MCDRV_DBG_ERROR("pid != 0 unsupported\n");
+				ret = -EINVAL;
+				break;
+			}
+
+			used_l2table =
+				find_used_l2_table_by_handle(params.in.handle);
+			if (used_l2table == NULL) {
+				ret = -EINVAL;
+				MCDRV_DBG_ERROR("entry not found\n");
+				break;
+			}
+
+			if (is_caller_mc_daemon(instance)) {
+				/* if daemon does this, we have to release the
+					MobiCore lock. */
+				used_l2table->flags &=
+					~MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC;
+			} else if (instance != used_l2table->owner) {
+				ret = -EINVAL;
+				MCDRV_DBG_ERROR("instance does no own it\n");
+				break;
+			}
+
+			/* free table (if no further locks exist) */
+			delete_used_l2_table(used_l2table, FREE_FROM_NWD);
+			used_l2table = NULL;
+
+			/* there are no out parameters */
+
+		} while (FALSE);
+
+		/* release semaphore */
+		up(&(mc_drv_kmod_ctx.wsm_l2_sem));
+
+	} while (FALSE);
+
+	MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+	return ret;
+}
+
+
+/*----------------------------------------------------------------------------*/
+static int handle_ioctl_daemon_lock_wsm_l2(
+	struct mc_instance				*instance,
+	struct mc_ioctl_daemon_lock_wsm_l2_params	*user_params
+)
+{
+	int						ret = 0;
+	struct mc_ioctl_daemon_lock_wsm_l2_params	params;
+	struct mc_used_l2_table				*used_l2table = NULL;
+
+	MCDRV_ASSERT(instance != NULL);
+	MCDRV_DBG_VERBOSE("enter\n");
+
+	do {
+		if (!is_caller_mc_daemon(instance)) {
+			MCDRV_DBG_ERROR("caller not MobiCore Daemon\n");
+			ret = -EFAULT;
+			break;
+		}
+
+		ret = copy_from_user(
+				  &(params.in),
+				  &(user_params->in),
+				  sizeof(params.in));
+
+		if (ret != 0) {
+			MCDRV_DBG_ERROR("copy_from_user\n");
+			break;
+		}
+		/* try to get the semaphore */
+		ret = down_interruptible(&(mc_drv_kmod_ctx.wsm_l2_sem));
+		if (ret != 0) {
+			MCDRV_DBG_ERROR("down_interruptible() failed with %d\n",
+					ret);
+			ret = -ERESTARTSYS;
+			break;
+		}
+
+		do {
+			used_l2table =
+				find_used_l2_table_by_handle(params.in.handle);
+			if (used_l2table == NULL) {
+				ret = -EINVAL;
+				MCDRV_DBG_ERROR("entry not found\n");
+				break;
+			}
+			if (instance != used_l2table->owner) {
+				ret = -EINVAL;
+				MCDRV_DBG_ERROR("instance does no own it\n");
+				break;
+			}
+
+			/* lock entry */
+			if ((used_l2table->flags &
+				  MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC) != 0) {
+				MCDRV_DBG_WARN("entry already locked\n");
+			}
+			used_l2table->flags |=
+				MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC;
+
+			/* prepare response */
+			memset(&(params.out), 0, sizeof(params.out));
+			params.out.phys_wsm_l2_table =
+				(uint32_t)get_l2_table_phys(used_l2table);
+
+			/* copy to user space */
+			ret = copy_to_user(
+					  &(user_params->out),
+					  &(params.out),
+					  sizeof(params.out));
+			if (ret != 0) {
+				MCDRV_DBG_ERROR("copy_to_user() failed\n");
+
+				/* undo, as userspace did not get it. */
+				used_l2table->flags |=
+					MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC;
+				break;
+			}
+
+		} while (FALSE);
+
+		/* release semaphore */
+		up(&(mc_drv_kmod_ctx.wsm_l2_sem));
+
+	} while (FALSE);
+
+	MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+	return ret;
+}
+
+
+/*----------------------------------------------------------------------------*/
+static int handle_ioctl_daemon_unlock_wsm_l2(
+	struct mc_instance				*instance,
+	struct mc_ioctl_daemon_unlock_wsm_l2_params	*user_params
+)
+{
+	int						ret = 0;
+	struct mc_ioctl_daemon_unlock_wsm_l2_params	params;
+	struct mc_used_l2_table				*used_l2table = NULL;
+
+	MCDRV_ASSERT(instance != NULL);
+	MCDRV_DBG_VERBOSE("enter\n");
+
+	do {
+		if (!is_caller_mc_daemon(instance)) {
+			MCDRV_DBG_ERROR("caller not MobiCore Daemon\n");
+			ret = -EFAULT;
+			break;
+		}
+
+		ret = copy_from_user(
+				  &(params.in),
+				  &(user_params->in),
+				  sizeof(params.in));
+
+		if (ret != 0) {
+			MCDRV_DBG_ERROR("copy_from_user\n");
+			break;
+		}
+		/* try to get the semaphore */
+		ret = down_interruptible(&(mc_drv_kmod_ctx.wsm_l2_sem));
+		if (ret != 0) {
+			MCDRV_DBG_ERROR("down_interruptible() failed with %d\n",
+						ret);
+			ret = -ERESTARTSYS;
+			break;
+		}
+
+		do {
+			used_l2table =
+				find_used_l2_table_by_handle(params.in.handle);
+			if (used_l2table == NULL) {
+				ret = -EINVAL;
+				MCDRV_DBG_ERROR("entry not found\n");
+				break;
+			}
+			if (instance != used_l2table->owner) {
+				ret = -EINVAL;
+				MCDRV_DBG_ERROR("instance does no own it\n");
+				break;
+			}
+
+			/* lock entry */
+			if ((used_l2table->flags &
+				  MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC) == 0) {
+				MCDRV_DBG_WARN("entry is not locked locked\n");
+			}
+
+			/* free table (if no further locks exist) */
+			delete_used_l2_table(used_l2table, FREE_FROM_SWD);
+			used_l2table = NULL;
+
+			/* there are no out parameters */
+
+		} while (FALSE);
+
+	} while (FALSE);
+
+
+	MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+	return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/** Clears the reserved bit of each page and frees the pages */
+static inline void free_continguous_pages(
+	void		*addr,
+	unsigned int	size
+)
+{
+	struct page *page = virt_to_page(addr);
+	int i;
+	for (i = 0; i < size; i++) {
+		MCDRV_DBG_VERBOSE("free page at 0x%p\n", page);
+		ClearPageReserved(page);
+		page++;
+	}
+	/* REV luh: see man kmalloc */
+	free_pages((unsigned long)addr, size_to_order(size));
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ * Free a WSM buffer allocated with mobicore_allocate_wsm
+ * @param instance
+ * @param handle		handle of the buffer
+ *
+ * @return 0 if no error
+ *
+ */
+int mobicore_free(
+	struct mc_instance	*instance,
+	uint32_t		handle
+)
+{
+	int ret = 0;
+	unsigned int i;
+	struct mc_contg_buffer	*contg_buffer;
+
+	do {
+		/* search for the given address in the contg_buffers list */
+		for (i = 0; i < MC_DRV_KMOD_CONTG_BUFFER_MAX; i++) {
+			contg_buffer = &(instance->contg_buffers[i]);
+			if (contg_buffer->handle == handle)
+				break;
+		}
+		if (i == MC_DRV_KMOD_CONTG_BUFFER_MAX) {
+			MCDRV_DBG_ERROR("contigous buffer not found\n");
+			ret = -EFAULT;
+			break;
+		}
+
+		MCDRV_DBG_VERBOSE("phys_addr=0x%p, virt_addr=0x%p\n",
+				contg_buffer->phys_addr,
+				contg_buffer->virt_kernel_addr);
+
+		free_continguous_pages(contg_buffer->virt_kernel_addr,
+					contg_buffer->num_pages);
+
+		memset(contg_buffer, 0, sizeof(*contg_buffer));
+
+		/* there are no out parameters */
+
+	} while (FALSE);
+
+
+	return ret;
+}
+EXPORT_SYMBOL(mobicore_free);
+/*----------------------------------------------------------------------------*/
+
+/**
+ *
+ * @param instance
+ * @param arg
+ *
+ * @return 0 if no error
+ *
+ */
+static int handle_ioctl_free(
+	struct mc_instance		*instance,
+	union mc_ioctl_free_params	*user_params
+)
+{
+	int				ret = 0;
+	union mc_ioctl_free_params	params;
+
+
+	MCDRV_ASSERT(instance != NULL);
+	MCDRV_DBG_VERBOSE("enter\n");
+
+	do {
+		ret = copy_from_user(
+				  &(params.in),
+				  &(user_params->in),
+				  sizeof(params.in));
+
+		if (ret != 0) {
+			MCDRV_DBG_ERROR("copy_from_user\n");
+			break;
+		}
+
+		/* daemon can do this for another task. */
+		if (params.in.pid != 0) {
+			MCDRV_DBG_ERROR("pid != 0 unsupported\n");
+			ret = -EINVAL;
+			break;
+		}
+
+		ret = mobicore_free(instance, params.in.handle);
+
+		/* there are no out parameters */
+
+	} while (FALSE);
+
+	MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+	return ret;
+
+}
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ *
+ * @param instance
+ * @param arg
+ *
+ * @return 0 if no error
+ *
+ */
+static int handle_ioctl_info(
+	struct mc_instance	*instance,
+	union mc_ioctl_info_params	*user_params
+)
+{
+	int			ret = 0;
+	union mc_ioctl_info_params	params;
+	union mc_fc_info		fc_info;
+
+
+	MCDRV_ASSERT(instance != NULL);
+	MCDRV_DBG_VERBOSE("enter\n");
+
+	do {
+		/* only the MobiCore Daemon is allowed to call this function */
+		if (!is_caller_mc_daemon(instance)) {
+			MCDRV_DBG_ERROR("caller not MobiCore Daemon\n");
+			ret = -EFAULT;
+			break;
+		}
+
+		ret = copy_from_user(
+				  &(params.in),
+				  &(user_params->in),
+				  sizeof(params.in));
+
+		if (ret != 0) {
+			MCDRV_DBG_ERROR("copy_from_user\n");
+			break;
+		}
+
+
+		memset(&fc_info, 0, sizeof(fc_info));
+		fc_info.as_in.cmd	   = MC_FC_INFO;
+		fc_info.as_in.ext_info_id = params.in.ext_info_id;
+
+		MCDRV_DBG(
+			"fc_info in cmd=0x%08x, ext_info_id=0x%08x "
+			"rfu=(0x%08x, 0x%08x)\n",
+			fc_info.as_in.cmd,
+			fc_info.as_in.ext_info_id,
+			fc_info.as_in.rfu[0],
+			fc_info.as_in.rfu[1]);
+
+		mc_fastcall(&(fc_info.as_generic));
+
+		MCDRV_DBG(
+			"fc_info out resp=0x%08x, ret=0x%08x "
+			"state=0x%08x, ext_info=0x%08x\n",
+			fc_info.as_out.resp,
+			fc_info.as_out.ret,
+			fc_info.as_out.state,
+			fc_info.as_out.ext_info);
+
+		ret = convert_fc_ret(fc_info.as_out.ret);
+		if (ret != 0)
+			break;
+
+		memset(&(params.out), 0, sizeof(params.out));
+		params.out.state  = fc_info.as_out.state;
+		params.out.ext_info = fc_info.as_out.ext_info;
+
+		ret = copy_to_user(
+				  &(user_params->out),
+				  &(params.out),
+				  sizeof(params.out));
+
+		if (ret != 0) {
+			MCDRV_DBG_ERROR("copy_to_user\n");
+			break;
+		}
+	} while (FALSE);
+
+	MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+	return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ *
+ * @param instance
+ * @param arg
+ *
+ * @return 0 if no error
+ *
+ */
+static int handle_ioctl_yield(
+	struct mc_instance	*instance
+)
+{
+	int			ret = 0;
+	union mc_fc_s_yield	fc_s_yield;
+
+	MCDRV_ASSERT(instance != NULL);
+
+	/* avoid putting debug output here, as we do this very often */
+	MCDRV_DBG_VERBOSE("enter\n");
+
+	do {
+		/* only the MobiCore Daemon is allowed to call this function */
+		if (!is_caller_mc_daemon(instance)) {
+			MCDRV_DBG_ERROR("caller not MobiCore Daemon\n");
+			ret = -EFAULT;
+			break;
+		}
+
+		memset(&fc_s_yield, 0, sizeof(fc_s_yield));
+		fc_s_yield.as_in.cmd = MC_SMC_N_YIELD;
+		mc_fastcall(&(fc_s_yield.as_generic));
+		ret = convert_fc_ret(fc_s_yield.as_out.ret);
+		if (ret != 0)
+			break;
+
+	} while (FALSE);
+
+	MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+	return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ * handle ioctl and call common notify
+ *
+ * @param instance
+ * @param arg
+ *
+ * @return 0 if no error
+ *
+ */
+static int handle_ioctl_nsiq(
+	struct mc_instance	*instance,
+	unsigned long		arg
+)
+{
+	int		ret = 0;
+
+	MCDRV_ASSERT(instance != NULL);
+
+	/* avoid putting debug output here, as we do this very often */
+	MCDRV_DBG_VERBOSE("enter\n");
+	/* only the MobiCore Daemon is allowed to call this function */
+	if (!is_caller_mc_daemon(instance)) {
+		MCDRV_DBG_ERROR("caller not MobiCore Daemon\n");
+		return -EFAULT;
+	}
+
+	do {
+		union mc_fc_nsiq fc_nsiq;
+		memset(&fc_nsiq, 0, sizeof(fc_nsiq));
+		fc_nsiq.as_in.cmd = MC_SMC_N_SIQ;
+		mc_fastcall(&(fc_nsiq.as_generic));
+		ret = convert_fc_ret(fc_nsiq.as_out.ret);
+		if (ret != 0)
+			break;
+	} while (FALSE);
+
+	MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+	return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ *
+ * @param instance
+ * @param arg
+ *
+ * @return 0 if no error
+ *
+ */
+static int handle_ioctl_dump_status(
+	struct mc_instance	*instance,
+	unsigned long		arg
+)
+{
+	int		ret = 0;
+	int		i = 0;
+	union mc_fc_info	fc_info;
+
+	MCDRV_ASSERT(instance != NULL);
+	MCDRV_DBG_VERBOSE("enter\n");
+
+	do {
+		/* anybody with root access can do this. */
+		if (!is_userland_caller_privileged()) {
+			MCDRV_DBG_ERROR("caller must have root privileges\n");
+			ret = -EFAULT;
+			break;
+		}
+
+		/* loop ext_info */
+		while (TRUE) {
+			memset(&fc_info, 0, sizeof(fc_info));
+			fc_info.as_in.cmd	   = MC_FC_INFO;
+			fc_info.as_in.ext_info_id = i;
+
+			MCDRV_DBG(
+				"fc_info in cmd=0x%08x, ext_info_id=0x%08x "
+				"rfu=(0x%08x, 0x%08x)\n",
+				fc_info.as_in.cmd,
+				fc_info.as_in.ext_info_id,
+				fc_info.as_in.rfu[0],
+				fc_info.as_in.rfu[1]);
+
+			mc_fastcall(&(fc_info.as_generic));
+
+			MCDRV_DBG(
+				"fc_info out resp=0x%08x, ret=0x%08x "
+				"state=0x%08x, ext_info=0x%08x\n",
+				fc_info.as_out.resp,
+				fc_info.as_out.ret,
+				fc_info.as_out.state,
+				fc_info.as_out.ext_info);
+
+			ret = convert_fc_ret(fc_info.as_out.ret);
+			if (ret != 0)
+				break;
+
+			MCDRV_DBG("state=%08X, idx=%02d: ext_info=%08X\n",
+				fc_info.as_out.state,
+				i,
+				fc_info.as_out.ext_info);
+			i++;
+		};
+
+		if (ret != 0)
+			break;
+
+
+	} while (FALSE);
+
+	MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+	return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ *
+ * @param instance
+ * @param arg
+ *
+ * @return 0 if no error
+ *
+ */
+static int handle_ioctl_init(
+	struct mc_instance	*instance,
+	union mc_ioctl_init_params	*user_params
+)
+{
+	int			ret = 0;
+	union mc_ioctl_init_params	params;
+	union mc_fc_init		fc_init;
+
+	MCDRV_ASSERT(instance != NULL);
+	MCDRV_DBG_VERBOSE("enter\n");
+
+	do {
+		/* only the MobiCore Daemon is allowed to call this function */
+		if (!is_caller_mc_daemon(instance)) {
+			MCDRV_DBG_ERROR("caller not MobiCore Daemon\n");
+			ret = -EFAULT;
+			break;
+		}
+
+		ret = copy_from_user(
+				  &(params.in),
+				  &(user_params->in),
+				  sizeof(params.in));
+		if (ret != 0) {
+			MCDRV_DBG_ERROR("copy_from_user failed\n");
+			break;
+		}
+
+		memset(&fc_init, 0, sizeof(fc_init));
+
+		fc_init.as_in.cmd	= MC_FC_INIT;
+		/* base address of mci buffer 4KB aligned */
+		fc_init.as_in.base   = (uint32_t)params.in.base;
+		/* notification buffer start/length [16:16] [start, length] */
+		fc_init.as_in.nq_info  = (params.in.nq_offset << 16)
+					  | (params.in.nq_length & 0xFFFF);
+		/* mcp buffer start/length [16:16] [start, length] */
+		fc_init.as_in.mcp_info = (params.in.mcp_offset << 16)
+					  | (params.in.mcp_length & 0xFFFF);
+
+		/* Set KMOD notification queue to start of MCI
+			mciInfo was already set up in mmap */
+		if (!mci_base) {
+			MCDRV_DBG_ERROR("No MCI set yet.\n");
+			return -EFAULT;
+		}
+		MCDRV_DBG("in cmd=0x%08x, base=0x%08x, "
+			  "nq_info=0x%08x, mcp_info=0x%08x\n",
+			  fc_init.as_in.cmd,
+			  fc_init.as_in.base,
+			  fc_init.as_in.nq_info,
+			  fc_init.as_in.mcp_info);
+
+		mc_fastcall(&(fc_init.as_generic));
+
+		MCDRV_DBG("out cmd=0x%08x, ret=0x%08x rfu=(0x%08x, 0x%08x)\n",
+			  fc_init.as_out.resp,
+			  fc_init.as_out.ret,
+			  fc_init.as_out.rfu[0],
+			  fc_init.as_out.rfu[1]);
+
+		ret = convert_fc_ret(fc_init.as_out.ret);
+		if (ret != 0)
+			break;
+
+		/* no ioctl response parameters */
+
+	} while (FALSE);
+
+	MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+	return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ *
+ * @param instance
+ * @param arg
+ *
+ * @return 0 if no error
+ *
+ */
+static int handle_ioctl_fc_execute(
+	struct mc_instance		*instance,
+	union mc_ioctl_fc_execute_params	*user_params
+)
+{
+	int				ret = 0;
+	union mc_ioctl_fc_execute_params	params;
+	union fc_generic			fc_params;
+
+	MCDRV_ASSERT(instance != NULL);
+	MCDRV_DBG_VERBOSE("enter\n");
+
+	do {
+		/* only the MobiCore Daemon is allowed to call this function */
+		if (!is_caller_mc_daemon(instance)) {
+			MCDRV_DBG_ERROR("caller not MobiCore Daemon\n");
+			ret = -EFAULT;
+			break;
+		}
+
+		ret = copy_from_user(
+				  &(params.in),
+				  &(user_params->in),
+				  sizeof(params.in));
+		if (ret != 0) {
+			MCDRV_DBG_ERROR("copy_from_user failed\n");
+			break;
+		}
+
+		fc_params.as_in.cmd = -4;/*FC_EXECUTE */
+		fc_params.as_in.param[0] = params.in.phys_start_addr;
+		fc_params.as_in.param[1] = params.in.length;
+		fc_params.as_in.param[2] = 0;
+
+		MCDRV_DBG("in cmd=0x%08x, startAddr=0x%08x, length=0x%08x\n",
+			  fc_params.as_in.cmd,
+			  fc_params.as_in.param[0],
+			  fc_params.as_in.param[1]);
+
+		mc_fastcall(&fc_params);
+
+		MCDRV_DBG("out cmd=0x%08x, ret=0x%08x rfu=(0x%08x, 0x%08x)\n",
+			  fc_params.as_out.resp,
+			  fc_params.as_out.ret,
+			  fc_params.as_out.param[0],
+			  fc_params.as_out.param[1]);
+
+		ret = convert_fc_ret(fc_params.as_out.ret);
+		if (ret != 0)
+			break;
+
+		/* no ioctl response parameters */
+
+	} while (FALSE);
+
+	MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+	return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+#define MC_MAKE_VERSION(major, minor) \
+		(((major & 0x0000ffff) << 16) | (minor & 0x0000ffff))
+/**
+ *
+ * @param instance
+ * @param arg
+ *
+ * @return 0 if no error
+ *
+ */
+static int handle_ioctl_get_version(
+	struct mc_instance			*instance,
+	struct mc_ioctl_get_version_params	*user_params
+)
+{
+	int ret = 0;
+	struct mc_ioctl_get_version_params params = {
+		{
+			MC_MAKE_VERSION(MCDRVMODULEAPI_VERSION_MAJOR,
+					MCDRVMODULEAPI_VERSION_MINOR)
+		}
+	};
+
+	MCDRV_ASSERT(instance != NULL);
+	MCDRV_DBG_VERBOSE("enter\n");
+
+	do {
+		MCDRV_DBG("mcDrvModuleApi version is %i.%i\n",
+				MCDRVMODULEAPI_VERSION_MAJOR,
+				MCDRVMODULEAPI_VERSION_MINOR);
+
+		/* no ioctl response parameters */
+		ret = copy_to_user(
+					&(user_params->out),
+					&(params.out),
+					sizeof(params.out));
+		if (ret != 0)
+			MCDRV_DBG_ERROR("copy_to_user() failed\n");
+
+	} while (FALSE);
+
+	MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+	return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ * This function will be called from user space as ioctl(...).
+ * @param file	pointer to file
+ * @param cmd	command
+ * @param arg	arguments
+ *
+ * @return int 0 for OK and an errno in case of error
+ */
+static long mc_kernel_module_ioctl(
+	struct file	*file,
+	unsigned int	cmd,
+	unsigned long	arg
+)
+{
+	int ret;
+	struct mc_instance *instance = get_instance(file);
+
+	MCDRV_ASSERT(instance != NULL);
+
+	switch (cmd) {
+	/*--------------------------------------------------------------------*/
+	case MC_DRV_KMOD_IOCTL_DUMP_STATUS:
+		ret = handle_ioctl_dump_status(
+				instance,
+				arg);
+		break;
+
+	/*--------------------------------------------------------------------*/
+	case MC_DRV_KMOD_IOCTL_FC_INIT:
+		ret = handle_ioctl_init(
+				instance,
+				(union mc_ioctl_init_params *)arg);
+		break;
+	/*--------------------------------------------------------------------*/
+	case MC_DRV_KMOD_IOCTL_FC_INFO:
+		ret = handle_ioctl_info(
+				instance,
+				(union mc_ioctl_info_params *)arg);
+		break;
+
+	/*--------------------------------------------------------------------*/
+	case MC_DRV_KMOD_IOCTL_FC_YIELD:
+		ret = handle_ioctl_yield(
+				instance);
+		break;
+
+	/*--------------------------------------------------------------------*/
+	case MC_DRV_KMOD_IOCTL_FC_NSIQ:
+		ret = handle_ioctl_nsiq(
+				instance,
+				arg);
+		break;
+
+	/*--------------------------------------------------------------------*/
+	case MC_DRV_KMOD_IOCTL_DAEMON_LOCK_WSM_L2:
+		ret = handle_ioctl_daemon_lock_wsm_l2(
+			instance,
+			(struct mc_ioctl_daemon_lock_wsm_l2_params *)arg);
+		break;
+
+	/*--------------------------------------------------------------------*/
+	case MC_DRV_KMOD_IOCTL_DAEMON_UNLOCK_WSM_L2:
+		ret = handle_ioctl_daemon_unlock_wsm_l2(
+			instance,
+			(struct mc_ioctl_daemon_unlock_wsm_l2_params *)arg);
+		break;
+
+	/*--------------------------------------------------------------------*/
+	case MC_DRV_KMOD_IOCTL_FREE:
+		/* called by ClientLib */
+		ret = handle_ioctl_free(
+				instance,
+				(union mc_ioctl_free_params *)arg);
+		break;
+
+	/*--------------------------------------------------------------------*/
+	case MC_DRV_KMOD_IOCTL_APP_REGISTER_WSM_L2:
+		/* called by ClientLib */
+		ret = handle_ioctl_app_register_wsm_l2(
+				instance,
+				(union mc_ioctl_app_reg_wsm_l2_params *)arg);
+		break;
+
+	/*--------------------------------------------------------------------*/
+	case MC_DRV_KMOD_IOCTL_APP_UNREGISTER_WSM_L2:
+		/* called by ClientLib */
+		ret = handle_ioctl_app_unregister_wsm_l2(
+				instance,
+				(struct mc_ioctl_app_unreg_wsm_l2_params *)arg);
+		break;
+
+	/*--------------------------------------------------------------------*/
+	case MC_DRV_KMOD_IOCTL_FC_EXECUTE:
+		ret = handle_ioctl_fc_execute(
+				instance,
+				(union mc_ioctl_fc_execute_params *)arg);
+		break;
+
+	/*--------------------------------------------------------------------*/
+	case MC_DRV_KMOD_IOCTL_GET_VERSION:
+		ret = handle_ioctl_get_version(
+				instance,
+				(struct mc_ioctl_get_version_params *)arg);
+		break;
+
+	/*--------------------------------------------------------------------*/
+	default:
+		MCDRV_DBG_ERROR("unsupported cmd=%d\n", cmd);
+		ret = -EFAULT;
+		break;
+
+	} /* end switch(cmd) */
+
+#ifdef MC_MEM_TRACES
+	mobicore_log_read();
+#endif
+
+	return (int)ret;
+}
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * This function will be called from user space as read(...).
+ * The read function is blocking until a interrupt occurs. In that case the
+ * event counter is copied into user space and the function is finished.
+ * @param *file
+ * @param *buffer  buffer where to copy to(userspace)
+ * @param buffer_len	 number of requested data
+ * @param *pos	 not used
+ * @return ssize_t  ok case: number of copied data
+ *				error case: return errno
+ */
+static ssize_t mc_kernel_module_read(
+	struct file	*file,
+	char		*buffer,
+	size_t		buffer_len,
+	loff_t		*pos
+)
+{
+	int ret = 0, ssiq_counter;
+	size_t retLen = 0;
+	struct mc_instance *instance = get_instance(file);
+
+	MCDRV_ASSERT(instance != NULL);
+
+	/* avoid debug output on non-error, because this is call quite often */
+	MCDRV_DBG_VERBOSE("enter\n");
+
+	do {
+		/* only the MobiCore Daemon is allowed to call this function */
+		if (!is_caller_mc_daemon(instance)) {
+			MCDRV_DBG_ERROR("caller not MobiCore Daemon\n");
+			ret = -EFAULT;
+			break;
+		}
+
+		if (buffer_len < sizeof(unsigned int)) {
+			MCDRV_DBG_ERROR("invalid length\n");
+			ret = (ssize_t)(-EINVAL);
+			break;
+		}
+
+		for (;;) {
+			if (down_interruptible(
+					&mc_drv_kmod_ctx.daemon_ctx.sem)) {
+				MCDRV_DBG_VERBOSE("read interrupted\n");
+				ret = (ssize_t)-ERESTARTSYS;
+				break;
+			}
+
+			ssiq_counter = atomic_read(
+					&(mc_drv_kmod_ctx.ssiq_ctx.counter));
+			MCDRV_DBG_VERBOSE("ssiq_counter=%i, ctx.counter=%i\n",
+				ssiq_counter,
+				mc_drv_kmod_ctx.daemon_ctx.ssiq_counter);
+
+			if (ssiq_counter !=
+				mc_drv_kmod_ctx.daemon_ctx.ssiq_counter) {
+				/* read data and exit loop without
+					error */
+				mc_drv_kmod_ctx.daemon_ctx.ssiq_counter =
+					ssiq_counter;
+				ret = 0;
+				break;
+			}
+
+			/* end loop if non-blocking */
+			if ((file->f_flags & O_NONBLOCK) != 0) {
+				MCDRV_DBG_ERROR("non-blocking read\n");
+				ret = (ssize_t)(-EAGAIN);
+				break;
+			}
+
+			if (signal_pending(current) != 0) {
+				MCDRV_DBG_VERBOSE("received signal.\n");
+				ret = (ssize_t)(-ERESTARTSYS);
+				break;
+			}
+
+		}
+
+		/* we are here if an event occurred or we had an
+			error.*/
+		if (ret != 0)
+			break;
+
+		/* read data and exit loop */
+		ret = copy_to_user(
+				  buffer,
+				  &(mc_drv_kmod_ctx.daemon_ctx.ssiq_counter),
+				  sizeof(unsigned int));
+
+
+		if (ret != 0) {
+			MCDRV_DBG_ERROR("copy_to_user failed\n");
+			ret = (ssize_t)(-EFAULT);
+			break;
+		}
+
+		retLen = sizeof(s32);
+
+	} while (FALSE);
+
+	/* avoid debug on non-error. */
+	if (ret == 0)
+		ret = (size_t)retLen;
+	else
+		MCDRV_DBG("exit with %d/0x%08X\n", ret, ret);
+
+	return (ssize_t)ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ * Allocate WSM for given instance
+ *
+ * @param instance		instance
+ * @param requested_size		size of the WSM
+ * @param handle		pointer where the handle will be saved
+ * @param virt_kernel_addr	pointer for the kernel virtual address
+ * @param phys_addr		pointer for the physical address
+ *
+ * @return error code or 0 for success
+ */
+int mobicore_allocate_wsm(
+	struct mc_instance	*instance,
+	unsigned long		requested_size,
+	uint32_t		*handle,
+	void			**virt_kernel_addr,
+	void			**phys_addr
+)
+{
+	unsigned int	i;
+	unsigned int	order;
+	unsigned long	allocated_size;
+	int		ret = 0;
+	struct mc_contg_buffer	*contg_buffer = 0;
+	void		*virt_kernel_addr_stack;
+	void		*phys_addr_stack;
+
+	MCDRV_ASSERT(instance != NULL);
+	MCDRV_DBG("%s (size=%ld)\n", __func__, requested_size);
+
+	order = size_to_order(requested_size);
+	if (order == INVALID_ORDER) {
+		MCDRV_DBG_ERROR(
+			"size to order converting failed for size %ld\n",
+			requested_size);
+		return INVALID_ORDER;
+	}
+
+	allocated_size = (1<<order)*PAGE_SIZE;
+
+	MCDRV_DBG("size %ld -> order %d --> %ld (2^n pages)\n",
+		  requested_size, order, allocated_size);
+
+	do {
+		/* Usual Wsm request, allocate contigous buffer. */
+		/* search for a free entry in the wsm buffer list
+		 * REV axh: serialize this over multiple instances. */
+		for (i = 0; i < MC_DRV_KMOD_CONTG_BUFFER_MAX; i++) {
+			contg_buffer = &(instance->contg_buffers[i]);
+			if (contg_buffer->handle == 0) {
+				contg_buffer->handle = get_mc_kmod_unique_id();
+				break;
+			}
+		}
+		if (i == MC_DRV_KMOD_CONTG_BUFFER_MAX) {
+			MCDRV_DBG_ERROR("no free contigous buffer\n");
+			ret = -EFAULT;
+			break;
+		}
+
+		/* Common code for all allocation paths */
+		virt_kernel_addr_stack = (void *)__get_free_pages(
+							GFP_USER | __GFP_COMP,
+							order);
+		if (virt_kernel_addr_stack == NULL) {
+			MCDRV_DBG_ERROR("get_free_pages failed\n");
+			ret = -ENOMEM;
+			break;
+		}
+
+		/* Get physical address to instance data */
+		phys_addr_stack = (void *)virt_to_phys(virt_kernel_addr_stack);
+		/* TODO: check for INVALID_ADDRESS? */
+
+		MCDRV_DBG(
+			"allocated phys=0x%p - 0x%p, "
+			"size=%ld, kernel_virt=0x%p, handle=%d\n",
+			phys_addr_stack,
+			(void *)((unsigned int)phys_addr_stack+allocated_size),
+			allocated_size,
+			virt_kernel_addr_stack,
+			contg_buffer->handle);
+
+		/* Usual Wsm request, allocate contg_buffer.
+		 *		Also, we never free a persistent Tci */
+		contg_buffer->phys_addr	 = phys_addr_stack;
+		contg_buffer->virt_kernel_addr = virt_kernel_addr_stack;
+		contg_buffer->virt_user_addr   = virt_kernel_addr_stack;
+		contg_buffer->num_pages	 = (1U << order);
+		*handle = contg_buffer->handle;
+		*virt_kernel_addr = virt_kernel_addr_stack;
+		*phys_addr = phys_addr_stack;
+
+	} while (FALSE);
+
+	MCDRV_DBG_VERBOSE("%s: exit with 0x%08X\n", __func__, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(mobicore_allocate_wsm);
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * This function will be called from user space as address = mmap(...).
+ *
+ * @param file
+ * @param vmarea
+ * vmarea.pg_offset != 0 is mapping of MCI is requested
+ *
+ * @return 0 if OK or -ENOMEM in case of error.
+ */
+static int mc_kernel_module_mmap(
+	struct file		*file,
+	struct vm_area_struct	*vmarea
+)
+{
+	unsigned int		i;
+	unsigned int		order;
+	void			*virt_kernel_addr_stack = 0;
+	void			*phys_addr = 0;
+	unsigned long		requested_size =
+					vmarea->vm_end - vmarea->vm_start;
+	unsigned long		allocated_size;
+	int			ret = 0;
+	struct mc_contg_buffer	*contg_buffer = 0;
+	unsigned int		handle = 0;
+	struct mc_instance	*instance = get_instance(file);
+	unsigned int		request = vmarea->vm_pgoff * 4096;
+#if defined(DEBUG)
+	bool release = false;
+#else
+	bool release = true;
+#endif
+
+	MCDRV_ASSERT(instance != NULL);
+	MCDRV_DBG("enter (vmaStart=0x%p, size=%ld, request=0x%x, mci=0x%x)\n",
+		  (void *)vmarea->vm_start,
+		  requested_size,
+		  request,
+		  mci_base);
+
+	order = size_to_order(requested_size);
+	if (order == INVALID_ORDER) {
+		MCDRV_DBG_ERROR(
+			"size to order converting failed for size %ld\n",
+			requested_size);
+		return -ENOMEM;
+	}
+
+	allocated_size = (1<<order)*PAGE_SIZE;
+
+	MCDRV_DBG("size %ld -> order %d --> %ld (2^n pages)\n",
+		  requested_size, order, allocated_size);
+
+	do {
+		/* Daemon tries to get an existing MCI */
+		if ((request == MC_DRV_KMOD_MMAP_MCI) && (mci_base != 0)) {
+			MCDRV_DBG("Request MCI, it is at (%x)\n", mci_base);
+
+			if (!is_caller_mc_daemon(instance)) {
+				ret = -EPERM;
+				break;
+			}
+			virt_kernel_addr_stack = (void *)mci_base;
+			phys_addr =
+				(void *)virt_to_phys(virt_kernel_addr_stack);
+		} else {
+			/* Usual Wsm request, allocate buffer. */
+			if (request == MC_DRV_KMOD_MMAP_WSM) {
+				/* search for a free entry in the buffer list
+				REV axh: serialize this over multiple instances.
+				*/
+				for (i = 0; i < MC_DRV_KMOD_CONTG_BUFFER_MAX;
+					i++) {
+					contg_buffer =
+						&(instance->contg_buffers[i]);
+					if (contg_buffer->handle == 0) {
+						contg_buffer->handle =
+							get_mc_kmod_unique_id();
+						break;
+					}
+				}
+				if (i == MC_DRV_KMOD_CONTG_BUFFER_MAX) {
+					MCDRV_DBG_ERROR(
+						"no free contigous buffer\n");
+					ret = -EFAULT;
+					break;
+				}
+			} else {
+				if (request <= MC_DRV_KMOD_MMAP_PERSISTENTWSM
+					|| release) {
+					/* Special Wsm request
+						--> only Daemon is allowed */
+					if (!is_caller_mc_daemon(instance)) {
+						ret = -EPERM;
+						break;
+					}
+				}
+			}
+			if (request <= MC_DRV_KMOD_MMAP_PERSISTENTWSM) {
+				/* Common code for all allocation paths
+					*  get physical address, */
+				virt_kernel_addr_stack =
+					(void *)__get_free_pages(
+							GFP_USER | __GFP_COMP,
+							order);
+				if (virt_kernel_addr_stack == NULL) {
+					MCDRV_DBG_ERROR(
+						"get_free_pages failed\n");
+					ret = -ENOMEM;
+					break;
+				}
+				if (request == MC_DRV_KMOD_MMAP_WSM)
+					handle = contg_buffer->handle;
+				/* Get physical address to instance data */
+				/* TODO: check for INVALID_ADDRESS? */
+				phys_addr = (void *)virt_to_phys(
+							virt_kernel_addr_stack);
+			} else {
+#if defined(DEBUG)
+				phys_addr = (void *)request;
+				virt_kernel_addr_stack = phys_to_virt(request);
+#endif
+			}
+		}
+		/* Common code for all mmap calls:
+		 * map page to user
+		 * store data in page */
+
+		MCDRV_DBG("allocated phys=0x%p - 0x%p, "
+			"size=%ld, kernel_virt=0x%p, handle=%d\n",
+			phys_addr,
+			(void *)((unsigned int)phys_addr+allocated_size),
+			allocated_size, virt_kernel_addr_stack, handle);
+
+		vmarea->vm_flags |= VM_RESERVED;
+		/* convert Kernel address to User Address. Kernel address begins
+			at PAGE_OFFSET, user Address range is below PAGE_OFFSET.
+			Remapping the area is always done, so multiple mappings
+			of one region are possible. Now remap kernel address
+			space into user space */
+		ret = (int)remap_pfn_range(
+				vmarea,
+				(vmarea->vm_start),
+				addr_to_pfn(phys_addr),
+				requested_size,
+				vmarea->vm_page_prot);
+		if (ret != 0) {
+			MCDRV_DBG_ERROR("remapPfnRange failed\n");
+
+			/* free allocated pages when mmap fails, however, do not
+				do it, when daemon tried to get an MCI that
+				existed */
+			if (!((request == MC_DRV_KMOD_MMAP_MCI) &&
+				  (mci_base != 0)))
+				free_continguous_pages(virt_kernel_addr_stack,
+							(1U << order));
+			break;
+		}
+
+		/* Usual Wsm request, allocate contg_buffer.
+			When requesting Mci, we do not associate the page with
+			the process.
+			Note: we also never free the Mci
+			Also, we never free a persistent Tci */
+		if (request == MC_DRV_KMOD_MMAP_WSM) {
+			contg_buffer->phys_addr = phys_addr;
+			contg_buffer->virt_kernel_addr = virt_kernel_addr_stack;
+			contg_buffer->virt_user_addr =
+						(void *)(vmarea->vm_start);
+			contg_buffer->num_pages = (1U << order);
+		}
+
+		/* set response in allocated buffer */
+		{
+			struct mc_mmap_resp *mmap_resp =
+				(struct mc_mmap_resp *)virt_kernel_addr_stack;
+			/* TODO: do this for daemon only, otherwise set NULL */
+			mmap_resp->phys_addr = (uint32_t)phys_addr;
+			mmap_resp->handle = handle;
+			if ((request == MC_DRV_KMOD_MMAP_MCI) &&
+				(mci_base != 0)) {
+				mmap_resp->is_reused = 1;
+			} else
+				mmap_resp->is_reused = 0;
+		}
+
+		/* store MCI pointer */
+		if ((request == MC_DRV_KMOD_MMAP_MCI) && (mci_base == 0)) {
+			mci_base = (uint32_t)virt_kernel_addr_stack;
+			MCDRV_DBG("MCI base set to 0x%x\n", mci_base);
+		}
+	} while (FALSE);
+
+	MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+	return (int)ret;
+}
+
+#ifdef CONFIG_SMP
+/*----------------------------------------------------------------------------*/
+/**
+ * Force migration of current task to CPU0(where the monitor resides)
+ *
+ * @return Error code or 0 for success
+ */
+static int goto_cpu0(
+	void
+)
+{
+	int		ret = 0;
+	struct cpumask	mask =  CPU_MASK_CPU0;
+
+	MCDRV_DBG_VERBOSE("System has %d CPU's, we are on CPU #%d\n"
+		  "\tBinding this process to CPU #0.\n"
+		  "\tactive mask is %lx, setting it to mask=%lx\n",
+		  nr_cpu_ids,
+		  raw_smp_processor_id(),
+		  cpu_active_mask->bits[0],
+		  mask.bits[0]);
+	ret = set_cpus_allowed_ptr(current, &mask);
+	if (ret != 0)
+		MCDRV_DBG_ERROR("set_cpus_allowed_ptr=%d.\n", ret);
+	MCDRV_DBG_VERBOSE("And now we are on CPU #%d\n",
+				raw_smp_processor_id());
+
+	return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ * Restore CPU mask for current to ALL Cpus(reverse of goto_cpu0)
+ *
+ * @return Error code or 0 for success
+ */
+static int goto_all_cpu(
+	void
+)
+{
+	int		ret = 0;
+
+	struct cpumask	mask =  CPU_MASK_ALL;
+
+	MCDRV_DBG_VERBOSE("System has %d CPU's, we are on CPU #%d\n"
+		  "\tBinding this process to CPU #0.\n"
+		  "\tactive mask is %lx, setting it to mask=%lx\n",
+		  nr_cpu_ids,
+		  raw_smp_processor_id(),
+		  cpu_active_mask->bits[0],
+		  mask.bits[0]);
+	ret = set_cpus_allowed_ptr(current, &mask);
+	if (ret != 0)
+		MCDRV_DBG_ERROR("set_cpus_allowed_ptr=%d.\n", ret);
+	MCDRV_DBG_VERBOSE("And now we are on CPU #%d\n",
+				raw_smp_processor_id());
+
+	return ret;
+}
+
+#else
+static int goto_cpu0(void)
+{
+	return 0;
+}
+
+static int goto_all_cpu(void)
+{
+	return 0;
+}
+#endif
+
+/*----------------------------------------------------------------------------*/
+/**
+ * Initialize a new mobicore API instance object
+ *
+ * @return Instance or NULL if no allocation was possible.
+ */
+struct mc_instance *mobicore_open(
+	void
+) {
+	struct mc_instance	*instance;
+	pid_t			pid_vnr;
+
+	instance = kzalloc(sizeof(*instance), GFP_KERNEL);
+	if (instance == NULL)
+		return NULL;
+
+	/* get a unique ID for this instance (PIDs are not unique) */
+	instance->handle = get_mc_kmod_unique_id();
+
+	/* get the PID of the calling process. We avoid using
+	 *	current->pid directly, as 2.6.24 introduced PID
+	 *	namespaces. See also http://lwn.net/Articles/259217 */
+	pid_vnr = task_pid_vnr(current);
+	instance->pid_vnr = pid_vnr;
+
+	return instance;
+}
+EXPORT_SYMBOL(mobicore_open);
+
+/*----------------------------------------------------------------------------*/
+/**
+ * This function will be called from user space as fd = open(...).
+ * A set of internal instance data are created and initialized.
+ *
+ * @param inode
+ * @param file
+ * @return 0 if OK or -ENOMEM if no allocation was possible.
+ */
+static int mc_kernel_module_open(
+	struct inode	*inode,
+	struct file	*file
+)
+{
+	struct mc_instance	*instance;
+	int			ret = 0;
+
+	MCDRV_DBG_VERBOSE("enter\n");
+
+	do {
+		instance = mobicore_open();
+		if (instance == NULL)
+			return -ENOMEM;
+
+		/* check if Daemon. We simply assume that the first to open us
+			with root privileges must be the daemon. */
+		if ((is_userland_caller_privileged())
+			&& (mc_drv_kmod_ctx.daemon_inst == NULL)) {
+			MCDRV_DBG("accept this as MobiCore Daemon\n");
+
+			/* Set the caller's CPU mask to CPU0*/
+			ret = goto_cpu0();
+			if (ret != 0) {
+				mobicore_release(instance);
+				file->private_data = NULL;
+				MCDRV_DBG("changing core failed!\n");
+				break;
+			}
+
+			mc_drv_kmod_ctx.daemon_inst = instance;
+			sema_init(&mc_drv_kmod_ctx.daemon_ctx.sem,
+					DAEMON_SEM_VAL);
+			/* init ssiq event counter */
+			mc_drv_kmod_ctx.daemon_ctx.ssiq_counter =
+				atomic_read(
+					&(mc_drv_kmod_ctx.ssiq_ctx.counter));
+
+#ifdef MC_MEM_TRACES
+			/* The traces have to be setup on CPU-0 since we must
+			 * do a fastcall to MobiCore. */
+			if (!mci_base)
+				/* Do the work only if MCI base is not
+				 * initialized properly */
+				work_on_cpu(0, mobicore_log_setup, NULL);
+#endif
+		}
+
+		/* store instance data reference */
+		file->private_data = instance;
+
+		/* TODO axh: link all instances to allow clean up? */
+
+	} while (FALSE);
+
+	MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+	return (int)ret;
+
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ * Release a mobicore instance object and all objects related to it
+ * @param instance instance
+ * @return 0 if Ok or -E ERROR
+ */
+int mobicore_release(
+	struct mc_instance	*instance
+)
+{
+	int ret = 0;
+	int i;
+	struct mc_used_l2_table	*used_l2table, *used_l2table_temp;
+
+	do {
+		/* try to get the semaphore */
+		ret = down_interruptible(&(mc_drv_kmod_ctx.wsm_l2_sem));
+		if (ret != 0) {
+			MCDRV_DBG_ERROR(
+				"down_interruptible() failed with %d\n", ret);
+			/* TODO: can be block here? */
+			ret = -ERESTARTSYS;
+		} else {
+			/* Check if some WSM is still in use. */
+			list_for_each_entry_safe(
+				used_l2table,
+				used_l2table_temp,
+				&(mc_drv_kmod_ctx.mc_used_l2_tables),
+				list
+			) {
+				if (used_l2table->owner == instance) {
+					MCDRV_DBG_WARN(
+						"trying to release WSM L2: "
+						"physBase=%p ,nr_of_pages=%d\n",
+						get_l2_table_phys(used_l2table),
+						used_l2table->nr_of_pages);
+
+					/* unlock app usage and free if MobiCore
+					does not use it */
+					delete_used_l2_table(used_l2table,
+							FREE_FROM_NWD);
+				}
+			} /* end while */
+
+			/* release semaphore */
+			up(&(mc_drv_kmod_ctx.wsm_l2_sem));
+		}
+
+
+		/* release all mapped data */
+		for (i = 0; i < MC_DRV_KMOD_CONTG_BUFFER_MAX; i++) {
+			struct mc_contg_buffer *contg_buffer =
+					&(instance->contg_buffers[i]);
+
+			if (contg_buffer->virt_user_addr != 0) {
+				free_continguous_pages(
+					contg_buffer->virt_kernel_addr,
+					contg_buffer->num_pages);
+			}
+		}
+
+		/* release instance context */
+		kfree(instance);
+	} while (FALSE);
+
+	return ret;
+}
+EXPORT_SYMBOL(mobicore_release);
+
+/*----------------------------------------------------------------------------*/
+/**
+ * This function will be called from user space as close(...).
+ * The instance data are freed and the associated memory pages are unreserved.
+ *
+ * @param inode
+ * @param file
+ *
+ * @return 0
+ */
+static int mc_kernel_module_release(
+	struct inode	*inode,
+	struct file	*file
+)
+{
+	int			ret = 0;
+	struct mc_instance	*instance = get_instance(file);
+
+	MCDRV_DBG_VERBOSE("enter\n");
+
+	do {
+		/* check if daemon closes us. */
+		if (is_caller_mc_daemon(instance)) {
+			/* TODO: cleanup?
+				* mc_drv_kmod_ctx.mc_used_l2_tables remains */
+			MCDRV_DBG_WARN("WARNING: MobiCore Daemon died\n");
+			mc_drv_kmod_ctx.daemon_inst = NULL;
+		}
+
+		ret = mobicore_release(instance);
+
+	} while (FALSE);
+
+	MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+	return (int)ret;
+}
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * This function represents the interrupt function of the mcDrvModule.
+ * It signals by incrementing of an event counter and the start of the read
+ * waiting queue, the read function a interrupt has occurred.
+ *
+ * @param   intr
+ * @param   *context  pointer to registered device data
+ *
+ * @return  IRQ_HANDLED
+ */
+static irqreturn_t mc_kernel_module_intr_ssiq(
+	int	intr,
+	void	*context
+)
+{
+	irqreturn_t	ret = IRQ_NONE;
+
+	/* we know the context. */
+	MCDRV_ASSERT(&mc_drv_kmod_ctx == context);
+
+	do {
+		if (intr != MC_INTR_SSIQ) {
+			/* this should not happen, as we did no register for any
+				other interrupt. For debugging, we print a
+				message, but continue */
+			MCDRV_DBG_WARN(
+				"unknown interrupt %d, expecting only %d\n",
+				intr, MC_INTR_SSIQ);
+		}
+		MCDRV_DBG_VERBOSE("received interrupt %d\n",
+				  intr);
+
+		/* increment interrupt event counter */
+		atomic_inc(&(mc_drv_kmod_ctx.ssiq_ctx.counter));
+
+		/* signal the daemon */
+		up(&mc_drv_kmod_ctx.daemon_ctx.sem);
+
+
+		ret = IRQ_HANDLED;
+
+	} while (FALSE);
+
+	return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/** function table structure of this device driver. */
+static const struct file_operations mc_kernel_module_file_operations = {
+	.owner		= THIS_MODULE, /**< driver owner */
+	.open		= mc_kernel_module_open, /**< driver open function */
+	.release	= mc_kernel_module_release, /**< driver release function*/
+	.unlocked_ioctl	= mc_kernel_module_ioctl, /**< driver ioctl function */
+	.mmap		= mc_kernel_module_mmap, /**< driver mmap function */
+	.read		= mc_kernel_module_read, /**< driver read function */
+};
+
+/*----------------------------------------------------------------------------*/
+/** registration structure as miscdevice. */
+static struct miscdevice mc_kernel_module_device = {
+	.name	= MC_DRV_MOD_DEVNODE, /**< device name */
+	.minor	= MISC_DYNAMIC_MINOR, /**< device minor number */
+	/** device interface function structure */
+	.fops	= &mc_kernel_module_file_operations,
+};
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * This function is called the kernel during startup or by a insmod command.
+ * This device is installed and registered as miscdevice, then interrupt and
+ * queue handling is set up
+ *
+ * @return 0 for no error or -EIO if registration fails
+ */
+static int __init mc_kernel_module_init(
+	void
+)
+{
+	int ret = 0;
+
+	MCDRV_DBG("enter (Build " __TIMESTAMP__ ")\n");
+	MCDRV_DBG("mcDrvModuleApi version is %i.%i\n",
+			MCDRVMODULEAPI_VERSION_MAJOR,
+			MCDRVMODULEAPI_VERSION_MINOR);
+#ifdef MOBICORE_COMPONENT_BUILD_TAG
+	MCDRV_DBG("%s\n", MOBICORE_COMPONENT_BUILD_TAG);
+#endif
+	do {
+		/* Hardware does not support ARM TrustZone
+			-> Cannot continue! */
+		if (!has_security_extensions()) {
+			MCDRV_DBG_ERROR(
+				"Hardware does't support ARM TrustZone!\n");
+			ret = -ENODEV;
+			break;
+		}
+
+		/* Running in secure mode -> Cannot load the driver! */
+		if (is_secure_mode()) {
+			MCDRV_DBG_ERROR("Running in secure MODE!\n");
+			ret = -ENODEV;
+			break;
+		}
+
+		sema_init(&mc_drv_kmod_ctx.daemon_ctx.sem, DAEMON_SEM_VAL);
+		/* set up S-SIQ interrupt handler */
+		ret = request_irq(
+				  MC_INTR_SSIQ,
+				  mc_kernel_module_intr_ssiq,
+				  IRQF_TRIGGER_RISING,
+				  MC_DRV_MOD_DEVNODE,
+				  &mc_drv_kmod_ctx);
+		if (ret != 0) {
+			MCDRV_DBG_ERROR("interrupt request failed\n");
+			break;
+		}
+
+		ret = misc_register(&mc_kernel_module_device);
+		if (ret != 0) {
+			MCDRV_DBG_ERROR("device register failed\n");
+			break;
+		}
+
+		/* initialize event counter for signaling of an IRQ to zero */
+		atomic_set(&(mc_drv_kmod_ctx.ssiq_ctx.counter), 0);
+
+		/* init list for WSM L2 chunks. */
+		INIT_LIST_HEAD(&(mc_drv_kmod_ctx.mc_l2_tables_sets));
+
+		/* L2 table descriptor list. */
+		INIT_LIST_HEAD(&(mc_drv_kmod_ctx.mc_used_l2_tables));
+
+		sema_init(&(mc_drv_kmod_ctx.wsm_l2_sem), 1);
+
+		/* initialize unique number counter which we can use for
+			handles. It is limited to 2^32, but this should be
+			enough to be roll-over safe for us. We start with 1
+			instead of 0. */
+		atomic_set(&(mc_drv_kmod_ctx.unique_counter), 1);
+
+		mci_base = 0;
+		MCDRV_DBG("initialized\n");
+
+		ret = 0;
+
+	} while (FALSE);
+
+	MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+	return (int)ret;
+}
+
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * This function removes this device driver from the Linux device manager .
+ */
+static void __exit mc_kernel_module_exit(
+	void
+)
+{
+	struct mc_used_l2_table	*used_l2table;
+
+	MCDRV_DBG_VERBOSE("enter\n");
+
+	mobicore_log_free();
+
+	/* Check if some WSM is still in use. */
+	list_for_each_entry(
+		used_l2table,
+		&(mc_drv_kmod_ctx.mc_used_l2_tables),
+		list
+	) {
+		MCDRV_DBG_WARN(
+			"WSM L2 still in use: physBase=%p ,nr_of_pages=%d\n",
+			get_l2_table_phys(used_l2table),
+			used_l2table->nr_of_pages);
+	} /* end while */
+
+	free_irq(MC_INTR_SSIQ, &mc_drv_kmod_ctx);
+
+	misc_deregister(&mc_kernel_module_device);
+	MCDRV_DBG_VERBOSE("exit");
+}
+
+
+/*----------------------------------------------------------------------------*/
+/* Linux Driver Module Macros */
+module_init(mc_kernel_module_init);
+module_exit(mc_kernel_module_exit);
+MODULE_AUTHOR("Giesecke & Devrient GmbH");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MobiCore driver");
+
+/** @} */
+
diff --git a/drivers/gud/mobicore_driver/mc_drv_module.h b/drivers/gud/mobicore_driver/mc_drv_module.h
new file mode 100644
index 0000000..8b402d6
--- /dev/null
+++ b/drivers/gud/mobicore_driver/mc_drv_module.h
@@ -0,0 +1,238 @@
+/**
+ * Header file of MobiCore Driver Kernel Module.
+ *
+ * @addtogroup MCD_MCDIMPL_KMOD_IMPL
+ * @{
+ * Internal structures of the McDrvModule
+ * @file
+ *
+ * Header file the MobiCore Driver Kernel Module,
+ * its internal structures and defines.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _MC_DRV_KMOD_H_
+#define _MC_DRV_KMOD_H_
+
+#include "mc_drv_module_linux_api.h"
+#include "public/mc_drv_module_api.h"
+/** Platform specific settings */
+#include "platform.h"
+
+/** ARM Specific masks and modes */
+#define ARM_CPSR_MASK 0x1F
+#define ARM_MONITOR_MODE 0b10110
+#define ARM_SECURITY_EXTENSION_MASK 0x30
+
+/**
+ * Number of page table entries in one L2 table. This is ARM specific, an
+ *  L2 table covers 1 MiB by using 256 entry referring to 4KiB pages each.
+ */
+#define MC_ARM_L2_TABLE_ENTRIES		256
+
+/** Maximum number of contiguous buffer allocations for one driver instance. */
+#define MC_DRV_KMOD_CONTG_BUFFER_MAX	16
+
+/** Number of pages for L2 tables. There are 4 table in each page. */
+#define MC_DRV_KMOD_L2_TABLE_PER_PAGES	4
+
+/** ARM level 2 (L2) table with 256 entries. Size: 1k */
+struct l2table {
+	pte_t	table_entries[MC_ARM_L2_TABLE_ENTRIES];
+};
+
+#define INVALID_ADDRESS     ((void *)(-1))
+
+/** ARM L2 PTE bits */
+#define L2_FLAG_SMALL_XN    (1U <<  0)
+#define L2_FLAG_SMALL       (1U <<  1)
+#define L2_FLAG_B           (1U <<  2)
+#define L2_FLAG_C           (1U <<  3)
+#define L2_FLAG_AP0         (1U <<  4)
+#define L2_FLAG_AP1         (1U <<  5)
+#define L2_FLAG_SMALL_TEX0  (1U <<  6)
+#define L2_FLAG_SMALL_TEX1  (1U <<  7)
+#define L2_FLAG_SMALL_TEX2  (1U <<  8)
+#define L2_FLAG_APX         (1U <<  9)
+#define L2_FLAG_S           (1U << 10)
+#define L2_FLAG_NG          (1U << 11)
+
+/**
+ * Contiguous buffer allocated to TLCs.
+ * These buffers are uses as world shared memory (wsm) and shared with
+ * secure world.
+ * The virtual kernel address is added for a simpler search algorithm.
+ */
+struct mc_contg_buffer {
+	unsigned int	handle; /* unique handle */
+	void		*virt_user_addr; /**< virtual User start address */
+	void		*virt_kernel_addr; /**< virtual Kernel start address */
+	void		*phys_addr; /**< physical start address */
+	unsigned int	num_pages; /**< number of pages */
+};
+
+/** Instance data for MobiCore Daemon and TLCs. */
+struct mc_instance {
+	/** unique handle */
+	unsigned int	handle;
+	/** process that opened this instance */
+	pid_t		pid_vnr;
+	/** buffer list for mmap generated address space and
+		its virtual client address */
+	struct mc_contg_buffer	contg_buffers[MC_DRV_KMOD_CONTG_BUFFER_MAX];
+};
+
+/** Store for four L2 tables in one 4kb page*/
+struct mc_l2_table_store {
+	struct l2table table[MC_DRV_KMOD_L2_TABLE_PER_PAGES];
+};
+
+/** Usage and maintenance information about mc_l2_table_store */
+struct mc_l2_tables_set {
+	struct list_head		list;
+	unsigned int			usage_bitmap;	/**< usage bitmap */
+	struct mc_l2_table_store	*kernel_virt;	/**< kernel virtual address */
+	struct mc_l2_table_store	*phys;		/**< physical address */
+	struct page			*page;		/**< pointer to page struct */
+};
+
+/**
+ * L2 table allocated to the Daemon or a TLC describing a world shared buffer.
+ * When users map a malloc()ed area into SWd, a L2 table is allocated.
+ * In addition, the area of maximum 1MB virtual address space is mapped into
+ * the L2 table and a handle for this table is returned to the user.
+ */
+struct mc_used_l2_table {
+	struct list_head	list;
+
+	/** handle as communicated to user mode */
+	unsigned int		handle;
+	unsigned int		flags;
+
+	/** owner of this L2 table */
+	struct mc_instance	*owner;
+
+	/** set describing where our L2 table is stored */
+	struct mc_l2_tables_set	*set;
+
+	/** index into L2 table set */
+	unsigned int		idx;
+
+	/** size of buffer */
+	unsigned int		nr_of_pages;
+};
+
+#define MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_APP   (1U << 0)
+#define MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC    (1U << 1)
+
+
+/** MobiCore S-SIQ interrupt context data. */
+struct mc_ssiq_ctx {
+	/** S-SIQ interrupt counter */
+	atomic_t	counter;
+};
+
+/** MobiCore Daemon context data. */
+struct mc_daemon_ctx {
+	/** event semaphore */
+	struct semaphore	sem;
+	struct fasync_struct	*async_queue;
+	/** event counter */
+	unsigned int		ssiq_counter;
+};
+
+/** MobiCore Driver Kernel Module context data. */
+struct mc_drv_kmod_ctx {
+
+	/** ever incrementing counter */
+	atomic_t		unique_counter;
+
+	/** S-SIQ interrupt context */
+	struct mc_ssiq_ctx	ssiq_ctx;
+
+	/** MobiCore Daemon context */
+	struct mc_daemon_ctx	daemon_ctx;
+
+	/** pointer to instance of daemon */
+	struct mc_instance	*daemon_inst;
+
+	/** Backing store for L2 tables */
+	struct list_head	mc_l2_tables_sets;
+
+	/** Bookkeeping for used L2 tables */
+	struct list_head	mc_used_l2_tables;
+
+	/** semaphore to synchronize access to above lists */
+	struct semaphore	wsm_l2_sem;
+};
+
+/** MobiCore internal trace buffer structure. */
+struct mc_trace_buf {
+	uint32_t version; /**< version of trace buffer */
+	uint32_t length; /**< length of allocated buffer(includes header) */
+	uint32_t write_pos; /**< last write position */
+	char  buff[1]; /**< start of the log buffer */
+};
+
+/*** MobiCore internal trace log setup. */
+void mobicore_log_read(void);
+long mobicore_log_setup(void *);
+void mobicore_log_free(void);
+
+#define MCDRV_DBG_ERROR(txt, ...) \
+	printk(KERN_ERR "mcDrvKMod [%d] %s() ### ERROR: " txt, \
+		task_pid_vnr(current), \
+		__func__, \
+		##__VA_ARGS__)
+
+/* dummy function helper macro. */
+#define DUMMY_FUNCTION()    do {} while (0)
+
+#if defined(DEBUG)
+
+/* #define DEBUG_VERBOSE */
+#if defined(DEBUG_VERBOSE)
+#define MCDRV_DBG_VERBOSE          MCDRV_DBG
+#else
+#define MCDRV_DBG_VERBOSE(...)     DUMMY_FUNCTION()
+#endif
+
+#define MCDRV_DBG(txt, ...) \
+	printk(KERN_INFO "mcDrvKMod [%d on CPU%d] %s(): " txt, \
+		task_pid_vnr(current), \
+		raw_smp_processor_id(), \
+		__func__, \
+		##__VA_ARGS__)
+
+#define MCDRV_DBG_WARN(txt, ...) \
+	printk(KERN_WARNING "mcDrvKMod [%d] %s() WARNING: " txt, \
+		task_pid_vnr(current), \
+		__func__, \
+		##__VA_ARGS__)
+
+#define MCDRV_ASSERT(cond) \
+	do { \
+		if (unlikely(!(cond))) { \
+			panic("mcDrvKMod Assertion failed: %s:%d\n", \
+				__FILE__, __LINE__); \
+		} \
+	} while (0)
+
+#else
+
+#define MCDRV_DBG_VERBOSE(...)	DUMMY_FUNCTION()
+#define MCDRV_DBG(...)		DUMMY_FUNCTION()
+#define MCDRV_DBG_WARN(...)	DUMMY_FUNCTION()
+
+#define MCDRV_ASSERT(...)	DUMMY_FUNCTION()
+
+#endif /* [not] defined(DEBUG) */
+
+
+#endif /* _MC_DRV_KMOD_H_ */
+/** @} */
diff --git a/drivers/gud/mobicore_driver/mc_drv_module_android.h b/drivers/gud/mobicore_driver/mc_drv_module_android.h
new file mode 100644
index 0000000..319509f
--- /dev/null
+++ b/drivers/gud/mobicore_driver/mc_drv_module_android.h
@@ -0,0 +1,37 @@
+/**
+ * Header file of MobiCore Driver Kernel Module.
+ *
+ * @addtogroup MobiCore_Driver_Kernel_Module
+ * @{
+ * Android specific defines
+ * @file
+ *
+ * Android specific defines
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _MC_DRV_MODULE_ANDROID_H_
+#define _MC_DRV_MODULE_ANDROID_H_
+
+/* Defines needed to identify the Daemon in Android systems
+ * For the full list see:
+ * platform_system_core/include/private/android_filesystem_config.h in the
+ * Android source tree
+ */
+/* traditional unix root user */
+#define AID_ROOT	0
+/* system server */
+#define AID_SYSTEM	1000
+/* access to misc storage */
+#define AID_MISC	9998
+#define AID_NOBODY	9999
+/* first app user */
+#define AID_APP		10000
+
+#endif /* _MC_DRV_MODULE_ANDROID_H_ */
+/** @} */
diff --git a/drivers/gud/mobicore_driver/mc_drv_module_fastcalls.h b/drivers/gud/mobicore_driver/mc_drv_module_fastcalls.h
new file mode 100644
index 0000000..d058043
--- /dev/null
+++ b/drivers/gud/mobicore_driver/mc_drv_module_fastcalls.h
@@ -0,0 +1,227 @@
+/**
+ * Header file of MobiCore Driver Kernel Module.
+ *
+ * @addtogroup MobiCore_Driver_Kernel_Module
+ * @{
+ * Internal structures of the McDrvModule
+ * @file
+ *
+ * MobiCore Fast Call interface
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _MC_DRV_MODULE_FC_H_
+#define _MC_DRV_MODULE_FC_H_
+
+#include "mc_drv_module.h"
+
+/**
+ * MobiCore SMCs
+ */
+enum mc_smc_codes {
+	MC_SMC_N_YIELD  = 0x3, /**< Yield to switch from NWd to SWd. */
+	MC_SMC_N_SIQ    = 0x4  /**< SIQ to switch from NWd to SWd. */
+};
+
+/**
+ * MobiCore fast calls. See MCI documentation
+ */
+enum mc_fast_call_codes {
+	MC_FC_INIT      = -1,
+	MC_FC_INFO      = -2,
+	MC_FC_POWER     = -3,
+	MC_FC_DUMP      = -4,
+	MC_FC_NWD_TRACE = -31 /**< Mem trace setup fastcall */
+};
+
+/**
+ * return code for fast calls
+ */
+enum mc_fast_calls_result {
+	MC_FC_RET_OK                       = 0,
+	MC_FC_RET_ERR_INVALID              = 1,
+	MC_FC_RET_ERR_ALREADY_INITIALIZED  = 5
+};
+
+
+
+/*------------------------------------------------------------------------------
+	structure wrappers for specific fastcalls
+------------------------------------------------------------------------------*/
+
+/** generic fast call parameters */
+union fc_generic {
+	struct {
+		uint32_t cmd;
+		uint32_t param[3];
+	} as_in;
+	struct {
+		uint32_t resp;
+		uint32_t ret;
+		uint32_t param[2];
+	} as_out;
+};
+
+
+/** fast call init */
+union mc_fc_init {
+	union fc_generic as_generic;
+	struct {
+		uint32_t cmd;
+		uint32_t base;
+		uint32_t nq_info;
+		uint32_t mcp_info;
+	} as_in;
+	struct {
+		uint32_t resp;
+		uint32_t ret;
+		uint32_t rfu[2];
+	} as_out;
+};
+
+
+/** fast call info parameters */
+union mc_fc_info {
+	union fc_generic as_generic;
+	struct {
+		uint32_t cmd;
+		uint32_t ext_info_id;
+		uint32_t rfu[2];
+	} as_in;
+	struct {
+		uint32_t resp;
+		uint32_t ret;
+		uint32_t state;
+		uint32_t ext_info;
+	} as_out;
+};
+
+
+/** fast call S-Yield parameters */
+union mc_fc_s_yield {
+	union fc_generic as_generic;
+	struct {
+		uint32_t cmd;
+		uint32_t rfu[3];
+	} as_in;
+	struct {
+		uint32_t resp;
+		uint32_t ret;
+		uint32_t rfu[2];
+	} as_out;
+};
+
+
+/** fast call N-SIQ parameters */
+union mc_fc_nsiq {
+	union fc_generic as_generic;
+	struct {
+		uint32_t cmd;
+		uint32_t rfu[3];
+	} as_in;
+	struct {
+		uint32_t resp;
+		uint32_t ret;
+		uint32_t rfu[2];
+	} as_out;
+};
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * fast call to MobiCore
+ *
+ * @param fc_generic pointer to fast call data
+ */
+static inline void mc_fastcall(
+	union fc_generic *fc_generic
+)
+{
+	MCDRV_ASSERT(fc_generic != NULL);
+	/* We only expect to make smc calls on CPU0 otherwise something wrong
+	 * will happen */
+	MCDRV_ASSERT(raw_smp_processor_id() == 0);
+	mb();
+#ifdef MC_SMC_FASTCALL
+	{
+		int ret = 0;
+		MCDRV_DBG("Going into SCM()");
+		ret = smc_fastcall((void *)fc_generic, sizeof(*fc_generic));
+		MCDRV_DBG("Coming from SCM, scm_call=%i, resp=%d/0x%x\n",
+			ret,
+			fc_generic->as_out.resp, fc_generic->as_out.resp);
+	}
+#else
+	{
+		/* SVC expect values in r0-r3 */
+		register u32 reg0 __asm__("r0") = fc_generic->as_in.cmd;
+		register u32 reg1 __asm__("r1") = fc_generic->as_in.param[0];
+		register u32 reg2 __asm__("r2") = fc_generic->as_in.param[1];
+		register u32 reg3 __asm__("r3") = fc_generic->as_in.param[2];
+
+		/* one of the famous preprocessor hacks to stingitize things.*/
+#define __STR2(x)   #x
+#define __STR(x)    __STR2(x)
+
+		/* compiler does not support certain instructions
+		"SMC": secure monitor call.*/
+#define ASM_ARM_SMC         0xE1600070
+		/*   "BPKT": debugging breakpoint. We keep this, as is comes
+				quite handy for debugging. */
+#define ASM_ARM_BPKT        0xE1200070
+#define ASM_THUMB_BPKT      0xBE00
+
+
+		__asm__ volatile (
+			".word " __STR(ASM_ARM_SMC) "\n"
+			: "+r"(reg0), "+r"(reg1), "+r"(reg2), "+r"(reg3)
+		);
+
+		/* set response */
+		fc_generic->as_out.resp     = reg0;
+		fc_generic->as_out.ret      = reg1;
+		fc_generic->as_out.param[0] = reg2;
+		fc_generic->as_out.param[1] = reg3;
+	}
+#endif
+}
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * convert fast call return code to linux driver module error code
+ *
+ */
+static inline int convert_fc_ret(
+	uint32_t sret
+)
+{
+	int         ret = -EFAULT;
+
+	switch (sret) {
+
+	case MC_FC_RET_OK:
+		ret = 0;
+		break;
+
+	case MC_FC_RET_ERR_INVALID:
+		ret = -EINVAL;
+		break;
+
+	case MC_FC_RET_ERR_ALREADY_INITIALIZED:
+		ret = -EBUSY;
+		break;
+
+	default:
+		break;
+	} /* end switch( sret ) */
+	return ret;
+}
+
+#endif /* _MC_DRV_MODULE_FC_H_ */
+/** @} */
diff --git a/drivers/gud/mobicore_driver/mc_drv_module_linux_api.h b/drivers/gud/mobicore_driver/mc_drv_module_linux_api.h
new file mode 100644
index 0000000..b2a99f1
--- /dev/null
+++ b/drivers/gud/mobicore_driver/mc_drv_module_linux_api.h
@@ -0,0 +1,187 @@
+/**
+ * Header file of MobiCore Driver Kernel Module.
+ *
+ * @addtogroup MobiCore_Driver_Kernel_Module
+ * @{
+ * Wrapper for Linux API
+ * @file
+ *
+ * Some convenient wrappers for memory functions
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _MC_DRV_MODULE_LINUX_API_H_
+#define _MC_DRV_MODULE_LINUX_API_H_
+
+#include <linux/version.h>
+#include <linux/miscdevice.h>
+#include <linux/interrupt.h>
+#include <linux/highmem.h>
+#include <linux/kthread.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <asm/sizes.h>
+#include <asm/pgtable.h>
+#include <linux/semaphore.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+
+
+/* make some nice types */
+#if !defined(TRUE)
+#define TRUE (1 == 1)
+#endif
+
+#if !defined(FALSE)
+#define FALSE (1 != 1)
+#endif
+
+
+/* Linux GCC modifiers */
+#if !defined(__init)
+#warning "missing definition: __init"
+/* define a dummy */
+#define __init
+#endif
+
+
+#if !defined(__exit)
+#warning "missing definition: __exit"
+/* define a dummy */
+#define __exit
+#endif
+
+
+#if !defined(__must_check)
+#warning "missing definition: __must_check"
+/* define a dummy */
+#define __must_check
+#endif
+
+
+#if !defined(__user)
+#warning "missing definition: __user"
+/* define a dummy */
+#define __user
+#endif
+
+#define INVALID_ORDER       ((unsigned int)(-1))
+
+/*----------------------------------------------------------------------------*/
+/* get start address of the 4 KiB page where the given addres is located in. */
+static inline void *get_page_start(
+	void *addr
+)
+{
+	return (void *)(((unsigned long)(addr)) & PAGE_MASK);
+}
+
+/*----------------------------------------------------------------------------*/
+/* get offset into the 4 KiB page where the given addres is located in. */
+static inline unsigned int get_offset_in_page(
+	void *addr
+)
+{
+	return (unsigned int)(((unsigned long)(addr)) & (~PAGE_MASK));
+}
+
+/*----------------------------------------------------------------------------*/
+/* get number of pages for a given buffer. */
+static inline unsigned int get_nr_of_pages_for_buffer(
+	void		*addr_start, /* may be null */
+	unsigned int	len
+)
+{
+	/* calculate used number of pages. Example:
+	offset+size    newSize+PAGE_SIZE-1    nr_of_pages
+	   0              4095                   0
+	   1              4096                   1
+	  4095            8190                   1
+	  4096            8191                   1
+	  4097            8192                   2 */
+
+	return (get_offset_in_page(addr_start) + len + PAGE_SIZE-1) / PAGE_SIZE;
+}
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * convert a given size to page order, which is equivalent to finding log_2(x).
+ * The maximum for order was 5 in Linux 2.0 corresponding to 32 pages.
+ * Later versions allow 9 corresponding to 512 pages, which is 2 MB on
+ * most platforms). Anyway, the bigger order is, the more likely it is
+ * that the allocation will fail.
+ * Size       0           1  4097  8193  12289  24577  28673   40961   61441
+ * Pages      -           1     2     3      4      7      8      15      16
+ * Order  INVALID_ORDER   0     1     1      2      2      3       3       4
+ *
+ * @param  size
+ * @return order
+ */
+static inline unsigned int size_to_order(
+	unsigned int size
+)
+{
+	unsigned int order = INVALID_ORDER;
+
+	if (size != 0) {
+		/* ARMv5 as a CLZ instruction which count the leading zeros of
+		the binary representation of a value. It return a value
+		between 0 and 32.
+		Value   0   1   2   3   4   5   6   7   8   9  10 ...
+		CLZ    32  31  30  30  29  29  29  29  28  28  28 ...
+
+		We have excluded Size==0 before, so this is safe. */
+		order = __builtin_clz(
+				get_nr_of_pages_for_buffer(NULL, size));
+
+		/* there is a size overflow in get_nr_of_pages_for_buffer when
+		 * the size is too large */
+		if (unlikely(order > 31))
+			return INVALID_ORDER;
+		order = 31 - order;
+
+		/* above algorithm rounds down: clz(5)=2 instead of 3 */
+		/* quick correction to fix it: */
+		if (((1<<order)*PAGE_SIZE) < size)
+			order++;
+	}
+	return order;
+}
+
+/* magic linux macro */
+#if !defined(list_for_each_entry)
+/* stop compiler */
+#error "missing macro: list_for_each_entry()"
+/* define a dummy */
+#define list_for_each_entry(a, b, c)    if (0)
+#endif
+
+/*----------------------------------------------------------------------------*/
+/* return the page frame number of an address */
+static inline unsigned int addr_to_pfn(
+	void *addr
+)
+{
+	/* there is no real API for this */
+	return ((unsigned int)(addr)) >> PAGE_SHIFT;
+}
+
+
+/*----------------------------------------------------------------------------*/
+/* return the address of a page frame number */
+static inline void *pfn_to_addr(
+	unsigned int pfn
+)
+{
+	/* there is no real API for this */
+	return (void *)(pfn << PAGE_SHIFT);
+}
+
+#endif /* _MC_DRV_MODULE_LINUX_API_H_ */
+/** @} */
diff --git a/drivers/gud/mobicore_driver/platforms/msm8960_surf_std/platform.h b/drivers/gud/mobicore_driver/platforms/msm8960_surf_std/platform.h
new file mode 100644
index 0000000..7034cb0
--- /dev/null
+++ b/drivers/gud/mobicore_driver/platforms/msm8960_surf_std/platform.h
@@ -0,0 +1,50 @@
+/**
+ * Header file of MobiCore Driver Kernel Module Platform
+ * specific structures
+ *
+ * @addtogroup MobiCore_Driver_Kernel_Module
+ * @{
+ * Internal structures of the McDrvModule
+ * @file
+ *
+ * Header file the MobiCore Driver Kernel Module,
+ * its internal structures and defines.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _MC_DRV_PLATFORM_H_
+#define _MC_DRV_PLATFORM_H_
+
+/** MobiCore Interrupt for Qualcomm */
+#define MC_INTR_SSIQ						218
+
+/** Use SMC for fastcalls */
+#define MC_SMC_FASTCALL
+
+
+/*--------------- Implementation -------------- */
+#include <mach/scm.h>
+/* from following file */
+#define SCM_SVC_MOBICORE		250
+#define SCM_CMD_MOBICORE		1
+
+extern int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len,
+			void *resp_buf, size_t resp_len);
+
+static inline int smc_fastcall(void *fc_generic, size_t size)
+{
+	return scm_call(SCM_SVC_MOBICORE, SCM_CMD_MOBICORE,
+			   fc_generic, size,
+			   fc_generic, size);
+}
+
+/** Enable mobicore mem traces */
+#define MC_MEM_TRACES
+
+#endif /* _MC_DRV_PLATFORM_H_ */
+/** @} */
diff --git a/drivers/gud/mobicore_driver/public/mc_drv_module_api.h b/drivers/gud/mobicore_driver/public/mc_drv_module_api.h
new file mode 100644
index 0000000..59366f3
--- /dev/null
+++ b/drivers/gud/mobicore_driver/public/mc_drv_module_api.h
@@ -0,0 +1,311 @@
+/** @addtogroup MCD_MCDIMPL_KMOD_API Mobicore Driver Module API
+ * @ingroup  MCD_MCDIMPL_KMOD
+ * @{
+ * Interface to Mobicore Driver Kernel Module.
+ * @file
+ *
+ * <h2>Introduction</h2>
+ * The MobiCore Driver Kernel Module is a Linux device driver, which represents
+ * the command proxy on the lowest layer to the secure world (Swd). Additional
+ * services like memory allocation via mmap and generation of a L2 tables for
+ * given virtual memory are also supported. IRQ functionallity receives
+ * information from the SWd in the non secure world (NWd).
+ * As customary the driver is handled as linux device driver with "open",
+ * "close" and "ioctl" commands. Access to the driver is possible after the
+ * device "/dev/mobicore" has been opened.
+ * The MobiCore Driver Kernel Module must be installed via
+ * "insmod mcDrvModule.ko".
+ *
+ *
+ * <h2>Version history</h2>
+ * <table class="customtab">
+ * <tr><td width="100px"><b>Date</b></td><td width="80px"><b>Version</b></td>
+ * <td><b>Changes</b></td></tr>
+ * <tr><td>2010-05-25</td><td>0.1</td><td>Initial Release</td></tr>
+ * </table>
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2010-2012 -->
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *	notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *	notice, this list of conditions and the following disclaimer in the
+ *	documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *	products derived from this software without specific prior
+ *	written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _MC_DRV_MODULEAPI_H_
+#define _MC_DRV_MODULEAPI_H_
+
+#include "version.h"
+
+#define MC_DRV_MOD_DEVNODE		   "mobicore"
+#define MC_DRV_MOD_DEVNODE_FULLPATH  "/dev/" MC_DRV_MOD_DEVNODE
+
+/**
+ * Data exchange structure of the MC_DRV_MODULE_INIT ioctl command.
+ * INIT request data to SWD
+ */
+union mc_ioctl_init_params {
+	struct {
+		/** base address of mci buffer 4KB align */
+		uint32_t  base;
+		/** notification buffer start/length [16:16] [start, length] */
+		uint32_t  nq_offset;
+		/** length of notification queue */
+		uint32_t  nq_length;
+		/** mcp buffer start/length [16:16] [start, length] */
+		uint32_t  mcp_offset;
+		/** length of mcp buffer */
+		uint32_t  mcp_length;
+	} in;
+	struct {
+		/* nothing */
+	} out;
+};
+
+
+/**
+ * Data exchange structure of the MC_DRV_MODULE_INFO ioctl command.
+ * INFO request data to the SWD
+ */
+union mc_ioctl_info_params {
+	struct {
+		uint32_t  ext_info_id; /**< extended info ID */
+	} in;
+	struct {
+		uint32_t  state; /**< state */
+		uint32_t  ext_info; /**< extended info */
+	} out;
+};
+
+/**
+ * Mmap allocates and maps contiguous memory into a process.
+ * We use the third parameter, void *offset, to distinguish between some cases
+ * offset = MC_DRV_KMOD_MMAP_WSM	usual operation, pages are registered in
+					device structure and freed later.
+ * offset = MC_DRV_KMOD_MMAP_MCI	get Instance of MCI, allocates or mmaps
+					the MCI to daemon
+ * offset = MC_DRV_KMOD_MMAP_PERSISTENTWSM	special operation, without
+						registration of pages
+ *
+ * In mmap(), the offset specifies which of several device I/O pages is
+ *  requested. Linux only transfers the page number, i.e. the upper 20 bits to
+ *  kernel module. Therefore we define our special offsets as multiples of page
+ *  size.
+ */
+enum mc_mmap_memtype {
+	MC_DRV_KMOD_MMAP_WSM		= 0,
+	MC_DRV_KMOD_MMAP_MCI		= 4096,
+	MC_DRV_KMOD_MMAP_PERSISTENTWSM	= 8192
+};
+
+struct mc_mmap_resp {
+	uint32_t  handle; /**< WSN handle */
+	uint32_t  phys_addr; /**< physical address of WSM (or NULL) */
+	bool	  is_reused; /**< if WSM memory was reused, or new allocated */
+};
+
+/**
+ * Data exchange structure of the MC_DRV_KMOD_IOCTL_FREE ioctl command.
+ */
+union mc_ioctl_free_params {
+	struct {
+		uint32_t  handle; /**< driver handle */
+		uint32_t  pid; /**< process id */
+	} in;
+	struct {
+		/* nothing */
+	} out;
+};
+
+
+/**
+ * Data exchange structure of the MC_DRV_KMOD_IOCTL_APP_REGISTER_WSM_L2 command.
+ *
+ * Allocates a physical L2 table and maps the buffer into this page.
+ * Returns the physical address of the L2 table.
+ * The page alignment will be created and the appropriated pSize and pOffsetL2
+ * will be modified to the used values.
+ */
+union mc_ioctl_app_reg_wsm_l2_params {
+	struct {
+		uint32_t  buffer; /**< base address of the virtual address  */
+		uint32_t  len; /**< size of the virtual address space */
+		uint32_t  pid; /**< process id */
+	} in;
+	struct {
+		uint32_t  handle; /**< driver handle for locked memory */
+		uint32_t  phys_wsm_l2_table; /* physical address of the L2 table */
+	} out;
+};
+
+
+/**
+ * Data exchange structure of the MC_DRV_KMOD_IOCTL_APP_UNREGISTER_WSM_L2
+ * command.
+ */
+struct mc_ioctl_app_unreg_wsm_l2_params {
+	struct {
+		uint32_t  handle; /**< driver handle for locked memory */
+		uint32_t  pid; /**< process id */
+	} in;
+	struct {
+		/* nothing */
+	} out;
+};
+
+
+/**
+ * Data exchange structure of the MC_DRV_KMOD_IOCTL_DAEMON_LOCK_WSM_L2 command.
+ */
+struct mc_ioctl_daemon_lock_wsm_l2_params {
+	struct {
+		uint32_t  handle; /**< driver handle for locked memory */
+	} in;
+	struct {
+		uint32_t phys_wsm_l2_table;
+	} out;
+};
+
+
+/**
+ * Data exchange structure of the MC_DRV_KMOD_IOCTL_DAEMON_UNLOCK_WSM_L2
+ * command.
+ */
+struct mc_ioctl_daemon_unlock_wsm_l2_params {
+	struct {
+		uint32_t  handle; /**< driver handle for locked memory */
+	} in;
+	struct {
+		/* nothing */
+	} out;
+};
+
+/**
+ * Data exchange structure of the MC_DRV_MODULE_FC_EXECUTE ioctl command.
+ */
+union mc_ioctl_fc_execute_params {
+	struct {
+		/**< base address of mobicore binary */
+		uint32_t  phys_start_addr;
+		/**< length of DDR area */
+		uint32_t  length;
+	} in;
+	struct {
+		/* nothing */
+	} out;
+};
+
+/**
+ * Data exchange structure of the MC_DRV_MODULE_GET_VERSION ioctl command.
+ */
+struct mc_ioctl_get_version_params {
+	struct {
+		uint32_t	kernel_module_version;
+	} out;
+};
+
+/* @defgroup Mobicore_Driver_Kernel_Module_Interface IOCTL */
+
+
+
+
+/* TODO: use IOCTL macros like _IOWR. See Documentation/ioctl/ioctl-number.txt,
+	Documentation/ioctl/ioctl-decoding.txt */
+/**
+ * defines for the ioctl mobicore driver module function call from user space.
+ */
+enum mc_kmod_ioctl {
+
+	/*
+	 * get detailed MobiCore Status
+	 */
+	MC_DRV_KMOD_IOCTL_DUMP_STATUS  = 200,
+
+	/*
+	 * initialize MobiCore
+	 */
+	MC_DRV_KMOD_IOCTL_FC_INIT  = 201,
+
+	/*
+	 * get MobiCore status
+	 */
+	MC_DRV_KMOD_IOCTL_FC_INFO  = 202,
+
+	/**
+	 * ioctl parameter to send the YIELD command to the SWD.
+	 * Only possible in Privileged Mode.
+	 * ioctl(fd, MC_DRV_MODULE_YIELD)
+	 */
+	MC_DRV_KMOD_IOCTL_FC_YIELD =  203,
+	/**
+	 * ioctl parameter to send the NSIQ signal to the SWD.
+	 * Only possible in Privileged Mode
+	 * ioctl(fd, MC_DRV_MODULE_NSIQ)
+	 */
+	MC_DRV_KMOD_IOCTL_FC_NSIQ   =  204,
+	/**
+	 * ioctl parameter to tzbsp to start Mobicore binary from DDR.
+	 * Only possible in Privileged Mode
+	 * ioctl(fd, MC_DRV_KMOD_IOCTL_FC_EXECUTE)
+	 */
+	MC_DRV_KMOD_IOCTL_FC_EXECUTE =  205,
+
+	/**
+	 * Free's memory which is formerly allocated by the driver's mmap
+	 * command. The parameter must be this mmaped address.
+	 * The internal instance data regarding to this address are deleted as
+	 * well as each according memory page and its appropriated reserved bit
+	 * is cleared (ClearPageReserved).
+	 * Usage: ioctl(fd, MC_DRV_MODULE_FREE, &address) with address beeing of
+	 * type long address
+	 */
+	MC_DRV_KMOD_IOCTL_FREE = 218,
+
+	/**
+	 * Creates a L2 Table of the given base address and the size of the
+	 * data.
+	 * Parameter: mc_ioctl_app_reg_wsm_l2_params
+	 */
+	MC_DRV_KMOD_IOCTL_APP_REGISTER_WSM_L2 = 220,
+
+	/**
+	 * Frees the L2 table created by a MC_DRV_KMOD_IOCTL_APP_REGISTER_WSM_L2
+	 * ioctl.
+	 * Parameter: mc_ioctl_app_unreg_wsm_l2_params
+	 */
+	MC_DRV_KMOD_IOCTL_APP_UNREGISTER_WSM_L2 = 221,
+
+
+	/* TODO: comment this. */
+	MC_DRV_KMOD_IOCTL_DAEMON_LOCK_WSM_L2 = 222,
+	MC_DRV_KMOD_IOCTL_DAEMON_UNLOCK_WSM_L2 = 223,
+
+	/**
+	 * Return kernel driver version.
+	 * Parameter: mc_ioctl_get_version_params
+	 */
+	MC_DRV_KMOD_IOCTL_GET_VERSION = 224,
+};
+
+
+#endif /* _MC_DRV_MODULEAPI_H_ */
+/** @} */
diff --git a/drivers/gud/mobicore_driver/public/mc_kernel_api.h b/drivers/gud/mobicore_driver/public/mc_kernel_api.h
new file mode 100644
index 0000000..fdfc618
--- /dev/null
+++ b/drivers/gud/mobicore_driver/public/mc_kernel_api.h
@@ -0,0 +1,100 @@
+/** @addtogroup MCD_MCDIMPL_KMOD_KAPI Mobicore Driver Module API inside Kernel.
+ * @ingroup  MCD_MCDIMPL_KMOD
+ * @{
+ * Interface to Mobicore Driver Kernel Module inside Kernel.
+ * @file
+ *
+ * Interface to be used by module MobiCoreKernelAPI.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2010-2012 -->
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _MOBICORE_KERNELMODULE_API_H_
+#define _MOBICORE_KERNELMODULE_API_H_
+
+struct mc_instance;
+
+/**
+ * Initialize a new mobicore API instance object
+ *
+ * @return Instance or NULL if no allocation was possible.
+ */
+struct mc_instance *mobicore_open(
+	void
+);
+
+/**
+ * Release a mobicore instance object and all objects related to it
+ * @param instance instance
+ * @return 0 if Ok or -E ERROR
+ */
+int mobicore_release(
+	struct mc_instance	*instance
+);
+
+/**
+ * Free a WSM buffer allocated with mobicore_allocate_wsm
+ * @param instance
+ * @param handle		handle of the buffer
+ *
+ * @return 0 if no error
+ *
+ */
+int mobicore_allocate_wsm(
+	struct mc_instance	*instance,
+	unsigned long		requested_size,
+	uint32_t		*handle,
+	void			**kernel_virt_addr,
+	void			**phys_addr
+);
+
+/**
+ * Free a WSM buffer allocated with mobicore_allocate_wsm
+ * @param instance
+ * @param handle		handle of the buffer
+ *
+ * @return 0 if no error
+ *
+ */
+int mobicore_free(
+	struct mc_instance	*instance,
+	uint32_t		handle
+);
+
+/**
+ * Map a virtual memory buffer structure to Mobicore
+ * @param instance
+ * @param addr		address of the buffer(NB it must be kernel virtual!)
+ * @param len		buffer length
+ * @param handle	pointer to handle
+ * @param phys_wsm_l2_table	pointer to physical L2 table(?)
+ *
+ * @return 0 if no error
+ *
+ */
+int mobicore_map_vmem(
+	struct mc_instance	*instance,
+	void			*addr,
+	uint32_t		len,
+	uint32_t		*handle,
+	void			**phys_wsm_l2_table
+);
+
+/**
+ * Unmap a virtual memory buffer from mobicore
+ * @param instance
+ * @param handle
+ *
+ * @return 0 if no error
+ *
+ */
+int mobicore_unmap_vmem(
+	struct mc_instance	*instance,
+	uint32_t		handle
+);
+#endif /* _MOBICORE_KERNELMODULE_API_H_ */
+/** @} */
diff --git a/drivers/gud/mobicore_driver/public/version.h b/drivers/gud/mobicore_driver/public/version.h
new file mode 100644
index 0000000..9b2dbca
--- /dev/null
+++ b/drivers/gud/mobicore_driver/public/version.h
@@ -0,0 +1,36 @@
+/** @addtogroup MCD_MCDIMPL_KMOD
+ * @{
+ * <!-- Copyright Giesecke & Devrient GmbH 2010-2012 -->
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _MC_DRV_VERSION_H_
+#define _MC_DRV_VERSION_H_
+
+#define MCDRVMODULEAPI_VERSION_MAJOR 0
+#define MCDRVMODULEAPI_VERSION_MINOR 1
+
+#endif /* _MC_DRV_VERSION_H_ */
diff --git a/drivers/gud/mobicore_kernelapi/clientlib.c b/drivers/gud/mobicore_kernelapi/clientlib.c
new file mode 100644
index 0000000..13826f2
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/clientlib.c
@@ -0,0 +1,1093 @@
+/**
+ * MobiCore KernelApi module
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/netlink.h>
+#include <net/sock.h>
+#include <net/net_namespace.h>
+#include <linux/list.h>
+
+#include "public/mobicore_driver_api.h"
+#include "public/mobicore_driver_cmd.h"
+#include "device.h"
+#include "session.h"
+
+/* device list */
+LIST_HEAD(devices);
+
+/*----------------------------------------------------------------------------*/
+static struct mcore_device_t *resolve_device_id(
+	uint32_t device_id
+) {
+	struct mcore_device_t *tmp;
+	struct list_head *pos;
+
+	/* Get mcore_device_t for device_id */
+	list_for_each(pos, &devices) {
+		tmp = list_entry(pos, struct mcore_device_t, list);
+		if (tmp->device_id == device_id)
+			return tmp;
+	}
+	return NULL;
+}
+
+
+/*----------------------------------------------------------------------------*/
+static void add_device(
+	struct mcore_device_t *device
+) {
+	list_add_tail(&(device->list), &devices);
+}
+
+
+/*----------------------------------------------------------------------------*/
+static bool remove_device(
+	uint32_t device_id
+) {
+	struct mcore_device_t *tmp;
+	struct list_head *pos, *q;
+
+	list_for_each_safe(pos, q, &devices) {
+		tmp = list_entry(pos, struct mcore_device_t, list);
+		if (tmp->device_id == device_id) {
+			list_del(pos);
+			mcore_device_cleanup(tmp);
+			return true;
+		}
+	}
+	return false;
+}
+
+
+/*----------------------------------------------------------------------------*/
+enum mc_result mc_open_device(
+	uint32_t device_id
+) {
+	enum mc_result mc_result = MC_DRV_OK;
+	struct connection *dev_con = NULL;
+
+	MCDRV_DBG_VERBOSE("===%s()===", __func__);
+
+	/* Enter critical section */
+
+	do {
+		struct mcore_device_t *device = resolve_device_id(device_id);
+		if (device != NULL) {
+			MCDRV_DBG_ERROR("Device %d already opened", device_id);
+			mc_result = MC_DRV_ERR_INVALID_OPERATION;
+			break;
+		}
+
+		/* Open new connection to device */
+		dev_con = connection_new();
+		if (!connection_connect(dev_con, MC_DAEMON_PID)) {
+			MCDRV_DBG_ERROR(
+				"Could not setup netlink connection to PID %u",
+				MC_DAEMON_PID);
+			mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+			break;
+		}
+
+		/* Forward device open to the daemon and read result */
+		struct mc_drv_cmd_open_device_t mc_drv_cmd_open_device = {
+			/* C++ does not support C99 designated initializers */
+			/* .header = */ {
+			/* .command_id = */ MC_DRV_CMD_OPEN_DEVICE
+			},
+		/* .payload = */ {
+		/* .device_id = */ device_id
+			}
+		};
+
+		int len = connection_write_data(
+				dev_con,
+				&mc_drv_cmd_open_device,
+				sizeof(struct mc_drv_cmd_open_device_t));
+		if (len < 0) {
+			MCDRV_DBG_ERROR("CMD_OPEN_DEVICE writeCmd failed "
+				"ret=%d", len);
+			mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+			break;
+		}
+
+		struct mc_drv_response_header_t  rsp_header;
+		len = connection_read_datablock(
+					dev_con,
+					&rsp_header,
+					sizeof(rsp_header));
+		if (len != sizeof(rsp_header)) {
+			MCDRV_DBG_ERROR("CMD_OPEN_DEVICE readRsp failed "
+				"ret=%d", len);
+			mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+			break;
+		}
+		if (rsp_header.response_id != MC_DRV_RSP_OK) {
+			MCDRV_DBG_ERROR("CMD_OPEN_DEVICE failed, respId=%d",
+							rsp_header.response_id);
+			switch (rsp_header.response_id) {
+			case MC_DRV_RSP_PAYLOAD_LENGTH_ERROR:
+				mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+				break;
+			case MC_DRV_INVALID_DEVICE_NAME:
+				mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+				break;
+			case MC_DRV_RSP_DEVICE_ALREADY_OPENED:
+			default:
+				mc_result = MC_DRV_ERR_INVALID_OPERATION;
+				break;
+			}
+			break;
+		}
+
+		/* there is no payload to read */
+
+		device = mcore_device_create(device_id, dev_con);
+		if (!mcore_device_open(device, MC_DRV_MOD_DEVNODE_FULLPATH)) {
+			mcore_device_cleanup(device);
+			MCDRV_DBG_ERROR("could not open device file: %s",
+				MC_DRV_MOD_DEVNODE_FULLPATH);
+			mc_result = MC_DRV_ERR_INVALID_DEVICE_FILE;
+			break;
+		}
+
+		add_device(device);
+
+	} while (false);
+
+	if (mc_result != MC_DRV_OK)
+		connection_cleanup(dev_con);
+
+	/* Exit critical section */
+
+	return mc_result;
+}
+EXPORT_SYMBOL(mc_open_device);
+
+/*----------------------------------------------------------------------------*/
+enum mc_result mc_close_device(
+	uint32_t device_id
+) {
+	enum mc_result mc_result = MC_DRV_OK;
+
+	MCDRV_DBG_VERBOSE("===%s()===", __func__);
+
+	/* Enter critical section */
+	do {
+		struct mcore_device_t *device = resolve_device_id(device_id);
+		if (device == NULL) {
+			MCDRV_DBG_ERROR("Device not found");
+			mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+			break;
+		}
+		struct connection *dev_con = device->connection;
+
+		/* Return if not all sessions have been closed */
+		if (mcore_device_has_sessions(device)) {
+			MCDRV_DBG_ERROR("cannot close with sessions pending");
+			mc_result = MC_DRV_ERR_SESSION_PENDING;
+			break;
+		}
+
+		struct mc_drv_cmd_close_device_t mc_drv_cmd_close_device = {
+			/* C++ does not support C99 designated initializers */
+			/* .header = */ {
+				/* .command_id = */ MC_DRV_CMD_CLOSE_DEVICE
+			}
+		};
+		int len = connection_write_data(
+				dev_con,
+				&mc_drv_cmd_close_device,
+				sizeof(struct mc_drv_cmd_close_device_t));
+		/* ignore error, but log details */
+		if (len < 0) {
+			MCDRV_DBG_ERROR("CMD_CLOSE_DEVICE writeCmd failed "
+				"ret=%d", len);
+			mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+		}
+
+		struct mc_drv_response_header_t  rsp_header;
+		len = connection_read_datablock(
+					dev_con,
+					&rsp_header,
+					sizeof(rsp_header));
+		if (len != sizeof(rsp_header)) {
+			MCDRV_DBG_ERROR("CMD_CLOSE_DEVICE readResp failed "
+				" ret=%d", len);
+			mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+			break;
+		}
+
+		if (rsp_header.response_id != MC_DRV_RSP_OK) {
+			MCDRV_DBG_ERROR("CMD_CLOSE_DEVICE failed, respId=%d",
+							rsp_header.response_id);
+			mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+			break;
+		}
+
+		remove_device(device_id);
+
+	} while (false);
+
+	/* Exit critical section */
+
+	return mc_result;
+}
+EXPORT_SYMBOL(mc_close_device);
+
+/*----------------------------------------------------------------------------*/
+enum mc_result mc_open_session(
+	struct mc_session_handle *session,
+	const struct mc_uuid_t	*uuid,
+	uint8_t			*tci,
+	uint32_t		len
+) {
+	enum mc_result mc_result = MC_DRV_OK;
+
+	MCDRV_DBG_VERBOSE("===%s()===", __func__);
+
+	/* Enter critical section */
+
+	do {
+		if (session == NULL) {
+			MCDRV_DBG_ERROR("Session is null");
+			mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+			break;
+		}
+		if (uuid == NULL) {
+			MCDRV_DBG_ERROR("UUID is null");
+			mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+			break;
+		}
+		if (tci == NULL) {
+			MCDRV_DBG_ERROR("TCI is null");
+			mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+			break;
+		}
+		if (len > MC_MAX_TCI_LEN) {
+			MCDRV_DBG_ERROR("TCI length is longer than %d",
+				MC_MAX_TCI_LEN);
+			mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+			break;
+		}
+
+		/* Get the device associated with the given session */
+		struct mcore_device_t *device =
+				resolve_device_id(session->device_id);
+		if (device == NULL) {
+			MCDRV_DBG_ERROR("Device not found");
+			mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+			break;
+		}
+		struct connection *dev_con = device->connection;
+
+		/* Get the physical address of the given TCI */
+		struct wsm *wsm =
+			mcore_device_find_contiguous_wsm(device, tci);
+		if (wsm == NULL) {
+			MCDRV_DBG_ERROR("Could not resolve TCI phy address ");
+			mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+			break;
+		}
+
+		if (wsm->len < len) {
+			MCDRV_DBG_ERROR("length is more than allocated TCI");
+			mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+			break;
+		}
+
+		/* Prepare open session command */
+		struct mc_drv_cmd_open_session_t cmdOpenSession = {
+			/* C++ does not support C99 designated initializers */
+			/* .header = */ {
+				/* .command_id = */ MC_DRV_CMD_OPEN_SESSION
+			},
+			/* .payload = */ {
+				/* .device_id = */ session->device_id,
+				/* .uuid = */ *uuid,
+				/* .tci = */ (uint32_t)wsm->phys_addr,
+				/* .len = */ len
+			}
+		};
+
+		/* Transmit command data */
+
+		int len = connection_write_data(
+						dev_con,
+						&cmdOpenSession,
+						sizeof(cmdOpenSession));
+		if (len != sizeof(cmdOpenSession)) {
+			MCDRV_DBG_ERROR("CMD_OPEN_SESSION writeData failed "
+				"ret=%d", len);
+			mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+			break;
+		}
+
+		/* Read command response */
+
+		/* read header first */
+		struct mc_drv_response_header_t rsp_header;
+		len = connection_read_datablock(
+					dev_con,
+					&rsp_header,
+					sizeof(rsp_header));
+		if (len != sizeof(rsp_header)) {
+			MCDRV_DBG_ERROR("CMD_OPEN_SESSION readResp failed "
+				" ret=%d", len);
+			mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+			break;
+		}
+
+		if (rsp_header.response_id != MC_DRV_RSP_OK) {
+			MCDRV_DBG_ERROR("CMD_OPEN_SESSION failed, respId=%d",
+							rsp_header.response_id);
+			switch (rsp_header.response_id) {
+			case MC_DRV_RSP_TRUSTLET_NOT_FOUND:
+				mc_result = MC_DRV_ERR_INVALID_DEVICE_FILE;
+				break;
+			case MC_DRV_RSP_PAYLOAD_LENGTH_ERROR:
+			case MC_DRV_RSP_DEVICE_NOT_OPENED:
+			case MC_DRV_RSP_FAILED:
+			default:
+				mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+				break;
+			}
+			break;
+		}
+
+		/* read payload */
+		struct mc_drv_rsp_open_session_payload_t
+						rsp_open_session_payload;
+		len = connection_read_datablock(
+					dev_con,
+					&rsp_open_session_payload,
+					sizeof(rsp_open_session_payload));
+		if (len != sizeof(rsp_open_session_payload)) {
+			MCDRV_DBG_ERROR("CMD_OPEN_SESSION readPayload failed "
+				"ret=%d", len);
+			mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+			break;
+		}
+
+		/* Register session with handle */
+		session->session_id = rsp_open_session_payload.session_id;
+
+		/* Set up second channel for notifications */
+		struct connection *session_connection = connection_new();
+		/*TODO: no real need to connect here? */
+		if (!connection_connect(session_connection, MC_DAEMON_PID)) {
+			MCDRV_DBG_ERROR(
+				"Could not setup netlink connection to PID %u",
+				MC_DAEMON_PID);
+			connection_cleanup(session_connection);
+			mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+			break;
+		}
+
+		/*TODO CONTINOUE HERE !!!! FIX RW RETURN HANDLING!!!! */
+
+		/* Write command to use channel for notifications */
+		struct mc_drv_cmd_nqconnect_t cmd_nqconnect = {
+			/* C++ does not support C99 designated initializers */
+			/* .header = */ {
+				/* .command_id = */ MC_DRV_CMD_NQ_CONNECT
+			},
+			/* .payload = */ {
+				/* .device_id =  */ session->device_id,
+				/* .session_id = */ session->session_id,
+				/* .device_session_id = */
+				rsp_open_session_payload.device_session_id,
+				/* .session_magic = */
+					rsp_open_session_payload.session_magic
+			}
+		};
+		connection_write_data(session_connection,
+			&cmd_nqconnect,
+			sizeof(cmd_nqconnect));
+
+		/* Read command response, header first */
+		len = connection_read_datablock(
+					session_connection,
+					&rsp_header,
+					sizeof(rsp_header));
+		if (len != sizeof(rsp_header)) {
+			MCDRV_DBG_ERROR("CMD_NQ_CONNECT readRsp failed "
+				"ret=%d", len);
+			connection_cleanup(session_connection);
+			mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+			break;
+		}
+
+		if (rsp_header.response_id != MC_DRV_RSP_OK) {
+			MCDRV_DBG_ERROR("CMD_NQ_CONNECT failed, respId=%d",
+					rsp_header.response_id);
+			connection_cleanup(session_connection);
+			mc_result = MC_DRV_ERR_NQ_FAILED;
+			break;
+		}
+
+		/* there is no payload. */
+
+		/* Session established, new session object must be created */
+		mcore_device_create_new_session(
+			device,
+			session->session_id,
+			session_connection);
+
+	} while (false);
+
+	/* Exit critical section */
+
+	return mc_result;
+}
+EXPORT_SYMBOL(mc_open_session);
+
+/*----------------------------------------------------------------------------*/
+enum mc_result mc_close_session(
+	struct mc_session_handle *session
+) {
+	enum mc_result mc_result = MC_DRV_OK;
+
+	MCDRV_DBG_VERBOSE("===%s()===", __func__);
+
+	/* Enter critical section */
+
+	do {
+		if (session == NULL) {
+			MCDRV_DBG_ERROR("Session is null");
+			mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+			break;
+		}
+
+		struct mcore_device_t  *device =
+					resolve_device_id(session->device_id);
+		if (device == NULL) {
+			MCDRV_DBG_ERROR("Device not found");
+			mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+			break;
+		}
+		struct connection  *dev_con = device->connection;
+
+		struct session  *nq_session =
+		 mcore_device_resolve_session_id(device, session->session_id);
+		if (nq_session == NULL) {
+			MCDRV_DBG_ERROR("Session not found");
+			mc_result = MC_DRV_ERR_UNKNOWN_SESSION;
+			break;
+		}
+
+		/* Write close session command */
+		struct mc_drv_cmd_close_session_t cmd_close_session = {
+			/* C++ does not support C99 designated initializers */
+			/* .header = */ {
+				/* .command_id = */ MC_DRV_CMD_CLOSE_SESSION
+			},
+			/* .payload = */ {
+				/* .session_id = */ session->session_id,
+			}
+		};
+		connection_write_data(
+			dev_con,
+			&cmd_close_session,
+			sizeof(cmd_close_session));
+
+		/* Read command response */
+		struct mc_drv_response_header_t rsp_header;
+		int len = connection_read_datablock(
+						dev_con,
+						&rsp_header,
+						sizeof(rsp_header));
+		if (len != sizeof(rsp_header)) {
+			MCDRV_DBG_ERROR("CMD_CLOSE_SESSION readRsp failed "
+				"ret=%d", len);
+			mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+			break;
+		}
+
+		if (rsp_header.response_id != MC_DRV_RSP_OK) {
+			MCDRV_DBG_ERROR("CMD_CLOSE_SESSION failed, respId=%d",
+							rsp_header.response_id);
+			mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+			break;
+		}
+
+		mcore_device_remove_session(device, session->session_id);
+		mc_result = MC_DRV_OK;
+
+	} while (false);
+
+	/* Exit critical section */
+
+	return mc_result;
+}
+EXPORT_SYMBOL(mc_close_session);
+
+/*----------------------------------------------------------------------------*/
+enum mc_result mc_notify(
+	struct mc_session_handle   *session
+) {
+	enum mc_result mc_result = MC_DRV_OK;
+
+	MCDRV_DBG_VERBOSE("===%s()===", __func__);
+
+	do {
+		if (session == NULL) {
+			MCDRV_DBG_ERROR("Session is null");
+			mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+			break;
+		}
+
+		struct mcore_device_t *device =
+					resolve_device_id(session->device_id);
+		if (device == NULL) {
+			MCDRV_DBG_ERROR("Device not found");
+			mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+			break;
+		}
+		struct connection *dev_con = device->connection;
+
+		struct session  *nqsession =
+		 mcore_device_resolve_session_id(device, session->session_id);
+		if (nqsession == NULL) {
+			MCDRV_DBG_ERROR("Session not found");
+			mc_result = MC_DRV_ERR_UNKNOWN_SESSION;
+			break;
+		}
+
+		struct mc_drv_cmd_notify_t cmd_notify = {
+			/* C++ does not support C99 designated initializers */
+			/* .header = */ {
+				/* .command_id = */ MC_DRV_CMD_NOTIFY
+			},
+			/* .payload = */ {
+				/* .session_id = */ session->session_id,
+			}
+		};
+
+		connection_write_data(
+			dev_con,
+			&cmd_notify,
+			sizeof(cmd_notify));
+
+		/* Daemon will not return a response */
+
+	} while (false);
+
+	return mc_result;
+}
+EXPORT_SYMBOL(mc_notify);
+
+/*----------------------------------------------------------------------------*/
+enum mc_result mc_wait_notification(
+	struct mc_session_handle  *session,
+	int32_t			timeout
+) {
+	enum mc_result mc_result = MC_DRV_OK;
+
+	MCDRV_DBG_VERBOSE("===%s()===", __func__);
+
+	do {
+		if (session == NULL) {
+			mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+			break;
+		}
+
+		struct mcore_device_t  *device =
+					resolve_device_id(session->device_id);
+		if (device == NULL) {
+			MCDRV_DBG_ERROR("Device not found");
+			mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+			break;
+		}
+
+		struct session  *nq_session =
+		 mcore_device_resolve_session_id(device, session->session_id);
+		if (nq_session == NULL) {
+			MCDRV_DBG_ERROR("Session not found");
+			mc_result = MC_DRV_ERR_UNKNOWN_SESSION;
+			break;
+		}
+
+		struct connection *nqconnection =
+					nq_session->notification_connection;
+		uint32_t count = 0;
+
+		/* Read notification queue till it's empty */
+		for (;;) {
+			struct notification notification;
+			ssize_t num_read = connection_read_data(
+				nqconnection,
+				&notification,
+				sizeof(notification),
+				timeout);
+			/* Exit on timeout in first run. Later runs have
+			 * timeout set to 0.
+			 * -2 means, there is no more data. */
+			if (count == 0 && num_read == -2) {
+				MCDRV_DBG_ERROR("read timeout");
+				mc_result = MC_DRV_ERR_TIMEOUT;
+				break;
+			}
+			/* After first notification the queue will be
+			 * drained, Thus we set no timeout for the
+			 * following reads */
+			timeout = 0;
+
+			if (num_read != sizeof(struct notification)) {
+				if (count == 0) {
+					/* failure in first read, notify it */
+					mc_result = MC_DRV_ERR_NOTIFICATION;
+					MCDRV_DBG_ERROR(
+					"read notification failed, "
+					"%i bytes received", (int)num_read);
+					break;
+				} else {
+					/* Read of the n-th notification
+					   failed/timeout. We don't tell the
+					   caller, as we got valid notifications
+					   before. */
+					mc_result = MC_DRV_OK;
+					break;
+				}
+			}
+
+			count++;
+			MCDRV_DBG_VERBOSE("readNq count=%d, SessionID=%d, "
+				"Payload=%d", count,
+				notification.session_id, notification.payload);
+
+			if (notification.payload != 0) {
+				/* Session end point died -> store exit code */
+				session_set_error_info(nq_session,
+					notification.payload);
+
+				mc_result = MC_DRV_INFO_NOTIFICATION;
+				break;
+			}
+		} /* for(;;) */
+
+	} while (false);
+
+	return mc_result;
+}
+EXPORT_SYMBOL(mc_wait_notification);
+
+/*----------------------------------------------------------------------------*/
+enum mc_result mc_malloc_wsm(
+	uint32_t	device_id,
+	uint32_t	align,
+	uint32_t	len,
+	uint8_t		**wsm,
+	uint32_t	wsm_flags
+) {
+	enum mc_result mc_result = MC_DRV_ERR_UNKNOWN;
+
+	MCDRV_DBG_VERBOSE("===%s()===", __func__);
+
+	/* Enter critical section */
+
+	do {
+		struct mcore_device_t *device = resolve_device_id(device_id);
+		if (device == NULL) {
+			MCDRV_DBG_ERROR("Device not found");
+			mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+			break;
+		}
+		if (wsm == NULL) {
+			mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+			break;
+		}
+
+		struct wsm *wsm_stack =
+			mcore_device_allocate_contiguous_wsm(device, len);
+		if (wsm_stack == NULL) {
+			MCDRV_DBG_ERROR("Allocation of WSM failed");
+			mc_result = MC_DRV_ERR_NO_FREE_MEMORY;
+			break;
+		}
+
+		*wsm = (uint8_t *)wsm_stack->virt_addr;
+		mc_result = MC_DRV_OK;
+
+	} while (false);
+
+	/* Exit critical section */
+
+	return mc_result;
+}
+EXPORT_SYMBOL(mc_malloc_wsm);
+
+/*----------------------------------------------------------------------------*/
+enum mc_result mc_free_wsm(
+	uint32_t	device_id,
+	uint8_t		*wsm
+) {
+	enum mc_result mc_result = MC_DRV_ERR_UNKNOWN;
+	struct mcore_device_t *device;
+
+
+	MCDRV_DBG_VERBOSE("===%s()===", __func__);
+
+	/* Enter critical section */
+
+	do {
+
+		/* Get the device associated wit the given session */
+		device = resolve_device_id(device_id);
+		if (device == NULL) {
+			MCDRV_DBG_ERROR("Device not found");
+			mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+			break;
+		}
+
+		/* find WSM object */
+		struct wsm *wsm_stack =
+			mcore_device_find_contiguous_wsm(device, wsm);
+		if (wsm_stack == NULL) {
+			MCDRV_DBG_ERROR("unknown address");
+			mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+			break;
+		}
+
+		/* Free the given virtual address */
+		if (!mcore_device_free_contiguous_wsm(device, wsm_stack)) {
+			MCDRV_DBG_ERROR("Free of virtual address failed");
+			mc_result = MC_DRV_ERR_FREE_MEMORY_FAILED;
+			break;
+		}
+		mc_result = MC_DRV_OK;
+
+	} while (false);
+
+	/* Exit critical section */
+
+	return mc_result;
+}
+EXPORT_SYMBOL(mc_free_wsm);
+
+/*----------------------------------------------------------------------------*/
+enum mc_result mc_map(
+	struct mc_session_handle	*session_handle,
+	void				*buf,
+	uint32_t			buf_len,
+	struct mc_bulk_map		*map_info
+) {
+	enum mc_result mc_result = MC_DRV_ERR_UNKNOWN;
+
+	MCDRV_DBG_VERBOSE("===%s()===", __func__);
+
+	/* Enter critical section */
+
+	do {
+		if (session_handle == NULL) {
+			MCDRV_DBG_ERROR("session_handle is null");
+			mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+			break;
+		}
+		if (map_info == NULL) {
+			MCDRV_DBG_ERROR("map_info is null");
+			mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+			break;
+		}
+		if (buf == NULL) {
+			MCDRV_DBG_ERROR("buf is null");
+			mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+			break;
+		}
+
+		/* Determine device the session belongs to */
+		struct mcore_device_t  *device = resolve_device_id(
+						session_handle->device_id);
+		if (device == NULL) {
+			MCDRV_DBG_ERROR("Device not found");
+			mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+			break;
+		}
+		struct connection *dev_con = device->connection;
+
+		/* Get session */
+		struct session  *session =
+		mcore_device_resolve_session_id(device,
+						session_handle->session_id);
+		if (session == NULL) {
+			MCDRV_DBG_ERROR("Session not found");
+			mc_result = MC_DRV_ERR_UNKNOWN_SESSION;
+			break;
+		}
+
+		/* Register mapped bulk buffer to Kernel Module and keep mapped
+		   bulk buffer in mind */
+		struct bulk_buffer_descriptor *bulk_buf = session_add_bulk_buf(
+			session, buf, buf_len);
+		if (bulk_buf == NULL) {
+			MCDRV_DBG_ERROR("Error mapping bulk buffer");
+			mc_result = MC_DRV_ERR_BULK_MAPPING;
+			break;
+		}
+
+		/* Prepare map command */
+		struct mc_drv_cmd_map_bulk_mem_t mc_drv_cmd_map_bulk_mem = {
+			/* C++ does not support C99 designated initializers */
+			/* .header = */ {
+				/* .command_id = */ MC_DRV_CMD_MAP_BULK_BUF
+			},
+			/* .payload = */ {
+				/* .session_id = */ session->session_id,
+				/* .phys_addr_l2; = */
+					(uint32_t)bulk_buf->phys_addr_wsm_l2,
+				/* .offset_payload = */
+					(uint32_t)(bulk_buf->virt_addr) & 0xFFF,
+				/* .len_bulk_mem = */ bulk_buf->len
+			}
+		};
+
+		/* Transmit map command to MobiCore device */
+		connection_write_data(
+			dev_con,
+			&mc_drv_cmd_map_bulk_mem,
+			sizeof(mc_drv_cmd_map_bulk_mem));
+
+		/* Read command response */
+		struct mc_drv_response_header_t rsp_header;
+		int len = connection_read_datablock(
+						dev_con,
+						&rsp_header,
+						sizeof(rsp_header));
+		if (len != sizeof(rsp_header)) {
+			MCDRV_DBG_ERROR("CMD_MAP_BULK_BUF readRsp failed, "
+				"ret=%d", len);
+			mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+			break;
+		}
+
+		if (rsp_header.response_id != MC_DRV_RSP_OK) {
+			MCDRV_DBG_ERROR("CMD_MAP_BULK_BUF failed, respId=%d",
+							rsp_header.response_id);
+			/* REV We ignore Daemon Error code because client cannot
+			   handle it anyhow. */
+			mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+
+			/* Unregister mapped bulk buffer from Kernel Module and
+			   remove mapped bulk buffer from session maintenance */
+			if (!session_remove_bulk_buf(session, buf)) {
+				/* Removing of bulk buffer not possible */
+				MCDRV_DBG_ERROR("Unregistering of bulk memory"
+					"from Kernel Module failed");
+			}
+			break;
+		}
+
+		struct mc_drv_rsp_map_bulk_mem_payload_t
+						rsp_map_bulk_mem_payload;
+		connection_read_datablock(
+			dev_con,
+			&rsp_map_bulk_mem_payload,
+			sizeof(rsp_map_bulk_mem_payload));
+
+		/* Set mapping info for Trustlet */
+		map_info->secure_virt_addr =
+			(void *)(rsp_map_bulk_mem_payload.secure_virtual_adr);
+		map_info->secure_virt_len = buf_len;
+		mc_result = MC_DRV_OK;
+
+	} while (false);
+
+	/* Exit critical section */
+
+	return mc_result;
+}
+EXPORT_SYMBOL(mc_map);
+
+/*----------------------------------------------------------------------------*/
+enum mc_result mc_unmap(
+	struct mc_session_handle	*session_handle,
+	void				*buf,
+	struct mc_bulk_map		*map_info
+) {
+	enum mc_result mc_result = MC_DRV_ERR_UNKNOWN;
+
+	MCDRV_DBG_VERBOSE("===%s()===", __func__);
+
+	/* Enter critical section */
+
+	do {
+		if (session_handle == NULL) {
+			MCDRV_DBG_ERROR("session_handle is null");
+			mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+			break;
+		}
+		if (map_info == NULL) {
+			MCDRV_DBG_ERROR("map_info is null");
+			mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+			break;
+		}
+		if (buf == NULL) {
+			MCDRV_DBG_ERROR("buf is null");
+			mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+			break;
+		}
+
+		/* Determine device the session belongs to */
+		struct mcore_device_t  *device =
+			resolve_device_id(session_handle->device_id);
+		if (device == NULL) {
+			MCDRV_DBG_ERROR("Device not found");
+			mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+			break;
+		}
+		struct connection  *dev_con = device->connection;
+
+		/* Get session */
+		struct session  *session =
+			mcore_device_resolve_session_id(device,
+						session_handle->session_id);
+		if (session == NULL) {
+			MCDRV_DBG_ERROR("Session not found");
+			mc_result = MC_DRV_ERR_UNKNOWN_SESSION;
+			break;
+		}
+
+		/* Prepare unmap command */
+		struct mc_drv_cmd_unmap_bulk_mem_t cmd_unmap_bulk_mem = {
+				/* .header = */ {
+					/* .command_id = */
+						MC_DRV_CMD_UNMAP_BULK_BUF
+				},
+				/* .payload = */ {
+					/* .session_id = */ session->session_id,
+					/* .secure_virtual_adr = */
+					(uint32_t)(map_info->secure_virt_addr),
+					/* .len_bulk_mem =
+						map_info->secure_virt_len*/
+				}
+			};
+
+		connection_write_data(
+			dev_con,
+			&cmd_unmap_bulk_mem,
+			sizeof(cmd_unmap_bulk_mem));
+
+		/* Read command response */
+		struct mc_drv_response_header_t rsp_header;
+		int len = connection_read_datablock(
+						dev_con,
+						&rsp_header,
+						sizeof(rsp_header));
+		if (len != sizeof(rsp_header)) {
+			MCDRV_DBG_ERROR("CMD_UNMAP_BULK_BUF readRsp failed, "
+				"ret=%d", len);
+			mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+			break;
+		}
+
+		if (rsp_header.response_id != MC_DRV_RSP_OK) {
+			MCDRV_DBG_ERROR("CMD_UNMAP_BULK_BUF failed, respId=%d",
+							rsp_header.response_id);
+			/* REV We ignore Daemon Error code because client
+			   cannot handle it anyhow. */
+			mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+			break;
+		}
+
+		struct mc_drv_rsp_unmap_bulk_mem_payload_t
+						rsp_unmap_bulk_mem_payload;
+		connection_read_datablock(
+			dev_con,
+			&rsp_unmap_bulk_mem_payload,
+			sizeof(rsp_unmap_bulk_mem_payload));
+
+		/* REV axh: what about check the payload? */
+
+		/* Unregister mapped bulk buffer from Kernel Module and
+		 * remove mapped bulk buffer from session maintenance */
+		if (!session_remove_bulk_buf(session, buf)) {
+			/* Removing of bulk buffer not possible */
+			MCDRV_DBG_ERROR("Unregistering of bulk memory from "
+							"Kernel Module failed");
+			mc_result = MC_DRV_ERR_BULK_UNMAPPING;
+			break;
+		}
+
+		mc_result = MC_DRV_OK;
+
+	} while (false);
+
+	/* Exit critical section */
+
+	return mc_result;
+}
+EXPORT_SYMBOL(mc_unmap);
+
+/*----------------------------------------------------------------------------*/
+enum mc_result mc_get_session_error_code(
+	struct mc_session_handle   *session,
+	int32_t			 *last_error
+) {
+	enum mc_result mc_result = MC_DRV_OK;
+
+	MCDRV_DBG_VERBOSE("===%s()===", __func__);
+
+	do {
+		if (session == NULL || last_error == NULL) {
+			mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+			break;
+		}
+
+		/* Get device */
+		struct mcore_device_t *device =
+					resolve_device_id(session->device_id);
+		if (device == NULL) {
+			MCDRV_DBG_ERROR("Device not found");
+			mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+			break;
+		}
+
+		/* Get session */
+		struct session *nqsession =
+		 mcore_device_resolve_session_id(device, session->session_id);
+		if (nqsession == NULL) {
+			MCDRV_DBG_ERROR("Session not found");
+			mc_result = MC_DRV_ERR_UNKNOWN_SESSION;
+			break;
+		}
+
+		/* get session error code from session */
+		*last_error = session_get_last_err(nqsession);
+
+	} while (false);
+
+	return mc_result;
+}
+EXPORT_SYMBOL(mc_get_session_error_code);
+
+/*----------------------------------------------------------------------------*/
+enum mc_result mc_driver_ctrl(
+	enum mc_driver_ctrl  param,
+	uint8_t		 *data,
+	uint32_t		len
+) {
+	MCDRV_DBG_WARN("not implemented");
+	return MC_DRV_ERR_NOT_IMPLEMENTED;
+}
+EXPORT_SYMBOL(mc_driver_ctrl);
+
+/*----------------------------------------------------------------------------*/
+enum mc_result mc_manage(
+	uint32_t  device_id,
+	uint8_t   *data,
+	uint32_t  len
+) {
+	MCDRV_DBG_WARN("not implemented");
+	return MC_DRV_ERR_NOT_IMPLEMENTED;
+}
+EXPORT_SYMBOL(mc_manage);
+
diff --git a/drivers/gud/mobicore_kernelapi/common.h b/drivers/gud/mobicore_kernelapi/common.h
new file mode 100644
index 0000000..2a73474
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/common.h
@@ -0,0 +1,97 @@
+/**
+ *
+ * Common data types
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef COMMON_H
+#define COMMON_H
+
+#include "connection.h"
+#include "mcinq.h"
+
+void mcapi_insert_connection(
+	struct connection *connection
+);
+
+void mcapi_remove_connection(
+	uint32_t seq
+);
+
+unsigned int mcapi_unique_id(
+	void
+);
+
+
+#define MC_DAEMON_PID 0xFFFFFFFF
+#define MC_DRV_MOD_DEVNODE_FULLPATH "/dev/mobicore"
+
+/* dummy function helper macro. */
+#define DUMMY_FUNCTION()	do {} while (0)
+
+#define MCDRV_ERROR(txt, ...) \
+	printk(KERN_ERR "mcKernelApi %s() ### ERROR: " txt, \
+		__func__, \
+		##__VA_ARGS__)
+
+#if defined(DEBUG)
+
+/* #define DEBUG_VERBOSE */
+#if defined(DEBUG_VERBOSE)
+#define MCDRV_DBG_VERBOSE		  MCDRV_DBG
+#else
+#define MCDRV_DBG_VERBOSE(...)	 DUMMY_FUNCTION()
+#endif
+
+#define MCDRV_DBG(txt, ...) \
+	printk(KERN_INFO "mcKernelApi %s(): " txt, \
+		__func__, \
+		##__VA_ARGS__)
+
+#define MCDRV_DBG_WARN(txt, ...) \
+	printk(KERN_WARNING "mcKernelApi %s() WARNING: " txt, \
+		__func__, \
+		##__VA_ARGS__)
+
+#define MCDRV_DBG_ERROR(txt, ...) \
+	printk(KERN_ERR "mcKernelApi %s() ### ERROR: " txt, \
+		__func__, \
+		##__VA_ARGS__)
+
+
+#define MCDRV_ASSERT(cond) \
+	do { \
+		if (unlikely(!(cond))) { \
+			panic("mcKernelApi Assertion failed: %s:%d\n", \
+				__FILE__, __LINE__); \
+		} \
+	} while (0)
+
+#elif defined(NDEBUG)
+
+#define MCDRV_DBG_VERBOSE(...)	DUMMY_FUNCTION()
+#define MCDRV_DBG(...)		DUMMY_FUNCTION()
+#define MCDRV_DBG_WARN(...)	DUMMY_FUNCTION()
+#define MCDRV_DBG_ERROR(...)	DUMMY_FUNCTION()
+
+#define MCDRV_ASSERT(...)	DUMMY_FUNCTION()
+
+#else
+#error "Define DEBUG or NDEBUG"
+#endif /* [not] defined(DEBUG_MCMODULE) */
+
+
+#define LOG_I MCDRV_DBG_VERBOSE
+#define LOG_W MCDRV_DBG_WARN
+#define LOG_E MCDRV_DBG_ERROR
+
+
+#define assert(expr) MCDRV_ASSERT(expr)
+
+#endif /* COMMON_H */
+
+/** @} */
diff --git a/drivers/gud/mobicore_kernelapi/connection.c b/drivers/gud/mobicore_kernelapi/connection.c
new file mode 100644
index 0000000..9048ae8
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/connection.c
@@ -0,0 +1,229 @@
+/** @addtogroup MCD_MCDIMPL_DAEMON_SRV
+ * @{
+ * @file
+ *
+ * Connection data.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/netlink.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/semaphore.h>
+#include <linux/time.h>
+#include <net/sock.h>
+#include <net/net_namespace.h>
+
+#include "connection.h"
+#include "common.h"
+
+/* Define the initial state of the Data Available Semaphore */
+#define SEM_NO_DATA_AVAILABLE 0
+
+/*----------------------------------------------------------------------------*/
+struct connection *connection_new(
+	void
+) {
+	struct connection *conn = kzalloc(sizeof(struct connection),
+					GFP_KERNEL);
+	conn->sequence_magic = mcapi_unique_id();
+	mutex_init(&conn->data_lock);
+	/* No data available */
+	sema_init(&conn->data_available_sem, SEM_NO_DATA_AVAILABLE);
+
+	mcapi_insert_connection(conn);
+	return conn;
+}
+
+/*----------------------------------------------------------------------------*/
+struct connection *connection_create(
+	int	 socket_descriptor,
+	pid_t   dest
+) {
+	struct connection *conn = connection_new();
+
+	conn->peer_pid = dest;
+	return conn;
+}
+
+
+/*----------------------------------------------------------------------------*/
+void connection_cleanup(
+	struct connection *conn
+) {
+	if (!conn)
+		return;
+
+	kfree_skb(conn->skb);
+
+	mcapi_remove_connection(conn->sequence_magic);
+	kfree(conn);
+}
+
+
+/*----------------------------------------------------------------------------*/
+bool connection_connect(
+	struct connection *conn,
+	pid_t		dest
+) {
+	/* Nothing to connect */
+	conn->peer_pid = dest;
+	return true;
+}
+
+/*----------------------------------------------------------------------------*/
+size_t connection_readDataMsg(
+	struct connection *conn,
+	void *buffer,
+	uint32_t len
+) {
+	size_t ret = -1;
+	MCDRV_DBG_VERBOSE("reading connection data %u, connection data left %u",
+			len, conn->data_len);
+	/* trying to read more than the left data */
+	if (len > conn->data_len) {
+		ret = conn->data_len;
+		memcpy(buffer, conn->data_start, conn->data_len);
+		conn->data_len = 0;
+	} else {
+		ret = len;
+		memcpy(buffer, conn->data_start, len);
+		conn->data_len -= len;
+		conn->data_start += len;
+	}
+
+	if (conn->data_len == 0)	{
+		conn->data_start = NULL;
+		kfree_skb(conn->skb);
+		conn->skb = NULL;
+	}
+	MCDRV_DBG_VERBOSE("read %u",  ret);
+	return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+size_t connection_read_datablock(
+	struct connection *conn,
+	void		 *buffer,
+	uint32_t	 len
+) {
+	return connection_read_data(conn, buffer, len, -1);
+}
+
+
+/*----------------------------------------------------------------------------*/
+size_t connection_read_data(
+	struct connection *conn,
+	void		*buffer,
+	uint32_t	len,
+	int32_t		timeout
+) {
+	size_t ret = 0;
+
+	MCDRV_ASSERT(buffer != NULL);
+	MCDRV_ASSERT(conn->socket_descriptor != NULL);
+
+	MCDRV_DBG_VERBOSE("read data len = %u for PID = %u",
+						len, conn->sequence_magic);
+	do {
+		/* Wait until data is available or timeout
+		   msecs_to_jiffies(-1) -> wait forever for the sem */
+		if (down_timeout(&(conn->data_available_sem),
+				  msecs_to_jiffies(timeout))) {
+			MCDRV_DBG_VERBOSE("Timeout reading the data sem");
+			ret = -2;
+			break;
+		}
+
+		if (mutex_lock_interruptible(&(conn->data_lock))) {
+			MCDRV_DBG_ERROR("interrupted reading the data sem");
+			ret = -1;
+			break;
+		}
+		/* Have data, use it */
+		if (conn->data_len > 0)
+			ret = connection_readDataMsg(conn, buffer, len);
+
+			mutex_unlock(&(conn->data_lock));
+
+		/* There is still some data left */
+		if (conn->data_len > 0)
+			up(&conn->data_available_sem);
+	} while (0);
+
+	return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+size_t connection_write_data(
+	struct connection *conn,
+	void		 *buffer,
+	uint32_t	 len
+) {
+	struct sk_buff *skb = NULL;
+	struct nlmsghdr *nlh;
+	int ret = 0;
+
+	MCDRV_DBG_VERBOSE("buffer length %u from pid %u\n",
+		  len,  conn->sequence_magic);
+	do {
+		skb = nlmsg_new(NLMSG_SPACE(len), GFP_KERNEL);
+		if (!skb) {
+			ret = -1;
+			break;
+		}
+
+		nlh = nlmsg_put(skb, 0, conn->sequence_magic, 2,
+					  NLMSG_LENGTH(len), NLM_F_REQUEST);
+		if (!nlh) {
+			ret = -1;
+			break;
+		}
+		memcpy(NLMSG_DATA(nlh), buffer, len);
+
+		netlink_unicast(conn->socket_descriptor, skb,
+						conn->peer_pid, MSG_DONTWAIT);
+		ret = len;
+	} while (0);
+
+	if (!ret && skb != NULL)
+		kfree_skb(skb);
+
+	return ret;
+}
+
+int connection_process(
+	struct connection *conn,
+	struct sk_buff *skb
+)
+{
+	int ret = 0;
+	do {
+		if (mutex_lock_interruptible(&(conn->data_lock))) {
+			MCDRV_DBG_ERROR("Interrupted getting data semaphore!");
+			ret = -1;
+			break;
+		}
+
+		kfree_skb(conn->skb);
+
+		/* Get a reference to the incomming skb */
+		conn->skb = skb_get(skb);
+		if (conn->skb) {
+			conn->data_msg = nlmsg_hdr(conn->skb);
+			conn->data_len = NLMSG_PAYLOAD(conn->data_msg, 0);
+			conn->data_start = NLMSG_DATA(conn->data_msg);
+			up(&(conn->data_available_sem));
+		}
+		mutex_unlock(&(conn->data_lock));
+		ret = 0;
+	} while (0);
+	return ret;
+}
+/** @} */
diff --git a/drivers/gud/mobicore_kernelapi/connection.h b/drivers/gud/mobicore_kernelapi/connection.h
new file mode 100644
index 0000000..0b468e6
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/connection.h
@@ -0,0 +1,122 @@
+/** @addtogroup MCD_MCDIMPL_DAEMON_SRV
+ * @{
+ * @file
+ *
+ * Connection data.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef CONNECTION_H_
+#define CONNECTION_H_
+
+#include <linux/semaphore.h>
+
+#include <stddef.h>
+#include <stdbool.h>
+
+#define MAX_PAYLOAD_SIZE 128
+
+struct connection {
+	struct sock *socket_descriptor; /**< Netlink socket */
+	uint32_t sequence_magic; /**< Random? magic to match requests/answers */
+
+	struct nlmsghdr *data_msg;
+	uint32_t data_len; /**< How much connection data is left */
+	void *data_start; /**< Start pointer of remaining data */
+	struct sk_buff *skb;
+
+	struct mutex data_lock; /**< Data protection lock */
+	struct semaphore data_available_sem; /**< Data protection semaphore */
+
+	pid_t self_pid; /**< PID address used for local connection */
+	pid_t peer_pid; /**< Remote PID for connection */
+
+	struct list_head list; /**< The list param for using the kernel lists*/
+};
+
+struct connection *connection_new(
+	void
+);
+
+struct connection *connection_create(
+	int		  socket_descriptor,
+	pid_t		dest
+);
+
+void connection_cleanup(
+	struct connection *conn
+);
+
+/**
+  * Connect to destination.
+  *
+  * @param Destination pointer.
+  * @return true on success.
+  */
+bool connection_connect(
+	struct connection *conn,
+	pid_t		dest
+);
+
+
+/**
+  * Read bytes from the connection.
+  *
+  * @param buffer	Pointer to destination buffer.
+  * @param len	   Number of bytes to read.
+  * @return Number of bytes read.
+  */
+size_t connection_read_datablock(
+	struct connection *conn,
+	void		 *buffer,
+	uint32_t	 len
+);
+/**
+  * Read bytes from the connection.
+  *
+  * @param buffer	Pointer to destination buffer.
+  * @param len	   Number of bytes to read.
+  * @param timeout   Timeout in milliseconds
+  * @return Number of bytes read.
+  * @return -1 if select() failed (returned -1)
+  * @return -2 if no data available, i.e. timeout
+  */
+size_t connection_read_data(
+	struct connection *conn,
+	void		 *buffer,
+	uint32_t	 len,
+	int32_t	  timeout
+);
+
+/**
+  * Write bytes to the connection.
+  *
+  * @param buffer	Pointer to source buffer.
+  * @param len		Number of bytes to read.
+  * @return Number of bytes written.
+  */
+size_t connection_write_data(
+	struct connection *conn,
+	void		 *buffer,
+	uint32_t	  len
+);
+
+/**
+ * Write bytes to the connection.
+ *
+ * @param buffer	Pointer to source buffer.
+ * @param len		Number of bytes to read.
+ * @return Number of bytes written.
+ */
+int connection_process(
+	struct connection *conn,
+	struct sk_buff *skb
+);
+
+#endif /* CONNECTION_H_ */
+
+/** @} */
diff --git a/drivers/gud/mobicore_kernelapi/device.c b/drivers/gud/mobicore_kernelapi/device.c
new file mode 100644
index 0000000..dbeee6a
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/device.c
@@ -0,0 +1,257 @@
+/** @addtogroup MCD_IMPL_LIB
+ * @{
+ * @file
+ *
+ * Client library device management.
+ *
+ * Device and Trustlet Session management Funtions.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/list.h>
+#include <linux/slab.h>
+#include "mc_kernel_api.h"
+#include "public/mobicore_driver_api.h"
+
+#include "device.h"
+#include "common.h"
+
+/*----------------------------------------------------------------------------*/
+struct wsm *wsm_create(
+	void	*virt_addr,
+	uint32_t  len,
+	uint32_t  handle,
+	void	*phys_addr /*= NULL this may be unknown, so is can be omitted.*/
+	)
+{
+	struct wsm *wsm = kzalloc(sizeof(struct wsm), GFP_KERNEL);
+	wsm->virt_addr = virt_addr;
+	wsm->len = len;
+	wsm->handle = handle;
+	wsm->phys_addr = phys_addr;
+	return wsm;
+}
+
+
+/*----------------------------------------------------------------------------*/
+struct mcore_device_t *mcore_device_create(
+	uint32_t  device_id,
+	struct connection  *connection
+) {
+	struct mcore_device_t *dev =
+			kzalloc(sizeof(struct mcore_device_t), GFP_KERNEL);
+	dev->device_id = device_id;
+	dev->connection = connection;
+
+	INIT_LIST_HEAD(&dev->session_vector);
+	INIT_LIST_HEAD(&dev->wsm_l2_vector);
+
+	return dev;
+}
+
+
+/*----------------------------------------------------------------------------*/
+void mcore_device_cleanup(
+	struct mcore_device_t *dev
+) {
+	struct session *tmp;
+	struct wsm *wsm;
+	struct list_head *pos, *q;
+
+	/* Delete all session objects. Usually this should not be needed
+	 * as closeDevice()requires that all sessions have been closed before.*/
+	list_for_each_safe(pos, q, &dev->session_vector) {
+		tmp = list_entry(pos, struct session, list);
+		list_del(pos);
+		session_cleanup(tmp);
+	}
+
+	/* Free all allocated WSM descriptors */
+	list_for_each_safe(pos, q, &dev->wsm_l2_vector) {
+		wsm = list_entry(pos, struct wsm, list);
+		/* mcKMod_free(dev->instance, wsm->handle); */
+		list_del(pos);
+		kfree(wsm);
+	}
+	connection_cleanup(dev->connection);
+
+	mcore_device_close(dev);
+	kfree(dev);
+}
+
+
+/*----------------------------------------------------------------------------*/
+bool mcore_device_open(
+	struct mcore_device_t   *dev,
+	const char *deviceName
+) {
+	dev->instance = mobicore_open();
+	return (dev->instance != NULL);
+}
+
+
+/*----------------------------------------------------------------------------*/
+void mcore_device_close(
+	struct mcore_device_t *dev
+) {
+	mobicore_release(dev->instance);
+}
+
+
+/*----------------------------------------------------------------------------*/
+bool mcore_device_has_sessions(
+	struct mcore_device_t *dev
+) {
+	return !list_empty(&dev->session_vector);
+}
+
+
+/*----------------------------------------------------------------------------*/
+bool mcore_device_create_new_session(
+	struct mcore_device_t	*dev,
+	uint32_t		session_id,
+	struct connection	*connection
+) {
+	/* Check if session_id already exists */
+	if (mcore_device_resolve_session_id(dev, session_id)) {
+		MCDRV_DBG_ERROR(" session %u already exists", session_id);
+		return false;
+	}
+	struct session *session = session_create(session_id, dev->instance,
+						connection);
+	list_add_tail(&(session->list), &(dev->session_vector));
+	return true;
+}
+
+
+/*----------------------------------------------------------------------------*/
+bool mcore_device_remove_session(
+	struct mcore_device_t *dev,
+	uint32_t session_id
+) {
+	bool ret = false;
+	struct session *tmp;
+	struct list_head *pos, *q;
+
+	list_for_each_safe(pos, q, &dev->session_vector) {
+		tmp = list_entry(pos, struct session, list);
+		if (tmp->session_id == session_id) {
+			list_del(pos);
+			session_cleanup(tmp);
+			ret = true;
+			break;
+		}
+	}
+	return ret;
+}
+
+
+/*----------------------------------------------------------------------------*/
+struct session *mcore_device_resolve_session_id(
+	struct mcore_device_t *dev,
+	uint32_t session_id
+) {
+	struct session  *ret = NULL;
+	struct session *tmp;
+	struct list_head *pos;
+
+
+	/* Get session for session_id */
+	list_for_each(pos, &dev->session_vector) {
+		tmp = list_entry(pos, struct session, list);
+		if (tmp->session_id == session_id) {
+			ret = tmp;
+			break;
+		}
+	}
+	return ret;
+}
+
+
+/*----------------------------------------------------------------------------*/
+struct wsm *mcore_device_allocate_contiguous_wsm(
+	struct mcore_device_t *dev,
+	uint32_t len
+) {
+	struct wsm *wsm = NULL;
+	do {
+		if (len == 0)
+			break;
+
+		/* Allocate shared memory */
+		void	*virt_addr;
+		uint32_t  handle;
+		void	*phys_addr;
+		int ret = mobicore_allocate_wsm(dev->instance,
+						len,
+						&handle,
+						&virt_addr,
+						&phys_addr);
+		if (ret != 0)
+			break;
+
+		/* Register (vaddr,paddr) with device */
+		wsm = wsm_create(virt_addr, len, handle, phys_addr);
+
+		list_add_tail(&(wsm->list), &(dev->wsm_l2_vector));
+
+	} while (0);
+
+	/* Return pointer to the allocated memory */
+	return wsm;
+}
+
+
+/*----------------------------------------------------------------------------*/
+bool mcore_device_free_contiguous_wsm(
+	struct mcore_device_t  *dev,
+	struct wsm   *wsm
+) {
+	bool ret = false;
+	struct wsm *tmp;
+	struct list_head *pos;
+
+	list_for_each(pos, &dev->wsm_l2_vector) {
+		tmp = list_entry(pos, struct wsm, list);
+		if (tmp == wsm) {
+			ret = true;
+			break;
+		}
+	}
+
+	if (ret) {
+		MCDRV_DBG_VERBOSE("freeWsm virt_addr=0x%p, handle=%d",
+				wsm->virt_addr, wsm->handle);
+
+		/* ignore return code */
+		mobicore_free(dev->instance, wsm->handle);
+
+		list_del(pos);
+		kfree(wsm);
+	}
+	return ret;
+}
+
+
+/*----------------------------------------------------------------------------*/
+struct wsm *mcore_device_find_contiguous_wsm(
+	struct mcore_device_t *dev,
+	void   *virt_addr
+) {
+	struct wsm *wsm;
+	struct list_head *pos;
+
+	list_for_each(pos, &dev->wsm_l2_vector) {
+		wsm = list_entry(pos, struct wsm, list);
+		if (virt_addr == wsm->virt_addr)
+			return wsm;
+	}
+
+	return NULL;
+}
+
+/** @} */
diff --git a/drivers/gud/mobicore_kernelapi/device.h b/drivers/gud/mobicore_kernelapi/device.h
new file mode 100644
index 0000000..f40d993
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/device.h
@@ -0,0 +1,139 @@
+/** @addtogroup MCD_IMPL_LIB
+ * @{
+ * @file
+ *
+ * Client library device management.
+ *
+ * Device and Trustlet Session management Functions.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef DEVICE_H_
+#define DEVICE_H_
+
+#include <linux/list.h>
+
+#include "connection.h"
+#include "session.h"
+#include "wsm.h"
+
+
+struct mcore_device_t {
+	struct list_head session_vector; /**< MobiCore Trustlet session
+				associated with the device */
+	struct list_head	 wsm_l2_vector; /**< WSM L2 Table  */
+
+	uint32_t		device_id; /**< Device identifier */
+	struct connection	*connection; /**< The device connection */
+	struct mc_instance  *instance; /**< MobiCore Driver instance */
+
+	struct list_head list; /**< The list param for using the kernel lists*/
+};
+
+struct mcore_device_t *mcore_device_create(
+	uint32_t	  device_id,
+	struct connection  *connection
+);
+
+void mcore_device_cleanup(
+	struct mcore_device_t *dev
+);
+
+/**
+  * Open the device.
+  * @param deviceName Name of the kernel modules device file.
+  * @return true if the device has been opened successfully
+  */
+bool mcore_device_open(
+	struct mcore_device_t   *dev,
+	const char *deviceName
+);
+
+/**
+  * Closes the device.
+  */
+void mcore_device_close(
+	struct mcore_device_t *dev
+);
+
+/**
+  * Check if the device has open sessions.
+  * @return true if the device has one or more open sessions.
+  */
+bool mcore_device_has_sessions(
+	struct mcore_device_t *dev
+);
+
+/**
+  * Add a session to the device.
+  * @param session_id session ID
+  * @param connection session connection
+  */
+bool mcore_device_create_new_session(
+	struct mcore_device_t	  *dev,
+	uint32_t	session_id,
+	struct connection  *connection
+);
+
+/**
+  * Remove the specified session from the device.
+  * The session object will be destroyed and all resources associated with it
+  * will be freed.
+  *
+  * @param session_id Session of the session to remove.
+  * @return true if a session has been found and removed.
+  */
+bool mcore_device_remove_session(
+	struct mcore_device_t *dev,
+	uint32_t session_id
+);
+
+/**
+  * Get as session object for a given session ID.
+  * @param session_id Identified of a previously opened session.
+  * @return Session object if available or NULL if no session has been found.
+  */
+struct session *mcore_device_resolve_session_id(
+	struct mcore_device_t *dev,
+	uint32_t session_id
+);
+
+/**
+  * Allocate a block of contiguous WSM.
+  * @param len The virtual address to be registered.
+  * @return The virtual address of the allocated memory or NULL if no memory
+  * is available.
+  */
+struct wsm *mcore_device_allocate_contiguous_wsm(
+	struct mcore_device_t *dev,
+	uint32_t len
+);
+
+/**
+  * Unregister a vaddr from a device.
+  * @param vaddr The virtual address to be registered.
+  * @param paddr The physical address to be registered.
+  */
+bool mcore_device_free_contiguous_wsm(
+	struct mcore_device_t  *dev,
+	struct wsm *wsm
+);
+
+/**
+  * Get a WSM object for a given virtual address.
+  * @param vaddr The virtual address which has been allocate with mc_malloc_wsm()
+  * in advance.
+  * @return the WSM object or NULL if no address has been found.
+  */
+struct wsm *mcore_device_find_contiguous_wsm(
+	struct mcore_device_t *dev,
+	void   *virt_addr
+);
+
+#endif /* DEVICE_H_ */
+
+/** @} */
diff --git a/drivers/gud/mobicore_kernelapi/include/mcinq.h b/drivers/gud/mobicore_kernelapi/include/mcinq.h
new file mode 100644
index 0000000..3cb82be
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/include/mcinq.h
@@ -0,0 +1,125 @@
+/** @addtogroup NQ
+ * @{
+ * Notifications inform the MobiCore runtime environment that information is
+ * pending in a WSM buffer.
+ * The Trustlet Connector (TLC) and the corresponding trustlet also utilize
+ * this buffer to notify each other about new data within the
+ * Trustlet Connector Interface (TCI).
+ *
+ * The buffer is set up as a queue, which means that more than one
+ * notification can be written to the buffer before the switch to the other
+ * world is performed. Each side therefore facilitates an incoming and an
+ * outgoing queue for communication with the other side.
+ *
+ * Notifications hold the session ID, which is used to reference the
+ * communication partner in the other world.
+ * So if, e.g., the TLC in the normal world wants to notify his trustlet
+ * about new data in the TLC buffer
+ *
+ * @file
+ * Notification queue declarations.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *	notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *	notice, this list of conditions and the following disclaimer in the
+ *	documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *	products derived from this software without specific prior
+ *	written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef NQ_H_
+#define NQ_H_
+
+/** \name NQ Size Defines
+ * Minimum and maximum count of elements in the notification queue.
+ * @{ */
+#define MIN_NQ_ELEM 1   /**< Minimum notification queue elements. */
+#define MAX_NQ_ELEM 64 /**< Maximum notification queue elements. */
+/** @} */
+
+/** \name NQ Length Defines
+ * Minimum and maximum notification queue length.
+ * @{ */
+/**< Minimum notification length (in bytes). */
+#define MIN_NQ_LEN (MIN_NQ_ELEM * sizeof(notification))
+/**< Maximum notification length (in bytes). */
+#define MAX_NQ_LEN (MAX_NQ_ELEM * sizeof(notification))
+/** @} */
+
+/** \name Session ID Defines
+ * Standard Session IDs.
+ * @{ */
+/**< MCP session ID is used when directly communicating with the MobiCore
+ * (e.g. for starting and stopping of trustlets). */
+#define SID_MCP	   0
+/**< Invalid session id is returned in case of an error. */
+#define SID_INVALID   0xffffffff
+/** @} */
+
+/** Notification data structure. */
+struct notification {
+	uint32_t session_id; /**< Session ID. */
+	int32_t payload;	/**< Additional notification information. */
+};
+
+/** Notification payload codes.
+ * 0 indicated a plain simple notification,
+ * a positive value is a termination reason from the task,
+ * a negative value is a termination reason from MobiCore.
+ * Possible negative values are given below.
+ */
+enum notification_payload {
+	/**< task terminated, but exit code is invalid */
+	ERR_INVALID_EXIT_CODE   = -1,
+	/**< task terminated due to session end, no exit code available */
+	ERR_SESSION_CLOSE	   = -2,
+	/**< task terminated due to invalid operation */
+	ERR_INVALID_OPERATION   = -3,
+	/**< session ID is unknown */
+	ERR_INVALID_SID		 = -4,
+	/**<  session is not active */
+	ERR_SID_NOT_ACTIVE	  = -5
+};
+
+/** Declaration of the notification queue header.
+ * layout as specified in the data structure specification.
+ */
+struct notification_queue_header {
+	uint32_t write_cnt;  /**< Write counter. */
+	uint32_t read_cnt;   /**< Read counter. */
+	uint32_t queue_size; /**< Queue size. */
+};
+
+/** Queue struct which defines a queue object.
+ * The queue struct is accessed by the queue<operation> type of
+ * function. elementCnt must be a power of two and the power needs
+ * to be smaller than power of uint32_t (obviously 32).
+ */
+struct notification_queue {
+	/**< Queue header. */
+	struct notification_queue_header hdr;
+	/**< Notification elements. */
+	struct notification notification[MIN_NQ_ELEM];
+} ;
+
+#endif /** NQ_H_ */
+
+/** @} */
diff --git a/drivers/gud/mobicore_kernelapi/include/mcuuid.h b/drivers/gud/mobicore_kernelapi/include/mcuuid.h
new file mode 100644
index 0000000..b72acb8
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/include/mcuuid.h
@@ -0,0 +1,74 @@
+/**
+ * @addtogroup MC_UUID mcUuid - Universally Unique Identifier.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2011-2012 -->
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *	notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *	notice, this list of conditions and the following disclaimer in the
+ *	documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *	products derived from this software without specific prior
+ *	written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @ingroup  MC_DATA_TYPES
+ * @{
+ */
+
+#ifndef MC_UUID_H_
+#define MC_UUID_H_
+
+#define UUID_TYPE
+
+/** Universally Unique Identifier (UUID) according to ISO/IEC 11578. */
+struct mc_uuid_t {
+	uint8_t value[16]; /**< Value of the UUID. */
+};
+
+/** UUID value used as free marker in service provider containers. */
+#define MC_UUID_FREE_DEFINE \
+	{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
+	  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
+
+static const struct mc_uuid_t MC_UUID_FREE = {
+	MC_UUID_FREE_DEFINE
+};
+
+/** Reserved UUID. */
+#define MC_UUID_RESERVED_DEFINE \
+	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
+
+static const struct mc_uuid_t MC_UUID_RESERVED = {
+	MC_UUID_RESERVED_DEFINE
+};
+
+/** UUID for system applications. */
+#define MC_UUID_SYSTEM_DEFINE \
+	{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
+	  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE }
+
+static const struct mc_uuid_t MC_UUID_SYSTEM = {
+	MC_UUID_SYSTEM_DEFINE
+};
+
+#endif /* MC_UUID_H_ */
+
+/** @} */
+
diff --git a/drivers/gud/mobicore_kernelapi/main.c b/drivers/gud/mobicore_kernelapi/main.c
new file mode 100644
index 0000000..62997f7
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/main.c
@@ -0,0 +1,181 @@
+/**
+ * MobiCore KernelApi module
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/netlink.h>
+#include <linux/kthread.h>
+#include <net/sock.h>
+
+#include <linux/list.h>
+
+#include "connection.h"
+#include "common.h"
+
+#define MC_DAEMON_NETLINK  17
+
+struct mc_kernelapi_ctx {
+	struct sock *sk;
+	struct list_head peers;
+	atomic_t counter;
+};
+
+struct mc_kernelapi_ctx *mod_ctx; /* = NULL; */
+
+/*----------------------------------------------------------------------------*/
+/* get a unique ID */
+unsigned int mcapi_unique_id(
+	void
+)
+{
+	return (unsigned int)atomic_inc_return(
+		&(mod_ctx->counter));
+}
+
+
+/*----------------------------------------------------------------------------*/
+static struct connection *mcapi_find_connection(
+	uint32_t seq
+)
+{
+	struct connection *tmp;
+	struct list_head *pos;
+
+	/* Get session for session_id */
+	list_for_each(pos, &mod_ctx->peers) {
+		tmp = list_entry(pos, struct connection, list);
+		if (tmp->sequence_magic == seq)
+			return tmp;
+	}
+
+	return NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+void mcapi_insert_connection(
+	struct connection *connection
+)
+{
+	list_add_tail(&(connection->list), &(mod_ctx->peers));
+	connection->socket_descriptor = mod_ctx->sk;
+}
+
+void mcapi_remove_connection(
+	uint32_t seq
+)
+{
+	struct connection *tmp;
+	struct list_head *pos, *q;
+
+	/* Delete all session objects. Usually this should not be needed as
+	   closeDevice() requires that all sessions have been closed before.*/
+	list_for_each_safe(pos, q, &mod_ctx->peers) {
+		tmp = list_entry(pos, struct connection, list);
+		if (tmp->sequence_magic == seq) {
+			list_del(pos);
+			break;
+		}
+	}
+}
+
+/*----------------------------------------------------------------------------*/
+static int mcapi_process(
+	struct sk_buff *skb,
+	struct nlmsghdr *nlh
+)
+{
+	struct connection *c;
+	int length;
+	int seq;
+	pid_t pid;
+	int ret;
+
+	pid = nlh->nlmsg_pid;
+	length = nlh->nlmsg_len;
+	seq = nlh->nlmsg_seq;
+	MCDRV_DBG_VERBOSE("nlmsg len %d type %d pid 0x%X seq %d\n",
+		   length, nlh->nlmsg_type, pid, seq);
+	do {
+		c = mcapi_find_connection(seq);
+		if (!c) {
+			MCDRV_ERROR("Invalid incomming connection - seq=%u!",
+				seq);
+			ret = -1;
+			break;
+		}
+
+		/* Pass the buffer to the appropriate connection */
+		connection_process(c, skb);
+
+		ret = 0;
+	} while (false);
+	return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+static void mcapi_callback(
+	struct sk_buff *skb
+)
+{
+	struct nlmsghdr *nlh = nlmsg_hdr(skb);
+	int len = skb->len;
+	int err = 0;
+
+	while (NLMSG_OK(nlh, len)) {
+		err = mcapi_process(skb, nlh);
+
+		/* if err or if this message says it wants a response */
+		if (err || (nlh->nlmsg_flags & NLM_F_ACK))
+			netlink_ack(skb, nlh, err);
+
+		nlh = NLMSG_NEXT(nlh, len);
+	}
+}
+
+/*----------------------------------------------------------------------------*/
+static int __init mcapi_init(void)
+{
+	printk(KERN_INFO "Mobicore API module initialized!\n");
+
+	mod_ctx = kzalloc(sizeof(struct mc_kernelapi_ctx), GFP_KERNEL);
+
+	/* start kernel thread */
+	mod_ctx->sk = netlink_kernel_create(&init_net, MC_DAEMON_NETLINK, 0,
+					mcapi_callback, NULL, THIS_MODULE);
+
+	if (!mod_ctx->sk) {
+		MCDRV_ERROR("register of recieve handler failed");
+		return -EFAULT;
+	}
+
+	INIT_LIST_HEAD(&mod_ctx->peers);
+	return 0;
+}
+
+static void __exit mcapi_exit(void)
+{
+	printk(KERN_INFO "Unloading Mobicore API module.\n");
+
+	if (mod_ctx->sk != NULL) {
+		netlink_kernel_release(mod_ctx->sk);
+		mod_ctx->sk = NULL;
+	}
+	kfree(mod_ctx);
+	mod_ctx = NULL;
+}
+
+module_init(mcapi_init);
+module_exit(mcapi_exit);
+
+MODULE_AUTHOR("Giesecke & Devrient GmbH");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MobiCore API driver");
diff --git a/drivers/gud/mobicore_kernelapi/public/mobicore_driver_api.h b/drivers/gud/mobicore_kernelapi/public/mobicore_driver_api.h
new file mode 100644
index 0000000..ccfb2e5
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/public/mobicore_driver_api.h
@@ -0,0 +1,483 @@
+/**
+ * @defgroup MCD_API					MobiCore Driver API
+ * @addtogroup MCD_API
+ * @{
+ *
+ * @if DOXYGEN_MCDRV_API
+ *   @mainpage MobiCore Driver API.
+ * @endif
+ *
+ * MobiCore Driver API.
+ *
+ * The MobiCore (MC) Driver API provides access functions to the MobiCore
+ * runtime environment and the contained Trustlets.
+ *
+ * @image html DoxyOverviewDrvApi500x.png
+ * @image latex DoxyOverviewDrvApi500x.png "MobiCore Overview" width=12cm
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *	notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *	notice, this list of conditions and the following disclaimer in the
+ *	documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *	products derived from this software without specific prior
+ *	written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef MCDRIVER_H_
+#define MCDRIVER_H_
+
+#define __MC_CLIENT_LIB_API
+
+#include "mcuuid.h"
+
+/**
+ * Return values of MobiCore driver functions.
+ */
+enum mc_result {
+	/**< Function call succeeded. */
+	MC_DRV_OK		= 0,
+	/**< No notification available. */
+	MC_DRV_NO_NOTIFICATION	= 1,
+	/**< Error during notification on communication level. */
+	MC_DRV_ERR_NOTIFICATION	= 2,
+	/**< Function not implemented. */
+	MC_DRV_ERR_NOT_IMPLEMENTED = 3,
+	/**< No more resources available. */
+	MC_DRV_ERR_OUT_OF_RESOURCES = 4,
+	/**< Driver initialization failed. */
+	MC_DRV_ERR_INIT = 5,
+	/**< Unknown error. */
+	MC_DRV_ERR_UNKNOWN	= 6,
+	/**< The specified device is unknown. */
+	MC_DRV_ERR_UNKNOWN_DEVICE = 7,
+	/**< The specified session is unknown.*/
+	MC_DRV_ERR_UNKNOWN_SESSION = 8,
+	/**< The specified operation is not allowed. */
+	MC_DRV_ERR_INVALID_OPERATION = 9,
+	/**< The response header from the MC is invalid. */
+	MC_DRV_ERR_INVALID_RESPONSE = 10,
+	/**< Function call timed out. */
+	MC_DRV_ERR_TIMEOUT = 11,
+	/**< Can not allocate additional memory. */
+	MC_DRV_ERR_NO_FREE_MEMORY = 12,
+	/**< Free memory failed. */
+	MC_DRV_ERR_FREE_MEMORY_FAILED = 13,
+	/**< Still some open sessions pending. */
+	MC_DRV_ERR_SESSION_PENDING = 14,
+	/**< MC daemon not reachable */
+	MC_DRV_ERR_DAEMON_UNREACHABLE = 15,
+	/**< The device file of the kernel module could not be opened. */
+	MC_DRV_ERR_INVALID_DEVICE_FILE = 16,
+	/**< Invalid parameter. */
+	MC_DRV_ERR_INVALID_PARAMETER	= 17,
+	/**< Unspecified error from Kernel Module*/
+	MC_DRV_ERR_KERNEL_MODULE = 18,
+	/**< Error during mapping of additional bulk memory to session. */
+	MC_DRV_ERR_BULK_MAPPING = 19,
+	/**< Error during unmapping of additional bulk memory to session. */
+	MC_DRV_ERR_BULK_UNMAPPING = 20,
+	/**< Notification received, exit code available. */
+	MC_DRV_INFO_NOTIFICATION = 21,
+	/**< Set up of NWd connection failed. */
+	MC_DRV_ERR_NQ_FAILED = 22
+};
+
+
+/**
+ * Driver control command.
+ */
+enum mc_driver_ctrl {
+	MC_CTRL_GET_VERSION = 1 /**< Return the driver version */
+};
+
+
+/** Structure of Session Handle, includes the Session ID and the Device ID the
+ * Session belongs to.
+ * The session handle will be used for session-based MobiCore communication.
+ * It will be passed to calls which address a communication end point in the
+ * MobiCore environment.
+ */
+struct mc_session_handle {
+	uint32_t session_id; /**< MobiCore session ID */
+	uint32_t device_id; /**< Device ID the session belongs to */
+};
+
+/** Information structure about additional mapped Bulk buffer between the
+ * Trustlet Connector (Nwd) and the Trustlet (Swd). This structure is
+ * initialized from a Trustlet Connector by calling mc_map().
+ * In order to use the memory within a Trustlet the Trustlet Connector has to
+ * inform the Trustlet with the content of this structure via the TCI.
+ */
+struct mc_bulk_map {
+	/**< The virtual address of the Bulk buffer regarding the address space
+	 * of the Trustlet, already includes a possible offset! */
+	void *secure_virt_addr;
+	uint32_t secure_virt_len; /**< Length of the mapped Bulk buffer */
+};
+
+
+/**< The default device ID */
+#define MC_DEVICE_ID_DEFAULT	0
+/**< Wait infinite for a response of the MC. */
+#define MC_INFINITE_TIMEOUT	((int32_t)(-1))
+/**< Do not wait for a response of the MC. */
+#define MC_NO_TIMEOUT		0
+/**< TCI/DCI must not exceed 1MiB */
+#define MC_MAX_TCI_LEN		0x100000
+
+
+
+/** Open a new connection to a MobiCore device.
+ *
+ * mc_open_device() initializes all device specific resources required to
+ * communicate with an MobiCore instance located on the specified device in the
+ * system. If the device does not exist the function will return
+ * MC_DRV_ERR_UNKNOWN_DEVICE.
+ *
+ * @param [in] device_id Identifier for the MobiCore device to be used.
+ * MC_DEVICE_ID_DEFAULT refers to the default device.
+ *
+ * @return MC_DRV_OK if operation has been successfully completed.
+ * @return MC_DRV_ERR_INVALID_OPERATION if device already opened.
+ * @return MC_DRV_ERR_DAEMON_UNREACHABLE when problems with daemon occur.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when device_id is unknown.
+ * @return MC_DRV_ERR_INVALID_DEVICE_FILE if kernel module under
+ * /dev/mobicore cannot be opened
+ *
+ * Uses a Mutex.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_open_device(
+	uint32_t device_id
+);
+
+/** Close the connection to a MobiCore device.
+ * When closing a device, active sessions have to be closed beforehand.
+ * Resources associated with the device will be released.
+ * The device may be opened again after it has been closed.
+ *
+ * @param [in] device_id Identifier for the MobiCore device.
+ * MC_DEVICE_ID_DEFAULT refers to the default device.
+ *
+ * @return MC_DRV_OK if operation has been successfully completed.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when device id is invalid.
+ * @return MC_DRV_ERR_SESSION_PENDING when a session is still open.
+ * @return MC_DRV_ERR_DAEMON_UNREACHABLE when problems with daemon occur.
+ *
+ * Uses a Mutex.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_close_device(
+	uint32_t device_id
+);
+
+/** Open a new session to a Trustlet. The trustlet with the given UUID has
+ * to be available in the flash filesystem.
+ *
+ * Write MCP open message to buffer and notify MobiCore about the availability
+ * of a new command.
+ * Waits till the MobiCore responses with the new session ID (stored in the MCP
+ * buffer).
+ *
+ * @param [in,out] session On success, the session data will be returned.
+ * Note that session.device_id has to be the device id of an opened device.
+ * @param [in] uuid UUID of the Trustlet to be opened.
+ * @param [in] tci TCI buffer for communicating with the trustlet.
+ * @param [in] tci_len Length of the TCI buffer. Maximum allowed value
+ * is MC_MAX_TCI_LEN.
+ *
+ * @return MC_DRV_OK if operation has been successfully completed.
+ * @return MC_DRV_INVALID_PARAMETER if session parameter is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when device id is invalid.
+ * @return MC_DRV_ERR_DAEMON_UNREACHABLE when problems with daemon socket occur.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when daemon returns an error.
+ *
+ * Uses a Mutex.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_open_session(
+	struct mc_session_handle  *session,
+	const struct mc_uuid_t *uuid,
+	uint8_t *tci,
+	uint32_t tci_len
+);
+
+/** Close a Trustlet session.
+ *
+ * Closes the specified MobiCore session. The call will block until the
+ * session has been closed.
+ *
+ * @pre Device device_id has to be opened in advance.
+ *
+ * @param [in] session Session to be closed.
+ *
+ * @return MC_DRV_OK if operation has been successfully completed.
+ * @return MC_DRV_INVALID_PARAMETER if session parameter is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_SESSION when session id is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when device id of session is invalid.
+ * @return MC_DRV_ERR_DAEMON_UNREACHABLE when problems with daemon occur.
+ * @return MC_DRV_ERR_INVALID_DEVICE_FILE when daemon cannot open trustlet file.
+ *
+ * Uses a Mutex.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_close_session(
+	struct mc_session_handle *session
+);
+
+/** Notify a session.
+ * Notifies the session end point about available message data.
+ * If the session parameter is correct, notify will always succeed.
+ * Corresponding errors can only be received by mc_wait_notification().
+ * @pre A session has to be opened in advance.
+ *
+ * @param session The session to be notified.
+ *
+ * @return MC_DRV_OK if operation has been successfully completed.
+ * @return MC_DRV_INVALID_PARAMETER if session parameter is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_SESSION when session id is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when device id of session is invalid.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_notify(
+	struct mc_session_handle *session
+);
+
+/** Wait for a notification.
+ *
+ * Wait for a notification issued by the MobiCore for a specific session.
+ * The timeout parameter specifies the number of milliseconds the call will wait
+ * for a notification.
+ * If the caller passes 0 as timeout value the call will immediately return.
+ * If timeout value is below 0 the call will block until a notification for the
+ session has been received.
+ *
+ * @attention if timeout is below 0, call will block:
+ * Caller has to trust the other side to send a notification to wake him up
+ * again.
+ *
+ * @param [in] session The session the notification should correspond to.
+ * @param [in] timeout Time in milliseconds to wait
+ * (MC_NO_TIMEOUT : direct return, > 0 : milliseconds,
+ * MC_INFINITE_TIMEOUT : wait infinitely)
+ *
+ * @return MC_DRV_OK if notification is available.
+ * @return MC_DRV_ERR_TIMEOUT if no notification arrived in time.
+ * @return MC_DRV_INFO_NOTIFICATION if a problem with the session was
+ * encountered. Get more details with mc_get_session_error_code().
+ * @return MC_DRV_ERR_NOTIFICATION if a problem with the socket occurred.
+ * @return MC_DRV_INVALID_PARAMETER if a parameter is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_SESSION when session id is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when device id of session is invalid.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_wait_notification(
+	struct mc_session_handle  *session,
+	int32_t			timeout
+);
+
+/**
+ * Allocate a block of world shared memory (WSM).
+ * The MC driver allocates a contiguous block of memory which can be used as
+ * WSM.
+ * This implicates that the allocated memory is aligned according to the
+ * alignment parameter.
+ * Always returns a buffer of size WSM_SIZE aligned to 4K.
+ *
+ * @param [in]  device_id The ID of an opened device to retrieve the WSM from.
+ * @param [in]  align The alignment (number of pages) of the memory block
+ * (e.g. 0x00000001 for 4kb).
+ * @param [in]  len	Length of the block in bytes.
+ * @param [out] wsm Virtual address of the world shared memory block.
+ * @param [in]  wsm_flags Platform specific flags describing the memory to
+ * be allocated.
+ *
+ * @attention: align and wsm_flags are currently ignored
+ *
+ * @return MC_DRV_OK if operation has been successfully completed.
+ * @return MC_DRV_INVALID_PARAMETER if a parameter is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when device id is invalid.
+ * @return MC_DRV_ERR_NO_FREE_MEMORY if no more contiguous memory is available
+ * in this size or for this process.
+ *
+ * Uses a Mutex.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_malloc_wsm(
+	uint32_t  device_id,
+	uint32_t  align,
+	uint32_t  len,
+	uint8_t   **wsm,
+	uint32_t  wsm_flags
+);
+
+/**
+ * Free a block of world shared memory (WSM).
+ * The MC driver will free a block of world shared memory (WSM) previously
+ * allocated with mc_malloc_wsm(). The caller has to assure that the address
+ * handed over to the driver is a valid WSM address.
+ *
+ * @param [in] device_id The ID to which the given address belongs.
+ * @param [in] wsm Address of WSM block to be freed.
+ *
+ * @return MC_DRV_OK if operation has been successfully completed.
+ * @return MC_DRV_INVALID_PARAMETER if a parameter is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when device id is invalid.
+ * @return MC_DRV_ERR_FREE_MEMORY_FAILED on failures.
+ *
+ * Uses a Mutex.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_free_wsm(
+	uint32_t  device_id,
+	uint8_t   *wsm
+);
+
+/**
+ * Map additional bulk buffer between a Trustlet Connector (TLC) and
+ * the Trustlet (TL) for a session.
+ * Memory allocated in user space of the TLC can be mapped as additional
+ * communication channel (besides TCI) to the Trustlet. Limitation of the
+ * Trustlet memory structure apply: only 6 chunks can be mapped with a maximum
+ * chunk size of 1 MiB each.
+ *
+ * @attention It is up to the application layer (TLC) to inform the Trustlet
+ * about the additional mapped bulk memory.
+ *
+ * @param [in] session Session handle with information of the device_id and
+ * the session_id. The
+ * given buffer is mapped to the session specified in the sessionHandle.
+ * @param [in] buf Virtual address of a memory portion (relative to TLC)
+ * to be shared with the Trustlet, already includes a possible offset!
+ * @param [in] len length of buffer block in bytes.
+ * @param [out] map_info Information structure about the mapped Bulk buffer
+ * between the TLC (Nwd) and
+ * the TL (Swd).
+ *
+ * @return MC_DRV_OK if operation has been successfully completed.
+ * @return MC_DRV_INVALID_PARAMETER if a parameter is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_SESSION when session id is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when device id of session is invalid.
+ * @return MC_DRV_ERR_DAEMON_UNREACHABLE when problems with daemon occur.
+ * @return MC_DRV_ERR_BULK_MAPPING when buf is already uses as bulk buffer or
+ * when registering the buffer failed.
+ *
+ * Uses a Mutex.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_map(
+	struct mc_session_handle	*session,
+	void				*buf,
+	uint32_t			len,
+	struct mc_bulk_map		*map_info
+);
+
+/**
+ * Remove additional mapped bulk buffer between Trustlet Connector (TLC)
+ * and the Trustlet (TL) for a session.
+ *
+ * @attention The bulk buffer will immediately be unmapped from the session
+ * context.
+ * @attention The application layer (TLC) must inform the TL about unmapping
+ * of the additional bulk memory before calling mc_unmap!
+ *
+ * @param [in] session Session handle with information of the device_id and
+ * the session_id. The given buffer is unmapped from the session specified
+ * in the sessionHandle.
+ * @param [in] buf Virtual address of a memory portion (relative to TLC)
+ * shared with the TL, already includes a possible offset!
+ * @param [in] map_info Information structure about the mapped Bulk buffer
+ * between the TLC (Nwd) and
+ * the TL (Swd).
+ * @attention The clientlib currently ignores the len field in map_info.
+ *
+ * @return MC_DRV_OK if operation has been successfully completed.
+ * @return MC_DRV_INVALID_PARAMETER if a parameter is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_SESSION when session id is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when device id of session is invalid.
+ * @return MC_DRV_ERR_DAEMON_UNREACHABLE when problems with daemon occur.
+ * @return MC_DRV_ERR_BULK_UNMAPPING when buf was not registered earlier
+ * or when unregistering failed.
+ *
+ * Uses a Mutex.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_unmap(
+	struct mc_session_handle	*session,
+	void				*buf,
+	struct mc_bulk_map		*map_info
+);
+
+
+/**
+ * @attention: Not implemented.
+ * Execute driver specific command.
+ * mc_driver_ctrl() can be used to execute driver specific commands.
+ * Besides the control command MC_CTRL_GET_VERSION commands are implementation
+ * specific.
+ * Please refer to the corresponding specification of the driver manufacturer.
+ *
+ * @param [in] param Command ID of the command to be executed.
+ * @param [in, out] data  Command data and response depending on command.
+ * @param [in] len Length of the data block.
+ *
+ * @return MC_DRV_ERR_NOT_IMPLEMENTED.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_driver_ctrl(
+	enum mc_driver_ctrl  param,
+	uint8_t		 *data,
+	uint32_t	len
+);
+
+/**
+ * @attention: Not implemented.
+ * Execute application management command.
+ * mc_manage() shall be used to exchange application management commands with
+ * the MobiCore.
+ * The MobiCore Application Management Protocol is described in [MCAMP].
+ *
+ * @param [in] device_id Identifier for the MobiCore device to be used.
+ * NULL refers to the default device.
+ * @param [in, out] data  Command data/response data depending on command.
+ * @param [in] len Length of the data block.
+ *
+ * @return MC_DRV_ERR_NOT_IMPLEMENTED.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_manage(
+	uint32_t  device_id,
+	uint8_t   *data,
+	uint32_t  len
+);
+
+/**
+ * Get additional error information of the last error that occured on a session.
+ * After the request the stored error code will be deleted.
+ *
+ * @param [in] session Session handle with information of the device_id and
+ * the session_id.
+ * @param [out] last_error >0 Trustlet has terminated itself with this value,
+ * <0 Trustlet is dead because of an error within the MobiCore
+ * (e.g. Kernel exception).
+ * See also MCI definition.
+ *
+ * @return MC_DRV_OK if operation has been successfully completed.
+ * @return MC_DRV_INVALID_PARAMETER if a parameter is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_SESSION when session id is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when device id of session is invalid.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_get_session_error_code(
+	struct mc_session_handle  *session,
+	int32_t			*last_error
+);
+
+#endif /** MCDRIVER_H_ */
+
+/** @} */
diff --git a/drivers/gud/mobicore_kernelapi/public/mobicore_driver_cmd.h b/drivers/gud/mobicore_kernelapi/public/mobicore_driver_cmd.h
new file mode 100644
index 0000000..9ff7989
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/public/mobicore_driver_cmd.h
@@ -0,0 +1,289 @@
+/** @addtogroup MCD_MCDIMPL_DAEMON
+ * @{
+ * @file
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *	notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *	notice, this list of conditions and the following disclaimer in the
+ *	documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *	products derived from this software without specific prior
+ *	written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef MCDAEMON_H_
+#define MCDAEMON_H_
+
+
+
+
+#include "mcuuid.h"
+
+enum mc_drv_cmd_t {
+	MC_DRV_CMD_PING			= 0,
+	MC_DRV_CMD_GET_INFO		= 1,
+	MC_DRV_CMD_OPEN_DEVICE		= 2,
+	MC_DRV_CMD_CLOSE_DEVICE		= 3,
+	MC_DRV_CMD_NQ_CONNECT		= 4,
+	MC_DRV_CMD_OPEN_SESSION		= 5,
+	MC_DRV_CMD_CLOSE_SESSION	= 6,
+	MC_DRV_CMD_NOTIFY		= 7,
+	MC_DRV_CMD_MAP_BULK_BUF		= 8,
+	MC_DRV_CMD_UNMAP_BULK_BUF	= 9
+};
+
+
+enum mc_drv_rsp_t {
+	MC_DRV_RSP_OK				= 0,
+	MC_DRV_RSP_FAILED			= 1,
+	MC_DRV_RSP_DEVICE_NOT_OPENED		= 2,
+	MC_DRV_RSP_DEVICE_ALREADY_OPENED	= 3,
+	MC_DRV_RSP_COMMAND_NOT_ALLOWED		= 4,
+	MC_DRV_INVALID_DEVICE_NAME		= 5,
+	MC_DRV_RSP_MAP_BULK_ERRO		= 6,
+	MC_DRV_RSP_TRUSTLET_NOT_FOUND		= 7,
+	MC_DRV_RSP_PAYLOAD_LENGTH_ERROR		= 8,
+};
+
+
+struct mc_drv_command_header_t {
+	uint32_t  command_id;
+};
+
+struct mc_drv_response_header_t {
+	uint32_t  response_id;
+};
+
+#define MC_DEVICE_ID_DEFAULT	0 /**< The default device ID */
+
+
+/*****************************************************************************/
+struct mc_drv_cmd_open_device_payload_t {
+	uint32_t  device_id;
+};
+
+struct mc_drv_cmd_open_device_t {
+	struct mc_drv_command_header_t header;
+	struct mc_drv_cmd_open_device_payload_t payload;
+};
+
+
+struct mc_drv_rsp_open_device_payload_t {
+	/* empty */
+};
+
+struct mc_drv_rsp_open_device_t {
+	struct mc_drv_response_header_t header;
+	struct mc_drv_rsp_open_device_payload_t payload;
+};
+
+
+/*****************************************************************************/
+struct mc_drv_cmd_close_device_t {
+	struct mc_drv_command_header_t header;
+	/* no payload here because close has none.
+	   If we use an empty struct, C++ will count it as 4 bytes.
+	   This will write too much into the socket at write(cmd,sizeof(cmd)) */
+};
+
+
+struct mc_drv_rsp_close_device_payload_t {
+	/* empty */
+};
+
+struct mc_drv_rsp_close_device_t {
+	struct mc_drv_response_header_t header;
+	struct mc_drv_rsp_close_device_payload_t payload;
+};
+
+
+/*****************************************************************************/
+struct mc_drv_cmd_open_session_payload_t {
+	uint32_t device_id;
+	struct mc_uuid_t uuid;
+	uint32_t tci;
+	uint32_t len;
+};
+
+struct mc_drv_cmd_open_session_t {
+	struct mc_drv_command_header_t header;
+	struct mc_drv_cmd_open_session_payload_t payload;
+};
+
+
+struct mc_drv_rsp_open_session_payload_t {
+	uint32_t device_id;
+	uint32_t session_id;
+	uint32_t device_session_id;
+	uint32_t mc_result;
+	uint32_t session_magic;
+};
+
+struct mc_drv_rsp_open_session_t {
+	struct mc_drv_response_header_t header;
+	struct mc_drv_rsp_open_session_payload_t  payload;
+};
+
+
+/*****************************************************************************/
+struct mc_drv_cmd_close_session_payload_t {
+	uint32_t  session_id;
+};
+
+struct mc_drv_cmd_close_session_t {
+	struct mc_drv_command_header_t header;
+	struct mc_drv_cmd_close_session_payload_t payload;
+};
+
+
+struct mc_drv_rsp_close_session_payload_t {
+	/* empty */
+};
+
+struct mc_drv_rsp_close_session_t {
+	struct mc_drv_response_header_t header;
+	struct mc_drv_rsp_close_session_payload_t payload;
+};
+
+
+/*****************************************************************************/
+struct mc_drv_cmd_notify_payload_t {
+	uint32_t session_id;
+};
+
+struct mc_drv_cmd_notify_t {
+	struct mc_drv_command_header_t header;
+	struct mc_drv_cmd_notify_payload_t payload;
+};
+
+
+struct mc_drv_rsp_notify_payload_t {
+	/* empty */
+};
+
+struct mc_drv_rsp_notify_t {
+	struct mc_drv_response_header_t header;
+	struct mc_drv_rsp_notify_payload_t  payload;
+};
+
+
+/*****************************************************************************/
+struct mc_drv_cmd_map_bulk_mem_payload_t {
+	uint32_t session_id;
+	uint32_t phys_addr_l2;
+	uint32_t offset_payload;
+	uint32_t len_bulk_mem;
+};
+
+struct mc_drv_cmd_map_bulk_mem_t {
+	struct mc_drv_command_header_t header;
+	struct mc_drv_cmd_map_bulk_mem_payload_t  payload;
+};
+
+
+struct mc_drv_rsp_map_bulk_mem_payload_t {
+	uint32_t session_id;
+	uint32_t secure_virtual_adr;
+	uint32_t mc_result;
+};
+
+struct mc_drv_rsp_map_bulk_mem_t {
+	struct mc_drv_response_header_t header;
+	struct mc_drv_rsp_map_bulk_mem_payload_t  payload;
+};
+
+
+/*****************************************************************************/
+struct mc_drv_cmd_unmap_bulk_mem_payload_t {
+	uint32_t session_id;
+	uint32_t secure_virtual_adr;
+	uint32_t len_bulk_mem;
+};
+
+struct mc_drv_cmd_unmap_bulk_mem_t {
+	struct mc_drv_command_header_t header;
+	struct mc_drv_cmd_unmap_bulk_mem_payload_t payload;
+};
+
+
+struct mc_drv_rsp_unmap_bulk_mem_payload_t {
+	uint32_t response_id;
+	uint32_t session_id;
+	uint32_t mc_result;
+};
+
+struct mc_drv_rsp_unmap_bulk_mem_t {
+	struct mc_drv_response_header_t header;
+	struct mc_drv_rsp_unmap_bulk_mem_payload_t payload;
+};
+
+
+/*****************************************************************************/
+struct mc_drv_cmd_nqconnect_payload_t {
+	uint32_t device_id;
+	uint32_t session_id;
+	uint32_t device_session_id;
+	uint32_t session_magic; /* Random data */
+};
+
+struct mc_drv_cmd_nqconnect_t {
+	struct mc_drv_command_header_t header;
+	struct mc_drv_cmd_nqconnect_payload_t  payload;
+};
+
+
+struct mc_drv_rsp_nqconnect_payload_t {
+	/* empty; */
+};
+
+struct mc_drv_rsp_nqconnect_t {
+	struct mc_drv_response_header_t header;
+	struct mc_drv_rsp_nqconnect_payload_t payload;
+};
+
+
+/*****************************************************************************/
+union mc_drv_command_t {
+	struct mc_drv_command_header_t		header;
+	struct mc_drv_cmd_open_device_t		mc_drv_cmd_open_device;
+	struct mc_drv_cmd_close_device_t	mc_drv_cmd_close_device;
+	struct mc_drv_cmd_open_session_t	mc_drv_cmd_open_session;
+	struct mc_drv_cmd_close_session_t	mc_drv_cmd_close_session;
+	struct mc_drv_cmd_nqconnect_t		mc_drv_cmd_nqconnect;
+	struct mc_drv_cmd_notify_t		mc_drv_cmd_notify;
+	struct mc_drv_cmd_map_bulk_mem_t	mc_drv_cmd_map_bulk_mem;
+	struct mc_drv_cmd_unmap_bulk_mem_t	mc_drv_cmd_unmap_bulk_mem;
+};
+
+union mc_drv_response_t {
+	struct mc_drv_response_header_t		header;
+	struct mc_drv_rsp_open_device_t		mc_drv_rsp_open_device;
+	struct mc_drv_rsp_close_device_t	mc_drv_rsp_close_device;
+	struct mc_drv_rsp_open_session_t	mc_drv_rsp_open_session;
+	struct mc_drv_rsp_close_session_t	mc_drv_rsp_close_session;
+	struct mc_drv_rsp_nqconnect_t		mc_drv_rsp_nqconnect;
+	struct mc_drv_rsp_notify_t		mc_drv_rsp_notify;
+	struct mc_drv_rsp_map_bulk_mem_t	mc_drv_rsp_map_bulk_mem;
+	struct mc_drv_rsp_unmap_bulk_mem_t	mc_drv_rsp_unmap_bulk_mem;
+};
+
+#endif /* MCDAEMON_H_ */
+
+/** @} */
diff --git a/drivers/gud/mobicore_kernelapi/session.c b/drivers/gud/mobicore_kernelapi/session.c
new file mode 100644
index 0000000..e62b4b3
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/session.c
@@ -0,0 +1,202 @@
+/** @addtogroup MCD_IMPL_LIB
+ * @{
+ * @file
+ * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/types.h>
+#include <linux/slab.h>
+#include "mc_kernel_api.h"
+#include "public/mobicore_driver_api.h"
+
+#include "session.h"
+
+/*****************************************************************************/
+struct bulk_buffer_descriptor *bulk_buffer_descriptor_create(
+	void		*virt_addr,
+	uint32_t	len,
+	uint32_t	handle,
+	void		*phys_addr_wsm_l2
+) {
+	struct bulk_buffer_descriptor *desc =
+		kzalloc(sizeof(struct bulk_buffer_descriptor), GFP_KERNEL);
+	desc->virt_addr = virt_addr;
+	desc->len = len;
+	desc->handle = handle;
+	desc->phys_addr_wsm_l2 = phys_addr_wsm_l2;
+	return desc;
+}
+
+/*****************************************************************************/
+struct session *session_create(
+	uint32_t	session_id,
+	void		*instance,
+	struct connection *connection
+) {
+	struct session *session =
+			kzalloc(sizeof(struct session), GFP_KERNEL);
+	session->session_id = session_id;
+	session->instance = instance;
+	session->notification_connection = connection;
+
+	session->session_info.last_error = SESSION_ERR_NO;
+	session->session_info.state = SESSION_STATE_INITIAL;
+
+	INIT_LIST_HEAD(&(session->bulk_buffer_descriptors));
+	return session;
+}
+
+
+/*****************************************************************************/
+void session_cleanup(
+	struct session *session
+) {
+	struct bulk_buffer_descriptor  *bulk_buf_descr;
+	struct list_head *pos, *q;
+
+	/* Unmap still mapped buffers */
+	list_for_each_safe(pos, q, &session->bulk_buffer_descriptors) {
+		bulk_buf_descr =
+			list_entry(pos, struct bulk_buffer_descriptor, list);
+
+		MCDRV_DBG_VERBOSE("Physical Address of L2 Table = 0x%X, "
+				"handle= %d",
+				(unsigned int)bulk_buf_descr->phys_addr_wsm_l2,
+				bulk_buf_descr->handle);
+
+		/* ignore any error, as we cannot do anything in this case. */
+		int ret = mobicore_unmap_vmem(session->instance,
+						bulk_buf_descr->handle);
+		if (ret != 0)
+			MCDRV_DBG_ERROR("mobicore_unmap_vmem failed: %d", ret);
+
+		list_del(pos);
+		kfree(bulk_buf_descr);
+	}
+
+	/* Finally delete notification connection */
+	connection_cleanup(session->notification_connection);
+	kfree(session);
+}
+
+
+/*****************************************************************************/
+void session_set_error_info(
+	struct session *session,
+	int32_t   err
+) {
+	session->session_info.last_error = err;
+}
+
+
+/*****************************************************************************/
+int32_t session_get_last_err(
+	struct session *session
+) {
+	return session->session_info.last_error;
+}
+
+
+/*****************************************************************************/
+struct bulk_buffer_descriptor *session_add_bulk_buf(
+	struct session	*session,
+	void		*buf,
+	uint32_t	len
+) {
+	struct bulk_buffer_descriptor *bulk_buf_descr = NULL;
+	struct bulk_buffer_descriptor  *tmp;
+	struct list_head *pos;
+
+	/* Search bulk buffer descriptors for existing vAddr
+	   At the moment a virtual address can only be added one time */
+	list_for_each(pos, &session->bulk_buffer_descriptors) {
+		tmp = list_entry(pos, struct bulk_buffer_descriptor, list);
+		if (tmp->virt_addr == buf)
+			return NULL;
+	}
+
+	do {
+		/* Prepare the interface structure for memory registration in
+		   Kernel Module */
+		void	*l2_table_phys;
+		uint32_t  handle;
+
+		int ret = mobicore_map_vmem(session->instance,
+					buf,
+					len,
+					&handle,
+					&l2_table_phys);
+
+		if (ret != 0) {
+			MCDRV_DBG_ERROR("mobicore_map_vmem failed, ret=%d",
+					ret);
+			break;
+		}
+
+		MCDRV_DBG_VERBOSE("Physical Address of L2 Table = 0x%X, "
+				"handle=%d",
+				(unsigned int)l2_table_phys,
+				handle);
+
+		/* Create new descriptor */
+		bulk_buf_descr = bulk_buffer_descriptor_create(
+							buf,
+							len,
+							handle,
+							l2_table_phys);
+
+		/* Add to vector of descriptors */
+		list_add_tail(&(bulk_buf_descr->list),
+			&(session->bulk_buffer_descriptors));
+	} while (0);
+
+	return bulk_buf_descr;
+}
+
+
+/*****************************************************************************/
+bool session_remove_bulk_buf(
+	struct session *session,
+	void	*virt_addr
+) {
+	bool ret = true;
+	struct bulk_buffer_descriptor  *bulk_buf_descr = NULL;
+	struct bulk_buffer_descriptor  *tmp;
+	struct list_head *pos, *q;
+
+	MCDRV_DBG_VERBOSE("Virtual Address = 0x%X", (unsigned int) virt_addr);
+
+	/* Search and remove bulk buffer descriptor */
+	list_for_each_safe(pos, q, &session->bulk_buffer_descriptors) {
+		tmp = list_entry(pos, struct bulk_buffer_descriptor, list);
+		if (tmp->virt_addr == virt_addr) {
+			bulk_buf_descr = tmp;
+			list_del(pos);
+			break;
+		}
+	}
+
+	if (bulk_buf_descr == NULL) {
+		MCDRV_DBG_ERROR("Virtual Address not found");
+		ret = false;
+	} else {
+		MCDRV_DBG_VERBOSE("WsmL2 phys=0x%X, handle=%d",
+			(unsigned int)bulk_buf_descr->phys_addr_wsm_l2,
+			bulk_buf_descr->handle);
+
+		/* ignore any error, as we cannot do anything */
+		int ret = mobicore_unmap_vmem(session->instance,
+						bulk_buf_descr->handle);
+		if (ret != 0)
+			MCDRV_DBG_ERROR("mobicore_unmap_vmem failed: %d", ret);
+
+		kfree(bulk_buf_descr);
+	}
+
+	return ret;
+}
+
+/** @} */
diff --git a/drivers/gud/mobicore_kernelapi/session.h b/drivers/gud/mobicore_kernelapi/session.h
new file mode 100644
index 0000000..9a53740
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/session.h
@@ -0,0 +1,136 @@
+/** @addtogroup MCD_IMPL_LIB
+ * @{
+ * @file
+ * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef SESSION_H_
+#define SESSION_H_
+
+#include "common.h"
+
+#include <linux/list.h>
+#include "connection.h"
+
+
+struct bulk_buffer_descriptor {
+	void		*virt_addr;/**< The virtual address of the Bulk buffer*/
+	uint32_t	len;	  /**< Length of the Bulk buffer*/
+	uint32_t	handle;
+	void		*phys_addr_wsm_l2; /**< The physical address of the
+				L2 table of the Bulk buffer*/
+	struct list_head list; /**< The list param for using the kernel lists*/
+};
+
+struct bulk_buffer_descriptor *bulk_buffer_descriptor_create(
+	void	*virt_addr,
+	uint32_t  len,
+	uint32_t  handle,
+	void	*phys_addr_wsm_l2
+);
+
+/** Session states.
+ * At the moment not used !!.
+ */
+enum session_state {
+	SESSION_STATE_INITIAL,
+	SESSION_STATE_OPEN,
+	SESSION_STATE_TRUSTLET_DEAD
+};
+
+#define SESSION_ERR_NO	  0 /**< No session error */
+
+/** Session information structure.
+ * The information structure is used to hold the state of the session, which
+ * will limit further actions for the session.
+ * Also the last error code will be stored till it's read.
+ */
+struct session_information {
+	enum session_state state;	   /**< Session state */
+	int32_t		last_error;	 /**< Last error of session */
+};
+
+
+struct session {
+	struct mc_instance		*instance;
+	/**< Descriptors of additional bulk buffer of a session */
+	struct list_head	bulk_buffer_descriptors;
+	/**< Informations about session */
+	struct session_information	 session_info;
+
+	uint32_t		 session_id;
+	struct connection	 *notification_connection;
+
+	/**< The list param for using the kernel lists*/
+	struct list_head list;
+};
+
+struct session *session_create(
+	uint32_t	 session_id,
+	void		 *instance,
+	struct connection *connection
+);
+
+void session_cleanup(
+	struct session *session
+);
+
+/**
+  * Add address information of additional bulk buffer memory to session and
+  * register virtual memory in kernel module.
+  *
+  * @attention The virtual address can only be added one time. If the virtual
+  * address already exist, NULL is returned.
+  *
+  * @param buf The virtual address of bulk buffer.
+  * @param len Length of bulk buffer.
+  *
+  * @return On success the actual Bulk buffer descriptor with all address
+  * information is retured, NULL if an error occurs.
+  */
+struct bulk_buffer_descriptor *session_add_bulk_buf(
+	struct session *session,
+	void	*buf,
+	uint32_t  len
+);
+
+/**
+  * Remove address information of additional bulk buffer memory from session and
+  * unregister virtual memory in kernel module
+  *
+  * @param buf The virtual address of the bulk buffer.
+  *
+  * @return true on success.
+  */
+bool session_remove_bulk_buf(
+	struct session *session,
+	void  *buf
+);
+
+/**
+  * Set additional error information of the last error that occured.
+  *
+  * @param errorCode The actual error.
+  */
+void session_set_error_info(
+	struct session *session,
+	int32_t err
+);
+
+/**
+  * Get additional error information of the last error that occured.
+  *
+  * @attention After request the information is set to SESSION_ERR_NO.
+  *
+  * @return Last stored error code or SESSION_ERR_NO.
+  */
+int32_t session_get_last_err(
+	struct session *session
+);
+
+#endif /* SESSION_H_ */
+
+/** @} */
diff --git a/drivers/gud/mobicore_kernelapi/wsm.h b/drivers/gud/mobicore_kernelapi/wsm.h
new file mode 100644
index 0000000..6877c53
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/wsm.h
@@ -0,0 +1,35 @@
+/** @addtogroup MCD_MCDIMPL_DAEMON_SRV
+ * @{
+ * @file
+ *
+ * World shared memory definitions.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef WSM_H_
+#define WSM_H_
+
+#include "common.h"
+#include <linux/list.h>
+
+struct wsm {
+	void *virt_addr;
+	uint32_t len;
+	uint32_t handle;
+	void *phys_addr;
+	struct list_head list;
+};
+
+struct wsm *wsm_create(
+	void	*virt_addr,
+	uint32_t  len,
+	uint32_t  handle,
+	void	*phys_addr /*= NULL this may be unknown, so is can be omitted.*/
+);
+#endif /* WSM_H_ */
+
+/** @} */
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 8deedc1..b050db2 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -838,6 +838,36 @@
 	  This driver can also be built as a module.  If so, the module
 	  will be called ntc-thermistor.
 
+config SENSORS_MSM_ADC
+	tristate "MSM ADC Driver for current measurement"
+	depends on ARCH_MSM7X30 || ARCH_MSM8X60 || ARCH_FSM9XXX || ARCH_MSM7X27A
+	default n
+	help
+	  Provides interface for measuring the ADC's on AMUX channels of XOADC,
+	  MPP's and the XOTHERM on pmic8058 for msm8x60 and provides post processing
+	  of the channel for the ADC Raw Data. For reading LTC and EPM ADC channels
+	  say yes here to include support for measuring current in real-time
+	  from various power-rails on the Fluid board.  The ADC circuit
+	  internally uses an array of LTC2499 and EPM ADCs in a differential
+	  configuration to provide a flat set of channels that can be addressed.
+
+config SENSORS_PM8XXX_ADC
+	tristate "Support for Qualcomm PM8XXX ADC"
+	depends on MFD_PM8XXX
+	help
+	  This is the ADC arbiter driver for Qualcomm PM8XXX Chip.
+
+	  The driver supports reading the HKADC, XOADC and support to set and receive
+	  temperature threshold notifications using the Battery temperature module.
+
+config SENSORS_EPM_ADC
+	tristate "EPM ADC Driver for power measurement"
+	depends on I2C && SPI_MASTER
+	default n
+	help
+	  Provides interface for measuring the current on specific power rails
+	  through the channels on ADC1158 ADC
+
 config SENSORS_PC87360
 	tristate "National Semiconductor PC87360 family"
 	depends on !PPC
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 6d3f11f..228c4e9 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -126,6 +126,10 @@
 obj-$(CONFIG_SENSORS_W83L786NG)	+= w83l786ng.o
 obj-$(CONFIG_SENSORS_WM831X)	+= wm831x-hwmon.o
 obj-$(CONFIG_SENSORS_WM8350)	+= wm8350-hwmon.o
+obj-$(CONFIG_SENSORS_WPCE775X)	+= wpce775x.o
+obj-$(CONFIG_SENSORS_MSM_ADC)	+= msm_adc.o m_adcproc.o
+obj-$(CONFIG_SENSORS_PM8XXX_ADC)	+= pm8xxx-adc.o pm8xxx-adc-scale.o
+obj-$(CONFIG_SENSORS_EPM_ADC)	+= epm_adc.o
 
 obj-$(CONFIG_PMBUS)		+= pmbus/
 
diff --git a/drivers/hwmon/epm_adc.c b/drivers/hwmon/epm_adc.c
new file mode 100644
index 0000000..a8b99b9
--- /dev/null
+++ b/drivers/hwmon/epm_adc.c
@@ -0,0 +1,917 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/hwmon.h>
+#include <linux/delay.h>
+#include <linux/epm_adc.h>
+#include <linux/uaccess.h>
+#include <linux/spi/spi.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+
+#define EPM_ADC_DRIVER_NAME		"epm_adc"
+#define EPM_ADC_MAX_FNAME		20
+#define EPM_ADC_CONVERSION_DELAY	100 /* milliseconds */
+/* Command Bits */
+#define EPM_ADC_ADS_SPI_BITS_PER_WORD	8
+#define EPM_ADC_ADS_DATA_READ_CMD	(0x1 << 5)
+#define EPM_ADC_ADS_REG_READ_CMD	(0x2 << 5)
+#define EPM_ADC_ADS_REG_WRITE_CMD	(0x3 << 5)
+#define EPM_ADC_ADS_PULSE_CONVERT_CMD	(0x4 << 5)
+#define EPM_ADC_ADS_MULTIPLE_REG_ACCESS	(0x1 << 4)
+/* Register map */
+#define EPM_ADC_ADS_CONFIG0_REG_ADDR	0x0
+#define EPM_ADC_ADS_CONFIG1_REG_ADDR	0x1
+#define EPM_ADC_ADS_MUXSG0_REG_ADDR	0x4
+#define EPM_ADC_ADS_MUXSG1_REG_ADDR	0x5
+/* Register map default data */
+#define EPM_ADC_ADS_REG0_DEFAULT	0x2
+#define EPM_ADC_ADS_REG1_DEFAULT	0x52
+#define EPM_ADC_ADS_CHANNEL_DATA_CHID	0x1f
+/* Channel ID */
+#define EPM_ADC_ADS_CHANNEL_OFFSET	0x18
+#define EPM_ADC_ADS_CHANNEL_VCC		0x1a
+#define EPM_ADC_ADS_CHANNEL_TEMP	0x1b
+#define EPM_ADC_ADS_CHANNEL_GAIN	0x1c
+#define EPM_ADC_ADS_CHANNEL_REF		0x1d
+/* Scaling data co-efficients */
+#define EPM_ADC_SCALE_MILLI		1000
+#define EPM_ADC_SCALE_CODE_VOLTS	3072
+#define EPM_ADC_SCALE_CODE_GAIN		30720
+#define EPM_ADC_TEMP_SENSOR_COEFF	394
+#define EPM_ADC_TEMP_TO_DEGC_COEFF	168000
+#define EPM_ADC_CHANNEL_AIN_OFFSET	8
+#define EPM_ADC_MAX_NEGATIVE_SCALE_CODE	0x8000
+#define EPM_ADC_NEG_LSB_CODE		0xffff
+#define EPM_ADC_VREF_CODE		0x7800
+#define EPM_ADC_MILLI_VOLTS_SOURCE	4750
+#define EPM_ADC_SCALE_FACTOR		64
+#define GPIO_EPM_GLOBAL_ENABLE		86
+#define EPM_ADC_CONVERSION_TIME_MIN	50000
+#define EPM_ADC_CONVERSION_TIME_MAX	51000
+
+struct epm_adc_drv {
+	struct platform_device		*pdev;
+	struct device			*hwmon;
+	struct sensor_device_attribute	*sens_attr;
+	char				**fnames;
+	struct spi_device		*epm_spi_client;
+	struct mutex			conv_lock;
+	uint32_t			bus_id;
+	struct miscdevice		misc;
+};
+
+static struct epm_adc_drv *epm_adc_drv;
+static struct i2c_board_info *epm_i2c_info;
+static bool epm_adc_first_request;
+static int epm_gpio_expander_base_addr;
+static bool epm_adc_expander_register;
+
+#define GPIO_EPM_EXPANDER_IO0	epm_gpio_expander_base_addr
+#define GPIO_PWR_MON_ENABLE	(GPIO_EPM_EXPANDER_IO0 + 1)
+#define GPIO_ADC1_PWDN_N	(GPIO_PWR_MON_ENABLE + 1)
+#define GPIO_PWR_MON_RESET_N	(GPIO_ADC1_PWDN_N + 1)
+#define GPIO_EPM_SPI_ADC1_CS_N	(GPIO_PWR_MON_RESET_N + 1)
+#define GPIO_PWR_MON_START	(GPIO_EPM_SPI_ADC1_CS_N + 1)
+#define GPIO_ADC1_DRDY_N	(GPIO_PWR_MON_START + 1)
+#define GPIO_ADC2_PWDN_N	(GPIO_ADC1_DRDY_N + 1)
+#define GPIO_EPM_SPI_ADC2_CS_N	(GPIO_ADC2_PWDN_N + 1)
+#define GPIO_ADC2_DRDY_N	(GPIO_EPM_SPI_ADC2_CS_N + 1)
+
+static int epm_adc_i2c_expander_register(void)
+{
+	int rc = 0;
+	static struct i2c_adapter *i2c_adap;
+	static struct i2c_client *epm_i2c_client;
+
+	rc = gpio_request(GPIO_EPM_GLOBAL_ENABLE, "EPM_GLOBAL_EN");
+	if (!rc) {
+		gpio_direction_output(GPIO_EPM_GLOBAL_ENABLE, 1);
+	} else {
+		pr_err("%s: Configure EPM_GLOBAL_EN Failed\n", __func__);
+		return rc;
+	}
+
+	usleep_range(EPM_ADC_CONVERSION_TIME_MIN,
+			EPM_ADC_CONVERSION_TIME_MAX);
+
+	i2c_adap = i2c_get_adapter(epm_adc_drv->bus_id);
+	if (i2c_adap == NULL) {
+		pr_err("%s: i2c_get_adapter() failed\n", __func__);
+		return -EINVAL;
+	}
+
+	usleep_range(EPM_ADC_CONVERSION_TIME_MIN,
+			EPM_ADC_CONVERSION_TIME_MAX);
+
+	epm_i2c_client = i2c_new_device(i2c_adap, epm_i2c_info);
+	if (IS_ERR(epm_i2c_client)) {
+		pr_err("Error with i2c epm device register\n");
+		return -ENODEV;
+	}
+
+	epm_adc_first_request = false;
+
+	return 0;
+}
+
+static int epm_adc_gpio_configure_expander_enable(void)
+{
+	int rc = 0;
+
+	if (epm_adc_first_request) {
+		rc = gpio_request(GPIO_EPM_GLOBAL_ENABLE, "EPM_GLOBAL_EN");
+		if (!rc) {
+			gpio_direction_output(GPIO_EPM_GLOBAL_ENABLE, 1);
+		} else {
+			pr_err("%s: Configure EPM_GLOBAL_EN Failed\n",
+								__func__);
+			return rc;
+		}
+	} else {
+		epm_adc_first_request = true;
+	}
+
+	usleep_range(EPM_ADC_CONVERSION_TIME_MIN,
+			EPM_ADC_CONVERSION_TIME_MAX);
+
+	rc = gpio_request(GPIO_PWR_MON_ENABLE, "GPIO_PWR_MON_ENABLE");
+	if (!rc) {
+		rc = gpio_direction_output(GPIO_PWR_MON_ENABLE, 1);
+		if (rc) {
+			pr_err("%s: Set GPIO_PWR_MON_ENABLE failed\n",
+					__func__);
+			return rc;
+		}
+	} else {
+		pr_err("%s: gpio_request GPIO_PWR_MON_ENABLE failed\n",
+				__func__);
+		return rc;
+	}
+
+	rc = gpio_request(GPIO_ADC1_PWDN_N, "GPIO_ADC1_PWDN_N");
+	if (!rc) {
+		rc = gpio_direction_output(GPIO_ADC1_PWDN_N, 1);
+		if (rc) {
+			pr_err("%s: Set GPIO_ADC1_PWDN_N failed\n", __func__);
+			return rc;
+		}
+	} else {
+		pr_err("%s: gpio_request GPIO_ADC1_PWDN_N failed\n", __func__);
+		return rc;
+	}
+
+	rc = gpio_request(GPIO_ADC2_PWDN_N, "GPIO_ADC2_PWDN_N");
+	if (!rc) {
+		rc = gpio_direction_output(GPIO_ADC2_PWDN_N, 1);
+		if (rc) {
+			pr_err("%s: Set GPIO_ADC2_PWDN_N failed\n",
+					__func__);
+			return rc;
+		}
+	} else {
+		pr_err("%s: gpio_request GPIO_ADC2_PWDN_N failed\n",
+				__func__);
+		return rc;
+	}
+
+	rc = gpio_request(GPIO_EPM_SPI_ADC1_CS_N, "GPIO_EPM_SPI_ADC1_CS_N");
+	if (!rc) {
+		rc = gpio_direction_output(GPIO_EPM_SPI_ADC1_CS_N, 1);
+		if (rc) {
+			pr_err("%s:Set GPIO_EPM_SPI_ADC1_CS_N failed\n",
+					__func__);
+			return rc;
+		}
+	} else {
+		pr_err("%s: gpio_request GPIO_EPM_SPI_ADC1_CS_N failed\n",
+				__func__);
+		return rc;
+	}
+
+	rc = gpio_request(GPIO_EPM_SPI_ADC2_CS_N,
+			"GPIO_EPM_SPI_ADC2_CS_N");
+	if (!rc) {
+		rc = gpio_direction_output(GPIO_EPM_SPI_ADC2_CS_N, 1);
+		if (rc) {
+			pr_err("%s: Set GPIO_EPM_SPI_ADC2_CS_N "
+					"failed\n", __func__);
+			return rc;
+		}
+	} else {
+		pr_err("%s: gpio_request GPIO_EPM_SPI_ADC2_CS_N "
+				"failed\n", __func__);
+		return rc;
+	}
+
+	rc = gpio_direction_output(GPIO_EPM_SPI_ADC1_CS_N, 0);
+	if (rc) {
+		pr_err("%s:Reset GPIO_EPM_SPI_ADC1_CS_N failed\n", __func__);
+		return rc;
+	}
+
+	rc = gpio_direction_output(GPIO_EPM_SPI_ADC1_CS_N, 1);
+	if (rc) {
+		pr_err("%s: Set GPIO_EPM_SPI_ADC1_CS_N failed\n", __func__);
+		return rc;
+	}
+
+	rc = gpio_request(GPIO_PWR_MON_START, "GPIO_PWR_MON_START");
+	if (!rc) {
+		rc = gpio_direction_output(GPIO_PWR_MON_START, 0);
+		if (rc) {
+			pr_err("%s: Reset GPIO_PWR_MON_START failed\n",
+					__func__);
+			return rc;
+		}
+	} else {
+		pr_err("%s: gpio_request GPIO_PWR_MON_START failed\n",
+				__func__);
+		return rc;
+	}
+
+	rc = gpio_request(GPIO_PWR_MON_RESET_N, "GPIO_PWR_MON_RESET_N");
+	if (!rc) {
+		rc = gpio_direction_output(GPIO_PWR_MON_RESET_N, 0);
+		if (rc) {
+			pr_err("%s: Reset GPIO_PWR_MON_RESET_N failed\n",
+					__func__);
+			return rc;
+		}
+	} else {
+		pr_err("%s: gpio_request GPIO_PWR_MON_RESET_N failed\n",
+				__func__);
+		return rc;
+	}
+
+	rc = gpio_direction_output(GPIO_PWR_MON_RESET_N, 1);
+	if (rc) {
+		pr_err("%s: Set GPIO_PWR_MON_RESET_N failed\n", __func__);
+		return rc;
+	}
+
+	rc = gpio_direction_output(GPIO_EPM_SPI_ADC1_CS_N, 0);
+	if (rc) {
+		pr_err("%s:Reset GPIO_EPM_SPI_ADC1_CS_N failed\n", __func__);
+		return rc;
+	}
+	return rc;
+}
+
+static int epm_adc_gpio_configure_expander_disable(void)
+{
+	int rc = 0;
+	gpio_free(GPIO_PWR_MON_ENABLE);
+	gpio_free(GPIO_ADC1_PWDN_N);
+	gpio_free(GPIO_ADC2_PWDN_N);
+	gpio_free(GPIO_EPM_SPI_ADC1_CS_N);
+	gpio_free(GPIO_EPM_SPI_ADC2_CS_N);
+	gpio_free(GPIO_PWR_MON_START);
+	gpio_free(GPIO_PWR_MON_RESET_N);
+	rc = gpio_direction_output(GPIO_EPM_GLOBAL_ENABLE, 0);
+	if (rc)
+		pr_debug("%s: Disable EPM_GLOBAL_EN Failed\n", __func__);
+	gpio_free(GPIO_EPM_GLOBAL_ENABLE);
+	return rc;
+}
+
+static int epm_adc_spi_chip_select(int32_t id)
+{
+	int rc = 0;
+	if (id == 0) {
+		rc = gpio_direction_output(GPIO_EPM_SPI_ADC2_CS_N, 1);
+		if (rc) {
+			pr_err("%s:Disable SPI_ADC2_CS failed",
+					__func__);
+			return rc;
+		}
+
+		rc = gpio_direction_output(GPIO_EPM_SPI_ADC1_CS_N, 0);
+		if (rc) {
+			pr_err("%s:Enable SPI_ADC1_CS failed", __func__);
+			return rc;
+		}
+	} else if (id == 1) {
+		rc = gpio_direction_output(GPIO_EPM_SPI_ADC1_CS_N, 1);
+		if (rc) {
+			pr_err("%s:Disable SPI_ADC1_CS failed", __func__);
+			return rc;
+		}
+		rc = gpio_direction_output(GPIO_EPM_SPI_ADC2_CS_N, 0);
+		if (rc) {
+			pr_err("%s:Enable SPI_ADC2_CS failed", __func__);
+			return rc;
+		}
+	} else {
+		rc = -EFAULT;
+	}
+	return rc;
+}
+
+static int epm_adc_ads_spi_write(struct epm_adc_drv *epm_adc,
+		uint8_t addr, uint8_t val)
+{
+	struct spi_message m;
+	struct spi_transfer t;
+	char tx_buf[2];
+	int rc = 0;
+
+	spi_setup(epm_adc->epm_spi_client);
+
+	memset(&t, 0, sizeof t);
+	memset(tx_buf, 0, sizeof tx_buf);
+	t.tx_buf = tx_buf;
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+
+	tx_buf[0] = EPM_ADC_ADS_REG_WRITE_CMD | addr;
+	tx_buf[1] = val;
+
+	t.len = sizeof(tx_buf);
+	t.bits_per_word = EPM_ADC_ADS_SPI_BITS_PER_WORD;
+
+	rc = spi_sync(epm_adc->epm_spi_client, &m);
+
+	return rc;
+}
+
+static int epm_adc_init_ads(struct epm_adc_drv *epm_adc)
+{
+	int rc = 0;
+
+	rc = epm_adc_ads_spi_write(epm_adc, EPM_ADC_ADS_CONFIG0_REG_ADDR,
+						EPM_ADC_ADS_REG0_DEFAULT);
+	if (rc)
+		return rc;
+
+	rc = epm_adc_ads_spi_write(epm_adc, EPM_ADC_ADS_CONFIG1_REG_ADDR,
+						EPM_ADC_ADS_REG1_DEFAULT);
+	if (rc)
+		return rc;
+	return rc;
+}
+
+static int epm_adc_ads_pulse_convert(struct epm_adc_drv *epm_adc)
+{
+	struct spi_message m;
+	struct spi_transfer t;
+	char tx_buf[1];
+	int rc = 0;
+
+	spi_setup(epm_adc->epm_spi_client);
+
+	memset(&t, 0, sizeof t);
+	memset(tx_buf, 0, sizeof tx_buf);
+	t.tx_buf = tx_buf;
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+
+	tx_buf[0] = EPM_ADC_ADS_PULSE_CONVERT_CMD;
+	t.len = sizeof(tx_buf);
+	t.bits_per_word = EPM_ADC_ADS_SPI_BITS_PER_WORD;
+
+	rc = spi_sync(epm_adc->epm_spi_client, &m);
+
+	return rc;
+}
+
+static int epm_adc_ads_read_data(struct epm_adc_drv *epm_adc, char *adc_data)
+{
+	struct spi_message m;
+	struct spi_transfer t;
+	char tx_buf[4], rx_buf[4];
+	int rc = 0;
+
+	spi_setup(epm_adc->epm_spi_client);
+
+	memset(&t, 0, sizeof t);
+	memset(tx_buf, 0, sizeof tx_buf);
+	memset(rx_buf, 0, sizeof tx_buf);
+	t.tx_buf = tx_buf;
+	t.rx_buf = rx_buf;
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+
+	tx_buf[0] = EPM_ADC_ADS_DATA_READ_CMD |
+			EPM_ADC_ADS_MULTIPLE_REG_ACCESS;
+
+	t.len = sizeof(tx_buf);
+	t.bits_per_word = EPM_ADC_ADS_SPI_BITS_PER_WORD;
+
+	rc = spi_sync(epm_adc->epm_spi_client, &m);
+	if (rc)
+		return rc;
+
+	rc = spi_sync(epm_adc->epm_spi_client, &m);
+	if (rc)
+		return rc;
+
+	rc = spi_sync(epm_adc->epm_spi_client, &m);
+	if (rc)
+		return rc;
+
+	adc_data[0] = rx_buf[1];
+	adc_data[1] = rx_buf[2];
+	adc_data[2] = rx_buf[3];
+
+	return rc;
+}
+
+static int epm_adc_hw_init(struct epm_adc_drv *epm_adc)
+{
+	int rc = 0;
+
+	mutex_lock(&epm_adc->conv_lock);
+	rc = epm_adc_gpio_configure_expander_enable();
+	if (rc != 0) {
+		pr_err("epm gpio configure expander failed, rc = %d\n", rc);
+		goto epm_adc_hw_init_err;
+	}
+	rc = epm_adc_init_ads(epm_adc);
+	if (rc) {
+		pr_err("epm_adc_init_ads failed, rc=%d\n", rc);
+		goto epm_adc_hw_init_err;
+	}
+
+epm_adc_hw_init_err:
+	mutex_unlock(&epm_adc->conv_lock);
+	return rc;
+}
+
+static int epm_adc_hw_deinit(struct epm_adc_drv *epm_adc)
+{
+	int rc = 0;
+
+	mutex_lock(&epm_adc->conv_lock);
+	rc = epm_adc_gpio_configure_expander_disable();
+	if (rc != 0) {
+		pr_err("epm gpio configure expander disable failed,"
+			" rc = %d\n", rc);
+		goto epm_adc_hw_deinit_err;
+	}
+
+epm_adc_hw_deinit_err:
+	mutex_unlock(&epm_adc->conv_lock);
+	return rc;
+}
+
+static int epm_adc_ads_scale_result(struct epm_adc_drv *epm_adc,
+		uint8_t *adc_raw_data, struct epm_chan_request *conv)
+{
+	uint32_t channel_num;
+	int16_t  sign_bit;
+	struct epm_adc_platform_data *pdata = epm_adc->pdev->dev.platform_data;
+	uint32_t chan_idx = (conv->device_idx * pdata->chan_per_adc) +
+					conv->channel_idx;
+	int32_t *adc_scaled_data = &conv->physical;
+
+	/* Get the channel number */
+	channel_num = (adc_raw_data[0] & EPM_ADC_ADS_CHANNEL_DATA_CHID);
+	sign_bit    = 1;
+	/* This is the 16-bit raw data */
+	*adc_scaled_data = ((adc_raw_data[1] << 8) | adc_raw_data[2]);
+	/* Obtain the internal system reading */
+	if (channel_num == EPM_ADC_ADS_CHANNEL_VCC) {
+		*adc_scaled_data *= EPM_ADC_SCALE_MILLI;
+		*adc_scaled_data /= EPM_ADC_SCALE_CODE_VOLTS;
+	} else if (channel_num == EPM_ADC_ADS_CHANNEL_GAIN) {
+		*adc_scaled_data /= EPM_ADC_SCALE_CODE_GAIN;
+	} else if (channel_num == EPM_ADC_ADS_CHANNEL_REF) {
+		*adc_scaled_data *= EPM_ADC_SCALE_MILLI;
+		*adc_scaled_data /= EPM_ADC_SCALE_CODE_VOLTS;
+	} else if (channel_num == EPM_ADC_ADS_CHANNEL_TEMP) {
+		/* Convert Code to micro-volts */
+		/* Use this formula to get the temperature reading */
+		*adc_scaled_data -= EPM_ADC_TEMP_TO_DEGC_COEFF;
+		*adc_scaled_data /= EPM_ADC_TEMP_SENSOR_COEFF;
+	} else if (channel_num == EPM_ADC_ADS_CHANNEL_OFFSET) {
+		/* The offset should be zero */
+		pr_debug("%s: ADC Channel Offset\n", __func__);
+		return -EFAULT;
+	} else {
+		channel_num -= EPM_ADC_CHANNEL_AIN_OFFSET;
+		/*
+		 * Conversion for the adc channels.
+		 * mvVRef is in milli-volts and resistorValue is in micro-ohms.
+		 * Hence, I = V/R gives us current in kilo-amps.
+		 */
+		if (*adc_scaled_data & EPM_ADC_MAX_NEGATIVE_SCALE_CODE) {
+			sign_bit = -1;
+			*adc_scaled_data = (~*adc_scaled_data
+				& EPM_ADC_NEG_LSB_CODE);
+		}
+		if (*adc_scaled_data != 0) {
+			*adc_scaled_data *= EPM_ADC_SCALE_FACTOR;
+			 /* Device is calibrated for 1LSB = VREF/7800h.*/
+			*adc_scaled_data *= EPM_ADC_MILLI_VOLTS_SOURCE;
+			*adc_scaled_data /= EPM_ADC_VREF_CODE;
+			 /* Data will now be in micro-volts.*/
+			*adc_scaled_data *= EPM_ADC_SCALE_MILLI;
+			 /* Divide by amplifier gain value.*/
+			*adc_scaled_data /= pdata->channel[chan_idx].gain;
+			 /* Data will now be in nano-volts.*/
+			*adc_scaled_data /= EPM_ADC_SCALE_FACTOR;
+			*adc_scaled_data *= EPM_ADC_SCALE_MILLI;
+			 /* Data is now in micro-amps.*/
+			*adc_scaled_data /=
+				pdata->channel[chan_idx].resistorValue;
+			 /* Set the sign bit for lekage current. */
+			*adc_scaled_data *= sign_bit;
+		}
+	}
+	return 0;
+}
+
+static int epm_adc_blocking_conversion(struct epm_adc_drv *epm_adc,
+					struct epm_chan_request *conv)
+{
+	struct epm_adc_platform_data *pdata = epm_adc->pdev->dev.platform_data;
+	int32_t channel_num = 0, mux_chan_idx = 0;
+	char adc_data[3];
+	int rc = 0;
+
+	mutex_lock(&epm_adc->conv_lock);
+
+	rc = epm_adc_spi_chip_select(conv->device_idx);
+	if (rc) {
+		pr_err("epm_adc_chip_select failed, rc=%d\n", rc);
+		goto conv_err;
+	}
+
+	if (conv->channel_idx < pdata->chan_per_mux) {
+		/* Reset MUXSG1_REGISTER */
+		rc = epm_adc_ads_spi_write(epm_adc, EPM_ADC_ADS_MUXSG1_REG_ADDR,
+							0x0);
+		if (rc)
+			goto conv_err;
+
+		mux_chan_idx = 1 << conv->channel_idx;
+		/* Select Channel index in MUXSG0_REGISTER */
+		rc = epm_adc_ads_spi_write(epm_adc, EPM_ADC_ADS_MUXSG0_REG_ADDR,
+				mux_chan_idx);
+		if (rc)
+			goto conv_err;
+	} else {
+		/* Reset MUXSG0_REGISTER */
+		rc = epm_adc_ads_spi_write(epm_adc, EPM_ADC_ADS_MUXSG0_REG_ADDR,
+							0x0);
+		if (rc)
+			goto conv_err;
+
+		mux_chan_idx = 1 << (conv->channel_idx - pdata->chan_per_mux);
+		/* Select Channel index in MUXSG1_REGISTER */
+		rc = epm_adc_ads_spi_write(epm_adc, EPM_ADC_ADS_MUXSG1_REG_ADDR,
+				mux_chan_idx);
+		if (rc)
+			goto conv_err;
+	}
+
+	rc = epm_adc_ads_pulse_convert(epm_adc);
+	if (rc) {
+		pr_err("epm_adc_ads_pulse_convert failed, rc=%d\n", rc);
+		goto conv_err;
+	}
+
+	rc = epm_adc_ads_read_data(epm_adc, adc_data);
+	if (rc) {
+		pr_err("epm_adc_ads_read_data failed, rc=%d\n", rc);
+		goto conv_err;
+	}
+
+	channel_num = (adc_data[0] & EPM_ADC_ADS_CHANNEL_DATA_CHID);
+	pr_debug("ADC data Read: adc_data =%d, %d, %d\n",
+			adc_data[0], adc_data[1], adc_data[2]);
+
+	epm_adc_ads_scale_result(epm_adc, (uint8_t *)adc_data, conv);
+
+	pr_debug("channel_num(0x) = %x, scaled_data = %d\n",
+		 (channel_num - EPM_ADC_ADS_SPI_BITS_PER_WORD),
+						conv->physical);
+conv_err:
+	mutex_unlock(&epm_adc->conv_lock);
+	return rc;
+}
+
+static long epm_adc_ioctl(struct file *file, unsigned int cmd,
+						unsigned long arg)
+{
+	struct epm_adc_drv *epm_adc = epm_adc_drv;
+
+	switch (cmd) {
+	case EPM_ADC_REQUEST:
+		{
+			struct epm_chan_request conv;
+			int rc;
+
+			if (copy_from_user(&conv, (void __user *)arg,
+					sizeof(struct epm_chan_request)))
+				return -EFAULT;
+
+			rc = epm_adc_blocking_conversion(epm_adc, &conv);
+			if (rc) {
+				pr_err("Failed EPM conversion:%d\n", rc);
+				return rc;
+			}
+
+			if (copy_to_user((void __user *)arg, &conv,
+				sizeof(struct epm_chan_request)))
+				return -EFAULT;
+			break;
+		}
+	case EPM_ADC_INIT:
+		{
+			uint32_t result;
+			if (!epm_adc_expander_register) {
+				result = epm_adc_i2c_expander_register();
+				if (result) {
+					pr_err("Failed i2c register:%d\n",
+								result);
+					return result;
+				}
+				epm_adc_expander_register = true;
+			}
+
+			result = epm_adc_hw_init(epm_adc_drv);
+
+			if (copy_to_user((void __user *)arg, &result,
+						sizeof(uint32_t)))
+				return -EFAULT;
+			break;
+		}
+	case EPM_ADC_DEINIT:
+		{
+			uint32_t result;
+			result = epm_adc_hw_deinit(epm_adc_drv);
+
+			if (copy_to_user((void __user *)arg, &result,
+						sizeof(uint32_t)))
+				return -EFAULT;
+			break;
+		}
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+const struct file_operations epm_adc_fops = {
+	.unlocked_ioctl = epm_adc_ioctl,
+};
+
+static ssize_t epm_adc_show_in(struct device *dev,
+				 struct device_attribute *devattr, char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct epm_adc_drv *epm_adc = dev_get_drvdata(dev);
+	struct epm_adc_platform_data *pdata = epm_adc->pdev->dev.platform_data;
+	struct epm_chan_request conv;
+	int rc = 0;
+
+	conv.device_idx = attr->index / pdata->chan_per_adc;
+	conv.channel_idx = attr->index % pdata->chan_per_adc;
+	conv.physical = 0;
+	pr_debug("%s: device_idx=%d channel_idx=%d", __func__, conv.device_idx,
+			conv.channel_idx);
+	if (!epm_adc_expander_register) {
+		rc = epm_adc_i2c_expander_register();
+		if (rc) {
+			pr_err("I2C expander register failed:%d\n", rc);
+			return rc;
+		}
+		epm_adc_expander_register = true;
+	}
+
+	rc = epm_adc_hw_init(epm_adc);
+	if (rc) {
+		pr_err("%s: epm_adc_hw_init() failed, rc = %d",
+			__func__, rc);
+		return 0;
+	}
+
+	rc = epm_adc_blocking_conversion(epm_adc, &conv);
+	if (rc) {
+		pr_err("%s: epm_adc_blocking_conversion() failed, rc = %d\n",
+			__func__, rc);
+		return 0;
+	}
+	rc = epm_adc_hw_deinit(epm_adc);
+	if (rc) {
+		pr_err("%s: epm_adc_hw_deinit() failed, rc = %d",
+			__func__, rc);
+		return 0;
+	}
+
+	return snprintf(buf, 16, "Result: %d\n", conv.physical);
+}
+
+static struct sensor_device_attribute epm_adc_in_attr =
+	SENSOR_ATTR(NULL, S_IRUGO, epm_adc_show_in, NULL, 0);
+
+static int __devinit epm_adc_init_hwmon(struct platform_device *pdev,
+					       struct epm_adc_drv *epm_adc)
+{
+	struct epm_adc_platform_data *pdata = pdev->dev.platform_data;
+	int num_chans = pdata->num_channels, dev_idx = 0, chan_idx = 0;
+	int i = 0, rc = 0;
+	const char prefix[] = "ads", postfix[] = "_chan";
+	char tmpbuf[3];
+
+	epm_adc->fnames = devm_kzalloc(&pdev->dev,
+				num_chans * EPM_ADC_MAX_FNAME +
+				num_chans * sizeof(char *), GFP_KERNEL);
+	if (!epm_adc->fnames) {
+		dev_err(&pdev->dev, "Unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	epm_adc->sens_attr = devm_kzalloc(&pdev->dev, num_chans *
+			    sizeof(struct sensor_device_attribute), GFP_KERNEL);
+	if (!epm_adc->sens_attr) {
+		dev_err(&pdev->dev, "Unable to allocate memory\n");
+		rc = -ENOMEM;
+	}
+
+	for (i = 0; i < num_chans; i++, chan_idx++) {
+		epm_adc->fnames[i] = (char *)epm_adc->fnames +
+			(i * EPM_ADC_MAX_FNAME) + (num_chans *
+			sizeof(char *));
+		if (chan_idx == pdata->chan_per_adc) {
+			chan_idx = 0;
+			dev_idx++;
+		}
+		strlcpy(epm_adc->fnames[i], prefix, EPM_ADC_MAX_FNAME);
+		snprintf(tmpbuf, sizeof(tmpbuf), "%d", dev_idx);
+		strlcat(epm_adc->fnames[i], tmpbuf, EPM_ADC_MAX_FNAME);
+		strlcat(epm_adc->fnames[i], postfix, EPM_ADC_MAX_FNAME);
+		snprintf(tmpbuf, sizeof(tmpbuf), "%d", chan_idx);
+		strlcat(epm_adc->fnames[i], tmpbuf, EPM_ADC_MAX_FNAME);
+		epm_adc_in_attr.index = i;
+		epm_adc_in_attr.dev_attr.attr.name = epm_adc->fnames[i];
+		memcpy(&epm_adc->sens_attr[i], &epm_adc_in_attr,
+						sizeof(epm_adc_in_attr));
+		rc = device_create_file(&pdev->dev,
+				&epm_adc->sens_attr[i].dev_attr);
+		if (rc) {
+			dev_err(&pdev->dev, "device_create_file failed\n");
+			return rc;
+		}
+	}
+
+	return rc;
+}
+
+static int __devinit epm_adc_spi_probe(struct spi_device *spi)
+
+{
+	if (!epm_adc_drv)
+		return -ENODEV;
+	epm_adc_drv->epm_spi_client = spi;
+	epm_adc_drv->epm_spi_client->bits_per_word =
+				EPM_ADC_ADS_SPI_BITS_PER_WORD;
+
+	return 0;
+}
+
+static int __devexit epm_adc_spi_remove(struct spi_device *spi)
+{
+	epm_adc_drv->epm_spi_client = NULL;
+	return 0;
+}
+
+static struct spi_driver epm_spi_driver = {
+	.probe = epm_adc_spi_probe,
+	.remove = __devexit_p(epm_adc_spi_remove),
+	.driver = {
+		.name = EPM_ADC_DRIVER_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __devinit epm_adc_probe(struct platform_device *pdev)
+{
+	struct epm_adc_drv *epm_adc;
+	struct epm_adc_platform_data *pdata = pdev->dev.platform_data;
+	int rc = 0;
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "no platform data?\n");
+		return -EINVAL;
+	}
+
+	epm_adc = kzalloc(sizeof(struct epm_adc_drv), GFP_KERNEL);
+	if (!epm_adc) {
+		dev_err(&pdev->dev, "Unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(pdev, epm_adc);
+	epm_adc_drv = epm_adc;
+	epm_adc->pdev = pdev;
+
+	epm_adc->misc.name = EPM_ADC_DRIVER_NAME;
+	epm_adc->misc.minor = MISC_DYNAMIC_MINOR;
+	epm_adc->misc.fops = &epm_adc_fops;
+
+	if (misc_register(&epm_adc->misc)) {
+		dev_err(&pdev->dev, "Unable to register misc device!\n");
+		return -EFAULT;
+	}
+
+	rc = epm_adc_init_hwmon(pdev, epm_adc);
+	if (rc) {
+		dev_err(&pdev->dev, "msm_adc_dev_init failed\n");
+		misc_deregister(&epm_adc->misc);
+		return rc;
+	}
+
+	epm_adc->hwmon = hwmon_device_register(&pdev->dev);
+	if (IS_ERR(epm_adc->hwmon)) {
+		dev_err(&pdev->dev, "hwmon_device_register failed\n");
+		misc_deregister(&epm_adc->misc);
+		rc = PTR_ERR(epm_adc->hwmon);
+		return rc;
+	}
+
+	mutex_init(&epm_adc->conv_lock);
+	epm_i2c_info = &pdata->epm_i2c_board_info;
+	epm_adc->bus_id = pdata->bus_id;
+	epm_gpio_expander_base_addr = pdata->gpio_expander_base_addr;
+	epm_adc_expander_register = false;
+	return rc;
+}
+
+static int __devexit epm_adc_remove(struct platform_device *pdev)
+{
+	struct epm_adc_drv *epm_adc = platform_get_drvdata(pdev);
+	struct epm_adc_platform_data *pdata = pdev->dev.platform_data;
+	int num_chans = pdata->num_channels;
+	int i = 0;
+
+	if (epm_adc->sens_attr)
+		for (i = 0; i < num_chans; i++)
+			device_remove_file(&pdev->dev,
+					&epm_adc->sens_attr[i].dev_attr);
+	hwmon_device_unregister(epm_adc->hwmon);
+	misc_deregister(&epm_adc->misc);
+	epm_adc = NULL;
+
+	return 0;
+}
+
+static struct platform_driver epm_adc_driver = {
+	.probe = epm_adc_probe,
+	.remove = __devexit_p(epm_adc_remove),
+	.driver = {
+		.name = EPM_ADC_DRIVER_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init epm_adc_init(void)
+{
+	int ret = 0;
+
+	ret = platform_driver_register(&epm_adc_driver);
+	if (ret) {
+		pr_err("%s: driver register failed, rc=%d\n", __func__, ret);
+		return ret;
+	}
+
+	ret = spi_register_driver(&epm_spi_driver);
+	if (ret)
+		pr_err("%s: spi register failed: rc=%d\n", __func__, ret);
+
+	return ret;
+}
+
+static void __exit epm_adc_exit(void)
+{
+	spi_unregister_driver(&epm_spi_driver);
+	platform_driver_unregister(&epm_adc_driver);
+}
+
+module_init(epm_adc_init);
+module_exit(epm_adc_exit);
+
+MODULE_DESCRIPTION("EPM ADC Driver");
+MODULE_ALIAS("platform:epm_adc");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/m_adcproc.c b/drivers/hwmon/m_adcproc.c
new file mode 100644
index 0000000..70e505e
--- /dev/null
+++ b/drivers/hwmon/m_adcproc.c
@@ -0,0 +1,469 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+
+#include <linux/msm_adc.h>
+
+#define KELVINMIL_DEGMIL	273160
+
+static const struct adc_map_pt adcmap_batttherm[] = {
+	{2020,	-30},
+	{1923,	-20},
+	{1796,	-10},
+	{1640,	  0},
+	{1459,	 10},
+	{1260,	 20},
+	{1159,	 25},
+	{1059,	 30},
+	{871,	 40},
+	{706,	 50},
+	{567,	 60},
+	{453,	 70},
+	{364,	 80}
+};
+
+static const struct adc_map_pt adcmap_msmtherm[] = {
+	{2150,	-30},
+	{2107,	-20},
+	{2037,	-10},
+	{1929,	  0},
+	{1776,	 10},
+	{1579,	 20},
+	{1467,	 25},
+	{1349,	 30},
+	{1108,	 40},
+	{878,	 50},
+	{677,	 60},
+	{513,	 70},
+	{385,	 80},
+	{287,	 90},
+	{215,	100},
+	{186,	110},
+	{107,	120}
+};
+
+static const struct adc_map_pt adcmap_ntcg104ef104fb[] = {
+	{696483,	-40960},
+	{649148,	-39936},
+	{605368,	-38912},
+	{564809,	-37888},
+	{527215,	-36864},
+	{492322,	-35840},
+	{460007,	-34816},
+	{429982,	-33792},
+	{402099,	-32768},
+	{376192,	-31744},
+	{352075,	-30720},
+	{329714,	-29696},
+	{308876,	-28672},
+	{289480,	-27648},
+	{271417,	-26624},
+	{254574,	-25600},
+	{238903,	-24576},
+	{224276,	-23552},
+	{210631,	-22528},
+	{197896,	-21504},
+	{186007,	-20480},
+	{174899,	-19456},
+	{164521,	-18432},
+	{154818,	-17408},
+	{145744,	-16384},
+	{137265,	-15360},
+	{129307,	-14336},
+	{121866,	-13312},
+	{114896,	-12288},
+	{108365,	-11264},
+	{102252,	-10240},
+	{96499,		-9216},
+	{91111,		-8192},
+	{86055,		-7168},
+	{81308,		-6144},
+	{76857,		-5120},
+	{72660,		-4096},
+	{68722,		-3072},
+	{65020,		-2048},
+	{61538,		-1024},
+	{58261,		0},
+	{55177,		1024},
+	{52274,		2048},
+	{49538,		3072},
+	{46962,		4096},
+	{44531,		5120},
+	{42243,		6144},
+	{40083,		7168},
+	{38045,		8192},
+	{36122,		9216},
+	{34308,		10240},
+	{32592,		11264},
+	{30972,		12288},
+	{29442,		13312},
+	{27995,		14336},
+	{26624,		15360},
+	{25333,		16384},
+	{24109,		17408},
+	{22951,		18432},
+	{21854,		19456},
+	{20807,		20480},
+	{19831,		21504},
+	{18899,		22528},
+	{18016,		23552},
+	{17178,		24576},
+	{16384,		25600},
+	{15631,		26624},
+	{14916,		27648},
+	{14237,		28672},
+	{13593,		29696},
+	{12976,		30720},
+	{12400,		31744},
+	{11848,		32768},
+	{11324,		33792},
+	{10825,		34816},
+	{10354,		35840},
+	{9900,		36864},
+	{9471,		37888},
+	{9062,		38912},
+	{8674,		39936},
+	{8306,		40960},
+	{7951,		41984},
+	{7616,		43008},
+	{7296,		44032},
+	{6991,		45056},
+	{6701,		46080},
+	{6424,		47104},
+	{6160,		48128},
+	{5908,		49152},
+	{5667,		50176},
+	{5439,		51200},
+	{5219,		52224},
+	{5010,		53248},
+	{4810,		54272},
+	{4619,		55296},
+	{4440,		56320},
+	{4263,		57344},
+	{4097,		58368},
+	{3938,		59392},
+	{3785,		60416},
+	{3637,		61440},
+	{3501,		62464},
+	{3368,		63488},
+	{3240,		64512},
+	{3118,		65536},
+	{2998,		66560},
+	{2889,		67584},
+	{2782,		68608},
+	{2680,		69632},
+	{2581,		70656},
+	{2490,		71680},
+	{2397,		72704},
+	{2310,		73728},
+	{2227,		74752},
+	{2147,		75776},
+	{2064,		76800},
+	{1998,		77824},
+	{1927,		78848},
+	{1860,		79872},
+	{1795,		80896},
+	{1736,		81920},
+	{1673,		82944},
+	{1615,		83968},
+	{1560,		84992},
+	{1507,		86016},
+	{1456,		87040},
+	{1407,		88064},
+	{1360,		89088},
+	{1314,		90112},
+	{1271,		91136},
+	{1228,		92160},
+	{1189,		93184},
+	{1150,		94208},
+	{1112,		95232},
+	{1076,		96256},
+	{1042,		97280},
+	{1008,		98304},
+	{976,		99328},
+	{945,		100352},
+	{915,		101376},
+	{886,		102400},
+	{859,		103424},
+	{832,		104448},
+	{807,		105472},
+	{782,		106496},
+	{756,		107520},
+	{735,		108544},
+	{712,		109568},
+	{691,		110592},
+	{670,		111616},
+	{650,		112640},
+	{631,		113664},
+	{612,		114688},
+	{594,		115712},
+	{577,		116736},
+	{560,		117760},
+	{544,		118784},
+	{528,		119808},
+	{513,		120832},
+	{498,		121856},
+	{483,		122880},
+	{470,		123904},
+	{457,		124928},
+	{444,		125952},
+	{431,		126976},
+	{419,		128000}
+};
+
+static int32_t
+	adc_map_linear(const struct adc_map_pt *pts,
+		uint32_t tablesize, int32_t input, int64_t *output)
+{
+	bool descending = 1;
+	uint32_t i = 0;
+
+	if ((pts == NULL) || (output == NULL))
+		return -EINVAL;
+
+	/* Check if table is descending or ascending */
+	if (tablesize > 1) {
+		if (pts[0].x < pts[1].x)
+			descending = 0;
+	}
+
+	while (i < tablesize) {
+		if ((descending == 1) && (pts[i].x < input)) {
+			/* table entry is less than measured
+				value and table is descending, stop */
+			break;
+		} else if ((descending == 0) &&
+				(pts[i].x > input)) {
+			/* table entry is greater than measured
+				value and table is ascending, stop */
+			break;
+		} else
+			i++;
+	}
+
+	if (i == 0)
+		*output = pts[0].y;
+	else if (i == tablesize)
+		*output = pts[tablesize-1].y;
+	else {
+	/* result is between search_index and search_index-1 */
+	/* interpolate linearly */
+	*output = (((int32_t) ((pts[i].y - pts[i-1].y)*
+			(input - pts[i-1].x))/
+			(pts[i].x - pts[i-1].x))+
+			pts[i-1].y);
+	}
+
+	return 0;
+}
+
+int32_t scale_default(int32_t adc_code,
+				const struct adc_properties *adc_properties,
+				const struct chan_properties *chan_properties,
+				struct adc_chan_result *adc_chan_result)
+{
+	bool negative_rawfromoffset = 0;
+	int32_t rawfromoffset = adc_code - chan_properties->adc_graph->offset;
+
+	if (!chan_properties->gain_numerator ||
+		!chan_properties->gain_denominator)
+		return -EINVAL;
+
+	adc_chan_result->adc_code = adc_code;
+	if (rawfromoffset < 0) {
+		if (adc_properties->bipolar) {
+			rawfromoffset = (rawfromoffset ^ -1) +  1;
+			negative_rawfromoffset = 1;
+		} else
+			rawfromoffset = 0;
+	}
+
+	if (rawfromoffset >= 1 << adc_properties->bitresolution)
+		rawfromoffset = (1 << adc_properties->bitresolution) - 1;
+
+	adc_chan_result->measurement = (int64_t)rawfromoffset*
+					chan_properties->adc_graph->dx*
+					chan_properties->gain_denominator;
+
+	/* do_div only perform positive integer division! */
+	do_div(adc_chan_result->measurement, chan_properties->adc_graph->dy*
+					chan_properties->gain_numerator);
+
+	if (negative_rawfromoffset)
+		adc_chan_result->measurement =
+		(adc_chan_result->measurement ^ -1) + 1;
+
+	/* Note: adc_chan_result->measurement is in the unit of
+	 * adc_properties.adc_reference. For generic channel processing,
+	 * channel measurement is a scale/ratio relative to the adc
+	 * reference input */
+	adc_chan_result->physical = (int32_t) adc_chan_result->measurement;
+
+	return 0;
+}
+
+int32_t scale_batt_therm(int32_t adc_code,
+				const struct adc_properties *adc_properties,
+				const struct chan_properties *chan_properties,
+				struct adc_chan_result *adc_chan_result)
+{
+	scale_default(adc_code, adc_properties, chan_properties,
+			adc_chan_result);
+	/* convert mV ---> degC using the table */
+	return adc_map_linear(
+			adcmap_batttherm,
+			sizeof(adcmap_batttherm)/sizeof(adcmap_batttherm[0]),
+			adc_chan_result->physical,
+			&adc_chan_result->physical);
+}
+
+int32_t scale_msm_therm(int32_t adc_code,
+		const struct adc_properties *adc_properties,
+		const struct chan_properties *chan_properties,
+		struct adc_chan_result *adc_chan_result)
+{
+	scale_default(adc_code, adc_properties, chan_properties,
+			adc_chan_result);
+	/* convert mV ---> degC using the table */
+	return adc_map_linear(
+			adcmap_msmtherm,
+			sizeof(adcmap_msmtherm)/sizeof(adcmap_msmtherm[0]),
+			adc_chan_result->physical,
+			&adc_chan_result->physical);
+}
+
+int32_t scale_pmic_therm(int32_t adc_code,
+				const struct adc_properties *adc_properties,
+				const struct chan_properties *chan_properties,
+				struct adc_chan_result *adc_chan_result)
+{
+	/* 2mV/K */
+	int32_t rawfromoffset = adc_code - chan_properties->adc_graph->offset;
+
+	if (!chan_properties->gain_numerator ||
+		!chan_properties->gain_denominator)
+		return -EINVAL;
+
+	adc_chan_result->adc_code = adc_code;
+	if (rawfromoffset > 0) {
+		if (rawfromoffset >= 1 << adc_properties->bitresolution)
+			rawfromoffset = (1 << adc_properties->bitresolution)
+									- 1;
+		adc_chan_result->measurement = (int64_t)rawfromoffset*
+					chan_properties->adc_graph->dx*
+					chan_properties->gain_denominator*1000;
+		do_div(adc_chan_result->measurement,
+			chan_properties->adc_graph->dy*
+			chan_properties->gain_numerator*2);
+	} else {
+		adc_chan_result->measurement = 0;
+	}
+	/* Note: adc_chan_result->measurement is in the unit of
+		adc_properties.adc_reference */
+	adc_chan_result->physical = (int32_t)adc_chan_result->measurement;
+	/* Change to .001 deg C */
+	adc_chan_result->physical -= KELVINMIL_DEGMIL;
+	adc_chan_result->measurement <<= 1;
+
+	return 0;
+}
+
+/* Scales the ADC code to 0.001 degrees C using the map
+ * table for the XO thermistor.
+ */
+int32_t tdkntcgtherm(int32_t adc_code,
+			const struct adc_properties *adc_properties,
+			const struct chan_properties *chan_properties,
+			struct adc_chan_result *adc_chan_result)
+{
+	int32_t offset = chan_properties->adc_graph->offset,
+		dy = chan_properties->adc_graph->dy,
+		dx = chan_properties->adc_graph->dx,
+		fullscale_calibrated_adc_code;
+
+	uint32_t rt_r25;
+	uint32_t num1, num2, denom;
+
+	adc_chan_result->adc_code = adc_code;
+	fullscale_calibrated_adc_code = dy + offset;
+	/* The above is a short cut in math that would reduce a lot of
+	   computation whereas the below expression
+		(adc_properties->adc_reference*dy+dx*offset+(dx>>1))/dx
+	   is a more generic formula when the 2 reference voltages are
+	   different than 0 and full scale voltage. */
+
+	if ((dy == 0) || (dx == 0) ||
+			(offset >= fullscale_calibrated_adc_code)) {
+		return -EINVAL;
+	} else {
+		if (adc_code >= fullscale_calibrated_adc_code) {
+			rt_r25 = (uint32_t)-1;
+		} else if (adc_code <= offset) {
+			rt_r25 = 0;
+		} else {
+	/* The formula used is (adc_code of current reading - offset)/
+	 * (the calibrated fullscale adc code - adc_code of current reading).
+	 * For this channel, at this time, chan_properties->gain_numerator =
+	 * chan_properties->gain_denominator = 1, so no need to incorporate
+	 * into the formula even though we could and multiply/divide by 1
+	 * which yields the same result but expensive on computation. */
+		num1 = (adc_code - offset) << 14;
+		num2 = (fullscale_calibrated_adc_code - adc_code) >> 1;
+		denom = fullscale_calibrated_adc_code - adc_code;
+
+			if ((int)denom <= 0)
+				rt_r25 = 0x7FFFFFFF;
+			else
+				rt_r25 = (num1 + num2) / denom;
+		}
+
+		if (rt_r25 > 0x7FFFFFFF)
+			rt_r25 = 0x7FFFFFFF;
+
+		adc_map_linear(adcmap_ntcg104ef104fb,
+		sizeof(adcmap_ntcg104ef104fb)/sizeof(adcmap_ntcg104ef104fb[0]),
+		(int32_t)rt_r25, &adc_chan_result->physical);
+	}
+
+	return 0;
+}
+
+int32_t scale_xtern_chgr_cur(int32_t adc_code,
+			const struct adc_properties *adc_properties,
+			const struct chan_properties *chan_properties,
+			struct adc_chan_result *adc_chan_result)
+{
+	int32_t rawfromoffset = adc_code - chan_properties->adc_graph->offset;
+
+	if (!chan_properties->gain_numerator ||
+		!chan_properties->gain_denominator)
+		return -EINVAL;
+
+	adc_chan_result->adc_code = adc_code;
+	if (rawfromoffset > 0) {
+		if (rawfromoffset >= 1 << adc_properties->bitresolution)
+			rawfromoffset = (1 << adc_properties->bitresolution)
+									- 1;
+		adc_chan_result->measurement = ((int64_t)rawfromoffset * 5)*
+						chan_properties->adc_graph->dx*
+					chan_properties->gain_denominator;
+		do_div(adc_chan_result->measurement,
+					chan_properties->adc_graph->dy*
+					chan_properties->gain_numerator);
+	} else {
+		adc_chan_result->measurement = 0;
+	}
+	adc_chan_result->physical = (int32_t) adc_chan_result->measurement;
+
+	return 0;
+}
diff --git a/drivers/hwmon/msm_adc.c b/drivers/hwmon/msm_adc.c
new file mode 100644
index 0000000..018ce79
--- /dev/null
+++ b/drivers/hwmon/msm_adc.c
@@ -0,0 +1,1533 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+#include <linux/msm_adc.h>
+#include <linux/pmic8058-xoadc.h>
+#include <linux/slab.h>
+#include <linux/semaphore.h>
+#include <linux/module.h>
+
+#include <mach/dal.h>
+
+#define MSM_ADC_DRIVER_NAME		"msm_adc"
+#define MSM_ADC_MAX_FNAME		15
+
+#define MSM_ADC_DALRPC_DEVICEID		0x02000067
+#define MSM_ADC_DALRPC_PORT_NAME	"DAL00"
+#define MSM_ADC_DALRPC_CPU		SMD_APPS_MODEM
+
+#define MSM_ADC_DALRPC_CMD_REQ_CONV	9
+#define MSM_ADC_DALRPC_CMD_INPUT_PROP	11
+
+#define MSM_ADC_DALRC_CONV_TIMEOUT	(5 * HZ)  /* 5 seconds */
+
+#define MSM_8x25_ADC_DEV_ID		0
+#define MSM_8x25_CHAN_ID		16
+
+enum dal_error {
+	DAL_ERROR_INVALID_DEVICE_IDX = 1,
+	DAL_ERROR_INVALID_CHANNEL_IDX,
+	DAL_ERROR_NULL_POINTER,
+	DAL_ERROR_DEVICE_QUEUE_FULL,
+	DAL_ERROR_INVALID_PROPERTY_LENGTH,
+	DAL_ERROR_REMOTE_EVENT_POOL_FULL
+};
+
+enum dal_result_status {
+	DAL_RESULT_STATUS_INVALID,
+	DAL_RESULT_STATUS_VALID
+};
+
+struct dal_conv_state {
+	struct dal_conv_slot		context[MSM_ADC_DEV_MAX_INFLIGHT];
+	struct list_head		slots;
+	struct mutex			list_lock;
+	struct semaphore		slot_count;
+};
+
+struct adc_dev {
+	char				*name;
+	uint32_t			nchans;
+	struct dal_conv_state		conv;
+	struct dal_translation		transl;
+	struct sensor_device_attribute	*sens_attr;
+	char				**fnames;
+};
+
+struct msm_adc_drv {
+	/*  Common to both XOADC and EPM  */
+	struct platform_device		*pdev;
+	struct device			*hwmon;
+	struct miscdevice		misc;
+	/*  XOADC variables  */
+	struct sensor_device_attribute	*sens_attr;
+	struct workqueue_struct		*wq;
+	atomic_t			online;
+	atomic_t			total_outst;
+	wait_queue_head_t		total_outst_wait;
+
+	/*  EPM variables  */
+	void				*dev_h;
+	struct adc_dev			*devs[MSM_ADC_MAX_NUM_DEVS];
+	struct mutex			prop_lock;
+	atomic_t			rpc_online;
+	atomic_t			rpc_total_outst;
+	wait_queue_head_t		rpc_total_outst_wait;
+};
+
+static bool epm_init;
+static bool epm_fluid_enabled;
+
+/* Needed to support file_op interfaces */
+static struct msm_adc_drv *msm_adc_drv;
+
+static bool conv_first_request;
+
+static ssize_t msm_adc_show_curr(struct device *dev,
+				struct device_attribute *devattr, char *buf);
+
+static int msm_rpc_adc_blocking_conversion(struct msm_adc_drv *msm_adc,
+				uint32_t chan, struct adc_chan_result *result);
+
+static int msm_adc_blocking_conversion(struct msm_adc_drv *msm_adc,
+				uint32_t chan, struct adc_chan_result *result);
+
+static int msm_adc_open(struct inode *inode, struct file *file)
+{
+	struct msm_client_data *client;
+	struct msm_adc_drv *msm_adc = msm_adc_drv;
+	struct platform_device *pdev = msm_adc->pdev;
+
+	client = kzalloc(sizeof(struct msm_client_data), GFP_KERNEL);
+	if (!client) {
+		dev_err(&pdev->dev, "Unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	if (!try_module_get(THIS_MODULE)) {
+		kfree(client);
+		return -EACCES;
+	}
+
+	mutex_init(&client->lock);
+	INIT_LIST_HEAD(&client->complete_list);
+	init_waitqueue_head(&client->data_wait);
+	init_waitqueue_head(&client->outst_wait);
+
+	client->online = 1;
+
+	file->private_data = client;
+
+	return nonseekable_open(inode, file);
+}
+
+static inline void msm_adc_restore_slot(struct dal_conv_state *conv_s,
+					struct dal_conv_slot *slot)
+{
+	mutex_lock(&conv_s->list_lock);
+	list_add(&slot->list, &conv_s->slots);
+	mutex_unlock(&conv_s->list_lock);
+
+	up(&conv_s->slot_count);
+}
+
+static int no_pending_client_requests(struct msm_client_data *client)
+{
+	mutex_lock(&client->lock);
+
+	if (client->num_outstanding == 0) {
+		mutex_unlock(&client->lock);
+		return 1;
+	}
+
+	mutex_unlock(&client->lock);
+
+	return 0;
+}
+
+static int data_avail(struct msm_client_data *client, uint32_t *pending)
+{
+	uint32_t completed;
+
+	mutex_lock(&client->lock);
+	completed = client->num_complete;
+	mutex_unlock(&client->lock);
+
+	if (completed > 0) {
+		if (pending != NULL)
+			*pending = completed;
+		return 1;
+	}
+
+	return 0;
+}
+
+static int msm_adc_release(struct inode *inode, struct file *file)
+{
+	struct msm_client_data *client = file->private_data;
+	struct adc_conv_slot *slot, *tmp;
+	int rc;
+	struct msm_adc_platform_data *pdata =
+					msm_adc_drv->pdev->dev.platform_data;
+	struct msm_adc_channels *channel = pdata->channel;
+
+	module_put(THIS_MODULE);
+
+	mutex_lock(&client->lock);
+
+	/* prevent any further requests while we teardown the client */
+	client->online = 0;
+
+	mutex_unlock(&client->lock);
+
+	/*
+	 * We may still have outstanding transactions in flight from this
+	 * client that have not completed. Make sure they're completed
+	 * before removing the client.
+	 */
+	rc = wait_event_interruptible(client->outst_wait,
+				      no_pending_client_requests(client));
+	if (rc) {
+		pr_err("%s: wait_event_interruptible failed rc = %d\n",
+								__func__, rc);
+		return rc;
+	}
+
+	/*
+	 * All transactions have completed. Add slot resources back to the
+	 * appropriate devices.
+	 */
+	list_for_each_entry_safe(slot, tmp, &client->complete_list, list) {
+		slot->client = NULL;
+		list_del(&slot->list);
+		channel[slot->conv.result.chan].adc_access_fn->adc_restore_slot(
+		channel[slot->conv.result.chan].adc_dev_instance, slot);
+	}
+
+	kfree(client);
+
+	return 0;
+}
+
+static int msm_adc_translate_dal_to_hwmon(struct msm_adc_drv *msm_adc,
+					  uint32_t chan,
+					  struct adc_dev_spec *dest)
+{
+	struct dal_translation *transl;
+	struct msm_adc_platform_data *pdata = msm_adc->pdev->dev.platform_data;
+	int i;
+
+	for (i = 0; i < pdata->num_adc; i++) {
+		transl = &msm_adc->devs[i]->transl;
+		if (chan >= transl->hwmon_start &&
+		    chan <= transl->hwmon_end) {
+			dest->dal.dev_idx = transl->dal_dev_idx;
+			dest->hwmon_dev_idx = transl->hwmon_dev_idx;
+			dest->dal.chan_idx = chan - transl->hwmon_start;
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+
+static int msm_adc_translate_hwmon_to_dal(struct msm_adc_drv *msm_adc,
+					  struct adc_dev_spec *source,
+					  uint32_t *chan)
+{
+	struct msm_adc_platform_data *pdata = msm_adc->pdev->dev.platform_data;
+	struct dal_translation *transl;
+	int i;
+
+	for (i = 0; i < pdata->num_adc; i++) {
+		transl = &msm_adc->devs[i]->transl;
+		if (source->dal.dev_idx != transl->dal_dev_idx)
+			continue;
+		*chan = transl->hwmon_start + source->dal.chan_idx;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int msm_adc_getinputproperties(struct msm_adc_drv *msm_adc,
+					  const char *lookup_name,
+					  struct adc_dev_spec *result)
+{
+	struct device *dev = &msm_adc->pdev->dev;
+	int rc;
+
+	mutex_lock(&msm_adc->prop_lock);
+
+	rc = dalrpc_fcn_8(MSM_ADC_DALRPC_CMD_INPUT_PROP, msm_adc->dev_h,
+			  lookup_name, strlen(lookup_name) + 1,
+			  &result->dal, sizeof(struct dal_dev_spec));
+	if (rc) {
+		dev_err(dev, "DAL getprop request failed: rc = %d\n", rc);
+		mutex_unlock(&msm_adc->prop_lock);
+		return -EIO;
+	}
+
+	mutex_unlock(&msm_adc->prop_lock);
+	return rc;
+}
+
+static int msm_adc_lookup(struct msm_adc_drv *msm_adc,
+			  struct msm_adc_lookup *lookup)
+{
+	struct msm_adc_platform_data *pdata = msm_adc->pdev->dev.platform_data;
+	struct adc_dev_spec target;
+	int rc = 0, i = 0;
+	uint32_t len = 0;
+
+	len = strnlen(lookup->name, MSM_ADC_MAX_CHAN_STR);
+	while (i < pdata->num_chan_supported) {
+		if (strncmp(lookup->name, pdata->channel[i].name, len))
+			i++;
+		else
+			break;
+	}
+
+	if (pdata->num_chan_supported > 0 && i < pdata->num_chan_supported) {
+		lookup->chan_idx = i;
+	} else if (msm_adc->dev_h) {
+		rc = msm_adc_getinputproperties(msm_adc, lookup->name, &target);
+		if (rc) {
+			pr_err("%s: Lookup failed for %s\n", __func__,
+				lookup->name);
+			return rc;
+		}
+		rc = msm_adc_translate_hwmon_to_dal(msm_adc, &target,
+						&lookup->chan_idx);
+		if (rc)
+			pr_err("%s: Translation failed for %s\n", __func__,
+						lookup->name);
+	} else {
+		pr_err("%s: Lookup failed for %s\n", __func__, lookup->name);
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+static int msm_adc_aio_conversion(struct msm_adc_drv *msm_adc,
+				  struct adc_chan_result *request,
+				  struct msm_client_data *client)
+{
+	struct msm_adc_platform_data *pdata =
+					msm_adc_drv->pdev->dev.platform_data;
+	struct msm_adc_channels *channel = &pdata->channel[request->chan];
+	struct adc_conv_slot *slot;
+
+	/* we could block here, but only for a bounded time */
+	channel->adc_access_fn->adc_slot_request(channel->adc_dev_instance,
+									&slot);
+
+	if (slot) {
+		atomic_inc(&msm_adc->total_outst);
+		mutex_lock(&client->lock);
+		client->num_outstanding++;
+		mutex_unlock(&client->lock);
+
+		/* indicates non blocking request to callback handler */
+		slot->blocking = 0;
+		slot->compk = NULL;/*For kernel space usage; n/a for usr space*/
+		slot->conv.result.chan = client->adc_chan = request->chan;
+		slot->client = client;
+		slot->adc_request = START_OF_CONV;
+		slot->chan_path = channel->chan_path_type;
+		slot->chan_adc_config = channel->adc_config_type;
+		slot->chan_adc_calib = channel->adc_calib_type;
+		queue_work(msm_adc->wq, &slot->work);
+		return 0;
+	}
+	return -EBUSY;
+}
+
+static int msm_adc_fluid_hw_deinit(struct msm_adc_drv *msm_adc)
+{
+	struct msm_adc_platform_data *pdata = msm_adc->pdev->dev.platform_data;
+
+	if (!epm_init)
+		return -EINVAL;
+
+	if (pdata->gpio_config == APROC_CONFIG &&
+		epm_fluid_enabled && pdata->adc_fluid_disable != NULL) {
+		pdata->adc_fluid_disable();
+		epm_fluid_enabled = false;
+	}
+
+	return 0;
+}
+
+static int msm_adc_fluid_hw_init(struct msm_adc_drv *msm_adc)
+{
+	struct msm_adc_platform_data *pdata = msm_adc->pdev->dev.platform_data;
+
+	if (!epm_init)
+		return -EINVAL;
+
+	if (!pdata->adc_fluid_enable)
+		return -ENODEV;
+
+	printk(KERN_DEBUG "msm_adc_fluid_hw_init: Calling adc_fluid_enable.\n");
+
+	if (pdata->gpio_config == APROC_CONFIG && !epm_fluid_enabled) {
+		pdata->adc_fluid_enable();
+		epm_fluid_enabled = true;
+	}
+
+  /* return success for now but check for errors from hw init configuration */
+	return 0;
+}
+
+static int msm_adc_poll_complete(struct msm_adc_drv *msm_adc,
+			     struct msm_client_data *client, uint32_t *pending)
+{
+	int rc;
+
+	/*
+	 * Don't proceed if there there's nothing queued on this client.
+	 * We could deadlock otherwise in a single threaded scenario.
+	 */
+	if (no_pending_client_requests(client) && !data_avail(client, pending))
+		return -EDEADLK;
+
+	rc = wait_event_interruptible(client->data_wait,
+				data_avail(client, pending));
+	if (rc)
+		return rc;
+
+	return 0;
+}
+
+static int msm_adc_read_result(struct msm_adc_drv *msm_adc,
+			       struct msm_client_data *client,
+			       struct adc_chan_result *result)
+{
+	struct msm_adc_platform_data *pdata = msm_adc->pdev->dev.platform_data;
+	struct msm_adc_channels *channel = pdata->channel;
+	struct adc_conv_slot *slot;
+	int rc = 0;
+
+	mutex_lock(&client->lock);
+
+	slot = list_first_entry(&client->complete_list,
+				struct adc_conv_slot, list);
+	if (!slot) {
+		mutex_unlock(&client->lock);
+		return -ENOMSG;
+	}
+
+	slot->client = NULL;
+	list_del(&slot->list);
+
+	client->num_complete--;
+
+	mutex_unlock(&client->lock);
+
+	*result = slot->conv.result;
+
+	/* restore this slot to reserve */
+	channel[slot->conv.result.chan].adc_access_fn->adc_restore_slot(
+		channel[slot->conv.result.chan].adc_dev_instance, slot);
+
+	return rc;
+}
+
+static long msm_adc_ioctl(struct file *file, unsigned int cmd,
+					     unsigned long arg)
+{
+	struct msm_client_data *client = file->private_data;
+	struct msm_adc_drv *msm_adc = msm_adc_drv;
+	struct platform_device *pdev = msm_adc->pdev;
+	struct msm_adc_platform_data *pdata = pdev->dev.platform_data;
+	uint32_t block_res = 0;
+
+	int rc;
+
+	switch (cmd) {
+	case MSM_ADC_REQUEST:
+		{
+			struct adc_chan_result conv;
+
+			if (copy_from_user(&conv, (void __user *)arg,
+					sizeof(struct adc_chan_result)))
+				return -EFAULT;
+
+			if (conv.chan < pdata->num_chan_supported) {
+				rc = msm_adc_blocking_conversion(msm_adc,
+							conv.chan, &conv);
+			} else {
+				if (!msm_adc->dev_h)
+					return -EAGAIN;
+
+				rc = msm_rpc_adc_blocking_conversion(msm_adc,
+							conv.chan, &conv);
+			}
+			if (rc) {
+				dev_dbg(&pdev->dev, "BLK conversion failed\n");
+				return rc;
+			}
+
+			if (copy_to_user((void __user *)arg, &conv,
+					sizeof(struct adc_chan_result)))
+				return -EFAULT;
+			break;
+		}
+	case MSM_ADC_AIO_REQUEST_BLOCK_RES:
+		block_res = 1;
+	case MSM_ADC_AIO_REQUEST:
+		{
+			struct adc_chan_result conv;
+
+			if (copy_from_user(&conv, (void __user *)arg,
+					sizeof(struct adc_chan_result)))
+				return -EFAULT;
+
+			if (conv.chan >= pdata->num_chan_supported)
+				return -EINVAL;
+
+			rc = msm_adc_aio_conversion(msm_adc, &conv, client);
+			if (rc) {
+				dev_dbg(&pdev->dev, "AIO conversion failed\n");
+				return rc;
+			}
+			if (copy_to_user((void __user *)arg, &conv,
+					sizeof(struct adc_chan_result)))
+				return -EFAULT;
+			break;
+		}
+	case MSM_ADC_AIO_POLL:
+		{
+			uint32_t completed;
+
+			rc = msm_adc_poll_complete(msm_adc, client, &completed);
+			if (rc) {
+				dev_dbg(&pdev->dev, "poll request failed\n");
+				return rc;
+			}
+
+			if (copy_to_user((void __user *)arg, &completed,
+					sizeof(uint32_t)))
+				return -EFAULT;
+
+			break;
+		}
+	case MSM_ADC_AIO_READ:
+		{
+			struct adc_chan_result result;
+
+			rc = msm_adc_read_result(msm_adc, client, &result);
+			if (rc) {
+				dev_dbg(&pdev->dev, "read result failed\n");
+				return rc;
+			}
+
+			if (copy_to_user((void __user *)arg, &result,
+					sizeof(struct adc_chan_result)))
+				return -EFAULT;
+			break;
+		}
+	case MSM_ADC_LOOKUP:
+		{
+			struct msm_adc_lookup lookup;
+
+			if (copy_from_user(&lookup, (void __user *)arg,
+					sizeof(struct msm_adc_lookup)))
+				return -EFAULT;
+
+			rc = msm_adc_lookup(msm_adc, &lookup);
+			if (rc) {
+				dev_dbg(&pdev->dev, "No such channel: %s\n",
+						lookup.name);
+				return rc;
+			}
+
+			if (copy_to_user((void __user *)arg, &lookup,
+					sizeof(struct msm_adc_lookup)))
+				return -EFAULT;
+			break;
+		}
+	case MSM_ADC_FLUID_INIT:
+		{
+			uint32_t result;
+
+			result = msm_adc_fluid_hw_init(msm_adc);
+
+			if (copy_to_user((void __user *)arg, &result,
+						sizeof(uint32_t)))	{
+				printk(KERN_ERR "MSM_ADC_FLUID_INIT: "
+					"copy_to_user returned an error.\n");
+				return -EFAULT;
+			}
+			printk(KERN_DEBUG "MSM_ADC_FLUID_INIT: Success.\n");
+			break;
+		}
+	case MSM_ADC_FLUID_DEINIT:
+		{
+			uint32_t result;
+
+			result = msm_adc_fluid_hw_deinit(msm_adc);
+
+			if (copy_to_user((void __user *)arg, &result,
+						sizeof(uint32_t)))
+				return -EFAULT;
+			break;
+		}
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+const struct file_operations msm_adc_fops = {
+	.open = msm_adc_open,
+	.release = msm_adc_release,
+	.unlocked_ioctl = msm_adc_ioctl,
+};
+
+static ssize_t msm_adc_show_curr(struct device *dev,
+				 struct device_attribute *devattr, char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct msm_adc_drv *msm_adc = dev_get_drvdata(dev);
+	struct msm_adc_platform_data *pdata = msm_adc->pdev->dev.platform_data;
+	struct adc_chan_result result;
+	int rc;
+
+#ifdef CONFIG_PMIC8058_XOADC
+	rc = pm8058_xoadc_registered();
+	if (rc <= 0)
+		return -ENODEV;
+#endif
+	if (attr->index < pdata->num_chan_supported) {
+		rc = msm_adc_blocking_conversion(msm_adc,
+					attr->index, &result);
+	} else {
+		if (pdata->gpio_config == APROC_CONFIG && !epm_fluid_enabled
+					&& pdata->adc_fluid_enable != NULL) {
+			printk(KERN_DEBUG "This is to read ADC value for "
+				"Fluid EPM and init. Do it only once.\n");
+			pdata->adc_fluid_enable();
+			epm_fluid_enabled = true;
+		}
+		rc = msm_rpc_adc_blocking_conversion(msm_adc,
+					attr->index, &result);
+	}
+	if (rc)
+		return 0;
+
+	return sprintf(buf, "Result: %lld Raw: %d\n", result.physical,
+		result.adc_code);
+}
+
+static int msm_rpc_adc_blocking_conversion(struct msm_adc_drv *msm_adc,
+		uint32_t hwmon_chan, struct adc_chan_result *result)
+{
+	struct msm_adc_platform_data *pdata = msm_adc->pdev->dev.platform_data;
+	struct dal_conv_request params;
+	struct device *dev = &msm_adc->pdev->dev;
+	struct adc_dev *adc_dev;
+	struct dal_conv_state *conv_s;
+	struct dal_conv_slot *slot;
+	struct adc_dev_spec dest;
+	int timeout, rc = 0;
+
+	if (pdata->gpio_config == APROC_CONFIG &&
+			pdata->adc_gpio_enable != NULL)
+		pdata->adc_gpio_enable(hwmon_chan-pdata->num_chan_supported);
+
+	rc = msm_adc_translate_dal_to_hwmon(msm_adc, hwmon_chan, &dest);
+	if (rc) {
+		dev_err(dev, "%s: translation from chan %u failed\n",
+							__func__, hwmon_chan);
+		if (pdata->gpio_config == APROC_CONFIG &&
+				pdata->adc_gpio_disable != NULL)
+			pdata->adc_gpio_disable(hwmon_chan
+					-pdata->num_chan_supported);
+		return -EINVAL;
+	}
+
+	adc_dev = msm_adc->devs[dest.hwmon_dev_idx];
+	conv_s = &adc_dev->conv;
+
+	down(&conv_s->slot_count);
+
+	mutex_lock(&conv_s->list_lock);
+
+	slot = list_first_entry(&conv_s->slots, struct dal_conv_slot, list);
+	list_del(&slot->list);
+	BUG_ON(!slot);
+
+	mutex_unlock(&conv_s->list_lock);
+
+	/* indicates blocking request to callback handler */
+	slot->blocking = 1;
+
+	params.target.dev_idx = dest.dal.dev_idx;
+	params.target.chan_idx = dest.dal.chan_idx;
+	params.cb_h = slot->cb_h;
+
+	rc = dalrpc_fcn_8(MSM_ADC_DALRPC_CMD_REQ_CONV, msm_adc->dev_h,
+			&params, sizeof(params), NULL, 0);
+	if (rc) {
+		dev_err(dev, "%s: Conversion for device = %u channel = %u"
+			     " failed\n", __func__, params.target.dev_idx,
+						    params.target.chan_idx);
+
+		rc = -EIO;
+		goto blk_conv_err;
+	}
+
+	timeout = wait_for_completion_interruptible_timeout(&slot->comp,
+					      MSM_ADC_DALRC_CONV_TIMEOUT);
+	if (timeout == 0) {
+		dev_err(dev, "read for device = %u channel = %u timed out\n",
+				params.target.dev_idx, params.target.chan_idx);
+		rc = -ETIMEDOUT;
+		goto blk_conv_err;
+	} else if (timeout < 0) {
+		rc = -EINTR;
+		goto blk_conv_err;
+	}
+
+	result->physical = (int64_t)slot->result.physical;
+
+	if (slot->result.status == DAL_RESULT_STATUS_INVALID)
+		rc = -ENODATA;
+
+blk_conv_err:
+	if (pdata->gpio_config == APROC_CONFIG &&
+			pdata->adc_gpio_disable != NULL)
+		pdata->adc_gpio_disable(hwmon_chan-pdata->num_chan_supported);
+	msm_adc_restore_slot(conv_s, slot);
+
+	return rc;
+}
+
+static int msm_adc_blocking_conversion(struct msm_adc_drv *msm_adc,
+			uint32_t hwmon_chan, struct adc_chan_result *result)
+{
+	struct adc_conv_slot *slot;
+	struct msm_adc_platform_data *pdata =
+					msm_adc_drv->pdev->dev.platform_data;
+	struct msm_adc_channels *channel = &pdata->channel[hwmon_chan];
+	int ret = 0;
+
+	if (conv_first_request) {
+		ret = pm8058_xoadc_calib_device(channel->adc_dev_instance);
+		if (ret) {
+			pr_err("pmic8058 xoadc calibration failed, retry\n");
+			return ret;
+		}
+		conv_first_request = false;
+	}
+
+	channel->adc_access_fn->adc_slot_request(channel->adc_dev_instance,
+									&slot);
+	if (slot) {
+		slot->conv.result.chan = hwmon_chan;
+		/* indicates blocking request to callback handler */
+		slot->blocking = 1;
+		slot->adc_request = START_OF_CONV;
+		slot->chan_path = channel->chan_path_type;
+		slot->chan_adc_config = channel->adc_config_type;
+		slot->chan_adc_calib = channel->adc_calib_type;
+		queue_work(msm_adc_drv->wq, &slot->work);
+
+		wait_for_completion_interruptible(&slot->comp);
+		*result = slot->conv.result;
+		channel->adc_access_fn->adc_restore_slot(
+					channel->adc_dev_instance, slot);
+		return 0;
+	}
+	return -EBUSY;
+}
+
+int32_t adc_channel_open(uint32_t channel, void **h)
+{
+	struct msm_client_data *client;
+	struct msm_adc_drv *msm_adc = msm_adc_drv;
+	struct msm_adc_platform_data *pdata;
+	struct platform_device *pdev;
+	int i = 0;
+
+	if (!msm_adc_drv)
+		return -EFAULT;
+
+#ifdef CONFIG_PMIC8058_XOADC
+	if (pm8058_xoadc_registered() <= 0)
+		return -ENODEV;
+#endif
+	pdata = msm_adc->pdev->dev.platform_data;
+	pdev = msm_adc->pdev;
+
+	while (i < pdata->num_chan_supported) {
+		if (channel == pdata->channel[i].channel_name)
+			break;
+		else
+			i++;
+	}
+
+	if (i == pdata->num_chan_supported)
+		return -EBADF; /* unknown channel */
+
+	client = kzalloc(sizeof(struct msm_client_data), GFP_KERNEL);
+	if (!client) {
+		dev_err(&pdev->dev, "Unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	if (!try_module_get(THIS_MODULE)) {
+		kfree(client);
+		return -EACCES;
+	}
+
+	mutex_init(&client->lock);
+	INIT_LIST_HEAD(&client->complete_list);
+	init_waitqueue_head(&client->data_wait);
+	init_waitqueue_head(&client->outst_wait);
+
+	client->online = 1;
+	client->adc_chan = i;
+	*h = (void *)client;
+	return 0;
+}
+
+int32_t adc_channel_close(void *h)
+{
+	struct msm_client_data *client = (struct msm_client_data *)h;
+
+	kfree(client);
+	return 0;
+}
+
+int32_t adc_channel_request_conv(void *h, struct completion *conv_complete_evt)
+{
+	struct msm_client_data *client = (struct msm_client_data *)h;
+	struct msm_adc_platform_data *pdata =
+					msm_adc_drv->pdev->dev.platform_data;
+	struct msm_adc_channels *channel = &pdata->channel[client->adc_chan];
+	struct adc_conv_slot *slot;
+	int ret;
+
+	if (conv_first_request) {
+		ret = pm8058_xoadc_calib_device(channel->adc_dev_instance);
+		if (ret) {
+			pr_err("pmic8058 xoadc calibration failed, retry\n");
+			return ret;
+		}
+		conv_first_request = false;
+	}
+
+	channel->adc_access_fn->adc_slot_request(channel->adc_dev_instance,
+									&slot);
+
+	if (slot) {
+		atomic_inc(&msm_adc_drv->total_outst);
+		mutex_lock(&client->lock);
+		client->num_outstanding++;
+		mutex_unlock(&client->lock);
+
+		slot->conv.result.chan = client->adc_chan;
+		slot->blocking = 0;
+		slot->compk = conv_complete_evt;
+		slot->client = client;
+		slot->adc_request = START_OF_CONV;
+		slot->chan_path = channel->chan_path_type;
+		slot->chan_adc_config = channel->adc_config_type;
+		slot->chan_adc_calib = channel->adc_calib_type;
+		queue_work(msm_adc_drv->wq, &slot->work);
+		return 0;
+	}
+	return -EBUSY;
+}
+
+int32_t adc_channel_read_result(void *h, struct adc_chan_result *chan_result)
+{
+	struct msm_client_data *client = (struct msm_client_data *)h;
+	struct msm_adc_platform_data *pdata =
+					msm_adc_drv->pdev->dev.platform_data;
+	struct msm_adc_channels *channel = pdata->channel;
+	struct adc_conv_slot *slot;
+	int rc = 0;
+
+	mutex_lock(&client->lock);
+
+	slot = list_first_entry(&client->complete_list,
+				struct adc_conv_slot, list);
+	if (!slot) {
+		mutex_unlock(&client->lock);
+		return -ENOMSG;
+	}
+
+	slot->client = NULL;
+	list_del(&slot->list);
+
+	client->num_complete--;
+
+	mutex_unlock(&client->lock);
+
+	*chan_result = slot->conv.result;
+
+	/* restore this slot to reserve */
+	channel[slot->conv.result.chan].adc_access_fn->adc_restore_slot(
+		channel[slot->conv.result.chan].adc_dev_instance, slot);
+
+	return rc;
+}
+
+static void msm_rpc_adc_conv_cb(void *context, u32 param,
+			    void *evt_buf, u32 len)
+{
+	struct dal_adc_result *result = evt_buf;
+	struct dal_conv_slot *slot = context;
+	struct msm_adc_drv *msm_adc = msm_adc_drv;
+
+	memcpy(&slot->result, result, sizeof(slot->result));
+
+	/* for blocking requests, signal complete */
+	if (slot->blocking)
+		complete(&slot->comp);
+
+	/* for non-blocking requests, add slot to the client completed list */
+	else {
+		struct msm_client_data *client = slot->client;
+
+		mutex_lock(&client->lock);
+
+		list_add(&slot->list, &client->complete_list);
+		client->num_complete++;
+		client->num_outstanding--;
+
+		/*
+		 * if the client release has been invoked and this is call
+		 * corresponds to the last request, then signal release
+		 * to complete.
+		 */
+		if (slot->client->online == 0 && client->num_outstanding == 0)
+			wake_up_interruptible_all(&client->outst_wait);
+
+		mutex_unlock(&client->lock);
+
+		wake_up_interruptible_all(&client->data_wait);
+
+		atomic_dec(&msm_adc->total_outst);
+
+		/* verify driver remove has not been invoked */
+		if (atomic_read(&msm_adc->online) == 0 &&
+				atomic_read(&msm_adc->total_outst) == 0)
+			wake_up_interruptible_all(&msm_adc->total_outst_wait);
+	}
+}
+
+void msm_adc_conv_cb(void *context, u32 param,
+			    void *evt_buf, u32 len)
+{
+	struct adc_conv_slot *slot = context;
+	struct msm_adc_drv *msm_adc = msm_adc_drv;
+
+	switch (slot->adc_request) {
+	case START_OF_CONV:
+		slot->adc_request = END_OF_CONV;
+	break;
+	case START_OF_CALIBRATION:
+		slot->adc_request = END_OF_CALIBRATION;
+	break;
+	case END_OF_CALIBRATION:
+	case END_OF_CONV:
+	break;
+	}
+	queue_work(msm_adc->wq, &slot->work);
+}
+
+static void msm_adc_teardown_device_conv(struct platform_device *pdev,
+				    struct adc_dev *adc_dev)
+{
+	struct dal_conv_state *conv_s = &adc_dev->conv;
+	struct msm_adc_drv *msm_adc = platform_get_drvdata(pdev);
+	struct dal_conv_slot *slot;
+	int i;
+
+	for (i = 0; i < MSM_ADC_DEV_MAX_INFLIGHT; i++) {
+		slot = &conv_s->context[i];
+		if (slot->cb_h) {
+			dalrpc_dealloc_cb(msm_adc->dev_h, slot->cb_h);
+			slot->cb_h = NULL;
+		}
+	}
+}
+
+static void msm_rpc_adc_teardown_device(struct platform_device *pdev,
+				    struct adc_dev *adc_dev)
+{
+	struct dal_translation *transl = &adc_dev->transl;
+	int i, num_chans = transl->hwmon_end - transl->hwmon_start + 1;
+
+	if (adc_dev->sens_attr)
+		for (i = 0; i < num_chans; i++)
+			device_remove_file(&pdev->dev,
+					&adc_dev->sens_attr[i].dev_attr);
+
+	msm_adc_teardown_device_conv(pdev, adc_dev);
+
+	kfree(adc_dev->fnames);
+	kfree(adc_dev->sens_attr);
+	kfree(adc_dev);
+}
+
+static void msm_rpc_adc_teardown_devices(struct platform_device *pdev)
+{
+	struct msm_adc_platform_data *pdata = pdev->dev.platform_data;
+	struct msm_adc_drv *msm_adc = platform_get_drvdata(pdev);
+	int i, rc = 0;
+
+	for (i = 0; i < pdata->num_adc; i++) {
+		if (msm_adc->devs[i]) {
+			msm_rpc_adc_teardown_device(pdev, msm_adc->devs[i]);
+			msm_adc->devs[i] = NULL;
+		} else
+			break;
+	}
+
+	if (msm_adc->dev_h) {
+		rc = daldevice_detach(msm_adc->dev_h);
+		if (rc)
+			dev_err(&pdev->dev, "Cannot detach from dal device\n");
+		msm_adc->dev_h = NULL;
+	}
+
+}
+
+static void msm_adc_teardown_device(struct platform_device *pdev,
+				    struct msm_adc_drv *msm_adc)
+{
+	struct msm_adc_platform_data *pdata = pdev->dev.platform_data;
+	int i, num_chans = pdata->num_chan_supported;
+
+	if (pdata->num_chan_supported > 0) {
+		if (msm_adc->sens_attr)
+			for (i = 0; i < num_chans; i++)
+				device_remove_file(&pdev->dev,
+					&msm_adc->sens_attr[i].dev_attr);
+		kfree(msm_adc->sens_attr);
+	}
+}
+
+static void msm_adc_teardown(struct platform_device *pdev)
+{
+	struct msm_adc_drv *msm_adc = platform_get_drvdata(pdev);
+
+	if (!msm_adc)
+		return;
+
+	misc_deregister(&msm_adc->misc);
+
+	if (msm_adc->hwmon)
+		hwmon_device_unregister(msm_adc->hwmon);
+
+	msm_rpc_adc_teardown_devices(pdev);
+	msm_adc_teardown_device(pdev, msm_adc);
+
+	kfree(msm_adc);
+	platform_set_drvdata(pdev, NULL);
+}
+
+static int __devinit msm_adc_device_conv_init(struct msm_adc_drv *msm_adc,
+					      struct adc_dev *adc_dev)
+{
+	struct platform_device *pdev = msm_adc->pdev;
+	struct dal_conv_state *conv_s = &adc_dev->conv;
+	struct dal_conv_slot *slot = conv_s->context;
+	int rc, i;
+
+	sema_init(&conv_s->slot_count, MSM_ADC_DEV_MAX_INFLIGHT);
+	mutex_init(&conv_s->list_lock);
+	INIT_LIST_HEAD(&conv_s->slots);
+
+	for (i = 0; i < MSM_ADC_DEV_MAX_INFLIGHT; i++) {
+		list_add(&slot->list, &conv_s->slots);
+		slot->cb_h = dalrpc_alloc_cb(msm_adc->dev_h,
+					     msm_rpc_adc_conv_cb, slot);
+		if (!slot->cb_h) {
+			dev_err(&pdev->dev, "Unable to allocate DAL callback"
+							" for slot %d\n", i);
+			rc = -ENOMEM;
+			goto dal_err_cb;
+		}
+		init_completion(&slot->comp);
+		slot->idx = i;
+		slot++;
+	}
+
+	return 0;
+
+dal_err_cb:
+	msm_adc_teardown_device_conv(pdev, adc_dev);
+
+	return rc;
+}
+
+static struct sensor_device_attribute msm_rpc_adc_curr_in_attr =
+	SENSOR_ATTR(NULL, S_IRUGO, msm_adc_show_curr, NULL, 0);
+
+static int __devinit msm_rpc_adc_device_init_hwmon(struct platform_device *pdev,
+						struct adc_dev *adc_dev)
+{
+	struct dal_translation *transl = &adc_dev->transl;
+	int i, rc, num_chans = transl->hwmon_end - transl->hwmon_start + 1;
+	const char prefix[] = "curr", postfix[] = "_input";
+	char tmpbuf[5];
+
+	adc_dev->fnames = kzalloc(num_chans * MSM_ADC_MAX_FNAME +
+				  num_chans * sizeof(char *), GFP_KERNEL);
+	if (!adc_dev->fnames) {
+		dev_err(&pdev->dev, "Unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	adc_dev->sens_attr = kzalloc(num_chans *
+			    sizeof(struct sensor_device_attribute), GFP_KERNEL);
+	if (!adc_dev->sens_attr) {
+		dev_err(&pdev->dev, "Unable to allocate memory\n");
+		rc = -ENOMEM;
+		goto hwmon_err_fnames;
+	}
+
+	for (i = 0; i < num_chans; i++) {
+		adc_dev->fnames[i] = (char *)adc_dev->fnames +
+			i * MSM_ADC_MAX_FNAME + num_chans * sizeof(char *);
+		strcpy(adc_dev->fnames[i], prefix);
+		sprintf(tmpbuf, "%d", transl->hwmon_start + i);
+		strcat(adc_dev->fnames[i], tmpbuf);
+		strcat(adc_dev->fnames[i], postfix);
+
+		msm_rpc_adc_curr_in_attr.index = transl->hwmon_start + i;
+		msm_rpc_adc_curr_in_attr.dev_attr.attr.name =
+					adc_dev->fnames[i];
+		memcpy(&adc_dev->sens_attr[i], &msm_rpc_adc_curr_in_attr,
+					sizeof(msm_rpc_adc_curr_in_attr));
+
+		rc = device_create_file(&pdev->dev,
+				&adc_dev->sens_attr[i].dev_attr);
+		if (rc) {
+			dev_err(&pdev->dev, "device_create_file failed for "
+						"dal dev %u chan %d\n",
+					    adc_dev->transl.dal_dev_idx, i);
+			goto hwmon_err_sens;
+		}
+	}
+
+	return 0;
+
+hwmon_err_sens:
+	kfree(adc_dev->sens_attr);
+hwmon_err_fnames:
+	kfree(adc_dev->fnames);
+
+	return rc;
+}
+
+static int __devinit msm_rpc_adc_device_init(struct platform_device *pdev)
+{
+	struct msm_adc_platform_data *pdata = pdev->dev.platform_data;
+	struct msm_adc_drv *msm_adc = platform_get_drvdata(pdev);
+	struct adc_dev *adc_dev;
+	struct adc_dev_spec target;
+	int i, rc;
+	int hwmon_cntr = pdata->num_chan_supported;
+
+	for (i = 0; i < pdata->num_adc; i++) {
+		adc_dev = kzalloc(sizeof(struct adc_dev), GFP_KERNEL);
+		if (!adc_dev) {
+			dev_err(&pdev->dev, "Unable to allocate memory\n");
+			rc = -ENOMEM;
+			goto dev_init_err;
+		}
+
+		msm_adc->devs[i] = adc_dev;
+		adc_dev->name = pdata->dev_names[i];
+
+		rc = msm_adc_device_conv_init(msm_adc, adc_dev);
+		if (rc) {
+			dev_err(&pdev->dev, "DAL device[%s] failed conv init\n",
+							adc_dev->name);
+			goto dev_init_err;
+		}
+
+		if (!pdata->target_hw == MSM_8x25) {
+			/* DAL device lookup */
+			rc = msm_adc_getinputproperties(msm_adc, adc_dev->name,
+								&target);
+			if (rc) {
+				dev_err(&pdev->dev, "No such DAL device[%s]\n",
+							adc_dev->name);
+				goto dev_init_err;
+			}
+
+			adc_dev->transl.dal_dev_idx = target.dal.dev_idx;
+			adc_dev->nchans = target.dal.chan_idx;
+		} else {
+			/* On targets prior to MSM7x30 the remote driver has
+			   only the channel list and no device id. */
+			adc_dev->transl.dal_dev_idx = MSM_8x25_ADC_DEV_ID;
+			adc_dev->nchans = MSM_8x25_CHAN_ID;
+		}
+
+		adc_dev->transl.hwmon_dev_idx = i;
+		adc_dev->transl.hwmon_start = hwmon_cntr;
+		adc_dev->transl.hwmon_end = hwmon_cntr + adc_dev->nchans - 1;
+		hwmon_cntr += adc_dev->nchans;
+
+		rc = msm_rpc_adc_device_init_hwmon(pdev, adc_dev);
+		if (rc)
+			goto dev_init_err;
+	}
+
+	return 0;
+
+dev_init_err:
+	msm_rpc_adc_teardown_devices(pdev);
+	return rc;
+}
+
+static int __devinit msm_rpc_adc_init(struct platform_device *pdev1)
+{
+	struct msm_adc_drv *msm_adc = msm_adc_drv;
+	struct platform_device *pdev = msm_adc->pdev;
+	struct msm_adc_platform_data *pdata = pdev->dev.platform_data;
+	int rc = 0;
+
+	dev_dbg(&pdev->dev, "msm_rpc_adc_init called\n");
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "no platform data?\n");
+		return -EINVAL;
+	}
+
+	mutex_init(&msm_adc->prop_lock);
+
+	rc = daldevice_attach(MSM_ADC_DALRPC_DEVICEID,
+			MSM_ADC_DALRPC_PORT_NAME,
+			MSM_ADC_DALRPC_CPU,
+			&msm_adc->dev_h);
+	if (rc) {
+		dev_err(&pdev->dev, "Cannot attach to dal device\n");
+		return rc;
+	}
+
+	dev_dbg(&pdev->dev, "Attach to dal device Succeeded\n");
+
+	rc = msm_rpc_adc_device_init(pdev);
+	if (rc) {
+		dev_err(&pdev->dev, "msm_adc_dev_init failed\n");
+		goto err_cleanup;
+	}
+
+	init_waitqueue_head(&msm_adc->rpc_total_outst_wait);
+	atomic_set(&msm_adc->rpc_online, 1);
+	atomic_set(&msm_adc->rpc_total_outst, 0);
+	epm_init = true;
+	pr_info("msm_adc successfully registered\n");
+
+	return 0;
+
+err_cleanup:
+	msm_rpc_adc_teardown_devices(pdev);
+
+	return rc;
+}
+
+/*
+ * Process the deferred job
+ */
+void msm_adc_wq_work(struct work_struct *work)
+{
+	struct adc_properties *adc_properties;
+	struct adc_conv_slot *slot = container_of(work,
+						struct adc_conv_slot, work);
+	uint32_t idx = slot->conv.result.chan;
+	struct msm_adc_platform_data *pdata =
+					msm_adc_drv->pdev->dev.platform_data;
+	struct msm_adc_channels *channel = &pdata->channel[idx];
+	int32_t adc_code;
+
+	switch (slot->adc_request) {
+	case START_OF_CONV:
+			channel->adc_access_fn->adc_select_chan_and_start_conv(
+					channel->adc_dev_instance, slot);
+	break;
+	case END_OF_CONV:
+		adc_properties = channel->adc_access_fn->adc_get_properties(
+						channel->adc_dev_instance);
+		if (channel->adc_access_fn->adc_read_adc_code)
+			channel->adc_access_fn->adc_read_adc_code(
+					channel->adc_dev_instance, &adc_code);
+		if (channel->chan_processor)
+			channel->chan_processor(adc_code, adc_properties,
+				&slot->chan_properties, &slot->conv.result);
+		/* Intentionally a fall thru here.  Calibraton does not need
+		to perform channel processing, etc.  However, both
+		end of conversion and end of calibration requires the below
+		fall thru code to be executed. */
+	case END_OF_CALIBRATION:
+		/* for blocking requests, signal complete */
+		if (slot->blocking)
+			complete(&slot->comp);
+		else {
+			struct msm_client_data *client = slot->client;
+
+			mutex_lock(&client->lock);
+
+			if (slot->adc_request == END_OF_CONV) {
+				list_add(&slot->list, &client->complete_list);
+				client->num_complete++;
+			}
+			client->num_outstanding--;
+
+		/*
+		 * if the client release has been invoked and this is call
+		 * corresponds to the last request, then signal release
+		 * to complete.
+		 */
+			if (slot->client->online == 0 &&
+						client->num_outstanding == 0)
+				wake_up_interruptible_all(&client->outst_wait);
+
+			mutex_unlock(&client->lock);
+
+			wake_up_interruptible_all(&client->data_wait);
+
+			atomic_dec(&msm_adc_drv->total_outst);
+
+			/* verify driver remove has not been invoked */
+			if (atomic_read(&msm_adc_drv->online) == 0 &&
+				atomic_read(&msm_adc_drv->total_outst) == 0)
+				wake_up_interruptible_all(
+					&msm_adc_drv->total_outst_wait);
+
+			if (slot->compk) /* Kernel space request */
+				complete(slot->compk);
+			if (slot->adc_request == END_OF_CALIBRATION)
+				channel->adc_access_fn->adc_restore_slot(
+					channel->adc_dev_instance, slot);
+		}
+	break;
+	case START_OF_CALIBRATION: /* code here to please code reviewers
+					to satisfy silly compiler warnings */
+	break;
+	}
+}
+
+static struct sensor_device_attribute msm_adc_curr_in_attr =
+	SENSOR_ATTR(NULL, S_IRUGO, msm_adc_show_curr, NULL, 0);
+
+static int __devinit msm_adc_init_hwmon(struct platform_device *pdev,
+					       struct msm_adc_drv *msm_adc)
+{
+	struct msm_adc_platform_data *pdata = pdev->dev.platform_data;
+	struct msm_adc_channels *channel = pdata->channel;
+	int i, rc, num_chans = pdata->num_chan_supported;
+
+	if (!channel)
+		return -EINVAL;
+
+	msm_adc->sens_attr = kzalloc(num_chans *
+			    sizeof(struct sensor_device_attribute), GFP_KERNEL);
+	if (!msm_adc->sens_attr) {
+		dev_err(&pdev->dev, "Unable to allocate memory\n");
+		rc = -ENOMEM;
+		goto hwmon_err_sens;
+	}
+
+	for (i = 0; i < num_chans; i++) {
+		msm_adc_curr_in_attr.index = i;
+		msm_adc_curr_in_attr.dev_attr.attr.name = channel[i].name;
+		memcpy(&msm_adc->sens_attr[i], &msm_adc_curr_in_attr,
+						sizeof(msm_adc_curr_in_attr));
+
+		rc = device_create_file(&pdev->dev,
+				&msm_adc->sens_attr[i].dev_attr);
+		if (rc) {
+			dev_err(&pdev->dev, "device_create_file failed for "
+					    "dal dev %s\n",
+					    channel[i].name);
+			goto hwmon_err_sens;
+		}
+	}
+
+	return 0;
+
+hwmon_err_sens:
+	kfree(msm_adc->sens_attr);
+
+	return rc;
+}
+
+static struct platform_driver msm_adc_rpcrouter_remote_driver = {
+	.probe          = msm_rpc_adc_init,
+	.driver         = {
+		.name   = MSM_ADC_DALRPC_PORT_NAME,
+		.owner  = THIS_MODULE,
+	},
+};
+
+static int __devinit msm_adc_probe(struct platform_device *pdev)
+{
+	struct msm_adc_platform_data *pdata = pdev->dev.platform_data;
+	struct msm_adc_drv *msm_adc;
+	int rc = 0;
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "no platform data?\n");
+		return -EINVAL;
+	}
+
+	msm_adc = kzalloc(sizeof(struct msm_adc_drv), GFP_KERNEL);
+	if (!msm_adc) {
+		dev_err(&pdev->dev, "Unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(pdev, msm_adc);
+	msm_adc_drv = msm_adc;
+	msm_adc->pdev = pdev;
+
+	if (pdata->target_hw == MSM_8x60 || pdata->target_hw == FSM_9xxx) {
+		rc = msm_adc_init_hwmon(pdev, msm_adc);
+		if (rc) {
+			dev_err(&pdev->dev, "msm_adc_dev_init failed\n");
+			goto err_cleanup;
+		}
+	}
+
+	msm_adc->hwmon = hwmon_device_register(&pdev->dev);
+	if (IS_ERR(msm_adc->hwmon)) {
+		dev_err(&pdev->dev, "hwmon_device_register failed\n");
+		rc = PTR_ERR(msm_adc->hwmon);
+		goto err_cleanup;
+	}
+
+	msm_adc->misc.name = MSM_ADC_DRIVER_NAME;
+	msm_adc->misc.minor = MISC_DYNAMIC_MINOR;
+	msm_adc->misc.fops = &msm_adc_fops;
+
+	if (misc_register(&msm_adc->misc)) {
+		dev_err(&pdev->dev, "Unable to register misc device!\n");
+		goto err_cleanup;
+	}
+
+	init_waitqueue_head(&msm_adc->total_outst_wait);
+	atomic_set(&msm_adc->online, 1);
+	atomic_set(&msm_adc->total_outst, 0);
+
+	msm_adc->wq = create_singlethread_workqueue("msm_adc");
+	if (!msm_adc->wq)
+		goto err_cleanup;
+
+	if (pdata->num_adc > 0) {
+		if (pdata->target_hw == MSM_8x60)
+			platform_driver_register(
+				&msm_adc_rpcrouter_remote_driver);
+		else
+			msm_rpc_adc_init(pdev);
+	}
+	conv_first_request = true;
+
+	pr_info("msm_adc successfully registered\n");
+
+	return 0;
+
+err_cleanup:
+	msm_adc_teardown(pdev);
+
+	return rc;
+}
+
+static int __devexit msm_adc_remove(struct platform_device *pdev)
+{
+	int rc;
+
+	struct msm_adc_drv *msm_adc = platform_get_drvdata(pdev);
+
+	atomic_set(&msm_adc->online, 0);
+
+	atomic_set(&msm_adc->rpc_online, 0);
+
+	misc_deregister(&msm_adc->misc);
+
+	hwmon_device_unregister(msm_adc->hwmon);
+	msm_adc->hwmon = NULL;
+
+	/*
+	 * We may still have outstanding transactions in flight that have not
+	 * completed. Make sure they're completed before tearing down.
+	 */
+	rc = wait_event_interruptible(msm_adc->total_outst_wait,
+				      atomic_read(&msm_adc->total_outst) == 0);
+	if (rc) {
+		pr_err("%s: wait_event_interruptible failed rc = %d\n",
+								__func__, rc);
+		return rc;
+	}
+
+	rc = wait_event_interruptible(msm_adc->rpc_total_outst_wait,
+	      atomic_read(&msm_adc->rpc_total_outst) == 0);
+	if (rc) {
+		pr_err("%s: wait_event_interruptible failed rc = %d\n",
+								__func__, rc);
+		return rc;
+	}
+
+	msm_adc_teardown(pdev);
+
+	pr_info("msm_adc unregistered\n");
+
+	return 0;
+}
+
+static struct platform_driver msm_adc_driver = {
+	.probe = msm_adc_probe,
+	.remove = __devexit_p(msm_adc_remove),
+	.driver = {
+		.name = MSM_ADC_DRIVER_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init msm_adc_init(void)
+{
+	return platform_driver_register(&msm_adc_driver);
+}
+module_init(msm_adc_init);
+
+static void __exit msm_adc_exit(void)
+{
+	platform_driver_unregister(&msm_adc_driver);
+}
+module_exit(msm_adc_exit);
+
+MODULE_DESCRIPTION("MSM ADC Driver");
+MODULE_ALIAS("platform:msm_adc");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.1");
diff --git a/drivers/hwmon/pm8xxx-adc-scale.c b/drivers/hwmon/pm8xxx-adc-scale.c
new file mode 100644
index 0000000..fb2f1d5
--- /dev/null
+++ b/drivers/hwmon/pm8xxx-adc-scale.c
@@ -0,0 +1,738 @@
+/*
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/mfd/pm8xxx/pm8xxx-adc.h>
+#define KELVINMIL_DEGMIL	273160
+
+/* Units for temperature below (on x axis) is in 0.1DegC as
+   required by the battery driver. Note the resolution used
+   here to compute the table was done for DegC to milli-volts.
+   In consideration to limit the size of the table for the given
+   temperature range below, the result is linearly interpolated
+   and provided to the battery driver in the units desired for
+   their framework which is 0.1DegC. True resolution of 0.1DegC
+   will result in the below table size to increase by 10 times */
+static const struct pm8xxx_adc_map_pt adcmap_btm_threshold[] = {
+	{-300,	1642},
+	{-200,	1544},
+	{-100,	1414},
+	{0,	1260},
+	{10,	1244},
+	{20,	1228},
+	{30,	1212},
+	{40,	1195},
+	{50,	1179},
+	{60,	1162},
+	{70,	1146},
+	{80,	1129},
+	{90,	1113},
+	{100,	1097},
+	{110,	1080},
+	{120,	1064},
+	{130,	1048},
+	{140,	1032},
+	{150,	1016},
+	{160,	1000},
+	{170,	985},
+	{180,	969},
+	{190,	954},
+	{200,	939},
+	{210,	924},
+	{220,	909},
+	{230,	894},
+	{240,	880},
+	{250,	866},
+	{260,	852},
+	{270,	838},
+	{280,	824},
+	{290,	811},
+	{300,	798},
+	{310,	785},
+	{320,	773},
+	{330,	760},
+	{340,	748},
+	{350,	736},
+	{360,	725},
+	{370,	713},
+	{380,	702},
+	{390,	691},
+	{400,	681},
+	{410,	670},
+	{420,	660},
+	{430,	650},
+	{440,	640},
+	{450,	631},
+	{460,	622},
+	{470,	613},
+	{480,	604},
+	{490,	595},
+	{500,	587},
+	{510,	579},
+	{520,	571},
+	{530,	563},
+	{540,	556},
+	{550,	548},
+	{560,	541},
+	{570,	534},
+	{580,	527},
+	{590,	521},
+	{600,	514},
+	{610,	508},
+	{620,	502},
+	{630,	496},
+	{640,	490},
+	{650,	485},
+	{660,	281},
+	{670,	274},
+	{680,	267},
+	{690,	260},
+	{700,	254},
+	{710,	247},
+	{720,	241},
+	{730,	235},
+	{740,	229},
+	{750,	224},
+	{760,	218},
+	{770,	213},
+	{780,	208},
+	{790,	203}
+};
+
+static const struct pm8xxx_adc_map_pt adcmap_pa_therm[] = {
+	{1677,	-30},
+	{1671,	-29},
+	{1663,	-28},
+	{1656,	-27},
+	{1648,	-26},
+	{1640,	-25},
+	{1632,	-24},
+	{1623,	-23},
+	{1615,	-22},
+	{1605,	-21},
+	{1596,	-20},
+	{1586,	-19},
+	{1576,	-18},
+	{1565,	-17},
+	{1554,	-16},
+	{1543,	-15},
+	{1531,	-14},
+	{1519,	-13},
+	{1507,	-12},
+	{1494,	-11},
+	{1482,	-10},
+	{1468,	-9},
+	{1455,	-8},
+	{1441,	-7},
+	{1427,	-6},
+	{1412,	-5},
+	{1398,	-4},
+	{1383,	-3},
+	{1367,	-2},
+	{1352,	-1},
+	{1336,	0},
+	{1320,	1},
+	{1304,	2},
+	{1287,	3},
+	{1271,	4},
+	{1254,	5},
+	{1237,	6},
+	{1219,	7},
+	{1202,	8},
+	{1185,	9},
+	{1167,	10},
+	{1149,	11},
+	{1131,	12},
+	{1114,	13},
+	{1096,	14},
+	{1078,	15},
+	{1060,	16},
+	{1042,	17},
+	{1024,	18},
+	{1006,	19},
+	{988,	20},
+	{970,	21},
+	{952,	22},
+	{934,	23},
+	{917,	24},
+	{899,	25},
+	{882,	26},
+	{865,	27},
+	{848,	28},
+	{831,	29},
+	{814,	30},
+	{797,	31},
+	{781,	32},
+	{764,	33},
+	{748,	34},
+	{732,	35},
+	{717,	36},
+	{701,	37},
+	{686,	38},
+	{671,	39},
+	{656,	40},
+	{642,	41},
+	{627,	42},
+	{613,	43},
+	{599,	44},
+	{586,	45},
+	{572,	46},
+	{559,	47},
+	{546,	48},
+	{534,	49},
+	{522,	50},
+	{509,	51},
+	{498,	52},
+	{486,	53},
+	{475,	54},
+	{463,	55},
+	{452,	56},
+	{442,	57},
+	{431,	58},
+	{421,	59},
+	{411,	60},
+	{401,	61},
+	{392,	62},
+	{383,	63},
+	{374,	64},
+	{365,	65},
+	{356,	66},
+	{348,	67},
+	{339,	68},
+	{331,	69},
+	{323,	70},
+	{316,	71},
+	{308,	72},
+	{301,	73},
+	{294,	74},
+	{287,	75},
+	{280,	76},
+	{273,	77},
+	{267,	78},
+	{261,	79},
+	{255,	80},
+	{249,	81},
+	{243,	82},
+	{237,	83},
+	{232,	84},
+	{226,	85},
+	{221,	86},
+	{216,	87},
+	{211,	88},
+	{206,	89},
+	{201,	90}
+};
+
+static const struct pm8xxx_adc_map_pt adcmap_ntcg_104ef_104fb[] = {
+	{696483,	-40960},
+	{649148,	-39936},
+	{605368,	-38912},
+	{564809,	-37888},
+	{527215,	-36864},
+	{492322,	-35840},
+	{460007,	-34816},
+	{429982,	-33792},
+	{402099,	-32768},
+	{376192,	-31744},
+	{352075,	-30720},
+	{329714,	-29696},
+	{308876,	-28672},
+	{289480,	-27648},
+	{271417,	-26624},
+	{254574,	-25600},
+	{238903,	-24576},
+	{224276,	-23552},
+	{210631,	-22528},
+	{197896,	-21504},
+	{186007,	-20480},
+	{174899,	-19456},
+	{164521,	-18432},
+	{154818,	-17408},
+	{145744,	-16384},
+	{137265,	-15360},
+	{129307,	-14336},
+	{121866,	-13312},
+	{114896,	-12288},
+	{108365,	-11264},
+	{102252,	-10240},
+	{96499,		-9216},
+	{91111,		-8192},
+	{86055,		-7168},
+	{81308,		-6144},
+	{76857,		-5120},
+	{72660,		-4096},
+	{68722,		-3072},
+	{65020,		-2048},
+	{61538,		-1024},
+	{58261,		0},
+	{55177,		1024},
+	{52274,		2048},
+	{49538,		3072},
+	{46962,		4096},
+	{44531,		5120},
+	{42243,		6144},
+	{40083,		7168},
+	{38045,		8192},
+	{36122,		9216},
+	{34308,		10240},
+	{32592,		11264},
+	{30972,		12288},
+	{29442,		13312},
+	{27995,		14336},
+	{26624,		15360},
+	{25333,		16384},
+	{24109,		17408},
+	{22951,		18432},
+	{21854,		19456},
+	{20807,		20480},
+	{19831,		21504},
+	{18899,		22528},
+	{18016,		23552},
+	{17178,		24576},
+	{16384,		25600},
+	{15631,		26624},
+	{14916,		27648},
+	{14237,		28672},
+	{13593,		29696},
+	{12976,		30720},
+	{12400,		31744},
+	{11848,		32768},
+	{11324,		33792},
+	{10825,		34816},
+	{10354,		35840},
+	{9900,		36864},
+	{9471,		37888},
+	{9062,		38912},
+	{8674,		39936},
+	{8306,		40960},
+	{7951,		41984},
+	{7616,		43008},
+	{7296,		44032},
+	{6991,		45056},
+	{6701,		46080},
+	{6424,		47104},
+	{6160,		48128},
+	{5908,		49152},
+	{5667,		50176},
+	{5439,		51200},
+	{5219,		52224},
+	{5010,		53248},
+	{4810,		54272},
+	{4619,		55296},
+	{4440,		56320},
+	{4263,		57344},
+	{4097,		58368},
+	{3938,		59392},
+	{3785,		60416},
+	{3637,		61440},
+	{3501,		62464},
+	{3368,		63488},
+	{3240,		64512},
+	{3118,		65536},
+	{2998,		66560},
+	{2889,		67584},
+	{2782,		68608},
+	{2680,		69632},
+	{2581,		70656},
+	{2490,		71680},
+	{2397,		72704},
+	{2310,		73728},
+	{2227,		74752},
+	{2147,		75776},
+	{2064,		76800},
+	{1998,		77824},
+	{1927,		78848},
+	{1860,		79872},
+	{1795,		80896},
+	{1736,		81920},
+	{1673,		82944},
+	{1615,		83968},
+	{1560,		84992},
+	{1507,		86016},
+	{1456,		87040},
+	{1407,		88064},
+	{1360,		89088},
+	{1314,		90112},
+	{1271,		91136},
+	{1228,		92160},
+	{1189,		93184},
+	{1150,		94208},
+	{1112,		95232},
+	{1076,		96256},
+	{1042,		97280},
+	{1008,		98304},
+	{976,		99328},
+	{945,		100352},
+	{915,		101376},
+	{886,		102400},
+	{859,		103424},
+	{832,		104448},
+	{807,		105472},
+	{782,		106496},
+	{756,		107520},
+	{735,		108544},
+	{712,		109568},
+	{691,		110592},
+	{670,		111616},
+	{650,		112640},
+	{631,		113664},
+	{612,		114688},
+	{594,		115712},
+	{577,		116736},
+	{560,		117760},
+	{544,		118784},
+	{528,		119808},
+	{513,		120832},
+	{498,		121856},
+	{483,		122880},
+	{470,		123904},
+	{457,		124928},
+	{444,		125952},
+	{431,		126976},
+	{419,		128000}
+};
+
+static int32_t pm8xxx_adc_map_linear(const struct pm8xxx_adc_map_pt *pts,
+		uint32_t tablesize, int32_t input, int64_t *output)
+{
+	bool descending = 1;
+	uint32_t i = 0;
+
+	if ((pts == NULL) || (output == NULL))
+		return -EINVAL;
+
+	/* Check if table is descending or ascending */
+	if (tablesize > 1) {
+		if (pts[0].x < pts[1].x)
+			descending = 0;
+	}
+
+	while (i < tablesize) {
+		if ((descending == 1) && (pts[i].x < input)) {
+			/* table entry is less than measured
+				value and table is descending, stop */
+			break;
+		} else if ((descending == 0) &&
+				(pts[i].x > input)) {
+			/* table entry is greater than measured
+				value and table is ascending, stop */
+			break;
+		} else {
+			i++;
+		}
+	}
+
+	if (i == 0)
+		*output = pts[0].y;
+	else if (i == tablesize)
+		*output = pts[tablesize-1].y;
+	else {
+		/* result is between search_index and search_index-1 */
+		/* interpolate linearly */
+		*output = (((int32_t) ((pts[i].y - pts[i-1].y)*
+			(input - pts[i-1].x))/
+			(pts[i].x - pts[i-1].x))+
+			pts[i-1].y);
+	}
+
+	return 0;
+}
+
+static int32_t pm8xxx_adc_map_batt_therm(const struct pm8xxx_adc_map_pt *pts,
+		uint32_t tablesize, int32_t input, int64_t *output)
+{
+	bool descending = 1;
+	uint32_t i = 0;
+
+	if ((pts == NULL) || (output == NULL))
+		return -EINVAL;
+
+	/* Check if table is descending or ascending */
+	if (tablesize > 1) {
+		if (pts[0].y < pts[1].y)
+			descending = 0;
+	}
+
+	while (i < tablesize) {
+		if ((descending == 1) && (pts[i].y < input)) {
+			/* table entry is less than measured
+				value and table is descending, stop */
+			break;
+		} else if ((descending == 0) && (pts[i].y > input)) {
+			/* table entry is greater than measured
+				value and table is ascending, stop */
+			break;
+		} else {
+			i++;
+		}
+	}
+
+	if (i == 0) {
+		*output = pts[0].x;
+	} else if (i == tablesize) {
+		*output = pts[tablesize-1].x;
+	} else {
+		/* result is between search_index and search_index-1 */
+		/* interpolate linearly */
+		*output = (((int32_t) ((pts[i].x - pts[i-1].x)*
+			(input - pts[i-1].y))/
+			(pts[i].y - pts[i-1].y))+
+			pts[i-1].x);
+	}
+
+	return 0;
+}
+
+int32_t pm8xxx_adc_scale_default(int32_t adc_code,
+		const struct pm8xxx_adc_properties *adc_properties,
+		const struct pm8xxx_adc_chan_properties *chan_properties,
+		struct pm8xxx_adc_chan_result *adc_chan_result)
+{
+	bool negative_rawfromoffset = 0, negative_offset = 0;
+	int64_t scale_voltage = 0;
+
+	if (!chan_properties || !chan_properties->offset_gain_numerator ||
+		!chan_properties->offset_gain_denominator || !adc_properties
+		|| !adc_chan_result)
+		return -EINVAL;
+
+	scale_voltage = (adc_code -
+		chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].adc_gnd)
+		* chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dx;
+	if (scale_voltage < 0) {
+		negative_offset = 1;
+		scale_voltage = -scale_voltage;
+	}
+	do_div(scale_voltage,
+		chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dy);
+	if (negative_offset)
+		scale_voltage = -scale_voltage;
+	scale_voltage += chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dx;
+
+	if (scale_voltage < 0) {
+		if (adc_properties->bipolar) {
+			scale_voltage = -scale_voltage;
+			negative_rawfromoffset = 1;
+		} else {
+			scale_voltage = 0;
+		}
+	}
+
+	adc_chan_result->measurement = scale_voltage *
+				chan_properties->offset_gain_denominator;
+
+	/* do_div only perform positive integer division! */
+	do_div(adc_chan_result->measurement,
+				chan_properties->offset_gain_numerator);
+
+	if (negative_rawfromoffset)
+		adc_chan_result->measurement = -adc_chan_result->measurement;
+
+	/* Note: adc_chan_result->measurement is in the unit of
+	 * adc_properties.adc_reference. For generic channel processing,
+	 * channel measurement is a scale/ratio relative to the adc
+	 * reference input */
+	adc_chan_result->physical = adc_chan_result->measurement;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_adc_scale_default);
+
+static int64_t pm8xxx_adc_scale_ratiometric_calib(int32_t adc_code,
+		const struct pm8xxx_adc_properties *adc_properties,
+		const struct pm8xxx_adc_chan_properties *chan_properties)
+{
+	int64_t adc_voltage = 0;
+	bool negative_offset = 0;
+
+	if (!chan_properties || !chan_properties->offset_gain_numerator ||
+		!chan_properties->offset_gain_denominator || !adc_properties)
+		return -EINVAL;
+
+	adc_voltage = (adc_code -
+		chan_properties->adc_graph[ADC_CALIB_RATIOMETRIC].adc_gnd)
+		* adc_properties->adc_vdd_reference;
+	if (adc_voltage < 0) {
+		negative_offset = 1;
+		adc_voltage = -adc_voltage;
+	}
+	do_div(adc_voltage,
+		chan_properties->adc_graph[ADC_CALIB_RATIOMETRIC].dy);
+	if (negative_offset)
+		adc_voltage = -adc_voltage;
+
+	return adc_voltage;
+}
+
+int32_t pm8xxx_adc_scale_batt_therm(int32_t adc_code,
+		const struct pm8xxx_adc_properties *adc_properties,
+		const struct pm8xxx_adc_chan_properties *chan_properties,
+		struct pm8xxx_adc_chan_result *adc_chan_result)
+{
+	int64_t bat_voltage = 0;
+
+	bat_voltage = pm8xxx_adc_scale_ratiometric_calib(adc_code,
+			adc_properties, chan_properties);
+
+	return pm8xxx_adc_map_batt_therm(
+			adcmap_btm_threshold,
+			ARRAY_SIZE(adcmap_btm_threshold),
+			bat_voltage,
+			&adc_chan_result->physical);
+}
+EXPORT_SYMBOL_GPL(pm8xxx_adc_scale_batt_therm);
+
+int32_t pm8xxx_adc_scale_pa_therm(int32_t adc_code,
+		const struct pm8xxx_adc_properties *adc_properties,
+		const struct pm8xxx_adc_chan_properties *chan_properties,
+		struct pm8xxx_adc_chan_result *adc_chan_result)
+{
+	int64_t pa_voltage = 0;
+
+	pa_voltage = pm8xxx_adc_scale_ratiometric_calib(adc_code,
+			adc_properties, chan_properties);
+
+	return pm8xxx_adc_map_linear(
+			adcmap_pa_therm,
+			ARRAY_SIZE(adcmap_pa_therm),
+			pa_voltage,
+			&adc_chan_result->physical);
+}
+EXPORT_SYMBOL_GPL(pm8xxx_adc_scale_pa_therm);
+
+int32_t pm8xxx_adc_scale_batt_id(int32_t adc_code,
+		const struct pm8xxx_adc_properties *adc_properties,
+		const struct pm8xxx_adc_chan_properties *chan_properties,
+		struct pm8xxx_adc_chan_result *adc_chan_result)
+{
+	int64_t batt_id_voltage = 0;
+
+	batt_id_voltage = pm8xxx_adc_scale_ratiometric_calib(adc_code,
+			adc_properties, chan_properties);
+	adc_chan_result->physical = batt_id_voltage;
+	adc_chan_result->physical = adc_chan_result->measurement;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_adc_scale_batt_id);
+
+int32_t pm8xxx_adc_scale_pmic_therm(int32_t adc_code,
+		const struct pm8xxx_adc_properties *adc_properties,
+		const struct pm8xxx_adc_chan_properties *chan_properties,
+		struct pm8xxx_adc_chan_result *adc_chan_result)
+{
+	int64_t pmic_voltage = 0;
+	bool negative_offset = 0;
+
+	if (!chan_properties || !chan_properties->offset_gain_numerator ||
+		!chan_properties->offset_gain_denominator || !adc_properties
+		|| !adc_chan_result)
+		return -EINVAL;
+
+	pmic_voltage = (adc_code -
+		chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].adc_gnd)
+		* chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dx;
+	if (pmic_voltage < 0) {
+		negative_offset = 1;
+		pmic_voltage = -pmic_voltage;
+	}
+	do_div(pmic_voltage,
+		chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dy);
+	if (negative_offset)
+		pmic_voltage = -pmic_voltage;
+	pmic_voltage += chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dx;
+
+	if (pmic_voltage > 0) {
+		/* 2mV/K */
+		adc_chan_result->measurement = pmic_voltage*
+			chan_properties->offset_gain_denominator;
+
+		do_div(adc_chan_result->measurement,
+			chan_properties->offset_gain_numerator * 2);
+	} else {
+		adc_chan_result->measurement = 0;
+	}
+	/* Change to .001 deg C */
+	adc_chan_result->measurement -= KELVINMIL_DEGMIL;
+	adc_chan_result->physical = (int32_t)adc_chan_result->measurement;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_adc_scale_pmic_therm);
+
+/* Scales the ADC code to 0.001 degrees C using the map
+ * table for the XO thermistor.
+ */
+int32_t pm8xxx_adc_tdkntcg_therm(int32_t adc_code,
+		const struct pm8xxx_adc_properties *adc_properties,
+		const struct pm8xxx_adc_chan_properties *chan_properties,
+		struct pm8xxx_adc_chan_result *adc_chan_result)
+{
+	int64_t xo_thm = 0;
+
+	if (!chan_properties || !chan_properties->offset_gain_numerator ||
+		!chan_properties->offset_gain_denominator || !adc_properties
+		|| !adc_chan_result)
+		return -EINVAL;
+
+	xo_thm = pm8xxx_adc_scale_ratiometric_calib(adc_code,
+			adc_properties, chan_properties);
+	xo_thm <<= 4;
+	pm8xxx_adc_map_linear(adcmap_ntcg_104ef_104fb,
+		ARRAY_SIZE(adcmap_ntcg_104ef_104fb),
+		xo_thm, &adc_chan_result->physical);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_adc_tdkntcg_therm);
+
+int32_t pm8xxx_adc_batt_scaler(struct pm8xxx_adc_arb_btm_param *btm_param,
+		const struct pm8xxx_adc_properties *adc_properties,
+		const struct pm8xxx_adc_chan_properties *chan_properties)
+{
+	int rc;
+
+	rc = pm8xxx_adc_map_linear(
+		adcmap_btm_threshold,
+		ARRAY_SIZE(adcmap_btm_threshold),
+		(btm_param->low_thr_temp),
+		&btm_param->low_thr_voltage);
+	if (rc)
+		return rc;
+
+	btm_param->low_thr_voltage *=
+		chan_properties->adc_graph[ADC_CALIB_RATIOMETRIC].dy;
+	do_div(btm_param->low_thr_voltage, adc_properties->adc_vdd_reference);
+	btm_param->low_thr_voltage +=
+		chan_properties->adc_graph[ADC_CALIB_RATIOMETRIC].adc_gnd;
+
+	rc = pm8xxx_adc_map_linear(
+		adcmap_btm_threshold,
+		ARRAY_SIZE(adcmap_btm_threshold),
+		(btm_param->high_thr_temp),
+		&btm_param->high_thr_voltage);
+	if (rc)
+		return rc;
+
+	btm_param->high_thr_voltage *=
+		chan_properties->adc_graph[ADC_CALIB_RATIOMETRIC].dy;
+	do_div(btm_param->high_thr_voltage, adc_properties->adc_vdd_reference);
+	btm_param->high_thr_voltage +=
+		chan_properties->adc_graph[ADC_CALIB_RATIOMETRIC].adc_gnd;
+
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_adc_batt_scaler);
diff --git a/drivers/hwmon/pm8xxx-adc.c b/drivers/hwmon/pm8xxx-adc.c
new file mode 100644
index 0000000..6bef3d3
--- /dev/null
+++ b/drivers/hwmon/pm8xxx-adc.c
@@ -0,0 +1,1307 @@
+/*
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Qualcomm's PM8921/PM8018 ADC Arbiter driver
+ */
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/hwmon.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/wakelock.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/mfd/pm8xxx/mpp.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mfd/pm8xxx/pm8xxx-adc.h>
+
+/* User Bank register set */
+#define PM8XXX_ADC_ARB_USRP_CNTRL1			0x197
+#define PM8XXX_ADC_ARB_USRP_CNTRL1_EN_ARB		BIT(0)
+#define PM8XXX_ADC_ARB_USRP_CNTRL1_RSV1			BIT(1)
+#define PM8XXX_ADC_ARB_USRP_CNTRL1_RSV2			BIT(2)
+#define PM8XXX_ADC_ARB_USRP_CNTRL1_RSV3			BIT(3)
+#define PM8XXX_ADC_ARB_USRP_CNTRL1_RSV4			BIT(4)
+#define PM8XXX_ADC_ARB_USRP_CNTRL1_RSV5			BIT(5)
+#define PM8XXX_ADC_ARB_USRP_CNTRL1_EOC			BIT(6)
+#define PM8XXX_ADC_ARB_USRP_CNTRL1_REQ			BIT(7)
+
+#define PM8XXX_ADC_ARB_USRP_AMUX_CNTRL			0x198
+#define PM8XXX_ADC_ARB_USRP_AMUX_CNTRL_RSV0		BIT(0)
+#define PM8XXX_ADC_ARB_USRP_AMUX_CNTRL_RSV1		BIT(1)
+#define PM8XXX_ADC_ARB_USRP_AMUX_CNTRL_PREMUX0		BIT(2)
+#define PM8XXX_ADC_ARB_USRP_AMUX_CNTRL_PREMUX1		BIT(3)
+#define PM8XXX_ADC_ARB_USRP_AMUX_CNTRL_SEL0		BIT(4)
+#define PM8XXX_ADC_ARB_USRP_AMUX_CNTRL_SEL1		BIT(5)
+#define PM8XXX_ADC_ARB_USRP_AMUX_CNTRL_SEL2		BIT(6)
+#define PM8XXX_ADC_ARB_USRP_AMUX_CNTRL_SEL3		BIT(7)
+
+#define PM8XXX_ADC_ARB_USRP_ANA_PARAM			0x199
+#define PM8XXX_ADC_ARB_USRP_DIG_PARAM			0x19A
+#define PM8XXX_ADC_ARB_USRP_DIG_PARAM_SEL_SHIFT0	BIT(0)
+#define PM8XXX_ADC_ARB_USRP_DIG_PARAM_SEL_SHIFT1	BIT(1)
+#define PM8XXX_ADC_ARB_USRP_DIG_PARAM_CLK_RATE0		BIT(2)
+#define PM8XXX_ADC_ARB_USRP_DIG_PARAM_CLK_RATE1		BIT(3)
+#define PM8XXX_ADC_ARB_USRP_DIG_PARAM_EOC		BIT(4)
+#define PM8XXX_ADC_ARB_USRP_DIG_PARAM_DEC_RATE0		BIT(5)
+#define PM8XXX_ADC_ARB_USRP_DIG_PARAM_DEC_RATE1		BIT(6)
+#define PM8XXX_ADC_ARB_USRP_DIG_PARAM_EN		BIT(7)
+
+#define PM8XXX_ADC_ARB_USRP_RSV				0x19B
+#define PM8XXX_ADC_ARB_USRP_RSV_RST			BIT(0)
+#define PM8XXX_ADC_ARB_USRP_RSV_DTEST0			BIT(1)
+#define PM8XXX_ADC_ARB_USRP_RSV_DTEST1			BIT(2)
+#define PM8XXX_ADC_ARB_USRP_RSV_OP			BIT(3)
+#define PM8XXX_ADC_ARB_USRP_RSV_IP_SEL0			BIT(4)
+#define PM8XXX_ADC_ARB_USRP_RSV_IP_SEL1			BIT(5)
+#define PM8XXX_ADC_ARB_USRP_RSV_IP_SEL2			BIT(6)
+#define PM8XXX_ADC_ARB_USRP_RSV_TRM			BIT(7)
+
+#define PM8XXX_ADC_ARB_USRP_DATA0			0x19D
+#define PM8XXX_ADC_ARB_USRP_DATA1			0x19C
+
+#define PM8XXX_ADC_ARB_BTM_CNTRL1			0x17e
+#define PM8XXX_ADC_ARB_BTM_CNTRL1_EN_BTM		BIT(0)
+#define PM8XXX_ADC_ARB_BTM_CNTRL1_SEL_OP_MODE		BIT(1)
+#define PM8XXX_ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL1	BIT(2)
+#define PM8XXX_ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL2	BIT(3)
+#define PM8XXX_ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL3	BIT(4)
+#define PM8XXX_ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL4	BIT(5)
+#define PM8XXX_ADC_ARB_BTM_CNTRL1_EOC			BIT(6)
+#define PM8XXX_ADC_ARB_BTM_CNTRL1_REQ			BIT(7)
+
+#define PM8XXX_ADC_ARB_BTM_CNTRL2			0x18c
+#define PM8XXX_ADC_ARB_BTM_AMUX_CNTRL			0x17f
+#define PM8XXX_ADC_ARB_BTM_ANA_PARAM			0x180
+#define PM8XXX_ADC_ARB_BTM_DIG_PARAM			0x181
+#define PM8XXX_ADC_ARB_BTM_RSV				0x182
+#define PM8XXX_ADC_ARB_BTM_DATA1			0x183
+#define PM8XXX_ADC_ARB_BTM_DATA0			0x184
+#define PM8XXX_ADC_ARB_BTM_BAT_COOL_THR1		0x185
+#define PM8XXX_ADC_ARB_BTM_BAT_COOL_THR0		0x186
+#define PM8XXX_ADC_ARB_BTM_BAT_WARM_THR1		0x187
+#define PM8XXX_ADC_ARB_BTM_BAT_WARM_THR0		0x188
+
+#define PM8XXX_ADC_ARB_ANA_DIG				0xa0
+#define PM8XXX_ADC_BTM_RSV				0x10
+#define PM8XXX_ADC_AMUX_MPP_SEL				2
+#define PM8XXX_ADC_AMUX_SEL				4
+#define PM8XXX_ADC_RSV_IP_SEL				4
+#define PM8XXX_ADC_BTM_CHANNEL_SEL			4
+#define PM8XXX_MAX_CHANNEL_PROPERTIES			2
+#define PM8XXX_ADC_IRQ_0				0
+#define PM8XXX_ADC_IRQ_1				1
+#define PM8XXX_ADC_IRQ_2				2
+#define PM8XXX_ADC_BTM_INTERVAL_SEL_MASK		0xF
+#define PM8XXX_ADC_BTM_INTERVAL_SEL_SHIFT		2
+#define PM8XXX_ADC_BTM_DECIMATION_SEL			5
+#define PM8XXX_ADC_MUL					10
+#define PM8XXX_ADC_CONV_TIME_MIN			2000
+#define PM8XXX_ADC_CONV_TIME_MAX			2100
+#define PM8XXX_ADC_MPP_SETTLE_TIME_MIN			200
+#define PM8XXX_ADC_MPP_SETTLE_TIME_MAX			200
+#define PM8XXX_ADC_PA_THERM_VREG_UV_MIN			1800000
+#define PM8XXX_ADC_PA_THERM_VREG_UV_MAX			1800000
+#define PM8XXX_ADC_PA_THERM_VREG_UA_LOAD		100000
+#define PM8XXX_ADC_HWMON_NAME_LENGTH			32
+#define PM8XXX_ADC_BTM_INTERVAL_MAX			0x14
+
+struct pm8xxx_adc {
+	struct device				*dev;
+	struct pm8xxx_adc_properties		*adc_prop;
+	int					adc_irq;
+	struct mutex				adc_lock;
+	struct mutex				mpp_adc_lock;
+	spinlock_t				btm_lock;
+	uint32_t				adc_num_board_channel;
+	struct completion			adc_rslt_completion;
+	struct pm8xxx_adc_amux			*adc_channel;
+	int					btm_warm_irq;
+	int					btm_cool_irq;
+	struct dentry				*dent;
+	struct work_struct			warm_work;
+	struct work_struct			cool_work;
+	uint32_t				mpp_base;
+	struct device				*hwmon;
+	struct wake_lock			adc_wakelock;
+	int					msm_suspend_check;
+	struct pm8xxx_adc_amux_properties	*conv;
+	struct pm8xxx_adc_arb_btm_param		batt;
+	struct sensor_device_attribute		sens_attr[0];
+};
+
+struct pm8xxx_adc_amux_properties {
+	uint32_t				amux_channel;
+	uint32_t				decimation;
+	uint32_t				amux_ip_rsv;
+	uint32_t				amux_mpp_channel;
+	struct pm8xxx_adc_chan_properties	chan_prop[0];
+};
+
+static const struct pm8xxx_adc_scaling_ratio pm8xxx_amux_scaling_ratio[] = {
+	{1, 1},
+	{1, 3},
+	{1, 4},
+	{1, 6}
+};
+
+static struct pm8xxx_adc *pmic_adc;
+static struct regulator *pa_therm;
+
+static struct pm8xxx_adc_scale_fn adc_scale_fn[] = {
+	[ADC_SCALE_DEFAULT] = {pm8xxx_adc_scale_default},
+	[ADC_SCALE_BATT_THERM] = {pm8xxx_adc_scale_batt_therm},
+	[ADC_SCALE_PA_THERM] = {pm8xxx_adc_scale_pa_therm},
+	[ADC_SCALE_PMIC_THERM] = {pm8xxx_adc_scale_pmic_therm},
+	[ADC_SCALE_XOTHERM] = {pm8xxx_adc_tdkntcg_therm},
+};
+
+/* On PM8921 ADC the MPP needs to first be configured
+as an analog input to the AMUX pre-mux channel before
+issuing a read request. PM8921 MPP 8 is mapped to AMUX8
+and is common between remote processor's.
+On PM8018 ADC the MPP is directly connected to the AMUX
+pre-mux. Therefore clients of the PM8018 MPP do not need
+to configure the MPP as an analog input to the pre-mux.
+Clients can directly issue request on the pre-mux AMUX
+channel to read the ADC on the MPP */
+static struct pm8xxx_mpp_config_data pm8xxx_adc_mpp_config = {
+	.type		= PM8XXX_MPP_TYPE_A_INPUT,
+	/* AMUX6 is dedicated to be used for apps processor */
+	.level		= PM8XXX_MPP_AIN_AMUX_CH6,
+	.control	= PM8XXX_MPP_AOUT_CTRL_DISABLE,
+};
+
+/* MPP Configuration for default settings */
+static struct pm8xxx_mpp_config_data pm8xxx_adc_mpp_unconfig = {
+	.type		= PM8XXX_MPP_TYPE_SINK,
+	.level		= PM8XXX_MPP_AIN_AMUX_CH5,
+	.control	= PM8XXX_MPP_AOUT_CTRL_DISABLE,
+};
+
+static bool pm8xxx_adc_calib_first_adc;
+static bool pm8xxx_adc_initialized, pm8xxx_adc_calib_device_init;
+
+static int32_t pm8xxx_adc_check_channel_valid(uint32_t channel)
+{
+	if (channel < CHANNEL_VCOIN ||
+	(channel > CHANNEL_MUXOFF && channel < ADC_MPP_1_ATEST_8) ||
+	(channel > ADC_MPP_1_ATEST_7 && channel < ADC_MPP_2_ATEST_8)
+	|| (channel >= ADC_CHANNEL_MAX_NUM))
+		return -EBADF;
+	else
+		return 0;
+}
+
+static int32_t pm8xxx_adc_arb_cntrl(uint32_t arb_cntrl,
+					uint32_t channel)
+{
+	struct pm8xxx_adc *adc_pmic = pmic_adc;
+	int i, rc;
+	u8 data_arb_cntrl = 0;
+
+	if (arb_cntrl) {
+		if (adc_pmic->msm_suspend_check)
+			pr_err("PM8xxx ADC request made after suspend_noirq "
+					"with channel: %d\n", channel);
+		data_arb_cntrl |= PM8XXX_ADC_ARB_USRP_CNTRL1_EN_ARB;
+		wake_lock(&adc_pmic->adc_wakelock);
+	}
+
+	/* Write twice to the CNTRL register for the arbiter settings
+	   to take into effect */
+	for (i = 0; i < 2; i++) {
+		rc = pm8xxx_writeb(adc_pmic->dev->parent,
+				PM8XXX_ADC_ARB_USRP_CNTRL1, data_arb_cntrl);
+		if (rc < 0) {
+			pr_err("PM8xxx arb cntrl write failed with %d\n", rc);
+			return rc;
+		}
+	}
+
+	if (arb_cntrl) {
+		data_arb_cntrl |= PM8XXX_ADC_ARB_USRP_CNTRL1_REQ;
+		INIT_COMPLETION(adc_pmic->adc_rslt_completion);
+		rc = pm8xxx_writeb(adc_pmic->dev->parent,
+			PM8XXX_ADC_ARB_USRP_CNTRL1, data_arb_cntrl);
+	} else
+		wake_unlock(&adc_pmic->adc_wakelock);
+
+	return 0;
+}
+
+static int32_t pm8xxx_adc_patherm_power(bool on)
+{
+	int rc = 0;
+
+	if (!pa_therm) {
+		pr_err("pm8xxx adc pa_therm not valid\n");
+		return -EINVAL;
+	}
+
+	if (on) {
+		rc = regulator_set_voltage(pa_therm,
+				PM8XXX_ADC_PA_THERM_VREG_UV_MIN,
+				PM8XXX_ADC_PA_THERM_VREG_UV_MAX);
+		if (rc < 0) {
+			pr_err("failed to set the voltage for "
+					"pa_therm with error %d\n", rc);
+			return rc;
+		}
+
+		rc = regulator_set_optimum_mode(pa_therm,
+				PM8XXX_ADC_PA_THERM_VREG_UA_LOAD);
+		if (rc < 0) {
+			pr_err("failed to set optimum mode for "
+					"pa_therm with error %d\n", rc);
+			return rc;
+		}
+
+		rc = regulator_enable(pa_therm);
+		if (rc < 0) {
+			pr_err("failed to enable pa_therm vreg "
+					"with error %d\n", rc);
+			return rc;
+		}
+	} else {
+		rc = regulator_disable(pa_therm);
+		if (rc < 0) {
+			pr_err("failed to disable pa_therm vreg "
+					"with error %d\n", rc);
+			return rc;
+		}
+	}
+
+	return rc;
+}
+
+static int32_t pm8xxx_adc_channel_power_enable(uint32_t channel,
+							bool power_cntrl)
+{
+	int rc = 0;
+
+	switch (channel)
+	case ADC_MPP_1_AMUX8:
+		rc = pm8xxx_adc_patherm_power(power_cntrl);
+
+	return rc;
+}
+
+
+static uint32_t pm8xxx_adc_read_reg(uint32_t reg, u8 *data)
+{
+	struct pm8xxx_adc *adc_pmic = pmic_adc;
+	int rc;
+
+	rc = pm8xxx_readb(adc_pmic->dev->parent, reg, data);
+	if (rc < 0) {
+		pr_err("PM8xxx adc read reg %d failed with %d\n", reg, rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+static uint32_t pm8xxx_adc_write_reg(uint32_t reg, u8 data)
+{
+	struct pm8xxx_adc *adc_pmic = pmic_adc;
+	int rc;
+
+	rc = pm8xxx_writeb(adc_pmic->dev->parent, reg, data);
+	if (rc < 0) {
+		pr_err("PM8xxx adc write reg %d failed with %d\n", reg, rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int32_t pm8xxx_adc_configure(
+				struct pm8xxx_adc_amux_properties *chan_prop)
+{
+	u8 data_amux_chan = 0, data_arb_rsv = 0, data_dig_param = 0;
+	int rc;
+
+	data_amux_chan |= chan_prop->amux_channel << PM8XXX_ADC_AMUX_SEL;
+
+	if (chan_prop->amux_mpp_channel)
+		data_amux_chan |= chan_prop->amux_mpp_channel <<
+					PM8XXX_ADC_AMUX_MPP_SEL;
+
+	rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_USRP_AMUX_CNTRL,
+							data_amux_chan);
+	if (rc < 0)
+		return rc;
+
+	rc = pm8xxx_adc_read_reg(PM8XXX_ADC_ARB_USRP_RSV, &data_arb_rsv);
+	if (rc < 0)
+		return rc;
+
+	data_arb_rsv &= (PM8XXX_ADC_ARB_USRP_RSV_RST |
+		PM8XXX_ADC_ARB_USRP_RSV_DTEST0 |
+		PM8XXX_ADC_ARB_USRP_RSV_DTEST1 |
+		PM8XXX_ADC_ARB_USRP_RSV_OP);
+	data_arb_rsv |= (chan_prop->amux_ip_rsv << PM8XXX_ADC_RSV_IP_SEL |
+				PM8XXX_ADC_ARB_USRP_RSV_TRM);
+
+	rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_USRP_RSV, data_arb_rsv);
+	if (rc < 0)
+		return rc;
+
+	rc = pm8xxx_adc_read_reg(PM8XXX_ADC_ARB_USRP_DIG_PARAM,
+							&data_dig_param);
+	if (rc < 0)
+		return rc;
+
+	/* Default 2.4Mhz clock rate */
+	/* Client chooses the decimation */
+	switch (chan_prop->decimation) {
+	case ADC_DECIMATION_TYPE1:
+		data_dig_param |= PM8XXX_ADC_ARB_USRP_DIG_PARAM_DEC_RATE0;
+		break;
+	case ADC_DECIMATION_TYPE2:
+		data_dig_param |= (PM8XXX_ADC_ARB_USRP_DIG_PARAM_DEC_RATE0
+				| PM8XXX_ADC_ARB_USRP_DIG_PARAM_DEC_RATE1);
+		break;
+	default:
+		data_dig_param |= PM8XXX_ADC_ARB_USRP_DIG_PARAM_DEC_RATE0;
+		break;
+	}
+	rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_USRP_DIG_PARAM,
+						PM8XXX_ADC_ARB_ANA_DIG);
+	if (rc < 0)
+		return rc;
+
+	rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_USRP_ANA_PARAM,
+						PM8XXX_ADC_ARB_ANA_DIG);
+	if (rc < 0)
+		return rc;
+
+	rc = pm8xxx_adc_arb_cntrl(1, data_amux_chan);
+	if (rc < 0) {
+		pr_err("Configuring ADC Arbiter"
+				"enable failed with %d\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+static uint32_t pm8xxx_adc_read_adc_code(int32_t *data)
+{
+	struct pm8xxx_adc *adc_pmic = pmic_adc;
+	uint8_t rslt_lsb, rslt_msb;
+	int32_t rc, max_ideal_adc_code = 1 << adc_pmic->adc_prop->bitresolution;
+
+	rc = pm8xxx_readb(adc_pmic->dev->parent,
+				PM8XXX_ADC_ARB_USRP_DATA0, &rslt_lsb);
+	if (rc < 0) {
+		pr_err("PM8xxx adc result read failed with %d\n", rc);
+		return rc;
+	}
+
+	rc = pm8xxx_readb(adc_pmic->dev->parent,
+				PM8XXX_ADC_ARB_USRP_DATA1, &rslt_msb);
+	if (rc < 0) {
+		pr_err("PM8xxx adc result read failed with %d\n", rc);
+		return rc;
+	}
+
+	*data = (rslt_msb << 8) | rslt_lsb;
+
+	/* Use the midpoint to determine underflow or overflow */
+	if (*data > max_ideal_adc_code + (max_ideal_adc_code >> 1))
+		*data |= ((1 << (8 * sizeof(*data) -
+			adc_pmic->adc_prop->bitresolution)) - 1) <<
+			adc_pmic->adc_prop->bitresolution;
+
+	/* Default value for switching off the arbiter after reading
+	   the ADC value. Bit 0 set to 0. */
+	rc = pm8xxx_adc_arb_cntrl(0, CHANNEL_NONE);
+	if (rc < 0) {
+		pr_err("%s: Configuring ADC Arbiter disable"
+					"failed\n", __func__);
+		return rc;
+	}
+
+	return 0;
+}
+
+static void pm8xxx_adc_btm_warm_scheduler_fn(struct work_struct *work)
+{
+	struct pm8xxx_adc *adc_pmic = container_of(work, struct pm8xxx_adc,
+					warm_work);
+	unsigned long flags = 0;
+	bool warm_status;
+
+	spin_lock_irqsave(&adc_pmic->btm_lock, flags);
+	warm_status = irq_read_line(adc_pmic->btm_warm_irq);
+	if (adc_pmic->batt.btm_warm_fn != NULL)
+		adc_pmic->batt.btm_warm_fn(warm_status);
+	spin_unlock_irqrestore(&adc_pmic->btm_lock, flags);
+}
+
+static void pm8xxx_adc_btm_cool_scheduler_fn(struct work_struct *work)
+{
+	struct pm8xxx_adc *adc_pmic = container_of(work, struct pm8xxx_adc,
+					cool_work);
+	unsigned long flags = 0;
+	bool cool_status;
+
+	spin_lock_irqsave(&adc_pmic->btm_lock, flags);
+	cool_status = irq_read_line(adc_pmic->btm_cool_irq);
+	if (adc_pmic->batt.btm_cool_fn != NULL)
+		adc_pmic->batt.btm_cool_fn(cool_status);
+	spin_unlock_irqrestore(&adc_pmic->btm_lock, flags);
+}
+
+void trigger_completion(struct work_struct *work)
+{
+	struct pm8xxx_adc *adc_8xxx = pmic_adc;
+
+	complete(&adc_8xxx->adc_rslt_completion);
+}
+DECLARE_WORK(trigger_completion_work, trigger_completion);
+
+static irqreturn_t pm8xxx_adc_isr(int irq, void *dev_id)
+{
+
+	if (pm8xxx_adc_calib_first_adc)
+		return IRQ_HANDLED;
+
+	schedule_work(&trigger_completion_work);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8xxx_btm_warm_isr(int irq, void *dev_id)
+{
+	struct pm8xxx_adc *btm_8xxx = dev_id;
+
+	schedule_work(&btm_8xxx->warm_work);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8xxx_btm_cool_isr(int irq, void *dev_id)
+{
+	struct pm8xxx_adc *btm_8xxx = dev_id;
+
+	schedule_work(&btm_8xxx->cool_work);
+
+	return IRQ_HANDLED;
+}
+
+static uint32_t pm8xxx_adc_calib_device(void)
+{
+	struct pm8xxx_adc *adc_pmic = pmic_adc;
+	struct pm8xxx_adc_amux_properties conv;
+	int rc, calib_read_1, calib_read_2;
+	u8 data_arb_usrp_cntrl1 = 0;
+
+	conv.amux_channel = CHANNEL_125V;
+	conv.decimation = ADC_DECIMATION_TYPE2;
+	conv.amux_ip_rsv = AMUX_RSV1;
+	conv.amux_mpp_channel = PREMUX_MPP_SCALE_0;
+	pm8xxx_adc_calib_first_adc = true;
+	rc = pm8xxx_adc_configure(&conv);
+	if (rc) {
+		pr_err("pm8xxx_adc configure failed with %d\n", rc);
+		goto calib_fail;
+	}
+
+	while (data_arb_usrp_cntrl1 != (PM8XXX_ADC_ARB_USRP_CNTRL1_EOC |
+					PM8XXX_ADC_ARB_USRP_CNTRL1_EN_ARB)) {
+		rc = pm8xxx_adc_read_reg(PM8XXX_ADC_ARB_USRP_CNTRL1,
+					&data_arb_usrp_cntrl1);
+		if (rc < 0)
+			return rc;
+		usleep_range(PM8XXX_ADC_CONV_TIME_MIN,
+					PM8XXX_ADC_CONV_TIME_MAX);
+	}
+	data_arb_usrp_cntrl1 = 0;
+
+	rc = pm8xxx_adc_read_adc_code(&calib_read_1);
+	if (rc) {
+		pr_err("pm8xxx_adc read adc failed with %d\n", rc);
+		pm8xxx_adc_calib_first_adc = false;
+		goto calib_fail;
+	}
+	pm8xxx_adc_calib_first_adc = false;
+
+	conv.amux_channel = CHANNEL_625MV;
+	conv.decimation = ADC_DECIMATION_TYPE2;
+	conv.amux_ip_rsv = AMUX_RSV1;
+	conv.amux_mpp_channel = PREMUX_MPP_SCALE_0;
+	pm8xxx_adc_calib_first_adc = true;
+	rc = pm8xxx_adc_configure(&conv);
+	if (rc) {
+		pr_err("pm8xxx_adc configure failed with %d\n", rc);
+		goto calib_fail;
+	}
+
+	while (data_arb_usrp_cntrl1 != (PM8XXX_ADC_ARB_USRP_CNTRL1_EOC |
+					PM8XXX_ADC_ARB_USRP_CNTRL1_EN_ARB)) {
+		rc = pm8xxx_adc_read_reg(PM8XXX_ADC_ARB_USRP_CNTRL1,
+					&data_arb_usrp_cntrl1);
+		if (rc < 0)
+			return rc;
+		usleep_range(PM8XXX_ADC_CONV_TIME_MIN,
+					PM8XXX_ADC_CONV_TIME_MAX);
+	}
+	data_arb_usrp_cntrl1 = 0;
+
+	rc = pm8xxx_adc_read_adc_code(&calib_read_2);
+	if (rc) {
+		pr_err("pm8xxx_adc read adc failed with %d\n", rc);
+		pm8xxx_adc_calib_first_adc = false;
+		goto calib_fail;
+	}
+	pm8xxx_adc_calib_first_adc = false;
+
+	adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_ABSOLUTE].dy =
+					(calib_read_1 - calib_read_2);
+	adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_ABSOLUTE].dx
+						= PM8XXX_CHANNEL_ADC_625_UV;
+	adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_ABSOLUTE].adc_vref =
+					calib_read_1;
+	adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_ABSOLUTE].adc_gnd =
+					calib_read_2;
+	rc = pm8xxx_adc_arb_cntrl(0, CHANNEL_NONE);
+	if (rc < 0) {
+		pr_err("%s: Configuring ADC Arbiter disable"
+					"failed\n", __func__);
+		return rc;
+	}
+	/* Ratiometric Calibration */
+	conv.amux_channel = CHANNEL_MUXOFF;
+	conv.decimation = ADC_DECIMATION_TYPE2;
+	conv.amux_ip_rsv = AMUX_RSV5;
+	conv.amux_mpp_channel = PREMUX_MPP_SCALE_0;
+	pm8xxx_adc_calib_first_adc = true;
+	rc = pm8xxx_adc_configure(&conv);
+	if (rc) {
+		pr_err("pm8xxx_adc configure failed with %d\n", rc);
+		goto calib_fail;
+	}
+
+	while (data_arb_usrp_cntrl1 != (PM8XXX_ADC_ARB_USRP_CNTRL1_EOC |
+					PM8XXX_ADC_ARB_USRP_CNTRL1_EN_ARB)) {
+		rc = pm8xxx_adc_read_reg(PM8XXX_ADC_ARB_USRP_CNTRL1,
+					&data_arb_usrp_cntrl1);
+		if (rc < 0)
+			return rc;
+		usleep_range(PM8XXX_ADC_CONV_TIME_MIN,
+					PM8XXX_ADC_CONV_TIME_MAX);
+	}
+	data_arb_usrp_cntrl1 = 0;
+
+	rc = pm8xxx_adc_read_adc_code(&calib_read_1);
+	if (rc) {
+		pr_err("pm8xxx_adc read adc failed with %d\n", rc);
+		pm8xxx_adc_calib_first_adc = false;
+		goto calib_fail;
+	}
+	pm8xxx_adc_calib_first_adc = false;
+
+	conv.amux_channel = CHANNEL_MUXOFF;
+	conv.decimation = ADC_DECIMATION_TYPE2;
+	conv.amux_ip_rsv = AMUX_RSV4;
+	conv.amux_mpp_channel = PREMUX_MPP_SCALE_0;
+	pm8xxx_adc_calib_first_adc = true;
+	rc = pm8xxx_adc_configure(&conv);
+	if (rc) {
+		pr_err("pm8xxx_adc configure failed with %d\n", rc);
+		goto calib_fail;
+	}
+
+	while (data_arb_usrp_cntrl1 != (PM8XXX_ADC_ARB_USRP_CNTRL1_EOC |
+					PM8XXX_ADC_ARB_USRP_CNTRL1_EN_ARB)) {
+		rc = pm8xxx_adc_read_reg(PM8XXX_ADC_ARB_USRP_CNTRL1,
+					&data_arb_usrp_cntrl1);
+		if (rc < 0)
+			return rc;
+		usleep_range(PM8XXX_ADC_CONV_TIME_MIN,
+					PM8XXX_ADC_CONV_TIME_MAX);
+	}
+	data_arb_usrp_cntrl1 = 0;
+
+	rc = pm8xxx_adc_read_adc_code(&calib_read_2);
+	if (rc) {
+		pr_err("pm8xxx_adc read adc failed with %d\n", rc);
+		pm8xxx_adc_calib_first_adc = false;
+		goto calib_fail;
+	}
+	pm8xxx_adc_calib_first_adc = false;
+
+	adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_RATIOMETRIC].dy =
+					(calib_read_1 - calib_read_2);
+	adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_RATIOMETRIC].dx =
+					adc_pmic->adc_prop->adc_vdd_reference;
+	adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_RATIOMETRIC].adc_vref =
+					calib_read_1;
+	adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_RATIOMETRIC].adc_gnd =
+					calib_read_2;
+calib_fail:
+	rc = pm8xxx_adc_arb_cntrl(0, CHANNEL_NONE);
+	if (rc < 0) {
+		pr_err("%s: Configuring ADC Arbiter disable"
+					"failed\n", __func__);
+	}
+
+	return rc;
+}
+
+uint32_t pm8xxx_adc_read(enum pm8xxx_adc_channels channel,
+				struct pm8xxx_adc_chan_result *result)
+{
+	struct pm8xxx_adc *adc_pmic = pmic_adc;
+	int i = 0, rc = 0, rc_fail, amux_prescaling, scale_type;
+	enum pm8xxx_adc_premux_mpp_scale_type mpp_scale;
+
+	if (!pm8xxx_adc_initialized)
+		return -ENODEV;
+
+	if (!pm8xxx_adc_calib_device_init) {
+		if (pm8xxx_adc_calib_device() == 0)
+			pm8xxx_adc_calib_device_init = true;
+	}
+
+	mutex_lock(&adc_pmic->adc_lock);
+
+	for (i = 0; i < adc_pmic->adc_num_board_channel; i++) {
+		if (channel == adc_pmic->adc_channel[i].channel_name)
+			break;
+	}
+
+	if (i == adc_pmic->adc_num_board_channel ||
+		(pm8xxx_adc_check_channel_valid(channel) != 0)) {
+		rc = -EBADF;
+		goto fail_unlock;
+	}
+
+	if (channel < PM8XXX_CHANNEL_MPP_SCALE1_IDX) {
+		mpp_scale = PREMUX_MPP_SCALE_0;
+		adc_pmic->conv->amux_channel = channel;
+	} else if (channel >= PM8XXX_CHANNEL_MPP_SCALE1_IDX &&
+			channel < PM8XXX_CHANNEL_MPP_SCALE3_IDX) {
+		mpp_scale = PREMUX_MPP_SCALE_1;
+		adc_pmic->conv->amux_channel = channel %
+				PM8XXX_CHANNEL_MPP_SCALE1_IDX;
+	} else {
+		mpp_scale = PREMUX_MPP_SCALE_1_DIV3;
+		adc_pmic->conv->amux_channel = channel %
+				PM8XXX_CHANNEL_MPP_SCALE3_IDX;
+	}
+
+	adc_pmic->conv->amux_mpp_channel = mpp_scale;
+	adc_pmic->conv->amux_ip_rsv = adc_pmic->adc_channel[i].adc_rsv;
+	adc_pmic->conv->decimation = adc_pmic->adc_channel[i].adc_decimation;
+	amux_prescaling = adc_pmic->adc_channel[i].chan_path_prescaling;
+
+	adc_pmic->conv->chan_prop->offset_gain_numerator =
+		pm8xxx_amux_scaling_ratio[amux_prescaling].num;
+	adc_pmic->conv->chan_prop->offset_gain_denominator =
+		 pm8xxx_amux_scaling_ratio[amux_prescaling].den;
+
+	rc = pm8xxx_adc_channel_power_enable(channel, true);
+	if (rc) {
+		rc = -EINVAL;
+		goto fail_unlock;
+	}
+
+	rc = pm8xxx_adc_configure(adc_pmic->conv);
+	if (rc) {
+		rc = -EINVAL;
+		goto fail;
+	}
+
+	wait_for_completion(&adc_pmic->adc_rslt_completion);
+
+	rc = pm8xxx_adc_read_adc_code(&result->adc_code);
+	if (rc) {
+		rc = -EINVAL;
+		goto fail;
+	}
+
+	scale_type = adc_pmic->adc_channel[i].adc_scale_fn;
+	if (scale_type >= ADC_SCALE_NONE) {
+		rc = -EBADF;
+		goto fail;
+	}
+
+	adc_scale_fn[scale_type].chan(result->adc_code,
+			adc_pmic->adc_prop, adc_pmic->conv->chan_prop, result);
+
+	rc = pm8xxx_adc_channel_power_enable(channel, false);
+	if (rc) {
+		rc = -EINVAL;
+		goto fail_unlock;
+	}
+
+	mutex_unlock(&adc_pmic->adc_lock);
+
+	return 0;
+fail:
+	rc_fail = pm8xxx_adc_channel_power_enable(channel, false);
+	if (rc_fail)
+		pr_err("pm8xxx adc power disable failed\n");
+fail_unlock:
+	mutex_unlock(&adc_pmic->adc_lock);
+	pr_err("pm8xxx adc error with %d\n", rc);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_adc_read);
+
+uint32_t pm8xxx_adc_mpp_config_read(uint32_t mpp_num,
+			enum pm8xxx_adc_channels channel,
+			struct pm8xxx_adc_chan_result *result)
+{
+	struct pm8xxx_adc *adc_pmic = pmic_adc;
+	int rc = 0;
+
+	if (!pm8xxx_adc_initialized)
+		return -ENODEV;
+
+	if (!adc_pmic->mpp_base) {
+		rc = -EINVAL;
+		pr_info("PM8xxx MPP base invalid with error %d\n", rc);
+		return rc;
+	}
+
+	if (mpp_num == PM8XXX_AMUX_MPP_8) {
+		rc = -EINVAL;
+		pr_info("PM8xxx MPP8 is already configured "
+			"to AMUX8. Use pm8xxx_adc_read() instead.\n");
+		return rc;
+	}
+
+	mutex_lock(&adc_pmic->mpp_adc_lock);
+
+	rc = pm8xxx_mpp_config(((mpp_num - 1) + adc_pmic->mpp_base),
+					&pm8xxx_adc_mpp_config);
+	if (rc < 0) {
+		pr_err("pm8xxx adc mpp config error with %d\n", rc);
+		goto fail;
+	}
+
+	usleep_range(PM8XXX_ADC_MPP_SETTLE_TIME_MIN,
+					PM8XXX_ADC_MPP_SETTLE_TIME_MAX);
+
+	rc = pm8xxx_adc_read(channel, result);
+	if (rc < 0)
+		pr_err("pm8xxx adc read error with %d\n", rc);
+
+	rc = pm8xxx_mpp_config(((mpp_num - 1) + adc_pmic->mpp_base),
+					&pm8xxx_adc_mpp_unconfig);
+	if (rc < 0)
+		pr_err("pm8xxx adc mpp config error with %d\n", rc);
+fail:
+	mutex_unlock(&adc_pmic->mpp_adc_lock);
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_adc_mpp_config_read);
+
+uint32_t pm8xxx_adc_btm_configure(struct pm8xxx_adc_arb_btm_param *btm_param)
+{
+	struct pm8xxx_adc *adc_pmic = pmic_adc;
+	u8 data_btm_cool_thr0, data_btm_cool_thr1;
+	u8 data_btm_warm_thr0, data_btm_warm_thr1;
+	u8 arb_btm_cntrl1;
+	unsigned long flags = 0;
+	int rc;
+
+	if (adc_pmic == NULL) {
+		pr_err("PMIC ADC not valid\n");
+		return -EINVAL;
+	}
+
+	if ((btm_param->btm_cool_fn == NULL) &&
+		(btm_param->btm_warm_fn == NULL)) {
+		pr_err("No BTM warm/cool notification??\n");
+		return -EINVAL;
+	}
+
+	rc = pm8xxx_adc_batt_scaler(btm_param, adc_pmic->adc_prop,
+					adc_pmic->conv->chan_prop);
+	if (rc < 0) {
+		pr_err("Failed to lookup the BTM thresholds\n");
+		return rc;
+	}
+
+	if (btm_param->interval > PM8XXX_ADC_BTM_INTERVAL_MAX) {
+		pr_info("Bug in PMIC BTM interval time and cannot set"
+		" a value greater than 0x14 %x\n", btm_param->interval);
+		btm_param->interval = PM8XXX_ADC_BTM_INTERVAL_MAX;
+	}
+
+	spin_lock_irqsave(&adc_pmic->btm_lock, flags);
+
+	data_btm_cool_thr0 = ((btm_param->low_thr_voltage << 24) >> 24);
+	data_btm_cool_thr1 = ((btm_param->low_thr_voltage << 16) >> 24);
+	data_btm_warm_thr0 = ((btm_param->high_thr_voltage << 24) >> 24);
+	data_btm_warm_thr1 = ((btm_param->high_thr_voltage << 16) >> 24);
+
+	if (btm_param->btm_cool_fn != NULL) {
+		rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_BTM_BAT_COOL_THR0,
+							data_btm_cool_thr0);
+		if (rc < 0)
+			goto write_err;
+
+		rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_BTM_BAT_COOL_THR1,
+							data_btm_cool_thr1);
+		if (rc < 0)
+			goto write_err;
+
+		adc_pmic->batt.btm_cool_fn = btm_param->btm_cool_fn;
+	}
+
+	if (btm_param->btm_warm_fn != NULL) {
+		rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_BTM_BAT_WARM_THR0,
+							data_btm_warm_thr0);
+		if (rc < 0)
+			goto write_err;
+
+		rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_BTM_BAT_WARM_THR1,
+							data_btm_warm_thr1);
+		if (rc < 0)
+			goto write_err;
+
+		adc_pmic->batt.btm_warm_fn = btm_param->btm_warm_fn;
+	}
+
+	rc = pm8xxx_adc_read_reg(PM8XXX_ADC_ARB_BTM_CNTRL1, &arb_btm_cntrl1);
+	if (rc < 0)
+		goto bail_out;
+
+	btm_param->interval &= PM8XXX_ADC_BTM_INTERVAL_SEL_MASK;
+	arb_btm_cntrl1 |=
+		btm_param->interval << PM8XXX_ADC_BTM_INTERVAL_SEL_SHIFT;
+
+	rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_BTM_CNTRL1, arb_btm_cntrl1);
+	if (rc < 0)
+		goto write_err;
+
+	spin_unlock_irqrestore(&adc_pmic->btm_lock, flags);
+
+	return rc;
+bail_out:
+write_err:
+	spin_unlock_irqrestore(&adc_pmic->btm_lock, flags);
+	pr_debug("%s: with error code %d\n", __func__, rc);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_adc_btm_configure);
+
+static uint32_t pm8xxx_adc_btm_read(uint32_t channel)
+{
+	struct pm8xxx_adc *adc_pmic = pmic_adc;
+	int rc, i;
+	u8 arb_btm_dig_param, arb_btm_ana_param, arb_btm_rsv;
+	u8 arb_btm_amux_cntrl, data_arb_btm_cntrl = 0;
+	unsigned long flags;
+
+	arb_btm_amux_cntrl = channel << PM8XXX_ADC_BTM_CHANNEL_SEL;
+	arb_btm_rsv = adc_pmic->adc_channel[channel].adc_rsv;
+	arb_btm_dig_param = arb_btm_ana_param = PM8XXX_ADC_ARB_ANA_DIG;
+
+	spin_lock_irqsave(&adc_pmic->btm_lock, flags);
+
+	rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_BTM_AMUX_CNTRL,
+						arb_btm_amux_cntrl);
+	if (rc < 0)
+		goto write_err;
+
+	arb_btm_rsv = PM8XXX_ADC_BTM_RSV;
+
+	rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_BTM_RSV, arb_btm_rsv);
+	if (rc < 0)
+		goto write_err;
+
+	rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_BTM_DIG_PARAM,
+						arb_btm_dig_param);
+	if (rc < 0)
+		goto write_err;
+
+	rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_BTM_ANA_PARAM,
+						arb_btm_ana_param);
+	if (rc < 0)
+		goto write_err;
+
+	data_arb_btm_cntrl |= PM8XXX_ADC_ARB_BTM_CNTRL1_EN_BTM;
+
+	for (i = 0; i < 2; i++) {
+		rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_BTM_CNTRL1,
+						data_arb_btm_cntrl);
+		if (rc < 0)
+			goto write_err;
+	}
+
+	data_arb_btm_cntrl |= PM8XXX_ADC_ARB_BTM_CNTRL1_REQ
+				| PM8XXX_ADC_ARB_BTM_CNTRL1_SEL_OP_MODE;
+
+	rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_BTM_CNTRL1,
+					data_arb_btm_cntrl);
+	if (rc < 0)
+		goto write_err;
+
+	if (pmic_adc->batt.btm_warm_fn != NULL)
+		enable_irq(adc_pmic->btm_warm_irq);
+
+	if (pmic_adc->batt.btm_cool_fn != NULL)
+		enable_irq(adc_pmic->btm_cool_irq);
+
+write_err:
+	spin_unlock_irqrestore(&adc_pmic->btm_lock, flags);
+	return rc;
+}
+
+uint32_t pm8xxx_adc_btm_start(void)
+{
+	return pm8xxx_adc_btm_read(CHANNEL_BATT_THERM);
+}
+EXPORT_SYMBOL_GPL(pm8xxx_adc_btm_start);
+
+uint32_t pm8xxx_adc_btm_end(void)
+{
+	struct pm8xxx_adc *adc_pmic = pmic_adc;
+	int i, rc;
+	u8 data_arb_btm_cntrl = 0;
+	unsigned long flags;
+
+	disable_irq_nosync(adc_pmic->btm_warm_irq);
+	disable_irq_nosync(adc_pmic->btm_cool_irq);
+
+	spin_lock_irqsave(&adc_pmic->btm_lock, flags);
+
+	/* Write twice to the CNTRL register for the arbiter settings
+	   to take into effect */
+	for (i = 0; i < 2; i++) {
+		rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_BTM_CNTRL1,
+							data_arb_btm_cntrl);
+		if (rc < 0) {
+			spin_unlock_irqrestore(&adc_pmic->btm_lock, flags);
+			return rc;
+		}
+	}
+
+	spin_unlock_irqrestore(&adc_pmic->btm_lock, flags);
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_adc_btm_end);
+
+static ssize_t pm8xxx_adc_show(struct device *dev,
+			struct device_attribute *devattr, char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct pm8xxx_adc_chan_result result;
+	int rc = -1;
+
+	rc = pm8xxx_adc_read(attr->index, &result);
+
+	if (rc)
+		return 0;
+
+	return snprintf(buf, PM8XXX_ADC_HWMON_NAME_LENGTH,
+		"Result:%lld Raw:%d\n", result.physical, result.adc_code);
+}
+
+static int get_adc(void *data, u64 *val)
+{
+	struct pm8xxx_adc_chan_result result;
+	int i = (int)data;
+	int rc;
+
+	rc = pm8xxx_adc_read(i, &result);
+	if (!rc)
+		pr_info("ADC value raw:%x physical:%lld\n",
+			result.adc_code, result.physical);
+	*val = result.physical;
+
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_adc, NULL, "%llu\n");
+
+static int get_mpp_adc(void *data, u64 *val)
+{
+	struct pm8xxx_adc_chan_result result;
+	int i = (int)data;
+	int rc;
+
+	rc = pm8xxx_adc_mpp_config_read(i,
+		ADC_MPP_1_AMUX6, &result);
+	if (!rc)
+		pr_info("ADC MPP value raw:%x physical:%lld\n",
+			result.adc_code, result.physical);
+	*val = result.physical;
+
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(reg_mpp_fops, get_mpp_adc, NULL, "%llu\n");
+
+#ifdef CONFIG_DEBUG_FS
+static void create_debugfs_entries(void)
+{
+	int i = 0;
+	pmic_adc->dent = debugfs_create_dir("pm8xxx_adc", NULL);
+
+	if (IS_ERR(pmic_adc->dent)) {
+		pr_err("pmic adc debugfs dir not created\n");
+		return;
+	}
+
+	for (i = 0; i < pmic_adc->adc_num_board_channel; i++)
+		debugfs_create_file(pmic_adc->adc_channel[i].name,
+			0644, pmic_adc->dent,
+			(void *)pmic_adc->adc_channel[i].channel_name,
+			&reg_fops);
+}
+#else
+static inline void create_debugfs_entries(void)
+{
+}
+#endif
+static struct sensor_device_attribute pm8xxx_adc_attr =
+	SENSOR_ATTR(NULL, S_IRUGO, pm8xxx_adc_show, NULL, 0);
+
+static int32_t pm8xxx_adc_init_hwmon(struct platform_device *pdev)
+{
+	struct pm8xxx_adc *adc_pmic = pmic_adc;
+	int rc = 0, i, channel;
+
+	for (i = 0; i < pmic_adc->adc_num_board_channel; i++) {
+		channel = adc_pmic->adc_channel[i].channel_name;
+		if (pm8xxx_adc_check_channel_valid(channel)) {
+			pr_err("Invalid ADC init HWMON channel: %d\n", channel);
+			continue;
+		}
+		pm8xxx_adc_attr.index = adc_pmic->adc_channel[i].channel_name;
+		pm8xxx_adc_attr.dev_attr.attr.name =
+						adc_pmic->adc_channel[i].name;
+		memcpy(&adc_pmic->sens_attr[i], &pm8xxx_adc_attr,
+						sizeof(pm8xxx_adc_attr));
+		rc = device_create_file(&pdev->dev,
+				&adc_pmic->sens_attr[i].dev_attr);
+		if (rc) {
+			dev_err(&pdev->dev, "device_create_file failed for "
+					    "dev %s\n",
+					    adc_pmic->adc_channel[i].name);
+			goto hwmon_err_sens;
+		}
+	}
+
+	return 0;
+hwmon_err_sens:
+	pr_info("Init HWMON failed for pm8xxx_adc with %d\n", rc);
+	return rc;
+}
+
+#ifdef CONFIG_PM
+static int pm8xxx_adc_suspend_noirq(struct device *dev)
+{
+	struct pm8xxx_adc *adc_pmic = pmic_adc;
+
+	adc_pmic->msm_suspend_check = 1;
+
+	return 0;
+}
+
+static int pm8xxx_adc_resume_noirq(struct device *dev)
+{
+	struct pm8xxx_adc *adc_pmic = pmic_adc;
+
+	adc_pmic->msm_suspend_check = 0;
+
+	return 0;
+}
+
+static const struct dev_pm_ops pm8xxx_adc_dev_pm_ops = {
+	.suspend_noirq = pm8xxx_adc_suspend_noirq,
+	.resume_noirq = pm8xxx_adc_resume_noirq,
+};
+
+#define PM8XXX_ADC_DEV_PM_OPS	(&pm8xxx_adc_dev_pm_ops)
+#else
+#define PM8XXX_ADC_DEV_PM_OPS NULL
+#endif
+
+static int __devexit pm8xxx_adc_teardown(struct platform_device *pdev)
+{
+	struct pm8xxx_adc *adc_pmic = pmic_adc;
+	int i;
+
+	wake_lock_destroy(&adc_pmic->adc_wakelock);
+	platform_set_drvdata(pdev, NULL);
+	pmic_adc = NULL;
+	if (!pa_therm) {
+		regulator_put(pa_therm);
+		pa_therm = NULL;
+	}
+	for (i = 0; i < adc_pmic->adc_num_board_channel; i++)
+		device_remove_file(adc_pmic->dev,
+				&adc_pmic->sens_attr[i].dev_attr);
+	pm8xxx_adc_initialized = false;
+
+	return 0;
+}
+
+static int __devinit pm8xxx_adc_probe(struct platform_device *pdev)
+{
+	const struct pm8xxx_adc_platform_data *pdata = pdev->dev.platform_data;
+	struct pm8xxx_adc *adc_pmic;
+	struct pm8xxx_adc_amux_properties *adc_amux_prop;
+	int rc = 0;
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "no platform data?\n");
+		return -EINVAL;
+	}
+
+	adc_pmic = devm_kzalloc(&pdev->dev, sizeof(struct pm8xxx_adc) +
+			(sizeof(struct sensor_device_attribute) *
+			pdata->adc_num_board_channel), GFP_KERNEL);
+	if (!adc_pmic) {
+		dev_err(&pdev->dev, "Unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	adc_amux_prop = devm_kzalloc(&pdev->dev,
+				sizeof(struct pm8xxx_adc_amux_properties) +
+				sizeof(struct pm8xxx_adc_chan_properties)
+				, GFP_KERNEL);
+	if (!adc_amux_prop) {
+		dev_err(&pdev->dev, "Unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	adc_pmic->dev = &pdev->dev;
+	adc_pmic->adc_prop = pdata->adc_prop;
+	adc_pmic->conv = adc_amux_prop;
+	init_completion(&adc_pmic->adc_rslt_completion);
+	adc_pmic->adc_channel = pdata->adc_channel;
+	adc_pmic->adc_num_board_channel = pdata->adc_num_board_channel;
+	adc_pmic->mpp_base = pdata->adc_mpp_base;
+
+	mutex_init(&adc_pmic->adc_lock);
+	mutex_init(&adc_pmic->mpp_adc_lock);
+	spin_lock_init(&adc_pmic->btm_lock);
+
+	adc_pmic->adc_irq = platform_get_irq(pdev, PM8XXX_ADC_IRQ_0);
+	if (adc_pmic->adc_irq < 0)
+		return adc_pmic->adc_irq;
+
+	rc = devm_request_irq(&pdev->dev, adc_pmic->adc_irq,
+				pm8xxx_adc_isr,
+		IRQF_TRIGGER_RISING, "pm8xxx_adc_interrupt", adc_pmic);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to request adc irq "
+						"with error %d\n", rc);
+	} else {
+		enable_irq_wake(adc_pmic->adc_irq);
+	}
+
+	adc_pmic->btm_warm_irq = platform_get_irq(pdev, PM8XXX_ADC_IRQ_1);
+	if (adc_pmic->btm_warm_irq < 0)
+		return adc_pmic->btm_warm_irq;
+
+	rc = devm_request_irq(&pdev->dev, adc_pmic->btm_warm_irq,
+				pm8xxx_btm_warm_isr,
+		IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+			"pm8xxx_btm_warm_interrupt", adc_pmic);
+	if (rc) {
+		pr_err("btm warm irq failed %d with interrupt number %d\n",
+						rc, adc_pmic->btm_warm_irq);
+		dev_err(&pdev->dev, "failed to request btm irq\n");
+	}
+
+	disable_irq_nosync(adc_pmic->btm_warm_irq);
+
+	adc_pmic->btm_cool_irq = platform_get_irq(pdev, PM8XXX_ADC_IRQ_2);
+	if (adc_pmic->btm_cool_irq < 0)
+		return adc_pmic->btm_cool_irq;
+
+	rc = devm_request_irq(&pdev->dev, adc_pmic->btm_cool_irq,
+				pm8xxx_btm_cool_isr,
+		IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+			"pm8xxx_btm_cool_interrupt", adc_pmic);
+	if (rc) {
+		pr_err("btm cool irq failed with return %d and number %d\n",
+						rc, adc_pmic->btm_cool_irq);
+		dev_err(&pdev->dev, "failed to request btm irq\n");
+	}
+
+	disable_irq_nosync(adc_pmic->btm_cool_irq);
+	platform_set_drvdata(pdev, adc_pmic);
+	wake_lock_init(&adc_pmic->adc_wakelock, WAKE_LOCK_SUSPEND,
+					"pm8xxx_adc_wakelock");
+	adc_pmic->msm_suspend_check = 0;
+	pmic_adc = adc_pmic;
+
+	INIT_WORK(&adc_pmic->warm_work, pm8xxx_adc_btm_warm_scheduler_fn);
+	INIT_WORK(&adc_pmic->cool_work, pm8xxx_adc_btm_cool_scheduler_fn);
+	create_debugfs_entries();
+	pm8xxx_adc_calib_first_adc = false;
+	pm8xxx_adc_calib_device_init = false;
+	pm8xxx_adc_initialized = true;
+
+	rc = pm8xxx_adc_init_hwmon(pdev);
+	if (rc) {
+		pr_err("pm8xxx adc init hwmon failed with %d\n", rc);
+		dev_err(&pdev->dev, "failed to initialize pm8xxx hwmon adc\n");
+	}
+	adc_pmic->hwmon = hwmon_device_register(adc_pmic->dev);
+
+	pa_therm = regulator_get(adc_pmic->dev, "pa_therm");
+	if (IS_ERR(pa_therm)) {
+		rc = PTR_ERR(pa_therm);
+		pr_err("failed to request pa_therm vreg with error %d\n", rc);
+		pa_therm = NULL;
+	}
+	return 0;
+}
+
+static struct platform_driver pm8xxx_adc_driver = {
+	.probe	= pm8xxx_adc_probe,
+	.remove	= __devexit_p(pm8xxx_adc_teardown),
+	.driver	= {
+		.name	= PM8XXX_ADC_DEV_NAME,
+		.owner	= THIS_MODULE,
+		.pm	= PM8XXX_ADC_DEV_PM_OPS,
+	},
+};
+
+static int __init pm8xxx_adc_init(void)
+{
+	return platform_driver_register(&pm8xxx_adc_driver);
+}
+module_init(pm8xxx_adc_init);
+
+static void __exit pm8xxx_adc_exit(void)
+{
+	platform_driver_unregister(&pm8xxx_adc_driver);
+}
+module_exit(pm8xxx_adc_exit);
+
+MODULE_ALIAS("platform:" PM8XXX_ADC_DEV_NAME);
+MODULE_DESCRIPTION("PMIC8921/8018 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/wpce775x.c b/drivers/hwmon/wpce775x.c
new file mode 100644
index 0000000..2d00700
--- /dev/null
+++ b/drivers/hwmon/wpce775x.c
@@ -0,0 +1,167 @@
+/* Quanta EC driver for the Winbond Embedded Controller
+ *
+ * Copyright (C) 2009 Quanta Computer 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/err.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+
+#define EC_ID_NAME          "qci-i2cec"
+#define EC_BUFFER_LEN		16
+#define EC_CMD_POWER_OFF	0xAC
+#define EC_CMD_RESTART	0xAB
+
+static struct i2c_client *g_i2cec_client;
+
+/* General structure to hold the driver data */
+struct i2cec_drv_data {
+		struct i2c_client *i2cec_client;
+		struct work_struct work;
+		char ec_data[EC_BUFFER_LEN+1];
+};
+
+static int __devinit wpce_probe(struct i2c_client *client,
+	const struct i2c_device_id *id);
+static int __devexit wpce_remove(struct i2c_client *kbd);
+
+#ifdef CONFIG_PM
+static int wpce_suspend(struct device *dev)
+{
+	return 0;
+}
+
+static int wpce_resume(struct device *dev)
+{
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_PM
+static struct dev_pm_ops wpce_pm_ops = {
+	.suspend  = wpce_suspend,
+	.resume   = wpce_resume,
+};
+#endif
+
+static const struct i2c_device_id wpce_idtable[] = {
+       { EC_ID_NAME, 0 },
+       { }
+};
+
+static struct i2c_driver wpce_driver = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name  = EC_ID_NAME,
+#ifdef CONFIG_PM
+		.pm = &wpce_pm_ops,
+#endif
+	},
+	.probe	  = wpce_probe,
+	.remove	  = __devexit_p(wpce_remove),
+	.id_table   = wpce_idtable,
+};
+
+static int __devinit wpce_probe(struct i2c_client *client,
+				    const struct i2c_device_id *id)
+{
+	int err = -ENOMEM;
+	struct i2cec_drv_data *context = 0;
+
+	/* there is no need to call i2c_check_functionality() since it is the
+	client's job to use the interface (I2C vs SMBUS) appropriate for it. */
+	client->driver = &wpce_driver;
+	context = kzalloc(sizeof(struct i2cec_drv_data), GFP_KERNEL);
+	if (!context)
+		return err;
+
+	context->i2cec_client = client;
+	g_i2cec_client = client;
+	i2c_set_clientdata(context->i2cec_client, context);
+
+	return 0;
+}
+
+static int __devexit wpce_remove(struct i2c_client *dev)
+{
+	struct i2cec_drv_data *context = i2c_get_clientdata(dev);
+	g_i2cec_client = NULL;
+	kfree(context);
+
+	return 0;
+}
+
+static int __init wpce_init(void)
+{
+	return i2c_add_driver(&wpce_driver);
+}
+
+static void __exit wpce_exit(void)
+{
+	i2c_del_driver(&wpce_driver);
+}
+
+struct i2c_client *wpce_get_i2c_client(void)
+{
+	return g_i2cec_client;
+}
+EXPORT_SYMBOL_GPL(wpce_get_i2c_client);
+
+void wpce_poweroff(void)
+{
+	if (g_i2cec_client == NULL)
+		return;
+	i2c_smbus_write_byte(g_i2cec_client, EC_CMD_POWER_OFF);
+}
+EXPORT_SYMBOL_GPL(wpce_poweroff);
+
+void wpce_restart(void)
+{
+	if (g_i2cec_client == NULL)
+		return;
+	i2c_smbus_write_byte(g_i2cec_client, EC_CMD_RESTART);
+}
+EXPORT_SYMBOL_GPL(wpce_restart);
+
+int wpce_i2c_transfer(struct i2c_msg *msg)
+{
+	if (g_i2cec_client == NULL)
+		return -1;
+	msg->addr = g_i2cec_client->addr;
+	return i2c_transfer(g_i2cec_client->adapter, msg, 1);
+}
+EXPORT_SYMBOL_GPL(wpce_i2c_transfer);
+
+int wpce_smbus_write_word_data(u8 command, u16 value)
+{
+	if (g_i2cec_client == NULL)
+		return -1;
+	return i2c_smbus_write_word_data(g_i2cec_client, command, value);
+}
+EXPORT_SYMBOL_GPL(wpce_smbus_write_word_data);
+
+int wpce_smbus_write_byte_data(u8 command, u8 value)
+{
+	if (g_i2cec_client == NULL)
+		return -1;
+	return i2c_smbus_write_byte_data(g_i2cec_client, command, value);
+}
+EXPORT_SYMBOL_GPL(wpce_smbus_write_byte_data);
+
+module_init(wpce_init);
+module_exit(wpce_exit);
+
+MODULE_AUTHOR("Quanta Computer Inc.");
+MODULE_DESCRIPTION("Quanta Embedded Controller I2C Bridge Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index d2c5095..574ec8a 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -470,6 +470,32 @@
 	  This driver can also be built as a module.  If so, the module
 	  will be called i2c-mpc.
 
+config I2C_MSM
+	tristate "MSM"
+	depends on I2C && (ARCH_MSM || ARCH_QSD)
+	default y
+	help
+	  If you say yes to this option, support will be included for the
+	  built-in I2C interface on the MSM or QSD family processors.
+
+config I2C_QUP
+	tristate "I2C_QUP"
+	depends on ARCH_MSM
+	help
+	  If you say yes to this option, support will be included for the
+	  built-in I2C interface on the MSM family processors.
+
+config I2C_SSBI
+	tristate "Qualcomm Single-wire Serial Bus Interface (SSBI)"
+	depends on I2C && (ARCH_MSM7X30 || ARCH_MSM8X60 || ARCH_FSM9XXX)
+	default n
+	help
+	  If you say yes to this option, support will be included for the
+	  built-in SSBI interface on the MSM family processors.
+
+          Note that SSBI is not an I2C device, but is functionally related
+          enough such that it is able to leverages the I2C framework.
+
 config I2C_MV64XXX
 	tristate "Marvell mv64xxx I2C Controller"
 	depends on (MV64X60 || PLAT_ORION) && EXPERIMENTAL
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 569567b..4f494fc 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -46,6 +46,9 @@
 obj-$(CONFIG_I2C_IOP3XX)	+= i2c-iop3xx.o
 obj-$(CONFIG_I2C_IXP2000)	+= i2c-ixp2000.o
 obj-$(CONFIG_I2C_MPC)		+= i2c-mpc.o
+obj-$(CONFIG_I2C_MSM)		+= i2c-msm.o
+obj-$(CONFIG_I2C_QUP)           += i2c-qup.o
+obj-$(CONFIG_I2C_SSBI)          += i2c-ssbi.o
 obj-$(CONFIG_I2C_MV64XXX)	+= i2c-mv64xxx.o
 obj-$(CONFIG_I2C_MXS)		+= i2c-mxs.o
 obj-$(CONFIG_I2C_NOMADIK)	+= i2c-nomadik.o
diff --git a/drivers/i2c/busses/i2c-msm.c b/drivers/i2c/busses/i2c-msm.c
new file mode 100644
index 0000000..b753fd0
--- /dev/null
+++ b/drivers/i2c/busses/i2c-msm.c
@@ -0,0 +1,798 @@
+/* drivers/i2c/busses/i2c-msm.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+/* #define DEBUG */
+
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <mach/board.h>
+#include <linux/mutex.h>
+#include <linux/timer.h>
+#include <linux/remote_spinlock.h>
+#include <linux/pm_qos.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+
+
+enum {
+	I2C_WRITE_DATA          = 0x00,
+	I2C_CLK_CTL             = 0x04,
+	I2C_STATUS              = 0x08,
+	I2C_READ_DATA           = 0x0c,
+	I2C_INTERFACE_SELECT    = 0x10,
+
+	I2C_WRITE_DATA_DATA_BYTE            = 0xff,
+	I2C_WRITE_DATA_ADDR_BYTE            = 1U << 8,
+	I2C_WRITE_DATA_LAST_BYTE            = 1U << 9,
+
+	I2C_CLK_CTL_FS_DIVIDER_VALUE        = 0xff,
+	I2C_CLK_CTL_HS_DIVIDER_VALUE        = 7U << 8,
+
+	I2C_STATUS_WR_BUFFER_FULL           = 1U << 0,
+	I2C_STATUS_RD_BUFFER_FULL           = 1U << 1,
+	I2C_STATUS_BUS_ERROR                = 1U << 2,
+	I2C_STATUS_PACKET_NACKED            = 1U << 3,
+	I2C_STATUS_ARB_LOST                 = 1U << 4,
+	I2C_STATUS_INVALID_WRITE            = 1U << 5,
+	I2C_STATUS_FAILED                   = 3U << 6,
+	I2C_STATUS_BUS_ACTIVE               = 1U << 8,
+	I2C_STATUS_BUS_MASTER               = 1U << 9,
+	I2C_STATUS_ERROR_MASK               = 0xfc,
+
+	I2C_INTERFACE_SELECT_INTF_SELECT    = 1U << 0,
+	I2C_INTERFACE_SELECT_SCL            = 1U << 8,
+	I2C_INTERFACE_SELECT_SDA            = 1U << 9,
+	I2C_STATUS_RX_DATA_STATE            = 3U << 11,
+	I2C_STATUS_LOW_CLK_STATE            = 3U << 13,
+};
+
+struct msm_i2c_dev {
+	struct device                *dev;
+	void __iomem                 *base;	/* virtual */
+	int                          irq;
+	struct clk                   *clk;
+	struct i2c_adapter           adap_pri;
+	struct i2c_adapter           adap_aux;
+
+	spinlock_t                   lock;
+
+	struct i2c_msg               *msg;
+	int                          rem;
+	int                          pos;
+	int                          cnt;
+	int                          err;
+	int                          flush_cnt;
+	int                          rd_acked;
+	int                          one_bit_t;
+	remote_mutex_t               r_lock;
+	int                          suspended;
+	struct mutex                 mlock;
+	struct msm_i2c_platform_data *pdata;
+	struct timer_list            pwr_timer;
+	int                          clk_state;
+	void                         *complete;
+
+	struct pm_qos_request pm_qos_req;
+};
+
+static void
+msm_i2c_pwr_mgmt(struct msm_i2c_dev *dev, unsigned int state)
+{
+	dev->clk_state = state;
+	if (state != 0)
+		clk_enable(dev->clk);
+	else
+		clk_disable(dev->clk);
+}
+
+static void
+msm_i2c_pwr_timer(unsigned long data)
+{
+	struct msm_i2c_dev *dev = (struct msm_i2c_dev *) data;
+	dev_dbg(dev->dev, "I2C_Power: Inactivity based power management\n");
+	if (dev->clk_state == 1)
+		msm_i2c_pwr_mgmt(dev, 0);
+}
+
+#ifdef DEBUG
+static void
+dump_status(uint32_t status)
+{
+	printk("STATUS (0x%.8x): ", status);
+	if (status & I2C_STATUS_BUS_MASTER)
+		printk("MST ");
+	if (status & I2C_STATUS_BUS_ACTIVE)
+		printk("ACT ");
+	if (status & I2C_STATUS_INVALID_WRITE)
+		printk("INV_WR ");
+	if (status & I2C_STATUS_ARB_LOST)
+		printk("ARB_LST ");
+	if (status & I2C_STATUS_PACKET_NACKED)
+		printk("NAK ");
+	if (status & I2C_STATUS_BUS_ERROR)
+		printk("BUS_ERR ");
+	if (status & I2C_STATUS_RD_BUFFER_FULL)
+		printk("RD_FULL ");
+	if (status & I2C_STATUS_WR_BUFFER_FULL)
+		printk("WR_FULL ");
+	if (status & I2C_STATUS_FAILED)
+		printk("FAIL 0x%x", (status & I2C_STATUS_FAILED));
+	printk("\n");
+}
+#endif
+
+static irqreturn_t
+msm_i2c_interrupt(int irq, void *devid)
+{
+	struct msm_i2c_dev *dev = devid;
+	uint32_t status = readl(dev->base + I2C_STATUS);
+	int err = 0;
+
+#ifdef DEBUG
+	dump_status(status);
+#endif
+
+	spin_lock(&dev->lock);
+	if (!dev->msg) {
+		printk(KERN_ERR "%s: IRQ but nothing to do!\n", __func__);
+		spin_unlock(&dev->lock);
+		return IRQ_HANDLED;
+	}
+
+	if (status & I2C_STATUS_ERROR_MASK) {
+		err = -EIO;
+		goto out_err;
+	}
+
+	if (dev->msg->flags & I2C_M_RD) {
+		if (status & I2C_STATUS_RD_BUFFER_FULL) {
+
+			/*
+			 * Theres something in the FIFO.
+			 * Are we expecting data or flush crap?
+			 */
+			if (dev->cnt) { /* DATA */
+				uint8_t *data = &dev->msg->buf[dev->pos];
+
+				/* This is in spin-lock. So there will be no
+				 * scheduling between reading the second-last
+				 * byte and writing LAST_BYTE to the controller.
+				 * So extra read-cycle-clock won't be generated
+				 * Per I2C MSM HW Specs: Write LAST_BYTE befure
+				 * reading 2nd last byte
+				 */
+				if (dev->cnt == 2)
+					writel(I2C_WRITE_DATA_LAST_BYTE,
+						dev->base + I2C_WRITE_DATA);
+				*data = readl(dev->base + I2C_READ_DATA);
+				dev->cnt--;
+				dev->pos++;
+				if (dev->msg->len == 1)
+					dev->rd_acked = 0;
+				if (dev->cnt == 0)
+					goto out_complete;
+
+			} else {
+				/* Now that extra read-cycle-clocks aren't
+				 * generated, this becomes error condition
+				 */
+				dev_err(dev->dev,
+					"read did not stop, status - %x\n",
+					status);
+				err = -EIO;
+				goto out_err;
+			}
+		} else if (dev->msg->len == 1 && dev->rd_acked == 0 &&
+				((status & I2C_STATUS_RX_DATA_STATE) ==
+				 I2C_STATUS_RX_DATA_STATE))
+			writel(I2C_WRITE_DATA_LAST_BYTE,
+				dev->base + I2C_WRITE_DATA);
+	} else {
+		uint16_t data;
+
+		if (status & I2C_STATUS_WR_BUFFER_FULL) {
+			dev_err(dev->dev,
+				"Write buffer full in ISR on write?\n");
+			err = -EIO;
+			goto out_err;
+		}
+
+		if (dev->cnt) {
+			/* Ready to take a byte */
+			data = dev->msg->buf[dev->pos];
+			if (dev->cnt == 1 && dev->rem == 1)
+				data |= I2C_WRITE_DATA_LAST_BYTE;
+
+			status = readl(dev->base + I2C_STATUS);
+			/*
+			 * Due to a hardware timing issue, data line setup time
+			 * may be reduced to less than recommended 250 ns.
+			 * This happens when next byte is written in a
+			 * particular window of clock line being low and master
+			 * not stretching the clock line. Due to setup time
+			 * violation, some slaves may miss first-bit of data, or
+			 * misinterprete data as start condition.
+			 * We introduce delay of just over 1/2 clock cycle to
+			 * ensure master stretches the clock line thereby
+			 * avoiding setup time violation. Delay is introduced
+			 * only if I2C clock FSM is LOW. The delay is not needed
+			 * if I2C clock FSM is HIGH or FORCED_LOW.
+			 */
+			if ((status & I2C_STATUS_LOW_CLK_STATE) ==
+					I2C_STATUS_LOW_CLK_STATE)
+				udelay((dev->one_bit_t >> 1) + 1);
+			writel(data, dev->base + I2C_WRITE_DATA);
+			dev->pos++;
+			dev->cnt--;
+		} else
+			goto out_complete;
+	}
+
+	spin_unlock(&dev->lock);
+	return IRQ_HANDLED;
+
+ out_err:
+	dev->err = err;
+ out_complete:
+	complete(dev->complete);
+	spin_unlock(&dev->lock);
+	return IRQ_HANDLED;
+}
+
+static int
+msm_i2c_poll_writeready(struct msm_i2c_dev *dev)
+{
+	uint32_t retries = 0;
+
+	while (retries != 2000) {
+		uint32_t status = readl(dev->base + I2C_STATUS);
+
+		if (!(status & I2C_STATUS_WR_BUFFER_FULL))
+			return 0;
+		if (retries++ > 1000)
+			usleep_range(100, 200);
+	}
+	return -ETIMEDOUT;
+}
+
+static int
+msm_i2c_poll_notbusy(struct msm_i2c_dev *dev)
+{
+	uint32_t retries = 0;
+
+	while (retries != 2000) {
+		uint32_t status = readl(dev->base + I2C_STATUS);
+
+		if (!(status & I2C_STATUS_BUS_ACTIVE))
+			return 0;
+		if (retries++ > 1000)
+			usleep_range(100, 200);
+	}
+	return -ETIMEDOUT;
+}
+
+static int
+msm_i2c_recover_bus_busy(struct msm_i2c_dev *dev, struct i2c_adapter *adap)
+{
+	int i;
+	int gpio_clk;
+	int gpio_dat;
+	uint32_t status = readl(dev->base + I2C_STATUS);
+	bool gpio_clk_status = false;
+
+	if (!(status & (I2C_STATUS_BUS_ACTIVE | I2C_STATUS_WR_BUFFER_FULL)))
+		return 0;
+
+	dev->pdata->msm_i2c_config_gpio(adap->nr, 0);
+	/* Even adapter is primary and Odd adapter is AUX */
+	if (adap->nr % 2) {
+		gpio_clk = dev->pdata->aux_clk;
+		gpio_dat = dev->pdata->aux_dat;
+	} else {
+		gpio_clk = dev->pdata->pri_clk;
+		gpio_dat = dev->pdata->pri_dat;
+	}
+
+	disable_irq(dev->irq);
+	if (status & I2C_STATUS_RD_BUFFER_FULL) {
+		dev_warn(dev->dev, "Read buffer full, status %x, intf %x\n",
+			 status, readl(dev->base + I2C_INTERFACE_SELECT));
+		writel(I2C_WRITE_DATA_LAST_BYTE, dev->base + I2C_WRITE_DATA);
+		readl(dev->base + I2C_READ_DATA);
+	} else if (status & I2C_STATUS_BUS_MASTER) {
+		dev_warn(dev->dev, "Still the bus master, status %x, intf %x\n",
+			 status, readl(dev->base + I2C_INTERFACE_SELECT));
+		writel(I2C_WRITE_DATA_LAST_BYTE | 0xff,
+		       dev->base + I2C_WRITE_DATA);
+	}
+
+	for (i = 0; i < 9; i++) {
+		if (gpio_get_value(gpio_dat) && gpio_clk_status)
+			break;
+		gpio_direction_output(gpio_clk, 0);
+		udelay(5);
+		gpio_direction_output(gpio_dat, 0);
+		udelay(5);
+		gpio_direction_input(gpio_clk);
+		udelay(5);
+		if (!gpio_get_value(gpio_clk))
+			usleep_range(20, 30);
+		if (!gpio_get_value(gpio_clk))
+			msleep(10);
+		gpio_clk_status = gpio_get_value(gpio_clk);
+		gpio_direction_input(gpio_dat);
+		udelay(5);
+	}
+	dev->pdata->msm_i2c_config_gpio(adap->nr, 1);
+	udelay(10);
+
+	status = readl(dev->base + I2C_STATUS);
+	if (!(status & I2C_STATUS_BUS_ACTIVE)) {
+		dev_info(dev->dev, "Bus busy cleared after %d clock cycles, "
+			 "status %x, intf %x\n",
+			 i, status, readl(dev->base + I2C_INTERFACE_SELECT));
+		enable_irq(dev->irq);
+		return 0;
+	}
+
+	dev_err(dev->dev, "Bus still busy, status %x, intf %x\n",
+		 status, readl(dev->base + I2C_INTERFACE_SELECT));
+	enable_irq(dev->irq);
+	return -EBUSY;
+}
+
+static int
+msm_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+	DECLARE_COMPLETION_ONSTACK(complete);
+	struct msm_i2c_dev *dev = i2c_get_adapdata(adap);
+	int ret;
+	int rem = num;
+	uint16_t addr;
+	long timeout;
+	unsigned long flags;
+	int check_busy = 1;
+
+	del_timer_sync(&dev->pwr_timer);
+	mutex_lock(&dev->mlock);
+	if (dev->suspended) {
+		mutex_unlock(&dev->mlock);
+		return -EIO;
+	}
+
+	if (dev->clk_state == 0) {
+		dev_dbg(dev->dev, "I2C_Power: Enable I2C clock(s)\n");
+		msm_i2c_pwr_mgmt(dev, 1);
+	}
+
+	/* Don't allow power collapse until we release remote spinlock */
+	pm_qos_update_request(&dev->pm_qos_req,  dev->pdata->pm_lat);
+	if (dev->pdata->rmutex) {
+		remote_mutex_lock(&dev->r_lock);
+		/* If other processor did some transactions, we may have
+		 * interrupt pending. Clear it
+		 */
+		irq_get_chip(dev->irq)->irq_ack(irq_get_irq_data(dev->irq));
+	}
+
+	if (adap == &dev->adap_pri)
+		writel(0, dev->base + I2C_INTERFACE_SELECT);
+	else
+		writel(I2C_INTERFACE_SELECT_INTF_SELECT,
+				dev->base + I2C_INTERFACE_SELECT);
+	enable_irq(dev->irq);
+	while (rem) {
+		addr = msgs->addr << 1;
+		if (msgs->flags & I2C_M_RD)
+			addr |= 1;
+
+		spin_lock_irqsave(&dev->lock, flags);
+		dev->msg = msgs;
+		dev->rem = rem;
+		dev->pos = 0;
+		dev->err = 0;
+		dev->flush_cnt = 0;
+		dev->cnt = msgs->len;
+		dev->complete = &complete;
+		spin_unlock_irqrestore(&dev->lock, flags);
+
+		if (check_busy) {
+			ret = msm_i2c_poll_notbusy(dev);
+			if (ret)
+				ret = msm_i2c_recover_bus_busy(dev, adap);
+				if (ret) {
+					dev_err(dev->dev,
+						"Error waiting for notbusy\n");
+					goto out_err;
+				}
+			check_busy = 0;
+		}
+
+		if (rem == 1 && msgs->len == 0)
+			addr |= I2C_WRITE_DATA_LAST_BYTE;
+
+		/* Wait for WR buffer not full */
+		ret = msm_i2c_poll_writeready(dev);
+		if (ret) {
+			ret = msm_i2c_recover_bus_busy(dev, adap);
+			if (ret) {
+				dev_err(dev->dev,
+				"Error waiting for write ready before addr\n");
+				goto out_err;
+			}
+		}
+
+		/* special case for doing 1 byte read.
+		 * There should be no scheduling between I2C controller becoming
+		 * ready to read and writing LAST-BYTE to I2C controller
+		 * This will avoid potential of I2C controller starting to latch
+		 * another extra byte.
+		 */
+		if ((msgs->len == 1) && (msgs->flags & I2C_M_RD)) {
+			uint32_t retries = 0;
+			spin_lock_irqsave(&dev->lock, flags);
+
+			writel(I2C_WRITE_DATA_ADDR_BYTE | addr,
+				dev->base + I2C_WRITE_DATA);
+
+			/* Poll for I2C controller going into RX_DATA mode to
+			 * ensure controller goes into receive mode.
+			 * Just checking write_buffer_full may not work since
+			 * there is delay between the write-buffer becoming
+			 * empty and the slave sending ACK to ensure I2C
+			 * controller goes in receive mode to receive data.
+			 */
+			while (retries != 2000) {
+				uint32_t status = readl(dev->base + I2C_STATUS);
+
+					if ((status & I2C_STATUS_RX_DATA_STATE)
+						== I2C_STATUS_RX_DATA_STATE)
+						break;
+				retries++;
+			}
+			if (retries >= 2000) {
+				dev->rd_acked = 0;
+				spin_unlock_irqrestore(&dev->lock, flags);
+				/* 1-byte-reads from slow devices in interrupt
+				 * context
+				 */
+				goto wait_for_int;
+			}
+
+			dev->rd_acked = 1;
+			writel(I2C_WRITE_DATA_LAST_BYTE,
+					dev->base + I2C_WRITE_DATA);
+			spin_unlock_irqrestore(&dev->lock, flags);
+		} else {
+			writel(I2C_WRITE_DATA_ADDR_BYTE | addr,
+					 dev->base + I2C_WRITE_DATA);
+		}
+		/* Polling and waiting for write_buffer_empty is not necessary.
+		 * Even worse, if we do, it can result in invalid status and
+		 * error if interrupt(s) occur while polling.
+		 */
+
+		/*
+		 * Now that we've setup the xfer, the ISR will transfer the data
+		 * and wake us up with dev->err set if there was an error
+		 */
+wait_for_int:
+
+		timeout = wait_for_completion_timeout(&complete, HZ);
+		if (!timeout) {
+			dev_err(dev->dev, "Transaction timed out\n");
+			writel(I2C_WRITE_DATA_LAST_BYTE,
+				dev->base + I2C_WRITE_DATA);
+			msleep(100);
+			/* FLUSH */
+			readl(dev->base + I2C_READ_DATA);
+			readl(dev->base + I2C_STATUS);
+			ret = -ETIMEDOUT;
+			goto out_err;
+		}
+		if (dev->err) {
+			dev_err(dev->dev,
+				"(%04x) Error during data xfer (%d)\n",
+				addr, dev->err);
+			ret = dev->err;
+			goto out_err;
+		}
+
+		if (msgs->flags & I2C_M_RD)
+			check_busy = 1;
+
+		msgs++;
+		rem--;
+	}
+
+	ret = num;
+ out_err:
+	spin_lock_irqsave(&dev->lock, flags);
+	dev->complete = NULL;
+	dev->msg = NULL;
+	dev->rem = 0;
+	dev->pos = 0;
+	dev->err = 0;
+	dev->flush_cnt = 0;
+	dev->cnt = 0;
+	spin_unlock_irqrestore(&dev->lock, flags);
+	disable_irq(dev->irq);
+	if (dev->pdata->rmutex)
+		remote_mutex_unlock(&dev->r_lock);
+	pm_qos_update_request(&dev->pm_qos_req,
+			      PM_QOS_DEFAULT_VALUE);
+	mod_timer(&dev->pwr_timer, (jiffies + 3*HZ));
+	mutex_unlock(&dev->mlock);
+	return ret;
+}
+
+static u32
+msm_i2c_func(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
+}
+
+static const struct i2c_algorithm msm_i2c_algo = {
+	.master_xfer	= msm_i2c_xfer,
+	.functionality	= msm_i2c_func,
+};
+
+static int
+msm_i2c_probe(struct platform_device *pdev)
+{
+	struct msm_i2c_dev	*dev;
+	struct resource		*mem, *irq, *ioarea;
+	int ret;
+	int fs_div;
+	int hs_div;
+	int i2c_clk;
+	int clk_ctl;
+	struct clk *clk;
+	struct msm_i2c_platform_data *pdata;
+
+	printk(KERN_INFO "msm_i2c_probe\n");
+
+	/* NOTE: driver uses the static register mapping */
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem) {
+		dev_err(&pdev->dev, "no mem resource?\n");
+		return -ENODEV;
+	}
+	irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!irq) {
+		dev_err(&pdev->dev, "no irq resource?\n");
+		return -ENODEV;
+	}
+
+	ioarea = request_mem_region(mem->start, (mem->end - mem->start) + 1,
+			pdev->name);
+	if (!ioarea) {
+		dev_err(&pdev->dev, "I2C region already claimed\n");
+		return -EBUSY;
+	}
+	clk = clk_get(&pdev->dev, "core_clk");
+	if (IS_ERR(clk)) {
+		dev_err(&pdev->dev, "Could not get clock\n");
+		ret = PTR_ERR(clk);
+		goto err_clk_get_failed;
+	}
+
+	pdata = pdev->dev.platform_data;
+	if (!pdata) {
+		dev_err(&pdev->dev, "platform data not initialized\n");
+		ret = -ENOSYS;
+		goto err_clk_get_failed;
+	}
+	if (!pdata->msm_i2c_config_gpio) {
+		dev_err(&pdev->dev, "config_gpio function not initialized\n");
+		ret = -ENOSYS;
+		goto err_clk_get_failed;
+	}
+	/* We support frequencies upto FAST Mode(400KHz) */
+	if (pdata->clk_freq <= 0 || pdata->clk_freq > 400000) {
+		dev_err(&pdev->dev, "clock frequency not supported\n");
+		ret = -EIO;
+		goto err_clk_get_failed;
+	}
+
+	dev = kzalloc(sizeof(struct msm_i2c_dev), GFP_KERNEL);
+	if (!dev) {
+		ret = -ENOMEM;
+		goto err_alloc_dev_failed;
+	}
+
+	dev->dev = &pdev->dev;
+	dev->irq = irq->start;
+	dev->clk = clk;
+	dev->pdata = pdata;
+	dev->base = ioremap(mem->start, (mem->end - mem->start) + 1);
+	if (!dev->base) {
+		ret = -ENOMEM;
+		goto err_ioremap_failed;
+	}
+
+	dev->one_bit_t = USEC_PER_SEC/pdata->clk_freq;
+	spin_lock_init(&dev->lock);
+	platform_set_drvdata(pdev, dev);
+
+	clk_enable(clk);
+
+	if (pdata->rmutex) {
+		struct remote_mutex_id rmid;
+		rmid.r_spinlock_id = pdata->rsl_id;
+		rmid.delay_us = 10000000/pdata->clk_freq;
+		if (remote_mutex_init(&dev->r_lock, &rmid) != 0)
+			pdata->rmutex = 0;
+	}
+	/* I2C_HS_CLK = I2C_CLK/(3*(HS_DIVIDER_VALUE+1) */
+	/* I2C_FS_CLK = I2C_CLK/(2*(FS_DIVIDER_VALUE+3) */
+	/* FS_DIVIDER_VALUE = ((I2C_CLK / I2C_FS_CLK) / 2) - 3 */
+	i2c_clk = 19200000; /* input clock */
+	fs_div = ((i2c_clk / pdata->clk_freq) / 2) - 3;
+	hs_div = 3;
+	clk_ctl = ((hs_div & 0x7) << 8) | (fs_div & 0xff);
+	writel(clk_ctl, dev->base + I2C_CLK_CTL);
+	printk(KERN_INFO "msm_i2c_probe: clk_ctl %x, %d Hz\n",
+	       clk_ctl, i2c_clk / (2 * ((clk_ctl & 0xff) + 3)));
+
+	i2c_set_adapdata(&dev->adap_pri, dev);
+	dev->adap_pri.algo = &msm_i2c_algo;
+	strlcpy(dev->adap_pri.name,
+		"MSM I2C adapter-PRI",
+		sizeof(dev->adap_pri.name));
+
+	dev->adap_pri.nr = pdev->id;
+	ret = i2c_add_numbered_adapter(&dev->adap_pri);
+	if (ret) {
+		dev_err(&pdev->dev, "Primary i2c_add_adapter failed\n");
+		goto err_i2c_add_adapter_failed;
+	}
+
+	i2c_set_adapdata(&dev->adap_aux, dev);
+	dev->adap_aux.algo = &msm_i2c_algo;
+	strlcpy(dev->adap_aux.name,
+		"MSM I2C adapter-AUX",
+		sizeof(dev->adap_aux.name));
+
+	dev->adap_aux.nr = pdev->id + 1;
+	ret = i2c_add_numbered_adapter(&dev->adap_aux);
+	if (ret) {
+		dev_err(&pdev->dev, "auxiliary i2c_add_adapter failed\n");
+		i2c_del_adapter(&dev->adap_pri);
+		goto err_i2c_add_adapter_failed;
+	}
+	ret = request_irq(dev->irq, msm_i2c_interrupt,
+			IRQF_TRIGGER_RISING, pdev->name, dev);
+	if (ret) {
+		dev_err(&pdev->dev, "request_irq failed\n");
+		goto err_request_irq_failed;
+	}
+	pm_qos_add_request(&dev->pm_qos_req, PM_QOS_CPU_DMA_LATENCY,
+					     PM_QOS_DEFAULT_VALUE);
+	disable_irq(dev->irq);
+	dev->suspended = 0;
+	mutex_init(&dev->mlock);
+	dev->clk_state = 0;
+	/* Config GPIOs for primary and secondary lines */
+	pdata->msm_i2c_config_gpio(dev->adap_pri.nr, 1);
+	pdata->msm_i2c_config_gpio(dev->adap_aux.nr, 1);
+	clk_disable(dev->clk);
+	setup_timer(&dev->pwr_timer, msm_i2c_pwr_timer, (unsigned long) dev);
+
+	return 0;
+
+err_request_irq_failed:
+	i2c_del_adapter(&dev->adap_pri);
+	i2c_del_adapter(&dev->adap_aux);
+err_i2c_add_adapter_failed:
+	clk_disable(clk);
+	iounmap(dev->base);
+err_ioremap_failed:
+	kfree(dev);
+err_alloc_dev_failed:
+	clk_put(clk);
+err_clk_get_failed:
+	release_mem_region(mem->start, (mem->end - mem->start) + 1);
+	return ret;
+}
+
+static int
+msm_i2c_remove(struct platform_device *pdev)
+{
+	struct msm_i2c_dev	*dev = platform_get_drvdata(pdev);
+	struct resource		*mem;
+
+	/* Grab mutex to ensure ongoing transaction is over */
+	mutex_lock(&dev->mlock);
+	dev->suspended = 1;
+	mutex_unlock(&dev->mlock);
+	mutex_destroy(&dev->mlock);
+	del_timer_sync(&dev->pwr_timer);
+	if (dev->clk_state != 0)
+		msm_i2c_pwr_mgmt(dev, 0);
+	platform_set_drvdata(pdev, NULL);
+	pm_qos_remove_request(&dev->pm_qos_req);
+	free_irq(dev->irq, dev);
+	i2c_del_adapter(&dev->adap_pri);
+	i2c_del_adapter(&dev->adap_aux);
+	clk_put(dev->clk);
+	iounmap(dev->base);
+	kfree(dev);
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (mem)
+		release_mem_region(mem->start, (mem->end - mem->start) + 1);
+	return 0;
+}
+
+static int msm_i2c_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct msm_i2c_dev *dev = platform_get_drvdata(pdev);
+	/* Wait until current transaction finishes
+	 * Make sure remote lock is released before we suspend
+	 */
+	if (dev) {
+		/* Grab mutex to ensure ongoing transaction is over */
+		mutex_lock(&dev->mlock);
+		dev->suspended = 1;
+		mutex_unlock(&dev->mlock);
+		del_timer_sync(&dev->pwr_timer);
+		if (dev->clk_state != 0)
+			msm_i2c_pwr_mgmt(dev, 0);
+	}
+
+	return 0;
+}
+
+static int msm_i2c_resume(struct platform_device *pdev)
+{
+	struct msm_i2c_dev *dev = platform_get_drvdata(pdev);
+	dev->suspended = 0;
+	return 0;
+}
+
+static struct platform_driver msm_i2c_driver = {
+	.probe		= msm_i2c_probe,
+	.remove		= msm_i2c_remove,
+	.suspend	= msm_i2c_suspend,
+	.resume		= msm_i2c_resume,
+	.driver		= {
+		.name	= "msm_i2c",
+		.owner	= THIS_MODULE,
+	},
+};
+
+/* I2C may be needed to bring up other drivers */
+static int __init
+msm_i2c_init_driver(void)
+{
+	return platform_driver_register(&msm_i2c_driver);
+}
+subsys_initcall(msm_i2c_init_driver);
+
+static void __exit msm_i2c_exit_driver(void)
+{
+	platform_driver_unregister(&msm_i2c_driver);
+}
+module_exit(msm_i2c_exit_driver);
+
diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
new file mode 100644
index 0000000..297afa7
--- /dev/null
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -0,0 +1,1515 @@
+/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+/*
+ * QUP driver for Qualcomm MSM platforms
+ *
+ */
+
+/* #define DEBUG */
+
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/mutex.h>
+#include <linux/timer.h>
+#include <linux/slab.h>
+#include <mach/board.h>
+#include <mach/gpiomux.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_i2c.h>
+
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.2");
+MODULE_ALIAS("platform:i2c_qup");
+
+/* QUP Registers */
+enum {
+	QUP_CONFIG              = 0x0,
+	QUP_STATE               = 0x4,
+	QUP_IO_MODE             = 0x8,
+	QUP_SW_RESET            = 0xC,
+	QUP_OPERATIONAL         = 0x18,
+	QUP_ERROR_FLAGS         = 0x1C,
+	QUP_ERROR_FLAGS_EN      = 0x20,
+	QUP_MX_READ_CNT         = 0x208,
+	QUP_MX_INPUT_CNT        = 0x200,
+	QUP_MX_WR_CNT           = 0x100,
+	QUP_OUT_DEBUG           = 0x108,
+	QUP_OUT_FIFO_CNT        = 0x10C,
+	QUP_OUT_FIFO_BASE       = 0x110,
+	QUP_IN_READ_CUR         = 0x20C,
+	QUP_IN_DEBUG            = 0x210,
+	QUP_IN_FIFO_CNT         = 0x214,
+	QUP_IN_FIFO_BASE        = 0x218,
+	QUP_I2C_CLK_CTL         = 0x400,
+	QUP_I2C_STATUS          = 0x404,
+};
+
+/* QUP States and reset values */
+enum {
+	QUP_RESET_STATE         = 0,
+	QUP_RUN_STATE           = 1U,
+	QUP_STATE_MASK          = 3U,
+	QUP_PAUSE_STATE         = 3U,
+	QUP_STATE_VALID         = 1U << 2,
+	QUP_I2C_MAST_GEN        = 1U << 4,
+	QUP_OPERATIONAL_RESET   = 0xFF0,
+	QUP_I2C_STATUS_RESET    = 0xFFFFFC,
+};
+
+/* QUP OPERATIONAL FLAGS */
+enum {
+	QUP_OUT_SVC_FLAG        = 1U << 8,
+	QUP_IN_SVC_FLAG         = 1U << 9,
+	QUP_MX_INPUT_DONE       = 1U << 11,
+};
+
+/* I2C mini core related values */
+enum {
+	I2C_MINI_CORE           = 2U << 8,
+	I2C_N_VAL               = 0xF,
+
+};
+
+/* Packing Unpacking words in FIFOs , and IO modes*/
+enum {
+	QUP_WR_BLK_MODE  = 1U << 10,
+	QUP_RD_BLK_MODE  = 1U << 12,
+	QUP_UNPACK_EN = 1U << 14,
+	QUP_PACK_EN = 1U << 15,
+};
+
+/* QUP tags */
+enum {
+	QUP_OUT_NOP   = 0,
+	QUP_OUT_START = 1U << 8,
+	QUP_OUT_DATA  = 2U << 8,
+	QUP_OUT_STOP  = 3U << 8,
+	QUP_OUT_REC   = 4U << 8,
+	QUP_IN_DATA   = 5U << 8,
+	QUP_IN_STOP   = 6U << 8,
+	QUP_IN_NACK   = 7U << 8,
+};
+
+/* Status, Error flags */
+enum {
+	I2C_STATUS_WR_BUFFER_FULL  = 1U << 0,
+	I2C_STATUS_BUS_ACTIVE      = 1U << 8,
+	I2C_STATUS_BUS_MASTER	   = 1U << 9,
+	I2C_STATUS_ERROR_MASK      = 0x38000FC,
+	QUP_I2C_NACK_FLAG          = 1U << 3,
+	QUP_IN_NOT_EMPTY           = 1U << 5,
+	QUP_STATUS_ERROR_FLAGS     = 0x7C,
+};
+
+/* Master status clock states */
+enum {
+	I2C_CLK_RESET_BUSIDLE_STATE	= 0,
+	I2C_CLK_FORCED_LOW_STATE	= 5,
+};
+
+#define QUP_MAX_CLK_STATE_RETRIES	300
+
+static char const * const i2c_rsrcs[] = {"i2c_clk", "i2c_sda"};
+
+static struct gpiomux_setting recovery_config = {
+	.func = GPIOMUX_FUNC_GPIO,
+	.drv = GPIOMUX_DRV_8MA,
+	.pull = GPIOMUX_PULL_NONE,
+};
+
+struct qup_i2c_dev {
+	struct device                *dev;
+	void __iomem                 *base;		/* virtual */
+	void __iomem                 *gsbi;		/* virtual */
+	int                          in_irq;
+	int                          out_irq;
+	int                          err_irq;
+	int                          num_irqs;
+	struct clk                   *clk;
+	struct clk                   *pclk;
+	struct i2c_adapter           adapter;
+
+	struct i2c_msg               *msg;
+	int                          pos;
+	int                          cnt;
+	int                          err;
+	int                          mode;
+	int                          clk_ctl;
+	int                          one_bit_t;
+	int                          out_fifo_sz;
+	int                          in_fifo_sz;
+	int                          out_blk_sz;
+	int                          in_blk_sz;
+	int                          wr_sz;
+	struct msm_i2c_platform_data *pdata;
+	int                          suspended;
+	int                          clk_state;
+	struct timer_list            pwr_timer;
+	struct mutex                 mlock;
+	void                         *complete;
+	int                          i2c_gpios[ARRAY_SIZE(i2c_rsrcs)];
+};
+
+#ifdef DEBUG
+static void
+qup_print_status(struct qup_i2c_dev *dev)
+{
+	uint32_t val;
+	val = readl_relaxed(dev->base+QUP_CONFIG);
+	dev_dbg(dev->dev, "Qup config is :0x%x\n", val);
+	val = readl_relaxed(dev->base+QUP_STATE);
+	dev_dbg(dev->dev, "Qup state is :0x%x\n", val);
+	val = readl_relaxed(dev->base+QUP_IO_MODE);
+	dev_dbg(dev->dev, "Qup mode is :0x%x\n", val);
+}
+#else
+static inline void qup_print_status(struct qup_i2c_dev *dev)
+{
+}
+#endif
+
+static irqreturn_t
+qup_i2c_interrupt(int irq, void *devid)
+{
+	struct qup_i2c_dev *dev = devid;
+	uint32_t status = readl_relaxed(dev->base + QUP_I2C_STATUS);
+	uint32_t status1 = readl_relaxed(dev->base + QUP_ERROR_FLAGS);
+	uint32_t op_flgs = readl_relaxed(dev->base + QUP_OPERATIONAL);
+	int err = 0;
+
+	if (!dev->msg || !dev->complete) {
+		/* Clear Error interrupt if it's a level triggered interrupt*/
+		if (dev->num_irqs == 1) {
+			writel_relaxed(QUP_RESET_STATE, dev->base+QUP_STATE);
+			/* Ensure that state is written before ISR exits */
+			mb();
+		}
+		return IRQ_HANDLED;
+	}
+
+	if (status & I2C_STATUS_ERROR_MASK) {
+		dev_err(dev->dev, "QUP: I2C status flags :0x%x, irq:%d\n",
+			status, irq);
+		err = status;
+		/* Clear Error interrupt if it's a level triggered interrupt*/
+		if (dev->num_irqs == 1) {
+			writel_relaxed(QUP_RESET_STATE, dev->base+QUP_STATE);
+			/* Ensure that state is written before ISR exits */
+			mb();
+		}
+		goto intr_done;
+	}
+
+	if (status1 & 0x7F) {
+		dev_err(dev->dev, "QUP: QUP status flags :0x%x\n", status1);
+		err = -status1;
+		/* Clear Error interrupt if it's a level triggered interrupt*/
+		if (dev->num_irqs == 1) {
+			writel_relaxed((status1 & QUP_STATUS_ERROR_FLAGS),
+				dev->base + QUP_ERROR_FLAGS);
+			/* Ensure that error flags are cleared before ISR
+			 * exits
+			 */
+			mb();
+		}
+		goto intr_done;
+	}
+
+	if ((dev->num_irqs == 3) && (dev->msg->flags == I2C_M_RD)
+		&& (irq == dev->out_irq))
+		return IRQ_HANDLED;
+	if (op_flgs & QUP_OUT_SVC_FLAG) {
+		writel_relaxed(QUP_OUT_SVC_FLAG, dev->base + QUP_OPERATIONAL);
+		/* Ensure that service flag is acknowledged before ISR exits */
+		mb();
+	}
+	if (dev->msg->flags == I2C_M_RD) {
+		if ((op_flgs & QUP_MX_INPUT_DONE) ||
+			(op_flgs & QUP_IN_SVC_FLAG)) {
+			writel_relaxed(QUP_IN_SVC_FLAG, dev->base
+					+ QUP_OPERATIONAL);
+			/* Ensure that service flag is acknowledged before ISR
+			 * exits
+			 */
+			mb();
+		} else
+			return IRQ_HANDLED;
+	}
+
+intr_done:
+	dev_dbg(dev->dev, "QUP intr= %d, i2c status=0x%x, qup status = 0x%x\n",
+			irq, status, status1);
+	qup_print_status(dev);
+	dev->err = err;
+	complete(dev->complete);
+	return IRQ_HANDLED;
+}
+
+static int
+qup_i2c_poll_state(struct qup_i2c_dev *dev, uint32_t req_state, bool only_valid)
+{
+	uint32_t retries = 0;
+
+	dev_dbg(dev->dev, "Polling for state:0x%x, or valid-only:%d\n",
+				req_state, only_valid);
+
+	while (retries != 2000) {
+		uint32_t status = readl_relaxed(dev->base + QUP_STATE);
+
+		/*
+		 * If only valid bit needs to be checked, requested state is
+		 * 'don't care'
+		 */
+		if (status & QUP_STATE_VALID) {
+			if (only_valid)
+				return 0;
+			else if ((req_state & QUP_I2C_MAST_GEN) &&
+					(status & QUP_I2C_MAST_GEN))
+				return 0;
+			else if ((status & QUP_STATE_MASK) == req_state)
+				return 0;
+		}
+		if (retries++ == 1000)
+			udelay(100);
+	}
+	return -ETIMEDOUT;
+}
+
+static int
+qup_update_state(struct qup_i2c_dev *dev, uint32_t state)
+{
+	if (qup_i2c_poll_state(dev, 0, true) != 0)
+		return -EIO;
+	writel_relaxed(state, dev->base + QUP_STATE);
+	if (qup_i2c_poll_state(dev, state, false) != 0)
+		return -EIO;
+	return 0;
+}
+
+/*
+ * Before calling qup_config_core_on_en(), please make
+ * sure that QuPE core is in RESET state.
+ */
+static void
+qup_config_core_on_en(struct qup_i2c_dev *dev)
+{
+	uint32_t status;
+
+	status = readl_relaxed(dev->base + QUP_CONFIG);
+	status |= BIT(13);
+	writel_relaxed(status, dev->base + QUP_CONFIG);
+	/* making sure that write has really gone through */
+	mb();
+}
+
+static void
+qup_i2c_pwr_mgmt(struct qup_i2c_dev *dev, unsigned int state)
+{
+	dev->clk_state = state;
+	if (state != 0) {
+		clk_enable(dev->clk);
+		clk_enable(dev->pclk);
+	} else {
+		qup_update_state(dev, QUP_RESET_STATE);
+		clk_disable(dev->clk);
+		qup_config_core_on_en(dev);
+		clk_disable(dev->pclk);
+	}
+}
+
+static void
+qup_i2c_pwr_timer(unsigned long data)
+{
+	struct qup_i2c_dev *dev = (struct qup_i2c_dev *) data;
+	dev_dbg(dev->dev, "QUP_Power: Inactivity based power management\n");
+	if (dev->clk_state == 1)
+		qup_i2c_pwr_mgmt(dev, 0);
+}
+
+static int
+qup_i2c_poll_writeready(struct qup_i2c_dev *dev, int rem)
+{
+	uint32_t retries = 0;
+
+	while (retries != 2000) {
+		uint32_t status = readl_relaxed(dev->base + QUP_I2C_STATUS);
+
+		if (!(status & I2C_STATUS_WR_BUFFER_FULL)) {
+			if (((dev->msg->flags & I2C_M_RD) || (rem == 0)) &&
+				!(status & I2C_STATUS_BUS_ACTIVE))
+				return 0;
+			else if ((dev->msg->flags == 0) && (rem > 0))
+				return 0;
+			else /* 1-bit delay before we check for bus busy */
+				udelay(dev->one_bit_t);
+		}
+		if (retries++ == 1000) {
+			/*
+			 * Wait for FIFO number of bytes to be absolutely sure
+			 * that I2C write state machine is not idle. Each byte
+			 * takes 9 clock cycles. (8 bits + 1 ack)
+			 */
+			usleep_range((dev->one_bit_t * (dev->out_fifo_sz * 9)),
+				(dev->one_bit_t * (dev->out_fifo_sz * 9)));
+		}
+	}
+	qup_print_status(dev);
+	return -ETIMEDOUT;
+}
+
+static int qup_i2c_poll_clock_ready(struct qup_i2c_dev *dev)
+{
+	uint32_t retries = 0;
+
+	/*
+	 * Wait for the clock state to transition to either IDLE or FORCED
+	 * LOW.  This will usually happen within one cycle of the i2c clock.
+	 */
+
+	while (retries++ < QUP_MAX_CLK_STATE_RETRIES) {
+		uint32_t status = readl_relaxed(dev->base + QUP_I2C_STATUS);
+		uint32_t clk_state = (status >> 13) & 0x7;
+
+		if (clk_state == I2C_CLK_RESET_BUSIDLE_STATE ||
+				clk_state == I2C_CLK_FORCED_LOW_STATE)
+			return 0;
+		/* 1-bit delay before we check again */
+		udelay(dev->one_bit_t);
+	}
+
+	dev_err(dev->dev, "Error waiting for clk ready\n");
+	return -ETIMEDOUT;
+}
+
+static inline int qup_i2c_request_gpios(struct qup_i2c_dev *dev)
+{
+	int i;
+	int result = 0;
+
+	for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) {
+		if (dev->i2c_gpios[i] >= 0) {
+			result = gpio_request(dev->i2c_gpios[i], i2c_rsrcs[i]);
+			if (result) {
+				dev_err(dev->dev,
+					"gpio_request for pin %d failed\
+					with error %d\n", dev->i2c_gpios[i],
+					result);
+				goto error;
+			}
+		}
+	}
+	return 0;
+
+error:
+	for (; --i >= 0;) {
+		if (dev->i2c_gpios[i] >= 0)
+			gpio_free(dev->i2c_gpios[i]);
+	}
+	return result;
+}
+
+static inline void qup_i2c_free_gpios(struct qup_i2c_dev *dev)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) {
+		if (dev->i2c_gpios[i] >= 0)
+			gpio_free(dev->i2c_gpios[i]);
+	}
+}
+
+#ifdef DEBUG
+static void qup_verify_fifo(struct qup_i2c_dev *dev, uint32_t val,
+				uint32_t addr, int rdwr)
+{
+	if (rdwr)
+		dev_dbg(dev->dev, "RD:Wrote 0x%x to out_ff:0x%x\n", val, addr);
+	else
+		dev_dbg(dev->dev, "WR:Wrote 0x%x to out_ff:0x%x\n", val, addr);
+}
+#else
+static inline void qup_verify_fifo(struct qup_i2c_dev *dev, uint32_t val,
+				uint32_t addr, int rdwr)
+{
+}
+#endif
+
+static void
+qup_issue_read(struct qup_i2c_dev *dev, struct i2c_msg *msg, int *idx,
+		uint32_t carry_over)
+{
+	uint16_t addr = (msg->addr << 1) | 1;
+	/* QUP limit 256 bytes per read. By HW design, 0 in the 8-bit field
+	 * is treated as 256 byte read.
+	 */
+	uint16_t rd_len = ((dev->cnt == 256) ? 0 : dev->cnt);
+
+	if (*idx % 4) {
+		writel_relaxed(carry_over | ((QUP_OUT_START | addr) << 16),
+		dev->base + QUP_OUT_FIFO_BASE);/* + (*idx-2)); */
+
+		qup_verify_fifo(dev, carry_over |
+			((QUP_OUT_START | addr) << 16), (uint32_t)dev->base
+			+ QUP_OUT_FIFO_BASE + (*idx - 2), 1);
+		writel_relaxed((QUP_OUT_REC | rd_len),
+			dev->base + QUP_OUT_FIFO_BASE);/* + (*idx+2)); */
+
+		qup_verify_fifo(dev, (QUP_OUT_REC | rd_len),
+		(uint32_t)dev->base + QUP_OUT_FIFO_BASE + (*idx + 2), 1);
+	} else {
+		writel_relaxed(((QUP_OUT_REC | rd_len) << 16)
+			| QUP_OUT_START | addr,
+			dev->base + QUP_OUT_FIFO_BASE);/* + (*idx)); */
+
+		qup_verify_fifo(dev, QUP_OUT_REC << 16 | rd_len << 16 |
+		QUP_OUT_START | addr,
+		(uint32_t)dev->base + QUP_OUT_FIFO_BASE + (*idx), 1);
+	}
+	*idx += 4;
+}
+
+static void
+qup_issue_write(struct qup_i2c_dev *dev, struct i2c_msg *msg, int rem,
+			int *idx, uint32_t *carry_over)
+{
+	int entries = dev->cnt;
+	int empty_sl = dev->wr_sz - ((*idx) >> 1);
+	int i = 0;
+	uint32_t val = 0;
+	uint32_t last_entry = 0;
+	uint16_t addr = msg->addr << 1;
+
+	if (dev->pos == 0) {
+		if (*idx % 4) {
+			writel_relaxed(*carry_over | ((QUP_OUT_START |
+							addr) << 16),
+					dev->base + QUP_OUT_FIFO_BASE);
+
+			qup_verify_fifo(dev, *carry_over | QUP_OUT_START << 16 |
+				addr << 16, (uint32_t)dev->base +
+				QUP_OUT_FIFO_BASE + (*idx) - 2, 0);
+		} else
+			val = QUP_OUT_START | addr;
+		*idx += 2;
+		i++;
+		entries++;
+	} else {
+		/* Avoid setp time issue by adding 1 NOP when number of bytes
+		 * are more than FIFO/BLOCK size. setup time issue can't appear
+		 * otherwise since next byte to be written will always be ready
+		 */
+		val = (QUP_OUT_NOP | 1);
+		*idx += 2;
+		i++;
+		entries++;
+	}
+	if (entries > empty_sl)
+		entries = empty_sl;
+
+	for (; i < (entries - 1); i++) {
+		if (*idx % 4) {
+			writel_relaxed(val | ((QUP_OUT_DATA |
+				msg->buf[dev->pos]) << 16),
+				dev->base + QUP_OUT_FIFO_BASE);
+
+			qup_verify_fifo(dev, val | QUP_OUT_DATA << 16 |
+				msg->buf[dev->pos] << 16, (uint32_t)dev->base +
+				QUP_OUT_FIFO_BASE + (*idx) - 2, 0);
+		} else
+			val = QUP_OUT_DATA | msg->buf[dev->pos];
+		(*idx) += 2;
+		dev->pos++;
+	}
+	if (dev->pos < (msg->len - 1))
+		last_entry = QUP_OUT_DATA;
+	else if (rem > 1) /* not last array entry */
+		last_entry = QUP_OUT_DATA;
+	else
+		last_entry = QUP_OUT_STOP;
+	if ((*idx % 4) == 0) {
+		/*
+		 * If read-start and read-command end up in different fifos, it
+		 * may result in extra-byte being read due to extra-read cycle.
+		 * Avoid that by inserting NOP as the last entry of fifo only
+		 * if write command(s) leave 1 space in fifo.
+		 */
+		if (rem > 1) {
+			struct i2c_msg *next = msg + 1;
+			if (next->addr == msg->addr && (next->flags & I2C_M_RD)
+				&& *idx == ((dev->wr_sz*2) - 4)) {
+				writel_relaxed(((last_entry |
+					msg->buf[dev->pos]) |
+					((1 | QUP_OUT_NOP) << 16)), dev->base +
+					QUP_OUT_FIFO_BASE);/* + (*idx) - 2); */
+
+				qup_verify_fifo(dev,
+					((last_entry | msg->buf[dev->pos]) |
+					((1 | QUP_OUT_NOP) << 16)),
+					(uint32_t)dev->base +
+					QUP_OUT_FIFO_BASE + (*idx), 0);
+				*idx += 2;
+			} else if (next->flags == 0 && dev->pos == msg->len - 1
+					&& *idx < (dev->wr_sz*2) &&
+					(next->addr != msg->addr)) {
+				/* Last byte of an intermittent write */
+				writel_relaxed((QUP_OUT_STOP |
+						msg->buf[dev->pos]),
+					dev->base + QUP_OUT_FIFO_BASE);
+
+				qup_verify_fifo(dev,
+					QUP_OUT_STOP | msg->buf[dev->pos],
+					(uint32_t)dev->base +
+					QUP_OUT_FIFO_BASE + (*idx), 0);
+				*idx += 2;
+			} else
+				*carry_over = (last_entry | msg->buf[dev->pos]);
+		} else {
+			writel_relaxed((last_entry | msg->buf[dev->pos]),
+			dev->base + QUP_OUT_FIFO_BASE);/* + (*idx) - 2); */
+
+			qup_verify_fifo(dev, last_entry | msg->buf[dev->pos],
+			(uint32_t)dev->base + QUP_OUT_FIFO_BASE +
+			(*idx), 0);
+		}
+	} else {
+		writel_relaxed(val | ((last_entry | msg->buf[dev->pos]) << 16),
+		dev->base + QUP_OUT_FIFO_BASE);/* + (*idx) - 2); */
+
+		qup_verify_fifo(dev, val | (last_entry << 16) |
+		(msg->buf[dev->pos] << 16), (uint32_t)dev->base +
+		QUP_OUT_FIFO_BASE + (*idx) - 2, 0);
+	}
+
+	*idx += 2;
+	dev->pos++;
+	dev->cnt = msg->len - dev->pos;
+}
+
+static void
+qup_set_read_mode(struct qup_i2c_dev *dev, int rd_len)
+{
+	uint32_t wr_mode = (dev->wr_sz < dev->out_fifo_sz) ?
+				QUP_WR_BLK_MODE : 0;
+	if (rd_len > 256) {
+		dev_dbg(dev->dev, "HW limit: Breaking reads in chunk of 256\n");
+		rd_len = 256;
+	}
+	if (rd_len <= dev->in_fifo_sz) {
+		writel_relaxed(wr_mode | QUP_PACK_EN | QUP_UNPACK_EN,
+			dev->base + QUP_IO_MODE);
+		writel_relaxed(rd_len, dev->base + QUP_MX_READ_CNT);
+	} else {
+		writel_relaxed(wr_mode | QUP_RD_BLK_MODE |
+			QUP_PACK_EN | QUP_UNPACK_EN, dev->base + QUP_IO_MODE);
+		writel_relaxed(rd_len, dev->base + QUP_MX_INPUT_CNT);
+	}
+}
+
+static int
+qup_set_wr_mode(struct qup_i2c_dev *dev, int rem)
+{
+	int total_len = 0;
+	int ret = 0;
+	int len = dev->msg->len;
+	struct i2c_msg *next = NULL;
+	if (rem > 1)
+		next = dev->msg + 1;
+	while (rem > 1 && next->flags == 0 && (next->addr == dev->msg->addr)) {
+		len += next->len + 1;
+		next = next + 1;
+		rem--;
+	}
+	if (len >= (dev->out_fifo_sz - 1)) {
+		total_len = len + 1 + (len/(dev->out_blk_sz-1));
+
+		writel_relaxed(QUP_WR_BLK_MODE | QUP_PACK_EN | QUP_UNPACK_EN,
+			dev->base + QUP_IO_MODE);
+		dev->wr_sz = dev->out_blk_sz;
+	} else
+		writel_relaxed(QUP_PACK_EN | QUP_UNPACK_EN,
+			dev->base + QUP_IO_MODE);
+
+	if (rem > 1) {
+		if (next->addr == dev->msg->addr &&
+			next->flags == I2C_M_RD) {
+			qup_set_read_mode(dev, next->len);
+			/* make sure read start & read command are in 1 blk */
+			if ((total_len % dev->out_blk_sz) ==
+				(dev->out_blk_sz - 1))
+				total_len += 3;
+			else
+				total_len += 2;
+		}
+	}
+	/* WRITE COUNT register valid/used only in block mode */
+	if (dev->wr_sz == dev->out_blk_sz)
+		writel_relaxed(total_len, dev->base + QUP_MX_WR_CNT);
+	return ret;
+}
+
+
+static void qup_i2c_recover_bus_busy(struct qup_i2c_dev *dev)
+{
+	int i;
+	int gpio_clk;
+	int gpio_dat;
+	bool gpio_clk_status = false;
+	uint32_t status = readl_relaxed(dev->base + QUP_I2C_STATUS);
+	struct gpiomux_setting old_gpio_setting;
+
+	if (dev->pdata->msm_i2c_config_gpio)
+		return;
+
+	if (!(status & (I2C_STATUS_BUS_ACTIVE)) ||
+		(status & (I2C_STATUS_BUS_MASTER)))
+		return;
+
+	gpio_clk = dev->i2c_gpios[0];
+	gpio_dat = dev->i2c_gpios[1];
+
+	if ((gpio_clk == -1) && (gpio_dat == -1)) {
+		dev_err(dev->dev, "Recovery failed due to undefined GPIO's\n");
+		return;
+	}
+
+	disable_irq(dev->err_irq);
+	for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) {
+		if (msm_gpiomux_write(dev->i2c_gpios[i], GPIOMUX_ACTIVE,
+				&recovery_config, &old_gpio_setting)) {
+			dev_err(dev->dev, "GPIO pins have no active setting\n");
+			goto recovery_end;
+		}
+	}
+
+	dev_warn(dev->dev, "i2c_scl: %d, i2c_sda: %d\n",
+		 gpio_get_value(gpio_clk), gpio_get_value(gpio_dat));
+
+	for (i = 0; i < 9; i++) {
+		if (gpio_get_value(gpio_dat) && gpio_clk_status)
+			break;
+		gpio_direction_output(gpio_clk, 0);
+		udelay(5);
+		gpio_direction_output(gpio_dat, 0);
+		udelay(5);
+		gpio_direction_input(gpio_clk);
+		udelay(5);
+		if (!gpio_get_value(gpio_clk))
+			udelay(20);
+		if (!gpio_get_value(gpio_clk))
+			usleep_range(10000, 10000);
+		gpio_clk_status = gpio_get_value(gpio_clk);
+		gpio_direction_input(gpio_dat);
+		udelay(5);
+	}
+
+	/* Configure ALT funciton to QUP I2C*/
+	for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) {
+		msm_gpiomux_write(dev->i2c_gpios[i], GPIOMUX_ACTIVE,
+				&old_gpio_setting, NULL);
+	}
+
+	udelay(10);
+
+	status = readl_relaxed(dev->base + QUP_I2C_STATUS);
+	if (!(status & I2C_STATUS_BUS_ACTIVE)) {
+		dev_info(dev->dev, "Bus busy cleared after %d clock cycles, "
+			 "status %x\n",
+			 i, status);
+		goto recovery_end;
+	}
+
+	dev_warn(dev->dev, "Bus still busy, status %x\n", status);
+
+recovery_end:
+	enable_irq(dev->err_irq);
+}
+
+static int
+qup_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+	DECLARE_COMPLETION_ONSTACK(complete);
+	struct qup_i2c_dev *dev = i2c_get_adapdata(adap);
+	int ret;
+	int rem = num;
+	long timeout;
+	int err;
+
+	del_timer_sync(&dev->pwr_timer);
+	mutex_lock(&dev->mlock);
+
+	if (dev->suspended) {
+		mutex_unlock(&dev->mlock);
+		return -EIO;
+	}
+
+	if (dev->clk_state == 0)
+		qup_i2c_pwr_mgmt(dev, 1);
+
+	/* Initialize QUP registers during first transfer */
+	if (dev->clk_ctl == 0) {
+		int fs_div;
+		int hs_div;
+		uint32_t fifo_reg;
+
+		if (dev->gsbi) {
+			writel_relaxed(0x2 << 4, dev->gsbi);
+			/* GSBI memory is not in the same 1K region as other
+			 * QUP registers. mb() here ensures that the GSBI
+			 * register is updated in correct order and that the
+			 * write has gone through before programming QUP core
+			 * registers
+			 */
+			mb();
+		}
+
+		fs_div = ((dev->pdata->src_clk_rate
+				/ dev->pdata->clk_freq) / 2) - 3;
+		hs_div = 3;
+		dev->clk_ctl = ((hs_div & 0x7) << 8) | (fs_div & 0xff);
+		fifo_reg = readl_relaxed(dev->base + QUP_IO_MODE);
+		if (fifo_reg & 0x3)
+			dev->out_blk_sz = (fifo_reg & 0x3) * 16;
+		else
+			dev->out_blk_sz = 16;
+		if (fifo_reg & 0x60)
+			dev->in_blk_sz = ((fifo_reg & 0x60) >> 5) * 16;
+		else
+			dev->in_blk_sz = 16;
+		/*
+		 * The block/fifo size w.r.t. 'actual data' is 1/2 due to 'tag'
+		 * associated with each byte written/received
+		 */
+		dev->out_blk_sz /= 2;
+		dev->in_blk_sz /= 2;
+		dev->out_fifo_sz = dev->out_blk_sz *
+					(2 << ((fifo_reg & 0x1C) >> 2));
+		dev->in_fifo_sz = dev->in_blk_sz *
+					(2 << ((fifo_reg & 0x380) >> 7));
+		dev_dbg(dev->dev, "QUP IN:bl:%d, ff:%d, OUT:bl:%d, ff:%d\n",
+				dev->in_blk_sz, dev->in_fifo_sz,
+				dev->out_blk_sz, dev->out_fifo_sz);
+	}
+
+	writel_relaxed(1, dev->base + QUP_SW_RESET);
+	ret = qup_i2c_poll_state(dev, QUP_RESET_STATE, false);
+	if (ret) {
+		dev_err(dev->dev, "QUP Busy:Trying to recover\n");
+		goto out_err;
+	}
+
+	if (dev->num_irqs == 3) {
+		enable_irq(dev->in_irq);
+		enable_irq(dev->out_irq);
+	}
+	enable_irq(dev->err_irq);
+
+	/* Initialize QUP registers */
+	writel_relaxed(0, dev->base + QUP_CONFIG);
+	writel_relaxed(QUP_OPERATIONAL_RESET, dev->base + QUP_OPERATIONAL);
+	writel_relaxed(QUP_STATUS_ERROR_FLAGS, dev->base + QUP_ERROR_FLAGS_EN);
+
+	writel_relaxed(I2C_MINI_CORE | I2C_N_VAL, dev->base + QUP_CONFIG);
+
+	/* Initialize I2C mini core registers */
+	writel_relaxed(0, dev->base + QUP_I2C_CLK_CTL);
+	writel_relaxed(QUP_I2C_STATUS_RESET, dev->base + QUP_I2C_STATUS);
+
+	while (rem) {
+		bool filled = false;
+
+		dev->cnt = msgs->len - dev->pos;
+		dev->msg = msgs;
+
+		dev->wr_sz = dev->out_fifo_sz;
+		dev->err = 0;
+		dev->complete = &complete;
+
+		if (qup_i2c_poll_state(dev, QUP_I2C_MAST_GEN, false) != 0) {
+			ret = -EIO;
+			goto out_err;
+		}
+
+		qup_print_status(dev);
+		/* HW limits Read upto 256 bytes in 1 read without stop */
+		if (dev->msg->flags & I2C_M_RD) {
+			qup_set_read_mode(dev, dev->cnt);
+			if (dev->cnt > 256)
+				dev->cnt = 256;
+		} else {
+			ret = qup_set_wr_mode(dev, rem);
+			if (ret != 0)
+				goto out_err;
+			/* Don't fill block till we get interrupt */
+			if (dev->wr_sz == dev->out_blk_sz)
+				filled = true;
+		}
+
+		err = qup_update_state(dev, QUP_RUN_STATE);
+		if (err < 0) {
+			ret = err;
+			goto out_err;
+		}
+
+		qup_print_status(dev);
+		writel_relaxed(dev->clk_ctl, dev->base + QUP_I2C_CLK_CTL);
+		/* CLK_CTL register is not in the same 1K region as other QUP
+		 * registers. Ensure that clock control is written before
+		 * programming other QUP registers
+		 */
+		mb();
+
+		do {
+			int idx = 0;
+			uint32_t carry_over = 0;
+
+			/* Transition to PAUSE state only possible from RUN */
+			err = qup_update_state(dev, QUP_PAUSE_STATE);
+			if (err < 0) {
+				ret = err;
+				goto out_err;
+			}
+
+			qup_print_status(dev);
+			/* This operation is Write, check the next operation
+			 * and decide mode
+			 */
+			while (filled == false) {
+				if ((msgs->flags & I2C_M_RD))
+					qup_issue_read(dev, msgs, &idx,
+							carry_over);
+				else if (!(msgs->flags & I2C_M_RD))
+					qup_issue_write(dev, msgs, rem, &idx,
+							&carry_over);
+				if (idx >= (dev->wr_sz << 1))
+					filled = true;
+				/* Start new message */
+				if (filled == false) {
+					if (msgs->flags & I2C_M_RD)
+							filled = true;
+					else if (rem > 1) {
+						/* Only combine operations with
+						 * same address
+						 */
+						struct i2c_msg *next = msgs + 1;
+						if (next->addr != msgs->addr)
+							filled = true;
+						else {
+							rem--;
+							msgs++;
+							dev->msg = msgs;
+							dev->pos = 0;
+							dev->cnt = msgs->len;
+							if (msgs->len > 256)
+								dev->cnt = 256;
+						}
+					} else
+						filled = true;
+				}
+			}
+			err = qup_update_state(dev, QUP_RUN_STATE);
+			if (err < 0) {
+				ret = err;
+				goto out_err;
+			}
+			dev_dbg(dev->dev, "idx:%d, rem:%d, num:%d, mode:%d\n",
+				idx, rem, num, dev->mode);
+
+			qup_print_status(dev);
+			timeout = wait_for_completion_timeout(&complete,
+					msecs_to_jiffies(dev->out_fifo_sz));
+			if (!timeout) {
+				uint32_t istatus = readl_relaxed(dev->base +
+							QUP_I2C_STATUS);
+				uint32_t qstatus = readl_relaxed(dev->base +
+							QUP_ERROR_FLAGS);
+				uint32_t op_flgs = readl_relaxed(dev->base +
+							QUP_OPERATIONAL);
+
+				/*
+				 * Dont wait for 1 sec if i2c sees the bus
+				 * active and controller is not master.
+				 * A slave has pulled line low. Try to recover
+				 */
+				if (!(istatus & I2C_STATUS_BUS_ACTIVE) ||
+					(istatus & I2C_STATUS_BUS_MASTER)) {
+					timeout =
+					wait_for_completion_timeout(&complete,
+									HZ);
+					if (timeout)
+						goto timeout_err;
+				}
+				qup_i2c_recover_bus_busy(dev);
+				dev_err(dev->dev,
+					"Transaction timed out, SL-AD = 0x%x\n",
+					dev->msg->addr);
+
+				dev_err(dev->dev, "I2C Status: %x\n", istatus);
+				dev_err(dev->dev, "QUP Status: %x\n", qstatus);
+				dev_err(dev->dev, "OP Flags: %x\n", op_flgs);
+				writel_relaxed(1, dev->base + QUP_SW_RESET);
+				/* Make sure that the write has gone through
+				 * before returning from the function
+				 */
+				mb();
+				ret = -ETIMEDOUT;
+				goto out_err;
+			}
+timeout_err:
+			if (dev->err) {
+				if (dev->err > 0 &&
+					dev->err & QUP_I2C_NACK_FLAG) {
+					dev_err(dev->dev,
+					"I2C slave addr:0x%x not connected\n",
+					dev->msg->addr);
+					dev->err = ENOTCONN;
+				} else if (dev->err < 0) {
+					dev_err(dev->dev,
+					"QUP data xfer error %d\n", dev->err);
+					ret = dev->err;
+					goto out_err;
+				} else if (dev->err > 0) {
+					/*
+					 * ISR returns +ve error if error code
+					 * is I2C related, e.g. unexpected start
+					 * So you may call recover-bus-busy when
+					 * this error happens
+					 */
+					qup_i2c_recover_bus_busy(dev);
+				}
+				ret = -dev->err;
+				goto out_err;
+			}
+			if (dev->msg->flags & I2C_M_RD) {
+				int i;
+				uint32_t dval = 0;
+				for (i = 0; dev->pos < dev->msg->len; i++,
+						dev->pos++) {
+					uint32_t rd_status =
+						readl_relaxed(dev->base
+							+ QUP_OPERATIONAL);
+					if (i % 2 == 0) {
+						if ((rd_status &
+							QUP_IN_NOT_EMPTY) == 0)
+							break;
+						dval = readl_relaxed(dev->base +
+							QUP_IN_FIFO_BASE);
+						dev->msg->buf[dev->pos] =
+							dval & 0xFF;
+					} else
+						dev->msg->buf[dev->pos] =
+							((dval & 0xFF0000) >>
+							 16);
+				}
+				dev->cnt -= i;
+			} else
+				filled = false; /* refill output FIFO */
+			dev_dbg(dev->dev, "pos:%d, len:%d, cnt:%d\n",
+					dev->pos, msgs->len, dev->cnt);
+		} while (dev->cnt > 0);
+		if (dev->cnt == 0) {
+			if (msgs->len == dev->pos) {
+				rem--;
+				msgs++;
+				dev->pos = 0;
+			}
+			if (rem) {
+				err = qup_i2c_poll_clock_ready(dev);
+				if (err < 0) {
+					ret = err;
+					goto out_err;
+				}
+				err = qup_update_state(dev, QUP_RESET_STATE);
+				if (err < 0) {
+					ret = err;
+					goto out_err;
+				}
+			}
+		}
+		/* Wait for I2C bus to be idle */
+		ret = qup_i2c_poll_writeready(dev, rem);
+		if (ret) {
+			dev_err(dev->dev,
+				"Error waiting for write ready\n");
+			goto out_err;
+		}
+	}
+
+	ret = num;
+ out_err:
+	disable_irq(dev->err_irq);
+	if (dev->num_irqs == 3) {
+		disable_irq(dev->in_irq);
+		disable_irq(dev->out_irq);
+	}
+	dev->complete = NULL;
+	dev->msg = NULL;
+	dev->pos = 0;
+	dev->err = 0;
+	dev->cnt = 0;
+	dev->pwr_timer.expires = jiffies + 3*HZ;
+	add_timer(&dev->pwr_timer);
+	mutex_unlock(&dev->mlock);
+	return ret;
+}
+
+static u32
+qup_i2c_func(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
+}
+
+static const struct i2c_algorithm qup_i2c_algo = {
+	.master_xfer	= qup_i2c_xfer,
+	.functionality	= qup_i2c_func,
+};
+
+static int __devinit
+qup_i2c_probe(struct platform_device *pdev)
+{
+	struct qup_i2c_dev	*dev;
+	struct resource         *qup_mem, *gsbi_mem, *qup_io, *gsbi_io, *res;
+	struct resource		*in_irq, *out_irq, *err_irq;
+	struct clk         *clk, *pclk;
+	int ret = 0;
+	int i;
+	struct msm_i2c_platform_data *pdata;
+
+	gsbi_mem = NULL;
+	dev_dbg(&pdev->dev, "qup_i2c_probe\n");
+
+	if (pdev->dev.of_node) {
+		struct device_node *node = pdev->dev.of_node;
+		pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+		if (!pdata)
+			return -ENOMEM;
+		ret = of_property_read_u32(node, "qcom,i2c-bus-freq",
+					&pdata->clk_freq);
+		if (ret)
+			goto get_res_failed;
+		ret = of_property_read_u32(node, "cell-index", &pdev->id);
+		if (ret)
+			goto get_res_failed;
+		/* Optional property */
+		of_property_read_u32(node, "qcom,i2c-src-freq",
+					&pdata->src_clk_rate);
+	} else
+		pdata = pdev->dev.platform_data;
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "platform data not initialized\n");
+		return -ENOSYS;
+	}
+	qup_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						"qup_phys_addr");
+	if (!qup_mem) {
+		dev_err(&pdev->dev, "no qup mem resource?\n");
+		ret = -ENODEV;
+		goto get_res_failed;
+	}
+
+	/*
+	 * We only have 1 interrupt for new hardware targets and in_irq,
+	 * out_irq will be NULL for those platforms
+	 */
+	in_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+						"qup_in_intr");
+
+	out_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+						"qup_out_intr");
+
+	err_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+						"qup_err_intr");
+	if (!err_irq) {
+		dev_err(&pdev->dev, "no error irq resource?\n");
+		ret = -ENODEV;
+		goto get_res_failed;
+	}
+
+	qup_io = request_mem_region(qup_mem->start, resource_size(qup_mem),
+					pdev->name);
+	if (!qup_io) {
+		dev_err(&pdev->dev, "QUP region already claimed\n");
+		ret = -EBUSY;
+		goto get_res_failed;
+	}
+	if (!pdata->use_gsbi_shared_mode) {
+		gsbi_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+							"gsbi_qup_i2c_addr");
+		if (!gsbi_mem) {
+			dev_dbg(&pdev->dev, "Assume BLSP\n");
+			/*
+			 * BLSP core does not need protocol programming so this
+			 * resource is not expected
+			 */
+			goto blsp_core_init;
+		}
+		gsbi_io = request_mem_region(gsbi_mem->start,
+						resource_size(gsbi_mem),
+						pdev->name);
+		if (!gsbi_io) {
+			dev_err(&pdev->dev, "GSBI region already claimed\n");
+			ret = -EBUSY;
+			goto err_res_failed;
+		}
+	}
+
+blsp_core_init:
+	clk = clk_get(&pdev->dev, "core_clk");
+	if (IS_ERR(clk)) {
+		dev_err(&pdev->dev, "Could not get core_clk\n");
+		ret = PTR_ERR(clk);
+		goto err_clk_get_failed;
+	}
+
+	pclk = clk_get(&pdev->dev, "iface_clk");
+	if (IS_ERR(pclk)) {
+		dev_err(&pdev->dev, "Could not get iface_clk\n");
+		ret = PTR_ERR(pclk);
+		clk_put(clk);
+		goto err_clk_get_failed;
+	}
+
+	/* We support frequencies upto FAST Mode(400KHz) */
+	if (pdata->clk_freq <= 0 ||
+			pdata->clk_freq > 400000) {
+		dev_err(&pdev->dev, "clock frequency not supported\n");
+		ret = -EIO;
+		goto err_config_failed;
+	}
+
+	dev = kzalloc(sizeof(struct qup_i2c_dev), GFP_KERNEL);
+	if (!dev) {
+		ret = -ENOMEM;
+		goto err_alloc_dev_failed;
+	}
+
+	dev->dev = &pdev->dev;
+	if (in_irq)
+		dev->in_irq = in_irq->start;
+	if (out_irq)
+		dev->out_irq = out_irq->start;
+	dev->err_irq = err_irq->start;
+	if (in_irq && out_irq)
+		dev->num_irqs = 3;
+	else
+		dev->num_irqs = 1;
+	dev->clk = clk;
+	dev->pclk = pclk;
+	dev->base = ioremap(qup_mem->start, resource_size(qup_mem));
+	if (!dev->base) {
+		ret = -ENOMEM;
+		goto err_ioremap_failed;
+	}
+
+	/* Configure GSBI block to use I2C functionality */
+	if (gsbi_mem) {
+		dev->gsbi = ioremap(gsbi_mem->start, resource_size(gsbi_mem));
+		if (!dev->gsbi) {
+			ret = -ENOMEM;
+			goto err_gsbi_failed;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) {
+		res = platform_get_resource_byname(pdev, IORESOURCE_IO,
+						   i2c_rsrcs[i]);
+		dev->i2c_gpios[i] = res ? res->start : -1;
+	}
+
+	ret = qup_i2c_request_gpios(dev);
+	if (ret)
+		goto err_request_gpio_failed;
+
+	platform_set_drvdata(pdev, dev);
+
+	dev->one_bit_t = (USEC_PER_SEC/pdata->clk_freq) + 1;
+	dev->pdata = pdata;
+	dev->clk_ctl = 0;
+	dev->pos = 0;
+
+	/*
+	 * If bootloaders leave a pending interrupt on certain GSBI's,
+	 * then we reset the core before registering for interrupts.
+	 */
+
+	if (dev->pdata->src_clk_rate > 0)
+		clk_set_rate(dev->clk, dev->pdata->src_clk_rate);
+	else
+		dev->pdata->src_clk_rate = 19200000;
+
+	clk_prepare_enable(dev->clk);
+	clk_prepare_enable(dev->pclk);
+	writel_relaxed(1, dev->base + QUP_SW_RESET);
+	if (qup_i2c_poll_state(dev, 0, true) != 0)
+		goto err_reset_failed;
+	clk_disable_unprepare(dev->clk);
+	clk_disable_unprepare(dev->pclk);
+
+	/*
+	 * We use num_irqs to also indicate if we got 3 interrupts or just 1.
+	 * If we have just 1, we use err_irq as the general purpose irq
+	 * and handle the changes in ISR accordingly
+	 * Per Hardware guidelines, if we have 3 interrupts, they are always
+	 * edge triggering, and if we have 1, it's always level-triggering
+	 */
+	if (dev->num_irqs == 3) {
+		ret = request_irq(dev->in_irq, qup_i2c_interrupt,
+				IRQF_TRIGGER_RISING, "qup_in_intr", dev);
+		if (ret) {
+			dev_err(&pdev->dev, "request_in_irq failed\n");
+			goto err_request_irq_failed;
+		}
+		/*
+		 * We assume out_irq exists if in_irq does since platform
+		 * configuration either has 3 interrupts assigned to QUP or 1
+		 */
+		ret = request_irq(dev->out_irq, qup_i2c_interrupt,
+				IRQF_TRIGGER_RISING, "qup_out_intr", dev);
+		if (ret) {
+			dev_err(&pdev->dev, "request_out_irq failed\n");
+			free_irq(dev->in_irq, dev);
+			goto err_request_irq_failed;
+		}
+		ret = request_irq(dev->err_irq, qup_i2c_interrupt,
+				IRQF_TRIGGER_RISING, "qup_err_intr", dev);
+		if (ret) {
+			dev_err(&pdev->dev, "request_err_irq failed\n");
+			free_irq(dev->out_irq, dev);
+			free_irq(dev->in_irq, dev);
+			goto err_request_irq_failed;
+		}
+	} else {
+		ret = request_irq(dev->err_irq, qup_i2c_interrupt,
+				IRQF_TRIGGER_HIGH, "qup_err_intr", dev);
+		if (ret) {
+			dev_err(&pdev->dev, "request_err_irq failed\n");
+			goto err_request_irq_failed;
+		}
+	}
+	disable_irq(dev->err_irq);
+	if (dev->num_irqs == 3) {
+		disable_irq(dev->in_irq);
+		disable_irq(dev->out_irq);
+	}
+	i2c_set_adapdata(&dev->adapter, dev);
+	dev->adapter.algo = &qup_i2c_algo;
+	strlcpy(dev->adapter.name,
+		"QUP I2C adapter",
+		sizeof(dev->adapter.name));
+	dev->adapter.nr = pdev->id;
+	if (pdata->msm_i2c_config_gpio)
+		pdata->msm_i2c_config_gpio(dev->adapter.nr, 1);
+
+	dev->suspended = 0;
+	mutex_init(&dev->mlock);
+	dev->clk_state = 0;
+	clk_prepare(dev->clk);
+	clk_prepare(dev->pclk);
+	setup_timer(&dev->pwr_timer, qup_i2c_pwr_timer, (unsigned long) dev);
+
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+
+	ret = i2c_add_numbered_adapter(&dev->adapter);
+	if (ret) {
+		dev_err(&pdev->dev, "i2c_add_adapter failed\n");
+		if (dev->num_irqs == 3) {
+			free_irq(dev->out_irq, dev);
+			free_irq(dev->in_irq, dev);
+		}
+		free_irq(dev->err_irq, dev);
+	} else {
+		if (dev->dev->of_node)
+			of_i2c_register_devices(&dev->adapter);
+		return 0;
+	}
+
+
+err_request_irq_failed:
+	qup_i2c_free_gpios(dev);
+	if (dev->gsbi)
+		iounmap(dev->gsbi);
+err_reset_failed:
+	clk_disable_unprepare(dev->clk);
+	clk_disable_unprepare(dev->pclk);
+err_request_gpio_failed:
+err_gsbi_failed:
+	iounmap(dev->base);
+err_ioremap_failed:
+	kfree(dev);
+err_alloc_dev_failed:
+err_config_failed:
+	clk_put(clk);
+	clk_put(pclk);
+err_clk_get_failed:
+	if (gsbi_mem)
+		release_mem_region(gsbi_mem->start, resource_size(gsbi_mem));
+err_res_failed:
+	release_mem_region(qup_mem->start, resource_size(qup_mem));
+get_res_failed:
+	if (pdev->dev.of_node)
+		kfree(pdata);
+	return ret;
+}
+
+static int __devexit
+qup_i2c_remove(struct platform_device *pdev)
+{
+	struct qup_i2c_dev	*dev = platform_get_drvdata(pdev);
+	struct resource		*qup_mem, *gsbi_mem;
+
+	/* Grab mutex to ensure ongoing transaction is over */
+	mutex_lock(&dev->mlock);
+	dev->suspended = 1;
+	mutex_unlock(&dev->mlock);
+	mutex_destroy(&dev->mlock);
+	del_timer_sync(&dev->pwr_timer);
+	if (dev->clk_state != 0)
+		qup_i2c_pwr_mgmt(dev, 0);
+	platform_set_drvdata(pdev, NULL);
+	if (dev->num_irqs == 3) {
+		free_irq(dev->out_irq, dev);
+		free_irq(dev->in_irq, dev);
+	}
+	free_irq(dev->err_irq, dev);
+	i2c_del_adapter(&dev->adapter);
+	clk_unprepare(dev->clk);
+	clk_unprepare(dev->pclk);
+	clk_put(dev->clk);
+	clk_put(dev->pclk);
+	qup_i2c_free_gpios(dev);
+	if (dev->gsbi)
+		iounmap(dev->gsbi);
+	iounmap(dev->base);
+
+	pm_runtime_disable(&pdev->dev);
+
+	if (!(dev->pdata->use_gsbi_shared_mode)) {
+		gsbi_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+							"gsbi_qup_i2c_addr");
+		release_mem_region(gsbi_mem->start, resource_size(gsbi_mem));
+	}
+	qup_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						"qup_phys_addr");
+	release_mem_region(qup_mem->start, resource_size(qup_mem));
+	if (dev->dev->of_node)
+		kfree(dev->pdata);
+	kfree(dev);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int qup_i2c_suspend(struct device *device)
+{
+	struct platform_device *pdev = to_platform_device(device);
+	struct qup_i2c_dev *dev = platform_get_drvdata(pdev);
+
+	/* Grab mutex to ensure ongoing transaction is over */
+	mutex_lock(&dev->mlock);
+	dev->suspended = 1;
+	mutex_unlock(&dev->mlock);
+	del_timer_sync(&dev->pwr_timer);
+	if (dev->clk_state != 0)
+		qup_i2c_pwr_mgmt(dev, 0);
+	clk_unprepare(dev->clk);
+	clk_unprepare(dev->pclk);
+	qup_i2c_free_gpios(dev);
+	return 0;
+}
+
+static int qup_i2c_resume(struct device *device)
+{
+	struct platform_device *pdev = to_platform_device(device);
+	struct qup_i2c_dev *dev = platform_get_drvdata(pdev);
+	BUG_ON(qup_i2c_request_gpios(dev) != 0);
+	clk_prepare(dev->clk);
+	clk_prepare(dev->pclk);
+	dev->suspended = 0;
+	return 0;
+}
+#endif /* CONFIG_PM */
+
+#ifdef CONFIG_PM_RUNTIME
+static int i2c_qup_runtime_idle(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: idle...\n");
+	return 0;
+}
+
+static int i2c_qup_runtime_suspend(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: suspending...\n");
+	return 0;
+}
+
+static int i2c_qup_runtime_resume(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: resuming...\n");
+	return 0;
+}
+#endif
+
+static const struct dev_pm_ops i2c_qup_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(
+		qup_i2c_suspend,
+		qup_i2c_resume
+	)
+	SET_RUNTIME_PM_OPS(
+		i2c_qup_runtime_suspend,
+		i2c_qup_runtime_resume,
+		i2c_qup_runtime_idle
+	)
+};
+
+static struct of_device_id i2c_qup_dt_match[] = {
+	{
+		.compatible = "qcom,i2c-qup",
+	},
+	{}
+};
+
+static struct platform_driver qup_i2c_driver = {
+	.probe		= qup_i2c_probe,
+	.remove		= __devexit_p(qup_i2c_remove),
+	.driver		= {
+		.name	= "qup_i2c",
+		.owner	= THIS_MODULE,
+		.pm = &i2c_qup_dev_pm_ops,
+		.of_match_table = i2c_qup_dt_match,
+	},
+};
+
+/* QUP may be needed to bring up other drivers */
+static int __init
+qup_i2c_init_driver(void)
+{
+	return platform_driver_register(&qup_i2c_driver);
+}
+arch_initcall(qup_i2c_init_driver);
+
+static void __exit qup_i2c_exit_driver(void)
+{
+	platform_driver_unregister(&qup_i2c_driver);
+}
+module_exit(qup_i2c_exit_driver);
+
diff --git a/drivers/i2c/busses/i2c-ssbi.c b/drivers/i2c/busses/i2c-ssbi.c
new file mode 100644
index 0000000..c803402
--- /dev/null
+++ b/drivers/i2c/busses/i2c-ssbi.c
@@ -0,0 +1,517 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+/*
+ * SSBI driver for Qualcomm MSM platforms
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/remote_spinlock.h>
+#include <mach/board.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+/* SSBI 2.0 controller registers */
+#define SSBI2_CMD			0x0008
+#define SSBI2_RD			0x0010
+#define SSBI2_STATUS			0x0014
+#define SSBI2_MODE2			0x001C
+
+/* SSBI_CMD fields */
+#define SSBI_CMD_RDWRN			(0x01 << 24)
+#define SSBI_CMD_REG_ADDR_SHFT		(0x10)
+#define SSBI_CMD_REG_ADDR_MASK		(0xFF << SSBI_CMD_REG_ADDR_SHFT)
+#define SSBI_CMD_REG_DATA_SHFT		(0x00)
+#define SSBI_CMD_REG_DATA_MASK		(0xFF << SSBI_CMD_REG_DATA_SHFT)
+
+/* SSBI_STATUS fields */
+#define SSBI_STATUS_DATA_IN		0x10
+#define SSBI_STATUS_RD_CLOBBERED	0x08
+#define SSBI_STATUS_RD_READY		0x04
+#define SSBI_STATUS_READY		0x02
+#define SSBI_STATUS_MCHN_BUSY		0x01
+
+/* SSBI_RD fields */
+#define SSBI_RD_RDWRN			0x01000000
+#define SSBI_RD_REG_ADDR_SHFT		0x10
+#define SSBI_RD_REG_ADDR_MASK		(0xFF << SSBI_RD_REG_ADDR_SHFT)
+#define SSBI_RD_REG_DATA_SHFT		(0x00)
+#define SSBI_RD_REG_DATA_MASK		(0xFF << SSBI_RD_REG_DATA_SHFT)
+
+/* SSBI_MODE2 fields */
+#define SSBI_MODE2_REG_ADDR_15_8_SHFT	0x04
+#define SSBI_MODE2_REG_ADDR_15_8_MASK	(0x7F << SSBI_MODE2_REG_ADDR_15_8_SHFT)
+#define SSBI_MODE2_ADDR_WIDTH_SHFT	0x01
+#define SSBI_MODE2_ADDR_WIDTH_MASK	(0x07 << SSBI_MODE2_ADDR_WIDTH_SHFT)
+#define SSBI_MODE2_SSBI2_MODE		0x00000001
+
+#define SSBI_MODE2_REG_ADDR_15_8(MD, AD) \
+	(((MD) & 0x0F) | ((((AD) >> 8) << SSBI_MODE2_REG_ADDR_15_8_SHFT) & \
+	SSBI_MODE2_REG_ADDR_15_8_MASK))
+
+#define SSBI_MODE2_ADDR_WIDTH(N) \
+	((((N) - 8) << SSBI_MODE2_ADDR_WIDTH_SHFT) & SSBI_MODE2_ADDR_WIDTH_MASK)
+
+#define SSBI_TIMEOUT_US			100
+
+#define SSBI_CMD_READ(AD) \
+	(SSBI_CMD_RDWRN | (((AD) & 0xFF) << SSBI_CMD_REG_ADDR_SHFT))
+
+#define SSBI_CMD_WRITE(AD, DT) \
+	((((AD) & 0xFF) << SSBI_CMD_REG_ADDR_SHFT) | \
+	 (((DT) & 0xFF) << SSBI_CMD_REG_DATA_SHFT))
+
+/* SSBI PMIC Arbiter command registers */
+#define SSBI_PA_CMD			0x0000
+#define SSBI_PA_RD_STATUS		0x0004
+
+/* SSBI_PA_CMD fields */
+#define SSBI_PA_CMD_RDWRN		(0x01 << 24)
+#define SSBI_PA_CMD_REG_ADDR_14_8_SHFT	(0x10)
+#define SSBI_PA_CMD_REG_ADDR_14_8_MASK	(0x7F << SSBI_PA_CMD_REG_ADDR_14_8_SHFT)
+#define SSBI_PA_CMD_REG_ADDR_7_0_SHFT	(0x08)
+#define SSBI_PA_CMD_REG_ADDR_7_0_MASK	(0xFF << SSBI_PA_CMD_REG_ADDR_7_0_SHFT)
+#define SSBI_PA_CMD_REG_DATA_SHFT	(0x00)
+#define SSBI_PA_CMD_REG_DATA_MASK	(0xFF << SSBI_PA_CMD_REG_DATA_SHFT)
+
+#define SSBI_PA_CMD_REG_DATA(DT) \
+	(((DT) << SSBI_PA_CMD_REG_DATA_SHFT) & SSBI_PA_CMD_REG_DATA_MASK)
+
+#define SSBI_PA_CMD_REG_ADDR(AD) \
+	(((AD) << SSBI_PA_CMD_REG_ADDR_7_0_SHFT) & \
+	(SSBI_PA_CMD_REG_ADDR_14_8_MASK|SSBI_PA_CMD_REG_ADDR_7_0_MASK))
+
+/* SSBI_PA_RD_STATUS fields */
+#define SSBI_PA_RD_STATUS_TRANS_DONE	(0x01 << 27)
+#define SSBI_PA_RD_STATUS_TRANS_DENIED	(0x01 << 26)
+#define SSBI_PA_RD_STATUS_REG_DATA_SHFT	(0x00)
+#define SSBI_PA_RD_STATUS_REG_DATA_MASK	(0xFF << SSBI_PA_CMD_REG_DATA_SHFT)
+#define SSBI_PA_RD_STATUS_TRANS_COMPLETE \
+	(SSBI_PA_RD_STATUS_TRANS_DONE|SSBI_PA_RD_STATUS_TRANS_DENIED)
+
+/* SSBI_FSM Read and Write commands for the FSM9xxx SSBI implementation */
+#define SSBI_FSM_CMD_REG_ADDR_SHFT	(0x08)
+
+#define SSBI_FSM_CMD_READ(AD) \
+	(SSBI_CMD_RDWRN | (((AD) & 0xFFFF) << SSBI_FSM_CMD_REG_ADDR_SHFT))
+
+#define SSBI_FSM_CMD_WRITE(AD, DT) \
+	((((AD) & 0xFFFF) << SSBI_FSM_CMD_REG_ADDR_SHFT) | \
+	 (((DT) & 0xFF) << SSBI_CMD_REG_DATA_SHFT))
+
+#define SSBI_MSM_NAME			"i2c_ssbi"
+
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("2.0");
+MODULE_ALIAS("platform:i2c_ssbi");
+
+struct i2c_ssbi_dev {
+	void __iomem		*base;
+	struct device           *dev;
+	struct i2c_adapter	 adapter;
+	unsigned long		 mem_phys_addr;
+	size_t			 mem_size;
+	bool			 use_rlock;
+	remote_spinlock_t	 rspin_lock;
+	enum msm_ssbi_controller_type controller_type;
+	int (*read)(struct i2c_ssbi_dev *, struct i2c_msg *);
+	int (*write)(struct i2c_ssbi_dev *, struct i2c_msg *);
+};
+
+static inline u32 ssbi_readl(struct i2c_ssbi_dev *ssbi, u32 reg)
+{
+	return readl_relaxed(ssbi->base + reg);
+}
+
+static inline void ssbi_writel(struct i2c_ssbi_dev *ssbi, u32 reg, u32 val)
+{
+	writel_relaxed(val, ssbi->base + reg);
+}
+
+static inline int
+i2c_ssbi_poll_for_device_ready(struct i2c_ssbi_dev *ssbi)
+{
+	u32 timeout = SSBI_TIMEOUT_US;
+
+	while (!(ssbi_readl(ssbi, SSBI2_STATUS) & SSBI_STATUS_READY)) {
+		if (--timeout == 0) {
+			dev_err(ssbi->dev, "%s: timeout, status %x\n", __func__,
+				ssbi_readl(ssbi, SSBI2_STATUS));
+			return -ETIMEDOUT;
+		}
+		udelay(1);
+	}
+
+	return 0;
+}
+
+static inline int
+i2c_ssbi_poll_for_read_completed(struct i2c_ssbi_dev *ssbi)
+{
+	u32 timeout = SSBI_TIMEOUT_US;
+
+	while (!(ssbi_readl(ssbi, SSBI2_STATUS) & SSBI_STATUS_RD_READY)) {
+		if (--timeout == 0) {
+			dev_err(ssbi->dev, "%s: timeout, status %x\n", __func__,
+				ssbi_readl(ssbi, SSBI2_STATUS));
+			return -ETIMEDOUT;
+		}
+		udelay(1);
+	}
+
+	return 0;
+}
+
+static inline int
+i2c_ssbi_poll_for_transfer_completed(struct i2c_ssbi_dev *ssbi)
+{
+	u32 timeout = SSBI_TIMEOUT_US;
+
+	while ((ssbi_readl(ssbi, SSBI2_STATUS) & SSBI_STATUS_MCHN_BUSY)) {
+		if (--timeout == 0) {
+			dev_err(ssbi->dev, "%s: timeout, status %x\n", __func__,
+				ssbi_readl(ssbi, SSBI2_STATUS));
+			return -ETIMEDOUT;
+		}
+		udelay(1);
+	}
+
+	return 0;
+}
+
+static int
+i2c_ssbi_read_bytes(struct i2c_ssbi_dev *ssbi, struct i2c_msg *msg)
+{
+	int ret = 0;
+	u8 *buf = msg->buf;
+	u16 len = msg->len;
+	u16 addr = msg->addr;
+	u32 read_cmd;
+
+	if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) {
+		u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2);
+		ssbi_writel(ssbi, SSBI2_MODE2,
+				SSBI_MODE2_REG_ADDR_15_8(mode2, addr));
+	}
+
+	if (ssbi->controller_type == FSM_SBI_CTRL_SSBI)
+		read_cmd = SSBI_FSM_CMD_READ(addr);
+	else
+		read_cmd = SSBI_CMD_READ(addr);
+
+	while (len) {
+		ret = i2c_ssbi_poll_for_device_ready(ssbi);
+		if (ret)
+			goto read_failed;
+
+		ssbi_writel(ssbi, SSBI2_CMD, read_cmd);
+
+		ret = i2c_ssbi_poll_for_read_completed(ssbi);
+		if (ret)
+			goto read_failed;
+
+		*buf++ = ssbi_readl(ssbi, SSBI2_RD) & SSBI_RD_REG_DATA_MASK;
+		len--;
+	}
+
+read_failed:
+	return ret;
+}
+
+static int
+i2c_ssbi_write_bytes(struct i2c_ssbi_dev *ssbi, struct i2c_msg *msg)
+{
+	int ret = 0;
+	u8 *buf = msg->buf;
+	u16 len = msg->len;
+	u16 addr = msg->addr;
+
+	if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) {
+		u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2);
+		ssbi_writel(ssbi, SSBI2_MODE2,
+				SSBI_MODE2_REG_ADDR_15_8(mode2, addr));
+	}
+
+	while (len) {
+		ret = i2c_ssbi_poll_for_device_ready(ssbi);
+		if (ret)
+			goto write_failed;
+
+		if (ssbi->controller_type == FSM_SBI_CTRL_SSBI)
+			ssbi_writel(ssbi, SSBI2_CMD,
+				SSBI_FSM_CMD_WRITE(addr, *buf++));
+		else
+			ssbi_writel(ssbi, SSBI2_CMD,
+				SSBI_CMD_WRITE(addr, *buf++));
+
+		ret = i2c_ssbi_poll_for_transfer_completed(ssbi);
+		if (ret)
+			goto write_failed;
+
+		len--;
+	}
+
+write_failed:
+	return ret;
+}
+
+static inline int
+i2c_ssbi_pa_transfer(struct i2c_ssbi_dev *ssbi, u32 cmd, u8 *data)
+{
+	u32 rd_status;
+	u32 timeout = SSBI_TIMEOUT_US;
+
+	ssbi_writel(ssbi, SSBI_PA_CMD, cmd);
+	rd_status = ssbi_readl(ssbi, SSBI_PA_RD_STATUS);
+
+	while ((rd_status & (SSBI_PA_RD_STATUS_TRANS_COMPLETE)) == 0) {
+
+		if (--timeout == 0) {
+			dev_err(ssbi->dev, "%s: timeout, status %x\n",
+					__func__, rd_status);
+			return -ETIMEDOUT;
+		}
+		udelay(1);
+		rd_status = ssbi_readl(ssbi, SSBI_PA_RD_STATUS);
+	}
+
+	if (rd_status & SSBI_PA_RD_STATUS_TRANS_DENIED) {
+		dev_err(ssbi->dev, "%s: transaction denied, status %x\n",
+				__func__, rd_status);
+		return -EPERM;
+	}
+
+	if (data)
+		*data = (rd_status & SSBI_PA_RD_STATUS_REG_DATA_MASK) >>
+					SSBI_PA_CMD_REG_DATA_SHFT;
+	return 0;
+}
+
+static int
+i2c_ssbi_pa_read_bytes(struct i2c_ssbi_dev *ssbi, struct i2c_msg *msg)
+{
+	int ret = 0;
+	u8  data;
+	u8 *buf = msg->buf;
+	u16 len = msg->len;
+	u32 read_cmd = (SSBI_PA_CMD_RDWRN | SSBI_PA_CMD_REG_ADDR(msg->addr));
+
+	while (len) {
+
+		ret = i2c_ssbi_pa_transfer(ssbi, read_cmd, &data);
+		if (ret)
+			goto read_failed;
+
+		*buf++ = data;
+		len--;
+	}
+
+read_failed:
+	return ret;
+}
+
+static int
+i2c_ssbi_pa_write_bytes(struct i2c_ssbi_dev *ssbi, struct i2c_msg *msg)
+{
+	int ret = 0;
+	u8 *buf = msg->buf;
+	u16 len = msg->len;
+	u32 addr = SSBI_PA_CMD_REG_ADDR(msg->addr);
+
+	while (len) {
+
+		u32 write_cmd = addr | (*buf++ & SSBI_PA_CMD_REG_DATA_MASK);
+
+		ret = i2c_ssbi_pa_transfer(ssbi, write_cmd, NULL);
+		if (ret)
+			goto write_failed;
+		len--;
+	}
+
+write_failed:
+	return ret;
+}
+
+static int
+i2c_ssbi_transfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+	int ret = 0;
+	int rem = num;
+	unsigned long flags = 0;
+	struct i2c_ssbi_dev *ssbi = i2c_get_adapdata(adap);
+
+	if (ssbi->use_rlock)
+		remote_spin_lock_irqsave(&ssbi->rspin_lock, flags);
+
+	while (rem) {
+		if (msgs->flags & I2C_M_RD) {
+			ret = ssbi->read(ssbi, msgs);
+			if (ret)
+				goto transfer_failed;
+		} else {
+			ret = ssbi->write(ssbi, msgs);
+			if (ret)
+				goto transfer_failed;
+		}
+
+		msgs++;
+		rem--;
+	}
+
+	if (ssbi->use_rlock)
+		remote_spin_unlock_irqrestore(&ssbi->rspin_lock, flags);
+
+	return num;
+
+transfer_failed:
+	if (ssbi->use_rlock)
+		remote_spin_unlock_irqrestore(&ssbi->rspin_lock, flags);
+	return ret;
+}
+
+static u32 i2c_ssbi_i2c_func(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C;
+}
+
+static const struct i2c_algorithm msm_i2c_algo = {
+	.master_xfer	= i2c_ssbi_transfer,
+	.functionality	= i2c_ssbi_i2c_func,
+};
+
+static int __init i2c_ssbi_probe(struct platform_device *pdev)
+{
+	int			 ret = 0;
+	struct resource		*ssbi_res;
+	struct i2c_ssbi_dev	*ssbi;
+	const struct msm_i2c_ssbi_platform_data *pdata;
+
+	pdata = pdev->dev.platform_data;
+	if (!pdata) {
+		ret = -ENXIO;
+		dev_err(&pdev->dev, "platform data not initialized\n");
+		goto err_probe_exit;
+	}
+
+	ssbi = kzalloc(sizeof(struct i2c_ssbi_dev), GFP_KERNEL);
+	if (!ssbi) {
+		ret = -ENOMEM;
+		dev_err(&pdev->dev, "allocation failed\n");
+		goto err_probe_exit;
+	}
+
+	ssbi_res = platform_get_resource_byname(pdev,
+						IORESOURCE_MEM, "ssbi_base");
+	if (!ssbi_res) {
+		ret = -ENXIO;
+		dev_err(&pdev->dev, "get_resource_byname failed\n");
+		goto err_probe_res;
+	}
+
+	ssbi->mem_phys_addr = ssbi_res->start;
+	ssbi->mem_size = resource_size(ssbi_res);
+	if (!request_mem_region(ssbi->mem_phys_addr, ssbi->mem_size,
+				SSBI_MSM_NAME)) {
+		ret = -ENXIO;
+		dev_err(&pdev->dev, "request_mem_region failed\n");
+		goto err_probe_reqmem;
+	}
+
+	ssbi->base = ioremap(ssbi->mem_phys_addr, ssbi->mem_size);
+	if (!ssbi->base) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		goto err_probe_ioremap;
+	}
+
+	ssbi->dev = &pdev->dev;
+	platform_set_drvdata(pdev, ssbi);
+
+	ssbi->controller_type = pdata->controller_type;
+	if (ssbi->controller_type == MSM_SBI_CTRL_PMIC_ARBITER) {
+		ssbi->read = i2c_ssbi_pa_read_bytes;
+		ssbi->write = i2c_ssbi_pa_write_bytes;
+	} else {
+		ssbi->read = i2c_ssbi_read_bytes;
+		ssbi->write = i2c_ssbi_write_bytes;
+	}
+
+	i2c_set_adapdata(&ssbi->adapter, ssbi);
+	ssbi->adapter.algo = &msm_i2c_algo;
+	strlcpy(ssbi->adapter.name,
+		"MSM SSBI adapter",
+		sizeof(ssbi->adapter.name));
+
+	if (pdata->rsl_id) {
+		ret = remote_spin_lock_init(&ssbi->rspin_lock, pdata->rsl_id);
+		if (ret) {
+			dev_err(&pdev->dev, "remote spinlock init failed\n");
+			goto err_remote_spinlock_init_failed;
+		}
+		ssbi->use_rlock = 1;
+	}
+
+	ssbi->adapter.nr = pdev->id;
+	ret = i2c_add_numbered_adapter(&ssbi->adapter);
+	if (ret) {
+		dev_err(&pdev->dev, "i2c_add_numbered_adapter failed\n");
+		goto err_add_adapter_failed;
+	}
+	return 0;
+
+err_add_adapter_failed:
+err_remote_spinlock_init_failed:
+	iounmap(ssbi->base);
+	platform_set_drvdata(pdev, NULL);
+err_probe_ioremap:
+	release_mem_region(ssbi->mem_phys_addr, ssbi->mem_size);
+err_probe_reqmem:
+err_probe_res:
+	kfree(ssbi);
+err_probe_exit:
+	return ret;
+}
+
+static int __devexit i2c_ssbi_remove(struct platform_device *pdev)
+{
+	struct i2c_ssbi_dev *ssbi = platform_get_drvdata(pdev);
+
+	platform_set_drvdata(pdev, NULL);
+	i2c_del_adapter(&ssbi->adapter);
+	iounmap(ssbi->base);
+	release_mem_region(ssbi->mem_phys_addr, ssbi->mem_size);
+	kfree(ssbi);
+	return 0;
+}
+
+static struct platform_driver i2c_ssbi_driver = {
+	.driver		= {
+		.name	= "i2c_ssbi",
+		.owner	= THIS_MODULE,
+	},
+	.remove		= __exit_p(i2c_ssbi_remove),
+};
+
+static int __init i2c_ssbi_init(void)
+{
+	return platform_driver_probe(&i2c_ssbi_driver, i2c_ssbi_probe);
+}
+arch_initcall(i2c_ssbi_init);
+
+static void __exit i2c_ssbi_exit(void)
+{
+	platform_driver_unregister(&i2c_ssbi_driver);
+}
+module_exit(i2c_ssbi_exit);
diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c
index 3a78489..fda9e0a 100644
--- a/drivers/infiniband/hw/mlx4/qp.c
+++ b/drivers/infiniband/hw/mlx4/qp.c
@@ -1363,7 +1363,7 @@
 	int is_eth;
 	int is_vlan = 0;
 	int is_grh;
-	u16 vlan;
+	u16 vlan = 0;
 
 	send_size = 0;
 	for (i = 0; i < wr->num_sge; ++i)
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index a937438..9ed3d53 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -323,6 +323,7 @@
 		goto err_put_evdev;
 	}
 
+	client->clkid = CLOCK_MONOTONIC;
 	client->bufsize = bufsize;
 	spin_lock_init(&client->buffer_lock);
 	snprintf(client->name, sizeof(client->name), "%s-%d",
diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig
index 56eb471..aaee448 100644
--- a/drivers/input/joystick/Kconfig
+++ b/drivers/input/joystick/Kconfig
@@ -329,4 +329,15 @@
 	  To compile this as a module choose M here: the module will be called
 	  maplecontrol.
 
+config TOUCHDISC_VTD518_SHINETSU
+	tristate "ShinEtsu VTD518 TouchDisc"
+	depends on I2C
+	default n
+	help
+	  Say Y here if you have the ShinEtsu VTD518 Touchdisc connected. It
+	  provides the detection of absolute and relative motions and dpad
+	  like buttons.
+
+	  To compile this as a module choose M here: the module will be called
+	  tdisc_vtd518_shinetsu.
 endif
diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile
index 92dc0de..7009c38 100644
--- a/drivers/input/joystick/Makefile
+++ b/drivers/input/joystick/Makefile
@@ -32,4 +32,4 @@
 obj-$(CONFIG_JOYSTICK_XPAD)		+= xpad.o
 obj-$(CONFIG_JOYSTICK_ZHENHUA)		+= zhenhua.o
 obj-$(CONFIG_JOYSTICK_WALKERA0701)	+= walkera0701.o
-
+obj-$(CONFIG_TOUCHDISC_VTD518_SHINETSU) += tdisc_vtd518_shinetsu.o
\ No newline at end of file
diff --git a/drivers/input/joystick/tdisc_vtd518_shinetsu.c b/drivers/input/joystick/tdisc_vtd518_shinetsu.c
new file mode 100644
index 0000000..efbe974
--- /dev/null
+++ b/drivers/input/joystick/tdisc_vtd518_shinetsu.c
@@ -0,0 +1,528 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/workqueue.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/input/tdisc_shinetsu.h>
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+#include <linux/earlysuspend.h>
+/* Early-suspend level */
+#define TDISC_SUSPEND_LEVEL 1
+#endif
+
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.1");
+MODULE_DESCRIPTION("Shinetsu Touchdisc driver");
+MODULE_ALIAS("platform:tdisc-shinetsu");
+
+#define TDSIC_BLK_READ_CMD		0x00
+#define TDISC_READ_DELAY		msecs_to_jiffies(25)
+#define X_MAX				(32)
+#define X_MIN				(-32)
+#define Y_MAX				(32)
+#define Y_MIN				(-32)
+#define PRESSURE_MAX			(32)
+#define PRESSURE_MIN			(0)
+#define TDISC_USER_ACTIVE_MASK		0x40
+#define TDISC_NORTH_SWITCH_MASK		0x20
+#define TDISC_SOUTH_SWITCH_MASK		0x10
+#define TDISC_EAST_SWITCH_MASK		0x08
+#define TDISC_WEST_SWITCH_MASK		0x04
+#define TDISC_CENTER_SWITCH		0x01
+#define TDISC_BUTTON_PRESS_MASK		0x3F
+
+#define DRIVER_NAME			"tdisc-shinetsu"
+#define DEVICE_NAME			"vtd518"
+#define TDISC_NAME			"tdisc_shinetsu"
+#define TDISC_INT			"tdisc_interrupt"
+
+struct tdisc_data {
+	struct input_dev  *tdisc_device;
+	struct i2c_client *clientp;
+	struct tdisc_platform_data *pdata;
+	struct delayed_work tdisc_work;
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+	struct early_suspend	tdisc_early_suspend;
+#endif
+};
+
+static void process_tdisc_data(struct tdisc_data *dd, u8 *data)
+{
+	int i;
+	static bool button_press;
+	s8 x, y;
+
+	/* Check if the user is actively navigating */
+	if (!(data[7] & TDISC_USER_ACTIVE_MASK)) {
+		pr_debug(" TDISC ! No Data to report ! False positive \n");
+		return;
+	}
+
+	for (i = 0; i < 8 ; i++)
+		pr_debug(" Data[%d] = %x\n", i, data[i]);
+
+	/* Check if there is a button press */
+	if (dd->pdata->tdisc_report_keys)
+		if (data[7] & TDISC_BUTTON_PRESS_MASK || button_press == true) {
+			input_report_key(dd->tdisc_device, KEY_UP,
+				(data[7] & TDISC_NORTH_SWITCH_MASK));
+
+			input_report_key(dd->tdisc_device, KEY_DOWN,
+				(data[7] & TDISC_SOUTH_SWITCH_MASK));
+
+			input_report_key(dd->tdisc_device, KEY_RIGHT,
+				 (data[7] & TDISC_EAST_SWITCH_MASK));
+
+			input_report_key(dd->tdisc_device, KEY_LEFT,
+				 (data[7] & TDISC_WEST_SWITCH_MASK));
+
+			input_report_key(dd->tdisc_device, KEY_ENTER,
+				 (data[7] & TDISC_CENTER_SWITCH));
+
+			if (data[7] & TDISC_BUTTON_PRESS_MASK)
+				button_press = true;
+			else
+				button_press = false;
+		}
+
+	if (dd->pdata->tdisc_report_relative) {
+		/* Report relative motion values */
+		x = (s8) data[0];
+		y = (s8) data[1];
+
+		if (dd->pdata->tdisc_reverse_x)
+			x *= -1;
+		if (dd->pdata->tdisc_reverse_y)
+			y *= -1;
+
+		input_report_rel(dd->tdisc_device, REL_X, x);
+		input_report_rel(dd->tdisc_device, REL_Y, y);
+	}
+
+	if (dd->pdata->tdisc_report_absolute) {
+		input_report_abs(dd->tdisc_device, ABS_X, data[2]);
+		input_report_abs(dd->tdisc_device, ABS_Y, data[3]);
+		input_report_abs(dd->tdisc_device, ABS_PRESSURE, data[4]);
+	}
+
+	if (dd->pdata->tdisc_report_wheel)
+		input_report_rel(dd->tdisc_device, REL_WHEEL, (s8) data[6]);
+
+	input_sync(dd->tdisc_device);
+}
+
+static void tdisc_work_f(struct work_struct *work)
+{
+	int rc;
+	u8 data[8];
+	struct tdisc_data	*dd =
+		container_of(work, struct tdisc_data, tdisc_work.work);
+
+	/*
+	 * Read the value of the interrupt pin. If low, perform
+	 * an I2C read of 8 bytes to get the touch values and then
+	 * reschedule the work after 25ms. If pin is high, exit
+	 * and wait for next interrupt.
+	 */
+	rc = gpio_get_value_cansleep(dd->pdata->tdisc_gpio);
+	if (rc < 0) {
+		rc = pm_runtime_put_sync(&dd->clientp->dev);
+		if (rc < 0)
+			dev_dbg(&dd->clientp->dev, "%s: pm_runtime_put_sync"
+				" failed\n", __func__);
+		enable_irq(dd->clientp->irq);
+		return;
+	}
+
+	pr_debug("%s: TDISC gpio_get_value = %d\n", __func__, rc);
+	if (rc == 0) {
+		/* We have data to read */
+		rc = i2c_smbus_read_i2c_block_data(dd->clientp,
+				TDSIC_BLK_READ_CMD, 8, data);
+		if (rc < 0) {
+			pr_debug("%s:I2C read failed,trying again\n", __func__);
+			rc = i2c_smbus_read_i2c_block_data(dd->clientp,
+						TDSIC_BLK_READ_CMD, 8, data);
+			if (rc < 0) {
+				pr_err("%s:I2C read failed again, exiting\n",
+								 __func__);
+				goto fail_i2c_read;
+			}
+		}
+		pr_debug("%s: TDISC: I2C read success\n", __func__);
+		process_tdisc_data(dd, data);
+	} else {
+		/*
+		 * We have no data to read.
+		 * Enable the IRQ to receive further interrupts.
+		 */
+		enable_irq(dd->clientp->irq);
+
+		rc = pm_runtime_put_sync(&dd->clientp->dev);
+		if (rc < 0)
+			dev_dbg(&dd->clientp->dev, "%s: pm_runtime_put_sync"
+				" failed\n", __func__);
+		return;
+	}
+
+fail_i2c_read:
+	schedule_delayed_work(&dd->tdisc_work, TDISC_READ_DELAY);
+}
+
+static irqreturn_t tdisc_interrupt(int irq, void *dev_id)
+{
+	/*
+	 * The touch disc intially generates an interrupt on any
+	 * touch. The interrupt line is pulled low and remains low
+	 * untill there are touch operations being performed. In case
+	 * there are no further touch operations, the line goes high. The
+	 * same process repeats again the next time,when the disc is touched.
+	 *
+	 * We do the following operations once we receive an interrupt.
+	 * 1. Disable the IRQ for any further interrutps.
+	 * 2. Schedule work every 25ms if the GPIO is still low.
+	 * 3. In the work queue do a I2C read to get the touch data.
+	 * 4. If the GPIO is pulled high, enable the IRQ and cancel the work.
+	 */
+	struct tdisc_data *dd = dev_id;
+	int rc;
+
+	rc = pm_runtime_get(&dd->clientp->dev);
+	if (rc < 0)
+		dev_dbg(&dd->clientp->dev, "%s: pm_runtime_get"
+			" failed\n", __func__);
+	pr_debug("%s: TDISC IRQ ! :-)\n", __func__);
+
+	/* Schedule the work immediately */
+	disable_irq_nosync(dd->clientp->irq);
+	schedule_delayed_work(&dd->tdisc_work, 0);
+	return IRQ_HANDLED;
+}
+
+static int tdisc_open(struct input_dev *dev)
+{
+	int rc;
+	struct tdisc_data *dd = input_get_drvdata(dev);
+
+	if (!dd->clientp) {
+		/* Check if a valid i2c client is present */
+		pr_err("%s: no i2c adapter present \n", __func__);
+		return  -ENODEV;
+	}
+
+	/* Enable the device */
+	if (dd->pdata->tdisc_enable != NULL) {
+		rc = dd->pdata->tdisc_enable();
+		if (rc)
+			goto fail_open;
+	}
+	rc = request_any_context_irq(dd->clientp->irq, tdisc_interrupt,
+				 IRQF_TRIGGER_FALLING, TDISC_INT, dd);
+	if (rc < 0) {
+		pr_err("%s: request IRQ failed\n", __func__);
+		goto fail_irq_open;
+	}
+
+	return 0;
+
+fail_irq_open:
+	if (dd->pdata->tdisc_disable != NULL)
+		dd->pdata->tdisc_disable();
+fail_open:
+	return rc;
+}
+
+static void tdisc_close(struct input_dev *dev)
+{
+	struct tdisc_data *dd = input_get_drvdata(dev);
+
+	free_irq(dd->clientp->irq, dd);
+	cancel_delayed_work_sync(&dd->tdisc_work);
+	if (dd->pdata->tdisc_disable != NULL)
+		dd->pdata->tdisc_disable();
+}
+
+static int __devexit tdisc_remove(struct i2c_client *client)
+{
+	struct tdisc_data		*dd;
+
+	pm_runtime_disable(&client->dev);
+	dd = i2c_get_clientdata(client);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	unregister_early_suspend(&dd->tdisc_early_suspend);
+#endif
+	input_unregister_device(dd->tdisc_device);
+	if (dd->pdata->tdisc_release != NULL)
+		dd->pdata->tdisc_release();
+	i2c_set_clientdata(client, NULL);
+	kfree(dd);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int tdisc_suspend(struct device *dev)
+{
+	int rc;
+	struct tdisc_data *dd;
+
+	dd = dev_get_drvdata(dev);
+	if (device_may_wakeup(&dd->clientp->dev))
+		enable_irq_wake(dd->clientp->irq);
+	else {
+		disable_irq(dd->clientp->irq);
+
+		if (cancel_delayed_work_sync(&dd->tdisc_work))
+			enable_irq(dd->clientp->irq);
+
+		if (dd->pdata->tdisc_disable) {
+			rc = dd->pdata->tdisc_disable();
+			if (rc) {
+				pr_err("%s: Suspend failed\n", __func__);
+				return rc;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int tdisc_resume(struct device *dev)
+{
+	int rc;
+	struct tdisc_data *dd;
+
+	dd = dev_get_drvdata(dev);
+	if (device_may_wakeup(&dd->clientp->dev))
+		disable_irq_wake(dd->clientp->irq);
+	else {
+		if (dd->pdata->tdisc_enable) {
+			rc = dd->pdata->tdisc_enable();
+			if (rc) {
+				pr_err("%s: Resume failed\n", __func__);
+				return rc;
+			}
+		}
+		enable_irq(dd->clientp->irq);
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void tdisc_early_suspend(struct early_suspend *h)
+{
+	struct tdisc_data *dd = container_of(h, struct tdisc_data,
+						tdisc_early_suspend);
+
+	tdisc_suspend(&dd->clientp->dev);
+}
+
+static void tdisc_late_resume(struct early_suspend *h)
+{
+	struct tdisc_data *dd = container_of(h, struct tdisc_data,
+						tdisc_early_suspend);
+
+	tdisc_resume(&dd->clientp->dev);
+}
+#endif
+
+static struct dev_pm_ops tdisc_pm_ops = {
+#ifndef CONFIG_HAS_EARLYSUSPEND
+	.suspend = tdisc_suspend,
+	.resume  = tdisc_resume,
+#endif
+};
+#endif
+
+static const struct i2c_device_id tdisc_id[] = {
+	{ DEVICE_NAME, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, tdisc_id);
+
+static int __devinit tdisc_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	int			rc = -1;
+	int	x_max, x_min, y_max, y_min, pressure_min, pressure_max;
+	struct tdisc_platform_data  *pd;
+	struct tdisc_data           *dd;
+
+	/* Check if the I2C adapter supports the BLOCK READ functionality */
+	if (!i2c_check_functionality(client->adapter,
+			I2C_FUNC_SMBUS_READ_I2C_BLOCK))
+		return -ENODEV;
+
+	/* Enable runtime PM ops, start in ACTIVE mode */
+	rc = pm_runtime_set_active(&client->dev);
+	if (rc < 0)
+		dev_dbg(&client->dev, "unable to set runtime pm state\n");
+	pm_runtime_enable(&client->dev);
+
+	dd = kzalloc(sizeof *dd, GFP_KERNEL);
+	if (!dd) {
+		rc = -ENOMEM;
+		goto probe_exit;
+	}
+
+	i2c_set_clientdata(client, dd);
+	dd->clientp = client;
+	pd = client->dev.platform_data;
+	if (!pd) {
+		pr_err("%s: platform data not set \n", __func__);
+		rc = -EFAULT;
+		goto probe_free_exit;
+	}
+
+	dd->pdata = pd;
+
+	dd->tdisc_device = input_allocate_device();
+	if (!dd->tdisc_device) {
+		rc = -ENOMEM;
+		goto probe_free_exit;
+	}
+
+	input_set_drvdata(dd->tdisc_device, dd);
+	dd->tdisc_device->open       = tdisc_open;
+	dd->tdisc_device->close      = tdisc_close;
+	dd->tdisc_device->name       = TDISC_NAME;
+	dd->tdisc_device->id.bustype = BUS_I2C;
+	dd->tdisc_device->id.product = 1;
+	dd->tdisc_device->id.version = 1;
+
+	if (pd->tdisc_abs) {
+		x_max = pd->tdisc_abs->x_max;
+		x_min = pd->tdisc_abs->x_min;
+		y_max = pd->tdisc_abs->y_max;
+		y_min = pd->tdisc_abs->y_min;
+		pressure_max = pd->tdisc_abs->pressure_max;
+		pressure_min = pd->tdisc_abs->pressure_min;
+	} else {
+		x_max = X_MAX;
+		x_min = X_MIN;
+		y_max = Y_MAX;
+		y_min = Y_MIN;
+		pressure_max = PRESSURE_MAX;
+		pressure_min = PRESSURE_MIN;
+	}
+
+	/* Device capablities for relative motion */
+	input_set_capability(dd->tdisc_device, EV_REL, REL_X);
+	input_set_capability(dd->tdisc_device, EV_REL, REL_Y);
+	input_set_capability(dd->tdisc_device, EV_KEY, BTN_MOUSE);
+
+	/* Device capablities for absolute motion */
+	input_set_capability(dd->tdisc_device, EV_ABS, ABS_X);
+	input_set_capability(dd->tdisc_device, EV_ABS, ABS_Y);
+	input_set_capability(dd->tdisc_device, EV_ABS, ABS_PRESSURE);
+
+	input_set_abs_params(dd->tdisc_device, ABS_X, x_min, x_max, 0, 0);
+	input_set_abs_params(dd->tdisc_device, ABS_Y, y_min, y_max, 0, 0);
+	input_set_abs_params(dd->tdisc_device, ABS_PRESSURE, pressure_min,
+							pressure_max, 0, 0);
+
+	/* Device capabilities for scroll and buttons */
+	input_set_capability(dd->tdisc_device, EV_REL, REL_WHEEL);
+	input_set_capability(dd->tdisc_device, EV_KEY, KEY_LEFT);
+	input_set_capability(dd->tdisc_device, EV_KEY, KEY_RIGHT);
+	input_set_capability(dd->tdisc_device, EV_KEY, KEY_UP);
+	input_set_capability(dd->tdisc_device, EV_KEY, KEY_DOWN);
+	input_set_capability(dd->tdisc_device, EV_KEY, KEY_ENTER);
+
+	/* Setup the device for operation */
+	if (dd->pdata->tdisc_setup != NULL) {
+		rc = dd->pdata->tdisc_setup();
+		if (rc) {
+			pr_err("%s: Setup failed \n", __func__);
+			goto probe_unreg_free_exit;
+		}
+	}
+
+	/* Setup wakeup capability */
+	device_init_wakeup(&dd->clientp->dev, dd->pdata->tdisc_wakeup);
+
+	INIT_DELAYED_WORK(&dd->tdisc_work, tdisc_work_f);
+
+	rc = input_register_device(dd->tdisc_device);
+	if (rc) {
+		pr_err("%s: input register device failed \n", __func__);
+		rc = -EINVAL;
+		goto probe_register_fail;
+	}
+
+	pm_runtime_set_suspended(&client->dev);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	dd->tdisc_early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN +
+						TDISC_SUSPEND_LEVEL;
+	dd->tdisc_early_suspend.suspend = tdisc_early_suspend;
+	dd->tdisc_early_suspend.resume = tdisc_late_resume;
+	register_early_suspend(&dd->tdisc_early_suspend);
+#endif
+	return 0;
+
+probe_register_fail:
+	if (dd->pdata->tdisc_release != NULL)
+		dd->pdata->tdisc_release();
+probe_unreg_free_exit:
+	input_free_device(dd->tdisc_device);
+probe_free_exit:
+	i2c_set_clientdata(client, NULL);
+	kfree(dd);
+probe_exit:
+	pm_runtime_set_suspended(&client->dev);
+	pm_runtime_disable(&client->dev);
+	return rc;
+}
+
+static struct i2c_driver tdisc_driver = {
+	.driver = {
+		.name   = DRIVER_NAME,
+		.owner  = THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm = &tdisc_pm_ops,
+#endif
+	},
+	.probe   = tdisc_probe,
+	.remove  =  __devexit_p(tdisc_remove),
+	.id_table = tdisc_id,
+};
+
+static int __init tdisc_init(void)
+{
+	int rc;
+
+	rc = i2c_add_driver(&tdisc_driver);
+	if (rc)
+		pr_err("%s: i2c add driver failed \n", __func__);
+	return rc;
+}
+
+static void __exit tdisc_exit(void)
+{
+	i2c_del_driver(&tdisc_driver);
+}
+
+module_init(tdisc_init);
+module_exit(tdisc_exit);
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index f354813..badbc2b 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -411,6 +411,13 @@
 	  To compile this driver as a module, choose M here; the
 	  module will be called opencores-kbd.
 
+config KEYBOARD_PM8058
+	bool "Qualcomm PM8058 Matrix Keypad support"
+	depends on PM8058
+	help
+	  Say Y here to enable the driver for the keypad matrix interface
+	  on the Qualcomm PM8058 power management I/C device.
+
 config KEYBOARD_PXA27x
 	tristate "PXA27x/PXA3xx keypad support"
 	depends on PXA27x || PXA3xx || ARCH_MMP
@@ -570,6 +577,30 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called xtkbd.
 
+config KEYBOARD_QCIKBD
+   tristate "Quanta Computer Inc. keyboard"
+   depends on I2C
+   default n
+   help
+     Say Y here if you want to use the Quanta keyboard driver for ST 1.5
+     platform.
+
+config KEYBOARD_QCIKBD_REPEAT
+	bool "Enable Quanta Computer Inc. keyboard key repeat feature"
+	depends on KEYBOARD_QCIKBD
+	default n
+	help
+	  Say Y here if you want to enable Quanta keyboard driver's key repeat
+	  feature.
+
+config KEYBOARD_QCIKBD_LID
+	bool "Enable lid event for Quanta Computer Inc. keyboard"
+	depends on KEYBOARD_QCIKBD
+	default n
+	help
+	  Say Y here if you want to register lid event in Quanta keyboard
+	  driver.
+
 config KEYBOARD_W90P910
 	tristate "W90P910 Matrix Keypad support"
 	depends on ARCH_W90X900
@@ -581,3 +612,5 @@
 	  module will be called w90p910_keypad.
 
 endif
+
+
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index df7061f..61b57ef 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -51,4 +51,6 @@
 obj-$(CONFIG_KEYBOARD_TNETV107X)	+= tnetv107x-keypad.o
 obj-$(CONFIG_KEYBOARD_TWL4030)		+= twl4030_keypad.o
 obj-$(CONFIG_KEYBOARD_XTKBD)		+= xtkbd.o
+obj-$(CONFIG_KEYBOARD_QCIKBD)       += qci_kbd.o
 obj-$(CONFIG_KEYBOARD_W90P910)		+= w90p910_keypad.o
+obj-$(CONFIG_KEYBOARD_PMIC8058)		+= pmic8058-keypad.o
diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c
index 9b223d7..dd05cca 100644
--- a/drivers/input/keyboard/matrix_keypad.c
+++ b/drivers/input/keyboard/matrix_keypad.c
@@ -2,6 +2,7 @@
  *  GPIO driven matrix keyboard driver
  *
  *  Copyright (c) 2008 Marek Vasut <marek.vasut@gmail.com>
+ *  Copyright (c) 2012, Code Aurora Forum. All rights reserved.
  *
  *  Based on corgikbd.c
  *
@@ -34,7 +35,7 @@
 
 	uint32_t last_key_state[MATRIX_MAX_COLS];
 	struct delayed_work work;
-	spinlock_t lock;
+	struct mutex lock;
 	bool scan_pending;
 	bool stopped;
 	bool gpio_all_disabled;
@@ -162,19 +163,17 @@
 
 	activate_all_cols(pdata, true);
 
-	/* Enable IRQs again */
-	spin_lock_irq(&keypad->lock);
+	mutex_lock(&keypad->lock);
 	keypad->scan_pending = false;
 	enable_row_irqs(keypad);
-	spin_unlock_irq(&keypad->lock);
+	mutex_unlock(&keypad->lock);
 }
 
 static irqreturn_t matrix_keypad_interrupt(int irq, void *id)
 {
 	struct matrix_keypad *keypad = id;
-	unsigned long flags;
 
-	spin_lock_irqsave(&keypad->lock, flags);
+	mutex_lock(&keypad->lock);
 
 	/*
 	 * See if another IRQ beaten us to it and scheduled the
@@ -190,7 +189,7 @@
 		msecs_to_jiffies(keypad->pdata->debounce_ms));
 
 out:
-	spin_unlock_irqrestore(&keypad->lock, flags);
+	mutex_unlock(&keypad->lock);
 	return IRQ_HANDLED;
 }
 
@@ -334,19 +333,22 @@
 				matrix_keypad_interrupt,
 				pdata->clustered_irq_flags,
 				"matrix-keypad", keypad);
-		if (err) {
+		if (err < 0) {
 			dev_err(&pdev->dev,
 				"Unable to acquire clustered interrupt\n");
 			goto err_free_rows;
 		}
 	} else {
 		for (i = 0; i < pdata->num_row_gpios; i++) {
-			err = request_irq(gpio_to_irq(pdata->row_gpios[i]),
+			err = request_threaded_irq(
+					gpio_to_irq(pdata->row_gpios[i]),
+					NULL,
 					matrix_keypad_interrupt,
+					IRQF_DISABLED | IRQF_ONESHOT |
 					IRQF_TRIGGER_RISING |
 					IRQF_TRIGGER_FALLING,
 					"matrix-keypad", keypad);
-			if (err) {
+			if (err < 0) {
 				dev_err(&pdev->dev,
 					"Unable to acquire interrupt "
 					"for GPIO line %i\n",
@@ -415,7 +417,7 @@
 	keypad->row_shift = row_shift;
 	keypad->stopped = true;
 	INIT_DELAYED_WORK(&keypad->work, matrix_keypad_scan);
-	spin_lock_init(&keypad->lock);
+	mutex_init(&keypad->lock);
 
 	input_dev->name		= pdev->name;
 	input_dev->id.bustype	= BUS_HOST;
@@ -477,6 +479,7 @@
 	for (i = 0; i < pdata->num_col_gpios; i++)
 		gpio_free(pdata->col_gpios[i]);
 
+	mutex_destroy(&keypad->lock);
 	input_unregister_device(keypad->input_dev);
 	platform_set_drvdata(pdev, NULL);
 	kfree(keypad->keycodes);
diff --git a/drivers/input/keyboard/pmic8xxx-keypad.c b/drivers/input/keyboard/pmic8xxx-keypad.c
index 01a1c9f..d529ea4 100644
--- a/drivers/input/keyboard/pmic8xxx-keypad.c
+++ b/drivers/input/keyboard/pmic8xxx-keypad.c
@@ -116,6 +116,9 @@
 	int rc;
 
 	rc = pm8xxx_writeb(kp->dev->parent, reg, data);
+	if (rc < 0)
+		dev_warn(kp->dev, "Error writing pmic8xxx: %X - ret %X\n",
+				reg, rc);
 	return rc;
 }
 
@@ -125,6 +128,10 @@
 	int rc;
 
 	rc = pm8xxx_read_buf(kp->dev->parent, reg, data, num_bytes);
+	if (rc < 0)
+		dev_warn(kp->dev, "Error reading pmic8xxx: %X - ret %X\n",
+				reg, rc);
+
 	return rc;
 }
 
@@ -134,6 +141,9 @@
 	int rc;
 
 	rc = pmic8xxx_kp_read(kp, data, reg, 1);
+	if (rc < 0)
+		dev_warn(kp->dev, "Error reading pmic8xxx: %X - ret %X\n",
+				reg, rc);
 	return rc;
 }
 
@@ -463,7 +473,7 @@
 					__func__, gpio_start + i, rc);
 			return rc;
 		}
-	 }
+	}
 
 	return 0;
 }
@@ -532,7 +542,7 @@
 		.output_buffer	= PM_GPIO_OUT_BUF_OPEN_DRAIN,
 		.output_value	= 0,
 		.pull		= PM_GPIO_PULL_NO,
-		.vin_sel	= PM_GPIO_VIN_S3,
+		.vin_sel	= PM_GPIO_VIN_S4,
 		.out_strength	= PM_GPIO_STRENGTH_LOW,
 		.function	= PM_GPIO_FUNC_1,
 		.inv_int_pol	= 1,
@@ -541,7 +551,7 @@
 	struct pm_gpio kypd_sns = {
 		.direction	= PM_GPIO_DIR_IN,
 		.pull		= PM_GPIO_PULL_UP_31P5,
-		.vin_sel	= PM_GPIO_VIN_S3,
+		.vin_sel	= PM_GPIO_VIN_S4,
 		.out_strength	= PM_GPIO_STRENGTH_NO,
 		.function	= PM_GPIO_FUNC_NORMAL,
 		.inv_int_pol	= 1,
diff --git a/drivers/input/keyboard/qci_kbd.c b/drivers/input/keyboard/qci_kbd.c
new file mode 100644
index 0000000..d735012
--- /dev/null
+++ b/drivers/input/keyboard/qci_kbd.c
@@ -0,0 +1,721 @@
+/* Quanta I2C Keyboard Driver
+ *
+ * Copyright (C) 2009 Quanta Computer Inc.
+ * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ * Author: Hsin Wu <hsin.wu@quantatw.com>
+ * Author: Austin Lai <austin.lai@quantatw.com>
+ *
+ * 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.
+ *
+ */
+
+ /*
+ *
+ *  The Driver with I/O communications via the I2C Interface for ON2 of AP BU.
+ *  And it is only working on the nuvoTon WPCE775x Embedded Controller.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/keyboard.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+
+#include <linux/input/qci_kbd.h>
+
+/* Keyboard special scancode */
+#define RC_KEY_FN          0x70
+#define RC_KEY_BREAK       0x80
+#define KEY_ACK_FA         0xFA
+#define SCAN_EMUL0         0xE0
+#define SCAN_EMUL1         0xE1
+#define SCAN_PAUSE1        0x1D
+#define SCAN_PAUSE2        0x45
+#define SCAN_LIDSW_OPEN    0x70
+#define SCAN_LIDSW_CLOSE   0x71
+
+/* Keyboard keycodes */
+#define NOKEY           KEY_RESERVED
+#define KEY_LEFTWIN     KEY_LEFTMETA
+#define KEY_RIGHTWIN    KEY_RIGHTMETA
+#define KEY_APPS        KEY_COMPOSE
+#define KEY_PRINTSCR    KEY_SYSRQ
+
+#define KEYBOARD_ID_NAME          "qci-i2ckbd"
+#define KEYBOARD_NAME                "Quanta Keyboard"
+#define KEYBOARD_DEVICE             "/i2c/input0"
+#define KEYBOARD_CMD_ENABLE             0xF4
+#define KEYBOARD_CMD_SET_LED            0xED
+
+/*-----------------------------------------------------------------------------
+ * Keyboard scancode to linux keycode translation table
+ *---------------------------------------------------------------------------*/
+
+static const unsigned char on2_keycode[256] = {
+	[0]   = NOKEY,
+	[1]   = NOKEY,
+	[2]   = NOKEY,
+	[3]   = KEY_5,
+	[4]   = KEY_7,
+	[5]   = KEY_9,
+	[6]   = KEY_MINUS,
+	[7]   = NOKEY,
+	[8]   = NOKEY,
+	[9]   = NOKEY,
+	[10]  = NOKEY,
+	[11]  = KEY_LEFTBRACE,
+	[12]  = KEY_F10,
+	[13]  = KEY_INSERT,
+	[14]  = KEY_F11,
+	[15]  = KEY_ESC,
+	[16]  = NOKEY,
+	[17]  = NOKEY,
+	[18]  = NOKEY,
+	[19]  = KEY_4,
+	[20]  = KEY_6,
+	[21]  = KEY_8,
+	[22]  = KEY_0,
+	[23]  = KEY_EQUAL,
+	[24]  = NOKEY,
+	[25]  = NOKEY,
+	[26]  = NOKEY,
+	[27]  = KEY_P,
+	[28]  = KEY_F9,
+	[29]  = KEY_DELETE,
+	[30]  = KEY_F12,
+	[31]  = KEY_GRAVE,
+	[32]  = KEY_W,
+	[33]  = NOKEY,
+	[34]  = NOKEY,
+	[35]  = KEY_R,
+	[36]  = KEY_T,
+	[37]  = KEY_U,
+	[38]  = KEY_O,
+	[39]  = KEY_RIGHTBRACE,
+	[40]  = NOKEY,
+	[41]  = NOKEY,
+	[42]  = NOKEY,
+	[43]  = KEY_APOSTROPHE,
+	[44]  = KEY_BACKSPACE,
+	[45]  = NOKEY,
+	[46]  = KEY_F8,
+	[47]  = KEY_F5,
+	[48]  = KEY_S,
+	[49]  = NOKEY,
+	[50]  = NOKEY,
+	[51]  = KEY_E,
+	[52]  = KEY_H,
+	[53]  = KEY_Y,
+	[54]  = KEY_I,
+	[55]  = KEY_ENTER,
+	[56]  = NOKEY,
+	[57]  = NOKEY,
+	[58]  = NOKEY,
+	[59]  = KEY_SEMICOLON,
+	[60]  = KEY_3,
+	[61]  = KEY_PAGEUP,
+	[62]  = KEY_Q,
+	[63]  = KEY_TAB,
+	[64]  = KEY_A,
+	[65]  = NOKEY,
+	[66]  = NOKEY,
+	[67]  = KEY_F,
+	[68]  = KEY_G,
+	[69]  = KEY_J,
+	[70]  = KEY_L,
+	[71]  = NOKEY,
+	[72]  = KEY_RIGHTSHIFT,
+	[73]  = NOKEY,
+	[74]  = NOKEY,
+	[75]  = KEY_SLASH,
+	[76]  = KEY_2,
+	[77]  = KEY_PAGEDOWN,
+	[78]  = KEY_F4,
+	[79]  = KEY_F1,
+	[80]  = KEY_Z,
+	[81]  = NOKEY,
+	[82]  = NOKEY,
+	[83]  = KEY_D,
+	[84]  = KEY_V,
+	[85]  = KEY_N,
+	[86]  = KEY_K,
+	[87]  = NOKEY,
+	[88]  = KEY_LEFTSHIFT,
+	[89]  = KEY_RIGHTCTRL,
+	[90]  = NOKEY,
+	[91]  = KEY_DOT,
+	[92]  = KEY_UP,
+	[93]  = KEY_RIGHT,
+	[94]  = KEY_F3,
+	[95]  = KEY_F2,
+	[96]  = NOKEY,
+	[97]  = NOKEY,
+	[98]  = KEY_RIGHTALT,
+	[99]  = KEY_X,
+	[100] = KEY_C,
+	[101] = KEY_B,
+	[102] = KEY_COMMA,
+	[103] = NOKEY,
+	[104] = NOKEY,
+	[105] = NOKEY,
+	[106] = NOKEY,
+	[107] = NOKEY,
+	[108] = KEY_PRINTSCR,
+	[109] = KEY_DOWN,
+	[110] = KEY_1,
+	[111] = KEY_CAPSLOCK,
+	[112] = KEY_F24,
+	[113] = KEY_HOME,
+	[114] = KEY_LEFTALT,
+	[115] = NOKEY,
+	[116] = KEY_SPACE,
+	[117] = KEY_BACKSLASH,
+	[118] = KEY_M,
+	[119] = KEY_COMPOSE,
+	[120] = NOKEY,
+	[121] = KEY_LEFTCTRL,
+	[122] = NOKEY,
+	[123] = NOKEY,
+	[124] = KEY_PAUSE,
+	[125] = KEY_LEFT,
+	[126] = KEY_F7,
+	[127] = KEY_F6,
+	[128] = NOKEY,
+	[129] = NOKEY,
+	[130] = NOKEY,
+	[131] = NOKEY,
+	[132] = NOKEY,
+	[133] = NOKEY,
+	[134] = NOKEY,
+	[135] = NOKEY,
+	[136] = NOKEY,
+	[137] = NOKEY,
+	[138] = NOKEY,
+	[139] = NOKEY,
+	[140] = NOKEY,
+	[141] = NOKEY,
+	[142] = NOKEY,
+	[143] = NOKEY,
+	[144] = NOKEY,
+	[145] = NOKEY,
+	[146] = NOKEY,
+	[147] = NOKEY,
+	[148] = NOKEY,
+	[149] = NOKEY,
+	[150] = NOKEY,
+	[151] = NOKEY,
+	[152] = NOKEY,
+	[153] = NOKEY,
+	[154] = NOKEY,
+	[155] = NOKEY,
+	[156] = NOKEY,
+	[157] = NOKEY,
+	[158] = NOKEY,
+	[159] = NOKEY,
+	[160] = NOKEY,
+	[161] = NOKEY,
+	[162] = NOKEY,
+	[163] = NOKEY,
+	[164] = NOKEY,
+	[165] = NOKEY,
+	[166] = NOKEY,
+	[167] = NOKEY,
+	[168] = NOKEY,
+	[169] = NOKEY,
+	[170] = NOKEY,
+	[171] = NOKEY,
+	[172] = NOKEY,
+	[173] = NOKEY,
+	[174] = NOKEY,
+	[175] = NOKEY,
+	[176] = NOKEY,
+	[177] = NOKEY,
+	[178] = NOKEY,
+	[179] = NOKEY,
+	[180] = NOKEY,
+	[181] = NOKEY,
+	[182] = NOKEY,
+	[183] = NOKEY,
+	[184] = NOKEY,
+	[185] = NOKEY,
+	[186] = NOKEY,
+	[187] = NOKEY,
+	[188] = NOKEY,
+	[189] = KEY_HOME,
+	[190] = NOKEY,
+	[191] = NOKEY,
+	[192] = NOKEY,
+	[193] = NOKEY,
+	[194] = NOKEY,
+	[195] = NOKEY,
+	[196] = NOKEY,
+	[197] = NOKEY,
+	[198] = NOKEY,
+	[199] = NOKEY,
+	[200] = NOKEY,
+	[201] = NOKEY,
+	[202] = NOKEY,
+	[203] = NOKEY,
+	[204] = NOKEY,
+	[205] = KEY_END,
+	[206] = NOKEY,
+	[207] = NOKEY,
+	[208] = NOKEY,
+	[209] = NOKEY,
+	[210] = NOKEY,
+	[211] = NOKEY,
+	[212] = NOKEY,
+	[213] = NOKEY,
+	[214] = NOKEY,
+	[215] = NOKEY,
+	[216] = NOKEY,
+	[217] = NOKEY,
+	[218] = NOKEY,
+	[219] = NOKEY,
+	[220] = KEY_VOLUMEUP,
+	[221] = KEY_BRIGHTNESSUP,
+	[222] = NOKEY,
+	[223] = NOKEY,
+	[224] = NOKEY,
+	[225] = NOKEY,
+	[226] = NOKEY,
+	[227] = NOKEY,
+	[228] = NOKEY,
+	[229] = NOKEY,
+	[230] = NOKEY,
+	[231] = NOKEY,
+	[232] = NOKEY,
+	[233] = NOKEY,
+	[234] = NOKEY,
+	[235] = NOKEY,
+	[236] = NOKEY,
+	[237] = KEY_VOLUMEDOWN,
+	[238] = NOKEY,
+	[239] = NOKEY,
+	[240] = NOKEY,
+	[241] = NOKEY,
+	[242] = NOKEY,
+	[243] = NOKEY,
+	[244] = NOKEY,
+	[245] = NOKEY,
+	[246] = NOKEY,
+	[247] = NOKEY,
+	[248] = NOKEY,
+	[249] = NOKEY,
+	[250] = NOKEY,
+	[251] = NOKEY,
+	[252] = NOKEY,
+	[253] = KEY_BRIGHTNESSDOWN,
+	[254] = NOKEY,
+	[255] = NOKEY,
+};
+
+static const u8 emul0_map[128] = {
+	  0,   0,   0,  0,  0,  0,  0,   0,   0,   0,  0,   0,  0,   0,  0,   0,
+	  0,   0,   0,  0,  0,  0,  0,   0,   0,   0,  0,   0, 96,  97,  0,   0,
+	113,   0,   0,  0,  0,  0,  0,   0,   0,   0,  0,   0,  0,   0, 114,  0,
+	115,   0,   0,  0,  0, 98,  0,  99, 100,   0,  0,   0,  0,   0,  0,   0,
+	  0,   0,   0,  0,  0,  0,  0, 102, 103, 104,  0, 105,  0, 106,  0, 107,
+	108, 109, 110, 111, 0,  0,  0,   0,   0,   0,  0, 139,  0, 150,  0,   0,
+	  0,   0,   0,  0,  0,  0,  0,   0,   0,   0,  0,   0,  0,   0,  0,   0,
+	  0,   0,   0,  0,  0,  0,  0,   0,   0,   0,  0,   0,  0,   0,  0,   0,
+};
+
+/*-----------------------------------------------------------------------------
+ * Global variables
+ *---------------------------------------------------------------------------*/
+
+struct input_dev *g_qci_keyboard_dev;
+
+/* General structure to hold the driver data */
+struct i2ckbd_drv_data {
+	struct i2c_client *ki2c_client;
+	struct work_struct work;
+	struct input_dev *qcikbd_dev;
+	struct mutex kb_mutex;
+	unsigned int qcikbd_gpio; /* GPIO used for interrupt */
+	unsigned int qcikbd_irq;
+	unsigned int key_down;
+	unsigned int escape;
+	unsigned int pause_seq;
+	unsigned int fn;
+	unsigned char led_status;
+	bool standard_scancodes;
+	bool kb_leds;
+	bool event_led;
+	bool emul0;
+	bool emul1;
+	bool pause1;
+};
+#ifdef CONFIG_PM
+static int qcikbd_suspend(struct device *dev)
+{
+	struct i2ckbd_drv_data *context = input_get_drvdata(g_qci_keyboard_dev);
+
+	enable_irq_wake(context->qcikbd_irq);
+	return 0;
+}
+
+static int qcikbd_resume(struct device *dev)
+{
+	struct i2ckbd_drv_data *context = input_get_drvdata(g_qci_keyboard_dev);
+	struct i2c_client *ikbdclient = context->ki2c_client;
+
+	disable_irq_wake(context->qcikbd_irq);
+
+	/* consume any keypress generated while suspended */
+	i2c_smbus_read_byte(ikbdclient);
+	return 0;
+}
+#endif
+static int __devinit qcikbd_probe(struct i2c_client *client,
+	const struct i2c_device_id *id);
+static int __devexit qcikbd_remove(struct i2c_client *kbd);
+
+static const struct i2c_device_id qcikbd_idtable[] = {
+	{ KEYBOARD_ID_NAME, 0 },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(i2c, qcikbd_idtable);
+
+#ifdef CONFIG_PM
+static struct dev_pm_ops qcikbd_pm_ops = {
+	.suspend  = qcikbd_suspend,
+	.resume   = qcikbd_resume,
+};
+#endif
+static struct i2c_driver i2ckbd_driver = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name  = KEYBOARD_ID_NAME,
+#ifdef CONFIG_PM
+		.pm = &qcikbd_pm_ops,
+#endif
+	},
+	.probe	  = qcikbd_probe,
+	.remove = __devexit_p(qcikbd_remove),
+	.id_table = qcikbd_idtable,
+};
+
+/*-----------------------------------------------------------------------------
+ * Driver functions
+ *---------------------------------------------------------------------------*/
+
+#ifdef CONFIG_KEYBOARD_QCIKBD_LID
+static void process_lid(struct input_dev *ikbdev, unsigned char scancode)
+{
+	if (scancode == SCAN_LIDSW_OPEN)
+		input_report_switch(ikbdev, SW_LID, 0);
+	else if (scancode == SCAN_LIDSW_CLOSE)
+		input_report_switch(ikbdev, SW_LID, 1);
+	else
+		return;
+	input_sync(ikbdev);
+}
+#endif
+
+static irqreturn_t qcikbd_interrupt(int irq, void *dev_id)
+{
+	struct i2ckbd_drv_data *ikbd_drv_data = dev_id;
+	schedule_work(&ikbd_drv_data->work);
+	return IRQ_HANDLED;
+}
+
+static void qcikbd_work_handler(struct work_struct *_work)
+{
+	unsigned char scancode;
+	unsigned char scancode_only;
+	unsigned int  keycode;
+
+	struct i2ckbd_drv_data *ikbd_drv_data =
+		container_of(_work, struct i2ckbd_drv_data, work);
+
+	struct i2c_client *ikbdclient = ikbd_drv_data->ki2c_client;
+	struct input_dev *ikbdev = ikbd_drv_data->qcikbd_dev;
+
+	mutex_lock(&ikbd_drv_data->kb_mutex);
+
+	if ((ikbd_drv_data->kb_leds) && (ikbd_drv_data->event_led)) {
+		i2c_smbus_write_byte(ikbdclient, KEYBOARD_CMD_SET_LED);
+		i2c_smbus_write_byte(ikbdclient, ikbd_drv_data->led_status);
+		ikbd_drv_data->event_led = 0;
+		goto work_exit;
+	}
+
+	scancode = i2c_smbus_read_byte(ikbdclient);
+
+	if (scancode == KEY_ACK_FA)
+		goto work_exit;
+
+	if (ikbd_drv_data->standard_scancodes) {
+		/* pause key is E1 1D 45 */
+		if (scancode == SCAN_EMUL1) {
+			ikbd_drv_data->emul1 = 1;
+			goto work_exit;
+		}
+		if (ikbd_drv_data->emul1) {
+			ikbd_drv_data->emul1 = 0;
+			if ((scancode & 0x7f) == SCAN_PAUSE1)
+				ikbd_drv_data->pause1 = 1;
+			goto work_exit;
+		}
+		if (ikbd_drv_data->pause1) {
+			ikbd_drv_data->pause1 = 0;
+			if ((scancode & 0x7f) == SCAN_PAUSE2) {
+				input_report_key(ikbdev, KEY_PAUSE,
+						 !(scancode & 0x80));
+				input_sync(ikbdev);
+			}
+			goto work_exit;
+		}
+
+		if (scancode == SCAN_EMUL0) {
+			ikbd_drv_data->emul0 = 1;
+			goto work_exit;
+		}
+		if (ikbd_drv_data->emul0) {
+			ikbd_drv_data->emul0 = 0;
+			scancode_only = scancode & 0x7f;
+#ifdef CONFIG_KEYBOARD_QCIKBD_LID
+			if ((scancode_only == SCAN_LIDSW_OPEN) ||
+			    (scancode_only == SCAN_LIDSW_CLOSE)) {
+				process_lid(ikbdev, scancode);
+				goto work_exit;
+			}
+#endif
+			keycode = emul0_map[scancode_only];
+			if (!keycode) {
+				dev_err(&ikbdev->dev,
+					"Unrecognized scancode %02x %02x\n",
+					SCAN_EMUL0, scancode);
+				goto work_exit;
+			}
+		} else {
+			keycode = scancode & 0x7f;
+		}
+		/* MS bit of scancode indicates direction of keypress */
+		ikbd_drv_data->key_down = !(scancode & 0x80);
+		if (keycode) {
+			input_event(ikbdev, EV_MSC, MSC_SCAN, scancode);
+			input_report_key(ikbdev, keycode,
+					 ikbd_drv_data->key_down);
+			input_sync(ikbdev);
+		}
+		goto work_exit;
+	}
+
+	mutex_unlock(&ikbd_drv_data->kb_mutex);
+
+	if (scancode == RC_KEY_FN) {
+		ikbd_drv_data->fn = 0x80;     /* select keycode table  > 0x7F */
+	} else {
+		ikbd_drv_data->key_down = 1;
+		if (scancode & RC_KEY_BREAK) {
+			ikbd_drv_data->key_down = 0;
+			if ((scancode & 0x7F) == RC_KEY_FN)
+				ikbd_drv_data->fn = 0;
+		}
+		keycode = on2_keycode[(scancode & 0x7F) | ikbd_drv_data->fn];
+		if (keycode != NOKEY) {
+			input_report_key(ikbdev,
+					 keycode,
+					 ikbd_drv_data->key_down);
+			input_sync(ikbdev);
+		}
+	}
+	return;
+
+work_exit:
+	mutex_unlock(&ikbd_drv_data->kb_mutex);
+}
+
+static int qcikbd_input_event(struct input_dev *dev, unsigned int type,
+			      unsigned int code, int value)
+{
+	struct i2ckbd_drv_data *ikbd_drv_data = input_get_drvdata(dev);
+	struct input_dev *ikbdev = ikbd_drv_data->qcikbd_dev;
+
+	if (type != EV_LED)
+		return -EINVAL;
+
+	ikbd_drv_data->led_status =
+		(test_bit(LED_SCROLLL, ikbdev->led) ? 1 : 0) |
+		(test_bit(LED_NUML, ikbdev->led) ? 2 : 0) |
+		(test_bit(LED_CAPSL, ikbdev->led) ? 4 : 0);
+	ikbd_drv_data->event_led = 1;
+
+	schedule_work(&ikbd_drv_data->work);
+	return 0;
+}
+
+static int qcikbd_open(struct input_dev *dev)
+{
+	struct i2ckbd_drv_data *ikbd_drv_data = input_get_drvdata(dev);
+	struct i2c_client *ikbdclient = ikbd_drv_data->ki2c_client;
+
+	/* Send F4h - enable keyboard */
+	i2c_smbus_write_byte(ikbdclient, KEYBOARD_CMD_ENABLE);
+	return 0;
+}
+
+static int __devinit qcikbd_probe(struct i2c_client *client,
+				    const struct i2c_device_id *id)
+{
+	int err;
+	int i;
+	struct i2ckbd_drv_data *context;
+	struct qci_kbd_platform_data *pdata = client->dev.platform_data;
+
+	if (!pdata) {
+		pr_err("[KBD] platform data not supplied\n");
+		return -EINVAL;
+	}
+
+	context = kzalloc(sizeof(struct i2ckbd_drv_data), GFP_KERNEL);
+	if (!context)
+		return -ENOMEM;
+	i2c_set_clientdata(client, context);
+	context->ki2c_client = client;
+	context->qcikbd_gpio = client->irq;
+	client->driver = &i2ckbd_driver;
+
+	INIT_WORK(&context->work, qcikbd_work_handler);
+	mutex_init(&context->kb_mutex);
+
+	err = gpio_request(context->qcikbd_gpio, "qci-kbd");
+	if (err) {
+		pr_err("[KBD] err gpio request\n");
+		goto gpio_request_fail;
+	}
+
+	context->qcikbd_irq = gpio_to_irq(context->qcikbd_gpio);
+	err = request_irq(context->qcikbd_irq,
+			  qcikbd_interrupt,
+			  IRQF_TRIGGER_FALLING,
+			  KEYBOARD_ID_NAME,
+			  context);
+	if (err) {
+		pr_err("[KBD] err unable to get IRQ\n");
+		goto request_irq_fail;
+	}
+
+	context->standard_scancodes = pdata->standard_scancodes;
+	context->kb_leds = pdata->kb_leds;
+	context->qcikbd_dev = input_allocate_device();
+	if (!context->qcikbd_dev) {
+		pr_err("[KBD]allocting memory err\n");
+		err = -ENOMEM;
+		goto allocate_fail;
+	}
+
+	context->qcikbd_dev->name       = KEYBOARD_NAME;
+	context->qcikbd_dev->phys       = KEYBOARD_DEVICE;
+	context->qcikbd_dev->id.bustype = BUS_I2C;
+	context->qcikbd_dev->id.vendor  = 0x1050;
+	context->qcikbd_dev->id.product = 0x0006;
+	context->qcikbd_dev->id.version = 0x0004;
+	context->qcikbd_dev->open       = qcikbd_open;
+	set_bit(EV_KEY, context->qcikbd_dev->evbit);
+	__set_bit(MSC_SCAN, context->qcikbd_dev->mscbit);
+
+	if (pdata->repeat)
+		set_bit(EV_REP, context->qcikbd_dev->evbit);
+
+	/* Enable all supported keys */
+	for (i = 1; i < ARRAY_SIZE(on2_keycode) ; i++)
+		set_bit(on2_keycode[i], context->qcikbd_dev->keybit);
+
+	set_bit(KEY_POWER, context->qcikbd_dev->keybit);
+	set_bit(KEY_END, context->qcikbd_dev->keybit);
+	set_bit(KEY_VOLUMEUP, context->qcikbd_dev->keybit);
+	set_bit(KEY_VOLUMEDOWN, context->qcikbd_dev->keybit);
+	set_bit(KEY_ZOOMIN, context->qcikbd_dev->keybit);
+	set_bit(KEY_ZOOMOUT, context->qcikbd_dev->keybit);
+
+#ifdef CONFIG_KEYBOARD_QCIKBD_LID
+	set_bit(EV_SW, context->qcikbd_dev->evbit);
+	set_bit(SW_LID, context->qcikbd_dev->swbit);
+#endif
+
+	if (context->kb_leds) {
+		context->qcikbd_dev->event = qcikbd_input_event;
+		__set_bit(EV_LED, context->qcikbd_dev->evbit);
+		__set_bit(LED_NUML, context->qcikbd_dev->ledbit);
+		__set_bit(LED_CAPSL, context->qcikbd_dev->ledbit);
+		__set_bit(LED_SCROLLL, context->qcikbd_dev->ledbit);
+	}
+
+	input_set_drvdata(context->qcikbd_dev, context);
+	err = input_register_device(context->qcikbd_dev);
+	if (err) {
+		pr_err("[KBD] err input register device\n");
+		goto register_fail;
+	}
+	g_qci_keyboard_dev = context->qcikbd_dev;
+	return 0;
+register_fail:
+	input_free_device(context->qcikbd_dev);
+
+allocate_fail:
+	free_irq(context->qcikbd_irq, context);
+
+request_irq_fail:
+	gpio_free(context->qcikbd_gpio);
+
+gpio_request_fail:
+	i2c_set_clientdata(client, NULL);
+	kfree(context);
+	return err;
+}
+
+static int __devexit qcikbd_remove(struct i2c_client *dev)
+{
+	struct i2ckbd_drv_data *context = i2c_get_clientdata(dev);
+
+	free_irq(context->qcikbd_irq, context);
+	gpio_free(context->qcikbd_gpio);
+	input_free_device(context->qcikbd_dev);
+	input_unregister_device(context->qcikbd_dev);
+	kfree(context);
+
+	return 0;
+}
+
+static int __init qcikbd_init(void)
+{
+	return i2c_add_driver(&i2ckbd_driver);
+}
+
+static void __exit qcikbd_exit(void)
+{
+	i2c_del_driver(&i2ckbd_driver);
+}
+
+struct input_dev *nkbc_keypad_get_input_dev(void)
+{
+	return g_qci_keyboard_dev;
+}
+EXPORT_SYMBOL(nkbc_keypad_get_input_dev);
+module_init(qcikbd_init);
+module_exit(qcikbd_exit);
+
+MODULE_AUTHOR("Quanta Computer Inc.");
+MODULE_DESCRIPTION("Quanta Embedded Controller I2C Keyboard Driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 66550c2..8fb19dc 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -423,6 +423,18 @@
 	help
 	  Say Y here if you want to support gpio based keys, wheels etc...
 
+config INPUT_ISA1200_FF_MEMLESS
+	tristate "ISA1200 haptic ff-memless support"
+	depends on I2C
+	select INPUT_FF_MEMLESS
+	help
+	  ISA1200 is a high performance enhanced haptic chip.
+	  Say Y here if you want to support ISA1200 connected via I2C,
+	  and select N if you are unsure.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called isa1200-ff-memless.
+
 config HP_SDC_RTC
 	tristate "HP SDC Real Time Clock"
 	depends on (GSC || HP300) && SERIO
@@ -606,4 +618,33 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called xen-kbdfront.
 
+config PMIC8058_OTHC
+        tristate "Qualcomm PMIC8058 OTHC support"
+        default n
+        depends on PMIC8058
+        help
+          Say Y here if you want support PMIC8058 OTHC.
+
+          To compile this driver as a module, choose M here: the
+          module will be called pmic8058-othc.
+
+config INPUT_PMIC8058_VIBRA_MEMLESS
+	tristate "Qualcomm PM8058 vibrator support (ff-memless)"
+	depends on PMIC8058 && INPUT_FF_MEMLESS
+	default n
+	help
+	  This option enables device driver support for the vibrator
+	  on Qualcomm PM8058 chip. This driver supports ff-memless interface
+	  from input framework.
+
+	  To compile this driver as module, choose M here: the
+	  module will be called pmic8058-vib-memless.
+
+config BOSCH_BMA150
+        tristate "SMB380/BMA150 acceleration sensor support"
+        depends on I2C=y
+        help
+          If you say yes here you get support for Bosch Sensortec's
+          acceleration sensors SMB380/BMA150.
+
 endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index f8cb522..eecee4e 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -57,3 +57,6 @@
 obj-$(CONFIG_INPUT_WM831X_ON)		+= wm831x-on.o
 obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND)	+= xen-kbdfront.o
 obj-$(CONFIG_INPUT_YEALINK)		+= yealink.o
+obj-$(CONFIG_PMIC8058_OTHC)             += pmic8058-othc.o
+obj-$(CONFIG_INPUT_PMIC8058_VIBRA_MEMLESS) += pmic8058-vib-memless.o
+obj-$(CONFIG_BOSCH_BMA150)              += bma150.o
diff --git a/drivers/input/misc/bma150.c b/drivers/input/misc/bma150.c
index e2f1e9f..8911c0b 100644
--- a/drivers/input/misc/bma150.c
+++ b/drivers/input/misc/bma150.c
@@ -1,50 +1,174 @@
-/*
- * Copyright (c) 2011 Bosch Sensortec GmbH
- * Copyright (c) 2011 Unixphere
- *
- * This driver adds support for Bosch Sensortec's digital acceleration
- * sensors BMA150 and SMB380.
- * The SMB380 is fully compatible with BMA150 and only differs in packaging.
- *
- * The datasheet for the BMA150 chip can be found here:
- * http://www.bosch-sensortec.com/content/language1/downloads/BST-BMA150-DS000-07.pdf
- *
- * 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+/*  Date: 2011/3/7 11:00:00
+ *  Revision: 2.11
  */
-#include <linux/kernel.h>
+
+/*
+ * This software program is licensed subject to the GNU General Public License
+ * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html
+
+ * (C) Copyright 2011 Bosch Sensortec GmbH
+ * All Rights Reserved
+ */
+
+
+/* file BMA150.c
+   brief This file contains all function implementations for the BMA150 in linux
+
+*/
+
 #include <linux/module.h>
+#include <linux/init.h>
 #include <linux/i2c.h>
-#include <linux/input.h>
-#include <linux/input-polldev.h>
 #include <linux/interrupt.h>
-#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/workqueue.h>
 #include <linux/slab.h>
-#include <linux/pm.h>
-#include <linux/pm_runtime.h>
+#include <linux/mutex.h>
 #include <linux/bma150.h>
 
-#define ABSMAX_ACC_VAL		0x01FF
-#define ABSMIN_ACC_VAL		-(ABSMAX_ACC_VAL)
+#define SENSOR_NAME			"bma150"
+#define GRAVITY_EARTH		9806550
+#define ABSMIN_2G			(-GRAVITY_EARTH * 2)
+#define ABSMAX_2G			(GRAVITY_EARTH * 2)
+#define BMA150_MAX_DELAY	200
+#define BMA150_CHIP_ID		2
+#define BMA150_RANGE_SET	0
+#define BMA150_BW_SET		4
 
-/* Each axis is represented by a 2-byte data word */
-#define BMA150_XYZ_DATA_SIZE	6
 
-/* Input poll interval in milliseconds */
-#define BMA150_POLL_INTERVAL	10
-#define BMA150_POLL_MAX		200
-#define BMA150_POLL_MIN		0
+
+#define BMA150_CHIP_ID_REG		0x00
+#define BMA150_X_AXIS_LSB_REG	0x02
+#define BMA150_X_AXIS_MSB_REG	0x03
+#define BMA150_Y_AXIS_LSB_REG	0x04
+#define BMA150_Y_AXIS_MSB_REG	0x05
+#define BMA150_Z_AXIS_LSB_REG	0x06
+#define BMA150_Z_AXIS_MSB_REG	0x07
+#define BMA150_STATUS_REG		0x09
+#define BMA150_CTRL_REG			0x0a
+#define BMA150_CONF1_REG		0x0b
+
+#define BMA150_CUSTOMER1_REG	0x12
+#define BMA150_CUSTOMER2_REG	0x13
+#define BMA150_RANGE_BWIDTH_REG	0x14
+#define BMA150_CONF2_REG		0x15
+
+#define BMA150_OFFS_GAIN_X_REG	0x16
+#define BMA150_OFFS_GAIN_Y_REG	0x17
+#define BMA150_OFFS_GAIN_Z_REG	0x18
+#define BMA150_OFFS_GAIN_T_REG	0x19
+#define BMA150_OFFSET_X_REG		0x1a
+#define BMA150_OFFSET_Y_REG		0x1b
+#define BMA150_OFFSET_Z_REG		0x1c
+#define BMA150_OFFSET_T_REG		0x1d
+
+#define BMA150_CHIP_ID__POS		0
+#define BMA150_CHIP_ID__MSK		0x07
+#define BMA150_CHIP_ID__LEN		3
+#define BMA150_CHIP_ID__REG		BMA150_CHIP_ID_REG
+
+/* DATA REGISTERS */
+
+#define BMA150_NEW_DATA_X__POS		0
+#define BMA150_NEW_DATA_X__LEN		1
+#define BMA150_NEW_DATA_X__MSK		0x01
+#define BMA150_NEW_DATA_X__REG		BMA150_X_AXIS_LSB_REG
+
+#define BMA150_ACC_X_LSB__POS		6
+#define BMA150_ACC_X_LSB__LEN		2
+#define BMA150_ACC_X_LSB__MSK		0xC0
+#define BMA150_ACC_X_LSB__REG		BMA150_X_AXIS_LSB_REG
+
+#define BMA150_ACC_X_MSB__POS		0
+#define BMA150_ACC_X_MSB__LEN		8
+#define BMA150_ACC_X_MSB__MSK		0xFF
+#define BMA150_ACC_X_MSB__REG		BMA150_X_AXIS_MSB_REG
+
+#define BMA150_ACC_Y_LSB__POS		6
+#define BMA150_ACC_Y_LSB__LEN		2
+#define BMA150_ACC_Y_LSB__MSK		0xC0
+#define BMA150_ACC_Y_LSB__REG		BMA150_Y_AXIS_LSB_REG
+
+#define BMA150_ACC_Y_MSB__POS		0
+#define BMA150_ACC_Y_MSB__LEN		8
+#define BMA150_ACC_Y_MSB__MSK		0xFF
+#define BMA150_ACC_Y_MSB__REG		BMA150_Y_AXIS_MSB_REG
+
+#define BMA150_ACC_Z_LSB__POS		6
+#define BMA150_ACC_Z_LSB__LEN		2
+#define BMA150_ACC_Z_LSB__MSK		0xC0
+#define BMA150_ACC_Z_LSB__REG		BMA150_Z_AXIS_LSB_REG
+
+#define BMA150_ACC_Z_MSB__POS		0
+#define BMA150_ACC_Z_MSB__LEN		8
+#define BMA150_ACC_Z_MSB__MSK		0xFF
+#define BMA150_ACC_Z_MSB__REG		BMA150_Z_AXIS_MSB_REG
+
+/* CONTROL BITS */
+
+#define BMA150_SLEEP__POS			0
+#define BMA150_SLEEP__LEN			1
+#define BMA150_SLEEP__MSK			0x01
+#define BMA150_SLEEP__REG			BMA150_CTRL_REG
+
+#define BMA150_SOFT_RESET__POS		1
+#define BMA150_SOFT_RESET__LEN		1
+#define BMA150_SOFT_RESET__MSK		0x02
+#define BMA150_SOFT_RESET__REG		BMA150_CTRL_REG
+
+#define BMA150_EE_W__POS			4
+#define BMA150_EE_W__LEN			1
+#define BMA150_EE_W__MSK			0x10
+#define BMA150_EE_W__REG			BMA150_CTRL_REG
+
+#define BMA150_UPDATE_IMAGE__POS	5
+#define BMA150_UPDATE_IMAGE__LEN	1
+#define BMA150_UPDATE_IMAGE__MSK	0x20
+#define BMA150_UPDATE_IMAGE__REG	BMA150_CTRL_REG
+
+#define BMA150_RESET_INT__POS		6
+#define BMA150_RESET_INT__LEN		1
+#define BMA150_RESET_INT__MSK		0x40
+#define BMA150_RESET_INT__REG		BMA150_CTRL_REG
+
+/* BANDWIDTH dependend definitions */
+
+#define BMA150_BANDWIDTH__POS				0
+#define BMA150_BANDWIDTH__LEN				3
+#define BMA150_BANDWIDTH__MSK				0x07
+#define BMA150_BANDWIDTH__REG				BMA150_RANGE_BWIDTH_REG
+
+/* RANGE */
+
+#define BMA150_RANGE__POS				3
+#define BMA150_RANGE__LEN				2
+#define BMA150_RANGE__MSK				0x18
+#define BMA150_RANGE__REG				BMA150_RANGE_BWIDTH_REG
+
+/* WAKE UP */
+
+#define BMA150_WAKE_UP__POS			0
+#define BMA150_WAKE_UP__LEN			1
+#define BMA150_WAKE_UP__MSK			0x01
+#define BMA150_WAKE_UP__REG			BMA150_CONF2_REG
+
+#define BMA150_WAKE_UP_PAUSE__POS		1
+#define BMA150_WAKE_UP_PAUSE__LEN		2
+#define BMA150_WAKE_UP_PAUSE__MSK		0x06
+#define BMA150_WAKE_UP_PAUSE__REG		BMA150_CONF2_REG
+
+#define BMA150_GET_BITSLICE(regvar, bitname)\
+	((regvar & bitname##__MSK) >> bitname##__POS)
+
+
+#define BMA150_SET_BITSLICE(regvar, bitname, val)\
+	((regvar & ~bitname##__MSK) | ((val<<bitname##__POS)&bitname##__MSK))
+
+/* range and bandwidth */
+
+#define BMA150_RANGE_2G			0
+#define BMA150_RANGE_4G			1
+#define BMA150_RANGE_8G			2
 
 #define BMA150_BW_25HZ		0
 #define BMA150_BW_50HZ		1
@@ -54,608 +178,583 @@
 #define BMA150_BW_750HZ		5
 #define BMA150_BW_1500HZ	6
 
-#define BMA150_RANGE_2G		0
-#define BMA150_RANGE_4G		1
-#define BMA150_RANGE_8G		2
+/* mode settings */
 
-#define BMA150_MODE_NORMAL	0
-#define BMA150_MODE_SLEEP	2
-#define BMA150_MODE_WAKE_UP	3
+#define BMA150_MODE_NORMAL      0
+#define BMA150_MODE_SLEEP       2
+#define BMA150_MODE_WAKE_UP     3
 
-/* Data register addresses */
-#define BMA150_DATA_0_REG	0x00
-#define BMA150_DATA_1_REG	0x01
-#define BMA150_DATA_2_REG	0x02
-
-/* Control register addresses */
-#define BMA150_CTRL_0_REG	0x0A
-#define BMA150_CTRL_1_REG	0x0B
-#define BMA150_CTRL_2_REG	0x14
-#define BMA150_CTRL_3_REG	0x15
-
-/* Configuration/Setting register addresses */
-#define BMA150_CFG_0_REG	0x0C
-#define BMA150_CFG_1_REG	0x0D
-#define BMA150_CFG_2_REG	0x0E
-#define BMA150_CFG_3_REG	0x0F
-#define BMA150_CFG_4_REG	0x10
-#define BMA150_CFG_5_REG	0x11
-
-#define BMA150_CHIP_ID		2
-#define BMA150_CHIP_ID_REG	BMA150_DATA_0_REG
-
-#define BMA150_ACC_X_LSB_REG	BMA150_DATA_2_REG
-
-#define BMA150_SLEEP_POS	0
-#define BMA150_SLEEP_MSK	0x01
-#define BMA150_SLEEP_REG	BMA150_CTRL_0_REG
-
-#define BMA150_BANDWIDTH_POS	0
-#define BMA150_BANDWIDTH_MSK	0x07
-#define BMA150_BANDWIDTH_REG	BMA150_CTRL_2_REG
-
-#define BMA150_RANGE_POS	3
-#define BMA150_RANGE_MSK	0x18
-#define BMA150_RANGE_REG	BMA150_CTRL_2_REG
-
-#define BMA150_WAKE_UP_POS	0
-#define BMA150_WAKE_UP_MSK	0x01
-#define BMA150_WAKE_UP_REG	BMA150_CTRL_3_REG
-
-#define BMA150_SW_RES_POS	1
-#define BMA150_SW_RES_MSK	0x02
-#define BMA150_SW_RES_REG	BMA150_CTRL_0_REG
-
-/* Any-motion interrupt register fields */
-#define BMA150_ANY_MOTION_EN_POS	6
-#define BMA150_ANY_MOTION_EN_MSK	0x40
-#define BMA150_ANY_MOTION_EN_REG	BMA150_CTRL_1_REG
-
-#define BMA150_ANY_MOTION_DUR_POS	6
-#define BMA150_ANY_MOTION_DUR_MSK	0xC0
-#define BMA150_ANY_MOTION_DUR_REG	BMA150_CFG_5_REG
-
-#define BMA150_ANY_MOTION_THRES_REG	BMA150_CFG_4_REG
-
-/* Advanced interrupt register fields */
-#define BMA150_ADV_INT_EN_POS		6
-#define BMA150_ADV_INT_EN_MSK		0x40
-#define BMA150_ADV_INT_EN_REG		BMA150_CTRL_3_REG
-
-/* High-G interrupt register fields */
-#define BMA150_HIGH_G_EN_POS		1
-#define BMA150_HIGH_G_EN_MSK		0x02
-#define BMA150_HIGH_G_EN_REG		BMA150_CTRL_1_REG
-
-#define BMA150_HIGH_G_HYST_POS		3
-#define BMA150_HIGH_G_HYST_MSK		0x38
-#define BMA150_HIGH_G_HYST_REG		BMA150_CFG_5_REG
-
-#define BMA150_HIGH_G_DUR_REG		BMA150_CFG_3_REG
-#define BMA150_HIGH_G_THRES_REG		BMA150_CFG_2_REG
-
-/* Low-G interrupt register fields */
-#define BMA150_LOW_G_EN_POS		0
-#define BMA150_LOW_G_EN_MSK		0x01
-#define BMA150_LOW_G_EN_REG		BMA150_CTRL_1_REG
-
-#define BMA150_LOW_G_HYST_POS		0
-#define BMA150_LOW_G_HYST_MSK		0x07
-#define BMA150_LOW_G_HYST_REG		BMA150_CFG_5_REG
-
-#define BMA150_LOW_G_DUR_REG		BMA150_CFG_1_REG
-#define BMA150_LOW_G_THRES_REG		BMA150_CFG_0_REG
+struct bma150acc{
+	s16	x,
+		y,
+		z;
+} ;
 
 struct bma150_data {
-	struct i2c_client *client;
-	struct input_polled_dev *input_polled;
+	struct i2c_client *bma150_client;
+	struct bma150_platform_data *platform_data;
+	int IRQ;
+	atomic_t delay;
+	unsigned char mode;
 	struct input_dev *input;
-	u8 mode;
+	struct bma150acc value;
+	struct mutex value_mutex;
+	struct mutex mode_mutex;
+	struct delayed_work work;
+	struct work_struct irq_work;
 };
 
-/*
- * The settings for the given range, bandwidth and interrupt features
- * are stated and verified by Bosch Sensortec where they are configured
- * to provide a generic sensitivity performance.
- */
-static struct bma150_cfg default_cfg __devinitdata = {
-	.any_motion_int = 1,
-	.hg_int = 1,
-	.lg_int = 1,
-	.any_motion_dur = 0,
-	.any_motion_thres = 0,
-	.hg_hyst = 0,
-	.hg_dur = 150,
-	.hg_thres = 160,
-	.lg_hyst = 0,
-	.lg_dur = 150,
-	.lg_thres = 20,
-	.range = BMA150_RANGE_2G,
-	.bandwidth = BMA150_BW_50HZ
-};
-
-static int bma150_write_byte(struct i2c_client *client, u8 reg, u8 val)
+static int bma150_smbus_read_byte(struct i2c_client *client,
+		unsigned char reg_addr, unsigned char *data)
 {
-	s32 ret;
+	s32 dummy;
+	dummy = i2c_smbus_read_byte_data(client, reg_addr);
+	if (dummy < 0)
+		return -EPERM;
+	*data = dummy & 0x000000ff;
 
-	/* As per specification, disable irq in between register writes */
-	if (client->irq)
-		disable_irq_nosync(client->irq);
-
-	ret = i2c_smbus_write_byte_data(client, reg, val);
-
-	if (client->irq)
-		enable_irq(client->irq);
-
-	return ret;
-}
-
-static int bma150_set_reg_bits(struct i2c_client *client,
-					int val, int shift, u8 mask, u8 reg)
-{
-	int data;
-
-	data = i2c_smbus_read_byte_data(client, reg);
-	if (data < 0)
-		return data;
-
-	data = (data & ~mask) | ((val << shift) & mask);
-	return bma150_write_byte(client, reg, data);
-}
-
-static int bma150_set_mode(struct bma150_data *bma150, u8 mode)
-{
-	int error;
-
-	error = bma150_set_reg_bits(bma150->client, mode, BMA150_WAKE_UP_POS,
-				BMA150_WAKE_UP_MSK, BMA150_WAKE_UP_REG);
-	if (error)
-		return error;
-
-	error = bma150_set_reg_bits(bma150->client, mode, BMA150_SLEEP_POS,
-				BMA150_SLEEP_MSK, BMA150_SLEEP_REG);
-	if (error)
-		return error;
-
-	if (mode == BMA150_MODE_NORMAL)
-		msleep(2);
-
-	bma150->mode = mode;
 	return 0;
 }
 
-static int __devinit bma150_soft_reset(struct bma150_data *bma150)
+static int bma150_smbus_write_byte(struct i2c_client *client,
+		unsigned char reg_addr, unsigned char *data)
 {
-	int error;
-
-	error = bma150_set_reg_bits(bma150->client, 1, BMA150_SW_RES_POS,
-				BMA150_SW_RES_MSK, BMA150_SW_RES_REG);
-	if (error)
-		return error;
-
-	msleep(2);
+	s32 dummy;
+	dummy = i2c_smbus_write_byte_data(client, reg_addr, *data);
+	if (dummy < 0)
+		return -EPERM;
 	return 0;
 }
 
-static int __devinit bma150_set_range(struct bma150_data *bma150, u8 range)
+static int bma150_smbus_read_byte_block(struct i2c_client *client,
+		unsigned char reg_addr, unsigned char *data, unsigned char len)
 {
-	return bma150_set_reg_bits(bma150->client, range, BMA150_RANGE_POS,
-				BMA150_RANGE_MSK, BMA150_RANGE_REG);
+	s32 dummy;
+	dummy = i2c_smbus_read_i2c_block_data(client, reg_addr, len, data);
+	if (dummy < 0)
+		return -EPERM;
+	return 0;
 }
 
-static int __devinit bma150_set_bandwidth(struct bma150_data *bma150, u8 bw)
+static int bma150_set_mode(struct i2c_client *client, unsigned char Mode)
 {
-	return bma150_set_reg_bits(bma150->client, bw, BMA150_BANDWIDTH_POS,
-				BMA150_BANDWIDTH_MSK, BMA150_BANDWIDTH_REG);
-}
+	int comres = 0;
+	unsigned char data1 = 0, data2 = 0;
+	struct bma150_data *bma150 = i2c_get_clientdata(client);
 
-static int __devinit bma150_set_low_g_interrupt(struct bma150_data *bma150,
-					u8 enable, u8 hyst, u8 dur, u8 thres)
-{
-	int error;
+	if (client == NULL) {
+		comres = -1;
+	} else{
+		if (Mode < 4 && Mode != 1) {
 
-	error = bma150_set_reg_bits(bma150->client, hyst,
-				BMA150_LOW_G_HYST_POS, BMA150_LOW_G_HYST_MSK,
-				BMA150_LOW_G_HYST_REG);
-	if (error)
-		return error;
+			comres = bma150_smbus_read_byte(client,
+						BMA150_WAKE_UP__REG, &data1);
+			data1 = BMA150_SET_BITSLICE(data1,
+						BMA150_WAKE_UP, Mode);
+			comres += bma150_smbus_read_byte(client,
+						BMA150_SLEEP__REG, &data2);
+			data2 = BMA150_SET_BITSLICE(data2,
+						BMA150_SLEEP, (Mode>>1));
+			comres += bma150_smbus_write_byte(client,
+						BMA150_WAKE_UP__REG, &data1);
+			comres += bma150_smbus_write_byte(client,
+						BMA150_SLEEP__REG, &data2);
+			mutex_lock(&bma150->mode_mutex);
+			bma150->mode = (unsigned char) Mode;
+			mutex_unlock(&bma150->mode_mutex);
 
-	error = bma150_write_byte(bma150->client, BMA150_LOW_G_DUR_REG, dur);
-	if (error)
-		return error;
+		} else{
+			comres = -1;
+		}
+	}
 
-	error = bma150_write_byte(bma150->client, BMA150_LOW_G_THRES_REG, thres);
-	if (error)
-		return error;
-
-	return bma150_set_reg_bits(bma150->client, !!enable,
-				BMA150_LOW_G_EN_POS, BMA150_LOW_G_EN_MSK,
-				BMA150_LOW_G_EN_REG);
-}
-
-static int __devinit bma150_set_high_g_interrupt(struct bma150_data *bma150,
-					u8 enable, u8 hyst, u8 dur, u8 thres)
-{
-	int error;
-
-	error = bma150_set_reg_bits(bma150->client, hyst,
-				BMA150_HIGH_G_HYST_POS, BMA150_HIGH_G_HYST_MSK,
-				BMA150_HIGH_G_HYST_REG);
-	if (error)
-		return error;
-
-	error = bma150_write_byte(bma150->client,
-				BMA150_HIGH_G_DUR_REG, dur);
-	if (error)
-		return error;
-
-	error = bma150_write_byte(bma150->client,
-				BMA150_HIGH_G_THRES_REG, thres);
-	if (error)
-		return error;
-
-	return bma150_set_reg_bits(bma150->client, !!enable,
-				BMA150_HIGH_G_EN_POS, BMA150_HIGH_G_EN_MSK,
-				BMA150_HIGH_G_EN_REG);
+	return comres;
 }
 
 
-static int __devinit bma150_set_any_motion_interrupt(struct bma150_data *bma150,
-						u8 enable, u8 dur, u8 thres)
+static int bma150_set_range(struct i2c_client *client, unsigned char Range)
 {
-	int error;
+	int comres = 0;
+	unsigned char data = 0;
 
-	error = bma150_set_reg_bits(bma150->client, dur,
-				BMA150_ANY_MOTION_DUR_POS,
-				BMA150_ANY_MOTION_DUR_MSK,
-				BMA150_ANY_MOTION_DUR_REG);
-	if (error)
-		return error;
+	if (client == NULL) {
+		comres = -1;
+	} else{
+		if (Range < 3) {
 
-	error = bma150_write_byte(bma150->client,
-				BMA150_ANY_MOTION_THRES_REG, thres);
-	if (error)
-		return error;
+			comres = bma150_smbus_read_byte(client,
+						BMA150_RANGE__REG, &data);
+			data = BMA150_SET_BITSLICE(data, BMA150_RANGE, Range);
+			comres += bma150_smbus_write_byte(client,
+						BMA150_RANGE__REG, &data);
 
-	error = bma150_set_reg_bits(bma150->client, !!enable,
-				BMA150_ADV_INT_EN_POS, BMA150_ADV_INT_EN_MSK,
-				BMA150_ADV_INT_EN_REG);
-	if (error)
-		return error;
+		} else{
+			comres = -1;
+		}
+	}
 
-	return bma150_set_reg_bits(bma150->client, !!enable,
-				BMA150_ANY_MOTION_EN_POS,
-				BMA150_ANY_MOTION_EN_MSK,
-				BMA150_ANY_MOTION_EN_REG);
+	return comres;
 }
 
-static void bma150_report_xyz(struct bma150_data *bma150)
+static int bma150_get_range(struct i2c_client *client, unsigned char *Range)
 {
-	u8 data[BMA150_XYZ_DATA_SIZE];
-	s16 x, y, z;
-	s32 ret;
+	int comres = 0;
+	unsigned char data;
 
-	ret = i2c_smbus_read_i2c_block_data(bma150->client,
-			BMA150_ACC_X_LSB_REG, BMA150_XYZ_DATA_SIZE, data);
-	if (ret != BMA150_XYZ_DATA_SIZE)
-		return;
+	if (client == NULL) {
+		comres = -1;
+	} else{
+		comres = bma150_smbus_read_byte(client,
+						BMA150_RANGE__REG, &data);
 
-	x = ((0xc0 & data[0]) >> 6) | (data[1] << 2);
-	y = ((0xc0 & data[2]) >> 6) | (data[3] << 2);
-	z = ((0xc0 & data[4]) >> 6) | (data[5] << 2);
+		*Range = BMA150_GET_BITSLICE(data, BMA150_RANGE);
 
-	/* sign extension */
-	x = (s16) (x << 6) >> 6;
-	y = (s16) (y << 6) >> 6;
-	z = (s16) (z << 6) >> 6;
+	}
 
-	input_report_abs(bma150->input, ABS_X, x);
-	input_report_abs(bma150->input, ABS_Y, y);
-	input_report_abs(bma150->input, ABS_Z, z);
+	return comres;
+}
+
+
+
+static int bma150_set_bandwidth(struct i2c_client *client, unsigned char BW)
+{
+	int comres = 0;
+	unsigned char data = 0;
+
+	if (client == NULL) {
+		comres = -1;
+	} else{
+		if (BW < 8) {
+			comres = bma150_smbus_read_byte(client,
+						BMA150_BANDWIDTH__REG, &data);
+			data = BMA150_SET_BITSLICE(data, BMA150_BANDWIDTH, BW);
+			comres += bma150_smbus_write_byte(client,
+						BMA150_BANDWIDTH__REG, &data);
+
+		} else{
+			comres = -1;
+		}
+	}
+
+	return comres;
+}
+
+static int bma150_get_bandwidth(struct i2c_client *client, unsigned char *BW)
+{
+	int comres = 0;
+	unsigned char data;
+
+	if (client == NULL) {
+		comres = -1;
+	} else{
+
+
+		comres = bma150_smbus_read_byte(client,
+						BMA150_BANDWIDTH__REG, &data);
+
+		*BW = BMA150_GET_BITSLICE(data, BMA150_BANDWIDTH);
+
+
+	}
+
+	return comres;
+}
+
+static int bma150_read_accel_xyz(struct i2c_client *client,
+		struct bma150acc *acc)
+{
+	int comres;
+	unsigned char data[6];
+	if (client == NULL) {
+		comres = -1;
+	} else{
+
+
+		comres = bma150_smbus_read_byte_block(client,
+					BMA150_ACC_X_LSB__REG, &data[0], 6);
+
+		acc->x = BMA150_GET_BITSLICE(data[0], BMA150_ACC_X_LSB) |
+			(BMA150_GET_BITSLICE(data[1], BMA150_ACC_X_MSB)<<
+							BMA150_ACC_X_LSB__LEN);
+		acc->x = acc->x << (sizeof(short)*8-(BMA150_ACC_X_LSB__LEN+
+							BMA150_ACC_X_MSB__LEN));
+		acc->x = acc->x >> (sizeof(short)*8-(BMA150_ACC_X_LSB__LEN+
+							BMA150_ACC_X_MSB__LEN));
+
+		acc->y = BMA150_GET_BITSLICE(data[2], BMA150_ACC_Y_LSB) |
+			(BMA150_GET_BITSLICE(data[3], BMA150_ACC_Y_MSB)<<
+							BMA150_ACC_Y_LSB__LEN);
+		acc->y = acc->y << (sizeof(short)*8-(BMA150_ACC_Y_LSB__LEN +
+							BMA150_ACC_Y_MSB__LEN));
+		acc->y = acc->y >> (sizeof(short)*8-(BMA150_ACC_Y_LSB__LEN +
+							BMA150_ACC_Y_MSB__LEN));
+
+
+		acc->z = BMA150_GET_BITSLICE(data[4], BMA150_ACC_Z_LSB);
+		acc->z |= (BMA150_GET_BITSLICE(data[5], BMA150_ACC_Z_MSB)<<
+							BMA150_ACC_Z_LSB__LEN);
+		acc->z = acc->z << (sizeof(short)*8-(BMA150_ACC_Z_LSB__LEN+
+							BMA150_ACC_Z_MSB__LEN));
+		acc->z = acc->z >> (sizeof(short)*8-(BMA150_ACC_Z_LSB__LEN+
+							BMA150_ACC_Z_MSB__LEN));
+
+	}
+
+	return comres;
+}
+
+static void bma150_work_func(struct work_struct *work)
+{
+	struct bma150_data *bma150 = container_of((struct delayed_work *)work,
+			struct bma150_data, work);
+	static struct bma150acc acc;
+	unsigned long delay = msecs_to_jiffies(atomic_read(&bma150->delay));
+
+
+
+	bma150_read_accel_xyz(bma150->bma150_client, &acc);
+	input_report_abs(bma150->input, ABS_X, acc.x);
+	input_report_abs(bma150->input, ABS_Y, acc.y);
+	input_report_abs(bma150->input, ABS_Z, acc.z);
 	input_sync(bma150->input);
+	mutex_lock(&bma150->value_mutex);
+	bma150->value = acc;
+	mutex_unlock(&bma150->value_mutex);
+	schedule_delayed_work(&bma150->work, delay);
 }
 
-static irqreturn_t bma150_irq_thread(int irq, void *dev)
+static ssize_t bma150_mode_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
 {
-	bma150_report_xyz(dev);
+	unsigned char data;
+	struct i2c_client *client = to_i2c_client(dev);
+	struct bma150_data *bma150 = i2c_get_clientdata(client);
 
-	return IRQ_HANDLED;
+	mutex_lock(&bma150->mode_mutex);
+	data = bma150->mode;
+	mutex_unlock(&bma150->mode_mutex);
+
+	return sprintf(buf, "%d\n", data);
 }
 
-static void bma150_poll(struct input_polled_dev *dev)
+static ssize_t bma150_mode_store(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t count)
 {
-	bma150_report_xyz(dev->private);
-}
-
-static int bma150_open(struct bma150_data *bma150)
-{
+	unsigned long data;
 	int error;
+	struct i2c_client *client = to_i2c_client(dev);
+	struct bma150_data *bma150 = i2c_get_clientdata(client);
 
-	error = pm_runtime_get_sync(&bma150->client->dev);
-	if (error && error != -ENOSYS)
+	error = strict_strtoul(buf, 10, &data);
+	if (error)
 		return error;
+	if (bma150_set_mode(bma150->bma150_client, (unsigned char) data) < 0)
+		return -EINVAL;
 
-	/*
-	 * See if runtime PM woke up the device. If runtime PM
-	 * is disabled we need to do it ourselves.
-	 */
-	if (bma150->mode != BMA150_MODE_NORMAL) {
-		error = bma150_set_mode(bma150, BMA150_MODE_NORMAL);
-		if (error)
-			return error;
-	}
 
-	return 0;
+	return count;
+}
+static ssize_t bma150_range_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	unsigned char data;
+	struct i2c_client *client = to_i2c_client(dev);
+	struct bma150_data *bma150 = i2c_get_clientdata(client);
+
+	if (bma150_get_range(bma150->bma150_client, &data) < 0)
+		return sprintf(buf, "Read error\n");
+
+	return sprintf(buf, "%d\n", data);
 }
 
-static void bma150_close(struct bma150_data *bma150)
+static ssize_t bma150_range_store(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t count)
 {
-	pm_runtime_put_sync(&bma150->client->dev);
+	unsigned long data;
+	int error;
+	struct i2c_client *client = to_i2c_client(dev);
+	struct bma150_data *bma150 = i2c_get_clientdata(client);
 
-	if (bma150->mode != BMA150_MODE_SLEEP)
-		bma150_set_mode(bma150, BMA150_MODE_SLEEP);
+	error = strict_strtoul(buf, 10, &data);
+	if (error)
+		return error;
+	if (bma150_set_range(bma150->bma150_client, (unsigned char) data) < 0)
+		return -EINVAL;
+
+	return count;
 }
 
-static int bma150_irq_open(struct input_dev *input)
+static ssize_t bma150_bandwidth_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
 {
+	unsigned char data;
+	struct i2c_client *client = to_i2c_client(dev);
+	struct bma150_data *bma150 = i2c_get_clientdata(client);
+
+	if (bma150_get_bandwidth(bma150->bma150_client, &data) < 0)
+		return sprintf(buf, "Read error\n");
+
+	return sprintf(buf, "%d\n", data);
+
+}
+
+static ssize_t bma150_bandwidth_store(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	unsigned long data;
+	int error;
+	struct i2c_client *client = to_i2c_client(dev);
+	struct bma150_data *bma150 = i2c_get_clientdata(client);
+
+	error = strict_strtoul(buf, 10, &data);
+	if (error)
+		return error;
+	if (bma150_set_bandwidth(bma150->bma150_client,
+				(unsigned char) data) < 0)
+		return -EINVAL;
+
+	return count;
+}
+
+static ssize_t bma150_value_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct input_dev *input = to_input_dev(dev);
 	struct bma150_data *bma150 = input_get_drvdata(input);
+	struct bma150acc acc_value;
 
-	return bma150_open(bma150);
+	mutex_lock(&bma150->value_mutex);
+	acc_value = bma150->value;
+	mutex_unlock(&bma150->value_mutex);
+
+	return sprintf(buf, "%d %d %d\n", acc_value.x, acc_value.y,
+			acc_value.z);
 }
 
-static void bma150_irq_close(struct input_dev *input)
-{
-	struct bma150_data *bma150 = input_get_drvdata(input);
 
-	bma150_close(bma150);
+
+static ssize_t bma150_delay_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct bma150_data *bma150 = i2c_get_clientdata(client);
+
+	return sprintf(buf, "%d\n", atomic_read(&bma150->delay));
+
 }
 
-static void bma150_poll_open(struct input_polled_dev *ipoll_dev)
+static ssize_t bma150_delay_store(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t count)
 {
-	struct bma150_data *bma150 = ipoll_dev->private;
-
-	bma150_open(bma150);
-}
-
-static void bma150_poll_close(struct input_polled_dev *ipoll_dev)
-{
-	struct bma150_data *bma150 = ipoll_dev->private;
-
-	bma150_close(bma150);
-}
-
-static int __devinit bma150_initialize(struct bma150_data *bma150,
-				       const struct bma150_cfg *cfg)
-{
+	unsigned long data;
 	int error;
+	struct i2c_client *client = to_i2c_client(dev);
+	struct bma150_data *bma150 = i2c_get_clientdata(client);
 
-	error = bma150_soft_reset(bma150);
+	error = strict_strtoul(buf, 10, &data);
 	if (error)
 		return error;
+	if (data > BMA150_MAX_DELAY)
+		data = BMA150_MAX_DELAY;
+	atomic_set(&bma150->delay, (unsigned int) data);
 
-	error = bma150_set_bandwidth(bma150, cfg->bandwidth);
-	if (error)
-		return error;
-
-	error = bma150_set_range(bma150, cfg->range);
-	if (error)
-		return error;
-
-	if (bma150->client->irq) {
-		error = bma150_set_any_motion_interrupt(bma150,
-					cfg->any_motion_int,
-					cfg->any_motion_dur,
-					cfg->any_motion_thres);
-		if (error)
-			return error;
-
-		error = bma150_set_high_g_interrupt(bma150,
-					cfg->hg_int, cfg->hg_hyst,
-					cfg->hg_dur, cfg->hg_thres);
-		if (error)
-			return error;
-
-		error = bma150_set_low_g_interrupt(bma150,
-					cfg->lg_int, cfg->lg_hyst,
-					cfg->lg_dur, cfg->lg_thres);
-		if (error)
-			return error;
-	}
-
-	return bma150_set_mode(bma150, BMA150_MODE_SLEEP);
+	return count;
 }
 
-static void __devinit bma150_init_input_device(struct bma150_data *bma150,
-						struct input_dev *idev)
+static DEVICE_ATTR(range, S_IRUGO|S_IWUSR|S_IWGRP,
+		bma150_range_show, bma150_range_store);
+static DEVICE_ATTR(bandwidth, S_IRUGO|S_IWUSR|S_IWGRP,
+		bma150_bandwidth_show, bma150_bandwidth_store);
+static DEVICE_ATTR(mode, S_IRUGO|S_IWUSR|S_IWGRP,
+		bma150_mode_show, bma150_mode_store);
+static DEVICE_ATTR(value, S_IRUGO|S_IWUSR|S_IWGRP,
+		bma150_value_show, NULL);
+static DEVICE_ATTR(delay, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+		bma150_delay_show, bma150_delay_store);
+
+static struct attribute *bma150_attributes[] = {
+	&dev_attr_range.attr,
+	&dev_attr_bandwidth.attr,
+	&dev_attr_mode.attr,
+	&dev_attr_value.attr,
+	&dev_attr_delay.attr,
+	NULL
+};
+
+static struct attribute_group bma150_attribute_group = {
+	.attrs = bma150_attributes
+};
+
+static int bma150_detect(struct i2c_client *client,
+			  struct i2c_board_info *info)
 {
-	idev->name = BMA150_DRIVER;
-	idev->phys = BMA150_DRIVER "/input0";
-	idev->id.bustype = BUS_I2C;
-	idev->dev.parent = &bma150->client->dev;
+	struct i2c_adapter *adapter = client->adapter;
 
-	idev->evbit[0] = BIT_MASK(EV_ABS);
-	input_set_abs_params(idev, ABS_X, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
-	input_set_abs_params(idev, ABS_Y, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
-	input_set_abs_params(idev, ABS_Z, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
-}
+	if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
+		return -ENODEV;
 
-static int __devinit bma150_register_input_device(struct bma150_data *bma150)
-{
-	struct input_dev *idev;
-	int error;
-
-	idev = input_allocate_device();
-	if (!idev)
-		return -ENOMEM;
-
-	bma150_init_input_device(bma150, idev);
-
-	idev->open = bma150_irq_open;
-	idev->close = bma150_irq_close;
-	input_set_drvdata(idev, bma150);
-
-	error = input_register_device(idev);
-	if (error) {
-		input_free_device(idev);
-		return error;
-	}
-
-	bma150->input = idev;
-	return 0;
-}
-
-static int __devinit bma150_register_polled_device(struct bma150_data *bma150)
-{
-	struct input_polled_dev *ipoll_dev;
-	int error;
-
-	ipoll_dev = input_allocate_polled_device();
-	if (!ipoll_dev)
-		return -ENOMEM;
-
-	ipoll_dev->private = bma150;
-	ipoll_dev->open = bma150_poll_open;
-	ipoll_dev->close = bma150_poll_close;
-	ipoll_dev->poll = bma150_poll;
-	ipoll_dev->poll_interval = BMA150_POLL_INTERVAL;
-	ipoll_dev->poll_interval_min = BMA150_POLL_MIN;
-	ipoll_dev->poll_interval_max = BMA150_POLL_MAX;
-
-	bma150_init_input_device(bma150, ipoll_dev->input);
-
-	error = input_register_polled_device(ipoll_dev);
-	if (error) {
-		input_free_polled_device(ipoll_dev);
-		return error;
-	}
-
-	bma150->input_polled = ipoll_dev;
-	bma150->input = ipoll_dev->input;
+	strlcpy(info->type, SENSOR_NAME, I2C_NAME_SIZE);
 
 	return 0;
 }
 
-static int __devinit bma150_probe(struct i2c_client *client,
-				  const struct i2c_device_id *id)
+static int bma150_input_init(struct bma150_data *bma150)
 {
-	const struct bma150_platform_data *pdata = client->dev.platform_data;
-	const struct bma150_cfg *cfg;
-	struct bma150_data *bma150;
-	int chip_id;
-	int error;
+	struct input_dev *dev;
+	int err;
+
+	dev = input_allocate_device();
+	if (!dev)
+		return -ENOMEM;
+	dev->name = SENSOR_NAME;
+	dev->id.bustype = BUS_I2C;
+
+	input_set_capability(dev, EV_ABS, ABS_MISC);
+	input_set_abs_params(dev, ABS_X, ABSMIN_2G, ABSMAX_2G, 0, 0);
+	input_set_abs_params(dev, ABS_Y, ABSMIN_2G, ABSMAX_2G, 0, 0);
+	input_set_abs_params(dev, ABS_Z, ABSMIN_2G, ABSMAX_2G, 0, 0);
+	input_set_drvdata(dev, bma150);
+
+	err = input_register_device(dev);
+	if (err < 0) {
+		input_free_device(dev);
+		return err;
+	}
+	bma150->input = dev;
+
+	return 0;
+}
+
+static void bma150_input_delete(struct bma150_data *bma150)
+{
+	struct input_dev *dev = bma150->input;
+
+	input_unregister_device(dev);
+	input_free_device(dev);
+}
+
+static int bma150_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	int err = 0;
+	int tempvalue;
+	struct bma150_data *data;
 
 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
-		dev_err(&client->dev, "i2c_check_functionality error\n");
-		return -EIO;
+		printk(KERN_INFO "i2c_check_functionality error\n");
+		goto exit;
+	}
+	data = kzalloc(sizeof(struct bma150_data), GFP_KERNEL);
+	if (!data) {
+		err = -ENOMEM;
+		goto exit;
 	}
 
-	chip_id = i2c_smbus_read_byte_data(client, BMA150_CHIP_ID_REG);
-	if (chip_id != BMA150_CHIP_ID) {
-		dev_err(&client->dev, "BMA150 chip id error: %d\n", chip_id);
-		return -EINVAL;
+	i2c_set_clientdata(client, data);
+	data->platform_data = client->dev.platform_data;
+
+	if (data->platform_data->power_on)
+		data->platform_data->power_on();
+	else
+		printk(KERN_ERR "power_on function not defined!!\n");
+
+	tempvalue = 0;
+	tempvalue = i2c_smbus_read_word_data(client, BMA150_CHIP_ID_REG);
+
+	if ((tempvalue&0x00FF) == BMA150_CHIP_ID) {
+		printk(KERN_INFO "Bosch Sensortec Device detected!\n" \
+				"BMA150 registered I2C driver!\n");
+	} else{
+		printk(KERN_INFO "Bosch Sensortec Device not found" \
+			"i2c error %d\n", tempvalue);
+		err = -1;
+		goto kfree_exit;
 	}
+	i2c_set_clientdata(client, data);
+	data->bma150_client = client;
+	mutex_init(&data->value_mutex);
+	mutex_init(&data->mode_mutex);
+	bma150_set_bandwidth(client, BMA150_BW_SET);
+	bma150_set_range(client, BMA150_RANGE_SET);
 
-	bma150 = kzalloc(sizeof(struct bma150_data), GFP_KERNEL);
-	if (!bma150)
-		return -ENOMEM;
 
-	bma150->client = client;
+	INIT_DELAYED_WORK(&data->work, bma150_work_func);
+	atomic_set(&data->delay, BMA150_MAX_DELAY);
+	err = bma150_input_init(data);
+	if (err < 0)
+		goto kfree_exit;
 
-	if (pdata) {
-		if (pdata->irq_gpio_cfg) {
-			error = pdata->irq_gpio_cfg();
-			if (error) {
-				dev_err(&client->dev,
-					"IRQ GPIO conf. error %d, error %d\n",
-					client->irq, error);
-				goto err_free_mem;
-			}
-		}
-		cfg = &pdata->cfg;
-	} else {
-		cfg = &default_cfg;
-	}
+	err = sysfs_create_group(&data->input->dev.kobj,
+			&bma150_attribute_group);
+	if (err < 0)
+		goto error_sysfs;
 
-	error = bma150_initialize(bma150, cfg);
-	if (error)
-		goto err_free_mem;
-
-	if (client->irq > 0) {
-		error = bma150_register_input_device(bma150);
-		if (error)
-			goto err_free_mem;
-
-		error = request_threaded_irq(client->irq,
-					NULL, bma150_irq_thread,
-					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
-					BMA150_DRIVER, bma150);
-		if (error) {
-			dev_err(&client->dev,
-				"irq request failed %d, error %d\n",
-				client->irq, error);
-			input_unregister_device(bma150->input);
-			goto err_free_mem;
-		}
-	} else {
-		error = bma150_register_polled_device(bma150);
-		if (error)
-			goto err_free_mem;
-	}
-
-	i2c_set_clientdata(client, bma150);
-
-	pm_runtime_enable(&client->dev);
+	schedule_delayed_work(&data->work,
+			msecs_to_jiffies(atomic_read(&data->delay)));
 
 	return 0;
 
-err_free_mem:
-	kfree(bma150);
-	return error;
+error_sysfs:
+	bma150_input_delete(data);
+
+kfree_exit:
+	kfree(data);
+exit:
+	return err;
 }
 
-static int __devexit bma150_remove(struct i2c_client *client)
+static int bma150_suspend(struct i2c_client *client, pm_message_t mesg)
 {
-	struct bma150_data *bma150 = i2c_get_clientdata(client);
+	struct bma150_data *data = i2c_get_clientdata(client);
 
-	pm_runtime_disable(&client->dev);
+	cancel_delayed_work_sync(&data->work);
 
-	if (client->irq > 0) {
-		free_irq(client->irq, bma150);
-		input_unregister_device(bma150->input);
-	} else {
-		input_unregister_polled_device(bma150->input_polled);
-		input_free_polled_device(bma150->input_polled);
-	}
+	bma150_set_mode(client, BMA150_MODE_SLEEP);
 
-	kfree(bma150);
+	if ((data->platform_data) && (data->platform_data->power_off))
+		data->platform_data->power_off();
 
 	return 0;
 }
 
-#ifdef CONFIG_PM
-static int bma150_suspend(struct device *dev)
+static int bma150_resume(struct i2c_client *client)
 {
-	struct i2c_client *client = to_i2c_client(dev);
-	struct bma150_data *bma150 = i2c_get_clientdata(client);
+	struct bma150_data *data = i2c_get_clientdata(client);
 
-	return bma150_set_mode(bma150, BMA150_MODE_SLEEP);
+	if ((data->platform_data) && (data->platform_data->power_on))
+		data->platform_data->power_on();
+
+	bma150_set_mode(client, BMA150_MODE_NORMAL);
+
+	schedule_delayed_work(&data->work,
+		msecs_to_jiffies(atomic_read(&data->delay)));
+
+	return 0;
 }
 
-static int bma150_resume(struct device *dev)
+static int bma150_remove(struct i2c_client *client)
 {
-	struct i2c_client *client = to_i2c_client(dev);
-	struct bma150_data *bma150 = i2c_get_clientdata(client);
+	struct bma150_data *data = i2c_get_clientdata(client);
 
-	return bma150_set_mode(bma150, BMA150_MODE_NORMAL);
+	if (data->platform_data->power_off)
+		data->platform_data->power_off();
+	else
+		printk(KERN_ERR "power_off function not defined!!\n");
+
+	sysfs_remove_group(&data->input->dev.kobj, &bma150_attribute_group);
+	bma150_input_delete(data);
+	free_irq(data->IRQ, data);
+	kfree(data);
+
+	return 0;
 }
-#endif
-
-static UNIVERSAL_DEV_PM_OPS(bma150_pm, bma150_suspend, bma150_resume, NULL);
 
 static const struct i2c_device_id bma150_id[] = {
-	{ "bma150", 0 },
-	{ "smb380", 0 },
-	{ "bma023", 0 },
+	{ SENSOR_NAME, 0 },
 	{ }
 };
 
@@ -664,17 +763,29 @@
 static struct i2c_driver bma150_driver = {
 	.driver = {
 		.owner	= THIS_MODULE,
-		.name	= BMA150_DRIVER,
-		.pm	= &bma150_pm,
+		.name	= SENSOR_NAME,
 	},
-	.class		= I2C_CLASS_HWMON,
+	.class          = I2C_CLASS_HWMON,
 	.id_table	= bma150_id,
 	.probe		= bma150_probe,
-	.remove		= __devexit_p(bma150_remove),
+	.remove		= bma150_remove,
+	.detect		= bma150_detect,
+	.suspend    = bma150_suspend,
+	.resume     = bma150_resume,
 };
 
-module_i2c_driver(bma150_driver);
+static int __init BMA150_init(void)
+{
+	return i2c_add_driver(&bma150_driver);
+}
 
-MODULE_AUTHOR("Albert Zhang <xu.zhang@bosch-sensortec.com>");
+static void __exit BMA150_exit(void)
+{
+	i2c_del_driver(&bma150_driver);
+}
+
 MODULE_DESCRIPTION("BMA150 driver");
-MODULE_LICENSE("GPL");
+
+module_init(BMA150_init);
+module_exit(BMA150_exit);
+
diff --git a/drivers/input/misc/isa1200-ff-memless.c b/drivers/input/misc/isa1200-ff-memless.c
new file mode 100644
index 0000000..f4e2c35
--- /dev/null
+++ b/drivers/input/misc/isa1200-ff-memless.c
@@ -0,0 +1,449 @@
+/*
+ * Copyright (C) 2009 Samsung Electronics
+ * Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/pwm.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/i2c/isa1200.h>
+
+#define ISA1200_HCTRL0			0x30
+#define HCTRL0_MODE_CTRL_BIT		(3)
+#define HCTRL0_OVERDRIVE_HIGH_BIT	(5)
+#define HCTRL0_OVERDRIVE_EN_BIT		(6)
+#define HCTRL0_HAP_EN			(7)
+#define HCTRL0_RESET			0x01
+#define HCTRL1_RESET			0x4B
+
+#define ISA1200_HCTRL1			0x31
+#define HCTRL1_SMART_ENABLE_BIT		(3)
+#define HCTRL1_ERM_BIT			(5)
+#define HCTRL1_EXT_CLK_ENABLE_BIT	(7)
+
+#define ISA1200_HCTRL5			0x35
+#define HCTRL5_VIB_STRT			0xD5
+#define HCTRL5_VIB_STOP			0x6B
+
+#define DIVIDER_128			(128)
+#define DIVIDER_1024			(1024)
+#define DIVIDE_SHIFTER_128		(7)
+
+#define FREQ_22400			(22400)
+#define FREQ_172600			(172600)
+
+#define POR_DELAY_USEC			250
+
+struct isa1200_chip {
+	const struct isa1200_platform_data *pdata;
+	struct i2c_client *client;
+	struct input_dev *input_device;
+	struct pwm_device *pwm;
+	unsigned int period_ns;
+	unsigned int state;
+	struct work_struct work;
+};
+
+static void isa1200_vib_set(struct isa1200_chip *haptic, int enable)
+{
+	int rc;
+
+	if (enable) {
+		if (haptic->pdata->mode_ctrl == PWM_INPUT_MODE) {
+			int period_us = haptic->period_ns / NSEC_PER_USEC;
+			rc = pwm_config(haptic->pwm,
+				(period_us * haptic->pdata->duty) / 100,
+				period_us);
+			if (rc < 0)
+				pr_err("pwm_config fail\n");
+			rc = pwm_enable(haptic->pwm);
+			if (rc < 0)
+				pr_err("pwm_enable fail\n");
+		} else if (haptic->pdata->mode_ctrl == PWM_GEN_MODE) {
+			rc = i2c_smbus_write_byte_data(haptic->client,
+						ISA1200_HCTRL5,
+						HCTRL5_VIB_STRT);
+			if (rc < 0)
+				pr_err("start vibration fail\n");
+		}
+	} else {
+		if (haptic->pdata->mode_ctrl == PWM_INPUT_MODE)
+			pwm_disable(haptic->pwm);
+		else if (haptic->pdata->mode_ctrl == PWM_GEN_MODE) {
+			rc = i2c_smbus_write_byte_data(haptic->client,
+						ISA1200_HCTRL5,
+						HCTRL5_VIB_STOP);
+			if (rc < 0)
+				pr_err("stop vibration fail\n");
+		}
+	}
+}
+
+static int isa1200_setup(struct i2c_client *client)
+{
+	struct isa1200_chip *haptic = i2c_get_clientdata(client);
+	int value, temp, rc;
+
+	gpio_set_value_cansleep(haptic->pdata->hap_en_gpio, 0);
+	udelay(POR_DELAY_USEC);
+	gpio_set_value_cansleep(haptic->pdata->hap_en_gpio, 1);
+
+	value =	(haptic->pdata->smart_en << HCTRL1_SMART_ENABLE_BIT) |
+		(haptic->pdata->is_erm << HCTRL1_ERM_BIT) |
+		(haptic->pdata->ext_clk_en << HCTRL1_EXT_CLK_ENABLE_BIT);
+
+	rc = i2c_smbus_write_byte_data(client, ISA1200_HCTRL1, value);
+	if (rc < 0) {
+		pr_err("i2c write failure\n");
+		return rc;
+	}
+
+	if (haptic->pdata->mode_ctrl == PWM_GEN_MODE) {
+		temp = haptic->pdata->pwm_fd.pwm_div;
+		if (temp < DIVIDER_128 || temp > DIVIDER_1024 ||
+					temp % DIVIDER_128) {
+			pr_err("Invalid divider\n");
+			rc = -EINVAL;
+			goto reset_hctrl1;
+		}
+		value = ((temp >> DIVIDE_SHIFTER_128) - 1);
+	} else if (haptic->pdata->mode_ctrl == PWM_INPUT_MODE) {
+		temp = haptic->pdata->pwm_fd.pwm_freq;
+		if (temp < FREQ_22400 || temp > FREQ_172600 ||
+					temp % FREQ_22400) {
+			pr_err("Invalid frequency\n");
+			rc = -EINVAL;
+			goto reset_hctrl1;
+		}
+		value = ((temp / FREQ_22400) - 1);
+		haptic->period_ns = NSEC_PER_SEC / temp;
+	}
+	value |= (haptic->pdata->mode_ctrl << HCTRL0_MODE_CTRL_BIT) |
+		(haptic->pdata->overdrive_high << HCTRL0_OVERDRIVE_HIGH_BIT) |
+		(haptic->pdata->overdrive_en << HCTRL0_OVERDRIVE_EN_BIT) |
+		(haptic->pdata->chip_en << HCTRL0_HAP_EN);
+
+	rc = i2c_smbus_write_byte_data(client, ISA1200_HCTRL0, value);
+	if (rc < 0) {
+		pr_err("i2c write failure\n");
+		goto reset_hctrl1;
+	}
+
+	return 0;
+
+reset_hctrl1:
+	i2c_smbus_write_byte_data(client, ISA1200_HCTRL1,
+				HCTRL1_RESET);
+	return rc;
+}
+
+static void isa1200_worker(struct work_struct *work)
+{
+	struct isa1200_chip *haptic;
+
+	haptic = container_of(work, struct isa1200_chip, work);
+	isa1200_vib_set(haptic, !!haptic->state);
+}
+
+static int isa1200_play_effect(struct input_dev *dev, void *data,
+				struct ff_effect *effect)
+{
+	struct isa1200_chip *haptic = input_get_drvdata(dev);
+
+	/* support basic vibration */
+	haptic->state = effect->u.rumble.strong_magnitude >> 8;
+	if (!haptic->state)
+		haptic->state = effect->u.rumble.weak_magnitude >> 9;
+
+	schedule_work(&haptic->work);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int isa1200_suspend(struct device *dev)
+{
+	struct isa1200_chip *haptic = dev_get_drvdata(dev);
+	int rc;
+
+	cancel_work_sync(&haptic->work);
+	/* turn-off current vibration */
+	isa1200_vib_set(haptic, 0);
+
+	if (haptic->pdata->power_on) {
+		rc = haptic->pdata->power_on(0);
+		if (rc) {
+			pr_err("power-down failed\n");
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+static int isa1200_resume(struct device *dev)
+{
+	struct isa1200_chip *haptic = dev_get_drvdata(dev);
+	int rc;
+
+	if (haptic->pdata->power_on) {
+		rc = haptic->pdata->power_on(1);
+		if (rc) {
+			pr_err("power-up failed\n");
+			return rc;
+		}
+	}
+
+	isa1200_setup(haptic->client);
+	return 0;
+}
+#else
+#define isa1200_suspend		NULL
+#define isa1200_resume		NULL
+#endif
+
+static int isa1200_open(struct input_dev *dev)
+{
+	struct isa1200_chip *haptic = input_get_drvdata(dev);
+	int rc;
+
+	/* device setup */
+	if (haptic->pdata->dev_setup) {
+		rc = haptic->pdata->dev_setup(true);
+		if (rc < 0) {
+			pr_err("setup failed!\n");
+			return rc;
+		}
+	}
+
+	/* power on */
+	if (haptic->pdata->power_on) {
+		rc = haptic->pdata->power_on(true);
+		if (rc < 0) {
+			pr_err("power failed\n");
+			goto err_setup;
+		}
+	}
+
+	/* request gpio */
+	rc = gpio_is_valid(haptic->pdata->hap_en_gpio);
+	if (rc) {
+		rc = gpio_request(haptic->pdata->hap_en_gpio, "haptic_gpio");
+		if (rc) {
+			pr_err("gpio %d request failed\n",
+					haptic->pdata->hap_en_gpio);
+			goto err_power_on;
+		}
+	} else {
+		pr_err("Invalid gpio %d\n",
+					haptic->pdata->hap_en_gpio);
+		goto err_power_on;
+	}
+
+	rc = gpio_direction_output(haptic->pdata->hap_en_gpio, 0);
+	if (rc) {
+		pr_err("gpio %d set direction failed\n",
+					haptic->pdata->hap_en_gpio);
+		goto err_gpio_free;
+	}
+
+	/* setup registers */
+	rc = isa1200_setup(haptic->client);
+	if (rc < 0) {
+		pr_err("setup fail %d\n", rc);
+		goto err_gpio_free;
+	}
+
+	if (haptic->pdata->mode_ctrl == PWM_INPUT_MODE) {
+		haptic->pwm = pwm_request(haptic->pdata->pwm_ch_id,
+				haptic->client->driver->id_table->name);
+		if (IS_ERR(haptic->pwm)) {
+			pr_err("pwm request failed\n");
+			rc = PTR_ERR(haptic->pwm);
+			goto err_reset_hctrl0;
+		}
+	}
+
+	/* init workqeueue */
+	INIT_WORK(&haptic->work, isa1200_worker);
+	return 0;
+
+err_reset_hctrl0:
+	i2c_smbus_write_byte_data(haptic->client, ISA1200_HCTRL0,
+					HCTRL0_RESET);
+err_gpio_free:
+	gpio_free(haptic->pdata->hap_en_gpio);
+err_power_on:
+	if (haptic->pdata->power_on)
+		haptic->pdata->power_on(0);
+err_setup:
+	if (haptic->pdata->dev_setup)
+		haptic->pdata->dev_setup(false);
+
+	return rc;
+}
+
+static void isa1200_close(struct input_dev *dev)
+{
+	struct isa1200_chip *haptic = input_get_drvdata(dev);
+
+	/* turn-off current vibration */
+	isa1200_vib_set(haptic, 0);
+
+	if (haptic->pdata->mode_ctrl == PWM_INPUT_MODE)
+		pwm_free(haptic->pwm);
+
+	gpio_free(haptic->pdata->hap_en_gpio);
+
+	/* reset hardware registers */
+	i2c_smbus_write_byte_data(haptic->client, ISA1200_HCTRL0,
+				HCTRL0_RESET);
+	i2c_smbus_write_byte_data(haptic->client, ISA1200_HCTRL1,
+				HCTRL1_RESET);
+
+	if (haptic->pdata->dev_setup)
+		haptic->pdata->dev_setup(false);
+
+	/* power-off the chip */
+	if (haptic->pdata->power_on)
+		haptic->pdata->power_on(0);
+}
+
+static int __devinit isa1200_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct isa1200_chip *haptic;
+	int rc;
+
+	if (!i2c_check_functionality(client->adapter,
+			I2C_FUNC_SMBUS_BYTE_DATA)) {
+		pr_err("i2c is not supported\n");
+		return -EIO;
+	}
+
+	if (!client->dev.platform_data) {
+		pr_err("pdata is not avaiable\n");
+		return -EINVAL;
+	}
+
+	haptic = kzalloc(sizeof(struct isa1200_chip), GFP_KERNEL);
+	if (!haptic) {
+		pr_err("no memory\n");
+		return -ENOMEM;
+	}
+
+	haptic->pdata = client->dev.platform_data;
+	haptic->client = client;
+
+	i2c_set_clientdata(client, haptic);
+
+	haptic->input_device = input_allocate_device();
+	if (!haptic->input_device) {
+		pr_err("input device alloc failed\n");
+		rc = -ENOMEM;
+		goto err_mem_alloc;
+	}
+
+	input_set_drvdata(haptic->input_device, haptic);
+	haptic->input_device->name = haptic->pdata->name ? :
+					"isa1200-ff-memless";
+
+	haptic->input_device->dev.parent = &client->dev;
+
+	input_set_capability(haptic->input_device, EV_FF, FF_RUMBLE);
+
+	haptic->input_device->open = isa1200_open;
+	haptic->input_device->close = isa1200_close;
+
+	rc = input_ff_create_memless(haptic->input_device, NULL,
+					isa1200_play_effect);
+	if (rc < 0) {
+		pr_err("unable to register with ff\n");
+		goto err_free_dev;
+	}
+
+	rc = input_register_device(haptic->input_device);
+	if (rc < 0) {
+		pr_err("unable to register input device\n");
+		goto err_ff_destroy;
+	}
+
+	return 0;
+
+err_ff_destroy:
+	input_ff_destroy(haptic->input_device);
+err_free_dev:
+	input_free_device(haptic->input_device);
+err_mem_alloc:
+	kfree(haptic);
+	return rc;
+}
+
+static int __devexit isa1200_remove(struct i2c_client *client)
+{
+	struct isa1200_chip *haptic = i2c_get_clientdata(client);
+
+	input_unregister_device(haptic->input_device);
+	kfree(haptic);
+
+	return 0;
+}
+
+static const struct i2c_device_id isa1200_id_table[] = {
+	{"isa1200_1", 0},
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, isa1200_id_table);
+
+static const struct dev_pm_ops isa1200_pm_ops = {
+	.suspend = isa1200_suspend,
+	.resume = isa1200_resume,
+};
+
+static struct i2c_driver isa1200_driver = {
+	.driver = {
+		.name = "isa1200-ff-memless",
+		.owner = THIS_MODULE,
+		.pm = &isa1200_pm_ops,
+	},
+	.probe = isa1200_probe,
+	.remove = __devexit_p(isa1200_remove),
+	.id_table = isa1200_id_table,
+};
+
+static int __init isa1200_init(void)
+{
+	return i2c_add_driver(&isa1200_driver);
+}
+module_init(isa1200_init);
+
+static void __exit isa1200_exit(void)
+{
+	i2c_del_driver(&isa1200_driver);
+}
+module_exit(isa1200_exit);
+
+MODULE_DESCRIPTION("isa1200 based vibrator chip driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>");
diff --git a/drivers/input/misc/pmic8058-othc.c b/drivers/input/misc/pmic8058-othc.c
new file mode 100644
index 0000000..cac748a
--- /dev/null
+++ b/drivers/input/misc/pmic8058-othc.c
@@ -0,0 +1,1188 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/switch.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/hrtimer.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/pmic8058-othc.h>
+#include <linux/msm_adc.h>
+
+#define PM8058_OTHC_LOW_CURR_MASK	0xF0
+#define PM8058_OTHC_HIGH_CURR_MASK	0x0F
+#define PM8058_OTHC_EN_SIG_MASK		0x3F
+#define PM8058_OTHC_HYST_PREDIV_MASK	0xC7
+#define PM8058_OTHC_CLK_PREDIV_MASK	0xF8
+#define PM8058_OTHC_HYST_CLK_MASK	0x0F
+#define PM8058_OTHC_PERIOD_CLK_MASK	0xF0
+
+#define PM8058_OTHC_LOW_CURR_SHIFT	0x4
+#define PM8058_OTHC_EN_SIG_SHIFT	0x6
+#define PM8058_OTHC_HYST_PREDIV_SHIFT	0x3
+#define PM8058_OTHC_HYST_CLK_SHIFT	0x4
+
+#define OTHC_GPIO_MAX_LEN		25
+
+struct pm8058_othc {
+	bool othc_sw_state;
+	bool switch_reject;
+	bool othc_support_n_switch;
+	bool accessory_support;
+	bool accessories_adc_support;
+	int othc_base;
+	int othc_irq_sw;
+	int othc_irq_ir;
+	int othc_ir_state;
+	int num_accessories;
+	int curr_accessory_code;
+	int curr_accessory;
+	int video_out_gpio;
+	u32 sw_key_code;
+	u32 accessories_adc_channel;
+	int ir_gpio;
+	unsigned long switch_debounce_ms;
+	unsigned long detection_delay_ms;
+	void *adc_handle;
+	void *accessory_adc_handle;
+	spinlock_t lock;
+	struct device *dev;
+	struct regulator *othc_vreg;
+	struct input_dev *othc_ipd;
+	struct switch_dev othc_sdev;
+	struct pmic8058_othc_config_pdata *othc_pdata;
+	struct othc_accessory_info *accessory_info;
+	struct hrtimer timer;
+	struct othc_n_switch_config *switch_config;
+	struct work_struct switch_work;
+	struct delayed_work detect_work;
+	struct delayed_work hs_work;
+};
+
+static struct pm8058_othc *config[OTHC_MICBIAS_MAX];
+
+static void hs_worker(struct work_struct *work)
+{
+	int rc;
+	struct pm8058_othc *dd =
+		container_of(work, struct pm8058_othc, hs_work.work);
+
+	rc = gpio_get_value_cansleep(dd->ir_gpio);
+	if (rc < 0) {
+		pr_err("Unable to read IR GPIO\n");
+		enable_irq(dd->othc_irq_ir);
+		return;
+	}
+
+	dd->othc_ir_state = !rc;
+	schedule_delayed_work(&dd->detect_work,
+				msecs_to_jiffies(dd->detection_delay_ms));
+}
+
+static irqreturn_t ir_gpio_irq(int irq, void *dev_id)
+{
+	unsigned long flags;
+	struct pm8058_othc *dd = dev_id;
+
+	spin_lock_irqsave(&dd->lock, flags);
+	/* Enable the switch reject flag */
+	dd->switch_reject = true;
+	spin_unlock_irqrestore(&dd->lock, flags);
+
+	/* Start the HR timer if one is not active */
+	if (hrtimer_active(&dd->timer))
+		hrtimer_cancel(&dd->timer);
+
+	hrtimer_start(&dd->timer,
+		ktime_set((dd->switch_debounce_ms / 1000),
+		(dd->switch_debounce_ms % 1000) * 1000000), HRTIMER_MODE_REL);
+
+	/* disable irq, this gets enabled in the workqueue */
+	disable_irq_nosync(dd->othc_irq_ir);
+	schedule_delayed_work(&dd->hs_work, 0);
+
+	return IRQ_HANDLED;
+}
+/*
+ * The API pm8058_micbias_enable() allows to configure
+ * the MIC_BIAS. Only the lines which are not used for
+ * headset detection can be configured using this API.
+ * The API returns an error code if it fails to configure
+ * the specified MIC_BIAS line, else it returns 0.
+ */
+int pm8058_micbias_enable(enum othc_micbias micbias,
+		enum othc_micbias_enable enable)
+{
+	int rc;
+	u8 reg;
+	struct pm8058_othc *dd = config[micbias];
+
+	if (dd == NULL) {
+		pr_err("MIC_BIAS not registered, cannot enable\n");
+		return -ENODEV;
+	}
+
+	if (dd->othc_pdata->micbias_capability != OTHC_MICBIAS) {
+		pr_err("MIC_BIAS enable capability not supported\n");
+		return -EINVAL;
+	}
+
+	rc = pm8xxx_readb(dd->dev->parent, dd->othc_base + 1, &reg);
+	if (rc < 0) {
+		pr_err("PM8058 read failed\n");
+		return rc;
+	}
+
+	reg &= PM8058_OTHC_EN_SIG_MASK;
+	reg |= (enable << PM8058_OTHC_EN_SIG_SHIFT);
+
+	rc = pm8xxx_writeb(dd->dev->parent, dd->othc_base + 1, reg);
+	if (rc < 0) {
+		pr_err("PM8058 write failed\n");
+		return rc;
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(pm8058_micbias_enable);
+
+int pm8058_othc_svideo_enable(enum othc_micbias micbias, bool enable)
+{
+	struct pm8058_othc *dd = config[micbias];
+
+	if (dd == NULL) {
+		pr_err("MIC_BIAS not registered, cannot enable\n");
+		return -ENODEV;
+	}
+
+	if (dd->othc_pdata->micbias_capability != OTHC_MICBIAS_HSED) {
+		pr_err("MIC_BIAS enable capability not supported\n");
+		return -EINVAL;
+	}
+
+	if (dd->accessories_adc_support) {
+		/* GPIO state for MIC_IN = 0, SVIDEO = 1 */
+		gpio_set_value_cansleep(dd->video_out_gpio, !!enable);
+		if (enable) {
+			pr_debug("Enable the video path\n");
+			switch_set_state(&dd->othc_sdev, dd->curr_accessory);
+			input_report_switch(dd->othc_ipd,
+						dd->curr_accessory_code, 1);
+			input_sync(dd->othc_ipd);
+		} else {
+			pr_debug("Disable the video path\n");
+			switch_set_state(&dd->othc_sdev, 0);
+			input_report_switch(dd->othc_ipd,
+					dd->curr_accessory_code, 0);
+			input_sync(dd->othc_ipd);
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(pm8058_othc_svideo_enable);
+
+#ifdef CONFIG_PM
+static int pm8058_othc_suspend(struct device *dev)
+{
+	int rc = 0;
+	struct pm8058_othc *dd = dev_get_drvdata(dev);
+
+	if (dd->othc_pdata->micbias_capability == OTHC_MICBIAS_HSED) {
+		if (device_may_wakeup(dev)) {
+			enable_irq_wake(dd->othc_irq_sw);
+			enable_irq_wake(dd->othc_irq_ir);
+		}
+	}
+
+	if (!device_may_wakeup(dev)) {
+		rc = regulator_disable(dd->othc_vreg);
+		if (rc)
+			pr_err("othc micbais power off failed\n");
+	}
+
+	return rc;
+}
+
+static int pm8058_othc_resume(struct device *dev)
+{
+	int rc = 0;
+	struct pm8058_othc *dd = dev_get_drvdata(dev);
+
+	if (dd->othc_pdata->micbias_capability == OTHC_MICBIAS_HSED) {
+		if (device_may_wakeup(dev)) {
+			disable_irq_wake(dd->othc_irq_sw);
+			disable_irq_wake(dd->othc_irq_ir);
+		}
+	}
+
+	if (!device_may_wakeup(dev)) {
+		rc = regulator_enable(dd->othc_vreg);
+		if (rc)
+			pr_err("othc micbais power on failed\n");
+	}
+
+	return rc;
+}
+
+static struct dev_pm_ops pm8058_othc_pm_ops = {
+	.suspend = pm8058_othc_suspend,
+	.resume = pm8058_othc_resume,
+};
+#endif
+
+static int __devexit pm8058_othc_remove(struct platform_device *pd)
+{
+	struct pm8058_othc *dd = platform_get_drvdata(pd);
+
+	pm_runtime_set_suspended(&pd->dev);
+	pm_runtime_disable(&pd->dev);
+
+	if (dd->othc_pdata->micbias_capability == OTHC_MICBIAS_HSED) {
+		device_init_wakeup(&pd->dev, 0);
+		if (dd->othc_support_n_switch == true) {
+			adc_channel_close(dd->adc_handle);
+			cancel_work_sync(&dd->switch_work);
+		}
+
+		if (dd->accessory_support == true) {
+			int i;
+			for (i = 0; i < dd->num_accessories; i++) {
+				if (dd->accessory_info[i].detect_flags &
+							OTHC_GPIO_DETECT)
+					gpio_free(dd->accessory_info[i].gpio);
+			}
+		}
+		cancel_delayed_work_sync(&dd->detect_work);
+		cancel_delayed_work_sync(&dd->hs_work);
+		free_irq(dd->othc_irq_sw, dd);
+		free_irq(dd->othc_irq_ir, dd);
+		if (dd->ir_gpio != -1)
+			gpio_free(dd->ir_gpio);
+		input_unregister_device(dd->othc_ipd);
+	}
+	regulator_disable(dd->othc_vreg);
+	regulator_put(dd->othc_vreg);
+
+	kfree(dd);
+
+	return 0;
+}
+
+static enum hrtimer_restart pm8058_othc_timer(struct hrtimer *timer)
+{
+	unsigned long flags;
+	struct pm8058_othc *dd = container_of(timer,
+					struct pm8058_othc, timer);
+
+	spin_lock_irqsave(&dd->lock, flags);
+	dd->switch_reject = false;
+	spin_unlock_irqrestore(&dd->lock, flags);
+
+	return HRTIMER_NORESTART;
+}
+
+static void othc_report_switch(struct pm8058_othc *dd, u32 res)
+{
+	u8 i;
+	struct othc_switch_info *sw_info = dd->switch_config->switch_info;
+
+	for (i = 0; i < dd->switch_config->num_keys; i++) {
+		if (res >= sw_info[i].min_adc_threshold &&
+				res <= sw_info[i].max_adc_threshold) {
+			dd->othc_sw_state = true;
+			dd->sw_key_code = sw_info[i].key_code;
+			input_report_key(dd->othc_ipd, sw_info[i].key_code, 1);
+			input_sync(dd->othc_ipd);
+			return;
+		}
+	}
+
+	/*
+	 * If the switch is not present in a specified ADC range
+	 * report a default switch press.
+	 */
+	if (dd->switch_config->default_sw_en) {
+		dd->othc_sw_state = true;
+		dd->sw_key_code =
+			sw_info[dd->switch_config->default_sw_idx].key_code;
+		input_report_key(dd->othc_ipd, dd->sw_key_code, 1);
+		input_sync(dd->othc_ipd);
+	}
+}
+
+static void switch_work_f(struct work_struct *work)
+{
+	int rc, i;
+	u32 res = 0;
+	struct adc_chan_result adc_result;
+	struct pm8058_othc *dd =
+		container_of(work, struct pm8058_othc, switch_work);
+	DECLARE_COMPLETION_ONSTACK(adc_wait);
+	u8 num_adc_samples = dd->switch_config->num_adc_samples;
+
+	/* sleep for settling time */
+	msleep(dd->switch_config->voltage_settling_time_ms);
+
+	for (i = 0; i < num_adc_samples; i++) {
+		rc = adc_channel_request_conv(dd->adc_handle, &adc_wait);
+		if (rc) {
+			pr_err("adc_channel_request_conv failed\n");
+			goto bail_out;
+		}
+		rc = wait_for_completion_interruptible(&adc_wait);
+		if (rc) {
+			pr_err("wait_for_completion_interruptible failed\n");
+			goto bail_out;
+		}
+		rc = adc_channel_read_result(dd->adc_handle, &adc_result);
+		if (rc) {
+			pr_err("adc_channel_read_result failed\n");
+			goto bail_out;
+		}
+		res += adc_result.physical;
+	}
+bail_out:
+	if (i == num_adc_samples && num_adc_samples != 0) {
+		res /= num_adc_samples;
+		othc_report_switch(dd, res);
+	} else
+		pr_err("Insufficient ADC samples\n");
+
+	enable_irq(dd->othc_irq_sw);
+}
+
+static int accessory_adc_detect(struct pm8058_othc *dd, int accessory)
+{
+	int rc;
+	u32 res;
+	struct adc_chan_result accessory_adc_result;
+	DECLARE_COMPLETION_ONSTACK(accessory_adc_wait);
+
+	rc = adc_channel_request_conv(dd->accessory_adc_handle,
+						&accessory_adc_wait);
+	if (rc) {
+		pr_err("adc_channel_request_conv failed\n");
+		goto adc_failed;
+	}
+	rc = wait_for_completion_interruptible(&accessory_adc_wait);
+	if (rc) {
+		pr_err("wait_for_completion_interruptible failed\n");
+		goto adc_failed;
+	}
+	rc = adc_channel_read_result(dd->accessory_adc_handle,
+						&accessory_adc_result);
+	if (rc) {
+		pr_err("adc_channel_read_result failed\n");
+		goto adc_failed;
+	}
+
+	res = accessory_adc_result.physical;
+
+	if (res >= dd->accessory_info[accessory].adc_thres.min_threshold &&
+		res <= dd->accessory_info[accessory].adc_thres.max_threshold) {
+		pr_debug("Accessory on ADC detected!, ADC Value = %u\n", res);
+		return 1;
+	}
+
+adc_failed:
+	return 0;
+}
+
+
+static int pm8058_accessory_report(struct pm8058_othc *dd, int status)
+{
+	int i, rc, detected = 0;
+	u8 micbias_status, switch_status;
+
+	if (dd->accessory_support == false) {
+		/* Report default headset */
+		switch_set_state(&dd->othc_sdev, !!status);
+		input_report_switch(dd->othc_ipd, SW_HEADPHONE_INSERT,
+							!!status);
+		input_sync(dd->othc_ipd);
+		return 0;
+	}
+
+	/* For accessory */
+	if (dd->accessory_support == true && status == 0) {
+		/* Report removal of the accessory. */
+
+		/*
+		 * If the current accessory is video cable, reject the removal
+		 * interrupt.
+		 */
+		pr_info("Accessory [%d] removed\n", dd->curr_accessory);
+		if (dd->curr_accessory == OTHC_SVIDEO_OUT)
+			return 0;
+
+		switch_set_state(&dd->othc_sdev, 0);
+		input_report_switch(dd->othc_ipd, dd->curr_accessory_code, 0);
+		input_sync(dd->othc_ipd);
+		return 0;
+	}
+
+	if (dd->ir_gpio < 0) {
+		/* Check the MIC_BIAS status */
+		rc = pm8xxx_read_irq_stat(dd->dev->parent, dd->othc_irq_ir);
+		if (rc < 0) {
+			pr_err("Unable to read IR status from PMIC\n");
+			goto fail_ir_accessory;
+		}
+		micbias_status = !!rc;
+	} else {
+		rc = gpio_get_value_cansleep(dd->ir_gpio);
+		if (rc < 0) {
+			pr_err("Unable to read IR status from GPIO\n");
+			goto fail_ir_accessory;
+		}
+		micbias_status = !rc;
+	}
+
+	/* Check the switch status */
+	rc = pm8xxx_read_irq_stat(dd->dev->parent, dd->othc_irq_sw);
+	if (rc < 0) {
+		pr_err("Unable to read SWITCH status\n");
+		goto fail_ir_accessory;
+	}
+	switch_status = !!rc;
+
+	/* Loop through to check which accessory is connected */
+	for (i = 0; i < dd->num_accessories; i++) {
+		detected = 0;
+		if (dd->accessory_info[i].enabled == false)
+			continue;
+
+		if (dd->accessory_info[i].detect_flags & OTHC_MICBIAS_DETECT) {
+			if (micbias_status)
+				detected = 1;
+			else
+				continue;
+		}
+		if (dd->accessory_info[i].detect_flags & OTHC_SWITCH_DETECT) {
+			if (switch_status)
+				detected = 1;
+			else
+				continue;
+		}
+		if (dd->accessory_info[i].detect_flags & OTHC_GPIO_DETECT) {
+			rc = gpio_get_value_cansleep(
+						dd->accessory_info[i].gpio);
+			if (rc < 0)
+				continue;
+
+			if (rc ^ dd->accessory_info[i].active_low)
+				detected = 1;
+			else
+				continue;
+		}
+		if (dd->accessory_info[i].detect_flags & OTHC_ADC_DETECT)
+			detected = accessory_adc_detect(dd, i);
+
+		if (detected)
+			break;
+	}
+
+	if (detected) {
+		dd->curr_accessory = dd->accessory_info[i].accessory;
+		dd->curr_accessory_code = dd->accessory_info[i].key_code;
+
+		/* if Video out cable detected enable the video path*/
+		if (dd->curr_accessory == OTHC_SVIDEO_OUT) {
+			pm8058_othc_svideo_enable(
+					dd->othc_pdata->micbias_select, true);
+
+		} else {
+			switch_set_state(&dd->othc_sdev, dd->curr_accessory);
+			input_report_switch(dd->othc_ipd,
+						dd->curr_accessory_code, 1);
+			input_sync(dd->othc_ipd);
+		}
+		pr_info("Accessory [%d] inserted\n", dd->curr_accessory);
+	} else
+		pr_info("Unable to detect accessory. False interrupt!\n");
+
+	return 0;
+
+fail_ir_accessory:
+	return rc;
+}
+
+static void detect_work_f(struct work_struct *work)
+{
+	int rc;
+	struct pm8058_othc *dd =
+		container_of(work, struct pm8058_othc, detect_work.work);
+
+	/* Accessory has been inserted */
+	rc = pm8058_accessory_report(dd, 1);
+	if (rc)
+		pr_err("Accessory insertion could not be detected\n");
+
+	enable_irq(dd->othc_irq_ir);
+}
+
+/*
+ * The pm8058_no_sw detects the switch press and release operation.
+ * The odd number call is press and even number call is release.
+ * The current state of the button is maintained in othc_sw_state variable.
+ * This isr gets called only for NO type headsets.
+ */
+static irqreturn_t pm8058_no_sw(int irq, void *dev_id)
+{
+	int level;
+	struct pm8058_othc *dd = dev_id;
+	unsigned long flags;
+
+	/* Check if headset has been inserted, else return */
+	if (!dd->othc_ir_state)
+		return IRQ_HANDLED;
+
+	spin_lock_irqsave(&dd->lock, flags);
+	if (dd->switch_reject == true) {
+		pr_debug("Rejected switch interrupt\n");
+		spin_unlock_irqrestore(&dd->lock, flags);
+		return IRQ_HANDLED;
+	}
+	spin_unlock_irqrestore(&dd->lock, flags);
+
+	level = pm8xxx_read_irq_stat(dd->dev->parent, dd->othc_irq_sw);
+	if (level < 0) {
+		pr_err("Unable to read IRQ status register\n");
+		return IRQ_HANDLED;
+	}
+
+	if (dd->othc_support_n_switch == true) {
+		if (level == 0) {
+			dd->othc_sw_state = false;
+			input_report_key(dd->othc_ipd, dd->sw_key_code, 0);
+			input_sync(dd->othc_ipd);
+		} else {
+			disable_irq_nosync(dd->othc_irq_sw);
+			schedule_work(&dd->switch_work);
+		}
+		return IRQ_HANDLED;
+	}
+	/*
+	 * It is necessary to check the software state and the hardware state
+	 * to make sure that the residual interrupt after the debounce time does
+	 * not disturb the software state machine.
+	 */
+	if (level == 1 && dd->othc_sw_state == false) {
+		/*  Switch has been pressed */
+		dd->othc_sw_state = true;
+		input_report_key(dd->othc_ipd, KEY_MEDIA, 1);
+	} else if (level == 0 && dd->othc_sw_state == true) {
+		/* Switch has been released */
+		dd->othc_sw_state = false;
+		input_report_key(dd->othc_ipd, KEY_MEDIA, 0);
+	}
+	input_sync(dd->othc_ipd);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * The pm8058_nc_ir detects insert / remove of the headset (for NO),
+ * The current state of the headset is maintained in othc_ir_state variable.
+ * Due to a hardware bug, false switch interrupts are seen during headset
+ * insert. This is handled in the software by rejecting the switch interrupts
+ * for a small period of time after the headset has been inserted.
+ */
+static irqreturn_t pm8058_nc_ir(int irq, void *dev_id)
+{
+	unsigned long flags, rc;
+	struct pm8058_othc *dd = dev_id;
+
+	spin_lock_irqsave(&dd->lock, flags);
+	/* Enable the switch reject flag */
+	dd->switch_reject = true;
+	spin_unlock_irqrestore(&dd->lock, flags);
+
+	/* Start the HR timer if one is not active */
+	if (hrtimer_active(&dd->timer))
+		hrtimer_cancel(&dd->timer);
+
+	hrtimer_start(&dd->timer,
+		ktime_set((dd->switch_debounce_ms / 1000),
+		(dd->switch_debounce_ms % 1000) * 1000000), HRTIMER_MODE_REL);
+
+
+	/* Check the MIC_BIAS status, to check if inserted or removed */
+	rc = pm8xxx_read_irq_stat(dd->dev->parent, dd->othc_irq_ir);
+	if (rc < 0) {
+		pr_err("Unable to read IR status\n");
+		goto fail_ir;
+	}
+
+	dd->othc_ir_state = rc;
+	if (dd->othc_ir_state) {
+		/* disable irq, this gets enabled in the workqueue */
+		disable_irq_nosync(dd->othc_irq_ir);
+		/* Accessory has been inserted, report with detection delay */
+		schedule_delayed_work(&dd->detect_work,
+				msecs_to_jiffies(dd->detection_delay_ms));
+	} else {
+		/* Accessory has been removed, report removal immediately */
+		rc = pm8058_accessory_report(dd, 0);
+		if (rc)
+			pr_err("Accessory removal could not be detected\n");
+		/* Clear existing switch state */
+		dd->othc_sw_state = false;
+	}
+
+fail_ir:
+	return IRQ_HANDLED;
+}
+
+static int pm8058_configure_micbias(struct pm8058_othc *dd)
+{
+	int rc;
+	u8 reg, value;
+	u32 value1;
+	u16 base_addr = dd->othc_base;
+	struct hsed_bias_config *hsed_config =
+			dd->othc_pdata->hsed_config->hsed_bias_config;
+
+	/* Intialize the OTHC module */
+	/* Control Register 1*/
+	rc = pm8xxx_readb(dd->dev->parent, base_addr, &reg);
+	if (rc < 0) {
+		pr_err("PM8058 read failed\n");
+		return rc;
+	}
+
+	/* set iDAC high current threshold */
+	value = (hsed_config->othc_highcurr_thresh_uA / 100) - 2;
+	reg =  (reg & PM8058_OTHC_HIGH_CURR_MASK) | value;
+
+	rc = pm8xxx_writeb(dd->dev->parent, base_addr, reg);
+	if (rc < 0) {
+		pr_err("PM8058 read failed\n");
+		return rc;
+	}
+
+	/* Control register 2*/
+	rc = pm8xxx_readb(dd->dev->parent, base_addr + 1, &reg);
+	if (rc < 0) {
+		pr_err("PM8058 read failed\n");
+		return rc;
+	}
+
+	value = dd->othc_pdata->micbias_enable;
+	reg &= PM8058_OTHC_EN_SIG_MASK;
+	reg |= (value << PM8058_OTHC_EN_SIG_SHIFT);
+
+	value = 0;
+	value1 = (hsed_config->othc_hyst_prediv_us << 10) / USEC_PER_SEC;
+	while (value1 != 0) {
+		value1 = value1 >> 1;
+		value++;
+	}
+	if (value > 7) {
+		pr_err("Invalid input argument - othc_hyst_prediv_us\n");
+		return -EINVAL;
+	}
+	reg &= PM8058_OTHC_HYST_PREDIV_MASK;
+	reg |= (value << PM8058_OTHC_HYST_PREDIV_SHIFT);
+
+	value = 0;
+	value1 = (hsed_config->othc_period_clkdiv_us << 10) / USEC_PER_SEC;
+	while (value1 != 1) {
+		value1 = value1 >> 1;
+		value++;
+	}
+	if (value > 8) {
+		pr_err("Invalid input argument - othc_period_clkdiv_us\n");
+		return -EINVAL;
+	}
+	reg = (reg &  PM8058_OTHC_CLK_PREDIV_MASK) | (value - 1);
+
+	rc = pm8xxx_writeb(dd->dev->parent, base_addr + 1, reg);
+	if (rc < 0) {
+		pr_err("PM8058 read failed\n");
+		return rc;
+	}
+
+	/* Control register 3 */
+	rc = pm8xxx_readb(dd->dev->parent, base_addr + 2 , &reg);
+	if (rc < 0) {
+		pr_err("PM8058 read failed\n");
+		return rc;
+	}
+
+	value = hsed_config->othc_hyst_clk_us /
+					hsed_config->othc_hyst_prediv_us;
+	if (value > 15) {
+		pr_err("Invalid input argument - othc_hyst_prediv_us\n");
+		return -EINVAL;
+	}
+	reg &= PM8058_OTHC_HYST_CLK_MASK;
+	reg |= value << PM8058_OTHC_HYST_CLK_SHIFT;
+
+	value = hsed_config->othc_period_clk_us /
+					hsed_config->othc_period_clkdiv_us;
+	if (value > 15) {
+		pr_err("Invalid input argument - othc_hyst_prediv_us\n");
+		return -EINVAL;
+	}
+	reg = (reg & PM8058_OTHC_PERIOD_CLK_MASK) | value;
+
+	rc = pm8xxx_writeb(dd->dev->parent, base_addr + 2, reg);
+	if (rc < 0) {
+		pr_err("PM8058 read failed\n");
+		return rc;
+	}
+
+	return 0;
+}
+
+static ssize_t othc_headset_print_name(struct switch_dev *sdev, char *buf)
+{
+	switch (switch_get_state(sdev)) {
+	case OTHC_NO_DEVICE:
+		return sprintf(buf, "No Device\n");
+	case OTHC_HEADSET:
+	case OTHC_HEADPHONE:
+	case OTHC_MICROPHONE:
+	case OTHC_ANC_HEADSET:
+	case OTHC_ANC_HEADPHONE:
+	case OTHC_ANC_MICROPHONE:
+		return sprintf(buf, "Headset\n");
+	}
+	return -EINVAL;
+}
+
+static int pm8058_configure_switch(struct pm8058_othc *dd)
+{
+	int rc, i;
+
+	if (dd->othc_support_n_switch == true) {
+		/* n-switch support */
+		rc = adc_channel_open(dd->switch_config->adc_channel,
+							&dd->adc_handle);
+		if (rc) {
+			pr_err("Unable to open ADC channel\n");
+			return -ENODEV;
+		}
+
+		for (i = 0; i < dd->switch_config->num_keys; i++) {
+			input_set_capability(dd->othc_ipd, EV_KEY,
+				dd->switch_config->switch_info[i].key_code);
+		}
+	} else /* Only single switch supported */
+		input_set_capability(dd->othc_ipd, EV_KEY, KEY_MEDIA);
+
+	return 0;
+}
+
+static int
+pm8058_configure_accessory(struct pm8058_othc *dd)
+{
+	int i, rc;
+	char name[OTHC_GPIO_MAX_LEN];
+
+	/*
+	 * Not bailing out if the gpio_* configure calls fail. This is required
+	 * as multiple accessories are detected by the same gpio.
+	 */
+	for (i = 0; i < dd->num_accessories; i++) {
+		if (dd->accessory_info[i].enabled == false)
+			continue;
+		if (dd->accessory_info[i].detect_flags & OTHC_GPIO_DETECT) {
+			snprintf(name, OTHC_GPIO_MAX_LEN, "%s%d",
+							"othc_acc_gpio_", i);
+			rc = gpio_request(dd->accessory_info[i].gpio, name);
+			if (rc) {
+				pr_debug("Unable to request GPIO [%d]\n",
+						dd->accessory_info[i].gpio);
+				continue;
+			}
+			rc = gpio_direction_input(dd->accessory_info[i].gpio);
+			if (rc) {
+				pr_debug("Unable to set-direction GPIO [%d]\n",
+						dd->accessory_info[i].gpio);
+				gpio_free(dd->accessory_info[i].gpio);
+				continue;
+			}
+		}
+		input_set_capability(dd->othc_ipd, EV_SW,
+					dd->accessory_info[i].key_code);
+	}
+
+	if (dd->accessories_adc_support) {
+		/*
+		 * Check if 3 switch is supported. If both are using the same
+		 * ADC channel, the same handle can be used.
+		 */
+		if (dd->othc_support_n_switch) {
+			if (dd->adc_handle != NULL &&
+				(dd->accessories_adc_channel ==
+				 dd->switch_config->adc_channel))
+				dd->accessory_adc_handle = dd->adc_handle;
+		} else {
+			rc = adc_channel_open(dd->accessories_adc_channel,
+						&dd->accessory_adc_handle);
+			if (rc) {
+				pr_err("Unable to open ADC channel\n");
+				rc = -ENODEV;
+				goto accessory_adc_fail;
+			}
+		}
+		if (dd->video_out_gpio != 0) {
+			rc = gpio_request(dd->video_out_gpio, "vout_enable");
+			if (rc < 0) {
+				pr_err("request VOUT gpio failed (%d)\n", rc);
+				goto accessory_adc_fail;
+			}
+			rc = gpio_direction_output(dd->video_out_gpio, 0);
+			if (rc < 0) {
+				pr_err("direction_out failed (%d)\n", rc);
+				goto accessory_adc_fail;
+			}
+		}
+
+	}
+
+	return 0;
+
+accessory_adc_fail:
+	for (i = 0; i < dd->num_accessories; i++) {
+		if (dd->accessory_info[i].enabled == false)
+			continue;
+		gpio_free(dd->accessory_info[i].gpio);
+	}
+	return rc;
+}
+
+static int
+othc_configure_hsed(struct pm8058_othc *dd, struct platform_device *pd)
+{
+	int rc;
+	struct input_dev *ipd;
+	struct pmic8058_othc_config_pdata *pdata = pd->dev.platform_data;
+	struct othc_hsed_config *hsed_config = pdata->hsed_config;
+
+	dd->othc_sdev.name = "h2w";
+	dd->othc_sdev.print_name = othc_headset_print_name;
+
+	rc = switch_dev_register(&dd->othc_sdev);
+	if (rc) {
+		pr_err("Unable to register switch device\n");
+		return rc;
+	}
+
+	ipd = input_allocate_device();
+	if (ipd == NULL) {
+		pr_err("Unable to allocate memory\n");
+		rc = -ENOMEM;
+		goto fail_input_alloc;
+	}
+
+	/* Get the IRQ for Headset Insert-remove and Switch-press */
+	dd->othc_irq_sw = platform_get_irq(pd, 0);
+	dd->othc_irq_ir = platform_get_irq(pd, 1);
+	if (dd->othc_irq_ir < 0 || dd->othc_irq_sw < 0) {
+		pr_err("othc resource:IRQs absent\n");
+		rc = -ENXIO;
+		goto fail_micbias_config;
+	}
+
+	if (pdata->hsed_name != NULL)
+		ipd->name = pdata->hsed_name;
+	else
+		ipd->name = "pmic8058_othc";
+
+	ipd->phys = "pmic8058_othc/input0";
+	ipd->dev.parent = &pd->dev;
+
+	dd->othc_ipd = ipd;
+	dd->ir_gpio = hsed_config->ir_gpio;
+	dd->othc_sw_state = false;
+	dd->switch_debounce_ms = hsed_config->switch_debounce_ms;
+	dd->othc_support_n_switch = hsed_config->othc_support_n_switch;
+	dd->accessory_support = pdata->hsed_config->accessories_support;
+	dd->detection_delay_ms = pdata->hsed_config->detection_delay_ms;
+
+	if (dd->othc_support_n_switch == true)
+		dd->switch_config = hsed_config->switch_config;
+
+	if (dd->accessory_support == true) {
+		dd->accessory_info = pdata->hsed_config->accessories;
+		dd->num_accessories = pdata->hsed_config->othc_num_accessories;
+		dd->accessories_adc_support =
+				pdata->hsed_config->accessories_adc_support;
+		dd->accessories_adc_channel =
+				pdata->hsed_config->accessories_adc_channel;
+		dd->video_out_gpio = pdata->hsed_config->video_out_gpio;
+	}
+
+	/* Configure the MIC_BIAS line for headset detection */
+	rc = pm8058_configure_micbias(dd);
+	if (rc < 0)
+		goto fail_micbias_config;
+
+	/* Configure for the switch events */
+	rc = pm8058_configure_switch(dd);
+	if (rc < 0)
+		goto fail_micbias_config;
+
+	/* Configure the accessory */
+	if (dd->accessory_support == true) {
+		rc = pm8058_configure_accessory(dd);
+		if (rc < 0)
+			goto fail_micbias_config;
+	}
+
+	input_set_drvdata(ipd, dd);
+	spin_lock_init(&dd->lock);
+
+	rc = input_register_device(ipd);
+	if (rc) {
+		pr_err("Unable to register OTHC device\n");
+		goto fail_micbias_config;
+	}
+
+	hrtimer_init(&dd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	dd->timer.function = pm8058_othc_timer;
+
+	/* Request the HEADSET IR interrupt */
+	if (dd->ir_gpio < 0) {
+		rc = request_threaded_irq(dd->othc_irq_ir, NULL, pm8058_nc_ir,
+		IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_DISABLED,
+					"pm8058_othc_ir", dd);
+		if (rc < 0) {
+			pr_err("Unable to request pm8058_othc_ir IRQ\n");
+			goto fail_ir_irq;
+		}
+	} else {
+		rc = gpio_request(dd->ir_gpio, "othc_ir_gpio");
+		if (rc) {
+			pr_err("Unable to request IR GPIO\n");
+			goto fail_ir_gpio_req;
+		}
+		rc = gpio_direction_input(dd->ir_gpio);
+		if (rc) {
+			pr_err("GPIO %d set_direction failed\n", dd->ir_gpio);
+			goto fail_ir_irq;
+		}
+		dd->othc_irq_ir = gpio_to_irq(dd->ir_gpio);
+		rc = request_any_context_irq(dd->othc_irq_ir, ir_gpio_irq,
+		IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+				"othc_gpio_ir_irq", dd);
+		if (rc < 0) {
+			pr_err("could not request hs irq err=%d\n", rc);
+			goto fail_ir_irq;
+		}
+	}
+	/* Request the  SWITCH press/release interrupt */
+	rc = request_threaded_irq(dd->othc_irq_sw, NULL, pm8058_no_sw,
+	IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_DISABLED,
+			"pm8058_othc_sw", dd);
+	if (rc < 0) {
+		pr_err("Unable to request pm8058_othc_sw IRQ\n");
+		goto fail_sw_irq;
+	}
+
+	/* Check if the accessory is already inserted during boot up */
+	if (dd->ir_gpio < 0) {
+		rc = pm8xxx_read_irq_stat(dd->dev->parent, dd->othc_irq_ir);
+		if (rc < 0) {
+			pr_err("Unable to get accessory status at boot\n");
+			goto fail_ir_status;
+		}
+	} else {
+		rc = gpio_get_value_cansleep(dd->ir_gpio);
+		if (rc < 0) {
+			pr_err("Unable to get accessory status at boot\n");
+			goto fail_ir_status;
+		}
+		rc = !rc;
+	}
+	if (rc) {
+		pr_debug("Accessory inserted during boot up\n");
+		/* process the data and report the inserted accessory */
+		rc = pm8058_accessory_report(dd, 1);
+		if (rc)
+			pr_debug("Unabele to detect accessory at boot up\n");
+	}
+
+	device_init_wakeup(&pd->dev,
+			hsed_config->hsed_bias_config->othc_wakeup);
+
+	INIT_DELAYED_WORK(&dd->detect_work, detect_work_f);
+
+	INIT_DELAYED_WORK(&dd->hs_work, hs_worker);
+
+	if (dd->othc_support_n_switch == true)
+		INIT_WORK(&dd->switch_work, switch_work_f);
+
+
+	return 0;
+
+fail_ir_status:
+	free_irq(dd->othc_irq_sw, dd);
+fail_sw_irq:
+	free_irq(dd->othc_irq_ir, dd);
+fail_ir_irq:
+	if (dd->ir_gpio != -1)
+		gpio_free(dd->ir_gpio);
+fail_ir_gpio_req:
+	input_unregister_device(ipd);
+	dd->othc_ipd = NULL;
+fail_micbias_config:
+	input_free_device(ipd);
+fail_input_alloc:
+	switch_dev_unregister(&dd->othc_sdev);
+	return rc;
+}
+
+static int __devinit pm8058_othc_probe(struct platform_device *pd)
+{
+	int rc;
+	struct pm8058_othc *dd;
+	struct resource *res;
+	struct pmic8058_othc_config_pdata *pdata = pd->dev.platform_data;
+
+	if (pdata == NULL) {
+		pr_err("Platform data not present\n");
+		return -EINVAL;
+	}
+
+	dd = kzalloc(sizeof(*dd), GFP_KERNEL);
+	if (dd == NULL) {
+		pr_err("Unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	/* Enable runtime PM ops, start in ACTIVE mode */
+	rc = pm_runtime_set_active(&pd->dev);
+	if (rc < 0)
+		dev_dbg(&pd->dev, "unable to set runtime pm state\n");
+	pm_runtime_enable(&pd->dev);
+
+	res = platform_get_resource_byname(pd, IORESOURCE_IO, "othc_base");
+	if (res == NULL) {
+		pr_err("othc resource:Base address absent\n");
+		rc = -ENXIO;
+		goto fail_get_res;
+	}
+
+	dd->dev = &pd->dev;
+	dd->othc_pdata = pdata;
+	dd->othc_base = res->start;
+	if (pdata->micbias_regulator == NULL) {
+		pr_err("OTHC regulator not specified\n");
+		goto fail_get_res;
+	}
+
+	dd->othc_vreg = regulator_get(NULL,
+				pdata->micbias_regulator->regulator);
+	if (IS_ERR(dd->othc_vreg)) {
+		pr_err("regulator get failed\n");
+		rc = PTR_ERR(dd->othc_vreg);
+		goto fail_get_res;
+	}
+
+	rc = regulator_set_voltage(dd->othc_vreg,
+				pdata->micbias_regulator->min_uV,
+				pdata->micbias_regulator->max_uV);
+	if (rc) {
+		pr_err("othc regulator set voltage failed\n");
+		goto fail_reg_enable;
+	}
+
+	rc = regulator_enable(dd->othc_vreg);
+	if (rc) {
+		pr_err("othc regulator enable failed\n");
+		goto fail_reg_enable;
+	}
+
+	platform_set_drvdata(pd, dd);
+
+	if (pdata->micbias_capability == OTHC_MICBIAS_HSED) {
+		/* HSED to be supported on this MICBIAS line */
+		if (pdata->hsed_config != NULL) {
+			rc = othc_configure_hsed(dd, pd);
+			if (rc < 0)
+				goto fail_othc_hsed;
+		} else {
+			pr_err("HSED config data not present\n");
+			rc = -EINVAL;
+			goto fail_othc_hsed;
+		}
+	}
+
+	/* Store the local driver data structure */
+	if (dd->othc_pdata->micbias_select < OTHC_MICBIAS_MAX)
+		config[dd->othc_pdata->micbias_select] = dd;
+
+	pr_debug("Device %s:%d successfully registered\n",
+			pd->name, pd->id);
+	return 0;
+
+fail_othc_hsed:
+	regulator_disable(dd->othc_vreg);
+fail_reg_enable:
+	regulator_put(dd->othc_vreg);
+fail_get_res:
+	pm_runtime_set_suspended(&pd->dev);
+	pm_runtime_disable(&pd->dev);
+
+	kfree(dd);
+	return rc;
+}
+
+static struct platform_driver pm8058_othc_driver = {
+	.driver = {
+		.name = "pm8058-othc",
+		.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm = &pm8058_othc_pm_ops,
+#endif
+	},
+	.probe = pm8058_othc_probe,
+	.remove = __devexit_p(pm8058_othc_remove),
+};
+
+static int __init pm8058_othc_init(void)
+{
+	return platform_driver_register(&pm8058_othc_driver);
+}
+
+static void __exit pm8058_othc_exit(void)
+{
+	platform_driver_unregister(&pm8058_othc_driver);
+}
+/*
+ * Move to late_initcall, to make sure that the ADC driver registration is
+ * completed before we open a ADC channel.
+ */
+late_initcall(pm8058_othc_init);
+module_exit(pm8058_othc_exit);
+
+MODULE_ALIAS("platform:pmic8058_othc");
+MODULE_DESCRIPTION("PMIC 8058 OTHC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/misc/pmic8058-vib-memless.c b/drivers/input/misc/pmic8058-vib-memless.c
new file mode 100644
index 0000000..ba05400
--- /dev/null
+++ b/drivers/input/misc/pmic8058-vib-memless.c
@@ -0,0 +1,282 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/pmic8058-vibrator.h>
+#include <linux/mfd/pmic8058.h>
+#include <linux/pm.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+
+#define VIB_DRV			0x4A
+
+#define VIB_DRV_SEL_MASK	0xf8
+#define VIB_DRV_SEL_SHIFT	0x03
+#define VIB_DRV_EN_MANUAL_MASK	0xfc
+
+#define VIB_MAX_LEVEL_mV	(3100)
+#define VIB_MIN_LEVEL_mV	(1200)
+#define VIB_MAX_LEVELS		(VIB_MAX_LEVEL_mV - VIB_MIN_LEVEL_mV)
+
+#define MAX_FF_SPEED		0xff
+
+struct pmic8058_vib {
+	struct input_dev *info;
+	spinlock_t lock;
+	struct work_struct work;
+
+	bool enabled;
+	int speed;
+	struct device *dev;
+	struct pmic8058_vibrator_pdata *pdata;
+	int state;
+	int level;
+	u8  reg_vib_drv;
+
+	struct pm8058_chip	*pm_chip;
+};
+
+/* REVISIT: just for debugging, will be removed in final working version */
+static void __dump_vib_regs(struct pmic8058_vib *vib, char *msg)
+{
+	u8 temp;
+
+	dev_dbg(vib->dev, "%s\n", msg);
+
+	pm8058_read(vib->pm_chip, VIB_DRV, &temp, 1);
+	dev_dbg(vib->dev, "VIB_DRV - %X\n", temp);
+}
+
+static int pmic8058_vib_read_u8(struct pmic8058_vib *vib,
+				 u8 *data, u16 reg)
+{
+	int rc;
+
+	rc = pm8058_read(vib->pm_chip, reg, data, 1);
+	if (rc < 0)
+		dev_warn(vib->dev, "Error reading pmic8058: %X - ret %X\n",
+				reg, rc);
+
+	return rc;
+}
+
+static int pmic8058_vib_write_u8(struct pmic8058_vib *vib,
+				 u8 data, u16 reg)
+{
+	int rc;
+
+	rc = pm8058_write(vib->pm_chip, reg, &data, 1);
+	if (rc < 0)
+		dev_warn(vib->dev, "Error writing pmic8058: %X - ret %X\n",
+				reg, rc);
+	return rc;
+}
+
+static int pmic8058_vib_set(struct pmic8058_vib *vib, int on)
+{
+	int rc;
+	u8 val;
+
+	if (on) {
+		val = vib->reg_vib_drv;
+		val |= ((vib->level << VIB_DRV_SEL_SHIFT) & VIB_DRV_SEL_MASK);
+		rc = pmic8058_vib_write_u8(vib, val, VIB_DRV);
+		if (rc < 0)
+			return rc;
+		vib->reg_vib_drv = val;
+		vib->enabled = 1;
+
+	} else {
+		val = vib->reg_vib_drv;
+		val &= ~VIB_DRV_SEL_MASK;
+		rc = pmic8058_vib_write_u8(vib, val, VIB_DRV);
+		if (rc < 0)
+			return rc;
+		vib->reg_vib_drv = val;
+		vib->enabled = 0;
+	}
+	__dump_vib_regs(vib, "vib_set_end");
+
+	return rc;
+}
+
+static void pmic8058_work_handler(struct work_struct *work)
+{
+	u8 val;
+	int rc;
+	struct pmic8058_vib *info;
+
+	info  = container_of(work, struct pmic8058_vib, work);
+
+	rc = pmic8058_vib_read_u8(info, &val, VIB_DRV);
+	if (rc < 0)
+		return;
+
+	/*
+	 * Vibrator support voltage ranges from 1.2 to 3.1V, so
+	 * scale the FF speed to these range.
+	 */
+	if (info->speed) {
+		info->state = 1;
+		info->level = ((VIB_MAX_LEVELS * info->speed) / MAX_FF_SPEED) +
+						VIB_MIN_LEVEL_mV;
+		info->level /= 100;
+	} else {
+		info->state = 0;
+		info->level = VIB_MIN_LEVEL_mV / 100;
+	}
+	pmic8058_vib_set(info, info->state);
+}
+
+static int pmic8058_vib_play_effect(struct input_dev *dev, void *data,
+		      struct ff_effect *effect)
+{
+	struct pmic8058_vib *info = input_get_drvdata(dev);
+
+	info->speed = effect->u.rumble.strong_magnitude >> 8;
+	if (!info->speed)
+		info->speed = effect->u.rumble.weak_magnitude >> 9;
+	schedule_work(&info->work);
+	return 0;
+}
+
+static int __devinit pmic8058_vib_probe(struct platform_device *pdev)
+
+{
+	struct pmic8058_vibrator_pdata *pdata = pdev->dev.platform_data;
+	struct pmic8058_vib *vib;
+	u8 val;
+	int rc;
+
+	struct pm8058_chip	*pm_chip;
+
+	pm_chip = dev_get_drvdata(pdev->parent.dev);
+	if (pm_chip == NULL) {
+		dev_err(&pdev->dev, "no parent data passed in\n");
+		return -EFAULT;
+	}
+
+	if (!pdata)
+		return -EINVAL;
+
+	if (pdata->level_mV < VIB_MIN_LEVEL_mV ||
+			 pdata->level_mV > VIB_MAX_LEVEL_mV)
+		return -EINVAL;
+
+	vib = kzalloc(sizeof(*vib), GFP_KERNEL);
+	if (!vib)
+		return -ENOMEM;
+
+	vib->pm_chip	= pm_chip;
+	vib->enabled	= 0;
+	vib->pdata	= pdata;
+	vib->level	= pdata->level_mV / 100;
+	vib->dev	= &pdev->dev;
+
+	spin_lock_init(&vib->lock);
+	INIT_WORK(&vib->work, pmic8058_work_handler);
+
+	vib->info = input_allocate_device();
+
+	if (vib->info == NULL) {
+		dev_err(&pdev->dev, "couldn't allocate input device\n");
+		return -ENOMEM;
+	}
+
+	input_set_drvdata(vib->info, vib);
+
+	vib->info->name = "pmic8058:vibrator";
+	vib->info->id.version = 1;
+	vib->info->dev.parent = pdev->dev.parent;
+
+	__set_bit(FF_RUMBLE, vib->info->ffbit);
+	__dump_vib_regs(vib, "boot_vib_default");
+
+	/* operate in manual mode */
+	rc = pmic8058_vib_read_u8(vib, &val, VIB_DRV);
+	if (rc < 0)
+		goto err_read_vib;
+	val &= ~VIB_DRV_EN_MANUAL_MASK;
+	rc = pmic8058_vib_write_u8(vib, val, VIB_DRV);
+	if (rc < 0)
+		goto err_read_vib;
+
+	vib->reg_vib_drv = val;
+
+	rc = input_ff_create_memless(vib->info, NULL, pmic8058_vib_play_effect);
+	if (rc < 0) {
+		dev_dbg(&pdev->dev, "couldn't register vibrator to FF\n");
+		goto create_memless_err;
+	}
+
+	platform_set_drvdata(pdev, vib);
+
+	rc = input_register_device(vib->info);
+	if (rc < 0) {
+		dev_dbg(&pdev->dev, "couldn't register input device\n");
+		goto reg_err;
+	}
+
+	return 0;
+
+reg_err:
+	input_ff_destroy(vib->info);
+create_memless_err:
+	input_free_device(vib->info);
+err_read_vib:
+	kfree(vib);
+	return rc;
+}
+
+static int __devexit pmic8058_vib_remove(struct platform_device *pdev)
+{
+	struct pmic8058_vib *vib = platform_get_drvdata(pdev);
+
+	cancel_work_sync(&vib->work);
+	if (vib->enabled)
+		pmic8058_vib_set(vib, 0);
+
+	input_unregister_device(vib->info);
+	kfree(vib);
+
+	return 0;
+}
+
+static struct platform_driver pmic8058_vib_driver = {
+	.probe		= pmic8058_vib_probe,
+	.remove		= __devexit_p(pmic8058_vib_remove),
+	.driver		= {
+		.name	= "pm8058-vib",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init pmic8058_vib_init(void)
+{
+	return platform_driver_register(&pmic8058_vib_driver);
+}
+module_init(pmic8058_vib_init);
+
+static void __exit pmic8058_vib_exit(void)
+{
+	platform_driver_unregister(&pmic8058_vib_driver);
+}
+module_exit(pmic8058_vib_exit);
+
+MODULE_ALIAS("platform:pmic8058_vib");
+MODULE_DESCRIPTION("PMIC8058 vibrator driver memless framework");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/misc/pmic8xxx-pwrkey.c b/drivers/input/misc/pmic8xxx-pwrkey.c
index 0f83d0f..58f9661 100644
--- a/drivers/input/misc/pmic8xxx-pwrkey.c
+++ b/drivers/input/misc/pmic8xxx-pwrkey.c
@@ -30,10 +30,12 @@
 /**
  * struct pmic8xxx_pwrkey - pmic8xxx pwrkey information
  * @key_press_irq: key press irq number
+ * @pdata: platform data
  */
 struct pmic8xxx_pwrkey {
 	struct input_dev *pwr;
 	int key_press_irq;
+	const struct pm8xxx_pwrkey_platform_data *pdata;
 };
 
 static irqreturn_t pwrkey_press_irq(int irq, void *_pwrkey)
@@ -98,7 +100,9 @@
 		return -EINVAL;
 	}
 
-	if (pdata->kpd_trigger_delay_us > 62500) {
+	/* Valid range of pwr key trigger delay is 1/64 sec to 2 seconds. */
+	if (pdata->kpd_trigger_delay_us > USEC_PER_SEC * 2 ||
+		pdata->kpd_trigger_delay_us < USEC_PER_SEC / 64) {
 		dev_err(&pdev->dev, "invalid power key trigger delay\n");
 		return -EINVAL;
 	}
@@ -107,6 +111,8 @@
 	if (!pwrkey)
 		return -ENOMEM;
 
+	pwrkey->pdata = pdata;
+
 	pwr = input_allocate_device();
 	if (!pwr) {
 		dev_dbg(&pdev->dev, "Can't allocate power button\n");
@@ -120,8 +126,8 @@
 	pwr->phys = "pmic8xxx_pwrkey/input0";
 	pwr->dev.parent = &pdev->dev;
 
-	delay = (pdata->kpd_trigger_delay_us << 10) / USEC_PER_SEC;
-	delay = 1 + ilog2(delay);
+	delay = (pdata->kpd_trigger_delay_us << 6) / USEC_PER_SEC;
+	delay = ilog2(delay);
 
 	err = pm8xxx_readb(pdev->dev.parent, PON_CNTL_1, &pon_cntl);
 	if (err < 0) {
@@ -153,7 +159,7 @@
 
 	platform_set_drvdata(pdev, pwrkey);
 
-	err = request_irq(key_press_irq, pwrkey_press_irq,
+	err = request_any_context_irq(key_press_irq, pwrkey_press_irq,
 		IRQF_TRIGGER_RISING, "pmic8xxx_pwrkey_press", pwrkey);
 	if (err < 0) {
 		dev_dbg(&pdev->dev, "Can't get %d IRQ for pwrkey: %d\n",
@@ -161,7 +167,7 @@
 		goto unreg_input_dev;
 	}
 
-	err = request_irq(key_release_irq, pwrkey_release_irq,
+	err = request_any_context_irq(key_release_irq, pwrkey_release_irq,
 		 IRQF_TRIGGER_RISING, "pmic8xxx_pwrkey_release", pwrkey);
 	if (err < 0) {
 		dev_dbg(&pdev->dev, "Can't get %d IRQ for pwrkey: %d\n",
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index 9b8db82..3326091 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -322,6 +322,14 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called synaptics_i2c.
 
+config MOUSE_QCITP
+	tristate "Quanta Computer Inc. Touchpad"
+	depends on I2C
+	default n
+	help
+	  Say Y here if you want to use the Quanta touchpad driver for Quanta
+	  smartbook platforms.
+
 config MOUSE_SYNAPTICS_USB
 	tristate "Synaptics USB device support"
 	depends on USB_ARCH_HAS_HCD
diff --git a/drivers/input/mouse/qci_touchpad.c b/drivers/input/mouse/qci_touchpad.c
new file mode 100644
index 0000000..ef93a7e
--- /dev/null
+++ b/drivers/input/mouse/qci_touchpad.c
@@ -0,0 +1,309 @@
+/* Quanta I2C Touchpad Driver
+ *
+ * Copyright (C) 2009 Quanta Computer Inc.
+ * Author: Hsin Wu <hsin.wu@quantatw.com>
+ * Author: Austin Lai <austin.lai@quantatw.com>
+ *
+ * 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.
+ *
+ */
+
+  /*
+ *
+ *  The Driver with I/O communications via the I2C Interface for ON2 of AP BU.
+ *  And it is only working on the nuvoTon WPCE775x Embedded Controller.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/keyboard.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+
+#define TOUCHPAD_ID_NAME          "qci-i2cpad"
+#define TOUCHPAD_NAME                "PS2 Touchpad"
+#define TOUCHPAD_DEVICE             "/i2c/input1"
+#define TOUCHPAD_CMD_ENABLE             0xF4
+#define TOUCHPAD_INIT_DELAY_MS    100
+
+static int __devinit qcitp_probe(struct i2c_client *client,
+	const struct i2c_device_id *id);
+static int __devexit qcitp_remove(struct i2c_client *kbd);
+
+/* General structure to hold the driver data */
+struct i2ctpad_drv_data {
+	struct i2c_client *ti2c_client;
+	struct work_struct work;
+	struct input_dev *qcitp_dev;
+	struct kobject *tp_kobj;
+	unsigned int  qcitp_gpio;
+	unsigned int  qcitp_irq;
+	char ecdata[8];
+};
+
+static int tp_sense_val = 10;
+static ssize_t tp_sensitive_show(struct kobject *kobj,
+	struct kobj_attribute *attr, char * buf)
+{
+	return sprintf(buf, "%d\n", tp_sense_val);
+}
+
+static ssize_t tp_sensitive_store(struct kobject *kobj,
+	struct kobj_attribute *attr, const char* buf, size_t n)
+{
+	unsigned int val = 0;
+	sscanf(buf, "%d", &val);
+
+	if (val >= 1 && val <= 10)
+		tp_sense_val = val;
+	else
+		return  -ENOSYS;
+
+	return sizeof(buf);
+}
+
+static struct kobj_attribute tp_sensitivity = __ATTR(tp_sensitivity ,
+						     0644 ,
+						     tp_sensitive_show ,
+						     tp_sensitive_store);
+
+static struct attribute *g_tp[] = {
+	&tp_sensitivity.attr,
+	NULL,
+};
+
+static struct attribute_group attr_group = {
+	.attrs = g_tp,
+};
+
+/*-----------------------------------------------------------------------------
+ * Driver functions
+ *---------------------------------------------------------------------------*/
+
+#ifdef CONFIG_PM
+static int qcitp_suspend(struct device *dev)
+{
+	return 0;
+}
+
+static int qcitp_resume(struct device *dev)
+{
+	return 0;
+}
+#endif
+
+static const struct i2c_device_id qcitp_idtable[] = {
+	{ TOUCHPAD_ID_NAME, 0 },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(i2c, qcitp_idtable);
+#ifdef CONFIG_PM
+static const struct dev_pm_ops qcitp_pm_ops = {
+	.suspend  = qcitp_suspend,
+	.resume   = qcitp_resume,
+};
+#endif
+static struct i2c_driver i2ctp_driver = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name  = TOUCHPAD_ID_NAME,
+#ifdef CONFIG_PM
+		.pm = &qcitp_pm_ops,
+#endif
+	},
+	.probe	  = qcitp_probe,
+	.remove	  = __devexit_p(qcitp_remove),
+	.id_table = qcitp_idtable,
+};
+
+static void qcitp_fetch_data(struct i2c_client *tpad_client,
+	char *ec_data)
+{
+	struct i2c_msg tp_msg;
+	int ret;
+	tp_msg.addr = tpad_client->addr;
+	tp_msg.flags = I2C_M_RD;
+	tp_msg.len = 3;
+	tp_msg.buf = (char *)&ec_data[0];
+	ret = i2c_transfer(tpad_client->adapter, &tp_msg, 1);
+}
+
+static void qcitp_report_key(struct input_dev *tpad_dev, char *ec_data)
+{
+	int dx = 0;
+	int dy = 0;
+
+	if (ec_data[1])
+		dx = (int) ec_data[1] -
+		     (int) ((ec_data[0] << 4) & 0x100);
+
+	if (ec_data[2])
+		dy = (int) ((ec_data[0] << 3) & 0x100) -
+		     (int) ec_data[2];
+
+	dx = (dx * tp_sense_val)/10;
+	dy = (dy * tp_sense_val)/10;
+
+	input_report_key(tpad_dev, BTN_LEFT, ec_data[0] & 0x01);
+	input_report_key(tpad_dev, BTN_RIGHT, ec_data[0] & 0x02);
+	input_report_key(tpad_dev, BTN_MIDDLE, ec_data[0] & 0x04);
+	input_report_rel(tpad_dev, REL_X, dx);
+	input_report_rel(tpad_dev, REL_Y, dy);
+	input_sync(tpad_dev);
+}
+
+static void qcitp_work_handler(struct work_struct *_work)
+{
+	struct i2ctpad_drv_data *itpad_drv_data =
+		container_of(_work, struct i2ctpad_drv_data, work);
+
+	struct i2c_client *itpad_client = itpad_drv_data->ti2c_client;
+	struct input_dev *itpad_dev = itpad_drv_data->qcitp_dev;
+
+	qcitp_fetch_data(itpad_client, itpad_drv_data->ecdata);
+	qcitp_report_key(itpad_dev, itpad_drv_data->ecdata);
+}
+
+static irqreturn_t qcitp_interrupt(int irq, void *dev_id)
+{
+	struct i2ctpad_drv_data *itpad_drv_data = dev_id;
+	schedule_work(&itpad_drv_data->work);
+	return IRQ_HANDLED;
+}
+
+static int __devinit qcitp_probe(struct i2c_client *client,
+				    const struct i2c_device_id *id)
+{
+	int err = -ENOMEM;
+	struct i2ctpad_drv_data *context = 0;
+
+	context = kzalloc(sizeof(struct i2ctpad_drv_data), GFP_KERNEL);
+	if (!context)
+		return err;
+	i2c_set_clientdata(client, context);
+	context->ti2c_client = client;
+	context->qcitp_gpio = client->irq;
+
+	/* Enable mouse */
+	i2c_smbus_write_byte(client, TOUCHPAD_CMD_ENABLE);
+	msleep(TOUCHPAD_INIT_DELAY_MS);
+	i2c_smbus_read_byte(client);
+	/*allocate and register input device*/
+	context->qcitp_dev = input_allocate_device();
+	if (!context->qcitp_dev) {
+		pr_err("[TouchPad] allocting memory fail\n");
+		err = -ENOMEM;
+		goto allocate_fail;
+	}
+	context->qcitp_dev->name        = TOUCHPAD_NAME;
+	context->qcitp_dev->phys         = TOUCHPAD_DEVICE;
+	context->qcitp_dev->id.bustype = BUS_I2C;
+	context->qcitp_dev->id.vendor  = 0x1050;
+	context->qcitp_dev->id.product = 0x1;
+	context->qcitp_dev->id.version = 0x1;
+	context->qcitp_dev->evbit[0]  = BIT_MASK(EV_KEY) |
+					BIT_MASK(EV_REL);
+	context->qcitp_dev->relbit[0] = BIT_MASK(REL_X) |
+					BIT_MASK(REL_Y);
+	context->qcitp_dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) |
+							 BIT_MASK(BTN_MIDDLE) |
+							 BIT_MASK(BTN_RIGHT);
+
+	input_set_drvdata(context->qcitp_dev, context);
+	err = input_register_device(context->qcitp_dev);
+	if (err) {
+		pr_err("[TouchPad] register device fail\n");
+		goto register_fail;
+	}
+
+	/*request intterrupt*/
+	INIT_WORK(&context->work, qcitp_work_handler);
+
+	err = gpio_request(context->qcitp_gpio, "qci-pad");
+	if (err) {
+		pr_err("[TouchPad]err gpio request\n");
+		goto gpio_request_fail;
+	}
+
+	context->qcitp_irq = gpio_to_irq(context->qcitp_gpio);
+	err = request_irq(context->qcitp_irq,
+			  qcitp_interrupt,
+			  IRQF_TRIGGER_FALLING,
+			  TOUCHPAD_ID_NAME,
+			  context);
+	if (err) {
+		pr_err("[TouchPad] unable to get IRQ\n");
+		goto request_irq_fail;
+	}
+	/*create touchpad kobject*/
+	context->tp_kobj = kobject_create_and_add("touchpad", NULL);
+
+	err = sysfs_create_group(context->tp_kobj, &attr_group);
+	if (err)
+		pr_warning("[TouchPad] sysfs create fail\n");
+
+	tp_sense_val = 10;
+
+	return 0;
+
+request_irq_fail:
+	gpio_free(context->qcitp_gpio);
+
+gpio_request_fail:
+	input_unregister_device(context->qcitp_dev);
+
+register_fail:
+	input_free_device(context->qcitp_dev);
+
+allocate_fail:
+	i2c_set_clientdata(client, NULL);
+	kfree(context);
+	return err;
+}
+
+static int __devexit qcitp_remove(struct i2c_client *dev)
+{
+	struct i2ctpad_drv_data *context = i2c_get_clientdata(dev);
+
+	free_irq(context->qcitp_irq, context);
+	gpio_free(context->qcitp_gpio);
+	input_free_device(context->qcitp_dev);
+	input_unregister_device(context->qcitp_dev);
+	kfree(context);
+
+	return 0;
+}
+
+static int __init qcitp_init(void)
+{
+	return i2c_add_driver(&i2ctp_driver);
+}
+
+
+static void __exit qcitp_exit(void)
+{
+	i2c_del_driver(&i2ctp_driver);
+}
+
+module_init(qcitp_init);
+module_exit(qcitp_exit);
+
+MODULE_AUTHOR("Quanta Computer Inc.");
+MODULE_DESCRIPTION("Quanta Embedded Controller I2C Touch Pad Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 1e7c563..77a0aff 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -55,6 +55,18 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called ad7877.
 
+config TOUCHSCREEN_ATMEL_MAXTOUCH
+       tristate "Atmel maXTouch based touchscreens"
+       depends on I2C
+       default n
+       help
+         Say Y here if you have an Atmel Maxtouch based touchscreen.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called maXTouch.
+
 config TOUCHSCREEN_AD7879
 	tristate "Analog Devices AD7879-1/AD7889-1 touchscreen interface"
 	help
@@ -431,6 +443,15 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called penmount.
 
+config TOUCHSCREEN_MSM
+	bool "Qualcomm MSM touchscreen controller"
+	depends on ARCH_MSM7X30 && MARIMBA_TSADC
+	default n
+	help
+	  Say Y here if you have a 4-wire resistive touchscreen panel
+	  connected to the TSSC touchscreen controller on a
+	  Qualcomm MSM/QSD based SoC.
+
 config TOUCHSCREEN_MIGOR
 	tristate "Renesas MIGO-R touchscreen"
 	depends on SH_MIGOR && I2C
@@ -457,6 +478,18 @@
 	help
 	  This enables support for Synaptics RMI over I2C based touchscreens.
 
+config TOUCHSCREEN_SYNAPTICS_RMI4_I2C
+	tristate "Synaptics i2c touchscreen(ClearPad 3000)"
+	depends on I2C
+	select SYNA_MULTI_TOUCH
+	help
+	  This enables support for Synaptics RMI over I2C based touchscreens(ClearPad 3000).
+
+config SYNA_MULTI_TOUCH
+	tristate "Synaptics i2c touchscreen(ClearPad 3000) MutilTouch support"
+	depends on TOUCHSCREEN_SYNAPTICS_RMI4_I2C
+	default y
+
 config TOUCHSCREEN_TOUCHRIGHT
 	tristate "Touchright serial touchscreen"
 	select SERIO
@@ -798,6 +831,27 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called tsc2007.
 
+config TOUCHSCREEN_MSM_LEGACY
+	default n
+	tristate "MSM Touchscreen"
+	depends on ARCH_MSM && !ARCH_MSM7X30
+	help
+	  Say Y here if you have a touchscreen interface using MSM
+	  touchscreen controller.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called msm_touch.
+
+config ANDROID_TOUCHSCREEN_MSM_HACKS
+	default y
+	depends on TOUCHSCREEN_MSM_LEGACY
+	bool "Android MSM Touchscreen hacks"
+	help
+	  Say Y here if you are running Android framework on Qualcomm
+	  MSM/QSD based Surf or FFAs. These hacks are required inorder
+	  to Android framework to receive adjusted x, y co-ordinates
+	  until proper calibration framework is in place.
+
 config TOUCHSCREEN_W90X900
 	tristate "W90P910 touchscreen driver"
 	depends on HAVE_CLK
@@ -851,4 +905,45 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called tps6507x_ts.
 
+config TOUCHSCREEN_CY8C_TS
+	tristate "Cypress TMA300-TMG200 based touchscreens"
+	depends on I2C
+	default n
+	help
+	  Say Y here if you have a Cypress TMA300/TMG200 based touchscreen.
+	  TMA300 is a multi-touch screen which can report upto 10
+	  touches at a time. TMG200 supports 2 touches.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cy8c_ts.
+
+config TOUCHSCREEN_CYTTSP_I2C_QC
+       tristate "Cypress TTSP based touchscreens"
+       depends on I2C
+       default n
+       help
+         Say Y here if you have a Cypress TTSP based touchscreen.
+         TMA300 is a multi-touch screen which can report upto 10
+         touches at a time.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called cyttsp-i2c.
+
+config TOUCHSCREEN_FT5X06
+       tristate "FocalTech touchscreens"
+       depends on I2C
+       help
+         Say Y here if you have a ft5X06 touchscreen.
+	 Ft5x06 controllers are multi touch controllers which can
+	 report 5 touches at a time.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called ft5x06_ts.
+
 endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 175d641..5a1eec7 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -12,10 +12,13 @@
 obj-$(CONFIG_TOUCHSCREEN_AD7879_I2C)	+= ad7879-i2c.o
 obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI)	+= ad7879-spi.o
 obj-$(CONFIG_TOUCHSCREEN_ADS7846)	+= ads7846.o
+obj-$(CONFIG_TOUCHSCREEN_ATMEL_MAXTOUCH)	+= atmel_maxtouch.o
 obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT)	+= atmel_mxt_ts.o
 obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC)	+= atmel_tsadcc.o
 obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR)	+= auo-pixcir-ts.o
 obj-$(CONFIG_TOUCHSCREEN_BITSY)		+= h3600_ts_input.o
+obj-$(CONFIG_TOUCHSCREEN_BU21013)       += bu21013_ts.o
+obj-$(CONFIG_TOUCHSCREEN_CYPRESS_TMG)	+= cy8c_tmg_ts.o
 obj-$(CONFIG_TOUCHSCREEN_BU21013)	+= bu21013_ts.o
 obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110)	+= cy8ctmg110_ts.o
 obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE)	+= cyttsp_core.o
@@ -26,6 +29,7 @@
 obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE)	+= hampshire.o
 obj-$(CONFIG_TOUCHSCREEN_GUNZE)		+= gunze.o
 obj-$(CONFIG_TOUCHSCREEN_EETI)		+= eeti_ts.o
+obj-$(CONFIG_TOUCHSCREEN_ELAN_I2C_8232) += elan8232_i2c.o
 obj-$(CONFIG_TOUCHSCREEN_ELO)		+= elo.o
 obj-$(CONFIG_TOUCHSCREEN_EGALAX)	+= egalax_ts.o
 obj-$(CONFIG_TOUCHSCREEN_FUJITSU)	+= fujitsu_ts.o
@@ -39,6 +43,7 @@
 obj-$(CONFIG_TOUCHSCREEN_MIGOR)		+= migor_ts.o
 obj-$(CONFIG_TOUCHSCREEN_MTOUCH)	+= mtouch.o
 obj-$(CONFIG_TOUCHSCREEN_MK712)		+= mk712.o
+obj-$(CONFIG_TOUCHSCREEN_MSM)		+= msm_ts.o
 obj-$(CONFIG_TOUCHSCREEN_HP600)		+= hp680_ts_input.o
 obj-$(CONFIG_TOUCHSCREEN_HP7XX)		+= jornada720_ts.o
 obj-$(CONFIG_TOUCHSCREEN_HTCPEN)	+= htcpen.o
@@ -52,6 +57,7 @@
 obj-$(CONFIG_TOUCHSCREEN_TI_TSCADC)	+= ti_tscadc.o
 obj-$(CONFIG_TOUCHSCREEN_TNETV107X)	+= tnetv107x-ts.o
 obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI)	+= synaptics_i2c_rmi.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_RMI4_I2C) +=synaptics/
 obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213)	+= touchit213.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT)	+= touchright.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN)	+= touchwin.o
@@ -70,3 +76,6 @@
 obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE)	+= zylonite-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_W90X900)	+= w90p910_ts.o
 obj-$(CONFIG_TOUCHSCREEN_TPS6507X)	+= tps6507x-ts.o
+obj-$(CONFIG_TOUCHSCREEN_MSM_LEGACY)		+= msm_touch.o
+obj-$(CONFIG_TOUCHSCREEN_CY8C_TS)	+= cy8c_ts.o
+obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C_QC)       += cyttsp-i2c-qc.o
diff --git a/drivers/input/touchscreen/atmel_maxtouch.c b/drivers/input/touchscreen/atmel_maxtouch.c
new file mode 100644
index 0000000..52f1f4a
--- /dev/null
+++ b/drivers/input/touchscreen/atmel_maxtouch.c
@@ -0,0 +1,2340 @@
+/*
+ *  Atmel maXTouch Touchscreen Controller Driver
+ *
+ *  
+ *  Copyright (C) 2010 Atmel Corporation
+ *  Copyright (C) 2010 Ulf Samuelsson (ulf@atmel.com)
+ *  Copyright (C) 2009 Raphael Derosso Pereira <raphaelpereira@gmail.com>
+ *
+ *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * 
+ * Driver for Atmel maXTouch family of touch controllers.
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/debugfs.h>
+#include <linux/cdev.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/module.h>
+
+#include <asm/uaccess.h>
+
+#include <linux/atmel_maxtouch.h>
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+#include <linux/earlysuspend.h>
+
+/* Early-suspend level */
+#define MXT_SUSPEND_LEVEL 1
+#endif
+
+
+#define DRIVER_VERSION "0.91a_mod"
+
+static int debug = DEBUG_INFO;
+static int comms = 0;
+module_param(debug, int, 0644);
+module_param(comms, int, 0644);
+
+MODULE_PARM_DESC(debug, "Activate debugging output");
+MODULE_PARM_DESC(comms, "Select communications mode");
+
+#define T7_DATA_SIZE 3
+
+/* Device Info descriptor */
+/* Parsed from maXTouch "Id information" inside device */
+struct mxt_device_info {
+	u8   family_id;
+	u8   variant_id;
+	u8   major;
+	u8   minor;
+	u8   build;
+	u8   num_objs;
+	u8   x_size;
+	u8   y_size;
+	char family_name[16];	 /* Family name */
+	char variant_name[16];    /* Variant name */
+	u16  num_nodes;           /* Number of sensor nodes */
+};
+
+/* object descriptor table, parsed from maXTouch "object table" */
+struct mxt_object {
+	u16 chip_addr;
+	u8  type;
+	u8  size;
+	u8  instances;
+	u8  num_report_ids;
+};
+
+
+/* Mapping from report id to object type and instance */
+struct report_id_map {
+	u8  object;
+	u8  instance;
+/*
+ * This is the first report ID belonging to object. It enables us to
+ * find out easily the touch number: each touch has different report
+ * ID (which are assigned to touches in increasing order). By
+ * subtracting the first report ID from current, we get the touch
+ * number.
+ */
+	u8  first_rid;
+};
+
+
+/* Driver datastructure */
+struct mxt_data {
+	struct i2c_client    *client;
+	struct input_dev     *input;
+	char                 phys_name[32];
+	int                  irq;
+
+	u16                  last_read_addr;
+	bool                 new_msgs;
+	u8                   *last_message;
+
+	int                  valid_irq_counter;
+	int                  invalid_irq_counter;
+	int                  irq_counter;
+	int                  message_counter;
+	int                  read_fail_counter;
+
+
+	int                  bytes_to_read;
+
+	struct delayed_work  dwork;
+	u8                   xpos_format;
+	u8                   ypos_format;
+
+	u8                   numtouch;
+
+	struct mxt_device_info	device_info;
+
+	u32		     info_block_crc;
+	u32                  configuration_crc;
+	u16                  report_id_count;
+	struct report_id_map *rid_map;
+	struct mxt_object    *object_table;
+
+	u16                  msg_proc_addr;
+	u8                   message_size;
+
+	u16                  min_x_val;
+	u16                  min_y_val;
+	u16                  max_x_val;
+	u16                  max_y_val;
+
+	int                  (*init_hw)(struct i2c_client *client);
+	int		     (*exit_hw)(struct i2c_client *client);
+	int		     (*power_on)(bool on);
+	u8                   (*valid_interrupt)(void);
+	u8                   (*read_chg)(void);
+
+	/* debugfs variables */
+	struct dentry        *debug_dir;
+	int                  current_debug_datap;
+
+	struct mutex         debug_mutex;
+	u16                  *debug_data;
+
+        /* Character device variables */
+	struct cdev          cdev;
+	struct cdev          cdev_messages;  /* 2nd Char dev for messages */
+	dev_t                dev_num;
+	struct class         *mxt_class;
+
+
+	u16                  address_pointer;
+	bool                 valid_ap;
+
+	/* Message buffer & pointers */
+	char                 *messages;
+	int                  msg_buffer_startp, msg_buffer_endp;
+        /* Put only non-touch messages to buffer if this is set */
+	char                 nontouch_msg_only; 
+	struct mutex         msg_mutex;
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+	struct early_suspend		early_suspend;
+#endif
+	u8 t7_data[T7_DATA_SIZE];
+	bool is_suspended;
+};
+/*default value, enough to read versioning*/
+#define CONFIG_DATA_SIZE	6
+static u16 t38_size = CONFIG_DATA_SIZE;
+static int mxt_read_block(struct i2c_client *client, u16 addr, u16 length,
+			  u8 *value);
+static int mxt_write_byte(struct i2c_client *client, u16 addr, u8 value);
+static int mxt_write_block(struct i2c_client *client, u16 addr, u16 length,
+			   u8 *value);
+static u8 mxt_valid_interrupt_dummy(void)
+{
+	return 1;
+}
+
+#define I2C_RETRY_COUNT 5
+#define I2C_PAYLOAD_SIZE 254
+
+/* Returns the start address of object in mXT memory. */
+#define	MXT_BASE_ADDR(object_type, mxt)					\
+	get_object_address(object_type, 0, mxt->object_table,           \
+			   mxt->device_info.num_objs)
+
+/* Maps a report ID to an object type (object type number). */
+#define	REPORT_ID_TO_OBJECT(rid, mxt)			\
+	(((rid) == 0xff) ? 0 : mxt->rid_map[rid].object)
+
+/* Maps a report ID to an object type (string). */
+#define	REPORT_ID_TO_OBJECT_NAME(rid, mxt)			\
+	object_type_name[REPORT_ID_TO_OBJECT(rid, mxt)]
+
+/* Returns non-zero if given object is a touch object */
+#define IS_TOUCH_OBJECT(object) \
+	((object == MXT_TOUCH_MULTITOUCHSCREEN_T9) || \
+	 (object == MXT_TOUCH_KEYARRAY_T15) ||	\
+	 (object == MXT_TOUCH_PROXIMITY_T23) || \
+	 (object == MXT_TOUCH_SINGLETOUCHSCREEN_T10) || \
+	 (object == MXT_TOUCH_XSLIDER_T11) || \
+	 (object == MXT_TOUCH_YSLIDER_T12) || \
+	 (object == MXT_TOUCH_XWHEEL_T13) || \
+	 (object == MXT_TOUCH_YWHEEL_T14) || \
+	 (object == MXT_TOUCH_KEYSET_T31) || \
+	 (object == MXT_TOUCH_XSLIDERSET_T32) ? 1 : 0)
+
+#define mxt_debug(level, ...) \
+	do { \
+		if (debug >= (level)) \
+			pr_debug(__VA_ARGS__); \
+	} while (0) 
+
+
+/* 
+ * Check whether we have multi-touch enabled kernel; if not, report just the
+ * first touch (on mXT224, the maximum is 10 simultaneous touches).
+ * Because just the 1st one is reported, it might seem that the screen is not
+ * responding to touch if the first touch is removed while the screen is being
+ * touched by another finger, so beware. 
+ *
+ */
+
+#ifdef ABS_MT_TRACKING_ID
+static inline void report_mt(int touch_number, int size, int x, int y, struct
+			mxt_data *mxt) {
+	input_report_abs(mxt->input, ABS_MT_TRACKING_ID, touch_number);
+	input_report_abs(mxt->input, ABS_MT_TOUCH_MAJOR, size);
+	input_report_abs(mxt->input, ABS_MT_POSITION_X, x);
+	input_report_abs(mxt->input, ABS_MT_POSITION_Y, y);
+	input_mt_sync(mxt->input);
+}
+#else
+static inline void report_mt(int touch_number, int size, int x, int y, struct
+			mxt_data *mxt) {
+	if (touch_number == 0) {
+		input_report_abs(mxt->input, ABS_TOOL_WIDTH, size);
+		input_report_abs(mxt->input, ABS_X, x);
+		input_report_abs(mxt->input, ABS_Y, y);
+	}
+}
+#endif
+
+
+static inline void report_gesture(int data, struct mxt_data *mxt)
+{
+	input_event(mxt->input, EV_MSC, MSC_GESTURE, data); 
+}
+
+
+static const u8	*object_type_name[] = {
+	[0]  = "Reserved",
+	[5]  = "GEN_MESSAGEPROCESSOR_T5",
+	[6]  = "GEN_COMMANDPROCESSOR_T6",
+	[7]  = "GEN_POWERCONFIG_T7",
+	[8]  = "GEN_ACQUIRECONFIG_T8",
+	[9]  = "TOUCH_MULTITOUCHSCREEN_T9",
+	[15] = "TOUCH_KEYARRAY_T15",
+	[17] = "SPT_COMMSCONFIG_T18",
+	[19] = "SPT_GPIOPWM_T19",
+	[20] = "PROCI_GRIPFACESUPPRESSION_T20",
+	[22] = "PROCG_NOISESUPPRESSION_T22",
+	[23] = "TOUCH_PROXIMITY_T23",
+	[24] = "PROCI_ONETOUCHGESTUREPROCESSOR_T24",
+	[25] = "SPT_SELFTEST_T25",
+	[27] = "PROCI_TWOTOUCHGESTUREPROCESSOR_T27",
+	[28] = "SPT_CTECONFIG_T28",
+	[37] = "DEBUG_DIAGNOSTICS_T37",
+	[38] = "SPT_USER_DATA_T38",
+	[40] = "PROCI_GRIPSUPPRESSION_T40",
+	[41] = "PROCI_PALMSUPPRESSION_T41",
+	[42] = "PROCI_FACESUPPRESSION_T42",
+	[43] = "SPT_DIGITIZER_T43",
+	[44] = "SPT_MESSAGECOUNT_T44",
+};
+
+
+static u16 get_object_address(uint8_t object_type,
+			      uint8_t instance,
+			      struct mxt_object *object_table,
+			      int max_objs);
+
+int mxt_write_ap(struct mxt_data *mxt, u16 ap);
+
+static int mxt_read_block_wo_addr(struct i2c_client *client,
+			   u16 length,
+				u8 *value);
+
+ssize_t debug_data_read(struct mxt_data *mxt, char *buf, size_t count, 
+			loff_t *ppos, u8 debug_command){
+	int i;
+	u16 *data;
+	u16 diagnostics_reg;
+	int offset = 0;
+	int size;
+	int read_size;
+	int error;
+	char *buf_start;
+	u16 debug_data_addr;
+	u16 page_address;
+	u8 page;
+	u8 debug_command_reg;
+
+	data = mxt->debug_data;
+	if (data == NULL)
+		return -EIO;
+
+	/* If first read after open, read all data to buffer. */
+	if (mxt->current_debug_datap == 0){
+
+		diagnostics_reg = MXT_BASE_ADDR(MXT_GEN_COMMANDPROCESSOR_T6, 
+						mxt) + 
+			          MXT_ADR_T6_DIAGNOSTIC;
+		if (count > (mxt->device_info.num_nodes * 2))
+			count = mxt->device_info.num_nodes;
+	
+		debug_data_addr = MXT_BASE_ADDR(MXT_DEBUG_DIAGNOSTIC_T37, mxt)+ 
+			          MXT_ADR_T37_DATA;
+		page_address = MXT_BASE_ADDR(MXT_DEBUG_DIAGNOSTIC_T37, mxt) +
+			       MXT_ADR_T37_PAGE;
+		error = mxt_read_block(mxt->client, page_address, 1, &page);
+		if (error < 0)
+			return error;
+		mxt_debug(DEBUG_TRACE, "debug data page = %d\n", page);		
+		while (page != 0) {
+			error = mxt_write_byte(mxt->client, 
+					diagnostics_reg, 
+					MXT_CMD_T6_PAGE_DOWN);
+			if (error < 0)
+				return error;
+			/* Wait for command to be handled; when it has, the
+			   register will be cleared. */
+			debug_command_reg = 1;
+			while (debug_command_reg != 0) {
+				error = mxt_read_block(mxt->client, 
+						diagnostics_reg, 1,
+						&debug_command_reg);
+				if (error < 0)
+					return error;
+				mxt_debug(DEBUG_TRACE, 
+					"Waiting for debug diag command "
+					"to propagate...\n");
+
+			}
+		        error = mxt_read_block(mxt->client, page_address, 1, 
+					&page);
+			if (error < 0)
+				return error;
+			mxt_debug(DEBUG_TRACE, "debug data page = %d\n", page);	
+		}
+
+		/*
+		 * Lock mutex to prevent writing some unwanted data to debug
+		 * command register. User can still write through the char 
+		 * device interface though. TODO: fix?
+		 */
+
+		mutex_lock(&mxt->debug_mutex);
+		/* Configure Debug Diagnostics object to show deltas/refs */
+		error = mxt_write_byte(mxt->client, diagnostics_reg,
+				debug_command);
+
+                /* Wait for command to be handled; when it has, the
+		 * register will be cleared. */
+		debug_command_reg = 1;
+		while (debug_command_reg != 0) {
+			error = mxt_read_block(mxt->client, 
+					diagnostics_reg, 1,
+					&debug_command_reg);
+			if (error < 0)
+				return error;
+			mxt_debug(DEBUG_TRACE, "Waiting for debug diag command "
+				"to propagate...\n");
+
+		}	
+
+		if (error < 0) {
+			printk (KERN_WARNING 
+				"Error writing to maXTouch device!\n");
+			return error;
+		}
+	
+		size = mxt->device_info.num_nodes * sizeof(u16);
+
+		while (size > 0) {
+			read_size = size > 128 ? 128 : size;
+			mxt_debug(DEBUG_TRACE, 
+				"Debug data read loop, reading %d bytes...\n",
+				read_size);
+			error = mxt_read_block(mxt->client, 
+					       debug_data_addr, 
+					       read_size, 
+					       (u8 *) &data[offset]);
+			if (error < 0) {
+				printk(KERN_WARNING 
+				       "Error reading debug data\n");
+				goto error;
+			}
+			offset += read_size/2;
+			size -= read_size;
+
+			/* Select next page */
+			error = mxt_write_byte(mxt->client, diagnostics_reg, 
+					MXT_CMD_T6_PAGE_UP);
+			if (error < 0) {
+				printk(KERN_WARNING
+					"Error writing to maXTouch device!\n");
+				goto error;
+			}
+		}
+		mutex_unlock(&mxt->debug_mutex);
+	}
+
+	buf_start = buf;
+	i = mxt->current_debug_datap;
+
+	while (((buf- buf_start) < (count - 6)) && 
+		(i < mxt->device_info.num_nodes)){
+
+		mxt->current_debug_datap++;
+		if (debug_command == MXT_CMD_T6_REFERENCES_MODE)
+			buf += sprintf(buf, "%d: %5d\n", i,
+				       (u16) le16_to_cpu(data[i]));
+		else if (debug_command == MXT_CMD_T6_DELTAS_MODE)
+			buf += sprintf(buf, "%d: %5d\n", i,
+				       (s16) le16_to_cpu(data[i]));
+		i++;
+	}
+
+	return (buf - buf_start);
+error:
+	mutex_unlock(&mxt->debug_mutex);
+	return error;
+}
+
+ssize_t deltas_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+	return debug_data_read(file->private_data, buf, count, ppos, 
+			       MXT_CMD_T6_DELTAS_MODE);
+}
+
+ssize_t refs_read(struct file *file, char *buf, size_t count, 
+			loff_t *ppos)
+{
+	return debug_data_read(file->private_data, buf, count, ppos, 
+			       MXT_CMD_T6_REFERENCES_MODE);
+}
+
+int debug_data_open(struct inode *inode, struct file *file)
+{
+	struct mxt_data *mxt;
+	int i;
+	mxt = inode->i_private;
+	if (mxt == NULL)
+		return -EIO;
+	mxt->current_debug_datap = 0;
+	mxt->debug_data = kmalloc(mxt->device_info.num_nodes * sizeof(u16),
+				  GFP_KERNEL);
+	if (mxt->debug_data == NULL)
+		return -ENOMEM;
+
+	
+	for (i = 0; i < mxt->device_info.num_nodes; i++)
+		mxt->debug_data[i] = 7777;
+	
+
+	file->private_data = mxt;
+	return 0;
+}
+
+int debug_data_release(struct inode *inode, struct file *file)
+{
+	struct mxt_data *mxt;
+	mxt = file->private_data;
+	kfree(mxt->debug_data);
+	return 0;
+}
+
+static struct file_operations delta_fops = {
+	.owner = THIS_MODULE,
+	.open = debug_data_open,
+	.release = debug_data_release,
+	.read = deltas_read,
+};
+
+static struct file_operations refs_fops = {
+	.owner = THIS_MODULE,
+	.open = debug_data_open,
+	.release = debug_data_release,
+	.read = refs_read,
+};
+
+
+int mxt_memory_open(struct inode *inode, struct file *file)
+{
+	struct mxt_data *mxt;
+	mxt = container_of(inode->i_cdev, struct mxt_data, cdev);
+	if (mxt == NULL)
+		return -EIO;
+	file->private_data = mxt;
+	return 0;
+}
+
+int mxt_message_open(struct inode *inode, struct file *file)
+{
+	struct mxt_data *mxt;
+	mxt = container_of(inode->i_cdev, struct mxt_data, cdev_messages);
+	if (mxt == NULL)
+		return -EIO;
+	file->private_data = mxt;
+	return 0;
+}
+
+
+ssize_t mxt_memory_read(struct file *file, char *buf, size_t count, 
+			loff_t *ppos)
+{
+	int i;
+	struct mxt_data *mxt;
+
+	mxt = file->private_data;
+	if (mxt->valid_ap){
+		mxt_debug(DEBUG_TRACE, "Reading %d bytes from current ap\n",
+			  (int) count);
+		i = mxt_read_block_wo_addr(mxt->client, count, (u8 *) buf);
+	} else {
+		mxt_debug(DEBUG_TRACE, "Address pointer changed since set;"
+			  "writing AP (%d) before reading %d bytes", 
+			  mxt->address_pointer, (int) count);
+		i = mxt_read_block(mxt->client, mxt->address_pointer, count,
+			           buf);
+	}
+			
+	return i;
+}
+
+ssize_t mxt_memory_write(struct file *file, const char *buf, size_t count,
+			 loff_t *ppos)
+{
+	int i;
+	int whole_blocks;
+	int last_block_size;
+	struct mxt_data *mxt;
+	u16 address;
+	
+	mxt = file->private_data;
+	address = mxt->address_pointer;
+
+	mxt_debug(DEBUG_TRACE, "mxt_memory_write entered\n");
+	whole_blocks = count / I2C_PAYLOAD_SIZE;
+	last_block_size = count % I2C_PAYLOAD_SIZE;
+
+	for (i = 0; i < whole_blocks; i++) {
+		mxt_debug(DEBUG_TRACE, "About to write to %d...", 
+			address);
+		mxt_write_block(mxt->client, address, I2C_PAYLOAD_SIZE, 
+				(u8 *) buf);
+		address += I2C_PAYLOAD_SIZE;
+		buf += I2C_PAYLOAD_SIZE;
+	}
+
+	mxt_write_block(mxt->client, address, last_block_size, (u8 *) buf);
+
+	return count;
+}
+
+static long mxt_ioctl(struct file *file,
+		     unsigned int cmd, unsigned long arg)
+{
+	int retval;
+	struct mxt_data *mxt;
+
+	retval = 0;
+	mxt = file->private_data;
+
+	switch (cmd) {
+	case MXT_SET_ADDRESS_IOCTL:
+		retval = mxt_write_ap(mxt, (u16) arg);
+		if (retval >= 0) {
+			mxt->address_pointer = (u16) arg;
+			mxt->valid_ap = 1;
+		}
+		break;
+	case MXT_RESET_IOCTL:
+		retval = mxt_write_byte(mxt->client,
+			      MXT_BASE_ADDR(MXT_GEN_COMMANDPROCESSOR_T6, mxt) +
+			      MXT_ADR_T6_RESET,
+			      1);
+		break;
+	case MXT_CALIBRATE_IOCTL:
+		retval = mxt_write_byte(mxt->client,
+			      MXT_BASE_ADDR(MXT_GEN_COMMANDPROCESSOR_T6, mxt) +
+			      MXT_ADR_T6_CALIBRATE,
+			      1);
+
+		break;
+	case MXT_BACKUP_IOCTL:
+		retval = mxt_write_byte(mxt->client,
+			      MXT_BASE_ADDR(MXT_GEN_COMMANDPROCESSOR_T6, mxt) +
+			      MXT_ADR_T6_BACKUPNV,
+			      MXT_CMD_T6_BACKUP);
+		break;
+	case MXT_NONTOUCH_MSG_IOCTL:
+		mxt->nontouch_msg_only = 1;
+		break;
+	case MXT_ALL_MSG_IOCTL:
+		mxt->nontouch_msg_only = 0;
+		break;
+	default:
+		return -EIO;
+	}
+
+	return retval;
+} 
+
+/*
+ * Copies messages from buffer to user space.
+ *
+ * NOTE: if less than (mxt->message_size * 5 + 1) bytes requested,
+ * this will return 0!
+ * 
+ */
+ssize_t mxt_message_read(struct file *file, char *buf, size_t count, 
+			 loff_t *ppos)
+{
+	int i;
+	struct mxt_data *mxt;
+	char *buf_start;
+	
+	mxt = file->private_data;
+	if (mxt == NULL)
+		return -EIO;
+	buf_start = buf;
+
+	mutex_lock(&mxt->msg_mutex);
+	/* Copy messages until buffer empty, or 'count' bytes written */
+	while ((mxt->msg_buffer_startp != mxt->msg_buffer_endp) &&
+		((buf - buf_start) < (count - (5 * mxt->message_size) - 1))){
+
+		for (i = 0; i < mxt->message_size; i++){
+			buf += sprintf(buf, "[%2X] ",
+				*(mxt->messages + mxt->msg_buffer_endp *
+					mxt->message_size + i));
+		}
+		buf += sprintf(buf, "\n");
+		if (mxt->msg_buffer_endp < MXT_MESSAGE_BUFFER_SIZE)
+			mxt->msg_buffer_endp++;
+		else
+			mxt->msg_buffer_endp = 0;
+	}
+	mutex_unlock(&mxt->msg_mutex);
+	return (buf - buf_start);
+}
+
+static struct file_operations mxt_message_fops = {
+	.owner = THIS_MODULE,
+	.open = mxt_message_open,
+	.read = mxt_message_read,
+};
+
+static struct file_operations mxt_memory_fops = {
+	.owner = THIS_MODULE,
+	.open = mxt_memory_open,
+	.read = mxt_memory_read,
+	.write = mxt_memory_write,
+	.unlocked_ioctl = mxt_ioctl,
+};
+
+
+/* Writes the address pointer (to set up following reads). */
+
+int mxt_write_ap(struct mxt_data *mxt, u16 ap)
+{
+	struct i2c_client *client;
+	__le16	le_ap = cpu_to_le16(ap);
+	client = mxt->client;
+	if (mxt != NULL)
+		mxt->last_read_addr = -1;
+	if (i2c_master_send(client, (u8 *) &le_ap, 2) == 2) {
+		mxt_debug(DEBUG_TRACE, "Address pointer set to %d\n", ap);
+		return 0;
+	} else {
+		mxt_debug(DEBUG_INFO, "Error writing address pointer!\n");
+		return -EIO;
+	}
+}
+
+
+
+/* Calculates the 24-bit CRC sum. */
+static u32 CRC_24(u32 crc, u8 byte1, u8 byte2)
+{
+	static const u32 crcpoly = 0x80001B;
+	u32 result;
+	u32 data_word;
+
+	data_word = ((((u16) byte2) << 8u) | byte1);
+	result = ((crc << 1u) ^ data_word);
+	if (result & 0x1000000)
+		result ^= crcpoly;
+	return result;
+}
+
+/* Returns object address in mXT chip, or zero if object is not found */
+static u16 get_object_address(uint8_t object_type,
+			      uint8_t instance,
+			      struct mxt_object *object_table,
+			      int max_objs)
+{
+	uint8_t object_table_index = 0;
+	uint8_t address_found = 0;
+	uint16_t address = 0;
+	struct mxt_object *obj;
+
+	while ((object_table_index < max_objs) && !address_found) {
+		obj = &object_table[object_table_index];
+		if (obj->type == object_type) {
+			address_found = 1;
+			/* Are there enough instances defined in the FW? */
+			if (obj->instances >= instance) {
+				address = obj->chip_addr +
+					  (obj->size + 1) * instance;
+			} else {
+				return 0;
+			}
+		}
+		object_table_index++;
+	}
+	return address;
+}
+
+
+/*
+ * Reads a block of bytes from given address from mXT chip. If we are
+ * reading from message window, and previous read was from message window,
+ * there's no need to write the address pointer: the mXT chip will
+ * automatically set the address pointer back to message window start.
+ */
+
+static int mxt_read_block(struct i2c_client *client,
+		   u16 addr,
+		   u16 length,
+		   u8 *value)
+{
+	struct i2c_adapter *adapter = client->adapter;
+	struct i2c_msg msg[2];
+	__le16	le_addr;
+	struct mxt_data *mxt;
+
+	mxt = i2c_get_clientdata(client);
+
+	if (mxt != NULL) {
+		if ((mxt->last_read_addr == addr) &&
+			(addr == mxt->msg_proc_addr)) {
+			if  (i2c_master_recv(client, value, length) == length)
+				return length;
+			else
+				return -EIO;
+		} else {
+			mxt->last_read_addr = addr;
+		}
+	}
+
+	mxt_debug(DEBUG_TRACE, "Writing address pointer & reading %d bytes "
+		"in on i2c transaction...\n", length); 
+
+	le_addr = cpu_to_le16(addr);
+	msg[0].addr  = client->addr;
+	msg[0].flags = 0x00;
+	msg[0].len   = 2;
+	msg[0].buf   = (u8 *) &le_addr;
+
+	msg[1].addr  = client->addr;
+	msg[1].flags = I2C_M_RD;
+	msg[1].len   = length;
+	msg[1].buf   = (u8 *) value;
+	if  (i2c_transfer(adapter, msg, 2) == 2)
+		return length;
+	else
+		return -EIO;
+
+}
+
+/* Reads a block of bytes from current address from mXT chip. */
+
+static int mxt_read_block_wo_addr(struct i2c_client *client,
+			   u16 length,
+			   u8 *value)
+{
+
+
+	if  (i2c_master_recv(client, value, length) == length) {
+		mxt_debug(DEBUG_TRACE, "I2C block read ok\n");
+		return length;
+	} else {
+		mxt_debug(DEBUG_INFO, "I2C block read failed\n");
+		return -EIO;
+	}
+
+}
+
+
+/* Writes one byte to given address in mXT chip. */
+
+static int mxt_write_byte(struct i2c_client *client, u16 addr, u8 value)
+{
+	struct {
+		__le16 le_addr;
+		u8 data;
+
+	} i2c_byte_transfer;
+
+	struct mxt_data *mxt;
+
+	mxt = i2c_get_clientdata(client);
+	if (mxt != NULL)
+		mxt->last_read_addr = -1;
+	i2c_byte_transfer.le_addr = cpu_to_le16(addr);
+	i2c_byte_transfer.data = value;
+	if  (i2c_master_send(client, (u8 *) &i2c_byte_transfer, 3) == 3)
+		return 0;
+	else
+		return -EIO;
+}
+
+
+/* Writes a block of bytes (max 256) to given address in mXT chip. */
+static int mxt_write_block(struct i2c_client *client,
+		    u16 addr,
+		    u16 length,
+		    u8 *value)
+{
+	int i;
+	struct {
+		__le16	le_addr;
+		u8	data[256];
+
+	} i2c_block_transfer;
+
+	struct mxt_data *mxt;
+
+	mxt_debug(DEBUG_TRACE, "Writing %d bytes to %d...", length, addr);
+	if (length > 256)
+		return -EINVAL;
+	mxt = i2c_get_clientdata(client);
+	if (mxt != NULL)
+		mxt->last_read_addr = -1;
+	for (i = 0; i < length; i++)
+		i2c_block_transfer.data[i] = *value++;
+	i2c_block_transfer.le_addr = cpu_to_le16(addr);
+	i = i2c_master_send(client, (u8 *) &i2c_block_transfer, length + 2);
+	if (i == (length + 2))
+		return length;
+	else
+		return -EIO;
+}
+
+/* Calculates the CRC value for mXT infoblock. */
+int calculate_infoblock_crc(u32 *crc_result, u8 *data, int crc_area_size)
+{
+	u32 crc = 0;
+	int i;
+
+	for (i = 0; i < (crc_area_size - 1); i = i + 2)
+		crc = CRC_24(crc, *(data + i), *(data + i + 1));
+	/* If uneven size, pad with zero */
+	if (crc_area_size & 0x0001)
+		crc = CRC_24(crc, *(data + i), 0);
+	/* Return only 24 bits of CRC. */
+	*crc_result = (crc & 0x00FFFFFF);
+
+	return 0;
+}
+
+/* Processes a touchscreen message. */
+void process_T9_message(u8 *message, struct mxt_data *mxt, int last_touch)
+{
+
+	struct	input_dev *input;
+	u8  status;
+	u16 xpos = 0xFFFF;
+	u16 ypos = 0xFFFF;
+	u8  touch_size = 255;
+	u8  touch_number;
+	u8  amplitude;
+	u8  report_id;
+
+	static int stored_size[10];
+	static int stored_x[10];
+	static int stored_y[10];
+	int i;
+	int active_touches = 0;
+	/*
+	 * If the 'last_touch' flag is set, we have received all the touch
+	 * messages
+	 * there are available in this cycle, so send the events for touches 
+	 * that are 
+  	 * active.
+ 	 */ 
+	if (last_touch){
+        /* TODO: For compatibility with single-touch systems, send ABS_X & 
+	 * ABS_Y */
+        /*
+        if (stored_size[0]){
+            input_report_abs(mxt->input, ABS_X, stored_x[0]);
+            input_report_abs(mxt->input, ABS_Y, stored_y[0]);
+        }*/
+
+    
+		for (i = 0; i < 10; i++){
+			if (stored_size[i]){
+				active_touches++;
+				input_report_abs(mxt->input, 
+						ABS_MT_TRACKING_ID,
+						i);
+				input_report_abs(mxt->input,
+						ABS_MT_TOUCH_MAJOR,
+						stored_size[i]);
+				input_report_abs(mxt->input,
+						ABS_MT_POSITION_X,
+						stored_x[i]);
+				input_report_abs(mxt->input,
+						ABS_MT_POSITION_Y,
+						stored_y[i]);
+				input_mt_sync(mxt->input);
+			}
+		}
+		input_report_key(mxt->input, BTN_TOUCH, !!active_touches);
+		if (active_touches == 0)
+			input_mt_sync(mxt->input);
+		input_sync(mxt->input);
+		
+	}else{	
+
+	input = mxt->input;
+	status = message[MXT_MSG_T9_STATUS];
+	report_id = message[0];
+
+	if (status & MXT_MSGB_T9_SUPPRESS) {
+		/* Touch has been suppressed by grip/face */
+		/* detection                              */
+		mxt_debug(DEBUG_TRACE, "SUPRESS");
+	} else {
+		/* Put together the 10-/12-bit coordinate values. */
+		xpos = message[MXT_MSG_T9_XPOSMSB] * 16 +
+			((message[MXT_MSG_T9_XYPOSLSB] >> 4) & 0xF);
+		ypos = message[MXT_MSG_T9_YPOSMSB] * 16 +
+			((message[MXT_MSG_T9_XYPOSLSB] >> 0) & 0xF);
+
+		if (mxt->max_x_val < 1024)
+			xpos >>= 2;
+		if (mxt->max_y_val < 1024)
+			ypos >>= 2;
+
+		touch_number = message[MXT_MSG_REPORTID] -
+			mxt->rid_map[report_id].first_rid;
+
+		stored_x[touch_number] = xpos;
+		stored_y[touch_number] = ypos;
+
+		if (status & MXT_MSGB_T9_DETECT) {
+			/*
+			 * mXT224 reports the number of touched nodes,
+			 * so the exact value for touch ellipse major
+			 * axis length in nodes would be 2*sqrt(touch_size/pi)
+			 * (assuming round touch shape), which would then need
+			 * to be scaled using information about how many sensor
+			 * lines we do have. So this is very much simplified,
+			 * but sufficient for most if not all apps?
+			 */
+			touch_size = message[MXT_MSG_T9_TCHAREA];
+			touch_size = touch_size >> 2;
+			if (!touch_size)
+				touch_size = 1;
+			/*
+             * report_mt(touch_number, touch_size, xpos, ypos, mxt);
+             */
+
+            stored_size[touch_number] = touch_size;
+
+			if (status & MXT_MSGB_T9_AMP)
+				/* Amplitude of touch has changed */
+				amplitude = message[MXT_MSG_T9_TCHAMPLITUDE];
+		}
+
+		if (status & MXT_MSGB_T9_RELEASE) {
+			/* The previously reported touch has been removed.*/
+			/* report_mt(touch_number, 0, xpos, ypos, mxt); */
+            stored_size[touch_number] = 0;
+		}
+
+		/* input_sync(input); */
+	}
+	
+	if (status & MXT_MSGB_T9_SUPPRESS) {
+		mxt_debug(DEBUG_TRACE, "SUPRESS");
+	} else {
+		if (status & MXT_MSGB_T9_DETECT) {
+			mxt_debug(DEBUG_TRACE, "DETECT:%s%s%s%s", 
+				((status & MXT_MSGB_T9_PRESS) ? " PRESS" : ""), 
+				((status & MXT_MSGB_T9_MOVE) ? " MOVE" : ""), 
+				((status & MXT_MSGB_T9_AMP) ? " AMP" : ""), 
+				((status & MXT_MSGB_T9_VECTOR) ? " VECT" : ""));
+
+		} else if (status & MXT_MSGB_T9_RELEASE) {
+			mxt_debug(DEBUG_TRACE, "RELEASE");
+		}
+	}
+	mxt_debug(DEBUG_TRACE, "X=%d, Y=%d, TOUCHSIZE=%d",
+		xpos, ypos, touch_size);
+    }
+	return;
+}
+
+
+int process_message(u8 *message, u8 object, struct mxt_data *mxt)
+{
+	struct i2c_client *client;
+	u8  status;
+	u16 xpos = 0xFFFF;
+	u16 ypos = 0xFFFF;
+	u8  event;
+	u8  direction;
+	u16 distance;
+	u8  length;
+	u8  report_id;
+	static u8 error_cond = 0; 
+
+	client = mxt->client;
+	length = mxt->message_size;
+	report_id = message[0];
+
+	if ((mxt->nontouch_msg_only == 0) ||
+	    (!IS_TOUCH_OBJECT(object))){
+		mutex_lock(&mxt->msg_mutex);
+		/* Copy the message to buffer */
+		if (mxt->msg_buffer_startp < MXT_MESSAGE_BUFFER_SIZE) {
+			mxt->msg_buffer_startp++;
+		} else {
+			mxt->msg_buffer_startp = 0;
+		}
+		
+		if (mxt->msg_buffer_startp == mxt->msg_buffer_endp) {
+			mxt_debug(DEBUG_TRACE, 
+				  "Message buf full, discarding last entry.\n");
+			if (mxt->msg_buffer_endp < MXT_MESSAGE_BUFFER_SIZE) {
+				mxt->msg_buffer_endp++;
+			} else {
+				mxt->msg_buffer_endp = 0;
+			}
+		}
+		memcpy((mxt->messages + mxt->msg_buffer_startp * length), 
+		       message,
+		       length);
+		mutex_unlock(&mxt->msg_mutex);
+	}
+
+	switch (object) {
+	case MXT_GEN_COMMANDPROCESSOR_T6:
+		status = message[1];
+
+		if (status & MXT_MSGB_T6_COMSERR) {
+			if ((!error_cond) & MXT_MSGB_T6_COMSERR){
+				dev_err(&client->dev,
+					"maXTouch checksum error\n");
+				error_cond |= MXT_MSGB_T6_COMSERR;
+			}
+		}
+		if (status & MXT_MSGB_T6_CFGERR) {
+			/* 
+			 * Configuration error. A proper configuration
+			 * needs to be written to chip and backed up.
+			 */
+			if ((!error_cond) & MXT_MSGB_T6_CFGERR){
+				dev_err(&client->dev,
+					"maXTouch configuration error\n");
+				error_cond |= MXT_MSGB_T6_CFGERR;
+			}
+		}
+		if (status & MXT_MSGB_T6_CAL) {
+			/* Calibration in action, no need to react */
+			dev_dbg(&client->dev,
+				"maXTouch calibration in progress\n");
+		}
+		if (status & MXT_MSGB_T6_SIGERR) {
+			/* 
+			 * Signal acquisition error, something is seriously
+			 * wrong, not much we can in the driver to correct
+			 * this
+			 */
+			if ((!error_cond) & MXT_MSGB_T6_SIGERR){
+				dev_err(&client->dev,
+					"maXTouch acquisition error\n");
+				error_cond |= MXT_MSGB_T6_SIGERR;
+			}
+		}
+		if (status & MXT_MSGB_T6_OFL) {
+			/*
+			 * Cycle overflow, the acquisition interval is too 
+			 * short.
+			 */
+			dev_err(&client->dev,
+				"maXTouch cycle overflow\n");
+		}
+		if (status & MXT_MSGB_T6_RESET) {
+			/* Chip has reseted, no need to react. */
+			dev_dbg(&client->dev,
+				"maXTouch chip reset\n");
+		}
+		if (status == 0) {
+			/* Chip status back to normal. */
+			dev_dbg(&client->dev,
+				"maXTouch status normal\n");
+			error_cond = 0;
+		}
+		break;
+
+	case MXT_TOUCH_MULTITOUCHSCREEN_T9:
+		process_T9_message(message, mxt, 0);
+		break;
+
+	case MXT_SPT_GPIOPWM_T19:
+		if (debug >= DEBUG_TRACE)
+			dev_info(&client->dev,
+				"Receiving GPIO message\n");
+		break;
+
+	case MXT_PROCI_GRIPFACESUPPRESSION_T20:
+		if (debug >= DEBUG_TRACE)
+			dev_info(&client->dev,
+				"Receiving face suppression msg\n");
+		break;
+
+	case MXT_PROCG_NOISESUPPRESSION_T22:
+		if (debug >= DEBUG_TRACE)
+			dev_info(&client->dev,
+				"Receiving noise suppression msg\n");
+		status = message[MXT_MSG_T22_STATUS];
+		if (status & MXT_MSGB_T22_FHCHG) {
+			if (debug >= DEBUG_TRACE)
+				dev_info(&client->dev,
+					"maXTouch: Freq changed\n");
+		}
+		if (status & MXT_MSGB_T22_GCAFERR) {
+			if (debug >= DEBUG_TRACE)
+				dev_info(&client->dev,
+					"maXTouch: High noise "
+					"level\n");
+		}
+		if (status & MXT_MSGB_T22_FHERR) {
+			if (debug >= DEBUG_TRACE)
+				dev_info(&client->dev,
+					"maXTouch: Freq changed - "
+					"Noise level too high\n");
+		}
+		break;
+
+	case MXT_PROCI_ONETOUCHGESTUREPROCESSOR_T24:
+		if (debug >= DEBUG_TRACE)
+			dev_info(&client->dev,
+				"Receiving one-touch gesture msg\n");
+
+		event = message[MXT_MSG_T24_STATUS] & 0x0F;
+		xpos = message[MXT_MSG_T24_XPOSMSB] * 16 +
+			((message[MXT_MSG_T24_XYPOSLSB] >> 4) & 0x0F);
+		ypos = message[MXT_MSG_T24_YPOSMSB] * 16 +
+			((message[MXT_MSG_T24_XYPOSLSB] >> 0) & 0x0F);
+		if (mxt->max_x_val < 1024)
+			xpos >>= 2;
+		if (mxt->max_y_val < 1024)
+			ypos >>= 2;
+		direction = message[MXT_MSG_T24_DIR];
+		distance = message[MXT_MSG_T24_DIST] +
+			   (message[MXT_MSG_T24_DIST + 1] << 16);
+
+		report_gesture((event << 24) | (direction << 16) | distance,
+			mxt);
+		report_gesture((xpos << 16) | ypos, mxt);
+
+		break;
+
+	case MXT_SPT_SELFTEST_T25:
+		if (debug >= DEBUG_TRACE)
+			dev_info(&client->dev,
+				"Receiving Self-Test msg\n");
+
+		if (message[MXT_MSG_T25_STATUS] == MXT_MSGR_T25_OK) {
+			if (debug >= DEBUG_TRACE)
+				dev_info(&client->dev,
+					"maXTouch: Self-Test OK\n");
+
+		} else  {
+			dev_err(&client->dev,
+				"maXTouch: Self-Test Failed [%02x]:"
+				"{%02x,%02x,%02x,%02x,%02x}\n",
+				message[MXT_MSG_T25_STATUS],
+				message[MXT_MSG_T25_STATUS + 0],
+				message[MXT_MSG_T25_STATUS + 1],
+				message[MXT_MSG_T25_STATUS + 2],
+				message[MXT_MSG_T25_STATUS + 3],
+				message[MXT_MSG_T25_STATUS + 4]
+				);
+		}
+		break;
+
+	case MXT_PROCI_TWOTOUCHGESTUREPROCESSOR_T27:
+		if (debug >= DEBUG_TRACE)
+			dev_info(&client->dev,
+				"Receiving 2-touch gesture message\n");
+
+		event = message[MXT_MSG_T27_STATUS] & 0xF0;
+		xpos = message[MXT_MSG_T27_XPOSMSB] * 16 +
+			((message[MXT_MSG_T27_XYPOSLSB] >> 4) & 0x0F);
+		ypos = message[MXT_MSG_T27_YPOSMSB] * 16 +
+			((message[MXT_MSG_T27_XYPOSLSB] >> 0) & 0x0F);
+		if (mxt->max_x_val < 1024)
+			xpos >>= 2;
+		if (mxt->max_y_val < 1024)
+			ypos >>= 2;
+
+		direction = message[MXT_MSG_T27_ANGLE];
+		distance = message[MXT_MSG_T27_SEPARATION] +
+			   (message[MXT_MSG_T27_SEPARATION + 1] << 16);
+
+		report_gesture((event << 24) | (direction << 16) | distance,
+			mxt);
+		report_gesture((xpos << 16) | ypos, mxt);
+
+
+		break;
+
+	case MXT_SPT_CTECONFIG_T28:
+		if (debug >= DEBUG_TRACE)
+			dev_info(&client->dev,
+				"Receiving CTE message...\n");
+		status = message[MXT_MSG_T28_STATUS];
+		if (status & MXT_MSGB_T28_CHKERR)
+			dev_err(&client->dev,
+				"maXTouch: Power-Up CRC failure\n");
+
+		break;
+	default:
+		if (debug >= DEBUG_TRACE)
+			dev_info(&client->dev,
+				"maXTouch: Unknown message!\n");
+
+		break;
+	}
+
+	return 0;
+}
+
+
+/*
+ * Processes messages when the interrupt line (CHG) is asserted. Keeps
+ * reading messages until a message with report ID 0xFF is received,
+ * which indicates that there is no more new messages.
+ *
+ */
+
+static void mxt_worker(struct work_struct *work)
+{
+	struct	mxt_data *mxt;
+	struct	i2c_client *client;
+
+	u8	*message;
+	u16	message_length;
+	u16	message_addr;
+	u8	report_id;
+	u8	object;
+	int	error;
+	int	i;
+	char    *message_string;
+	char    *message_start;
+
+	message = NULL;
+	mxt = container_of(work, struct mxt_data, dwork.work);
+	client = mxt->client;
+	message_addr = 	mxt->msg_proc_addr;
+	message_length = mxt->message_size;
+
+	if (message_length < 256) {
+		message = kmalloc(message_length, GFP_KERNEL);
+		if (message == NULL) {
+			dev_err(&client->dev, "Error allocating memory\n");
+			goto fail_worker;
+		}
+	} else {
+		dev_err(&client->dev,
+			"Message length larger than 256 bytes not supported\n");
+		goto fail_worker;
+	}
+
+	mxt_debug(DEBUG_TRACE, "maXTouch worker active:\n");
+	
+	do {
+		/* Read next message, reread on failure. */
+        /* TODO: message length, CRC included? */
+		mxt->message_counter++;
+		for (i = 1; i < I2C_RETRY_COUNT; i++) {
+			error = mxt_read_block(client,
+					       message_addr,
+					       message_length - 1,
+					       message);
+			if (error >= 0)
+				break;
+			mxt->read_fail_counter++;
+			dev_err(&client->dev,
+				"Failure reading maxTouch device\n");
+		}
+		if (error < 0) {
+			kfree(message);
+			goto fail_worker;
+		}
+		
+		if (mxt->address_pointer != message_addr)
+			mxt->valid_ap = 0;
+		report_id = message[0];
+
+		if (debug >= DEBUG_RAW) {
+			mxt_debug(DEBUG_RAW, "%s message [msg count: %08x]:",
+				  REPORT_ID_TO_OBJECT_NAME(report_id, mxt),
+				  mxt->message_counter
+			);
+			/* 5 characters per one byte */
+			message_string = kmalloc(message_length * 5, 
+						 GFP_KERNEL);
+			if (message_string == NULL) {
+				dev_err(&client->dev, 
+					"Error allocating memory\n");
+				kfree(message);
+				goto fail_worker;
+			}
+			message_start = message_string;
+			for (i = 0; i < message_length; i++) {
+				message_string += 
+					sprintf(message_string, 
+						"0x%02X ", message[i]);
+			}
+			mxt_debug(DEBUG_RAW, "%s", message_start);
+			kfree(message_start);
+		}
+		
+		if ((report_id != MXT_END_OF_MESSAGES) && (report_id != 0)) {
+			memcpy(mxt->last_message, message, message_length);
+			mxt->new_msgs = 1;
+			smp_wmb();
+			/* Get type of object and process the message */
+			object = mxt->rid_map[report_id].object;
+			process_message(message, object, mxt);
+		}
+
+		mxt_debug(DEBUG_TRACE, "chgline: %d\n", mxt->read_chg());
+	} while (comms ? (mxt->read_chg() == 0) : 
+		((report_id != MXT_END_OF_MESSAGES) && (report_id != 0)));
+
+	/* All messages processed, send the events) */
+	process_T9_message(NULL, mxt, 1);
+
+	kfree(message);
+
+fail_worker:
+	/* Make sure we just didn't miss a interrupt. */
+	if (mxt->read_chg() == 0){
+		schedule_delayed_work(&mxt->dwork, 0);
+	} else
+		enable_irq(mxt->irq);
+}
+
+
+/*
+ * The maXTouch device will signal the host about a new message by asserting
+ * the CHG line. This ISR schedules a worker routine to read the message when
+ * that happens.
+ */
+
+static irqreturn_t mxt_irq_handler(int irq, void *_mxt)
+{
+	struct mxt_data *mxt = _mxt;
+
+	mxt->irq_counter++;
+	if (mxt->valid_interrupt()) {
+		/* Send the signal only if falling edge generated the irq. */
+		disable_irq_nosync(mxt->irq);
+		schedule_delayed_work(&mxt->dwork, 0);
+		mxt->valid_irq_counter++;
+	} else {
+		mxt->invalid_irq_counter++;
+		return IRQ_NONE;
+	}
+
+	return IRQ_HANDLED;
+}
+
+
+
+/******************************************************************************/
+/* Initialization of driver                                                   */
+/******************************************************************************/
+
+static int __devinit mxt_identify(struct i2c_client *client,
+				  struct mxt_data *mxt,
+				  u8 *id_block_data)
+{
+	u8 buf[MXT_ID_BLOCK_SIZE];
+	int error;
+	int identified;
+
+	identified = 0;
+
+	/* Read Device info to check if chip is valid */
+	error = mxt_read_block(client, MXT_ADDR_INFO_BLOCK, MXT_ID_BLOCK_SIZE,
+			       (u8 *) buf);
+
+	if (error < 0) {
+		mxt->read_fail_counter++;
+		dev_err(&client->dev, "Failure accessing maXTouch device\n");
+		return -EIO;
+	}
+
+	memcpy(id_block_data, buf, MXT_ID_BLOCK_SIZE);
+
+	mxt->device_info.family_id  = buf[0];
+	mxt->device_info.variant_id = buf[1];
+	mxt->device_info.major	    = ((buf[2] >> 4) & 0x0F);
+	mxt->device_info.minor      = (buf[2] & 0x0F);
+	mxt->device_info.build	    = buf[3];
+	mxt->device_info.x_size	    = buf[4];
+	mxt->device_info.y_size	    = buf[5];
+	mxt->device_info.num_objs   = buf[6];
+	mxt->device_info.num_nodes  = mxt->device_info.x_size *
+				      mxt->device_info.y_size;
+
+	/*
+         * Check Family & Variant Info; warn if not recognized but
+         * still continue.
+         */
+
+	/* MXT224 */
+	if (mxt->device_info.family_id == MXT224_FAMILYID) {
+		strcpy(mxt->device_info.family_name, "mXT224");
+
+		if (mxt->device_info.variant_id == MXT224_CAL_VARIANTID) {
+			strcpy(mxt->device_info.variant_name, "Calibrated");
+		} else if (mxt->device_info.variant_id == 
+			MXT224_UNCAL_VARIANTID) {
+			strcpy(mxt->device_info.variant_name, "Uncalibrated");
+		} else {
+			dev_err(&client->dev,
+				"Warning: maXTouch Variant ID [%d] not "
+				"supported\n",
+				mxt->device_info.variant_id);
+			strcpy(mxt->device_info.variant_name, "UNKNOWN");
+			/* identified = -ENXIO; */
+		}
+
+	/* MXT1386 */
+	} else if (mxt->device_info.family_id == MXT1386_FAMILYID) {
+		strcpy(mxt->device_info.family_name, "mXT1386");
+
+		if (mxt->device_info.variant_id == MXT1386_CAL_VARIANTID) {
+			strcpy(mxt->device_info.variant_name, "Calibrated");
+		} else {
+			dev_err(&client->dev,
+				"Warning: maXTouch Variant ID [%d] not "
+				"supported\n",
+				mxt->device_info.variant_id);
+			strcpy(mxt->device_info.variant_name, "UNKNOWN");
+			/* identified = -ENXIO; */
+		}
+	/* Unknown family ID! */
+	} else {
+		dev_err(&client->dev,
+			"Warning: maXTouch Family ID [%d] not supported\n",
+			mxt->device_info.family_id);
+		strcpy(mxt->device_info.family_name, "UNKNOWN");
+		strcpy(mxt->device_info.variant_name, "UNKNOWN");
+		/* identified = -ENXIO; */
+	}
+
+	dev_info(
+		&client->dev,
+		"Atmel maXTouch (Family %s (%X), Variant %s (%X)) Firmware "
+		"version [%d.%d] Build %d\n",
+		mxt->device_info.family_name,
+		mxt->device_info.family_id,
+		mxt->device_info.variant_name,
+		mxt->device_info.variant_id,
+		mxt->device_info.major,
+		mxt->device_info.minor,
+		mxt->device_info.build
+	);
+	dev_dbg(
+		&client->dev,
+		"Atmel maXTouch Configuration "
+		"[X: %d] x [Y: %d]\n",
+		mxt->device_info.x_size,
+		mxt->device_info.y_size
+	);
+	return identified;
+}
+
+/*
+ * Reads the object table from maXTouch chip to get object data like
+ * address, size, report id. For Info Block CRC calculation, already read
+ * id data is passed to this function too (Info Block consists of the ID
+ * block and object table).
+ *
+ */
+static int __devinit mxt_read_object_table(struct i2c_client *client,
+					   struct mxt_data *mxt,
+					   u8 *raw_id_data)
+{
+	u16	report_id_count;
+	u8	buf[MXT_OBJECT_TABLE_ELEMENT_SIZE];
+	u8      *raw_ib_data;
+	u8	object_type;
+	u16	object_address;
+	u16	object_size;
+	u8	object_instances;
+	u8	object_report_ids;
+	u16	object_info_address;
+	u32	crc;
+	u32     calculated_crc;
+	int	i;
+	int	error;
+
+	u8	object_instance;
+	u8	object_report_id;
+	u8	report_id;
+	int     first_report_id;
+	int     ib_pointer;
+	struct mxt_object *object_table;
+
+	mxt_debug(DEBUG_TRACE, "maXTouch driver reading configuration\n");
+
+	object_table = kzalloc(sizeof(struct mxt_object) *
+			       mxt->device_info.num_objs,
+			       GFP_KERNEL);
+	if (object_table == NULL) {
+		printk(KERN_WARNING "maXTouch: Memory allocation failed!\n");
+		error = -ENOMEM;
+		goto err_object_table_alloc;
+	}
+
+	raw_ib_data = kmalloc(MXT_OBJECT_TABLE_ELEMENT_SIZE *
+			mxt->device_info.num_objs + MXT_ID_BLOCK_SIZE,
+			GFP_KERNEL);
+	if (raw_ib_data == NULL) {
+		printk(KERN_WARNING "maXTouch: Memory allocation failed!\n");
+		error = -ENOMEM;
+		goto err_ib_alloc;
+	}
+
+	/* Copy the ID data for CRC calculation. */
+	memcpy(raw_ib_data, raw_id_data, MXT_ID_BLOCK_SIZE);
+	ib_pointer = MXT_ID_BLOCK_SIZE;
+
+	mxt->object_table = object_table;
+
+	mxt_debug(DEBUG_TRACE, "maXTouch driver Memory allocated\n");
+
+	object_info_address = MXT_ADDR_OBJECT_TABLE;
+
+	report_id_count = 0;
+	for (i = 0; i < mxt->device_info.num_objs; i++) {
+		mxt_debug(DEBUG_TRACE, "Reading maXTouch at [0x%04x]: ",
+			  object_info_address);
+
+		error = mxt_read_block(client, object_info_address,
+				       MXT_OBJECT_TABLE_ELEMENT_SIZE, buf);
+
+		if (error < 0) {
+			mxt->read_fail_counter++;
+			dev_err(&client->dev,
+				"maXTouch Object %d could not be read\n", i);
+			error = -EIO;
+			goto err_object_read;
+		}
+
+		memcpy(raw_ib_data + ib_pointer, buf, 
+		       MXT_OBJECT_TABLE_ELEMENT_SIZE);
+		ib_pointer += MXT_OBJECT_TABLE_ELEMENT_SIZE;
+
+		object_type       =  buf[0];
+		object_address    = (buf[2] << 8) + buf[1];
+		object_size       =  buf[3] + 1;
+		object_instances  =  buf[4] + 1;
+		object_report_ids =  buf[5];
+		mxt_debug(DEBUG_TRACE, "Type=%03d, Address=0x%04x, "
+			  "Size=0x%02x, %d instances, %d report id's\n",
+			  object_type,
+			  object_address,
+			  object_size,
+			  object_instances,
+			  object_report_ids
+		);
+
+		if (object_type == 38)
+			t38_size = object_size;
+		/* TODO: check whether object is known and supported? */
+		
+		/* Save frequently needed info. */
+		if (object_type == MXT_GEN_MESSAGEPROCESSOR_T5) {
+			mxt->msg_proc_addr = object_address;
+			mxt->message_size = object_size;
+		}
+
+		object_table[i].type            = object_type;
+		object_table[i].chip_addr       = object_address;
+		object_table[i].size            = object_size;
+		object_table[i].instances       = object_instances;
+		object_table[i].num_report_ids  = object_report_ids;
+		report_id_count += object_instances * object_report_ids;
+
+		object_info_address += MXT_OBJECT_TABLE_ELEMENT_SIZE;
+	}
+
+	mxt->rid_map =
+		kzalloc(sizeof(struct report_id_map) * (report_id_count + 1),
+			/* allocate for report_id 0, even if not used */
+			GFP_KERNEL);
+	if (mxt->rid_map == NULL) {
+		printk(KERN_WARNING "maXTouch: Can't allocate memory!\n");
+		error = -ENOMEM;
+		goto err_rid_map_alloc;
+	}
+
+	mxt->messages = kzalloc(mxt->message_size * MXT_MESSAGE_BUFFER_SIZE,
+				GFP_KERNEL);
+	if (mxt->messages == NULL) {
+		printk(KERN_WARNING "maXTouch: Can't allocate memory!\n");
+		error = -ENOMEM;
+		goto err_msg_alloc;
+	}
+
+	mxt->last_message = kzalloc(mxt->message_size, GFP_KERNEL);
+	if (mxt->last_message == NULL) {
+		printk(KERN_WARNING "maXTouch: Can't allocate memory!\n");
+		error = -ENOMEM;
+		goto err_msg_alloc;
+	}
+
+	mxt->report_id_count = report_id_count;
+	if (report_id_count > 254) {	/* 0 & 255 are reserved */
+			dev_err(&client->dev,
+				"Too many maXTouch report id's [%d]\n",
+				report_id_count);
+			error = -ENXIO;
+			goto err_max_rid;
+	}
+
+	/* Create a mapping from report id to object type */
+	report_id = 1; /* Start from 1, 0 is reserved. */
+
+	/* Create table associating report id's with objects & instances */
+	for (i = 0; i < mxt->device_info.num_objs; i++) {
+		for (object_instance = 0;
+		     object_instance < object_table[i].instances;
+		     object_instance++){
+			first_report_id = report_id;
+			for (object_report_id = 0;
+			     object_report_id < object_table[i].num_report_ids;
+			     object_report_id++) {
+				mxt->rid_map[report_id].object =
+					object_table[i].type;
+				mxt->rid_map[report_id].instance =
+					object_instance;
+				mxt->rid_map[report_id].first_rid =
+					first_report_id;
+				report_id++;
+			}
+		}
+	}
+
+	/* Read 3 byte CRC */
+	error = mxt_read_block(client, object_info_address, 3, buf);
+	if (error < 0) {
+		mxt->read_fail_counter++;
+		dev_err(&client->dev, "Error reading CRC\n");
+	}
+
+	crc = (buf[2] << 16) | (buf[1] << 8) | buf[0];
+
+	if (calculate_infoblock_crc(&calculated_crc, raw_ib_data,
+				    ib_pointer)) {
+		printk(KERN_WARNING "Error while calculating CRC!\n");
+		calculated_crc = 0;
+	}
+	kfree(raw_ib_data);
+
+	mxt_debug(DEBUG_TRACE, "\nReported info block CRC = 0x%6X\n", crc);
+	mxt_debug(DEBUG_TRACE, "Calculated info block CRC = 0x%6X\n\n",
+		       calculated_crc);
+	
+	if (crc == calculated_crc) {
+		mxt->info_block_crc = crc;
+	} else {
+		mxt->info_block_crc = 0;
+		printk(KERN_ALERT "maXTouch: Info block CRC invalid!\n");
+	}
+
+	if (debug >= DEBUG_VERBOSE) {
+
+		dev_info(&client->dev, "maXTouch: %d Objects\n",
+				mxt->device_info.num_objs);
+
+		for (i = 0; i < mxt->device_info.num_objs; i++) {
+			dev_info(&client->dev, "Type:\t\t\t[%d]: %s\n",
+				 object_table[i].type,
+				 object_type_name[object_table[i].type]);
+			dev_info(&client->dev, "\tAddress:\t0x%04X\n",
+				object_table[i].chip_addr);
+			dev_info(&client->dev, "\tSize:\t\t%d Bytes\n",
+				 object_table[i].size);
+			dev_info(&client->dev, "\tInstances:\t%d\n",
+				 object_table[i].instances);
+			dev_info(&client->dev, "\tReport Id's:\t%d\n",
+				 object_table[i].num_report_ids);
+		}
+	}
+
+	return 0;
+
+
+err_max_rid:
+	kfree(mxt->last_message);
+err_msg_alloc:
+	kfree(mxt->rid_map);
+err_rid_map_alloc:
+err_object_read:
+	kfree(raw_ib_data);
+err_ib_alloc:
+	kfree(object_table);
+err_object_table_alloc:
+	return error;
+}
+
+#if defined(CONFIG_PM)
+static int mxt_suspend(struct device *dev)
+{
+	struct mxt_data *mxt = dev_get_drvdata(dev);
+	int error, i;
+	u8 t7_deepsl_data[T7_DATA_SIZE];
+	u16 t7_addr;
+
+	if (device_may_wakeup(dev)) {
+		enable_irq_wake(mxt->irq);
+		return 0;
+	}
+
+	disable_irq(mxt->irq);
+
+	flush_delayed_work_sync(&mxt->dwork);
+
+	for (i = 0; i < T7_DATA_SIZE; i++)
+		t7_deepsl_data[i] = 0;
+
+	t7_addr = MXT_BASE_ADDR(MXT_GEN_POWERCONFIG_T7, mxt);
+	/* save current power state values */
+	error = mxt_read_block(mxt->client, t7_addr,
+			ARRAY_SIZE(mxt->t7_data), mxt->t7_data);
+	if (error < 0)
+		goto err_enable_irq;
+
+	/* configure deep sleep mode */
+	error = mxt_write_block(mxt->client, t7_addr,
+			ARRAY_SIZE(t7_deepsl_data), t7_deepsl_data);
+	if (error < 0)
+		goto err_enable_irq;
+
+	/* power off the device */
+	if (mxt->power_on) {
+		error = mxt->power_on(false);
+		if (error) {
+			dev_err(dev, "power off failed");
+			goto err_write_block;
+		}
+	}
+	mxt->is_suspended = true;
+	return 0;
+
+err_write_block:
+	mxt_write_block(mxt->client, t7_addr,
+			ARRAY_SIZE(mxt->t7_data), mxt->t7_data);
+err_enable_irq:
+	enable_irq(mxt->irq);
+	return error;
+}
+
+static int mxt_resume(struct device *dev)
+{
+	struct mxt_data *mxt = dev_get_drvdata(dev);
+	int error;
+	u16 t7_addr;
+
+	if (device_may_wakeup(dev)) {
+		disable_irq_wake(mxt->irq);
+		return 0;
+	}
+
+	if (!mxt->is_suspended)
+		return 0;
+
+	/* power on the device */
+	if (mxt->power_on) {
+		error = mxt->power_on(true);
+		if (error) {
+			dev_err(dev, "power on failed");
+			return error;
+		}
+	}
+
+	t7_addr = MXT_BASE_ADDR(MXT_GEN_POWERCONFIG_T7, mxt);
+	/* restore the old power state values */
+	error = mxt_write_block(mxt->client, t7_addr,
+			ARRAY_SIZE(mxt->t7_data), mxt->t7_data);
+	if (error < 0)
+		goto err_write_block;
+
+	/* Make sure we just didn't miss a interrupt. */
+	if (mxt->read_chg() == 0)
+		schedule_delayed_work(&mxt->dwork, 0);
+	else
+		enable_irq(mxt->irq);
+
+	mxt->is_suspended = false;
+
+	return 0;
+
+err_write_block:
+	if (mxt->power_on)
+		mxt->power_on(false);
+	return error;
+}
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+static void mxt_early_suspend(struct early_suspend *h)
+{
+	struct mxt_data *mxt = container_of(h, struct mxt_data, early_suspend);
+
+	mxt_suspend(&mxt->client->dev);
+}
+
+static void mxt_late_resume(struct early_suspend *h)
+{
+	struct mxt_data *mxt = container_of(h, struct mxt_data, early_suspend);
+
+	mxt_resume(&mxt->client->dev);
+}
+#endif
+
+static const struct dev_pm_ops mxt_pm_ops = {
+#ifndef CONFIG_HAS_EARLYSUSPEND
+	.suspend	= mxt_suspend,
+	.resume		= mxt_resume,
+#endif
+};
+#endif
+
+static int __devinit mxt_probe(struct i2c_client *client,
+			       const struct i2c_device_id *id)
+{
+	struct mxt_data          *mxt;
+	struct maxtouch_platform_data *pdata;
+	struct input_dev         *input;
+	u8 *id_data;
+	u8 *t38_data;
+	u16 t38_addr;
+	int error;
+
+	mxt_debug(DEBUG_INFO, "mXT224: mxt_probe\n");
+
+	if (client == NULL) {
+		pr_debug("maXTouch: client == NULL\n");
+		return	-EINVAL;
+	} else if (client->adapter == NULL) {
+		pr_debug("maXTouch: client->adapter == NULL\n");
+		return	-EINVAL;
+	} else if (&client->dev == NULL) {
+		pr_debug("maXTouch: client->dev == NULL\n");
+		return	-EINVAL;
+	} else if (&client->adapter->dev == NULL) {
+		pr_debug("maXTouch: client->adapter->dev == NULL\n");
+		return	-EINVAL;
+	} else if (id == NULL) {
+		pr_debug("maXTouch: id == NULL\n");
+		return	-EINVAL;
+	}
+
+	/* Enable runtime PM ops, start in ACTIVE mode */
+	error = pm_runtime_set_active(&client->dev);
+	if (error < 0)
+		dev_dbg(&client->dev, "unable to set runtime pm state\n");
+	pm_runtime_enable(&client->dev);
+
+	mxt_debug(DEBUG_INFO, "maXTouch driver v. %s\n", DRIVER_VERSION);
+	mxt_debug(DEBUG_INFO, "\t \"%s\"\n", client->name);
+	mxt_debug(DEBUG_INFO, "\taddr:\t0x%04x\n", client->addr);
+	mxt_debug(DEBUG_INFO, "\tirq:\t%d\n", client->irq);
+	mxt_debug(DEBUG_INFO, "\tflags:\t0x%04x\n", client->flags);
+	mxt_debug(DEBUG_INFO, "\tadapter:\"%s\"\n", client->adapter->name);
+	mxt_debug(DEBUG_INFO, "\tdevice:\t\"%s\"\n", client->dev.init_name);
+
+	mxt_debug(DEBUG_TRACE, "maXTouch driver functionality OK\n");
+
+	/* Allocate structure - we need it to identify device */
+	mxt = kzalloc(sizeof(struct mxt_data), GFP_KERNEL);
+	if (mxt == NULL) {
+		dev_err(&client->dev, "insufficient memory\n");
+		error = -ENOMEM;
+		goto err_mxt_alloc;
+	}
+
+	id_data = kmalloc(MXT_ID_BLOCK_SIZE, GFP_KERNEL);
+	if (id_data == NULL) {
+		dev_err(&client->dev, "insufficient memory\n");
+		error = -ENOMEM;
+		goto err_id_alloc;
+	}
+
+	input = input_allocate_device();
+	if (!input) {
+		dev_err(&client->dev, "error allocating input device\n");
+		error = -ENOMEM;
+		goto err_input_dev_alloc;
+	}
+
+	/* Initialize Platform data */
+
+	pdata = client->dev.platform_data;
+	if (pdata == NULL) {
+		dev_err(&client->dev, "platform data is required!\n");
+		error = -EINVAL;
+		goto err_pdata;
+	}
+	if (debug >= DEBUG_TRACE)
+		printk(KERN_INFO "Platform OK: pdata = 0x%08x\n",
+		       (unsigned int) pdata);
+
+	mxt->is_suspended = false;
+	mxt->read_fail_counter = 0;
+	mxt->message_counter   = 0;
+
+	if (pdata->min_x)
+		mxt->min_x_val = pdata->min_x;
+	else
+		mxt->min_x_val = 0;
+
+	if (pdata->min_y)
+		mxt->min_y_val = pdata->min_y;
+	else
+		mxt->min_y_val = 0;
+
+	mxt->max_x_val         = pdata->max_x;
+	mxt->max_y_val         = pdata->max_y;
+
+	/* Get data that is defined in board specific code. */
+	mxt->init_hw = pdata->init_platform_hw;
+	mxt->exit_hw = pdata->exit_platform_hw;
+	mxt->power_on = pdata->power_on;
+	mxt->read_chg = pdata->read_chg;
+
+	if (pdata->valid_interrupt != NULL)
+		mxt->valid_interrupt = pdata->valid_interrupt;
+	else
+		mxt->valid_interrupt = mxt_valid_interrupt_dummy;
+
+	if (mxt->init_hw) {
+		error = mxt->init_hw(client);
+		if (error) {
+			dev_err(&client->dev, "hw init failed");
+			goto err_init_hw;
+		}
+	}
+
+	/* power on the device */
+	if (mxt->power_on) {
+		error = mxt->power_on(true);
+		if (error) {
+			dev_err(&client->dev, "power on failed");
+			goto err_pwr_on;
+		}
+	}
+
+	if (debug >= DEBUG_TRACE)
+		printk(KERN_INFO "maXTouch driver identifying chip\n");
+
+	if (mxt_identify(client, mxt, id_data) < 0) {
+		dev_err(&client->dev, "Chip could not be identified\n");
+		error = -ENODEV;
+		goto err_identify;
+	}
+	/* Chip is valid and active. */
+	if (debug >= DEBUG_TRACE)
+		printk(KERN_INFO "maXTouch driver allocating input device\n");
+
+	mxt->client = client;
+	mxt->input  = input;
+
+	INIT_DELAYED_WORK(&mxt->dwork, mxt_worker);
+	mutex_init(&mxt->debug_mutex);
+	mutex_init(&mxt->msg_mutex);
+	mxt_debug(DEBUG_TRACE, "maXTouch driver creating device name\n");
+
+	snprintf(
+		mxt->phys_name,
+		sizeof(mxt->phys_name),
+		"%s/input0",
+		dev_name(&client->dev)
+	);
+	input->name = "Atmel maXTouch Touchscreen controller";
+	input->phys = mxt->phys_name;
+	input->id.bustype = BUS_I2C;
+	input->dev.parent = &client->dev;
+
+	mxt_debug(DEBUG_INFO, "maXTouch name: \"%s\"\n", input->name);
+	mxt_debug(DEBUG_INFO, "maXTouch phys: \"%s\"\n", input->phys);
+	mxt_debug(DEBUG_INFO, "maXTouch driver setting abs parameters\n");
+	
+	set_bit(BTN_TOUCH, input->keybit);
+
+	/* Single touch */
+	input_set_abs_params(input, ABS_X, mxt->min_x_val,
+				mxt->max_x_val, 0, 0);
+	input_set_abs_params(input, ABS_Y, mxt->min_y_val,
+				mxt->max_y_val, 0, 0);
+	input_set_abs_params(input, ABS_PRESSURE, 0, MXT_MAX_REPORTED_PRESSURE,
+			     0, 0);
+	input_set_abs_params(input, ABS_TOOL_WIDTH, 0, MXT_MAX_REPORTED_WIDTH,
+			     0, 0);
+
+	/* Multitouch */
+	input_set_abs_params(input, ABS_MT_POSITION_X, mxt->min_x_val,
+				mxt->max_x_val, 0, 0);
+	input_set_abs_params(input, ABS_MT_POSITION_Y, mxt->min_y_val,
+				mxt->max_y_val, 0, 0);
+	input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, MXT_MAX_TOUCH_SIZE,
+			     0, 0);
+	input_set_abs_params(input, ABS_MT_TRACKING_ID, 0, MXT_MAX_NUM_TOUCHES,
+			     0, 0);
+	
+	__set_bit(EV_ABS, input->evbit);
+	__set_bit(EV_SYN, input->evbit);
+	__set_bit(EV_KEY, input->evbit);
+	__set_bit(EV_MSC, input->evbit);
+	input->mscbit[0] = BIT_MASK(MSC_GESTURE);
+
+	mxt_debug(DEBUG_TRACE, "maXTouch driver setting client data\n");
+	i2c_set_clientdata(client, mxt);
+	mxt_debug(DEBUG_TRACE, "maXTouch driver setting drv data\n");
+	input_set_drvdata(input, mxt);
+	mxt_debug(DEBUG_TRACE, "maXTouch driver input register device\n");
+	error = input_register_device(mxt->input);
+	if (error < 0) {
+		dev_err(&client->dev,
+			"Failed to register input device\n");
+		goto err_register_device;
+	}
+
+	error = mxt_read_object_table(client, mxt, id_data);
+	if (error < 0)
+		goto err_read_ot;
+
+
+	/* Create debugfs entries. */
+	mxt->debug_dir = debugfs_create_dir("maXTouch", NULL);
+	if (mxt->debug_dir == ERR_PTR(-ENODEV)) {
+		/* debugfs is not enabled. */
+		printk(KERN_WARNING "debugfs not enabled in kernel\n");
+	} else if (mxt->debug_dir == NULL) {
+		printk(KERN_WARNING "error creating debugfs dir\n");
+	} else {
+		mxt_debug(DEBUG_TRACE, "created \"maXTouch\" debugfs dir\n");
+		
+		debugfs_create_file("deltas", S_IRUSR, mxt->debug_dir, mxt, 
+				    &delta_fops);
+		debugfs_create_file("refs", S_IRUSR, mxt->debug_dir, mxt,
+				    &refs_fops);
+	}
+
+        /* Create character device nodes for reading & writing registers */
+	mxt->mxt_class = class_create(THIS_MODULE, "maXTouch_memory");
+	if (IS_ERR(mxt->mxt_class)){
+	  printk(KERN_WARNING "class create failed! exiting...");
+	  goto err_class_create;
+	  
+	}
+	/* 2 numbers; one for memory and one for messages */
+	error = alloc_chrdev_region(&mxt->dev_num, 0, 2, 
+				    "maXTouch_memory");
+	mxt_debug(DEBUG_VERBOSE, 
+		"device number %d allocated!\n", MAJOR(mxt->dev_num));
+	if (error){
+		printk(KERN_WARNING "Error registering device\n");
+	}
+	cdev_init(&mxt->cdev, &mxt_memory_fops);
+	cdev_init(&mxt->cdev_messages, &mxt_message_fops);
+	
+	mxt_debug(DEBUG_VERBOSE, "cdev initialized\n");
+	mxt->cdev.owner = THIS_MODULE;
+	mxt->cdev_messages.owner = THIS_MODULE;
+	
+	error = cdev_add(&mxt->cdev, mxt->dev_num, 1);
+	if (error){
+		printk(KERN_WARNING "Bad cdev\n");
+	}
+	
+	error = cdev_add(&mxt->cdev_messages, mxt->dev_num + 1, 1);
+	if (error){
+		printk(KERN_WARNING "Bad cdev\n");
+	}
+	
+	mxt_debug(DEBUG_VERBOSE, "cdev added\n");
+	
+	device_create(mxt->mxt_class, NULL, MKDEV(MAJOR(mxt->dev_num), 0), NULL,
+		"maXTouch");
+
+	device_create(mxt->mxt_class, NULL, MKDEV(MAJOR(mxt->dev_num), 1), NULL,
+		"maXTouch_messages");
+
+	mxt->msg_buffer_startp = 0;
+	mxt->msg_buffer_endp = 0;
+
+	/* Allocate the interrupt */
+	mxt_debug(DEBUG_TRACE, "maXTouch driver allocating interrupt...\n");
+	mxt->irq = client->irq;
+	mxt->valid_irq_counter = 0;
+	mxt->invalid_irq_counter = 0;
+	mxt->irq_counter = 0;
+	if (mxt->irq) {
+		/* Try to request IRQ with falling edge first. This is
+		 * not always supported. If it fails, try with any edge. */
+		error = request_irq(mxt->irq,
+				    mxt_irq_handler,
+				    IRQF_TRIGGER_FALLING,
+				    client->dev.driver->name,
+				    mxt);
+		if (error < 0) {
+			/* TODO: why only 0 works on STK1000? */
+			error = request_irq(mxt->irq,
+					    mxt_irq_handler,
+					    0,
+					    client->dev.driver->name,
+					    mxt);
+		}
+
+		if (error < 0) {
+			dev_err(&client->dev,
+				"failed to allocate irq %d\n", mxt->irq);
+			goto err_irq;
+		}
+	}
+
+        if (debug > DEBUG_INFO)
+		dev_info(&client->dev, "touchscreen, irq %d\n", mxt->irq);
+		
+	t38_data = kmalloc(t38_size*sizeof(u8), GFP_KERNEL);
+
+	if (t38_data == NULL) {
+		dev_err(&client->dev, "insufficient memory\n");
+		error = -ENOMEM;
+		goto err_t38;
+	}
+
+	t38_addr = MXT_BASE_ADDR(MXT_USER_INFO_T38, mxt);
+	mxt_read_block(client, t38_addr, t38_size, t38_data);
+	dev_info(&client->dev, "VERSION:%02x.%02x.%02x, DATE: %d/%d/%d\n",
+		t38_data[0], t38_data[1], t38_data[2],
+		t38_data[3], t38_data[4], t38_data[5]);
+
+	/* Schedule a worker routine to read any messages that might have
+	 * been sent before interrupts were enabled. */
+	cancel_delayed_work(&mxt->dwork);
+	disable_irq(mxt->irq);
+	schedule_delayed_work(&mxt->dwork, 0);
+	kfree(t38_data);
+	kfree(id_data);
+
+	device_init_wakeup(&client->dev, pdata->wakeup);
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+	mxt->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN +
+						MXT_SUSPEND_LEVEL;
+	mxt->early_suspend.suspend = mxt_early_suspend;
+	mxt->early_suspend.resume = mxt_late_resume;
+	register_early_suspend(&mxt->early_suspend);
+#endif
+
+	return 0;
+
+err_t38:
+	free_irq(mxt->irq, mxt);
+err_irq:
+	kfree(mxt->rid_map);
+	kfree(mxt->object_table);
+	kfree(mxt->last_message);
+err_class_create:
+	if (mxt->debug_dir)
+		debugfs_remove(mxt->debug_dir);
+	kfree(mxt->last_message);
+	kfree(mxt->rid_map);
+	kfree(mxt->object_table);
+err_read_ot:
+	input_unregister_device(mxt->input);
+	mxt->input = NULL;
+err_register_device:
+	mutex_destroy(&mxt->debug_mutex);
+	mutex_destroy(&mxt->msg_mutex);
+err_identify:
+	if (mxt->power_on)
+		mxt->power_on(false);
+err_pwr_on:
+	if (mxt->exit_hw != NULL)
+		mxt->exit_hw(client);
+err_init_hw:
+err_pdata:
+	input_free_device(input);
+err_input_dev_alloc:
+	kfree(id_data);
+err_id_alloc:
+	kfree(mxt);
+err_mxt_alloc:
+	pm_runtime_set_suspended(&client->dev);
+	pm_runtime_disable(&client->dev);
+	return error;
+}
+
+static int __devexit mxt_remove(struct i2c_client *client)
+{
+	struct mxt_data *mxt;
+
+	pm_runtime_set_suspended(&client->dev);
+	pm_runtime_disable(&client->dev);
+
+	mxt = i2c_get_clientdata(client);
+
+	/* Remove debug dir entries */
+	debugfs_remove_recursive(mxt->debug_dir);
+
+	device_init_wakeup(&client->dev, 0);
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+	unregister_early_suspend(&mxt->early_suspend);
+#endif
+
+	if (mxt != NULL) {
+		if (mxt->power_on)
+			mxt->power_on(false);
+
+		if (mxt->exit_hw != NULL)
+			mxt->exit_hw(client);
+
+		if (mxt->irq) {
+			free_irq(mxt->irq, mxt);
+		}
+
+		unregister_chrdev_region(mxt->dev_num, 2);
+		device_destroy(mxt->mxt_class, MKDEV(MAJOR(mxt->dev_num), 0));
+		device_destroy(mxt->mxt_class, MKDEV(MAJOR(mxt->dev_num), 1));
+		cdev_del(&mxt->cdev);
+		cdev_del(&mxt->cdev_messages);
+		cancel_delayed_work_sync(&mxt->dwork);
+		input_unregister_device(mxt->input);
+		class_destroy(mxt->mxt_class);
+		debugfs_remove(mxt->debug_dir);
+
+		kfree(mxt->rid_map);
+		kfree(mxt->object_table);
+		kfree(mxt->last_message);
+	}
+	kfree(mxt);
+
+	i2c_set_clientdata(client, NULL);
+	if (debug >= DEBUG_TRACE)
+		dev_info(&client->dev, "Touchscreen unregistered\n");
+
+	return 0;
+}
+
+static const struct i2c_device_id mxt_idtable[] = {
+	{"maXTouch", 0,},
+	{ }
+};
+
+MODULE_DEVICE_TABLE(i2c, mxt_idtable);
+
+static struct i2c_driver mxt_driver = {
+	.driver = {
+		.name	= "maXTouch",
+		.owner  = THIS_MODULE,
+#if defined(CONFIG_PM)
+		.pm = &mxt_pm_ops,
+#endif
+	},
+
+	.id_table	= mxt_idtable,
+	.probe		= mxt_probe,
+	.remove		= __devexit_p(mxt_remove),
+};
+
+static int __init mxt_init(void)
+{
+	int err;
+	err = i2c_add_driver(&mxt_driver);
+	if (err) {
+		printk(KERN_WARNING "Adding maXTouch driver failed "
+		       "(errno = %d)\n", err);
+	} else {
+		mxt_debug(DEBUG_TRACE, "Successfully added driver %s\n",
+		          mxt_driver.driver.name);
+	}
+	return err;
+}
+
+static void __exit mxt_cleanup(void)
+{
+	i2c_del_driver(&mxt_driver);
+}
+
+
+module_init(mxt_init);
+module_exit(mxt_cleanup);
+
+MODULE_AUTHOR("Iiro Valkonen");
+MODULE_DESCRIPTION("Driver for Atmel maXTouch Touchscreen Controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 19d4ea6..7fe5498 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -3,6 +3,7 @@
  *
  * Copyright (C) 2010 Samsung Electronics Co.Ltd
  * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute  it and/or modify it
  * under  the terms of  the GNU General  Public License as published by the
@@ -20,21 +21,54 @@
 #include <linux/input/mt.h>
 #include <linux/interrupt.h>
 #include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/regulator/consumer.h>
+#include <linux/string.h>
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+#include <linux/earlysuspend.h>
+/* Early-suspend level */
+#define MXT_SUSPEND_LEVEL 1
+#endif
+
+/* Family ID */
+#define MXT224_ID	0x80
+#define MXT224E_ID	0x81
+#define MXT1386_ID	0xA0
 
 /* Version */
 #define MXT_VER_20		20
 #define MXT_VER_21		21
 #define MXT_VER_22		22
 
-/* Slave addresses */
-#define MXT_APP_LOW		0x4a
-#define MXT_APP_HIGH		0x4b
-#define MXT_BOOT_LOW		0x24
-#define MXT_BOOT_HIGH		0x25
+/* I2C slave address pairs */
+struct mxt_address_pair {
+	int bootloader;
+	int application;
+};
+
+static const struct mxt_address_pair mxt_slave_addresses[] = {
+	{ 0x24, 0x4a },
+	{ 0x25, 0x4b },
+	{ 0x25, 0x4b },
+	{ 0x26, 0x4c },
+	{ 0x27, 0x4d },
+	{ 0x34, 0x5a },
+	{ 0x35, 0x5b },
+	{ 0 },
+};
+
+enum mxt_device_state { INIT, APPMODE, BOOTLOADER };
 
 /* Firmware */
 #define MXT_FW_NAME		"maxtouch.fw"
 
+/* Firmware frame size including frame data and CRC */
+#define MXT_SINGLE_FW_MAX_FRAME_SIZE	278
+#define MXT_CHIPSET_FW_MAX_FRAME_SIZE	534
+
 /* Registers */
 #define MXT_FAMILY_ID		0x00
 #define MXT_VARIANT_ID		0x01
@@ -66,6 +100,7 @@
 #define MXT_PROCI_PALM_T41		41
 #define MXT_PROCI_TOUCHSUPPRESSION_T42	42
 #define MXT_PROCI_STYLUS_T47		47
+#define MXT_PROCI_SHIELDLESS_T56	56
 #define MXT_PROCG_NOISESUPPRESSION_T48	48
 #define MXT_SPT_COMMSCONFIG_T18		18
 #define MXT_SPT_GPIOPWM_T19		19
@@ -97,7 +132,7 @@
 #define MXT_ACQUIRE_ATCHCALST	6
 #define MXT_ACQUIRE_ATCHCALSTHR	7
 
-/* MXT_TOUCH_MULTI_T9 field */
+/* MXT_TOUCH_MULT_T9 field */
 #define MXT_TOUCH_CTRL		0
 #define MXT_TOUCH_XORIGIN	1
 #define MXT_TOUCH_YORIGIN	2
@@ -172,13 +207,35 @@
 #define MXT_VOLTAGE_DEFAULT	2700000
 #define MXT_VOLTAGE_STEP	10000
 
+/* Analog voltage @2.7 V */
+#define MXT_VTG_MIN_UV		2700000
+#define MXT_VTG_MAX_UV		3300000
+#define MXT_ACTIVE_LOAD_UA	15000
+#define MXT_LPM_LOAD_UA		10
+/* Digital voltage @1.8 V */
+#define MXT_VTG_DIG_MIN_UV	1800000
+#define MXT_VTG_DIG_MAX_UV	1800000
+#define MXT_ACTIVE_LOAD_DIG_UA	10000
+#define MXT_LPM_LOAD_DIG_UA	10
+
+#define MXT_I2C_VTG_MIN_UV	1800000
+#define MXT_I2C_VTG_MAX_UV	1800000
+#define MXT_I2C_LOAD_UA		10000
+#define MXT_I2C_LPM_LOAD_UA	10
+
 /* Define for MXT_GEN_COMMAND_T6 */
 #define MXT_BOOT_VALUE		0xa5
 #define MXT_BACKUP_VALUE	0x55
 #define MXT_BACKUP_TIME		25	/* msec */
-#define MXT_RESET_TIME		65	/* msec */
+#define MXT224_RESET_TIME	65	/* msec */
+#define MXT224E_RESET_TIME	22	/* msec */
+#define MXT1386_RESET_TIME	250	/* msec */
+#define MXT_RESET_TIME		250	/* msec */
+#define MXT_RESET_NOCHGREAD	400	/* msec */
 
-#define MXT_FWRESET_TIME	175	/* msec */
+#define MXT_FWRESET_TIME	1000	/* msec */
+
+#define MXT_WAKE_TIME		25
 
 /* Command to unlock bootloader */
 #define MXT_UNLOCK_CMD_MSB	0xaa
@@ -192,6 +249,8 @@
 #define MXT_FRAME_CRC_PASS	0x04
 #define MXT_APP_CRC_FAIL	0x40	/* valid 7 8 bit only */
 #define MXT_BOOT_STATUS_MASK	0x3f
+#define MXT_BOOT_EXTENDED_ID	(1 << 5)
+#define MXT_BOOT_ID_MASK	0x1f
 
 /* Touch status */
 #define MXT_SUPPRESS		(1 << 1)
@@ -212,6 +271,17 @@
 
 #define MXT_MAX_FINGER		10
 
+#define T7_DATA_SIZE		3
+#define MXT_MAX_RW_TRIES	3
+#define MXT_BLOCK_SIZE		256
+#define MXT_CFG_VERSION_LEN	3
+#define MXT_CFG_VERSION_EQUAL	0
+#define MXT_CFG_VERSION_LESS	1
+#define MXT_CFG_VERSION_GREATER	2
+
+#define MXT_DEBUGFS_DIR		"atmel_mxt_ts"
+#define MXT_DEBUGFS_FILE	"object"
+
 struct mxt_info {
 	u8 family_id;
 	u8 variant_id;
@@ -252,14 +322,36 @@
 	struct i2c_client *client;
 	struct input_dev *input_dev;
 	const struct mxt_platform_data *pdata;
+	const struct mxt_config_info *config_info;
+	enum mxt_device_state state;
 	struct mxt_object *object_table;
 	struct mxt_info info;
 	struct mxt_finger finger[MXT_MAX_FINGER];
 	unsigned int irq;
-	unsigned int max_x;
-	unsigned int max_y;
+	struct regulator *vcc_ana;
+	struct regulator *vcc_dig;
+	struct regulator *vcc_i2c;
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+	struct early_suspend early_suspend;
+#endif
+
+	u8 t7_data[T7_DATA_SIZE];
+	u16 t7_start_addr;
+	u32 keyarray_old;
+	u32 keyarray_new;
+	u8 t9_max_reportid;
+	u8 t9_min_reportid;
+	u8 t15_max_reportid;
+	u8 t15_min_reportid;
+	u8 cfg_version[MXT_CFG_VERSION_LEN];
+	int cfg_version_idx;
+	int t38_start_addr;
+	bool update_cfg;
+	const char *fw_name;
 };
 
+static struct dentry *debug_base;
+
 static bool mxt_object_readable(unsigned int type)
 {
 	switch (type) {
@@ -280,6 +372,7 @@
 	case MXT_PROCI_PALM_T41:
 	case MXT_PROCI_TOUCHSUPPRESSION_T42:
 	case MXT_PROCI_STYLUS_T47:
+	case MXT_PROCI_SHIELDLESS_T56:
 	case MXT_PROCG_NOISESUPPRESSION_T48:
 	case MXT_SPT_COMMSCONFIG_T18:
 	case MXT_SPT_GPIOPWM_T19:
@@ -312,11 +405,13 @@
 	case MXT_PROCI_PALM_T41:
 	case MXT_PROCI_TOUCHSUPPRESSION_T42:
 	case MXT_PROCI_STYLUS_T47:
+	case MXT_PROCI_SHIELDLESS_T56:
 	case MXT_PROCG_NOISESUPPRESSION_T48:
 	case MXT_SPT_COMMSCONFIG_T18:
 	case MXT_SPT_GPIOPWM_T19:
 	case MXT_SPT_SELFTEST_T25:
 	case MXT_SPT_CTECONFIG_T28:
+	case MXT_SPT_USERDATA_T38:
 	case MXT_SPT_DIGITIZER_T43:
 	case MXT_SPT_CTECONFIG_T46:
 		return true;
@@ -339,8 +434,116 @@
 	dev_dbg(dev, "checksum:\t0x%x\n", message->checksum);
 }
 
+static int mxt_switch_to_bootloader_address(struct mxt_data *data)
+{
+	int i;
+	struct i2c_client *client = data->client;
+
+	if (data->state == BOOTLOADER) {
+		dev_err(&client->dev, "Already in BOOTLOADER state\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; mxt_slave_addresses[i].application != 0;  i++) {
+		if (mxt_slave_addresses[i].application == client->addr) {
+			dev_info(&client->dev, "Changing to bootloader address: "
+				"%02x -> %02x",
+				client->addr,
+				mxt_slave_addresses[i].bootloader);
+
+			client->addr = mxt_slave_addresses[i].bootloader;
+			data->state = BOOTLOADER;
+			return 0;
+		}
+	}
+
+	dev_err(&client->dev, "Address 0x%02x not found in address table",
+								client->addr);
+	return -EINVAL;
+}
+
+static int mxt_switch_to_appmode_address(struct mxt_data *data)
+{
+	int i;
+	struct i2c_client *client = data->client;
+
+	if (data->state == APPMODE) {
+		dev_err(&client->dev, "Already in APPMODE state\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; mxt_slave_addresses[i].application != 0;  i++) {
+		if (mxt_slave_addresses[i].bootloader == client->addr) {
+			dev_info(&client->dev,
+				"Changing to application mode address: "
+							"0x%02x -> 0x%02x",
+				client->addr,
+				mxt_slave_addresses[i].application);
+
+			client->addr = mxt_slave_addresses[i].application;
+			data->state = APPMODE;
+			return 0;
+		}
+	}
+
+	dev_err(&client->dev, "Address 0x%02x not found in address table",
+								client->addr);
+	return -EINVAL;
+}
+
+static int mxt_get_bootloader_version(struct i2c_client *client, u8 val)
+{
+	u8 buf[3];
+
+	if (val | MXT_BOOT_EXTENDED_ID)	{
+		dev_dbg(&client->dev,
+				"Retrieving extended mode ID information");
+
+		if (i2c_master_recv(client, &buf[0], 3) != 3) {
+			dev_err(&client->dev, "%s: i2c recv failed\n",
+								__func__);
+			return -EIO;
+		}
+
+		dev_info(&client->dev, "Bootloader ID:%d Version:%d",
+			buf[1], buf[2]);
+
+		return buf[0];
+	} else {
+		dev_info(&client->dev, "Bootloader ID:%d",
+			val & MXT_BOOT_ID_MASK);
+
+		return val;
+	}
+}
+
+static int mxt_get_bootloader_id(struct i2c_client *client)
+{
+	u8 val;
+	u8 buf[3];
+
+	if (i2c_master_recv(client, &val, 1) != 1) {
+		dev_err(&client->dev, "%s: i2c recv failed\n", __func__);
+		return -EIO;
+	}
+
+	if (val | MXT_BOOT_EXTENDED_ID)	{
+		if (i2c_master_recv(client, &buf[0], 3) != 3) {
+			dev_err(&client->dev, "%s: i2c recv failed\n",
+								__func__);
+			return -EIO;
+		}
+		return buf[1];
+	} else {
+		dev_info(&client->dev, "Bootloader ID:%d",
+			val & MXT_BOOT_ID_MASK);
+
+		return val & MXT_BOOT_ID_MASK;
+	}
+}
+
 static int mxt_check_bootloader(struct i2c_client *client,
-				     unsigned int state)
+				unsigned int state)
 {
 	u8 val;
 
@@ -352,19 +555,28 @@
 
 	switch (state) {
 	case MXT_WAITING_BOOTLOAD_CMD:
+		val = mxt_get_bootloader_version(client, val);
+		val &= ~MXT_BOOT_STATUS_MASK;
+		break;
 	case MXT_WAITING_FRAME_DATA:
+	case MXT_APP_CRC_FAIL:
 		val &= ~MXT_BOOT_STATUS_MASK;
 		break;
 	case MXT_FRAME_CRC_PASS:
 		if (val == MXT_FRAME_CRC_CHECK)
 			goto recheck;
+		if (val == MXT_FRAME_CRC_FAIL) {
+			dev_err(&client->dev, "Bootloader CRC fail\n");
+			return -EINVAL;
+		}
 		break;
 	default:
 		return -EINVAL;
 	}
 
 	if (val != state) {
-		dev_err(&client->dev, "Unvalid bootloader mode state\n");
+		dev_err(&client->dev, "Invalid bootloader mode state %X\n",
+			val);
 		return -EINVAL;
 	}
 
@@ -387,7 +599,7 @@
 }
 
 static int mxt_fw_write(struct i2c_client *client,
-			     const u8 *data, unsigned int frame_size)
+			const u8 *data, unsigned int frame_size)
 {
 	if (i2c_master_send(client, data, frame_size) != frame_size) {
 		dev_err(&client->dev, "%s: i2c send failed\n", __func__);
@@ -402,6 +614,7 @@
 {
 	struct i2c_msg xfer[2];
 	u8 buf[2];
+	int i = 0;
 
 	buf[0] = reg & 0xff;
 	buf[1] = (reg >> 8) & 0xff;
@@ -418,12 +631,14 @@
 	xfer[1].len = len;
 	xfer[1].buf = val;
 
-	if (i2c_transfer(client->adapter, xfer, 2) != 2) {
-		dev_err(&client->dev, "%s: i2c transfer failed\n", __func__);
-		return -EIO;
-	}
+	do {
+		if (i2c_transfer(client->adapter, xfer, 2) == 2)
+			return 0;
+		msleep(MXT_WAKE_TIME);
+	} while (++i < MXT_MAX_RW_TRIES);
 
-	return 0;
+	dev_err(&client->dev, "%s: i2c transfer failed\n", __func__);
+	return -EIO;
 }
 
 static int mxt_read_reg(struct i2c_client *client, u16 reg, u8 *val)
@@ -431,20 +646,33 @@
 	return __mxt_read_reg(client, reg, 1, val);
 }
 
+static int __mxt_write_reg(struct i2c_client *client,
+		    u16 addr, u16 length, u8 *value)
+{
+	u8 buf[MXT_BLOCK_SIZE + 2];
+	int i, tries = 0;
+
+	if (length > MXT_BLOCK_SIZE)
+		return -EINVAL;
+
+	buf[0] = addr & 0xff;
+	buf[1] = (addr >> 8) & 0xff;
+	for (i = 0; i < length; i++)
+		buf[i + 2] = *value++;
+
+	do {
+		if (i2c_master_send(client, buf, length + 2) == (length + 2))
+			return 0;
+		msleep(MXT_WAKE_TIME);
+	} while (++tries < MXT_MAX_RW_TRIES);
+
+	dev_err(&client->dev, "%s: i2c send failed\n", __func__);
+	return -EIO;
+}
+
 static int mxt_write_reg(struct i2c_client *client, u16 reg, u8 val)
 {
-	u8 buf[3];
-
-	buf[0] = reg & 0xff;
-	buf[1] = (reg >> 8) & 0xff;
-	buf[2] = val;
-
-	if (i2c_master_send(client, buf, 3) != 3) {
-		dev_err(&client->dev, "%s: i2c send failed\n", __func__);
-		return -EIO;
-	}
-
-	return 0;
+	return __mxt_write_reg(client, reg, 1, &val);
 }
 
 static int mxt_read_object_table(struct i2c_client *client,
@@ -526,6 +754,17 @@
 			continue;
 
 		input_mt_slot(input_dev, id);
+		/* Firmware reports min/max values when the touch is
+		 * outside screen area. Send a release event in
+		 * such cases to avoid unwanted touches.
+		 */
+		if (finger[id].x <= data->pdata->panel_minx ||
+				finger[id].x >= data->pdata->panel_maxx ||
+				finger[id].y <= data->pdata->panel_miny ||
+				finger[id].y >= data->pdata->panel_maxy) {
+			finger[id].status = MXT_RELEASE;
+		}
+
 		input_mt_report_slot_state(input_dev, MT_TOOL_FINGER,
 				finger[id].status != MXT_RELEASE);
 
@@ -538,7 +777,7 @@
 			input_report_abs(input_dev, ABS_MT_POSITION_Y,
 					finger[id].y);
 			input_report_abs(input_dev, ABS_MT_PRESSURE,
-					finger[id].pressure);
+					 finger[id].pressure);
 		} else {
 			finger[id].status = 0;
 		}
@@ -546,11 +785,18 @@
 
 	input_report_key(input_dev, BTN_TOUCH, finger_num > 0);
 
+	if (finger[single_id].x <= data->pdata->panel_minx ||
+		finger[single_id].x >= data->pdata->panel_maxx ||
+		finger[single_id].y <= data->pdata->panel_miny ||
+		finger[single_id].y >= data->pdata->panel_maxy) {
+		status = MXT_RELEASE;
+	}
+
 	if (status != MXT_RELEASE) {
 		input_report_abs(input_dev, ABS_X, finger[single_id].x);
 		input_report_abs(input_dev, ABS_Y, finger[single_id].y);
 		input_report_abs(input_dev,
-				 ABS_PRESSURE, finger[single_id].pressure);
+			ABS_PRESSURE, finger[single_id].pressure);
 	}
 
 	input_sync(input_dev);
@@ -584,9 +830,9 @@
 
 	x = (message->message[1] << 4) | ((message->message[3] >> 4) & 0xf);
 	y = (message->message[2] << 4) | ((message->message[3] & 0xf));
-	if (data->max_x < 1024)
+	if (data->pdata->panel_maxx < 1024)
 		x = x >> 2;
-	if (data->max_y < 1024)
+	if (data->pdata->panel_maxy < 1024)
 		y = y >> 2;
 
 	area = message->message[4];
@@ -606,36 +852,75 @@
 	mxt_input_report(data, id);
 }
 
+static void mxt_handle_key_array(struct mxt_data *data,
+				struct mxt_message *message)
+{
+	u32 keys_changed;
+	int i;
+
+	if (!data->pdata->key_codes) {
+		dev_err(&data->client->dev, "keyarray is not supported\n");
+		return;
+	}
+
+	data->keyarray_new = message->message[1] |
+				(message->message[2] << 8) |
+				(message->message[3] << 16) |
+				(message->message[4] << 24);
+
+	keys_changed = data->keyarray_old ^ data->keyarray_new;
+
+	if (!keys_changed) {
+		dev_dbg(&data->client->dev, "no keys changed\n");
+		return;
+	}
+
+	for (i = 0; i < MXT_KEYARRAY_MAX_KEYS; i++) {
+		if (!(keys_changed & (1 << i)))
+			continue;
+
+		input_report_key(data->input_dev, data->pdata->key_codes[i],
+					(data->keyarray_new & (1 << i)));
+		input_sync(data->input_dev);
+	}
+
+	data->keyarray_old = data->keyarray_new;
+}
+
 static irqreturn_t mxt_interrupt(int irq, void *dev_id)
 {
 	struct mxt_data *data = dev_id;
 	struct mxt_message message;
-	struct mxt_object *object;
 	struct device *dev = &data->client->dev;
 	int id;
 	u8 reportid;
-	u8 max_reportid;
-	u8 min_reportid;
+
+	if (data->state != APPMODE) {
+		dev_err(dev, "Ignoring IRQ - not in APPMODE state\n");
+		return IRQ_HANDLED;
+	}
 
 	do {
 		if (mxt_read_message(data, &message)) {
 			dev_err(dev, "Failed to read message\n");
 			goto end;
 		}
-
 		reportid = message.reportid;
 
-		/* whether reportid is thing of MXT_TOUCH_MULTI_T9 */
-		object = mxt_get_object(data, MXT_TOUCH_MULTI_T9);
-		if (!object)
-			goto end;
+		if (!reportid) {
+			dev_dbg(dev, "Report id 0 is reserved\n");
+			continue;
+		}
 
-		max_reportid = object->max_reportid;
-		min_reportid = max_reportid - object->num_report_ids + 1;
-		id = reportid - min_reportid;
+		/* check whether report id is part of T9 or T15 */
+		id = reportid - data->t9_min_reportid;
 
-		if (reportid >= min_reportid && reportid <= max_reportid)
+		if (reportid >= data->t9_min_reportid &&
+					reportid <= data->t9_max_reportid)
 			mxt_input_touchevent(data, &message, id);
+		else if (reportid >= data->t15_min_reportid &&
+					reportid <= data->t15_max_reportid)
+			mxt_handle_key_array(data, &message);
 		else
 			mxt_dump_message(dev, &message);
 	} while (reportid != 0xff);
@@ -646,13 +931,13 @@
 
 static int mxt_check_reg_init(struct mxt_data *data)
 {
-	const struct mxt_platform_data *pdata = data->pdata;
+	const struct mxt_config_info *config_info = data->config_info;
 	struct mxt_object *object;
 	struct device *dev = &data->client->dev;
 	int index = 0;
 	int i, j, config_offset;
 
-	if (!pdata->config) {
+	if (!config_info) {
 		dev_dbg(dev, "No cfg data defined, skipping reg init\n");
 		return 0;
 	}
@@ -663,18 +948,16 @@
 		if (!mxt_object_writable(object->type))
 			continue;
 
-		for (j = 0;
-		     j < (object->size + 1) * (object->instances + 1);
-		     j++) {
+		for (j = 0; j < object->size + 1; j++) {
 			config_offset = index + j;
-			if (config_offset > pdata->config_length) {
+			if (config_offset > config_info->config_length) {
 				dev_err(dev, "Not enough config data!\n");
 				return -EINVAL;
 			}
 			mxt_write_object(data, object->type, j,
-					 pdata->config[config_offset]);
+					 config_info->config[config_offset]);
 		}
-		index += (object->size + 1) * (object->instances + 1);
+		index += object->size + 1;
 	}
 
 	return 0;
@@ -702,54 +985,6 @@
 	return 0;
 }
 
-static void mxt_handle_pdata(struct mxt_data *data)
-{
-	const struct mxt_platform_data *pdata = data->pdata;
-	u8 voltage;
-
-	/* Set touchscreen lines */
-	mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_XSIZE,
-			pdata->x_line);
-	mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_YSIZE,
-			pdata->y_line);
-
-	/* Set touchscreen orient */
-	mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_ORIENT,
-			pdata->orient);
-
-	/* Set touchscreen burst length */
-	mxt_write_object(data, MXT_TOUCH_MULTI_T9,
-			MXT_TOUCH_BLEN, pdata->blen);
-
-	/* Set touchscreen threshold */
-	mxt_write_object(data, MXT_TOUCH_MULTI_T9,
-			MXT_TOUCH_TCHTHR, pdata->threshold);
-
-	/* Set touchscreen resolution */
-	mxt_write_object(data, MXT_TOUCH_MULTI_T9,
-			MXT_TOUCH_XRANGE_LSB, (pdata->x_size - 1) & 0xff);
-	mxt_write_object(data, MXT_TOUCH_MULTI_T9,
-			MXT_TOUCH_XRANGE_MSB, (pdata->x_size - 1) >> 8);
-	mxt_write_object(data, MXT_TOUCH_MULTI_T9,
-			MXT_TOUCH_YRANGE_LSB, (pdata->y_size - 1) & 0xff);
-	mxt_write_object(data, MXT_TOUCH_MULTI_T9,
-			MXT_TOUCH_YRANGE_MSB, (pdata->y_size - 1) >> 8);
-
-	/* Set touchscreen voltage */
-	if (pdata->voltage) {
-		if (pdata->voltage < MXT_VOLTAGE_DEFAULT) {
-			voltage = (MXT_VOLTAGE_DEFAULT - pdata->voltage) /
-				MXT_VOLTAGE_STEP;
-			voltage = 0xff - voltage + 1;
-		} else
-			voltage = (pdata->voltage - MXT_VOLTAGE_DEFAULT) /
-				MXT_VOLTAGE_STEP;
-
-		mxt_write_object(data, MXT_SPT_CTECONFIG_T28,
-				MXT_CTE_VOLTAGE, voltage);
-	}
-}
-
 static int mxt_get_info(struct mxt_data *data)
 {
 	struct i2c_client *client = data->client;
@@ -792,6 +1027,7 @@
 	u16 reg;
 	u8 reportid = 0;
 	u8 buf[MXT_OBJECT_SIZE];
+	bool found_t38 = false;
 
 	for (i = 0; i < data->info.object_num; i++) {
 		struct mxt_object *object = data->object_table + i;
@@ -812,6 +1048,278 @@
 					(object->instances + 1);
 			object->max_reportid = reportid;
 		}
+
+		/* Calculate index for config major version in config array.
+		 * Major version is the first byte in object T38.
+		 */
+		if (object->type == MXT_SPT_USERDATA_T38) {
+			data->t38_start_addr = object->start_address;
+			found_t38 = true;
+		}
+		if (!found_t38 && mxt_object_writable(object->type))
+			data->cfg_version_idx += object->size + 1;
+	}
+
+	return 0;
+}
+
+static int compare_versions(const u8 *v1, const u8 *v2)
+{
+	int i;
+
+	if (!v1 || !v2)
+		return -EINVAL;
+
+	/* The major version number stays the same across different versions for
+	 * a particular controller on a target. The minor and sub-minor version
+	 * numbers indicate which version is newer.
+	 */
+	if (v1[0] != v2[0])
+		return -EINVAL;
+
+	for (i = 1; i < MXT_CFG_VERSION_LEN; i++) {
+		if (v1[i] > v2[i])
+			return MXT_CFG_VERSION_LESS;	/* v2 is older */
+
+		if (v1[i] < v2[i])
+			return MXT_CFG_VERSION_GREATER;	/* v2 is newer */
+	}
+
+	return MXT_CFG_VERSION_EQUAL;	/* v1 and v2 are equal */
+}
+
+static void mxt_check_config_version(struct mxt_data *data,
+			const struct mxt_config_info *cfg_info,
+			bool match_major,
+			const u8 **cfg_version_found,
+			bool *found_cfg_major_match)
+{
+	const u8 *cfg_version;
+	int result = -EINVAL;
+
+	cfg_version = cfg_info->config + data->cfg_version_idx;
+
+	if (*cfg_version_found)
+		result = compare_versions(*cfg_version_found, cfg_version);
+
+	if (match_major) {
+		if (result >= MXT_CFG_VERSION_EQUAL)
+			*found_cfg_major_match = true;
+
+		if (result == MXT_CFG_VERSION_EQUAL ||
+			result == MXT_CFG_VERSION_GREATER) {
+			data->config_info = cfg_info;
+			data->fw_name = cfg_info->fw_name;
+			*cfg_version_found = cfg_version;
+		}
+
+		if (result == MXT_CFG_VERSION_GREATER)
+			data->update_cfg = true;
+	} else if (!*cfg_version_found || result == MXT_CFG_VERSION_GREATER) {
+		data->config_info = cfg_info;
+		data->fw_name = cfg_info->fw_name;
+		data->update_cfg = true;
+		*cfg_version_found = cfg_version;
+	}
+}
+
+/* If the controller's config version has a non-zero major number, call this
+ * function with match_major = true to look for the latest config present in
+ * the pdata based on matching family id, variant id, f/w version, build, and
+ * config major number. If the controller is programmed with wrong config data
+ * previously, call this function with match_major = false to look for latest
+ * config based on based on matching family id, variant id, f/w version and
+ * build only.
+ */
+static int mxt_search_config_array(struct mxt_data *data, bool match_major)
+{
+
+	const struct mxt_platform_data *pdata = data->pdata;
+	const struct mxt_config_info *cfg_info;
+	const struct mxt_info *info = &data->info;
+	const u8 *cfg_version_found;
+	bool found_cfg_major_match = false;
+	int i;
+
+	cfg_version_found = match_major ? data->cfg_version : NULL;
+
+	for (i = 0; i < pdata->config_array_size; i++) {
+
+		cfg_info = &pdata->config_array[i];
+
+		if (!cfg_info->config || !cfg_info->config_length)
+			continue;
+
+		if (info->family_id == cfg_info->family_id &&
+			info->variant_id == cfg_info->variant_id &&
+			info->version == cfg_info->version &&
+			info->build == cfg_info->build) {
+
+			mxt_check_config_version(data, cfg_info, match_major,
+				&cfg_version_found, &found_cfg_major_match);
+		}
+	}
+
+	if (data->config_info || found_cfg_major_match)
+		return 0;
+
+	data->config_info = NULL;
+	data->fw_name = NULL;
+
+	return -EINVAL;
+}
+
+static int mxt_get_config(struct mxt_data *data)
+{
+	const struct mxt_platform_data *pdata = data->pdata;
+	struct device *dev = &data->client->dev;
+	struct mxt_object *object;
+	int error;
+
+	if (!pdata->config_array || !pdata->config_array_size) {
+		dev_dbg(dev, "No cfg data provided by platform data\n");
+		return 0;
+	}
+
+	/* Get current config version */
+	object = mxt_get_object(data, MXT_SPT_USERDATA_T38);
+	if (!object) {
+		dev_err(dev, "Unable to obtain USERDATA object\n");
+		return -EINVAL;
+	}
+
+	error = __mxt_read_reg(data->client, object->start_address,
+				sizeof(data->cfg_version), data->cfg_version);
+	if (error) {
+		dev_err(dev, "Unable to read config version\n");
+		return error;
+	}
+	dev_info(dev, "Current config version on the controller is %d.%d.%d\n",
+			data->cfg_version[0], data->cfg_version[1],
+			data->cfg_version[2]);
+
+	/* It is possible that the config data on the controller is not
+	 * versioned and the version number returns 0. In this case,
+	 * find a match without the config version checking.
+	 */
+	error = mxt_search_config_array(data,
+				data->cfg_version[0] != 0 ? true : false);
+	if (error) {
+		/* If a match wasn't found for a non-zero config version,
+		 * it means the controller has the wrong config data. Search
+		 * for a best match based on controller and firmware version,
+		 * but not config version.
+		 */
+		if (data->cfg_version[0])
+			error = mxt_search_config_array(data, false);
+		if (error) {
+			dev_err(dev,
+				"Unable to find matching config in pdata\n");
+			return error;
+		}
+	}
+
+	return 0;
+}
+
+static void mxt_reset_delay(struct mxt_data *data)
+{
+	struct mxt_info *info = &data->info;
+
+	switch (info->family_id) {
+	case MXT224_ID:
+		msleep(MXT224_RESET_TIME);
+		break;
+	case MXT224E_ID:
+		msleep(MXT224E_RESET_TIME);
+		break;
+	case MXT1386_ID:
+		msleep(MXT1386_RESET_TIME);
+		break;
+	default:
+		msleep(MXT_RESET_TIME);
+	}
+}
+
+static int mxt_backup_nv(struct mxt_data *data)
+{
+	int error;
+	u8 command_register;
+	int timeout_counter = 0;
+
+	/* Backup to memory */
+	mxt_write_object(data, MXT_GEN_COMMAND_T6,
+			MXT_COMMAND_BACKUPNV,
+			MXT_BACKUP_VALUE);
+	msleep(MXT_BACKUP_TIME);
+
+	do {
+		error = mxt_read_object(data, MXT_GEN_COMMAND_T6,
+					MXT_COMMAND_BACKUPNV,
+					&command_register);
+		if (error)
+			return error;
+
+		usleep_range(1000, 2000);
+
+	} while ((command_register != 0) && (++timeout_counter <= 100));
+
+	if (timeout_counter > 100) {
+		dev_err(&data->client->dev, "No response after backup!\n");
+		return -EIO;
+	}
+
+	/* Soft reset */
+	mxt_write_object(data, MXT_GEN_COMMAND_T6, MXT_COMMAND_RESET, 1);
+
+	mxt_reset_delay(data);
+
+	return 0;
+}
+
+static int mxt_save_objects(struct mxt_data *data)
+{
+	struct i2c_client *client = data->client;
+	struct mxt_object *t7_object;
+	struct mxt_object *t9_object;
+	struct mxt_object *t15_object;
+	int error;
+
+	/* Store T7 and T9 locally, used in suspend/resume operations */
+	t7_object = mxt_get_object(data, MXT_GEN_POWER_T7);
+	if (!t7_object) {
+		dev_err(&client->dev, "Failed to get T7 object\n");
+		return -EINVAL;
+	}
+
+	data->t7_start_addr = t7_object->start_address;
+	error = __mxt_read_reg(client, data->t7_start_addr,
+				T7_DATA_SIZE, data->t7_data);
+	if (error < 0) {
+		dev_err(&client->dev,
+			"Failed to save current power state\n");
+		return error;
+	}
+
+	/* Store T9, T15's min and max report ids */
+	t9_object = mxt_get_object(data, MXT_TOUCH_MULTI_T9);
+	if (!t9_object) {
+		dev_err(&client->dev, "Failed to get T9 object\n");
+		return -EINVAL;
+	}
+	data->t9_max_reportid = t9_object->max_reportid;
+	data->t9_min_reportid = t9_object->max_reportid -
+					t9_object->num_report_ids + 1;
+
+	if (data->pdata->key_codes) {
+		t15_object = mxt_get_object(data, MXT_TOUCH_KEYARRAY_T15);
+		if (!t15_object)
+			dev_dbg(&client->dev, "T15 object is not available\n");
+		else {
+			data->t15_max_reportid = t15_object->max_reportid;
+			data->t15_min_reportid = t15_object->max_reportid -
+						t15_object->num_report_ids + 1;
+		}
 	}
 
 	return 0;
@@ -823,10 +1331,33 @@
 	struct mxt_info *info = &data->info;
 	int error;
 	u8 val;
+	const u8 *cfg_ver;
 
 	error = mxt_get_info(data);
-	if (error)
-		return error;
+	if (error) {
+		/* Try bootloader mode */
+		error = mxt_switch_to_bootloader_address(data);
+		if (error)
+			return error;
+
+		error = mxt_check_bootloader(client, MXT_APP_CRC_FAIL);
+		if (error)
+			return error;
+
+		dev_err(&client->dev, "Application CRC failure\n");
+		data->state = BOOTLOADER;
+
+		return 0;
+	}
+
+	dev_info(&client->dev,
+			"Family ID: %d Variant ID: %d Version: %d.%d "
+			"Build: 0x%02X Object Num: %d\n",
+			info->family_id, info->variant_id,
+			info->version >> 4, info->version & 0xf,
+			info->build, info->object_num);
+
+	data->state = APPMODE;
 
 	data->object_table = kcalloc(info->object_num,
 				     sizeof(struct mxt_object),
@@ -839,62 +1370,68 @@
 	/* Get object table information */
 	error = mxt_get_object_table(data);
 	if (error)
-		return error;
+		goto free_object_table;
+
+	/* Get config data from platform data */
+	error = mxt_get_config(data);
+	if (error)
+		dev_dbg(&client->dev, "Config info not found.\n");
 
 	/* Check register init values */
-	error = mxt_check_reg_init(data);
+	if (data->config_info && data->config_info->config) {
+		if (data->update_cfg) {
+			error = mxt_check_reg_init(data);
+			if (error) {
+				dev_err(&client->dev,
+					"Failed to check reg init value\n");
+				goto free_object_table;
+			}
+
+			error = mxt_backup_nv(data);
+			if (error) {
+				dev_err(&client->dev, "Failed to back up NV\n");
+				goto free_object_table;
+			}
+
+			cfg_ver = data->config_info->config +
+							data->cfg_version_idx;
+			dev_info(&client->dev,
+				"Config updated from %d.%d.%d to %d.%d.%d\n",
+				data->cfg_version[0], data->cfg_version[1],
+				data->cfg_version[2],
+				cfg_ver[0], cfg_ver[1], cfg_ver[2]);
+
+			memcpy(data->cfg_version, cfg_ver, MXT_CFG_VERSION_LEN);
+		}
+	} else {
+		dev_info(&client->dev,
+			"No cfg data defined, skipping check reg init\n");
+	}
+
+	error = mxt_save_objects(data);
 	if (error)
-		return error;
-
-	mxt_handle_pdata(data);
-
-	/* Backup to memory */
-	mxt_write_object(data, MXT_GEN_COMMAND_T6,
-			MXT_COMMAND_BACKUPNV,
-			MXT_BACKUP_VALUE);
-	msleep(MXT_BACKUP_TIME);
-
-	/* Soft reset */
-	mxt_write_object(data, MXT_GEN_COMMAND_T6,
-			MXT_COMMAND_RESET, 1);
-	msleep(MXT_RESET_TIME);
+		goto free_object_table;
 
 	/* Update matrix size at info struct */
 	error = mxt_read_reg(client, MXT_MATRIX_X_SIZE, &val);
 	if (error)
-		return error;
+		goto free_object_table;
 	info->matrix_xsize = val;
 
 	error = mxt_read_reg(client, MXT_MATRIX_Y_SIZE, &val);
 	if (error)
-		return error;
+		goto free_object_table;
 	info->matrix_ysize = val;
 
 	dev_info(&client->dev,
-			"Family ID: %d Variant ID: %d Version: %d Build: %d\n",
-			info->family_id, info->variant_id, info->version,
-			info->build);
-
-	dev_info(&client->dev,
-			"Matrix X Size: %d Matrix Y Size: %d Object Num: %d\n",
-			info->matrix_xsize, info->matrix_ysize,
-			info->object_num);
+			"Matrix X Size: %d Matrix Y Size: %d\n",
+			info->matrix_xsize, info->matrix_ysize);
 
 	return 0;
-}
 
-static void mxt_calc_resolution(struct mxt_data *data)
-{
-	unsigned int max_x = data->pdata->x_size - 1;
-	unsigned int max_y = data->pdata->y_size - 1;
-
-	if (data->pdata->orient & MXT_XY_SWITCH) {
-		data->max_x = max_y;
-		data->max_y = max_x;
-	} else {
-		data->max_x = max_x;
-		data->max_y = max_y;
-	}
+free_object_table:
+	kfree(data->object_table);
+	return error;
 }
 
 static ssize_t mxt_object_show(struct device *dev,
@@ -944,107 +1481,238 @@
 	return count;
 }
 
+static int strtobyte(const char *data, u8 *value)
+{
+	char str[3];
+
+	str[0] = data[0];
+	str[1] = data[1];
+	str[2] = '\0';
+
+	return kstrtou8(str, 16, value);
+}
+
 static int mxt_load_fw(struct device *dev, const char *fn)
 {
 	struct mxt_data *data = dev_get_drvdata(dev);
 	struct i2c_client *client = data->client;
 	const struct firmware *fw = NULL;
 	unsigned int frame_size;
+	unsigned int retry = 0;
 	unsigned int pos = 0;
-	int ret;
+	int ret, i, max_frame_size;
+	u8 *frame;
 
-	ret = request_firmware(&fw, fn, dev);
-	if (ret) {
-		dev_err(dev, "Unable to open firmware %s\n", fn);
-		return ret;
+	switch (data->info.family_id) {
+	case MXT224_ID:
+		max_frame_size = MXT_SINGLE_FW_MAX_FRAME_SIZE;
+		break;
+	case MXT1386_ID:
+		max_frame_size = MXT_CHIPSET_FW_MAX_FRAME_SIZE;
+		break;
+	default:
+		return -EINVAL;
 	}
 
-	/* Change to the bootloader mode */
-	mxt_write_object(data, MXT_GEN_COMMAND_T6,
-			MXT_COMMAND_RESET, MXT_BOOT_VALUE);
-	msleep(MXT_RESET_TIME);
+	frame = kmalloc(max_frame_size, GFP_KERNEL);
+	if (!frame) {
+		dev_err(dev, "Unable to allocate memory for frame data\n");
+		return -ENOMEM;
+	}
 
-	/* Change to slave address of bootloader */
-	if (client->addr == MXT_APP_LOW)
-		client->addr = MXT_BOOT_LOW;
-	else
-		client->addr = MXT_BOOT_HIGH;
+	ret = request_firmware(&fw, fn, dev);
+	if (ret < 0) {
+		dev_err(dev, "Unable to open firmware %s\n", fn);
+		goto free_frame;
+	}
+
+	if (data->state != BOOTLOADER) {
+		/* Change to the bootloader mode */
+		mxt_write_object(data, MXT_GEN_COMMAND_T6,
+				MXT_COMMAND_RESET, MXT_BOOT_VALUE);
+		mxt_reset_delay(data);
+
+		ret = mxt_switch_to_bootloader_address(data);
+		if (ret)
+			goto release_firmware;
+	}
 
 	ret = mxt_check_bootloader(client, MXT_WAITING_BOOTLOAD_CMD);
-	if (ret)
-		goto out;
+	if (ret) {
+		/* Bootloader may still be unlocked from previous update
+		 * attempt */
+		ret = mxt_check_bootloader(client,
+			MXT_WAITING_FRAME_DATA);
 
-	/* Unlock bootloader */
-	mxt_unlock_bootloader(client);
+		if (ret)
+			goto return_to_app_mode;
+	} else {
+		dev_info(dev, "Unlocking bootloader\n");
+		/* Unlock bootloader */
+		mxt_unlock_bootloader(client);
+	}
 
 	while (pos < fw->size) {
 		ret = mxt_check_bootloader(client,
 						MXT_WAITING_FRAME_DATA);
 		if (ret)
-			goto out;
+			goto release_firmware;
 
-		frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1));
+		/* Get frame length MSB */
+		ret = strtobyte(fw->data + pos, frame);
+		if (ret)
+			goto release_firmware;
+
+		/* Get frame length LSB */
+		ret = strtobyte(fw->data + pos + 2, frame + 1);
+		if (ret)
+			goto release_firmware;
+
+		frame_size = ((*frame << 8) | *(frame + 1));
 
 		/* We should add 2 at frame size as the the firmware data is not
 		 * included the CRC bytes.
 		 */
 		frame_size += 2;
 
+		if (frame_size > max_frame_size) {
+			dev_err(dev, "Invalid frame size - %d\n", frame_size);
+			ret = -EINVAL;
+			goto release_firmware;
+		}
+
+		/* Convert frame data and CRC from hex to binary */
+		for (i = 2; i < frame_size; i++) {
+			ret = strtobyte(fw->data + pos + i * 2, frame + i);
+			if (ret)
+				goto release_firmware;
+		}
+
 		/* Write one frame to device */
-		mxt_fw_write(client, fw->data + pos, frame_size);
+		mxt_fw_write(client, frame, frame_size);
 
 		ret = mxt_check_bootloader(client,
 						MXT_FRAME_CRC_PASS);
-		if (ret)
-			goto out;
+		if (ret) {
+			retry++;
 
-		pos += frame_size;
+			/* Back off by 20ms per retry */
+			msleep(retry * 20);
 
-		dev_dbg(dev, "Updated %d bytes / %zd bytes\n", pos, fw->size);
+			if (retry > 20)
+				goto release_firmware;
+		} else {
+			retry = 0;
+			pos += frame_size * 2;
+			dev_dbg(dev, "Updated %d/%zd bytes\n", pos, fw->size);
+		}
 	}
 
-out:
+return_to_app_mode:
+	mxt_switch_to_appmode_address(data);
+release_firmware:
 	release_firmware(fw);
-
-	/* Change to slave address of application */
-	if (client->addr == MXT_BOOT_LOW)
-		client->addr = MXT_APP_LOW;
-	else
-		client->addr = MXT_APP_HIGH;
+free_frame:
+	kfree(frame);
 
 	return ret;
 }
 
+static const char *
+mxt_search_fw_name(struct mxt_data *data, u8 bootldr_id)
+{
+	const struct mxt_platform_data *pdata = data->pdata;
+	const struct mxt_config_info *cfg_info;
+	const char *fw_name = NULL;
+	int i;
+
+	for (i = 0; i < pdata->config_array_size; i++) {
+		cfg_info = &pdata->config_array[i];
+		if (bootldr_id == cfg_info->bootldr_id && cfg_info->fw_name) {
+			data->config_info = cfg_info;
+			data->info.family_id = cfg_info->family_id;
+			fw_name = cfg_info->fw_name;
+		}
+	}
+
+	return fw_name;
+}
+
 static ssize_t mxt_update_fw_store(struct device *dev,
 					struct device_attribute *attr,
 					const char *buf, size_t count)
 {
 	struct mxt_data *data = dev_get_drvdata(dev);
 	int error;
+	const char *fw_name;
+	u8 bootldr_id;
+	u8 cfg_version[MXT_CFG_VERSION_LEN] = {0};
+
+	/* If fw_name is set, then the existing firmware has an upgrade */
+	if (!data->fw_name) {
+		/*
+		 * If the device boots up in the bootloader mode, check if
+		 * there is a firmware to upgrade.
+		 */
+		if (data->state == BOOTLOADER) {
+			bootldr_id = mxt_get_bootloader_id(data->client);
+			if (bootldr_id <= 0) {
+				dev_err(dev,
+					"Unable to retrieve bootloader id\n");
+				return -EINVAL;
+			}
+			fw_name = mxt_search_fw_name(data, bootldr_id);
+			if (fw_name == NULL) {
+				dev_err(dev,
+				"Unable to find fw from bootloader id\n");
+				return -EINVAL;
+			}
+		} else {
+			/* In APPMODE, if the f/w name does not exist, quit */
+			dev_err(dev,
+			"Firmware name not specified in platform data\n");
+			return -EINVAL;
+		}
+	} else {
+		fw_name = data->fw_name;
+	}
+
+	dev_info(dev, "Upgrading the firmware file to %s\n", fw_name);
 
 	disable_irq(data->irq);
 
-	error = mxt_load_fw(dev, MXT_FW_NAME);
+	error = mxt_load_fw(dev, fw_name);
 	if (error) {
 		dev_err(dev, "The firmware update failed(%d)\n", error);
 		count = error;
 	} else {
-		dev_dbg(dev, "The firmware update succeeded\n");
+		dev_info(dev, "The firmware update succeeded\n");
 
 		/* Wait for reset */
 		msleep(MXT_FWRESET_TIME);
 
+		data->state = INIT;
 		kfree(data->object_table);
 		data->object_table = NULL;
+		data->cfg_version_idx = 0;
+		data->update_cfg = false;
+
+		error = __mxt_write_reg(data->client, data->t38_start_addr,
+				sizeof(cfg_version), cfg_version);
+		if (error)
+			dev_err(dev,
+			"Unable to zero out config version after fw upgrade\n");
 
 		mxt_initialize(data);
 	}
 
-	enable_irq(data->irq);
+	if (data->state == APPMODE) {
+		enable_irq(data->irq);
 
-	error = mxt_make_highchg(data);
-	if (error)
-		return error;
+		error = mxt_make_highchg(data);
+		if (error)
+			return error;
+	}
 
 	return count;
 }
@@ -1062,25 +1730,50 @@
 	.attrs = mxt_attrs,
 };
 
-static void mxt_start(struct mxt_data *data)
+static int mxt_start(struct mxt_data *data)
 {
-	/* Touch enable */
-	mxt_write_object(data,
-			MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0x83);
+	int error;
+
+	/* restore the old power state values and reenable touch */
+	error = __mxt_write_reg(data->client, data->t7_start_addr,
+				T7_DATA_SIZE, data->t7_data);
+	if (error < 0) {
+		dev_err(&data->client->dev,
+			"failed to restore old power state\n");
+		return error;
+	}
+
+	return 0;
 }
 
-static void mxt_stop(struct mxt_data *data)
+static int mxt_stop(struct mxt_data *data)
 {
-	/* Touch disable */
-	mxt_write_object(data,
-			MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0);
+	int error;
+	u8 t7_data[T7_DATA_SIZE] = {0};
+
+	error = __mxt_write_reg(data->client, data->t7_start_addr,
+				T7_DATA_SIZE, t7_data);
+	if (error < 0) {
+		dev_err(&data->client->dev,
+			"failed to configure deep sleep mode\n");
+		return error;
+	}
+
+	return 0;
 }
 
 static int mxt_input_open(struct input_dev *dev)
 {
 	struct mxt_data *data = input_get_drvdata(dev);
+	int error;
 
-	mxt_start(data);
+	if (data->state == APPMODE) {
+		error = mxt_start(data);
+		if (error < 0) {
+			dev_err(&data->client->dev, "mxt_start failed in input_open\n");
+			return error;
+		}
+	}
 
 	return 0;
 }
@@ -1088,8 +1781,447 @@
 static void mxt_input_close(struct input_dev *dev)
 {
 	struct mxt_data *data = input_get_drvdata(dev);
+	int error;
 
-	mxt_stop(data);
+	if (data->state == APPMODE) {
+		error = mxt_stop(data);
+		if (error < 0)
+			dev_err(&data->client->dev, "mxt_stop failed in input_close\n");
+	}
+}
+
+static int reg_set_optimum_mode_check(struct regulator *reg, int load_uA)
+{
+	return (regulator_count_voltages(reg) > 0) ?
+		regulator_set_optimum_mode(reg, load_uA) : 0;
+}
+
+static int mxt_power_on(struct mxt_data *data, bool on)
+{
+	int rc;
+
+	if (on == false)
+		goto power_off;
+
+	rc = reg_set_optimum_mode_check(data->vcc_ana, MXT_ACTIVE_LOAD_UA);
+	if (rc < 0) {
+		dev_err(&data->client->dev,
+			"Regulator vcc_ana set_opt failed rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = regulator_enable(data->vcc_ana);
+	if (rc) {
+		dev_err(&data->client->dev,
+			"Regulator vcc_ana enable failed rc=%d\n", rc);
+		goto error_reg_en_vcc_ana;
+	}
+
+	if (data->pdata->digital_pwr_regulator) {
+		rc = reg_set_optimum_mode_check(data->vcc_dig,
+					MXT_ACTIVE_LOAD_DIG_UA);
+		if (rc < 0) {
+			dev_err(&data->client->dev,
+				"Regulator vcc_dig set_opt failed rc=%d\n",
+				rc);
+			goto error_reg_opt_vcc_dig;
+		}
+
+		rc = regulator_enable(data->vcc_dig);
+		if (rc) {
+			dev_err(&data->client->dev,
+				"Regulator vcc_dig enable failed rc=%d\n", rc);
+			goto error_reg_en_vcc_dig;
+		}
+	}
+
+	if (data->pdata->i2c_pull_up) {
+		rc = reg_set_optimum_mode_check(data->vcc_i2c, MXT_I2C_LOAD_UA);
+		if (rc < 0) {
+			dev_err(&data->client->dev,
+				"Regulator vcc_i2c set_opt failed rc=%d\n", rc);
+			goto error_reg_opt_i2c;
+		}
+
+		rc = regulator_enable(data->vcc_i2c);
+		if (rc) {
+			dev_err(&data->client->dev,
+				"Regulator vcc_i2c enable failed rc=%d\n", rc);
+			goto error_reg_en_vcc_i2c;
+		}
+	}
+
+	msleep(130);
+
+	return 0;
+
+error_reg_en_vcc_i2c:
+	if (data->pdata->i2c_pull_up)
+		reg_set_optimum_mode_check(data->vcc_i2c, 0);
+error_reg_opt_i2c:
+	if (data->pdata->digital_pwr_regulator)
+		regulator_disable(data->vcc_dig);
+error_reg_en_vcc_dig:
+	if (data->pdata->digital_pwr_regulator)
+		reg_set_optimum_mode_check(data->vcc_dig, 0);
+error_reg_opt_vcc_dig:
+	regulator_disable(data->vcc_ana);
+error_reg_en_vcc_ana:
+	reg_set_optimum_mode_check(data->vcc_ana, 0);
+	return rc;
+
+power_off:
+	reg_set_optimum_mode_check(data->vcc_ana, 0);
+	regulator_disable(data->vcc_ana);
+	if (data->pdata->digital_pwr_regulator) {
+		reg_set_optimum_mode_check(data->vcc_dig, 0);
+		regulator_disable(data->vcc_dig);
+	}
+	if (data->pdata->i2c_pull_up) {
+		reg_set_optimum_mode_check(data->vcc_i2c, 0);
+		regulator_disable(data->vcc_i2c);
+	}
+	msleep(50);
+	return 0;
+}
+
+static int mxt_regulator_configure(struct mxt_data *data, bool on)
+{
+	int rc;
+
+	if (on == false)
+		goto hw_shutdown;
+
+	data->vcc_ana = regulator_get(&data->client->dev, "vdd_ana");
+	if (IS_ERR(data->vcc_ana)) {
+		rc = PTR_ERR(data->vcc_ana);
+		dev_err(&data->client->dev,
+			"Regulator get failed vcc_ana rc=%d\n", rc);
+		return rc;
+	}
+
+	if (regulator_count_voltages(data->vcc_ana) > 0) {
+		rc = regulator_set_voltage(data->vcc_ana, MXT_VTG_MIN_UV,
+							MXT_VTG_MAX_UV);
+		if (rc) {
+			dev_err(&data->client->dev,
+				"regulator set_vtg failed rc=%d\n", rc);
+			goto error_set_vtg_vcc_ana;
+		}
+	}
+	if (data->pdata->digital_pwr_regulator) {
+		data->vcc_dig = regulator_get(&data->client->dev, "vdd_dig");
+		if (IS_ERR(data->vcc_dig)) {
+			rc = PTR_ERR(data->vcc_dig);
+			dev_err(&data->client->dev,
+				"Regulator get dig failed rc=%d\n", rc);
+			goto error_get_vtg_vcc_dig;
+		}
+
+		if (regulator_count_voltages(data->vcc_dig) > 0) {
+			rc = regulator_set_voltage(data->vcc_dig,
+				MXT_VTG_DIG_MIN_UV, MXT_VTG_DIG_MAX_UV);
+			if (rc) {
+				dev_err(&data->client->dev,
+					"regulator set_vtg failed rc=%d\n", rc);
+				goto error_set_vtg_vcc_dig;
+			}
+		}
+	}
+	if (data->pdata->i2c_pull_up) {
+		data->vcc_i2c = regulator_get(&data->client->dev, "vcc_i2c");
+		if (IS_ERR(data->vcc_i2c)) {
+			rc = PTR_ERR(data->vcc_i2c);
+			dev_err(&data->client->dev,
+				"Regulator get failed rc=%d\n",	rc);
+			goto error_get_vtg_i2c;
+		}
+		if (regulator_count_voltages(data->vcc_i2c) > 0) {
+			rc = regulator_set_voltage(data->vcc_i2c,
+				MXT_I2C_VTG_MIN_UV, MXT_I2C_VTG_MAX_UV);
+			if (rc) {
+				dev_err(&data->client->dev,
+					"regulator set_vtg failed rc=%d\n", rc);
+				goto error_set_vtg_i2c;
+			}
+		}
+	}
+
+	return 0;
+
+error_set_vtg_i2c:
+	regulator_put(data->vcc_i2c);
+error_get_vtg_i2c:
+	if (data->pdata->digital_pwr_regulator)
+		if (regulator_count_voltages(data->vcc_dig) > 0)
+			regulator_set_voltage(data->vcc_dig, 0,
+				MXT_VTG_DIG_MAX_UV);
+error_set_vtg_vcc_dig:
+	if (data->pdata->digital_pwr_regulator)
+		regulator_put(data->vcc_dig);
+error_get_vtg_vcc_dig:
+	if (regulator_count_voltages(data->vcc_ana) > 0)
+		regulator_set_voltage(data->vcc_ana, 0, MXT_VTG_MAX_UV);
+error_set_vtg_vcc_ana:
+	regulator_put(data->vcc_ana);
+	return rc;
+
+hw_shutdown:
+	if (regulator_count_voltages(data->vcc_ana) > 0)
+		regulator_set_voltage(data->vcc_ana, 0, MXT_VTG_MAX_UV);
+	regulator_put(data->vcc_ana);
+	if (data->pdata->digital_pwr_regulator) {
+		if (regulator_count_voltages(data->vcc_dig) > 0)
+			regulator_set_voltage(data->vcc_dig, 0,
+						MXT_VTG_DIG_MAX_UV);
+		regulator_put(data->vcc_dig);
+	}
+	if (data->pdata->i2c_pull_up) {
+		if (regulator_count_voltages(data->vcc_i2c) > 0)
+			regulator_set_voltage(data->vcc_i2c, 0,
+						MXT_I2C_VTG_MAX_UV);
+		regulator_put(data->vcc_i2c);
+	}
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int mxt_regulator_lpm(struct mxt_data *data, bool on)
+{
+
+	int rc;
+
+	if (on == false)
+		goto regulator_hpm;
+
+	rc = reg_set_optimum_mode_check(data->vcc_ana, MXT_LPM_LOAD_UA);
+	if (rc < 0) {
+		dev_err(&data->client->dev,
+			"Regulator vcc_ana set_opt failed rc=%d\n", rc);
+		goto fail_regulator_lpm;
+	}
+
+	if (data->pdata->digital_pwr_regulator) {
+		rc = reg_set_optimum_mode_check(data->vcc_dig,
+						MXT_LPM_LOAD_DIG_UA);
+		if (rc < 0) {
+			dev_err(&data->client->dev,
+				"Regulator vcc_dig set_opt failed rc=%d\n", rc);
+			goto fail_regulator_lpm;
+		}
+	}
+
+	if (data->pdata->i2c_pull_up) {
+		rc = reg_set_optimum_mode_check(data->vcc_i2c,
+						MXT_I2C_LPM_LOAD_UA);
+		if (rc < 0) {
+			dev_err(&data->client->dev,
+				"Regulator vcc_i2c set_opt failed rc=%d\n", rc);
+			goto fail_regulator_lpm;
+		}
+	}
+
+	return 0;
+
+regulator_hpm:
+
+	rc = reg_set_optimum_mode_check(data->vcc_ana, MXT_ACTIVE_LOAD_UA);
+	if (rc < 0) {
+		dev_err(&data->client->dev,
+			"Regulator vcc_ana set_opt failed rc=%d\n", rc);
+		goto fail_regulator_hpm;
+	}
+
+	if (data->pdata->digital_pwr_regulator) {
+		rc = reg_set_optimum_mode_check(data->vcc_dig,
+						 MXT_ACTIVE_LOAD_DIG_UA);
+		if (rc < 0) {
+			dev_err(&data->client->dev,
+				"Regulator vcc_dig set_opt failed rc=%d\n", rc);
+			goto fail_regulator_hpm;
+		}
+	}
+
+	if (data->pdata->i2c_pull_up) {
+		rc = reg_set_optimum_mode_check(data->vcc_i2c, MXT_I2C_LOAD_UA);
+		if (rc < 0) {
+			dev_err(&data->client->dev,
+				"Regulator vcc_i2c set_opt failed rc=%d\n", rc);
+			goto fail_regulator_hpm;
+		}
+	}
+
+	return 0;
+
+fail_regulator_lpm:
+	reg_set_optimum_mode_check(data->vcc_ana, MXT_ACTIVE_LOAD_UA);
+	if (data->pdata->digital_pwr_regulator)
+		reg_set_optimum_mode_check(data->vcc_dig,
+					MXT_ACTIVE_LOAD_DIG_UA);
+	if (data->pdata->i2c_pull_up)
+		reg_set_optimum_mode_check(data->vcc_i2c, MXT_I2C_LOAD_UA);
+
+	return rc;
+
+fail_regulator_hpm:
+	reg_set_optimum_mode_check(data->vcc_ana, MXT_LPM_LOAD_UA);
+	if (data->pdata->digital_pwr_regulator)
+		reg_set_optimum_mode_check(data->vcc_dig, MXT_LPM_LOAD_DIG_UA);
+	if (data->pdata->i2c_pull_up)
+		reg_set_optimum_mode_check(data->vcc_i2c, MXT_I2C_LPM_LOAD_UA);
+
+	return rc;
+}
+
+static int mxt_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct mxt_data *data = i2c_get_clientdata(client);
+	struct input_dev *input_dev = data->input_dev;
+	int error;
+
+	mutex_lock(&input_dev->mutex);
+
+	if (input_dev->users) {
+		error = mxt_stop(data);
+		if (error < 0) {
+			dev_err(dev, "mxt_stop failed in suspend\n");
+			mutex_unlock(&input_dev->mutex);
+			return error;
+		}
+
+	}
+
+	mutex_unlock(&input_dev->mutex);
+
+	/* put regulators in low power mode */
+	error = mxt_regulator_lpm(data, true);
+	if (error < 0) {
+		dev_err(dev, "failed to enter low power mode\n");
+		return error;
+	}
+
+	return 0;
+}
+
+static int mxt_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct mxt_data *data = i2c_get_clientdata(client);
+	struct input_dev *input_dev = data->input_dev;
+	int error;
+
+	/* put regulators in high power mode */
+	error = mxt_regulator_lpm(data, false);
+	if (error < 0) {
+		dev_err(dev, "failed to enter high power mode\n");
+		return error;
+	}
+
+	mutex_lock(&input_dev->mutex);
+
+	if (input_dev->users) {
+		error = mxt_start(data);
+		if (error < 0) {
+			dev_err(dev, "mxt_start failed in resume\n");
+			mutex_unlock(&input_dev->mutex);
+			return error;
+		}
+	}
+
+	mutex_unlock(&input_dev->mutex);
+
+	return 0;
+}
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+static void mxt_early_suspend(struct early_suspend *h)
+{
+	struct mxt_data *data = container_of(h, struct mxt_data, early_suspend);
+
+	mxt_suspend(&data->client->dev);
+}
+
+static void mxt_late_resume(struct early_suspend *h)
+{
+	struct mxt_data *data = container_of(h, struct mxt_data, early_suspend);
+
+	mxt_resume(&data->client->dev);
+}
+#endif
+
+static const struct dev_pm_ops mxt_pm_ops = {
+#ifndef CONFIG_HAS_EARLYSUSPEND
+	.suspend	= mxt_suspend,
+	.resume		= mxt_resume,
+#endif
+};
+#endif
+
+static int mxt_debugfs_object_show(struct seq_file *m, void *v)
+{
+	struct mxt_data *data = m->private;
+	struct mxt_object *object;
+	struct device *dev = &data->client->dev;
+	int i, j, k;
+	int error;
+	int obj_size;
+	u8 val;
+
+	for (i = 0; i < data->info.object_num; i++) {
+		object = data->object_table + i;
+		obj_size = object->size + 1;
+
+		seq_printf(m, "Object[%d] (Type %d)\n", i + 1, object->type);
+
+		for (j = 0; j < object->instances + 1; j++) {
+			seq_printf(m, "[Instance %d]\n", j);
+
+			for (k = 0; k < obj_size; k++) {
+				error = mxt_read_object(data, object->type,
+							j * obj_size + k, &val);
+				if (error) {
+					dev_err(dev,
+						"Failed to read object %d "
+						"instance %d at offset %d\n",
+						object->type, j, k);
+					return error;
+				}
+
+				seq_printf(m, "Byte %d: 0x%02x (%d)\n",
+						k, val, val);
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int mxt_debugfs_object_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, mxt_debugfs_object_show, inode->i_private);
+}
+
+static const struct file_operations mxt_object_fops = {
+	.owner		= THIS_MODULE,
+	.open		= mxt_debugfs_object_open,
+	.read		= seq_read,
+	.release	= single_release,
+};
+
+static void __devinit mxt_debugfs_init(struct mxt_data *data)
+{
+	debug_base = debugfs_create_dir(MXT_DEBUGFS_DIR, NULL);
+	if (IS_ERR_OR_NULL(debug_base))
+		pr_err("atmel_mxt_ts: Failed to create debugfs dir\n");
+	if (IS_ERR_OR_NULL(debugfs_create_file(MXT_DEBUGFS_FILE,
+					       0444,
+					       debug_base,
+					       data,
+					       &mxt_object_fops))) {
+		pr_err("atmel_mxt_ts: Failed to create object file\n");
+		debugfs_remove_recursive(debug_base);
+	}
 }
 
 static int __devinit mxt_probe(struct i2c_client *client,
@@ -1098,7 +2230,7 @@
 	const struct mxt_platform_data *pdata = client->dev.platform_data;
 	struct mxt_data *data;
 	struct input_dev *input_dev;
-	int error;
+	int error, i;
 
 	if (!pdata)
 		return -EINVAL;
@@ -1111,7 +2243,8 @@
 		goto err_free_mem;
 	}
 
-	input_dev->name = "Atmel maXTouch Touchscreen";
+	data->state = INIT;
+	input_dev->name = "atmel_mxt_ts";
 	input_dev->id.bustype = BUS_I2C;
 	input_dev->dev.parent = &client->dev;
 	input_dev->open = mxt_input_open;
@@ -1122,17 +2255,15 @@
 	data->pdata = pdata;
 	data->irq = client->irq;
 
-	mxt_calc_resolution(data);
-
 	__set_bit(EV_ABS, input_dev->evbit);
 	__set_bit(EV_KEY, input_dev->evbit);
 	__set_bit(BTN_TOUCH, input_dev->keybit);
 
 	/* For single touch */
 	input_set_abs_params(input_dev, ABS_X,
-			     0, data->max_x, 0, 0);
+			pdata->disp_minx, pdata->disp_maxx, 0, 0);
 	input_set_abs_params(input_dev, ABS_Y,
-			     0, data->max_y, 0, 0);
+			pdata->disp_miny, pdata->disp_maxy, 0, 0);
 	input_set_abs_params(input_dev, ABS_PRESSURE,
 			     0, 255, 0, 0);
 
@@ -1141,18 +2272,83 @@
 	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
 			     0, MXT_MAX_AREA, 0, 0);
 	input_set_abs_params(input_dev, ABS_MT_POSITION_X,
-			     0, data->max_x, 0, 0);
+			pdata->disp_minx, pdata->disp_maxx, 0, 0);
 	input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
-			     0, data->max_y, 0, 0);
+			pdata->disp_miny, pdata->disp_maxy, 0, 0);
 	input_set_abs_params(input_dev, ABS_MT_PRESSURE,
 			     0, 255, 0, 0);
 
+	/* set key array supported keys */
+	if (pdata->key_codes) {
+		for (i = 0; i < MXT_KEYARRAY_MAX_KEYS; i++) {
+			if (pdata->key_codes[i])
+				input_set_capability(input_dev, EV_KEY,
+							pdata->key_codes[i]);
+		}
+	}
+
 	input_set_drvdata(input_dev, data);
 	i2c_set_clientdata(client, data);
 
+	if (pdata->init_hw)
+		error = pdata->init_hw(true);
+	else
+		error = mxt_regulator_configure(data, true);
+	if (error) {
+		dev_err(&client->dev, "Failed to intialize hardware\n");
+		goto err_free_mem;
+	}
+
+	if (pdata->power_on)
+		error = pdata->power_on(true);
+	else
+		error = mxt_power_on(data, true);
+	if (error) {
+		dev_err(&client->dev, "Failed to power on hardware\n");
+		goto err_regulator_on;
+	}
+
+	if (gpio_is_valid(pdata->irq_gpio)) {
+		/* configure touchscreen irq gpio */
+		error = gpio_request(pdata->irq_gpio,
+							"mxt_irq_gpio");
+		if (error) {
+			pr_err("%s: unable to request gpio [%d]\n", __func__,
+						pdata->irq_gpio);
+			goto err_power_on;
+		}
+		error = gpio_direction_input(pdata->irq_gpio);
+		if (error) {
+			pr_err("%s: unable to set_direction for gpio [%d]\n",
+					__func__, pdata->irq_gpio);
+			goto err_irq_gpio_req;
+		}
+	}
+
+	if (gpio_is_valid(pdata->reset_gpio)) {
+		/* configure touchscreen reset out gpio */
+		error = gpio_request(pdata->reset_gpio,
+						"mxt_reset_gpio");
+		if (error) {
+			pr_err("%s: unable to request reset gpio %d\n",
+				__func__, pdata->reset_gpio);
+			goto err_irq_gpio_req;
+		}
+
+		error = gpio_direction_output(
+					pdata->reset_gpio, 1);
+		if (error) {
+			pr_err("%s: unable to set direction for gpio %d\n",
+				__func__, pdata->reset_gpio);
+			goto err_reset_gpio_req;
+		}
+	}
+
+	mxt_reset_delay(data);
+
 	error = mxt_initialize(data);
 	if (error)
-		goto err_free_object;
+		goto err_reset_gpio_req;
 
 	error = request_threaded_irq(client->irq, NULL, mxt_interrupt,
 			pdata->irqflags, client->dev.driver->name, data);
@@ -1161,9 +2357,13 @@
 		goto err_free_object;
 	}
 
-	error = mxt_make_highchg(data);
-	if (error)
-		goto err_free_irq;
+	if (data->state == APPMODE) {
+		error = mxt_make_highchg(data);
+		if (error) {
+			dev_err(&client->dev, "Failed to make high CHG\n");
+			goto err_free_irq;
+		}
+	}
 
 	error = input_register_device(input_dev);
 	if (error)
@@ -1173,6 +2373,16 @@
 	if (error)
 		goto err_unregister_device;
 
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+	data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN +
+						MXT_SUSPEND_LEVEL;
+	data->early_suspend.suspend = mxt_early_suspend;
+	data->early_suspend.resume = mxt_late_resume;
+	register_early_suspend(&data->early_suspend);
+#endif
+
+	mxt_debugfs_init(data);
+
 	return 0;
 
 err_unregister_device:
@@ -1182,6 +2392,22 @@
 	free_irq(client->irq, data);
 err_free_object:
 	kfree(data->object_table);
+err_reset_gpio_req:
+	if (gpio_is_valid(pdata->reset_gpio))
+		gpio_free(pdata->reset_gpio);
+err_irq_gpio_req:
+	if (gpio_is_valid(pdata->irq_gpio))
+		gpio_free(pdata->irq_gpio);
+err_power_on:
+	if (pdata->power_on)
+		pdata->power_on(false);
+	else
+		mxt_power_on(data, false);
+err_regulator_on:
+	if (pdata->init_hw)
+		pdata->init_hw(false);
+	else
+		mxt_regulator_configure(data, false);
 err_free_mem:
 	input_free_device(input_dev);
 	kfree(data);
@@ -1195,57 +2421,34 @@
 	sysfs_remove_group(&client->dev.kobj, &mxt_attr_group);
 	free_irq(data->irq, data);
 	input_unregister_device(data->input_dev);
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+	unregister_early_suspend(&data->early_suspend);
+#endif
+
+	if (data->pdata->power_on)
+		data->pdata->power_on(false);
+	else
+		mxt_power_on(data, false);
+
+	if (data->pdata->init_hw)
+		data->pdata->init_hw(false);
+	else
+		mxt_regulator_configure(data, false);
+
+	if (gpio_is_valid(data->pdata->reset_gpio))
+		gpio_free(data->pdata->reset_gpio);
+
+	if (gpio_is_valid(data->pdata->irq_gpio))
+		gpio_free(data->pdata->irq_gpio);
+
 	kfree(data->object_table);
 	kfree(data);
 
-	return 0;
-}
-
-#ifdef CONFIG_PM
-static int mxt_suspend(struct device *dev)
-{
-	struct i2c_client *client = to_i2c_client(dev);
-	struct mxt_data *data = i2c_get_clientdata(client);
-	struct input_dev *input_dev = data->input_dev;
-
-	mutex_lock(&input_dev->mutex);
-
-	if (input_dev->users)
-		mxt_stop(data);
-
-	mutex_unlock(&input_dev->mutex);
+	debugfs_remove_recursive(debug_base);
 
 	return 0;
 }
 
-static int mxt_resume(struct device *dev)
-{
-	struct i2c_client *client = to_i2c_client(dev);
-	struct mxt_data *data = i2c_get_clientdata(client);
-	struct input_dev *input_dev = data->input_dev;
-
-	/* Soft reset */
-	mxt_write_object(data, MXT_GEN_COMMAND_T6,
-			MXT_COMMAND_RESET, 1);
-
-	msleep(MXT_RESET_TIME);
-
-	mutex_lock(&input_dev->mutex);
-
-	if (input_dev->users)
-		mxt_start(data);
-
-	mutex_unlock(&input_dev->mutex);
-
-	return 0;
-}
-
-static const struct dev_pm_ops mxt_pm_ops = {
-	.suspend	= mxt_suspend,
-	.resume		= mxt_resume,
-};
-#endif
-
 static const struct i2c_device_id mxt_id[] = {
 	{ "qt602240_ts", 0 },
 	{ "atmel_mxt_ts", 0 },
diff --git a/drivers/input/touchscreen/cy8c_tmg_ts.c b/drivers/input/touchscreen/cy8c_tmg_ts.c
new file mode 100644
index 0000000..f48374e
--- /dev/null
+++ b/drivers/input/touchscreen/cy8c_tmg_ts.c
@@ -0,0 +1,467 @@
+/* drivers/input/touchscreen/cy8c_tmg_ts.c
+ *
+ * Copyright (C) 2007-2008 HTC Corporation.
+ *
+ * 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/cy8c_tmg_ts.h>
+#include <linux/delay.h>
+#include <linux/earlysuspend.h>
+#include <linux/hrtimer.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#define CY8C_REG_START_NEW_SCAN 0x0F
+#define CY8C_REG_INTR_STATUS    0x3C
+#define CY8C_REG_VERSION        0x3E
+
+struct cy8c_ts_data {
+	struct i2c_client *client;
+	struct input_dev *input_dev;
+	int use_irq;
+	struct hrtimer timer;
+	struct work_struct work;
+	uint16_t version;
+	int (*power) (int on);
+	struct early_suspend early_suspend;
+};
+
+struct workqueue_struct *cypress_touch_wq;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void cy8c_ts_early_suspend(struct early_suspend *h);
+static void cy8c_ts_late_resume(struct early_suspend *h);
+#endif
+
+uint16_t sample_count, X_mean, Y_mean, first_touch;
+
+static s32 cy8c_read_word_data(struct i2c_client *client,
+			       u8 command, uint16_t * data)
+{
+	s32 ret = i2c_smbus_read_word_data(client, command);
+	if (ret != -1) {
+		*data = (u16) ((ret << 8) | (ret >> 8));
+	}
+	return ret;
+}
+
+static int cy8c_init_panel(struct cy8c_ts_data *ts)
+{
+	int ret;
+	sample_count = X_mean = Y_mean = first_touch = 0;
+
+	/* clean intr busy */
+	ret = i2c_smbus_write_byte_data(ts->client, CY8C_REG_INTR_STATUS,
+					0x00);
+	if (ret < 0) {
+		dev_err(&ts->client->dev,
+			"cy8c_init_panel failed for clean intr busy\n");
+		goto exit;
+	}
+
+	/* start new scan */
+	ret = i2c_smbus_write_byte_data(ts->client, CY8C_REG_START_NEW_SCAN,
+					0x01);
+	if (ret < 0) {
+		dev_err(&ts->client->dev,
+			"cy8c_init_panel failed for start new scan\n");
+		goto exit;
+	}
+
+exit:
+	return ret;
+}
+
+static void cy8c_ts_reset(struct i2c_client *client)
+{
+	struct cy8c_ts_data *ts = i2c_get_clientdata(client);
+
+	if (ts->power) {
+		ts->power(0);
+		msleep(10);
+		ts->power(1);
+		msleep(10);
+	}
+
+	cy8c_init_panel(ts);
+}
+
+static void cy8c_ts_work_func(struct work_struct *work)
+{
+	struct cy8c_ts_data *ts = container_of(work, struct cy8c_ts_data, work);
+	uint16_t x1, y1, x2, y2;
+	uint8_t is_touch, start_reg, force, area, finger2_pressed;
+	uint8_t buf[11];
+	struct i2c_msg msg[2];
+	int ret = 0;
+
+	x2 = y2 = 0;
+
+	/*printk("%s: enter\n",__func__);*/
+	is_touch = i2c_smbus_read_byte_data(ts->client, 0x20);
+	dev_dbg(&ts->client->dev, "fIsTouch %d,\n", is_touch);
+	if (is_touch < 0 || is_touch > 3) {
+		pr_err("%s: invalid is_touch = %d\n", __func__, is_touch);
+		cy8c_ts_reset(ts->client);
+		msleep(10);
+		goto done;
+	}
+
+	msg[0].addr = ts->client->addr;
+	msg[0].flags = 0;
+	msg[0].len = 1;
+	start_reg = 0x16;
+	msg[0].buf = &start_reg;
+
+	msg[1].addr = ts->client->addr;
+	msg[1].flags = I2C_M_RD;
+	msg[1].len = sizeof(buf);
+	msg[1].buf = buf;
+
+	ret = i2c_transfer(ts->client->adapter, msg, 2);
+	if (ret < 0)
+		goto done;
+
+	/* parse data */
+	force = buf[0];
+	area = buf[1];
+	x1 = (buf[2] << 8) | buf[3];
+	y1 = (buf[6] << 8) | buf[7];
+	is_touch = buf[10];
+
+	if (is_touch == 2) {
+		x2 = (buf[4] << 8) | buf[5];
+		y2 = (buf[8] << 8) | buf[9];
+		finger2_pressed = 1;
+	}
+
+	dev_dbg(&ts->client->dev,
+		"bFingerForce %d, bFingerArea %d \n", force, area);
+	dev_dbg(&ts->client->dev, "x1: %d, y1: %d \n", x1, y1);
+	if (finger2_pressed)
+		dev_dbg(&ts->client->dev, "x2: %d, y2: %d \n", x2, y2);
+
+	/* drop the first one? */
+	if ((is_touch == 1) && (first_touch == 0)) {
+		first_touch = 1;
+		goto done;
+	}
+
+	if (!first_touch)
+		goto done;
+
+	if (is_touch == 2)
+		finger2_pressed = 1;
+
+	input_report_abs(ts->input_dev, ABS_X, x1);
+	input_report_abs(ts->input_dev, ABS_Y, y1);
+	input_report_abs(ts->input_dev, ABS_PRESSURE, force);
+	input_report_abs(ts->input_dev, ABS_TOOL_WIDTH, area);
+	input_report_key(ts->input_dev, BTN_TOUCH, is_touch);
+	input_report_key(ts->input_dev, BTN_2, finger2_pressed);
+
+	if (finger2_pressed) {
+		input_report_abs(ts->input_dev, ABS_HAT0X, x2);
+		input_report_abs(ts->input_dev, ABS_HAT0Y, y2);
+	}
+	input_sync(ts->input_dev);
+
+done:
+	if (is_touch == 0)
+		first_touch = sample_count = 0;
+
+	/* prepare for next intr */
+	i2c_smbus_write_byte_data(ts->client, CY8C_REG_INTR_STATUS, 0x00);
+	if (!ts->use_irq)
+		hrtimer_start(&ts->timer, ktime_set(0, 12500000), HRTIMER_MODE_REL);
+	else
+		enable_irq(ts->client->irq);
+}
+
+static enum hrtimer_restart cy8c_ts_timer_func(struct hrtimer *timer)
+{
+	struct cy8c_ts_data *ts;
+
+	ts = container_of(timer, struct cy8c_ts_data, timer);
+	queue_work(cypress_touch_wq, &ts->work);
+	return HRTIMER_NORESTART;
+}
+
+static irqreturn_t cy8c_ts_irq_handler(int irq, void *dev_id)
+{
+	struct cy8c_ts_data *ts = dev_id;
+
+	disable_irq_nosync(ts->client->irq);
+	queue_work(cypress_touch_wq, &ts->work);
+	return IRQ_HANDLED;
+}
+
+static int cy8c_ts_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	struct cy8c_ts_data *ts;
+	struct cy8c_i2c_platform_data *pdata;
+	uint16_t panel_version;
+	int ret = 0;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		dev_err(&client->dev, "need I2C_FUNC_I2C\n");
+		ret = -ENODEV;
+		goto err_check_functionality_failed;
+	}
+
+	ts = kzalloc(sizeof(struct cy8c_ts_data), GFP_KERNEL);
+	if (ts == NULL) {
+		dev_err(&client->dev, "allocate cy8c_ts_data failed\n");
+		ret = -ENOMEM;
+		goto err_alloc_data_failed;
+	}
+
+	INIT_WORK(&ts->work, cy8c_ts_work_func);
+	ts->client = client;
+	i2c_set_clientdata(client, ts);
+
+	pdata = client->dev.platform_data;
+	if (pdata) {
+		ts->version = pdata->version;
+		ts->power = pdata->power;
+	}
+
+	if (ts->power) {
+		ret = ts->power(1);
+		msleep(10);
+		if (ret < 0) {
+			dev_err(&client->dev, "power on failed\n");
+			goto err_power_failed;
+		}
+	}
+
+	ret = cy8c_read_word_data(ts->client, CY8C_REG_VERSION, &panel_version);
+	if (ret < 0) {
+		dev_err(&client->dev, "init panel failed\n");
+		goto err_detect_failed;
+	}
+	dev_info(&client->dev, "Panel Version %04X\n", panel_version);
+	if (pdata) {
+		while (pdata->version > panel_version) {
+			dev_info(&client->dev, "old tp detected, "
+				 "panel version = %x\n", panel_version);
+			pdata++;
+		}
+	}
+
+	ret = cy8c_init_panel(ts);
+	if (ret < 0) {
+		dev_err(&client->dev, "init panel failed\n");
+		goto err_detect_failed;
+	}
+
+	ts->input_dev = input_allocate_device();
+	if (ts->input_dev == NULL) {
+		ret = -ENOMEM;
+		dev_err(&client->dev, "Failed to allocate input device\n");
+		goto err_input_dev_alloc_failed;
+	}
+	ts->input_dev->name = "cy8c-touchscreen";
+
+	set_bit(EV_SYN, ts->input_dev->evbit);
+	set_bit(EV_ABS, ts->input_dev->evbit);
+	set_bit(EV_KEY, ts->input_dev->evbit);
+	input_set_capability(ts->input_dev, EV_KEY, BTN_TOUCH);
+	input_set_capability(ts->input_dev, EV_KEY, BTN_2);
+
+	input_set_abs_params(ts->input_dev, ABS_X,
+			     pdata->abs_x_min, pdata->abs_x_max, 5, 0);
+	input_set_abs_params(ts->input_dev, ABS_Y,
+			     pdata->abs_y_min, pdata->abs_y_max, 5, 0);
+	input_set_abs_params(ts->input_dev, ABS_HAT0X,
+			     pdata->abs_x_min, pdata->abs_x_max, 0, 0);
+	input_set_abs_params(ts->input_dev, ABS_HAT0Y,
+			     pdata->abs_y_min, pdata->abs_y_max, 0, 0);
+	input_set_abs_params(ts->input_dev, ABS_PRESSURE,
+			     pdata->abs_pressure_min, pdata->abs_pressure_max,
+			     0, 0);
+	input_set_abs_params(ts->input_dev, ABS_TOOL_WIDTH,
+			     pdata->abs_width_min, pdata->abs_width_max, 0, 0);
+
+	ret = input_register_device(ts->input_dev);
+	if (ret) {
+		dev_err(&client->dev,
+			"cy8c_ts_probe: Unable to register %s input device\n",
+			ts->input_dev->name);
+		goto err_input_register_device_failed;
+	}
+
+	if (client->irq) {
+		ret = request_irq(client->irq, cy8c_ts_irq_handler,
+				  IRQF_TRIGGER_LOW, CYPRESS_TMG_NAME, ts);
+		if (ret == 0)
+			ts->use_irq = 1;
+		else
+			dev_err(&client->dev, "request_irq failed\n");
+	}
+
+	if (!ts->use_irq) {
+		hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+		ts->timer.function = cy8c_ts_timer_func;
+		hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
+	}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+	ts->early_suspend.suspend = cy8c_ts_early_suspend;
+	ts->early_suspend.resume = cy8c_ts_late_resume;
+	register_early_suspend(&ts->early_suspend);
+#endif
+
+	dev_info(&client->dev, "Start touchscreen %s in %s mode\n",
+		 ts->input_dev->name, (ts->use_irq ? "interrupt" : "polling"));
+
+	return 0;
+
+err_input_register_device_failed:
+	input_free_device(ts->input_dev);
+
+err_input_dev_alloc_failed:
+	if (ts->power)
+		ts->power(0);
+
+err_detect_failed:
+err_power_failed:
+	kfree(ts);
+
+err_alloc_data_failed:
+err_check_functionality_failed:
+	return ret;
+}
+
+static int cy8c_ts_remove(struct i2c_client *client)
+{
+	struct cy8c_ts_data *ts = i2c_get_clientdata(client);
+
+	unregister_early_suspend(&ts->early_suspend);
+
+	if (ts->use_irq)
+		free_irq(client->irq, ts);
+	else
+		hrtimer_cancel(&ts->timer);
+
+	input_unregister_device(ts->input_dev);
+	kfree(ts);
+
+	return 0;
+}
+
+static int cy8c_ts_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+	struct cy8c_ts_data *ts = i2c_get_clientdata(client);
+	int ret;
+
+	if (ts->use_irq)
+		disable_irq_nosync(client->irq);
+	else
+		hrtimer_cancel(&ts->timer);
+
+	ret = cancel_work_sync(&ts->work);
+	if (ret && ts->use_irq)
+		enable_irq(client->irq);
+
+	if (ts->power)
+		ts->power(0);
+
+	return 0;
+}
+
+static int cy8c_ts_resume(struct i2c_client *client)
+{
+	int ret;
+	struct cy8c_ts_data *ts = i2c_get_clientdata(client);
+
+	if (ts->power) {
+		ret = ts->power(1);
+		if (ret < 0)
+			dev_err(&client->dev,
+				"cy8c_ts_resume power on failed\n");
+		msleep(10);
+
+		cy8c_init_panel(ts);
+	}
+
+	if (ts->use_irq)
+		enable_irq(client->irq);
+	else
+		hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
+
+	return 0;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void cy8c_ts_early_suspend(struct early_suspend *h)
+{
+	struct cy8c_ts_data *ts;
+	ts = container_of(h, struct cy8c_ts_data, early_suspend);
+	cy8c_ts_suspend(ts->client, PMSG_SUSPEND);
+}
+
+static void cy8c_ts_late_resume(struct early_suspend *h)
+{
+	struct cy8c_ts_data *ts;
+	ts = container_of(h, struct cy8c_ts_data, early_suspend);
+	cy8c_ts_resume(ts->client);
+}
+#endif
+
+static const struct i2c_device_id cy8c_ts_i2c_id[] = {
+	{CYPRESS_TMG_NAME, 0},
+	{}
+};
+
+static struct i2c_driver cy8c_ts_driver = {
+	.id_table = cy8c_ts_i2c_id,
+	.probe = cy8c_ts_probe,
+	.remove = cy8c_ts_remove,
+#ifndef CONFIG_HAS_EARLYSUSPEND
+	.suspend = cy8c_ts_suspend,
+	.resume = cy8c_ts_resume,
+#endif
+	.driver = {
+		.name = CYPRESS_TMG_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __devinit cy8c_ts_init(void)
+{
+	cypress_touch_wq = create_singlethread_workqueue("cypress_touch_wq");
+	if (!cypress_touch_wq)
+		return -ENOMEM;
+
+	return i2c_add_driver(&cy8c_ts_driver);
+}
+
+static void __exit cy8c_ts_exit(void)
+{
+	if (cypress_touch_wq)
+		destroy_workqueue(cypress_touch_wq);
+
+	i2c_del_driver(&cy8c_ts_driver);
+}
+
+module_init(cy8c_ts_init);
+module_exit(cy8c_ts_exit);
+
+MODULE_DESCRIPTION("Cypress TMG Touchscreen Driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/input/touchscreen/cy8c_ts.c b/drivers/input/touchscreen/cy8c_ts.c
new file mode 100644
index 0000000..f708582
--- /dev/null
+++ b/drivers/input/touchscreen/cy8c_ts.c
@@ -0,0 +1,824 @@
+/* Source for:
+ * Cypress CY8CTMA300 Prototype touchscreen driver.
+ * drivers/input/touchscreen/cy8c_ts.c
+ *
+ * Copyright (C) 2009, 2010 Cypress Semiconductor, Inc.
+ * Copyright (c) 2010, 2011 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Cypress reserves the right to make changes without further notice
+ * to the materials described herein. Cypress does not assume any
+ * liability arising out of the application described herein.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com
+ *
+ * History:
+ *			(C) 2010 Cypress - Update for GPL distribution
+ *			(C) 2009 Cypress - Assume maintenance ownership
+ *			(C) 2009 Enea - Original prototype
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/input/cy8c_ts.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+#include <linux/earlysuspend.h>
+
+/* Early-suspend level */
+#define CY8C_TS_SUSPEND_LEVEL 1
+#endif
+
+#define CY8CTMA300	0x0
+#define CY8CTMG200	0x1
+#define CY8CTMA340	0x2
+
+#define INVALID_DATA	0xff
+
+#define TOUCHSCREEN_TIMEOUT	(msecs_to_jiffies(10))
+#define INITIAL_DELAY		(msecs_to_jiffies(25000))
+
+struct cy8c_ts_data {
+	u8 x_index;
+	u8 y_index;
+	u8 z_index;
+	u8 id_index;
+	u8 touch_index;
+	u8 data_reg;
+	u8 status_reg;
+	u8 data_size;
+	u8 touch_bytes;
+	u8 update_data;
+	u8 touch_meta_data;
+	u8 finger_size;
+};
+
+static struct cy8c_ts_data devices[] = {
+	[0] = {
+		.x_index = 6,
+		.y_index = 4,
+		.z_index = 3,
+		.id_index = 0,
+		.data_reg = 0x3,
+		.status_reg = 0x1,
+		.update_data = 0x4,
+		.touch_bytes = 8,
+		.touch_meta_data = 3,
+		.finger_size = 70,
+	},
+	[1] = {
+		.x_index = 2,
+		.y_index = 4,
+		.id_index = 6,
+		.data_reg = 0x6,
+		.status_reg = 0x5,
+		.update_data = 0x1,
+		.touch_bytes = 12,
+		.finger_size = 70,
+	},
+	[2] = {
+		.x_index = 1,
+		.y_index = 3,
+		.z_index = 5,
+		.id_index = 6,
+		.data_reg = 0x2,
+		.status_reg = 0,
+		.update_data = 0x4,
+		.touch_bytes = 6,
+		.touch_meta_data = 3,
+		.finger_size = 70,
+	},
+};
+
+struct cy8c_ts {
+	struct i2c_client *client;
+	struct input_dev *input;
+	struct delayed_work work;
+	struct workqueue_struct *wq;
+	struct cy8c_ts_platform_data *pdata;
+	struct cy8c_ts_data *dd;
+	u8 *touch_data;
+	u8 device_id;
+	u8 prev_touches;
+	bool is_suspended;
+	bool int_pending;
+	struct mutex sus_lock;
+	u32 pen_irq;
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+	struct early_suspend		early_suspend;
+#endif
+};
+
+static inline u16 join_bytes(u8 a, u8 b)
+{
+	u16 ab = 0;
+	ab = ab | a;
+	ab = ab << 8 | b;
+	return ab;
+}
+
+static s32 cy8c_ts_write_reg_u8(struct i2c_client *client, u8 reg, u8 val)
+{
+	s32 data;
+
+	data = i2c_smbus_write_byte_data(client, reg, val);
+	if (data < 0)
+		dev_err(&client->dev, "error %d in writing reg 0x%x\n",
+						 data, reg);
+
+	return data;
+}
+
+static s32 cy8c_ts_read_reg_u8(struct i2c_client *client, u8 reg)
+{
+	s32 data;
+
+	data = i2c_smbus_read_byte_data(client, reg);
+	if (data < 0)
+		dev_err(&client->dev, "error %d in reading reg 0x%x\n",
+						 data, reg);
+
+	return data;
+}
+
+static int cy8c_ts_read(struct i2c_client *client, u8 reg, u8 *buf, int num)
+{
+	struct i2c_msg xfer_msg[2];
+
+	xfer_msg[0].addr = client->addr;
+	xfer_msg[0].len = 1;
+	xfer_msg[0].flags = 0;
+	xfer_msg[0].buf = &reg;
+
+	xfer_msg[1].addr = client->addr;
+	xfer_msg[1].len = num;
+	xfer_msg[1].flags = I2C_M_RD;
+	xfer_msg[1].buf = buf;
+
+	return i2c_transfer(client->adapter, xfer_msg, 2);
+}
+
+static void report_data(struct cy8c_ts *ts, u16 x, u16 y, u8 pressure, u8 id)
+{
+	if (ts->pdata->swap_xy)
+		swap(x, y);
+
+	/* handle inverting coordinates */
+	if (ts->pdata->invert_x)
+		x = ts->pdata->res_x - x;
+	if (ts->pdata->invert_y)
+		y = ts->pdata->res_y - y;
+
+	input_report_abs(ts->input, ABS_MT_TRACKING_ID, id);
+	input_report_abs(ts->input, ABS_MT_POSITION_X, x);
+	input_report_abs(ts->input, ABS_MT_POSITION_Y, y);
+	input_report_abs(ts->input, ABS_MT_PRESSURE, pressure);
+	input_mt_sync(ts->input);
+}
+
+static void process_tma300_data(struct cy8c_ts *ts)
+{
+	u8 id, pressure, touches, i;
+	u16 x, y;
+
+	touches = ts->touch_data[ts->dd->touch_index];
+
+	for (i = 0; i < touches; i++) {
+		id = ts->touch_data[i * ts->dd->touch_bytes +
+						ts->dd->id_index];
+		pressure = ts->touch_data[i * ts->dd->touch_bytes +
+							ts->dd->z_index];
+		x = join_bytes(ts->touch_data[i * ts->dd->touch_bytes +
+							ts->dd->x_index],
+			ts->touch_data[i * ts->dd->touch_bytes +
+							ts->dd->x_index + 1]);
+		y = join_bytes(ts->touch_data[i * ts->dd->touch_bytes +
+							ts->dd->y_index],
+			ts->touch_data[i * ts->dd->touch_bytes +
+							ts->dd->y_index + 1]);
+
+		report_data(ts, x, y, pressure, id);
+	}
+
+	for (i = 0; i < ts->prev_touches - touches; i++) {
+		input_report_abs(ts->input, ABS_MT_PRESSURE, 0);
+		input_mt_sync(ts->input);
+	}
+
+	ts->prev_touches = touches;
+	input_sync(ts->input);
+}
+
+static void process_tmg200_data(struct cy8c_ts *ts)
+{
+	u8 id, touches, i;
+	u16 x, y;
+
+	touches = ts->touch_data[ts->dd->touch_index];
+
+	if (touches > 0) {
+		x = join_bytes(ts->touch_data[ts->dd->x_index],
+				ts->touch_data[ts->dd->x_index+1]);
+		y = join_bytes(ts->touch_data[ts->dd->y_index],
+				ts->touch_data[ts->dd->y_index+1]);
+		id = ts->touch_data[ts->dd->id_index];
+
+		report_data(ts, x, y, 255, id - 1);
+
+		if (touches == 2) {
+			x = join_bytes(ts->touch_data[ts->dd->x_index+5],
+					ts->touch_data[ts->dd->x_index+6]);
+			y = join_bytes(ts->touch_data[ts->dd->y_index+5],
+				ts->touch_data[ts->dd->y_index+6]);
+			id = ts->touch_data[ts->dd->id_index+5];
+
+			report_data(ts, x, y, 255, id - 1);
+		}
+	} else {
+		for (i = 0; i < ts->prev_touches; i++) {
+			input_report_abs(ts->input, ABS_MT_PRESSURE,	0);
+			input_mt_sync(ts->input);
+		}
+	}
+
+	input_sync(ts->input);
+	ts->prev_touches = touches;
+}
+
+static void cy8c_ts_xy_worker(struct work_struct *work)
+{
+	int rc;
+	struct cy8c_ts *ts = container_of(work, struct cy8c_ts,
+				 work.work);
+
+	mutex_lock(&ts->sus_lock);
+	if (ts->is_suspended == true) {
+		dev_dbg(&ts->client->dev, "TS is supended\n");
+		ts->int_pending = true;
+		mutex_unlock(&ts->sus_lock);
+		return;
+	}
+	mutex_unlock(&ts->sus_lock);
+
+	/* read data from DATA_REG */
+	rc = cy8c_ts_read(ts->client, ts->dd->data_reg, ts->touch_data,
+							ts->dd->data_size);
+	if (rc < 0) {
+		dev_err(&ts->client->dev, "read failed\n");
+		goto schedule;
+	}
+
+	if (ts->touch_data[ts->dd->touch_index] == INVALID_DATA)
+		goto schedule;
+
+	if ((ts->device_id == CY8CTMA300) || (ts->device_id == CY8CTMA340))
+		process_tma300_data(ts);
+	else
+		process_tmg200_data(ts);
+
+schedule:
+	enable_irq(ts->pen_irq);
+
+	/* write to STATUS_REG to update coordinates*/
+	rc = cy8c_ts_write_reg_u8(ts->client, ts->dd->status_reg,
+						ts->dd->update_data);
+	if (rc < 0) {
+		dev_err(&ts->client->dev, "write failed, try once more\n");
+
+		rc = cy8c_ts_write_reg_u8(ts->client, ts->dd->status_reg,
+						ts->dd->update_data);
+		if (rc < 0)
+			dev_err(&ts->client->dev, "write failed, exiting\n");
+	}
+}
+
+static irqreturn_t cy8c_ts_irq(int irq, void *dev_id)
+{
+	struct cy8c_ts *ts = dev_id;
+
+	disable_irq_nosync(irq);
+
+	queue_delayed_work(ts->wq, &ts->work, 0);
+
+	return IRQ_HANDLED;
+}
+
+static int cy8c_ts_init_ts(struct i2c_client *client, struct cy8c_ts *ts)
+{
+	struct input_dev *input_device;
+	int rc = 0;
+
+	ts->dd = &devices[ts->device_id];
+
+	if (!ts->pdata->nfingers) {
+		dev_err(&client->dev, "Touches information not specified\n");
+		return -EINVAL;
+	}
+
+	if (ts->device_id == CY8CTMA300) {
+		if (ts->pdata->nfingers > 10) {
+			dev_err(&client->dev, "Touches >=1 & <= 10\n");
+			return -EINVAL;
+		}
+		ts->dd->data_size = ts->pdata->nfingers * ts->dd->touch_bytes +
+						ts->dd->touch_meta_data;
+		ts->dd->touch_index = ts->pdata->nfingers *
+						ts->dd->touch_bytes;
+	} else if (ts->device_id == CY8CTMG200) {
+		if (ts->pdata->nfingers > 2) {
+			dev_err(&client->dev, "Touches >=1 & <= 2\n");
+			return -EINVAL;
+		}
+		ts->dd->data_size = ts->dd->touch_bytes;
+		ts->dd->touch_index = 0x0;
+	} else if (ts->device_id == CY8CTMA340) {
+		if (ts->pdata->nfingers > 10) {
+			dev_err(&client->dev, "Touches >=1 & <= 10\n");
+			return -EINVAL;
+		}
+		ts->dd->data_size = ts->pdata->nfingers * ts->dd->touch_bytes +
+						ts->dd->touch_meta_data;
+		ts->dd->touch_index = 0x0;
+	}
+
+	ts->touch_data = kzalloc(ts->dd->data_size, GFP_KERNEL);
+	if (!ts->touch_data) {
+		pr_err("%s: Unable to allocate memory\n", __func__);
+		return -ENOMEM;
+	}
+
+	ts->prev_touches = 0;
+
+	input_device = input_allocate_device();
+	if (!input_device) {
+		rc = -ENOMEM;
+		goto error_alloc_dev;
+	}
+
+	ts->input = input_device;
+	input_device->name = ts->pdata->ts_name;
+	input_device->id.bustype = BUS_I2C;
+	input_device->dev.parent = &client->dev;
+	input_set_drvdata(input_device, ts);
+
+	__set_bit(EV_ABS, input_device->evbit);
+
+	if (ts->device_id == CY8CTMA340) {
+		/* set up virtual key */
+		__set_bit(EV_KEY, input_device->evbit);
+		/* set dummy key to make driver work with virtual keys */
+		input_set_capability(input_device, EV_KEY, KEY_PROG1);
+	}
+
+	input_set_abs_params(input_device, ABS_MT_POSITION_X,
+			ts->pdata->dis_min_x, ts->pdata->dis_max_x, 0, 0);
+	input_set_abs_params(input_device, ABS_MT_POSITION_Y,
+			ts->pdata->dis_min_y, ts->pdata->dis_max_y, 0, 0);
+	input_set_abs_params(input_device, ABS_MT_PRESSURE,
+			ts->pdata->min_touch, ts->pdata->max_touch, 0, 0);
+	input_set_abs_params(input_device, ABS_MT_TRACKING_ID,
+			ts->pdata->min_tid, ts->pdata->max_tid, 0, 0);
+
+	ts->wq = create_singlethread_workqueue("kworkqueue_ts");
+	if (!ts->wq) {
+		dev_err(&client->dev, "Could not create workqueue\n");
+		goto error_wq_create;
+	}
+
+	INIT_DELAYED_WORK(&ts->work, cy8c_ts_xy_worker);
+
+	rc = input_register_device(input_device);
+	if (rc)
+		goto error_unreg_device;
+
+	return 0;
+
+error_unreg_device:
+	destroy_workqueue(ts->wq);
+error_wq_create:
+	input_free_device(input_device);
+error_alloc_dev:
+	kfree(ts->touch_data);
+	return rc;
+}
+
+#ifdef CONFIG_PM
+static int cy8c_ts_suspend(struct device *dev)
+{
+	struct cy8c_ts *ts = dev_get_drvdata(dev);
+	int rc = 0;
+
+	if (device_may_wakeup(dev)) {
+		/* mark suspend flag */
+		mutex_lock(&ts->sus_lock);
+		ts->is_suspended = true;
+		mutex_unlock(&ts->sus_lock);
+
+		enable_irq_wake(ts->pen_irq);
+	} else {
+		disable_irq_nosync(ts->pen_irq);
+
+		rc = cancel_delayed_work_sync(&ts->work);
+
+		if (rc) {
+			/* missed the worker, write to STATUS_REG to
+			   acknowledge interrupt */
+			rc = cy8c_ts_write_reg_u8(ts->client,
+				ts->dd->status_reg, ts->dd->update_data);
+			if (rc < 0) {
+				dev_err(&ts->client->dev,
+					"write failed, try once more\n");
+
+				rc = cy8c_ts_write_reg_u8(ts->client,
+					ts->dd->status_reg,
+					ts->dd->update_data);
+				if (rc < 0)
+					dev_err(&ts->client->dev,
+						"write failed, exiting\n");
+			}
+
+			enable_irq(ts->pen_irq);
+		}
+
+		gpio_free(ts->pdata->irq_gpio);
+
+		if (ts->pdata->power_on) {
+			rc = ts->pdata->power_on(0);
+			if (rc) {
+				dev_err(dev, "unable to goto suspend\n");
+				return rc;
+			}
+		}
+	}
+	return 0;
+}
+
+static int cy8c_ts_resume(struct device *dev)
+{
+	struct cy8c_ts *ts = dev_get_drvdata(dev);
+	int rc = 0;
+
+	if (device_may_wakeup(dev)) {
+		disable_irq_wake(ts->pen_irq);
+
+		mutex_lock(&ts->sus_lock);
+		ts->is_suspended = false;
+
+		if (ts->int_pending == true) {
+			ts->int_pending = false;
+
+			/* start a delayed work */
+			queue_delayed_work(ts->wq, &ts->work, 0);
+		}
+		mutex_unlock(&ts->sus_lock);
+
+	} else {
+		if (ts->pdata->power_on) {
+			rc = ts->pdata->power_on(1);
+			if (rc) {
+				dev_err(dev, "unable to resume\n");
+				return rc;
+			}
+		}
+
+		/* configure touchscreen interrupt gpio */
+		rc = gpio_request(ts->pdata->irq_gpio, "cy8c_irq_gpio");
+		if (rc) {
+			pr_err("%s: unable to request gpio %d\n",
+				__func__, ts->pdata->irq_gpio);
+			goto err_power_off;
+		}
+
+		rc = gpio_direction_input(ts->pdata->irq_gpio);
+		if (rc) {
+			pr_err("%s: unable to set direction for gpio %d\n",
+				__func__, ts->pdata->irq_gpio);
+			goto err_gpio_free;
+		}
+
+		enable_irq(ts->pen_irq);
+
+		/* Clear the status register of the TS controller */
+		rc = cy8c_ts_write_reg_u8(ts->client,
+			ts->dd->status_reg, ts->dd->update_data);
+		if (rc < 0) {
+			dev_err(&ts->client->dev,
+				"write failed, try once more\n");
+
+			rc = cy8c_ts_write_reg_u8(ts->client,
+				ts->dd->status_reg,
+				ts->dd->update_data);
+			if (rc < 0)
+				dev_err(&ts->client->dev,
+					"write failed, exiting\n");
+		}
+	}
+	return 0;
+err_gpio_free:
+	gpio_free(ts->pdata->irq_gpio);
+err_power_off:
+	if (ts->pdata->power_on)
+		rc = ts->pdata->power_on(0);
+	return rc;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void cy8c_ts_early_suspend(struct early_suspend *h)
+{
+	struct cy8c_ts *ts = container_of(h, struct cy8c_ts, early_suspend);
+
+	cy8c_ts_suspend(&ts->client->dev);
+}
+
+static void cy8c_ts_late_resume(struct early_suspend *h)
+{
+	struct cy8c_ts *ts = container_of(h, struct cy8c_ts, early_suspend);
+
+	cy8c_ts_resume(&ts->client->dev);
+}
+#endif
+
+static struct dev_pm_ops cy8c_ts_pm_ops = {
+#ifndef CONFIG_HAS_EARLYSUSPEND
+	.suspend	= cy8c_ts_suspend,
+	.resume		= cy8c_ts_resume,
+#endif
+};
+#endif
+
+static int __devinit cy8c_ts_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct cy8c_ts *ts;
+	struct cy8c_ts_platform_data *pdata = client->dev.platform_data;
+	int rc, temp_reg;
+
+	if (!pdata) {
+		dev_err(&client->dev, "platform data is required!\n");
+		return -EINVAL;
+	}
+
+	if (!i2c_check_functionality(client->adapter,
+				     I2C_FUNC_SMBUS_READ_WORD_DATA)) {
+		dev_err(&client->dev, "I2C functionality not supported\n");
+		return -EIO;
+	}
+
+	ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	/* Enable runtime PM ops, start in ACTIVE mode */
+	rc = pm_runtime_set_active(&client->dev);
+	if (rc < 0)
+		dev_dbg(&client->dev, "unable to set runtime pm state\n");
+	pm_runtime_enable(&client->dev);
+
+	ts->client = client;
+	ts->pdata = pdata;
+	i2c_set_clientdata(client, ts);
+	ts->device_id = id->driver_data;
+
+	if (ts->pdata->dev_setup) {
+		rc = ts->pdata->dev_setup(1);
+		if (rc < 0) {
+			dev_err(&client->dev, "dev setup failed\n");
+			goto error_touch_data_alloc;
+		}
+	}
+
+	/* power on the device */
+	if (ts->pdata->power_on) {
+		rc = ts->pdata->power_on(1);
+		if (rc) {
+			pr_err("%s: Unable to power on the device\n", __func__);
+			goto error_dev_setup;
+		}
+	}
+
+	/* read one byte to make sure i2c device exists */
+	if (id->driver_data == CY8CTMA300)
+		temp_reg = 0x01;
+	else if (id->driver_data == CY8CTMA340)
+		temp_reg = 0x00;
+	else
+		temp_reg = 0x05;
+
+	rc = cy8c_ts_read_reg_u8(client, temp_reg);
+	if (rc < 0) {
+		dev_err(&client->dev, "i2c sanity check failed\n");
+		goto error_power_on;
+	}
+
+	ts->is_suspended = false;
+	ts->int_pending = false;
+	mutex_init(&ts->sus_lock);
+
+	rc = cy8c_ts_init_ts(client, ts);
+	if (rc < 0) {
+		dev_err(&client->dev, "CY8CTMG200-TMA300 init failed\n");
+		goto error_mutex_destroy;
+	}
+
+	if (ts->pdata->resout_gpio < 0)
+		goto config_irq_gpio;
+
+	/* configure touchscreen reset out gpio */
+	rc = gpio_request(ts->pdata->resout_gpio, "cy8c_resout_gpio");
+	if (rc) {
+		pr_err("%s: unable to request gpio %d\n",
+			__func__, ts->pdata->resout_gpio);
+		goto error_uninit_ts;
+	}
+
+	rc = gpio_direction_output(ts->pdata->resout_gpio, 0);
+	if (rc) {
+		pr_err("%s: unable to set direction for gpio %d\n",
+			__func__, ts->pdata->resout_gpio);
+		goto error_resout_gpio_dir;
+	}
+	/* reset gpio stabilization time */
+	msleep(20);
+
+config_irq_gpio:
+	/* configure touchscreen interrupt gpio */
+	rc = gpio_request(ts->pdata->irq_gpio, "cy8c_irq_gpio");
+	if (rc) {
+		pr_err("%s: unable to request gpio %d\n",
+			__func__, ts->pdata->irq_gpio);
+		goto error_irq_gpio_req;
+	}
+
+	rc = gpio_direction_input(ts->pdata->irq_gpio);
+	if (rc) {
+		pr_err("%s: unable to set direction for gpio %d\n",
+			__func__, ts->pdata->irq_gpio);
+		goto error_irq_gpio_dir;
+	}
+
+	ts->pen_irq = gpio_to_irq(ts->pdata->irq_gpio);
+	rc = request_irq(ts->pen_irq, cy8c_ts_irq,
+				IRQF_TRIGGER_FALLING,
+				ts->client->dev.driver->name, ts);
+	if (rc) {
+		dev_err(&ts->client->dev, "could not request irq\n");
+		goto error_req_irq_fail;
+	}
+
+	/* Clear the status register of the TS controller */
+	rc = cy8c_ts_write_reg_u8(ts->client, ts->dd->status_reg,
+						ts->dd->update_data);
+	if (rc < 0) {
+		/* Do multiple writes in case of failure */
+		dev_err(&ts->client->dev, "%s: write failed %d"
+				"trying again\n", __func__, rc);
+		rc = cy8c_ts_write_reg_u8(ts->client,
+			ts->dd->status_reg, ts->dd->update_data);
+		if (rc < 0) {
+			dev_err(&ts->client->dev, "%s: write failed"
+				"second time(%d)\n", __func__, rc);
+		}
+	}
+
+	device_init_wakeup(&client->dev, ts->pdata->wakeup);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN +
+						CY8C_TS_SUSPEND_LEVEL;
+	ts->early_suspend.suspend = cy8c_ts_early_suspend;
+	ts->early_suspend.resume = cy8c_ts_late_resume;
+	register_early_suspend(&ts->early_suspend);
+#endif
+
+	return 0;
+error_req_irq_fail:
+error_irq_gpio_dir:
+	gpio_free(ts->pdata->irq_gpio);
+error_irq_gpio_req:
+error_resout_gpio_dir:
+	if (ts->pdata->resout_gpio >= 0)
+		gpio_free(ts->pdata->resout_gpio);
+error_uninit_ts:
+	destroy_workqueue(ts->wq);
+	input_unregister_device(ts->input);
+	kfree(ts->touch_data);
+error_mutex_destroy:
+	mutex_destroy(&ts->sus_lock);
+error_power_on:
+	if (ts->pdata->power_on)
+		ts->pdata->power_on(0);
+error_dev_setup:
+	if (ts->pdata->dev_setup)
+		ts->pdata->dev_setup(0);
+error_touch_data_alloc:
+	pm_runtime_set_suspended(&client->dev);
+	pm_runtime_disable(&client->dev);
+	kfree(ts);
+	return rc;
+}
+
+static int __devexit cy8c_ts_remove(struct i2c_client *client)
+{
+	struct cy8c_ts *ts = i2c_get_clientdata(client);
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+	unregister_early_suspend(&ts->early_suspend);
+#endif
+	pm_runtime_set_suspended(&client->dev);
+	pm_runtime_disable(&client->dev);
+
+	device_init_wakeup(&client->dev, 0);
+
+	cancel_delayed_work_sync(&ts->work);
+
+	free_irq(ts->pen_irq, ts);
+
+	gpio_free(ts->pdata->irq_gpio);
+
+	if (ts->pdata->resout_gpio >= 0)
+		gpio_free(ts->pdata->resout_gpio);
+
+	destroy_workqueue(ts->wq);
+
+	input_unregister_device(ts->input);
+
+	mutex_destroy(&ts->sus_lock);
+
+	if (ts->pdata->power_on)
+		ts->pdata->power_on(0);
+
+	if (ts->pdata->dev_setup)
+		ts->pdata->dev_setup(0);
+
+	kfree(ts->touch_data);
+	kfree(ts);
+
+	return 0;
+}
+
+static const struct i2c_device_id cy8c_ts_id[] = {
+	{"cy8ctma300", CY8CTMA300},
+	{"cy8ctmg200", CY8CTMG200},
+	{"cy8ctma340", CY8CTMA340},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, cy8c_ts_id);
+
+
+static struct i2c_driver cy8c_ts_driver = {
+	.driver = {
+		.name = "cy8c_ts",
+		.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm = &cy8c_ts_pm_ops,
+#endif
+	},
+	.probe		= cy8c_ts_probe,
+	.remove		= __devexit_p(cy8c_ts_remove),
+	.id_table	= cy8c_ts_id,
+};
+
+static int __init cy8c_ts_init(void)
+{
+	return i2c_add_driver(&cy8c_ts_driver);
+}
+/* Making this as late init to avoid power fluctuations
+ * during LCD initialization.
+ */
+late_initcall(cy8c_ts_init);
+
+static void __exit cy8c_ts_exit(void)
+{
+	return i2c_del_driver(&cy8c_ts_driver);
+}
+module_exit(cy8c_ts_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("CY8CTMA340-CY8CTMG200 touchscreen controller driver");
+MODULE_AUTHOR("Cypress");
+MODULE_ALIAS("platform:cy8c_ts");
diff --git a/drivers/input/touchscreen/cyttsp-i2c-qc.c b/drivers/input/touchscreen/cyttsp-i2c-qc.c
new file mode 100644
index 0000000..e82dd13
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp-i2c-qc.c
@@ -0,0 +1,3132 @@
+/* Source for:
+ * Cypress TrueTouch(TM) Standard Product I2C touchscreen driver.
+ * drivers/input/touchscreen/cyttsp-i2c.c
+ *
+ * Copyright (C) 2009, 2010 Cypress Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Cypress reserves the right to make changes without further notice
+ * to the materials described herein. Cypress does not assume any
+ * liability arising out of the application described herein.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/byteorder/generic.h>
+#include <linux/bitops.h>
+#include <linux/pm_runtime.h>
+#include <linux/firmware.h>
+#include <linux/mutex.h>
+#include <linux/regulator/consumer.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+
+#define CY_DECLARE_GLOBALS
+
+#include <linux/cyttsp-qc.h>
+
+uint32_t cyttsp_tsdebug1 = 0xff;
+module_param_named(tsdebug1, cyttsp_tsdebug1, uint, 0664);
+
+#define FW_FNAME_LEN 40
+#define TTSP_BUFF_SIZE 50
+
+/* CY TTSP I2C Driver private data */
+struct cyttsp {
+	struct i2c_client *client;
+	struct input_dev *input;
+	struct timer_list timer;
+	struct mutex mutex;
+	char phys[32];
+	struct cyttsp_platform_data *platform_data;
+	u8 num_prv_st_tch;
+	u16 fw_start_addr;
+	u16 act_trk[CY_NUM_TRK_ID];
+	u16 prv_st_tch[CY_NUM_ST_TCH_ID];
+	u16 prv_mt_tch[CY_NUM_MT_TCH_ID];
+	u16 prv_mt_pos[CY_NUM_TRK_ID][2];
+	atomic_t irq_enabled;
+	bool cyttsp_update_fw;
+	bool cyttsp_fwloader_mode;
+	bool is_suspended;
+	struct regulator **vdd;
+	char fw_fname[FW_FNAME_LEN];
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	struct early_suspend early_suspend;
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+};
+static u8 irq_cnt;		/* comparison counter with register valuw */
+static u32 irq_cnt_total;	/* total interrupts */
+static u32 irq_err_cnt;		/* count number of touch interrupts with err */
+#define CY_IRQ_CNT_MASK	0x000000FF	/* mapped for sizeof count in reg */
+#define CY_IRQ_CNT_REG	0x00		/* tt_undef[0]=reg 0x1B - Gen3 only */
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void cyttsp_early_suspend(struct early_suspend *handler);
+static void cyttsp_late_resume(struct early_suspend *handler);
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+
+
+/* ****************************************************************************
+ * Prototypes for static functions
+ * ************************************************************************** */
+static irqreturn_t cyttsp_irq(int irq, void *handle);
+static int cyttsp_inlist(u16 prev_track[],
+			u8 cur_trk_id, u8 *prev_loc, u8 num_touches);
+static int cyttsp_next_avail_inlist(u16 cur_trk[],
+			u8 *new_loc, u8 num_touches);
+static int cyttsp_putbl(struct cyttsp *ts, int show,
+			int show_status, int show_version, int show_cid);
+static int __devinit cyttsp_probe(struct i2c_client *client,
+			const struct i2c_device_id *id);
+static int __devexit cyttsp_remove(struct i2c_client *client);
+static int cyttsp_resume(struct device *dev);
+static int cyttsp_suspend(struct device *dev);
+
+/* Static variables */
+static struct cyttsp_gen3_xydata_t g_xy_data;
+static struct cyttsp_bootloader_data_t g_bl_data;
+static struct cyttsp_sysinfo_data_t g_sysinfo_data;
+static const struct i2c_device_id cyttsp_id[] = {
+	{ CY_I2C_NAME, 0 },  { }
+};
+static u8 bl_cmd[] = {
+	CY_BL_FILE0, CY_BL_CMD, CY_BL_EXIT,
+	CY_BL_KEY0, CY_BL_KEY1, CY_BL_KEY2,
+	CY_BL_KEY3, CY_BL_KEY4, CY_BL_KEY5,
+	CY_BL_KEY6, CY_BL_KEY7};
+
+MODULE_DEVICE_TABLE(i2c, cyttsp_id);
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops cyttsp_pm_ops = {
+#ifndef CONFIG_HAS_EARLYSUSPEND
+	.suspend = cyttsp_suspend,
+	.resume = cyttsp_resume,
+#endif
+};
+#endif
+
+static struct i2c_driver cyttsp_driver = {
+	.driver = {
+		.name = CY_I2C_NAME,
+		.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm = &cyttsp_pm_ops,
+#endif
+	},
+	.probe = cyttsp_probe,
+	.remove = __devexit_p(cyttsp_remove),
+	.id_table = cyttsp_id,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen driver");
+MODULE_AUTHOR("Cypress");
+
+static ssize_t cyttsp_irq_status(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+	struct cyttsp *ts = i2c_get_clientdata(client);
+	return snprintf(buf, TTSP_BUFF_SIZE, "%u\n",
+				atomic_read(&ts->irq_enabled));
+}
+
+static ssize_t cyttsp_irq_enable(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t size)
+{
+	struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+	struct cyttsp *ts = i2c_get_clientdata(client);
+	int err = 0;
+	unsigned long value;
+
+	if (size > 2)
+		return -EINVAL;
+
+	err = strict_strtoul(buf, 10, &value);
+	if (err != 0)
+		return err;
+
+	switch (value) {
+	case 0:
+		if (atomic_cmpxchg(&ts->irq_enabled, 1, 0)) {
+			pr_info("touch irq disabled!\n");
+			disable_irq_nosync(ts->client->irq);
+		}
+		err = size;
+		break;
+	case 1:
+		if (!atomic_cmpxchg(&ts->irq_enabled, 0, 1)) {
+			pr_info("touch irq enabled!\n");
+			enable_irq(ts->client->irq);
+		}
+		err = size;
+		break;
+	default:
+		pr_info("cyttsp_irq_enable failed -> irq_enabled = %d\n",
+		atomic_read(&ts->irq_enabled));
+		err = -EINVAL;
+		break;
+	}
+
+	return err;
+}
+
+static DEVICE_ATTR(irq_enable, 0664, cyttsp_irq_status, cyttsp_irq_enable);
+
+static ssize_t cyttsp_fw_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, TTSP_BUFF_SIZE, "%d.%d.%d\n", g_bl_data.appid_lo,
+				g_bl_data.appver_hi, g_bl_data.appver_lo);
+}
+
+static DEVICE_ATTR(cyttsp_fw_ver, 0664, cyttsp_fw_show, NULL);
+
+/* firmware flashing block */
+#define BLK_SIZE     16
+#define DATA_REC_LEN 64
+#define BLK_SEED     0xff
+#define RECAL_REG    0x1b
+
+enum bl_commands {
+	BL_CMD_WRBLK     = 0x39,
+	BL_CMD_INIT      = 0x38,
+	BL_CMD_TERMINATE = 0x3b,
+};
+/* TODO: Add key as part of platform data */
+#define KEY_CS  (0 + 1 + 2 + 3 + 4 + 5 + 6 + 7)
+#define KEY {0, 1, 2, 3, 4, 5, 6, 7}
+
+static const  char _key[] = KEY;
+#define KEY_LEN sizeof(_key)
+
+static int rec_cnt;
+struct fw_record {
+	u8 seed;
+	u8 cmd;
+	u8 key[KEY_LEN];
+	u8 blk_hi;
+	u8 blk_lo;
+	u8 data[DATA_REC_LEN];
+	u8 data_cs;
+	u8 rec_cs;
+};
+#define fw_rec_size (sizeof(struct fw_record))
+
+struct cmd_record {
+	u8 reg;
+	u8 seed;
+	u8 cmd;
+	u8 key[KEY_LEN];
+};
+#define cmd_rec_size (sizeof(struct cmd_record))
+
+static struct fw_record data_record = {
+	.seed = BLK_SEED,
+	.cmd = BL_CMD_WRBLK,
+	.key = KEY,
+};
+
+static const struct cmd_record terminate_rec = {
+	.reg = 0,
+	.seed = BLK_SEED,
+	.cmd = BL_CMD_TERMINATE,
+	.key = KEY,
+};
+static const struct cmd_record initiate_rec = {
+	.reg = 0,
+	.seed = BLK_SEED,
+	.cmd = BL_CMD_INIT,
+	.key = KEY,
+};
+
+#define BL_REC1_ADDR          0x0780
+#define BL_REC2_ADDR          0x07c0
+#define BL_CHECKSUM_MASK      0x01
+
+#define ID_INFO_REC           ":40078000"
+#define ID_INFO_OFFSET_IN_REC 77
+
+#define REC_START_CHR     ':'
+#define REC_LEN_OFFSET     1
+#define REC_ADDR_HI_OFFSET 3
+#define REC_ADDR_LO_OFFSET 5
+#define REC_TYPE_OFFSET    7
+#define REC_DATA_OFFSET    9
+#define REC_LINE_SIZE	141
+
+#define NUM_CHAR_IN_HEX 2
+#define ID_INFO_REC_LEN 9
+
+static int cyttsp_soft_reset(struct cyttsp *ts)
+{
+	int retval = 0, tries = 0;
+	u8 host_reg = CY_SOFT_RESET_MODE;
+
+	do {
+		retval = i2c_smbus_write_i2c_block_data(ts->client,
+				CY_REG_BASE, sizeof(host_reg), &host_reg);
+		if (retval < 0)
+			msleep(20);
+	} while (tries++ < 10 && (retval < 0));
+
+	if (retval < 0) {
+		pr_err("%s: failed\n", __func__);
+		return retval;
+	}
+
+	tries = 0;
+	do {
+		msleep(20);
+		cyttsp_putbl(ts, 1, true, true, false);
+	} while (g_bl_data.bl_status != 0x10 &&
+		g_bl_data.bl_status != 0x11 &&
+		tries++ < 100);
+
+	if (g_bl_data.bl_status != 0x11 && g_bl_data.bl_status != 0x10)
+		return -EINVAL;
+
+	return 0;
+}
+
+static void cyttsp_exit_bl_mode(struct cyttsp *ts)
+{
+	int retval, tries = 0;
+
+	do {
+		retval = i2c_smbus_write_i2c_block_data(ts->client,
+			CY_REG_BASE, sizeof(bl_cmd), bl_cmd);
+		if (retval < 0)
+			msleep(20);
+	} while (tries++ < 10 && (retval < 0));
+}
+
+static void cyttsp_set_sysinfo_mode(struct cyttsp *ts)
+{
+	int retval, tries = 0;
+	u8 host_reg = CY_SYSINFO_MODE;
+
+	do {
+		retval = i2c_smbus_write_i2c_block_data(ts->client,
+			CY_REG_BASE, sizeof(host_reg), &host_reg);
+		if (retval < 0)
+			msleep(20);
+	} while (tries++ < 10 && (retval < 0));
+
+	/* wait for TTSP Device to complete switch to SysInfo mode */
+	if (!(retval < 0)) {
+		retval = i2c_smbus_read_i2c_block_data(ts->client,
+				CY_REG_BASE,
+				sizeof(struct cyttsp_sysinfo_data_t),
+				(u8 *)&g_sysinfo_data);
+	} else
+		pr_err("%s: failed\n", __func__);
+}
+
+static void cyttsp_set_opmode(struct cyttsp *ts)
+{
+	int retval, tries = 0;
+	u8 host_reg = CY_OP_MODE;
+
+	do {
+		retval = i2c_smbus_write_i2c_block_data(ts->client,
+				CY_REG_BASE, sizeof(host_reg), &host_reg);
+		if (retval < 0)
+			msleep(20);
+	} while (tries++ < 10 && (retval < 0));
+}
+
+static int str2uc(char *str, u8 *val)
+{
+	char substr[3];
+	unsigned long ulval;
+	int rc;
+
+	if (!str)
+		return -EINVAL;
+
+	if (strnlen(str, NUM_CHAR_IN_HEX) < 2)
+		return -EINVAL;
+
+	substr[0] = str[0];
+	substr[1] = str[1];
+	substr[2] = '\0';
+
+	rc = strict_strtoul(substr, 16, &ulval);
+	if (rc != 0)
+		return rc;
+
+	*val = (u8) ulval;
+
+	return 0;
+}
+
+static int flash_block(struct cyttsp *ts, u8 *blk, int len)
+{
+	int retval, i, tries = 0;
+	char buf[(2 * (BLK_SIZE + 1)) + 1];
+	char *p = buf;
+
+	for (i = 0; i < len; i++, p += 2)
+		snprintf(p, TTSP_BUFF_SIZE, "%02x", blk[i]);
+	pr_debug("%s: size %d, pos %ld payload %s\n",
+		       __func__, len, (long)0, buf);
+
+	do {
+		retval = i2c_smbus_write_i2c_block_data(ts->client,
+			CY_REG_BASE, len, blk);
+		if (retval < 0)
+			msleep(20);
+	} while (tries++ < 20 && (retval < 0));
+
+	if (retval < 0) {
+		pr_err("%s: failed\n", __func__);
+		return retval;
+	}
+
+	return 0;
+}
+
+static int flash_command(struct cyttsp *ts, const struct cmd_record *record)
+{
+	return flash_block(ts, (u8 *)record, cmd_rec_size);
+}
+
+static void init_data_record(struct fw_record *rec, unsigned short addr)
+{
+	addr >>= 6;
+	rec->blk_hi = (addr >> 8) & 0xff;
+	rec->blk_lo = addr & 0xff;
+	rec->rec_cs = rec->blk_hi + rec->blk_lo +
+			(unsigned char)(BLK_SEED + BL_CMD_WRBLK + KEY_CS);
+	rec->data_cs = 0;
+}
+
+static int check_record(struct cyttsp *ts, u8 *rec)
+{
+	int rc;
+	u16 addr;
+	u8 r_len, type, hi_off, lo_off;
+
+	rc = str2uc(rec + REC_LEN_OFFSET, &r_len);
+	if (rc < 0)
+		return rc;
+
+	rc = str2uc(rec + REC_TYPE_OFFSET, &type);
+	if (rc < 0)
+		return rc;
+
+	if (*rec != REC_START_CHR || r_len != DATA_REC_LEN || type != 0)
+		return -EINVAL;
+
+	rc = str2uc(rec + REC_ADDR_HI_OFFSET, &hi_off);
+	if (rc < 0)
+		return rc;
+
+	rc = str2uc(rec + REC_ADDR_LO_OFFSET, &lo_off);
+	if (rc < 0)
+		return rc;
+
+	addr = (hi_off << 8) | lo_off;
+
+	if (addr >= ts->fw_start_addr || addr == BL_REC1_ADDR
+			|| addr == BL_REC2_ADDR)
+		return 0;
+
+	return -EINVAL;
+}
+
+static struct fw_record *prepare_record(u8 *rec)
+{
+	int i, rc;
+	u16 addr;
+	u8 hi_off, lo_off;
+	u8 *p;
+
+	rc = str2uc(rec + REC_ADDR_HI_OFFSET, &hi_off);
+	if (rc < 0)
+		return ERR_PTR((long) rc);
+
+	rc = str2uc(rec + REC_ADDR_LO_OFFSET, &lo_off);
+	if (rc < 0)
+		return ERR_PTR((long) rc);
+
+	addr = (hi_off << 8) | lo_off;
+
+	init_data_record(&data_record, addr);
+	p = rec + REC_DATA_OFFSET;
+	for (i = 0; i < DATA_REC_LEN; i++) {
+		rc = str2uc(p, &data_record.data[i]);
+		if (rc < 0)
+			return ERR_PTR((long) rc);
+		data_record.data_cs += data_record.data[i];
+		data_record.rec_cs += data_record.data[i];
+		p += 2;
+	}
+	data_record.rec_cs += data_record.data_cs;
+
+	return &data_record;
+}
+
+static int flash_record(struct cyttsp *ts, const struct fw_record *record)
+{
+	int len = fw_rec_size;
+	int blk_len, rc;
+	u8 *rec = (u8 *)record;
+	u8 data[BLK_SIZE + 1];
+	u8 blk_offset;
+
+	for (blk_offset = 0; len; len -= blk_len) {
+		data[0] = blk_offset;
+		blk_len = len > BLK_SIZE ? BLK_SIZE : len;
+		memcpy(data + 1, rec, blk_len);
+		rec += blk_len;
+		rc = flash_block(ts, data, blk_len + 1);
+		if (rc < 0)
+			return rc;
+		blk_offset += blk_len;
+	}
+	return 0;
+}
+
+static int flash_data_rec(struct cyttsp *ts, u8 *buf)
+{
+	struct fw_record *rec;
+	int rc, tries;
+
+	if (!buf)
+		return -EINVAL;
+
+	rc = check_record(ts, buf);
+
+	if (rc < 0) {
+		pr_debug("%s: record ignored %s", __func__, buf);
+		return 0;
+	}
+
+	rec = prepare_record(buf);
+	if (IS_ERR_OR_NULL(rec))
+		return PTR_ERR(rec);
+
+	rc = flash_record(ts, rec);
+	if (rc < 0)
+		return rc;
+
+	tries = 0;
+	do {
+		if (rec_cnt%2)
+			msleep(20);
+		cyttsp_putbl(ts, 4, true, false, false);
+	} while (g_bl_data.bl_status != 0x10 &&
+		g_bl_data.bl_status != 0x11 &&
+		tries++ < 100);
+	rec_cnt++;
+	return rc;
+}
+
+static int cyttspfw_flash_firmware(struct cyttsp *ts, const u8 *data,
+					int data_len)
+{
+	u8 *buf;
+	int i, j;
+	int rc, tries = 0;
+
+	/* initiate bootload: this will erase all the existing data */
+	rc = flash_command(ts, &initiate_rec);
+	if (rc < 0)
+		return rc;
+
+	do {
+		msleep(100);
+		cyttsp_putbl(ts, 4, true, false, false);
+	} while (g_bl_data.bl_status != 0x10 &&
+		g_bl_data.bl_status != 0x11 &&
+		tries++ < 100);
+
+	buf = kzalloc(REC_LINE_SIZE + 1, GFP_KERNEL);
+	if (!buf) {
+		pr_err("%s: no memory\n", __func__);
+		return -ENOMEM;
+	}
+
+	rec_cnt = 0;
+	/* flash data records */
+	for (i = 0, j = 0; i < data_len; i++, j++) {
+		if ((data[i] == REC_START_CHR) && j) {
+			buf[j] = 0;
+			rc = flash_data_rec(ts, buf);
+			if (rc < 0)
+				return rc;
+			j = 0;
+		}
+		buf[j] = data[i];
+	}
+
+	/* flash last data record */
+	if (j) {
+		buf[j] = 0;
+		rc = flash_data_rec(ts, buf);
+		if (rc < 0)
+			return rc;
+	}
+
+	kfree(buf);
+
+	/* termiate bootload */
+	tries = 0;
+	rc = flash_command(ts, &terminate_rec);
+	do {
+		msleep(100);
+		cyttsp_putbl(ts, 4, true, false, false);
+	} while (g_bl_data.bl_status != 0x10 &&
+		g_bl_data.bl_status != 0x11 &&
+		tries++ < 100);
+
+	return rc;
+}
+
+static int get_hex_fw_ver(u8 *p, u8 *ttspver_hi, u8 *ttspver_lo,
+			u8 *appid_hi, u8 *appid_lo, u8 *appver_hi,
+			u8 *appver_lo, u8 *cid_0, u8 *cid_1, u8 *cid_2)
+{
+	int rc;
+
+	p = p + ID_INFO_OFFSET_IN_REC;
+	rc = str2uc(p, ttspver_hi);
+	if (rc < 0)
+		return rc;
+	p += 2;
+	rc = str2uc(p, ttspver_lo);
+	if (rc < 0)
+		return rc;
+	p += 2;
+	rc = str2uc(p, appid_hi);
+	if (rc < 0)
+		return rc;
+	p += 2;
+	rc = str2uc(p, appid_lo);
+	if (rc < 0)
+		return rc;
+	p += 2;
+	rc = str2uc(p, appver_hi);
+	if (rc < 0)
+		return rc;
+	p += 2;
+	rc = str2uc(p, appver_lo);
+	if (rc < 0)
+		return rc;
+	p += 2;
+	rc = str2uc(p, cid_0);
+	if (rc < 0)
+		return rc;
+	p += 2;
+	rc = str2uc(p, cid_1);
+	if (rc < 0)
+		return rc;
+	p += 2;
+	rc = str2uc(p, cid_2);
+	if (rc < 0)
+		return rc;
+
+	return 0;
+}
+
+static void cyttspfw_flash_start(struct cyttsp *ts, const u8 *data,
+				int data_len, u8 *buf, bool force)
+{
+	int rc;
+	u8 ttspver_hi = 0, ttspver_lo = 0, fw_upgrade = 0;
+	u8 appid_hi = 0, appid_lo = 0;
+	u8 appver_hi = 0, appver_lo = 0;
+	u8 cid_0 = 0, cid_1 = 0, cid_2 = 0;
+	char *p = buf;
+
+	/* get hex firmware version */
+	rc = get_hex_fw_ver(p, &ttspver_hi, &ttspver_lo,
+		&appid_hi, &appid_lo, &appver_hi,
+		&appver_lo, &cid_0, &cid_1, &cid_2);
+
+	if (rc < 0) {
+		pr_err("%s: unable to get hex firmware version\n", __func__);
+		return;
+	}
+
+	/* disable interrupts before flashing */
+	if (ts->client->irq == 0)
+		del_timer(&ts->timer);
+	else
+		disable_irq(ts->client->irq);
+
+	/* enter bootloader idle mode */
+	rc = cyttsp_soft_reset(ts);
+
+	if (rc < 0) {
+		pr_err("%s: try entering into idle mode"
+				" second time\n", __func__);
+		msleep(1000);
+		rc = cyttsp_soft_reset(ts);
+	}
+
+	if (rc < 0) {
+		pr_err("%s: try again later\n", __func__);
+		return;
+	}
+
+
+	pr_info("Current firmware: %d.%d.%d", g_bl_data.appid_lo,
+				g_bl_data.appver_hi, g_bl_data.appver_lo);
+	pr_info("New firmware: %d.%d.%d", appid_lo, appver_hi, appver_lo);
+
+	if (force)
+		fw_upgrade = 1;
+	else if (!(g_bl_data.bl_status & BL_CHECKSUM_MASK) &&
+			(appid_lo == ts->platform_data->correct_fw_ver))
+		fw_upgrade = 1;
+	else if ((appid_hi == g_bl_data.appid_hi) &&
+			(appid_lo == g_bl_data.appid_lo))
+			if (appver_hi > g_bl_data.appver_hi)
+				fw_upgrade = 1;
+			else if ((appver_hi == g_bl_data.appver_hi) &&
+					 (appver_lo > g_bl_data.appver_lo))
+				fw_upgrade = 1;
+			else {
+				fw_upgrade = 0;
+				pr_info("%s: Firmware version "
+				"lesser/equal to existing firmware, "
+				"upgrade not needed\n", __func__);
+			}
+	else if (appid_lo == ts->platform_data->correct_fw_ver)
+		fw_upgrade = 1;
+	else {
+		fw_upgrade = 0;
+		pr_info("%s: Firmware versions do not match, "
+					"cannot upgrade\n", __func__);
+	}
+
+	if (fw_upgrade) {
+		pr_info("%s: Starting firmware upgrade\n", __func__);
+		rc = cyttspfw_flash_firmware(ts, data, data_len);
+		if (rc < 0)
+			pr_err("%s: firmware upgrade failed\n", __func__);
+		else
+			pr_info("%s: firmware upgrade success\n", __func__);
+	}
+
+	/* enter bootloader idle mode */
+	cyttsp_soft_reset(ts);
+	/* exit bootloader mode */
+	cyttsp_exit_bl_mode(ts);
+	msleep(100);
+	/* set sysinfo details */
+	cyttsp_set_sysinfo_mode(ts);
+	/* enter application mode */
+	cyttsp_set_opmode(ts);
+
+	/* enable interrupts */
+	if (ts->client->irq == 0)
+		mod_timer(&ts->timer, jiffies + TOUCHSCREEN_TIMEOUT);
+	else
+		enable_irq(ts->client->irq);
+}
+
+static void cyttspfw_upgrade_start(struct cyttsp *ts, const u8 *data,
+					int data_len, bool force)
+{
+	int i, j;
+	u8 *buf;
+
+	buf = kzalloc(REC_LINE_SIZE + 1, GFP_KERNEL);
+	if (!buf) {
+		pr_err("%s: no memory\n", __func__);
+		return;
+	}
+
+	for (i = 0, j = 0; i < data_len; i++, j++) {
+		if ((data[i] == REC_START_CHR) && j) {
+			buf[j] = 0;
+			j = 0;
+			if (!strncmp(buf, ID_INFO_REC,
+				strnlen(ID_INFO_REC, ID_INFO_REC_LEN))) {
+				cyttspfw_flash_start(ts, data, data_len,
+							buf, force);
+				break;
+			}
+		}
+		buf[j] = data[i];
+	}
+
+	/* check in the last record of firmware */
+	if (j) {
+		buf[j] = 0;
+		if (!strncmp(buf, ID_INFO_REC,
+			strnlen(ID_INFO_REC, ID_INFO_REC_LEN))) {
+			cyttspfw_flash_start(ts, data, data_len,
+						buf, force);
+		}
+	}
+
+	kfree(buf);
+}
+
+static void cyttspfw_upgrade(struct device *dev, bool force)
+{
+	struct cyttsp *ts = dev_get_drvdata(dev);
+	const struct firmware *cyttsp_fw;
+	int retval = 0;
+
+	if (ts->is_suspended == true) {
+		pr_err("%s: in suspend state, resume it\n", __func__);
+		retval = cyttsp_resume(dev);
+		if (retval < 0) {
+			pr_err("%s: unable to resume\n", __func__);
+			return;
+		}
+	}
+
+	retval = request_firmware(&cyttsp_fw, ts->fw_fname, dev);
+	if (retval < 0) {
+		pr_err("%s: %s request failed(%d)\n", __func__,
+						ts->fw_fname, retval);
+	} else {
+		/* check and start upgrade */
+		cyttspfw_upgrade_start(ts, cyttsp_fw->data,
+				cyttsp_fw->size, force);
+		release_firmware(cyttsp_fw);
+	}
+}
+
+static ssize_t cyttsp_update_fw_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct cyttsp *ts = dev_get_drvdata(dev);
+	return snprintf(buf, 2, "%d\n", ts->cyttsp_fwloader_mode);
+}
+
+static ssize_t cyttsp_force_update_fw_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t size)
+{
+	struct cyttsp *ts = dev_get_drvdata(dev);
+	unsigned long val;
+	int rc;
+
+	if (size > 2)
+		return -EINVAL;
+
+	rc = strict_strtoul(buf, 10, &val);
+	if (rc != 0)
+		return rc;
+
+	mutex_lock(&ts->mutex);
+	if (!ts->cyttsp_fwloader_mode  && val) {
+		ts->cyttsp_fwloader_mode = 1;
+		cyttspfw_upgrade(dev, true);
+		ts->cyttsp_fwloader_mode = 0;
+	}
+	mutex_unlock(&ts->mutex);
+	return size;
+}
+
+static DEVICE_ATTR(cyttsp_force_update_fw, 0664, cyttsp_update_fw_show,
+					cyttsp_force_update_fw_store);
+
+static ssize_t cyttsp_update_fw_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t size)
+{
+	struct cyttsp *ts = dev_get_drvdata(dev);
+	unsigned long val;
+	int rc;
+
+	if (size > 2)
+		return -EINVAL;
+
+	rc = strict_strtoul(buf, 10, &val);
+	if (rc != 0)
+		return rc;
+
+	mutex_lock(&ts->mutex);
+	if (!ts->cyttsp_fwloader_mode  && val) {
+		ts->cyttsp_fwloader_mode = 1;
+		cyttspfw_upgrade(dev, false);
+		ts->cyttsp_fwloader_mode = 0;
+	}
+	mutex_unlock(&ts->mutex);
+
+	return size;
+}
+
+static DEVICE_ATTR(cyttsp_update_fw, 0664, cyttsp_update_fw_show,
+					cyttsp_update_fw_store);
+
+static ssize_t cyttsp_fw_name_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct cyttsp *ts = dev_get_drvdata(dev);
+	return snprintf(buf, FW_FNAME_LEN - 1, "%s\n", ts->fw_fname);
+}
+
+static ssize_t cyttsp_fw_name_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t size)
+{
+	struct cyttsp *ts = dev_get_drvdata(dev);
+
+	if (size > FW_FNAME_LEN - 1)
+		return -EINVAL;
+
+	strlcpy(ts->fw_fname, buf, size);
+	if (ts->fw_fname[size-1] == '\n')
+		ts->fw_fname[size-1] = 0;
+
+	return size;
+}
+
+static DEVICE_ATTR(cyttsp_fw_name, 0664, cyttsp_fw_name_show,
+					cyttsp_fw_name_store);
+
+static void cyttsp_xy_handler(struct cyttsp *ts)
+{
+	u8 id, tilt, rev_x, rev_y;
+	u8 i, loc;
+	u8 prv_tch;		/* number of previous touches */
+	u8 cur_tch;	/* number of current touches */
+	u16 tmp_trk[CY_NUM_MT_TCH_ID];
+	u16 snd_trk[CY_NUM_MT_TCH_ID];
+	u16 cur_trk[CY_NUM_TRK_ID];
+	u16 cur_st_tch[CY_NUM_ST_TCH_ID];
+	u16 cur_mt_tch[CY_NUM_MT_TCH_ID];
+	/* if NOT CY_USE_TRACKING_ID then
+	 * only uses CY_NUM_MT_TCH_ID positions */
+	u16 cur_mt_pos[CY_NUM_TRK_ID][2];
+	/* if NOT CY_USE_TRACKING_ID then
+	 * only uses CY_NUM_MT_TCH_ID positions */
+	u8 cur_mt_z[CY_NUM_TRK_ID];
+	u8 curr_tool_width;
+	u16 st_x1, st_y1;
+	u8 st_z1;
+	u16 st_x2, st_y2;
+	u8 st_z2;
+	s32 retval;
+	int val;
+
+	cyttsp_xdebug("TTSP handler start 1:\n");
+
+	/* get event data from CYTTSP device */
+	i = CY_NUM_RETRY;
+	do {
+		retval = i2c_smbus_read_i2c_block_data(ts->client,
+			CY_REG_BASE,
+			sizeof(struct cyttsp_gen3_xydata_t), (u8 *)&g_xy_data);
+	} while ((retval < CY_OK) && --i);
+
+	if (retval < CY_OK) {
+		/* return immediately on
+		 * failure to read device on the i2c bus */
+		goto exit_xy_handler;
+	}
+
+	cyttsp_xdebug("TTSP handler start 2:\n");
+
+	/* compare own irq counter with the device irq counter */
+	if (ts->client->irq) {
+		u8 host_reg;
+		u8 cur_cnt;
+		if (ts->platform_data->use_hndshk) {
+
+			host_reg = g_xy_data.hst_mode & CY_HNDSHK_BIT ?
+				g_xy_data.hst_mode & ~CY_HNDSHK_BIT :
+				g_xy_data.hst_mode | CY_HNDSHK_BIT;
+			retval = i2c_smbus_write_i2c_block_data(ts->client,
+				CY_REG_BASE, sizeof(host_reg), &host_reg);
+		}
+		cur_cnt = g_xy_data.tt_undef[CY_IRQ_CNT_REG];
+		irq_cnt_total++;
+		irq_cnt++;
+		if (irq_cnt != cur_cnt) {
+			irq_err_cnt++;
+			cyttsp_debug("i_c_ER: dv=%d fw=%d hm=%02X t=%lu te=%lu\n", \
+				irq_cnt, \
+				cur_cnt, g_xy_data.hst_mode, \
+				(unsigned long)irq_cnt_total, \
+				(unsigned long)irq_err_cnt);
+		} else {
+			cyttsp_debug("i_c_ok: dv=%d fw=%d hm=%02X t=%lu te=%lu\n", \
+				irq_cnt, \
+				cur_cnt, g_xy_data.hst_mode, \
+				(unsigned long)irq_cnt_total, \
+				(unsigned long)irq_err_cnt);
+		}
+		irq_cnt = cur_cnt;
+	}
+
+	/* Get the current num touches and return if there are no touches */
+	if ((GET_BOOTLOADERMODE(g_xy_data.tt_mode) == 1) ||
+		(GET_HSTMODE(g_xy_data.hst_mode) != CY_OK)) {
+		u8 host_reg, tries;
+		/* the TTSP device has suffered spurious reset or mode switch */
+		cyttsp_debug( \
+			"Spurious err opmode (tt_mode=%02X hst_mode=%02X)\n", \
+			g_xy_data.tt_mode, g_xy_data.hst_mode);
+		cyttsp_debug("Reset TTSP Device; Terminating active tracks\n");
+		/* terminate all active tracks */
+		cur_tch = CY_NTCH;
+		/* reset TTSP part and take it back out of Bootloader mode */
+		/* reset TTSP Device back to bootloader mode */
+		host_reg = CY_SOFT_RESET_MODE;
+		retval = i2c_smbus_write_i2c_block_data(ts->client, CY_REG_BASE,
+			sizeof(host_reg), &host_reg);
+		/* wait for TTSP Device to complete reset back to bootloader */
+		tries = 0;
+		do {
+			usleep_range(1000, 1000);
+			cyttsp_putbl(ts, 1, false, false, false);
+		} while (g_bl_data.bl_status != 0x10 &&
+			g_bl_data.bl_status != 0x11 &&
+			tries++ < 100);
+		retval = cyttsp_putbl(ts, 1, true, true, true);
+		/* switch back to operational mode */
+		/* take TTSP device out of bootloader mode;
+		 * switch back to TrueTouch operational mode */
+		if (!(retval < CY_OK)) {
+			int tries;
+			retval = i2c_smbus_write_i2c_block_data(ts->client,
+				CY_REG_BASE,
+				sizeof(bl_cmd), bl_cmd);
+			/* wait for TTSP Device to complete
+			 * switch to Operational mode */
+			tries = 0;
+			do {
+				msleep(100);
+				cyttsp_putbl(ts, 2, false, false, false);
+			} while (GET_BOOTLOADERMODE(g_bl_data.bl_status) &&
+				tries++ < 100);
+			cyttsp_putbl(ts, 2, true, false, false);
+		}
+		goto exit_xy_handler;
+	} else {
+		cur_tch = GET_NUM_TOUCHES(g_xy_data.tt_stat);
+		if (IS_LARGE_AREA(g_xy_data.tt_stat)) {
+			/* terminate all active tracks */
+			cur_tch = CY_NTCH;
+			cyttsp_debug("Large obj detect (tt_stat=0x%02X). Terminate act trks\n", \
+			    g_xy_data.tt_stat);
+		} else if (cur_tch > CY_NUM_MT_TCH_ID) {
+			/* if the number of fingers on the touch surface
+			 * is more than the maximum then
+			 * there will be no new track information
+			 * even for the original touches.
+			 * Therefore, terminate all active tracks.
+			 */
+			cur_tch = CY_NTCH;
+			cyttsp_debug("Num touch err (tt_stat=0x%02X). Terminate act trks\n", \
+			    g_xy_data.tt_stat);
+		}
+	}
+
+	/* set tool size */
+	curr_tool_width = CY_SMALL_TOOL_WIDTH;
+
+	/* translate Gen2 interface data into comparable Gen3 data */
+	if (ts->platform_data->gen == CY_GEN2) {
+		struct cyttsp_gen2_xydata_t *pxy_gen2_data;
+		pxy_gen2_data = (struct cyttsp_gen2_xydata_t *)(&g_xy_data);
+
+		/* use test data? */
+		cyttsp_testdat(&g_xy_data, &tt_gen2_testray, \
+			sizeof(struct cyttsp_gen3_xydata_t));
+
+		if (ts->platform_data->disable_ghost_det &&
+				(cur_tch == CY_GEN2_GHOST))
+			cur_tch = CY_GEN2_2TOUCH;
+
+		if (pxy_gen2_data->evnt_idx == CY_GEN2_NOTOUCH) {
+			cur_tch = 0;
+		} else if (cur_tch == CY_GEN2_GHOST) {
+			cur_tch = 0;
+		} else if (cur_tch == CY_GEN2_2TOUCH) {
+			/* stuff artificial track ID1 and ID2 */
+			g_xy_data.touch12_id = 0x12;
+			g_xy_data.z1 = CY_MAXZ;
+			g_xy_data.z2 = CY_MAXZ;
+			cur_tch--;			/* 2 touches */
+		} else if (cur_tch == CY_GEN2_1TOUCH) {
+			/* stuff artificial track ID1 and ID2 */
+			g_xy_data.touch12_id = 0x12;
+			g_xy_data.z1 = CY_MAXZ;
+			g_xy_data.z2 = CY_NTCH;
+			if (pxy_gen2_data->evnt_idx == CY_GEN2_TOUCH2) {
+				/* push touch 2 data into touch1
+				 * (first finger up; second finger down) */
+				/* stuff artificial track ID1 for touch2 info */
+				g_xy_data.touch12_id = 0x20;
+				/* stuff touch 1 with touch 2 coordinate data */
+				g_xy_data.x1 = g_xy_data.x2;
+				g_xy_data.y1 = g_xy_data.y2;
+			}
+		} else {
+			cur_tch = 0;
+		}
+	} else {
+		/* use test data? */
+		cyttsp_testdat(&g_xy_data, &tt_gen3_testray, \
+			sizeof(struct cyttsp_gen3_xydata_t));
+	}
+
+
+
+	/* clear current active track ID array and count previous touches */
+	for (id = 0, prv_tch = CY_NTCH;
+		id < CY_NUM_TRK_ID; id++) {
+		cur_trk[id] = CY_NTCH;
+		prv_tch += ts->act_trk[id];
+	}
+
+	/* send no events if no previous touches and no new touches */
+	if ((prv_tch == CY_NTCH) &&
+		((cur_tch == CY_NTCH) ||
+		(cur_tch > CY_NUM_MT_TCH_ID))) {
+		goto exit_xy_handler;
+	}
+
+	cyttsp_debug("prev=%d  curr=%d\n", prv_tch, cur_tch);
+
+	for (id = 0; id < CY_NUM_ST_TCH_ID; id++) {
+		/* clear current single touches array */
+		cur_st_tch[id] = CY_IGNR_TCH;
+	}
+
+	/* clear single touch positions */
+	st_x1 = CY_NTCH;
+	st_y1 = CY_NTCH;
+	st_z1 = CY_NTCH;
+	st_x2 = CY_NTCH;
+	st_y2 = CY_NTCH;
+	st_z2 = CY_NTCH;
+
+	for (id = 0; id < CY_NUM_MT_TCH_ID; id++) {
+		/* clear current multi-touches array and
+		 * multi-touch positions/z */
+		cur_mt_tch[id] = CY_IGNR_TCH;
+	}
+
+	if (ts->platform_data->use_trk_id) {
+		for (id = 0; id < CY_NUM_MT_TCH_ID; id++) {
+			cur_mt_pos[id][CY_XPOS] = 0;
+			cur_mt_pos[id][CY_YPOS] = 0;
+			cur_mt_z[id] = 0;
+		}
+	} else {
+		for (id = 0; id < CY_NUM_TRK_ID; id++) {
+			cur_mt_pos[id][CY_XPOS] = 0;
+			cur_mt_pos[id][CY_YPOS] = 0;
+			cur_mt_z[id] = 0;
+		}
+	}
+
+	/* Determine if display is tilted */
+	if (FLIP_DATA(ts->platform_data->flags))
+		tilt = true;
+	else
+		tilt = false;
+
+	/* Check for switch in origin */
+	if (REVERSE_X(ts->platform_data->flags))
+		rev_x = true;
+	else
+		rev_x = false;
+
+	if (REVERSE_Y(ts->platform_data->flags))
+		rev_y = true;
+	else
+		rev_y = false;
+
+	if (cur_tch) {
+		struct cyttsp_gen2_xydata_t *pxy_gen2_data;
+		struct cyttsp_gen3_xydata_t *pxy_gen3_data;
+		switch (ts->platform_data->gen) {
+		case CY_GEN2: {
+			pxy_gen2_data =
+				(struct cyttsp_gen2_xydata_t *)(&g_xy_data);
+			cyttsp_xdebug("TTSP Gen2 report:\n");
+			cyttsp_xdebug("%02X %02X %02X\n", \
+				pxy_gen2_data->hst_mode, \
+				pxy_gen2_data->tt_mode, \
+				pxy_gen2_data->tt_stat);
+			cyttsp_xdebug("%04X %04X %02X  %02X\n", \
+				pxy_gen2_data->x1, \
+				pxy_gen2_data->y1, \
+				pxy_gen2_data->z1, \
+				pxy_gen2_data->evnt_idx);
+			cyttsp_xdebug("%04X %04X %02X\n", \
+				pxy_gen2_data->x2, \
+				pxy_gen2_data->y2, \
+				pxy_gen2_data->tt_undef1);
+			cyttsp_xdebug("%02X %02X %02X\n", \
+				pxy_gen2_data->gest_cnt, \
+				pxy_gen2_data->gest_id, \
+				pxy_gen2_data->gest_set);
+			break;
+		}
+		case CY_GEN3:
+		default: {
+			pxy_gen3_data =
+				(struct cyttsp_gen3_xydata_t *)(&g_xy_data);
+			cyttsp_xdebug("TTSP Gen3 report:\n");
+			cyttsp_xdebug("%02X %02X %02X\n", \
+				pxy_gen3_data->hst_mode,
+				pxy_gen3_data->tt_mode,
+				pxy_gen3_data->tt_stat);
+			cyttsp_xdebug("%04X %04X %02X  %02X", \
+				pxy_gen3_data->x1,
+				pxy_gen3_data->y1,
+				pxy_gen3_data->z1, \
+				pxy_gen3_data->touch12_id);
+			cyttsp_xdebug("%04X %04X %02X\n", \
+				pxy_gen3_data->x2, \
+				pxy_gen3_data->y2, \
+				pxy_gen3_data->z2);
+			cyttsp_xdebug("%02X %02X %02X\n", \
+				pxy_gen3_data->gest_cnt, \
+				pxy_gen3_data->gest_id, \
+				pxy_gen3_data->gest_set);
+			cyttsp_xdebug("%04X %04X %02X  %02X\n", \
+				pxy_gen3_data->x3, \
+				pxy_gen3_data->y3, \
+				pxy_gen3_data->z3, \
+				pxy_gen3_data->touch34_id);
+			cyttsp_xdebug("%04X %04X %02X\n", \
+				pxy_gen3_data->x4, \
+				pxy_gen3_data->y4, \
+				pxy_gen3_data->z4);
+			break;
+		}
+		}
+	}
+
+	/* process the touches */
+	switch (cur_tch) {
+	case 4: {
+		g_xy_data.x4 = be16_to_cpu(g_xy_data.x4);
+		g_xy_data.y4 = be16_to_cpu(g_xy_data.y4);
+		if (tilt)
+			FLIP_XY(g_xy_data.x4, g_xy_data.y4);
+
+		if (rev_x) {
+			val = INVERT_X(g_xy_data.x4,
+					ts->platform_data->panel_maxx);
+			if (val >= 0)
+				g_xy_data.x4 = val;
+			else
+				pr_debug("X value is negative. Please configure"
+					" maxx in platform data structure\n");
+		}
+		if (rev_y) {
+			val = INVERT_X(g_xy_data.y4,
+					ts->platform_data->panel_maxy);
+			if (val >= 0)
+				g_xy_data.y4 = val;
+			else
+				pr_debug("Y value is negative. Please configure"
+					" maxy in platform data structure\n");
+
+		}
+		id = GET_TOUCH4_ID(g_xy_data.touch34_id);
+		if (ts->platform_data->use_trk_id) {
+			cur_mt_pos[CY_MT_TCH4_IDX][CY_XPOS] =
+				g_xy_data.x4;
+			cur_mt_pos[CY_MT_TCH4_IDX][CY_YPOS] =
+				g_xy_data.y4;
+			cur_mt_z[CY_MT_TCH4_IDX] = g_xy_data.z4;
+		} else {
+			cur_mt_pos[id][CY_XPOS] = g_xy_data.x4;
+			cur_mt_pos[id][CY_YPOS] = g_xy_data.y4;
+			cur_mt_z[id] = g_xy_data.z4;
+		}
+		cur_mt_tch[CY_MT_TCH4_IDX] = id;
+		cur_trk[id] = CY_TCH;
+		if (ts->prv_st_tch[CY_ST_FNGR1_IDX] <
+			CY_NUM_TRK_ID) {
+			if (ts->prv_st_tch[CY_ST_FNGR1_IDX] == id) {
+				st_x1 = g_xy_data.x4;
+				st_y1 = g_xy_data.y4;
+				st_z1 = g_xy_data.z4;
+				cur_st_tch[CY_ST_FNGR1_IDX] = id;
+			} else if (ts->prv_st_tch[CY_ST_FNGR2_IDX] == id) {
+				st_x2 = g_xy_data.x4;
+				st_y2 = g_xy_data.y4;
+				st_z2 = g_xy_data.z4;
+				cur_st_tch[CY_ST_FNGR2_IDX] = id;
+			}
+		}
+		cyttsp_xdebug("4th XYZ:% 3d,% 3d,% 3d  ID:% 2d\n\n", \
+			g_xy_data.x4, g_xy_data.y4, g_xy_data.z4, \
+			(g_xy_data.touch34_id & 0x0F));
+		/* do not break */
+	}
+	case 3: {
+		g_xy_data.x3 = be16_to_cpu(g_xy_data.x3);
+		g_xy_data.y3 = be16_to_cpu(g_xy_data.y3);
+		if (tilt)
+			FLIP_XY(g_xy_data.x3, g_xy_data.y3);
+
+		if (rev_x) {
+			val = INVERT_X(g_xy_data.x3,
+					ts->platform_data->panel_maxx);
+			if (val >= 0)
+				g_xy_data.x3 = val;
+			else
+				pr_debug("X value is negative. Please configure"
+					" maxx in platform data structure\n");
+
+		}
+		if (rev_y) {
+			val = INVERT_X(g_xy_data.y3,
+					ts->platform_data->panel_maxy);
+			if (val >= 0)
+				g_xy_data.y3 = val;
+			else
+				pr_debug("Y value is negative. Please configure"
+					" maxy in platform data structure\n");
+
+		}
+		id = GET_TOUCH3_ID(g_xy_data.touch34_id);
+		if (ts->platform_data->use_trk_id) {
+			cur_mt_pos[CY_MT_TCH3_IDX][CY_XPOS] =
+				g_xy_data.x3;
+			cur_mt_pos[CY_MT_TCH3_IDX][CY_YPOS] =
+				g_xy_data.y3;
+			cur_mt_z[CY_MT_TCH3_IDX] = g_xy_data.z3;
+		} else {
+			cur_mt_pos[id][CY_XPOS] = g_xy_data.x3;
+			cur_mt_pos[id][CY_YPOS] = g_xy_data.y3;
+			cur_mt_z[id] = g_xy_data.z3;
+		}
+		cur_mt_tch[CY_MT_TCH3_IDX] = id;
+		cur_trk[id] = CY_TCH;
+		if (ts->prv_st_tch[CY_ST_FNGR1_IDX] <
+			CY_NUM_TRK_ID) {
+			if (ts->prv_st_tch[CY_ST_FNGR1_IDX] == id) {
+				st_x1 = g_xy_data.x3;
+				st_y1 = g_xy_data.y3;
+				st_z1 = g_xy_data.z3;
+				cur_st_tch[CY_ST_FNGR1_IDX] = id;
+			} else if (ts->prv_st_tch[CY_ST_FNGR2_IDX] == id) {
+				st_x2 = g_xy_data.x3;
+				st_y2 = g_xy_data.y3;
+				st_z2 = g_xy_data.z3;
+				cur_st_tch[CY_ST_FNGR2_IDX] = id;
+			}
+		}
+		cyttsp_xdebug("3rd XYZ:% 3d,% 3d,% 3d  ID:% 2d\n", \
+			g_xy_data.x3, g_xy_data.y3, g_xy_data.z3, \
+			((g_xy_data.touch34_id >> 4) & 0x0F));
+		/* do not break */
+	}
+	case 2: {
+		g_xy_data.x2 = be16_to_cpu(g_xy_data.x2);
+		g_xy_data.y2 = be16_to_cpu(g_xy_data.y2);
+		if (tilt)
+			FLIP_XY(g_xy_data.x2, g_xy_data.y2);
+
+		if (rev_x) {
+			val = INVERT_X(g_xy_data.x2,
+					ts->platform_data->panel_maxx);
+			if (val >= 0)
+				g_xy_data.x2 = val;
+			else
+				pr_debug("X value is negative. Please configure"
+					" maxx in platform data structure\n");
+		}
+		if (rev_y) {
+			val = INVERT_X(g_xy_data.y2,
+					ts->platform_data->panel_maxy);
+			if (val >= 0)
+				g_xy_data.y2 = val;
+			else
+				pr_debug("Y value is negative. Please configure"
+					" maxy in platform data structure\n");
+		}
+		id = GET_TOUCH2_ID(g_xy_data.touch12_id);
+		if (ts->platform_data->use_trk_id) {
+			cur_mt_pos[CY_MT_TCH2_IDX][CY_XPOS] =
+				g_xy_data.x2;
+			cur_mt_pos[CY_MT_TCH2_IDX][CY_YPOS] =
+				g_xy_data.y2;
+			cur_mt_z[CY_MT_TCH2_IDX] = g_xy_data.z2;
+		} else {
+			cur_mt_pos[id][CY_XPOS] = g_xy_data.x2;
+			cur_mt_pos[id][CY_YPOS] = g_xy_data.y2;
+			cur_mt_z[id] = g_xy_data.z2;
+		}
+		cur_mt_tch[CY_MT_TCH2_IDX] = id;
+		cur_trk[id] = CY_TCH;
+		if (ts->prv_st_tch[CY_ST_FNGR1_IDX] <
+			CY_NUM_TRK_ID) {
+			if (ts->prv_st_tch[CY_ST_FNGR1_IDX] == id) {
+				st_x1 = g_xy_data.x2;
+				st_y1 = g_xy_data.y2;
+				st_z1 = g_xy_data.z2;
+				cur_st_tch[CY_ST_FNGR1_IDX] = id;
+			} else if (ts->prv_st_tch[CY_ST_FNGR2_IDX] == id) {
+				st_x2 = g_xy_data.x2;
+				st_y2 = g_xy_data.y2;
+				st_z2 = g_xy_data.z2;
+				cur_st_tch[CY_ST_FNGR2_IDX] = id;
+			}
+		}
+		cyttsp_xdebug("2nd XYZ:% 3d,% 3d,% 3d  ID:% 2d\n", \
+			g_xy_data.x2, g_xy_data.y2, g_xy_data.z2, \
+			(g_xy_data.touch12_id & 0x0F));
+		/* do not break */
+	}
+	case 1:	{
+		g_xy_data.x1 = be16_to_cpu(g_xy_data.x1);
+		g_xy_data.y1 = be16_to_cpu(g_xy_data.y1);
+		if (tilt)
+			FLIP_XY(g_xy_data.x1, g_xy_data.y1);
+
+		if (rev_x) {
+			val = INVERT_X(g_xy_data.x1,
+					ts->platform_data->panel_maxx);
+			if (val >= 0)
+				g_xy_data.x1 = val;
+			else
+				pr_debug("X value is negative. Please configure"
+					" maxx in platform data structure\n");
+		}
+		if (rev_y) {
+			val = INVERT_X(g_xy_data.y1,
+					ts->platform_data->panel_maxy);
+			if (val >= 0)
+				g_xy_data.y1 = val;
+			else
+				pr_debug("Y value is negative. Please configure"
+					" maxy in platform data structure");
+		}
+		id = GET_TOUCH1_ID(g_xy_data.touch12_id);
+		if (ts->platform_data->use_trk_id) {
+			cur_mt_pos[CY_MT_TCH1_IDX][CY_XPOS] =
+				g_xy_data.x1;
+			cur_mt_pos[CY_MT_TCH1_IDX][CY_YPOS] =
+				g_xy_data.y1;
+			cur_mt_z[CY_MT_TCH1_IDX] = g_xy_data.z1;
+		} else {
+			cur_mt_pos[id][CY_XPOS] = g_xy_data.x1;
+			cur_mt_pos[id][CY_YPOS] = g_xy_data.y1;
+			cur_mt_z[id] = g_xy_data.z1;
+		}
+		cur_mt_tch[CY_MT_TCH1_IDX] = id;
+		cur_trk[id] = CY_TCH;
+		if (ts->prv_st_tch[CY_ST_FNGR1_IDX] <
+			CY_NUM_TRK_ID) {
+			if (ts->prv_st_tch[CY_ST_FNGR1_IDX] == id) {
+				st_x1 = g_xy_data.x1;
+				st_y1 = g_xy_data.y1;
+				st_z1 = g_xy_data.z1;
+				cur_st_tch[CY_ST_FNGR1_IDX] = id;
+			} else if (ts->prv_st_tch[CY_ST_FNGR2_IDX] == id) {
+				st_x2 = g_xy_data.x1;
+				st_y2 = g_xy_data.y1;
+				st_z2 = g_xy_data.z1;
+				cur_st_tch[CY_ST_FNGR2_IDX] = id;
+			}
+		}
+		cyttsp_xdebug("1st XYZ:% 3d,% 3d,% 3d  ID:% 2d\n", \
+			g_xy_data.x1, g_xy_data.y1, g_xy_data.z1, \
+			((g_xy_data.touch12_id >> 4) & 0x0F));
+		break;
+	}
+	case 0:
+	default:{
+		break;
+	}
+	}
+
+	/* handle Single Touch signals */
+	if (ts->platform_data->use_st) {
+		cyttsp_xdebug("ST STEP 0 - ST1 ID=%d  ST2 ID=%d\n", \
+			cur_st_tch[CY_ST_FNGR1_IDX], \
+			cur_st_tch[CY_ST_FNGR2_IDX]);
+		if (cur_st_tch[CY_ST_FNGR1_IDX] > CY_NUM_TRK_ID) {
+			/* reassign finger 1 and 2 positions to new tracks */
+			if (cur_tch > 0) {
+				/* reassign st finger1 */
+				if (ts->platform_data->use_trk_id) {
+					id = CY_MT_TCH1_IDX;
+					cur_st_tch[CY_ST_FNGR1_IDX] = cur_mt_tch[id];
+				} else {
+					id = GET_TOUCH1_ID(g_xy_data.touch12_id);
+					cur_st_tch[CY_ST_FNGR1_IDX] = id;
+				}
+				st_x1 = cur_mt_pos[id][CY_XPOS];
+				st_y1 = cur_mt_pos[id][CY_YPOS];
+				st_z1 = cur_mt_z[id];
+				cyttsp_xdebug("ST STEP 1 - ST1 ID=%3d\n", \
+					cur_st_tch[CY_ST_FNGR1_IDX]);
+				if ((cur_tch > 1) &&
+					(cur_st_tch[CY_ST_FNGR2_IDX] >
+					CY_NUM_TRK_ID)) {
+					/* reassign st finger2 */
+					if (cur_tch > 1) {
+						if (ts->platform_data->use_trk_id) {
+							id = CY_MT_TCH2_IDX;
+							cur_st_tch[CY_ST_FNGR2_IDX] = cur_mt_tch[id];
+						} else {
+							id = GET_TOUCH2_ID(g_xy_data.touch12_id);
+							cur_st_tch[CY_ST_FNGR2_IDX] = id;
+						}
+						st_x2 = cur_mt_pos[id][CY_XPOS];
+						st_y2 = cur_mt_pos[id][CY_YPOS];
+						st_z2 = cur_mt_z[id];
+						cyttsp_xdebug("ST STEP 2 - ST2 ID=%3d\n", \
+							cur_st_tch[CY_ST_FNGR2_IDX]);
+					}
+				}
+			}
+		} else if (cur_st_tch[CY_ST_FNGR2_IDX] > CY_NUM_TRK_ID) {
+			if (cur_tch > 1) {
+				/* reassign st finger2 */
+				if (ts->platform_data->use_trk_id) {
+					/* reassign st finger2 */
+					id = CY_MT_TCH2_IDX;
+					cur_st_tch[CY_ST_FNGR2_IDX] =
+						cur_mt_tch[id];
+				} else {
+					/* reassign st finger2 */
+					id = GET_TOUCH2_ID(g_xy_data.touch12_id);
+					cur_st_tch[CY_ST_FNGR2_IDX] = id;
+				}
+				st_x2 = cur_mt_pos[id][CY_XPOS];
+				st_y2 = cur_mt_pos[id][CY_YPOS];
+				st_z2 = cur_mt_z[id];
+				cyttsp_xdebug("ST STEP 3 - ST2 ID=%3d\n", \
+					cur_st_tch[CY_ST_FNGR2_IDX]);
+			}
+		}
+		/* if the 1st touch is missing and there is a 2nd touch,
+		 * then set the 1st touch to 2nd touch and terminate 2nd touch
+		 */
+		if ((cur_st_tch[CY_ST_FNGR1_IDX] > CY_NUM_TRK_ID) &&
+		    (cur_st_tch[CY_ST_FNGR2_IDX] < CY_NUM_TRK_ID)) {
+			st_x1 = st_x2;
+			st_y1 = st_y2;
+			st_z1 = st_z2;
+			cur_st_tch[CY_ST_FNGR1_IDX] =
+				cur_st_tch[CY_ST_FNGR2_IDX];
+			cur_st_tch[CY_ST_FNGR2_IDX] =
+				CY_IGNR_TCH;
+		}
+		/* if the 2nd touch ends up equal to the 1st touch,
+		 * then just report a single touch */
+		if (cur_st_tch[CY_ST_FNGR1_IDX] ==
+			cur_st_tch[CY_ST_FNGR2_IDX]) {
+			cur_st_tch[CY_ST_FNGR2_IDX] =
+				CY_IGNR_TCH;
+		}
+		/* set Single Touch current event signals */
+		if (cur_st_tch[CY_ST_FNGR1_IDX] < CY_NUM_TRK_ID) {
+			input_report_abs(ts->input,
+				ABS_X, st_x1);
+			input_report_abs(ts->input,
+				ABS_Y, st_y1);
+			input_report_abs(ts->input,
+				ABS_PRESSURE, st_z1);
+			input_report_key(ts->input,
+				BTN_TOUCH,
+				CY_TCH);
+			input_report_abs(ts->input,
+				ABS_TOOL_WIDTH,
+				curr_tool_width);
+			cyttsp_debug("ST->F1:%3d X:%3d Y:%3d Z:%3d\n", \
+				cur_st_tch[CY_ST_FNGR1_IDX], \
+				st_x1, st_y1, st_z1);
+			if (cur_st_tch[CY_ST_FNGR2_IDX] < CY_NUM_TRK_ID) {
+				input_report_key(ts->input, BTN_2, CY_TCH);
+				input_report_abs(ts->input, ABS_HAT0X, st_x2);
+				input_report_abs(ts->input, ABS_HAT0Y, st_y2);
+				cyttsp_debug("ST->F2:%3d X:%3d Y:%3d Z:%3d\n", \
+					cur_st_tch[CY_ST_FNGR2_IDX],
+					st_x2, st_y2, st_z2);
+			} else {
+				input_report_key(ts->input,
+					BTN_2,
+					CY_NTCH);
+			}
+		} else {
+			input_report_abs(ts->input, ABS_PRESSURE, CY_NTCH);
+			input_report_key(ts->input, BTN_TOUCH, CY_NTCH);
+			input_report_key(ts->input, BTN_2, CY_NTCH);
+		}
+		/* update platform data for the current single touch info */
+		ts->prv_st_tch[CY_ST_FNGR1_IDX] = cur_st_tch[CY_ST_FNGR1_IDX];
+		ts->prv_st_tch[CY_ST_FNGR2_IDX] = cur_st_tch[CY_ST_FNGR2_IDX];
+
+	}
+
+	/* handle Multi-touch signals */
+	if (ts->platform_data->use_mt) {
+		if (ts->platform_data->use_trk_id) {
+			/* terminate any previous touch where the track
+			 * is missing from the current event */
+			for (id = 0; id < CY_NUM_TRK_ID; id++) {
+				if ((ts->act_trk[id] != CY_NTCH) &&
+					(cur_trk[id] == CY_NTCH)) {
+					input_report_abs(ts->input,
+						ABS_MT_TRACKING_ID,
+						id);
+					input_report_abs(ts->input,
+						ABS_MT_TOUCH_MAJOR,
+						CY_NTCH);
+					input_report_abs(ts->input,
+						ABS_MT_WIDTH_MAJOR,
+						curr_tool_width);
+					input_report_abs(ts->input,
+						ABS_MT_POSITION_X,
+						ts->prv_mt_pos[id][CY_XPOS]);
+					input_report_abs(ts->input,
+						ABS_MT_POSITION_Y,
+						ts->prv_mt_pos[id][CY_YPOS]);
+					CY_MT_SYNC(ts->input);
+					ts->act_trk[id] = CY_NTCH;
+					ts->prv_mt_pos[id][CY_XPOS] = 0;
+					ts->prv_mt_pos[id][CY_YPOS] = 0;
+				}
+			}
+			/* set Multi-Touch current event signals */
+			for (id = 0; id < CY_NUM_MT_TCH_ID; id++) {
+				if (cur_mt_tch[id] < CY_NUM_TRK_ID) {
+					input_report_abs(ts->input,
+						ABS_MT_TRACKING_ID,
+						cur_mt_tch[id]);
+					input_report_abs(ts->input,
+						ABS_MT_TOUCH_MAJOR,
+						cur_mt_z[id]);
+					input_report_abs(ts->input,
+						ABS_MT_WIDTH_MAJOR,
+						curr_tool_width);
+					input_report_abs(ts->input,
+						ABS_MT_POSITION_X,
+						cur_mt_pos[id][CY_XPOS]);
+					input_report_abs(ts->input,
+						ABS_MT_POSITION_Y,
+						cur_mt_pos[id][CY_YPOS]);
+					CY_MT_SYNC(ts->input);
+					ts->act_trk[id] = CY_TCH;
+					ts->prv_mt_pos[id][CY_XPOS] =
+						cur_mt_pos[id][CY_XPOS];
+					ts->prv_mt_pos[id][CY_YPOS] =
+						cur_mt_pos[id][CY_YPOS];
+				}
+			}
+		} else {
+			/* set temporary track array elements to voids */
+			for (id = 0; id < CY_NUM_MT_TCH_ID; id++) {
+				tmp_trk[id] = CY_IGNR_TCH;
+				snd_trk[id] = CY_IGNR_TCH;
+			}
+
+			/* get what is currently active */
+			for (i = 0, id = 0;
+				id < CY_NUM_TRK_ID && i < CY_NUM_MT_TCH_ID;
+				id++) {
+				if (cur_trk[id] == CY_TCH) {
+					/* only incr counter if track found */
+					tmp_trk[i] = id;
+					i++;
+				}
+			}
+			cyttsp_xdebug("T1: t0=%d, t1=%d, t2=%d, t3=%d\n", \
+				tmp_trk[0], tmp_trk[1], tmp_trk[2], \
+				tmp_trk[3]);
+			cyttsp_xdebug("T1: p0=%d, p1=%d, p2=%d, p3=%d\n", \
+				ts->prv_mt_tch[0], ts->prv_mt_tch[1], \
+				ts->prv_mt_tch[2], ts->prv_mt_tch[3]);
+
+			/* pack in still active previous touches */
+			for (id = 0, prv_tch = 0;
+				id < CY_NUM_MT_TCH_ID; id++) {
+				if (tmp_trk[id] < CY_NUM_TRK_ID) {
+					if (cyttsp_inlist(ts->prv_mt_tch,
+						tmp_trk[id], &loc,
+						CY_NUM_MT_TCH_ID)) {
+						loc &= CY_NUM_MT_TCH_ID - 1;
+						snd_trk[loc] = tmp_trk[id];
+						prv_tch++;
+						cyttsp_xdebug("inlist s[%d]=%d t[%d]=%d l=%d p=%d\n", \
+							loc, snd_trk[loc], \
+							id, tmp_trk[id], \
+							loc, prv_tch);
+					} else {
+						cyttsp_xdebug("not inlist s[%d]=%d t[%d]=%d l=%d \n", \
+							id, snd_trk[id], \
+							id, tmp_trk[id], \
+							loc);
+					}
+				}
+			}
+			cyttsp_xdebug("S1: s0=%d, s1=%d, s2=%d, s3=%d p=%d\n", \
+				snd_trk[0], snd_trk[1], snd_trk[2], \
+				snd_trk[3], prv_tch);
+
+			/* pack in new touches */
+			for (id = 0; id < CY_NUM_MT_TCH_ID; id++) {
+				if (tmp_trk[id] < CY_NUM_TRK_ID) {
+					if (!cyttsp_inlist(snd_trk, tmp_trk[id], &loc, CY_NUM_MT_TCH_ID)) {
+						cyttsp_xdebug("not inlist t[%d]=%d l=%d\n", \
+							id, tmp_trk[id], loc);
+						if (cyttsp_next_avail_inlist(snd_trk, &loc, CY_NUM_MT_TCH_ID)) {
+							loc &= CY_NUM_MT_TCH_ID - 1;
+							snd_trk[loc] = tmp_trk[id];
+							cyttsp_xdebug("put inlist s[%d]=%d t[%d]=%d\n",
+								loc, snd_trk[loc], id, tmp_trk[id]);
+						}
+					} else {
+						cyttsp_xdebug("is in list s[%d]=%d t[%d]=%d loc=%d\n", \
+							id, snd_trk[id], id, tmp_trk[id], loc);
+					}
+				}
+			}
+			cyttsp_xdebug("S2: s0=%d, s1=%d, s2=%d, s3=%d\n", \
+				snd_trk[0], snd_trk[1],
+				snd_trk[2], snd_trk[3]);
+
+			/* sync motion event signals for each current touch */
+			for (id = 0; id < CY_NUM_MT_TCH_ID; id++) {
+				/* z will either be 0 (NOTOUCH) or
+				 * some pressure (TOUCH) */
+				cyttsp_xdebug("MT0 prev[%d]=%d temp[%d]=%d send[%d]=%d\n", \
+					id, ts->prv_mt_tch[id], \
+					id, tmp_trk[id], \
+					id, snd_trk[id]);
+				if (snd_trk[id] < CY_NUM_TRK_ID) {
+					input_report_abs(ts->input,
+						ABS_MT_TOUCH_MAJOR,
+						cur_mt_z[snd_trk[id]]);
+					input_report_abs(ts->input,
+						ABS_MT_WIDTH_MAJOR,
+						curr_tool_width);
+					input_report_abs(ts->input,
+						ABS_MT_POSITION_X,
+						cur_mt_pos[snd_trk[id]][CY_XPOS]);
+					input_report_abs(ts->input,
+						ABS_MT_POSITION_Y,
+						cur_mt_pos[snd_trk[id]][CY_YPOS]);
+					CY_MT_SYNC(ts->input);
+					cyttsp_debug("MT1->TID:%2d X:%3d Y:%3d Z:%3d touch-sent\n", \
+						snd_trk[id], \
+						cur_mt_pos[snd_trk[id]][CY_XPOS], \
+						cur_mt_pos[snd_trk[id]][CY_YPOS], \
+						cur_mt_z[snd_trk[id]]);
+				} else if (ts->prv_mt_tch[id] < CY_NUM_TRK_ID) {
+					/* void out this touch */
+					input_report_abs(ts->input,
+						ABS_MT_TOUCH_MAJOR,
+						CY_NTCH);
+					input_report_abs(ts->input,
+						ABS_MT_WIDTH_MAJOR,
+						curr_tool_width);
+					input_report_abs(ts->input,
+						ABS_MT_POSITION_X,
+						ts->prv_mt_pos[ts->prv_mt_tch[id]][CY_XPOS]);
+					input_report_abs(ts->input,
+						ABS_MT_POSITION_Y,
+						ts->prv_mt_pos[ts->prv_mt_tch[id]][CY_YPOS]);
+					CY_MT_SYNC(ts->input);
+					cyttsp_debug("MT2->TID:%2d X:%3d Y:%3d Z:%3d lift off-sent\n", \
+						ts->prv_mt_tch[id], \
+						ts->prv_mt_pos[ts->prv_mt_tch[id]][CY_XPOS], \
+						ts->prv_mt_pos[ts->prv_mt_tch[id]][CY_YPOS], \
+						CY_NTCH);
+				} else {
+					/* do not stuff any signals for this
+					 * previously and currently
+					 * void touches */
+					cyttsp_xdebug("MT3->send[%d]=%d - No touch - NOT sent\n", \
+							id, snd_trk[id]);
+				}
+			}
+
+			/* save current posted tracks to
+			 * previous track memory */
+			for (id = 0; id < CY_NUM_MT_TCH_ID; id++) {
+				ts->prv_mt_tch[id] = snd_trk[id];
+				if (snd_trk[id] < CY_NUM_TRK_ID) {
+					ts->prv_mt_pos[snd_trk[id]][CY_XPOS] =
+						cur_mt_pos[snd_trk[id]][CY_XPOS];
+					ts->prv_mt_pos[snd_trk[id]][CY_YPOS] =
+						cur_mt_pos[snd_trk[id]][CY_YPOS];
+					cyttsp_xdebug("MT4->TID:%2d X:%3d Y:%3d Z:%3d save for previous\n", \
+						snd_trk[id], \
+						ts->prv_mt_pos[snd_trk[id]][CY_XPOS], \
+						ts->prv_mt_pos[snd_trk[id]][CY_YPOS], \
+						CY_NTCH);
+				}
+			}
+			for (id = 0; id < CY_NUM_TRK_ID; id++)
+				ts->act_trk[id] = CY_NTCH;
+			for (id = 0; id < CY_NUM_MT_TCH_ID; id++) {
+				if (snd_trk[id] < CY_NUM_TRK_ID)
+					ts->act_trk[snd_trk[id]] = CY_TCH;
+			}
+		}
+	}
+
+	/* handle gestures */
+	if (ts->platform_data->use_gestures) {
+		if (g_xy_data.gest_id) {
+			input_report_key(ts->input,
+				BTN_3, CY_TCH);
+			input_report_abs(ts->input,
+				ABS_HAT1X, g_xy_data.gest_id);
+			input_report_abs(ts->input,
+				ABS_HAT2Y, g_xy_data.gest_cnt);
+		}
+	}
+
+	/* signal the view motion event */
+	input_sync(ts->input);
+
+	for (id = 0; id < CY_NUM_TRK_ID; id++) {
+		/* update platform data for the current MT information */
+		ts->act_trk[id] = cur_trk[id];
+	}
+
+exit_xy_handler:
+	/* restart event timer */
+	if (ts->client->irq == 0)
+		mod_timer(&ts->timer, jiffies + TOUCHSCREEN_TIMEOUT);
+	return;
+}
+
+static int cyttsp_inlist(u16 prev_track[], u8 cur_trk_id,
+			u8 *prev_loc, u8 num_touches)
+{
+	u8 id = 0;
+
+	*prev_loc = CY_IGNR_TCH;
+
+		cyttsp_xdebug("IN p[%d]=%d c=%d n=%d loc=%d\n", \
+			id, prev_track[id], cur_trk_id, \
+			num_touches, *prev_loc);
+	for (id = 0, *prev_loc = CY_IGNR_TCH;
+		(id < num_touches); id++) {
+		cyttsp_xdebug("p[%d]=%d c=%d n=%d loc=%d\n", \
+			id, prev_track[id], cur_trk_id, \
+			num_touches, *prev_loc);
+		if (prev_track[id] == cur_trk_id) {
+			*prev_loc = id;
+			break;
+		}
+	}
+	cyttsp_xdebug("OUT p[%d]=%d c=%d n=%d loc=%d\n", \
+		id, prev_track[id], cur_trk_id, num_touches, *prev_loc);
+
+	return ((*prev_loc < CY_NUM_TRK_ID) ? true : false);
+}
+
+static int cyttsp_next_avail_inlist(u16 cur_trk[],
+			u8 *new_loc, u8 num_touches)
+{
+	u8 id;
+
+	for (id = 0, *new_loc = CY_IGNR_TCH;
+		(id < num_touches); id++) {
+		if (cur_trk[id] > CY_NUM_TRK_ID) {
+			*new_loc = id;
+			break;
+		}
+	}
+
+	return ((*new_loc < CY_NUM_TRK_ID) ? true : false);
+}
+
+/* Timer function used as dummy interrupt driver */
+static void cyttsp_timer(unsigned long handle)
+{
+	struct cyttsp *ts = (struct cyttsp *) handle;
+
+	cyttsp_xdebug("TTSP Device timer event\n");
+
+	/* schedule motion signal handling */
+	cyttsp_xy_handler(ts);
+
+	return;
+}
+
+
+
+/* ************************************************************************
+ * ISR function. This function is general, initialized in drivers init
+ * function
+ * ************************************************************************ */
+static irqreturn_t cyttsp_irq(int irq, void *handle)
+{
+	struct cyttsp *ts = (struct cyttsp *) handle;
+
+	cyttsp_xdebug("%s: Got IRQ\n", CY_I2C_NAME);
+
+	cyttsp_xy_handler(ts);
+
+	return IRQ_HANDLED;
+}
+
+/* ************************************************************************
+ * Probe initialization functions
+ * ************************************************************************ */
+static int cyttsp_putbl(struct cyttsp *ts, int show,
+			int show_status, int show_version, int show_cid)
+{
+	int retval = CY_OK;
+
+	int num_bytes = (show_status * 3) + (show_version * 6) + (show_cid * 3);
+
+	if (show_cid)
+		num_bytes = sizeof(struct cyttsp_bootloader_data_t);
+	else if (show_version)
+		num_bytes = sizeof(struct cyttsp_bootloader_data_t) - 3;
+	else
+		num_bytes = sizeof(struct cyttsp_bootloader_data_t) - 9;
+
+	if (show) {
+		retval = i2c_smbus_read_i2c_block_data(ts->client,
+			CY_REG_BASE, num_bytes, (u8 *)&g_bl_data);
+		if (show_status) {
+			cyttsp_debug("BL%d: f=%02X s=%02X err=%02X bl=%02X%02X bld=%02X%02X\n", \
+				show, \
+				g_bl_data.bl_file, \
+				g_bl_data.bl_status, \
+				g_bl_data.bl_error, \
+				g_bl_data.blver_hi, g_bl_data.blver_lo, \
+				g_bl_data.bld_blver_hi, g_bl_data.bld_blver_lo);
+		}
+		if (show_version) {
+			cyttsp_debug("BL%d: ttspver=0x%02X%02X appid=0x%02X%02X appver=0x%02X%02X\n", \
+				show, \
+				g_bl_data.ttspver_hi, g_bl_data.ttspver_lo, \
+				g_bl_data.appid_hi, g_bl_data.appid_lo, \
+				g_bl_data.appver_hi, g_bl_data.appver_lo);
+		}
+		if (show_cid) {
+			cyttsp_debug("BL%d: cid=0x%02X%02X%02X\n", \
+				show, \
+				g_bl_data.cid_0, \
+				g_bl_data.cid_1, \
+				g_bl_data.cid_2);
+		}
+	}
+
+	return retval;
+}
+
+#ifdef CY_INCLUDE_LOAD_FILE
+#define CY_MAX_I2C_LEN	256
+#define CY_MAX_TRY		10
+#define CY_BL_PAGE_SIZE	16
+#define CY_BL_NUM_PAGES	5
+static int cyttsp_i2c_wr_blk_chunks(struct cyttsp *ts, u8 command,
+	u8 length, const u8 *values)
+{
+	int retval = CY_OK;
+	int block = 1;
+
+	u8 dataray[CY_MAX_I2C_LEN];
+
+	/* first page already includes the bl page offset */
+	retval = i2c_smbus_write_i2c_block_data(ts->client, CY_REG_BASE,
+		CY_BL_PAGE_SIZE+1, values);
+	values += CY_BL_PAGE_SIZE+1;
+	length -= CY_BL_PAGE_SIZE+1;
+
+	/* rem blocks require bl page offset stuffing */
+	while (length &&
+		(block < CY_BL_NUM_PAGES) &&
+		!(retval < CY_OK)) {
+		udelay(43*2);	/* TRM * 2 */
+		dataray[0] = CY_BL_PAGE_SIZE*block;
+		memcpy(&dataray[1], values,
+			length >= CY_BL_PAGE_SIZE ?
+			CY_BL_PAGE_SIZE : length);
+		retval = i2c_smbus_write_i2c_block_data(ts->client,
+			CY_REG_BASE,
+			length >= CY_BL_PAGE_SIZE ?
+			CY_BL_PAGE_SIZE + 1 : length+1, dataray);
+		values += CY_BL_PAGE_SIZE;
+		length = length >= CY_BL_PAGE_SIZE ?
+			length - CY_BL_PAGE_SIZE : 0;
+		block++;
+	}
+
+	return retval;
+}
+
+static int cyttsp_bootload_app(struct cyttsp *ts)
+{
+	int retval = CY_OK;
+	int i, tries;
+	u8 host_reg;
+
+	cyttsp_debug("load new firmware \n");
+	/* reset TTSP Device back to bootloader mode */
+	host_reg = CY_SOFT_RESET_MODE;
+	retval = i2c_smbus_write_i2c_block_data(ts->client, CY_REG_BASE,
+		sizeof(host_reg), &host_reg);
+	/* wait for TTSP Device to complete reset back to bootloader */
+	tries = 0;
+	do {
+		usleep_range(1000, 1000);
+		cyttsp_putbl(ts, 3, false, false, false);
+	} while (g_bl_data.bl_status != 0x10 &&
+		g_bl_data.bl_status != 0x11 &&
+		tries++ < 100);
+	cyttsp_debug("load file - tver=0x%02X%02X a_id=0x%02X%02X aver=0x%02X%02X\n", \
+		cyttsp_fw_tts_verh, cyttsp_fw_tts_verl, \
+		cyttsp_fw_app_idh, cyttsp_fw_app_idl, \
+		cyttsp_fw_app_verh, cyttsp_fw_app_verl);
+
+	/* download new TTSP Application to the Bootloader */
+	if (!(retval < CY_OK)) {
+		i = 0;
+		/* send bootload initiation command */
+		if (cyttsp_fw[i].Command == CY_BL_INIT_LOAD) {
+			g_bl_data.bl_file = 0;
+			g_bl_data.bl_status = 0;
+			g_bl_data.bl_error = 0;
+			retval = i2c_smbus_write_i2c_block_data(ts->client,
+				CY_REG_BASE,
+				cyttsp_fw[i].Length, cyttsp_fw[i].Block);
+			/* delay to allow bl to get ready for block writes */
+			i++;
+			tries = 0;
+			do {
+				msleep(100);
+				cyttsp_putbl(ts, 4, false, false, false);
+			} while (g_bl_data.bl_status != 0x10 &&
+				g_bl_data.bl_status != 0x11 &&
+				tries++ < 100);
+			cyttsp_debug("wait init f=%02X, s=%02X, e=%02X t=%d\n", \
+				g_bl_data.bl_file, g_bl_data.bl_status, \
+				g_bl_data.bl_error, tries);
+			/* send bootload firmware load blocks */
+			if (!(retval < CY_OK)) {
+				while (cyttsp_fw[i].Command == CY_BL_WRITE_BLK) {
+					retval = cyttsp_i2c_wr_blk_chunks(ts,
+						CY_REG_BASE,
+						cyttsp_fw[i].Length,
+						cyttsp_fw[i].Block);
+					cyttsp_xdebug("BL DNLD Rec=% 3d Len=% 3d Addr=%04X\n", \
+						cyttsp_fw[i].Record, \
+						cyttsp_fw[i].Length, \
+						cyttsp_fw[i].Address);
+					i++;
+					if (retval < CY_OK) {
+						cyttsp_debug("BL fail Rec=%3d retval=%d\n", \
+							cyttsp_fw[i-1].Record, \
+							retval);
+						break;
+					} else {
+						tries = 0;
+						cyttsp_putbl(ts, 5, false, false, false);
+						while (!((g_bl_data.bl_status == 0x10) &&
+							(g_bl_data.bl_error == 0x20)) &&
+							!((g_bl_data.bl_status == 0x11) &&
+							(g_bl_data.bl_error == 0x20)) &&
+							(tries++ < 100)) {
+							usleep_range(1000, 1000);
+							cyttsp_putbl(ts, 5, false, false, false);
+						}
+					}
+				}
+
+				if (!(retval < CY_OK)) {
+					while (i < cyttsp_fw_records) {
+						retval = i2c_smbus_write_i2c_block_data(ts->client, CY_REG_BASE,
+							cyttsp_fw[i].Length,
+							cyttsp_fw[i].Block);
+						i++;
+						tries = 0;
+						do {
+							msleep(100);
+							cyttsp_putbl(ts, 6, true, false, false);
+						} while (g_bl_data.bl_status != 0x10 &&
+							g_bl_data.bl_status != 0x11 &&
+							tries++ < 100);
+						cyttsp_debug("wait term f=%02X, s=%02X, e=%02X t=%d\n", \
+							g_bl_data.bl_file, \
+							g_bl_data.bl_status, \
+							g_bl_data.bl_error, \
+							tries);
+						if (retval < CY_OK)
+							break;
+					}
+				}
+			}
+		}
+	}
+
+	/* reset TTSP Device back to bootloader mode */
+	host_reg = CY_SOFT_RESET_MODE;
+	retval = i2c_smbus_write_i2c_block_data(ts->client, CY_REG_BASE,
+		sizeof(host_reg), &host_reg);
+	/* wait for TTSP Device to complete reset back to bootloader */
+	tries = 0;
+	do {
+		usleep_range(1000, 1000);
+		cyttsp_putbl(ts, 3, false, false, false);
+	} while (g_bl_data.bl_status != 0x10 &&
+		g_bl_data.bl_status != 0x11 &&
+		tries++ < 100);
+
+	/* set arg2 to non-0 to activate */
+	retval = cyttsp_putbl(ts, 8, true, true, true);
+
+	return retval;
+}
+#else
+static int cyttsp_bootload_app(struct cyttsp *ts)
+{
+	cyttsp_debug("no-load new firmware \n");
+	return CY_OK;
+}
+#endif /* CY_INCLUDE_LOAD_FILE */
+
+
+static int cyttsp_power_on(struct cyttsp *ts)
+{
+	int retval = CY_OK;
+	u8 host_reg;
+	int tries;
+
+	cyttsp_debug("Power up \n");
+
+	/* check if the TTSP device has a bootloader installed */
+	host_reg = CY_SOFT_RESET_MODE;
+	retval = i2c_smbus_write_i2c_block_data(ts->client, CY_REG_BASE,
+		sizeof(host_reg), &host_reg);
+	tries = 0;
+	do {
+		usleep_range(1000, 1000);
+
+		/* set arg2 to non-0 to activate */
+		retval = cyttsp_putbl(ts, 1, true, true, true);
+		cyttsp_info("BL%d: f=%02X s=%02X err=%02X bl=%02X%02X bld=%02X%02X R=%d\n", \
+			101, \
+			g_bl_data.bl_file, g_bl_data.bl_status, \
+			g_bl_data.bl_error, \
+			g_bl_data.blver_hi, g_bl_data.blver_lo, \
+			g_bl_data.bld_blver_hi, g_bl_data.bld_blver_lo,
+			retval);
+		cyttsp_info("BL%d: tver=%02X%02X a_id=%02X%02X aver=%02X%02X\n", \
+			102, \
+			g_bl_data.ttspver_hi, g_bl_data.ttspver_lo, \
+			g_bl_data.appid_hi, g_bl_data.appid_lo, \
+			g_bl_data.appver_hi, g_bl_data.appver_lo);
+		cyttsp_info("BL%d: c_id=%02X%02X%02X\n", \
+			103, \
+			g_bl_data.cid_0, g_bl_data.cid_1, g_bl_data.cid_2);
+	} while (!(retval < CY_OK) &&
+		!GET_BOOTLOADERMODE(g_bl_data.bl_status) &&
+		!(g_bl_data.bl_file == CY_OP_MODE + CY_LOW_PWR_MODE) &&
+		tries++ < 100);
+
+	/* is bootloader missing? */
+	if (!(retval < CY_OK)) {
+		cyttsp_xdebug("Ret=%d  Check if bootloader is missing...\n", \
+			retval);
+		if (!GET_BOOTLOADERMODE(g_bl_data.bl_status)) {
+			/* skip all bl and sys info and go to op mode */
+			if (!(retval < CY_OK)) {
+				cyttsp_xdebug("Bl is missing (ret=%d)\n", \
+					retval);
+				host_reg = CY_OP_MODE/* + CY_LOW_PWR_MODE*/;
+				retval = i2c_smbus_write_i2c_block_data(ts->client, CY_REG_BASE,
+					sizeof(host_reg), &host_reg);
+				/* wait for TTSP Device to complete switch to
+				 * Operational mode */
+				msleep(1000);
+				goto bypass;
+			}
+		}
+	}
+
+
+	/* take TTSP out of bootloader mode; go to TrueTouch operational mode */
+	if (!(retval < CY_OK)) {
+		cyttsp_xdebug1("exit bootloader; go operational\n");
+		tries = 0;
+		do {
+			msleep(100);
+			retval = i2c_smbus_write_i2c_block_data(ts->client,
+				CY_REG_BASE, sizeof(bl_cmd), bl_cmd);
+			if (retval == CY_OK)
+				break;
+		} while (tries++ < 5);
+
+		if (retval == CY_OK) {
+			tries = 0;
+			do {
+				msleep(100);
+				cyttsp_putbl(ts, 4, true, false, false);
+				cyttsp_info("BL%d: f=%02X s=%02X err=%02X" \
+					"bl=%02X%02X bld=%02X%02X\n", 104, \
+					g_bl_data.bl_file, \
+					g_bl_data.bl_status, \
+					g_bl_data.bl_error, \
+					g_bl_data.blver_hi, \
+					g_bl_data.blver_lo, \
+					g_bl_data.bld_blver_hi, \
+					g_bl_data.bld_blver_lo);
+			} while (GET_BOOTLOADERMODE(g_bl_data.bl_status) &&
+				tries++ < 5);
+		}
+	}
+
+	if (!(retval < CY_OK) &&
+		cyttsp_app_load()) {
+		if (CY_DIFF(g_bl_data.ttspver_hi, cyttsp_tts_verh())  ||
+			CY_DIFF(g_bl_data.ttspver_lo, cyttsp_tts_verl())  ||
+			CY_DIFF(g_bl_data.appid_hi, cyttsp_app_idh())  ||
+			CY_DIFF(g_bl_data.appid_lo, cyttsp_app_idl())  ||
+			CY_DIFF(g_bl_data.appver_hi, cyttsp_app_verh())  ||
+			CY_DIFF(g_bl_data.appver_lo, cyttsp_app_verl())  ||
+			CY_DIFF(g_bl_data.cid_0, cyttsp_cid_0())  ||
+			CY_DIFF(g_bl_data.cid_1, cyttsp_cid_1())  ||
+			CY_DIFF(g_bl_data.cid_2, cyttsp_cid_2())  ||
+			cyttsp_force_fw_load()) {
+			cyttsp_debug("blttsp=0x%02X%02X flttsp=0x%02X%02X force=%d\n", \
+				g_bl_data.ttspver_hi, g_bl_data.ttspver_lo, \
+				cyttsp_tts_verh(), cyttsp_tts_verl(), \
+				cyttsp_force_fw_load());
+			cyttsp_debug("blappid=0x%02X%02X flappid=0x%02X%02X\n", \
+				g_bl_data.appid_hi, g_bl_data.appid_lo, \
+				cyttsp_app_idh(), cyttsp_app_idl());
+			cyttsp_debug("blappver=0x%02X%02X flappver=0x%02X%02X\n", \
+				g_bl_data.appver_hi, g_bl_data.appver_lo, \
+				cyttsp_app_verh(), cyttsp_app_verl());
+			cyttsp_debug("blcid=0x%02X%02X%02X flcid=0x%02X%02X%02X\n", \
+				g_bl_data.cid_0, \
+				g_bl_data.cid_1, \
+				g_bl_data.cid_2, \
+				cyttsp_cid_0(), \
+				cyttsp_cid_1(), \
+				cyttsp_cid_2());
+			/* enter bootloader to load new app into TTSP Device */
+			retval = cyttsp_bootload_app(ts);
+			/* take TTSP device out of bootloader mode;
+			 * switch back to TrueTouch operational mode */
+			if (!(retval < CY_OK)) {
+				retval = i2c_smbus_write_i2c_block_data(ts->client,
+					CY_REG_BASE,
+					sizeof(bl_cmd), bl_cmd);
+				/* wait for TTSP Device to complete
+				 * switch to Operational mode */
+				tries = 0;
+				do {
+					msleep(100);
+					cyttsp_putbl(ts, 9, false, false, false);
+				} while (GET_BOOTLOADERMODE(g_bl_data.bl_status) &&
+					tries++ < 100);
+				cyttsp_putbl(ts, 9, true, false, false);
+			}
+		}
+	}
+
+bypass:
+	/* switch to System Information mode to read versions
+	 * and set interval registers */
+	if (!(retval < CY_OK)) {
+		cyttsp_debug("switch to sysinfo mode \n");
+		host_reg = CY_SYSINFO_MODE;
+		retval = i2c_smbus_write_i2c_block_data(ts->client,
+			CY_REG_BASE, sizeof(host_reg), &host_reg);
+		/* wait for TTSP Device to complete switch to SysInfo mode */
+		msleep(100);
+		if (!(retval < CY_OK)) {
+			retval = i2c_smbus_read_i2c_block_data(ts->client,
+				CY_REG_BASE,
+				sizeof(struct cyttsp_sysinfo_data_t),
+				(u8 *)&g_sysinfo_data);
+			cyttsp_debug("SI2: hst_mode=0x%02X mfg_cmd=0x%02X mfg_stat=0x%02X\n", \
+				g_sysinfo_data.hst_mode, \
+				g_sysinfo_data.mfg_cmd, \
+				g_sysinfo_data.mfg_stat);
+			cyttsp_debug("SI2: bl_ver=0x%02X%02X\n", \
+				g_sysinfo_data.bl_verh, \
+				g_sysinfo_data.bl_verl);
+			cyttsp_debug("SI2: sysinfo act_int=0x%02X tch_tmout=0x%02X lp_int=0x%02X\n", \
+				g_sysinfo_data.act_intrvl, \
+				g_sysinfo_data.tch_tmout, \
+				g_sysinfo_data.lp_intrvl);
+			cyttsp_info("SI%d: tver=%02X%02X a_id=%02X%02X aver=%02X%02X\n", \
+				102, \
+				g_sysinfo_data.tts_verh, \
+				g_sysinfo_data.tts_verl, \
+				g_sysinfo_data.app_idh, \
+				g_sysinfo_data.app_idl, \
+				g_sysinfo_data.app_verh, \
+				g_sysinfo_data.app_verl);
+			cyttsp_info("SI%d: c_id=%02X%02X%02X\n", \
+				103, \
+				g_sysinfo_data.cid[0], \
+				g_sysinfo_data.cid[1], \
+				g_sysinfo_data.cid[2]);
+			if (!(retval < CY_OK) &&
+				(CY_DIFF(ts->platform_data->act_intrvl,
+					CY_ACT_INTRVL_DFLT)  ||
+				CY_DIFF(ts->platform_data->tch_tmout,
+					CY_TCH_TMOUT_DFLT) ||
+				CY_DIFF(ts->platform_data->lp_intrvl,
+					CY_LP_INTRVL_DFLT))) {
+				if (!(retval < CY_OK)) {
+					u8 intrvl_ray[sizeof(ts->platform_data->act_intrvl) +
+						sizeof(ts->platform_data->tch_tmout) +
+						sizeof(ts->platform_data->lp_intrvl)];
+					u8 i = 0;
+
+					intrvl_ray[i++] =
+						ts->platform_data->act_intrvl;
+					intrvl_ray[i++] =
+						ts->platform_data->tch_tmout;
+					intrvl_ray[i++] =
+						ts->platform_data->lp_intrvl;
+
+					cyttsp_debug("SI2: platinfo act_intrvl=0x%02X tch_tmout=0x%02X lp_intrvl=0x%02X\n", \
+						ts->platform_data->act_intrvl, \
+						ts->platform_data->tch_tmout, \
+						ts->platform_data->lp_intrvl);
+					/* set intrvl registers */
+					retval = i2c_smbus_write_i2c_block_data(
+						ts->client,
+						CY_REG_ACT_INTRVL,
+						sizeof(intrvl_ray), intrvl_ray);
+					msleep(CY_DLY_SYSINFO);
+				}
+			}
+		}
+		/* switch back to Operational mode */
+		cyttsp_debug("switch back to operational mode \n");
+		if (!(retval < CY_OK)) {
+			host_reg = CY_OP_MODE/* + CY_LOW_PWR_MODE*/;
+			retval = i2c_smbus_write_i2c_block_data(ts->client,
+				CY_REG_BASE,
+				sizeof(host_reg), &host_reg);
+			/* wait for TTSP Device to complete
+			 * switch to Operational mode */
+			msleep(100);
+		}
+	}
+	/* init gesture setup;
+	 * this is required even if not using gestures
+	 * in order to set the active distance */
+	if (!(retval < CY_OK)) {
+		u8 gesture_setup;
+		cyttsp_debug("init gesture setup \n");
+		gesture_setup = ts->platform_data->gest_set;
+		retval = i2c_smbus_write_i2c_block_data(ts->client,
+			CY_REG_GEST_SET,
+			sizeof(gesture_setup), &gesture_setup);
+		msleep(CY_DLY_DFLT);
+	}
+
+	if (!(retval < CY_OK))
+		ts->platform_data->power_state = CY_ACTIVE_STATE;
+	else
+		ts->platform_data->power_state = CY_IDLE_STATE;
+
+	cyttsp_debug("Retval=%d Power state is %s\n", \
+		retval, \
+		ts->platform_data->power_state == CY_ACTIVE_STATE ? \
+		 "ACTIVE" : "IDLE");
+
+	return retval;
+}
+
+static int cyttsp_power_device(struct cyttsp *ts, bool on)
+{
+	int rc = 0, i;
+	const struct cyttsp_regulator *reg_info =
+				ts->platform_data->regulator_info;
+	u8 num_reg = ts->platform_data->num_regulators;
+
+	if (!reg_info) {
+		pr_err("regulator pdata not specified\n");
+		return -EINVAL;
+	}
+
+	if (on == false) /* Turn off the regulators */
+		goto ts_reg_disable;
+
+	ts->vdd = kzalloc(num_reg * sizeof(struct regulator *), GFP_KERNEL);
+	if (!ts->vdd) {
+		pr_err("unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < num_reg; i++) {
+		ts->vdd[i] = regulator_get(&ts->client->dev, reg_info[i].name);
+		if (IS_ERR(ts->vdd[i])) {
+			rc = PTR_ERR(ts->vdd[i]);
+			pr_err("%s:regulator get failed rc=%d\n",
+							__func__, rc);
+			goto error_vdd;
+		}
+
+		if (regulator_count_voltages(ts->vdd[i]) > 0) {
+			rc = regulator_set_voltage(ts->vdd[i],
+				reg_info[i].min_uV, reg_info[i].max_uV);
+			if (rc) {
+				pr_err("%s: regulator_set_voltage"
+					"failed rc =%d\n", __func__, rc);
+				regulator_put(ts->vdd[i]);
+				goto error_vdd;
+			}
+
+			rc = regulator_set_optimum_mode(ts->vdd[i],
+						reg_info[i].hpm_load_uA);
+			if (rc < 0) {
+				pr_err("%s: regulator_set_optimum_mode failed "
+				       "rc=%d\n", __func__, rc);
+
+				regulator_set_voltage(ts->vdd[i], 0,
+							reg_info[i].max_uV);
+				regulator_put(ts->vdd[i]);
+				goto error_vdd;
+			}
+		}
+
+		rc = regulator_enable(ts->vdd[i]);
+		if (rc) {
+			pr_err("%s: regulator_enable failed rc =%d\n",
+								__func__, rc);
+			if (regulator_count_voltages(ts->vdd[i]) > 0) {
+				regulator_set_optimum_mode(ts->vdd[i], 0);
+				regulator_set_voltage(ts->vdd[i], 0,
+						      reg_info[i].max_uV);
+			}
+			regulator_put(ts->vdd[i]);
+			goto error_vdd;
+		}
+	}
+
+	return rc;
+
+ts_reg_disable:
+	i = ts->platform_data->num_regulators;
+error_vdd:
+	while (--i >= 0) {
+		if (regulator_count_voltages(ts->vdd[i]) > 0) {
+			regulator_set_voltage(ts->vdd[i], 0,
+						reg_info[i].max_uV);
+			regulator_set_optimum_mode(ts->vdd[i], 0);
+		}
+		regulator_disable(ts->vdd[i]);
+		regulator_put(ts->vdd[i]);
+	}
+	kfree(ts->vdd);
+	return rc;
+}
+
+/* cyttsp_initialize: Driver Initialization. This function takes
+ * care of the following tasks:
+ * 1. Create and register an input device with input layer
+ * 2. Take CYTTSP device out of bootloader mode; go operational
+ * 3. Start any timers/Work queues.  */
+static int cyttsp_initialize(struct i2c_client *client, struct cyttsp *ts)
+{
+	struct input_dev *input_device;
+	int error = 0;
+	int retval = CY_OK;
+	u8 id;
+
+	/* Create the input device and register it. */
+	input_device = input_allocate_device();
+	if (!input_device) {
+		error = -ENOMEM;
+		cyttsp_xdebug1("err input allocate device\n");
+		goto error_free_device;
+	}
+
+	if (!client) {
+		error = ~ENODEV;
+		cyttsp_xdebug1("err client is Null\n");
+		goto error_free_device;
+	}
+
+	if (!ts) {
+		error = ~ENODEV;
+		cyttsp_xdebug1("err context is Null\n");
+		goto error_free_device;
+	}
+
+	ts->input = input_device;
+	input_device->name = CY_I2C_NAME;
+	input_device->phys = ts->phys;
+	input_device->dev.parent = &client->dev;
+
+	/* init the touch structures */
+	ts->num_prv_st_tch = CY_NTCH;
+	for (id = 0; id < CY_NUM_TRK_ID; id++) {
+		ts->act_trk[id] = CY_NTCH;
+		ts->prv_mt_pos[id][CY_XPOS] = 0;
+		ts->prv_mt_pos[id][CY_YPOS] = 0;
+	}
+
+	for (id = 0; id < CY_NUM_MT_TCH_ID; id++)
+		ts->prv_mt_tch[id] = CY_IGNR_TCH;
+
+	for (id = 0; id < CY_NUM_ST_TCH_ID; id++)
+		ts->prv_st_tch[id] = CY_IGNR_TCH;
+
+	set_bit(EV_SYN, input_device->evbit);
+	set_bit(EV_KEY, input_device->evbit);
+	set_bit(EV_ABS, input_device->evbit);
+	set_bit(BTN_TOUCH, input_device->keybit);
+	set_bit(BTN_2, input_device->keybit);
+	if (ts->platform_data->use_gestures)
+		set_bit(BTN_3, input_device->keybit);
+
+	input_set_abs_params(input_device, ABS_X, ts->platform_data->disp_minx,
+		ts->platform_data->disp_maxx, 0, 0);
+	input_set_abs_params(input_device, ABS_Y, ts->platform_data->disp_miny,
+		ts->platform_data->disp_maxy, 0, 0);
+	input_set_abs_params(input_device,
+		ABS_TOOL_WIDTH, 0, CY_LARGE_TOOL_WIDTH, 0 , 0);
+	input_set_abs_params(input_device,
+		ABS_PRESSURE, 0, CY_MAXZ, 0, 0);
+	input_set_abs_params(input_device,
+		ABS_HAT0X, 0, ts->platform_data->panel_maxx, 0, 0);
+	input_set_abs_params(input_device,
+		ABS_HAT0Y, 0, ts->platform_data->panel_maxy, 0, 0);
+	if (ts->platform_data->use_gestures) {
+		input_set_abs_params(input_device,
+			ABS_HAT1X, 0, CY_MAXZ, 0, 0);
+		input_set_abs_params(input_device,
+			ABS_HAT1Y, 0, CY_MAXZ, 0, 0);
+	}
+	if (ts->platform_data->use_mt) {
+		input_set_abs_params(input_device, ABS_MT_POSITION_X,
+			ts->platform_data->disp_minx,
+			ts->platform_data->disp_maxx, 0, 0);
+		input_set_abs_params(input_device, ABS_MT_POSITION_Y,
+			ts->platform_data->disp_miny,
+			ts->platform_data->disp_maxy, 0, 0);
+		input_set_abs_params(input_device,
+			ABS_MT_TOUCH_MAJOR, 0, CY_MAXZ, 0, 0);
+		input_set_abs_params(input_device,
+			ABS_MT_WIDTH_MAJOR, 0, CY_LARGE_TOOL_WIDTH, 0, 0);
+		if (ts->platform_data->use_trk_id) {
+			input_set_abs_params(input_device,
+				ABS_MT_TRACKING_ID, 0, CY_NUM_TRK_ID, 0, 0);
+		}
+	}
+
+	/* set dummy key to make driver work with virtual keys */
+	input_set_capability(input_device, EV_KEY, KEY_PROG1);
+
+	cyttsp_info("%s: Register input device\n", CY_I2C_NAME);
+	error = input_register_device(input_device);
+	if (error) {
+		cyttsp_alert("%s: Failed to register input device\n", \
+			CY_I2C_NAME);
+		retval = error;
+		goto error_free_device;
+	}
+
+	if (gpio_is_valid(ts->platform_data->resout_gpio)) {
+		/* configure touchscreen reset out gpio */
+		retval = gpio_request(ts->platform_data->resout_gpio,
+						"cyttsp_resout_gpio");
+		if (retval) {
+			pr_err("%s: unable to request reset gpio %d\n",
+				__func__, ts->platform_data->resout_gpio);
+			goto error_free_device;
+		}
+
+		retval = gpio_direction_output(
+					ts->platform_data->resout_gpio, 1);
+		if (retval) {
+			pr_err("%s: unable to set direction for gpio %d\n",
+				__func__, ts->platform_data->resout_gpio);
+			goto error_resout_gpio_dir;
+		}
+	}
+
+	if (gpio_is_valid(ts->platform_data->sleep_gpio)) {
+		/* configure touchscreen reset out gpio */
+		retval = gpio_request(ts->platform_data->sleep_gpio,
+						"cy8c_sleep_gpio");
+		if (retval) {
+			pr_err("%s: unable to request sleep gpio %d\n",
+				__func__, ts->platform_data->sleep_gpio);
+			goto error_sleep_gpio_req;
+		}
+
+		retval = gpio_direction_output(
+					ts->platform_data->sleep_gpio, 0);
+		if (retval) {
+			pr_err("%s: unable to set direction for gpio %d\n",
+			__func__, ts->platform_data->resout_gpio);
+			goto error_sleep_gpio_dir;
+		}
+	}
+
+	if (gpio_is_valid(ts->platform_data->irq_gpio)) {
+		/* configure touchscreen irq gpio */
+		retval = gpio_request(ts->platform_data->irq_gpio,
+							"ts_irq_gpio");
+		if (retval) {
+			pr_err("%s: unable to request gpio [%d]\n", __func__,
+						ts->platform_data->irq_gpio);
+			goto error_irq_gpio_req;
+		}
+		retval = gpio_direction_input(ts->platform_data->irq_gpio);
+		if (retval) {
+			pr_err("%s: unable to set_direction for gpio [%d]\n",
+					__func__, ts->platform_data->irq_gpio);
+			goto error_irq_gpio_dir;
+		}
+	}
+
+	if (ts->platform_data->regulator_info) {
+		retval = cyttsp_power_device(ts, true);
+		if (retval) {
+			pr_err("%s: Unable to power device %d\n",
+						 __func__, retval);
+			goto error_irq_gpio_dir;
+		}
+	}
+
+	/* Power on the chip and make sure that I/Os are set as specified
+	 * in the platform */
+	if (ts->platform_data->init) {
+		retval = ts->platform_data->init(client);
+		if (retval) {
+			pr_err("%s: ts init failed\n", __func__);
+			goto error_power_device;
+		}
+	}
+
+	msleep(100);
+
+	/* check this device active by reading first byte/register */
+	retval = i2c_smbus_read_byte_data(ts->client, 0x01);
+	if (retval < 0) {
+		pr_err("%s: i2c sanity check failed\n", __func__);
+		goto error_power_device;
+	}
+
+	retval = cyttsp_power_on(ts);
+	if (retval < 0) {
+		pr_err("%s: cyttsp_power_on failed\n", __func__);
+		goto error_power_device;
+	}
+
+	/* Timer or Interrupt setup */
+	if (ts->client->irq == 0) {
+		cyttsp_info("Setting up timer\n");
+		setup_timer(&ts->timer, cyttsp_timer, (unsigned long) ts);
+		mod_timer(&ts->timer, jiffies + TOUCHSCREEN_TIMEOUT);
+	} else {
+		cyttsp_info("Setting up interrupt\n");
+		error = request_threaded_irq(client->irq, NULL, cyttsp_irq,
+			IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+			client->dev.driver->name, ts);
+		if (error) {
+			cyttsp_alert("error: could not request irq\n");
+			retval = error;
+			goto error_power_device;
+		}
+	}
+
+	irq_cnt = 0;
+	irq_cnt_total = 0;
+	irq_err_cnt = 0;
+
+	atomic_set(&ts->irq_enabled, 1);
+	retval = device_create_file(&ts->client->dev, &dev_attr_irq_enable);
+	if (retval < CY_OK) {
+		cyttsp_alert("File device creation failed: %d\n", retval);
+		retval = -ENODEV;
+		goto error_free_irq;
+	}
+
+	retval = device_create_file(&client->dev, &dev_attr_cyttsp_fw_ver);
+	if (retval) {
+		cyttsp_alert("sysfs entry for firmware version failed\n");
+		goto error_rm_dev_file_irq_en;
+	}
+
+	ts->cyttsp_fwloader_mode = 0;
+	retval = device_create_file(&client->dev, &dev_attr_cyttsp_update_fw);
+	if (retval) {
+		cyttsp_alert("sysfs entry for firmware update failed\n");
+		goto error_rm_dev_file_fw_ver;
+	}
+
+	retval = device_create_file(&client->dev,
+				&dev_attr_cyttsp_force_update_fw);
+	if (retval) {
+		cyttsp_alert("sysfs entry for force firmware update failed\n");
+		goto error_rm_dev_file_update_fw;
+	}
+	if (ts->platform_data->correct_fw_ver) {
+		if (g_bl_data.appid_lo != ts->platform_data->correct_fw_ver)
+			pr_warn("%s: Invalid firmware version detected;"
+				" Please update.\n", __func__);
+	}
+
+	retval = device_create_file(&client->dev,
+				&dev_attr_cyttsp_fw_name);
+	if (retval) {
+		cyttsp_alert("sysfs entry for file name selection failed\n");
+		goto error_rm_dev_file_fupdate_fw;
+	}
+
+	cyttsp_info("%s: Successful registration\n", CY_I2C_NAME);
+
+	goto success;
+
+error_rm_dev_file_fupdate_fw:
+	device_remove_file(&client->dev, &dev_attr_cyttsp_force_update_fw);
+error_rm_dev_file_update_fw:
+	device_remove_file(&client->dev, &dev_attr_cyttsp_update_fw);
+error_rm_dev_file_fw_ver:
+	device_remove_file(&client->dev, &dev_attr_cyttsp_fw_ver);
+error_rm_dev_file_irq_en:
+	device_remove_file(&client->dev, &dev_attr_irq_enable);
+error_free_irq:
+	if (ts->client->irq)
+		free_irq(client->irq, ts);
+error_power_device:
+	if (ts->platform_data->regulator_info)
+		cyttsp_power_device(ts, false);
+error_irq_gpio_dir:
+	if (gpio_is_valid(ts->platform_data->irq_gpio))
+		gpio_free(ts->platform_data->irq_gpio);
+error_irq_gpio_req:
+	if (gpio_is_valid(ts->platform_data->sleep_gpio))
+		gpio_direction_output(ts->platform_data->sleep_gpio, 1);
+error_sleep_gpio_dir:
+	if (gpio_is_valid(ts->platform_data->sleep_gpio))
+		gpio_free(ts->platform_data->sleep_gpio);
+error_sleep_gpio_req:
+	if (gpio_is_valid(ts->platform_data->resout_gpio))
+		gpio_direction_output(ts->platform_data->resout_gpio, 0);
+error_resout_gpio_dir:
+	if (gpio_is_valid(ts->platform_data->resout_gpio))
+		gpio_free(ts->platform_data->resout_gpio);
+error_free_device:
+	if (input_device)
+		input_free_device(input_device);
+
+success:
+	return retval;
+}
+
+/* I2C driver probe function */
+static int __devinit cyttsp_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct cyttsp *ts;
+	int error;
+	int retval = CY_OK;
+
+	cyttsp_info("Start Probe 1.2\n");
+
+	/* allocate and clear memory */
+	ts = kzalloc(sizeof(struct cyttsp), GFP_KERNEL);
+	if (ts == NULL) {
+		cyttsp_xdebug1("err kzalloc for cyttsp\n");
+		return -ENOMEM;
+	}
+
+	/* Enable runtime PM ops, start in ACTIVE mode */
+	error = pm_runtime_set_active(&client->dev);
+	if (error < 0)
+		dev_dbg(&client->dev, "unable to set runtime pm state\n");
+	pm_runtime_enable(&client->dev);
+
+	if (!(retval < CY_OK)) {
+		/* register driver_data */
+		ts->client = client;
+		ts->platform_data = client->dev.platform_data;
+
+		if (ts->platform_data->fw_fname)
+			strlcpy(ts->fw_fname, ts->platform_data->fw_fname,
+							FW_FNAME_LEN - 1);
+		else
+			strlcpy(ts->fw_fname, "cyttsp.hex", FW_FNAME_LEN - 1);
+
+		if (ts->platform_data->gen == CY_GEN3) {
+			ts->fw_start_addr = 0x0b00;
+		} else if (ts->platform_data->gen == CY_GEN2) {
+			ts->fw_start_addr = 0x0880;
+		} else {
+			pr_err("%s: unsupported cypress chip\n", __func__);
+			kfree(ts);
+			return -EINVAL;
+		}
+
+		i2c_set_clientdata(client, ts);
+
+		error = cyttsp_initialize(client, ts);
+		if (error) {
+			cyttsp_xdebug1("err cyttsp_initialize\n");
+			/* deallocate memory */
+			kfree(ts);
+/*
+			i2c_del_driver(&cyttsp_driver);
+*/
+			return -ENODEV;
+		}
+	}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	if (!(retval < CY_OK)) {
+		ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+		ts->early_suspend.suspend = cyttsp_early_suspend;
+		ts->early_suspend.resume = cyttsp_late_resume;
+		register_early_suspend(&ts->early_suspend);
+	}
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+	device_init_wakeup(&client->dev, ts->platform_data->wakeup);
+	mutex_init(&ts->mutex);
+
+	cyttsp_info("Start Probe %s\n", \
+		(retval < CY_OK) ? "FAIL" : "PASS");
+
+	return retval;
+}
+
+#ifdef CONFIG_PM
+static int cyttsp_regulator_lpm(struct cyttsp *ts, bool on)
+{
+	int rc = 0, i;
+	const struct cyttsp_regulator *reg_info =
+			ts->platform_data->regulator_info;
+	u8 num_reg = ts->platform_data->num_regulators;
+
+	if (on == false)
+		goto regulator_hpm;
+
+	for (i = 0; i < num_reg; i++) {
+		if (regulator_count_voltages(ts->vdd[i]) < 0)
+			continue;
+		rc = regulator_set_optimum_mode(ts->vdd[i],
+					reg_info[i].lpm_load_uA);
+		if (rc < 0) {
+			pr_err("%s: regulator_set_optimum failed rc = %d\n",
+							__func__, rc);
+			goto fail_regulator_lpm;
+		}
+
+	}
+
+	return 0;
+
+regulator_hpm:
+	for (i = 0; i < num_reg; i++) {
+		if (regulator_count_voltages(ts->vdd[i]) < 0)
+			continue;
+		rc = regulator_set_optimum_mode(ts->vdd[i],
+					reg_info[i].hpm_load_uA);
+		if (rc < 0) {
+			pr_err("%s: regulator_set_optimum failed"
+				"rc = %d\n", __func__, rc);
+			goto fail_regulator_hpm;
+		}
+	}
+
+	return 0;
+
+fail_regulator_lpm:
+	while (i--) {
+		if (regulator_count_voltages(ts->vdd[i]) < 0)
+			continue;
+		regulator_set_optimum_mode(ts->vdd[i],
+					reg_info[i].hpm_load_uA);
+	}
+
+	return rc;
+
+fail_regulator_hpm:
+	while (i--) {
+		if (regulator_count_voltages(ts->vdd[i]) < 0)
+			continue;
+		regulator_set_optimum_mode(ts->vdd[i],
+					reg_info[i].lpm_load_uA);
+	}
+
+	return rc;
+}
+
+/* Function to manage power-on resume */
+static int cyttsp_resume(struct device *dev)
+{
+	struct cyttsp *ts = dev_get_drvdata(dev);
+	int retval = CY_OK;
+
+	cyttsp_debug("Wake Up\n");
+
+	if (ts->is_suspended == false) {
+		pr_err("%s: in wakeup state\n", __func__);
+		return 0;
+	}
+
+	if (device_may_wakeup(dev)) {
+		if (ts->client->irq)
+			disable_irq_wake(ts->client->irq);
+		return 0;
+	}
+
+	/* re-enable the interrupt prior to wake device */
+	if (ts->client->irq)
+		enable_irq(ts->client->irq);
+
+	if (ts->platform_data->use_sleep &&
+		(ts->platform_data->power_state != CY_ACTIVE_STATE)) {
+		if (ts->platform_data->resume)
+			retval = ts->platform_data->resume(ts->client);
+		else
+			retval = cyttsp_regulator_lpm(ts, false);
+		/* take TTSP device out of bootloader mode;
+		 * switch back to TrueTouch operational mode */
+		if (!(retval < CY_OK)) {
+			int tries = 0;
+			do {
+				msleep(100);
+				retval = i2c_smbus_write_i2c_block_data(
+					ts->client, CY_REG_BASE,
+					sizeof(bl_cmd), bl_cmd);
+				if (retval == CY_OK)
+					break;
+			} while (tries++ < 2);
+			/* wait for TTSP Device to complete
+			 * switch to Operational mode */
+			tries = 0;
+			do {
+				msleep(100);
+				cyttsp_putbl(ts, 16, false, false, false);
+			} while (GET_BOOTLOADERMODE(g_bl_data.bl_status) &&
+				tries++ < 2);
+			cyttsp_putbl(ts, 16, true, false, false);
+		}
+	}
+
+	if (!(retval < CY_OK) &&
+		(GET_HSTMODE(g_bl_data.bl_file) == CY_OK)) {
+		ts->platform_data->power_state = CY_ACTIVE_STATE;
+
+		/* re-enable the timer after resuming */
+		if (ts->client->irq == 0)
+			mod_timer(&ts->timer, jiffies + TOUCHSCREEN_TIMEOUT);
+	} else
+		retval = -ENODEV;
+
+	ts->is_suspended = false;
+	cyttsp_debug("Wake Up %s\n", \
+		(retval < CY_OK) ? "FAIL" : "PASS");
+
+	return retval;
+}
+
+/* Function to manage low power suspend */
+static int cyttsp_suspend(struct device *dev)
+{
+	struct cyttsp *ts = dev_get_drvdata(dev);
+	u8 sleep_mode = CY_OK;
+	int retval = CY_OK;
+
+	cyttsp_debug("Enter Sleep\n");
+
+	if (ts->is_suspended == true) {
+		pr_err("%s: in sleep state\n", __func__);
+		return 0;
+	}
+
+	mutex_lock(&ts->mutex);
+	if (ts->cyttsp_fwloader_mode) {
+		pr_err("%s:firmware upgrade mode:"
+			"suspend not allowed\n", __func__);
+		mutex_unlock(&ts->mutex);
+		return -EBUSY;
+	}
+	mutex_unlock(&ts->mutex);
+
+	if (device_may_wakeup(dev)) {
+		if (ts->client->irq)
+			enable_irq_wake(ts->client->irq);
+		return 0;
+	}
+
+	if (ts->client->irq == 0)
+		del_timer(&ts->timer);
+	else
+		disable_irq(ts->client->irq);
+
+	if (!(retval < CY_OK)) {
+		if (ts->platform_data->use_sleep &&
+			(ts->platform_data->power_state == CY_ACTIVE_STATE)) {
+			if (ts->platform_data->suspend) {
+				retval =
+				ts->platform_data->suspend(ts->client);
+			} else {
+				retval = cyttsp_regulator_lpm(ts, true);
+			}
+			if (ts->platform_data->use_sleep & CY_USE_DEEP_SLEEP_SEL)
+				sleep_mode = CY_DEEP_SLEEP_MODE;
+			else
+				sleep_mode = CY_LOW_PWR_MODE;
+
+			if (!(retval < CY_OK)) {
+				retval =
+				i2c_smbus_write_i2c_block_data(ts->client,
+								CY_REG_BASE,
+					sizeof(sleep_mode), &sleep_mode);
+			}
+		}
+	}
+
+	if (!(retval < CY_OK)) {
+		if (sleep_mode == CY_DEEP_SLEEP_MODE)
+			ts->platform_data->power_state = CY_SLEEP_STATE;
+		else if (sleep_mode == CY_LOW_PWR_MODE)
+			ts->platform_data->power_state = CY_LOW_PWR_STATE;
+	}
+
+	ts->is_suspended = true;
+	cyttsp_debug("Sleep Power state is %s\n", \
+		(ts->platform_data->power_state == CY_ACTIVE_STATE) ? \
+		"ACTIVE" : \
+		((ts->platform_data->power_state == CY_SLEEP_STATE) ? \
+		"SLEEP" : "LOW POWER"));
+
+	return retval;
+}
+#endif
+
+/* registered in driver struct */
+static int __devexit cyttsp_remove(struct i2c_client *client)
+{
+	/* clientdata registered on probe */
+	struct cyttsp *ts = i2c_get_clientdata(client);
+	int err;
+
+	cyttsp_alert("Unregister\n");
+
+	pm_runtime_set_suspended(&client->dev);
+	pm_runtime_disable(&client->dev);
+
+	device_init_wakeup(&client->dev, 0);
+	device_remove_file(&ts->client->dev, &dev_attr_irq_enable);
+	device_remove_file(&client->dev, &dev_attr_cyttsp_fw_ver);
+	device_remove_file(&client->dev, &dev_attr_cyttsp_update_fw);
+	device_remove_file(&client->dev, &dev_attr_cyttsp_force_update_fw);
+	device_remove_file(&client->dev, &dev_attr_cyttsp_fw_name);
+
+	/* free up timer or irq */
+	if (ts->client->irq == 0) {
+		err = del_timer(&ts->timer);
+		if (err < CY_OK)
+			cyttsp_alert("error: failed to delete timer\n");
+	} else
+		free_irq(client->irq, ts);
+
+	if (ts->platform_data->regulator_info)
+		cyttsp_power_device(ts, false);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	unregister_early_suspend(&ts->early_suspend);
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+
+	mutex_destroy(&ts->mutex);
+
+	if (gpio_is_valid(ts->platform_data->sleep_gpio)) {
+		gpio_direction_output(ts->platform_data->sleep_gpio, 1);
+		gpio_free(ts->platform_data->sleep_gpio);
+	}
+
+	if (gpio_is_valid(ts->platform_data->resout_gpio)) {
+		gpio_direction_output(ts->platform_data->resout_gpio, 0);
+		gpio_free(ts->platform_data->resout_gpio);
+	}
+
+	if (gpio_is_valid(ts->platform_data->irq_gpio))
+		gpio_free(ts->platform_data->irq_gpio);
+
+	/* housekeeping */
+	kfree(ts);
+
+	cyttsp_alert("Leaving\n");
+
+	return 0;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void cyttsp_early_suspend(struct early_suspend *handler)
+{
+	struct cyttsp *ts;
+
+	ts = container_of(handler, struct cyttsp, early_suspend);
+	cyttsp_suspend(&ts->client->dev);
+}
+
+static void cyttsp_late_resume(struct early_suspend *handler)
+{
+	struct cyttsp *ts;
+
+	ts = container_of(handler, struct cyttsp, early_suspend);
+	cyttsp_resume(&ts->client->dev);
+}
+#endif  /* CONFIG_HAS_EARLYSUSPEND */
+
+static int cyttsp_init(void)
+{
+	int ret;
+
+	cyttsp_info("Cypress TrueTouch(R) Standard Product\n");
+	cyttsp_info("I2C Touchscreen Driver (Built %s @ %s)\n", \
+		__DATE__, __TIME__);
+
+	ret = i2c_add_driver(&cyttsp_driver);
+
+	return ret;
+}
+
+static void cyttsp_exit(void)
+{
+	return i2c_del_driver(&cyttsp_driver);
+}
+
+module_init(cyttsp_init);
+module_exit(cyttsp_exit);
+MODULE_FIRMWARE("cyttsp.fw");
+
diff --git a/drivers/input/touchscreen/cyttsp_fw.h b/drivers/input/touchscreen/cyttsp_fw.h
new file mode 100644
index 0000000..f14153e
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp_fw.h
@@ -0,0 +1,4307 @@
+/* Header file for: 
+ * Cypress TrueTouch(TM) Standard Product touchscreen drivers.
+ * drivers/input/touchscreen/cyttsp_fw.h
+ *
+ * Copyright (C) 2009, 2010 Cypress Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Cypress reserves the right to make changes without further notice
+ * to the materials described herein. Cypress does not assume any
+ * liability arising out of the application described herein.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com
+ *
+ */
+
+#define CYTTSP_BL_OST_LEN 1
+#define CYTTSP_BL_CMD_LEN 2
+#define CYTTSP_BL_KEY_LEN 8
+#define CYTTSP_LD_ADR_LEN 2
+#define CYTTSP_LD_DAT_LEN 64
+#define CYTTSP_LD_CHK_LEN 2
+#define CYTTSP_LD_BLK_LEN (CYTTSP_BL_OST_LEN + CYTTSP_BL_CMD_LEN + CYTTSP_BL_KEY_LEN + \
+    CYTTSP_LD_ADR_LEN + CYTTSP_LD_DAT_LEN + CYTTSP_LD_CHK_LEN)
+
+typedef struct cyttsp_ld_blk_ray_t {
+    unsigned short Record;
+    unsigned short Length;
+    unsigned char  Command;
+    unsigned short Address;
+    unsigned char  Block[CYTTSP_LD_BLK_LEN];
+} cyttsp_ld_blk_ray, *pcyttsp_ld_blk_ray;
+
+cyttsp_ld_blk_ray cyttsp_fw[] = {
+	{
+		0,
+		11,
+		0x38,
+		-1,
+		{
+			0x00, 0xFF, 0x38, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
+		}
+	},
+	{
+		1,
+		79,
+		0x39,
+		0x002C,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x2C, 0x40, 0x7D, 0x0B, 0x68, 0x30, 0x30, 0x30, 0x30, 0x7E, 0x30, 0x30, 0x30, 0x7D, 0x10, 0x12, 0x7E, 0x7D, 0x10, 0x36, 0x7E, 0x7E, 0x30, 0x30, 0x30, 0x7E, 0x30, 0x30, 0x30, 0x7D, 0x1F, 0x2A, 0x7E, 0x7E, 0x30, 0x30, 0x30, 0x7E, 0x30, 0x30, 0x30, 0x7E, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x7E, 0x30, 0x30, 0x30, 0x7E, 0x30, 0x30, 0x30, 0x7D, 0x20, 0x70, 0x7E, 0x7E, 0x30, 0x30, 0x30, 0x5B, 0x36
+		}
+	},
+	{
+		2,
+		79,
+		0x39,
+		0x002D,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x2D, 0x7E, 0x30, 0x30, 0x30, 0x7E, 0x30, 0x30, 0x30, 0x7E, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x40, 0x43, 0xE6, 0x02, 0x40, 0x70, 0xCF, 0x71, 0x10, 0x62, 0xE3, 0x02, 0x70, 0xCF, 0x41, 0xFF, 0xEF, 0x50, 0x80, 0x4E, 0x5D, 0xD5, 0x08, 0x62, 0x44, 0x09
+		}
+	},
+	{
+		3,
+		79,
+		0x39,
+		0x002E,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x2E, 0xD5, 0x00, 0x55, 0xFA, 0x01, 0x40, 0x50, 0x06, 0x55, 0xF8, 0x3A, 0x7C, 0x00, 0x60, 0x40, 0x40, 0x70, 0xCF, 0x71, 0x10, 0x51, 0xFA, 0x60, 0xE8, 0x70, 0xCF, 0x18, 0x60, 0xD5, 0x55, 0xF8, 0x00, 0x55, 0xF9, 0x00, 0x70, 0xCF, 0x71, 0x10, 0x41, 0x9F, 0xFE, 0x70, 0xCF, 0x62, 0xE3, 0x38, 0x62, 0xD1, 0x0F, 0x50, 0x00, 0x4E, 0x62, 0xD3, 0x0F, 0x62, 0xD0, 0x00, 0x62, 0xD5, 0x00, 0x62, 0xD4, 0x35, 0xEC
+		}
+	},
+	{
+		4,
+		79,
+		0x39,
+		0x002F,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x2F, 0x00, 0x71, 0xC0, 0x7C, 0x0F, 0x76, 0x62, 0xD0, 0x00, 0x50, 0x0F, 0x57, 0x74, 0x08, 0x28, 0x53, 0xE9, 0x18, 0x75, 0x09, 0x00, 0x28, 0x4B, 0x51, 0xE9, 0x80, 0x04, 0x75, 0x09, 0x00, 0x62, 0xE3, 0x00, 0x08, 0x28, 0x60, 0xD5, 0x74, 0xA0, 0x4B, 0x18, 0x75, 0x09, 0x00, 0x08, 0x28, 0x53, 0xE9, 0x18, 0x75, 0x09, 0x00, 0x08, 0x28, 0xA0, 0x1C, 0x53, 0xE8, 0x18, 0x75, 0x09, 0x00, 0x08, 0x28, 0xCD, 0x1D
+		}
+	},
+	{
+		5,
+		79,
+		0x39,
+		0x0030,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x30, 0x3F, 0xE9, 0x47, 0xE9, 0xFF, 0xB0, 0x06, 0x5D, 0xD5, 0x74, 0x60, 0xD5, 0x18, 0x7A, 0xE8, 0xBF, 0xEB, 0x8F, 0xC9, 0x18, 0x75, 0x09, 0x00, 0x08, 0x28, 0x53, 0xE8, 0x50, 0x00, 0x3F, 0xE9, 0x47, 0xE9, 0xFF, 0xB0, 0x08, 0x5D, 0xD5, 0x74, 0x60, 0xD5, 0x50, 0x00, 0x7A, 0xE8, 0xBF, 0xEF, 0x18, 0x8F, 0xAA, 0x18, 0x70, 0xCF, 0x71, 0x10, 0x62, 0xEC, 0x10, 0x43, 0xE3, 0x00, 0x70, 0xCF, 0x62, 0x4D, 0x1E
+		}
+	},
+	{
+		6,
+		79,
+		0x39,
+		0x0031,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x31, 0xE0, 0x00, 0x41, 0xFE, 0xE7, 0x43, 0xFE, 0x10, 0x70, 0xCF, 0x71, 0x10, 0x62, 0xE0, 0x53, 0x70, 0xCF, 0x62, 0xE2, 0x00, 0x7C, 0x3E, 0xD3, 0x8F, 0xFF, 0x7F, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xE9, 0x57
+		}
+	},
+	{
+		7,
+		79,
+		0x39,
+		0x0032,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x32, 0x5D, 0x04, 0x73, 0x21, 0xA0, 0xBF, 0xFA, 0x5D, 0x04, 0x73, 0x21, 0xA0, 0xBF, 0xF3, 0x5D, 0x04, 0x73, 0x21, 0xA0, 0xBF, 0xEC, 0x50, 0x18, 0x49, 0x04, 0x20, 0xAF, 0xE5, 0x60, 0xFF, 0x49, 0xC9, 0x01, 0xB0, 0x1A, 0x41, 0xD6, 0xFE, 0x70, 0xCF, 0x71, 0x10, 0x41, 0x04, 0x5F, 0x70, 0xCF, 0x43, 0xD6, 0x01, 0x40, 0x70, 0xCF, 0x71, 0x10, 0x43, 0x04, 0xA0, 0x70, 0xCF, 0x7F, 0x30, 0x30, 0x30, 0x81, 0x88
+		}
+	},
+	{
+		8,
+		79,
+		0x39,
+		0x0033,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x87
+		}
+	},
+	{
+		9,
+		79,
+		0x39,
+		0x0034,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x34, 0x0A, 0x20, 0x20, 0x51, 0x55, 0x41, 0x4C, 0x43, 0x4F, 0x4D, 0x4D, 0x20, 0x56, 0x50, 0x30, 0x34, 0x33, 0x2D, 0x48, 0x32, 0x20, 0x54, 0x4D, 0x41, 0x33, 0x30, 0x30, 0x45, 0x20, 0x46, 0x69, 0x72, 0x6D, 0x77, 0x61, 0x72, 0x65, 0x20, 0x49, 0x64, 0x65, 0x6E, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x42, 0x6C, 0x6F, 0x63, 0x6B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x56, 0x99, 0xBA
+		}
+	},
+	{
+		10,
+		79,
+		0x39,
+		0x0035,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x35, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x20, 0x30, 0x32, 0x2E, 0x30, 0x34, 0x2E, 0x30, 0x30, 0x20, 0x43, 0x6F, 0x6D, 0x70, 0x69, 0x6C, 0x65, 0x64, 0x20, 0x4A, 0x75, 0x6C, 0x20, 0x31, 0x34, 0x20, 0x32, 0x30, 0x31, 0x30, 0x20, 0x31, 0x32, 0x3A, 0x35, 0x33, 0x3A, 0x31, 0x33, 0x0A, 0x20, 0x20, 0x45, 0x6E, 0x64, 0x20, 0x6F, 0x66, 0x20, 0x49, 0x44, 0x20, 0x42, 0x6C, 0x6F, 0x63, 0x6B, 0x0A, 0x0D, 0xA3
+		}
+	},
+	{
+		11,
+		79,
+		0x39,
+		0x0036,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x36, 0x00, 0x03, 0x09, 0x10, 0x16, 0x06, 0x02, 0x02, 0x02, 0x01, 0xF4, 0x00, 0x0A, 0x01, 0xF4, 0x00, 0x0A, 0x01, 0xF4, 0x00, 0x0A, 0x14, 0x19, 0x19, 0x00, 0x32, 0x02, 0x14, 0x01, 0x01, 0xE0, 0x03, 0x98, 0x0C, 0x0C, 0x00, 0x10, 0x10, 0x08, 0x00, 0x04, 0x08, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x00, 0x04, 0x08, 0x00, 0x00, 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x01, 0x80, 0x10, 0x01, 0x80, 0x50, 0x2A
+		}
+	},
+	{
+		12,
+		79,
+		0x39,
+		0x0037,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x37, 0x01, 0x40, 0x04, 0x02, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x02, 0x40, 0x08, 0x80, 0x20, 0x80, 0x08, 0x04, 0x02, 0x40, 0x20, 0x23, 0x04, 0x21, 0x20, 0x22, 0x00, 0x61, 0x00, 0xFD, 0x00, 0xA0, 0x00, 0xA1, 0x00, 0xA2, 0x00, 0xA3, 0x00, 0xA8, 0x00, 0xA7, 0x00, 0x7C, 0x00, 0x7A, 0x00, 0x7B, 0x00, 0x79, 0x00, 0xCA, 0x24, 0xD6, 0x04, 0xCF, 0x00, 0xC8, 0x00, 0xA9, 0x00, 0xB7, 0x00, 0xB0, 0xB3, 0xF1
+		}
+	},
+	{
+		13,
+		79,
+		0x39,
+		0x0038,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x38, 0xCA, 0xB1, 0x0B, 0xB2, 0x00, 0xB3, 0x33, 0xB4, 0x33, 0xB5, 0x80, 0xB6, 0x00, 0x6C, 0x00, 0x6D, 0x00, 0x6E, 0x00, 0x6F, 0x00, 0xE6, 0x00, 0xE9, 0x00, 0xEC, 0x00, 0xE8, 0x20, 0xEB, 0x00, 0xEE, 0x00, 0xE7, 0x00, 0xEA, 0x00, 0xED, 0x00, 0xFF, 0x23, 0x00, 0x20, 0x20, 0x21, 0x07, 0x22, 0x40, 0x76, 0x00, 0xAF, 0x00, 0xD1, 0x00, 0xA1, 0x00, 0xD3, 0x00, 0xA3, 0x00, 0xD0, 0x00, 0xA0, 0x00, 0x69, 0x5E
+		}
+	},
+	{
+		14,
+		79,
+		0x39,
+		0x0039,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x39, 0xD2, 0x00, 0xA2, 0x00, 0xDC, 0x08, 0xE1, 0xFF, 0xE2, 0x01, 0xDF, 0xFF, 0xDE, 0x02, 0xDD, 0x00, 0x99, 0x00, 0x9C, 0x00, 0xD8, 0x00, 0xD9, 0x00, 0xDA, 0x00, 0xDB, 0x00, 0x9E, 0x00, 0xAC, 0x00, 0xFF, 0x70, 0xCF, 0x62, 0x00, 0x04, 0x70, 0xCF, 0x71, 0x10, 0x62, 0x00, 0xFF, 0x62, 0x01, 0xF6, 0x70, 0xCF, 0x62, 0x02, 0x00, 0x62, 0x01, 0x00, 0x62, 0x04, 0xAB, 0x70, 0xCF, 0x71, 0x10, 0x62, 0xF2, 0x71
+		}
+	},
+	{
+		15,
+		79,
+		0x39,
+		0x003A,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x3A, 0x04, 0xEF, 0x62, 0x05, 0xFC, 0x70, 0xCF, 0x62, 0x06, 0x00, 0x62, 0x05, 0x00, 0x62, 0x08, 0x04, 0x70, 0xCF, 0x71, 0x10, 0x62, 0x08, 0xFF, 0x62, 0x09, 0x8F, 0x70, 0xCF, 0x62, 0x0A, 0x00, 0x62, 0x09, 0x00, 0x62, 0x0C, 0x00, 0x70, 0xCF, 0x71, 0x10, 0x62, 0x0C, 0xFF, 0x62, 0x0D, 0xFF, 0x70, 0xCF, 0x62, 0x0E, 0x00, 0x62, 0x0D, 0x00, 0x62, 0x10, 0x00, 0x70, 0xCF, 0x71, 0x10, 0x62, 0x10, 0xD6, 0x3A
+		}
+	},
+	{
+		16,
+		79,
+		0x39,
+		0x003B,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x3B, 0xFF, 0x62, 0x11, 0xEF, 0x70, 0xCF, 0x62, 0x12, 0x00, 0x62, 0x11, 0x00, 0x70, 0xCF, 0x7F, 0xC1, 0x00, 0xC2, 0x00, 0xC3, 0x00, 0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0xA8, 0x00, 0xA9, 0x00, 0xAA, 0x00, 0x84, 0x00, 0x85, 0x00, 0x86, 0x00, 0x87, 0x00, 0xAB, 0x00, 0xAC, 0x00, 0xAD, 0x00, 0x88, 0x00, 0x89, 0x00, 0x8A, 0x00, 0x8B, 0x00, 0xAE, 0x00, 0xAF, 0x00, 0xB0, 0x00, 0x8C, 0x65, 0x59
+		}
+	},
+	{
+		17,
+		79,
+		0x39,
+		0x003C,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x3C, 0x00, 0x8D, 0x00, 0x8E, 0x00, 0x8F, 0x00, 0xB1, 0x00, 0xB2, 0x00, 0xB3, 0x00, 0x90, 0x00, 0x91, 0x00, 0x92, 0x00, 0x93, 0x00, 0xB4, 0x00, 0xB5, 0x00, 0xB6, 0x00, 0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, 0xB7, 0x00, 0xB8, 0x00, 0xB9, 0x00, 0x98, 0x00, 0x99, 0x00, 0x9A, 0x00, 0x9B, 0x00, 0xBA, 0x00, 0xBB, 0x00, 0xBC, 0x00, 0x9C, 0x00, 0x9D, 0x00, 0x9E, 0x00, 0x9F, 0x00, 0xBD, 0x6D, 0x6A
+		}
+	},
+	{
+		18,
+		79,
+		0x39,
+		0x003D,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x3D, 0x00, 0xBE, 0x00, 0xBF, 0x00, 0xA4, 0x00, 0xC0, 0x00, 0xFF, 0x11, 0x06, 0x12, 0x02, 0x13, 0x87, 0x14, 0x03, 0x1B, 0x30, 0x1C, 0x00, 0x19, 0x24, 0x1A, 0x30, 0x0A, 0x3C, 0x0B, 0x3C, 0xFF, 0x01, 0x02, 0x06, 0x00, 0x01, 0x02, 0x01, 0x00, 0x02, 0x00, 0x02, 0x01, 0x02, 0x00, 0x01, 0x01, 0x02, 0x00, 0x02, 0x01, 0x00, 0x73, 0x97, 0x55, 0xAE, 0x04, 0x55, 0xAF, 0xAB, 0x55, 0xB0, 0x04, 0x55, 0x6F, 0x6F
+		}
+	},
+	{
+		19,
+		79,
+		0x39,
+		0x003E,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x3E, 0xB1, 0x00, 0x55, 0xB2, 0x00, 0x7C, 0x0F, 0x8C, 0x7C, 0x0E, 0x61, 0x7F, 0x10, 0x70, 0xCF, 0x50, 0x00, 0x08, 0x50, 0x0D, 0x57, 0xD5, 0x7C, 0x0F, 0xBF, 0x18, 0x50, 0x01, 0x08, 0x50, 0x0E, 0x57, 0x28, 0x7C, 0x0F, 0xBF, 0x18, 0x50, 0x02, 0x08, 0x50, 0x0E, 0x57, 0xCF, 0x7C, 0x0F, 0xBF, 0x18, 0x50, 0x03, 0x08, 0x50, 0x0F, 0x57, 0x4A, 0x7C, 0x0F, 0xBF, 0x18, 0x70, 0xCF, 0x20, 0x7F, 0x38, 0x76, 0x7E
+		}
+	},
+	{
+		20,
+		79,
+		0x39,
+		0x003F,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x3F, 0x02, 0x10, 0x08, 0x4F, 0x52, 0xF9, 0x64, 0x08, 0x64, 0x03, 0x00, 0x54, 0xFC, 0x18, 0x18, 0x20, 0x70, 0xCF, 0x62, 0xE3, 0x00, 0x10, 0x08, 0x28, 0x39, 0xFF, 0xA0, 0x30, 0x4F, 0x54, 0xFD, 0x52, 0xFC, 0x39, 0x00, 0xA0, 0x13, 0x11, 0x06, 0xE0, 0x01, 0x70, 0xCF, 0x71, 0x10, 0x80, 0x09, 0x70, 0xCF, 0x71, 0x20, 0x80, 0x03, 0x71, 0x30, 0x18, 0x20, 0x75, 0x09, 0x00, 0x10, 0x08, 0x28, 0x4F, 0x47, 0x21
+		}
+	},
+	{
+		21,
+		79,
+		0x39,
+		0x0040,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x40, 0x59, 0xFD, 0x61, 0x00, 0x18, 0x20, 0x75, 0x09, 0x00, 0x8F, 0xC6, 0x38, 0xFC, 0x70, 0x3F, 0x71, 0xC0, 0x7F, 0x08, 0x10, 0x5D, 0xD0, 0x08, 0x5D, 0xD3, 0x08, 0x5D, 0xD4, 0x08, 0x5D, 0xD5, 0x08, 0x70, 0x3F, 0x71, 0x80, 0x62, 0xD0, 0x00, 0x18, 0x60, 0xD5, 0x18, 0x60, 0xD4, 0x18, 0x60, 0xD3, 0x18, 0x60, 0xD0, 0x20, 0x18, 0x7E, 0x08, 0x51, 0x54, 0x04, 0x01, 0x51, 0x53, 0x0C, 0x00, 0x51, 0xB4, 0xFC
+		}
+	},
+	{
+		22,
+		79,
+		0x39,
+		0x0041,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x41, 0x54, 0x04, 0x03, 0x51, 0x53, 0x0C, 0x02, 0x51, 0x54, 0x04, 0x05, 0x51, 0x53, 0x0C, 0x04, 0x51, 0x54, 0x04, 0x07, 0x51, 0x53, 0x0C, 0x06, 0x18, 0x08, 0x51, 0x0B, 0x04, 0x0D, 0x51, 0x0A, 0x0C, 0x0C, 0x41, 0x23, 0xFE, 0x55, 0xBB, 0x00, 0x51, 0x55, 0x60, 0x21, 0x62, 0xDB, 0xFE, 0x43, 0x23, 0x01, 0x18, 0x7E, 0x05, 0x62, 0xD0, 0x00, 0x51, 0x42, 0x08, 0x26, 0x42, 0xEF, 0x7C, 0x19, 0x73, 0xD7, 0x43
+		}
+	},
+	{
+		23,
+		79,
+		0x39,
+		0x0042,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x42, 0x7C, 0x19, 0x73, 0x62, 0xD0, 0x00, 0x18, 0x53, 0x42, 0x70, 0xBF, 0x57, 0x98, 0x62, 0xD3, 0x05, 0x52, 0x00, 0x73, 0x54, 0x00, 0x62, 0xD3, 0x05, 0x54, 0x00, 0x79, 0xDF, 0xF1, 0x7C, 0x19, 0x64, 0x7C, 0x19, 0x64, 0x70, 0xBF, 0x57, 0x98, 0x62, 0xD3, 0x05, 0x52, 0x00, 0x62, 0xD3, 0x08, 0x54, 0x00, 0x62, 0xD3, 0x07, 0x56, 0x00, 0x00, 0x79, 0xDF, 0xEE, 0x70, 0x3F, 0x71, 0xC0, 0x7F, 0x7F, 0x12, 0xBA
+		}
+	},
+	{
+		24,
+		79,
+		0x39,
+		0x0043,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x43, 0x5C, 0x51, 0x41, 0xE0, 0x01, 0x80, 0x13, 0x80, 0x08, 0x80, 0x01, 0x5B, 0x9F, 0xF1, 0x80, 0x77, 0x62, 0xD3, 0x05, 0x51, 0x57, 0x54, 0x00, 0x80, 0x6E, 0x62, 0xD3, 0x05, 0x51, 0x57, 0x73, 0x53, 0x46, 0x47, 0x42, 0x07, 0xB0, 0x05, 0x54, 0x00, 0x80, 0x15, 0x47, 0x42, 0x04, 0xA0, 0x10, 0x62, 0xD3, 0x05, 0x3B, 0x00, 0xA0, 0x09, 0xC0, 0x04, 0x78, 0x80, 0x02, 0x74, 0x54, 0x00, 0x62, 0xD3, 0xA3, 0xDD
+		}
+	},
+	{
+		25,
+		79,
+		0x39,
+		0x0044,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x44, 0x08, 0x13, 0x00, 0xD0, 0x0E, 0x62, 0xD3, 0x02, 0x56, 0x00, 0x00, 0x3C, 0x0E, 0x00, 0xB0, 0x37, 0x80, 0x16, 0x62, 0xD3, 0x02, 0x08, 0x11, 0x05, 0xD0, 0x03, 0x50, 0x00, 0x54, 0x00, 0x18, 0x3A, 0x15, 0xD0, 0x24, 0x51, 0x0E, 0xB0, 0x20, 0x62, 0xD3, 0x08, 0x52, 0x00, 0x53, 0x45, 0x51, 0x47, 0x12, 0x45, 0x1E, 0x45, 0x00, 0x62, 0xD3, 0x07, 0x03, 0x00, 0x0E, 0x45, 0x00, 0x54, 0x00, 0x51, 0x53, 0x3E
+		}
+	},
+	{
+		26,
+		79,
+		0x39,
+		0x0045,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x45, 0x45, 0x62, 0xD3, 0x08, 0x54, 0x00, 0x7F, 0x7C, 0x21, 0xD2, 0x08, 0x18, 0x7F, 0x50, 0xFF, 0x3C, 0x10, 0x80, 0xC0, 0x11, 0x34, 0x12, 0x76, 0x12, 0x34, 0x11, 0x0E, 0x11, 0x00, 0x34, 0x10, 0x0E, 0x10, 0x00, 0x53, 0x0F, 0x50, 0x00, 0x53, 0x48, 0x53, 0x49, 0x53, 0x4A, 0x53, 0x4B, 0x55, 0x46, 0x18, 0x65, 0x12, 0x6B, 0x11, 0x6B, 0x10, 0x6B, 0x4B, 0x6B, 0x4A, 0x6B, 0x49, 0x51, 0x4B, 0x1A, 0xFD, 0x93
+		}
+	},
+	{
+		27,
+		79,
+		0x39,
+		0x0046,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x46, 0x14, 0x51, 0x4A, 0x1A, 0x13, 0x51, 0x49, 0x19, 0x00, 0xC0, 0x0D, 0x53, 0x49, 0x51, 0x14, 0x14, 0x4B, 0x51, 0x13, 0x1C, 0x4A, 0x76, 0x12, 0x7A, 0x46, 0xBF, 0xD7, 0x50, 0xFF, 0x3C, 0x0F, 0x80, 0xC0, 0x11, 0x34, 0x12, 0x76, 0x12, 0x34, 0x11, 0x0E, 0x11, 0x00, 0x34, 0x10, 0x0E, 0x10, 0x00, 0x34, 0x0F, 0x7F, 0x50, 0x00, 0x53, 0x48, 0x53, 0x49, 0x53, 0x4A, 0x53, 0x4B, 0x51, 0x12, 0x04, 0xCE, 0x36
+		}
+	},
+	{
+		28,
+		79,
+		0x39,
+		0x0047,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x47, 0x49, 0x51, 0x11, 0x0C, 0x48, 0x90, 0x54, 0x90, 0x52, 0x51, 0x12, 0x04, 0x49, 0x51, 0x11, 0x0C, 0x48, 0x90, 0x48, 0x90, 0x46, 0x51, 0x12, 0x04, 0x49, 0x51, 0x11, 0x0C, 0x48, 0x90, 0x3C, 0x90, 0x3A, 0x51, 0x12, 0x04, 0x49, 0x51, 0x11, 0x0C, 0x48, 0x90, 0x30, 0x90, 0x2E, 0x51, 0x12, 0x04, 0x49, 0x51, 0x11, 0x0C, 0x48, 0x90, 0x24, 0x90, 0x22, 0x51, 0x12, 0x04, 0x49, 0x51, 0x11, 0x0C, 0xA3, 0xE1
+		}
+	},
+	{
+		29,
+		79,
+		0x39,
+		0x0048,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x48, 0x48, 0x90, 0x18, 0x90, 0x16, 0x51, 0x12, 0x04, 0x49, 0x51, 0x11, 0x0C, 0x48, 0x90, 0x0C, 0x51, 0x12, 0x04, 0x49, 0x51, 0x11, 0x0C, 0x48, 0x90, 0x02, 0x90, 0x00, 0x70, 0xFB, 0x6E, 0x48, 0x6E, 0x49, 0x6E, 0x4A, 0x6E, 0x4B, 0x7F, 0x50, 0x00, 0x53, 0x48, 0x53, 0x49, 0x53, 0x4A, 0x53, 0x4B, 0x9F, 0xE9, 0x51, 0x12, 0x04, 0x49, 0x51, 0x11, 0x0C, 0x48, 0x9F, 0xDF, 0x51, 0x12, 0x04, 0x49, 0xC6, 0x28
+		}
+	},
+	{
+		30,
+		79,
+		0x39,
+		0x0049,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x49, 0x51, 0x11, 0x0C, 0x48, 0x9F, 0xD5, 0x51, 0x12, 0x04, 0x49, 0x51, 0x11, 0x0C, 0x48, 0x9F, 0xCB, 0x51, 0x12, 0x04, 0x49, 0x51, 0x11, 0x0C, 0x48, 0x9F, 0xC1, 0x9F, 0xBF, 0x9F, 0xBD, 0x9F, 0xBB, 0x9F, 0xB9, 0x51, 0x12, 0x04, 0x49, 0x51, 0x11, 0x0C, 0x48, 0x9F, 0xAF, 0x51, 0x12, 0x04, 0x49, 0x51, 0x11, 0x0C, 0x48, 0x9F, 0xA5, 0x9F, 0xA3, 0x51, 0x12, 0x04, 0x49, 0x51, 0x11, 0x0C, 0x48, 0x32, 0x01
+		}
+	},
+	{
+		31,
+		79,
+		0x39,
+		0x004A,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x4A, 0x9F, 0x99, 0x51, 0x12, 0x04, 0x49, 0x51, 0x11, 0x0C, 0x48, 0x9F, 0x8F, 0x9F, 0x8D, 0x8F, 0x8C, 0x70, 0xBF, 0x62, 0xD0, 0x00, 0x53, 0x44, 0x55, 0x0F, 0x80, 0x55, 0x10, 0x60, 0x55, 0x11, 0x00, 0x62, 0xD3, 0x02, 0x50, 0x10, 0x57, 0x98, 0x54, 0x00, 0x79, 0xDF, 0xFC, 0x62, 0xD3, 0x01, 0x51, 0x0F, 0x57, 0x1A, 0x54, 0xA0, 0x79, 0xDF, 0xFC, 0x55, 0x3D, 0x00, 0x7C, 0x17, 0x18, 0x55, 0x45, 0x6E, 0x7A
+		}
+	},
+	{
+		32,
+		79,
+		0x39,
+		0x004B,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x4B, 0x00, 0x62, 0xD3, 0x01, 0x58, 0xA1, 0x51, 0x10, 0x54, 0xC5, 0x62, 0xD3, 0x01, 0x58, 0xA1, 0x51, 0x0F, 0x54, 0xA0, 0x55, 0x4A, 0x80, 0x52, 0xC5, 0x70, 0xCF, 0x71, 0x20, 0x60, 0xA5, 0x70, 0xCF, 0x62, 0xD3, 0x01, 0x58, 0xA1, 0x52, 0xA0, 0x60, 0xFD, 0x55, 0x4B, 0x10, 0x7C, 0x1B, 0x87, 0x51, 0xA0, 0x01, 0x00, 0x5C, 0x62, 0xD3, 0x02, 0x51, 0x45, 0x7C, 0x19, 0x8F, 0x43, 0xA4, 0x08, 0x47, 0x37, 0x0D
+		}
+	},
+	{
+		33,
+		79,
+		0x39,
+		0x004C,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x4C, 0x9F, 0x01, 0xA0, 0x03, 0x71, 0x01, 0x70, 0xCF, 0x7C, 0x1C, 0x8E, 0x7C, 0x1C, 0xE3, 0x55, 0x56, 0x00, 0x55, 0x57, 0xFF, 0x55, 0x48, 0x07, 0x62, 0xD3, 0x00, 0x58, 0x48, 0x3D, 0x70, 0x00, 0xA0, 0x2A, 0x52, 0x68, 0x08, 0x51, 0x48, 0x64, 0x5C, 0x52, 0x59, 0x20, 0x62, 0xD3, 0x02, 0x3A, 0x44, 0xD0, 0x06, 0x51, 0x4B, 0x73, 0x25, 0x00, 0x51, 0x4B, 0x67, 0x2D, 0x00, 0x52, 0x00, 0x3A, 0x56, 0x92, 0xC4
+		}
+	},
+	{
+		34,
+		79,
+		0x39,
+		0x004D,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x4D, 0xC0, 0x03, 0x53, 0x56, 0x3A, 0x57, 0xD0, 0x03, 0x53, 0x57, 0x7A, 0x48, 0xDF, 0xCA, 0x68, 0x4B, 0xDF, 0x9B, 0x51, 0x4A, 0xA0, 0x42, 0x47, 0x11, 0x01, 0xB0, 0x3D, 0x58, 0xA1, 0x62, 0xD3, 0x01, 0x51, 0x57, 0x02, 0x56, 0x39, 0x1F, 0xA0, 0x30, 0xD0, 0x06, 0x51, 0x4A, 0x73, 0x25, 0xA0, 0x51, 0x4A, 0x67, 0x21, 0x7F, 0x2D, 0xA0, 0x68, 0x4A, 0x26, 0x4A, 0x7F, 0x55, 0x48, 0x07, 0x62, 0xD3, 0xBE, 0x1D
+		}
+	},
+	{
+		35,
+		79,
+		0x39,
+		0x004E,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x4E, 0x00, 0x58, 0x48, 0x3D, 0x70, 0x00, 0xA0, 0x0A, 0x52, 0x68, 0x5C, 0x62, 0xD3, 0x02, 0x56, 0x00, 0x10, 0x7A, 0x48, 0xDF, 0xEA, 0x8F, 0x4A, 0x47, 0x11, 0x02, 0xB0, 0x32, 0x3C, 0x56, 0x1F, 0xC0, 0x2D, 0x62, 0xD3, 0x01, 0x58, 0xA1, 0x3D, 0xC5, 0x40, 0xA0, 0x23, 0x17, 0xC5, 0x20, 0x55, 0x48, 0x07, 0x62, 0xD3, 0x00, 0x62, 0xD3, 0x00, 0x58, 0x48, 0x3D, 0x70, 0x00, 0xA0, 0x0A, 0x52, 0x68, 0xD7, 0x50
+		}
+	},
+	{
+		36,
+		79,
+		0x39,
+		0x004F,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x4F, 0x5C, 0x62, 0xD3, 0x02, 0x56, 0x00, 0x10, 0x7A, 0x48, 0xDF, 0xEA, 0x8E, 0xFE, 0x76, 0xA1, 0x51, 0x45, 0x7C, 0x1A, 0xE9, 0x76, 0x45, 0x3C, 0x45, 0x03, 0xCE, 0xE7, 0x7C, 0x17, 0x55, 0x76, 0x3D, 0x3C, 0x3D, 0x09, 0xCE, 0xD7, 0x70, 0x3F, 0x71, 0xC0, 0x7F, 0x70, 0xBF, 0x62, 0xD0, 0x00, 0x55, 0x44, 0x99, 0x53, 0x0F, 0x5A, 0x10, 0x55, 0x11, 0x03, 0x8E, 0xA6, 0x70, 0xBF, 0x62, 0xD0, 0x00, 0xAE, 0xFF
+		}
+	},
+	{
+		37,
+		79,
+		0x39,
+		0x0050,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x50, 0x55, 0x44, 0x99, 0x53, 0x0F, 0x55, 0x10, 0x60, 0x55, 0x11, 0x01, 0x8E, 0x94, 0x70, 0xBF, 0x62, 0xD0, 0x00, 0x55, 0x44, 0x99, 0x55, 0x0F, 0x80, 0x53, 0x10, 0x55, 0x11, 0x02, 0x8E, 0x82, 0x90, 0x11, 0x55, 0x38, 0x03, 0x51, 0x38, 0x90, 0x1A, 0x76, 0x38, 0x3C, 0x38, 0x0A, 0xCF, 0xF6, 0x90, 0x66, 0x7F, 0x62, 0xD5, 0x02, 0x62, 0xD0, 0x00, 0x55, 0x34, 0x99, 0x55, 0x0E, 0x00, 0x55, 0x33, 0x76, 0x90
+		}
+	},
+	{
+		38,
+		79,
+		0x39,
+		0x0051,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x51, 0x01, 0x7F, 0x70, 0xBF, 0x62, 0xD3, 0x02, 0x62, 0xD5, 0x02, 0x62, 0xD0, 0x00, 0x11, 0x02, 0x53, 0x45, 0x51, 0x33, 0x02, 0x4C, 0x53, 0x33, 0x53, 0x32, 0x55, 0x44, 0x01, 0x58, 0x32, 0x52, 0x00, 0x3A, 0x16, 0xC0, 0x24, 0x3B, 0x12, 0xC0, 0x20, 0x3B, 0x11, 0xC0, 0x1C, 0x3B, 0x10, 0xC0, 0x18, 0x3B, 0x01, 0xC0, 0x14, 0x78, 0x3B, 0xFF, 0xC0, 0x0F, 0x3B, 0xF0, 0xC0, 0x0B, 0x3B, 0xEF, 0xC0, 0x6C, 0x7D
+		}
+	},
+	{
+		39,
+		79,
+		0x39,
+		0x0052,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x52, 0x07, 0x3B, 0xEE, 0xC0, 0x03, 0x91, 0x84, 0x76, 0x32, 0x76, 0x44, 0x51, 0x4C, 0x78, 0x3A, 0x44, 0xBF, 0xCB, 0x70, 0x3F, 0x71, 0xC0, 0x7F, 0x70, 0xBF, 0x62, 0xD3, 0x02, 0x62, 0xD5, 0x02, 0x62, 0xD0, 0x00, 0x55, 0x32, 0x01, 0x55, 0x44, 0x01, 0x55, 0x45, 0x00, 0x58, 0x32, 0x52, 0x00, 0x3A, 0x16, 0xC0, 0x18, 0x3B, 0x12, 0xC0, 0x14, 0x3B, 0x11, 0xC0, 0x10, 0x3B, 0x10, 0xC0, 0x0C, 0x3B, 0x06, 0xB2
+		}
+	},
+	{
+		40,
+		79,
+		0x39,
+		0x0053,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x53, 0x01, 0xC0, 0x08, 0x78, 0x3B, 0xFF, 0xC0, 0x03, 0x91, 0x41, 0x76, 0x32, 0x76, 0x44, 0x51, 0x4C, 0x78, 0x3A, 0x44, 0xBF, 0xD7, 0x55, 0x44, 0x01, 0x51, 0x4D, 0x78, 0x53, 0x45, 0x51, 0x4E, 0x12, 0x4C, 0x74, 0x74, 0x53, 0x32, 0x58, 0x32, 0x52, 0x00, 0x3A, 0x16, 0xC0, 0x18, 0x3B, 0x01, 0xC0, 0x14, 0x78, 0x3B, 0xFF, 0xC0, 0x0F, 0x3B, 0xF0, 0xC0, 0x0B, 0x3B, 0xEF, 0xC0, 0x07, 0x3B, 0xEE, 0xF2, 0x8B
+		}
+	},
+	{
+		41,
+		79,
+		0x39,
+		0x0054,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x54, 0xC0, 0x03, 0x91, 0x07, 0x76, 0x32, 0x76, 0x44, 0x51, 0x4C, 0x78, 0x3A, 0x44, 0xBF, 0xD7, 0x51, 0x4C, 0x78, 0x53, 0x44, 0x55, 0x45, 0x01, 0x02, 0x4C, 0x53, 0x32, 0x58, 0x32, 0x52, 0x00, 0x3A, 0x16, 0xC0, 0x18, 0x3B, 0x11, 0xC0, 0x14, 0x3B, 0x10, 0xC0, 0x10, 0x78, 0x3B, 0xFF, 0xC0, 0x0B, 0x3B, 0xEF, 0xC0, 0x07, 0x3B, 0xEE, 0xC0, 0x03, 0x90, 0xD1, 0x51, 0x4C, 0x04, 0x32, 0x76, 0x45, 0x88, 0xB8
+		}
+	},
+	{
+		42,
+		79,
+		0x39,
+		0x0055,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x55, 0x51, 0x4D, 0x78, 0x3A, 0x45, 0xBF, 0xD5, 0x55, 0x44, 0x00, 0x55, 0x45, 0x01, 0x51, 0x4C, 0x53, 0x32, 0x58, 0x32, 0x52, 0x00, 0x3A, 0x16, 0xC0, 0x18, 0x3B, 0x12, 0xC0, 0x14, 0x3B, 0x11, 0xC0, 0x10, 0x3B, 0x01, 0xC0, 0x0C, 0x78, 0x3B, 0xF0, 0xC0, 0x07, 0x3B, 0xEF, 0xC0, 0x03, 0x90, 0x9B, 0x51, 0x4C, 0x04, 0x32, 0x76, 0x45, 0x51, 0x4D, 0x78, 0x3A, 0x45, 0xBF, 0xD5, 0x50, 0x00, 0x53, 0xA4, 0xF1
+		}
+	},
+	{
+		43,
+		79,
+		0x39,
+		0x0056,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x56, 0x44, 0x53, 0x45, 0x55, 0x32, 0x00, 0x5C, 0x52, 0x00, 0x3A, 0x16, 0xC0, 0x0F, 0x3B, 0x12, 0xC0, 0x0B, 0x3B, 0x11, 0xC0, 0x07, 0x3B, 0x01, 0xC0, 0x03, 0x90, 0x70, 0x55, 0x44, 0x00, 0x51, 0x4D, 0x78, 0x53, 0x45, 0x51, 0x4E, 0x12, 0x4C, 0x74, 0x53, 0x32, 0x5C, 0x52, 0x00, 0x3A, 0x16, 0xC0, 0x10, 0x3B, 0x01, 0xC0, 0x0C, 0x78, 0x3B, 0xF0, 0xC0, 0x07, 0x3B, 0xEF, 0xC0, 0x03, 0x90, 0x4B, 0x9F, 0xE8
+		}
+	},
+	{
+		44,
+		79,
+		0x39,
+		0x0057,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x57, 0x55, 0x45, 0x00, 0x51, 0x4C, 0x78, 0x53, 0x44, 0x53, 0x32, 0x5C, 0x52, 0x00, 0x3A, 0x16, 0xC0, 0x10, 0x3B, 0x11, 0xC0, 0x0C, 0x3B, 0x10, 0xC0, 0x08, 0x78, 0x3B, 0xFF, 0xC0, 0x03, 0x90, 0x2B, 0x51, 0x4D, 0x53, 0x45, 0x51, 0x4C, 0x53, 0x44, 0x51, 0x4E, 0x7A, 0x44, 0x7A, 0x45, 0x53, 0x32, 0x5C, 0x52, 0x00, 0x3A, 0x16, 0xC0, 0x10, 0x78, 0x3B, 0xFF, 0xC0, 0x0B, 0x3B, 0xEF, 0xC0, 0x07, 0x3B, 0x21
+		}
+	},
+	{
+		45,
+		79,
+		0x39,
+		0x0058,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x58, 0x3B, 0xEE, 0xC0, 0x03, 0x90, 0x05, 0x70, 0x3F, 0x71, 0xC0, 0x7F, 0x51, 0x43, 0x01, 0x03, 0x3A, 0x0E, 0xC0, 0x0F, 0x51, 0x45, 0x3F, 0x34, 0x51, 0x44, 0x3F, 0x34, 0x51, 0x32, 0x3F, 0x34, 0x76, 0x0E, 0x7F, 0x84, 0x88, 0x8C, 0x90, 0x94, 0x98, 0x9C, 0x9C, 0x9C, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x20, 0x40, 0x62, 0xD0, 0x00, 0x55, 0x4C, 0x11, 0x55, 0x4D, 0x09, 0x55, 0x4E, 0x98, 0xAB, 0x02
+		}
+	},
+	{
+		46,
+		79,
+		0x39,
+		0x0059,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x59, 0x55, 0x4F, 0x03, 0x55, 0x50, 0x97, 0x55, 0x51, 0x01, 0x55, 0x52, 0xDF, 0x55, 0x15, 0x08, 0x55, 0x16, 0x08, 0x55, 0x17, 0x08, 0x55, 0x42, 0x1C, 0x55, 0x43, 0x04, 0x55, 0xA2, 0x00, 0x55, 0xA3, 0x00, 0x55, 0xA4, 0x48, 0x55, 0xA5, 0x04, 0x55, 0xA6, 0x08, 0x55, 0xA9, 0x01, 0x55, 0xA7, 0x0C, 0x55, 0xA8, 0x05, 0x55, 0x18, 0x04, 0x55, 0xAD, 0x02, 0x55, 0x40, 0x00, 0x55, 0x3F, 0x00, 0x51, 0xE1, 0x6F
+		}
+	},
+	{
+		47,
+		79,
+		0x39,
+		0x005A,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x5A, 0xA9, 0xA0, 0x08, 0x51, 0xA7, 0x58, 0xA8, 0x7C, 0x18, 0xF9, 0x70, 0xBF, 0x62, 0xD3, 0x01, 0x57, 0x3F, 0x50, 0x09, 0x28, 0x54, 0xA0, 0x79, 0xDF, 0xF9, 0x70, 0x3F, 0x71, 0xC0, 0x5D, 0xFC, 0x70, 0xCF, 0x71, 0x10, 0x62, 0x76, 0x07, 0x43, 0xE2, 0x08, 0x70, 0xCF, 0x71, 0x20, 0x62, 0xA4, 0x01, 0x62, 0xC0, 0x00, 0x39, 0x04, 0xD0, 0x04, 0x43, 0xC8, 0x04, 0x7C, 0x19, 0x41, 0x70, 0xCF, 0x71, 0x3B, 0x24
+		}
+	},
+	{
+		48,
+		79,
+		0x39,
+		0x005B,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x5B, 0x20, 0x43, 0x81, 0x0E, 0x43, 0x85, 0x0E, 0x43, 0x89, 0x0E, 0x43, 0x8D, 0x0E, 0x43, 0x91, 0x0E, 0x43, 0x95, 0x0E, 0x43, 0x99, 0x0E, 0x43, 0x9D, 0x0E, 0x70, 0xCF, 0x55, 0x9A, 0x07, 0x55, 0x9C, 0x02, 0x55, 0x9E, 0x06, 0x55, 0x9D, 0x00, 0x50, 0x48, 0x57, 0x00, 0x7C, 0x18, 0xB7, 0x71, 0x30, 0x62, 0x1B, 0x40, 0x70, 0xCF, 0x62, 0xA2, 0x10, 0x7C, 0x2A, 0xD8, 0x50, 0x04, 0x7C, 0x18, 0xE7, 0x6B, 0x85
+		}
+	},
+	{
+		49,
+		79,
+		0x39,
+		0x005C,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x5C, 0x70, 0xCF, 0x7C, 0x19, 0x4E, 0x7F, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x00, 0x03, 0x06, 0x09, 0x0C, 0x0F, 0x12, 0x15, 0x18, 0x62, 0xD0, 0x00, 0x70, 0xCF, 0x71, 0x10, 0x51, 0x3D, 0xF0, 0x60, 0x5C, 0x51, 0x3D, 0xF0, 0x75, 0x73, 0x53, 0x09, 0x5E, 0x00, 0x22, 0x09, 0x61, 0x00, 0x70, 0xCF, 0x71, 0x20, 0x51, 0x3D, 0xFE, 0xE9, 0x5C, 0x51, 0x3D, 0xFE, 0xED, 0x53, 0x09, 0xBF, 0x2E
+		}
+	},
+	{
+		50,
+		79,
+		0x39,
+		0x005D,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x5D, 0x5E, 0x00, 0x2A, 0x09, 0x61, 0x00, 0x70, 0xCF, 0x51, 0x3D, 0xFF, 0xBA, 0x53, 0xA0, 0x51, 0x3D, 0xFF, 0xBD, 0x53, 0xA1, 0x7F, 0x70, 0xCF, 0x71, 0x20, 0x51, 0x3D, 0xFE, 0xC5, 0x5C, 0x51, 0x3D, 0xFE, 0xC9, 0x73, 0x53, 0x09, 0x5E, 0x00, 0x22, 0x09, 0x61, 0x00, 0x70, 0xCF, 0x71, 0x10, 0x51, 0x3D, 0xF0, 0x10, 0x5C, 0x51, 0x3D, 0xF0, 0x25, 0x53, 0x09, 0x5E, 0x00, 0x2A, 0x09, 0x61, 0x00, 0x4E, 0x4D
+		}
+	},
+	{
+		51,
+		79,
+		0x39,
+		0x005E,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x5E, 0x70, 0xCF, 0x7F, 0x0C, 0x0C, 0x00, 0x10, 0x10, 0x08, 0x00, 0x04, 0x08, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x00, 0x04, 0x08, 0x00, 0x00, 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x01, 0x80, 0x10, 0x01, 0x80, 0x01, 0x40, 0x04, 0x02, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x02, 0x40, 0x08, 0x80, 0x20, 0x80, 0x08, 0x04, 0x02, 0x40, 0x20, 0x62, 0xD0, 0x00, 0x55, 0x9D, 0x00, 0x51, 0xA6, 0x91, 0x11, 0xD4
+		}
+	},
+	{
+		52,
+		79,
+		0x39,
+		0x005F,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x5F, 0x76, 0x51, 0xA4, 0x58, 0xA3, 0x7C, 0x18, 0xB7, 0x51, 0xA5, 0x7C, 0x18, 0xE7, 0x70, 0xCF, 0x71, 0x20, 0x50, 0x00, 0x60, 0x80, 0x60, 0x84, 0x60, 0x88, 0x60, 0x8C, 0x60, 0x90, 0x60, 0x94, 0x60, 0x98, 0x60, 0x9C, 0x60, 0x82, 0x60, 0x86, 0x60, 0x8A, 0x60, 0x8E, 0x60, 0x92, 0x60, 0x96, 0x60, 0x9A, 0x60, 0x9E, 0x60, 0xC0, 0x43, 0x81, 0x04, 0x43, 0x85, 0x04, 0x43, 0x89, 0x04, 0x43, 0x8D, 0x86, 0xBF
+		}
+	},
+	{
+		53,
+		79,
+		0x39,
+		0x0060,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x60, 0x04, 0x43, 0x91, 0x04, 0x43, 0x95, 0x04, 0x43, 0x99, 0x04, 0x43, 0x9D, 0x04, 0x71, 0x30, 0x71, 0x30, 0x62, 0x1F, 0x00, 0x62, 0x1B, 0x70, 0x62, 0x13, 0x87, 0x70, 0xCF, 0x71, 0x10, 0x55, 0x09, 0x19, 0x51, 0x09, 0xFF, 0x5E, 0x5C, 0x51, 0x09, 0xFF, 0x73, 0x53, 0x45, 0x5E, 0x00, 0x2A, 0x45, 0x61, 0x00, 0x7A, 0x09, 0xDF, 0xEC, 0x70, 0xCF, 0x41, 0xA2, 0x3F, 0x55, 0x40, 0x00, 0x55, 0x3F, 0xDC, 0x6C
+		}
+	},
+	{
+		54,
+		79,
+		0x39,
+		0x0061,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x61, 0x00, 0x51, 0xA9, 0xA0, 0x08, 0x51, 0xA7, 0x58, 0xA8, 0x7C, 0x18, 0xF9, 0x7F, 0x41, 0xE0, 0xFB, 0x71, 0x30, 0x41, 0x1B, 0xBF, 0x70, 0xCF, 0x71, 0x20, 0x41, 0x81, 0xFD, 0x41, 0x85, 0xFD, 0x41, 0x89, 0xFD, 0x41, 0x8D, 0xFD, 0x41, 0x91, 0xFD, 0x41, 0x95, 0xFD, 0x41, 0x99, 0xFD, 0x41, 0x9D, 0xFD, 0x70, 0xCF, 0x41, 0xA2, 0xEF, 0x7C, 0x19, 0x5A, 0x70, 0xCF, 0x71, 0x10, 0x41, 0xE2, 0xF7, 0x90, 0xD5
+		}
+	},
+	{
+		55,
+		79,
+		0x39,
+		0x0062,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x62, 0x70, 0xCF, 0x7F, 0x7C, 0x19, 0x41, 0x70, 0xCF, 0x71, 0x10, 0x43, 0xE2, 0x08, 0x71, 0x30, 0x43, 0x1B, 0x40, 0x70, 0xCF, 0x71, 0x20, 0x43, 0x81, 0x02, 0x43, 0x85, 0x02, 0x43, 0x89, 0x02, 0x43, 0x8D, 0x02, 0x43, 0x91, 0x02, 0x43, 0x95, 0x02, 0x43, 0x99, 0x02, 0x43, 0x9D, 0x02, 0x70, 0xCF, 0x43, 0xA2, 0x10, 0x7C, 0x19, 0x4E, 0x7F, 0x62, 0xD0, 0x00, 0x53, 0x49, 0x5A, 0x48, 0x53, 0x9B, 0x24, 0xFE
+		}
+	},
+	{
+		56,
+		79,
+		0x39,
+		0x0063,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x63, 0x5B, 0x21, 0x01, 0xA0, 0x06, 0x2E, 0x9E, 0x01, 0x80, 0x04, 0x26, 0x9E, 0xFE, 0x68, 0x48, 0x6E, 0x49, 0x51, 0x49, 0x78, 0x70, 0xCF, 0x71, 0x20, 0x60, 0xC9, 0x70, 0xCF, 0x7C, 0x2A, 0xBA, 0x7F, 0x00, 0x04, 0x0C, 0x1C, 0x3C, 0x7C, 0xFC, 0x62, 0xD0, 0x00, 0x53, 0x3E, 0x76, 0x3E, 0xFF, 0xF0, 0x26, 0x9C, 0x03, 0x2C, 0x9C, 0x7C, 0x2A, 0xBA, 0x7F, 0x62, 0xD0, 0x00, 0x53, 0xA7, 0x5A, 0xA8, 0xA6, 0x03
+		}
+	},
+	{
+		57,
+		79,
+		0x39,
+		0x0064,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x64, 0x90, 0x01, 0x7F, 0x62, 0xD0, 0x00, 0x08, 0x5A, 0x3E, 0x55, 0x40, 0xFF, 0x55, 0x3F, 0x01, 0x78, 0xA0, 0x0A, 0x06, 0x40, 0xFF, 0x0E, 0x3F, 0x01, 0x78, 0xBF, 0xF8, 0x51, 0x3E, 0x68, 0x3F, 0x6E, 0x40, 0x78, 0xDF, 0xFA, 0x16, 0x40, 0x7F, 0x1E, 0x3F, 0x00, 0x18, 0x78, 0x64, 0x64, 0x26, 0x9C, 0x03, 0x2C, 0x9C, 0x7C, 0x2A, 0xBA, 0x7F, 0x62, 0xD0, 0x00, 0x78, 0x53, 0x9A, 0x7C, 0x2A, 0xBA, 0x11, 0xDA
+		}
+	},
+	{
+		58,
+		79,
+		0x39,
+		0x0065,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x65, 0x7F, 0x70, 0xCF, 0x71, 0x20, 0x62, 0xA7, 0x89, 0x62, 0xA7, 0x49, 0x70, 0xCF, 0x7F, 0x70, 0xCF, 0x71, 0x20, 0x49, 0xC8, 0x08, 0xAF, 0xFC, 0x70, 0xCF, 0x7F, 0x70, 0xCF, 0x71, 0x20, 0x62, 0xA7, 0x09, 0x70, 0xCF, 0x7F, 0x70, 0xBF, 0x62, 0xD0, 0x00, 0x55, 0x41, 0x00, 0x93, 0xA8, 0x70, 0x3F, 0x71, 0xC0, 0x7F, 0x70, 0xBF, 0x62, 0xD0, 0x00, 0x55, 0x41, 0x02, 0x93, 0x99, 0x70, 0x3F, 0x71, 0xB9, 0x2B
+		}
+	},
+	{
+		59,
+		79,
+		0x39,
+		0x0066,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x66, 0xC0, 0x7F, 0x57, 0x98, 0x50, 0x0A, 0x28, 0x21, 0xE0, 0xB0, 0x04, 0x79, 0xDF, 0xF7, 0x7F, 0x70, 0xCF, 0x71, 0x10, 0x64, 0xE0, 0x01, 0x80, 0x09, 0x80, 0x75, 0x80, 0xE1, 0x81, 0x3D, 0x81, 0x49, 0x41, 0x00, 0xFD, 0x41, 0x0C, 0xF7, 0x41, 0x0C, 0xBF, 0x41, 0x00, 0x7F, 0x41, 0x10, 0xF7, 0x41, 0x10, 0xBF, 0x70, 0xCF, 0x71, 0x20, 0x5D, 0xF7, 0x53, 0x9F, 0x70, 0xFE, 0x43, 0x80, 0x04, 0x52, 0xE2, 0x7E
+		}
+	},
+	{
+		60,
+		79,
+		0x39,
+		0x0067,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x67, 0x06, 0x60, 0x83, 0x06, 0x68, 0x06, 0x2E, 0x70, 0x01, 0x43, 0x84, 0x08, 0x52, 0x03, 0x60, 0x87, 0x06, 0x69, 0x03, 0x2E, 0x71, 0x01, 0x43, 0x88, 0x04, 0x52, 0x00, 0x60, 0x8B, 0x06, 0x6A, 0x00, 0x2E, 0x72, 0x01, 0x43, 0x8C, 0x02, 0x52, 0x09, 0x60, 0x8F, 0x06, 0x6B, 0x09, 0x2E, 0x73, 0x01, 0x43, 0x90, 0x01, 0x52, 0x0C, 0x60, 0x93, 0x06, 0x6C, 0x0C, 0x2E, 0x74, 0x01, 0x43, 0x94, 0x02, 0x8C, 0xD3
+		}
+	},
+	{
+		61,
+		79,
+		0x39,
+		0x0068,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x68, 0x52, 0x0F, 0x60, 0x97, 0x06, 0x6D, 0x0F, 0x2E, 0x75, 0x01, 0x43, 0xA3, 0x3F, 0x7F, 0x41, 0x04, 0xBF, 0x41, 0x0C, 0xFB, 0x41, 0x0C, 0xDF, 0x41, 0x00, 0xDF, 0x41, 0x10, 0xFB, 0x41, 0x10, 0xDF, 0x70, 0xCF, 0x71, 0x20, 0x5D, 0xF7, 0x53, 0x9F, 0x70, 0xFE, 0x43, 0x80, 0x02, 0x52, 0x07, 0x60, 0x83, 0x06, 0x68, 0x07, 0x2E, 0x70, 0x01, 0x43, 0x84, 0x04, 0x52, 0x04, 0x60, 0x87, 0x06, 0x69, 0x46, 0x48
+		}
+	},
+	{
+		62,
+		79,
+		0x39,
+		0x0069,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x69, 0x04, 0x2E, 0x71, 0x01, 0x43, 0x88, 0x02, 0x52, 0x01, 0x60, 0x8B, 0x06, 0x6A, 0x01, 0x2E, 0x72, 0x01, 0x43, 0x8C, 0x04, 0x52, 0x0A, 0x60, 0x8F, 0x06, 0x6B, 0x0A, 0x2E, 0x73, 0x01, 0x43, 0x90, 0x02, 0x52, 0x0D, 0x60, 0x93, 0x06, 0x6C, 0x0D, 0x2E, 0x74, 0x01, 0x43, 0x94, 0x04, 0x52, 0x10, 0x60, 0x97, 0x06, 0x6D, 0x10, 0x2E, 0x75, 0x01, 0x43, 0xA3, 0x3F, 0x7F, 0x41, 0x08, 0xF7, 0x41, 0xC5, 0x47
+		}
+	},
+	{
+		63,
+		79,
+		0x39,
+		0x006A,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x6A, 0x0C, 0xFD, 0x41, 0x0C, 0xEF, 0x41, 0x08, 0x7F, 0x41, 0x10, 0xFD, 0x70, 0xCF, 0x71, 0x20, 0x5D, 0xF7, 0x53, 0x9F, 0x70, 0xFE, 0x43, 0x80, 0x01, 0x52, 0x08, 0x60, 0x83, 0x06, 0x68, 0x08, 0x2E, 0x70, 0x01, 0x43, 0x84, 0x02, 0x52, 0x05, 0x60, 0x87, 0x06, 0x69, 0x05, 0x2E, 0x71, 0x01, 0x43, 0x88, 0x01, 0x52, 0x02, 0x60, 0x8B, 0x06, 0x6A, 0x02, 0x2E, 0x72, 0x01, 0x43, 0x8C, 0x08, 0x52, 0x57, 0x6C
+		}
+	},
+	{
+		64,
+		79,
+		0x39,
+		0x006B,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x6B, 0x0B, 0x60, 0x8F, 0x06, 0x6B, 0x0B, 0x2E, 0x73, 0x01, 0x43, 0x90, 0x04, 0x52, 0x0E, 0x60, 0x93, 0x06, 0x6C, 0x0E, 0x2E, 0x74, 0x01, 0x43, 0xA3, 0x1F, 0x7F, 0x70, 0xCF, 0x71, 0x20, 0x5D, 0xF7, 0x53, 0x9F, 0x70, 0xFE, 0x43, 0xA3, 0x00, 0x7F, 0x7F, 0x70, 0xCF, 0x71, 0x10, 0x64, 0xE0, 0x01, 0x80, 0x09, 0x80, 0x34, 0x80, 0x5F, 0x80, 0x84, 0x80, 0x8B, 0x43, 0x00, 0x02, 0x43, 0x0C, 0x08, 0x1D, 0xF9
+		}
+	},
+	{
+		65,
+		79,
+		0x39,
+		0x006C,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x6C, 0x43, 0x0C, 0x40, 0x43, 0x00, 0x80, 0x43, 0x10, 0x08, 0x43, 0x10, 0x40, 0x70, 0xCF, 0x71, 0x20, 0x41, 0x80, 0xFB, 0x41, 0x84, 0xF7, 0x41, 0x88, 0xFB, 0x41, 0x8C, 0xFD, 0x41, 0x90, 0xFE, 0x41, 0x94, 0xFD, 0x62, 0xA3, 0x00, 0x80, 0x5E, 0x43, 0x04, 0x40, 0x43, 0x0C, 0x04, 0x43, 0x0C, 0x20, 0x43, 0x00, 0x20, 0x43, 0x10, 0x04, 0x43, 0x10, 0x20, 0x70, 0xCF, 0x71, 0x20, 0x41, 0x80, 0xFD, 0x9E, 0xFC
+		}
+	},
+	{
+		66,
+		79,
+		0x39,
+		0x006D,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x6D, 0x41, 0x84, 0xFB, 0x41, 0x88, 0xFD, 0x41, 0x8C, 0xFB, 0x41, 0x90, 0xFD, 0x41, 0x94, 0xFB, 0x62, 0xA3, 0x00, 0x80, 0x31, 0x43, 0x08, 0x08, 0x43, 0x0C, 0x02, 0x43, 0x0C, 0x10, 0x43, 0x08, 0x80, 0x43, 0x10, 0x02, 0x70, 0xCF, 0x71, 0x20, 0x41, 0x80, 0xFE, 0x41, 0x84, 0xFD, 0x41, 0x88, 0xFE, 0x41, 0x8C, 0xF7, 0x41, 0x90, 0xFB, 0x62, 0xA3, 0x00, 0x80, 0x0A, 0x70, 0xCF, 0x71, 0x20, 0x62, 0x2E, 0x1D
+		}
+	},
+	{
+		67,
+		79,
+		0x39,
+		0x006E,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x6E, 0xA3, 0x00, 0x80, 0x01, 0x70, 0xCF, 0x7F, 0x62, 0xD3, 0x00, 0x57, 0x07, 0x52, 0x70, 0x54, 0x80, 0x52, 0x68, 0x54, 0x78, 0x51, 0xA0, 0x56, 0x70, 0x00, 0x54, 0x68, 0x79, 0xDF, 0xEF, 0x7F, 0x62, 0xD5, 0x00, 0x62, 0xD3, 0x00, 0x58, 0xA0, 0x55, 0x09, 0x88, 0x50, 0x0A, 0x28, 0x21, 0x1F, 0x3F, 0x09, 0x75, 0x3C, 0x09, 0x99, 0xCF, 0xF4, 0x7F, 0x62, 0xD0, 0x00, 0x55, 0xAA, 0x00, 0x55, 0xAC, 0xE5, 0x8C
+		}
+	},
+	{
+		68,
+		79,
+		0x39,
+		0x006F,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x6F, 0x00, 0x57, 0x07, 0x62, 0xD3, 0x00, 0x5B, 0x3D, 0x80, 0x00, 0xA0, 0x5A, 0x10, 0x64, 0x5C, 0x52, 0x58, 0x53, 0x56, 0x52, 0x59, 0x53, 0x57, 0x51, 0x3E, 0x6E, 0x56, 0x6E, 0x57, 0x78, 0xDF, 0xFA, 0x51, 0x40, 0x14, 0x57, 0x51, 0x3F, 0x1C, 0x56, 0xA0, 0x0E, 0xD0, 0x06, 0x55, 0x57, 0x00, 0x80, 0x04, 0x55, 0x57, 0xFF, 0x55, 0x56, 0x00, 0x20, 0x10, 0x52, 0x78, 0x5C, 0x62, 0xD3, 0x02, 0x51, 0x6C, 0x9B
+		}
+	},
+	{
+		69,
+		79,
+		0x39,
+		0x0070,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x70, 0x57, 0x73, 0x54, 0x00, 0x39, 0xDC, 0xD0, 0x05, 0x39, 0x23, 0xD0, 0x04, 0x55, 0xAB, 0x01, 0x62, 0xD3, 0x08, 0x13, 0x00, 0xC0, 0x07, 0x39, 0x0F, 0xD0, 0x0B, 0x80, 0x05, 0x39, 0xF1, 0xC0, 0x05, 0x04, 0xAA, 0x76, 0xAC, 0x20, 0x79, 0xDF, 0x9C, 0x51, 0xAC, 0x47, 0xAA, 0x80, 0xA0, 0x03, 0x76, 0xAA, 0x68, 0xAA, 0x39, 0x02, 0xC0, 0x18, 0x47, 0xAA, 0x80, 0xA0, 0x03, 0x76, 0xAA, 0x68, 0xAA, 0x67, 0x92
+		}
+	},
+	{
+		70,
+		79,
+		0x39,
+		0x0071,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x71, 0x67, 0x39, 0x02, 0xC0, 0x0A, 0x47, 0xAA, 0x80, 0xA0, 0x03, 0x76, 0xAA, 0x68, 0xAA, 0x57, 0x07, 0x62, 0xD3, 0x00, 0x3D, 0x80, 0x00, 0xA0, 0x33, 0x10, 0x52, 0x78, 0x5C, 0x62, 0xD3, 0x02, 0x52, 0x00, 0x53, 0x47, 0x47, 0x42, 0x10, 0xA0, 0x1B, 0x51, 0xAA, 0x15, 0x00, 0xD0, 0x0B, 0x47, 0xAA, 0x80, 0xB0, 0x0E, 0x56, 0x00, 0x00, 0x80, 0x09, 0x47, 0xAA, 0x80, 0xA0, 0x04, 0x56, 0x00, 0xFF, 0xE5, 0x8F
+		}
+	},
+	{
+		71,
+		79,
+		0x39,
+		0x0072,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x72, 0x52, 0x00, 0x73, 0x53, 0x57, 0x5B, 0x7C, 0x10, 0xC0, 0x20, 0x79, 0xDF, 0xC4, 0x7F, 0x62, 0xD0, 0x00, 0x70, 0xCF, 0x71, 0x20, 0x49, 0xC4, 0x01, 0xAF, 0xFC, 0x41, 0xA4, 0xF7, 0x41, 0xC4, 0xFE, 0x5D, 0xA8, 0x53, 0x59, 0x5D, 0xA9, 0x53, 0x58, 0x5D, 0xAB, 0x53, 0x5B, 0x5D, 0xAC, 0x53, 0x5A, 0x5D, 0xAE, 0x53, 0x5D, 0x5D, 0xAF, 0x53, 0x5C, 0x5D, 0xB1, 0x53, 0x5F, 0x5D, 0xB2, 0x53, 0x5E, 0x2F, 0x24
+		}
+	},
+	{
+		72,
+		79,
+		0x39,
+		0x0073,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x73, 0x5D, 0xB4, 0x53, 0x61, 0x5D, 0xB5, 0x53, 0x60, 0x5D, 0xB7, 0x53, 0x63, 0x5D, 0xB8, 0x53, 0x62, 0x5D, 0xBA, 0x53, 0x65, 0x5D, 0xBB, 0x53, 0x64, 0x5D, 0xBD, 0x53, 0x67, 0x5D, 0xBE, 0x53, 0x66, 0x70, 0xCF, 0x7F, 0x62, 0xD3, 0x00, 0x57, 0x07, 0x5B, 0x3D, 0x70, 0x00, 0xA0, 0x25, 0x10, 0x64, 0x5C, 0x51, 0x3E, 0x6F, 0x58, 0x6F, 0x59, 0x78, 0xDF, 0xFA, 0x51, 0x40, 0x15, 0x59, 0x51, 0x3F, 0x50, 0x67
+		}
+	},
+	{
+		73,
+		79,
+		0x39,
+		0x0074,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x74, 0x1D, 0x58, 0xA0, 0x0E, 0xD0, 0x06, 0x56, 0x59, 0x00, 0x80, 0x04, 0x56, 0x59, 0xFF, 0x56, 0x58, 0x00, 0x20, 0x79, 0xDF, 0xD4, 0x7F, 0x55, 0x3D, 0x00, 0x55, 0x37, 0x01, 0x7C, 0x17, 0x18, 0x9E, 0x7E, 0x58, 0xA1, 0x62, 0xD3, 0x01, 0x52, 0xA0, 0x60, 0xFD, 0x52, 0xC5, 0x70, 0xCF, 0x71, 0x20, 0x60, 0xA5, 0x70, 0xCF, 0x9E, 0x51, 0x50, 0x00, 0x57, 0x88, 0x9C, 0x53, 0x43, 0xA4, 0x08, 0x47, 0x25, 0x12
+		}
+	},
+	{
+		74,
+		79,
+		0x39,
+		0x0075,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x75, 0x9F, 0x01, 0xA0, 0x03, 0x71, 0x01, 0x70, 0xCF, 0x51, 0x3D, 0xA0, 0x05, 0x9E, 0x6A, 0x51, 0x3D, 0x9F, 0x3C, 0x50, 0x00, 0x9D, 0x93, 0x62, 0xD3, 0x01, 0x58, 0xA1, 0x52, 0xA1, 0x60, 0xFD, 0x52, 0xC6, 0x70, 0xCF, 0x71, 0x20, 0x60, 0xA5, 0x70, 0xCF, 0x9E, 0x1C, 0x50, 0x01, 0x57, 0x88, 0x9C, 0x1E, 0x43, 0xA4, 0x08, 0x47, 0x9F, 0x01, 0xA0, 0x03, 0x71, 0x01, 0x70, 0xCF, 0x9E, 0x39, 0x9F, 0x07, 0xD7
+		}
+	},
+	{
+		75,
+		79,
+		0x39,
+		0x0076,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x76, 0x0D, 0x50, 0x01, 0x9D, 0x64, 0x62, 0xD3, 0x01, 0x58, 0xA1, 0x52, 0xA2, 0x60, 0xFD, 0x52, 0xC7, 0x70, 0xCF, 0x71, 0x20, 0x60, 0xA5, 0x70, 0xCF, 0x9D, 0xED, 0x50, 0x02, 0x57, 0x88, 0x9B, 0xEF, 0x43, 0xA4, 0x08, 0x47, 0x9F, 0x01, 0xA0, 0x03, 0x71, 0x01, 0x70, 0xCF, 0x9E, 0x0A, 0x9E, 0xDE, 0x50, 0x02, 0x9D, 0x35, 0x7C, 0x17, 0x55, 0x76, 0x3D, 0x3C, 0x3D, 0x09, 0xCF, 0x5F, 0x62, 0xD3, 0x43, 0x50
+		}
+	},
+	{
+		76,
+		79,
+		0x39,
+		0x0077,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x77, 0x00, 0x9D, 0xC4, 0x9D, 0xF3, 0x7C, 0x14, 0x1F, 0x55, 0x37, 0x00, 0x7F, 0x43, 0xE0, 0x08, 0x7F, 0x41, 0xE0, 0xF7, 0x7F, 0x62, 0xE6, 0x04, 0x62, 0xD0, 0x00, 0x5A, 0x53, 0x53, 0x54, 0x10, 0x08, 0x51, 0x55, 0x08, 0x38, 0x03, 0x4F, 0x50, 0x00, 0x54, 0xFE, 0x54, 0xFD, 0x01, 0x08, 0x54, 0xFF, 0x48, 0xFC, 0x01, 0xA0, 0x09, 0x52, 0xFB, 0x05, 0xFE, 0x52, 0xFA, 0x0D, 0xFD, 0x6F, 0xFD, 0x6F, 0xCC, 0x63
+		}
+	},
+	{
+		77,
+		79,
+		0x39,
+		0x0078,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x78, 0xFE, 0x6F, 0xFC, 0x7B, 0xFF, 0xBF, 0xEA, 0x52, 0xFC, 0x60, 0xE8, 0x52, 0xFE, 0x60, 0xE7, 0x62, 0xE6, 0x00, 0x62, 0xE6, 0x01, 0x38, 0xFA, 0x70, 0x3F, 0x71, 0xC0, 0x7F, 0x5D, 0xF7, 0x08, 0x70, 0xFE, 0x57, 0xF0, 0x50, 0x00, 0x62, 0xE6, 0x04, 0x62, 0xE8, 0x01, 0x62, 0xE7, 0x00, 0x62, 0xE6, 0x00, 0x62, 0xE6, 0x01, 0x62, 0xDA, 0xF7, 0x49, 0xDA, 0x08, 0xAF, 0xFC, 0x62, 0xDA, 0xF7, 0x08, 0xF1, 0xAE
+		}
+	},
+	{
+		78,
+		79,
+		0x39,
+		0x0079,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x79, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x51, 0x00, 0x18, 0x49, 0xDA, 0x08, 0xB0, 0x04, 0x40, 0x80, 0x05, 0x74, 0x62, 0xDA, 0xF7, 0x79, 0xBF, 0xE0, 0x49, 0xDA, 0x08, 0xA0, 0x02, 0x74, 0x62, 0xE6, 0x04, 0x60, 0xE8, 0x62, 0xE7, 0x00, 0x62, 0xE6, 0x00, 0x62, 0xE6, 0x01, 0x62, 0xD0, 0x00, 0x53, 0x55, 0x55, 0x53, 0x00, 0x55, 0x54, 0x01, 0x7E, 0x55, 0x77
+		}
+	},
+	{
+		79,
+		79,
+		0x39,
+		0x007A,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x7A, 0x5D, 0xF7, 0x08, 0x70, 0xFE, 0x62, 0xD0, 0x00, 0x51, 0x05, 0x58, 0x04, 0x7E, 0x5D, 0xF7, 0x08, 0x70, 0xFE, 0x62, 0xD0, 0x00, 0x51, 0x01, 0x58, 0x00, 0x7E, 0x5D, 0xF7, 0x08, 0x70, 0xFE, 0x62, 0xD0, 0x00, 0x51, 0x03, 0x58, 0x02, 0x7E, 0x5D, 0xF7, 0x08, 0x70, 0xFE, 0x62, 0xD0, 0x00, 0x51, 0x07, 0x58, 0x06, 0x7E, 0x08, 0x08, 0x10, 0x4F, 0x5D, 0xF7, 0x54, 0xFD, 0x70, 0x3F, 0x71, 0xC0, 0x9C, 0x06
+		}
+	},
+	{
+		80,
+		79,
+		0x39,
+		0x007B,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x7B, 0x20, 0x18, 0x70, 0xFE, 0x62, 0xD0, 0x00, 0x53, 0x05, 0x5A, 0x04, 0x7E, 0x08, 0x08, 0x10, 0x4F, 0x5D, 0xF7, 0x54, 0xFD, 0x70, 0x3F, 0x71, 0xC0, 0x20, 0x18, 0x70, 0xFE, 0x62, 0xD0, 0x00, 0x53, 0x01, 0x5A, 0x00, 0x7E, 0x08, 0x08, 0x10, 0x4F, 0x5D, 0xF7, 0x54, 0xFD, 0x70, 0x3F, 0x71, 0xC0, 0x20, 0x18, 0x70, 0xFE, 0x62, 0xD0, 0x00, 0x53, 0x03, 0x5A, 0x02, 0x7E, 0x0E, 0x1E, 0x3D, 0x7A, 0xE3, 0x95
+		}
+	},
+	{
+		81,
+		79,
+		0x39,
+		0x007C,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x7C, 0x07, 0x03, 0x00, 0x00, 0x07, 0x0E, 0x1E, 0x3D, 0x03, 0x01, 0x00, 0x00, 0x1E, 0x3D, 0x7A, 0xF6, 0x0E, 0x07, 0x01, 0x00, 0x58, 0x45, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x79, 0xDF, 0xF6, 0x7A, 0x44, 0xBF, 0xF0, 0x70, 0x3F, 0x71, 0xC0, 0x7F, 0x08, 0x10, 0x70, 0x3F, 0x71, 0x80, 0x5D, 0xD3, 0x08, 0x5D, 0xD0, 0x08, 0x62, 0xD0, 0x00, 0x51, 0xB6, 0x60, 0xD3, 0x2E, 0xB3, 0x80, 0x48, 0x60
+		}
+	},
+	{
+		82,
+		79,
+		0x39,
+		0x007D,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x7D, 0x49, 0xD7, 0x08, 0xA0, 0x09, 0x26, 0xB3, 0xF0, 0x2E, 0xB3, 0x00, 0x80, 0x08, 0x49, 0xD7, 0x20, 0xA0, 0x03, 0x80, 0xA6, 0x51, 0xB3, 0x21, 0x0E, 0xE0, 0x01, 0x80, 0x11, 0x80, 0x67, 0x80, 0x79, 0x80, 0x47, 0x80, 0x96, 0x80, 0x94, 0x80, 0x92, 0x80, 0x90, 0x80, 0x97, 0x5D, 0xD8, 0x21, 0xFE, 0x39, 0x48, 0xA0, 0x06, 0x62, 0xD7, 0x00, 0x80, 0x8A, 0x49, 0xD8, 0x01, 0xB0, 0x0F, 0x55, 0xBA, 0x69, 0xA3
+		}
+	},
+	{
+		83,
+		79,
+		0x39,
+		0x007E,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x7E, 0x02, 0x26, 0xB3, 0xF0, 0x2E, 0xB3, 0x02, 0x62, 0xD7, 0x10, 0x80, 0x77, 0x55, 0xBA, 0x01, 0x26, 0xB3, 0xF0, 0x2E, 0xB3, 0x06, 0x5F, 0xB5, 0xB4, 0x51, 0xB7, 0x02, 0xB5, 0x5C, 0x52, 0x00, 0x60, 0xD8, 0x76, 0xB5, 0x62, 0xD7, 0x14, 0x80, 0x5B, 0x51, 0xB8, 0x78, 0x3A, 0xB5, 0xC0, 0x0F, 0x51, 0xB7, 0x02, 0xB5, 0x5C, 0x52, 0x00, 0x60, 0xD8, 0x76, 0xB5, 0x2E, 0xB3, 0x20, 0x60, 0xD8, 0x62, 0x18, 0x02
+		}
+	},
+	{
+		84,
+		79,
+		0x39,
+		0x007F,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x7F, 0xD7, 0x04, 0x80, 0x3F, 0x5D, 0xD8, 0x3A, 0xB8, 0xD0, 0x2B, 0xA0, 0x29, 0x53, 0xB5, 0x53, 0xB4, 0x26, 0xB3, 0xF0, 0x2E, 0xB3, 0x04, 0x80, 0x18, 0x51, 0xB9, 0x78, 0x3A, 0xB5, 0xC0, 0x16, 0x51, 0xB7, 0x02, 0xB5, 0x5C, 0x5D, 0xD8, 0x54, 0x00, 0x2E, 0xB3, 0x10, 0x76, 0xB5, 0x80, 0x01, 0x62, 0xD7, 0x10, 0x80, 0x0F, 0x62, 0xD7, 0x00, 0x80, 0x0A, 0x26, 0xB3, 0xF0, 0x2E, 0xB3, 0x00, 0x55, 0xFC, 0xCB
+		}
+	},
+	{
+		85,
+		79,
+		0x39,
+		0x0080,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x80, 0xBA, 0x00, 0x18, 0x60, 0xD0, 0x18, 0x60, 0xD3, 0x20, 0x18, 0x7E, 0x62, 0xD0, 0x00, 0x70, 0xCF, 0x71, 0x10, 0x41, 0x04, 0x5F, 0x43, 0x05, 0xA0, 0x70, 0xCF, 0x26, 0xAF, 0x5F, 0x51, 0xAF, 0x60, 0x04, 0x55, 0xBA, 0x00, 0x90, 0x1F, 0x90, 0x24, 0x40, 0x40, 0x40, 0x40, 0x40, 0x50, 0x00, 0x53, 0xB4, 0x70, 0xCF, 0x71, 0x10, 0x43, 0x04, 0xA0, 0x43, 0x05, 0xA0, 0x70, 0xCF, 0x2E, 0xAF, 0xA0, 0xAC, 0x2C
+		}
+	},
+	{
+		86,
+		79,
+		0x39,
+		0x0081,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x81, 0x51, 0xAF, 0x60, 0x04, 0x7F, 0x41, 0xE0, 0x7F, 0x43, 0xE0, 0x80, 0x7F, 0x43, 0xD6, 0x31, 0x7F, 0x41, 0xE0, 0x7F, 0x41, 0xD6, 0xFE, 0x7F, 0x62, 0xD0, 0x00, 0x4F, 0x52, 0xFD, 0x53, 0xB8, 0x52, 0xFC, 0x53, 0xB9, 0x52, 0xFB, 0x53, 0xB7, 0x52, 0xFA, 0x53, 0xB6, 0x70, 0x3F, 0x71, 0xC0, 0x7F, 0x76, 0xBB, 0xD0, 0x04, 0x55, 0xBB, 0xFF, 0x7E, 0x43, 0xE1, 0x01, 0x7F, 0x41, 0xE1, 0xFE, 0x7F, 0xB7, 0x43
+		}
+	},
+	{
+		87,
+		79,
+		0x39,
+		0x0082,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x82, 0x43, 0x23, 0x01, 0x7F, 0x54, 0x00, 0x70, 0xFE, 0x41, 0x23, 0xFE, 0x18, 0x60, 0x22, 0x18, 0x60, 0x23, 0x18, 0x70, 0x3F, 0x71, 0xC0, 0x7E, 0x30, 0x62, 0xD0, 0x00, 0x53, 0xF8, 0x5D, 0xF7, 0x08, 0x21, 0xC0, 0xB0, 0x07, 0x56, 0x01, 0x00, 0x55, 0xF8, 0x00, 0x51, 0xF8, 0x70, 0x3F, 0x71, 0x80, 0x60, 0xD3, 0x55, 0xFD, 0x01, 0x3C, 0xFD, 0x01, 0xB0, 0xAE, 0x70, 0xCF, 0x71, 0x10, 0x5D, 0xE0, 0xFE, 0xD2
+		}
+	},
+	{
+		88,
+		79,
+		0x39,
+		0x0083,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x83, 0x08, 0x21, 0xF8, 0x49, 0xFE, 0x08, 0xB0, 0x0A, 0x49, 0xFE, 0x10, 0xB0, 0x09, 0x29, 0x01, 0x80, 0x07, 0x29, 0x02, 0x80, 0x03, 0x29, 0x00, 0x60, 0xE0, 0x70, 0xCF, 0x80, 0x01, 0x65, 0xFD, 0x3C, 0xFD, 0x02, 0xB0, 0x84, 0x65, 0xFD, 0x70, 0xCF, 0x71, 0x10, 0x49, 0xE4, 0x08, 0xA0, 0x05, 0x70, 0xCF, 0x80, 0x20, 0x70, 0xCF, 0x52, 0x00, 0x53, 0xFA, 0x51, 0xFD, 0x39, 0x04, 0xB0, 0x69, 0x08, 0xF8, 0xC7
+		}
+	},
+	{
+		89,
+		79,
+		0x39,
+		0x0084,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x84, 0x10, 0x50, 0x03, 0x55, 0xF8, 0x3A, 0x7C, 0x00, 0x60, 0x20, 0x18, 0x53, 0xFD, 0x3C, 0xF8, 0x00, 0xA0, 0x09, 0x55, 0xFF, 0x00, 0x55, 0xFD, 0x10, 0x80, 0x37, 0x65, 0xFD, 0x52, 0x00, 0x53, 0xFA, 0x52, 0x02, 0x53, 0xFB, 0x52, 0x01, 0x60, 0xD4, 0x52, 0x05, 0x53, 0xFC, 0x55, 0xFE, 0x56, 0x51, 0xFD, 0x39, 0x08, 0xB0, 0x33, 0x08, 0x10, 0x50, 0x02, 0x55, 0xF8, 0x3A, 0x7C, 0x00, 0x60, 0x20, 0x70, 0xB8
+		}
+	},
+	{
+		90,
+		79,
+		0x39,
+		0x0085,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x85, 0x18, 0x53, 0xFD, 0x55, 0xFF, 0x01, 0x3C, 0xF8, 0x00, 0xA0, 0x04, 0x55, 0xFF, 0x00, 0x65, 0xFD, 0x3C, 0xFD, 0x10, 0xB0, 0x13, 0x18, 0x70, 0xCF, 0x71, 0x10, 0x60, 0xE0, 0x70, 0xCF, 0x65, 0xFD, 0x51, 0xFF, 0x3C, 0xFD, 0x20, 0xA0, 0x04, 0x30, 0x8F, 0xFE, 0x62, 0xD0, 0x00, 0x62, 0xD5, 0x00, 0x62, 0xD4, 0x00, 0x7E, 0x30, 0x30, 0x30, 0x51, 0xF8, 0x70, 0x3F, 0x71, 0x80, 0x60, 0xD3, 0x52, 0x35, 0x43
+		}
+	},
+	{
+		91,
+		79,
+		0x39,
+		0x0086,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x86, 0x02, 0x53, 0xFB, 0x52, 0x01, 0x60, 0xD5, 0x52, 0x03, 0x74, 0x53, 0xFD, 0x52, 0x04, 0x53, 0xFE, 0x50, 0x00, 0x6C, 0x00, 0x6A, 0x6C, 0x00, 0x6A, 0x6C, 0x00, 0x6A, 0x6C, 0x00, 0x6A, 0x6C, 0x00, 0x6A, 0x6C, 0x00, 0x6A, 0x6C, 0x00, 0x6A, 0x08, 0x52, 0x00, 0x5C, 0x18, 0x08, 0x28, 0x3F, 0xFB, 0x18, 0x75, 0xB0, 0x02, 0x74, 0x7A, 0xFE, 0xB0, 0x05, 0x7A, 0xFD, 0xA0, 0x0F, 0x3C, 0xFB, 0x00, 0x37, 0x48
+		}
+	},
+	{
+		92,
+		79,
+		0x39,
+		0x0087,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x87, 0xBF, 0xEB, 0x08, 0x5D, 0xD5, 0x74, 0x60, 0xD5, 0x18, 0x8F, 0xE2, 0x62, 0xD0, 0x00, 0x62, 0xD5, 0x00, 0x7E, 0x70, 0xBF, 0x62, 0xD0, 0x00, 0x47, 0x36, 0x40, 0xB0, 0x0F, 0x47, 0x36, 0x80, 0xA0, 0x0A, 0x26, 0x36, 0x3F, 0x51, 0x36, 0x3A, 0x0E, 0xA0, 0x01, 0x70, 0xBF, 0x51, 0x0E, 0xA1, 0x1A, 0x55, 0xBE, 0x00, 0x3C, 0x0E, 0x02, 0xC0, 0x04, 0x55, 0xBE, 0x01, 0x5F, 0x36, 0x0E, 0x62, 0xD4, 0xE5, 0xA5
+		}
+	},
+	{
+		93,
+		79,
+		0x39,
+		0x0088,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x88, 0x02, 0x62, 0xD5, 0x01, 0x55, 0x0E, 0x00, 0x55, 0x35, 0x99, 0x55, 0x34, 0xE0, 0x3E, 0x35, 0x53, 0x45, 0x3E, 0x35, 0x53, 0x44, 0x3E, 0x35, 0x53, 0x32, 0x3C, 0x45, 0x02, 0xC0, 0x94, 0x51, 0x4D, 0x11, 0x03, 0x3A, 0x45, 0xC0, 0x8C, 0x3C, 0x44, 0x02, 0xC0, 0x87, 0x51, 0x4C, 0x11, 0x03, 0x3A, 0x44, 0xC0, 0x7F, 0x62, 0xD3, 0x02, 0x58, 0x32, 0x52, 0xFE, 0x53, 0x23, 0x52, 0xFF, 0x53, 0x24, 0x10, 0xFC
+		}
+	},
+	{
+		94,
+		79,
+		0x39,
+		0x0089,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x89, 0x52, 0x00, 0x53, 0x25, 0x52, 0x01, 0x53, 0x26, 0x52, 0x02, 0x53, 0x27, 0x5B, 0x12, 0x4C, 0x5C, 0x52, 0xFE, 0x53, 0x1E, 0x52, 0xFF, 0x53, 0x1F, 0x52, 0x00, 0x53, 0x20, 0x52, 0x01, 0x53, 0x21, 0x52, 0x02, 0x53, 0x22, 0x5B, 0x12, 0x4C, 0x5C, 0x52, 0xFE, 0x53, 0x19, 0x52, 0xFF, 0x53, 0x1A, 0x52, 0x00, 0x53, 0x1B, 0x52, 0x01, 0x53, 0x1C, 0x52, 0x02, 0x53, 0x1D, 0x51, 0x32, 0x02, 0x4C, 0xF8, 0xCD
+		}
+	},
+	{
+		95,
+		79,
+		0x39,
+		0x008A,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x8A, 0x5C, 0x52, 0xFE, 0x53, 0x28, 0x52, 0xFF, 0x53, 0x29, 0x52, 0x00, 0x53, 0x2A, 0x52, 0x01, 0x53, 0x2B, 0x52, 0x02, 0x53, 0x2C, 0x5B, 0x02, 0x4C, 0x5C, 0x52, 0xFE, 0x53, 0x2D, 0x52, 0xFF, 0x53, 0x2E, 0x52, 0x00, 0x53, 0x2F, 0x52, 0x01, 0x53, 0x30, 0x52, 0x02, 0x53, 0x31, 0x90, 0x62, 0x80, 0x44, 0x7C, 0x25, 0x70, 0x90, 0x5B, 0x55, 0xBD, 0x00, 0x51, 0x45, 0xA0, 0x18, 0x51, 0x4D, 0x78, 0xB8, 0x4E
+		}
+	},
+	{
+		96,
+		79,
+		0x39,
+		0x008B,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x8B, 0x3A, 0x45, 0xA0, 0x11, 0x51, 0x44, 0xA0, 0x1A, 0x51, 0x4C, 0x78, 0x3A, 0x44, 0xA0, 0x13, 0x7C, 0x26, 0x0E, 0x80, 0x21, 0x51, 0x44, 0xA0, 0x17, 0x51, 0x4C, 0x78, 0x3A, 0x44, 0xA0, 0x10, 0x80, 0x11, 0x51, 0x45, 0xA0, 0x0A, 0x51, 0x4D, 0x78, 0x3A, 0x45, 0xA0, 0x03, 0x80, 0x04, 0x55, 0xBD, 0x01, 0x7C, 0x26, 0x94, 0x51, 0x0E, 0x3A, 0x43, 0xC0, 0x05, 0x50, 0xFF, 0x80, 0x0C, 0x7C, 0x23, 0x96, 0x0B
+		}
+	},
+	{
+		97,
+		79,
+		0x39,
+		0x008C,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x8C, 0x48, 0x7A, 0x36, 0x51, 0x36, 0xBF, 0x07, 0x51, 0x0E, 0x55, 0x36, 0x00, 0x70, 0x3F, 0x71, 0xC0, 0x7F, 0x55, 0x13, 0x00, 0x51, 0x1F, 0x02, 0x20, 0x0E, 0x13, 0x00, 0x02, 0x21, 0x0E, 0x13, 0x00, 0x02, 0x24, 0x0E, 0x13, 0x00, 0x02, 0x25, 0x0E, 0x13, 0x00, 0x02, 0x26, 0x0E, 0x13, 0x00, 0x02, 0x29, 0x0E, 0x13, 0x00, 0x02, 0x2A, 0x0E, 0x13, 0x00, 0x02, 0x2B, 0x0E, 0x13, 0x00, 0x3C, 0x13, 0xFB, 0xD6
+		}
+	},
+	{
+		98,
+		79,
+		0x39,
+		0x008D,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x8D, 0x00, 0xA0, 0x03, 0x50, 0xFF, 0x53, 0xBC, 0x7F, 0x3C, 0xBE, 0x01, 0xB0, 0x23, 0x50, 0x00, 0x53, 0x19, 0x53, 0x1A, 0x53, 0x1B, 0x53, 0x1C, 0x53, 0x1D, 0x53, 0x1E, 0x53, 0x22, 0x53, 0x23, 0x53, 0x27, 0x53, 0x28, 0x53, 0x2C, 0x53, 0x2D, 0x53, 0x2E, 0x53, 0x2F, 0x53, 0x30, 0x53, 0x31, 0x62, 0xD5, 0x01, 0x06, 0x34, 0x03, 0x50, 0x00, 0x53, 0x0F, 0x53, 0x10, 0x53, 0x12, 0x53, 0x13, 0x62, 0xD5, 0x8B
+		}
+	},
+	{
+		99,
+		79,
+		0x39,
+		0x008E,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x8E, 0xD3, 0x00, 0x10, 0x51, 0x31, 0x57, 0x17, 0x03, 0x19, 0x0E, 0x13, 0x00, 0x79, 0xDF, 0xF9, 0x53, 0x14, 0x20, 0x51, 0xBC, 0x80, 0x08, 0x3C, 0x13, 0x00, 0xA0, 0x03, 0x50, 0xFF, 0x3F, 0x34, 0x51, 0x31, 0x02, 0x2C, 0x0E, 0x10, 0x00, 0x02, 0x27, 0x0E, 0x10, 0x00, 0x02, 0x22, 0x0E, 0x10, 0x00, 0x02, 0x1D, 0x0E, 0x10, 0x00, 0x12, 0x19, 0x1E, 0x10, 0x00, 0x12, 0x1E, 0x1E, 0x10, 0x00, 0x12, 0x8E, 0xFE
+		}
+	},
+	{
+		100,
+		79,
+		0x39,
+		0x008F,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x8F, 0x23, 0x1E, 0x10, 0x00, 0x12, 0x28, 0x1E, 0x10, 0x00, 0x12, 0x2D, 0x1E, 0x10, 0x00, 0x64, 0x6B, 0x10, 0x02, 0x30, 0x0E, 0x10, 0x00, 0x02, 0x2B, 0x0E, 0x10, 0x00, 0x02, 0x26, 0x0E, 0x10, 0x00, 0x02, 0x21, 0x0E, 0x10, 0x00, 0x02, 0x1C, 0x0E, 0x10, 0x00, 0x12, 0x1A, 0x1E, 0x10, 0x00, 0x12, 0x1F, 0x1E, 0x10, 0x00, 0x12, 0x24, 0x1E, 0x10, 0x00, 0x12, 0x29, 0x1E, 0x10, 0x00, 0x12, 0x2E, 0x29, 0x35
+		}
+	},
+	{
+		101,
+		79,
+		0x39,
+		0x0090,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x90, 0x1E, 0x10, 0x00, 0x53, 0x11, 0x7C, 0x11, 0x4D, 0x51, 0x44, 0x06, 0x12, 0x80, 0x0C, 0x11, 0x0E, 0x10, 0x00, 0x47, 0x10, 0x80, 0xA0, 0x0A, 0x55, 0x10, 0x00, 0x55, 0x11, 0x00, 0x55, 0x12, 0x00, 0x7C, 0x12, 0x26, 0x47, 0x42, 0x08, 0xA0, 0x36, 0x62, 0xD3, 0x01, 0x4D, 0x34, 0x51, 0x48, 0x3B, 0x00, 0xC0, 0x1E, 0xB0, 0x09, 0x51, 0x49, 0x3B, 0x01, 0xA0, 0x21, 0xC0, 0x14, 0x51, 0x48, 0x3A, 0x02, 0xE8
+		}
+	},
+	{
+		102,
+		79,
+		0x39,
+		0x0091,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x91, 0x4F, 0xB0, 0x07, 0x51, 0x49, 0x3A, 0x50, 0xA0, 0x13, 0x7A, 0x49, 0x1E, 0x48, 0x00, 0x80, 0x0C, 0x51, 0x48, 0x2A, 0x49, 0xA0, 0x06, 0x76, 0x49, 0x0E, 0x48, 0x00, 0x4D, 0x34, 0x51, 0x48, 0x3A, 0x4F, 0xC0, 0x0B, 0xB0, 0x13, 0x51, 0x49, 0x3A, 0x50, 0xC0, 0x03, 0xB0, 0x0B, 0x51, 0x48, 0x3F, 0x34, 0x51, 0x49, 0x3F, 0x34, 0x80, 0x09, 0x51, 0x4F, 0x3F, 0x34, 0x51, 0x50, 0x3F, 0x34, 0x50, 0x45, 0x6F
+		}
+	},
+	{
+		103,
+		79,
+		0x39,
+		0x0092,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x92, 0x00, 0x53, 0x10, 0x53, 0x12, 0x51, 0x2D, 0x02, 0x2E, 0x0E, 0x10, 0x00, 0x02, 0x2F, 0x0E, 0x10, 0x00, 0x02, 0x30, 0x0E, 0x10, 0x00, 0x02, 0x31, 0x0E, 0x10, 0x00, 0x12, 0x19, 0x1E, 0x10, 0x00, 0x12, 0x1A, 0x1E, 0x10, 0x00, 0x12, 0x1B, 0x1E, 0x10, 0x00, 0x12, 0x1C, 0x1E, 0x10, 0x00, 0x12, 0x1D, 0x1E, 0x10, 0x00, 0x64, 0x6B, 0x10, 0x02, 0x28, 0x0E, 0x10, 0x00, 0x02, 0x29, 0x0E, 0x10, 0xBB, 0x5C
+		}
+	},
+	{
+		104,
+		79,
+		0x39,
+		0x0093,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x93, 0x00, 0x02, 0x2A, 0x0E, 0x10, 0x00, 0x02, 0x2B, 0x0E, 0x10, 0x00, 0x02, 0x2C, 0x0E, 0x10, 0x00, 0x12, 0x1E, 0x1E, 0x10, 0x00, 0x12, 0x1F, 0x1E, 0x10, 0x00, 0x12, 0x20, 0x1E, 0x10, 0x00, 0x12, 0x21, 0x1E, 0x10, 0x00, 0x12, 0x22, 0x1E, 0x10, 0x00, 0x53, 0x11, 0x7C, 0x11, 0x4D, 0x51, 0x45, 0x06, 0x12, 0x80, 0x0C, 0x11, 0x0E, 0x10, 0x00, 0x47, 0x10, 0x80, 0xA0, 0x0A, 0x55, 0x10, 0x00, 0x4E, 0x83
+		}
+	},
+	{
+		105,
+		79,
+		0x39,
+		0x0094,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x94, 0x55, 0x11, 0x00, 0x55, 0x12, 0x00, 0x7C, 0x11, 0xB3, 0x47, 0x42, 0x08, 0xA0, 0x36, 0x62, 0xD3, 0x01, 0x4D, 0x34, 0x51, 0x48, 0x3B, 0x00, 0xC0, 0x1E, 0xB0, 0x09, 0x51, 0x49, 0x3B, 0x01, 0xA0, 0x21, 0xC0, 0x14, 0x51, 0x48, 0x3A, 0x51, 0xB0, 0x07, 0x51, 0x49, 0x3A, 0x52, 0xA0, 0x13, 0x7A, 0x49, 0x1E, 0x48, 0x00, 0x80, 0x0C, 0x51, 0x48, 0x2A, 0x49, 0xA0, 0x06, 0x76, 0x49, 0x0E, 0x48, 0x31, 0x4A
+		}
+	},
+	{
+		106,
+		79,
+		0x39,
+		0x0095,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x95, 0x00, 0x4D, 0x34, 0x51, 0x48, 0x3A, 0x51, 0xC0, 0x0B, 0xB0, 0x13, 0x51, 0x49, 0x3A, 0x52, 0xC0, 0x03, 0xB0, 0x0B, 0x51, 0x48, 0x3F, 0x34, 0x51, 0x49, 0x3F, 0x34, 0x80, 0x09, 0x51, 0x51, 0x3F, 0x34, 0x51, 0x52, 0x3F, 0x34, 0x62, 0xD3, 0x02, 0x76, 0x0E, 0x51, 0x0E, 0x55, 0xBC, 0x00, 0x7F, 0x55, 0x12, 0x00, 0x5F, 0x11, 0x45, 0x06, 0x11, 0xFE, 0x5F, 0x10, 0x44, 0x06, 0x10, 0xFE, 0x51, 0x97, 0x17
+		}
+	},
+	{
+		107,
+		79,
+		0x39,
+		0x0096,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x96, 0x32, 0x08, 0x51, 0x4C, 0x14, 0x32, 0x14, 0x32, 0x16, 0x32, 0x02, 0x55, 0x0F, 0x06, 0x7A, 0x0F, 0x51, 0x0F, 0xA0, 0x74, 0x47, 0x11, 0x80, 0xB0, 0x44, 0x51, 0x4D, 0x78, 0x3A, 0x11, 0xC0, 0x3D, 0x55, 0x13, 0x06, 0x7A, 0x13, 0x51, 0x13, 0xA0, 0x4F, 0x47, 0x10, 0x80, 0xB0, 0x1E, 0x51, 0x4C, 0x78, 0x3A, 0x10, 0xC0, 0x17, 0x58, 0x32, 0x62, 0xD3, 0x02, 0x52, 0x00, 0x58, 0x12, 0x62, 0xD3, 0x19, 0x1C
+		}
+	},
+	{
+		108,
+		79,
+		0x39,
+		0x0097,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x97, 0x00, 0x54, 0x19, 0x76, 0x12, 0x76, 0x10, 0x76, 0x32, 0x8F, 0xD9, 0x58, 0x12, 0x62, 0xD3, 0x00, 0x56, 0x19, 0x00, 0x75, 0x5A, 0x12, 0x76, 0x10, 0x76, 0x32, 0x8F, 0xC8, 0x58, 0x12, 0x62, 0xD3, 0x00, 0x50, 0x00, 0x54, 0x19, 0x75, 0x54, 0x19, 0x75, 0x54, 0x19, 0x75, 0x54, 0x19, 0x75, 0x54, 0x19, 0x75, 0x5A, 0x12, 0x06, 0x32, 0x05, 0x76, 0x11, 0x5F, 0x10, 0x44, 0x06, 0x10, 0xFE, 0x51, 0xA0, 0x2B
+		}
+	},
+	{
+		109,
+		79,
+		0x39,
+		0x0098,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x98, 0x4C, 0x11, 0x05, 0x04, 0x32, 0x8F, 0x88, 0x62, 0xD3, 0x02, 0x18, 0x53, 0x32, 0x7F, 0x62, 0xD3, 0x00, 0x3C, 0x45, 0x01, 0xB0, 0x1B, 0x57, 0x05, 0x52, 0x19, 0x6D, 0x6D, 0x6D, 0x21, 0x1F, 0x53, 0x0F, 0x6D, 0x21, 0x7F, 0x02, 0x0F, 0x54, 0x14, 0x75, 0x5B, 0x39, 0x0A, 0xBF, 0xEB, 0x80, 0x21, 0x51, 0x4D, 0x11, 0x02, 0x3A, 0x45, 0xB0, 0x19, 0x57, 0x0F, 0x52, 0x19, 0x6D, 0x6D, 0x6D, 0x21, 0x4A, 0x80
+		}
+	},
+	{
+		110,
+		79,
+		0x39,
+		0x0099,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x99, 0x1F, 0x53, 0x0F, 0x6D, 0x21, 0x7F, 0x02, 0x0F, 0x54, 0x1E, 0x75, 0x5B, 0x39, 0x14, 0xBF, 0xEB, 0x3C, 0x44, 0x01, 0xB0, 0x1D, 0x57, 0x01, 0x52, 0x19, 0x6D, 0x6D, 0x6D, 0x21, 0x1F, 0x53, 0x0F, 0x6D, 0x21, 0x7F, 0x02, 0x0F, 0x54, 0x18, 0x5B, 0x01, 0x05, 0x5C, 0x39, 0x15, 0xBF, 0xE9, 0x80, 0x23, 0x51, 0x4C, 0x11, 0x02, 0x3A, 0x44, 0xB0, 0x1B, 0x57, 0x03, 0x52, 0x19, 0x6D, 0x6D, 0x6D, 0xB7, 0x5B
+		}
+	},
+	{
+		111,
+		79,
+		0x39,
+		0x009A,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x9A, 0x21, 0x1F, 0x53, 0x0F, 0x6D, 0x21, 0x7F, 0x02, 0x0F, 0x54, 0x1A, 0x5B, 0x01, 0x05, 0x5C, 0x39, 0x17, 0xBF, 0xE9, 0x7F, 0x62, 0xD3, 0x00, 0x51, 0x45, 0xB0, 0x94, 0x55, 0x19, 0x04, 0x55, 0x1A, 0x10, 0x55, 0x1B, 0x10, 0x55, 0x1C, 0x10, 0x55, 0x1D, 0x04, 0x51, 0xBD, 0xB0, 0x23, 0x51, 0x25, 0xA0, 0x0D, 0x51, 0x24, 0x5F, 0x12, 0x26, 0x5F, 0x14, 0x25, 0x92, 0x47, 0x53, 0x1B, 0x51, 0x2A, 0x43, 0x74
+		}
+	},
+	{
+		112,
+		79,
+		0x39,
+		0x009B,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x9B, 0xA0, 0x0F, 0x51, 0x29, 0x5F, 0x12, 0x2B, 0x5F, 0x14, 0x2A, 0x92, 0x37, 0x53, 0x1A, 0x53, 0x1C, 0x57, 0x04, 0x52, 0x23, 0x53, 0x4A, 0x52, 0x19, 0x53, 0x4B, 0x7C, 0x29, 0x2D, 0x50, 0x04, 0x6E, 0x4A, 0x6E, 0x4B, 0x78, 0xBF, 0xFA, 0x52, 0x28, 0x14, 0x4B, 0x1E, 0x4A, 0x00, 0x47, 0x4A, 0x80, 0xA0, 0x07, 0x55, 0x4A, 0x00, 0x55, 0x4B, 0x00, 0x47, 0x4A, 0x7F, 0xA0, 0x04, 0x55, 0x4B, 0xFF, 0xD0, 0x8F
+		}
+	},
+	{
+		113,
+		79,
+		0x39,
+		0x009C,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x9C, 0x51, 0x4B, 0x3C, 0xAD, 0x02, 0xA0, 0x0D, 0x3C, 0xAD, 0x03, 0xA0, 0x03, 0x80, 0x10, 0x6D, 0x21, 0x7F, 0x80, 0x0B, 0x6D, 0x21, 0x7F, 0x53, 0x4B, 0x6D, 0x21, 0x7F, 0x02, 0x4B, 0x54, 0x1E, 0x6D, 0x6D, 0x6D, 0x21, 0x1F, 0x54, 0x19, 0x6D, 0x21, 0x7F, 0x05, 0x19, 0x79, 0xDF, 0xA5, 0x51, 0x4D, 0x11, 0x01, 0x3A, 0x45, 0xB0, 0x94, 0x55, 0x2D, 0x04, 0x55, 0x2E, 0x10, 0x55, 0x2F, 0x10, 0x55, 0xF3, 0xD6
+		}
+	},
+	{
+		114,
+		79,
+		0x39,
+		0x009D,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x9D, 0x30, 0x10, 0x55, 0x31, 0x04, 0x51, 0xBD, 0xB0, 0x23, 0x51, 0x25, 0xA0, 0x0D, 0x51, 0x24, 0x5F, 0x12, 0x26, 0x5F, 0x14, 0x25, 0x91, 0xAC, 0x53, 0x2F, 0x51, 0x20, 0xA0, 0x0F, 0x51, 0x1F, 0x5F, 0x12, 0x21, 0x5F, 0x14, 0x20, 0x91, 0x9C, 0x53, 0x2E, 0x53, 0x30, 0x57, 0x04, 0x52, 0x23, 0x53, 0x4A, 0x52, 0x2D, 0x53, 0x4B, 0x7C, 0x29, 0x2D, 0x50, 0x04, 0x6E, 0x4A, 0x6E, 0x4B, 0x78, 0xBF, 0x6F, 0xCF
+		}
+	},
+	{
+		115,
+		79,
+		0x39,
+		0x009E,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x9E, 0xFA, 0x52, 0x1E, 0x14, 0x4B, 0x1E, 0x4A, 0x00, 0x47, 0x4A, 0x80, 0xA0, 0x07, 0x55, 0x4A, 0x00, 0x55, 0x4B, 0x00, 0x47, 0x4A, 0x7F, 0xA0, 0x04, 0x55, 0x4B, 0xFF, 0x51, 0x4B, 0x3C, 0xAD, 0x02, 0xA0, 0x0D, 0x3C, 0xAD, 0x03, 0xA0, 0x03, 0x80, 0x10, 0x6D, 0x21, 0x7F, 0x80, 0x0B, 0x6D, 0x21, 0x7F, 0x53, 0x4B, 0x6D, 0x21, 0x7F, 0x02, 0x4B, 0x54, 0x28, 0x6D, 0x6D, 0x6D, 0x21, 0x1F, 0x54, 0xC2, 0x76
+		}
+	},
+	{
+		116,
+		79,
+		0x39,
+		0x009F,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x9F, 0x2D, 0x6D, 0x21, 0x7F, 0x05, 0x2D, 0x79, 0xDF, 0xA5, 0x3C, 0x44, 0x00, 0xB0, 0x97, 0x55, 0x19, 0x04, 0x55, 0x1E, 0x10, 0x55, 0x23, 0x10, 0x55, 0x28, 0x10, 0x55, 0x2D, 0x04, 0x51, 0xBD, 0xB0, 0x23, 0x51, 0x25, 0xA0, 0x0D, 0x51, 0x20, 0x5F, 0x12, 0x2A, 0x5F, 0x14, 0x25, 0x91, 0x14, 0x53, 0x23, 0x51, 0x26, 0xA0, 0x0F, 0x51, 0x21, 0x5F, 0x12, 0x2B, 0x5F, 0x14, 0x26, 0x91, 0x04, 0x53, 0x38, 0x63
+		}
+	},
+	{
+		117,
+		79,
+		0x39,
+		0x00A0,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xA0, 0x1E, 0x53, 0x28, 0x57, 0x14, 0x52, 0x1B, 0x53, 0x4A, 0x52, 0x19, 0x53, 0x4B, 0x7C, 0x29, 0x2D, 0x50, 0x04, 0x6E, 0x4A, 0x6E, 0x4B, 0x78, 0xBF, 0xFA, 0x52, 0x1C, 0x14, 0x4B, 0x1E, 0x4A, 0x00, 0x47, 0x4A, 0x80, 0xA0, 0x07, 0x55, 0x4A, 0x00, 0x55, 0x4B, 0x00, 0x47, 0x4A, 0x7F, 0xA0, 0x04, 0x55, 0x4B, 0xFF, 0x51, 0x4B, 0x3C, 0xAD, 0x02, 0xA0, 0x0D, 0x3C, 0xAD, 0x03, 0xA0, 0x03, 0x80, 0xA1, 0x36
+		}
+	},
+	{
+		118,
+		79,
+		0x39,
+		0x00A1,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xA1, 0x10, 0x6D, 0x21, 0x7F, 0x80, 0x0B, 0x6D, 0x21, 0x7F, 0x53, 0x4B, 0x6D, 0x21, 0x7F, 0x02, 0x4B, 0x54, 0x1A, 0x6D, 0x6D, 0x6D, 0x21, 0x1F, 0x54, 0x19, 0x6D, 0x21, 0x7F, 0x05, 0x19, 0x5B, 0x11, 0x05, 0x5C, 0xDF, 0xA2, 0x51, 0x4C, 0x11, 0x01, 0x3A, 0x44, 0xB0, 0x97, 0x55, 0x1D, 0x04, 0x55, 0x22, 0x10, 0x55, 0x27, 0x10, 0x55, 0x2C, 0x10, 0x55, 0x31, 0x04, 0x51, 0xBD, 0xB0, 0x23, 0x51, 0xD2, 0x99
+		}
+	},
+	{
+		119,
+		79,
+		0x39,
+		0x00A2,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xA2, 0x25, 0xA0, 0x0D, 0x51, 0x20, 0x5F, 0x12, 0x2A, 0x5F, 0x14, 0x25, 0x90, 0x76, 0x53, 0x27, 0x51, 0x24, 0xA0, 0x0F, 0x51, 0x1F, 0x5F, 0x12, 0x29, 0x5F, 0x14, 0x24, 0x90, 0x66, 0x53, 0x22, 0x53, 0x2C, 0x57, 0x14, 0x52, 0x1B, 0x53, 0x4A, 0x52, 0x1D, 0x53, 0x4B, 0x7C, 0x29, 0x2D, 0x50, 0x04, 0x6E, 0x4A, 0x6E, 0x4B, 0x78, 0xBF, 0xFA, 0x52, 0x1A, 0x14, 0x4B, 0x1E, 0x4A, 0x00, 0x47, 0x4A, 0xB3, 0x5C
+		}
+	},
+	{
+		120,
+		79,
+		0x39,
+		0x00A3,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xA3, 0x80, 0xA0, 0x07, 0x55, 0x4A, 0x00, 0x55, 0x4B, 0x00, 0x47, 0x4A, 0x7F, 0xA0, 0x04, 0x55, 0x4B, 0xFF, 0x51, 0x4B, 0x3C, 0xAD, 0x02, 0xA0, 0x0D, 0x3C, 0xAD, 0x03, 0xA0, 0x03, 0x80, 0x10, 0x6D, 0x21, 0x7F, 0x80, 0x0B, 0x6D, 0x21, 0x7F, 0x53, 0x4B, 0x6D, 0x21, 0x7F, 0x02, 0x4B, 0x54, 0x1C, 0x6D, 0x6D, 0x6D, 0x21, 0x1F, 0x54, 0x1D, 0x6D, 0x21, 0x7F, 0x05, 0x1D, 0x5B, 0x11, 0x05, 0x5C, 0x0D, 0x11
+		}
+	},
+	{
+		121,
+		79,
+		0x39,
+		0x00A4,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xA4, 0xDF, 0xA2, 0x7F, 0x55, 0x11, 0x00, 0x04, 0x12, 0x0E, 0x11, 0x00, 0x65, 0x12, 0x6B, 0x11, 0x65, 0x12, 0x6B, 0x11, 0x65, 0x12, 0x6B, 0x11, 0x65, 0x12, 0x6B, 0x11, 0x55, 0x10, 0x00, 0x55, 0x13, 0x00, 0x7C, 0x11, 0x4D, 0x51, 0x12, 0x39, 0x10, 0xD0, 0x03, 0x50, 0x10, 0x7F, 0x12, 0x4B, 0x55, 0x10, 0x08, 0x47, 0x4B, 0x01, 0xA0, 0x03, 0x02, 0x4A, 0x6D, 0x6E, 0x4B, 0x7A, 0x10, 0xBF, 0xF3, 0x1A, 0x2C
+		}
+	},
+	{
+		122,
+		79,
+		0x39,
+		0x00A5,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xA5, 0x53, 0x4A, 0x7F, 0x62, 0xD0, 0x00, 0x3C, 0x0E, 0x02, 0xC0, 0x0E, 0x55, 0x36, 0x00, 0x90, 0x09, 0x47, 0x36, 0x40, 0xA0, 0x04, 0x7C, 0x14, 0x1F, 0x7F, 0x70, 0xBF, 0x62, 0xD4, 0x02, 0x62, 0xD3, 0x02, 0x50, 0x00, 0x53, 0x32, 0x53, 0x35, 0x53, 0x44, 0x53, 0x45, 0x55, 0x34, 0x99, 0x3E, 0x34, 0x53, 0x19, 0x3E, 0x34, 0x53, 0x1A, 0x3E, 0x34, 0x53, 0x1B, 0x76, 0x45, 0x51, 0x45, 0x3A, 0x0E, 0x9D, 0x33
+		}
+	},
+	{
+		123,
+		79,
+		0x39,
+		0x00A6,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xA6, 0xD1, 0x1F, 0x3E, 0x34, 0x53, 0x1C, 0x3E, 0x34, 0x53, 0x1D, 0x3E, 0x34, 0x53, 0x1E, 0x51, 0x19, 0x12, 0x1C, 0xD0, 0x03, 0x73, 0x74, 0x53, 0x1F, 0x51, 0x1A, 0x12, 0x1D, 0xD0, 0x03, 0x73, 0x74, 0x53, 0x20, 0x51, 0x1F, 0xA0, 0x07, 0x39, 0x02, 0xA0, 0x03, 0x80, 0x2D, 0x51, 0x20, 0xA0, 0x05, 0x39, 0x02, 0xB0, 0x25, 0x51, 0x19, 0x3A, 0x1C, 0xC0, 0x0B, 0xA0, 0x11, 0x51, 0x1B, 0x12, 0x4C, 0xEF, 0xD8
+		}
+	},
+	{
+		124,
+		79,
+		0x39,
+		0x00A7,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xA7, 0x53, 0x21, 0x80, 0x0C, 0x51, 0x1B, 0x02, 0x4C, 0x53, 0x21, 0x80, 0x04, 0x5F, 0x21, 0x1B, 0x51, 0x1A, 0x12, 0x1D, 0x67, 0x14, 0x21, 0x80, 0x8D, 0x3C, 0x1F, 0x02, 0xB0, 0x41, 0x3C, 0x20, 0x01, 0xB0, 0x3C, 0x51, 0x19, 0x3A, 0x1C, 0xC0, 0x0B, 0xA0, 0x11, 0x51, 0x1B, 0x12, 0x4C, 0x53, 0x21, 0x80, 0x0C, 0x51, 0x1B, 0x02, 0x4C, 0x53, 0x21, 0x80, 0x04, 0x5F, 0x21, 0x1B, 0x51, 0x1A, 0x3A, 0x1F, 0x39
+		}
+	},
+	{
+		125,
+		79,
+		0x39,
+		0x00A8,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xA8, 0x1D, 0xC0, 0x0E, 0x58, 0x21, 0x52, 0x00, 0x79, 0x3B, 0x00, 0xD0, 0x10, 0x7A, 0x21, 0x80, 0x0C, 0x58, 0x21, 0x52, 0x00, 0x75, 0x3B, 0x00, 0xD0, 0x03, 0x76, 0x21, 0x80, 0x48, 0x3C, 0x1F, 0x01, 0xB0, 0x41, 0x3C, 0x20, 0x02, 0xB0, 0x3C, 0x51, 0x1A, 0x3A, 0x1D, 0xC0, 0x08, 0x51, 0x1B, 0x78, 0x53, 0x21, 0x80, 0x06, 0x51, 0x1B, 0x74, 0x53, 0x21, 0x51, 0x19, 0x3A, 0x1C, 0xC0, 0x0B, 0xA0, 0x9C, 0x34
+		}
+	},
+	{
+		126,
+		79,
+		0x39,
+		0x00A9,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xA9, 0x11, 0x51, 0x21, 0x12, 0x4C, 0x53, 0x1F, 0x80, 0x0C, 0x51, 0x21, 0x02, 0x4C, 0x53, 0x1F, 0x80, 0x04, 0x55, 0x1F, 0x00, 0x58, 0x21, 0x52, 0x00, 0x58, 0x1F, 0x3B, 0x00, 0xD0, 0x03, 0x5A, 0x21, 0x80, 0x03, 0x8F, 0x17, 0x58, 0x1B, 0x52, 0x00, 0x58, 0x21, 0x13, 0x00, 0xCF, 0x0D, 0x3A, 0x18, 0xDF, 0x09, 0x58, 0x1E, 0x52, 0x00, 0x58, 0x21, 0x13, 0x00, 0xCE, 0xFF, 0x3A, 0x18, 0xDE, 0xFB, 0xB0, 0x5D
+		}
+	},
+	{
+		127,
+		79,
+		0x39,
+		0x00AA,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xAA, 0x58, 0x1B, 0x52, 0x00, 0x58, 0x1E, 0x3B, 0x00, 0xD0, 0x0A, 0x52, 0x00, 0x01, 0x01, 0x55, 0x36, 0x40, 0x80, 0x06, 0x01, 0x01, 0x55, 0x36, 0x40, 0x58, 0x21, 0x54, 0x00, 0x76, 0x32, 0x8E, 0xDB, 0x76, 0x44, 0x5F, 0x45, 0x44, 0x06, 0x35, 0x03, 0x51, 0x35, 0x55, 0x34, 0x99, 0x04, 0x34, 0x51, 0x44, 0x3A, 0x0E, 0xCE, 0xBA, 0x70, 0x3F, 0x71, 0xC0, 0x7F, 0x62, 0xD0, 0x00, 0x70, 0xCF, 0x71, 0x06, 0x0A
+		}
+	},
+	{
+		128,
+		79,
+		0x39,
+		0x00AB,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xAB, 0x20, 0x51, 0x9A, 0x60, 0xA0, 0x51, 0x9C, 0x60, 0xA2, 0x51, 0x9B, 0x60, 0xA1, 0x51, 0x9E, 0x60, 0xC7, 0x51, 0x9D, 0x60, 0xA4, 0x70, 0xCF, 0x7F, 0x62, 0xD0, 0x00, 0x62, 0xD5, 0x00, 0x62, 0xD4, 0x00, 0x62, 0xD3, 0x00, 0x55, 0xFA, 0x00, 0x50, 0x06, 0x55, 0xF8, 0x3A, 0x7C, 0x00, 0x60, 0x3C, 0xF8, 0x05, 0xB0, 0x12, 0x70, 0xCF, 0x71, 0x20, 0x62, 0xA6, 0x00, 0x71, 0x30, 0x62, 0x1B, 0x30, 0xAA, 0x53
+		}
+	},
+	{
+		129,
+		79,
+		0x39,
+		0x00AC,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xAC, 0x43, 0x1B, 0x40, 0x70, 0xCF, 0x7F, 0x10, 0x4F, 0x38, 0x02, 0x62, 0xD0, 0x03, 0x51, 0xE1, 0x54, 0x01, 0x51, 0xE0, 0x54, 0x00, 0x38, 0xFE, 0x20, 0x7F, 0x7F, 0x7F, 0x10, 0x4F, 0x20, 0x7F, 0x10, 0x4F, 0x20, 0x7F, 0x62, 0xD0, 0x00, 0x26, 0xAF, 0xFD, 0x7C, 0x72, 0x51, 0x26, 0xAE, 0xFB, 0x51, 0xAE, 0x60, 0x00, 0x7F, 0x10, 0x4F, 0x20, 0x7F, 0x10, 0x4F, 0x20, 0x7F, 0x10, 0x4F, 0x20, 0x7F, 0xEF, 0xDE
+		}
+	},
+	{
+		130,
+		79,
+		0x39,
+		0x00AD,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xAD, 0x10, 0x4F, 0x20, 0x7F, 0x10, 0x4F, 0x20, 0x7F, 0x10, 0x4F, 0x20, 0x7F, 0x10, 0x4F, 0x62, 0xD0, 0x00, 0x51, 0xAF, 0x29, 0x02, 0x7C, 0x6F, 0x20, 0x62, 0xD0, 0x00, 0x51, 0xAE, 0x29, 0x04, 0x53, 0xAE, 0x51, 0xAE, 0x60, 0x00, 0x20, 0x7F, 0x7F, 0x7F, 0x08, 0x62, 0xD0, 0x00, 0x55, 0xFA, 0x00, 0x62, 0xD5, 0x00, 0x62, 0xD4, 0x00, 0x4F, 0x5B, 0x01, 0x03, 0x53, 0xF9, 0x55, 0xF8, 0x3A, 0x50, 0xE3, 0xC7
+		}
+	},
+	{
+		131,
+		79,
+		0x39,
+		0x00AE,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xAE, 0x06, 0x00, 0x20, 0x70, 0xBF, 0x62, 0xD3, 0x00, 0x52, 0xF8, 0x70, 0x3F, 0x71, 0xC0, 0x7F, 0x62, 0xD0, 0x00, 0x55, 0xFA, 0x00, 0x62, 0xD5, 0x00, 0x62, 0xD4, 0x00, 0x4F, 0x5B, 0x01, 0x03, 0x53, 0xF9, 0x55, 0xF8, 0x3A, 0x50, 0x06, 0x00, 0x7F, 0x11, 0x04, 0x4B, 0xD0, 0x04, 0x78, 0xC0, 0x09, 0x3A, 0x80, 0x40, 0x79, 0x19, 0x00, 0xDF, 0xF9, 0x7F, 0x71, 0x40, 0xA0, 0x05, 0x70, 0xCF, 0x71, 0xD5, 0xAC
+		}
+	},
+	{
+		132,
+		79,
+		0x39,
+		0x00AF,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xAF, 0x10, 0x5E, 0x00, 0x70, 0xCF, 0x70, 0x3F, 0x71, 0xC0, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x56, 0x00, 0x00, 0x80, 0x13, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0xC0, 0x7C, 0x6F, 0x54, 0x52, 0x00, 0x3F, 0xE8, 0x77, 0x00, 0x3D, 0x00, 0x04, 0xCF, 0xEA, 0x62, 0xD0, 0x04, 0x55, 0xB6, 0x00, 0x62, 0xD0, 0x04, 0x55, 0xB5, 0x00, 0x7C, 0x73, 0x74, 0x38, 0xFF, 0x20, 0x7F, 0x7F, 0x10, 0x4F, 0xBC, 0x7B
+		}
+	},
+	{
+		133,
+		79,
+		0x39,
+		0x00B0,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xB0, 0x38, 0x01, 0x10, 0x7C, 0x11, 0x47, 0x62, 0xD0, 0x00, 0x20, 0x54, 0x00, 0x50, 0x0F, 0x08, 0x10, 0x7C, 0x2B, 0x38, 0x38, 0xFE, 0x52, 0x00, 0x62, 0xD0, 0x00, 0x38, 0xFF, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x02, 0x56, 0x01, 0x00, 0x9F, 0xD7, 0x62, 0xD0, 0x00, 0x54, 0x00, 0x52, 0x00, 0x08, 0x7C, 0x47, 0x34, 0x38, 0xFF, 0x52, 0x00, 0x08, 0x90, 0x46, 0x52, 0x00, 0x08, 0x62, 0xD0, 0x04, 0x51, 0x2E, 0x60
+		}
+	},
+	{
+		134,
+		79,
+		0x39,
+		0x00B1,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xB1, 0xB5, 0x08, 0x7C, 0x3A, 0x9B, 0x38, 0xFD, 0x62, 0xD0, 0x00, 0x54, 0x01, 0x5A, 0xE8, 0x06, 0xE8, 0x01, 0x50, 0x0F, 0x08, 0x51, 0xE8, 0x08, 0x52, 0x00, 0x08, 0x91, 0x47, 0x62, 0xD0, 0x00, 0x5A, 0xE8, 0x06, 0xE8, 0x01, 0x50, 0x0F, 0x08, 0x51, 0xE8, 0x08, 0x7C, 0x2B, 0x3C, 0x38, 0xFB, 0x52, 0x00, 0x62, 0xD0, 0x04, 0x53, 0xB5, 0x52, 0x01, 0x62, 0xD0, 0x00, 0x38, 0xFE, 0x20, 0x7F, 0x10, 0xF0, 0xE5
+		}
+	},
+	{
+		135,
+		79,
+		0x39,
+		0x00B2,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xB2, 0x4F, 0x38, 0x06, 0x50, 0x04, 0x3B, 0xFC, 0xD0, 0x04, 0x56, 0xFC, 0x04, 0x56, 0x05, 0x00, 0x56, 0x04, 0x00, 0x80, 0x67, 0x56, 0x02, 0xE0, 0x56, 0x01, 0x01, 0x56, 0x00, 0x00, 0x80, 0x23, 0x7C, 0x6F, 0x4C, 0x52, 0x01, 0x7C, 0x70, 0xCD, 0x7C, 0x6F, 0x44, 0x06, 0xE8, 0xC4, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x3B, 0x03, 0xB0, 0x03, 0x80, 0x0F, 0x07, 0x02, 0x08, 0x0F, 0x01, 0x00, 0x77, 0x09, 0x18
+		}
+	},
+	{
+		136,
+		79,
+		0x39,
+		0x00B3,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xB3, 0x00, 0x52, 0x00, 0x3B, 0xFC, 0xCF, 0xD9, 0x52, 0x00, 0x3B, 0xFC, 0xA0, 0x2C, 0x62, 0xD0, 0x00, 0x7C, 0x6F, 0x18, 0x06, 0xE8, 0xC0, 0x7C, 0x6F, 0x54, 0x52, 0x00, 0x3F, 0xE8, 0x7C, 0x6F, 0x18, 0x06, 0xE8, 0xC4, 0x7C, 0x6F, 0x54, 0x52, 0x03, 0x3F, 0xE8, 0x52, 0x02, 0x53, 0xE8, 0x52, 0x01, 0x60, 0xD5, 0x50, 0xFF, 0x3F, 0xE8, 0x77, 0x05, 0x77, 0x04, 0x62, 0xD0, 0x04, 0x52, 0x04, 0x3A, 0xDB, 0xBD
+		}
+	},
+	{
+		137,
+		79,
+		0x39,
+		0x00B4,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xB4, 0xB6, 0xCF, 0x92, 0x52, 0x05, 0x62, 0xD0, 0x04, 0x53, 0xB6, 0x3D, 0x05, 0x04, 0xD0, 0x55, 0x56, 0x02, 0xE0, 0x56, 0x01, 0x01, 0x56, 0x00, 0x00, 0x80, 0x44, 0x7C, 0x6F, 0x4C, 0x52, 0x01, 0x7C, 0x70, 0xCD, 0x3D, 0x03, 0xFF, 0xA0, 0x2F, 0x62, 0xD0, 0x04, 0x51, 0xB6, 0x7C, 0x70, 0x0E, 0x06, 0xE8, 0xC0, 0x7C, 0x6F, 0x54, 0x52, 0x00, 0x7C, 0x72, 0xE6, 0x7C, 0x70, 0x0E, 0x06, 0xE8, 0xC4, 0x09, 0x1A
+		}
+	},
+	{
+		138,
+		79,
+		0x39,
+		0x00B5,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xB5, 0x7C, 0x6F, 0x54, 0x52, 0x03, 0x7C, 0x72, 0xE6, 0x01, 0x01, 0x53, 0xB6, 0x62, 0xD0, 0x00, 0x39, 0x04, 0xC0, 0x03, 0x80, 0x0F, 0x07, 0x02, 0x08, 0x0F, 0x01, 0x00, 0x77, 0x00, 0x52, 0x00, 0x3B, 0xFC, 0xCF, 0xB8, 0x56, 0x04, 0x00, 0x80, 0x32, 0x62, 0xD0, 0x00, 0x7C, 0x6F, 0x44, 0x06, 0xE8, 0xC4, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x53, 0xE9, 0x7C, 0x72, 0x31, 0x06, 0xE6, 0xC0, 0x0E, 0x76, 0xF5
+		}
+	},
+	{
+		139,
+		79,
+		0x39,
+		0x00B6,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xB6, 0xE7, 0x03, 0x51, 0xE7, 0x60, 0xD4, 0x3E, 0xE6, 0x7C, 0x6E, 0xB6, 0x7C, 0x70, 0x1E, 0x06, 0xE6, 0xE0, 0x0E, 0xE7, 0x01, 0x7C, 0x6D, 0xEA, 0x77, 0x04, 0x52, 0x04, 0x3B, 0x05, 0xCF, 0xCA, 0x38, 0xFA, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x3D, 0xFC, 0x00, 0xB0, 0x06, 0x7C, 0x73, 0x74, 0x80, 0x28, 0x90, 0x29, 0x54, 0x00, 0x3D, 0x00, 0x00, 0xA0, 0x1F, 0x62, 0xD0, 0x04, 0x3C, 0xB4, 0x00, 0xF5, 0xF4
+		}
+	},
+	{
+		140,
+		79,
+		0x39,
+		0x00B7,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xB7, 0xB0, 0x10, 0x62, 0xD0, 0x00, 0x52, 0xFB, 0x53, 0xE8, 0x52, 0xFA, 0x60, 0xD5, 0x50, 0x01, 0x3F, 0xE8, 0x52, 0x00, 0x62, 0xD0, 0x04, 0x53, 0xB4, 0x38, 0xFF, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x02, 0x7C, 0x6F, 0xC9, 0x80, 0x22, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0x00, 0x0E, 0xE9, 0x02, 0x7C, 0x6D, 0x83, 0x53, 0xE9, 0x62, 0xD0, 0x00, 0x51, 0x16, 0x62, 0xD0, 0x00, 0x3A, 0xE9, 0x04, 0x13
+		}
+	},
+	{
+		141,
+		79,
+		0x39,
+		0x00B8,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xB8, 0xD0, 0x03, 0x77, 0x01, 0x77, 0x00, 0x3D, 0x00, 0x99, 0xD0, 0x07, 0x50, 0x28, 0x3B, 0x01, 0xDF, 0xD5, 0x50, 0x28, 0x3B, 0x01, 0xD0, 0x08, 0x62, 0xD0, 0x00, 0x50, 0x10, 0x80, 0x06, 0x62, 0xD0, 0x00, 0x50, 0x00, 0x38, 0xFE, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x7C, 0x6F, 0x2F, 0xB0, 0x0A, 0x3D, 0xFC, 0x00, 0xA0, 0x4E, 0x91, 0x78, 0x80, 0x4A, 0x3D, 0x00, 0x10, 0xB0, 0x03, 0x80, 0x43, 0xFB, 0x02
+		}
+	},
+	{
+		142,
+		79,
+		0x39,
+		0x00B9,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xB9, 0x3D, 0x00, 0x20, 0xB0, 0x2B, 0x62, 0xD0, 0x04, 0x51, 0xB5, 0x21, 0x0F, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x62, 0xD0, 0x04, 0x51, 0xB4, 0x21, 0xF0, 0x62, 0xD0, 0x00, 0x2A, 0xE9, 0x62, 0xD0, 0x03, 0x53, 0x9A, 0x51, 0x9A, 0x08, 0x50, 0x02, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x80, 0x14, 0x3D, 0x00, 0x30, 0xB0, 0x05, 0x90, 0x10, 0x80, 0x0B, 0x62, 0xD0, 0x03, 0x47, 0x99, 0x40, 0xA0, 0x03, 0xDB, 0xC3
+		}
+	},
+	{
+		143,
+		79,
+		0x39,
+		0x00BA,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xBA, 0x90, 0xE4, 0x38, 0xFF, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x04, 0x62, 0xD0, 0x04, 0x51, 0xB5, 0x08, 0x50, 0x23, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x62, 0xD0, 0x04, 0x50, 0x04, 0x3A, 0xB5, 0xC0, 0xB7, 0x56, 0x03, 0x00, 0x80, 0xA9, 0x62, 0xD0, 0x00, 0x52, 0x03, 0x7C, 0x6D, 0x8A, 0x51, 0xE8, 0x01, 0xE0, 0x54, 0x02, 0x51, 0xE9, 0x09, 0x01, 0x54, 0x01, 0x52, 0x03, 0x64, 0x64, 0x64, 0x01, 0x8B, 0x24
+		}
+	},
+	{
+		144,
+		79,
+		0x39,
+		0x00BB,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xBB, 0x03, 0x54, 0x00, 0x7C, 0x6F, 0xF6, 0x08, 0x52, 0x00, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x52, 0x02, 0x01, 0x01, 0x53, 0xE8, 0x52, 0x01, 0x09, 0x00, 0x7C, 0x6D, 0xE3, 0x3E, 0xE8, 0x53, 0xE8, 0x51, 0xE9, 0x08, 0x51, 0xE8, 0x08, 0x52, 0x00, 0x01, 0x01, 0x08, 0x7C, 0x32, 0x52, 0x62, 0xD0, 0x00, 0x52, 0x02, 0x01, 0x03, 0x7C, 0x6E, 0xA3, 0x08, 0x52, 0x00, 0x01, 0x03, 0x2A, 0x63
+		}
+	},
+	{
+		145,
+		79,
+		0x39,
+		0x00BC,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xBC, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFB, 0x62, 0xD0, 0x00, 0x52, 0x02, 0x01, 0x04, 0x53, 0xE8, 0x52, 0x01, 0x09, 0x00, 0x7C, 0x6D, 0xE3, 0x3E, 0xE8, 0x53, 0xE8, 0x51, 0xE9, 0x08, 0x51, 0xE8, 0x08, 0x52, 0x00, 0x01, 0x04, 0x08, 0x7C, 0x32, 0x52, 0x62, 0xD0, 0x00, 0x52, 0x02, 0x01, 0x06, 0x53, 0xE8, 0x52, 0x01, 0x09, 0x00, 0x7C, 0x6D, 0xE3, 0x3E, 0xE8, 0x53, 0xE8, 0x51, 0xE9, 0x08, 0x51, 0x0A, 0x24
+		}
+	},
+	{
+		146,
+		79,
+		0x39,
+		0x00BD,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xBD, 0xE8, 0x08, 0x52, 0x00, 0x01, 0x06, 0x08, 0x7C, 0x32, 0x52, 0x38, 0xFA, 0x77, 0x03, 0x62, 0xD0, 0x04, 0x52, 0x03, 0x3A, 0xB5, 0xCF, 0x50, 0x50, 0x00, 0x08, 0x50, 0x25, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x38, 0xFC, 0x20, 0x7F, 0x62, 0xD0, 0x04, 0x51, 0xB5, 0x21, 0x0F, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x62, 0xD0, 0x04, 0x51, 0xB4, 0x21, 0xF0, 0x62, 0xD0, 0x00, 0x2A, 0xE9, 0x62, 0xD0, 0xCB, 0xA7
+		}
+	},
+	{
+		147,
+		79,
+		0x39,
+		0x00BE,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xBE, 0x03, 0x53, 0x9A, 0x51, 0x9A, 0x08, 0x50, 0x02, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x62, 0xD0, 0x01, 0x51, 0xE6, 0x08, 0x51, 0xE7, 0x08, 0x50, 0x03, 0x08, 0x7C, 0x32, 0x52, 0x62, 0xD0, 0x01, 0x51, 0xE4, 0x08, 0x51, 0xE5, 0x08, 0x50, 0x05, 0x08, 0x7C, 0x32, 0x52, 0x38, 0xFA, 0x7F, 0x10, 0x4F, 0x38, 0x07, 0x62, 0xD0, 0x04, 0x51, 0xB5, 0x21, 0x0F, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x62, 0xD0, 0xB2
+		}
+	},
+	{
+		148,
+		79,
+		0x39,
+		0x00BF,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xBF, 0xD0, 0x04, 0x51, 0xB4, 0x21, 0xF0, 0x62, 0xD0, 0x00, 0x2A, 0xE9, 0x62, 0xD0, 0x03, 0x53, 0x9A, 0x51, 0x9A, 0x08, 0x50, 0x02, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x56, 0x00, 0x00, 0x80, 0xCA, 0x56, 0x04, 0x00, 0x62, 0xD0, 0x04, 0x52, 0x00, 0x3A, 0xB5, 0xD0, 0x12, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0xC0, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x54, 0x04, 0x62, 0xD0, 0x00, 0x3F, 0x91
+		}
+	},
+	{
+		149,
+		79,
+		0x39,
+		0x00C0,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xC0, 0x52, 0x04, 0x7C, 0x6D, 0x8A, 0x51, 0xE8, 0x01, 0xE0, 0x54, 0x02, 0x51, 0xE9, 0x09, 0x01, 0x54, 0x01, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0x81, 0x0E, 0xE9, 0x0D, 0x7C, 0x6F, 0x5C, 0x54, 0x03, 0x52, 0x02, 0x01, 0x06, 0x53, 0xE8, 0x52, 0x01, 0x09, 0x00, 0x7C, 0x6D, 0xE3, 0x3E, 0xE8, 0x53, 0xE8, 0x51, 0xE9, 0x08, 0x51, 0xE8, 0x08, 0x52, 0x03, 0x08, 0x7C, 0x32, 0x52, 0x38, 0xFD, 0x62, 0xD0, 0xD7, 0xC2
+		}
+	},
+	{
+		150,
+		79,
+		0x39,
+		0x00C1,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xC1, 0x00, 0x52, 0x02, 0x01, 0x04, 0x53, 0xE8, 0x52, 0x01, 0x09, 0x00, 0x7C, 0x6D, 0xE3, 0x3E, 0xE8, 0x53, 0xE8, 0x51, 0xE9, 0x08, 0x51, 0xE8, 0x08, 0x52, 0x03, 0x01, 0x02, 0x08, 0x7C, 0x32, 0x52, 0x62, 0xD0, 0x00, 0x52, 0x02, 0x01, 0x03, 0x7C, 0x6E, 0xA3, 0x08, 0x52, 0x03, 0x01, 0x04, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFB, 0x7C, 0x6F, 0x4C, 0x52, 0x01, 0x60, 0xD4, 0x3E, 0xE8, 0x54, 0x05, 0xAB, 0x6B
+		}
+	},
+	{
+		151,
+		79,
+		0x39,
+		0x00C2,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xC2, 0x48, 0x00, 0x01, 0xA0, 0x18, 0x52, 0x05, 0x21, 0x0F, 0x53, 0xE9, 0x52, 0x06, 0x2A, 0xE9, 0x08, 0x52, 0x03, 0x11, 0x01, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x80, 0x0C, 0x52, 0x05, 0x62, 0xD0, 0x00, 0x64, 0x64, 0x64, 0x64, 0x54, 0x06, 0x77, 0x00, 0x3D, 0x00, 0x04, 0xCF, 0x33, 0x38, 0xF9, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x71, 0x10, 0x41, 0x04, 0x5F, 0x70, 0xCF, 0x62, 0xD0, 0x00, 0x4D, 0xB0
+		}
+	},
+	{
+		152,
+		79,
+		0x39,
+		0x00C3,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xC3, 0x51, 0xAF, 0x29, 0xA0, 0x7C, 0x6F, 0x20, 0x10, 0x7C, 0x20, 0x0B, 0x7C, 0x20, 0x50, 0x20, 0x10, 0x50, 0x01, 0x08, 0x50, 0x00, 0x08, 0x50, 0xA0, 0x08, 0x08, 0x7C, 0x20, 0x57, 0x38, 0xFC, 0x20, 0x62, 0xC8, 0x0B, 0x62, 0xCA, 0x24, 0x43, 0xD6, 0x01, 0x62, 0xCD, 0x00, 0x56, 0x00, 0x20, 0x80, 0x06, 0x62, 0xCF, 0x00, 0x7B, 0x00, 0x3D, 0x00, 0x00, 0xBF, 0xF7, 0x41, 0xD6, 0xFE, 0x38, 0xFF, 0x54, 0xBF
+		}
+	},
+	{
+		153,
+		79,
+		0x39,
+		0x00C4,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xC4, 0x20, 0x7F, 0x10, 0x4F, 0x3D, 0xFC, 0x21, 0xD0, 0x0C, 0x41, 0xD6, 0xEF, 0x41, 0xE0, 0x7F, 0x62, 0xC8, 0x0B, 0x80, 0x0A, 0x62, 0xC8, 0x00, 0x43, 0xD6, 0x10, 0x43, 0xE0, 0x80, 0x20, 0x7F, 0x43, 0xD6, 0x01, 0x40, 0x62, 0xD0, 0x00, 0x51, 0xAF, 0x29, 0xA0, 0x7C, 0x6F, 0x20, 0x71, 0x10, 0x43, 0x04, 0xA0, 0x70, 0xCF, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x5D, 0xF7, 0x54, 0x00, 0x70, 0xFE, 0x7C, 0xDE, 0xD4
+		}
+	},
+	{
+		154,
+		79,
+		0x39,
+		0x00C5,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xC5, 0x70, 0xF8, 0xB0, 0x13, 0x7C, 0x73, 0x90, 0xBF, 0xFC, 0x71, 0x01, 0x40, 0x70, 0xFE, 0x62, 0xE3, 0x38, 0x41, 0xD6, 0xFE, 0x80, 0x06, 0x10, 0x7C, 0x33, 0x60, 0x20, 0x71, 0x10, 0x41, 0x04, 0x5F, 0x70, 0xCF, 0x62, 0xD0, 0x00, 0x51, 0xAF, 0x29, 0xA0, 0x7C, 0x6F, 0x20, 0x48, 0x00, 0x01, 0xA0, 0x03, 0x71, 0x01, 0x38, 0xFF, 0x20, 0x7F, 0x10, 0x7C, 0x33, 0x60, 0x20, 0x71, 0x10, 0x41, 0x04, 0x7F, 0x17
+		}
+	},
+	{
+		155,
+		79,
+		0x39,
+		0x00C6,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xC6, 0x5F, 0x70, 0xCF, 0x62, 0xD0, 0x00, 0x51, 0xAF, 0x29, 0xA0, 0x7C, 0x6F, 0x20, 0x71, 0x10, 0x43, 0xEC, 0x02, 0x70, 0xCF, 0x62, 0xDA, 0x7F, 0x43, 0xE0, 0x80, 0x9F, 0x83, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x9F, 0x92, 0x71, 0x10, 0x43, 0xEC, 0x02, 0x70, 0xCF, 0x62, 0xDA, 0x7F, 0x43, 0xE0, 0x80, 0x9F, 0x6D, 0x38, 0xFF, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x7C, 0x73, 0x89, 0x38, 0xFF, 0x20, 0xF5, 0x04
+		}
+	},
+	{
+		156,
+		79,
+		0x39,
+		0x00C7,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xC7, 0x7F, 0x7C, 0x73, 0x89, 0x7F, 0x10, 0x4F, 0x38, 0x02, 0x5D, 0xC8, 0x39, 0x00, 0xB0, 0x18, 0x7C, 0x73, 0x90, 0xA0, 0x09, 0x56, 0x01, 0x01, 0x56, 0x00, 0x00, 0x80, 0x04, 0x7C, 0x6F, 0xC9, 0x62, 0xD0, 0x00, 0x52, 0x01, 0x80, 0x1D, 0x5D, 0xC9, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x47, 0xE9, 0x01, 0xA0, 0x09, 0x56, 0x01, 0x01, 0x56, 0x00, 0x00, 0x80, 0x04, 0x7C, 0x6F, 0xC9, 0x62, 0xD0, 0x00, 0xEB, 0xF1
+		}
+	},
+	{
+		157,
+		79,
+		0x39,
+		0x00C8,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xC8, 0x52, 0x01, 0x38, 0xFE, 0x20, 0x7F, 0x62, 0xD0, 0x00, 0x50, 0x00, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x7C, 0x70, 0xF8, 0xA0, 0x25, 0x5D, 0xD6, 0x53, 0xE9, 0x2E, 0xE9, 0xFE, 0x51, 0xE9, 0x54, 0x00, 0x43, 0xD6, 0x01, 0x52, 0xFC, 0x60, 0xCD, 0x52, 0xFB, 0x60, 0xCF, 0x5D, 0xD6, 0x53, 0xE9, 0x52, 0x00, 0x24, 0xE9, 0x51, 0xE9, 0x60, 0xD6, 0x80, 0x16, 0x3D, 0xFC, 0xA0, 0xD0, 0x11, 0x7C, 0x6F, 0x06, 0x28
+		}
+	},
+	{
+		158,
+		79,
+		0x39,
+		0x00C9,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xC9, 0xB9, 0x55, 0xE9, 0x00, 0x06, 0xE8, 0x00, 0x7C, 0x71, 0x08, 0x52, 0xFB, 0x3F, 0xE8, 0x38, 0xFF, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x03, 0x52, 0xFB, 0x54, 0x01, 0x52, 0xFA, 0x54, 0x00, 0x7C, 0x70, 0xF8, 0xA0, 0x1C, 0x7C, 0x71, 0x65, 0x60, 0xCD, 0x52, 0x00, 0x60, 0xCF, 0x52, 0x01, 0x60, 0xCF, 0x5D, 0xD6, 0x53, 0xE9, 0x52, 0x02, 0x24, 0xE9, 0x51, 0xE9, 0x60, 0xD6, 0x80, 0x26, 0x3D, 0xFC, 0x41, 0x9F
+		}
+	},
+	{
+		159,
+		79,
+		0x39,
+		0x00CA,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xCA, 0x9F, 0xD0, 0x21, 0x7C, 0x6F, 0xB9, 0x55, 0xE9, 0x00, 0x06, 0xE8, 0x00, 0x0E, 0xE9, 0x01, 0x7C, 0x6F, 0xD9, 0x52, 0xFC, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x06, 0xE8, 0x01, 0x7C, 0x71, 0x08, 0x52, 0x01, 0x3F, 0xE8, 0x38, 0xFD, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x02, 0x7C, 0x70, 0xF8, 0xA0, 0x29, 0x5D, 0xD6, 0x53, 0xE9, 0x2E, 0xE9, 0xFE, 0x51, 0xE9, 0x54, 0x01, 0x43, 0xD6, 0x01, 0x10, 0x52, 0xEA, 0xF2
+		}
+	},
+	{
+		160,
+		79,
+		0x39,
+		0x00CB,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xCB, 0xFC, 0x7C, 0x33, 0x49, 0x62, 0xD0, 0x00, 0x20, 0x54, 0x00, 0x5D, 0xD6, 0x53, 0xE9, 0x52, 0x01, 0x24, 0xE9, 0x51, 0xE9, 0x60, 0xD6, 0x80, 0x17, 0x3D, 0xFC, 0xA0, 0xD0, 0x12, 0x7C, 0x6F, 0xB9, 0x55, 0xE9, 0x00, 0x06, 0xE8, 0x00, 0x0E, 0xE9, 0x01, 0x7C, 0x6D, 0x83, 0x54, 0x00, 0x52, 0x00, 0x62, 0xD0, 0x00, 0x38, 0xFE, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x03, 0x7C, 0x70, 0xF8, 0xA0, 0x1B, 0x45, 0xA9
+		}
+	},
+	{
+		161,
+		79,
+		0x39,
+		0x00CC,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xCC, 0x7C, 0x71, 0x65, 0x08, 0x7C, 0x33, 0x53, 0x38, 0xFF, 0x7C, 0x72, 0x25, 0x5D, 0xD6, 0x53, 0xE9, 0x52, 0x02, 0x24, 0xE9, 0x51, 0xE9, 0x60, 0xD6, 0x80, 0x29, 0x3D, 0xFC, 0x9F, 0xD0, 0x24, 0x7C, 0x6F, 0xB9, 0x55, 0xE9, 0x00, 0x06, 0xE8, 0x00, 0x0E, 0xE9, 0x01, 0x7C, 0x6D, 0x83, 0x54, 0x00, 0x52, 0xFC, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x06, 0xE8, 0x01, 0x0E, 0xE9, 0x01, 0x7C, 0x6D, 0x83, 0xFD, 0x1A
+		}
+	},
+	{
+		162,
+		79,
+		0x39,
+		0x00CD,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xCD, 0x54, 0x01, 0x7C, 0x71, 0x2E, 0x38, 0xFD, 0x20, 0x7F, 0x60, 0xCD, 0x5D, 0xF7, 0x08, 0x70, 0xFE, 0x5D, 0xCF, 0x7E, 0x60, 0xCD, 0x5D, 0xF7, 0x08, 0x70, 0xFE, 0x5D, 0xCF, 0x5C, 0x5D, 0xCF, 0x7E, 0x49, 0xC9, 0x01, 0xBF, 0xFC, 0x41, 0xD6, 0xFE, 0x7F, 0x41, 0x05, 0xF7, 0x7C, 0x73, 0x82, 0x62, 0xD0, 0x00, 0x51, 0xAF, 0x29, 0x08, 0x7C, 0x6F, 0x20, 0x71, 0x10, 0x43, 0x05, 0x08, 0x43, 0x04, 0xA4, 0x69
+		}
+	},
+	{
+		163,
+		79,
+		0x39,
+		0x00CE,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xCE, 0x08, 0x70, 0xCF, 0x62, 0xD0, 0x04, 0x55, 0xB9, 0x00, 0x62, 0xD0, 0x03, 0x55, 0x99, 0x04, 0x55, 0x9A, 0x00, 0x55, 0x9B, 0xF8, 0x55, 0x9C, 0x00, 0x55, 0x9E, 0x64, 0x55, 0x9D, 0x32, 0x55, 0x9F, 0x00, 0x55, 0xA0, 0x00, 0x7C, 0x30, 0xB2, 0x90, 0x10, 0x7C, 0x6F, 0x64, 0x10, 0x57, 0x01, 0x50, 0xF4, 0x7C, 0x2B, 0xA8, 0x20, 0x7C, 0x6E, 0xE8, 0x7F, 0x10, 0x4F, 0x38, 0x03, 0x7C, 0x31, 0x35, 0x13, 0x48
+		}
+	},
+	{
+		164,
+		79,
+		0x39,
+		0x00CF,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xCF, 0x7C, 0x6F, 0x2F, 0xB0, 0x06, 0x56, 0x01, 0x20, 0x80, 0x04, 0x56, 0x01, 0xA0, 0x52, 0x01, 0x08, 0x7C, 0x31, 0x02, 0x38, 0xFF, 0x62, 0xD0, 0x03, 0x51, 0x99, 0x21, 0xFC, 0x62, 0xD0, 0x00, 0x08, 0x50, 0x00, 0x08, 0x7C, 0x32, 0x0C, 0x50, 0x20, 0x08, 0x50, 0x01, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x3D, 0x00, 0x00, 0xB0, 0x28, 0x62, 0xD0, 0x04, 0x51, 0xB9, 0x08, 0x50, 0x01, 0x08, 0x7C, 0x7C, 0x1B
+		}
+	},
+	{
+		165,
+		79,
+		0x39,
+		0x00D0,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xD0, 0x32, 0x0C, 0x38, 0xFE, 0x50, 0x00, 0x08, 0x50, 0x02, 0x08, 0x7C, 0x32, 0x0C, 0x62, 0xD0, 0x03, 0x51, 0x9B, 0x08, 0x50, 0x1E, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x82, 0x52, 0x3D, 0x00, 0x10, 0xB1, 0x87, 0x50, 0x00, 0x08, 0x50, 0x02, 0x08, 0x7C, 0x32, 0x0C, 0x7C, 0x40, 0x1F, 0x62, 0xD0, 0x00, 0x08, 0x50, 0x01, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x50, 0xC0, 0x08, 0x50, 0x03, 0x08, 0x01, 0x26
+		}
+	},
+	{
+		166,
+		79,
+		0x39,
+		0x00D1,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xD1, 0x7C, 0x32, 0x0C, 0x50, 0xC1, 0x08, 0x50, 0x04, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x50, 0xC2, 0x08, 0x50, 0x05, 0x08, 0x7C, 0x32, 0x0C, 0x50, 0x00, 0x08, 0x50, 0x06, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x10, 0x50, 0x00, 0x7C, 0x2B, 0x69, 0x62, 0xD0, 0x00, 0x20, 0x08, 0x50, 0x07, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x10, 0x50, 0x01, 0x7C, 0x2B, 0x69, 0x62, 0xD0, 0x00, 0x20, 0x08, 0xD6, 0xD1
+		}
+	},
+	{
+		167,
+		79,
+		0x39,
+		0x00D2,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xD2, 0x50, 0x08, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x10, 0x50, 0x02, 0x7C, 0x2B, 0x69, 0x62, 0xD0, 0x00, 0x20, 0x08, 0x50, 0x09, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x10, 0x50, 0x03, 0x7C, 0x2B, 0x69, 0x62, 0xD0, 0x00, 0x20, 0x08, 0x50, 0x0A, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x10, 0x50, 0x04, 0x7C, 0x2B, 0x69, 0x62, 0xD0, 0x00, 0x20, 0x08, 0x50, 0x0B, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xAF, 0x84
+		}
+	},
+	{
+		168,
+		79,
+		0x39,
+		0x00D3,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xD3, 0xFE, 0x10, 0x50, 0x05, 0x7C, 0x2B, 0x69, 0x62, 0xD0, 0x00, 0x20, 0x08, 0x50, 0x0C, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x10, 0x50, 0x06, 0x7C, 0x2B, 0x69, 0x62, 0xD0, 0x00, 0x20, 0x08, 0x50, 0x0D, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x10, 0x50, 0x07, 0x7C, 0x2B, 0x69, 0x62, 0xD0, 0x00, 0x20, 0x08, 0x50, 0x0E, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x50, 0x07, 0x10, 0x06, 0x33
+		}
+	},
+	{
+		169,
+		79,
+		0x39,
+		0x00D4,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xD4, 0x08, 0x57, 0xA0, 0x28, 0x53, 0xE9, 0x18, 0x75, 0x09, 0x00, 0x28, 0x53, 0xE8, 0x20, 0x51, 0xE9, 0x08, 0x51, 0xE8, 0x08, 0x50, 0x0F, 0x08, 0x7C, 0x32, 0x52, 0x38, 0xFD, 0x50, 0x10, 0x08, 0x50, 0x12, 0x08, 0x50, 0x11, 0x08, 0x7C, 0x32, 0x52, 0x50, 0xA0, 0x08, 0x50, 0x02, 0x08, 0x50, 0x13, 0x08, 0x7C, 0x32, 0x52, 0x38, 0xFA, 0x50, 0x04, 0x08, 0x50, 0x00, 0x08, 0x50, 0x15, 0x08, 0x7C, 0x62, 0xEC
+		}
+	},
+	{
+		170,
+		79,
+		0x39,
+		0x00D5,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xD5, 0x32, 0x52, 0x50, 0x00, 0x08, 0x50, 0x17, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFB, 0x50, 0x00, 0x08, 0x50, 0x18, 0x08, 0x7C, 0x32, 0x0C, 0x50, 0x00, 0x08, 0x50, 0x19, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x50, 0x00, 0x08, 0x50, 0x1A, 0x08, 0x7C, 0x32, 0x0C, 0x50, 0x00, 0x08, 0x50, 0x1B, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x50, 0x00, 0x08, 0x50, 0x1C, 0x08, 0x7C, 0x32, 0x0C, 0x62, 0xD0, 0x66, 0xF5
+		}
+	},
+	{
+		171,
+		79,
+		0x39,
+		0x00D6,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xD6, 0x03, 0x51, 0x9C, 0x08, 0x50, 0x1D, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x62, 0xD0, 0x03, 0x51, 0x9E, 0x08, 0x50, 0x1E, 0x08, 0x7C, 0x32, 0x0C, 0x62, 0xD0, 0x03, 0x51, 0x9D, 0x08, 0x50, 0x1F, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x80, 0xC7, 0x3D, 0x00, 0x20, 0xB0, 0x03, 0x80, 0xC0, 0x3D, 0x00, 0x30, 0xB0, 0xBB, 0x50, 0x01, 0x08, 0x50, 0x00, 0x08, 0x7C, 0x32, 0x0C, 0x50, 0x04, 0x08, 0x7D, 0x24
+		}
+	},
+	{
+		172,
+		79,
+		0x39,
+		0x00D7,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xD7, 0x50, 0x01, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x50, 0x01, 0x08, 0x50, 0x02, 0x08, 0x7C, 0x32, 0x0C, 0x62, 0xD0, 0x03, 0x51, 0x9F, 0x08, 0x50, 0x29, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x50, 0x04, 0x08, 0x50, 0x2A, 0x08, 0x7C, 0x32, 0x0C, 0x50, 0x08, 0x08, 0x50, 0x2B, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x50, 0x08, 0x08, 0x50, 0x2C, 0x08, 0x7C, 0x32, 0x0C, 0x50, 0x00, 0x08, 0x50, 0xFF, 0x29
+		}
+	},
+	{
+		173,
+		79,
+		0x39,
+		0x00D8,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xD8, 0x48, 0x08, 0x50, 0x2D, 0x08, 0x7C, 0x32, 0x52, 0x38, 0xFB, 0x50, 0x1C, 0x08, 0x50, 0x2F, 0x08, 0x7C, 0x32, 0x0C, 0x50, 0x00, 0x08, 0x50, 0x30, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x50, 0x08, 0x08, 0x50, 0x31, 0x08, 0x7C, 0x32, 0x0C, 0x50, 0x08, 0x08, 0x50, 0x32, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x50, 0x5A, 0x08, 0x50, 0x33, 0x08, 0x7C, 0x32, 0x0C, 0x50, 0x00, 0x08, 0x50, 0x34, 0xD9, 0xDE
+		}
+	},
+	{
+		174,
+		79,
+		0x39,
+		0x00D9,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xD9, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x50, 0x04, 0x08, 0x50, 0x35, 0x08, 0x7C, 0x32, 0x0C, 0x50, 0x0C, 0x08, 0x50, 0x36, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x50, 0x05, 0x08, 0x50, 0x37, 0x08, 0x7C, 0x32, 0x0C, 0x50, 0x01, 0x08, 0x50, 0x38, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x7C, 0x2B, 0x1A, 0x7C, 0x31, 0x1F, 0x38, 0xFD, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x02, 0x7C, 0x32, 0x06, 0x7C, 0x20, 0x6D
+		}
+	},
+	{
+		175,
+		79,
+		0x39,
+		0x00DA,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xDA, 0x6F, 0x3C, 0x54, 0x00, 0x3D, 0x00, 0x30, 0xB0, 0x05, 0x90, 0xE8, 0x80, 0x03, 0x90, 0x4B, 0x62, 0xD0, 0x03, 0x51, 0x99, 0x21, 0x01, 0x62, 0xD0, 0x00, 0x39, 0x01, 0xB0, 0x19, 0x7C, 0x31, 0x35, 0x62, 0xD4, 0x00, 0x62, 0xD5, 0x00, 0x62, 0xD1, 0x00, 0x62, 0xD3, 0x00, 0x62, 0xD0, 0x00, 0x62, 0xE3, 0x38, 0x50, 0x00, 0x00, 0x7C, 0x6F, 0x3C, 0x54, 0x01, 0x52, 0x01, 0x3B, 0x00, 0xA0, 0x17, 0xE4, 0xF6
+		}
+	},
+	{
+		176,
+		79,
+		0x39,
+		0x00DB,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xDB, 0x52, 0x01, 0x08, 0x52, 0x00, 0x08, 0x7C, 0x2B, 0x1B, 0x9C, 0xEE, 0x52, 0x01, 0x08, 0x52, 0x00, 0x08, 0x7C, 0x2B, 0x1F, 0x38, 0xFC, 0x38, 0xFE, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x7C, 0x6F, 0x2F, 0xB0, 0x30, 0x50, 0x00, 0x08, 0x7C, 0x32, 0xA7, 0x62, 0xD0, 0x03, 0x53, 0x99, 0x50, 0x1E, 0x08, 0x7C, 0x32, 0xA7, 0x38, 0xFE, 0x62, 0xD0, 0x03, 0x53, 0x9B, 0x47, 0x99, 0x02, 0xA0, 0x70, 0xFB, 0x25
+		}
+	},
+	{
+		177,
+		79,
+		0x39,
+		0x00DC,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xDC, 0x51, 0x99, 0x21, 0xFD, 0x62, 0xD0, 0x00, 0x08, 0x50, 0x00, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x80, 0x5E, 0x3D, 0x00, 0x10, 0xB0, 0x33, 0x50, 0x00, 0x08, 0x7C, 0x32, 0xA7, 0x62, 0xD0, 0x03, 0x53, 0x99, 0x50, 0x1D, 0x08, 0x7C, 0x32, 0xA7, 0x38, 0xFE, 0x62, 0xD0, 0x03, 0x53, 0x9C, 0x50, 0x1F, 0x08, 0x7C, 0x32, 0xA7, 0x62, 0xD0, 0x03, 0x53, 0x9D, 0x50, 0x1E, 0x08, 0x7C, 0x32, 0xA7, 0x46, 0xBC
+		}
+	},
+	{
+		178,
+		79,
+		0x39,
+		0x00DD,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xDD, 0x38, 0xFE, 0x62, 0xD0, 0x03, 0x53, 0x9E, 0x80, 0x27, 0x3D, 0x00, 0x20, 0xB0, 0x10, 0x50, 0x00, 0x08, 0x7C, 0x32, 0xA7, 0x38, 0xFF, 0x62, 0xD0, 0x03, 0x53, 0x99, 0x80, 0x13, 0x48, 0x00, 0x40, 0xA0, 0x0E, 0x50, 0x00, 0x08, 0x7C, 0x32, 0xA7, 0x38, 0xFF, 0x62, 0xD0, 0x03, 0x53, 0x99, 0x38, 0xFF, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x04, 0x50, 0x00, 0x08, 0x7C, 0x32, 0xA7, 0x38, 0xFF, 0x62, 0xAA, 0x85
+		}
+	},
+	{
+		179,
+		79,
+		0x39,
+		0x00DE,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xDE, 0xD0, 0x00, 0x54, 0x00, 0x3D, 0x00, 0x01, 0xA0, 0x1F, 0x52, 0x00, 0x21, 0x70, 0x39, 0x30, 0xB0, 0x0E, 0x50, 0x01, 0x08, 0x50, 0x00, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x80, 0xE7, 0x52, 0x00, 0x62, 0xD0, 0x03, 0x53, 0x99, 0x80, 0xDE, 0x50, 0x29, 0x08, 0x7C, 0x32, 0xA7, 0x62, 0xD0, 0x03, 0x53, 0x9F, 0x50, 0x02, 0x08, 0x7C, 0x32, 0xA7, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x39, 0x81, 0xB0, 0x82, 0x36
+		}
+	},
+	{
+		180,
+		79,
+		0x39,
+		0x00DF,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xDF, 0xC4, 0x50, 0x2A, 0x08, 0x7C, 0x32, 0xA7, 0x62, 0xD0, 0x00, 0x53, 0xA5, 0x50, 0x2B, 0x08, 0x7C, 0x32, 0xA7, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x53, 0x15, 0x50, 0x2C, 0x08, 0x7C, 0x32, 0xA7, 0x62, 0xD0, 0x00, 0x53, 0x16, 0x50, 0x2D, 0x08, 0x7C, 0x32, 0xF7, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x08, 0x51, 0xE9, 0x62, 0xD0, 0x00, 0x53, 0xA3, 0x18, 0x53, 0xA4, 0x50, 0x2F, 0x08, 0x7C, 0x28, 0x83
+		}
+	},
+	{
+		181,
+		79,
+		0x39,
+		0x00E0,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xE0, 0x32, 0xA7, 0x38, 0xFF, 0x62, 0xD0, 0x00, 0x53, 0x42, 0x50, 0x30, 0x08, 0x7C, 0x32, 0xA7, 0x62, 0xD0, 0x04, 0x53, 0xB7, 0x50, 0x31, 0x08, 0x7C, 0x32, 0xA7, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x53, 0xA6, 0x50, 0x32, 0x08, 0x7C, 0x32, 0xA7, 0x62, 0xD0, 0x00, 0x53, 0x17, 0x50, 0x36, 0x08, 0x7C, 0x32, 0xA7, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x53, 0xA7, 0x50, 0x37, 0x08, 0x7C, 0x32, 0xA7, 0x62, 0x39, 0xA6
+		}
+	},
+	{
+		182,
+		79,
+		0x39,
+		0x00E1,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xE1, 0xD0, 0x00, 0x53, 0xA8, 0x50, 0x38, 0x08, 0x7C, 0x32, 0xA7, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x53, 0xA9, 0x10, 0x7C, 0x18, 0x83, 0x7C, 0x17, 0xB7, 0x20, 0x80, 0x04, 0x62, 0xE3, 0x38, 0x7C, 0x31, 0xC5, 0x62, 0xD0, 0x00, 0x39, 0x00, 0xBF, 0xF4, 0x7C, 0x71, 0x10, 0x7C, 0x49, 0x07, 0x62, 0xE3, 0x38, 0x52, 0x01, 0x71, 0x10, 0x60, 0xE0, 0x50, 0x01, 0x08, 0x50, 0x02, 0x08, 0x70, 0xCF, 0x7C, 0xFE, 0x31
+		}
+	},
+	{
+		183,
+		79,
+		0x39,
+		0x00E2,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xE2, 0x32, 0x0C, 0x38, 0xFE, 0x38, 0xFC, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x7C, 0x6F, 0x2F, 0xB0, 0x06, 0x3D, 0xFC, 0x00, 0xB0, 0x0B, 0x3D, 0x00, 0x20, 0xA0, 0x06, 0x48, 0x00, 0x40, 0xA0, 0x26, 0x62, 0xD0, 0x04, 0x06, 0xB9, 0x40, 0x51, 0xB9, 0x29, 0x20, 0x62, 0xD0, 0x00, 0x08, 0x50, 0x01, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x80, 0x04, 0x62, 0xE3, 0x38, 0x7C, 0x31, 0xC5, 0x62, 0xD0, 0xA4, 0x7E
+		}
+	},
+	{
+		184,
+		79,
+		0x39,
+		0x00E3,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xE3, 0x00, 0x39, 0x00, 0xBF, 0xF4, 0x38, 0xFF, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x7C, 0x6F, 0x2F, 0xB0, 0x06, 0x3D, 0xFC, 0x00, 0xB0, 0x0B, 0x3D, 0x00, 0x20, 0xA0, 0x06, 0x48, 0x00, 0x40, 0xA0, 0x0F, 0x62, 0xD0, 0x04, 0x51, 0xB9, 0x08, 0x50, 0x01, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x50, 0x00, 0x08, 0x7C, 0x32, 0xA7, 0x38, 0xFF, 0x21, 0x80, 0x62, 0xD0, 0x04, 0x53, 0xB8, 0x38, 0xFF, 0xE6, 0x03
+		}
+	},
+	{
+		185,
+		79,
+		0x39,
+		0x00E4,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xE4, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x7C, 0x6F, 0x2F, 0xB0, 0x0A, 0x3D, 0xFC, 0x01, 0xB0, 0x19, 0x90, 0xC1, 0x80, 0x15, 0x3D, 0x00, 0x10, 0xB0, 0x05, 0x90, 0x63, 0x80, 0x0C, 0x3D, 0x00, 0x30, 0xB0, 0x05, 0x90, 0x21, 0x80, 0x03, 0x90, 0x56, 0x52, 0xFC, 0x08, 0x7C, 0x2B, 0x4C, 0x38, 0xFF, 0x38, 0xFF, 0x20, 0x7F, 0x62, 0xD0, 0x04, 0x51, 0xB9, 0x62, 0xD0, 0x00, 0x67, 0x67, 0x67, 0x67, 0x15, 0x62
+		}
+	},
+	{
+		186,
+		79,
+		0x39,
+		0x00E5,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xE5, 0x67, 0x67, 0x21, 0x03, 0x7F, 0x50, 0x84, 0x08, 0x50, 0x01, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x7C, 0x6F, 0x64, 0x7C, 0x32, 0x06, 0x62, 0xD0, 0x00, 0x62, 0xE3, 0x38, 0x50, 0x01, 0x08, 0x7C, 0x32, 0xA7, 0x38, 0xFF, 0x62, 0xD0, 0x00, 0x39, 0x04, 0xA0, 0x10, 0x50, 0x00, 0x08, 0x7C, 0x32, 0xA7, 0x38, 0xFF, 0x62, 0xD0, 0x00, 0x39, 0x01, 0xAF, 0xDA, 0x7C, 0x6E, 0xE8, 0x7F, 0x10, 0x4F, 0xD5, 0xE3
+		}
+	},
+	{
+		187,
+		79,
+		0x39,
+		0x00E6,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xE6, 0x38, 0x02, 0x7C, 0x6F, 0x64, 0x56, 0x01, 0xFA, 0x56, 0x00, 0x00, 0x80, 0x36, 0x7C, 0x32, 0x06, 0x62, 0xD0, 0x00, 0x50, 0x00, 0x08, 0x7C, 0x32, 0xA7, 0x38, 0xFF, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x26, 0xE9, 0x80, 0x62, 0xD0, 0x04, 0x51, 0xB8, 0x62, 0xD0, 0x00, 0x3A, 0xE9, 0xA0, 0x03, 0x80, 0x1C, 0x10, 0x57, 0x03, 0x50, 0xE3, 0x7C, 0x2B, 0xA8, 0x20, 0x62, 0xE3, 0x38, 0x7B, 0x01, 0x1F, 0xA4, 0x82
+		}
+	},
+	{
+		188,
+		79,
+		0x39,
+		0x00E7,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xE7, 0x00, 0x00, 0x3D, 0x00, 0x00, 0xBF, 0xC7, 0x3D, 0x01, 0x00, 0xBF, 0xC2, 0x7C, 0x6E, 0xE8, 0x38, 0xFE, 0x20, 0x7F, 0x7C, 0x6F, 0x64, 0x10, 0x57, 0x01, 0x50, 0xF4, 0x7C, 0x2B, 0xA8, 0x20, 0x7C, 0x6E, 0xE8, 0x7C, 0x32, 0x06, 0x62, 0xD0, 0x00, 0x7F, 0x7C, 0x31, 0x77, 0x7F, 0x7C, 0x31, 0xC1, 0x7F, 0x43, 0x05, 0x08, 0x62, 0xD0, 0x00, 0x26, 0xB0, 0xFB, 0x51, 0xB0, 0x60, 0x00, 0x62, 0xDA, 0x4A, 0xCF
+		}
+	},
+	{
+		189,
+		79,
+		0x39,
+		0x00E8,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xE8, 0xEF, 0x43, 0xE0, 0x10, 0x7C, 0x31, 0x9D, 0x7F, 0x7C, 0x31, 0xB6, 0x7C, 0x73, 0x82, 0x41, 0x05, 0xF7, 0x62, 0xD0, 0x00, 0x51, 0xB0, 0x29, 0x04, 0x53, 0xB0, 0x51, 0xB0, 0x60, 0x00, 0x7F, 0x7F, 0x7F, 0x7F, 0x62, 0xD0, 0x00, 0x50, 0x00, 0x7F, 0x10, 0x4F, 0x20, 0x7F, 0x7F, 0x10, 0x4F, 0x20, 0x7F, 0x62, 0xD0, 0x04, 0x55, 0xC3, 0x00, 0x62, 0xD0, 0x04, 0x55, 0xC2, 0x00, 0x62, 0xD0, 0x04, 0x09, 0x4E
+		}
+	},
+	{
+		190,
+		79,
+		0x39,
+		0x00E9,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xE9, 0x55, 0xC1, 0x00, 0x7C, 0x3A, 0x1F, 0x10, 0x7C, 0x49, 0x5B, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x62, 0xD0, 0x03, 0x51, 0x9B, 0x21, 0xF0, 0x62, 0xD0, 0x04, 0x53, 0xC2, 0x7C, 0x6F, 0x2F, 0xA0, 0x06, 0x3D, 0x00, 0x30, 0xB0, 0x0B, 0x7C, 0x73, 0x26, 0x62, 0xD0, 0x00, 0x53, 0x39, 0x80, 0x07, 0x62, 0xD0, 0x00, 0x55, 0x39, 0x00, 0x62, 0xD0, 0x00, 0x51, 0x39, 0x62, 0xD0, 0x00, 0x53, 0x3A, 0x4C, 0xD5
+		}
+	},
+	{
+		191,
+		79,
+		0x39,
+		0x00EA,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xEA, 0x62, 0xD0, 0x00, 0x51, 0x39, 0x62, 0xD0, 0x00, 0x53, 0x3B, 0x62, 0xD0, 0x00, 0x51, 0x39, 0x62, 0xD0, 0x00, 0x53, 0x3C, 0x7C, 0x3A, 0x21, 0x38, 0xFF, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x02, 0x62, 0xD0, 0x04, 0x51, 0xCF, 0x54, 0x01, 0x10, 0x52, 0xFB, 0x7C, 0x49, 0x92, 0x20, 0x62, 0xD0, 0x04, 0x53, 0xC3, 0x3C, 0xC3, 0x00, 0xB0, 0x4F, 0x52, 0xFB, 0x3B, 0xFC, 0xA0, 0x49, 0x52, 0xFC, 0x3B, 0xFE, 0x3A
+		}
+	},
+	{
+		192,
+		79,
+		0x39,
+		0x00EB,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xEB, 0xFB, 0xD0, 0x09, 0x62, 0xD0, 0x04, 0x55, 0xC3, 0x2F, 0x80, 0x21, 0x62, 0xD0, 0x04, 0x55, 0xC3, 0x4F, 0x3D, 0xFB, 0x00, 0xB0, 0x16, 0x7C, 0x3A, 0x2C, 0x7C, 0x3A, 0x22, 0x62, 0xD0, 0x00, 0x39, 0x00, 0xA0, 0x09, 0x7C, 0x3A, 0x22, 0x62, 0xD0, 0x04, 0x53, 0xC3, 0x62, 0xD0, 0x04, 0x51, 0xC1, 0x62, 0xD0, 0x04, 0x3A, 0xC3, 0xB0, 0x0C, 0x62, 0xD0, 0x04, 0x52, 0x01, 0x01, 0x01, 0x53, 0xCF, 0x63, 0x05
+		}
+	},
+	{
+		193,
+		79,
+		0x39,
+		0x00EC,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xEC, 0x80, 0x04, 0x7C, 0x70, 0x5B, 0x62, 0xD0, 0x04, 0x3C, 0xC3, 0x00, 0xA0, 0x09, 0x52, 0xFB, 0x08, 0x7C, 0x3A, 0x28, 0x38, 0xFF, 0x62, 0xD0, 0x04, 0x51, 0xC3, 0x08, 0x52, 0xFB, 0x08, 0x91, 0x86, 0x38, 0xFE, 0x39, 0x00, 0xA0, 0x21, 0x62, 0xD0, 0x04, 0x55, 0xC3, 0xFF, 0x62, 0xD0, 0x04, 0x51, 0xC1, 0x62, 0xD0, 0x04, 0x3A, 0xC3, 0xB0, 0x0C, 0x62, 0xD0, 0x04, 0x52, 0x01, 0x01, 0x01, 0x53, 0x63, 0x06
+		}
+	},
+	{
+		194,
+		79,
+		0x39,
+		0x00ED,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xED, 0xCF, 0x80, 0x04, 0x7C, 0x70, 0x5B, 0x62, 0xD0, 0x04, 0x3C, 0xC3, 0x00, 0xA0, 0x06, 0x56, 0x00, 0x01, 0x80, 0x04, 0x56, 0x00, 0x00, 0x52, 0xFB, 0x08, 0x52, 0xFC, 0x08, 0x50, 0x04, 0x08, 0x50, 0xC3, 0x08, 0x62, 0xD0, 0x00, 0x50, 0x0F, 0x08, 0x10, 0x7C, 0x2B, 0x40, 0x38, 0xFA, 0x62, 0xD0, 0x04, 0x3C, 0xC3, 0x00, 0xA0, 0x2B, 0x90, 0x31, 0x62, 0xD0, 0x04, 0x3C, 0xC3, 0x00, 0xA0, 0x21, 0x41, 0xC3
+		}
+	},
+	{
+		195,
+		79,
+		0x39,
+		0x00EE,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xEE, 0x62, 0xD0, 0x04, 0x3C, 0xCF, 0x00, 0xB0, 0x0F, 0x62, 0xD0, 0x04, 0x3C, 0xC3, 0xFF, 0xA0, 0x07, 0x62, 0xD0, 0x04, 0x55, 0xCF, 0x80, 0x62, 0xD0, 0x04, 0x51, 0xC3, 0x62, 0xD0, 0x04, 0x53, 0xC1, 0x52, 0x00, 0x62, 0xD0, 0x00, 0x38, 0xFE, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x62, 0xD0, 0x04, 0x51, 0xC3, 0x54, 0x00, 0x3C, 0xC3, 0x00, 0xA0, 0x0B, 0x7C, 0x6F, 0x3C, 0x62, 0xD0, 0x00, 0x39, 0x14, 0x6A
+		}
+	},
+	{
+		196,
+		79,
+		0x39,
+		0x00EF,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xEF, 0x30, 0xB0, 0x03, 0x80, 0xB1, 0x50, 0x10, 0x08, 0x50, 0x29, 0x08, 0x50, 0x28, 0x08, 0x90, 0xA9, 0x50, 0x20, 0x08, 0x50, 0x3F, 0x08, 0x50, 0x30, 0x08, 0x90, 0x9E, 0x38, 0xFA, 0x50, 0x40, 0x08, 0x50, 0x49, 0x08, 0x50, 0x48, 0x08, 0x90, 0x91, 0x50, 0x80, 0x08, 0x50, 0x9F, 0x08, 0x50, 0x90, 0x08, 0x90, 0x86, 0x38, 0xFA, 0x62, 0xD0, 0x04, 0x51, 0xC3, 0x3B, 0x00, 0xA0, 0x6E, 0x3D, 0x00, 0x76, 0x2F
+		}
+	},
+	{
+		197,
+		79,
+		0x39,
+		0x00F0,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xF0, 0x28, 0xC0, 0x69, 0x50, 0x29, 0x3B, 0x00, 0xC0, 0x63, 0x62, 0xD0, 0x04, 0x51, 0xDF, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x65, 0xE9, 0x51, 0xE9, 0x01, 0x10, 0x62, 0xD0, 0x04, 0x53, 0xC3, 0x62, 0xD0, 0x04, 0x51, 0xC1, 0x62, 0xD0, 0x04, 0x3A, 0xC3, 0xB0, 0x0F, 0x62, 0xD0, 0x04, 0x51, 0xBF, 0x01, 0x01, 0x62, 0xD0, 0x04, 0x53, 0xCF, 0x80, 0x04, 0x7C, 0x70, 0x5B, 0x50, 0x10, 0x08, 0x50, 0x29, 0x67, 0x12
+		}
+	},
+	{
+		198,
+		79,
+		0x39,
+		0x00F1,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xF1, 0x08, 0x50, 0x28, 0x08, 0x90, 0x33, 0x50, 0x20, 0x08, 0x50, 0x3F, 0x08, 0x50, 0x30, 0x08, 0x90, 0x28, 0x38, 0xFA, 0x50, 0x40, 0x08, 0x50, 0x49, 0x08, 0x50, 0x48, 0x08, 0x90, 0x1B, 0x50, 0x80, 0x08, 0x50, 0x9F, 0x08, 0x50, 0x90, 0x08, 0x90, 0x10, 0x38, 0xFA, 0x62, 0xD0, 0x04, 0x51, 0xCF, 0x62, 0xD0, 0x04, 0x53, 0xBF, 0x38, 0xFF, 0x20, 0x7F, 0x10, 0x4F, 0x62, 0xD0, 0x04, 0x51, 0xC3, 0xF6, 0x31
+		}
+	},
+	{
+		199,
+		79,
+		0x39,
+		0x00F2,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xF2, 0x3B, 0xFC, 0xC0, 0x21, 0x62, 0xD0, 0x04, 0x52, 0xFB, 0x3A, 0xC3, 0xC0, 0x18, 0x62, 0xD0, 0x04, 0x51, 0xC2, 0x23, 0xFA, 0x39, 0x00, 0xB0, 0x0D, 0x62, 0xD0, 0x04, 0x55, 0xC3, 0x00, 0x62, 0xD0, 0x04, 0x55, 0xCF, 0x00, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x0A, 0x50, 0x02, 0x3B, 0xFC, 0xD1, 0xA6, 0x7C, 0x73, 0x26, 0x54, 0x03, 0x56, 0x00, 0x00, 0x80, 0xCA, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x80, 0x46
+		}
+	},
+	{
+		200,
+		79,
+		0x39,
+		0x00F3,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xF3, 0x06, 0xE8, 0xC0, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x7C, 0x6D, 0x8A, 0x51, 0xE8, 0x01, 0xE0, 0x54, 0x02, 0x51, 0xE9, 0x09, 0x01, 0x54, 0x01, 0x7C, 0x6D, 0xA5, 0x65, 0xE8, 0x6B, 0xE9, 0x06, 0xE8, 0xDD, 0x0E, 0xE9, 0x02, 0x7C, 0x6D, 0x83, 0x7C, 0x6E, 0xE1, 0x52, 0x02, 0x53, 0xE6, 0x52, 0x01, 0x60, 0xD4, 0x3E, 0xE6, 0x53, 0xE6, 0x50, 0x00, 0x3A, 0xE9, 0xB0, 0x07, 0x51, 0xE6, 0x3A, 0xC6, 0xD3
+		}
+	},
+	{
+		201,
+		79,
+		0x39,
+		0x00F4,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xF4, 0xE8, 0xA0, 0x03, 0x80, 0x84, 0x62, 0xD0, 0x00, 0x52, 0x02, 0x01, 0x06, 0x7C, 0x6E, 0xA3, 0x54, 0x04, 0x3E, 0xE8, 0x54, 0x05, 0x52, 0x02, 0x01, 0x04, 0x7C, 0x6E, 0xA3, 0x54, 0x06, 0x3E, 0xE8, 0x54, 0x07, 0x7C, 0x6D, 0xA5, 0x65, 0xE8, 0x6B, 0xE9, 0x06, 0xE8, 0xF5, 0x0E, 0xE9, 0x02, 0x7C, 0x6D, 0x83, 0x7C, 0x6E, 0xE1, 0x51, 0xE9, 0x08, 0x51, 0xE8, 0x08, 0x52, 0x04, 0x08, 0x52, 0x05, 0xBB, 0xBE
+		}
+	},
+	{
+		202,
+		79,
+		0x39,
+		0x00F5,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xF5, 0x08, 0x91, 0x1B, 0x38, 0xFC, 0x62, 0xD0, 0x00, 0x52, 0x03, 0x7C, 0x71, 0xD1, 0xC0, 0x31, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x65, 0xE8, 0x6B, 0xE9, 0x06, 0xE8, 0xA1, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x7C, 0x6E, 0xE1, 0x51, 0xE9, 0x08, 0x51, 0xE8, 0x08, 0x52, 0x06, 0x08, 0x52, 0x07, 0x08, 0x90, 0xEB, 0x38, 0xFC, 0x62, 0xD0, 0x00, 0x52, 0x03, 0x7C, 0x71, 0xD1, 0xD0, 0x03, 0x80, 0xCA, 0xDD
+		}
+	},
+	{
+		203,
+		79,
+		0x39,
+		0x00F6,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xF6, 0x08, 0x77, 0x00, 0x7C, 0x72, 0x41, 0xCF, 0x33, 0x50, 0x04, 0x3B, 0xFC, 0xD0, 0x09, 0x62, 0xD0, 0x04, 0x55, 0xC0, 0x04, 0x80, 0x08, 0x52, 0xFC, 0x62, 0xD0, 0x04, 0x53, 0xC0, 0x7C, 0x72, 0x41, 0xA0, 0xAD, 0x56, 0x00, 0x00, 0x80, 0x89, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0xC0, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x7C, 0x6D, 0x8A, 0x51, 0xE8, 0x01, 0xE0, 0x54, 0x02, 0x51, 0x65, 0x14
+		}
+	},
+	{
+		204,
+		79,
+		0x39,
+		0x00F7,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xF7, 0xE9, 0x09, 0x01, 0x54, 0x01, 0x7C, 0x6F, 0xF6, 0x53, 0xE8, 0x7C, 0x6E, 0xAE, 0x65, 0xE6, 0x6B, 0xE7, 0x06, 0xE6, 0xDD, 0x0E, 0xE7, 0x02, 0x51, 0xE7, 0x60, 0xD5, 0x50, 0x00, 0x3F, 0xE6, 0x51, 0xE8, 0x3F, 0xE6, 0x52, 0x02, 0x01, 0x06, 0x53, 0xE8, 0x52, 0x01, 0x09, 0x00, 0x7C, 0x6D, 0xE3, 0x3E, 0xE8, 0x53, 0xE8, 0x7C, 0x6E, 0xAE, 0x65, 0xE6, 0x6B, 0xE7, 0x06, 0xE6, 0xF5, 0x0E, 0xE7, 0xC3, 0xD1
+		}
+	},
+	{
+		205,
+		79,
+		0x39,
+		0x00F8,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xF8, 0x02, 0x7C, 0x6D, 0xEA, 0x51, 0xE8, 0x3F, 0xE6, 0x52, 0x02, 0x01, 0x04, 0x53, 0xE8, 0x52, 0x01, 0x09, 0x00, 0x7C, 0x6D, 0xE3, 0x3E, 0xE8, 0x53, 0xE8, 0x7C, 0x6E, 0xAE, 0x65, 0xE6, 0x6B, 0xE7, 0x06, 0xE6, 0xA1, 0x0E, 0xE7, 0x03, 0x7C, 0x6D, 0xEA, 0x51, 0xE8, 0x3F, 0xE6, 0x77, 0x00, 0x7C, 0x72, 0x41, 0xCF, 0x74, 0x3D, 0xFB, 0x00, 0xB0, 0x09, 0x56, 0x09, 0x01, 0x56, 0x08, 0x00, 0x80, 0x53, 0xF2
+		}
+	},
+	{
+		206,
+		79,
+		0x39,
+		0x00F9,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xF9, 0x07, 0x56, 0x09, 0x00, 0x56, 0x08, 0x00, 0x62, 0xD0, 0x00, 0x52, 0x09, 0x80, 0x0D, 0x62, 0xD0, 0x00, 0x50, 0x00, 0x80, 0x06, 0x62, 0xD0, 0x00, 0x50, 0x00, 0x38, 0xF6, 0x20, 0x7F, 0x10, 0x4F, 0x52, 0xFC, 0x13, 0xFA, 0x52, 0xFB, 0x1B, 0xF9, 0xC0, 0x12, 0x62, 0xD0, 0x00, 0x52, 0xFC, 0x13, 0xFA, 0x53, 0xE8, 0x52, 0xFB, 0x1B, 0xF9, 0x53, 0xE9, 0x80, 0x10, 0x62, 0xD0, 0x00, 0x52, 0xFA, 0x95, 0x77
+		}
+	},
+	{
+		207,
+		79,
+		0x39,
+		0x00FA,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xFA, 0x13, 0xFC, 0x53, 0xE8, 0x52, 0xF9, 0x1B, 0xFB, 0x53, 0xE9, 0x20, 0x7F, 0x10, 0x4F, 0x7C, 0x72, 0x93, 0xB0, 0x22, 0x3D, 0xFC, 0x01, 0xB0, 0x32, 0x62, 0xD0, 0x04, 0x51, 0xCF, 0x08, 0x50, 0x0E, 0x08, 0x7C, 0x32, 0x0C, 0x62, 0xD0, 0x04, 0x51, 0xC3, 0x08, 0x50, 0x0F, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x80, 0x16, 0x7C, 0x6F, 0x3C, 0x39, 0x30, 0xB0, 0x0F, 0x62, 0xD0, 0x04, 0x51, 0xC3, 0xA8, 0x9E
+		}
+	},
+	{
+		208,
+		79,
+		0x39,
+		0x00FB,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xFB, 0x08, 0x50, 0x24, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x52, 0xFC, 0x08, 0x7C, 0x3A, 0x2D, 0x38, 0xFF, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x02, 0x62, 0xE3, 0x38, 0x7C, 0x2B, 0x06, 0x71, 0x01, 0x90, 0x67, 0x90, 0xC5, 0x80, 0x5D, 0x62, 0xD0, 0x00, 0x26, 0xAE, 0xFB, 0x51, 0xAE, 0x60, 0x00, 0x7C, 0x70, 0x4B, 0x51, 0xAE, 0x29, 0x04, 0x53, 0xAE, 0x51, 0xAE, 0x60, 0x00, 0x7C, 0x70, 0x4B, 0x26, 0x61, 0x11
+		}
+	},
+	{
+		209,
+		79,
+		0x39,
+		0x00FC,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xFC, 0xAF, 0xFD, 0x51, 0xAF, 0x60, 0x04, 0x7C, 0x70, 0x4B, 0x51, 0xAF, 0x29, 0x02, 0x7C, 0x6F, 0x20, 0x56, 0x01, 0x00, 0x80, 0x03, 0x77, 0x01, 0x3D, 0x01, 0x0A, 0xCF, 0xFA, 0x62, 0xE3, 0x38, 0x90, 0x9D, 0x62, 0xD0, 0x04, 0x3C, 0xC4, 0x00, 0xA0, 0x0C, 0x7C, 0x45, 0x53, 0x7C, 0x2C, 0x1E, 0x62, 0xD0, 0x00, 0x54, 0x00, 0x52, 0x00, 0x08, 0x90, 0x9B, 0x52, 0x00, 0x08, 0x7C, 0x39, 0x02, 0x38, 0x9A, 0x84
+		}
+	},
+	{
+		210,
+		79,
+		0x39,
+		0x00FD,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xFD, 0xFE, 0x8F, 0xA3, 0x38, 0xFE, 0x20, 0x8F, 0xFF, 0x10, 0x4F, 0x38, 0x01, 0x10, 0x7C, 0x16, 0x34, 0x20, 0x10, 0x57, 0x13, 0x50, 0x88, 0x7C, 0x2B, 0xA8, 0x20, 0x62, 0xD0, 0x04, 0x55, 0xC4, 0x01, 0x62, 0xD0, 0x00, 0x55, 0xA7, 0x0C, 0x62, 0xD0, 0x00, 0x55, 0xA8, 0x05, 0x62, 0xD0, 0x00, 0x55, 0xA9, 0x01, 0x10, 0x7C, 0x17, 0xB7, 0x7C, 0x19, 0x82, 0x62, 0xD0, 0x00, 0x20, 0x39, 0x00, 0xA0, 0xEE, 0x2D
+		}
+	},
+	{
+		211,
+		79,
+		0x39,
+		0x00FE,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xFE, 0x1F, 0x71, 0x10, 0x5D, 0xE0, 0x54, 0x00, 0x41, 0xE0, 0xE7, 0x43, 0xE0, 0x18, 0x70, 0xCF, 0x62, 0xE3, 0x38, 0x7C, 0x49, 0x07, 0x62, 0xE3, 0x38, 0x52, 0x00, 0x7C, 0x72, 0xB2, 0x80, 0x06, 0x10, 0x7C, 0x10, 0x74, 0x20, 0x38, 0xFF, 0x20, 0x7F, 0x7C, 0x33, 0x69, 0x7C, 0x40, 0x59, 0x7C, 0x45, 0x51, 0x7C, 0x2B, 0xCA, 0x7C, 0x3A, 0x31, 0x7C, 0x40, 0x10, 0x7C, 0x2B, 0x19, 0x7F, 0x7C, 0x40, 0x55, 0xFC
+		}
+	},
+	{
+		212,
+		79,
+		0x39,
+		0x00FF,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xFF, 0x98, 0x7C, 0x36, 0x78, 0x7C, 0x45, 0x52, 0x7C, 0x2B, 0xFD, 0x7C, 0x3A, 0x4C, 0x7C, 0x40, 0x1E, 0x7C, 0x2B, 0x23, 0x7F, 0x10, 0x4F, 0x52, 0xFC, 0x08, 0x7C, 0x38, 0x88, 0x52, 0xFC, 0x08, 0x7C, 0x45, 0x4D, 0x38, 0xFE, 0x52, 0xFC, 0x08, 0x7C, 0x45, 0x76, 0x52, 0xFC, 0x08, 0x7C, 0x2E, 0x27, 0x38, 0xFE, 0x52, 0xFC, 0x08, 0x7C, 0x3E, 0x8C, 0x52, 0xFC, 0x08, 0x7C, 0x40, 0x28, 0x38, 0xFE, 0x1F, 0x91
+		}
+	},
+	{
+		213,
+		79,
+		0x39,
+		0x0100,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x00, 0x52, 0xFC, 0x08, 0x7C, 0x2B, 0x48, 0x52, 0xFC, 0x08, 0x7C, 0x38, 0xC9, 0x38, 0xFE, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x62, 0xD0, 0x04, 0x55, 0xC5, 0x04, 0x38, 0xFF, 0x20, 0x7F, 0x7F, 0x62, 0xD0, 0x04, 0x51, 0xC5, 0x62, 0xD0, 0x00, 0x7F, 0x10, 0x4F, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x02, 0x62, 0xD0, 0x00, 0x51, 0x54, 0x54, 0x01, 0x51, 0x53, 0x54, 0x00, 0x70, 0xFE, 0x10, 0x7C, 0x1E, 0xFE, 0x51
+		}
+	},
+	{
+		214,
+		79,
+		0x39,
+		0x0101,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x01, 0x1C, 0x62, 0xD0, 0x00, 0x20, 0x10, 0x52, 0x00, 0x08, 0x52, 0x01, 0x20, 0x7C, 0x1D, 0xD4, 0x20, 0x62, 0xDA, 0xF7, 0x71, 0x01, 0x38, 0xFE, 0x20, 0x7F, 0x10, 0x7C, 0x1E, 0x1C, 0x20, 0x70, 0xFE, 0x62, 0xD0, 0x00, 0x55, 0x0D, 0x00, 0x55, 0x0C, 0x00, 0x7C, 0x72, 0x19, 0x92, 0x21, 0x7C, 0x73, 0x6D, 0x10, 0x7C, 0x1D, 0xCC, 0x7C, 0x20, 0x80, 0x7C, 0x20, 0x78, 0x20, 0x62, 0xD0, 0x00, 0x55, 0xFD, 0x50
+		}
+	},
+	{
+		215,
+		79,
+		0x39,
+		0x0102,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x02, 0xEF, 0x00, 0x55, 0xEE, 0x00, 0x62, 0xD0, 0x03, 0x55, 0xC8, 0x00, 0x55, 0xC9, 0x00, 0x55, 0xCA, 0x00, 0x55, 0xCB, 0x00, 0x7C, 0x2B, 0x67, 0x7F, 0x10, 0x4F, 0x38, 0x03, 0x7C, 0x72, 0x93, 0xB0, 0xF9, 0x7C, 0x70, 0xE8, 0x54, 0x01, 0x51, 0x0C, 0x54, 0x00, 0x71, 0x01, 0x62, 0xD0, 0x03, 0x47, 0x99, 0x02, 0xA0, 0x3F, 0x93, 0x58, 0x9F, 0x74, 0x7C, 0x70, 0xE8, 0x08, 0x51, 0x0C, 0x62, 0xD0, 0xDD, 0x11
+		}
+	},
+	{
+		216,
+		79,
+		0x39,
+		0x0103,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x03, 0x00, 0x53, 0xEE, 0x18, 0x53, 0xEF, 0x71, 0x01, 0x62, 0xD0, 0x03, 0x55, 0xC8, 0x00, 0x55, 0xC9, 0x00, 0x55, 0xCA, 0x00, 0x55, 0xCB, 0x00, 0x7C, 0x2B, 0x67, 0x10, 0x7C, 0x18, 0x83, 0x20, 0x10, 0x57, 0x13, 0x50, 0x88, 0x7C, 0x2B, 0xA8, 0x7C, 0x10, 0x74, 0x20, 0x91, 0x3B, 0x7C, 0x6F, 0xEA, 0x81, 0x33, 0x62, 0xD0, 0x03, 0x51, 0x9A, 0x21, 0x0F, 0x54, 0x02, 0x62, 0xD0, 0x04, 0x3C, 0xCB, 0x0A, 0x6C
+		}
+	},
+	{
+		217,
+		79,
+		0x39,
+		0x0104,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x04, 0xF1, 0xB0, 0x45, 0x62, 0xD0, 0x03, 0x3C, 0x9C, 0x00, 0xA0, 0x03, 0x93, 0x67, 0x3D, 0x02, 0x00, 0xA0, 0x06, 0x7C, 0x6F, 0xEA, 0x80, 0x97, 0x7C, 0x73, 0x2E, 0xA0, 0x27, 0x62, 0xD0, 0x03, 0x52, 0x01, 0x12, 0xE5, 0x62, 0xD0, 0x00, 0x53, 0xE8, 0x52, 0x00, 0x62, 0xD0, 0x03, 0x1A, 0xE4, 0x7C, 0x72, 0x49, 0x62, 0xD0, 0x03, 0x12, 0xDB, 0x7C, 0x70, 0xA1, 0x1A, 0xDA, 0xC0, 0x70, 0x91, 0x2A, 0x7B, 0x4F
+		}
+	},
+	{
+		218,
+		79,
+		0x39,
+		0x0105,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x05, 0x80, 0x6C, 0x7C, 0x6F, 0xEA, 0x80, 0x67, 0x62, 0xD0, 0x04, 0x3C, 0xCB, 0xF2, 0xB0, 0x1E, 0x3D, 0x02, 0x00, 0xB0, 0x06, 0x7C, 0x73, 0x2E, 0xB0, 0x08, 0x90, 0xCD, 0x7C, 0x6F, 0xEA, 0x80, 0x4E, 0x62, 0xD0, 0x03, 0x3C, 0x9D, 0x00, 0xA0, 0x46, 0x93, 0x0A, 0x80, 0x42, 0x9E, 0xBE, 0x7C, 0x70, 0xE8, 0x08, 0x51, 0x0C, 0x62, 0xD0, 0x00, 0x53, 0xEE, 0x18, 0x53, 0xEF, 0x71, 0x01, 0x62, 0xD0, 0xC8, 0xEA
+		}
+	},
+	{
+		219,
+		79,
+		0x39,
+		0x0106,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x06, 0x03, 0x55, 0xC8, 0x00, 0x55, 0xC9, 0x00, 0x55, 0xCA, 0x00, 0x55, 0xCB, 0x00, 0x7C, 0x2B, 0x67, 0x90, 0xFD, 0x90, 0x94, 0x7C, 0x6F, 0xEA, 0x80, 0x15, 0x62, 0xD0, 0x04, 0x3C, 0xCB, 0xF4, 0xA0, 0x0D, 0x10, 0x57, 0x00, 0x50, 0x01, 0x7C, 0x1D, 0xD4, 0x20, 0x7C, 0x73, 0x6D, 0x7C, 0x72, 0x93, 0xB0, 0x70, 0x7C, 0x70, 0xE8, 0x54, 0x01, 0x51, 0x0C, 0x54, 0x00, 0x71, 0x01, 0x62, 0xD0, 0x00, 0x44, 0xE3
+		}
+	},
+	{
+		220,
+		79,
+		0x39,
+		0x0107,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x07, 0x52, 0x01, 0x12, 0xEF, 0x62, 0xD0, 0x00, 0x53, 0xE8, 0x52, 0x00, 0x62, 0xD0, 0x00, 0x1A, 0xEE, 0x7C, 0x72, 0x49, 0x53, 0xE6, 0x51, 0xE9, 0x53, 0xE7, 0x51, 0xE6, 0x62, 0xD0, 0x03, 0x04, 0xCB, 0x62, 0xD0, 0x00, 0x51, 0xE7, 0x62, 0xD0, 0x03, 0x0C, 0xCA, 0x0E, 0xC9, 0x00, 0x0E, 0xC8, 0x00, 0x62, 0xD0, 0x00, 0x52, 0x01, 0x53, 0xEF, 0x52, 0x00, 0x53, 0xEE, 0x62, 0xD0, 0x03, 0x51, 0xCB, 0x39, 0xCE
+		}
+	},
+	{
+		221,
+		79,
+		0x39,
+		0x0108,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x08, 0x11, 0x30, 0x51, 0xCA, 0x19, 0x75, 0x51, 0xC9, 0x19, 0x00, 0x51, 0xC8, 0x19, 0x00, 0xC0, 0x12, 0x9E, 0x1A, 0x62, 0xD0, 0x03, 0x55, 0xC8, 0x00, 0x55, 0xC9, 0x00, 0x55, 0xCA, 0x00, 0x55, 0xCB, 0x00, 0x7C, 0x2B, 0x68, 0x38, 0xFD, 0x20, 0x7F, 0x62, 0xD0, 0x04, 0x55, 0xCB, 0xF1, 0x62, 0xD0, 0x03, 0x51, 0x9C, 0x08, 0x62, 0xD0, 0x03, 0x55, 0xD8, 0x00, 0x18, 0x53, 0xD9, 0x62, 0xD0, 0x03, 0xB7, 0xCB
+		}
+	},
+	{
+		222,
+		79,
+		0x39,
+		0x0109,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x09, 0x3C, 0x9C, 0x00, 0xA0, 0x14, 0x10, 0x62, 0xD0, 0x03, 0x51, 0xD8, 0x08, 0x51, 0xD9, 0x20, 0x7C, 0x1D, 0xD4, 0x20, 0x7C, 0x70, 0xAA, 0x80, 0x0F, 0x10, 0x57, 0x00, 0x50, 0x01, 0x7C, 0x1D, 0xD4, 0x20, 0x70, 0xFE, 0x7C, 0x72, 0x19, 0x7C, 0x71, 0x54, 0x7F, 0x62, 0xD0, 0x04, 0x55, 0xCB, 0xF2, 0x62, 0xD0, 0x03, 0x51, 0xD7, 0x08, 0x51, 0xD6, 0x62, 0xD0, 0x03, 0x53, 0xD8, 0x18, 0x53, 0xD9, 0xF0, 0x3E
+		}
+	},
+	{
+		223,
+		79,
+		0x39,
+		0x010A,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x0A, 0x10, 0x51, 0xD8, 0x08, 0x51, 0xD9, 0x20, 0x7C, 0x1D, 0xD4, 0x20, 0x7C, 0x70, 0xAA, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x62, 0xD0, 0x03, 0x50, 0x78, 0x3A, 0x9D, 0xD0, 0x07, 0x62, 0xD0, 0x03, 0x55, 0x9D, 0x78, 0x7C, 0x71, 0xD8, 0x53, 0xE9, 0x65, 0xE9, 0x7C, 0x71, 0xD8, 0x64, 0x64, 0x64, 0x02, 0xE9, 0x54, 0x00, 0x80, 0x09, 0x62, 0xD0, 0x03, 0x76, 0x9D, 0x07, 0x00, 0x0A, 0x62, 0xD0, 0x03, 0xA5, 0xA9
+		}
+	},
+	{
+		224,
+		79,
+		0x39,
+		0x010B,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x0B, 0x3C, 0x9D, 0x1A, 0xD0, 0x0A, 0x62, 0xD0, 0x03, 0x52, 0x00, 0x3A, 0x9C, 0xCF, 0xE8, 0x62, 0xD0, 0x03, 0x51, 0x9D, 0x08, 0x62, 0xD0, 0x03, 0x55, 0xD6, 0x00, 0x18, 0x53, 0xD7, 0x62, 0xD0, 0x03, 0x51, 0x9D, 0x7C, 0x70, 0x0E, 0x7C, 0x6D, 0x9C, 0x62, 0xD0, 0x03, 0x51, 0xD7, 0x62, 0xD0, 0x00, 0x04, 0xE8, 0x62, 0xD0, 0x03, 0x51, 0xD6, 0x62, 0xD0, 0x00, 0x0C, 0xE9, 0x7C, 0x70, 0x17, 0x08, 0x59, 0x12
+		}
+	},
+	{
+		225,
+		79,
+		0x39,
+		0x010C,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x0C, 0x51, 0xE9, 0x62, 0xD0, 0x03, 0x53, 0xD6, 0x18, 0x53, 0xD7, 0x62, 0xD0, 0x03, 0x51, 0x9E, 0x08, 0x62, 0xD0, 0x03, 0x55, 0xDA, 0x00, 0x18, 0x53, 0xDB, 0x51, 0xDB, 0x08, 0x51, 0xDA, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x18, 0x53, 0xE8, 0x7C, 0x6D, 0x9C, 0x62, 0xD0, 0x03, 0x51, 0xDB, 0x62, 0xD0, 0x00, 0x04, 0xE8, 0x62, 0xD0, 0x03, 0x51, 0xDA, 0x62, 0xD0, 0x00, 0x0C, 0xE9, 0x7C, 0x70, 0x17, 0x5E, 0x1D
+		}
+	},
+	{
+		226,
+		79,
+		0x39,
+		0x010D,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x0D, 0x08, 0x51, 0xE9, 0x62, 0xD0, 0x03, 0x53, 0xDA, 0x18, 0x53, 0xDB, 0x38, 0xFF, 0x20, 0x7F, 0x62, 0xD0, 0x00, 0x51, 0xAE, 0x21, 0x0D, 0x60, 0x00, 0x62, 0xD0, 0x00, 0x51, 0xAF, 0x21, 0xBB, 0x60, 0x04, 0x62, 0xD0, 0x00, 0x51, 0xB0, 0x21, 0x74, 0x7C, 0x73, 0x1E, 0x21, 0x00, 0x7C, 0x73, 0x16, 0x21, 0x10, 0x60, 0x10, 0x71, 0x10, 0x5D, 0x00, 0x70, 0xCF, 0x62, 0xD0, 0x04, 0x53, 0xCA, 0x71, 0x6E, 0x3E
+		}
+	},
+	{
+		227,
+		79,
+		0x39,
+		0x010E,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x0E, 0x10, 0x5D, 0x04, 0x70, 0xCF, 0x62, 0xD0, 0x04, 0x53, 0xC9, 0x71, 0x10, 0x5D, 0x08, 0x70, 0xCF, 0x62, 0xD0, 0x04, 0x53, 0xC8, 0x71, 0x10, 0x5D, 0x0C, 0x70, 0xCF, 0x62, 0xD0, 0x04, 0x53, 0xC7, 0x71, 0x10, 0x5D, 0x10, 0x70, 0xCF, 0x62, 0xD0, 0x04, 0x53, 0xC6, 0x71, 0x10, 0x43, 0x00, 0xF2, 0x43, 0x04, 0x44, 0x43, 0x08, 0x8B, 0x43, 0x0C, 0xFF, 0x43, 0x10, 0xEF, 0x70, 0xCF, 0x7F, 0x62, 0x34, 0xCB
+		}
+	},
+	{
+		228,
+		79,
+		0x39,
+		0x010F,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x0F, 0xD0, 0x04, 0x51, 0xCA, 0x71, 0x10, 0x60, 0x00, 0x70, 0xCF, 0x62, 0xD0, 0x04, 0x51, 0xC9, 0x71, 0x10, 0x60, 0x04, 0x70, 0xCF, 0x62, 0xD0, 0x04, 0x51, 0xC8, 0x71, 0x10, 0x60, 0x08, 0x70, 0xCF, 0x62, 0xD0, 0x04, 0x51, 0xC7, 0x71, 0x10, 0x60, 0x0C, 0x70, 0xCF, 0x62, 0xD0, 0x04, 0x51, 0xC6, 0x71, 0x10, 0x60, 0x10, 0x70, 0xCF, 0x62, 0xD0, 0x00, 0x51, 0xAE, 0x60, 0x00, 0x62, 0xD0, 0x00, 0xB4, 0xCC
+		}
+	},
+	{
+		229,
+		79,
+		0x39,
+		0x0110,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x10, 0x7C, 0x72, 0x51, 0x51, 0xB0, 0x7C, 0x73, 0x1E, 0x7C, 0x73, 0x16, 0x60, 0x10, 0x7F, 0x10, 0x4F, 0x38, 0x02, 0x71, 0x10, 0x43, 0xEC, 0x01, 0x70, 0xFE, 0x70, 0xCF, 0x9F, 0x32, 0x7C, 0x31, 0xC5, 0x62, 0xD0, 0x00, 0x39, 0x00, 0xB0, 0x3A, 0x7C, 0x72, 0xCB, 0x10, 0x70, 0xCF, 0x7C, 0x1D, 0xD0, 0x7C, 0x20, 0x7C, 0x20, 0x62, 0xDB, 0xFE, 0x7C, 0x39, 0xF1, 0x71, 0x10, 0x43, 0xD7, 0x20, 0x43, 0x58, 0x15
+		}
+	},
+	{
+		230,
+		79,
+		0x39,
+		0x0111,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x11, 0xC9, 0x80, 0x70, 0xCF, 0x43, 0xFF, 0x08, 0x71, 0x10, 0x41, 0xC9, 0x7F, 0x41, 0xD7, 0xDF, 0x40, 0x7C, 0x73, 0x7B, 0x70, 0xCF, 0x7C, 0x3A, 0x08, 0x10, 0x7C, 0x20, 0x78, 0x7C, 0x1D, 0xCC, 0x20, 0x62, 0xD0, 0x00, 0x55, 0xBB, 0xFF, 0x62, 0xD0, 0x03, 0x26, 0x99, 0xFD, 0x9F, 0x51, 0x71, 0x01, 0x38, 0xFE, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x02, 0x56, 0x00, 0x00, 0x71, 0x10, 0x41, 0xEC, 0xFE, 0x27, 0xB4
+		}
+	},
+	{
+		231,
+		79,
+		0x39,
+		0x0112,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x12, 0x70, 0xFE, 0x70, 0xCF, 0x9E, 0xC9, 0x7C, 0x71, 0x10, 0x80, 0x6B, 0x7C, 0x32, 0x06, 0x62, 0xD0, 0x00, 0x7C, 0x31, 0xC5, 0x39, 0x00, 0xB0, 0x5A, 0x3D, 0x00, 0x00, 0xB0, 0x55, 0x62, 0xD0, 0x00, 0x3C, 0xBB, 0xFF, 0xA0, 0x4D, 0x7C, 0x39, 0xE9, 0x62, 0xD0, 0x03, 0x51, 0xD9, 0x11, 0x02, 0x62, 0xD0, 0x00, 0x53, 0xE8, 0x62, 0xD0, 0x03, 0x51, 0xD8, 0x19, 0x00, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x50, 0x07
+		}
+	},
+	{
+		232,
+		79,
+		0x39,
+		0x0113,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x13, 0x62, 0xD0, 0x00, 0x51, 0xBB, 0x62, 0xD0, 0x00, 0x7C, 0x70, 0xBF, 0xC0, 0x22, 0x7C, 0x72, 0xCB, 0x56, 0x00, 0x01, 0x10, 0x70, 0xCF, 0x7C, 0x20, 0x7C, 0x20, 0x62, 0xDB, 0xFE, 0x10, 0x7C, 0x0C, 0x80, 0x20, 0x71, 0x10, 0x7C, 0x73, 0x7B, 0x10, 0x70, 0xCF, 0x7C, 0x20, 0x78, 0x20, 0x7C, 0x39, 0xED, 0x71, 0x01, 0x70, 0xFE, 0x62, 0xD0, 0x00, 0x51, 0x0D, 0x62, 0xD0, 0x03, 0x12, 0xD5, 0x62, 0x05, 0x72
+		}
+	},
+	{
+		233,
+		79,
+		0x39,
+		0x0114,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x14, 0xD0, 0x00, 0x53, 0xE8, 0x62, 0xD0, 0x00, 0x51, 0x0C, 0x62, 0xD0, 0x03, 0x1A, 0xD4, 0x7C, 0x72, 0x49, 0x62, 0xD0, 0x03, 0x12, 0xD9, 0x7C, 0x70, 0xA1, 0x1A, 0xD8, 0xCF, 0x6F, 0x52, 0x01, 0x7C, 0x72, 0xB2, 0x9E, 0x9B, 0x7C, 0x71, 0x54, 0x71, 0x01, 0x38, 0xFE, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x02, 0x62, 0xD0, 0x04, 0x3C, 0xCB, 0xF2, 0xB0, 0x09, 0x56, 0x01, 0x01, 0x56, 0x00, 0x00, 0x80, 0x34, 0xD1
+		}
+	},
+	{
+		234,
+		79,
+		0x39,
+		0x0115,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x15, 0x04, 0x7C, 0x6F, 0xC9, 0x62, 0xD0, 0x00, 0x52, 0x01, 0x38, 0xFE, 0x20, 0x7F, 0x10, 0x4F, 0x20, 0x7F, 0x7F, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x56, 0x00, 0x00, 0x10, 0x7C, 0x18, 0x83, 0x7C, 0x19, 0x64, 0x20, 0x56, 0x00, 0x01, 0x52, 0x00, 0x08, 0x7C, 0x2B, 0x34, 0x38, 0xFF, 0x10, 0x7C, 0x18, 0x4D, 0x20, 0x38, 0xFF, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x04, 0x7C, 0x6F, 0x3C, 0x54, 0x01, 0x3D, 0x3A, 0xDE
+		}
+	},
+	{
+		235,
+		79,
+		0x39,
+		0x0116,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x16, 0x01, 0x50, 0xB0, 0x29, 0x56, 0x00, 0x00, 0x80, 0x1D, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0x00, 0x0E, 0xE9, 0x02, 0x7C, 0x6D, 0x83, 0x08, 0x52, 0x00, 0x01, 0x07, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x77, 0x00, 0x3D, 0x00, 0x99, 0xCF, 0xE0, 0x81, 0x85, 0x3D, 0x01, 0x40, 0xB0, 0x29, 0x56, 0x00, 0x00, 0x80, 0x1D, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0x00, 0x0E, 0x92, 0x8F
+		}
+	},
+	{
+		236,
+		79,
+		0x39,
+		0x0117,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x17, 0xE9, 0x05, 0x7C, 0x6D, 0x83, 0x08, 0x52, 0x00, 0x01, 0x07, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x77, 0x00, 0x3D, 0x00, 0x99, 0xCF, 0xE0, 0x81, 0x58, 0x3D, 0x01, 0x70, 0xB0, 0x61, 0x7C, 0x39, 0x34, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x26, 0xE9, 0x01, 0x3C, 0xE9, 0x01, 0xB0, 0x29, 0x56, 0x00, 0x00, 0x80, 0x1D, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0x00, 0x0E, 0xE9, 0x05, 0x7C, 0xC7, 0xFA
+		}
+	},
+	{
+		237,
+		79,
+		0x39,
+		0x0118,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x18, 0x6D, 0x83, 0x08, 0x52, 0x00, 0x01, 0x07, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x77, 0x00, 0x3D, 0x00, 0x99, 0xCF, 0xE0, 0x81, 0x1B, 0x56, 0x00, 0x00, 0x80, 0x1D, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0x00, 0x0E, 0xE9, 0x08, 0x7C, 0x6D, 0x83, 0x08, 0x52, 0x00, 0x01, 0x07, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x77, 0x00, 0x3D, 0x00, 0x99, 0xCF, 0xE0, 0x80, 0xF3, 0x3D, 0x01, 0xF5, 0x57
+		}
+	},
+	{
+		238,
+		79,
+		0x39,
+		0x0119,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x19, 0x60, 0xB0, 0x95, 0x7C, 0x39, 0x34, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x26, 0xE9, 0x01, 0x3C, 0xE9, 0x01, 0xB0, 0x5D, 0x50, 0x1B, 0x08, 0x50, 0x07, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x56, 0x00, 0x00, 0x80, 0x1D, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0x00, 0x0E, 0xE9, 0x09, 0x7C, 0x6F, 0x5C, 0x08, 0x52, 0x00, 0x01, 0x08, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x77, 0x00, 0x3D, 0xF8, 0x5E
+		}
+	},
+	{
+		239,
+		79,
+		0x39,
+		0x011A,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x1A, 0x00, 0x1B, 0xCF, 0xE0, 0x56, 0x02, 0x23, 0x56, 0x00, 0x00, 0x80, 0x1D, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0x25, 0x0E, 0xE9, 0x09, 0x7C, 0x6F, 0x5C, 0x08, 0x52, 0x02, 0x03, 0x00, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x77, 0x00, 0x3D, 0x00, 0x1B, 0xCF, 0xE0, 0x80, 0x82, 0x56, 0x00, 0x00, 0x80, 0x1D, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0x00, 0x0E, 0xE9, 0x0A, 0xCB, 0x05
+		}
+	},
+	{
+		240,
+		79,
+		0x39,
+		0x011B,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x1B, 0x7C, 0x6F, 0x5C, 0x08, 0x52, 0x00, 0x01, 0x07, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x77, 0x00, 0x3D, 0x00, 0x99, 0xCF, 0xE0, 0x80, 0x5A, 0x3D, 0x01, 0x30, 0xB0, 0x55, 0x62, 0xD0, 0x03, 0x3C, 0x9F, 0x99, 0xD0, 0x4D, 0x62, 0xD0, 0x03, 0x51, 0x9F, 0x7C, 0x70, 0x0E, 0x06, 0xE8, 0x00, 0x0E, 0xE9, 0x08, 0x7C, 0x6D, 0x83, 0x08, 0x50, 0x26, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x62, 0xD0, 0xD0, 0x10
+		}
+	},
+	{
+		241,
+		79,
+		0x39,
+		0x011C,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x1C, 0x03, 0x51, 0x9F, 0x7C, 0x70, 0x0E, 0x06, 0xE8, 0x00, 0x0E, 0xE9, 0x02, 0x7C, 0x6D, 0x83, 0x08, 0x50, 0x27, 0x08, 0x7C, 0x32, 0x0C, 0x62, 0xD0, 0x03, 0x51, 0x9F, 0x7C, 0x70, 0x0E, 0x06, 0xE8, 0x00, 0x0E, 0xE9, 0x05, 0x7C, 0x6D, 0x83, 0x08, 0x50, 0x28, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x38, 0xFC, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x03, 0x56, 0x00, 0x00, 0x80, 0x5A, 0x62, 0xD0, 0x00, 0x3B, 0xE7
+		}
+	},
+	{
+		242,
+		79,
+		0x39,
+		0x011D,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x1D, 0x52, 0x00, 0x7C, 0x6D, 0x8A, 0x06, 0xE8, 0xE6, 0x0E, 0xE9, 0x01, 0x7C, 0x6D, 0x83, 0x54, 0x01, 0x3E, 0xE8, 0x54, 0x02, 0x7C, 0x70, 0x01, 0x7C, 0x70, 0xD4, 0x52, 0x02, 0x53, 0xE9, 0x7C, 0x6E, 0xAE, 0x06, 0xE6, 0xB8, 0x0E, 0xE7, 0x03, 0x7C, 0x6D, 0xEA, 0x52, 0x00, 0x7C, 0x6D, 0x8A, 0x06, 0xE8, 0xE4, 0x0E, 0xE9, 0x01, 0x7C, 0x6D, 0x83, 0x54, 0x01, 0x3E, 0xE8, 0x54, 0x02, 0x7C, 0x70, 0xCA, 0x06
+		}
+	},
+	{
+		243,
+		79,
+		0x39,
+		0x011E,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x1E, 0xD4, 0x7C, 0x70, 0x01, 0x52, 0x02, 0x53, 0xE9, 0x7C, 0x6E, 0xAE, 0x06, 0xE6, 0xB4, 0x0E, 0xE7, 0x03, 0x7C, 0x6D, 0xEA, 0x77, 0x00, 0x7C, 0x6F, 0xC1, 0xCF, 0xA3, 0x52, 0xFC, 0x08, 0x7C, 0x5F, 0x91, 0x38, 0xFF, 0x38, 0xFD, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x7C, 0x6F, 0xB9, 0x52, 0xFB, 0x60, 0xD5, 0x50, 0x04, 0x3F, 0xE8, 0x52, 0xFC, 0x01, 0x01, 0x7C, 0x71, 0x76, 0x50, 0x01, 0x3F, 0x32, 0xD7
+		}
+	},
+	{
+		244,
+		79,
+		0x39,
+		0x011F,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x1F, 0xE8, 0x38, 0xFF, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x03, 0x7C, 0x6F, 0xB9, 0x52, 0xFB, 0x7C, 0x6D, 0xE3, 0x47, 0xE9, 0x80, 0xBF, 0xF4, 0x52, 0xFC, 0x53, 0xE8, 0x52, 0xFB, 0x60, 0xD4, 0x3E, 0xE8, 0x39, 0x04, 0xB0, 0x73, 0x56, 0x00, 0x00, 0x80, 0x3F, 0x62, 0xD0, 0x00, 0x52, 0x00, 0x7C, 0x6D, 0x8A, 0x52, 0xFC, 0x01, 0x02, 0x53, 0xE6, 0x52, 0xFB, 0x09, 0x00, 0x53, 0xE7, 0x51, 0xE8, 0x02, 0x69, 0x46
+		}
+	},
+	{
+		245,
+		79,
+		0x39,
+		0x0120,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x20, 0xE6, 0x53, 0xE6, 0x51, 0xE9, 0x0A, 0xE7, 0x53, 0xE7, 0x06, 0xE8, 0xE0, 0x0E, 0xE9, 0x01, 0x51, 0xE9, 0x60, 0xD4, 0x51, 0xE7, 0x60, 0xD5, 0x10, 0x57, 0x08, 0x62, 0xD0, 0x00, 0x3E, 0xE8, 0x3F, 0xE6, 0x79, 0xBF, 0xF7, 0x20, 0x77, 0x00, 0x7C, 0x6F, 0xC1, 0xCF, 0xBE, 0x62, 0xD0, 0x00, 0x52, 0xFC, 0x01, 0x22, 0x7C, 0x71, 0x76, 0x52, 0xF9, 0x3F, 0xE8, 0x52, 0xFC, 0x01, 0x23, 0x7C, 0x71, 0xE7, 0x43
+		}
+	},
+	{
+		246,
+		79,
+		0x39,
+		0x0121,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x21, 0x76, 0x52, 0xFA, 0x3F, 0xE8, 0x52, 0xFC, 0x01, 0x24, 0x7C, 0x71, 0x76, 0x62, 0xD0, 0x00, 0x51, 0xA2, 0x62, 0xD0, 0x00, 0x3F, 0xE8, 0x7C, 0x6F, 0xB9, 0x52, 0xFB, 0x60, 0xD5, 0x50, 0x84, 0x3F, 0xE8, 0x38, 0xFD, 0x20, 0x7F, 0x10, 0x4F, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x02, 0x62, 0xD0, 0x03, 0x55, 0xAC, 0x19, 0x55, 0xAB, 0x00, 0x55, 0xAA, 0x02, 0x62, 0xD0, 0x00, 0x55, 0xE8, 0x00, 0x55, 0xDD, 0x30
+		}
+	},
+	{
+		247,
+		79,
+		0x39,
+		0x0122,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x22, 0xE9, 0x0A, 0x7C, 0x6D, 0xAD, 0x7C, 0x6D, 0xC6, 0x51, 0xE8, 0x62, 0xD0, 0x03, 0x53, 0xA9, 0x10, 0x50, 0x03, 0x08, 0x50, 0xA9, 0x5C, 0x18, 0x7C, 0x20, 0x98, 0x20, 0x62, 0xD0, 0x03, 0x50, 0x00, 0x01, 0x80, 0x53, 0xAB, 0x50, 0x02, 0x09, 0x00, 0x53, 0xAA, 0x62, 0xD0, 0x00, 0x55, 0xE8, 0x00, 0x55, 0xE9, 0x0A, 0x7C, 0x6D, 0xC6, 0x7C, 0x6D, 0xAD, 0x7C, 0x73, 0x3C, 0x51, 0xE8, 0x62, 0xD0, 0xBC, 0xEF
+		}
+	},
+	{
+		248,
+		79,
+		0x39,
+		0x0123,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x23, 0x03, 0x53, 0xA9, 0x10, 0x50, 0x03, 0x08, 0x50, 0xA9, 0x5C, 0x18, 0x7C, 0x20, 0x98, 0x20, 0x62, 0xD0, 0x03, 0x55, 0xAB, 0xA0, 0x55, 0xAA, 0x01, 0x62, 0xD0, 0x00, 0x55, 0xE8, 0x00, 0x55, 0xE9, 0x0A, 0x7C, 0x6D, 0xC6, 0x7C, 0x6D, 0xAD, 0x16, 0xE8, 0x02, 0x1E, 0xE9, 0x00, 0x51, 0xE8, 0x62, 0xD0, 0x03, 0x53, 0xA9, 0x10, 0x50, 0x03, 0x08, 0x50, 0xA9, 0x5C, 0x18, 0x7C, 0x20, 0x98, 0x62, 0xDB, 0x2E
+		}
+	},
+	{
+		249,
+		79,
+		0x39,
+		0x0124,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x24, 0xD0, 0x00, 0x20, 0x38, 0xFE, 0x20, 0x7F, 0x10, 0x50, 0x99, 0x7C, 0x12, 0x90, 0x20, 0x9F, 0x59, 0x10, 0x7C, 0x10, 0x74, 0x20, 0x7F, 0x10, 0x4F, 0x10, 0x52, 0xFB, 0x08, 0x52, 0xFC, 0x20, 0x7C, 0x13, 0xEA, 0x20, 0x9F, 0x44, 0x10, 0x7C, 0x10, 0x74, 0x20, 0x20, 0x7F, 0x10, 0x4F, 0x10, 0x52, 0xFC, 0x7C, 0x13, 0xFB, 0x20, 0x9F, 0x32, 0x10, 0x7C, 0x10, 0x74, 0x20, 0x20, 0x7F, 0x10, 0x4F, 0x86, 0x85
+		}
+	},
+	{
+		250,
+		79,
+		0x39,
+		0x0125,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x25, 0x10, 0x52, 0xFC, 0x7C, 0x14, 0x0D, 0x20, 0x9F, 0x20, 0x10, 0x7C, 0x10, 0x74, 0x20, 0x20, 0x7F, 0x10, 0x4F, 0x52, 0xFC, 0x62, 0xD0, 0x00, 0x53, 0xA9, 0x20, 0x7F, 0x62, 0xD0, 0x00, 0x55, 0x39, 0x08, 0x55, 0x3A, 0x08, 0x55, 0x3B, 0x08, 0x55, 0x3C, 0x08, 0x55, 0x01, 0x00, 0x55, 0x00, 0x00, 0x55, 0x03, 0x00, 0x55, 0x02, 0x00, 0x55, 0x05, 0x00, 0x55, 0x04, 0x00, 0x55, 0x07, 0x00, 0x55, 0x71, 0x5C
+		}
+	},
+	{
+		251,
+		79,
+		0x39,
+		0x0126,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x26, 0x06, 0x00, 0x55, 0x55, 0x20, 0x55, 0x54, 0x01, 0x55, 0x53, 0x00, 0x43, 0xE6, 0x01, 0x43, 0xE0, 0x08, 0x7F, 0x49, 0xE0, 0x08, 0xB0, 0x05, 0x50, 0x00, 0x80, 0x07, 0x08, 0x7C, 0x54, 0x59, 0x38, 0xFF, 0x7F, 0x08, 0x7C, 0x54, 0xA4, 0x38, 0xFF, 0x7F, 0x08, 0x7C, 0x54, 0xB5, 0x38, 0xFF, 0x7F, 0x08, 0x7C, 0x54, 0xE2, 0x38, 0xFF, 0x7F, 0x08, 0x7C, 0x54, 0xF3, 0x38, 0xFF, 0x7F, 0x08, 0x7C, 0x84, 0x83
+		}
+	},
+	{
+		252,
+		79,
+		0x39,
+		0x0127,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x27, 0x55, 0x04, 0x38, 0xFF, 0x7F, 0x08, 0x7C, 0x55, 0x15, 0x38, 0xFF, 0x7F, 0x08, 0x7C, 0x5B, 0xE0, 0x38, 0xFF, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x50, 0x00, 0x3D, 0xF9, 0x80, 0xC0, 0x06, 0x7C, 0x4A, 0x8B, 0x50, 0xC0, 0x3D, 0xF5, 0x80, 0xC0, 0x0C, 0x10, 0x4B, 0x11, 0x04, 0x4B, 0x7C, 0x4A, 0x8B, 0x31, 0x80, 0x20, 0x08, 0x7C, 0x4A, 0x28, 0x18, 0x6A, 0xD0, 0x04, 0x7C, 0x4A, 0x8B, 0x6A, 0xD0, 0x1F, 0xBA
+		}
+	},
+	{
+		253,
+		79,
+		0x39,
+		0x0128,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x28, 0x08, 0x4B, 0x11, 0x04, 0x4B, 0x7C, 0x4A, 0x8B, 0x38, 0xFF, 0x20, 0x70, 0x3F, 0x71, 0xC0, 0x7F, 0x10, 0x4F, 0x5D, 0xD0, 0x08, 0x62, 0xD0, 0x00, 0x7C, 0x4A, 0xA3, 0x51, 0xE1, 0x54, 0xFB, 0x18, 0x60, 0xD0, 0x20, 0x70, 0x3F, 0x71, 0xC0, 0x7F, 0x5D, 0xD0, 0x08, 0x62, 0xD0, 0x00, 0x51, 0xE9, 0x08, 0x50, 0x00, 0x53, 0xE9, 0x53, 0xE1, 0x53, 0xE0, 0x53, 0xDF, 0x56, 0x00, 0x20, 0x66, 0xFC, 0xD7, 0x2B
+		}
+	},
+	{
+		254,
+		79,
+		0x39,
+		0x0129,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x29, 0x6C, 0xFB, 0x6C, 0xFA, 0x6C, 0xF9, 0x6B, 0xDF, 0x6B, 0xE0, 0x6B, 0xE1, 0x6B, 0xE9, 0x51, 0xDF, 0x1B, 0xF8, 0x51, 0xE0, 0x1B, 0xF7, 0x51, 0xE1, 0x1B, 0xF6, 0x51, 0xE9, 0x1B, 0xF5, 0xC0, 0x11, 0x53, 0xE9, 0x52, 0xF8, 0x14, 0xDF, 0x52, 0xF7, 0x1C, 0xE0, 0x52, 0xF6, 0x1C, 0xE1, 0x77, 0xFC, 0x7B, 0x00, 0xBF, 0xCB, 0x51, 0xDF, 0x54, 0xF8, 0x51, 0xE0, 0x54, 0xF7, 0x51, 0xE1, 0x54, 0xF6, 0x3A, 0xF2
+		}
+	},
+	{
+		255,
+		79,
+		0x39,
+		0x012A,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x2A, 0x51, 0xE9, 0x54, 0xF5, 0x18, 0x53, 0xE9, 0x18, 0x60, 0xD0, 0x7F, 0x37, 0xFC, 0xFF, 0x77, 0xFC, 0x37, 0xFB, 0xFF, 0x0F, 0xFB, 0x00, 0x37, 0xFA, 0xFF, 0x0F, 0xFA, 0x00, 0x37, 0xF9, 0xFF, 0x0F, 0xF9, 0x00, 0x7F, 0x55, 0xE1, 0x00, 0x55, 0xE0, 0x08, 0x66, 0xFC, 0x6B, 0xE1, 0x51, 0xE1, 0x1B, 0xFB, 0xC0, 0x05, 0x53, 0xE1, 0x77, 0xFC, 0x7A, 0xE0, 0xBF, 0xEF, 0x7F, 0x08, 0x10, 0x4F, 0x50, 0x80, 0x7F
+		}
+	},
+	{
+		256,
+		79,
+		0x39,
+		0x012B,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x2B, 0x00, 0x6F, 0xFF, 0xD0, 0x03, 0x03, 0xFE, 0x66, 0xFE, 0xBF, 0xF7, 0x38, 0xFE, 0x70, 0x3F, 0x71, 0xC0, 0x7F, 0x7C, 0x6E, 0x82, 0x7C, 0x6E, 0xC0, 0x7F, 0x62, 0xD0, 0x01, 0x51, 0xE7, 0x08, 0x51, 0xE6, 0x62, 0xD0, 0x03, 0x53, 0xFE, 0x18, 0x53, 0xFF, 0x62, 0xD0, 0x01, 0x51, 0xE5, 0x08, 0x51, 0xE4, 0x62, 0xD0, 0x03, 0x53, 0xFC, 0x18, 0x53, 0xFD, 0x62, 0xD0, 0x01, 0x51, 0xEF, 0x08, 0x51, 0x7E, 0x7C
+		}
+	},
+	{
+		257,
+		79,
+		0x39,
+		0x012C,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x2C, 0xEE, 0x62, 0xD0, 0x03, 0x53, 0xFA, 0x18, 0x53, 0xFB, 0x62, 0xD0, 0x01, 0x51, 0xED, 0x08, 0x51, 0xEC, 0x62, 0xD0, 0x03, 0x53, 0xF8, 0x18, 0x53, 0xF9, 0x7F, 0x62, 0xD0, 0x01, 0x51, 0xE7, 0x02, 0xEF, 0x62, 0xD0, 0x00, 0x53, 0xE8, 0x62, 0xD0, 0x01, 0x51, 0xE6, 0x0A, 0xEE, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x7C, 0x6D, 0xF3, 0x51, 0xE8, 0x08, 0x51, 0xE9, 0x62, 0xD0, 0x04, 0x53, 0xA3, 0x18, 0x23, 0xC7
+		}
+	},
+	{
+		258,
+		79,
+		0x39,
+		0x012D,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x2D, 0x53, 0xA4, 0x62, 0xD0, 0x01, 0x51, 0xE5, 0x02, 0xED, 0x62, 0xD0, 0x00, 0x53, 0xE8, 0x62, 0xD0, 0x01, 0x51, 0xE4, 0x0A, 0xEC, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x7C, 0x6D, 0xF3, 0x7C, 0x71, 0xC3, 0x7F, 0x10, 0x4F, 0x52, 0xFA, 0x13, 0xFC, 0x52, 0xF9, 0x1B, 0xFB, 0xD0, 0x12, 0x62, 0xD0, 0x00, 0x52, 0xFC, 0x13, 0xFA, 0x53, 0xE8, 0x52, 0xFB, 0x1B, 0xF9, 0x53, 0xE9, 0x80, 0x10, 0x62, 0xD0, 0xB7, 0xF0
+		}
+	},
+	{
+		259,
+		79,
+		0x39,
+		0x012E,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x2E, 0x00, 0x52, 0xFA, 0x13, 0xFC, 0x53, 0xE8, 0x52, 0xF9, 0x1B, 0xFB, 0x53, 0xE9, 0x20, 0x7F, 0x62, 0xD0, 0x04, 0x55, 0xCD, 0x00, 0x7C, 0x70, 0x41, 0x7F, 0x62, 0xD0, 0x03, 0x51, 0xEC, 0x08, 0x51, 0xED, 0x08, 0x62, 0xD0, 0x01, 0x51, 0xE6, 0x08, 0x51, 0xE7, 0x08, 0x9F, 0xB4, 0x38, 0xFC, 0x7C, 0x6F, 0xA8, 0x62, 0xD0, 0x03, 0x51, 0xE8, 0x08, 0x51, 0xE9, 0x08, 0x62, 0xD0, 0x01, 0x51, 0xEE, 0x8C, 0x9B
+		}
+	},
+	{
+		260,
+		79,
+		0x39,
+		0x012F,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x2F, 0x08, 0x51, 0xEF, 0x08, 0x9F, 0x9B, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x62, 0xD0, 0x04, 0x04, 0xA0, 0x7C, 0x71, 0x7F, 0x0C, 0x9F, 0x62, 0xD0, 0x03, 0x51, 0xEA, 0x08, 0x51, 0xEB, 0x08, 0x62, 0xD0, 0x01, 0x51, 0xE4, 0x08, 0x51, 0xE5, 0x08, 0x9F, 0x78, 0x38, 0xF8, 0x7C, 0x6F, 0x76, 0x62, 0xD0, 0x03, 0x51, 0xE6, 0x08, 0x51, 0xE7, 0x08, 0x62, 0xD0, 0x01, 0x51, 0xEC, 0x08, 0x51, 0xED, 0x08, 0x6A, 0x58
+		}
+	},
+	{
+		261,
+		79,
+		0x39,
+		0x0130,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x30, 0x9F, 0x5F, 0x38, 0xFC, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x62, 0xD0, 0x04, 0x04, 0x9E, 0x7C, 0x71, 0x7F, 0x0C, 0x9D, 0x7F, 0x10, 0x7C, 0x1E, 0x80, 0x62, 0xD0, 0x00, 0x5A, 0xE9, 0x20, 0x7C, 0x71, 0x22, 0x51, 0xAE, 0x62, 0xD0, 0x03, 0x12, 0xDD, 0x62, 0xD0, 0x04, 0x53, 0xA6, 0x62, 0xD0, 0x04, 0x51, 0xAD, 0x62, 0xD0, 0x03, 0x1A, 0xDC, 0x62, 0xD0, 0x04, 0x53, 0xA5, 0x62, 0xD0, 0x04, 0x51, 0x3D, 0xFF
+		}
+	},
+	{
+		262,
+		79,
+		0x39,
+		0x0131,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x31, 0xAE, 0x08, 0x51, 0xAD, 0x62, 0xD0, 0x03, 0x53, 0xDC, 0x18, 0x53, 0xDD, 0x62, 0xD0, 0x04, 0x51, 0xA6, 0x62, 0xD0, 0x00, 0x53, 0xE6, 0x62, 0xD0, 0x04, 0x51, 0xA5, 0x62, 0xD0, 0x00, 0x53, 0xE7, 0x62, 0xD0, 0x04, 0x51, 0xA0, 0x08, 0x62, 0xD0, 0x00, 0x18, 0x53, 0xE5, 0x55, 0xE4, 0x00, 0x65, 0xE5, 0x65, 0xE4, 0x6B, 0xE5, 0x51, 0xE4, 0x53, 0xE2, 0x51, 0xE5, 0x53, 0xE3, 0x50, 0x00, 0x08, 0x8B, 0x9C
+		}
+	},
+	{
+		263,
+		79,
+		0x39,
+		0x0132,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x32, 0x08, 0x51, 0xE7, 0x08, 0x51, 0xE6, 0x08, 0x50, 0x00, 0x08, 0x08, 0x51, 0xE3, 0x08, 0x51, 0xE2, 0x08, 0x7C, 0x49, 0xD3, 0x18, 0x53, 0xE6, 0x18, 0x53, 0xE7, 0x18, 0x18, 0x38, 0xFC, 0x51, 0xE6, 0x53, 0xE8, 0x51, 0xE7, 0x53, 0xE9, 0x51, 0xE8, 0x08, 0x51, 0xE9, 0x62, 0xD0, 0x04, 0x53, 0x9B, 0x18, 0x53, 0x9C, 0x62, 0xD0, 0x04, 0x51, 0xA6, 0x62, 0xD0, 0x00, 0x53, 0xE6, 0x62, 0xD0, 0x04, 0x57, 0x35
+		}
+	},
+	{
+		264,
+		79,
+		0x39,
+		0x0133,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x33, 0x51, 0xA5, 0x62, 0xD0, 0x00, 0x53, 0xE7, 0x62, 0xD0, 0x04, 0x51, 0x9E, 0x08, 0x62, 0xD0, 0x00, 0x18, 0x53, 0xE5, 0x55, 0xE4, 0x00, 0x65, 0xE5, 0x65, 0xE4, 0x6B, 0xE5, 0x51, 0xE4, 0x53, 0xE2, 0x51, 0xE5, 0x53, 0xE3, 0x50, 0x00, 0x08, 0x08, 0x51, 0xE7, 0x08, 0x51, 0xE6, 0x08, 0x50, 0x00, 0x08, 0x08, 0x51, 0xE3, 0x08, 0x51, 0xE2, 0x08, 0x7C, 0x49, 0xD3, 0x18, 0x53, 0xE6, 0x18, 0x53, 0x0D, 0xA2
+		}
+	},
+	{
+		265,
+		79,
+		0x39,
+		0x0134,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x34, 0xE7, 0x18, 0x18, 0x38, 0xFC, 0x51, 0xE6, 0x53, 0xE8, 0x51, 0xE7, 0x53, 0xE9, 0x51, 0xE8, 0x08, 0x51, 0xE9, 0x62, 0xD0, 0x04, 0x53, 0x99, 0x18, 0x53, 0x9A, 0x7C, 0x6E, 0x82, 0x7F, 0x10, 0x4F, 0x38, 0x08, 0x62, 0xD0, 0x04, 0x55, 0xDE, 0x00, 0x62, 0xD0, 0x04, 0x55, 0xDD, 0x00, 0x52, 0xFC, 0x03, 0xFA, 0x54, 0x01, 0x52, 0xFB, 0x0B, 0xF9, 0x54, 0x00, 0x52, 0xF8, 0x03, 0xF6, 0x54, 0x03, 0x0D, 0xA3
+		}
+	},
+	{
+		266,
+		79,
+		0x39,
+		0x0135,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x35, 0x52, 0xF7, 0x0B, 0xF5, 0x54, 0x02, 0x52, 0xFC, 0x03, 0xF6, 0x54, 0x05, 0x52, 0xFB, 0x0B, 0xF5, 0x54, 0x04, 0x52, 0xFA, 0x03, 0xF8, 0x54, 0x07, 0x52, 0xF9, 0x0B, 0xF7, 0x54, 0x06, 0x52, 0xFC, 0x13, 0xF8, 0x52, 0xFB, 0x1B, 0xF7, 0xC0, 0x43, 0x7C, 0x72, 0xD4, 0xD0, 0x1E, 0x62, 0xD0, 0x04, 0x55, 0xDE, 0x01, 0x52, 0x01, 0x13, 0x03, 0x52, 0x00, 0x1B, 0x02, 0xD0, 0x06, 0x7C, 0x73, 0x66, 0x05, 0x94
+		}
+	},
+	{
+		267,
+		79,
+		0x39,
+		0x0136,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x36, 0x80, 0x69, 0x62, 0xD0, 0x04, 0x55, 0xDD, 0x02, 0x80, 0x61, 0x62, 0xD0, 0x04, 0x55, 0xDE, 0x02, 0x52, 0x07, 0x13, 0x05, 0x52, 0x06, 0x1B, 0x04, 0xD0, 0x09, 0x62, 0xD0, 0x04, 0x55, 0xDD, 0x02, 0x80, 0x49, 0x62, 0xD0, 0x04, 0x55, 0xDD, 0x03, 0x80, 0x41, 0x7C, 0x72, 0xD4, 0xC0, 0x21, 0x62, 0xD0, 0x04, 0x55, 0xDE, 0x03, 0x52, 0x03, 0x13, 0x01, 0x52, 0x02, 0x1B, 0x00, 0xD0, 0x09, 0x62, 0xE1, 0x4D
+		}
+	},
+	{
+		268,
+		79,
+		0x39,
+		0x0137,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x37, 0xD0, 0x04, 0x55, 0xDD, 0x03, 0x80, 0x24, 0x62, 0xD0, 0x04, 0x55, 0xDD, 0x04, 0x80, 0x1C, 0x62, 0xD0, 0x04, 0x55, 0xDE, 0x04, 0x52, 0x05, 0x13, 0x07, 0x52, 0x04, 0x1B, 0x06, 0xD0, 0x09, 0x62, 0xD0, 0x04, 0x55, 0xDD, 0x04, 0x80, 0x04, 0x7C, 0x73, 0x66, 0x62, 0xD0, 0x04, 0x3C, 0xDD, 0x01, 0xB0, 0x29, 0x62, 0xD0, 0x00, 0x52, 0xF3, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x52, 0xFA, 0x02, 0xE8, 0x77, 0x7A
+		}
+	},
+	{
+		269,
+		79,
+		0x39,
+		0x0138,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x38, 0x53, 0xE8, 0x52, 0xF9, 0x0A, 0xE9, 0x53, 0xE9, 0x51, 0xE8, 0x13, 0xF6, 0x51, 0xE9, 0x1B, 0xF5, 0xD0, 0x09, 0x62, 0xD0, 0x04, 0x55, 0xE0, 0x01, 0x80, 0x97, 0x62, 0xD0, 0x04, 0x3C, 0xDD, 0x02, 0xB0, 0x29, 0x62, 0xD0, 0x00, 0x52, 0xF4, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x52, 0xF8, 0x02, 0xE8, 0x53, 0xE8, 0x52, 0xF7, 0x0A, 0xE9, 0x53, 0xE9, 0x51, 0xE8, 0x13, 0xFC, 0x51, 0xE9, 0x1B, 0xFB, 0x37, 0xFB
+		}
+	},
+	{
+		270,
+		79,
+		0x39,
+		0x0139,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x39, 0xD0, 0x09, 0x62, 0xD0, 0x04, 0x55, 0xE0, 0x02, 0x80, 0x67, 0x62, 0xD0, 0x04, 0x3C, 0xDD, 0x03, 0xB0, 0x29, 0x62, 0xD0, 0x00, 0x52, 0xF3, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x52, 0xF6, 0x02, 0xE8, 0x53, 0xE8, 0x52, 0xF5, 0x0A, 0xE9, 0x53, 0xE9, 0x51, 0xE8, 0x13, 0xFA, 0x51, 0xE9, 0x1B, 0xF9, 0xD0, 0x09, 0x62, 0xD0, 0x04, 0x55, 0xE0, 0x03, 0x80, 0x37, 0x62, 0xD0, 0x04, 0x3C, 0xDD, 0x04, 0x10, 0xAE
+		}
+	},
+	{
+		271,
+		79,
+		0x39,
+		0x013A,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x3A, 0xB0, 0x29, 0x62, 0xD0, 0x00, 0x52, 0xF4, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x52, 0xFC, 0x02, 0xE8, 0x53, 0xE8, 0x52, 0xFB, 0x0A, 0xE9, 0x53, 0xE9, 0x51, 0xE8, 0x13, 0xF8, 0x51, 0xE9, 0x1B, 0xF7, 0xD0, 0x09, 0x62, 0xD0, 0x04, 0x55, 0xE0, 0x04, 0x80, 0x07, 0x62, 0xD0, 0x04, 0x55, 0xE0, 0x00, 0x62, 0xD0, 0x04, 0x3C, 0xDE, 0x04, 0xB0, 0x15, 0x62, 0xD0, 0x04, 0x3C, 0xDD, 0x01, 0xB0, 0x0D, 0xA9, 0xE1
+		}
+	},
+	{
+		272,
+		79,
+		0x39,
+		0x013B,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x3B, 0x62, 0xD0, 0x04, 0x51, 0xDE, 0x01, 0x03, 0x62, 0xD0, 0x00, 0x80, 0x15, 0x62, 0xD0, 0x04, 0x51, 0xDE, 0x62, 0xD0, 0x04, 0x02, 0xDD, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x16, 0xE9, 0x02, 0x51, 0xE9, 0x38, 0xF8, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x09, 0x52, 0xF7, 0x3B, 0xFB, 0xB0, 0x1B, 0x52, 0xF8, 0x3B, 0xFC, 0xB0, 0x15, 0x52, 0xF5, 0x3B, 0xF9, 0xB0, 0x0F, 0x52, 0xF6, 0x3B, 0xFA, 0xB0, 0x09, 0xC2, 0x14
+		}
+	},
+	{
+		273,
+		79,
+		0x39,
+		0x013C,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x3C, 0x62, 0xD0, 0x04, 0x55, 0xDF, 0xFE, 0x81, 0x17, 0x52, 0xF7, 0x3B, 0xFB, 0xB0, 0x07, 0x52, 0xF8, 0x3B, 0xFC, 0xA0, 0x0D, 0x52, 0xF5, 0x3B, 0xF9, 0xB0, 0x4E, 0x52, 0xF6, 0x3B, 0xFA, 0xB0, 0x48, 0x62, 0xD0, 0x04, 0x3C, 0xCE, 0x00, 0xA0, 0x06, 0x3C, 0xCE, 0x07, 0xB0, 0x09, 0x62, 0xD0, 0x04, 0x55, 0xDF, 0x00, 0x80, 0xEA, 0x62, 0xD0, 0x04, 0x3C, 0xCE, 0x01, 0xA0, 0x06, 0x3C, 0xCE, 0x02, 0xC9, 0x23
+		}
+	},
+	{
+		274,
+		79,
+		0x39,
+		0x013D,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x3D, 0xB0, 0x09, 0x62, 0xD0, 0x04, 0x55, 0xDF, 0x02, 0x80, 0xD5, 0x62, 0xD0, 0x04, 0x3C, 0xCE, 0x03, 0xA0, 0x06, 0x3C, 0xCE, 0x04, 0xB0, 0x09, 0x62, 0xD0, 0x04, 0x55, 0xDF, 0x04, 0x80, 0xC0, 0x62, 0xD0, 0x04, 0x55, 0xDF, 0x06, 0x80, 0xB8, 0x52, 0xFB, 0x08, 0x52, 0xFC, 0x08, 0x52, 0xF7, 0x08, 0x52, 0xF8, 0x08, 0x9B, 0xEC, 0x7C, 0x72, 0x25, 0x52, 0xF9, 0x08, 0x52, 0xFA, 0x08, 0x52, 0xF5, 0x56, 0x3E
+		}
+	},
+	{
+		275,
+		79,
+		0x39,
+		0x013E,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x3E, 0x08, 0x52, 0xF6, 0x08, 0x9B, 0xDB, 0x38, 0xF8, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x54, 0x03, 0x51, 0xE9, 0x54, 0x02, 0x52, 0x01, 0x13, 0x03, 0x52, 0x00, 0x1B, 0x02, 0xD0, 0x23, 0x7C, 0x71, 0x2E, 0x7C, 0x70, 0x17, 0x7C, 0x73, 0x4A, 0xD0, 0x09, 0x56, 0x06, 0x01, 0x56, 0x05, 0x00, 0x80, 0x07, 0x56, 0x06, 0x00, 0x56, 0x05, 0x00, 0x62, 0xD0, 0x00, 0x52, 0x06, 0x54, 0x04, 0x80, 0x2C, 0x62, 0x31, 0xF5
+		}
+	},
+	{
+		276,
+		79,
+		0x39,
+		0x013F,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x3F, 0xD0, 0x00, 0x52, 0x03, 0x53, 0xE8, 0x52, 0x02, 0x53, 0xE9, 0x7C, 0x70, 0x17, 0x13, 0x01, 0x51, 0xE9, 0x1B, 0x00, 0xD0, 0x09, 0x56, 0x08, 0x01, 0x56, 0x07, 0x00, 0x80, 0x07, 0x56, 0x08, 0x00, 0x56, 0x07, 0x00, 0x62, 0xD0, 0x00, 0x52, 0x08, 0x54, 0x04, 0x62, 0xD0, 0x04, 0x47, 0xCE, 0x01, 0xB0, 0x1B, 0x62, 0xD0, 0x00, 0x52, 0x04, 0x31, 0x01, 0x53, 0xE9, 0x62, 0xD0, 0x04, 0x51, 0xCE, 0x79, 0x86
+		}
+	},
+	{
+		277,
+		79,
+		0x39,
+		0x0140,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x40, 0x62, 0xD0, 0x00, 0x02, 0xE9, 0x62, 0xD0, 0x04, 0x53, 0xDF, 0x80, 0x0D, 0x62, 0xD0, 0x04, 0x51, 0xCE, 0x03, 0x04, 0x62, 0xD0, 0x04, 0x53, 0xDF, 0x62, 0xD0, 0x04, 0x26, 0xDF, 0x07, 0x62, 0xD0, 0x04, 0x51, 0xDF, 0x01, 0x01, 0x62, 0xD0, 0x00, 0x38, 0xF7, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x04, 0x56, 0x00, 0x00, 0x7C, 0x72, 0xAB, 0x10, 0x7C, 0x1E, 0x80, 0x62, 0xD0, 0x00, 0x5A, 0xE9, 0x20, 0xC9, 0x27
+		}
+	},
+	{
+		278,
+		79,
+		0x39,
+		0x0141,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x41, 0x7C, 0x71, 0x22, 0x3D, 0xFC, 0x01, 0xA0, 0x62, 0x3D, 0xFC, 0x00, 0xB0, 0x41, 0x62, 0xD0, 0x04, 0x55, 0xD0, 0x00, 0x62, 0xD0, 0x04, 0x7C, 0x71, 0xEE, 0x3C, 0xAD, 0x00, 0xB0, 0x06, 0x3C, 0xAE, 0x00, 0xA0, 0x22, 0x62, 0xD0, 0x04, 0x51, 0xAE, 0x08, 0x51, 0xAD, 0x62, 0xD0, 0x04, 0x53, 0xAF, 0x18, 0x53, 0xB0, 0x10, 0x50, 0x00, 0x5C, 0x7C, 0x1E, 0xB4, 0x20, 0x62, 0xD0, 0x03, 0x55, 0xDD, 0x4B, 0x2C
+		}
+	},
+	{
+		279,
+		79,
+		0x39,
+		0x0142,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x42, 0x00, 0x55, 0xDC, 0x00, 0x62, 0xD0, 0x04, 0x55, 0xA6, 0x00, 0x55, 0xA5, 0x00, 0x7C, 0x72, 0x85, 0x62, 0xD0, 0x03, 0x55, 0xE3, 0x00, 0x55, 0xE2, 0x00, 0x7C, 0x73, 0x5F, 0x62, 0xD0, 0x04, 0x55, 0xD4, 0x00, 0x62, 0xD0, 0x00, 0x50, 0x00, 0x83, 0xAD, 0x62, 0xD0, 0x04, 0x3C, 0xDC, 0x01, 0xA0, 0x2A, 0x62, 0xD0, 0x01, 0x51, 0xE7, 0x08, 0x51, 0xE6, 0x62, 0xD0, 0x03, 0x53, 0xFE, 0x18, 0x53, 0x86, 0xA3
+		}
+	},
+	{
+		280,
+		79,
+		0x39,
+		0x0143,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x43, 0xFF, 0x62, 0xD0, 0x01, 0x51, 0xE5, 0x08, 0x51, 0xE4, 0x62, 0xD0, 0x03, 0x53, 0xFC, 0x18, 0x53, 0xFD, 0x7C, 0x6E, 0x82, 0x62, 0xD0, 0x04, 0x55, 0xDC, 0x01, 0x62, 0xD0, 0x01, 0x51, 0xE7, 0x08, 0x51, 0xE6, 0x62, 0xD0, 0x03, 0x53, 0xF6, 0x18, 0x53, 0xF7, 0x62, 0xD0, 0x01, 0x51, 0xE5, 0x08, 0x51, 0xE4, 0x62, 0xD0, 0x03, 0x53, 0xF4, 0x18, 0x53, 0xF5, 0x62, 0xD0, 0x00, 0x51, 0x3A, 0x08, 0x36, 0x04
+		}
+	},
+	{
+		281,
+		79,
+		0x39,
+		0x0144,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x44, 0x62, 0xD0, 0x00, 0x51, 0x39, 0x08, 0x62, 0xD0, 0x03, 0x51, 0xEA, 0x08, 0x51, 0xEB, 0x08, 0x62, 0xD0, 0x03, 0x51, 0xEC, 0x08, 0x51, 0xED, 0x08, 0x62, 0xD0, 0x01, 0x51, 0xE4, 0x08, 0x51, 0xE5, 0x08, 0x51, 0xE6, 0x08, 0x51, 0xE7, 0x08, 0x9B, 0xF5, 0x38, 0xF6, 0x7C, 0x71, 0x88, 0x54, 0x00, 0x3D, 0x00, 0x00, 0xA1, 0x23, 0x3D, 0xFB, 0x00, 0xA1, 0x1E, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0xAC, 0xF1
+		}
+	},
+	{
+		282,
+		79,
+		0x39,
+		0x0145,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x45, 0x62, 0xD0, 0x03, 0x51, 0xE3, 0x21, 0x0F, 0x62, 0xD0, 0x00, 0x53, 0xE6, 0x62, 0xD0, 0x03, 0x51, 0xE2, 0x21, 0x00, 0x62, 0xD0, 0x00, 0x3A, 0xE9, 0xB0, 0x07, 0x51, 0xE6, 0x3A, 0xE8, 0xA0, 0xA4, 0x62, 0xD0, 0x03, 0x65, 0xE3, 0x6B, 0xE2, 0x65, 0xE3, 0x6B, 0xE2, 0x65, 0xE3, 0x6B, 0xE2, 0x65, 0xE3, 0x6B, 0xE2, 0x52, 0x00, 0x2C, 0xE3, 0x3C, 0xE2, 0x12, 0xB0, 0x06, 0x3C, 0xE3, 0x34, 0xA0, 0xF9, 0x8C
+		}
+	},
+	{
+		283,
+		79,
+		0x39,
+		0x0146,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x46, 0x28, 0x62, 0xD0, 0x03, 0x3C, 0xE2, 0x23, 0xB0, 0x06, 0x3C, 0xE3, 0x41, 0xA0, 0x1B, 0x62, 0xD0, 0x03, 0x3C, 0xE2, 0x34, 0xB0, 0x06, 0x3C, 0xE3, 0x12, 0xA0, 0x0E, 0x62, 0xD0, 0x03, 0x3C, 0xE2, 0x41, 0xB0, 0x14, 0x3C, 0xE3, 0x23, 0xB0, 0x0F, 0x62, 0xD0, 0x04, 0x55, 0xD2, 0x01, 0x62, 0xD0, 0x04, 0x55, 0xD3, 0x01, 0x80, 0x49, 0x62, 0xD0, 0x03, 0x3C, 0xE2, 0x43, 0xB0, 0x06, 0x3C, 0xE3, 0xD5, 0x45
+		}
+	},
+	{
+		284,
+		79,
+		0x39,
+		0x0147,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x47, 0x21, 0xA0, 0x28, 0x62, 0xD0, 0x03, 0x3C, 0xE2, 0x32, 0xB0, 0x06, 0x3C, 0xE3, 0x14, 0xA0, 0x1B, 0x62, 0xD0, 0x03, 0x3C, 0xE2, 0x21, 0xB0, 0x06, 0x3C, 0xE3, 0x43, 0xA0, 0x0E, 0x62, 0xD0, 0x03, 0x3C, 0xE2, 0x14, 0xB0, 0x14, 0x3C, 0xE3, 0x32, 0xB0, 0x0F, 0x62, 0xD0, 0x04, 0x55, 0xD2, 0x00, 0x62, 0xD0, 0x04, 0x55, 0xD3, 0x01, 0x80, 0x07, 0x62, 0xD0, 0x04, 0x55, 0xD3, 0x00, 0x7C, 0x72, 0x16, 0xC8
+		}
+	},
+	{
+		285,
+		79,
+		0x39,
+		0x0148,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x48, 0x85, 0x80, 0x3D, 0x7C, 0x73, 0x0E, 0x39, 0x00, 0xA0, 0x36, 0x62, 0xD0, 0x04, 0x3C, 0xD3, 0x01, 0xB0, 0x2E, 0x62, 0xD0, 0x00, 0x7C, 0x73, 0x0E, 0x53, 0xE9, 0x62, 0xD0, 0x04, 0x51, 0xD1, 0x62, 0xD0, 0x00, 0x3A, 0xE9, 0xA0, 0x08, 0x62, 0xD0, 0x04, 0x76, 0xD1, 0x80, 0x13, 0x62, 0xD0, 0x03, 0x55, 0xE3, 0x00, 0x55, 0xE2, 0x00, 0x7C, 0x72, 0x85, 0x62, 0xD0, 0x04, 0x55, 0xD3, 0x00, 0x62, 0x19, 0xCF
+		}
+	},
+	{
+		286,
+		79,
+		0x39,
+		0x0149,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x49, 0xD0, 0x04, 0x3C, 0xD3, 0x01, 0xB0, 0x11, 0x62, 0xD0, 0x04, 0x3C, 0xD2, 0x01, 0xB0, 0x06, 0x56, 0x00, 0x28, 0x80, 0x04, 0x56, 0x00, 0x29, 0x3D, 0x00, 0x00, 0xA0, 0x3E, 0x62, 0xD0, 0x03, 0x51, 0xEA, 0x08, 0x51, 0xEB, 0x08, 0x62, 0xD0, 0x03, 0x51, 0xEC, 0x08, 0x51, 0xED, 0x08, 0x62, 0xD0, 0x01, 0x51, 0xE4, 0x08, 0x51, 0xE5, 0x08, 0x51, 0xE6, 0x08, 0x51, 0xE7, 0x08, 0x9C, 0x65, 0x38, 0x1E, 0xDA
+		}
+	},
+	{
+		287,
+		79,
+		0x39,
+		0x014A,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x4A, 0xF8, 0x54, 0x03, 0x3D, 0xFB, 0x00, 0xA0, 0x09, 0x62, 0xD0, 0x04, 0x3C, 0xD3, 0x00, 0xB0, 0x0A, 0x52, 0x03, 0x54, 0x00, 0x66, 0x00, 0x07, 0x00, 0x0E, 0x7C, 0x72, 0xF6, 0x39, 0x00, 0xA1, 0x5B, 0x3D, 0x00, 0x00, 0xA1, 0x56, 0x3D, 0x00, 0x28, 0xA1, 0x51, 0x3D, 0x00, 0x29, 0xA1, 0x4C, 0x62, 0xD0, 0x03, 0x51, 0xEC, 0x08, 0x51, 0xED, 0x08, 0x62, 0xD0, 0x01, 0x51, 0xE6, 0x08, 0x51, 0xE7, 0xB4, 0x07
+		}
+	},
+	{
+		288,
+		79,
+		0x39,
+		0x014B,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x4B, 0x08, 0x7C, 0x4B, 0x61, 0x38, 0xFC, 0x7C, 0x6F, 0xA8, 0x62, 0xD0, 0x03, 0x51, 0xEA, 0x08, 0x51, 0xEB, 0x08, 0x62, 0xD0, 0x01, 0x51, 0xE4, 0x08, 0x51, 0xE5, 0x08, 0x7C, 0x4B, 0x61, 0x38, 0xFC, 0x7C, 0x6F, 0x76, 0x62, 0xD0, 0x04, 0x52, 0x00, 0x3A, 0xD4, 0xB0, 0x04, 0x56, 0x01, 0x01, 0x3D, 0x01, 0x00, 0xB0, 0xD4, 0x62, 0xD0, 0x00, 0x3C, 0x39, 0x00, 0xB0, 0x73, 0x62, 0xD0, 0x00, 0x3C, 0xBA, 0x14
+		}
+	},
+	{
+		289,
+		79,
+		0x39,
+		0x014C,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x4C, 0x3A, 0x00, 0xB0, 0x6B, 0x62, 0xD0, 0x04, 0x3C, 0x9F, 0x00, 0xB0, 0x06, 0x3C, 0xA0, 0x00, 0xA0, 0x0E, 0x62, 0xD0, 0x04, 0x3C, 0x9D, 0x00, 0xB0, 0x56, 0x3C, 0x9E, 0x00, 0xB0, 0x51, 0x3D, 0x00, 0x10, 0xB0, 0x13, 0x62, 0xD0, 0x04, 0x3C, 0xD4, 0x14, 0xA0, 0x06, 0x3C, 0xD4, 0x1C, 0xB0, 0x3F, 0x56, 0x01, 0x01, 0x80, 0x3A, 0x3D, 0x00, 0x1C, 0xB0, 0x13, 0x62, 0xD0, 0x04, 0x3C, 0xD4, 0x18, 0x4D, 0x3B
+		}
+	},
+	{
+		290,
+		79,
+		0x39,
+		0x014D,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x4D, 0xA0, 0x06, 0x3C, 0xD4, 0x10, 0xB0, 0x28, 0x56, 0x01, 0x01, 0x80, 0x23, 0x3D, 0x00, 0x18, 0xA0, 0x06, 0x3D, 0x00, 0x14, 0xB0, 0x19, 0x62, 0xD0, 0x00, 0x52, 0x00, 0x11, 0x04, 0x7C, 0x70, 0x27, 0xA0, 0x0A, 0x52, 0x00, 0x01, 0x04, 0x7C, 0x70, 0x27, 0xB0, 0x04, 0x56, 0x01, 0x01, 0x3D, 0x00, 0x10, 0xB0, 0x18, 0x62, 0xD0, 0x04, 0x3C, 0xD4, 0x10, 0xA0, 0x0B, 0x3C, 0xD4, 0x1E, 0xA0, 0x06, 0x64, 0x6A
+		}
+	},
+	{
+		291,
+		79,
+		0x39,
+		0x014E,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x4E, 0x3C, 0xD4, 0x12, 0xB0, 0x43, 0x56, 0x01, 0x01, 0x80, 0x3E, 0x3D, 0x00, 0x1E, 0xB0, 0x18, 0x62, 0xD0, 0x04, 0x3C, 0xD4, 0x1E, 0xA0, 0x0B, 0x3C, 0xD4, 0x10, 0xA0, 0x06, 0x3C, 0xD4, 0x1C, 0xB0, 0x27, 0x56, 0x01, 0x01, 0x80, 0x22, 0x62, 0xD0, 0x04, 0x51, 0xD4, 0x3B, 0x00, 0xA0, 0x16, 0x62, 0xD0, 0x00, 0x52, 0x00, 0x11, 0x02, 0x7C, 0x70, 0x27, 0xA0, 0x0A, 0x52, 0x00, 0x01, 0x02, 0x7C, 0x91, 0xC5
+		}
+	},
+	{
+		292,
+		79,
+		0x39,
+		0x014F,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x4F, 0x70, 0x27, 0xB0, 0x04, 0x56, 0x01, 0x01, 0x3D, 0x01, 0x01, 0xB0, 0x22, 0x62, 0xD0, 0x00, 0x7C, 0x72, 0xF6, 0x53, 0xE9, 0x62, 0xD0, 0x04, 0x51, 0xD5, 0x62, 0xD0, 0x00, 0x3A, 0xE9, 0xA0, 0x1B, 0x7C, 0x6E, 0x82, 0x62, 0xD0, 0x04, 0x76, 0xD5, 0x56, 0x00, 0x00, 0x80, 0x0E, 0x7C, 0x73, 0x5F, 0x52, 0x00, 0x62, 0xD0, 0x04, 0x53, 0xD4, 0x56, 0x00, 0x00, 0x3D, 0x00, 0x00, 0xA0, 0x52, 0x62, 0x27, 0xF2
+		}
+	},
+	{
+		293,
+		79,
+		0x39,
+		0x0150,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x50, 0xD0, 0x03, 0x51, 0xEC, 0x08, 0x51, 0xED, 0x08, 0x62, 0xD0, 0x01, 0x51, 0xE6, 0x08, 0x51, 0xE7, 0x08, 0x7C, 0x4B, 0x61, 0x38, 0xFC, 0x7C, 0x6F, 0xA8, 0x62, 0xD0, 0x03, 0x51, 0xEA, 0x08, 0x51, 0xEB, 0x08, 0x62, 0xD0, 0x01, 0x51, 0xE4, 0x08, 0x51, 0xE5, 0x08, 0x7C, 0x4B, 0x61, 0x38, 0xFC, 0x7C, 0x6F, 0x76, 0x7C, 0x4C, 0x14, 0x62, 0xD0, 0x04, 0x52, 0x00, 0x3A, 0xD0, 0xB0, 0x08, 0x62, 0xAE, 0x01
+		}
+	},
+	{
+		294,
+		79,
+		0x39,
+		0x0151,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x51, 0xD0, 0x04, 0x76, 0xCF, 0x80, 0x04, 0x7C, 0x70, 0x5B, 0x52, 0x00, 0x62, 0xD0, 0x04, 0x53, 0xD0, 0x52, 0x00, 0x62, 0xD0, 0x00, 0x38, 0xFC, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x03, 0x7C, 0x72, 0xAB, 0x56, 0x00, 0x00, 0x52, 0xFC, 0x08, 0x7C, 0x5B, 0xE0, 0x38, 0xFF, 0x62, 0xD0, 0x00, 0x54, 0x00, 0x52, 0xFC, 0x08, 0x90, 0x6D, 0x62, 0xD0, 0x00, 0x54, 0x01, 0x52, 0xFC, 0x08, 0x90, 0x96, 0x38, 0xC7, 0x34
+		}
+	},
+	{
+		295,
+		79,
+		0x39,
+		0x0152,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x52, 0xFE, 0x62, 0xD0, 0x00, 0x54, 0x02, 0x3D, 0x00, 0x00, 0xA0, 0x05, 0x52, 0x00, 0x80, 0x12, 0x3D, 0x01, 0x00, 0xA0, 0x08, 0x52, 0x01, 0x62, 0xD0, 0x00, 0x80, 0x06, 0x52, 0x02, 0x62, 0xD0, 0x00, 0x38, 0xFD, 0x20, 0x7F, 0x10, 0x4F, 0x50, 0x00, 0x08, 0x52, 0xFC, 0x08, 0x9B, 0x7E, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x56, 0x00, 0x00, 0x50, 0x01, 0x08, 0x52, 0x5D, 0x61
+		}
+	},
+	{
+		296,
+		79,
+		0x39,
+		0x0153,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x53, 0xFC, 0x08, 0x9B, 0x68, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x54, 0x00, 0x3D, 0x00, 0x28, 0xA0, 0x0A, 0x3D, 0x00, 0x29, 0xA0, 0x05, 0x50, 0x00, 0x80, 0x06, 0x52, 0x00, 0x62, 0xD0, 0x00, 0x38, 0xFF, 0x20, 0x7F, 0x10, 0x4F, 0x50, 0x01, 0x08, 0x52, 0xFC, 0x08, 0x9B, 0x40, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x20, 0x7F, 0x10, 0x4F, 0x50, 0x00, 0x08, 0x52, 0xFC, 0x08, 0x90, 0x29, 0x38, 0xFE, 0x62, 0x5B, 0x5E
+		}
+	},
+	{
+		297,
+		79,
+		0x39,
+		0x0154,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x54, 0xD0, 0x00, 0x20, 0x7F, 0x10, 0x4F, 0x50, 0x01, 0x08, 0x52, 0xFC, 0x08, 0x90, 0x18, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x20, 0x7F, 0x10, 0x4F, 0x50, 0x02, 0x08, 0x52, 0xFC, 0x08, 0x90, 0x07, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x0F, 0x56, 0x00, 0x00, 0x56, 0x04, 0x00, 0x7C, 0x72, 0xAB, 0x56, 0x03, 0x00, 0x10, 0x7C, 0x1E, 0x80, 0x62, 0xD0, 0x00, 0x5A, 0xE9, 0x20, 0xE0, 0x69
+		}
+	},
+	{
+		298,
+		79,
+		0x39,
+		0x0155,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x55, 0x7C, 0x71, 0x22, 0x10, 0x7C, 0x1E, 0x8D, 0x62, 0xD0, 0x00, 0x5A, 0xE9, 0x20, 0x08, 0x51, 0xE9, 0x62, 0xD0, 0x04, 0x53, 0xAB, 0x18, 0x53, 0xAC, 0x3D, 0xFC, 0x02, 0xD0, 0x61, 0x3D, 0xFC, 0x00, 0xB0, 0x41, 0x62, 0xD0, 0x04, 0x55, 0xD0, 0x00, 0x62, 0xD0, 0x04, 0x7C, 0x71, 0xEE, 0x3C, 0xAD, 0x00, 0xB0, 0x06, 0x3C, 0xAE, 0x00, 0xA0, 0x22, 0x62, 0xD0, 0x04, 0x51, 0xAE, 0x08, 0x51, 0xAD, 0xF0, 0x8A
+		}
+	},
+	{
+		299,
+		79,
+		0x39,
+		0x0156,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x56, 0x62, 0xD0, 0x04, 0x53, 0xAF, 0x18, 0x53, 0xB0, 0x10, 0x50, 0x00, 0x5C, 0x7C, 0x1E, 0xB4, 0x20, 0x62, 0xD0, 0x03, 0x55, 0xDD, 0x00, 0x55, 0xDC, 0x00, 0x62, 0xD0, 0x04, 0x55, 0xA6, 0x00, 0x55, 0xA5, 0x00, 0x62, 0xD0, 0x04, 0x55, 0xDB, 0x00, 0x7C, 0x73, 0x58, 0x7C, 0x73, 0x51, 0x10, 0x50, 0x00, 0x5C, 0x7C, 0x1E, 0xCC, 0x20, 0x62, 0xD0, 0x00, 0x50, 0x00, 0x83, 0x71, 0x3D, 0xFC, 0x02, 0x20, 0xEB
+		}
+	},
+	{
+		300,
+		79,
+		0x39,
+		0x0157,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x57, 0xB3, 0x0F, 0x62, 0xD0, 0x04, 0x3C, 0xDC, 0x02, 0xD0, 0x55, 0x62, 0xD0, 0x00, 0x50, 0x0D, 0x10, 0x7C, 0x72, 0x01, 0x20, 0x3C, 0xE9, 0x00, 0xB0, 0x05, 0x39, 0x00, 0xA0, 0x33, 0x62, 0xD0, 0x00, 0x50, 0x0D, 0x10, 0x7C, 0x72, 0x01, 0x53, 0xE8, 0x20, 0x62, 0xD0, 0x04, 0x51, 0xAC, 0x62, 0xD0, 0x00, 0x12, 0xE8, 0x62, 0xD0, 0x04, 0x51, 0xAB, 0x62, 0xD0, 0x00, 0x1A, 0xE9, 0xD0, 0x1A, 0x7C, 0xE0, 0x6C
+		}
+	},
+	{
+		301,
+		79,
+		0x39,
+		0x0158,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x58, 0x4A, 0xD9, 0x7C, 0x4A, 0xD2, 0x7C, 0x4B, 0x1A, 0x62, 0xD0, 0x00, 0x50, 0x00, 0x83, 0x1F, 0x7C, 0x4A, 0xD9, 0x7C, 0x4A, 0xD2, 0x7C, 0x4B, 0x1A, 0x62, 0xD0, 0x04, 0x55, 0xDC, 0x02, 0x3D, 0xFB, 0x01, 0xA0, 0x06, 0x3D, 0xFB, 0x02, 0xB1, 0x49, 0x62, 0xD0, 0x01, 0x51, 0xEE, 0x08, 0x51, 0xEF, 0x08, 0x51, 0xE6, 0x08, 0x51, 0xE7, 0x08, 0x7C, 0x4B, 0x61, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x54, 0x7C, 0xA5
+		}
+	},
+	{
+		302,
+		79,
+		0x39,
+		0x0159,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x59, 0x08, 0x51, 0xE9, 0x54, 0x07, 0x62, 0xD0, 0x01, 0x51, 0xEC, 0x08, 0x51, 0xED, 0x08, 0x51, 0xE4, 0x08, 0x51, 0xE5, 0x08, 0x7C, 0x4B, 0x61, 0x38, 0xF8, 0x7C, 0x71, 0xF5, 0x62, 0xD0, 0x03, 0x51, 0xE8, 0x08, 0x51, 0xE9, 0x08, 0x62, 0xD0, 0x03, 0x51, 0xEC, 0x08, 0x51, 0xED, 0x08, 0x7C, 0x4B, 0x61, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x54, 0x0C, 0x51, 0xE9, 0x54, 0x0B, 0x62, 0xD0, 0x03, 0x51, 0x9A, 0xE2
+		}
+	},
+	{
+		303,
+		79,
+		0x39,
+		0x015A,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x5A, 0xE6, 0x08, 0x51, 0xE7, 0x08, 0x62, 0xD0, 0x03, 0x51, 0xEA, 0x08, 0x51, 0xEB, 0x08, 0x7C, 0x4B, 0x61, 0x38, 0xF8, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x54, 0x0E, 0x51, 0xE9, 0x54, 0x0D, 0x52, 0x08, 0x13, 0x0C, 0x54, 0x06, 0x52, 0x07, 0x1B, 0x0B, 0x54, 0x05, 0x62, 0xD0, 0x00, 0x51, 0x3B, 0x62, 0xD0, 0x00, 0x13, 0x06, 0x52, 0x05, 0x31, 0x80, 0x53, 0xE1, 0x50, 0x00, 0x31, 0x80, 0x1A, 0xE1, 0x96, 0xDB
+		}
+	},
+	{
+		304,
+		79,
+		0x39,
+		0x015B,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x5B, 0xD0, 0x06, 0x56, 0x00, 0x48, 0x80, 0x41, 0x52, 0x06, 0x11, 0x00, 0x52, 0x05, 0x31, 0x80, 0x19, 0x80, 0xD0, 0x35, 0x62, 0xD0, 0x00, 0x52, 0x06, 0x73, 0x53, 0xE8, 0x52, 0x05, 0x73, 0x53, 0xE9, 0x51, 0xE8, 0x01, 0x01, 0x54, 0x06, 0x51, 0xE9, 0x09, 0x00, 0x54, 0x05, 0x62, 0xD0, 0x00, 0x51, 0x3B, 0x62, 0xD0, 0x00, 0x13, 0x06, 0x52, 0x05, 0x31, 0x80, 0x53, 0xE1, 0x50, 0x00, 0x31, 0x80, 0xF9, 0xA2
+		}
+	},
+	{
+		305,
+		79,
+		0x39,
+		0x015C,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x5C, 0x1A, 0xE1, 0xD0, 0x04, 0x56, 0x00, 0x49, 0x52, 0x0A, 0x13, 0x0E, 0x54, 0x06, 0x52, 0x09, 0x1B, 0x0D, 0x54, 0x05, 0x62, 0xD0, 0x00, 0x51, 0x3C, 0x62, 0xD0, 0x00, 0x13, 0x06, 0x52, 0x05, 0x31, 0x80, 0x53, 0xE1, 0x50, 0x00, 0x31, 0x80, 0x1A, 0xE1, 0xD0, 0x06, 0x56, 0x00, 0x48, 0x80, 0x41, 0x52, 0x06, 0x11, 0x00, 0x52, 0x05, 0x31, 0x80, 0x19, 0x80, 0xD0, 0x35, 0x62, 0xD0, 0x00, 0x52, 0x2B, 0x07
+		}
+	},
+	{
+		306,
+		79,
+		0x39,
+		0x015D,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x5D, 0x06, 0x73, 0x53, 0xE8, 0x52, 0x05, 0x73, 0x53, 0xE9, 0x51, 0xE8, 0x01, 0x01, 0x54, 0x06, 0x51, 0xE9, 0x09, 0x00, 0x54, 0x05, 0x62, 0xD0, 0x00, 0x51, 0x3C, 0x62, 0xD0, 0x00, 0x13, 0x06, 0x52, 0x05, 0x31, 0x80, 0x53, 0xE1, 0x50, 0x00, 0x31, 0x80, 0x1A, 0xE1, 0xD0, 0x04, 0x56, 0x00, 0x49, 0x3D, 0xFB, 0x00, 0xA0, 0x06, 0x3D, 0xFB, 0x02, 0xB1, 0x57, 0x62, 0xD0, 0x00, 0x51, 0x3A, 0x08, 0x85, 0xBC
+		}
+	},
+	{
+		307,
+		79,
+		0x39,
+		0x015E,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x5E, 0x62, 0xD0, 0x00, 0x51, 0x39, 0x08, 0x62, 0xD0, 0x03, 0x51, 0xEA, 0x08, 0x51, 0xEB, 0x08, 0x62, 0xD0, 0x03, 0x51, 0xEC, 0x08, 0x51, 0xED, 0x08, 0x62, 0xD0, 0x01, 0x51, 0xE4, 0x08, 0x51, 0xE5, 0x08, 0x51, 0xE6, 0x08, 0x51, 0xE7, 0x08, 0x7C, 0x4D, 0x1E, 0x7C, 0x71, 0x88, 0x54, 0x01, 0x62, 0xD0, 0x03, 0x51, 0xEA, 0x08, 0x51, 0xEB, 0x08, 0x62, 0xD0, 0x03, 0x51, 0xEC, 0x08, 0x51, 0xED, 0x92, 0xD7
+		}
+	},
+	{
+		308,
+		79,
+		0x39,
+		0x015F,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x5F, 0x08, 0x62, 0xD0, 0x01, 0x51, 0xE4, 0x08, 0x51, 0xE5, 0x08, 0x51, 0xE6, 0x08, 0x51, 0xE7, 0x08, 0x7C, 0x4E, 0xE4, 0x38, 0xEE, 0x54, 0x02, 0x62, 0xD0, 0x00, 0x51, 0x3A, 0x08, 0x62, 0xD0, 0x00, 0x51, 0x39, 0x08, 0x62, 0xD0, 0x03, 0x51, 0xE6, 0x08, 0x51, 0xE7, 0x08, 0x62, 0xD0, 0x03, 0x51, 0xE8, 0x08, 0x51, 0xE9, 0x08, 0x62, 0xD0, 0x01, 0x51, 0xEC, 0x08, 0x51, 0xED, 0x08, 0x51, 0xEE, 0x51, 0x56
+		}
+	},
+	{
+		309,
+		79,
+		0x39,
+		0x0160,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x60, 0x08, 0x51, 0xEF, 0x08, 0x7C, 0x4D, 0x1E, 0x7C, 0x71, 0x88, 0x05, 0x01, 0x62, 0xD0, 0x03, 0x51, 0xE6, 0x08, 0x51, 0xE7, 0x08, 0x62, 0xD0, 0x03, 0x51, 0xE8, 0x08, 0x51, 0xE9, 0x08, 0x62, 0xD0, 0x01, 0x51, 0xEC, 0x08, 0x51, 0xED, 0x08, 0x51, 0xEE, 0x08, 0x51, 0xEF, 0x08, 0x7C, 0x4E, 0xE4, 0x38, 0xEE, 0x62, 0xD0, 0x00, 0x54, 0x03, 0x3D, 0x01, 0x00, 0xA0, 0x95, 0x3D, 0x02, 0xFF, 0xA0, 0x19, 0xE7
+		}
+	},
+	{
+		310,
+		79,
+		0x39,
+		0x0161,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x61, 0x90, 0x3D, 0x03, 0xFF, 0xA0, 0x8B, 0x52, 0x02, 0x3B, 0x03, 0xA0, 0x0B, 0x3D, 0x02, 0x08, 0xB0, 0x0B, 0x3D, 0x03, 0x01, 0xB0, 0x06, 0x7C, 0x71, 0xB9, 0x80, 0x76, 0x3D, 0x03, 0x08, 0xB0, 0x11, 0x3D, 0x02, 0x01, 0xB0, 0x0C, 0x52, 0x03, 0x03, 0x03, 0x54, 0x00, 0x07, 0x00, 0x2E, 0x80, 0x61, 0x52, 0x03, 0x3B, 0x02, 0xD0, 0x2C, 0x62, 0xD0, 0x00, 0x52, 0x02, 0x13, 0x03, 0x39, 0x01, 0xB0, 0xAA, 0x0A
+		}
+	},
+	{
+		311,
+		79,
+		0x39,
+		0x0162,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x62, 0x21, 0x50, 0x02, 0x08, 0x52, 0x02, 0x08, 0x7C, 0x4A, 0x10, 0x38, 0xFF, 0x18, 0x39, 0x00, 0xB0, 0x06, 0x7C, 0x71, 0xB9, 0x80, 0x3B, 0x52, 0x03, 0x03, 0x03, 0x54, 0x00, 0x07, 0x00, 0x2E, 0x80, 0x30, 0x52, 0x02, 0x3B, 0x03, 0xD0, 0x2A, 0x62, 0xD0, 0x00, 0x52, 0x03, 0x13, 0x02, 0x39, 0x01, 0xB0, 0x1F, 0x50, 0x02, 0x08, 0x52, 0x03, 0x08, 0x7C, 0x4A, 0x10, 0x38, 0xFF, 0x18, 0x39, 0x00, 0x26, 0x03
+		}
+	},
+	{
+		312,
+		79,
+		0x39,
+		0x0163,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x63, 0xB0, 0x0C, 0x52, 0x03, 0x03, 0x03, 0x54, 0x00, 0x07, 0x00, 0x2E, 0x80, 0x04, 0x7C, 0x71, 0xB9, 0x3D, 0x00, 0x00, 0xA0, 0x54, 0x3D, 0xFB, 0x01, 0xB0, 0x0D, 0x52, 0x00, 0x08, 0x90, 0x52, 0x38, 0xFF, 0x62, 0xD0, 0x00, 0x80, 0x48, 0x3D, 0xFB, 0x00, 0xB0, 0x12, 0x62, 0xD0, 0x00, 0x52, 0x00, 0x29, 0x30, 0x08, 0x91, 0x16, 0x38, 0xFF, 0x62, 0xD0, 0x00, 0x80, 0x32, 0x3D, 0xFB, 0x02, 0xB0, 0xE9, 0x8A
+		}
+	},
+	{
+		313,
+		79,
+		0x39,
+		0x0164,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x64, 0x28, 0x3D, 0x00, 0x48, 0xA0, 0x06, 0x3D, 0x00, 0x49, 0xB0, 0x0D, 0x52, 0x00, 0x08, 0x90, 0x21, 0x38, 0xFF, 0x62, 0xD0, 0x00, 0x80, 0x17, 0x62, 0xD0, 0x00, 0x52, 0x00, 0x29, 0x30, 0x08, 0x90, 0xEA, 0x38, 0xFF, 0x62, 0xD0, 0x00, 0x80, 0x06, 0x62, 0xD0, 0x00, 0x50, 0x00, 0x38, 0xF1, 0x20, 0x7F, 0x10, 0x4F, 0x7C, 0x72, 0xEE, 0x39, 0x00, 0xA0, 0x43, 0x3D, 0xFC, 0x00, 0xA0, 0x3E, 0x62, 0x09, 0xCB
+		}
+	},
+	{
+		314,
+		79,
+		0x39,
+		0x0165,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x65, 0xD0, 0x04, 0x52, 0xFC, 0x3A, 0xD7, 0xB0, 0x2C, 0x62, 0xD0, 0x00, 0x7C, 0x72, 0xEE, 0x53, 0xE9, 0x62, 0xD0, 0x04, 0x51, 0xD8, 0x62, 0xD0, 0x00, 0x3A, 0xE9, 0xA0, 0x21, 0x7C, 0x72, 0x39, 0x39, 0x00, 0xA0, 0x04, 0x7C, 0x72, 0x8C, 0x7C, 0x4A, 0xD2, 0x62, 0xD0, 0x04, 0x76, 0xD8, 0x56, 0xFC, 0x00, 0x80, 0x0A, 0x62, 0xD0, 0x04, 0x55, 0xD8, 0x00, 0x7C, 0x72, 0x59, 0x7C, 0x72, 0x39, 0x39, 0x4E, 0x56
+		}
+	},
+	{
+		315,
+		79,
+		0x39,
+		0x0166,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x66, 0x00, 0xA0, 0x4D, 0x3D, 0xFC, 0x00, 0xA0, 0x48, 0x62, 0xD0, 0x04, 0x3C, 0xDB, 0x00, 0xA0, 0x3D, 0x3C, 0xDB, 0x48, 0xA0, 0x38, 0x3C, 0xDB, 0x49, 0xA0, 0x33, 0x62, 0xD0, 0x04, 0x52, 0xFC, 0x3A, 0xD7, 0xB0, 0x22, 0x62, 0xD0, 0x00, 0x7C, 0x72, 0x39, 0x53, 0xE9, 0x62, 0xD0, 0x04, 0x51, 0xD6, 0x62, 0xD0, 0x00, 0x3A, 0xE9, 0xA0, 0x19, 0x7C, 0x4A, 0xD2, 0x62, 0xD0, 0x04, 0x76, 0xD6, 0x56, 0x79, 0xAD
+		}
+	},
+	{
+		316,
+		79,
+		0x39,
+		0x0167,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x67, 0xFC, 0x00, 0x80, 0x0C, 0x7C, 0x72, 0x8C, 0x7C, 0x72, 0x59, 0x80, 0x04, 0x7C, 0x72, 0x8C, 0x3D, 0xFC, 0x00, 0xA0, 0x31, 0x7C, 0x4B, 0x1A, 0x7C, 0x4B, 0x99, 0x7C, 0x4C, 0x14, 0x7C, 0x6E, 0xC0, 0x62, 0xD0, 0x04, 0x52, 0xFC, 0x3A, 0xD0, 0xB0, 0x08, 0x62, 0xD0, 0x04, 0x76, 0xCF, 0x80, 0x04, 0x7C, 0x70, 0x5B, 0x52, 0xFC, 0x62, 0xD0, 0x04, 0x53, 0xD0, 0x52, 0xFC, 0x62, 0xD0, 0x04, 0x53, 0x22, 0x00
+		}
+	},
+	{
+		317,
+		79,
+		0x39,
+		0x0168,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x68, 0xDB, 0x7C, 0x73, 0x58, 0x52, 0xFC, 0x62, 0xD0, 0x00, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x05, 0x7C, 0x72, 0x64, 0x39, 0x00, 0xA1, 0x10, 0x56, 0x00, 0x00, 0x3D, 0xFC, 0x00, 0xA1, 0x08, 0x7C, 0x4B, 0x99, 0x62, 0xD0, 0x04, 0x52, 0xFC, 0x3A, 0xD9, 0xB0, 0x04, 0x56, 0x00, 0x01, 0x3D, 0x00, 0x00, 0xB0, 0xC1, 0x62, 0xD0, 0x00, 0x3C, 0x39, 0x00, 0xB0, 0x73, 0x62, 0xD0, 0x00, 0x3C, 0x3A, 0x00, 0x73, 0xA3
+		}
+	},
+	{
+		318,
+		79,
+		0x39,
+		0x0169,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x69, 0xB0, 0x6B, 0x62, 0xD0, 0x04, 0x3C, 0x9F, 0x00, 0xB0, 0x06, 0x3C, 0xA0, 0x00, 0xA0, 0x0E, 0x62, 0xD0, 0x04, 0x3C, 0x9D, 0x00, 0xB0, 0x56, 0x3C, 0x9E, 0x00, 0xB0, 0x51, 0x3D, 0xFC, 0x30, 0xB0, 0x13, 0x62, 0xD0, 0x04, 0x3C, 0xD9, 0x34, 0xA0, 0x06, 0x3C, 0xD9, 0x3C, 0xB0, 0x3F, 0x56, 0x00, 0x01, 0x80, 0x3A, 0x3D, 0xFC, 0x3C, 0xB0, 0x13, 0x62, 0xD0, 0x04, 0x3C, 0xD9, 0x38, 0xA0, 0x06, 0x5F, 0x7C
+		}
+	},
+	{
+		319,
+		79,
+		0x39,
+		0x016A,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x6A, 0x3C, 0xD9, 0x30, 0xB0, 0x28, 0x56, 0x00, 0x01, 0x80, 0x23, 0x3D, 0xFC, 0x38, 0xA0, 0x06, 0x3D, 0xFC, 0x34, 0xB0, 0x19, 0x62, 0xD0, 0x00, 0x52, 0xFC, 0x11, 0x04, 0x7C, 0x70, 0x34, 0xA0, 0x0A, 0x52, 0xFC, 0x01, 0x04, 0x7C, 0x70, 0x34, 0xB0, 0x04, 0x56, 0x00, 0x01, 0x3D, 0xFC, 0x30, 0xB0, 0x13, 0x62, 0xD0, 0x04, 0x3C, 0xD9, 0x3E, 0xA0, 0x06, 0x3C, 0xD9, 0x32, 0xB0, 0x35, 0x56, 0x00, 0x1E, 0xFB
+		}
+	},
+	{
+		320,
+		79,
+		0x39,
+		0x016B,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x6B, 0x01, 0x80, 0x30, 0x3D, 0xFC, 0x3E, 0xB0, 0x13, 0x62, 0xD0, 0x04, 0x3C, 0xD9, 0x30, 0xA0, 0x06, 0x3C, 0xD9, 0x3C, 0xB0, 0x1E, 0x56, 0x00, 0x01, 0x80, 0x19, 0x62, 0xD0, 0x00, 0x52, 0xFC, 0x11, 0x02, 0x7C, 0x70, 0x34, 0xA0, 0x0A, 0x52, 0xFC, 0x01, 0x02, 0x7C, 0x70, 0x34, 0xB0, 0x04, 0x56, 0x00, 0x01, 0x3D, 0x00, 0x01, 0xB0, 0x1F, 0x7C, 0x72, 0x64, 0x53, 0xE9, 0x62, 0xD0, 0x04, 0x51, 0x16, 0xEC
+		}
+	},
+	{
+		321,
+		79,
+		0x39,
+		0x016C,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x6C, 0xDA, 0x62, 0xD0, 0x00, 0x3A, 0xE9, 0xA0, 0x1E, 0x7C, 0x4A, 0xD2, 0x62, 0xD0, 0x04, 0x76, 0xDA, 0x56, 0xFC, 0x00, 0x80, 0x11, 0x62, 0xD0, 0x04, 0x55, 0xDA, 0x00, 0x52, 0xFC, 0x62, 0xD0, 0x04, 0x53, 0xD9, 0x56, 0xFC, 0x00, 0x3D, 0xFC, 0x00, 0xA0, 0xAE, 0x7C, 0x4B, 0x1A, 0x62, 0xD0, 0x03, 0x51, 0xED, 0x62, 0xD0, 0x03, 0x02, 0xE9, 0x62, 0xD0, 0x00, 0x53, 0xE8, 0x62, 0xD0, 0x03, 0x51, 0x43, 0x47
+		}
+	},
+	{
+		322,
+		79,
+		0x39,
+		0x016D,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x6D, 0xEC, 0x62, 0xD0, 0x03, 0x0A, 0xE8, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x7C, 0x6D, 0xF3, 0x51, 0xE8, 0x54, 0x02, 0x51, 0xE9, 0x54, 0x01, 0x62, 0xD0, 0x03, 0x51, 0xEB, 0x62, 0xD0, 0x03, 0x02, 0xE7, 0x62, 0xD0, 0x00, 0x53, 0xE8, 0x62, 0xD0, 0x03, 0x51, 0xEA, 0x62, 0xD0, 0x03, 0x0A, 0xE6, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x7C, 0x6D, 0xF3, 0x51, 0xE8, 0x54, 0x04, 0x51, 0xE9, 0x54, 0x03, 0x52, 0xCA, 0x56
+		}
+	},
+	{
+		323,
+		79,
+		0x39,
+		0x016E,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x6E, 0x01, 0x08, 0x52, 0x02, 0x08, 0x62, 0xD0, 0x04, 0x51, 0xA3, 0x08, 0x51, 0xA4, 0x08, 0x7C, 0x4B, 0x61, 0x38, 0xFC, 0x7C, 0x6F, 0xA8, 0x52, 0x03, 0x08, 0x52, 0x04, 0x08, 0x62, 0xD0, 0x04, 0x51, 0xA1, 0x08, 0x51, 0xA2, 0x08, 0x7C, 0x4B, 0x61, 0x38, 0xFC, 0x7C, 0x6F, 0x76, 0x7C, 0x4C, 0x14, 0x7C, 0x6E, 0xC0, 0x62, 0xD0, 0x04, 0x52, 0xFC, 0x3A, 0xD0, 0xB0, 0x08, 0x62, 0xD0, 0x04, 0x76, 0x9E, 0xFF
+		}
+	},
+	{
+		324,
+		79,
+		0x39,
+		0x016F,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x6F, 0xCF, 0x80, 0x04, 0x7C, 0x70, 0x5B, 0x52, 0xFC, 0x62, 0xD0, 0x04, 0x53, 0xD0, 0x52, 0xFC, 0x62, 0xD0, 0x04, 0x53, 0xDB, 0x7C, 0x73, 0x51, 0x52, 0xFC, 0x62, 0xD0, 0x00, 0x38, 0xFB, 0x20, 0x7F, 0x10, 0x4F, 0x50, 0x00, 0x08, 0x52, 0xFC, 0x08, 0x90, 0x07, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x04, 0x7C, 0x6F, 0xC9, 0x3D, 0xFB, 0x00, 0xA0, 0x37, 0x62, 0xD0, 0x04, 0xC4, 0x4C
+		}
+	},
+	{
+		325,
+		79,
+		0x39,
+		0x0170,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x70, 0x55, 0xCD, 0x00, 0x7C, 0x70, 0x62, 0x50, 0x0D, 0x10, 0x08, 0x57, 0x8D, 0x28, 0x53, 0xE7, 0x18, 0x75, 0x09, 0x00, 0x28, 0x20, 0x02, 0xE8, 0x53, 0xE8, 0x51, 0xE7, 0x0A, 0xE9, 0x10, 0x08, 0x51, 0xE8, 0x20, 0x7C, 0x1E, 0xE4, 0x20, 0x7C, 0x73, 0x43, 0x7C, 0x70, 0x41, 0x52, 0xFB, 0x62, 0xD0, 0x00, 0x83, 0x5B, 0x3D, 0xFC, 0x00, 0xB2, 0xB8, 0x7C, 0x70, 0x5B, 0x10, 0x7C, 0x1E, 0x9A, 0x62, 0xB4, 0x2D
+		}
+	},
+	{
+		326,
+		79,
+		0x39,
+		0x0171,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x71, 0xD0, 0x00, 0x5A, 0xE9, 0x20, 0x08, 0x51, 0xE9, 0x62, 0xD0, 0x04, 0x53, 0xA9, 0x18, 0x53, 0xAA, 0x10, 0x7C, 0x1E, 0xA7, 0x62, 0xD0, 0x00, 0x5A, 0xE9, 0x20, 0x08, 0x51, 0xE9, 0x62, 0xD0, 0x04, 0x53, 0xA7, 0x18, 0x53, 0xA8, 0x62, 0xD0, 0x04, 0x3C, 0xA9, 0x00, 0xB0, 0x06, 0x3C, 0xAA, 0x00, 0xA1, 0x37, 0x62, 0xD0, 0x04, 0x3C, 0xCC, 0x01, 0xB0, 0xFB, 0x62, 0xD0, 0x00, 0x50, 0x0D, 0x10, 0x3F, 0x44
+		}
+	},
+	{
+		327,
+		79,
+		0x39,
+		0x0172,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x72, 0x08, 0x57, 0x8F, 0x28, 0x53, 0xE9, 0x18, 0x75, 0x09, 0x00, 0x28, 0x20, 0x62, 0xD0, 0x04, 0x12, 0xAA, 0x7C, 0x71, 0x7F, 0x1A, 0xA9, 0xD0, 0xDD, 0x62, 0xD0, 0x00, 0x50, 0x0D, 0x10, 0x08, 0x57, 0x8D, 0x28, 0x53, 0xE9, 0x18, 0x75, 0x09, 0x00, 0x28, 0x53, 0xE8, 0x20, 0x7C, 0x70, 0x77, 0xD0, 0xC4, 0x7C, 0x72, 0xB9, 0x3A, 0xFE, 0xB0, 0x08, 0x7C, 0x72, 0xC2, 0x3A, 0xFF, 0xA0, 0x96, 0x62, 0x15, 0xF1
+		}
+	},
+	{
+		328,
+		79,
+		0x39,
+		0x0173,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x73, 0xD0, 0x03, 0x51, 0xF4, 0x62, 0xD0, 0x03, 0x3A, 0xFC, 0xB0, 0x0D, 0x62, 0xD0, 0x03, 0x51, 0xF5, 0x62, 0xD0, 0x03, 0x3A, 0xFD, 0xA0, 0x7E, 0x62, 0xD0, 0x03, 0x51, 0xF6, 0x08, 0x51, 0xF7, 0x08, 0x62, 0xD0, 0x03, 0x51, 0xFE, 0x08, 0x51, 0xFF, 0x08, 0x7C, 0x4B, 0x61, 0x38, 0xFC, 0x62, 0xD0, 0x00, 0x50, 0x0D, 0x10, 0x57, 0x96, 0x28, 0x20, 0x7C, 0x71, 0xD1, 0xC0, 0x27, 0x62, 0xD0, 0x03, 0x07, 0xD6
+		}
+	},
+	{
+		329,
+		79,
+		0x39,
+		0x0174,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x74, 0x51, 0xF4, 0x08, 0x51, 0xF5, 0x08, 0x62, 0xD0, 0x03, 0x51, 0xFC, 0x08, 0x51, 0xFD, 0x08, 0x7C, 0x4B, 0x61, 0x38, 0xFC, 0x62, 0xD0, 0x00, 0x50, 0x0D, 0x10, 0x57, 0x97, 0x28, 0x20, 0x7C, 0x71, 0xD1, 0xD0, 0x32, 0x56, 0x01, 0x01, 0x62, 0xD0, 0x04, 0x55, 0xCD, 0x00, 0x7C, 0x70, 0x41, 0x62, 0xD0, 0x03, 0x50, 0x0D, 0x10, 0x08, 0x57, 0x9D, 0x28, 0x53, 0xBC, 0x18, 0x75, 0x09, 0x00, 0x28, 0xDC, 0x81
+		}
+	},
+	{
+		330,
+		79,
+		0x39,
+		0x0175,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x75, 0x53, 0xBD, 0x20, 0x50, 0x0D, 0x10, 0x08, 0x57, 0x9F, 0x28, 0x53, 0xBE, 0x18, 0x75, 0x09, 0x00, 0x28, 0x53, 0xBF, 0x20, 0x3D, 0x01, 0x00, 0xB0, 0x1C, 0x62, 0xD0, 0x04, 0x3C, 0xCD, 0x00, 0xB0, 0x07, 0x7C, 0x6F, 0x87, 0x56, 0x00, 0x20, 0x62, 0xD0, 0x04, 0x76, 0xCD, 0x3C, 0xCD, 0x01, 0xB0, 0x04, 0x7C, 0x71, 0x3A, 0x62, 0xD0, 0x04, 0x3C, 0xCC, 0x02, 0xB0, 0x2D, 0x62, 0xD0, 0x00, 0x50, 0xA9, 0x1C
+		}
+	},
+	{
+		331,
+		79,
+		0x39,
+		0x0176,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x76, 0x0D, 0x10, 0x08, 0x57, 0x8B, 0x28, 0x53, 0xE9, 0x18, 0x75, 0x09, 0x00, 0x28, 0x20, 0x62, 0xD0, 0x04, 0x12, 0xAA, 0x7C, 0x71, 0x7F, 0x1A, 0xA9, 0xD0, 0x0F, 0x7C, 0x70, 0x62, 0x7C, 0x70, 0x77, 0xD0, 0x07, 0x56, 0x00, 0x40, 0x7C, 0x4B, 0x8F, 0x62, 0xD0, 0x03, 0x3C, 0xEE, 0x00, 0xB0, 0x06, 0x3C, 0xEF, 0x00, 0xA1, 0x26, 0x62, 0xD0, 0x04, 0x3C, 0xCC, 0x00, 0xB0, 0x42, 0x62, 0xD0, 0x04, 0x2A, 0x1F
+		}
+	},
+	{
+		332,
+		79,
+		0x39,
+		0x0177,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x77, 0x3C, 0xCD, 0x01, 0xB0, 0x3A, 0x62, 0xD0, 0x03, 0x51, 0xEF, 0x62, 0xD0, 0x04, 0x12, 0xA8, 0x62, 0xD0, 0x03, 0x51, 0xEE, 0x62, 0xD0, 0x04, 0x1A, 0xA7, 0xD0, 0x0D, 0x7C, 0x72, 0x6F, 0x54, 0x03, 0x7C, 0x72, 0x7A, 0x54, 0x02, 0x80, 0x04, 0x7C, 0x70, 0x41, 0x62, 0xD0, 0x00, 0x50, 0x0D, 0x10, 0x7C, 0x72, 0x0D, 0x20, 0x7C, 0x73, 0x4A, 0xD0, 0xEA, 0x7C, 0x4B, 0x8F, 0x80, 0xE5, 0x62, 0xD0, 0x5E, 0x88
+		}
+	},
+	{
+		333,
+		79,
+		0x39,
+		0x0178,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x78, 0x04, 0x3C, 0xCC, 0x01, 0xB0, 0xDD, 0x7C, 0x73, 0x35, 0xB0, 0xD8, 0x62, 0xD0, 0x03, 0x51, 0xEF, 0x62, 0xD0, 0x04, 0x12, 0xA8, 0x62, 0xD0, 0x03, 0x51, 0xEE, 0x62, 0xD0, 0x04, 0x1A, 0xA7, 0xD0, 0x0D, 0x7C, 0x72, 0x6F, 0x53, 0xEF, 0x7C, 0x72, 0x7A, 0x53, 0xEE, 0x80, 0x04, 0x7C, 0x70, 0x41, 0x62, 0xD0, 0x00, 0x50, 0x0D, 0x10, 0x08, 0x57, 0x93, 0x28, 0x53, 0xE9, 0x18, 0x75, 0x09, 0x00, 0x77, 0xBB
+		}
+	},
+	{
+		334,
+		79,
+		0x39,
+		0x0179,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x79, 0x28, 0x20, 0x62, 0xD0, 0x03, 0x12, 0xEF, 0x7C, 0x70, 0xA1, 0x1A, 0xEE, 0xD0, 0x88, 0x62, 0xD0, 0x00, 0x50, 0x0D, 0x10, 0x7C, 0x72, 0x0D, 0x53, 0xE8, 0x20, 0x62, 0xD0, 0x03, 0x51, 0xEF, 0x62, 0xD0, 0x00, 0x12, 0xE8, 0x62, 0xD0, 0x03, 0x51, 0xEE, 0x62, 0xD0, 0x00, 0x1A, 0xE9, 0xD0, 0x66, 0x62, 0xD0, 0x03, 0x51, 0xF6, 0x08, 0x51, 0xF7, 0x08, 0x62, 0xD0, 0x03, 0x51, 0xBC, 0x08, 0x51, 0x4F, 0x6C
+		}
+	},
+	{
+		335,
+		79,
+		0x39,
+		0x017A,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x7A, 0xBD, 0x08, 0x7C, 0x4B, 0x61, 0x38, 0xFC, 0x62, 0xD0, 0x00, 0x50, 0x0D, 0x10, 0x57, 0x95, 0x28, 0x20, 0x7C, 0x70, 0xBF, 0xD0, 0x2F, 0x62, 0xD0, 0x03, 0x51, 0xF4, 0x08, 0x51, 0xF5, 0x08, 0x62, 0xD0, 0x03, 0x51, 0xBE, 0x08, 0x51, 0xBF, 0x08, 0x7C, 0x4B, 0x61, 0x38, 0xFC, 0x62, 0xD0, 0x00, 0x50, 0x0D, 0x10, 0x57, 0x95, 0x28, 0x20, 0x7C, 0x70, 0xBF, 0xD0, 0x09, 0x56, 0x00, 0x22, 0x7C, 0x79, 0xC1
+		}
+	},
+	{
+		336,
+		79,
+		0x39,
+		0x017B,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x7B, 0x4B, 0x8F, 0x80, 0x1F, 0x7C, 0x6F, 0x87, 0x62, 0xD0, 0x04, 0x55, 0xCD, 0x01, 0x7C, 0x71, 0x3A, 0x56, 0x00, 0x20, 0x80, 0x0E, 0x7C, 0x4B, 0x8F, 0x80, 0x09, 0x7C, 0x73, 0x35, 0xB0, 0x04, 0x7C, 0x4B, 0x8F, 0x10, 0x50, 0x00, 0x5C, 0x7C, 0x1E, 0xE4, 0x20, 0x7C, 0x73, 0x43, 0x80, 0x53, 0x62, 0xD0, 0x04, 0x3C, 0xCC, 0x00, 0xB0, 0x04, 0x7C, 0x4A, 0xD9, 0x52, 0xFC, 0x62, 0xD0, 0x04, 0x53, 0x43, 0x56
+		}
+	},
+	{
+		337,
+		79,
+		0x39,
+		0x017C,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x7C, 0xCC, 0x62, 0xD0, 0x01, 0x51, 0xE7, 0x08, 0x51, 0xE6, 0x62, 0xD0, 0x03, 0x53, 0xF6, 0x18, 0x53, 0xF7, 0x62, 0xD0, 0x01, 0x51, 0xE5, 0x08, 0x51, 0xE4, 0x62, 0xD0, 0x03, 0x53, 0xF4, 0x18, 0x53, 0xF5, 0x62, 0xD0, 0x01, 0x51, 0xEF, 0x08, 0x51, 0xEE, 0x62, 0xD0, 0x03, 0x53, 0xF2, 0x18, 0x53, 0xF3, 0x62, 0xD0, 0x01, 0x51, 0xED, 0x08, 0x51, 0xEC, 0x62, 0xD0, 0x03, 0x53, 0xF0, 0x18, 0x53, 0x03, 0xD7
+		}
+	},
+	{
+		338,
+		79,
+		0x39,
+		0x017D,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x7D, 0xF1, 0x3D, 0x00, 0x40, 0xB0, 0x43, 0x7C, 0x72, 0xC2, 0x02, 0xF3, 0x62, 0xD0, 0x00, 0x53, 0xE8, 0x7C, 0x72, 0xB9, 0x0A, 0xF2, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x7C, 0x6D, 0xF3, 0x51, 0xE8, 0x08, 0x51, 0xE9, 0x62, 0xD0, 0x04, 0x53, 0xA3, 0x18, 0x53, 0xA4, 0x62, 0xD0, 0x03, 0x51, 0xF5, 0x02, 0xF5, 0x62, 0xD0, 0x00, 0x53, 0xE8, 0x62, 0xD0, 0x03, 0x51, 0xF4, 0x0A, 0xF4, 0x62, 0xD0, 0x00, 0x9F, 0x10
+		}
+	},
+	{
+		339,
+		79,
+		0x39,
+		0x017E,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x7E, 0x53, 0xE9, 0x7C, 0x6D, 0xF3, 0x7C, 0x71, 0xC3, 0x52, 0x00, 0x62, 0xD0, 0x00, 0x38, 0xFC, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x06, 0x62, 0xD0, 0x00, 0x3C, 0x0E, 0x00, 0xA0, 0x06, 0x3D, 0xFC, 0xFF, 0xB0, 0x09, 0x62, 0xD0, 0x04, 0x55, 0xE2, 0x01, 0x85, 0xED, 0x62, 0xD0, 0x04, 0x3C, 0xE1, 0x00, 0xA0, 0x06, 0x3C, 0xE1, 0xFF, 0xB0, 0x74, 0x56, 0x00, 0x00, 0x56, 0x00, 0x00, 0x80, 0x65, 0x62, 0xDB, 0x89
+		}
+	},
+	{
+		340,
+		79,
+		0x39,
+		0x017F,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x7F, 0xD0, 0x00, 0x52, 0x00, 0x7C, 0x6D, 0x8A, 0x06, 0xE8, 0xE0, 0x7C, 0x71, 0x08, 0x62, 0xD0, 0x04, 0x51, 0xE2, 0x62, 0xD0, 0x00, 0x3F, 0xE8, 0x52, 0x00, 0x7C, 0x6D, 0x8A, 0x06, 0xE8, 0xE1, 0x7C, 0x71, 0x08, 0x7C, 0x72, 0x9A, 0x62, 0xD0, 0x04, 0x76, 0xE2, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0xB8, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x53, 0xE9, 0x7C, 0x6E, 0xAE, 0x06, 0xE6, 0x45, 0x5E
+		}
+	},
+	{
+		341,
+		79,
+		0x39,
+		0x0180,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x80, 0xB0, 0x0E, 0xE7, 0x03, 0x7C, 0x6D, 0xEA, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0xB4, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x53, 0xE9, 0x7C, 0x6E, 0xAE, 0x06, 0xE6, 0xEA, 0x0E, 0xE7, 0x00, 0x7C, 0x6D, 0xEA, 0x77, 0x00, 0x7C, 0x6F, 0xC1, 0xCF, 0x98, 0x85, 0x6D, 0x62, 0xD0, 0x04, 0x50, 0x03, 0x3A, 0xE1, 0xC0, 0x0A, 0x62, 0xD0, 0x00, 0x50, 0x03, 0x3A, 0x0E, 0xD4, 0xAD, 0x7C, 0x71, 0x93, 0xC2, 0xFD, 0xCF
+		}
+	},
+	{
+		342,
+		79,
+		0x39,
+		0x0181,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x81, 0x0D, 0x7C, 0x6F, 0xC9, 0x80, 0x13, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0xCC, 0x7C, 0x6F, 0x54, 0x50, 0xFF, 0x3F, 0xE8, 0x77, 0x00, 0x7C, 0x71, 0xB1, 0xCF, 0xEA, 0x56, 0x00, 0x00, 0x80, 0x51, 0x56, 0x02, 0x00, 0x80, 0x41, 0x62, 0xD0, 0x00, 0x7C, 0x6F, 0x27, 0x06, 0xE8, 0xCC, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x53, 0xE9, 0x47, 0xE9, 0x0F, 0xA0, 0x29, 0x52, 0x02, 0x08, 0x95, 0x00
+		}
+	},
+	{
+		343,
+		79,
+		0x39,
+		0x0182,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x82, 0x52, 0x00, 0x08, 0x95, 0xA1, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x54, 0x03, 0x50, 0x0D, 0x10, 0x57, 0x85, 0x28, 0x20, 0x3B, 0x03, 0xC0, 0x0F, 0x52, 0x02, 0x08, 0x52, 0x00, 0x08, 0x95, 0x0D, 0x38, 0xFE, 0x77, 0x01, 0x80, 0x0C, 0x77, 0x02, 0x62, 0xD0, 0x04, 0x52, 0x02, 0x3A, 0xE1, 0xCF, 0xB8, 0x77, 0x00, 0x7C, 0x6F, 0xC1, 0xCF, 0xAC, 0x7C, 0x71, 0xE1, 0x3D, 0x00, 0x02, 0xA0, 0x06, 0x3D, 0xB2, 0x3B
+		}
+	},
+	{
+		344,
+		79,
+		0x39,
+		0x0183,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x83, 0x00, 0x03, 0xB1, 0x09, 0x7C, 0x68, 0xBB, 0x52, 0x01, 0x08, 0x7C, 0x66, 0xDE, 0x52, 0x01, 0x08, 0x7C, 0x6A, 0x7F, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x51, 0x0E, 0x13, 0x01, 0x62, 0xD0, 0x00, 0x54, 0x00, 0x56, 0x02, 0x00, 0x80, 0xDF, 0x62, 0xD0, 0x00, 0x7C, 0x6F, 0x27, 0x55, 0xE6, 0x03, 0x55, 0xE7, 0x00, 0x55, 0xE1, 0x00, 0x55, 0xE0, 0x00, 0x3C, 0xE7, 0x00, 0xB0, 0x06, 0x3C, 0xE6, 0x00, 0x73, 0xBE
+		}
+	},
+	{
+		345,
+		79,
+		0x39,
+		0x0184,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x84, 0xA0, 0x1A, 0x70, 0xFB, 0x6E, 0xE7, 0x6E, 0xE6, 0xD0, 0x0C, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x04, 0xE1, 0x51, 0xE9, 0x0C, 0xE0, 0x65, 0xE8, 0x6B, 0xE9, 0x8F, 0xDE, 0x5F, 0xE8, 0xE1, 0x5F, 0xE9, 0xE0, 0x62, 0xD0, 0x00, 0x06, 0xE8, 0xCE, 0x0E, 0xE9, 0x02, 0x7C, 0x6D, 0x83, 0x54, 0x04, 0x7C, 0x6F, 0x27, 0x06, 0xE8, 0xFD, 0x0E, 0xE9, 0x02, 0x7C, 0x6D, 0x83, 0x54, 0x03, 0x7C, 0x71, 0x00, 0xCF, 0x77
+		}
+	},
+	{
+		346,
+		79,
+		0x39,
+		0x0185,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x85, 0x55, 0xE6, 0x03, 0x55, 0xE7, 0x00, 0x55, 0xE1, 0x00, 0x55, 0xE0, 0x00, 0x3C, 0xE7, 0x00, 0xB0, 0x06, 0x3C, 0xE6, 0x00, 0xA0, 0x1A, 0x70, 0xFB, 0x6E, 0xE7, 0x6E, 0xE6, 0xD0, 0x0C, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x04, 0xE1, 0x51, 0xE9, 0x0C, 0xE0, 0x65, 0xE8, 0x6B, 0xE9, 0x8F, 0xDE, 0x5F, 0xE8, 0xE1, 0x5F, 0xE9, 0xE0, 0x62, 0xD0, 0x00, 0x06, 0xE8, 0xC5, 0x0E, 0xE9, 0x02, 0x7C, 0x6D, 0x25, 0x24
+		}
+	},
+	{
+		347,
+		79,
+		0x39,
+		0x0186,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x86, 0x83, 0x54, 0x03, 0x52, 0x03, 0x7C, 0x6D, 0x8A, 0x06, 0xE8, 0xE0, 0x0E, 0xE9, 0x01, 0x7C, 0x6D, 0x83, 0x53, 0xE9, 0x7C, 0x72, 0x31, 0x06, 0xE6, 0xD0, 0x0E, 0xE7, 0x03, 0x7C, 0x6D, 0xEA, 0x52, 0x03, 0x7C, 0x6D, 0x8A, 0x06, 0xE8, 0xE1, 0x0E, 0xE9, 0x01, 0x7C, 0x6D, 0x83, 0x7C, 0x6E, 0xE1, 0x52, 0x04, 0x7C, 0x6E, 0xB6, 0x06, 0xE6, 0xD5, 0x0E, 0xE7, 0x02, 0x7C, 0x6D, 0xEA, 0x51, 0xE8, 0x36, 0x47
+		}
+	},
+	{
+		348,
+		79,
+		0x39,
+		0x0187,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x87, 0x3F, 0xE6, 0x77, 0x02, 0x52, 0x02, 0x3B, 0x00, 0xCF, 0x1D, 0x83, 0x3A, 0x56, 0x00, 0x00, 0x80, 0x76, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0xCC, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x53, 0xE9, 0x47, 0xE9, 0xF0, 0xA0, 0x5E, 0x56, 0x03, 0x00, 0x56, 0x04, 0xFF, 0x56, 0x02, 0x00, 0x80, 0x3B, 0x62, 0xD0, 0x00, 0x7C, 0x6F, 0x27, 0x06, 0xE8, 0xCC, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x02, 0xE0
+		}
+	},
+	{
+		349,
+		79,
+		0x39,
+		0x0188,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x88, 0x83, 0x53, 0xE9, 0x47, 0xE9, 0x0F, 0xA0, 0x23, 0x52, 0x02, 0x08, 0x52, 0x00, 0x08, 0x94, 0x16, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x54, 0x05, 0x3D, 0x03, 0x00, 0xB0, 0x06, 0x7C, 0x71, 0x4B, 0x80, 0x0A, 0x52, 0x05, 0x3B, 0x03, 0xD0, 0x04, 0x7C, 0x71, 0x4B, 0x77, 0x02, 0x62, 0xD0, 0x04, 0x52, 0x02, 0x3A, 0xE1, 0xCF, 0xBE, 0x3D, 0x04, 0xFF, 0xA0, 0x0B, 0x52, 0x04, 0x08, 0x52, 0x00, 0x08, 0x89, 0xEF
+		}
+	},
+	{
+		350,
+		79,
+		0x39,
+		0x0189,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x89, 0x93, 0x6A, 0x38, 0xFE, 0x77, 0x00, 0x7C, 0x6F, 0xC1, 0xCF, 0x87, 0x82, 0xB9, 0x7C, 0x6F, 0xC9, 0x80, 0x13, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0xCC, 0x7C, 0x6F, 0x54, 0x50, 0xFF, 0x3F, 0xE8, 0x77, 0x00, 0x7C, 0x6F, 0xC1, 0xCF, 0xEA, 0x56, 0x00, 0x00, 0x80, 0x51, 0x56, 0x02, 0x00, 0x80, 0x41, 0x62, 0xD0, 0x00, 0x7C, 0x6F, 0x27, 0x06, 0xE8, 0xCC, 0x0E, 0xE9, 0x03, 0x7C, 0xED, 0xB8
+		}
+	},
+	{
+		351,
+		79,
+		0x39,
+		0x018A,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x8A, 0x6D, 0x83, 0x53, 0xE9, 0x47, 0xE9, 0xF0, 0xA0, 0x29, 0x52, 0x00, 0x08, 0x52, 0x02, 0x08, 0x93, 0x95, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x54, 0x03, 0x50, 0x0D, 0x10, 0x57, 0x85, 0x28, 0x20, 0x3B, 0x03, 0xC0, 0x0F, 0x52, 0x00, 0x08, 0x52, 0x02, 0x08, 0x93, 0x01, 0x38, 0xFE, 0x77, 0x01, 0x80, 0x0C, 0x77, 0x02, 0x62, 0xD0, 0x00, 0x52, 0x02, 0x3A, 0x0E, 0xCF, 0xB8, 0x77, 0x00, 0x7C, 0x71, 0x06, 0xEB
+		}
+	},
+	{
+		352,
+		79,
+		0x39,
+		0x018B,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x8B, 0xB1, 0xCF, 0xAC, 0x7C, 0x71, 0xE1, 0x50, 0x00, 0x3B, 0x00, 0xC0, 0x06, 0x3D, 0x00, 0x04, 0xD1, 0x24, 0x62, 0xD0, 0x00, 0x51, 0x0E, 0x13, 0x01, 0x62, 0xD0, 0x00, 0x39, 0x04, 0xD1, 0x16, 0x7C, 0x68, 0xBB, 0x52, 0x01, 0x08, 0x93, 0xF7, 0x52, 0x01, 0x08, 0x7C, 0x6B, 0x94, 0x38, 0xFE, 0x56, 0x02, 0x00, 0x80, 0xF9, 0x62, 0xD0, 0x00, 0x7C, 0x6F, 0x27, 0x55, 0xE6, 0x03, 0x55, 0xE7, 0x00, 0x9B, 0x16
+		}
+	},
+	{
+		353,
+		79,
+		0x39,
+		0x018C,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x8C, 0x55, 0xE1, 0x00, 0x55, 0xE0, 0x00, 0x3C, 0xE7, 0x00, 0xB0, 0x06, 0x3C, 0xE6, 0x00, 0xA0, 0x1A, 0x70, 0xFB, 0x6E, 0xE7, 0x6E, 0xE6, 0xD0, 0x0C, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x04, 0xE1, 0x51, 0xE9, 0x0C, 0xE0, 0x65, 0xE8, 0x6B, 0xE9, 0x8F, 0xDE, 0x5F, 0xE8, 0xE1, 0x5F, 0xE9, 0xE0, 0x62, 0xD0, 0x00, 0x06, 0xE8, 0xC5, 0x0E, 0xE9, 0x02, 0x7C, 0x6D, 0x83, 0x54, 0x04, 0x7C, 0x6F, 0x27, 0x98, 0x11
+		}
+	},
+	{
+		354,
+		79,
+		0x39,
+		0x018D,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x8D, 0x06, 0xE8, 0xFD, 0x0E, 0xE9, 0x02, 0x7C, 0x6D, 0x83, 0x54, 0x03, 0x7C, 0x6F, 0x44, 0x06, 0xE8, 0xCC, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x7A, 0xE8, 0x53, 0xE7, 0x26, 0xE7, 0xF0, 0x7C, 0x6F, 0xD0, 0x7C, 0x71, 0x00, 0x55, 0xE6, 0x03, 0x55, 0xE7, 0x00, 0x55, 0xE1, 0x00, 0x55, 0xE0, 0x00, 0x3C, 0xE7, 0x00, 0xB0, 0x06, 0x3C, 0xE6, 0x00, 0xA0, 0x1A, 0x70, 0xFB, 0x6E, 0xE7, 0x6E, 0xE6, 0xE5, 0xAC
+		}
+	},
+	{
+		355,
+		79,
+		0x39,
+		0x018E,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x8E, 0xD0, 0x0C, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x04, 0xE1, 0x51, 0xE9, 0x0C, 0xE0, 0x65, 0xE8, 0x6B, 0xE9, 0x8F, 0xDE, 0x5F, 0xE8, 0xE1, 0x5F, 0xE9, 0xE0, 0x62, 0xD0, 0x00, 0x06, 0xE8, 0xCE, 0x0E, 0xE9, 0x02, 0x7C, 0x6D, 0x83, 0x54, 0x03, 0x52, 0x04, 0x7C, 0x6D, 0x8A, 0x06, 0xE8, 0xE0, 0x0E, 0xE9, 0x01, 0x7C, 0x6D, 0x83, 0x53, 0xE9, 0x52, 0x03, 0x53, 0xE6, 0x55, 0xE7, 0x00, 0x06, 0xE6, 0x47, 0x71
+		}
+	},
+	{
+		356,
+		79,
+		0x39,
+		0x018F,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x8F, 0xD0, 0x0E, 0xE7, 0x03, 0x7C, 0x6D, 0xEA, 0x52, 0x04, 0x7C, 0x6D, 0x8A, 0x06, 0xE8, 0xE1, 0x0E, 0xE9, 0x01, 0x7C, 0x6D, 0x83, 0x7C, 0x6E, 0xE1, 0x52, 0x03, 0x7C, 0x6E, 0xB6, 0x06, 0xE6, 0xD5, 0x0E, 0xE7, 0x02, 0x7C, 0x6D, 0xEA, 0x51, 0xE8, 0x3F, 0xE6, 0x77, 0x02, 0x52, 0x02, 0x3B, 0x00, 0xCF, 0x03, 0x80, 0x80, 0x56, 0x00, 0x00, 0x80, 0x76, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0x2C, 0x3C
+		}
+	},
+	{
+		357,
+		79,
+		0x39,
+		0x0190,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x90, 0xE8, 0xCC, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x53, 0xE9, 0x47, 0xE9, 0x0F, 0xA0, 0x5E, 0x56, 0x03, 0x00, 0x56, 0x04, 0xFF, 0x56, 0x02, 0x00, 0x80, 0x3B, 0x62, 0xD0, 0x00, 0x7C, 0x6F, 0x27, 0x06, 0xE8, 0xCC, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x53, 0xE9, 0x47, 0xE9, 0xF0, 0xA0, 0x23, 0x52, 0x00, 0x08, 0x52, 0x02, 0x08, 0x91, 0xEE, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x54, 0x05, 0x3D, 0x0E, 0x01
+		}
+	},
+	{
+		358,
+		79,
+		0x39,
+		0x0191,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x91, 0x03, 0x00, 0xB0, 0x06, 0x7C, 0x71, 0x4B, 0x80, 0x0A, 0x52, 0x05, 0x3B, 0x03, 0xD0, 0x04, 0x7C, 0x71, 0x4B, 0x77, 0x02, 0x62, 0xD0, 0x00, 0x52, 0x02, 0x3A, 0x0E, 0xCF, 0xBE, 0x3D, 0x04, 0xFF, 0xA0, 0x0B, 0x52, 0x00, 0x08, 0x52, 0x04, 0x08, 0x91, 0x42, 0x38, 0xFE, 0x77, 0x00, 0x7C, 0x71, 0xB1, 0xCF, 0x87, 0x7C, 0x71, 0x93, 0xD0, 0x8E, 0x7C, 0x72, 0xDD, 0x12, 0xE1, 0x62, 0xD0, 0x00, 0xD5, 0x90
+		}
+	},
+	{
+		359,
+		79,
+		0x39,
+		0x0192,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x92, 0x54, 0x00, 0x56, 0x02, 0x00, 0x80, 0x57, 0x62, 0xD0, 0x00, 0x7C, 0x6F, 0x27, 0x06, 0xE8, 0xCC, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x53, 0xE9, 0x26, 0xE9, 0xF0, 0x3C, 0xE9, 0xF0, 0xB0, 0x35, 0x7C, 0x6F, 0x27, 0x06, 0xE8, 0xD0, 0x7C, 0x6F, 0x54, 0x62, 0xD0, 0x04, 0x51, 0xE2, 0x62, 0xD0, 0x00, 0x3F, 0xE8, 0x52, 0x02, 0x7C, 0x6D, 0xD9, 0x06, 0xE8, 0xD5, 0x7C, 0x6F, 0xE2, 0x7C, 0x72, 0x81, 0xE9
+		}
+	},
+	{
+		360,
+		79,
+		0x39,
+		0x0193,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x93, 0x9A, 0x62, 0xD0, 0x04, 0x51, 0xE2, 0x08, 0x91, 0xCC, 0x38, 0xFF, 0x62, 0xD0, 0x04, 0x53, 0xE2, 0x7B, 0x00, 0x80, 0x08, 0x3D, 0x00, 0x00, 0xB0, 0x03, 0x80, 0x2B, 0x77, 0x02, 0x62, 0xD0, 0x00, 0x52, 0x02, 0x3A, 0x0E, 0xCF, 0xA2, 0x80, 0x1E, 0x50, 0x00, 0x08, 0x91, 0xF1, 0x38, 0xFF, 0x7C, 0x71, 0x93, 0xC0, 0x0A, 0x50, 0x00, 0x08, 0x95, 0x86, 0x38, 0xFF, 0x80, 0x09, 0x50, 0x00, 0x08, 0xE4, 0xB0
+		}
+	},
+	{
+		361,
+		79,
+		0x39,
+		0x0194,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x94, 0x7C, 0x6B, 0x94, 0x38, 0xFF, 0x56, 0x00, 0x00, 0x80, 0x88, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0xD0, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x53, 0xE9, 0x7C, 0x6E, 0xAE, 0x65, 0xE6, 0x6B, 0xE7, 0x7C, 0x70, 0x1E, 0x06, 0xE6, 0xE0, 0x0E, 0xE7, 0x01, 0x7C, 0x6D, 0xEA, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0xB8, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x53, 0xE9, 0x7C, 0x6E, 0xAE, 0x06, 0x78, 0xD9
+		}
+	},
+	{
+		362,
+		79,
+		0x39,
+		0x0195,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x95, 0xE6, 0xB0, 0x0E, 0xE7, 0x03, 0x7C, 0x6D, 0xEA, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0xB4, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x53, 0xE9, 0x7C, 0x6E, 0xAE, 0x06, 0xE6, 0xEA, 0x0E, 0xE7, 0x00, 0x7C, 0x6D, 0xEA, 0x7C, 0x6D, 0xA5, 0x65, 0xE8, 0x6B, 0xE9, 0x06, 0xE8, 0xD5, 0x0E, 0xE9, 0x02, 0x7C, 0x6D, 0x83, 0x7C, 0x6E, 0xE1, 0x7C, 0x73, 0x3C, 0x7C, 0x6E, 0xAE, 0x65, 0xE6, 0x6B, 0xE7, 0x7C, 0x67, 0xB8
+		}
+	},
+	{
+		363,
+		79,
+		0x39,
+		0x0196,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x96, 0x70, 0x1E, 0x06, 0xE6, 0xE1, 0x0E, 0xE7, 0x01, 0x7C, 0x6D, 0xEA, 0x51, 0xE8, 0x3F, 0xE6, 0x77, 0x00, 0x7C, 0x6F, 0xC1, 0xCF, 0x75, 0x3D, 0xFC, 0xFF, 0xA0, 0x08, 0x7C, 0x72, 0xDD, 0x53, 0xE1, 0x80, 0x07, 0x62, 0xD0, 0x04, 0x55, 0xE1, 0xFF, 0x38, 0xFA, 0x20, 0x7F, 0x10, 0x4F, 0x62, 0xD0, 0x00, 0x52, 0xFB, 0x97, 0xD5, 0x40, 0x06, 0xE8, 0xE0, 0x0E, 0xE9, 0x01, 0x97, 0xC5, 0x40, 0x53, 0xCA, 0x7F
+		}
+	},
+	{
+		364,
+		79,
+		0x39,
+		0x0197,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x97, 0xE9, 0x52, 0xFC, 0x53, 0xE6, 0x55, 0xE7, 0x00, 0x06, 0xE6, 0xD0, 0x0E, 0xE7, 0x03, 0x7C, 0x6D, 0xEA, 0x52, 0xFB, 0x97, 0xB5, 0x40, 0x06, 0xE8, 0xE1, 0x0E, 0xE9, 0x01, 0x97, 0xA5, 0x40, 0x7C, 0x6E, 0xE1, 0x52, 0xFC, 0x7C, 0x6E, 0xB6, 0x06, 0xE6, 0xD5, 0x0E, 0xE7, 0x02, 0x97, 0xFB, 0x40, 0x51, 0xE8, 0x3F, 0xE6, 0x7C, 0x70, 0xF0, 0x06, 0xE8, 0xCC, 0x0E, 0xE9, 0x03, 0x97, 0x84, 0x40, 0xDB, 0xA2
+		}
+	},
+	{
+		365,
+		79,
+		0x39,
+		0x0198,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x98, 0x7A, 0xE8, 0x53, 0xE7, 0x26, 0xE7, 0xF0, 0x7C, 0x6F, 0xD0, 0x52, 0xFC, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x06, 0xE8, 0xCC, 0x0E, 0xE9, 0x03, 0x97, 0x6A, 0x40, 0x7A, 0xE8, 0x53, 0xE7, 0x26, 0xE7, 0x0F, 0x7C, 0x6F, 0xD0, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x03, 0x7C, 0x6F, 0xB9, 0x55, 0xE9, 0x00, 0x06, 0xE8, 0xB8, 0x0E, 0xE9, 0x03, 0x97, 0x4B, 0x40, 0x54, 0x00, 0x7C, 0x70, 0xF0, 0x06, 0xE8, 0x7E, 0xE9
+		}
+	},
+	{
+		366,
+		79,
+		0x39,
+		0x0199,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x99, 0xB0, 0x0E, 0xE9, 0x03, 0x97, 0x3D, 0x40, 0x54, 0x01, 0x52, 0x00, 0x3B, 0x01, 0xD0, 0x08, 0x7C, 0x73, 0x06, 0x54, 0x02, 0x80, 0x06, 0x7C, 0x72, 0xFE, 0x54, 0x02, 0x7C, 0x6F, 0xB9, 0x55, 0xE9, 0x00, 0x06, 0xE8, 0xB4, 0x0E, 0xE9, 0x03, 0x97, 0x1A, 0x40, 0x54, 0x00, 0x7C, 0x70, 0xF0, 0x06, 0xE8, 0xEA, 0x0E, 0xE9, 0x00, 0x97, 0x0C, 0x40, 0x54, 0x01, 0x52, 0x00, 0x3B, 0x01, 0xD0, 0x08, 0x98, 0x1E
+		}
+	},
+	{
+		367,
+		79,
+		0x39,
+		0x019A,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x9A, 0x7C, 0x73, 0x06, 0x05, 0x02, 0x80, 0x06, 0x7C, 0x72, 0xFE, 0x05, 0x02, 0x52, 0x02, 0x62, 0xD0, 0x00, 0x38, 0xFD, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x02, 0x77, 0xFC, 0xB0, 0x03, 0x77, 0xFC, 0x50, 0x0F, 0x3B, 0xFC, 0xD0, 0x04, 0x56, 0xFC, 0x01, 0x52, 0xFC, 0x54, 0x01, 0x56, 0x00, 0x00, 0x80, 0x1A, 0x62, 0xD0, 0x00, 0x52, 0x00, 0x96, 0xD2, 0x40, 0x06, 0xE8, 0xE0, 0x0E, 0xE9, 0x01, 0x96, 0xD9, 0xA1
+		}
+	},
+	{
+		368,
+		79,
+		0x39,
+		0x019B,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x9B, 0xC2, 0x40, 0x3B, 0xFC, 0xB0, 0x03, 0x77, 0xFC, 0x77, 0x00, 0x7C, 0x6F, 0xC1, 0xCF, 0xE3, 0x52, 0xFC, 0x3B, 0x01, 0xBF, 0xD4, 0x52, 0xFC, 0x62, 0xD0, 0x00, 0x38, 0xFE, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x0B, 0x3D, 0xFC, 0x00, 0xB0, 0x11, 0x62, 0xD0, 0x04, 0x51, 0xE1, 0x54, 0x03, 0x62, 0xD0, 0x00, 0x51, 0x0E, 0x54, 0x02, 0x80, 0x0D, 0x62, 0xD0, 0x04, 0x51, 0xE1, 0x13, 0xFC, 0x54, 0x03, 0x43, 0x76
+		}
+	},
+	{
+		369,
+		79,
+		0x39,
+		0x019C,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x9C, 0x7C, 0x70, 0xDB, 0x56, 0x00, 0x00, 0x56, 0x01, 0x00, 0x81, 0xA7, 0x56, 0x04, 0x00, 0x81, 0x97, 0x3D, 0xFC, 0x00, 0xB0, 0x22, 0x62, 0xD0, 0x00, 0x7C, 0x6F, 0x44, 0x06, 0xE8, 0xB8, 0x0E, 0xE9, 0x03, 0x96, 0x60, 0x40, 0x54, 0x05, 0x96, 0x7D, 0x40, 0x06, 0xE8, 0xB0, 0x0E, 0xE9, 0x03, 0x96, 0x52, 0x40, 0x54, 0x06, 0x80, 0x54, 0x62, 0xD0, 0x00, 0x52, 0x04, 0x96, 0xBD, 0x40, 0x96, 0x43, 0xDE, 0xAD
+		}
+	},
+	{
+		370,
+		79,
+		0x39,
+		0x019D,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x9D, 0x40, 0x54, 0x05, 0x96, 0x60, 0x40, 0x55, 0xE6, 0x03, 0x55, 0xE7, 0x00, 0x55, 0xE1, 0x00, 0x55, 0xE0, 0x00, 0x3C, 0xE7, 0x00, 0xB0, 0x06, 0x3C, 0xE6, 0x00, 0xA0, 0x1A, 0x70, 0xFB, 0x6E, 0xE7, 0x6E, 0xE6, 0xD0, 0x0C, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x04, 0xE1, 0x51, 0xE9, 0x0C, 0xE0, 0x65, 0xE8, 0x6B, 0xE9, 0x8F, 0xDE, 0x5F, 0xE8, 0xE1, 0x5F, 0xE9, 0xE0, 0x62, 0xD0, 0x00, 0x06, 0xE8, 0x4D, 0x8C
+		}
+	},
+	{
+		371,
+		79,
+		0x39,
+		0x019E,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x9E, 0xC3, 0x0E, 0xE9, 0x02, 0x95, 0xFD, 0x40, 0x54, 0x06, 0x52, 0x05, 0x3B, 0x06, 0xD0, 0x0C, 0x62, 0xD0, 0x00, 0x52, 0x06, 0x13, 0x05, 0x54, 0x07, 0x80, 0x0A, 0x62, 0xD0, 0x00, 0x52, 0x05, 0x13, 0x06, 0x54, 0x07, 0x3D, 0xFC, 0x00, 0xB0, 0x22, 0x62, 0xD0, 0x00, 0x97, 0x97, 0x40, 0x06, 0xE8, 0xB4, 0x0E, 0xE9, 0x03, 0x95, 0xCD, 0x40, 0x54, 0x05, 0x95, 0xEA, 0x40, 0x06, 0xE8, 0xEA, 0x0E, 0xC7, 0x81
+		}
+	},
+	{
+		372,
+		79,
+		0x39,
+		0x019F,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x9F, 0xE9, 0x00, 0x95, 0xBF, 0x40, 0x54, 0x06, 0x80, 0x54, 0x62, 0xD0, 0x00, 0x52, 0x04, 0x96, 0x6E, 0x40, 0x95, 0xB0, 0x40, 0x54, 0x05, 0x95, 0xCD, 0x40, 0x55, 0xE6, 0x03, 0x55, 0xE7, 0x00, 0x55, 0xE1, 0x00, 0x55, 0xE0, 0x00, 0x3C, 0xE7, 0x00, 0xB0, 0x06, 0x3C, 0xE6, 0x00, 0xA0, 0x1A, 0x70, 0xFB, 0x6E, 0xE7, 0x6E, 0xE6, 0xD0, 0x0C, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x04, 0xE1, 0x51, 0xE9, 0x0B, 0x0A
+		}
+	},
+	{
+		373,
+		79,
+		0x39,
+		0x01A0,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xA0, 0x0C, 0xE0, 0x65, 0xE8, 0x6B, 0xE9, 0x8F, 0xDE, 0x5F, 0xE8, 0xE1, 0x5F, 0xE9, 0xE0, 0x62, 0xD0, 0x00, 0x06, 0xE8, 0xC4, 0x0E, 0xE9, 0x02, 0x95, 0x6A, 0x40, 0x54, 0x06, 0x52, 0x05, 0x3B, 0x06, 0xD0, 0x0C, 0x62, 0xD0, 0x00, 0x52, 0x06, 0x13, 0x05, 0x54, 0x08, 0x80, 0x0A, 0x62, 0xD0, 0x00, 0x52, 0x05, 0x13, 0x06, 0x54, 0x08, 0x62, 0xD0, 0x00, 0x52, 0x07, 0x53, 0xE8, 0x50, 0x00, 0x08, 0xD8, 0xA5
+		}
+	},
+	{
+		374,
+		79,
+		0x39,
+		0x01A1,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xA1, 0x51, 0xE8, 0x08, 0x52, 0x07, 0x08, 0x95, 0x13, 0x7C, 0x71, 0xF5, 0x52, 0x08, 0x53, 0xE6, 0x50, 0x00, 0x08, 0x51, 0xE6, 0x08, 0x52, 0x08, 0x08, 0x95, 0x01, 0x38, 0xFA, 0x62, 0xD0, 0x00, 0x52, 0x0A, 0x02, 0xE8, 0x62, 0xD0, 0x03, 0x53, 0xDF, 0x52, 0x09, 0x62, 0xD0, 0x00, 0x0A, 0xE9, 0x62, 0xD0, 0x03, 0x53, 0xDE, 0x62, 0xD0, 0x00, 0x96, 0xCB, 0x40, 0x52, 0x01, 0x02, 0xE8, 0x53, 0xE8, 0x90, 0x16
+		}
+	},
+	{
+		375,
+		79,
+		0x39,
+		0x01A2,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xA2, 0x50, 0x00, 0x0A, 0xE9, 0x53, 0xE9, 0x65, 0xE8, 0x6B, 0xE9, 0x06, 0xE8, 0xB1, 0x97, 0x53, 0x40, 0x62, 0xD0, 0x03, 0x51, 0xDE, 0x62, 0xD0, 0x00, 0x3F, 0xE8, 0x62, 0xD0, 0x03, 0x51, 0xDF, 0x62, 0xD0, 0x00, 0x3F, 0xE8, 0x77, 0x04, 0x52, 0x04, 0x3B, 0x02, 0xCE, 0x65, 0x77, 0x00, 0x07, 0x01, 0x03, 0x52, 0x00, 0x3B, 0x03, 0xCE, 0x55, 0x38, 0xF5, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x02, 0x97, 0xD6, 0xA3
+		}
+	},
+	{
+		376,
+		79,
+		0x39,
+		0x01A3,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xA3, 0x08, 0x40, 0x80, 0x95, 0x62, 0xD0, 0x00, 0x94, 0xDC, 0x40, 0x06, 0xE8, 0xCC, 0x0E, 0xE9, 0x03, 0x94, 0xB1, 0x40, 0x53, 0xE9, 0x47, 0xE9, 0xF0, 0xA0, 0x7D, 0x52, 0x01, 0x95, 0x1C, 0x40, 0x95, 0xCD, 0x40, 0x06, 0xE6, 0xB8, 0x0E, 0xE7, 0x03, 0x51, 0xE7, 0x60, 0xD4, 0x3E, 0xE6, 0x53, 0xE7, 0x96, 0xDE, 0x40, 0x52, 0x01, 0x95, 0x47, 0x40, 0x95, 0xB4, 0x40, 0x06, 0xE6, 0xB4, 0x0E, 0xE7, 0x7E, 0xF4
+		}
+	},
+	{
+		377,
+		79,
+		0x39,
+		0x01A4,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xA4, 0x03, 0x51, 0xE7, 0x60, 0xD4, 0x3E, 0xE6, 0x53, 0xE7, 0x96, 0xC5, 0x40, 0x52, 0x01, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x55, 0xE6, 0x03, 0x55, 0xE7, 0x00, 0x55, 0xE1, 0x00, 0x55, 0xE0, 0x00, 0x3C, 0xE7, 0x00, 0xB0, 0x06, 0x3C, 0xE6, 0x00, 0xA0, 0x1A, 0x70, 0xFB, 0x6E, 0xE7, 0x6E, 0xE6, 0xD0, 0x0C, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x04, 0xE1, 0x51, 0xE9, 0x0C, 0xE0, 0x65, 0xE8, 0x6B, 0xE9, 0xD5, 0xA3
+		}
+	},
+	{
+		378,
+		79,
+		0x39,
+		0x01A5,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xA5, 0x8F, 0xDE, 0x5F, 0xE8, 0xE1, 0x5F, 0xE9, 0xE0, 0x62, 0xD0, 0x00, 0x06, 0xE8, 0xCE, 0x0E, 0xE9, 0x02, 0x96, 0x86, 0x40, 0x77, 0x01, 0x77, 0x00, 0x96, 0x67, 0x40, 0xCF, 0x68, 0x96, 0x6A, 0x40, 0x81, 0x15, 0x62, 0xD0, 0x00, 0x94, 0x3E, 0x40, 0x06, 0xE8, 0xCC, 0x0E, 0xE9, 0x03, 0x94, 0x13, 0x40, 0x53, 0xE9, 0x47, 0xE9, 0x0F, 0xA0, 0xFD, 0x52, 0x01, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x55, 0x51, 0x9C
+		}
+	},
+	{
+		379,
+		79,
+		0x39,
+		0x01A6,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xA6, 0xE6, 0x03, 0x55, 0xE7, 0x00, 0x55, 0xE1, 0x00, 0x55, 0xE0, 0x00, 0x3C, 0xE7, 0x00, 0xB0, 0x06, 0x3C, 0xE6, 0x00, 0xA0, 0x1A, 0x70, 0xFB, 0x6E, 0xE7, 0x6E, 0xE6, 0xD0, 0x0C, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x04, 0xE1, 0x51, 0xE9, 0x0C, 0xE0, 0x65, 0xE8, 0x6B, 0xE9, 0x8F, 0xDE, 0x5F, 0xE8, 0xE1, 0x5F, 0xE9, 0xE0, 0x62, 0xD0, 0x00, 0x06, 0xE8, 0xC3, 0x0E, 0xE9, 0x02, 0x94, 0xEF, 0x40, 0xA8, 0x4B
+		}
+	},
+	{
+		380,
+		79,
+		0x39,
+		0x01A7,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xA7, 0x06, 0xE6, 0xB0, 0x0E, 0xE7, 0x03, 0x51, 0xE7, 0x60, 0xD4, 0x3E, 0xE6, 0x53, 0xE7, 0x96, 0x00, 0x40, 0x52, 0x01, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x55, 0xE6, 0x03, 0x55, 0xE7, 0x00, 0x55, 0xE1, 0x00, 0x55, 0xE0, 0x00, 0x3C, 0xE7, 0x00, 0xB0, 0x06, 0x3C, 0xE6, 0x00, 0xA0, 0x1A, 0x70, 0xFB, 0x6E, 0xE7, 0x6E, 0xE6, 0xD0, 0x0C, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x04, 0xE1, 0x51, 0xE9, 0x0C, 0x20, 0x3C
+		}
+	},
+	{
+		381,
+		79,
+		0x39,
+		0x01A8,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xA8, 0xE0, 0x65, 0xE8, 0x6B, 0xE9, 0x8F, 0xDE, 0x5F, 0xE8, 0xE1, 0x5F, 0xE9, 0xE0, 0x62, 0xD0, 0x00, 0x06, 0xE8, 0xC4, 0x0E, 0xE9, 0x02, 0x94, 0x96, 0x40, 0x06, 0xE6, 0xEA, 0x0E, 0xE7, 0x00, 0x51, 0xE7, 0x60, 0xD4, 0x3E, 0xE6, 0x53, 0xE7, 0x95, 0xA7, 0x40, 0x52, 0x01, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x55, 0xE6, 0x03, 0x55, 0xE7, 0x00, 0x55, 0xE1, 0x00, 0x55, 0xE0, 0x00, 0x3C, 0xE7, 0x00, 0x6A, 0xD1
+		}
+	},
+	{
+		382,
+		79,
+		0x39,
+		0x01A9,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xA9, 0xB0, 0x06, 0x3C, 0xE6, 0x00, 0xA0, 0x1A, 0x70, 0xFB, 0x6E, 0xE7, 0x6E, 0xE6, 0xD0, 0x0C, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x04, 0xE1, 0x51, 0xE9, 0x0C, 0xE0, 0x65, 0xE8, 0x6B, 0xE9, 0x8F, 0xDE, 0x5F, 0xE8, 0xE1, 0x5F, 0xE9, 0xE0, 0x62, 0xD0, 0x00, 0x06, 0xE8, 0xC5, 0x0E, 0xE9, 0x02, 0x95, 0x68, 0x40, 0x77, 0x01, 0x77, 0x00, 0x97, 0x39, 0x40, 0xCE, 0xE8, 0x38, 0xFE, 0x20, 0x7F, 0x10, 0x0B, 0x14
+		}
+	},
+	{
+		383,
+		79,
+		0x39,
+		0x01AA,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xAA, 0x4F, 0x38, 0x07, 0x3D, 0xFC, 0x00, 0xB0, 0x11, 0x62, 0xD0, 0x04, 0x51, 0xE1, 0x54, 0x05, 0x62, 0xD0, 0x00, 0x51, 0x0E, 0x54, 0x02, 0x80, 0x0D, 0x62, 0xD0, 0x04, 0x51, 0xE1, 0x13, 0xFC, 0x54, 0x05, 0x96, 0x38, 0x40, 0x62, 0xD0, 0x00, 0x94, 0x6F, 0x40, 0x06, 0xE8, 0x5E, 0x0E, 0xE9, 0x0F, 0x94, 0xAA, 0x40, 0x54, 0x04, 0x56, 0x03, 0x00, 0x56, 0x01, 0x00, 0x97, 0xE4, 0x40, 0x80, 0xCB, 0xEE, 0xDB
+		}
+	},
+	{
+		384,
+		79,
+		0x39,
+		0x01AB,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xAB, 0x62, 0xD0, 0x03, 0x55, 0xDF, 0x00, 0x55, 0xDE, 0x00, 0x56, 0x06, 0x00, 0x80, 0x34, 0x96, 0xCE, 0x40, 0x52, 0x01, 0x94, 0x2B, 0x40, 0x10, 0x57, 0x03, 0x7C, 0x4A, 0xBC, 0x20, 0x03, 0x06, 0x54, 0x00, 0x92, 0xC2, 0x40, 0x65, 0xE8, 0x6B, 0xE9, 0x06, 0xE8, 0xB1, 0x0E, 0xE9, 0x02, 0x92, 0x93, 0x40, 0x53, 0xE9, 0x3E, 0xE8, 0x62, 0xD0, 0x03, 0x04, 0xDF, 0x95, 0xA5, 0x40, 0x0C, 0xDE, 0x77, 0x92, 0x24
+		}
+	},
+	{
+		385,
+		79,
+		0x39,
+		0x01AC,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xAC, 0x06, 0x52, 0x06, 0x3B, 0x02, 0xCF, 0xC8, 0x95, 0x83, 0x40, 0xD0, 0x7A, 0x62, 0xD0, 0x03, 0x51, 0xDF, 0x08, 0x51, 0xDE, 0x62, 0xD0, 0x04, 0x53, 0xB1, 0x18, 0x53, 0xB2, 0x56, 0x06, 0x00, 0x80, 0x5F, 0x96, 0x7B, 0x40, 0x52, 0x01, 0x93, 0xD8, 0x40, 0x54, 0x00, 0x3D, 0xFC, 0x00, 0xB0, 0x42, 0x52, 0x00, 0x92, 0x56, 0x40, 0x06, 0xE8, 0xE0, 0x0E, 0xE9, 0x01, 0x92, 0x46, 0x40, 0x53, 0xE9, 0x64, 0xC9
+		}
+	},
+	{
+		386,
+		79,
+		0x39,
+		0x01AD,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xAD, 0x96, 0x67, 0x40, 0x06, 0xE6, 0xD0, 0x0E, 0xE7, 0x03, 0x92, 0x9F, 0x40, 0x52, 0x00, 0x92, 0x3A, 0x40, 0x06, 0xE8, 0xE1, 0x0E, 0xE9, 0x01, 0x92, 0x2A, 0x40, 0x93, 0x85, 0x40, 0x52, 0x06, 0x93, 0x55, 0x40, 0x06, 0xE6, 0xD5, 0x0E, 0xE7, 0x02, 0x92, 0x80, 0x40, 0x51, 0xE8, 0x3F, 0xE6, 0x80, 0x0D, 0x96, 0x2B, 0x40, 0x06, 0xE8, 0xFD, 0x0E, 0xE9, 0x02, 0x94, 0x5D, 0x40, 0x77, 0x06, 0x52, 0x35, 0x6C
+		}
+	},
+	{
+		387,
+		79,
+		0x39,
+		0x01AE,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xAE, 0x06, 0x3B, 0x02, 0xCF, 0x9D, 0x77, 0x03, 0x07, 0x01, 0x03, 0x52, 0x03, 0x3B, 0x04, 0xCF, 0x31, 0x38, 0xF9, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x07, 0x3D, 0xFC, 0x00, 0xB0, 0x11, 0x62, 0xD0, 0x04, 0x51, 0xE1, 0x54, 0x01, 0x62, 0xD0, 0x00, 0x51, 0x0E, 0x54, 0x02, 0x80, 0x0D, 0x62, 0xD0, 0x04, 0x51, 0xE1, 0x13, 0xFC, 0x54, 0x01, 0x95, 0x23, 0x40, 0x62, 0xD0, 0x00, 0x93, 0x69, 0x40, 0x06, 0x99, 0x35
+		}
+	},
+	{
+		388,
+		79,
+		0x39,
+		0x01AF,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xAF, 0xE8, 0x5E, 0x0E, 0xE9, 0x0F, 0x93, 0x95, 0x40, 0x54, 0x04, 0x56, 0x03, 0x00, 0x56, 0x00, 0x00, 0x96, 0xCF, 0x40, 0x81, 0x6E, 0x62, 0xD0, 0x03, 0x55, 0xDF, 0x00, 0x55, 0xDE, 0x00, 0x56, 0x05, 0x00, 0x80, 0x39, 0x62, 0xD0, 0x00, 0x93, 0x30, 0x40, 0x52, 0x00, 0x93, 0x13, 0x40, 0x53, 0xE9, 0x10, 0x52, 0x05, 0x57, 0x03, 0x7C, 0x4A, 0xBC, 0x20, 0x02, 0xE9, 0x54, 0x06, 0x52, 0x06, 0x91, 0x39, 0x76
+		}
+	},
+	{
+		389,
+		79,
+		0x39,
+		0x01B0,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xB0, 0xD8, 0x40, 0x06, 0xE8, 0xB1, 0x0E, 0xE9, 0x02, 0x91, 0x79, 0x40, 0x53, 0xE9, 0x3E, 0xE8, 0x62, 0xD0, 0x03, 0x04, 0xDF, 0x94, 0x8B, 0x40, 0x0C, 0xDE, 0x77, 0x05, 0x52, 0x05, 0x3B, 0x01, 0xCF, 0xC3, 0x94, 0x69, 0x40, 0xD1, 0x18, 0x62, 0xD0, 0x03, 0x51, 0xDF, 0x08, 0x51, 0xDE, 0x62, 0xD0, 0x04, 0x53, 0xB1, 0x18, 0x53, 0xB2, 0x56, 0x05, 0x00, 0x80, 0x2A, 0x3D, 0xFC, 0x00, 0xB0, 0x13, 0x78, 0xF5
+		}
+	},
+	{
+		390,
+		79,
+		0x39,
+		0x01B1,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xB1, 0x62, 0xD0, 0x00, 0x92, 0xD3, 0x40, 0x06, 0xE8, 0xD0, 0x93, 0x09, 0x40, 0x50, 0x00, 0x3F, 0xE8, 0x80, 0x11, 0x62, 0xD0, 0x00, 0x92, 0xC1, 0x40, 0x06, 0xE8, 0xFD, 0x93, 0x85, 0x40, 0x50, 0xFF, 0x3F, 0xE8, 0x77, 0x05, 0x52, 0x05, 0x3B, 0x02, 0xCF, 0xD2, 0x56, 0x05, 0x00, 0x80, 0x66, 0x62, 0xD0, 0x00, 0x92, 0xA4, 0x40, 0x52, 0x00, 0x92, 0x87, 0x40, 0x54, 0x06, 0x3D, 0xFC, 0x00, 0xB0, 0x7F, 0x04
+		}
+	},
+	{
+		391,
+		79,
+		0x39,
+		0x01B2,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xB2, 0x42, 0x52, 0x05, 0x91, 0x05, 0x40, 0x06, 0xE8, 0xE0, 0x0E, 0xE9, 0x01, 0x90, 0xF5, 0x40, 0x53, 0xE9, 0x95, 0x16, 0x40, 0x06, 0xE6, 0xD0, 0x0E, 0xE7, 0x03, 0x91, 0x4E, 0x40, 0x52, 0x05, 0x90, 0xE9, 0x40, 0x06, 0xE8, 0xE1, 0x0E, 0xE9, 0x01, 0x90, 0xD9, 0x40, 0x92, 0x34, 0x40, 0x52, 0x06, 0x92, 0x04, 0x40, 0x06, 0xE6, 0xD5, 0x0E, 0xE7, 0x02, 0x91, 0x2F, 0x40, 0x51, 0xE8, 0x3F, 0xE6, 0xBE, 0x83
+		}
+	},
+	{
+		392,
+		79,
+		0x39,
+		0x01B3,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xB3, 0x80, 0x11, 0x62, 0xD0, 0x00, 0x92, 0x51, 0x40, 0x06, 0xE8, 0xFD, 0x93, 0x15, 0x40, 0x52, 0x06, 0x3F, 0xE8, 0x77, 0x05, 0x52, 0x05, 0x3B, 0x01, 0xCF, 0x96, 0x3D, 0xFC, 0x00, 0xB0, 0x5F, 0x62, 0xD0, 0x04, 0x51, 0xE2, 0x62, 0xD0, 0x04, 0x53, 0xE3, 0x56, 0x05, 0x00, 0x80, 0x4A, 0x62, 0xD0, 0x00, 0x92, 0x25, 0x40, 0x06, 0xE8, 0xD0, 0x0E, 0xE9, 0x03, 0x90, 0x87, 0x40, 0x39, 0x00, 0xB0, 0x0F, 0x26
+		}
+	},
+	{
+		393,
+		79,
+		0x39,
+		0x01B4,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xB4, 0x35, 0x92, 0x15, 0x40, 0x06, 0xE8, 0xD0, 0x92, 0x4B, 0x40, 0x62, 0xD0, 0x04, 0x51, 0xE3, 0x62, 0xD0, 0x00, 0x3F, 0xE8, 0x62, 0xD0, 0x04, 0x51, 0xE3, 0x08, 0x7C, 0x66, 0x95, 0x38, 0xFF, 0x62, 0xD0, 0x04, 0x53, 0xE3, 0x62, 0xD0, 0x00, 0x52, 0x05, 0x90, 0xAE, 0x40, 0x06, 0xE8, 0xD5, 0x92, 0xB1, 0x40, 0x95, 0x66, 0x40, 0x77, 0x05, 0x52, 0x05, 0x3B, 0x02, 0xCF, 0xB2, 0x77, 0x03, 0x07, 0xE0, 0xC9
+		}
+	},
+	{
+		394,
+		79,
+		0x39,
+		0x01B5,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xB5, 0x00, 0x03, 0x52, 0x03, 0x3B, 0x04, 0xCE, 0x8E, 0x3D, 0xFC, 0x00, 0xB0, 0x0B, 0x62, 0xD0, 0x04, 0x51, 0xE3, 0x62, 0xD0, 0x04, 0x53, 0xE2, 0x38, 0xF9, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x02, 0x92, 0x68, 0x40, 0x48, 0xFC, 0x01, 0xA0, 0x09, 0x52, 0xFB, 0x05, 0x01, 0x52, 0xFA, 0x0D, 0x00, 0x66, 0xFB, 0x6C, 0xFA, 0x70, 0xFB, 0x6F, 0xFC, 0x3D, 0xFC, 0x00, 0xBF, 0xE7, 0x93, 0xB0, 0x40, 0x38, 0x30, 0x6A
+		}
+	},
+	{
+		395,
+		79,
+		0x39,
+		0x01B6,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xB6, 0xFE, 0x20, 0x7F, 0x51, 0xE9, 0x60, 0xD4, 0x3E, 0xE8, 0x7F, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x65, 0xE8, 0x6B, 0xE9, 0x65, 0xE8, 0x6B, 0xE9, 0x65, 0xE8, 0x6B, 0xE9, 0x7F, 0x65, 0xE8, 0x6B, 0xE9, 0x65, 0xE8, 0x6B, 0xE9, 0x7F, 0x52, 0x00, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x7F, 0x70, 0xFB, 0x6E, 0xE9, 0x6E, 0xE8, 0x70, 0xFB, 0x6E, 0xE9, 0x6E, 0xE8, 0x70, 0xFB, 0x6E, 0xE9, 0x6E, 0xE8, 0x70, 0x4E, 0xA7
+		}
+	},
+	{
+		396,
+		79,
+		0x39,
+		0x01B7,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xB7, 0xFB, 0x6E, 0xE9, 0x6E, 0xE8, 0x7F, 0x70, 0xFB, 0x6E, 0xE9, 0x6E, 0xE8, 0x70, 0xFB, 0x6E, 0xE9, 0x6E, 0xE8, 0x70, 0xFB, 0x6E, 0xE9, 0x6E, 0xE8, 0x7F, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x65, 0xE8, 0x6B, 0xE9, 0x7F, 0x60, 0xD4, 0x3E, 0xE8, 0x53, 0xE9, 0x7F, 0x51, 0xE7, 0x60, 0xD5, 0x51, 0xE9, 0x3F, 0xE6, 0x7F, 0x70, 0xFB, 0x6E, 0xE9, 0x6E, 0xE8, 0x7F, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x55, 0x4C, 0xA4
+		}
+	},
+	{
+		397,
+		79,
+		0x39,
+		0x01B8,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xB8, 0xE6, 0x03, 0x55, 0xE7, 0x00, 0x55, 0xE1, 0x00, 0x55, 0xE0, 0x00, 0x3C, 0xE7, 0x00, 0xB0, 0x06, 0x3C, 0xE6, 0x00, 0xA0, 0x1A, 0x70, 0xFB, 0x6E, 0xE7, 0x6E, 0xE6, 0xD0, 0x0C, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x04, 0xE1, 0x51, 0xE9, 0x0C, 0xE0, 0x65, 0xE8, 0x6B, 0xE9, 0x8F, 0xDE, 0x5F, 0xE8, 0xE1, 0x5F, 0xE9, 0xE0, 0x62, 0xD0, 0x00, 0x06, 0xE8, 0xCC, 0x0E, 0xE9, 0x02, 0x7F, 0x53, 0xE8, 0xA8, 0x5D
+		}
+	},
+	{
+		398,
+		79,
+		0x39,
+		0x01B9,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xB9, 0x55, 0xE9, 0x00, 0x55, 0xE6, 0x03, 0x55, 0xE7, 0x00, 0x55, 0xE1, 0x00, 0x55, 0xE0, 0x00, 0x3C, 0xE7, 0x00, 0xB0, 0x06, 0x3C, 0xE6, 0x00, 0xA0, 0x1A, 0x70, 0xFB, 0x6E, 0xE7, 0x6E, 0xE6, 0xD0, 0x0C, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x04, 0xE1, 0x51, 0xE9, 0x0C, 0xE0, 0x65, 0xE8, 0x6B, 0xE9, 0x8F, 0xDE, 0x5F, 0xE8, 0xE1, 0x5F, 0xE9, 0xE0, 0x62, 0xD0, 0x00, 0x06, 0xE8, 0xCD, 0x0E, 0xE9, 0x80, 0x0E
+		}
+	},
+	{
+		399,
+		79,
+		0x39,
+		0x01BA,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xBA, 0x02, 0x7F, 0x62, 0xD0, 0x01, 0x51, 0xE7, 0x08, 0x51, 0xE6, 0x62, 0xD0, 0x03, 0x53, 0xEC, 0x18, 0x53, 0xED, 0x62, 0xD0, 0x01, 0x51, 0xE5, 0x08, 0x51, 0xE4, 0x62, 0xD0, 0x03, 0x53, 0xEA, 0x18, 0x53, 0xEB, 0x7F, 0x53, 0xE8, 0x52, 0x01, 0x09, 0x00, 0x60, 0xD4, 0x3E, 0xE8, 0x7F, 0x52, 0x00, 0x53, 0xE6, 0x55, 0xE7, 0x00, 0x7F, 0x53, 0xE6, 0x55, 0xE7, 0x00, 0x65, 0xE6, 0x6B, 0xE7, 0x7F, 0x2B, 0x65
+		}
+	},
+	{
+		400,
+		79,
+		0x39,
+		0x01BB,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xBB, 0x62, 0xD0, 0x01, 0x51, 0xEF, 0x08, 0x51, 0xEE, 0x62, 0xD0, 0x03, 0x53, 0xE8, 0x18, 0x53, 0xE9, 0x62, 0xD0, 0x01, 0x51, 0xED, 0x08, 0x51, 0xEC, 0x62, 0xD0, 0x03, 0x53, 0xE6, 0x18, 0x53, 0xE7, 0x7F, 0x53, 0xE9, 0x3E, 0xE8, 0x53, 0xE8, 0x7F, 0x62, 0xD0, 0x00, 0x51, 0xAF, 0x29, 0x01, 0x53, 0xAF, 0x51, 0xAF, 0x60, 0x04, 0x51, 0xAF, 0x29, 0x08, 0x53, 0xAF, 0x51, 0xAF, 0x60, 0x04, 0x7F, 0x67, 0xDE
+		}
+	},
+	{
+		401,
+		79,
+		0x39,
+		0x01BC,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xBC, 0x02, 0xE8, 0x53, 0xE8, 0x50, 0x00, 0x0A, 0xE9, 0x53, 0xE9, 0x06, 0xE8, 0x62, 0x0E, 0xE9, 0x0F, 0x51, 0xE9, 0x10, 0x58, 0xE8, 0x28, 0x20, 0x7F, 0x52, 0x05, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x7F, 0x53, 0xAF, 0x51, 0xAF, 0x60, 0x04, 0x7F, 0x52, 0x02, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x7F, 0x62, 0xD0, 0x03, 0x51, 0x99, 0x21, 0x70, 0x54, 0x00, 0x3D, 0x00, 0x00, 0x7F, 0x62, 0xD0, 0x03, 0x51, 0x11, 0x33
+		}
+	},
+	{
+		402,
+		79,
+		0x39,
+		0x01BD,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xBD, 0x99, 0x21, 0x70, 0x7F, 0x52, 0x04, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x7F, 0x62, 0xD0, 0x00, 0x52, 0x02, 0x53, 0xE8, 0x7F, 0x0E, 0xE9, 0x03, 0x51, 0xE9, 0x60, 0xD5, 0x7F, 0x51, 0xE9, 0x10, 0x58, 0xE8, 0x28, 0x20, 0x7F, 0x62, 0xD0, 0x00, 0x26, 0xAF, 0xF7, 0x51, 0xAF, 0x60, 0x04, 0x26, 0xAF, 0xFE, 0x51, 0xAF, 0x60, 0x04, 0x7F, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x08, 0x51, 0xE9, 0x62, 0xD0, 0x67, 0xE0
+		}
+	},
+	{
+		403,
+		79,
+		0x39,
+		0x01BE,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xBE, 0x04, 0x53, 0x9D, 0x18, 0x53, 0x9E, 0x7F, 0x62, 0xD0, 0x03, 0x51, 0xF7, 0x08, 0x51, 0xF6, 0x62, 0xD0, 0x03, 0x53, 0xBC, 0x18, 0x53, 0xBD, 0x62, 0xD0, 0x03, 0x51, 0xF5, 0x08, 0x51, 0xF4, 0x62, 0xD0, 0x03, 0x53, 0xBE, 0x18, 0x53, 0xBF, 0x7F, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x08, 0x51, 0xE9, 0x62, 0xD0, 0x04, 0x53, 0x9F, 0x18, 0x53, 0xA0, 0x7F, 0x62, 0xD0, 0x00, 0x52, 0xFC, 0x53, 0xE8, 0x85, 0x1D
+		}
+	},
+	{
+		404,
+		79,
+		0x39,
+		0x01BF,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xBF, 0x7F, 0x62, 0xD0, 0x00, 0x52, 0x00, 0x3A, 0x0E, 0x7F, 0x56, 0x01, 0x00, 0x56, 0x00, 0x00, 0x7F, 0x51, 0xE9, 0x60, 0xD5, 0x51, 0xE7, 0x3F, 0xE8, 0x7F, 0x51, 0xE9, 0x60, 0xD5, 0x52, 0x00, 0x3F, 0xE8, 0x7F, 0x0E, 0xE9, 0x02, 0x51, 0xE9, 0x60, 0xD5, 0x7F, 0x62, 0xD0, 0x03, 0x52, 0x01, 0x53, 0xE5, 0x52, 0x00, 0x53, 0xE4, 0x7F, 0x52, 0x02, 0x53, 0xE8, 0x52, 0x01, 0x60, 0xD4, 0x3E, 0xE8, 0x95, 0x3E
+		}
+	},
+	{
+		405,
+		79,
+		0x39,
+		0x01C0,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xC0, 0x7F, 0x70, 0xFB, 0x6F, 0x01, 0x6F, 0x02, 0x70, 0xFB, 0x6F, 0x01, 0x6F, 0x02, 0x7F, 0x62, 0xD0, 0x00, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x7F, 0x65, 0xE8, 0x6B, 0xE9, 0x51, 0xE8, 0x7F, 0x65, 0xE6, 0x6B, 0xE7, 0x65, 0xE6, 0x6B, 0xE7, 0x7F, 0x53, 0xE9, 0x62, 0xD0, 0x04, 0x51, 0xD4, 0x62, 0xD0, 0x00, 0x3A, 0xE9, 0x7F, 0x53, 0xE9, 0x62, 0xD0, 0x04, 0x51, 0xD9, 0x62, 0xD0, 0x00, 0x3A, 0xE9, 0x2E, 0x71
+		}
+	},
+	{
+		406,
+		79,
+		0x39,
+		0x01C1,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xC1, 0x7F, 0x62, 0xD0, 0x03, 0x55, 0xEF, 0x00, 0x55, 0xEE, 0x00, 0x7F, 0x56, 0x01, 0x00, 0x80, 0x03, 0x77, 0x01, 0x3D, 0x01, 0x0A, 0xCF, 0xFA, 0x62, 0xD0, 0x00, 0x7F, 0x62, 0xD0, 0x04, 0x55, 0xCF, 0x01, 0x7F, 0x62, 0xD0, 0x00, 0x50, 0x0D, 0x10, 0x08, 0x57, 0x89, 0x28, 0x53, 0xE9, 0x18, 0x75, 0x09, 0x00, 0x28, 0x53, 0xE8, 0x20, 0x7F, 0x62, 0xD0, 0x04, 0x51, 0xAA, 0x62, 0xD0, 0x00, 0x12, 0xA0, 0x56
+		}
+	},
+	{
+		407,
+		79,
+		0x39,
+		0x01C2,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xC2, 0xE8, 0x62, 0xD0, 0x04, 0x51, 0xA9, 0x62, 0xD0, 0x00, 0x1A, 0xE9, 0x7F, 0x62, 0xD0, 0x03, 0x51, 0xDF, 0x62, 0xD0, 0x04, 0x12, 0xB2, 0x62, 0xD0, 0x03, 0x51, 0xDE, 0x62, 0xD0, 0x04, 0x1A, 0xB1, 0x7F, 0x62, 0xD0, 0x00, 0x51, 0xE9, 0x62, 0xD0, 0x03, 0x7F, 0x70, 0xFE, 0x62, 0xD0, 0x03, 0x51, 0xD9, 0x08, 0x51, 0xD8, 0x62, 0xD0, 0x00, 0x53, 0x0A, 0x18, 0x53, 0x0B, 0x71, 0x01, 0x7F, 0x53, 0x76, 0x03
+		}
+	},
+	{
+		408,
+		79,
+		0x39,
+		0x01C3,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xC3, 0xE6, 0x55, 0xE7, 0x00, 0x51, 0xE8, 0x12, 0xE6, 0x51, 0xE9, 0x1A, 0xE7, 0x7F, 0x60, 0xD4, 0x3E, 0xE8, 0x54, 0x03, 0x7F, 0x70, 0xFB, 0x6F, 0x01, 0x6F, 0x02, 0x7F, 0x62, 0xD0, 0x00, 0x51, 0x0E, 0x13, 0xFC, 0x62, 0xD0, 0x00, 0x54, 0x02, 0x7F, 0x70, 0xFE, 0x62, 0xD0, 0x00, 0x51, 0x0D, 0x7F, 0x52, 0xFB, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x7F, 0x5D, 0xC8, 0x62, 0xD0, 0x00, 0x39, 0x00, 0x7F, 0x80, 0x18
+		}
+	},
+	{
+		409,
+		79,
+		0x39,
+		0x01C4,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xC4, 0x52, 0x03, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x7F, 0x0E, 0xE9, 0x01, 0x51, 0xE9, 0x60, 0xD5, 0x7F, 0x71, 0x10, 0x5D, 0xE0, 0x54, 0x01, 0x41, 0xE0, 0xE7, 0x43, 0xE0, 0x18, 0x70, 0xCF, 0x62, 0xE3, 0x38, 0x7F, 0x08, 0x51, 0xE9, 0x62, 0xD0, 0x04, 0x53, 0xAD, 0x18, 0x53, 0xAE, 0x7F, 0x62, 0xD0, 0x00, 0x52, 0x01, 0x53, 0xE8, 0x52, 0x00, 0x53, 0xE9, 0x7F, 0x62, 0xD0, 0x04, 0x51, 0xA8, 0x08, 0xD8, 0xC9
+		}
+	},
+	{
+		410,
+		79,
+		0x39,
+		0x01C5,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xC5, 0x51, 0xA7, 0x62, 0xD0, 0x03, 0x53, 0xEE, 0x18, 0x53, 0xEF, 0x7F, 0x52, 0x05, 0x54, 0x03, 0x52, 0x02, 0x54, 0x04, 0x7F, 0x62, 0xD0, 0x00, 0x51, 0x0D, 0x08, 0x51, 0x0C, 0x62, 0xD0, 0x03, 0x53, 0xD4, 0x18, 0x53, 0xD5, 0x7F, 0x5D, 0xD6, 0x53, 0xE9, 0x2E, 0xE9, 0xFE, 0x51, 0xE9, 0x54, 0x02, 0x43, 0xD6, 0x01, 0x52, 0xFC, 0x7F, 0x53, 0xE8, 0x52, 0xFB, 0x09, 0x00, 0x60, 0xD5, 0x7F, 0x62, 0xD2, 0xBE
+		}
+	},
+	{
+		411,
+		79,
+		0x39,
+		0x01C6,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xC6, 0xD0, 0x00, 0x51, 0xE9, 0x62, 0xD0, 0x04, 0x7F, 0x62, 0xD0, 0x04, 0x53, 0xCE, 0x62, 0xD0, 0x04, 0x51, 0xE0, 0x7F, 0x62, 0xD0, 0x04, 0x51, 0xE1, 0x62, 0xD0, 0x00, 0x3A, 0x0E, 0x7F, 0x62, 0xD0, 0x00, 0x52, 0x06, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x7F, 0x52, 0x06, 0x53, 0xE6, 0x55, 0xE7, 0x00, 0x7F, 0x62, 0xD0, 0x04, 0x52, 0x00, 0x3A, 0xE1, 0x7F, 0x52, 0x02, 0x03, 0x02, 0x54, 0x00, 0x07, 0x01, 0x1D
+		}
+	},
+	{
+		412,
+		79,
+		0x39,
+		0x01C7,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xC7, 0x00, 0x2E, 0x7F, 0x51, 0xE8, 0x08, 0x51, 0xE9, 0x62, 0xD0, 0x04, 0x53, 0xA1, 0x18, 0x53, 0xA2, 0x7F, 0x12, 0xE8, 0x50, 0x00, 0x1A, 0xE9, 0x7F, 0x62, 0xD0, 0x03, 0x51, 0x9D, 0x62, 0xD0, 0x00, 0x7F, 0x62, 0xD0, 0x04, 0x51, 0xE1, 0x13, 0x01, 0x62, 0xD0, 0x00, 0x54, 0x00, 0x7F, 0x55, 0xDC, 0x00, 0x62, 0xD0, 0x04, 0x7F, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x54, 0x0A, 0x51, 0xE9, 0x54, 0x09, 0x45, 0xA6
+		}
+	},
+	{
+		413,
+		79,
+		0x39,
+		0x01C8,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xC8, 0x7F, 0x08, 0x57, 0x98, 0x28, 0x53, 0xE9, 0x18, 0x75, 0x09, 0x00, 0x28, 0x7F, 0x08, 0x57, 0x91, 0x28, 0x53, 0xE9, 0x18, 0x75, 0x09, 0x00, 0x28, 0x7F, 0x62, 0xD0, 0x00, 0x55, 0x0B, 0x01, 0x55, 0x0A, 0x00, 0x71, 0x01, 0x7F, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x54, 0x01, 0x51, 0xE9, 0x54, 0x00, 0x7F, 0x52, 0x04, 0x53, 0xE6, 0x55, 0xE7, 0x00, 0x7F, 0x50, 0x0D, 0x10, 0x57, 0x88, 0x28, 0x20, 0x36, 0x89
+		}
+	},
+	{
+		414,
+		79,
+		0x39,
+		0x01C9,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xC9, 0x7F, 0x62, 0xD0, 0x04, 0x52, 0x00, 0x3A, 0xC0, 0x7F, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x51, 0xE8, 0x7F, 0x51, 0xAF, 0x60, 0x04, 0x62, 0xD0, 0x00, 0x7F, 0x52, 0xFC, 0x62, 0xD0, 0x04, 0x53, 0xD7, 0x56, 0xFC, 0x00, 0x7F, 0x62, 0xD0, 0x00, 0x50, 0x0D, 0x10, 0x57, 0x87, 0x28, 0x20, 0x7F, 0x62, 0xD0, 0x04, 0x51, 0xA8, 0x62, 0xD0, 0x03, 0x12, 0xEF, 0x7F, 0x62, 0xD0, 0x04, 0x51, 0xA7, 0x62, 0xF2, 0x02
+		}
+	},
+	{
+		415,
+		79,
+		0x39,
+		0x01CA,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xCA, 0xD0, 0x03, 0x1A, 0xEE, 0x7F, 0x62, 0xD0, 0x04, 0x55, 0xD1, 0x00, 0x7F, 0x62, 0xD0, 0x04, 0x55, 0xD6, 0x00, 0x7F, 0x62, 0xD0, 0x03, 0x47, 0x99, 0x70, 0x7F, 0x50, 0x00, 0x3F, 0xE8, 0x3F, 0xE8, 0x7F, 0x62, 0xD0, 0x04, 0x55, 0xB2, 0xFF, 0x55, 0xB1, 0xFF, 0x7F, 0x56, 0x01, 0x00, 0x56, 0x02, 0x00, 0x7F, 0x71, 0x10, 0x60, 0xE0, 0x70, 0xCF, 0x7F, 0x62, 0xD0, 0x03, 0x51, 0xF6, 0x62, 0xD0, 0x51, 0xC1
+		}
+	},
+	{
+		416,
+		79,
+		0x39,
+		0x01CB,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xCB, 0x03, 0x7F, 0x62, 0xD0, 0x03, 0x51, 0xF7, 0x62, 0xD0, 0x03, 0x7F, 0x71, 0x10, 0x43, 0xD7, 0x20, 0x43, 0xE0, 0x40, 0x7F, 0x52, 0xFA, 0x13, 0xF6, 0x52, 0xF9, 0x1B, 0xF5, 0x7F, 0x62, 0xD0, 0x00, 0x51, 0x0E, 0x62, 0xD0, 0x04, 0x7F, 0x3F, 0xE8, 0x62, 0xD0, 0x04, 0x51, 0xB6, 0x7F, 0x50, 0x0D, 0x10, 0x57, 0x86, 0x28, 0x20, 0x7F, 0x50, 0x0D, 0x10, 0x57, 0x9A, 0x28, 0x20, 0x7F, 0x62, 0xD0, 0x10, 0x40
+		}
+	},
+	{
+		417,
+		79,
+		0x39,
+		0x01CC,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xCC, 0x00, 0x52, 0x00, 0x13, 0x01, 0x7F, 0x62, 0xD0, 0x00, 0x52, 0x01, 0x13, 0x00, 0x7F, 0x50, 0x0D, 0x10, 0x57, 0x9B, 0x28, 0x20, 0x7F, 0x60, 0x0C, 0x62, 0xD0, 0x00, 0x51, 0xB2, 0x7F, 0x60, 0x08, 0x62, 0xD0, 0x00, 0x51, 0xB1, 0x7F, 0x62, 0xD0, 0x03, 0x51, 0x9B, 0x21, 0x0F, 0x7F, 0x62, 0xD0, 0x03, 0x47, 0x99, 0x04, 0x7F, 0x62, 0xD0, 0x04, 0x3C, 0xCD, 0x02, 0x7F, 0x06, 0xE8, 0x01, 0x0E, 0x82, 0x25
+		}
+	},
+	{
+		418,
+		79,
+		0x39,
+		0x01CD,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xCD, 0xE9, 0x00, 0x7F, 0x62, 0xD0, 0x04, 0x55, 0xCC, 0x00, 0x7F, 0x13, 0x03, 0x51, 0xE9, 0x1B, 0x02, 0x7F, 0x62, 0xD0, 0x04, 0x55, 0xD7, 0x00, 0x7F, 0x62, 0xD0, 0x04, 0x55, 0xD9, 0x00, 0x7F, 0x62, 0xD0, 0x04, 0x55, 0xD5, 0x00, 0x7F, 0x62, 0xD0, 0x04, 0x55, 0xDD, 0x01, 0x7F, 0x62, 0xD0, 0x04, 0x55, 0xCB, 0xF4, 0x7F, 0x62, 0xD0, 0x04, 0x55, 0xB4, 0x00, 0x7F, 0x41, 0xD7, 0xDF, 0x41, 0xE0, 0x54, 0xCA
+		}
+	},
+	{
+		419,
+		79,
+		0x39,
+		0x01CE,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xCE, 0xBF, 0x7F, 0x41, 0xE0, 0xEF, 0x62, 0xDA, 0xEF, 0x7F, 0x41, 0xE0, 0x7F, 0x62, 0xDA, 0x7F, 0x7F, 0x62, 0xD0, 0x00, 0x3C, 0xBA, 0x00, 0x7F, 0x00, 0xBF, 0x00, 0x20, 0x00, 0xEA, 0x00, 0x06, 0x01, 0x00, 0x00, 0xA0, 0x02, 0xB1, 0x00, 0x4F, 0x03, 0x99, 0x00, 0x47, 0x03, 0xE0, 0x01, 0x0D, 0x03, 0xE1, 0x00, 0x1F, 0x04, 0x99, 0x00, 0x49, 0x04, 0xE2, 0x02, 0x01, 0x00, 0xFF, 0x00, 0x30, 0x30, 0xF0, 0x03
+		}
+	},
+	{
+		420,
+		79,
+		0x39,
+		0x01CF,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xCF, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x24
+		}
+	},
+	{
+		421,
+		79,
+		0x39,
+		0x01D0,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xD0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x25
+		}
+	},
+	{
+		422,
+		79,
+		0x39,
+		0x01D1,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xD1, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x26
+		}
+	},
+	{
+		423,
+		79,
+		0x39,
+		0x01D2,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xD2, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x27
+		}
+	},
+	{
+		424,
+		79,
+		0x39,
+		0x01D3,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xD3, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x28
+		}
+	},
+	{
+		425,
+		79,
+		0x39,
+		0x01D4,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xD4, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x29
+		}
+	},
+	{
+		426,
+		79,
+		0x39,
+		0x01D5,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xD5, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x2A
+		}
+	},
+	{
+		427,
+		79,
+		0x39,
+		0x01D6,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xD6, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x2B
+		}
+	},
+	{
+		428,
+		79,
+		0x39,
+		0x01D7,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xD7, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x2C
+		}
+	},
+	{
+		429,
+		79,
+		0x39,
+		0x01D8,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xD8, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x2D
+		}
+	},
+	{
+		430,
+		79,
+		0x39,
+		0x01D9,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xD9, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x2E
+		}
+	},
+	{
+		431,
+		79,
+		0x39,
+		0x01DA,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xDA, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x2F
+		}
+	},
+	{
+		432,
+		79,
+		0x39,
+		0x01DB,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xDB, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x30
+		}
+	},
+	{
+		433,
+		79,
+		0x39,
+		0x01DC,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xDC, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x31
+		}
+	},
+	{
+		434,
+		79,
+		0x39,
+		0x01DD,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xDD, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x32
+		}
+	},
+	{
+		435,
+		79,
+		0x39,
+		0x01DE,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xDE, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x33
+		}
+	},
+	{
+		436,
+		79,
+		0x39,
+		0x01DF,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xDF, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x34
+		}
+	},
+	{
+		437,
+		79,
+		0x39,
+		0x01E0,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xE0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x35
+		}
+	},
+	{
+		438,
+		79,
+		0x39,
+		0x01E1,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xE1, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x36
+		}
+	},
+	{
+		439,
+		79,
+		0x39,
+		0x01E2,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xE2, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x37
+		}
+	},
+	{
+		440,
+		79,
+		0x39,
+		0x01E3,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xE3, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x38
+		}
+	},
+	{
+		441,
+		79,
+		0x39,
+		0x01E4,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xE4, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x39
+		}
+	},
+	{
+		442,
+		79,
+		0x39,
+		0x01E5,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xE5, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x3A
+		}
+	},
+	{
+		443,
+		79,
+		0x39,
+		0x01E6,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xE6, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x3B
+		}
+	},
+	{
+		444,
+		79,
+		0x39,
+		0x01E7,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xE7, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x3C
+		}
+	},
+	{
+		445,
+		79,
+		0x39,
+		0x01E8,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xE8, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x3D
+		}
+	},
+	{
+		446,
+		79,
+		0x39,
+		0x01E9,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xE9, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x3E
+		}
+	},
+	{
+		447,
+		79,
+		0x39,
+		0x01EA,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xEA, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x3F
+		}
+	},
+	{
+		448,
+		79,
+		0x39,
+		0x01EB,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xEB, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x40
+		}
+	},
+	{
+		449,
+		79,
+		0x39,
+		0x01EC,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xEC, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x41
+		}
+	},
+	{
+		450,
+		79,
+		0x39,
+		0x01ED,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xED, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x42
+		}
+	},
+	{
+		451,
+		79,
+		0x39,
+		0x01EE,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xEE, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x43
+		}
+	},
+	{
+		452,
+		79,
+		0x39,
+		0x01EF,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xEF, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x44
+		}
+	},
+	{
+		453,
+		79,
+		0x39,
+		0x01F0,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xF0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x45
+		}
+	},
+	{
+		454,
+		79,
+		0x39,
+		0x01F1,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xF1, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x46
+		}
+	},
+	{
+		455,
+		79,
+		0x39,
+		0x01F2,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xF2, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x47
+		}
+	},
+	{
+		456,
+		79,
+		0x39,
+		0x01F3,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xF3, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x48
+		}
+	},
+	{
+		457,
+		79,
+		0x39,
+		0x01F4,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xF4, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x49
+		}
+	},
+	{
+		458,
+		79,
+		0x39,
+		0x01F5,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xF5, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x4A
+		}
+	},
+	{
+		459,
+		79,
+		0x39,
+		0x01F6,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xF6, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x4B
+		}
+	},
+	{
+		460,
+		79,
+		0x39,
+		0x01F7,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xF7, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x4C
+		}
+	},
+	{
+		461,
+		79,
+		0x39,
+		0x01F8,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xF8, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x4D
+		}
+	},
+	{
+		462,
+		79,
+		0x39,
+		0x01F9,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xF9, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x4E
+		}
+	},
+	{
+		463,
+		79,
+		0x39,
+		0x01FA,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xFA, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x4F
+		}
+	},
+	{
+		464,
+		79,
+		0x39,
+		0x01FB,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xFB, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x50
+		}
+	},
+	{
+		465,
+		79,
+		0x39,
+		0x01FC,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xFC, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x51
+		}
+	},
+	{
+		466,
+		79,
+		0x39,
+		0x01FD,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xFD, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x52
+		}
+	},
+	{
+		467,
+		79,
+		0x39,
+		0x01FE,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xFE, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x53
+		}
+	},
+	{
+		468,
+		79,
+		0x39,
+		0x01FF,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xFF, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x54
+		}
+	},
+	{
+		469,
+		79,
+		0x39,
+		0x001E,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x1E, 0x19, 0xE5, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x01, 0x0B, 0x10, 0x12, 0xA0, 0x02, 0x04, 0x00, 0xC0, 0xC1, 0xC2, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xA5, 0xBC
+		}
+	},
+	{
+		470,
+		79,
+		0x39,
+		0x001F,
+		{
+			0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x1F, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xA0, 0x07, 0x5F, 0xF8, 0x3E, 0xEF
+		}
+	},
+	{
+		471,
+		11,
+		0x3B,
+		-1,
+		{
+			0x00, 0xFF, 0x3B, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
+		}
+	},
+};
+
+unsigned short cyttsp_fw_records = 472;
+
+unsigned char cyttsp_fw_tts_verh = 0x10;
+unsigned char cyttsp_fw_tts_verl = 0x12;
+unsigned char cyttsp_fw_app_idh = 0xA0;
+unsigned char cyttsp_fw_app_idl = 0x02;
+unsigned char cyttsp_fw_app_verh = 0x04;
+unsigned char cyttsp_fw_app_verl = 0x00;
+unsigned char cyttsp_fw_cid_0 = 0xC0;
+unsigned char cyttsp_fw_cid_1 = 0xC1;
+unsigned char cyttsp_fw_cid_2 = 0xC2;
diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c
new file mode 100644
index 0000000..c9905a4
--- /dev/null
+++ b/drivers/input/touchscreen/ft5x06_ts.c
@@ -0,0 +1,654 @@
+/*
+ *
+ * FocalTech ft5x06 TouchScreen driver.
+ *
+ * Copyright (c) 2010  Focal tech Ltd.
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * 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/i2c.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/input/ft5x06_ts.h>
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+/* Early-suspend level */
+#define FT5X06_SUSPEND_LEVEL 1
+#endif
+
+#define CFG_MAX_TOUCH_POINTS	5
+
+#define FT_STARTUP_DLY		150
+#define FT_RESET_DLY		20
+
+#define FT_PRESS		0x7F
+#define FT_MAX_ID		0x0F
+#define FT_TOUCH_STEP		6
+#define FT_TOUCH_X_H_POS	3
+#define FT_TOUCH_X_L_POS	4
+#define FT_TOUCH_Y_H_POS	5
+#define FT_TOUCH_Y_L_POS	6
+#define FT_TOUCH_EVENT_POS	3
+#define FT_TOUCH_ID_POS		5
+
+#define POINT_READ_BUF	(3 + FT_TOUCH_STEP * CFG_MAX_TOUCH_POINTS)
+
+/*register address*/
+#define FT5X06_REG_PMODE	0xA5
+#define FT5X06_REG_FW_VER	0xA6
+#define FT5X06_REG_POINT_RATE	0x88
+#define FT5X06_REG_THGROUP	0x80
+
+/* power register bits*/
+#define FT5X06_PMODE_ACTIVE		0x00
+#define FT5X06_PMODE_MONITOR		0x01
+#define FT5X06_PMODE_STANDBY		0x02
+#define FT5X06_PMODE_HIBERNATE		0x03
+
+#define FT5X06_VTG_MIN_UV	2600000
+#define FT5X06_VTG_MAX_UV	3300000
+#define FT5X06_I2C_VTG_MIN_UV	1800000
+#define FT5X06_I2C_VTG_MAX_UV	1800000
+
+struct ts_event {
+	u16 x[CFG_MAX_TOUCH_POINTS];	/*x coordinate */
+	u16 y[CFG_MAX_TOUCH_POINTS];	/*y coordinate */
+	/* touch event: 0 -- down; 1-- contact; 2 -- contact */
+	u8 touch_event[CFG_MAX_TOUCH_POINTS];
+	u8 finger_id[CFG_MAX_TOUCH_POINTS];	/*touch ID */
+	u16 pressure;
+	u8 touch_point;
+};
+
+struct ft5x06_ts_data {
+	struct i2c_client *client;
+	struct input_dev *input_dev;
+	struct ts_event event;
+	const struct ft5x06_ts_platform_data *pdata;
+	struct regulator *vdd;
+	struct regulator *vcc_i2c;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	struct early_suspend early_suspend;
+#endif
+};
+
+static int ft5x06_i2c_read(struct i2c_client *client, char *writebuf,
+			   int writelen, char *readbuf, int readlen)
+{
+	int ret;
+
+	if (writelen > 0) {
+		struct i2c_msg msgs[] = {
+			{
+				 .addr = client->addr,
+				 .flags = 0,
+				 .len = writelen,
+				 .buf = writebuf,
+			 },
+			{
+				 .addr = client->addr,
+				 .flags = I2C_M_RD,
+				 .len = readlen,
+				 .buf = readbuf,
+			 },
+		};
+		ret = i2c_transfer(client->adapter, msgs, 2);
+		if (ret < 0)
+			dev_err(&client->dev, "%s: i2c read error.\n",
+				__func__);
+	} else {
+		struct i2c_msg msgs[] = {
+			{
+				 .addr = client->addr,
+				 .flags = I2C_M_RD,
+				 .len = readlen,
+				 .buf = readbuf,
+			 },
+		};
+		ret = i2c_transfer(client->adapter, msgs, 1);
+		if (ret < 0)
+			dev_err(&client->dev, "%s:i2c read error.\n", __func__);
+	}
+	return ret;
+}
+
+static int ft5x06_i2c_write(struct i2c_client *client, char *writebuf,
+			    int writelen)
+{
+	int ret;
+
+	struct i2c_msg msgs[] = {
+		{
+			 .addr = client->addr,
+			 .flags = 0,
+			 .len = writelen,
+			 .buf = writebuf,
+		 },
+	};
+	ret = i2c_transfer(client->adapter, msgs, 1);
+	if (ret < 0)
+		dev_err(&client->dev, "%s: i2c write error.\n", __func__);
+
+	return ret;
+}
+
+static void ft5x06_report_value(struct ft5x06_ts_data *data)
+{
+	struct ts_event *event = &data->event;
+	int i;
+	int fingerdown = 0;
+
+	for (i = 0; i < event->touch_point; i++) {
+		if (event->touch_event[i] == 0 || event->touch_event[i] == 2) {
+			event->pressure = FT_PRESS;
+			fingerdown++;
+		} else {
+			event->pressure = 0;
+		}
+
+		input_report_abs(data->input_dev, ABS_MT_POSITION_X,
+				 event->x[i]);
+		input_report_abs(data->input_dev, ABS_MT_POSITION_Y,
+				 event->y[i]);
+		input_report_abs(data->input_dev, ABS_MT_PRESSURE,
+				 event->pressure);
+		input_report_abs(data->input_dev, ABS_MT_TRACKING_ID,
+				 event->finger_id[i]);
+		input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR,
+				 event->pressure);
+		input_mt_sync(data->input_dev);
+	}
+
+	input_report_key(data->input_dev, BTN_TOUCH, !!fingerdown);
+	input_sync(data->input_dev);
+}
+
+static int ft5x06_handle_touchdata(struct ft5x06_ts_data *data)
+{
+	struct ts_event *event = &data->event;
+	int ret, i;
+	u8 buf[POINT_READ_BUF] = { 0 };
+	u8 pointid = FT_MAX_ID;
+
+	ret = ft5x06_i2c_read(data->client, buf, 1, buf, POINT_READ_BUF);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "%s read touchdata failed.\n",
+			__func__);
+		return ret;
+	}
+	memset(event, 0, sizeof(struct ts_event));
+
+	event->touch_point = 0;
+	for (i = 0; i < CFG_MAX_TOUCH_POINTS; i++) {
+		pointid = (buf[FT_TOUCH_ID_POS + FT_TOUCH_STEP * i]) >> 4;
+		if (pointid >= FT_MAX_ID)
+			break;
+		else
+			event->touch_point++;
+		event->x[i] =
+		    (s16) (buf[FT_TOUCH_X_H_POS + FT_TOUCH_STEP * i] & 0x0F) <<
+		    8 | (s16) buf[FT_TOUCH_X_L_POS + FT_TOUCH_STEP * i];
+		event->y[i] =
+		    (s16) (buf[FT_TOUCH_Y_H_POS + FT_TOUCH_STEP * i] & 0x0F) <<
+		    8 | (s16) buf[FT_TOUCH_Y_L_POS + FT_TOUCH_STEP * i];
+		event->touch_event[i] =
+		    buf[FT_TOUCH_EVENT_POS + FT_TOUCH_STEP * i] >> 6;
+		event->finger_id[i] =
+		    (buf[FT_TOUCH_ID_POS + FT_TOUCH_STEP * i]) >> 4;
+	}
+
+	ft5x06_report_value(data);
+
+	return 0;
+}
+
+static irqreturn_t ft5x06_ts_interrupt(int irq, void *dev_id)
+{
+	struct ft5x06_ts_data *data = dev_id;
+	int rc;
+
+	rc = ft5x06_handle_touchdata(data);
+	if (rc)
+		pr_err("%s: handling touchdata failed\n", __func__);
+
+	return IRQ_HANDLED;
+}
+
+static int ft5x06_power_on(struct ft5x06_ts_data *data, bool on)
+{
+	int rc;
+
+	if (!on)
+		goto power_off;
+
+	rc = regulator_enable(data->vdd);
+	if (rc) {
+		dev_err(&data->client->dev,
+			"Regulator vdd enable failed rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = regulator_enable(data->vcc_i2c);
+	if (rc) {
+		dev_err(&data->client->dev,
+			"Regulator vcc_i2c enable failed rc=%d\n", rc);
+		regulator_disable(data->vdd);
+	}
+
+	return rc;
+
+power_off:
+	rc = regulator_disable(data->vdd);
+	if (rc) {
+		dev_err(&data->client->dev,
+			"Regulator vdd disable failed rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = regulator_disable(data->vcc_i2c);
+	if (rc) {
+		dev_err(&data->client->dev,
+			"Regulator vcc_i2c disable failed rc=%d\n", rc);
+		regulator_enable(data->vdd);
+	}
+
+	return rc;
+}
+
+static int ft5x06_power_init(struct ft5x06_ts_data *data, bool on)
+{
+	int rc;
+
+	if (!on)
+		goto pwr_deinit;
+
+	data->vdd = regulator_get(&data->client->dev, "vdd");
+	if (IS_ERR(data->vdd)) {
+		rc = PTR_ERR(data->vdd);
+		dev_err(&data->client->dev,
+			"Regulator get failed vdd rc=%d\n", rc);
+		return rc;
+	}
+
+	if (regulator_count_voltages(data->vdd) > 0) {
+		rc = regulator_set_voltage(data->vdd, FT5X06_VTG_MIN_UV,
+					   FT5X06_VTG_MAX_UV);
+		if (rc) {
+			dev_err(&data->client->dev,
+				"Regulator set_vtg failed vdd rc=%d\n", rc);
+			goto reg_vdd_put;
+		}
+	}
+
+	data->vcc_i2c = regulator_get(&data->client->dev, "vcc_i2c");
+	if (IS_ERR(data->vcc_i2c)) {
+		rc = PTR_ERR(data->vcc_i2c);
+		dev_err(&data->client->dev,
+			"Regulator get failed vcc_i2c rc=%d\n", rc);
+		goto reg_vdd_set_vtg;
+	}
+
+	if (regulator_count_voltages(data->vcc_i2c) > 0) {
+		rc = regulator_set_voltage(data->vcc_i2c, FT5X06_I2C_VTG_MIN_UV,
+					   FT5X06_I2C_VTG_MAX_UV);
+		if (rc) {
+			dev_err(&data->client->dev,
+			"Regulator set_vtg failed vcc_i2c rc=%d\n", rc);
+			goto reg_vcc_i2c_put;
+		}
+	}
+
+	return 0;
+
+reg_vcc_i2c_put:
+	regulator_put(data->vcc_i2c);
+reg_vdd_set_vtg:
+	if (regulator_count_voltages(data->vdd) > 0)
+		regulator_set_voltage(data->vdd, 0, FT5X06_VTG_MAX_UV);
+reg_vdd_put:
+	regulator_put(data->vdd);
+	return rc;
+
+pwr_deinit:
+	if (regulator_count_voltages(data->vdd) > 0)
+		regulator_set_voltage(data->vdd, 0, FT5X06_VTG_MAX_UV);
+
+	regulator_put(data->vdd);
+
+	if (regulator_count_voltages(data->vcc_i2c) > 0)
+		regulator_set_voltage(data->vcc_i2c, 0, FT5X06_I2C_VTG_MAX_UV);
+
+	regulator_put(data->vcc_i2c);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int ft5x06_ts_suspend(struct device *dev)
+{
+	struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+	char txbuf[2];
+
+	disable_irq(data->client->irq);
+
+	if (gpio_is_valid(data->pdata->reset_gpio)) {
+		txbuf[0] = FT5X06_REG_PMODE;
+		txbuf[1] = FT5X06_PMODE_HIBERNATE;
+		ft5x06_i2c_write(data->client, txbuf, sizeof(txbuf));
+	}
+
+	return 0;
+}
+
+static int ft5x06_ts_resume(struct device *dev)
+{
+	struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+
+	if (gpio_is_valid(data->pdata->reset_gpio)) {
+		gpio_set_value_cansleep(data->pdata->reset_gpio, 0);
+		msleep(FT_RESET_DLY);
+		gpio_set_value_cansleep(data->pdata->reset_gpio, 1);
+	}
+	enable_irq(data->client->irq);
+
+	return 0;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void ft5x06_ts_early_suspend(struct early_suspend *handler)
+{
+	struct ft5x06_ts_data *data = container_of(handler,
+						   struct ft5x06_ts_data,
+						   early_suspend);
+
+	ft5x06_ts_suspend(&data->client->dev);
+}
+
+static void ft5x06_ts_late_resume(struct early_suspend *handler)
+{
+	struct ft5x06_ts_data *data = container_of(handler,
+						   struct ft5x06_ts_data,
+						   early_suspend);
+
+	ft5x06_ts_resume(&data->client->dev);
+}
+#endif
+
+static const struct dev_pm_ops ft5x06_ts_pm_ops = {
+#ifndef CONFIG_HAS_EARLYSUSPEND
+	.suspend = ft5x06_ts_suspend,
+	.resume = ft5x06_ts_resume,
+#endif
+};
+#endif
+
+static int ft5x06_ts_probe(struct i2c_client *client,
+			   const struct i2c_device_id *id)
+{
+	const struct ft5x06_ts_platform_data *pdata = client->dev.platform_data;
+	struct ft5x06_ts_data *data;
+	struct input_dev *input_dev;
+	u8 reg_value;
+	u8 reg_addr;
+	int err;
+
+	if (!pdata) {
+		dev_err(&client->dev, "Invalid pdata\n");
+		return -EINVAL;
+	}
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		dev_err(&client->dev, "I2C not supported\n");
+		return -ENODEV;
+	}
+
+	data = kzalloc(sizeof(struct ft5x06_ts_data), GFP_KERNEL);
+	if (!data) {
+		dev_err(&client->dev, "Not enough memory\n");
+		return -ENOMEM;
+	}
+
+	input_dev = input_allocate_device();
+	if (!input_dev) {
+		err = -ENOMEM;
+		dev_err(&client->dev, "failed to allocate input device\n");
+		goto free_mem;
+	}
+
+	data->input_dev = input_dev;
+	data->client = client;
+	data->pdata = pdata;
+
+	input_dev->name = "ft5x06_ts";
+	input_dev->id.bustype = BUS_I2C;
+	input_dev->dev.parent = &client->dev;
+
+	input_set_drvdata(input_dev, data);
+	i2c_set_clientdata(client, data);
+
+	__set_bit(EV_KEY, input_dev->evbit);
+	__set_bit(EV_ABS, input_dev->evbit);
+	__set_bit(BTN_TOUCH, input_dev->keybit);
+
+	input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,
+			     pdata->x_max, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0,
+			     pdata->y_max, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0,
+			     CFG_MAX_TOUCH_POINTS, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, FT_PRESS, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, FT_PRESS, 0, 0);
+
+	err = input_register_device(input_dev);
+	if (err) {
+		dev_err(&client->dev, "Input device registration failed\n");
+		goto free_inputdev;
+	}
+
+	if (pdata->power_init) {
+		err = pdata->power_init(true);
+		if (err) {
+			dev_err(&client->dev, "power init failed");
+			goto unreg_inputdev;
+		}
+	} else {
+		err = ft5x06_power_init(data, true);
+		if (err) {
+			dev_err(&client->dev, "power init failed");
+			goto unreg_inputdev;
+		}
+	}
+
+	if (pdata->power_on) {
+		err = pdata->power_on(true);
+		if (err) {
+			dev_err(&client->dev, "power on failed");
+			goto pwr_deinit;
+		}
+	} else {
+		err = ft5x06_power_on(data, true);
+		if (err) {
+			dev_err(&client->dev, "power on failed");
+			goto pwr_deinit;
+		}
+	}
+
+	if (gpio_is_valid(pdata->irq_gpio)) {
+		err = gpio_request(pdata->irq_gpio, "ft5x06_irq_gpio");
+		if (err) {
+			dev_err(&client->dev, "irq gpio request failed");
+			goto pwr_off;
+		}
+		err = gpio_direction_input(pdata->irq_gpio);
+		if (err) {
+			dev_err(&client->dev,
+				"set_direction for irq gpio failed\n");
+			goto free_irq_gpio;
+		}
+	}
+
+	if (gpio_is_valid(pdata->reset_gpio)) {
+		err = gpio_request(pdata->reset_gpio, "ft5x06_reset_gpio");
+		if (err) {
+			dev_err(&client->dev, "reset gpio request failed");
+			goto free_irq_gpio;
+		}
+
+		err = gpio_direction_output(pdata->reset_gpio, 0);
+		if (err) {
+			dev_err(&client->dev,
+				"set_direction for reset gpio failed\n");
+			goto free_reset_gpio;
+		}
+		msleep(FT_RESET_DLY);
+		gpio_set_value_cansleep(data->pdata->reset_gpio, 1);
+	}
+
+	/* make sure CTP already finish startup process */
+	msleep(FT_STARTUP_DLY);
+
+	/*get some register information */
+	reg_addr = FT5X06_REG_FW_VER;
+	err = ft5x06_i2c_read(client, &reg_addr, 1, &reg_value, 1);
+	if (err)
+		dev_err(&client->dev, "version read failed");
+
+	dev_info(&client->dev, "[FTS] Firmware version = 0x%x\n", reg_value);
+
+	reg_addr = FT5X06_REG_POINT_RATE;
+	ft5x06_i2c_read(client, &reg_addr, 1, &reg_value, 1);
+	if (err)
+		dev_err(&client->dev, "report rate read failed");
+	dev_info(&client->dev, "[FTS] report rate is %dHz.\n", reg_value * 10);
+
+	reg_addr = FT5X06_REG_THGROUP;
+	err = ft5x06_i2c_read(client, &reg_addr, 1, &reg_value, 1);
+	if (err)
+		dev_err(&client->dev, "threshold read failed");
+	dev_dbg(&client->dev, "[FTS] touch threshold is %d.\n", reg_value * 4);
+
+	err = request_threaded_irq(client->irq, NULL,
+				   ft5x06_ts_interrupt, pdata->irqflags,
+				   client->dev.driver->name, data);
+	if (err) {
+		dev_err(&client->dev, "request irq failed\n");
+		goto free_reset_gpio;
+	}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN +
+	    FT5X06_SUSPEND_LEVEL;
+	data->early_suspend.suspend = ft5x06_ts_early_suspend;
+	data->early_suspend.resume = ft5x06_ts_late_resume;
+	register_early_suspend(&data->early_suspend);
+#endif
+
+	return 0;
+
+free_reset_gpio:
+	if (gpio_is_valid(pdata->reset_gpio))
+		gpio_free(pdata->reset_gpio);
+free_irq_gpio:
+	if (gpio_is_valid(pdata->irq_gpio))
+		gpio_free(pdata->reset_gpio);
+pwr_off:
+	if (pdata->power_on)
+		pdata->power_on(false);
+	else
+		ft5x06_power_on(data, false);
+pwr_deinit:
+	if (pdata->power_init)
+		pdata->power_init(false);
+	else
+		ft5x06_power_init(data, false);
+unreg_inputdev:
+	input_unregister_device(input_dev);
+	input_dev = NULL;
+free_inputdev:
+	input_free_device(input_dev);
+free_mem:
+	kfree(data);
+	return err;
+}
+
+static int __devexit ft5x06_ts_remove(struct i2c_client *client)
+{
+	struct ft5x06_ts_data *data = i2c_get_clientdata(client);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	unregister_early_suspend(&data->early_suspend);
+#endif
+	free_irq(client->irq, data);
+
+	if (gpio_is_valid(data->pdata->reset_gpio))
+		gpio_free(data->pdata->reset_gpio);
+
+	if (gpio_is_valid(data->pdata->irq_gpio))
+		gpio_free(data->pdata->reset_gpio);
+
+	if (data->pdata->power_on)
+		data->pdata->power_on(false);
+	else
+		ft5x06_power_on(data, false);
+
+	if (data->pdata->power_init)
+		data->pdata->power_init(false);
+	else
+		ft5x06_power_init(data, false);
+
+	input_unregister_device(data->input_dev);
+	kfree(data);
+
+	return 0;
+}
+
+static const struct i2c_device_id ft5x06_ts_id[] = {
+	{"ft5x06_ts", 0},
+	{},
+};
+
+MODULE_DEVICE_TABLE(i2c, ft5x06_ts_id);
+
+static struct i2c_driver ft5x06_ts_driver = {
+	.probe = ft5x06_ts_probe,
+	.remove = __devexit_p(ft5x06_ts_remove),
+	.driver = {
+		   .name = "ft5x06_ts",
+		   .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+		   .pm = &ft5x06_ts_pm_ops,
+#endif
+		   },
+	.id_table = ft5x06_ts_id,
+};
+
+static int __init ft5x06_ts_init(void)
+{
+	return i2c_add_driver(&ft5x06_ts_driver);
+}
+module_init(ft5x06_ts_init);
+
+static void __exit ft5x06_ts_exit(void)
+{
+	i2c_del_driver(&ft5x06_ts_driver);
+}
+module_exit(ft5x06_ts_exit);
+
+MODULE_DESCRIPTION("FocalTech ft5x06 TouchScreen driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/msm_touch.c b/drivers/input/touchscreen/msm_touch.c
new file mode 100644
index 0000000..7ba896a
--- /dev/null
+++ b/drivers/input/touchscreen/msm_touch.c
@@ -0,0 +1,317 @@
+/* drivers/input/touchscreen/msm_touch.c
+ *
+ * Copyright (c) 2008-2009, 2011, Code Aurora Forum. All rights reserved.
+ *
+ * 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/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/jiffies.h>
+#include <linux/io.h>
+
+#include <mach/msm_touch.h>
+
+/* HW register map */
+#define TSSC_CTL_REG      0x100
+#define TSSC_SI_REG       0x108
+#define TSSC_OPN_REG      0x104
+#define TSSC_STATUS_REG   0x10C
+#define TSSC_AVG12_REG    0x110
+
+/* status bits */
+#define TSSC_STS_OPN_SHIFT 0x6
+#define TSSC_STS_OPN_BMSK  0x1C0
+#define TSSC_STS_NUMSAMP_SHFT 0x1
+#define TSSC_STS_NUMSAMP_BMSK 0x3E
+
+/* CTL bits */
+#define TSSC_CTL_EN		(0x1 << 0)
+#define TSSC_CTL_SW_RESET	(0x1 << 2)
+#define TSSC_CTL_MASTER_MODE	(0x3 << 3)
+#define TSSC_CTL_AVG_EN		(0x1 << 5)
+#define TSSC_CTL_DEB_EN		(0x1 << 6)
+#define TSSC_CTL_DEB_12_MS	(0x2 << 7)	/* 1.2 ms */
+#define TSSC_CTL_DEB_16_MS	(0x3 << 7)	/* 1.6 ms */
+#define TSSC_CTL_DEB_2_MS	(0x4 << 7)	/* 2 ms */
+#define TSSC_CTL_DEB_3_MS	(0x5 << 7)	/* 3 ms */
+#define TSSC_CTL_DEB_4_MS	(0x6 << 7)	/* 4 ms */
+#define TSSC_CTL_DEB_6_MS	(0x7 << 7)	/* 6 ms */
+#define TSSC_CTL_INTR_FLAG1	(0x1 << 10)
+#define TSSC_CTL_DATA		(0x1 << 11)
+#define TSSC_CTL_SSBI_CTRL_EN	(0x1 << 13)
+
+/* control reg's default state */
+#define TSSC_CTL_STATE	  ( \
+		TSSC_CTL_DEB_12_MS | \
+		TSSC_CTL_DEB_EN | \
+		TSSC_CTL_AVG_EN | \
+		TSSC_CTL_MASTER_MODE | \
+		TSSC_CTL_EN)
+
+#define TSSC_NUMBER_OF_OPERATIONS 2
+#define TS_PENUP_TIMEOUT_MS 20
+
+#define TS_DRIVER_NAME "msm_touchscreen"
+
+#define X_MAX	1024
+#define Y_MAX	1024
+#define P_MAX	256
+
+struct ts {
+	struct input_dev *input;
+	struct timer_list timer;
+	int irq;
+	unsigned int x_max;
+	unsigned int y_max;
+};
+
+static void __iomem *virt;
+#define TSSC_REG(reg) (virt + TSSC_##reg##_REG)
+
+static void ts_update_pen_state(struct ts *ts, int x, int y, int pressure)
+{
+	if (pressure) {
+		input_report_abs(ts->input, ABS_X, x);
+		input_report_abs(ts->input, ABS_Y, y);
+		input_report_abs(ts->input, ABS_PRESSURE, pressure);
+		input_report_key(ts->input, BTN_TOUCH, !!pressure);
+	} else {
+		input_report_abs(ts->input, ABS_PRESSURE, 0);
+		input_report_key(ts->input, BTN_TOUCH, 0);
+	}
+
+	input_sync(ts->input);
+}
+
+static void ts_timer(unsigned long arg)
+{
+	struct ts *ts = (struct ts *)arg;
+
+	ts_update_pen_state(ts, 0, 0, 0);
+}
+
+static irqreturn_t ts_interrupt(int irq, void *dev_id)
+{
+	u32 avgs, x, y, lx, ly;
+	u32 num_op, num_samp;
+	u32 status;
+
+	struct ts *ts = dev_id;
+
+	status = readl_relaxed(TSSC_REG(STATUS));
+	avgs = readl_relaxed(TSSC_REG(AVG12));
+	x = avgs & 0xFFFF;
+	y = avgs >> 16;
+
+	/* For pen down make sure that the data just read is still valid.
+	 * The DATA bit will still be set if the ARM9 hasn't clobbered
+	 * the TSSC. If it's not set, then it doesn't need to be cleared
+	 * here, so just return.
+	 */
+	if (!(readl_relaxed(TSSC_REG(CTL)) & TSSC_CTL_DATA))
+		goto out;
+
+	/* Data has been read, OK to clear the data flag */
+	writel_relaxed(TSSC_CTL_STATE, TSSC_REG(CTL));
+	/* barrier: Write to complete before the next sample */
+	mb();
+	/* Valid samples are indicated by the sample number in the status
+	 * register being the number of expected samples and the number of
+	 * samples collected being zero (this check is due to ADC contention).
+	 */
+	num_op = (status & TSSC_STS_OPN_BMSK) >> TSSC_STS_OPN_SHIFT;
+	num_samp = (status & TSSC_STS_NUMSAMP_BMSK) >> TSSC_STS_NUMSAMP_SHFT;
+
+	if ((num_op == TSSC_NUMBER_OF_OPERATIONS) && (num_samp == 0)) {
+		/* TSSC can do Z axis measurment, but driver doesn't support
+		 * this yet.
+		 */
+
+		/*
+		 * REMOVE THIS:
+		 * These x, y co-ordinates adjustments will be removed once
+		 * Android framework adds calibration framework.
+		 */
+#ifdef CONFIG_ANDROID_TOUCHSCREEN_MSM_HACKS
+		lx = ts->x_max - x;
+		ly = ts->y_max - y;
+#else
+		lx = x;
+		ly = y;
+#endif
+		ts_update_pen_state(ts, lx, ly, 255);
+		/* kick pen up timer - to make sure it expires again(!) */
+		mod_timer(&ts->timer,
+			jiffies + msecs_to_jiffies(TS_PENUP_TIMEOUT_MS));
+
+	} else
+		printk(KERN_INFO "Ignored interrupt: {%3d, %3d},"
+				" op = %3d samp = %3d\n",
+				 x, y, num_op, num_samp);
+
+out:
+	return IRQ_HANDLED;
+}
+
+static int __devinit ts_probe(struct platform_device *pdev)
+{
+	int result;
+	struct input_dev *input_dev;
+	struct resource *res, *ioarea;
+	struct ts *ts;
+	unsigned int x_max, y_max, pressure_max;
+	struct msm_ts_platform_data *pdata = pdev->dev.platform_data;
+
+	/* The primary initialization of the TS Hardware
+	 * is taken care of by the ADC code on the modem side
+	 */
+
+	ts = kzalloc(sizeof(struct ts), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!input_dev || !ts) {
+		result = -ENOMEM;
+		goto fail_alloc_mem;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");
+		result = -ENOENT;
+		goto fail_alloc_mem;
+	}
+
+	ts->irq = platform_get_irq(pdev, 0);
+	if (!ts->irq) {
+		dev_err(&pdev->dev, "Could not get IORESOURCE_IRQ\n");
+		result = -ENODEV;
+		goto fail_alloc_mem;
+	}
+
+	ioarea = request_mem_region(res->start, resource_size(res), pdev->name);
+	if (!ioarea) {
+		dev_err(&pdev->dev, "Could not allocate io region\n");
+		result = -EBUSY;
+		goto fail_alloc_mem;
+	}
+
+	virt = ioremap(res->start, resource_size(res));
+	if (!virt) {
+		dev_err(&pdev->dev, "Could not ioremap region\n");
+		result = -ENOMEM;
+		goto fail_ioremap;
+	}
+
+	input_dev->name = TS_DRIVER_NAME;
+	input_dev->phys = "msm_touch/input0";
+	input_dev->id.bustype = BUS_HOST;
+	input_dev->id.vendor = 0x0001;
+	input_dev->id.product = 0x0002;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &pdev->dev;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input_dev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
+	input_dev->absbit[BIT_WORD(ABS_MISC)] = BIT_MASK(ABS_MISC);
+	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+	if (pdata) {
+		x_max = pdata->x_max ? : X_MAX;
+		y_max = pdata->y_max ? : Y_MAX;
+		pressure_max = pdata->pressure_max ? : P_MAX;
+	} else {
+		x_max = X_MAX;
+		y_max = Y_MAX;
+		pressure_max = P_MAX;
+	}
+
+	ts->x_max = x_max;
+	ts->y_max = y_max;
+
+	input_set_abs_params(input_dev, ABS_X, 0, x_max, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y, 0, y_max, 0, 0);
+	input_set_abs_params(input_dev, ABS_PRESSURE, 0, pressure_max, 0, 0);
+
+	result = input_register_device(input_dev);
+	if (result)
+		goto fail_ip_reg;
+
+	ts->input = input_dev;
+
+	setup_timer(&ts->timer, ts_timer, (unsigned long)ts);
+	result = request_irq(ts->irq, ts_interrupt, IRQF_TRIGGER_RISING,
+				 "touchscreen", ts);
+	if (result)
+		goto fail_req_irq;
+
+	platform_set_drvdata(pdev, ts);
+
+	return 0;
+
+fail_req_irq:
+	input_unregister_device(input_dev);
+	input_dev = NULL;
+fail_ip_reg:
+	iounmap(virt);
+fail_ioremap:
+	release_mem_region(res->start, resource_size(res));
+fail_alloc_mem:
+	input_free_device(input_dev);
+	kfree(ts);
+	return result;
+}
+
+static int __devexit ts_remove(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct ts *ts = platform_get_drvdata(pdev);
+
+	free_irq(ts->irq, ts);
+	del_timer_sync(&ts->timer);
+
+	input_unregister_device(ts->input);
+	iounmap(virt);
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(res->start, resource_size(res));
+	platform_set_drvdata(pdev, NULL);
+	kfree(ts);
+
+	return 0;
+}
+
+static struct platform_driver ts_driver = {
+	.probe		= ts_probe,
+	.remove		= __devexit_p(ts_remove),
+	.driver		= {
+		.name = TS_DRIVER_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init ts_init(void)
+{
+	return platform_driver_register(&ts_driver);
+}
+module_init(ts_init);
+
+static void __exit ts_exit(void)
+{
+	platform_driver_unregister(&ts_driver);
+}
+module_exit(ts_exit);
+
+MODULE_DESCRIPTION("MSM Touch Screen driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:msm_touchscreen");
diff --git a/drivers/input/touchscreen/msm_ts.c b/drivers/input/touchscreen/msm_ts.c
new file mode 100644
index 0000000..eb2e73b
--- /dev/null
+++ b/drivers/input/touchscreen/msm_ts.c
@@ -0,0 +1,513 @@
+/* drivers/input/touchscreen/msm_ts.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * 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.
+ *
+ * TODO:
+ *      - Add a timer to simulate a pen_up in case there's a timeout.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/marimba-tsadc.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+#include <linux/earlysuspend.h>
+#endif
+
+#include <linux/input/msm_ts.h>
+
+#define TSSC_CTL			0x100
+#define 	TSSC_CTL_PENUP_IRQ	(1 << 12)
+#define 	TSSC_CTL_DATA_FLAG	(1 << 11)
+#define 	TSSC_CTL_DEBOUNCE_EN	(1 << 6)
+#define 	TSSC_CTL_EN_AVERAGE	(1 << 5)
+#define 	TSSC_CTL_MODE_MASTER	(3 << 3)
+#define 	TSSC_CTL_SW_RESET	(1 << 2)
+#define 	TSSC_CTL_ENABLE		(1 << 0)
+#define TSSC_OPN			0x104
+#define 	TSSC_OPN_NOOP		0x00
+#define 	TSSC_OPN_4WIRE_X	0x01
+#define 	TSSC_OPN_4WIRE_Y	0x02
+#define 	TSSC_OPN_4WIRE_Z1	0x03
+#define 	TSSC_OPN_4WIRE_Z2	0x04
+#define TSSC_SAMPLING_INT		0x108
+#define TSSC_STATUS			0x10c
+#define TSSC_AVG_12			0x110
+#define TSSC_AVG_34			0x114
+#define TSSC_SAMPLE(op,samp)		((0x118 + ((op & 0x3) * 0x20)) + \
+					 ((samp & 0x7) * 0x4))
+#define TSSC_TEST_1			0x198
+	#define TSSC_TEST_1_EN_GATE_DEBOUNCE (1 << 2)
+#define TSSC_TEST_2			0x19c
+
+struct msm_ts {
+	struct msm_ts_platform_data	*pdata;
+	struct input_dev		*input_dev;
+	void __iomem			*tssc_base;
+	uint32_t			ts_down:1;
+	struct ts_virt_key		*vkey_down;
+	struct marimba_tsadc_client	*ts_client;
+
+	unsigned int			sample_irq;
+	unsigned int			pen_up_irq;
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+	struct early_suspend		early_suspend;
+#endif
+	struct device			*dev;
+};
+
+static uint32_t msm_tsdebug;
+module_param_named(tsdebug, msm_tsdebug, uint, 0664);
+
+#define tssc_readl(t, a)	(readl_relaxed(((t)->tssc_base) + (a)))
+#define tssc_writel(t, v, a)	do {writel_relaxed(v, \
+					((t)->tssc_base) + (a)); } \
+					while (0)
+
+static void setup_next_sample(struct msm_ts *ts)
+{
+	uint32_t tmp;
+
+	/* 1.2ms debounce time */
+	tmp = ((2 << 7) | TSSC_CTL_DEBOUNCE_EN | TSSC_CTL_EN_AVERAGE |
+	       TSSC_CTL_MODE_MASTER | TSSC_CTL_ENABLE);
+	tssc_writel(ts, tmp, TSSC_CTL);
+	/* barrier: Make sure the write completes before the next sample */
+	mb();
+}
+
+static struct ts_virt_key *find_virt_key(struct msm_ts *ts,
+					 struct msm_ts_virtual_keys *vkeys,
+					 uint32_t val)
+{
+	int i;
+
+	if (!vkeys)
+		return NULL;
+
+	for (i = 0; i < vkeys->num_keys; ++i)
+		if ((val >= vkeys->keys[i].min) && (val <= vkeys->keys[i].max))
+			return &vkeys->keys[i];
+	return NULL;
+}
+
+
+static irqreturn_t msm_ts_irq(int irq, void *dev_id)
+{
+	struct msm_ts *ts = dev_id;
+	struct msm_ts_platform_data *pdata = ts->pdata;
+
+	uint32_t tssc_avg12, tssc_avg34, tssc_status, tssc_ctl;
+	int x, y, z1, z2;
+	int was_down;
+	int down;
+
+	tssc_ctl = tssc_readl(ts, TSSC_CTL);
+	tssc_status = tssc_readl(ts, TSSC_STATUS);
+	tssc_avg12 = tssc_readl(ts, TSSC_AVG_12);
+	tssc_avg34 = tssc_readl(ts, TSSC_AVG_34);
+
+	setup_next_sample(ts);
+
+	x = tssc_avg12 & 0xffff;
+	y = tssc_avg12 >> 16;
+	z1 = tssc_avg34 & 0xffff;
+	z2 = tssc_avg34 >> 16;
+
+	/* invert the inputs if necessary */
+	if (pdata->inv_x) x = pdata->inv_x - x;
+	if (pdata->inv_y) y = pdata->inv_y - y;
+	if (x < 0) x = 0;
+	if (y < 0) y = 0;
+
+	down = !(tssc_ctl & TSSC_CTL_PENUP_IRQ);
+	was_down = ts->ts_down;
+	ts->ts_down = down;
+
+	/* no valid data */
+	if (down && !(tssc_ctl & TSSC_CTL_DATA_FLAG))
+		return IRQ_HANDLED;
+
+	if (msm_tsdebug & 2)
+		printk("%s: down=%d, x=%d, y=%d, z1=%d, z2=%d, status %x\n",
+		       __func__, down, x, y, z1, z2, tssc_status);
+
+	if (!was_down && down) {
+		struct ts_virt_key *vkey = NULL;
+
+		if (pdata->vkeys_y && (y > pdata->virt_y_start))
+			vkey = find_virt_key(ts, pdata->vkeys_y, x);
+		if (!vkey && ts->pdata->vkeys_x && (x > pdata->virt_x_start))
+			vkey = find_virt_key(ts, pdata->vkeys_x, y);
+
+		if (vkey) {
+			WARN_ON(ts->vkey_down != NULL);
+			if(msm_tsdebug)
+				printk("%s: virtual key down %d\n", __func__,
+				       vkey->key);
+			ts->vkey_down = vkey;
+			input_report_key(ts->input_dev, vkey->key, 1);
+			input_sync(ts->input_dev);
+			return IRQ_HANDLED;
+		}
+	} else if (ts->vkey_down != NULL) {
+		if (!down) {
+			if(msm_tsdebug)
+				printk("%s: virtual key up %d\n", __func__,
+				       ts->vkey_down->key);
+			input_report_key(ts->input_dev, ts->vkey_down->key, 0);
+			input_sync(ts->input_dev);
+			ts->vkey_down = NULL;
+		}
+		return IRQ_HANDLED;
+	}
+
+	if (down) {
+		input_report_abs(ts->input_dev, ABS_X, x);
+		input_report_abs(ts->input_dev, ABS_Y, y);
+		input_report_abs(ts->input_dev, ABS_PRESSURE, z1);
+	}
+	input_report_key(ts->input_dev, BTN_TOUCH, down);
+	input_sync(ts->input_dev);
+
+	return IRQ_HANDLED;
+}
+
+static void dump_tssc_regs(struct msm_ts *ts)
+{
+#define __dump_tssc_reg(r) \
+		do { printk(#r " %x\n", tssc_readl(ts, (r))); } while(0)
+
+	__dump_tssc_reg(TSSC_CTL);
+	__dump_tssc_reg(TSSC_OPN);
+	__dump_tssc_reg(TSSC_SAMPLING_INT);
+	__dump_tssc_reg(TSSC_STATUS);
+	__dump_tssc_reg(TSSC_AVG_12);
+	__dump_tssc_reg(TSSC_AVG_34);
+	__dump_tssc_reg(TSSC_TEST_1);
+#undef __dump_tssc_reg
+}
+
+static int msm_ts_hw_init(struct msm_ts *ts)
+{
+	uint32_t tmp;
+
+	/* Enable the register clock to tssc so we can configure it. */
+	tssc_writel(ts, TSSC_CTL_ENABLE, TSSC_CTL);
+	/* Enable software reset*/
+	tssc_writel(ts, TSSC_CTL_SW_RESET, TSSC_CTL);
+
+	/* op1 - measure X, 1 sample, 12bit resolution */
+	tmp = (TSSC_OPN_4WIRE_X << 16) | (2 << 8) | (2 << 0);
+	/* op2 - measure Y, 1 sample, 12bit resolution */
+	tmp |= (TSSC_OPN_4WIRE_Y << 20) | (2 << 10) | (2 << 2);
+	/* op3 - measure Z1, 1 sample, 8bit resolution */
+	tmp |= (TSSC_OPN_4WIRE_Z1 << 24) | (2 << 12) | (0 << 4);
+
+	/* XXX: we don't actually need to measure Z2 (thus 0 samples) when
+	 * doing voltage-driven measurement */
+	/* op4 - measure Z2, 0 samples, 8bit resolution */
+	tmp |= (TSSC_OPN_4WIRE_Z2 << 28) | (0 << 14) | (0 << 6);
+	tssc_writel(ts, tmp, TSSC_OPN);
+
+	/* 16ms sampling interval */
+	tssc_writel(ts, 16, TSSC_SAMPLING_INT);
+	/* Enable gating logic to fix the timing delays caused because of
+	 * enabling debounce logic */
+	tssc_writel(ts, TSSC_TEST_1_EN_GATE_DEBOUNCE, TSSC_TEST_1);
+
+	setup_next_sample(ts);
+
+	return 0;
+}
+
+static void msm_ts_enable(struct msm_ts *ts, bool enable)
+{
+	uint32_t val;
+
+	if (enable == true)
+		msm_ts_hw_init(ts);
+	else {
+		val = tssc_readl(ts, TSSC_CTL);
+		val &= ~TSSC_CTL_ENABLE;
+		tssc_writel(ts, val, TSSC_CTL);
+	}
+}
+
+#ifdef CONFIG_PM
+static int
+msm_ts_suspend(struct device *dev)
+{
+	struct msm_ts *ts =  dev_get_drvdata(dev);
+
+	if (device_may_wakeup(dev) &&
+			device_may_wakeup(dev->parent))
+		enable_irq_wake(ts->sample_irq);
+	else {
+		disable_irq(ts->sample_irq);
+		disable_irq(ts->pen_up_irq);
+		msm_ts_enable(ts, false);
+	}
+
+	return 0;
+}
+
+static int
+msm_ts_resume(struct device *dev)
+{
+	struct msm_ts *ts =  dev_get_drvdata(dev);
+
+	if (device_may_wakeup(dev) &&
+			device_may_wakeup(dev->parent))
+		disable_irq_wake(ts->sample_irq);
+	else {
+		msm_ts_enable(ts, true);
+		enable_irq(ts->sample_irq);
+		enable_irq(ts->pen_up_irq);
+	}
+
+	return 0;
+}
+
+static struct dev_pm_ops msm_touchscreen_pm_ops = {
+#ifndef CONFIG_HAS_EARLYSUSPEND
+	.suspend	= msm_ts_suspend,
+	.resume		= msm_ts_resume,
+#endif
+};
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void msm_ts_early_suspend(struct early_suspend *h)
+{
+	struct msm_ts *ts = container_of(h, struct msm_ts, early_suspend);
+
+	msm_ts_suspend(ts->dev);
+}
+
+static void msm_ts_late_resume(struct early_suspend *h)
+{
+	struct msm_ts *ts = container_of(h, struct msm_ts, early_suspend);
+
+	msm_ts_resume(ts->dev);
+}
+#endif
+
+
+static int __devinit msm_ts_probe(struct platform_device *pdev)
+{
+	struct msm_ts_platform_data *pdata = pdev->dev.platform_data;
+	struct msm_ts *ts;
+	struct resource *tssc_res;
+	struct resource *irq1_res;
+	struct resource *irq2_res;
+	int err = 0;
+	int i;
+	struct marimba_tsadc_client *ts_client;
+
+	printk("%s\n", __func__);
+
+	tssc_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tssc");
+	irq1_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "tssc1");
+	irq2_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "tssc2");
+
+	if (!tssc_res || !irq1_res || !irq2_res) {
+		pr_err("%s: required resources not defined\n", __func__);
+		return -ENODEV;
+	}
+
+	if (pdata == NULL) {
+		pr_err("%s: missing platform_data\n", __func__);
+		return -ENODEV;
+	}
+
+	ts = kzalloc(sizeof(struct msm_ts), GFP_KERNEL);
+	if (ts == NULL) {
+		pr_err("%s: No memory for struct msm_ts\n", __func__);
+		return -ENOMEM;
+	}
+	ts->pdata = pdata;
+	ts->dev	  = &pdev->dev;
+
+	ts->sample_irq = irq1_res->start;
+	ts->pen_up_irq = irq2_res->start;
+
+	ts->tssc_base = ioremap(tssc_res->start, resource_size(tssc_res));
+	if (ts->tssc_base == NULL) {
+		pr_err("%s: Can't ioremap region (0x%08x - 0x%08x)\n", __func__,
+		       (uint32_t)tssc_res->start, (uint32_t)tssc_res->end);
+		err = -ENOMEM;
+		goto err_ioremap_tssc;
+	}
+
+	ts_client = marimba_tsadc_register(pdev, 1);
+	if (IS_ERR(ts_client)) {
+		pr_err("%s: Unable to register with TSADC\n", __func__);
+		err = -ENOMEM;
+		goto err_tsadc_register;
+	}
+	ts->ts_client = ts_client;
+
+	err = marimba_tsadc_start(ts_client);
+	if (err) {
+		pr_err("%s: Unable to start TSADC\n", __func__);
+		err = -EINVAL;
+		goto err_start_tsadc;
+	}
+
+	ts->input_dev = input_allocate_device();
+	if (ts->input_dev == NULL) {
+		pr_err("failed to allocate touchscreen input device\n");
+		err = -ENOMEM;
+		goto err_alloc_input_dev;
+	}
+	ts->input_dev->name = "msm-touchscreen";
+	ts->input_dev->dev.parent = &pdev->dev;
+
+	input_set_drvdata(ts->input_dev, ts);
+
+	input_set_capability(ts->input_dev, EV_KEY, BTN_TOUCH);
+	set_bit(EV_ABS, ts->input_dev->evbit);
+
+	input_set_abs_params(ts->input_dev, ABS_X, pdata->min_x, pdata->max_x,
+			     0, 0);
+	input_set_abs_params(ts->input_dev, ABS_Y, pdata->min_y, pdata->max_y,
+			     0, 0);
+	input_set_abs_params(ts->input_dev, ABS_PRESSURE, pdata->min_press,
+			     pdata->max_press, 0, 0);
+
+	for (i = 0; pdata->vkeys_x && (i < pdata->vkeys_x->num_keys); ++i)
+		input_set_capability(ts->input_dev, EV_KEY,
+				     pdata->vkeys_x->keys[i].key);
+	for (i = 0; pdata->vkeys_y && (i < pdata->vkeys_y->num_keys); ++i)
+		input_set_capability(ts->input_dev, EV_KEY,
+				     pdata->vkeys_y->keys[i].key);
+
+	err = input_register_device(ts->input_dev);
+	if (err != 0) {
+		pr_err("%s: failed to register input device\n", __func__);
+		goto err_input_dev_reg;
+	}
+
+	msm_ts_hw_init(ts);
+
+	err = request_irq(ts->sample_irq, msm_ts_irq,
+			  (irq1_res->flags & ~IORESOURCE_IRQ) | IRQF_DISABLED,
+			  "msm_touchscreen", ts);
+	if (err != 0) {
+		pr_err("%s: Cannot register irq1 (%d)\n", __func__, err);
+		goto err_request_irq1;
+	}
+
+	err = request_irq(ts->pen_up_irq, msm_ts_irq,
+			  (irq2_res->flags & ~IORESOURCE_IRQ) | IRQF_DISABLED,
+			  "msm_touchscreen", ts);
+	if (err != 0) {
+		pr_err("%s: Cannot register irq2 (%d)\n", __func__, err);
+		goto err_request_irq2;
+	}
+
+	platform_set_drvdata(pdev, ts);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN +
+						TSSC_SUSPEND_LEVEL;
+	ts->early_suspend.suspend = msm_ts_early_suspend;
+	ts->early_suspend.resume = msm_ts_late_resume;
+	register_early_suspend(&ts->early_suspend);
+#endif
+
+	device_init_wakeup(&pdev->dev, pdata->can_wakeup);
+	pr_info("%s: tssc_base=%p irq1=%d irq2=%d\n", __func__,
+		ts->tssc_base, (int)ts->sample_irq, (int)ts->pen_up_irq);
+	dump_tssc_regs(ts);
+	return 0;
+
+err_request_irq2:
+	free_irq(ts->sample_irq, ts);
+
+err_request_irq1:
+	/* disable the tssc */
+	tssc_writel(ts, TSSC_CTL_ENABLE, TSSC_CTL);
+
+err_input_dev_reg:
+	input_set_drvdata(ts->input_dev, NULL);
+	input_free_device(ts->input_dev);
+
+err_alloc_input_dev:
+err_start_tsadc:
+	marimba_tsadc_unregister(ts->ts_client);
+
+err_tsadc_register:
+	iounmap(ts->tssc_base);
+
+err_ioremap_tssc:
+	kfree(ts);
+	return err;
+}
+
+static int __devexit msm_ts_remove(struct platform_device *pdev)
+{
+	struct msm_ts *ts = platform_get_drvdata(pdev);
+
+	device_init_wakeup(&pdev->dev, 0);
+	marimba_tsadc_unregister(ts->ts_client);
+	free_irq(ts->sample_irq, ts);
+	free_irq(ts->pen_up_irq, ts);
+	input_unregister_device(ts->input_dev);
+	iounmap(ts->tssc_base);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	unregister_early_suspend(&ts->early_suspend);
+#endif
+	platform_set_drvdata(pdev, NULL);
+	kfree(ts);
+
+	return 0;
+}
+
+static struct platform_driver msm_touchscreen_driver = {
+	.driver = {
+		.name = "msm_touchscreen",
+		.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm = &msm_touchscreen_pm_ops,
+#endif
+	},
+	.probe		= msm_ts_probe,
+	.remove		= __devexit_p(msm_ts_remove),
+};
+
+static int __init msm_ts_init(void)
+{
+	return platform_driver_register(&msm_touchscreen_driver);
+}
+
+static void __exit msm_ts_exit(void)
+{
+	platform_driver_unregister(&msm_touchscreen_driver);
+}
+
+module_init(msm_ts_init);
+module_exit(msm_ts_exit);
+MODULE_DESCRIPTION("Qualcomm MSM/QSD Touchscreen controller driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:msm_touchscreen");
diff --git a/drivers/input/touchscreen/synaptics/Makefile b/drivers/input/touchscreen/synaptics/Makefile
new file mode 100644
index 0000000..32cbd76
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics/Makefile
@@ -0,0 +1,11 @@
+CFLAGS_rmi_bus.o := -DDEBUG
+CFLAGS_rmi_sensor.o := -DDEBUG
+CFLAGS_rmi_function.o := -DDEBUG
+CFLAGS_rmi_f01.o := -DDEBUG
+CFLAGS_rmi_f05.o := -DDEBUG
+CFLAGS_rmi_f11.o := -DDEBUG
+CFLAGS_rmi_f19.o := -DDEBUG
+CFLAGS_rmi_f34.o := -DDEBUG
+CFLAGS_rmi_i2c.o := -DDEBUG
+CFLAGS_rmi_spi.o := -DDEBUG
+obj-y +=  rmi_bus.o rmi_sensor.o rmi_function.o rmi_f01.o rmi_f05.o rmi_f11.o rmi_f19.o rmi_f34.o rmi_i2c.o
diff --git a/drivers/input/touchscreen/synaptics/rmi.h b/drivers/input/touchscreen/synaptics/rmi.h
new file mode 100644
index 0000000..7484258
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics/rmi.h
@@ -0,0 +1,164 @@
+/**
+ *
+ * Synaptics Register Mapped Interface (RMI4) Header File.
+ * Copyright (c) 2007 - 2011, Synaptics Incorporated
+ *
+ *
+ */
+/*
+ * This file is licensed under the GPL2 license.
+ *
+ *#############################################################################
+ * GPL
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ *#############################################################################
+ */
+
+#ifndef _RMI_H
+#define _RMI_H
+
+/*  RMI4 Protocol Support
+ */
+
+/* For each function present on the RMI device, we need to get the RMI4 Function
+ * Descriptor info from the Page Descriptor Table. This will give us the
+ * addresses for Query, Command, Control, Data and the Source Count (number
+ * of sources for this function) and the function id.
+ */
+struct rmi_function_descriptor {
+	unsigned char queryBaseAddr;
+	unsigned char commandBaseAddr;
+	unsigned char controlBaseAddr;
+	unsigned char dataBaseAddr;
+	unsigned char interruptSrcCnt;
+	unsigned char functionNum;
+};
+
+/*  This encapsulates the information found using the RMI4 Function $01
+ *  query registers. There is only one Function $01 per device.
+ *
+ *  Assuming appropriate endian-ness, you can populate most of this
+ *  structure by reading query registers starting at the query base address
+ *  that was obtained from RMI4 function 0x01 function descriptor info read
+ *  from the Page Descriptor Table.
+ *
+ *  Specific register information is provided in the comments for each field.
+ *  For further reference, please see the "Synaptics RMI 4 Interfacing
+ *  Guide" document : go to http://www.synaptics.com/developers/manuals - and
+ *  select "Synaptics RMI 4 Interfacting Guide".
+ */
+struct rmi_F01_query {
+	/* The manufacturer identification byte.*/
+	unsigned char mfgid;
+
+	/* The Product Properties information.*/
+	unsigned char properties;
+
+	/* The product info bytes.*/
+	unsigned char prod_info[2];
+
+	/* Date Code - Year, Month, Day.*/
+	unsigned char date_code[3];
+
+	/* Tester ID (14 bits).*/
+	unsigned short tester_id;
+
+	/* Serial Number (14 bits).*/
+	unsigned short serial_num;
+
+	/* A null-terminated string that identifies this particular product.*/
+	char prod_id[11];
+};
+
+/* This encapsulates the F01 Device Control control registers.
+ * TODO: This isn't right.  The number of interrupt enables needs to be determined
+ * dynamically as the sensor is initialized.  Fix this.
+ */
+struct rmi_F01_control {
+    unsigned char deviceControl;
+    unsigned char interruptEnable[1];
+};
+
+/** This encapsulates the F01 Device Control data registers.
+ * TODO: This isn't right.  The number of irqs needs to be determined
+ * dynamically as the sensor is initialized.  Fix this.
+ */
+struct rmi_F01_data {
+    unsigned char deviceStatus;
+    unsigned char irqs[1];
+};
+
+
+/**********************************************************/
+
+/** This is the data read from the F11 query registers.
+ */
+struct rmi_F11_device_query {
+    bool hasQuery9;
+    unsigned char numberOfSensors;
+};
+
+struct rmi_F11_sensor_query {
+    bool configurable;
+    bool hasSensitivityAdjust;
+    bool hasGestures;
+    bool hasAbs;
+    bool hasRel;
+    unsigned char numberOfFingers;
+    unsigned char numberOfXElectrodes;
+    unsigned char numberOfYElectrodes;
+    unsigned char maximumElectrodes;
+    bool hasAnchoredFinger;
+    unsigned char absDataSize;
+};
+
+struct rmi_F11_control {
+    bool relativeBallistics;
+    bool relativePositionFilter;
+    bool absolutePositionFilter;
+    unsigned char reportingMode;
+    bool manuallyTrackedFinger;
+    bool manuallyTrackedFingerEnable;
+    unsigned char motionSensitivity;
+    unsigned char palmDetectThreshold;
+    unsigned char deltaXPosThreshold;
+    unsigned char deltaYPosThreshold;
+    unsigned char velocity;
+    unsigned char acceleration;
+    unsigned short sensorMaxXPos;
+    unsigned short sensorMaxYPos;
+};
+
+
+/**********************************************************/
+
+/** This is the data read from the F19 query registers.
+ */
+struct rmi_F19_query {
+	bool hasHysteresisThreshold;
+	bool hasSensitivityAdjust;
+	bool configurable;
+	unsigned char buttonCount;
+};
+
+struct rmi_F19_control {
+	unsigned char buttonUsage;
+	unsigned char filterMode;
+	unsigned char *intEnableRegisters;
+	unsigned char *singleButtonControl;
+	unsigned char *sensorMap;
+	unsigned char *singleButtonSensitivity;
+	unsigned char globalSensitivityAdjustment;
+	unsigned char globalHysteresisThreshold;
+};
+
+#endif
diff --git a/drivers/input/touchscreen/synaptics/rmi_bus.c b/drivers/input/touchscreen/synaptics/rmi_bus.c
new file mode 100644
index 0000000..b65eabb
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics/rmi_bus.c
@@ -0,0 +1,402 @@
+/**
+ * Synaptics Register Mapped Interface (RMI4) - RMI Bus Module.
+ * Copyright (C) 2007 - 2011, Synaptics Incorporated
+ *
+ * Impliments "rmi" bus per Documentation/driver-model/bus.txt
+ *
+ * This protocol is layered as follows.
+ *
+ *
+ *
+ *  +-------+ +-------+ +-------+ +--------+
+ *  | Fn32  | |   Fn11| |  Fn19 | |  Fn11  |   Devices/Functions
+ *  *---|---+ +--|----+ +----|--+ +----|---*   (2D, cap. btns, etc.)
+ *      |        |           |         |
+ *  +----------------+      +----------------+
+ *  | Sensor0        |      |  Sensor1       | Sensors Dev/Drivers
+ *  +----------------+      +----------------+ (a sensor has one or
+ *          |                      |            more functions)
+ *          |                      |
+ *  +----------------------------------------+
+ *  |                                        |
+ *  |                RMI4 Bus                | RMI Bus Layer
+ *  |                (this file)             |
+ *  *--|-----|------|--------------|---------*
+ *     |     |      |              |
+ *     |     |      |              |
+ *  +-----+-----+-------+--------------------+
+ *  | I2C | SPI | SMBus |         etc.       | Physical Layer
+ *  +-----+-----+-------+--------------------+
+ *
+ */
+/*
+ * This file is licensed under the GPL2 license.
+ *
+ *#############################################################################
+ * GPL
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ *#############################################################################
+ */
+
+static const char busname[] = "rmi";
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/hrtimer.h>
+#include <linux/list.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/input/rmi_platformdata.h>
+#include <linux/module.h>
+
+#include "rmi_drvr.h"
+#include "rmi.h"
+#include "rmi_bus.h"
+#include "rmi_sensor.h"
+#include "rmi_function.h"
+
+/* list of physical drivers - i2c, spi, etc. */
+static LIST_HEAD(phys_drivers);
+static DEFINE_MUTEX(phys_drivers_mutex);
+
+/* list of sensors found on a physical bus (i2c, smi, etc.)*/
+static LIST_HEAD(sensor_drivers);
+static DEFINE_MUTEX(sensor_drivers_mutex);
+static LIST_HEAD(sensor_devices);
+static DEFINE_MUTEX(sensor_devices_mutex);
+
+#define PDT_START_SCAN_LOCATION 0x00E9
+#define PDT_END_SCAN_LOCATION 0x0005
+#define PDT_ENTRY_SIZE 0x0006
+
+/* definitions for rmi bus */
+struct device rmi_bus_device;
+
+struct bus_type rmi_bus_type;
+EXPORT_SYMBOL(rmi_bus_type);
+
+
+/*
+ * This method is called, perhaps multiple times, whenever a new device or driver
+ * is added for this bus. It should return a nonzero value if the given device can be
+ * handled by the given driver. This function must be handled at the bus level,
+ * because that is where the proper logic exists; the core kernel cannot know how
+ * to match devices and drivers for every possible bus type
+ * The match function does a comparison between the hardware ID provided by
+ * the device itself and the IDs supported by the driver.
+ *
+ */
+static int rmi_bus_match(struct device *dev, struct device_driver *driver)
+{
+	printk(KERN_DEBUG "%s: Matching %s for rmi bus.\n", __func__, dev->bus->name);
+	return !strncmp(dev->bus->name, driver->name, strlen(driver->name));
+}
+
+/** Stub for now.
+ */
+static int rmi_bus_suspend(struct device *dev, pm_message_t state)
+{
+	printk(KERN_INFO "%s: RMI bus suspending.", __func__);
+	return 0;
+}
+
+/** Stub for now.
+ */
+static int rmi_bus_resume(struct device *dev)
+{
+	printk(KERN_INFO "%s: RMI bus resuming.", __func__);
+	return 0;
+}
+
+/*
+ * This method is called, whenever a new device is added for this bus.
+ * It will scan the devices PDT to get the function $01 query, control,
+ * command and data regsiters so that it can create a function $01 (sensor)
+ * device for the new physical device. It also caches the PDT for later use by
+ * other functions that are created for the device. For example, if a function
+ * $11 is found it will need the query, control, command and data register
+ * addresses for that function. The new function could re-scan the PDT but
+ * since it is being done here we can cache it and keep it around.
+ *
+ * TODO: If the device is reset or some action takes place that would invalidate
+ * the PDT - such as a reflash of the firmware - then the device should be re-added
+ * to the bus and the PDT re-scanned and cached.
+ *
+ */
+int rmi_register_sensor(struct rmi_phys_driver *rpd, struct rmi_sensordata *sensordata)
+{
+	int i;
+	int pdt_entry_count = 0;
+	struct rmi_sensor_device *rmi_sensor_dev;
+	struct rmi_function_info *rfi;
+	struct rmi_function_descriptor rmi_fd;
+	int retval;
+	static int index;
+
+	/* Make sure we have a read, write, read_multiple, write_multiple
+	function pointers from whatever physical layer the sensor is on.
+	*/
+	if (!rpd->name) {
+		printk(KERN_ERR "%s: Physical driver must specify a name",
+			__func__);
+		return -EINVAL;
+	}
+	if (!rpd->write) {
+		printk(KERN_ERR
+			"%s: Physical driver %s must specify a writer.",
+			__func__, rpd->name);
+		return -EINVAL;
+	}
+	if (!rpd->read) {
+		printk(KERN_ERR
+			"%s: Physical driver %s must specify a reader.",
+			__func__, rpd->name);
+		return -EINVAL;
+	}
+	if (!rpd->write_multiple) {
+		printk(KERN_ERR "%s: Physical driver %s must specify a "
+			"multiple writer.",
+			__func__, rpd->name);
+		return -EINVAL;
+	}
+	if (!rpd->read_multiple) {
+		printk(KERN_ERR "%s: Physical driver %s must specify a "
+			"multiple reader.",
+			__func__, rpd->name);
+		return -EINVAL;
+	}
+
+	/* Get some information from the device */
+	printk(KERN_DEBUG "%s: Identifying sensors by presence of F01...", __func__);
+
+	rmi_sensor_dev = NULL;
+
+	/* Scan the page descriptor table until we find F01.  If we find that,
+	 * we assume that we can reliably talk to this sensor.
+	 */
+	for (i = PDT_START_SCAN_LOCATION;	/* Register the rmi sensor driver */
+			i >= PDT_END_SCAN_LOCATION;
+			i -= PDT_ENTRY_SIZE) {
+		retval = rpd->read_multiple(rpd, i, (char *)&rmi_fd,
+				sizeof(rmi_fd));
+		if (!retval) {
+			rfi = NULL;
+
+			if (rmi_fd.functionNum != 0x00 && rmi_fd.functionNum != 0xff) {
+				pdt_entry_count++;
+				if ((rmi_fd.functionNum & 0xff) == 0x01) {
+					printk(KERN_DEBUG "%s: F01 Found - RMI Device Control", __func__);
+
+					/* This appears to be a valid device, so create a sensor
+					* device and sensor driver for it. */
+					rmi_sensor_dev = kzalloc(sizeof(*rmi_sensor_dev), GFP_KERNEL);
+					if (!rmi_sensor_dev) {
+						printk(KERN_ERR "%s: Error allocating memory for rmi_sensor_device\n", __func__);
+						retval = -ENOMEM;
+						goto exit_fail;
+					}
+					rmi_sensor_dev->dev.bus = &rmi_bus_type;
+
+					retval = rmi_sensor_register_device(rmi_sensor_dev, index++);
+					if (retval < 0) {
+						printk(KERN_ERR "%s: Error %d registering sensor device.", __func__, retval);
+						goto exit_fail;
+					}
+
+					rmi_sensor_dev->driver = kzalloc(sizeof(struct rmi_sensor_driver), GFP_KERNEL);
+					if (!rmi_sensor_dev->driver) {
+						printk(KERN_ERR "%s: Error allocating memory for rmi_sensor_driver\n", __func__);
+						retval = -ENOMEM;
+						goto exit_fail;
+					}
+					rmi_sensor_dev->driver->sensor_device = rmi_sensor_dev;
+					rmi_sensor_dev->driver->polling_required = rpd->polling_required;
+					rmi_sensor_dev->driver->rpd = rpd;
+					if (sensordata)
+						rmi_sensor_dev->driver->perfunctiondata = sensordata->perfunctiondata;
+					INIT_LIST_HEAD(&rmi_sensor_dev->driver->functions);
+
+					retval = rmi_sensor_register_driver(rmi_sensor_dev->driver);
+					if (retval < 0) {
+						printk(KERN_ERR "%s: Error %d registering sensor driver.", __func__, retval);
+						goto exit_fail;
+					}
+
+					/* link the attention fn in the rpd to the sensor attn fn */
+
+					rpd->sensor = rmi_sensor_dev->driver;
+					rpd->attention = rmi_sensor_dev->driver->attention;
+
+					/* Add it into the list of sensors on the rmi bus */
+					mutex_lock(&sensor_devices_mutex);
+					list_add_tail(&rmi_sensor_dev->sensors, &sensor_devices);
+					mutex_unlock(&sensor_devices_mutex);
+
+					/* All done with this sensor, fall out of PDT scan loop. */
+					break;
+				} else {
+					/* Just print out the function found for now */
+					printk(KERN_DEBUG "%s: Found Function %02x - Ignored.\n", __func__, rmi_fd.functionNum & 0xff);
+				}
+			} else {
+				/* A zero or 0xff in the function number
+				signals the end of the PDT */
+				pr_debug("%s:   Found End of PDT.",
+					__func__);
+				break;
+			}
+		} else {
+			/* failed to read next PDT entry - end PDT
+			scan - this may result in an incomplete set
+			of recognized functions - should probably
+			return an error but the driver may still be
+			viable for diagnostics and debugging so let's
+			let it continue. */
+			printk(KERN_ERR "%s: Read Error %d when reading next PDT entry - "
+				"ending PDT scan.",
+				__func__, retval);
+			break;
+		}
+	}
+
+	/* If we actually found a sensor, keep it around. */
+	if (rmi_sensor_dev) {
+		/* Add physical driver struct to list */
+		mutex_lock(&phys_drivers_mutex);
+		list_add_tail(&rpd->drivers, &phys_drivers);
+		mutex_unlock(&phys_drivers_mutex);
+		printk(KERN_DEBUG "%s: Registered sensor drivers.", __func__);
+		retval = 0;
+	} else {
+		printk(KERN_ERR "%s: Failed to find sensor. PDT contained %d entries.", __func__, pdt_entry_count);
+		retval = -ENODEV;
+	}
+
+exit_fail:
+	return retval;
+}
+EXPORT_SYMBOL(rmi_register_sensor);
+
+int rmi_unregister_sensors(struct rmi_phys_driver *rpd)
+{
+	if (rpd->sensor) {
+		printk(KERN_WARNING "%s: WARNING: unregister of %s while %s still attached.",
+			__func__, rpd->name, rpd->sensor->drv.name);
+	}
+
+	pr_debug("%s: Unregistering sensor drivers %s\n", __func__, rpd->name);
+
+	/* TODO: We should call sensor_teardown() for each sensor before we get
+	 * rid of this list.
+	 */
+
+	mutex_lock(&sensor_drivers_mutex);
+	list_del(&rpd->sensor->sensor_drivers);
+	mutex_unlock(&sensor_drivers_mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL(rmi_unregister_sensors);
+
+
+static void rmi_bus_dev_release(struct device *dev)
+{
+	printk(KERN_DEBUG "rmi bus device release\n");
+}
+
+
+int rmi_register_bus_device(struct device *rmibusdev)
+{
+	printk(KERN_DEBUG "%s: Registering RMI4 bus device.\n", __func__);
+
+	/* Here, we simply fill in some of the embedded device structure fields
+	(which individual drivers should not need to know about), and register
+	the device with the driver core. */
+
+	rmibusdev->bus = &rmi_bus_type;
+	rmibusdev->parent = &rmi_bus_device;
+	rmibusdev->release = rmi_bus_dev_release;
+	dev_set_name(rmibusdev, "rmi");
+
+	/* If we wanted to add bus-specific attributes to the device, we could do so here.*/
+
+	return device_register(rmibusdev);
+}
+EXPORT_SYMBOL(rmi_register_bus_device);
+
+void rmi_unregister_bus_device(struct device *rmibusdev)
+{
+	printk(KERN_DEBUG "%s: Unregistering bus device.", __func__);
+
+	device_unregister(rmibusdev);
+}
+EXPORT_SYMBOL(rmi_unregister_bus_device);
+
+static int __init rmi_bus_init(void)
+{
+	int status;
+
+	status = 0;
+
+	printk(KERN_INFO "%s: RMI Bus Driver Init", __func__);
+
+	/* Register the rmi bus */
+	rmi_bus_type.name = busname;
+	rmi_bus_type.match = rmi_bus_match;
+	rmi_bus_type.suspend = rmi_bus_suspend;
+	rmi_bus_type.resume = rmi_bus_resume;
+	status = bus_register(&rmi_bus_type);
+	if (status < 0) {
+		printk(KERN_ERR "%s: Error %d registering the rmi bus.", __func__, status);
+		goto err_exit;
+	}
+	printk(KERN_DEBUG "%s: registered bus.", __func__);
+
+#if 0
+	/** This doesn't seem to be required any more.  It worked OK in Froyo,
+	 * but breaks in Gingerbread */
+	/* Register the rmi bus device - "rmi". There is only one rmi bus device. */
+	status = rmi_register_bus_device(&rmi_bus_device);
+	if (status < 0) {
+		printk(KERN_ERR "%s: Error %d registering rmi bus device.", __func__, status);
+		bus_unregister(&rmi_bus_type);
+		goto err_exit;
+	}
+	printk(KERN_DEBUG "%s: Registered bus device.", __func__);
+#endif
+
+	return 0;
+err_exit:
+	return status;
+}
+
+static void __exit rmi_bus_exit(void)
+{
+	printk(KERN_DEBUG "%s: RMI Bus Driver Exit.", __func__);
+
+	/* Unregister the rmi bus device - "rmi". There is only one rmi bus device. */
+	rmi_unregister_bus_device(&rmi_bus_device);
+
+	/* Unregister the rmi bus */
+	bus_unregister(&rmi_bus_type);
+}
+
+
+module_init(rmi_bus_init);
+module_exit(rmi_bus_exit);
+
+MODULE_AUTHOR("Synaptics, Inc.");
+MODULE_DESCRIPTION("RMI4 Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/synaptics/rmi_bus.h b/drivers/input/touchscreen/synaptics/rmi_bus.h
new file mode 100644
index 0000000..1e9bd24
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics/rmi_bus.h
@@ -0,0 +1,32 @@
+/**
+ *
+ * Synaptics Register Mapped Interface (RMI4) - RMI Bus Module Header.
+ * Copyright (C) 2007 - 2010, Synaptics Incorporated
+ *
+ */
+/*
+ *
+ * This file is licensed under the GPL2 license.
+ *
+ *#############################################################################
+ * GPL
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ *#############################################################################
+ */
+
+#ifndef _RMI_BUS_H
+#define _RMI_BUS_H
+
+
+extern struct bus_type rmi_bus_type;
+
+#endif
diff --git a/drivers/input/touchscreen/synaptics/rmi_drvr.h b/drivers/input/touchscreen/synaptics/rmi_drvr.h
new file mode 100644
index 0000000..d8c848d
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics/rmi_drvr.h
@@ -0,0 +1,104 @@
+/**
+ *
+ * Synaptics Register Mapped Interface (RMI4) RMI Driver Header File.
+ * Copyright (c) 2007 - 2011, Synaptics Incorporated
+ *
+ *
+ */
+/*
+ * This file is licensed under the GPL2 license.
+ *
+ *#############################################################################
+ * GPL
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ *#############################################################################
+ */
+
+#include "rmi.h"
+
+#ifndef _RMI_DRVR_H
+#define _RMI_DRVR_H
+
+#include <linux/input/rmi_platformdata.h>
+
+/*  RMI4 Protocol Support
+ */
+
+struct rmi_phys_driver {
+	char *name;
+	int (*write)(struct rmi_phys_driver *physdrvr, unsigned short address,
+			char data);
+	int (*read)(struct rmi_phys_driver *physdrvr, unsigned short address,
+			char *buffer);
+	int (*write_multiple)(struct rmi_phys_driver *physdrvr,
+			unsigned short address, char *buffer, int length);
+	int (*read_multiple)(struct rmi_phys_driver *physdrvr, unsigned short address,
+			char *buffer, int length);
+	void (*attention)(struct rmi_phys_driver *physdrvr, int instance);
+	bool polling_required;
+	int irq;
+
+	/* Standard kernel linked list implementation.
+	*  Documentation on how to use it can be found at
+	*  http://isis.poly.edu/kulesh/stuff/src/klist/.
+	*/
+	struct list_head drivers;
+	struct rmi_sensor_driver *sensor;
+	struct module *module;
+};
+
+int rmi_read(struct rmi_sensor_driver *sensor, unsigned short address, char *dest);
+int rmi_write(struct rmi_sensor_driver *sensor, unsigned short address,
+		unsigned char data);
+int rmi_read_multiple(struct rmi_sensor_driver *sensor, unsigned short address,
+		char *dest, int length);
+int rmi_write_multiple(struct rmi_sensor_driver *sensor, unsigned short address,
+		unsigned char *data, int length);
+int rmi_register_sensor(struct rmi_phys_driver *physdrvr,
+						 struct rmi_sensordata *sensordata);
+int rmi_unregister_sensors(struct rmi_phys_driver *physdrvr);
+
+/* Utility routine to set bits in a register. */
+int rmi_set_bits(struct rmi_sensor_driver *sensor, unsigned short address, unsigned char bits);
+/* Utility routine to clear bits in a register. */
+int rmi_clear_bits(struct rmi_sensor_driver *sensor, unsigned short address, unsigned char bits);
+/* Utility routine to set the value of a bit field in a register. */
+int rmi_set_bit_field(struct rmi_sensor_driver *sensor, unsigned short address,
+					  unsigned char field_mask, unsigned char bits);
+
+/* Set this to 1 to turn on code used in detecting buffer leaks. */
+#define RMI_ALLOC_STATS 1
+
+#if RMI_ALLOC_STATS
+extern int appallocsrmi;
+extern int rfiallocsrmi;
+extern int fnallocsrmi;
+
+#define INC_ALLOC_STAT(X)   (X##allocsrmi++)
+#define DEC_ALLOC_STAT(X)   \
+	do { \
+		if (X##allocsrmi) X##allocsrmi--; \
+		else printk(KERN_DEBUG "Too many " #X " frees\n"); \
+	} while (0)
+#define CHECK_ALLOC_STAT(X) \
+	do { \
+		if (X##allocsrmi) \
+			printk(KERN_DEBUG "Left over " #X " buffers: %d\n", \
+					X##allocsrmi); \
+	} while (0)
+#else
+#define INC_ALLOC_STAT(X) do { } while (0)
+#define DEC_ALLOC_STAT(X) do { } while (0)
+#define CHECK_ALLOC_STAT(X) do { } while (0)
+#endif
+
+#endif
diff --git a/drivers/input/touchscreen/synaptics/rmi_f01.c b/drivers/input/touchscreen/synaptics/rmi_f01.c
new file mode 100644
index 0000000..25a337d
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics/rmi_f01.c
@@ -0,0 +1,603 @@
+/**
+ *
+ * Synaptics Register Mapped Interface (RMI4) Function $01 support for sensor
+ * control and configuration.
+ *
+ * Copyright (c) 2007 - 2011, Synaptics Incorporated
+ *
+ */
+/*
+ * This file is licensed under the GPL2 license.
+ *
+ *#############################################################################
+ * GPL
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ *#############################################################################
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/param.h>
+#include <linux/input/rmi_platformdata.h>
+#include <linux/module.h>
+
+#include "rmi.h"
+#include "rmi_drvr.h"
+#include "rmi_bus.h"
+#include "rmi_sensor.h"
+#include "rmi_function.h"
+#include "rmi_f01.h"
+
+/* Control register bits. */
+#define F01_CONFIGURED (1 << 7)
+#define NONSTANDARD_REPORT_RATE (1 << 6)
+
+/* Command register bits. */
+#define F01_RESET 1
+#define F01_SHUTDOWN (1 << 1)
+
+/* Data register 0 bits. */
+#define F01_UNCONFIGURED (1 << 7)
+#define F01_FLASH_PROGRAMMING_MODE (1 << 6)
+#define F01_STATUS_MASK 0x0F
+
+/** Context data for each F01 we find.
+ */
+struct f01_instance_data {
+	struct rmi_F01_control *controlRegisters;
+	struct rmi_F01_data *dataRegisters;
+	struct rmi_F01_query *query_registers;
+
+	bool nonstandard_report_rate;
+};
+
+static ssize_t rmi_fn_01_productinfo_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_01_productinfo_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count);
+
+DEVICE_ATTR(productinfo, 0444, rmi_fn_01_productinfo_show, rmi_fn_01_productinfo_store);     /* RO attr */
+
+static ssize_t rmi_fn_01_productid_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_01_productid_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count);
+
+DEVICE_ATTR(productid, 0444, rmi_fn_01_productid_show, rmi_fn_01_productid_store);     /* RO attr */
+
+static ssize_t rmi_fn_01_manufacturer_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_01_manufacturer_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count);
+
+DEVICE_ATTR(manufacturer, 0444, rmi_fn_01_manufacturer_show, rmi_fn_01_manufacturer_store);     /* RO attr */
+
+static ssize_t rmi_fn_01_datecode_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_01_datecode_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count);
+
+DEVICE_ATTR(datecode, 0444, rmi_fn_01_datecode_show, rmi_fn_01_datecode_store);     /* RO attr */
+
+static ssize_t rmi_fn_01_reportrate_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_01_reportrate_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count);
+
+DEVICE_ATTR(reportrate, 0644, rmi_fn_01_reportrate_show, rmi_fn_01_reportrate_store);     /* RW attr */
+
+static ssize_t rmi_fn_01_reset_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_01_reset_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count);
+
+DEVICE_ATTR(reset, 0200, rmi_fn_01_reset_show, rmi_fn_01_reset_store);     /* WO attr */
+
+static ssize_t rmi_fn_01_testerid_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_01_testerid_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count);
+
+DEVICE_ATTR(testerid, 0444, rmi_fn_01_testerid_show, rmi_fn_01_testerid_store);     /* RO attr */
+
+static ssize_t rmi_fn_01_serialnumber_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_01_serialnumber_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count);
+
+DEVICE_ATTR(serialnumber, 0444, rmi_fn_01_serialnumber_show, rmi_fn_01_serialnumber_store);     /* RO attr */
+
+static int set_report_rate(struct rmi_function_info *function_info, bool nonstandard)
+{
+	if (nonstandard) {
+		return rmi_set_bits(function_info->sensor, function_info->funcDescriptor.controlBaseAddr, NONSTANDARD_REPORT_RATE);
+	} else {
+		return rmi_set_bits(function_info->sensor, function_info->funcDescriptor.controlBaseAddr, NONSTANDARD_REPORT_RATE);
+	}
+}
+
+/*.
+ * The interrupt handler for Fn $01 doesn't do anything (for now).
+ */
+void FN_01_inthandler(struct rmi_function_info *rmifninfo,
+	unsigned int assertedIRQs)
+{
+	struct f01_instance_data *instanceData = (struct f01_instance_data *) rmifninfo->fndata;
+
+	printk(KERN_DEBUG "%s: Read device status.", __func__);
+
+	if (rmi_read_multiple(rmifninfo->sensor, rmifninfo->funcDescriptor.dataBaseAddr,
+		&instanceData->dataRegisters->deviceStatus, 1)) {
+		printk(KERN_ERR "%s : Could not read F01 device status.\n",
+			__func__);
+	}
+	printk(KERN_INFO "%s: read device status register.  Value 0x%02X.", __func__, instanceData->dataRegisters->deviceStatus);
+
+	if (instanceData->dataRegisters->deviceStatus & F01_UNCONFIGURED) {
+		printk(KERN_INFO "%s: ++++ Device reset detected.", __func__);
+		/* TODO: Handle device reset appropriately.
+		*/
+	}
+}
+EXPORT_SYMBOL(FN_01_inthandler);
+
+/*
+ * This reads in the function $01 source data.
+ *
+ */
+void FN_01_attention(struct rmi_function_info *rmifninfo)
+{
+	struct f01_instance_data *instanceData = (struct f01_instance_data *) rmifninfo->fndata;
+
+	/* TODO: Compute size to read and number of IRQ registers to processors
+	* dynamically.  See comments in rmi.h. */
+	if (rmi_read_multiple(rmifninfo->sensor, rmifninfo->funcDescriptor.dataBaseAddr+1,
+		instanceData->dataRegisters->irqs, 1)) {
+		printk(KERN_ERR "%s : Could not read interrupt status registers at 0x%02x\n",
+			__func__, rmifninfo->funcDescriptor.dataBaseAddr);
+		return;
+	}
+
+	if (instanceData->dataRegisters->irqs[0] & instanceData->controlRegisters->interruptEnable[0]) {
+//		printk(KERN_INFO "%s: ++++ IRQs == 0x%02X", __func__, instanceData->dataRegisters->irqs[0]);
+		/* call down to the sensors irq dispatcher to dispatch all enabled IRQs */
+		rmifninfo->sensor->dispatchIRQs(rmifninfo->sensor,
+			instanceData->dataRegisters->irqs[0]);
+	}
+
+}
+EXPORT_SYMBOL(FN_01_attention);
+
+int FN_01_config(struct rmi_function_info *rmifninfo)
+{
+	int retval = 0;
+	struct f01_instance_data *instance_data = rmifninfo->fndata;
+
+	printk(KERN_DEBUG "%s: RMI4 function $01 config\n", __func__);
+
+	/* First thing to do is set the configuration bit.  We'll check this at
+	 * the end to determine if the device has reset during the config process.
+	 */
+	retval = rmi_set_bits(rmifninfo->sensor, rmifninfo->funcDescriptor.controlBaseAddr, F01_CONFIGURED);
+	if (retval)
+		printk(KERN_WARNING "%s: failed to set configured bit, errno = %d.",
+				__func__, retval);
+
+	/* At config time, the device is presumably in its default state, so we
+	 * only need to write non-default configuration settings.
+	 */
+	if (instance_data->nonstandard_report_rate) {
+		retval = set_report_rate(rmifninfo, true);
+		if (!retval)
+			printk(KERN_WARNING "%s: failed to configure report rate, errno = %d.",
+					__func__, retval);
+	}
+
+	/* TODO: Check for reset! */
+
+	return retval;
+}
+EXPORT_SYMBOL(FN_01_config);
+
+/* Initialize any function $01 specific params and settings - input
+ * settings, device settings, etc.
+ */
+int FN_01_init(struct rmi_function_device *function_device)
+{
+	int retval;
+	struct rmi_f01_functiondata *functiondata = rmi_sensor_get_functiondata(function_device->sensor, RMI_F01_INDEX);
+	struct f01_instance_data *instance_data = function_device->rfi->fndata;
+
+	pr_debug("%s: RMI4 function $01 init\n", __func__);
+
+	if (functiondata) {
+		instance_data->nonstandard_report_rate = functiondata->nonstandard_report_rate;
+	}
+
+	retval = device_create_file(&function_device->dev, &dev_attr_productinfo);
+	if (retval) {
+		printk(KERN_ERR "%s: Failed to create productinfo.", __func__);
+		return retval;
+	}
+	retval = device_create_file(&function_device->dev, &dev_attr_productid);
+	if (retval) {
+		printk(KERN_ERR "%s: Failed to create productid.", __func__);
+		return retval;
+	}
+	retval = device_create_file(&function_device->dev, &dev_attr_manufacturer);
+	if (retval) {
+		printk(KERN_ERR "%s: Failed to create manufacturer.", __func__);
+		return retval;
+	}
+	retval = device_create_file(&function_device->dev, &dev_attr_datecode);
+	if (retval) {
+		printk(KERN_ERR "%s: Failed to create datecode.", __func__);
+		return retval;
+	}
+	retval = device_create_file(&function_device->dev, &dev_attr_reportrate);
+	if (retval) {
+		printk(KERN_ERR "%s: Failed to create reportrate.", __func__);
+		return retval;
+	}
+	retval = device_create_file(&function_device->dev, &dev_attr_reset);
+	if (retval) {
+		printk(KERN_ERR "%s: Failed to create reset.", __func__);
+		return retval;
+	}
+	retval = device_create_file(&function_device->dev, &dev_attr_serialnumber);
+	if (retval) {
+		printk(KERN_ERR "%s: Failed to create serialnumber.", __func__);
+		return retval;
+	}
+	retval = device_create_file(&function_device->dev, &dev_attr_testerid);
+	if (retval) {
+		printk(KERN_ERR "%s: Failed to create testerid.", __func__);
+		return retval;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(FN_01_init);
+
+int FN_01_detect(struct rmi_function_info *rmifninfo,
+	struct rmi_function_descriptor *fndescr, unsigned int interruptCount)
+{
+	int i;
+	int InterruptOffset;
+	int retval = 0;
+	struct f01_instance_data *instanceData = NULL;
+	struct rmi_F01_control *controlRegisters = NULL;
+	struct rmi_F01_data *dataRegisters = NULL;
+	struct rmi_F01_query *query_registers = NULL;
+	unsigned char query_buffer[21];
+
+	pr_debug("%s: RMI4 function $01 detect\n", __func__);
+
+	/* Store addresses - used elsewhere to read data,
+	* control, query, etc. */
+	rmifninfo->funcDescriptor.queryBaseAddr = fndescr->queryBaseAddr;
+	rmifninfo->funcDescriptor.commandBaseAddr = fndescr->commandBaseAddr;
+	rmifninfo->funcDescriptor.controlBaseAddr = fndescr->controlBaseAddr;
+	rmifninfo->funcDescriptor.dataBaseAddr = fndescr->dataBaseAddr;
+	rmifninfo->funcDescriptor.interruptSrcCnt = fndescr->interruptSrcCnt;
+	rmifninfo->funcDescriptor.functionNum = fndescr->functionNum;
+
+	rmifninfo->numSources = fndescr->interruptSrcCnt;
+
+	/* Set up context data. */
+	instanceData = kzalloc(sizeof(*instanceData), GFP_KERNEL);
+	if (!instanceData) {
+		printk(KERN_ERR "%s: Error allocating memory for F01 context data.\n", __func__);
+		retval = -ENOMEM;
+		goto error_exit;
+	}
+	query_registers = kzalloc(sizeof(*query_registers), GFP_KERNEL);
+	if (!query_registers) {
+		printk(KERN_ERR "%s: Error allocating memory for F01 query registers.\n", __func__);
+		retval = -ENOMEM;
+		goto error_exit;
+	}
+	instanceData->query_registers = query_registers;
+
+	/* Read the query info and unpack it. */
+	retval = rmi_read_multiple(rmifninfo->sensor, rmifninfo->funcDescriptor.queryBaseAddr,
+		query_buffer, 21);
+	if (retval) {
+		printk(KERN_ERR "%s : Could not read F01 query registers at 0x%02x. Error %d.\n",
+			__func__, rmifninfo->funcDescriptor.queryBaseAddr, retval);
+		/* Presumably if the read fails, the buffer should be all zeros, so we're OK to continue. */
+	}
+	query_registers->mfgid = query_buffer[0];
+	query_registers->properties = query_buffer[1];
+	query_registers->prod_info[0] = query_buffer[2] & 0x7F;
+	query_registers->prod_info[1] = query_buffer[3] & 0x7F;
+	query_registers->date_code[0] = query_buffer[4] & 0x1F;
+	query_registers->date_code[1] = query_buffer[5] & 0x0F;
+	query_registers->date_code[2] = query_buffer[6] & 0x1F;
+	query_registers->tester_id = (((unsigned short) query_buffer[7] & 0x7F) << 7) | (query_buffer[8] & 0x7F);
+	query_registers->serial_num = (((unsigned short) query_buffer[9] & 0x7F) << 7) | (query_buffer[10] & 0x7F);
+	memcpy(query_registers->prod_id, &query_buffer[11], 10);
+
+	printk(KERN_DEBUG "%s: RMI4 Protocol Function $01 Query information, rmifninfo->funcDescriptor.queryBaseAddr = %d\n", __func__, rmifninfo->funcDescriptor.queryBaseAddr);
+	printk(KERN_DEBUG "%s: Manufacturer ID: %d %s\n", __func__,
+		query_registers->mfgid, query_registers->mfgid == 1 ? "(Synaptics)" : "");
+	printk(KERN_DEBUG "%s: Product Properties: 0x%x\n",
+		__func__, query_registers->properties);
+	printk(KERN_DEBUG "%s: Product Info: 0x%x 0x%x\n",
+		__func__, query_registers->prod_info[0], query_registers->prod_info[1]);
+	printk(KERN_DEBUG "%s: Date Code: Year : %d Month: %d Day: %d\n",
+		__func__, query_registers->date_code[0], query_registers->date_code[1],
+		query_registers->date_code[2]);
+	printk(KERN_DEBUG "%s: Tester ID: %d\n", __func__, query_registers->tester_id);
+	printk(KERN_DEBUG "%s: Serial Number: 0x%x\n",
+		__func__, query_registers->serial_num);
+	printk(KERN_DEBUG "%s: Product ID: %s\n", __func__, query_registers->prod_id);
+
+	/* TODO: size of control registers needs to be computed dynamically.  See comment
+	* in rmi.h. */
+	controlRegisters = kzalloc(sizeof(*controlRegisters), GFP_KERNEL);
+	if (!controlRegisters) {
+		printk(KERN_ERR "%s: Error allocating memory for F01 control registers.\n", __func__);
+		retval = -ENOMEM;
+		goto error_exit;
+	}
+	instanceData->controlRegisters = controlRegisters;
+	retval = rmi_read_multiple(rmifninfo->sensor, rmifninfo->funcDescriptor.controlBaseAddr,
+		(char *)instanceData->controlRegisters, sizeof(struct rmi_F01_control));
+	if (retval) {
+		printk(KERN_ERR "%s : Could not read F01 control registers at 0x%02x. Error %d.\n",
+			__func__, rmifninfo->funcDescriptor.controlBaseAddr, retval);
+	}
+
+	/* TODO: size of data registers needs to be computed dynamically.  See comment
+	 * in rmi.h. */
+	dataRegisters = kzalloc(sizeof(*dataRegisters), GFP_KERNEL);
+	if (!dataRegisters) {
+		printk(KERN_ERR "%s: Error allocating memory for F01 data registers.\n", __func__);
+		retval = -ENOMEM;
+		goto error_exit;
+	}
+	instanceData->dataRegisters = dataRegisters;
+	rmifninfo->fndata = instanceData;
+
+	/* Need to get interrupt info to be used later when handling
+	 * interrupts. */
+	rmifninfo->interruptRegister = interruptCount/8;
+
+	/* loop through interrupts for each source and or in a bit
+	 * to the interrupt mask for each. */
+	InterruptOffset = interruptCount % 8;
+
+	for (i = InterruptOffset;
+		i < ((fndescr->interruptSrcCnt & 0x7) + InterruptOffset);
+		i++) {
+			rmifninfo->interruptMask |= 1 << i;
+	}
+
+	return retval;
+
+error_exit:
+	kfree(instanceData);
+	kfree(query_registers);
+	kfree(controlRegisters);
+	kfree(dataRegisters);
+	return retval;
+}
+EXPORT_SYMBOL(FN_01_detect);
+
+static ssize_t rmi_fn_01_productinfo_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f01_instance_data *instance_data = (struct f01_instance_data *)fn->rfi->fndata;
+
+	if (instance_data && instance_data->query_registers && instance_data->query_registers->prod_info)
+		return sprintf(buf, "0x%02X 0x%02X\n", instance_data->query_registers->prod_info[0], instance_data->query_registers->prod_info[1]);
+
+	return sprintf(buf, "unknown");
+}
+
+static ssize_t rmi_fn_01_productinfo_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	return -EPERM;
+}
+
+
+static ssize_t rmi_fn_01_productid_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f01_instance_data *instance_data = (struct f01_instance_data *)fn->rfi->fndata;
+
+	if (instance_data && instance_data->query_registers && instance_data->query_registers->prod_id)
+		return sprintf(buf, "%s\n", instance_data->query_registers->prod_id);
+
+	return sprintf(buf, "unknown");
+}
+
+static ssize_t rmi_fn_01_productid_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	return -EPERM;
+}
+
+static ssize_t rmi_fn_01_manufacturer_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f01_instance_data *instance_data = (struct f01_instance_data *)fn->rfi->fndata;
+
+	if (instance_data && instance_data->query_registers)
+		return sprintf(buf, "0x%02X\n", instance_data->query_registers->mfgid);
+
+	return sprintf(buf, "unknown");
+}
+
+static ssize_t rmi_fn_01_manufacturer_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	return -EPERM;
+}
+
+static ssize_t rmi_fn_01_datecode_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f01_instance_data *instance_data = (struct f01_instance_data *)fn->rfi->fndata;
+
+	if (instance_data && instance_data->query_registers && instance_data->query_registers->date_code)
+		return sprintf(buf, "20%02u-%02u-%02u\n", instance_data->query_registers->date_code[0], instance_data->query_registers->date_code[1], instance_data->query_registers->date_code[2]);
+
+	return sprintf(buf, "unknown");
+}
+
+static ssize_t rmi_fn_01_datecode_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	return -EPERM;
+}
+
+static ssize_t rmi_fn_01_reportrate_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f01_instance_data *instance_data = (struct f01_instance_data *)fn->rfi->fndata;
+
+	if (instance_data && instance_data->query_registers && instance_data->query_registers->date_code)
+		return sprintf(buf, "%d\n", instance_data->nonstandard_report_rate);
+
+	return sprintf(buf, "unknown");
+}
+
+static ssize_t rmi_fn_01_reportrate_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f01_instance_data *instance_data = (struct f01_instance_data *)fn->rfi->fndata;
+	unsigned int new_rate;
+	int retval;
+
+	printk(KERN_DEBUG "%s: Report rate set to %s", __func__, buf);
+
+	if (sscanf(buf, "%u", &new_rate) != 1)
+		return -EINVAL;
+	if (new_rate < 0 || new_rate > 1)
+		return -EINVAL;
+	instance_data->nonstandard_report_rate = new_rate;
+
+	retval = set_report_rate(fn->rfi, new_rate);
+	if (retval < 0) {
+		printk(KERN_ERR "%s: failed to set report rate bit, error = %d.", __func__, retval);
+		return retval;
+	}
+
+	return count;
+}
+
+static ssize_t rmi_fn_01_reset_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	return -EPERM;
+}
+
+static ssize_t rmi_fn_01_reset_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	unsigned int reset;
+	int retval;
+
+	printk(KERN_INFO "%s: Reset written with %s", __func__, buf);
+
+	if (sscanf(buf, "%u", &reset) != 1)
+		return -EINVAL;
+	if (reset < 0 || reset > 1)
+		return -EINVAL;
+
+	/* Per spec, 0 has no effect, so we skip it entirely. */
+	if (reset) {
+		retval = rmi_set_bits(fn->sensor, fn->rfi->funcDescriptor.commandBaseAddr, F01_RESET);
+		if (retval < 0) {
+			printk(KERN_ERR "%s: failed to issue reset command, error = %d.", __func__, retval);
+			return retval;
+		}
+	}
+
+	return count;
+}
+
+static ssize_t rmi_fn_01_serialnumber_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f01_instance_data *instance_data = (struct f01_instance_data *)fn->rfi->fndata;
+
+	if (instance_data && instance_data->query_registers)
+		return sprintf(buf, "%u\n", instance_data->query_registers->serial_num);
+
+	return sprintf(buf, "unknown");
+}
+
+static ssize_t rmi_fn_01_serialnumber_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	return -EPERM;
+}
+
+static ssize_t rmi_fn_01_testerid_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f01_instance_data *instance_data = (struct f01_instance_data *)fn->rfi->fndata;
+
+	if (instance_data && instance_data->query_registers)
+		return sprintf(buf, "%u\n", instance_data->query_registers->tester_id);
+
+	return sprintf(buf, "unknown");
+}
+
+static ssize_t rmi_fn_01_testerid_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	return -EPERM;
+}
diff --git a/drivers/input/touchscreen/synaptics/rmi_f01.h b/drivers/input/touchscreen/synaptics/rmi_f01.h
new file mode 100644
index 0000000..976e062
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics/rmi_f01.h
@@ -0,0 +1,40 @@
+/**
+ *
+ * Synaptics Register Mapped Interface (RMI4) Function $01 header.
+ * Copyright (c) 2007 - 2011, Synaptics Incorporated
+ *
+ * There is only one function $01 for each RMI4 sensor. This will be
+ * the function that is used to set sensor control and configurations
+ * and check the interrupts to find the source function that is interrupting.
+ *
+ *
+ */
+/*
+ * This file is licensed under the GPL2 license.
+ *
+ *#############################################################################
+ * GPL
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ *#############################################################################
+ */
+#ifndef _RMI_FUNCTION_01_H
+#define _RMI_FUNCTION_01_H
+
+void FN_01_inthandler(struct rmi_function_info *rmifninfo,
+	unsigned int assertedIRQs);
+int FN_01_config(struct rmi_function_info *rmifninfo);
+int FN_01_init(struct rmi_function_device *function_device);
+int FN_01_detect(struct rmi_function_info *rmifninfo,
+		struct rmi_function_descriptor *fndescr,
+		unsigned int interruptCount);
+void FN_01_attention(struct rmi_function_info *rmifninfo);
+#endif
diff --git a/drivers/input/touchscreen/synaptics/rmi_f05.c b/drivers/input/touchscreen/synaptics/rmi_f05.c
new file mode 100644
index 0000000..4f209e7
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics/rmi_f05.c
@@ -0,0 +1,137 @@
+/**
+ *
+ * Synaptics Register Mapped Interface (RMI4) Function $11 support for 2D.
+ * Copyright (c) 2007 - 2011, Synaptics Incorporated
+ *
+ */
+/*
+ * This file is licensed under the GPL2 license.
+ *
+ *#############################################################################
+ * GPL
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ *#############################################################################
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/input/rmi_platformdata.h>
+#include <linux/module.h>
+
+#include "rmi.h"
+#include "rmi_drvr.h"
+#include "rmi_bus.h"
+#include "rmi_sensor.h"
+#include "rmi_function.h"
+#include "rmi_f05.h"
+
+struct f05_instance_data {
+	int dummy;	/* TODO: Write this */
+};
+
+/*
+ * There is no attention function for F05 - it is left NULL
+ * in the function table so it is not called.
+ *
+ */
+
+
+/*
+ * This reads in a sample and reports the F05 source data to the
+ * input subsystem. It is used for both polling and interrupt driven
+ * operation. This is called a lot so don't put in any informational
+ * printks since they will slow things way down!
+ */
+void FN_05_inthandler(struct rmi_function_info *rmifninfo,
+	unsigned int assertedIRQs)
+{
+//	struct f05_instance_data *instance_data = rmifninfo->fndata;
+}
+EXPORT_SYMBOL(FN_05_inthandler);
+
+int FN_05_config(struct rmi_function_info *rmifninfo)
+{
+	int retval = 0;
+
+	pr_debug("%s: RMI4 F05 config\n", __func__);
+
+	/* TODO: Perform configuration.  In particular, write any cached control
+	 * register values to the device. */
+
+	return retval;
+}
+EXPORT_SYMBOL(FN_05_config);
+
+/* Initialize any F05 specific params and settings - input
+ * settings, device settings, etc.
+ */
+int FN_05_init(struct rmi_function_device *function_device)
+{
+	int retval = 0;
+//	struct f05_instance_data *instance_data = function_device->rfi->fndata;
+//	struct rmi_f05_functiondata *functiondata = rmi_sensor_get_functiondata(function_device->sensor, RMI_F05_INDEX);
+
+	printk(KERN_DEBUG "%s: RMI4 F05 init\n", __func__);
+
+	return retval;
+}
+EXPORT_SYMBOL(FN_05_init);
+
+
+int FN_05_detect(struct rmi_function_info *rmifninfo,
+	struct rmi_function_descriptor *fndescr, unsigned int interruptCount)
+{
+	int retval = 0;
+	int i;
+	struct f05_instance_data *instanceData;
+	int fn05InterruptOffset;
+
+	printk(KERN_DEBUG "%s: RMI4 F05 detect\n", __func__);
+
+	instanceData = kzalloc(sizeof(struct f05_instance_data), GFP_KERNEL);
+	if (!instanceData) {
+		printk(KERN_ERR "%s: Error allocating F05 instance data.\n", __func__);
+		return -ENOMEM;
+	}
+	rmifninfo->fndata = instanceData;
+
+	/* Store addresses - used elsewhere to read data,
+	* control, query, etc. */
+	rmifninfo->funcDescriptor.queryBaseAddr = fndescr->queryBaseAddr;
+	rmifninfo->funcDescriptor.commandBaseAddr = fndescr->commandBaseAddr;
+	rmifninfo->funcDescriptor.controlBaseAddr = fndescr->controlBaseAddr;
+	rmifninfo->funcDescriptor.dataBaseAddr = fndescr->dataBaseAddr;
+	rmifninfo->funcDescriptor.interruptSrcCnt = fndescr->interruptSrcCnt;
+	rmifninfo->funcDescriptor.functionNum = fndescr->functionNum;
+
+	rmifninfo->numSources = fndescr->interruptSrcCnt;
+	/* Need to get interrupt info to be used later when handling
+	interrupts. */
+	rmifninfo->interruptRegister = interruptCount/8;
+
+	/* loop through interrupts for each source in fn $11 and or in a bit
+	to the interrupt mask for each. */
+	fn05InterruptOffset = interruptCount % 8;
+
+	for (i = fn05InterruptOffset;
+			i < ((fndescr->interruptSrcCnt & 0x7) + fn05InterruptOffset);
+			i++)
+	rmifninfo->interruptMask |= 1 << i;
+
+	return retval;
+}
+EXPORT_SYMBOL(FN_05_detect);
diff --git a/drivers/input/touchscreen/synaptics/rmi_f05.h b/drivers/input/touchscreen/synaptics/rmi_f05.h
new file mode 100644
index 0000000..b820e71
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics/rmi_f05.h
@@ -0,0 +1,43 @@
+/**
+ *
+ * Synaptics Register Mapped Interface (RMI4) Function $11 header.
+ * Copyright (c) 2007 - 2010, Synaptics Incorporated
+ *
+ * For every RMI4 function that has a data source - like 2D sensors,
+ * buttons, LEDs, GPIOs, etc. - the user will create a new rmi_function_xx.c
+ * file and add these functions to perform the config(), init(), report()
+ * and detect() functionality. The function pointers are then srored under
+ * the RMI function info and these functions will automatically be called by
+ * the global config(), init(), report() and detect() functions that will
+ * loop through all data sources and call the data sources functions using
+ * these functions pointed to by the function ptrs.
+ */
+/*
+ * This file is licensed under the GPL2 license.
+ *
+ *#############################################################################
+ * GPL
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ *#############################################################################
+ */
+#ifndef _RMI_FUNCTION_05_H
+#define _RMI_FUNCTION_05_H
+
+void FN_05_inthandler(struct rmi_function_info *rmifninfo,
+	unsigned int assertedIRQs);
+int FN_05_config(struct rmi_function_info *rmifninfo);
+int FN_05_init(struct rmi_function_device *function_device);
+int FN_05_detect(struct rmi_function_info *rmifninfo,
+		struct rmi_function_descriptor *fndescr,
+		unsigned int interruptCount);
+/* No attention function for F05 */
+#endif
diff --git a/drivers/input/touchscreen/synaptics/rmi_f11.c b/drivers/input/touchscreen/synaptics/rmi_f11.c
new file mode 100644
index 0000000..b75b3e0
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics/rmi_f11.c
@@ -0,0 +1,913 @@
+/**
+ *
+ * Synaptics Register Mapped Interface (RMI4) Function $11 support for 2D.
+ * Copyright (c) 2007 - 2011, Synaptics Incorporated
+ *
+ */
+/*
+ * This file is licensed under the GPL2 license.
+ *
+ *#############################################################################
+ * GPL
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ *#############################################################################
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/input/rmi_platformdata.h>
+#include <linux/module.h>
+
+#include "rmi.h"
+#include "rmi_drvr.h"
+#include "rmi_bus.h"
+#include "rmi_sensor.h"
+#include "rmi_function.h"
+#include "rmi_f11.h"
+
+static int sensorMaxX;
+static int sensorMaxY;
+
+struct f11_instance_data {
+	struct rmi_F11_device_query *deviceInfo;
+	struct rmi_F11_sensor_query *sensorInfo;
+	struct rmi_F11_control *controlRegisters;
+	int button_height;
+	unsigned char fingerDataBufferSize;
+	unsigned char absDataOffset;
+	unsigned char absDataSize;
+	unsigned char relDataOffset;
+	unsigned char gestureDataOffset;
+	unsigned char *fingerDataBuffer;
+		/* Last X & Y seen, needed at finger lift.  Was down indicates at least one finger was here. */
+		/* TODO: Eventually we'll need to track this info on a per finger basis. */
+	bool wasdown;
+	unsigned int oldX;
+	unsigned int oldY;
+		/* Transformations to be applied to coordinates before reporting. */
+	bool flipX;
+	bool flipY;
+	int offsetX;
+	int offsetY;
+	int clipXLow;
+	int clipXHigh;
+	int clipYLow;
+	int clipYHigh;
+	bool swap_axes;
+	bool relReport;
+};
+
+enum f11_finger_state {
+	F11_NO_FINGER = 0,
+	F11_PRESENT = 1,
+	F11_INACCURATE = 2,
+	F11_RESERVED = 3
+};
+
+static ssize_t rmi_fn_11_flip_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_11_flip_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count);
+
+DEVICE_ATTR(flip, 0664, rmi_fn_11_flip_show, rmi_fn_11_flip_store);     /* RW attr */
+
+static ssize_t rmi_fn_11_clip_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_11_clip_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count);
+
+DEVICE_ATTR(clip, 0664, rmi_fn_11_clip_show, rmi_fn_11_clip_store);     /* RW attr */
+
+static ssize_t rmi_fn_11_offset_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_11_offset_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count);
+
+DEVICE_ATTR(offset, 0664, rmi_fn_11_offset_show, rmi_fn_11_offset_store);     /* RW attr */
+
+static ssize_t rmi_fn_11_swap_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_11_swap_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count);
+
+DEVICE_ATTR(swap, 0664, rmi_fn_11_swap_show, rmi_fn_11_swap_store);     /* RW attr */
+
+static ssize_t rmi_fn_11_relreport_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_11_relreport_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count);
+
+DEVICE_ATTR(relreport, 0664, rmi_fn_11_relreport_show, rmi_fn_11_relreport_store);     /* RW attr */
+
+static ssize_t rmi_fn_11_maxPos_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_11_maxPos_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count);
+
+DEVICE_ATTR(maxPos, 0664, rmi_fn_11_maxPos_show, rmi_fn_11_maxPos_store);     /* RW attr */
+
+
+static void FN_11_relreport(struct rmi_function_info *rmifninfo);
+
+/*
+ * There is no attention function for Fn $11 - it is left NULL
+ * in the function table so it is not called.
+ *
+ */
+
+
+/*
+ * This reads in a sample and reports the function $11 source data to the
+ * input subsystem. It is used for both polling and interrupt driven
+ * operation. This is called a lot so don't put in any informational
+ * printks since they will slow things way down!
+ */
+void FN_11_inthandler(struct rmi_function_info *rmifninfo,
+	unsigned int assertedIRQs)
+{
+	/* number of touch points - fingers down in this case */
+	int fingerDownCount;
+	int finger;
+	struct rmi_function_device *function_device;
+	struct f11_instance_data *instanceData;
+
+	instanceData = (struct f11_instance_data *) rmifninfo->fndata;
+
+	fingerDownCount = 0;
+	function_device = rmifninfo->function_device;
+
+	/* get 2D sensor finger data */
+
+	if (rmi_read_multiple(rmifninfo->sensor, rmifninfo->funcDescriptor.dataBaseAddr,
+		instanceData->fingerDataBuffer, instanceData->fingerDataBufferSize)) {
+		printk(KERN_ERR "%s: Failed to read finger data registers.\n", __func__);
+		return;
+	}
+
+	/* First we need to count the fingers and generate some events related to that. */
+	for (finger = 0; finger < instanceData->sensorInfo->numberOfFingers; finger++) {
+		int reg;
+		int fingerShift;
+		int fingerStatus;
+
+		/* determine which data byte the finger status is in */
+		reg = finger/4;
+		/* bit shift to get finger's status */
+		fingerShift = (finger % 4) * 2;
+		fingerStatus = (instanceData->fingerDataBuffer[reg] >> fingerShift) & 3;
+
+		if (fingerStatus == F11_PRESENT || fingerStatus == F11_INACCURATE) {
+			fingerDownCount++;
+			instanceData->wasdown = true;
+		}
+	}
+	input_report_key(function_device->input,
+			BTN_TOUCH, !!fingerDownCount);
+
+	for (finger = 0; finger < instanceData->sensorInfo->numberOfFingers; finger++) {
+		int reg;
+		int fingerShift;
+		int fingerStatus;
+		int X = 0, Y = 0, Z = 0, Wy = 0, Wx = 0;
+
+		/* determine which data byte the finger status is in */
+		reg = finger/4;
+		/* bit shift to get finger's status */
+		fingerShift = (finger % 4) * 2;
+		fingerStatus = (instanceData->fingerDataBuffer[reg] >> fingerShift) & 3;
+
+		/* if finger status indicates a finger is present then
+		read the finger data and report it */
+		if (fingerStatus == F11_PRESENT || fingerStatus == F11_INACCURATE) {
+
+			if (instanceData->sensorInfo->hasAbs) {
+				int maxX = instanceData->controlRegisters->sensorMaxXPos;
+				int maxY = instanceData->controlRegisters->sensorMaxYPos;
+				reg = instanceData->absDataOffset + (finger * instanceData->absDataSize);
+				X = (instanceData->fingerDataBuffer[reg] << 4) & 0x0ff0;
+				X |= (instanceData->fingerDataBuffer[reg+2] & 0x0f);
+				Y = (instanceData->fingerDataBuffer[reg+1] << 4) & 0x0ff0;
+				Y |= ((instanceData->fingerDataBuffer[reg+2] & 0xf0) >> 4) & 0x0f;
+				/* First thing to do is swap axes if needed.
+				 */
+				if (instanceData->swap_axes) {
+					int temp = X;
+					X = Y;
+					Y = temp;
+					maxX = instanceData->controlRegisters->sensorMaxYPos;
+					maxY = instanceData->controlRegisters->sensorMaxXPos;
+				}
+				if (instanceData->flipX)
+					X = max(maxX-X, 0);
+				X = X - instanceData->offsetX;
+				X = min(max(X, instanceData->clipXLow), instanceData->clipXHigh);
+				if (instanceData->flipY)
+					Y = max(maxY-Y, 0);
+				Y = Y - instanceData->offsetY;
+				Y = min(max(Y, instanceData->clipYLow), instanceData->clipYHigh);
+
+				/* upper 4 bits of W are Wy,
+				lower 4 of W are Wx */
+				Wy =  (instanceData->fingerDataBuffer[reg+3] >> 4) & 0x0f;
+				Wx = instanceData->fingerDataBuffer[reg+3] & 0x0f;
+				if (instanceData->swap_axes) {
+					int temp = Wx;
+					Wx = Wy;
+					Wy = temp;
+				}
+
+				Z = instanceData->fingerDataBuffer[reg+4];
+
+				/* if this is the first finger report normal
+				ABS_X, ABS_Y, PRESSURE, TOOL_WIDTH events for
+				non-MT apps. Apps that support Multi-touch
+				will ignore these events and use the MT events.
+				Apps that don't support Multi-touch will still
+				function.
+				*/
+				if (fingerDownCount == 1) {
+					instanceData->oldX = X;
+					instanceData->oldY = Y;
+					input_report_abs(function_device->input, ABS_X, X);
+					input_report_abs(function_device->input, ABS_Y, Y);
+					input_report_abs(function_device->input, ABS_PRESSURE, Z);
+					input_report_abs(function_device->input, ABS_TOOL_WIDTH,
+							max(Wx, Wy));
+
+				} else {
+					/* TODO generate non MT events for multifinger situation. */
+				}
+#ifdef CONFIG_SYNA_MULTI_TOUCH
+				/* Report Multi-Touch events for each finger */
+				input_report_abs(function_device->input,
+							ABS_MT_PRESSURE, Z);
+				input_report_abs(function_device->input, ABS_MT_POSITION_X, X);
+				input_report_abs(function_device->input, ABS_MT_POSITION_Y, Y);
+
+				/* TODO: Tracking ID needs to be reported but not used yet. */
+				/* Could be formed by keeping an id per position and assiging */
+				/* a new id when fingerStatus changes for that position.*/
+				input_report_abs(function_device->input, ABS_MT_TRACKING_ID,
+						finger);
+				/* MT sync between fingers */
+				input_mt_sync(function_device->input);
+#endif
+			}
+		}
+	}
+
+	/* if we had a finger down before and now we don't have any send a button up. */
+	if ((fingerDownCount == 0) && instanceData->wasdown) {
+		instanceData->wasdown = false;
+
+#ifdef CONFIG_SYNA_MULTI_TOUCH
+		input_report_abs(function_device->input, ABS_MT_PRESSURE, 0);
+		input_report_key(function_device->input, BTN_TOUCH, 0);
+		input_mt_sync(function_device->input);
+#endif
+	}
+
+	FN_11_relreport(rmifninfo);
+	input_sync(function_device->input); /* sync after groups of events */
+
+}
+EXPORT_SYMBOL(FN_11_inthandler);
+
+/* This function reads in relative data for first finger and send to input system */
+static void FN_11_relreport(struct rmi_function_info *rmifninfo)
+{
+	struct f11_instance_data *instanceData;
+	struct rmi_function_device *function_device;
+	signed char X, Y;
+	unsigned short fn11DataBaseAddr;
+
+	instanceData = (struct f11_instance_data *) rmifninfo->fndata;
+
+	if (instanceData->sensorInfo->hasRel && instanceData->relReport) {
+		int reg = instanceData->relDataOffset;
+
+		function_device = rmifninfo->function_device;
+
+		fn11DataBaseAddr = rmifninfo->funcDescriptor.dataBaseAddr;
+		/* Read and report Rel data for primary finger one register for X and one for Y*/
+		X = instanceData->fingerDataBuffer[reg];
+		Y = instanceData->fingerDataBuffer[reg+1];
+		if (instanceData->swap_axes) {
+			signed char temp = X;
+			X = Y;
+			Y = temp;
+		}
+		if (instanceData->flipX) {
+			X = -X;
+		}
+		if (instanceData->flipY) {
+			Y = -Y;
+		}
+		X = (signed char) min(127, max(-128, (int) X));
+		Y = (signed char) min(127, max(-128, (int) Y));
+
+		input_report_rel(function_device->input, REL_X, X);
+		input_report_rel(function_device->input, REL_Y, Y);
+	}
+}
+
+int FN_11_config(struct rmi_function_info *rmifninfo)
+{
+	/* For the data source - print info and do any
+	source specific configuration. */
+	unsigned char data[14];
+	int retval = 0;
+
+	pr_debug("%s: RMI4 function $11 config\n", __func__);
+
+	/* Get and print some info about the data source... */
+
+	/* To Query 2D devices we need to read from the address obtained
+	* from the function descriptor stored in the RMI function info.
+	*/
+	retval = rmi_read_multiple(rmifninfo->sensor, rmifninfo->funcDescriptor.queryBaseAddr,
+		data, 9);
+	if (retval) {
+		printk(KERN_ERR "%s: RMI4 function $11 config:"
+			"Could not read function query registers 0x%x\n",
+			__func__, rmifninfo->funcDescriptor.queryBaseAddr);
+	} else {
+		pr_debug("%s:  Number of Fingers:   %d\n",
+				__func__, data[1] & 7);
+		pr_debug("%s:  Is Configurable:     %d\n",
+				__func__, data[1] & (1 << 7) ? 1 : 0);
+		pr_debug("%s:  Has Gestures:        %d\n",
+				__func__, data[1] & (1 << 5) ? 1 : 0);
+		pr_debug("%s:  Has Absolute:        %d\n",
+				__func__, data[1] & (1 << 4) ? 1 : 0);
+		pr_debug("%s:  Has Relative:        %d\n",
+				__func__, data[1] & (1 << 3) ? 1 : 0);
+
+		pr_debug("%s:  Number X Electrodes: %d\n",
+				__func__, data[2] & 0x1f);
+		pr_debug("%s:  Number Y Electrodes: %d\n",
+				__func__, data[3] & 0x1f);
+		pr_debug("%s:  Maximum Electrodes:  %d\n",
+				__func__, data[4] & 0x1f);
+
+		pr_debug("%s:  Absolute Data Size:  %d\n",
+				__func__, data[5] & 3);
+
+		pr_debug("%s:  Has XY Dist:         %d\n",
+				__func__, data[7] & (1 << 7) ? 1 : 0);
+		pr_debug("%s:  Has Pinch:           %d\n",
+				__func__, data[7] & (1 << 6) ? 1 : 0);
+		pr_debug("%s:  Has Press:           %d\n",
+				__func__, data[7] & (1 << 5) ? 1 : 0);
+		pr_debug("%s:  Has Flick:           %d\n",
+				__func__, data[7] & (1 << 4) ? 1 : 0);
+		pr_debug("%s:  Has Early Tap:       %d\n",
+				__func__, data[7] & (1 << 3) ? 1 : 0);
+		pr_debug("%s:  Has Double Tap:      %d\n",
+				__func__, data[7] & (1 << 2) ? 1 : 0);
+		pr_debug("%s:  Has Tap and Hold:    %d\n",
+				__func__, data[7] & (1 << 1) ? 1 : 0);
+		pr_debug("%s:  Has Tap:             %d\n",
+				__func__, data[7] & 1 ? 1 : 0);
+		pr_debug("%s:  Has Palm Detect:     %d\n",
+				__func__, data[8] & 1 ? 1 : 0);
+		pr_debug("%s:  Has Rotate:          %d\n",
+				__func__, data[8] & (1 << 1) ? 1 : 0);
+
+		retval = rmi_read_multiple(rmifninfo->sensor,
+				rmifninfo->funcDescriptor.controlBaseAddr, data, 14);
+		if (retval) {
+			printk(KERN_ERR "%s: RMI4 function $11 config:"
+				"Could not read control registers 0x%x\n",
+				__func__, rmifninfo->funcDescriptor.controlBaseAddr);
+			return retval;
+		}
+
+		/* Store these for use later...*/
+		sensorMaxX = ((data[6] & 0x1f) << 8) | ((data[7] & 0xff) << 0);
+		sensorMaxY = ((data[8] & 0x1f) << 8) | ((data[9] & 0xff) << 0);
+
+		pr_debug("%s:  Sensor Max X:  %d\n", __func__, sensorMaxX);
+		pr_debug("%s:  Sensor Max Y:  %d\n", __func__, sensorMaxY);
+	}
+
+	return retval;
+}
+EXPORT_SYMBOL(FN_11_config);
+
+/* This operation is done in a number of places, so we have a handy routine
+ * for it.
+ */
+static void f11_set_abs_params(struct rmi_function_device *function_device)
+{
+	struct f11_instance_data *instance_data = function_device->rfi->fndata;
+	/* Use the max X and max Y read from the device, or the clip values,
+	 * whichever is stricter.
+	 */
+	int xMin = instance_data->clipXLow;
+	int xMax = min((int) instance_data->controlRegisters->sensorMaxXPos, instance_data->clipXHigh);
+	int yMin = instance_data->clipYLow;
+	int yMax = min((int) instance_data->controlRegisters->sensorMaxYPos, instance_data->clipYHigh) - instance_data->button_height;
+	if (instance_data->swap_axes) {
+		int temp = xMin;
+		xMin = yMin;
+		yMin = temp;
+		temp = xMax;
+		xMax = yMax;
+		yMax = temp;
+	}
+	printk(KERN_DEBUG "%s: Set ranges X=[%d..%d] Y=[%d..%d].", __func__, xMin, xMax, yMin, yMax);
+	input_set_abs_params(function_device->input, ABS_X, xMin, xMax,
+		0, 0);
+	input_set_abs_params(function_device->input, ABS_Y, yMin, yMax,
+		0, 0);
+	input_set_abs_params(function_device->input, ABS_PRESSURE, 0, 255, 0, 0);
+	input_set_abs_params(function_device->input, ABS_TOOL_WIDTH, 0, 15, 0, 0);
+
+#ifdef CONFIG_SYNA_MULTI_TOUCH
+	input_set_abs_params(function_device->input, ABS_MT_PRESSURE,
+							0, 15, 0, 0);
+	input_set_abs_params(function_device->input, ABS_MT_ORIENTATION, 0, 1, 0, 0);
+	input_set_abs_params(function_device->input, ABS_MT_TRACKING_ID,
+							0, 10, 0, 0);
+	input_set_abs_params(function_device->input, ABS_MT_POSITION_X, xMin, xMax,
+		0, 0);
+	input_set_abs_params(function_device->input, ABS_MT_POSITION_Y, yMin, yMax,
+		0, 0);
+#endif
+}
+
+/* Initialize any function $11 specific params and settings - input
+ * settings, device settings, etc.
+ */
+int FN_11_init(struct rmi_function_device *function_device)
+{
+	struct f11_instance_data *instanceData = function_device->rfi->fndata;
+	int retval = 0;
+	struct rmi_f11_functiondata *functiondata = rmi_sensor_get_functiondata(function_device->sensor, RMI_F11_INDEX);
+	printk(KERN_DEBUG "%s: RMI4 F11 init", __func__);
+
+	/* TODO: Initialize these through some normal kernel mechanism.
+	 */
+	instanceData->flipX = false;
+	instanceData->flipY = false;
+	instanceData->swap_axes = false;
+	instanceData->relReport = true;
+	instanceData->offsetX = instanceData->offsetY = 0;
+	instanceData->clipXLow = instanceData->clipYLow = 0;
+	/* TODO: 65536 should actually be the largest valid RMI4 position coordinate */
+	instanceData->clipXHigh = instanceData->clipYHigh = 65536;
+
+	/* Load any overrides that were specified via platform data.
+	 */
+	if (functiondata) {
+		printk(KERN_DEBUG "%s: found F11 per function platformdata.", __func__);
+		instanceData->flipX = functiondata->flipX;
+		instanceData->flipY = functiondata->flipY;
+		instanceData->button_height = functiondata->button_height;
+		instanceData->swap_axes = functiondata->swap_axes;
+		if (functiondata->offset) {
+			instanceData->offsetX = functiondata->offset->x;
+			instanceData->offsetY = functiondata->offset->y;
+		}
+		if (functiondata->clipX) {
+			if (functiondata->clipX->min >= functiondata->clipX->max) {
+				printk(KERN_WARNING "%s: Clip X min (%d) >= X clip max (%d) - ignored.",
+					   __func__, functiondata->clipX->min, functiondata->clipX->max);
+			} else {
+				instanceData->clipXLow = functiondata->clipX->min;
+				instanceData->clipXHigh = functiondata->clipX->max;
+			}
+		}
+		if (functiondata->clipY) {
+			if (functiondata->clipY->min >= functiondata->clipY->max) {
+				printk(KERN_WARNING "%s: Clip Y min (%d) >= Y clip max (%d) - ignored.",
+					   __func__, functiondata->clipY->min, functiondata->clipY->max);
+			} else {
+				instanceData->clipYLow = functiondata->clipY->min;
+				instanceData->clipYHigh = functiondata->clipY->max;
+			}
+		}
+	}
+
+	/* need to init the input abs params for the 2D */
+	set_bit(EV_ABS, function_device->input->evbit);
+	set_bit(EV_SYN, function_device->input->evbit);
+	set_bit(EV_KEY, function_device->input->evbit);
+	set_bit(BTN_TOUCH, function_device->input->keybit);
+	set_bit(KEY_OK, function_device->input->keybit);
+
+	f11_set_abs_params(function_device);
+
+	printk(KERN_DEBUG "%s: Creating sysfs files.", __func__);
+	retval = device_create_file(&function_device->dev, &dev_attr_flip);
+	if (retval) {
+		printk(KERN_ERR "%s: Failed to create flip.", __func__);
+		return retval;
+	}
+	retval = device_create_file(&function_device->dev, &dev_attr_clip);
+	if (retval) {
+		printk(KERN_ERR "%s: Failed to create clip.", __func__);
+		return retval;
+	}
+	retval = device_create_file(&function_device->dev, &dev_attr_offset);
+	if (retval) {
+		printk(KERN_ERR "%s: Failed to create offset.", __func__);
+		return retval;
+	}
+	retval = device_create_file(&function_device->dev, &dev_attr_swap);
+	if (retval) {
+		printk(KERN_ERR "%s: Failed to create swap.", __func__);
+		return retval;
+	}
+	retval = device_create_file(&function_device->dev, &dev_attr_relreport);
+	if (retval) {
+		printk(KERN_ERR "%s: Failed to create relreport.", __func__);
+		return retval;
+	}
+	retval = device_create_file(&function_device->dev, &dev_attr_maxPos);
+	if (retval) {
+		printk(KERN_ERR "%s: Failed to create maxPos.", __func__);
+		return retval;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(FN_11_init);
+
+int FN_11_detect(struct rmi_function_info *rmifninfo,
+	struct rmi_function_descriptor *fndescr, unsigned int interruptCount)
+{
+	unsigned char fn11Queries[12];   /* TODO: Compute size correctly. */
+	unsigned char fn11Control[12];   /* TODO: Compute size correctly. */
+	int i;
+	unsigned short fn11InterruptOffset;
+	unsigned char fn11AbsDataBlockSize;
+	int fn11HasPinch, fn11HasFlick, fn11HasTap;
+	int fn11HasTapAndHold, fn11HasDoubleTap;
+	int fn11HasEarlyTap, fn11HasPress;
+	int fn11HasPalmDetect, fn11HasRotate;
+	int fn11HasRel;
+	unsigned char f11_egr_0, f11_egr_1;
+	unsigned int fn11AllDataBlockSize;
+	int retval = 0;
+	struct f11_instance_data *instanceData;
+
+	printk(KERN_DEBUG "%s: RMI4 F11 detect\n", __func__);
+
+	instanceData = kzalloc(sizeof(struct f11_instance_data), GFP_KERNEL);
+	if (!instanceData) {
+		printk(KERN_ERR "%s: Error allocating F11 instance data.\n", __func__);
+		return -ENOMEM;
+	}
+	instanceData->deviceInfo = kzalloc(sizeof(struct rmi_F11_device_query), GFP_KERNEL);
+	if (!instanceData->deviceInfo) {
+		printk(KERN_ERR "%s: Error allocating F11 device query.\n", __func__);
+		return -ENOMEM;
+	}
+	instanceData->sensorInfo = kzalloc(sizeof(struct rmi_F11_sensor_query), GFP_KERNEL);
+	if (!instanceData->sensorInfo) {
+		printk(KERN_ERR "%s: Error allocating F11 sensor query.\n", __func__);
+		return -ENOMEM;
+	}
+	rmifninfo->fndata = instanceData;
+
+	/* Store addresses - used elsewhere to read data,
+	* control, query, etc. */
+	rmifninfo->funcDescriptor.queryBaseAddr = fndescr->queryBaseAddr;
+	rmifninfo->funcDescriptor.commandBaseAddr = fndescr->commandBaseAddr;
+	rmifninfo->funcDescriptor.controlBaseAddr = fndescr->controlBaseAddr;
+	rmifninfo->funcDescriptor.dataBaseAddr = fndescr->dataBaseAddr;
+	rmifninfo->funcDescriptor.interruptSrcCnt = fndescr->interruptSrcCnt;
+	rmifninfo->funcDescriptor.functionNum = fndescr->functionNum;
+
+	rmifninfo->numSources = fndescr->interruptSrcCnt;
+
+	/* need to get number of fingers supported, data size, etc. -
+	to be used when getting data since the number of registers to
+	read depends on the number of fingers supported and data size. */
+	retval = rmi_read_multiple(rmifninfo->sensor, fndescr->queryBaseAddr, fn11Queries,
+			sizeof(fn11Queries));
+	if (retval) {
+		printk(KERN_ERR "%s: RMI4 function $11 detect: "
+			"Could not read function query registers 0x%x\n",
+			__func__,  rmifninfo->funcDescriptor.queryBaseAddr);
+		return retval;
+	}
+
+	/* Extract device data. */
+	instanceData->deviceInfo->hasQuery9 = (fn11Queries[0] & 0x04) != 0;
+	instanceData->deviceInfo->numberOfSensors = (fn11Queries[0] & 0x07) + 1;
+	printk(KERN_DEBUG "%s: F11 device - %d sensors.  Query 9? %d.", __func__, instanceData->deviceInfo->numberOfSensors, instanceData->deviceInfo->hasQuery9);
+
+	/* Extract sensor data. */
+	/* 2D data sources have only 3 bits for the number of fingers
+	supported - so the encoding is a bit wierd. */
+	instanceData->sensorInfo->numberOfFingers = 2; /* default number of fingers supported */
+	if ((fn11Queries[1] & 0x7) <= 4)
+		/* add 1 since zero based */
+		instanceData->sensorInfo->numberOfFingers = (fn11Queries[1] & 0x7) + 1;
+	else {
+		/* a value of 5 is up to 10 fingers - 6 and 7 are reserved
+		(shouldn't get these i int retval;n a normal 2D source). */
+		if ((fn11Queries[1] & 0x7) == 5)
+			instanceData->sensorInfo->numberOfFingers = 10;
+	}
+	instanceData->sensorInfo->configurable = (fn11Queries[1] & 0x80) != 0;
+	instanceData->sensorInfo->hasSensitivityAdjust = (fn11Queries[1] & 0x40) != 0;
+	instanceData->sensorInfo->hasGestures = (fn11Queries[1] & 0x20) != 0;
+	instanceData->sensorInfo->hasAbs = (fn11Queries[1] & 0x10) != 0;
+	instanceData->sensorInfo->hasRel = (fn11Queries[1] & 0x08) != 0;
+	instanceData->sensorInfo->absDataSize = fn11Queries[5] & 0x03;
+	printk(KERN_DEBUG "%s: Number of fingers: %d.", __func__, instanceData->sensorInfo->numberOfFingers);
+
+	/* Need to get interrupt info to be used later when handling
+	interrupts. */
+	rmifninfo->interruptRegister = interruptCount/8;
+
+	/* loop through interrupts for each source in fn $11 and or in a bit
+	to the interrupt mask for each. */
+	fn11InterruptOffset = interruptCount % 8;
+
+	for (i = fn11InterruptOffset;
+			i < ((fndescr->interruptSrcCnt & 0x7) + fn11InterruptOffset);
+			i++)
+		rmifninfo->interruptMask |= 1 << i;
+
+	/* Figure out just how much data we'll need to read. */
+	instanceData->fingerDataBufferSize = (instanceData->sensorInfo->numberOfFingers + 3) / 4;
+	/* One each for X and Y, one for LSB for X & Y, one for W, one for Z */
+	fn11AbsDataBlockSize = 5;
+	if (instanceData->sensorInfo->absDataSize != 0)
+		printk(KERN_WARNING "%s: Unrecognized abs data size %d ignored.", __func__, instanceData->sensorInfo->absDataSize);
+	if (instanceData->sensorInfo->hasAbs) {
+		instanceData->absDataSize = fn11AbsDataBlockSize;
+		instanceData->absDataOffset = instanceData->fingerDataBufferSize;
+		instanceData->fingerDataBufferSize += instanceData->sensorInfo->numberOfFingers * fn11AbsDataBlockSize;
+	}
+	if (instanceData->sensorInfo->hasRel) {
+		instanceData->relDataOffset = ((instanceData->sensorInfo->numberOfFingers + 3) / 4) +
+			/* absolute data, per finger times number of fingers */
+			(fn11AbsDataBlockSize * instanceData->sensorInfo->numberOfFingers);
+		instanceData->fingerDataBufferSize += instanceData->sensorInfo->numberOfFingers * 2;
+	}
+	if (instanceData->sensorInfo->hasGestures) {
+		instanceData->gestureDataOffset = instanceData->fingerDataBufferSize;
+		printk(KERN_WARNING "%s: WARNING Need to correctly compute gesture data location.", __func__);
+	}
+
+	/* need to determine the size of data to read - this depends on
+	conditions such as whether Relative data is reported and if Gesture
+	data is reported. */
+	f11_egr_0 = fn11Queries[7];
+	f11_egr_1 = fn11Queries[8];
+
+	/* Get info about what EGR data is supported, whether it has
+	Relative data supported, etc. */
+	fn11HasPinch = f11_egr_0 & 0x40;
+	fn11HasFlick = f11_egr_0 & 0x10;
+	fn11HasTap = f11_egr_0 & 0x01;
+	fn11HasTapAndHold = f11_egr_0 & 0x02;
+	fn11HasDoubleTap = f11_egr_0 & 0x04;
+	fn11HasEarlyTap = f11_egr_0 & 0x08;
+	fn11HasPress = f11_egr_0 & 0x20;
+	fn11HasPalmDetect = f11_egr_1 & 0x01;
+	fn11HasRotate = f11_egr_1 & 0x02;
+	fn11HasRel = fn11Queries[1] & 0x08;
+
+	/* Size of all data including finger status, absolute data for each
+	finger, relative data and EGR data */
+	fn11AllDataBlockSize =
+		/* finger status, four fingers per register */
+		((instanceData->sensorInfo->numberOfFingers + 3) / 4) +
+		/* absolute data, per finger times number of fingers */
+		(fn11AbsDataBlockSize * instanceData->sensorInfo->numberOfFingers) +
+		/* two relative registers (if relative is being reported) */
+		2 * fn11HasRel +
+		/* F11_2D_Data8 is only present if the egr_0
+		register is non-zero. */
+		!!(f11_egr_0) +
+		/* F11_2D_Data9 is only present if either egr_0 or
+		egr_1 registers are non-zero. */
+		(f11_egr_0 || f11_egr_1) +
+		/* F11_2D_Data10 is only present if EGR_PINCH or EGR_FLICK of
+		egr_0 reports as 1. */
+		!!(fn11HasPinch | fn11HasFlick) +
+		/* F11_2D_Data11 and F11_2D_Data12 are only present if
+		EGR_FLICK of egr_0 reports as 1. */
+		2 * !!(fn11HasFlick);
+	instanceData->fingerDataBuffer = kcalloc(instanceData->fingerDataBufferSize, sizeof(unsigned char), GFP_KERNEL);
+	if (!instanceData->fingerDataBuffer) {
+		printk(KERN_ERR "%s: Failed to allocate finger data buffer.", __func__);
+		return -ENOMEM;
+	}
+
+	/* Grab a copy of the control registers. */
+	instanceData->controlRegisters = kzalloc(sizeof(struct rmi_F11_control), GFP_KERNEL);
+	if (!instanceData->controlRegisters) {
+		printk(KERN_ERR "%s: Error allocating F11 control registers.\n", __func__);
+		return -ENOMEM;
+	}
+	retval = rmi_read_multiple(rmifninfo->sensor, fndescr->controlBaseAddr,
+		fn11Control, sizeof(fn11Control));
+	if (retval) {
+		printk(KERN_ERR "%s: Failed to read F11 control registers.", __func__);
+		return retval;
+	}
+	instanceData->controlRegisters->sensorMaxXPos = (((int) fn11Control[7] & 0x0F) << 8) + fn11Control[6];
+	instanceData->controlRegisters->sensorMaxYPos = (((int) fn11Control[9] & 0x0F) << 8) + fn11Control[8];
+	printk(KERN_DEBUG "%s: Max X %d Max Y %d", __func__, instanceData->controlRegisters->sensorMaxXPos, instanceData->controlRegisters->sensorMaxYPos);
+	return 0;
+}
+EXPORT_SYMBOL(FN_11_detect);
+
+static ssize_t rmi_fn_11_maxPos_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f11_instance_data *instance_data = (struct f11_instance_data *)fn->rfi->fndata;
+
+	return sprintf(buf, "%u %u\n", instance_data->controlRegisters->sensorMaxXPos, instance_data->controlRegisters->sensorMaxYPos);
+}
+
+static ssize_t rmi_fn_11_maxPos_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	return -EPERM;
+}
+
+static ssize_t rmi_fn_11_flip_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f11_instance_data *instance_data = (struct f11_instance_data *)fn->rfi->fndata;
+
+	return sprintf(buf, "%u %u\n", instance_data->flipX, instance_data->flipY);
+}
+
+static ssize_t rmi_fn_11_flip_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f11_instance_data *instance_data = (struct f11_instance_data *)fn->rfi->fndata;
+	unsigned int newX, newY;
+
+	printk(KERN_DEBUG "%s: Flip set to %s", __func__, buf);
+
+	if (sscanf(buf, "%u %u", &newX, &newY) != 2)
+		return -EINVAL;
+	if (newX < 0 || newX > 1 || newY < 0 || newY > 1)
+		return -EINVAL;
+	instance_data->flipX = newX;
+	instance_data->flipY = newY;
+
+	return count;
+}
+
+static ssize_t rmi_fn_11_swap_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f11_instance_data *instance_data = (struct f11_instance_data *)fn->rfi->fndata;
+
+	return sprintf(buf, "%u\n", instance_data->swap_axes);
+}
+
+static ssize_t rmi_fn_11_swap_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f11_instance_data *instance_data = (struct f11_instance_data *)fn->rfi->fndata;
+	unsigned int newSwap;
+
+	printk(KERN_DEBUG "%s: Swap set to %s", __func__, buf);
+
+	if (sscanf(buf, "%u", &newSwap) != 1)
+		return -EINVAL;
+	if (newSwap < 0 || newSwap > 1)
+		return -EINVAL;
+	instance_data->swap_axes = newSwap;
+
+	f11_set_abs_params(fn);
+
+	return count;
+}
+
+static ssize_t rmi_fn_11_relreport_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f11_instance_data *instance_data = (struct f11_instance_data *)fn->rfi->fndata;
+
+	return sprintf(buf, "%u \n", instance_data->relReport);
+}
+
+static ssize_t rmi_fn_11_relreport_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f11_instance_data *instance_data = (struct f11_instance_data *)fn->rfi->fndata;
+	unsigned int relRep;
+
+	printk(KERN_DEBUG "%s: relReport set to %s", __func__, buf);
+	if (sscanf(buf, "%u", &relRep) != 1)
+		return -EINVAL;
+	if (relRep < 0 || relRep > 1)
+		return -EINVAL;
+	instance_data->relReport = relRep;
+
+	return count;
+}
+
+static ssize_t rmi_fn_11_offset_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f11_instance_data *instance_data = (struct f11_instance_data *)fn->rfi->fndata;
+
+	return sprintf(buf, "%d %d\n", instance_data->offsetX, instance_data->offsetY);
+}
+
+static ssize_t rmi_fn_11_offset_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f11_instance_data *instance_data = (struct f11_instance_data *)fn->rfi->fndata;
+	int newX, newY;
+
+	printk(KERN_DEBUG "%s: Offset set to %s", __func__, buf);
+
+	if (sscanf(buf, "%d %d", &newX, &newY) != 2)
+		return -EINVAL;
+	instance_data->offsetX = newX;
+	instance_data->offsetY = newY;
+
+	return count;
+}
+
+static ssize_t rmi_fn_11_clip_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f11_instance_data *instance_data = (struct f11_instance_data *)fn->rfi->fndata;
+
+	return sprintf(buf, "%u %u %u %u\n",
+				   instance_data->clipXLow, instance_data->clipXHigh,
+				   instance_data->clipYLow, instance_data->clipYHigh);
+}
+
+static ssize_t rmi_fn_11_clip_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f11_instance_data *instance_data = (struct f11_instance_data *)fn->rfi->fndata;
+	unsigned int newXLow, newXHigh, newYLow, newYHigh;
+
+	printk(KERN_DEBUG "%s: Clip set to %s", __func__, buf);
+
+	if (sscanf(buf, "%u %u %u %u", &newXLow, &newXHigh, &newYLow, &newYHigh) != 4)
+		return -EINVAL;
+	if (newXLow < 0 || newXLow >= newXHigh || newYLow < 0 || newYLow >= newYHigh)
+		return -EINVAL;
+	instance_data->clipXLow = newXLow;
+	instance_data->clipXHigh = newXHigh;
+	instance_data->clipYLow = newYLow;
+	instance_data->clipYHigh = newYHigh;
+
+	f11_set_abs_params(fn);
+
+	return count;
+}
diff --git a/drivers/input/touchscreen/synaptics/rmi_f11.h b/drivers/input/touchscreen/synaptics/rmi_f11.h
new file mode 100644
index 0000000..0bf386a
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics/rmi_f11.h
@@ -0,0 +1,43 @@
+/**
+ *
+ * Synaptics Register Mapped Interface (RMI4) Function $11 header.
+ * Copyright (c) 2007 - 2010, Synaptics Incorporated
+ *
+ * For every RMI4 function that has a data source - like 2D sensors,
+ * buttons, LEDs, GPIOs, etc. - the user will create a new rmi_function_xx.c
+ * file and add these functions to perform the config(), init(), report()
+ * and detect() functionality. The function pointers are then srored under
+ * the RMI function info and these functions will automatically be called by
+ * the global config(), init(), report() and detect() functions that will
+ * loop through all data sources and call the data sources functions using
+ * these functions pointed to by the function ptrs.
+ */
+/*
+ * This file is licensed under the GPL2 license.
+ *
+ *#############################################################################
+ * GPL
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ *#############################################################################
+ */
+#ifndef _RMI_FUNCTION_11_H
+#define _RMI_FUNCTION_11_H
+
+void FN_11_inthandler(struct rmi_function_info *rmifninfo,
+	unsigned int assertedIRQs);
+int FN_11_config(struct rmi_function_info *rmifninfo);
+int FN_11_init(struct rmi_function_device *function_device);
+int FN_11_detect(struct rmi_function_info *rmifninfo,
+		struct rmi_function_descriptor *fndescr,
+		unsigned int interruptCount);
+/* No attention function for Fn $11 */
+#endif
diff --git a/drivers/input/touchscreen/synaptics/rmi_f19.c b/drivers/input/touchscreen/synaptics/rmi_f19.c
new file mode 100644
index 0000000..7bb8712
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics/rmi_f19.c
@@ -0,0 +1,514 @@
+/**
+ *
+ * Synaptics Register Mapped Interface (RMI4) Function $11 support for 2D.
+ * Copyright (c) 2007 - 2011, Synaptics Incorporated
+ *
+ */
+/*
+ * This file is licensed under the GPL2 license.
+ *
+ *#############################################################################
+ * GPL
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ *#############################################################################
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/input/rmi_platformdata.h>
+#include <linux/module.h>
+
+#include "rmi.h"
+#include "rmi_drvr.h"
+#include "rmi_bus.h"
+#include "rmi_sensor.h"
+#include "rmi_function.h"
+#include "rmi_f19.h"
+
+struct f19_instance_data {
+	struct rmi_F19_query *deviceInfo;
+	struct rmi_F19_control *controlRegisters;
+	bool *buttonDown;
+	unsigned char buttonDataBufferSize;
+	unsigned char *buttonDataBuffer;
+	unsigned char *buttonMap;
+	int fn19ControlRegisterSize;
+	int fn19regCountForBitPerButton;
+	int fn19btnUsageandfilterModeOffset;
+	int fn19intEnableOffset;
+	int fn19intEnableLen;
+	int fn19singleBtnCtrlLen;
+	int fn19singleBtnCtrlOffset;
+	int fn19sensorMapCtrlOffset;
+	int fn19sensorMapCtrlLen;
+	int fn19singleBtnSensOffset;
+	int fn19singleBtnSensLen;
+	int fn19globalSensOffset;
+	int fn19globalHystThreshOffset;
+};
+
+static ssize_t rmi_f19_buttonCount_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_f19_buttonCount_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count);
+
+DEVICE_ATTR(buttonCount, 0444, rmi_f19_buttonCount_show, rmi_f19_buttonCount_store);	/* RO attr */
+
+static ssize_t rmi_f19_buttonMap_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_f19_buttonMap_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count);
+
+DEVICE_ATTR(buttonMap, 0664, rmi_f19_buttonMap_show, rmi_f19_buttonMap_store);	/* RW attr */
+
+
+/*
+ * There is no attention function for F19 - it is left NULL
+ * in the function table so it is not called.
+ *
+ */
+
+
+/*
+ * This reads in a sample and reports the F19 source data to the
+ * input subsystem. It is used for both polling and interrupt driven
+ * operation. This is called a lot so don't put in any informational
+ * printks since they will slow things way down!
+ */
+void FN_19_inthandler(struct rmi_function_info *rmifninfo,
+	unsigned int assertedIRQs)
+{
+	struct rmi_function_device *function_device;
+	struct f19_instance_data *instanceData;
+	int button;
+
+	instanceData = (struct f19_instance_data *) rmifninfo->fndata;
+
+	function_device = rmifninfo->function_device;
+
+	/* Read the button data. */
+
+	if (rmi_read_multiple(rmifninfo->sensor, rmifninfo->funcDescriptor.dataBaseAddr,
+		instanceData->buttonDataBuffer, instanceData->buttonDataBufferSize)) {
+		printk(KERN_ERR "%s: Failed to read button data registers.\n", __func__);
+		return;
+	}
+
+	/* Generate events for buttons that change state. */
+	for (button = 0; button < instanceData->deviceInfo->buttonCount; button++) {
+		int buttonReg;
+		int buttonShift;
+		bool buttonStatus;
+
+		/* determine which data byte the button status is in */
+		buttonReg = button/4;
+		/* bit shift to get button's status */
+		buttonShift = button % 8;
+		buttonStatus = ((instanceData->buttonDataBuffer[buttonReg] >> buttonShift) & 0x01) != 0;
+
+		/* if the button state changed from the last time report it and store the new state */
+		if (buttonStatus != instanceData->buttonDown[button]) {
+			printk(KERN_DEBUG "%s: Button %d (code %d) -> %d.", __func__, button, instanceData->buttonMap[button], buttonStatus);
+			/* Generate an event here. */
+			input_report_key(function_device->input,
+				instanceData->buttonMap[button], buttonStatus);
+			instanceData->buttonDown[button] = buttonStatus;
+		}
+	}
+
+	input_sync(function_device->input); /* sync after groups of events */
+}
+EXPORT_SYMBOL(FN_19_inthandler);
+
+int FN_19_config(struct rmi_function_info *rmifninfo)
+{
+	int retval = 0;
+
+	pr_debug("%s: RMI4 F19 config\n", __func__);
+
+	/* TODO: Perform configuration.  In particular, write any cached control
+	 * register values to the device. */
+
+	return retval;
+}
+EXPORT_SYMBOL(FN_19_config);
+
+/* Initialize any F19 specific params and settings - input
+ * settings, device settings, etc.
+ */
+int FN_19_init(struct rmi_function_device *function_device)
+{
+	int i, retval = 0;
+	struct f19_instance_data *instance_data = function_device->rfi->fndata;
+	struct rmi_f19_functiondata *functiondata = rmi_sensor_get_functiondata(function_device->sensor, RMI_F19_INDEX);
+
+	printk(KERN_DEBUG "%s: RMI4 F19 init\n", __func__);
+
+	if (functiondata) {
+		if (functiondata->button_map) {
+			if (functiondata->button_map->nbuttons != instance_data->deviceInfo->buttonCount) {
+				printk(KERN_WARNING "%s: Platformdata button map size (%d) != number of buttons on device (%d) - ignored.", __func__, functiondata->button_map->nbuttons, instance_data->deviceInfo->buttonCount);
+			} else if (!functiondata->button_map->map) {
+				printk(KERN_WARNING "%s: Platformdata button map is missing!", __func__);
+			} else {
+				for (i = 0; i < functiondata->button_map->nbuttons; i++)
+					instance_data->buttonMap[i] = functiondata->button_map->map[i];
+			}
+		}
+	}
+
+	/* Set up any input events. */
+	set_bit(EV_SYN, function_device->input->evbit);
+	set_bit(EV_KEY, function_device->input->evbit);
+	/* set bits for each button...*/
+	for (i = 0; i < instance_data->deviceInfo->buttonCount; i++) {
+		set_bit(instance_data->buttonMap[i], function_device->input->keybit);
+	}
+
+	printk(KERN_DEBUG "%s: Creating sysfs files.", __func__);
+	retval = device_create_file(&function_device->dev, &dev_attr_buttonCount);
+	if (retval) {
+		printk(KERN_ERR "%s: Failed to create button count.", __func__);
+		return retval;
+	}
+
+	retval = device_create_file(&function_device->dev, &dev_attr_buttonMap);
+	if (retval) {
+		printk(KERN_ERR "%s: Failed to create button map.", __func__);
+		return retval;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(FN_19_init);
+
+static int getControlRegisters(struct rmi_function_info *rmifninfo,
+	struct rmi_function_descriptor *fndescr)
+{
+	struct f19_instance_data *instanceData;
+	unsigned char *fn19Control = NULL;
+	int retval = 0;
+
+	/* Get the instance data - it should have been allocated and stored in detect.*/
+	instanceData = rmifninfo->fndata;
+
+	/* Check to make sure instanceData is really there before using.*/
+	if (!instanceData) {
+		printk(KERN_ERR "%s: Error - instance data not initialized yet when getting fn19 control registers.\n", __func__);
+		return -EINVAL;
+	}
+
+	/* Allocate memory for the control registers. */
+	instanceData->controlRegisters = kzalloc(sizeof(struct rmi_F19_control), GFP_KERNEL);
+	if (!instanceData->controlRegisters) {
+		printk(KERN_ERR "%s: Error allocating F19 control registers.\n", __func__);
+		return -ENOMEM;
+	}
+
+	instanceData->fn19regCountForBitPerButton = (instanceData->deviceInfo->buttonCount + 7)/8;
+
+	/* Need to compute the amount of data to read since it varies with the
+	 * number of buttons */
+	instanceData->fn19ControlRegisterSize = 1  /* 1 for filter mode and button usage bits */
+		+ 2*instanceData->fn19regCountForBitPerButton  /* interrupt enable bits and single button participation bits */
+		+ 2*instanceData->deviceInfo->buttonCount  /* sensormap registers + single button sensitivity registers */
+		+ 2; /* 1 for global sensitivity adjust + 1 for global hysteresis threshold */
+
+	/* Allocate a temp memory buffer to read the control registers into */
+	fn19Control = kzalloc(instanceData->fn19ControlRegisterSize, GFP_KERNEL);
+	if (!fn19Control) {
+		printk(KERN_ERR "%s: Error allocating temp storage to read fn19 control info.\n", __func__);
+		return -ENOMEM;
+	}
+
+	/* Grab a copy of the control registers. */
+	retval = rmi_read_multiple(rmifninfo->sensor, fndescr->controlBaseAddr,
+		fn19Control, instanceData->fn19ControlRegisterSize);
+	if (retval) {
+		printk(KERN_ERR "%s: Failed to read F19 control registers.", __func__);
+		return retval;
+	}
+
+	/* Copy over control registers data to the instance data */
+	instanceData->fn19btnUsageandfilterModeOffset = 0;
+	instanceData->controlRegisters->buttonUsage = fn19Control[instanceData->fn19btnUsageandfilterModeOffset] & 0x3;
+	instanceData->controlRegisters->filterMode = fn19Control[instanceData->fn19btnUsageandfilterModeOffset] & 0xc;
+
+	/* Fill in interrupt enable registers */
+	instanceData->fn19intEnableOffset = 1;
+	instanceData->fn19intEnableLen = instanceData->fn19regCountForBitPerButton;
+	instanceData->controlRegisters->intEnableRegisters = kzalloc(instanceData->fn19intEnableLen, GFP_KERNEL);
+	if (!instanceData->controlRegisters->intEnableRegisters) {
+		printk(KERN_ERR "%s: Error allocating storage for interrupt enable control info.\n", __func__);
+		return -ENOMEM;
+	}
+	memcpy(instanceData->controlRegisters->intEnableRegisters, &fn19Control[instanceData->fn19intEnableOffset],
+		instanceData->fn19intEnableLen);
+
+	/* Fill in single button control registers */
+	instanceData->fn19singleBtnCtrlOffset = instanceData->fn19intEnableOffset + instanceData->fn19intEnableLen;
+	instanceData->fn19singleBtnCtrlLen = instanceData->fn19regCountForBitPerButton;
+	instanceData->controlRegisters->singleButtonControl = kzalloc(instanceData->fn19singleBtnCtrlLen, GFP_KERNEL);
+	if (!instanceData->controlRegisters->singleButtonControl) {
+		printk(KERN_ERR "%s: Error allocating storage for single button participation control info.\n", __func__);
+		return -ENOMEM;
+	}
+	memcpy(instanceData->controlRegisters->singleButtonControl, &fn19Control[instanceData->fn19singleBtnCtrlOffset],
+		instanceData->fn19singleBtnCtrlLen);
+
+	/* Fill in sensor map registers */
+	instanceData->fn19sensorMapCtrlOffset = instanceData->fn19singleBtnCtrlOffset + instanceData->fn19singleBtnCtrlLen;
+	instanceData->fn19sensorMapCtrlLen = instanceData->deviceInfo->buttonCount;
+	instanceData->controlRegisters->sensorMap = kzalloc(instanceData->fn19sensorMapCtrlLen, GFP_KERNEL);
+	if (!instanceData->controlRegisters->sensorMap) {
+		printk(KERN_ERR "%s: Error allocating storage for sensor map control info.\n", __func__);
+		return -ENOMEM;
+	}
+	memcpy(instanceData->controlRegisters->sensorMap, &fn19Control[instanceData->fn19sensorMapCtrlOffset],
+		instanceData->fn19sensorMapCtrlLen);
+
+	/* Fill in single button sensitivity registers */
+	instanceData->fn19singleBtnSensOffset = instanceData->fn19sensorMapCtrlOffset + instanceData->fn19sensorMapCtrlLen;
+	instanceData->fn19singleBtnSensLen = instanceData->deviceInfo->buttonCount;
+	instanceData->controlRegisters->singleButtonSensitivity = kzalloc(instanceData->fn19singleBtnSensLen, GFP_KERNEL);
+	if (!instanceData->controlRegisters->intEnableRegisters) {
+		printk(KERN_ERR "%s: Error allocating storage for single button sensitivity control info.\n", __func__);
+		return -ENOMEM;
+	}
+	memcpy(instanceData->controlRegisters->singleButtonSensitivity, &fn19Control[instanceData->fn19singleBtnSensOffset],
+		instanceData->fn19singleBtnSensLen);
+
+	/* Fill in global sensitivity adjustment and global hysteresis threshold values */
+	instanceData->fn19globalSensOffset = instanceData->fn19singleBtnSensOffset + instanceData->fn19singleBtnSensLen;
+	instanceData->fn19globalHystThreshOffset = instanceData->fn19globalSensOffset + 1;
+	instanceData->controlRegisters->globalSensitivityAdjustment = fn19Control[instanceData->fn19globalSensOffset] & 0x1f;
+	instanceData->controlRegisters->globalHysteresisThreshold = fn19Control[instanceData->fn19globalHystThreshOffset] & 0x0f;
+
+	/* Free up temp storage that held copy of control registers */
+	kfree(fn19Control);
+
+	return 0;
+}
+
+int FN_19_detect(struct rmi_function_info *rmifninfo,
+	struct rmi_function_descriptor *fndescr, unsigned int interruptCount)
+{
+	unsigned char fn19queries[2];
+	int retval = 0;
+	int i;
+	struct f19_instance_data *instanceData;
+	int fn19InterruptOffset;
+
+	printk(KERN_DEBUG "%s: RMI4 F19 detect\n", __func__);
+
+	instanceData = kzalloc(sizeof(struct f19_instance_data), GFP_KERNEL);
+	if (!instanceData) {
+		printk(KERN_ERR "%s: Error allocating F19 instance data.\n", __func__);
+		return -ENOMEM;
+	}
+	instanceData->deviceInfo = kzalloc(sizeof(struct rmi_F19_query), GFP_KERNEL);
+	if (!instanceData->deviceInfo) {
+		printk(KERN_ERR "%s: Error allocating F19 device query.\n", __func__);
+		return -ENOMEM;
+	}
+	rmifninfo->fndata = instanceData;
+
+	/* Store addresses - used elsewhere to read data,
+	* control, query, etc. */
+	rmifninfo->funcDescriptor.queryBaseAddr = fndescr->queryBaseAddr;
+	rmifninfo->funcDescriptor.commandBaseAddr = fndescr->commandBaseAddr;
+	rmifninfo->funcDescriptor.controlBaseAddr = fndescr->controlBaseAddr;
+	rmifninfo->funcDescriptor.dataBaseAddr = fndescr->dataBaseAddr;
+	rmifninfo->funcDescriptor.interruptSrcCnt = fndescr->interruptSrcCnt;
+	rmifninfo->funcDescriptor.functionNum = fndescr->functionNum;
+
+	rmifninfo->numSources = fndescr->interruptSrcCnt;
+
+	/* need to get number of fingers supported, data size, etc. -
+	to be used when getting data since the number of registers to
+	read depends on the number of fingers supported and data size. */
+	retval = rmi_read_multiple(rmifninfo->sensor, fndescr->queryBaseAddr, fn19queries,
+			sizeof(fn19queries));
+	if (retval) {
+		printk(KERN_ERR "%s: RMI4 F19 detect: "
+			"Could not read function query registers 0x%x\n",
+			__func__,  rmifninfo->funcDescriptor.queryBaseAddr);
+		return retval;
+	}
+
+	/* Extract device data. */
+	instanceData->deviceInfo->configurable = fn19queries[0] & 0x01;
+	instanceData->deviceInfo->hasSensitivityAdjust = fn19queries[0] & 0x02;
+	instanceData->deviceInfo->hasHysteresisThreshold = fn19queries[0] & 0x04;
+	instanceData->deviceInfo->buttonCount = fn19queries[1] & 0x01F;
+	printk(KERN_DEBUG "%s: F19 device - %d buttons...", __func__, instanceData->deviceInfo->buttonCount);
+
+	/* Need to get interrupt info to be used later when handling
+	interrupts. */
+	rmifninfo->interruptRegister = interruptCount/8;
+
+	/* loop through interrupts for each source in fn $11 and or in a bit
+	to the interrupt mask for each. */
+	fn19InterruptOffset = interruptCount % 8;
+
+	for (i = fn19InterruptOffset;
+			i < ((fndescr->interruptSrcCnt & 0x7) + fn19InterruptOffset);
+			i++)
+		rmifninfo->interruptMask |= 1 << i;
+
+	/* Figure out just how much data we'll need to read. */
+	instanceData->buttonDown = kcalloc(instanceData->deviceInfo->buttonCount, sizeof(bool), GFP_KERNEL);
+	if (!instanceData->buttonDown) {
+		printk(KERN_ERR "%s: Error allocating F19 button state buffer.\n", __func__);
+		return -ENOMEM;
+	}
+
+	instanceData->buttonDataBufferSize = (instanceData->deviceInfo->buttonCount + 7) / 8;
+	instanceData->buttonDataBuffer = kcalloc(instanceData->buttonDataBufferSize, sizeof(unsigned char), GFP_KERNEL);
+	if (!instanceData->buttonDataBuffer) {
+		printk(KERN_ERR "%s: Failed to allocate button data buffer.", __func__);
+		return -ENOMEM;
+	}
+
+	instanceData->buttonMap = kcalloc(instanceData->deviceInfo->buttonCount, sizeof(unsigned char),  GFP_KERNEL);
+	if (!instanceData->buttonMap) {
+		printk(KERN_ERR "%s: Error allocating F19 button map.\n", __func__);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < instanceData->deviceInfo->buttonCount; i++)
+		instanceData->buttonMap[i] = BTN_0 + i; /* default values */
+
+	/* Grab the control register info. */
+	retval = getControlRegisters(rmifninfo, fndescr);
+	if (retval) {
+		printk(KERN_ERR "%s: Error %d getting fn19 control register info.\n", __func__, retval);
+		return retval;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(FN_19_detect);
+
+static ssize_t rmi_f19_buttonCount_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f19_instance_data *instance_data = (struct f19_instance_data *)fn->rfi->fndata;
+
+	return sprintf(buf, "%u\n", instance_data->deviceInfo->buttonCount);
+}
+
+static ssize_t rmi_f19_buttonCount_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	/* Not allowed. */
+	return -EPERM;
+}
+
+static ssize_t rmi_f19_buttonMap_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f19_instance_data *instance_data = (struct f19_instance_data *)fn->rfi->fndata;
+	int i, len, totalLen = 0;
+
+	/* loop through each button map value and copy it's string representation into buf */
+	for (i = 0; i < instance_data->deviceInfo->buttonCount; i++) {
+		/* get next button mapping value and write it to buf */
+		len = sprintf(buf, "%u ", instance_data->buttonMap[i]);
+		/* bump up ptr to next location in buf if the sprintf was valid */
+		if (len > 0) {
+			buf += len;
+			totalLen += len;
+		}
+	}
+
+	return totalLen;
+}
+
+static ssize_t rmi_f19_buttonMap_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f19_instance_data *instance_data = (struct f19_instance_data *)fn->rfi->fndata;
+	unsigned int button;
+	int i;
+	int retval = count;
+	int buttonCount = 0;
+	unsigned char *tmpButtonMap;
+
+	/* Do validation on the button map data passed in. */
+	/* Store button mappings into a temp buffer and then verify button count
+	and data prior to clearing out old button mappings and storing the new ones. */
+	tmpButtonMap = kzalloc(instance_data->deviceInfo->buttonCount, GFP_KERNEL);
+	if (!tmpButtonMap) {
+		printk(KERN_ERR "%s: Error allocating temp button map.\n", __func__);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < instance_data->deviceInfo->buttonCount && *buf != 0; i++) {
+		/* get next button mapping value and store and bump up to point to next item in buf */
+		sscanf(buf, "%u", &button);
+
+		/* Make sure the key is a valid key */
+		if (button > KEY_MAX) {
+			printk(KERN_ERR "%s: Error - button map for button %d is not a valid value 0x%x.\n",
+				__func__, i, button);
+			retval = -EINVAL;
+			goto err_ret;
+		}
+
+		tmpButtonMap[i] = button;
+		buttonCount++;
+
+		/* bump up buf to point to next item to read */
+		while (*buf != 0) {
+			buf++;
+			if (*(buf-1) == ' ')
+				break;
+		}
+	}
+
+	/* Make sure the button count matches */
+	if (buttonCount != instance_data->deviceInfo->buttonCount) {
+		printk(KERN_ERR "%s: Error - button map count of %d doesn't match device button count of %d.\n"
+			 , __func__, buttonCount, instance_data->deviceInfo->buttonCount);
+		retval = -EINVAL;
+		goto err_ret;
+	}
+
+	/* Clear out old buttonMap data */
+	memset(instance_data->buttonMap, 0, buttonCount);
+
+	/* Loop through the temp buffer and copy the button event and set the key bit for the new mapping. */
+	for (i = 0; i < buttonCount; i++) {
+		instance_data->buttonMap[i] = tmpButtonMap[1];
+		set_bit(instance_data->buttonMap[i], fn->input->keybit);
+	}
+
+err_ret:
+	kfree(tmpButtonMap);
+
+	return retval;
+}
diff --git a/drivers/input/touchscreen/synaptics/rmi_f19.h b/drivers/input/touchscreen/synaptics/rmi_f19.h
new file mode 100644
index 0000000..41f3e4d
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics/rmi_f19.h
@@ -0,0 +1,43 @@
+/**
+ *
+ * Synaptics Register Mapped Interface (RMI4) Function $11 header.
+ * Copyright (c) 2007 - 2010, Synaptics Incorporated
+ *
+ * For every RMI4 function that has a data source - like 2D sensors,
+ * buttons, LEDs, GPIOs, etc. - the user will create a new rmi_function_xx.c
+ * file and add these functions to perform the config(), init(), report()
+ * and detect() functionality. The function pointers are then srored under
+ * the RMI function info and these functions will automatically be called by
+ * the global config(), init(), report() and detect() functions that will
+ * loop through all data sources and call the data sources functions using
+ * these functions pointed to by the function ptrs.
+ */
+/*
+ * This file is licensed under the GPL2 license.
+ *
+ *#############################################################################
+ * GPL
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ *#############################################################################
+ */
+#ifndef _RMI_FUNCTION_19_H
+#define _RMI_FUNCTION_19_H
+
+void FN_19_inthandler(struct rmi_function_info *rmifninfo,
+	unsigned int assertedIRQs);
+int FN_19_config(struct rmi_function_info *rmifninfo);
+int FN_19_init(struct rmi_function_device *function_device);
+int FN_19_detect(struct rmi_function_info *rmifninfo,
+		struct rmi_function_descriptor *fndescr,
+		unsigned int interruptCount);
+/* No attention function for Fn $19 */
+#endif
diff --git a/drivers/input/touchscreen/synaptics/rmi_f34.c b/drivers/input/touchscreen/synaptics/rmi_f34.c
new file mode 100644
index 0000000..26b6389
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics/rmi_f34.c
@@ -0,0 +1,557 @@
+/**
+ *
+ * Synaptics Register Mapped Interface (RMI4) Function $34 support for sensor
+ * firmware reflashing.
+ *
+ * Copyright (c) 2007 - 2011, Synaptics Incorporated
+ *
+ */
+/*
+ * This file is licensed under the GPL2 license.
+ *
+ *#############################################################################
+ * GPL
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ *#############################################################################
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/input.h>
+#include <linux/sysfs.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include "rmi_drvr.h"
+#include "rmi_bus.h"
+#include "rmi_sensor.h"
+#include "rmi_function.h"
+#include "rmi_f34.h"
+
+/* data specific to fn $34 that needs to be kept around */
+struct rmi_fn_34_data {
+	unsigned char   status;
+	unsigned char   cmd;
+	unsigned short  bootloaderid;
+	unsigned short  blocksize;
+};
+
+
+static ssize_t rmi_fn_34_status_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_34_status_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count);
+
+
+static ssize_t rmi_fn_34_cmd_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_34_cmd_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count);
+
+static ssize_t rmi_fn_34_data_read(struct file *,
+				struct kobject *kobj,
+				struct bin_attribute *attributes,
+				char *buf, loff_t pos, size_t count);
+
+static ssize_t rmi_fn_34_data_write(struct file *,
+				struct kobject *kobj,
+				struct bin_attribute *attributes,
+				char *buf, loff_t pos, size_t count);
+
+static ssize_t rmi_fn_34_bootloaderid_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_34_bootloaderid_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count);
+
+static ssize_t rmi_fn_34_blocksize_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_34_blocksize_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count);
+
+/* define the device attributes using DEVICE_ATTR macros */
+DEVICE_ATTR(status, 0444, rmi_fn_34_status_show, rmi_fn_34_status_store);  /* RO attr */
+DEVICE_ATTR(cmd, 0664, rmi_fn_34_cmd_show, rmi_fn_34_cmd_store);     /* RW attr */
+DEVICE_ATTR(bootloaderid, 0644, rmi_fn_34_bootloaderid_show, rmi_fn_34_bootloaderid_store); /* RW attr */
+DEVICE_ATTR(blocksize, 0444, rmi_fn_34_blocksize_show, rmi_fn_34_blocksize_store);    /* RO attr */
+
+
+struct bin_attribute dev_attr_data = {
+	.attr = {
+		.name = "data",
+		.mode = 0644
+	},
+	.size = 0,
+	.read = rmi_fn_34_data_read,
+	.write = rmi_fn_34_data_write,
+};
+
+/* Helper fn to convert from processor specific data to our firmware specific endianness.
+ * TODO: Should we use ntohs or something like that?
+ */
+void copyEndianAgnostic(unsigned char *dest, unsigned short src)
+{
+	dest[0] = src%0x100;
+	dest[1] = src/0x100;
+}
+
+/*.
+ * The interrupt handler for Fn $34.
+ */
+void FN_34_inthandler(struct rmi_function_info *rmifninfo,
+	unsigned int assertedIRQs)
+{
+	unsigned int status;
+	struct rmi_fn_34_data *fn34data = (struct rmi_fn_34_data *)rmifninfo->fndata;
+
+	/* Read the Fn $34 status register to see whether the previous command executed OK */
+	/* inform user space - through a sysfs param. */
+	if (rmi_read_multiple(rmifninfo->sensor, rmifninfo->funcDescriptor.dataBaseAddr+3,
+		(unsigned char *)&status, 1)) {
+		printk(KERN_ERR "%s : Could not read status from 0x%x\n",
+			__func__, rmifninfo->funcDescriptor.dataBaseAddr+3);
+		status = 0xff; /* failure */
+	}
+
+	/* set a sysfs value that the user mode can read - only upper 4 bits are the status */
+	fn34data->status = status & 0xf0; /* successful is $80, anything else is failure */
+}
+EXPORT_SYMBOL(FN_34_inthandler);
+
+void FN_34_attention(struct rmi_function_info *rmifninfo)
+{
+
+}
+EXPORT_SYMBOL(FN_34_attention);
+
+int FN_34_config(struct rmi_function_info *rmifninfo)
+{
+	pr_debug("%s: RMI4 function $34 config\n", __func__);
+	return 0;
+}
+EXPORT_SYMBOL(FN_34_config);
+
+
+int FN_34_init(struct rmi_function_device *function_device)
+{
+	int retval = 0;
+	unsigned char uData[2];
+	struct rmi_function_info *rmifninfo = function_device->rfi;
+	struct rmi_fn_34_data *fn34data;
+
+	pr_debug("%s: RMI4 function $34 init\n", __func__);
+
+	/* Here we will need to set up sysfs files for Bootloader ID and Block size */
+	fn34data = kzalloc(sizeof(struct rmi_fn_34_data), GFP_KERNEL);
+	if (!fn34data) {
+		printk(KERN_ERR "%s: Error allocating memeory for rmi_fn_34_data.\n", __func__);
+		return -ENOMEM;
+	}
+	rmifninfo->fndata = (void *)fn34data;
+
+	/* set up sysfs file for Bootloader ID. */
+	if (sysfs_create_file(&function_device->dev.kobj, &dev_attr_bootloaderid.attr) < 0) {
+		printk(KERN_ERR "%s: Failed to create sysfs file for fn 34 bootloaderid.\n", __func__);
+		return -ENODEV;
+	}
+
+	/* set up sysfs file for Block Size. */
+	if (sysfs_create_file(&function_device->dev.kobj, &dev_attr_blocksize.attr) < 0) {
+		printk(KERN_ERR "%s: Failed to create sysfs file for fn 34 blocksize.\n", __func__);
+		return -ENODEV;
+	}
+
+	/* get the Bootloader ID and Block Size and store in the sysfs attributes. */
+	retval = rmi_read_multiple(rmifninfo->sensor, rmifninfo->funcDescriptor.queryBaseAddr,
+		uData, 2);
+	if (retval) {
+		printk(KERN_ERR "%s : Could not read bootloaderid from 0x%x\n",
+			__func__, function_device->function->functionQueryBaseAddr);
+		return retval;
+	}
+	/* need to convert from our firmware storage to processore specific data */
+	fn34data->bootloaderid = (unsigned int)uData[0] + (unsigned int)uData[1]*0x100;
+
+	retval = rmi_read_multiple(rmifninfo->sensor, rmifninfo->funcDescriptor.queryBaseAddr+3,
+		uData, 2);
+	if (retval) {
+		printk(KERN_ERR "%s : Could not read block size from 0x%x\n",
+			__func__, rmifninfo->funcDescriptor.queryBaseAddr+3);
+		return retval;
+	}
+	/* need to convert from our firmware storage to processor specific data */
+	fn34data->blocksize = (unsigned int)uData[0] + (unsigned int)uData[1]*0x100;
+
+	/* set up sysfs file for status. */
+	if (sysfs_create_file(&function_device->dev.kobj, &dev_attr_status.attr) < 0) {
+		printk(KERN_ERR "%s: Failed to create sysfs file for fn 34 status.\n", __func__);
+		return -ENODEV;
+	}
+
+	/* Also, sysfs will need to have a file set up to distinguish between commands - like
+	Config write/read, Image write/verify.*/
+	/* set up sysfs file for command code. */
+	if (sysfs_create_file(&function_device->dev.kobj, &dev_attr_cmd.attr) < 0) {
+		printk(KERN_ERR "%s: Failed to create sysfs file for fn 34 cmd.\n", __func__);
+		return -ENODEV;
+	}
+
+	/* We will also need a sysfs file for the image/config block to write or read.*/
+	/* set up sysfs bin file for binary data block. Since the image is already in our format
+	there is no need to convert the data for endianess. */
+	if (sysfs_create_bin_file(&function_device->dev.kobj, &dev_attr_data) < 0) {
+		printk(KERN_ERR "%s: Failed to create sysfs file for fn 34 data.\n", __func__);
+		return -ENODEV;
+	}
+
+	return retval;
+}
+EXPORT_SYMBOL(FN_34_init);
+
+int FN_34_detect(struct rmi_function_info *rmifninfo,
+	struct rmi_function_descriptor *fndescr, unsigned int interruptCount)
+{
+	int i;
+	int InterruptOffset;
+	int retval = 0;
+
+	pr_debug("%s: RMI4 function $34 detect\n", __func__);
+	if (rmifninfo->sensor == NULL) {
+		printk(KERN_ERR "%s: NULL sensor passed in!", __func__);
+		return -EINVAL;
+	}
+
+	/* Store addresses - used elsewhere to read data,
+	* control, query, etc. */
+	rmifninfo->funcDescriptor.queryBaseAddr = fndescr->queryBaseAddr;
+	rmifninfo->funcDescriptor.commandBaseAddr = fndescr->commandBaseAddr;
+	rmifninfo->funcDescriptor.controlBaseAddr = fndescr->controlBaseAddr;
+	rmifninfo->funcDescriptor.dataBaseAddr = fndescr->dataBaseAddr;
+	rmifninfo->funcDescriptor.interruptSrcCnt = fndescr->interruptSrcCnt;
+	rmifninfo->funcDescriptor.functionNum = fndescr->functionNum;
+
+	rmifninfo->numSources = fndescr->interruptSrcCnt;
+
+	/* Need to get interrupt info to be used later when handling
+	interrupts. */
+	rmifninfo->interruptRegister = interruptCount/8;
+
+	/* loop through interrupts for each source and or in a bit
+	to the interrupt mask for each. */
+	InterruptOffset = interruptCount % 8;
+
+	for (i = InterruptOffset;
+		i < ((fndescr->interruptSrcCnt & 0x7) + InterruptOffset);
+		i++) {
+			rmifninfo->interruptMask |= 1 << i;
+	}
+
+	return retval;
+}
+EXPORT_SYMBOL(FN_34_detect);
+
+static ssize_t rmi_fn_34_bootloaderid_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct rmi_fn_34_data *fn34data = (struct rmi_fn_34_data *)fn->rfi->fndata;
+
+	return sprintf(buf, "%u\n", fn34data->bootloaderid);
+}
+
+static ssize_t rmi_fn_34_bootloaderid_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	int error;
+	unsigned long val;
+	unsigned char uData[2];
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct rmi_fn_34_data *fn34data = (struct rmi_fn_34_data *)fn->rfi->fndata;
+
+	/* need to convert the string data to an actual value */
+	error = strict_strtoul(buf, 10, &val);
+
+	if (error)
+		return error;
+
+	fn34data->bootloaderid = val;
+
+	/* Write the Bootloader ID key data back to the first two Block Data registers
+	(F34_Flash_Data2.0 and F34_Flash_Data2.1).*/
+	copyEndianAgnostic(uData, (unsigned short)val);
+	error = rmi_write_multiple(fn->sensor, fn->function->functionDataBaseAddr,
+		uData, 2);
+	if (error) {
+		printk(KERN_ERR "%s : Could not write bootloader id to 0x%x\n",
+			__func__, fn->function->functionDataBaseAddr);
+		return error;
+	}
+
+	return count;
+}
+
+static ssize_t rmi_fn_34_blocksize_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct rmi_fn_34_data *fn34data = (struct rmi_fn_34_data *)fn->rfi->fndata;
+
+	return sprintf(buf, "%u\n", fn34data->blocksize);
+}
+
+static ssize_t rmi_fn_34_blocksize_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	/* Block Size is RO so we shouldn't do anything if the
+	user space writes to the sysfs file. */
+
+	return -EPERM;
+}
+
+static ssize_t rmi_fn_34_status_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct rmi_fn_34_data *fn34data = (struct rmi_fn_34_data *)fn->rfi->fndata;
+
+	return sprintf(buf, "%u\n", fn34data->status);
+}
+
+static ssize_t rmi_fn_34_status_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	/* Status is RO so we shouldn't do anything if the user
+	app writes to the sysfs file. */
+	return -EPERM;
+}
+
+static ssize_t rmi_fn_34_cmd_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct rmi_fn_34_data *fn34data = (struct rmi_fn_34_data *)fn->rfi->fndata;
+
+	return sprintf(buf, "%u\n", fn34data->cmd);
+}
+
+static ssize_t rmi_fn_34_cmd_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct rmi_fn_34_data *fn34data = (struct rmi_fn_34_data *)fn->rfi->fndata;
+	unsigned long val;
+	unsigned char cmd;
+	int error;
+
+	/* need to convert the string data to an actual value */
+	error = strict_strtoul(buf, 10, &val);
+
+	if (error)
+		return error;
+
+	fn34data->cmd = val;
+
+	/* determine the proper command to issue.
+	*/
+	switch (val) {
+	case ENABLE_FLASH_PROG:
+		/* Issue a Flash Program Enable ($0F) command to the Flash Command
+		(F34_Flash_Data3, bits 3:0) field.*/
+		cmd = 0x0F;
+		error = rmi_write_multiple(fn->sensor, fn->function->functionDataBaseAddr+3,
+			(unsigned char *)&cmd, 1);
+		if (error) {
+			printk(KERN_ERR "%s : Could not write Flash Program Enable cmd to 0x%x\n",
+				__func__, fn->function->functionDataBaseAddr+3);
+			return error;
+		}
+		break;
+
+	case ERASE_ALL:
+		/* Issue a Erase All ($03) command to the Flash Command
+		(F34_Flash_Data3, bits 3:0) field.*/
+		cmd = 0x03;
+		error = rmi_write_multiple(fn->sensor, fn->function->functionDataBaseAddr+3,
+			(unsigned char *)&cmd, 1);
+		if (error) {
+			printk(KERN_ERR "%s : Could not write Erase All cmd to 0x%x\n",
+				__func__, fn->function->functionDataBaseAddr+3);
+			return error;
+		}
+		break;
+
+	case ERASE_CONFIG:
+		/* Issue a Erase Configuration ($07) command to the Flash Command
+		(F34_Flash_Data3, bits 3:0) field.*/
+		cmd = 0x07;
+		error = rmi_write_multiple(fn->sensor, fn->function->functionDataBaseAddr+3,
+			(unsigned char *)&cmd, 1);
+		if (error) {
+			printk(KERN_ERR "%s : Could not write Erase Configuration cmd to 0x%x\n",
+				__func__, fn->function->functionDataBaseAddr+3);
+			return error;
+		}
+		break;
+
+	case WRITE_FW_BLOCK:
+		/* Issue a Write Firmware Block ($02) command to the Flash Command
+		(F34_Flash_Data3, bits 3:0) field.*/
+		cmd = 0x02;
+		error = rmi_write_multiple(fn->sensor, fn->function->functionDataBaseAddr+3,
+			(unsigned char *)&cmd, 1);
+		if (error) {
+			printk(KERN_ERR "%s : Could not write Write Firmware Block cmd to 0x%x\n",
+				__func__, fn->function->functionDataBaseAddr+3);
+		return error;
+		}
+		break;
+
+	case WRITE_CONFIG_BLOCK:
+		/* Issue a Write Config Block ($06) command to the Flash Command
+		(F34_Flash_Data3, bits 3:0) field.*/
+		cmd = 0x06;
+		error = rmi_write_multiple(fn->sensor, fn->function->functionDataBaseAddr+3,
+			(unsigned char *)&cmd, 1);
+		if (error) {
+			printk(KERN_ERR "%s : Could not write Write Config Block cmd to 0x%x\n",
+				__func__, fn->function->functionDataBaseAddr+3);
+			return error;
+		}
+		break;
+
+	case READ_CONFIG_BLOCK:
+		/* Issue a Read Config Block ($05) command to the Flash Command
+		(F34_Flash_Data3, bits 3:0) field.*/
+		cmd = 0x05;
+		error = rmi_write_multiple(fn->sensor, fn->function->functionDataBaseAddr+3,
+			(unsigned char *)&cmd, 1);
+		if (error) {
+			printk(KERN_ERR "%s : Could not write Read Config Block cmd to 0x%x\n",
+				__func__, fn->function->functionDataBaseAddr+3);
+			return error;
+		}
+		break;
+
+	case DISABLE_FLASH_PROG:
+		/* Issue a reset command ($01) - this will reboot the sensor and ATTN will now go to
+		the Fn $01 instead of the Fn $34 since the sensor will no longer be in Flash mode. */
+		cmd = 0x01;
+		/*if ((error = rmi_write_multiple(fn->sensor, fn->sensor->sensorCommandBaseAddr,
+			(unsigned char *)&cmd, 1))) {
+			printk(KERN_ERR "%s : Could not write Reset cmd to 0x%x\n",
+				__func__, fn->sensor->sensorCommandBaseAddr);
+		return error;
+		}*/
+		break;
+
+	default:
+		pr_debug("%s: RMI4 function $34 - unknown command.\n", __func__);
+		break;
+	}
+
+	return count;
+}
+
+static ssize_t rmi_fn_34_data_read(struct file * filp,
+				struct kobject *kobj,
+				struct bin_attribute *attributes,
+				char *buf, loff_t pos, size_t count)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	int error;
+
+	/* TODO: add check for count to verify it's the correct blocksize */
+
+	/* read the data from flash into buf. */
+	/* the app layer will be blocked at reading from the sysfs file. */
+	/* when we return the count (or error if we fail) the app will resume. */
+	error = rmi_read_multiple(fn->sensor, fn->function->functionDataBaseAddr+pos,
+		(unsigned char *)buf, count);
+	if (error) {
+		printk(KERN_ERR "%s : Could not read data from 0x%llx\n",
+			__func__, fn->function->functionDataBaseAddr+pos);
+		return error;
+	}
+
+	return count;
+}
+
+static ssize_t rmi_fn_34_data_write(struct file *filp,
+				struct kobject *kobj,
+				struct bin_attribute *attributes,
+				char *buf, loff_t pos, size_t count)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct rmi_fn_34_data *fn34data = (struct rmi_fn_34_data *)fn->rfi->fndata;
+	unsigned int blocknum;
+	int error;
+
+	/* write the data from buf to flash. */
+	/* the app layer will be blocked at writing to the sysfs file. */
+	/* when we return the count (or error if we fail) the app will resume. */
+
+	/* TODO: Add check on count - if non-zero veriy it's the correct blocksize */
+
+	/* Verify that the byte offset is always aligned on a block boundary and if not
+	return an error.  We can't just use the mod operator % and do a (pos % fn34data->blocksize) because of a gcc
+	bug that results in undefined symbols.  So we have to compute it the hard
+	way.  Grumble. */
+	unsigned int remainder;
+	div_u64_rem(pos, fn34data->blocksize, &remainder);
+	if (remainder) {
+		printk(KERN_ERR "%s : Invalid byte offset of %llx leads to invalid block number.\n",
+			__func__, pos);
+		return -EINVAL;
+	}
+
+	/* Compute the block number using the byte offset (pos) and the block size.
+	once again, we can't just do a divide due to a gcc bug. */
+	blocknum = div_u64(pos, fn34data->blocksize);
+
+	/* Write the block number first */
+	error = rmi_write_multiple(fn->sensor, fn->function->functionDataBaseAddr,
+		(unsigned char *)&blocknum, 2);
+	if (error) {
+		printk(KERN_ERR "%s : Could not write block number to 0x%x\n",
+			__func__, fn->function->functionDataBaseAddr);
+		return error;
+	}
+
+	/* Write the data block - only if the count is non-zero  */
+	if (count) {
+		error = rmi_write_multiple(fn->sensor, fn->function->functionDataBaseAddr+2,
+			(unsigned char *)buf, count);
+		if (error) {
+			printk(KERN_ERR "%s : Could not write block data to 0x%x\n",
+				__func__, fn->function->functionDataBaseAddr+2);
+			return error;
+		}
+	}
+
+	return count;
+}
diff --git a/drivers/input/touchscreen/synaptics/rmi_f34.h b/drivers/input/touchscreen/synaptics/rmi_f34.h
new file mode 100644
index 0000000..48293e3
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics/rmi_f34.h
@@ -0,0 +1,50 @@
+/**
+ *
+ * Synaptics Register Mapped Interface (RMI4) Function $34 header.
+ * Copyright (c) 2007 - 2011, Synaptics Incorporated
+ *
+ * There is only one function $34 for each RMI4 sensor. This will be
+ * the function that is used to reflash the firmware and get the
+ * boot loader address and the boot image block size.
+ *
+ *
+ */
+/*
+ * This file is licensed under the GPL2 license.
+ *
+ *#############################################################################
+ * GPL
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ *#############################################################################
+ */
+#ifndef _RMI_FUNCTION_34_H
+#define _RMI_FUNCTION_34_H
+
+/* define fn $34 commands */
+#define WRITE_FW_BLOCK            2
+#define ERASE_ALL                 3
+#define READ_CONFIG_BLOCK         5
+#define WRITE_CONFIG_BLOCK        6
+#define ERASE_CONFIG              7
+#define ENABLE_FLASH_PROG         15
+#define DISABLE_FLASH_PROG        16
+
+void FN_34_inthandler(struct rmi_function_info *rmifninfo,
+	unsigned int assertedIRQs);
+int FN_34_config(struct rmi_function_info *rmifninfo);
+int FN_34_init(struct rmi_function_device *function_device);
+int FN_34_detect(struct rmi_function_info *rmifninfo,
+		struct rmi_function_descriptor *fndescr,
+		unsigned int interruptCount);
+void FN_34_attention(struct rmi_function_info *rmifninfo);
+
+#endif
diff --git a/drivers/input/touchscreen/synaptics/rmi_function.c b/drivers/input/touchscreen/synaptics/rmi_function.c
new file mode 100644
index 0000000..4a029f7
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics/rmi_function.c
@@ -0,0 +1,326 @@
+/**
+ * Synaptics Register Mapped Interface (RMI4) - RMI Function Module.
+ * Copyright (C) 2007 - 2011, Synaptics Incorporated
+ *
+ */
+/*
+ * This file is licensed under the GPL2 license.
+ *
+ *#############################################################################
+ * GPL
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ *#############################################################################
+ */
+
+static const char functionname[10] = "fn";
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/hrtimer.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+
+#include "rmi_drvr.h"
+#include "rmi_function.h"
+#include "rmi_bus.h"
+#include "rmi_sensor.h"
+#include "rmi_f01.h"
+#include "rmi_f05.h"
+#include "rmi_f11.h"
+#include "rmi_f19.h"
+#include "rmi_f34.h"
+
+/* Each time a new RMI4 function support is added the developer needs to
+bump the number of supported functions and add the info for
+that RMI4 function to the array along with pointers to the report,
+config, init and detect functions that they coded in rmi_fxx.c
+and rmi_fxx.h - where xx is the RMI4 function number in hex for the new
+RMI4 data source function. The information for the RMI4 functions is
+obtained from the RMI4 specification document.
+ */
+#define rmi4_num_supported_data_src_fns 5
+
+/* supported RMI4 functions list - controls what we
+ * will provide support for - if it's not in the list then
+ * the developer needs to add support functions for it.*/
+static LIST_HEAD(fns_list);
+static DEFINE_MUTEX(fns_mutex);
+
+/* NOTE: Developer - add in any new RMI4 fn data info - function number
+ * and ptrs to report, config, init and detect functions.  This data is
+ * used to point to the functions that need to be called to config, init,
+ * detect and report data for the new RMI4 function. Refer to the RMI4
+ * specification for information on RMI4 functions.
+ */
+/* TODO: This will eventually go away, and each function will be an independent
+ * module. */
+static struct rmi_functions_data
+	rmi4_supported_data_src_functions[rmi4_num_supported_data_src_fns] = {
+	/* Fn $11 - 2D sensing */
+	{.functionNumber = 0x11, .inthandlerFn = FN_11_inthandler, .configFn = FN_11_config, .initFn = FN_11_init, .detectFn = FN_11_detect, .attnFn = NULL},
+	/* Fn $01 - device control */
+	{.functionNumber = 0x01, .inthandlerFn = FN_01_inthandler, .configFn = FN_01_config, .initFn = FN_01_init, .detectFn = FN_01_detect, .attnFn = FN_01_attention},
+	/* Fn $05 - analog report */
+	{.functionNumber = 0x05, .inthandlerFn = FN_05_inthandler, .configFn = FN_05_config, .initFn = FN_05_init, .detectFn = FN_05_detect, .attnFn = NULL},
+	/* Fn $19 - buttons */
+	{.functionNumber = 0x19, .inthandlerFn = FN_19_inthandler, .configFn = FN_19_config, .initFn = FN_19_init, .detectFn = FN_19_detect, .attnFn = NULL},
+	/* Fn $34 - firmware reflash */
+	{.functionNumber = 0x34, .inthandlerFn = FN_34_inthandler, .configFn = FN_34_config, .initFn = FN_34_init, .detectFn = FN_34_detect, .attnFn = FN_34_attention},
+};
+
+
+/* This function is here to provide a way for external modules to access the
+ * functions list.  It will try to find a matching function base on the passed
+ * in RMI4 function number and return  the pointer to the struct rmi_functions
+ * if a match is found or NULL if not found.
+ */
+struct rmi_functions *rmi_find_function(int functionNum)
+{
+	struct rmi_functions *fn;
+	bool found = false;
+
+	list_for_each_entry(fn, &fns_list, link) {
+		if (functionNum == fn->functionNum) {
+			found = true;
+			break;
+		}
+	}
+
+	if (!found)
+		return NULL;
+	else
+		return fn;
+}
+EXPORT_SYMBOL(rmi_find_function);
+
+
+static void rmi_function_config(struct rmi_function_device *function)
+{
+	printk(KERN_DEBUG "%s: rmi_function_config", __func__);
+
+}
+
+#if 0 /* This may not be needed anymore. */
+/**
+ * This is the probe function passed to the RMI4 subsystem that gives us a
+ * chance to recognize an RMI4 function.
+ */
+static int rmi_function_probe(struct rmi_function_driver *function)
+{
+	struct rmi_phys_driver *rpd;
+
+	rpd = function->rpd;
+
+	if (!rpd) {
+		printk(KERN_ERR "%s: Invalid rmi physical driver - null ptr.", __func__);
+		return 0;
+	}
+
+	return 1;
+}
+#endif
+
+/** Just a stub for now.
+ */
+static int rmi_function_suspend(struct device *dev, pm_message_t state)
+{
+	printk(KERN_INFO "%s: function suspend called.", __func__);
+	return 0;
+}
+
+/** Just a stub for now.
+ */
+static int rmi_function_resume(struct device *dev)
+{
+	printk(KERN_INFO "%s: function resume called.", __func__);
+	return 0;
+}
+
+int rmi_function_register_driver(struct rmi_function_driver *drv, int fnNumber)
+{
+	int retval;
+	char *drvrname;
+
+	printk(KERN_INFO "%s: Registering function driver for F%02x.\n", __func__, fnNumber);
+
+	retval = 0;
+
+	/* Create a function device and function driver for this Fn */
+	drvrname = kzalloc(sizeof(functionname) + 4, GFP_KERNEL);
+	if (!drvrname) {
+		printk(KERN_ERR "%s: Error allocating memeory for rmi_function_driver name.\n", __func__);
+		return -ENOMEM;
+	}
+	sprintf(drvrname, "fn%02x", fnNumber);
+
+	drv->drv.name = drvrname;
+	drv->module = drv->drv.owner;
+
+	drv->drv.suspend = rmi_function_suspend;
+	drv->drv.resume = rmi_function_resume;
+
+	/* register the sensor driver */
+	retval = driver_register(&drv->drv);
+	if (retval) {
+		printk(KERN_ERR "%s: Failed driver_register %d\n",
+			__func__, retval);
+	}
+
+	return retval;
+}
+EXPORT_SYMBOL(rmi_function_register_driver);
+
+void rmi_function_unregister_driver(struct rmi_function_driver *drv)
+{
+	printk(KERN_INFO "%s: Unregistering function driver.\n", __func__);
+
+	driver_unregister(&drv->drv);
+}
+EXPORT_SYMBOL(rmi_function_unregister_driver);
+
+int rmi_function_register_device(struct rmi_function_device *function_device, int fnNumber)
+{
+	struct input_dev *input;
+	int retval;
+
+	printk(KERN_INFO "%s: Registering function device for F%02x.\n", __func__, fnNumber);
+
+	retval = 0;
+
+	/* make name - fn11, fn19, etc. */
+	dev_set_name(&function_device->dev, "%sfn%02x", function_device->sensor->drv.name, fnNumber);
+	dev_set_drvdata(&function_device->dev, function_device);
+	retval = device_register(&function_device->dev);
+	if (retval) {
+		printk(KERN_ERR "%s:  Failed device_register for function device.\n",
+			__func__);
+		return retval;
+	}
+
+	input = input_allocate_device();
+	if (input == NULL) {
+		printk(KERN_ERR "%s:  Failed to allocate memory for a "
+			"new input device.\n",
+			__func__);
+		return -ENOMEM;
+	}
+
+	input->name = dev_name(&function_device->dev);
+	input->phys = "rmi_function";
+	function_device->input = input;
+
+
+	/* init any input specific params for this function */
+	function_device->rmi_funcs->init(function_device);
+
+	retval = input_register_device(input);
+
+	if (retval) {
+		printk(KERN_ERR "%s:  Failed input_register_device.\n",
+			__func__);
+		return retval;
+	}
+
+
+	rmi_function_config(function_device);
+
+	return retval;
+}
+EXPORT_SYMBOL(rmi_function_register_device);
+
+void rmi_function_unregister_device(struct rmi_function_device *dev)
+{
+	printk(KERN_INFO "%s: Unregistering function device.n", __func__);
+
+	input_unregister_device(dev->input);
+	device_unregister(&dev->dev);
+}
+EXPORT_SYMBOL(rmi_function_unregister_device);
+
+static int __init rmi_function_init(void)
+{
+	struct rmi_functions_data *rmi4_fn;
+	int i;
+
+	printk(KERN_DEBUG "%s: RMI Function Init\n", __func__);
+
+	/* Initialize global list of RMI4 Functions.
+	We need to add the supported RMI4 funcions so that we will have
+	pointers to the associated functions for init, config, report and
+	detect. See rmi.h for more details. The developer will add a new
+	RMI4 function number in the array in rmi_drvr.h, then add a new file to
+	the build (called rmi_fXX.c where XX is the hex number for
+	the added RMI4 function). The rest should be automatic.
+	*/
+
+	/* for each function number defined in rmi.h creat a new rmi_function
+	struct and initialize the pointers to the servicing functions and then
+	add it into the global list for function support.
+	*/
+	for (i = 0; i < rmi4_num_supported_data_src_fns; i++) {
+		/* Add new rmi4 function struct to list */
+		struct rmi_functions *fn = kzalloc(sizeof(*fn), GFP_KERNEL);
+		if (!fn) {
+			printk(KERN_ERR "%s: could not allocate memory "
+				"for rmi_function struct for function 0x%x\n",
+				__func__,
+				rmi4_supported_data_src_functions[i].functionNumber);
+			return -ENOMEM;
+		} else {
+
+			rmi4_fn = &rmi4_supported_data_src_functions[i];
+			fn->functionNum = rmi4_fn->functionNumber;
+			/* Fill in ptrs to functions. The functions are
+			linked in from a file called rmi_fxx.c
+			where xx is the hex number of the RMI4 function
+			from the RMI4 spec. Also, the function prototypes
+			need to be added to rmi_fxx.h - also where
+			xx is the hex number of the RMI4 function.  So
+			that you don't get compile errors and that new
+			header needs to be included in the rmi_function.h
+			*/
+			fn->inthandler = rmi4_fn->inthandlerFn;
+			fn->config = rmi4_fn->configFn;
+			fn->init =   rmi4_fn->initFn;
+			fn->detect = rmi4_fn->detectFn;
+			fn->attention = rmi4_fn->attnFn;
+
+			/* Add the new fn to the global list */
+			mutex_lock(&fns_mutex);
+			list_add_tail(&fn->link, &fns_list);
+			mutex_unlock(&fns_mutex);
+		}
+	}
+
+	return 0;
+}
+
+static void __exit rmi_function_exit(void)
+{
+	printk(KERN_DEBUG "%s: RMI Function Exit\n", __func__);
+}
+
+
+module_init(rmi_function_init);
+module_exit(rmi_function_exit);
+
+MODULE_AUTHOR("Synaptics, Inc.");
+MODULE_DESCRIPTION("RMI4 Function Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/synaptics/rmi_function.h b/drivers/input/touchscreen/synaptics/rmi_function.h
new file mode 100644
index 0000000..801609b
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics/rmi_function.h
@@ -0,0 +1,213 @@
+/**
+ *
+ * Synaptics Register Mapped Interface (RMI4) Function Device Header File.
+ * Copyright (c) 2007 - 2011, Synaptics Incorporated
+ *
+ *
+ */
+/*
+ * This file is licensed under the GPL2 license.
+ *
+ *#############################################################################
+ * GPL
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ *#############################################################################
+ */
+
+#ifndef _RMI_FUNCTION_H
+#define _RMI_FUNCTION_H
+
+#include <linux/input.h>
+#include <linux/device.h>
+
+
+/* For each function present on the RMI device, there will be a corresponding
+ * entry in the functions list of the rmi_sensor_driver structure.  This entry
+ * gives information about the number of data sources and the number of data
+ * registers associated with the function.
+ */
+struct rmi_function_info {
+	/* The sensor this function belongs to.
+	*/
+	struct rmi_sensor_driver *sensor;
+
+	/* A device associated with this function.
+	*/
+	struct rmi_function_device *function_device;
+
+	unsigned char functionNum;
+
+	/* This is the number of data sources associated with the function.*/
+	unsigned char numSources;
+
+	/* This is the number of data registers to read.*/
+	unsigned char dataRegBlockSize;
+
+	/* This is the interrupt register and mask - needed for enabling the
+	*  interrupts and for checking what source had caused the attention line
+	* interrupt.
+	*/
+	unsigned char interruptRegister;
+	unsigned char interruptMask;
+
+	/* This is the RMI function descriptor associated with this function.
+	*  It contains the Base addresses for the functions query, command,
+	*  control, and data registers.
+	*/
+	struct rmi_function_descriptor funcDescriptor;
+
+	/* pointer to data specific to a functions implementation. */
+	void *fndata;
+
+	/* A list of the function information.
+	*  This list uses the standard kernel linked list implementation.
+	*  Documentation on on how to use it can be found at
+	*  http://isis.poly.edu/kulesh/stuff/src/klist/.
+	*/
+	struct list_head link;
+};
+
+
+/* This struct is for creating a list of RMI4 functions that have data sources
+associated with them. This is to facilitate adding new support for other
+data sources besides 2D sensors.
+To add a new data source support, the developer will create a new file
+and add these 4 functions below with FN$## in front of the names - where
+## is the hex number for the function taken from the RMI4 specification.
+
+The function number will be associated with this and later will be used to
+match the RMI4 function to the 4 functions for that RMI4 function number.
+The user will also have to add code that adds the new rmi_functions item
+to the global list of RMI4 functions and stores the pointers to the 4
+functions in the function pointers.
+ */
+struct rmi_functions {
+	unsigned char functionNum;
+
+	/* Pointers to function specific functions for interruptHandler, config, init
+	, detect and attention. */
+	/* These ptrs. need to be filled in for every RMI4 function that has
+	data source(s) associated with it - like fn $11 (2D sensors),
+	fn $19 (buttons), etc. Each RMI4 function that has data sources
+	will be added into a list that is used to match the function
+	number against the number stored here.
+	*/
+	/* The sensor implementation will call this whenever and IRQ is
+	* dispatched that this function is interested in.
+	*/
+	void (*inthandler)(struct rmi_function_info *rfi, unsigned int assertedIRQs);
+
+	int (*config)(struct rmi_function_info *rmifninfo);
+	int (*init)(struct rmi_function_device *function_device);
+	int (*detect)(struct rmi_function_info *rmifninfo,
+		struct rmi_function_descriptor *fndescr,
+		unsigned int interruptCount);
+	/** If this is non-null, the sensor implemenation will call this
+	* whenever the ATTN line is asserted.
+	*/
+	void (*attention)(struct rmi_function_info *rmifninfo);
+
+
+	/* Standard kernel linked list implementation.
+	* Documentation on how to use it can be found at
+	* http://isis.poly.edu/kulesh/stuff/src/klist/.
+	*/
+	struct list_head link;
+};
+
+
+typedef void(*inthandlerFuncPtr)(struct rmi_function_info *rfi, unsigned int assertedIRQs);
+typedef int(*configFuncPtr)(struct rmi_function_info *rmifninfo);
+typedef int(*initFuncPtr)(struct rmi_function_device *function_device);
+typedef int(*detectFuncPtr)(struct rmi_function_info *rmifninfo,
+		struct rmi_function_descriptor *fndescr,
+		unsigned int interruptCount);
+typedef	void (*attnFuncPtr)(struct rmi_function_info *rmifninfo);
+
+struct rmi_functions_data {
+	int functionNumber;
+	inthandlerFuncPtr inthandlerFn;
+	configFuncPtr configFn;
+	initFuncPtr initFn;
+	detectFuncPtr detectFn;
+	attnFuncPtr attnFn;
+};
+
+
+struct rmi_functions *rmi_find_function(int functionNum);
+int rmi_functions_init(struct input_dev *inputdev);
+
+struct rmi_function_driver {
+	struct module *module;
+	struct device_driver drv;
+
+	/* Probe Function
+	*  This function is called to give the function driver layer an
+	*  opportunity to claim an RMI function.
+	*/
+	int (*probe)(struct rmi_function_driver *function);
+	/* Config Function
+	*  This function is called after a successful probe.  It gives the
+	*  function driver an opportunity to query and/or configure an RMI
+	*  function before data starts flowing.
+	*/
+	void (*config)(struct rmi_function_driver *function);
+
+	unsigned short functionQueryBaseAddr; /* RMI4 function control */
+	unsigned short functionControlBaseAddr;
+	unsigned short functionCommandBaseAddr;
+	unsigned short functionDataBaseAddr;
+	unsigned int interruptRegisterOffset; /* offset from start of interrupt registers */
+	unsigned int interruptMask;
+
+	/* pointer to the corresponding phys driver info for this sensor */
+	/* The phys driver has the pointers to read, write, etc. */
+	/* Probably don't need it here - used down in bus driver and sensor driver */
+	struct rmi_phys_driver *rpd;
+
+	/* Standard kernel linked list implementation.
+	*  Documentation on how to use it can be found at
+	*  http://isis.poly.edu/kulesh/stuff/src/klist/.
+	*/
+	struct list_head function_drivers; /* link function drivers into list */
+};
+
+struct rmi_function_device {
+	struct rmi_function_driver *function;
+	struct device dev;
+	struct input_dev *input;
+	struct rmi_sensor_driver *sensor; /* need this to be bound to phys driver layer */
+
+	/* the function ptrs to the config, init, detect and
+	report fns for this rmi function device. */
+	struct rmi_functions *rmi_funcs;
+	struct rmi_function_info *rfi;
+
+	/** An RMI sensor might actually have several IRQ registers -
+	* this tells us which IRQ register this function is interested in.
+	*/
+	unsigned int irqRegisterSet;
+
+	/** This is a mask of the IRQs the function is interested in.
+	*/
+	unsigned int irqMask;
+
+	/* Standard kernel linked list implementation.
+	*  Documentation on how to use it can be found at
+	*  http://isis.poly.edu/kulesh/stuff/src/klist/.
+	*/
+	struct list_head functions; /* link functions into list */
+};
+
+int rmi_function_register_device(struct rmi_function_device *dev, int fnNumber);
+
+#endif
diff --git a/drivers/input/touchscreen/synaptics/rmi_i2c.c b/drivers/input/touchscreen/synaptics/rmi_i2c.c
new file mode 100644
index 0000000..1932b9b
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics/rmi_i2c.c
@@ -0,0 +1,633 @@
+/**
+ *
+ * Synaptics Register Mapped Interface (RMI4) I2C Physical Layer Driver.
+ * Copyright (c) 2007-2011, Synaptics Incorporated
+ *
+ */
+/*
+ * This file is licensed under the GPL2 license.
+ *
+ *#############################################################################
+ * GPL
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ *#############################################################################
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/input/rmi_platformdata.h>
+#include <linux/input/rmi_i2c.h>
+
+#include "rmi_drvr.h"
+
+#define DRIVER_NAME "rmi4_ts"
+
+#define DEVICE_NAME "rmi4_ts"
+
+/* Used to lock access to the page address.*/
+/* TODO: for multiple device support will need a per-device mutex */
+static DEFINE_MUTEX(page_mutex);
+
+
+static const struct i2c_device_id rmi_i2c_id_table[] = {
+	{ DEVICE_NAME, 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, rmi_i2c_id_table);
+
+
+/* Used to count the number of I2C modules we get.
+ */
+static int device_count;
+
+
+/*
+ * This is the data kept on a per instance (client) basis.  This data is
+ * always accessible by using the container_of() macro of the various elements
+ * inside.
+ */
+struct instance_data {
+	int instance_no;
+	int irq;
+	struct rmi_phys_driver rmiphysdrvr;
+	struct i2c_client *i2cclient; /* pointer to i2c_client for later use in
+					read, write, read_multiple, etc. */
+	int page;
+};
+
+/*
+ * RMI devices have 16-bit addressing, but some of the physical
+ * implementations (like SMBus) only have 8-bit addressing.  So RMI implements
+ * a page address at 0xff of every page so we can reliable page addresses
+ * every 256 registers.  This function sets the page.
+ *
+ * The page_mutex lock must be held when this function is entered.
+ *
+ * param[in] id - The pointer to the instance_data struct
+ * param[in] page - The new page address.
+ * returns zero on success, non-zero on failure.
+ */
+/** Writing to page select is giving errors in some configurations.  It's
+ * not needed for basic operation, so we've turned it off for the moment.
+ */
+#if	defined(USE_PAGESELECT)
+int
+rmi_set_page(struct instance_data *instancedata, unsigned int page)
+{
+	char txbuf[2];
+	int retval;
+	txbuf[0] = 0xff;
+	txbuf[1] = page;
+	retval = i2c_master_send(instancedata->i2cclient, txbuf, 2);
+	if (retval != 2) {
+		dev_err(&instancedata->i2cclient->dev,
+				"%s: Set page failed: %d.", __func__, retval);
+	} else {
+		retval = 0;
+		instancedata->page = page;
+	}
+	return retval;
+}
+#else
+int
+rmi_set_page(struct instance_data *instancedata, unsigned int page)
+{
+	return 0;
+}
+#endif
+
+/*
+ * Read a single register through i2c.
+ *
+ * param[in] pd - The pointer to the rmi_phys_driver struct
+ * param[in] address - The address at which to start the data read.
+ * param[out] valp - Pointer to the buffer where the data will be stored.
+ * returns zero upon success (with the byte read in valp), non-zero upon error.
+ */
+static int
+rmi_i2c_read(struct rmi_phys_driver *physdrvr, unsigned short address, char *valp)
+{
+	struct instance_data *instancedata =
+		container_of(physdrvr, struct instance_data, rmiphysdrvr);
+
+	char txbuf[2];
+	int retval = 0;
+	int retry_count = 0;
+
+	/* Can't have anyone else changing the page behind our backs */
+	mutex_lock(&page_mutex);
+
+	if (((address >> 8) & 0xff) != instancedata->page) {
+		/* Switch pages */
+		retval = rmi_set_page(instancedata, ((address >> 8) & 0xff));
+		if (retval)
+			goto exit;
+	}
+
+retry:
+	txbuf[0] = address & 0xff;
+	retval = i2c_master_send(instancedata->i2cclient, txbuf, 1);
+
+	if (retval != 1) {
+		dev_err(&instancedata->i2cclient->dev, "%s: Write fail: %d\n",
+				__func__, retval);
+		goto exit;
+	}
+	retval = i2c_master_recv(instancedata->i2cclient, txbuf, 1);
+
+	if (retval != 1) {
+		if (++retry_count == 5) {
+			dev_err(&instancedata->i2cclient->dev,
+					"%s: Read of 0x%04x fail: %d\n",
+					__func__, address, retval);
+		} else {
+			mdelay(10);
+			rmi_set_page(instancedata, ((address >> 8) & 0xff));
+			goto retry;
+		}
+	} else {
+		retval = 0;
+		*valp = txbuf[0];
+	}
+exit:
+
+	mutex_unlock(&page_mutex);
+	return retval;
+}
+
+/*
+ * Same as rmi_i2c_read, except that multiple bytes are allowed to be read.
+ *
+ * param[in] pd - The pointer to the rmi_phys_driver struct
+ * param[in] address - The address at which to start the data read.
+ * param[out] valp - Pointer to the buffer where the data will be stored.  This
+ *     buffer must be at least size bytes long.
+ * param[in] size - The number of bytes to be read.
+ * returns zero upon success (with the byte read in valp), non-zero upon error.
+ *
+ */
+static int
+rmi_i2c_read_multiple(struct rmi_phys_driver *physdrvr, unsigned short address,
+	char *valp, int size)
+{
+	struct instance_data *instancedata =
+		container_of(physdrvr, struct instance_data, rmiphysdrvr);
+
+	char txbuf[2];
+	int retval = 0;
+	int retry_count = 0;
+
+	/* Can't have anyone else changing the page behind our backs */
+	mutex_lock(&page_mutex);
+
+	if (((address >> 8) & 0xff) != instancedata->page) {
+		/* Switch pages */
+		retval = rmi_set_page(instancedata, ((address >> 8) & 0xff));
+		if (retval)
+			goto exit;
+	}
+
+retry:
+	txbuf[0] = address & 0xff;
+	retval = i2c_master_send(instancedata->i2cclient, txbuf, 1);
+
+	if (retval != 1) {
+		dev_err(&instancedata->i2cclient->dev, "%s: Write fail: %d\n",
+				__func__, retval);
+		goto exit;
+	}
+	retval = i2c_master_recv(instancedata->i2cclient, valp, size);
+
+	if (retval != size) {
+		if (++retry_count == 5) {
+			dev_err(&instancedata->i2cclient->dev,
+					"%s: Read of 0x%04x size %d fail: %d\n",
+					__func__, address, size, retval);
+		} else {
+			mdelay(10);
+			rmi_set_page(instancedata, ((address >> 8) & 0xff));
+			goto retry;
+		}
+	} else {
+		retval = 0;
+	}
+exit:
+
+	mutex_unlock(&page_mutex);
+	return retval;
+}
+
+
+/*
+ * Write a single register through i2c.
+ * You can write multiple registers at once, but I made the functions for that
+ * seperate for performance reasons.  Writing multiple requires allocation and
+ * freeing.
+ *
+ * param[in] pd - The pointer to the rmi_phys_driver struct
+ * param[in] address - The address at which to start the write.
+ * param[in] data - The data to be written.
+ * returns one upon success, something else upon error.
+ */
+static int
+rmi_i2c_write(struct rmi_phys_driver *physdrvr, unsigned short address, char data)
+{
+	struct instance_data *instancedata =
+		container_of(physdrvr, struct instance_data, rmiphysdrvr);
+
+	unsigned char txbuf[2];
+	int retval = 0;
+
+	/* Can't have anyone else changing the page behind our backs */
+	mutex_lock(&page_mutex);
+
+	if (((address >> 8) & 0xff) != instancedata->page) {
+		/* Switch pages */
+		retval = rmi_set_page(instancedata, ((address >> 8) & 0xff));
+		if (retval)
+			goto exit;
+	}
+
+	txbuf[0] = address & 0xff;
+	txbuf[1] = data;
+	retval = i2c_master_send(instancedata->i2cclient, txbuf, 2);
+
+	/* TODO: Add in retry on writes only in certian error return values */
+	if (retval != 2) {
+		dev_err(&instancedata->i2cclient->dev, "%s: Write fail: %d\n",
+			__func__, retval);
+		goto exit; /* Leave this in case we add code below */
+	} else {
+		retval = 1;
+	}
+exit:
+
+	mutex_unlock(&page_mutex);
+	return retval;
+}
+
+/*
+ * Write multiple registers.
+ *
+ * For fast writes of 16 bytes of less we will re-use a buffer on the stack.
+ * For larger writes (like for RMI reflashing) we will need to allocate a
+ * temp buffer.
+ *
+ * param[in] pd - The pointer to the rmi_phys_driver struct
+ * param[in] address - The address at which to start the write.
+ * param[in] valp - A pointer to a buffer containing the data to be written.
+ * param[in] size - The number of bytes to write.
+ * returns one upon success, something else upon error.
+ */
+static int
+rmi_i2c_write_multiple(struct rmi_phys_driver *physdrvr, unsigned short address,
+	char *valp, int size)
+{
+	struct instance_data *instancedata =
+		container_of(physdrvr, struct instance_data, rmiphysdrvr);
+
+	unsigned char *txbuf;
+	unsigned char txbuf_most[17]; /* Use this buffer for fast writes of 16
+					bytes or less.  The first byte will
+					contain the address at which to start
+					the write. */
+	int retval = 0;
+	int i;
+
+	if (size < sizeof(txbuf_most)) {
+		/* Avoid an allocation if we can help it. */
+		txbuf = txbuf_most;
+	} else {
+		/* over 16 bytes write we'll need to allocate a temp buffer */
+		txbuf = kzalloc(size + 1, GFP_KERNEL);
+		if (!txbuf)
+			return -ENOMEM;
+	}
+
+	/* Yes, it stinks here that we have to copy the buffer */
+	/* We copy from valp to txbuf leaving
+	the first location open for the address */
+	for (i = 0; i < size; i++)
+		txbuf[i + 1] = valp[i];
+
+	/* Can't have anyone else changing the page behind our backs */
+	mutex_lock(&page_mutex);
+
+	if (((address >> 8) & 0xff) != instancedata->page) {
+		/* Switch pages */
+		retval = rmi_set_page(instancedata, ((address >> 8) & 0xff));
+		if (retval)
+			goto exit;
+	}
+
+	txbuf[0] = address & 0xff; /* put the address in the first byte */
+	retval = i2c_master_send(instancedata->i2cclient, txbuf, size + 1);
+
+	/* TODO: Add in retyr on writes only in certian error return values */
+	if (retval != 1) {
+		dev_err(&instancedata->i2cclient->dev, "%s: Write fail: %d\n",
+				__func__, retval);
+		goto exit;
+	}
+exit:
+
+	mutex_unlock(&page_mutex);
+	if (txbuf != txbuf_most)
+		kfree(txbuf);
+	return retval;
+}
+
+/*
+ * This is the Interrupt Service Routine.  It just notifies the application
+ * layer that attention is required.
+ */
+static irqreturn_t
+i2c_attn_isr(int irq, void *info)
+{
+	struct instance_data *instancedata = info;
+
+	disable_irq_nosync(instancedata->irq);
+
+	if (instancedata->rmiphysdrvr.attention) {
+		instancedata->rmiphysdrvr.attention(&instancedata->rmiphysdrvr,
+			instancedata->instance_no);
+	}
+
+	return IRQ_HANDLED;
+}
+
+/* The Driver probe function - will allocate and initialize the instance
+ * data and request the irq and set the instance data as the clients
+ * platform data then register the physical driver which will do a scan of
+ * the RMI4 Physical Device Table and enumerate any RMI4 functions that
+ * have data sources associated with them.
+ */
+static int
+rmi_i2c_probe(struct i2c_client *client, const struct i2c_device_id *dev_id)
+{
+
+	struct instance_data *instancedata;
+	int retval = 0;
+	int irqtype = 0;
+
+	struct rmi_i2c_platformdata *platformdata;
+	struct rmi_sensordata *sensordata;
+
+	if (client == NULL) {
+		printk(KERN_ERR "%s: Invalid NULL client received.", __func__);
+		return -EINVAL;
+	}
+
+	printk(KERN_DEBUG "%s: Probing i2c RMI device, addr: 0x%02x", __func__, client->addr);
+
+
+	/* Allocate and initialize the instance data for this client */
+	instancedata = kzalloc(sizeof(*instancedata), GFP_KERNEL);
+	if (!instancedata) {
+		dev_err(&client->dev,
+			"%s: Out of memory trying to allocate instance_data.\n",
+			__func__);
+		return -ENOMEM;
+	}
+
+	instancedata->rmiphysdrvr.name           = DRIVER_NAME;
+	instancedata->rmiphysdrvr.write          = rmi_i2c_write;
+	instancedata->rmiphysdrvr.read           = rmi_i2c_read;
+	instancedata->rmiphysdrvr.write_multiple = rmi_i2c_write_multiple;
+	instancedata->rmiphysdrvr.read_multiple  = rmi_i2c_read_multiple;
+	instancedata->rmiphysdrvr.module         = THIS_MODULE;
+
+	/* Set default to polling in case no matching platform data is located
+	for this device. We'll still work but in polling mode since we didn't
+	find any irq info */
+	instancedata->rmiphysdrvr.polling_required = true;
+
+	instancedata->page = 0xffff; /* Force a set page the first time */
+
+	/* cast to our struct rmi_i2c_platformdata so we know
+	the fields (see rmi_ic2.h) */
+	platformdata = client->dev.platform_data;
+	if (platformdata == NULL) {
+		printk(KERN_ERR "%s: CONFIGURATION ERROR - platform data is NULL.", __func__);
+		return -EINVAL;
+	}
+	sensordata = platformdata->sensordata;
+
+	/* Egregiously horrible delay here that seems to prevent I2C disasters on
+	 * certain broken dev systems.  In most cases, you can safely leave this
+	 * as zero.
+	 */
+	if (platformdata->delay_ms > 0)
+		mdelay(platformdata->delay_ms);
+
+	/* Call the platform setup routine, to do any setup that is required before
+	 * interacting with the device.
+	 */
+	if (sensordata && sensordata->rmi_sensor_setup) {
+		retval = sensordata->rmi_sensor_setup();
+		if (retval) {
+			printk(KERN_ERR "%s: sensor setup failed with code %d.", __func__, retval);
+			return retval;
+		}
+	}
+
+	printk(KERN_DEBUG "%s: sensor addr: 0x%02x irq: 0x%x type: %d",
+		__func__, platformdata->i2c_address, platformdata->irq, platformdata->irq_type);
+	if (client->addr != platformdata->i2c_address) {
+		printk(KERN_ERR "%s: CONFIGURATION ERROR - client I2C address 0x%02x doesn't match platform data address 0x%02x.", __func__, client->addr, platformdata->i2c_address);
+		return -EINVAL;
+	}
+
+	instancedata->instance_no = device_count++;
+
+	/* set the device name using the instance_no appended
+	to DEVICE_NAME to make a unique name */
+	dev_set_name(&client->dev,
+		"rmi4-i2c%d", instancedata->instance_no);
+
+	/* Determine if we need to poll (inefficient) or use interrupts.
+	*/
+	if (platformdata->irq) {
+		instancedata->irq = platformdata->irq;
+		switch (platformdata->irq_type) {
+		case IORESOURCE_IRQ_HIGHEDGE:
+			irqtype = IRQF_TRIGGER_RISING;
+			break;
+		case IORESOURCE_IRQ_LOWEDGE:
+			irqtype = IRQF_TRIGGER_FALLING;
+			break;
+		case IORESOURCE_IRQ_HIGHLEVEL:
+			irqtype = IRQF_TRIGGER_HIGH;
+			break;
+		case IORESOURCE_IRQ_LOWLEVEL:
+			irqtype = IRQF_TRIGGER_LOW;
+			break;
+		default:
+			dev_warn(&client->dev,
+				"%s: Invalid IRQ flags in platform data.\n",
+				__func__);
+			kfree(instancedata);
+			return -ENXIO;
+		}
+
+		instancedata->rmiphysdrvr.polling_required = false;
+		instancedata->rmiphysdrvr.irq = instancedata->irq;
+
+	} else {
+		instancedata->rmiphysdrvr.polling_required = true;
+		dev_info(&client->dev,
+				"%s: No IRQ info given. Polling required.\n",
+				__func__);
+	}
+
+	/* Store the instance data in the i2c_client - we need to do this prior
+	* to calling register_physical_driver since it may use the read, write
+	* functions. If nothing was found then the id fields will be set to 0
+	* for the irq and the default  will be set to polling required so we
+	* will still work but in polling mode. */
+	i2c_set_clientdata(client, instancedata);
+
+	/* Copy i2c_client pointer into instance_data's i2c_client pointer for
+	later use in rmi4_read, rmi4_write, etc. */
+	instancedata->i2cclient = client;
+
+	/* Register sensor drivers - this will call the detect function that
+	* will then scan the device and determine the supported RMI4 sensors
+	* and functions.
+	*/
+	retval = rmi_register_sensor(&instancedata->rmiphysdrvr, platformdata->sensordata);
+	if (retval) {
+		dev_err(&client->dev, "%s: Failed to Register %s sensor drivers\n",
+				__func__, instancedata->rmiphysdrvr.name);
+		i2c_set_clientdata(client, NULL);
+		kfree(instancedata);
+		return retval;
+	}
+
+	if (instancedata->rmiphysdrvr.polling_required == false) {
+		retval = request_irq(instancedata->irq, i2c_attn_isr,
+				irqtype, "rmi_i2c", instancedata);
+		if (retval) {
+			dev_err(&client->dev, "%s: failed to obtain IRQ %d. Result: %d.",
+				__func__, instancedata->irq, retval);
+			dev_info(&client->dev, "%s: Reverting to polling.\n", __func__);
+			instancedata->rmiphysdrvr.polling_required = true;
+			/* TODO: Need to revert back to polling - create and start timer. */
+		} else {
+			dev_dbg(&client->dev, "%s: got irq.\n", __func__);
+		}
+	}
+
+	dev_dbg(&client->dev, "%s: Successfully registered %s sensor driver.\n",
+			__func__, instancedata->rmiphysdrvr.name);
+
+	printk(KERN_INFO "%s: Successfully registered %s sensor driver.\n", __func__, instancedata->rmiphysdrvr.name);
+
+	return retval;
+}
+
+/* The Driver remove function.  We tear down the instance data and unregister
+ * the phys driver in this call.
+ */
+static int
+rmi_i2c_remove(struct i2c_client *client)
+{
+	struct instance_data *instancedata =
+		i2c_get_clientdata(client);
+
+	dev_dbg(&client->dev, "%s: Unregistering phys driver %s\n", __func__,
+		instancedata->rmiphysdrvr.name);
+
+	rmi_unregister_sensors(&instancedata->rmiphysdrvr);
+
+	dev_dbg(&client->dev, "%s: Unregistered phys driver %s\n",
+			__func__, instancedata->rmiphysdrvr.name);
+
+	/* only free irq if we have an irq - otherwise the instance_data
+	will be 0 for that field */
+	if (instancedata->irq)
+		free_irq(instancedata->irq, instancedata);
+
+	kfree(instancedata);
+	dev_dbg(&client->dev, "%s: Remove successful\n", __func__);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int
+rmi_i2c_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+	/* Touch sleep mode */
+	return 0;
+}
+
+static int
+rmi_i2c_resume(struct i2c_client *client)
+{
+	/* Re-initialize upon resume */
+	return 0;
+}
+#else
+#define rmi_i2c_suspend	NULL
+#define rmi_i2c_resume	NULL
+#endif
+
+/*
+ * This structure tells the i2c subsystem about us.
+ *
+ * TODO: we should add .suspend and .resume fns.
+ *
+ */
+static struct i2c_driver rmi_i2c_driver = {
+	.probe		= rmi_i2c_probe,
+	.remove		= rmi_i2c_remove,
+	.suspend	= rmi_i2c_suspend,
+	.resume		= rmi_i2c_resume,
+	.driver = {
+		.name  = DRIVER_NAME,
+		.owner = THIS_MODULE,
+	},
+	.id_table	= rmi_i2c_id_table,
+};
+
+/*
+ * Register ourselves with i2c Chip Driver.
+ *
+ */
+static int __init rmi_phys_i2c_init(void)
+{
+	return i2c_add_driver(&rmi_i2c_driver);
+}
+
+/*
+ * Un-register ourselves from the i2c Chip Driver.
+ *
+ */
+static void __exit rmi_phys_i2c_exit(void)
+{
+	i2c_del_driver(&rmi_i2c_driver);
+}
+
+
+module_init(rmi_phys_i2c_init);
+module_exit(rmi_phys_i2c_exit);
+
+MODULE_AUTHOR("Synaptics, Inc.");
+MODULE_DESCRIPTION("RMI4 Driver I2C Physical Layer");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/synaptics/rmi_sensor.c b/drivers/input/touchscreen/synaptics/rmi_sensor.c
new file mode 100644
index 0000000..2c64609
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics/rmi_sensor.c
@@ -0,0 +1,662 @@
+/**
+ * Synaptics Register Mapped Interface (RMI4) - RMI Sensor Module.
+ * Copyright (C) 2007 - 2011, Synaptics Incorporated
+ *
+ */
+/*
+ * This file is licensed under the GPL2 license.
+ *
+ *############################################################################
+ * GPL
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ *############################################################################
+ */
+
+static const char sensorname[] = "sensor";
+
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/hrtimer.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+
+
+#include "rmi_drvr.h"
+#include "rmi_bus.h"
+#include "rmi_function.h"
+#include "rmi_sensor.h"
+
+long polltime = 25000000;   /* Shared with rmi_function.c. */
+EXPORT_SYMBOL(polltime);
+module_param(polltime, long, 0644);
+MODULE_PARM_DESC(polltime, "How long to wait between polls (in nano seconds).");
+
+
+#define PDT_START_SCAN_LOCATION 0x00E9
+#define PDT_END_SCAN_LOCATION 0x0005
+#define PDT_ENTRY_SIZE 0x0006
+
+static DEFINE_MUTEX(rfi_mutex);
+
+struct rmi_functions *rmi_find_function(int functionNum);
+
+int rmi_read(struct rmi_sensor_driver *sensor, unsigned short address,
+		char *dest)
+{
+	struct rmi_phys_driver *rpd = sensor->rpd;
+	if (!rpd)
+		return -ENODEV;
+	return rpd->read(rpd, address, dest);
+}
+EXPORT_SYMBOL(rmi_read);
+
+int rmi_write(struct rmi_sensor_driver *sensor, unsigned short address,
+		unsigned char data)
+{
+	struct rmi_phys_driver *rpd = sensor->rpd;
+	if (!rpd)
+		return -ENODEV;
+	return rpd->write(rpd, address, data);
+}
+EXPORT_SYMBOL(rmi_write);
+
+int rmi_read_multiple(struct rmi_sensor_driver *sensor,
+		unsigned short address,	char *dest, int length)
+{
+	struct rmi_phys_driver *rpd = sensor->rpd;
+	if (!rpd)
+		return -ENODEV;
+	return rpd->read_multiple(rpd, address, dest, length);
+}
+EXPORT_SYMBOL(rmi_read_multiple);
+
+int rmi_write_multiple(struct rmi_sensor_driver *sensor,
+		unsigned short address,	unsigned char *data, int length)
+{
+	struct rmi_phys_driver *rpd = sensor->rpd;
+	if (!rpd)
+		return -ENODEV;
+	return rpd->write_multiple(rpd, address, data, length);
+}
+EXPORT_SYMBOL(rmi_write_multiple);
+
+/* Utility routine to set bits in a register. */
+int rmi_set_bits(struct rmi_sensor_driver *sensor, unsigned short address,
+		unsigned char bits)
+{
+	unsigned char reg_contents;
+	int retval;
+
+	retval = rmi_read(sensor, address, &reg_contents);
+	if (retval)
+		return retval;
+	reg_contents = reg_contents | bits;
+	retval = rmi_write(sensor, address, reg_contents);
+	if (retval == 1)
+		return 0;
+	else if (retval == 0)
+		return -EINVAL;	/* TODO: What should this be? */
+	else
+		return retval;
+}
+EXPORT_SYMBOL(rmi_set_bits);
+
+/* Utility routine to clear bits in a register. */
+int rmi_clear_bits(struct rmi_sensor_driver *sensor,
+		unsigned short address, unsigned char bits)
+{
+	unsigned char reg_contents;
+	int retval;
+
+	retval = rmi_read(sensor, address, &reg_contents);
+	if (retval)
+		return retval;
+	reg_contents = reg_contents & ~bits;
+	retval = rmi_write(sensor, address, reg_contents);
+	if (retval == 1)
+		return 0;
+	else if (retval == 0)
+		return -EINVAL;	/* TODO: What should this be? */
+	else
+		return retval;
+}
+EXPORT_SYMBOL(rmi_clear_bits);
+
+/* Utility routine to set the value of a bit field in a register. */
+int rmi_set_bit_field(struct rmi_sensor_driver *sensor,
+	unsigned short address, unsigned char field_mask, unsigned char bits)
+{
+	unsigned char reg_contents;
+	int retval;
+
+	retval = rmi_read(sensor, address, &reg_contents);
+	if (retval)
+		return retval;
+	reg_contents = (reg_contents & ~field_mask) | bits;
+	retval = rmi_write(sensor, address, reg_contents);
+	if (retval == 1)
+		return 0;
+	else if (retval == 0)
+		return -EINVAL;	/* TODO: What should this be? */
+	else
+		return retval;
+}
+EXPORT_SYMBOL(rmi_set_bit_field);
+
+bool rmi_polling_required(struct rmi_sensor_driver *sensor)
+{
+	return sensor->polling_required;
+}
+EXPORT_SYMBOL(rmi_polling_required);
+
+/** Functions can call this in order to dispatch IRQs. */
+void dispatchIRQs(struct rmi_sensor_driver *sensor, unsigned int irqStatus)
+{
+	struct rmi_function_info *functionInfo;
+
+	list_for_each_entry(functionInfo, &sensor->functions, link) {
+		if ((functionInfo->interruptMask & irqStatus)) {
+			if (functionInfo->function_device->
+					rmi_funcs->inthandler) {
+			/* Call the functions interrupt handler function. */
+				functionInfo->function_device->rmi_funcs->
+				inthandler(functionInfo,
+				(functionInfo->interruptMask & irqStatus));
+			}
+		}
+	}
+}
+
+/**
+ * This is the function we pass to the RMI4 subsystem so we can be notified
+ * when attention is required.  It may be called in interrupt context.
+ */
+static void attention(struct rmi_phys_driver *physdrvr, int instance)
+{
+	/* All we have to do is schedule work. */
+
+	/* TODO: It's possible that workIsReady is not really needed anymore.
+	 * Investigate this to see if the race condition between setting up
+	 * the work and enabling the interrupt still exists.
+	 */
+	if (physdrvr->sensor->workIsReady) {
+		schedule_work(&(physdrvr->sensor->work));
+	} else {
+		/* Got an interrupt but we're not ready so enable the irq
+		 * so it doesn't get hung up
+		 */
+		printk(KERN_DEBUG "%s: Work not initialized yet -"
+				"enabling irqs.\n", __func__);
+		enable_irq(physdrvr->irq);
+	}
+}
+
+/**
+ * This notifies any interested functions that there
+ * is an Attention interrupt.  The interested functions should take
+ * appropriate
+ * actions (such as reading the interrupt status register and dispatching any
+ * appropriate RMI4 interrupts).
+ */
+void attn_notify(struct rmi_sensor_driver *sensor)
+{
+	struct rmi_function_info *functionInfo;
+
+	/* check each function that has data sources and if the interrupt for
+	 * that triggered then call that RMI4 functions report() function to
+	 * gather data and report it to the input subsystem
+	 */
+	list_for_each_entry(functionInfo, &sensor->functions, link) {
+		if (functionInfo->function_device &&
+			functionInfo->function_device->rmi_funcs->attention)
+			functionInfo->function_device->
+				rmi_funcs->attention(functionInfo);
+	}
+}
+
+/* This is the worker function - for now it simply has to call attn_notify.
+ * This work should be scheduled whenever an ATTN interrupt is asserted by
+ * the touch sensor.
+ * We then call attn_notify to dispatch notification of the ATTN interrupt
+ * to all
+ * interested functions. After all the attention handling functions
+ * have returned, it is presumed safe to re-enable the Attention interrupt.
+ */
+static void sensor_work_func(struct work_struct *work)
+{
+	struct rmi_sensor_driver *sensor = container_of(work,
+			struct rmi_sensor_driver, work);
+
+	attn_notify(sensor);
+
+	/* we only need to enable the irq if doing interrupts */
+	if (!rmi_polling_required(sensor))
+		enable_irq(sensor->rpd->irq);
+}
+
+/* This is the timer function for polling - it simply has to schedule work
+ * and restart the timer. */
+static enum hrtimer_restart sensor_poll_timer_func(struct hrtimer *timer)
+{
+	struct rmi_sensor_driver *sensor = container_of(timer,
+			struct rmi_sensor_driver, timer);
+
+	schedule_work(&sensor->work);
+	hrtimer_start(&sensor->timer, ktime_set(0, polltime),
+			HRTIMER_MODE_REL);
+	return HRTIMER_NORESTART;
+}
+
+/* This is the probe function passed to the RMI4 subsystem that gives us a
+ * chance to recognize an RMI4 device.  In this case, we're looking for
+ * Synaptics devices that have data sources - such as touch screens, buttons,
+ * etc.
+ *
+ * TODO: Well, it used to do this.  I'm not sure it's required any more.
+ */
+static int probe(struct rmi_sensor_driver *sensor)
+{
+	struct rmi_phys_driver *rpd;
+
+	rpd = sensor->rpd;
+
+	if (!rpd) {
+		printk(KERN_ERR "%s: Invalid rmi physical driver - null ptr:"
+				"%p\n", __func__, rpd);
+		return 0;
+	}
+
+	return 1;
+}
+
+static void config(struct rmi_sensor_driver *sensor)
+{
+	/* For each data source we had detected print info and set up interrupts
+	or polling. */
+	struct rmi_function_info *functionInfo;
+	struct rmi_phys_driver *rpd;
+
+	rpd = sensor->rpd; /* get ptr to rmi_physical_driver from app */
+
+	list_for_each_entry(functionInfo, &sensor->functions, link) {
+		/* Get and print some info about the data sources... */
+		struct rmi_functions *fn;
+		bool found = false;
+		/* check if function number matches - if so call that
+		config function */
+		fn = rmi_find_function(functionInfo->functionNum);
+		if (fn) {
+			found = true;
+
+			if (fn->config) {
+				fn->config(functionInfo);
+			} else {
+				/* the developer did not add in the
+				pointer to the config function into
+				rmi4_supported_data_src_functions */
+				printk(KERN_ERR
+					"%s: no config function for "
+					"function 0x%x\n",
+					__func__, functionInfo->functionNum);
+				break;
+			}
+		}
+
+		if (!found) {
+			/* if no support found for this RMI4 function
+			it means the developer did not add the
+			appropriate function pointer list into the
+			rmi4_supported_data_src_functions array and/or
+			did not bump up the number of supported RMI4
+			functions in rmi.h as required */
+			printk(KERN_ERR "%s: could not find support "
+				"for function 0x%x\n",
+				__func__, functionInfo->functionNum);
+		}
+	}
+
+	/* This will handle interrupts on the ATTN line (interrupt driven)
+	* or will be called every poll interval (when we're not interrupt
+	* driven).
+	*/
+	INIT_WORK(&sensor->work, sensor_work_func);
+	sensor->workIsReady = true;
+
+	if (rmi_polling_required(sensor)) {
+		/* We're polling driven, so set up the polling timer
+		and timer function. */
+		hrtimer_init(&sensor->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+		sensor->timer.function = sensor_poll_timer_func;
+		hrtimer_start(&sensor->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
+	}
+}
+
+/** Just a stub for now.
+ */
+static int rmi_sensor_suspend(struct device *dev, pm_message_t state)
+{
+	printk(KERN_INFO "%s: sensor suspend called.", __func__);
+	return 0;
+}
+
+/** Just a stub for now.
+ */
+static int rmi_sensor_resume(struct device *dev)
+{
+	printk(KERN_INFO "%s: sensor resume called.", __func__);
+	return 0;
+}
+
+/*
+ * This method is called, whenever a new sensor device is added for the rmi
+ * bus.
+ *
+ * It will scan the devices PDT to determine the supported functions
+ * and create a new function device for each of these. It will read
+ * the query, control, command and data regsiters for the function
+ * to be used for each newly created function device.
+ *
+ * The sensor device is then bound to every function it supports.
+ *
+ */
+int rmi_sensor_register_functions(struct rmi_sensor_driver *sensor)
+{
+	struct rmi_function_device *function;
+	unsigned int interruptRegisterCount;
+	struct rmi_phys_driver *rpd;
+	int i;
+	unsigned char interruptCount;
+	struct rmi_function_info *functionInfo;
+	struct rmi_function_descriptor rmi_fd;
+	struct rmi_functions *fn;
+	int retval;
+
+	pr_debug("%s: Registering sensor functions\n", __func__);
+
+	retval = 0;
+
+	/* Scan device for functions that may be supported */
+	{
+		pr_debug("%s: Scanning sensor for Functions:\n", __func__);
+
+		interruptCount = 0;
+		rpd = sensor->rpd;
+
+		/* Read the Page Descriptor Table to determine what functions
+		 * are present */
+
+		printk(KERN_DEBUG "%s: Scanning page descriptors.", __func__);
+		for (i = PDT_START_SCAN_LOCATION;
+				i >= PDT_END_SCAN_LOCATION;
+				i -= PDT_ENTRY_SIZE) {
+			printk(KERN_DEBUG "%s: Reading page descriptor 0x%02x", __func__, i);
+			retval = rpd->read_multiple(rpd, i, (char *)&rmi_fd,
+					sizeof(rmi_fd));
+			if (!retval) {
+				functionInfo = NULL;
+
+				if (rmi_fd.functionNum != 0x00 && rmi_fd.functionNum != 0xff) {
+					printk(KERN_DEBUG "%s: F%02x - queries %02x commands %02x control %02x data %02x ints %02x", __func__, rmi_fd.functionNum, rmi_fd.queryBaseAddr, rmi_fd.commandBaseAddr, rmi_fd.controlBaseAddr, rmi_fd.dataBaseAddr, rmi_fd.interruptSrcCnt);
+
+					if ((rmi_fd.functionNum & 0xff) == 0x01)
+						printk(KERN_DEBUG "%s:   Fn $01 Found - RMI Device Control", __func__);
+
+					/* determine if the function is supported and if so
+					 * then bind this function device to the sensor */
+					if (rmi_fd.interruptSrcCnt) {
+						functionInfo = kzalloc(sizeof(*functionInfo), GFP_KERNEL);
+						if (!functionInfo) {
+							printk(KERN_ERR "%s: could not allocate memory for function 0x%x.",
+								__func__, rmi_fd.functionNum);
+							retval = -ENOMEM;
+							goto exit_fail;
+						}
+						functionInfo->sensor = sensor;
+						functionInfo->functionNum = (rmi_fd.functionNum & 0xff);
+						INIT_LIST_HEAD(&functionInfo->link);
+						/* Get the ptr to the detect function based on
+						 * the function number */
+						printk(KERN_DEBUG "%s: Checking for RMI function F%02x.", __func__, rmi_fd.functionNum);
+						fn = rmi_find_function(rmi_fd.functionNum);
+						if (fn) {
+							retval = fn->detect(functionInfo, &rmi_fd,
+								interruptCount);
+							if (retval)
+								printk(KERN_ERR "%s: Function detect for F%02x failed with %d.",
+									   __func__, rmi_fd.functionNum, retval);
+
+							/* Create a function device and function driver for this Fn */
+							function = kzalloc(sizeof(*function), GFP_KERNEL);
+							if (!function) {
+								printk(KERN_ERR "%s: Error allocating memory for rmi_function_device.", __func__);
+								return -ENOMEM;
+							}
+
+							function->dev.parent = &sensor->sensor_device->dev;
+							function->dev.bus = sensor->sensor_device->dev.bus;
+							function->rmi_funcs = fn;
+							function->sensor = sensor;
+							function->rfi = functionInfo;
+							functionInfo->function_device = function;
+
+							/* Check if we have an interrupt mask of 0 and a non-NULL interrupt
+							handler function and print a debug message since we should never
+							have this.
+							*/
+							if (functionInfo->interruptMask == 0 && fn->inthandler != NULL) {
+								printk(KERN_DEBUG "%s: Can't have a zero interrupt mask for function F%02x (which requires an interrupt handler).\n",
+									__func__, rmi_fd.functionNum);
+							}
+
+
+							/* Check if we have a non-zero interrupt mask and a NULL interrupt
+							handler function and print a debug message since we should never
+							have this.
+							*/
+							if (functionInfo->interruptMask != 0 && fn->inthandler == NULL) {
+								printk(KERN_DEBUG "%s: Can't have a non-zero interrupt mask %d for function F%02x with a NULL inthandler fn.\n",
+									__func__, functionInfo->interruptMask, rmi_fd.functionNum);
+							}
+
+							/* Register the rmi function device */
+							retval = rmi_function_register_device(function, rmi_fd.functionNum);
+							if (retval) {
+								printk(KERN_ERR "%s:  Failed rmi_function_register_device.\n",
+									__func__);
+								return retval;
+							}
+						} else {
+							printk(KERN_ERR "%s: could not find support for function 0x%02X.\n",
+								__func__, rmi_fd.functionNum);
+						}
+					} else {
+						printk(KERN_DEBUG "%s: Found function F%02x - Ignored.\n", __func__, rmi_fd.functionNum & 0xff);
+					}
+
+					/* bump interrupt count for next iteration */
+					/* NOTE: The value 7 is reserved - for now, only bump up one for an interrupt count of 7 */
+					if ((rmi_fd.interruptSrcCnt & 0x7) == 0x7) {
+						interruptCount += 1;
+					} else {
+						interruptCount +=
+							(rmi_fd.interruptSrcCnt & 0x7);
+					}
+
+					/* link this function info to the RMI module infos list
+					of functions */
+					if (functionInfo == NULL) {
+						printk(KERN_DEBUG "%s: WTF? functionInfo is null here.", __func__);
+					} else {
+						printk(KERN_DEBUG "%s: Adding function F%02x with %d sources.\n",
+							__func__, functionInfo->functionNum, functionInfo->numSources);
+
+						mutex_lock(&rfi_mutex);
+						list_add_tail(&functionInfo->link,
+							&sensor->functions);
+						mutex_unlock(&rfi_mutex);
+					}
+
+				} else {
+					/* A zero or 0xff in the function number
+					signals the end of the PDT */
+					printk(KERN_DEBUG "%s:   Found End of PDT\n",
+						__func__);
+					break;
+				}
+			} else {
+				/* failed to read next PDT entry - end PDT
+				scan - this may result in an incomplete set
+				of recognized functions - should probably
+				return an error but the driver may still be
+				viable for diagnostics and debugging so let's
+				let it continue. */
+				printk(KERN_ERR "%s: Read Error %d when reading next PDT entry - "
+					"ending PDT scan.\n",
+					__func__, retval);
+				break;
+			}
+		}
+		printk(KERN_DEBUG "%s: Done scanning.", __func__);
+
+		/* calculate the interrupt register count - used in the
+		ISR to read the correct number of interrupt registers */
+		interruptRegisterCount = (interruptCount + 7) / 8;
+		sensor->interruptRegisterCount = interruptRegisterCount;    /* TODO: Is this needed by the sensor anymore? */
+	}
+
+	return 0;
+
+exit_fail:
+	return retval;
+}
+EXPORT_SYMBOL(rmi_sensor_register_functions);
+
+int rmi_sensor_register_device(struct rmi_sensor_device *dev, int index)
+{
+	int status;
+
+	printk(KERN_INFO "%s: Registering sensor device.\n", __func__);
+
+	/* make name - sensor00, sensor01, etc. */
+	dev_set_name(&dev->dev, "sensor%02d", index);
+	status = device_register(&dev->dev);
+
+	return status;
+}
+EXPORT_SYMBOL(rmi_sensor_register_device);
+
+static void rmi_sensor_unregister_device(struct rmi_sensor_device *rmisensordev)
+{
+	printk(KERN_INFO "%s: Unregistering sensor device.\n", __func__);
+
+	device_unregister(&rmisensordev->dev);
+}
+EXPORT_SYMBOL(rmi_sensor_unregister_device);
+
+int rmi_sensor_register_driver(struct rmi_sensor_driver *driver)
+{
+	static int index;
+	int ret;
+	char *drvrname;
+
+	driver->workIsReady = false;
+
+	printk(KERN_INFO "%s: Registering sensor driver.\n", __func__);
+	driver->dispatchIRQs = dispatchIRQs;
+	driver->attention = attention;
+	driver->config = config;
+	driver->probe = probe;
+
+	/* assign the bus type for this driver to be rmi bus */
+	driver->drv.bus = &rmi_bus_type;
+	driver->drv.suspend = rmi_sensor_suspend;
+	driver->drv.resume = rmi_sensor_resume;
+	/* Create a function device and function driver for this Fn */
+	drvrname = kzalloc(sizeof(sensorname) + 4, GFP_KERNEL);
+	if (!drvrname) {
+		printk(KERN_ERR "%s: Error allocating memeory for rmi_sensor_driver name.\n", __func__);
+		return -ENOMEM;
+	}
+	sprintf(drvrname, "sensor%02d", index++);
+
+	driver->drv.name = drvrname;
+	driver->module = driver->drv.owner;
+
+	/* register the sensor driver */
+	ret = driver_register(&driver->drv);
+	if (ret) {
+		printk(KERN_ERR "%s: Failed driver_register %d\n",
+			__func__, ret);
+		goto exit_fail;
+	}
+
+	/* register the functions on the sensor */
+	ret = rmi_sensor_register_functions(driver);
+	if (ret) {
+		printk(KERN_ERR "%s: Failed rmi_sensor_register_functions %d\n",
+			__func__, ret);
+	}
+
+	/* configure the sensor - enable interrupts for each function, init work, set polling timer or adjust report rate, etc. */
+	config(driver);
+
+	printk(KERN_DEBUG "%s: sensor driver registration completed.", __func__);
+
+exit_fail:
+	return ret;
+}
+EXPORT_SYMBOL(rmi_sensor_register_driver);
+
+static void rmi_sensor_unregister_driver(struct rmi_sensor_driver *driver)
+{
+	printk(KERN_DEBUG "%s: Unregistering sensor driver.\n", __func__);
+
+	/* Stop the polling timer if doing polling */
+	if (rmi_polling_required(driver))
+		hrtimer_cancel(&driver->timer);
+
+	flush_scheduled_work(); /* Make sure all scheduled work is stopped */
+
+	driver_unregister(&driver->drv);
+}
+EXPORT_SYMBOL(rmi_sensor_unregister_driver);
+
+
+static int __init rmi_sensor_init(void)
+{
+	printk(KERN_DEBUG "%s: RMI Sensor Init\n", __func__);
+	return 0;
+}
+
+static void __exit rmi_sensor_exit(void)
+{
+	printk(KERN_DEBUG "%s: RMI Sensor Driver Exit\n", __func__);
+	flush_scheduled_work(); /* Make sure all scheduled work is stopped */
+}
+
+
+module_init(rmi_sensor_init);
+module_exit(rmi_sensor_exit);
+
+MODULE_AUTHOR("Synaptics, Inc.");
+MODULE_DESCRIPTION("RMI4 Sensor Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/synaptics/rmi_sensor.h b/drivers/input/touchscreen/synaptics/rmi_sensor.h
new file mode 100644
index 0000000..63d2555
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics/rmi_sensor.h
@@ -0,0 +1,143 @@
+/**
+ *
+ * Synaptics Register Mapped Interface (RMI4) - RMI Sensor Module Header.
+ * Copyright (C) 2007 - 2011, Synaptics Incorporated
+ *
+ */
+/*
+ *
+ * This file is licensed under the GPL2 license.
+ *
+ *############################################################################
+ * GPL
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ *############################################################################
+ */
+
+#include <linux/device.h>
+
+#ifndef _RMI_SENSOR_H
+#define _RMI_SENSOR_H
+
+#include <linux/input/rmi_platformdata.h>
+
+struct rmi_sensor_driver {
+	struct module *module;
+	struct device_driver drv;
+	struct rmi_sensor_device *sensor_device;
+
+	/* Attention Function
+	 *  This function is called by the low level isr in the physical
+	 * driver. It merely schedules work to be done.
+	 */
+	void (*attention)(struct rmi_phys_driver *physdrvr, int instance);
+	/* Probe Function
+	 *  This function is called to give the sensor driver layer an
+	 *  opportunity to claim an RMI device.  The sensor layer cannot
+	 *  read RMI registers at this point since the rmi physical driver
+	 *  has not been bound to it yet.  Defer that to the config
+	 *  function call which occurs immediately after a successful probe.
+	 */
+	int (*probe)(struct rmi_sensor_driver *sensor);
+	/* Config Function
+	 *  This function is called after a successful probe.  It gives the
+	 *  sensor driver an opportunity to query and/or configure an RMI
+	 *  device before data starts flowing.
+	 */
+	void (*config)(struct rmi_sensor_driver *sensor);
+
+	/* Functions can call this in order to dispatch IRQs. */
+	void (*dispatchIRQs)(struct rmi_sensor_driver *sensor,
+		unsigned int irqStatus);
+
+	/* Register Functions
+	 *  This function is called in the rmi bus
+	 * driver to have the sensor driver scan for any supported
+	 * functions on the sensor and add devices for each one.
+	 */
+	void (*rmi_sensor_register_functions)(struct rmi_sensor_driver
+							*sensor);
+
+	unsigned int interruptRegisterCount;
+
+	bool polling_required;
+
+	/* pointer to the corresponding phys driver info for this sensor */
+	/* The phys driver has the pointers to read, write, etc. */
+	struct rmi_phys_driver *rpd;
+
+	struct hrtimer timer;
+	struct work_struct work;
+	bool workIsReady;
+
+	/* This list is for keeping around the list of sensors.
+	 *  Every time that a physical device is detected by the
+	 *  physical layer - be it i2c, spi, or some other - then
+	 *  we need to bind the physical layer to the device. When
+	 *  the Page Descriptor Table is scanned and when Function $01
+	 *  is found then a new sensor device is created. The corresponding
+	 *  rmi_phys_driver struct pointer needs to be bound to the new
+	 *  sensor since Function $01 will be used to control and get
+	 *  interrupt information about the particular data source that is
+	 *  doing the interrupt. The rmi_phys_driver contains the pointers
+	 *  to the particular read, write, read_multiple, write_multiple
+	 *  functions for this device. This rmi_phys_driver struct will
+	 *  have to be up-bound to any drivers upstream that need it.
+	 */
+
+	/* Standard kernel linked list implementation.
+	 *  Documentation on how to use it can be found at
+	 *  http://isis.poly.edu/kulesh/stuff/src/klist/.
+	 */
+	struct list_head sensor_drivers; /* link sensor drivers into list */
+
+	struct list_head functions;     /* List of rmi_function_infos */
+	/* Per function initialization data. */
+	struct rmi_functiondata_list *perfunctiondata;
+};
+
+/* macro to get the pointer to the device_driver struct from the sensor */
+#define to_rmi_sensor_driver(drv) container_of(drv, \
+		struct rmi_sensor_driver, drv);
+
+struct rmi_sensor_device {
+	struct rmi_sensor_driver *driver;
+	struct device dev;
+
+	/* Standard kernel linked list implementation.
+	 *  Documentation on how to use it can be found at
+	 *  http://isis.poly.edu/kulesh/stuff/src/klist/.
+	 */
+	struct list_head sensors; /* link sensors into list */
+};
+
+int rmi_sensor_register_device(struct rmi_sensor_device *dev, int index);
+int rmi_sensor_register_driver(struct rmi_sensor_driver *driver);
+int rmi_sensor_register_functions(struct rmi_sensor_driver *sensor);
+bool rmi_polling_required(struct rmi_sensor_driver *sensor);
+
+static inline void *rmi_sensor_get_functiondata(struct rmi_sensor_driver
+		*driver, unsigned char function_index)
+{
+	int i;
+	if (driver->perfunctiondata) {
+		for (i = 0; i < driver->perfunctiondata->count; i++) {
+			if (driver->perfunctiondata->functiondata[i].
+					function_index == function_index)
+				return driver->perfunctiondata->
+					functiondata[i].data;
+		}
+	}
+	return NULL;
+}
+
+#endif
diff --git a/drivers/input/touchscreen/synaptics/rmi_spi.c b/drivers/input/touchscreen/synaptics/rmi_spi.c
new file mode 100644
index 0000000..d6b247d
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics/rmi_spi.c
@@ -0,0 +1,616 @@
+/**
+ *
+ * Synaptics Register Mapped Interface (RMI4) SPI Physical Layer Driver.
+ * Copyright (C) 2008-2011, Synaptics Incorporated
+ *
+ */
+/*
+ * This file is licensed under the GPL2 license.
+ *
+ *############################################################################
+ * GPL
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ *############################################################################
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/spi/spi.h>
+#include <linux/platform_device.h>
+#include <linux/semaphore.h>
+#include <linux/spi/spi.h>
+#include <linux/input/rmi_platformdata.h>
+#include "rmi_spi.h"
+#include "rmi_drvr.h"
+
+#define COMM_DEBUG  1 /* Set to 1 to dump transfers. */
+
+/* 65 microseconds inter-byte delay between bytes for RMI chip*/
+#define RMI_DEFAULT_BYTE_DELAY_US	0 /* 65 */
+#define	SPI_BUFFER_SIZE	32
+
+static u8 *buf;
+
+/* This is the data kept on a per instance (client) basis.  This data is
+ * always accessible by using the container_of() macro of the various elements
+ * inside.
+ */
+struct spi_device_instance_data {
+	int		instance_no;
+	int		irq;
+	unsigned int	byte_delay_us;
+	struct		rmi_phys_driver rpd;
+	struct		spi_device *spidev;
+	struct		rmi_spi_platformdata *platformdata;
+};
+
+static int spi_xfer(struct spi_device_instance_data *instance_data,
+	const u8 *txbuf, unsigned n_tx, u8 *rxbuf, unsigned n_rx)
+{
+	struct spi_device *spi = instance_data->spidev;
+#if COMM_DEBUG
+	int i;
+#endif
+	int status;
+	struct spi_message message;
+	struct spi_transfer *xfer_list;
+	u8 *local_buf;
+	int nXfers = 0;
+	int xfer_index = 0;
+
+	if ((n_tx + n_rx) > SPI_BUFFER_SIZE)
+		return -EINVAL;
+
+	if (n_tx)
+		nXfers += 1;
+	if (n_rx) {
+		if (instance_data->byte_delay_us)
+			nXfers += n_rx;
+		else
+			nXfers += 1;
+	}
+
+	xfer_list = kcalloc(nXfers, sizeof(struct spi_transfer), GFP_KERNEL);
+	if (!xfer_list)
+		return -ENOMEM;
+
+	/* ... unless someone else is using the pre-allocated buffer */
+	local_buf = kzalloc(SPI_BUFFER_SIZE, GFP_KERNEL);
+	if (!local_buf) {
+		kfree(xfer_list);
+		return -ENOMEM;
+	}
+
+	spi_message_init(&message);
+
+	if (n_tx) {
+		memset(&xfer_list[0], 0, sizeof(struct spi_transfer));
+		xfer_list[0].len = n_tx;
+		xfer_list[0].delay_usecs = instance_data->byte_delay_us;
+		spi_message_add_tail(&xfer_list[0], &message);
+		memcpy(local_buf, txbuf, n_tx);
+		xfer_list[0].tx_buf = local_buf;
+		xfer_index++;
+	}
+	if (n_rx) {
+		if (instance_data->byte_delay_us) {
+			int buffer_offset = n_tx;
+			for (; xfer_index < nXfers; xfer_index++) {
+				memset(&xfer_list[xfer_index], 0,
+						sizeof(struct spi_transfer));
+				xfer_list[xfer_index].len = 1;
+				xfer_list[xfer_index].delay_usecs =
+					instance_data->byte_delay_us;
+				xfer_list[xfer_index].rx_buf =
+					local_buf + buffer_offset;
+				buffer_offset++;
+				spi_message_add_tail(&xfer_list[xfer_index],
+						&message);
+#ifdef CONFIG_ARCH_OMAP
+				printk(KERN_INFO "%s: Did you compensate for
+					      ARCH_OMAP?", __func__);
+/*		x[1].len = n_rx-1; */	/* since OMAP has one dummy byte. */
+#else
+/*		x[1].len = n_rx;  */
+#endif
+			}
+		} else {
+			memset(&xfer_list[xfer_index], 0, sizeof(struct
+						spi_transfer));
+#ifdef CONFIG_ARCH_OMAP
+			/* since OMAP has one dummy byte. */
+			xfer_list[xfer_index].len = n_rx-1;
+#else
+			xfer_list[xfer_index].len = n_rx;
+#endif
+			xfer_list[xfer_index].rx_buf = local_buf + n_tx;
+			spi_message_add_tail(&xfer_list[xfer_index],
+					&message);
+			xfer_index++;
+		}
+	}
+	printk(KERN_INFO "%s: Ready to go, xfer_index = %d, nXfers = %d.",
+			__func__, xfer_index, nXfers);
+#if COMM_DEBUG
+	printk(KERN_INFO "%s: SPI transmits %d bytes...", __func__, n_tx);
+	for (i = 0; i < n_tx; i++)
+		printk(KERN_INFO "    0x%02X", local_buf[i]);
+#endif
+
+	/* do the i/o */
+	status = spi_sync(spi, &message);
+	if (status == 0) {
+		memcpy(rxbuf, local_buf + n_tx, n_rx);
+		status = message.status;
+#if COMM_DEBUG
+		if (n_rx) {
+			printk(KERN_INFO "%s: SPI received %d bytes...",
+					__func__, n_rx);
+			for (i = 0; i < n_rx; i++)
+				printk(KERN_INFO "    0x%02X", rxbuf[i]);
+		}
+#endif
+	} else {
+		printk(KERN_ERR "%s: spi_sync failed with error code %d.",
+				__func__, status);
+	}
+
+	kfree(local_buf);
+	kfree(xfer_list);
+
+	return status;
+}
+
+/**
+ * Read a single register through spi.
+ * \param[in] pd
+ * \param[in] address The address at which to start the data read.
+ * \param[out] valp Pointer to the buffer where the data will be stored.
+ * \return zero upon success (with the byte read in valp),non-zero upon error.
+ */
+static int
+rmi_spi_read(struct rmi_phys_driver *pd, unsigned short address, char *valp)
+{
+	struct spi_device_instance_data *id =
+		container_of(pd, struct spi_device_instance_data, rpd);
+
+	char rxbuf[2];
+	int retval;
+	unsigned short addr = address;
+
+	addr = ((addr & 0xff00) >> 8);
+	address = ((address & 0x00ff) << 8);
+	addr |= address;
+	addr |= 0x80;		/* High bit set indicates read. */
+
+	retval = spi_xfer(id, (u8 *)&addr, 2, rxbuf, 1);
+
+	*valp = rxbuf[0];
+
+	return retval;
+}
+
+/**
+ * Same as rmi_spi_read, except that multiple bytes are allowed to be read.
+ * \param[in] pd
+ * \param[in] address The address at which to start the data read.
+ * \param[out] valp Pointer to the buffer where the data will be stored.  This
+ * buffer must be at least size bytes long.
+ * \param[in] size The number of bytes to be read.
+ * \return zero upon success(with the byte read in valp), non-zero upon error.
+ */
+static int
+rmi_spi_read_multiple(struct rmi_phys_driver *pd, unsigned short address,
+	char *valp, int size)
+{
+	struct spi_device_instance_data *id =
+		container_of(pd, struct spi_device_instance_data, rpd);
+	int retval;
+
+	unsigned short addr = address;
+
+	addr = ((addr & 0xff00) >> 8);
+	address = ((address & 0x00ff) << 8);
+	addr |= address;
+	addr |= 0x80;		/* High bit set indicates read. */
+
+	retval = spi_xfer(id, (u8 *)&addr, 2, valp, size);
+
+	return retval;
+}
+
+/**
+ * Write a single register through spi.
+ * You can write multiple registers at once, but I made the functions for that
+ * seperate for performance reasons.  Writing multiple requires allocation and
+ * freeing.
+ * \param[in] pd
+ * \param[in] address The address at which to start the write.
+ * \param[in] data The data to be written.
+ * \return one upon success, something else upon error.
+ */
+static int
+rmi_spi_write(struct rmi_phys_driver *pd, unsigned short address, char data)
+{
+	struct spi_device_instance_data *id =
+		container_of(pd, struct spi_device_instance_data, rpd);
+	unsigned char txbuf[4];
+	int retval;
+
+	txbuf[2]  = data;
+	txbuf[1]  = address;
+	txbuf[0]  = address>>8;
+
+	retval = spi_xfer(id, txbuf, 3, NULL, 0);
+	return retval ? 0 : 1;
+}
+
+/**
+ * Write multiple registers.
+ * \param[in] pd
+ * \param[in] address The address at which to start the write.
+ * \param[in] valp A pointer to a buffer containing the data to be written.
+ * \param[in] size The number of bytes to write.
+ * \return one upon success, something else upon error.
+ */
+static int
+rmi_spi_write_multiple(struct rmi_phys_driver *pd, unsigned short address,
+	char *valp, int size)
+{
+	struct spi_device_instance_data *id =
+		container_of(pd, struct spi_device_instance_data, rpd);
+	unsigned char txbuf[32];
+	int retval;
+	int i;
+
+	txbuf[1]  = address;
+	txbuf[0]  = address>>8;
+
+	for (i = 0; i < size; i++)
+		txbuf[i + 2] = valp[i];
+
+	retval = spi_xfer(id, txbuf, size+2, NULL, 0);
+
+	return retval ? 0 : 1;
+}
+
+/**
+ * This is the Interrupt Service Routine.
+ * It just notifies the physical device
+ * that attention is required.
+ */
+static irqreturn_t spi_attn_isr(int irq, void *info)
+{
+	struct spi_device_instance_data *instance_data = info;
+	disable_irq_nosync(instance_data->irq);
+	if (instance_data->rpd.attention)
+		instance_data->rpd.attention(&instance_data->rpd,
+				instance_data->instance_no);
+	return IRQ_HANDLED;
+}
+
+/* TODO: Move this to rmi_bus, and call a function to get the next sensorID
+ */
+static int sensor_count;
+
+static int __devinit rmi_spi_probe(struct spi_device *spi)
+{
+	struct spi_device_instance_data *instance_data;
+	int retval;
+	struct rmi_spi_platformdata *platformdata;
+	struct rmi_sensordata *sensordata;
+	int irqtype = 0;
+
+	printk(KERN_INFO "Probing RMI4 SPI device\n");
+
+	/* This should have already been set up in the board file,
+	   shouldn't it? */
+	spi->bits_per_word = 8;
+
+	spi->mode = SPI_MODE_3;
+
+	retval = spi_setup(spi);
+	if (retval < 0) {
+		printk(KERN_ERR "%s: spi_setup failed with %d.", __func__,
+				retval);
+		return retval;
+	}
+
+	buf = kzalloc(SPI_BUFFER_SIZE, GFP_KERNEL);
+	if (!buf) {
+		printk(KERN_ERR "%s: Failed to allocate memory for spi
+				buffer.", __func__);
+		return -ENOMEM;
+	}
+
+	instance_data = kzalloc(sizeof(*instance_data), GFP_KERNEL);
+	if (!instance_data) {
+		printk(KERN_ERR "%s: Failer to allocate memory for instance
+				data.", __func__);
+		return -ENOMEM;
+	}
+
+	instance_data->byte_delay_us      = RMI_DEFAULT_BYTE_DELAY_US;
+	instance_data->spidev             = spi;
+	instance_data->rpd.name           = RMI4_SPI_DRIVER_NAME;
+	instance_data->rpd.write          = rmi_spi_write;
+	instance_data->rpd.read           = rmi_spi_read;
+	instance_data->rpd.write_multiple = rmi_spi_write_multiple;
+	instance_data->rpd.read_multiple  = rmi_spi_read_multiple;
+	instance_data->rpd.module         = THIS_MODULE;
+	/* default to polling if irq not used */
+	instance_data->rpd.polling_required = true;
+
+	platformdata = spi->dev.platform_data;
+	if (platformdata == NULL) {
+		printk(KERN_ERR "%s: CONFIGURATION ERROR - platform data
+				is NULL.", __func__);
+		return -EINVAL;
+	}
+
+	instance_data->platformdata = platformdata;
+	sensordata = platformdata->sensordata;
+
+	/* Call the platform setup routine, to do any setup that is required
+	 * before
+	 * interacting with the device.
+	 */
+	if (sensordata && sensordata->rmi_sensor_setup) {
+		retval = sensordata->rmi_sensor_setup();
+		if (retval) {
+			printk(KERN_ERR "%s: sensor setup failed with
+					code %d.", __func__, retval);
+			kfree(instance_data);
+			return retval;
+		}
+	}
+
+	/* TODO: I think this if is no longer required. */
+	if (platformdata->chip == RMI_SUPPORT) {
+		instance_data->instance_no = sensor_count;
+		sensor_count++;
+
+		/* set the device name using the instance_no
+		 * appended to DEVICE_NAME to make a unique name
+		 */
+		dev_set_name(&spi->dev, "%s%d", RMI4_SPI_DEVICE_NAME,
+						instance_data->instance_no);
+		/*
+		 * Determine if we need to poll (inefficient) or
+		 * use interrupts.
+		 */
+		if (platformdata->irq) {
+			switch (platformdata->irq_type) {
+			case IORESOURCE_IRQ_HIGHEDGE:
+				irqtype = IRQF_TRIGGER_RISING;
+				break;
+			case IORESOURCE_IRQ_LOWEDGE:
+				irqtype = IRQF_TRIGGER_FALLING;
+				break;
+			case IORESOURCE_IRQ_HIGHLEVEL:
+				irqtype = IRQF_TRIGGER_HIGH;
+				break;
+			case IORESOURCE_IRQ_LOWLEVEL:
+				irqtype = IRQF_TRIGGER_LOW;
+				break;
+			default:
+				dev_warn(&spi->dev, "%s: Invalid IRQ flags
+					   in platform data.", __func__);
+				retval = -ENXIO;
+				goto error_exit;
+			}
+/*
+			retval = request_irq(instance_data->irq, spi_attn_isr,
+					irqtype, "rmi_spi", instance_data);
+			if (retval) {
+				dev_info(&spi->dev, "%s: Unable to get attn
+				irq %d. Reverting to polling. ", __func__,
+				instance_data->irq);
+				instance_data->rpd.polling_required = true;
+			} else {
+				dev_dbg(&spi->dev, "%s: got irq", __func__);
+				instance_data->rpd.polling_required = false;
+				instance_data->rpd.irq = instance_data->irq;
+			}
+*/
+			instance_data->rpd.polling_required = false;
+		} else {
+			instance_data->rpd.polling_required = true;
+			dev_info(&spi->dev, "%s: No IRQ info given.
+				      Polling required.", __func__);
+		}
+	}
+
+	/* Store instance data for later access. */
+	if (instance_data)
+		spi_set_drvdata(spi, instance_data);
+
+	/* Register the sensor driver -
+	 * which will trigger a scan of the PDT.
+	 */
+	retval = rmi_register_sensor(&instance_data->rpd,
+			platformdata->sensordata);
+	if (retval) {
+		printk(KERN_ERR "%s: sensor registration failed with code
+				%d.", __func__, retval);
+		goto error_exit;
+	}
+
+	if (instance_data->rpd.polling_required == false) {
+		instance_data->irq = platformdata->irq;
+		retval = request_irq(platformdata->irq, spi_attn_isr,
+				irqtype, dev_name(&spi->dev), instance_data);
+		if (retval) {
+			dev_err(&spi->dev, "%s: failed to obtain IRQ %d.
+					Result: %d.", __func__,
+					platformdata->irq, retval);
+			dev_info(&spi->dev, "%s: Reverting to polling.\n",
+					__func__);
+			instance_data->rpd.polling_required = true;
+			instance_data->irq = 0;
+			/* TODO: Need to revert back to polling
+			 * - create and start timer.
+			 */
+		} else {
+			dev_dbg(&spi->dev, "%s: got irq.\n", __func__);
+			instance_data->rpd.irq = instance_data->irq;
+		}
+	}
+
+	printk(KERN_INFO "%s: Successfully Registered %s.",
+			__func__, instance_data->rpd.name);
+
+	return 0;
+
+error_exit:
+	if (sensordata && sensordata->rmi_sensor_teardown)
+		sensordata->rmi_sensor_teardown();
+	if (instance_data->irq)
+		free_irq(instance_data->irq, instance_data);
+	kfree(instance_data);
+	return retval;
+}
+
+static int rmi_spi_suspend(struct spi_device *spi, pm_message_t message)
+{
+	printk(KERN_INFO "%s: Suspending...", __func__);
+	return 0;
+}
+
+static int rmi_spi_resume(struct spi_device *spi)
+{
+	printk(KERN_INFO "%s: Resuming...", __func__);
+	return 0;
+}
+
+static int __devexit rmi_spi_remove(struct spi_device *spi)
+{
+	struct spi_device_instance_data *id = spi_get_drvdata(spi);
+
+	printk(KERN_INFO "%s: RMI SPI device removed.", __func__);
+
+	rmi_spi_suspend(spi, PMSG_SUSPEND);
+
+	rmi_unregister_sensors(&id->rpd);
+
+	if (id) {
+		if (id->irq)
+			free_irq(id->irq, id);
+		kfree(id);
+	}
+
+	return 0;
+}
+
+static struct spi_driver rmi_spi_driver = {
+	.driver = {
+		.name  = RMI4_SPI_DRIVER_NAME,
+		.bus   = &spi_bus_type,
+		.owner = THIS_MODULE,
+	},
+	.probe    = rmi_spi_probe,
+	.remove   = __devexit_p(rmi_spi_remove),
+	.suspend  = rmi_spi_suspend,
+	.resume   = rmi_spi_resume,
+};
+
+/**
+ * The Platform Driver probe function.  We just tell the spi subsystem about
+ * ourselves in this call.
+ */
+static int
+rmi_spi_plat_probe(struct platform_device *dev)
+{
+	struct rmi_spi_platformdata *platform_data = dev->dev.platform_data;
+
+	printk(KERN_INFO "%s: Platform driver probe.", __func__);
+
+	if (!platform_data) {
+		printk(KERN_ERR "A platform device must contain
+				rmi_spi_platformdata\n");
+		return -ENXIO;
+	}
+
+	return spi_register_driver(&rmi_spi_driver);
+}
+
+/**
+ * Tell the spi subsystem that we're done.
+ * \param[in] dev
+ * \return Always returns 0.
+ */
+static int
+rmi_spi_plat_remove(struct platform_device *dev)
+{
+	printk(KERN_INFO "%s: Platform driver removed.", __func__);
+	spi_unregister_driver(&rmi_spi_driver);
+	return 0;
+}
+
+/**
+ * Structure used to tell the Platform Driver subsystem about us.
+ */
+static struct platform_driver rmi_spi_platform_driver = {
+	.driver		= {
+		.name	= RMI4_SPI_DRIVER_NAME,
+		.owner	= THIS_MODULE,
+	},
+	.probe		= rmi_spi_plat_probe,
+	.remove		= __devexit_p(rmi_spi_plat_remove),
+};
+
+static int __init rmi_spi_init(void)
+{
+	int retval;
+
+	printk(KERN_INFO "%s: RMI SPI physical layer initialization.",
+							__func__);
+	retval = spi_register_driver(&rmi_spi_driver);
+	if (retval < 0) {
+		printk(KERN_ERR "%s: Failed to register spi driver, code
+				= %d.", __func__, retval);
+		return retval;
+	}
+/*
+#else
+	retval = platform_driver_register(&rmi_spi_platform_driver);
+	if (retval < 0) {
+		printk(KERN_ERR "%s: Failed to register platform driver,
+				code = %d.", __func__, retval);
+		return retval;
+	}
+#endif
+*/
+	printk(KERN_INFO "%s:    result = %d", __func__, retval);
+	return retval;
+}
+module_init(rmi_spi_init);
+
+static void __exit rmi_spi_exit(void)
+{
+	printk(KERN_INFO "%s: RMI SPI physical layer exits.", __func__);
+	kfree(buf);
+	buf = NULL;
+	platform_driver_unregister(&rmi_spi_platform_driver);
+}
+module_exit(rmi_spi_exit);
+
+/** Standard driver module information - the author of the module.
+ */
+MODULE_AUTHOR("Synaptics, Inc.");
+/** Standard driver module information - a summary description of this module.
+ */
+MODULE_DESCRIPTION("RMI4 Driver SPI Physical Layer");
+/** Standard driver module information - the license under which this module
+ * is included in the kernel.
+ */
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/synaptics/rmi_spi.h b/drivers/input/touchscreen/synaptics/rmi_spi.h
new file mode 100644
index 0000000..daeebed
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics/rmi_spi.h
@@ -0,0 +1,57 @@
+/**
+ *
+ * Register Mapped Interface SPI Physical Layer Driver Header File.
+ * Copyright (C) 2008-2011, Synaptics Incorporated
+ *
+ */
+/*
+ * This file is licensed under the GPL2 license.
+ *
+ *#############################################################################
+ * GPL
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ *#############################################################################
+ */
+
+#if !defined(_RMI_SPI_H)
+#define _RMI_SPI_H
+
+#include <linux/input/rmi_platformdata.h>
+
+#define RMI_CHIP_VER_3	0
+#define RMI_CHIP_VER_4	1
+
+#define RMI_SUPPORT (RMI_CHIP_VER_3|RMI_CHIP_VER_4)
+
+#define RMI4_SPI_DRIVER_NAME "rmi4_ts"
+#define RMI4_SPI_DEVICE_NAME "rmi4_ts"
+
+/** Platform-specific configuration data.
+ * This structure is used by the platform-specific driver to designate
+ * specific information about the hardware.  A platform client may supply
+ * an array of these to the rmi_phys_spi driver.
+ */
+struct rmi_spi_platformdata {
+	int chip;
+
+	/* The number of the irq.  Set to zero if polling is required. */
+	int irq;
+
+	/* The type of the irq (e.g., IRQF_TRIGGER_FALLING).  Only valid if
+	* irq != 0 */
+	int irq_type;
+
+	/* Use this to specify platformdata that is not I2C specific. */
+	struct rmi_sensordata *sensordata;
+};
+
+#endif
diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c
index 1473d23..e59ca17 100644
--- a/drivers/input/touchscreen/tsc2007.c
+++ b/drivers/input/touchscreen/tsc2007.c
@@ -26,6 +26,12 @@
 #include <linux/interrupt.h>
 #include <linux/i2c.h>
 #include <linux/i2c/tsc2007.h>
+#include <linux/pm.h>
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+#include <linux/earlysuspend.h>
+#define TSC2007_SUSPEND_LEVEL 1
+#endif
 
 #define TSC2007_MEASURE_TEMP0		(0x0 << 4)
 #define TSC2007_MEASURE_AUX		(0x2 << 4)
@@ -66,6 +72,7 @@
 struct tsc2007 {
 	struct input_dev	*input;
 	char			phys[32];
+	struct delayed_work	work;
 
 	struct i2c_client	*client;
 
@@ -74,14 +81,25 @@
 	u16			max_rt;
 	unsigned long		poll_delay;
 	unsigned long		poll_period;
+	u16			min_x;
+	u16			max_x;
+	u16			min_y;
+	u16			max_y;
 
+	bool			pendown;
 	int			irq;
 
-	wait_queue_head_t	wait;
-	bool			stopped;
+	bool			invert_x;
+	bool			invert_y;
+	bool			invert_z1;
+	bool			invert_z2;
 
 	int			(*get_pendown_state)(void);
 	void			(*clear_penirq)(void);
+	int			(*power_shutdown)(bool);
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+	struct early_suspend	early_suspend;
+#endif
 };
 
 static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
@@ -118,6 +136,18 @@
 	tc->z1 = tsc2007_xfer(tsc, READ_Z1);
 	tc->z2 = tsc2007_xfer(tsc, READ_Z2);
 
+	if (tsc->invert_x == true)
+		tc->x = MAX_12BIT - tc->x;
+
+	if (tsc->invert_y == true)
+		tc->y = MAX_12BIT - tc->y;
+
+	if (tsc->invert_z1 == true)
+		tc->z1 = MAX_12BIT - tc->z1;
+
+	if (tsc->invert_z2 == true)
+		tc->z2 = MAX_12BIT - tc->z2;
+
 	/* Prepare for next touch reading - power down ADC, enable PENIRQ */
 	tsc2007_xfer(tsc, PWRDOWN);
 }
@@ -142,8 +172,25 @@
 	return rt;
 }
 
-static bool tsc2007_is_pen_down(struct tsc2007 *ts)
+static void tsc2007_send_up_event(struct tsc2007 *tsc)
 {
+	struct input_dev *input = tsc->input;
+
+	dev_dbg(&tsc->client->dev, "UP\n");
+
+	input_report_key(input, BTN_TOUCH, 0);
+	input_report_abs(input, ABS_PRESSURE, 0);
+	input_sync(input);
+}
+
+static void tsc2007_work(struct work_struct *work)
+{
+	struct tsc2007 *ts =
+		container_of(to_delayed_work(work), struct tsc2007, work);
+	bool debounced = false;
+	struct ts_event tc;
+	u32 rt;
+
 	/*
 	 * NOTE: We can't rely on the pressure to determine the pen down
 	 * state, even though this controller has a pressure sensor.
@@ -154,82 +201,79 @@
 	 * The only safe way to check for the pen up condition is in the
 	 * work function by reading the pen signal state (it's a GPIO
 	 * and IRQ). Unfortunately such callback is not always available,
-	 * in that case we assume that the pen is down and expect caller
-	 * to fall back on the pressure reading.
+	 * in that case we have rely on the pressure anyway.
 	 */
-
-	if (!ts->get_pendown_state)
-		return true;
-
-	return ts->get_pendown_state();
-}
-
-static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
-{
-	struct tsc2007 *ts = handle;
-	struct input_dev *input = ts->input;
-	struct ts_event tc;
-	u32 rt;
-
-	while (!ts->stopped && tsc2007_is_pen_down(ts)) {
-
-		/* pen is down, continue with the measurement */
-		tsc2007_read_values(ts, &tc);
-
-		rt = tsc2007_calculate_pressure(ts, &tc);
-
-		if (rt == 0 && !ts->get_pendown_state) {
-			/*
-			 * If pressure reported is 0 and we don't have
-			 * callback to check pendown state, we have to
-			 * assume that pen was lifted up.
-			 */
-			break;
+	if (ts->get_pendown_state) {
+		if (unlikely(!ts->get_pendown_state())) {
+			tsc2007_send_up_event(ts);
+			ts->pendown = false;
+			goto out;
 		}
 
-		if (rt <= ts->max_rt) {
-			dev_dbg(&ts->client->dev,
-				"DOWN point(%4d,%4d), pressure (%4u)\n",
-				tc.x, tc.y, rt);
-
-			input_report_key(input, BTN_TOUCH, 1);
-			input_report_abs(input, ABS_X, tc.x);
-			input_report_abs(input, ABS_Y, tc.y);
-			input_report_abs(input, ABS_PRESSURE, rt);
-
-			input_sync(input);
-
-		} else {
-			/*
-			 * Sample found inconsistent by debouncing or pressure is
-			 * beyond the maximum. Don't report it to user space,
-			 * repeat at least once more the measurement.
-			 */
-			dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
-		}
-
-		wait_event_timeout(ts->wait, ts->stopped,
-				   msecs_to_jiffies(ts->poll_period));
+		dev_dbg(&ts->client->dev, "pen is still down\n");
 	}
 
-	dev_dbg(&ts->client->dev, "UP\n");
+	tsc2007_read_values(ts, &tc);
 
-	input_report_key(input, BTN_TOUCH, 0);
-	input_report_abs(input, ABS_PRESSURE, 0);
-	input_sync(input);
+	rt = tsc2007_calculate_pressure(ts, &tc);
+	if (rt > ts->max_rt) {
+		/*
+		 * Sample found inconsistent by debouncing or pressure is
+		 * beyond the maximum. Don't report it to user space,
+		 * repeat at least once more the measurement.
+		 */
+		dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
+		debounced = true;
+		goto out;
 
-	if (ts->clear_penirq)
-		ts->clear_penirq();
+	}
 
-	return IRQ_HANDLED;
+	if (rt) {
+		struct input_dev *input = ts->input;
+
+		if (!ts->pendown) {
+			dev_dbg(&ts->client->dev, "DOWN\n");
+
+			input_report_key(input, BTN_TOUCH, 1);
+			ts->pendown = true;
+		}
+
+		input_report_abs(input, ABS_X, tc.x);
+		input_report_abs(input, ABS_Y, tc.y);
+		input_report_abs(input, ABS_PRESSURE, rt);
+
+		input_sync(input);
+
+		dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n",
+			tc.x, tc.y, rt);
+
+	} else if (!ts->get_pendown_state && ts->pendown) {
+		/*
+		 * We don't have callback to check pendown state, so we
+		 * have to assume that since pressure reported is 0 the
+		 * pen was lifted up.
+		 */
+		tsc2007_send_up_event(ts);
+		ts->pendown = false;
+	}
+
+ out:
+	if (ts->pendown || debounced)
+		schedule_delayed_work(&ts->work,
+				      msecs_to_jiffies(ts->poll_period));
+	else
+		enable_irq(ts->irq);
 }
 
-static irqreturn_t tsc2007_hard_irq(int irq, void *handle)
+static irqreturn_t tsc2007_irq(int irq, void *handle)
 {
 	struct tsc2007 *ts = handle;
 
-	if (!ts->get_pendown_state || likely(ts->get_pendown_state()))
-		return IRQ_WAKE_THREAD;
+	if (!ts->get_pendown_state || likely(ts->get_pendown_state())) {
+		disable_irq_nosync(ts->irq);
+		schedule_delayed_work(&ts->work,
+				      msecs_to_jiffies(ts->poll_delay));
+	}
 
 	if (ts->clear_penirq)
 		ts->clear_penirq();
@@ -237,42 +281,85 @@
 	return IRQ_HANDLED;
 }
 
-static void tsc2007_stop(struct tsc2007 *ts)
+static void tsc2007_free_irq(struct tsc2007 *ts)
 {
-	ts->stopped = true;
-	mb();
-	wake_up(&ts->wait);
-
-	disable_irq(ts->irq);
+	free_irq(ts->irq, ts);
+	if (cancel_delayed_work_sync(&ts->work)) {
+		/*
+		 * Work was pending, therefore we need to enable
+		 * IRQ here to balance the disable_irq() done in the
+		 * interrupt handler.
+		 */
+		enable_irq(ts->irq);
+	}
 }
 
-static int tsc2007_open(struct input_dev *input_dev)
+#ifdef CONFIG_PM
+static int tsc2007_suspend(struct device *dev)
 {
-	struct tsc2007 *ts = input_get_drvdata(input_dev);
-	int err;
+	int rc;
+	struct tsc2007	*ts = dev_get_drvdata(dev);
 
-	ts->stopped = false;
-	mb();
+	disable_irq(ts->irq);
 
-	enable_irq(ts->irq);
+	if (cancel_delayed_work_sync(&ts->work))
+		enable_irq(ts->irq);
 
-	/* Prepare for touch readings - power down ADC and enable PENIRQ */
-	err = tsc2007_xfer(ts, PWRDOWN);
-	if (err < 0) {
-		tsc2007_stop(ts);
-		return err;
+	if (ts->power_shutdown) {
+		rc = ts->power_shutdown(true);
+		if (rc) {
+			pr_err("%s: Power off failed, suspend failed (%d)\n",
+							__func__, rc);
+			return rc;
+		}
 	}
 
 	return 0;
 }
 
-static void tsc2007_close(struct input_dev *input_dev)
+static int tsc2007_resume(struct device *dev)
 {
-	struct tsc2007 *ts = input_get_drvdata(input_dev);
+	int rc;
+	struct tsc2007	*ts = dev_get_drvdata(dev);
 
-	tsc2007_stop(ts);
+	if (ts->power_shutdown) {
+		rc = ts->power_shutdown(false);
+		if (rc) {
+			pr_err("%s: Power on failed, resume failed (%d)\n",
+							 __func__, rc);
+			return rc;
+		}
+	}
+
+	enable_irq(ts->irq);
+
+	return 0;
 }
 
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void tsc2007_early_suspend(struct early_suspend *h)
+{
+	struct tsc2007 *ts = container_of(h, struct tsc2007, early_suspend);
+
+	tsc2007_suspend(&ts->client->dev);
+}
+
+static void tsc2007_late_resume(struct early_suspend *h)
+{
+	struct tsc2007 *ts = container_of(h, struct tsc2007, early_suspend);
+
+	tsc2007_resume(&ts->client->dev);
+}
+#endif
+
+static const struct dev_pm_ops tsc2007_pm_ops = {
+#ifndef CONFIG_HAS_EARLYSUSPEND
+	.suspend	= tsc2007_suspend,
+	.resume		= tsc2007_resume,
+#endif
+};
+#endif
+
 static int __devinit tsc2007_probe(struct i2c_client *client,
 				   const struct i2c_device_id *id)
 {
@@ -300,7 +387,7 @@
 	ts->client = client;
 	ts->irq = client->irq;
 	ts->input = input_dev;
-	init_waitqueue_head(&ts->wait);
+	INIT_DELAYED_WORK(&ts->work, tsc2007_work);
 
 	ts->model             = pdata->model;
 	ts->x_plate_ohms      = pdata->x_plate_ohms;
@@ -309,12 +396,15 @@
 	ts->poll_period       = pdata->poll_period ? : 1;
 	ts->get_pendown_state = pdata->get_pendown_state;
 	ts->clear_penirq      = pdata->clear_penirq;
-
-	if (pdata->x_plate_ohms == 0) {
-		dev_err(&client->dev, "x_plate_ohms is not set up in platform data");
-		err = -EINVAL;
-		goto err_free_mem;
-	}
+	ts->invert_x	      = pdata->invert_x;
+	ts->invert_y	      = pdata->invert_y;
+	ts->invert_z1	      = pdata->invert_z1;
+	ts->invert_z2	      = pdata->invert_z2;
+	ts->min_x	      = pdata->min_x ? pdata->min_x : 0;
+	ts->max_x	      = pdata->max_x ? pdata->max_x : MAX_12BIT;
+	ts->min_y	      = pdata->min_y ? pdata->min_y : 0;
+	ts->max_y	      = pdata->max_y ? pdata->max_y : MAX_12BIT;
+	ts->power_shutdown    = pdata->power_shutdown;
 
 	snprintf(ts->phys, sizeof(ts->phys),
 		 "%s/input0", dev_name(&client->dev));
@@ -323,41 +413,49 @@
 	input_dev->phys = ts->phys;
 	input_dev->id.bustype = BUS_I2C;
 
-	input_dev->open = tsc2007_open;
-	input_dev->close = tsc2007_close;
-
-	input_set_drvdata(input_dev, ts);
-
 	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
 	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
 
-	input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, pdata->fuzzx, 0);
-	input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, pdata->fuzzy, 0);
+	input_set_abs_params(input_dev, ABS_X, ts->min_x,
+				ts->max_x, pdata->fuzzx, 0);
+	input_set_abs_params(input_dev, ABS_Y, ts->min_y,
+				ts->max_y, pdata->fuzzy, 0);
 	input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT,
 			pdata->fuzzz, 0);
 
 	if (pdata->init_platform_hw)
 		pdata->init_platform_hw();
 
-	err = request_threaded_irq(ts->irq, tsc2007_hard_irq, tsc2007_soft_irq,
-				   IRQF_ONESHOT, client->dev.driver->name, ts);
+	err = request_irq(ts->irq, tsc2007_irq, pdata->irq_flags,
+			client->dev.driver->name, ts);
 	if (err < 0) {
 		dev_err(&client->dev, "irq %d busy?\n", ts->irq);
 		goto err_free_mem;
 	}
 
-	tsc2007_stop(ts);
+	/* Prepare for touch readings - power down ADC and enable PENIRQ */
+	err = tsc2007_xfer(ts, PWRDOWN);
+	if (err < 0)
+		goto err_free_irq;
 
 	err = input_register_device(input_dev);
 	if (err)
 		goto err_free_irq;
 
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN +
+						TSC2007_SUSPEND_LEVEL;
+	ts->early_suspend.suspend = tsc2007_early_suspend;
+	ts->early_suspend.resume = tsc2007_late_resume;
+	register_early_suspend(&ts->early_suspend);
+#endif
+
 	i2c_set_clientdata(client, ts);
 
 	return 0;
 
  err_free_irq:
-	free_irq(ts->irq, ts);
+	tsc2007_free_irq(ts);
 	if (pdata->exit_platform_hw)
 		pdata->exit_platform_hw();
  err_free_mem:
@@ -371,11 +469,14 @@
 	struct tsc2007	*ts = i2c_get_clientdata(client);
 	struct tsc2007_platform_data *pdata = client->dev.platform_data;
 
-	free_irq(ts->irq, ts);
+	tsc2007_free_irq(ts);
 
 	if (pdata->exit_platform_hw)
 		pdata->exit_platform_hw();
 
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	unregister_early_suspend(&ts->early_suspend);
+#endif
 	input_unregister_device(ts->input);
 	kfree(ts);
 
@@ -392,7 +493,10 @@
 static struct i2c_driver tsc2007_driver = {
 	.driver = {
 		.owner	= THIS_MODULE,
-		.name	= "tsc2007"
+		.name	= "tsc2007",
+#ifdef CONFIG_PM
+		.pm = &tsc2007_pm_ops,
+#endif
 	},
 	.id_table	= tsc2007_idtable,
 	.probe		= tsc2007_probe,
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 3bd9fff..84a69bf 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -16,7 +16,7 @@
 # MSM IOMMU support
 config MSM_IOMMU
 	bool "MSM IOMMU Support"
-	depends on ARCH_MSM8X60 || ARCH_MSM8960
+	depends on ARCH_MSM8X60 || ARCH_MSM8960 || ARCH_APQ8064 || ARCH_MSMCOPPER
 	select IOMMU_API
 	help
 	  Support for the IOMMUs found on certain Qualcomm SOCs.
@@ -26,8 +26,14 @@
 	  If unsure, say N here.
 
 config IOMMU_PGTABLES_L2
-	def_bool y
+	bool "Allow SMMU page tables in the L2 cache (Experimental)"
 	depends on MSM_IOMMU && MMU && SMP && CPU_DCACHE_DISABLE=n
+	default y
+        help
+         Improves TLB miss latency at the expense of potential L2 pollution.
+         However, with large multimedia buffers, the TLB should mostly contain
+         section mappings and TLB misses should be quite infrequent.
+         Most people can probably say Y here.
 
 # AMD IOMMU support
 config AMD_IOMMU
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index 7ad7a3b..066bc3e 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -1,5 +1,8 @@
 obj-$(CONFIG_IOMMU_API) += iommu.o
 obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o
+ifdef CONFIG_OF
+obj-$(CONFIG_MSM_IOMMU) += msm_iommu-v2.o msm_iommu_dev-v2.o msm_iommu_pagetable.o
+endif
 obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
 obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o
 obj-$(CONFIG_DMAR_TABLE) += dmar.o
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 2198b2d..ef69d91 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -26,6 +26,7 @@
 #include <linux/slab.h>
 #include <linux/errno.h>
 #include <linux/iommu.h>
+#include <linux/scatterlist.h>
 
 static ssize_t show_iommu_group(struct device *dev,
 				struct device_attribute *attr, char *buf)
@@ -135,7 +136,7 @@
 }
 EXPORT_SYMBOL_GPL(iommu_set_fault_handler);
 
-struct iommu_domain *iommu_domain_alloc(struct bus_type *bus)
+struct iommu_domain *iommu_domain_alloc(struct bus_type *bus, int flags)
 {
 	struct iommu_domain *domain;
 	int ret;
@@ -149,7 +150,7 @@
 
 	domain->ops = bus->iommu_ops;
 
-	ret = domain->ops->domain_init(domain);
+	ret = domain->ops->domain_init(domain, flags);
 	if (ret)
 		goto out_free;
 
@@ -333,6 +334,39 @@
 }
 EXPORT_SYMBOL_GPL(iommu_unmap);
 
+int iommu_map_range(struct iommu_domain *domain, unsigned int iova,
+		    struct scatterlist *sg, unsigned int len, int prot)
+{
+	if (unlikely(domain->ops->map_range == NULL))
+		return -ENODEV;
+
+	BUG_ON(iova & (~PAGE_MASK));
+
+	return domain->ops->map_range(domain, iova, sg, len, prot);
+}
+EXPORT_SYMBOL_GPL(iommu_map_range);
+
+int iommu_unmap_range(struct iommu_domain *domain, unsigned int iova,
+		      unsigned int len)
+{
+	if (unlikely(domain->ops->unmap_range == NULL))
+		return -ENODEV;
+
+	BUG_ON(iova & (~PAGE_MASK));
+
+	return domain->ops->unmap_range(domain, iova, len);
+}
+EXPORT_SYMBOL_GPL(iommu_unmap_range);
+
+phys_addr_t iommu_get_pt_base_addr(struct iommu_domain *domain)
+{
+	if (unlikely(domain->ops->get_pt_base_addr == NULL))
+		return 0;
+
+	return domain->ops->get_pt_base_addr(domain);
+}
+EXPORT_SYMBOL_GPL(iommu_get_pt_base_addr);
+
 int iommu_device_group(struct device *dev, unsigned int *groupid)
 {
 	if (iommu_present(dev->bus) && dev->bus->iommu_ops->device_group)
diff --git a/drivers/iommu/msm_iommu-v2.c b/drivers/iommu/msm_iommu-v2.c
new file mode 100644
index 0000000..10d0b66
--- /dev/null
+++ b/drivers/iommu/msm_iommu-v2.c
@@ -0,0 +1,575 @@
+/* Copyright (c) 2012 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/iommu.h>
+#include <linux/clk.h>
+#include <linux/scatterlist.h>
+
+#include <asm/sizes.h>
+
+#include <mach/iommu_hw-v2.h>
+#include <mach/iommu.h>
+
+#include "msm_iommu_pagetable.h"
+
+/* bitmap of the page sizes currently supported */
+#define MSM_IOMMU_PGSIZES	(SZ_4K | SZ_64K | SZ_1M | SZ_16M)
+
+static DEFINE_MUTEX(msm_iommu_lock);
+
+struct msm_priv {
+	struct iommu_pt pt;
+	struct list_head list_attached;
+};
+
+static int __enable_clocks(struct msm_iommu_drvdata *drvdata)
+{
+	int ret;
+
+	ret = clk_prepare_enable(drvdata->pclk);
+	if (ret)
+		goto fail;
+
+	if (drvdata->clk) {
+		ret = clk_prepare_enable(drvdata->clk);
+		if (ret)
+			clk_disable_unprepare(drvdata->pclk);
+	}
+fail:
+	return ret;
+}
+
+static void __disable_clocks(struct msm_iommu_drvdata *drvdata)
+{
+	if (drvdata->clk)
+		clk_disable_unprepare(drvdata->clk);
+	clk_disable_unprepare(drvdata->pclk);
+}
+
+static int __flush_iotlb_va(struct iommu_domain *domain, unsigned int va)
+{
+	struct msm_priv *priv = domain->priv;
+	struct msm_iommu_drvdata *iommu_drvdata;
+	struct msm_iommu_ctx_drvdata *ctx_drvdata;
+	int asid;
+
+	list_for_each_entry(ctx_drvdata, &priv->list_attached, attached_elm) {
+		BUG_ON(!ctx_drvdata->pdev || !ctx_drvdata->pdev->dev.parent);
+
+		iommu_drvdata = dev_get_drvdata(ctx_drvdata->pdev->dev.parent);
+		BUG_ON(!iommu_drvdata);
+
+		asid = GET_CB_CONTEXTIDR_ASID(iommu_drvdata->base,
+					   ctx_drvdata->num);
+
+		SET_TLBIVA(iommu_drvdata->base, ctx_drvdata->num,
+			   asid | (va & CB_TLBIVA_VA));
+		mb();
+	}
+
+	return 0;
+}
+
+static int __flush_iotlb(struct iommu_domain *domain)
+{
+	struct msm_priv *priv = domain->priv;
+	struct msm_iommu_drvdata *iommu_drvdata;
+	struct msm_iommu_ctx_drvdata *ctx_drvdata;
+	int asid;
+
+	list_for_each_entry(ctx_drvdata, &priv->list_attached, attached_elm) {
+		BUG_ON(!ctx_drvdata->pdev || !ctx_drvdata->pdev->dev.parent);
+
+		iommu_drvdata = dev_get_drvdata(ctx_drvdata->pdev->dev.parent);
+		BUG_ON(!iommu_drvdata);
+
+		asid = GET_CB_CONTEXTIDR_ASID(iommu_drvdata->base,
+					   ctx_drvdata->num);
+
+		SET_TLBIASID(iommu_drvdata->base, ctx_drvdata->num, asid);
+		mb();
+	}
+
+	return 0;
+}
+
+static void __reset_context(void __iomem *base, int ctx)
+{
+	SET_ACTLR(base, ctx, 0);
+	SET_FAR(base, ctx, 0);
+	SET_FSRRESTORE(base, ctx, 0);
+	SET_NMRR(base, ctx, 0);
+	SET_PAR(base, ctx, 0);
+	SET_PRRR(base, ctx, 0);
+	SET_SCTLR(base, ctx, 0);
+	SET_TLBIALL(base, ctx, 0);
+	SET_TTBCR(base, ctx, 0);
+	SET_TTBR0(base, ctx, 0);
+	SET_TTBR1(base, ctx, 0);
+	mb();
+}
+
+static void __program_context(void __iomem *base, int ctx, int ncb,
+				phys_addr_t pgtable, int redirect)
+{
+	unsigned int prrr, nmrr;
+	unsigned int pn;
+	int i, j, found;
+
+	__reset_context(base, ctx);
+
+	pn = pgtable >> CB_TTBR0_ADDR_SHIFT;
+	SET_TTBCR(base, ctx, 0);
+	SET_CB_TTBR0_ADDR(base, ctx, pn);
+
+	/* Enable context fault interrupt */
+	SET_CB_SCTLR_CFIE(base, ctx, 1);
+
+	/* Redirect all cacheable requests to L2 slave port. */
+	SET_CB_ACTLR_BPRCISH(base, ctx, 1);
+	SET_CB_ACTLR_BPRCOSH(base, ctx, 1);
+	SET_CB_ACTLR_BPRCNSH(base, ctx, 1);
+
+	/* Turn on TEX Remap */
+	SET_CB_SCTLR_TRE(base, ctx, 1);
+
+	/* Enable private ASID namespace */
+	SET_CB_SCTLR_ASIDPNE(base, ctx, 1);
+
+	/* Set TEX remap attributes */
+	RCP15_PRRR(prrr);
+	RCP15_NMRR(nmrr);
+	SET_PRRR(base, ctx, prrr);
+	SET_NMRR(base, ctx, nmrr);
+
+	/* Configure page tables as inner-cacheable and shareable to reduce
+	 * the TLB miss penalty.
+	 */
+	if (redirect) {
+		SET_CB_TTBR0_S(base, ctx, 1);
+		SET_CB_TTBR0_NOS(base, ctx, 1);
+		SET_CB_TTBR0_IRGN1(base, ctx, 0); /* WB, WA */
+		SET_CB_TTBR0_IRGN0(base, ctx, 1);
+		SET_CB_TTBR0_RGN(base, ctx, 1);   /* WB, WA */
+	}
+
+       /* Find if this page table is used elsewhere, and re-use ASID */
+	found = 0;
+	for (i = 0; i < ncb; i++)
+		if ((GET_CB_TTBR0_ADDR(base, i) == pn) && (i != ctx)) {
+			SET_CB_CONTEXTIDR_ASID(base, ctx, \
+					GET_CB_CONTEXTIDR_ASID(base, i));
+			found = 1;
+			break;
+		}
+
+	/* If page table is new, find an unused ASID */
+	if (!found) {
+		for (i = 0; i < ncb; i++) {
+			found = 0;
+			for (j = 0; j < ncb; j++) {
+				if (GET_CB_CONTEXTIDR_ASID(base, j) == i &&
+				    j != ctx)
+					found = 1;
+			}
+
+			if (!found) {
+				SET_CB_CONTEXTIDR_ASID(base, ctx, i);
+				break;
+			}
+		}
+		BUG_ON(found);
+	}
+
+	/* Enable the MMU */
+	SET_CB_SCTLR_M(base, ctx, 1);
+	mb();
+}
+
+static int msm_iommu_domain_init(struct iommu_domain *domain, int flags)
+{
+	struct msm_priv *priv;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		goto fail_nomem;
+
+#ifdef CONFIG_IOMMU_PGTABLES_L2
+	priv->pt.redirect = flags & MSM_IOMMU_DOMAIN_PT_CACHEABLE;
+#endif
+
+	INIT_LIST_HEAD(&priv->list_attached);
+	if (msm_iommu_pagetable_alloc(&priv->pt))
+		goto fail_nomem;
+
+	domain->priv = priv;
+	return 0;
+
+fail_nomem:
+	kfree(priv);
+	return -ENOMEM;
+}
+
+static void msm_iommu_domain_destroy(struct iommu_domain *domain)
+{
+	struct msm_priv *priv;
+
+	mutex_lock(&msm_iommu_lock);
+	priv = domain->priv;
+	domain->priv = NULL;
+
+	if (priv)
+		msm_iommu_pagetable_free(&priv->pt);
+
+	kfree(priv);
+	mutex_unlock(&msm_iommu_lock);
+}
+
+static int msm_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
+{
+	struct msm_priv *priv;
+	struct msm_iommu_drvdata *iommu_drvdata;
+	struct msm_iommu_ctx_drvdata *ctx_drvdata;
+	struct msm_iommu_ctx_drvdata *tmp_drvdata;
+	int ret = 0;
+
+	mutex_lock(&msm_iommu_lock);
+
+	priv = domain->priv;
+	if (!priv || !dev) {
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	iommu_drvdata = dev_get_drvdata(dev->parent);
+	ctx_drvdata = dev_get_drvdata(dev);
+	if (!iommu_drvdata || !ctx_drvdata) {
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	if (!list_empty(&ctx_drvdata->attached_elm)) {
+		ret = -EBUSY;
+		goto fail;
+	}
+
+	list_for_each_entry(tmp_drvdata, &priv->list_attached, attached_elm)
+		if (tmp_drvdata == ctx_drvdata) {
+			ret = -EBUSY;
+			goto fail;
+		}
+
+	ret = __enable_clocks(iommu_drvdata);
+	if (ret)
+		goto fail;
+
+	__program_context(iommu_drvdata->base, ctx_drvdata->num,
+		iommu_drvdata->ncb, __pa(priv->pt.fl_table),
+		priv->pt.redirect);
+
+	__disable_clocks(iommu_drvdata);
+	list_add(&(ctx_drvdata->attached_elm), &priv->list_attached);
+	ctx_drvdata->attached_domain = domain;
+
+fail:
+	mutex_unlock(&msm_iommu_lock);
+	return ret;
+}
+
+static void msm_iommu_detach_dev(struct iommu_domain *domain,
+				 struct device *dev)
+{
+	struct msm_priv *priv;
+	struct msm_iommu_drvdata *iommu_drvdata;
+	struct msm_iommu_ctx_drvdata *ctx_drvdata;
+	int ret;
+
+	mutex_lock(&msm_iommu_lock);
+	priv = domain->priv;
+	if (!priv || !dev)
+		goto fail;
+
+	iommu_drvdata = dev_get_drvdata(dev->parent);
+	ctx_drvdata = dev_get_drvdata(dev);
+	if (!iommu_drvdata || !ctx_drvdata || !ctx_drvdata->attached_domain)
+		goto fail;
+
+	ret = __enable_clocks(iommu_drvdata);
+	if (ret)
+		goto fail;
+
+	SET_TLBIASID(iommu_drvdata->base, ctx_drvdata->num,
+		GET_CB_CONTEXTIDR_ASID(iommu_drvdata->base, ctx_drvdata->num));
+
+	__reset_context(iommu_drvdata->base, ctx_drvdata->num);
+	__disable_clocks(iommu_drvdata);
+	list_del_init(&ctx_drvdata->attached_elm);
+	ctx_drvdata->attached_domain = NULL;
+
+fail:
+	mutex_unlock(&msm_iommu_lock);
+}
+
+static int msm_iommu_map(struct iommu_domain *domain, unsigned long va,
+			 phys_addr_t pa, size_t len, int prot)
+{
+	struct msm_priv *priv;
+	int ret = 0;
+
+	mutex_lock(&msm_iommu_lock);
+
+	priv = domain->priv;
+	if (!priv) {
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	ret = msm_iommu_pagetable_map(&priv->pt, va, pa, len, prot);
+	if (ret)
+		goto fail;
+
+	ret = __flush_iotlb_va(domain, va);
+fail:
+	mutex_unlock(&msm_iommu_lock);
+	return ret;
+}
+
+static size_t msm_iommu_unmap(struct iommu_domain *domain, unsigned long va,
+			    size_t len)
+{
+	struct msm_priv *priv;
+	int ret = -ENODEV;
+
+	mutex_lock(&msm_iommu_lock);
+
+	priv = domain->priv;
+	if (!priv)
+		goto fail;
+
+	ret = msm_iommu_pagetable_unmap(&priv->pt, va, len);
+	if (ret < 0)
+		goto fail;
+
+	ret = __flush_iotlb_va(domain, va);
+fail:
+	mutex_unlock(&msm_iommu_lock);
+
+	/* the IOMMU API requires us to return how many bytes were unmapped */
+	len = ret ? 0 : len;
+	return len;
+}
+
+static int msm_iommu_map_range(struct iommu_domain *domain, unsigned int va,
+			       struct scatterlist *sg, unsigned int len,
+			       int prot)
+{
+	int ret;
+	struct msm_priv *priv;
+
+	mutex_lock(&msm_iommu_lock);
+
+	priv = domain->priv;
+	if (!priv) {
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	ret = msm_iommu_pagetable_map_range(&priv->pt, va, sg, len, prot);
+	if (ret)
+		goto fail;
+
+	__flush_iotlb(domain);
+fail:
+	mutex_unlock(&msm_iommu_lock);
+	return ret;
+}
+
+
+static int msm_iommu_unmap_range(struct iommu_domain *domain, unsigned int va,
+				 unsigned int len)
+{
+	struct msm_priv *priv;
+
+	mutex_lock(&msm_iommu_lock);
+
+	priv = domain->priv;
+	msm_iommu_pagetable_unmap_range(&priv->pt, va, len);
+
+	__flush_iotlb(domain);
+	mutex_unlock(&msm_iommu_lock);
+	return 0;
+}
+
+static phys_addr_t msm_iommu_iova_to_phys(struct iommu_domain *domain,
+					  unsigned long va)
+{
+	struct msm_priv *priv;
+	struct msm_iommu_drvdata *iommu_drvdata;
+	struct msm_iommu_ctx_drvdata *ctx_drvdata;
+	unsigned int par;
+	void __iomem *base;
+	phys_addr_t pa = 0;
+	int ctx;
+
+	mutex_lock(&msm_iommu_lock);
+
+	priv = domain->priv;
+	if (list_empty(&priv->list_attached))
+		goto fail;
+
+	ctx_drvdata = list_entry(priv->list_attached.next,
+				 struct msm_iommu_ctx_drvdata, attached_elm);
+	iommu_drvdata = dev_get_drvdata(ctx_drvdata->pdev->dev.parent);
+
+	base = iommu_drvdata->base;
+	ctx = ctx_drvdata->num;
+
+	SET_ATS1PR(base, ctx, va & CB_ATS1PR_ADDR);
+	mb();
+	while (GET_CB_ATSR_ACTIVE(base, ctx))
+		cpu_relax();
+
+	par = GET_PAR(base, ctx);
+	if (par & CB_PAR_F) {
+		pa = 0;
+	} else {
+		/* We are dealing with a supersection */
+		if (par & CB_PAR_SS)
+			pa = (par & 0xFF000000) | (va & 0x00FFFFFF);
+		else /* Upper 20 bits from PAR, lower 12 from VA */
+			pa = (par & 0xFFFFF000) | (va & 0x00000FFF);
+	}
+
+fail:
+	mutex_unlock(&msm_iommu_lock);
+	return pa;
+}
+
+static int msm_iommu_domain_has_cap(struct iommu_domain *domain,
+				    unsigned long cap)
+{
+	return 0;
+}
+
+static void print_ctx_regs(void __iomem *base, int ctx, unsigned int fsr)
+{
+	pr_err("FAR    = %08x    PAR    = %08x\n",
+		 GET_FAR(base, ctx), GET_PAR(base, ctx));
+	pr_err("FSR    = %08x [%s%s%s%s%s%s%s%s%s]\n", fsr,
+			(fsr & 0x02) ? "TF " : "",
+			(fsr & 0x04) ? "AFF " : "",
+			(fsr & 0x08) ? "PF " : "",
+			(fsr & 0x10) ? "EF " : "",
+			(fsr & 0x20) ? "TLBMCF " : "",
+			(fsr & 0x40) ? "TLBLKF " : "",
+			(fsr & 0x80) ? "MHF " : "",
+			(fsr & 0x40000000) ? "SS " : "",
+			(fsr & 0x80000000) ? "MULTI " : "");
+
+	pr_err("FSYNR0 = %08x    FSYNR1 = %08x\n",
+		 GET_FSYNR0(base, ctx), GET_FSYNR1(base, ctx));
+	pr_err("TTBR0  = %08x    TTBR1  = %08x\n",
+		 GET_TTBR0(base, ctx), GET_TTBR1(base, ctx));
+	pr_err("SCTLR  = %08x    ACTLR  = %08x\n",
+		 GET_SCTLR(base, ctx), GET_ACTLR(base, ctx));
+	pr_err("PRRR   = %08x    NMRR   = %08x\n",
+		 GET_PRRR(base, ctx), GET_NMRR(base, ctx));
+}
+
+irqreturn_t msm_iommu_fault_handler_v2(int irq, void *dev_id)
+{
+	struct platform_device *pdev = dev_id;
+	struct msm_iommu_drvdata *drvdata;
+	struct msm_iommu_ctx_drvdata *ctx_drvdata;
+	unsigned int fsr;
+	int ret = IRQ_NONE;
+
+	mutex_lock(&msm_iommu_lock);
+
+	BUG_ON(!pdev);
+
+	drvdata = dev_get_drvdata(pdev->dev.parent);
+	BUG_ON(!drvdata);
+
+	ctx_drvdata = dev_get_drvdata(&pdev->dev);
+	BUG_ON(!ctx_drvdata);
+
+	fsr = GET_FSR(drvdata->base, ctx_drvdata->num);
+	if (fsr) {
+		if (!ctx_drvdata->attached_domain) {
+			pr_err("Bad domain in interrupt handler\n");
+			ret = -ENOSYS;
+		} else
+			ret = report_iommu_fault(ctx_drvdata->attached_domain,
+				&ctx_drvdata->pdev->dev,
+				GET_FAR(drvdata->base, ctx_drvdata->num), 0);
+
+		if (ret == -ENOSYS) {
+			pr_err("Unexpected IOMMU page fault!\n");
+			pr_err("name = %s\n", drvdata->name);
+			pr_err("context = %s (%d)\n", ctx_drvdata->name,
+							ctx_drvdata->num);
+			pr_err("Interesting registers:\n");
+			print_ctx_regs(drvdata->base, ctx_drvdata->num, fsr);
+		}
+
+		SET_FSR(drvdata->base, ctx_drvdata->num, fsr);
+		ret = IRQ_HANDLED;
+	} else
+		ret = IRQ_NONE;
+
+	mutex_unlock(&msm_iommu_lock);
+	return ret;
+}
+
+static phys_addr_t msm_iommu_get_pt_base_addr(struct iommu_domain *domain)
+{
+	struct msm_priv *priv = domain->priv;
+	return __pa(priv->pt.fl_table);
+}
+
+static struct iommu_ops msm_iommu_ops = {
+	.domain_init = msm_iommu_domain_init,
+	.domain_destroy = msm_iommu_domain_destroy,
+	.attach_dev = msm_iommu_attach_dev,
+	.detach_dev = msm_iommu_detach_dev,
+	.map = msm_iommu_map,
+	.unmap = msm_iommu_unmap,
+	.map_range = msm_iommu_map_range,
+	.unmap_range = msm_iommu_unmap_range,
+	.iova_to_phys = msm_iommu_iova_to_phys,
+	.domain_has_cap = msm_iommu_domain_has_cap,
+	.get_pt_base_addr = msm_iommu_get_pt_base_addr,
+	.pgsize_bitmap = MSM_IOMMU_PGSIZES,
+};
+
+static int __init msm_iommu_init(void)
+{
+	msm_iommu_pagetable_init();
+	bus_set_iommu(&platform_bus_type, &msm_iommu_ops);
+	return 0;
+}
+
+subsys_initcall(msm_iommu_init);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MSM SMMU v2 Driver");
diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c
index cee307e..e17e1f8 100644
--- a/drivers/iommu/msm_iommu.c
+++ b/drivers/iommu/msm_iommu.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -8,11 +8,6 @@
  * 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 Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
  */
 
 #define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
@@ -27,6 +22,7 @@
 #include <linux/slab.h>
 #include <linux/iommu.h>
 #include <linux/clk.h>
+#include <linux/scatterlist.h>
 
 #include <asm/cacheflush.h>
 #include <asm/sizes.h>
@@ -42,15 +38,34 @@
 #define RCP15_PRRR(reg)		MRC(reg, p15, 0, c10, c2, 0)
 #define RCP15_NMRR(reg)		MRC(reg, p15, 0, c10, c2, 1)
 
+/* Sharability attributes of MSM IOMMU mappings */
+#define MSM_IOMMU_ATTR_NON_SH		0x0
+#define MSM_IOMMU_ATTR_SH		0x4
+
+/* Cacheability attributes of MSM IOMMU mappings */
+#define MSM_IOMMU_ATTR_NONCACHED	0x0
+#define MSM_IOMMU_ATTR_CACHED_WB_WA	0x1
+#define MSM_IOMMU_ATTR_CACHED_WB_NWA	0x2
+#define MSM_IOMMU_ATTR_CACHED_WT	0x3
+
+
+static inline void clean_pte(unsigned long *start, unsigned long *end,
+			     int redirect)
+{
+	if (!redirect)
+		dmac_flush_range(start, end);
+}
+
 /* bitmap of the page sizes currently supported */
 #define MSM_IOMMU_PGSIZES	(SZ_4K | SZ_64K | SZ_1M | SZ_16M)
 
 static int msm_iommu_tex_class[4];
 
-DEFINE_SPINLOCK(msm_iommu_lock);
+DEFINE_MUTEX(msm_iommu_lock);
 
 struct msm_priv {
 	unsigned long *pgtable;
+	int redirect;
 	struct list_head list_attached;
 };
 
@@ -58,14 +73,14 @@
 {
 	int ret;
 
-	ret = clk_enable(drvdata->pclk);
+	ret = clk_prepare_enable(drvdata->pclk);
 	if (ret)
 		goto fail;
 
 	if (drvdata->clk) {
-		ret = clk_enable(drvdata->clk);
+		ret = clk_prepare_enable(drvdata->clk);
 		if (ret)
-			clk_disable(drvdata->pclk);
+			clk_disable_unprepare(drvdata->pclk);
 	}
 fail:
 	return ret;
@@ -74,8 +89,40 @@
 static void __disable_clocks(struct msm_iommu_drvdata *drvdata)
 {
 	if (drvdata->clk)
-		clk_disable(drvdata->clk);
-	clk_disable(drvdata->pclk);
+		clk_disable_unprepare(drvdata->clk);
+	clk_disable_unprepare(drvdata->pclk);
+}
+
+static int __flush_iotlb_va(struct iommu_domain *domain, unsigned int va)
+{
+	struct msm_priv *priv = domain->priv;
+	struct msm_iommu_drvdata *iommu_drvdata;
+	struct msm_iommu_ctx_drvdata *ctx_drvdata;
+	int ret = 0;
+	int asid;
+
+	list_for_each_entry(ctx_drvdata, &priv->list_attached, attached_elm) {
+		if (!ctx_drvdata->pdev || !ctx_drvdata->pdev->dev.parent)
+			BUG();
+
+		iommu_drvdata = dev_get_drvdata(ctx_drvdata->pdev->dev.parent);
+		if (!iommu_drvdata)
+			BUG();
+
+		ret = __enable_clocks(iommu_drvdata);
+		if (ret)
+			goto fail;
+
+		asid = GET_CONTEXTIDR_ASID(iommu_drvdata->base,
+					   ctx_drvdata->num);
+
+		SET_TLBIVA(iommu_drvdata->base, ctx_drvdata->num,
+			   asid | (va & TLBIVA_VA));
+		mb();
+		__disable_clocks(iommu_drvdata);
+	}
+fail:
+	return ret;
 }
 
 static int __flush_iotlb(struct iommu_domain *domain)
@@ -84,34 +131,25 @@
 	struct msm_iommu_drvdata *iommu_drvdata;
 	struct msm_iommu_ctx_drvdata *ctx_drvdata;
 	int ret = 0;
-#ifndef CONFIG_IOMMU_PGTABLES_L2
-	unsigned long *fl_table = priv->pgtable;
-	int i;
-
-	if (!list_empty(&priv->list_attached)) {
-		dmac_flush_range(fl_table, fl_table + SZ_16K);
-
-		for (i = 0; i < NUM_FL_PTE; i++)
-			if ((fl_table[i] & 0x03) == FL_TYPE_TABLE) {
-				void *sl_table = __va(fl_table[i] &
-								FL_BASE_MASK);
-				dmac_flush_range(sl_table, sl_table + SZ_4K);
-			}
-	}
-#endif
+	int asid;
 
 	list_for_each_entry(ctx_drvdata, &priv->list_attached, attached_elm) {
 		if (!ctx_drvdata->pdev || !ctx_drvdata->pdev->dev.parent)
 			BUG();
 
 		iommu_drvdata = dev_get_drvdata(ctx_drvdata->pdev->dev.parent);
-		BUG_ON(!iommu_drvdata);
+		if (!iommu_drvdata)
+			BUG();
 
 		ret = __enable_clocks(iommu_drvdata);
 		if (ret)
 			goto fail;
 
-		SET_CTX_TLBIALL(iommu_drvdata->base, ctx_drvdata->num, 0);
+		asid = GET_CONTEXTIDR_ASID(iommu_drvdata->base,
+					   ctx_drvdata->num);
+
+		SET_TLBIASID(iommu_drvdata->base, ctx_drvdata->num, asid);
+		mb();
 		__disable_clocks(iommu_drvdata);
 	}
 fail:
@@ -134,17 +172,20 @@
 	SET_BFBCR(base, ctx, 0);
 	SET_PAR(base, ctx, 0);
 	SET_FAR(base, ctx, 0);
-	SET_CTX_TLBIALL(base, ctx, 0);
 	SET_TLBFLPTER(base, ctx, 0);
 	SET_TLBSLPTER(base, ctx, 0);
 	SET_TLBLKCR(base, ctx, 0);
 	SET_PRRR(base, ctx, 0);
 	SET_NMRR(base, ctx, 0);
+	mb();
 }
 
-static void __program_context(void __iomem *base, int ctx, phys_addr_t pgtable)
+static void __program_context(void __iomem *base, int ctx, int ncb,
+			      phys_addr_t pgtable, int redirect,
+			      int ttbr_split)
 {
 	unsigned int prrr, nmrr;
+	int i, j, found;
 	__reset_context(base, ctx);
 
 	/* Set up HTW mode */
@@ -154,14 +195,10 @@
 	/* V2P configuration: HTW for access */
 	SET_V2PCFG(base, ctx, 0x3);
 
-	SET_TTBCR(base, ctx, 0);
-	SET_TTBR0_PA(base, ctx, (pgtable >> 14));
-
-	/* Invalidate the TLB for this context */
-	SET_CTX_TLBIALL(base, ctx, 0);
-
-	/* Set interrupt number to "secure" interrupt */
-	SET_IRPTNDX(base, ctx, 0);
+	SET_TTBCR(base, ctx, ttbr_split);
+	SET_TTBR0_PA(base, ctx, (pgtable >> TTBR0_PA_SHIFT));
+	if (ttbr_split)
+		SET_TTBR1_PA(base, ctx, (pgtable >> TTBR1_PA_SHIFT));
 
 	/* Enable context fault interrupt */
 	SET_CFEIE(base, ctx, 1);
@@ -186,31 +223,61 @@
 	/* Turn on BFB prefetch */
 	SET_BFBDFE(base, ctx, 1);
 
-#ifdef CONFIG_IOMMU_PGTABLES_L2
 	/* Configure page tables as inner-cacheable and shareable to reduce
 	 * the TLB miss penalty.
 	 */
-	SET_TTBR0_SH(base, ctx, 1);
-	SET_TTBR1_SH(base, ctx, 1);
+	if (redirect) {
+		SET_TTBR0_SH(base, ctx, 1);
+		SET_TTBR1_SH(base, ctx, 1);
 
-	SET_TTBR0_NOS(base, ctx, 1);
-	SET_TTBR1_NOS(base, ctx, 1);
+		SET_TTBR0_NOS(base, ctx, 1);
+		SET_TTBR1_NOS(base, ctx, 1);
 
-	SET_TTBR0_IRGNH(base, ctx, 0); /* WB, WA */
-	SET_TTBR0_IRGNL(base, ctx, 1);
+		SET_TTBR0_IRGNH(base, ctx, 0); /* WB, WA */
+		SET_TTBR0_IRGNL(base, ctx, 1);
 
-	SET_TTBR1_IRGNH(base, ctx, 0); /* WB, WA */
-	SET_TTBR1_IRGNL(base, ctx, 1);
+		SET_TTBR1_IRGNH(base, ctx, 0); /* WB, WA */
+		SET_TTBR1_IRGNL(base, ctx, 1);
 
-	SET_TTBR0_ORGN(base, ctx, 1); /* WB, WA */
-	SET_TTBR1_ORGN(base, ctx, 1); /* WB, WA */
-#endif
+		SET_TTBR0_ORGN(base, ctx, 1); /* WB, WA */
+		SET_TTBR1_ORGN(base, ctx, 1); /* WB, WA */
+	}
+
+	/* Find if this page table is used elsewhere, and re-use ASID */
+	found = 0;
+	for (i = 0; i < ncb; i++)
+		if (GET_TTBR0_PA(base, i) == (pgtable >> TTBR0_PA_SHIFT) &&
+		    i != ctx) {
+			SET_CONTEXTIDR_ASID(base, ctx, \
+					    GET_CONTEXTIDR_ASID(base, i));
+			found = 1;
+			break;
+		}
+
+	/* If page table is new, find an unused ASID */
+	if (!found) {
+		for (i = 0; i < ncb; i++) {
+			found = 0;
+			for (j = 0; j < ncb; j++) {
+				if (GET_CONTEXTIDR_ASID(base, j) == i &&
+				    j != ctx)
+					found = 1;
+			}
+
+			if (!found) {
+				SET_CONTEXTIDR_ASID(base, ctx, i);
+				break;
+			}
+		}
+		BUG_ON(found);
+	}
 
 	/* Enable the MMU */
 	SET_M(base, ctx, 1);
+	mb();
 }
 
-static int msm_iommu_domain_init(struct iommu_domain *domain)
+static int msm_iommu_domain_init(struct iommu_domain *domain, int flags)
 {
 	struct msm_priv *priv = kzalloc(sizeof(*priv), GFP_KERNEL);
 
@@ -224,8 +291,15 @@
 	if (!priv->pgtable)
 		goto fail_nomem;
 
+#ifdef CONFIG_IOMMU_PGTABLES_L2
+	priv->redirect = flags & MSM_IOMMU_DOMAIN_PT_CACHEABLE;
+#endif
+
 	memset(priv->pgtable, 0, SZ_16K);
 	domain->priv = priv;
+
+	clean_pte(priv->pgtable, priv->pgtable + NUM_FL_PTE, priv->redirect);
+
 	return 0;
 
 fail_nomem:
@@ -236,11 +310,10 @@
 static void msm_iommu_domain_destroy(struct iommu_domain *domain)
 {
 	struct msm_priv *priv;
-	unsigned long flags;
 	unsigned long *fl_table;
 	int i;
 
-	spin_lock_irqsave(&msm_iommu_lock, flags);
+	mutex_lock(&msm_iommu_lock);
 	priv = domain->priv;
 	domain->priv = NULL;
 
@@ -257,7 +330,7 @@
 	}
 
 	kfree(priv);
-	spin_unlock_irqrestore(&msm_iommu_lock, flags);
+	mutex_unlock(&msm_iommu_lock);
 }
 
 static int msm_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
@@ -268,9 +341,8 @@
 	struct msm_iommu_ctx_drvdata *ctx_drvdata;
 	struct msm_iommu_ctx_drvdata *tmp_drvdata;
 	int ret = 0;
-	unsigned long flags;
 
-	spin_lock_irqsave(&msm_iommu_lock, flags);
+	mutex_lock(&msm_iommu_lock);
 
 	priv = domain->priv;
 
@@ -303,15 +375,16 @@
 	if (ret)
 		goto fail;
 
-	__program_context(iommu_drvdata->base, ctx_dev->num,
-			  __pa(priv->pgtable));
+	__program_context(iommu_drvdata->base, ctx_dev->num, iommu_drvdata->ncb,
+			  __pa(priv->pgtable), priv->redirect,
+			  iommu_drvdata->ttbr_split);
 
 	__disable_clocks(iommu_drvdata);
 	list_add(&(ctx_drvdata->attached_elm), &priv->list_attached);
-	ret = __flush_iotlb(domain);
 
+	ctx_drvdata->attached_domain = domain;
 fail:
-	spin_unlock_irqrestore(&msm_iommu_lock, flags);
+	mutex_unlock(&msm_iommu_lock);
 	return ret;
 }
 
@@ -322,10 +395,9 @@
 	struct msm_iommu_ctx_dev *ctx_dev;
 	struct msm_iommu_drvdata *iommu_drvdata;
 	struct msm_iommu_ctx_drvdata *ctx_drvdata;
-	unsigned long flags;
 	int ret;
 
-	spin_lock_irqsave(&msm_iommu_lock, flags);
+	mutex_lock(&msm_iommu_lock);
 	priv = domain->priv;
 
 	if (!priv || !dev)
@@ -338,27 +410,67 @@
 	if (!iommu_drvdata || !ctx_drvdata || !ctx_dev)
 		goto fail;
 
-	ret = __flush_iotlb(domain);
-	if (ret)
-		goto fail;
-
 	ret = __enable_clocks(iommu_drvdata);
 	if (ret)
 		goto fail;
 
+	SET_TLBIASID(iommu_drvdata->base, ctx_dev->num,
+		     GET_CONTEXTIDR_ASID(iommu_drvdata->base, ctx_dev->num));
+
 	__reset_context(iommu_drvdata->base, ctx_dev->num);
 	__disable_clocks(iommu_drvdata);
 	list_del_init(&ctx_drvdata->attached_elm);
-
+	ctx_drvdata->attached_domain = NULL;
 fail:
-	spin_unlock_irqrestore(&msm_iommu_lock, flags);
+	mutex_unlock(&msm_iommu_lock);
+}
+
+static int __get_pgprot(int prot, int len)
+{
+	unsigned int pgprot;
+	int tex;
+
+	if (!(prot & (IOMMU_READ | IOMMU_WRITE))) {
+		prot |= IOMMU_READ | IOMMU_WRITE;
+		WARN_ONCE(1, "No attributes in iommu mapping; assuming RW\n");
+	}
+
+	if ((prot & IOMMU_WRITE) && !(prot & IOMMU_READ)) {
+		prot |= IOMMU_READ;
+		WARN_ONCE(1, "Write-only iommu mappings unsupported; falling back to RW\n");
+	}
+
+	if (prot & IOMMU_CACHE)
+		tex = (pgprot_kernel >> 2) & 0x07;
+	else
+		tex = msm_iommu_tex_class[MSM_IOMMU_ATTR_NONCACHED];
+
+	if (tex < 0 || tex > NUM_TEX_CLASS - 1)
+		return 0;
+
+	if (len == SZ_16M || len == SZ_1M) {
+		pgprot = FL_SHARED;
+		pgprot |= tex & 0x01 ? FL_BUFFERABLE : 0;
+		pgprot |= tex & 0x02 ? FL_CACHEABLE : 0;
+		pgprot |= tex & 0x04 ? FL_TEX0 : 0;
+		pgprot |= FL_AP0 | FL_AP1;
+		pgprot |= prot & IOMMU_WRITE ? 0 : FL_AP2;
+	} else	{
+		pgprot = SL_SHARED;
+		pgprot |= tex & 0x01 ? SL_BUFFERABLE : 0;
+		pgprot |= tex & 0x02 ? SL_CACHEABLE : 0;
+		pgprot |= tex & 0x04 ? SL_TEX0 : 0;
+		pgprot |= SL_AP0 | SL_AP1;
+		pgprot |= prot & IOMMU_WRITE ? 0 : SL_AP2;
+	}
+
+	return pgprot;
 }
 
 static int msm_iommu_map(struct iommu_domain *domain, unsigned long va,
 			 phys_addr_t pa, size_t len, int prot)
 {
 	struct msm_priv *priv;
-	unsigned long flags;
 	unsigned long *fl_table;
 	unsigned long *fl_pte;
 	unsigned long fl_offset;
@@ -366,17 +478,9 @@
 	unsigned long *sl_pte;
 	unsigned long sl_offset;
 	unsigned int pgprot;
-	int ret = 0, tex, sh;
+	int ret = 0;
 
-	spin_lock_irqsave(&msm_iommu_lock, flags);
-
-	sh = (prot & MSM_IOMMU_ATTR_SH) ? 1 : 0;
-	tex = msm_iommu_tex_class[prot & MSM_IOMMU_CP_MASK];
-
-	if (tex < 0 || tex > NUM_TEX_CLASS - 1) {
-		ret = -EINVAL;
-		goto fail;
-	}
+	mutex_lock(&msm_iommu_lock);
 
 	priv = domain->priv;
 	if (!priv) {
@@ -399,16 +503,11 @@
 		goto fail;
 	}
 
-	if (len == SZ_16M || len == SZ_1M) {
-		pgprot = sh ? FL_SHARED : 0;
-		pgprot |= tex & 0x01 ? FL_BUFFERABLE : 0;
-		pgprot |= tex & 0x02 ? FL_CACHEABLE : 0;
-		pgprot |= tex & 0x04 ? FL_TEX0 : 0;
-	} else	{
-		pgprot = sh ? SL_SHARED : 0;
-		pgprot |= tex & 0x01 ? SL_BUFFERABLE : 0;
-		pgprot |= tex & 0x02 ? SL_CACHEABLE : 0;
-		pgprot |= tex & 0x04 ? SL_TEX0 : 0;
+	pgprot = __get_pgprot(prot, len);
+
+	if (!pgprot) {
+		ret = -EINVAL;
+		goto fail;
 	}
 
 	fl_offset = FL_OFFSET(va);	/* Upper 12 bits */
@@ -416,52 +515,92 @@
 
 	if (len == SZ_16M) {
 		int i = 0;
+
 		for (i = 0; i < 16; i++)
-			*(fl_pte+i) = (pa & 0xFF000000) | FL_SUPERSECTION |
-				  FL_AP_READ | FL_AP_WRITE | FL_TYPE_SECT |
-				  FL_SHARED | FL_NG | pgprot;
+			if (*(fl_pte+i)) {
+				ret = -EBUSY;
+				goto fail;
+			}
+
+		for (i = 0; i < 16; i++)
+			*(fl_pte+i) = (pa & 0xFF000000) | FL_SUPERSECTION
+				  | FL_TYPE_SECT | FL_SHARED | FL_NG | pgprot;
+		clean_pte(fl_pte, fl_pte + 16, priv->redirect);
 	}
 
-	if (len == SZ_1M)
-		*fl_pte = (pa & 0xFFF00000) | FL_AP_READ | FL_AP_WRITE | FL_NG |
-					    FL_TYPE_SECT | FL_SHARED | pgprot;
-
-	/* Need a 2nd level table */
-	if ((len == SZ_4K || len == SZ_64K) && (*fl_pte) == 0) {
-		unsigned long *sl;
-		sl = (unsigned long *) __get_free_pages(GFP_ATOMIC,
-							get_order(SZ_4K));
-
-		if (!sl) {
-			pr_debug("Could not allocate second level table\n");
-			ret = -ENOMEM;
+	if (len == SZ_1M) {
+		if (*fl_pte) {
+			ret = -EBUSY;
 			goto fail;
 		}
 
-		memset(sl, 0, SZ_4K);
-		*fl_pte = ((((int)__pa(sl)) & FL_BASE_MASK) | FL_TYPE_TABLE);
+		*fl_pte = (pa & 0xFFF00000) | FL_NG | FL_TYPE_SECT | FL_SHARED
+					    | pgprot;
+		clean_pte(fl_pte, fl_pte + 1, priv->redirect);
+	}
+
+	/* Need a 2nd level table */
+	if (len == SZ_4K || len == SZ_64K) {
+
+		if (*fl_pte == 0) {
+			unsigned long *sl;
+			sl = (unsigned long *) __get_free_pages(GFP_KERNEL,
+							get_order(SZ_4K));
+
+			if (!sl) {
+				pr_debug("Could not allocate second level table\n");
+				ret = -ENOMEM;
+				goto fail;
+			}
+			memset(sl, 0, SZ_4K);
+			clean_pte(sl, sl + NUM_SL_PTE, priv->redirect);
+
+			*fl_pte = ((((int)__pa(sl)) & FL_BASE_MASK) | \
+						      FL_TYPE_TABLE);
+
+			clean_pte(fl_pte, fl_pte + 1, priv->redirect);
+		}
+
+		if (!(*fl_pte & FL_TYPE_TABLE)) {
+			ret = -EBUSY;
+			goto fail;
+		}
 	}
 
 	sl_table = (unsigned long *) __va(((*fl_pte) & FL_BASE_MASK));
 	sl_offset = SL_OFFSET(va);
 	sl_pte = sl_table + sl_offset;
 
+	if (len == SZ_4K) {
+		if (*sl_pte) {
+			ret = -EBUSY;
+			goto fail;
+		}
 
-	if (len == SZ_4K)
-		*sl_pte = (pa & SL_BASE_MASK_SMALL) | SL_AP0 | SL_AP1 | SL_NG |
-					  SL_SHARED | SL_TYPE_SMALL | pgprot;
+		*sl_pte = (pa & SL_BASE_MASK_SMALL) | SL_NG | SL_SHARED
+						    | SL_TYPE_SMALL | pgprot;
+		clean_pte(sl_pte, sl_pte + 1, priv->redirect);
+	}
 
 	if (len == SZ_64K) {
 		int i;
 
 		for (i = 0; i < 16; i++)
-			*(sl_pte+i) = (pa & SL_BASE_MASK_LARGE) | SL_AP0 |
-			    SL_NG | SL_AP1 | SL_SHARED | SL_TYPE_LARGE | pgprot;
+			if (*(sl_pte+i)) {
+				ret = -EBUSY;
+				goto fail;
+			}
+
+		for (i = 0; i < 16; i++)
+			*(sl_pte+i) = (pa & SL_BASE_MASK_LARGE) | SL_NG
+					  | SL_SHARED | SL_TYPE_LARGE | pgprot;
+
+		clean_pte(sl_pte, sl_pte + 16, priv->redirect);
 	}
 
-	ret = __flush_iotlb(domain);
+	ret = __flush_iotlb_va(domain, va);
 fail:
-	spin_unlock_irqrestore(&msm_iommu_lock, flags);
+	mutex_unlock(&msm_iommu_lock);
 	return ret;
 }
 
@@ -469,7 +608,6 @@
 			    size_t len)
 {
 	struct msm_priv *priv;
-	unsigned long flags;
 	unsigned long *fl_table;
 	unsigned long *fl_pte;
 	unsigned long fl_offset;
@@ -478,7 +616,7 @@
 	unsigned long sl_offset;
 	int i, ret = 0;
 
-	spin_lock_irqsave(&msm_iommu_lock, flags);
+	mutex_lock(&msm_iommu_lock);
 
 	priv = domain->priv;
 
@@ -507,13 +645,19 @@
 	}
 
 	/* Unmap supersection */
-	if (len == SZ_16M)
+	if (len == SZ_16M) {
 		for (i = 0; i < 16; i++)
 			*(fl_pte+i) = 0;
 
-	if (len == SZ_1M)
+		clean_pte(fl_pte, fl_pte + 16, priv->redirect);
+	}
+
+	if (len == SZ_1M) {
 		*fl_pte = 0;
 
+		clean_pte(fl_pte, fl_pte + 1, priv->redirect);
+	}
+
 	sl_table = (unsigned long *) __va(((*fl_pte) & FL_BASE_MASK));
 	sl_offset = SL_OFFSET(va);
 	sl_pte = sl_table + sl_offset;
@@ -521,11 +665,16 @@
 	if (len == SZ_64K) {
 		for (i = 0; i < 16; i++)
 			*(sl_pte+i) = 0;
+
+		clean_pte(sl_pte, sl_pte + 16, priv->redirect);
 	}
 
-	if (len == SZ_4K)
+	if (len == SZ_4K) {
 		*sl_pte = 0;
 
+		clean_pte(sl_pte, sl_pte + 1, priv->redirect);
+	}
+
 	if (len == SZ_4K || len == SZ_64K) {
 		int used = 0;
 
@@ -535,19 +684,211 @@
 		if (!used) {
 			free_page((unsigned long)sl_table);
 			*fl_pte = 0;
+
+			clean_pte(fl_pte, fl_pte + 1, priv->redirect);
 		}
 	}
 
-	ret = __flush_iotlb(domain);
+	ret = __flush_iotlb_va(domain, va);
 
 fail:
-	spin_unlock_irqrestore(&msm_iommu_lock, flags);
+	mutex_unlock(&msm_iommu_lock);
 
 	/* the IOMMU API requires us to return how many bytes were unmapped */
 	len = ret ? 0 : len;
 	return len;
 }
 
+static unsigned int 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.
+	 */
+	unsigned int pa = sg_dma_address(sg);
+	if (pa == 0)
+		pa = sg_phys(sg);
+	return pa;
+}
+
+static int msm_iommu_map_range(struct iommu_domain *domain, unsigned int va,
+			       struct scatterlist *sg, unsigned int len,
+			       int prot)
+{
+	unsigned int pa;
+	unsigned int offset = 0;
+	unsigned int pgprot;
+	unsigned long *fl_table;
+	unsigned long *fl_pte;
+	unsigned long fl_offset;
+	unsigned long *sl_table;
+	unsigned long sl_offset, sl_start;
+	unsigned int chunk_offset = 0;
+	unsigned int chunk_pa;
+	int ret = 0;
+	struct msm_priv *priv;
+
+	mutex_lock(&msm_iommu_lock);
+
+	BUG_ON(len & (SZ_4K - 1));
+
+	priv = domain->priv;
+	fl_table = priv->pgtable;
+
+	pgprot = __get_pgprot(prot, SZ_4K);
+
+	if (!pgprot) {
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	fl_offset = FL_OFFSET(va);	/* Upper 12 bits */
+	fl_pte = fl_table + fl_offset;	/* int pointers, 4 bytes */
+
+	sl_table = (unsigned long *) __va(((*fl_pte) & FL_BASE_MASK));
+	sl_offset = SL_OFFSET(va);
+
+	chunk_pa = get_phys_addr(sg);
+	if (chunk_pa == 0) {
+		pr_debug("No dma address for sg %p\n", sg);
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	while (offset < len) {
+		/* Set up a 2nd level page table if one doesn't exist */
+		if (*fl_pte == 0) {
+			sl_table = (unsigned long *)
+				 __get_free_pages(GFP_KERNEL, get_order(SZ_4K));
+
+			if (!sl_table) {
+				pr_debug("Could not allocate second level table\n");
+				ret = -ENOMEM;
+				goto fail;
+			}
+
+			memset(sl_table, 0, SZ_4K);
+			clean_pte(sl_table, sl_table + NUM_SL_PTE,
+				  priv->redirect);
+
+			*fl_pte = ((((int)__pa(sl_table)) & FL_BASE_MASK) |
+							    FL_TYPE_TABLE);
+			clean_pte(fl_pte, fl_pte + 1, priv->redirect);
+		} else
+			sl_table = (unsigned long *)
+					       __va(((*fl_pte) & FL_BASE_MASK));
+
+		/* Keep track of initial position so we
+		 * don't clean more than we have to
+		 */
+		sl_start = sl_offset;
+
+		/* Build the 2nd level page table */
+		while (offset < len && sl_offset < NUM_SL_PTE) {
+			pa = chunk_pa + chunk_offset;
+			sl_table[sl_offset] = (pa & SL_BASE_MASK_SMALL) |
+				     pgprot | SL_NG | SL_SHARED | SL_TYPE_SMALL;
+			sl_offset++;
+			offset += SZ_4K;
+
+			chunk_offset += SZ_4K;
+
+			if (chunk_offset >= sg->length && offset < len) {
+				chunk_offset = 0;
+				sg = sg_next(sg);
+				chunk_pa = get_phys_addr(sg);
+				if (chunk_pa == 0) {
+					pr_debug("No dma address for sg %p\n",
+						 sg);
+					ret = -EINVAL;
+					goto fail;
+				}
+			}
+		}
+
+		clean_pte(sl_table + sl_start, sl_table + sl_offset,
+			  priv->redirect);
+
+		fl_pte++;
+		sl_offset = 0;
+	}
+	__flush_iotlb(domain);
+fail:
+	mutex_unlock(&msm_iommu_lock);
+	return ret;
+}
+
+
+static int msm_iommu_unmap_range(struct iommu_domain *domain, unsigned int va,
+				 unsigned int len)
+{
+	unsigned int offset = 0;
+	unsigned long *fl_table;
+	unsigned long *fl_pte;
+	unsigned long fl_offset;
+	unsigned long *sl_table;
+	unsigned long sl_start, sl_end;
+	int used, i;
+	struct msm_priv *priv;
+
+	mutex_lock(&msm_iommu_lock);
+
+	BUG_ON(len & (SZ_4K - 1));
+
+	priv = domain->priv;
+	fl_table = priv->pgtable;
+
+	fl_offset = FL_OFFSET(va);	/* Upper 12 bits */
+	fl_pte = fl_table + fl_offset;	/* int pointers, 4 bytes */
+
+	sl_start = SL_OFFSET(va);
+
+	while (offset < len) {
+		sl_table = (unsigned long *) __va(((*fl_pte) & FL_BASE_MASK));
+		sl_end = ((len - offset) / SZ_4K) + sl_start;
+
+		if (sl_end > NUM_SL_PTE)
+			sl_end = NUM_SL_PTE;
+
+		memset(sl_table + sl_start, 0, (sl_end - sl_start) * 4);
+		clean_pte(sl_table + sl_start, sl_table + sl_end,
+			  priv->redirect);
+
+		offset += (sl_end - sl_start) * SZ_4K;
+
+		/* Unmap and free the 2nd level table if all mappings in it
+		 * were removed. This saves memory, but the table will need
+		 * to be re-allocated the next time someone tries to map these
+		 * VAs.
+		 */
+		used = 0;
+
+		/* If we just unmapped the whole table, don't bother
+		 * seeing if there are still used entries left.
+		 */
+		if (sl_end - sl_start != NUM_SL_PTE)
+			for (i = 0; i < NUM_SL_PTE; i++)
+				if (sl_table[i]) {
+					used = 1;
+					break;
+				}
+		if (!used) {
+			free_page((unsigned long)sl_table);
+			*fl_pte = 0;
+
+			clean_pte(fl_pte, fl_pte + 1, priv->redirect);
+		}
+
+		sl_start = 0;
+		fl_pte++;
+	}
+
+	__flush_iotlb(domain);
+	mutex_unlock(&msm_iommu_lock);
+	return 0;
+}
+
 static phys_addr_t msm_iommu_iova_to_phys(struct iommu_domain *domain,
 					  unsigned long va)
 {
@@ -555,12 +896,11 @@
 	struct msm_iommu_drvdata *iommu_drvdata;
 	struct msm_iommu_ctx_drvdata *ctx_drvdata;
 	unsigned int par;
-	unsigned long flags;
 	void __iomem *base;
 	phys_addr_t ret = 0;
 	int ctx;
 
-	spin_lock_irqsave(&msm_iommu_lock, flags);
+	mutex_lock(&msm_iommu_lock);
 
 	priv = domain->priv;
 	if (list_empty(&priv->list_attached))
@@ -577,10 +917,9 @@
 	if (ret)
 		goto fail;
 
-	/* Invalidate context TLB */
-	SET_CTX_TLBIALL(base, ctx, 0);
 	SET_V2PPR(base, ctx, va & V2Pxx_VA);
 
+	mb();
 	par = GET_PAR(base, ctx);
 
 	/* We are dealing with a supersection */
@@ -594,7 +933,7 @@
 
 	__disable_clocks(iommu_drvdata);
 fail:
-	spin_unlock_irqrestore(&msm_iommu_lock, flags);
+	mutex_unlock(&msm_iommu_lock);
 	return ret;
 }
 
@@ -633,40 +972,61 @@
 
 irqreturn_t msm_iommu_fault_handler(int irq, void *dev_id)
 {
-	struct msm_iommu_drvdata *drvdata = dev_id;
+	struct msm_iommu_ctx_drvdata *ctx_drvdata = dev_id;
+	struct msm_iommu_drvdata *drvdata;
 	void __iomem *base;
-	unsigned int fsr;
-	int i, ret;
+	unsigned int fsr, num;
+	int ret;
 
-	spin_lock(&msm_iommu_lock);
+	mutex_lock(&msm_iommu_lock);
+	BUG_ON(!ctx_drvdata);
 
-	if (!drvdata) {
-		pr_err("Invalid device ID in context interrupt handler\n");
-		goto fail;
-	}
+	drvdata = dev_get_drvdata(ctx_drvdata->pdev->dev.parent);
+	BUG_ON(!drvdata);
 
 	base = drvdata->base;
-
-	pr_err("Unexpected IOMMU page fault!\n");
-	pr_err("base = %08x\n", (unsigned int) base);
+	num = ctx_drvdata->num;
 
 	ret = __enable_clocks(drvdata);
 	if (ret)
 		goto fail;
 
-	for (i = 0; i < drvdata->ncb; i++) {
-		fsr = GET_FSR(base, i);
-		if (fsr) {
-			pr_err("Fault occurred in context %d.\n", i);
+	fsr = GET_FSR(base, num);
+
+	if (fsr) {
+		if (!ctx_drvdata->attached_domain) {
+			pr_err("Bad domain in interrupt handler\n");
+			ret = -ENOSYS;
+		} else
+			ret = report_iommu_fault(ctx_drvdata->attached_domain,
+						&ctx_drvdata->pdev->dev,
+						GET_FAR(base, num), 0);
+
+		if (ret == -ENOSYS) {
+			pr_err("Unexpected IOMMU page fault!\n");
+			pr_err("name    = %s\n", drvdata->name);
+			pr_err("context = %s (%d)\n", ctx_drvdata->name, num);
 			pr_err("Interesting registers:\n");
-			print_ctx_regs(base, i);
-			SET_FSR(base, i, 0x4000000F);
+			print_ctx_regs(base, num);
 		}
-	}
+
+		SET_FSR(base, num, fsr);
+		SET_RESUME(base, num, 1);
+
+		ret = IRQ_HANDLED;
+	} else
+		ret = IRQ_NONE;
+
 	__disable_clocks(drvdata);
 fail:
-	spin_unlock(&msm_iommu_lock);
-	return 0;
+	mutex_unlock(&msm_iommu_lock);
+	return ret;
+}
+
+static phys_addr_t msm_iommu_get_pt_base_addr(struct iommu_domain *domain)
+{
+	struct msm_priv *priv = domain->priv;
+	return __pa(priv->pgtable);
 }
 
 static struct iommu_ops msm_iommu_ops = {
@@ -676,8 +1036,11 @@
 	.detach_dev = msm_iommu_detach_dev,
 	.map = msm_iommu_map,
 	.unmap = msm_iommu_unmap,
+	.map_range = msm_iommu_map_range,
+	.unmap_range = msm_iommu_unmap_range,
 	.iova_to_phys = msm_iommu_iova_to_phys,
 	.domain_has_cap = msm_iommu_domain_has_cap,
+	.get_pt_base_addr = msm_iommu_get_pt_base_addr,
 	.pgsize_bitmap = MSM_IOMMU_PGSIZES,
 };
 
@@ -721,6 +1084,9 @@
 
 static int __init msm_iommu_init(void)
 {
+	if (!msm_soc_version_supports_iommu_v1())
+		return -ENODEV;
+
 	setup_iommu_tex_classes();
 	bus_set_iommu(&platform_bus_type, &msm_iommu_ops);
 	return 0;
diff --git a/drivers/iommu/msm_iommu_dev-v2.c b/drivers/iommu/msm_iommu_dev-v2.c
new file mode 100644
index 0000000..e690ada
--- /dev/null
+++ b/drivers/iommu/msm_iommu_dev-v2.c
@@ -0,0 +1,376 @@
+/* Copyright (c) 2012 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/iommu.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/atomic.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+
+#include <mach/iommu_hw-v2.h>
+#include <mach/iommu.h>
+
+static void msm_iommu_reset(void __iomem *base)
+{
+	int i;
+
+	SET_ACR(base, 0);
+	SET_NSACR(base, 0);
+	SET_CR2(base, 0);
+	SET_NSCR2(base, 0);
+	SET_GFAR(base, 0);
+	SET_GFSRRESTORE(base, 0);
+	SET_TLBIALLNSNH(base, 0);
+	SET_PMCR(base, 0);
+	SET_SCR1(base, 0);
+	SET_SSDR_N(base, 0, 0);
+
+	for (i = 0; i < MAX_NUM_SMR; i++)
+		SET_SMR_VALID(base, i, 0);
+
+	mb();
+}
+
+static int msm_iommu_parse_dt(struct platform_device *pdev,
+				struct msm_iommu_drvdata *drvdata)
+{
+	struct device_node *child;
+	int ret;
+
+	ret = device_move(&pdev->dev, &msm_iommu_root_dev->dev, DPM_ORDER_NONE);
+	if (ret)
+		return ret;
+
+	for_each_child_of_node(pdev->dev.of_node, child) {
+		drvdata->ncb++;
+		if (!of_platform_device_create(child, NULL, &pdev->dev))
+			pr_err("Failed to create %s device\n", child->name);
+	}
+
+	drvdata->name = dev_name(&pdev->dev);
+	return 0;
+}
+
+static atomic_t msm_iommu_next_id = ATOMIC_INIT(-1);
+
+static int __devinit msm_iommu_probe(struct platform_device *pdev)
+{
+	struct msm_iommu_drvdata *drvdata;
+	struct resource *r;
+	int ret;
+
+	if (msm_iommu_root_dev == pdev)
+		return 0;
+
+	if (pdev->id == -1)
+		pdev->id = atomic_inc_return(&msm_iommu_next_id) - 1;
+
+	drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
+	if (!drvdata)
+		return -ENOMEM;
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!r)
+		return -EINVAL;
+
+	drvdata->base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
+	if (!drvdata->base)
+		return -ENOMEM;
+
+	drvdata->pclk = clk_get(&pdev->dev, "iface_clk");
+	if (IS_ERR(drvdata->pclk))
+		return PTR_ERR(drvdata->pclk);
+
+	ret = clk_prepare_enable(drvdata->pclk);
+	if (ret)
+		goto fail_enable;
+
+	drvdata->clk = clk_get(&pdev->dev, "core_clk");
+	if (!IS_ERR(drvdata->clk)) {
+		if (clk_get_rate(drvdata->clk) == 0) {
+			ret = clk_round_rate(drvdata->clk, 1);
+			clk_set_rate(drvdata->clk, ret);
+		}
+
+		ret = clk_prepare_enable(drvdata->clk);
+		if (ret) {
+			clk_put(drvdata->clk);
+			goto fail_pclk;
+		}
+	} else
+		drvdata->clk = NULL;
+
+	msm_iommu_reset(drvdata->base);
+
+	SET_CR0_SMCFCFG(drvdata->base, 1);
+	SET_CR0_USFCFG(drvdata->base, 1);
+	SET_CR0_STALLD(drvdata->base, 1);
+	SET_CR0_GCFGFIE(drvdata->base, 1);
+	SET_CR0_GCFGFRE(drvdata->base, 1);
+	SET_CR0_GFIE(drvdata->base, 1);
+	SET_CR0_GFRE(drvdata->base, 1);
+	SET_CR0_CLIENTPD(drvdata->base, 0);
+
+	ret = msm_iommu_parse_dt(pdev, drvdata);
+	if (ret)
+		goto fail_clk;
+
+	pr_info("device %s mapped at %p, with %d ctx banks\n",
+		drvdata->name, drvdata->base, drvdata->ncb);
+
+	platform_set_drvdata(pdev, drvdata);
+
+	if (drvdata->clk)
+		clk_disable_unprepare(drvdata->clk);
+
+	clk_disable_unprepare(drvdata->pclk);
+
+	return 0;
+
+fail_clk:
+	if (drvdata->clk) {
+		clk_disable_unprepare(drvdata->clk);
+		clk_put(drvdata->clk);
+	}
+fail_pclk:
+	clk_disable_unprepare(drvdata->pclk);
+fail_enable:
+	clk_put(drvdata->pclk);
+	return ret;
+}
+
+static int __devexit msm_iommu_remove(struct platform_device *pdev)
+{
+	struct msm_iommu_drvdata *drv = NULL;
+
+	drv = platform_get_drvdata(pdev);
+	if (drv) {
+		if (drv->clk)
+			clk_put(drv->clk);
+		clk_put(drv->pclk);
+		platform_set_drvdata(pdev, NULL);
+	}
+	return 0;
+}
+
+static int msm_iommu_ctx_parse_dt(struct platform_device *pdev,
+				struct msm_iommu_drvdata *drvdata,
+				struct msm_iommu_ctx_drvdata *ctx_drvdata)
+{
+	struct resource *r, rp;
+	u32 sids[MAX_NUM_SMR];
+	int num = 0;
+	int irq, i, ret, len = 0;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq > 0) {
+		ret = request_threaded_irq(irq, NULL,
+				msm_iommu_fault_handler_v2,
+				IRQF_ONESHOT | IRQF_SHARED,
+				"msm_iommu_nonsecure_irq", pdev);
+		if (ret) {
+			pr_err("Request IRQ %d failed with ret=%d\n", irq, ret);
+			return ret;
+		}
+	}
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!r)
+		return -EINVAL;
+
+	ret = of_address_to_resource(pdev->dev.parent->of_node, 0, &rp);
+	if (ret)
+		return -EINVAL;
+
+	/* Calculate the context bank number using the base addresses. The
+	 * first 8 pages belong to the global address space which is followed
+	 * by the context banks, hence subtract by 8 to get the context bank
+	 * number.
+	 */
+	ctx_drvdata->num = ((r->start - rp.start) >> CTX_SHIFT) - 8;
+
+	if (of_property_read_string(pdev->dev.of_node, "qcom,iommu-ctx-name",
+					&ctx_drvdata->name))
+		ctx_drvdata->name = dev_name(&pdev->dev);
+
+	of_get_property(pdev->dev.of_node, "qcom,iommu-ctx-sids", &len);
+	BUG_ON(len >= sizeof(sids));
+	if (of_property_read_u32_array(pdev->dev.of_node, "qcom,iommu-ctx-sids",
+					sids, len / sizeof(*sids)))
+		return -EINVAL;
+
+	/* Program the M2V tables for this context */
+	for (i = 0; i < len / sizeof(*sids); i++) {
+		for (; num < MAX_NUM_SMR; num++)
+			if (GET_SMR_VALID(drvdata->base, num) == 0)
+				break;
+		BUG_ON(num >= MAX_NUM_SMR);
+
+		SET_SMR_VALID(drvdata->base, num, 1);
+		SET_SMR_MASK(drvdata->base, num, 0);
+		SET_SMR_ID(drvdata->base, num, sids[i]);
+
+		/* Set VMID = 0 */
+		SET_S2CR_N(drvdata->base, num, 0);
+		SET_S2CR_CBNDX(drvdata->base, num, ctx_drvdata->num);
+		/* Set security bit override to be Non-secure */
+		SET_S2CR_NSCFG(drvdata->base, sids[i], 3);
+
+		SET_CBAR_N(drvdata->base, ctx_drvdata->num, 0);
+		/* Stage 1 Context with Stage 2 bypass */
+		SET_CBAR_TYPE(drvdata->base, ctx_drvdata->num, 1);
+		/* Route page faults to the non-secure interrupt */
+		SET_CBAR_IRPTNDX(drvdata->base, ctx_drvdata->num, 1);
+	}
+	mb();
+
+	return 0;
+}
+
+static int __devinit msm_iommu_ctx_probe(struct platform_device *pdev)
+{
+	struct msm_iommu_drvdata *drvdata;
+	struct msm_iommu_ctx_drvdata *ctx_drvdata = NULL;
+	int ret;
+
+	if (!pdev->dev.parent)
+		return -EINVAL;
+
+	drvdata = dev_get_drvdata(pdev->dev.parent);
+	if (!drvdata)
+		return -ENODEV;
+
+	ctx_drvdata = devm_kzalloc(&pdev->dev, sizeof(*ctx_drvdata),
+					GFP_KERNEL);
+	if (!ctx_drvdata)
+		return -ENOMEM;
+
+	ctx_drvdata->pdev = pdev;
+	INIT_LIST_HEAD(&ctx_drvdata->attached_elm);
+	platform_set_drvdata(pdev, ctx_drvdata);
+
+	ret = clk_prepare_enable(drvdata->pclk);
+	if (ret)
+		return ret;
+
+	if (drvdata->clk) {
+		ret = clk_prepare_enable(drvdata->clk);
+		if (ret) {
+			clk_disable_unprepare(drvdata->pclk);
+			return ret;
+		}
+	}
+
+	ret = msm_iommu_ctx_parse_dt(pdev, drvdata, ctx_drvdata);
+	if (!ret)
+		dev_info(&pdev->dev, "context %s using bank %d\n",
+				dev_name(&pdev->dev), ctx_drvdata->num);
+
+	if (drvdata->clk)
+		clk_disable_unprepare(drvdata->clk);
+	clk_disable_unprepare(drvdata->pclk);
+
+	return ret;
+}
+
+static int __devexit msm_iommu_ctx_remove(struct platform_device *pdev)
+{
+	platform_set_drvdata(pdev, NULL);
+	return 0;
+}
+
+static struct of_device_id msm_iommu_match_table[] = {
+	{ .compatible = "qcom,msm-smmu-v2", },
+	{}
+};
+
+static struct platform_driver msm_iommu_driver = {
+	.driver = {
+		.name	= "msm_iommu_v2",
+		.of_match_table = msm_iommu_match_table,
+	},
+	.probe		= msm_iommu_probe,
+	.remove		= __devexit_p(msm_iommu_remove),
+};
+
+static struct of_device_id msm_iommu_ctx_match_table[] = {
+	{ .name = "qcom,iommu-ctx", },
+	{}
+};
+
+static struct platform_driver msm_iommu_ctx_driver = {
+	.driver = {
+		.name	= "msm_iommu_ctx_v2",
+		.of_match_table = msm_iommu_ctx_match_table,
+	},
+	.probe		= msm_iommu_ctx_probe,
+	.remove		= __devexit_p(msm_iommu_ctx_remove),
+};
+
+static int __init msm_iommu_driver_init(void)
+{
+	struct device_node *node;
+	int ret;
+
+	node = of_find_compatible_node(NULL, NULL, "qcom,msm-smmu-v2");
+	if (!node)
+		return -ENODEV;
+
+	of_node_put(node);
+
+	msm_iommu_root_dev = platform_device_register_simple(
+						"msm_iommu", -1, 0, 0);
+	if (!msm_iommu_root_dev) {
+		pr_err("Failed to create root IOMMU device\n");
+		ret = -ENODEV;
+		goto error;
+	}
+
+	atomic_inc(&msm_iommu_next_id);
+
+	ret = platform_driver_register(&msm_iommu_driver);
+	if (ret != 0) {
+		pr_err("Failed to register IOMMU driver\n");
+		goto error;
+	}
+
+	ret = platform_driver_register(&msm_iommu_ctx_driver);
+	if (ret != 0) {
+		pr_err("Failed to register IOMMU context driver\n");
+		goto error;
+	}
+
+error:
+	return ret;
+}
+
+static void __exit msm_iommu_driver_exit(void)
+{
+	platform_driver_unregister(&msm_iommu_ctx_driver);
+	platform_driver_unregister(&msm_iommu_driver);
+	platform_device_unregister(msm_iommu_root_dev);
+}
+
+subsys_initcall(msm_iommu_driver_init);
+module_exit(msm_iommu_driver_exit);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iommu/msm_iommu_dev.c b/drivers/iommu/msm_iommu_dev.c
index 8e8fb07..c164825 100644
--- a/drivers/iommu/msm_iommu_dev.c
+++ b/drivers/iommu/msm_iommu_dev.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -8,11 +8,6 @@
  * 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 Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
  */
 
 #define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
@@ -29,7 +24,6 @@
 
 #include <mach/iommu_hw-8xxx.h>
 #include <mach/iommu.h>
-#include <mach/clk.h>
 
 struct iommu_ctx_iter_data {
 	/* input */
@@ -39,13 +33,14 @@
 	struct device *dev;
 };
 
-static struct platform_device *msm_iommu_root_dev;
+struct platform_device *msm_iommu_root_dev;
 
 static int each_iommu_ctx(struct device *dev, void *data)
 {
 	struct iommu_ctx_iter_data *res = data;
-	struct msm_iommu_ctx_dev *c = dev->platform_data;
+	struct msm_iommu_ctx_drvdata *c;
 
+	c = dev_get_drvdata(dev);
 	if (!res || !c || !c->name || !res->name)
 		return -EINVAL;
 
@@ -74,7 +69,7 @@
 	r.name = ctx_name;
 	found = device_for_each_child(&msm_iommu_root_dev->dev, &r, each_iommu);
 
-	if (!found) {
+	if (!found || !dev_get_drvdata(r.dev)) {
 		pr_err("Could not find context <%s>\n", ctx_name);
 		goto fail;
 	}
@@ -116,26 +111,28 @@
 		SET_BFBCR(base, ctx, 0);
 		SET_PAR(base, ctx, 0);
 		SET_FAR(base, ctx, 0);
-		SET_CTX_TLBIALL(base, ctx, 0);
 		SET_TLBFLPTER(base, ctx, 0);
 		SET_TLBSLPTER(base, ctx, 0);
 		SET_TLBLKCR(base, ctx, 0);
+		SET_CTX_TLBIALL(base, ctx, 0);
+		SET_TLBIVA(base, ctx, 0);
 		SET_PRRR(base, ctx, 0);
 		SET_NMRR(base, ctx, 0);
 		SET_CONTEXTIDR(base, ctx, 0);
 	}
+	mb();
 }
 
 static int msm_iommu_probe(struct platform_device *pdev)
 {
 	struct resource *r, *r2;
-	struct clk *iommu_clk;
-	struct clk *iommu_pclk;
+	struct clk *iommu_clk = NULL;
+	struct clk *iommu_pclk = NULL;
 	struct msm_iommu_drvdata *drvdata;
 	struct msm_iommu_dev *iommu_dev = pdev->dev.platform_data;
 	void __iomem *regs_base;
 	resource_size_t	len;
-	int ret, irq, par;
+	int ret, par;
 
 	if (pdev->id == -1) {
 		msm_iommu_root_dev = pdev;
@@ -154,23 +151,25 @@
 		goto fail;
 	}
 
-	iommu_pclk = clk_get(NULL, "smmu_pclk");
+	iommu_pclk = clk_get_sys("msm_iommu", "iface_clk");
 	if (IS_ERR(iommu_pclk)) {
 		ret = -ENODEV;
 		goto fail;
 	}
 
-	ret = clk_enable(iommu_pclk);
+	ret = clk_prepare_enable(iommu_pclk);
 	if (ret)
 		goto fail_enable;
 
-	iommu_clk = clk_get(&pdev->dev, "iommu_clk");
+	iommu_clk = clk_get(&pdev->dev, "core_clk");
 
 	if (!IS_ERR(iommu_clk))	{
-		if (clk_get_rate(iommu_clk) == 0)
-			clk_set_min_rate(iommu_clk, 1);
+		if (clk_get_rate(iommu_clk) == 0) {
+			ret = clk_round_rate(iommu_clk, 1);
+			clk_set_rate(iommu_clk, ret);
+		}
 
-		ret = clk_enable(iommu_clk);
+		ret = clk_prepare_enable(iommu_clk);
 		if (ret) {
 			clk_put(iommu_clk);
 			goto fail_pclk;
@@ -204,21 +203,17 @@
 		goto fail_mem;
 	}
 
-	irq = platform_get_irq_byname(pdev, "secure_irq");
-	if (irq < 0) {
-		ret = -ENODEV;
-		goto fail_io;
-	}
-
 	msm_iommu_reset(regs_base, iommu_dev->ncb);
 
 	SET_M(regs_base, 0, 1);
 	SET_PAR(regs_base, 0, 0);
 	SET_V2PCFG(regs_base, 0, 1);
 	SET_V2PPR(regs_base, 0, 0);
+	mb();
 	par = GET_PAR(regs_base, 0);
 	SET_V2PCFG(regs_base, 0, 0);
 	SET_M(regs_base, 0, 0);
+	mb();
 
 	if (!par) {
 		pr_err("%s: Invalid PAR value detected\n", iommu_dev->name);
@@ -226,29 +221,22 @@
 		goto fail_io;
 	}
 
-	ret = request_irq(irq, msm_iommu_fault_handler, 0,
-			"msm_iommu_secure_irpt_handler", drvdata);
-	if (ret) {
-		pr_err("Request IRQ %d failed with ret=%d\n", irq, ret);
-		goto fail_io;
-	}
-
-
 	drvdata->pclk = iommu_pclk;
 	drvdata->clk = iommu_clk;
 	drvdata->base = regs_base;
-	drvdata->irq = irq;
 	drvdata->ncb = iommu_dev->ncb;
+	drvdata->ttbr_split = iommu_dev->ttbr_split;
+	drvdata->name = iommu_dev->name;
 
-	pr_info("device %s mapped at %p, irq %d with %d ctx banks\n",
-		iommu_dev->name, regs_base, irq, iommu_dev->ncb);
+	pr_info("device %s mapped at %p, with %d ctx banks\n",
+		iommu_dev->name, regs_base, iommu_dev->ncb);
 
 	platform_set_drvdata(pdev, drvdata);
 
 	if (iommu_clk)
-		clk_disable(iommu_clk);
+		clk_disable_unprepare(iommu_clk);
 
-	clk_disable(iommu_pclk);
+	clk_disable_unprepare(iommu_pclk);
 
 	return 0;
 fail_io:
@@ -257,11 +245,11 @@
 	release_mem_region(r->start, len);
 fail_clk:
 	if (iommu_clk) {
-		clk_disable(iommu_clk);
+		clk_disable_unprepare(iommu_clk);
 		clk_put(iommu_clk);
 	}
 fail_pclk:
-	clk_disable(iommu_pclk);
+	clk_disable_unprepare(iommu_pclk);
 fail_enable:
 	clk_put(iommu_pclk);
 fail:
@@ -290,7 +278,7 @@
 	struct msm_iommu_ctx_dev *c = pdev->dev.platform_data;
 	struct msm_iommu_drvdata *drvdata;
 	struct msm_iommu_ctx_drvdata *ctx_drvdata = NULL;
-	int i, ret;
+	int i, ret, irq;
 	if (!c || !pdev->dev.parent) {
 		ret = -EINVAL;
 		goto fail;
@@ -310,18 +298,35 @@
 	}
 	ctx_drvdata->num = c->num;
 	ctx_drvdata->pdev = pdev;
+	ctx_drvdata->name = c->name;
+
+	irq = platform_get_irq_byname(to_platform_device(pdev->dev.parent),
+				      "nonsecure_irq");
+	if (irq < 0) {
+		ret = -ENODEV;
+		goto fail;
+	}
+
+	ret = request_threaded_irq(irq, NULL, msm_iommu_fault_handler,
+				   IRQF_ONESHOT | IRQF_SHARED,
+				   "msm_iommu_nonsecure_irq", ctx_drvdata);
+
+	if (ret) {
+		pr_err("request_threaded_irq %d failed: %d\n", irq, ret);
+		goto fail;
+	}
 
 	INIT_LIST_HEAD(&ctx_drvdata->attached_elm);
 	platform_set_drvdata(pdev, ctx_drvdata);
 
-	ret = clk_enable(drvdata->pclk);
+	ret = clk_prepare_enable(drvdata->pclk);
 	if (ret)
 		goto fail;
 
 	if (drvdata->clk) {
-		ret = clk_enable(drvdata->clk);
+		ret = clk_prepare_enable(drvdata->clk);
 		if (ret) {
-			clk_disable(drvdata->pclk);
+			clk_disable_unprepare(drvdata->pclk);
 			goto fail;
 		}
 	}
@@ -335,25 +340,29 @@
 		SET_M2VCBR_N(drvdata->base, mid, 0);
 		SET_CBACR_N(drvdata->base, c->num, 0);
 
+		/* Route page faults to the non-secure interrupt */
+		SET_IRPTNDX(drvdata->base, c->num, 1);
+
 		/* Set VMID = 0 */
 		SET_VMID(drvdata->base, mid, 0);
 
 		/* Set the context number for that MID to this context */
 		SET_CBNDX(drvdata->base, mid, c->num);
 
-		/* Set MID associated with this context bank to 0*/
+		/* Set MID associated with this context bank to 0 */
 		SET_CBVMID(drvdata->base, c->num, 0);
 
-		/* Set the ASID for TLB tagging for this context */
-		SET_CONTEXTIDR_ASID(drvdata->base, c->num, c->num);
+		/* Set the ASID for TLB tagging for this context to 0 */
+		SET_CONTEXTIDR_ASID(drvdata->base, c->num, 0);
 
 		/* Set security bit override to be Non-secure */
 		SET_NSCFG(drvdata->base, mid, 3);
 	}
+	mb();
 
 	if (drvdata->clk)
-		clk_disable(drvdata->clk);
-	clk_disable(drvdata->pclk);
+		clk_disable_unprepare(drvdata->clk);
+	clk_disable_unprepare(drvdata->pclk);
 
 	dev_info(&pdev->dev, "context %s using bank %d\n", c->name, c->num);
 	return 0;
diff --git a/drivers/iommu/msm_iommu_pagetable.c b/drivers/iommu/msm_iommu_pagetable.c
new file mode 100644
index 0000000..b93860e
--- /dev/null
+++ b/drivers/iommu/msm_iommu_pagetable.c
@@ -0,0 +1,519 @@
+/* Copyright (c) 2012 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/iommu.h>
+#include <linux/scatterlist.h>
+
+#include <asm/cacheflush.h>
+
+#include <mach/iommu.h>
+#include "msm_iommu_pagetable.h"
+
+/* Sharability attributes of MSM IOMMU mappings */
+#define MSM_IOMMU_ATTR_NON_SH		0x0
+#define MSM_IOMMU_ATTR_SH		0x4
+
+/* Cacheability attributes of MSM IOMMU mappings */
+#define MSM_IOMMU_ATTR_NONCACHED	0x0
+#define MSM_IOMMU_ATTR_CACHED_WB_WA	0x1
+#define MSM_IOMMU_ATTR_CACHED_WB_NWA	0x2
+#define MSM_IOMMU_ATTR_CACHED_WT	0x3
+
+static int msm_iommu_tex_class[4];
+
+static inline void clean_pte(unsigned long *start, unsigned long *end,
+				int redirect)
+{
+	if (!redirect)
+		dmac_flush_range(start, end);
+}
+
+int msm_iommu_pagetable_alloc(struct iommu_pt *pt)
+{
+	pt->fl_table = (unsigned long *)__get_free_pages(GFP_KERNEL,
+							  get_order(SZ_16K));
+	if (!pt->fl_table)
+		return -ENOMEM;
+
+	memset(pt->fl_table, 0, SZ_16K);
+	clean_pte(pt->fl_table, pt->fl_table + NUM_FL_PTE, pt->redirect);
+
+	return 0;
+}
+
+void msm_iommu_pagetable_free(struct iommu_pt *pt)
+{
+	unsigned long *fl_table;
+	int i;
+
+	fl_table = pt->fl_table;
+	for (i = 0; i < NUM_FL_PTE; i++)
+		if ((fl_table[i] & 0x03) == FL_TYPE_TABLE)
+			free_page((unsigned long) __va(((fl_table[i]) &
+							FL_BASE_MASK)));
+	free_pages((unsigned long)fl_table, get_order(SZ_16K));
+	pt->fl_table = 0;
+}
+
+static int __get_pgprot(int prot, int len)
+{
+	unsigned int pgprot;
+	int tex;
+
+	if (!(prot & (IOMMU_READ | IOMMU_WRITE))) {
+		prot |= IOMMU_READ | IOMMU_WRITE;
+		WARN_ONCE(1, "No attributes in iommu mapping; assuming RW\n");
+	}
+
+	if ((prot & IOMMU_WRITE) && !(prot & IOMMU_READ)) {
+		prot |= IOMMU_READ;
+		WARN_ONCE(1, "Write-only unsupported; falling back to RW\n");
+	}
+
+	if (prot & IOMMU_CACHE)
+		tex = (pgprot_kernel >> 2) & 0x07;
+	else
+		tex = msm_iommu_tex_class[MSM_IOMMU_ATTR_NONCACHED];
+
+	if (tex < 0 || tex > NUM_TEX_CLASS - 1)
+		return 0;
+
+	if (len == SZ_16M || len == SZ_1M) {
+		pgprot = FL_SHARED;
+		pgprot |= tex & 0x01 ? FL_BUFFERABLE : 0;
+		pgprot |= tex & 0x02 ? FL_CACHEABLE : 0;
+		pgprot |= tex & 0x04 ? FL_TEX0 : 0;
+		pgprot |= FL_AP0 | FL_AP1;
+		pgprot |= prot & IOMMU_WRITE ? 0 : FL_AP2;
+	} else	{
+		pgprot = SL_SHARED;
+		pgprot |= tex & 0x01 ? SL_BUFFERABLE : 0;
+		pgprot |= tex & 0x02 ? SL_CACHEABLE : 0;
+		pgprot |= tex & 0x04 ? SL_TEX0 : 0;
+		pgprot |= SL_AP0 | SL_AP1;
+		pgprot |= prot & IOMMU_WRITE ? 0 : SL_AP2;
+	}
+
+	return pgprot;
+}
+
+int msm_iommu_pagetable_map(struct iommu_pt *pt, unsigned long va,
+			phys_addr_t pa, size_t len, int prot)
+{
+	unsigned long *fl_pte;
+	unsigned long fl_offset;
+	unsigned long *sl_table;
+	unsigned long *sl_pte;
+	unsigned long sl_offset;
+	unsigned int pgprot;
+	int ret = 0;
+
+	if (len != SZ_16M && len != SZ_1M &&
+	    len != SZ_64K && len != SZ_4K) {
+		pr_debug("Bad size: %d\n", len);
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	if (!pt->fl_table) {
+		pr_debug("Null page table\n");
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	pgprot = __get_pgprot(prot, len);
+	if (!pgprot) {
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	fl_offset = FL_OFFSET(va);		/* Upper 12 bits */
+	fl_pte = pt->fl_table + fl_offset;	/* int pointers, 4 bytes */
+
+	if (len == SZ_16M) {
+		int i = 0;
+
+		for (i = 0; i < 16; i++)
+			if (*(fl_pte+i)) {
+				ret = -EBUSY;
+				goto fail;
+			}
+
+		for (i = 0; i < 16; i++)
+			*(fl_pte+i) = (pa & 0xFF000000) | FL_SUPERSECTION |
+				  FL_TYPE_SECT | FL_SHARED | FL_NG | pgprot;
+		clean_pte(fl_pte, fl_pte + 16, pt->redirect);
+	}
+
+	if (len == SZ_1M) {
+		if (*fl_pte) {
+			ret = -EBUSY;
+			goto fail;
+		}
+
+		*fl_pte = (pa & 0xFFF00000) | FL_NG | FL_TYPE_SECT
+					| FL_SHARED | pgprot;
+		clean_pte(fl_pte, fl_pte + 1, pt->redirect);
+	}
+
+	/* Need a 2nd level table */
+	if (len == SZ_4K || len == SZ_64K) {
+
+		if (*fl_pte == 0) {
+			unsigned long *sl;
+			sl = (unsigned long *) __get_free_pages(GFP_KERNEL,
+							get_order(SZ_4K));
+
+			if (!sl) {
+				pr_debug("Could not allocate second level table\n");
+				ret = -ENOMEM;
+				goto fail;
+			}
+			memset(sl, 0, SZ_4K);
+			clean_pte(sl, sl + NUM_SL_PTE, pt->redirect);
+
+			*fl_pte = ((((int)__pa(sl)) & FL_BASE_MASK) | \
+						      FL_TYPE_TABLE);
+			clean_pte(fl_pte, fl_pte + 1, pt->redirect);
+		}
+
+		if (!(*fl_pte & FL_TYPE_TABLE)) {
+			ret = -EBUSY;
+			goto fail;
+		}
+	}
+
+	sl_table = (unsigned long *) __va(((*fl_pte) & FL_BASE_MASK));
+	sl_offset = SL_OFFSET(va);
+	sl_pte = sl_table + sl_offset;
+
+	if (len == SZ_4K) {
+		if (*sl_pte) {
+			ret = -EBUSY;
+			goto fail;
+		}
+
+		*sl_pte = (pa & SL_BASE_MASK_SMALL) | SL_NG | SL_SHARED
+						| SL_TYPE_SMALL | pgprot;
+		clean_pte(sl_pte, sl_pte + 1, pt->redirect);
+	}
+
+	if (len == SZ_64K) {
+		int i;
+
+		for (i = 0; i < 16; i++)
+			if (*(sl_pte+i)) {
+				ret = -EBUSY;
+				goto fail;
+			}
+
+		for (i = 0; i < 16; i++)
+			*(sl_pte+i) = (pa & SL_BASE_MASK_LARGE) | SL_NG
+					| SL_SHARED | SL_TYPE_LARGE | pgprot;
+
+		clean_pte(sl_pte, sl_pte + 16, pt->redirect);
+	}
+
+fail:
+	return ret;
+}
+
+size_t msm_iommu_pagetable_unmap(struct iommu_pt *pt, unsigned long va,
+				size_t len)
+{
+	unsigned long *fl_pte;
+	unsigned long fl_offset;
+	unsigned long *sl_table;
+	unsigned long *sl_pte;
+	unsigned long sl_offset;
+	int i, ret = 0;
+
+	if (len != SZ_16M && len != SZ_1M &&
+	    len != SZ_64K && len != SZ_4K) {
+		pr_debug("Bad length: %d\n", len);
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	if (!pt->fl_table) {
+		pr_debug("Null page table\n");
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	fl_offset = FL_OFFSET(va);		/* Upper 12 bits */
+	fl_pte = pt->fl_table + fl_offset;	/* int pointers, 4 bytes */
+
+	if (*fl_pte == 0) {
+		pr_debug("First level PTE is 0\n");
+		ret = -ENODEV;
+		goto fail;
+	}
+
+	/* Unmap supersection */
+	if (len == SZ_16M) {
+		for (i = 0; i < 16; i++)
+			*(fl_pte+i) = 0;
+
+		clean_pte(fl_pte, fl_pte + 16, pt->redirect);
+	}
+
+	if (len == SZ_1M) {
+		*fl_pte = 0;
+		clean_pte(fl_pte, fl_pte + 1, pt->redirect);
+	}
+
+	sl_table = (unsigned long *) __va(((*fl_pte) & FL_BASE_MASK));
+	sl_offset = SL_OFFSET(va);
+	sl_pte = sl_table + sl_offset;
+
+	if (len == SZ_64K) {
+		for (i = 0; i < 16; i++)
+			*(sl_pte+i) = 0;
+
+		clean_pte(sl_pte, sl_pte + 16, pt->redirect);
+	}
+
+	if (len == SZ_4K) {
+		*sl_pte = 0;
+		clean_pte(sl_pte, sl_pte + 1, pt->redirect);
+	}
+
+	if (len == SZ_4K || len == SZ_64K) {
+		int used = 0;
+
+		for (i = 0; i < NUM_SL_PTE; i++)
+			if (sl_table[i])
+				used = 1;
+		if (!used) {
+			free_page((unsigned long)sl_table);
+			*fl_pte = 0;
+			clean_pte(fl_pte, fl_pte + 1, pt->redirect);
+		}
+	}
+
+fail:
+	return ret;
+}
+
+static unsigned int 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.
+	 */
+	unsigned int pa = sg_dma_address(sg);
+	if (pa == 0)
+		pa = sg_phys(sg);
+	return pa;
+}
+
+int msm_iommu_pagetable_map_range(struct iommu_pt *pt, unsigned int va,
+		       struct scatterlist *sg, unsigned int len, int prot)
+{
+	unsigned int pa;
+	unsigned int offset = 0;
+	unsigned int pgprot;
+	unsigned long *fl_pte;
+	unsigned long fl_offset;
+	unsigned long *sl_table;
+	unsigned long sl_offset, sl_start;
+	unsigned int chunk_offset = 0;
+	unsigned int chunk_pa;
+	int ret = 0;
+
+	BUG_ON(len & (SZ_4K - 1));
+
+	pgprot = __get_pgprot(prot, SZ_4K);
+	if (!pgprot) {
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	fl_offset = FL_OFFSET(va);		/* Upper 12 bits */
+	fl_pte = pt->fl_table + fl_offset;	/* int pointers, 4 bytes */
+
+	sl_table = (unsigned long *) __va(((*fl_pte) & FL_BASE_MASK));
+	sl_offset = SL_OFFSET(va);
+
+	chunk_pa = get_phys_addr(sg);
+	if (chunk_pa == 0) {
+		pr_debug("No dma address for sg %p\n", sg);
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	while (offset < len) {
+		/* Set up a 2nd level page table if one doesn't exist */
+		if (*fl_pte == 0) {
+			sl_table = (unsigned long *)
+				 __get_free_pages(GFP_KERNEL, get_order(SZ_4K));
+
+			if (!sl_table) {
+				pr_debug("Could not allocate second level table\n");
+				ret = -ENOMEM;
+				goto fail;
+			}
+
+			memset(sl_table, 0, SZ_4K);
+			clean_pte(sl_table, sl_table + NUM_SL_PTE,
+					pt->redirect);
+
+			*fl_pte = ((((int)__pa(sl_table)) & FL_BASE_MASK) |
+							    FL_TYPE_TABLE);
+			clean_pte(fl_pte, fl_pte + 1, pt->redirect);
+		} else
+			sl_table = (unsigned long *)
+					       __va(((*fl_pte) & FL_BASE_MASK));
+
+		/* Keep track of initial position so we
+		 * don't clean more than we have to
+		 */
+		sl_start = sl_offset;
+
+		/* Build the 2nd level page table */
+		while (offset < len && sl_offset < NUM_SL_PTE) {
+			pa = chunk_pa + chunk_offset;
+			sl_table[sl_offset] = (pa & SL_BASE_MASK_SMALL) |
+			      pgprot | SL_NG | SL_SHARED | SL_TYPE_SMALL;
+			sl_offset++;
+			offset += SZ_4K;
+
+			chunk_offset += SZ_4K;
+
+			if (chunk_offset >= sg->length && offset < len) {
+				chunk_offset = 0;
+				sg = sg_next(sg);
+				chunk_pa = get_phys_addr(sg);
+				if (chunk_pa == 0) {
+					pr_debug("No dma address for sg %p\n",
+						sg);
+					ret = -EINVAL;
+					goto fail;
+				}
+			}
+		}
+
+		clean_pte(sl_table + sl_start, sl_table + sl_offset,
+				pt->redirect);
+		fl_pte++;
+		sl_offset = 0;
+	}
+
+fail:
+	return ret;
+}
+
+void msm_iommu_pagetable_unmap_range(struct iommu_pt *pt, unsigned int va,
+				 unsigned int len)
+{
+	unsigned int offset = 0;
+	unsigned long *fl_pte;
+	unsigned long fl_offset;
+	unsigned long *sl_table;
+	unsigned long sl_start, sl_end;
+	int used, i;
+
+	BUG_ON(len & (SZ_4K - 1));
+
+	fl_offset = FL_OFFSET(va);		/* Upper 12 bits */
+	fl_pte = pt->fl_table + fl_offset;	/* int pointers, 4 bytes */
+
+	sl_start = SL_OFFSET(va);
+
+	while (offset < len) {
+		sl_table = (unsigned long *) __va(((*fl_pte) & FL_BASE_MASK));
+		sl_end = ((len - offset) / SZ_4K) + sl_start;
+
+		if (sl_end > NUM_SL_PTE)
+			sl_end = NUM_SL_PTE;
+
+		memset(sl_table + sl_start, 0, (sl_end - sl_start) * 4);
+		clean_pte(sl_table + sl_start, sl_table + sl_end,
+				pt->redirect);
+
+		offset += (sl_end - sl_start) * SZ_4K;
+
+		/* Unmap and free the 2nd level table if all mappings in it
+		 * were removed. This saves memory, but the table will need
+		 * to be re-allocated the next time someone tries to map these
+		 * VAs.
+		 */
+		used = 0;
+
+		/* If we just unmapped the whole table, don't bother
+		 * seeing if there are still used entries left.
+		 */
+		if (sl_end - sl_start != NUM_SL_PTE)
+			for (i = 0; i < NUM_SL_PTE; i++)
+				if (sl_table[i]) {
+					used = 1;
+					break;
+				}
+		if (!used) {
+			free_page((unsigned long)sl_table);
+			*fl_pte = 0;
+			clean_pte(fl_pte, fl_pte + 1, pt->redirect);
+		}
+
+		sl_start = 0;
+		fl_pte++;
+	}
+}
+
+static int __init get_tex_class(int icp, int ocp, int mt, int nos)
+{
+	int i = 0;
+	unsigned int prrr = 0;
+	unsigned int nmrr = 0;
+	int c_icp, c_ocp, c_mt, c_nos;
+
+	RCP15_PRRR(prrr);
+	RCP15_NMRR(nmrr);
+
+	for (i = 0; i < NUM_TEX_CLASS; i++) {
+		c_nos = PRRR_NOS(prrr, i);
+		c_mt = PRRR_MT(prrr, i);
+		c_icp = NMRR_ICP(nmrr, i);
+		c_ocp = NMRR_OCP(nmrr, i);
+
+		if (icp == c_icp && ocp == c_ocp && c_mt == mt && c_nos == nos)
+			return i;
+	}
+
+	return -ENODEV;
+}
+
+static void __init setup_iommu_tex_classes(void)
+{
+	msm_iommu_tex_class[MSM_IOMMU_ATTR_NONCACHED] =
+			get_tex_class(CP_NONCACHED, CP_NONCACHED, MT_NORMAL, 1);
+
+	msm_iommu_tex_class[MSM_IOMMU_ATTR_CACHED_WB_WA] =
+			get_tex_class(CP_WB_WA, CP_WB_WA, MT_NORMAL, 1);
+
+	msm_iommu_tex_class[MSM_IOMMU_ATTR_CACHED_WB_NWA] =
+			get_tex_class(CP_WB_NWA, CP_WB_NWA, MT_NORMAL, 1);
+
+	msm_iommu_tex_class[MSM_IOMMU_ATTR_CACHED_WT] =
+			get_tex_class(CP_WT, CP_WT, MT_NORMAL, 1);
+}
+
+void __init msm_iommu_pagetable_init(void)
+{
+	setup_iommu_tex_classes();
+}
diff --git a/drivers/iommu/msm_iommu_pagetable.h b/drivers/iommu/msm_iommu_pagetable.h
new file mode 100644
index 0000000..b943084
--- /dev/null
+++ b/drivers/iommu/msm_iommu_pagetable.h
@@ -0,0 +1,90 @@
+/* Copyright (c) 2012 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ARCH_ARM_MACH_MSM_IOMMU_PAGETABLE_H
+#define __ARCH_ARM_MACH_MSM_IOMMU_PAGETABLE_H
+
+#define NUM_FL_PTE      4096
+#define NUM_SL_PTE      256
+#define NUM_TEX_CLASS   8
+
+/* First-level page table bits */
+#define FL_BASE_MASK            0xFFFFFC00
+#define FL_TYPE_TABLE           (1 << 0)
+#define FL_TYPE_SECT            (2 << 0)
+#define FL_SUPERSECTION         (1 << 18)
+#define FL_AP0                  (1 << 10)
+#define FL_AP1                  (1 << 11)
+#define FL_AP2                  (1 << 15)
+#define FL_SHARED               (1 << 16)
+#define FL_BUFFERABLE           (1 << 2)
+#define FL_CACHEABLE            (1 << 3)
+#define FL_TEX0                 (1 << 12)
+#define FL_OFFSET(va)           (((va) & 0xFFF00000) >> 20)
+#define FL_NG                   (1 << 17)
+
+/* Second-level page table bits */
+#define SL_BASE_MASK_LARGE      0xFFFF0000
+#define SL_BASE_MASK_SMALL      0xFFFFF000
+#define SL_TYPE_LARGE           (1 << 0)
+#define SL_TYPE_SMALL           (2 << 0)
+#define SL_AP0                  (1 << 4)
+#define SL_AP1                  (2 << 4)
+#define SL_AP2                  (1 << 9)
+#define SL_SHARED               (1 << 10)
+#define SL_BUFFERABLE           (1 << 2)
+#define SL_CACHEABLE            (1 << 3)
+#define SL_TEX0                 (1 << 6)
+#define SL_OFFSET(va)           (((va) & 0xFF000) >> 12)
+#define SL_NG                   (1 << 11)
+
+/* Memory type and cache policy attributes */
+#define MT_SO                   0
+#define MT_DEV                  1
+#define MT_NORMAL               2
+#define CP_NONCACHED            0
+#define CP_WB_WA                1
+#define CP_WT                   2
+#define CP_WB_NWA               3
+
+/* TEX Remap Registers */
+#define NMRR_ICP(nmrr, n) (((nmrr) & (3 << ((n) * 2))) >> ((n) * 2))
+#define NMRR_OCP(nmrr, n) (((nmrr) & (3 << ((n) * 2 + 16))) >> ((n) * 2 + 16))
+
+#define PRRR_NOS(prrr, n) ((prrr) & (1 << ((n) + 24)) ? 1 : 0)
+#define PRRR_MT(prrr, n)  ((((prrr) & (3 << ((n) * 2))) >> ((n) * 2)))
+
+#define MRC(reg, processor, op1, crn, crm, op2)                         \
+__asm__ __volatile__ (                                                  \
+"   mrc   "   #processor "," #op1 ", %0,"  #crn "," #crm "," #op2 "\n"  \
+: "=r" (reg))
+
+#define RCP15_PRRR(reg)   MRC(reg, p15, 0, c10, c2, 0)
+#define RCP15_NMRR(reg)   MRC(reg, p15, 0, c10, c2, 1)
+
+struct iommu_pt {
+	unsigned long *fl_table;
+	int redirect;
+};
+
+void msm_iommu_pagetable_init(void);
+int msm_iommu_pagetable_alloc(struct iommu_pt *pt);
+void msm_iommu_pagetable_free(struct iommu_pt *pt);
+int msm_iommu_pagetable_map(struct iommu_pt *pt, unsigned long va,
+			phys_addr_t pa, size_t len, int prot);
+size_t msm_iommu_pagetable_unmap(struct iommu_pt *pt, unsigned long va,
+				size_t len);
+int msm_iommu_pagetable_map_range(struct iommu_pt *pt, unsigned int va,
+			struct scatterlist *sg, unsigned int len, int prot);
+void msm_iommu_pagetable_unmap_range(struct iommu_pt *pt, unsigned int va,
+				unsigned int len);
+#endif
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index ef2ddd4..d49bfa6 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -157,6 +157,47 @@
 	  defined as platform devices and/or OpenFirmware platform devices.
 	  The code to use these bindings can be selected below.
 
+config LEDS_MSM_PDM
+	tristate "LED Support through PDM"
+	depends on LEDS_CLASS
+	help
+	  This option enables support for the LEDs operated through Pulse
+	  Denisty Modulation.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called leds-msm-pdm.
+
+config LEDS_PMIC_MPP
+        tristate "LED Support for Qualcomm PMIC MPP connected LEDs"
+        depends on LEDS_CLASS && MSM_SMD
+        help
+          This option enables support for LEDs connected to  PMIC MPPs
+	  on Qualcomm reference boards.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called leds-pmic-mpp.
+
+config LEDS_GPIO_PLATFORM
+	bool "Platform device bindings for GPIO LEDs"
+	depends on LEDS_GPIO
+	default y
+	help
+	  Let the leds-gpio driver drive LEDs which have been defined as
+	  platform devices.  If you don't know what this means, say yes.
+
+config LEDS_GPIO_OF
+	bool "OpenFirmware platform device bindings for GPIO LEDs"
+	depends on LEDS_GPIO && OF_DEVICE
+	default y
+	help
+	  Let the leds-gpio driver drive LEDs which have been defined as
+	  of_platform devices.  For instance, LEDs which are listed in a "dts"
+	  file.
+
 config LEDS_LP3944
 	tristate "LED Support for N.S. LP3944 (Fun Light) I2C chip"
 	depends on LEDS_CLASS
@@ -169,6 +210,12 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called leds-lp3944.
 
+config LEDS_CPLD
+	tristate "LED Support for CPLD connected LEDs"
+	depends on LEDS_CLASS
+	help
+	  This option enables support for the LEDs connected to CPLD
+
 config LEDS_LP5521
 	tristate "LED Support for N.S. LP5521 LED driver chip"
 	depends on LEDS_CLASS && I2C
@@ -227,6 +274,17 @@
 	  LED driver chips accessed via the I2C bus.  Supported
 	  devices include PCA9550, PCA9551, PCA9552, and PCA9553.
 
+config LEDS_PM8XXX
+	tristate "LED Support for Qualcomm PMIC8XXX"
+	depends on MFD_PM8XXX
+	help
+	  This option enables support for LEDs connected over PMIC8XXX
+	  (Power Management IC) chip on Qualcomm reference boards,
+	  for example SURF and FFAs.
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called leds-pmic8xxx.
+
 config LEDS_PCA9633
 	tristate "LED support for PCA9633 I2C chip"
 	depends on LEDS_CLASS
@@ -289,6 +347,24 @@
 	  This option enables support for BD2802GU RGB LED driver chips
 	  accessed via the I2C bus.
 
+config LEDS_MSM_PMIC
+        tristate "LED Support for Qualcomm PMIC connected LEDs"
+        default n
+        depends on ARCH_MSM
+        help
+          This option enables support for LEDs connected over PMIC
+          (Power Management IC) chip on Qualcomm reference boards,
+          for example SURF and FFAs.
+
+config LEDS_PMIC8058
+	tristate "LED Support for Qualcomm PMIC8058"
+	default n
+	depends on PMIC8058
+	help
+	  This option enables support for LEDs connected over PMIC8058
+	  (Power Management IC) chip on Qualcomm reference boards,
+	  for example SURF and FFAs.
+
 config LEDS_INTEL_SS4200
 	tristate "LED driver for Intel NAS SS4200 series"
 	depends on LEDS_CLASS
@@ -335,6 +411,14 @@
 	  This option enable support for on-chip LED drivers found
 	  on Freescale Semiconductor MC13783 PMIC.
 
+config LEDS_QCIBL
+	tristate "LED Support for Quanta LCD backlight"
+	depends on SENSORS_WPCE775X && ARCH_MSM_SCORPION
+	default n
+	help
+	  Say Y here if you want to use the Quanta backlight driver for ST15
+	  platform.
+
 config LEDS_NS2
 	tristate "LED support for Network Space v2 GPIO LEDs"
 	depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 9d3b109..aa518d4 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -18,6 +18,7 @@
 obj-$(CONFIG_LEDS_COBALT_RAQ)		+= leds-cobalt-raq.o
 obj-$(CONFIG_LEDS_SUNFIRE)		+= leds-sunfire.o
 obj-$(CONFIG_LEDS_PCA9532)		+= leds-pca9532.o
+obj-$(CONFIG_LEDS_PM8XXX)		+= leds-pm8xxx.o
 obj-$(CONFIG_LEDS_GPIO_REGISTER)	+= leds-gpio-register.o
 obj-$(CONFIG_LEDS_GPIO)			+= leds-gpio.o
 obj-$(CONFIG_LEDS_LP3944)		+= leds-lp3944.o
@@ -25,10 +26,12 @@
 obj-$(CONFIG_LEDS_LP5523)		+= leds-lp5523.o
 obj-$(CONFIG_LEDS_TCA6507)		+= leds-tca6507.o
 obj-$(CONFIG_LEDS_CLEVO_MAIL)		+= leds-clevo-mail.o
+obj-$(CONFIG_LEDS_CPLD)			+= leds-cpld.o
 obj-$(CONFIG_LEDS_HP6XX)		+= leds-hp6xx.o
 obj-$(CONFIG_LEDS_OT200)		+= leds-ot200.o
 obj-$(CONFIG_LEDS_FSG)			+= leds-fsg.o
 obj-$(CONFIG_LEDS_PCA955X)		+= leds-pca955x.o
+obj-$(CONFIG_LEDS_MSM_PMIC)		+= leds-msm-pmic.o
 obj-$(CONFIG_LEDS_PCA9633)		+= leds-pca9633.o
 obj-$(CONFIG_LEDS_DA903X)		+= leds-da903x.o
 obj-$(CONFIG_LEDS_WM831X_STATUS)	+= leds-wm831x-status.o
@@ -45,6 +48,10 @@
 obj-$(CONFIG_LEDS_ASIC3)		+= leds-asic3.o
 obj-$(CONFIG_LEDS_RENESAS_TPU)		+= leds-renesas-tpu.o
 obj-$(CONFIG_LEDS_MAX8997)		+= leds-max8997.o
+obj-$(CONFIG_LEDS_PMIC8058)		+= leds-pmic8058.o
+obj-$(CONFIG_LEDS_PMIC_MPP)		+= leds-pmic-mpp.o
+obj-$(CONFIG_LEDS_QCIBL)		+= leds-qci-backlight.o
+obj-$(CONFIG_LEDS_MSM_PDM)		+= leds-msm-pdm.o
 
 # LED SPI Drivers
 obj-$(CONFIG_LEDS_DAC124S085)		+= leds-dac124s085.o
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index 5bff843..a2b2f3d 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -21,6 +21,8 @@
 #include <linux/leds.h>
 #include "leds.h"
 
+#define LED_BUFF_SIZE 50
+
 static struct class *leds_class;
 
 static void led_update_brightness(struct led_classdev *led_cdev)
@@ -37,7 +39,7 @@
 	/* no lock needed for this */
 	led_update_brightness(led_cdev);
 
-	return sprintf(buf, "%u\n", led_cdev->brightness);
+	return snprintf(buf, LED_BUFF_SIZE, "%u\n", led_cdev->brightness);
 }
 
 static ssize_t led_brightness_store(struct device *dev,
@@ -63,17 +65,37 @@
 	return ret;
 }
 
+static ssize_t led_max_brightness_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	ssize_t ret = -EINVAL;
+	unsigned long state = 0;
+
+	ret = strict_strtoul(buf, 10, &state);
+	if (!ret) {
+		ret = size;
+		if (state > LED_FULL)
+			state = LED_FULL;
+		led_cdev->max_brightness = state;
+		led_set_brightness(led_cdev, led_cdev->brightness);
+	}
+
+	return ret;
+}
+
 static ssize_t led_max_brightness_show(struct device *dev,
 		struct device_attribute *attr, char *buf)
 {
 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
 
-	return sprintf(buf, "%u\n", led_cdev->max_brightness);
+	return snprintf(buf, LED_BUFF_SIZE, "%u\n", led_cdev->max_brightness);
 }
 
 static struct device_attribute led_class_attrs[] = {
 	__ATTR(brightness, 0644, led_brightness_show, led_brightness_store),
-	__ATTR(max_brightness, 0444, led_max_brightness_show, NULL),
+	__ATTR(max_brightness, 0644, led_max_brightness_show,
+			led_max_brightness_store),
 #ifdef CONFIG_LEDS_TRIGGERS
 	__ATTR(trigger, 0644, led_trigger_show, led_trigger_store),
 #endif
diff --git a/drivers/leds/leds-cpld.c b/drivers/leds/leds-cpld.c
new file mode 100644
index 0000000..eab004c
--- /dev/null
+++ b/drivers/leds/leds-cpld.c
@@ -0,0 +1,405 @@
+/* include/asm/mach-msm/leds-cpld.c
+ *
+ * Copyright (C) 2008 HTC Corporation.
+ *
+ * Author: Farmer Tseng
+ *
+ * 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/init.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/leds.h>
+#include <linux/spinlock.h>
+#include <linux/ctype.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <asm/mach-types.h>
+
+#define DEBUG_LED_CHANGE 0
+
+static int _g_cpld_led_addr;
+
+struct CPLD_LED_data {
+	spinlock_t data_lock;
+	struct led_classdev leds[4];	/* blue, green, red */
+};
+
+static ssize_t led_blink_solid_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	struct CPLD_LED_data *CPLD_LED;
+	int idx = 2;
+	uint8_t reg_val;
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	ssize_t ret = 0;
+
+	if (!strcmp(led_cdev->name, "red"))
+		idx = 0;
+	else if (!strcmp(led_cdev->name, "green"))
+		idx = 1;
+	else
+		idx = 2;
+
+	CPLD_LED = container_of(led_cdev, struct CPLD_LED_data, leds[idx]);
+
+	spin_lock(&CPLD_LED->data_lock);
+	reg_val = readb(_g_cpld_led_addr);
+	reg_val = reg_val >> (2 * idx + 1);
+	reg_val &= 0x1;
+	spin_unlock(&CPLD_LED->data_lock);
+
+	/* no lock needed for this */
+	sprintf(buf, "%u\n", reg_val);
+	ret = strlen(buf) + 1;
+
+	return ret;
+}
+
+static ssize_t led_blink_solid_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t size)
+{
+	struct CPLD_LED_data *CPLD_LED;
+	int idx = 2;
+	uint8_t reg_val;
+	char *after;
+	unsigned long state;
+	ssize_t ret = -EINVAL;
+	size_t count;
+
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+
+	if (!strcmp(led_cdev->name, "red"))
+		idx = 0;
+	else if (!strcmp(led_cdev->name, "green"))
+		idx = 1;
+	else
+		idx = 2;
+
+	CPLD_LED = container_of(led_cdev, struct CPLD_LED_data, leds[idx]);
+
+	state = simple_strtoul(buf, &after, 10);
+
+	count = after - buf;
+
+	if (*after && isspace(*after))
+		count++;
+
+	if (count == size) {
+		ret = count;
+		spin_lock(&CPLD_LED->data_lock);
+		reg_val = readb(_g_cpld_led_addr);
+		if (state)
+			reg_val |= 1 << (2 * idx + 1);
+		else
+			reg_val &= ~(1 << (2 * idx + 1));
+
+		writeb(reg_val, _g_cpld_led_addr);
+		spin_unlock(&CPLD_LED->data_lock);
+	}
+
+	return ret;
+}
+
+static DEVICE_ATTR(blink, 0644, led_blink_solid_show, led_blink_solid_store);
+
+static ssize_t cpldled_blink_all_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	uint8_t reg_val;
+	struct CPLD_LED_data *CPLD_LED = dev_get_drvdata(dev);
+	ssize_t ret = 0;
+
+	spin_lock(&CPLD_LED->data_lock);
+	reg_val = readb(_g_cpld_led_addr);
+	reg_val &= 0x2A;
+	if (reg_val == 0x2A)
+		reg_val = 1;
+	else
+		reg_val = 0;
+	spin_unlock(&CPLD_LED->data_lock);
+
+	/* no lock needed for this */
+	sprintf(buf, "%u\n", reg_val);
+	ret = strlen(buf) + 1;
+
+	return ret;
+}
+
+static ssize_t cpldled_blink_all_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf, size_t size)
+{
+	uint8_t reg_val;
+	char *after;
+	unsigned long state;
+	ssize_t ret = -EINVAL;
+	size_t count;
+	struct CPLD_LED_data *CPLD_LED = dev_get_drvdata(dev);
+
+	state = simple_strtoul(buf, &after, 10);
+
+	count = after - buf;
+
+	if (*after && isspace(*after))
+		count++;
+
+	if (count == size) {
+		ret = count;
+		spin_lock(&CPLD_LED->data_lock);
+		reg_val = readb(_g_cpld_led_addr);
+		if (state)
+			reg_val |= 0x2A;
+		else
+			reg_val &= ~0x2A;
+
+		writeb(reg_val, _g_cpld_led_addr);
+		spin_unlock(&CPLD_LED->data_lock);
+	}
+
+	return ret;
+}
+
+static struct device_attribute dev_attr_blink_all = {
+	.attr = {
+		 .name = "blink",
+		 .mode = 0644,
+		 },
+	.show = cpldled_blink_all_show,
+	.store = cpldled_blink_all_store,
+};
+
+static void led_brightness_set(struct led_classdev *led_cdev,
+			       enum led_brightness brightness)
+{
+	struct CPLD_LED_data *CPLD_LED;
+	int idx = 2;
+	struct led_classdev *led;
+	uint8_t reg_val;
+
+	if (!strcmp(led_cdev->name, "jogball-backlight")) {
+		if (brightness > 7)
+			reg_val = 1;
+		else
+			reg_val = brightness;
+		writeb(0, _g_cpld_led_addr + 0x8);
+		writeb(reg_val, _g_cpld_led_addr + 0x8);
+#if DEBUG_LED_CHANGE
+		printk(KERN_INFO "LED change: jogball backlight = %d \n",
+		       reg_val);
+#endif
+		return;
+	} else if (!strcmp(led_cdev->name, "red")) {
+		idx = 0;
+	} else if (!strcmp(led_cdev->name, "green")) {
+		idx = 1;
+	} else {
+		idx = 2;
+	}
+
+	CPLD_LED = container_of(led_cdev, struct CPLD_LED_data, leds[idx]);
+	spin_lock(&CPLD_LED->data_lock);
+	reg_val = readb(_g_cpld_led_addr);
+	led = &CPLD_LED->leds[idx];
+
+	if (led->brightness > LED_OFF)
+		reg_val |= 1 << (2 * idx);
+	else
+		reg_val &= ~(1 << (2 * idx));
+
+	writeb(reg_val, _g_cpld_led_addr);
+#if DEBUG_LED_CHANGE
+	printk(KERN_INFO "LED change: %s = %d \n", led_cdev->name, led->brightness);
+#endif
+	spin_unlock(&CPLD_LED->data_lock);
+}
+
+static ssize_t cpldled_grpfreq_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%u\n", 0);
+}
+
+static ssize_t cpldled_grpfreq_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	return 0;
+}
+
+static DEVICE_ATTR(grpfreq, 0644, cpldled_grpfreq_show, cpldled_grpfreq_store);
+
+static ssize_t cpldled_grppwm_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%u\n", 0);
+}
+
+static ssize_t cpldled_grppwm_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count)
+{
+	return 0;
+}
+
+static DEVICE_ATTR(grppwm, 0644, cpldled_grppwm_show, cpldled_grppwm_store);
+
+static int CPLD_LED_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	int i, j;
+	struct resource *res;
+	struct CPLD_LED_data *CPLD_LED;
+
+	CPLD_LED = kzalloc(sizeof(struct CPLD_LED_data), GFP_KERNEL);
+	if (CPLD_LED == NULL) {
+		printk(KERN_ERR "CPLD_LED_probe: no memory for device\n");
+		ret = -ENOMEM;
+		goto err_alloc_failed;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		ret = -ENOMEM;
+		goto err_alloc_failed;
+	}
+
+	_g_cpld_led_addr = res->start;
+	if (!_g_cpld_led_addr) {
+		ret = -ENOMEM;
+		goto err_alloc_failed;
+	}
+
+	memset(CPLD_LED, 0, sizeof(struct CPLD_LED_data));
+	writeb(0x00, _g_cpld_led_addr);
+
+	CPLD_LED->leds[0].name = "red";
+	CPLD_LED->leds[0].brightness_set = led_brightness_set;
+
+	CPLD_LED->leds[1].name = "green";
+	CPLD_LED->leds[1].brightness_set = led_brightness_set;
+
+	CPLD_LED->leds[2].name = "blue";
+	CPLD_LED->leds[2].brightness_set = led_brightness_set;
+
+	CPLD_LED->leds[3].name = "jogball-backlight";
+	CPLD_LED->leds[3].brightness_set = led_brightness_set;
+
+	spin_lock_init(&CPLD_LED->data_lock);
+
+	for (i = 0; i < 4; i++) {	/* red, green, blue jogball */
+		ret = led_classdev_register(&pdev->dev, &CPLD_LED->leds[i]);
+		if (ret) {
+			printk(KERN_ERR
+			       "CPLD_LED: led_classdev_register failed\n");
+			goto err_led_classdev_register_failed;
+		}
+	}
+
+	for (i = 0; i < 3; i++) {
+		ret =
+		    device_create_file(CPLD_LED->leds[i].dev, &dev_attr_blink);
+		if (ret) {
+			printk(KERN_ERR
+			       "CPLD_LED: device_create_file failed\n");
+			goto err_out_attr_blink;
+		}
+	}
+
+	dev_set_drvdata(&pdev->dev, CPLD_LED);
+	ret = device_create_file(&pdev->dev, &dev_attr_blink_all);
+	if (ret) {
+		printk(KERN_ERR
+		       "CPLD_LED: create dev_attr_blink_all failed\n");
+		goto err_out_attr_blink;
+	}
+	ret = device_create_file(&pdev->dev, &dev_attr_grppwm);
+	if (ret) {
+		printk(KERN_ERR
+		       "CPLD_LED: create dev_attr_grppwm failed\n");
+		goto err_out_attr_grppwm;
+	}
+	ret = device_create_file(&pdev->dev, &dev_attr_grpfreq);
+	if (ret) {
+		printk(KERN_ERR
+		       "CPLD_LED: create dev_attr_grpfreq failed\n");
+		goto err_out_attr_grpfreq;
+	}
+
+	return 0;
+
+err_out_attr_grpfreq:
+	device_remove_file(&pdev->dev, &dev_attr_grppwm);
+err_out_attr_grppwm:
+	device_remove_file(&pdev->dev, &dev_attr_blink_all);
+err_out_attr_blink:
+	for (j = 0; j < i; j++)
+		device_remove_file(CPLD_LED->leds[j].dev, &dev_attr_blink);
+	i = 3;
+
+err_led_classdev_register_failed:
+	for (j = 0; j < i; j++)
+		led_classdev_unregister(&CPLD_LED->leds[j]);
+
+err_alloc_failed:
+	kfree(CPLD_LED);
+
+	return ret;
+}
+
+static int __devexit CPLD_LED_remove(struct platform_device *pdev)
+{
+	struct CPLD_LED_data *CPLD_LED;
+	int i;
+
+	CPLD_LED = platform_get_drvdata(pdev);
+
+	for (i = 0; i < 3; i++) {
+		device_remove_file(CPLD_LED->leds[i].dev, &dev_attr_blink);
+		led_classdev_unregister(&CPLD_LED->leds[i]);
+	}
+
+	device_remove_file(&pdev->dev, &dev_attr_blink_all);
+	device_remove_file(&pdev->dev, &dev_attr_grppwm);
+	device_remove_file(&pdev->dev, &dev_attr_grpfreq);
+
+	kfree(CPLD_LED);
+	return 0;
+}
+
+static struct platform_driver CPLD_LED_driver = {
+	.probe = CPLD_LED_probe,
+	.remove = __devexit_p(CPLD_LED_remove),
+	.driver = {
+		   .name = "leds-cpld",
+		   .owner = THIS_MODULE,
+		   },
+};
+
+static int __init CPLD_LED_init(void)
+{
+	return platform_driver_register(&CPLD_LED_driver);
+}
+
+static void __exit CPLD_LED_exit(void)
+{
+	platform_driver_unregister(&CPLD_LED_driver);
+}
+
+MODULE_AUTHOR("Farmer Tseng");
+MODULE_DESCRIPTION("CPLD_LED driver");
+MODULE_LICENSE("GPL");
+
+module_init(CPLD_LED_init);
+module_exit(CPLD_LED_exit);
diff --git a/drivers/leds/leds-msm-pdm.c b/drivers/leds/leds-msm-pdm.c
new file mode 100644
index 0000000..467026b
--- /dev/null
+++ b/drivers/leds/leds-msm-pdm.c
@@ -0,0 +1,234 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/module.h>
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+
+/* Early-suspend level */
+#define LED_SUSPEND_LEVEL 1
+#endif
+
+#define PDM_DUTY_MAXVAL BIT(16)
+#define PDM_DUTY_REFVAL BIT(15)
+
+struct pdm_led_data {
+	struct led_classdev cdev;
+	void __iomem *perph_base;
+	int pdm_offset;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	struct early_suspend early_suspend;
+#endif
+};
+
+static void msm_led_brightness_set_percent(struct pdm_led_data *led,
+						int duty_per)
+{
+	u16 duty_val;
+
+	duty_val = PDM_DUTY_REFVAL - ((PDM_DUTY_MAXVAL * duty_per) / 100);
+
+	if (!duty_per)
+		duty_val--;
+
+	writel_relaxed(duty_val, led->perph_base + led->pdm_offset);
+}
+
+static void msm_led_brightness_set(struct led_classdev *led_cdev,
+				enum led_brightness value)
+{
+	struct pdm_led_data *led =
+		container_of(led_cdev, struct pdm_led_data, cdev);
+
+	msm_led_brightness_set_percent(led, (value * 100) / LED_FULL);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int msm_led_pdm_suspend(struct device *dev)
+{
+	struct pdm_led_data *led = dev_get_drvdata(dev);
+
+	msm_led_brightness_set_percent(led, 0);
+
+	return 0;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void msm_led_pdm_early_suspend(struct early_suspend *h)
+{
+	struct pdm_led_data *led = container_of(h,
+			struct pdm_led_data, early_suspend);
+
+	msm_led_pdm_suspend(led->cdev.dev->parent);
+}
+
+#endif
+
+static const struct dev_pm_ops msm_led_pdm_pm_ops = {
+#ifndef CONFIG_HAS_EARLYSUSPEND
+	.suspend	= msm_led_pdm_suspend,
+#endif
+};
+#endif
+
+static int __devinit msm_pdm_led_probe(struct platform_device *pdev)
+{
+	const struct led_info *pdata = pdev->dev.platform_data;
+	struct pdm_led_data *led;
+	struct resource *res, *ioregion;
+	u32 tcxo_pdm_ctl;
+	int rc;
+
+	if (!pdata) {
+		pr_err("platform data is invalid\n");
+		return -EINVAL;
+	}
+
+	if (pdev->id > 2) {
+		pr_err("pdm id is invalid\n");
+		return -EINVAL;
+	}
+
+	led = kzalloc(sizeof(struct pdm_led_data), GFP_KERNEL);
+	if (!led)
+		return -ENOMEM;
+
+	/* Enable runtime PM ops, start in ACTIVE mode */
+	rc = pm_runtime_set_active(&pdev->dev);
+	if (rc < 0)
+		dev_dbg(&pdev->dev, "unable to set runtime pm state\n");
+	pm_runtime_enable(&pdev->dev);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		pr_err("get resource failed\n");
+		rc = -EINVAL;
+		goto err_get_res;
+	}
+
+	ioregion = request_mem_region(res->start, resource_size(res),
+						pdev->name);
+	if (!ioregion) {
+		pr_err("request for mem region failed\n");
+		rc = -ENOMEM;
+		goto err_get_res;
+	}
+
+	led->perph_base = ioremap(res->start, resource_size(res));
+	if (!led->perph_base) {
+		pr_err("ioremap failed\n");
+		rc = -ENOMEM;
+		goto err_ioremap;
+	}
+
+	/* Pulse Density Modulation(PDM) ids start with 0 and
+	 * every PDM register takes 4 bytes
+	 */
+	led->pdm_offset = ((pdev->id) + 1) * 4;
+
+	/* program tcxo_pdm_ctl register to enable pdm*/
+	tcxo_pdm_ctl = readl_relaxed(led->perph_base);
+	tcxo_pdm_ctl |= (1 << pdev->id);
+	writel_relaxed(tcxo_pdm_ctl, led->perph_base);
+
+	/* Start with LED in off state */
+	msm_led_brightness_set_percent(led, 0);
+
+	led->cdev.brightness_set = msm_led_brightness_set;
+	led->cdev.name = pdata->name ? : "leds-msm-pdm";
+
+	rc = led_classdev_register(&pdev->dev, &led->cdev);
+	if (rc) {
+		pr_err("led class registration failed\n");
+		goto err_led_reg;
+	}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	led->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN +
+						LED_SUSPEND_LEVEL;
+	led->early_suspend.suspend = msm_led_pdm_early_suspend;
+	register_early_suspend(&led->early_suspend);
+#endif
+
+	platform_set_drvdata(pdev, led);
+	return 0;
+
+err_led_reg:
+	iounmap(led->perph_base);
+err_ioremap:
+	release_mem_region(res->start, resource_size(res));
+err_get_res:
+	pm_runtime_set_suspended(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+	kfree(led);
+	return rc;
+}
+
+static int __devexit msm_pdm_led_remove(struct platform_device *pdev)
+{
+	struct pdm_led_data *led = platform_get_drvdata(pdev);
+	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	unregister_early_suspend(&led->early_suspend);
+#endif
+	pm_runtime_set_suspended(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+
+	led_classdev_unregister(&led->cdev);
+	msm_led_brightness_set_percent(led, 0);
+	iounmap(led->perph_base);
+	release_mem_region(res->start, resource_size(res));
+	kfree(led);
+
+	return 0;
+}
+
+static struct platform_driver msm_pdm_led_driver = {
+	.probe		= msm_pdm_led_probe,
+	.remove		= __devexit_p(msm_pdm_led_remove),
+	.driver		= {
+		.name	= "leds-msm-pdm",
+		.owner	= THIS_MODULE,
+#ifdef CONFIG_PM_SLEEP
+		.pm	= &msm_led_pdm_pm_ops,
+#endif
+	},
+};
+
+static int __init msm_pdm_led_init(void)
+{
+	return platform_driver_register(&msm_pdm_led_driver);
+}
+module_init(msm_pdm_led_init);
+
+static void __exit msm_pdm_led_exit(void)
+{
+	platform_driver_unregister(&msm_pdm_led_driver);
+}
+module_exit(msm_pdm_led_exit);
+
+MODULE_DESCRIPTION("MSM PDM LEDs driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:leds-msm-pdm");
diff --git a/drivers/leds/leds-msm-pmic.c b/drivers/leds/leds-msm-pmic.c
new file mode 100644
index 0000000..b9c6a53
--- /dev/null
+++ b/drivers/leds/leds-msm-pmic.c
@@ -0,0 +1,105 @@
+/*
+ * leds-msm-pmic.c - MSM PMIC LEDs driver.
+ *
+ * Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+
+#include <mach/pmic.h>
+
+#define MAX_KEYPAD_BL_LEVEL	16
+
+static void msm_keypad_bl_led_set(struct led_classdev *led_cdev,
+	enum led_brightness value)
+{
+	int ret;
+
+	ret = pmic_set_led_intensity(LED_KEYPAD, value / MAX_KEYPAD_BL_LEVEL);
+	if (ret)
+		dev_err(led_cdev->dev, "can't set keypad backlight\n");
+}
+
+static struct led_classdev msm_kp_bl_led = {
+	.name			= "keyboard-backlight",
+	.brightness_set		= msm_keypad_bl_led_set,
+	.brightness		= LED_OFF,
+};
+
+static int msm_pmic_led_probe(struct platform_device *pdev)
+{
+	int rc;
+
+	rc = led_classdev_register(&pdev->dev, &msm_kp_bl_led);
+	if (rc) {
+		dev_err(&pdev->dev, "unable to register led class driver\n");
+		return rc;
+	}
+	msm_keypad_bl_led_set(&msm_kp_bl_led, LED_OFF);
+	return rc;
+}
+
+static int __devexit msm_pmic_led_remove(struct platform_device *pdev)
+{
+	led_classdev_unregister(&msm_kp_bl_led);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int msm_pmic_led_suspend(struct platform_device *dev,
+		pm_message_t state)
+{
+	led_classdev_suspend(&msm_kp_bl_led);
+
+	return 0;
+}
+
+static int msm_pmic_led_resume(struct platform_device *dev)
+{
+	led_classdev_resume(&msm_kp_bl_led);
+
+	return 0;
+}
+#else
+#define msm_pmic_led_suspend NULL
+#define msm_pmic_led_resume NULL
+#endif
+
+static struct platform_driver msm_pmic_led_driver = {
+	.probe		= msm_pmic_led_probe,
+	.remove		= __devexit_p(msm_pmic_led_remove),
+	.suspend	= msm_pmic_led_suspend,
+	.resume		= msm_pmic_led_resume,
+	.driver		= {
+		.name	= "pmic-leds",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init msm_pmic_led_init(void)
+{
+	return platform_driver_register(&msm_pmic_led_driver);
+}
+module_init(msm_pmic_led_init);
+
+static void __exit msm_pmic_led_exit(void)
+{
+	platform_driver_unregister(&msm_pmic_led_driver);
+}
+module_exit(msm_pmic_led_exit);
+
+MODULE_DESCRIPTION("MSM PMIC LEDs driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:pmic-leds");
diff --git a/drivers/leds/leds-pm8xxx.c b/drivers/leds/leds-pm8xxx.c
new file mode 100644
index 0000000..3261037
--- /dev/null
+++ b/drivers/leds/leds-pm8xxx.c
@@ -0,0 +1,969 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt)	"%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/workqueue.h>
+#include <linux/err.h>
+
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/pwm.h>
+#include <linux/leds-pm8xxx.h>
+
+#define SSBI_REG_ADDR_DRV_KEYPAD	0x48
+#define PM8XXX_DRV_KEYPAD_BL_MASK	0xf0
+#define PM8XXX_DRV_KEYPAD_BL_SHIFT	0x04
+
+#define SSBI_REG_ADDR_FLASH_DRV0        0x49
+#define PM8XXX_DRV_FLASH_MASK           0xf0
+#define PM8XXX_DRV_FLASH_SHIFT          0x04
+
+#define SSBI_REG_ADDR_FLASH_DRV1        0xFB
+
+#define SSBI_REG_ADDR_LED_CTRL_BASE	0x131
+#define SSBI_REG_ADDR_LED_CTRL(n)	(SSBI_REG_ADDR_LED_CTRL_BASE + (n))
+#define PM8XXX_DRV_LED_CTRL_MASK	0xf8
+#define PM8XXX_DRV_LED_CTRL_SHIFT	0x03
+
+#define SSBI_REG_ADDR_WLED_CTRL_BASE	0x25A
+#define SSBI_REG_ADDR_WLED_CTRL(n)	(SSBI_REG_ADDR_WLED_CTRL_BASE + (n) - 1)
+
+/* wled control registers */
+#define WLED_MOD_CTRL_REG		SSBI_REG_ADDR_WLED_CTRL(1)
+#define WLED_MAX_CURR_CFG_REG(n)	SSBI_REG_ADDR_WLED_CTRL(n + 2)
+#define WLED_BRIGHTNESS_CNTL_REG1(n)	SSBI_REG_ADDR_WLED_CTRL(n + 5)
+#define WLED_BRIGHTNESS_CNTL_REG2(n)	SSBI_REG_ADDR_WLED_CTRL(n + 6)
+#define WLED_SYNC_REG			SSBI_REG_ADDR_WLED_CTRL(11)
+#define WLED_OVP_CFG_REG		SSBI_REG_ADDR_WLED_CTRL(13)
+#define WLED_BOOST_CFG_REG		SSBI_REG_ADDR_WLED_CTRL(14)
+#define WLED_HIGH_POLE_CAP_REG		SSBI_REG_ADDR_WLED_CTRL(16)
+
+#define WLED_STRINGS			0x03
+#define WLED_OVP_VAL_MASK		0x30
+#define WLED_OVP_VAL_BIT_SHFT		0x04
+#define WLED_BOOST_LIMIT_MASK		0xE0
+#define WLED_BOOST_LIMIT_BIT_SHFT	0x05
+#define WLED_BOOST_OFF			0x00
+#define WLED_EN_MASK			0x01
+#define WLED_CP_SELECT_MAX		0x03
+#define WLED_CP_SELECT_MASK		0x03
+#define WLED_DIG_MOD_GEN_MASK		0x70
+#define WLED_CS_OUT_MASK		0x0E
+#define WLED_CTL_DLY_STEP		200
+#define WLED_CTL_DLY_MAX		1400
+#define WLED_CTL_DLY_MASK		0xE0
+#define WLED_CTL_DLY_BIT_SHFT		0x05
+#define WLED_MAX_CURR			25
+#define WLED_MAX_CURR_MASK		0x1F
+#define WLED_OP_FDBCK_MASK		0x1C
+#define WLED_OP_FDBCK_BIT_SHFT		0x02
+
+#define WLED_MAX_LEVEL			255
+#define WLED_8_BIT_MASK			0xFF
+#define WLED_8_BIT_SHFT			0x08
+#define WLED_MAX_DUTY_CYCLE		0xFFF
+
+#define WLED_SYNC_VAL			0x07
+#define WLED_SYNC_RESET_VAL		0x00
+
+#define SSBI_REG_ADDR_RGB_CNTL1		0x12D
+#define SSBI_REG_ADDR_RGB_CNTL2		0x12E
+
+#define PM8XXX_DRV_RGB_RED_LED		BIT(2)
+#define PM8XXX_DRV_RGB_GREEN_LED	BIT(1)
+#define PM8XXX_DRV_RGB_BLUE_LED		BIT(0)
+
+#define MAX_FLASH_LED_CURRENT		300
+#define MAX_LC_LED_CURRENT		40
+#define MAX_KP_BL_LED_CURRENT		300
+
+#define PM8XXX_ID_LED_CURRENT_FACTOR	2  /* Iout = x * 2mA */
+#define PM8XXX_ID_FLASH_CURRENT_FACTOR	20 /* Iout = x * 20mA */
+
+#define PM8XXX_FLASH_MODE_DBUS1		1
+#define PM8XXX_FLASH_MODE_DBUS2		2
+#define PM8XXX_FLASH_MODE_PWM		3
+
+#define MAX_LC_LED_BRIGHTNESS		20
+#define MAX_FLASH_BRIGHTNESS		15
+#define MAX_KB_LED_BRIGHTNESS		15
+
+#define PM8XXX_LED_OFFSET(id) ((id) - PM8XXX_ID_LED_0)
+
+#define PM8XXX_LED_PWM_FLAGS	(PM_PWM_LUT_LOOP | PM_PWM_LUT_RAMP_UP)
+
+#define LED_MAP(_version, _kb, _led0, _led1, _led2, _flash_led0, _flash_led1, \
+	_wled, _rgb_led_red, _rgb_led_green, _rgb_led_blue)\
+	{\
+		.version = _version,\
+		.supported = _kb << PM8XXX_ID_LED_KB_LIGHT | \
+			_led0 << PM8XXX_ID_LED_0 | _led1 << PM8XXX_ID_LED_1 | \
+			_led2 << PM8XXX_ID_LED_2  | \
+			_flash_led0 << PM8XXX_ID_FLASH_LED_0 | \
+			_flash_led1 << PM8XXX_ID_FLASH_LED_1 | \
+			_wled << PM8XXX_ID_WLED | \
+			_rgb_led_red << PM8XXX_ID_RGB_LED_RED | \
+			_rgb_led_green << PM8XXX_ID_RGB_LED_GREEN | \
+			_rgb_led_blue << PM8XXX_ID_RGB_LED_BLUE, \
+	}
+
+/**
+ * supported_leds - leds supported for each PMIC version
+ * @version - version of PMIC
+ * @supported - which leds are supported on version
+ */
+
+struct supported_leds {
+	enum pm8xxx_version version;
+	u32 supported;
+};
+
+static const struct supported_leds led_map[] = {
+	LED_MAP(PM8XXX_VERSION_8058, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0),
+	LED_MAP(PM8XXX_VERSION_8921, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0),
+	LED_MAP(PM8XXX_VERSION_8018, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0),
+	LED_MAP(PM8XXX_VERSION_8922, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1),
+	LED_MAP(PM8XXX_VERSION_8038, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1),
+};
+
+/**
+ * struct pm8xxx_led_data - internal led data structure
+ * @led_classdev - led class device
+ * @id - led index
+ * @work - workqueue for led
+ * @lock - to protect the transactions
+ * @reg - cached value of led register
+ * @pwm_dev - pointer to PWM device if LED is driven using PWM
+ * @pwm_channel - PWM channel ID
+ * @pwm_period_us - PWM period in micro seconds
+ * @pwm_duty_cycles - struct that describes PWM duty cycles info
+ */
+struct pm8xxx_led_data {
+	struct led_classdev	cdev;
+	int			id;
+	u8			reg;
+	u8			wled_mod_ctrl_val;
+	struct device		*dev;
+	struct work_struct	work;
+	struct mutex		lock;
+	struct pwm_device	*pwm_dev;
+	int			pwm_channel;
+	u32			pwm_period_us;
+	struct pm8xxx_pwm_duty_cycles *pwm_duty_cycles;
+	struct wled_config_data *wled_cfg;
+	int			max_current;
+};
+
+static void led_kp_set(struct pm8xxx_led_data *led, enum led_brightness value)
+{
+	int rc;
+	u8 level;
+
+	level = (value << PM8XXX_DRV_KEYPAD_BL_SHIFT) &
+				 PM8XXX_DRV_KEYPAD_BL_MASK;
+
+	led->reg &= ~PM8XXX_DRV_KEYPAD_BL_MASK;
+	led->reg |= level;
+
+	rc = pm8xxx_writeb(led->dev->parent, SSBI_REG_ADDR_DRV_KEYPAD,
+								led->reg);
+	if (rc < 0)
+		dev_err(led->cdev.dev,
+			"can't set keypad backlight level rc=%d\n", rc);
+}
+
+static void led_lc_set(struct pm8xxx_led_data *led, enum led_brightness value)
+{
+	int rc, offset;
+	u8 level;
+
+	level = (value << PM8XXX_DRV_LED_CTRL_SHIFT) &
+				PM8XXX_DRV_LED_CTRL_MASK;
+
+	offset = PM8XXX_LED_OFFSET(led->id);
+
+	led->reg &= ~PM8XXX_DRV_LED_CTRL_MASK;
+	led->reg |= level;
+
+	rc = pm8xxx_writeb(led->dev->parent, SSBI_REG_ADDR_LED_CTRL(offset),
+								led->reg);
+	if (rc)
+		dev_err(led->cdev.dev, "can't set (%d) led value rc=%d\n",
+				led->id, rc);
+}
+
+static void
+led_flash_set(struct pm8xxx_led_data *led, enum led_brightness value)
+{
+	int rc;
+	u8 level;
+	u16 reg_addr;
+
+	level = (value << PM8XXX_DRV_FLASH_SHIFT) &
+				 PM8XXX_DRV_FLASH_MASK;
+
+	led->reg &= ~PM8XXX_DRV_FLASH_MASK;
+	led->reg |= level;
+
+	if (led->id == PM8XXX_ID_FLASH_LED_0)
+		reg_addr = SSBI_REG_ADDR_FLASH_DRV0;
+	else
+		reg_addr = SSBI_REG_ADDR_FLASH_DRV1;
+
+	rc = pm8xxx_writeb(led->dev->parent, reg_addr, led->reg);
+	if (rc < 0)
+		dev_err(led->cdev.dev, "can't set flash led%d level rc=%d\n",
+			 led->id, rc);
+}
+
+static int
+led_wled_set(struct pm8xxx_led_data *led, enum led_brightness value)
+{
+	int rc, duty;
+	u8 val, i, num_wled_strings;
+
+	if (value > WLED_MAX_LEVEL)
+		value = WLED_MAX_LEVEL;
+
+	if (value == 0) {
+		rc = pm8xxx_writeb(led->dev->parent, WLED_MOD_CTRL_REG,
+				WLED_BOOST_OFF);
+		if (rc) {
+			dev_err(led->dev->parent, "can't write wled ctrl config"
+				" register rc=%d\n", rc);
+			return rc;
+		}
+	} else {
+		rc = pm8xxx_writeb(led->dev->parent, WLED_MOD_CTRL_REG,
+				led->wled_mod_ctrl_val);
+		if (rc) {
+			dev_err(led->dev->parent, "can't write wled ctrl config"
+				" register rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	duty = (WLED_MAX_DUTY_CYCLE * value) / WLED_MAX_LEVEL;
+
+	num_wled_strings = led->wled_cfg->num_strings;
+
+	/* program brightness control registers */
+	for (i = 0; i < num_wled_strings; i++) {
+		rc = pm8xxx_readb(led->dev->parent,
+				WLED_BRIGHTNESS_CNTL_REG1(i), &val);
+		if (rc) {
+			dev_err(led->dev->parent, "can't read wled brightnes ctrl"
+				" register1 rc=%d\n", rc);
+			return rc;
+		}
+
+		val = (val & ~WLED_MAX_CURR_MASK) | (duty >> WLED_8_BIT_SHFT);
+		rc = pm8xxx_writeb(led->dev->parent,
+				WLED_BRIGHTNESS_CNTL_REG1(i), val);
+		if (rc) {
+			dev_err(led->dev->parent, "can't write wled brightness ctrl"
+				" register1 rc=%d\n", rc);
+			return rc;
+		}
+
+		val = duty & WLED_8_BIT_MASK;
+		rc = pm8xxx_writeb(led->dev->parent,
+				WLED_BRIGHTNESS_CNTL_REG2(i), val);
+		if (rc) {
+			dev_err(led->dev->parent, "can't write wled brightness ctrl"
+				" register2 rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	/* sync */
+	val = WLED_SYNC_VAL;
+	rc = pm8xxx_writeb(led->dev->parent, WLED_SYNC_REG, val);
+	if (rc) {
+		dev_err(led->dev->parent,
+			"can't read wled sync register rc=%d\n", rc);
+		return rc;
+	}
+
+	val = WLED_SYNC_RESET_VAL;
+	rc = pm8xxx_writeb(led->dev->parent, WLED_SYNC_REG, val);
+	if (rc) {
+		dev_err(led->dev->parent,
+			"can't read wled sync register rc=%d\n", rc);
+		return rc;
+	}
+	return 0;
+}
+
+static void wled_dump_regs(struct pm8xxx_led_data *led)
+{
+	int i;
+	u8 val;
+
+	for (i = 1; i < 17; i++) {
+		pm8xxx_readb(led->dev->parent,
+				SSBI_REG_ADDR_WLED_CTRL(i), &val);
+		pr_debug("WLED_CTRL_%d = 0x%x\n", i, val);
+	}
+}
+
+static void
+led_rgb_set(struct pm8xxx_led_data *led, enum led_brightness value)
+{
+	int rc;
+	u8 val, mask;
+
+	rc = pm8xxx_readb(led->dev->parent, SSBI_REG_ADDR_RGB_CNTL2, &val);
+	if (rc) {
+		dev_err(led->cdev.dev, "can't read rgb ctrl register rc=%d\n",
+							rc);
+		return;
+	}
+
+	switch (led->id) {
+	case PM8XXX_ID_RGB_LED_RED:
+		mask = PM8XXX_DRV_RGB_RED_LED;
+		break;
+	case PM8XXX_ID_RGB_LED_GREEN:
+		mask = PM8XXX_DRV_RGB_GREEN_LED;
+		break;
+	case PM8XXX_ID_RGB_LED_BLUE:
+		mask = PM8XXX_DRV_RGB_BLUE_LED;
+		break;
+	default:
+		return;
+	}
+
+	if (value)
+		val |= mask;
+	else
+		val &= ~mask;
+
+	rc = pm8xxx_writeb(led->dev->parent, SSBI_REG_ADDR_RGB_CNTL2, val);
+	if (rc < 0)
+		dev_err(led->cdev.dev, "can't set rgb led %d level rc=%d\n",
+			 led->id, rc);
+}
+
+static int pm8xxx_led_pwm_work(struct pm8xxx_led_data *led)
+{
+	int duty_us;
+	int rc = 0;
+
+	if (led->pwm_duty_cycles == NULL) {
+		duty_us = (led->pwm_period_us * led->cdev.brightness) /
+								LED_FULL;
+		rc = pwm_config(led->pwm_dev, duty_us, led->pwm_period_us);
+		if (led->cdev.brightness)
+			rc = pwm_enable(led->pwm_dev);
+		else
+			pwm_disable(led->pwm_dev);
+	} else {
+		rc = pm8xxx_pwm_lut_enable(led->pwm_dev, led->cdev.brightness);
+	}
+
+	return rc;
+}
+
+static void __pm8xxx_led_work(struct pm8xxx_led_data *led,
+					enum led_brightness level)
+{
+	int rc;
+
+	mutex_lock(&led->lock);
+
+	switch (led->id) {
+	case PM8XXX_ID_LED_KB_LIGHT:
+		led_kp_set(led, level);
+		break;
+	case PM8XXX_ID_LED_0:
+	case PM8XXX_ID_LED_1:
+	case PM8XXX_ID_LED_2:
+		led_lc_set(led, level);
+		break;
+	case PM8XXX_ID_FLASH_LED_0:
+	case PM8XXX_ID_FLASH_LED_1:
+		led_flash_set(led, level);
+		break;
+	case PM8XXX_ID_WLED:
+		rc = led_wled_set(led, level);
+		if (rc < 0)
+			pr_err("wled brightness set failed %d\n", rc);
+		break;
+	case PM8XXX_ID_RGB_LED_RED:
+	case PM8XXX_ID_RGB_LED_GREEN:
+	case PM8XXX_ID_RGB_LED_BLUE:
+		led_rgb_set(led, level);
+		break;
+	default:
+		dev_err(led->cdev.dev, "unknown led id %d", led->id);
+		break;
+	}
+
+	mutex_unlock(&led->lock);
+}
+
+static void pm8xxx_led_work(struct work_struct *work)
+{
+	int rc;
+
+	struct pm8xxx_led_data *led = container_of(work,
+					 struct pm8xxx_led_data, work);
+
+	if (led->pwm_dev == NULL) {
+		__pm8xxx_led_work(led, led->cdev.brightness);
+	} else {
+		rc = pm8xxx_led_pwm_work(led);
+		if (rc)
+			pr_err("could not configure PWM mode for LED:%d\n",
+								led->id);
+	}
+}
+
+static void pm8xxx_led_set(struct led_classdev *led_cdev,
+	enum led_brightness value)
+{
+	struct	pm8xxx_led_data *led;
+
+	led = container_of(led_cdev, struct pm8xxx_led_data, cdev);
+
+	if (value < LED_OFF || value > led->cdev.max_brightness) {
+		dev_err(led->cdev.dev, "Invalid brightness value exceeds");
+		return;
+	}
+
+	led->cdev.brightness = value;
+	schedule_work(&led->work);
+}
+
+static int pm8xxx_set_led_mode_and_max_brightness(struct pm8xxx_led_data *led,
+		enum pm8xxx_led_modes led_mode, int max_current)
+{
+	switch (led->id) {
+	case PM8XXX_ID_LED_0:
+	case PM8XXX_ID_LED_1:
+	case PM8XXX_ID_LED_2:
+		led->cdev.max_brightness = max_current /
+						PM8XXX_ID_LED_CURRENT_FACTOR;
+		if (led->cdev.max_brightness > MAX_LC_LED_BRIGHTNESS)
+			led->cdev.max_brightness = MAX_LC_LED_BRIGHTNESS;
+		led->reg = led_mode;
+		break;
+	case PM8XXX_ID_LED_KB_LIGHT:
+	case PM8XXX_ID_FLASH_LED_0:
+	case PM8XXX_ID_FLASH_LED_1:
+		led->cdev.max_brightness = max_current /
+						PM8XXX_ID_FLASH_CURRENT_FACTOR;
+		if (led->cdev.max_brightness > MAX_FLASH_BRIGHTNESS)
+			led->cdev.max_brightness = MAX_FLASH_BRIGHTNESS;
+
+		switch (led_mode) {
+		case PM8XXX_LED_MODE_PWM1:
+		case PM8XXX_LED_MODE_PWM2:
+		case PM8XXX_LED_MODE_PWM3:
+			led->reg = PM8XXX_FLASH_MODE_PWM;
+			break;
+		case PM8XXX_LED_MODE_DTEST1:
+			led->reg = PM8XXX_FLASH_MODE_DBUS1;
+			break;
+		case PM8XXX_LED_MODE_DTEST2:
+			led->reg = PM8XXX_FLASH_MODE_DBUS2;
+			break;
+		default:
+			led->reg = PM8XXX_LED_MODE_MANUAL;
+			break;
+		}
+		break;
+	case PM8XXX_ID_WLED:
+		led->cdev.max_brightness = WLED_MAX_LEVEL;
+		break;
+	case PM8XXX_ID_RGB_LED_RED:
+	case PM8XXX_ID_RGB_LED_GREEN:
+	case PM8XXX_ID_RGB_LED_BLUE:
+		led->cdev.max_brightness = LED_FULL;
+		break;
+	default:
+		dev_err(led->cdev.dev, "LED Id is invalid");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static enum led_brightness pm8xxx_led_get(struct led_classdev *led_cdev)
+{
+	struct pm8xxx_led_data *led;
+
+	led = container_of(led_cdev, struct pm8xxx_led_data, cdev);
+
+	return led->cdev.brightness;
+}
+
+static int __devinit init_wled(struct pm8xxx_led_data *led)
+{
+	int rc, i;
+	u8 val, num_wled_strings;
+
+	num_wled_strings = led->wled_cfg->num_strings;
+
+	/* program over voltage protection threshold */
+	if (led->wled_cfg->ovp_val > WLED_OVP_37V) {
+		dev_err(led->dev->parent, "Invalid ovp value");
+		return -EINVAL;
+	}
+
+	rc = pm8xxx_readb(led->dev->parent, WLED_OVP_CFG_REG, &val);
+	if (rc) {
+		dev_err(led->dev->parent, "can't read wled ovp config"
+			" register rc=%d\n", rc);
+		return rc;
+	}
+
+	val = (val & ~WLED_OVP_VAL_MASK) |
+		(led->wled_cfg->ovp_val << WLED_OVP_VAL_BIT_SHFT);
+
+	rc = pm8xxx_writeb(led->dev->parent, WLED_OVP_CFG_REG, val);
+	if (rc) {
+		dev_err(led->dev->parent, "can't write wled ovp config"
+			" register rc=%d\n", rc);
+		return rc;
+	}
+
+	/* program current boost limit and output feedback*/
+	if (led->wled_cfg->boost_curr_lim > WLED_CURR_LIMIT_1680mA) {
+		dev_err(led->dev->parent, "Invalid boost current limit");
+		return -EINVAL;
+	}
+
+	rc = pm8xxx_readb(led->dev->parent, WLED_BOOST_CFG_REG, &val);
+	if (rc) {
+		dev_err(led->dev->parent, "can't read wled boost config"
+			" register rc=%d\n", rc);
+		return rc;
+	}
+
+	val = (val & ~WLED_BOOST_LIMIT_MASK) |
+		(led->wled_cfg->boost_curr_lim << WLED_BOOST_LIMIT_BIT_SHFT);
+
+	val = (val & ~WLED_OP_FDBCK_MASK) |
+		(led->wled_cfg->op_fdbck << WLED_OP_FDBCK_BIT_SHFT);
+
+	rc = pm8xxx_writeb(led->dev->parent, WLED_BOOST_CFG_REG, val);
+	if (rc) {
+		dev_err(led->dev->parent, "can't write wled boost config"
+			" register rc=%d\n", rc);
+		return rc;
+	}
+
+	/* program high pole capacitance */
+	if (led->wled_cfg->cp_select > WLED_CP_SELECT_MAX) {
+		dev_err(led->dev->parent, "Invalid pole capacitance");
+		return -EINVAL;
+	}
+
+	rc = pm8xxx_readb(led->dev->parent, WLED_HIGH_POLE_CAP_REG, &val);
+	if (rc) {
+		dev_err(led->dev->parent, "can't read wled high pole"
+			" capacitance register rc=%d\n", rc);
+		return rc;
+	}
+
+	val = (val & ~WLED_CP_SELECT_MASK) | led->wled_cfg->cp_select;
+
+	rc = pm8xxx_writeb(led->dev->parent, WLED_HIGH_POLE_CAP_REG, val);
+	if (rc) {
+		dev_err(led->dev->parent, "can't write wled high pole"
+			" capacitance register rc=%d\n", rc);
+		return rc;
+	}
+
+	/* program activation delay and maximum current */
+	for (i = 0; i < num_wled_strings; i++) {
+		rc = pm8xxx_readb(led->dev->parent,
+				WLED_MAX_CURR_CFG_REG(i + 2), &val);
+		if (rc) {
+			dev_err(led->dev->parent, "can't read wled max current"
+				" config register rc=%d\n", rc);
+			return rc;
+		}
+
+		if ((led->wled_cfg->ctrl_delay_us % WLED_CTL_DLY_STEP) ||
+			(led->wled_cfg->ctrl_delay_us > WLED_CTL_DLY_MAX)) {
+			dev_err(led->dev->parent, "Invalid control delay\n");
+			return rc;
+		}
+
+		val = val / WLED_CTL_DLY_STEP;
+		val = (val & ~WLED_CTL_DLY_MASK) |
+			(led->wled_cfg->ctrl_delay_us << WLED_CTL_DLY_BIT_SHFT);
+
+		if ((led->max_current > WLED_MAX_CURR)) {
+			dev_err(led->dev->parent, "Invalid max current\n");
+			return -EINVAL;
+		}
+
+		val = (val & ~WLED_MAX_CURR_MASK) | led->max_current;
+
+		rc = pm8xxx_writeb(led->dev->parent,
+				WLED_MAX_CURR_CFG_REG(i + 2), val);
+		if (rc) {
+			dev_err(led->dev->parent, "can't write wled max current"
+				" config register rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	/* program digital module generator, cs out and enable the module */
+	rc = pm8xxx_readb(led->dev->parent, WLED_MOD_CTRL_REG, &val);
+	if (rc) {
+		dev_err(led->dev->parent, "can't read wled module ctrl"
+			" register rc=%d\n", rc);
+		return rc;
+	}
+
+	if (led->wled_cfg->dig_mod_gen_en)
+		val |= WLED_DIG_MOD_GEN_MASK;
+
+	if (led->wled_cfg->cs_out_en)
+		val |= WLED_CS_OUT_MASK;
+
+	val |= WLED_EN_MASK;
+
+	rc = pm8xxx_writeb(led->dev->parent, WLED_MOD_CTRL_REG, val);
+	if (rc) {
+		dev_err(led->dev->parent, "can't write wled module ctrl"
+			" register rc=%d\n", rc);
+		return rc;
+	}
+	led->wled_mod_ctrl_val = val;
+
+	/* dump wled registers */
+	wled_dump_regs(led);
+
+	return 0;
+}
+
+static int __devinit init_rgb_led(struct pm8xxx_led_data *led)
+{
+	int rc;
+	u8 val;
+
+	rc = pm8xxx_readb(led->dev->parent, SSBI_REG_ADDR_RGB_CNTL1, &val);
+	if (rc) {
+		dev_err(led->cdev.dev, "can't read rgb ctrl1 register rc=%d\n",
+							rc);
+		return rc;
+	}
+
+	switch (led->id) {
+	case PM8XXX_ID_RGB_LED_RED:
+		val |= PM8XXX_DRV_RGB_RED_LED;
+		break;
+	case PM8XXX_ID_RGB_LED_GREEN:
+		val |= PM8XXX_DRV_RGB_GREEN_LED;
+		break;
+	case PM8XXX_ID_RGB_LED_BLUE:
+		val |= PM8XXX_DRV_RGB_BLUE_LED;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return pm8xxx_writeb(led->dev->parent, SSBI_REG_ADDR_RGB_CNTL1, val);
+}
+
+static int __devinit get_init_value(struct pm8xxx_led_data *led, u8 *val)
+{
+	int rc, offset;
+	u16 addr;
+
+	switch (led->id) {
+	case PM8XXX_ID_LED_KB_LIGHT:
+		addr = SSBI_REG_ADDR_DRV_KEYPAD;
+		break;
+	case PM8XXX_ID_LED_0:
+	case PM8XXX_ID_LED_1:
+	case PM8XXX_ID_LED_2:
+		offset = PM8XXX_LED_OFFSET(led->id);
+		addr = SSBI_REG_ADDR_LED_CTRL(offset);
+		break;
+	case PM8XXX_ID_FLASH_LED_0:
+		addr = SSBI_REG_ADDR_FLASH_DRV0;
+		break;
+	case PM8XXX_ID_FLASH_LED_1:
+		addr = SSBI_REG_ADDR_FLASH_DRV1;
+		break;
+	case PM8XXX_ID_WLED:
+		rc = init_wled(led);
+		if (rc)
+			dev_err(led->cdev.dev, "can't initialize wled rc=%d\n",
+								rc);
+		return rc;
+	case PM8XXX_ID_RGB_LED_RED:
+	case PM8XXX_ID_RGB_LED_GREEN:
+	case PM8XXX_ID_RGB_LED_BLUE:
+		rc = init_rgb_led(led);
+		if (rc) {
+			dev_err(led->cdev.dev, "can't initialize rgb rc=%d\n",
+								rc);
+			return rc;
+		}
+		addr = SSBI_REG_ADDR_RGB_CNTL1;
+		break;
+	default:
+		dev_err(led->cdev.dev, "unknown led id %d", led->id);
+		return -EINVAL;
+	}
+
+	rc = pm8xxx_readb(led->dev->parent, addr, val);
+	if (rc)
+		dev_err(led->cdev.dev, "can't get led(%d) level rc=%d\n",
+							led->id, rc);
+
+	return rc;
+}
+
+static int pm8xxx_led_pwm_configure(struct pm8xxx_led_data *led)
+{
+	int start_idx, idx_len, duty_us, rc;
+
+	led->pwm_dev = pwm_request(led->pwm_channel,
+					led->cdev.name);
+
+	if (IS_ERR_OR_NULL(led->pwm_dev)) {
+		pr_err("could not acquire PWM Channel %d, "
+			"error %ld\n", led->pwm_channel,
+			PTR_ERR(led->pwm_dev));
+		led->pwm_dev = NULL;
+		return -ENODEV;
+	}
+
+	if (led->pwm_duty_cycles != NULL) {
+		start_idx = led->pwm_duty_cycles->start_idx;
+		idx_len = led->pwm_duty_cycles->num_duty_pcts;
+
+		if (idx_len >= PM_PWM_LUT_SIZE && start_idx) {
+			pr_err("Wrong LUT size or index\n");
+			return -EINVAL;
+		}
+		if ((start_idx + idx_len) > PM_PWM_LUT_SIZE) {
+			pr_err("Exceed LUT limit\n");
+			return -EINVAL;
+		}
+
+		rc = pm8xxx_pwm_lut_config(led->pwm_dev, led->pwm_period_us,
+				led->pwm_duty_cycles->duty_pcts,
+				led->pwm_duty_cycles->duty_ms,
+				start_idx, idx_len, 0, 0,
+				PM8XXX_LED_PWM_FLAGS);
+	} else {
+		duty_us = led->pwm_period_us;
+		rc = pwm_config(led->pwm_dev, duty_us, led->pwm_period_us);
+	}
+
+	return rc;
+}
+
+
+static int __devinit pm8xxx_led_probe(struct platform_device *pdev)
+{
+	const struct pm8xxx_led_platform_data *pdata = pdev->dev.platform_data;
+	const struct led_platform_data *pcore_data;
+	struct led_info *curr_led;
+	struct pm8xxx_led_data *led, *led_dat;
+	struct pm8xxx_led_config *led_cfg;
+	enum pm8xxx_version version;
+	bool found = false;
+	int rc, i, j;
+
+	if (pdata == NULL) {
+		dev_err(&pdev->dev, "platform data not supplied\n");
+		return -EINVAL;
+	}
+
+	pcore_data = pdata->led_core;
+
+	if (pcore_data->num_leds != pdata->num_configs) {
+		dev_err(&pdev->dev, "#no. of led configs and #no. of led"
+				"entries are not equal\n");
+		return -EINVAL;
+	}
+
+	led = kcalloc(pcore_data->num_leds, sizeof(*led), GFP_KERNEL);
+	if (led == NULL) {
+		dev_err(&pdev->dev, "failed to alloc memory\n");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < pcore_data->num_leds; i++) {
+		curr_led	= &pcore_data->leds[i];
+		led_dat		= &led[i];
+		led_cfg		= &pdata->configs[i];
+
+		led_dat->id     = led_cfg->id;
+		led_dat->pwm_channel = led_cfg->pwm_channel;
+		led_dat->pwm_period_us = led_cfg->pwm_period_us;
+		led_dat->pwm_duty_cycles = led_cfg->pwm_duty_cycles;
+		led_dat->wled_cfg = led_cfg->wled_cfg;
+		led_dat->max_current = led_cfg->max_current;
+
+		if (!((led_dat->id >= PM8XXX_ID_LED_KB_LIGHT) &&
+				(led_dat->id < PM8XXX_ID_MAX))) {
+			dev_err(&pdev->dev, "invalid LED ID(%d) specified\n",
+				led_dat->id);
+			rc = -EINVAL;
+			goto fail_id_check;
+
+		}
+
+		found = false;
+		version = pm8xxx_get_version(pdev->dev.parent);
+		for (j = 0; j < ARRAY_SIZE(led_map); j++) {
+			if (version == led_map[j].version
+			&& (led_map[j].supported & (1 << led_dat->id))) {
+				found = true;
+				break;
+			}
+		}
+
+		if (!found) {
+			dev_err(&pdev->dev, "invalid LED ID(%d) specified\n",
+				led_dat->id);
+			rc = -EINVAL;
+			goto fail_id_check;
+		}
+
+		led_dat->cdev.name		= curr_led->name;
+		led_dat->cdev.default_trigger   = curr_led->default_trigger;
+		led_dat->cdev.brightness_set    = pm8xxx_led_set;
+		led_dat->cdev.brightness_get    = pm8xxx_led_get;
+		led_dat->cdev.brightness	= LED_OFF;
+		led_dat->cdev.flags		= curr_led->flags;
+		led_dat->dev			= &pdev->dev;
+
+		rc =  get_init_value(led_dat, &led_dat->reg);
+		if (rc < 0)
+			goto fail_id_check;
+
+		rc = pm8xxx_set_led_mode_and_max_brightness(led_dat,
+					led_cfg->mode, led_cfg->max_current);
+		if (rc < 0)
+			goto fail_id_check;
+
+		mutex_init(&led_dat->lock);
+		INIT_WORK(&led_dat->work, pm8xxx_led_work);
+
+		rc = led_classdev_register(&pdev->dev, &led_dat->cdev);
+		if (rc) {
+			dev_err(&pdev->dev, "unable to register led %d,rc=%d\n",
+						 led_dat->id, rc);
+			goto fail_id_check;
+		}
+
+		/* configure default state */
+		if (led_cfg->default_state)
+			led->cdev.brightness = led_dat->cdev.max_brightness;
+		else
+			led->cdev.brightness = LED_OFF;
+
+		if (led_cfg->mode != PM8XXX_LED_MODE_MANUAL) {
+			if (led_dat->id == PM8XXX_ID_RGB_LED_RED ||
+				led_dat->id == PM8XXX_ID_RGB_LED_GREEN ||
+				led_dat->id == PM8XXX_ID_RGB_LED_BLUE)
+				__pm8xxx_led_work(led_dat, 0);
+			else
+				__pm8xxx_led_work(led_dat,
+					led_dat->cdev.max_brightness);
+
+			if (led_dat->pwm_channel != -1) {
+				led_dat->cdev.max_brightness = LED_FULL;
+				rc = pm8xxx_led_pwm_configure(led_dat);
+				if (rc) {
+					dev_err(&pdev->dev, "failed to "
+					"configure LED, error: %d\n", rc);
+					goto fail_id_check;
+				}
+			schedule_work(&led->work);
+			}
+		} else {
+			__pm8xxx_led_work(led_dat, led->cdev.brightness);
+		}
+	}
+
+	platform_set_drvdata(pdev, led);
+
+	return 0;
+
+fail_id_check:
+	if (i > 0) {
+		for (i = i - 1; i >= 0; i--) {
+			mutex_destroy(&led[i].lock);
+			led_classdev_unregister(&led[i].cdev);
+			if (led[i].pwm_dev != NULL)
+				pwm_free(led[i].pwm_dev);
+		}
+	}
+	kfree(led);
+	return rc;
+}
+
+static int __devexit pm8xxx_led_remove(struct platform_device *pdev)
+{
+	int i;
+	const struct led_platform_data *pdata =
+				pdev->dev.platform_data;
+	struct pm8xxx_led_data *led = platform_get_drvdata(pdev);
+
+	for (i = 0; i < pdata->num_leds; i++) {
+		cancel_work_sync(&led[i].work);
+		mutex_destroy(&led[i].lock);
+		led_classdev_unregister(&led[i].cdev);
+		if (led[i].pwm_dev != NULL)
+			pwm_free(led[i].pwm_dev);
+	}
+
+	kfree(led);
+
+	return 0;
+}
+
+static struct platform_driver pm8xxx_led_driver = {
+	.probe		= pm8xxx_led_probe,
+	.remove		= __devexit_p(pm8xxx_led_remove),
+	.driver		= {
+		.name	= PM8XXX_LEDS_DEV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init pm8xxx_led_init(void)
+{
+	return platform_driver_register(&pm8xxx_led_driver);
+}
+subsys_initcall(pm8xxx_led_init);
+
+static void __exit pm8xxx_led_exit(void)
+{
+	platform_driver_unregister(&pm8xxx_led_driver);
+}
+module_exit(pm8xxx_led_exit);
+
+MODULE_DESCRIPTION("PM8XXX LEDs driver");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pm8xxx-led");
diff --git a/drivers/leds/leds-pmic-mpp.c b/drivers/leds/leds-pmic-mpp.c
new file mode 100644
index 0000000..a3762be
--- /dev/null
+++ b/drivers/leds/leds-pmic-mpp.c
@@ -0,0 +1,141 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#include <mach/pmic.h>
+
+#define LED_MPP(x)		((x) & 0xFF)
+#define LED_CURR(x)		((x) >> 16)
+
+struct pmic_mpp_led_data {
+	struct led_classdev cdev;
+	int curr;
+	int mpp;
+};
+
+static void pm_mpp_led_set(struct led_classdev *led_cdev,
+	enum led_brightness value)
+{
+	struct pmic_mpp_led_data *led;
+	int ret;
+
+	led = container_of(led_cdev, struct pmic_mpp_led_data, cdev);
+
+	if (value < LED_OFF || value > led->cdev.max_brightness) {
+		dev_err(led->cdev.dev, "Invalid brightness value");
+		return;
+	}
+
+	ret = pmic_secure_mpp_config_i_sink(led->mpp, led->curr,
+			value ? PM_MPP__I_SINK__SWITCH_ENA :
+				PM_MPP__I_SINK__SWITCH_DIS);
+	if (ret)
+		dev_err(led_cdev->dev, "can't set mpp led\n");
+}
+
+static int pmic_mpp_led_probe(struct platform_device *pdev)
+{
+	const struct led_platform_data *pdata = pdev->dev.platform_data;
+	struct pmic_mpp_led_data *led, *tmp_led;
+	int i, rc;
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "platform data not supplied\n");
+		return -EINVAL;
+	}
+
+	led = kcalloc(pdata->num_leds, sizeof(*led), GFP_KERNEL);
+	if (!led) {
+		dev_err(&pdev->dev, "failed to alloc memory\n");
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(pdev, led);
+
+	for (i = 0; i < pdata->num_leds; i++) {
+		tmp_led	= &led[i];
+		tmp_led->cdev.name = pdata->leds[i].name;
+		tmp_led->cdev.brightness_set = pm_mpp_led_set;
+		tmp_led->cdev.brightness = LED_OFF;
+		tmp_led->cdev.max_brightness = LED_FULL;
+		tmp_led->mpp = LED_MPP(pdata->leds[i].flags);
+		tmp_led->curr = LED_CURR(pdata->leds[i].flags);
+
+		if (tmp_led->curr < PM_MPP__I_SINK__LEVEL_5mA ||
+			tmp_led->curr > PM_MPP__I_SINK__LEVEL_40mA) {
+			dev_err(&pdev->dev, "invalid current\n");
+			goto unreg_led_cdev;
+		}
+
+		rc = led_classdev_register(&pdev->dev, &tmp_led->cdev);
+		if (rc) {
+			dev_err(&pdev->dev, "failed to register led\n");
+			goto unreg_led_cdev;
+		}
+	}
+
+	return 0;
+
+unreg_led_cdev:
+	while (i)
+		led_classdev_unregister(&led[--i].cdev);
+
+	kfree(led);
+	return rc;
+
+}
+
+static int __devexit pmic_mpp_led_remove(struct platform_device *pdev)
+{
+	const struct led_platform_data *pdata = pdev->dev.platform_data;
+	struct pmic_mpp_led_data *led = platform_get_drvdata(pdev);
+	int i;
+
+	for (i = 0; i < pdata->num_leds; i++)
+		led_classdev_unregister(&led[i].cdev);
+
+	kfree(led);
+
+	return 0;
+}
+
+static struct platform_driver pmic_mpp_led_driver = {
+	.probe		= pmic_mpp_led_probe,
+	.remove		= __devexit_p(pmic_mpp_led_remove),
+	.driver		= {
+		.name	= "pmic-mpp-leds",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init pmic_mpp_led_init(void)
+{
+	return platform_driver_register(&pmic_mpp_led_driver);
+}
+module_init(pmic_mpp_led_init);
+
+static void __exit pmic_mpp_led_exit(void)
+{
+	platform_driver_unregister(&pmic_mpp_led_driver);
+}
+module_exit(pmic_mpp_led_exit);
+
+MODULE_DESCRIPTION("PMIC MPP LEDs driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:pmic-mpp-leds");
diff --git a/drivers/leds/leds-pmic8058.c b/drivers/leds/leds-pmic8058.c
new file mode 100644
index 0000000..0ba3f58
--- /dev/null
+++ b/drivers/leds/leds-pmic8058.c
@@ -0,0 +1,428 @@
+/* Copyright (c) 2010, 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/workqueue.h>
+#include <linux/spinlock.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/leds-pmic8058.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#define SSBI_REG_ADDR_DRV_KEYPAD	0x48
+#define PM8058_DRV_KEYPAD_BL_MASK	0xf0
+#define PM8058_DRV_KEYPAD_BL_SHIFT	0x04
+
+#define SSBI_REG_ADDR_FLASH_DRV0        0x49
+#define PM8058_DRV_FLASH_MASK           0xf0
+#define PM8058_DRV_FLASH_SHIFT          0x04
+
+#define SSBI_REG_ADDR_FLASH_DRV1        0xFB
+
+#define SSBI_REG_ADDR_LED_CTRL_BASE	0x131
+#define SSBI_REG_ADDR_LED_CTRL(n)	(SSBI_REG_ADDR_LED_CTRL_BASE + (n))
+#define PM8058_DRV_LED_CTRL_MASK	0xf8
+#define PM8058_DRV_LED_CTRL_SHIFT	0x03
+
+#define MAX_FLASH_CURRENT	300
+#define MAX_KEYPAD_CURRENT 300
+#define MAX_KEYPAD_BL_LEVEL	(1 << 4)
+#define MAX_LED_DRV_LEVEL	20 /* 2 * 20 mA */
+
+#define PMIC8058_LED_OFFSET(id) ((id) - PMIC8058_ID_LED_0)
+
+struct pmic8058_led_data {
+	struct device		*dev;
+	struct led_classdev	cdev;
+	int			id;
+	enum led_brightness	brightness;
+	u8			flags;
+	struct work_struct	work;
+	struct mutex		lock;
+	spinlock_t		value_lock;
+	u8			reg_kp;
+	u8			reg_led_ctrl[3];
+	u8			reg_flash_led0;
+	u8			reg_flash_led1;
+};
+
+#define PM8058_MAX_LEDS		7
+static struct pmic8058_led_data led_data[PM8058_MAX_LEDS];
+
+static void kp_bl_set(struct pmic8058_led_data *led, enum led_brightness value)
+{
+	int rc;
+	u8 level;
+	unsigned long flags;
+
+	spin_lock_irqsave(&led->value_lock, flags);
+	level = (value << PM8058_DRV_KEYPAD_BL_SHIFT) &
+				 PM8058_DRV_KEYPAD_BL_MASK;
+
+	led->reg_kp &= ~PM8058_DRV_KEYPAD_BL_MASK;
+	led->reg_kp |= level;
+	spin_unlock_irqrestore(&led->value_lock, flags);
+
+	rc = pm8xxx_writeb(led->dev->parent, SSBI_REG_ADDR_DRV_KEYPAD,
+						led->reg_kp);
+	if (rc)
+		pr_err("%s: can't set keypad backlight level\n", __func__);
+}
+
+static enum led_brightness kp_bl_get(struct pmic8058_led_data *led)
+{
+	if ((led->reg_kp & PM8058_DRV_KEYPAD_BL_MASK) >>
+			 PM8058_DRV_KEYPAD_BL_SHIFT)
+		return LED_FULL;
+	else
+		return LED_OFF;
+}
+
+static void led_lc_set(struct pmic8058_led_data *led, enum led_brightness value)
+{
+	unsigned long flags;
+	int rc, offset;
+	u8 level, tmp;
+
+	spin_lock_irqsave(&led->value_lock, flags);
+
+	level = (led->brightness << PM8058_DRV_LED_CTRL_SHIFT) &
+		PM8058_DRV_LED_CTRL_MASK;
+
+	offset = PMIC8058_LED_OFFSET(led->id);
+	tmp = led->reg_led_ctrl[offset];
+
+	tmp &= ~PM8058_DRV_LED_CTRL_MASK;
+	tmp |= level;
+	spin_unlock_irqrestore(&led->value_lock, flags);
+
+	rc = pm8xxx_writeb(led->dev->parent, SSBI_REG_ADDR_LED_CTRL(offset),
+								tmp);
+	if (rc) {
+		dev_err(led->cdev.dev, "can't set (%d) led value\n",
+				led->id);
+		return;
+	}
+
+	spin_lock_irqsave(&led->value_lock, flags);
+	led->reg_led_ctrl[offset] = tmp;
+	spin_unlock_irqrestore(&led->value_lock, flags);
+}
+
+static enum led_brightness led_lc_get(struct pmic8058_led_data *led)
+{
+	int offset;
+	u8 value;
+
+	offset = PMIC8058_LED_OFFSET(led->id);
+	value = led->reg_led_ctrl[offset];
+
+	if ((value & PM8058_DRV_LED_CTRL_MASK) >>
+			PM8058_DRV_LED_CTRL_SHIFT)
+		return LED_FULL;
+	else
+		return LED_OFF;
+}
+
+static void
+led_flash_set(struct pmic8058_led_data *led, enum led_brightness value)
+{
+	int rc;
+	u8 level;
+	unsigned long flags;
+	u8 reg_flash_led;
+	u16 reg_addr;
+
+	spin_lock_irqsave(&led->value_lock, flags);
+	level = (value << PM8058_DRV_FLASH_SHIFT) &
+				 PM8058_DRV_FLASH_MASK;
+
+	if (led->id == PMIC8058_ID_FLASH_LED_0) {
+		led->reg_flash_led0 &= ~PM8058_DRV_FLASH_MASK;
+		led->reg_flash_led0 |= level;
+		reg_flash_led	    = led->reg_flash_led0;
+		reg_addr	    = SSBI_REG_ADDR_FLASH_DRV0;
+	} else {
+		led->reg_flash_led1 &= ~PM8058_DRV_FLASH_MASK;
+		led->reg_flash_led1 |= level;
+		reg_flash_led	    = led->reg_flash_led1;
+		reg_addr	    = SSBI_REG_ADDR_FLASH_DRV1;
+	}
+	spin_unlock_irqrestore(&led->value_lock, flags);
+
+	rc = pm8xxx_writeb(led->dev->parent, reg_addr, reg_flash_led);
+	if (rc)
+		pr_err("%s: can't set flash led%d level %d\n", __func__,
+			led->id, rc);
+}
+
+int pm8058_set_flash_led_current(enum pmic8058_leds id, unsigned mA)
+{
+	struct pmic8058_led_data *led;
+
+	if ((id < PMIC8058_ID_FLASH_LED_0) || (id > PMIC8058_ID_FLASH_LED_1)) {
+		pr_err("%s: invalid LED ID (%d) specified\n", __func__, id);
+		return -EINVAL;
+	}
+
+	led = &led_data[id];
+	if (!led) {
+		pr_err("%s: flash led not available\n", __func__);
+		return -EINVAL;
+	}
+
+	if (mA > MAX_FLASH_CURRENT)
+		return -EINVAL;
+
+	led_flash_set(led, mA / 20);
+
+	return 0;
+}
+EXPORT_SYMBOL(pm8058_set_flash_led_current);
+
+int pm8058_set_led_current(enum pmic8058_leds id, unsigned mA)
+{
+	struct pmic8058_led_data *led;
+	int brightness = 0;
+
+	if ((id < PMIC8058_ID_LED_KB_LIGHT) || (id > PMIC8058_ID_FLASH_LED_1)) {
+		pr_err("%s: invalid LED ID (%d) specified\n", __func__, id);
+		return -EINVAL;
+	}
+
+	led = &led_data[id];
+	if (!led) {
+		pr_err("%s: flash led not available\n", __func__);
+		return -EINVAL;
+	}
+
+	switch (id) {
+	case PMIC8058_ID_LED_0:
+	case PMIC8058_ID_LED_1:
+	case PMIC8058_ID_LED_2:
+		brightness = mA / 2;
+		if (brightness  > led->cdev.max_brightness)
+			return -EINVAL;
+		led_lc_set(led, brightness);
+		break;
+
+	case PMIC8058_ID_LED_KB_LIGHT:
+	case PMIC8058_ID_FLASH_LED_0:
+	case PMIC8058_ID_FLASH_LED_1:
+		brightness = mA / 20;
+		if (brightness  > led->cdev.max_brightness)
+			return -EINVAL;
+		if (id == PMIC8058_ID_LED_KB_LIGHT)
+			kp_bl_set(led, brightness);
+		else
+			led_flash_set(led, brightness);
+		break;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(pm8058_set_led_current);
+
+static void pmic8058_led_set(struct led_classdev *led_cdev,
+	enum led_brightness value)
+{
+	struct pmic8058_led_data *led;
+	unsigned long flags;
+
+	led = container_of(led_cdev, struct pmic8058_led_data, cdev);
+
+	spin_lock_irqsave(&led->value_lock, flags);
+	led->brightness = value;
+	schedule_work(&led->work);
+	spin_unlock_irqrestore(&led->value_lock, flags);
+}
+
+static void pmic8058_led_work(struct work_struct *work)
+{
+	struct pmic8058_led_data *led = container_of(work,
+					 struct pmic8058_led_data, work);
+
+	mutex_lock(&led->lock);
+
+	switch (led->id) {
+	case PMIC8058_ID_LED_KB_LIGHT:
+		kp_bl_set(led, led->brightness);
+		break;
+	case PMIC8058_ID_LED_0:
+	case PMIC8058_ID_LED_1:
+	case PMIC8058_ID_LED_2:
+		led_lc_set(led, led->brightness);
+		break;
+	case PMIC8058_ID_FLASH_LED_0:
+	case PMIC8058_ID_FLASH_LED_1:
+		led_flash_set(led, led->brightness);
+		break;
+	}
+
+	mutex_unlock(&led->lock);
+}
+
+static enum led_brightness pmic8058_led_get(struct led_classdev *led_cdev)
+{
+	struct pmic8058_led_data *led;
+
+	led = container_of(led_cdev, struct pmic8058_led_data, cdev);
+
+	switch (led->id) {
+	case PMIC8058_ID_LED_KB_LIGHT:
+		return kp_bl_get(led);
+	case PMIC8058_ID_LED_0:
+	case PMIC8058_ID_LED_1:
+	case PMIC8058_ID_LED_2:
+		return led_lc_get(led);
+	}
+	return LED_OFF;
+}
+
+static int pmic8058_led_probe(struct platform_device *pdev)
+{
+	struct pmic8058_leds_platform_data *pdata = pdev->dev.platform_data;
+	struct pmic8058_led_data *led_dat;
+	struct pmic8058_led *curr_led;
+	int rc, i = 0;
+	u8			reg_kp;
+	u8			reg_led_ctrl[3];
+	u8			reg_flash_led0;
+	u8			reg_flash_led1;
+
+	if (pdata == NULL) {
+		dev_err(&pdev->dev, "platform data not supplied\n");
+		return -EINVAL;
+	}
+
+	rc = pm8xxx_readb(pdev->dev.parent, SSBI_REG_ADDR_DRV_KEYPAD, &reg_kp);
+	if (rc) {
+		dev_err(&pdev->dev, "can't get keypad backlight level\n");
+		goto err_reg_read;
+	}
+
+	rc = pm8xxx_read_buf(pdev->dev.parent, SSBI_REG_ADDR_LED_CTRL_BASE,
+							reg_led_ctrl, 3);
+	if (rc) {
+		dev_err(&pdev->dev, "can't get led levels\n");
+		goto err_reg_read;
+	}
+
+	rc = pm8xxx_readb(pdev->dev.parent, SSBI_REG_ADDR_FLASH_DRV0,
+						&reg_flash_led0);
+	if (rc) {
+		dev_err(&pdev->dev, "can't read flash led0\n");
+		goto err_reg_read;
+	}
+
+	rc = pm8xxx_readb(pdev->dev.parent, SSBI_REG_ADDR_FLASH_DRV1,
+						&reg_flash_led1);
+	if (rc) {
+		dev_err(&pdev->dev, "can't get flash led1\n");
+		goto err_reg_read;
+	}
+
+	for (i = 0; i < pdata->num_leds; i++) {
+		curr_led	= &pdata->leds[i];
+		led_dat		= &led_data[curr_led->id];
+
+		led_dat->cdev.name		= curr_led->name;
+		led_dat->cdev.default_trigger   = curr_led->default_trigger;
+		led_dat->cdev.brightness_set    = pmic8058_led_set;
+		led_dat->cdev.brightness_get    = pmic8058_led_get;
+		led_dat->cdev.brightness	= LED_OFF;
+		led_dat->cdev.max_brightness	= curr_led->max_brightness;
+		led_dat->cdev.flags		= LED_CORE_SUSPENDRESUME;
+
+		led_dat->id		        = curr_led->id;
+		led_dat->reg_kp			= reg_kp;
+		memcpy(led_data->reg_led_ctrl, reg_led_ctrl,
+					 sizeof(reg_led_ctrl));
+		led_dat->reg_flash_led0		= reg_flash_led0;
+		led_dat->reg_flash_led1		= reg_flash_led1;
+
+		if (!((led_dat->id >= PMIC8058_ID_LED_KB_LIGHT) &&
+				(led_dat->id <= PMIC8058_ID_FLASH_LED_1))) {
+			dev_err(&pdev->dev, "invalid LED ID (%d) specified\n",
+						 led_dat->id);
+			rc = -EINVAL;
+			goto fail_id_check;
+		}
+
+		led_dat->dev			= &pdev->dev;
+
+		mutex_init(&led_dat->lock);
+		spin_lock_init(&led_dat->value_lock);
+		INIT_WORK(&led_dat->work, pmic8058_led_work);
+
+		rc = led_classdev_register(&pdev->dev, &led_dat->cdev);
+		if (rc) {
+			dev_err(&pdev->dev, "unable to register led %d\n",
+						 led_dat->id);
+			goto fail_id_check;
+		}
+	}
+
+	platform_set_drvdata(pdev, led_data);
+
+	return 0;
+
+err_reg_read:
+fail_id_check:
+	if (i > 0) {
+		for (i = i - 1; i >= 0; i--)
+			led_classdev_unregister(&led_data[i].cdev);
+	}
+	return rc;
+}
+
+static int __devexit pmic8058_led_remove(struct platform_device *pdev)
+{
+	int i;
+	struct pmic8058_leds_platform_data *pdata = pdev->dev.platform_data;
+	struct pmic8058_led_data *led = platform_get_drvdata(pdev);
+
+	for (i = 0; i < pdata->num_leds; i++) {
+		led_classdev_unregister(&led[led->id].cdev);
+		cancel_work_sync(&led[led->id].work);
+	}
+
+	return 0;
+}
+
+static struct platform_driver pmic8058_led_driver = {
+	.probe		= pmic8058_led_probe,
+	.remove		= __devexit_p(pmic8058_led_remove),
+	.driver		= {
+		.name	= "pm8058-led",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init pmic8058_led_init(void)
+{
+	return platform_driver_register(&pmic8058_led_driver);
+}
+module_init(pmic8058_led_init);
+
+static void __exit pmic8058_led_exit(void)
+{
+	platform_driver_unregister(&pmic8058_led_driver);
+}
+module_exit(pmic8058_led_exit);
+
+MODULE_DESCRIPTION("PMIC8058 LEDs driver");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pmic8058-led");
diff --git a/drivers/leds/leds-qci-backlight.c b/drivers/leds/leds-qci-backlight.c
new file mode 100644
index 0000000..67502e8
--- /dev/null
+++ b/drivers/leds/leds-qci-backlight.c
@@ -0,0 +1,71 @@
+/* Quanta I2C Backlight Driver
+ *
+ * Copyright (C) 2009 Quanta Computer 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.
+ *
+ */
+
+/*
+ *
+ *  The Driver with I/O communications via the I2C Interface for ST15 platform.
+ *  And it is only working on the nuvoTon WPCE775x Embedded Controller.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/leds.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/wpce775x.h>
+
+#define EC_CMD_SET_BACKLIGHT 0xB1
+
+static void qci_backlight_store(struct led_classdev *led_cdev,
+	enum led_brightness val);
+
+static struct platform_device *bl_pdev;
+static struct led_classdev lcd_backlight = {
+	.name = "lcd-backlight",
+	.brightness = 147,
+	.brightness_set = qci_backlight_store,
+};
+
+static void qci_backlight_store(struct led_classdev *led_cdev,
+	enum led_brightness val)
+{
+	u16 value = val;
+	wpce_smbus_write_word_data(EC_CMD_SET_BACKLIGHT, value);
+	msleep(10);
+
+	dev_dbg(&bl_pdev->dev, "[backlight_store] : value  = %d\n", value);
+}
+
+static int __init qci_backlight_init(void)
+{
+	int err = 0;
+	bl_pdev = platform_device_register_simple("backlight", 0, NULL, 0);
+	err = led_classdev_register(&bl_pdev->dev, &lcd_backlight);
+	return err;
+}
+
+static void __exit qci_backlight_exit(void)
+{
+	led_classdev_unregister(&lcd_backlight);
+	platform_device_unregister(bl_pdev);
+}
+
+module_init(qci_backlight_init);
+module_exit(qci_backlight_exit);
+
+MODULE_AUTHOR("Quanta Computer Inc.");
+MODULE_DESCRIPTION("Quanta Embedded Controller I2C Backlight Driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index 3f06df5..da5db8b 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -18,7 +18,6 @@
 #include <linux/crypto.h>
 #include <linux/workqueue.h>
 #include <linux/backing-dev.h>
-#include <linux/percpu.h>
 #include <linux/atomic.h>
 #include <linux/scatterlist.h>
 #include <asm/page.h>
@@ -43,7 +42,8 @@
 	unsigned int idx_in;
 	unsigned int idx_out;
 	sector_t sector;
-	atomic_t pending;
+	atomic_t cc_pending;
+	struct ablkcipher_request *req;
 };
 
 /*
@@ -56,7 +56,7 @@
 
 	struct convert_context ctx;
 
-	atomic_t pending;
+	atomic_t io_pending;
 	int error;
 	sector_t sector;
 	struct dm_crypt_io *base_io;
@@ -105,18 +105,7 @@
 enum flags { DM_CRYPT_SUSPENDED, DM_CRYPT_KEY_VALID };
 
 /*
- * Duplicated per-CPU state for cipher.
- */
-struct crypt_cpu {
-	struct ablkcipher_request *req;
-	/* ESSIV: struct crypto_cipher *essiv_tfm */
-	void *iv_private;
-	struct crypto_ablkcipher *tfms[0];
-};
-
-/*
  * The fields in here must be read only after initialization,
- * changing state should be in crypt_cpu.
  */
 struct crypt_config {
 	struct dm_dev *dev;
@@ -146,11 +135,9 @@
 	sector_t iv_offset;
 	unsigned int iv_size;
 
-	/*
-	 * Duplicated per cpu state. Access through
-	 * per_cpu_ptr() only.
-	 */
-	struct crypt_cpu __percpu *cpu;
+	/* ESSIV: struct crypto_cipher *essiv_tfm */
+	void *iv_private;
+	struct crypto_ablkcipher **tfms;
 	unsigned tfms_count;
 
 	/*
@@ -183,17 +170,12 @@
 static void kcryptd_queue_crypt(struct dm_crypt_io *io);
 static u8 *iv_of_dmreq(struct crypt_config *cc, struct dm_crypt_request *dmreq);
 
-static struct crypt_cpu *this_crypt_config(struct crypt_config *cc)
-{
-	return this_cpu_ptr(cc->cpu);
-}
-
 /*
  * Use this to access cipher attributes that are the same for each CPU.
  */
 static struct crypto_ablkcipher *any_tfm(struct crypt_config *cc)
 {
-	return __this_cpu_ptr(cc->cpu)->tfms[0];
+	return cc->tfms[0];
 }
 
 /*
@@ -258,7 +240,7 @@
 	struct hash_desc desc;
 	struct scatterlist sg;
 	struct crypto_cipher *essiv_tfm;
-	int err, cpu;
+	int err;
 
 	sg_init_one(&sg, cc->key, cc->key_size);
 	desc.tfm = essiv->hash_tfm;
@@ -268,14 +250,12 @@
 	if (err)
 		return err;
 
-	for_each_possible_cpu(cpu) {
-		essiv_tfm = per_cpu_ptr(cc->cpu, cpu)->iv_private,
+	essiv_tfm = cc->iv_private;
 
-		err = crypto_cipher_setkey(essiv_tfm, essiv->salt,
-				    crypto_hash_digestsize(essiv->hash_tfm));
-		if (err)
-			return err;
-	}
+	err = crypto_cipher_setkey(essiv_tfm, essiv->salt,
+			    crypto_hash_digestsize(essiv->hash_tfm));
+	if (err)
+		return err;
 
 	return 0;
 }
@@ -286,16 +266,14 @@
 	struct iv_essiv_private *essiv = &cc->iv_gen_private.essiv;
 	unsigned salt_size = crypto_hash_digestsize(essiv->hash_tfm);
 	struct crypto_cipher *essiv_tfm;
-	int cpu, r, err = 0;
+	int r, err = 0;
 
 	memset(essiv->salt, 0, salt_size);
 
-	for_each_possible_cpu(cpu) {
-		essiv_tfm = per_cpu_ptr(cc->cpu, cpu)->iv_private;
-		r = crypto_cipher_setkey(essiv_tfm, essiv->salt, salt_size);
-		if (r)
-			err = r;
-	}
+	essiv_tfm = cc->iv_private;
+	r = crypto_cipher_setkey(essiv_tfm, essiv->salt, salt_size);
+	if (r)
+		err = r;
 
 	return err;
 }
@@ -335,8 +313,6 @@
 
 static void crypt_iv_essiv_dtr(struct crypt_config *cc)
 {
-	int cpu;
-	struct crypt_cpu *cpu_cc;
 	struct crypto_cipher *essiv_tfm;
 	struct iv_essiv_private *essiv = &cc->iv_gen_private.essiv;
 
@@ -346,15 +322,13 @@
 	kzfree(essiv->salt);
 	essiv->salt = NULL;
 
-	for_each_possible_cpu(cpu) {
-		cpu_cc = per_cpu_ptr(cc->cpu, cpu);
-		essiv_tfm = cpu_cc->iv_private;
+	essiv_tfm = cc->iv_private;
 
-		if (essiv_tfm)
-			crypto_free_cipher(essiv_tfm);
+	if (essiv_tfm)
+		crypto_free_cipher(essiv_tfm);
 
-		cpu_cc->iv_private = NULL;
-	}
+	cc->iv_private = NULL;
+
 }
 
 static int crypt_iv_essiv_ctr(struct crypt_config *cc, struct dm_target *ti,
@@ -363,7 +337,7 @@
 	struct crypto_cipher *essiv_tfm = NULL;
 	struct crypto_hash *hash_tfm = NULL;
 	u8 *salt = NULL;
-	int err, cpu;
+	int err;
 
 	if (!opts) {
 		ti->error = "Digest algorithm missing for ESSIV mode";
@@ -388,15 +362,13 @@
 	cc->iv_gen_private.essiv.salt = salt;
 	cc->iv_gen_private.essiv.hash_tfm = hash_tfm;
 
-	for_each_possible_cpu(cpu) {
-		essiv_tfm = setup_essiv_cpu(cc, ti, salt,
-					crypto_hash_digestsize(hash_tfm));
-		if (IS_ERR(essiv_tfm)) {
-			crypt_iv_essiv_dtr(cc);
-			return PTR_ERR(essiv_tfm);
-		}
-		per_cpu_ptr(cc->cpu, cpu)->iv_private = essiv_tfm;
+	essiv_tfm = setup_essiv_cpu(cc, ti, salt,
+				crypto_hash_digestsize(hash_tfm));
+	if (IS_ERR(essiv_tfm)) {
+		crypt_iv_essiv_dtr(cc);
+		return PTR_ERR(essiv_tfm);
 	}
+	cc->iv_private = essiv_tfm;
 
 	return 0;
 
@@ -410,7 +382,8 @@
 static int crypt_iv_essiv_gen(struct crypt_config *cc, u8 *iv,
 			      struct dm_crypt_request *dmreq)
 {
-	struct crypto_cipher *essiv_tfm = this_crypt_config(cc)->iv_private;
+
+	struct crypto_cipher *essiv_tfm = cc->iv_private;
 
 	memset(iv, 0, cc->iv_size);
 	*(__le64 *)iv = cpu_to_le64(dmreq->iv_sector);
@@ -748,16 +721,15 @@
 static void crypt_alloc_req(struct crypt_config *cc,
 			    struct convert_context *ctx)
 {
-	struct crypt_cpu *this_cc = this_crypt_config(cc);
 	unsigned key_index = ctx->sector & (cc->tfms_count - 1);
 
-	if (!this_cc->req)
-		this_cc->req = mempool_alloc(cc->req_pool, GFP_NOIO);
+	if (!ctx->req)
+		ctx->req = mempool_alloc(cc->req_pool, GFP_NOIO);
 
-	ablkcipher_request_set_tfm(this_cc->req, this_cc->tfms[key_index]);
-	ablkcipher_request_set_callback(this_cc->req,
+	ablkcipher_request_set_tfm(ctx->req, cc->tfms[key_index]);
+	ablkcipher_request_set_callback(ctx->req,
 	    CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
-	    kcryptd_async_done, dmreq_of_req(cc, this_cc->req));
+	    kcryptd_async_done, dmreq_of_req(cc, ctx->req));
 }
 
 /*
@@ -766,19 +738,18 @@
 static int crypt_convert(struct crypt_config *cc,
 			 struct convert_context *ctx)
 {
-	struct crypt_cpu *this_cc = this_crypt_config(cc);
 	int r;
 
-	atomic_set(&ctx->pending, 1);
+	atomic_set(&ctx->cc_pending, 1);
 
 	while(ctx->idx_in < ctx->bio_in->bi_vcnt &&
 	      ctx->idx_out < ctx->bio_out->bi_vcnt) {
 
 		crypt_alloc_req(cc, ctx);
 
-		atomic_inc(&ctx->pending);
+		atomic_inc(&ctx->cc_pending);
 
-		r = crypt_convert_block(cc, ctx, this_cc->req);
+		r = crypt_convert_block(cc, ctx, ctx->req);
 
 		switch (r) {
 		/* async */
@@ -787,20 +758,20 @@
 			INIT_COMPLETION(ctx->restart);
 			/* fall through*/
 		case -EINPROGRESS:
-			this_cc->req = NULL;
+			ctx->req = NULL;
 			ctx->sector++;
 			continue;
 
 		/* sync */
 		case 0:
-			atomic_dec(&ctx->pending);
+			atomic_dec(&ctx->cc_pending);
 			ctx->sector++;
 			cond_resched();
 			continue;
 
 		/* error */
 		default:
-			atomic_dec(&ctx->pending);
+			atomic_dec(&ctx->cc_pending);
 			return r;
 		}
 	}
@@ -896,14 +867,15 @@
 	io->sector = sector;
 	io->error = 0;
 	io->base_io = NULL;
-	atomic_set(&io->pending, 0);
+	io->ctx.req = NULL;
+	atomic_set(&io->io_pending, 0);
 
 	return io;
 }
 
 static void crypt_inc_pending(struct dm_crypt_io *io)
 {
-	atomic_inc(&io->pending);
+	atomic_inc(&io->io_pending);
 }
 
 /*
@@ -918,9 +890,11 @@
 	struct dm_crypt_io *base_io = io->base_io;
 	int error = io->error;
 
-	if (!atomic_dec_and_test(&io->pending))
+	if (!atomic_dec_and_test(&io->io_pending))
 		return;
 
+	if (io->ctx.req)
+		mempool_free(io->ctx.req, cc->req_pool);
 	mempool_free(io, cc->io_pool);
 
 	if (likely(!base_io))
@@ -1106,8 +1080,7 @@
 		r = crypt_convert(cc, &io->ctx);
 		if (r < 0)
 			io->error = -EIO;
-
-		crypt_finished = atomic_dec_and_test(&io->ctx.pending);
+		crypt_finished = atomic_dec_and_test(&io->ctx.cc_pending);
 
 		/* Encryption was already finished, submit io now */
 		if (crypt_finished) {
@@ -1178,10 +1151,11 @@
 			   io->sector);
 
 	r = crypt_convert(cc, &io->ctx);
+
 	if (r < 0)
 		io->error = -EIO;
 
-	if (atomic_dec_and_test(&io->ctx.pending))
+	if (atomic_dec_and_test(&io->ctx.cc_pending))
 		kcryptd_crypt_read_done(io);
 
 	crypt_dec_pending(io);
@@ -1208,7 +1182,7 @@
 
 	mempool_free(req_of_dmreq(cc, dmreq), cc->req_pool);
 
-	if (!atomic_dec_and_test(&ctx->pending))
+	if (!atomic_dec_and_test(&ctx->cc_pending))
 		return;
 
 	if (bio_data_dir(io->base_bio) == READ)
@@ -1276,29 +1250,35 @@
 	}
 }
 
-static void crypt_free_tfms(struct crypt_config *cc, int cpu)
+static void crypt_free_tfms(struct crypt_config *cc)
 {
-	struct crypt_cpu *cpu_cc = per_cpu_ptr(cc->cpu, cpu);
 	unsigned i;
 
+	if (!cc->tfms)
+		return;
+
 	for (i = 0; i < cc->tfms_count; i++)
-		if (cpu_cc->tfms[i] && !IS_ERR(cpu_cc->tfms[i])) {
-			crypto_free_ablkcipher(cpu_cc->tfms[i]);
-			cpu_cc->tfms[i] = NULL;
+		if (cc->tfms[i] && !IS_ERR(cc->tfms[i])) {
+			crypto_free_ablkcipher(cc->tfms[i]);
+			cc->tfms[i] = NULL;
 		}
 }
 
-static int crypt_alloc_tfms(struct crypt_config *cc, int cpu, char *ciphermode)
+static int crypt_alloc_tfms(struct crypt_config *cc, char *ciphermode)
 {
-	struct crypt_cpu *cpu_cc = per_cpu_ptr(cc->cpu, cpu);
 	unsigned i;
 	int err;
 
+	cc->tfms = kmalloc(cc->tfms_count * sizeof(struct crypto_ablkcipher *),
+			   GFP_KERNEL);
+	if (!cc->tfms)
+		return -ENOMEM;
+
 	for (i = 0; i < cc->tfms_count; i++) {
-		cpu_cc->tfms[i] = crypto_alloc_ablkcipher(ciphermode, 0, 0);
-		if (IS_ERR(cpu_cc->tfms[i])) {
-			err = PTR_ERR(cpu_cc->tfms[i]);
-			crypt_free_tfms(cc, cpu);
+		cc->tfms[i] = crypto_alloc_ablkcipher(ciphermode, 0, 0);
+		if (IS_ERR(cc->tfms[i])) {
+			err = PTR_ERR(cc->tfms[i]);
+			crypt_free_tfms(cc);
 			return err;
 		}
 	}
@@ -1309,15 +1289,14 @@
 static int crypt_setkey_allcpus(struct crypt_config *cc)
 {
 	unsigned subkey_size = cc->key_size >> ilog2(cc->tfms_count);
-	int cpu, err = 0, i, r;
+	int err = 0, i, r;
 
-	for_each_possible_cpu(cpu) {
-		for (i = 0; i < cc->tfms_count; i++) {
-			r = crypto_ablkcipher_setkey(per_cpu_ptr(cc->cpu, cpu)->tfms[i],
-						     cc->key + (i * subkey_size), subkey_size);
-			if (r)
-				err = r;
-		}
+	for (i = 0; i < cc->tfms_count; i++) {
+		r = crypto_ablkcipher_setkey(cc->tfms[i],
+					     cc->key + (i * subkey_size),
+					     subkey_size);
+		if (r)
+			err = r;
 	}
 
 	return err;
@@ -1361,8 +1340,6 @@
 static void crypt_dtr(struct dm_target *ti)
 {
 	struct crypt_config *cc = ti->private;
-	struct crypt_cpu *cpu_cc;
-	int cpu;
 
 	ti->private = NULL;
 
@@ -1374,13 +1351,7 @@
 	if (cc->crypt_queue)
 		destroy_workqueue(cc->crypt_queue);
 
-	if (cc->cpu)
-		for_each_possible_cpu(cpu) {
-			cpu_cc = per_cpu_ptr(cc->cpu, cpu);
-			if (cpu_cc->req)
-				mempool_free(cpu_cc->req, cc->req_pool);
-			crypt_free_tfms(cc, cpu);
-		}
+	crypt_free_tfms(cc);
 
 	if (cc->bs)
 		bioset_free(cc->bs);
@@ -1398,9 +1369,6 @@
 	if (cc->dev)
 		dm_put_device(ti, cc->dev);
 
-	if (cc->cpu)
-		free_percpu(cc->cpu);
-
 	kzfree(cc->cipher);
 	kzfree(cc->cipher_string);
 
@@ -1414,7 +1382,7 @@
 	struct crypt_config *cc = ti->private;
 	char *tmp, *cipher, *chainmode, *ivmode, *ivopts, *keycount;
 	char *cipher_api = NULL;
-	int cpu, ret = -EINVAL;
+	int ret = -EINVAL;
 	char dummy;
 
 	/* Convert to crypto api definition? */
@@ -1455,14 +1423,6 @@
 	if (tmp)
 		DMWARN("Ignoring unexpected additional cipher options");
 
-	cc->cpu = __alloc_percpu(sizeof(*(cc->cpu)) +
-				 cc->tfms_count * sizeof(*(cc->cpu->tfms)),
-				 __alignof__(struct crypt_cpu));
-	if (!cc->cpu) {
-		ti->error = "Cannot allocate per cpu state";
-		goto bad_mem;
-	}
-
 	/*
 	 * For compatibility with the original dm-crypt mapping format, if
 	 * only the cipher name is supplied, use cbc-plain.
@@ -1489,12 +1449,10 @@
 	}
 
 	/* Allocate cipher */
-	for_each_possible_cpu(cpu) {
-		ret = crypt_alloc_tfms(cc, cpu, cipher_api);
-		if (ret < 0) {
-			ti->error = "Error allocating crypto tfm";
-			goto bad;
-		}
+	ret = crypt_alloc_tfms(cc, cipher_api);
+	if (ret < 0) {
+		ti->error = "Error allocating crypto tfm";
+		goto bad;
 	}
 
 	/* Initialize and set key */
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index 754f38f..5a61c5f 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -770,6 +770,11 @@
 	if (!argc)
 		return 0;
 
+	if (argc > as->argc) {
+		ti->error = "not enough arguments for features";
+		return -EINVAL;
+	}
+
 	do {
 		arg_name = dm_shift_arg(as);
 		argc--;
diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig
index f6e40b3..915dcaa 100644
--- a/drivers/media/dvb/Kconfig
+++ b/drivers/media/dvb/Kconfig
@@ -88,4 +88,8 @@
 	depends on DVB_CORE
 source "drivers/media/dvb/frontends/Kconfig"
 
+comment "Qualcomm MPQ adapter"
+	depends on ARCH_MSM && DVB_CORE
+source "drivers/media/dvb/mpq/Kconfig"
+
 endif # DVB_CAPTURE_DRIVERS
diff --git a/drivers/media/dvb/Makefile b/drivers/media/dvb/Makefile
index b2cefe6..d3438d6 100644
--- a/drivers/media/dvb/Makefile
+++ b/drivers/media/dvb/Makefile
@@ -19,3 +19,5 @@
 		ddbridge/
 
 obj-$(CONFIG_DVB_FIREDTV)	+= firewire/
+obj-$(CONFIG_DVB_MPQ)		+= mpq/
+
diff --git a/drivers/media/dvb/dvb-core/demux.h b/drivers/media/dvb/dvb-core/demux.h
index eb91fd8..a57ad44 100644
--- a/drivers/media/dvb/dvb-core/demux.h
+++ b/drivers/media/dvb/dvb-core/demux.h
@@ -7,6 +7,8 @@
  * Copyright (c) 2000 Nokia Research Center
  *                    Tampere, FINLAND
  *
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License
  * as published by the Free Software Foundation; either version 2.1
@@ -134,6 +136,8 @@
 		    struct timespec timeout);
 	int (*start_filtering) (struct dmx_ts_feed* feed);
 	int (*stop_filtering) (struct dmx_ts_feed* feed);
+	int (*set_indexing_params) (struct dmx_ts_feed *feed,
+				struct dmx_indexing_video_params *params);
 };
 
 /*--------------------------------------------------------------------------*/
@@ -190,6 +194,14 @@
 				struct dmx_section_filter * source,
 				enum dmx_success success);
 
+typedef int (*dmx_ts_fullness) (
+				struct dmx_ts_feed *source,
+				int required_space);
+
+typedef int (*dmx_section_fullness) (
+				struct dmx_section_filter *source,
+				int required_space);
+
 /*--------------------------------------------------------------------------*/
 /* DVB Front-End */
 /*--------------------------------------------------------------------------*/
@@ -247,7 +259,7 @@
 	void* priv;                  /* Pointer to private data of the API client */
 	int (*open) (struct dmx_demux* demux);
 	int (*close) (struct dmx_demux* demux);
-	int (*write) (struct dmx_demux* demux, const char __user *buf, size_t count);
+	int (*write) (struct dmx_demux *demux, const char *buf, size_t count);
 	int (*allocate_ts_feed) (struct dmx_demux* demux,
 				 struct dmx_ts_feed** feed,
 				 dmx_ts_cb callback);
@@ -271,7 +283,20 @@
 
 	int (*get_caps) (struct dmx_demux* demux, struct dmx_caps *caps);
 
-	int (*set_source) (struct dmx_demux* demux, const dmx_source_t *src);
+	int (*set_source) (struct dmx_demux *demux, const dmx_source_t *src);
+
+	int (*set_tsp_format) (struct dmx_demux *demux,
+				enum dmx_tsp_format_t tsp_format);
+
+	int (*set_tsp_out_format) (struct dmx_demux *demux,
+				enum dmx_tsp_format_t tsp_format);
+
+	int (*set_playback_mode) (struct dmx_demux *demux,
+				 enum dmx_playback_mode_t mode,
+				 dmx_ts_fullness ts_fullness_callback,
+				 dmx_section_fullness sec_fullness_callback);
+
+	int (*write_cancel) (struct dmx_demux *demux);
 
 	int (*get_stc) (struct dmx_demux* demux, unsigned int num,
 			u64 *stc, unsigned int *base);
diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c
index 73970cd..6dfa193 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.c
+++ b/drivers/media/dvb/dvb-core/dmxdev.c
@@ -4,6 +4,8 @@
  * Copyright (C) 2000 Ralph Metzler & Marcus Metzler
  *		      for convergence integrated media GmbH
  *
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License
  * as published by the Free Software Foundation; either version 2.1
@@ -28,6 +30,7 @@
 #include <linux/poll.h>
 #include <linux/ioctl.h>
 #include <linux/wait.h>
+#include <linux/mm.h>
 #include <asm/uaccess.h>
 #include "dmxdev.h"
 
@@ -80,12 +83,15 @@
 			break;
 		}
 
-		ret = wait_event_interruptible(src->queue,
-					       !dvb_ringbuffer_empty(src) ||
-					       (src->error != 0));
+		ret = wait_event_interruptible(src->queue, (!src->data) ||
+						!dvb_ringbuffer_empty(src) ||
+						(src->error != 0));
 		if (ret < 0)
 			break;
 
+		if (!src->data)
+			return 0;
+
 		if (src->error) {
 			ret = src->error;
 			dvb_ringbuffer_flush(src);
@@ -103,6 +109,9 @@
 		buf += ret;
 	}
 
+	if (count - todo) /* some data was read? */
+		wake_up_all(&src->queue);
+
 	return (count - todo) ? (count - todo) : ret;
 }
 
@@ -125,8 +134,11 @@
 	struct dvb_device *dvbdev = file->private_data;
 	struct dmxdev *dmxdev = dvbdev->priv;
 	struct dmx_frontend *front;
+	void *mem;
 
-	dprintk("function : %s\n", __func__);
+	dprintk("function : %s(%X)\n",
+			__func__,
+			(file->f_flags & O_ACCMODE));
 
 	if (mutex_lock_interruptible(&dmxdev->mutex))
 		return -ERESTARTSYS;
@@ -144,21 +156,20 @@
 	}
 
 	if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
-		void *mem;
 		if (!dvbdev->readers) {
 			mutex_unlock(&dmxdev->mutex);
 			return -EBUSY;
 		}
-		mem = vmalloc(DVR_BUFFER_SIZE);
+		mem = vmalloc_user(DVR_BUFFER_SIZE);
 		if (!mem) {
 			mutex_unlock(&dmxdev->mutex);
 			return -ENOMEM;
 		}
 		dvb_ringbuffer_init(&dmxdev->dvr_buffer, mem, DVR_BUFFER_SIZE);
 		dvbdev->readers--;
-	}
-
-	if ((file->f_flags & O_ACCMODE) == O_WRONLY) {
+	} else if (!dvbdev->writers) {
+		dmxdev->dvr_in_exit = 0;
+		dmxdev->dvr_processing_input = 0;
 		dmxdev->dvr_orig_fe = dmxdev->demux->frontend;
 
 		if (!dmxdev->demux->write) {
@@ -172,9 +183,22 @@
 			mutex_unlock(&dmxdev->mutex);
 			return -EINVAL;
 		}
+
+		mem = vmalloc_user(DVR_BUFFER_SIZE);
+		if (!mem) {
+			mutex_unlock(&dmxdev->mutex);
+			return -ENOMEM;
+		}
+
 		dmxdev->demux->disconnect_frontend(dmxdev->demux);
 		dmxdev->demux->connect_frontend(dmxdev->demux, front);
+
+		dvb_ringbuffer_init(&dmxdev->dvr_input_buffer,
+							mem,
+							DVR_BUFFER_SIZE);
+		dvbdev->writers--;
 	}
+
 	dvbdev->users++;
 	mutex_unlock(&dmxdev->mutex);
 	return 0;
@@ -187,11 +211,6 @@
 
 	mutex_lock(&dmxdev->mutex);
 
-	if ((file->f_flags & O_ACCMODE) == O_WRONLY) {
-		dmxdev->demux->disconnect_frontend(dmxdev->demux);
-		dmxdev->demux->connect_frontend(dmxdev->demux,
-						dmxdev->dvr_orig_fe);
-	}
 	if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
 		dvbdev->readers++;
 		if (dmxdev->dvr_buffer.data) {
@@ -200,6 +219,51 @@
 			spin_lock_irq(&dmxdev->lock);
 			dmxdev->dvr_buffer.data = NULL;
 			spin_unlock_irq(&dmxdev->lock);
+			wake_up_all(&dmxdev->dvr_buffer.queue);
+			vfree(mem);
+		}
+	} else {
+		int i;
+
+		dmxdev->dvr_in_exit = 1;
+		wake_up_all(&dmxdev->dvr_input_buffer.queue);
+
+		/*
+		 * There might be dmx filters reading now from DVR
+		 * device, in PULL mode, they might be also stalled
+		 * on output, signal to them that DVR is exiting.
+		 */
+		if (dmxdev->playback_mode == DMX_PB_MODE_PULL) {
+			wake_up_all(&dmxdev->dvr_buffer.queue);
+
+			for (i = 0; i < dmxdev->filternum; i++)
+				if (dmxdev->filter[i].state == DMXDEV_STATE_GO)
+					wake_up_all(
+					&dmxdev->filter[i].buffer.queue);
+		}
+
+		/* notify kernel demux that we are canceling */
+		if (dmxdev->demux->write_cancel)
+			dmxdev->demux->write_cancel(dmxdev->demux);
+
+		/*
+		 * Now flush dvr-in workqueue so that no one
+		 * would process data from dvr input buffer any more
+		 * before it gets freed.
+		 */
+		flush_workqueue(dmxdev->dvr_input_workqueue);
+
+		dvbdev->writers++;
+		dmxdev->demux->disconnect_frontend(dmxdev->demux);
+		dmxdev->demux->connect_frontend(dmxdev->demux,
+						dmxdev->dvr_orig_fe);
+
+		if (dmxdev->dvr_input_buffer.data) {
+			void *mem = dmxdev->dvr_input_buffer.data;
+			mb();
+			spin_lock_irq(&dmxdev->dvr_in_lock);
+			dmxdev->dvr_input_buffer.data = NULL;
+			spin_unlock_irq(&dmxdev->dvr_in_lock);
 			vfree(mem);
 		}
 	}
@@ -216,17 +280,20 @@
 	return 0;
 }
 
-static ssize_t dvb_dvr_write(struct file *file, const char __user *buf,
-			     size_t count, loff_t *ppos)
+
+static int dvb_dvr_mmap(struct file *filp, struct vm_area_struct *vma)
 {
-	struct dvb_device *dvbdev = file->private_data;
+	struct dvb_device *dvbdev = filp->private_data;
 	struct dmxdev *dmxdev = dvbdev->priv;
+	struct dvb_ringbuffer *buffer;
+	int vma_size;
+	int buffer_size;
 	int ret;
 
-	if (!dmxdev->demux->write)
-		return -EOPNOTSUPP;
-	if ((file->f_flags & O_ACCMODE) != O_WRONLY)
+	if (((filp->f_flags & O_ACCMODE) == O_RDONLY) &&
+		(vma->vm_flags & VM_WRITE))
 		return -EINVAL;
+
 	if (mutex_lock_interruptible(&dmxdev->mutex))
 		return -ERESTARTSYS;
 
@@ -234,11 +301,114 @@
 		mutex_unlock(&dmxdev->mutex);
 		return -ENODEV;
 	}
-	ret = dmxdev->demux->write(dmxdev->demux, buf, count);
+
+	if ((filp->f_flags & O_ACCMODE) == O_RDONLY)
+		buffer = &dmxdev->dvr_buffer;
+	else
+		buffer = &dmxdev->dvr_input_buffer;
+
+	vma_size = vma->vm_end - vma->vm_start;
+
+	/* Make sure requested mapping is not larger than buffer size */
+	buffer_size = buffer->size + (PAGE_SIZE-1);
+	buffer_size = buffer_size & ~(PAGE_SIZE-1);
+
+	if (vma_size != buffer_size) {
+		mutex_unlock(&dmxdev->mutex);
+		return -EINVAL;
+	}
+
+	ret = remap_vmalloc_range(vma, buffer->data, 0);
+	if (ret) {
+		mutex_unlock(&dmxdev->mutex);
+		return ret;
+	}
+
+	vma->vm_flags |= VM_RESERVED;
+	vma->vm_flags |= VM_DONTEXPAND;
+
 	mutex_unlock(&dmxdev->mutex);
 	return ret;
 }
 
+static ssize_t dvb_dvr_write(struct file *file, const char __user *buf,
+			     size_t count, loff_t *ppos)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct dmxdev *dmxdev = dvbdev->priv;
+	struct dvb_ringbuffer *src = &dmxdev->dvr_input_buffer;
+	int ret;
+	size_t todo;
+	ssize_t free_space;
+
+	if (!dmxdev->demux->write)
+		return -EOPNOTSUPP;
+
+	if ((file->f_flags & O_ACCMODE) == O_RDONLY)
+		return -EINVAL;
+
+	if ((file->f_flags & O_NONBLOCK) &&
+		(dvb_ringbuffer_free(src) == 0))
+		return -EWOULDBLOCK;
+
+	ret = 0;
+	for (todo = count; todo > 0; todo -= ret) {
+		ret = wait_event_interruptible(src->queue,
+						   (!src->data) ||
+					       (dvb_ringbuffer_free(src)) ||
+					       (src->error != 0) ||
+					       (dmxdev->dvr_in_exit));
+
+		if (ret < 0)
+			return ret;
+
+		if (mutex_lock_interruptible(&dmxdev->mutex))
+			return -ERESTARTSYS;
+
+		if (!src->data) {
+			mutex_unlock(&dmxdev->mutex);
+			return 0;
+		}
+
+		if (dmxdev->exit || dmxdev->dvr_in_exit) {
+			mutex_unlock(&dmxdev->mutex);
+			return -ENODEV;
+		}
+
+		if (src->error) {
+			ret = src->error;
+			dvb_ringbuffer_flush(src);
+			mutex_unlock(&dmxdev->mutex);
+			wake_up_all(&src->queue);
+			return ret;
+		}
+
+		free_space = dvb_ringbuffer_free(src);
+
+		if (free_space > todo)
+			free_space = todo;
+
+		ret = dvb_ringbuffer_write_user(src, buf, free_space);
+
+		if (ret < 0) {
+			mutex_unlock(&dmxdev->mutex);
+			return ret;
+		}
+
+		buf += ret;
+
+		mutex_unlock(&dmxdev->mutex);
+
+		wake_up_all(&src->queue);
+
+		if (!work_pending(&dmxdev->dvr_input_work))
+			queue_work(dmxdev->dvr_input_workqueue,
+						&dmxdev->dvr_input_work);
+	}
+
+	return (count - todo) ? (count - todo) : ret;
+}
+
 static ssize_t dvb_dvr_read(struct file *file, char __user *buf, size_t count,
 			    loff_t *ppos)
 {
@@ -253,39 +423,223 @@
 				      buf, count, ppos);
 }
 
-static int dvb_dvr_set_buffer_size(struct dmxdev *dmxdev,
-				      unsigned long size)
+static void dvr_input_work_func(struct work_struct *worker)
 {
-	struct dvb_ringbuffer *buf = &dmxdev->dvr_buffer;
+	struct dmxdev *dmxdev =
+		container_of(worker, struct dmxdev, dvr_input_work);
+	struct dvb_ringbuffer *src = &dmxdev->dvr_input_buffer;
+	int ret;
+	size_t todo;
+	size_t split;
+
+	while (1) {
+		/* wait for input */
+		ret = wait_event_interruptible(src->queue,
+						   (!src->data) ||
+					       (dvb_ringbuffer_avail(src)) ||
+					       (src->error != 0) ||
+					       (dmxdev->dvr_in_exit));
+
+		if (ret < 0)
+			break;
+
+		spin_lock(&dmxdev->dvr_in_lock);
+
+		if (!src->data || dmxdev->exit || dmxdev->dvr_in_exit) {
+			spin_unlock(&dmxdev->dvr_in_lock);
+			break;
+		}
+
+		if (src->error) {
+			spin_unlock(&dmxdev->dvr_in_lock);
+			wake_up_all(&src->queue);
+			break;
+		}
+
+		dmxdev->dvr_processing_input = 1;
+
+		ret = dvb_ringbuffer_avail(src);
+		todo = ret;
+
+		split = (src->pread + ret > src->size) ?
+				src->size - src->pread :
+				0;
+
+		/*
+		 * In DVR PULL mode, write might block.
+		 * Lock on DVR buffer is released before calling to
+		 * write, if DVR was released meanwhile, dvr_in_exit is
+		 * prompted. Lock is aquired when updating the read pointer
+		 * again to preserve read/write pointers consistancy
+		 */
+		if (split > 0) {
+			spin_unlock(&dmxdev->dvr_in_lock);
+			dmxdev->demux->write(dmxdev->demux,
+						src->data + src->pread,
+						split);
+
+			if (dmxdev->dvr_in_exit)
+				break;
+
+			spin_lock(&dmxdev->dvr_in_lock);
+
+			todo -= split;
+			DVB_RINGBUFFER_SKIP(src, split);
+		}
+
+		spin_unlock(&dmxdev->dvr_in_lock);
+		dmxdev->demux->write(dmxdev->demux,
+					src->data + src->pread, todo);
+
+		if (dmxdev->dvr_in_exit)
+			break;
+
+		spin_lock(&dmxdev->dvr_in_lock);
+
+		DVB_RINGBUFFER_SKIP(src, todo);
+		dmxdev->dvr_processing_input = 0;
+		spin_unlock(&dmxdev->dvr_in_lock);
+
+		wake_up_all(&src->queue);
+	}
+}
+
+static int dvb_dvr_set_buffer_size(struct dmxdev *dmxdev,
+						unsigned int f_flags,
+						unsigned long size)
+{
+	struct dvb_ringbuffer *buf;
 	void *newmem;
 	void *oldmem;
+	spinlock_t *lock;
 
 	dprintk("function : %s\n", __func__);
 
+	if ((f_flags & O_ACCMODE) == O_RDONLY) {
+		buf = &dmxdev->dvr_buffer;
+		lock = &dmxdev->lock;
+	} else {
+		buf = &dmxdev->dvr_input_buffer;
+		lock = &dmxdev->dvr_in_lock;
+	}
+
 	if (buf->size == size)
 		return 0;
 	if (!size)
 		return -EINVAL;
 
-	newmem = vmalloc(size);
+	newmem = vmalloc_user(size);
 	if (!newmem)
 		return -ENOMEM;
 
 	oldmem = buf->data;
 
-	spin_lock_irq(&dmxdev->lock);
+	spin_lock_irq(lock);
+
+	if (((f_flags & O_ACCMODE) != O_RDONLY) &&
+		(dmxdev->dvr_processing_input)) {
+		spin_unlock_irq(lock);
+		vfree(oldmem);
+		return -EBUSY;
+	}
+
 	buf->data = newmem;
 	buf->size = size;
 
 	/* reset and not flush in case the buffer shrinks */
 	dvb_ringbuffer_reset(buf);
-	spin_unlock_irq(&dmxdev->lock);
+
+	spin_unlock_irq(lock);
 
 	vfree(oldmem);
 
 	return 0;
 }
 
+static int dvb_dvr_get_buffer_status(struct dmxdev *dmxdev,
+				unsigned int f_flags,
+				struct dmx_buffer_status *dmx_buffer_status)
+{
+	struct dvb_ringbuffer *buf;
+	spinlock_t *lock;
+
+	if ((f_flags & O_ACCMODE) == O_RDONLY) {
+		buf = &dmxdev->dvr_buffer;
+		lock = &dmxdev->lock;
+	} else {
+		buf = &dmxdev->dvr_input_buffer;
+		lock = &dmxdev->dvr_in_lock;
+	}
+
+	spin_lock_irq(lock);
+
+	dmx_buffer_status->error = buf->error;
+	if (buf->error)
+		dvb_ringbuffer_flush(buf);
+
+	dmx_buffer_status->fullness = dvb_ringbuffer_avail(buf);
+	dmx_buffer_status->free_bytes = dvb_ringbuffer_free(buf);
+	dmx_buffer_status->read_offset = buf->pread;
+	dmx_buffer_status->write_offset = buf->pwrite;
+	dmx_buffer_status->size = buf->size;
+
+	spin_unlock_irq(lock);
+
+	return 0;
+}
+
+static int dvb_dvr_release_data(struct dmxdev *dmxdev,
+					unsigned int f_flags,
+					u32 bytes_count)
+{
+	ssize_t buff_fullness;
+
+	if (!(f_flags & O_ACCMODE) == O_RDONLY)
+		return -EINVAL;
+
+	if (!bytes_count)
+		return 0;
+
+	buff_fullness = dvb_ringbuffer_avail(&dmxdev->dvr_buffer);
+
+	if (bytes_count > buff_fullness)
+		return -EINVAL;
+
+	DVB_RINGBUFFER_SKIP(&dmxdev->dvr_buffer, bytes_count);
+	wake_up_all(&dmxdev->dvr_buffer.queue);
+	return 0;
+}
+
+static int dvb_dvr_feed_data(struct dmxdev *dmxdev,
+					unsigned int f_flags,
+					u32 bytes_count)
+{
+	ssize_t free_space;
+	struct dvb_ringbuffer *buffer = &dmxdev->dvr_input_buffer;
+
+	if ((f_flags & O_ACCMODE) == O_RDONLY)
+		return -EINVAL;
+
+	if (!bytes_count)
+		return 0;
+
+	free_space = dvb_ringbuffer_free(buffer);
+
+	if (bytes_count > free_space)
+		return -EINVAL;
+
+	buffer->pwrite =
+		(buffer->pwrite + bytes_count) % buffer->size;
+
+	wake_up_all(&buffer->queue);
+
+	if (!work_pending(&dmxdev->dvr_input_work))
+		queue_work(dmxdev->dvr_input_workqueue,
+					&dmxdev->dvr_input_work);
+
+	return 0;
+}
+
 static inline void dvb_dmxdev_filter_state_set(struct dmxdev_filter
 					       *dmxdevfilter, int state)
 {
@@ -308,7 +662,7 @@
 	if (dmxdevfilter->state >= DMXDEV_STATE_GO)
 		return -EBUSY;
 
-	newmem = vmalloc(size);
+	newmem = vmalloc_user(size);
 	if (!newmem)
 		return -ENOMEM;
 
@@ -323,6 +677,212 @@
 	spin_unlock_irq(&dmxdevfilter->dev->lock);
 
 	vfree(oldmem);
+	return 0;
+}
+
+static int dvb_dmxdev_set_pes_buffer_size(struct dmxdev_filter *dmxdevfilter,
+					unsigned long size)
+{
+	if (dmxdevfilter->pes_buffer_size == size)
+		return 0;
+	if (!size)
+		return -EINVAL;
+	if (dmxdevfilter->state >= DMXDEV_STATE_GO)
+		return -EBUSY;
+
+	dmxdevfilter->pes_buffer_size = size;
+
+	return 0;
+}
+
+static int dvb_dmxdev_set_source(struct dmxdev_filter *dmxdevfilter,
+					dmx_source_t *source)
+{
+	struct dmxdev *dev;
+
+	if (dmxdevfilter->state == DMXDEV_STATE_GO)
+		return -EBUSY;
+
+	dev = dmxdevfilter->dev;
+
+	dev->source = *source;
+
+	if (dev->demux->set_source)
+		return dev->demux->set_source(dev->demux, source);
+
+	return 0;
+}
+
+static int dvb_dmxdev_ts_fullness_callback(
+				struct dmx_ts_feed *filter,
+				int required_space)
+{
+	struct dmxdev_filter *dmxdevfilter = filter->priv;
+	struct dvb_ringbuffer *src;
+	int ret;
+
+	if (dmxdevfilter->params.pes.output == DMX_OUT_TAP
+		|| dmxdevfilter->params.pes.output == DMX_OUT_TSDEMUX_TAP)
+		src = &dmxdevfilter->buffer;
+	else
+		src = &dmxdevfilter->dev->dvr_buffer;
+
+	do {
+		ret = 0;
+
+		if (dmxdevfilter->dev->dvr_in_exit)
+			return -ENODEV;
+
+		spin_lock(&dmxdevfilter->dev->lock);
+
+		if ((!src->data) ||
+			(dmxdevfilter->state != DMXDEV_STATE_GO))
+			ret = -EINVAL;
+		else if (src->error)
+			ret = src->error;
+
+		if (ret) {
+			spin_unlock(&dmxdevfilter->dev->lock);
+			return ret;
+		}
+
+		if (required_space <= dvb_ringbuffer_free(src)) {
+			spin_unlock(&dmxdevfilter->dev->lock);
+			return 0;
+		}
+
+		spin_unlock(&dmxdevfilter->dev->lock);
+
+		ret = wait_event_interruptible(src->queue,
+				(!src->data) ||
+				(dvb_ringbuffer_free(src) >= required_space) ||
+				(src->error != 0) ||
+				(dmxdevfilter->state != DMXDEV_STATE_GO) ||
+				dmxdevfilter->dev->dvr_in_exit);
+
+		if (ret < 0)
+			return ret;
+	} while (1);
+}
+
+static int dvb_dmxdev_sec_fullness_callback(
+				struct dmx_section_filter *filter,
+				int required_space)
+{
+	struct dmxdev_filter *dmxdevfilter = filter->priv;
+	struct dvb_ringbuffer *src = &dmxdevfilter->buffer;
+	int ret;
+
+	do {
+		ret = 0;
+
+		if (dmxdevfilter->dev->dvr_in_exit)
+			return -ENODEV;
+
+		spin_lock(&dmxdevfilter->dev->lock);
+
+		if ((!src->data) ||
+			(dmxdevfilter->state != DMXDEV_STATE_GO))
+			ret = -EINVAL;
+		else if (src->error)
+			ret = src->error;
+
+		if (ret) {
+			spin_unlock(&dmxdevfilter->dev->lock);
+			return ret;
+		}
+
+		if (required_space <= dvb_ringbuffer_free(src)) {
+			spin_unlock(&dmxdevfilter->dev->lock);
+			return 0;
+		}
+
+		spin_unlock(&dmxdevfilter->dev->lock);
+
+		ret = wait_event_interruptible(src->queue,
+				(!src->data) ||
+				(dvb_ringbuffer_free(src) >= required_space) ||
+				(src->error != 0) ||
+				(dmxdevfilter->state != DMXDEV_STATE_GO) ||
+				dmxdevfilter->dev->dvr_in_exit);
+
+		if (ret < 0)
+			return ret;
+	} while (1);
+}
+
+static int dvb_dmxdev_set_playback_mode(struct dmxdev_filter *dmxdevfilter,
+					enum dmx_playback_mode_t playback_mode)
+{
+	struct dmxdev *dmxdev = dmxdevfilter->dev;
+
+	if ((playback_mode != DMX_PB_MODE_PUSH) &&
+		(playback_mode != DMX_PB_MODE_PULL))
+		return -EINVAL;
+
+	if (((dmxdev->source < DMX_SOURCE_DVR0) ||
+		!dmxdev->demux->set_playback_mode ||
+		!(dmxdev->capabilities & DMXDEV_CAP_PULL_MODE)) &&
+		(playback_mode == DMX_PB_MODE_PULL))
+		return -EPERM;
+
+	if (dmxdevfilter->state == DMXDEV_STATE_GO)
+		return -EBUSY;
+
+	dmxdev->playback_mode = playback_mode;
+
+	return dmxdev->demux->set_playback_mode(
+				dmxdev->demux,
+				dmxdev->playback_mode,
+				dvb_dmxdev_ts_fullness_callback,
+				dvb_dmxdev_sec_fullness_callback);
+}
+
+static int dvb_dmxdev_get_buffer_status(
+		struct dmxdev_filter *dmxdevfilter,
+		struct dmx_buffer_status *dmx_buffer_status)
+{
+	struct dvb_ringbuffer *buf = &dmxdevfilter->buffer;
+
+	if (!buf->data)
+		return -EINVAL;
+
+	spin_lock_irq(&dmxdevfilter->dev->lock);
+
+	dmx_buffer_status->error = buf->error;
+	if (buf->error)
+		dvb_ringbuffer_flush(buf);
+
+	dmx_buffer_status->fullness = dvb_ringbuffer_avail(buf);
+	dmx_buffer_status->free_bytes = dvb_ringbuffer_free(buf);
+	dmx_buffer_status->read_offset = buf->pread;
+	dmx_buffer_status->write_offset = buf->pwrite;
+	dmx_buffer_status->size = buf->size;
+
+	spin_unlock_irq(&dmxdevfilter->dev->lock);
+
+	return 0;
+}
+
+static int dvb_dmxdev_release_data(struct dmxdev_filter *dmxdevfilter,
+					u32 bytes_count)
+{
+	ssize_t buff_fullness;
+
+	if (!dmxdevfilter->buffer.data)
+		return -EINVAL;
+
+	if (!bytes_count)
+		return 0;
+
+	buff_fullness = dvb_ringbuffer_avail(&dmxdevfilter->buffer);
+
+	if (bytes_count > buff_fullness)
+		return -EINVAL;
+
+	DVB_RINGBUFFER_SKIP(&dmxdevfilter->buffer, bytes_count);
+
+	wake_up_all(&dmxdevfilter->buffer.queue);
 
 	return 0;
 }
@@ -335,7 +895,7 @@
 	spin_lock_irq(&dmxdevfilter->dev->lock);
 	dmxdevfilter->state = DMXDEV_STATE_TIMEDOUT;
 	spin_unlock_irq(&dmxdevfilter->dev->lock);
-	wake_up(&dmxdevfilter->buffer.queue);
+	wake_up_all(&dmxdevfilter->buffer.queue);
 }
 
 static void dvb_dmxdev_filter_timer(struct dmxdev_filter *dmxdevfilter)
@@ -361,7 +921,7 @@
 	int ret;
 
 	if (dmxdevfilter->buffer.error) {
-		wake_up(&dmxdevfilter->buffer.queue);
+		wake_up_all(&dmxdevfilter->buffer.queue);
 		return 0;
 	}
 	spin_lock(&dmxdevfilter->dev->lock);
@@ -386,7 +946,7 @@
 	if (dmxdevfilter->params.sec.flags & DMX_ONESHOT)
 		dmxdevfilter->state = DMXDEV_STATE_DONE;
 	spin_unlock(&dmxdevfilter->dev->lock);
-	wake_up(&dmxdevfilter->buffer.queue);
+	wake_up_all(&dmxdevfilter->buffer.queue);
 	return 0;
 }
 
@@ -401,18 +961,34 @@
 
 	spin_lock(&dmxdevfilter->dev->lock);
 	if (dmxdevfilter->params.pes.output == DMX_OUT_DECODER) {
-		spin_unlock(&dmxdevfilter->dev->lock);
-		return 0;
-	}
-
-	if (dmxdevfilter->params.pes.output == DMX_OUT_TAP
+		if ((dmxdevfilter->dev->capabilities &
+			DMXDEV_CAP_PCR_EXTRACTION) &&
+			((dmxdevfilter->params.pes.pes_type == DMX_PES_PCR0) ||
+			(dmxdevfilter->params.pes.pes_type == DMX_PES_PCR1) ||
+			(dmxdevfilter->params.pes.pes_type == DMX_PES_PCR2) ||
+			(dmxdevfilter->params.pes.pes_type == DMX_PES_PCR3))) {
+			/*
+			 * Support for reporting PCR and STC pairs to user.
+			 * Reported data should have the following format:
+			 * <8 bit flags><64 bits of STC> <64bits of PCR>
+			 * STC and PCR values are in 27MHz.
+			 * The current flags that are defined:
+			 * 0x00000001: discontinuity_indicator
+			 */
+			buffer = &dmxdevfilter->buffer;
+		} else {
+			spin_unlock(&dmxdevfilter->dev->lock);
+			return 0;
+		}
+	} else if (dmxdevfilter->params.pes.output == DMX_OUT_TAP
 	    || dmxdevfilter->params.pes.output == DMX_OUT_TSDEMUX_TAP)
 		buffer = &dmxdevfilter->buffer;
 	else
 		buffer = &dmxdevfilter->dev->dvr_buffer;
+
 	if (buffer->error) {
 		spin_unlock(&dmxdevfilter->dev->lock);
-		wake_up(&buffer->queue);
+		wake_up_all(&buffer->queue);
 		return 0;
 	}
 	ret = dvb_dmxdev_buffer_write(buffer, buffer1, buffer1_len);
@@ -423,7 +999,7 @@
 		buffer->error = ret;
 	}
 	spin_unlock(&dmxdevfilter->dev->lock);
-	wake_up(&buffer->queue);
+	wake_up_all(&buffer->queue);
 	return 0;
 }
 
@@ -533,6 +1109,8 @@
 	}
 
 	dvb_ringbuffer_flush(&dmxdevfilter->buffer);
+	wake_up_all(&dmxdevfilter->buffer.queue);
+
 	return 0;
 }
 
@@ -599,12 +1177,32 @@
 	tsfeed = feed->ts;
 	tsfeed->priv = filter;
 
-	ret = tsfeed->set(tsfeed, feed->pid, ts_type, ts_pes, 32768, timeout);
+	ret = tsfeed->set(tsfeed, feed->pid,
+					ts_type, ts_pes,
+					filter->pes_buffer_size, timeout);
 	if (ret < 0) {
 		dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed);
 		return ret;
 	}
 
+	/* Support indexing for video PES */
+	if ((para->pes_type == DMX_PES_VIDEO0) ||
+	    (para->pes_type == DMX_PES_VIDEO1) ||
+	    (para->pes_type == DMX_PES_VIDEO2) ||
+	    (para->pes_type == DMX_PES_VIDEO3)) {
+
+		if (tsfeed->set_indexing_params) {
+			ret = tsfeed->set_indexing_params(tsfeed,
+							&para->video_params);
+
+			if (ret < 0) {
+				dmxdev->demux->release_ts_feed(dmxdev->demux,
+								tsfeed);
+				return ret;
+			}
+		}
+	}
+
 	ret = tsfeed->start_filtering(tsfeed);
 	if (ret < 0) {
 		dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed);
@@ -628,7 +1226,7 @@
 		dvb_dmxdev_filter_stop(filter);
 
 	if (!filter->buffer.data) {
-		mem = vmalloc(filter->buffer.size);
+		mem = vmalloc_user(filter->buffer.size);
 		if (!mem)
 			return -ENOMEM;
 		spin_lock_irq(&filter->dev->lock);
@@ -648,7 +1246,6 @@
 		*secfilter = NULL;
 		*secfeed = NULL;
 
-
 		/* find active filter/feed with same PID */
 		for (i = 0; i < dmxdev->filternum; i++) {
 			if (dmxdev->filter[i].state >= DMXDEV_STATE_GO &&
@@ -762,6 +1359,8 @@
 	dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
 	init_timer(&dmxdevfilter->timer);
 
+	dmxdevfilter->pes_buffer_size = 32768;
+
 	dvbdev->users++;
 
 	mutex_unlock(&dmxdev->mutex);
@@ -787,7 +1386,7 @@
 	}
 
 	dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_FREE);
-	wake_up(&dmxdevfilter->buffer.queue);
+	wake_up_all(&dmxdevfilter->buffer.queue);
 	mutex_unlock(&dmxdevfilter->mutex);
 	mutex_unlock(&dmxdev->mutex);
 	return 0;
@@ -882,6 +1481,23 @@
 	if (params->pes_type > DMX_PES_OTHER || params->pes_type < 0)
 		return -EINVAL;
 
+	if (params->flags & DMX_ENABLE_INDEXING) {
+		if (!(dmxdev->capabilities & DMXDEV_CAP_INDEXING))
+			return -EINVAL;
+
+		/* can do indexing only on video PES */
+		if ((params->pes_type != DMX_PES_VIDEO0) &&
+		    (params->pes_type != DMX_PES_VIDEO1) &&
+		    (params->pes_type != DMX_PES_VIDEO2) &&
+		    (params->pes_type != DMX_PES_VIDEO3))
+			return -EINVAL;
+
+		/* can do indexing only when recording */
+		if ((params->output != DMX_OUT_TS_TAP) &&
+		    (params->output != DMX_OUT_TSDEMUX_TAP))
+			return -EINVAL;
+	}
+
 	dmxdevfilter->type = DMXDEV_TYPE_PES;
 	memcpy(&dmxdevfilter->params, params,
 	       sizeof(struct dmx_pes_filter_params));
@@ -1022,6 +1638,24 @@
 		mutex_unlock(&dmxdevfilter->mutex);
 		break;
 
+	case DMX_GET_BUFFER_STATUS:
+		if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+			mutex_unlock(&dmxdev->mutex);
+			return -ERESTARTSYS;
+		}
+		ret = dvb_dmxdev_get_buffer_status(dmxdevfilter, parg);
+		mutex_unlock(&dmxdevfilter->mutex);
+		break;
+
+	case DMX_RELEASE_DATA:
+		if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+			mutex_unlock(&dmxdev->mutex);
+			return -ERESTARTSYS;
+		}
+		ret = dvb_dmxdev_release_data(dmxdevfilter, arg);
+		mutex_unlock(&dmxdevfilter->mutex);
+		break;
+
 	case DMX_GET_PES_PIDS:
 		if (!dmxdev->demux->get_pes_pids) {
 			ret = -EINVAL;
@@ -1039,11 +1673,59 @@
 		break;
 
 	case DMX_SET_SOURCE:
-		if (!dmxdev->demux->set_source) {
+		if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+			mutex_unlock(&dmxdev->mutex);
+			return -ERESTARTSYS;
+		}
+		ret = dvb_dmxdev_set_source(dmxdevfilter, parg);
+		mutex_unlock(&dmxdevfilter->mutex);
+		break;
+
+	case DMX_SET_TS_PACKET_FORMAT:
+		if (!dmxdev->demux->set_tsp_format) {
 			ret = -EINVAL;
 			break;
 		}
-		ret = dmxdev->demux->set_source(dmxdev->demux, parg);
+
+		if (dmxdevfilter->state >= DMXDEV_STATE_GO) {
+			ret = -EBUSY;
+			break;
+		}
+		ret = dmxdev->demux->set_tsp_format(
+				dmxdev->demux,
+				*(enum dmx_tsp_format_t *)parg);
+		break;
+
+	case DMX_SET_TS_OUT_FORMAT:
+		if (!dmxdev->demux->set_tsp_out_format) {
+			ret = -EINVAL;
+			break;
+		}
+
+		if (dmxdevfilter->state >= DMXDEV_STATE_GO) {
+			ret = -EBUSY;
+			break;
+		}
+
+		ret = dmxdev->demux->set_tsp_out_format(
+				dmxdev->demux,
+				*(enum dmx_tsp_format_t *)parg);
+		break;
+
+	case DMX_SET_DECODER_BUFFER_SIZE:
+		if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+			mutex_unlock(&dmxdev->mutex);
+			return -ERESTARTSYS;
+		}
+
+		ret = dvb_dmxdev_set_pes_buffer_size(dmxdevfilter, arg);
+		mutex_unlock(&dmxdevfilter->mutex);
+		break;
+
+	case DMX_SET_PLAYBACK_MODE:
+		ret = dvb_dmxdev_set_playback_mode(
+				dmxdevfilter,
+				*(enum dmx_playback_mode_t *)parg);
 		break;
 
 	case DMX_GET_STC:
@@ -1113,6 +1795,59 @@
 	return mask;
 }
 
+static int dvb_demux_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	struct dmxdev_filter *dmxdevfilter = filp->private_data;
+	struct dmxdev *dmxdev = dmxdevfilter->dev;
+	int ret;
+	int vma_size;
+	int buffer_size;
+
+	vma_size = vma->vm_end - vma->vm_start;
+
+	if (vma->vm_flags & VM_WRITE)
+		return -EINVAL;
+
+	if (mutex_lock_interruptible(&dmxdev->mutex))
+		return -ERESTARTSYS;
+
+	if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+		mutex_unlock(&dmxdev->mutex);
+		return -ERESTARTSYS;
+	}
+
+	if (!dmxdevfilter->buffer.data) {
+		mutex_unlock(&dmxdevfilter->mutex);
+		mutex_unlock(&dmxdev->mutex);
+		return -EINVAL;
+	}
+
+	/* Make sure requested mapping is not larger than buffer size */
+	buffer_size = dmxdevfilter->buffer.size + (PAGE_SIZE-1);
+	buffer_size = buffer_size & ~(PAGE_SIZE-1);
+
+	if (vma_size != buffer_size) {
+		mutex_unlock(&dmxdevfilter->mutex);
+		mutex_unlock(&dmxdev->mutex);
+		return -EINVAL;
+	}
+
+	ret = remap_vmalloc_range(vma, dmxdevfilter->buffer.data, 0);
+	if (ret) {
+		mutex_unlock(&dmxdevfilter->mutex);
+		mutex_unlock(&dmxdev->mutex);
+		return ret;
+	}
+
+	vma->vm_flags |= VM_RESERVED;
+	vma->vm_flags |= VM_DONTEXPAND;
+
+	mutex_unlock(&dmxdevfilter->mutex);
+	mutex_unlock(&dmxdev->mutex);
+
+	return 0;
+}
+
 static int dvb_demux_release(struct inode *inode, struct file *file)
 {
 	struct dmxdev_filter *dmxdevfilter = file->private_data;
@@ -1143,6 +1878,7 @@
 	.release = dvb_demux_release,
 	.poll = dvb_demux_poll,
 	.llseek = default_llseek,
+	.mmap = dvb_demux_mmap,
 };
 
 static struct dvb_device dvbdev_demux = {
@@ -1165,7 +1901,19 @@
 
 	switch (cmd) {
 	case DMX_SET_BUFFER_SIZE:
-		ret = dvb_dvr_set_buffer_size(dmxdev, arg);
+		ret = dvb_dvr_set_buffer_size(dmxdev, file->f_flags, arg);
+		break;
+
+	case DMX_GET_BUFFER_STATUS:
+		ret = dvb_dvr_get_buffer_status(dmxdev, file->f_flags, parg);
+		break;
+
+	case DMX_RELEASE_DATA:
+		ret = dvb_dvr_release_data(dmxdev, file->f_flags, arg);
+		break;
+
+	case DMX_FEED_DATA:
+		ret = dvb_dvr_feed_data(dmxdev, file->f_flags, arg);
 		break;
 
 	default:
@@ -1190,16 +1938,22 @@
 
 	dprintk("function : %s\n", __func__);
 
-	poll_wait(file, &dmxdev->dvr_buffer.queue, wait);
-
 	if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
+		poll_wait(file, &dmxdev->dvr_buffer.queue, wait);
+
 		if (dmxdev->dvr_buffer.error)
 			mask |= (POLLIN | POLLRDNORM | POLLPRI | POLLERR);
 
 		if (!dvb_ringbuffer_empty(&dmxdev->dvr_buffer))
 			mask |= (POLLIN | POLLRDNORM | POLLPRI);
-	} else
-		mask |= (POLLOUT | POLLWRNORM | POLLPRI);
+	} else {
+		poll_wait(file, &dmxdev->dvr_input_buffer.queue, wait);
+		if (dmxdev->dvr_input_buffer.error)
+			mask |= (POLLOUT | POLLRDNORM | POLLPRI | POLLERR);
+
+		if (dvb_ringbuffer_free(&dmxdev->dvr_input_buffer))
+			mask |= (POLLOUT | POLLRDNORM | POLLPRI);
+	}
 
 	return mask;
 }
@@ -1208,6 +1962,7 @@
 	.owner = THIS_MODULE,
 	.read = dvb_dvr_read,
 	.write = dvb_dvr_write,
+	.mmap = dvb_dvr_mmap,
 	.unlocked_ioctl = dvb_dvr_ioctl,
 	.open = dvb_dvr_open,
 	.release = dvb_dvr_release,
@@ -1233,8 +1988,19 @@
 	if (!dmxdev->filter)
 		return -ENOMEM;
 
+	dmxdev->dvr_input_workqueue =
+		create_singlethread_workqueue("dvr_workqueue");
+
+	if (dmxdev->dvr_input_workqueue == NULL) {
+		vfree(dmxdev->filter);
+		return -ENOMEM;
+	}
+
+	dmxdev->playback_mode = DMX_PB_MODE_PUSH;
+
 	mutex_init(&dmxdev->mutex);
 	spin_lock_init(&dmxdev->lock);
+	spin_lock_init(&dmxdev->dvr_in_lock);
 	for (i = 0; i < dmxdev->filternum; i++) {
 		dmxdev->filter[i].dev = dmxdev;
 		dmxdev->filter[i].buffer.data = NULL;
@@ -1248,6 +2014,10 @@
 			    dmxdev, DVB_DEVICE_DVR);
 
 	dvb_ringbuffer_init(&dmxdev->dvr_buffer, NULL, 8192);
+	dvb_ringbuffer_init(&dmxdev->dvr_input_buffer, NULL, 8192);
+
+	INIT_WORK(&dmxdev->dvr_input_work,
+			  dvr_input_work_func);
 
 	return 0;
 }
@@ -1266,6 +2036,9 @@
 				dmxdev->dvr_dvbdev->users==1);
 	}
 
+	flush_workqueue(dmxdev->dvr_input_workqueue);
+	destroy_workqueue(dmxdev->dvr_input_workqueue);
+
 	dvb_unregister_device(dmxdev->dvbdev);
 	dvb_unregister_device(dmxdev->dvr_dvbdev);
 
diff --git a/drivers/media/dvb/dvb-core/dmxdev.h b/drivers/media/dvb/dvb-core/dmxdev.h
index 02ebe28..4c52e84 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.h
+++ b/drivers/media/dvb/dvb-core/dmxdev.h
@@ -4,6 +4,8 @@
  * Copyright (C) 2000 Ralph Metzler & Marcus Metzler
  *                    for convergence integrated media GmbH
  *
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License
  * as published by the Free Software Foundation; either version 2.1
@@ -32,7 +34,7 @@
 #include <linux/string.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
-
+#include <linux/workqueue.h>
 #include <linux/dvb/dmx.h>
 
 #include "dvbdev.h"
@@ -83,14 +85,18 @@
 
 	struct mutex mutex;
 
+	/* relevent for decoder PES */
+	unsigned long pes_buffer_size;
+
 	/* only for sections */
 	struct timer_list timer;
 	int todo;
 	u8 secheader[3];
 };
 
-
 struct dmxdev {
+	struct work_struct dvr_input_work;
+
 	struct dvb_device *dvbdev;
 	struct dvb_device *dvr_dvbdev;
 
@@ -99,16 +105,29 @@
 
 	int filternum;
 	int capabilities;
+#define DMXDEV_CAP_DUPLEX			0x1
+#define DMXDEV_CAP_PULL_MODE		0x2
+#define DMXDEV_CAP_PCR_EXTRACTION	0x4
+#define DMXDEV_CAP_INDEXING		0x8
+
+	enum dmx_playback_mode_t playback_mode;
+	dmx_source_t source;
 
 	unsigned int exit:1;
-#define DMXDEV_CAP_DUPLEX 1
+	unsigned int dvr_in_exit:1;
+	unsigned int dvr_processing_input:1;
+
 	struct dmx_frontend *dvr_orig_fe;
 
 	struct dvb_ringbuffer dvr_buffer;
+	struct dvb_ringbuffer dvr_input_buffer;
+	struct workqueue_struct *dvr_input_workqueue;
+
 #define DVR_BUFFER_SIZE (10*188*1024)
 
 	struct mutex mutex;
 	spinlock_t lock;
+	spinlock_t dvr_in_lock;
 };
 
 
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c
index faa3671..0ff2a55 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.c
+++ b/drivers/media/dvb/dvb-core/dvb_demux.c
@@ -5,6 +5,8 @@
  *		       & Marcus Metzler <marcus@convergence.de>
  *			 for convergence integrated media GmbH
  *
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License
  * as published by the Free Software Foundation; either version 2.1
@@ -50,6 +52,14 @@
 MODULE_PARM_DESC(dvb_demux_speedcheck,
 		"enable transport stream speed check");
 
+/* counter advancing for each new dvb-demux device */
+static int dvb_demux_index;
+
+static int dvb_demux_performancecheck;
+module_param(dvb_demux_performancecheck, int, 0644);
+MODULE_PARM_DESC(dvb_demux_performancecheck,
+		"enable transport stream performance check, reported through debugfs");
+
 #define dprintk_tscheck(x...) do {                              \
 		if (dvb_demux_tscheck && printk_ratelimit())    \
 			printk(x);                              \
@@ -95,6 +105,19 @@
 	memcpy(d, s, len);
 }
 
+static u32 dvb_dmx_calc_time_delta(struct timespec past_time)
+{
+	struct timespec curr_time, delta_time;
+	u64 delta_time_us;
+
+	curr_time = current_kernel_time();
+	delta_time = timespec_sub(curr_time, past_time);
+	delta_time_us = ((s64)delta_time.tv_sec * USEC_PER_SEC) +
+					delta_time.tv_nsec / 1000;
+
+	return (u32)delta_time_us;
+}
+
 /******************************************************************************
  * Software filter functions
  ******************************************************************************/
@@ -120,8 +143,14 @@
 		printk("missed packet!\n");
 	*/
 
-	if (buf[1] & 0x40)	// PUSI ?
+	/* PUSI ? */
+	if (buf[1] & 0x40) {
 		feed->peslen = 0xfffa;
+		feed->pusi_seen = 1;
+	}
+
+	if (feed->pusi_seen == 0)
+		return 0;
 
 	feed->peslen += count;
 
@@ -164,10 +193,23 @@
 		return 0;
 
 	if (sec->check_crc) {
+		struct timespec pre_crc_time;
+
+		if (dvb_demux_performancecheck)
+			pre_crc_time = current_kernel_time();
+
 		section_syntax_indicator = ((sec->secbuf[1] & 0x80) != 0);
 		if (section_syntax_indicator &&
-		    demux->check_crc32(feed, sec->secbuf, sec->seclen))
+		    demux->check_crc32(feed, sec->secbuf, sec->seclen)) {
+			if (dvb_demux_performancecheck)
+				demux->total_crc_time +=
+					dvb_dmx_calc_time_delta(pre_crc_time);
 			return -1;
+		}
+
+		if (dvb_demux_performancecheck)
+			demux->total_crc_time +=
+				dvb_dmx_calc_time_delta(pre_crc_time);
 	}
 
 	do {
@@ -351,6 +393,161 @@
 	return 0;
 }
 
+static inline void dvb_dmx_swfilter_output_packet(
+	struct dvb_demux_feed *feed,
+	const u8 *buf)
+{
+	u8 time_stamp[4] = {0};
+	struct dvb_demux *demux = feed->demux;
+
+	/*
+	 * if we output 192 packet with timestamp at head of packet,
+	 * output the timestamp now before the 188 TS packet
+	 */
+	if (demux->tsp_out_format == DMX_TSP_FORMAT_192_HEAD)
+		feed->cb.ts(time_stamp, 4, NULL, 0, &feed->feed.ts, DMX_OK);
+
+	feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, DMX_OK);
+
+	/*
+	 * if we output 192 packet with timestamp at tail of packet,
+	 * output the timestamp now after the 188 TS packet
+	 */
+	if (demux->tsp_out_format == DMX_TSP_FORMAT_192_TAIL)
+		feed->cb.ts(time_stamp, 4, NULL, 0, &feed->feed.ts, DMX_OK);
+}
+
+static inline void dvb_dmx_configure_decoder_fullness(
+						struct dvb_demux *demux,
+						int initialize)
+{
+	struct dvb_demux_feed *feed;
+	int j;
+
+	for (j = 0; j < demux->feednum; j++) {
+		feed = &demux->feed[j];
+
+		if ((feed->state != DMX_STATE_GO) ||
+			(feed->type != DMX_TYPE_TS) ||
+			!(feed->ts_type & TS_DECODER))
+			continue;
+
+		if (initialize) {
+			if (demux->decoder_fullness_init)
+				demux->decoder_fullness_init(feed);
+		} else {
+			if (demux->decoder_fullness_abort)
+				demux->decoder_fullness_abort(feed);
+		}
+	}
+}
+
+static inline int dvb_dmx_swfilter_buffer_check(
+					struct dvb_demux *demux,
+					u16 pid)
+{
+	int desired_space;
+	int ret;
+	struct dmx_ts_feed *ts;
+	struct dvb_demux_filter *f;
+	struct dvb_demux_feed *feed;
+	int was_locked;
+	int i, j;
+
+	if (likely(spin_is_locked(&demux->lock)))
+		was_locked = 1;
+	else
+		was_locked = 0;
+
+	/*
+	 * Check that there's enough free space for data output.
+	 * If there no space, wait for it (block).
+	 * Since this function is called while spinlock
+	 * is aquired, the lock should be released first.
+	 * Once we get control back, lock is aquired back
+	 * and checks that the filter is still valid.
+	 */
+	for (j = 0; j < demux->feednum; j++) {
+		feed = &demux->feed[j];
+
+		if (demux->sw_filter_abort)
+			return -ENODEV;
+
+		if ((feed->state != DMX_STATE_GO) ||
+			((feed->pid != pid) && (feed->pid != 0x2000)))
+			continue;
+
+		if (feed->type == DMX_TYPE_TS) {
+			desired_space = 192; /* upper bound */
+			ts = &feed->feed.ts;
+
+			if (feed->ts_type & TS_PACKET) {
+				if (likely(was_locked))
+					spin_unlock(&demux->lock);
+
+				ret = demux->buffer_ctrl.ts(ts, desired_space);
+
+				if (likely(was_locked))
+					spin_lock(&demux->lock);
+
+				if (ret < 0)
+					continue;
+			}
+
+			if (demux->sw_filter_abort)
+				return -ENODEV;
+
+			if (!ts->is_filtering)
+				continue;
+
+			if ((feed->ts_type & TS_DECODER) &&
+				(demux->decoder_fullness_wait)) {
+				if (likely(was_locked))
+					spin_unlock(&demux->lock);
+
+				ret = demux->decoder_fullness_wait(
+								feed,
+								desired_space);
+
+				if (likely(was_locked))
+					spin_lock(&demux->lock);
+
+				if (ret < 0)
+					continue;
+			}
+
+			continue;
+		}
+
+		/* else - section case */
+		desired_space = feed->feed.sec.tsfeedp + 188; /* upper bound */
+		for (i = 0; i < demux->filternum; i++) {
+			if (demux->sw_filter_abort)
+				return -EPERM;
+
+			if (!feed->feed.sec.is_filtering)
+				continue;
+
+			f = &demux->filter[i];
+			if (f->feed != feed)
+				continue;
+
+			if (likely(was_locked))
+				spin_unlock(&demux->lock);
+
+			ret = demux->buffer_ctrl.sec(&f->filter, desired_space);
+
+			if (likely(was_locked))
+				spin_lock(&demux->lock);
+
+			if (ret < 0)
+				break;
+		}
+	}
+
+	return 0;
+}
+
 static inline void dvb_dmx_swfilter_packet_type(struct dvb_demux_feed *feed,
 						const u8 *buf)
 {
@@ -362,8 +559,7 @@
 			if (feed->ts_type & TS_PAYLOAD_ONLY)
 				dvb_dmx_swfilter_payload(feed, buf);
 			else
-				feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts,
-					    DMX_OK);
+				dvb_dmx_swfilter_output_packet(feed, buf);
 		}
 		if (feed->ts_type & TS_DECODER)
 			if (feed->demux->write_to_decoder)
@@ -446,6 +642,10 @@
 		/* end check */
 	};
 
+	if (demux->playback_mode == DMX_PB_MODE_PULL)
+		if (dvb_dmx_swfilter_buffer_check(demux, pid) < 0)
+			return;
+
 	list_for_each_entry(feed, &demux->feed_list, list_head) {
 		if ((feed->pid != pid) && (feed->pid != 0x2000))
 			continue;
@@ -457,16 +657,25 @@
 
 		if (feed->pid == pid)
 			dvb_dmx_swfilter_packet_type(feed, buf);
-		else if (feed->pid == 0x2000)
-			feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, DMX_OK);
+		else if ((feed->pid == 0x2000) &&
+			     (feed->feed.ts.is_filtering))
+			dvb_dmx_swfilter_output_packet(feed, buf);
 	}
 }
 
 void dvb_dmx_swfilter_packets(struct dvb_demux *demux, const u8 *buf,
 			      size_t count)
 {
+	struct timespec pre_time;
+
+	if (dvb_demux_performancecheck)
+		pre_time = current_kernel_time();
+
 	spin_lock(&demux->lock);
 
+	demux->sw_filter_abort = 0;
+	dvb_dmx_configure_decoder_fullness(demux, 1);
+
 	while (count--) {
 		if (buf[0] == 0x47)
 			dvb_dmx_swfilter_packet(demux, buf);
@@ -474,18 +683,24 @@
 	}
 
 	spin_unlock(&demux->lock);
+
+	if (dvb_demux_performancecheck)
+		demux->total_process_time += dvb_dmx_calc_time_delta(pre_time);
 }
 
 EXPORT_SYMBOL(dvb_dmx_swfilter_packets);
 
 static inline int find_next_packet(const u8 *buf, int pos, size_t count,
-				   const int pktsize)
+				   const int pktsize, const int leadingbytes)
 {
 	int start = pos, lost;
 
 	while (pos < count) {
-		if (buf[pos] == 0x47 ||
-		    (pktsize == 204 && buf[pos] == 0xB8))
+		if ((buf[pos] == 0x47 && !leadingbytes) ||
+		    (pktsize == 204 && buf[pos] == 0xB8) ||
+			(pktsize == 192 && leadingbytes &&
+			 (pos+leadingbytes < count) &&
+				buf[pos+leadingbytes] == 0x47))
 			break;
 		pos++;
 	}
@@ -495,7 +710,9 @@
 		/* This garbage is part of a valid packet? */
 		int backtrack = pos - pktsize;
 		if (backtrack >= 0 && (buf[backtrack] == 0x47 ||
-		    (pktsize == 204 && buf[backtrack] == 0xB8)))
+		    (pktsize == 204 && buf[backtrack] == 0xB8) ||
+			(pktsize == 192 &&
+			buf[backtrack+leadingbytes] == 0x47)))
 			return backtrack;
 	}
 
@@ -504,13 +721,20 @@
 
 /* Filter all pktsize= 188 or 204 sized packets and skip garbage. */
 static inline void _dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf,
-		size_t count, const int pktsize)
+		size_t count, const int pktsize, const int leadingbytes)
 {
 	int p = 0, i, j;
 	const u8 *q;
+	struct timespec pre_time;
+
+	if (dvb_demux_performancecheck)
+		pre_time = current_kernel_time();
 
 	spin_lock(&demux->lock);
 
+	demux->sw_filter_abort = 0;
+	dvb_dmx_configure_decoder_fullness(demux, 1);
+
 	if (demux->tsbufp) { /* tsbuf[0] is now 0x47. */
 		i = demux->tsbufp;
 		j = pktsize - i;
@@ -520,14 +744,22 @@
 			goto bailout;
 		}
 		memcpy(&demux->tsbuf[i], buf, j);
-		if (demux->tsbuf[0] == 0x47) /* double check */
+		if (pktsize == 192 &&
+			leadingbytes &&
+			demux->tsbuf[leadingbytes] == 0x47)  /* double check */
+			dvb_dmx_swfilter_packet(demux, demux->tsbuf+4);
+		else if (demux->tsbuf[0] == 0x47) /* double check */
 			dvb_dmx_swfilter_packet(demux, demux->tsbuf);
 		demux->tsbufp = 0;
 		p += j;
 	}
 
 	while (1) {
-		p = find_next_packet(buf, p, count, pktsize);
+		p = find_next_packet(buf, p, count, pktsize, leadingbytes);
+
+		if (demux->sw_filter_abort)
+			goto bailout;
+
 		if (p >= count)
 			break;
 		if (count - p < pktsize)
@@ -540,6 +772,10 @@
 			demux->tsbuf[0] = 0x47;
 			q = demux->tsbuf;
 		}
+
+		if (pktsize == 192 && leadingbytes)
+			q = &buf[p+leadingbytes];
+
 		dvb_dmx_swfilter_packet(demux, q);
 		p += pktsize;
 	}
@@ -554,20 +790,55 @@
 
 bailout:
 	spin_unlock(&demux->lock);
+
+	if (dvb_demux_performancecheck)
+		demux->total_process_time += dvb_dmx_calc_time_delta(pre_time);
 }
 
 void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count)
 {
-	_dvb_dmx_swfilter(demux, buf, count, 188);
+	_dvb_dmx_swfilter(demux, buf, count, 188, 0);
 }
 EXPORT_SYMBOL(dvb_dmx_swfilter);
 
 void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, size_t count)
 {
-	_dvb_dmx_swfilter(demux, buf, count, 204);
+	_dvb_dmx_swfilter(demux, buf, count, 204, 0);
 }
 EXPORT_SYMBOL(dvb_dmx_swfilter_204);
 
+void dvb_dmx_swfilter_format(
+			struct dvb_demux *demux,
+			const u8 *buf,
+			size_t count,
+			enum dmx_tsp_format_t tsp_format)
+{
+	switch (tsp_format) {
+	case DMX_TSP_FORMAT_188:
+		_dvb_dmx_swfilter(demux, buf, count, 188, 0);
+		break;
+
+	case DMX_TSP_FORMAT_192_TAIL:
+		_dvb_dmx_swfilter(demux, buf, count, 192, 0);
+		break;
+
+	case DMX_TSP_FORMAT_192_HEAD:
+		_dvb_dmx_swfilter(demux, buf, count, 192, 4);
+		break;
+
+	case DMX_TSP_FORMAT_204:
+		_dvb_dmx_swfilter(demux, buf, count, 204, 0);
+		break;
+
+	default:
+		printk(KERN_ERR "%s: invalid TS packet format (format=%d)\n",
+			   __func__,
+			   tsp_format);
+		break;
+	}
+}
+EXPORT_SYMBOL(dvb_dmx_swfilter_format);
+
 static struct dvb_demux_filter *dvb_dmx_filter_alloc(struct dvb_demux *demux)
 {
 	int i;
@@ -756,6 +1027,18 @@
 	return ret;
 }
 
+static int dmx_ts_set_indexing_params(
+	struct dmx_ts_feed *ts_feed,
+	struct dmx_indexing_video_params *params)
+{
+	struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
+
+	memcpy(&feed->indexing_params, params,
+			sizeof(struct dmx_indexing_video_params));
+
+	return 0;
+}
+
 static int dvbdmx_allocate_ts_feed(struct dmx_demux *dmx,
 				   struct dmx_ts_feed **ts_feed,
 				   dmx_ts_cb callback)
@@ -777,6 +1060,15 @@
 	feed->pid = 0xffff;
 	feed->peslen = 0xfffa;
 	feed->buffer = NULL;
+	memset(&feed->indexing_params, 0,
+			sizeof(struct dmx_indexing_video_params));
+
+	/* default behaviour - pass first PES data even if it is
+	 * partial PES data from previous PES that we didn't receive its header.
+	 * Override this to 0 in your start_feed function in order to handle
+	 * first PES differently.
+	 */
+	feed->pusi_seen = 1;
 
 	(*ts_feed) = &feed->feed.ts;
 	(*ts_feed)->parent = dmx;
@@ -785,6 +1077,7 @@
 	(*ts_feed)->start_filtering = dmx_ts_feed_start_filtering;
 	(*ts_feed)->stop_filtering = dmx_ts_feed_stop_filtering;
 	(*ts_feed)->set = dmx_ts_feed_set;
+	(*ts_feed)->set_indexing_params = dmx_ts_set_indexing_params;
 
 	if (!(feed->filter = dvb_dmx_filter_alloc(demux))) {
 		feed->state = DMX_STATE_FREE;
@@ -1119,30 +1412,54 @@
 	return 0;
 }
 
-static int dvbdmx_write(struct dmx_demux *demux, const char __user *buf, size_t count)
+static int dvbdmx_write(struct dmx_demux *demux, const char *buf, size_t count)
 {
 	struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
-	void *p;
 
 	if ((!demux->frontend) || (demux->frontend->source != DMX_MEMORY_FE))
 		return -EINVAL;
 
-	p = memdup_user(buf, count);
-	if (IS_ERR(p))
-		return PTR_ERR(p);
-	if (mutex_lock_interruptible(&dvbdemux->mutex)) {
-		kfree(p);
-		return -ERESTARTSYS;
-	}
-	dvb_dmx_swfilter(dvbdemux, p, count);
-	kfree(p);
-	mutex_unlock(&dvbdemux->mutex);
+	dvb_dmx_swfilter_format(dvbdemux, buf, count, dvbdemux->tsp_format);
 
 	if (signal_pending(current))
 		return -EINTR;
 	return count;
 }
 
+static int dvbdmx_write_cancel(struct dmx_demux *demux)
+{
+	struct dvb_demux *dvbdmx = (struct dvb_demux *)demux;
+
+	spin_lock_irq(&dvbdmx->lock);
+
+	/* cancel any pending wait for decoder's buffers */
+	dvbdmx->sw_filter_abort = 1;
+	dvbdmx->tsbufp = 0;
+	dvb_dmx_configure_decoder_fullness(dvbdmx, 0);
+
+	spin_unlock_irq(&dvbdmx->lock);
+
+	return 0;
+}
+
+static int dvbdmx_set_playback_mode(struct dmx_demux *demux,
+				 enum dmx_playback_mode_t mode,
+				 dmx_ts_fullness ts_fullness_callback,
+				 dmx_section_fullness sec_fullness_callback)
+{
+	struct dvb_demux *dvbdmx = (struct dvb_demux *)demux;
+
+	mutex_lock(&dvbdmx->mutex);
+
+	dvbdmx->playback_mode = mode;
+	dvbdmx->buffer_ctrl.ts = ts_fullness_callback;
+	dvbdmx->buffer_ctrl.sec = sec_fullness_callback;
+
+	mutex_unlock(&dvbdmx->mutex);
+
+	return 0;
+}
+
 static int dvbdmx_add_frontend(struct dmx_demux *demux,
 			       struct dmx_frontend *frontend)
 {
@@ -1214,6 +1531,40 @@
 	return 0;
 }
 
+static int dvbdmx_set_tsp_format(
+	struct dmx_demux *demux,
+	enum dmx_tsp_format_t tsp_format)
+{
+	struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
+
+	if ((tsp_format > DMX_TSP_FORMAT_204) ||
+		(tsp_format < DMX_TSP_FORMAT_188))
+		return -EINVAL;
+
+	mutex_lock(&dvbdemux->mutex);
+
+	dvbdemux->tsp_format = tsp_format;
+	mutex_unlock(&dvbdemux->mutex);
+	return 0;
+}
+
+static int dvbdmx_set_tsp_out_format(
+	struct dmx_demux *demux,
+	enum dmx_tsp_format_t tsp_format)
+{
+	struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
+
+	if ((tsp_format > DMX_TSP_FORMAT_192_HEAD) ||
+		(tsp_format < DMX_TSP_FORMAT_188))
+		return -EINVAL;
+
+	mutex_lock(&dvbdemux->mutex);
+
+	dvbdemux->tsp_out_format = tsp_format;
+	mutex_unlock(&dvbdemux->mutex);
+	return 0;
+}
+
 int dvb_dmx_init(struct dvb_demux *dvbdemux)
 {
 	int i;
@@ -1232,6 +1583,30 @@
 		dvbdemux->filter = NULL;
 		return -ENOMEM;
 	}
+
+	dvbdemux->total_process_time = 0;
+	dvbdemux->total_crc_time = 0;
+	snprintf(dvbdemux->alias,
+			MAX_DVB_DEMUX_NAME_LEN,
+			"demux%d",
+			dvb_demux_index++);
+
+	dvbdemux->debugfs_demux_dir = debugfs_create_dir(dvbdemux->alias, NULL);
+
+	if (dvbdemux->debugfs_demux_dir != NULL) {
+		debugfs_create_u32(
+			"total_processing_time",
+			S_IRUGO|S_IWUGO,
+			dvbdemux->debugfs_demux_dir,
+			&dvbdemux->total_process_time);
+
+		debugfs_create_u32(
+			"total_crc_time",
+			S_IRUGO|S_IWUGO,
+			dvbdemux->debugfs_demux_dir,
+			&dvbdemux->total_crc_time);
+	}
+
 	for (i = 0; i < dvbdemux->filternum; i++) {
 		dvbdemux->filter[i].state = DMX_STATE_FREE;
 		dvbdemux->filter[i].index = i;
@@ -1258,6 +1633,9 @@
 	dvbdemux->recording = 0;
 	dvbdemux->tsbufp = 0;
 
+	dvbdemux->tsp_format = DMX_TSP_FORMAT_188;
+	dvbdemux->tsp_out_format = DMX_TSP_FORMAT_188;
+
 	if (!dvbdemux->check_crc32)
 		dvbdemux->check_crc32 = dvb_dmx_crc32;
 
@@ -1269,6 +1647,8 @@
 	dmx->open = dvbdmx_open;
 	dmx->close = dvbdmx_close;
 	dmx->write = dvbdmx_write;
+	dmx->write_cancel = dvbdmx_write_cancel;
+	dmx->set_playback_mode = dvbdmx_set_playback_mode;
 	dmx->allocate_ts_feed = dvbdmx_allocate_ts_feed;
 	dmx->release_ts_feed = dvbdmx_release_ts_feed;
 	dmx->allocate_section_feed = dvbdmx_allocate_section_feed;
@@ -1281,6 +1661,9 @@
 	dmx->disconnect_frontend = dvbdmx_disconnect_frontend;
 	dmx->get_pes_pids = dvbdmx_get_pes_pids;
 
+	dmx->set_tsp_format = dvbdmx_set_tsp_format;
+	dmx->set_tsp_out_format = dvbdmx_set_tsp_out_format;
+
 	mutex_init(&dvbdemux->mutex);
 	spin_lock_init(&dvbdemux->lock);
 
@@ -1291,6 +1674,9 @@
 
 void dvb_dmx_release(struct dvb_demux *dvbdemux)
 {
+	if (dvbdemux->debugfs_demux_dir != NULL)
+		debugfs_remove_recursive(dvbdemux->debugfs_demux_dir);
+
 	vfree(dvbdemux->cnt_storage);
 	vfree(dvbdemux->filter);
 	vfree(dvbdemux->feed);
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.h b/drivers/media/dvb/dvb-core/dvb_demux.h
index a7d876f..17f4960 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.h
+++ b/drivers/media/dvb/dvb-core/dvb_demux.h
@@ -4,6 +4,8 @@
  * Copyright (C) 2000-2001 Marcus Metzler & Ralph Metzler
  *                         for convergence integrated media GmbH
  *
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License
  * as published by the Free Software Foundation; either version 2.1
@@ -27,6 +29,7 @@
 #include <linux/timer.h>
 #include <linux/spinlock.h>
 #include <linux/mutex.h>
+#include <linux/debugfs.h>
 
 #include "demux.h"
 
@@ -92,10 +95,12 @@
 	int cc;
 	int pusi_seen;		/* prevents feeding of garbage from previous section */
 
-	u16 peslen;
+	u32 peslen;
 
 	struct list_head list_head;
 	unsigned int index;	/* a unique index for each feed (can be used as hardware pid filter index) */
+
+	struct dmx_indexing_video_params indexing_params;
 };
 
 struct dvb_demux {
@@ -107,6 +112,10 @@
 	int (*stop_feed)(struct dvb_demux_feed *feed);
 	int (*write_to_decoder)(struct dvb_demux_feed *feed,
 				 const u8 *buf, size_t len);
+	int (*decoder_fullness_init)(struct dvb_demux_feed *feed);
+	int (*decoder_fullness_wait)(struct dvb_demux_feed *feed,
+				 size_t required_space);
+	int (*decoder_fullness_abort)(struct dvb_demux_feed *feed);
 	u32 (*check_crc32)(struct dvb_demux_feed *feed,
 			    const u8 *buf, size_t len);
 	void (*memcopy)(struct dvb_demux_feed *feed, u8 *dst,
@@ -136,6 +145,28 @@
 
 	struct timespec speed_last_time; /* for TS speed check */
 	uint32_t speed_pkts_cnt; /* for TS speed check */
+
+	enum dmx_tsp_format_t tsp_format;
+	enum dmx_tsp_format_t tsp_out_format;
+
+	enum dmx_playback_mode_t playback_mode;
+	int sw_filter_abort;
+
+	struct {
+		dmx_ts_fullness ts;
+		dmx_section_fullness sec;
+	} buffer_ctrl;
+
+	/*
+	 * the following is used for debugfs exposing info
+	 * about dvb demux performance.
+	 */
+#define MAX_DVB_DEMUX_NAME_LEN 10
+	char alias[MAX_DVB_DEMUX_NAME_LEN];
+
+	u32 total_process_time;
+	u32 total_crc_time;
+	struct dentry *debugfs_demux_dir;
 };
 
 int dvb_dmx_init(struct dvb_demux *dvbdemux);
@@ -145,5 +176,10 @@
 void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count);
 void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf,
 			  size_t count);
+void dvb_dmx_swfilter_format(
+			struct dvb_demux *demux, const u8 *buf,
+			size_t count,
+			enum dmx_tsp_format_t tsp_format);
+
 
 #endif /* _DVB_DEMUX_H_ */
diff --git a/drivers/media/dvb/dvb-core/dvb_ringbuffer.c b/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
index a5712cd..36cc475 100644
--- a/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
+++ b/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
@@ -5,6 +5,8 @@
  * Copyright (C) 2003 Oliver Endriss
  * Copyright (C) 2004 Andrew de Quincey
  *
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
  * based on code originally found in av7110.c & dvb_ci.c:
  * Copyright (C) 1999-2003 Ralph  Metzler
  *                       & Marcus Metzler for convergence integrated media GmbH
@@ -37,6 +39,8 @@
 
 #define PKT_READY 0
 #define PKT_DISPOSED 1
+#define PKT_PENDING 2
+
 
 
 void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, size_t len)
@@ -166,6 +170,35 @@
 	return len;
 }
 
+ssize_t dvb_ringbuffer_write_user(struct dvb_ringbuffer *rbuf,
+					const u8 *buf, size_t len)
+{
+	size_t todo = len;
+	size_t split;
+	ssize_t oldpwrite = rbuf->pwrite;
+
+	split = (rbuf->pwrite + len > rbuf->size) ?
+			rbuf->size - rbuf->pwrite :
+			0;
+
+	if (split > 0) {
+		if (copy_from_user(rbuf->data + rbuf->pwrite, buf, split))
+			return -EFAULT;
+		buf += split;
+		todo -= split;
+		rbuf->pwrite = 0;
+	}
+
+	if (copy_from_user(rbuf->data + rbuf->pwrite, buf, todo)) {
+		rbuf->pwrite = oldpwrite;
+		return -EFAULT;
+	}
+
+	rbuf->pwrite = (rbuf->pwrite + todo) % rbuf->size;
+
+	return len;
+}
+
 ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, size_t len)
 {
 	int status;
@@ -180,6 +213,31 @@
 	return status;
 }
 
+ssize_t dvb_ringbuffer_pkt_start(struct dvb_ringbuffer *rbuf, size_t len)
+{
+	ssize_t oldpwrite = rbuf->pwrite;
+
+	DVB_RINGBUFFER_WRITE_BYTE(rbuf, len >> 8);
+	DVB_RINGBUFFER_WRITE_BYTE(rbuf, len & 0xff);
+	DVB_RINGBUFFER_WRITE_BYTE(rbuf, PKT_PENDING);
+
+	return oldpwrite;
+}
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_start);
+
+int dvb_ringbuffer_pkt_close(struct dvb_ringbuffer *rbuf, ssize_t idx)
+{
+	idx = (idx + 2) % rbuf->size;
+
+	if (rbuf->data[idx] != PKT_PENDING)
+		return -EINVAL;
+
+	rbuf->data[idx] = PKT_READY;
+
+	return 0;
+}
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_close);
+
 ssize_t dvb_ringbuffer_pkt_read_user(struct dvb_ringbuffer *rbuf, size_t idx,
 				int offset, u8 __user *buf, size_t len)
 {
@@ -187,6 +245,9 @@
 	size_t split;
 	size_t pktlen;
 
+	if (DVB_RINGBUFFER_PEEK(rbuf, (idx+2)) != PKT_READY)
+		return -EINVAL;
+
 	pktlen = rbuf->data[idx] << 8;
 	pktlen |= rbuf->data[(idx + 1) % rbuf->size];
 	if (offset > pktlen) return -EINVAL;
@@ -207,6 +268,7 @@
 
 	return len;
 }
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_read_user);
 
 ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx,
 				int offset, u8* buf, size_t len)
@@ -215,9 +277,13 @@
 	size_t split;
 	size_t pktlen;
 
+	if (rbuf->data[(idx + 2) % rbuf->size] != PKT_READY)
+		return -EINVAL;
+
 	pktlen = rbuf->data[idx] << 8;
 	pktlen |= rbuf->data[(idx + 1) % rbuf->size];
 	if (offset > pktlen) return -EINVAL;
+
 	if ((offset + len) > pktlen) len = pktlen - offset;
 
 	idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size;
@@ -232,6 +298,7 @@
 	memcpy(buf, rbuf->data+idx, todo);
 	return len;
 }
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_read);
 
 void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx)
 {
@@ -251,6 +318,7 @@
 		}
 	}
 }
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_dispose);
 
 ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen)
 {
@@ -279,6 +347,9 @@
 			return idx;
 		}
 
+		if (curpktstatus == PKT_PENDING)
+			return -EFAULT;
+
 		consumed += curpktlen + DVB_RINGBUFFER_PKTHDRSIZE;
 		idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size;
 	}
@@ -286,8 +357,7 @@
 	// no packets available
 	return -1;
 }
-
-
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_next);
 
 EXPORT_SYMBOL(dvb_ringbuffer_init);
 EXPORT_SYMBOL(dvb_ringbuffer_empty);
@@ -297,3 +367,5 @@
 EXPORT_SYMBOL(dvb_ringbuffer_read_user);
 EXPORT_SYMBOL(dvb_ringbuffer_read);
 EXPORT_SYMBOL(dvb_ringbuffer_write);
+EXPORT_SYMBOL(dvb_ringbuffer_write_user);
+
diff --git a/drivers/media/dvb/dvb-core/dvb_ringbuffer.h b/drivers/media/dvb/dvb-core/dvb_ringbuffer.h
index 41f04da..8b591a6 100644
--- a/drivers/media/dvb/dvb-core/dvb_ringbuffer.h
+++ b/drivers/media/dvb/dvb-core/dvb_ringbuffer.h
@@ -5,6 +5,8 @@
  * Copyright (C) 2003 Oliver Endriss
  * Copyright (C) 2004 Andrew de Quincey
  *
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
  * based on code originally found in av7110.c & dvb_ci.c:
  * Copyright (C) 1999-2003 Ralph Metzler & Marcus Metzler
  *                         for convergence integrated media GmbH
@@ -134,6 +136,8 @@
 extern ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf,
 				    size_t len);
 
+extern ssize_t dvb_ringbuffer_write_user(struct dvb_ringbuffer *rbuf,
+					const u8 *buf, size_t len);
 
 /**
  * Write a packet into the ringbuffer.
@@ -183,4 +187,30 @@
 extern ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen);
 
 
+/**
+ * Start a new packet that will be written directly by the user to the packet buffer.
+ * The function only writes the header of the packet into the packet buffer,
+ * and the packet is in pending state (can't be read by the reader) until it is
+ * closed using dvb_ringbuffer_pkt_close. You must write the data into the
+ * packet buffer using dvb_ringbuffer_write followed by
+ * dvb_ringbuffer_pkt_close.
+ *
+ * <rbuf> Ringbuffer concerned.
+ * <len> Size of the packet's data
+ * returns Index of the packet's header that was started.
+ */
+extern ssize_t dvb_ringbuffer_pkt_start(struct dvb_ringbuffer *rbuf,
+						size_t len);
+
+/**
+ * Close a packet that was started using dvb_ringbuffer_pkt_start.
+ * The packet will be marked as ready to be ready.
+ *
+ * <rbuf> Ringbuffer concerned.
+ * <idx> Packet index that was returned by dvb_ringbuffer_pkt_start
+ * returns error status, -EINVAL if the provided index is invalid
+ */
+extern int dvb_ringbuffer_pkt_close(struct dvb_ringbuffer *rbuf, ssize_t idx);
+
+
 #endif /* _DVB_RINGBUFFER_H_ */
diff --git a/drivers/media/dvb/mpq/Kconfig b/drivers/media/dvb/mpq/Kconfig
new file mode 100644
index 0000000..868ad8c
--- /dev/null
+++ b/drivers/media/dvb/mpq/Kconfig
@@ -0,0 +1,12 @@
+config DVB_MPQ
+	tristate "Qualcomm Multimedia Processor DVB Adapter"
+	depends on ARCH_MSM && DVB_CORE
+	default n
+
+	help
+	  Support for Qualcomm MPQ based DVB adapter.
+	  Say Y or M if you own such a device and want to use it.
+
+source "drivers/media/dvb/mpq/demux/Kconfig"
+
+
diff --git a/drivers/media/dvb/mpq/Makefile b/drivers/media/dvb/mpq/Makefile
new file mode 100644
index 0000000..7ccf13e
--- /dev/null
+++ b/drivers/media/dvb/mpq/Makefile
@@ -0,0 +1,5 @@
+
+obj-$(CONFIG_DVB_MPQ)	    += adapter/
+obj-$(CONFIG_DVB_MPQ_DEMUX) += demux/
+
+
diff --git a/drivers/media/dvb/mpq/adapter/Makefile b/drivers/media/dvb/mpq/adapter/Makefile
new file mode 100644
index 0000000..ed664da
--- /dev/null
+++ b/drivers/media/dvb/mpq/adapter/Makefile
@@ -0,0 +1,8 @@
+
+EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/
+EXTRA_CFLAGS += -Idrivers/media/dvb/mpq/include/
+
+obj-$(CONFIG_DVB_MPQ) += mpq-adapter.o
+
+mpq-adapter-y := mpq_adapter.o mpq_stream_buffer.o
+
diff --git a/drivers/media/dvb/mpq/adapter/mpq_adapter.c b/drivers/media/dvb/mpq/adapter/mpq_adapter.c
new file mode 100644
index 0000000..9664f04
--- /dev/null
+++ b/drivers/media/dvb/mpq/adapter/mpq_adapter.c
@@ -0,0 +1,212 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include "mpq_adapter.h"
+#include "mpq_dvb_debug.h"
+
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+/* data-structure holding MPQ adapter information */
+static struct
+{
+	/* MPQ adapter registered to dvb-core */
+	struct dvb_adapter adapter;
+
+	/* mutex protect against the data-structure */
+	struct mutex mutex;
+
+	/* List of stream interfaces registered to the MPQ adapter */
+	struct {
+		/* pointer to the stream buffer using for data tunneling */
+		struct mpq_streambuffer *stream_buffer;
+
+		/* callback triggered when the stream interface is registered */
+		mpq_adapter_stream_if_callback callback;
+
+		/* parameter passed to the callback function */
+		void *user_param;
+	} interfaces[MPQ_ADAPTER_MAX_NUM_OF_INTERFACES];
+} mpq_info;
+
+
+/**
+ * Initialize MPQ DVB adapter module.
+ *
+ * Return     error status
+ */
+static int __init mpq_adapter_init(void)
+{
+	int i;
+	int result;
+
+	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+	mutex_init(&mpq_info.mutex);
+
+	/* reset stream interfaces list */
+	for (i = 0; i < MPQ_ADAPTER_MAX_NUM_OF_INTERFACES; i++) {
+		mpq_info.interfaces[i].stream_buffer = NULL;
+		mpq_info.interfaces[i].callback = NULL;
+	}
+
+	/* regsiter a new dvb-adapter to dvb-core */
+	result = dvb_register_adapter(&mpq_info.adapter,
+								  "Qualcomm DVB adapter",
+								  THIS_MODULE,
+								  NULL,
+								  adapter_nr);
+
+	if (result < 0) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: dvb_register_adapter failed, errno %d\n",
+			__func__,
+			result);
+	}
+
+	return result;
+}
+
+
+/**
+ * Cleanup MPQ DVB adapter module.
+ */
+static void __exit mpq_adapter_exit(void)
+{
+	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+	/* un-regsiter adapter from dvb-core */
+	dvb_unregister_adapter(&mpq_info.adapter);
+	mutex_destroy(&mpq_info.mutex);
+}
+
+struct dvb_adapter *mpq_adapter_get(void)
+{
+	return &mpq_info.adapter;
+}
+EXPORT_SYMBOL(mpq_adapter_get);
+
+
+int mpq_adapter_register_stream_if(
+		enum mpq_adapter_stream_if interface_id,
+		struct mpq_streambuffer *stream_buffer)
+{
+	int ret;
+
+	if (interface_id >= MPQ_ADAPTER_MAX_NUM_OF_INTERFACES) {
+		ret = -EINVAL;
+		goto register_failed;
+	}
+
+	if (mutex_lock_interruptible(&mpq_info.mutex)) {
+		ret = -ERESTARTSYS;
+		goto register_failed;
+	}
+
+	if (mpq_info.interfaces[interface_id].stream_buffer != NULL) {
+		/* already registered interface */
+		ret = -EINVAL;
+		goto register_failed_unlock_mutex;
+	}
+
+	mpq_info.interfaces[interface_id].stream_buffer = stream_buffer;
+	mutex_unlock(&mpq_info.mutex);
+
+	/*
+	 * If callback is installed, trigger it to notify that
+	 * stream interface was registered.
+	 */
+	if (mpq_info.interfaces[interface_id].callback != NULL) {
+		mpq_info.interfaces[interface_id].callback(
+				interface_id,
+				mpq_info.interfaces[interface_id].user_param);
+	}
+
+	return 0;
+
+register_failed_unlock_mutex:
+	mutex_unlock(&mpq_info.mutex);
+register_failed:
+	return ret;
+}
+EXPORT_SYMBOL(mpq_adapter_register_stream_if);
+
+
+int mpq_adapter_unregister_stream_if(
+		enum mpq_adapter_stream_if interface_id)
+{
+	if (interface_id >= MPQ_ADAPTER_MAX_NUM_OF_INTERFACES)
+		return -EINVAL;
+
+	if (mutex_lock_interruptible(&mpq_info.mutex))
+		return -ERESTARTSYS;
+
+	/* clear the registered interface */
+	mpq_info.interfaces[interface_id].stream_buffer = NULL;
+
+	mutex_unlock(&mpq_info.mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL(mpq_adapter_unregister_stream_if);
+
+
+int mpq_adapter_get_stream_if(
+		enum mpq_adapter_stream_if interface_id,
+		struct mpq_streambuffer **stream_buffer)
+{
+	if ((interface_id >= MPQ_ADAPTER_MAX_NUM_OF_INTERFACES) ||
+		(stream_buffer == NULL))
+		return -EINVAL;
+
+	if (mutex_lock_interruptible(&mpq_info.mutex))
+		return -ERESTARTSYS;
+
+	*stream_buffer = mpq_info.interfaces[interface_id].stream_buffer;
+
+	mutex_unlock(&mpq_info.mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL(mpq_adapter_get_stream_if);
+
+
+int mpq_adapter_notify_stream_if(
+		enum mpq_adapter_stream_if interface_id,
+		mpq_adapter_stream_if_callback callback,
+		void *user_param)
+{
+	if (interface_id >= MPQ_ADAPTER_MAX_NUM_OF_INTERFACES)
+		return -EINVAL;
+
+	if (mutex_lock_interruptible(&mpq_info.mutex))
+		return -ERESTARTSYS;
+
+	mpq_info.interfaces[interface_id].callback = callback;
+	mpq_info.interfaces[interface_id].user_param = user_param;
+
+	mutex_unlock(&mpq_info.mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL(mpq_adapter_notify_stream_if);
+
+
+module_init(mpq_adapter_init);
+module_exit(mpq_adapter_exit);
+
+MODULE_DESCRIPTION("Qualcomm MPQ adapter");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/media/dvb/mpq/adapter/mpq_stream_buffer.c b/drivers/media/dvb/mpq/adapter/mpq_stream_buffer.c
new file mode 100644
index 0000000..738d730
--- /dev/null
+++ b/drivers/media/dvb/mpq/adapter/mpq_stream_buffer.c
@@ -0,0 +1,224 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include "mpq_dvb_debug.h"
+#include "mpq_stream_buffer.h"
+
+
+void mpq_streambuffer_init(
+		struct mpq_streambuffer *sbuff,
+		void *data_buff, size_t data_buff_len,
+		void *packet_buff, size_t packet_buff_size)
+{
+	dvb_ringbuffer_init(&sbuff->raw_data, data_buff, data_buff_len);
+	dvb_ringbuffer_init(&sbuff->packet_data, packet_buff, packet_buff_size);
+}
+EXPORT_SYMBOL(mpq_streambuffer_init);
+
+
+ssize_t mpq_streambuffer_pkt_next(
+		struct mpq_streambuffer *sbuff,
+		ssize_t idx, size_t *pktlen)
+{
+	return dvb_ringbuffer_pkt_next(&sbuff->packet_data, idx, pktlen);
+}
+EXPORT_SYMBOL(mpq_streambuffer_pkt_next);
+
+
+ssize_t mpq_streambuffer_pkt_read(
+		struct mpq_streambuffer *sbuff,
+		size_t idx,
+		struct mpq_streambuffer_packet_header *packet,
+		u8 *user_data)
+{
+	size_t ret;
+	size_t read_len;
+
+	/* read-out the packet header first */
+	ret = dvb_ringbuffer_pkt_read(
+				&sbuff->packet_data, idx, 0,
+				(u8 *)packet,
+				sizeof(struct mpq_streambuffer_packet_header));
+
+	/* verify length, at least packet header should exist */
+	if (ret != sizeof(struct mpq_streambuffer_packet_header))
+		return -EINVAL;
+
+	read_len = ret;
+
+	/* read-out private user-data if there are such */
+	if ((packet->user_data_len) && (user_data != NULL)) {
+		ret = dvb_ringbuffer_pkt_read(
+				&sbuff->packet_data,
+				idx,
+				sizeof(struct mpq_streambuffer_packet_header),
+				user_data,
+				packet->user_data_len);
+
+		if (ret < 0)
+			return ret;
+
+		read_len += ret;
+	}
+
+	return read_len;
+}
+EXPORT_SYMBOL(mpq_streambuffer_pkt_read);
+
+
+int mpq_streambuffer_pkt_dispose(
+			struct mpq_streambuffer *sbuff,
+			size_t idx,
+			int dispose_data)
+{
+	int ret;
+	struct mpq_streambuffer_packet_header packet;
+
+	if (dispose_data) {
+		/* read-out the packet header first */
+		ret = dvb_ringbuffer_pkt_read(
+				&sbuff->packet_data,
+				idx,
+				0,
+				(u8 *)&packet,
+				sizeof(struct mpq_streambuffer_packet_header));
+
+		if (ret != sizeof(struct mpq_streambuffer_packet_header))
+			return -EINVAL;
+
+		/* Advance the read pointer in the raw-data buffer first */
+		ret = mpq_streambuffer_data_read_dispose(
+							sbuff,
+							packet.raw_data_len);
+		if (ret != 0)
+			return ret;
+	}
+
+	/* Now clear the packet from the packet header */
+	dvb_ringbuffer_pkt_dispose(&sbuff->packet_data, idx);
+
+	return 0;
+}
+EXPORT_SYMBOL(mpq_streambuffer_pkt_dispose);
+
+
+int mpq_streambuffer_pkt_write(
+			struct mpq_streambuffer *sbuff,
+			struct mpq_streambuffer_packet_header *packet,
+			u8 *user_data)
+{
+	ssize_t idx;
+	size_t len;
+
+	len =
+		sizeof(struct mpq_streambuffer_packet_header) +
+		packet->user_data_len;
+
+	/* Make sure enough space available for packet header */
+	if (dvb_ringbuffer_free(&sbuff->packet_data) < len)
+		return -ENOSPC;
+
+	/* Starting writting packet header */
+	idx = dvb_ringbuffer_pkt_start(&sbuff->packet_data, len);
+
+	/* Write non-user private data header */
+	dvb_ringbuffer_write(
+				&sbuff->packet_data,
+				(u8 *)packet,
+				sizeof(struct mpq_streambuffer_packet_header));
+
+	/* Write user's own private data header */
+	dvb_ringbuffer_write(&sbuff->packet_data,
+						 user_data,
+						 packet->user_data_len);
+
+	dvb_ringbuffer_pkt_close(&sbuff->packet_data, idx);
+
+	wake_up_all(&sbuff->packet_data.queue);
+
+	return 0;
+}
+EXPORT_SYMBOL(mpq_streambuffer_pkt_write);
+
+
+ssize_t mpq_streambuffer_data_write(
+			struct mpq_streambuffer *sbuff,
+			const u8 *buf, size_t len)
+{
+	int res;
+
+	if (unlikely(dvb_ringbuffer_free(&sbuff->raw_data) < len))
+		return -ENOSPC;
+
+	res = dvb_ringbuffer_write(&sbuff->raw_data, buf, len);
+	wake_up_all(&sbuff->raw_data.queue);
+
+	return res;
+}
+EXPORT_SYMBOL(mpq_streambuffer_data_write);
+
+
+int mpq_streambuffer_data_write_deposit(
+				struct mpq_streambuffer *sbuff,
+				size_t len)
+{
+	if (unlikely(dvb_ringbuffer_free(&sbuff->raw_data) < len))
+		return -ENOSPC;
+
+	sbuff->raw_data.pwrite =
+		(sbuff->raw_data.pwrite+len) % sbuff->raw_data.size;
+
+	wake_up_all(&sbuff->raw_data.queue);
+
+	return 0;
+}
+EXPORT_SYMBOL(mpq_streambuffer_data_write_deposit);
+
+
+size_t mpq_streambuffer_data_read(
+				struct mpq_streambuffer *sbuff,
+				u8 *buf, size_t len)
+{
+	int actual_len;
+
+	actual_len = dvb_ringbuffer_avail(&sbuff->raw_data);
+	if (actual_len < len)
+		len = actual_len;
+
+	if (actual_len)
+		dvb_ringbuffer_read(&sbuff->raw_data, buf, actual_len);
+
+	wake_up_all(&sbuff->raw_data.queue);
+
+	return actual_len;
+}
+EXPORT_SYMBOL(mpq_streambuffer_data_read);
+
+
+int mpq_streambuffer_data_read_dispose(
+			struct mpq_streambuffer *sbuff,
+			size_t len)
+{
+	if (unlikely(dvb_ringbuffer_avail(&sbuff->raw_data) < len))
+		return -EINVAL;
+
+	DVB_RINGBUFFER_SKIP(&sbuff->raw_data, len);
+
+	wake_up_all(&sbuff->raw_data.queue);
+	return 0;
+}
+EXPORT_SYMBOL(mpq_streambuffer_data_read_dispose);
+
diff --git a/drivers/media/dvb/mpq/demux/Kconfig b/drivers/media/dvb/mpq/demux/Kconfig
new file mode 100644
index 0000000..34961c2
--- /dev/null
+++ b/drivers/media/dvb/mpq/demux/Kconfig
@@ -0,0 +1,45 @@
+config DVB_MPQ_DEMUX
+	tristate "DVB Demux Device"
+	depends on DVB_MPQ && ION && ION_MSM
+	default n
+
+	help
+	  Support for Qualcomm based dvb demux device.
+	  Say Y or M if you own such a device and want to use it.
+
+config DVB_MPQ_NUM_DMX_DEVICES
+	int "Number of demux devices"
+	depends on DVB_MPQ_DEMUX
+	default 4
+	range 1 255
+
+	help
+	  Configure number of demux devices. Depends on your use-cases for maximum concurrent stream playback.
+
+choice
+	prompt "Demux Hardware Plugin"
+	depends on DVB_MPQ_DEMUX
+	default DVB_MPQ_TSIF
+	help
+	  Enable support of specific demux HW plugin depending on the existing HW support.
+	  Depending on the enabled HW, demux may take advantage of HW capbailities to perform some tasks in HW instead of SW.
+
+	config DVB_MPQ_TSPP1
+		bool "TSPPv1 plugin"
+		depends on TSPP
+		help
+			Use this option of your HW has Transport Stream Packet Processor (TSPP) version1 support
+
+	config DVB_MPQ_TSPP2
+		bool "TSPPv2 plugin"
+		depends on TSPP
+		help
+			Use this option of your HW has Transport Stream Packet Processor (TSPP) version2 support
+
+	config DVB_MPQ_TSIF
+		bool "TSIF plugin"
+		depends on TSIF
+		help
+			Use this option of your HW has only TSIF input without any Transport Stream Packet Processor (TSPP) support
+endchoice
+
diff --git a/drivers/media/dvb/mpq/demux/Makefile b/drivers/media/dvb/mpq/demux/Makefile
new file mode 100644
index 0000000..b9310c3
--- /dev/null
+++ b/drivers/media/dvb/mpq/demux/Makefile
@@ -0,0 +1,14 @@
+
+EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/
+EXTRA_CFLAGS += -Idrivers/media/dvb/mpq/include/
+
+obj-$(CONFIG_DVB_MPQ_DEMUX) += mpq-dmx-hw-plugin.o
+
+mpq-dmx-hw-plugin-y := mpq_dmx_plugin_common.o
+
+mpq-dmx-hw-plugin-$(CONFIG_DVB_MPQ_TSPP1) += mpq_dmx_plugin_tspp_v1.o
+
+mpq-dmx-hw-plugin-$(CONFIG_DVB_MPQ_TSPP2) += mpq_dmx_plugin_tspp_v2.o
+
+mpq-dmx-hw-plugin-$(CONFIG_DVB_MPQ_TSIF) += mpq_dmx_plugin_tsif.o
+
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c
new file mode 100644
index 0000000..03b3929
--- /dev/null
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c
@@ -0,0 +1,1956 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include "mpq_dvb_debug.h"
+#include "mpq_dmx_plugin_common.h"
+
+
+/* Length of mandatory fields that must exist in header of video PES */
+#define PES_MANDATORY_FIELDS_LEN			9
+
+
+/*
+ * 500 PES header packets in the meta-data buffer,
+ * should be more than enough
+ */
+#define VIDEO_NUM_OF_PES_PACKETS			500
+
+#define VIDEO_META_DATA_BUFFER_SIZE              \
+	(VIDEO_NUM_OF_PES_PACKETS *                  \
+	  (DVB_RINGBUFFER_PKTHDRSIZE +               \
+	   sizeof(struct mpq_streambuffer_packet_header) + \
+	   sizeof(struct mpq_adapter_video_meta_data)))
+
+/*
+ * PCR/STC information length saved in ring-buffer.
+ * PCR / STC are saved in ring-buffer in the following form:
+ * <8 bit flags><64 bits of STC> <64bits of PCR>
+ * STC and PCR values are in 27MHz.
+ * The current flags that are defined:
+ * 0x00000001: discontinuity_indicator
+ */
+#define PCR_STC_LEN					17
+
+
+/* Number of demux devices, has default of linux configuration */
+static int mpq_demux_device_num = CONFIG_DVB_MPQ_NUM_DMX_DEVICES;
+module_param(mpq_demux_device_num, int, S_IRUGO);
+
+/**
+ * Maximum allowed framing pattern size
+ */
+#define MPQ_MAX_PATTERN_SIZE				6
+
+/**
+ * Number of patterns to look for when doing framing, per video standard
+ */
+#define MPQ_MPEG2_PATTERN_NUM				5
+#define MPQ_H264_PATTERN_NUM				5
+#define MPQ_VC1_PATTERN_NUM				3
+
+/*
+ * mpq_framing_pattern_lookup_params - framing pattern lookup parameters.
+ *
+ * @pattern: the byte pattern to look for.
+ * @mask: the byte mask to use (same length as pattern).
+ * @size: the length of the pattern, in bytes.
+ * @type: the type of the pattern.
+ */
+struct mpq_framing_pattern_lookup_params {
+	u8 pattern[MPQ_MAX_PATTERN_SIZE];
+	u8 mask[MPQ_MAX_PATTERN_SIZE];
+	size_t size;
+	enum dmx_framing_pattern_type type;
+};
+
+/*
+ * Pre-defined video framing lookup pattern information.
+ * Note: the first pattern in each patterns database must
+ * be the Sequence Header (or equivalent SPS in H.264).
+ * The code assumes this is the case when prepending
+ * Sequence Header data in case it is required.
+ */
+static const struct mpq_framing_pattern_lookup_params
+		mpeg2_patterns[MPQ_MPEG2_PATTERN_NUM] = {
+	{{0x00, 0x00, 0x01, 0xB3}, {0xFF, 0xFF, 0xFF, 0xFF}, 4,
+			DMX_FRM_MPEG2_SEQUENCE_HEADER},
+	{{0x00, 0x00, 0x01, 0xB8}, {0xFF, 0xFF, 0xFF, 0xFF}, 4,
+			DMX_FRM_MPEG2_GOP_HEADER},
+	{{0x00, 0x00, 0x01, 0x00, 0x00, 0x08},
+			{0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x38}, 6,
+			DMX_FRM_MPEG2_I_PIC},
+	{{0x00, 0x00, 0x01, 0x00, 0x00, 0x10},
+			{0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x38}, 6,
+			DMX_FRM_MPEG2_P_PIC},
+	{{0x00, 0x00, 0x01, 0x00, 0x00, 0x18},
+			{0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x38}, 6,
+			DMX_FRM_MPEG2_B_PIC}
+};
+
+static const struct mpq_framing_pattern_lookup_params
+		h264_patterns[MPQ_H264_PATTERN_NUM] = {
+	{{0x00, 0x00, 0x01, 0x07}, {0xFF, 0xFF, 0xFF, 0x1F}, 4,
+			DMX_FRM_H264_SPS},
+	{{0x00, 0x00, 0x01, 0x08}, {0xFF, 0xFF, 0xFF, 0x1F}, 4,
+			DMX_FRM_H264_PPS},
+	{{0x00, 0x00, 0x01, 0x05, 0x80}, {0xFF, 0xFF, 0xFF, 0x1F, 0x80}, 5,
+			DMX_FRM_H264_IDR_PIC},
+	{{0x00, 0x00, 0x01, 0x01, 0x80}, {0xFF, 0xFF, 0xFF, 0x1F, 0x80}, 5,
+			DMX_FRM_H264_NON_IDR_PIC}
+};
+
+static const struct mpq_framing_pattern_lookup_params
+		vc1_patterns[MPQ_VC1_PATTERN_NUM] = {
+	{{0x00, 0x00, 0x01, 0x0F}, {0xFF, 0xFF, 0xFF, 0xFF}, 4,
+			DMX_FRM_VC1_SEQUENCE_HEADER},
+	{{0x00, 0x00, 0x01, 0x0E}, {0xFF, 0xFF, 0xFF, 0xFF}, 4,
+			DMX_FRM_VC1_ENTRY_POINT_HEADER},
+	{{0x00, 0x00, 0x01, 0x0D}, {0xFF, 0xFF, 0xFF, 0xFF}, 4,
+			DMX_FRM_VC1_FRAME_START_CODE}
+};
+
+/* Global data-structure for managing demux devices */
+static struct
+{
+	/* ION demux client used for memory allocation */
+	struct ion_client *ion_client;
+
+	/* demux devices array */
+	struct mpq_demux *devices;
+
+	/* Stream buffers objects used for tunneling to decoders */
+	struct mpq_streambuffer
+		decoder_buffers[MPQ_ADAPTER_MAX_NUM_OF_INTERFACES];
+
+	/*
+	 * Indicates whether the video decoder handles framing
+	 * or we are required to provide framing information
+	 * in the meta-data passed to the decoder.
+	 */
+	int decoder_framing;
+} mpq_dmx_info;
+
+/* Check that PES header is valid and that it is a video PES */
+static int mpq_dmx_is_valid_video_pes(struct pes_packet_header *pes_header)
+{
+	/* start-code valid? */
+	if ((pes_header->packet_start_code_prefix_1 != 0) ||
+		(pes_header->packet_start_code_prefix_2 != 0) ||
+		(pes_header->packet_start_code_prefix_3 != 1))
+		return -EINVAL;
+
+	/* stream_id is video? */
+	if ((pes_header->stream_id & 0xF0) != 0xE0)
+		return -EINVAL;
+
+	return 0;
+}
+
+/* Check if a framing pattern is a video frame pattern or a header pattern */
+static inline int mpq_dmx_is_video_frame(
+				enum dmx_indexing_video_standard standard,
+				enum dmx_framing_pattern_type pattern_type)
+{
+	switch (standard) {
+	case DMX_INDEXING_MPEG2:
+		if ((pattern_type == DMX_FRM_MPEG2_I_PIC) ||
+			(pattern_type == DMX_FRM_MPEG2_P_PIC) ||
+			(pattern_type == DMX_FRM_MPEG2_B_PIC))
+			return 1;
+		return 0;
+	case DMX_INDEXING_H264:
+		if ((pattern_type == DMX_FRM_H264_IDR_PIC) ||
+			(pattern_type == DMX_FRM_H264_NON_IDR_PIC))
+			return 1;
+		return 0;
+	case DMX_INDEXING_VC1:
+		if (pattern_type == DMX_FRM_VC1_FRAME_START_CODE)
+			return 1;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+/*
+ * mpq_framing_pattern_lookup_results - framing lookup results
+ *
+ * @offset: The offset in the buffer where the pattern was found.
+ * If a pattern is found using a prefix (i.e. started on the
+ * previous buffer), offset is zero.
+ * @type: the type of the pattern found.
+ * @used_prefix_size: the prefix size that was used to find this pattern
+ */
+struct mpq_framing_pattern_lookup_results {
+	struct {
+		u32 offset;
+		enum dmx_framing_pattern_type type;
+		u32 used_prefix_size;
+	} info[MPQ_MAX_FOUND_PATTERNS];
+};
+
+/*
+ * Check if two patterns are identical, taking mask into consideration.
+ * @pattern1: the first byte pattern to compare.
+ * @pattern2: the second byte pattern to compare.
+ * @mask: the bit mask to use.
+ * @pattern_size: the length of both patterns and the mask, in bytes.
+ *
+ * Return: 1 if patterns match, 0 otherwise.
+ */
+static inline int mpq_dmx_patterns_match(const u8 *pattern1, const u8 *pattern2,
+					const u8 *mask, size_t pattern_size)
+{
+	int i;
+
+	/*
+	 * Assumption: it is OK to access pattern1, pattern2 and mask.
+	 * This function performs no sanity checks to keep things fast.
+	 */
+
+	for (i = 0; i < pattern_size; i++)
+		if ((pattern1[i] & mask[i]) != (pattern2[i] & mask[i]))
+			return 0;
+
+	return 1;
+}
+
+/*
+ * mpq_dmx_framing_pattern_search -
+ * search for framing patterns in a given buffer.
+ *
+ * Optimized version: first search for a common substring, e.g. 0x00 0x00 0x01.
+ * If this string is found, go over all the given patterns (all must start
+ * with this string) and search for their ending in the buffer.
+ *
+ * Assumption: the patterns we look for do not spread over more than two
+ * buffers.
+ *
+ * @paterns: the full patterns information to look for.
+ * @patterns_num: the number of patterns to look for.
+ * @buf: the buffer to search.
+ * @buf_size: the size of the buffer to search. we search the entire buffer.
+ * @prefix_size_masks: a bit mask (per pattern) of possible prefix sizes to use
+ * when searching for a pattern that started at the last buffer.
+ * Updated in this function for use in the next lookup.
+ * @results: lookup results (offset, type, used_prefix_size) per found pattern,
+ * up to MPQ_MAX_FOUND_PATTERNS.
+ *
+ * Return:
+ *   Number of patterns found (up to MPQ_MAX_FOUND_PATTERNS).
+ *   0 if pattern was not found.
+ *   Negative error value on failure.
+ */
+static int mpq_dmx_framing_pattern_search(
+		const struct mpq_framing_pattern_lookup_params *patterns,
+		int patterns_num,
+		const u8 *buf,
+		size_t buf_size,
+		struct mpq_framing_prefix_size_masks *prefix_size_masks,
+		struct mpq_framing_pattern_lookup_results *results)
+{
+	int i, j;
+	unsigned int current_size;
+	u32 prefix;
+	int found = 0;
+	int start_offset = 0;
+	/* the starting common substring to look for */
+	u8 string[] = {0x00, 0x00, 0x01};
+	/* the mask for the starting string */
+	u8 string_mask[] = {0xFF, 0xFF, 0xFF};
+	/* the size of the starting string (in bytes) */
+	size_t string_size = 3;
+
+	/* sanity checks - can be commented out for optimization purposes */
+	if ((patterns == NULL) || (patterns_num <= 0) || (buf == NULL)) {
+		MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	memset(results, 0, sizeof(struct mpq_framing_pattern_lookup_results));
+
+	/*
+	 * handle prefix - disregard string, simply check all patterns,
+	 * looking for a matching suffix at the very beginning of the buffer.
+	 */
+	for (j = 0; (j < patterns_num) && !found; j++) {
+		prefix = prefix_size_masks->size_mask[j];
+		current_size = 32;
+		while (prefix) {
+			if (prefix & (0x1 << (current_size - 1))) {
+				/*
+				 * check that we don't look further
+				 * than buf_size boundary
+				 */
+				if ((int)(patterns[j].size - current_size) >
+						buf_size)
+					break;
+
+				if (mpq_dmx_patterns_match(
+					(patterns[j].pattern + current_size),
+					buf, (patterns[j].mask + current_size),
+					(patterns[j].size - current_size))) {
+
+					MPQ_DVB_DBG_PRINT(
+						"%s: Found matching pattern"
+						"using prefix of size %d\n",
+						__func__, current_size);
+					/*
+					 * pattern found using prefix at the
+					 * very beginning of the buffer, so
+					 * offset is 0, but we already zeroed
+					 * everything in the beginning of the
+					 * function. that's why the next line
+					 * is commented.
+					 */
+					/* results->info[found].offset = 0; */
+					results->info[found].type =
+							patterns[j].type;
+					results->info[found].used_prefix_size =
+							current_size;
+					found++;
+					/*
+					 * save offset to start looking from
+					 * in the buffer, to avoid reusing the
+					 * data of a pattern we already found.
+					 */
+					start_offset = (patterns[j].size -
+							current_size);
+
+					if (found >= MPQ_MAX_FOUND_PATTERNS)
+						goto next_prefix_lookup;
+					/*
+					 * we don't want to search for the same
+					 * pattern with several possible prefix
+					 * sizes if we have already found it,
+					 * so we break from the inner loop.
+					 * since we incremented 'found', we
+					 * will not search for additional
+					 * patterns using a prefix - that would
+					 * imply ambiguous patterns where one
+					 * pattern can be included in another.
+					 * the for loop will exit.
+					 */
+					break;
+				}
+			}
+			current_size--;
+			prefix &= ~(0x1 << (current_size - 1));
+		}
+	}
+
+	/*
+	 * Search buffer for entire pattern, starting with the string.
+	 * Note the external for loop does not execute if buf_size is
+	 * smaller than string_size (the cast to int is required, since
+	 * size_t is unsigned).
+	 */
+	for (i = start_offset; i < (int)(buf_size - string_size + 1); i++) {
+		if (mpq_dmx_patterns_match(string, (buf + i), string_mask,
+							string_size)) {
+			/* now search for patterns: */
+			for (j = 0; j < patterns_num; j++) {
+				/* avoid overflow to next buffer */
+				if ((i + patterns[j].size) > buf_size)
+					continue;
+
+				if (mpq_dmx_patterns_match(
+					(patterns[j].pattern + string_size),
+					(buf + i + string_size),
+					(patterns[j].mask + string_size),
+					(patterns[j].size - string_size))) {
+
+					results->info[found].offset = i;
+					results->info[found].type =
+						patterns[j].type;
+					/*
+					 * save offset to start next prefix
+					 * lookup, to avoid reusing the data
+					 * of any pattern we already found.
+					 */
+					if ((i + patterns[j].size) >
+							start_offset)
+						start_offset = (i +
+							patterns[j].size);
+					/*
+					 * did not use a prefix to find this
+					 * pattern, but we zeroed everything
+					 * in the beginning of the function.
+					 * So no need to zero used_prefix_size
+					 * for results->info[found]
+					 */
+
+					found++;
+					if (found >= MPQ_MAX_FOUND_PATTERNS)
+						goto next_prefix_lookup;
+					/*
+					 * theoretically we don't have to break
+					 * here, but we don't want to search
+					 * for the other matching patterns on
+					 * the very same same place in the
+					 * buffer. That would mean the
+					 * (pattern & mask) combinations are
+					 * not unique. So we break from inner
+					 * loop and move on to the next place
+					 * in the buffer.
+					 */
+					break;
+				}
+			}
+		}
+	}
+
+next_prefix_lookup:
+	/* check for possible prefix sizes for the next buffer */
+	for (j = 0; j < patterns_num; j++) {
+		prefix_size_masks->size_mask[j] = 0;
+		for (i = 1; i < patterns[j].size; i++) {
+			/*
+			 * avoid looking outside of the buffer
+			 * or reusing previously used data.
+			 */
+			if (i > (buf_size - start_offset))
+				break;
+
+			if (mpq_dmx_patterns_match(patterns[j].pattern,
+					(buf + buf_size - i),
+					patterns[j].mask, i)) {
+				prefix_size_masks->size_mask[j] |=
+						(1 << (i - 1));
+			}
+		}
+	}
+
+	return found;
+}
+
+/*
+ * mpq_dmx_get_pattern_params -
+ * get a pointer to the relevant pattern parameters structure,
+ * based on the video parameters.
+ *
+ * @video_params: the video parameters (e.g. video standard).
+ * @patterns: a pointer to a pointer to the pattern parameters,
+ * updated by this function.
+ * @patterns_num: number of patterns, updated by this function.
+ */
+static inline int mpq_dmx_get_pattern_params(
+		struct dmx_indexing_video_params *video_params,
+		const struct mpq_framing_pattern_lookup_params **patterns,
+		int *patterns_num)
+{
+	switch (video_params->standard) {
+	case DMX_INDEXING_MPEG2:
+		*patterns = mpeg2_patterns;
+		*patterns_num = MPQ_MPEG2_PATTERN_NUM;
+		break;
+	case DMX_INDEXING_H264:
+		*patterns = h264_patterns;
+		*patterns_num = MPQ_H264_PATTERN_NUM;
+		break;
+	case DMX_INDEXING_VC1:
+		*patterns = vc1_patterns;
+		*patterns_num = MPQ_VC1_PATTERN_NUM;
+		break;
+	default:
+		MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
+		*patterns = NULL;
+		*patterns_num = 0;
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* Extend dvb-demux debugfs with HW statistics */
+void mpq_dmx_init_hw_statistics(struct mpq_demux *mpq_demux)
+{
+	/*
+	 * Extend dvb-demux debugfs with HW statistics.
+	 * Note that destruction of debugfs directory is done
+	 * when dvb-demux is terminated.
+	 */
+	mpq_demux->hw_notification_count = 0;
+	mpq_demux->hw_notification_rate = 0;
+	mpq_demux->hw_notification_size = 0;
+	mpq_demux->decoder_tsp_drop_count = 0;
+
+	if (mpq_demux->demux.debugfs_demux_dir != NULL) {
+		debugfs_create_u32(
+			"hw_notification_rate",
+			S_IRUGO|S_IWUGO,
+			mpq_demux->demux.debugfs_demux_dir,
+			&mpq_demux->hw_notification_rate);
+
+		debugfs_create_u32(
+			"hw_notification_count",
+			S_IRUGO|S_IWUGO,
+			mpq_demux->demux.debugfs_demux_dir,
+			&mpq_demux->hw_notification_count);
+
+		debugfs_create_u32(
+			"hw_notification_size",
+			S_IRUGO|S_IWUGO,
+			mpq_demux->demux.debugfs_demux_dir,
+			&mpq_demux->hw_notification_size);
+
+		debugfs_create_u32(
+			"decoder_tsp_drop_count",
+			S_IRUGO|S_IWUGO,
+			mpq_demux->demux.debugfs_demux_dir,
+			&mpq_demux->decoder_tsp_drop_count);
+	}
+}
+EXPORT_SYMBOL(mpq_dmx_init_hw_statistics);
+
+
+/* Update dvb-demux debugfs with HW notification statistics */
+void mpq_dmx_update_hw_statistics(struct mpq_demux *mpq_demux)
+{
+	struct timespec curr_time, delta_time;
+	u64 delta_time_ms;
+
+	curr_time = current_kernel_time();
+	if (likely(mpq_demux->hw_notification_count)) {
+		/* calculate time-delta between notifications */
+		delta_time =
+			timespec_sub(
+					curr_time,
+					mpq_demux->last_notification_time);
+
+		delta_time_ms = (u64)timespec_to_ns(&delta_time);
+		delta_time_ms = div64_u64(delta_time_ms, 1000000); /* ns->ms */
+
+		mpq_demux->hw_notification_rate = delta_time_ms;
+	}
+
+	mpq_demux->hw_notification_count++;
+	mpq_demux->last_notification_time = curr_time;
+}
+EXPORT_SYMBOL(mpq_dmx_update_hw_statistics);
+
+
+int mpq_dmx_plugin_init(mpq_dmx_init dmx_init_func)
+{
+	int i;
+	int result;
+	struct mpq_demux *mpq_demux;
+	struct dvb_adapter *mpq_adapter;
+
+	MPQ_DVB_DBG_PRINT("%s executed, device num %d\n",
+					  __func__,
+					  mpq_demux_device_num);
+
+	mpq_adapter = mpq_adapter_get();
+
+	if (mpq_adapter == NULL) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: mpq_adapter is not valid\n",
+			__func__);
+		result = -EPERM;
+		goto init_failed;
+	}
+
+	if (mpq_demux_device_num == 0) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: mpq_demux_device_num set to 0\n",
+			__func__);
+
+		result = -EPERM;
+		goto init_failed;
+	}
+
+	mpq_dmx_info.devices = NULL;
+	mpq_dmx_info.ion_client = NULL;
+
+	/*
+	 * TODO: the following should be set based on the decoder:
+	 * 0 means the decoder doesn't handle framing, so framing
+	 * is done by demux. 1 means the decoder handles framing.
+	 */
+	mpq_dmx_info.decoder_framing = 0;
+
+	/* Allocate memory for all MPQ devices */
+	mpq_dmx_info.devices =
+		vmalloc(mpq_demux_device_num*sizeof(struct mpq_demux));
+
+	if (!mpq_dmx_info.devices) {
+		MPQ_DVB_ERR_PRINT(
+				"%s: failed to allocate devices memory\n",
+				__func__);
+
+		result = -ENOMEM;
+		goto init_failed;
+	}
+
+	/* Zero allocated memory */
+	memset(mpq_dmx_info.devices,
+		   0,
+		   mpq_demux_device_num*sizeof(struct mpq_demux));
+
+	/*
+	 * Create a new ION client used by demux to allocate memory
+	 * for decoder's buffers.
+	 */
+	mpq_dmx_info.ion_client =
+		msm_ion_client_create(UINT_MAX, "demux client");
+
+	if (IS_ERR_OR_NULL(mpq_dmx_info.ion_client)) {
+		MPQ_DVB_ERR_PRINT(
+				"%s: msm_ion_client_create\n",
+				__func__);
+
+		result = PTR_ERR(mpq_dmx_info.ion_client);
+		mpq_dmx_info.ion_client = NULL;
+		goto init_failed_free_demux_devices;
+	}
+
+	/* Initialize and register all demux devices to the system */
+	for (i = 0; i < mpq_demux_device_num; i++) {
+		mpq_demux = mpq_dmx_info.devices+i;
+
+		/* initialize demux source to memory by default */
+		mpq_demux->source = DMX_SOURCE_DVR0 + i;
+
+		/*
+		 * Give the plugin pointer to the ion client so
+		 * that it can allocate memory from ION if it requires so
+		 */
+		mpq_demux->ion_client = mpq_dmx_info.ion_client;
+
+		spin_lock_init(&mpq_demux->feed_lock);
+
+		/*
+		 * mpq_demux_plugin_hw_init should be implemented
+		 * by the specific plugin
+		 */
+		result = dmx_init_func(mpq_adapter, mpq_demux);
+		if (result < 0) {
+			MPQ_DVB_ERR_PRINT(
+				"%s: dmx_init_func (errno=%d)\n",
+				__func__,
+				result);
+
+			goto init_failed_free_demux_devices;
+		}
+
+		mpq_demux->is_initialized = 1;
+
+		/*
+		 * Add capability of receiving input from memory.
+		 * Every demux in our system may be connected to memory input,
+		 * or any live input.
+		 */
+		mpq_demux->fe_memory.source = DMX_MEMORY_FE;
+		result =
+			mpq_demux->demux.dmx.add_frontend(
+					&mpq_demux->demux.dmx,
+					&mpq_demux->fe_memory);
+
+		if (result < 0) {
+			MPQ_DVB_ERR_PRINT(
+				"%s: add_frontend (mem) failed (errno=%d)\n",
+				__func__,
+				result);
+
+			goto init_failed_free_demux_devices;
+		}
+	}
+
+	return 0;
+
+init_failed_free_demux_devices:
+	mpq_dmx_plugin_exit();
+init_failed:
+	return result;
+}
+EXPORT_SYMBOL(mpq_dmx_plugin_init);
+
+
+void mpq_dmx_plugin_exit(void)
+{
+	int i;
+	struct mpq_demux *mpq_demux;
+
+	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+	if (mpq_dmx_info.ion_client != NULL) {
+		ion_client_destroy(mpq_dmx_info.ion_client);
+		mpq_dmx_info.ion_client = NULL;
+	}
+
+	if (mpq_dmx_info.devices != NULL) {
+		for (i = 0; i < mpq_demux_device_num; i++) {
+			mpq_demux = mpq_dmx_info.devices+i;
+
+			if (mpq_demux->is_initialized) {
+				mpq_demux->demux.dmx.remove_frontend(
+							&mpq_demux->demux.dmx,
+							&mpq_demux->fe_memory);
+
+				dvb_dmxdev_release(&mpq_demux->dmxdev);
+				dvb_dmx_release(&mpq_demux->demux);
+			}
+		}
+
+		vfree(mpq_dmx_info.devices);
+		mpq_dmx_info.devices = NULL;
+	}
+}
+EXPORT_SYMBOL(mpq_dmx_plugin_exit);
+
+
+int mpq_dmx_set_source(
+		struct dmx_demux *demux,
+		const dmx_source_t *src)
+{
+	int i;
+	int dvr_index;
+	int dmx_index;
+	struct dvb_demux *dvb_demux = (struct dvb_demux *)demux->priv;
+	struct mpq_demux *mpq_demux;
+
+	if ((mpq_dmx_info.devices == NULL) || (dvb_demux == NULL)) {
+		MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	mpq_demux = (struct mpq_demux *)dvb_demux->priv;
+	if (mpq_demux == NULL) {
+		MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	/*
+	 * For dvr sources,
+	 * verify that this source is connected to the respective demux
+	 */
+	dmx_index = mpq_demux - mpq_dmx_info.devices;
+
+	if (*src >= DMX_SOURCE_DVR0) {
+		dvr_index = *src - DMX_SOURCE_DVR0;
+
+		if (dvr_index != dmx_index) {
+			MPQ_DVB_ERR_PRINT(
+				"%s: can't connect demux%d to dvr%d\n",
+				__func__,
+				dmx_index,
+				dvr_index);
+			return -EINVAL;
+		}
+	}
+
+	/*
+	 * For front-end sources,
+	 * verify that this source is not already set to different demux
+	 */
+	for (i = 0; i < mpq_demux_device_num; i++) {
+		if ((&mpq_dmx_info.devices[i] != mpq_demux) &&
+			(mpq_dmx_info.devices[i].source == *src)) {
+			MPQ_DVB_ERR_PRINT(
+				"%s: demux%d source can't be set,\n"
+				"demux%d occupies this source already\n",
+				__func__,
+				dmx_index,
+				i);
+			return -EBUSY;
+		}
+	}
+
+	mpq_demux->source = *src;
+	return 0;
+}
+EXPORT_SYMBOL(mpq_dmx_set_source);
+
+
+int mpq_dmx_init_video_feed(struct dvb_demux_feed *feed)
+{
+	int ret;
+	void *packet_buffer;
+	void *payload_buffer;
+	struct mpq_video_feed_info *feed_data;
+	struct mpq_demux *mpq_demux =
+		(struct mpq_demux *)feed->demux->priv;
+	struct mpq_streambuffer *stream_buffer;
+	int actual_buffer_size;
+
+	/* Allocate memory for private feed data */
+	feed_data = vmalloc(sizeof(struct mpq_video_feed_info));
+
+	if (feed_data == NULL) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: FAILED to allocate private video feed data\n",
+			__func__);
+
+		ret = -ENOMEM;
+		goto init_failed;
+	}
+
+	/* get and store framing information if required */
+	if (!mpq_dmx_info.decoder_framing) {
+		mpq_dmx_get_pattern_params(&feed->indexing_params,
+				&feed_data->patterns, &feed_data->patterns_num);
+		if (feed_data->patterns == NULL) {
+			MPQ_DVB_ERR_PRINT(
+				"%s: FAILED to get framing pattern parameters\n",
+				__func__);
+
+			ret = -EINVAL;
+			goto init_failed_free_priv_data;
+		}
+	}
+
+	/* Allocate packet buffer holding the meta-data */
+	packet_buffer = vmalloc(VIDEO_META_DATA_BUFFER_SIZE);
+
+	if (packet_buffer == NULL) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: FAILED to allocate packets buffer\n",
+			__func__);
+
+		ret = -ENOMEM;
+		goto init_failed_free_priv_data;
+	}
+
+	/*
+	 * Allocate payload buffer through ION.
+	 * TODO: for scrambling support, need to check if the
+	 * stream is scrambled and allocate the buffer with secure
+	 * flag set.
+	 */
+
+	actual_buffer_size = feed->buffer_size;
+
+	actual_buffer_size += (SZ_4K - 1);
+	actual_buffer_size &= ~(SZ_4K - 1);
+
+	feed_data->payload_buff_handle =
+		ion_alloc(mpq_demux->ion_client,
+				  actual_buffer_size,
+				  SZ_4K,
+				  ION_HEAP(ION_CP_MM_HEAP_ID));
+
+	if (IS_ERR_OR_NULL(feed_data->payload_buff_handle)) {
+		ret = PTR_ERR(feed_data->payload_buff_handle);
+
+		MPQ_DVB_ERR_PRINT(
+			"%s: FAILED to allocate payload buffer %d\n",
+			__func__,
+			ret);
+
+		goto init_failed_free_packet_buffer;
+	}
+
+	payload_buffer =
+		ion_map_kernel(mpq_demux->ion_client,
+					   feed_data->payload_buff_handle,
+					   0);
+
+	if (IS_ERR_OR_NULL(payload_buffer)) {
+		ret = PTR_ERR(payload_buffer);
+
+		MPQ_DVB_ERR_PRINT(
+			"%s: FAILED to map payload buffer %d\n",
+			__func__,
+			ret);
+
+		goto init_failed_free_payload_buffer;
+	}
+
+	/* Register the new stream-buffer interface to MPQ adapter */
+	switch (feed->pes_type) {
+	case DMX_TS_PES_VIDEO0:
+		feed_data->stream_interface =
+			MPQ_ADAPTER_VIDEO0_STREAM_IF;
+		break;
+
+	case DMX_TS_PES_VIDEO1:
+		feed_data->stream_interface =
+			MPQ_ADAPTER_VIDEO1_STREAM_IF;
+		break;
+
+	case DMX_TS_PES_VIDEO2:
+		feed_data->stream_interface =
+			MPQ_ADAPTER_VIDEO2_STREAM_IF;
+		break;
+
+	case DMX_TS_PES_VIDEO3:
+		feed_data->stream_interface =
+			MPQ_ADAPTER_VIDEO3_STREAM_IF;
+		break;
+
+	default:
+		MPQ_DVB_ERR_PRINT(
+			"%s: Invalid pes type %d\n",
+			__func__,
+			feed->pes_type);
+		ret = -EINVAL;
+		goto init_failed_unmap_payload_buffer;
+	}
+
+	/* make sure not occupied already */
+	stream_buffer = NULL;
+	mpq_adapter_get_stream_if(
+			feed_data->stream_interface,
+			&stream_buffer);
+	if (stream_buffer != NULL) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: Video interface %d already occupied!\n",
+			__func__,
+			feed_data->stream_interface);
+		ret = -EBUSY;
+		goto init_failed_unmap_payload_buffer;
+	}
+
+	feed_data->video_buffer =
+		&mpq_dmx_info.decoder_buffers[feed_data->stream_interface];
+
+	mpq_streambuffer_init(
+			feed_data->video_buffer,
+			payload_buffer,
+			actual_buffer_size,
+			packet_buffer,
+			VIDEO_META_DATA_BUFFER_SIZE);
+
+	ret =
+		mpq_adapter_register_stream_if(
+			feed_data->stream_interface,
+			feed_data->video_buffer);
+
+	if (ret < 0) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: mpq_adapter_register_stream_if failed, "
+			"err = %d\n",
+			__func__,
+			ret);
+		goto init_failed_unmap_payload_buffer;
+	}
+
+	feed->buffer_size = actual_buffer_size;
+	feed_data->pes_payload_address =
+		(u32)feed_data->video_buffer->raw_data.data;
+
+	feed_data->pes_header_left_bytes = PES_MANDATORY_FIELDS_LEN;
+	feed_data->pes_header_offset = 0;
+	feed->pusi_seen = 0;
+	feed->peslen = 0;
+	feed_data->fullness_wait_cancel = 0;
+	feed_data->last_framing_match_address = 0;
+	feed_data->last_framing_match_type = DMX_FRM_UNKNOWN;
+	feed_data->found_sequence_header_pattern = 0;
+	memset(&feed_data->prefix_size, 0,
+			sizeof(struct mpq_framing_prefix_size_masks));
+	feed_data->first_pattern_offset = 0;
+	feed_data->first_prefix_size = 0;
+	feed_data->write_pts_dts = 0;
+
+	spin_lock(&mpq_demux->feed_lock);
+	feed->priv = (void *)feed_data;
+	spin_unlock(&mpq_demux->feed_lock);
+
+	return 0;
+
+init_failed_unmap_payload_buffer:
+	ion_unmap_kernel(mpq_demux->ion_client,
+					 feed_data->payload_buff_handle);
+init_failed_free_payload_buffer:
+	ion_free(mpq_demux->ion_client,
+			feed_data->payload_buff_handle);
+init_failed_free_packet_buffer:
+	vfree(packet_buffer);
+init_failed_free_priv_data:
+	vfree(feed_data);
+	feed->priv = NULL;
+init_failed:
+
+	return ret;
+}
+EXPORT_SYMBOL(mpq_dmx_init_video_feed);
+
+
+int mpq_dmx_terminate_video_feed(struct dvb_demux_feed *feed)
+{
+	struct mpq_video_feed_info *feed_data;
+	struct mpq_demux *mpq_demux;
+
+	if (feed->priv == NULL) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: invalid feed, feed->priv is NULL\n",
+			__func__);
+
+		return -EINVAL;
+	}
+
+	mpq_demux =
+		(struct mpq_demux *)feed->demux->priv;
+
+	feed_data =
+		(struct mpq_video_feed_info *)feed->priv;
+
+	spin_lock(&mpq_demux->feed_lock);
+	feed->priv = NULL;
+	spin_unlock(&mpq_demux->feed_lock);
+
+	wake_up_all(&feed_data->video_buffer->raw_data.queue);
+
+	mpq_adapter_unregister_stream_if(
+		feed_data->stream_interface);
+
+	vfree(feed_data->video_buffer->packet_data.data);
+
+	ion_unmap_kernel(mpq_demux->ion_client,
+					 feed_data->payload_buff_handle);
+
+	ion_free(mpq_demux->ion_client,
+			 feed_data->payload_buff_handle);
+
+	vfree(feed_data);
+
+	return 0;
+}
+EXPORT_SYMBOL(mpq_dmx_terminate_video_feed);
+
+int mpq_dmx_decoder_fullness_init(struct dvb_demux_feed *feed)
+{
+	struct mpq_demux *mpq_demux;
+
+	mpq_demux =
+		(struct mpq_demux *)feed->demux->priv;
+
+	if (mpq_dmx_is_video_feed(feed)) {
+		struct mpq_video_feed_info *feed_data;
+
+		spin_lock(&mpq_demux->feed_lock);
+
+		if (feed->priv == NULL) {
+			MPQ_DVB_ERR_PRINT(
+				"%s: invalid feed, feed->priv is NULL\n",
+				__func__);
+			spin_unlock(&mpq_demux->feed_lock);
+			return -EINVAL;
+		}
+
+		feed_data =
+			(struct mpq_video_feed_info *)feed->priv;
+
+		feed_data->fullness_wait_cancel = 0;
+
+		spin_unlock(&mpq_demux->feed_lock);
+
+		return 0;
+	}
+
+	/* else */
+	MPQ_DVB_DBG_PRINT(
+		"%s: Invalid feed type %d\n",
+		__func__,
+		feed->pes_type);
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL(mpq_dmx_decoder_fullness_init);
+
+int mpq_dmx_decoder_fullness_wait(
+		struct dvb_demux_feed *feed,
+		size_t required_space)
+{
+	struct mpq_demux *mpq_demux;
+
+	mpq_demux =
+		(struct mpq_demux *)feed->demux->priv;
+
+	if (mpq_dmx_is_video_feed(feed)) {
+		int ret;
+		struct mpq_video_feed_info *feed_data;
+		struct dvb_ringbuffer *video_buff;
+
+		spin_lock(&mpq_demux->feed_lock);
+
+		if (feed->priv == NULL) {
+			spin_unlock(&mpq_demux->feed_lock);
+			return -EINVAL;
+		}
+
+		feed_data =
+			(struct mpq_video_feed_info *)feed->priv;
+
+		video_buff =
+			&feed_data->video_buffer->raw_data;
+
+		ret = 0;
+		if ((feed_data != NULL) &&
+			(!feed_data->fullness_wait_cancel) &&
+			(dvb_ringbuffer_free(video_buff) < required_space)) {
+			DEFINE_WAIT(__wait);
+			for (;;) {
+				prepare_to_wait(
+					&video_buff->queue,
+					&__wait,
+					TASK_INTERRUPTIBLE);
+
+				if ((feed->priv == NULL) ||
+					(feed_data->fullness_wait_cancel) ||
+					(dvb_ringbuffer_free(video_buff) >=
+					required_space))
+					break;
+
+				if (!signal_pending(current)) {
+					spin_unlock(&mpq_demux->feed_lock);
+					schedule();
+					spin_lock(&mpq_demux->feed_lock);
+					continue;
+				}
+				ret = -ERESTARTSYS;
+				break;
+			}
+			finish_wait(&video_buff->queue, &__wait);
+		}
+
+		if (ret < 0) {
+			spin_unlock(&mpq_demux->feed_lock);
+			return ret;
+		}
+
+		if ((feed->priv == NULL) ||
+			(feed_data->fullness_wait_cancel)) {
+			spin_unlock(&mpq_demux->feed_lock);
+			return -EINVAL;
+		}
+
+		spin_unlock(&mpq_demux->feed_lock);
+		return 0;
+	}
+
+	/* else */
+	MPQ_DVB_DBG_PRINT(
+		"%s: Invalid feed type %d\n",
+		__func__,
+		feed->pes_type);
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL(mpq_dmx_decoder_fullness_wait);
+
+int mpq_dmx_decoder_fullness_abort(struct dvb_demux_feed *feed)
+{
+	struct mpq_demux *mpq_demux;
+
+	mpq_demux =
+		(struct mpq_demux *)feed->demux->priv;
+
+	if (mpq_dmx_is_video_feed(feed)) {
+		struct mpq_video_feed_info *feed_data;
+		struct dvb_ringbuffer *video_buff;
+
+		spin_lock(&mpq_demux->feed_lock);
+
+		if (feed->priv == NULL) {
+			MPQ_DVB_ERR_PRINT(
+				"%s: invalid feed, feed->priv is NULL\n",
+				__func__);
+			spin_unlock(&mpq_demux->feed_lock);
+			return -EINVAL;
+		}
+
+		feed_data =
+			(struct mpq_video_feed_info *)feed->priv;
+
+		video_buff =
+			&feed_data->video_buffer->raw_data;
+
+		feed_data->fullness_wait_cancel = 1;
+		spin_unlock(&mpq_demux->feed_lock);
+
+		wake_up_all(&video_buff->queue);
+
+		return 0;
+	}
+
+	/* else */
+	MPQ_DVB_ERR_PRINT(
+		"%s: Invalid feed type %d\n",
+		__func__,
+		feed->pes_type);
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL(mpq_dmx_decoder_fullness_abort);
+
+
+static inline int mpq_dmx_parse_mandatory_pes_header(
+				struct dvb_demux_feed *feed,
+				struct mpq_video_feed_info *feed_data,
+				struct pes_packet_header *pes_header,
+				const u8 *buf,
+				u32 *ts_payload_offset,
+				int *bytes_avail)
+{
+	int left_size, copy_len;
+
+	if (feed_data->pes_header_offset < PES_MANDATORY_FIELDS_LEN) {
+		left_size =
+			PES_MANDATORY_FIELDS_LEN -
+			feed_data->pes_header_offset;
+
+		copy_len = (left_size > *bytes_avail) ?
+					*bytes_avail :
+					left_size;
+
+		memcpy((u8 *)((u8 *)pes_header + feed_data->pes_header_offset),
+				(buf + *ts_payload_offset),
+				copy_len);
+
+		feed_data->pes_header_offset += copy_len;
+
+		if (left_size > *bytes_avail)
+			return -EINVAL;
+
+		/* else - we have beginning of PES header */
+		*bytes_avail -= left_size;
+		*ts_payload_offset += left_size;
+
+		/* Make sure the PES packet is valid */
+		if (mpq_dmx_is_valid_video_pes(pes_header) < 0) {
+			/*
+			 * Since the new PES header parsing
+			 * failed, reset pusi_seen to drop all
+			 * data until next PUSI
+			 */
+			feed->pusi_seen = 0;
+			feed_data->pes_header_offset = 0;
+
+			MPQ_DVB_ERR_PRINT(
+				"%s: invalid packet\n",
+				__func__);
+
+			return -EINVAL;
+		}
+
+		feed_data->pes_header_left_bytes =
+			pes_header->pes_header_data_length;
+	}
+
+	return 0;
+}
+
+static inline int mpq_dmx_parse_remaining_pes_header(
+				struct dvb_demux_feed *feed,
+				struct mpq_video_feed_info *feed_data,
+				struct pes_packet_header *pes_header,
+				const u8 *buf,
+				u32 *ts_payload_offset,
+				int *bytes_avail)
+{
+	int left_size, copy_len;
+
+	/* Remainning header bytes that need to be processed? */
+	if (!feed_data->pes_header_left_bytes)
+		return 0;
+
+	/* Did we capture the PTS value (if exists)? */
+	if ((*bytes_avail != 0) &&
+		(feed_data->pes_header_offset <
+		 (PES_MANDATORY_FIELDS_LEN+5)) &&
+		((pes_header->pts_dts_flag == 2) ||
+		 (pes_header->pts_dts_flag == 3))) {
+
+		/* 5 more bytes should be there */
+		left_size =
+			PES_MANDATORY_FIELDS_LEN + 5 -
+			feed_data->pes_header_offset;
+
+		copy_len = (left_size > *bytes_avail) ?
+					*bytes_avail :
+					left_size;
+
+		memcpy((u8 *)((u8 *)pes_header + feed_data->pes_header_offset),
+			(buf + *ts_payload_offset),
+			copy_len);
+
+		feed_data->pes_header_offset += copy_len;
+		feed_data->pes_header_left_bytes -= copy_len;
+
+		if (left_size > *bytes_avail)
+			return -EINVAL;
+
+		/* else - we have the PTS */
+		*bytes_avail -= copy_len;
+		*ts_payload_offset += copy_len;
+		feed_data->write_pts_dts = 1;
+	}
+
+	/* Did we capture the DTS value (if exist)? */
+	if ((*bytes_avail != 0) &&
+		(feed_data->pes_header_offset <
+		 (PES_MANDATORY_FIELDS_LEN+10)) &&
+		(pes_header->pts_dts_flag == 3)) {
+
+		/* 5 more bytes should be there */
+		left_size =
+			PES_MANDATORY_FIELDS_LEN + 10 -
+			feed_data->pes_header_offset;
+
+		copy_len = (left_size > *bytes_avail) ?
+					*bytes_avail :
+					left_size;
+
+		memcpy((u8 *)((u8 *)pes_header + feed_data->pes_header_offset),
+			(buf + *ts_payload_offset),
+			copy_len);
+
+		feed_data->pes_header_offset += copy_len;
+		feed_data->pes_header_left_bytes -= copy_len;
+
+		if (left_size > *bytes_avail)
+			return -EINVAL;
+
+		/* else - we have the DTS */
+		*bytes_avail -= copy_len;
+		*ts_payload_offset += copy_len;
+		feed_data->write_pts_dts = 1;
+	}
+
+	/* Any more header bytes?! */
+	if (feed_data->pes_header_left_bytes >= *bytes_avail) {
+		feed_data->pes_header_left_bytes -= *bytes_avail;
+		return -EINVAL;
+	}
+
+	/* Got PES header, process payload */
+	*bytes_avail -= feed_data->pes_header_left_bytes;
+	*ts_payload_offset += feed_data->pes_header_left_bytes;
+	feed_data->pes_header_left_bytes = 0;
+
+	return 0;
+}
+
+static inline void mpq_dmx_get_pts_dts(struct mpq_video_feed_info *feed_data,
+				struct pes_packet_header *pes_header,
+				struct mpq_adapter_video_meta_data *meta_data,
+				enum dmx_packet_type packet_type)
+{
+	struct dmx_pts_dts_info *info;
+
+	if (packet_type == DMX_PES_PACKET)
+		info = &(meta_data->info.pes.pts_dts_info);
+	else
+		info = &(meta_data->info.framing.pts_dts_info);
+
+	if (feed_data->write_pts_dts) {
+		if ((pes_header->pts_dts_flag == 2) ||
+			(pes_header->pts_dts_flag == 3)) {
+			info->pts_exist = 1;
+
+			info->pts =
+				((u64)pes_header->pts_1 << 30) |
+				((u64)pes_header->pts_2 << 22) |
+				((u64)pes_header->pts_3 << 15) |
+				((u64)pes_header->pts_4 << 7) |
+				(u64)pes_header->pts_5;
+		} else {
+			info->pts_exist = 0;
+			info->pts = 0;
+		}
+
+		if (pes_header->pts_dts_flag == 3) {
+			info->dts_exist = 1;
+
+			info->dts =
+				((u64)pes_header->dts_1 << 30) |
+				((u64)pes_header->dts_2 << 22) |
+				((u64)pes_header->dts_3 << 15) |
+				((u64)pes_header->dts_4 << 7) |
+				(u64)pes_header->dts_5;
+		} else {
+			info->dts_exist = 0;
+			info->dts = 0;
+		}
+	} else {
+		info->pts_exist = 0;
+		info->dts_exist = 0;
+	}
+}
+
+static int mpq_dmx_process_video_packet_framing(
+			struct dvb_demux_feed *feed,
+			const u8 *buf)
+{
+	int bytes_avail;
+	u32 ts_payload_offset;
+	struct mpq_video_feed_info *feed_data;
+	const struct ts_packet_header *ts_header;
+	struct mpq_streambuffer *stream_buffer;
+	struct pes_packet_header *pes_header;
+	struct mpq_demux *mpq_demux;
+
+	struct mpq_framing_pattern_lookup_results framing_res;
+	int found_patterns = 0;
+	int first_pattern = 0;
+	int i;
+	u32 pattern_addr = 0;
+	int is_video_frame = 0;
+
+	mpq_demux = (struct mpq_demux *)feed->demux->priv;
+
+	spin_lock(&mpq_demux->feed_lock);
+
+	feed_data = (struct mpq_video_feed_info *)feed->priv;
+
+	if (unlikely(feed_data == NULL)) {
+		spin_unlock(&mpq_demux->feed_lock);
+		return 0;
+	}
+
+	ts_header = (const struct ts_packet_header *)buf;
+
+	stream_buffer = feed_data->video_buffer;
+
+	pes_header = &feed_data->pes_header;
+
+	/* MPQ_DVB_DBG_PRINT("TS packet: %X %X %X %X %X%X %X %X %X\n",
+		ts_header->sync_byte,
+		ts_header->transport_error_indicator,
+		ts_header->payload_unit_start_indicator,
+		ts_header->transport_priority,
+		ts_header->pid_msb,
+		ts_header->pid_lsb,
+		ts_header->transport_scrambling_control,
+		ts_header->adaptation_field_control,
+		ts_header->continuity_counter); */
+
+	/* Make sure this TS packet has a payload and not scrambled */
+	if ((ts_header->sync_byte != 0x47) ||
+		(ts_header->adaptation_field_control == 0) ||
+		(ts_header->adaptation_field_control == 2) ||
+		(ts_header->transport_scrambling_control)) {
+		/* continue to next packet */
+		spin_unlock(&mpq_demux->feed_lock);
+		return 0;
+	}
+
+	if (ts_header->payload_unit_start_indicator) { /* PUSI? */
+		if (feed->pusi_seen) { /* Did we see PUSI before? */
+			/*
+			 * Double check that we are not in middle of
+			 * previous PES header parsing.
+			 */
+			if (feed_data->pes_header_left_bytes != 0) {
+				MPQ_DVB_ERR_PRINT(
+					"%s: received PUSI"
+					"while handling PES header"
+					"of previous PES\n",
+					__func__);
+			}
+
+			feed->peslen = 0;
+			feed_data->pes_header_offset = 0;
+			feed_data->pes_header_left_bytes =
+				PES_MANDATORY_FIELDS_LEN;
+			feed_data->write_pts_dts = 0;
+		} else {
+			feed->pusi_seen = 1;
+		}
+	}
+
+	/*
+	 * Parse PES data only if PUSI was encountered,
+	 * otherwise the data is dropped
+	 */
+	if (!feed->pusi_seen) {
+		spin_unlock(&mpq_demux->feed_lock);
+		return 0; /* drop and wait for next packets */
+	}
+
+	ts_payload_offset = sizeof(struct ts_packet_header);
+
+	/* Skip adaptation field if exists */
+	if (ts_header->adaptation_field_control == 3)
+		ts_payload_offset += buf[ts_payload_offset] + 1;
+
+	/* 188 bytes: the size of a TS packet including the TS packet header */
+	bytes_avail = 188 - ts_payload_offset;
+
+	/* Get the mandatory fields of the video PES header */
+	if (mpq_dmx_parse_mandatory_pes_header(feed, feed_data,
+						pes_header, buf,
+						&ts_payload_offset,
+						&bytes_avail)) {
+		spin_unlock(&mpq_demux->feed_lock);
+		return 0;
+	}
+
+	if (mpq_dmx_parse_remaining_pes_header(feed, feed_data,
+						pes_header, buf,
+						&ts_payload_offset,
+						&bytes_avail)) {
+		spin_unlock(&mpq_demux->feed_lock);
+		return 0;
+	}
+
+	/*
+	 * If we reached here,
+	 * then we are now at the PES payload data
+	 */
+	if (bytes_avail == 0) {
+		spin_unlock(&mpq_demux->feed_lock);
+		return 0;
+	}
+
+	/*
+	 * the decoder requires demux to do framing,
+	 * so search for the patterns now.
+	 */
+	found_patterns = mpq_dmx_framing_pattern_search(
+				feed_data->patterns,
+				feed_data->patterns_num,
+				(buf + ts_payload_offset),
+				bytes_avail,
+				&feed_data->prefix_size,
+				&framing_res);
+
+	if (!(feed_data->found_sequence_header_pattern)) {
+		for (i = 0; i < found_patterns; i++) {
+			if ((framing_res.info[i].type ==
+				DMX_FRM_MPEG2_SEQUENCE_HEADER) ||
+			    (framing_res.info[i].type ==
+				DMX_FRM_H264_SPS) ||
+			    (framing_res.info[i].type ==
+				DMX_FRM_VC1_SEQUENCE_HEADER)) {
+
+				MPQ_DVB_DBG_PRINT(
+					"%s: Found Sequence Pattern, buf %p, "
+					"i = %d, offset = %d, type = %d\n",
+					__func__, buf, i,
+					framing_res.info[i].offset,
+					framing_res.info[i].type);
+
+				first_pattern = i;
+				feed_data->found_sequence_header_pattern = 1;
+				ts_payload_offset +=
+					framing_res.info[i].offset;
+				bytes_avail -= framing_res.info[i].offset;
+
+				if (framing_res.info[i].used_prefix_size) {
+					feed_data->first_prefix_size =
+						framing_res.info[i].
+							used_prefix_size;
+				}
+				/*
+				 * if this is the first pattern we write,
+				 * no need to take offset into account since we
+				 * dropped all data before it (so effectively
+				 * offset is 0).
+				 * we save the first pattern offset and take
+				 * it into consideration for the rest of the
+				 * patterns found in this buffer.
+				 */
+				feed_data->first_pattern_offset =
+					framing_res.info[i].offset;
+				break;
+			}
+		}
+	}
+
+	/*
+	 * If decoder requires demux to do framing,
+	 * pass data to decoder only after sequence header
+	 * or equivalent is found. Otherwise the data is dropped.
+	 */
+	if (!(feed_data->found_sequence_header_pattern)) {
+		spin_unlock(&mpq_demux->feed_lock);
+		return 0;
+	}
+
+	/*
+	 * write prefix used to find first Sequence pattern, if needed.
+	 * feed_data->patterns[0].pattern always contains the Sequence
+	 * pattern.
+	 */
+	if (feed_data->first_prefix_size) {
+		if (mpq_streambuffer_data_write(stream_buffer,
+					(feed_data->patterns[0].pattern),
+					feed_data->first_prefix_size) < 0) {
+			mpq_demux->decoder_tsp_drop_count++;
+			spin_unlock(&mpq_demux->feed_lock);
+			return 0;
+		}
+		feed_data->first_prefix_size = 0;
+	}
+	/* write data to payload buffer */
+	if (mpq_streambuffer_data_write(stream_buffer,
+					(buf + ts_payload_offset),
+					bytes_avail) < 0) {
+		mpq_demux->decoder_tsp_drop_count++;
+	} else {
+		struct mpq_streambuffer_packet_header packet;
+		struct mpq_adapter_video_meta_data meta_data;
+
+		feed->peslen += bytes_avail;
+
+		meta_data.packet_type = DMX_FRAMING_INFO_PACKET;
+		packet.user_data_len =
+				sizeof(struct mpq_adapter_video_meta_data);
+
+		for (i = first_pattern; i < found_patterns; i++) {
+			if (feed_data->last_framing_match_address) {
+				is_video_frame = mpq_dmx_is_video_frame(
+					feed->indexing_params.standard,
+					feed_data->last_framing_match_type);
+				if (is_video_frame == 1) {
+					mpq_dmx_get_pts_dts(feed_data,
+						pes_header,
+						&meta_data,
+						DMX_FRAMING_INFO_PACKET);
+				} else {
+					meta_data.info.framing.
+						pts_dts_info.pts_exist = 0;
+					meta_data.info.framing.
+						pts_dts_info.dts_exist = 0;
+				}
+				/*
+				 * writing meta-data that includes
+				 * framing information
+				 */
+				meta_data.info.framing.pattern_type =
+					feed_data->last_framing_match_type;
+				packet.raw_data_addr =
+					feed_data->last_framing_match_address;
+
+				pattern_addr = feed_data->pes_payload_address +
+					framing_res.info[i].offset -
+					framing_res.info[i].used_prefix_size;
+
+				if ((pattern_addr -
+					feed_data->first_pattern_offset) <
+					feed_data->last_framing_match_address) {
+					/* wraparound case */
+					packet.raw_data_len =
+						(pattern_addr -
+						feed_data->
+						   last_framing_match_address +
+						stream_buffer->raw_data.size) -
+						feed_data->first_pattern_offset;
+				} else {
+					packet.raw_data_len =
+					  pattern_addr -
+					  feed_data->
+						last_framing_match_address -
+					  feed_data->first_pattern_offset;
+				}
+
+				MPQ_DVB_DBG_PRINT("Writing Packet: "
+					"addr = 0x%X, len = %d, type = %d, "
+					"isPts = %d, isDts = %d\n",
+					packet.raw_data_addr,
+					packet.raw_data_len,
+					meta_data.info.framing.pattern_type,
+					meta_data.info.framing.
+						pts_dts_info.pts_exist,
+					meta_data.info.framing.
+						pts_dts_info.dts_exist);
+
+				if (mpq_streambuffer_pkt_write(stream_buffer,
+						&packet,
+						(u8 *)&meta_data) < 0) {
+							MPQ_DVB_ERR_PRINT(
+								"%s: "
+								"Couldn't write packet. "
+								"Should never happen\n",
+								__func__);
+				} else {
+					if (is_video_frame == 1)
+						feed_data->write_pts_dts = 0;
+				}
+			}
+
+			/* save the last match for next time */
+			feed_data->last_framing_match_type =
+					framing_res.info[i].type;
+
+			feed_data->last_framing_match_address =
+				(feed_data->pes_payload_address +
+				framing_res.info[i].offset -
+				framing_res.info[i].used_prefix_size -
+				feed_data->first_pattern_offset);
+		}
+		/*
+		 * the first pattern offset is needed only for the group of
+		 * patterns that are found and written with the first pattern.
+		 */
+		feed_data->first_pattern_offset = 0;
+
+		feed_data->pes_payload_address =
+			(u32)stream_buffer->raw_data.data +
+			stream_buffer->raw_data.pwrite;
+	}
+
+	spin_unlock(&mpq_demux->feed_lock);
+
+	return 0;
+}
+
+static int mpq_dmx_process_video_packet_no_framing(
+			struct dvb_demux_feed *feed,
+			const u8 *buf)
+{
+	int bytes_avail;
+	u32 ts_payload_offset;
+	struct mpq_video_feed_info *feed_data;
+	const struct ts_packet_header *ts_header;
+	struct mpq_streambuffer *stream_buffer;
+	struct pes_packet_header *pes_header;
+	struct mpq_demux *mpq_demux;
+
+	mpq_demux = (struct mpq_demux *)feed->demux->priv;
+
+	spin_lock(&mpq_demux->feed_lock);
+
+	feed_data = (struct mpq_video_feed_info *)feed->priv;
+
+	if (unlikely(feed_data == NULL)) {
+		spin_unlock(&mpq_demux->feed_lock);
+		return 0;
+	}
+
+	ts_header = (const struct ts_packet_header *)buf;
+
+	stream_buffer =	feed_data->video_buffer;
+
+	pes_header = &feed_data->pes_header;
+
+	/* MPQ_DVB_DBG_PRINT("TS packet: %X %X %X %X %X%X %X %X %X\n",
+		ts_header->sync_byte,
+		ts_header->transport_error_indicator,
+		ts_header->payload_unit_start_indicator,
+		ts_header->transport_priority,
+		ts_header->pid_msb,
+		ts_header->pid_lsb,
+		ts_header->transport_scrambling_control,
+		ts_header->adaptation_field_control,
+		ts_header->continuity_counter); */
+
+	/* Make sure this TS packet has a payload and not scrambled */
+	if ((ts_header->sync_byte != 0x47) ||
+		(ts_header->adaptation_field_control == 0) ||
+		(ts_header->adaptation_field_control == 2) ||
+		(ts_header->transport_scrambling_control)) {
+		/* continue to next packet */
+		spin_unlock(&mpq_demux->feed_lock);
+		return 0;
+	}
+
+	if (ts_header->payload_unit_start_indicator) { /* PUSI? */
+		if (feed->pusi_seen) { /* Did we see PUSI before? */
+			struct mpq_streambuffer_packet_header packet;
+			struct mpq_adapter_video_meta_data meta_data;
+
+			/*
+			 * Close previous PES.
+			 * Push new packet to the meta-data buffer.
+			 * Double check that we are not in middle of
+			 * previous PES header parsing.
+			 */
+
+			if (0 == feed_data->pes_header_left_bytes) {
+				packet.raw_data_addr =
+					feed_data->pes_payload_address;
+
+				packet.raw_data_len = feed->peslen;
+
+				packet.user_data_len =
+					sizeof(struct
+						mpq_adapter_video_meta_data);
+
+				mpq_dmx_get_pts_dts(feed_data, pes_header,
+							&meta_data,
+							DMX_PES_PACKET);
+
+				meta_data.packet_type = DMX_PES_PACKET;
+
+				if (mpq_streambuffer_pkt_write(
+						stream_buffer,
+						&packet,
+						(u8 *)&meta_data) < 0)
+					MPQ_DVB_ERR_PRINT(
+						"%s: "
+						"Couldn't write packet. "
+						"Should never happen\n",
+						__func__);
+				else
+					feed_data->write_pts_dts = 0;
+			} else {
+				MPQ_DVB_ERR_PRINT(
+					"%s: received PUSI"
+					"while handling PES header"
+					"of previous PES\n",
+					__func__);
+			}
+
+			/* Reset PES info */
+			feed_data->pes_payload_address =
+				(u32)stream_buffer->raw_data.data +
+				stream_buffer->raw_data.pwrite;
+
+			feed->peslen = 0;
+			feed_data->pes_header_offset = 0;
+			feed_data->pes_header_left_bytes =
+				PES_MANDATORY_FIELDS_LEN;
+		} else {
+			feed->pusi_seen = 1;
+		}
+	}
+
+	/*
+	 * Parse PES data only if PUSI was encountered,
+	 * otherwise the data is dropped
+	 */
+	if (!feed->pusi_seen) {
+		spin_unlock(&mpq_demux->feed_lock);
+		return 0; /* drop and wait for next packets */
+	}
+
+	ts_payload_offset = sizeof(struct ts_packet_header);
+
+	/* Skip adaptation field if exists */
+	if (ts_header->adaptation_field_control == 3)
+		ts_payload_offset += buf[ts_payload_offset] + 1;
+
+	/* 188 bytes: size of a TS packet including the TS packet header */
+	bytes_avail = 188 - ts_payload_offset;
+
+	/* Get the mandatory fields of the video PES header */
+	if (mpq_dmx_parse_mandatory_pes_header(feed, feed_data,
+						pes_header, buf,
+						&ts_payload_offset,
+						&bytes_avail)) {
+		spin_unlock(&mpq_demux->feed_lock);
+		return 0;
+	}
+
+	if (mpq_dmx_parse_remaining_pes_header(feed, feed_data,
+						pes_header, buf,
+						&ts_payload_offset,
+						&bytes_avail)) {
+		spin_unlock(&mpq_demux->feed_lock);
+		return 0;
+	}
+
+	/*
+	 * If we reached here,
+	 * then we are now at the PES payload data
+	 */
+	if (bytes_avail == 0) {
+		spin_unlock(&mpq_demux->feed_lock);
+		return 0;
+	}
+
+	if (mpq_streambuffer_data_write(
+				stream_buffer,
+				buf+ts_payload_offset,
+				bytes_avail) < 0)
+		mpq_demux->decoder_tsp_drop_count++;
+	else
+		feed->peslen += bytes_avail;
+
+	spin_unlock(&mpq_demux->feed_lock);
+
+	return 0;
+}
+
+int mpq_dmx_process_video_packet(
+			struct dvb_demux_feed *feed,
+			const u8 *buf)
+{
+	if (mpq_dmx_info.decoder_framing)
+		return mpq_dmx_process_video_packet_no_framing(feed, buf);
+	else
+		return mpq_dmx_process_video_packet_framing(feed, buf);
+}
+EXPORT_SYMBOL(mpq_dmx_process_video_packet);
+
+int mpq_dmx_process_pcr_packet(
+			struct dvb_demux_feed *feed,
+			const u8 *buf)
+{
+	int i;
+	u64 pcr;
+	u64 stc;
+	u8 output[PCR_STC_LEN];
+	struct mpq_demux *mpq_demux =
+		(struct mpq_demux *)feed->demux->priv;
+	const struct ts_packet_header *ts_header;
+	const struct ts_adaptation_field *adaptation_field;
+
+	/*
+	 * When we play from front-end, we configure HW
+	 * to output the extra timestamp, if we are playing
+	 * from DVR, make sure the format is 192 packet.
+	 */
+	if ((mpq_demux->source >= DMX_SOURCE_DVR0) &&
+		(mpq_demux->demux.tsp_format != DMX_TSP_FORMAT_192_TAIL)) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: invalid packet format %d for PCR extraction\n",
+			__func__,
+			mpq_demux->demux.tsp_format);
+
+		 return -EINVAL;
+	}
+
+	ts_header = (const struct ts_packet_header *)buf;
+
+	/* Make sure this TS packet has a adaptation field */
+	if ((ts_header->sync_byte != 0x47) ||
+		(ts_header->adaptation_field_control == 0) ||
+		(ts_header->adaptation_field_control == 1)) {
+		return 0;
+	}
+
+	adaptation_field = (const struct ts_adaptation_field *)
+			(buf + sizeof(struct ts_packet_header));
+
+	if ((!adaptation_field->adaptation_field_length) ||
+		(!adaptation_field->PCR_flag))
+		return 0; /* 0 adaptation field or no PCR */
+
+	pcr = ((u64)adaptation_field->program_clock_reference_base_1) << 25;
+	pcr += ((u64)adaptation_field->program_clock_reference_base_2) << 17;
+	pcr += ((u64)adaptation_field->program_clock_reference_base_3) << 9;
+	pcr += ((u64)adaptation_field->program_clock_reference_base_4) << 1;
+	pcr += adaptation_field->program_clock_reference_base_5;
+	pcr *= 300;
+	pcr +=
+		(((u64)adaptation_field->program_clock_reference_ext_1) << 8) +
+		adaptation_field->program_clock_reference_ext_2;
+
+	stc = buf[190] << 16;
+	stc += buf[189] << 8;
+	stc += buf[188];
+	stc *= 256; /* convert from 105.47 KHZ to 27MHz */
+
+	output[0] = adaptation_field->discontinuity_indicator;
+
+	for (i = 1; i <= 8; i++)
+		output[i] = (stc >> ((8-i) << 3)) & 0xFF;
+
+	for (i = 9; i <= 16; i++)
+		output[i] = (pcr >> ((16-i) << 3)) & 0xFF;
+
+	feed->cb.ts(output, PCR_STC_LEN,
+				NULL, 0,
+				&feed->feed.ts, DMX_OK);
+	return 0;
+}
+EXPORT_SYMBOL(mpq_dmx_process_pcr_packet);
+
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h
new file mode 100644
index 0000000..0275b14
--- /dev/null
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h
@@ -0,0 +1,538 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MPQ_DMX_PLUGIN_COMMON_H
+#define _MPQ_DMX_PLUGIN_COMMON_H
+
+#include <linux/ion.h>
+
+#include "dvbdev.h"
+#include "dmxdev.h"
+#include "demux.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "mpq_adapter.h"
+
+
+/* Max number open() request can be done on demux device */
+#define MPQ_MAX_DMX_FILES				128
+
+
+/**
+ * TSIF alias name length
+ */
+#define TSIF_NAME_LENGTH				10
+
+#define MPQ_MAX_FOUND_PATTERNS				5
+
+/**
+ * struct mpq_demux - mpq demux information
+ * @demux: The dvb_demux instance used by mpq_demux
+ * @dmxdev: The dmxdev instance used by mpq_demux
+ * @fe_memory: Handle of front-end memory source to mpq_demux
+ * @source: The current source connected to the demux
+ * @is_initialized: Indicates whether this demux device was
+ *                  initialized or not.
+ * @ion_client: ION demux client used to allocate memory from ION.
+ * @feed_lock: Lock used to protect against private feed data
+ * @hw_notification_rate: Notification rate in msec, exposed in debugfs.
+ * @hw_notification_count: Notification count, exposed in debugfs.
+ * @hw_notification_size: Notification size in bytes, exposed in debugfs.
+ * @decoder_tsp_drop_count: Counter of number of dropped TS packets
+ * due to decoder buffer fullness, exposed in debugfs.
+ * @last_notification_time: Time of last HW notification.
+ */
+struct mpq_demux {
+	struct dvb_demux demux;
+	struct dmxdev dmxdev;
+	struct dmx_frontend fe_memory;
+	dmx_source_t source;
+	int is_initialized;
+	struct ion_client *ion_client;
+	spinlock_t feed_lock;
+
+	/* debug-fs */
+	u32 hw_notification_rate;
+	u32 hw_notification_count;
+	u32 hw_notification_size;
+	u32 decoder_tsp_drop_count;
+	struct timespec last_notification_time;
+};
+
+/**
+ * mpq_dmx_init - initialization and registration function of
+ * single MPQ demux device
+ *
+ * @adapter: The adapter to register mpq_demux to
+ * @mpq_demux: The mpq demux to initialize
+ *
+ * Every HW pluging need to provide implementation of such
+ * function that will be called for each demux device on the
+ * module initialization. The function mpq_demux_plugin_init
+ * should be called during the HW plugin module initialization.
+ */
+typedef int (*mpq_dmx_init)(
+				struct dvb_adapter *mpq_adapter,
+				struct mpq_demux *demux);
+
+/**
+ * struct ts_packet_header - Transport packet header
+ * as defined in MPEG2 transport stream standard.
+ */
+struct ts_packet_header {
+#if defined(__BIG_ENDIAN_BITFIELD)
+	unsigned sync_byte:8;
+	unsigned transport_error_indicator:1;
+	unsigned payload_unit_start_indicator:1;
+	unsigned transport_priority:1;
+	unsigned pid_msb:5;
+	unsigned pid_lsb:8;
+	unsigned transport_scrambling_control:2;
+	unsigned adaptation_field_control:2;
+	unsigned continuity_counter:4;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+	unsigned sync_byte:8;
+	unsigned pid_msb:5;
+	unsigned transport_priority:1;
+	unsigned payload_unit_start_indicator:1;
+	unsigned transport_error_indicator:1;
+	unsigned pid_lsb:8;
+	unsigned continuity_counter:4;
+	unsigned adaptation_field_control:2;
+	unsigned transport_scrambling_control:2;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+} __packed;
+
+/**
+ * struct ts_adaptation_field - Adaptation field prefix
+ * as defined in MPEG2 transport stream standard.
+ */
+struct ts_adaptation_field {
+#if defined(__BIG_ENDIAN_BITFIELD)
+	unsigned adaptation_field_length:8;
+	unsigned discontinuity_indicator:1;
+	unsigned random_access_indicator:1;
+	unsigned elementary_stream_priority_indicator:1;
+	unsigned PCR_flag:1;
+	unsigned OPCR_flag:1;
+	unsigned splicing_point_flag:1;
+	unsigned transport_private_data_flag:1;
+	unsigned adaptation_field_extension_flag:1;
+	unsigned program_clock_reference_base_1:8;
+	unsigned program_clock_reference_base_2:8;
+	unsigned program_clock_reference_base_3:8;
+	unsigned program_clock_reference_base_4:8;
+	unsigned program_clock_reference_base_5:1;
+	unsigned reserved:6;
+	unsigned program_clock_reference_ext_1:1;
+	unsigned program_clock_reference_ext_2:8;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+	unsigned adaptation_field_length:8;
+	unsigned adaptation_field_extension_flag:1;
+	unsigned transport_private_data_flag:1;
+	unsigned splicing_point_flag:1;
+	unsigned OPCR_flag:1;
+	unsigned PCR_flag:1;
+	unsigned elementary_stream_priority_indicator:1;
+	unsigned random_access_indicator:1;
+	unsigned discontinuity_indicator:1;
+	unsigned program_clock_reference_base_1:8;
+	unsigned program_clock_reference_base_2:8;
+	unsigned program_clock_reference_base_3:8;
+	unsigned program_clock_reference_base_4:8;
+	unsigned program_clock_reference_ext_1:1;
+	unsigned reserved:6;
+	unsigned program_clock_reference_base_5:1;
+	unsigned program_clock_reference_ext_2:8;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+} __packed;
+
+
+/*
+ * PES packet header containing dts and/or pts values
+ * as defined in MPEG2 transport stream standard.
+ */
+struct pes_packet_header {
+#if defined(__BIG_ENDIAN_BITFIELD)
+	unsigned packet_start_code_prefix_1:8;
+	unsigned packet_start_code_prefix_2:8;
+	unsigned packet_start_code_prefix_3:8;
+	unsigned stream_id:8;
+	unsigned pes_packet_length_msb:8;
+	unsigned pes_packet_length_lsb:8;
+	unsigned reserved_bits0:2;
+	unsigned pes_scrambling_control:2;
+	unsigned pes_priority:1;
+	unsigned data_alignment_indicator:1;
+	unsigned copyright:1;
+	unsigned original_or_copy:1;
+	unsigned pts_dts_flag:2;
+	unsigned escr_flag:1;
+	unsigned es_rate_flag:1;
+	unsigned dsm_trick_mode_flag:1;
+	unsigned additional_copy_info_flag:1;
+	unsigned pes_crc_flag:1;
+	unsigned pes_extension_flag:1;
+	unsigned pes_header_data_length:8;
+	unsigned reserved_bits1:4;
+	unsigned pts_1:3;
+	unsigned marker_bit0:1;
+	unsigned pts_2:8;
+	unsigned pts_3:7;
+	unsigned marker_bit1:1;
+	unsigned pts_4:8;
+	unsigned pts_5:7;
+	unsigned marker_bit2:1;
+	unsigned reserved_bits2:4;
+	unsigned dts_1:3;
+	unsigned marker_bit3:1;
+	unsigned dts_2:8;
+	unsigned dts_3:7;
+	unsigned marker_bit4:1;
+	unsigned dts_4:8;
+	unsigned dts_5:7;
+	unsigned marker_bit5:1;
+	unsigned reserved_bits3:4;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+	unsigned packet_start_code_prefix_1:8;
+	unsigned packet_start_code_prefix_2:8;
+	unsigned packet_start_code_prefix_3:8;
+	unsigned stream_id:8;
+	unsigned pes_packet_length_lsb:8;
+	unsigned pes_packet_length_msb:8;
+	unsigned original_or_copy:1;
+	unsigned copyright:1;
+	unsigned data_alignment_indicator:1;
+	unsigned pes_priority:1;
+	unsigned pes_scrambling_control:2;
+	unsigned reserved_bits0:2;
+	unsigned pes_extension_flag:1;
+	unsigned pes_crc_flag:1;
+	unsigned additional_copy_info_flag:1;
+	unsigned dsm_trick_mode_flag:1;
+	unsigned es_rate_flag:1;
+	unsigned escr_flag:1;
+	unsigned pts_dts_flag:2;
+	unsigned pes_header_data_length:8;
+	unsigned marker_bit0:1;
+	unsigned pts_1:3;
+	unsigned reserved_bits1:4;
+	unsigned pts_2:8;
+	unsigned marker_bit1:1;
+	unsigned pts_3:7;
+	unsigned pts_4:8;
+	unsigned marker_bit2:1;
+	unsigned pts_5:7;
+	unsigned marker_bit3:1;
+	unsigned dts_1:3;
+	unsigned reserved_bits2:4;
+	unsigned dts_2:8;
+	unsigned marker_bit4:1;
+	unsigned dts_3:7;
+	unsigned dts_4:8;
+	unsigned marker_bit5:1;
+	unsigned dts_5:7;
+	unsigned reserved_bits3:4;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+} __packed;
+
+/*
+ * mpq_framing_prefix_size_masks - possible prefix sizes.
+ *
+ * @size_mask: a bit mask (per pattern) of possible prefix sizes to use
+ * when searching for a pattern that started in the last buffer.
+ * Updated in mpq_dmx_framing_pattern_search for use in the next lookup
+ */
+struct mpq_framing_prefix_size_masks {
+	u32 size_mask[MPQ_MAX_FOUND_PATTERNS];
+};
+
+/*
+ * mpq_video_feed_info - private data used for video feed.
+ *
+ * @plugin_data: Underlying plugin's own private data.
+ * @video_buffer: Holds the streamer buffer shared with
+ * the decoder for feeds having the data going to the decoder.
+ * @pes_header: Used for feeds that output data to decoder,
+ * holds PES header of current processed PES.
+ * @pes_header_left_bytes: Used for feeds that output data to decoder,
+ * holds remainning PES header bytes of current processed PES.
+ * @pes_header_offset: Holds the offset within the current processed
+ * pes header.
+ * @fullness_wait_cancel: Flag used to signal to abort waiting for
+ * decoder's fullness.
+ * @pes_payload_address: Used for feeds that output data to decoder,
+ * holds current PES payload start address.
+ * @payload_buff_handle: ION handle for the allocated payload buffer
+ * @stream_interface: The ID of the video stream interface registered
+ * with this stream buffer.
+ * @patterns: pointer to the framing patterns to look for.
+ * @patterns_num: number of framing patterns.
+ * @last_framing_match_address: Used for saving the raw data address of
+ * the previous pattern match found in this video feed.
+ * @last_framing_match_type: Used for saving the type of
+ * the previous pattern match found in this video feed.
+ * @found_sequence_header_pattern: Flag used to note that an MPEG-2
+ * Sequence Header, H.264 SPS or VC-1 Sequence Header pattern
+ * (whichever is relevant according to the video standard) had already
+ * been found.
+ * @prefix_size: a bit mask representing the size(s) of possible prefixes
+ * to the pattern, already found in the previous buffer. If bit 0 is set,
+ * a prefix of size 1 was found. If bit 1 is set, a prefix of size 2 was
+ * found, etc. This supports a prefix size of up to 32, which is more
+ * than we need. The search function updates prefix_size as needed
+ * for the next buffer search.
+ * @first_pattern_offset: used to save the offset of the first pattern written
+ * to the stream buffer.
+ * @first_prefix_size: used to save the prefix size used to find the first
+ * pattern written to the stream buffer.
+ * @write_pts_dts: Flag used to decide if to write PTS/DTS information
+ * (if it is available in the PES header) in the meta-data passed
+ * to the video decoder. PTS/DTS information is written in the first
+ * packet after it is available.
+ */
+struct mpq_video_feed_info {
+	void *plugin_data;
+	struct mpq_streambuffer *video_buffer;
+	struct pes_packet_header pes_header;
+	u32 pes_header_left_bytes;
+	u32 pes_header_offset;
+	u32 pes_payload_address;
+	int fullness_wait_cancel;
+	struct ion_handle *payload_buff_handle;
+	enum mpq_adapter_stream_if stream_interface;
+	const struct mpq_framing_pattern_lookup_params *patterns;
+	int patterns_num;
+	u32 last_framing_match_address;
+	enum dmx_framing_pattern_type last_framing_match_type;
+	int found_sequence_header_pattern;
+	struct mpq_framing_prefix_size_masks prefix_size;
+	u32 first_pattern_offset;
+	u32 first_prefix_size;
+	int write_pts_dts;
+};
+
+/**
+ * mpq_demux_plugin_init - Initialize demux devices and register
+ * them to the dvb adapter.
+ *
+ * @dmx_init_func: Pointer to the function to be used
+ *  to initialize demux of the udnerlying HW plugin.
+ *
+ * Return     error code
+ *
+ * Should be called at the HW plugin module initialization.
+ */
+int mpq_dmx_plugin_init(mpq_dmx_init dmx_init_func);
+
+/**
+ * mpq_demux_plugin_exit - terminate demux devices.
+ *
+ * Should be called at the HW plugin module termination.
+ */
+void mpq_dmx_plugin_exit(void);
+
+/**
+ * mpq_dmx_set_source - implmenetation of set_source routine.
+ *
+ * @demux: The demux device to set its source.
+ * @src: The source to be set.
+ *
+ * Return     error code
+ *
+ * Can be used by the underlying plugins to implement kernel
+ * demux API set_source routine.
+ */
+int mpq_dmx_set_source(struct dmx_demux *demux, const dmx_source_t *src);
+
+/**
+ * mpq_dmx_init_video_feed - Initializes video feed
+ * used to pass data to decoder directly.
+ *
+ * @feed: The feed used for the video TS packets
+ *
+ * Return     error code.
+ *
+ * If the underlying plugin wishes to perform SW PES assmebly
+ * for the video data and stream it to the decoder, it should
+ * call this function when video feed is initialized before
+ * using mpq_dmx_process_video_packet.
+ *
+ * The function allocates mpq_video_feed_info and saves in
+ * feed->priv.
+ */
+int mpq_dmx_init_video_feed(struct dvb_demux_feed *feed);
+
+/**
+ * mpq_dmx_terminate_video_feed - Free private data of
+ * video feed allocated in mpq_dmx_init_video_feed
+ *
+ * @feed: The feed used for the video TS packets
+ *
+ * Return     error code.
+ */
+int mpq_dmx_terminate_video_feed(struct dvb_demux_feed *feed);
+
+/**
+ * mpq_dmx_decoder_fullness_init - Initialize waiting
+ * mechanism on decoder's buffer fullness.
+ *
+ * @feed: The decoder's feed
+ *
+ * Return     error code.
+ */
+int mpq_dmx_decoder_fullness_init(
+		struct dvb_demux_feed *feed);
+
+/**
+ * mpq_dmx_decoder_fullness_wait - Checks whether decoder buffer
+ * have free space as required, if not, wait for it.
+ *
+ * @feed: The decoder's feed
+ * @required_space: the required free space to wait for
+ *
+ * Return     error code.
+ */
+int mpq_dmx_decoder_fullness_wait(
+		struct dvb_demux_feed *feed,
+		size_t required_space);
+
+/**
+ * mpq_dmx_decoder_fullness_abort - Aborts waiting
+ * on decoder's buffer fullness if any waiting is done
+ * now. After calling this, to wait again the user must
+ * call mpq_dmx_decoder_fullness_init.
+ *
+ * @feed: The decoder's feed
+ *
+ * Return     error code.
+ */
+int mpq_dmx_decoder_fullness_abort(
+		struct dvb_demux_feed *feed);
+
+/**
+ * mpq_dmx_process_video_packet - Assemble PES data and output it
+ * to the stream-buffer connected to the decoder.
+ *
+ * @feed: The feed used for the video TS packets
+ * @buf: The buffer holding video TS packet.
+ *
+ * Return     error code.
+ *
+ * The function assumes it receives buffer with single TS packet
+ * of the relevant PID.
+ * If the output buffer is full while assembly, the function drops
+ * the packet and does not write them to the output buffer.
+ * Scrambled packets are bypassed.
+ */
+int mpq_dmx_process_video_packet(
+		struct dvb_demux_feed *feed,
+		const u8 *buf);
+
+/**
+ * mpq_dmx_process_pcr_packet - Extract PCR/STC pairs from
+ * a 192 bytes packet.
+ *
+ * @feed: The feed used for the PCR TS packets
+ * @buf: The buffer holding pcr/stc packet.
+ *
+ * Return     error code.
+ *
+ * The function assumes it receives buffer with single TS packet
+ * of the relevant PID, and that it has 4 bytes
+ * suffix as extra timestamp in the following format:
+ *
+ * Byte3: TSIF flags
+ * Byte0-2: TTS, 0..2^24-1 at 105.47 Khz (27*10^6/256).
+ *
+ * The function callbacks dmxdev after extraction of the pcr/stc
+ * pair.
+ */
+int mpq_dmx_process_pcr_packet(
+			struct dvb_demux_feed *feed,
+			const u8 *buf);
+
+/**
+ * mpq_dmx_is_video_feed - Returns whether the PES feed
+ * is video one.
+ *
+ * @feed: The feed to be checked.
+ *
+ * Return     1 if feed is video feed, 0 otherwise.
+ */
+static inline int mpq_dmx_is_video_feed(struct dvb_demux_feed *feed)
+{
+	if (feed->type != DMX_TYPE_TS)
+		return 0;
+
+	if (feed->ts_type & (~TS_DECODER))
+		return 0;
+
+	if ((feed->pes_type == DMX_TS_PES_VIDEO0) ||
+		(feed->pes_type == DMX_TS_PES_VIDEO1) ||
+		(feed->pes_type == DMX_TS_PES_VIDEO2) ||
+		(feed->pes_type == DMX_TS_PES_VIDEO3))
+		return 1;
+
+	return 0;
+}
+
+/**
+ * mpq_dmx_is_pcr_feed - Returns whether the PES feed
+ * is PCR one.
+ *
+ * @feed: The feed to be checked.
+ *
+ * Return     1 if feed is PCR feed, 0 otherwise.
+ */
+static inline int mpq_dmx_is_pcr_feed(struct dvb_demux_feed *feed)
+{
+	if (feed->type != DMX_TYPE_TS)
+		return 0;
+
+	if (feed->ts_type & (~TS_DECODER))
+		return 0;
+
+	if ((feed->pes_type == DMX_TS_PES_PCR0) ||
+		(feed->pes_type == DMX_TS_PES_PCR1) ||
+		(feed->pes_type == DMX_TS_PES_PCR2) ||
+		(feed->pes_type == DMX_TS_PES_PCR3))
+		return 1;
+
+	return 0;
+}
+
+/**
+ * mpq_dmx_init_hw_statistics -
+ * Extend dvb-demux debugfs with HW statistics.
+ *
+ * @mpq_demux: The mpq_demux device to initialize.
+ */
+void mpq_dmx_init_hw_statistics(struct mpq_demux *mpq_demux);
+
+
+/**
+ * mpq_dmx_update_hw_statistics -
+ * Update dvb-demux debugfs with HW notification statistics.
+ *
+ * @mpq_demux: The mpq_demux device to update.
+ */
+void mpq_dmx_update_hw_statistics(struct mpq_demux *mpq_demux);
+
+#endif /* _MPQ_DMX_PLUGIN_COMMON_H */
+
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c
new file mode 100644
index 0000000..bfd6b96
--- /dev/null
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c
@@ -0,0 +1,791 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/tsif_api.h>
+#include <linux/workqueue.h>
+#include <linux/moduleparam.h>
+#include "mpq_dvb_debug.h"
+#include "mpq_dmx_plugin_common.h"
+
+
+/* TSIF HW configuration: */
+#define TSIF_COUNT				2
+
+/* Max number of section filters */
+#define DMX_TSIF_MAX_SECTION_FILTER_NUM	64
+
+/* When TSIF driver notifies demux that new packets are received */
+#define DMX_TSIF_PACKETS_IN_CHUNK_DEF		16
+#define DMX_TSIF_CHUNKS_IN_BUF			8
+#define DMX_TSIF_TIME_LIMIT			10000
+
+/* TSIF_DRIVER_MODE: 3 means manual control from debugfs. use 1 normally. */
+#define DMX_TSIF_DRIVER_MODE_DEF		1
+
+
+/* module parameters for load time configuration: */
+static int threshold = DMX_TSIF_PACKETS_IN_CHUNK_DEF;
+static int mode = DMX_TSIF_DRIVER_MODE_DEF;
+module_param(threshold, int, S_IRUGO);
+module_param(mode, int, S_IRUGO);
+
+/*
+ * Work scheduled each time TSIF notifies dmx
+ * of new TS packet
+ */
+struct tsif_work {
+	struct work_struct work;
+	int tsif_id;
+};
+
+
+/*
+ * TSIF driver information
+ */
+struct tsif_driver_info {
+	/* handler to TSIF driver */
+	void *tsif_handler;
+	/* TSIF driver data buffer pointer */
+	void *data_buffer;
+	/* TSIF driver data buffer size, in packets */
+	int buffer_size;
+	/* TSIF driver read pointer */
+	int ri;
+	/* TSIF driver write pointer */
+	int wi;
+	/* TSIF driver state */
+	enum tsif_state state;
+};
+
+
+/*
+ * The following structure hold singelton information
+ * required for dmx implementation on top of TSIF.
+ */
+static struct
+{
+	/* Information for each TSIF input processing */
+	struct {
+		/* work used to submit to workqueue for processing */
+		struct tsif_work work;
+
+		/* workqueue that processes TS packets from specific TSIF */
+		struct workqueue_struct *workqueue;
+
+		/* TSIF alias */
+		char name[TSIF_NAME_LENGTH];
+
+		/* TSIF driver information */
+		struct tsif_driver_info tsif_driver;
+
+		/* TSIF reference count (counts start/stop operations */
+		int ref_count;
+
+		/* Pointer to the demux connected to this TSIF */
+		struct mpq_demux *mpq_demux;
+
+		/* mutex protecting the data-structure */
+		struct mutex mutex;
+	} tsif[TSIF_COUNT];
+} mpq_dmx_tsif_info;
+
+
+/**
+ * Worker function that processes the TS packets notified by the TSIF driver.
+ *
+ * @worker: the executed work
+ */
+static void mpq_dmx_tsif_work(struct work_struct *worker)
+{
+	struct tsif_work *tsif_work =
+		container_of(worker, struct tsif_work, work);
+	struct mpq_demux *mpq_demux;
+	struct tsif_driver_info *tsif_driver;
+	size_t packets = 0;
+	int tsif = tsif_work->tsif_id;
+
+	mpq_demux = mpq_dmx_tsif_info.tsif[tsif].mpq_demux;
+	tsif_driver = &(mpq_dmx_tsif_info.tsif[tsif].tsif_driver);
+
+	MPQ_DVB_DBG_PRINT(
+		"%s executed, tsif = %d\n",
+		__func__,
+		tsif);
+
+	if (mutex_lock_interruptible(&mpq_dmx_tsif_info.tsif[tsif].mutex))
+		return;
+
+	/* Check if driver handler is still valid */
+	if (tsif_driver->tsif_handler == NULL) {
+		mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
+		MPQ_DVB_ERR_PRINT("%s: tsif_driver->tsif_handler is NULL!\n",
+				__func__);
+		return;
+	}
+
+	tsif_get_state(tsif_driver->tsif_handler, &(tsif_driver->ri),
+				&(tsif_driver->wi), &(tsif_driver->state));
+
+	if ((tsif_driver->wi == tsif_driver->ri) ||
+		(tsif_driver->state == tsif_state_stopped) ||
+		(tsif_driver->state == tsif_state_error)) {
+
+		mpq_demux->hw_notification_size = 0;
+
+		mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
+
+		MPQ_DVB_ERR_PRINT(
+			"%s: invalid TSIF state (%d), wi = (%d), ri = (%d)\n",
+			__func__,
+			tsif_driver->state, tsif_driver->wi, tsif_driver->ri);
+		return;
+	}
+
+	if (tsif_driver->wi > tsif_driver->ri) {
+		packets = (tsif_driver->wi - tsif_driver->ri);
+		mpq_demux->hw_notification_size = packets;
+
+		dvb_dmx_swfilter_format(
+			&mpq_demux->demux,
+			(tsif_driver->data_buffer +
+			(tsif_driver->ri * TSIF_PKT_SIZE)),
+			(packets * TSIF_PKT_SIZE),
+			DMX_TSP_FORMAT_192_TAIL);
+
+		tsif_driver->ri =
+			(tsif_driver->ri + packets) % tsif_driver->buffer_size;
+
+		tsif_reclaim_packets(tsif_driver->tsif_handler,
+					tsif_driver->ri);
+	} else {
+		/*
+		 * wi < ri, means wraparound on cyclic buffer.
+		 * Handle in two stages.
+		 */
+		packets = (tsif_driver->buffer_size - tsif_driver->ri);
+		mpq_demux->hw_notification_size = packets;
+
+		dvb_dmx_swfilter_format(
+			&mpq_demux->demux,
+			(tsif_driver->data_buffer +
+			(tsif_driver->ri * TSIF_PKT_SIZE)),
+			(packets * TSIF_PKT_SIZE),
+			DMX_TSP_FORMAT_192_TAIL);
+
+		/* tsif_driver->ri should be 0 after this */
+		tsif_driver->ri =
+			(tsif_driver->ri + packets) % tsif_driver->buffer_size;
+
+		packets = tsif_driver->wi;
+		if (packets > 0) {
+			mpq_demux->hw_notification_size += packets;
+
+			dvb_dmx_swfilter_format(
+				&mpq_demux->demux,
+				(tsif_driver->data_buffer +
+				(tsif_driver->ri * TSIF_PKT_SIZE)),
+				(packets * TSIF_PKT_SIZE),
+				DMX_TSP_FORMAT_192_TAIL);
+
+			tsif_driver->ri =
+				(tsif_driver->ri + packets) %
+				tsif_driver->buffer_size;
+		}
+
+		tsif_reclaim_packets(tsif_driver->tsif_handler,
+					tsif_driver->ri);
+	}
+
+	mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
+}
+
+
+/**
+ * Callback function from TSIF driver when new data is ready.
+ *
+ * @user: user-data holding TSIF number
+ */
+static void mpq_tsif_callback(void *user)
+{
+	int tsif = (int)user;
+	struct work_struct *work;
+	struct mpq_demux *mpq_demux;
+
+	MPQ_DVB_DBG_PRINT("%s executed, tsif = %d\n", __func__,	tsif);
+
+	/* Save statistics on TSIF notifications */
+	mpq_demux = mpq_dmx_tsif_info.tsif[tsif].mpq_demux;
+	mpq_dmx_update_hw_statistics(mpq_demux);
+
+	work = &mpq_dmx_tsif_info.tsif[tsif].work.work;
+
+	/* Scheudle a new work to demux workqueue */
+	if (!work_pending(work))
+		queue_work(mpq_dmx_tsif_info.tsif[tsif].workqueue, work);
+}
+
+
+/**
+ * Attach to TSIF driver and start TSIF operation.
+ *
+ * @mpq_demux: the mpq_demux we are working on.
+ *
+ * Return	error code.
+ */
+static int mpq_tsif_dmx_start(struct mpq_demux *mpq_demux)
+{
+	int ret = 0;
+	int tsif;
+	struct tsif_driver_info *tsif_driver;
+
+	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+	/* determine the TSIF we are reading from */
+	if (mpq_demux->source == DMX_SOURCE_FRONT0) {
+		tsif = 0;
+	} else if (mpq_demux->source == DMX_SOURCE_FRONT1) {
+		tsif = 1;
+	} else {
+		/* invalid source */
+		MPQ_DVB_ERR_PRINT(
+			"%s: invalid input source (%d)\n",
+			__func__,
+			mpq_demux->source);
+
+		return -EINVAL;
+	}
+
+	if (mutex_lock_interruptible(&mpq_dmx_tsif_info.tsif[tsif].mutex))
+		return -ERESTARTSYS;
+
+	if (mpq_dmx_tsif_info.tsif[tsif].ref_count == 0) {
+		tsif_driver = &(mpq_dmx_tsif_info.tsif[tsif].tsif_driver);
+
+		/* Attach to TSIF driver */
+
+		tsif_driver->tsif_handler =
+			tsif_attach(tsif, mpq_tsif_callback, (void *)tsif);
+		if (IS_ERR_OR_NULL(tsif_driver->tsif_handler)) {
+			tsif_driver->tsif_handler = NULL;
+			mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
+			MPQ_DVB_DBG_PRINT("%s: tsif_attach(%d) failed\n",
+					__func__, tsif);
+			return -ENODEV;
+		}
+
+		/* Set TSIF driver mode */
+		ret = tsif_set_mode(tsif_driver->tsif_handler,
+					mode);
+		if (ret < 0) {
+			MPQ_DVB_ERR_PRINT("%s: tsif_set_mode (%d) failed\n",
+				__func__, mode);
+		}
+
+		/* Set TSIF buffer configuration */
+		ret = tsif_set_buf_config(tsif_driver->tsif_handler,
+						threshold,
+						DMX_TSIF_CHUNKS_IN_BUF);
+		if (ret < 0) {
+			MPQ_DVB_ERR_PRINT(
+				"%s: tsif_set_buf_config (%d, %d) failed\n",
+				__func__, threshold,
+				DMX_TSIF_CHUNKS_IN_BUF);
+			MPQ_DVB_ERR_PRINT("Using default TSIF driver values\n");
+		}
+
+
+		/* Set TSIF driver time limit */
+		/* TODO: needed?? */
+/*		ret = tsif_set_time_limit(tsif_driver->tsif_handler,
+						DMX_TSIF_TIME_LIMIT);
+		if (ret < 0) {
+			MPQ_DVB_ERR_PRINT(
+				"%s: tsif_set_time_limit (%d) failed\n",
+				__func__, DMX_TSIF_TIME_LIMIT);
+		}
+*/
+
+		/* Start TSIF driver */
+		ret = tsif_start(tsif_driver->tsif_handler);
+		if (ret < 0) {
+			mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
+			MPQ_DVB_ERR_PRINT("%s: tsif_start failed\n", __func__);
+			return ret;
+		}
+
+		/*
+		 * Get data buffer information from TSIF driver
+		 * (must be called after tsif_start)
+		 */
+		tsif_get_info(tsif_driver->tsif_handler,
+				&(tsif_driver->data_buffer),
+				&(tsif_driver->buffer_size));
+
+		/* save pointer to the mpq_demux we are working on */
+		mpq_dmx_tsif_info.tsif[tsif].mpq_demux = mpq_demux;
+	}
+	mpq_dmx_tsif_info.tsif[tsif].ref_count++;
+
+	mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
+
+	return ret;
+}
+
+
+/**
+ * Stop TSIF operation and detach from TSIF driver.
+ *
+ * @mpq_demux: the mpq_demux we are working on.
+ *
+ * Return	error code.
+ */
+static int mpq_tsif_dmx_stop(struct mpq_demux *mpq_demux)
+{
+	int tsif;
+	struct tsif_driver_info *tsif_driver;
+
+	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+	/* determine the TSIF we are reading from */
+	if (mpq_demux->source == DMX_SOURCE_FRONT0) {
+		tsif = 0;
+	} else if (mpq_demux->source == DMX_SOURCE_FRONT1) {
+		tsif = 1;
+	} else {
+		/* invalid source */
+		MPQ_DVB_ERR_PRINT(
+			"%s: invalid input source (%d)\n",
+			__func__,
+			mpq_demux->source);
+
+		return -EINVAL;
+	}
+
+	if (mutex_lock_interruptible(&mpq_dmx_tsif_info.tsif[tsif].mutex))
+		return -ERESTARTSYS;
+
+	mpq_dmx_tsif_info.tsif[tsif].ref_count--;
+
+	if (mpq_dmx_tsif_info.tsif[tsif].ref_count == 0) {
+		tsif_driver = &(mpq_dmx_tsif_info.tsif[tsif].tsif_driver);
+		tsif_stop(tsif_driver->tsif_handler);
+		tsif_detach(tsif_driver->tsif_handler);
+		/*
+		 * temporarily release mutex and flush the work queue
+		 * before setting tsif_handler to NULL
+		 */
+		mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
+		flush_workqueue(mpq_dmx_tsif_info.tsif[tsif].workqueue);
+		/* re-acquire mutex */
+		if (mutex_lock_interruptible(
+				&mpq_dmx_tsif_info.tsif[tsif].mutex))
+			return -ERESTARTSYS;
+
+		tsif_driver->tsif_handler = NULL;
+		tsif_driver->data_buffer = NULL;
+		tsif_driver->buffer_size = 0;
+		mpq_dmx_tsif_info.tsif[tsif].mpq_demux = NULL;
+	}
+
+	mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
+
+	return 0;
+}
+
+
+/**
+ * Start filtering according to feed parameter.
+ *
+ * @feed: the feed we are working on.
+ *
+ * Return	error code.
+ */
+static int mpq_tsif_dmx_start_filtering(struct dvb_demux_feed *feed)
+{
+	int ret = 0;
+	struct mpq_demux *mpq_demux =
+		(struct mpq_demux *)feed->demux->priv;
+
+	MPQ_DVB_DBG_PRINT(
+		"%s(%d) executed\n",
+		__func__,
+		feed->pid);
+
+	if (mpq_demux == NULL) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: invalid mpq_demux handle\n",
+			__func__);
+
+		return -EINVAL;
+	}
+
+	if (mpq_demux->source < DMX_SOURCE_DVR0) {
+		/* Source from TSIF, need to configure TSIF hardware */
+		ret = mpq_tsif_dmx_start(mpq_demux);
+
+		if (ret < 0) {
+			MPQ_DVB_DBG_PRINT(
+				"%s: mpq_tsif_dmx_start failed(%d)\n",
+				__func__,
+				ret);
+			return ret;
+		}
+	}
+
+	/* Always feed sections/PES starting from a new one and
+	 * do not partial transfer data from older one
+	 */
+	feed->pusi_seen = 0;
+
+	/*
+	 * For video PES, data is tunneled to the decoder,
+	 * initialize tunneling and pes parsing.
+	 */
+	if (mpq_dmx_is_video_feed(feed)) {
+		ret = mpq_dmx_init_video_feed(feed);
+
+		if (ret < 0) {
+			MPQ_DVB_DBG_PRINT(
+				"%s: mpq_dmx_init_video_feed failed(%d)\n",
+				__func__,
+				ret);
+
+			if (mpq_demux->source < DMX_SOURCE_DVR0)
+				mpq_tsif_dmx_stop(mpq_demux);
+
+			return ret;
+		}
+	}
+
+	return ret;
+}
+
+
+/**
+ * Stop filtering according to feed parameter.
+ *
+ * @feed: the feed we are working on.
+ *
+ * Return	error code.
+ */
+static int mpq_tsif_dmx_stop_filtering(struct dvb_demux_feed *feed)
+{
+	int ret = 0;
+	struct mpq_demux *mpq_demux =
+		(struct mpq_demux *)feed->demux->priv;
+
+	MPQ_DVB_DBG_PRINT(
+		"%s(%d) executed\n",
+		__func__,
+		feed->pid);
+
+	if (mpq_demux == NULL) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: invalid mpq_demux handle\n",
+			__func__);
+
+		return -EINVAL;
+	}
+
+	/*
+	 * For video PES, data is tunneled to the decoder,
+	 * terminate tunnel and pes parsing.
+	 */
+	if (mpq_dmx_is_video_feed(feed))
+		mpq_dmx_terminate_video_feed(feed);
+
+	if (mpq_demux->source < DMX_SOURCE_DVR0) {
+		/* Source from TSIF, need to configure TSIF hardware */
+		ret = mpq_tsif_dmx_stop(mpq_demux);
+	}
+
+	return ret;
+}
+
+
+/**
+ * TSIF demux plugin write-to-decoder function.
+ *
+ * @feed: The feed we are working on.
+ * @buf: The data buffer to process.
+ * @len: The data buffer length.
+ *
+ * Return	error code
+ */
+static int mpq_tsif_dmx_write_to_decoder(
+					struct dvb_demux_feed *feed,
+					const u8 *buf,
+					size_t len)
+{
+	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+	/*
+	 * It is assumed that this function is called once for each
+	 * TS packet of the relevant feed.
+	 */
+	if (len > TSIF_PKT_SIZE)
+		MPQ_DVB_DBG_PRINT(
+				"%s: warnning - len larger than one packet\n",
+				__func__);
+
+	if (mpq_dmx_is_video_feed(feed))
+		return mpq_dmx_process_video_packet(feed, buf);
+
+	if (mpq_dmx_is_pcr_feed(feed))
+		return mpq_dmx_process_pcr_packet(feed, buf);
+
+	return 0;
+}
+
+/**
+ * Returns demux capabilities of TSIF plugin
+ *
+ * @demux: demux device
+ * @caps: Returned capbabilities
+ *
+ * Return     error code
+ */
+static int mpq_tsif_dmx_get_caps(struct dmx_demux *demux,
+				struct dmx_caps *caps)
+{
+	struct dvb_demux *dvb_demux = (struct dvb_demux *)demux->priv;
+
+	if ((dvb_demux == NULL) || (caps == NULL)) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: invalid parameters\n",
+			__func__);
+
+		return -EINVAL;
+	}
+
+	caps->caps = DMX_CAP_PULL_MODE | DMX_CAP_VIDEO_DECODER_DATA;
+	caps->num_decoders = MPQ_ADAPTER_MAX_NUM_OF_INTERFACES;
+	caps->num_demux_devices = CONFIG_DVB_MPQ_NUM_DMX_DEVICES;
+	caps->num_pid_filters = dvb_demux->feednum;
+	caps->num_section_filters = dvb_demux->filternum;
+	caps->num_section_filters_per_pid = dvb_demux->filternum;
+	caps->section_filter_length = DMX_FILTER_SIZE;
+	caps->num_demod_inputs = TSIF_COUNT;
+	caps->num_memory_inputs = CONFIG_DVB_MPQ_NUM_DMX_DEVICES;
+	caps->max_bitrate = 144;
+	caps->demod_input_max_bitrate = 72;
+	caps->memory_input_max_bitrate = 72;
+
+	return 0;
+}
+
+/**
+ * Initialize a single demux device.
+ *
+ * @mpq_adapter: MPQ DVB adapter
+ * @mpq_demux: The demux device to initialize
+ *
+ * Return	error code
+ */
+static int mpq_tsif_dmx_init(
+		struct dvb_adapter *mpq_adapter,
+		struct mpq_demux *mpq_demux)
+{
+	int result;
+
+	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+	/* Set the kernel-demux object capabilities */
+	mpq_demux->demux.dmx.capabilities =
+		DMX_TS_FILTERING		|
+		DMX_PES_FILTERING		|
+		DMX_SECTION_FILTERING		|
+		DMX_MEMORY_BASED_FILTERING	|
+		DMX_CRC_CHECKING		|
+		DMX_TS_DESCRAMBLING;
+
+	/* Set dvb-demux "virtual" function pointers */
+	mpq_demux->demux.priv = (void *)mpq_demux;
+	mpq_demux->demux.filternum = DMX_TSIF_MAX_SECTION_FILTER_NUM;
+	mpq_demux->demux.feednum = MPQ_MAX_DMX_FILES;
+	mpq_demux->demux.start_feed = mpq_tsif_dmx_start_filtering;
+	mpq_demux->demux.stop_feed = mpq_tsif_dmx_stop_filtering;
+	mpq_demux->demux.write_to_decoder = mpq_tsif_dmx_write_to_decoder;
+
+	mpq_demux->demux.decoder_fullness_init =
+		mpq_dmx_decoder_fullness_init;
+
+	mpq_demux->demux.decoder_fullness_wait =
+		mpq_dmx_decoder_fullness_wait;
+
+	mpq_demux->demux.decoder_fullness_abort =
+		mpq_dmx_decoder_fullness_abort;
+
+	/* Initialize dvb_demux object */
+	result = dvb_dmx_init(&mpq_demux->demux);
+	if (result < 0) {
+		MPQ_DVB_ERR_PRINT("%s: dvb_dmx_init failed\n", __func__);
+		goto init_failed;
+	}
+
+	/* Now initailize the dmx-dev object */
+	mpq_demux->dmxdev.filternum = MPQ_MAX_DMX_FILES;
+	mpq_demux->dmxdev.demux = &mpq_demux->demux.dmx;
+	mpq_demux->dmxdev.capabilities =
+		DMXDEV_CAP_DUPLEX |
+		DMXDEV_CAP_PULL_MODE |
+		DMXDEV_CAP_PCR_EXTRACTION |
+		DMXDEV_CAP_INDEXING;
+
+	mpq_demux->dmxdev.demux->set_source = mpq_dmx_set_source;
+	mpq_demux->dmxdev.demux->get_caps = mpq_tsif_dmx_get_caps;
+
+	result = dvb_dmxdev_init(&mpq_demux->dmxdev, mpq_adapter);
+	if (result < 0) {
+		MPQ_DVB_ERR_PRINT("%s: dvb_dmxdev_init failed (errno=%d)\n",
+						  __func__,
+						  result);
+		goto init_failed_dmx_release;
+	}
+
+	/* Extend dvb-demux debugfs with TSIF statistics. */
+	mpq_dmx_init_hw_statistics(mpq_demux);
+
+	return 0;
+
+init_failed_dmx_release:
+	dvb_dmx_release(&mpq_demux->demux);
+init_failed:
+	return result;
+}
+
+
+/**
+ * Module initialization function.
+ *
+ * Return	error code
+ */
+static int __init mpq_dmx_tsif_plugin_init(void)
+{
+	int i;
+	int ret;
+
+	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+	/* check module parameters validity */
+	if (threshold < 1) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: invalid threshold parameter, using %d instead\n",
+			__func__, DMX_TSIF_PACKETS_IN_CHUNK_DEF);
+		threshold = DMX_TSIF_PACKETS_IN_CHUNK_DEF;
+	}
+	if ((mode < 1) || (mode > 3)) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: invalid mode parameter, using %d instead\n",
+			__func__, DMX_TSIF_DRIVER_MODE_DEF);
+		mode = DMX_TSIF_DRIVER_MODE_DEF;
+	}
+
+	for (i = 0; i < TSIF_COUNT; i++) {
+		mpq_dmx_tsif_info.tsif[i].work.tsif_id = i;
+
+		INIT_WORK(&mpq_dmx_tsif_info.tsif[i].work.work,
+				  mpq_dmx_tsif_work);
+
+		snprintf(mpq_dmx_tsif_info.tsif[i].name,
+				TSIF_NAME_LENGTH,
+				"tsif_%d",
+				i);
+
+		mpq_dmx_tsif_info.tsif[i].workqueue =
+			create_singlethread_workqueue(
+				mpq_dmx_tsif_info.tsif[i].name);
+
+		if (mpq_dmx_tsif_info.tsif[i].workqueue == NULL) {
+			int j;
+
+			for (j = 0; j < i; j++) {
+				destroy_workqueue(
+					mpq_dmx_tsif_info.tsif[j].workqueue);
+				mutex_destroy(&mpq_dmx_tsif_info.tsif[j].mutex);
+			}
+
+			MPQ_DVB_ERR_PRINT(
+				"%s: create_singlethread_workqueue failed\n",
+				__func__);
+
+			return -ENOMEM;
+		}
+
+		mutex_init(&mpq_dmx_tsif_info.tsif[i].mutex);
+
+		mpq_dmx_tsif_info.tsif[i].tsif_driver.tsif_handler = NULL;
+		mpq_dmx_tsif_info.tsif[i].ref_count = 0;
+	}
+
+	ret = mpq_dmx_plugin_init(mpq_tsif_dmx_init);
+
+	if (ret < 0) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: mpq_dmx_plugin_init failed (errno=%d)\n",
+			__func__,
+			ret);
+
+		for (i = 0; i < TSIF_COUNT; i++) {
+			destroy_workqueue(mpq_dmx_tsif_info.tsif[i].workqueue);
+			mutex_destroy(&mpq_dmx_tsif_info.tsif[i].mutex);
+		}
+	}
+
+	return ret;
+}
+
+
+/**
+ * Module exit function.
+ */
+static void __exit mpq_dmx_tsif_plugin_exit(void)
+{
+	int i;
+	struct tsif_driver_info *tsif_driver;
+
+	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+	for (i = 0; i < TSIF_COUNT; i++) {
+		mutex_lock(&mpq_dmx_tsif_info.tsif[i].mutex);
+
+		tsif_driver = &(mpq_dmx_tsif_info.tsif[i].tsif_driver);
+		if (mpq_dmx_tsif_info.tsif[i].ref_count > 0) {
+			mpq_dmx_tsif_info.tsif[i].ref_count = 0;
+			if (tsif_driver->tsif_handler)
+				tsif_stop(tsif_driver->tsif_handler);
+		}
+		/* Detach from TSIF driver to avoid further notifications. */
+		if (tsif_driver->tsif_handler)
+			tsif_detach(tsif_driver->tsif_handler);
+
+		/* release mutex to allow work queue to finish scheduled work */
+		mutex_unlock(&mpq_dmx_tsif_info.tsif[i].mutex);
+		/* flush the work queue and destroy it */
+		flush_workqueue(mpq_dmx_tsif_info.tsif[i].workqueue);
+		destroy_workqueue(mpq_dmx_tsif_info.tsif[i].workqueue);
+
+		mutex_destroy(&mpq_dmx_tsif_info.tsif[i].mutex);
+	}
+
+	mpq_dmx_plugin_exit();
+}
+
+
+module_init(mpq_dmx_tsif_plugin_init);
+module_exit(mpq_dmx_tsif_plugin_exit);
+
+MODULE_DESCRIPTION("Qualcomm demux TSIF HW Plugin");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c
new file mode 100644
index 0000000..a552fdf
--- /dev/null
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c
@@ -0,0 +1,894 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <mach/msm_tspp.h>
+#include "mpq_dvb_debug.h"
+#include "mpq_dmx_plugin_common.h"
+
+
+#define TSIF_COUNT					2
+
+#define TSPP_MAX_PID_FILTER_NUM		16
+
+/* Max number of section filters */
+#define TSPP_MAX_SECTION_FILTER_NUM		64
+
+/* For each TSIF we allocate two pipes, one for PES and one for sections */
+#define TSPP_PES_CHANNEL				0
+#define TSPP_SECTION_CHANNEL			1
+
+/* the channel_id set to TSPP driver based on TSIF number and channel type */
+#define TSPP_CHANNEL_ID(tsif, ch)		((tsif << 1) + ch)
+#define TSPP_IS_PES_CHANNEL(ch_id)		((ch_id & 0x1) == 0)
+#define TSPP_GET_TSIF_NUM(ch_id)		(ch_id >> 1)
+
+/* mask that set to care for all bits in pid filter */
+#define TSPP_PID_MASK					0x1FFF
+
+/* dvb-demux defines pid 0x2000 as full capture pid */
+#define TSPP_PASS_THROUGH_PID			0x2000
+
+/* TODO - NEED TO SET THESE PROPERLY
+ * once TSPP driver is ready, reduce TSPP_BUFFER_SIZE
+ * to single packet and set TSPP_BUFFER_COUNT accordingly
+ */
+
+#define TSPP_RAW_TTS_SIZE				192
+
+/* Size of single descriptor.
+ * Assuming 20MBit/sec stream, with 200 packets
+ * per descriptor there would be about 68 descriptors.
+ * Meanning about 68 interrupts per second.
+ */
+#define TSPP_BUFFER_SIZE			(TSPP_RAW_TTS_SIZE * 200)
+
+/* Number of descriptors, total size: TSPP_BUFFER_SIZE*TSPP_BUFFER_COUNT */
+#define TSPP_BUFFER_COUNT				(16)
+
+/* When TSPP notifies demux that new packets are received */
+#define TSPP_NOTIFICATION_SIZE			(TSPP_RAW_TTS_SIZE * 100)
+
+/* Channel timeout in msec */
+#define TSPP_CHANNEL_TIMEOUT			16
+
+
+/*
+ * Work scheduled each time TSPP notifies dmx
+ * of new TS packet in some channel
+ */
+struct tspp_work {
+	struct work_struct work;
+	int channel_id;
+};
+
+/* The following structure hold singelton information
+ * required for dmx implementation on top of TSPP.
+ */
+static struct
+{
+	/* Information for each TSIF input processing */
+	struct {
+		/*
+		 * TSPP pipe holding all TS packets with PES data.
+		 * The following is reference count for number of feeds
+		 * allocated on that pipe.
+		 */
+		int pes_channel_ref;
+
+		/* work used to submit to workqueue to process pes channel */
+		struct tspp_work pes_work;
+
+		/*
+		 * TSPP pipe holding all TS packets with section data.
+		 * The following is reference count for number of feeds
+		 * allocated on that pipe.
+		 */
+		int section_channel_ref;
+
+		/* work used to submit to workqueue to process pes channel */
+		struct tspp_work section_work;
+
+		/*
+		 * Holds PIDs of allocated TSPP filters along with
+		 * how many feeds are opened on same PID.
+		 */
+		struct {
+			int pid;
+			int ref_count;
+		} filters[TSPP_MAX_PID_FILTER_NUM];
+
+		/* workqueue that processes TS packets from specific TSIF */
+		struct workqueue_struct *workqueue;
+
+		/* TSIF alias */
+		char name[TSIF_NAME_LENGTH];
+
+		/* Pointer to the demux connected to this TSIF */
+		struct mpq_demux *mpq_demux;
+
+		/* mutex protecting the data-structure */
+		struct mutex mutex;
+	} tsif[TSIF_COUNT];
+} mpq_dmx_tspp_info;
+
+
+/**
+ * Returns a free filter slot that can be used.
+ *
+ * @tsif: The TSIF to allocate filter from
+ * @channel_id: The channel allocating filter to
+ *
+ * Return  filter index or -1 if no filters available
+ *
+ * To give priority to PES data, for pes filters
+ * the table is scanned from high to low priority,
+ * and sections from low to high priority. This way TSPP
+ * would get a match on PES data filters faster as they
+ * are scanned first.
+ */
+static int mpq_tspp_get_free_filter_slot(int tsif, int channel_id)
+{
+	int i;
+
+	if (TSPP_IS_PES_CHANNEL(channel_id)) {
+		for (i = 0; i < TSPP_MAX_PID_FILTER_NUM; i++)
+			if (mpq_dmx_tspp_info.tsif[tsif].filters[i].pid == -1)
+				return i;
+	} else {
+		for (i = TSPP_MAX_PID_FILTER_NUM-1; i >= 0; i--)
+			if (mpq_dmx_tspp_info.tsif[tsif].filters[i].pid == -1)
+				return i;
+	}
+
+	return -ENOMEM;
+}
+
+/**
+ * Returns filter index of specific pid.
+ *
+ * @tsif: The TSIF to which the pid is allocated
+ * @pid: The pid to search for
+ *
+ * Return  filter index or -1 if no filter available
+ */
+static int mpq_tspp_get_filter_slot(int tsif, int pid)
+{
+	int i;
+
+	for (i = 0; i < TSPP_MAX_PID_FILTER_NUM; i++)
+		if (mpq_dmx_tspp_info.tsif[tsif].filters[i].pid == pid)
+			return i;
+
+	return -EINVAL;
+}
+
+/**
+ * Worker function that processes the TS packets notified by TSPP.
+ *
+ * @worker: the executed work
+ */
+static void mpq_dmx_tspp_work(struct work_struct *worker)
+{
+	struct tspp_work *tspp_work =
+		container_of(worker, struct tspp_work, work);
+	struct mpq_demux *mpq_demux;
+	int channel_id = tspp_work->channel_id;
+	int tsif = TSPP_GET_TSIF_NUM(channel_id);
+	const struct tspp_data_descriptor *tspp_data_desc;
+	int ref_count;
+
+	mpq_demux = mpq_dmx_tspp_info.tsif[tsif].mpq_demux;
+
+	/* Lock against the TSPP filters data-structure */
+	if (mutex_lock_interruptible(&mpq_dmx_tspp_info.tsif[tsif].mutex))
+		return;
+
+	/* Make sure channel is still active */
+	if (TSPP_IS_PES_CHANNEL(channel_id))
+		ref_count = mpq_dmx_tspp_info.tsif[tsif].pes_channel_ref;
+	else
+		ref_count = mpq_dmx_tspp_info.tsif[tsif].section_channel_ref;
+
+	if (ref_count == 0) {
+		mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
+		return;
+	}
+
+	mpq_demux->hw_notification_size = 0;
+
+	/* Go through all filled descriptors and perform demuxing on them */
+	while ((tspp_data_desc = tspp_get_buffer(0, channel_id)) != NULL) {
+		mpq_demux->hw_notification_size +=
+			(tspp_data_desc->size / TSPP_RAW_TTS_SIZE);
+
+		dvb_dmx_swfilter_format(
+				&mpq_demux->demux,
+				tspp_data_desc->virt_base,
+				tspp_data_desc->size,
+				DMX_TSP_FORMAT_192_TAIL);
+
+		/* Notify TSPP that the buffer is no longer needed */
+		tspp_release_buffer(0, channel_id, tspp_data_desc->id);
+	}
+
+	mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
+}
+
+/**
+ * Callback function from TSPP when new data is ready.
+ *
+ * @channel_id: Channel with new TS packets
+ * @user: user-data holding TSIF number
+ */
+static void mpq_tspp_callback(u32 channel_id, void *user)
+{
+	int tsif = (int)user;
+	struct work_struct *work;
+	struct mpq_demux *mpq_demux;
+
+	/* Save statistics on TSPP notifications */
+	mpq_demux = mpq_dmx_tspp_info.tsif[tsif].mpq_demux;
+	mpq_dmx_update_hw_statistics(mpq_demux);
+
+	if (TSPP_IS_PES_CHANNEL(channel_id))
+		work = &mpq_dmx_tspp_info.tsif[tsif].pes_work.work;
+	else
+		work = &mpq_dmx_tspp_info.tsif[tsif].section_work.work;
+
+	/* Scheudle a new work to demux workqueue */
+	if (!work_pending(work))
+		queue_work(mpq_dmx_tspp_info.tsif[tsif].workqueue, work);
+}
+
+/**
+ * Configure TSPP channel to filter the PID of new feed.
+ *
+ * @feed: The feed to configure the channel with
+ *
+ * Return  error status
+ *
+ * The function checks if the new PID can be added to an already
+ * allocated channel, if not, a new channel is allocated and configured.
+ */
+static int mpq_tspp_dmx_add_channel(struct dvb_demux_feed *feed)
+{
+	struct mpq_demux *mpq_demux = (struct mpq_demux *)feed->demux->priv;
+	enum tspp_source tspp_source;
+	struct tspp_filter tspp_filter;
+	int tsif;
+	int ret;
+	int channel_id;
+	int *channel_ref_count;
+
+	/* determine the TSIF we are reading from */
+	if (mpq_demux->source == DMX_SOURCE_FRONT0) {
+		tsif = 0;
+		tspp_source = TSPP_SOURCE_TSIF0;
+	} else if (mpq_demux->source == DMX_SOURCE_FRONT1) {
+		tsif = 1;
+		tspp_source = TSPP_SOURCE_TSIF1;
+	} else {
+		/* invalid source */
+		MPQ_DVB_ERR_PRINT(
+			"%s: invalid input source (%d)\n",
+			__func__,
+			mpq_demux->source);
+
+		return -EINVAL;
+	}
+
+	if (mutex_lock_interruptible(&mpq_dmx_tspp_info.tsif[tsif].mutex))
+		return -ERESTARTSYS;
+
+	/*
+	 * It is possible that this PID was already requested before.
+	 * Can happen if we play and record same PES or PCR
+	 * piggypacked on video packet.
+	 */
+	ret = mpq_tspp_get_filter_slot(tsif, feed->pid);
+	if (ret >= 0) {
+		/* PID already configured */
+		mpq_dmx_tspp_info.tsif[tsif].filters[ret].ref_count++;
+		mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
+		return 0;
+	}
+
+	/* determine to which pipe the feed should be routed: section or pes */
+	if ((feed->type == DMX_TYPE_PES) || (feed->type == DMX_TYPE_TS)) {
+		channel_id = TSPP_CHANNEL_ID(tsif, TSPP_PES_CHANNEL);
+		channel_ref_count =
+			&mpq_dmx_tspp_info.tsif[tsif].pes_channel_ref;
+	} else {
+		channel_id = TSPP_CHANNEL_ID(tsif, TSPP_SECTION_CHANNEL);
+		channel_ref_count =
+			&mpq_dmx_tspp_info.tsif[tsif].section_channel_ref;
+	}
+
+	/* check if required TSPP pipe is already allocated or not */
+	if (*channel_ref_count == 0) {
+		ret = tspp_open_channel(0, channel_id);
+
+		if (ret < 0) {
+			MPQ_DVB_ERR_PRINT(
+				"%s: tspp_open_channel(%d) failed (%d)\n",
+				__func__,
+				channel_id,
+				ret);
+
+			goto add_channel_failed;
+		}
+
+		/* set TSPP source */
+		ret = tspp_open_stream(0, channel_id, tspp_source);
+		if (ret < 0) {
+			MPQ_DVB_ERR_PRINT(
+				"%s: tspp_select_source(%d,%d) failed (%d)\n",
+				__func__,
+				channel_id,
+				tspp_source,
+				ret);
+
+			goto add_channel_close_ch;
+		}
+
+		/* register notification on TS packets */
+		tspp_register_notification(0,
+					   channel_id,
+					   mpq_tspp_callback,
+					   (void *)tsif,
+					   TSPP_CHANNEL_TIMEOUT);
+
+		/* TODO: register allocater and provide allocation function
+		 * that allocate from continous memory so that we can have
+		 * big notification size, smallest descriptor, and still provide
+		 * TZ with single big buffer based on notification size.
+		 */
+
+		/* set buffer/descriptor size and count */
+		ret = tspp_allocate_buffers(0,
+					    channel_id,
+					    TSPP_BUFFER_COUNT,
+					    TSPP_BUFFER_SIZE,
+					    TSPP_NOTIFICATION_SIZE,
+					    NULL,
+					    NULL);
+		if (ret < 0) {
+			MPQ_DVB_ERR_PRINT(
+				"%s: tspp_allocate_buffers(%d) failed (%d)\n",
+				__func__,
+				channel_id,
+				ret);
+
+			goto add_channel_unregister_notif;
+		}
+
+		mpq_dmx_tspp_info.tsif[tsif].mpq_demux = mpq_demux;
+	}
+
+	/* add new PID to the existing pipe */
+	ret = mpq_tspp_get_free_filter_slot(tsif, channel_id);
+	if (ret < 0) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: mpq_allocate_filter_slot(%d, %d) failed\n",
+			__func__,
+			tsif,
+			channel_id);
+
+		goto add_channel_unregister_notif;
+	}
+
+	mpq_dmx_tspp_info.tsif[tsif].filters[ret].pid = feed->pid;
+	mpq_dmx_tspp_info.tsif[tsif].filters[ret].ref_count++;
+
+	tspp_filter.priority = ret;
+	if (feed->pid == TSPP_PASS_THROUGH_PID) {
+		/* pass all pids */
+		tspp_filter.pid = 0;
+		tspp_filter.mask = 0;
+	} else {
+		tspp_filter.pid = feed->pid;
+		tspp_filter.mask = TSPP_PID_MASK;
+	}
+
+	/*
+	 * Include TTS in RAW packets, if you change this to
+	 * TSPP_MODE_RAW_NO_SUFFIX you must also change TSPP_RAW_TTS_SIZE
+	 * accordingly.
+	 */
+	tspp_filter.mode = TSPP_MODE_RAW;
+	tspp_filter.source = tspp_source;
+	tspp_filter.decrypt = 0;
+	ret = tspp_add_filter(0, channel_id, &tspp_filter);
+	if (ret < 0) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: tspp_add_filter(%d) failed (%d)\n",
+			__func__,
+			channel_id,
+			ret);
+
+		goto add_channel_free_filter_slot;
+	}
+
+	(*channel_ref_count)++;
+
+	mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
+	return 0;
+
+add_channel_free_filter_slot:
+	mpq_dmx_tspp_info.tsif[tsif].filters[tspp_filter.priority].pid = -1;
+	mpq_dmx_tspp_info.tsif[tsif].filters[tspp_filter.priority].ref_count--;
+add_channel_unregister_notif:
+	tspp_unregister_notification(0, channel_id);
+add_channel_close_ch:
+	tspp_close_channel(0, channel_id);
+add_channel_failed:
+	mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
+	return ret;
+}
+
+/**
+ * Removes filter from TSPP.
+ *
+ * @feed: The feed to remove
+ *
+ * Return  error status
+ *
+ * The function checks if this is the only PID allocated within
+ * the channel, if so, the channel is closed as well.
+ */
+static int mpq_tspp_dmx_remove_channel(struct dvb_demux_feed *feed)
+{
+	int tsif;
+	int ret;
+	int channel_id;
+	int *channel_ref_count;
+	struct tspp_filter tspp_filter;
+	struct mpq_demux *mpq_demux = (struct mpq_demux *)feed->demux->priv;
+
+	/* determine the TSIF we are reading from */
+	if (mpq_demux->source == DMX_SOURCE_FRONT0) {
+		tsif = 0;
+	} else if (mpq_demux->source == DMX_SOURCE_FRONT1) {
+		tsif = 1;
+	} else {
+		/* invalid source */
+		MPQ_DVB_ERR_PRINT(
+			"%s: invalid input source (%d)\n",
+			__func__,
+			mpq_demux->source);
+
+		return -EINVAL;
+	}
+
+	if (mutex_lock_interruptible(&mpq_dmx_tspp_info.tsif[tsif].mutex))
+		return -ERESTARTSYS;
+
+	/* determine to which pipe the feed should be routed: section or pes */
+	if ((feed->type == DMX_TYPE_PES) || (feed->type == DMX_TYPE_TS)) {
+		channel_id = TSPP_CHANNEL_ID(tsif, TSPP_PES_CHANNEL);
+		channel_ref_count =
+			&mpq_dmx_tspp_info.tsif[tsif].pes_channel_ref;
+	} else {
+		channel_id = TSPP_CHANNEL_ID(tsif, TSPP_SECTION_CHANNEL);
+		channel_ref_count =
+			&mpq_dmx_tspp_info.tsif[tsif].section_channel_ref;
+	}
+
+	/* check if required TSPP pipe is already allocated or not */
+	if (*channel_ref_count == 0) {
+		/* invalid feed provided as the channel is not allocated */
+		MPQ_DVB_ERR_PRINT(
+			"%s: invalid feed (%d)\n",
+			__func__,
+			channel_id);
+
+		ret = -EINVAL;
+		goto remove_channel_failed;
+	}
+
+	tspp_filter.priority = mpq_tspp_get_filter_slot(tsif, feed->pid);
+
+	if (tspp_filter.priority < 0) {
+		/* invalid feed provided as it has no filter allocated */
+		MPQ_DVB_ERR_PRINT(
+			"%s: mpq_tspp_get_filter_slot failed (%d,%d)\n",
+			__func__,
+			feed->pid,
+			tsif);
+
+		ret = -EINVAL;
+		goto remove_channel_failed;
+	}
+
+	mpq_dmx_tspp_info.tsif[tsif].filters[tspp_filter.priority].ref_count--;
+
+	if (mpq_dmx_tspp_info.tsif[tsif].
+		filters[tspp_filter.priority].ref_count) {
+		/*
+		 * there are still references to this pid, do not
+		 * remove the filter yet
+		 */
+		mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
+		return 0;
+	}
+
+	ret = tspp_remove_filter(0, channel_id, &tspp_filter);
+	if (ret < 0) {
+		/* invalid feed provided as it has no filter allocated */
+		MPQ_DVB_ERR_PRINT(
+			"%s: tspp_remove_filter failed (%d,%d)\n",
+			__func__,
+			channel_id,
+			tspp_filter.priority);
+
+		goto remove_channel_failed_restore_count;
+	}
+
+	mpq_dmx_tspp_info.tsif[tsif].filters[tspp_filter.priority].pid = -1;
+	(*channel_ref_count)--;
+
+	if (*channel_ref_count == 0) {
+		/* channel is not used any more, release it */
+		tspp_unregister_notification(0, channel_id);
+		tspp_close_channel(0, channel_id);
+	}
+
+	mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
+	return 0;
+
+remove_channel_failed_restore_count:
+	mpq_dmx_tspp_info.tsif[tsif].filters[tspp_filter.priority].ref_count++;
+
+remove_channel_failed:
+	mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
+	return ret;
+}
+
+static int mpq_tspp_dmx_start_filtering(struct dvb_demux_feed *feed)
+{
+	int ret;
+	struct mpq_demux *mpq_demux =
+		(struct mpq_demux *)feed->demux->priv;
+
+	MPQ_DVB_DBG_PRINT(
+		"%s(%d) executed\n",
+		__func__,
+		feed->pid);
+
+	if (mpq_demux == NULL) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: invalid mpq_demux handle\n",
+			__func__);
+
+		return -EINVAL;
+	}
+
+	if (mpq_demux->source < DMX_SOURCE_DVR0) {
+		/* source from TSPP, need to configure tspp pipe */
+		ret = mpq_tspp_dmx_add_channel(feed);
+
+		if (ret < 0) {
+			MPQ_DVB_DBG_PRINT(
+				"%s: mpq_tspp_dmx_add_channel failed(%d)\n",
+				__func__,
+				ret);
+			return ret;
+		}
+	}
+
+	/*
+	 * Always feed sections/PES starting from a new one and
+	 * do not partial transfer data from older one
+	 */
+	feed->pusi_seen = 0;
+
+	/*
+	 * For video PES, data is tunneled to the decoder,
+	 * initialize tunneling and pes parsing.
+	 */
+	if (mpq_dmx_is_video_feed(feed)) {
+		ret = mpq_dmx_init_video_feed(feed);
+
+		if (ret < 0) {
+			MPQ_DVB_DBG_PRINT(
+				"%s: mpq_dmx_init_video_feed failed(%d)\n",
+				__func__,
+				ret);
+
+			if (mpq_demux->source < DMX_SOURCE_DVR0)
+				mpq_tspp_dmx_remove_channel(feed);
+
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int mpq_tspp_dmx_stop_filtering(struct dvb_demux_feed *feed)
+{
+	int ret = 0;
+	struct mpq_demux *mpq_demux = (struct mpq_demux *)feed->demux->priv;
+
+	MPQ_DVB_DBG_PRINT(
+		"%s(%d) executed\n",
+		__func__,
+		feed->pid);
+
+	/*
+	 * For video PES, data is tunneled to the decoder,
+	 * terminate tunnel and pes parsing.
+	 */
+	if (mpq_dmx_is_video_feed(feed))
+		mpq_dmx_terminate_video_feed(feed);
+
+	if (mpq_demux->source < DMX_SOURCE_DVR0) {
+		/* source from TSPP, need to configure tspp pipe */
+		ret = mpq_tspp_dmx_remove_channel(feed);
+	}
+
+	return ret;
+}
+
+static int mpq_tspp_dmx_write_to_decoder(
+					struct dvb_demux_feed *feed,
+					const u8 *buf,
+					size_t len)
+{
+
+	/*
+	 * It is assumed that this function is called once for each
+	 * TS packet of the relevant feed.
+	 */
+	if (len > TSPP_RAW_TTS_SIZE)
+		MPQ_DVB_DBG_PRINT(
+				"%s: warnning - len larger than one packet\n",
+				__func__);
+
+	if (mpq_dmx_is_video_feed(feed))
+		return mpq_dmx_process_video_packet(feed, buf);
+
+	if (mpq_dmx_is_pcr_feed(feed))
+		return mpq_dmx_process_pcr_packet(feed, buf);
+
+	return 0;
+}
+
+/**
+ * Returns demux capabilities of TSPPv1 plugin
+ *
+ * @demux: demux device
+ * @caps: Returned capbabilities
+ *
+ * Return     error code
+ */
+static int mpq_tspp_dmx_get_caps(struct dmx_demux *demux,
+				struct dmx_caps *caps)
+{
+	struct dvb_demux *dvb_demux = (struct dvb_demux *)demux->priv;
+
+	if ((dvb_demux == NULL) || (caps == NULL)) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: invalid parameters\n",
+			__func__);
+
+		return -EINVAL;
+	}
+
+	caps->caps = DMX_CAP_PULL_MODE | DMX_CAP_VIDEO_DECODER_DATA;
+	caps->num_decoders = MPQ_ADAPTER_MAX_NUM_OF_INTERFACES;
+	caps->num_demux_devices = CONFIG_DVB_MPQ_NUM_DMX_DEVICES;
+	caps->num_pid_filters = TSPP_MAX_PID_FILTER_NUM;
+	caps->num_section_filters = dvb_demux->filternum;
+	caps->num_section_filters_per_pid = dvb_demux->filternum;
+	caps->section_filter_length = DMX_FILTER_SIZE;
+	caps->num_demod_inputs = TSIF_COUNT;
+	caps->num_memory_inputs = CONFIG_DVB_MPQ_NUM_DMX_DEVICES;
+	caps->max_bitrate = 144;
+	caps->demod_input_max_bitrate = 72;
+	caps->memory_input_max_bitrate = 72;
+
+	return 0;
+}
+
+static int mpq_tspp_dmx_init(
+			struct dvb_adapter *mpq_adapter,
+			struct mpq_demux *mpq_demux)
+{
+	int result;
+
+	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+	/* Set the kernel-demux object capabilities */
+	mpq_demux->demux.dmx.capabilities =
+		DMX_TS_FILTERING			|
+		DMX_PES_FILTERING			|
+		DMX_SECTION_FILTERING		|
+		DMX_MEMORY_BASED_FILTERING	|
+		DMX_CRC_CHECKING			|
+		DMX_TS_DESCRAMBLING;
+
+	/* Set dvb-demux "virtual" function pointers */
+	mpq_demux->demux.priv = (void *)mpq_demux;
+	mpq_demux->demux.filternum = TSPP_MAX_SECTION_FILTER_NUM;
+	mpq_demux->demux.feednum = MPQ_MAX_DMX_FILES;
+	mpq_demux->demux.start_feed = mpq_tspp_dmx_start_filtering;
+	mpq_demux->demux.stop_feed = mpq_tspp_dmx_stop_filtering;
+	mpq_demux->demux.write_to_decoder = mpq_tspp_dmx_write_to_decoder;
+
+	mpq_demux->demux.decoder_fullness_init =
+		mpq_dmx_decoder_fullness_init;
+
+	mpq_demux->demux.decoder_fullness_wait =
+		mpq_dmx_decoder_fullness_wait;
+
+	mpq_demux->demux.decoder_fullness_abort =
+		mpq_dmx_decoder_fullness_abort;
+
+	/* Initialize dvb_demux object */
+	result = dvb_dmx_init(&mpq_demux->demux);
+	if (result < 0) {
+		MPQ_DVB_ERR_PRINT("%s: dvb_dmx_init failed\n", __func__);
+		goto init_failed;
+	}
+
+	/* Now initailize the dmx-dev object */
+	mpq_demux->dmxdev.filternum = MPQ_MAX_DMX_FILES;
+	mpq_demux->dmxdev.demux = &mpq_demux->demux.dmx;
+	mpq_demux->dmxdev.capabilities =
+		DMXDEV_CAP_DUPLEX |
+		DMXDEV_CAP_PULL_MODE |
+		DMXDEV_CAP_PCR_EXTRACTION |
+		DMXDEV_CAP_INDEXING;
+
+	mpq_demux->dmxdev.demux->set_source = mpq_dmx_set_source;
+	mpq_demux->dmxdev.demux->get_caps = mpq_tspp_dmx_get_caps;
+
+	result = dvb_dmxdev_init(&mpq_demux->dmxdev, mpq_adapter);
+	if (result < 0) {
+		MPQ_DVB_ERR_PRINT("%s: dvb_dmxdev_init failed (errno=%d)\n",
+						  __func__,
+						  result);
+		goto init_failed_dmx_release;
+	}
+
+	/* Extend dvb-demux debugfs with TSPP statistics. */
+	mpq_dmx_init_hw_statistics(mpq_demux);
+
+	return 0;
+
+init_failed_dmx_release:
+	dvb_dmx_release(&mpq_demux->demux);
+init_failed:
+	return result;
+}
+
+static int __init mpq_dmx_tspp_plugin_init(void)
+{
+	int i;
+	int j;
+	int ret;
+
+	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+	for (i = 0; i < TSIF_COUNT; i++) {
+		mpq_dmx_tspp_info.tsif[i].pes_channel_ref = 0;
+
+		mpq_dmx_tspp_info.tsif[i].pes_work.channel_id =
+			TSPP_CHANNEL_ID(i, TSPP_PES_CHANNEL);
+
+		INIT_WORK(&mpq_dmx_tspp_info.tsif[i].pes_work.work,
+				  mpq_dmx_tspp_work);
+
+		mpq_dmx_tspp_info.tsif[i].section_channel_ref = 0;
+
+		mpq_dmx_tspp_info.tsif[i].section_work.channel_id =
+			TSPP_CHANNEL_ID(i, TSPP_SECTION_CHANNEL);
+
+		INIT_WORK(&mpq_dmx_tspp_info.tsif[i].section_work.work,
+				  mpq_dmx_tspp_work);
+
+		for (j = 0; j < TSPP_MAX_PID_FILTER_NUM; j++) {
+			mpq_dmx_tspp_info.tsif[i].filters[j].pid = -1;
+			mpq_dmx_tspp_info.tsif[i].filters[j].ref_count = 0;
+		}
+
+		snprintf(mpq_dmx_tspp_info.tsif[i].name,
+				TSIF_NAME_LENGTH,
+				"tsif_%d",
+				i);
+
+		mpq_dmx_tspp_info.tsif[i].workqueue =
+			create_singlethread_workqueue(
+				mpq_dmx_tspp_info.tsif[i].name);
+
+		if (mpq_dmx_tspp_info.tsif[i].workqueue == NULL) {
+
+			for (j = 0; j < i; j++) {
+				destroy_workqueue(
+					mpq_dmx_tspp_info.tsif[j].workqueue);
+
+				mutex_destroy(&mpq_dmx_tspp_info.tsif[j].mutex);
+			}
+
+			MPQ_DVB_ERR_PRINT(
+				"%s: create_singlethread_workqueue failed\n",
+				__func__);
+
+			return -ENOMEM;
+		}
+
+		mutex_init(&mpq_dmx_tspp_info.tsif[i].mutex);
+	}
+
+	ret = mpq_dmx_plugin_init(mpq_tspp_dmx_init);
+
+	if (ret < 0) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: mpq_dmx_plugin_init failed (errno=%d)\n",
+			__func__,
+			ret);
+
+		for (i = 0; i < TSIF_COUNT; i++) {
+			destroy_workqueue(mpq_dmx_tspp_info.tsif[i].workqueue);
+			mutex_destroy(&mpq_dmx_tspp_info.tsif[i].mutex);
+		}
+	}
+
+	return ret;
+}
+
+static void __exit mpq_dmx_tspp_plugin_exit(void)
+{
+	int i;
+
+	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+	for (i = 0; i < TSIF_COUNT; i++) {
+		mutex_lock(&mpq_dmx_tspp_info.tsif[i].mutex);
+
+		if (mpq_dmx_tspp_info.tsif[i].pes_channel_ref) {
+			tspp_unregister_notification(0, TSPP_PES_CHANNEL);
+			tspp_close_channel(0,
+				TSPP_CHANNEL_ID(i, TSPP_PES_CHANNEL));
+		}
+
+		if (mpq_dmx_tspp_info.tsif[i].section_channel_ref) {
+			tspp_unregister_notification(0, TSPP_SECTION_CHANNEL);
+			tspp_close_channel(0,
+				TSPP_CHANNEL_ID(i, TSPP_SECTION_CHANNEL));
+		}
+
+		/* TODO: if we allocate buffer
+		 * to TSPP ourself, need to free those as well
+		 */
+
+		mutex_unlock(&mpq_dmx_tspp_info.tsif[i].mutex);
+		flush_workqueue(mpq_dmx_tspp_info.tsif[i].workqueue);
+		destroy_workqueue(mpq_dmx_tspp_info.tsif[i].workqueue);
+		mutex_destroy(&mpq_dmx_tspp_info.tsif[i].mutex);
+	}
+
+	mpq_dmx_plugin_exit();
+}
+
+
+module_init(mpq_dmx_tspp_plugin_init);
+module_exit(mpq_dmx_tspp_plugin_exit);
+
+MODULE_DESCRIPTION("Qualcomm demux TSPP version 1 HW Plugin");
+MODULE_LICENSE("GPL v2");
+
+
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v2.c b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v2.c
new file mode 100644
index 0000000..6c484a0
--- /dev/null
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v2.c
@@ -0,0 +1,183 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include "mpq_dvb_debug.h"
+#include "mpq_dmx_plugin_common.h"
+
+
+#define TSIF_COUNT				2
+
+#define BAM_INPUT_COUNT			4
+
+/* Max number of PID filters */
+#define TSPP_MAX_PID_FILTER_NUM		128
+
+/* Max number of section filters */
+#define TSPP_MAX_SECTION_FILTER_NUM		64
+
+
+static int mpq_tspp_dmx_start_filtering(struct dvb_demux_feed *feed)
+{
+	MPQ_DVB_DBG_PRINT(
+		"%s(%d) executed\n",
+		__func__,
+		feed->pid);
+
+	/* Always feed sections/PES starting from a new one and
+	 * do not partial transfer data from older one
+	 */
+	feed->pusi_seen = 0;
+	return 0;
+}
+
+static int mpq_tspp_dmx_stop_filtering(struct dvb_demux_feed *feed)
+{
+	MPQ_DVB_DBG_PRINT(
+		"%s(%d) executed\n",
+		__func__,
+		feed->pid);
+
+	return 0;
+}
+
+/**
+ * Returns demux capabilities of TSPPv2 plugin
+ *
+ * @demux: demux device
+ * @caps: Returned capbabilities
+ *
+ * Return     error code
+ */
+static int mpq_tspp_dmx_get_caps(struct dmx_demux *demux,
+				struct dmx_caps *caps)
+{
+	struct dvb_demux *dvb_demux = (struct dvb_demux *)demux->priv;
+
+	if ((dvb_demux == NULL) || (caps == NULL)) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: invalid parameters\n",
+			__func__);
+
+		return -EINVAL;
+	}
+
+	caps->caps = DMX_CAP_PULL_MODE | DMX_CAP_VIDEO_INDEXING |
+		DMX_CAP_VIDEO_DECODER_DATA;
+	caps->num_decoders = MPQ_ADAPTER_MAX_NUM_OF_INTERFACES;
+	caps->num_demux_devices = CONFIG_DVB_MPQ_NUM_DMX_DEVICES;
+	caps->num_pid_filters = TSPP_MAX_PID_FILTER_NUM;
+	caps->num_section_filters = dvb_demux->filternum;
+	caps->num_section_filters_per_pid = dvb_demux->filternum;
+	caps->section_filter_length = DMX_FILTER_SIZE;
+	caps->num_demod_inputs = TSIF_COUNT;
+	caps->num_memory_inputs = BAM_INPUT_COUNT;
+	caps->max_bitrate = 320;
+	caps->demod_input_max_bitrate = 96;
+	caps->memory_input_max_bitrate = 80;
+
+	return 0;
+}
+
+/**
+ * Initialize a single demux device.
+ *
+ * @mpq_adapter: MPQ DVB adapter
+ * @mpq_demux: The demux device to initialize
+ *
+ * Return     error code
+ */
+static int mpq_tspp_dmx_init(
+				struct dvb_adapter *mpq_adapter,
+				struct mpq_demux *mpq_demux)
+{
+	int result;
+
+	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+	/* Set the kernel-demux object capabilities */
+	mpq_demux->demux.dmx.capabilities =
+		DMX_TS_FILTERING			|
+		DMX_PES_FILTERING			|
+		DMX_SECTION_FILTERING		|
+		DMX_MEMORY_BASED_FILTERING	|
+		DMX_CRC_CHECKING			|
+		DMX_TS_DESCRAMBLING;
+
+	/* Set dvb-demux "virtual" function pointers */
+	mpq_demux->demux.priv = (void *)mpq_demux;
+	mpq_demux->demux.filternum = TSPP_MAX_SECTION_FILTER_NUM;
+	mpq_demux->demux.feednum = MPQ_MAX_DMX_FILES;
+	mpq_demux->demux.start_feed = mpq_tspp_dmx_start_filtering;
+	mpq_demux->demux.stop_feed = mpq_tspp_dmx_stop_filtering;
+	mpq_demux->demux.write_to_decoder = NULL;
+	mpq_demux->demux.decoder_fullness_init = NULL;
+	mpq_demux->demux.decoder_fullness_wait = NULL;
+	mpq_demux->demux.decoder_fullness_abort = NULL;
+
+	/* Initialize dvb_demux object */
+	result = dvb_dmx_init(&mpq_demux->demux);
+	if (result < 0) {
+		MPQ_ERR_PRINT("%s: dvb_dmx_init failed\n", __func__);
+		goto init_failed;
+	}
+
+	/* Now initailize the dmx-dev object */
+	mpq_demux->dmxdev.filternum = MPQ_MAX_DMX_FILES;
+	mpq_demux->dmxdev.demux = &mpq_demux->demux.dmx;
+	mpq_demux->dmxdev.capabilities =
+		DMXDEV_CAP_DUPLEX |
+		DMXDEV_CAP_PULL_MODE |
+		DMXDEV_CAP_PCR_EXTRACTION |
+		DMXDEV_CAP_INDEXING;
+
+	mpq_demux->dmxdev.demux->set_source = mpq_dmx_set_source;
+	mpq_demux->dmxdev.demux->get_caps = mpq_tspp_dmx_get_caps;
+
+	result = dvb_dmxdev_init(&mpq_demux->dmxdev, mpq_adapter);
+	if (result < 0) {
+		MPQ_DVB_ERR_PRINT("%s: dvb_dmxdev_init failed (errno=%d)\n",
+						  __func__,
+						  result);
+
+		goto init_failed_dmx_release;
+	}
+
+	return 0;
+
+init_failed_dmx_release:
+	dvb_dmx_release(&mpq_demux->demux);
+init_failed:
+	return result;
+}
+
+static int __init mpq_dmx_tspp_plugin_init(void)
+{
+	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+	return mpq_dmx_plugin_init(mpq_tspp_dmx_init);
+}
+
+static void __exit mpq_dmx_tspp_plugin_exit(void)
+{
+	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+	mpq_dmx_plugin_exit();
+}
+
+
+module_init(mpq_dmx_tspp_plugin_init);
+module_exit(mpq_dmx_tspp_plugin_exit);
+
+MODULE_DESCRIPTION("Qualcomm demux TSPP version2 HW Plugin");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/media/dvb/mpq/include/mpq_adapter.h b/drivers/media/dvb/mpq/include/mpq_adapter.h
new file mode 100644
index 0000000..c9b2441
--- /dev/null
+++ b/drivers/media/dvb/mpq/include/mpq_adapter.h
@@ -0,0 +1,193 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MPQ_ADAPTER_H
+#define _MPQ_ADAPTER_H
+
+#include "dvbdev.h"
+#include "mpq_stream_buffer.h"
+
+
+
+/** IDs of interfaces holding stream-buffers */
+enum mpq_adapter_stream_if {
+	/** Interface holding stream-buffer for video0 stream */
+	MPQ_ADAPTER_VIDEO0_STREAM_IF = 0,
+
+	/** Interface holding stream-buffer for video1 stream */
+	MPQ_ADAPTER_VIDEO1_STREAM_IF = 1,
+
+	/** Interface holding stream-buffer for video1 stream */
+	MPQ_ADAPTER_VIDEO2_STREAM_IF = 2,
+
+	/** Interface holding stream-buffer for video1 stream */
+	MPQ_ADAPTER_VIDEO3_STREAM_IF = 3,
+
+	/** Maximum number of interfaces holding stream-buffers */
+	MPQ_ADAPTER_MAX_NUM_OF_INTERFACES,
+};
+
+
+enum dmx_framing_pattern_type {
+	/* MPEG-2 */
+	DMX_FRM_MPEG2_SEQUENCE_HEADER,
+	DMX_FRM_MPEG2_GOP_HEADER,
+	DMX_FRM_MPEG2_I_PIC,
+	DMX_FRM_MPEG2_P_PIC,
+	DMX_FRM_MPEG2_B_PIC,
+	/* H.264 */
+	DMX_FRM_H264_SPS,
+	DMX_FRM_H264_PPS,
+	/* H.264 First Coded slice of an IDR Picture */
+	DMX_FRM_H264_IDR_PIC,
+	/* H.264 First Coded slice of a non-IDR Picture */
+	DMX_FRM_H264_NON_IDR_PIC,
+	/* VC-1 Sequence Header*/
+	DMX_FRM_VC1_SEQUENCE_HEADER,
+	/* VC-1 Entry Point Header (Advanced Profile only) */
+	DMX_FRM_VC1_ENTRY_POINT_HEADER,
+	/* VC-1 Frame Start Code */
+	DMX_FRM_VC1_FRAME_START_CODE,
+	/* Unknown or invalid framing information */
+	DMX_FRM_UNKNOWN
+};
+
+enum dmx_packet_type {
+	DMX_PADDING_PACKET,
+	DMX_PES_PACKET,
+	DMX_FRAMING_INFO_PACKET,
+	DMX_EOS_PACKET
+};
+
+struct dmx_pts_dts_info {
+	/** Indication whether PTS exist */
+	int pts_exist;
+
+	/** Indication whether DTS exist */
+	int dts_exist;
+
+	/** PTS value associated with the PES data if any */
+	u64 pts;
+
+	/** DTS value associated with the PES data if any */
+	u64 dts;
+};
+
+struct dmx_framing_packet_info {
+	/** framing pattern type */
+	enum dmx_framing_pattern_type pattern_type;
+	/** PTS/DTS information */
+	struct dmx_pts_dts_info pts_dts_info;
+};
+
+struct dmx_pes_packet_info {
+	/** PTS/DTS information */
+	struct dmx_pts_dts_info pts_dts_info;
+};
+
+/** The meta-data used for video interface */
+struct mpq_adapter_video_meta_data {
+	/** meta-data packet type */
+	enum dmx_packet_type packet_type;
+
+	/** packet-type specific information */
+	union {
+		struct dmx_framing_packet_info framing;
+		struct dmx_pes_packet_info pes;
+	} info;
+} __packed;
+
+
+/** Callback function to notify on registrations of specific interfaces */
+typedef void (*mpq_adapter_stream_if_callback)(
+				enum mpq_adapter_stream_if interface_id,
+				void *user_param);
+
+
+/**
+ * mpq_adapter_get - Returns pointer to Qualcomm DVB adapter
+ *
+ * Return     dvb adapter or NULL if not exist.
+ */
+struct dvb_adapter *mpq_adapter_get(void);
+
+
+/**
+ * mpq_adapter_register_stream_if - Register a stream interface.
+ *
+ * @interface_id: The interface id
+ * @stream_buffer: The buffer used for the interface
+ *
+ * Return     error status
+ *
+ * Stream interface used to connect between two units in tunneling
+ * mode using mpq_streambuffer implementation.
+ * The producer of the interface should register the new interface,
+ * consumer may get the interface using mpq_adapter_get_stream_if.
+ *
+ * Note that the function holds a pointer to this interface,
+ * stream_buffer pointer assumed to be valid as long as interface
+ * is active.
+ */
+int mpq_adapter_register_stream_if(
+		enum mpq_adapter_stream_if interface_id,
+		struct mpq_streambuffer *stream_buffer);
+
+
+/**
+ * mpq_adapter_unregister_stream_if - Un-register a stream interface.
+ *
+ * @interface_id: The interface id
+ *
+ * Return     error status
+ */
+int mpq_adapter_unregister_stream_if(
+		enum mpq_adapter_stream_if interface_id);
+
+
+/**
+ * mpq_adapter_get_stream_if - Get buffer used for a stream interface.
+ *
+ * @interface_id: The interface id
+ * @stream_buffer: The returned stream buffer
+ *
+ * Return     error status
+ */
+int mpq_adapter_get_stream_if(
+		enum mpq_adapter_stream_if interface_id,
+		struct mpq_streambuffer **stream_buffer);
+
+
+/**
+ * mpq_adapter_notify_stream_if - Register notification
+ * to be triggered when a stream interface is registered.
+ *
+ * @interface_id: The interface id
+ * @callback: The callback to be triggered when the interface is registered
+ * @user_param: A parameter that is passed back to the callback function
+ *				when triggered.
+ *
+ * Return     error status
+ *
+ * Producer may use this to register notification when desired
+ * interface registered in the system and query its information
+ * afterwards using mpq_adapter_get_stream_if.
+ * To remove the callback, this function should be called with NULL
+ * value in callback parameter.
+ */
+int mpq_adapter_notify_stream_if(
+		enum mpq_adapter_stream_if interface_id,
+		mpq_adapter_stream_if_callback callback,
+		void *user_param);
+
+#endif /* _MPQ_ADAPTER_H */
+
diff --git a/drivers/media/dvb/mpq/include/mpq_dvb_debug.h b/drivers/media/dvb/mpq/include/mpq_dvb_debug.h
new file mode 100644
index 0000000..4890b85
--- /dev/null
+++ b/drivers/media/dvb/mpq/include/mpq_dvb_debug.h
@@ -0,0 +1,38 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MPQ_DVB_DEBUG_H
+#define _MPQ_DVB_DEBUG_H
+
+/* Enable this line if you want to output debug printouts */
+#define MPG_DVB_DEBUG_ENABLE
+
+#undef MPQ_DVB_DBG_PRINT		/* undef it, just in case */
+
+#ifdef MPG_DVB_DEBUG_ENABLE
+#define MPQ_DVB_DBG_PRINT(fmt, args...) pr_debug(fmt, ## args)
+#define MPQ_DVB_ERR_PRINT(fmt, args...) pr_err(fmt, ## args)
+#else  /* MPG_DVB_DEBUG_ENABLE */
+#define MPQ_DVB_DBG_PRINT(fmt, args...)
+#define MPQ_DVB_ERR_PRINT(fmt, args...)
+#endif /* MPG_DVB_DEBUG_ENABLE */
+
+
+/*
+ * The following can be used to disable specific printout
+ * by adding a letter to the end of MPQ_DVB_DBG_PRINT
+ */
+#undef MPQ_DVB_DBG_PRINTT
+#define MPQ_DVB_DBG_PRINTT(fmt, args...)
+
+#endif /* _MPQ_DVB_DEBUG_H */
+
diff --git a/drivers/media/dvb/mpq/include/mpq_stream_buffer.h b/drivers/media/dvb/mpq/include/mpq_stream_buffer.h
new file mode 100644
index 0000000..4ea4222
--- /dev/null
+++ b/drivers/media/dvb/mpq/include/mpq_stream_buffer.h
@@ -0,0 +1,269 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MPQ_STREAM_BUFFER_H
+#define _MPQ_STREAM_BUFFER_H
+
+#include "dvb_ringbuffer.h"
+
+
+/**
+ * DOC: MPQ Stream Buffer
+ *
+ * A stream buffer implmenetation used to transfer data between two units
+ * such as demux and decoders. The implementation relies on dvb_ringbuffer
+ * implementation. Refer to dvb_ringbuffer.h for details.
+ *
+ * The implementation uses two dvb_ringbuffers, one to pass the
+ * raw-data (PES payload for example) and the other to pass
+ * meta-data (information from PES header for example).
+ *
+ * The meta-data uses dvb_ringbuffer packet interface. Each meta-data
+ * packet hold the address and size of raw-data described by the
+ * meta-data packet, in addition to user's own parameters if any required.
+ *
+ * Contrary to dvb_ringbuffer implementation, this API makes sure there's
+ * enough data to read/write when making read/write operations.
+ * Users interested to flush/reset specific buffer, check for bytes
+ * ready or space available for write should use the respective services
+ * in dvb_ringbuffer (dvb_ringbuffer_avail, dvb_ringbuffer_free,
+ * dvb_ringbuffer_reset, dvb_ringbuffer_flush,
+ * dvb_ringbuffer_flush_spinlock_wakeup).
+ *
+ * Concurrency protection is handled in the same manner as in
+ * dvb_ringbuffer implementation.
+ *
+ * Typical call flow from producer:
+ *
+ * - Start writting the raw-data of new packet, the following call is
+ *   repeated until end of data of the specific packet
+ *
+ *     mpq_streambuffer_data_write(...)
+ *
+ * - Now write a new packet describing the new available raw-data
+ *     mpq_streambuffer_pkt_write(...)
+ *
+ * Typical call flow from consumer:
+ *
+ * - Poll for next available packet:
+ *      mpq_streambuffer_pkt_next(&streambuff,-1)
+ *
+ *   In different approach, consumer can wait on event for new data and then
+ *   call mpq_streambuffer_pkt_next, waiting for data can be done as follows:
+ *
+ *      wait_event_interruptible(
+ *			streambuff->packet_data->queue,
+ *			!dvb_ringbuffer_empty(&streambuff->packet_data) ||
+ *			(streambuff->packet_data.error != 0);
+ *
+ * - Get the new packet information:
+ *      mpq_streambuffer_pkt_read(..)
+ *
+ * - Read the raw-data of the new packet. Here you can use two methods:
+ *
+ *   1. Read the data to a user supplied buffer:
+ *         mpq_streambuffer_data_read()
+ *
+ *      In this case memory copy is done, read pointer is updated in the raw
+ *      data buffer, the amount of raw-data is provided part of the
+ *      packet's information. User should then call mpq_streambuffer_pkt_dispose
+ *      with dispose_data set to 0 as the raw-data was already disposed.
+ *
+ *   2. Access the data directly using the raw-data address. The address
+ *      of the raw data is provided part of the packet's information. User
+ *      then should call mpq_streambuffer_pkt_dispose with dispose_data set
+ *      to 1 to dispose the packet along with it's raw-data.
+ */
+
+/**
+ * struct mpq_streambuffer - mpq stream buffer representation
+ *
+ * @raw_data: The buffer used to hold the raw-data
+ * @packet_data: The buffer user to hold the meta-data
+ */
+struct mpq_streambuffer {
+	struct dvb_ringbuffer raw_data;
+	struct dvb_ringbuffer packet_data;
+};
+
+/**
+ * struct mpq_streambuffer_packet_header - packet header saved in packet buffer
+ * @user_data_len: length of private user (meta) data
+ * @raw_data_addr: raw-data address in the raw-buffer described by the packet
+ * @raw_data_len: size of raw-data in the raw-data buffer (can be 0)
+ *
+ * The packet structure that is saved in each packet-buffer:
+ * user_data_len
+ * raw_data_addr
+ * raw_data_len
+ * private user-data bytes
+ */
+struct mpq_streambuffer_packet_header {
+	u32 user_data_len;
+	u32	raw_data_addr;
+	u32	raw_data_len;
+} __packed;
+
+/**
+ * mpq_streambuffer_init - Initialize a new stream buffer
+ *
+ * @sbuff: The buffer to initialize
+ * @data_buff: The buffer holding raw-data
+ * @data_buff_len: Size of raw-data buffer
+ * @packet_buff: The buffer holding meta-data
+ * @packet_buff_size: Size of meta-data buffer
+ */
+void mpq_streambuffer_init(
+		struct mpq_streambuffer *sbuff,
+		void *data_buff, size_t data_buff_len,
+		void *packet_buff, size_t packet_buff_size);
+
+/**
+ * mpq_streambuffer_packet_next - Returns index of next avaialble packet.
+ *
+ * @sbuff: The stream buffer
+ * @idx: Previous packet index or -1 to return index of the the first
+ *       available packet.
+ * @pktlen: The length of the ready packet
+ *
+ * Return index to the packet-buffer, -1 if buffer is empty
+ *
+ * After getting the index, the user of this function can either
+ * access the packet buffer directly using the returned index
+ * or ask to read the data back from the buffer using mpq_ringbuffer_pkt_read
+ */
+ssize_t mpq_streambuffer_pkt_next(
+		struct mpq_streambuffer *sbuff,
+		ssize_t idx, size_t *pktlen);
+
+/**
+ * mpq_streambuffer_pkt_read - Reads out the packet from the provided index.
+ *
+ * @sbuff: The stream buffer
+ * @idx: The index of the packet to be read
+ * @packet: The read packet's header
+ * @user_data: The read private user data
+ *
+ * Return  The actual number of bytes read, -EINVAL if the packet is
+ * already disposed or the packet-data is invalid.
+ *
+ * The packet is not disposed after this function is called, to dispose it
+ * along with the raw-data it points to use mpq_streambuffer_pkt_dispose.
+ * If there are no private user-data, the user-data pointer can be NULL.
+ * The caller of this function must make sure that the private user-data
+ * buffer has enough space for the private user-data length
+ */
+ssize_t mpq_streambuffer_pkt_read(
+		struct mpq_streambuffer *sbuff,
+		size_t idx,
+		struct mpq_streambuffer_packet_header *packet,
+		u8 *user_data);
+
+/**
+ * mpq_streambuffer_pkt_dispose - Disposes a packet from the packet buffer
+ *
+ * @sbuff: The stream buffer
+ * @idx: The index of the packet to be disposed
+ * @dispose_data: Indicates whether to update the read pointer inside the
+ * raw-data buffer for the respective data pointed by the packet.
+ *
+ * Return  error status, -EINVAL if the packet-data is invalid
+ *
+ * The function updates the read pointer inside the raw-data buffer
+ * for the respective data pointed by the packet if dispose_data is set.
+ */
+int mpq_streambuffer_pkt_dispose(
+		struct mpq_streambuffer *sbuff,
+		size_t idx,
+		int dispose_data);
+
+/**
+ * mpq_streambuffer_pkt_write - Write a new packet to the packet buffer.
+ *
+ * @sbuff: The stream buffer
+ * @packet: The packet header to write
+ * @user_data: The private user-data to be written
+ *
+ * Return  error status, -ENOSPC if there's no space to write the packet
+ */
+int mpq_streambuffer_pkt_write(
+		struct mpq_streambuffer *sbuff,
+		struct mpq_streambuffer_packet_header *packet,
+		u8 *user_data);
+
+/**
+ * mpq_streambuffer_data_write - Write data to raw-data buffer
+ *
+ * @sbuff: The stream buffer
+ * @buf: The buffer holding the data to be written
+ * @len: The length of the data buffer
+ *
+ * Return  The actual number of bytes written or -ENOSPC if
+ *			no space to write the data
+ */
+ssize_t mpq_streambuffer_data_write(
+		struct mpq_streambuffer *sbuff,
+		const u8 *buf, size_t len);
+
+/**
+ * mpq_streambuffer_data_write_deposit - Advances the raw-buffer write pointer.
+ * Assumes the raw-data was written by the user directly
+ *
+ * @sbuff: The stream buffer
+ * @len: The length of the raw-data that was already written
+ *
+ * Return  error status
+ */
+int mpq_streambuffer_data_write_deposit(
+		struct mpq_streambuffer *sbuff,
+		size_t len);
+
+/**
+ * mpq_streambuffer_data_read - Reads out raw-data to the provided buffer.
+ *
+ * @sbuff: The stream buffer
+ * @buf: The buffer to read the raw-data data to
+ * @len: The length of the buffer that will hold the raw-data
+ *
+ * Return  The actual number of bytes read
+ *
+ * This fucntion copies the data from the ring-buffer to the
+ * provided buf parameter. The user can save the extra copy by accessing
+ * the data pointer directly and reading from it, then update the
+ * read pointer by the amount of data that was read using
+ * mpq_streambuffer_data_read_dispose
+ */
+size_t mpq_streambuffer_data_read(
+		struct mpq_streambuffer *sbuff,
+		u8 *buf, size_t len);
+
+/**
+ * mpq_streambuffer_data_read_dispose - Advances the raw-buffer read pointer.
+ * Assumes the raw-data was read by the user directly.
+ *
+ * @sbuff: The stream buffer
+ * @len: The length of the raw-data to be disposed
+ *
+ * Return  error status, -EINVAL if buffer there's no enough data to
+ *			be disposed
+ *
+ * The user can instead dipose a packet along with the data in the
+ * raw-data buffer using mpq_streambuffer_pkt_dispose.
+ */
+int mpq_streambuffer_data_read_dispose(
+		struct mpq_streambuffer *sbuff,
+		size_t len);
+
+
+
+#endif /* _MPQ_STREAM_BUFFER_H */
+
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
index 8db2d7f..8786fe6 100644
--- a/drivers/media/radio/Kconfig
+++ b/drivers/media/radio/Kconfig
@@ -432,4 +432,38 @@
 
 endif # V4L_RADIO_ISA_DRIVERS
 
+config RADIO_TAVARUA
+	tristate "Qualcomm Tavaraua I2C FM support"
+	depends on I2C && VIDEO_V4L2 && MARIMBA_CORE
+	default n
+	---help---
+	  Say Y here if you want to use the Qualcomm FM chip (Tavarua).
+	  This FM chip uses I2C interface.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called radio-tavarua.
+
+config RADIO_IRIS
+        tristate "Qualcomm IRIS FM support"
+        depends on VIDEO_V4L2
+        default n
+        ---help---
+          Say Y here if you want to use the Qualcomm FM chip (IRIS).
+          This FM chip uses SMD interface
+
+          To compile this driver as a module, choose M here: the
+          module will be called radio-iris.
+
+
+config RADIO_IRIS_TRANSPORT
+        tristate "Qualcomm IRIS Transport"
+        depends on RADIO_IRIS
+        default n
+        ---help---
+          Say Y here if you want to use the Qualcomm FM chip (IRIS).
+          with SMD as transport.
+
+          To compile this driver as a module, choose M here: the
+          module will be called radio-iris-transport.
+
 endif # RADIO_ADAPTERS
diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile
index ca8c7d1..b80eba7 100644
--- a/drivers/media/radio/Makefile
+++ b/drivers/media/radio/Makefile
@@ -28,5 +28,8 @@
 obj-$(CONFIG_RADIO_TIMBERDALE) += radio-timb.o
 obj-$(CONFIG_RADIO_WL1273) += radio-wl1273.o
 obj-$(CONFIG_RADIO_WL128X) += wl128x/
+obj-$(CONFIG_RADIO_TAVARUA) += radio-tavarua.o
+obj-$(CONFIG_RADIO_IRIS) += radio-iris.o
+obj-$(CONFIG_RADIO_IRIS_TRANSPORT) += radio-iris-transport.o
 
 ccflags-y += -Isound
diff --git a/drivers/media/radio/radio-iris-transport.c b/drivers/media/radio/radio-iris-transport.c
new file mode 100644
index 0000000..ed6ab4d
--- /dev/null
+++ b/drivers/media/radio/radio-iris-transport.c
@@ -0,0 +1,202 @@
+/*
+ *  Qualcomm's FM Shared Memory Transport Driver
+ *
+ *  FM HCI_SMD ( FM HCI Shared Memory Driver) is Qualcomm's Shared memory driver
+ *  for the HCI protocol. This file is based on drivers/bluetooth/hci_vhci.c
+ *
+ *  Copyright (c) 2000-2001, 2011-2012 Code Aurora Forum. All rights reserved.
+ *
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2004-2006  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/skbuff.h>
+#include <linux/workqueue.h>
+#include <mach/msm_smd.h>
+#include <media/radio-iris.h>
+
+struct radio_data {
+	struct radio_hci_dev *hdev;
+	struct tasklet_struct   rx_task;
+	struct smd_channel  *fm_channel;
+};
+struct radio_data hs;
+
+static struct work_struct *reset_worker;
+
+static void radio_hci_smd_destruct(struct radio_hci_dev *hdev)
+{
+	radio_hci_unregister_dev(hs.hdev);
+}
+
+
+static void radio_hci_smd_recv_event(unsigned long temp)
+{
+	int len;
+	int rc;
+	struct sk_buff *skb;
+	unsigned  char *buf;
+	struct radio_data *hsmd = &hs;
+	len = smd_read_avail(hsmd->fm_channel);
+
+	while (len) {
+		skb = alloc_skb(len, GFP_ATOMIC);
+		if (!skb) {
+			FMDERR("Memory not allocated for the socket");
+			return;
+		}
+
+		buf = kmalloc(len, GFP_ATOMIC);
+		if (!buf) {
+			kfree_skb(skb);
+			FMDERR("Error in allocating buffer memory");
+			return;
+		}
+
+		rc = smd_read(hsmd->fm_channel, (void *)buf, len);
+
+		memcpy(skb_put(skb, len), buf, len);
+
+		skb_orphan(skb);
+		skb->dev = (struct net_device   *)hs.hdev;
+
+		rc = radio_hci_recv_frame(skb);
+
+		kfree(buf);
+		len = smd_read_avail(hsmd->fm_channel);
+	}
+}
+
+static int radio_hci_smd_send_frame(struct sk_buff *skb)
+{
+	int len = 0;
+
+	len = smd_write(hs.fm_channel, skb->data, skb->len);
+	if (len < skb->len) {
+		FMDERR("Failed to write Data %d", len);
+		kfree_skb(skb);
+		return -ENODEV;
+	}
+	kfree_skb(skb);
+	return 0;
+}
+
+
+static void send_disable_event(struct work_struct *worker)
+{
+	struct sk_buff *skb;
+	unsigned char buf[6] = { 0x0f, 0x04, 0x01, 0x02, 0x4c, 0x00 };
+	int len = sizeof(buf);
+
+	skb = alloc_skb(len, GFP_ATOMIC);
+	if (!skb) {
+		FMDERR("Memory not allocated for the socket");
+		return;
+	}
+
+	FMDERR("FM INSERT DISABLE Rx Event");
+
+	memcpy(skb_put(skb, len), buf, len);
+
+	skb_orphan(skb);
+	skb->dev = (struct net_device   *)hs.hdev;
+
+	radio_hci_recv_frame(skb);
+	kfree(worker);
+}
+
+static void radio_hci_smd_notify_cmd(void *data, unsigned int event)
+{
+	struct radio_hci_dev *hdev = hs.hdev;
+
+	if (!hdev) {
+		FMDERR("Frame for unknown HCI device (hdev=NULL)");
+		return;
+	}
+
+	switch (event) {
+	case SMD_EVENT_DATA:
+		tasklet_schedule(&hs.rx_task);
+		break;
+	case SMD_EVENT_OPEN:
+		break;
+	case SMD_EVENT_CLOSE:
+		reset_worker = kzalloc(sizeof(*reset_worker), GFP_ATOMIC);
+		if (!reset_worker) {
+			FMDERR("Out of memory");
+			break;
+		}
+		INIT_WORK(reset_worker, send_disable_event);
+		schedule_work(reset_worker);
+		break;
+	default:
+		break;
+	}
+}
+
+static int radio_hci_smd_register_dev(struct radio_data *hsmd)
+{
+	struct radio_hci_dev *hdev;
+	int rc;
+
+	hdev = kmalloc(sizeof(struct radio_hci_dev), GFP_KERNEL);
+	hsmd->hdev = hdev;
+	tasklet_init(&hsmd->rx_task, radio_hci_smd_recv_event,
+		(unsigned long) hsmd);
+	hdev->send  = radio_hci_smd_send_frame;
+	hdev->destruct = radio_hci_smd_destruct;
+
+	/* Open the SMD Channel and device and register the callback function */
+	rc = smd_named_open_on_edge("APPS_FM", SMD_APPS_WCNSS,
+		&hsmd->fm_channel, hdev, radio_hci_smd_notify_cmd);
+
+	if (rc < 0) {
+		FMDERR("Cannot open the command channel");
+		return -ENODEV;
+	}
+
+	smd_disable_read_intr(hsmd->fm_channel);
+
+	if (radio_hci_register_dev(hdev) < 0) {
+		FMDERR("Can't register HCI device");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void radio_hci_smd_deregister(void)
+{
+	smd_close(hs.fm_channel);
+	hs.fm_channel = 0;
+}
+
+static int radio_hci_smd_init(void)
+{
+	return radio_hci_smd_register_dev(&hs);
+}
+module_init(radio_hci_smd_init);
+
+static void __exit radio_hci_smd_exit(void)
+{
+	radio_hci_smd_deregister();
+}
+module_exit(radio_hci_smd_exit);
+
+MODULE_DESCRIPTION("Bluetooth SMD driver");
+MODULE_AUTHOR("Ankur Nandwani <ankurn@codeaurora.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/radio/radio-iris.c b/drivers/media/radio/radio-iris.c
new file mode 100644
index 0000000..53eb85c
--- /dev/null
+++ b/drivers/media/radio/radio-iris.c
@@ -0,0 +1,3605 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define DRIVER_AUTHOR "Archana Ramchandran <archanar@codeaurora.org>"
+#define DRIVER_NAME "radio-iris"
+#define DRIVER_CARD "Qualcomm FM Radio Transceiver"
+#define DRIVER_DESC "Driver for Qualcomm FM Radio Transceiver "
+
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/kfifo.h>
+#include <linux/param.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/version.h>
+#include <linux/videodev2.h>
+#include <linux/mutex.h>
+#include <linux/unistd.h>
+#include <linux/atomic.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/radio-iris.h>
+#include <asm/unaligned.h>
+
+static unsigned int rds_buf = 100;
+module_param(rds_buf, uint, 0);
+MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*");
+
+static void radio_hci_cmd_task(unsigned long arg);
+static void radio_hci_rx_task(unsigned long arg);
+static struct video_device *video_get_dev(void);
+static DEFINE_RWLOCK(hci_task_lock);
+
+struct iris_device {
+	struct device *dev;
+	struct kfifo data_buf[IRIS_BUF_MAX];
+
+	int pending_xfrs[IRIS_XFR_MAX];
+	int xfr_bytes_left;
+	int xfr_in_progress;
+	struct completion sync_xfr_start;
+	int tune_req;
+	unsigned int mode;
+
+	__u16 pi;
+	__u8 pty;
+	__u8 ps_repeatcount;
+	__u8 prev_trans_rds;
+	__u8 af_jump_bit;
+	struct video_device *videodev;
+
+	struct mutex lock;
+	spinlock_t buf_lock[IRIS_BUF_MAX];
+	wait_queue_head_t event_queue;
+	wait_queue_head_t read_queue;
+
+	struct radio_hci_dev *fm_hdev;
+
+	struct v4l2_capability *g_cap;
+	struct v4l2_control *g_ctl;
+
+	struct hci_fm_mute_mode_req mute_mode;
+	struct hci_fm_stereo_mode_req stereo_mode;
+	struct hci_fm_station_rsp fm_st_rsp;
+	struct hci_fm_search_station_req srch_st;
+	struct hci_fm_search_rds_station_req srch_rds;
+	struct hci_fm_search_station_list_req srch_st_list;
+	struct hci_fm_recv_conf_req recv_conf;
+	struct hci_fm_trans_conf_req_struct trans_conf;
+	struct hci_fm_rds_grp_req rds_grp;
+	unsigned char g_search_mode;
+	unsigned char power_mode;
+	int search_on;
+	unsigned int tone_freq;
+	unsigned char g_scan_time;
+	unsigned int g_antenna;
+	unsigned int g_rds_grp_proc_ps;
+	unsigned char event_mask;
+	enum iris_region_t region;
+	struct hci_fm_dbg_param_rsp st_dbg_param;
+	struct hci_ev_srch_list_compl srch_st_result;
+	struct hci_fm_riva_poke   riva_data_req;
+	struct hci_fm_ssbi_req    ssbi_data_accs;
+	struct hci_fm_ssbi_peek   ssbi_peek_reg;
+	struct hci_fm_sig_threshold_rsp sig_th;
+	struct hci_fm_ch_det_threshold ch_det_threshold;
+	struct hci_fm_data_rd_rsp default_data;
+};
+
+static struct video_device *priv_videodev;
+static int iris_do_calibration(struct iris_device *radio);
+
+static struct v4l2_queryctrl iris_v4l2_queryctrl[] = {
+	{
+	.id	= V4L2_CID_AUDIO_VOLUME,
+	.type	= V4L2_CTRL_TYPE_INTEGER,
+	.name	= "Volume",
+	.minimum	= 0,
+	.maximum	= 15,
+	.step	=	1,
+	.default_value	=	15,
+	},
+	{
+	.id	=	V4L2_CID_AUDIO_BALANCE,
+	.flags	= V4L2_CTRL_FLAG_DISABLED,
+	},
+	{
+	.id	=	V4L2_CID_AUDIO_BASS,
+	.flags	=	V4L2_CTRL_FLAG_DISABLED,
+	},
+	{
+	.id	=	V4L2_CID_AUDIO_TREBLE,
+	.flags	=	V4L2_CTRL_FLAG_DISABLED,
+	},
+	{
+	.id	=	V4L2_CID_AUDIO_MUTE,
+	.type	=	V4L2_CTRL_TYPE_BOOLEAN,
+	.name	=	"Mute",
+	.minimum	=	0,
+	.maximum	=	1,
+	.step	=	1,
+	.default_value	= 1,
+	},
+	{
+	.id	=	V4L2_CID_AUDIO_LOUDNESS,
+	.flags	=	V4L2_CTRL_FLAG_DISABLED,
+	},
+	{
+	.id	=	V4L2_CID_PRIVATE_IRIS_SRCHMODE,
+	.type	=	V4L2_CTRL_TYPE_INTEGER,
+	.name	=	"Search mode",
+	.minimum	=	0,
+	.maximum	= 7,
+	.step	= 1,
+	.default_value	= 0,
+	},
+	{
+	.id	= V4L2_CID_PRIVATE_IRIS_SCANDWELL,
+	.type	= V4L2_CTRL_TYPE_INTEGER,
+	.name	=	"Search dwell time",
+	.minimum	= 0,
+	.maximum	= 7,
+	.step	= 1,
+	.default_value	= 0,
+	},
+	{
+	.id	= V4L2_CID_PRIVATE_IRIS_SRCHON,
+	.type	= V4L2_CTRL_TYPE_BOOLEAN,
+	.name	= "Search on/off",
+	.minimum	= 0,
+	.maximum	= 1,
+	.step	= 1,
+	.default_value	= 1,
+
+	},
+	{
+	.id	=	V4L2_CID_PRIVATE_IRIS_STATE,
+	.type	= V4L2_CTRL_TYPE_INTEGER,
+	.name	= "radio 0ff/rx/tx/reset",
+	.minimum	= 0,
+	.maximum	= 3,
+	.step	= 1,
+	.default_value	=	1,
+
+	},
+	{
+	.id	= V4L2_CID_PRIVATE_IRIS_REGION,
+	.type	= V4L2_CTRL_TYPE_INTEGER,
+	.name	=	"radio standard",
+	.minimum	=	0,
+	.maximum	=	2,
+	.step	=	1,
+	.default_value	=	0,
+	},
+	{
+	.id	=	V4L2_CID_PRIVATE_IRIS_SIGNAL_TH,
+	.type	=	V4L2_CTRL_TYPE_INTEGER,
+	.name	=	"Signal Threshold",
+	.minimum	=	0x80,
+	.maximum	=	0x7F,
+	.step	=	1,
+	.default_value	=	0,
+	},
+	{
+	.id	=	V4L2_CID_PRIVATE_IRIS_SRCH_PTY,
+	.type	=	V4L2_CTRL_TYPE_INTEGER,
+	.name	=	"Search PTY",
+	.minimum	=	0,
+	.maximum	=	31,
+	.default_value	=	0,
+	},
+	{
+	.id	=	V4L2_CID_PRIVATE_IRIS_SRCH_PI,
+	.type	=	V4L2_CTRL_TYPE_INTEGER,
+	.name	=	"Search PI",
+	.minimum	=	0,
+	.maximum	=	0xFF,
+	.default_value	=	0,
+	},
+	{
+	.id	=	V4L2_CID_PRIVATE_IRIS_SRCH_CNT,
+	.type	=	V4L2_CTRL_TYPE_INTEGER,
+	.name	=	"Preset num",
+	.minimum	=	0,
+	.maximum	=	12,
+	.default_value	=	0,
+	},
+	{
+	.id	=	V4L2_CID_PRIVATE_IRIS_EMPHASIS,
+	.type	=	V4L2_CTRL_TYPE_BOOLEAN,
+	.name	=	"Emphasis",
+	.minimum	=	0,
+	.maximum	=	1,
+	.default_value	=	0,
+	},
+	{
+	.id	=	V4L2_CID_PRIVATE_IRIS_RDS_STD,
+	.type	=	V4L2_CTRL_TYPE_BOOLEAN,
+	.name	=	"RDS standard",
+	.minimum	=	0,
+	.maximum	=	1,
+	.default_value	=	0,
+	},
+	{
+	.id	=	V4L2_CID_PRIVATE_IRIS_SPACING,
+	.type	=	V4L2_CTRL_TYPE_INTEGER,
+	.name	=	"Channel spacing",
+	.minimum	=	0,
+	.maximum	=	2,
+	.default_value	=	0,
+	},
+	{
+	.id	=	V4L2_CID_PRIVATE_IRIS_RDSON,
+	.type	=	V4L2_CTRL_TYPE_BOOLEAN,
+	.name	=	"RDS on/off",
+	.minimum	=	0,
+	.maximum	=	1,
+	.default_value	=	0,
+	},
+	{
+	.id	=	V4L2_CID_PRIVATE_IRIS_RDSGROUP_MASK,
+	.type	=	V4L2_CTRL_TYPE_INTEGER,
+	.name	=	"RDS group mask",
+	.minimum	=	0,
+	.maximum	=	0xFFFFFFFF,
+	.default_value	=	0,
+	},
+	{
+	.id	=	V4L2_CID_PRIVATE_IRIS_RDSGROUP_PROC,
+	.type	=	V4L2_CTRL_TYPE_INTEGER,
+	.name	=	"RDS processing",
+	.minimum	=	0,
+	.maximum	=	0xFF,
+	.default_value	=	0,
+	},
+	{
+	.id	=	V4L2_CID_PRIVATE_IRIS_RDSD_BUF,
+	.type	=	V4L2_CTRL_TYPE_INTEGER,
+	.name	=	"RDS data groups to buffer",
+	.minimum	=	1,
+	.maximum	=	21,
+	.default_value	=	0,
+	},
+	{
+	.id	=	V4L2_CID_PRIVATE_IRIS_PSALL,
+	.type	=	V4L2_CTRL_TYPE_BOOLEAN,
+	.name	=	"pass all ps strings",
+	.minimum	=	0,
+	.maximum	=	1,
+	.default_value	=	0,
+	},
+	{
+	.id	=	V4L2_CID_PRIVATE_IRIS_LP_MODE,
+	.type	=	V4L2_CTRL_TYPE_BOOLEAN,
+	.name	=	"Low power mode",
+	.minimum	=	0,
+	.maximum	=	1,
+	.default_value	=	0,
+	},
+	{
+	.id	=	V4L2_CID_PRIVATE_IRIS_ANTENNA,
+	.type	=	V4L2_CTRL_TYPE_BOOLEAN,
+	.name	=	"headset/internal",
+	.minimum	=	0,
+	.maximum	=	1,
+	.default_value	=	0,
+	},
+	{
+	.id	=	V4L2_CID_PRIVATE_IRIS_TX_SETPSREPEATCOUNT,
+	.type	=	V4L2_CTRL_TYPE_INTEGER,
+	.name	=	"Set PS REPEATCOUNT",
+	.minimum	=	0,
+	.maximum	=	15,
+	},
+	{
+	.id	=	V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_PS_NAME,
+	.type	=	V4L2_CTRL_TYPE_BOOLEAN,
+	.name	=	"Stop PS NAME",
+	.minimum	=	0,
+	.maximum	=	1,
+	},
+	{
+	.id	=	V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_RT,
+	.type	=	V4L2_CTRL_TYPE_BOOLEAN,
+	.name	=	"Stop RT",
+	.minimum	=	0,
+	.maximum	=	1,
+	},
+	{
+	.id	=	V4L2_CID_PRIVATE_IRIS_SOFT_MUTE,
+	.type	=	V4L2_CTRL_TYPE_BOOLEAN,
+	.name	=	"Soft Mute",
+	.minimum	=	0,
+	.maximum	=	1,
+	},
+	{
+	.id	=	V4L2_CID_PRIVATE_IRIS_RIVA_ACCS_ADDR,
+	.type	=	V4L2_CTRL_TYPE_BOOLEAN,
+	.name	=	"Riva addr",
+	.minimum	=	0x3180000,
+	.maximum	=	0x31E0004,
+	},
+	{
+	.id	=	V4L2_CID_PRIVATE_IRIS_RIVA_ACCS_LEN,
+	.type	=	V4L2_CTRL_TYPE_INTEGER,
+	.name	=	"Data len",
+	.minimum	=	0,
+	.maximum	=	0xFF,
+	},
+	{
+	.id	=	V4L2_CID_PRIVATE_IRIS_RIVA_PEEK,
+	.type	=	V4L2_CTRL_TYPE_BOOLEAN,
+	.name	=	"Riva peek",
+	.minimum	=	0,
+	.maximum	=	1,
+	},
+	{
+	.id	=	V4L2_CID_PRIVATE_IRIS_RIVA_POKE,
+	.type	=	V4L2_CTRL_TYPE_INTEGER,
+	.name	=	"Riva poke",
+	.minimum	=	0x3180000,
+	.maximum	=	0x31E0004,
+	},
+	{
+	.id	=	V4L2_CID_PRIVATE_IRIS_SSBI_ACCS_ADDR,
+	.type	=	V4L2_CTRL_TYPE_INTEGER,
+	.name	=	"Ssbi addr",
+	.minimum	=	0x280,
+	.maximum	=	0x37F,
+	},
+	{
+	.id	=	V4L2_CID_PRIVATE_IRIS_SSBI_PEEK,
+	.type	=	V4L2_CTRL_TYPE_INTEGER,
+	.name	=	"Ssbi peek",
+	.minimum	=	0,
+	.maximum	=	0x37F,
+	},
+	{
+	.id	=	V4L2_CID_PRIVATE_IRIS_SSBI_POKE,
+	.type	=	V4L2_CTRL_TYPE_INTEGER,
+	.name	=	"ssbi poke",
+	.minimum	=	0x01,
+	.maximum	=	0xFF,
+	},
+	{
+	.id =	 V4L2_CID_PRIVATE_IRIS_HLSI,
+	.type	=	V4L2_CTRL_TYPE_INTEGER,
+	.name	=	"set hlsi",
+	.minimum	=	0,
+	.maximum	=	2,
+	},
+	{
+	.id =	 V4L2_CID_PRIVATE_IRIS_RDS_GRP_COUNTERS,
+	.type	=	V4L2_CTRL_TYPE_BOOLEAN,
+	.name	=	"RDS grp",
+	.minimum	=	0,
+	.maximum	=	1,
+	},
+	{
+	.id	=	V4L2_CID_PRIVATE_IRIS_SET_NOTCH_FILTER,
+	.type	=	V4L2_CTRL_TYPE_INTEGER,
+	.name	=	"Notch filter",
+	.minimum	=	0,
+	.maximum	=	2,
+	},
+	{
+	.id	=	V4L2_CID_PRIVATE_IRIS_READ_DEFAULT,
+	.type	=	V4L2_CTRL_TYPE_INTEGER,
+	.name	=	"Read default",
+	},
+	{
+	.id	=	V4L2_CID_PRIVATE_IRIS_WRITE_DEFAULT,
+	.type	=	V4L2_CTRL_TYPE_INTEGER,
+	.name	=	"Write default",
+	},
+	{
+	.id	=	V4L2_CID_PRIVATE_IRIS_SET_CALIBRATION,
+	.type	=	V4L2_CTRL_TYPE_BOOLEAN,
+	.name	=	"SET Calibration",
+	.minimum	=	0,
+	.maximum	=	1,
+	},
+	{
+	.id	=	V4L2_CID_PRIVATE_IRIS_DO_CALIBRATION,
+	.type	=	V4L2_CTRL_TYPE_BOOLEAN,
+	.name	=	"SET Calibration",
+	.minimum	=	0,
+	.maximum	=	1,
+	},
+	{
+	.id     =       V4L2_CID_PRIVATE_IRIS_GET_SINR,
+	.type   =       V4L2_CTRL_TYPE_INTEGER,
+	.name   =       "GET SINR",
+	.minimum        =       -128,
+	.maximum        =       127,
+	},
+	{
+	.id     =       V4L2_CID_PRIVATE_INTF_HIGH_THRESHOLD,
+	.type   =       V4L2_CTRL_TYPE_INTEGER,
+	.name   =       "Intf High Threshold",
+	.minimum        =       0,
+	.maximum        =       0xFF,
+	.default_value  =       0,
+	},
+	{
+	.id     =       V4L2_CID_PRIVATE_INTF_LOW_THRESHOLD,
+	.type   =       V4L2_CTRL_TYPE_INTEGER,
+	.name   =       "Intf low Threshold",
+	.minimum        =       0,
+	.maximum        =       0xFF,
+	.default_value  =       0,
+	},
+	{
+	.id     =       V4L2_CID_PRIVATE_SINR_THRESHOLD,
+	.type   =       V4L2_CTRL_TYPE_INTEGER,
+	.name   =       "SINR Threshold",
+	.minimum        =       -128,
+	.maximum        =       127,
+	.default_value  =       0,
+	},
+	{
+	.id     =       V4L2_CID_PRIVATE_SINR_SAMPLES,
+	.type   =       V4L2_CTRL_TYPE_INTEGER,
+	.name   =       "SINR samples",
+	.minimum        =       1,
+	.maximum        =       0xFF,
+	.default_value  =       0,
+	},
+};
+
+static void iris_q_event(struct iris_device *radio,
+				enum iris_evt_t event)
+{
+	struct kfifo *data_b = &radio->data_buf[IRIS_BUF_EVENTS];
+	unsigned char evt = event;
+	if (kfifo_in_locked(data_b, &evt, 1, &radio->buf_lock[IRIS_BUF_EVENTS]))
+		wake_up_interruptible(&radio->event_queue);
+}
+
+static int hci_send_frame(struct sk_buff *skb)
+{
+	struct radio_hci_dev *hdev = (struct radio_hci_dev *) skb->dev;
+
+	if (!hdev) {
+		kfree_skb(skb);
+		return -ENODEV;
+	}
+
+	__net_timestamp(skb);
+
+	skb_orphan(skb);
+	return hdev->send(skb);
+}
+
+static void radio_hci_cmd_task(unsigned long arg)
+{
+	struct radio_hci_dev *hdev = (struct radio_hci_dev *) arg;
+	struct sk_buff *skb;
+	if (!(atomic_read(&hdev->cmd_cnt))
+		&& time_after(jiffies, hdev->cmd_last_tx + HZ)) {
+		FMDERR("%s command tx timeout", hdev->name);
+		atomic_set(&hdev->cmd_cnt, 1);
+	}
+
+	skb = skb_dequeue(&hdev->cmd_q);
+	if (atomic_read(&hdev->cmd_cnt) && skb) {
+		kfree_skb(hdev->sent_cmd);
+		hdev->sent_cmd = skb_clone(skb, GFP_ATOMIC);
+		if (hdev->sent_cmd) {
+			atomic_dec(&hdev->cmd_cnt);
+			hci_send_frame(skb);
+			hdev->cmd_last_tx = jiffies;
+		} else {
+			skb_queue_head(&hdev->cmd_q, skb);
+			tasklet_schedule(&hdev->cmd_task);
+		}
+	}
+
+}
+
+static void radio_hci_rx_task(unsigned long arg)
+{
+	struct radio_hci_dev *hdev = (struct radio_hci_dev *) arg;
+	struct sk_buff *skb;
+
+	read_lock(&hci_task_lock);
+
+	skb = skb_dequeue(&hdev->rx_q);
+	radio_hci_event_packet(hdev, skb);
+
+	read_unlock(&hci_task_lock);
+}
+
+int radio_hci_register_dev(struct radio_hci_dev *hdev)
+{
+	struct iris_device *radio = video_get_drvdata(video_get_dev());
+	if (!radio) {
+		FMDERR(":radio is null");
+		return -EINVAL;
+	}
+
+	if (!hdev) {
+		FMDERR("hdev is null");
+		return -EINVAL;
+	}
+
+	hdev->flags = 0;
+
+	tasklet_init(&hdev->cmd_task, radio_hci_cmd_task, (unsigned long)
+		hdev);
+	tasklet_init(&hdev->rx_task, radio_hci_rx_task, (unsigned long)
+		hdev);
+
+	init_waitqueue_head(&hdev->req_wait_q);
+
+	skb_queue_head_init(&hdev->rx_q);
+	skb_queue_head_init(&hdev->cmd_q);
+	skb_queue_head_init(&hdev->raw_q);
+
+	if (!radio)
+		FMDERR(":radio is null");
+
+	radio->fm_hdev = hdev;
+
+	return 0;
+}
+EXPORT_SYMBOL(radio_hci_register_dev);
+
+int radio_hci_unregister_dev(struct radio_hci_dev *hdev)
+{
+	struct iris_device *radio = video_get_drvdata(video_get_dev());
+	if (!radio) {
+		FMDERR(":radio is null");
+		return -EINVAL;
+	}
+
+	tasklet_kill(&hdev->rx_task);
+	tasklet_kill(&hdev->cmd_task);
+	skb_queue_purge(&hdev->rx_q);
+	skb_queue_purge(&hdev->cmd_q);
+	skb_queue_purge(&hdev->raw_q);
+	kfree(radio->fm_hdev);
+	kfree(radio->videodev);
+
+	return 0;
+}
+EXPORT_SYMBOL(radio_hci_unregister_dev);
+
+int radio_hci_recv_frame(struct sk_buff *skb)
+{
+	struct radio_hci_dev *hdev = (struct radio_hci_dev *) skb->dev;
+	if (!hdev) {
+		FMDERR("%s hdev is null while receiving frame", hdev->name);
+		kfree_skb(skb);
+		return -ENXIO;
+	}
+
+	__net_timestamp(skb);
+
+	radio_hci_event_packet(hdev, skb);
+	kfree_skb(skb);
+	return 0;
+}
+EXPORT_SYMBOL(radio_hci_recv_frame);
+
+int radio_hci_send_cmd(struct radio_hci_dev *hdev, __u16 opcode, __u32 plen,
+		void *param)
+{
+	int len = RADIO_HCI_COMMAND_HDR_SIZE + plen;
+	struct radio_hci_command_hdr *hdr;
+	struct sk_buff *skb;
+	int ret = 0;
+
+	skb = alloc_skb(len, GFP_ATOMIC);
+	if (!skb) {
+		FMDERR("%s no memory for command", hdev->name);
+		return -ENOMEM;
+	}
+
+	hdr = (struct radio_hci_command_hdr *) skb_put(skb,
+		RADIO_HCI_COMMAND_HDR_SIZE);
+	hdr->opcode = cpu_to_le16(opcode);
+	hdr->plen   = plen;
+
+	if (plen)
+		memcpy(skb_put(skb, plen), param, plen);
+
+	skb->dev = (void *) hdev;
+
+	ret = hci_send_frame(skb);
+
+	return ret;
+}
+EXPORT_SYMBOL(radio_hci_send_cmd);
+
+static int hci_fm_enable_recv_req(struct radio_hci_dev *hdev,
+	unsigned long param)
+{
+	__u16 opcode = 0;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+		HCI_OCF_FM_ENABLE_RECV_REQ);
+	return radio_hci_send_cmd(hdev, opcode, 0, NULL);
+}
+
+static int hci_fm_tone_generator(struct radio_hci_dev *hdev,
+	unsigned long param)
+{
+	struct iris_device *radio = video_get_drvdata(video_get_dev());
+	__u16 opcode = 0;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_DIAGNOSTIC_CMD_REQ,
+		HCI_FM_SET_INTERNAL_TONE_GENRATOR);
+	return radio_hci_send_cmd(hdev, opcode,
+			sizeof(radio->tone_freq), &radio->tone_freq);
+}
+
+static int hci_fm_enable_trans_req(struct radio_hci_dev *hdev,
+	unsigned long param)
+{
+	__u16 opcode = 0;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ,
+		HCI_OCF_FM_ENABLE_TRANS_REQ);
+	return radio_hci_send_cmd(hdev, opcode, 0, NULL);
+}
+
+static int hci_fm_disable_recv_req(struct radio_hci_dev *hdev,
+	unsigned long param)
+{
+	__u16 opcode = 0;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+		HCI_OCF_FM_DISABLE_RECV_REQ);
+	return radio_hci_send_cmd(hdev, opcode, 0, NULL);
+}
+
+static int hci_fm_disable_trans_req(struct radio_hci_dev *hdev,
+	unsigned long param)
+{
+	__u16 opcode = 0;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ,
+		HCI_OCF_FM_DISABLE_TRANS_REQ);
+	return radio_hci_send_cmd(hdev, opcode, 0, NULL);
+}
+
+static int hci_get_fm_recv_conf_req(struct radio_hci_dev *hdev,
+	unsigned long param)
+{
+	__u16 opcode = 0;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+		HCI_OCF_FM_GET_RECV_CONF_REQ);
+	return radio_hci_send_cmd(hdev, opcode, 0, NULL);
+}
+
+static int hci_get_fm_trans_conf_req(struct radio_hci_dev *hdev,
+	unsigned long param)
+{
+	u16 opcode = 0;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ,
+		HCI_OCF_FM_GET_TRANS_CONF_REQ);
+	return radio_hci_send_cmd(hdev, opcode, 0, NULL);
+}
+static int hci_set_fm_recv_conf_req(struct radio_hci_dev *hdev,
+	unsigned long param)
+{
+	__u16 opcode = 0;
+
+	struct hci_fm_recv_conf_req *recv_conf_req =
+		(struct hci_fm_recv_conf_req *) param;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+		HCI_OCF_FM_SET_RECV_CONF_REQ);
+	return radio_hci_send_cmd(hdev, opcode, sizeof((*recv_conf_req)),
+		recv_conf_req);
+}
+
+static int hci_set_fm_trans_conf_req(struct radio_hci_dev *hdev,
+	unsigned long param)
+{
+	__u16 opcode = 0;
+
+	struct hci_fm_trans_conf_req_struct *trans_conf_req =
+		(struct hci_fm_trans_conf_req_struct *) param;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ,
+		HCI_OCF_FM_SET_TRANS_CONF_REQ);
+	return radio_hci_send_cmd(hdev, opcode, sizeof((*trans_conf_req)),
+		trans_conf_req);
+}
+
+static int hci_fm_get_station_param_req(struct radio_hci_dev *hdev,
+		unsigned long param)
+{
+	__u16 opcode = 0;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+		HCI_OCF_FM_GET_STATION_PARAM_REQ);
+	return radio_hci_send_cmd(hdev, opcode, 0, NULL);
+}
+
+static int hci_set_fm_mute_mode_req(struct radio_hci_dev *hdev,
+		unsigned long param)
+{
+	__u16 opcode = 0;
+	struct hci_fm_mute_mode_req *mute_mode_req =
+		(struct hci_fm_mute_mode_req *) param;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+		HCI_OCF_FM_SET_MUTE_MODE_REQ);
+	return radio_hci_send_cmd(hdev, opcode, sizeof((*mute_mode_req)),
+		mute_mode_req);
+}
+
+
+static int hci_trans_ps_req(struct radio_hci_dev *hdev,
+		unsigned long param)
+{
+	__u16 opcode = 0;
+	struct hci_fm_tx_ps *tx_ps_req =
+		(struct hci_fm_tx_ps *) param;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ,
+		HCI_OCF_FM_RDS_PS_REQ);
+
+	return radio_hci_send_cmd(hdev, opcode, sizeof((*tx_ps_req)),
+		tx_ps_req);
+}
+
+static int hci_trans_rt_req(struct radio_hci_dev *hdev,
+		unsigned long param)
+{
+	__u16 opcode = 0;
+	struct hci_fm_tx_rt *tx_rt_req =
+		(struct hci_fm_tx_rt *) param;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ,
+		HCI_OCF_FM_RDS_RT_REQ);
+
+	return radio_hci_send_cmd(hdev, opcode, sizeof((*tx_rt_req)),
+		tx_rt_req);
+}
+
+static int hci_set_fm_stereo_mode_req(struct radio_hci_dev *hdev,
+		unsigned long param)
+{
+	__u16 opcode = 0;
+	struct hci_fm_stereo_mode_req *stereo_mode_req =
+		(struct hci_fm_stereo_mode_req *) param;
+	opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+		HCI_OCF_FM_SET_STEREO_MODE_REQ);
+	return radio_hci_send_cmd(hdev, opcode, sizeof((*stereo_mode_req)),
+		stereo_mode_req);
+}
+
+static int hci_fm_set_antenna_req(struct radio_hci_dev *hdev,
+	unsigned long param)
+{
+	__u16 opcode = 0;
+
+	__u8 antenna = param;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+		HCI_OCF_FM_SET_ANTENNA);
+	return radio_hci_send_cmd(hdev, opcode, sizeof(antenna), &antenna);
+}
+
+static int hci_fm_set_sig_threshold_req(struct radio_hci_dev *hdev,
+		unsigned long param)
+{
+	__u16 opcode = 0;
+
+	__u8 sig_threshold = param;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+		HCI_OCF_FM_SET_SIGNAL_THRESHOLD);
+	return radio_hci_send_cmd(hdev, opcode, sizeof(sig_threshold),
+		&sig_threshold);
+}
+
+static int hci_fm_set_event_mask(struct radio_hci_dev *hdev,
+		unsigned long param)
+{
+	u16 opcode = 0;
+	u8 event_mask = param;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+		HCI_OCF_FM_SET_EVENT_MASK);
+	return radio_hci_send_cmd(hdev, opcode, sizeof(event_mask),
+		&event_mask);
+}
+static int hci_fm_get_sig_threshold_req(struct radio_hci_dev *hdev,
+		unsigned long param)
+{
+	__u16 opcode = 0;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+		HCI_OCF_FM_GET_SIGNAL_THRESHOLD);
+	return radio_hci_send_cmd(hdev, opcode, 0, NULL);
+}
+
+static int hci_fm_get_program_service_req(struct radio_hci_dev *hdev,
+		unsigned long param)
+{
+	__u16 opcode = 0;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+		HCI_OCF_FM_GET_PROGRAM_SERVICE_REQ);
+	return radio_hci_send_cmd(hdev, opcode, 0, NULL);
+}
+
+static int hci_fm_get_radio_text_req(struct radio_hci_dev *hdev,
+		unsigned long param)
+{
+	__u16 opcode = 0;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+		HCI_OCF_FM_GET_RADIO_TEXT_REQ);
+	return radio_hci_send_cmd(hdev, opcode, 0, NULL);
+}
+
+static int hci_fm_get_af_list_req(struct radio_hci_dev *hdev,
+	unsigned long param)
+{
+	__u16 opcode = 0;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+		HCI_OCF_FM_GET_AF_LIST_REQ);
+	return radio_hci_send_cmd(hdev, opcode, 0, NULL);
+}
+
+static int hci_fm_search_stations_req(struct radio_hci_dev *hdev,
+		unsigned long param)
+{
+	__u16 opcode = 0;
+	struct hci_fm_search_station_req *srch_stations =
+		(struct hci_fm_search_station_req *) param;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+		HCI_OCF_FM_SEARCH_STATIONS);
+	return radio_hci_send_cmd(hdev, opcode, sizeof((*srch_stations)),
+		srch_stations);
+}
+
+static int hci_fm_srch_rds_stations_req(struct radio_hci_dev *hdev,
+		unsigned long param)
+{
+	__u16 opcode = 0;
+	struct hci_fm_search_rds_station_req *srch_stations =
+		(struct hci_fm_search_rds_station_req *) param;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+		HCI_OCF_FM_SEARCH_RDS_STATIONS);
+	return radio_hci_send_cmd(hdev, opcode, sizeof((*srch_stations)),
+		srch_stations);
+}
+
+static int hci_fm_srch_station_list_req(struct radio_hci_dev *hdev,
+		unsigned long param)
+{
+	__u16 opcode = 0;
+	struct hci_fm_search_station_list_req *srch_list =
+		(struct hci_fm_search_station_list_req *) param;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+		HCI_OCF_FM_SEARCH_STATIONS_LIST);
+	return radio_hci_send_cmd(hdev, opcode, sizeof((*srch_list)),
+		srch_list);
+}
+
+static int hci_fm_cancel_search_req(struct radio_hci_dev *hdev,
+		unsigned long param)
+{
+	__u16 opcode = 0;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+		HCI_OCF_FM_CANCEL_SEARCH);
+	return radio_hci_send_cmd(hdev, opcode, 0, NULL);
+}
+
+static int hci_fm_rds_grp_process_req(struct radio_hci_dev *hdev,
+		unsigned long param)
+{
+	__u16 opcode = 0;
+
+	__u32 fm_grps_process = param;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+		HCI_OCF_FM_RDS_GRP_PROCESS);
+	return radio_hci_send_cmd(hdev, opcode, sizeof(fm_grps_process),
+		&fm_grps_process);
+}
+
+static int hci_fm_tune_station_req(struct radio_hci_dev *hdev,
+	unsigned long param)
+{
+	__u16 opcode = 0;
+
+	__u32 tune_freq = param;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
+		HCI_OCF_FM_TUNE_STATION_REQ);
+	return radio_hci_send_cmd(hdev, opcode, sizeof(tune_freq), &tune_freq);
+}
+
+static int hci_def_data_read_req(struct radio_hci_dev *hdev,
+	unsigned long param)
+{
+	__u16 opcode = 0;
+	struct hci_fm_def_data_rd_req *def_data_rd =
+		(struct hci_fm_def_data_rd_req *) param;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
+		HCI_OCF_FM_DEFAULT_DATA_READ);
+	return radio_hci_send_cmd(hdev, opcode, sizeof((*def_data_rd)),
+	def_data_rd);
+}
+
+static int hci_def_data_write_req(struct radio_hci_dev *hdev,
+	unsigned long param)
+{
+	__u16 opcode = 0;
+	struct hci_fm_def_data_wr_req *def_data_wr =
+		(struct hci_fm_def_data_wr_req *) param;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
+		HCI_OCF_FM_DEFAULT_DATA_WRITE);
+
+	return radio_hci_send_cmd(hdev, opcode, (def_data_wr->length+2),
+	def_data_wr);
+}
+
+static int hci_set_notch_filter_req(struct radio_hci_dev *hdev,
+	unsigned long param)
+{
+	__u16 opcode = 0;
+	__u8 notch_filter_val = param;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+		HCI_OCF_FM_EN_NOTCH_CTRL);
+	return radio_hci_send_cmd(hdev, opcode, sizeof(notch_filter_val),
+	&notch_filter_val);
+}
+
+
+
+static int hci_fm_reset_req(struct radio_hci_dev *hdev, unsigned long param)
+{
+	__u16 opcode = 0;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
+		HCI_OCF_FM_RESET);
+	return radio_hci_send_cmd(hdev, opcode, 0, NULL);
+}
+
+static int hci_fm_get_feature_lists_req(struct radio_hci_dev *hdev,
+		unsigned long param)
+{
+	__u16 opcode = 0;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
+		HCI_OCF_FM_GET_FEATURE_LIST);
+	return radio_hci_send_cmd(hdev, opcode, 0, NULL);
+}
+
+static int hci_fm_do_calibration_req(struct radio_hci_dev *hdev,
+		unsigned long param)
+{
+	__u16 opcode = 0;
+
+	__u8 mode = param;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
+		HCI_OCF_FM_DO_CALIBRATION);
+	return radio_hci_send_cmd(hdev, opcode, sizeof(mode), &mode);
+}
+
+static int hci_read_grp_counters_req(struct radio_hci_dev *hdev,
+		unsigned long param)
+{
+	__u16 opcode = 0;
+
+	__u8 reset_counters = param;
+	opcode = hci_opcode_pack(HCI_OGF_FM_STATUS_PARAMETERS_CMD_REQ,
+		HCI_OCF_FM_READ_GRP_COUNTERS);
+	return radio_hci_send_cmd(hdev, opcode, sizeof(reset_counters),
+		&reset_counters);
+}
+
+static int hci_peek_data_req(struct radio_hci_dev *hdev, unsigned long param)
+{
+	__u16 opcode = 0;
+	struct hci_fm_riva_data *peek_data = (struct hci_fm_riva_data *)param;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_DIAGNOSTIC_CMD_REQ,
+		HCI_OCF_FM_PEEK_DATA);
+	return radio_hci_send_cmd(hdev, opcode, sizeof((*peek_data)),
+	peek_data);
+}
+
+static int hci_poke_data_req(struct radio_hci_dev *hdev, unsigned long param)
+{
+	__u16 opcode = 0;
+	struct hci_fm_riva_poke *poke_data = (struct hci_fm_riva_poke *) param;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_DIAGNOSTIC_CMD_REQ,
+		HCI_OCF_FM_POKE_DATA);
+	return radio_hci_send_cmd(hdev, opcode, sizeof((*poke_data)),
+	poke_data);
+}
+
+static int hci_ssbi_peek_reg_req(struct radio_hci_dev *hdev,
+	unsigned long param)
+{
+	__u16 opcode = 0;
+	struct hci_fm_ssbi_peek *ssbi_peek = (struct hci_fm_ssbi_peek *) param;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_DIAGNOSTIC_CMD_REQ,
+		HCI_OCF_FM_SSBI_PEEK_REG);
+	return radio_hci_send_cmd(hdev, opcode, sizeof((*ssbi_peek)),
+	ssbi_peek);
+}
+
+static int hci_ssbi_poke_reg_req(struct radio_hci_dev *hdev,
+	unsigned long param)
+{
+	__u16 opcode = 0;
+	struct hci_fm_ssbi_req *ssbi_poke = (struct hci_fm_ssbi_req *) param;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_DIAGNOSTIC_CMD_REQ,
+		HCI_OCF_FM_SSBI_POKE_REG);
+	return radio_hci_send_cmd(hdev, opcode, sizeof((*ssbi_poke)),
+	ssbi_poke);
+}
+
+static int hci_fm_get_station_dbg_param_req(struct radio_hci_dev *hdev,
+		unsigned long param)
+{
+	__u16 opcode = 0;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_DIAGNOSTIC_CMD_REQ,
+		HCI_OCF_FM_STATION_DBG_PARAM);
+	return radio_hci_send_cmd(hdev, opcode, 0, NULL);
+}
+
+static int hci_fm_set_ch_det_th(struct radio_hci_dev *hdev,
+	unsigned long param)
+{
+	struct hci_fm_ch_det_threshold *ch_det_th =
+			 (struct hci_fm_ch_det_threshold *) param;
+	u16 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+		HCI_OCF_FM_SET_CH_DET_THRESHOLD);
+	return radio_hci_send_cmd(hdev, opcode, sizeof((*ch_det_th)),
+		ch_det_th);
+}
+
+static int hci_fm_get_ch_det_th(struct radio_hci_dev *hdev,
+		unsigned long param)
+{
+	u16 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+			HCI_OCF_FM_GET_CH_DET_THRESHOLD);
+	return radio_hci_send_cmd(hdev, opcode, 0, NULL);
+}
+
+static int radio_hci_err(__u16 code)
+{
+	switch (code) {
+	case 0:
+		return 0;
+	case 0x01:
+		return -EBADRQC;
+	case 0x02:
+		return -ENOTCONN;
+	case 0x03:
+		return -EIO;
+	case 0x07:
+		return -ENOMEM;
+	case 0x0c:
+		return -EBUSY;
+	case 0x11:
+		return -EOPNOTSUPP;
+	case 0x12:
+		return -EINVAL;
+	default:
+		return -ENOSYS;
+	}
+}
+
+static int __radio_hci_request(struct radio_hci_dev *hdev,
+		int (*req)(struct radio_hci_dev *hdev,
+			unsigned long param),
+			unsigned long param, __u32 timeout)
+{
+	int err = 0;
+
+	DECLARE_WAITQUEUE(wait, current);
+
+	hdev->req_status = HCI_REQ_PEND;
+
+	add_wait_queue(&hdev->req_wait_q, &wait);
+	set_current_state(TASK_INTERRUPTIBLE);
+
+	err = req(hdev, param);
+
+	schedule_timeout(timeout);
+
+	remove_wait_queue(&hdev->req_wait_q, &wait);
+
+	if (signal_pending(current))
+		return -EINTR;
+
+	switch (hdev->req_status) {
+	case HCI_REQ_DONE:
+	case HCI_REQ_STATUS:
+		err = radio_hci_err(hdev->req_result);
+		break;
+
+	case HCI_REQ_CANCELED:
+		err = -hdev->req_result;
+		break;
+
+	default:
+		err = -ETIMEDOUT;
+		break;
+	}
+
+	hdev->req_status = hdev->req_result = 0;
+
+	return err;
+}
+
+static inline int radio_hci_request(struct radio_hci_dev *hdev,
+		int (*req)(struct
+		radio_hci_dev * hdev, unsigned long param),
+		unsigned long param, __u32 timeout)
+{
+	int ret = 0;
+
+	ret = __radio_hci_request(hdev, req, param, timeout);
+
+	return ret;
+}
+
+static inline int hci_conf_event_mask(__u8 *arg,
+		struct radio_hci_dev *hdev)
+{
+	u8 event_mask = *arg;
+	return  radio_hci_request(hdev, hci_fm_set_event_mask,
+				event_mask, RADIO_HCI_TIMEOUT);
+}
+static int hci_set_fm_recv_conf(struct hci_fm_recv_conf_req *arg,
+		struct radio_hci_dev *hdev)
+{
+	int ret = 0;
+	struct hci_fm_recv_conf_req *set_recv_conf = arg;
+
+	ret = radio_hci_request(hdev, hci_set_fm_recv_conf_req, (unsigned
+		long)set_recv_conf, RADIO_HCI_TIMEOUT);
+
+	return ret;
+}
+
+static int hci_set_fm_trans_conf(struct hci_fm_trans_conf_req_struct *arg,
+		struct radio_hci_dev *hdev)
+{
+	int ret = 0;
+	struct hci_fm_trans_conf_req_struct *set_trans_conf = arg;
+
+	ret = radio_hci_request(hdev, hci_set_fm_trans_conf_req, (unsigned
+		long)set_trans_conf, RADIO_HCI_TIMEOUT);
+
+	return ret;
+}
+
+static int hci_fm_tune_station(__u32 *arg, struct radio_hci_dev *hdev)
+{
+	int ret = 0;
+	__u32 tune_freq = *arg;
+
+	ret = radio_hci_request(hdev, hci_fm_tune_station_req, tune_freq,
+		RADIO_HCI_TIMEOUT);
+
+	return ret;
+}
+
+static int hci_set_fm_mute_mode(struct hci_fm_mute_mode_req *arg,
+	struct radio_hci_dev *hdev)
+{
+	int ret = 0;
+	struct hci_fm_mute_mode_req *set_mute_conf = arg;
+
+	ret = radio_hci_request(hdev, hci_set_fm_mute_mode_req, (unsigned
+		long)set_mute_conf, RADIO_HCI_TIMEOUT);
+
+	return ret;
+}
+
+static int hci_set_fm_stereo_mode(struct hci_fm_stereo_mode_req *arg,
+	struct radio_hci_dev *hdev)
+{
+	int ret = 0;
+	struct hci_fm_stereo_mode_req *set_stereo_conf = arg;
+
+	ret = radio_hci_request(hdev, hci_set_fm_stereo_mode_req, (unsigned
+		long)set_stereo_conf, RADIO_HCI_TIMEOUT);
+
+	return ret;
+}
+
+static int hci_fm_set_antenna(__u8 *arg, struct radio_hci_dev *hdev)
+{
+	int ret = 0;
+	__u8 antenna = *arg;
+
+	ret = radio_hci_request(hdev, hci_fm_set_antenna_req, antenna,
+		RADIO_HCI_TIMEOUT);
+
+	return ret;
+}
+
+static int hci_fm_set_signal_threshold(__u8 *arg,
+	struct radio_hci_dev *hdev)
+{
+	int ret = 0;
+	__u8 sig_threshold = *arg;
+
+	ret = radio_hci_request(hdev, hci_fm_set_sig_threshold_req,
+		sig_threshold, RADIO_HCI_TIMEOUT);
+
+	return ret;
+}
+
+static int hci_fm_search_stations(struct hci_fm_search_station_req *arg,
+	struct radio_hci_dev *hdev)
+{
+	int ret = 0;
+	struct hci_fm_search_station_req *srch_stations = arg;
+
+	ret = radio_hci_request(hdev, hci_fm_search_stations_req, (unsigned
+		long)srch_stations, RADIO_HCI_TIMEOUT);
+
+	return ret;
+}
+
+static int hci_fm_search_rds_stations(struct hci_fm_search_rds_station_req *arg,
+	struct radio_hci_dev *hdev)
+{
+	int ret = 0;
+	struct hci_fm_search_rds_station_req *srch_stations = arg;
+
+	ret = radio_hci_request(hdev, hci_fm_srch_rds_stations_req, (unsigned
+		long)srch_stations, RADIO_HCI_TIMEOUT);
+
+	return ret;
+}
+
+static int hci_fm_search_station_list
+	(struct hci_fm_search_station_list_req *arg,
+	struct radio_hci_dev *hdev)
+{
+	int ret = 0;
+	struct hci_fm_search_station_list_req *srch_list = arg;
+
+	ret = radio_hci_request(hdev, hci_fm_srch_station_list_req, (unsigned
+		long)srch_list, RADIO_HCI_TIMEOUT);
+
+	return ret;
+}
+
+static int hci_fm_rds_grp(struct hci_fm_rds_grp_req *arg,
+	struct radio_hci_dev *hdev)
+{
+	return 0;
+}
+
+static int hci_fm_rds_grps_process(__u32 *arg, struct radio_hci_dev *hdev)
+{
+	int ret = 0;
+	__u32 fm_grps_process = *arg;
+
+	ret = radio_hci_request(hdev, hci_fm_rds_grp_process_req,
+		fm_grps_process, RADIO_HCI_TIMEOUT);
+
+	return ret;
+}
+
+int hci_def_data_read(struct hci_fm_def_data_rd_req *arg,
+	struct radio_hci_dev *hdev)
+{
+	int ret = 0;
+	struct hci_fm_def_data_rd_req *def_data_rd = arg;
+	ret = radio_hci_request(hdev, hci_def_data_read_req, (unsigned
+		long)def_data_rd, RADIO_HCI_TIMEOUT);
+
+	return ret;
+}
+
+int hci_def_data_write(struct hci_fm_def_data_wr_req *arg,
+	struct radio_hci_dev *hdev)
+{
+	int ret = 0;
+	struct hci_fm_def_data_wr_req *def_data_wr = arg;
+	ret = radio_hci_request(hdev, hci_def_data_write_req, (unsigned
+		long)def_data_wr, RADIO_HCI_TIMEOUT);
+
+	return ret;
+}
+
+int hci_fm_do_calibration(__u8 *arg, struct radio_hci_dev *hdev)
+{
+	int ret = 0;
+	__u8 mode = *arg;
+
+	ret = radio_hci_request(hdev, hci_fm_do_calibration_req, mode,
+		RADIO_HCI_TIMEOUT);
+
+	return ret;
+}
+
+static int hci_read_grp_counters(__u8 *arg, struct radio_hci_dev *hdev)
+{
+	int ret = 0;
+	__u8 reset_counters = *arg;
+	ret = radio_hci_request(hdev, hci_read_grp_counters_req,
+		reset_counters, RADIO_HCI_TIMEOUT);
+
+	return ret;
+}
+
+static int hci_set_notch_filter(__u8 *arg, struct radio_hci_dev *hdev)
+{
+	int ret = 0;
+	__u8 notch_filter = *arg;
+	ret = radio_hci_request(hdev, hci_set_notch_filter_req,
+		notch_filter, RADIO_HCI_TIMEOUT);
+
+	return ret;
+}
+
+static int hci_peek_data(struct hci_fm_riva_data *arg,
+				struct radio_hci_dev *hdev)
+{
+	int ret = 0;
+	struct hci_fm_riva_data *peek_data = arg;
+
+	ret = radio_hci_request(hdev, hci_peek_data_req, (unsigned
+		long)peek_data, RADIO_HCI_TIMEOUT);
+
+	return ret;
+}
+
+static int hci_poke_data(struct hci_fm_riva_poke *arg,
+			struct radio_hci_dev *hdev)
+{
+	int ret = 0;
+	struct hci_fm_riva_poke *poke_data = arg;
+
+	ret = radio_hci_request(hdev, hci_poke_data_req, (unsigned
+		long)poke_data, RADIO_HCI_TIMEOUT);
+
+	return ret;
+}
+
+static int hci_ssbi_peek_reg(struct hci_fm_ssbi_peek *arg,
+	struct radio_hci_dev *hdev)
+{
+	int ret = 0;
+	struct hci_fm_ssbi_peek *ssbi_peek_reg = arg;
+
+	ret = radio_hci_request(hdev, hci_ssbi_peek_reg_req, (unsigned
+		long)ssbi_peek_reg, RADIO_HCI_TIMEOUT);
+
+	return ret;
+}
+
+static int hci_ssbi_poke_reg(struct hci_fm_ssbi_req *arg,
+			struct radio_hci_dev *hdev)
+{
+	int ret = 0;
+	struct hci_fm_ssbi_req *ssbi_poke_reg = arg;
+
+	ret = radio_hci_request(hdev, hci_ssbi_poke_reg_req, (unsigned
+		long)ssbi_poke_reg, RADIO_HCI_TIMEOUT);
+
+	return ret;
+}
+
+static int hci_set_ch_det_thresholds_req(struct hci_fm_ch_det_threshold *arg,
+		struct radio_hci_dev *hdev)
+{
+	int ret = 0;
+	struct hci_fm_ch_det_threshold *ch_det_threshold = arg;
+	ret = radio_hci_request(hdev, hci_fm_set_ch_det_th,
+		 (unsigned long)ch_det_threshold, RADIO_HCI_TIMEOUT);
+	return ret;
+}
+
+static int hci_fm_set_cal_req_proc(struct radio_hci_dev *hdev,
+		unsigned long param)
+{
+	u16 opcode = 0;
+	struct hci_fm_set_cal_req_proc *cal_req =
+		(struct hci_fm_set_cal_req_proc *)param;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
+		HCI_OCF_FM_SET_CALIBRATION);
+	return radio_hci_send_cmd(hdev, opcode, sizeof(*cal_req),
+		cal_req);
+}
+
+static int hci_fm_do_cal_req(struct radio_hci_dev *hdev,
+		unsigned long param)
+{
+	u16 opcode = 0;
+	u8 cal_mode = param;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
+		HCI_OCF_FM_DO_CALIBRATION);
+	return radio_hci_send_cmd(hdev, opcode, sizeof(cal_mode),
+		&cal_mode);
+
+}
+static int hci_cmd(unsigned int cmd, struct radio_hci_dev *hdev)
+{
+	int ret = 0;
+	unsigned long arg = 0;
+
+	if (!hdev)
+		return -ENODEV;
+
+	switch (cmd) {
+	case HCI_FM_ENABLE_RECV_CMD:
+		ret = radio_hci_request(hdev, hci_fm_enable_recv_req, arg,
+			msecs_to_jiffies(RADIO_HCI_TIMEOUT));
+		break;
+
+	case HCI_FM_DISABLE_RECV_CMD:
+		ret = radio_hci_request(hdev, hci_fm_disable_recv_req, arg,
+			msecs_to_jiffies(RADIO_HCI_TIMEOUT));
+		break;
+
+	case HCI_FM_GET_RECV_CONF_CMD:
+		ret = radio_hci_request(hdev, hci_get_fm_recv_conf_req, arg,
+			msecs_to_jiffies(RADIO_HCI_TIMEOUT));
+		break;
+
+	case HCI_FM_GET_STATION_PARAM_CMD:
+		ret = radio_hci_request(hdev,
+			hci_fm_get_station_param_req, arg,
+			msecs_to_jiffies(RADIO_HCI_TIMEOUT));
+		break;
+
+	case HCI_FM_GET_SIGNAL_TH_CMD:
+		ret = radio_hci_request(hdev,
+			hci_fm_get_sig_threshold_req, arg,
+			msecs_to_jiffies(RADIO_HCI_TIMEOUT));
+		break;
+
+	case HCI_FM_GET_PROGRAM_SERVICE_CMD:
+		ret = radio_hci_request(hdev,
+			hci_fm_get_program_service_req, arg,
+			msecs_to_jiffies(RADIO_HCI_TIMEOUT));
+		break;
+
+	case HCI_FM_GET_RADIO_TEXT_CMD:
+		ret = radio_hci_request(hdev, hci_fm_get_radio_text_req, arg,
+			msecs_to_jiffies(RADIO_HCI_TIMEOUT));
+		break;
+
+	case HCI_FM_GET_AF_LIST_CMD:
+		ret = radio_hci_request(hdev, hci_fm_get_af_list_req, arg,
+			msecs_to_jiffies(RADIO_HCI_TIMEOUT));
+		break;
+
+	case HCI_FM_CANCEL_SEARCH_CMD:
+		ret = radio_hci_request(hdev, hci_fm_cancel_search_req, arg,
+			msecs_to_jiffies(RADIO_HCI_TIMEOUT));
+		break;
+
+	case HCI_FM_RESET_CMD:
+		ret = radio_hci_request(hdev, hci_fm_reset_req, arg,
+			msecs_to_jiffies(RADIO_HCI_TIMEOUT));
+		break;
+
+	case HCI_FM_GET_FEATURES_CMD:
+		ret = radio_hci_request(hdev,
+		hci_fm_get_feature_lists_req, arg,
+			msecs_to_jiffies(RADIO_HCI_TIMEOUT));
+		break;
+
+	case HCI_FM_STATION_DBG_PARAM_CMD:
+		ret = radio_hci_request(hdev,
+		hci_fm_get_station_dbg_param_req, arg,
+			msecs_to_jiffies(RADIO_HCI_TIMEOUT));
+		break;
+
+	case HCI_FM_ENABLE_TRANS_CMD:
+		ret = radio_hci_request(hdev, hci_fm_enable_trans_req, arg,
+			msecs_to_jiffies(RADIO_HCI_TIMEOUT));
+		break;
+
+	case HCI_FM_DISABLE_TRANS_CMD:
+		ret = radio_hci_request(hdev, hci_fm_disable_trans_req, arg,
+			msecs_to_jiffies(RADIO_HCI_TIMEOUT));
+		break;
+
+	case HCI_FM_GET_TX_CONFIG:
+		ret = radio_hci_request(hdev, hci_get_fm_trans_conf_req, arg,
+			msecs_to_jiffies(RADIO_HCI_TIMEOUT));
+		break;
+	case HCI_FM_GET_DET_CH_TH_CMD:
+		ret = radio_hci_request(hdev, hci_fm_get_ch_det_th, arg,
+					msecs_to_jiffies(RADIO_HCI_TIMEOUT));
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static void radio_hci_req_complete(struct radio_hci_dev *hdev, int result)
+{
+	hdev->req_result = result;
+	hdev->req_status = HCI_REQ_DONE;
+	wake_up_interruptible(&hdev->req_wait_q);
+}
+
+static void radio_hci_status_complete(struct radio_hci_dev *hdev, int result)
+{
+	hdev->req_result = result;
+	hdev->req_status = HCI_REQ_STATUS;
+	wake_up_interruptible(&hdev->req_wait_q);
+}
+
+static void hci_cc_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb)
+{
+	__u8 status = *((__u8 *) skb->data);
+
+	if (status)
+		return;
+
+	radio_hci_req_complete(hdev, status);
+}
+
+static void hci_cc_fm_disable_rsp(struct radio_hci_dev *hdev,
+	struct sk_buff *skb)
+{
+	__u8 status = *((__u8 *) skb->data);
+	struct iris_device *radio = video_get_drvdata(video_get_dev());
+
+	if (status)
+		return;
+	if (radio->mode != FM_CALIB)
+		iris_q_event(radio, IRIS_EVT_RADIO_DISABLED);
+
+	radio_hci_req_complete(hdev, status);
+}
+
+static void hci_cc_conf_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb)
+{
+	struct hci_fm_conf_rsp  *rsp = (void *)skb->data;
+	struct iris_device *radio = video_get_drvdata(video_get_dev());
+
+	if (rsp->status)
+		return;
+
+	radio->recv_conf = rsp->recv_conf_rsp;
+	radio_hci_req_complete(hdev, rsp->status);
+}
+
+static void hci_cc_fm_trans_get_conf_rsp(struct radio_hci_dev *hdev,
+		struct sk_buff *skb)
+{
+	struct hci_fm_get_trans_conf_rsp  *rsp = (void *)skb->data;
+	struct iris_device *radio = video_get_drvdata(video_get_dev());
+
+	if (rsp->status)
+		return;
+	memcpy((void *)&radio->trans_conf,  (void*)&rsp->trans_conf_rsp,
+			sizeof(rsp->trans_conf_rsp));
+	radio_hci_req_complete(hdev, rsp->status);
+}
+
+static void hci_cc_fm_enable_rsp(struct radio_hci_dev *hdev,
+	struct sk_buff *skb)
+{
+	struct hci_fm_conf_rsp  *rsp = (void *)skb->data;
+	struct iris_device *radio = video_get_drvdata(video_get_dev());
+
+	if (rsp->status)
+		return;
+	if (radio->mode != FM_CALIB)
+		iris_q_event(radio, IRIS_EVT_RADIO_READY);
+
+	radio_hci_req_complete(hdev, rsp->status);
+}
+
+
+static void hci_cc_fm_trans_set_conf_rsp(struct radio_hci_dev *hdev,
+	struct sk_buff *skb)
+{
+	struct hci_fm_conf_rsp  *rsp = (void *)skb->data;
+	struct iris_device *radio = video_get_drvdata(video_get_dev());
+
+	if (rsp->status)
+		return;
+
+	iris_q_event(radio, HCI_EV_CMD_COMPLETE);
+
+	radio_hci_req_complete(hdev, rsp->status);
+}
+
+
+static void hci_cc_sig_threshold_rsp(struct radio_hci_dev *hdev,
+		struct sk_buff *skb)
+{
+	struct hci_fm_sig_threshold_rsp  *rsp = (void *)skb->data;
+	struct iris_device *radio = video_get_drvdata(video_get_dev());
+
+	if (rsp->status)
+		return;
+
+	memcpy(&radio->sig_th, rsp, sizeof(struct hci_fm_sig_threshold_rsp));
+	radio_hci_req_complete(hdev, rsp->status);
+}
+
+static void hci_cc_station_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb)
+{
+	struct iris_device *radio = video_get_drvdata(video_get_dev());
+	struct hci_fm_station_rsp *rsp = (void *)skb->data;
+	radio->fm_st_rsp = *(rsp);
+
+	/* Tune is always succesful */
+	radio_hci_req_complete(hdev, 0);
+}
+
+static void hci_cc_prg_srv_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb)
+{
+	struct hci_fm_prgm_srv_rsp  *rsp = (void *)skb->data;
+
+	if (rsp->status)
+		return;
+
+	radio_hci_req_complete(hdev, rsp->status);
+}
+
+static void hci_cc_rd_txt_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb)
+{
+	struct hci_fm_radio_txt_rsp  *rsp = (void *)skb->data;
+
+	if (rsp->status)
+		return;
+
+	radio_hci_req_complete(hdev, rsp->status);
+}
+
+static void hci_cc_af_list_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb)
+{
+	struct hci_fm_af_list_rsp  *rsp = (void *)skb->data;
+
+	if (rsp->status)
+		return;
+
+	radio_hci_req_complete(hdev, rsp->status);
+}
+
+static void hci_cc_feature_list_rsp(struct radio_hci_dev *hdev,
+	struct sk_buff *skb)
+{
+	struct hci_fm_feature_list_rsp  *rsp = (void *)skb->data;
+	struct iris_device *radio = video_get_drvdata(video_get_dev());
+	struct v4l2_capability *v4l_cap = radio->g_cap;
+
+	if (rsp->status)
+		return;
+	v4l_cap->capabilities = (rsp->feature_mask & 0x000002) |
+		(rsp->feature_mask & 0x000001);
+
+	radio_hci_req_complete(hdev, rsp->status);
+}
+
+static void hci_cc_dbg_param_rsp(struct radio_hci_dev *hdev,
+	struct sk_buff *skb)
+{
+	struct iris_device *radio = video_get_drvdata(video_get_dev());
+	struct hci_fm_dbg_param_rsp *rsp = (void *)skb->data;
+	radio->st_dbg_param = *(rsp);
+
+	if (radio->st_dbg_param.status)
+		return;
+
+	radio_hci_req_complete(hdev, radio->st_dbg_param.status);
+}
+
+static void iris_q_evt_data(struct iris_device *radio,
+				char *data, int len, int event)
+{
+	struct kfifo *data_b = &radio->data_buf[event];
+	if (kfifo_in_locked(data_b, data, len, &radio->buf_lock[event]))
+		wake_up_interruptible(&radio->event_queue);
+}
+
+static void hci_cc_riva_peek_rsp(struct radio_hci_dev *hdev,
+		struct sk_buff *skb)
+{
+	struct iris_device *radio = video_get_drvdata(video_get_dev());
+	__u8 status = *((__u8 *) skb->data);
+	int len;
+	char *data;
+
+	if (status)
+		return;
+	len = skb->data[RIVA_PEEK_LEN_OFSET] + RIVA_PEEK_PARAM;
+	data = kmalloc(len, GFP_ATOMIC);
+
+	if (!data) {
+		FMDERR("Memory allocation failed");
+		return;
+	}
+
+	memcpy(data, &skb->data[PEEK_DATA_OFSET], len);
+	iris_q_evt_data(radio, data, len, IRIS_BUF_PEEK);
+	radio_hci_req_complete(hdev, status);
+	kfree(data);
+
+}
+
+static void hci_cc_riva_read_default_rsp(struct radio_hci_dev *hdev,
+		struct sk_buff *skb)
+{
+	struct iris_device *radio = video_get_drvdata(video_get_dev());
+	__u8 status = *((__u8 *) skb->data);
+	__u8 len;
+
+	if (status)
+		return;
+	len = skb->data[1];
+
+	memset(&radio->default_data, 0 , sizeof(struct hci_fm_data_rd_rsp));
+	memcpy(&radio->default_data, &skb->data[0], len+2);
+	iris_q_evt_data(radio, &skb->data[0], len+2, IRIS_BUF_RD_DEFAULT);
+	radio_hci_req_complete(hdev, status);
+}
+
+static void hci_cc_ssbi_peek_rsp(struct radio_hci_dev *hdev,
+		struct sk_buff *skb)
+{
+	struct iris_device *radio = video_get_drvdata(video_get_dev());
+	__u8 status = *((__u8 *) skb->data);
+	char *data;
+
+	if (status)
+		return;
+	data = kmalloc(SSBI_PEEK_LEN, GFP_ATOMIC);
+	if (!data) {
+		FMDERR("Memory allocation failed");
+		return;
+	}
+
+	data[0] = skb->data[PEEK_DATA_OFSET];
+	iris_q_evt_data(radio, data, SSBI_PEEK_LEN, IRIS_BUF_SSBI_PEEK);
+	radio_hci_req_complete(hdev, status);
+	kfree(data);
+}
+
+static void hci_cc_rds_grp_cntrs_rsp(struct radio_hci_dev *hdev,
+		struct sk_buff *skb)
+{
+	struct iris_device *radio = video_get_drvdata(video_get_dev());
+	__u8 status = *((__u8 *) skb->data);
+	char *data;
+	if (status)
+		return;
+	data = kmalloc(RDS_GRP_CNTR_LEN, GFP_ATOMIC);
+	if (!data) {
+		FMDERR("memory allocation failed");
+		return;
+	}
+	memcpy(data, &skb->data[1], RDS_GRP_CNTR_LEN);
+	iris_q_evt_data(radio, data, RDS_GRP_CNTR_LEN, IRIS_BUF_RDS_CNTRS);
+	radio_hci_req_complete(hdev, status);
+	kfree(data);
+
+}
+
+static void hci_cc_do_calibration_rsp(struct radio_hci_dev *hdev,
+		struct sk_buff *skb)
+{
+	struct iris_device *radio = video_get_drvdata(video_get_dev());
+	static struct hci_cc_do_calibration_rsp rsp ;
+	rsp.status = skb->data[0];
+	rsp.mode = skb->data[CALIB_MODE_OFSET];
+
+	if (rsp.status) {
+		FMDERR("status = %d", rsp.status);
+		return;
+	}
+	if (rsp.mode == PROCS_CALIB_MODE) {
+		memcpy(&rsp.data[0], &skb->data[CALIB_DATA_OFSET],
+				PROCS_CALIB_SIZE);
+	iris_q_evt_data(radio, rsp.data, PROCS_CALIB_SIZE,
+					IRIS_BUF_CAL_DATA);
+	} else {
+		return;
+	}
+
+	radio_hci_req_complete(hdev, rsp.status);
+}
+
+static void hci_cc_get_ch_det_threshold_rsp(struct radio_hci_dev *hdev,
+		struct sk_buff *skb)
+{
+	struct iris_device *radio = video_get_drvdata(video_get_dev());
+	u8  status = skb->data[0];
+	if (status) {
+		FMDERR("status = %d", status);
+		return;
+	}
+	memcpy(&radio->ch_det_threshold, &skb->data[1],
+		sizeof(struct hci_fm_ch_det_threshold));
+	radio_hci_req_complete(hdev, status);
+}
+
+static inline void hci_cmd_complete_event(struct radio_hci_dev *hdev,
+		struct sk_buff *skb)
+{
+	struct hci_ev_cmd_complete *cmd_compl_ev = (void *) skb->data;
+	__u16 opcode;
+
+	skb_pull(skb, sizeof(*cmd_compl_ev));
+
+	opcode = __le16_to_cpu(cmd_compl_ev->cmd_opcode);
+
+	switch (opcode) {
+	case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_ENABLE_RECV_REQ):
+	case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_ENABLE_TRANS_REQ):
+		hci_cc_fm_enable_rsp(hdev, skb);
+		break;
+	case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_RECV_CONF_REQ):
+		hci_cc_conf_rsp(hdev, skb);
+		break;
+
+	case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_DISABLE_RECV_REQ):
+	case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_DISABLE_TRANS_REQ):
+		hci_cc_fm_disable_rsp(hdev, skb);
+		break;
+
+	case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_RECV_CONF_REQ):
+	case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_MUTE_MODE_REQ):
+	case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_STEREO_MODE_REQ):
+	case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_ANTENNA):
+	case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_SIGNAL_THRESHOLD):
+	case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_CANCEL_SEARCH):
+	case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_RDS_GRP):
+	case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_RDS_GRP_PROCESS):
+	case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_EN_WAN_AVD_CTRL):
+	case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_EN_NOTCH_CTRL):
+	case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_CH_DET_THRESHOLD):
+	case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_RDS_RT_REQ):
+	case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_RDS_PS_REQ):
+	case hci_common_cmd_op_pack(HCI_OCF_FM_DEFAULT_DATA_WRITE):
+		hci_cc_rsp(hdev, skb);
+		break;
+	case hci_common_cmd_op_pack(HCI_OCF_FM_RESET):
+	case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_SSBI_POKE_REG):
+	case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_POKE_DATA):
+	case hci_diagnostic_cmd_op_pack(HCI_FM_SET_INTERNAL_TONE_GENRATOR):
+	case hci_common_cmd_op_pack(HCI_OCF_FM_SET_CALIBRATION):
+	case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_EVENT_MASK):
+		hci_cc_rsp(hdev, skb);
+		break;
+
+	case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_SSBI_PEEK_REG):
+		hci_cc_ssbi_peek_rsp(hdev, skb);
+		break;
+	case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_SIGNAL_THRESHOLD):
+		hci_cc_sig_threshold_rsp(hdev, skb);
+		break;
+
+	case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_STATION_PARAM_REQ):
+		hci_cc_station_rsp(hdev, skb);
+		break;
+
+	case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_PROGRAM_SERVICE_REQ):
+		hci_cc_prg_srv_rsp(hdev, skb);
+		break;
+
+	case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_RADIO_TEXT_REQ):
+		hci_cc_rd_txt_rsp(hdev, skb);
+		break;
+
+	case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_AF_LIST_REQ):
+		hci_cc_af_list_rsp(hdev, skb);
+		break;
+
+	case hci_common_cmd_op_pack(HCI_OCF_FM_DEFAULT_DATA_READ):
+		hci_cc_riva_read_default_rsp(hdev, skb);
+		break;
+
+	case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_PEEK_DATA):
+		hci_cc_riva_peek_rsp(hdev, skb);
+		break;
+
+	case hci_common_cmd_op_pack(HCI_OCF_FM_GET_FEATURE_LIST):
+		hci_cc_feature_list_rsp(hdev, skb);
+		break;
+
+	case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_STATION_DBG_PARAM):
+		hci_cc_dbg_param_rsp(hdev, skb);
+		break;
+	case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_SET_TRANS_CONF_REQ):
+		hci_cc_fm_trans_set_conf_rsp(hdev, skb);
+		break;
+
+	case hci_status_param_op_pack(HCI_OCF_FM_READ_GRP_COUNTERS):
+		hci_cc_rds_grp_cntrs_rsp(hdev, skb);
+		break;
+	case hci_common_cmd_op_pack(HCI_OCF_FM_DO_CALIBRATION):
+		hci_cc_do_calibration_rsp(hdev, skb);
+		break;
+
+	case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_GET_TRANS_CONF_REQ):
+		hci_cc_fm_trans_get_conf_rsp(hdev, skb);
+		break;
+	case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_CH_DET_THRESHOLD):
+		hci_cc_get_ch_det_threshold_rsp(hdev, skb);
+		break;
+	default:
+		FMDERR("%s opcode 0x%x", hdev->name, opcode);
+		break;
+	}
+
+}
+
+static inline void hci_cmd_status_event(struct radio_hci_dev *hdev,
+		struct sk_buff *skb)
+{
+	struct hci_ev_cmd_status *ev = (void *) skb->data;
+	radio_hci_status_complete(hdev, ev->status);
+}
+
+static inline void hci_ev_tune_status(struct radio_hci_dev *hdev,
+		struct sk_buff *skb)
+{
+	int i;
+	struct iris_device *radio = video_get_drvdata(video_get_dev());
+
+	memcpy(&radio->fm_st_rsp.station_rsp, &skb->data[0],
+				sizeof(struct hci_ev_tune_status));
+	iris_q_event(radio, IRIS_EVT_TUNE_SUCC);
+
+	for (i = 0; i < IRIS_BUF_MAX; i++) {
+		if (i >= IRIS_BUF_RT_RDS)
+			kfifo_reset(&radio->data_buf[i]);
+	}
+	if (radio->fm_st_rsp.station_rsp.serv_avble)
+		iris_q_event(radio, IRIS_EVT_ABOVE_TH);
+	else
+		iris_q_event(radio, IRIS_EVT_BELOW_TH);
+
+	if (radio->fm_st_rsp.station_rsp.stereo_prg)
+		iris_q_event(radio, IRIS_EVT_STEREO);
+
+	if (radio->fm_st_rsp.station_rsp.mute_mode)
+		iris_q_event(radio, IRIS_EVT_MONO);
+
+	if (radio->fm_st_rsp.station_rsp.rds_sync_status)
+		iris_q_event(radio, IRIS_EVT_RDS_AVAIL);
+	else
+		iris_q_event(radio, IRIS_EVT_RDS_NOT_AVAIL);
+}
+
+static inline void hci_ev_search_compl(struct radio_hci_dev *hdev,
+		struct sk_buff *skb)
+{
+	struct iris_device *radio = video_get_drvdata(video_get_dev());
+	iris_q_event(radio, IRIS_EVT_SEEK_COMPLETE);
+}
+
+static inline void hci_ev_srch_st_list_compl(struct radio_hci_dev *hdev,
+		struct sk_buff *skb)
+{
+	struct iris_device *radio = video_get_drvdata(video_get_dev());
+	struct hci_ev_srch_list_compl *ev ;
+	int cnt;
+	int stn_num;
+	int rel_freq;
+	int abs_freq;
+	int len;
+
+	ev = kmalloc(sizeof(*ev), GFP_ATOMIC);
+	if (!ev) {
+		FMDERR("Memory allocation failed");
+		return ;
+	}
+
+	ev->num_stations_found = skb->data[STN_NUM_OFFSET];
+	len = ev->num_stations_found * PARAMS_PER_STATION + STN_FREQ_OFFSET;
+
+	for (cnt = STN_FREQ_OFFSET, stn_num = 0;
+		(cnt < len) && (stn_num < ev->num_stations_found)
+		&& (stn_num < ARRAY_SIZE(ev->rel_freq));
+		cnt += PARAMS_PER_STATION, stn_num++) {
+		abs_freq = *((int *)&skb->data[cnt]);
+		rel_freq = abs_freq - radio->recv_conf.band_low_limit;
+		rel_freq = (rel_freq * 20) / KHZ_TO_MHZ;
+
+		ev->rel_freq[stn_num].rel_freq_lsb = GET_LSB(rel_freq);
+		ev->rel_freq[stn_num].rel_freq_msb = GET_MSB(rel_freq);
+	}
+
+	len = ev->num_stations_found * 2 + sizeof(ev->num_stations_found);
+	iris_q_event(radio, IRIS_EVT_NEW_SRCH_LIST);
+	iris_q_evt_data(radio, (char *)ev, len, IRIS_BUF_SRCH_LIST);
+	kfree(ev);
+}
+
+static inline void hci_ev_search_next(struct radio_hci_dev *hdev,
+		struct sk_buff *skb)
+{
+	struct iris_device *radio = video_get_drvdata(video_get_dev());
+	iris_q_event(radio, IRIS_EVT_SCAN_NEXT);
+}
+
+static inline void hci_ev_stereo_status(struct radio_hci_dev *hdev,
+		struct sk_buff *skb)
+{
+	struct iris_device *radio = video_get_drvdata(video_get_dev());
+	__u8 st_status = *((__u8 *) skb->data);
+	if (st_status)
+		iris_q_event(radio, IRIS_EVT_STEREO);
+	else
+		iris_q_event(radio, IRIS_EVT_MONO);
+}
+
+
+static inline void hci_ev_program_service(struct radio_hci_dev *hdev,
+		struct sk_buff *skb)
+{
+	struct iris_device *radio = video_get_drvdata(video_get_dev());
+	int len;
+	char *data;
+
+	len = (skb->data[RDS_PS_LENGTH_OFFSET] * RDS_STRING) + RDS_OFFSET;
+	iris_q_event(radio, IRIS_EVT_NEW_PS_RDS);
+	data = kmalloc(len, GFP_ATOMIC);
+	if (!data) {
+		FMDERR("Failed to allocate memory");
+		return;
+	}
+
+	data[0] = skb->data[RDS_PS_LENGTH_OFFSET];
+	data[1] = skb->data[RDS_PTYPE];
+	data[2] = skb->data[RDS_PID_LOWER];
+	data[3] = skb->data[RDS_PID_HIGHER];
+	data[4] = 0;
+
+	memcpy(data+RDS_OFFSET, &skb->data[RDS_PS_DATA_OFFSET], len-RDS_OFFSET);
+
+	iris_q_evt_data(radio, data, len, IRIS_BUF_PS_RDS);
+
+	kfree(data);
+}
+
+
+static inline void hci_ev_radio_text(struct radio_hci_dev *hdev,
+	struct sk_buff *skb)
+{
+	struct iris_device *radio = video_get_drvdata(video_get_dev());
+	int len = 0;
+	char *data;
+
+	iris_q_event(radio, IRIS_EVT_NEW_RT_RDS);
+
+	while ((skb->data[len+RDS_OFFSET] != 0x0d) && (len < RX_RT_DATA_LENGTH))
+		len++;
+	data = kmalloc(len+RDS_OFFSET, GFP_ATOMIC);
+	if (!data) {
+		FMDERR("Failed to allocate memory");
+		return;
+	}
+
+	data[0] = len;
+	data[1] = skb->data[RDS_PTYPE];
+	data[2] = skb->data[RDS_PID_LOWER];
+	data[3] = skb->data[RDS_PID_HIGHER];
+	data[4] = 0;
+
+	memcpy(data+RDS_OFFSET, &skb->data[RDS_OFFSET], len);
+	data[len+RDS_OFFSET] = 0x00;
+
+	iris_q_evt_data(radio, data, len+RDS_OFFSET, IRIS_BUF_RT_RDS);
+
+	kfree(data);
+}
+
+static void hci_ev_af_list(struct radio_hci_dev *hdev,
+	struct sk_buff *skb)
+{
+	struct iris_device *radio = video_get_drvdata(video_get_dev());
+	struct hci_ev_af_list ev;
+
+	ev.tune_freq = *((int *) &skb->data[0]);
+	ev.pi_code = *((__le16 *) &skb->data[PI_CODE_OFFSET]);
+	ev.af_size = skb->data[AF_SIZE_OFFSET];
+	memcpy(&ev.af_list[0], &skb->data[AF_LIST_OFFSET], ev.af_size);
+	iris_q_event(radio, IRIS_EVT_NEW_AF_LIST);
+	iris_q_evt_data(radio, (char *)&ev, sizeof(ev), IRIS_BUF_AF_LIST);
+}
+
+static void hci_ev_rds_lock_status(struct radio_hci_dev *hdev,
+	struct sk_buff *skb)
+{
+	struct iris_device *radio = video_get_drvdata(video_get_dev());
+	__u8 rds_status = skb->data[0];
+
+	if (rds_status)
+		iris_q_event(radio, IRIS_EVT_RDS_AVAIL);
+	else
+		iris_q_event(radio, IRIS_EVT_RDS_NOT_AVAIL);
+}
+
+static void hci_ev_service_available(struct radio_hci_dev *hdev,
+	struct sk_buff *skb)
+{
+	struct iris_device *radio = video_get_drvdata(video_get_dev());
+	u8 serv_avble = skb->data[0];
+	if (serv_avble)
+		iris_q_event(radio, IRIS_EVT_ABOVE_TH);
+	else
+		iris_q_event(radio, IRIS_EVT_BELOW_TH);
+}
+
+static void hci_ev_rds_grp_complete(struct radio_hci_dev *hdev,
+	struct sk_buff *skb)
+{
+	struct iris_device *radio = video_get_drvdata(video_get_dev());
+	iris_q_event(radio, IRIS_EVT_TXRDSDONE);
+}
+
+void radio_hci_event_packet(struct radio_hci_dev *hdev, struct sk_buff *skb)
+{
+	struct radio_hci_event_hdr *hdr;
+	u8 event;
+
+	if (skb == NULL) {
+		FMDERR("Socket buffer is NULL");
+		return;
+	}
+
+	hdr = (void *) skb->data;
+	event = hdr->evt;
+
+	skb_pull(skb, RADIO_HCI_EVENT_HDR_SIZE);
+
+	switch (event) {
+	case HCI_EV_TUNE_STATUS:
+		hci_ev_tune_status(hdev, skb);
+		break;
+	case HCI_EV_SEARCH_PROGRESS:
+	case HCI_EV_SEARCH_RDS_PROGRESS:
+	case HCI_EV_SEARCH_LIST_PROGRESS:
+		hci_ev_search_next(hdev, skb);
+		break;
+	case HCI_EV_STEREO_STATUS:
+		hci_ev_stereo_status(hdev, skb);
+		break;
+	case HCI_EV_RDS_LOCK_STATUS:
+		hci_ev_rds_lock_status(hdev, skb);
+		break;
+	case HCI_EV_SERVICE_AVAILABLE:
+		hci_ev_service_available(hdev, skb);
+		break;
+	case HCI_EV_RDS_RX_DATA:
+		break;
+	case HCI_EV_PROGRAM_SERVICE:
+		hci_ev_program_service(hdev, skb);
+		break;
+	case HCI_EV_RADIO_TEXT:
+		hci_ev_radio_text(hdev, skb);
+		break;
+	case HCI_EV_FM_AF_LIST:
+		hci_ev_af_list(hdev, skb);
+		break;
+	case HCI_EV_TX_RDS_GRP_COMPL:
+		hci_ev_rds_grp_complete(hdev, skb);
+		break;
+	case HCI_EV_TX_RDS_CONT_GRP_COMPL:
+		break;
+
+	case HCI_EV_CMD_COMPLETE:
+		hci_cmd_complete_event(hdev, skb);
+		break;
+
+	case HCI_EV_CMD_STATUS:
+		hci_cmd_status_event(hdev, skb);
+		break;
+
+	case HCI_EV_SEARCH_COMPLETE:
+	case HCI_EV_SEARCH_RDS_COMPLETE:
+		hci_ev_search_compl(hdev, skb);
+		break;
+
+	case HCI_EV_SEARCH_LIST_COMPLETE:
+		hci_ev_srch_st_list_compl(hdev, skb);
+		break;
+
+	default:
+		break;
+	}
+}
+
+/*
+ * fops/IOCTL helper functions
+ */
+
+static int iris_search(struct iris_device *radio, int on, int dir)
+{
+	int retval = 0;
+	enum search_t srch = radio->g_search_mode & SRCH_MODE;
+	radio->search_on = on;
+
+	if (on) {
+		switch (srch) {
+		case SCAN_FOR_STRONG:
+		case SCAN_FOR_WEAK:
+			radio->srch_st_list.srch_list_dir = dir;
+			radio->srch_st_list.srch_list_mode = srch;
+			radio->srch_st_list.srch_list_max = 0;
+			retval = hci_fm_search_station_list(
+				&radio->srch_st_list, radio->fm_hdev);
+			break;
+		case RDS_SEEK_PTY:
+		case RDS_SCAN_PTY:
+		case RDS_SEEK_PI:
+			srch = srch - SEARCH_RDS_STNS_MODE_OFFSET;
+			radio->srch_rds.srch_station.srch_mode = srch;
+			radio->srch_rds.srch_station.srch_dir = dir;
+			radio->srch_rds.srch_station.scan_time =
+				radio->g_scan_time;
+			retval = hci_fm_search_rds_stations(&radio->srch_rds,
+				radio->fm_hdev);
+			break;
+		default:
+			radio->srch_st.srch_mode = srch;
+			radio->srch_st.scan_time = radio->g_scan_time;
+			radio->srch_st.srch_dir = dir;
+			retval = hci_fm_search_stations(
+				&radio->srch_st, radio->fm_hdev);
+			break;
+		}
+
+	} else {
+		retval = hci_cmd(HCI_FM_CANCEL_SEARCH_CMD, radio->fm_hdev);
+	}
+
+	return retval;
+}
+
+static int set_low_power_mode(struct iris_device *radio, int power_mode)
+{
+
+	int rds_grps_proc = 0x00;
+	int retval = 0;
+	if (radio->power_mode != power_mode) {
+
+		if (power_mode) {
+			radio->event_mask = 0x00;
+			if (radio->af_jump_bit)
+				rds_grps_proc = 0x00 | AF_JUMP_ENABLE;
+			else
+				rds_grps_proc = 0x00;
+			retval = hci_fm_rds_grps_process(
+				&rds_grps_proc,
+				radio->fm_hdev);
+			if (retval < 0) {
+				FMDERR("Disable RDS failed");
+				return retval;
+			}
+			retval = hci_conf_event_mask(&radio->event_mask,
+				radio->fm_hdev);
+		} else {
+
+			radio->event_mask = SIG_LEVEL_INTR |
+					RDS_SYNC_INTR | AUDIO_CTRL_INTR;
+			retval = hci_conf_event_mask(&radio->event_mask,
+				radio->fm_hdev);
+			if (retval < 0) {
+				FMDERR("Enable Async events failed");
+				return retval;
+			}
+			retval = hci_fm_rds_grps_process(
+				&radio->g_rds_grp_proc_ps,
+				radio->fm_hdev);
+		}
+		radio->power_mode = power_mode;
+	}
+	return retval;
+}
+static int iris_recv_set_region(struct iris_device *radio, int req_region)
+{
+	int retval;
+	radio->region = req_region;
+
+	switch (radio->region) {
+	case IRIS_REGION_US:
+		radio->recv_conf.band_low_limit =
+			REGION_US_EU_BAND_LOW;
+		radio->recv_conf.band_high_limit =
+			REGION_US_EU_BAND_HIGH;
+		break;
+	case IRIS_REGION_EU:
+		radio->recv_conf.band_low_limit =
+			REGION_US_EU_BAND_LOW;
+		radio->recv_conf.band_high_limit =
+			REGION_US_EU_BAND_HIGH;
+		break;
+	case IRIS_REGION_JAPAN:
+		radio->recv_conf.band_low_limit =
+			REGION_JAPAN_STANDARD_BAND_LOW;
+		radio->recv_conf.band_high_limit =
+			REGION_JAPAN_STANDARD_BAND_HIGH;
+		break;
+	case IRIS_REGION_JAPAN_WIDE:
+		radio->recv_conf.band_low_limit =
+			REGION_JAPAN_WIDE_BAND_LOW;
+		radio->recv_conf.band_high_limit =
+			REGION_JAPAN_WIDE_BAND_HIGH;
+		break;
+	default:
+		/* The user specifies the value.
+		   So nothing needs to be done */
+		break;
+	}
+
+	retval = hci_set_fm_recv_conf(
+			&radio->recv_conf,
+			radio->fm_hdev);
+
+	return retval;
+}
+
+
+static int iris_trans_set_region(struct iris_device *radio, int req_region)
+{
+	int retval;
+	radio->region = req_region;
+
+	switch (radio->region) {
+	case IRIS_REGION_US:
+		radio->trans_conf.band_low_limit =
+			REGION_US_EU_BAND_LOW;
+		radio->trans_conf.band_high_limit =
+			REGION_US_EU_BAND_HIGH;
+		break;
+	case IRIS_REGION_EU:
+		radio->trans_conf.band_low_limit =
+			REGION_US_EU_BAND_LOW;
+		radio->trans_conf.band_high_limit =
+			REGION_US_EU_BAND_HIGH;
+		break;
+	case IRIS_REGION_JAPAN:
+		radio->trans_conf.band_low_limit =
+			REGION_JAPAN_STANDARD_BAND_LOW;
+		radio->trans_conf.band_high_limit =
+			REGION_JAPAN_STANDARD_BAND_HIGH;
+		break;
+	case IRIS_REGION_JAPAN_WIDE:
+		radio->recv_conf.band_low_limit =
+			REGION_JAPAN_WIDE_BAND_LOW;
+		radio->recv_conf.band_high_limit =
+			REGION_JAPAN_WIDE_BAND_HIGH;
+	default:
+		break;
+	}
+
+	retval = hci_set_fm_trans_conf(
+			&radio->trans_conf,
+				radio->fm_hdev);
+	return retval;
+}
+
+
+static int iris_set_freq(struct iris_device *radio, unsigned int freq)
+{
+
+	int retval;
+	retval = hci_fm_tune_station(&freq, radio->fm_hdev);
+	if (retval < 0)
+		FMDERR("Error while setting the frequency : %d\n", retval);
+	return retval;
+}
+
+
+static int iris_vidioc_queryctrl(struct file *file, void *priv,
+		struct v4l2_queryctrl *qc)
+{
+	unsigned char i;
+	int retval = -EINVAL;
+
+	for (i = 0; i < ARRAY_SIZE(iris_v4l2_queryctrl); i++) {
+		if (qc->id && qc->id == iris_v4l2_queryctrl[i].id) {
+			memcpy(qc, &(iris_v4l2_queryctrl[i]), sizeof(*qc));
+			retval = 0;
+			break;
+		}
+	}
+
+	return retval;
+}
+
+static int iris_do_calibration(struct iris_device *radio)
+{
+	char cal_mode = 0x00;
+	int retval = 0x00;
+
+	cal_mode = PROCS_CALIB_MODE;
+	radio->mode = FM_CALIB;
+	retval = hci_cmd(HCI_FM_ENABLE_RECV_CMD,
+			radio->fm_hdev);
+	if (retval < 0) {
+		FMDERR("Enable failed before calibration %x", retval);
+		radio->mode = FM_OFF;
+		return retval;
+	}
+	retval = radio_hci_request(radio->fm_hdev, hci_fm_do_cal_req,
+		(unsigned long)cal_mode, RADIO_HCI_TIMEOUT);
+	if (retval < 0) {
+		FMDERR("Do Process calibration failed %x", retval);
+		radio->mode = FM_RECV;
+		return retval;
+	}
+	retval = hci_cmd(HCI_FM_DISABLE_RECV_CMD,
+			radio->fm_hdev);
+	if (retval < 0)
+		FMDERR("Disable Failed after calibration %d", retval);
+	radio->mode = FM_OFF;
+	return retval;
+}
+static int iris_vidioc_g_ctrl(struct file *file, void *priv,
+		struct v4l2_control *ctrl)
+{
+	struct iris_device *radio = video_get_drvdata(video_devdata(file));
+	int retval = 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_VOLUME:
+		break;
+	case V4L2_CID_AUDIO_MUTE:
+		ctrl->value = radio->mute_mode.hard_mute;
+		break;
+	case V4L2_CID_PRIVATE_IRIS_SRCHMODE:
+		ctrl->value = radio->g_search_mode;
+		break;
+	case V4L2_CID_PRIVATE_IRIS_SCANDWELL:
+		ctrl->value = radio->g_scan_time;
+		break;
+	case V4L2_CID_PRIVATE_IRIS_SRCHON:
+		ctrl->value = radio->search_on;
+		break;
+	case V4L2_CID_PRIVATE_IRIS_STATE:
+		ctrl->value = radio->mode;
+		break;
+	case V4L2_CID_PRIVATE_IRIS_IOVERC:
+		retval = hci_cmd(HCI_FM_STATION_DBG_PARAM_CMD, radio->fm_hdev);
+		if (retval < 0)
+			return retval;
+		ctrl->value = radio->st_dbg_param.io_verc;
+		break;
+	case V4L2_CID_PRIVATE_IRIS_INTDET:
+		retval = hci_cmd(HCI_FM_STATION_DBG_PARAM_CMD, radio->fm_hdev);
+		if (retval < 0)
+			return retval;
+		ctrl->value = radio->st_dbg_param.in_det_out;
+		break;
+	case V4L2_CID_PRIVATE_IRIS_REGION:
+		ctrl->value = radio->region;
+		break;
+	case V4L2_CID_PRIVATE_IRIS_SIGNAL_TH:
+		retval = hci_cmd(HCI_FM_GET_SIGNAL_TH_CMD, radio->fm_hdev);
+		if (retval < 0) {
+			FMDERR("Error in get signal threshold %d\n", retval);
+			return retval;
+		}
+		ctrl->value = radio->sig_th.sig_threshold;
+		break;
+	case V4L2_CID_PRIVATE_IRIS_SRCH_PTY:
+		ctrl->value = radio->srch_rds.srch_pty;
+		break;
+	case V4L2_CID_PRIVATE_IRIS_SRCH_PI:
+		ctrl->value = radio->srch_rds.srch_pi;
+		break;
+	case V4L2_CID_PRIVATE_IRIS_SRCH_CNT:
+		ctrl->value = radio->srch_st_result.num_stations_found;
+		break;
+	case V4L2_CID_PRIVATE_IRIS_EMPHASIS:
+		if (radio->mode == FM_RECV) {
+			ctrl->value = radio->recv_conf.emphasis;
+		} else if (radio->mode == FM_TRANS) {
+			ctrl->value = radio->trans_conf.emphasis;
+		} else {
+			FMDERR("Error in radio mode"
+				" %d\n", retval);
+			return -EINVAL;
+		}
+		break;
+	case V4L2_CID_PRIVATE_IRIS_RDS_STD:
+		if (radio->mode == FM_RECV) {
+			ctrl->value = radio->recv_conf.rds_std;
+		} else if (radio->mode == FM_TRANS) {
+			ctrl->value = radio->trans_conf.rds_std;
+		} else {
+			FMDERR("Error in radio mode"
+				" %d\n", retval);
+			return -EINVAL;
+		}
+		break;
+	case V4L2_CID_PRIVATE_IRIS_SPACING:
+		if (radio->mode == FM_RECV) {
+			ctrl->value = radio->recv_conf.ch_spacing;
+		} else {
+			FMDERR("Error in radio mode"
+				" %d\n", retval);
+			return -EINVAL;
+		}
+		break;
+	case V4L2_CID_PRIVATE_IRIS_RDSON:
+		if (radio->mode == FM_RECV) {
+			ctrl->value = radio->recv_conf.rds_std;
+		} else {
+			FMDERR("Error in radio mode"
+				" %d\n", retval);
+			return -EINVAL;
+		}
+		break;
+	case V4L2_CID_PRIVATE_IRIS_RDSGROUP_MASK:
+		ctrl->value = radio->rds_grp.rds_grp_enable_mask;
+		break;
+	case V4L2_CID_PRIVATE_IRIS_RDSGROUP_PROC:
+	case V4L2_CID_PRIVATE_IRIS_PSALL:
+		ctrl->value = (radio->g_rds_grp_proc_ps << RDS_CONFIG_OFFSET);
+		break;
+	case V4L2_CID_PRIVATE_IRIS_RDSD_BUF:
+		ctrl->value = radio->rds_grp.rds_buf_size;
+		break;
+	case V4L2_CID_PRIVATE_IRIS_LP_MODE:
+		ctrl->value = radio->power_mode;
+		break;
+	case V4L2_CID_PRIVATE_IRIS_ANTENNA:
+		ctrl->value = radio->g_antenna;
+		break;
+	case V4L2_CID_PRIVATE_IRIS_SOFT_MUTE:
+		ctrl->value = radio->mute_mode.soft_mute;
+		break;
+	case V4L2_CID_PRIVATE_IRIS_DO_CALIBRATION:
+		retval = iris_do_calibration(radio);
+		break;
+	case V4L2_CID_PRIVATE_IRIS_GET_SINR:
+		if (radio->mode == FM_RECV) {
+			retval = hci_cmd(HCI_FM_GET_STATION_PARAM_CMD,
+						 radio->fm_hdev);
+			if (retval < 0) {
+				FMDERR("Get SINR Failed");
+				return retval;
+			}
+			ctrl->value = radio->fm_st_rsp.station_rsp.sinr;
+
+		} else
+			retval = -EINVAL;
+		break;
+	case V4L2_CID_PRIVATE_INTF_HIGH_THRESHOLD:
+		retval = hci_cmd(HCI_FM_GET_DET_CH_TH_CMD, radio->fm_hdev);
+		if (retval < 0) {
+			FMDERR("Get High det threshold failed %x", retval);
+			return retval;
+		}
+		ctrl->value = radio->ch_det_threshold.high_th;
+		break;
+	case V4L2_CID_PRIVATE_INTF_LOW_THRESHOLD:
+		retval = hci_cmd(HCI_FM_GET_DET_CH_TH_CMD, radio->fm_hdev);
+		if (retval < 0) {
+			FMDERR("Get Low det threshold failed %x", retval);
+			return retval;
+		}
+		ctrl->value = radio->ch_det_threshold.low_th;
+		break;
+	case V4L2_CID_PRIVATE_SINR_THRESHOLD:
+		retval = hci_cmd(HCI_FM_GET_DET_CH_TH_CMD, radio->fm_hdev);
+		if (retval < 0) {
+			FMDERR("Get SINR threshold failed %x", retval);
+			return retval;
+		}
+		ctrl->value = radio->ch_det_threshold.sinr;
+		break;
+	case V4L2_CID_PRIVATE_SINR_SAMPLES:
+		retval = hci_cmd(HCI_FM_GET_DET_CH_TH_CMD, radio->fm_hdev);
+		if (retval < 0) {
+			FMDERR("Get SINR samples failed %x", retval);
+			return retval;
+		}
+
+		ctrl->value = radio->ch_det_threshold.sinr_samples;
+		break;
+	default:
+		retval = -EINVAL;
+	}
+	if (retval < 0)
+		FMDERR("get control failed with %d, id: %d\n",
+			retval, ctrl->id);
+	return retval;
+}
+
+static int iris_vidioc_g_ext_ctrls(struct file *file, void *priv,
+			struct v4l2_ext_controls *ctrl)
+{
+	int retval = 0;
+	char *data = NULL;
+	struct iris_device *radio = video_get_drvdata(video_devdata(file));
+	struct hci_fm_def_data_rd_req default_data_rd;
+
+	switch ((ctrl->controls[0]).id) {
+	case V4L2_CID_PRIVATE_IRIS_READ_DEFAULT:
+		data = (ctrl->controls[0]).string;
+		memset(&default_data_rd, 0, sizeof(default_data_rd));
+		if (copy_from_user(&default_data_rd.mode, data,
+					sizeof(default_data_rd)))
+			return -EFAULT;
+		retval = hci_def_data_read(&default_data_rd, radio->fm_hdev);
+		break;
+	default:
+		retval = -EINVAL;
+	}
+
+	return retval;
+}
+
+static int iris_vidioc_s_ext_ctrls(struct file *file, void *priv,
+			struct v4l2_ext_controls *ctrl)
+{
+	int retval = 0;
+	int bytes_to_copy;
+	struct hci_fm_tx_ps tx_ps;
+	struct hci_fm_tx_rt tx_rt;
+	struct hci_fm_def_data_wr_req default_data;
+	struct hci_fm_set_cal_req_proc proc_cal_req;
+
+	struct iris_device *radio = video_get_drvdata(video_devdata(file));
+	char *data = NULL;
+
+	switch ((ctrl->controls[0]).id) {
+	case V4L2_CID_RDS_TX_PS_NAME:
+		FMDBG("In V4L2_CID_RDS_TX_PS_NAME\n");
+		/*Pass a sample PS string */
+
+		memset(tx_ps.ps_data, 0, MAX_PS_LENGTH);
+		bytes_to_copy = min((int)(ctrl->controls[0]).size,
+			MAX_PS_LENGTH);
+		data = (ctrl->controls[0]).string;
+
+		if (copy_from_user(tx_ps.ps_data,
+				data, bytes_to_copy))
+				return -EFAULT;
+		tx_ps.ps_control =  0x01;
+		tx_ps.pi = radio->pi;
+		tx_ps.pty = radio->pty;
+		tx_ps.ps_repeatcount = radio->ps_repeatcount;
+		tx_ps.ps_len = bytes_to_copy;
+
+		retval = radio_hci_request(radio->fm_hdev, hci_trans_ps_req,
+				(unsigned long)&tx_ps, RADIO_HCI_TIMEOUT);
+		break;
+	case V4L2_CID_RDS_TX_RADIO_TEXT:
+		bytes_to_copy =
+		    min((int)(ctrl->controls[0]).size, MAX_RT_LENGTH);
+		data = (ctrl->controls[0]).string;
+
+		memset(tx_rt.rt_data, 0, MAX_RT_LENGTH);
+
+		if (copy_from_user(tx_rt.rt_data,
+				data, bytes_to_copy))
+				return -EFAULT;
+
+		tx_rt.rt_control =  0x01;
+		tx_rt.pi = radio->pi;
+		tx_rt.pty = radio->pty;
+		tx_rt.ps_len = bytes_to_copy;
+
+		retval = radio_hci_request(radio->fm_hdev, hci_trans_rt_req,
+				(unsigned long)&tx_rt, RADIO_HCI_TIMEOUT);
+		break;
+	case V4L2_CID_PRIVATE_IRIS_WRITE_DEFAULT:
+		data = (ctrl->controls[0]).string;
+		memset(&default_data, 0, sizeof(default_data));
+		/*
+		 * Check if length of the 'FM Default Data' to be sent
+		 * is within the maximum  'FM Default Data' packet limit.
+		 * Max. 'FM Default Data' packet length is 251 bytes:
+		 *	1 byte    - XFR Mode
+		 *	1 byte    - length of the default data
+		 *	249 bytes - actual data to be configured
+		 */
+		if (ctrl->controls[0].size > (DEFAULT_DATA_SIZE + 2)) {
+			pr_err("%s: Default data buffer overflow!\n", __func__);
+			return -EINVAL;
+		}
+
+		/* copy only 'size' bytes of data as requested by user */
+		retval = copy_from_user(&default_data, data,
+			ctrl->controls[0].size);
+		if (retval > 0) {
+			pr_err("%s: Failed to copy %d bytes of default data"
+				" passed by user\n", __func__, retval);
+			return -EFAULT;
+		}
+		FMDBG("%s: XFR Mode\t: 0x%x\n", __func__, default_data.mode);
+		FMDBG("%s: XFR Data Length\t: %d\n", __func__,
+			default_data.length);
+		/*
+		 * Check if the 'length' of the actual XFR data to be configured
+		 * is valid or not. Length of actual XFR data should be always
+		 * 2 bytes less than the total length of the 'FM Default Data'.
+		 * Length of 'FM Default Data' DEF_DATA_LEN: (1+1+XFR Data Size)
+		 * Length of 'Actual XFR Data' XFR_DATA_LEN: (DEF_DATA_LEN - 2)
+		 */
+		if (default_data.length != (ctrl->controls[0].size - 2)) {
+			pr_err("%s: Invalid 'length' parameter passed for "
+				"actual xfr data\n", __func__);
+			return -EINVAL;
+		}
+		retval = hci_def_data_write(&default_data, radio->fm_hdev);
+		break;
+	case V4L2_CID_PRIVATE_IRIS_SET_CALIBRATION:
+		data = (ctrl->controls[0]).string;
+		bytes_to_copy = (ctrl->controls[0]).size;
+		if (bytes_to_copy < PROCS_CALIB_SIZE) {
+			FMDERR("data is less than required size");
+			return -EFAULT;
+		}
+		memset(proc_cal_req.data, 0, PROCS_CALIB_SIZE);
+		proc_cal_req.mode = PROCS_CALIB_MODE;
+		if (copy_from_user(&proc_cal_req.data[0],
+				data, sizeof(proc_cal_req.data)))
+				return -EFAULT;
+		retval = radio_hci_request(radio->fm_hdev,
+				hci_fm_set_cal_req_proc,
+				(unsigned long)&proc_cal_req,
+				 RADIO_HCI_TIMEOUT);
+		if (retval < 0)
+			FMDERR("Set Process calibration failed %d", retval);
+		break;
+	default:
+		FMDBG("Shouldn't reach here\n");
+		retval = -1;
+	}
+	return retval;
+}
+
+static int iris_vidioc_s_ctrl(struct file *file, void *priv,
+		struct v4l2_control *ctrl)
+{
+	struct iris_device *radio = video_get_drvdata(video_devdata(file));
+	int retval = 0;
+	unsigned int rds_grps_proc = 0;
+	__u8 temp_val = 0;
+	unsigned long arg = 0;
+	struct hci_fm_tx_ps tx_ps = {0};
+	struct hci_fm_tx_rt tx_rt = {0};
+	struct hci_fm_def_data_rd_req rd_txgain;
+	struct hci_fm_def_data_wr_req wr_txgain;
+
+	switch (ctrl->id) {
+	case V4L2_CID_PRIVATE_IRIS_TX_TONE:
+		radio->tone_freq = ctrl->value;
+		retval = radio_hci_request(radio->fm_hdev,
+				hci_fm_tone_generator, arg,
+				msecs_to_jiffies(RADIO_HCI_TIMEOUT));
+		if (retval < 0)
+			FMDERR("Error while setting the tone %d", retval);
+		break;
+	case V4L2_CID_AUDIO_VOLUME:
+		break;
+	case V4L2_CID_AUDIO_MUTE:
+		radio->mute_mode.hard_mute = ctrl->value;
+		radio->mute_mode.soft_mute = IOC_SFT_MUTE;
+		retval = hci_set_fm_mute_mode(
+				&radio->mute_mode,
+				radio->fm_hdev);
+		if (retval < 0)
+			FMDERR("Error while set FM hard mute"" %d\n",
+			retval);
+		break;
+	case V4L2_CID_PRIVATE_IRIS_SRCHMODE:
+		radio->g_search_mode = ctrl->value;
+		break;
+	case V4L2_CID_PRIVATE_IRIS_SCANDWELL:
+		radio->g_scan_time = ctrl->value;
+		break;
+	case V4L2_CID_PRIVATE_IRIS_SRCHON:
+		iris_search(radio, ctrl->value, SRCH_DIR_UP);
+		break;
+	case V4L2_CID_PRIVATE_IRIS_STATE:
+		switch (ctrl->value) {
+		case FM_RECV:
+			retval = hci_cmd(HCI_FM_ENABLE_RECV_CMD,
+							 radio->fm_hdev);
+			if (retval < 0) {
+				FMDERR("Error while enabling RECV FM"
+							" %d\n", retval);
+				return retval;
+			}
+			radio->mode = FM_RECV;
+			radio->mute_mode.soft_mute = CTRL_ON;
+			retval = hci_set_fm_mute_mode(
+						&radio->mute_mode,
+							radio->fm_hdev);
+			if (retval < 0) {
+				FMDERR("Failed to enable Smute\n");
+				return retval;
+			}
+			radio->stereo_mode.stereo_mode = CTRL_OFF;
+			radio->stereo_mode.sig_blend = CTRL_ON;
+			radio->stereo_mode.intf_blend = CTRL_ON;
+			radio->stereo_mode.most_switch = CTRL_ON;
+			retval = hci_set_fm_stereo_mode(
+						&radio->stereo_mode,
+							radio->fm_hdev);
+			if (retval < 0) {
+				FMDERR("Failed to set stereo mode\n");
+				return retval;
+			}
+			radio->event_mask = SIG_LEVEL_INTR |
+						RDS_SYNC_INTR | AUDIO_CTRL_INTR;
+			retval = hci_conf_event_mask(&radio->event_mask,
+							radio->fm_hdev);
+			if (retval < 0) {
+				FMDERR("Enable Async events failed");
+				return retval;
+			}
+			retval = hci_cmd(HCI_FM_GET_RECV_CONF_CMD,
+						radio->fm_hdev);
+			if (retval < 0)
+				FMDERR("Failed to get the Recv Config\n");
+			break;
+		case FM_TRANS:
+			retval = hci_cmd(HCI_FM_ENABLE_TRANS_CMD,
+							 radio->fm_hdev);
+			if (retval < 0) {
+				FMDERR("Error while enabling TRANS FM"
+							" %d\n", retval);
+				return retval;
+			}
+			radio->mode = FM_TRANS;
+			retval = hci_cmd(HCI_FM_GET_TX_CONFIG, radio->fm_hdev);
+			if (retval < 0)
+				FMDERR("get frequency failed %d\n", retval);
+			break;
+		case FM_OFF:
+			switch (radio->mode) {
+			case FM_RECV:
+				retval = hci_cmd(HCI_FM_DISABLE_RECV_CMD,
+						radio->fm_hdev);
+				if (retval < 0) {
+					FMDERR("Err on disable recv FM"
+						   " %d\n", retval);
+					return retval;
+				}
+				radio->mode = FM_OFF;
+				break;
+			case FM_TRANS:
+				retval = hci_cmd(HCI_FM_DISABLE_TRANS_CMD,
+						radio->fm_hdev);
+
+				if (retval < 0) {
+					FMDERR("Err disabling trans FM"
+						" %d\n", retval);
+					return retval;
+				}
+				radio->mode = FM_OFF;
+				break;
+			default:
+				retval = -EINVAL;
+			}
+			break;
+		default:
+			retval = -EINVAL;
+		}
+		break;
+	case V4L2_CID_PRIVATE_IRIS_REGION:
+		if (radio->mode == FM_RECV) {
+			retval = iris_recv_set_region(radio, ctrl->value);
+		} else {
+			if (radio->mode == FM_TRANS)
+				retval = iris_trans_set_region(radio,
+						ctrl->value);
+			else
+				retval = -EINVAL;
+		}
+		break;
+	case V4L2_CID_PRIVATE_IRIS_SIGNAL_TH:
+		temp_val = ctrl->value;
+		retval = hci_fm_set_signal_threshold(
+				&temp_val,
+				radio->fm_hdev);
+		if (retval < 0) {
+			FMDERR("Error while setting signal threshold\n");
+			break;
+		}
+		break;
+	case V4L2_CID_PRIVATE_IRIS_SRCH_PTY:
+		radio->srch_rds.srch_pty = ctrl->value;
+		radio->srch_st_list.srch_pty = ctrl->value;
+		break;
+	case V4L2_CID_PRIVATE_IRIS_SRCH_PI:
+		radio->srch_rds.srch_pi = ctrl->value;
+		break;
+	case V4L2_CID_PRIVATE_IRIS_SRCH_CNT:
+		break;
+	case V4L2_CID_PRIVATE_IRIS_SPACING:
+		if (radio->mode == FM_RECV) {
+			radio->recv_conf.ch_spacing = ctrl->value;
+			retval = hci_set_fm_recv_conf(
+					&radio->recv_conf,
+						radio->fm_hdev);
+			if (retval < 0)
+				FMDERR("Error in setting channel spacing");
+		}
+		break;
+	case V4L2_CID_PRIVATE_IRIS_EMPHASIS:
+		switch (radio->mode) {
+		case FM_RECV:
+			radio->recv_conf.emphasis = ctrl->value;
+			retval = hci_set_fm_recv_conf(
+					&radio->recv_conf,
+						radio->fm_hdev);
+			if (retval < 0)
+				FMDERR("Error in setting emphasis");
+			break;
+		case FM_TRANS:
+			radio->trans_conf.emphasis = ctrl->value;
+			retval = hci_set_fm_trans_conf(
+					&radio->trans_conf,
+						radio->fm_hdev);
+			if (retval < 0)
+				FMDERR("Error in setting emphasis");
+			break;
+		default:
+			retval = -EINVAL;
+		}
+		break;
+	case V4L2_CID_PRIVATE_IRIS_RDS_STD:
+		switch (radio->mode) {
+		case FM_RECV:
+			radio->recv_conf.rds_std = ctrl->value;
+			retval = hci_set_fm_recv_conf(
+					&radio->recv_conf,
+						radio->fm_hdev);
+			if (retval < 0)
+				FMDERR("Error in rds_std");
+			break;
+		case FM_TRANS:
+			radio->trans_conf.rds_std = ctrl->value;
+			retval = hci_set_fm_trans_conf(
+					&radio->trans_conf,
+						radio->fm_hdev);
+			if (retval < 0)
+				FMDERR("Error in rds_Std");
+			break;
+		default:
+			retval = -EINVAL;
+		}
+		break;
+	case V4L2_CID_PRIVATE_IRIS_RDSON:
+		switch (radio->mode) {
+		case FM_RECV:
+			radio->recv_conf.rds_std = ctrl->value;
+			retval = hci_set_fm_recv_conf(
+					&radio->recv_conf,
+						radio->fm_hdev);
+			if (retval < 0)
+				FMDERR("Error in rds_std");
+			break;
+		case FM_TRANS:
+			radio->trans_conf.rds_std = ctrl->value;
+			retval = hci_set_fm_trans_conf(
+					&radio->trans_conf,
+						radio->fm_hdev);
+			if (retval < 0)
+				FMDERR("Error in rds_Std");
+			break;
+		default:
+			retval = -EINVAL;
+		}
+		break;
+	case V4L2_CID_PRIVATE_IRIS_RDSGROUP_MASK:
+		radio->rds_grp.rds_grp_enable_mask = ctrl->value;
+		retval = hci_fm_rds_grp(&radio->rds_grp, radio->fm_hdev);
+		break;
+	case V4L2_CID_PRIVATE_IRIS_RDSGROUP_PROC:
+		rds_grps_proc = radio->g_rds_grp_proc_ps | ctrl->value;
+		radio->g_rds_grp_proc_ps = (rds_grps_proc >> RDS_CONFIG_OFFSET);
+		retval = hci_fm_rds_grps_process(
+				&radio->g_rds_grp_proc_ps,
+				radio->fm_hdev);
+		break;
+	case V4L2_CID_PRIVATE_IRIS_RDSD_BUF:
+		radio->rds_grp.rds_buf_size = ctrl->value;
+		break;
+	case V4L2_CID_PRIVATE_IRIS_PSALL:
+		rds_grps_proc = (ctrl->value << RDS_CONFIG_OFFSET);
+		radio->g_rds_grp_proc_ps |= rds_grps_proc;
+		retval = hci_fm_rds_grps_process(
+				&radio->g_rds_grp_proc_ps,
+				radio->fm_hdev);
+		break;
+	case V4L2_CID_PRIVATE_IRIS_AF_JUMP:
+		/*Clear the current AF jump settings*/
+		radio->g_rds_grp_proc_ps &= ~(1 << RDS_AF_JUMP_OFFSET);
+		radio->af_jump_bit = ctrl->value;
+		rds_grps_proc = 0x00;
+		rds_grps_proc = (ctrl->value << RDS_AF_JUMP_OFFSET);
+		radio->g_rds_grp_proc_ps |= rds_grps_proc;
+		retval = hci_fm_rds_grps_process(
+				&radio->g_rds_grp_proc_ps,
+				radio->fm_hdev);
+		break;
+	case V4L2_CID_PRIVATE_IRIS_LP_MODE:
+		set_low_power_mode(radio, ctrl->value);
+		break;
+	case V4L2_CID_PRIVATE_IRIS_ANTENNA:
+		temp_val = ctrl->value;
+		retval = hci_fm_set_antenna(&temp_val, radio->fm_hdev);
+		if (retval < 0) {
+			FMDERR("Set Antenna failed retval = %x", retval);
+			return retval;
+		}
+		radio->g_antenna =  ctrl->value;
+		break;
+	case V4L2_CID_RDS_TX_PTY:
+		radio->pty = ctrl->value;
+		break;
+	case V4L2_CID_RDS_TX_PI:
+		radio->pi = ctrl->value;
+		break;
+	case V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_PS_NAME:
+		tx_ps.ps_control =  0x00;
+		retval = radio_hci_request(radio->fm_hdev, hci_trans_ps_req,
+				(unsigned long)&tx_ps, RADIO_HCI_TIMEOUT);
+		break;
+	case V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_RT:
+		tx_rt.rt_control =  0x00;
+		retval = radio_hci_request(radio->fm_hdev, hci_trans_rt_req,
+				(unsigned long)&tx_rt, RADIO_HCI_TIMEOUT);
+		break;
+	case V4L2_CID_PRIVATE_IRIS_TX_SETPSREPEATCOUNT:
+		radio->ps_repeatcount = ctrl->value;
+		break;
+	case V4L2_CID_TUNE_POWER_LEVEL:
+		if (ctrl->value > FM_TX_PWR_LVL_MAX)
+			ctrl->value = FM_TX_PWR_LVL_MAX;
+		if (ctrl->value < FM_TX_PWR_LVL_0)
+			ctrl->value = FM_TX_PWR_LVL_0;
+		rd_txgain.mode = FM_TX_PHY_CFG_MODE;
+		rd_txgain.length = FM_TX_PHY_CFG_LEN;
+		rd_txgain.param_len = 0x00;
+		rd_txgain.param = 0x00;
+
+		retval = hci_def_data_read(&rd_txgain, radio->fm_hdev);
+		if (retval < 0) {
+			FMDERR("Default data read failed for PHY_CFG %d\n",
+			retval);
+			break;
+		}
+		memset(&wr_txgain, 0, sizeof(wr_txgain));
+		wr_txgain.mode = FM_TX_PHY_CFG_MODE;
+		wr_txgain.length = FM_TX_PHY_CFG_LEN;
+		memcpy(&wr_txgain.data, &radio->default_data.data,
+					radio->default_data.ret_data_len);
+		wr_txgain.data[FM_TX_PWR_GAIN_OFFSET] =
+				(ctrl->value) * FM_TX_PWR_LVL_STEP_SIZE;
+		retval = hci_def_data_write(&wr_txgain, radio->fm_hdev);
+		if (retval < 0)
+			FMDERR("Default write failed for PHY_TXGAIN %d\n",
+			retval);
+		break;
+	case V4L2_CID_PRIVATE_IRIS_SOFT_MUTE:
+		radio->mute_mode.soft_mute = ctrl->value;
+		retval = hci_set_fm_mute_mode(
+				&radio->mute_mode,
+				radio->fm_hdev);
+		if (retval < 0)
+			FMDERR("Error while setting FM soft mute"" %d\n",
+			retval);
+		break;
+	case V4L2_CID_PRIVATE_IRIS_RIVA_ACCS_ADDR:
+		radio->riva_data_req.cmd_params.start_addr = ctrl->value;
+		break;
+	case V4L2_CID_PRIVATE_IRIS_RIVA_ACCS_LEN:
+		radio->riva_data_req.cmd_params.length = ctrl->value;
+		break;
+	case V4L2_CID_PRIVATE_IRIS_RIVA_POKE:
+		memcpy(radio->riva_data_req.data, (void *)ctrl->value,
+					radio->riva_data_req.cmd_params.length);
+		radio->riva_data_req.cmd_params.subopcode = RIVA_POKE_OPCODE;
+		retval = hci_poke_data(&radio->riva_data_req , radio->fm_hdev);
+		break;
+	case V4L2_CID_PRIVATE_IRIS_SSBI_ACCS_ADDR:
+		radio->ssbi_data_accs.start_addr = ctrl->value;
+		break;
+	case V4L2_CID_PRIVATE_IRIS_SSBI_POKE:
+		radio->ssbi_data_accs.data = ctrl->value;
+		retval = hci_ssbi_poke_reg(&radio->ssbi_data_accs ,
+								radio->fm_hdev);
+		break;
+	case V4L2_CID_PRIVATE_IRIS_RIVA_PEEK:
+		radio->riva_data_req.cmd_params.subopcode = RIVA_PEEK_OPCODE;
+		ctrl->value = hci_peek_data(&radio->riva_data_req.cmd_params ,
+						radio->fm_hdev);
+		break;
+	case V4L2_CID_PRIVATE_IRIS_SSBI_PEEK:
+		radio->ssbi_peek_reg.start_address = ctrl->value;
+		hci_ssbi_peek_reg(&radio->ssbi_peek_reg, radio->fm_hdev);
+		break;
+	case V4L2_CID_PRIVATE_IRIS_RDS_GRP_COUNTERS:
+		temp_val = ctrl->value;
+		hci_read_grp_counters(&temp_val, radio->fm_hdev);
+		break;
+	case V4L2_CID_PRIVATE_IRIS_HLSI:
+		retval = hci_cmd(HCI_FM_GET_RECV_CONF_CMD,
+						radio->fm_hdev);
+		if (retval)
+			break;
+		radio->recv_conf.hlsi = ctrl->value;
+		retval = hci_set_fm_recv_conf(
+					&radio->recv_conf,
+						radio->fm_hdev);
+		break;
+	case V4L2_CID_PRIVATE_IRIS_SET_NOTCH_FILTER:
+		temp_val = ctrl->value;
+		retval = hci_set_notch_filter(&temp_val, radio->fm_hdev);
+		break;
+	case V4L2_CID_PRIVATE_INTF_HIGH_THRESHOLD:
+		retval = hci_cmd(HCI_FM_GET_DET_CH_TH_CMD, radio->fm_hdev);
+		if (retval < 0) {
+			FMDERR("Failed to get chnl det thresholds  %d", retval);
+			return retval;
+		}
+		radio->ch_det_threshold.high_th = ctrl->value;
+		retval = hci_set_ch_det_thresholds_req(&radio->ch_det_threshold,
+							 radio->fm_hdev);
+		if (retval < 0) {
+			FMDERR("Failed to set High det threshold %d ", retval);
+			return retval;
+		}
+		break;
+
+	case V4L2_CID_PRIVATE_INTF_LOW_THRESHOLD:
+		retval = hci_cmd(HCI_FM_GET_DET_CH_TH_CMD, radio->fm_hdev);
+		if (retval < 0) {
+			FMDERR("Failed to get chnl det thresholds  %d", retval);
+			return retval;
+		}
+		radio->ch_det_threshold.low_th = ctrl->value;
+		retval = hci_set_ch_det_thresholds_req(&radio->ch_det_threshold,
+							 radio->fm_hdev);
+		if (retval < 0) {
+			FMDERR("Failed to Set Low det threshold %d", retval);
+			return retval;
+		}
+		break;
+
+	case V4L2_CID_PRIVATE_SINR_THRESHOLD:
+		retval = hci_cmd(HCI_FM_GET_DET_CH_TH_CMD, radio->fm_hdev);
+		if (retval < 0) {
+			FMDERR("Failed to get chnl det thresholds  %d", retval);
+			return retval;
+		}
+		radio->ch_det_threshold.sinr = ctrl->value;
+		retval = hci_set_ch_det_thresholds_req(&radio->ch_det_threshold,
+							 radio->fm_hdev);
+		if (retval < 0) {
+			FMDERR("Failed to set SINR threshold %d", retval);
+			return retval;
+		}
+		break;
+
+	case V4L2_CID_PRIVATE_SINR_SAMPLES:
+		retval = hci_cmd(HCI_FM_GET_DET_CH_TH_CMD, radio->fm_hdev);
+		if (retval < 0) {
+			FMDERR("Failed to get chnl det thresholds  %d", retval);
+			return retval;
+		}
+		radio->ch_det_threshold.sinr_samples = ctrl->value;
+		retval = hci_set_ch_det_thresholds_req(&radio->ch_det_threshold,
+							 radio->fm_hdev);
+	       if (retval < 0) {
+			FMDERR("Failed to set SINR samples  %d", retval);
+			return retval;
+		}
+		break;
+
+	case V4L2_CID_PRIVATE_IRIS_SRCH_ALGORITHM:
+	case V4L2_CID_PRIVATE_IRIS_SET_AUDIO_PATH:
+		/*
+		These private controls are place holders to keep the
+		driver compatible with changes done in the frameworks
+		which are specific to TAVARUA.
+		*/
+		retval = 0;
+		break;
+	default:
+		retval = -EINVAL;
+	}
+	return retval;
+}
+
+static int iris_vidioc_g_tuner(struct file *file, void *priv,
+		struct v4l2_tuner *tuner)
+{
+	int retval;
+	struct iris_device *radio = video_get_drvdata(video_devdata(file));
+
+	if (tuner->index > 0) {
+		FMDERR("Invalid Tuner Index");
+		return -EINVAL;
+	}
+	if (radio->mode == FM_RECV) {
+		retval = hci_cmd(HCI_FM_GET_STATION_PARAM_CMD, radio->fm_hdev);
+		if (retval < 0) {
+			FMDERR("Failed to Get station params");
+			return retval;
+		}
+		tuner->type = V4L2_TUNER_RADIO;
+		tuner->rangelow  =
+			radio->recv_conf.band_low_limit * TUNE_PARAM;
+		tuner->rangehigh =
+			radio->recv_conf.band_high_limit * TUNE_PARAM;
+		tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+		tuner->capability = V4L2_TUNER_CAP_LOW;
+		tuner->signal = radio->fm_st_rsp.station_rsp.rssi;
+		tuner->audmode = radio->fm_st_rsp.station_rsp.stereo_prg;
+		tuner->afc = 0;
+	} else if (radio->mode == FM_TRANS) {
+		retval = hci_cmd(HCI_FM_GET_TX_CONFIG, radio->fm_hdev);
+		if (retval < 0) {
+			FMDERR("get Tx config failed %d\n", retval);
+			return retval;
+		} else {
+			tuner->type = V4L2_TUNER_RADIO;
+			tuner->rangelow =
+				radio->trans_conf.band_low_limit * TUNE_PARAM;
+			tuner->rangehigh =
+				radio->trans_conf.band_high_limit * TUNE_PARAM;
+		}
+
+	} else
+		return -EINVAL;
+	return 0;
+}
+
+static int iris_vidioc_s_tuner(struct file *file, void *priv,
+		struct v4l2_tuner *tuner)
+{
+	struct iris_device *radio = video_get_drvdata(video_devdata(file));
+	int retval = 0;
+	if (tuner->index > 0)
+		return -EINVAL;
+
+	if (radio->mode == FM_RECV) {
+		radio->recv_conf.band_low_limit = tuner->rangelow / TUNE_PARAM;
+		radio->recv_conf.band_high_limit =
+			tuner->rangehigh / TUNE_PARAM;
+		if (tuner->audmode == V4L2_TUNER_MODE_MONO) {
+			radio->stereo_mode.stereo_mode = 0x01;
+			retval = hci_set_fm_stereo_mode(
+					&radio->stereo_mode,
+					radio->fm_hdev);
+		} else {
+			radio->stereo_mode.stereo_mode = 0x00;
+			retval = hci_set_fm_stereo_mode(
+					&radio->stereo_mode,
+					radio->fm_hdev);
+		}
+		if (retval < 0)
+			FMDERR(": set tuner failed with %d\n", retval);
+		return retval;
+	} else if (radio->mode == FM_TRANS) {
+			radio->trans_conf.band_low_limit =
+				tuner->rangelow / TUNE_PARAM;
+			radio->trans_conf.band_high_limit =
+				tuner->rangehigh / TUNE_PARAM;
+	} else
+		return -EINVAL;
+
+	return retval;
+}
+
+static int iris_vidioc_g_frequency(struct file *file, void *priv,
+		struct v4l2_frequency *freq)
+{
+	struct iris_device *radio = video_get_drvdata(video_devdata(file));
+	if ((freq != NULL) && (radio != NULL)) {
+		freq->frequency =
+			radio->fm_st_rsp.station_rsp.station_freq * TUNE_PARAM;
+	} else
+		return -EINVAL;
+	return 0;
+}
+
+static int iris_vidioc_s_frequency(struct file *file, void *priv,
+					struct v4l2_frequency *freq)
+{
+	struct iris_device  *radio = video_get_drvdata(video_devdata(file));
+	int retval = -1;
+	freq->frequency = freq->frequency / TUNE_PARAM;
+
+	if (freq->type != V4L2_TUNER_RADIO)
+		return -EINVAL;
+
+	/* We turn off RDS prior to tuning to a new station.
+	   because of a bug in SoC which prevents tuning
+	   during RDS transmission.
+	 */
+	if (radio->mode == FM_TRANS
+		&& (radio->trans_conf.rds_std == 0 ||
+			radio->trans_conf.rds_std == 1)) {
+		radio->prev_trans_rds = radio->trans_conf.rds_std;
+		radio->trans_conf.rds_std = 2;
+		hci_set_fm_trans_conf(&radio->trans_conf,
+				radio->fm_hdev);
+	}
+
+	retval = iris_set_freq(radio, freq->frequency);
+
+	if (radio->mode == FM_TRANS
+		 && radio->trans_conf.rds_std == 2
+			&& (radio->prev_trans_rds == 1
+				|| radio->prev_trans_rds == 0)) {
+		radio->trans_conf.rds_std = radio->prev_trans_rds;
+		hci_set_fm_trans_conf(&radio->trans_conf,
+				radio->fm_hdev);
+	}
+
+	if (retval < 0)
+		FMDERR(" set frequency failed with %d\n", retval);
+	return retval;
+}
+
+static int iris_vidioc_dqbuf(struct file *file, void *priv,
+				struct v4l2_buffer *buffer)
+{
+	struct iris_device  *radio = video_get_drvdata(video_devdata(file));
+	enum iris_buf_t buf_type = buffer->index;
+	struct kfifo *data_fifo;
+	unsigned char *buf = (unsigned char *)buffer->m.userptr;
+	unsigned int len = buffer->length;
+	if (!access_ok(VERIFY_WRITE, buf, len))
+		return -EFAULT;
+	if ((buf_type < IRIS_BUF_MAX) && (buf_type >= 0)) {
+		data_fifo = &radio->data_buf[buf_type];
+		if (buf_type == IRIS_BUF_EVENTS)
+			if (wait_event_interruptible(radio->event_queue,
+				kfifo_len(data_fifo)) < 0)
+				return -EINTR;
+	} else {
+		FMDERR("invalid buffer type\n");
+		return -EINVAL;
+	}
+	buffer->bytesused = kfifo_out_locked(data_fifo, buf, len,
+					&radio->buf_lock[buf_type]);
+
+	return 0;
+}
+
+static int iris_vidioc_g_fmt_type_private(struct file *file, void *priv,
+						struct v4l2_format *f)
+{
+	return 0;
+
+}
+
+static int iris_vidioc_s_hw_freq_seek(struct file *file, void *priv,
+					struct v4l2_hw_freq_seek *seek)
+{
+	struct iris_device *radio = video_get_drvdata(video_devdata(file));
+	int dir;
+	if (seek->seek_upward)
+		dir = SRCH_DIR_UP;
+	else
+		dir = SRCH_DIR_DOWN;
+	return iris_search(radio, CTRL_ON, dir);
+}
+
+static int iris_vidioc_querycap(struct file *file, void *priv,
+	struct v4l2_capability *capability)
+{
+	struct iris_device *radio;
+	radio = video_get_drvdata(video_devdata(file));
+	strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver));
+	strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
+	radio->g_cap = capability;
+	return 0;
+}
+
+
+static const struct v4l2_ioctl_ops iris_ioctl_ops = {
+	.vidioc_querycap              = iris_vidioc_querycap,
+	.vidioc_queryctrl             = iris_vidioc_queryctrl,
+	.vidioc_g_ctrl                = iris_vidioc_g_ctrl,
+	.vidioc_s_ctrl                = iris_vidioc_s_ctrl,
+	.vidioc_g_tuner               = iris_vidioc_g_tuner,
+	.vidioc_s_tuner               = iris_vidioc_s_tuner,
+	.vidioc_g_frequency           = iris_vidioc_g_frequency,
+	.vidioc_s_frequency           = iris_vidioc_s_frequency,
+	.vidioc_s_hw_freq_seek        = iris_vidioc_s_hw_freq_seek,
+	.vidioc_dqbuf                 = iris_vidioc_dqbuf,
+	.vidioc_g_fmt_type_private    = iris_vidioc_g_fmt_type_private,
+	.vidioc_s_ext_ctrls           = iris_vidioc_s_ext_ctrls,
+	.vidioc_g_ext_ctrls           = iris_vidioc_g_ext_ctrls,
+};
+
+static const struct v4l2_file_operations iris_fops = {
+	.owner = THIS_MODULE,
+	.unlocked_ioctl = video_ioctl2,
+};
+
+static struct video_device iris_viddev_template = {
+	.fops                   = &iris_fops,
+	.ioctl_ops              = &iris_ioctl_ops,
+	.name                   = DRIVER_NAME,
+	.release                = video_device_release,
+};
+
+static struct video_device *video_get_dev(void)
+{
+	return priv_videodev;
+}
+
+static int __init iris_probe(struct platform_device *pdev)
+{
+	struct iris_device *radio;
+	int retval;
+	int radio_nr = -1;
+	int i;
+
+	if (!pdev) {
+		FMDERR(": pdev is null\n");
+		return -ENOMEM;
+	}
+
+	radio = kzalloc(sizeof(struct iris_device), GFP_KERNEL);
+	if (!radio) {
+		FMDERR(": Could not allocate radio device\n");
+		return -ENOMEM;
+	}
+
+	radio->dev = &pdev->dev;
+	platform_set_drvdata(pdev, radio);
+
+	radio->videodev = video_device_alloc();
+	if (!radio->videodev) {
+		FMDERR(": Could not allocate V4L device\n");
+		kfree(radio);
+		return -ENOMEM;
+	}
+
+	memcpy(radio->videodev, &iris_viddev_template,
+	  sizeof(iris_viddev_template));
+
+	for (i = 0; i < IRIS_BUF_MAX; i++) {
+		int kfifo_alloc_rc = 0;
+		spin_lock_init(&radio->buf_lock[i]);
+
+		if ((i == IRIS_BUF_RAW_RDS) || (i == IRIS_BUF_PEEK))
+			kfifo_alloc_rc = kfifo_alloc(&radio->data_buf[i],
+				rds_buf*3, GFP_KERNEL);
+		else if ((i == IRIS_BUF_CAL_DATA) || (i == IRIS_BUF_RT_RDS))
+			kfifo_alloc_rc = kfifo_alloc(&radio->data_buf[i],
+				STD_BUF_SIZE*2, GFP_KERNEL);
+		else
+			kfifo_alloc_rc = kfifo_alloc(&radio->data_buf[i],
+				STD_BUF_SIZE, GFP_KERNEL);
+
+		if (kfifo_alloc_rc != 0) {
+			FMDERR("failed allocating buffers %d\n",
+				   kfifo_alloc_rc);
+			for (; i > -1; i--) {
+				kfifo_free(&radio->data_buf[i]);
+				kfree(radio);
+				return -ENOMEM;
+			}
+		}
+	}
+
+	mutex_init(&radio->lock);
+	init_completion(&radio->sync_xfr_start);
+	radio->tune_req = 0;
+	radio->prev_trans_rds = 2;
+	init_waitqueue_head(&radio->event_queue);
+	init_waitqueue_head(&radio->read_queue);
+
+	video_set_drvdata(radio->videodev, radio);
+
+	if (NULL == video_get_drvdata(radio->videodev))
+		FMDERR(": video_get_drvdata failed\n");
+
+	retval = video_register_device(radio->videodev, VFL_TYPE_RADIO,
+								   radio_nr);
+	if (retval) {
+		FMDERR(": Could not register video device\n");
+		video_device_release(radio->videodev);
+		for (; i > -1; i--)
+			kfifo_free(&radio->data_buf[i]);
+		kfree(radio);
+		return retval;
+	} else {
+		priv_videodev = kzalloc(sizeof(struct video_device),
+			GFP_KERNEL);
+		memcpy(priv_videodev, radio->videodev,
+			sizeof(struct video_device));
+	}
+	return 0;
+}
+
+
+static int __devexit iris_remove(struct platform_device *pdev)
+{
+	int i;
+	struct iris_device *radio = platform_get_drvdata(pdev);
+
+	video_unregister_device(radio->videodev);
+
+	for (i = 0; i < IRIS_BUF_MAX; i++)
+		kfifo_free(&radio->data_buf[i]);
+
+	kfree(radio);
+
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver iris_driver = {
+	.driver = {
+		.owner  = THIS_MODULE,
+		.name   = "iris_fm",
+	},
+	.remove = __devexit_p(iris_remove),
+};
+
+static int __init iris_radio_init(void)
+{
+	return platform_driver_probe(&iris_driver, iris_probe);
+}
+module_init(iris_radio_init);
+
+static void __exit iris_radio_exit(void)
+{
+	platform_driver_unregister(&iris_driver);
+}
+module_exit(iris_radio_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/media/radio/radio-tavarua.c b/drivers/media/radio/radio-tavarua.c
new file mode 100644
index 0000000..c15609f
--- /dev/null
+++ b/drivers/media/radio/radio-tavarua.c
@@ -0,0 +1,4204 @@
+/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+/*
+ * Qualcomm Tavarua FM core driver
+ */
+
+/* driver definitions */
+#define DRIVER_AUTHOR "Qualcomm"
+#define DRIVER_NAME "radio-tavarua"
+#define DRIVER_CARD "Qualcomm FM Radio Transceiver"
+#define DRIVER_DESC "I2C radio driver for Qualcomm FM Radio Transceiver "
+#define DRIVER_VERSION "1.0.0"
+
+#include <linux/version.h>
+#include <linux/init.h>         /* Initdata                     */
+#include <linux/delay.h>        /* udelay                       */
+#include <linux/uaccess.h>      /* copy to/from user            */
+#include <linux/kfifo.h>        /* lock free circular buffer    */
+#include <linux/param.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+
+/* kernel includes */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/videodev2.h>
+#include <linux/mutex.h>
+#include <media/v4l2-common.h>
+#include <asm/unaligned.h>
+#include <media/v4l2-ioctl.h>
+#include <linux/unistd.h>
+#include <asm/atomic.h>
+#include <media/tavarua.h>
+#include <linux/mfd/marimba.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+/*
+regional parameters for radio device
+*/
+struct region_params_t {
+	enum tavarua_region_t region;
+	unsigned int band_high;
+	unsigned int band_low;
+	char emphasis;
+	char rds_std;
+	char spacing;
+};
+
+struct srch_params_t {
+	unsigned short srch_pi;
+	unsigned char srch_pty;
+	unsigned int preset_num;
+	int get_list;
+};
+
+/* Main radio device structure,
+acts as a shadow copy of the
+actual tavaura registers */
+struct tavarua_device {
+	struct video_device *videodev;
+	/* driver management */
+	atomic_t users;
+	/* top level driver data */
+	struct marimba *marimba;
+	struct device *dev;
+	/* platform specific functionality */
+	struct marimba_fm_platform_data *pdata;
+	unsigned int chipID;
+	/*RDS buffers + Radio event buffer*/
+	struct kfifo data_buf[TAVARUA_BUF_MAX];
+	/* search paramters */
+	struct srch_params_t srch_params;
+	/* keep track of pending xfrs */
+	int pending_xfrs[TAVARUA_XFR_MAX];
+	int xfr_bytes_left;
+	int xfr_in_progress;
+	/* Transmit data */
+	enum tavarua_xfr_ctrl_t tx_mode;
+	/* synchrnous xfr data */
+	unsigned char sync_xfr_regs[XFR_REG_NUM];
+	struct completion sync_xfr_start;
+	struct completion shutdown_done;
+	struct completion sync_req_done;
+	int tune_req;
+	/* internal register status */
+	unsigned char registers[RADIO_REGISTERS];
+	/* regional settings */
+	struct region_params_t region_params;
+	/* power mode */
+	int lp_mode;
+	int handle_irq;
+	/* global lock */
+	struct mutex lock;
+	/* buffer locks*/
+	spinlock_t buf_lock[TAVARUA_BUF_MAX];
+	/* work queue */
+	struct workqueue_struct *wqueue;
+	struct delayed_work work;
+	/* wait queue for blocking event read */
+	wait_queue_head_t event_queue;
+	/* wait queue for raw rds read */
+	wait_queue_head_t read_queue;
+	/* PTY for FM Tx */
+	int pty;
+	/* PI for FM TX */
+	int pi;
+	/*PS repeatcount for PS Tx */
+	int ps_repeatcount;
+	int enable_optimized_srch_alg;
+};
+
+/**************************************************************************
+ * Module Parameters
+ **************************************************************************/
+
+/* Radio Nr */
+static int radio_nr = -1;
+module_param(radio_nr, int, 0);
+MODULE_PARM_DESC(radio_nr, "Radio Nr");
+static int wait_timeout = WAIT_TIMEOUT;
+/* Bahama's version*/
+static u8 bahama_version;
+/* RDS buffer blocks */
+static unsigned int rds_buf = 100;
+module_param(rds_buf, uint, 0);
+MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*");
+/* static variables */
+static struct tavarua_device *private_data;
+/* forward declerations */
+static int tavarua_disable_interrupts(struct tavarua_device *radio);
+static int tavarua_setup_interrupts(struct tavarua_device *radio,
+					enum radio_state_t state);
+static int tavarua_start(struct tavarua_device *radio,
+			enum radio_state_t state);
+static int tavarua_request_irq(struct tavarua_device *radio);
+static void start_pending_xfr(struct tavarua_device *radio);
+/* work function */
+static void read_int_stat(struct work_struct *work);
+
+static int is_bahama(void)
+{
+	int id = 0;
+
+	switch (id = adie_get_detected_connectivity_type()) {
+	case BAHAMA_ID:
+		FMDBG("It is Bahama\n");
+		return 1;
+
+	case MARIMBA_ID:
+		FMDBG("It is Marimba\n");
+		return 0;
+	default:
+		printk(KERN_ERR "%s: unexpected adie connectivity type: %d\n",
+			__func__, id);
+		return -ENODEV;
+	}
+}
+
+static int set_fm_slave_id(struct tavarua_device *radio)
+{
+	int bahama_present = is_bahama();
+
+	if (bahama_present == -ENODEV)
+		return -ENODEV;
+
+	if (bahama_present)
+		radio->marimba->mod_id = SLAVE_ID_BAHAMA_FM;
+	else
+		radio->marimba->mod_id = MARIMBA_SLAVE_ID_FM;
+
+	return 0;
+}
+
+/*=============================================================================
+FUNCTION:  tavarua_isr
+=============================================================================*/
+/**
+  This function is called when GPIO is toggled. This functions queues the event
+  to interrupt queue, which is later handled by isr handling funcion.
+  i.e. INIT_DELAYED_WORK(&radio->work, read_int_stat);
+
+  @param irq: irq that is toggled.
+  @param dev_id: structure pointer passed by client.
+
+  @return IRQ_HANDLED.
+*/
+static irqreturn_t tavarua_isr(int irq, void *dev_id)
+{
+	struct tavarua_device *radio = dev_id;
+	/* schedule a tasklet to handle host intr */
+  /* The call to queue_delayed_work ensures that a minimum delay (in jiffies)
+   * passes before the work is actually executed. The return value from the
+   * function is nonzero if the work_struct was actually added to queue
+   * (otherwise, it may have already been there and will not be added a second
+   * time).
+   */
+	queue_delayed_work(radio->wqueue, &radio->work,
+				msecs_to_jiffies(TAVARUA_DELAY));
+	return IRQ_HANDLED;
+}
+
+/**************************************************************************
+ * Interface to radio internal registers over top level marimba driver
+ *************************************************************************/
+
+/*=============================================================================
+FUNCTION:  tavarua_read_registers
+=============================================================================*/
+/**
+  This function is called to read a number of bytes from an I2C interface.
+  The bytes read are stored in internal register status (shadow copy).
+
+  @param radio: structure pointer passed by client.
+  @param offset: register offset.
+  @param len: num of bytes.
+
+  @return => 0 if successful.
+  @return < 0 if failure.
+*/
+static int tavarua_read_registers(struct tavarua_device *radio,
+				unsigned char offset, int len)
+{
+	int retval = 0, i = 0;
+	retval = set_fm_slave_id(radio);
+
+	if (retval == -ENODEV)
+		return retval;
+
+	FMDBG_I2C("I2C Slave: %x, Read Offset(%x): Data [",
+						radio->marimba->mod_id,
+						offset);
+
+	retval =  marimba_read(radio->marimba, offset,
+				&radio->registers[offset], len);
+
+	if (retval > 0) {
+		for (i = 0; i < len; i++)
+			FMDBG_I2C("%02x ", radio->registers[offset+i]);
+		FMDBG_I2C(" ]\n");
+
+	}
+	return retval;
+}
+
+/*=============================================================================
+FUNCTION:  tavarua_write_register
+=============================================================================*/
+/**
+  This function is called to write a byte over the I2C interface.
+  The corresponding shadow copy is stored in internal register status.
+
+  @param radio: structure pointer passed by client.
+  @param offset: register offset.
+  @param value: buffer to be written to the registers.
+
+  @return => 0 if successful.
+  @return < 0 if failure.
+*/
+static int tavarua_write_register(struct tavarua_device *radio,
+			unsigned char offset, unsigned char value)
+{
+	int retval;
+	retval = set_fm_slave_id(radio);
+
+	if (retval == -ENODEV)
+		return retval;
+
+	FMDBG_I2C("I2C Slave: %x, Write Offset(%x): Data[",
+						radio->marimba->mod_id,
+						offset);
+	retval = marimba_write(radio->marimba, offset, &value, 1);
+	if (retval > 0) {
+		if (offset < RADIO_REGISTERS) {
+			radio->registers[offset] = value;
+			FMDBG_I2C("%02x ", radio->registers[offset]);
+		}
+		FMDBG_I2C(" ]\n");
+	}
+	return retval;
+}
+
+/*=============================================================================
+FUNCTION:  tavarua_write_registers
+=============================================================================*/
+/**
+  This function is called to write a number of bytes over the I2C interface.
+  The corresponding shadow copy is stored in internal register status.
+
+  @param radio: structure pointer passed by client.
+  @param offset: register offset.
+  @param buf: buffer to be written to the registers.
+  @param len: num of bytes.
+
+  @return => 0 if successful.
+  @return < 0 if failure.
+*/
+static int tavarua_write_registers(struct tavarua_device *radio,
+			unsigned char offset, unsigned char *buf, int len)
+{
+
+	int i;
+	int retval;
+	retval = set_fm_slave_id(radio);
+
+	if (retval == -ENODEV)
+		return retval;
+
+	FMDBG_I2C("I2C Slave: %x, Write Offset(%x): Data[",
+						radio->marimba->mod_id,
+						offset);
+	retval = marimba_write(radio->marimba, offset, buf, len);
+	if (retval > 0) { /* if write successful, update internal state too */
+		for (i = 0; i < len; i++) {
+			if ((offset+i) < RADIO_REGISTERS) {
+				radio->registers[offset+i] = buf[i];
+				FMDBG_I2C("%x ",  radio->registers[offset+i]);
+			}
+		}
+		FMDBG_I2C(" ]\n");
+	}
+	return retval;
+}
+
+/*=============================================================================
+FUNCTION:  read_data_blocks
+=============================================================================*/
+/**
+  This function reads Raw RDS blocks from Core regs to driver
+  internal regs (shadow copy).
+
+  @param radio: structure pointer passed by client.
+  @param offset: register offset.
+
+  @return => 0 if successful.
+  @return < 0 if failure.
+*/
+static int read_data_blocks(struct tavarua_device *radio, unsigned char offset)
+{
+	/* read all 3 RDS blocks */
+	return tavarua_read_registers(radio, offset, RDS_BLOCK*4);
+}
+
+/*=============================================================================
+FUNCTION:  tavarua_rds_read
+=============================================================================*/
+/**
+  This is a rds processing function reads that reads Raw RDS blocks from Core
+  regs to driver internal regs (shadow copy). It then fills the V4L2 RDS buffer,
+  which is read by App using JNI interface.
+
+  @param radio: structure pointer passed by client.
+
+  @return None.
+*/
+static void tavarua_rds_read(struct tavarua_device *radio)
+{
+	struct kfifo *rds_buf = &radio->data_buf[TAVARUA_BUF_RAW_RDS];
+	unsigned char blocknum;
+	unsigned char tmp[3];
+
+	if (read_data_blocks(radio, RAW_RDS) < 0)
+		return;
+	 /* copy all four RDS blocks to internal buffer */
+	for (blocknum = 0; blocknum < RDS_BLOCKS_NUM; blocknum++) {
+		/* Fill the V4L2 RDS buffer */
+		put_unaligned(cpu_to_le16(radio->registers[RAW_RDS +
+			blocknum*RDS_BLOCK]), (unsigned short *) tmp);
+		tmp[2] = blocknum;		/* offset name */
+		tmp[2] |= blocknum << 3;	/* received offset */
+		tmp[2] |= 0x40; /* corrected error(s) */
+
+		/* copy RDS block to internal buffer */
+		kfifo_in_locked(rds_buf, tmp, 3, &radio->buf_lock[TAVARUA_BUF_RAW_RDS]);
+	}
+	/* wake up read queue */
+	if (kfifo_len(rds_buf))
+		wake_up_interruptible(&radio->read_queue);
+
+}
+
+/*=============================================================================
+FUNCTION:  request_read_xfr
+=============================================================================*/
+/**
+  This function sets the desired MODE in the XFRCTRL register and also sets the
+  CTRL field to read.
+  This is an asynchronous way of reading the XFR registers. Client would request
+  by setting the desired mode in the XFRCTRL register and then would initiate
+  the actual data register read by calling copy_from_xfr up on SOC signals
+  success.
+
+  NOTE:
+
+  The Data Transfer (XFR) registers are used to pass various data and
+  configuration parameters between the Core and host processor.
+
+  To read from the XFR registers, the host processor must set the desired MODE
+  in the XFRCTRL register and set the CTRL field to read. The Core will then
+  populate the XFRDAT0 - XFRDAT15 registers with the defined mode bytes. The
+  Core will set the TRANSFER interrupt status bit and interrupt the host if the
+  TRANSFERCTRL interrupt control bit is set. The host can then extract the XFR
+  mode bytes once it detects that the Core has updated the registers.
+
+  @param radio: structure pointer passed by client.
+
+  @return Always returns 0.
+*/
+static int request_read_xfr(struct tavarua_device *radio,
+				enum tavarua_xfr_ctrl_t mode){
+
+	tavarua_write_register(radio, XFRCTRL, mode);
+	msleep(TAVARUA_DELAY);
+	return 0;
+}
+
+/*=============================================================================
+FUNCTION:  copy_from_xfr
+=============================================================================*/
+/**
+  This function is used to read XFR mode bytes once it detects that the Core
+  has updated the registers. It also updates XFR regs to the appropriate
+  internal buffer n bytes.
+
+  NOTE:
+
+  This function should be used in conjuction with request_read_xfr. Refer
+  request_read_xfr for XFR mode transaction details.
+
+  @param radio: structure pointer passed by client.
+  @param buf_type: Index into RDS/Radio event buffer to use.
+  @param len: num of bytes.
+
+  @return Always returns 0.
+*/
+static int copy_from_xfr(struct tavarua_device *radio,
+		enum tavarua_buf_t buf_type, unsigned int n){
+
+	struct kfifo *data_fifo = &radio->data_buf[buf_type];
+	unsigned char *xfr_regs = &radio->registers[XFRCTRL+1];
+	kfifo_in_locked(data_fifo, xfr_regs, n, &radio->buf_lock[buf_type]);
+	return 0;
+}
+
+/*=============================================================================
+FUNCTION:  write_to_xfr
+=============================================================================*/
+/**
+  This function sets the desired MODE in the XFRCTRL register and it also sets
+  the CTRL field and data to write.
+  This also writes all the XFRDATx registers with the desired input buffer.
+
+  NOTE:
+
+  The Data Transfer (XFR) registers are used to pass various data and
+  configuration parameters between the Core and host processor.
+
+  To write data to the Core, the host processor updates XFRDAT0 - XFRDAT15 with
+  the appropriate mode bytes. The host processor must then set the desired MODE
+  in the XFRCTRL register and set the CTRL field to write. The core will detect
+  that the XFRCTRL register was written to and will read the XFR mode bytes.
+  After reading all the mode bytes, the Core will set the TRANSFER interrupt
+  status bit and interrupt the host if the TRANSFERCTRL interrupt control bit
+  is set.
+
+  @param radio: structure pointer passed by client.
+  @param mode: XFR mode to write in XFRCTRL register.
+  @param buf: buffer to be written to the registers.
+  @param len: num of bytes.
+
+  @return => 0 if successful.
+  @return < 0 if failure.
+*/
+static int write_to_xfr(struct tavarua_device *radio, unsigned char mode,
+			char *buf, int len)
+{
+	char buffer[len+1];
+	memcpy(buffer+1, buf, len);
+	/* buffer[0] corresponds to XFRCTRL register
+	   set the CTRL bit to 1 for write mode
+	*/
+	buffer[0] = ((1<<7) | mode);
+	return tavarua_write_registers(radio, XFRCTRL, buffer, sizeof(buffer));
+}
+
+/*=============================================================================
+FUNCTION:  xfr_intf_own
+=============================================================================*/
+/**
+  This function is used to check if there is any pending XFR mode operation.
+  If yes, wait for it to complete, else update the flag to indicate XFR
+  operation is in progress
+
+  @param radio: structure pointer passed by client.
+
+  @return 0      on success.
+	-ETIME on timeout.
+*/
+static int xfr_intf_own(struct tavarua_device *radio)
+{
+
+	mutex_lock(&radio->lock);
+	if (radio->xfr_in_progress) {
+		radio->pending_xfrs[TAVARUA_XFR_SYNC] = 1;
+		mutex_unlock(&radio->lock);
+		if (!wait_for_completion_timeout(&radio->sync_xfr_start,
+			msecs_to_jiffies(wait_timeout)))
+			return -ETIME;
+	} else {
+		FMDBG("gained ownership of xfr\n");
+		radio->xfr_in_progress = 1;
+		mutex_unlock(&radio->lock);
+	}
+	return 0;
+}
+
+/*=============================================================================
+FUNCTION:  sync_read_xfr
+=============================================================================*/
+/**
+  This function is used to do synchronous XFR read operation.
+
+  @param radio: structure pointer passed by client.
+  @param xfr_type: XFR mode to write in XFRCTRL register.
+  @param buf: buffer to be read from the core.
+
+  @return => 0 if successful.
+  @return < 0 if failure.
+*/
+static int sync_read_xfr(struct tavarua_device *radio,
+			enum tavarua_xfr_ctrl_t xfr_type, unsigned char *buf)
+{
+	int retval;
+	retval = xfr_intf_own(radio);
+	if (retval < 0)
+		return retval;
+	retval = tavarua_write_register(radio, XFRCTRL, xfr_type);
+
+	if (retval >= 0) {
+		/* Wait for interrupt i.e. complete
+		(&radio->sync_req_done); call */
+		if (!wait_for_completion_timeout(&radio->sync_req_done,
+			msecs_to_jiffies(wait_timeout)) || (retval < 0)) {
+			retval = -ETIME;
+		} else {
+			memcpy(buf, radio->sync_xfr_regs, XFR_REG_NUM);
+		}
+	}
+	radio->xfr_in_progress = 0;
+	start_pending_xfr(radio);
+	FMDBG("%s: %d\n", __func__, retval);
+	return retval;
+}
+
+/*=============================================================================
+FUNCTION:  sync_write_xfr
+=============================================================================*/
+/**
+  This function is used to do synchronous XFR write operation.
+
+  @param radio: structure pointer passed by client.
+  @param xfr_type: XFR mode to write in XFRCTRL register.
+  @param buf: buffer to be written to the core.
+
+  @return => 0 if successful.
+  @return < 0 if failure.
+*/
+static int sync_write_xfr(struct tavarua_device *radio,
+		enum tavarua_xfr_ctrl_t xfr_type, unsigned char *buf)
+{
+	int retval;
+	retval = xfr_intf_own(radio);
+	if (retval < 0)
+		return retval;
+	retval = write_to_xfr(radio, xfr_type, buf, XFR_REG_NUM);
+
+	if (retval >= 0) {
+		/* Wait for interrupt i.e. complete
+		(&radio->sync_req_done); call */
+		if (!wait_for_completion_timeout(&radio->sync_req_done,
+			msecs_to_jiffies(wait_timeout)) || (retval < 0)) {
+			FMDBG("Write xfr timeout");
+		}
+	}
+	radio->xfr_in_progress = 0;
+	start_pending_xfr(radio);
+	FMDBG("%s: %d\n", __func__,  retval);
+	return retval;
+}
+
+
+/*=============================================================================
+FUNCTION:  start_pending_xfr
+=============================================================================*/
+/**
+  This function checks if their are any pending xfr interrupts and if
+  the interrupts are either RDS PS, RDS RT, RDS AF, SCANNEXT, SEARCH or SYNC
+  then initiates corresponding read operation. Preference is given to RAW RDS
+  data (SYNC) over processed data (PS, RT, AF, etc) from core.
+
+  @param radio: structure pointer passed by client.
+
+  @return None.
+*/
+static void start_pending_xfr(struct tavarua_device *radio)
+{
+	int i;
+	enum tavarua_xfr_t xfr;
+	for (i = 0; i < TAVARUA_XFR_MAX; i++) {
+		if (radio->pending_xfrs[i]) {
+			radio->xfr_in_progress = 1;
+			xfr = (enum tavarua_xfr_t)i;
+			switch (xfr) {
+			/* priority given to synchronous xfrs */
+			case TAVARUA_XFR_SYNC:
+				complete(&radio->sync_xfr_start);
+				break;
+			/* asynchrnous xfrs */
+			case TAVARUA_XFR_SRCH_LIST:
+				request_read_xfr(radio, RX_STATIONS_0);
+				break;
+			case TAVARUA_XFR_RT_RDS:
+				request_read_xfr(radio, RDS_RT_0);
+				break;
+			case TAVARUA_XFR_PS_RDS:
+				request_read_xfr(radio, RDS_PS_0);
+				break;
+			case TAVARUA_XFR_AF_LIST:
+				request_read_xfr(radio, RDS_AF_0);
+				break;
+			default:
+				FMDERR("%s: Unsupported XFR %d\n",
+					 __func__, xfr);
+			}
+			radio->pending_xfrs[i] = 0;
+			FMDBG("resurrect xfr %d\n", i);
+			}
+	}
+	return;
+}
+
+/*=============================================================================
+FUNCTION:  tavarua_q_event
+=============================================================================*/
+/**
+  This function is called to queue an event for user.
+
+  NOTE:
+  Applications call the VIDIOC_QBUF ioctl to enqueue an empty (capturing) or
+  filled (output) buffer in the driver's incoming queue.
+
+  Pleaes refer tavarua_probe where we register different ioctl's for FM.
+
+  @param radio: structure pointer passed by client.
+  @param event: event to be queued.
+
+  @return None.
+*/
+static void tavarua_q_event(struct tavarua_device *radio,
+				enum tavarua_evt_t event)
+{
+
+	struct kfifo *data_b = &radio->data_buf[TAVARUA_BUF_EVENTS];
+	unsigned char evt = event;
+	FMDBG("updating event_q with event %x\n", event);
+	if (kfifo_in_locked(data_b, &evt, 1, &radio->buf_lock[TAVARUA_BUF_EVENTS]))
+		wake_up_interruptible(&radio->event_queue);
+}
+
+/*=============================================================================
+FUNCTION:  tavarua_start_xfr
+=============================================================================*/
+/**
+  This function is called to process interrupts which require multiple XFR
+  operations (RDS search, RDS PS, RDS RT, etc). if any XFR operation is
+  already in progress we store information about pending interrupt, which
+  will be processed in future when current pending operation is done.
+
+  @param radio: structure pointer passed by client.
+  @param pending_id: XFR operation (which requires multiple XFR operations in
+	steps) to start.
+  @param xfr_id: XFR mode to write in XFRCTRL register.
+
+  @return None.
+*/
+static void tavarua_start_xfr(struct tavarua_device *radio,
+		enum tavarua_xfr_t pending_id, enum tavarua_xfr_ctrl_t xfr_id)
+{
+		if (radio->xfr_in_progress)
+			radio->pending_xfrs[pending_id] = 1;
+		else {
+			radio->xfr_in_progress = 1;
+			request_read_xfr(radio, xfr_id);
+		}
+}
+
+/*=============================================================================
+FUNCTION:  tavarua_handle_interrupts
+=============================================================================*/
+/**
+  This function processes the interrupts.
+
+  NOTE:
+  tavarua_q_event is used to queue events in App buffer. i.e. App calls the
+  VIDIOC_QBUF ioctl to enqueue an empty (capturing) buffer, which is filled
+  by tavarua_q_event call.
+
+  Any async event that requires multiple steps, i.e. search, RT, PS, etc is
+  handled one at a time. (We preserve other interrupts when processing one).
+  Sync interrupts are given priority.
+
+  @param radio: structure pointer passed by client.
+
+  @return None.
+*/
+static void tavarua_handle_interrupts(struct tavarua_device *radio)
+{
+	int i;
+	int retval;
+	unsigned char xfr_status;
+	if (!radio->handle_irq) {
+		FMDBG("IRQ happend, but I wont handle it\n");
+		return;
+	}
+	mutex_lock(&radio->lock);
+	tavarua_read_registers(radio, STATUS_REG1, STATUS_REG_NUM);
+
+	FMDBG("INTSTAT1 <%x>\n", radio->registers[STATUS_REG1]);
+	FMDBG("INTSTAT2 <%x>\n", radio->registers[STATUS_REG2]);
+	FMDBG("INTSTAT3 <%x>\n", radio->registers[STATUS_REG3]);
+
+	if (radio->registers[STATUS_REG1] & READY) {
+		complete(&radio->sync_req_done);
+		tavarua_q_event(radio, TAVARUA_EVT_RADIO_READY);
+	}
+
+	/* Tune completed */
+	if (radio->registers[STATUS_REG1] & TUNE) {
+		if (radio->tune_req) {
+			complete(&radio->sync_req_done);
+			radio->tune_req = 0;
+		}
+		tavarua_q_event(radio, TAVARUA_EVT_TUNE_SUCC);
+		if (radio->srch_params.get_list) {
+			tavarua_start_xfr(radio, TAVARUA_XFR_SRCH_LIST,
+							RX_STATIONS_0);
+		}
+		radio->srch_params.get_list = 0;
+		radio->xfr_in_progress = 0;
+		radio->xfr_bytes_left = 0;
+		for (i = 0; i < TAVARUA_BUF_MAX; i++) {
+			if (i >= TAVARUA_BUF_RT_RDS)
+				kfifo_reset(&radio->data_buf[i]);
+		}
+		for (i = 0; i < TAVARUA_XFR_MAX; i++) {
+			if (i >= TAVARUA_XFR_RT_RDS)
+				radio->pending_xfrs[i] = 0;
+		}
+		retval = tavarua_read_registers(radio, TUNECTRL, 1);
+		/* send to user station parameters */
+		if (retval > -1) {
+			/* Signal strength */
+			if (!(radio->registers[TUNECTRL] & SIGSTATE))
+				tavarua_q_event(radio, TAVARUA_EVT_BELOW_TH);
+			else
+				tavarua_q_event(radio, TAVARUA_EVT_ABOVE_TH);
+			/* mono/stereo */
+			if ((radio->registers[TUNECTRL] & MOSTSTATE))
+				tavarua_q_event(radio, TAVARUA_EVT_STEREO);
+			else
+				tavarua_q_event(radio, TAVARUA_EVT_MONO);
+			/* is RDS available */
+			if ((radio->registers[TUNECTRL] & RDSSYNC))
+				tavarua_q_event(radio, TAVARUA_EVT_RDS_AVAIL);
+			else
+				tavarua_q_event(radio,
+						TAVARUA_EVT_RDS_NOT_AVAIL);
+		}
+
+	} else {
+		if (radio->tune_req) {
+			FMDERR("Tune INT is pending\n");
+			mutex_unlock(&radio->lock);
+			return;
+		}
+	}
+	/* Search completed (read FREQ) */
+	if (radio->registers[STATUS_REG1] & SEARCH)
+		tavarua_q_event(radio, TAVARUA_EVT_SEEK_COMPLETE);
+
+	/* Scanning for next station */
+	if (radio->registers[STATUS_REG1] & SCANNEXT)
+		tavarua_q_event(radio, TAVARUA_EVT_SCAN_NEXT);
+
+	/* Signal indicator change (read SIGSTATE) */
+	if (radio->registers[STATUS_REG1] & SIGNAL) {
+		retval = tavarua_read_registers(radio, TUNECTRL, 1);
+		if (retval > -1) {
+			if (!(radio->registers[TUNECTRL] & SIGSTATE))
+				tavarua_q_event(radio, TAVARUA_EVT_BELOW_TH);
+			else
+				tavarua_q_event(radio, TAVARUA_EVT_ABOVE_TH);
+		}
+	}
+
+	/* RDS synchronization state change (read RDSSYNC) */
+	if (radio->registers[STATUS_REG1] & SYNC) {
+		retval = tavarua_read_registers(radio, TUNECTRL, 1);
+		if (retval > -1) {
+			if ((radio->registers[TUNECTRL] & RDSSYNC))
+				tavarua_q_event(radio, TAVARUA_EVT_RDS_AVAIL);
+			else
+				tavarua_q_event(radio,
+						TAVARUA_EVT_RDS_NOT_AVAIL);
+		}
+	}
+
+	/* Audio Control indicator (read AUDIOIND) */
+	if (radio->registers[STATUS_REG1] & AUDIO) {
+		retval = tavarua_read_registers(radio, AUDIOIND, 1);
+		if (retval > -1) {
+			if ((radio->registers[AUDIOIND] & 0x01))
+				tavarua_q_event(radio, TAVARUA_EVT_STEREO);
+			else
+				tavarua_q_event(radio, TAVARUA_EVT_MONO);
+		}
+	}
+
+	/* interrupt register 2 */
+
+	/* New unread RDS data group available */
+	if (radio->registers[STATUS_REG2] & RDSDAT) {
+		FMDBG("Raw RDS Available\n");
+		tavarua_rds_read(radio);
+		tavarua_q_event(radio, TAVARUA_EVT_NEW_RAW_RDS);
+	}
+
+	/* New RDS Program Service Table available */
+	if (radio->registers[STATUS_REG2] & RDSPS) {
+		FMDBG("New PS RDS\n");
+		tavarua_start_xfr(radio, TAVARUA_XFR_PS_RDS, RDS_PS_0);
+	}
+
+	/* New RDS Radio Text available */
+	if (radio->registers[STATUS_REG2] & RDSRT) {
+		FMDBG("New RT RDS\n");
+		tavarua_start_xfr(radio, TAVARUA_XFR_RT_RDS, RDS_RT_0);
+	}
+
+	/* New RDS Radio Text available */
+	if (radio->registers[STATUS_REG2] & RDSAF) {
+		FMDBG("New AF RDS\n");
+		tavarua_start_xfr(radio, TAVARUA_XFR_AF_LIST, RDS_AF_0);
+	}
+	/* Trasmitter an RDS Group */
+	if (radio->registers[STATUS_REG2] & TXRDSDAT) {
+		FMDBG("New TXRDSDAT\n");
+		tavarua_q_event(radio, TAVARUA_EVT_TXRDSDAT);
+	}
+
+	/* Complete RDS buffer is available for transmission */
+	if (radio->registers[STATUS_REG2] & TXRDSDONE) {
+		FMDBG("New TXRDSDAT\n");
+		tavarua_q_event(radio, TAVARUA_EVT_TXRDSDONE);
+	}
+	/* interrupt register 3 */
+
+	/* Data transfer (XFR) completed */
+	if (radio->registers[STATUS_REG3] & TRANSFER) {
+		FMDBG("XFR Interrupt\n");
+		tavarua_read_registers(radio, XFRCTRL, XFR_REG_NUM+1);
+		FMDBG("XFRCTRL IS: %x\n", radio->registers[XFRCTRL]);
+		xfr_status = radio->registers[XFRCTRL];
+		switch (xfr_status) {
+		case RDS_PS_0:
+			FMDBG("PS Header\n");
+			copy_from_xfr(radio, TAVARUA_BUF_PS_RDS, 5);
+			radio->xfr_bytes_left = (radio->registers[XFRCTRL+1] &
+								0x0F) * 8;
+			FMDBG("PS RDS Length: %d\n", radio->xfr_bytes_left);
+			if ((radio->xfr_bytes_left > 0) &&
+			    (radio->xfr_bytes_left < 97))
+				request_read_xfr(radio,	RDS_PS_1);
+			else
+				radio->xfr_in_progress = 0;
+			break;
+		case RDS_PS_1:
+		case RDS_PS_2:
+		case RDS_PS_3:
+		case RDS_PS_4:
+		case RDS_PS_5:
+		case RDS_PS_6:
+			FMDBG("PS Data\n");
+			copy_from_xfr(radio, TAVARUA_BUF_PS_RDS, XFR_REG_NUM);
+			radio->xfr_bytes_left -= XFR_REG_NUM;
+			if (radio->xfr_bytes_left > 0) {
+				if ((xfr_status + 1) > RDS_PS_6)
+					request_read_xfr(radio,	RDS_PS_6);
+				else
+					request_read_xfr(radio,	xfr_status+1);
+			} else {
+				radio->xfr_in_progress = 0;
+				tavarua_q_event(radio, TAVARUA_EVT_NEW_PS_RDS);
+			}
+			break;
+		case RDS_RT_0:
+			FMDBG("RT Header\n");
+			copy_from_xfr(radio, TAVARUA_BUF_RT_RDS, 5);
+			radio->xfr_bytes_left = radio->registers[XFRCTRL+1]
+									& 0x7F;
+			FMDBG("RT RDS Length: %d\n", radio->xfr_bytes_left);
+			/*RT_1 to RT_4  16 byte registers so 64 bytes */
+			if ((radio->xfr_bytes_left > 0)
+					 && (radio->xfr_bytes_left < 65))
+				request_read_xfr(radio, RDS_RT_1);
+			break;
+		case RDS_RT_1:
+		case RDS_RT_2:
+		case RDS_RT_3:
+		case RDS_RT_4:
+			FMDBG("xfr interrupt RT data\n");
+			copy_from_xfr(radio, TAVARUA_BUF_RT_RDS, XFR_REG_NUM);
+			radio->xfr_bytes_left -= XFR_REG_NUM;
+			if (radio->xfr_bytes_left > 0)
+				request_read_xfr(radio,	xfr_status+1);
+			else {
+				radio->xfr_in_progress = 0;
+				tavarua_q_event(radio, TAVARUA_EVT_NEW_RT_RDS);
+			}
+			break;
+		case RDS_AF_0:
+			copy_from_xfr(radio, TAVARUA_BUF_AF_LIST,
+						XFR_REG_NUM);
+			radio->xfr_bytes_left = radio->registers[XFRCTRL+5]-11;
+			if (radio->xfr_bytes_left > 0)
+				request_read_xfr(radio,	RDS_AF_1);
+			else
+				radio->xfr_in_progress = 0;
+			break;
+		case RDS_AF_1:
+			copy_from_xfr(radio, TAVARUA_BUF_AF_LIST,
+						radio->xfr_bytes_left);
+			tavarua_q_event(radio, TAVARUA_EVT_NEW_AF_LIST);
+			radio->xfr_in_progress = 0;
+			break;
+		case RX_CONFIG:
+		case RADIO_CONFIG:
+		case RDS_CONFIG:
+			memcpy(radio->sync_xfr_regs,
+				&radio->registers[XFRCTRL+1], XFR_REG_NUM);
+			complete(&radio->sync_req_done);
+			break;
+		case RX_STATIONS_0:
+			FMDBG("Search list has %d stations\n",
+						radio->registers[XFRCTRL+1]);
+			radio->xfr_bytes_left = radio->registers[XFRCTRL+1]*2;
+			if (radio->xfr_bytes_left > 14) {
+				copy_from_xfr(radio, TAVARUA_BUF_SRCH_LIST,
+							XFR_REG_NUM);
+				request_read_xfr(radio,	RX_STATIONS_1);
+			} else if (radio->xfr_bytes_left) {
+				FMDBG("In else RX_STATIONS_0\n");
+				copy_from_xfr(radio, TAVARUA_BUF_SRCH_LIST,
+						radio->xfr_bytes_left+1);
+				tavarua_q_event(radio,
+						TAVARUA_EVT_NEW_SRCH_LIST);
+				radio->xfr_in_progress = 0;
+			}
+			break;
+		case RX_STATIONS_1:
+			FMDBG("In RX_STATIONS_1");
+			copy_from_xfr(radio, TAVARUA_BUF_SRCH_LIST,
+						radio->xfr_bytes_left);
+			tavarua_q_event(radio, TAVARUA_EVT_NEW_SRCH_LIST);
+			radio->xfr_in_progress = 0;
+			break;
+		case PHY_TXGAIN:
+			FMDBG("read PHY_TXGAIN is successful");
+			complete(&radio->sync_req_done);
+			break;
+		case (0x80 | RX_CONFIG):
+		case (0x80 | RADIO_CONFIG):
+		case (0x80 | RDS_CONFIG):
+		case (0x80 | INT_CTRL):
+			complete(&radio->sync_req_done);
+			break;
+		case (0x80 | RDS_RT_0):
+			FMDBG("RT Header Sent\n");
+			complete(&radio->sync_req_done);
+			break;
+		case (0x80 | RDS_RT_1):
+		case (0x80 | RDS_RT_2):
+		case (0x80 | RDS_RT_3):
+		case (0x80 | RDS_RT_4):
+			FMDBG("xfr interrupt RT data Sent\n");
+			complete(&radio->sync_req_done);
+			break;
+		/*TX Specific transfer */
+		case (0x80 | RDS_PS_0):
+			FMDBG("PS Header Sent\n");
+			complete(&radio->sync_req_done);
+			break;
+		case (0x80 | RDS_PS_1):
+		case (0x80 | RDS_PS_2):
+		case (0x80 | RDS_PS_3):
+		case (0x80 | RDS_PS_4):
+		case (0x80 | RDS_PS_5):
+		case (0x80 | RDS_PS_6):
+			FMDBG("xfr interrupt PS data Sent\n");
+			complete(&radio->sync_req_done);
+			break;
+		case (0x80 | PHY_TXGAIN):
+			FMDBG("write PHY_TXGAIN is successful");
+			complete(&radio->sync_req_done);
+			break;
+		default:
+			FMDERR("UNKNOWN XFR = %d\n", xfr_status);
+		}
+		if (!radio->xfr_in_progress)
+			start_pending_xfr(radio);
+
+	}
+
+	/* Error occurred. Read ERRCODE to determine cause */
+	if (radio->registers[STATUS_REG3] & ERROR) {
+#ifdef FM_DEBUG
+		unsigned char xfr_buf[XFR_REG_NUM];
+		int retval = sync_read_xfr(radio, ERROR_CODE, xfr_buf);
+		FMDBG("retval of ERROR_CODE read : %d\n", retval);
+#endif
+		FMDERR("ERROR STATE\n");
+	}
+
+	mutex_unlock(&radio->lock);
+	FMDBG("Work is done\n");
+
+}
+
+/*=============================================================================
+FUNCTION:  read_int_stat
+=============================================================================*/
+/**
+  This function is scheduled whenever there is an interrupt pending in interrupt
+  queue. i.e. kfmradio.
+
+  Whenever there is a GPIO interrupt, a delayed work will be queued in to the
+  'kfmradio' work queue. Upon execution of this work in the queue, a  a call
+  to read_int_stat function will be made , which would in turn handle the
+  interrupts by reading the INTSTATx registers.
+  NOTE:
+  Tasks to be run out of a workqueue need to be packaged in a struct
+  work_struct structure.
+
+  @param work: work_struct structure.
+
+  @return None.
+*/
+static void read_int_stat(struct work_struct *work)
+{
+	struct tavarua_device *radio = container_of(work,
+					struct tavarua_device, work.work);
+	tavarua_handle_interrupts(radio);
+}
+
+static void fm_shutdown(struct work_struct *work)
+{
+	struct tavarua_device *radio = container_of(work,
+					struct tavarua_device, work.work);
+	FMDERR("%s: Releasing the FM I2S GPIO\n", __func__);
+	if (radio->pdata->config_i2s_gpio != NULL)
+		radio->pdata->config_i2s_gpio(FM_I2S_OFF);
+	FMDERR("%s: Shutting down FM SOC\n", __func__);
+	radio->pdata->fm_shutdown(radio->pdata);
+	complete(&radio->shutdown_done);
+}
+
+
+/*************************************************************************
+ * irq helper functions
+ ************************************************************************/
+
+/*=============================================================================
+FUNCTION:  tavarua_request_irq
+=============================================================================*/
+/**
+  This function is called to acquire a FM GPIO and enable FM interrupts.
+
+  @param radio: structure pointer passed by client.
+
+  @return 0 if success else otherwise.
+*/
+static int tavarua_request_irq(struct tavarua_device *radio)
+{
+	int retval;
+	int irq = radio->pdata->irq;
+	if (radio == NULL)
+		return -EINVAL;
+
+  /* A workqueue created with create_workqueue() will have one worker thread
+   * for each CPU on the system; create_singlethread_workqueue(), instead,
+   * creates a workqueue with a single worker process. The name of the queue
+   * is limited to ten characters; it is only used for generating the "command"
+   * for the kernel thread(s) (which can be seen in ps or top).
+   */
+  /* allocate an interrupt line */
+  /* On success, request_irq() returns 0 if everything goes  as
+     planned.  Your interrupt handler will start receiving its
+     interrupts immediately. On failure, request_irq()
+     returns:
+	-EINVAL
+		The  IRQ  number  you  requested  was either
+		invalid or reserved, or your passed  a  NULL
+		pointer for the handler() parameter.
+
+	-EBUSY The  IRQ you requested is already being
+		handled, and the IRQ cannot  be  shared.
+
+	-ENXIO The m68k returns this value for  an  invalid
+		IRQ number.
+  */
+	/* Use request_any_context_irq, So that it might work for nested or
+	nested interrupts. in MSM8x60, FM is connected to PMIC GPIO and it
+	is a nested interrupt*/
+	retval = request_any_context_irq(irq, tavarua_isr,
+				IRQ_TYPE_EDGE_FALLING, "fm interrupt", radio);
+	if (retval < 0) {
+		FMDERR("Couldn't acquire FM gpio %d\n", irq);
+		return retval;
+	} else {
+		FMDBG("FM GPIO %d registered\n", irq);
+	}
+	retval = enable_irq_wake(irq);
+	if (retval < 0) {
+		FMDERR("Could not enable FM interrupt\n ");
+		free_irq(irq , radio);
+	}
+	return retval;
+}
+
+/*=============================================================================
+FUNCTION:  tavarua_disable_irq
+=============================================================================*/
+/**
+  This function is called to disable FM irq and free up FM interrupt handling
+  resources.
+
+  @param radio: structure pointer passed by client.
+
+  @return 0 if success else otherwise.
+*/
+static int tavarua_disable_irq(struct tavarua_device *radio)
+{
+	int irq;
+	if (!radio)
+		return -EINVAL;
+	irq = radio->pdata->irq;
+	disable_irq_wake(irq);
+	free_irq(irq, radio);
+	cancel_delayed_work_sync(&radio->work);
+	flush_workqueue(radio->wqueue);
+	return 0;
+}
+
+static int optimized_search_algorithm(struct tavarua_device *radio,
+				int region)
+{
+	unsigned char adie_type_bahma;
+	int retval = 0;
+	unsigned int rdsMask = 0;
+	unsigned char value;
+
+	adie_type_bahma = is_bahama();
+
+	switch (region) {
+	case TAVARUA_REGION_US:
+	/*
+	Radio band for all the 200KHz channel-spaced regions
+	coming under EUROPE too, have been set as TAVARUA_REGION_US.
+	*/
+	FMDBG("%s: The region selected from APP is"
+		" : TAVARUA_REGION_US", __func__);
+	break;
+	case TAVARUA_REGION_EU:
+	/*
+	Radio band for all the 50KHz channel-spaced regions
+	coming under EUROPE, have been set as TAVARUA_REGION_EU.
+	*/
+	FMDBG("%s: The region selected from APP is : "
+		"TAVARUA_REGION_EU", __func__);
+	break;
+	case TAVARUA_REGION_JAPAN:
+	/*
+	Radio band for the 100KHz channel-spaced JAPAN region
+	has been set as TAVARUA_REGION_JAPAN.
+	*/
+	FMDBG("%s: The region selected from APP is"
+		" : TAVARUA_REGION_JAPAN", __func__);
+	break;
+	case TAVARUA_REGION_JAPAN_WIDE:
+	/*
+	Radio band for the 50KHz channel-spaced JAPAN WIDE region
+	has been set as TAVARUA_REGION_JAPAN_WIDE.
+	*/
+	FMDBG("%s: The region selected from APP is"
+		" : TAVARUA_REGION_JAPAN_WIDE", __func__);
+	break;
+	case TAVARUA_REGION_OTHER:
+	/*
+	Radio band for all the 100KHz channel-spaced regions
+	including those coming under EUROPE have been set as
+	TAVARUA_REGION_OTHER.
+	*/
+	FMDBG("%s: The region selected from APP is"
+		" : TAVARUA_REGION_OTHER", __func__);
+	break;
+	default:
+		pr_err("%s: Should not reach here.", __func__);
+		break;
+
+	}
+
+	/* Enable or Disable the 200KHz enforcer */
+	switch (region) {
+	case TAVARUA_REGION_US:
+	case TAVARUA_REGION_JAPAN:
+	case TAVARUA_REGION_OTHER:
+	/*
+	These are the 3 bands for which we need to enable the
+	200KHz enforcer in ADVCTL reg.
+	*/
+		if (adie_type_bahma) {
+			FMDBG("Adie type : Bahama\n");
+			FMDBG("%s: Enabling the 200KHz enforcer for"
+				" Region : %d", __func__, region);
+			/*Enable the 200KHz enforcer*/
+			retval = tavarua_read_registers(radio,
+				ADVCTRL, 1);
+			if (retval >= 0) {
+				rdsMask = radio->registers[ADVCTRL];
+				SET_REG_FIELD(rdsMask, ENF_SRCH200khz,
+					SRCH200KHZ_OFFSET, SRCH_MASK);
+				retval = tavarua_write_register(radio,
+					ADVCTRL, rdsMask);
+			} else
+				return retval;
+		} /* if Marimba do nothing */
+		break;
+	case TAVARUA_REGION_EU:
+	case TAVARUA_REGION_JAPAN_WIDE:
+	/*
+	These are the 2 bands for which we need to disable the
+	200KHz enforcer in ADVCTL reg.
+	Radio band for all the 50KHz channel-spaced regions
+	coming under EUROPE have been set as TAVARUA_REGION_EU.
+	*/
+		if (adie_type_bahma) {
+			FMDBG("Adie type : Bahama\n");
+			FMDBG("%s: Disabling the 200KHz enforcer for"
+				" Region : %d", __func__, region);
+			/*
+			Disable 200KHz enforcer for all 50 KHz
+			spaced regions.
+			*/
+			retval = tavarua_read_registers(radio,
+				ADVCTRL, 1);
+			if (retval >= 0) {
+				rdsMask = radio->registers[ADVCTRL];
+				SET_REG_FIELD(rdsMask, NO_SRCH200khz,
+					SRCH200KHZ_OFFSET, SRCH_MASK);
+				retval = tavarua_write_register(radio,
+					ADVCTRL, rdsMask);
+			} else
+				return retval;
+		} /* if Marimba do nothing */
+		break;
+	default:
+		FMDBG("%s: Defaulting in case of Enabling/Disabling"
+			"the 200KHz Enforcer", __func__);
+		break;
+	}
+
+	/* Set channel spacing */
+	switch (region) {
+	case TAVARUA_REGION_US:
+		if (adie_type_bahma) {
+			FMDBG("Adie type : Bahama\n");
+			/*
+			Configuring all 200KHZ spaced regions as 100KHz due to
+			change in the new Bahma FM SoC search algorithm.
+			*/
+			value = FM_CH_SPACE_100KHZ;
+		} else {
+			FMDBG("Adie type : Marimba\n");
+			value = FM_CH_SPACE_200KHZ;
+		}
+		break;
+	case TAVARUA_REGION_JAPAN:
+	case TAVARUA_REGION_OTHER:
+		if (adie_type_bahma) {
+			FMDBG("Adie type : Bahama\n");
+			FMDBG("%s: Configuring the channel-spacing as 50KHz"
+				"for the Region : %d", __func__, region);
+			/*
+			Configuring all 100KHZ spaced regions as 50KHz due to
+			change in the new Bahma FM SoC search algorithm.
+			*/
+			value = FM_CH_SPACE_50KHZ;
+		} else {
+			FMDBG("Adie type : Marimba\n");
+			value = FM_CH_SPACE_100KHZ;
+		}
+		break;
+	case TAVARUA_REGION_EU:
+	case TAVARUA_REGION_JAPAN_WIDE:
+		value = FM_CH_SPACE_50KHZ;
+		break;
+	default:
+		FMDBG("%s: Defualting in case of Channel-Spacing", __func__);
+		break;
+	}
+
+	SET_REG_FIELD(radio->registers[RDCTRL], value,
+		RDCTRL_CHSPACE_OFFSET, RDCTRL_CHSPACE_MASK);
+
+	return retval;
+}
+/*************************************************************************
+ * fops/IOCTL helper functions
+ ************************************************************************/
+
+/*=============================================================================
+FUNCTION:  tavarua_search
+=============================================================================*/
+/**
+  This interface sets the search control features.
+
+  @param radio: structure pointer passed by client.
+  @param on: The value of a control.
+  @param dir: FM search direction.
+
+  @return => 0 if successful.
+  @return < 0 if failure.
+*/
+static int tavarua_search(struct tavarua_device *radio, int on, int dir)
+{
+	enum search_t srch = radio->registers[SRCHCTRL] & SRCH_MODE;
+
+	FMDBG("In tavarua_search\n");
+	if (on) {
+		radio->registers[SRCHRDS1] = 0x00;
+		radio->registers[SRCHRDS2] = 0x00;
+		/* Set freq band */
+		switch (srch) {
+		case SCAN_FOR_STRONG:
+		case SCAN_FOR_WEAK:
+			radio->srch_params.get_list = 1;
+			radio->registers[SRCHRDS2] =
+					radio->srch_params.preset_num;
+			break;
+		case RDS_SEEK_PTY:
+		case RDS_SCAN_PTY:
+			radio->registers[SRCHRDS2] =
+					radio->srch_params.srch_pty;
+			break;
+		case RDS_SEEK_PI:
+			radio->registers[SRCHRDS1] =
+				(radio->srch_params.srch_pi & 0xFF00) >> 8;
+			radio->registers[SRCHRDS2] =
+				(radio->srch_params.srch_pi & 0x00FF);
+			break;
+		default:
+			break;
+		}
+		radio->registers[SRCHCTRL] |= SRCH_ON;
+	} else {
+		radio->registers[SRCHCTRL] &= ~SRCH_ON;
+		radio->srch_params.get_list = 0;
+	}
+	radio->registers[SRCHCTRL] = (dir << 3) |
+				(radio->registers[SRCHCTRL] & 0xF7);
+
+	FMDBG("SRCHCTRL <%x>\n", radio->registers[SRCHCTRL]);
+	FMDBG("Search Started\n");
+	return tavarua_write_registers(radio, SRCHRDS1,
+				&radio->registers[SRCHRDS1], 3);
+}
+
+/*=============================================================================
+FUNCTION:  tavarua_set_region
+=============================================================================*/
+/**
+  This interface configures the FM radio.
+
+  @param radio: structure pointer passed by client.
+  @param req_region: FM band types.  These types defines the FM band minimum and
+  maximum frequencies in the FM band.
+
+  @return => 0 if successful.
+  @return < 0 if failure.
+*/
+static int tavarua_set_region(struct tavarua_device *radio,
+				int req_region)
+{
+	int retval = 0;
+	unsigned int rdsMask = 0;
+	unsigned char xfr_buf[XFR_REG_NUM];
+	unsigned char value;
+	unsigned int spacing = 0.100 * FREQ_MUL;
+	unsigned int band_low, band_high;
+	unsigned int low_band_limit = 76.0 * FREQ_MUL;
+	enum tavarua_region_t region = req_region;
+	unsigned char adie_type_bahma;
+
+	adie_type_bahma = is_bahama();
+
+	/* Set freq band */
+	if (region == TAVARUA_REGION_JAPAN)
+		SET_REG_FIELD(radio->registers[RDCTRL], 1,
+			RDCTRL_BAND_OFFSET, RDCTRL_BAND_MASK);
+	else
+		SET_REG_FIELD(radio->registers[RDCTRL], 0,
+			RDCTRL_BAND_OFFSET, RDCTRL_BAND_MASK);
+
+	/* Set De-emphasis and soft band range*/
+	SET_REG_FIELD(radio->registers[RDCTRL], radio->region_params.emphasis,
+		RDCTRL_DEEMPHASIS_OFFSET, RDCTRL_DEEMPHASIS_MASK);
+
+	/* set RDS standard */
+	SET_REG_FIELD(radio->registers[RDSCTRL], radio->region_params.rds_std,
+		RDSCTRL_STANDARD_OFFSET, RDSCTRL_STANDARD_MASK);
+
+	FMDBG("RDSCTRLL %x\n", radio->registers[RDSCTRL]);
+	retval = tavarua_write_register(radio, RDSCTRL,
+					radio->registers[RDSCTRL]);
+	if (retval < 0) {
+		FMDERR("Failed to set RDS/RBDS standard\n");
+		return retval;
+	}
+
+	/* Set the lower and upper band limits*/
+	retval = sync_read_xfr(radio, RADIO_CONFIG, xfr_buf);
+	if (retval < 0) {
+		FMDERR("failed to get RADIO_CONFIG\n");
+		return retval;
+	}
+
+	band_low = (radio->region_params.band_low -
+				low_band_limit) / spacing;
+	band_high = (radio->region_params.band_high -
+				low_band_limit) / spacing;
+
+	xfr_buf[0] = RSH_DATA(band_low, 8);
+	xfr_buf[1] = GET_ABS_VAL(band_low);
+	xfr_buf[2] = RSH_DATA(band_high, 8);
+	xfr_buf[3] = GET_ABS_VAL(band_high);
+	xfr_buf[4] = 0; /* Active LOW */
+	retval = sync_write_xfr(radio, RADIO_CONFIG, xfr_buf);
+	if (retval < 0) {
+		FMDERR("Could not set regional settings\n");
+		return retval;
+	}
+	radio->region_params.region = region;
+
+	/* Check for the FM Algorithm used */
+	if (radio->enable_optimized_srch_alg) {
+		FMDBG("Optimized Srch Algorithm!!!");
+		optimized_search_algorithm(radio, region);
+	} else {
+		FMDBG("Native Srch Algorithm!!!");
+		/* Enable/Disable the 200KHz enforcer */
+		switch (region) {
+		case TAVARUA_REGION_US:
+			if (adie_type_bahma) {
+				FMDBG("Adie type : Bahama\n");
+				/*Enable the 200KHz enforcer*/
+				retval = tavarua_read_registers(radio,
+					ADVCTRL, 1);
+				if (retval >= 0) {
+					rdsMask = radio->registers[ADVCTRL];
+					SET_REG_FIELD(rdsMask, ENF_SRCH200khz,
+						SRCH200KHZ_OFFSET, SRCH_MASK);
+					retval = tavarua_write_register(radio,
+						ADVCTRL, rdsMask);
+				} else
+					return retval;
+			} /* if Marimba do nothing */
+			break;
+		case TAVARUA_REGION_EU:
+		case TAVARUA_REGION_JAPAN:
+		case TAVARUA_REGION_JAPAN_WIDE:
+		default:
+			if (adie_type_bahma) {
+				FMDBG("Adie type : Bahama\n");
+				/*
+				Disable 200KHz enforcer for all 100/50 KHz
+				spaced regions.
+				*/
+				retval = tavarua_read_registers(radio,
+					ADVCTRL, 1);
+				if (retval >= 0) {
+					rdsMask = radio->registers[ADVCTRL];
+					SET_REG_FIELD(rdsMask, NO_SRCH200khz,
+						SRCH200KHZ_OFFSET, SRCH_MASK);
+					retval = tavarua_write_register(radio,
+						ADVCTRL, rdsMask);
+				} else
+					return retval;
+			} /* if Marimba do nothing */
+			break;
+		}
+
+		/* Set channel spacing */
+		if (region == TAVARUA_REGION_US) {
+			if (adie_type_bahma) {
+				FMDBG("Adie type : Bahama\n");
+				/*
+				Configuring all 200KHZ spaced regions as
+				100KHz due to change in the new Bahma
+				FM SoC search algorithm.
+				*/
+				value = FM_CH_SPACE_100KHZ;
+			} else {
+				FMDBG("Adie type : Marimba\n");
+				value = FM_CH_SPACE_200KHZ;
+			}
+		} else {
+			/*
+			Set the channel spacing as configured from
+			the upper layers.
+			*/
+			value = radio->region_params.spacing;
+		}
+		SET_REG_FIELD(radio->registers[RDCTRL], value,
+			RDCTRL_CHSPACE_OFFSET, RDCTRL_CHSPACE_MASK);
+
+	}
+
+	/* Write the config values into RDCTL register */
+	FMDBG("RDCTRL: %x\n", radio->registers[RDCTRL]);
+	retval = tavarua_write_register(radio, RDCTRL,
+					radio->registers[RDCTRL]);
+	if (retval < 0) {
+		FMDERR("Could not set region in rdctrl\n");
+		return retval;
+	}
+
+	return retval;
+}
+
+/*=============================================================================
+FUNCTION:  tavarua_get_freq
+=============================================================================*/
+/**
+  This interface gets the current frequency.
+
+  @param radio: structure pointer passed by client.
+  @param freq: struct v4l2_frequency. This will be set to the resultant
+  frequency in units of 62.5 kHz on success.
+
+  NOTE:
+  To get the current tuner or modulator radio frequency applications set the
+  tuner field of a struct v4l2_frequency to the respective tuner or modulator
+  number (only input devices have tuners, only output devices have modulators),
+  zero out the reserved array and call the VIDIOC_G_FREQUENCY ioctl with a
+  pointer to this structure. The driver stores the current frequency in the
+  frequency field.
+
+  Tuning frequency is in units of 62.5 kHz, or if the struct v4l2_tuner or
+  struct v4l2_modulator capabilities flag V4L2_TUNER_CAP_LOW is set, in
+  units of 62.5 Hz.
+
+  @return => 0 if successful.
+  @return < 0 if failure.
+*/
+static int tavarua_get_freq(struct tavarua_device *radio,
+				struct v4l2_frequency *freq)
+{
+	int retval;
+	unsigned short chan;
+	unsigned int band_bottom;
+	unsigned int spacing;
+	band_bottom = radio->region_params.band_low;
+	spacing  = 0.100 * FREQ_MUL;
+	/* read channel */
+	retval = tavarua_read_registers(radio, FREQ, 2);
+	chan = radio->registers[FREQ];
+
+	/* Frequency (MHz) = 100 (kHz) x Channel + Bottom of Band (MHz) */
+	freq->frequency = spacing * chan + band_bottom;
+	if (radio->registers[TUNECTRL] & ADD_OFFSET)
+		freq->frequency += 800;
+	return retval;
+}
+
+/*=============================================================================
+FUNCTION:  tavarua_set_freq
+=============================================================================*/
+/**
+  This interface sets the current frequency.
+
+  @param radio: structure pointer passed by client.
+  @param freq: desired frequency sent by the client in 62.5 kHz units.
+
+  NOTE:
+  To change the current tuner or modulator radio frequency, applications
+  initialize the tuner, type and frequency fields, and the reserved array of a
+  struct v4l2_frequency and call the VIDIOC_S_FREQUENCY ioctl with a pointer to
+  this structure. When the requested frequency is not possible the driver
+  assumes the closest possible value. However VIDIOC_S_FREQUENCY is a
+  write-only ioctl, it does not return the actual new frequency.
+
+  Tuning frequency is in units of 62.5 kHz, or if the struct v4l2_tuner
+  or struct v4l2_modulator capabilities flag V4L2_TUNER_CAP_LOW is set,
+  in units of 62.5 Hz.
+
+  @return => 0 if successful.
+  @return < 0 if failure.
+*/
+static int tavarua_set_freq(struct tavarua_device *radio, unsigned int freq)
+{
+
+	unsigned int band_bottom;
+	unsigned char chan;
+	unsigned char cmd[] = {0x00, 0x00};
+	unsigned int spacing;
+	int retval;
+	band_bottom = radio->region_params.band_low;
+	spacing  = 0.100 * FREQ_MUL;
+	if ((freq % 1600) == 800) {
+		cmd[1] = ADD_OFFSET;
+		freq -= 800;
+	}
+	/* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / 100 (kHz) */
+	chan = (freq - band_bottom) / spacing;
+
+	cmd[0] = chan;
+	cmd[1] |= TUNE_STATION;
+	radio->tune_req = 1;
+	retval = tavarua_write_registers(radio, FREQ, cmd, 2);
+	if (retval < 0)
+		radio->tune_req = 0;
+	return retval;
+
+}
+
+/**************************************************************************
+ * File Operations Interface
+ *************************************************************************/
+
+/*=============================================================================
+FUNCTION:  tavarua_fops_read
+=============================================================================*/
+/**
+  This function is called when a process, which already opened the dev file,
+  attempts to read from it.
+
+  In case of tavarua driver, it is called to read RDS data.
+
+  @param file: file descriptor.
+	@param buf: The buffer to fill with data.
+	@param count: The length of the buffer in bytes.
+	@param ppos: Our offset in the file.
+
+  @return The number of bytes put into the buffer on sucess.
+	-EFAULT if there is no access to user buffer
+*/
+static ssize_t tavarua_fops_read(struct file *file, char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
+	struct kfifo *rds_buf = &radio->data_buf[TAVARUA_BUF_RAW_RDS];
+
+	/* block if no new data available */
+	while (!kfifo_len(rds_buf)) {
+		if (file->f_flags & O_NONBLOCK)
+			return -EWOULDBLOCK;
+		if (wait_event_interruptible(radio->read_queue,
+			kfifo_len(rds_buf)) < 0)
+			return -EINTR;
+	}
+
+	/* calculate block count from byte count */
+	count /= BYTES_PER_BLOCK;
+
+
+	/* check if we can write to the user buffer */
+	if (!access_ok(VERIFY_WRITE, buf, count*BYTES_PER_BLOCK))
+		return -EFAULT;
+
+	/* copy RDS block out of internal buffer and to user buffer */
+	return kfifo_out_locked(rds_buf, buf, count*BYTES_PER_BLOCK,
+				&radio->buf_lock[TAVARUA_BUF_RAW_RDS]);
+}
+
+/*=============================================================================
+FUNCTION:  tavarua_fops_write
+=============================================================================*/
+/**
+  This function is called when a process, which already opened the dev file,
+  attempts to write to it.
+
+  In case of tavarua driver, it is called to write RDS data to host.
+
+  @param file: file descriptor.
+	@param buf: The buffer which has data to write.
+	@param count: The length of the buffer.
+	@param ppos: Our offset in the file.
+
+  @return The number of bytes written from the buffer.
+*/
+static ssize_t tavarua_fops_write(struct file *file, const char __user *data,
+			size_t count, loff_t *ppos)
+{
+	struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
+	int retval = 0;
+	int bytes_to_copy;
+	int bytes_copied = 0;
+	int bytes_left;
+	int chunk_index = 0;
+	unsigned char tx_data[XFR_REG_NUM];
+	/* Disable TX of this type first */
+	switch (radio->tx_mode) {
+	case TAVARUA_TX_RT:
+		bytes_left = min((int)count, MAX_RT_LENGTH);
+		tx_data[1] = 0;
+		break;
+	case TAVARUA_TX_PS:
+		bytes_left = min((int)count, MAX_PS_LENGTH);
+		tx_data[4] = 0;
+		break;
+	default:
+		FMDERR("%s: Unknown TX mode\n", __func__);
+		return -1;
+	}
+	retval = sync_write_xfr(radio, radio->tx_mode, tx_data);
+	if (retval < 0)
+		return retval;
+
+	/* send payload to FM hardware */
+	while (bytes_left) {
+		chunk_index++;
+		bytes_to_copy = min(bytes_left, XFR_REG_NUM);
+		if (copy_from_user(tx_data, data + bytes_copied, bytes_to_copy))
+			return -EFAULT;
+		retval = sync_write_xfr(radio, radio->tx_mode +
+						chunk_index, tx_data);
+		if (retval < 0)
+			return retval;
+
+		bytes_copied += bytes_to_copy;
+		bytes_left -= bytes_to_copy;
+	}
+
+	/* send the header */
+	switch (radio->tx_mode) {
+	case TAVARUA_TX_RT:
+		FMDBG("Writing RT header\n");
+		tx_data[0] = bytes_copied;
+		tx_data[1] = TX_ON | 0x03; /* on | PTY */
+		tx_data[2] = 0x12; /* PI high */
+		tx_data[3] = 0x34; /* PI low */
+		break;
+	case TAVARUA_TX_PS:
+		FMDBG("Writing PS header\n");
+		tx_data[0] = chunk_index;
+		tx_data[1] = 0x03; /* PTY */
+		tx_data[2] = 0x12; /* PI high */
+		tx_data[3] = 0x34; /* PI low */
+		tx_data[4] = TX_ON | 0x01;
+		break;
+	default:
+		FMDERR("%s: Unknown TX mode\n", __func__);
+		return -1;
+	}
+	retval = sync_write_xfr(radio, radio->tx_mode, tx_data);
+	if (retval < 0)
+		return retval;
+	FMDBG("done writing: %d\n", retval);
+	return bytes_copied;
+}
+
+/*=============================================================================
+FUNCTION:  tavarua_fops_open
+=============================================================================*/
+/**
+  This function is called when a process tries to open the device file, like
+	"cat /dev/mycharfile"
+
+  @param file: file descriptor.
+
+  @return => 0 if successful.
+  @return < 0 if failure.
+*/
+static int tavarua_fops_open(struct file *file)
+{
+	struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
+	int retval = -ENODEV;
+	unsigned char value;
+	/* FM core bring up */
+	int i = 0;
+	char fm_ctl0_part1[] = { 0xCA, 0xCE, 0xD6 };
+	char fm_ctl1[] = { 0x03 };
+	char fm_ctl0_part2[] = { 0xB6, 0xB7 };
+	char buffer[] = {0x00, 0x48, 0x8A, 0x8E, 0x97, 0xB7};
+	int bahama_present = -ENODEV;
+
+	INIT_DELAYED_WORK(&radio->work, read_int_stat);
+	if (!atomic_dec_and_test(&radio->users)) {
+		pr_err("%s: Device already in use."
+			"Try again later", __func__);
+		atomic_inc(&radio->users);
+		return -EBUSY;
+	}
+
+	/* initial gpio pin config & Power up */
+	retval = radio->pdata->fm_setup(radio->pdata);
+	if (retval) {
+		printk(KERN_ERR "%s: failed config gpio & pmic\n", __func__);
+		goto open_err_setup;
+	}
+	if (radio->pdata->config_i2s_gpio != NULL) {
+		retval = radio->pdata->config_i2s_gpio(FM_I2S_ON);
+		if (retval) {
+			printk(KERN_ERR "%s: failed config gpio\n", __func__);
+			goto config_i2s_err;
+		}
+	}
+	/* enable irq */
+	retval = tavarua_request_irq(radio);
+	if (retval < 0) {
+		printk(KERN_ERR "%s: failed to request irq\n", __func__);
+		goto open_err_req_irq;
+	}
+	/* call top level marimba interface here to enable FM core */
+	FMDBG("initializing SoC\n");
+
+	bahama_present = is_bahama();
+
+	if (bahama_present == -ENODEV)
+		return -ENODEV;
+
+	if (bahama_present)
+		radio->marimba->mod_id = SLAVE_ID_BAHAMA;
+	else
+		radio->marimba->mod_id = MARIMBA_SLAVE_ID_MARIMBA;
+
+	value = FM_ENABLE;
+	retval = marimba_write_bit_mask(radio->marimba,
+			MARIMBA_XO_BUFF_CNTRL, &value, 1, value);
+	if (retval < 0) {
+		printk(KERN_ERR "%s:XO_BUFF_CNTRL write failed\n",
+					__func__);
+		goto open_err_all;
+	}
+
+
+	/* Bring up FM core */
+	if (bahama_present)	{
+
+		radio->marimba->mod_id = SLAVE_ID_BAHAMA;
+		/* Read the Bahama version*/
+		retval = marimba_read_bit_mask(radio->marimba,
+				0x00,  &bahama_version, 1, 0x1F);
+		if (retval < 0) {
+			printk(KERN_ERR "%s: version read failed",
+				__func__);
+			goto open_err_all;
+		}
+
+		/* Check for Bahama V2 variant*/
+		if (bahama_version == 0x09)	{
+
+			/* In case of Bahama v2, forcefully enable the
+			 * internal analog and digital voltage controllers
+			 */
+			value = 0x06;
+			/* value itself used as mask in these writes*/
+			retval = marimba_write_bit_mask(radio->marimba,
+			BAHAMA_LDO_DREG_CTL0, &value, 1, value);
+			if (retval < 0) {
+				printk(KERN_ERR "%s:0xF0 write failed\n",
+					__func__);
+				goto open_err_all;
+			}
+			value = 0x86;
+			retval = marimba_write_bit_mask(radio->marimba,
+				BAHAMA_LDO_AREG_CTL0, &value, 1, value);
+			if (retval < 0) {
+				printk(KERN_ERR "%s:0xF4 write failed\n",
+					__func__);
+				goto open_err_all;
+			}
+		}
+
+		/*write FM mode*/
+		retval = tavarua_write_register(radio, BAHAMA_FM_MODE_REG,
+					BAHAMA_FM_MODE_NORMAL);
+		if (retval < 0) {
+			printk(KERN_ERR "failed to set the FM mode: %d\n",
+					retval);
+			goto open_err_all;
+		}
+		/*Write first sequence of bytes to FM_CTL0*/
+		for (i = 0; i < 3; i++)  {
+			retval = tavarua_write_register(radio,
+					BAHAMA_FM_CTL0_REG, fm_ctl0_part1[i]);
+			if (retval < 0) {
+				printk(KERN_ERR "FM_CTL0:set-1 failure: %d\n",
+							retval);
+				goto open_err_all;
+			}
+		}
+		/*Write the FM_CTL1 sequence*/
+		for (i = 0; i < 1; i++)  {
+			retval = tavarua_write_register(radio,
+					BAHAMA_FM_CTL1_REG, fm_ctl1[i]);
+			if (retval < 0) {
+				printk(KERN_ERR "FM_CTL1 write failure: %d\n",
+							retval);
+				goto open_err_all;
+			}
+		}
+		/*Write second sequence of bytes to FM_CTL0*/
+		for (i = 0; i < 2; i++)  {
+			retval = tavarua_write_register(radio,
+					BAHAMA_FM_CTL0_REG, fm_ctl0_part2[i]);
+			if (retval < 0) {
+				printk(KERN_ERR "FM_CTL0:set-2 failure: %d\n",
+					retval);
+			goto open_err_all;
+			}
+		}
+	} else {
+		retval = tavarua_write_registers(radio, LEAKAGE_CNTRL,
+						buffer, 6);
+		if (retval < 0) {
+			printk(KERN_ERR "%s: failed to bring up FM Core\n",
+						__func__);
+			goto open_err_all;
+		}
+	}
+	/* Wait for interrupt i.e. complete(&radio->sync_req_done); call */
+	/*Initialize the completion variable for
+	for the proper behavior*/
+	init_completion(&radio->sync_req_done);
+	if (!wait_for_completion_timeout(&radio->sync_req_done,
+		msecs_to_jiffies(wait_timeout))) {
+		retval = -1;
+		FMDERR("Timeout waiting for initialization\n");
+	}
+
+	/* get Chip ID */
+	retval = tavarua_write_register(radio, XFRCTRL, CHIPID);
+	if (retval < 0)
+		goto open_err_all;
+	msleep(TAVARUA_DELAY);
+	tavarua_read_registers(radio, XFRCTRL, XFR_REG_NUM+1);
+	if (radio->registers[XFRCTRL] != CHIPID)
+		goto open_err_all;
+
+	radio->chipID = (radio->registers[XFRCTRL+2] << 24) |
+			(radio->registers[XFRCTRL+5] << 16) |
+			(radio->registers[XFRCTRL+6] << 8)  |
+			(radio->registers[XFRCTRL+7]);
+
+	printk(KERN_WARNING DRIVER_NAME ": Chip ID %x\n", radio->chipID);
+	if (radio->chipID == MARIMBA_A0) {
+		printk(KERN_WARNING DRIVER_NAME ": Unsupported hardware: %x\n",
+						radio->chipID);
+		retval = -1;
+		goto open_err_all;
+	}
+
+	radio->handle_irq = 0;
+	radio->marimba->mod_id = SLAVE_ID_BAHAMA;
+	marimba_set_fm_status(radio->marimba, true);
+	return 0;
+
+
+open_err_all:
+    /*Disable FM in case of error*/
+	value = 0x00;
+	marimba_write_bit_mask(radio->marimba, MARIMBA_XO_BUFF_CNTRL,
+							&value, 1, value);
+	tavarua_disable_irq(radio);
+open_err_req_irq:
+	if (radio->pdata->config_i2s_gpio != NULL)
+		radio->pdata->config_i2s_gpio(FM_I2S_OFF);
+config_i2s_err:
+	radio->pdata->fm_shutdown(radio->pdata);
+open_err_setup:
+	radio->handle_irq = 1;
+	atomic_inc(&radio->users);
+	return retval;
+}
+
+/*=============================================================================
+FUNCTION:  tavarua_fops_release
+=============================================================================*/
+/**
+  This function is called when a process closes the device file.
+
+  @param file: file descriptor.
+
+  @return => 0 if successful.
+  @return < 0 if failure.
+*/
+static int tavarua_fops_release(struct file *file)
+{
+	int retval;
+	struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
+	unsigned char value;
+	int i = 0;
+	/*FM Core shutdown sequence for Bahama*/
+	char fm_ctl0_part1[] = { 0xB7 };
+	char fm_ctl1[] = { 0x03 };
+	char fm_ctl0_part2[] = { 0x9F, 0x48, 0x02 };
+	int bahama_present = -ENODEV;
+	/*FM Core shutdown sequence for Marimba*/
+	char buffer[] = {0x18, 0xB7, 0x48};
+	bool bt_status = false;
+	int index;
+	/* internal regulator controllers DREG_CTL0, AREG_CTL0
+	 * has to be kept in the valid state based on the bt status.
+	 * 1st row is the state when no clients are active,
+	 * and the second when bt is in on state.
+	 */
+	char internal_vreg_ctl[2][2] = {
+		{ 0x04, 0x84 },
+		{ 0x00, 0x80 }
+	};
+
+	if (!radio) {
+		pr_err("%s: Radio device not available...", __func__);
+		return -ENODEV;
+	}
+
+	FMDBG("In %s", __func__);
+
+	FMDBG("%s, Disabling the IRQs\n", __func__);
+	/* disable irq */
+	retval = tavarua_disable_irq(radio);
+	if (retval < 0) {
+		printk(KERN_ERR "%s: failed to disable irq\n", __func__);
+		return retval;
+	}
+
+	/* disable radio ctrl */
+	retval = tavarua_write_register(radio, RDCTRL, 0x00);
+	if (retval < 0) {
+		printk(KERN_ERR "%s: failed to disable FM\n", __func__);
+		return retval;
+	}
+
+	init_completion(&radio->shutdown_done);
+
+	bahama_present = is_bahama();
+
+	if (bahama_present == -ENODEV)
+		return -ENODEV;
+
+	INIT_DELAYED_WORK(&radio->work, fm_shutdown);
+
+	if (bahama_present)	{
+		/*Write first sequence of bytes to FM_CTL0*/
+		for (i = 0; i < 1; i++) {
+			retval = tavarua_write_register(radio,
+					BAHAMA_FM_CTL0_REG, fm_ctl0_part1[i]);
+			if (retval < 0) {
+				printk(KERN_ERR "FM_CTL0:Set-1 failure: %d\n",
+						retval);
+				break;
+			}
+		}
+		/*Write the FM_CTL1 sequence*/
+		for (i = 0; i < 1; i++)  {
+			retval = tavarua_write_register(radio,
+					BAHAMA_FM_CTL1_REG, fm_ctl1[i]);
+			if (retval < 0) {
+				printk(KERN_ERR "FM_CTL1 failure: %d\n",
+						retval);
+				break;
+			}
+		}
+		/*Write second sequence of bytes to FM_CTL0*/
+		for (i = 0; i < 3; i++)   {
+			retval = tavarua_write_register(radio,
+					BAHAMA_FM_CTL0_REG, fm_ctl0_part2[i]);
+			if (retval < 0) {
+				printk(KERN_ERR "FM_CTL0:Set-2 failure: %d\n",
+						retval);
+			break;
+			}
+		}
+	}	else	{
+
+		retval = tavarua_write_registers(radio, FM_CTL0,
+				buffer, sizeof(buffer)/sizeof(buffer[0]));
+		if (retval < 0) {
+			printk(KERN_ERR "%s: failed to bring down the  FM Core\n",
+							__func__);
+			return retval;
+		}
+	}
+	radio->marimba->mod_id = SLAVE_ID_BAHAMA;
+	bt_status = marimba_get_bt_status(radio->marimba);
+	/* Set the index based on the bt status*/
+	index = bt_status ?  1 : 0;
+	/* Check for Bahama's existance and Bahama V2 variant*/
+	if (bahama_present && (bahama_version == 0x09))   {
+		radio->marimba->mod_id = SLAVE_ID_BAHAMA;
+		/* actual value itself used as mask*/
+		retval = marimba_write_bit_mask(radio->marimba,
+			BAHAMA_LDO_DREG_CTL0, &internal_vreg_ctl[bt_status][0],
+			 1, internal_vreg_ctl[index][0]);
+		if (retval < 0) {
+			printk(KERN_ERR "%s:0xF0 write failed\n", __func__);
+			goto exit;
+		}
+		/* actual value itself used as mask*/
+		retval = marimba_write_bit_mask(radio->marimba,
+			BAHAMA_LDO_AREG_CTL0, &internal_vreg_ctl[bt_status][1],
+			1, internal_vreg_ctl[index][1]);
+		if (retval < 0) {
+			printk(KERN_ERR "%s:0xF4 write failed\n", __func__);
+			goto exit;
+		}
+	} else    {
+		/* disable fm core */
+		radio->marimba->mod_id = MARIMBA_SLAVE_ID_MARIMBA;
+	}
+
+	value = 0x00;
+	retval = marimba_write_bit_mask(radio->marimba, MARIMBA_XO_BUFF_CNTRL,
+							&value, 1, FM_ENABLE);
+	if (retval < 0) {
+		printk(KERN_ERR "%s:XO_BUFF_CNTRL write failed\n", __func__);
+		goto exit;
+	}
+exit:
+	FMDBG("%s, Calling fm_shutdown\n", __func__);
+	queue_delayed_work(radio->wqueue, &radio->work,
+				msecs_to_jiffies(TAVARUA_DELAY/2));
+	/* teardown gpio and pmic */
+	marimba_set_fm_status(radio->marimba, false);
+	wait_for_completion(&radio->shutdown_done);
+	radio->handle_irq = 1;
+	radio->lp_mode = 1;
+	atomic_inc(&radio->users);
+	radio->marimba->mod_id = SLAVE_ID_BAHAMA;
+	flush_workqueue(radio->wqueue);
+	return retval;
+}
+
+/*
+ * tavarua_fops - file operations interface
+ */
+static const struct v4l2_file_operations tavarua_fops = {
+	.owner = THIS_MODULE,
+	.read = tavarua_fops_read,
+	.write = tavarua_fops_write,
+	.ioctl = video_ioctl2,
+	.open  = tavarua_fops_open,
+	.release = tavarua_fops_release,
+};
+
+/*************************************************************************
+ * Video4Linux Interface
+ *************************************************************************/
+
+/*
+ * tavarua_v4l2_queryctrl - query control
+ */
+static struct v4l2_queryctrl tavarua_v4l2_queryctrl[] = {
+	{
+		.id	       = V4L2_CID_AUDIO_VOLUME,
+		.type	       = V4L2_CTRL_TYPE_INTEGER,
+		.name	       = "Volume",
+		.minimum       = 0,
+		.maximum       = 15,
+		.step	       = 1,
+		.default_value = 15,
+	},
+	{
+		.id	       = V4L2_CID_AUDIO_BALANCE,
+		.flags	       = V4L2_CTRL_FLAG_DISABLED,
+	},
+	{
+		.id	       = V4L2_CID_AUDIO_BASS,
+		.flags	       = V4L2_CTRL_FLAG_DISABLED,
+	},
+	{
+		.id	       = V4L2_CID_AUDIO_TREBLE,
+		.flags	       = V4L2_CTRL_FLAG_DISABLED,
+	},
+	{
+		.id	       = V4L2_CID_AUDIO_MUTE,
+		.type	       = V4L2_CTRL_TYPE_BOOLEAN,
+		.name	       = "Mute",
+		.minimum       = 0,
+		.maximum       = 1,
+		.step	       = 1,
+		.default_value = 1,
+	},
+	{
+		.id	       = V4L2_CID_AUDIO_LOUDNESS,
+		.flags	       = V4L2_CTRL_FLAG_DISABLED,
+	},
+	{
+		.id	       = V4L2_CID_PRIVATE_TAVARUA_SRCHMODE,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+		.name	       = "Search mode",
+		.minimum       = 0,
+		.maximum       = 7,
+		.step	       = 1,
+		.default_value = 0,
+	},
+	{
+		.id            = V4L2_CID_PRIVATE_TAVARUA_SCANDWELL,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+		.name          = "Search dwell time",
+		.minimum       = 0,
+		.maximum       = 7,
+		.step          = 1,
+		.default_value = 0,
+	},
+	{
+		.id            = V4L2_CID_PRIVATE_TAVARUA_SRCHON,
+		.type          = V4L2_CTRL_TYPE_BOOLEAN,
+		.name          = "Search on/off",
+		.minimum       = 0,
+		.maximum       = 1,
+		.step          = 1,
+		.default_value = 1,
+
+	},
+	{
+		.id            = V4L2_CID_PRIVATE_TAVARUA_STATE,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+		.name          = "radio 0ff/rx/tx/reset",
+		.minimum       = 0,
+		.maximum       = 3,
+		.step          = 1,
+		.default_value = 1,
+
+	},
+	{
+		.id            = V4L2_CID_PRIVATE_TAVARUA_REGION,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+		.name          = "radio standard",
+		.minimum       = 0,
+		.maximum       = 2,
+		.step          = 1,
+		.default_value = 0,
+	},
+	{
+		.id            = V4L2_CID_PRIVATE_TAVARUA_SIGNAL_TH,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+		.name          = "Signal Threshold",
+		.minimum       = 0x80,
+		.maximum       = 0x7F,
+		.step          = 1,
+		.default_value = 0,
+	},
+	{
+		.id            = V4L2_CID_PRIVATE_TAVARUA_SRCH_PTY,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+		.name          = "Search PTY",
+		.minimum       = 0,
+		.maximum       = 31,
+		.default_value = 0,
+	},
+	{
+		.id            = V4L2_CID_PRIVATE_TAVARUA_SRCH_PI,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+		.name          = "Search PI",
+		.minimum       = 0,
+		.maximum       = 0xFF,
+		.default_value = 0,
+	},
+	{
+		.id            = V4L2_CID_PRIVATE_TAVARUA_SRCH_CNT,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+		.name          = "Preset num",
+		.minimum       = 0,
+		.maximum       = 12,
+		.default_value = 0,
+	},
+	{
+		.id            = V4L2_CID_PRIVATE_TAVARUA_EMPHASIS,
+		.type          = V4L2_CTRL_TYPE_BOOLEAN,
+		.name          = "Emphasis",
+		.minimum       = 0,
+		.maximum       = 1,
+		.default_value = 0,
+	},
+	{
+		.id            = V4L2_CID_PRIVATE_TAVARUA_RDS_STD,
+		.type          = V4L2_CTRL_TYPE_BOOLEAN,
+		.name          = "RDS standard",
+		.minimum       = 0,
+		.maximum       = 1,
+		.default_value = 0,
+	},
+	{
+		.id            = V4L2_CID_PRIVATE_TAVARUA_SPACING,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+		.name          = "Channel spacing",
+		.minimum       = 0,
+		.maximum       = 2,
+		.default_value = 0,
+	},
+	{
+		.id            = V4L2_CID_PRIVATE_TAVARUA_RDSON,
+		.type          = V4L2_CTRL_TYPE_BOOLEAN,
+		.name          = "RDS on/off",
+		.minimum       = 0,
+		.maximum       = 1,
+		.default_value = 0,
+	},
+	{
+		.id            = V4L2_CID_PRIVATE_TAVARUA_RDSGROUP_MASK,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+		.name          = "RDS group mask",
+		.minimum       = 0,
+		.maximum       = 0xFFFFFFFF,
+		.default_value = 0,
+	},
+	{
+		.id            = V4L2_CID_PRIVATE_TAVARUA_RDSGROUP_PROC,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+		.name          = "RDS processing",
+		.minimum       = 0,
+		.maximum       = 0xFF,
+		.default_value = 0,
+	},
+	{
+		.id            = V4L2_CID_PRIVATE_TAVARUA_RDSD_BUF,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+		.name          = "RDS data groups to buffer",
+		.minimum       = 1,
+		.maximum       = 21,
+		.default_value = 0,
+	},
+	{
+		.id            = V4L2_CID_PRIVATE_TAVARUA_PSALL,
+		.type          = V4L2_CTRL_TYPE_BOOLEAN,
+		.name          = "pass all ps strings",
+		.minimum       = 0,
+		.maximum       = 1,
+		.default_value = 0,
+	},
+	{
+		.id            = V4L2_CID_PRIVATE_TAVARUA_LP_MODE,
+		.type          = V4L2_CTRL_TYPE_BOOLEAN,
+		.name          = "Low power mode",
+		.minimum       = 0,
+		.maximum       = 1,
+		.default_value = 0,
+	},
+	{
+		.id            = V4L2_CID_PRIVATE_TAVARUA_ANTENNA,
+		.type          = V4L2_CTRL_TYPE_BOOLEAN,
+		.name          = "headset/internal",
+		.minimum       = 0,
+		.maximum       = 1,
+		.default_value = 0,
+	},
+	/* Private controls for FM TX*/
+	{
+		.id            = V4L2_CID_PRIVATE_TAVARUA_TX_SETPSREPEATCOUNT,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+		.name          = "Set PS REPEATCOUNT",
+		.minimum       = 0,
+		.maximum       = 15,
+	},
+	{
+		.id            = V4L2_CID_PRIVATE_TAVARUA_STOP_RDS_TX_PS_NAME,
+		.type          = V4L2_CTRL_TYPE_BOOLEAN,
+		.name          = "Stop PS NAME",
+		.minimum       = 0,
+		.maximum       = 1,
+	},
+	{
+		.id            = V4L2_CID_PRIVATE_TAVARUA_STOP_RDS_TX_RT,
+		.type          = V4L2_CTRL_TYPE_BOOLEAN,
+		.name          = "Stop RT",
+		.minimum       = 0,
+		.maximum       = 1,
+	},
+	{	.id	       = V4L2_CID_PRIVATE_SET_NOTCH_FILTER,
+		.type	       = V4L2_CTRL_TYPE_INTEGER,
+		.name	       = "Notch filter",
+		.minimum       = 0,
+		.maximum       = 2,
+	},
+
+};
+
+/*=============================================================================
+FUNCTION:  tavarua_vidioc_querycap
+=============================================================================*/
+/**
+  This function is called to query device capabilities.
+
+  NOTE:
+  All V4L2 devices support the VIDIOC_QUERYCAP ioctl. It is used to identify
+  kernel devices compatible with this specification and to obtain information
+  about driver and hardware capabilities. The ioctl takes a pointer to a struct
+  v4l2_capability which is filled by the driver. When the driver is not
+  compatible with this specification the ioctl returns an EINVAL error code.
+
+  @param file: File descriptor returned by open().
+  @param capability: pointer to struct v4l2_capability.
+
+  @return On success 0 is returned, else error code.
+  @return EINVAL: The device is not compatible with this specification.
+*/
+static int tavarua_vidioc_querycap(struct file *file, void *priv,
+		struct v4l2_capability *capability)
+{
+	struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
+
+	strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver));
+	strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
+	sprintf(capability->bus_info, "I2C");
+	capability->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+
+	capability->version = radio->chipID;
+
+	return 0;
+}
+
+/*=============================================================================
+FUNCTION:  tavarua_vidioc_queryctrl
+=============================================================================*/
+/**
+  This function is called to query the device and driver for supported video
+  controls (enumerate control items).
+
+  NOTE:
+  To query the attributes of a control, the applications set the id field of
+  a struct v4l2_queryctrl and call the VIDIOC_QUERYCTRL ioctl with a pointer
+  to this structure. The driver fills the rest of the structure or returns an
+  EINVAL error code when the id is invalid.
+
+  @param file: File descriptor returned by open().
+  @param qc: pointer to struct v4l2_queryctrl.
+
+  @return On success 0 is returned, else error code.
+  @return EINVAL: The struct v4l2_queryctrl id is invalid.
+*/
+static int tavarua_vidioc_queryctrl(struct file *file, void *priv,
+		struct v4l2_queryctrl *qc)
+{
+	unsigned char i;
+	int retval = -EINVAL;
+
+	for (i = 0; i < ARRAY_SIZE(tavarua_v4l2_queryctrl); i++) {
+		if (qc->id && qc->id == tavarua_v4l2_queryctrl[i].id) {
+			memcpy(qc, &(tavarua_v4l2_queryctrl[i]), sizeof(*qc));
+			retval = 0;
+			break;
+		}
+	}
+	if (retval < 0)
+		printk(KERN_WARNING DRIVER_NAME
+			": query conv4ltrol failed with %d\n", retval);
+
+	return retval;
+}
+static int peek_MPX_DCC(struct tavarua_device *radio)
+{
+	int retval = 0;
+	unsigned char xfr_buf[XFR_REG_NUM];
+	int MPX_DCC[] = { 0 };
+	int DCC = 0;
+	int ct = 0;
+	unsigned char size = 0;
+
+	/*
+	Poking the MPX_DCC_BYPASS register to freeze the
+	value of MPX_DCC from changing while we access it
+	*/
+
+	/*Poking the MPX_DCC_BYPASS register : 0x88C0 */
+	size = 0x01;
+	xfr_buf[0] = (XFR_POKE_MODE | (size << 1));
+	xfr_buf[1] = MPX_DCC_BYPASS_POKE_MSB;
+	xfr_buf[2] = MPX_DCC_BYPASS_POKE_LSB;
+	xfr_buf[3] = 0x01;
+
+	retval = tavarua_write_registers(radio, XFRCTRL, xfr_buf, 4);
+	if (retval < 0) {
+		FMDBG("Failed to write\n");
+		return retval;
+	}
+	/*Wait for the XFR interrupt */
+	msleep(TAVARUA_DELAY*15);
+
+	for (ct = 0; ct < 5; ct++)
+		xfr_buf[ct] = 0;
+
+	/* Peeking Regs 0x88C2-0x88C4 */
+	size = 0x03;
+	xfr_buf[0] = (XFR_PEEK_MODE | (size << 1));
+	xfr_buf[1] = MPX_DCC_PEEK_MSB_REG1;
+	xfr_buf[2] = MPX_DCC_PEEK_LSB_REG1;
+	retval = tavarua_write_registers(radio, XFRCTRL, xfr_buf, 3);
+	if (retval < 0) {
+		FMDBG("Failed to write\n");
+		return retval;
+	}
+	/*Wait for the XFR interrupt */
+	msleep(TAVARUA_DELAY*10);
+	retval = tavarua_read_registers(radio, XFRDAT0, 3);
+	if (retval < 0) {
+		printk(KERN_INFO "INT_DET: Read failure\n");
+		return retval;
+	}
+	MPX_DCC[0] = (int)radio->registers[XFRDAT0];
+	MPX_DCC[1] = (int)radio->registers[XFRDAT1];
+	MPX_DCC[2] = (int)radio->registers[XFRDAT2];
+
+	/*
+	Form the final MPX_DCC parameter
+	MPX_DCC[0] will form the LSB part
+	MPX_DCC[1] will be the middle part and 4 bits of
+	MPX_DCC[2] will be the MSB par of the 20-bit signed MPX_DCC
+	*/
+
+	DCC = ((int)MPX_DCC[2] << 16) | ((int)MPX_DCC[1] << 8) |
+		((int)MPX_DCC[0]);
+
+	/*
+	if bit-19 is '1',set remaining bits to '1' &  make it -tive
+	*/
+	if (DCC & 0x00080000) {
+		FMDBG(KERN_INFO "bit-19 is '1'\n");
+		DCC |= 0xFFF00000;
+	}
+
+	/*
+	Poking the MPX_DCC_BYPASS register to be back to normal
+	*/
+
+	/*Poking the MPX_DCC_BYPASS register : 0x88C0 */
+	size = 0x01;
+	xfr_buf[0] = (XFR_POKE_MODE | (size << 1));
+	xfr_buf[1] = MPX_DCC_BYPASS_POKE_MSB;
+	xfr_buf[2] = MPX_DCC_BYPASS_POKE_LSB;
+	xfr_buf[3] = 0x00;
+
+	retval = tavarua_write_registers(radio, XFRCTRL, xfr_buf, 4);
+	if (retval < 0) {
+		FMDBG("Failed to write\n");
+		return retval;
+	}
+	/*Wait for the XFR interrupt */
+	msleep(TAVARUA_DELAY*10);
+
+	return DCC;
+}
+/*=============================================================================
+FUNCTION:  tavarua_vidioc_g_ctrl
+=============================================================================*/
+/**
+  This function is called to get the value of a control.
+
+  NOTE:
+  To get the current value of a control, applications initialize the id field
+  of a struct v4l2_control and call the VIDIOC_G_CTRL ioctl with a pointer to
+  this structure.
+
+  When the id is invalid drivers return an EINVAL error code. When the value is
+  out of bounds drivers can choose to take the closest valid value or return an
+  ERANGE error code, whatever seems more appropriate.
+
+  @param file: File descriptor returned by open().
+  @param ctrl: pointer to struct v4l2_control.
+
+  @return On success 0 is returned, else error code.
+  @return EINVAL: The struct v4l2_control id is invalid.
+  @return ERANGE: The struct v4l2_control value is out of bounds.
+  @return EBUSY: The control is temporarily not changeable, possibly because
+  another applications took over control of the device function this control
+  belongs to.
+*/
+static int tavarua_vidioc_g_ctrl(struct file *file, void *priv,
+		struct v4l2_control *ctrl)
+{
+	struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
+	int retval = 0;
+	int cnt = 0;
+	unsigned char xfr_buf[XFR_REG_NUM];
+	signed char cRmssiThreshold;
+	signed char ioc;
+	unsigned char size = 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_VOLUME:
+		break;
+	case V4L2_CID_AUDIO_MUTE:
+		ctrl->value = radio->registers[IOCTRL] & 0x03 ;
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_SRCHMODE:
+		ctrl->value = radio->registers[SRCHCTRL] & SRCH_MODE;
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_SCANDWELL:
+		ctrl->value = (radio->registers[SRCHCTRL] & SCAN_DWELL) >> 4;
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_SRCHON:
+		ctrl->value = (radio->registers[SRCHCTRL] & SRCH_ON) >> 7 ;
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_STATE:
+		ctrl->value = (radio->registers[RDCTRL] & 0x03);
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_IOVERC:
+		retval = tavarua_read_registers(radio, IOVERC, 1);
+		if (retval < 0)
+			return retval;
+		ioc = radio->registers[IOVERC];
+		ctrl->value = ioc;
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_INTDET:
+		size = 0x1;
+		xfr_buf[0] = (XFR_PEEK_MODE | (size << 1));
+		xfr_buf[1] = INTDET_PEEK_MSB;
+		xfr_buf[2] = INTDET_PEEK_LSB;
+		retval = tavarua_write_registers(radio, XFRCTRL, xfr_buf, 3);
+		if (retval < 0) {
+			FMDBG("Failed to write\n");
+			return retval;
+		}
+		FMDBG("INT_DET:Sync write success\n");
+		/*Wait for the XFR interrupt */
+		msleep(TAVARUA_DELAY*10);
+		/* Read the XFRDAT0 register populated by FM SoC */
+		retval = tavarua_read_registers(radio, XFRDAT0, 3);
+		if (retval < 0) {
+			FMDBG("INT_DET: Read failure\n");
+			return retval;
+		}
+		ctrl->value = radio->registers[XFRDAT0];
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_MPX_DCC:
+		ctrl->value = peek_MPX_DCC(radio);
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_REGION:
+		ctrl->value = radio->region_params.region;
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_SIGNAL_TH:
+		retval = sync_read_xfr(radio, RX_CONFIG, xfr_buf);
+		if (retval < 0) {
+			FMDBG("[G IOCTL=V4L2_CID_PRIVATE_TAVARUA_SIGNAL_TH]\n");
+			FMDBG("sync_read_xfr error: [retval=%d]\n", retval);
+			break;
+		}
+		/* Since RMSSI Threshold is signed value */
+		cRmssiThreshold = (signed char)xfr_buf[0];
+		ctrl->value  = cRmssiThreshold;
+		FMDBG("cRmssiThreshold: %d\n", cRmssiThreshold);
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_SRCH_PTY:
+		ctrl->value = radio->srch_params.srch_pty;
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_SRCH_PI:
+		ctrl->value = radio->srch_params.srch_pi;
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_SRCH_CNT:
+		ctrl->value = radio->srch_params.preset_num;
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_EMPHASIS:
+		ctrl->value = radio->region_params.emphasis;
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_RDS_STD:
+		ctrl->value = radio->region_params.rds_std;
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_SPACING:
+		ctrl->value = radio->region_params.spacing;
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_RDSON:
+		ctrl->value = radio->registers[RDSCTRL] & RDS_ON;
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_RDSGROUP_MASK:
+		retval = sync_read_xfr(radio, RDS_CONFIG, xfr_buf);
+		if (retval > -1)
+			ctrl->value =   (xfr_buf[8] << 24) |
+					(xfr_buf[9] << 16) |
+					(xfr_buf[10] << 8) |
+					 xfr_buf[11];
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_RDSGROUP_PROC:
+		retval = tavarua_read_registers(radio, ADVCTRL, 1);
+		if (retval > -1)
+			ctrl->value = radio->registers[ADVCTRL];
+		msleep(TAVARUA_DELAY*5);
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_RSSI_DELTA:
+		retval = sync_read_xfr(radio, RX_CONFIG, xfr_buf);
+		if (retval < 0) {
+			FMDERR("V4L2_CID_PRIVATE_TAVARUA_RSSI_DELTA]\n");
+			FMDERR("sync_read_xfr [retval=%d]\n", retval);
+			break;
+		}
+		ctrl->value = (unsigned char)xfr_buf[4];
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_RDSD_BUF:
+		retval = sync_read_xfr(radio, RDS_CONFIG, xfr_buf);
+		if (retval > -1)
+			ctrl->value = xfr_buf[1];
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_PSALL:
+		retval = sync_read_xfr(radio, RDS_CONFIG, xfr_buf);
+		if (retval > -1)
+			ctrl->value = xfr_buf[12] & RDS_CONFIG_PSALL;
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_LP_MODE:
+		ctrl->value = radio->lp_mode;
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_ANTENNA:
+		ctrl->value = GET_REG_FIELD(radio->registers[IOCTRL],
+			IOC_ANTENNA_OFFSET, IOC_ANTENNA_MASK);
+		break;
+	case V4L2_CID_PRIVATE_INTF_LOW_THRESHOLD:
+		size = 0x04;
+		xfr_buf[0] = (XFR_PEEK_MODE | (size << 1));
+		xfr_buf[1] = ON_CHANNEL_TH_MSB;
+		xfr_buf[2] = ON_CHANNEL_TH_LSB;
+		retval = tavarua_write_registers(radio, XFRCTRL, xfr_buf, 3);
+		if (retval < 0) {
+			pr_err("%s: Failed to write\n", __func__);
+			return retval;
+		}
+		/*Wait for the XFR interrupt */
+		msleep(TAVARUA_DELAY*10);
+		retval = tavarua_read_registers(radio, XFRDAT0, 4);
+		if (retval < 0) {
+			pr_err("%s: On Ch. DET: Read failure\n", __func__);
+			return retval;
+		}
+		for (cnt = 0; cnt < 4; cnt++)
+			FMDBG("On-Channel data set is : 0x%x\t",
+				(int)radio->registers[XFRDAT0+cnt]);
+
+		ctrl->value =	LSH_DATA(radio->registers[XFRDAT0],   24) |
+				LSH_DATA(radio->registers[XFRDAT0+1], 16) |
+				LSH_DATA(radio->registers[XFRDAT0+2],  8) |
+				(radio->registers[XFRDAT0+3]);
+		FMDBG("The On Channel Threshold value is : 0x%x", ctrl->value);
+		break;
+	case V4L2_CID_PRIVATE_INTF_HIGH_THRESHOLD:
+		size = 0x04;
+		xfr_buf[0] = (XFR_PEEK_MODE | (size << 1));
+		xfr_buf[1] = OFF_CHANNEL_TH_MSB;
+		xfr_buf[2] = OFF_CHANNEL_TH_LSB;
+		retval = tavarua_write_registers(radio, XFRCTRL, xfr_buf, 3);
+		if (retval < 0) {
+			pr_err("%s: Failed to write\n", __func__);
+			return retval;
+		}
+		/*Wait for the XFR interrupt */
+		msleep(TAVARUA_DELAY*10);
+		retval = tavarua_read_registers(radio, XFRDAT0, 4);
+		if (retval < 0) {
+			pr_err("%s: Off Ch. DET: Read failure\n", __func__);
+			return retval;
+		}
+		for (cnt = 0; cnt < 4; cnt++)
+			FMDBG("Off-channel data set is : 0x%x\t",
+				(int)radio->registers[XFRDAT0+cnt]);
+
+		ctrl->value =	LSH_DATA(radio->registers[XFRDAT0],   24) |
+				LSH_DATA(radio->registers[XFRDAT0+1], 16) |
+				LSH_DATA(radio->registers[XFRDAT0+2],  8) |
+				(radio->registers[XFRDAT0+3]);
+		FMDBG("The Off Channel Threshold value is : 0x%x", ctrl->value);
+		break;
+	/*
+	 * These IOCTL's are place holders to keep the
+	 * driver compatible with change in frame works for IRIS
+	 */
+	case V4L2_CID_PRIVATE_SINR_THRESHOLD:
+	case V4L2_CID_PRIVATE_SINR_SAMPLES:
+	case V4L2_CID_PRIVATE_IRIS_GET_SINR:
+		retval = 0;
+		break;
+	default:
+		retval = -EINVAL;
+	}
+	if (retval < 0)
+		printk(KERN_WARNING DRIVER_NAME
+		": get control failed with %d, id: %d\n", retval, ctrl->id);
+
+	return retval;
+}
+
+static int tavarua_vidioc_s_ext_ctrls(struct file *file, void *priv,
+			struct v4l2_ext_controls *ctrl)
+{
+	int retval = 0;
+	int bytes_to_copy;
+	int bytes_copied = 0;
+	int bytes_left = 0;
+	int chunk_index = 0;
+	char tx_data[XFR_REG_NUM];
+	struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
+	char *data = NULL;
+	int extra_name_byte = 0;
+	int name_bytes = 0;
+
+	switch ((ctrl->controls[0]).id)	{
+	case V4L2_CID_RDS_TX_PS_NAME: {
+		FMDBG("In V4L2_CID_RDS_TX_PS_NAME\n");
+		/*Pass a sample PS string */
+
+		chunk_index = 0;
+		bytes_copied = 0;
+		bytes_left = min((int)(ctrl->controls[0]).size,
+			MAX_PS_LENGTH);
+		data = (ctrl->controls[0]).string;
+
+		/* send payload to FM hardware */
+		while (bytes_left) {
+			chunk_index++;
+			FMDBG("chunk is %d", chunk_index);
+			bytes_to_copy = min(bytes_left, XFR_REG_NUM);
+			/*Clear the tx_data */
+			memset(tx_data, 0, XFR_REG_NUM);
+			if (copy_from_user(tx_data,
+				data + bytes_copied, bytes_to_copy))
+				return -EFAULT;
+			retval = sync_write_xfr(radio,
+				RDS_PS_0 + chunk_index, tx_data);
+			if (retval < 0)	{
+				FMDBG("sync_write_xfr:  %d", retval);
+				return retval;
+			}
+			bytes_copied += bytes_to_copy;
+			bytes_left -= bytes_to_copy;
+		}
+		memset(tx_data, 0, XFR_REG_NUM);
+		/*Write the PS Header*/
+		FMDBG("Writing PS header\n");
+		extra_name_byte = (bytes_copied%8) ? 1 : 0;
+		name_bytes = (bytes_copied/8) + extra_name_byte;
+		/*8 bytes are grouped as 1 name */
+		tx_data[0] = (name_bytes) & MASK_TXREPCOUNT;
+		tx_data[1] = radio->pty & MASK_PTY; /* PTY */
+		tx_data[2] = ((radio->pi & MASK_PI_MSB) >> 8);
+		tx_data[3] = radio->pi & MASK_PI_LSB;
+		/* TX ctrl + repeatCount*/
+		tx_data[4] = TX_ON |
+		    (radio->ps_repeatcount & MASK_TXREPCOUNT);
+		retval = sync_write_xfr(radio, RDS_PS_0, tx_data);
+		if (retval < 0)	{
+			FMDBG("sync_write_xfr returned %d", retval);
+			return retval;
+		}
+	} break;
+	case V4L2_CID_RDS_TX_RADIO_TEXT: {
+		chunk_index = 0;
+		bytes_copied = 0;
+		FMDBG("In V4L2_CID_RDS_TX_RADIO_TEXT\n");
+		/*Pass a sample PS string */
+		FMDBG("Passed RT String : %s\n",
+			(ctrl->controls[0]).string);
+		bytes_left =
+		    min((int)(ctrl->controls[0]).size, MAX_RT_LENGTH);
+		data = (ctrl->controls[0]).string;
+		/* send payload to FM hardware */
+		while (bytes_left) {
+			chunk_index++;
+			bytes_to_copy = min(bytes_left, XFR_REG_NUM);
+			memset(tx_data, 0, XFR_REG_NUM);
+			if (copy_from_user(tx_data,
+				    data + bytes_copied, bytes_to_copy))
+				return -EFAULT;
+			retval = sync_write_xfr(radio,
+				RDS_RT_0 + chunk_index, tx_data);
+			if (retval < 0)
+				return retval;
+			bytes_copied += bytes_to_copy;
+			bytes_left -= bytes_to_copy;
+		}
+		/*Write the RT  Header */
+		tx_data[0] = bytes_copied;
+		/* PTY */
+		tx_data[1] = TX_ON | ((radio->pty & MASK_PTY) >> 8);
+		/* PI high */
+		tx_data[2] = ((radio->pi & MASK_PI_MSB) >> 8);
+		/* PI low */
+		tx_data[3] = radio->pi & MASK_PI_LSB;
+		retval = sync_write_xfr(radio, RDS_RT_0 , tx_data);
+		if (retval < 0)
+			return retval;
+		FMDBG("done RT writing: %d\n", retval);
+	} break;
+	default:
+	{
+		FMDBG("Shouldn't reach here\n");
+		retval = -1;
+	}
+	}
+	return retval;
+}
+
+/*=============================================================================
+FUNCTION:  tavarua_vidioc_s_ctrl
+=============================================================================*/
+/**
+  This function is called to set the value of a control.
+
+  NOTE:
+  To change the value of a control, applications initialize the id and value
+  fields of a struct v4l2_control and call the VIDIOC_S_CTRL ioctl.
+
+  When the id is invalid drivers return an EINVAL error code. When the value is
+  out of bounds drivers can choose to take the closest valid value or return an
+  ERANGE error code, whatever seems more appropriate.
+
+  @param file: File descriptor returned by open().
+  @param ctrl: pointer to struct v4l2_control.
+
+  @return On success 0 is returned, else error code.
+  @return EINVAL: The struct v4l2_control id is invalid.
+  @return ERANGE: The struct v4l2_control value is out of bounds.
+  @return EBUSY: The control is temporarily not changeable, possibly because
+  another applications took over control of the device function this control
+  belongs to.
+*/
+static int tavarua_vidioc_s_ctrl(struct file *file, void *priv,
+		struct v4l2_control *ctrl)
+{
+	struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
+	int retval = 0;
+	int size = 0, cnt = 0;
+	unsigned char value;
+	unsigned char xfr_buf[XFR_REG_NUM];
+	unsigned char tx_data[XFR_REG_NUM];
+	unsigned char dis_buf[XFR_REG_NUM];
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_VOLUME:
+		break;
+	case V4L2_CID_AUDIO_MUTE:
+		value = (radio->registers[IOCTRL] & ~IOC_HRD_MUTE) |
+							(ctrl->value & 0x03);
+		retval = tavarua_write_register(radio, IOCTRL, value);
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_SRCHMODE:
+		value = (radio->registers[SRCHCTRL] & ~SRCH_MODE) |
+							ctrl->value;
+		radio->registers[SRCHCTRL] = value;
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_SCANDWELL:
+		value = (radio->registers[SRCHCTRL] & ~SCAN_DWELL) |
+						(ctrl->value << 4);
+		radio->registers[SRCHCTRL] = value;
+		break;
+	/* start/stop search */
+	case V4L2_CID_PRIVATE_TAVARUA_SRCHON:
+		FMDBG("starting search\n");
+		tavarua_search(radio, ctrl->value, SRCH_DIR_UP);
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_STATE:
+		/* check if already on */
+		radio->handle_irq = 1;
+		if (((ctrl->value == FM_RECV) || (ctrl->value == FM_TRANS))
+				    && !(radio->registers[RDCTRL] &
+							ctrl->value)) {
+			FMDBG("clearing flags\n");
+			init_completion(&radio->sync_xfr_start);
+			init_completion(&radio->sync_req_done);
+			radio->xfr_in_progress = 0;
+			radio->xfr_bytes_left = 0;
+			FMDBG("turning on ..\n");
+			retval = tavarua_start(radio, ctrl->value);
+			if (retval >= 0) {
+				/* Enabling 'SoftMute' & 'SignalBlending' */
+				value = (radio->registers[IOCTRL] |
+				    IOC_SFT_MUTE | IOC_SIG_BLND);
+				retval = tavarua_write_register(radio,
+					IOCTRL, value);
+				if (retval < 0)
+					FMDBG("SMute and SBlending"
+						"not enabled\n");
+			}
+		}
+		/* check if off */
+		else if ((ctrl->value == FM_OFF) && radio->registers[RDCTRL]) {
+			FMDBG("%s: turning off...\n", __func__);
+			tavarua_write_register(radio, RDCTRL, ctrl->value);
+			/* flush the event and work queues */
+			kfifo_reset(&radio->data_buf[TAVARUA_BUF_EVENTS]);
+			flush_workqueue(radio->wqueue);
+			/*
+			 * queue the READY event from the host side
+			 * in case of FM off
+			 */
+			tavarua_q_event(radio, TAVARUA_EVT_RADIO_DISABLED);
+
+			FMDBG("%s, Disable All Interrupts\n", __func__);
+			/* disable irq */
+			dis_buf[STATUS_REG1] = 0x00;
+			dis_buf[STATUS_REG2] = 0x00;
+			dis_buf[STATUS_REG3] = TRANSFER;
+			retval = sync_write_xfr(radio, INT_CTRL, dis_buf);
+			if (retval < 0) {
+				pr_err("%s: failed to disable"
+						"Interrupts\n", __func__);
+				return retval;
+			}
+		}
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_SET_AUDIO_PATH:
+		FMDBG("Setting audio path ...\n");
+		if (ctrl->value == FM_DIGITAL_PATH) {
+			FMDBG("Digital audio path enabled ...\n");
+			retval = tavarua_set_audio_path(
+				TAVARUA_AUDIO_OUT_DIGITAL_ON,
+				TAVARUA_AUDIO_OUT_ANALOG_OFF);
+			if (retval < 0) {
+				FMDERR("Error in tavarua_set_audio_path"
+					" %d\n", retval);
+			}
+		} else if (ctrl->value == FM_ANALOG_PATH) {
+			FMDBG("Analog audio path enabled ...\n");
+			retval = tavarua_set_audio_path(
+				TAVARUA_AUDIO_OUT_DIGITAL_OFF,
+				TAVARUA_AUDIO_OUT_ANALOG_ON);
+			if (retval < 0) {
+				FMDERR("Error in tavarua_set_audio_path"
+					" %d\n", retval);
+			}
+		}
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_SRCH_ALGORITHM:
+		radio->enable_optimized_srch_alg = ctrl->value;
+		FMDBG("V4L2_CID_PRIVATE_TAVARUA_SRCH_ALGORITHM : %d",
+			radio->enable_optimized_srch_alg);
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_REGION:
+		retval = tavarua_set_region(radio, ctrl->value);
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_SIGNAL_TH:
+		retval = sync_read_xfr(radio, RX_CONFIG, xfr_buf);
+		if (retval < 0)	{
+			FMDERR("V4L2_CID_PRIVATE_TAVARUA_SIGNAL_TH]\n");
+			FMDERR("sync_read_xfr [retval=%d]\n", retval);
+			break;
+		}
+		/* RMSSI Threshold is a signed 8 bit value */
+		xfr_buf[0] = (unsigned char)ctrl->value;
+		xfr_buf[1] = (unsigned char)ctrl->value;
+		retval = sync_write_xfr(radio, RX_CONFIG, xfr_buf);
+		if (retval < 0) {
+			FMDERR("V4L2_CID_PRIVATE_TAVARUA_SIGNAL_TH]\n");
+			FMDERR("sync_write_xfr [retval=%d]\n", retval);
+			break;
+		}
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_SRCH_PTY:
+		radio->srch_params.srch_pty = ctrl->value;
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_SRCH_PI:
+		radio->srch_params.srch_pi = ctrl->value;
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_SRCH_CNT:
+		radio->srch_params.preset_num = ctrl->value;
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_EMPHASIS:
+		radio->region_params.emphasis = ctrl->value;
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_RDS_STD:
+		radio->region_params.rds_std = ctrl->value;
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_SPACING:
+		radio->region_params.spacing = ctrl->value;
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_RDSON:
+		retval = 0;
+		if (ctrl->value != (radio->registers[RDSCTRL] & RDS_ON)) {
+			value = radio->registers[RDSCTRL] | ctrl->value;
+			retval = tavarua_write_register(radio, RDSCTRL, value);
+		}
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_RDSGROUP_MASK:
+		retval = sync_read_xfr(radio, RDS_CONFIG, xfr_buf);
+		if (retval < 0)
+			break;
+		xfr_buf[8] = (ctrl->value & 0xFF000000) >> 24;
+		xfr_buf[9] = (ctrl->value & 0x00FF0000) >> 16;
+		xfr_buf[10] = (ctrl->value & 0x0000FF00) >> 8;
+		xfr_buf[11] = (ctrl->value & 0x000000FF);
+		retval = sync_write_xfr(radio, RDS_CONFIG, xfr_buf);
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_RDSGROUP_PROC:
+		value  = radio->registers[ADVCTRL] | ctrl->value;
+		retval = tavarua_write_register(radio, ADVCTRL, value);
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_AF_JUMP:
+		retval = tavarua_read_registers(radio, ADVCTRL, 1);
+		SET_REG_FIELD(radio->registers[ADVCTRL], ctrl->value,
+			RDSAF_OFFSET, RDSAF_MASK);
+		msleep(TAVARUA_DELAY*5);
+		retval = tavarua_write_register(radio,
+			ADVCTRL, radio->registers[ADVCTRL]);
+		msleep(TAVARUA_DELAY*5);
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_RSSI_DELTA:
+		retval = sync_read_xfr(radio, RX_CONFIG, xfr_buf);
+		if (retval < 0) {
+			FMDERR("V4L2_CID_PRIVATE_TAVARUA_RSSI_DELTA]\n");
+			FMDERR("sync_read_xfr [retval=%d]\n", retval);
+			break;
+		}
+		xfr_buf[4] = (unsigned char)ctrl->value;
+		retval = sync_write_xfr(radio, RX_CONFIG, xfr_buf);
+		if (retval < 0) {
+			FMDERR("V4L2_CID_PRIVATE_TAVARUA_RSSI_DELTA]\n");
+			FMDERR("sync_write_xfr [retval=%d]\n", retval);
+			break;
+		}
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_HLSI:
+		retval = tavarua_read_registers(radio, RDCTRL, 1);
+		SET_REG_FIELD(radio->registers[RDCTRL], ctrl->value,
+			RDCTRL_HLSI_OFFSET, RDCTRL_HLSI_MASK);
+		retval = tavarua_write_register(radio, RDCTRL,
+			radio->registers[RDCTRL]);
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_RDSD_BUF:
+		retval = sync_read_xfr(radio, RDS_CONFIG, xfr_buf);
+		if (retval < 0)
+			break;
+		xfr_buf[1] = ctrl->value;
+		retval = sync_write_xfr(radio, RDS_CONFIG, xfr_buf);
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_PSALL:
+		retval = sync_read_xfr(radio, RDS_CONFIG, xfr_buf);
+		value = ctrl->value & RDS_CONFIG_PSALL;
+		if (retval < 0)
+			break;
+		xfr_buf[12] &= ~RDS_CONFIG_PSALL;
+		xfr_buf[12] |= value;
+		retval = sync_write_xfr(radio, RDS_CONFIG, xfr_buf);
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_LP_MODE:
+		retval = 0;
+		if (ctrl->value == radio->lp_mode)
+			break;
+		if (ctrl->value) {
+			FMDBG("going into low power mode\n");
+			retval = tavarua_disable_interrupts(radio);
+		} else {
+			FMDBG("going into normal power mode\n");
+			tavarua_setup_interrupts(radio,
+				(radio->registers[RDCTRL] & 0x03));
+		}
+		break;
+	case V4L2_CID_PRIVATE_TAVARUA_ANTENNA:
+		SET_REG_FIELD(radio->registers[IOCTRL], ctrl->value,
+					IOC_ANTENNA_OFFSET, IOC_ANTENNA_MASK);
+		break;
+	case V4L2_CID_PRIVATE_INTF_LOW_THRESHOLD:
+		size = 0x04;
+		/* Poking the value of ON Channel Threshold value */
+		xfr_buf[0] = (XFR_POKE_MODE | (size << 1));
+		xfr_buf[1] = ON_CHANNEL_TH_MSB;
+		xfr_buf[2] = ON_CHANNEL_TH_LSB;
+		/* Data to be poked into the register */
+		xfr_buf[3] = (ctrl->value & 0xFF000000) >> 24;
+		xfr_buf[4] = (ctrl->value & 0x00FF0000) >> 16;
+		xfr_buf[5] = (ctrl->value & 0x0000FF00) >> 8;
+		xfr_buf[6] = (ctrl->value & 0x000000FF);
+
+		for (cnt = 3; cnt < 7; cnt++) {
+			FMDBG("On-channel data to be poked is : %d",
+				(int)xfr_buf[cnt]);
+		}
+
+		retval = tavarua_write_registers(radio, XFRCTRL,
+				xfr_buf, size+3);
+		if (retval < 0) {
+			pr_err("%s: Failed to write\n", __func__);
+			return retval;
+		}
+		/*Wait for the XFR interrupt */
+		msleep(TAVARUA_DELAY*10);
+		break;
+	case V4L2_CID_PRIVATE_INTF_HIGH_THRESHOLD:
+		size = 0x04;
+		/* Poking the value of OFF Channel Threshold value */
+		xfr_buf[0] = (XFR_POKE_MODE | (size << 1));
+		xfr_buf[1] = OFF_CHANNEL_TH_MSB;
+		xfr_buf[2] = OFF_CHANNEL_TH_LSB;
+		/* Data to be poked into the register */
+		xfr_buf[3] = (ctrl->value & 0xFF000000) >> 24;
+		xfr_buf[4] = (ctrl->value & 0x00FF0000) >> 16;
+		xfr_buf[5] = (ctrl->value & 0x0000FF00) >> 8;
+		xfr_buf[6] = (ctrl->value & 0x000000FF);
+
+		for (cnt = 3; cnt < 7; cnt++) {
+			FMDBG("Off-channel data to be poked is : %d",
+				(int)xfr_buf[cnt]);
+		}
+
+		retval = tavarua_write_registers(radio, XFRCTRL,
+				xfr_buf, size+3);
+		if (retval < 0) {
+			pr_err("%s: Failed to write\n", __func__);
+			return retval;
+		}
+		/*Wait for the XFR interrupt */
+		msleep(TAVARUA_DELAY*10);
+		break;
+	/* TX Controls */
+
+	case V4L2_CID_RDS_TX_PTY: {
+			radio->pty = ctrl->value;
+		} break;
+	case V4L2_CID_RDS_TX_PI: {
+			radio->pi = ctrl->value;
+		} break;
+	case V4L2_CID_PRIVATE_TAVARUA_STOP_RDS_TX_PS_NAME: {
+			FMDBG("In STOP_RDS_TX_PS_NAME\n");
+			/*Pass a sample PS string */
+			memset(tx_data, '0', XFR_REG_NUM);
+			FMDBG("Writing PS header\n");
+			retval = sync_write_xfr(radio, RDS_PS_0, tx_data);
+			FMDBG("retval of PS Header write: %d", retval);
+
+		} break;
+
+	case V4L2_CID_PRIVATE_TAVARUA_STOP_RDS_TX_RT: {
+			memset(tx_data, '0', XFR_REG_NUM);
+			FMDBG("Writing RT header\n");
+			retval = sync_write_xfr(radio, RDS_RT_0, tx_data);
+			FMDBG("retval of Header write: %d", retval);
+
+		} break;
+
+	case V4L2_CID_PRIVATE_TAVARUA_TX_SETPSREPEATCOUNT: {
+			radio->ps_repeatcount = ctrl->value;
+		} break;
+	case V4L2_CID_TUNE_POWER_LEVEL: {
+		unsigned char tx_power_lvl_config[FM_TX_PWR_LVL_MAX+1] = {
+			0x85, /* tx_da<5:3> = 0  lpf<2:0> = 5*/
+			0x95, /* tx_da<5:3> = 2  lpf<2:0> = 5*/
+			0x9D, /* tx_da<5:3> = 3  lpf<2:0> = 5*/
+			0xA5, /* tx_da<5:3> = 4  lpf<2:0> = 5*/
+			0xAD, /* tx_da<5:3> = 5  lpf<2:0> = 5*/
+			0xB5, /* tx_da<5:3> = 6  lpf<2:0> = 5*/
+			0xBD, /* tx_da<5:3> = 7  lpf<2:0> = 5*/
+			0xBF  /* tx_da<5:3> = 7  lpf<2:0> = 7*/
+		};
+		if (ctrl->value > FM_TX_PWR_LVL_MAX)
+			ctrl->value = FM_TX_PWR_LVL_MAX;
+		if (ctrl->value < FM_TX_PWR_LVL_0)
+			ctrl->value = FM_TX_PWR_LVL_0;
+		retval = sync_read_xfr(radio, PHY_TXGAIN, xfr_buf);
+		FMDBG("return for PHY_TXGAIN is %d", retval);
+		if (retval < 0) {
+			FMDBG("read failed");
+			break;
+		}
+		xfr_buf[2] = tx_power_lvl_config[ctrl->value];
+		retval = sync_write_xfr(radio, PHY_TXGAIN, xfr_buf);
+		FMDBG("return for write PHY_TXGAIN is %d", retval);
+		if (retval < 0)
+			FMDBG("write failed");
+	} break;
+	/*These IOCTL's are place holders to keep the
+	driver compatible with change in frame works for IRIS */
+	case V4L2_CID_PRIVATE_SOFT_MUTE:
+	case V4L2_CID_PRIVATE_RIVA_ACCS_ADDR:
+	case V4L2_CID_PRIVATE_RIVA_ACCS_LEN:
+	case V4L2_CID_PRIVATE_RIVA_PEEK:
+	case V4L2_CID_PRIVATE_RIVA_POKE:
+	case V4L2_CID_PRIVATE_SSBI_ACCS_ADDR:
+	case V4L2_CID_PRIVATE_SSBI_PEEK:
+	case V4L2_CID_PRIVATE_SSBI_POKE:
+	case V4L2_CID_PRIVATE_TX_TONE:
+	case V4L2_CID_PRIVATE_RDS_GRP_COUNTERS:
+	case V4L2_CID_PRIVATE_SET_NOTCH_FILTER:
+	case V4L2_CID_PRIVATE_TAVARUA_DO_CALIBRATION:
+	case V4L2_CID_PRIVATE_SINR_THRESHOLD:
+	case V4L2_CID_PRIVATE_SINR_SAMPLES:
+		retval = 0;
+		break;
+	default:
+		retval = -EINVAL;
+	}
+	if (retval < 0)
+		printk(KERN_WARNING DRIVER_NAME
+		": set control failed with %d, id : %d\n", retval, ctrl->id);
+
+	return retval;
+}
+
+/*=============================================================================
+FUNCTION:  tavarua_vidioc_g_tuner
+=============================================================================*/
+/**
+  This function is called to get tuner attributes.
+
+  NOTE:
+  To query the attributes of a tuner, applications initialize the index field
+  and zero out the reserved array of a struct v4l2_tuner and call the
+  VIDIOC_G_TUNER ioctl with a pointer to this structure. Drivers fill the rest
+  of the structure or return an EINVAL error code when the index is out of
+  bounds. To enumerate all tuners applications shall begin at index zero,
+  incrementing by one until the driver returns EINVAL.
+
+  @param file: File descriptor returned by open().
+  @param tuner: pointer to struct v4l2_tuner.
+
+  @return On success 0 is returned, else error code.
+  @return EINVAL: The struct v4l2_tuner index is out of bounds.
+*/
+static int tavarua_vidioc_g_tuner(struct file *file, void *priv,
+		struct v4l2_tuner *tuner)
+{
+	struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
+	int retval;
+	unsigned char xfr_buf[XFR_REG_NUM];
+	char rmssi = 0;
+	unsigned char size = 0;
+
+	if (tuner->index > 0)
+		return -EINVAL;
+
+	/* read status rssi */
+	retval = tavarua_read_registers(radio, IOCTRL, 1);
+	if (retval < 0)
+		return retval;
+	/* read RMSSI */
+	size = 0x1;
+	xfr_buf[0] = (XFR_PEEK_MODE | (size << 1));
+	xfr_buf[1] = RMSSI_PEEK_MSB;
+	xfr_buf[2] = RMSSI_PEEK_LSB;
+	retval = tavarua_write_registers(radio, XFRCTRL, xfr_buf, 3);
+	msleep(TAVARUA_DELAY*10);
+	retval = tavarua_read_registers(radio, XFRDAT0, 3);
+	rmssi = radio->registers[XFRDAT0];
+	tuner->signal = rmssi;
+
+	strcpy(tuner->name, "FM");
+	tuner->type = V4L2_TUNER_RADIO;
+	tuner->rangelow  =  radio->region_params.band_low;
+	tuner->rangehigh =  radio->region_params.band_high;
+	tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+	tuner->capability = V4L2_TUNER_CAP_LOW;
+
+	/* Stereo indicator == Stereo (instead of Mono) */
+	if (radio->registers[IOCTRL] & IOC_MON_STR)
+		tuner->audmode = V4L2_TUNER_MODE_STEREO;
+	else
+	  tuner->audmode = V4L2_TUNER_MODE_MONO;
+
+	/* automatic frequency control: -1: freq to low, 1 freq to high */
+	tuner->afc = 0;
+
+	return 0;
+}
+
+/*=============================================================================
+FUNCTION:  tavarua_vidioc_s_tuner
+=============================================================================*/
+/**
+  This function is called to set tuner attributes. Used to set mono/stereo mode.
+
+  NOTE:
+  Tuners have two writable properties, the audio mode and the radio frequency.
+  To change the audio mode, applications initialize the index, audmode and
+  reserved fields and call the VIDIOC_S_TUNER ioctl. This will not change the
+  current tuner, which is determined by the current video input. Drivers may
+  choose a different audio mode if the requested mode is invalid or unsupported.
+  Since this is a write-only ioctl, it does not return the actually selected
+  audio mode.
+
+  To change the radio frequency the VIDIOC_S_FREQUENCY ioctl is available.
+
+  @param file: File descriptor returned by open().
+  @param tuner: pointer to struct v4l2_tuner.
+
+  @return On success 0 is returned, else error code.
+  @return -EINVAL: The struct v4l2_tuner index is out of bounds.
+*/
+static int tavarua_vidioc_s_tuner(struct file *file, void *priv,
+		struct v4l2_tuner *tuner)
+{
+	struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
+	int retval;
+	int audmode;
+	if (tuner->index > 0)
+		return -EINVAL;
+
+	FMDBG("%s: set low to %d\n", __func__, tuner->rangelow);
+	radio->region_params.band_low = tuner->rangelow;
+	radio->region_params.band_high = tuner->rangehigh;
+	if (tuner->audmode == V4L2_TUNER_MODE_MONO)
+		/* Mono */
+		audmode = (radio->registers[IOCTRL] | IOC_MON_STR);
+	 else
+		/* Stereo */
+		audmode = (radio->registers[IOCTRL] & ~IOC_MON_STR);
+	retval = tavarua_write_register(radio, IOCTRL, audmode);
+	if (retval < 0)
+		printk(KERN_WARNING DRIVER_NAME
+			": set tuner failed with %d\n", retval);
+
+	return retval;
+}
+
+/*=============================================================================
+FUNCTION:  tavarua_vidioc_g_frequency
+=============================================================================*/
+/**
+  This function is called to get tuner or modulator radio frequency.
+
+  NOTE:
+  To get the current tuner or modulator radio frequency applications set the
+  tuner field of a struct v4l2_frequency to the respective tuner or modulator
+  number (only input devices have tuners, only output devices have modulators),
+  zero out the reserved array and call the VIDIOC_G_FREQUENCY ioctl with a
+  pointer to this structure. The driver stores the current frequency in the
+  frequency field.
+
+  @param file: File descriptor returned by open().
+  @param freq: pointer to struct v4l2_frequency. This will be set to the
+   resultant
+  frequency in 62.5 khz on success.
+
+  @return On success 0 is returned, else error code.
+  @return EINVAL: The tuner index is out of bounds or the value in the type
+  field is wrong.
+*/
+static int tavarua_vidioc_g_frequency(struct file *file, void *priv,
+		struct v4l2_frequency *freq)
+{
+	struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
+	freq->type = V4L2_TUNER_RADIO;
+	return tavarua_get_freq(radio, freq);
+
+}
+
+/*=============================================================================
+FUNCTION:  tavarua_vidioc_s_frequency
+=============================================================================*/
+/**
+  This function is called to set tuner or modulator radio frequency.
+
+  NOTE:
+  To change the current tuner or modulator radio frequency applications
+  initialize the tuner, type and frequency fields, and the reserved array of
+  a struct v4l2_frequency and call the VIDIOC_S_FREQUENCY ioctl with a pointer
+  to this structure. When the requested frequency is not possible the driver
+  assumes the closest possible value. However VIDIOC_S_FREQUENCY is a
+  write-only ioctl, it does not return the actual new frequency.
+
+  @param file: File descriptor returned by open().
+  @param freq: pointer to struct v4l2_frequency.
+
+  @return On success 0 is returned, else error code.
+  @return EINVAL: The tuner index is out of bounds or the value in the type
+  field is wrong.
+*/
+static int tavarua_vidioc_s_frequency(struct file *file, void *priv,
+					struct v4l2_frequency *freq)
+{
+	struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
+	int retval = -1;
+	struct v4l2_frequency getFreq;
+
+	FMDBG("%s\n", __func__);
+
+	if (freq->type != V4L2_TUNER_RADIO)
+		return -EINVAL;
+
+	FMDBG("Calling tavarua_set_freq\n");
+
+	INIT_COMPLETION(radio->sync_req_done);
+	retval = tavarua_set_freq(radio, freq->frequency);
+	if (retval < 0) {
+		printk(KERN_WARNING DRIVER_NAME
+			": set frequency failed with %d\n", retval);
+	} else {
+		/* Wait for interrupt i.e. complete
+		(&radio->sync_req_done); call */
+		if (!wait_for_completion_timeout(&radio->sync_req_done,
+			msecs_to_jiffies(wait_timeout))) {
+			FMDERR("Timeout: No Tune response");
+			retval = tavarua_get_freq(radio, &getFreq);
+			radio->tune_req = 0;
+			if (retval > 0) {
+				if (getFreq.frequency == freq->frequency) {
+					/** This is success, queut the event*/
+					tavarua_q_event(radio,
+						TAVARUA_EVT_TUNE_SUCC);
+					return 0;
+				} else {
+					return -EIO;
+				}
+			}
+		}
+	}
+	radio->tune_req = 0;
+	return retval;
+}
+
+/*=============================================================================
+FUNCTION:  tavarua_vidioc_dqbuf
+=============================================================================*/
+/**
+  This function is called to exchange a buffer with the driver.
+  This is main buffer function, in essense its equivalent to a blocking
+  read call.
+
+  Applications call the VIDIOC_DQBUF ioctl to dequeue a filled (capturing) or
+  displayed (output) buffer from the driver's outgoing queue. They just set
+  the type and memory fields of a struct v4l2_buffer as above, when VIDIOC_DQBUF
+  is called with a pointer to this structure the driver fills the remaining
+  fields or returns an error code.
+
+  NOTE:
+  By default VIDIOC_DQBUF blocks when no buffer is in the outgoing queue.
+  When the O_NONBLOCK flag was given to the open() function, VIDIOC_DQBUF
+  returns immediately with an EAGAIN error code when no buffer is available.
+
+  @param file: File descriptor returned by open().
+  @param buffer: pointer to struct v4l2_buffer.
+
+  @return On success 0 is returned, else error code.
+  @return EAGAIN: Non-blocking I/O has been selected using O_NONBLOCK and no
+  buffer was in the outgoing queue.
+  @return EINVAL: The buffer type is not supported, or the index is out of
+  bounds, or no buffers have been allocated yet, or the userptr or length are
+  invalid.
+  @return ENOMEM: Not enough physical or virtual memory was available to enqueue
+  a user pointer buffer.
+  @return EIO: VIDIOC_DQBUF failed due to an internal error. Can also indicate
+  temporary problems like signal loss. Note the driver might dequeue an (empty)
+  buffer despite returning an error, or even stop capturing.
+*/
+static int tavarua_vidioc_dqbuf(struct file *file, void *priv,
+				struct v4l2_buffer *buffer)
+{
+
+	struct tavarua_device  *radio = video_get_drvdata(video_devdata(file));
+	enum tavarua_buf_t buf_type = -1;
+	unsigned char buf_fifo[STD_BUF_SIZE] = {0};
+	struct kfifo *data_fifo = NULL;
+	unsigned char *buf = NULL;
+	unsigned int len = 0, retval = -1;
+
+	if ((radio == NULL) || (buffer == NULL)) {
+		FMDERR("radio/buffer is NULL\n");
+		return -ENXIO;
+	}
+	buf_type = buffer->index;
+	buf = (unsigned char *)buffer->m.userptr;
+	len = buffer->length;
+	FMDBG("%s: requesting buffer %d\n", __func__, buf_type);
+
+	if ((buf_type < TAVARUA_BUF_MAX) && (buf_type >= 0)) {
+		data_fifo = &radio->data_buf[buf_type];
+		if (buf_type == TAVARUA_BUF_EVENTS) {
+			if (wait_event_interruptible(radio->event_queue,
+				kfifo_len(data_fifo)) < 0) {
+				return -EINTR;
+			}
+		}
+	} else {
+		FMDERR("invalid buffer type\n");
+		return -EINVAL;
+	}
+	if (len <= STD_BUF_SIZE) {
+		buffer->bytesused = kfifo_out_locked(data_fifo, &buf_fifo[0],
+					len, &radio->buf_lock[buf_type]);
+	} else {
+		FMDERR("kfifo_out_locked can not use len more than 128\n");
+		return -EINVAL;
+	}
+	retval = copy_to_user(buf, &buf_fifo[0], buffer->bytesused);
+	if (retval > 0) {
+		FMDERR("Failed to copy %d bytes of data\n", retval);
+		return -EAGAIN;
+	}
+
+	return retval;
+}
+
+/*=============================================================================
+FUNCTION:  tavarua_vidioc_g_fmt_type_private
+=============================================================================*/
+/**
+  This function is here to make the v4l2 framework happy.
+  We cannot use private buffers without it.
+
+  @param file: File descriptor returned by open().
+  @param f: pointer to struct v4l2_format.
+
+  @return On success 0 is returned, else error code.
+  @return EINVAL: The tuner index is out of bounds or the value in the type
+  field is wrong.
+*/
+static int tavarua_vidioc_g_fmt_type_private(struct file *file, void *priv,
+						struct v4l2_format *f)
+{
+	return 0;
+
+}
+
+/*=============================================================================
+FUNCTION:  tavarua_vidioc_s_hw_freq_seek
+=============================================================================*/
+/**
+  This function is called to perform a hardware frequency seek.
+
+  Start a hardware frequency seek from the current frequency. To do this
+  applications initialize the tuner, type, seek_upward and wrap_around fields,
+  and zero out the reserved array of a struct v4l2_hw_freq_seek and call the
+  VIDIOC_S_HW_FREQ_SEEK ioctl with a pointer to this structure.
+
+  This ioctl is supported if the V4L2_CAP_HW_FREQ_SEEK capability is set.
+
+  @param file: File descriptor returned by open().
+  @param seek: pointer to struct v4l2_hw_freq_seek.
+
+  @return On success 0 is returned, else error code.
+  @return EINVAL: The tuner index is out of bounds or the value in the type
+  field is wrong.
+  @return EAGAIN: The ioctl timed-out. Try again.
+*/
+static int tavarua_vidioc_s_hw_freq_seek(struct file *file, void *priv,
+					struct v4l2_hw_freq_seek *seek)
+{
+	struct tavarua_device  *radio = video_get_drvdata(video_devdata(file));
+	int dir;
+	if (seek->seek_upward)
+		dir = SRCH_DIR_UP;
+	else
+		dir = SRCH_DIR_DOWN;
+	FMDBG("starting search\n");
+	return tavarua_search(radio, CTRL_ON, dir);
+}
+
+/*
+ * tavarua_viddev_tamples - video device interface
+ */
+static const struct v4l2_ioctl_ops tavarua_ioctl_ops = {
+	.vidioc_querycap              = tavarua_vidioc_querycap,
+	.vidioc_queryctrl             = tavarua_vidioc_queryctrl,
+	.vidioc_g_ctrl                = tavarua_vidioc_g_ctrl,
+	.vidioc_s_ctrl                = tavarua_vidioc_s_ctrl,
+	.vidioc_g_tuner               = tavarua_vidioc_g_tuner,
+	.vidioc_s_tuner               = tavarua_vidioc_s_tuner,
+	.vidioc_g_frequency           = tavarua_vidioc_g_frequency,
+	.vidioc_s_frequency           = tavarua_vidioc_s_frequency,
+	.vidioc_s_hw_freq_seek        = tavarua_vidioc_s_hw_freq_seek,
+	.vidioc_dqbuf                 = tavarua_vidioc_dqbuf,
+	.vidioc_g_fmt_type_private    = tavarua_vidioc_g_fmt_type_private,
+	.vidioc_s_ext_ctrls           = tavarua_vidioc_s_ext_ctrls,
+};
+
+static struct video_device tavarua_viddev_template = {
+	.fops                   = &tavarua_fops,
+	.ioctl_ops              = &tavarua_ioctl_ops,
+	.name                   = DRIVER_NAME,
+	.release                = video_device_release,
+};
+
+/*==============================================================
+FUNCTION:  FmQSocCom_EnableInterrupts
+==============================================================*/
+/**
+  This function enable interrupts.
+
+  @param radio: structure pointer passed by client.
+  @param state: FM radio state (receiver/transmitter/off/reset).
+
+  @return => 0 if successful.
+  @return < 0 if failure.
+*/
+static int tavarua_setup_interrupts(struct tavarua_device *radio,
+					enum radio_state_t state)
+{
+	int retval;
+	unsigned char int_ctrl[XFR_REG_NUM];
+
+	if (!radio->lp_mode)
+		return 0;
+
+	int_ctrl[STATUS_REG1] = READY | TUNE | SEARCH | SCANNEXT |
+				SIGNAL | INTF | SYNC | AUDIO;
+	if (state == FM_RECV)
+		int_ctrl[STATUS_REG2] =  RDSDAT | RDSRT | RDSPS | RDSAF;
+	else
+		int_ctrl[STATUS_REG2] = TXRDSDAT | TXRDSDONE;
+
+	int_ctrl[STATUS_REG3] = TRANSFER | ERROR;
+
+	/* use xfr for interrupt setup */
+    if (radio->chipID == MARIMBA_2_1 || radio->chipID == BAHAMA_1_0
+		|| radio->chipID == BAHAMA_2_0) {
+		FMDBG("Setting interrupts\n");
+		retval =  sync_write_xfr(radio, INT_CTRL, int_ctrl);
+	/* use register write to setup interrupts */
+	} else {
+		retval = tavarua_write_register(radio,
+					STATUS_REG1, int_ctrl[STATUS_REG1]);
+		if (retval < 0)
+			return retval;
+
+		retval = tavarua_write_register(radio,
+					STATUS_REG2, int_ctrl[STATUS_REG2]);
+		if (retval < 0)
+			return retval;
+
+		retval = tavarua_write_register(radio,
+					STATUS_REG3, int_ctrl[STATUS_REG3]);
+		if (retval < 0)
+			return retval;
+	}
+
+	radio->lp_mode = 0;
+	/* tavarua_handle_interrupts force reads all the interrupt status
+	*  registers and it is not valid for MBA 2.1
+	*/
+	if ((radio->chipID != MARIMBA_2_1) && (radio->chipID != BAHAMA_1_0)
+		&& (radio->chipID != BAHAMA_2_0))
+		tavarua_handle_interrupts(radio);
+
+	return retval;
+
+}
+
+/*==============================================================
+FUNCTION:  tavarua_disable_interrupts
+==============================================================*/
+/**
+  This function disables interrupts.
+
+  @param radio: structure pointer passed by client.
+
+  @return => 0 if successful.
+  @return < 0 if failure.
+*/
+static int tavarua_disable_interrupts(struct tavarua_device *radio)
+{
+	unsigned char lpm_buf[XFR_REG_NUM];
+	int retval;
+	if (radio->lp_mode)
+		return 0;
+	FMDBG("%s\n", __func__);
+	/* In Low power mode, disable all the interrupts that are not being
+		 waited by the Application */
+	lpm_buf[STATUS_REG1] = TUNE | SEARCH | SCANNEXT;
+	lpm_buf[STATUS_REG2] = 0x00;
+	lpm_buf[STATUS_REG3] = TRANSFER;
+	/* use xfr for interrupt setup */
+	wait_timeout = 100;
+	if (radio->chipID == MARIMBA_2_1 || radio->chipID == BAHAMA_1_0
+		|| radio->chipID == BAHAMA_2_0)
+		retval = sync_write_xfr(radio, INT_CTRL, lpm_buf);
+	/* use register write to setup interrupts */
+	else
+		retval = tavarua_write_registers(radio, STATUS_REG1, lpm_buf,
+							ARRAY_SIZE(lpm_buf));
+
+	/*INT_CTL writes may fail with TIME_OUT as all the
+	interrupts have been disabled
+	*/
+	if (retval > -1 || retval == -ETIME) {
+		radio->lp_mode = 1;
+		/*Consider timeout as a valid case here*/
+		retval = 0;
+	}
+	wait_timeout = WAIT_TIMEOUT;
+	return retval;
+
+}
+
+/*==============================================================
+FUNCTION:  tavarua_start
+==============================================================*/
+/**
+  Starts/enables the device (FM radio).
+
+  @param radio: structure pointer passed by client.
+  @param state: FM radio state (receiver/transmitter/off/reset).
+
+  @return On success 0 is returned, else error code.
+*/
+static int tavarua_start(struct tavarua_device *radio,
+				enum radio_state_t state)
+{
+
+	int retval;
+	FMDBG("%s <%d>\n", __func__, state);
+	/* set geographic region */
+	radio->region_params.region = TAVARUA_REGION_US;
+
+	/* set radio mode */
+	retval = tavarua_write_register(radio, RDCTRL, state);
+	if (retval < 0)
+		return retval;
+	/* wait for radio to init */
+	msleep(RADIO_INIT_TIME);
+	/* enable interrupts */
+	tavarua_setup_interrupts(radio, state);
+	/* default region is US */
+	radio->region_params.band_low = US_LOW_BAND * FREQ_MUL;
+	radio->region_params.band_high = US_HIGH_BAND * FREQ_MUL;
+
+	return 0;
+}
+
+/*==============================================================
+FUNCTION:  tavarua_suspend
+==============================================================*/
+/**
+  Save state and stop all devices in system.
+
+  @param pdev: platform device to be suspended.
+  @param state: Power state to put each device in.
+
+  @return On success 0 is returned, else error code.
+*/
+static int tavarua_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct tavarua_device *radio = platform_get_drvdata(pdev);
+	int retval;
+	int users = 0;
+	printk(KERN_INFO DRIVER_NAME "%s: radio suspend\n\n", __func__);
+	if (radio) {
+		users = atomic_read(&radio->users);
+		if (!users) {
+			retval = tavarua_disable_interrupts(radio);
+			if (retval < 0) {
+				printk(KERN_INFO DRIVER_NAME
+					"tavarua_suspend error %d\n", retval);
+				return -EIO;
+			}
+		}
+	}
+	return 0;
+}
+
+/*==============================================================
+FUNCTION:  tavarua_resume
+==============================================================*/
+/**
+  Restore state of each device in system.
+
+  @param pdev: platform device to be resumed.
+
+  @return On success 0 is returned, else error code.
+*/
+static int tavarua_resume(struct platform_device *pdev)
+{
+
+	struct tavarua_device *radio = platform_get_drvdata(pdev);
+	int retval;
+	int users = 0;
+	printk(KERN_INFO DRIVER_NAME "%s: radio resume\n\n", __func__);
+	if (radio) {
+		users = atomic_read(&radio->users);
+
+		if (!users) {
+			retval = tavarua_setup_interrupts(radio,
+			(radio->registers[RDCTRL] & 0x03));
+			if (retval < 0) {
+				printk(KERN_INFO DRIVER_NAME "Error in \
+					tavarua_resume %d\n", retval);
+				return -EIO;
+			}
+		}
+	}
+	return 0;
+}
+
+/*==============================================================
+FUNCTION:  tavarua_set_audio_path
+==============================================================*/
+/**
+  This function will configure the audio path to and from the
+  FM core.
+
+  This interface is expected to be called from the multimedia
+  driver's thread.  This interface should only be called when
+  the FM hardware is enabled.  If the FM hardware is not
+  currently enabled, this interface will return an error.
+
+  @param digital_on: Digital audio from the FM core should be enabled/disbled.
+  @param analog_on: Analog audio from the FM core should be enabled/disbled.
+
+  @return On success 0 is returned, else error code.
+*/
+int tavarua_set_audio_path(int digital_on, int analog_on)
+{
+	struct tavarua_device *radio = private_data;
+	int rx_on = radio->registers[RDCTRL] & FM_RECV;
+	int retval = 0;
+	if (!radio)
+		return -ENOMEM;
+	/* RX */
+	FMDBG("%s: digital: %d analog: %d\n", __func__, digital_on, analog_on);
+	if ((radio->pdata != NULL) && (radio->pdata->config_i2s_gpio != NULL)) {
+		if (digital_on) {
+			retval = radio->pdata->config_i2s_gpio(FM_I2S_ON);
+			if (retval) {
+				pr_err("%s: config_i2s_gpio failed\n",
+								__func__);
+			}
+		} else {
+			retval = radio->pdata->config_i2s_gpio(FM_I2S_OFF);
+			if (retval) {
+				pr_err("%s: config_i2s_gpio failed\n",
+								__func__);
+			}
+		}
+	}
+
+	SET_REG_FIELD(radio->registers[AUDIOCTRL],
+		((rx_on && analog_on) ? 1 : 0),
+		AUDIORX_ANALOG_OFFSET,
+		AUDIORX_ANALOG_MASK);
+	SET_REG_FIELD(radio->registers[AUDIOCTRL],
+		((rx_on && digital_on) ? 1 : 0),
+		AUDIORX_DIGITAL_OFFSET,
+		AUDIORX_DIGITAL_MASK);
+	SET_REG_FIELD(radio->registers[AUDIOCTRL],
+		(rx_on ? 0 : 1),
+		AUDIOTX_OFFSET,
+		AUDIOTX_MASK);
+	/*
+
+	I2S Master/Slave configuration:
+	Setting the FM SoC as I2S Master/Slave
+		'false'		- FM SoC is I2S Slave
+		'true'		- FM SoC is I2S Master
+
+	We get this infomation from the respective target's board file :
+		MSM7x30         - FM SoC is I2S Slave
+		MSM8x60         - FM SoC is I2S Slave
+		MSM7x27A        - FM SoC is I2S Master
+	*/
+
+	if (!radio->pdata->is_fm_soc_i2s_master) {
+		FMDBG("FM SoC is I2S Slave\n");
+		SET_REG_FIELD(radio->registers[AUDIOCTRL],
+		(0),
+		I2SCTRL_OFFSET,
+		I2SCTRL_MASK);
+	} else {
+		FMDBG("FM SoC is I2S Master\n");
+		SET_REG_FIELD(radio->registers[AUDIOCTRL],
+		(1),
+		I2SCTRL_OFFSET,
+		I2SCTRL_MASK);
+	}
+	FMDBG("%s: %x\n", __func__, radio->registers[AUDIOCTRL]);
+	return tavarua_write_register(radio, AUDIOCTRL,
+					radio->registers[AUDIOCTRL]);
+
+}
+
+/*==============================================================
+FUNCTION:  tavarua_probe
+==============================================================*/
+/**
+  Once called this functions initiates, allocates resources and registers video
+  tuner device with the v4l2 framework.
+
+  NOTE:
+  probe() should verify that the specified device hardware
+  actually exists; sometimes platform setup code can't be sure.  The probing
+  can use device resources, including clocks, and device platform_data.
+
+  @param pdev: platform device to be probed.
+
+  @return On success 0 is returned, else error code.
+	-ENOMEM in low memory cases
+*/
+static int  __init tavarua_probe(struct platform_device *pdev)
+{
+
+	struct marimba_fm_platform_data *tavarua_pdata;
+	struct tavarua_device *radio;
+	int retval;
+	int i;
+	FMDBG("%s: probe called\n", __func__);
+	/* private data allocation */
+	radio = kzalloc(sizeof(struct tavarua_device), GFP_KERNEL);
+	if (!radio) {
+		retval = -ENOMEM;
+	goto err_initial;
+	}
+
+	radio->marimba = platform_get_drvdata(pdev);
+	tavarua_pdata = pdev->dev.platform_data;
+	radio->pdata = tavarua_pdata;
+	radio->dev = &pdev->dev;
+	platform_set_drvdata(pdev, radio);
+
+	/* video device allocation */
+	radio->videodev = video_device_alloc();
+	if (!radio->videodev)
+		goto err_radio;
+
+	/* initial configuration */
+	memcpy(radio->videodev, &tavarua_viddev_template,
+	  sizeof(tavarua_viddev_template));
+
+	/*allocate internal buffers for decoded rds and event buffer*/
+	for (i = 0; i < TAVARUA_BUF_MAX; i++) {
+		int kfifo_alloc_rc=0;
+		spin_lock_init(&radio->buf_lock[i]);
+
+		if (i == TAVARUA_BUF_RAW_RDS)
+			kfifo_alloc_rc = kfifo_alloc(&radio->data_buf[i],
+				rds_buf*3, GFP_KERNEL);
+		else if (i == TAVARUA_BUF_RT_RDS)
+			kfifo_alloc_rc = kfifo_alloc(&radio->data_buf[i],
+				STD_BUF_SIZE * 2, GFP_KERNEL);
+		else
+			kfifo_alloc_rc = kfifo_alloc(&radio->data_buf[i],
+				STD_BUF_SIZE, GFP_KERNEL);
+
+		if (kfifo_alloc_rc!=0) {
+			printk(KERN_ERR "%s: failed allocating buffers %d\n",
+				__func__, kfifo_alloc_rc);
+			goto err_bufs;
+		}
+	}
+	/* initializing the device count  */
+	atomic_set(&radio->users, 1);
+	radio->xfr_in_progress = 0;
+	radio->xfr_bytes_left = 0;
+	for (i = 0; i < TAVARUA_XFR_MAX; i++)
+		radio->pending_xfrs[i] = 0;
+
+	/* init transmit data */
+	radio->tx_mode = TAVARUA_TX_RT;
+		/* Init RT and PS Tx datas*/
+	radio->pty = 0;
+	radio->pi = 0;
+	radio->ps_repeatcount = 0;
+		/* init search params */
+	radio->srch_params.srch_pty = 0;
+	radio->srch_params.srch_pi = 0;
+	radio->srch_params.preset_num = 0;
+	radio->srch_params.get_list = 0;
+	/* radio initializes to low power mode */
+	radio->lp_mode = 1;
+	radio->handle_irq = 1;
+	/* init lock */
+	mutex_init(&radio->lock);
+	/* init completion flags */
+	init_completion(&radio->sync_xfr_start);
+	init_completion(&radio->sync_req_done);
+	radio->tune_req = 0;
+	/* initialize wait queue for event read */
+	init_waitqueue_head(&radio->event_queue);
+	/* initialize wait queue for raw rds read */
+	init_waitqueue_head(&radio->read_queue);
+
+	video_set_drvdata(radio->videodev, radio);
+    /*Start the worker thread for event handling and register read_int_stat
+	as worker function*/
+	radio->wqueue  = create_singlethread_workqueue("kfmradio");
+	if (!radio->wqueue)
+		return -ENOMEM;
+
+	/* register video device */
+	if (video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr)) {
+		printk(KERN_WARNING DRIVER_NAME
+				": Could not register video device\n");
+		goto err_all;
+	}
+	private_data = radio;
+	return 0;
+
+err_all:
+	video_device_release(radio->videodev);
+err_bufs:
+	for (; i > -1; i--)
+		kfifo_free(&radio->data_buf[i]);
+err_radio:
+	kfree(radio);
+err_initial:
+	return retval;
+}
+
+/*==============================================================
+FUNCTION:  tavarua_remove
+==============================================================*/
+/**
+  Removes the device.
+
+  @param pdev: platform device to be removed.
+
+  @return On success 0 is returned, else error code.
+*/
+static int __devexit tavarua_remove(struct platform_device *pdev)
+{
+	int i;
+	struct tavarua_device *radio = platform_get_drvdata(pdev);
+
+	/* disable irq */
+	tavarua_disable_irq(radio);
+
+	destroy_workqueue(radio->wqueue);
+
+	video_unregister_device(radio->videodev);
+
+	/* free internal buffers */
+	for (i = 0; i < TAVARUA_BUF_MAX; i++)
+		kfifo_free(&radio->data_buf[i]);
+
+	/* free state struct */
+	kfree(radio);
+
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+/*
+ Platform drivers follow the standard driver model convention, where
+ discovery/enumeration is handled outside the drivers, and drivers
+ provide probe() and remove() methods.  They support power management
+ and shutdown notifications using the standard conventions.
+*/
+static struct platform_driver tavarua_driver = {
+	.driver = {
+		.owner  = THIS_MODULE,
+		.name   = "marimba_fm",
+	},
+	.remove = __devexit_p(tavarua_remove),
+	.suspend = tavarua_suspend,
+	.resume = tavarua_resume,
+}; /* platform device we're adding */
+
+
+/*************************************************************************
+ * Module Interface
+ ************************************************************************/
+
+/*==============================================================
+FUNCTION:  radio_module_init
+==============================================================*/
+/**
+  Module entry - add a platform-level device.
+
+  @return Returns zero if the driver registered and bound to a device, else
+  returns a negative error code when the driver not registered.
+*/
+static int __init radio_module_init(void)
+{
+	printk(KERN_INFO DRIVER_DESC ", Version " DRIVER_VERSION "\n");
+	return platform_driver_probe(&tavarua_driver, tavarua_probe);
+}
+
+/*==============================================================
+FUNCTION:  radio_module_exit
+==============================================================*/
+/**
+  Module exit - removes a platform-level device.
+
+  NOTE:
+  Note that this function will also release all memory- and port-based
+  resources owned by the device (dev->resource).
+
+  @return none.
+*/
+static void __exit radio_module_exit(void)
+{
+  platform_driver_unregister(&tavarua_driver);
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+
+module_init(radio_module_init);
+module_exit(radio_module_exit);
+
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
index a3fbb21..d2ed996 100644
--- a/drivers/media/rc/Kconfig
+++ b/drivers/media/rc/Kconfig
@@ -24,6 +24,17 @@
 	   LIRC daemon handles protocol decoding for IR reception and
 	   encoding for IR transmitting (aka "blasting").
 
+config USER_RC_INPUT
+	tristate "User Space Input device wrapper for Remote Control"
+	depends on RC_CORE
+
+	---help---
+	   Say Y if you want to report remote control input events
+	   from userspace.
+
+	   To compile this driver as a module, choose M here: the module will
+	   be called user-rc-input.
+
 source "drivers/media/rc/keymaps/Kconfig"
 
 config IR_NEC_DECODER
diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
index 29f364f..be02eec 100644
--- a/drivers/media/rc/Makefile
+++ b/drivers/media/rc/Makefile
@@ -13,6 +13,7 @@
 obj-$(CONFIG_IR_SANYO_DECODER) += ir-sanyo-decoder.o
 obj-$(CONFIG_IR_MCE_KBD_DECODER) += ir-mce_kbd-decoder.o
 obj-$(CONFIG_IR_LIRC_CODEC) += ir-lirc-codec.o
+obj-$(CONFIG_USER_RC_INPUT) += user-rc-input.o
 
 # stand-alone IR receivers/transmitters
 obj-$(CONFIG_RC_ATI_REMOTE) += ati_remote.o
diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c
index 0d87545..3416c91 100644
--- a/drivers/media/rc/gpio-ir-recv.c
+++ b/drivers/media/rc/gpio-ir-recv.c
@@ -26,14 +26,14 @@
 
 struct gpio_rc_dev {
 	struct rc_dev *rcdev;
-	int gpio_nr;
+	unsigned int gpio_nr;
 	bool active_low;
 };
 
 static irqreturn_t gpio_ir_recv_irq(int irq, void *dev_id)
 {
 	struct gpio_rc_dev *gpio_dev = dev_id;
-	int gval;
+	unsigned int gval;
 	int rc = 0;
 	enum raw_event_type type = IR_SPACE;
 
@@ -87,7 +87,7 @@
 	rcdev->input_name = GPIO_IR_DEVICE_NAME;
 	rcdev->input_id.bustype = BUS_HOST;
 	rcdev->driver_name = GPIO_IR_DRIVER_NAME;
-	rcdev->map_name = RC_MAP_EMPTY;
+	rcdev->map_name = RC_MAP_SAMSUNG_NECX;
 
 	gpio_dev->rcdev = rcdev;
 	gpio_dev->gpio_nr = pdata->gpio_nr;
@@ -115,6 +115,8 @@
 	if (rc < 0)
 		goto err_request_irq;
 
+	device_init_wakeup(&pdev->dev, pdata->can_wakeup);
+
 	return 0;
 
 err_request_irq:
diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile
index 49ce266..613e62c 100644
--- a/drivers/media/rc/keymaps/Makefile
+++ b/drivers/media/rc/keymaps/Makefile
@@ -61,6 +61,7 @@
 			rc-norwood.o \
 			rc-npgtech.o \
 			rc-pctv-sedna.o \
+			rc-philips.o \
 			rc-pinnacle-color.o \
 			rc-pinnacle-grey.o \
 			rc-pinnacle-pctv-hd.o \
@@ -76,6 +77,7 @@
 			rc-rc6-mce.o \
 			rc-real-audio-220-32-keys.o \
 			rc-snapstream-firefly.o \
+			rc-samsung-necx.o\
 			rc-streamzap.o \
 			rc-tbs-nec.o \
 			rc-technisat-usb2.o \
@@ -88,6 +90,7 @@
 			rc-trekstor.o \
 			rc-tt-1500.o \
 			rc-twinhan1027.o \
+			rc-ue-rf4ce.o \
 			rc-videomate-m1f.o \
 			rc-videomate-s350.o \
 			rc-videomate-tv-pvr.o \
diff --git a/drivers/media/rc/keymaps/rc-philips.c b/drivers/media/rc/keymaps/rc-philips.c
new file mode 100644
index 0000000..9f63520
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-philips.c
@@ -0,0 +1,82 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <media/rc-map.h>
+
+static struct rc_map_table philips[] = {
+
+	{ 0x00, KEY_NUMERIC_0 },
+	{ 0x01, KEY_NUMERIC_1 },
+	{ 0x02, KEY_NUMERIC_2 },
+	{ 0x03, KEY_NUMERIC_3 },
+	{ 0x04, KEY_NUMERIC_4 },
+	{ 0x05, KEY_NUMERIC_5 },
+	{ 0x06, KEY_NUMERIC_6 },
+	{ 0x07, KEY_NUMERIC_7 },
+	{ 0x08, KEY_NUMERIC_8 },
+	{ 0x09, KEY_NUMERIC_9 },
+	{ 0xF4, KEY_SOUND },
+	{ 0xF3, KEY_SCREEN },	/* Picture */
+
+	{ 0x10, KEY_VOLUMEUP },
+	{ 0x11, KEY_VOLUMEDOWN },
+	{ 0x0d, KEY_MUTE },
+	{ 0x20, KEY_CHANNELUP },
+	{ 0x21, KEY_CHANNELDOWN },
+	{ 0x0A, KEY_BACK },
+	{ 0x0f, KEY_INFO },
+	{ 0x5c, KEY_OK },
+	{ 0x58, KEY_UP },
+	{ 0x59, KEY_DOWN },
+	{ 0x5a, KEY_LEFT },
+	{ 0x5b, KEY_RIGHT },
+	{ 0xcc, KEY_PAUSE },
+	{ 0x6d, KEY_PVR },	/* Demo */
+	{ 0x40, KEY_EXIT },
+	{ 0x6e, KEY_PROG1 },	/* Scenea */
+	{ 0x6f, KEY_MODE },	/* Dual */
+	{ 0x70, KEY_SLEEP },
+	{ 0xf5, KEY_TUNER },	/* Format */
+
+	{ 0x4f, KEY_TV },
+	{ 0x3c, KEY_NEW },	/* USB */
+	{ 0x38, KEY_COMPOSE },	/* Source */
+	{ 0x54, KEY_MENU },
+
+	{ 0x0C, KEY_POWER },
+};
+
+static struct rc_map_list rc6_philips_map = {
+	.map = {
+		.scan    = philips,
+		.size    = ARRAY_SIZE(philips),
+		.rc_type = RC_TYPE_RC6,
+		.name    = RC_MAP_RC6_PHILIPS,
+	}
+};
+
+static int __init init_rc_map_rc6_philips(void)
+{
+	return rc_map_register(&rc6_philips_map);
+}
+
+static void __exit exit_rc_map_rc6_philips(void)
+{
+	rc_map_unregister(&rc6_philips_map);
+}
+
+module_init(init_rc_map_rc6_philips)
+module_exit(exit_rc_map_rc6_philips)
+
+MODULE_DESCRIPTION("Philips Remote Keymap ");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/rc/keymaps/rc-samsung-necx.c b/drivers/media/rc/keymaps/rc-samsung-necx.c
new file mode 100644
index 0000000..6cf837f
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-samsung-necx.c
@@ -0,0 +1,82 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+
+#include <media/rc-map.h>
+
+static struct rc_map_table samsung_necx[] = {
+	{ 0x70702, KEY_POWER},		/* power */
+	{ 0x7070f, KEY_MUTE},		/* mute */
+	{ 0x70704, KEY_1},
+	{ 0x70705, KEY_2},
+	{ 0x70706, KEY_3},
+	{ 0x70708, KEY_4},
+	{ 0x70709, KEY_5},
+	{ 0x7070a, KEY_6},
+	{ 0x7070c, KEY_7},
+	{ 0x7070d, KEY_8},
+	{ 0x7070e, KEY_9},
+	{ 0x70711, KEY_0},
+	{ 0x70712, KEY_CHANNELUP},
+	{ 0x70710, KEY_CHANNELDOWN},
+	{ 0x70707, KEY_VOLUMEUP},
+	{ 0x7070b, KEY_VOLUMEDOWN},
+	{ 0x70760, KEY_UP},
+	{ 0x70768, KEY_ENTER},		/* ok */
+	{ 0x70761, KEY_DOWN},
+	{ 0x70765, KEY_LEFT},
+	{ 0x70762, KEY_RIGHT},
+	{ 0x7072d, KEY_EXIT},
+	{ 0x70749, KEY_RECORD},
+	{ 0x70747, KEY_PLAY},
+	{ 0x70746, KEY_STOP},
+	{ 0x70745, KEY_REWIND},
+	{ 0x70748, KEY_FORWARD},
+	{ 0x7074a, KEY_PAUSE},
+	{ 0x70703, KEY_SLEEP},
+	{ 0x7076c, KEY_A},		/* search */
+	{ 0x70714, KEY_B},		/* camera */
+	{ 0x70715, KEY_C},
+	{ 0x70716, KEY_D},
+	{ 0x70758, KEY_BACK},
+	{ 0x7071a, KEY_MENU},
+	{ 0x7076b, KEY_LIST},
+	{ 0x70701, KEY_SCREENLOCK},
+	{ 0x7071f, KEY_HOME},
+
+};
+
+static struct rc_map_list samsung_necx_map = {
+	.map = {
+		.scan    = samsung_necx,
+		.size    = ARRAY_SIZE(samsung_necx),
+		.rc_type = RC_TYPE_NEC,
+		.name    = RC_MAP_SAMSUNG_NECX,
+	}
+};
+
+static int __init init_rc_map_samsung_necx(void)
+{
+	return rc_map_register(&samsung_necx_map);
+}
+
+static void __exit exit_rc_map_samsung_necx(void)
+{
+	rc_map_unregister(&samsung_necx_map);
+}
+
+module_init(init_rc_map_samsung_necx)
+module_exit(exit_rc_map_samsung_necx)
+
+MODULE_DESCRIPTION("SAMSUNG IR Remote Keymap");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/rc/keymaps/rc-ue-rf4ce.c b/drivers/media/rc/keymaps/rc-ue-rf4ce.c
new file mode 100644
index 0000000..71c5505
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-ue-rf4ce.c
@@ -0,0 +1,83 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <media/rc-map.h>
+
+static struct rc_map_table ue_rf4ce[] = {
+	{ 0x0a, KEY_SETUP },
+	{ 0x6b, KEY_POWER },
+	{ 0x00, KEY_OK },
+	{ 0x03, KEY_LEFT },
+	{ 0x04, KEY_RIGHT },
+	{ 0x01, KEY_UP },
+	{ 0x02, KEY_DOWN },
+	{ 0x53, KEY_HOMEPAGE },
+	{ 0x0d, KEY_EXIT },
+	{ 0x72, KEY_TV },
+	{ 0x73, KEY_VIDEO },
+	{ 0x74, KEY_COMPOSE },
+	{ 0x71, KEY_AUX },
+	{ 0x45, KEY_STOP },
+	{ 0x0b, KEY_LIST },
+	{ 0x47, KEY_RECORD },
+	{ 0x48, KEY_REWIND },
+	{ 0x44, KEY_PLAY },
+	{ 0x49, KEY_FASTFORWARD },
+	{ 0x4c, KEY_BACK },
+	{ 0x46, KEY_PAUSE },
+	{ 0x4b, KEY_NEXT },
+	{ 0x41, KEY_VOLUMEUP },
+	{ 0x42, KEY_VOLUMEDOWN },
+	{ 0x32, KEY_LAST },
+	{ 0x43, KEY_MUTE },
+	{ 0x30, KEY_CHANNELUP },
+	{ 0x31, KEY_CHANNELDOWN },
+
+	{ 0x20, KEY_NUMERIC_0 },
+	{ 0x21, KEY_NUMERIC_1 },
+	{ 0x22, KEY_NUMERIC_2 },
+	{ 0x23, KEY_NUMERIC_3 },
+	{ 0x24, KEY_NUMERIC_4 },
+	{ 0x25, KEY_NUMERIC_5 },
+	{ 0x26, KEY_NUMERIC_6 },
+	{ 0x27, KEY_NUMERIC_7 },
+	{ 0x28, KEY_NUMERIC_8 },
+	{ 0x29, KEY_NUMERIC_9 },
+	{ 0x34, KEY_INSERT },
+	{ 0x2b, KEY_ENTER },
+};
+
+static struct rc_map_list ue_rf4ce_map = {
+	.map = {
+		.scan    = ue_rf4ce,
+		.size    = ARRAY_SIZE(ue_rf4ce),
+		.rc_type = RC_TYPE_OTHER,
+		.name    = RC_MAP_UE_RF4CE,
+	}
+};
+
+static int __init init_rc_map_ue_rf4ce(void)
+{
+	return rc_map_register(&ue_rf4ce_map);
+}
+
+static void __exit exit_rc_map_ue_rf4ce(void)
+{
+	rc_map_unregister(&ue_rf4ce_map);
+}
+
+module_init(init_rc_map_ue_rf4ce)
+module_exit(exit_rc_map_ue_rf4ce)
+
+MODULE_DESCRIPTION("UE RF4CE Remote Keymap ");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/rc/user-rc-input.c b/drivers/media/rc/user-rc-input.c
new file mode 100644
index 0000000..f1a9334
--- /dev/null
+++ b/drivers/media/rc/user-rc-input.c
@@ -0,0 +1,251 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/ioctl.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/slab.h>
+
+#include <media/rc-core.h>
+#include <media/user-rc-input.h>
+
+#define MAX_RC_DEVICES		1
+#define USER_RC_INPUT_DEV_NAME	"user-rc-input"
+#define USER_RC_INPUT_DRV_NAME	"rc-user-input"
+
+struct user_rc_input_dev {
+	struct cdev rc_input_cdev;
+	struct class *rc_input_class;
+	struct device *rc_input_dev;
+	struct rc_dev *rcdev;
+	dev_t rc_input_base_dev;
+	struct device *dev;
+	int in_use;
+};
+
+static int user_rc_input_open(struct inode *inode, struct file *file)
+{
+	struct cdev *input_cdev	= inode->i_cdev;
+	struct user_rc_input_dev *input_dev =
+	container_of(input_cdev, struct user_rc_input_dev, rc_input_cdev);
+
+	if (input_dev->in_use) {
+		dev_err(input_dev->dev,
+		"Device is already open..only one instance is allowed\n");
+		return -EBUSY;
+	}
+	input_dev->in_use++;
+	file->private_data = input_dev;
+
+	return 0;
+}
+
+static int user_rc_input_release(struct inode *inode, struct file *file)
+{
+	struct user_rc_input_dev *input_dev = file->private_data;
+
+	input_dev->in_use--;
+
+	return 0;
+}
+
+static ssize_t user_rc_input_write(struct file *file, const char __user *buffer,
+						size_t count, loff_t *ppos)
+{
+	int ret;
+	struct user_rc_input_dev *input_dev = file->private_data;
+	__u8 *buf;
+
+	buf = kmalloc(count * sizeof(__u8), GFP_KERNEL);
+	if (!buf) {
+		dev_err(input_dev->dev,
+			"kmalloc failed...Insufficient memory\n");
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	if (copy_from_user(buf, buffer, count)) {
+		dev_err(input_dev->dev, "Copy from user failed\n");
+		ret = -EFAULT;
+		goto out_free;
+	}
+
+	switch (buf[0])	{
+	case USER_CONTROL_PRESSED:
+		dev_dbg(input_dev->dev, "user controlled"
+					" pressed 0x%x\n", buf[1]);
+		rc_keydown(input_dev->rcdev, buf[1], 0);
+		break;
+	case USER_CONTROL_REPEATED:
+		dev_dbg(input_dev->dev, "user controlled"
+					" repeated 0x%x\n", buf[1]);
+		rc_repeat(input_dev->rcdev);
+		break;
+	case USER_CONTROL_RELEASED:
+		dev_dbg(input_dev->dev, "user controlled"
+					" released 0x%x\n", buf[1]);
+		rc_keyup(input_dev->rcdev);
+		break;
+	}
+
+out_free:
+	kfree(buf);
+out:
+	return ret;
+}
+
+const struct file_operations fops = {
+	.owner  = THIS_MODULE,
+	.open   = user_rc_input_open,
+	.write  = user_rc_input_write,
+	.release = user_rc_input_release,
+};
+
+static int __devinit user_rc_input_probe(struct platform_device *pdev)
+{
+	struct user_rc_input_dev *user_rc_dev;
+	struct rc_dev *rcdev;
+	int retval;
+
+	user_rc_dev = kzalloc(sizeof(struct user_rc_input_dev), GFP_KERNEL);
+	if (!user_rc_dev)
+		return -ENOMEM;
+
+	user_rc_dev->rc_input_class = class_create(THIS_MODULE,
+						"user-rc-input-loopback");
+
+	if (IS_ERR(user_rc_dev->rc_input_class)) {
+		retval = PTR_ERR(user_rc_dev->rc_input_class);
+		goto err;
+	}
+
+	retval = alloc_chrdev_region(&user_rc_dev->rc_input_base_dev, 0,
+				MAX_RC_DEVICES,	USER_RC_INPUT_DEV_NAME);
+
+	if (retval) {
+		dev_err(&pdev->dev,
+			"alloc_chrdev_region failed\n");
+		goto alloc_chrdev_err;
+	}
+
+	dev_info(&pdev->dev, "User space report key event input "
+					"loopback driver registered, "
+		"major %d\n", MAJOR(user_rc_dev->rc_input_base_dev));
+
+	cdev_init(&user_rc_dev->rc_input_cdev, &fops);
+	retval = cdev_add(&user_rc_dev->rc_input_cdev,
+				user_rc_dev->rc_input_base_dev,
+							MAX_RC_DEVICES);
+	if (retval) {
+		dev_err(&pdev->dev, "cdev_add failed\n");
+		goto cdev_add_err;
+	}
+	user_rc_dev->rc_input_dev =
+				device_create(user_rc_dev->rc_input_class,
+									NULL,
+				MKDEV(MAJOR(user_rc_dev->rc_input_base_dev),
+				0), NULL, "user-rc-input-dev%d", 0);
+
+	if (IS_ERR(user_rc_dev->rc_input_dev)) {
+		retval = PTR_ERR(user_rc_dev->rc_input_dev);
+		dev_err(&pdev->dev, "device_create failed\n");
+		goto device_create_err;
+	}
+
+	rcdev = rc_allocate_device();
+	if (!rcdev) {
+		dev_err(&pdev->dev, "failed to allocate rc device");
+		retval = -ENOMEM;
+		goto err_allocate_device;
+	}
+
+	rcdev->driver_type = RC_DRIVER_SCANCODE;
+	rcdev->allowed_protos = RC_TYPE_OTHER;
+	rcdev->input_name = USER_RC_INPUT_DEV_NAME;
+	rcdev->input_id.bustype = BUS_HOST;
+	rcdev->driver_name = USER_RC_INPUT_DRV_NAME;
+	rcdev->map_name = RC_MAP_UE_RF4CE;
+
+	retval = rc_register_device(rcdev);
+	if (retval < 0) {
+		dev_err(&pdev->dev, "failed to register rc device\n");
+		goto rc_register_err;
+	}
+	user_rc_dev->rcdev = rcdev;
+	user_rc_dev->dev = &pdev->dev;
+	platform_set_drvdata(pdev, user_rc_dev);
+	user_rc_dev->in_use = 0;
+
+	return 0;
+
+rc_register_err:
+	rc_free_device(rcdev);
+err_allocate_device:
+	device_destroy(user_rc_dev->rc_input_class,
+			MKDEV(MAJOR(user_rc_dev->rc_input_base_dev), 0));
+cdev_add_err:
+	unregister_chrdev_region(user_rc_dev->rc_input_base_dev,
+							MAX_RC_DEVICES);
+device_create_err:
+	cdev_del(&user_rc_dev->rc_input_cdev);
+alloc_chrdev_err:
+	class_destroy(user_rc_dev->rc_input_class);
+err:
+	kfree(user_rc_dev);
+	return retval;
+}
+
+static int __devexit user_rc_input_remove(struct platform_device *pdev)
+{
+	struct user_rc_input_dev *user_rc_dev = platform_get_drvdata(pdev);
+
+	platform_set_drvdata(pdev, NULL);
+	rc_free_device(user_rc_dev->rcdev);
+	device_destroy(user_rc_dev->rc_input_class,
+			MKDEV(MAJOR(user_rc_dev->rc_input_base_dev), 0));
+	unregister_chrdev_region(user_rc_dev->rc_input_base_dev,
+							MAX_RC_DEVICES);
+	cdev_del(&user_rc_dev->rc_input_cdev);
+	class_destroy(user_rc_dev->rc_input_class);
+	kfree(user_rc_dev);
+
+	return 0;
+}
+
+static struct platform_driver user_rc_input_driver = {
+	.probe  = user_rc_input_probe,
+	.remove = __devexit_p(user_rc_input_remove),
+	.driver = {
+		.name   = USER_RC_INPUT_DRV_NAME,
+		.owner  = THIS_MODULE,
+	},
+};
+
+static int __init user_rc_input_init(void)
+{
+	return platform_driver_register(&user_rc_input_driver);
+}
+module_init(user_rc_input_init);
+
+static void __exit user_rc_input_exit(void)
+{
+	platform_driver_unregister(&user_rc_input_driver);
+}
+module_exit(user_rc_input_exit);
+
+MODULE_DESCRIPTION("User RC Input driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index ce1e7ba..ae020e8 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -66,6 +66,12 @@
 	select VIDEOBUF2_CORE
 	select VIDEOBUF2_MEMOPS
 	tristate
+
+config VIDEOBUF2_MSM_MEM
+	tristate "MSM videobuf2 extensions"
+	select VIDEOBUF2_DMA_SG
+	select VIDEOBUF2_DMA_CONTIG
+	select VIDEOBUF2_VMALLOC
 #
 # Multimedia Video device configuration
 #
@@ -633,6 +639,14 @@
 	  Say Y here if you want to test video apps or debug V4L devices.
 	  In doubt, say N.
 
+config MSM_VCAP
+	tristate "Qualcomm MSM VCAP"
+	depends on VIDEO_DEV && VIDEO_V4L2
+	default y
+	---help---
+		Enables VCAP driver. This device allows for video capture and
+		video processing using the v4l2 api
+
 #
 # USB Multimedia device configuration
 #
@@ -1160,6 +1174,30 @@
 
 source "drivers/media/video/s5p-tv/Kconfig"
 
+#
+# MSM camera configuration
+#
+
+comment "Qualcomm MSM Camera And Video"
+
+menuconfig MSM_CAMERA
+	bool "Qualcomm MSM camera and video capture support"
+	depends on ARCH_MSM && VIDEO_V4L2 && I2C
+	default y
+	help
+	  Say Y here to enable selecting the video adapters for
+	  Qualcomm msm camera and video encoding
+
+config MSM_CAMERA_DEBUG
+	bool "Qualcomm MSM camera debugging with printk"
+	depends on MSM_CAMERA
+	default n
+	help
+	  Enable printk() debug for msm camera
+
+
+source "drivers/media/video/msm/Kconfig"
+
 endif # V4L_PLATFORM_DRIVERS
 endif # VIDEO_CAPTURE_DRIVERS
 
@@ -1222,3 +1260,5 @@
 	    conversion.
 
 endif # V4L_MEM2MEM_DRIVERS
+
+source "drivers/media/video/msm_vidc/Kconfig"
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index a6282a3..956a043 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -12,6 +12,7 @@
 
 videodev-objs	:=	v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \
 			v4l2-event.o v4l2-ctrls.o v4l2-subdev.o
+
 ifeq ($(CONFIG_COMPAT),y)
   videodev-objs += v4l2-compat-ioctl32.o
 endif
@@ -134,6 +135,7 @@
 obj-$(CONFIG_VIDEOBUF2_VMALLOC)		+= videobuf2-vmalloc.o
 obj-$(CONFIG_VIDEOBUF2_DMA_CONTIG)	+= videobuf2-dma-contig.o
 obj-$(CONFIG_VIDEOBUF2_DMA_SG)		+= videobuf2-dma-sg.o
+obj-$(CONFIG_VIDEOBUF2_MSM_MEM)		+= videobuf2-msm-mem.o
 
 obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
 
@@ -168,6 +170,9 @@
 obj-$(CONFIG_VIDEO_MEM2MEM_TESTDEV) += mem2mem_testdev.o
 obj-$(CONFIG_VIDEO_CX23885) += cx23885/
 
+obj-$(CONFIG_MSM_VCAP) += vcap_v4l2.o
+obj-$(CONFIG_MSM_VCAP) += vcap_vc.o
+obj-$(CONFIG_MSM_VCAP) += vcap_vp.o
 obj-$(CONFIG_VIDEO_AK881X)		+= ak881x.o
 
 obj-$(CONFIG_VIDEO_OMAP2)		+= omap2cam.o
@@ -207,7 +212,9 @@
 
 obj-y	+= davinci/
 
+obj-$(CONFIG_MSM_CAMERA) += msm/
 obj-$(CONFIG_ARCH_OMAP)	+= omap/
+obj-$(CONFIG_MSM_VIDC) += msm_vidc/
 
 ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core
 ccflags-y += -I$(srctree)/drivers/media/dvb/frontends
diff --git a/drivers/media/video/msm/Kconfig b/drivers/media/video/msm/Kconfig
new file mode 100644
index 0000000..ab4a6f2
--- /dev/null
+++ b/drivers/media/video/msm/Kconfig
@@ -0,0 +1,288 @@
+config MSM_CAMERA_V4L2
+        bool "MSM Camera V4L2 Interface"
+        depends on MSM_CAMERA
+        default n
+        ---help---
+          This flag enables V4L2 interface of MSM
+          camera driver. If enabled, application interacts
+          with /dev/video0 through V4L2 APIs. Otherwise,
+          native APIs are used through /dev/config0, /dev/frame0,
+          and /dev/control0.
+
+comment "Camera Sensor Selection"
+config MT9T013
+	bool "Sensor mt9t013 (BAYER 3M)"
+	depends on MSM_CAMERA && !ARCH_MSM8X60 && !ARCH_MSM8960 && !MSM_CAMERA_V4L2
+	default y
+	---help---
+	  MICRON 3M Bayer Sensor with AutoFocus
+config MT9D113
+	bool "Sensor mt9d113 (YUV 2M)"
+	depends on MSM_CAMERA && ARCH_MSM8X60 && !MSM_CAMERA_V4L2
+	default y
+	---help---
+	  MICRON 2M YUV Sensor
+	  This sensor is the front camera on QT8660.
+	  This uses csi mipi interface.
+	  This sensor is used only on QT device.
+config MT9D112
+	bool "Sensor mt9d112 (YUV 2M)"
+	depends on MSM_CAMERA && !ARCH_MSM8X60 && !ARCH_MSM8960 && !MSM_CAMERA_V4L2
+	default y
+	---help---
+	  MICRON 2M YUV Sensor
+config IMX074
+	bool "Sensor IMX074 (BAYER 13.5M)"
+	depends on MSM_CAMERA
+	---help---
+	SONY 13.5 MP Bayer Sensor
+config OV5640
+	bool "Sensor OV5640 (YUV 5M)"
+	depends on MSM_CAMERA && !MSM_CAMERA_V4L2
+	default n
+	---help---
+	Omni 5M YUV Sensor
+
+config OV5647
+	bool "Sensor ov5647 (BAYER 5M)"
+	depends on MSM_CAMERA
+	---help---
+	  OV 5M Bayer Sensor with AutoFocus
+
+config AD5046_ACT
+	bool "Lens actuator ad5046"
+	depends on MSM_CAMERA && OV5647
+	---help---
+	  ad5046 lens actuator driver for ov5647.
+	  Say Y here if this is msm7627A variant platform.
+config WEBCAM_OV7692_QRD
+	bool "Sensor OV7692 QRD(VGA YUV)"
+	depends on MSM_CAMERA && (ARCH_MSM7X27A || ARCH_MSM8X60)
+	default n
+	---help---
+	  Omni Vision VGA YUV Sensor for QRD Devices
+config MT9M114
+        bool "Sensor MT9M114 (YUV 1.26M)"
+        depends on MSM_CAMERA
+        ---help---
+        APTINA 1.26 MP yuv Sensor
+config WEBCAM_OV7692
+	bool "Sensor OV7692 (VGA YUV)"
+	depends on MSM_CAMERA && ARCH_MSM8X60 && !MSM_CAMERA_V4L2
+	default y
+	---help---
+	  Omni Vision VGA YUV Sensor.
+config WEBCAM_OV9726
+	bool "Sensor OV9726 (VGA Bayer)"
+	depends on MSM_CAMERA && (ARCH_MSM8X60 || ARCH_MSM7X30 || ARCH_MSM7X27A)
+	default n
+	---help---
+	  Omni Vision VGA Bayer Sensor.
+#	This Senosr is used as a webcam.
+#	This uses the CSI interface.
+config VX6953
+	bool "Sensor VX6953 (BAYER 5M)"
+	depends on MSM_CAMERA && (ARCH_MSM7X30 || ARCH_MSM8X60) && !MSM_CAMERA_V4L2
+	default y
+	---help---
+	STM 5M Bayer Sensor with EDOF
+config SN12M0PZ
+	bool "Sensor sn12m0pz (Bayer 12 MP)"
+	depends on MSM_CAMERA && ARCH_MSM7X30 && !MSM_CAMERA_V4L2
+	default y
+	---help---
+	  Sony 12 MP Bayer Sensor
+config MT9P012
+	bool "Sensor mt9p012 (BAYER 5M)"
+	depends on MSM_CAMERA && !ARCH_MSM8X60 && !MSM_CAMERA_V4L2
+	default y
+	---help---
+	  MICRON 5M Bayer Sensor with Autofocus
+
+choice
+	prompt "AF module"
+	depends on MT9P012 && !ARCH_MSM8X60 && !MSM_CAMERA_V4L2
+	default MSM_CAMERA_AF_FOXCONN
+
+config MSM_CAMERA_AF_FOXCONN
+	bool "FOXCONN Module"
+	help
+	  This driver supports FOXCONN AF module for 5M Bayer sensor
+
+config MSM_CAMERA_AF_BAM
+	bool "BAM Module"
+	help
+	  This driver supports BAM AF module for 5M Bayer sensor
+
+endchoice
+
+config MT9P012_KM
+	bool "Sensor mt9p012 KM module (BAYER 5M)"
+	depends on MSM_CAMERA && !ARCH_MSM8X60 && !MSM_CAMERA_V4L2
+	default y
+	---help---
+	  MICRON 5M Bayer Sensor KM modules with Autofocus
+
+config MT9E013
+	bool "Sensor mt9e013 module (BAYER 8M)"
+	depends on MSM_CAMERA && (ARCH_MSM7X30 || ARCH_MSM8X60 || ARCH_MSM7X27A)
+	default n
+	---help---
+	  Aptina 8M Bayer Sensor modules with Autofocus
+
+config IMX074_ACT
+	bool "Actuator IMX074 (BAYER 13.5M)"
+	depends on MSM_CAMERA
+	---help---
+	Actuator for SONY 13.5 MP Bayer Sensor
+
+config S5K3E2FX
+	bool "Sensor s5k3e2fx (Samsung 5M)"
+	depends on MSM_CAMERA && !ARCH_MSM8X60 && !MSM_CAMERA_V4L2
+	default y
+	---help---
+	  Samsung 5M with Autofocus
+
+config QS_S5K4E1
+	bool "Sensor qs_s5k4e1 (Samsung 5M)"
+	depends on MSM_CAMERA && ARCH_MSM8X60 && !MSM_CAMERA_V4L2
+	default y
+	---help---
+	  Samsung 5M with Autofocus
+
+config S5K4E1
+	bool "Sensor Sensor s5k4e1 (Samsung 5M)"
+	depends on MSM_CAMERA
+	default n
+	---help---
+	  Support for S5k4E1 samsung sensor driver.
+	  It is a Bayer 5MP sensor with auto focus and it supports
+	  two mipi lanes, required for msm7x2xA platform.
+	  Say Y here if this is msm7x2xA variant platform.
+
+config DW9712_ACT
+	bool "Lens actuator dw9721"
+	depends on MSM_CAMERA && S5K4E1
+	---help---
+	  dw9721 lens actuator driver for S5K4E1.
+	  Say Y here if this is msm7627A variant platform.
+
+config MSM_CAMERA_FLASH_SC628A
+	bool "Qualcomm MSM camera sc628a flash support"
+	depends on MSM_CAMERA
+	default n
+	---help---
+	  Enable support for LED flash for msm camera.
+	  It is a samtech charge pump flash driver and it
+	  supports spotlight and flash light modes with
+	  differrent current levels.
+
+config MSM_CAMERA_FLASH_TPS61310
+	bool "Qualcomm MSM camera tps61310 flash support"
+	depends on MSM_CAMERA
+	default n
+	---help---
+	  Enable support for LED flash for msm camera.
+	  It is a Texas Instruments multiple LED Flash
+	  for camera flash and video light applications.
+
+config IMX072
+	bool "Sensor imx072 (Sony 5M)"
+	default n
+	---help---
+	  Support for IMX072 sony sensor driver.
+	  It is a Bayer 5MP sensor with auto focus and it supports
+	  two mipi lanes, required for msm7x2xA platform.
+	  Say Y here if this is msm7x2xA variant platform.
+
+config OV2720
+	bool "Sensor ov2720 (Omnivision 2MP)"
+	depends on MSM_CAMERA
+
+config VB6801
+	bool "Sensor vb6801"
+	depends on MSM_CAMERA && !ARCH_MSM8X60 && !MSM_CAMERA_V4L2
+	---help---
+	  5M with flash
+
+config MSM_CAMERA_FLASH
+	bool "Qualcomm MSM camera flash support"
+	depends on MSM_CAMERA
+	default y
+	---help---
+	  Enable support for LED flash for msm camera
+
+config MSM_CAMERA_SENSOR
+	bool "Qualcomm MSM camera sensor support"
+	depends on MSM_CAMERA
+
+config MSM_ACTUATOR
+	bool "Qualcomm MSM actuator support"
+	depends on MSM_CAMERA
+
+config MSM_EEPROM
+	bool "Qualcomm MSM EEPROM support"
+	depends on MSM_CAMERA
+
+config IMX074_EEPROM
+	bool "IMX074 EEPROM support"
+	depends on MSM_CAMERA
+
+config IMX091_EEPROM
+	bool "IMX091 EEPROM support"
+	depends on MSM_CAMERA
+
+config MSM_GEMINI
+	tristate "Qualcomm MSM Gemini Jpeg Engine support"
+	depends on MSM_CAMERA && (ARCH_MSM7X30 || ARCH_MSM8X60 || ARCH_MSM8960)
+	default n
+	---help---
+	  Enable support for Gemini Jpeg Engine
+
+config MSM_VPE
+	tristate "Qualcomm MSM Video Pre-processing Engine support"
+	depends on MSM_CAMERA && (ARCH_MSM7X30 || ARCH_MSM8X60)
+	default y
+	---help---
+	  Enable support for Video Pre-processing Engine
+
+config QUP_EXCLUSIVE_TO_CAMERA
+	bool "QUP exclusive to camera"
+	depends on MSM_CAMERA
+	default y
+	---help---
+	  This flag enabled states that QUP
+	  is exclusive to camera. In case this
+	  is disabled, the lvs1 voltage is enabled
+	  by QUP in the board file as QUP is used by
+	  applications other than camera.
+
+config S5K3L1YX
+	bool "Sensor S5K3L1YX (BAYER 12M)"
+	depends on MSM_CAMERA
+	---help---
+		Samsung 12 MP Bayer Sensor with auto focus, uses
+		4 mipi lanes, preview config = 1984 * 1508 at 30 fps,
+		snapshot config = 4000 * 3000 at 20 fps,
+		hfr video at 60, 90 and 120 fps.
+
+config IMX091
+        bool "Sensor imx091 (Sony 13MP)"
+        depends on MSM_CAMERA
+	---help---
+	  Sony 13MP sensor back camera that uses 4 mipi lanes,
+	  runs at 30 fps preview and 14 fps snapshot
+
+config MSM_V4L2_VIDEO_OVERLAY_DEVICE
+	tristate "Qualcomm MSM V4l2 video overlay device"
+	---help---
+	  Enables support for the MSM V4L2 video
+	  overlay driver. This allows video rendering
+	  apps to render overlaid video using Video4Linux2
+	  APIs, by using /dev/videoX device
+
+config OV7692
+	bool "Sensor OV7692 (VGA YUV)"
+	depends on MSM_CAMERA
+	---help---
+	  Omni Vision VGA YUV Sensor
diff --git a/drivers/media/video/msm/Makefile b/drivers/media/video/msm/Makefile
new file mode 100644
index 0000000..b60f99f
--- /dev/null
+++ b/drivers/media/video/msm/Makefile
@@ -0,0 +1,63 @@
+GCC_VERSION      := $(shell $(CONFIG_SHELL) $(PWD)/scripts/gcc-version.sh $(CROSS_COMPILE)gcc)
+ifeq ($(GCC_VERSION),0404)
+CFLAGS_REMOVE_msm_vfe8x.o = -Wframe-larger-than=1024
+endif
+
+EXTRA_CFLAGS += -Idrivers/media/video/msm/io
+obj-$(CONFIG_MSM_CAMERA) += io/
+ifeq ($(CONFIG_MSM_CAMERA_V4L2),y)
+  EXTRA_CFLAGS += -Idrivers/media/video/msm/csi
+  EXTRA_CFLAGS += -Idrivers/media/video/msm/io
+  EXTRA_CFLAGS += -Idrivers/media/video/msm/eeprom
+  EXTRA_CFLAGS += -Idrivers/media/video/msm/sensors
+  EXTRA_CFLAGS += -Idrivers/media/video/msm/actuators
+  obj-$(CONFIG_MSM_CAMERA) += msm_isp.o msm.o msm_mem.o msm_mctl.o msm_mctl_buf.o msm_mctl_pp.o
+  obj-$(CONFIG_MSM_CAMERA) += io/ eeprom/ sensors/ actuators/ csi/
+  obj-$(CONFIG_MSM_CAMERA) += msm_gesture.o
+else
+  obj-$(CONFIG_MSM_CAMERA) += msm_camera.o
+endif
+obj-$(CONFIG_MSM_CAMERA) += msm_axi_qos.o gemini/
+obj-$(CONFIG_MSM_CAMERA_FLASH) += flash.o
+obj-$(CONFIG_ARCH_MSM_ARM11) += msm_vfe7x.o
+ifeq ($(CONFIG_MSM_CAMERA_V4L2),y)
+  obj-$(CONFIG_ARCH_MSM7X27A) += msm_vfe7x27a_v4l2.o
+else
+  obj-$(CONFIG_ARCH_MSM7X27A) += msm_vfe7x27a.o
+endif
+obj-$(CONFIG_ARCH_QSD8X50) += msm_vfe8x.o msm_vfe8x_proc.o
+ifeq ($(CONFIG_MSM_CAMERA_V4L2),y)
+  obj-$(CONFIG_ARCH_MSM8X60) += msm_vfe31_v4l2.o msm_vpe.o
+  obj-$(CONFIG_ARCH_MSM7X30) += msm_vfe31_v4l2.o msm_vpe.o msm_axi_qos.o
+else
+  obj-$(CONFIG_ARCH_MSM8X60) += msm_vfe31.o msm_vpe1.o
+  obj-$(CONFIG_ARCH_MSM7X30) += msm_vfe31.o msm_vpe1.o
+endif
+obj-$(CONFIG_ARCH_MSM8960) += msm_vfe32.o msm_vpe.o
+obj-$(CONFIG_MT9T013) += mt9t013.o mt9t013_reg.o
+obj-$(CONFIG_SN12M0PZ) += sn12m0pz.o sn12m0pz_reg.o
+obj-$(CONFIG_MT9P012) += mt9p012_reg.o
+obj-$(CONFIG_MSM_CAMERA_AF_FOXCONN) += mt9p012_fox.o
+obj-$(CONFIG_MSM_CAMERA_AF_BAM) += mt9p012_bam.o
+obj-$(CONFIG_MT9P012_KM) += mt9p012_km.o mt9p012_km_reg.o
+obj-$(CONFIG_S5K3E2FX) += s5k3e2fx.o
+#FIXME: Merge the two ifeq causes VX6953 preview not coming up.
+ifneq ($(CONFIG_MSM_CAMERA_V4L2),y)
+  obj-$(CONFIG_VX6953) += vx6953.o vx6953_reg.o
+  obj-$(CONFIG_IMX074) += imx074.o imx074_reg.o
+  obj-$(CONFIG_MT9E013) += mt9e013.o mt9e013_reg.o
+  obj-$(CONFIG_WEBCAM_OV9726) += ov9726.o ov9726_reg.o
+  obj-$(CONFIG_OV5647) += ov5647.o ov5647_reg.o
+  obj-$(CONFIG_S5K4E1) += s5k4e1.o s5k4e1_reg.o
+  obj-$(CONFIG_WEBCAM_OV7692) += ov7692.o
+  obj-$(CONFIG_WEBCAM_OV7692_QRD) += ov7692_qrd.o
+endif
+obj-$(CONFIG_QS_S5K4E1) += qs_s5k4e1.o qs_s5k4e1_reg.o
+obj-$(CONFIG_VB6801) += vb6801.o
+obj-$(CONFIG_IMX072) += imx072.o imx072_reg.o
+obj-$(CONFIG_OV5640) += ov5640.o
+obj-$(CONFIG_MT9D112) += mt9d112.o mt9d112_reg.o
+
+obj-$(CONFIG_MT9D113) += mt9d113.o mt9d113_reg.o
+obj-$(CONFIG_FB_MSM_WRITEBACK_MSM_PANEL) += wfd/
+obj-$(CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE) += msm_v4l2_video.o
diff --git a/drivers/media/video/msm/actuators/Makefile b/drivers/media/video/msm/actuators/Makefile
new file mode 100644
index 0000000..70a3a19
--- /dev/null
+++ b/drivers/media/video/msm/actuators/Makefile
@@ -0,0 +1,4 @@
+GCC_VERSION      := $(shell $(CONFIG_SHELL) $(PWD)/scripts/gcc-version.sh $(CROSS_COMPILE)gcc)
+EXTRA_CFLAGS += -Idrivers/media/video/msm
+EXTRA_CFLAGS += -Idrivers/media/video/msm/io
+obj-$(CONFIG_MSM_ACTUATOR) += msm_actuator.o
diff --git a/drivers/media/video/msm/actuators/msm_actuator.c b/drivers/media/video/msm/actuators/msm_actuator.c
new file mode 100644
index 0000000..50399de
--- /dev/null
+++ b/drivers/media/video/msm/actuators/msm_actuator.c
@@ -0,0 +1,649 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include "msm_actuator.h"
+
+static struct msm_actuator_ctrl_t msm_actuator_t;
+
+static struct msm_actuator msm_vcm_actuator_table = {
+	.act_type = ACTUATOR_VCM,
+	.func_tbl = {
+		.actuator_init_step_table = msm_actuator_init_step_table,
+		.actuator_move_focus = msm_actuator_move_focus,
+		.actuator_write_focus = msm_actuator_write_focus,
+		.actuator_set_default_focus = msm_actuator_set_default_focus,
+		.actuator_init_focus = msm_actuator_init_focus,
+		.actuator_i2c_write = msm_actuator_i2c_write,
+	},
+};
+
+static struct msm_actuator msm_piezo_actuator_table = {
+	.act_type = ACTUATOR_PIEZO,
+	.func_tbl = {
+		.actuator_init_step_table = NULL,
+		.actuator_move_focus = msm_actuator_piezo_move_focus,
+		.actuator_write_focus = NULL,
+		.actuator_set_default_focus =
+			msm_actuator_piezo_set_default_focus,
+		.actuator_init_focus = msm_actuator_init_focus,
+		.actuator_i2c_write = msm_actuator_i2c_write,
+	},
+};
+
+static struct msm_actuator *actuators[] = {
+	&msm_vcm_actuator_table,
+	&msm_piezo_actuator_table,
+};
+
+int32_t msm_actuator_piezo_set_default_focus(
+	struct msm_actuator_ctrl_t *a_ctrl,
+	struct msm_actuator_move_params_t *move_params)
+{
+	int32_t rc = 0;
+
+	if (a_ctrl->curr_step_pos != 0) {
+		rc = a_ctrl->func_tbl->actuator_i2c_write(a_ctrl,
+			a_ctrl->initial_code, 0);
+		rc = a_ctrl->func_tbl->actuator_i2c_write(a_ctrl,
+			a_ctrl->initial_code, 0);
+		a_ctrl->curr_step_pos = 0;
+	}
+	return rc;
+}
+
+int32_t msm_actuator_i2c_write(struct msm_actuator_ctrl_t *a_ctrl,
+	int16_t next_lens_position, uint32_t hw_params)
+{
+	struct msm_actuator_reg_params_t *write_arr = a_ctrl->reg_tbl;
+	uint32_t hw_dword = hw_params;
+	uint16_t i2c_byte1 = 0, i2c_byte2 = 0;
+	uint16_t value = 0;
+	uint32_t size = a_ctrl->reg_tbl_size, i = 0;
+	int32_t rc = 0;
+	CDBG("%s: IN\n", __func__);
+	for (i = 0; i < size; i++) {
+		if (write_arr[i].reg_write_type == MSM_ACTUATOR_WRITE_DAC) {
+			value = (next_lens_position <<
+				write_arr[i].data_shift) |
+				((hw_dword & write_arr[i].hw_mask) >>
+				write_arr[i].hw_shift);
+
+			if (write_arr[i].reg_addr != 0xFFFF) {
+				i2c_byte1 = write_arr[i].reg_addr;
+				i2c_byte2 = value;
+				if (size != (i+1)) {
+					i2c_byte2 = (i2c_byte2 & 0xFF00) >> 8;
+					CDBG("%s: byte1:0x%x, byte2:0x%x\n",
+					__func__, i2c_byte1, i2c_byte2);
+					rc = msm_camera_i2c_write(
+						&a_ctrl->i2c_client,
+						i2c_byte1, i2c_byte2,
+						a_ctrl->i2c_data_type);
+					if (rc < 0) {
+						pr_err("%s: i2c write error:%d\n",
+							__func__, rc);
+						return rc;
+					}
+
+					i++;
+					i2c_byte1 = write_arr[i].reg_addr;
+					i2c_byte2 = value & 0xFF;
+				}
+			} else {
+				i2c_byte1 = (value & 0xFF00) >> 8;
+				i2c_byte2 = value & 0xFF;
+			}
+		} else {
+			i2c_byte1 = write_arr[i].reg_addr;
+			i2c_byte2 = (hw_dword & write_arr[i].hw_mask) >>
+				write_arr[i].hw_shift;
+		}
+		CDBG("%s: i2c_byte1:0x%x, i2c_byte2:0x%x\n", __func__,
+			i2c_byte1, i2c_byte2);
+		rc = msm_camera_i2c_write(&a_ctrl->i2c_client,
+			i2c_byte1, i2c_byte2, a_ctrl->i2c_data_type);
+	}
+		CDBG("%s: OUT\n", __func__);
+	return rc;
+}
+
+int32_t msm_actuator_init_focus(struct msm_actuator_ctrl_t *a_ctrl,
+	uint16_t size, enum msm_actuator_data_type type,
+	struct reg_settings_t *settings)
+{
+	int32_t rc = -EFAULT;
+	int32_t i = 0;
+	CDBG("%s called\n", __func__);
+
+	for (i = 0; i < size; i++) {
+		switch (type) {
+		case MSM_ACTUATOR_BYTE_DATA:
+			rc = msm_camera_i2c_write(
+				&a_ctrl->i2c_client,
+				settings[i].reg_addr,
+				settings[i].reg_data, MSM_CAMERA_I2C_BYTE_DATA);
+			break;
+		case MSM_ACTUATOR_WORD_DATA:
+			rc = msm_camera_i2c_write(
+				&a_ctrl->i2c_client,
+				settings[i].reg_addr,
+				settings[i].reg_data, MSM_CAMERA_I2C_WORD_DATA);
+			break;
+		default:
+			pr_err("%s: Unsupport data type: %d\n",
+				__func__, type);
+			break;
+		}
+		if (rc < 0)
+			break;
+	}
+
+	a_ctrl->curr_step_pos = 0;
+	CDBG("%s Exit:%d\n", __func__, rc);
+	return rc;
+}
+
+int32_t msm_actuator_write_focus(
+	struct msm_actuator_ctrl_t *a_ctrl,
+	uint16_t curr_lens_pos,
+	struct damping_params_t *damping_params,
+	int8_t sign_direction,
+	int16_t code_boundary)
+{
+	int32_t rc = 0;
+	int16_t next_lens_pos = 0;
+	uint16_t damping_code_step = 0;
+	uint16_t wait_time = 0;
+
+	damping_code_step = damping_params->damping_step;
+	wait_time = damping_params->damping_delay;
+
+	/* Write code based on damping_code_step in a loop */
+	for (next_lens_pos =
+		curr_lens_pos + (sign_direction * damping_code_step);
+		(sign_direction * next_lens_pos) <=
+			(sign_direction * code_boundary);
+		next_lens_pos =
+			(next_lens_pos +
+				(sign_direction * damping_code_step))) {
+		rc = a_ctrl->func_tbl->
+			actuator_i2c_write(a_ctrl, next_lens_pos,
+				damping_params->hw_params);
+		if (rc < 0) {
+			pr_err("%s: error:%d\n",
+				__func__, rc);
+			return rc;
+		}
+		curr_lens_pos = next_lens_pos;
+		usleep(wait_time);
+	}
+
+	if (curr_lens_pos != code_boundary) {
+		rc = a_ctrl->func_tbl->
+			actuator_i2c_write(a_ctrl, code_boundary,
+				damping_params->hw_params);
+		usleep(wait_time);
+	}
+	return rc;
+}
+
+int32_t msm_actuator_piezo_move_focus(
+	struct msm_actuator_ctrl_t *a_ctrl,
+	struct msm_actuator_move_params_t *move_params)
+{
+	int32_t dest_step_position = move_params->dest_step_pos;
+	int32_t rc = 0;
+	int32_t num_steps = move_params->num_steps;
+
+	if (num_steps == 0)
+		return rc;
+
+	rc = a_ctrl->func_tbl->
+		actuator_i2c_write(a_ctrl,
+		(num_steps *
+		a_ctrl->region_params[0].code_per_step),
+		move_params->ringing_params[0].hw_params);
+
+	a_ctrl->curr_step_pos = dest_step_position;
+	return rc;
+}
+
+int32_t msm_actuator_move_focus(
+	struct msm_actuator_ctrl_t *a_ctrl,
+	struct msm_actuator_move_params_t *move_params)
+{
+	int32_t rc = 0;
+	int8_t sign_dir = move_params->sign_dir;
+	uint16_t step_boundary = 0;
+	uint16_t target_step_pos = 0;
+	uint16_t target_lens_pos = 0;
+	int16_t dest_step_pos = move_params->dest_step_pos;
+	uint16_t curr_lens_pos = 0;
+	int dir = move_params->dir;
+	int32_t num_steps = move_params->num_steps;
+
+	CDBG("%s called, dir %d, num_steps %d\n",
+		__func__,
+		dir,
+		num_steps);
+
+	if (dest_step_pos == a_ctrl->curr_step_pos)
+		return rc;
+
+	curr_lens_pos = a_ctrl->step_position_table[a_ctrl->curr_step_pos];
+	CDBG("curr_step_pos =%d dest_step_pos =%d curr_lens_pos=%d\n",
+		a_ctrl->curr_step_pos, dest_step_pos, curr_lens_pos);
+
+	while (a_ctrl->curr_step_pos != dest_step_pos) {
+		step_boundary =
+			a_ctrl->region_params[a_ctrl->curr_region_index].
+			step_bound[dir];
+		if ((dest_step_pos * sign_dir) <=
+			(step_boundary * sign_dir)) {
+
+			target_step_pos = dest_step_pos;
+			target_lens_pos =
+				a_ctrl->step_position_table[target_step_pos];
+			if (curr_lens_pos == target_lens_pos)
+				return rc;
+			rc = a_ctrl->func_tbl->
+				actuator_write_focus(
+					a_ctrl,
+					curr_lens_pos,
+					&(move_params->
+						ringing_params[a_ctrl->
+						curr_region_index]),
+					sign_dir,
+					target_lens_pos);
+			if (rc < 0) {
+				pr_err("%s: error:%d\n",
+					__func__, rc);
+				return rc;
+			}
+			curr_lens_pos = target_lens_pos;
+
+		} else {
+			target_step_pos = step_boundary;
+			target_lens_pos =
+				a_ctrl->step_position_table[target_step_pos];
+			if (curr_lens_pos == target_lens_pos)
+				return rc;
+			rc = a_ctrl->func_tbl->
+				actuator_write_focus(
+					a_ctrl,
+					curr_lens_pos,
+					&(move_params->
+						ringing_params[a_ctrl->
+						curr_region_index]),
+					sign_dir,
+					target_lens_pos);
+			if (rc < 0) {
+				pr_err("%s: error:%d\n",
+					__func__, rc);
+				return rc;
+			}
+			curr_lens_pos = target_lens_pos;
+
+			a_ctrl->curr_region_index += sign_dir;
+		}
+		a_ctrl->curr_step_pos = target_step_pos;
+	}
+
+	return rc;
+}
+
+int32_t msm_actuator_init_step_table(struct msm_actuator_ctrl_t *a_ctrl,
+	struct msm_actuator_set_info_t *set_info)
+{
+	int16_t code_per_step = 0;
+	int32_t rc = 0;
+	int16_t cur_code = 0;
+	int16_t step_index = 0, region_index = 0;
+	uint16_t step_boundary = 0;
+	uint32_t max_code_size = 1;
+	uint16_t data_size = set_info->actuator_params.data_size;
+	CDBG("%s called\n", __func__);
+
+	for (; data_size > 0; data_size--)
+		max_code_size *= 2;
+
+	kfree(a_ctrl->step_position_table);
+	a_ctrl->step_position_table = NULL;
+
+	/* Fill step position table */
+	a_ctrl->step_position_table =
+		kmalloc(sizeof(uint16_t) *
+		(set_info->af_tuning_params.total_steps + 1), GFP_KERNEL);
+
+	if (a_ctrl->step_position_table == NULL)
+		return -EFAULT;
+
+	cur_code = set_info->af_tuning_params.initial_code;
+	a_ctrl->step_position_table[step_index++] = cur_code;
+	for (region_index = 0;
+		region_index < a_ctrl->region_size;
+		region_index++) {
+		code_per_step =
+			a_ctrl->region_params[region_index].code_per_step;
+		step_boundary =
+			a_ctrl->region_params[region_index].
+			step_bound[MOVE_NEAR];
+		for (; step_index <= step_boundary;
+			step_index++) {
+			cur_code += code_per_step;
+			if (cur_code < max_code_size)
+				a_ctrl->step_position_table[step_index] =
+					cur_code;
+			else {
+				for (; step_index <
+					set_info->af_tuning_params.total_steps;
+					step_index++)
+					a_ctrl->
+						step_position_table[
+						step_index] =
+						max_code_size;
+
+				return rc;
+			}
+		}
+	}
+
+	return rc;
+}
+
+int32_t msm_actuator_set_default_focus(
+	struct msm_actuator_ctrl_t *a_ctrl,
+	struct msm_actuator_move_params_t *move_params)
+{
+	int32_t rc = 0;
+	CDBG("%s called\n", __func__);
+
+	if (a_ctrl->curr_step_pos != 0)
+		rc = a_ctrl->func_tbl->actuator_move_focus(a_ctrl, move_params);
+	return rc;
+}
+
+int32_t msm_actuator_power_down(struct msm_actuator_ctrl_t *a_ctrl)
+{
+	int32_t rc = 0;
+	if (a_ctrl->vcm_enable) {
+		rc = gpio_direction_output(a_ctrl->vcm_pwd, 0);
+		if (!rc)
+			gpio_free(a_ctrl->vcm_pwd);
+	}
+
+	kfree(a_ctrl->step_position_table);
+	a_ctrl->step_position_table = NULL;
+	return rc;
+}
+
+int32_t msm_actuator_init(struct msm_actuator_ctrl_t *a_ctrl,
+	struct msm_actuator_set_info_t *set_info) {
+	struct reg_settings_t *init_settings = NULL;
+	int32_t rc = -EFAULT;
+	uint16_t i = 0;
+	CDBG("%s: IN\n", __func__);
+
+	for (i = 0; i < ARRAY_SIZE(actuators); i++) {
+		if (set_info->actuator_params.act_type ==
+			actuators[i]->act_type) {
+			a_ctrl->func_tbl = &actuators[i]->func_tbl;
+			rc = 0;
+		}
+	}
+
+	if (rc < 0) {
+		pr_err("%s: Actuator function table not found\n", __func__);
+		return rc;
+	}
+
+	a_ctrl->region_size = set_info->af_tuning_params.region_size;
+	if (a_ctrl->region_size > MAX_ACTUATOR_REGION) {
+		pr_err("%s: MAX_ACTUATOR_REGION is exceeded.\n", __func__);
+		return -EFAULT;
+	}
+	a_ctrl->total_steps = set_info->af_tuning_params.total_steps;
+	a_ctrl->pwd_step = set_info->af_tuning_params.pwd_step;
+	a_ctrl->total_steps = set_info->af_tuning_params.total_steps;
+
+	if (copy_from_user(&a_ctrl->region_params,
+		(void *)set_info->af_tuning_params.region_params,
+		a_ctrl->region_size * sizeof(struct region_params_t)))
+		return -EFAULT;
+
+	a_ctrl->i2c_data_type = set_info->actuator_params.i2c_data_type;
+	a_ctrl->i2c_client.client->addr = set_info->actuator_params.i2c_addr;
+	a_ctrl->i2c_client.addr_type = set_info->actuator_params.i2c_addr_type;
+	a_ctrl->reg_tbl_size = set_info->actuator_params.reg_tbl_size;
+	if (a_ctrl->reg_tbl_size > MAX_ACTUATOR_REG_TBL_SIZE) {
+		pr_err("%s: MAX_ACTUATOR_REG_TBL_SIZE is exceeded.\n",
+			__func__);
+		return -EFAULT;
+	}
+	if (copy_from_user(&a_ctrl->reg_tbl,
+		(void *)set_info->actuator_params.reg_tbl_params,
+		a_ctrl->reg_tbl_size *
+		sizeof(struct msm_actuator_reg_params_t)))
+		return -EFAULT;
+
+	if (set_info->actuator_params.init_setting_size) {
+		if (a_ctrl->func_tbl->actuator_init_focus) {
+			init_settings = kmalloc(sizeof(struct reg_settings_t) *
+				(set_info->actuator_params.init_setting_size),
+				GFP_KERNEL);
+			if (init_settings == NULL) {
+				pr_err("%s Error allocating memory for init_settings\n",
+					__func__);
+				return -EFAULT;
+			}
+			if (copy_from_user(init_settings,
+				(void *)set_info->actuator_params.init_settings,
+				set_info->actuator_params.init_setting_size *
+				sizeof(struct reg_settings_t))) {
+				kfree(init_settings);
+				pr_err("%s Error copying init_settings\n",
+					__func__);
+				return -EFAULT;
+			}
+			rc = a_ctrl->func_tbl->actuator_init_focus(a_ctrl,
+				set_info->actuator_params.init_setting_size,
+				a_ctrl->i2c_data_type,
+				init_settings);
+			kfree(init_settings);
+			if (rc < 0) {
+				pr_err("%s Error actuator_init_focus\n",
+					__func__);
+				return -EFAULT;
+			}
+		}
+	}
+
+	a_ctrl->initial_code = set_info->af_tuning_params.initial_code;
+	if (a_ctrl->func_tbl->actuator_init_step_table)
+		rc = a_ctrl->func_tbl->
+			actuator_init_step_table(a_ctrl, set_info);
+
+	a_ctrl->curr_step_pos = 0;
+	a_ctrl->curr_region_index = 0;
+
+	return rc;
+}
+
+
+int32_t msm_actuator_config(struct msm_actuator_ctrl_t *a_ctrl,
+							void __user *argp)
+{
+	struct msm_actuator_cfg_data cdata;
+	int32_t rc = 0;
+	if (copy_from_user(&cdata,
+		(void *)argp,
+		sizeof(struct msm_actuator_cfg_data)))
+		return -EFAULT;
+	mutex_lock(a_ctrl->actuator_mutex);
+	CDBG("%s called, type %d\n", __func__, cdata.cfgtype);
+	switch (cdata.cfgtype) {
+	case CFG_SET_ACTUATOR_INFO:
+		rc = msm_actuator_init(a_ctrl, &cdata.cfg.set_info);
+		if (rc < 0)
+			pr_err("%s init table failed %d\n", __func__, rc);
+		break;
+
+	case CFG_SET_DEFAULT_FOCUS:
+		rc = a_ctrl->func_tbl->actuator_set_default_focus(a_ctrl,
+			&cdata.cfg.move);
+		if (rc < 0)
+			pr_err("%s move focus failed %d\n", __func__, rc);
+		break;
+
+	case CFG_MOVE_FOCUS:
+		rc = a_ctrl->func_tbl->actuator_move_focus(a_ctrl,
+			&cdata.cfg.move);
+		if (rc < 0)
+			pr_err("%s move focus failed %d\n", __func__, rc);
+		break;
+
+	default:
+		break;
+	}
+	mutex_unlock(a_ctrl->actuator_mutex);
+	return rc;
+}
+
+int32_t msm_actuator_i2c_probe(
+	struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	int rc = 0;
+	struct msm_actuator_ctrl_t *act_ctrl_t = NULL;
+	CDBG("%s called\n", __func__);
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		pr_err("i2c_check_functionality failed\n");
+		goto probe_failure;
+	}
+
+	act_ctrl_t = (struct msm_actuator_ctrl_t *)(id->driver_data);
+	CDBG("%s client = %x\n",
+		__func__, (unsigned int) client);
+	act_ctrl_t->i2c_client.client = client;
+
+	/* Assign name for sub device */
+	snprintf(act_ctrl_t->sdev.name, sizeof(act_ctrl_t->sdev.name),
+			 "%s", act_ctrl_t->i2c_driver->driver.name);
+
+	/* Initialize sub device */
+	v4l2_i2c_subdev_init(&act_ctrl_t->sdev,
+		act_ctrl_t->i2c_client.client,
+		act_ctrl_t->act_v4l2_subdev_ops);
+
+	CDBG("%s succeeded\n", __func__);
+	return rc;
+
+probe_failure:
+	pr_err("%s failed! rc = %d\n", __func__, rc);
+	return rc;
+}
+
+int32_t msm_actuator_power_up(struct msm_actuator_ctrl_t *a_ctrl)
+{
+	int rc = 0;
+	CDBG("%s called\n", __func__);
+
+	CDBG("vcm info: %x %x\n", a_ctrl->vcm_pwd,
+		a_ctrl->vcm_enable);
+	if (a_ctrl->vcm_enable) {
+		rc = gpio_request(a_ctrl->vcm_pwd, "msm_actuator");
+		if (!rc) {
+			CDBG("Enable VCM PWD\n");
+			gpio_direction_output(a_ctrl->vcm_pwd, 1);
+		}
+	}
+	return rc;
+}
+
+DEFINE_MUTEX(msm_actuator_mutex);
+
+static const struct i2c_device_id msm_actuator_i2c_id[] = {
+	{"msm_actuator", (kernel_ulong_t)&msm_actuator_t},
+	{ }
+};
+
+static struct i2c_driver msm_actuator_i2c_driver = {
+	.id_table = msm_actuator_i2c_id,
+	.probe  = msm_actuator_i2c_probe,
+	.remove = __exit_p(msm_actuator_i2c_remove),
+	.driver = {
+		.name = "msm_actuator",
+	},
+};
+
+static int __init msm_actuator_i2c_add_driver(
+	void)
+{
+	CDBG("%s called\n", __func__);
+	return i2c_add_driver(msm_actuator_t.i2c_driver);
+}
+
+long msm_actuator_subdev_ioctl(struct v4l2_subdev *sd,
+			unsigned int cmd, void *arg)
+{
+	struct msm_actuator_ctrl_t *a_ctrl = get_actrl(sd);
+	void __user *argp = (void __user *)arg;
+	switch (cmd) {
+	case VIDIOC_MSM_ACTUATOR_CFG:
+		return msm_actuator_config(a_ctrl, argp);
+	default:
+		return -ENOIOCTLCMD;
+	}
+}
+
+int32_t msm_actuator_power(struct v4l2_subdev *sd, int on)
+{
+	int rc = 0;
+	struct msm_actuator_ctrl_t *a_ctrl = get_actrl(sd);
+	mutex_lock(a_ctrl->actuator_mutex);
+	if (on)
+		rc = msm_actuator_power_up(a_ctrl);
+	else
+		rc = msm_actuator_power_down(a_ctrl);
+	mutex_unlock(a_ctrl->actuator_mutex);
+	return rc;
+}
+
+struct msm_actuator_ctrl_t *get_actrl(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct msm_actuator_ctrl_t, sdev);
+}
+
+static struct v4l2_subdev_core_ops msm_actuator_subdev_core_ops = {
+	.ioctl = msm_actuator_subdev_ioctl,
+	.s_power = msm_actuator_power,
+};
+
+static struct v4l2_subdev_ops msm_actuator_subdev_ops = {
+	.core = &msm_actuator_subdev_core_ops,
+};
+
+static struct msm_actuator_ctrl_t msm_actuator_t = {
+	.i2c_driver = &msm_actuator_i2c_driver,
+	.act_v4l2_subdev_ops = &msm_actuator_subdev_ops,
+
+	.curr_step_pos = 0,
+	.curr_region_index = 0,
+	.actuator_mutex = &msm_actuator_mutex,
+
+};
+
+subsys_initcall(msm_actuator_i2c_add_driver);
+MODULE_DESCRIPTION("MSM ACTUATOR");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/actuators/msm_actuator.h b/drivers/media/video/msm/actuators/msm_actuator.h
new file mode 100644
index 0000000..4f936e7
--- /dev/null
+++ b/drivers/media/video/msm/actuators/msm_actuator.h
@@ -0,0 +1,128 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef MSM_ACTUATOR_H
+#define MSM_ACTUATOR_H
+
+#include <linux/i2c.h>
+#include <mach/camera.h>
+#include <mach/gpio.h>
+#include <media/v4l2-subdev.h>
+#include <media/msm_camera.h>
+#include "msm_camera_i2c.h"
+
+#ifdef LERROR
+#undef LERROR
+#endif
+
+#ifdef LINFO
+#undef LINFO
+#endif
+
+#define LERROR(fmt, args...) pr_err(fmt, ##args)
+
+#define CONFIG_MSM_CAMERA_ACT_DBG 0
+
+#if CONFIG_MSM_CAMERA_ACT_DBG
+#define LINFO(fmt, args...) printk(fmt, ##args)
+#else
+#define LINFO(fmt, args...) CDBG(fmt, ##args)
+#endif
+
+struct msm_actuator_ctrl_t;
+
+struct msm_actuator_func_tbl {
+	int32_t (*actuator_i2c_write_b_af)(struct msm_actuator_ctrl_t *,
+			uint8_t,
+			uint8_t);
+	int32_t (*actuator_init_step_table)(struct msm_actuator_ctrl_t *,
+		struct msm_actuator_set_info_t *);
+	int32_t (*actuator_init_focus)(struct msm_actuator_ctrl_t *,
+		uint16_t, enum msm_actuator_data_type, struct reg_settings_t *);
+	int32_t (*actuator_set_default_focus) (struct msm_actuator_ctrl_t *,
+			struct msm_actuator_move_params_t *);
+	int32_t (*actuator_move_focus) (struct msm_actuator_ctrl_t *,
+			struct msm_actuator_move_params_t *);
+	int32_t (*actuator_i2c_write)(struct msm_actuator_ctrl_t *,
+			int16_t, uint32_t);
+	int32_t (*actuator_write_focus)(struct msm_actuator_ctrl_t *,
+			uint16_t,
+			struct damping_params_t *,
+			int8_t,
+			int16_t);
+};
+
+struct msm_actuator {
+	enum actuator_type act_type;
+	struct msm_actuator_func_tbl func_tbl;
+};
+
+struct msm_actuator_ctrl_t {
+	struct i2c_driver *i2c_driver;
+	struct msm_camera_i2c_client i2c_client;
+	struct mutex *actuator_mutex;
+	struct msm_actuator_func_tbl *func_tbl;
+	enum msm_actuator_data_type i2c_data_type;
+	struct v4l2_subdev sdev;
+	struct v4l2_subdev_ops *act_v4l2_subdev_ops;
+
+	int16_t curr_step_pos;
+	uint16_t curr_region_index;
+	uint16_t *step_position_table;
+	struct region_params_t region_params[MAX_ACTUATOR_REGION];
+	uint16_t reg_tbl_size;
+	struct msm_actuator_reg_params_t reg_tbl[MAX_ACTUATOR_REG_TBL_SIZE];
+	uint16_t region_size;
+	void *user_data;
+	uint32_t vcm_pwd;
+	uint32_t vcm_enable;
+	uint32_t total_steps;
+	uint16_t pwd_step;
+	uint16_t initial_code;
+};
+
+struct msm_actuator_ctrl_t *get_actrl(struct v4l2_subdev *sd);
+int32_t msm_actuator_i2c_write(struct msm_actuator_ctrl_t *a_ctrl,
+		int16_t next_lens_position, uint32_t hw_params);
+int32_t msm_actuator_init_focus(struct msm_actuator_ctrl_t *a_ctrl,
+		uint16_t size, enum msm_actuator_data_type type,
+		struct reg_settings_t *settings);
+int32_t msm_actuator_i2c_write_b_af(struct msm_actuator_ctrl_t *a_ctrl,
+		uint8_t msb,
+		uint8_t lsb);
+int32_t msm_actuator_move_focus(struct msm_actuator_ctrl_t *a_ctrl,
+		struct msm_actuator_move_params_t *move_params);
+int32_t msm_actuator_piezo_move_focus(
+		struct msm_actuator_ctrl_t *a_ctrl,
+		struct msm_actuator_move_params_t *move_params);
+int32_t msm_actuator_init_step_table(struct msm_actuator_ctrl_t *a_ctrl,
+		struct msm_actuator_set_info_t *set_info);
+int32_t msm_actuator_set_default_focus(struct msm_actuator_ctrl_t *a_ctrl,
+		struct msm_actuator_move_params_t *move_params);
+int32_t msm_actuator_piezo_set_default_focus(
+		struct msm_actuator_ctrl_t *a_ctrl,
+		struct msm_actuator_move_params_t *move_params);
+int32_t msm_actuator_i2c_probe(struct i2c_client *client,
+		const struct i2c_device_id *id);
+int32_t msm_actuator_write_focus(struct msm_actuator_ctrl_t *a_ctrl,
+		uint16_t curr_lens_pos, struct damping_params_t *damping_params,
+		int8_t sign_direction, int16_t code_boundary);
+int32_t msm_actuator_write_focus2(struct msm_actuator_ctrl_t *a_ctrl,
+		uint16_t curr_lens_pos, struct damping_params_t *damping_params,
+		int8_t sign_direction, int16_t code_boundary);
+long msm_actuator_subdev_ioctl(struct v4l2_subdev *sd,
+			unsigned int cmd, void *arg);
+int32_t msm_actuator_power(struct v4l2_subdev *sd, int on);
+
+#define VIDIOC_MSM_ACTUATOR_CFG \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 11, void __user *)
+
+#endif
diff --git a/drivers/media/video/msm/csi/Makefile b/drivers/media/video/msm/csi/Makefile
new file mode 100644
index 0000000..e5d5966
--- /dev/null
+++ b/drivers/media/video/msm/csi/Makefile
@@ -0,0 +1,6 @@
+GCC_VERSION      := $(shell $(CONFIG_SHELL) $(PWD)/scripts/gcc-version.sh $(CROSS_COMPILE)gcc)
+EXTRA_CFLAGS += -Idrivers/media/video/msm
+obj-$(CONFIG_ARCH_MSM8960) += msm_csiphy.o msm_csid.o msm_ispif.o
+obj-$(CONFIG_ARCH_MSM7X27A) += msm_csic.o
+obj-$(CONFIG_ARCH_MSM8X60) += msm_csic.o
+obj-$(CONFIG_ARCH_MSM7X30) += msm_csic.o
diff --git a/drivers/media/video/msm/csi/msm_csic.c b/drivers/media/video/msm/csi/msm_csic.c
new file mode 100644
index 0000000..a217f9d
--- /dev/null
+++ b/drivers/media/video/msm/csi/msm_csic.c
@@ -0,0 +1,490 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <mach/clk.h>
+#include <mach/board.h>
+#include <mach/camera.h>
+#include <media/msm_isp.h>
+#include "msm_csic.h"
+#include "msm.h"
+
+#define DBG_CSIC 0
+
+#define V4L2_IDENT_CSIC			50004
+/* MIPI	CSI controller registers */
+#define	MIPI_PHY_CONTROL		0x00000000
+#define	MIPI_PROTOCOL_CONTROL		0x00000004
+#define	MIPI_INTERRUPT_STATUS		0x00000008
+#define	MIPI_INTERRUPT_MASK		0x0000000C
+#define	MIPI_CAMERA_CNTL		0x00000024
+#define	MIPI_CALIBRATION_CONTROL	0x00000018
+#define	MIPI_PHY_D0_CONTROL2		0x00000038
+#define	MIPI_PHY_D1_CONTROL2		0x0000003C
+#define	MIPI_PHY_D2_CONTROL2		0x00000040
+#define	MIPI_PHY_D3_CONTROL2		0x00000044
+#define	MIPI_PHY_CL_CONTROL		0x00000048
+#define	MIPI_PHY_D0_CONTROL		0x00000034
+#define	MIPI_PHY_D1_CONTROL		0x00000020
+#define	MIPI_PHY_D2_CONTROL		0x0000002C
+#define	MIPI_PHY_D3_CONTROL		0x00000030
+#define	MIPI_PWR_CNTL			0x00000054
+
+/*
+ * MIPI_PROTOCOL_CONTROL register bits to enable/disable the features of
+ * CSI Rx Block
+ */
+
+/* DPCM scheme */
+#define	MIPI_PROTOCOL_CONTROL_DPCM_SCHEME_SHFT			0x1e
+/* SW_RST to issue a SW reset to the CSI core */
+#define	MIPI_PROTOCOL_CONTROL_SW_RST_BMSK			0x8000000
+/* To Capture Long packet Header Info in MIPI_PROTOCOL_STATUS register */
+#define	MIPI_PROTOCOL_CONTROL_LONG_PACKET_HEADER_CAPTURE_BMSK	0x200000
+/* Data format for unpacking purpose */
+#define	MIPI_PROTOCOL_CONTROL_DATA_FORMAT_SHFT			0x13
+/* Enable decoding of payload based on data type filed of packet hdr */
+#define	MIPI_PROTOCOL_CONTROL_DECODE_ID_BMSK			0x40000
+/* Enable error correction on packet headers */
+#define	MIPI_PROTOCOL_CONTROL_ECC_EN_BMSK			0x20000
+
+/*
+ * MIPI_CALIBRATION_CONTROL register contains control info for
+ * calibration impledence controller
+*/
+
+/* Enable bit for calibration pad */
+#define	MIPI_CALIBRATION_CONTROL_SWCAL_CAL_EN_SHFT		0x16
+/* With SWCAL_STRENGTH_OVERRIDE_EN, SW_CAL_EN and MANUAL_OVERRIDE_EN
+ * the hardware calibration circuitry associated with CAL_SW_HW_MODE
+ * is bypassed
+*/
+#define	MIPI_CALIBRATION_CONTROL_SWCAL_STRENGTH_OVERRIDE_EN_SHFT	0x15
+/* To indicate the Calibration process is in the control of HW/SW */
+#define	MIPI_CALIBRATION_CONTROL_CAL_SW_HW_MODE_SHFT		0x14
+/* When this is set the strength value of the data and clk lane impedence
+ * termination is updated with MANUAL_STRENGTH settings and calibration
+ * sensing logic is idle.
+*/
+#define	MIPI_CALIBRATION_CONTROL_MANUAL_OVERRIDE_EN_SHFT	0x7
+
+/* Data lane0 control */
+/* T-hs Settle count value  for Rx */
+#define	MIPI_PHY_D0_CONTROL2_SETTLE_COUNT_SHFT			0x18
+/* Rx termination control */
+#define	MIPI_PHY_D0_CONTROL2_HS_TERM_IMP_SHFT			0x10
+/* LP Rx enable */
+#define	MIPI_PHY_D0_CONTROL2_LP_REC_EN_SHFT			0x4
+/*
+ * Enable for error in sync sequence
+ * 1 - one bit error in sync seq
+ * 0 - requires all 8 bit correct seq
+*/
+#define	MIPI_PHY_D0_CONTROL2_ERR_SOT_HS_EN_SHFT			0x3
+
+/* Comments are same as D0 */
+#define	MIPI_PHY_D1_CONTROL2_SETTLE_COUNT_SHFT			0x18
+#define	MIPI_PHY_D1_CONTROL2_HS_TERM_IMP_SHFT			0x10
+#define	MIPI_PHY_D1_CONTROL2_LP_REC_EN_SHFT			0x4
+#define	MIPI_PHY_D1_CONTROL2_ERR_SOT_HS_EN_SHFT			0x3
+
+/* Comments are same as D0 */
+#define	MIPI_PHY_D2_CONTROL2_SETTLE_COUNT_SHFT			0x18
+#define	MIPI_PHY_D2_CONTROL2_HS_TERM_IMP_SHFT			0x10
+#define	MIPI_PHY_D2_CONTROL2_LP_REC_EN_SHFT			0x4
+#define	MIPI_PHY_D2_CONTROL2_ERR_SOT_HS_EN_SHFT			0x3
+
+/* Comments are same as D0 */
+#define	MIPI_PHY_D3_CONTROL2_SETTLE_COUNT_SHFT			0x18
+#define	MIPI_PHY_D3_CONTROL2_HS_TERM_IMP_SHFT			0x10
+#define	MIPI_PHY_D3_CONTROL2_LP_REC_EN_SHFT			0x4
+#define	MIPI_PHY_D3_CONTROL2_ERR_SOT_HS_EN_SHFT			0x3
+
+/* PHY_CL_CTRL programs the parameters of clk lane of CSIRXPHY */
+/* HS Rx termination control */
+#define	MIPI_PHY_CL_CONTROL_HS_TERM_IMP_SHFT			0x18
+/* Start signal for T-hs delay */
+#define	MIPI_PHY_CL_CONTROL_LP_REC_EN_SHFT			0x2
+
+/* PHY DATA lane 0 control */
+/*
+ * HS RX equalizer strength control
+ * 00 - 0db 01 - 3db 10 - 5db 11 - 7db
+*/
+#define	MIPI_PHY_D0_CONTROL_HS_REC_EQ_SHFT			0x1c
+/* PHY DATA lane 1 control */
+/* Shutdown signal for MIPI clk phy line */
+#define	MIPI_PHY_D1_CONTROL_MIPI_CLK_PHY_SHUTDOWNB_SHFT		0x9
+/* Shutdown signal for MIPI data phy line */
+#define	MIPI_PHY_D1_CONTROL_MIPI_DATA_PHY_SHUTDOWNB_SHFT	0x8
+
+#define MSM_AXI_QOS_PREVIEW 200000
+#define MSM_AXI_QOS_SNAPSHOT 200000
+#define MSM_AXI_QOS_RECORDING 200000
+
+#define MIPI_PWR_CNTL_EN	0x07
+#define MIPI_PWR_CNTL_DIS	0x0
+
+static int msm_csic_config(struct csic_cfg_params *cfg_params)
+{
+	int rc = 0;
+	uint32_t val = 0;
+	struct csic_device *csic_dev;
+	struct msm_camera_csi_params *csic_params;
+	void __iomem *csicbase;
+	int i;
+
+	csic_dev = v4l2_get_subdevdata(cfg_params->subdev);
+	csicbase = csic_dev->base;
+	csic_params = cfg_params->parms;
+
+	/* Enable error correction for DATA lane. Applies to all data lanes */
+	msm_camera_io_w(0x4, csicbase + MIPI_PHY_CONTROL);
+
+	msm_camera_io_w(MIPI_PROTOCOL_CONTROL_SW_RST_BMSK,
+		csicbase + MIPI_PROTOCOL_CONTROL);
+
+	val = MIPI_PROTOCOL_CONTROL_LONG_PACKET_HEADER_CAPTURE_BMSK |
+		MIPI_PROTOCOL_CONTROL_DECODE_ID_BMSK |
+		MIPI_PROTOCOL_CONTROL_ECC_EN_BMSK;
+	val |= (uint32_t)(csic_params->data_format) <<
+		MIPI_PROTOCOL_CONTROL_DATA_FORMAT_SHFT;
+	val |= csic_params->dpcm_scheme <<
+		MIPI_PROTOCOL_CONTROL_DPCM_SCHEME_SHFT;
+	CDBG("%s MIPI_PROTOCOL_CONTROL val=0x%x\n", __func__, val);
+	msm_camera_io_w(val, csicbase + MIPI_PROTOCOL_CONTROL);
+
+	val = (csic_params->settle_cnt <<
+		MIPI_PHY_D0_CONTROL2_SETTLE_COUNT_SHFT) |
+		(0x0F << MIPI_PHY_D0_CONTROL2_HS_TERM_IMP_SHFT) |
+		(0x1 << MIPI_PHY_D0_CONTROL2_LP_REC_EN_SHFT) |
+		(0x1 << MIPI_PHY_D0_CONTROL2_ERR_SOT_HS_EN_SHFT);
+	CDBG("%s MIPI_PHY_D0_CONTROL2 val=0x%x\n", __func__, val);
+	for (i = 0; i < csic_params->lane_cnt; i++)
+		msm_camera_io_w(val, csicbase + MIPI_PHY_D0_CONTROL2 + i * 4);
+
+
+	val = (0x0F << MIPI_PHY_CL_CONTROL_HS_TERM_IMP_SHFT) |
+		(0x1 << MIPI_PHY_CL_CONTROL_LP_REC_EN_SHFT);
+	CDBG("%s MIPI_PHY_CL_CONTROL val=0x%x\n", __func__, val);
+	msm_camera_io_w(val, csicbase + MIPI_PHY_CL_CONTROL);
+
+	val = 0 << MIPI_PHY_D0_CONTROL_HS_REC_EQ_SHFT;
+	msm_camera_io_w(val, csicbase + MIPI_PHY_D0_CONTROL);
+
+	val = (0x1 << MIPI_PHY_D1_CONTROL_MIPI_CLK_PHY_SHUTDOWNB_SHFT) |
+		(0x1 << MIPI_PHY_D1_CONTROL_MIPI_DATA_PHY_SHUTDOWNB_SHFT);
+	CDBG("%s MIPI_PHY_D1_CONTROL val=0x%x\n", __func__, val);
+	msm_camera_io_w(val, csicbase + MIPI_PHY_D1_CONTROL);
+
+	msm_camera_io_w(0x00000000, csicbase + MIPI_PHY_D2_CONTROL);
+	msm_camera_io_w(0x00000000, csicbase + MIPI_PHY_D3_CONTROL);
+
+	/* program number of lanes and lane mapping */
+	switch (csic_params->lane_cnt) {
+	case 1:
+		msm_camera_io_w(csic_params->lane_assign << 8 | 0x4,
+			csicbase + MIPI_CAMERA_CNTL);
+		break;
+	case 2:
+		msm_camera_io_w(csic_params->lane_assign << 8 | 0x5,
+			csicbase + MIPI_CAMERA_CNTL);
+		break;
+	case 3:
+		msm_camera_io_w(csic_params->lane_assign << 8 | 0x6,
+			csicbase + MIPI_CAMERA_CNTL);
+		break;
+	case 4:
+		msm_camera_io_w(csic_params->lane_assign << 8 | 0x7,
+			csicbase + MIPI_CAMERA_CNTL);
+		break;
+	}
+
+	msm_camera_io_w(0xF077F3C0, csicbase + MIPI_INTERRUPT_MASK);
+	/*clear IRQ bits*/
+	msm_camera_io_w(0xF077F3C0, csicbase + MIPI_INTERRUPT_STATUS);
+
+	return rc;
+}
+
+static irqreturn_t msm_csic_irq(int irq_num, void *data)
+{
+	uint32_t irq;
+	struct csic_device *csic_dev = data;
+
+	pr_info("msm_csic_irq: %x\n", (unsigned int)csic_dev->base);
+	irq = msm_camera_io_r(csic_dev->base + MIPI_INTERRUPT_STATUS);
+	pr_info("%s MIPI_INTERRUPT_STATUS = 0x%x 0x%x\n",
+		__func__, irq,
+		msm_camera_io_r(csic_dev->base + MIPI_PROTOCOL_CONTROL));
+	msm_camera_io_w(irq, csic_dev->base + MIPI_INTERRUPT_STATUS);
+
+	/* TODO: Needs to send this info to upper layers */
+	if ((irq >> 19) & 0x1)
+		pr_info("Unsupported packet format is received\n");
+	return IRQ_HANDLED;
+}
+
+static int msm_csic_subdev_g_chip_ident(struct v4l2_subdev *sd,
+			struct v4l2_dbg_chip_ident *chip)
+{
+	BUG_ON(!chip);
+	chip->ident = V4L2_IDENT_CSIC;
+	chip->revision = 0;
+	return 0;
+}
+
+static struct msm_cam_clk_info csic_8x_clk_info[] = {
+	{"csi_src_clk", 384000000},
+	{"csi_clk", -1},
+	{"csi_vfe_clk", -1},
+	{"csi_pclk", -1},
+};
+
+static struct msm_cam_clk_info csic_7x_clk_info[] = {
+	{"csi_clk", 400000000},
+	{"csi_vfe_clk", -1},
+	{"csi_pclk", -1},
+};
+
+static int msm_csic_init(struct v4l2_subdev *sd, uint32_t *csic_version)
+{
+	int rc = 0;
+	struct csic_device *csic_dev;
+	csic_dev = v4l2_get_subdevdata(sd);
+	if (csic_dev == NULL) {
+		rc = -ENOMEM;
+		return rc;
+	}
+
+	csic_dev->base = ioremap(csic_dev->mem->start,
+		resource_size(csic_dev->mem));
+	if (!csic_dev->base) {
+		rc = -ENOMEM;
+		return rc;
+	}
+
+	csic_dev->hw_version = CSIC_8X;
+	rc = msm_cam_clk_enable(&csic_dev->pdev->dev, csic_8x_clk_info,
+		csic_dev->csic_clk, ARRAY_SIZE(csic_8x_clk_info), 1);
+	if (rc < 0) {
+		csic_dev->hw_version = CSIC_7X;
+		rc = msm_cam_clk_enable(&csic_dev->pdev->dev, csic_7x_clk_info,
+			csic_dev->csic_clk, ARRAY_SIZE(csic_7x_clk_info), 1);
+		if (rc < 0) {
+			csic_dev->hw_version = 0;
+			iounmap(csic_dev->base);
+			csic_dev->base = NULL;
+			return rc;
+		}
+	}
+	if (csic_dev->hw_version == CSIC_7X)
+		msm_camio_vfe_blk_reset_3();
+
+#if DBG_CSIC
+	enable_irq(csic_dev->irq->start);
+#endif
+
+	return 0;
+}
+
+static void msm_csic_disable(struct v4l2_subdev *sd)
+{
+	uint32_t val;
+	struct csic_device *csic_dev;
+	csic_dev = v4l2_get_subdevdata(sd);
+
+	val = 0x0;
+	if (csic_dev->base != NULL) {
+		CDBG("%s MIPI_PHY_D0_CONTROL2 val=0x%x\n", __func__, val);
+		msm_camera_io_w(val, csic_dev->base + MIPI_PHY_D0_CONTROL2);
+		msm_camera_io_w(val, csic_dev->base + MIPI_PHY_D1_CONTROL2);
+		msm_camera_io_w(val, csic_dev->base + MIPI_PHY_D2_CONTROL2);
+		msm_camera_io_w(val, csic_dev->base + MIPI_PHY_D3_CONTROL2);
+		CDBG("%s MIPI_PHY_CL_CONTROL val=0x%x\n", __func__, val);
+		msm_camera_io_w(val, csic_dev->base + MIPI_PHY_CL_CONTROL);
+		msleep(20);
+		val = msm_camera_io_r(csic_dev->base + MIPI_PHY_D1_CONTROL);
+		val &=
+		~((0x1 << MIPI_PHY_D1_CONTROL_MIPI_CLK_PHY_SHUTDOWNB_SHFT)
+		|(0x1 << MIPI_PHY_D1_CONTROL_MIPI_DATA_PHY_SHUTDOWNB_SHFT));
+		CDBG("%s MIPI_PHY_D1_CONTROL val=0x%x\n", __func__, val);
+		msm_camera_io_w(val, csic_dev->base + MIPI_PHY_D1_CONTROL);
+		usleep_range(5000, 6000);
+		msm_camera_io_w(0x0, csic_dev->base + MIPI_INTERRUPT_MASK);
+		msm_camera_io_w(0x0, csic_dev->base + MIPI_INTERRUPT_STATUS);
+		msm_camera_io_w(MIPI_PROTOCOL_CONTROL_SW_RST_BMSK,
+			csic_dev->base + MIPI_PROTOCOL_CONTROL);
+
+		msm_camera_io_w(0xE400, csic_dev->base + MIPI_CAMERA_CNTL);
+	}
+}
+
+static int msm_csic_release(struct v4l2_subdev *sd)
+{
+	struct csic_device *csic_dev;
+	csic_dev = v4l2_get_subdevdata(sd);
+
+	msm_csic_disable(sd);
+#if DBG_CSIC
+	disable_irq(csic_dev->irq->start);
+#endif
+
+	if (csic_dev->hw_version == CSIC_8X) {
+		msm_cam_clk_enable(&csic_dev->pdev->dev, csic_8x_clk_info,
+			csic_dev->csic_clk, ARRAY_SIZE(csic_8x_clk_info), 0);
+	} else if (csic_dev->hw_version == CSIC_7X) {
+		msm_cam_clk_enable(&csic_dev->pdev->dev, csic_7x_clk_info,
+			csic_dev->csic_clk, ARRAY_SIZE(csic_7x_clk_info), 0);
+	}
+
+	iounmap(csic_dev->base);
+	csic_dev->base = NULL;
+	return 0;
+}
+
+static long msm_csic_subdev_ioctl(struct v4l2_subdev *sd,
+			unsigned int cmd, void *arg)
+{
+	struct csic_cfg_params cfg_params;
+	switch (cmd) {
+	case VIDIOC_MSM_CSIC_CFG:
+		cfg_params.subdev = sd;
+		cfg_params.parms = arg;
+		return msm_csic_config((struct csic_cfg_params *)&cfg_params);
+	case VIDIOC_MSM_CSIC_INIT:
+		return msm_csic_init(sd, (uint32_t *)arg);
+	case VIDIOC_MSM_CSIC_RELEASE:
+		return msm_csic_release(sd);
+	default:
+		return -ENOIOCTLCMD;
+	}
+}
+
+static struct v4l2_subdev_core_ops msm_csic_subdev_core_ops = {
+	.g_chip_ident = &msm_csic_subdev_g_chip_ident,
+	.ioctl = &msm_csic_subdev_ioctl,
+};
+
+static const struct v4l2_subdev_ops msm_csic_subdev_ops = {
+	.core = &msm_csic_subdev_core_ops,
+};
+
+static const struct v4l2_subdev_internal_ops msm_csic_internal_ops;
+
+static int __devinit csic_probe(struct platform_device *pdev)
+{
+	struct csic_device *new_csic_dev;
+	int rc = 0;
+	CDBG("%s: device id = %d\n", __func__, pdev->id);
+	new_csic_dev = kzalloc(sizeof(struct csic_device), GFP_KERNEL);
+	if (!new_csic_dev) {
+		pr_err("%s: no enough memory\n", __func__);
+		return -ENOMEM;
+	}
+
+	v4l2_subdev_init(&new_csic_dev->subdev, &msm_csic_subdev_ops);
+	new_csic_dev->subdev.internal_ops = &msm_csic_internal_ops;
+	new_csic_dev->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	snprintf(new_csic_dev->subdev.name,
+			ARRAY_SIZE(new_csic_dev->subdev.name), "msm_csic");
+	v4l2_set_subdevdata(&new_csic_dev->subdev, new_csic_dev);
+	platform_set_drvdata(pdev, &new_csic_dev->subdev);
+	mutex_init(&new_csic_dev->mutex);
+
+	new_csic_dev->mem = platform_get_resource_byname(pdev,
+					IORESOURCE_MEM, "csic");
+	if (!new_csic_dev->mem) {
+		pr_err("%s: no mem resource?\n", __func__);
+		rc = -ENODEV;
+		goto csic_no_resource;
+	}
+	new_csic_dev->irq = platform_get_resource_byname(pdev,
+					IORESOURCE_IRQ, "csic");
+	if (!new_csic_dev->irq) {
+		pr_err("%s: no irq resource?\n", __func__);
+		rc = -ENODEV;
+		goto csic_no_resource;
+	}
+	new_csic_dev->io = request_mem_region(new_csic_dev->mem->start,
+		resource_size(new_csic_dev->mem), pdev->name);
+	if (!new_csic_dev->io) {
+		pr_err("%s: no valid mem region\n", __func__);
+		rc = -EBUSY;
+		goto csic_no_resource;
+	}
+
+	rc = request_irq(new_csic_dev->irq->start, msm_csic_irq,
+		IRQF_TRIGGER_HIGH, "csic", new_csic_dev);
+	if (rc < 0) {
+		release_mem_region(new_csic_dev->mem->start,
+			resource_size(new_csic_dev->mem));
+		pr_err("%s: irq request fail\n", __func__);
+		rc = -EBUSY;
+		goto csic_no_resource;
+	}
+	disable_irq(new_csic_dev->irq->start);
+
+	new_csic_dev->pdev = pdev;
+
+	rc = msm_cam_clk_enable(&new_csic_dev->pdev->dev, &csic_7x_clk_info[2],
+				new_csic_dev->csic_clk, 1, 1);
+	new_csic_dev->base = ioremap(new_csic_dev->mem->start,
+		resource_size(new_csic_dev->mem));
+	if (!new_csic_dev->base) {
+		rc = -ENOMEM;
+		return rc;
+	}
+
+	msm_camera_io_w(MIPI_PWR_CNTL_DIS, new_csic_dev->base + MIPI_PWR_CNTL);
+
+	rc = msm_cam_clk_enable(&new_csic_dev->pdev->dev, &csic_7x_clk_info[2],
+				new_csic_dev->csic_clk, 1, 0);
+
+	iounmap(new_csic_dev->base);
+	new_csic_dev->base = NULL;
+	msm_cam_register_subdev_node(
+		&new_csic_dev->subdev, CSIC_DEV, pdev->id);
+
+	return 0;
+
+csic_no_resource:
+	mutex_destroy(&new_csic_dev->mutex);
+	kfree(new_csic_dev);
+	return 0;
+}
+
+static struct platform_driver csic_driver = {
+	.probe = csic_probe,
+	.driver = {
+		.name = MSM_CSIC_DRV_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init msm_csic_init_module(void)
+{
+	return platform_driver_register(&csic_driver);
+}
+
+static void __exit msm_csic_exit_module(void)
+{
+	platform_driver_unregister(&csic_driver);
+}
+
+module_init(msm_csic_init_module);
+module_exit(msm_csic_exit_module);
+MODULE_DESCRIPTION("MSM csic driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/csi/msm_csic.h b/drivers/media/video/msm/csi/msm_csic.h
new file mode 100644
index 0000000..177e9d1
--- /dev/null
+++ b/drivers/media/video/msm/csi/msm_csic.h
@@ -0,0 +1,51 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MSM_CSIC_H
+#define MSM_CSIC_H
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <media/v4l2-subdev.h>
+
+#define CSIC_7X 0x1
+#define CSIC_8X (0x1 << 1)
+
+struct csic_device {
+	struct platform_device *pdev;
+	struct v4l2_subdev subdev;
+	struct resource *mem;
+	struct resource *irq;
+	struct resource *io;
+	void __iomem *base;
+	struct mutex mutex;
+	uint32_t hw_version;
+
+	struct clk *csic_clk[5];
+};
+
+struct csic_cfg_params {
+	struct v4l2_subdev *subdev;
+	void *parms;
+};
+
+#define VIDIOC_MSM_CSIC_CFG \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 4, struct csic_cfg_params)
+
+#define VIDIOC_MSM_CSIC_INIT \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 5, struct v4l2_subdev*)
+
+#define VIDIOC_MSM_CSIC_RELEASE \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 6, struct v4l2_subdev*)
+
+#endif
+
diff --git a/drivers/media/video/msm/csi/msm_csid.c b/drivers/media/video/msm/csi/msm_csid.c
new file mode 100644
index 0000000..0495b7b
--- /dev/null
+++ b/drivers/media/video/msm/csi/msm_csid.c
@@ -0,0 +1,369 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <mach/board.h>
+#include <mach/camera.h>
+#include <media/msm_isp.h>
+#include "msm_csid.h"
+#include "msm.h"
+
+#define V4L2_IDENT_CSID                            50002
+
+/* MIPI	CSID registers */
+#define CSID_HW_VERSION_ADDR                        0x0
+#define CSID_CORE_CTRL_ADDR                         0x4
+#define CSID_RST_CMD_ADDR                           0x8
+#define CSID_CID_LUT_VC_0_ADDR                      0xc
+#define CSID_CID_LUT_VC_1_ADDR                      0x10
+#define CSID_CID_LUT_VC_2_ADDR                      0x14
+#define CSID_CID_LUT_VC_3_ADDR                      0x18
+#define CSID_CID_n_CFG_ADDR                         0x1C
+#define CSID_IRQ_CLEAR_CMD_ADDR                     0x5c
+#define CSID_IRQ_MASK_ADDR                          0x60
+#define CSID_IRQ_STATUS_ADDR                        0x64
+#define CSID_CAPTURED_UNMAPPED_LONG_PKT_HDR_ADDR    0x68
+#define CSID_CAPTURED_MMAPPED_LONG_PKT_HDR_ADDR     0x6c
+#define CSID_CAPTURED_SHORT_PKT_ADDR                0x70
+#define CSID_CAPTURED_LONG_PKT_HDR_ADDR             0x74
+#define CSID_CAPTURED_LONG_PKT_FTR_ADDR             0x78
+#define CSID_PIF_MISR_DL0_ADDR                      0x7C
+#define CSID_PIF_MISR_DL1_ADDR                      0x80
+#define CSID_PIF_MISR_DL2_ADDR                      0x84
+#define CSID_PIF_MISR_DL3_ADDR                      0x88
+#define CSID_STATS_TOTAL_PKTS_RCVD_ADDR             0x8C
+#define CSID_STATS_ECC_ADDR                         0x90
+#define CSID_STATS_CRC_ADDR                         0x94
+#define CSID_TG_CTRL_ADDR                           0x9C
+#define CSID_TG_VC_CFG_ADDR                         0xA0
+#define CSID_TG_DT_n_CFG_0_ADDR                     0xA8
+#define CSID_TG_DT_n_CFG_1_ADDR                     0xAC
+#define CSID_TG_DT_n_CFG_2_ADDR                     0xB0
+#define CSID_TG_DT_n_CFG_3_ADDR                     0xD8
+#define CSID_RST_DONE_IRQ_BITSHIFT                  11
+#define CSID_RST_STB_ALL                            0x7FFF
+
+#define DBG_CSID 0
+
+static int msm_csid_cid_lut(
+	struct msm_camera_csid_lut_params *csid_lut_params,
+	void __iomem *csidbase)
+{
+	int rc = 0, i = 0;
+	uint32_t val = 0;
+
+	for (i = 0; i < csid_lut_params->num_cid && i < 4; i++) {
+		if (csid_lut_params->vc_cfg[i].dt < 0x12 ||
+			csid_lut_params->vc_cfg[i].dt > 0x37) {
+			CDBG("%s: unsupported data type 0x%x\n",
+				 __func__, csid_lut_params->vc_cfg[i].dt);
+			return rc;
+		}
+		val = msm_camera_io_r(csidbase + CSID_CID_LUT_VC_0_ADDR +
+		(csid_lut_params->vc_cfg[i].cid >> 2) * 4)
+		& ~(0xFF << csid_lut_params->vc_cfg[i].cid * 8);
+		val |= csid_lut_params->vc_cfg[i].dt <<
+			csid_lut_params->vc_cfg[i].cid * 8;
+		msm_camera_io_w(val, csidbase + CSID_CID_LUT_VC_0_ADDR +
+			(csid_lut_params->vc_cfg[i].cid >> 2) * 4);
+		val = csid_lut_params->vc_cfg[i].decode_format << 4 | 0x3;
+		msm_camera_io_w(val, csidbase + CSID_CID_n_CFG_ADDR +
+			(csid_lut_params->vc_cfg[i].cid * 4));
+	}
+	return rc;
+}
+
+#if DBG_CSID
+static void msm_csid_set_debug_reg(void __iomem *csidbase,
+	struct msm_camera_csid_params *csid_params)
+{
+	uint32_t val = 0;
+	val = ((1 << csid_params->lane_cnt) - 1) << 20;
+	msm_camera_io_w(0x7f010800 | val, csidbase + CSID_IRQ_MASK_ADDR);
+	msm_camera_io_w(0x7f010800 | val, csidbase + CSID_IRQ_CLEAR_CMD_ADDR);
+}
+#else
+static void msm_csid_set_debug_reg(void __iomem *csidbase,
+	struct msm_camera_csid_params *csid_params) {}
+#endif
+
+static int msm_csid_config(struct csid_cfg_params *cfg_params)
+{
+	int rc = 0;
+	uint32_t val = 0;
+	struct csid_device *csid_dev;
+	struct msm_camera_csid_params *csid_params;
+	void __iomem *csidbase;
+	csid_dev = v4l2_get_subdevdata(cfg_params->subdev);
+	csidbase = csid_dev->base;
+	csid_params = cfg_params->parms;
+
+	val = csid_params->lane_cnt - 1;
+	val |= csid_params->lane_assign << 2;
+	val |= 0x1 << 10;
+	val |= 0x1 << 11;
+	val |= 0x1 << 12;
+	val |= 0x1 << 13;
+	val |= 0x1 << 28;
+	msm_camera_io_w(val, csidbase + CSID_CORE_CTRL_ADDR);
+
+	rc = msm_csid_cid_lut(&csid_params->lut_params, csidbase);
+	if (rc < 0)
+		return rc;
+
+	msm_csid_set_debug_reg(csidbase, csid_params);
+
+	return rc;
+}
+
+static irqreturn_t msm_csid_irq(int irq_num, void *data)
+{
+	uint32_t irq;
+	struct csid_device *csid_dev = data;
+	irq = msm_camera_io_r(csid_dev->base + CSID_IRQ_STATUS_ADDR);
+	CDBG("%s CSID%d_IRQ_STATUS_ADDR = 0x%x\n",
+		 __func__, csid_dev->pdev->id, irq);
+	if (irq & (0x1 << CSID_RST_DONE_IRQ_BITSHIFT))
+			complete(&csid_dev->reset_complete);
+	msm_camera_io_w(irq, csid_dev->base + CSID_IRQ_CLEAR_CMD_ADDR);
+	return IRQ_HANDLED;
+}
+
+static void msm_csid_reset(struct csid_device *csid_dev)
+{
+	msm_camera_io_w(CSID_RST_STB_ALL, csid_dev->base + CSID_RST_CMD_ADDR);
+	wait_for_completion_interruptible(&csid_dev->reset_complete);
+	return;
+}
+
+static int msm_csid_subdev_g_chip_ident(struct v4l2_subdev *sd,
+			struct v4l2_dbg_chip_ident *chip)
+{
+	BUG_ON(!chip);
+	chip->ident = V4L2_IDENT_CSID;
+	chip->revision = 0;
+	return 0;
+}
+
+static struct msm_cam_clk_info csid_clk_info[] = {
+	{"csi_src_clk", 177780000},
+	{"csi_clk", -1},
+	{"csi_phy_clk", -1},
+	{"csi_pclk", -1},
+};
+
+static struct camera_vreg_t csid_vreg_info[] = {
+	{"mipi_csi_vdd", REG_LDO, 1200000, 1200000, 20000},
+};
+
+static int msm_csid_init(struct v4l2_subdev *sd, uint32_t *csid_version)
+{
+	int rc = 0;
+	struct csid_device *csid_dev;
+	csid_dev = v4l2_get_subdevdata(sd);
+	if (csid_dev == NULL) {
+		rc = -ENOMEM;
+		return rc;
+	}
+
+	csid_dev->base = ioremap(csid_dev->mem->start,
+		resource_size(csid_dev->mem));
+	if (!csid_dev->base) {
+		rc = -ENOMEM;
+		return rc;
+	}
+
+	rc = msm_camera_config_vreg(&csid_dev->pdev->dev, csid_vreg_info,
+		ARRAY_SIZE(csid_vreg_info), &csid_dev->csi_vdd, 1);
+	if (rc < 0) {
+		pr_err("%s: regulator on failed\n", __func__);
+		goto vreg_config_failed;
+	}
+
+	rc = msm_camera_enable_vreg(&csid_dev->pdev->dev, csid_vreg_info,
+		ARRAY_SIZE(csid_vreg_info), &csid_dev->csi_vdd, 1);
+	if (rc < 0) {
+		pr_err("%s: regulator enable failed\n", __func__);
+		goto vreg_enable_failed;
+	}
+
+	rc = msm_cam_clk_enable(&csid_dev->pdev->dev, csid_clk_info,
+		csid_dev->csid_clk, ARRAY_SIZE(csid_clk_info), 1);
+	if (rc < 0) {
+		pr_err("%s: regulator enable failed\n", __func__);
+		goto clk_enable_failed;
+	}
+
+	csid_dev->hw_version =
+		msm_camera_io_r(csid_dev->base + CSID_HW_VERSION_ADDR);
+	*csid_version = csid_dev->hw_version;
+
+	init_completion(&csid_dev->reset_complete);
+
+	rc = request_irq(csid_dev->irq->start, msm_csid_irq,
+		IRQF_TRIGGER_RISING, "csid", csid_dev);
+
+	msm_csid_reset(csid_dev);
+	return rc;
+
+clk_enable_failed:
+	msm_camera_enable_vreg(&csid_dev->pdev->dev, csid_vreg_info,
+		ARRAY_SIZE(csid_vreg_info), &csid_dev->csi_vdd, 0);
+vreg_enable_failed:
+	msm_camera_config_vreg(&csid_dev->pdev->dev, csid_vreg_info,
+		ARRAY_SIZE(csid_vreg_info), &csid_dev->csi_vdd, 0);
+vreg_config_failed:
+	iounmap(csid_dev->base);
+	csid_dev->base = NULL;
+	return rc;
+}
+
+static int msm_csid_release(struct v4l2_subdev *sd)
+{
+	uint32_t irq;
+	struct csid_device *csid_dev;
+	csid_dev = v4l2_get_subdevdata(sd);
+
+	irq = msm_camera_io_r(csid_dev->base + CSID_IRQ_STATUS_ADDR);
+	msm_camera_io_w(irq, csid_dev->base + CSID_IRQ_CLEAR_CMD_ADDR);
+	msm_camera_io_w(0, csid_dev->base + CSID_IRQ_MASK_ADDR);
+
+	free_irq(csid_dev->irq->start, csid_dev);
+
+	msm_cam_clk_enable(&csid_dev->pdev->dev, csid_clk_info,
+		csid_dev->csid_clk, ARRAY_SIZE(csid_clk_info), 0);
+
+	msm_camera_enable_vreg(&csid_dev->pdev->dev, csid_vreg_info,
+		ARRAY_SIZE(csid_vreg_info), &csid_dev->csi_vdd, 0);
+
+	msm_camera_config_vreg(&csid_dev->pdev->dev, csid_vreg_info,
+		ARRAY_SIZE(csid_vreg_info), &csid_dev->csi_vdd, 0);
+
+	iounmap(csid_dev->base);
+	csid_dev->base = NULL;
+	return 0;
+}
+
+static long msm_csid_subdev_ioctl(struct v4l2_subdev *sd,
+			unsigned int cmd, void *arg)
+{
+	int rc = -ENOIOCTLCMD;
+	struct csid_cfg_params cfg_params;
+	struct csid_device *csid_dev = v4l2_get_subdevdata(sd);
+	mutex_lock(&csid_dev->mutex);
+	switch (cmd) {
+	case VIDIOC_MSM_CSID_CFG:
+		cfg_params.subdev = sd;
+		cfg_params.parms = arg;
+		rc = msm_csid_config((struct csid_cfg_params *)&cfg_params);
+		break;
+	case VIDIOC_MSM_CSID_INIT:
+		rc = msm_csid_init(sd, (uint32_t *)arg);
+		break;
+	case VIDIOC_MSM_CSID_RELEASE:
+		rc = msm_csid_release(sd);
+		break;
+	default:
+		pr_err("%s: command not found\n", __func__);
+	}
+	mutex_unlock(&csid_dev->mutex);
+	return rc;
+}
+
+static const struct v4l2_subdev_internal_ops msm_csid_internal_ops;
+
+static struct v4l2_subdev_core_ops msm_csid_subdev_core_ops = {
+	.g_chip_ident = &msm_csid_subdev_g_chip_ident,
+	.ioctl = &msm_csid_subdev_ioctl,
+};
+
+static const struct v4l2_subdev_ops msm_csid_subdev_ops = {
+	.core = &msm_csid_subdev_core_ops,
+};
+
+static int __devinit csid_probe(struct platform_device *pdev)
+{
+	struct csid_device *new_csid_dev;
+	int rc = 0;
+	CDBG("%s: device id = %d\n", __func__, pdev->id);
+	new_csid_dev = kzalloc(sizeof(struct csid_device), GFP_KERNEL);
+	if (!new_csid_dev) {
+		pr_err("%s: no enough memory\n", __func__);
+		return -ENOMEM;
+	}
+
+	v4l2_subdev_init(&new_csid_dev->subdev, &msm_csid_subdev_ops);
+	new_csid_dev->subdev.internal_ops = &msm_csid_internal_ops;
+	new_csid_dev->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	snprintf(new_csid_dev->subdev.name,
+			ARRAY_SIZE(new_csid_dev->subdev.name), "msm_csid");
+	v4l2_set_subdevdata(&new_csid_dev->subdev, new_csid_dev);
+	platform_set_drvdata(pdev, &new_csid_dev->subdev);
+	mutex_init(&new_csid_dev->mutex);
+
+	new_csid_dev->mem = platform_get_resource_byname(pdev,
+					IORESOURCE_MEM, "csid");
+	if (!new_csid_dev->mem) {
+		pr_err("%s: no mem resource?\n", __func__);
+		rc = -ENODEV;
+		goto csid_no_resource;
+	}
+	new_csid_dev->irq = platform_get_resource_byname(pdev,
+					IORESOURCE_IRQ, "csid");
+	if (!new_csid_dev->irq) {
+		pr_err("%s: no irq resource?\n", __func__);
+		rc = -ENODEV;
+		goto csid_no_resource;
+	}
+	new_csid_dev->io = request_mem_region(new_csid_dev->mem->start,
+		resource_size(new_csid_dev->mem), pdev->name);
+	if (!new_csid_dev->io) {
+		pr_err("%s: no valid mem region\n", __func__);
+		rc = -EBUSY;
+		goto csid_no_resource;
+	}
+
+	new_csid_dev->pdev = pdev;
+	msm_cam_register_subdev_node(&new_csid_dev->subdev, CSID_DEV, pdev->id);
+	return 0;
+
+csid_no_resource:
+	mutex_destroy(&new_csid_dev->mutex);
+	kfree(new_csid_dev);
+	return 0;
+}
+
+static struct platform_driver csid_driver = {
+	.probe = csid_probe,
+	.driver = {
+		.name = MSM_CSID_DRV_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init msm_csid_init_module(void)
+{
+	return platform_driver_register(&csid_driver);
+}
+
+static void __exit msm_csid_exit_module(void)
+{
+	platform_driver_unregister(&csid_driver);
+}
+
+module_init(msm_csid_init_module);
+module_exit(msm_csid_exit_module);
+MODULE_DESCRIPTION("MSM CSID driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/csi/msm_csid.h b/drivers/media/video/msm/csi/msm_csid.h
new file mode 100644
index 0000000..2eae49c
--- /dev/null
+++ b/drivers/media/video/msm/csi/msm_csid.h
@@ -0,0 +1,50 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MSM_CSID_H
+#define MSM_CSID_H
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <media/v4l2-subdev.h>
+
+struct csid_device {
+	struct platform_device *pdev;
+	struct v4l2_subdev subdev;
+	struct resource *mem;
+	struct resource *irq;
+	struct resource *io;
+	struct regulator *csi_vdd;
+	void __iomem *base;
+	struct mutex mutex;
+	struct completion reset_complete;
+	uint32_t hw_version;
+
+	struct clk *csid_clk[5];
+};
+
+struct csid_cfg_params {
+	struct v4l2_subdev *subdev;
+	void *parms;
+};
+
+#define VIDIOC_MSM_CSID_CFG \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 4, struct csid_cfg_params)
+
+#define VIDIOC_MSM_CSID_INIT \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 5, struct v4l2_subdev*)
+
+#define VIDIOC_MSM_CSID_RELEASE \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 6, struct v4l2_subdev*)
+
+#endif
+
diff --git a/drivers/media/video/msm/csi/msm_csiphy.c b/drivers/media/video/msm/csi/msm_csiphy.c
new file mode 100644
index 0000000..c384b5a
--- /dev/null
+++ b/drivers/media/video/msm/csi/msm_csiphy.c
@@ -0,0 +1,364 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <mach/board.h>
+#include <mach/camera.h>
+#include <mach/vreg.h>
+#include <media/msm_isp.h>
+#include "msm_csiphy.h"
+#include "msm.h"
+
+#define DBG_CSIPHY 0
+
+#define V4L2_IDENT_CSIPHY                        50003
+
+/*MIPI CSI PHY registers*/
+#define MIPI_CSIPHY_LNn_CFG1_ADDR                0x0
+#define MIPI_CSIPHY_LNn_CFG2_ADDR                0x4
+#define MIPI_CSIPHY_LNn_CFG3_ADDR                0x8
+#define MIPI_CSIPHY_LNn_CFG4_ADDR                0xC
+#define MIPI_CSIPHY_LNn_CFG5_ADDR                0x10
+#define MIPI_CSIPHY_LNCK_CFG1_ADDR               0x100
+#define MIPI_CSIPHY_LNCK_CFG2_ADDR               0x104
+#define MIPI_CSIPHY_LNCK_CFG3_ADDR               0x108
+#define MIPI_CSIPHY_LNCK_CFG4_ADDR               0x10C
+#define MIPI_CSIPHY_LNCK_CFG5_ADDR               0x110
+#define MIPI_CSIPHY_LNCK_MISC1_ADDR              0x128
+#define MIPI_CSIPHY_GLBL_T_INIT_CFG0_ADDR        0x1E0
+#define MIPI_CSIPHY_T_WAKEUP_CFG0_ADDR           0x1E8
+#define MIPI_CSIPHY_T_WAKEUP_CFG1_ADDR           0x1EC
+#define MIPI_CSIPHY_GLBL_RESET_ADDR             0x0140
+#define MIPI_CSIPHY_GLBL_PWR_CFG_ADDR           0x0144
+#define MIPI_CSIPHY_INTERRUPT_STATUS0_ADDR      0x0180
+#define MIPI_CSIPHY_INTERRUPT_STATUS1_ADDR      0x0184
+#define MIPI_CSIPHY_INTERRUPT_STATUS2_ADDR      0x0188
+#define MIPI_CSIPHY_INTERRUPT_STATUS3_ADDR      0x018C
+#define MIPI_CSIPHY_INTERRUPT_STATUS4_ADDR      0x0190
+#define MIPI_CSIPHY_INTERRUPT_MASK0_ADDR        0x01A0
+#define MIPI_CSIPHY_INTERRUPT_MASK1_ADDR        0x01A4
+#define MIPI_CSIPHY_INTERRUPT_MASK2_ADDR        0x01A8
+#define MIPI_CSIPHY_INTERRUPT_MASK3_ADDR        0x01AC
+#define MIPI_CSIPHY_INTERRUPT_MASK4_ADDR        0x01B0
+#define MIPI_CSIPHY_INTERRUPT_CLEAR0_ADDR       0x01C0
+#define MIPI_CSIPHY_INTERRUPT_CLEAR1_ADDR       0x01C4
+#define MIPI_CSIPHY_INTERRUPT_CLEAR2_ADDR       0x01C8
+#define MIPI_CSIPHY_INTERRUPT_CLEAR3_ADDR       0x01CC
+#define MIPI_CSIPHY_INTERRUPT_CLEAR4_ADDR       0x01D0
+
+int msm_csiphy_config(struct csiphy_cfg_params *cfg_params)
+{
+	int rc = 0;
+	int j = 0;
+	uint32_t val = 0;
+	uint8_t lane_cnt = 0, lane_mask = 0;
+	struct csiphy_device *csiphy_dev;
+	struct msm_camera_csiphy_params *csiphy_params;
+	void __iomem *csiphybase;
+	csiphy_dev = v4l2_get_subdevdata(cfg_params->subdev);
+	csiphybase = csiphy_dev->base;
+	csiphy_params = cfg_params->parms;
+	lane_mask = csiphy_params->lane_mask;
+	lane_cnt = csiphy_params->lane_cnt;
+	if (csiphy_params->lane_cnt < 1 || csiphy_params->lane_cnt > 4) {
+		CDBG("%s: unsupported lane cnt %d\n",
+			__func__, csiphy_params->lane_cnt);
+		return rc;
+	}
+
+	val = 0x3;
+	msm_camera_io_w((csiphy_params->lane_mask << 2) | val,
+			csiphybase + MIPI_CSIPHY_GLBL_PWR_CFG_ADDR);
+	msm_camera_io_w(0x1, csiphybase + MIPI_CSIPHY_GLBL_T_INIT_CFG0_ADDR);
+	msm_camera_io_w(0x1, csiphybase + MIPI_CSIPHY_T_WAKEUP_CFG0_ADDR);
+
+	while (lane_mask & 0xf) {
+		if (!(lane_mask & 0x1)) {
+			j++;
+			lane_mask >>= 1;
+			continue;
+		}
+		msm_camera_io_w(0x10,
+			csiphybase + MIPI_CSIPHY_LNn_CFG2_ADDR + 0x40*j);
+		msm_camera_io_w(csiphy_params->settle_cnt,
+			csiphybase + MIPI_CSIPHY_LNn_CFG3_ADDR + 0x40*j);
+		msm_camera_io_w(0x6F,
+			csiphybase + MIPI_CSIPHY_INTERRUPT_MASK0_ADDR +
+				0x4*(j+1));
+		msm_camera_io_w(0x6F,
+			csiphybase + MIPI_CSIPHY_INTERRUPT_CLEAR0_ADDR +
+				0x4*(j+1));
+		j++;
+		lane_mask >>= 1;
+	}
+
+	msm_camera_io_w(0x10, csiphybase + MIPI_CSIPHY_LNCK_CFG2_ADDR);
+	msm_camera_io_w(csiphy_params->settle_cnt,
+			 csiphybase + MIPI_CSIPHY_LNCK_CFG3_ADDR);
+
+	msm_camera_io_w(0x24,
+		csiphybase + MIPI_CSIPHY_INTERRUPT_MASK0_ADDR);
+	msm_camera_io_w(0x24,
+		csiphybase + MIPI_CSIPHY_INTERRUPT_CLEAR0_ADDR);
+	return rc;
+}
+
+static irqreturn_t msm_csiphy_irq(int irq_num, void *data)
+{
+	uint32_t irq;
+	struct csiphy_device *csiphy_dev = data;
+
+	irq = msm_camera_io_r(
+		csiphy_dev->base + MIPI_CSIPHY_INTERRUPT_STATUS0_ADDR);
+	msm_camera_io_w(irq,
+		csiphy_dev->base + MIPI_CSIPHY_INTERRUPT_CLEAR0_ADDR);
+	CDBG("%s MIPI_CSIPHY%d_INTERRUPT_STATUS0 = 0x%x\n",
+		 __func__, csiphy_dev->pdev->id, irq);
+
+	irq = msm_camera_io_r(
+		csiphy_dev->base + MIPI_CSIPHY_INTERRUPT_STATUS1_ADDR);
+	msm_camera_io_w(irq,
+		csiphy_dev->base + MIPI_CSIPHY_INTERRUPT_CLEAR1_ADDR);
+	CDBG("%s MIPI_CSIPHY%d_INTERRUPT_STATUS1 = 0x%x\n",
+		__func__, csiphy_dev->pdev->id, irq);
+
+	irq = msm_camera_io_r(
+		csiphy_dev->base + MIPI_CSIPHY_INTERRUPT_STATUS2_ADDR);
+	msm_camera_io_w(irq,
+		csiphy_dev->base + MIPI_CSIPHY_INTERRUPT_CLEAR2_ADDR);
+	CDBG("%s MIPI_CSIPHY%d_INTERRUPT_STATUS2 = 0x%x\n",
+		__func__, csiphy_dev->pdev->id, irq);
+
+	irq = msm_camera_io_r(
+		csiphy_dev->base + MIPI_CSIPHY_INTERRUPT_STATUS3_ADDR);
+	msm_camera_io_w(irq,
+		csiphy_dev->base + MIPI_CSIPHY_INTERRUPT_CLEAR3_ADDR);
+	CDBG("%s MIPI_CSIPHY%d_INTERRUPT_STATUS3 = 0x%x\n",
+		__func__, csiphy_dev->pdev->id, irq);
+
+	irq = msm_camera_io_r(
+		csiphy_dev->base + MIPI_CSIPHY_INTERRUPT_STATUS4_ADDR);
+	msm_camera_io_w(irq,
+		csiphy_dev->base + MIPI_CSIPHY_INTERRUPT_CLEAR4_ADDR);
+	CDBG("%s MIPI_CSIPHY%d_INTERRUPT_STATUS4 = 0x%x\n",
+		__func__, csiphy_dev->pdev->id, irq);
+	msm_camera_io_w(0x1, csiphy_dev->base + 0x164);
+	msm_camera_io_w(0x0, csiphy_dev->base + 0x164);
+	return IRQ_HANDLED;
+}
+
+static void msm_csiphy_reset(struct csiphy_device *csiphy_dev)
+{
+	msm_camera_io_w(0x1, csiphy_dev->base + MIPI_CSIPHY_GLBL_RESET_ADDR);
+	usleep_range(5000, 8000);
+	msm_camera_io_w(0x0, csiphy_dev->base + MIPI_CSIPHY_GLBL_RESET_ADDR);
+}
+
+static int msm_csiphy_subdev_g_chip_ident(struct v4l2_subdev *sd,
+			struct v4l2_dbg_chip_ident *chip)
+{
+	BUG_ON(!chip);
+	chip->ident = V4L2_IDENT_CSIPHY;
+	chip->revision = 0;
+	return 0;
+}
+
+static struct msm_cam_clk_info csiphy_clk_info[] = {
+	{"csiphy_timer_src_clk", 177780000},
+	{"csiphy_timer_clk", -1},
+};
+
+static int msm_csiphy_init(struct v4l2_subdev *sd)
+{
+	int rc = 0;
+	struct csiphy_device *csiphy_dev;
+	csiphy_dev = v4l2_get_subdevdata(sd);
+	if (csiphy_dev == NULL) {
+		rc = -ENOMEM;
+		return rc;
+	}
+
+	csiphy_dev->base = ioremap(csiphy_dev->mem->start,
+		resource_size(csiphy_dev->mem));
+	if (!csiphy_dev->base) {
+		rc = -ENOMEM;
+		return rc;
+	}
+
+	rc = msm_cam_clk_enable(&csiphy_dev->pdev->dev, csiphy_clk_info,
+			csiphy_dev->csiphy_clk, ARRAY_SIZE(csiphy_clk_info), 1);
+
+	if (rc < 0) {
+		iounmap(csiphy_dev->base);
+		csiphy_dev->base = NULL;
+		return rc;
+	}
+
+#if DBG_CSIPHY
+	enable_irq(csiphy_dev->irq->start);
+#endif
+	msm_csiphy_reset(csiphy_dev);
+
+	return 0;
+}
+
+static int msm_csiphy_release(struct v4l2_subdev *sd)
+{
+	struct csiphy_device *csiphy_dev;
+	int i;
+	csiphy_dev = v4l2_get_subdevdata(sd);
+	for (i = 0; i < 4; i++)
+		msm_camera_io_w(0x0, csiphy_dev->base +
+		MIPI_CSIPHY_LNn_CFG2_ADDR + 0x40*i);
+
+	msm_camera_io_w(0x0, csiphy_dev->base + MIPI_CSIPHY_LNCK_CFG2_ADDR);
+	msm_camera_io_w(0x0, csiphy_dev->base + MIPI_CSIPHY_GLBL_PWR_CFG_ADDR);
+
+#if DBG_CSIPHY
+	disable_irq(csiphy_dev->irq->start);
+#endif
+	msm_cam_clk_enable(&csiphy_dev->pdev->dev, csiphy_clk_info,
+		csiphy_dev->csiphy_clk, ARRAY_SIZE(csiphy_clk_info), 0);
+
+	iounmap(csiphy_dev->base);
+	csiphy_dev->base = NULL;
+	return 0;
+}
+
+static long msm_csiphy_subdev_ioctl(struct v4l2_subdev *sd,
+			unsigned int cmd, void *arg)
+{
+	int rc = -ENOIOCTLCMD;
+	struct csiphy_cfg_params cfg_params;
+	struct csiphy_device *csiphy_dev = v4l2_get_subdevdata(sd);
+	mutex_lock(&csiphy_dev->mutex);
+	switch (cmd) {
+	case VIDIOC_MSM_CSIPHY_CFG:
+		cfg_params.subdev = sd;
+		cfg_params.parms = arg;
+		rc = msm_csiphy_config(
+			(struct csiphy_cfg_params *)&cfg_params);
+		break;
+	case VIDIOC_MSM_CSIPHY_INIT:
+		rc = msm_csiphy_init(sd);
+		break;
+	case VIDIOC_MSM_CSIPHY_RELEASE:
+		rc = msm_csiphy_release(sd);
+		break;
+	default:
+		pr_err("%s: command not found\n", __func__);
+	}
+	mutex_unlock(&csiphy_dev->mutex);
+	return rc;
+}
+
+static const struct v4l2_subdev_internal_ops msm_csiphy_internal_ops;
+
+static struct v4l2_subdev_core_ops msm_csiphy_subdev_core_ops = {
+	.g_chip_ident = &msm_csiphy_subdev_g_chip_ident,
+	.ioctl = &msm_csiphy_subdev_ioctl,
+};
+
+static const struct v4l2_subdev_ops msm_csiphy_subdev_ops = {
+	.core = &msm_csiphy_subdev_core_ops,
+};
+
+static int __devinit csiphy_probe(struct platform_device *pdev)
+{
+	struct csiphy_device *new_csiphy_dev;
+	int rc = 0;
+	CDBG("%s: device id = %d\n", __func__, pdev->id);
+	new_csiphy_dev = kzalloc(sizeof(struct csiphy_device), GFP_KERNEL);
+	if (!new_csiphy_dev) {
+		pr_err("%s: no enough memory\n", __func__);
+		return -ENOMEM;
+	}
+
+	v4l2_subdev_init(&new_csiphy_dev->subdev, &msm_csiphy_subdev_ops);
+	new_csiphy_dev->subdev.internal_ops = &msm_csiphy_internal_ops;
+	new_csiphy_dev->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	snprintf(new_csiphy_dev->subdev.name,
+			ARRAY_SIZE(new_csiphy_dev->subdev.name), "msm_csiphy");
+	v4l2_set_subdevdata(&new_csiphy_dev->subdev, new_csiphy_dev);
+	platform_set_drvdata(pdev, &new_csiphy_dev->subdev);
+
+	mutex_init(&new_csiphy_dev->mutex);
+
+	new_csiphy_dev->mem = platform_get_resource_byname(pdev,
+					IORESOURCE_MEM, "csiphy");
+	if (!new_csiphy_dev->mem) {
+		pr_err("%s: no mem resource?\n", __func__);
+		rc = -ENODEV;
+		goto csiphy_no_resource;
+	}
+	new_csiphy_dev->irq = platform_get_resource_byname(pdev,
+					IORESOURCE_IRQ, "csiphy");
+	if (!new_csiphy_dev->irq) {
+		pr_err("%s: no irq resource?\n", __func__);
+		rc = -ENODEV;
+		goto csiphy_no_resource;
+	}
+	new_csiphy_dev->io = request_mem_region(new_csiphy_dev->mem->start,
+		resource_size(new_csiphy_dev->mem), pdev->name);
+	if (!new_csiphy_dev->io) {
+		pr_err("%s: no valid mem region\n", __func__);
+		rc = -EBUSY;
+		goto csiphy_no_resource;
+	}
+
+	rc = request_irq(new_csiphy_dev->irq->start, msm_csiphy_irq,
+		IRQF_TRIGGER_RISING, "csiphy", new_csiphy_dev);
+	if (rc < 0) {
+		release_mem_region(new_csiphy_dev->mem->start,
+			resource_size(new_csiphy_dev->mem));
+		pr_err("%s: irq request fail\n", __func__);
+		rc = -EBUSY;
+		goto csiphy_no_resource;
+	}
+	disable_irq(new_csiphy_dev->irq->start);
+
+	new_csiphy_dev->pdev = pdev;
+	msm_cam_register_subdev_node(
+		&new_csiphy_dev->subdev, CSIPHY_DEV, pdev->id);
+	return 0;
+
+csiphy_no_resource:
+	mutex_destroy(&new_csiphy_dev->mutex);
+	kfree(new_csiphy_dev);
+	return 0;
+}
+
+static struct platform_driver csiphy_driver = {
+	.probe = csiphy_probe,
+	.driver = {
+		.name = MSM_CSIPHY_DRV_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init msm_csiphy_init_module(void)
+{
+	return platform_driver_register(&csiphy_driver);
+}
+
+static void __exit msm_csiphy_exit_module(void)
+{
+	platform_driver_unregister(&csiphy_driver);
+}
+
+module_init(msm_csiphy_init_module);
+module_exit(msm_csiphy_exit_module);
+MODULE_DESCRIPTION("MSM CSIPHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/csi/msm_csiphy.h b/drivers/media/video/msm/csi/msm_csiphy.h
new file mode 100644
index 0000000..522a1c1
--- /dev/null
+++ b/drivers/media/video/msm/csi/msm_csiphy.h
@@ -0,0 +1,46 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MSM_CSIPHY_H
+#define MSM_CSIPHY_H
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <media/v4l2-subdev.h>
+
+struct csiphy_device {
+	struct platform_device *pdev;
+	struct v4l2_subdev subdev;
+	struct resource *mem;
+	struct resource *irq;
+	struct resource *io;
+	void __iomem *base;
+	struct mutex mutex;
+
+	struct clk *csiphy_clk[2];
+};
+
+struct csiphy_cfg_params {
+	struct v4l2_subdev *subdev;
+	void *parms;
+};
+
+#define VIDIOC_MSM_CSIPHY_CFG \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 7, struct csiphy_cfg_params)
+
+#define VIDIOC_MSM_CSIPHY_INIT \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 8, struct v4l2_subdev*)
+
+#define VIDIOC_MSM_CSIPHY_RELEASE \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 9, struct v4l2_subdev*)
+
+#endif
diff --git a/drivers/media/video/msm/csi/msm_ispif.c b/drivers/media/video/msm/csi/msm_ispif.c
new file mode 100644
index 0000000..23982dd
--- /dev/null
+++ b/drivers/media/video/msm/csi/msm_ispif.c
@@ -0,0 +1,688 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <mach/gpio.h>
+#include <mach/camera.h>
+#include "msm_ispif.h"
+#include "msm.h"
+
+#define V4L2_IDENT_ISPIF			50001
+#define CSID_VERSION_V2                      0x2000011
+
+/* ISPIF registers */
+
+#define ISPIF_RST_CMD_ADDR                        0X00
+#define ISPIF_INTF_CMD_ADDR                       0X04
+#define ISPIF_CTRL_ADDR                           0X08
+#define ISPIF_INPUT_SEL_ADDR                      0X0C
+#define ISPIF_PIX_INTF_CID_MASK_ADDR              0X10
+#define ISPIF_RDI_INTF_CID_MASK_ADDR              0X14
+#define ISPIF_PIX_1_INTF_CID_MASK_ADDR            0X38
+#define ISPIF_RDI_1_INTF_CID_MASK_ADDR            0X3C
+#define ISPIF_PIX_STATUS_ADDR                     0X24
+#define ISPIF_RDI_STATUS_ADDR                     0X28
+#define ISPIF_RDI_1_STATUS_ADDR                   0X64
+#define ISPIF_IRQ_MASK_ADDR                     0X0100
+#define ISPIF_IRQ_CLEAR_ADDR                    0X0104
+#define ISPIF_IRQ_STATUS_ADDR                   0X0108
+#define ISPIF_IRQ_MASK_1_ADDR                   0X010C
+#define ISPIF_IRQ_CLEAR_1_ADDR                  0X0110
+#define ISPIF_IRQ_STATUS_1_ADDR                 0X0114
+#define ISPIF_IRQ_GLOBAL_CLEAR_CMD_ADDR         0x0124
+
+/*ISPIF RESET BITS*/
+
+#define VFE_CLK_DOMAIN_RST           31
+#define RDI_CLK_DOMAIN_RST           30
+#define PIX_CLK_DOMAIN_RST           29
+#define AHB_CLK_DOMAIN_RST           28
+#define RDI_1_CLK_DOMAIN_RST         27
+#define RDI_1_VFE_RST_STB            13
+#define RDI_1_CSID_RST_STB           12
+#define RDI_VFE_RST_STB              7
+#define RDI_CSID_RST_STB             6
+#define PIX_VFE_RST_STB              4
+#define PIX_CSID_RST_STB             3
+#define SW_REG_RST_STB               2
+#define MISC_LOGIC_RST_STB           1
+#define STROBED_RST_EN               0
+
+#define PIX_INTF_0_OVERFLOW_IRQ      12
+#define RAW_INTF_0_OVERFLOW_IRQ      25
+#define RAW_INTF_1_OVERFLOW_IRQ      25
+#define RESET_DONE_IRQ               27
+
+#define ISPIF_IRQ_STATUS_MASK        0xA493000
+#define ISPIF_IRQ_1_STATUS_MASK      0xA493000
+#define ISPIF_IRQ_STATUS_RDI_SOF_MASK	0x492000
+#define ISPIF_IRQ_GLOBAL_CLEAR_CMD     0x1
+
+#define MAX_CID 15
+
+
+static struct ispif_device *ispif;
+atomic_t ispif_irq_cnt;
+spinlock_t  ispif_tasklet_lock;
+struct list_head ispif_tasklet_q;
+
+static uint32_t global_intf_cmd_mask = 0xFFFFFFFF;
+
+
+static int msm_ispif_intf_reset(uint8_t intfmask)
+{
+	int rc = 0;
+	uint32_t data = 0x1;
+	uint8_t intfnum = 0, mask = intfmask;
+	while (mask != 0) {
+		if (!(intfmask & (0x1 << intfnum))) {
+			mask >>= 1;
+			intfnum++;
+			continue;
+		}
+		switch (intfnum) {
+		case PIX0:
+			data = (0x1 << STROBED_RST_EN) +
+				(0x1 << PIX_VFE_RST_STB) +
+				(0x1 << PIX_CSID_RST_STB);
+			break;
+
+		case RDI0:
+			data = (0x1 << STROBED_RST_EN) +
+				(0x1 << RDI_VFE_RST_STB)  +
+				(0x1 << RDI_CSID_RST_STB);
+			break;
+
+		case RDI1:
+			data = (0x1 << STROBED_RST_EN) +
+				(0x1 << RDI_1_VFE_RST_STB) +
+				(0x1 << RDI_1_CSID_RST_STB);
+			break;
+
+		default:
+			rc = -EINVAL;
+			break;
+		}
+		mask >>= 1;
+		intfnum++;
+	}	/*end while */
+	if (rc >= 0) {
+		msm_camera_io_w(data, ispif->base + ISPIF_RST_CMD_ADDR);
+		rc = wait_for_completion_interruptible(&ispif->reset_complete);
+	}
+
+	return rc;
+}
+
+static int msm_ispif_reset(void)
+{
+	uint32_t data = (0x1 << STROBED_RST_EN) +
+		(0x1 << SW_REG_RST_STB) +
+		(0x1 << MISC_LOGIC_RST_STB) +
+		(0x1 << PIX_VFE_RST_STB) +
+		(0x1 << PIX_CSID_RST_STB) +
+		(0x1 << RDI_VFE_RST_STB) +
+		(0x1 << RDI_CSID_RST_STB) +
+		(0x1 << RDI_1_VFE_RST_STB) +
+		(0x1 << RDI_1_CSID_RST_STB);
+	msm_camera_io_w(data, ispif->base + ISPIF_RST_CMD_ADDR);
+	return wait_for_completion_interruptible(&ispif->reset_complete);
+}
+
+static int msm_ispif_subdev_g_chip_ident(struct v4l2_subdev *sd,
+			struct v4l2_dbg_chip_ident *chip)
+{
+	BUG_ON(!chip);
+	chip->ident = V4L2_IDENT_ISPIF;
+	chip->revision = 0;
+	return 0;
+}
+
+static void msm_ispif_sel_csid_core(uint8_t intftype, uint8_t csid)
+{
+	int rc = 0;
+	uint32_t data;
+	if (ispif->ispif_clk[intftype] == NULL) {
+		pr_err("%s: ispif NULL clk\n", __func__);
+		return;
+	}
+
+	rc = clk_set_rate(ispif->ispif_clk[intftype], csid);
+	if (rc < 0)
+		pr_err("%s: clk_set_rate failed %d\n", __func__, rc);
+
+	data = msm_camera_io_r(ispif->base + ISPIF_INPUT_SEL_ADDR);
+	data |= csid<<(intftype*4);
+	msm_camera_io_w(data, ispif->base + ISPIF_INPUT_SEL_ADDR);
+}
+
+static void msm_ispif_enable_intf_cids(uint8_t intftype, uint16_t cid_mask)
+{
+	uint32_t data;
+	mutex_lock(&ispif->mutex);
+	switch (intftype) {
+	case PIX0:
+		data = msm_camera_io_r(ispif->base +
+				ISPIF_PIX_INTF_CID_MASK_ADDR);
+		data |= cid_mask;
+		msm_camera_io_w(data, ispif->base +
+				ISPIF_PIX_INTF_CID_MASK_ADDR);
+		break;
+
+	case RDI0:
+		data = msm_camera_io_r(ispif->base +
+				ISPIF_RDI_INTF_CID_MASK_ADDR);
+		data |= cid_mask;
+		msm_camera_io_w(data, ispif->base +
+				ISPIF_RDI_INTF_CID_MASK_ADDR);
+		break;
+
+	case RDI1:
+		data = msm_camera_io_r(ispif->base +
+				ISPIF_RDI_1_INTF_CID_MASK_ADDR);
+		data |= cid_mask;
+		msm_camera_io_w(data, ispif->base +
+				ISPIF_RDI_1_INTF_CID_MASK_ADDR);
+		break;
+	}
+	mutex_unlock(&ispif->mutex);
+}
+
+static int msm_ispif_config(struct msm_ispif_params_list *params_list)
+{
+	uint32_t params_len;
+	struct msm_ispif_params *ispif_params;
+	uint32_t data, data1;
+	int rc = 0, i = 0;
+	params_len = params_list->len;
+	ispif_params = params_list->params;
+	CDBG("Enable interface\n");
+	data = msm_camera_io_r(ispif->base + ISPIF_PIX_STATUS_ADDR);
+	data1 = msm_camera_io_r(ispif->base + ISPIF_RDI_STATUS_ADDR);
+	if (((data & 0xf) != 0xf) || ((data1 & 0xf) != 0xf))
+		return -EBUSY;
+	msm_camera_io_w(0x00000000, ispif->base + ISPIF_IRQ_MASK_ADDR);
+	for (i = 0; i < params_len; i++) {
+		msm_ispif_sel_csid_core(ispif_params[i].intftype,
+			ispif_params[i].csid);
+		msm_ispif_enable_intf_cids(ispif_params[i].intftype,
+			ispif_params[i].cid_mask);
+	}
+
+	msm_camera_io_w(ISPIF_IRQ_STATUS_MASK, ispif->base +
+					ISPIF_IRQ_MASK_ADDR);
+	msm_camera_io_w(ISPIF_IRQ_STATUS_MASK, ispif->base +
+					ISPIF_IRQ_CLEAR_ADDR);
+	msm_camera_io_w(ISPIF_IRQ_GLOBAL_CLEAR_CMD, ispif->base +
+		 ISPIF_IRQ_GLOBAL_CLEAR_CMD_ADDR);
+	return rc;
+}
+
+static uint32_t msm_ispif_get_cid_mask(uint8_t intftype)
+{
+	uint32_t mask = 0;
+	switch (intftype) {
+	case PIX0:
+		mask = msm_camera_io_r(ispif->base +
+			ISPIF_PIX_INTF_CID_MASK_ADDR);
+		break;
+
+	case RDI0:
+		mask = msm_camera_io_r(ispif->base +
+			ISPIF_RDI_INTF_CID_MASK_ADDR);
+		break;
+
+	case RDI1:
+		mask = msm_camera_io_r(ispif->base +
+			ISPIF_RDI_1_INTF_CID_MASK_ADDR);
+		break;
+
+	default:
+		break;
+	}
+	return mask;
+}
+
+static void
+msm_ispif_intf_cmd(uint8_t intfmask, uint8_t intf_cmd_mask)
+{
+	uint8_t vc = 0, val = 0;
+	uint8_t mask = intfmask, intfnum = 0;
+	uint32_t cid_mask = 0;
+	while (mask != 0) {
+		if (!(intfmask & (0x1 << intfnum))) {
+			mask >>= 1;
+			intfnum++;
+			continue;
+		}
+
+		cid_mask = msm_ispif_get_cid_mask(intfnum);
+		vc = 0;
+
+		while (cid_mask != 0) {
+			if ((cid_mask & 0xf) != 0x0) {
+				val = (intf_cmd_mask>>(vc*2)) & 0x3;
+				global_intf_cmd_mask |=
+					(0x3 << ((vc * 2) + (intfnum * 8)));
+				global_intf_cmd_mask &= ~((0x3 & ~val)
+					<< ((vc * 2) +
+					(intfnum * 8)));
+			}
+			vc++;
+			cid_mask >>= 4;
+		}
+		mask >>= 1;
+		intfnum++;
+	}
+	msm_camera_io_w(global_intf_cmd_mask,
+		ispif->base + ISPIF_INTF_CMD_ADDR);
+}
+
+static int msm_ispif_abort_intf_transfer(uint8_t intfmask)
+{
+	int rc = 0;
+	uint8_t intf_cmd_mask = 0xAA;
+	uint8_t intfnum = 0, mask = intfmask;
+	mutex_lock(&ispif->mutex);
+	msm_ispif_intf_cmd(intfmask, intf_cmd_mask);
+	while (mask != 0) {
+		if (intfmask & (0x1 << intfnum))
+			global_intf_cmd_mask |= (0xFF << (intfnum * 8));
+		mask >>= 1;
+		intfnum++;
+	}
+	mutex_unlock(&ispif->mutex);
+	return rc;
+}
+
+static int msm_ispif_start_intf_transfer(uint8_t intfmask)
+{
+	uint8_t intf_cmd_mask = 0x55;
+	int rc = 0;
+	mutex_lock(&ispif->mutex);
+	rc = msm_ispif_intf_reset(intfmask);
+	msm_ispif_intf_cmd(intfmask, intf_cmd_mask);
+	mutex_unlock(&ispif->mutex);
+	return rc;
+}
+
+static int msm_ispif_stop_intf_transfer(uint8_t intfmask)
+{
+	int rc = 0;
+	uint8_t intf_cmd_mask = 0x00;
+	uint8_t intfnum = 0, mask = intfmask;
+	mutex_lock(&ispif->mutex);
+	msm_ispif_intf_cmd(intfmask, intf_cmd_mask);
+	while (mask != 0) {
+		if (intfmask & (0x1 << intfnum)) {
+			switch (intfnum) {
+			case PIX0:
+				while ((msm_camera_io_r(ispif->base +
+					ISPIF_PIX_STATUS_ADDR)
+					& 0xf) != 0xf) {
+					CDBG("Wait for pix0 Idle\n");
+				}
+				break;
+
+			case RDI0:
+				while ((msm_camera_io_r(ispif->base +
+					ISPIF_RDI_STATUS_ADDR)
+					& 0xf) != 0xf) {
+					CDBG("Wait for rdi0 Idle\n");
+				}
+				break;
+
+			case RDI1:
+				while ((msm_camera_io_r(ispif->base +
+					ISPIF_RDI_1_STATUS_ADDR)
+					& 0xf) != 0xf) {
+					CDBG("Wait for rdi1 Idle\n");
+				}
+				break;
+
+			default:
+				break;
+			}
+			global_intf_cmd_mask |= (0xFF << (intfnum * 8));
+		}
+		mask >>= 1;
+		intfnum++;
+	}
+	mutex_unlock(&ispif->mutex);
+	return rc;
+}
+
+static int msm_ispif_subdev_video_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct ispif_device *ispif =
+			(struct ispif_device *)v4l2_get_subdevdata(sd);
+	int32_t cmd = enable & ((1<<ISPIF_S_STREAM_SHIFT)-1);
+	enum msm_ispif_intftype intf = enable >> ISPIF_S_STREAM_SHIFT;
+	int rc = -EINVAL;
+
+	BUG_ON(!ispif);
+	switch (cmd) {
+	case ISPIF_ON_FRAME_BOUNDARY:
+		rc = msm_ispif_start_intf_transfer(intf);
+		break;
+	case ISPIF_OFF_FRAME_BOUNDARY:
+		rc = msm_ispif_stop_intf_transfer(intf);
+		break;
+	case ISPIF_OFF_IMMEDIATELY:
+		rc = msm_ispif_abort_intf_transfer(intf);
+		break;
+	default:
+		break;
+	}
+	return rc;
+}
+
+static void ispif_do_tasklet(unsigned long data)
+{
+	unsigned long flags;
+
+	struct ispif_isr_queue_cmd *qcmd = NULL;
+	CDBG("=== ispif_do_tasklet start ===\n");
+
+	while (atomic_read(&ispif_irq_cnt)) {
+		spin_lock_irqsave(&ispif_tasklet_lock, flags);
+		qcmd = list_first_entry(&ispif_tasklet_q,
+			struct ispif_isr_queue_cmd, list);
+		atomic_sub(1, &ispif_irq_cnt);
+
+		if (!qcmd) {
+			spin_unlock_irqrestore(&ispif_tasklet_lock,
+				flags);
+			return;
+		}
+		list_del(&qcmd->list);
+		spin_unlock_irqrestore(&ispif_tasklet_lock,
+			flags);
+		if (qcmd->ispifInterruptStatus0 &
+			ISPIF_IRQ_STATUS_RDI_SOF_MASK) {
+			CDBG("ispif rdi irq status\n");
+		}
+		if (qcmd->ispifInterruptStatus1 &
+			ISPIF_IRQ_STATUS_RDI_SOF_MASK) {
+			CDBG("ispif rdi1 irq status\n");
+		}
+		kfree(qcmd);
+	}
+	CDBG("=== ispif_do_tasklet end ===\n");
+}
+
+DECLARE_TASKLET(ispif_tasklet, ispif_do_tasklet, 0);
+
+static void ispif_process_irq(struct ispif_irq_status *out)
+{
+	unsigned long flags;
+	struct ispif_isr_queue_cmd *qcmd;
+
+	CDBG("ispif_process_irq\n");
+	qcmd = kzalloc(sizeof(struct ispif_isr_queue_cmd),
+		GFP_ATOMIC);
+	if (!qcmd) {
+		pr_err("ispif_process_irq: qcmd malloc failed!\n");
+		return;
+	}
+	qcmd->ispifInterruptStatus0 = out->ispifIrqStatus0;
+	qcmd->ispifInterruptStatus1 = out->ispifIrqStatus1;
+
+	spin_lock_irqsave(&ispif_tasklet_lock, flags);
+	list_add_tail(&qcmd->list, &ispif_tasklet_q);
+
+	atomic_add(1, &ispif_irq_cnt);
+	spin_unlock_irqrestore(&ispif_tasklet_lock, flags);
+	tasklet_schedule(&ispif_tasklet);
+	return;
+}
+
+static inline void msm_ispif_read_irq_status(struct ispif_irq_status *out)
+{
+	out->ispifIrqStatus0 = msm_camera_io_r(ispif->base +
+		ISPIF_IRQ_STATUS_ADDR);
+	out->ispifIrqStatus1 = msm_camera_io_r(ispif->base +
+		ISPIF_IRQ_STATUS_1_ADDR);
+	msm_camera_io_w(out->ispifIrqStatus0,
+		ispif->base + ISPIF_IRQ_CLEAR_ADDR);
+	msm_camera_io_w(out->ispifIrqStatus1,
+		ispif->base + ISPIF_IRQ_CLEAR_1_ADDR);
+
+	CDBG("ispif->irq: Irq_status0 = 0x%x\n",
+		out->ispifIrqStatus0);
+	if (out->ispifIrqStatus0 & ISPIF_IRQ_STATUS_MASK) {
+		if (out->ispifIrqStatus0 & (0x1 << RESET_DONE_IRQ))
+			complete(&ispif->reset_complete);
+		if (out->ispifIrqStatus0 & (0x1 << PIX_INTF_0_OVERFLOW_IRQ))
+			pr_err("%s: pix intf 0 overflow.\n", __func__);
+		if (out->ispifIrqStatus0 & (0x1 << RAW_INTF_0_OVERFLOW_IRQ))
+			pr_err("%s: rdi intf 0 overflow.\n", __func__);
+		if ((out->ispifIrqStatus0 & ISPIF_IRQ_STATUS_RDI_SOF_MASK) ||
+			(out->ispifIrqStatus1 &
+				ISPIF_IRQ_STATUS_RDI_SOF_MASK)) {
+			ispif_process_irq(out);
+		}
+	}
+	msm_camera_io_w(ISPIF_IRQ_GLOBAL_CLEAR_CMD, ispif->base +
+		ISPIF_IRQ_GLOBAL_CLEAR_CMD_ADDR);
+}
+
+static irqreturn_t msm_io_ispif_irq(int irq_num, void *data)
+{
+	struct ispif_irq_status irq;
+	msm_ispif_read_irq_status(&irq);
+	return IRQ_HANDLED;
+}
+
+static struct msm_cam_clk_info ispif_clk_info[] = {
+	{"csi_pix_clk", 0},
+	{"csi_rdi_clk", 0},
+	{"csi_pix1_clk", 0},
+	{"csi_rdi1_clk", 0},
+	{"csi_rdi2_clk", 0},
+};
+
+static int msm_ispif_init(const uint32_t *csid_version)
+{
+	int rc = 0;
+	spin_lock_init(&ispif_tasklet_lock);
+	INIT_LIST_HEAD(&ispif_tasklet_q);
+	rc = request_irq(ispif->irq->start, msm_io_ispif_irq,
+		IRQF_TRIGGER_RISING, "ispif", 0);
+
+	global_intf_cmd_mask = 0xFFFFFFFF;
+	init_completion(&ispif->reset_complete);
+
+	ispif->csid_version = *csid_version;
+	if (ispif->csid_version == CSID_VERSION_V2) {
+		rc = msm_cam_clk_enable(&ispif->pdev->dev, ispif_clk_info,
+			ispif->ispif_clk, ARRAY_SIZE(ispif_clk_info), 1);
+		if (rc < 0)
+			return rc;
+	} else {
+		rc = msm_cam_clk_enable(&ispif->pdev->dev, ispif_clk_info,
+			ispif->ispif_clk, 2, 1);
+		if (rc < 0)
+			return rc;
+	}
+
+	rc = msm_ispif_reset();
+	return rc;
+}
+
+static void msm_ispif_release(struct v4l2_subdev *sd)
+{
+	struct ispif_device *ispif =
+			(struct ispif_device *)v4l2_get_subdevdata(sd);
+
+	CDBG("%s, free_irq\n", __func__);
+	free_irq(ispif->irq->start, 0);
+	tasklet_kill(&ispif_tasklet);
+
+	if (ispif->csid_version == CSID_VERSION_V2)
+		msm_cam_clk_enable(&ispif->pdev->dev, ispif_clk_info,
+			ispif->ispif_clk, ARRAY_SIZE(ispif_clk_info), 0);
+	else
+		msm_cam_clk_enable(&ispif->pdev->dev, ispif_clk_info,
+			ispif->ispif_clk, 2, 0);
+}
+
+void msm_ispif_vfe_get_cid(uint8_t intftype, char *cids, int *num)
+{
+	uint32_t data = 0;
+	int i = 0, j = 0;
+	switch (intftype) {
+	case PIX0:
+		data = msm_camera_io_r(ispif->base +
+			ISPIF_PIX_INTF_CID_MASK_ADDR);
+		break;
+
+	case RDI0:
+		data = msm_camera_io_r(ispif->base +
+			ISPIF_RDI_INTF_CID_MASK_ADDR);
+		break;
+
+	case RDI1:
+		data = msm_camera_io_r(ispif->base +
+			ISPIF_RDI_1_INTF_CID_MASK_ADDR);
+		break;
+
+	default:
+		break;
+	}
+	for (i = 0; i <= MAX_CID; i++) {
+		if ((data & 0x1) == 0x1) {
+			cids[j++] = i;
+			(*num)++;
+		}
+		data >>= 1;
+	}
+}
+
+static long msm_ispif_subdev_ioctl(struct v4l2_subdev *sd, unsigned int cmd,
+								void *arg)
+{
+	switch (cmd) {
+	case VIDIOC_MSM_ISPIF_CFG:
+		return msm_ispif_config((struct msm_ispif_params_list *)arg);
+	case VIDIOC_MSM_ISPIF_INIT:
+		return msm_ispif_init((uint32_t *)arg);
+	case VIDIOC_MSM_ISPIF_RELEASE:
+		msm_ispif_release(sd);
+	default:
+		return -ENOIOCTLCMD;
+	}
+}
+
+static struct v4l2_subdev_core_ops msm_ispif_subdev_core_ops = {
+	.g_chip_ident = &msm_ispif_subdev_g_chip_ident,
+	.ioctl = &msm_ispif_subdev_ioctl,
+};
+
+static struct v4l2_subdev_video_ops msm_ispif_subdev_video_ops = {
+	.s_stream = &msm_ispif_subdev_video_s_stream,
+};
+
+static const struct v4l2_subdev_ops msm_ispif_subdev_ops = {
+	.core = &msm_ispif_subdev_core_ops,
+	.video = &msm_ispif_subdev_video_ops,
+};
+
+static const struct v4l2_subdev_internal_ops msm_ispif_internal_ops;
+
+static int __devinit ispif_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	CDBG("%s\n", __func__);
+	ispif = kzalloc(sizeof(struct ispif_device), GFP_KERNEL);
+	if (!ispif) {
+		pr_err("%s: no enough memory\n", __func__);
+		return -ENOMEM;
+	}
+
+	v4l2_subdev_init(&ispif->subdev, &msm_ispif_subdev_ops);
+	ispif->subdev.internal_ops = &msm_ispif_internal_ops;
+	ispif->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	snprintf(ispif->subdev.name,
+			ARRAY_SIZE(ispif->subdev.name), "msm_ispif");
+	v4l2_set_subdevdata(&ispif->subdev, ispif);
+	platform_set_drvdata(pdev, &ispif->subdev);
+	snprintf(ispif->subdev.name, sizeof(ispif->subdev.name),
+								"ispif");
+	mutex_init(&ispif->mutex);
+
+	ispif->mem = platform_get_resource_byname(pdev,
+					IORESOURCE_MEM, "ispif");
+	if (!ispif->mem) {
+		pr_err("%s: no mem resource?\n", __func__);
+		rc = -ENODEV;
+		goto ispif_no_resource;
+	}
+	ispif->irq = platform_get_resource_byname(pdev,
+					IORESOURCE_IRQ, "ispif");
+	if (!ispif->irq) {
+		pr_err("%s: no irq resource?\n", __func__);
+		rc = -ENODEV;
+		goto ispif_no_resource;
+	}
+	ispif->io = request_mem_region(ispif->mem->start,
+		resource_size(ispif->mem), pdev->name);
+	if (!ispif->io) {
+		pr_err("%s: no valid mem region\n", __func__);
+		rc = -EBUSY;
+		goto ispif_no_resource;
+	}
+	ispif->base = ioremap(ispif->mem->start,
+		resource_size(ispif->mem));
+	if (!ispif->base) {
+		rc = -ENOMEM;
+		goto ispif_no_mem;
+	}
+
+	ispif->pdev = pdev;
+	msm_cam_register_subdev_node(&ispif->subdev, ISPIF_DEV, pdev->id);
+	return 0;
+
+ispif_no_mem:
+	release_mem_region(ispif->mem->start,
+		resource_size(ispif->mem));
+ispif_no_resource:
+	mutex_destroy(&ispif->mutex);
+	kfree(ispif);
+	return rc;
+}
+
+static struct platform_driver ispif_driver = {
+	.probe = ispif_probe,
+	.driver = {
+		.name = MSM_ISPIF_DRV_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init msm_ispif_init_module(void)
+{
+	return platform_driver_register(&ispif_driver);
+}
+
+static void __exit msm_ispif_exit_module(void)
+{
+	platform_driver_unregister(&ispif_driver);
+}
+
+module_init(msm_ispif_init_module);
+module_exit(msm_ispif_exit_module);
+MODULE_DESCRIPTION("MSM ISP Interface driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/csi/msm_ispif.h b/drivers/media/video/msm/csi/msm_ispif.h
new file mode 100644
index 0000000..8f1dd12
--- /dev/null
+++ b/drivers/media/video/msm/csi/msm_ispif.h
@@ -0,0 +1,68 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MSM_ISPIF_H
+#define MSM_ISPIF_H
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <media/v4l2-subdev.h>
+
+struct ispif_irq_status {
+	uint32_t ispifIrqStatus0;
+	uint32_t ispifIrqStatus1;
+};
+
+struct ispif_device {
+	struct platform_device *pdev;
+	struct v4l2_subdev subdev;
+	struct resource *mem;
+	struct resource *irq;
+	struct resource *io;
+	void __iomem *base;
+	struct mutex mutex;
+	uint8_t start_ack_pending;
+	struct completion reset_complete;
+	uint32_t csid_version;
+	struct clk *ispif_clk[5];
+};
+
+struct ispif_isr_queue_cmd {
+	struct list_head list;
+	uint32_t    ispifInterruptStatus0;
+	uint32_t    ispifInterruptStatus1;
+};
+
+#define VIDIOC_MSM_ISPIF_CFG \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 1, struct msm_ispif_params)
+
+#define VIDIOC_MSM_ISPIF_INIT \
+	_IO('V', BASE_VIDIOC_PRIVATE + 2)
+
+#define VIDIOC_MSM_ISPIF_RELEASE \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 3, struct v4l2_subdev*)
+
+#define ISPIF_STREAM(intf, action) (((intf)<<ISPIF_S_STREAM_SHIFT)+(action))
+#define ISPIF_ON_FRAME_BOUNDARY	(0x01 << 0)
+#define ISPIF_OFF_FRAME_BOUNDARY    (0x01 << 1)
+#define ISPIF_OFF_IMMEDIATELY       (0x01 << 2)
+#define ISPIF_S_STREAM_SHIFT	4
+
+
+#define PIX_0 (0x01 << 0)
+#define RDI_0 (0x01 << 1)
+#define PIX_1 (0x01 << 2)
+#define RDI_1 (0x01 << 3)
+
+void msm_ispif_vfe_get_cid(uint8_t intftype, char *cids, int *num);
+
+#endif
diff --git a/drivers/media/video/msm/eeprom/Makefile b/drivers/media/video/msm/eeprom/Makefile
new file mode 100644
index 0000000..f7b7f5d
--- /dev/null
+++ b/drivers/media/video/msm/eeprom/Makefile
@@ -0,0 +1,5 @@
+GCC_VERSION      := $(shell $(CONFIG_SHELL) $(PWD)/scripts/gcc-version.sh $(CROSS_COMPILE)gcc)
+EXTRA_CFLAGS += -Idrivers/media/video/msm/io
+obj-$(CONFIG_MSM_EEPROM) += msm_camera_eeprom.o
+obj-$(CONFIG_IMX074_EEPROM) += imx074_eeprom.o
+obj-$(CONFIG_IMX091_EEPROM) += imx091_eeprom.o
\ No newline at end of file
diff --git a/drivers/media/video/msm/eeprom/imx074_eeprom.c b/drivers/media/video/msm/eeprom/imx074_eeprom.c
new file mode 100644
index 0000000..21cbafa
--- /dev/null
+++ b/drivers/media/video/msm/eeprom/imx074_eeprom.c
@@ -0,0 +1,112 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include "msm_camera_eeprom.h"
+#include "msm_camera_i2c.h"
+
+DEFINE_MUTEX(imx074_eeprom_mutex);
+static struct msm_eeprom_ctrl_t imx074_eeprom_t;
+
+static const struct i2c_device_id imx074_eeprom_i2c_id[] = {
+	{"imx074_eeprom", (kernel_ulong_t)&imx074_eeprom_t},
+	{ }
+};
+
+static struct i2c_driver imx074_eeprom_i2c_driver = {
+	.id_table = imx074_eeprom_i2c_id,
+	.probe  = msm_eeprom_i2c_probe,
+	.remove = __exit_p(imx074_eeprom_i2c_remove),
+	.driver = {
+		.name = "imx074_eeprom",
+	},
+};
+
+static int __init imx074_eeprom_i2c_add_driver(void)
+{
+	int rc = 0;
+	rc = i2c_add_driver(imx074_eeprom_t.i2c_driver);
+	return rc;
+}
+
+static struct v4l2_subdev_core_ops imx074_eeprom_subdev_core_ops = {
+	.ioctl = msm_eeprom_subdev_ioctl,
+};
+
+static struct v4l2_subdev_ops imx074_eeprom_subdev_ops = {
+	.core = &imx074_eeprom_subdev_core_ops,
+};
+
+uint8_t imx074_wbcalib_data[6];
+struct msm_calib_wb imx074_wb_data;
+
+static struct msm_camera_eeprom_info_t imx074_calib_supp_info = {
+	{FALSE, 0, 0, 1},
+	{TRUE, 6, 0, 1024},
+	{FALSE, 0, 0, 1},
+	{FALSE, 0, 0, 1},
+};
+
+static struct msm_camera_eeprom_read_t imx074_eeprom_read_tbl[] = {
+	{0x10, &imx074_wbcalib_data[0], 6, 0},
+};
+
+
+static struct msm_camera_eeprom_data_t imx074_eeprom_data_tbl[] = {
+	{&imx074_wb_data, sizeof(struct msm_calib_wb)},
+};
+
+static void imx074_format_wbdata(void)
+{
+	imx074_wb_data.r_over_g = (uint16_t)(imx074_wbcalib_data[0] << 8) |
+		imx074_wbcalib_data[1];
+	imx074_wb_data.b_over_g = (uint16_t)(imx074_wbcalib_data[2] << 8) |
+		imx074_wbcalib_data[3];
+	imx074_wb_data.gr_over_gb = (uint16_t)(imx074_wbcalib_data[4] << 8) |
+		imx074_wbcalib_data[5];
+}
+
+void imx074_format_calibrationdata(void)
+{
+	imx074_format_wbdata();
+}
+static struct msm_eeprom_ctrl_t imx074_eeprom_t = {
+	.i2c_driver = &imx074_eeprom_i2c_driver,
+	.i2c_addr = 0xA4,
+	.eeprom_v4l2_subdev_ops = &imx074_eeprom_subdev_ops,
+
+	.i2c_client = {
+		.addr_type = MSM_CAMERA_I2C_BYTE_ADDR,
+	},
+
+	.eeprom_mutex = &imx074_eeprom_mutex,
+
+	.func_tbl = {
+		.eeprom_init = NULL,
+		.eeprom_release = NULL,
+		.eeprom_get_info = msm_camera_eeprom_get_info,
+		.eeprom_get_data = msm_camera_eeprom_get_data,
+		.eeprom_set_dev_addr = NULL,
+		.eeprom_format_data = imx074_format_calibrationdata,
+	},
+	.info = &imx074_calib_supp_info,
+	.info_size = sizeof(struct msm_camera_eeprom_info_t),
+	.read_tbl = imx074_eeprom_read_tbl,
+	.read_tbl_size = ARRAY_SIZE(imx074_eeprom_read_tbl),
+	.data_tbl = imx074_eeprom_data_tbl,
+	.data_tbl_size = ARRAY_SIZE(imx074_eeprom_data_tbl),
+};
+
+subsys_initcall(imx074_eeprom_i2c_add_driver);
+MODULE_DESCRIPTION("IMX074 EEPROM");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/eeprom/imx091_eeprom.c b/drivers/media/video/msm/eeprom/imx091_eeprom.c
new file mode 100644
index 0000000..68a2361
--- /dev/null
+++ b/drivers/media/video/msm/eeprom/imx091_eeprom.c
@@ -0,0 +1,127 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include "msm_camera_eeprom.h"
+#include "msm_camera_i2c.h"
+
+DEFINE_MUTEX(imx091_eeprom_mutex);
+static struct msm_eeprom_ctrl_t imx091_eeprom_t;
+
+static const struct i2c_device_id imx091_eeprom_i2c_id[] = {
+	{"imx091_eeprom", (kernel_ulong_t)&imx091_eeprom_t},
+	{ }
+};
+
+static struct i2c_driver imx091_eeprom_i2c_driver = {
+	.id_table = imx091_eeprom_i2c_id,
+	.probe  = msm_eeprom_i2c_probe,
+	.remove = __exit_p(imx091_eeprom_i2c_remove),
+	.driver = {
+		.name = "imx091_eeprom",
+	},
+};
+
+static int __init imx091_eeprom_i2c_add_driver(void)
+{
+	int rc = 0;
+	rc = i2c_add_driver(imx091_eeprom_t.i2c_driver);
+	return rc;
+}
+
+static struct v4l2_subdev_core_ops imx091_eeprom_subdev_core_ops = {
+	.ioctl = msm_eeprom_subdev_ioctl,
+};
+
+static struct v4l2_subdev_ops imx091_eeprom_subdev_ops = {
+	.core = &imx091_eeprom_subdev_core_ops,
+};
+
+uint8_t imx091_wbcalib_data[6];
+uint8_t imx091_afcalib_data[6];
+struct msm_calib_wb imx091_wb_data;
+struct msm_calib_af imx091_af_data;
+
+static struct msm_camera_eeprom_info_t imx091_calib_supp_info = {
+	{TRUE, 6, 1, 1},
+	{TRUE, 6, 0, 32768},
+	{FALSE, 0, 0, 1},
+	{FALSE, 0, 0, 1},
+};
+
+static struct msm_camera_eeprom_read_t imx091_eeprom_read_tbl[] = {
+	{0x05, &imx091_wbcalib_data[0], 6, 0},
+	{0x0B, &imx091_afcalib_data[0], 6, 0},
+};
+
+
+static struct msm_camera_eeprom_data_t imx091_eeprom_data_tbl[] = {
+	{&imx091_wb_data, sizeof(struct msm_calib_wb)},
+	{&imx091_af_data, sizeof(struct msm_calib_af)},
+};
+
+static void imx091_format_wbdata(void)
+{
+	imx091_wb_data.r_over_g = (uint16_t)(imx091_wbcalib_data[1] << 8) |
+		(imx091_wbcalib_data[0] - 0x32);
+	imx091_wb_data.b_over_g = (uint16_t)(imx091_wbcalib_data[3] << 8) |
+		(imx091_wbcalib_data[2] - 0x32);
+	imx091_wb_data.gr_over_gb = (uint16_t)(imx091_wbcalib_data[5] << 8) |
+		(imx091_wbcalib_data[4] - 0x32);
+}
+
+static void imx091_format_afdata(void)
+{
+	imx091_af_data.inf_dac = (uint16_t)(imx091_afcalib_data[1] << 8) |
+		imx091_afcalib_data[0];
+	imx091_af_data.macro_dac = (uint16_t)(imx091_afcalib_data[3] << 8) |
+		imx091_afcalib_data[2];
+	imx091_af_data.start_dac = (uint16_t)(imx091_afcalib_data[5] << 8) |
+		imx091_afcalib_data[4];
+}
+
+void imx091_format_calibrationdata(void)
+{
+	imx091_format_wbdata();
+	imx091_format_afdata();
+}
+static struct msm_eeprom_ctrl_t imx091_eeprom_t = {
+	.i2c_driver = &imx091_eeprom_i2c_driver,
+	.i2c_addr = 0xA1,
+	.eeprom_v4l2_subdev_ops = &imx091_eeprom_subdev_ops,
+
+	.i2c_client = {
+		.addr_type = MSM_CAMERA_I2C_BYTE_ADDR,
+	},
+
+	.eeprom_mutex = &imx091_eeprom_mutex,
+
+	.func_tbl = {
+		.eeprom_init = NULL,
+		.eeprom_release = NULL,
+		.eeprom_get_info = msm_camera_eeprom_get_info,
+		.eeprom_get_data = msm_camera_eeprom_get_data,
+		.eeprom_set_dev_addr = NULL,
+		.eeprom_format_data = imx091_format_calibrationdata,
+	},
+	.info = &imx091_calib_supp_info,
+	.info_size = sizeof(struct msm_camera_eeprom_info_t),
+	.read_tbl = imx091_eeprom_read_tbl,
+	.read_tbl_size = ARRAY_SIZE(imx091_eeprom_read_tbl),
+	.data_tbl = imx091_eeprom_data_tbl,
+	.data_tbl_size = ARRAY_SIZE(imx091_eeprom_data_tbl),
+};
+
+subsys_initcall(imx091_eeprom_i2c_add_driver);
+MODULE_DESCRIPTION("imx091 EEPROM");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/eeprom/msm_camera_eeprom.c b/drivers/media/video/msm/eeprom/msm_camera_eeprom.c
new file mode 100644
index 0000000..96a6e04
--- /dev/null
+++ b/drivers/media/video/msm/eeprom/msm_camera_eeprom.c
@@ -0,0 +1,196 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include "msm_camera_eeprom.h"
+
+int32_t msm_camera_eeprom_read(struct msm_eeprom_ctrl_t *ectrl,
+	uint32_t reg_addr, void *data, uint32_t num_byte,
+	uint16_t convert_endian)
+{
+	int rc = 0;
+	if (ectrl->func_tbl.eeprom_set_dev_addr != NULL)
+		ectrl->func_tbl.eeprom_set_dev_addr(ectrl, &reg_addr);
+
+	if (!convert_endian) {
+		rc = msm_camera_i2c_read_seq(
+			&ectrl->i2c_client, reg_addr, data, num_byte);
+	} else {
+		unsigned char buf[num_byte];
+		uint8_t *data_ptr = (uint8_t *) data;
+		int i;
+		rc = msm_camera_i2c_read_seq(
+			&ectrl->i2c_client, reg_addr, buf, num_byte);
+		for (i = 0; i < num_byte; i += 2) {
+			data_ptr[i] = buf[i+1];
+			data_ptr[i+1] = buf[i];
+		}
+	}
+	return rc;
+}
+
+int32_t msm_camera_eeprom_read_tbl(struct msm_eeprom_ctrl_t *ectrl,
+	struct msm_camera_eeprom_read_t *read_tbl, uint16_t tbl_size)
+{
+	int i, rc = 0;
+	CDBG("%s: open\n", __func__);
+	if (read_tbl == NULL)
+		return rc;
+
+	for (i = 0; i < tbl_size; i++) {
+		rc = msm_camera_eeprom_read
+			(ectrl, read_tbl[i].reg_addr,
+			read_tbl[i].dest_ptr, read_tbl[i].num_byte,
+			read_tbl[i].convert_endian);
+		if (rc < 0) {
+			pr_err("%s: read failed\n", __func__);
+			return rc;
+		}
+	}
+	CDBG("%s: done\n", __func__);
+	return rc;
+}
+
+int32_t msm_camera_eeprom_get_info(struct msm_eeprom_ctrl_t *ectrl,
+	struct msm_camera_eeprom_info_t *einfo)
+{
+	int rc = 0;
+	CDBG("%s: open\n", __func__);
+	memcpy(einfo, ectrl->info, ectrl->info_size);
+	CDBG("%s: done =%d\n", __func__, rc);
+	return rc;
+}
+
+int32_t msm_camera_eeprom_get_data(struct msm_eeprom_ctrl_t *ectrl,
+	struct msm_eeprom_data_t *edata)
+{
+	int rc = 0;
+	if (edata->index >= ectrl->data_tbl_size)
+		return -EFAULT;
+	if (copy_to_user(edata->eeprom_data,
+		ectrl->data_tbl[edata->index].data,
+		ectrl->data_tbl[edata->index].size))
+		rc = -EFAULT;
+	return rc;
+}
+
+int32_t msm_eeprom_config(struct msm_eeprom_ctrl_t *e_ctrl,
+	void __user *argp)
+{
+	struct msm_eeprom_cfg_data cdata;
+	int32_t rc = 0;
+	if (copy_from_user(&cdata,
+		(void *)argp,
+		sizeof(struct msm_eeprom_cfg_data)))
+		return -EFAULT;
+	mutex_lock(e_ctrl->eeprom_mutex);
+
+	switch (cdata.cfgtype) {
+	case CFG_GET_EEPROM_INFO:
+		if (e_ctrl->func_tbl.eeprom_get_info == NULL) {
+			rc = -EFAULT;
+			break;
+		}
+		rc = e_ctrl->func_tbl.eeprom_get_info(e_ctrl,
+			&cdata.cfg.get_info);
+
+		if (copy_to_user((void *)argp,
+			&cdata,
+			sizeof(struct msm_eeprom_cfg_data)))
+			rc = -EFAULT;
+		break;
+	case CFG_GET_EEPROM_DATA:
+		if (e_ctrl->func_tbl.eeprom_get_data == NULL) {
+			rc = -EFAULT;
+			break;
+		}
+		rc = e_ctrl->func_tbl.eeprom_get_data(e_ctrl,
+			&cdata.cfg.get_data);
+
+		if (copy_to_user((void *)argp,
+			&cdata,
+			sizeof(struct msm_eeprom_cfg_data)))
+			rc = -EFAULT;
+		break;
+	default:
+		break;
+	}
+	mutex_unlock(e_ctrl->eeprom_mutex);
+	return rc;
+}
+
+struct msm_eeprom_ctrl_t *get_ectrl(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct msm_eeprom_ctrl_t, sdev);
+}
+
+long msm_eeprom_subdev_ioctl(struct v4l2_subdev *sd,
+	unsigned int cmd, void *arg)
+{
+	struct msm_eeprom_ctrl_t *e_ctrl = get_ectrl(sd);
+	void __user *argp = (void __user *)arg;
+	switch (cmd) {
+	case VIDIOC_MSM_EEPROM_CFG:
+		return msm_eeprom_config(e_ctrl, argp);
+	default:
+		return -ENOIOCTLCMD;
+	}
+}
+
+int32_t msm_eeprom_i2c_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	int rc = 0;
+	struct msm_eeprom_ctrl_t *e_ctrl_t = NULL;
+	CDBG("%s called\n", __func__);
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		pr_err("i2c_check_functionality failed\n");
+		goto probe_failure;
+	}
+
+	e_ctrl_t = (struct msm_eeprom_ctrl_t *)(id->driver_data);
+	e_ctrl_t->i2c_client.client = client;
+
+	if (e_ctrl_t->i2c_addr != 0)
+		e_ctrl_t->i2c_client.client->addr = e_ctrl_t->i2c_addr;
+
+	CDBG("%s client = %x\n", __func__, (unsigned int) client);
+
+	/* Assign name for sub device */
+	snprintf(e_ctrl_t->sdev.name, sizeof(e_ctrl_t->sdev.name),
+		"%s", e_ctrl_t->i2c_driver->driver.name);
+
+	if (e_ctrl_t->func_tbl.eeprom_init != NULL) {
+		rc = e_ctrl_t->func_tbl.eeprom_init(e_ctrl_t,
+			e_ctrl_t->i2c_client.client->adapter);
+	}
+	msm_camera_eeprom_read_tbl(e_ctrl_t,
+		e_ctrl_t->read_tbl,
+		e_ctrl_t->read_tbl_size);
+
+	if (e_ctrl_t->func_tbl.eeprom_format_data != NULL)
+		e_ctrl_t->func_tbl.eeprom_format_data();
+
+	if (e_ctrl_t->func_tbl.eeprom_release != NULL)
+		rc = e_ctrl_t->func_tbl.eeprom_release(e_ctrl_t);
+
+
+	/* Initialize sub device */
+	v4l2_i2c_subdev_init(&e_ctrl_t->sdev,
+		e_ctrl_t->i2c_client.client,
+		e_ctrl_t->eeprom_v4l2_subdev_ops);
+	CDBG("%s success resut=%d\n", __func__, rc);
+	return rc;
+
+probe_failure:
+	pr_err("%s failed! rc = %d\n", __func__, rc);
+	return rc;
+}
diff --git a/drivers/media/video/msm/eeprom/msm_camera_eeprom.h b/drivers/media/video/msm/eeprom/msm_camera_eeprom.h
new file mode 100644
index 0000000..830e5d8
--- /dev/null
+++ b/drivers/media/video/msm/eeprom/msm_camera_eeprom.h
@@ -0,0 +1,82 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef MSM_CAMERA_EEPROM_H
+#define MSM_CAMERA_EEPROM_H
+
+#include <linux/delay.h>
+#include <mach/camera.h>
+#include <media/v4l2-subdev.h>
+#include "msm_camera_i2c.h"
+
+#define TRUE  1
+#define FALSE 0
+
+struct msm_eeprom_ctrl_t;
+
+struct msm_camera_eeprom_fn_t {
+	int32_t (*eeprom_init)
+		(struct msm_eeprom_ctrl_t *ectrl,
+		struct i2c_adapter *adapter);
+	int32_t (*eeprom_release)
+		(struct msm_eeprom_ctrl_t *ectrl);
+	int32_t (*eeprom_get_info)
+		(struct msm_eeprom_ctrl_t *ectrl,
+		 struct msm_camera_eeprom_info_t *einfo);
+	int32_t (*eeprom_get_data)
+		(struct msm_eeprom_ctrl_t *ectrl,
+		 struct msm_eeprom_data_t *edata);
+	void (*eeprom_set_dev_addr)
+		(struct msm_eeprom_ctrl_t*, uint32_t*);
+	void (*eeprom_format_data)
+		(void);
+};
+
+struct msm_camera_eeprom_read_t {
+	uint32_t reg_addr;
+	void *dest_ptr;
+	uint32_t num_byte;
+	uint16_t convert_endian;
+};
+
+struct msm_camera_eeprom_data_t {
+	void *data;
+	uint16_t size;
+};
+
+struct msm_eeprom_ctrl_t {
+	struct msm_camera_i2c_client i2c_client;
+	uint16_t i2c_addr;
+	struct i2c_driver *i2c_driver;
+	struct mutex *eeprom_mutex;
+	struct v4l2_subdev sdev;
+	struct v4l2_subdev_ops *eeprom_v4l2_subdev_ops;
+	struct msm_camera_eeprom_fn_t func_tbl;
+	struct msm_camera_eeprom_info_t *info;
+	uint16_t info_size;
+	struct msm_camera_eeprom_read_t *read_tbl;
+	uint16_t read_tbl_size;
+	struct msm_camera_eeprom_data_t *data_tbl;
+	uint16_t data_tbl_size;
+};
+
+int32_t msm_camera_eeprom_get_data(struct msm_eeprom_ctrl_t *ectrl,
+	struct msm_eeprom_data_t *edata);
+int32_t msm_camera_eeprom_get_info(struct msm_eeprom_ctrl_t *ectrl,
+	struct msm_camera_eeprom_info_t *einfo);
+int32_t msm_eeprom_i2c_probe(struct i2c_client *client,
+	const struct i2c_device_id *id);
+long msm_eeprom_subdev_ioctl(struct v4l2_subdev *sd,
+	unsigned int cmd, void *arg);
+
+#define VIDIOC_MSM_EEPROM_CFG \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 12, void __user *)
+#endif
diff --git a/drivers/media/video/msm/flash.c b/drivers/media/video/msm/flash.c
new file mode 100644
index 0000000..7c4021d
--- /dev/null
+++ b/drivers/media/video/msm/flash.c
@@ -0,0 +1,777 @@
+
+/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/leds-pmic8058.h>
+#include <linux/pwm.h>
+#include <linux/pmic8058-pwm.h>
+#include <linux/hrtimer.h>
+#include <linux/i2c.h>
+#include <linux/export.h>
+#include <mach/pmic.h>
+#include <mach/camera.h>
+#include <mach/gpio.h>
+
+struct i2c_client *sx150x_client;
+struct timer_list timer_flash;
+static struct msm_camera_sensor_info *sensor_data;
+enum msm_cam_flash_stat{
+	MSM_CAM_FLASH_OFF,
+	MSM_CAM_FLASH_ON,
+};
+
+static struct i2c_client *sc628a_client;
+
+static int32_t flash_i2c_txdata(struct i2c_client *client,
+		unsigned char *txdata, int length)
+{
+	struct i2c_msg msg[] = {
+		{
+			.addr = client->addr >> 1,
+			.flags = 0,
+			.len = length,
+			.buf = txdata,
+		},
+	};
+	if (i2c_transfer(client->adapter, msg, 1) < 0) {
+		CDBG("flash_i2c_txdata faild 0x%x\n", client->addr >> 1);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int32_t flash_i2c_write_b(struct i2c_client *client,
+	uint8_t baddr, uint8_t bdata)
+{
+	int32_t rc = -EFAULT;
+	unsigned char buf[2];
+	if (!client)
+		return  -ENOTSUPP;
+
+	memset(buf, 0, sizeof(buf));
+	buf[0] = baddr;
+	buf[1] = bdata;
+
+	rc = flash_i2c_txdata(client, buf, 2);
+	if (rc < 0) {
+		CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+				baddr, bdata);
+	}
+	usleep_range(4000, 5000);
+
+	return rc;
+}
+
+static const struct i2c_device_id sc628a_i2c_id[] = {
+	{"sc628a", 0},
+	{ }
+};
+
+static int sc628a_i2c_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	int rc = 0;
+	CDBG("sc628a_probe called!\n");
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		pr_err("i2c_check_functionality failed\n");
+		goto probe_failure;
+	}
+
+	sc628a_client = client;
+
+	CDBG("sc628a_probe success rc = %d\n", rc);
+	return 0;
+
+probe_failure:
+	pr_err("sc628a_probe failed! rc = %d\n", rc);
+	return rc;
+}
+
+static struct i2c_driver sc628a_i2c_driver = {
+	.id_table = sc628a_i2c_id,
+	.probe  = sc628a_i2c_probe,
+	.remove = __exit_p(sc628a_i2c_remove),
+	.driver = {
+		.name = "sc628a",
+	},
+};
+
+static struct i2c_client *tps61310_client;
+
+static const struct i2c_device_id tps61310_i2c_id[] = {
+	{"tps61310", 0},
+	{ }
+};
+
+static int tps61310_i2c_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	int rc = 0;
+	CDBG("%s enter\n", __func__);
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		pr_err("i2c_check_functionality failed\n");
+		goto probe_failure;
+	}
+
+	tps61310_client = client;
+
+	rc = flash_i2c_write_b(tps61310_client, 0x01, 0x00);
+	if (rc < 0) {
+		tps61310_client = NULL;
+		goto probe_failure;
+	}
+
+	CDBG("%s success! rc = %d\n", __func__, rc);
+	return 0;
+
+probe_failure:
+	pr_err("%s failed! rc = %d\n", __func__, rc);
+	return rc;
+}
+
+static struct i2c_driver tps61310_i2c_driver = {
+	.id_table = tps61310_i2c_id,
+	.probe  = tps61310_i2c_probe,
+	.remove = __exit_p(tps61310_i2c_remove),
+	.driver = {
+		.name = "tps61310",
+	},
+};
+
+static int config_flash_gpio_table(enum msm_cam_flash_stat stat,
+			struct msm_camera_sensor_strobe_flash_data *sfdata)
+{
+	int rc = 0, i = 0;
+	int msm_cam_flash_gpio_tbl[][2] = {
+		{sfdata->flash_trigger, 1},
+		{sfdata->flash_charge, 1},
+		{sfdata->flash_charge_done, 0}
+	};
+
+	if (stat == MSM_CAM_FLASH_ON) {
+		for (i = 0; i < ARRAY_SIZE(msm_cam_flash_gpio_tbl); i++) {
+			rc = gpio_request(msm_cam_flash_gpio_tbl[i][0],
+							  "CAM_FLASH_GPIO");
+			if (unlikely(rc < 0)) {
+				pr_err("%s not able to get gpio\n", __func__);
+				for (i--; i >= 0; i--)
+					gpio_free(msm_cam_flash_gpio_tbl[i][0]);
+				break;
+			}
+			if (msm_cam_flash_gpio_tbl[i][1])
+				gpio_direction_output(
+					msm_cam_flash_gpio_tbl[i][0], 0);
+			else
+				gpio_direction_input(
+					msm_cam_flash_gpio_tbl[i][0]);
+		}
+	} else {
+		for (i = 0; i < ARRAY_SIZE(msm_cam_flash_gpio_tbl); i++) {
+			gpio_direction_input(msm_cam_flash_gpio_tbl[i][0]);
+			gpio_free(msm_cam_flash_gpio_tbl[i][0]);
+		}
+	}
+	return rc;
+}
+
+int msm_camera_flash_current_driver(
+	struct msm_camera_sensor_flash_current_driver *current_driver,
+	unsigned led_state)
+{
+	int rc = 0;
+#if defined CONFIG_LEDS_PMIC8058
+	int idx;
+	const struct pmic8058_leds_platform_data *driver_channel =
+		current_driver->driver_channel;
+	int num_leds = driver_channel->num_leds;
+
+	CDBG("%s: led_state = %d\n", __func__, led_state);
+
+	/* Evenly distribute current across all channels */
+	switch (led_state) {
+	case MSM_CAMERA_LED_OFF:
+		for (idx = 0; idx < num_leds; ++idx) {
+			rc = pm8058_set_led_current(
+				driver_channel->leds[idx].id, 0);
+			if (rc < 0)
+				pr_err(
+					"%s: FAIL name = %s, rc = %d\n",
+					__func__,
+					driver_channel->leds[idx].name,
+					rc);
+		}
+		break;
+
+	case MSM_CAMERA_LED_LOW:
+		for (idx = 0; idx < num_leds; ++idx) {
+			rc = pm8058_set_led_current(
+				driver_channel->leds[idx].id,
+				current_driver->low_current/num_leds);
+			if (rc < 0)
+				pr_err(
+					"%s: FAIL name = %s, rc = %d\n",
+					__func__,
+					driver_channel->leds[idx].name,
+					rc);
+		}
+		break;
+
+	case MSM_CAMERA_LED_HIGH:
+		for (idx = 0; idx < num_leds; ++idx) {
+			rc = pm8058_set_led_current(
+				driver_channel->leds[idx].id,
+				current_driver->high_current/num_leds);
+			if (rc < 0)
+				pr_err(
+					"%s: FAIL name = %s, rc = %d\n",
+					__func__,
+					driver_channel->leds[idx].name,
+					rc);
+		}
+		break;
+	case MSM_CAMERA_LED_INIT:
+	case MSM_CAMERA_LED_RELEASE:
+		break;
+
+	default:
+		rc = -EFAULT;
+		break;
+	}
+	CDBG("msm_camera_flash_led_pmic8058: return %d\n", rc);
+#endif /* CONFIG_LEDS_PMIC8058 */
+	return rc;
+}
+
+int msm_camera_flash_led(
+		struct msm_camera_sensor_flash_external *external,
+		unsigned led_state)
+{
+	int rc = 0;
+
+	CDBG("msm_camera_flash_led: %d\n", led_state);
+	switch (led_state) {
+	case MSM_CAMERA_LED_INIT:
+		rc = gpio_request(external->led_en, "sgm3141");
+		CDBG("MSM_CAMERA_LED_INIT: gpio_req: %d %d\n",
+				external->led_en, rc);
+		if (!rc)
+			gpio_direction_output(external->led_en, 0);
+		else
+			return 0;
+
+		rc = gpio_request(external->led_flash_en, "sgm3141");
+		CDBG("MSM_CAMERA_LED_INIT: gpio_req: %d %d\n",
+				external->led_flash_en, rc);
+		if (!rc)
+			gpio_direction_output(external->led_flash_en, 0);
+
+			break;
+
+	case MSM_CAMERA_LED_RELEASE:
+		CDBG("MSM_CAMERA_LED_RELEASE\n");
+		gpio_set_value_cansleep(external->led_en, 0);
+		gpio_free(external->led_en);
+		gpio_set_value_cansleep(external->led_flash_en, 0);
+		gpio_free(external->led_flash_en);
+		break;
+
+	case MSM_CAMERA_LED_OFF:
+		CDBG("MSM_CAMERA_LED_OFF\n");
+		gpio_set_value_cansleep(external->led_en, 0);
+		gpio_set_value_cansleep(external->led_flash_en, 0);
+		break;
+
+	case MSM_CAMERA_LED_LOW:
+		CDBG("MSM_CAMERA_LED_LOW\n");
+		gpio_set_value_cansleep(external->led_en, 1);
+		gpio_set_value_cansleep(external->led_flash_en, 1);
+		break;
+
+	case MSM_CAMERA_LED_HIGH:
+		CDBG("MSM_CAMERA_LED_HIGH\n");
+		gpio_set_value_cansleep(external->led_en, 1);
+		gpio_set_value_cansleep(external->led_flash_en, 1);
+		break;
+
+	default:
+		rc = -EFAULT;
+		break;
+	}
+
+	return rc;
+}
+
+int msm_camera_flash_external(
+	struct msm_camera_sensor_flash_external *external,
+	unsigned led_state)
+{
+	int rc = 0;
+
+	switch (led_state) {
+
+	case MSM_CAMERA_LED_INIT:
+		if (external->flash_id == MAM_CAMERA_EXT_LED_FLASH_SC628A) {
+			if (!sc628a_client) {
+				rc = i2c_add_driver(&sc628a_i2c_driver);
+				if (rc < 0 || sc628a_client == NULL) {
+					pr_err("sc628a_i2c_driver add failed\n");
+					rc = -ENOTSUPP;
+					return rc;
+				}
+			}
+		} else if (external->flash_id ==
+			MAM_CAMERA_EXT_LED_FLASH_TPS61310) {
+			if (!tps61310_client) {
+				rc = i2c_add_driver(&tps61310_i2c_driver);
+				if (rc < 0 || tps61310_client == NULL) {
+					pr_err("tps61310_i2c_driver add failed\n");
+					rc = -ENOTSUPP;
+					return rc;
+				}
+			}
+		} else {
+			pr_err("Flash id not supported\n");
+			rc = -ENOTSUPP;
+			return rc;
+		}
+
+#if defined(CONFIG_GPIO_SX150X) || defined(CONFIG_GPIO_SX150X_MODULE)
+		if (external->expander_info && !sx150x_client) {
+			struct i2c_adapter *adapter =
+			i2c_get_adapter(external->expander_info->bus_id);
+			if (adapter)
+				sx150x_client = i2c_new_device(adapter,
+					external->expander_info->board_info);
+			if (!sx150x_client || !adapter) {
+				pr_err("sx150x_client is not available\n");
+				rc = -ENOTSUPP;
+				if (sc628a_client) {
+					i2c_del_driver(&sc628a_i2c_driver);
+					sc628a_client = NULL;
+				}
+				if (tps61310_client) {
+					i2c_del_driver(&tps61310_i2c_driver);
+					tps61310_client = NULL;
+				}
+				return rc;
+			}
+			i2c_put_adapter(adapter);
+		}
+#endif
+		if (sc628a_client)
+			rc = gpio_request(external->led_en, "sc628a");
+		if (tps61310_client)
+			rc = gpio_request(external->led_en, "tps61310");
+
+		if (!rc) {
+			gpio_direction_output(external->led_en, 0);
+		} else {
+			goto error;
+		}
+
+		if (sc628a_client)
+			rc = gpio_request(external->led_flash_en, "sc628a");
+		if (tps61310_client)
+			rc = gpio_request(external->led_flash_en, "tps61310");
+
+		if (!rc) {
+			gpio_direction_output(external->led_flash_en, 0);
+			break;
+		}
+
+		gpio_set_value_cansleep(external->led_en, 0);
+		gpio_free(external->led_en);
+error:
+		pr_err("%s gpio request failed\n", __func__);
+		if (sc628a_client) {
+			i2c_del_driver(&sc628a_i2c_driver);
+			sc628a_client = NULL;
+		}
+		if (tps61310_client) {
+			i2c_del_driver(&tps61310_i2c_driver);
+			tps61310_client = NULL;
+		}
+		break;
+
+	case MSM_CAMERA_LED_RELEASE:
+		if (sc628a_client || tps61310_client) {
+			gpio_set_value_cansleep(external->led_en, 0);
+			gpio_free(external->led_en);
+			gpio_set_value_cansleep(external->led_flash_en, 0);
+			gpio_free(external->led_flash_en);
+			if (sc628a_client) {
+				i2c_del_driver(&sc628a_i2c_driver);
+				sc628a_client = NULL;
+			}
+			if (tps61310_client) {
+				i2c_del_driver(&tps61310_i2c_driver);
+				tps61310_client = NULL;
+			}
+		}
+#if defined(CONFIG_GPIO_SX150X) || defined(CONFIG_GPIO_SX150X_MODULE)
+		if (external->expander_info && sx150x_client) {
+			i2c_unregister_device(sx150x_client);
+			sx150x_client = NULL;
+		}
+#endif
+		break;
+
+	case MSM_CAMERA_LED_OFF:
+		if (sc628a_client)
+			rc = flash_i2c_write_b(sc628a_client, 0x02, 0x00);
+		if (tps61310_client)
+			rc = flash_i2c_write_b(tps61310_client, 0x01, 0x00);
+		gpio_set_value_cansleep(external->led_en, 0);
+		gpio_set_value_cansleep(external->led_flash_en, 0);
+		break;
+
+	case MSM_CAMERA_LED_LOW:
+		gpio_set_value_cansleep(external->led_en, 1);
+		gpio_set_value_cansleep(external->led_flash_en, 1);
+		usleep_range(2000, 3000);
+		if (sc628a_client)
+			rc = flash_i2c_write_b(sc628a_client, 0x02, 0x06);
+		if (tps61310_client)
+			rc = flash_i2c_write_b(tps61310_client, 0x01, 0x86);
+		break;
+
+	case MSM_CAMERA_LED_HIGH:
+		gpio_set_value_cansleep(external->led_en, 1);
+		gpio_set_value_cansleep(external->led_flash_en, 1);
+		usleep_range(2000, 3000);
+		if (sc628a_client)
+			rc = flash_i2c_write_b(sc628a_client, 0x02, 0x49);
+		if (tps61310_client)
+			rc = flash_i2c_write_b(tps61310_client, 0x01, 0x8B);
+		break;
+
+	default:
+		rc = -EFAULT;
+		break;
+	}
+	return rc;
+}
+
+static int msm_camera_flash_pwm(
+	struct msm_camera_sensor_flash_pwm *pwm,
+	unsigned led_state)
+{
+	int rc = 0;
+	int PWM_PERIOD = USEC_PER_SEC / pwm->freq;
+
+	static struct pwm_device *flash_pwm;
+
+	if (!flash_pwm) {
+		flash_pwm = pwm_request(pwm->channel, "camera-flash");
+		if (flash_pwm == NULL || IS_ERR(flash_pwm)) {
+			pr_err("%s: FAIL pwm_request(): flash_pwm=%p\n",
+			       __func__, flash_pwm);
+			flash_pwm = NULL;
+			return -ENXIO;
+		}
+	}
+
+	switch (led_state) {
+	case MSM_CAMERA_LED_LOW:
+		rc = pwm_config(flash_pwm,
+			(PWM_PERIOD/pwm->max_load)*pwm->low_load,
+			PWM_PERIOD);
+		if (rc >= 0)
+			rc = pwm_enable(flash_pwm);
+		break;
+
+	case MSM_CAMERA_LED_HIGH:
+		rc = pwm_config(flash_pwm,
+			(PWM_PERIOD/pwm->max_load)*pwm->high_load,
+			PWM_PERIOD);
+		if (rc >= 0)
+			rc = pwm_enable(flash_pwm);
+		break;
+
+	case MSM_CAMERA_LED_OFF:
+		pwm_disable(flash_pwm);
+		break;
+	case MSM_CAMERA_LED_INIT:
+	case MSM_CAMERA_LED_RELEASE:
+		break;
+
+	default:
+		rc = -EFAULT;
+		break;
+	}
+	return rc;
+}
+
+int msm_camera_flash_pmic(
+	struct msm_camera_sensor_flash_pmic *pmic,
+	unsigned led_state)
+{
+	int rc = 0;
+
+	switch (led_state) {
+	case MSM_CAMERA_LED_OFF:
+		rc = pmic->pmic_set_current(pmic->led_src_1, 0);
+		if (pmic->num_of_src > 1)
+			rc = pmic->pmic_set_current(pmic->led_src_2, 0);
+		break;
+
+	case MSM_CAMERA_LED_LOW:
+		rc = pmic->pmic_set_current(pmic->led_src_1,
+				pmic->low_current);
+		if (pmic->num_of_src > 1)
+			rc = pmic->pmic_set_current(pmic->led_src_2, 0);
+		break;
+
+	case MSM_CAMERA_LED_HIGH:
+		rc = pmic->pmic_set_current(pmic->led_src_1,
+			pmic->high_current);
+		if (pmic->num_of_src > 1)
+			rc = pmic->pmic_set_current(pmic->led_src_2,
+				pmic->high_current);
+		break;
+
+	case MSM_CAMERA_LED_INIT:
+	case MSM_CAMERA_LED_RELEASE:
+		 break;
+
+	default:
+		rc = -EFAULT;
+		break;
+	}
+	CDBG("flash_set_led_state: return %d\n", rc);
+
+	return rc;
+}
+
+int32_t msm_camera_flash_set_led_state(
+	struct msm_camera_sensor_flash_data *fdata, unsigned led_state)
+{
+	int32_t rc;
+
+	if (fdata->flash_type != MSM_CAMERA_FLASH_LED ||
+		fdata->flash_src == NULL)
+		return -ENODEV;
+
+	switch (fdata->flash_src->flash_sr_type) {
+	case MSM_CAMERA_FLASH_SRC_PMIC:
+		rc = msm_camera_flash_pmic(&fdata->flash_src->_fsrc.pmic_src,
+			led_state);
+		break;
+
+	case MSM_CAMERA_FLASH_SRC_PWM:
+		rc = msm_camera_flash_pwm(&fdata->flash_src->_fsrc.pwm_src,
+			led_state);
+		break;
+
+	case MSM_CAMERA_FLASH_SRC_CURRENT_DRIVER:
+		rc = msm_camera_flash_current_driver(
+			&fdata->flash_src->_fsrc.current_driver_src,
+			led_state);
+		break;
+
+	case MSM_CAMERA_FLASH_SRC_EXT:
+		rc = msm_camera_flash_external(
+			&fdata->flash_src->_fsrc.ext_driver_src,
+			led_state);
+		break;
+
+	case MSM_CAMERA_FLASH_SRC_LED1:
+		rc = msm_camera_flash_led(
+				&fdata->flash_src->_fsrc.ext_driver_src,
+				led_state);
+		break;
+
+	default:
+		rc = -ENODEV;
+		break;
+	}
+
+	return rc;
+}
+
+static int msm_strobe_flash_xenon_charge(int32_t flash_charge,
+		int32_t charge_enable, uint32_t flash_recharge_duration)
+{
+	gpio_set_value_cansleep(flash_charge, charge_enable);
+	if (charge_enable) {
+		timer_flash.expires = jiffies +
+			msecs_to_jiffies(flash_recharge_duration);
+		/* add timer for the recharge */
+		if (!timer_pending(&timer_flash))
+			add_timer(&timer_flash);
+	} else
+		del_timer_sync(&timer_flash);
+	return 0;
+}
+
+static void strobe_flash_xenon_recharge_handler(unsigned long data)
+{
+	unsigned long flags;
+	struct msm_camera_sensor_strobe_flash_data *sfdata =
+		(struct msm_camera_sensor_strobe_flash_data *)data;
+
+	spin_lock_irqsave(&sfdata->timer_lock, flags);
+	msm_strobe_flash_xenon_charge(sfdata->flash_charge, 1,
+		sfdata->flash_recharge_duration);
+	spin_unlock_irqrestore(&sfdata->timer_lock, flags);
+
+	return;
+}
+
+static irqreturn_t strobe_flash_charge_ready_irq(int irq_num, void *data)
+{
+	struct msm_camera_sensor_strobe_flash_data *sfdata =
+		(struct msm_camera_sensor_strobe_flash_data *)data;
+
+	/* put the charge signal to low */
+	gpio_set_value_cansleep(sfdata->flash_charge, 0);
+
+	return IRQ_HANDLED;
+}
+
+static int msm_strobe_flash_xenon_init(
+	struct msm_camera_sensor_strobe_flash_data *sfdata)
+{
+	unsigned long flags;
+	int rc = 0;
+
+	spin_lock_irqsave(&sfdata->spin_lock, flags);
+	if (!sfdata->state) {
+
+		rc = config_flash_gpio_table(MSM_CAM_FLASH_ON, sfdata);
+		if (rc < 0) {
+			pr_err("%s: gpio_request failed\n", __func__);
+			goto go_out;
+		}
+		rc = request_irq(sfdata->irq, strobe_flash_charge_ready_irq,
+			IRQF_TRIGGER_RISING, "charge_ready", sfdata);
+		if (rc < 0) {
+			pr_err("%s: request_irq failed %d\n", __func__, rc);
+			goto go_out;
+		}
+
+		spin_lock_init(&sfdata->timer_lock);
+		/* setup timer */
+		init_timer(&timer_flash);
+		timer_flash.function = strobe_flash_xenon_recharge_handler;
+		timer_flash.data = (unsigned long)sfdata;
+	}
+	sfdata->state++;
+go_out:
+	spin_unlock_irqrestore(&sfdata->spin_lock, flags);
+
+	return rc;
+}
+
+static int msm_strobe_flash_xenon_release
+(struct msm_camera_sensor_strobe_flash_data *sfdata, int32_t final_release)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&sfdata->spin_lock, flags);
+	if (sfdata->state > 0) {
+		if (final_release)
+			sfdata->state = 0;
+		else
+			sfdata->state--;
+
+		if (!sfdata->state) {
+			free_irq(sfdata->irq, sfdata);
+			config_flash_gpio_table(MSM_CAM_FLASH_OFF, sfdata);
+			if (timer_pending(&timer_flash))
+				del_timer_sync(&timer_flash);
+		}
+	}
+	spin_unlock_irqrestore(&sfdata->spin_lock, flags);
+	return 0;
+}
+
+static void msm_strobe_flash_xenon_fn_init
+	(struct msm_strobe_flash_ctrl *strobe_flash_ptr)
+{
+	strobe_flash_ptr->strobe_flash_init =
+				msm_strobe_flash_xenon_init;
+	strobe_flash_ptr->strobe_flash_charge =
+				msm_strobe_flash_xenon_charge;
+	strobe_flash_ptr->strobe_flash_release =
+				msm_strobe_flash_xenon_release;
+}
+
+int msm_strobe_flash_init(struct msm_sync *sync, uint32_t sftype)
+{
+	int rc = 0;
+	switch (sftype) {
+	case MSM_CAMERA_STROBE_FLASH_XENON:
+		if (sync->sdata->strobe_flash_data) {
+			msm_strobe_flash_xenon_fn_init(&sync->sfctrl);
+			rc = sync->sfctrl.strobe_flash_init(
+			sync->sdata->strobe_flash_data);
+		} else
+			return -ENODEV;
+		break;
+	default:
+		rc = -ENODEV;
+	}
+	return rc;
+}
+
+int msm_strobe_flash_ctrl(struct msm_camera_sensor_strobe_flash_data *sfdata,
+	struct strobe_flash_ctrl_data *strobe_ctrl)
+{
+	int rc = 0;
+	switch (strobe_ctrl->type) {
+	case STROBE_FLASH_CTRL_INIT:
+		if (!sfdata)
+			return -ENODEV;
+		rc = msm_strobe_flash_xenon_init(sfdata);
+		break;
+	case STROBE_FLASH_CTRL_CHARGE:
+		rc = msm_strobe_flash_xenon_charge(sfdata->flash_charge,
+			strobe_ctrl->charge_en,
+			sfdata->flash_recharge_duration);
+		break;
+	case STROBE_FLASH_CTRL_RELEASE:
+		if (sfdata)
+			rc = msm_strobe_flash_xenon_release(sfdata, 0);
+		break;
+	default:
+		pr_err("Invalid Strobe Flash State\n");
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+int msm_flash_ctrl(struct msm_camera_sensor_info *sdata,
+	struct flash_ctrl_data *flash_info)
+{
+	int rc = 0;
+	sensor_data = sdata;
+	switch (flash_info->flashtype) {
+	case LED_FLASH:
+		rc = msm_camera_flash_set_led_state(sdata->flash_data,
+			flash_info->ctrl_data.led_state);
+			break;
+	case STROBE_FLASH:
+		rc = msm_strobe_flash_ctrl(sdata->strobe_flash_data,
+			&(flash_info->ctrl_data.strobe_ctrl));
+		break;
+	default:
+		pr_err("Invalid Flash MODE\n");
+		rc = -EINVAL;
+	}
+	return rc;
+}
diff --git a/drivers/media/video/msm/gemini/Makefile b/drivers/media/video/msm/gemini/Makefile
new file mode 100644
index 0000000..8a7cd93
--- /dev/null
+++ b/drivers/media/video/msm/gemini/Makefile
@@ -0,0 +1,3 @@
+GCC_VERSION      := $(shell $(CONFIG_SHELL) $(PWD)/scripts/gcc-version.sh $(CROSS_COMPILE)gcc)
+EXTRA_CFLAGS += -Idrivers/media/video/msm
+obj-$(CONFIG_MSM_GEMINI) += msm_gemini_dev.o msm_gemini_sync.o msm_gemini_core.o msm_gemini_hw.o msm_gemini_platform.o
diff --git a/drivers/media/video/msm/gemini/msm_gemini_common.h b/drivers/media/video/msm/gemini/msm_gemini_common.h
new file mode 100644
index 0000000..0ddedc5
--- /dev/null
+++ b/drivers/media/video/msm/gemini/msm_gemini_common.h
@@ -0,0 +1,39 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MSM_GEMINI_COMMON_H
+#define MSM_GEMINI_COMMON_H
+
+#define MSM_GEMINI_DEBUG
+#ifdef MSM_GEMINI_DEBUG
+#define GMN_DBG(fmt, args...) pr_debug(fmt, ##args)
+#else
+#define GMN_DBG(fmt, args...) do { } while (0)
+#endif
+
+#define GMN_PR_ERR   pr_err
+
+enum GEMINI_MODE {
+	GEMINI_MODE_DISABLE,
+	GEMINI_MODE_OFFLINE,
+	GEMINI_MODE_REALTIME,
+	GEMINI_MODE_REALTIME_ROTATION
+};
+
+enum GEMINI_ROTATION {
+	GEMINI_ROTATION_0,
+	GEMINI_ROTATION_90,
+	GEMINI_ROTATION_180,
+	GEMINI_ROTATION_270
+};
+
+#endif /* MSM_GEMINI_COMMON_H */
diff --git a/drivers/media/video/msm/gemini/msm_gemini_core.c b/drivers/media/video/msm/gemini/msm_gemini_core.c
new file mode 100644
index 0000000..45d3937
--- /dev/null
+++ b/drivers/media/video/msm/gemini/msm_gemini_core.c
@@ -0,0 +1,250 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include "msm_gemini_hw.h"
+#include "msm_gemini_core.h"
+#include "msm_gemini_platform.h"
+#include "msm_gemini_common.h"
+
+static struct msm_gemini_hw_pingpong fe_pingpong_buf;
+static struct msm_gemini_hw_pingpong we_pingpong_buf;
+static int we_pingpong_index;
+static int reset_done_ack;
+static spinlock_t reset_lock;
+static wait_queue_head_t reset_wait;
+
+int msm_gemini_core_reset(uint8_t op_mode, void *base, int size)
+{
+	unsigned long flags;
+	int rc = 0;
+	int tm = 500; /*500ms*/
+	memset(&fe_pingpong_buf, 0, sizeof(fe_pingpong_buf));
+	fe_pingpong_buf.is_fe = 1;
+	we_pingpong_index = 0;
+	memset(&we_pingpong_buf, 0, sizeof(we_pingpong_buf));
+	spin_lock_irqsave(&reset_lock, flags);
+	reset_done_ack = 0;
+	msm_gemini_hw_reset(base, size);
+	spin_unlock_irqrestore(&reset_lock, flags);
+	rc = wait_event_interruptible_timeout(
+			reset_wait,
+			reset_done_ack,
+			msecs_to_jiffies(tm));
+
+	if (!reset_done_ack) {
+		GMN_DBG("%s: reset ACK failed %d", __func__, rc);
+		return -EBUSY;
+	}
+
+	GMN_DBG("%s: reset_done_ack rc %d", __func__, rc);
+	spin_lock_irqsave(&reset_lock, flags);
+	reset_done_ack = 0;
+	spin_unlock_irqrestore(&reset_lock, flags);
+
+	if (op_mode == MSM_GEMINI_MODE_REALTIME_ENCODE) {
+		/* Nothing needed for fe buffer cfg, config we only */
+		msm_gemini_hw_we_buffer_cfg(1);
+	} else {
+		/* Nothing needed for fe buffer cfg, config we only */
+		msm_gemini_hw_we_buffer_cfg(0);
+	}
+
+	/* @todo wait for reset done irq */
+
+	return 0;
+}
+
+void msm_gemini_core_release(int release_buf)
+{
+	int i = 0;
+	for (i = 0; i < 2; i++) {
+		if (we_pingpong_buf.buf_status[i] && release_buf)
+			msm_gemini_platform_p2v(we_pingpong_buf.buf[i].file,
+					&we_pingpong_buf.buf[i].handle);
+		we_pingpong_buf.buf_status[i] = 0;
+	}
+}
+
+void msm_gemini_core_init(void)
+{
+	init_waitqueue_head(&reset_wait);
+	spin_lock_init(&reset_lock);
+}
+
+int msm_gemini_core_fe_start(void)
+{
+	msm_gemini_hw_fe_start();
+	return 0;
+}
+
+/* fetch engine */
+int msm_gemini_core_fe_buf_update(struct msm_gemini_core_buf *buf)
+{
+	GMN_DBG("%s:%d] 0x%08x %d 0x%08x %d\n", __func__, __LINE__,
+		(int) buf->y_buffer_addr, buf->y_len,
+		(int) buf->cbcr_buffer_addr, buf->cbcr_len);
+	return msm_gemini_hw_pingpong_update(&fe_pingpong_buf, buf);
+}
+
+void *msm_gemini_core_fe_pingpong_irq(int gemini_irq_status, void *context)
+{
+	return msm_gemini_hw_pingpong_irq(&fe_pingpong_buf);
+}
+
+/* write engine */
+int msm_gemini_core_we_buf_update(struct msm_gemini_core_buf *buf)
+{
+	int rc;
+	GMN_DBG("%s:%d] 0x%08x 0x%08x %d\n", __func__, __LINE__,
+		(int) buf->y_buffer_addr, (int) buf->cbcr_buffer_addr,
+		buf->y_len);
+	we_pingpong_buf.buf_status[we_pingpong_index] = 0;
+	we_pingpong_index = (we_pingpong_index + 1)%2;
+	rc = msm_gemini_hw_pingpong_update(&we_pingpong_buf, buf);
+	return 0;
+}
+
+int msm_gemini_core_we_buf_reset(struct msm_gemini_hw_buf *buf)
+{
+	int i = 0;
+	for (i = 0; i < 2; i++) {
+		if (we_pingpong_buf.buf[i].y_buffer_addr
+					== buf->y_buffer_addr)
+			we_pingpong_buf.buf_status[i] = 0;
+	}
+	return 0;
+}
+
+void *msm_gemini_core_we_pingpong_irq(int gemini_irq_status, void *context)
+{
+	GMN_DBG("%s:%d]\n", __func__, __LINE__);
+
+	return msm_gemini_hw_pingpong_irq(&we_pingpong_buf);
+}
+
+void *msm_gemini_core_framedone_irq(int gemini_irq_status, void *context)
+{
+	struct msm_gemini_hw_buf *buf_p;
+
+	GMN_DBG("%s:%d]\n", __func__, __LINE__);
+
+	buf_p = msm_gemini_hw_pingpong_active_buffer(&we_pingpong_buf);
+	if (buf_p) {
+		buf_p->framedone_len = msm_gemini_hw_encode_output_size();
+		GMN_DBG("%s:%d] framedone_len %d\n", __func__, __LINE__,
+			buf_p->framedone_len);
+	}
+
+	return buf_p;
+}
+
+void *msm_gemini_core_reset_ack_irq(int gemini_irq_status, void *context)
+{
+	/* @todo return the status back to msm_gemini_core_reset */
+	GMN_DBG("%s:%d]\n", __func__, __LINE__);
+	return NULL;
+}
+
+void *msm_gemini_core_err_irq(int gemini_irq_status, void *context)
+{
+	GMN_PR_ERR("%s:%d]\n", __func__, gemini_irq_status);
+	return NULL;
+}
+
+static int (*msm_gemini_irq_handler) (int, void *, void *);
+
+irqreturn_t msm_gemini_core_irq(int irq_num, void *context)
+{
+	void *data = NULL;
+	unsigned long flags;
+	int gemini_irq_status;
+
+	GMN_DBG("%s:%d] irq_num = %d\n", __func__, __LINE__, irq_num);
+
+	spin_lock_irqsave(&reset_lock, flags);
+	reset_done_ack = 1;
+	spin_unlock_irqrestore(&reset_lock, flags);
+	gemini_irq_status = msm_gemini_hw_irq_get_status();
+
+	GMN_DBG("%s:%d] gemini_irq_status = %0x\n", __func__, __LINE__,
+		gemini_irq_status);
+
+	/*For reset and framedone IRQs, clear all bits*/
+	if (gemini_irq_status & 0x400) {
+		wake_up(&reset_wait);
+		msm_gemini_hw_irq_clear(HWIO_JPEG_IRQ_CLEAR_RMSK,
+			JPEG_IRQ_CLEAR_ALL);
+	} else if (gemini_irq_status & 0x1) {
+		msm_gemini_hw_irq_clear(HWIO_JPEG_IRQ_CLEAR_RMSK,
+			JPEG_IRQ_CLEAR_ALL);
+	} else {
+		msm_gemini_hw_irq_clear(HWIO_JPEG_IRQ_CLEAR_RMSK,
+			gemini_irq_status);
+	}
+
+	if (msm_gemini_hw_irq_is_frame_done(gemini_irq_status)) {
+		data = msm_gemini_core_framedone_irq(gemini_irq_status,
+			context);
+		if (msm_gemini_irq_handler)
+			msm_gemini_irq_handler(
+				MSM_GEMINI_HW_MASK_COMP_FRAMEDONE,
+				context, data);
+	}
+
+	if (msm_gemini_hw_irq_is_fe_pingpong(gemini_irq_status)) {
+		data = msm_gemini_core_fe_pingpong_irq(gemini_irq_status,
+			context);
+		if (msm_gemini_irq_handler)
+			msm_gemini_irq_handler(MSM_GEMINI_HW_MASK_COMP_FE,
+				context, data);
+	}
+
+	if (msm_gemini_hw_irq_is_we_pingpong(gemini_irq_status) &&
+	    !msm_gemini_hw_irq_is_frame_done(gemini_irq_status)) {
+		data = msm_gemini_core_we_pingpong_irq(gemini_irq_status,
+			context);
+		if (msm_gemini_irq_handler)
+			msm_gemini_irq_handler(MSM_GEMINI_HW_MASK_COMP_WE,
+				context, data);
+	}
+
+	if (msm_gemini_hw_irq_is_reset_ack(gemini_irq_status)) {
+		data = msm_gemini_core_reset_ack_irq(gemini_irq_status,
+			context);
+		if (msm_gemini_irq_handler)
+			msm_gemini_irq_handler(
+				MSM_GEMINI_HW_MASK_COMP_RESET_ACK,
+				context, data);
+	}
+
+	/* Unexpected/unintended HW interrupt */
+	if (msm_gemini_hw_irq_is_err(gemini_irq_status)) {
+		data = msm_gemini_core_err_irq(gemini_irq_status, context);
+		if (msm_gemini_irq_handler)
+			msm_gemini_irq_handler(MSM_GEMINI_HW_MASK_COMP_ERR,
+				context, data);
+	}
+
+	return IRQ_HANDLED;
+}
+
+void msm_gemini_core_irq_install(int (*irq_handler) (int, void *, void *))
+{
+	msm_gemini_irq_handler = irq_handler;
+}
+
+void msm_gemini_core_irq_remove(void)
+{
+	msm_gemini_irq_handler = NULL;
+}
diff --git a/drivers/media/video/msm/gemini/msm_gemini_core.h b/drivers/media/video/msm/gemini/msm_gemini_core.h
new file mode 100644
index 0000000..f240505
--- /dev/null
+++ b/drivers/media/video/msm/gemini/msm_gemini_core.h
@@ -0,0 +1,35 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MSM_GEMINI_CORE_H
+#define MSM_GEMINI_CORE_H
+
+#include <linux/interrupt.h>
+#include "msm_gemini_hw.h"
+
+#define msm_gemini_core_buf msm_gemini_hw_buf
+
+irqreturn_t msm_gemini_core_irq(int irq_num, void *context);
+
+void msm_gemini_core_irq_install(int (*irq_handler) (int, void *, void *));
+void msm_gemini_core_irq_remove(void);
+
+int msm_gemini_core_fe_buf_update(struct msm_gemini_core_buf *buf);
+int msm_gemini_core_we_buf_update(struct msm_gemini_core_buf *buf);
+int msm_gemini_core_we_buf_reset(struct msm_gemini_hw_buf *buf);
+
+int msm_gemini_core_reset(uint8_t op_mode, void *base, int size);
+int msm_gemini_core_fe_start(void);
+
+void msm_gemini_core_release(int);
+void msm_gemini_core_init(void);
+#endif /* MSM_GEMINI_CORE_H */
diff --git a/drivers/media/video/msm/gemini/msm_gemini_dev.c b/drivers/media/video/msm/gemini/msm_gemini_dev.c
new file mode 100644
index 0000000..01d45ed
--- /dev/null
+++ b/drivers/media/video/msm/gemini/msm_gemini_dev.c
@@ -0,0 +1,266 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <mach/board.h>
+
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/uaccess.h>
+#include <media/msm_gemini.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#include "msm.h"
+#include "msm_gemini_sync.h"
+#include "msm_gemini_common.h"
+
+#define MSM_GEMINI_NAME "gemini"
+
+static int msm_gemini_open(struct inode *inode, struct file *filp)
+{
+	int rc;
+
+	struct msm_gemini_device *pgmn_dev = container_of(inode->i_cdev,
+		struct msm_gemini_device, cdev);
+	filp->private_data = pgmn_dev;
+
+	GMN_DBG("%s:%d]\n", __func__, __LINE__);
+
+	rc = __msm_gemini_open(pgmn_dev);
+
+	GMN_DBG(KERN_INFO "%s:%d] %s open_count = %d\n", __func__, __LINE__,
+		filp->f_path.dentry->d_name.name, pgmn_dev->open_count);
+
+	return rc;
+}
+
+static int msm_gemini_release(struct inode *inode, struct file *filp)
+{
+	int rc;
+
+	struct msm_gemini_device *pgmn_dev = filp->private_data;
+
+	GMN_DBG(KERN_INFO "%s:%d]\n", __func__, __LINE__);
+
+	rc = __msm_gemini_release(pgmn_dev);
+
+	GMN_DBG(KERN_INFO "%s:%d] %s open_count = %d\n", __func__, __LINE__,
+		filp->f_path.dentry->d_name.name, pgmn_dev->open_count);
+	return rc;
+}
+
+static long msm_gemini_ioctl(struct file *filp, unsigned int cmd,
+	unsigned long arg)
+{
+	int rc;
+	struct msm_gemini_device *pgmn_dev = filp->private_data;
+
+	GMN_DBG("%s:%d] cmd=%d pgmn_dev=0x%x arg=0x%x\n", __func__,
+		__LINE__, _IOC_NR(cmd), (uint32_t)pgmn_dev, (uint32_t)arg);
+
+	rc = __msm_gemini_ioctl(pgmn_dev, cmd, arg);
+
+	GMN_DBG("%s:%d]\n", __func__, __LINE__);
+	return rc;
+}
+
+static const struct file_operations msm_gemini_fops = {
+	.owner	  = THIS_MODULE,
+	.open	   = msm_gemini_open,
+	.release	= msm_gemini_release,
+	.unlocked_ioctl = msm_gemini_ioctl,
+};
+
+static struct class *msm_gemini_class;
+static dev_t msm_gemini_devno;
+struct msm_gemini_device *msm_gemini_device_p;
+
+int msm_gemini_subdev_init(struct v4l2_subdev *gemini_sd)
+{
+	int rc;
+	struct msm_gemini_device *pgmn_dev =
+		(struct msm_gemini_device *)gemini_sd->host_priv;
+
+	GMN_DBG("%s:%d: gemini_sd=0x%x pgmn_dev=0x%x\n",
+		__func__, __LINE__, (uint32_t)gemini_sd, (uint32_t)pgmn_dev);
+	rc = __msm_gemini_open(pgmn_dev);
+	GMN_DBG("%s:%d: rc=%d\n",
+		__func__, __LINE__, rc);
+	return rc;
+}
+
+static long msm_gemini_subdev_ioctl(struct v4l2_subdev *sd,
+	unsigned int cmd, void *arg)
+{
+	long rc;
+	struct msm_gemini_device *pgmn_dev =
+		(struct msm_gemini_device *)sd->host_priv;
+
+	GMN_DBG("%s: cmd=%d\n", __func__, cmd);
+
+	GMN_DBG("%s: pgmn_dev 0x%x", __func__, (uint32_t)pgmn_dev);
+
+	GMN_DBG("%s: Calling __msm_gemini_ioctl\n", __func__);
+
+	rc = __msm_gemini_ioctl(pgmn_dev, cmd, (unsigned long)arg);
+	pr_debug("%s: X\n", __func__);
+	return rc;
+}
+
+void msm_gemini_subdev_release(struct v4l2_subdev *gemini_sd)
+{
+	int rc;
+	struct msm_gemini_device *pgmn_dev =
+		(struct msm_gemini_device *)gemini_sd->host_priv;
+	GMN_DBG("%s:pgmn_dev=0x%x", __func__, (uint32_t)pgmn_dev);
+	rc = __msm_gemini_release(pgmn_dev);
+	GMN_DBG("%s:rc=%d", __func__, rc);
+}
+
+static const struct v4l2_subdev_core_ops msm_gemini_subdev_core_ops = {
+	.ioctl = msm_gemini_subdev_ioctl,
+};
+
+static const struct v4l2_subdev_ops msm_gemini_subdev_ops = {
+	.core = &msm_gemini_subdev_core_ops,
+};
+
+static int msm_gemini_init(struct platform_device *pdev)
+{
+	int rc = -1;
+	struct device *dev;
+
+	GMN_DBG("%s:\n", __func__);
+	msm_gemini_device_p = __msm_gemini_init(pdev);
+	if (msm_gemini_device_p == NULL) {
+		GMN_PR_ERR("%s: initialization failed\n", __func__);
+		goto fail;
+	}
+
+	v4l2_subdev_init(&msm_gemini_device_p->subdev, &msm_gemini_subdev_ops);
+	v4l2_set_subdev_hostdata(&msm_gemini_device_p->subdev,
+		msm_gemini_device_p);
+	pr_debug("%s: msm_gemini_device_p 0x%x", __func__,
+			(uint32_t)msm_gemini_device_p);
+	GMN_DBG("%s:gemini: platform_set_drvdata\n", __func__);
+	platform_set_drvdata(pdev, &msm_gemini_device_p->subdev);
+
+	rc = alloc_chrdev_region(&msm_gemini_devno, 0, 1, MSM_GEMINI_NAME);
+	if (rc < 0) {
+		GMN_PR_ERR("%s: failed to allocate chrdev\n", __func__);
+		goto fail_1;
+	}
+
+	if (!msm_gemini_class) {
+		msm_gemini_class = class_create(THIS_MODULE, MSM_GEMINI_NAME);
+		if (IS_ERR(msm_gemini_class)) {
+			rc = PTR_ERR(msm_gemini_class);
+			GMN_PR_ERR("%s: create device class failed\n",
+				__func__);
+			goto fail_2;
+		}
+	}
+
+	dev = device_create(msm_gemini_class, NULL,
+		MKDEV(MAJOR(msm_gemini_devno), MINOR(msm_gemini_devno)), NULL,
+		"%s%d", MSM_GEMINI_NAME, 0);
+
+	if (IS_ERR(dev)) {
+		GMN_PR_ERR("%s: error creating device\n", __func__);
+		rc = -ENODEV;
+		goto fail_3;
+	}
+
+	cdev_init(&msm_gemini_device_p->cdev, &msm_gemini_fops);
+	msm_gemini_device_p->cdev.owner = THIS_MODULE;
+	msm_gemini_device_p->cdev.ops   =
+		(const struct file_operations *) &msm_gemini_fops;
+	rc = cdev_add(&msm_gemini_device_p->cdev, msm_gemini_devno, 1);
+	if (rc < 0) {
+		GMN_PR_ERR("%s: error adding cdev\n", __func__);
+		rc = -ENODEV;
+		goto fail_4;
+	}
+
+	GMN_DBG("%s %s: success\n", __func__, MSM_GEMINI_NAME);
+
+	return rc;
+
+fail_4:
+	device_destroy(msm_gemini_class, msm_gemini_devno);
+
+fail_3:
+	class_destroy(msm_gemini_class);
+
+fail_2:
+	unregister_chrdev_region(msm_gemini_devno, 1);
+
+fail_1:
+	__msm_gemini_exit(msm_gemini_device_p);
+
+fail:
+	return rc;
+}
+
+static void msm_gemini_exit(void)
+{
+	cdev_del(&msm_gemini_device_p->cdev);
+	device_destroy(msm_gemini_class, msm_gemini_devno);
+	class_destroy(msm_gemini_class);
+	unregister_chrdev_region(msm_gemini_devno, 1);
+
+	__msm_gemini_exit(msm_gemini_device_p);
+}
+
+static int __msm_gemini_probe(struct platform_device *pdev)
+{
+	return msm_gemini_init(pdev);
+}
+
+static int __msm_gemini_remove(struct platform_device *pdev)
+{
+	msm_gemini_exit();
+	return 0;
+}
+
+static struct platform_driver msm_gemini_driver = {
+	.probe  = __msm_gemini_probe,
+	.remove = __msm_gemini_remove,
+	.driver = {
+		.name = MSM_GEMINI_DRV_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init msm_gemini_driver_init(void)
+{
+	int rc;
+	rc = platform_driver_register(&msm_gemini_driver);
+	return rc;
+}
+
+static void __exit msm_gemini_driver_exit(void)
+{
+	platform_driver_unregister(&msm_gemini_driver);
+}
+
+MODULE_DESCRIPTION("msm gemini jpeg driver");
+MODULE_VERSION("msm gemini 0.1");
+
+module_init(msm_gemini_driver_init);
+module_exit(msm_gemini_driver_exit);
+
diff --git a/drivers/media/video/msm/gemini/msm_gemini_hw.c b/drivers/media/video/msm/gemini/msm_gemini_hw.c
new file mode 100644
index 0000000..ba8f353
--- /dev/null
+++ b/drivers/media/video/msm/gemini/msm_gemini_hw.c
@@ -0,0 +1,525 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include "msm_gemini_hw.h"
+#include "msm_gemini_common.h"
+
+#include <linux/io.h>
+
+static void *gemini_region_base;
+static uint32_t gemini_region_size;
+
+int msm_gemini_hw_pingpong_update(struct msm_gemini_hw_pingpong *pingpong_hw,
+	struct msm_gemini_hw_buf *buf)
+{
+	int buf_free_index = -1;
+
+	if (!pingpong_hw->buf_status[0]) {
+		buf_free_index = 0;
+	} else if (!pingpong_hw->buf_status[1]) {
+		buf_free_index = 1;
+	} else {
+		GMN_PR_ERR("%s:%d: pingpong buffer busy\n", __func__, __LINE__);
+		return -1;
+	}
+
+	pingpong_hw->buf[buf_free_index] = *buf;
+	pingpong_hw->buf_status[buf_free_index] = 1;
+
+	if (pingpong_hw->is_fe) {
+		/* it is fe */
+		msm_gemini_hw_fe_buffer_update(
+			&pingpong_hw->buf[buf_free_index], buf_free_index);
+	} else {
+		/* it is we */
+		msm_gemini_hw_we_buffer_update(
+			&pingpong_hw->buf[buf_free_index], buf_free_index);
+	}
+	return 0;
+}
+
+void *msm_gemini_hw_pingpong_irq(struct msm_gemini_hw_pingpong *pingpong_hw)
+{
+	struct msm_gemini_hw_buf *buf_p = NULL;
+
+	if (pingpong_hw->buf_status[pingpong_hw->buf_active_index]) {
+		buf_p = &pingpong_hw->buf[pingpong_hw->buf_active_index];
+		pingpong_hw->buf_status[pingpong_hw->buf_active_index] = 0;
+	}
+
+	pingpong_hw->buf_active_index = !pingpong_hw->buf_active_index;
+
+	return (void *) buf_p;
+}
+
+void *msm_gemini_hw_pingpong_active_buffer(
+	struct msm_gemini_hw_pingpong *pingpong_hw)
+{
+	struct msm_gemini_hw_buf *buf_p = NULL;
+
+	if (pingpong_hw->buf_status[pingpong_hw->buf_active_index])
+		buf_p = &pingpong_hw->buf[pingpong_hw->buf_active_index];
+
+	return (void *) buf_p;
+}
+
+struct msm_gemini_hw_cmd hw_cmd_irq_get_status[] = {
+	/* type, repeat n times, offset, mask, data or pdata */
+	{MSM_GEMINI_HW_CMD_TYPE_READ, 1, HWIO_JPEG_IRQ_STATUS_ADDR,
+		HWIO_JPEG_IRQ_STATUS_RMSK, {0} },
+};
+
+int msm_gemini_hw_irq_get_status(void)
+{
+	uint32_t n_irq_status = 0;
+	rmb();
+	n_irq_status = msm_gemini_hw_read(&hw_cmd_irq_get_status[0]);
+	rmb();
+	return n_irq_status;
+}
+
+struct msm_gemini_hw_cmd hw_cmd_encode_output_size[] = {
+	/* type, repeat n times, offset, mask, data or pdata */
+	{MSM_GEMINI_HW_CMD_TYPE_READ, 1,
+		HWIO_JPEG_STATUS_ENCODE_OUTPUT_SIZE_ADDR,
+		HWIO_JPEG_STATUS_ENCODE_OUTPUT_SIZE_RMSK, {0} },
+};
+
+long msm_gemini_hw_encode_output_size(void)
+{
+	uint32_t encode_output_size = 0;
+
+	encode_output_size = msm_gemini_hw_read(&hw_cmd_encode_output_size[0]);
+
+	return encode_output_size;
+}
+
+struct msm_gemini_hw_cmd hw_cmd_irq_clear[] = {
+	/* type, repeat n times, offset, mask, data or pdata */
+	{MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_IRQ_CLEAR_ADDR,
+		HWIO_JPEG_IRQ_CLEAR_RMSK, {JPEG_IRQ_CLEAR_ALL} },
+};
+
+void msm_gemini_hw_irq_clear(uint32_t mask, uint32_t data)
+{
+	GMN_DBG("%s:%d] mask %0x data %0x", __func__, __LINE__, mask, data);
+	hw_cmd_irq_clear[0].mask = mask;
+	hw_cmd_irq_clear[0].data = data;
+	msm_gemini_hw_write(&hw_cmd_irq_clear[0]);
+}
+
+struct msm_gemini_hw_cmd hw_cmd_fe_ping_update[] = {
+	/* type, repeat n times, offset, mask, data or pdata */
+	{MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_BUFFER_CFG_ADDR,
+		HWIO_JPEG_FE_BUFFER_CFG_RMSK, {0} },
+	{MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_Y_PING_ADDR_ADDR,
+		HWIO_JPEG_FE_Y_PING_ADDR_RMSK, {0} },
+	{MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_CBCR_PING_ADDR_ADDR,
+		HWIO_JPEG_FE_CBCR_PING_ADDR_RMSK, {0} },
+	{MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_CMD_ADDR,
+		HWIO_JPEG_FE_CMD_RMSK, {JPEG_FE_CMD_BUFFERRELOAD} },
+};
+
+struct msm_gemini_hw_cmd hw_cmd_fe_pong_update[] = {
+	/* type, repeat n times, offset, mask, data or pdata */
+	{MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_BUFFER_CFG_ADDR,
+		HWIO_JPEG_FE_BUFFER_CFG_RMSK, {0} },
+	{MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_Y_PONG_ADDR_ADDR,
+		HWIO_JPEG_FE_Y_PONG_ADDR_RMSK, {0} },
+	{MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_CBCR_PONG_ADDR_ADDR,
+		HWIO_JPEG_FE_CBCR_PONG_ADDR_RMSK, {0} },
+	{MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_CMD_ADDR,
+		HWIO_JPEG_FE_CMD_RMSK, {JPEG_FE_CMD_BUFFERRELOAD} },
+};
+
+void msm_gemini_hw_fe_buffer_update(struct msm_gemini_hw_buf *p_input,
+	uint8_t pingpong_index)
+{
+	uint32_t n_reg_val = 0;
+
+	struct msm_gemini_hw_cmd *hw_cmd_p;
+
+	if (pingpong_index == 0) {
+		hw_cmd_p = &hw_cmd_fe_ping_update[0];
+		n_reg_val = ((((p_input->num_of_mcu_rows - 1) <<
+			HWIO_JPEG_FE_BUFFER_CFG_CBCR_MCU_ROWS_SHFT) &
+			HWIO_JPEG_FE_BUFFER_CFG_CBCR_MCU_ROWS_BMSK) |
+			(((p_input->num_of_mcu_rows - 1) <<
+			HWIO_JPEG_FE_BUFFER_CFG_Y_MCU_ROWS_SHFT) &
+			HWIO_JPEG_FE_BUFFER_CFG_Y_MCU_ROWS_BMSK));
+		hw_cmd_p->data = n_reg_val;
+		msm_gemini_hw_write(hw_cmd_p++);
+
+		n_reg_val = ((p_input->y_buffer_addr <<
+			HWIO_JPEG_FE_Y_PING_ADDR_FE_Y_PING_START_ADDR_SHFT) &
+			HWIO_JPEG_FE_Y_PING_ADDR_FE_Y_PING_START_ADDR_BMSK);
+		hw_cmd_p->data = n_reg_val;
+		msm_gemini_hw_write(hw_cmd_p++);
+
+		n_reg_val = ((p_input->cbcr_buffer_addr<<
+		HWIO_JPEG_FE_CBCR_PING_ADDR_FE_CBCR_PING_START_ADDR_SHFT) &
+		HWIO_JPEG_FE_CBCR_PING_ADDR_FE_CBCR_PING_START_ADDR_BMSK);
+		hw_cmd_p->data = n_reg_val;
+		msm_gemini_hw_write(hw_cmd_p++);
+
+		msm_gemini_hw_write(hw_cmd_p);
+	} else if (pingpong_index == 1) {
+		hw_cmd_p = &hw_cmd_fe_pong_update[0];
+		n_reg_val = ((((p_input->num_of_mcu_rows - 1) <<
+			HWIO_JPEG_FE_BUFFER_CFG_CBCR_MCU_ROWS_SHFT) &
+			HWIO_JPEG_FE_BUFFER_CFG_CBCR_MCU_ROWS_BMSK) |
+			(((p_input->num_of_mcu_rows - 1) <<
+			HWIO_JPEG_FE_BUFFER_CFG_Y_MCU_ROWS_SHFT) &
+			HWIO_JPEG_FE_BUFFER_CFG_Y_MCU_ROWS_BMSK));
+		hw_cmd_p->data = n_reg_val;
+		msm_gemini_hw_write(hw_cmd_p++);
+
+		n_reg_val = ((p_input->y_buffer_addr <<
+			HWIO_JPEG_FE_Y_PONG_ADDR_FE_Y_PONG_START_ADDR_SHFT) &
+			HWIO_JPEG_FE_Y_PONG_ADDR_FE_Y_PONG_START_ADDR_BMSK);
+		hw_cmd_p->data = n_reg_val;
+		msm_gemini_hw_write(hw_cmd_p++);
+
+		n_reg_val = ((p_input->cbcr_buffer_addr<<
+		HWIO_JPEG_FE_CBCR_PONG_ADDR_FE_CBCR_PONG_START_ADDR_SHFT) &
+		HWIO_JPEG_FE_CBCR_PONG_ADDR_FE_CBCR_PONG_START_ADDR_BMSK);
+		hw_cmd_p->data = n_reg_val;
+		msm_gemini_hw_write(hw_cmd_p++);
+
+		msm_gemini_hw_write(hw_cmd_p);
+	} else {
+		/* shall not get to here */
+	}
+
+	return;
+}
+
+struct msm_gemini_hw_cmd hw_cmd_fe_start[] = {
+	/* type, repeat n times, offset, mask, data or pdata */
+	{MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_CMD_ADDR,
+		HWIO_JPEG_FE_CMD_RMSK, {JPEG_OFFLINE_CMD_START} },
+};
+
+void msm_gemini_hw_fe_start(void)
+{
+	msm_gemini_hw_write(&hw_cmd_fe_start[0]);
+
+	return;
+}
+
+struct msm_gemini_hw_cmd hw_cmd_we_buffer_cfg[] = {
+	/* type, repeat n times, offset, mask, data or pdata */
+	{MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_WE_Y_THRESHOLD_ADDR,
+		HWIO_JPEG_WE_Y_THRESHOLD_RMSK, {0} },
+	{MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_WE_Y_UB_CFG_ADDR,
+		HWIO_JPEG_WE_Y_UB_CFG_RMSK, {JPEG_WE_YUB_ENCODE} },
+	{MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_WE_CBCR_THRESHOLD_ADDR,
+		HWIO_JPEG_WE_CBCR_THRESHOLD_RMSK, {0} },
+};
+
+/* first dimension is WE_ASSERT_STALL_TH and WE_DEASSERT_STALL_TH
+   second dimension is for offline and real-time settings
+ */
+static const uint32_t GEMINI_WE_Y_THRESHOLD[2][2] = {
+	{ 0x00000190, 0x000001ff },
+	{ 0x0000016a, 0x000001ff }
+};
+
+/* first dimension is WE_ASSERT_STALL_TH and WE_DEASSERT_STALL_TH
+   second dimension is for offline and real-time settings
+ */
+static const uint32_t GEMINI_WE_CBCR_THRESHOLD[2][2] = {
+	{ 0x00000190, 0x000001ff },
+	{ 0x0000016a, 0x000001ff }
+};
+
+void msm_gemini_hw_we_buffer_cfg(uint8_t is_realtime)
+{
+	uint32_t              n_reg_val = 0;
+
+	struct msm_gemini_hw_cmd *hw_cmd_p = &hw_cmd_we_buffer_cfg[0];
+
+	n_reg_val = (((GEMINI_WE_Y_THRESHOLD[1][is_realtime] <<
+		HWIO_JPEG_WE_Y_THRESHOLD_WE_DEASSERT_STALL_TH_SHFT) &
+		HWIO_JPEG_WE_Y_THRESHOLD_WE_DEASSERT_STALL_TH_BMSK) |
+		((GEMINI_WE_Y_THRESHOLD[0][is_realtime] <<
+		HWIO_JPEG_WE_Y_THRESHOLD_WE_ASSERT_STALL_TH_SHFT) &
+		HWIO_JPEG_WE_Y_THRESHOLD_WE_ASSERT_STALL_TH_BMSK));
+	hw_cmd_p->data = n_reg_val;
+	msm_gemini_hw_write(hw_cmd_p++);
+
+	msm_gemini_hw_write(hw_cmd_p++);
+
+	/* @todo maybe not for realtime? */
+	n_reg_val = (((GEMINI_WE_CBCR_THRESHOLD[1][is_realtime] <<
+		HWIO_JPEG_WE_CBCR_THRESHOLD_WE_DEASSERT_STALL_TH_SHFT) &
+		HWIO_JPEG_WE_CBCR_THRESHOLD_WE_DEASSERT_STALL_TH_BMSK) |
+		((GEMINI_WE_CBCR_THRESHOLD[0][is_realtime] <<
+		HWIO_JPEG_WE_CBCR_THRESHOLD_WE_ASSERT_STALL_TH_SHFT) &
+		HWIO_JPEG_WE_CBCR_THRESHOLD_WE_ASSERT_STALL_TH_BMSK));
+	hw_cmd_p->data = n_reg_val;
+	msm_gemini_hw_write(hw_cmd_p);
+
+	return;
+}
+
+struct msm_gemini_hw_cmd hw_cmd_we_ping_update[] = {
+	/* type, repeat n times, offset, mask, data or pdata */
+	{MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_WE_Y_PING_BUFFER_CFG_ADDR,
+		HWIO_JPEG_WE_Y_PING_BUFFER_CFG_RMSK, {0} },
+	{MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_WE_Y_PING_ADDR_ADDR,
+		HWIO_JPEG_WE_Y_PING_ADDR_RMSK, {0} },
+};
+
+struct msm_gemini_hw_cmd hw_cmd_we_pong_update[] = {
+	/* type, repeat n times, offset, mask, data or pdata */
+	{MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_WE_Y_PONG_BUFFER_CFG_ADDR,
+		HWIO_JPEG_WE_Y_PONG_BUFFER_CFG_RMSK, {0} },
+	{MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_WE_Y_PONG_ADDR_ADDR,
+		HWIO_JPEG_WE_Y_PONG_ADDR_RMSK, {0} },
+};
+
+void msm_gemini_hw_we_buffer_update(struct msm_gemini_hw_buf *p_input,
+	uint8_t pingpong_index)
+{
+	uint32_t n_reg_val = 0;
+
+	struct msm_gemini_hw_cmd *hw_cmd_p;
+
+	if (pingpong_index == 0) {
+		hw_cmd_p = &hw_cmd_we_ping_update[0];
+
+		n_reg_val = ((p_input->y_len <<
+			HWIO_JPEG_WE_Y_PING_BUFFER_CFG_WE_BUFFER_LENGTH_SHFT) &
+			HWIO_JPEG_WE_Y_PING_BUFFER_CFG_WE_BUFFER_LENGTH_BMSK);
+		hw_cmd_p->data = n_reg_val;
+		msm_gemini_hw_write(hw_cmd_p++);
+
+		n_reg_val = p_input->y_buffer_addr;
+		hw_cmd_p->data = n_reg_val;
+		msm_gemini_hw_write(hw_cmd_p++);
+	} else if (pingpong_index == 1) {
+		hw_cmd_p = &hw_cmd_we_pong_update[0];
+
+		n_reg_val = ((p_input->y_len <<
+			HWIO_JPEG_WE_Y_PONG_BUFFER_CFG_WE_BUFFER_LENGTH_SHFT) &
+			HWIO_JPEG_WE_Y_PONG_BUFFER_CFG_WE_BUFFER_LENGTH_BMSK);
+		hw_cmd_p->data = n_reg_val;
+		msm_gemini_hw_write(hw_cmd_p++);
+
+		n_reg_val = p_input->y_buffer_addr;
+		hw_cmd_p->data = n_reg_val;
+		msm_gemini_hw_write(hw_cmd_p++);
+	} else {
+		/* shall not get to here */
+	}
+
+	return;
+}
+
+struct msm_gemini_hw_cmd hw_cmd_reset[] = {
+	/* type, repeat n times, offset, mask, data or pdata */
+	{MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_IRQ_MASK_ADDR,
+		HWIO_JPEG_IRQ_MASK_RMSK, {JPEG_IRQ_DISABLE_ALL} },
+	{MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_IRQ_CLEAR_ADDR,
+		HWIO_JPEG_IRQ_MASK_RMSK, {JPEG_IRQ_CLEAR_ALL} },
+	{MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_IRQ_MASK_ADDR,
+		HWIO_JPEG_IRQ_MASK_RMSK, {JPEG_IRQ_ALLSOURCES_ENABLE} },
+	{MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_RESET_CMD_ADDR,
+		HWIO_JPEG_RESET_CMD_RMSK, {JPEG_RESET_DEFAULT} },
+};
+
+void msm_gemini_hw_init(void *base, int size)
+{
+	gemini_region_base = base;
+	gemini_region_size = size;
+}
+
+void msm_gemini_hw_reset(void *base, int size)
+{
+	struct msm_gemini_hw_cmd *hw_cmd_p;
+
+	hw_cmd_p = &hw_cmd_reset[0];
+
+	wmb();
+	msm_gemini_hw_write(hw_cmd_p++);
+	msm_gemini_hw_write(hw_cmd_p++);
+	msm_gemini_hw_write(hw_cmd_p++);
+	msm_gemini_hw_write(hw_cmd_p);
+	wmb();
+
+	return;
+}
+
+uint32_t msm_gemini_hw_read(struct msm_gemini_hw_cmd *hw_cmd_p)
+{
+	uint32_t *paddr;
+	uint32_t data;
+
+	paddr = gemini_region_base + hw_cmd_p->offset;
+
+	data = readl(paddr);
+	data &= hw_cmd_p->mask;
+
+	GMN_DBG("%s:%d] type-%d n-%d offset-0x%4x mask-0x%8x data-0x%8x\n",
+		__func__, __LINE__, hw_cmd_p->type, hw_cmd_p->n,
+		hw_cmd_p->offset, hw_cmd_p->mask, data);
+	return data;
+}
+
+void msm_gemini_hw_write(struct msm_gemini_hw_cmd *hw_cmd_p)
+{
+	uint32_t *paddr;
+	uint32_t old_data, new_data;
+
+	/* type, repeat n times, offset, mask, data or pdata */
+	GMN_DBG("%s:%d] type-%d n-%d offset-0x%4x mask-0x%8x data-0x%8x\n",
+		__func__, __LINE__, hw_cmd_p->type, hw_cmd_p->n,
+		hw_cmd_p->offset, hw_cmd_p->mask, hw_cmd_p->data);
+
+	paddr = gemini_region_base + hw_cmd_p->offset;
+
+	if (hw_cmd_p->mask == 0xffffffff) {
+		old_data = 0;
+	} else {
+		old_data = readl(paddr);
+		old_data &= ~hw_cmd_p->mask;
+	}
+
+	new_data = hw_cmd_p->data & hw_cmd_p->mask;
+	new_data |= old_data;
+	writel(new_data, paddr);
+}
+
+int msm_gemini_hw_wait(struct msm_gemini_hw_cmd *hw_cmd_p, int m_us)
+{
+	int tm = hw_cmd_p->n;
+	uint32_t data;
+	uint32_t wait_data = hw_cmd_p->data & hw_cmd_p->mask;
+
+	data = msm_gemini_hw_read(hw_cmd_p);
+	if (data != wait_data) {
+		while (tm) {
+			udelay(m_us);
+			data = msm_gemini_hw_read(hw_cmd_p);
+			if (data == wait_data)
+				break;
+			tm--;
+		}
+	}
+	hw_cmd_p->data = data;
+	return tm;
+}
+
+void msm_gemini_hw_delay(struct msm_gemini_hw_cmd *hw_cmd_p, int m_us)
+{
+	int tm = hw_cmd_p->n;
+	while (tm) {
+		udelay(m_us);
+		tm--;
+	}
+}
+
+int msm_gemini_hw_exec_cmds(struct msm_gemini_hw_cmd *hw_cmd_p, int m_cmds)
+{
+	int is_copy_to_user = -1;
+	uint32_t data;
+
+	while (m_cmds--) {
+		if (hw_cmd_p->offset > gemini_region_size) {
+			GMN_PR_ERR("%s:%d] %d exceed hw region %d\n", __func__,
+				__LINE__, hw_cmd_p->offset, gemini_region_size);
+			return -EFAULT;
+		}
+
+		switch (hw_cmd_p->type) {
+		case MSM_GEMINI_HW_CMD_TYPE_READ:
+			hw_cmd_p->data = msm_gemini_hw_read(hw_cmd_p);
+			is_copy_to_user = 1;
+			break;
+
+		case MSM_GEMINI_HW_CMD_TYPE_WRITE:
+			msm_gemini_hw_write(hw_cmd_p);
+			break;
+
+		case MSM_GEMINI_HW_CMD_TYPE_WRITE_OR:
+			data = msm_gemini_hw_read(hw_cmd_p);
+			hw_cmd_p->data = (hw_cmd_p->data & hw_cmd_p->mask) |
+				data;
+			msm_gemini_hw_write(hw_cmd_p);
+			break;
+
+		case MSM_GEMINI_HW_CMD_TYPE_UWAIT:
+			msm_gemini_hw_wait(hw_cmd_p, 1);
+			break;
+
+		case MSM_GEMINI_HW_CMD_TYPE_MWAIT:
+			msm_gemini_hw_wait(hw_cmd_p, 1000);
+			break;
+
+		case MSM_GEMINI_HW_CMD_TYPE_UDELAY:
+			msm_gemini_hw_delay(hw_cmd_p, 1);
+			break;
+
+		case MSM_GEMINI_HW_CMD_TYPE_MDELAY:
+			msm_gemini_hw_delay(hw_cmd_p, 1000);
+			break;
+
+		default:
+			GMN_PR_ERR("wrong hw command type\n");
+			break;
+		}
+
+		hw_cmd_p++;
+	}
+	return is_copy_to_user;
+}
+
+void msm_gemini_hw_region_dump(int size)
+{
+	uint32_t *p;
+	uint8_t *p8;
+
+	if (size > gemini_region_size)
+		GMN_PR_ERR("%s:%d] wrong region dump size\n",
+			__func__, __LINE__);
+
+	p = (uint32_t *) gemini_region_base;
+	while (size >= 16) {
+		GMN_DBG("0x%08X] %08X %08X %08X %08X\n",
+			gemini_region_size - size,
+			readl(p), readl(p+1), readl(p+2), readl(p+3));
+		p += 4;
+		size -= 16;
+	}
+
+	if (size > 0) {
+		uint32_t d;
+		GMN_DBG("0x%08X] ", gemini_region_size - size);
+		while (size >= 4) {
+			GMN_DBG("%08X ", readl(p++));
+			size -= 4;
+		}
+
+		d = readl(p);
+		p8 = (uint8_t *) &d;
+		while (size) {
+			GMN_DBG("%02X", *p8++);
+			size--;
+		}
+
+		GMN_DBG("\n");
+	}
+}
+
diff --git a/drivers/media/video/msm/gemini/msm_gemini_hw.h b/drivers/media/video/msm/gemini/msm_gemini_hw.h
new file mode 100644
index 0000000..233f082
--- /dev/null
+++ b/drivers/media/video/msm/gemini/msm_gemini_hw.h
@@ -0,0 +1,104 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MSM_GEMINI_HW_H
+#define MSM_GEMINI_HW_H
+
+#include <media/msm_gemini.h>
+#include "msm_gemini_hw_reg.h"
+#include <linux/ion.h>
+#include <mach/iommu_domains.h>
+
+struct msm_gemini_hw_buf {
+	struct msm_gemini_buf vbuf;
+	struct file  *file;
+	uint32_t framedone_len;
+	uint32_t y_buffer_addr;
+	uint32_t y_len;
+	uint32_t cbcr_buffer_addr;
+	uint32_t cbcr_len;
+	uint32_t num_of_mcu_rows;
+	struct ion_handle *handle;
+};
+
+struct msm_gemini_hw_pingpong {
+	uint8_t is_fe; /* 1: fe; 0: we */
+	struct  msm_gemini_hw_buf buf[2];
+	int     buf_status[2];
+	int     buf_active_index;
+};
+
+int msm_gemini_hw_pingpong_update(struct msm_gemini_hw_pingpong *pingpong_hw,
+	struct msm_gemini_hw_buf *buf);
+void *msm_gemini_hw_pingpong_irq(struct msm_gemini_hw_pingpong *pingpong_hw);
+void *msm_gemini_hw_pingpong_active_buffer(struct msm_gemini_hw_pingpong
+	*pingpong_hw);
+
+void msm_gemini_hw_irq_clear(uint32_t, uint32_t);
+int msm_gemini_hw_irq_get_status(void);
+long msm_gemini_hw_encode_output_size(void);
+#define MSM_GEMINI_HW_MASK_COMP_FRAMEDONE \
+		MSM_GEMINI_HW_IRQ_STATUS_FRAMEDONE_MASK
+#define MSM_GEMINI_HW_MASK_COMP_FE \
+		MSM_GEMINI_HW_IRQ_STATUS_FE_RD_DONE_MASK
+#define MSM_GEMINI_HW_MASK_COMP_WE \
+		(MSM_GEMINI_HW_IRQ_STATUS_WE_Y_PINGPONG_MASK | \
+		 MSM_GEMINI_HW_IRQ_STATUS_WE_CBCR_PINGPONG_MASK)
+#define MSM_GEMINI_HW_MASK_COMP_RESET_ACK \
+		MSM_GEMINI_HW_IRQ_STATUS_RESET_ACK_MASK
+#define MSM_GEMINI_HW_MASK_COMP_ERR \
+		(MSM_GEMINI_HW_IRQ_STATUS_FE_RTOVF_MASK | \
+		MSM_GEMINI_HW_IRQ_STATUS_FE_VFE_OVERFLOW_MASK | \
+		MSM_GEMINI_HW_IRQ_STATUS_WE_Y_BUFFER_OVERFLOW_MASK | \
+		MSM_GEMINI_HW_IRQ_STATUS_WE_CBCR_BUFFER_OVERFLOW_MASK | \
+		MSM_GEMINI_HW_IRQ_STATUS_WE_CH0_DATAFIFO_OVERFLOW_MASK | \
+		MSM_GEMINI_HW_IRQ_STATUS_WE_CH1_DATAFIFO_OVERFLOW_MASK | \
+		MSM_GEMINI_HW_IRQ_STATUS_BUS_ERROR_MASK | \
+		MSM_GEMINI_HW_IRQ_STATUS_VIOLATION_MASK)
+
+#define msm_gemini_hw_irq_is_frame_done(gemini_irq_status) \
+	(gemini_irq_status & MSM_GEMINI_HW_MASK_COMP_FRAMEDONE)
+#define msm_gemini_hw_irq_is_fe_pingpong(gemini_irq_status) \
+	(gemini_irq_status & MSM_GEMINI_HW_MASK_COMP_FE)
+#define msm_gemini_hw_irq_is_we_pingpong(gemini_irq_status) \
+	(gemini_irq_status & MSM_GEMINI_HW_MASK_COMP_WE)
+#define msm_gemini_hw_irq_is_reset_ack(gemini_irq_status) \
+	(gemini_irq_status & MSM_GEMINI_HW_MASK_COMP_RESET_ACK)
+#define msm_gemini_hw_irq_is_err(gemini_irq_status) \
+	(gemini_irq_status & MSM_GEMINI_HW_MASK_COMP_ERR)
+
+void msm_gemini_hw_fe_buffer_update(struct msm_gemini_hw_buf *p_input,
+	uint8_t pingpong_index);
+void msm_gemini_hw_we_buffer_update(struct msm_gemini_hw_buf *p_input,
+	uint8_t pingpong_index);
+
+void msm_gemini_hw_we_buffer_cfg(uint8_t is_realtime);
+
+void msm_gemini_hw_fe_start(void);
+void msm_gemini_hw_clk_cfg(void);
+
+void msm_gemini_hw_reset(void *base, int size);
+void msm_gemini_hw_irq_cfg(void);
+void msm_gemini_hw_init(void *base, int size);
+
+uint32_t msm_gemini_hw_read(struct msm_gemini_hw_cmd *hw_cmd_p);
+void msm_gemini_hw_write(struct msm_gemini_hw_cmd *hw_cmd_p);
+int msm_gemini_hw_wait(struct msm_gemini_hw_cmd *hw_cmd_p, int m_us);
+void msm_gemini_hw_delay(struct msm_gemini_hw_cmd *hw_cmd_p, int m_us);
+int msm_gemini_hw_exec_cmds(struct msm_gemini_hw_cmd *hw_cmd_p, int m_cmds);
+void msm_gemini_hw_region_dump(int size);
+
+#define MSM_GEMINI_PIPELINE_CLK_128MHZ 128 /* 8MP  128MHz */
+#define MSM_GEMINI_PIPELINE_CLK_140MHZ 140 /* 9MP  140MHz */
+#define MSM_GEMINI_PIPELINE_CLK_200MHZ 153 /* 12MP 153MHz */
+
+#endif /* MSM_GEMINI_HW_H */
diff --git a/drivers/media/video/msm/gemini/msm_gemini_hw_reg.h b/drivers/media/video/msm/gemini/msm_gemini_hw_reg.h
new file mode 100644
index 0000000..4bddfbb
--- /dev/null
+++ b/drivers/media/video/msm/gemini/msm_gemini_hw_reg.h
@@ -0,0 +1,176 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MSM_GEMINI_HW_REG_H
+#define MSM_GEMINI_HW_REG_H
+
+#define GEMINI_REG_BASE 0
+
+#define MSM_GEMINI_HW_IRQ_MASK_ADDR 0x00000014
+#define MSM_GEMINI_HW_IRQ_MASK_RMSK 0xffffffff
+#define MSM_GEMINI_HW_IRQ_MASK_SHFT 0
+#define MSM_GEMINI_HW_IRQ_DISABLE 0
+#define MSM_GEMINI_HW_IRQ_ENABLE 0xffffffff
+
+#define MSM_GEMINI_HW_IRQ_CLEAR_ADDR 0x00000018
+#define MSM_GEMINI_HW_IRQ_CLEAR_RMSK 0xffffffff
+#define MSM_GEMINI_HW_IRQ_CLEAR_SHFT 0
+#define MSM_GEMINI_HW_IRQ_CLEAR  0xffffffff
+
+#define MSM_GEMINI_HW_IRQ_STATUS_FRAMEDONE_MASK 0x00000001
+#define MSM_GEMINI_HW_IRQ_STATUS_FRAMEDONE_SHIFT 0x00000000
+
+#define MSM_GEMINI_HW_IRQ_STATUS_FE_RD_DONE_MASK 0x00000002
+#define MSM_GEMINI_HW_IRQ_STATUS_FE_RD_DONE_SHIFT 0x00000001
+
+#define MSM_GEMINI_HW_IRQ_STATUS_FE_RTOVF_MASK 0x00000004
+#define MSM_GEMINI_HW_IRQ_STATUS_FE_RTOVF_SHIFT 0x00000002
+
+#define MSM_GEMINI_HW_IRQ_STATUS_FE_VFE_OVERFLOW_MASK 0x00000008
+#define MSM_GEMINI_HW_IRQ_STATUS_FE_VFE_OVERFLOW_SHIFT 0x00000003
+
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_Y_PINGPONG_MASK 0x00000010
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_Y_PINGPONG_SHIFT 0x00000004
+
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_CBCR_PINGPONG_MASK 0x00000020
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_CBCR_PINGPONG_SHIFT 0x00000005
+
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_Y_BUFFER_OVERFLOW_MASK 0x00000040
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_Y_BUFFER_OVERFLOW_SHIFT 0x00000006
+
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_CBCR_BUFFER_OVERFLOW_MASK 0x00000080
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_CBCR_BUFFER_OVERFLOW_SHIFT 0x00000007
+
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_CH0_DATAFIFO_OVERFLOW_MASK 0x00000100
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_CH0_DATAFIFO_OVERFLOW_SHIFT 0x00000008
+
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_CH1_DATAFIFO_OVERFLOW_MASK 0x00000200
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_CH1_DATAFIFO_OVERFLOW_SHIFT 0x00000009
+
+#define MSM_GEMINI_HW_IRQ_STATUS_RESET_ACK_MASK 0x00000400
+#define MSM_GEMINI_HW_IRQ_STATUS_RESET_ACK_SHIFT 0x0000000a
+
+#define MSM_GEMINI_HW_IRQ_STATUS_BUS_ERROR_MASK 0x00000800
+#define MSM_GEMINI_HW_IRQ_STATUS_BUS_ERROR_SHIFT 0x0000000b
+
+#define MSM_GEMINI_HW_IRQ_STATUS_VIOLATION_MASK 0x00001000
+#define MSM_GEMINI_HW_IRQ_STATUS_VIOLATION_SHIFT 0x0000000c
+
+#define JPEG_BUS_CMD_HALT_REQ 0x00000001
+
+#define JPEG_REALTIME_CMD_STOP_FB 0x00000000
+#define JPEG_REALTIME_CMD_STOP_IM 0x00000003
+#define JPEG_REALTIME_CMD_START 0x00000001
+
+#define JPEG_OFFLINE_CMD_START 0x00000003
+
+#define JPEG_DMI_CFG_DISABLE 0x00000000
+#define JPEG_DMI_ADDR_START 0x00000000
+
+#define JPEG_FE_CMD_BUFFERRELOAD 0x00000001
+
+#define JPEG_WE_YUB_ENCODE 0x01ff0000
+
+#define JPEG_RESET_DEFAULT 0x0004ffff /* cfff? */
+
+#define JPEG_IRQ_DISABLE_ALL 0x00000000
+#define JPEG_IRQ_CLEAR_ALL 0xffffffff
+#define JPEG_IRQ_ALLSOURCES_ENABLE 0xffffffff
+
+#define HWIO_JPEG_FE_BUFFER_CFG_ADDR (GEMINI_REG_BASE + 0x00000080)
+#define HWIO_JPEG_FE_BUFFER_CFG_RMSK 0x1fff1fff
+
+#define HWIO_JPEG_FE_Y_PING_ADDR_ADDR (GEMINI_REG_BASE + 0x00000084)
+#define HWIO_JPEG_FE_Y_PING_ADDR_RMSK 0xffffffff
+
+#define HWIO_JPEG_FE_Y_PONG_ADDR_ADDR (GEMINI_REG_BASE + 0x00000088)
+#define HWIO_JPEG_FE_Y_PONG_ADDR_RMSK 0xffffffff
+
+#define HWIO_JPEG_FE_CBCR_PING_ADDR_ADDR (GEMINI_REG_BASE + 0x0000008c)
+#define HWIO_JPEG_FE_CBCR_PING_ADDR_RMSK 0xffffffff
+
+#define HWIO_JPEG_FE_CBCR_PONG_ADDR_ADDR (GEMINI_REG_BASE + 0x00000090)
+#define HWIO_JPEG_FE_CBCR_PONG_ADDR_RMSK 0xffffffff
+
+#define HWIO_JPEG_FE_CMD_ADDR (GEMINI_REG_BASE + 0x00000094)
+#define HWIO_JPEG_FE_CMD_RMSK 0x3
+
+#define HWIO_JPEG_FE_BUFFER_CFG_CBCR_MCU_ROWS_BMSK 0x1fff0000
+#define HWIO_JPEG_FE_BUFFER_CFG_CBCR_MCU_ROWS_SHFT 0x10
+#define HWIO_JPEG_FE_BUFFER_CFG_Y_MCU_ROWS_BMSK 0x1fff
+#define HWIO_JPEG_FE_BUFFER_CFG_Y_MCU_ROWS_SHFT 0
+
+#define HWIO_JPEG_FE_Y_PING_ADDR_FE_Y_PING_START_ADDR_BMSK 0xffffffff
+#define HWIO_JPEG_FE_Y_PING_ADDR_FE_Y_PING_START_ADDR_SHFT 0
+
+#define HWIO_JPEG_FE_CBCR_PING_ADDR_FE_CBCR_PING_START_ADDR_BMSK 0xffffffff
+#define HWIO_JPEG_FE_CBCR_PING_ADDR_FE_CBCR_PING_START_ADDR_SHFT 0
+
+#define HWIO_JPEG_FE_Y_PONG_ADDR_FE_Y_PONG_START_ADDR_BMSK 0xffffffff
+#define HWIO_JPEG_FE_Y_PONG_ADDR_FE_Y_PONG_START_ADDR_SHFT 0
+
+#define HWIO_JPEG_FE_CBCR_PONG_ADDR_FE_CBCR_PONG_START_ADDR_BMSK 0xffffffff
+#define HWIO_JPEG_FE_CBCR_PONG_ADDR_FE_CBCR_PONG_START_ADDR_SHFT 0
+
+#define HWIO_JPEG_WE_Y_THRESHOLD_ADDR (GEMINI_REG_BASE + 0x000000c0)
+#define HWIO_JPEG_WE_Y_THRESHOLD_RMSK 0x1ff01ff
+
+#define HWIO_JPEG_WE_CBCR_THRESHOLD_ADDR (GEMINI_REG_BASE      + 0x000000c4)
+#define HWIO_JPEG_WE_CBCR_THRESHOLD_RMSK 0x1ff01ff
+
+#define HWIO_JPEG_WE_Y_UB_CFG_ADDR (GEMINI_REG_BASE + 0x000000e8)
+#define HWIO_JPEG_WE_Y_UB_CFG_RMSK 0x1ff01ff
+
+#define HWIO_JPEG_WE_Y_THRESHOLD_WE_DEASSERT_STALL_TH_BMSK 0x1ff0000
+#define HWIO_JPEG_WE_Y_THRESHOLD_WE_DEASSERT_STALL_TH_SHFT 0x10
+#define HWIO_JPEG_WE_Y_THRESHOLD_WE_ASSERT_STALL_TH_BMSK 0x1ff
+#define HWIO_JPEG_WE_Y_THRESHOLD_WE_ASSERT_STALL_TH_SHFT 0
+
+#define HWIO_JPEG_WE_CBCR_THRESHOLD_WE_DEASSERT_STALL_TH_BMSK 0x1ff0000
+#define HWIO_JPEG_WE_CBCR_THRESHOLD_WE_DEASSERT_STALL_TH_SHFT 0x10
+#define HWIO_JPEG_WE_CBCR_THRESHOLD_WE_ASSERT_STALL_TH_BMSK 0x1ff
+#define HWIO_JPEG_WE_CBCR_THRESHOLD_WE_ASSERT_STALL_TH_SHFT 0
+
+#define HWIO_JPEG_WE_Y_PING_BUFFER_CFG_ADDR (GEMINI_REG_BASE + 0x000000c8)
+#define HWIO_JPEG_WE_Y_PING_BUFFER_CFG_RMSK 0x7fffff
+
+#define HWIO_JPEG_WE_Y_PING_ADDR_ADDR (GEMINI_REG_BASE + 0x000000d8)
+#define HWIO_JPEG_WE_Y_PING_ADDR_RMSK 0xfffffff8
+
+#define HWIO_JPEG_WE_Y_PONG_BUFFER_CFG_ADDR (GEMINI_REG_BASE + 0x000000cc)
+#define HWIO_JPEG_WE_Y_PONG_BUFFER_CFG_RMSK 0x7fffff
+
+#define HWIO_JPEG_WE_Y_PONG_ADDR_ADDR (GEMINI_REG_BASE + 0x000000dc)
+#define HWIO_JPEG_WE_Y_PONG_ADDR_RMSK 0xfffffff8
+
+#define HWIO_JPEG_WE_Y_PING_BUFFER_CFG_WE_BUFFER_LENGTH_BMSK 0x7fffff
+#define HWIO_JPEG_WE_Y_PING_BUFFER_CFG_WE_BUFFER_LENGTH_SHFT 0
+
+#define HWIO_JPEG_WE_Y_PONG_BUFFER_CFG_WE_BUFFER_LENGTH_BMSK 0x7fffff
+#define HWIO_JPEG_WE_Y_PONG_BUFFER_CFG_WE_BUFFER_LENGTH_SHFT 0
+
+#define HWIO_JPEG_IRQ_MASK_ADDR (GEMINI_REG_BASE + 0x00000014)
+#define HWIO_JPEG_IRQ_MASK_RMSK 0xffffffff
+
+#define HWIO_JPEG_IRQ_CLEAR_ADDR (GEMINI_REG_BASE + 0x00000018)
+#define HWIO_JPEG_IRQ_CLEAR_RMSK 0xffffffff
+
+#define HWIO_JPEG_RESET_CMD_ADDR (GEMINI_REG_BASE + 0x00000004)
+#define HWIO_JPEG_RESET_CMD_RMSK 0xe004ffff
+
+#define HWIO_JPEG_IRQ_STATUS_ADDR (GEMINI_REG_BASE + 0x0000001c)
+#define HWIO_JPEG_IRQ_STATUS_RMSK 0xffffffff
+
+#define HWIO_JPEG_STATUS_ENCODE_OUTPUT_SIZE_ADDR (GEMINI_REG_BASE + 0x00000034)
+#define HWIO_JPEG_STATUS_ENCODE_OUTPUT_SIZE_RMSK 0xffffff
+
+#endif /* MSM_GEMINI_HW_REG_H */
diff --git a/drivers/media/video/msm/gemini/msm_gemini_platform.c b/drivers/media/video/msm/gemini/msm_gemini_platform.c
new file mode 100644
index 0000000..d7f6bd8
--- /dev/null
+++ b/drivers/media/video/msm/gemini/msm_gemini_platform.c
@@ -0,0 +1,255 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/pm_qos.h>
+#include <linux/clk.h>
+#include <mach/clk.h>
+#include <linux/io.h>
+#include <linux/android_pmem.h>
+#include <mach/camera.h>
+#include <mach/iommu_domains.h>
+
+#include "msm_gemini_platform.h"
+#include "msm_gemini_sync.h"
+#include "msm_gemini_common.h"
+#include "msm_gemini_hw.h"
+
+/* AXI rate in KHz */
+#define MSM_SYSTEM_BUS_RATE	160000
+struct ion_client *gemini_client;
+
+void msm_gemini_platform_p2v(struct file  *file,
+				struct ion_handle **ionhandle)
+{
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+	ion_unmap_iommu(gemini_client, *ionhandle, CAMERA_DOMAIN, GEN_POOL);
+	ion_free(gemini_client, *ionhandle);
+	*ionhandle = NULL;
+#elif CONFIG_ANDROID_PMEM
+	put_pmem_file(file);
+#endif
+}
+
+uint32_t msm_gemini_platform_v2p(int fd, uint32_t len, struct file **file_p,
+				struct ion_handle **ionhandle)
+{
+	unsigned long paddr;
+	unsigned long size;
+	int rc;
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+	*ionhandle = ion_import_fd(gemini_client, fd);
+	if (IS_ERR_OR_NULL(*ionhandle))
+		return 0;
+
+	rc = ion_map_iommu(gemini_client, *ionhandle, CAMERA_DOMAIN, GEN_POOL,
+			SZ_4K, 0, &paddr, (unsigned long *)&size, UNCACHED, 0);
+#elif CONFIG_ANDROID_PMEM
+	unsigned long kvstart;
+	rc = get_pmem_file(fd, &paddr, &kvstart, &size, file_p);
+#else
+	rc = 0;
+	paddr = 0;
+	size = 0;
+#endif
+	if (rc < 0) {
+		GMN_PR_ERR("%s: get_pmem_file fd %d error %d\n", __func__, fd,
+			rc);
+		goto error1;
+	}
+
+	/* validate user input */
+	if (len > size) {
+		GMN_PR_ERR("%s: invalid offset + len\n", __func__);
+		goto error1;
+	}
+
+	return paddr;
+error1:
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+	ion_free(gemini_client, *ionhandle);
+#endif
+	return 0;
+}
+
+static struct msm_cam_clk_info gemini_8x_clk_info[] = {
+	{"core_clk", 228571000},
+	{"iface_clk", -1},
+};
+
+static struct msm_cam_clk_info gemini_7x_clk_info[] = {
+	{"core_clk", 153600000},
+	{"iface_clk", -1},
+};
+
+static struct msm_cam_clk_info gemini_imem_clk_info[] = {
+	{"mem_clk", -1},
+};
+
+int msm_gemini_platform_init(struct platform_device *pdev,
+	struct resource **mem,
+	void **base,
+	int *irq,
+	irqreturn_t (*handler) (int, void *),
+	void *context)
+{
+	int rc = -1;
+	int gemini_irq;
+	struct resource *gemini_mem, *gemini_io, *gemini_irq_res;
+	void *gemini_base;
+	struct msm_gemini_device *pgmn_dev =
+		(struct msm_gemini_device *) context;
+
+	gemini_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!gemini_mem) {
+		GMN_PR_ERR("%s: no mem resource?\n", __func__);
+		return -ENODEV;
+	}
+
+	gemini_irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!gemini_irq_res) {
+		GMN_PR_ERR("no irq resource?\n");
+		return -ENODEV;
+	}
+	gemini_irq = gemini_irq_res->start;
+
+	gemini_io = request_mem_region(gemini_mem->start,
+		resource_size(gemini_mem), pdev->name);
+	if (!gemini_io) {
+		GMN_PR_ERR("%s: region already claimed\n", __func__);
+		return -EBUSY;
+	}
+
+	gemini_base = ioremap(gemini_mem->start, resource_size(gemini_mem));
+	if (!gemini_base) {
+		rc = -ENOMEM;
+		GMN_PR_ERR("%s: ioremap failed\n", __func__);
+		goto fail1;
+	}
+
+	pgmn_dev->hw_version = GEMINI_8X60;
+	rc = msm_cam_clk_enable(&pgmn_dev->pdev->dev, gemini_8x_clk_info,
+	 pgmn_dev->gemini_clk, ARRAY_SIZE(gemini_8x_clk_info), 1);
+	if (rc < 0) {
+		pgmn_dev->hw_version = GEMINI_7X;
+		rc = msm_cam_clk_enable(&pgmn_dev->pdev->dev,
+			gemini_7x_clk_info, pgmn_dev->gemini_clk,
+			ARRAY_SIZE(gemini_7x_clk_info), 1);
+		if (rc < 0) {
+			GMN_PR_ERR("%s: clk failed rc = %d\n", __func__, rc);
+			goto fail2;
+		}
+	} else {
+		rc = msm_cam_clk_enable(&pgmn_dev->pdev->dev,
+				gemini_imem_clk_info, &pgmn_dev->gemini_clk[2],
+				ARRAY_SIZE(gemini_imem_clk_info), 1);
+		if (!rc)
+			pgmn_dev->hw_version = GEMINI_8960;
+	}
+
+	if (pgmn_dev->hw_version != GEMINI_7X) {
+		if (pgmn_dev->gemini_fs == NULL) {
+			pgmn_dev->gemini_fs =
+				regulator_get(&pgmn_dev->pdev->dev, "vdd");
+			if (IS_ERR(pgmn_dev->gemini_fs)) {
+				pr_err("%s: Regulator FS_ijpeg get failed %ld\n",
+					__func__, PTR_ERR(pgmn_dev->gemini_fs));
+				pgmn_dev->gemini_fs = NULL;
+				goto gemini_fs_failed;
+			} else if (regulator_enable(pgmn_dev->gemini_fs)) {
+				pr_err("%s: Regulator FS_ijpeg enable failed\n",
+								__func__);
+				regulator_put(pgmn_dev->gemini_fs);
+				pgmn_dev->gemini_fs = NULL;
+				goto gemini_fs_failed;
+			}
+		}
+	}
+
+	msm_gemini_hw_init(gemini_base, resource_size(gemini_mem));
+	rc = request_irq(gemini_irq, handler, IRQF_TRIGGER_RISING, "gemini",
+		context);
+	if (rc) {
+		GMN_PR_ERR("%s: request_irq failed, %d\n", __func__,
+			gemini_irq);
+		goto fail3;
+	}
+
+	*mem  = gemini_mem;
+	*base = gemini_base;
+	*irq  = gemini_irq;
+
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+	gemini_client = msm_ion_client_create(-1, "camera/gemini");
+#endif
+	GMN_DBG("%s:%d] success\n", __func__, __LINE__);
+
+	return rc;
+
+fail3:
+	if (pgmn_dev->hw_version != GEMINI_7X) {
+		regulator_disable(pgmn_dev->gemini_fs);
+		regulator_put(pgmn_dev->gemini_fs);
+		pgmn_dev->gemini_fs = NULL;
+	}
+gemini_fs_failed:
+	if (pgmn_dev->hw_version == GEMINI_8960)
+		msm_cam_clk_enable(&pgmn_dev->pdev->dev, gemini_imem_clk_info,
+		 &pgmn_dev->gemini_clk[2], ARRAY_SIZE(gemini_imem_clk_info), 0);
+	if (pgmn_dev->hw_version != GEMINI_7X)
+		msm_cam_clk_enable(&pgmn_dev->pdev->dev, gemini_8x_clk_info,
+		pgmn_dev->gemini_clk, ARRAY_SIZE(gemini_8x_clk_info), 0);
+	else
+		msm_cam_clk_enable(&pgmn_dev->pdev->dev, gemini_7x_clk_info,
+		pgmn_dev->gemini_clk, ARRAY_SIZE(gemini_7x_clk_info), 0);
+fail2:
+	iounmap(gemini_base);
+fail1:
+	release_mem_region(gemini_mem->start, resource_size(gemini_mem));
+	GMN_DBG("%s:%d] fail\n", __func__, __LINE__);
+	return rc;
+}
+
+int msm_gemini_platform_release(struct resource *mem, void *base, int irq,
+	void *context)
+{
+	int result = 0;
+	struct msm_gemini_device *pgmn_dev =
+		(struct msm_gemini_device *) context;
+
+	free_irq(irq, context);
+
+	if (pgmn_dev->hw_version != GEMINI_7X) {
+		regulator_disable(pgmn_dev->gemini_fs);
+		regulator_put(pgmn_dev->gemini_fs);
+		pgmn_dev->gemini_fs = NULL;
+	}
+
+	if (pgmn_dev->hw_version == GEMINI_8960)
+		msm_cam_clk_enable(&pgmn_dev->pdev->dev, gemini_imem_clk_info,
+		 &pgmn_dev->gemini_clk[2], ARRAY_SIZE(gemini_imem_clk_info), 0);
+	if (pgmn_dev->hw_version != GEMINI_7X)
+		msm_cam_clk_enable(&pgmn_dev->pdev->dev, gemini_8x_clk_info,
+		pgmn_dev->gemini_clk, ARRAY_SIZE(gemini_8x_clk_info), 0);
+	else
+		msm_cam_clk_enable(&pgmn_dev->pdev->dev, gemini_7x_clk_info,
+		pgmn_dev->gemini_clk, ARRAY_SIZE(gemini_7x_clk_info), 0);
+
+	iounmap(base);
+	release_mem_region(mem->start, resource_size(mem));
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+	ion_client_destroy(gemini_client);
+#endif
+	GMN_DBG("%s:%d] success\n", __func__, __LINE__);
+	return result;
+}
+
diff --git a/drivers/media/video/msm/gemini/msm_gemini_platform.h b/drivers/media/video/msm/gemini/msm_gemini_platform.h
new file mode 100644
index 0000000..4542129
--- /dev/null
+++ b/drivers/media/video/msm/gemini/msm_gemini_platform.h
@@ -0,0 +1,36 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MSM_GEMINI_PLATFORM_H
+#define MSM_GEMINI_PLATFORM_H
+
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/ion.h>
+void msm_gemini_platform_p2v(struct file  *file,
+				struct ion_handle **ionhandle);
+uint32_t msm_gemini_platform_v2p(int fd, uint32_t len, struct file **file,
+				struct ion_handle **ionhandle);
+
+int msm_gemini_platform_clk_enable(void);
+int msm_gemini_platform_clk_disable(void);
+
+int msm_gemini_platform_init(struct platform_device *pdev,
+	struct resource **mem,
+	void **base,
+	int *irq,
+	irqreturn_t (*handler) (int, void *),
+	void *context);
+int msm_gemini_platform_release(struct resource *mem, void *base, int irq,
+	void *context);
+
+#endif /* MSM_GEMINI_PLATFORM_H */
diff --git a/drivers/media/video/msm/gemini/msm_gemini_sync.c b/drivers/media/video/msm/gemini/msm_gemini_sync.c
new file mode 100644
index 0000000..fe7c99f
--- /dev/null
+++ b/drivers/media/video/msm/gemini/msm_gemini_sync.c
@@ -0,0 +1,850 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <media/msm_gemini.h>
+#include "msm_gemini_sync.h"
+#include "msm_gemini_core.h"
+#include "msm_gemini_platform.h"
+#include "msm_gemini_common.h"
+
+static int release_buf;
+
+/*************** queue helper ****************/
+inline void msm_gemini_q_init(char const *name, struct msm_gemini_q *q_p)
+{
+	GMN_DBG("%s:%d] %s\n", __func__, __LINE__, name);
+	q_p->name = name;
+	spin_lock_init(&q_p->lck);
+	INIT_LIST_HEAD(&q_p->q);
+	init_waitqueue_head(&q_p->wait);
+	q_p->unblck = 0;
+}
+
+inline void *msm_gemini_q_out(struct msm_gemini_q *q_p)
+{
+	unsigned long flags;
+	struct msm_gemini_q_entry *q_entry_p = NULL;
+	void *data = NULL;
+
+	GMN_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+	spin_lock_irqsave(&q_p->lck, flags);
+	if (!list_empty(&q_p->q)) {
+		q_entry_p = list_first_entry(&q_p->q, struct msm_gemini_q_entry,
+			list);
+		list_del_init(&q_entry_p->list);
+	}
+	spin_unlock_irqrestore(&q_p->lck, flags);
+
+	if (q_entry_p) {
+		data = q_entry_p->data;
+		kfree(q_entry_p);
+	} else {
+		GMN_DBG("%s:%d] %s no entry\n", __func__, __LINE__,
+			q_p->name);
+	}
+
+	return data;
+}
+
+inline int msm_gemini_q_in(struct msm_gemini_q *q_p, void *data)
+{
+	unsigned long flags;
+
+	struct msm_gemini_q_entry *q_entry_p;
+
+	GMN_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+
+	q_entry_p = kmalloc(sizeof(struct msm_gemini_q_entry), GFP_ATOMIC);
+	if (!q_entry_p) {
+		GMN_PR_ERR("%s: no mem\n", __func__);
+		return -1;
+	}
+	q_entry_p->data = data;
+
+	spin_lock_irqsave(&q_p->lck, flags);
+	list_add_tail(&q_entry_p->list, &q_p->q);
+	spin_unlock_irqrestore(&q_p->lck, flags);
+
+	return 0;
+}
+
+inline int msm_gemini_q_in_buf(struct msm_gemini_q *q_p,
+	struct msm_gemini_core_buf *buf)
+{
+	struct msm_gemini_core_buf *buf_p;
+
+	GMN_DBG("%s:%d]\n", __func__, __LINE__);
+	buf_p = kmalloc(sizeof(struct msm_gemini_core_buf), GFP_ATOMIC);
+	if (!buf_p) {
+		GMN_PR_ERR("%s: no mem\n", __func__);
+		return -1;
+	}
+
+	memcpy(buf_p, buf, sizeof(struct msm_gemini_core_buf));
+
+	msm_gemini_q_in(q_p, buf_p);
+	return 0;
+}
+
+inline int msm_gemini_q_wait(struct msm_gemini_q *q_p)
+{
+	int tm = MAX_SCHEDULE_TIMEOUT; /* 500ms */
+	int rc;
+
+	GMN_DBG("%s:%d] %s wait\n", __func__, __LINE__, q_p->name);
+	rc = wait_event_interruptible_timeout(q_p->wait,
+		(!list_empty_careful(&q_p->q) || q_p->unblck),
+		msecs_to_jiffies(tm));
+	GMN_DBG("%s:%d] %s wait done\n", __func__, __LINE__, q_p->name);
+	if (list_empty_careful(&q_p->q)) {
+		if (rc == 0) {
+			rc = -ETIMEDOUT;
+			GMN_PR_ERR("%s:%d] %s timeout\n", __func__, __LINE__,
+				q_p->name);
+		} else if (q_p->unblck) {
+			GMN_DBG("%s:%d] %s unblock is true\n", __func__,
+				__LINE__, q_p->name);
+			q_p->unblck = 0;
+			rc = -ECANCELED;
+		} else if (rc < 0) {
+			GMN_PR_ERR("%s:%d] %s rc %d\n", __func__, __LINE__,
+				q_p->name, rc);
+		}
+	}
+	return rc;
+}
+
+inline int msm_gemini_q_wakeup(struct msm_gemini_q *q_p)
+{
+	GMN_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+	wake_up(&q_p->wait);
+	return 0;
+}
+
+inline int msm_gemini_q_unblock(struct msm_gemini_q *q_p)
+{
+	GMN_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+	q_p->unblck = 1;
+	wake_up(&q_p->wait);
+	return 0;
+}
+
+inline void msm_gemini_outbuf_q_cleanup(struct msm_gemini_q *q_p)
+{
+	struct msm_gemini_core_buf *buf_p;
+	GMN_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+	do {
+		buf_p = msm_gemini_q_out(q_p);
+		if (buf_p) {
+			msm_gemini_platform_p2v(buf_p->file,
+				&buf_p->handle);
+			GMN_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+			kfree(buf_p);
+		}
+	} while (buf_p);
+	q_p->unblck = 0;
+}
+
+inline void msm_gemini_q_cleanup(struct msm_gemini_q *q_p)
+{
+	void *data;
+	GMN_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+	do {
+		data = msm_gemini_q_out(q_p);
+		if (data) {
+			GMN_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+			kfree(data);
+		}
+	} while (data);
+	q_p->unblck = 0;
+}
+
+/*************** event queue ****************/
+
+int msm_gemini_framedone_irq(struct msm_gemini_device *pgmn_dev,
+	struct msm_gemini_core_buf *buf_in)
+{
+	int rc = 0;
+
+	GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+
+	if (buf_in) {
+		buf_in->vbuf.framedone_len = buf_in->framedone_len;
+		buf_in->vbuf.type = MSM_GEMINI_EVT_FRAMEDONE;
+		GMN_DBG("%s:%d] 0x%08x %d framedone_len %d\n",
+			__func__, __LINE__,
+			(int) buf_in->y_buffer_addr, buf_in->y_len,
+			buf_in->vbuf.framedone_len);
+		rc = msm_gemini_q_in_buf(&pgmn_dev->evt_q, buf_in);
+	} else {
+		GMN_PR_ERR("%s:%d] no output return buffer\n",
+			__func__, __LINE__);
+		rc = -1;
+	}
+
+	if (buf_in)
+		rc = msm_gemini_q_wakeup(&pgmn_dev->evt_q);
+
+	return rc;
+}
+
+int msm_gemini_evt_get(struct msm_gemini_device *pgmn_dev,
+	void __user *to)
+{
+	struct msm_gemini_core_buf *buf_p;
+	struct msm_gemini_ctrl_cmd ctrl_cmd;
+
+	GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+
+	msm_gemini_q_wait(&pgmn_dev->evt_q);
+	buf_p = msm_gemini_q_out(&pgmn_dev->evt_q);
+
+	if (!buf_p) {
+		GMN_DBG("%s:%d] no buffer\n", __func__, __LINE__);
+		return -EAGAIN;
+	}
+
+	ctrl_cmd.type = buf_p->vbuf.type;
+	kfree(buf_p);
+
+	GMN_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__,
+		(int) ctrl_cmd.value, ctrl_cmd.len);
+
+	if (copy_to_user(to, &ctrl_cmd, sizeof(ctrl_cmd))) {
+		GMN_PR_ERR("%s:%d]\n", __func__, __LINE__);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+int msm_gemini_evt_get_unblock(struct msm_gemini_device *pgmn_dev)
+{
+	GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+	msm_gemini_q_unblock(&pgmn_dev->evt_q);
+	return 0;
+}
+
+void msm_gemini_reset_ack_irq(struct msm_gemini_device *pgmn_dev)
+{
+	GMN_DBG("%s:%d]\n", __func__, __LINE__);
+}
+
+void msm_gemini_err_irq(struct msm_gemini_device *pgmn_dev,
+	int event)
+{
+	int rc = 0;
+	struct msm_gemini_core_buf buf;
+
+	GMN_PR_ERR("%s:%d] error: %d\n", __func__, __LINE__, event);
+
+	buf.vbuf.type = MSM_GEMINI_EVT_ERR;
+	rc = msm_gemini_q_in_buf(&pgmn_dev->evt_q, &buf);
+	if (!rc)
+		rc = msm_gemini_q_wakeup(&pgmn_dev->evt_q);
+
+	if (!rc)
+		GMN_PR_ERR("%s:%d] err err\n", __func__, __LINE__);
+
+	return;
+}
+
+/*************** output queue ****************/
+
+int msm_gemini_we_pingpong_irq(struct msm_gemini_device *pgmn_dev,
+	struct msm_gemini_core_buf *buf_in)
+{
+	int rc = 0;
+	struct msm_gemini_core_buf *buf_out;
+
+	GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+	if (buf_in) {
+		GMN_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__,
+			(int) buf_in->y_buffer_addr, buf_in->y_len);
+		rc = msm_gemini_q_in_buf(&pgmn_dev->output_rtn_q, buf_in);
+	} else {
+		GMN_DBG("%s:%d] no output return buffer\n", __func__,
+			__LINE__);
+		rc = -1;
+	}
+
+	buf_out = msm_gemini_q_out(&pgmn_dev->output_buf_q);
+
+	if (buf_out) {
+		rc = msm_gemini_core_we_buf_update(buf_out);
+		kfree(buf_out);
+	} else {
+		msm_gemini_core_we_buf_reset(buf_in);
+		GMN_DBG("%s:%d] no output buffer\n", __func__, __LINE__);
+		rc = -2;
+	}
+
+	if (buf_in)
+		rc = msm_gemini_q_wakeup(&pgmn_dev->output_rtn_q);
+
+	return rc;
+}
+
+int msm_gemini_output_get(struct msm_gemini_device *pgmn_dev, void __user *to)
+{
+	struct msm_gemini_core_buf *buf_p;
+	struct msm_gemini_buf buf_cmd;
+
+	GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+
+	msm_gemini_q_wait(&pgmn_dev->output_rtn_q);
+	buf_p = msm_gemini_q_out(&pgmn_dev->output_rtn_q);
+
+	if (!buf_p) {
+		GMN_DBG("%s:%d] no output buffer return\n",
+			__func__, __LINE__);
+		return -EAGAIN;
+	}
+
+	buf_cmd = buf_p->vbuf;
+	msm_gemini_platform_p2v(buf_p->file, &buf_p->handle);
+	kfree(buf_p);
+
+	GMN_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__,
+		(int) buf_cmd.vaddr, buf_cmd.y_len);
+
+	if (copy_to_user(to, &buf_cmd, sizeof(buf_cmd))) {
+		GMN_PR_ERR("%s:%d]", __func__, __LINE__);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+int msm_gemini_output_get_unblock(struct msm_gemini_device *pgmn_dev)
+{
+	GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+	msm_gemini_q_unblock(&pgmn_dev->output_rtn_q);
+	return 0;
+}
+
+int msm_gemini_output_buf_enqueue(struct msm_gemini_device *pgmn_dev,
+	void __user *arg)
+{
+	struct msm_gemini_buf buf_cmd;
+	struct msm_gemini_core_buf *buf_p;
+
+	GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+	if (copy_from_user(&buf_cmd, arg, sizeof(struct msm_gemini_buf))) {
+		GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+		return -EFAULT;
+	}
+
+	buf_p = kmalloc(sizeof(struct msm_gemini_core_buf), GFP_ATOMIC);
+	if (!buf_p) {
+		GMN_PR_ERR("%s:%d] no mem\n", __func__, __LINE__);
+		return -1;
+	}
+
+	GMN_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__, (int) buf_cmd.vaddr,
+		buf_cmd.y_len);
+
+	buf_p->y_buffer_addr = msm_gemini_platform_v2p(buf_cmd.fd,
+		buf_cmd.y_len, &buf_p->file, &buf_p->handle);
+	if (!buf_p->y_buffer_addr) {
+		GMN_PR_ERR("%s:%d] v2p wrong\n", __func__, __LINE__);
+		kfree(buf_p);
+		return -1;
+	}
+	buf_p->y_len = buf_cmd.y_len;
+	buf_p->vbuf = buf_cmd;
+
+	msm_gemini_q_in(&pgmn_dev->output_buf_q, buf_p);
+	return 0;
+}
+
+/*************** input queue ****************/
+
+int msm_gemini_fe_pingpong_irq(struct msm_gemini_device *pgmn_dev,
+	struct msm_gemini_core_buf *buf_in)
+{
+	struct msm_gemini_core_buf *buf_out;
+	int rc = 0;
+
+	GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+	if (buf_in) {
+		GMN_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__,
+			(int) buf_in->y_buffer_addr, buf_in->y_len);
+		rc = msm_gemini_q_in_buf(&pgmn_dev->input_rtn_q, buf_in);
+	} else {
+		GMN_DBG("%s:%d] no input return buffer\n", __func__,
+			__LINE__);
+		rc = -1;
+	}
+
+	buf_out = msm_gemini_q_out(&pgmn_dev->input_buf_q);
+
+	if (buf_out) {
+		rc = msm_gemini_core_fe_buf_update(buf_out);
+		kfree(buf_out);
+		msm_gemini_core_fe_start();
+	} else {
+		GMN_DBG("%s:%d] no input buffer\n", __func__, __LINE__);
+		rc = -2;
+	}
+
+	if (buf_in)
+		rc = msm_gemini_q_wakeup(&pgmn_dev->input_rtn_q);
+
+	return rc;
+}
+
+int msm_gemini_input_get(struct msm_gemini_device *pgmn_dev, void __user * to)
+{
+	struct msm_gemini_core_buf *buf_p;
+	struct msm_gemini_buf buf_cmd;
+
+	GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+	msm_gemini_q_wait(&pgmn_dev->input_rtn_q);
+	buf_p = msm_gemini_q_out(&pgmn_dev->input_rtn_q);
+
+	if (!buf_p) {
+		GMN_DBG("%s:%d] no input buffer return\n",
+			__func__, __LINE__);
+		return -EAGAIN;
+	}
+
+	buf_cmd = buf_p->vbuf;
+	if (pgmn_dev->op_mode == MSM_GEMINI_MODE_OFFLINE_ENCODE ||
+		pgmn_dev->op_mode == MSM_GEMINI_MODE_OFFLINE_ROTATION) {
+		msm_gemini_platform_p2v(buf_p->file, &buf_p->handle);
+	}
+	kfree(buf_p);
+
+	GMN_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__,
+		(int) buf_cmd.vaddr, buf_cmd.y_len);
+
+	if (copy_to_user(to, &buf_cmd, sizeof(buf_cmd))) {
+		GMN_PR_ERR("%s:%d]\n", __func__, __LINE__);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+int msm_gemini_input_get_unblock(struct msm_gemini_device *pgmn_dev)
+{
+	GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+	msm_gemini_q_unblock(&pgmn_dev->input_rtn_q);
+	return 0;
+}
+
+int msm_gemini_input_buf_enqueue(struct msm_gemini_device *pgmn_dev,
+	void __user *arg)
+{
+	struct msm_gemini_core_buf *buf_p;
+	struct msm_gemini_buf buf_cmd;
+
+	if (copy_from_user(&buf_cmd, arg, sizeof(struct msm_gemini_buf))) {
+		GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+		return -EFAULT;
+	}
+
+	buf_p = kmalloc(sizeof(struct msm_gemini_core_buf), GFP_ATOMIC);
+	if (!buf_p) {
+		GMN_PR_ERR("%s:%d] no mem\n", __func__, __LINE__);
+		return -1;
+	}
+
+	GMN_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__,
+		(int) buf_cmd.vaddr, buf_cmd.y_len);
+
+	if (pgmn_dev->op_mode == MSM_GEMINI_MODE_REALTIME_ENCODE) {
+		buf_p->y_buffer_addr    = buf_cmd.y_off;
+	} else {
+	buf_p->y_buffer_addr    = msm_gemini_platform_v2p(buf_cmd.fd,
+		buf_cmd.y_len + buf_cmd.cbcr_len, &buf_p->file,
+		&buf_p->handle)	+ buf_cmd.offset;
+	}
+	buf_p->y_len          = buf_cmd.y_len;
+
+	buf_p->cbcr_buffer_addr = buf_p->y_buffer_addr + buf_cmd.y_len;
+	buf_p->cbcr_len       = buf_cmd.cbcr_len;
+
+	buf_p->num_of_mcu_rows = buf_cmd.num_of_mcu_rows;
+	GMN_DBG("%s: y_addr=%x,y_len=%x,cbcr_addr=%x,cbcr_len=%x\n", __func__,
+		buf_p->y_buffer_addr, buf_p->y_len, buf_p->cbcr_buffer_addr,
+		buf_p->cbcr_len);
+
+	if (!buf_p->y_buffer_addr || !buf_p->cbcr_buffer_addr) {
+		GMN_PR_ERR("%s:%d] v2p wrong\n", __func__, __LINE__);
+		kfree(buf_p);
+		return -1;
+	}
+	buf_p->vbuf           = buf_cmd;
+
+	msm_gemini_q_in(&pgmn_dev->input_buf_q, buf_p);
+
+	return 0;
+}
+
+int msm_gemini_irq(int event, void *context, void *data)
+{
+	struct msm_gemini_device *pgmn_dev =
+		(struct msm_gemini_device *) context;
+
+	switch (event) {
+	case MSM_GEMINI_HW_MASK_COMP_FRAMEDONE:
+		msm_gemini_framedone_irq(pgmn_dev, data);
+		msm_gemini_we_pingpong_irq(pgmn_dev, data);
+		break;
+
+	case MSM_GEMINI_HW_MASK_COMP_FE:
+		msm_gemini_fe_pingpong_irq(pgmn_dev, data);
+		break;
+
+	case MSM_GEMINI_HW_MASK_COMP_WE:
+		msm_gemini_we_pingpong_irq(pgmn_dev, data);
+		break;
+
+	case MSM_GEMINI_HW_MASK_COMP_RESET_ACK:
+		msm_gemini_reset_ack_irq(pgmn_dev);
+		break;
+
+	case MSM_GEMINI_HW_MASK_COMP_ERR:
+	default:
+		msm_gemini_err_irq(pgmn_dev, event);
+		break;
+	}
+
+	return 0;
+}
+
+int __msm_gemini_open(struct msm_gemini_device *pgmn_dev)
+{
+	int rc;
+
+	mutex_lock(&pgmn_dev->lock);
+	if (pgmn_dev->open_count) {
+		/* only open once */
+		GMN_PR_ERR("%s:%d] busy\n", __func__, __LINE__);
+		mutex_unlock(&pgmn_dev->lock);
+		return -EBUSY;
+	}
+	pgmn_dev->open_count++;
+	mutex_unlock(&pgmn_dev->lock);
+
+	msm_gemini_core_irq_install(msm_gemini_irq);
+	rc = msm_gemini_platform_init(pgmn_dev->pdev,
+		&pgmn_dev->mem, &pgmn_dev->base,
+		&pgmn_dev->irq, msm_gemini_core_irq, pgmn_dev);
+	if (rc) {
+		GMN_PR_ERR("%s:%d] platform_init fail %d\n", __func__,
+			__LINE__, rc);
+		return rc;
+	}
+
+	GMN_DBG("%s:%d] platform resources - mem %p, base %p, irq %d\n",
+		__func__, __LINE__,
+		pgmn_dev->mem, pgmn_dev->base, pgmn_dev->irq);
+
+	msm_gemini_q_cleanup(&pgmn_dev->evt_q);
+	msm_gemini_q_cleanup(&pgmn_dev->output_rtn_q);
+	msm_gemini_outbuf_q_cleanup(&pgmn_dev->output_buf_q);
+	msm_gemini_q_cleanup(&pgmn_dev->input_rtn_q);
+	msm_gemini_q_cleanup(&pgmn_dev->input_buf_q);
+	msm_gemini_core_init();
+
+	GMN_DBG("%s:%d] success\n", __func__, __LINE__);
+	return rc;
+}
+
+int __msm_gemini_release(struct msm_gemini_device *pgmn_dev)
+{
+	GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+	mutex_lock(&pgmn_dev->lock);
+	if (!pgmn_dev->open_count) {
+		GMN_PR_ERR(KERN_ERR "%s: not opened\n", __func__);
+		mutex_unlock(&pgmn_dev->lock);
+		return -EINVAL;
+	}
+	pgmn_dev->open_count--;
+	mutex_unlock(&pgmn_dev->lock);
+
+	msm_gemini_core_release(release_buf);
+	msm_gemini_q_cleanup(&pgmn_dev->evt_q);
+	msm_gemini_q_cleanup(&pgmn_dev->output_rtn_q);
+	msm_gemini_outbuf_q_cleanup(&pgmn_dev->output_buf_q);
+	msm_gemini_q_cleanup(&pgmn_dev->input_rtn_q);
+	msm_gemini_outbuf_q_cleanup(&pgmn_dev->input_buf_q);
+
+	if (pgmn_dev->open_count)
+		GMN_PR_ERR(KERN_ERR "%s: multiple opens\n", __func__);
+
+	msm_gemini_platform_release(pgmn_dev->mem, pgmn_dev->base,
+		pgmn_dev->irq, pgmn_dev);
+
+	return 0;
+}
+
+int msm_gemini_ioctl_hw_cmd(struct msm_gemini_device *pgmn_dev,
+	void * __user arg)
+{
+	struct msm_gemini_hw_cmd hw_cmd;
+	int is_copy_to_user;
+
+	if (copy_from_user(&hw_cmd, arg, sizeof(struct msm_gemini_hw_cmd))) {
+		GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+		return -EFAULT;
+	}
+
+	is_copy_to_user = msm_gemini_hw_exec_cmds(&hw_cmd, 1);
+	GMN_DBG("%s:%d] type %d, n %d, offset %d, mask %x, data %x, pdata %x\n",
+		__func__, __LINE__, hw_cmd.type, hw_cmd.n, hw_cmd.offset,
+		hw_cmd.mask, hw_cmd.data, (int) hw_cmd.pdata);
+
+	if (is_copy_to_user >= 0) {
+		if (copy_to_user(arg, &hw_cmd, sizeof(hw_cmd))) {
+			GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+			return -EFAULT;
+		}
+	}
+
+	return 0;
+}
+
+int msm_gemini_ioctl_hw_cmds(struct msm_gemini_device *pgmn_dev,
+	void * __user arg)
+{
+	int is_copy_to_user;
+	int len;
+	uint32_t m;
+	struct msm_gemini_hw_cmds *hw_cmds_p;
+	struct msm_gemini_hw_cmd *hw_cmd_p;
+
+	if (copy_from_user(&m, arg, sizeof(m))) {
+		GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+		return -EFAULT;
+	}
+
+	len = sizeof(struct msm_gemini_hw_cmds) +
+		sizeof(struct msm_gemini_hw_cmd) * (m - 1);
+	hw_cmds_p = kmalloc(len, GFP_KERNEL);
+	if (!hw_cmds_p) {
+		GMN_PR_ERR("%s:%d] no mem %d\n", __func__, __LINE__, len);
+		return -EFAULT;
+	}
+
+	if (copy_from_user(hw_cmds_p, arg, len)) {
+		GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+		kfree(hw_cmds_p);
+		return -EFAULT;
+	}
+
+	hw_cmd_p = (struct msm_gemini_hw_cmd *) &(hw_cmds_p->hw_cmd);
+
+	is_copy_to_user = msm_gemini_hw_exec_cmds(hw_cmd_p, m);
+
+	if (is_copy_to_user >= 0) {
+		if (copy_to_user(arg, hw_cmds_p, len)) {
+			GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+			kfree(hw_cmds_p);
+			return -EFAULT;
+		}
+	}
+	kfree(hw_cmds_p);
+	return 0;
+}
+
+int msm_gemini_start(struct msm_gemini_device *pgmn_dev, void * __user arg)
+{
+	struct msm_gemini_core_buf *buf_out;
+	struct msm_gemini_core_buf *buf_out_free[2] = {NULL, NULL};
+	int i, rc;
+
+	GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+
+	release_buf = 1;
+	for (i = 0; i < 2; i++) {
+		buf_out = msm_gemini_q_out(&pgmn_dev->input_buf_q);
+
+		if (buf_out) {
+			msm_gemini_core_fe_buf_update(buf_out);
+			kfree(buf_out);
+		} else {
+			GMN_DBG("%s:%d] no input buffer\n", __func__, __LINE__);
+			break;
+		}
+	}
+
+	for (i = 0; i < 2; i++) {
+		buf_out_free[i] = msm_gemini_q_out(&pgmn_dev->output_buf_q);
+
+		if (buf_out_free[i]) {
+			msm_gemini_core_we_buf_update(buf_out_free[i]);
+		} else if (i == 1) {
+			/* set the pong to same address as ping */
+			buf_out_free[0]->y_len >>= 1;
+			buf_out_free[0]->y_buffer_addr +=
+				buf_out_free[0]->y_len;
+			msm_gemini_core_we_buf_update(buf_out_free[0]);
+			/* since ping and pong are same buf release only once*/
+			release_buf = 0;
+		} else {
+			GMN_DBG("%s:%d] no output buffer\n",
+			__func__, __LINE__);
+			break;
+		}
+	}
+
+	for (i = 0; i < 2; i++)
+		kfree(buf_out_free[i]);
+
+	rc = msm_gemini_ioctl_hw_cmds(pgmn_dev, arg);
+	GMN_DBG("%s:%d]\n", __func__, __LINE__);
+	return rc;
+}
+
+int msm_gemini_ioctl_reset(struct msm_gemini_device *pgmn_dev,
+	void * __user arg)
+{
+	int rc;
+	struct msm_gemini_ctrl_cmd ctrl_cmd;
+
+	GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+	if (copy_from_user(&ctrl_cmd, arg, sizeof(ctrl_cmd))) {
+		GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+		return -EFAULT;
+	}
+
+	pgmn_dev->op_mode = ctrl_cmd.type;
+
+	rc = msm_gemini_core_reset(pgmn_dev->op_mode, pgmn_dev->base,
+		resource_size(pgmn_dev->mem));
+	return rc;
+}
+
+int msm_gemini_ioctl_test_dump_region(struct msm_gemini_device *pgmn_dev,
+	unsigned long arg)
+{
+	GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+	msm_gemini_hw_region_dump(arg);
+	return 0;
+}
+
+long __msm_gemini_ioctl(struct msm_gemini_device *pgmn_dev,
+	unsigned int cmd, unsigned long arg)
+{
+	int rc = 0;
+	switch (cmd) {
+	case MSM_GMN_IOCTL_GET_HW_VERSION:
+		GMN_DBG("%s:%d] VERSION 1\n", __func__, __LINE__);
+		rc = msm_gemini_ioctl_hw_cmd(pgmn_dev, (void __user *) arg);
+		break;
+
+	case MSM_GMN_IOCTL_RESET:
+		rc = msm_gemini_ioctl_reset(pgmn_dev, (void __user *) arg);
+		break;
+
+	case MSM_GMN_IOCTL_STOP:
+		rc = msm_gemini_ioctl_hw_cmds(pgmn_dev, (void __user *) arg);
+		break;
+
+	case MSM_GMN_IOCTL_START:
+		rc = msm_gemini_start(pgmn_dev, (void __user *) arg);
+		break;
+
+	case MSM_GMN_IOCTL_INPUT_BUF_ENQUEUE:
+		rc = msm_gemini_input_buf_enqueue(pgmn_dev,
+			(void __user *) arg);
+		break;
+
+	case MSM_GMN_IOCTL_INPUT_GET:
+		rc = msm_gemini_input_get(pgmn_dev, (void __user *) arg);
+		break;
+
+	case MSM_GMN_IOCTL_INPUT_GET_UNBLOCK:
+		rc = msm_gemini_input_get_unblock(pgmn_dev);
+		break;
+
+	case MSM_GMN_IOCTL_OUTPUT_BUF_ENQUEUE:
+		rc = msm_gemini_output_buf_enqueue(pgmn_dev,
+			(void __user *) arg);
+		break;
+
+	case MSM_GMN_IOCTL_OUTPUT_GET:
+		rc = msm_gemini_output_get(pgmn_dev, (void __user *) arg);
+		break;
+
+	case MSM_GMN_IOCTL_OUTPUT_GET_UNBLOCK:
+		rc = msm_gemini_output_get_unblock(pgmn_dev);
+		break;
+
+	case MSM_GMN_IOCTL_EVT_GET:
+		rc = msm_gemini_evt_get(pgmn_dev, (void __user *) arg);
+		break;
+
+	case MSM_GMN_IOCTL_EVT_GET_UNBLOCK:
+		rc = msm_gemini_evt_get_unblock(pgmn_dev);
+		break;
+
+	case MSM_GMN_IOCTL_HW_CMD:
+		rc = msm_gemini_ioctl_hw_cmd(pgmn_dev, (void __user *) arg);
+		break;
+
+	case MSM_GMN_IOCTL_HW_CMDS:
+		rc = msm_gemini_ioctl_hw_cmds(pgmn_dev, (void __user *) arg);
+		break;
+
+	case MSM_GMN_IOCTL_TEST_DUMP_REGION:
+		rc = msm_gemini_ioctl_test_dump_region(pgmn_dev, arg);
+		break;
+
+	default:
+		GMN_PR_ERR(KERN_INFO "%s:%d] cmd = %d not supported\n",
+			__func__, __LINE__, _IOC_NR(cmd));
+		rc = -EINVAL;
+		break;
+	}
+	return rc;
+}
+
+struct msm_gemini_device *__msm_gemini_init(struct platform_device *pdev)
+{
+	struct msm_gemini_device *pgmn_dev;
+
+	pgmn_dev = kzalloc(sizeof(struct msm_gemini_device), GFP_ATOMIC);
+	if (!pgmn_dev) {
+		GMN_PR_ERR("%s:%d]no mem\n", __func__, __LINE__);
+		return NULL;
+	}
+
+	mutex_init(&pgmn_dev->lock);
+
+	pgmn_dev->pdev = pdev;
+
+	msm_gemini_q_init("evt_q", &pgmn_dev->evt_q);
+	msm_gemini_q_init("output_rtn_q", &pgmn_dev->output_rtn_q);
+	msm_gemini_q_init("output_buf_q", &pgmn_dev->output_buf_q);
+	msm_gemini_q_init("input_rtn_q", &pgmn_dev->input_rtn_q);
+	msm_gemini_q_init("input_buf_q", &pgmn_dev->input_buf_q);
+
+	return pgmn_dev;
+}
+
+int __msm_gemini_exit(struct msm_gemini_device *pgmn_dev)
+{
+	mutex_destroy(&pgmn_dev->lock);
+	kfree(pgmn_dev);
+	return 0;
+}
+
diff --git a/drivers/media/video/msm/gemini/msm_gemini_sync.h b/drivers/media/video/msm/gemini/msm_gemini_sync.h
new file mode 100644
index 0000000..1c6726d
--- /dev/null
+++ b/drivers/media/video/msm/gemini/msm_gemini_sync.h
@@ -0,0 +1,88 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MSM_GEMINI_SYNC_H
+#define MSM_GEMINI_SYNC_H
+
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/cdev.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include "msm_gemini_core.h"
+
+#define GEMINI_7X 0x1
+#define GEMINI_8X60 (0x1 << 1)
+#define GEMINI_8960 (0x1 << 2)
+
+struct msm_gemini_q {
+	char const	*name;
+	struct list_head  q;
+	spinlock_t	lck;
+	wait_queue_head_t wait;
+	int	       unblck;
+};
+
+struct msm_gemini_q_entry {
+	struct list_head list;
+	void   *data;
+};
+
+struct msm_gemini_device {
+	struct platform_device *pdev;
+	struct resource        *mem;
+	int                     irq;
+	void                   *base;
+	struct clk *gemini_clk[3];
+	struct regulator *gemini_fs;
+	uint32_t hw_version;
+
+	struct device *device;
+	struct cdev   cdev;
+	struct mutex  lock;
+	char	  open_count;
+	uint8_t       op_mode;
+
+	/* event queue including frame done & err indications
+	 */
+	struct msm_gemini_q evt_q;
+
+	/* output return queue
+	 */
+	struct msm_gemini_q output_rtn_q;
+
+	/* output buf queue
+	 */
+	struct msm_gemini_q output_buf_q;
+
+	/* input return queue
+	 */
+	struct msm_gemini_q input_rtn_q;
+
+	/* input buf queue
+	 */
+	struct msm_gemini_q input_buf_q;
+
+	struct v4l2_subdev subdev;
+};
+
+int __msm_gemini_open(struct msm_gemini_device *pgmn_dev);
+int __msm_gemini_release(struct msm_gemini_device *pgmn_dev);
+
+long __msm_gemini_ioctl(struct msm_gemini_device *pgmn_dev,
+	unsigned int cmd, unsigned long arg);
+
+struct msm_gemini_device *__msm_gemini_init(struct platform_device *pdev);
+int __msm_gemini_exit(struct msm_gemini_device *pgmn_dev);
+
+#endif /* MSM_GEMINI_SYNC_H */
diff --git a/drivers/media/video/msm/imx072.c b/drivers/media/video/msm/imx072.c
new file mode 100644
index 0000000..7e34178
--- /dev/null
+++ b/drivers/media/video/msm/imx072.c
@@ -0,0 +1,1184 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <media/msm_camera.h>
+#include <mach/gpio.h>
+#include <mach/camera.h>
+#include "imx072.h"
+
+/* SENSOR REGISTER DEFINES */
+#define REG_GROUPED_PARAMETER_HOLD		0x0104
+#define GROUPED_PARAMETER_HOLD_OFF		0x00
+#define GROUPED_PARAMETER_HOLD			0x01
+/* Integration Time */
+#define REG_COARSE_INTEGRATION_TIME		0x0202
+/* Gain */
+#define REG_GLOBAL_GAIN					0x0204
+
+/* PLL registers */
+#define REG_FRAME_LENGTH_LINES			0x0340
+#define REG_LINE_LENGTH_PCK				0x0342
+
+/* 16bit address - 8 bit context register structure */
+#define Q8  0x00000100
+#define Q10 0x00000400
+#define IMX072_MASTER_CLK_RATE 24000000
+#define IMX072_OFFSET		3
+
+/* AF Total steps parameters */
+#define IMX072_AF_I2C_ADDR	0x18
+#define IMX072_TOTAL_STEPS_NEAR_TO_FAR    30
+
+static uint16_t imx072_step_position_table[IMX072_TOTAL_STEPS_NEAR_TO_FAR+1];
+static uint16_t imx072_nl_region_boundary1;
+static uint16_t imx072_nl_region_code_per_step1;
+static uint16_t imx072_l_region_code_per_step = 12;
+static uint16_t imx072_sw_damping_time_wait = 8;
+static uint16_t imx072_af_initial_code = 350;
+static uint16_t imx072_damping_threshold = 10;
+
+struct imx072_work_t {
+	struct work_struct work;
+};
+
+static struct imx072_work_t *imx072_sensorw;
+static struct i2c_client *imx072_client;
+
+struct imx072_ctrl_t {
+	const struct  msm_camera_sensor_info *sensordata;
+
+	uint32_t sensormode;
+	uint32_t fps_divider;/* init to 1 * 0x00000400 */
+	uint32_t pict_fps_divider;/* init to 1 * 0x00000400 */
+	uint16_t fps;
+
+	uint16_t curr_lens_pos;
+	uint16_t curr_step_pos;
+	uint16_t my_reg_gain;
+	uint32_t my_reg_line_count;
+	uint16_t total_lines_per_frame;
+
+	enum imx072_resolution_t prev_res;
+	enum imx072_resolution_t pict_res;
+	enum imx072_resolution_t curr_res;
+	enum imx072_test_mode_t  set_test;
+	enum imx072_cam_mode_t cam_mode;
+};
+
+static uint16_t prev_line_length_pck;
+static uint16_t prev_frame_length_lines;
+static uint16_t snap_line_length_pck;
+static uint16_t snap_frame_length_lines;
+
+static bool CSI_CONFIG;
+static struct imx072_ctrl_t *imx072_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(imx072_wait_queue);
+DEFINE_MUTEX(imx072_mut);
+
+#ifdef CONFIG_DEBUG_FS
+static int cam_debug_init(void);
+static struct dentry *debugfs_base;
+#endif
+
+static int imx072_i2c_rxdata(unsigned short saddr,
+	unsigned char *rxdata, int length)
+{
+	struct i2c_msg msgs[] = {
+		{
+			.addr  = saddr,
+			.flags = 0,
+			.len   = length,
+			.buf   = rxdata,
+		},
+		{
+			.addr  = saddr,
+			.flags = I2C_M_RD,
+			.len   = length,
+			.buf   = rxdata,
+		},
+	};
+	if (i2c_transfer(imx072_client->adapter, msgs, 2) < 0) {
+		pr_err("imx072_i2c_rxdata faild 0x%x\n", saddr);
+		return -EIO;
+	}
+	return 0;
+}
+
+static int32_t imx072_i2c_txdata(unsigned short saddr,
+				unsigned char *txdata, int length)
+{
+	struct i2c_msg msg[] = {
+		{
+			.addr = saddr,
+			.flags = 0,
+			.len = length,
+			.buf = txdata,
+		 },
+	};
+	if (i2c_transfer(imx072_client->adapter, msg, 1) < 0) {
+		pr_err("imx072_i2c_txdata faild 0x%x\n", saddr);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int32_t imx072_i2c_read(unsigned short raddr,
+	unsigned short *rdata, int rlen)
+{
+	int32_t rc = 0;
+	unsigned char buf[2];
+	if (!rdata)
+		return -EIO;
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (raddr & 0xFF00) >> 8;
+	buf[1] = (raddr & 0x00FF);
+	rc = imx072_i2c_rxdata(imx072_client->addr>>1, buf, rlen);
+	if (rc < 0) {
+		pr_err("imx072_i2c_read 0x%x failed!\n", raddr);
+		return rc;
+	}
+	*rdata = (rlen == 2 ? buf[0] << 8 | buf[1] : buf[0]);
+	CDBG("imx072_i2c_read 0x%x val = 0x%x!\n", raddr, *rdata);
+	return rc;
+}
+
+static int32_t imx072_i2c_write_w_sensor(unsigned short waddr,
+	uint16_t wdata)
+{
+	int32_t rc = -EFAULT;
+	unsigned char buf[4];
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (waddr & 0xFF00) >> 8;
+	buf[1] = (waddr & 0x00FF);
+	buf[2] = (wdata & 0xFF00) >> 8;
+	buf[3] = (wdata & 0x00FF);
+	CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, wdata);
+	rc = imx072_i2c_txdata(imx072_client->addr>>1, buf, 4);
+	if (rc < 0) {
+		pr_err("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+			waddr, wdata);
+	}
+	return rc;
+}
+
+static int32_t imx072_i2c_write_b_sensor(unsigned short waddr,
+	uint8_t bdata)
+{
+	int32_t rc = -EFAULT;
+	unsigned char buf[3];
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (waddr & 0xFF00) >> 8;
+	buf[1] = (waddr & 0x00FF);
+	buf[2] = bdata;
+	CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, bdata);
+	rc = imx072_i2c_txdata(imx072_client->addr>>1, buf, 3);
+	if (rc < 0)
+		pr_err("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+			waddr, bdata);
+	return rc;
+}
+
+static int32_t imx072_i2c_write_b_af(uint8_t msb, uint8_t lsb)
+{
+	int32_t rc = -EFAULT;
+	unsigned char buf[2];
+
+	buf[0] = msb;
+	buf[1] = lsb;
+	rc = imx072_i2c_txdata(IMX072_AF_I2C_ADDR>>1, buf, 2);
+	if (rc < 0)
+		pr_err("af_i2c_write faield msb = 0x%x lsb = 0x%x",
+			msb, lsb);
+	return rc;
+}
+
+static int32_t imx072_i2c_write_w_table(struct imx072_i2c_reg_conf const
+					 *reg_conf_tbl, int num)
+{
+	int i;
+	int32_t rc = -EIO;
+	for (i = 0; i < num; i++) {
+		rc = imx072_i2c_write_b_sensor(reg_conf_tbl->waddr,
+			reg_conf_tbl->wdata);
+		if (rc < 0)
+			break;
+		reg_conf_tbl++;
+	}
+	return rc;
+}
+
+static void imx072_group_hold_on(void)
+{
+	imx072_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+						GROUPED_PARAMETER_HOLD);
+}
+
+static void imx072_group_hold_off(void)
+{
+	imx072_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+						GROUPED_PARAMETER_HOLD_OFF);
+}
+
+static void imx072_start_stream(void)
+{
+	imx072_i2c_write_b_sensor(0x0100, 0x01);
+}
+
+static void imx072_stop_stream(void)
+{
+	imx072_i2c_write_b_sensor(0x0100, 0x00);
+}
+
+static void imx072_get_pict_fps(uint16_t fps, uint16_t *pfps)
+{
+	/* input fps is preview fps in Q8 format */
+	uint32_t divider, d1, d2;
+
+	d1 = prev_frame_length_lines * 0x00000400 / snap_frame_length_lines;
+	d2 = prev_line_length_pck * 0x00000400 / snap_line_length_pck;
+	divider = d1 * d2 / 0x400;
+
+	/*Verify PCLK settings and frame sizes.*/
+	*pfps = (uint16_t) (fps * divider / 0x400);
+}
+
+static uint16_t imx072_get_prev_lines_pf(void)
+{
+	return prev_frame_length_lines;
+}
+
+static uint16_t imx072_get_prev_pixels_pl(void)
+{
+	return prev_line_length_pck;
+}
+
+static uint16_t imx072_get_pict_lines_pf(void)
+{
+	return snap_frame_length_lines;
+}
+
+static uint16_t imx072_get_pict_pixels_pl(void)
+{
+	return snap_line_length_pck;
+}
+
+static uint32_t imx072_get_pict_max_exp_lc(void)
+{
+	return snap_frame_length_lines  * 24;
+}
+
+static int32_t imx072_set_fps(struct fps_cfg   *fps)
+{
+	uint16_t total_lines_per_frame;
+	int32_t rc = 0;
+	total_lines_per_frame = (uint16_t)
+		((prev_frame_length_lines *
+		imx072_ctrl->fps_divider)/0x400);
+	imx072_ctrl->fps_divider = fps->fps_div;
+	imx072_ctrl->pict_fps_divider = fps->pict_fps_div;
+
+	imx072_group_hold_on();
+	rc = imx072_i2c_write_w_sensor(REG_FRAME_LENGTH_LINES,
+							total_lines_per_frame);
+	imx072_group_hold_off();
+	return rc;
+}
+
+static int32_t imx072_write_exp_gain(uint16_t gain, uint32_t line)
+{
+	uint32_t fl_lines = 0;
+	uint8_t offset;
+	int32_t rc = 0;
+	if (imx072_ctrl->curr_res == imx072_ctrl->prev_res)
+		fl_lines = prev_frame_length_lines;
+	else if (imx072_ctrl->curr_res == imx072_ctrl->pict_res)
+		fl_lines = snap_frame_length_lines;
+	line = (line * imx072_ctrl->fps_divider) / Q10;
+	offset = IMX072_OFFSET;
+	if (line > (fl_lines - offset))
+		fl_lines = line + offset;
+
+	imx072_group_hold_on();
+	rc = imx072_i2c_write_w_sensor(REG_FRAME_LENGTH_LINES, fl_lines);
+	rc = imx072_i2c_write_w_sensor(REG_COARSE_INTEGRATION_TIME, line);
+	rc = imx072_i2c_write_w_sensor(REG_GLOBAL_GAIN, gain);
+	imx072_group_hold_off();
+	return rc;
+}
+
+static int32_t imx072_set_pict_exp_gain(uint16_t gain, uint32_t line)
+{
+	int32_t rc = 0;
+	rc = imx072_write_exp_gain(gain, line);
+	return rc;
+}
+
+static int32_t imx072_sensor_setting(int update_type, int rt)
+{
+
+	int32_t rc = 0;
+	struct msm_camera_csi_params imx072_csi_params;
+
+	imx072_stop_stream();
+	msleep(30);
+	if (update_type == REG_INIT) {
+		msleep(20);
+		CSI_CONFIG = 0;
+		imx072_i2c_write_w_table(imx072_regs.rec_settings,
+			imx072_regs.rec_size);
+	} else if (update_type == UPDATE_PERIODIC) {
+#ifdef CONFIG_DEBUG_FS
+		cam_debug_init();
+#endif
+		msleep(20);
+		if (!CSI_CONFIG) {
+			imx072_csi_params.lane_cnt = 2;
+			imx072_csi_params.data_format = CSI_10BIT;
+			imx072_csi_params.lane_assign = 0xe4;
+			imx072_csi_params.dpcm_scheme = 0;
+			imx072_csi_params.settle_cnt = 0x18;
+			msm_camio_vfe_clk_rate_set(192000000);
+			rc = msm_camio_csi_config(&imx072_csi_params);
+			msleep(100);
+			CSI_CONFIG = 1;
+		}
+		imx072_i2c_write_w_table(
+			imx072_regs.conf_array[rt].conf,
+			imx072_regs.conf_array[rt].size);
+		imx072_start_stream();
+		msleep(30);
+	}
+	return rc;
+}
+
+static int32_t imx072_video_config(int mode)
+{
+
+	int32_t rc = 0;
+	/* change sensor resolution if needed */
+	if (imx072_sensor_setting(UPDATE_PERIODIC,
+		imx072_ctrl->prev_res) < 0)
+		return rc;
+
+	imx072_ctrl->curr_res = imx072_ctrl->prev_res;
+	imx072_ctrl->sensormode = mode;
+	return rc;
+}
+
+static int32_t imx072_snapshot_config(int mode)
+{
+	int32_t rc = 0;
+	/*change sensor resolution if needed */
+	if (imx072_ctrl->curr_res != imx072_ctrl->pict_res) {
+		if (imx072_sensor_setting(UPDATE_PERIODIC,
+					imx072_ctrl->pict_res) < 0)
+			return rc;
+	}
+
+	imx072_ctrl->curr_res = imx072_ctrl->pict_res;
+	imx072_ctrl->sensormode = mode;
+	return rc;
+}
+
+static int32_t imx072_raw_snapshot_config(int mode)
+{
+	int32_t rc = 0;
+	/* change sensor resolution if needed */
+	if (imx072_ctrl->curr_res != imx072_ctrl->pict_res) {
+		if (imx072_sensor_setting(UPDATE_PERIODIC,
+					imx072_ctrl->pict_res) < 0)
+			return rc;
+	}
+
+	imx072_ctrl->curr_res = imx072_ctrl->pict_res;
+	imx072_ctrl->sensormode = mode;
+	return rc;
+}
+
+static int32_t imx072_mode_init(int mode, struct sensor_init_cfg init_info)
+{
+	int32_t rc = 0;
+	CDBG("%s: %d\n", __func__, __LINE__);
+	if (mode != imx072_ctrl->cam_mode) {
+		imx072_ctrl->prev_res = init_info.prev_res;
+		imx072_ctrl->pict_res = init_info.pict_res;
+		imx072_ctrl->cam_mode = mode;
+
+		prev_frame_length_lines =
+			imx072_regs.conf_array[imx072_ctrl->prev_res].
+			conf[IMX072_FRAME_LENGTH_LINES_HI].wdata << 8 |
+			imx072_regs.conf_array[imx072_ctrl->prev_res].
+			conf[IMX072_FRAME_LENGTH_LINES_LO].wdata;
+		prev_line_length_pck =
+			imx072_regs.conf_array[imx072_ctrl->prev_res].
+			conf[IMX072_LINE_LENGTH_PCK_HI].wdata << 8 |
+			imx072_regs.conf_array[imx072_ctrl->prev_res].
+			conf[IMX072_LINE_LENGTH_PCK_LO].wdata;
+		snap_frame_length_lines =
+			imx072_regs.conf_array[imx072_ctrl->pict_res].
+			conf[IMX072_FRAME_LENGTH_LINES_HI].wdata << 8 |
+			imx072_regs.conf_array[imx072_ctrl->pict_res].
+			conf[IMX072_FRAME_LENGTH_LINES_LO].wdata;
+		snap_line_length_pck =
+			imx072_regs.conf_array[imx072_ctrl->pict_res].
+			conf[IMX072_LINE_LENGTH_PCK_HI].wdata << 8 |
+			imx072_regs.conf_array[imx072_ctrl->pict_res].
+			conf[IMX072_LINE_LENGTH_PCK_LO].wdata;
+
+		rc = imx072_sensor_setting(REG_INIT,
+			imx072_ctrl->prev_res);
+	}
+	return rc;
+}
+
+static int32_t imx072_set_sensor_mode(int mode,
+	int res)
+{
+	int32_t rc = 0;
+
+	switch (mode) {
+	case SENSOR_PREVIEW_MODE:
+		imx072_ctrl->prev_res = res;
+		rc = imx072_video_config(mode);
+		break;
+	case SENSOR_SNAPSHOT_MODE:
+		imx072_ctrl->pict_res = res;
+		rc = imx072_snapshot_config(mode);
+		break;
+	case SENSOR_RAW_SNAPSHOT_MODE:
+		imx072_ctrl->pict_res = res;
+		rc = imx072_raw_snapshot_config(mode);
+		break;
+	default:
+		rc = -EINVAL;
+		break;
+	}
+	return rc;
+}
+
+#define DIV_CEIL(x, y) ((x/y + ((x%y) ? 1 : 0)))
+static int32_t imx072_move_focus(int direction,
+	int32_t num_steps)
+{
+	int32_t rc = 0;
+	int16_t step_direction, dest_lens_position, dest_step_position;
+	uint8_t code_val_msb, code_val_lsb;
+	int16_t next_lens_position, target_dist, small_step;
+
+	if (direction == MOVE_NEAR)
+		step_direction = 1;
+	else if (direction == MOVE_FAR)
+		step_direction = -1;
+	else {
+		pr_err("Illegal focus direction\n");
+		return -EINVAL;
+	}
+	dest_step_position = imx072_ctrl->curr_step_pos +
+			(step_direction * num_steps);
+
+	if (dest_step_position < 0)
+		dest_step_position = 0;
+	else if (dest_step_position > IMX072_TOTAL_STEPS_NEAR_TO_FAR)
+		dest_step_position = IMX072_TOTAL_STEPS_NEAR_TO_FAR;
+
+	if (dest_step_position == imx072_ctrl->curr_step_pos) {
+		CDBG("imx072 same position No-Move exit\n");
+		return rc;
+	}
+	CDBG("%s Index = [%d]\n", __func__, dest_step_position);
+
+	dest_lens_position = imx072_step_position_table[dest_step_position];
+	CDBG("%s lens_position value = %d\n", __func__, dest_lens_position);
+	target_dist = step_direction * (dest_lens_position -
+		imx072_ctrl->curr_lens_pos);
+	if (step_direction < 0 && (target_dist >=
+		(imx072_step_position_table[imx072_damping_threshold]
+			- imx072_af_initial_code))) {
+		small_step = DIV_CEIL(target_dist, 10);
+		imx072_sw_damping_time_wait = 30;
+	} else {
+		small_step = DIV_CEIL(target_dist, 4);
+		imx072_sw_damping_time_wait = 20;
+	}
+
+	CDBG("%s: small_step:%d, wait_time:%d\n", __func__, small_step,
+		imx072_sw_damping_time_wait);
+	for (next_lens_position = imx072_ctrl->curr_lens_pos +
+		(step_direction * small_step);
+		(step_direction * next_lens_position) <=
+		(step_direction * dest_lens_position);
+		next_lens_position += (step_direction * small_step)) {
+
+		code_val_msb = ((next_lens_position & 0x03F0) >> 4);
+		code_val_lsb = ((next_lens_position & 0x000F) << 4);
+		CDBG("position value = %d\n", next_lens_position);
+		CDBG("movefocus vcm_msb = %d\n", code_val_msb);
+		CDBG("movefocus vcm_lsb = %d\n", code_val_lsb);
+		rc = imx072_i2c_write_b_af(code_val_msb, code_val_lsb);
+		if (rc < 0) {
+			pr_err("imx072_move_focus failed writing i2c\n");
+			return rc;
+			}
+		imx072_ctrl->curr_lens_pos = next_lens_position;
+		usleep(imx072_sw_damping_time_wait*100);
+	}
+	if (imx072_ctrl->curr_lens_pos != dest_lens_position) {
+		code_val_msb = ((dest_lens_position & 0x03F0) >> 4);
+		code_val_lsb = ((dest_lens_position & 0x000F) << 4);
+		CDBG("position value = %d\n", dest_lens_position);
+		CDBG("movefocus vcm_msb = %d\n", code_val_msb);
+		CDBG("movefocus vcm_lsb = %d\n", code_val_lsb);
+		rc = imx072_i2c_write_b_af(code_val_msb, code_val_lsb);
+		if (rc < 0) {
+			pr_err("imx072_move_focus failed writing i2c\n");
+			return rc;
+			}
+		usleep(imx072_sw_damping_time_wait * 100);
+	}
+	imx072_ctrl->curr_lens_pos = dest_lens_position;
+	imx072_ctrl->curr_step_pos = dest_step_position;
+	return rc;
+
+}
+
+static int32_t imx072_init_focus(void)
+{
+	uint8_t i;
+	int32_t rc = 0;
+
+	imx072_step_position_table[0] = imx072_af_initial_code;
+	for (i = 1; i <= IMX072_TOTAL_STEPS_NEAR_TO_FAR; i++) {
+		if (i <= imx072_nl_region_boundary1)
+			imx072_step_position_table[i] =
+				imx072_step_position_table[i-1]
+				+ imx072_nl_region_code_per_step1;
+		else
+			imx072_step_position_table[i] =
+				imx072_step_position_table[i-1]
+				+ imx072_l_region_code_per_step;
+
+		if (imx072_step_position_table[i] > 1023)
+			imx072_step_position_table[i] = 1023;
+	}
+	imx072_ctrl->curr_lens_pos = 0;
+
+	return rc;
+}
+
+static int32_t imx072_set_default_focus(void)
+{
+	int32_t rc = 0;
+	uint8_t code_val_msb, code_val_lsb;
+	int16_t dest_lens_position = 0;
+
+	CDBG("%s Index = [%d]\n", __func__, 0);
+	if (imx072_ctrl->curr_step_pos != 0)
+		rc = imx072_move_focus(MOVE_FAR,
+		imx072_ctrl->curr_step_pos);
+	else {
+		dest_lens_position = imx072_af_initial_code;
+		code_val_msb = ((dest_lens_position & 0x03F0) >> 4);
+		code_val_lsb = ((dest_lens_position & 0x000F) << 4);
+
+		CDBG("position value = %d\n", dest_lens_position);
+		CDBG("movefocus vcm_msb = %d\n", code_val_msb);
+		CDBG("movefocus vcm_lsb = %d\n", code_val_lsb);
+		rc = imx072_i2c_write_b_af(code_val_msb, code_val_lsb);
+		if (rc < 0) {
+			pr_err("imx072_set_default_focus failed writing i2c\n");
+			return rc;
+		}
+
+		imx072_ctrl->curr_lens_pos = dest_lens_position;
+		imx072_ctrl->curr_step_pos = 0;
+
+	}
+	usleep(5000);
+	return rc;
+}
+
+static int32_t imx072_af_power_down(void)
+{
+	int32_t rc = 0;
+	int32_t i = 0;
+	int16_t dest_lens_position = imx072_af_initial_code;
+
+	if (imx072_ctrl->curr_lens_pos != 0) {
+		rc = imx072_set_default_focus();
+		CDBG("%s after imx072_set_default_focus\n", __func__);
+		msleep(40);
+		/*to avoid the sound during the power off.
+		brings the actuator to mechanical infinity gradually.*/
+		for (i = 0; i < IMX072_TOTAL_STEPS_NEAR_TO_FAR; i++) {
+			dest_lens_position = dest_lens_position -
+				(imx072_af_initial_code /
+					IMX072_TOTAL_STEPS_NEAR_TO_FAR);
+			CDBG("position value = %d\n", dest_lens_position);
+			rc = imx072_i2c_write_b_af(
+				((dest_lens_position & 0x03F0) >> 4),
+				((dest_lens_position & 0x000F) << 4));
+			CDBG("count = %d\n", i);
+			msleep(20);
+			if (rc < 0) {
+				pr_err("imx072_set_default_focus failed writing i2c\n");
+				return rc;
+			}
+		}
+		rc = imx072_i2c_write_b_af(0x00, 00);
+		msleep(40);
+	}
+	rc = imx072_i2c_write_b_af(0x80, 00);
+	return rc;
+}
+
+static int32_t imx072_power_down(void)
+{
+	int32_t rc = 0;
+
+	rc = imx072_af_power_down();
+	return rc;
+}
+
+static int imx072_probe_init_done(const struct msm_camera_sensor_info *data)
+{
+	pr_err("probe done\n");
+	gpio_free(data->sensor_reset);
+	return 0;
+}
+
+static int imx072_probe_init_sensor(
+	const struct msm_camera_sensor_info *data)
+{
+	int32_t rc = 0;
+	uint16_t chipid = 0;
+
+	CDBG("%s: %d\n", __func__, __LINE__);
+	rc = gpio_request(data->sensor_reset, "imx072");
+	CDBG(" imx072_probe_init_sensor\n");
+	if (!rc) {
+		pr_err("sensor_reset = %d\n", rc);
+		gpio_direction_output(data->sensor_reset, 0);
+		msleep(50);
+		gpio_set_value_cansleep(data->sensor_reset, 1);
+		msleep(20);
+	} else
+		goto gpio_req_fail;
+
+	CDBG(" imx072_probe_init_sensor is called\n");
+	rc = imx072_i2c_read(0x0, &chipid, 2);
+	CDBG("ID: %d\n", chipid);
+	/* 4. Compare sensor ID to IMX072 ID: */
+	if (chipid != 0x0045) {
+		rc = -ENODEV;
+		pr_err("imx072_probe_init_sensor chip id doesnot match\n");
+		goto init_probe_fail;
+	}
+
+	return rc;
+init_probe_fail:
+	pr_err(" imx072_probe_init_sensor fails\n");
+	gpio_set_value_cansleep(data->sensor_reset, 0);
+	imx072_probe_init_done(data);
+	if (data->vcm_enable) {
+		int ret = gpio_request(data->vcm_pwd, "imx072_af");
+		if (!ret) {
+			gpio_direction_output(data->vcm_pwd, 0);
+			msleep(20);
+			gpio_free(data->vcm_pwd);
+		}
+	}
+gpio_req_fail:
+	return rc;
+}
+
+int imx072_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+	int32_t rc = 0;
+
+	CDBG("%s: %d\n", __func__, __LINE__);
+	imx072_ctrl = kzalloc(sizeof(struct imx072_ctrl_t), GFP_KERNEL);
+	if (!imx072_ctrl) {
+		pr_err("imx072_init failed!\n");
+		rc = -ENOMEM;
+		goto init_done;
+	}
+	imx072_ctrl->fps_divider = 1 * 0x00000400;
+	imx072_ctrl->pict_fps_divider = 1 * 0x00000400;
+	imx072_ctrl->set_test = TEST_OFF;
+	imx072_ctrl->cam_mode = MODE_INVALID;
+
+	if (data)
+		imx072_ctrl->sensordata = data;
+	if (rc < 0) {
+		pr_err("Calling imx072_sensor_open_init fail1\n");
+		return rc;
+	}
+	CDBG("%s: %d\n", __func__, __LINE__);
+	/* enable mclk first */
+	msm_camio_clk_rate_set(IMX072_MASTER_CLK_RATE);
+	rc = imx072_probe_init_sensor(data);
+	if (rc < 0)
+		goto init_fail;
+
+	imx072_init_focus();
+	imx072_ctrl->fps = 30*Q8;
+	if (rc < 0) {
+		gpio_set_value_cansleep(data->sensor_reset, 0);
+		goto init_fail;
+	} else
+		goto init_done;
+init_fail:
+	pr_err("init_fail\n");
+	imx072_probe_init_done(data);
+init_done:
+	pr_err("init_done\n");
+	return rc;
+}
+
+static int imx072_init_client(struct i2c_client *client)
+{
+	/* Initialize the MSM_CAMI2C Chip */
+	init_waitqueue_head(&imx072_wait_queue);
+	return 0;
+}
+
+static const struct i2c_device_id imx072_i2c_id[] = {
+	{"imx072", 0},
+	{ }
+};
+
+static int imx072_i2c_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	int rc = 0;
+	CDBG("imx072_probe called!\n");
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		pr_err("i2c_check_functionality failed\n");
+		goto probe_failure;
+	}
+
+	imx072_sensorw = kzalloc(sizeof(struct imx072_work_t),
+			GFP_KERNEL);
+	if (!imx072_sensorw) {
+		pr_err("kzalloc failed.\n");
+		rc = -ENOMEM;
+		goto probe_failure;
+	}
+
+	i2c_set_clientdata(client, imx072_sensorw);
+	imx072_init_client(client);
+	imx072_client = client;
+
+	msleep(50);
+
+	CDBG("imx072_probe successed! rc = %d\n", rc);
+	return 0;
+
+probe_failure:
+	pr_err("imx072_probe failed! rc = %d\n", rc);
+	return rc;
+}
+
+static int imx072_send_wb_info(struct wb_info_cfg *wb)
+{
+	return 0;
+
+}
+
+static int __exit imx072_remove(struct i2c_client *client)
+{
+	struct imx072_work_t_t *sensorw = i2c_get_clientdata(client);
+	free_irq(client->irq, sensorw);
+	imx072_client = NULL;
+	kfree(sensorw);
+	return 0;
+}
+
+static struct i2c_driver imx072_i2c_driver = {
+	.id_table = imx072_i2c_id,
+	.probe  = imx072_i2c_probe,
+	.remove = __exit_p(imx072_i2c_remove),
+	.driver = {
+		.name = "imx072",
+	},
+};
+
+int imx072_sensor_config(void __user *argp)
+{
+	struct sensor_cfg_data cdata;
+	long   rc = 0;
+	if (copy_from_user(&cdata,
+		(void *)argp,
+		sizeof(struct sensor_cfg_data)))
+		return -EFAULT;
+	mutex_lock(&imx072_mut);
+	CDBG("imx072_sensor_config: cfgtype = %d\n",
+		 cdata.cfgtype);
+	switch (cdata.cfgtype) {
+	case CFG_GET_PICT_FPS:
+		imx072_get_pict_fps(
+			cdata.cfg.gfps.prevfps,
+			&(cdata.cfg.gfps.pictfps));
+
+		if (copy_to_user((void *)argp,
+			&cdata,
+			sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+	case CFG_GET_PREV_L_PF:
+		cdata.cfg.prevl_pf =
+		imx072_get_prev_lines_pf();
+
+		if (copy_to_user((void *)argp,
+			&cdata,
+			sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+	case CFG_GET_PREV_P_PL:
+		cdata.cfg.prevp_pl =
+			imx072_get_prev_pixels_pl();
+
+		if (copy_to_user((void *)argp,
+			&cdata,
+			sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+	case CFG_GET_PICT_L_PF:
+		cdata.cfg.pictl_pf =
+			imx072_get_pict_lines_pf();
+
+		if (copy_to_user((void *)argp,
+			&cdata,
+			sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+	case CFG_GET_PICT_P_PL:
+		cdata.cfg.pictp_pl =
+			imx072_get_pict_pixels_pl();
+
+		if (copy_to_user((void *)argp,
+			&cdata,
+			sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+	case CFG_GET_PICT_MAX_EXP_LC:
+		cdata.cfg.pict_max_exp_lc =
+			imx072_get_pict_max_exp_lc();
+
+		if (copy_to_user((void *)argp,
+			&cdata,
+			sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+	case CFG_SET_FPS:
+	case CFG_SET_PICT_FPS:
+		rc = imx072_set_fps(&(cdata.cfg.fps));
+		break;
+	case CFG_SET_EXP_GAIN:
+		rc = imx072_write_exp_gain(
+			cdata.cfg.exp_gain.gain,
+			cdata.cfg.exp_gain.line);
+		break;
+	case CFG_SET_PICT_EXP_GAIN:
+		rc = imx072_set_pict_exp_gain(
+			cdata.cfg.exp_gain.gain,
+			cdata.cfg.exp_gain.line);
+		break;
+	case CFG_SET_MODE:
+		rc = imx072_set_sensor_mode(cdata.mode, cdata.rs);
+		break;
+	case CFG_PWR_DOWN:
+		rc = imx072_power_down();
+		break;
+	case CFG_MOVE_FOCUS:
+		rc = imx072_move_focus(cdata.cfg.focus.dir,
+				cdata.cfg.focus.steps);
+		break;
+	case CFG_SET_DEFAULT_FOCUS:
+		imx072_set_default_focus();
+		break;
+	case CFG_GET_AF_MAX_STEPS:
+		cdata.max_steps = IMX072_TOTAL_STEPS_NEAR_TO_FAR;
+		if (copy_to_user((void *)argp,
+			&cdata,
+			sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+	case CFG_SET_EFFECT:
+		break;
+	case CFG_SEND_WB_INFO:
+		rc = imx072_send_wb_info(
+			&(cdata.cfg.wb_info));
+	break;
+	case CFG_SENSOR_INIT:
+		rc = imx072_mode_init(cdata.mode,
+				cdata.cfg.init_info);
+	break;
+	case CFG_SET_LENS_SHADING:
+		break;
+	default:
+		rc = -EFAULT;
+		break;
+	}
+
+	mutex_unlock(&imx072_mut);
+
+	return rc;
+}
+
+static int imx072_sensor_release(void)
+{
+	int rc = -EBADF;
+	mutex_lock(&imx072_mut);
+	imx072_power_down();
+	gpio_set_value_cansleep(imx072_ctrl->sensordata->sensor_reset, 0);
+	msleep(20);
+	gpio_free(imx072_ctrl->sensordata->sensor_reset);
+	if (imx072_ctrl->sensordata->vcm_enable) {
+		gpio_set_value_cansleep(imx072_ctrl->sensordata->vcm_pwd, 0);
+		gpio_free(imx072_ctrl->sensordata->vcm_pwd);
+	}
+	kfree(imx072_ctrl);
+	imx072_ctrl = NULL;
+	pr_err("imx072_release completed\n");
+	mutex_unlock(&imx072_mut);
+
+	return rc;
+}
+
+static int imx072_sensor_probe(const struct msm_camera_sensor_info *info,
+		struct msm_sensor_ctrl *s)
+{
+	int rc = 0;
+	rc = i2c_add_driver(&imx072_i2c_driver);
+	if (rc < 0 || imx072_client == NULL) {
+		rc = -ENOTSUPP;
+		pr_err("I2C add driver failed");
+		goto probe_fail;
+	}
+	msm_camio_clk_rate_set(IMX072_MASTER_CLK_RATE);
+	rc = imx072_probe_init_sensor(info);
+	if (rc < 0)
+		goto probe_fail;
+	s->s_init = imx072_sensor_open_init;
+	s->s_release = imx072_sensor_release;
+	s->s_config  = imx072_sensor_config;
+	s->s_mount_angle = info->sensor_platform_info->mount_angle;
+
+	gpio_set_value_cansleep(info->sensor_reset, 0);
+	imx072_probe_init_done(info);
+	if (info->vcm_enable) {
+		rc = gpio_request(info->vcm_pwd, "imx072_af");
+		if (!rc) {
+			gpio_direction_output(info->vcm_pwd, 0);
+			msleep(20);
+			gpio_free(info->vcm_pwd);
+		} else
+			return rc;
+	}
+	pr_info("imx072_sensor_probe : SUCCESS\n");
+	return rc;
+
+probe_fail:
+	pr_err("imx072_sensor_probe: SENSOR PROBE FAILS!\n");
+	return rc;
+}
+
+static int __imx072_probe(struct platform_device *pdev)
+{
+	return msm_camera_drv_start(pdev, imx072_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+	.probe = __imx072_probe,
+	.driver = {
+		.name = "msm_camera_imx072",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init imx072_init(void)
+{
+	return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(imx072_init);
+void imx072_exit(void)
+{
+	i2c_del_driver(&imx072_i2c_driver);
+}
+MODULE_DESCRIPTION("Aptina 8 MP Bayer sensor driver");
+MODULE_LICENSE("GPL v2");
+
+#ifdef CONFIG_DEBUG_FS
+static bool streaming = 1;
+
+static int cam_debug_stream_set(void *data, u64 val)
+{
+	int rc = 0;
+
+	if (val) {
+		imx072_start_stream();
+		streaming = 1;
+	} else {
+		imx072_stop_stream();
+		streaming = 0;
+	}
+
+	return rc;
+}
+
+static int cam_debug_stream_get(void *data, u64 *val)
+{
+	*val = streaming;
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(cam_stream, cam_debug_stream_get,
+			cam_debug_stream_set, "%llu\n");
+
+
+
+static int imx072_set_af_codestep(void *data, u64 val)
+{
+	imx072_l_region_code_per_step = val;
+	imx072_init_focus();
+	return 0;
+}
+
+static int imx072_get_af_codestep(void *data, u64 *val)
+{
+	*val = imx072_l_region_code_per_step;
+	return 0;
+}
+
+static uint16_t imx072_linear_total_step = IMX072_TOTAL_STEPS_NEAR_TO_FAR;
+static int imx072_set_linear_total_step(void *data, u64 val)
+{
+	imx072_linear_total_step = val;
+	return 0;
+}
+
+static int imx072_af_linearity_test(void *data, u64 *val)
+{
+	int i = 0;
+
+	imx072_set_default_focus();
+	msleep(3000);
+	for (i = 0; i < imx072_linear_total_step; i++) {
+		imx072_move_focus(MOVE_NEAR, 1);
+		CDBG("moved to index =[%d]\n", i);
+		msleep(1000);
+	}
+
+	for (i = 0; i < imx072_linear_total_step; i++) {
+		imx072_move_focus(MOVE_FAR, 1);
+		CDBG("moved to index =[%d]\n", i);
+		msleep(1000);
+	}
+	return 0;
+}
+
+static uint16_t imx072_step_val = IMX072_TOTAL_STEPS_NEAR_TO_FAR;
+static uint8_t imx072_step_dir = MOVE_NEAR;
+static int imx072_af_step_config(void *data, u64 val)
+{
+	imx072_step_val = val & 0xFFFF;
+	imx072_step_dir = (val >> 16) & 0x1;
+	return 0;
+}
+
+static int imx072_af_step(void *data, u64 *val)
+{
+	int i = 0;
+	int dir = MOVE_NEAR;
+	imx072_set_default_focus();
+	msleep(3000);
+	if (imx072_step_dir == 1)
+		dir = MOVE_FAR;
+
+	for (i = 0; i < imx072_step_val; i += 4) {
+		imx072_move_focus(dir, 4);
+		msleep(1000);
+	}
+	imx072_set_default_focus();
+	msleep(3000);
+	return 0;
+}
+
+static int imx072_af_set_resolution(void *data, u64 val)
+{
+	imx072_init_focus();
+	return 0;
+}
+
+static int imx072_af_get_resolution(void *data, u64 *val)
+{
+	*val = 0xFF;
+	return 0;
+}
+
+
+
+DEFINE_SIMPLE_ATTRIBUTE(af_codeperstep, imx072_get_af_codestep,
+			imx072_set_af_codestep, "%llu\n");
+
+DEFINE_SIMPLE_ATTRIBUTE(af_linear, imx072_af_linearity_test,
+			imx072_set_linear_total_step, "%llu\n");
+
+DEFINE_SIMPLE_ATTRIBUTE(af_step, imx072_af_step,
+			imx072_af_step_config, "%llu\n");
+
+DEFINE_SIMPLE_ATTRIBUTE(af_step_res, imx072_af_get_resolution,
+			imx072_af_set_resolution, "%llu\n");
+
+static int cam_debug_init(void)
+{
+	struct dentry *cam_dir;
+	debugfs_base = debugfs_create_dir("sensor", NULL);
+	if (!debugfs_base)
+		return -ENOMEM;
+
+	cam_dir = debugfs_create_dir("imx072", debugfs_base);
+	if (!cam_dir)
+		return -ENOMEM;
+
+	if (!debugfs_create_file("stream", S_IRUGO | S_IWUSR, cam_dir,
+							 NULL, &cam_stream))
+		return -ENOMEM;
+
+	if (!debugfs_create_file("af_codeperstep", S_IRUGO | S_IWUSR, cam_dir,
+							 NULL, &af_codeperstep))
+		return -ENOMEM;
+	if (!debugfs_create_file("af_linear", S_IRUGO | S_IWUSR, cam_dir,
+							 NULL, &af_linear))
+		return -ENOMEM;
+	if (!debugfs_create_file("af_step", S_IRUGO | S_IWUSR, cam_dir,
+							 NULL, &af_step))
+		return -ENOMEM;
+
+	if (!debugfs_create_file("af_step_res", S_IRUGO | S_IWUSR, cam_dir,
+							 NULL, &af_step_res))
+		return -ENOMEM;
+
+	return 0;
+}
+#endif
diff --git a/drivers/media/video/msm/imx072.h b/drivers/media/video/msm/imx072.h
new file mode 100644
index 0000000..e3d279f
--- /dev/null
+++ b/drivers/media/video/msm/imx072.h
@@ -0,0 +1,79 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef IMX072_H
+#define IMX072_H
+#include <linux/types.h>
+#include <mach/board.h>
+extern struct imx072_reg imx072_regs;
+
+struct imx072_i2c_reg_conf {
+	unsigned short waddr;
+	unsigned short wdata;
+};
+
+struct imx072_i2c_conf_array {
+	struct imx072_i2c_reg_conf *conf;
+	unsigned short size;
+};
+
+enum imx072_test_mode_t {
+	TEST_OFF,
+	TEST_1,
+	TEST_2,
+	TEST_3
+};
+
+enum imx072_resolution_t {
+	QTR_2D_SIZE,
+	FULL_2D_SIZE,
+	QTR_3D_SIZE,
+	FULL_3D_SIZE,
+	INVALID_SIZE
+};
+enum imx072_setting {
+	RES_PREVIEW,
+	RES_CAPTURE,
+	RES_3D_PREVIEW,
+	RES_3D_CAPTURE
+};
+enum imx072_cam_mode_t {
+	MODE_2D_RIGHT,
+	MODE_2D_LEFT,
+	MODE_3D,
+	MODE_INVALID
+};
+enum imx072_reg_update {
+	/* Sensor egisters that need to be updated during initialization */
+	REG_INIT,
+	/* Sensor egisters that needs periodic I2C writes */
+	UPDATE_PERIODIC,
+	/* All the sensor Registers will be updated */
+	UPDATE_ALL,
+	/* Not valid update */
+	UPDATE_INVALID
+};
+
+enum imx072_reg_mode {
+	IMX072_FRAME_LENGTH_LINES_HI = 0,
+	IMX072_FRAME_LENGTH_LINES_LO,
+	IMX072_LINE_LENGTH_PCK_HI,
+	IMX072_LINE_LENGTH_PCK_LO,
+};
+
+struct imx072_reg {
+	const struct imx072_i2c_reg_conf *rec_settings;
+	const unsigned short rec_size;
+	const struct imx072_i2c_conf_array *conf_array;
+};
+#endif /* IMX072_H */
diff --git a/drivers/media/video/msm/imx072_reg.c b/drivers/media/video/msm/imx072_reg.c
new file mode 100644
index 0000000..ea75548
--- /dev/null
+++ b/drivers/media/video/msm/imx072_reg.c
@@ -0,0 +1,153 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "imx072.h"
+
+struct imx072_i2c_reg_conf imx072_prev_settings[] = {
+	{0x0340, 0x03},/*frame_length*/
+	{0x0341, 0xF7},/*frame_length*/
+	{0x0342, 0x0A},/*line_length*/
+	{0x0343, 0xE0},/*line_length*/
+	{0x0344, 0x00},/*x_addr_start*/
+	{0x0345, 0x00},/*x_addr_start*/
+	{0x0346, 0x00},/*y_addr_start*/
+	{0x0347, 0x00},/*y_addr_start*/
+	{0x0348, 0x0A},/*x_addr_end*/
+	{0x0349, 0x2F},/*x_addr_end*/
+	{0x034A, 0x07},/*y_addr_end*/
+	{0x034B, 0xA7},/*y_addr_end*/
+	{0x034C, 0x05},/*x_out_size*/
+	{0x034D, 0x18},/*x_out_size*/
+	{0x034E, 0x03},/*y_out_size*/
+	{0x034F, 0xD4},/*y_out_size*/
+	{0x0381, 0x01},/*x_even_inc*/
+	{0x0383, 0x03},/*x_odd_inc*/
+	{0x0385, 0x01},/*y_even_inc*/
+	{0x0387, 0x03},/*y_odd_inc*/
+	{0x3016, 0x06},/*VMODEADD*/
+	{0x3017, 0x40},
+	{0x3069, 0x24},
+	{0x306A, 0x00},
+	{0x306B, 0xCB},
+	{0x306C, 0x07},
+	{0x30E8, 0x86},
+	{0x3304, 0x03},
+	{0x3305, 0x02},
+	{0x3306, 0x0A},
+	{0x3307, 0x02},
+	{0x3308, 0x11},
+	{0x3309, 0x04},
+	{0x330A, 0x05},
+	{0x330B, 0x04},
+	{0x330C, 0x05},
+	{0x330D, 0x04},
+	{0x330E, 0x01},
+	{0x3301, 0x80},
+};
+
+struct imx072_i2c_reg_conf imx072_snap_settings[] = {
+	{0x0340, 0x07},/*frame_length*/
+	{0x0341, 0xEE},/*frame_length*/
+	{0x0342, 0x0A},/*line_length*/
+	{0x0343, 0xE0},/*line_length*/
+	{0x0344, 0x00},/*x_addr_start*/
+	{0x0345, 0x00},/*x_addr_start*/
+	{0x0346, 0x00},/*y_addr_start*/
+	{0x0347, 0x00},/*y_addr_start*/
+	{0x0348, 0x0A},/*x_addr_end*/
+	{0x0349, 0x2F},/*x_addr_end*/
+	{0x034A, 0x07},/*y_addr_end*/
+	{0x034B, 0xA7},/*y_addr_end*/
+	{0x034C, 0x0A},/*x_out_size*/
+	{0x034D, 0x30},/*x_out_size*/
+	{0x034E, 0x07},/*y_out_size*/
+	{0x034F, 0xA8},/*y_out_size*/
+	{0x0381, 0x01},/*x_even_inc*/
+	{0x0383, 0x01},/*x_odd_inc*/
+	{0x0385, 0x01},/*y_even_inc*/
+	{0x0387, 0x01},/*y_odd_inc*/
+	{0x3016, 0x06},/*VMODEADD*/
+	{0x3017, 0x40},
+	{0x3069, 0x24},
+	{0x306A, 0x00},
+	{0x306B, 0xCB},
+	{0x306C, 0x07},
+	{0x30E8, 0x06},
+	{0x3304, 0x05},
+	{0x3305, 0x04},
+	{0x3306, 0x15},
+	{0x3307, 0x02},
+	{0x3308, 0x11},
+	{0x3309, 0x07},
+	{0x330A, 0x05},
+	{0x330B, 0x04},
+	{0x330C, 0x05},
+	{0x330D, 0x04},
+	{0x330E, 0x01},
+	{0x3301, 0x00},
+};
+
+struct imx072_i2c_reg_conf imx072_recommend_settings[] = {
+	{0x0307, 0x12},
+	{0x302B, 0x4B},
+	{0x0101, 0x03},
+	{0x300A, 0x80},
+	{0x3014, 0x08},
+	{0x3015, 0x37},
+	{0x3017, 0x40},
+	{0x301C, 0x01},
+	{0x3031, 0x28},
+	{0x3040, 0x00},
+	{0x3041, 0x60},
+	{0x3051, 0x24},
+	{0x3053, 0x34},
+	{0x3055, 0x3B},
+	{0x3057, 0xC0},
+	{0x3060, 0x30},
+	{0x3065, 0x00},
+	{0x30AA, 0x88},
+	{0x30AB, 0x1C},
+	{0x30B0, 0x32},
+	{0x30B2, 0x83},
+	{0x30D3, 0x04},
+	{0x310E, 0xDD},
+	{0x31A4, 0xD8},
+	{0x31A6, 0x17},
+	{0x31AC, 0xCF},
+	{0x31AE, 0xF1},
+	{0x31B4, 0xD8},
+	{0x31B6, 0x17},
+	{0x3304, 0x05},
+	{0x3305, 0x04},
+	{0x3306, 0x15},
+	{0x3307, 0x02},
+	{0x3308, 0x11},
+	{0x3309, 0x07},
+	{0x330A, 0x05},
+	{0x330B, 0x04},
+	{0x330C, 0x05},
+	{0x330D, 0x04},
+	{0x330E, 0x01},
+	{0x30d8, 0x20},
+};
+
+struct imx072_i2c_conf_array imx072_confs[] = {
+	{&imx072_prev_settings[0], ARRAY_SIZE(imx072_prev_settings)},
+	{&imx072_snap_settings[0], ARRAY_SIZE(imx072_snap_settings)},
+};
+
+struct imx072_reg imx072_regs = {
+	.rec_settings = &imx072_recommend_settings[0],
+	.rec_size = ARRAY_SIZE(imx072_recommend_settings),
+	.conf_array = &imx072_confs[0],
+};
diff --git a/drivers/media/video/msm/imx074.c b/drivers/media/video/msm/imx074.c
new file mode 100644
index 0000000..636b402
--- /dev/null
+++ b/drivers/media/video/msm/imx074.c
@@ -0,0 +1,1414 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <media/msm_camera.h>
+#include <mach/gpio.h>
+#include <mach/camera.h>
+#include <asm/mach-types.h>
+#include "imx074.h"
+
+/*SENSOR REGISTER DEFINES*/
+#define	IMX074_EEPROM_SLAVE_ADDR			0x52
+#define REG_GROUPED_PARAMETER_HOLD			0x0104
+#define GROUPED_PARAMETER_HOLD_OFF			0x00
+#define GROUPED_PARAMETER_HOLD				0x01
+#define REG_MODE_SELECT					0x100
+#define MODE_SELECT_STANDBY_MODE			0x00
+#define MODE_SELECT_STREAM				0x01
+/* Integration Time */
+#define REG_COARSE_INTEGRATION_TIME_HI			0x0202
+#define REG_COARSE_INTEGRATION_TIME_LO			0x0203
+/* Gain */
+#define REG_ANALOGUE_GAIN_CODE_GLOBAL_HI		0x0204
+#define REG_ANALOGUE_GAIN_CODE_GLOBAL_LO		0x0205
+/* PLL registers */
+#define REG_PLL_MULTIPLIER				0x0307
+#define REG_PRE_PLL_CLK_DIV				0x0305
+#define REG_PLSTATIM					0x302b
+#define REG_3024					0x3024
+#define REG_IMAGE_ORIENTATION				0x0101
+#define REG_VNDMY_ABLMGSHLMT				0x300a
+#define REG_Y_OPBADDR_START_DI				0x3014
+#define REG_3015					0x3015
+#define REG_301C					0x301C
+#define REG_302C					0x302C
+#define REG_3031					0x3031
+#define REG_3041					0x3041
+#define REG_3051					0x3051
+#define REG_3053					0x3053
+#define REG_3057					0x3057
+#define REG_305C					0x305C
+#define REG_305D					0x305D
+#define REG_3060					0x3060
+#define REG_3065					0x3065
+#define REG_30AA					0x30AA
+#define REG_30AB					0x30AB
+#define REG_30B0					0x30B0
+#define REG_30B2					0x30B2
+#define REG_30D3					0x30D3
+#define REG_3106					0x3106
+#define REG_310C					0x310C
+#define REG_3304					0x3304
+#define REG_3305					0x3305
+#define REG_3306					0x3306
+#define REG_3307					0x3307
+#define REG_3308					0x3308
+#define REG_3309					0x3309
+#define REG_330A					0x330A
+#define REG_330B					0x330B
+#define REG_330C					0x330C
+#define REG_330D					0x330D
+#define REG_330F					0x330F
+#define REG_3381					0x3381
+
+/* mode setting */
+#define REG_FRAME_LENGTH_LINES_HI			0x0340
+#define REG_FRAME_LENGTH_LINES_LO			0x0341
+#define REG_YADDR_START					0x0347
+#define REG_YAAAR_END					0x034b
+#define REG_X_OUTPUT_SIZE_MSB				0x034c
+#define REG_X_OUTPUT_SIZE_LSB				0x034d
+#define REG_Y_OUTPUT_SIZE_MSB				0x034e
+#define REG_Y_OUTPUT_SIZE_LSB				0x034f
+#define REG_X_EVEN_INC					0x0381
+#define REG_X_ODD_INC					0x0383
+#define REG_Y_EVEN_INC					0x0385
+#define REG_Y_ODD_INC					0x0387
+#define REG_HMODEADD					0x3001
+#define REG_VMODEADD					0x3016
+#define REG_VAPPLINE_START				0x3069
+#define REG_VAPPLINE_END				0x306b
+#define REG_SHUTTER					0x3086
+#define REG_HADDAVE					0x30e8
+#define REG_LANESEL					0x3301
+/* Test Pattern */
+#define REG_TEST_PATTERN_MODE				0x0601
+
+#define REG_LINE_LENGTH_PCK_HI				0x0342
+#define REG_LINE_LENGTH_PCK_LO				0x0343
+/*..... TYPE DECLARATIONS.....*/
+#define	IMX074_OFFSET					3
+#define	IMX074_DEFAULT_MASTER_CLK_RATE			24000000
+/* Full	Size */
+#define	IMX074_FULL_SIZE_WIDTH				4208
+#define	IMX074_FULL_SIZE_HEIGHT				3120
+#define	IMX074_FULL_SIZE_DUMMY_PIXELS			0
+#define	IMX074_FULL_SIZE_DUMMY_LINES			0
+/* Quarter Size	*/
+#define	IMX074_QTR_SIZE_WIDTH				2104
+#define	IMX074_QTR_SIZE_HEIGHT				1560
+#define	IMX074_QTR_SIZE_DUMMY_PIXELS			0
+#define	IMX074_QTR_SIZE_DUMMY_LINES			0
+/* Blanking as measured	on the scope */
+/* Full	Size */
+#define	IMX074_HRZ_FULL_BLK_PIXELS			264
+#define	IMX074_VER_FULL_BLK_LINES			96
+/* Quarter Size	*/
+#define	IMX074_HRZ_QTR_BLK_PIXELS			2368
+#define	IMX074_VER_QTR_BLK_LINES			21
+#define	Q8						0x100
+#define	Q10						0x400
+#define	IMX074_AF_I2C_SLAVE_ID				0x72
+#define	IMX074_STEPS_NEAR_TO_CLOSEST_INF		52
+#define	IMX074_TOTAL_STEPS_NEAR_TO_FAR			52
+static uint32_t imx074_l_region_code_per_step = 2;
+
+struct imx074_work_t {
+	struct work_struct work;
+};
+
+static struct imx074_work_t *imx074_sensorw;
+static struct i2c_client *imx074_client;
+
+struct imx074_ctrl_t {
+	const struct msm_camera_sensor_info *sensordata;
+	uint32_t sensormode;
+	uint32_t fps_divider;/* init to 1 * 0x00000400 */
+	uint32_t pict_fps_divider;/* init to 1 * 0x00000400 */
+	uint16_t fps;
+	int16_t curr_lens_pos;
+	uint16_t curr_step_pos;
+	uint16_t my_reg_gain;
+	uint32_t my_reg_line_count;
+	uint16_t total_lines_per_frame;
+	enum imx074_resolution_t prev_res;
+	enum imx074_resolution_t pict_res;
+	enum imx074_resolution_t curr_res;
+	enum imx074_test_mode_t set_test;
+	unsigned short imgaddr;
+};
+static uint8_t imx074_delay_msecs_stdby = 5;
+static uint16_t imx074_delay_msecs_stream = 5;
+static int32_t config_csi;
+
+static struct imx074_ctrl_t *imx074_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(imx074_wait_queue);
+DEFINE_MUTEX(imx074_mut);
+
+/*=============================================================*/
+
+static int imx074_i2c_rxdata(unsigned short saddr,
+	unsigned char *rxdata, int length)
+{
+	struct i2c_msg msgs[] = {
+		{
+			.addr  = saddr,
+			.flags = 0,
+			.len   = 2,
+			.buf   = rxdata,
+		},
+		{
+			.addr  = saddr,
+			.flags = I2C_M_RD,
+			.len   = 2,
+			.buf   = rxdata,
+		},
+	};
+	if (i2c_transfer(imx074_client->adapter, msgs, 2) < 0) {
+		CDBG("imx074_i2c_rxdata failed!\n");
+		return -EIO;
+	}
+	return 0;
+}
+static int32_t imx074_i2c_txdata(unsigned short saddr,
+				unsigned char *txdata, int length)
+{
+	struct i2c_msg msg[] = {
+		{
+			.addr = saddr,
+			.flags = 0,
+			.len = length,
+			.buf = txdata,
+		 },
+	};
+	if (i2c_transfer(imx074_client->adapter, msg, 1) < 0) {
+		CDBG("imx074_i2c_txdata faild 0x%x\n", imx074_client->addr);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+
+static int32_t imx074_i2c_read(unsigned short raddr,
+	unsigned short *rdata, int rlen)
+{
+	int32_t rc = 0;
+	unsigned char buf[2];
+	if (!rdata)
+		return -EIO;
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (raddr & 0xFF00) >> 8;
+	buf[1] = (raddr & 0x00FF);
+	rc = imx074_i2c_rxdata(imx074_client->addr, buf, rlen);
+	if (rc < 0) {
+		CDBG("imx074_i2c_read 0x%x failed!\n", raddr);
+		return rc;
+	}
+	*rdata = (rlen == 2 ? buf[0] << 8 | buf[1] : buf[0]);
+	return rc;
+}
+
+static int imx074_af_i2c_rxdata_b(unsigned short saddr,
+	unsigned char *rxdata, int length)
+{
+	struct i2c_msg msgs[] = {
+		{
+		.addr  = saddr,
+		.flags = 0,
+		.len   = 1,
+		.buf   = rxdata,
+		},
+		{
+		.addr  = saddr,
+		.flags = I2C_M_RD,
+		.len   = 1,
+		.buf   = rxdata,
+		},
+	};
+
+	if (i2c_transfer(imx074_client->adapter, msgs, 2) < 0) {
+		CDBG("imx074_i2c_rxdata_b failed!\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int32_t imx074_i2c_read_w_eeprom(unsigned short raddr,
+	unsigned short *rdata)
+{
+	int32_t rc;
+	unsigned char buf;
+	if (!rdata)
+		return -EIO;
+	/* Read 2 bytes in sequence */
+	buf = (raddr & 0x00FF);
+	rc = imx074_af_i2c_rxdata_b(IMX074_EEPROM_SLAVE_ADDR, &buf, 1);
+	if (rc < 0) {
+		CDBG("imx074_i2c_read_eeprom 0x%x failed!\n", raddr);
+		return rc;
+	}
+	*rdata = buf<<8;
+
+	/* Read Second byte of data */
+	buf = (raddr & 0x00FF) + 1;
+	rc = imx074_af_i2c_rxdata_b(IMX074_EEPROM_SLAVE_ADDR, &buf, 1);
+	if (rc < 0) {
+		CDBG("imx074_i2c_read_eeprom 0x%x failed!\n", raddr);
+		return rc;
+	}
+	*rdata |= buf;
+	return rc;
+}
+
+static int32_t imx074_i2c_write_b_sensor(unsigned short waddr, uint8_t bdata)
+{
+	int32_t rc = -EFAULT;
+	unsigned char buf[3];
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (waddr & 0xFF00) >> 8;
+	buf[1] = (waddr & 0x00FF);
+	buf[2] = bdata;
+	CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, bdata);
+	rc = imx074_i2c_txdata(imx074_client->addr, buf, 3);
+	if (rc < 0) {
+		CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+			waddr, bdata);
+	}
+	return rc;
+}
+static int16_t imx074_i2c_write_b_af(unsigned short saddr,
+	unsigned short baddr, unsigned short bdata)
+{
+	int32_t rc;
+	unsigned char buf[2];
+	memset(buf, 0, sizeof(buf));
+	buf[0] = baddr;
+	buf[1] = bdata;
+	rc = imx074_i2c_txdata(saddr, buf, 2);
+	if (rc < 0)
+		CDBG("AFi2c_write failed, saddr = 0x%x addr = 0x%x, val =0x%x!",
+			saddr, baddr, bdata);
+	return rc;
+}
+
+static int32_t imx074_i2c_write_w_table(struct imx074_i2c_reg_conf const
+					 *reg_conf_tbl, int num)
+{
+	int i;
+	int32_t rc = -EIO;
+	for (i = 0; i < num; i++) {
+		rc = imx074_i2c_write_b_sensor(reg_conf_tbl->waddr,
+			reg_conf_tbl->wdata);
+		if (rc < 0)
+			break;
+		reg_conf_tbl++;
+	}
+	return rc;
+}
+static int16_t imx074_af_init(void)
+{
+	int32_t rc;
+	/* Initialize waveform */
+	rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x01, 0xA9);
+	rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x02, 0xD2);
+	rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x03, 0x0C);
+	rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x04, 0x14);
+	rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x05, 0xB6);
+	rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x06, 0x4F);
+	return rc;
+}
+
+static void imx074_get_pict_fps(uint16_t fps, uint16_t *pfps)
+{
+	/* input fps is preview fps in Q8 format */
+	uint16_t preview_frame_length_lines, snapshot_frame_length_lines;
+	uint32_t divider, d1;
+	uint32_t pclk_mult;/*Q10 */
+	/* Total frame_length_lines and line_length_pck for preview */
+	preview_frame_length_lines = IMX074_QTR_SIZE_HEIGHT +
+		IMX074_VER_QTR_BLK_LINES;
+	/* Total frame_length_lines and line_length_pck for snapshot */
+	snapshot_frame_length_lines = IMX074_FULL_SIZE_HEIGHT +
+		IMX074_VER_FULL_BLK_LINES;
+	d1 = preview_frame_length_lines * 0x00010000 /
+		snapshot_frame_length_lines;
+	pclk_mult =
+		(uint32_t) ((imx074_regs.reg_pat[RES_CAPTURE].pll_multiplier *
+		0x00010000) /
+		(imx074_regs.reg_pat[RES_PREVIEW].pll_multiplier));
+	divider = d1 * pclk_mult / 0x00010000;
+	*pfps = (uint16_t) (fps * divider / 0x00010000);
+}
+
+static uint16_t imx074_get_prev_lines_pf(void)
+{
+	if (imx074_ctrl->prev_res == QTR_SIZE)
+		return IMX074_QTR_SIZE_HEIGHT + IMX074_VER_QTR_BLK_LINES;
+	else
+		return IMX074_FULL_SIZE_HEIGHT + IMX074_VER_FULL_BLK_LINES;
+
+}
+
+static uint16_t imx074_get_prev_pixels_pl(void)
+{
+	if (imx074_ctrl->prev_res == QTR_SIZE)
+		return IMX074_QTR_SIZE_WIDTH + IMX074_HRZ_QTR_BLK_PIXELS;
+	else
+		return IMX074_FULL_SIZE_WIDTH + IMX074_HRZ_FULL_BLK_PIXELS;
+}
+
+static uint16_t imx074_get_pict_lines_pf(void)
+{
+		if (imx074_ctrl->pict_res == QTR_SIZE)
+			return IMX074_QTR_SIZE_HEIGHT +
+				IMX074_VER_QTR_BLK_LINES;
+		else
+			return IMX074_FULL_SIZE_HEIGHT +
+				IMX074_VER_FULL_BLK_LINES;
+}
+
+static uint16_t imx074_get_pict_pixels_pl(void)
+{
+	if (imx074_ctrl->pict_res == QTR_SIZE)
+		return IMX074_QTR_SIZE_WIDTH +
+			IMX074_HRZ_QTR_BLK_PIXELS;
+	else
+		return IMX074_FULL_SIZE_WIDTH +
+			IMX074_HRZ_FULL_BLK_PIXELS;
+}
+
+static uint32_t imx074_get_pict_max_exp_lc(void)
+{
+	if (imx074_ctrl->pict_res == QTR_SIZE)
+		return (IMX074_QTR_SIZE_HEIGHT +
+			IMX074_VER_QTR_BLK_LINES)*24;
+	else
+		return (IMX074_FULL_SIZE_HEIGHT +
+			IMX074_VER_FULL_BLK_LINES)*24;
+}
+
+static int32_t imx074_set_fps(struct fps_cfg	*fps)
+{
+	uint16_t total_lines_per_frame;
+	int32_t rc = 0;
+	imx074_ctrl->fps_divider = fps->fps_div;
+	imx074_ctrl->pict_fps_divider = fps->pict_fps_div;
+	if (imx074_ctrl->curr_res  == QTR_SIZE) {
+		total_lines_per_frame = (uint16_t)(((IMX074_QTR_SIZE_HEIGHT +
+			IMX074_VER_QTR_BLK_LINES) *
+			imx074_ctrl->fps_divider) / 0x400);
+	} else {
+		total_lines_per_frame = (uint16_t)(((IMX074_FULL_SIZE_HEIGHT +
+			IMX074_VER_FULL_BLK_LINES) *
+			imx074_ctrl->pict_fps_divider) / 0x400);
+	}
+	if (imx074_i2c_write_b_sensor(REG_FRAME_LENGTH_LINES_HI,
+		((total_lines_per_frame & 0xFF00) >> 8)) < 0)
+		return rc;
+	if (imx074_i2c_write_b_sensor(REG_FRAME_LENGTH_LINES_LO,
+		(total_lines_per_frame & 0x00FF)) < 0)
+		return rc;
+	return rc;
+}
+
+static int32_t imx074_write_exp_gain(uint16_t gain, uint32_t line)
+{
+	static uint16_t max_legal_gain = 0x00E0;
+	uint8_t gain_msb, gain_lsb;
+	uint8_t intg_time_msb, intg_time_lsb;
+	uint8_t frame_length_line_msb, frame_length_line_lsb;
+	uint16_t frame_length_lines;
+	int32_t rc = -1;
+
+	CDBG("imx074_write_exp_gain : gain = %d line = %d", gain, line);
+	if (imx074_ctrl->curr_res  == QTR_SIZE) {
+		frame_length_lines = IMX074_QTR_SIZE_HEIGHT +
+			IMX074_VER_QTR_BLK_LINES;
+		frame_length_lines = frame_length_lines *
+			imx074_ctrl->fps_divider / 0x400;
+	} else {
+		frame_length_lines = IMX074_FULL_SIZE_HEIGHT +
+			IMX074_VER_FULL_BLK_LINES;
+		frame_length_lines = frame_length_lines *
+			imx074_ctrl->pict_fps_divider / 0x400;
+	}
+	if (line > (frame_length_lines - IMX074_OFFSET))
+		frame_length_lines = line + IMX074_OFFSET;
+
+	CDBG("imx074 setting line = %d\n", line);
+
+
+	CDBG("imx074 setting frame_length_lines = %d\n",
+					frame_length_lines);
+
+	if (gain > max_legal_gain)
+		/* range: 0 to 224 */
+		gain = max_legal_gain;
+
+	/* update gain registers */
+	gain_msb = (uint8_t) ((gain & 0xFF00) >> 8);
+	gain_lsb = (uint8_t) (gain & 0x00FF);
+
+	frame_length_line_msb = (uint8_t) ((frame_length_lines & 0xFF00) >> 8);
+	frame_length_line_lsb = (uint8_t) (frame_length_lines & 0x00FF);
+
+	/* update line count registers */
+	intg_time_msb = (uint8_t) ((line & 0xFF00) >> 8);
+	intg_time_lsb = (uint8_t) (line & 0x00FF);
+
+	rc = imx074_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+					GROUPED_PARAMETER_HOLD);
+	if (rc < 0)
+		return rc;
+	CDBG("imx074 setting REG_ANALOGUE_GAIN_CODE_GLOBAL_HI = 0x%X\n",
+					gain_msb);
+	rc = imx074_i2c_write_b_sensor(REG_ANALOGUE_GAIN_CODE_GLOBAL_HI,
+					gain_msb);
+	if (rc < 0)
+		return rc;
+	CDBG("imx074 setting REG_ANALOGUE_GAIN_CODE_GLOBAL_LO = 0x%X\n",
+					gain_lsb);
+	rc = imx074_i2c_write_b_sensor(REG_ANALOGUE_GAIN_CODE_GLOBAL_LO,
+					gain_lsb);
+	if (rc < 0)
+		return rc;
+
+	CDBG("imx074 setting REG_FRAME_LENGTH_LINES_HI = 0x%X\n",
+					frame_length_line_msb);
+	rc = imx074_i2c_write_b_sensor(REG_FRAME_LENGTH_LINES_HI,
+			frame_length_line_msb);
+	if (rc < 0)
+		return rc;
+
+	CDBG("imx074 setting REG_FRAME_LENGTH_LINES_LO = 0x%X\n",
+			frame_length_line_lsb);
+	rc = imx074_i2c_write_b_sensor(REG_FRAME_LENGTH_LINES_LO,
+			frame_length_line_lsb);
+	if (rc < 0)
+		return rc;
+
+	CDBG("imx074 setting REG_COARSE_INTEGRATION_TIME_HI = 0x%X\n",
+					intg_time_msb);
+	rc = imx074_i2c_write_b_sensor(REG_COARSE_INTEGRATION_TIME_HI,
+					intg_time_msb);
+	if (rc < 0)
+		return rc;
+
+	CDBG("imx074 setting REG_COARSE_INTEGRATION_TIME_LO = 0x%X\n",
+					intg_time_lsb);
+	rc = imx074_i2c_write_b_sensor(REG_COARSE_INTEGRATION_TIME_LO,
+					intg_time_lsb);
+	if (rc < 0)
+		return rc;
+
+	rc = imx074_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+					GROUPED_PARAMETER_HOLD_OFF);
+	if (rc < 0)
+		return rc;
+
+	return rc;
+}
+
+static int32_t imx074_set_pict_exp_gain(uint16_t gain, uint32_t line)
+{
+	int32_t rc = 0;
+	rc = imx074_write_exp_gain(gain, line);
+	return rc;
+}
+
+static int32_t imx074_move_focus(int direction,
+	int32_t num_steps)
+{
+	int32_t step_direction, dest_step_position, bit_mask;
+	int32_t rc = 0;
+
+	if (num_steps == 0)
+		return rc;
+
+	if (direction == MOVE_NEAR) {
+		step_direction = 1;
+		bit_mask = 0x80;
+	} else if (direction == MOVE_FAR) {
+		step_direction = -1;
+		bit_mask = 0x00;
+	} else {
+		CDBG("imx074_move_focus: Illegal focus direction");
+		return -EINVAL;
+	}
+	dest_step_position = imx074_ctrl->curr_step_pos +
+		(step_direction * num_steps);
+	if (dest_step_position < 0)
+		dest_step_position = 0;
+	else if (dest_step_position > IMX074_TOTAL_STEPS_NEAR_TO_FAR)
+		dest_step_position = IMX074_TOTAL_STEPS_NEAR_TO_FAR;
+	rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x00,
+		((num_steps * imx074_l_region_code_per_step) | bit_mask));
+	CDBG("%s: Index: %d\n", __func__, dest_step_position);
+	imx074_ctrl->curr_step_pos = dest_step_position;
+	return rc;
+}
+
+
+static int32_t imx074_set_default_focus(uint8_t af_step)
+{
+	int32_t rc;
+	/* Initialize to infinity */
+	rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x00, 0x7F);
+	rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x00, 0x7F);
+	imx074_ctrl->curr_step_pos = 0;
+	return rc;
+}
+static int32_t imx074_test(enum imx074_test_mode_t mo)
+{
+	int32_t rc = 0;
+	if (mo == TEST_OFF)
+		return rc;
+	else {
+		/* Set mo to 2 inorder to enable test pattern*/
+		if (imx074_i2c_write_b_sensor(REG_TEST_PATTERN_MODE,
+			(uint8_t) mo) < 0) {
+			return rc;
+		}
+	}
+	return rc;
+}
+static int32_t imx074_sensor_setting(int update_type, int rt)
+{
+	int32_t rc = 0;
+	struct msm_camera_csi_params imx074_csi_params;
+	switch (update_type) {
+	case REG_INIT:
+		if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+			struct imx074_i2c_reg_conf init_tbl[] = {
+				{REG_PRE_PLL_CLK_DIV,
+					imx074_regs.reg_pat_init[0].
+					pre_pll_clk_div},
+				{REG_PLSTATIM,
+					imx074_regs.reg_pat_init[0].
+					plstatim},
+				{REG_3024,
+					imx074_regs.reg_pat_init[0].
+					reg_3024},
+				{REG_IMAGE_ORIENTATION,
+					imx074_regs.reg_pat_init[0].
+					image_orientation},
+				{REG_VNDMY_ABLMGSHLMT,
+					imx074_regs.reg_pat_init[0].
+					vndmy_ablmgshlmt},
+				{REG_Y_OPBADDR_START_DI,
+					imx074_regs.reg_pat_init[0].
+					y_opbaddr_start_di},
+				{REG_3015,
+					imx074_regs.reg_pat_init[0].
+					reg_0x3015},
+				{REG_301C,
+					imx074_regs.reg_pat_init[0].
+					reg_0x301c},
+				{REG_302C,
+					imx074_regs.reg_pat_init[0].
+					reg_0x302c},
+				{REG_3031,
+					imx074_regs.reg_pat_init[0].reg_0x3031},
+				{REG_3041,
+					imx074_regs.reg_pat_init[0].reg_0x3041},
+				{REG_3051,
+					imx074_regs.reg_pat_init[0].reg_0x3051},
+				{REG_3053,
+					imx074_regs.reg_pat_init[0].reg_0x3053},
+				{REG_3057,
+					imx074_regs.reg_pat_init[0].reg_0x3057},
+				{REG_305C,
+					imx074_regs.reg_pat_init[0].reg_0x305c},
+				{REG_305D,
+					imx074_regs.reg_pat_init[0].reg_0x305d},
+				{REG_3060,
+					imx074_regs.reg_pat_init[0].reg_0x3060},
+				{REG_3065,
+					imx074_regs.reg_pat_init[0].reg_0x3065},
+				{REG_30AA,
+					imx074_regs.reg_pat_init[0].reg_0x30aa},
+				{REG_30AB,
+					imx074_regs.reg_pat_init[0].reg_0x30ab},
+				{REG_30B0,
+					imx074_regs.reg_pat_init[0].reg_0x30b0},
+				{REG_30B2,
+					imx074_regs.reg_pat_init[0].reg_0x30b2},
+				{REG_30D3,
+					imx074_regs.reg_pat_init[0].reg_0x30d3},
+				{REG_3106,
+					imx074_regs.reg_pat_init[0].reg_0x3106},
+				{REG_310C,
+					imx074_regs.reg_pat_init[0].reg_0x310c},
+				{REG_3304,
+					imx074_regs.reg_pat_init[0].reg_0x3304},
+				{REG_3305,
+					imx074_regs.reg_pat_init[0].reg_0x3305},
+				{REG_3306,
+					imx074_regs.reg_pat_init[0].reg_0x3306},
+				{REG_3307,
+					imx074_regs.reg_pat_init[0].reg_0x3307},
+				{REG_3308,
+					imx074_regs.reg_pat_init[0].reg_0x3308},
+				{REG_3309,
+					imx074_regs.reg_pat_init[0].reg_0x3309},
+				{REG_330A,
+					imx074_regs.reg_pat_init[0].reg_0x330a},
+				{REG_330B,
+					imx074_regs.reg_pat_init[0].reg_0x330b},
+				{REG_330C,
+					imx074_regs.reg_pat_init[0].reg_0x330c},
+				{REG_330D,
+					imx074_regs.reg_pat_init[0].reg_0x330d},
+				{REG_330F,
+					imx074_regs.reg_pat_init[0].reg_0x330f},
+				{REG_3381,
+					imx074_regs.reg_pat_init[0].reg_0x3381},
+			};
+			struct imx074_i2c_reg_conf init_mode_tbl[] = {
+				{REG_GROUPED_PARAMETER_HOLD,
+					GROUPED_PARAMETER_HOLD},
+				{REG_PLL_MULTIPLIER,
+					imx074_regs.reg_pat[rt].
+					pll_multiplier},
+				{REG_FRAME_LENGTH_LINES_HI,
+					imx074_regs.reg_pat[rt].
+					frame_length_lines_hi},
+				{REG_FRAME_LENGTH_LINES_LO,
+					imx074_regs.reg_pat[rt].
+					frame_length_lines_lo},
+				{REG_YADDR_START ,
+					imx074_regs.reg_pat[rt].
+					y_addr_start},
+				{REG_YAAAR_END,
+					imx074_regs.reg_pat[rt].
+					y_add_end},
+				{REG_X_OUTPUT_SIZE_MSB,
+					imx074_regs.reg_pat[rt].
+					x_output_size_msb},
+				{REG_X_OUTPUT_SIZE_LSB,
+					imx074_regs.reg_pat[rt].
+					x_output_size_lsb},
+				{REG_Y_OUTPUT_SIZE_MSB,
+					imx074_regs.reg_pat[rt].
+					y_output_size_msb},
+				{REG_Y_OUTPUT_SIZE_LSB ,
+					imx074_regs.reg_pat[rt].
+					y_output_size_lsb},
+				{REG_X_EVEN_INC,
+					imx074_regs.reg_pat[rt].
+					x_even_inc},
+				{REG_X_ODD_INC,
+					imx074_regs.reg_pat[rt].
+					x_odd_inc},
+				{REG_Y_EVEN_INC,
+					imx074_regs.reg_pat[rt].
+					y_even_inc},
+				{REG_Y_ODD_INC,
+					imx074_regs.reg_pat[rt].
+					y_odd_inc},
+				{REG_HMODEADD,
+					imx074_regs.reg_pat[rt].
+					hmodeadd},
+				{REG_VMODEADD,
+					imx074_regs.reg_pat[rt].
+					vmodeadd},
+				{REG_VAPPLINE_START,
+					imx074_regs.reg_pat[rt].
+					vapplinepos_start},
+				{REG_VAPPLINE_END,
+					imx074_regs.reg_pat[rt].
+					vapplinepos_end},
+				{REG_SHUTTER,
+					imx074_regs.reg_pat[rt].
+					shutter},
+				{REG_HADDAVE,
+					imx074_regs.reg_pat[rt].
+					haddave},
+				{REG_LANESEL,
+					imx074_regs.reg_pat[rt].
+					lanesel},
+				{REG_GROUPED_PARAMETER_HOLD,
+					GROUPED_PARAMETER_HOLD_OFF},
+
+			};
+			/* reset fps_divider */
+			imx074_ctrl->fps = 30 * Q8;
+			imx074_ctrl->fps_divider = 1 * 0x400;
+			/* stop streaming */
+			rc = imx074_i2c_write_b_sensor(REG_MODE_SELECT,
+				MODE_SELECT_STANDBY_MODE);
+			if (rc < 0)
+				return rc;
+			msleep(imx074_delay_msecs_stdby);
+			rc = imx074_i2c_write_w_table(&init_tbl[0],
+				ARRAY_SIZE(init_tbl));
+			if (rc < 0)
+				return rc;
+			rc = imx074_i2c_write_w_table(&init_mode_tbl[0],
+				ARRAY_SIZE(init_mode_tbl));
+			if (rc < 0)
+				return rc;
+			rc = imx074_test(imx074_ctrl->set_test);
+			return rc;
+		}
+		break;
+	case UPDATE_PERIODIC:
+		if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+			struct imx074_i2c_reg_conf mode_tbl[] = {
+				{REG_GROUPED_PARAMETER_HOLD,
+					GROUPED_PARAMETER_HOLD},
+				{REG_PLL_MULTIPLIER,
+					imx074_regs.reg_pat[rt].
+					pll_multiplier},
+				{REG_FRAME_LENGTH_LINES_HI,
+					imx074_regs.reg_pat[rt].
+					frame_length_lines_hi},
+				{REG_FRAME_LENGTH_LINES_LO,
+					imx074_regs.reg_pat[rt].
+					frame_length_lines_lo},
+				{REG_YADDR_START ,
+					imx074_regs.reg_pat[rt].
+					y_addr_start},
+				{REG_YAAAR_END,
+					imx074_regs.reg_pat[rt].
+					y_add_end},
+				{REG_X_OUTPUT_SIZE_MSB,
+					imx074_regs.reg_pat[rt].
+					x_output_size_msb},
+				{REG_X_OUTPUT_SIZE_LSB,
+					imx074_regs.reg_pat[rt].
+					x_output_size_lsb},
+				{REG_Y_OUTPUT_SIZE_MSB,
+					imx074_regs.reg_pat[rt].
+					y_output_size_msb},
+				{REG_Y_OUTPUT_SIZE_LSB ,
+					imx074_regs.reg_pat[rt].
+					y_output_size_lsb},
+				{REG_X_EVEN_INC,
+					imx074_regs.reg_pat[rt].
+					x_even_inc},
+				{REG_X_ODD_INC,
+					imx074_regs.reg_pat[rt].
+					x_odd_inc},
+				{REG_Y_EVEN_INC,
+					imx074_regs.reg_pat[rt].
+					y_even_inc},
+				{REG_Y_ODD_INC,
+					imx074_regs.reg_pat[rt].
+					y_odd_inc},
+				{REG_HMODEADD,
+					imx074_regs.reg_pat[rt].
+					hmodeadd},
+				{REG_VMODEADD,
+					imx074_regs.reg_pat[rt].
+					vmodeadd},
+				{REG_VAPPLINE_START,
+					imx074_regs.reg_pat[rt].
+					vapplinepos_start},
+				{REG_VAPPLINE_END,
+					imx074_regs.reg_pat[rt].
+					vapplinepos_end},
+				{REG_SHUTTER,
+					imx074_regs.reg_pat[rt].
+					shutter},
+				{REG_HADDAVE,
+					imx074_regs.reg_pat[rt].
+					haddave},
+				{REG_LANESEL,
+					imx074_regs.reg_pat[rt].
+					lanesel},
+				{REG_GROUPED_PARAMETER_HOLD,
+					GROUPED_PARAMETER_HOLD_OFF},
+			};
+
+			/* stop streaming */
+			rc = imx074_i2c_write_b_sensor(REG_MODE_SELECT,
+				MODE_SELECT_STANDBY_MODE);
+			msleep(imx074_delay_msecs_stdby);
+			if (config_csi == 0) {
+				imx074_csi_params.lane_cnt = 4;
+				imx074_csi_params.data_format = CSI_10BIT;
+				imx074_csi_params.lane_assign = 0xe4;
+				imx074_csi_params.dpcm_scheme = 0;
+				imx074_csi_params.settle_cnt = 0x14;
+				rc = msm_camio_csi_config(&imx074_csi_params);
+				/*imx074_delay_msecs_stdby*/
+				msleep(imx074_delay_msecs_stream);
+				config_csi = 1;
+			}
+			rc = imx074_i2c_write_w_table(&mode_tbl[0],
+				ARRAY_SIZE(mode_tbl));
+			if (rc < 0)
+				return rc;
+			rc = imx074_i2c_write_b_sensor(REG_MODE_SELECT,
+				MODE_SELECT_STREAM);
+			if (rc < 0)
+				return rc;
+			msleep(imx074_delay_msecs_stream);
+		}
+		break;
+	default:
+		rc = -EINVAL;
+		break;
+	}
+
+	return rc;
+}
+
+
+static int32_t imx074_video_config(int mode)
+{
+
+	int32_t	rc = 0;
+	int	rt;
+	/* change sensor resolution	if needed */
+	if (imx074_ctrl->prev_res == QTR_SIZE) {
+		rt = RES_PREVIEW;
+	} else {
+		rt = RES_CAPTURE;
+	}
+	if (imx074_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+		return rc;
+	imx074_ctrl->curr_res = imx074_ctrl->prev_res;
+	imx074_ctrl->sensormode = mode;
+	return rc;
+}
+
+static int32_t imx074_snapshot_config(int mode)
+{
+	int32_t rc = 0;
+	int rt = RES_PREVIEW; /* TODO: Used without initialization, guessing. */
+	/* change sensor resolution if needed */
+	if (imx074_ctrl->curr_res != imx074_ctrl->pict_res) {
+		if (imx074_ctrl->pict_res == QTR_SIZE) {
+			rt = RES_PREVIEW;
+		} else {
+			rt = RES_CAPTURE;
+		}
+	}
+	if (imx074_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+		return rc;
+	imx074_ctrl->curr_res = imx074_ctrl->pict_res;
+	imx074_ctrl->sensormode = mode;
+	return rc;
+}
+static int32_t imx074_raw_snapshot_config(int mode)
+{
+	int32_t rc = 0;
+	int rt = RES_PREVIEW; /* TODO: Used without initialization, guessing. */
+	/* change sensor resolution if needed */
+	if (imx074_ctrl->curr_res != imx074_ctrl->pict_res) {
+		if (imx074_ctrl->pict_res == QTR_SIZE) {
+			rt = RES_PREVIEW;
+		} else {
+			rt = RES_CAPTURE;
+		}
+	}
+	if (imx074_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+		return rc;
+	imx074_ctrl->curr_res = imx074_ctrl->pict_res;
+	imx074_ctrl->sensormode = mode;
+	return rc;
+}
+static int32_t imx074_set_sensor_mode(int mode,
+	int res)
+{
+	int32_t rc = 0;
+	switch (mode) {
+	case SENSOR_PREVIEW_MODE:
+		rc = imx074_video_config(mode);
+		break;
+	case SENSOR_SNAPSHOT_MODE:
+		rc = imx074_snapshot_config(mode);
+		break;
+	case SENSOR_RAW_SNAPSHOT_MODE:
+		rc = imx074_raw_snapshot_config(mode);
+		break;
+	default:
+		rc = -EINVAL;
+		break;
+	}
+	return rc;
+}
+static int32_t imx074_power_down(void)
+{
+	imx074_i2c_write_b_sensor(REG_MODE_SELECT,
+		MODE_SELECT_STANDBY_MODE);
+	msleep(imx074_delay_msecs_stdby);
+	return 0;
+}
+static int imx074_probe_init_done(const struct msm_camera_sensor_info *data)
+{
+	gpio_set_value_cansleep(data->sensor_reset, 0);
+	gpio_direction_input(data->sensor_reset);
+	gpio_free(data->sensor_reset);
+	return 0;
+}
+
+static int imx074_read_eeprom_data(struct sensor_cfg_data *cfg)
+{
+	int32_t rc = 0;
+	uint16_t eepromdata = 0;
+	uint8_t addr = 0;
+
+	addr = 0x10;
+	rc = imx074_i2c_read_w_eeprom(addr, &eepromdata);
+	if (rc < 0) {
+		CDBG("%s: Error Reading EEPROM @ 0x%x\n", __func__, addr);
+		return rc;
+	}
+	cfg->cfg.calib_info.r_over_g = eepromdata;
+
+	addr = 0x12;
+	rc = imx074_i2c_read_w_eeprom(addr, &eepromdata);
+	if (rc < 0) {
+		CDBG("%s: Error Reading EEPROM @ 0x%x\n", __func__, addr);
+		return rc;
+	}
+	cfg->cfg.calib_info.b_over_g = eepromdata;
+
+	addr = 0x14;
+	rc = imx074_i2c_read_w_eeprom(addr, &eepromdata);
+	if (rc < 0) {
+		CDBG("%s: Error Reading EEPROM @ 0x%x\n", __func__, addr);
+		return rc;
+	}
+	cfg->cfg.calib_info.gr_over_gb = eepromdata;
+
+	addr = 0x1A;
+	rc = imx074_i2c_read_w_eeprom(addr, &eepromdata);
+	if (rc < 0) {
+		CDBG("%s: Error Reading EEPROM @ 0x%x\n", __func__, addr);
+		return rc;
+	}
+	cfg->cfg.calib_info.macro_2_inf = eepromdata;
+
+	addr = 0x1C;
+	rc = imx074_i2c_read_w_eeprom(addr, &eepromdata);
+	if (rc < 0) {
+		CDBG("%s: Error Reading EEPROM @ 0x%x\n", __func__, addr);
+		return rc;
+	}
+	cfg->cfg.calib_info.inf_2_macro = eepromdata;
+
+	addr = 0x1E;
+	rc = imx074_i2c_read_w_eeprom(addr, &eepromdata);
+	if (rc < 0) {
+		CDBG("%s: Error Reading EEPROM @ 0x%x\n", __func__, addr);
+		return rc;
+	}
+	cfg->cfg.calib_info.stroke_amt = eepromdata;
+
+	addr = 0x20;
+	rc = imx074_i2c_read_w_eeprom(addr, &eepromdata);
+	if (rc < 0) {
+		CDBG("%s: Error Reading EEPROM @ 0x%x\n", __func__, addr);
+		return rc;
+	}
+	cfg->cfg.calib_info.af_pos_1m = eepromdata;
+
+	addr = 0x22;
+	rc = imx074_i2c_read_w_eeprom(addr, &eepromdata);
+	if (rc < 0) {
+		CDBG("%s: Error Reading EEPROM @ 0x%x\n", __func__, addr);
+		return rc;
+	}
+	cfg->cfg.calib_info.af_pos_inf = eepromdata;
+
+	return rc;
+}
+
+static int imx074_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+	int32_t rc = 0;
+	unsigned short chipidl, chipidh;
+	CDBG("%s: %d\n", __func__, __LINE__);
+	rc = gpio_request(data->sensor_reset, "imx074");
+	CDBG(" imx074_probe_init_sensor \n");
+	if (!rc) {
+		CDBG("sensor_reset = %d\n", rc);
+		gpio_direction_output(data->sensor_reset, 0);
+		usleep_range(5000, 6000);
+		gpio_set_value_cansleep(data->sensor_reset, 1);
+		usleep_range(5000, 6000);
+	} else {
+		CDBG("gpio reset fail");
+		goto init_probe_done;
+	}
+	CDBG("imx074_probe_init_sensor is called\n");
+	/* 3. Read sensor Model ID: */
+	rc = imx074_i2c_read(0x0000, &chipidh, 1);
+	if (rc < 0) {
+		CDBG("Model read failed\n");
+		goto init_probe_fail;
+	}
+	rc = imx074_i2c_read(0x0001, &chipidl, 1);
+	if (rc < 0) {
+		CDBG("Model read failed\n");
+		goto init_probe_fail;
+	}
+	CDBG("imx074 model_id = 0x%x  0x%x\n", chipidh, chipidl);
+	/* 4. Compare sensor ID to IMX074 ID: */
+	if (chipidh != 0x00 || chipidl != 0x74) {
+		rc = -ENODEV;
+		CDBG("imx074_probe_init_sensor fail chip id doesnot match\n");
+		goto init_probe_fail;
+	}
+	goto init_probe_done;
+init_probe_fail:
+	CDBG("imx074_probe_init_sensor fails\n");
+	imx074_probe_init_done(data);
+init_probe_done:
+	CDBG(" imx074_probe_init_sensor finishes\n");
+	return rc;
+	}
+static int32_t imx074_poweron_af(void)
+{
+	int32_t rc = 0;
+	CDBG("imx074 enable AF actuator, gpio = %d\n",
+			imx074_ctrl->sensordata->vcm_pwd);
+	rc = gpio_request(imx074_ctrl->sensordata->vcm_pwd, "imx074");
+	if (!rc) {
+		gpio_direction_output(imx074_ctrl->sensordata->vcm_pwd, 1);
+		msleep(20);
+		rc = imx074_af_init();
+		if (rc < 0)
+			CDBG("imx074 AF initialisation failed\n");
+	} else {
+		CDBG("%s: AF PowerON gpio_request failed %d\n", __func__, rc);
+	 }
+	return rc;
+}
+static void imx074_poweroff_af(void)
+{
+	gpio_set_value_cansleep(imx074_ctrl->sensordata->vcm_pwd, 0);
+	gpio_free(imx074_ctrl->sensordata->vcm_pwd);
+}
+/* camsensor_iu060f_imx074_reset */
+int imx074_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+	int32_t rc = 0;
+	CDBG("%s: %d\n", __func__, __LINE__);
+	CDBG("Calling imx074_sensor_open_init\n");
+	imx074_ctrl = kzalloc(sizeof(struct imx074_ctrl_t), GFP_KERNEL);
+	if (!imx074_ctrl) {
+		CDBG("imx074_init failed!\n");
+		rc = -ENOMEM;
+		goto init_done;
+	}
+	imx074_ctrl->fps_divider = 1 * 0x00000400;
+	imx074_ctrl->pict_fps_divider = 1 * 0x00000400;
+	imx074_ctrl->fps = 30 * Q8;
+	imx074_ctrl->set_test = TEST_OFF;
+	imx074_ctrl->prev_res = QTR_SIZE;
+	imx074_ctrl->pict_res = FULL_SIZE;
+	imx074_ctrl->curr_res = INVALID_SIZE;
+	config_csi = 0;
+
+	if (data)
+		imx074_ctrl->sensordata = data;
+
+	/* enable mclk first */
+	msm_camio_clk_rate_set(IMX074_DEFAULT_MASTER_CLK_RATE);
+	usleep_range(1000, 2000);
+	rc = imx074_probe_init_sensor(data);
+	if (rc < 0) {
+		CDBG("Calling imx074_sensor_open_init fail\n");
+		goto probe_fail;
+	}
+
+	rc = imx074_sensor_setting(REG_INIT, RES_PREVIEW);
+	if (rc < 0) {
+		CDBG("imx074_sensor_setting failed\n");
+		goto init_fail;
+	}
+	if (machine_is_msm8x60_fluid())
+		rc = imx074_poweron_af();
+	else
+		rc = imx074_af_init();
+	if (rc < 0) {
+		CDBG("AF initialisation failed\n");
+		goto init_fail;
+	} else
+		goto init_done;
+probe_fail:
+	CDBG(" imx074_sensor_open_init probe fail\n");
+	kfree(imx074_ctrl);
+	return rc;
+init_fail:
+	CDBG(" imx074_sensor_open_init fail\n");
+	imx074_probe_init_done(data);
+	kfree(imx074_ctrl);
+init_done:
+	CDBG("imx074_sensor_open_init done\n");
+	return rc;
+}
+static int imx074_init_client(struct i2c_client *client)
+{
+	/* Initialize the MSM_CAMI2C Chip */
+	init_waitqueue_head(&imx074_wait_queue);
+	return 0;
+}
+
+static const struct i2c_device_id imx074_i2c_id[] = {
+	{"imx074", 0},
+	{ }
+};
+
+static int imx074_i2c_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	int rc = 0;
+	CDBG("imx074_probe called!\n");
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		CDBG("i2c_check_functionality failed\n");
+		goto probe_failure;
+	}
+
+	imx074_sensorw = kzalloc(sizeof(struct imx074_work_t), GFP_KERNEL);
+	if (!imx074_sensorw) {
+		CDBG("kzalloc failed.\n");
+		rc = -ENOMEM;
+		goto probe_failure;
+	}
+
+	i2c_set_clientdata(client, imx074_sensorw);
+	imx074_init_client(client);
+	imx074_client = client;
+
+
+	CDBG("imx074_probe successed! rc = %d\n", rc);
+	return 0;
+
+probe_failure:
+	CDBG("imx074_probe failed! rc = %d\n", rc);
+	return rc;
+}
+
+static int __exit imx074_remove(struct i2c_client *client)
+{
+	struct imx074_work_t_t *sensorw = i2c_get_clientdata(client);
+	free_irq(client->irq, sensorw);
+	imx074_client = NULL;
+	kfree(sensorw);
+	return 0;
+}
+
+static struct i2c_driver imx074_i2c_driver = {
+	.id_table = imx074_i2c_id,
+	.probe  = imx074_i2c_probe,
+	.remove = __exit_p(imx074_i2c_remove),
+	.driver = {
+		.name = "imx074",
+	},
+};
+
+int imx074_sensor_config(void __user *argp)
+{
+	struct sensor_cfg_data cdata;
+	long   rc = 0;
+	if (copy_from_user(&cdata,
+		(void *)argp,
+		sizeof(struct sensor_cfg_data)))
+		return -EFAULT;
+	mutex_lock(&imx074_mut);
+	CDBG("imx074_sensor_config: cfgtype = %d\n",
+	cdata.cfgtype);
+	switch (cdata.cfgtype) {
+	case CFG_GET_PICT_FPS:
+		imx074_get_pict_fps(
+			cdata.cfg.gfps.prevfps,
+			&(cdata.cfg.gfps.pictfps));
+		if (copy_to_user((void *)argp,
+			&cdata,
+			sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+			break;
+	case CFG_GET_PREV_L_PF:
+		cdata.cfg.prevl_pf =
+			imx074_get_prev_lines_pf();
+		if (copy_to_user((void *)argp,
+			&cdata,
+			sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+			break;
+	case CFG_GET_PREV_P_PL:
+		cdata.cfg.prevp_pl =
+			imx074_get_prev_pixels_pl();
+		if (copy_to_user((void *)argp,
+			&cdata,
+			sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+			break;
+
+	case CFG_GET_PICT_L_PF:
+		cdata.cfg.pictl_pf =
+			imx074_get_pict_lines_pf();
+		if (copy_to_user((void *)argp,
+			&cdata,
+			sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+			break;
+	case CFG_GET_PICT_P_PL:
+		cdata.cfg.pictp_pl =
+			imx074_get_pict_pixels_pl();
+		if (copy_to_user((void *)argp,
+			&cdata,
+			sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+			break;
+	case CFG_GET_PICT_MAX_EXP_LC:
+		cdata.cfg.pict_max_exp_lc =
+			imx074_get_pict_max_exp_lc();
+		if (copy_to_user((void *)argp,
+			&cdata,
+			sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+			break;
+	case CFG_SET_FPS:
+	case CFG_SET_PICT_FPS:
+		rc = imx074_set_fps(&(cdata.cfg.fps));
+		break;
+	case CFG_SET_EXP_GAIN:
+		rc =
+			imx074_write_exp_gain(
+			cdata.cfg.exp_gain.gain,
+			cdata.cfg.exp_gain.line);
+			break;
+	case CFG_SET_PICT_EXP_GAIN:
+		rc =
+			imx074_set_pict_exp_gain(
+			cdata.cfg.exp_gain.gain,
+			cdata.cfg.exp_gain.line);
+			break;
+	case CFG_SET_MODE:
+		rc = imx074_set_sensor_mode(cdata.mode,
+			cdata.rs);
+			break;
+	case CFG_PWR_DOWN:
+		rc = imx074_power_down();
+			break;
+	case CFG_GET_CALIB_DATA:
+		rc = imx074_read_eeprom_data(&cdata);
+		if (rc < 0)
+			break;
+		if (copy_to_user((void *)argp,
+			&cdata,
+			sizeof(cdata)))
+			rc = -EFAULT;
+		break;
+	case CFG_MOVE_FOCUS:
+		rc =
+			imx074_move_focus(
+			cdata.cfg.focus.dir,
+			cdata.cfg.focus.steps);
+			break;
+	case CFG_SET_DEFAULT_FOCUS:
+		rc =
+			imx074_set_default_focus(
+			cdata.cfg.focus.steps);
+			break;
+	case CFG_GET_AF_MAX_STEPS:
+		cdata.max_steps = IMX074_STEPS_NEAR_TO_CLOSEST_INF;
+		if (copy_to_user((void *)argp,
+			&cdata,
+			sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+			break;
+	case CFG_SET_EFFECT:
+	default:
+		rc = -EFAULT;
+		break;
+	}
+
+	mutex_unlock(&imx074_mut);
+
+	return rc;
+}
+static int imx074_sensor_release(void)
+{
+	int rc = -EBADF;
+	mutex_lock(&imx074_mut);
+	if (machine_is_msm8x60_fluid())
+		imx074_poweroff_af();
+	imx074_power_down();
+	gpio_set_value_cansleep(imx074_ctrl->sensordata->sensor_reset, 0);
+	msleep(5);
+	gpio_direction_input(imx074_ctrl->sensordata->sensor_reset);
+	gpio_free(imx074_ctrl->sensordata->sensor_reset);
+	kfree(imx074_ctrl);
+	imx074_ctrl = NULL;
+	CDBG("imx074_release completed\n");
+	mutex_unlock(&imx074_mut);
+
+	return rc;
+}
+
+static int imx074_sensor_probe(const struct msm_camera_sensor_info *info,
+		struct msm_sensor_ctrl *s)
+{
+	int rc = 0;
+	rc = i2c_add_driver(&imx074_i2c_driver);
+	if (rc < 0 || imx074_client == NULL) {
+		rc = -ENOTSUPP;
+		goto probe_fail;
+	}
+	msm_camio_clk_rate_set(IMX074_DEFAULT_MASTER_CLK_RATE);
+	rc = imx074_probe_init_sensor(info);
+	if (rc < 0)
+		goto probe_fail;
+	s->s_init = imx074_sensor_open_init;
+	s->s_release = imx074_sensor_release;
+	s->s_config  = imx074_sensor_config;
+	s->s_mount_angle = info->sensor_platform_info->mount_angle;
+	imx074_probe_init_done(info);
+	return rc;
+
+probe_fail:
+	CDBG("imx074_sensor_probe: SENSOR PROBE FAILS!\n");
+	i2c_del_driver(&imx074_i2c_driver);
+	return rc;
+}
+
+static int __imx074_probe(struct platform_device *pdev)
+{
+
+	return msm_camera_drv_start(pdev, imx074_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+	.probe = __imx074_probe,
+	.driver = {
+		.name = "msm_camera_imx074",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init imx074_init(void)
+{
+	return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(imx074_init);
+
+MODULE_DESCRIPTION("Sony 13 MP Bayer sensor driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/media/video/msm/imx074.h b/drivers/media/video/msm/imx074.h
new file mode 100644
index 0000000..8be0fb7
--- /dev/null
+++ b/drivers/media/video/msm/imx074.h
@@ -0,0 +1,118 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef IMX074_H
+#define IMX074_H
+#include <linux/types.h>
+#include <mach/board.h>
+extern struct imx074_reg imx074_regs;
+struct reg_struct_init {
+    /* PLL setting */
+	uint8_t pre_pll_clk_div; /* 0x0305 */
+	uint8_t plstatim; /* 0x302b */
+	uint8_t reg_3024; /*ox3024*/
+	uint8_t image_orientation;  /* 0x0101*/
+	uint8_t vndmy_ablmgshlmt; /*0x300a*/
+	uint8_t y_opbaddr_start_di; /*0x3014*/
+	uint8_t reg_0x3015; /*0x3015*/
+	uint8_t reg_0x301c; /*0x301c*/
+	uint8_t reg_0x302c; /*0x302c*/
+	uint8_t reg_0x3031; /*0x3031*/
+	uint8_t reg_0x3041; /* 0x3041 */
+	uint8_t reg_0x3051; /* 0x3051 */
+	uint8_t reg_0x3053; /* 0x3053 */
+	uint8_t reg_0x3057; /* 0x3057 */
+	uint8_t reg_0x305c; /* 0x305c */
+	uint8_t reg_0x305d; /* 0x305d */
+	uint8_t reg_0x3060; /* 0x3060 */
+	uint8_t reg_0x3065; /* 0x3065 */
+	uint8_t reg_0x30aa; /* 0x30aa */
+	uint8_t reg_0x30ab;
+	uint8_t reg_0x30b0;
+	uint8_t reg_0x30b2;
+	uint8_t reg_0x30d3;
+	uint8_t reg_0x3106;
+	uint8_t reg_0x310c;
+	uint8_t reg_0x3304;
+	uint8_t reg_0x3305;
+	uint8_t reg_0x3306;
+	uint8_t reg_0x3307;
+	uint8_t reg_0x3308;
+	uint8_t reg_0x3309;
+	uint8_t reg_0x330a;
+	uint8_t reg_0x330b;
+	uint8_t reg_0x330c;
+	uint8_t reg_0x330d;
+	uint8_t reg_0x330f;
+	uint8_t reg_0x3381;
+};
+
+struct reg_struct {
+	uint8_t pll_multiplier; /* 0x0307 */
+	uint8_t frame_length_lines_hi; /* 0x0340*/
+	uint8_t frame_length_lines_lo; /* 0x0341*/
+	uint8_t y_addr_start;  /* 0x347 */
+	uint8_t y_add_end;  /* 0x034b */
+	uint8_t x_output_size_msb;  /* 0x034c */
+	uint8_t x_output_size_lsb;  /* 0x034d */
+	uint8_t y_output_size_msb; /* 0x034e */
+	uint8_t y_output_size_lsb; /* 0x034f */
+	uint8_t x_even_inc;  /* 0x0381 */
+	uint8_t x_odd_inc; /* 0x0383 */
+	uint8_t y_even_inc;  /* 0x0385 */
+	uint8_t y_odd_inc; /* 0x0387 */
+	uint8_t hmodeadd;   /* 0x3001 */
+	uint8_t vmodeadd;   /* 0x3016 */
+	uint8_t vapplinepos_start;/*ox3069*/
+	uint8_t vapplinepos_end;/*306b*/
+	uint8_t shutter;	/* 0x3086 */
+	uint8_t haddave;	/* 0x30e8 */
+	uint8_t lanesel;    /* 0x3301 */
+};
+
+struct imx074_i2c_reg_conf {
+	unsigned short waddr;
+	unsigned short wdata;
+};
+
+enum imx074_test_mode_t {
+	TEST_OFF,
+	TEST_1,
+	TEST_2,
+	TEST_3
+};
+
+enum imx074_resolution_t {
+	QTR_SIZE,
+	FULL_SIZE,
+	INVALID_SIZE
+};
+enum imx074_setting {
+	RES_PREVIEW,
+	RES_CAPTURE
+};
+enum mt9p012_reg_update {
+	/* Sensor egisters that need to be updated during initialization */
+	REG_INIT,
+	/* Sensor egisters that needs periodic I2C writes */
+	UPDATE_PERIODIC,
+	/* All the sensor Registers will be updated */
+	UPDATE_ALL,
+	/* Not valid update */
+	UPDATE_INVALID
+};
+
+struct imx074_reg {
+	const struct reg_struct_init  *reg_pat_init;
+	const struct reg_struct  *reg_pat;
+};
+#endif /* IMX074_H */
diff --git a/drivers/media/video/msm/imx074_reg.c b/drivers/media/video/msm/imx074_reg.c
new file mode 100644
index 0000000..ccc9b2f
--- /dev/null
+++ b/drivers/media/video/msm/imx074_reg.c
@@ -0,0 +1,111 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "imx074.h"
+const struct reg_struct_init imx074_reg_init[1] = {
+	{
+		/* PLL setting */
+		0x02,	/* pll_divider 0x0305 */
+		0x4B,	/* plstatim 0x302b */
+		0x03,	/* reg_3024 */
+		0x00,	/* image_orientation 0x0101 */
+		0x80,	/* vndmy_ablmgshlmt 0x300a*/
+		0x08,	/* y_opbaddr_start_di 3014*/
+		0x37,	/* 0x3015*/
+		0x01,	/* 0x301c*/
+		0x05,	/* 0x302c*/
+		0x26,	/* 0x3031*/
+		0x60,	/* 0x3041*/
+		0x24,	/* 0x3051 CLK DIV*/
+		0x34,	/* 0x3053*/
+		0xc0,	/* 0x3057*/
+		0x09,	/* 0x305c*/
+		0x07,	/* 0x305d */
+		0x30,	/* 0x3060 */
+		0x00,	/* 0x3065 */
+		0x08,	/* 0x30aa */
+		0x1c,	/* 0x30ab */
+		0x32,	/* 0x30b0 */
+		0x83,	/* 0x30b2 */
+		0x04,	/* 0x30d3 */
+		0x78,	/* 0x3106 */
+		0x82,	/* 0x310c */
+		0x05,	/* 0x3304 */
+		0x04,	/* 0x3305 */
+		0x11,	/* 0x3306 */
+		0x02,	/* 0x3307 */
+		0x0c,	/* 0x3308 */
+		0x06,	/* 0x3309 */
+		0x08,	/* 0x330a */
+		0x04,	/* 0x330b */
+		0x08,	/* 0x330c */
+		0x06,	/* 0x330d */
+		0x01,	/* 0x330f */
+		0x00,	/* 0x3381 */
+
+	}
+};
+
+/* Preview / Snapshot register settings	*/
+const struct reg_struct	imx074_reg_pat[2] = {
+	/*preview*/
+	{
+		0x2D, /*pll_multiplier*/
+		0x06, /*frame_length_lines_hi 0x0340*/
+		0x2D, /* frame_length_lines_lo 0x0341*/
+		0x00, /* y_addr_start 0x347 */
+		0x2F, /* y_add_end 0x034b */
+		0x08, /* x_output_size_msb0x034c */
+		0x38, /* x_output_size_lsb0x034d */
+		0x06, /*  y_output_size_msb0x034e */
+		0x18, /*  y_output_size_lsb0x034f */
+		0x01, /* x_even_inc 0x0381 */
+		0x03, /* x_odd_inc 0x0383 */
+		0x01, /* y_even_inc 0x0385 */
+		0x03, /* y_odd_inc 0x0387 */
+		0x80, /* hmodeadd0x3001 */
+		0x16, /* vmodeadd0x3016 */
+		0x24, /* vapplinepos_startox3069*/
+		0x53, /* vapplinepos_end306b*/
+		0x00,/*  shutter 0x3086 */
+		0x80, /* haddave 0x30e8 */
+		0x83, /* lanesel 0x3301 */
+	},
+
+	/*snapshot*/
+	{
+		0x26, /*pll_multiplier*/
+		0x0C, /* frame_length_lines_hi 0x0340*/
+		0x90, /* frame_length_lines_lo 0x0341*/
+		0x00, /* y_addr_start 0x347 */
+		0x2F, /* y_add_end 0x034b */
+		0x10, /* x_output_size_msb0x034c */
+		0x70, /* x_output_size_lsb0x034d */
+		0x0c, /* y_output_size_msb0x034e */
+		0x30, /* y_output_size_lsb0x034f */
+		0x01, /* x_even_inc 0x0381 */
+		0x01, /* x_odd_inc 0x0383 */
+		0x01, /* y_even_inc 0x0385 */
+		0x01, /* y_odd_inc 0x0387 */
+		0x00, /* hmodeadd0x3001 */
+		0x06, /* vmodeadd0x3016 */
+		0x24, /* vapplinepos_startox3069*/
+		0x53, /* vapplinepos_end306b*/
+		0x00, /* shutter 0x3086 */
+		0x00, /* haddave 0x30e8 */
+		0x03, /* lanesel 0x3301 */
+	}
+};
+struct imx074_reg imx074_regs = {
+	.reg_pat_init = &imx074_reg_init[0],
+	.reg_pat = &imx074_reg_pat[0],
+};
diff --git a/drivers/media/video/msm/io/Makefile b/drivers/media/video/msm/io/Makefile
new file mode 100644
index 0000000..64df7fb
--- /dev/null
+++ b/drivers/media/video/msm/io/Makefile
@@ -0,0 +1,17 @@
+GCC_VERSION      := $(shell $(CONFIG_SHELL) $(PWD)/scripts/gcc-version.sh $(CROSS_COMPILE)gcc)
+
+obj-$(CONFIG_MSM_CAMERA)   += msm_camera_io_util.o
+EXTRA_CFLAGS += -Idrivers/media/video/msm
+ifeq ($(CONFIG_MSM_CAMERA_V4L2),y)
+  obj-$(CONFIG_MSM_CAMERA) += msm_camera_i2c.o msm_camera_i2c_mux.o
+  obj-$(CONFIG_ARCH_MSM7X27A) += msm_io_7x27a_v4l2.o
+  obj-$(CONFIG_ARCH_MSM8X60) += msm_io_vfe31_v4l2.o
+  obj-$(CONFIG_ARCH_MSM7X30) += msm_io_vfe31_v4l2.o
+else
+  obj-$(CONFIG_ARCH_MSM7X27A) += msm_io_7x27a.o
+  obj-$(CONFIG_ARCH_MSM8X60) += msm_io_8x60.o
+  obj-$(CONFIG_ARCH_MSM7X30) += msm_io_vfe31.o
+endif
+obj-$(CONFIG_ARCH_MSM_ARM11) += msm_io7x.o
+obj-$(CONFIG_ARCH_QSD8X50) += msm_io8x.o
+obj-$(CONFIG_ARCH_MSM8960) += msm_io_8960.o
diff --git a/drivers/media/video/msm/io/msm_camera_i2c.c b/drivers/media/video/msm/io/msm_camera_i2c.c
new file mode 100644
index 0000000..6f45637
--- /dev/null
+++ b/drivers/media/video/msm/io/msm_camera_i2c.c
@@ -0,0 +1,429 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "msm_camera_i2c.h"
+
+int32_t msm_camera_i2c_rxdata(struct msm_camera_i2c_client *dev_client,
+	unsigned char *rxdata, int data_length)
+{
+	int32_t rc = 0;
+	uint16_t saddr = dev_client->client->addr >> 1;
+	struct i2c_msg msgs[] = {
+		{
+			.addr  = saddr,
+			.flags = 0,
+			.len   = dev_client->addr_type,
+			.buf   = rxdata,
+		},
+		{
+			.addr  = saddr,
+			.flags = I2C_M_RD,
+			.len   = data_length,
+			.buf   = rxdata,
+		},
+	};
+	rc = i2c_transfer(dev_client->client->adapter, msgs, 2);
+	if (rc < 0)
+		S_I2C_DBG("msm_camera_i2c_rxdata failed 0x%x\n", saddr);
+	return rc;
+}
+
+int32_t msm_camera_i2c_txdata(struct msm_camera_i2c_client *dev_client,
+				unsigned char *txdata, int length)
+{
+	int32_t rc = 0;
+	uint16_t saddr = dev_client->client->addr >> 1;
+	struct i2c_msg msg[] = {
+		{
+			.addr = saddr,
+			.flags = 0,
+			.len = length,
+			.buf = txdata,
+		 },
+	};
+	rc = i2c_transfer(dev_client->client->adapter, msg, 1);
+	if (rc < 0)
+		S_I2C_DBG("msm_camera_i2c_txdata faild 0x%x\n", saddr);
+	return 0;
+}
+
+int32_t msm_camera_i2c_write(struct msm_camera_i2c_client *client,
+	uint16_t addr, uint16_t data,
+	enum msm_camera_i2c_data_type data_type)
+{
+	int32_t rc = -EFAULT;
+	unsigned char buf[client->addr_type+data_type];
+	uint8_t len = 0;
+	if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+		&& client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+		|| (data_type != MSM_CAMERA_I2C_BYTE_DATA
+		&& data_type != MSM_CAMERA_I2C_WORD_DATA))
+		return rc;
+
+	S_I2C_DBG("%s reg addr = 0x%x data type: %d\n",
+			  __func__, addr, data_type);
+	if (client->addr_type == MSM_CAMERA_I2C_BYTE_ADDR) {
+		buf[0] = addr;
+		S_I2C_DBG("%s byte %d: 0x%x\n", __func__, len, buf[len]);
+		len = 1;
+	} else if (client->addr_type == MSM_CAMERA_I2C_WORD_ADDR) {
+		buf[0] = addr >> BITS_PER_BYTE;
+		buf[1] = addr;
+		S_I2C_DBG("%s byte %d: 0x%x\n", __func__, len, buf[len]);
+		S_I2C_DBG("%s byte %d: 0x%x\n", __func__, len+1, buf[len+1]);
+		len = 2;
+	}
+	S_I2C_DBG("Data: 0x%x\n", data);
+	if (data_type == MSM_CAMERA_I2C_BYTE_DATA) {
+		buf[len] = data;
+		S_I2C_DBG("Byte %d: 0x%x\n", len, buf[len]);
+		len += 1;
+	} else if (data_type == MSM_CAMERA_I2C_WORD_DATA) {
+		buf[len] = data >> BITS_PER_BYTE;
+		buf[len+1] = data;
+		S_I2C_DBG("Byte %d: 0x%x\n", len, buf[len]);
+		S_I2C_DBG("Byte %d: 0x%x\n", len+1, buf[len+1]);
+		len += 2;
+	}
+
+	rc = msm_camera_i2c_txdata(client, buf, len);
+	if (rc < 0)
+		S_I2C_DBG("%s fail\n", __func__);
+	return rc;
+}
+
+int32_t msm_camera_i2c_write_seq(struct msm_camera_i2c_client *client,
+	uint16_t addr, uint8_t *data, uint16_t num_byte)
+{
+	int32_t rc = -EFAULT;
+	unsigned char buf[client->addr_type+num_byte];
+	uint8_t len = 0, i = 0;
+
+	if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+		&& client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+		|| num_byte == 0)
+		return rc;
+
+	S_I2C_DBG("%s reg addr = 0x%x num bytes: %d\n",
+			  __func__, addr, num_byte);
+	if (client->addr_type == MSM_CAMERA_I2C_BYTE_ADDR) {
+		buf[0] = addr;
+		S_I2C_DBG("%s byte %d: 0x%x\n", __func__, len, buf[len]);
+		len = 1;
+	} else if (client->addr_type == MSM_CAMERA_I2C_WORD_ADDR) {
+		buf[0] = addr >> BITS_PER_BYTE;
+		buf[1] = addr;
+		S_I2C_DBG("%s byte %d: 0x%x\n", __func__, len, buf[len]);
+		S_I2C_DBG("%s byte %d: 0x%x\n", __func__, len+1, buf[len+1]);
+		len = 2;
+	}
+	for (i = 0; i < num_byte; i++) {
+		buf[i+len] = data[i];
+		S_I2C_DBG("Byte %d: 0x%x\n", i+len, buf[i+len]);
+		S_I2C_DBG("Data: 0x%x\n", data[i]);
+	}
+
+	rc = msm_camera_i2c_txdata(client, buf, len+num_byte);
+	if (rc < 0)
+		S_I2C_DBG("%s fail\n", __func__);
+	return rc;
+}
+
+int32_t msm_camera_i2c_set_mask(struct msm_camera_i2c_client *client,
+	uint16_t addr, uint16_t mask,
+	enum msm_camera_i2c_data_type data_type, uint16_t set_mask)
+{
+	int32_t rc;
+	uint16_t reg_data;
+
+	rc = msm_camera_i2c_read(client, addr, &reg_data, data_type);
+	if (rc < 0) {
+		S_I2C_DBG("%s read fail\n", __func__);
+		return rc;
+	}
+	S_I2C_DBG("%s addr: 0x%x data: 0x%x setmask: 0x%x\n",
+			__func__, addr, reg_data, mask);
+
+	if (set_mask)
+		reg_data |= mask;
+	else
+		reg_data &= ~mask;
+	S_I2C_DBG("%s write: 0x%x\n", __func__, reg_data);
+
+	rc = msm_camera_i2c_write(client, addr, reg_data, data_type);
+	if (rc < 0)
+		S_I2C_DBG("%s write fail\n", __func__);
+
+	return rc;
+}
+
+int32_t msm_camera_i2c_compare(struct msm_camera_i2c_client *client,
+	uint16_t addr, uint16_t data,
+	enum msm_camera_i2c_data_type data_type)
+{
+	int32_t rc = -EIO;
+	uint16_t reg_data = 0;
+	int data_len = 0;
+	switch (data_type) {
+	case MSM_CAMERA_I2C_BYTE_DATA:
+	case MSM_CAMERA_I2C_WORD_DATA:
+		data_len = data_type;
+		break;
+	case MSM_CAMERA_I2C_SET_BYTE_MASK:
+	case MSM_CAMERA_I2C_UNSET_BYTE_MASK:
+		data_len = MSM_CAMERA_I2C_BYTE_DATA;
+		break;
+	case MSM_CAMERA_I2C_SET_WORD_MASK:
+	case MSM_CAMERA_I2C_UNSET_WORD_MASK:
+		data_len = MSM_CAMERA_I2C_WORD_DATA;
+		break;
+	default:
+		pr_err("%s: Unsupport data type: %d\n", __func__, data_type);
+		break;
+	}
+
+	rc = msm_camera_i2c_read(client,
+		addr, &reg_data, data_len);
+	if (rc < 0)
+		return rc;
+
+	rc = 0;
+	switch (data_type) {
+	case MSM_CAMERA_I2C_BYTE_DATA:
+	case MSM_CAMERA_I2C_WORD_DATA:
+		if (data == reg_data)
+			return rc;
+		break;
+	case MSM_CAMERA_I2C_SET_BYTE_MASK:
+	case MSM_CAMERA_I2C_SET_WORD_MASK:
+		if ((reg_data & data) == data)
+			return rc;
+		break;
+	case MSM_CAMERA_I2C_UNSET_BYTE_MASK:
+	case MSM_CAMERA_I2C_UNSET_WORD_MASK:
+		if (!(reg_data & data))
+			return rc;
+		break;
+	default:
+		pr_err("%s: Unsupport data type: %d\n", __func__, data_type);
+		break;
+	}
+
+	S_I2C_DBG("%s: Register and data does not match\n", __func__);
+	rc = 1;
+	return rc;
+}
+
+int32_t msm_camera_i2c_poll(struct msm_camera_i2c_client *client,
+	uint16_t addr, uint16_t data,
+	enum msm_camera_i2c_data_type data_type)
+{
+	int32_t rc = -EIO;
+	int i;
+	S_I2C_DBG("%s: addr: 0x%x data: 0x%x dt: %d\n",
+		__func__, addr, data, data_type);
+
+	for (i = 0; i < 20; i++) {
+		rc = msm_camera_i2c_compare(client,
+			addr, data, data_type);
+		if (rc == 0 || rc < 0)
+			break;
+		usleep_range(10000, 11000);
+	}
+	return rc;
+}
+
+int32_t msm_camera_i2c_write_tbl(struct msm_camera_i2c_client *client,
+	struct msm_camera_i2c_reg_conf *reg_conf_tbl, uint16_t size,
+	enum msm_camera_i2c_data_type data_type)
+{
+	int i;
+	int32_t rc = -EFAULT;
+	for (i = 0; i < size; i++) {
+		enum msm_camera_i2c_data_type dt;
+		if (reg_conf_tbl->cmd_type == MSM_CAMERA_I2C_CMD_POLL) {
+			rc = msm_camera_i2c_poll(client, reg_conf_tbl->reg_addr,
+				reg_conf_tbl->reg_data, reg_conf_tbl->dt);
+		} else {
+			if (reg_conf_tbl->dt == 0)
+				dt = data_type;
+			else
+				dt = reg_conf_tbl->dt;
+
+			switch (dt) {
+			case MSM_CAMERA_I2C_BYTE_DATA:
+			case MSM_CAMERA_I2C_WORD_DATA:
+				rc = msm_camera_i2c_write(
+					client,
+					reg_conf_tbl->reg_addr,
+					reg_conf_tbl->reg_data, dt);
+				break;
+			case MSM_CAMERA_I2C_SET_BYTE_MASK:
+				rc = msm_camera_i2c_set_mask(client,
+					reg_conf_tbl->reg_addr,
+					reg_conf_tbl->reg_data,
+					MSM_CAMERA_I2C_BYTE_DATA, 1);
+				break;
+			case MSM_CAMERA_I2C_UNSET_BYTE_MASK:
+				rc = msm_camera_i2c_set_mask(client,
+					reg_conf_tbl->reg_addr,
+					reg_conf_tbl->reg_data,
+					MSM_CAMERA_I2C_BYTE_DATA, 0);
+				break;
+			case MSM_CAMERA_I2C_SET_WORD_MASK:
+				rc = msm_camera_i2c_set_mask(client,
+					reg_conf_tbl->reg_addr,
+					reg_conf_tbl->reg_data,
+					MSM_CAMERA_I2C_WORD_DATA, 1);
+				break;
+			case MSM_CAMERA_I2C_UNSET_WORD_MASK:
+				rc = msm_camera_i2c_set_mask(client,
+					reg_conf_tbl->reg_addr,
+					reg_conf_tbl->reg_data,
+					MSM_CAMERA_I2C_WORD_DATA, 0);
+				break;
+			default:
+				pr_err("%s: Unsupport data type: %d\n",
+					__func__, dt);
+				break;
+			}
+		}
+		if (rc < 0)
+			break;
+		reg_conf_tbl++;
+	}
+	return rc;
+}
+
+int32_t msm_camera_i2c_read(struct msm_camera_i2c_client *client,
+	uint16_t addr, uint16_t *data,
+	enum msm_camera_i2c_data_type data_type)
+{
+	int32_t rc = -EFAULT;
+	unsigned char buf[client->addr_type+data_type];
+
+	if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+		&& client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+		|| (data_type != MSM_CAMERA_I2C_BYTE_DATA
+		&& data_type != MSM_CAMERA_I2C_WORD_DATA))
+		return rc;
+
+	if (client->addr_type == MSM_CAMERA_I2C_BYTE_ADDR) {
+		buf[0] = addr;
+	} else if (client->addr_type == MSM_CAMERA_I2C_WORD_ADDR) {
+		buf[0] = addr >> BITS_PER_BYTE;
+		buf[1] = addr;
+	}
+	rc = msm_camera_i2c_rxdata(client, buf, data_type);
+	if (rc < 0) {
+		S_I2C_DBG("%s fail\n", __func__);
+		return rc;
+	}
+	if (data_type == MSM_CAMERA_I2C_BYTE_DATA)
+		*data = buf[0];
+	else
+		*data = buf[0] << 8 | buf[1];
+
+	S_I2C_DBG("%s addr = 0x%x data: 0x%x\n", __func__, addr, *data);
+	return rc;
+}
+
+int32_t msm_camera_i2c_read_seq(struct msm_camera_i2c_client *client,
+	uint16_t addr, uint8_t *data, uint16_t num_byte)
+{
+	int32_t rc = -EFAULT;
+	unsigned char buf[client->addr_type+num_byte];
+	int i;
+
+	if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+		&& client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+		|| num_byte == 0)
+		return rc;
+
+	if (client->addr_type == MSM_CAMERA_I2C_BYTE_ADDR) {
+		buf[0] = addr;
+	} else if (client->addr_type == MSM_CAMERA_I2C_WORD_ADDR) {
+		buf[0] = addr >> BITS_PER_BYTE;
+		buf[1] = addr;
+	}
+	rc = msm_camera_i2c_rxdata(client, buf, num_byte);
+	if (rc < 0) {
+		S_I2C_DBG("%s fail\n", __func__);
+		return rc;
+	}
+
+	S_I2C_DBG("%s addr = 0x%x", __func__, addr);
+	for (i = 0; i < num_byte; i++) {
+		data[i] = buf[i];
+		S_I2C_DBG("Byte %d: 0x%x\n", i, buf[i]);
+		S_I2C_DBG("Data: 0x%x\n", data[i]);
+	}
+	return rc;
+}
+
+int32_t msm_sensor_write_conf_array(struct msm_camera_i2c_client *client,
+			struct msm_camera_i2c_conf_array *array, uint16_t index)
+{
+	int32_t rc;
+
+	rc = msm_camera_i2c_write_tbl(client,
+		(struct msm_camera_i2c_reg_conf *) array[index].conf,
+		array[index].size, array[index].data_type);
+	if (array[index].delay > 20)
+		msleep(array[index].delay);
+	else
+		usleep_range(array[index].delay*1000,
+					(array[index].delay+1)*1000);
+	return rc;
+}
+
+int32_t msm_sensor_write_enum_conf_array(struct msm_camera_i2c_client *client,
+			struct msm_camera_i2c_enum_conf_array *conf,
+			uint16_t enum_val)
+{
+	int32_t rc = -1, i;
+	for (i = 0; i < conf->num_enum; i++) {
+		if (conf->conf_enum[i] == enum_val)
+			break;
+		if (conf->conf_enum[i] > enum_val)
+			break;
+	}
+	if (i == conf->num_enum)
+		i = conf->num_enum - 1;
+
+	if (i >= conf->num_index)
+		return rc;
+
+	rc = msm_sensor_write_all_conf_array(client,
+		&conf->conf[i*conf->num_conf], conf->num_conf);
+
+	if (conf->delay > 20)
+		msleep(conf->delay);
+	else
+		usleep_range(conf->delay*1000,
+					(conf->delay+1)*1000);
+	return rc;
+}
+
+int32_t msm_sensor_write_all_conf_array(struct msm_camera_i2c_client *client,
+			struct msm_camera_i2c_conf_array *array, uint16_t size)
+{
+	int32_t rc = 0, i;
+	for (i = 0; i < size; i++) {
+		rc = msm_sensor_write_conf_array(client, array, i);
+		if (rc < 0)
+			break;
+	}
+	return rc;
+}
+
+
diff --git a/drivers/media/video/msm/io/msm_camera_i2c.h b/drivers/media/video/msm/io/msm_camera_i2c.h
new file mode 100644
index 0000000..2fbf5ca
--- /dev/null
+++ b/drivers/media/video/msm/io/msm_camera_i2c.h
@@ -0,0 +1,120 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MSM_CAMERA_I2C_H
+#define MSM_CAMERA_I2C_H
+
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <mach/camera.h>
+
+#define CONFIG_MSM_CAMERA_I2C_DBG 0
+
+#if CONFIG_MSM_CAMERA_I2C_DBG
+#define S_I2C_DBG(fmt, args...) printk(fmt, ##args)
+#else
+#define S_I2C_DBG(fmt, args...) CDBG(fmt, ##args)
+#endif
+
+enum msm_camera_i2c_reg_addr_type {
+	MSM_CAMERA_I2C_BYTE_ADDR = 1,
+	MSM_CAMERA_I2C_WORD_ADDR,
+};
+
+struct msm_camera_i2c_client {
+	struct i2c_client *client;
+	enum msm_camera_i2c_reg_addr_type addr_type;
+};
+
+enum msm_camera_i2c_data_type {
+	MSM_CAMERA_I2C_BYTE_DATA = 1,
+	MSM_CAMERA_I2C_WORD_DATA,
+	MSM_CAMERA_I2C_SET_BYTE_MASK,
+	MSM_CAMERA_I2C_UNSET_BYTE_MASK,
+	MSM_CAMERA_I2C_SET_WORD_MASK,
+	MSM_CAMERA_I2C_UNSET_WORD_MASK,
+};
+
+enum msm_camera_i2c_cmd_type {
+	MSM_CAMERA_I2C_CMD_WRITE,
+	MSM_CAMERA_I2C_CMD_POLL,
+};
+
+struct msm_camera_i2c_reg_conf {
+	uint16_t reg_addr;
+	uint16_t reg_data;
+	enum msm_camera_i2c_data_type dt;
+	enum msm_camera_i2c_cmd_type cmd_type;
+};
+
+struct msm_camera_i2c_conf_array {
+	struct msm_camera_i2c_reg_conf *conf;
+	uint16_t size;
+	uint16_t delay;
+	enum msm_camera_i2c_data_type data_type;
+};
+
+struct msm_camera_i2c_enum_conf_array {
+	struct msm_camera_i2c_conf_array *conf;
+	int *conf_enum;
+	uint16_t num_enum;
+	uint16_t num_index;
+	uint16_t num_conf;
+	uint16_t delay;
+	enum msm_camera_i2c_data_type data_type;
+};
+
+int32_t msm_camera_i2c_rxdata(struct msm_camera_i2c_client *client,
+	unsigned char *rxdata, int data_length);
+
+int32_t msm_camera_i2c_txdata(struct msm_camera_i2c_client *client,
+	unsigned char *txdata, int length);
+
+int32_t msm_camera_i2c_read(struct msm_camera_i2c_client *client,
+	uint16_t addr, uint16_t *data,
+	enum msm_camera_i2c_data_type data_type);
+
+int32_t msm_camera_i2c_read_seq(struct msm_camera_i2c_client *client,
+	uint16_t addr, uint8_t *data, uint16_t num_byte);
+
+int32_t msm_camera_i2c_write(struct msm_camera_i2c_client *client,
+	uint16_t addr, uint16_t data,
+	enum msm_camera_i2c_data_type data_type);
+
+int32_t msm_camera_i2c_write_seq(struct msm_camera_i2c_client *client,
+	uint16_t addr, uint8_t *data, uint16_t num_byte);
+
+int32_t msm_camera_i2c_set_mask(struct msm_camera_i2c_client *client,
+	uint16_t addr, uint16_t mask,
+	enum msm_camera_i2c_data_type data_type, uint16_t flag);
+
+int32_t msm_camera_i2c_compare(struct msm_camera_i2c_client *client,
+	uint16_t addr, uint16_t data,
+	enum msm_camera_i2c_data_type data_type);
+
+int32_t msm_camera_i2c_poll(struct msm_camera_i2c_client *client,
+	uint16_t addr, uint16_t data,
+	enum msm_camera_i2c_data_type data_type);
+
+int32_t msm_camera_i2c_write_tbl(struct msm_camera_i2c_client *client,
+	struct msm_camera_i2c_reg_conf *reg_conf_tbl, uint16_t size,
+	enum msm_camera_i2c_data_type data_type);
+
+int32_t msm_sensor_write_conf_array(struct msm_camera_i2c_client *client,
+	struct msm_camera_i2c_conf_array *array, uint16_t index);
+
+int32_t msm_sensor_write_enum_conf_array(struct msm_camera_i2c_client *client,
+	struct msm_camera_i2c_enum_conf_array *conf, uint16_t enum_val);
+
+int32_t msm_sensor_write_all_conf_array(struct msm_camera_i2c_client *client,
+	struct msm_camera_i2c_conf_array *array, uint16_t size);
+#endif
diff --git a/drivers/media/video/msm/io/msm_camera_i2c_mux.c b/drivers/media/video/msm/io/msm_camera_i2c_mux.c
new file mode 100644
index 0000000..60f3e10
--- /dev/null
+++ b/drivers/media/video/msm/io/msm_camera_i2c_mux.c
@@ -0,0 +1,187 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <mach/board.h>
+#include <mach/camera.h>
+#include "msm.h"
+#include "msm_camera_i2c_mux.h"
+
+static int msm_i2c_mux_config(struct i2c_mux_device *mux_device, uint8_t *mode)
+{
+	uint32_t val;
+	val = msm_camera_io_r(mux_device->ctl_base);
+	if (*mode == MODE_DUAL) {
+		msm_camera_io_w(val | 0x3, mux_device->ctl_base);
+	} else if (*mode == MODE_L) {
+		msm_camera_io_w(((val | 0x2) & ~(0x1)), mux_device->ctl_base);
+		val = msm_camera_io_r(mux_device->ctl_base);
+		CDBG("the camio mode config left value is %d\n", val);
+	} else {
+		msm_camera_io_w(((val | 0x1) & ~(0x2)), mux_device->ctl_base);
+		val = msm_camera_io_r(mux_device->ctl_base);
+		CDBG("the camio mode config right value is %d\n", val);
+	}
+	return 0;
+}
+
+static int msm_i2c_mux_init(struct i2c_mux_device *mux_device)
+{
+	int rc = 0, val = 0;
+	if (mux_device->use_count == 0) {
+		mux_device->ctl_base = ioremap(mux_device->ctl_mem->start,
+			resource_size(mux_device->ctl_mem));
+		if (!mux_device->ctl_base) {
+			rc = -ENOMEM;
+			return rc;
+		}
+		mux_device->rw_base = ioremap(mux_device->rw_mem->start,
+			resource_size(mux_device->rw_mem));
+		if (!mux_device->rw_base) {
+			rc = -ENOMEM;
+			iounmap(mux_device->ctl_base);
+			return rc;
+		}
+		val = msm_camera_io_r(mux_device->rw_base);
+		msm_camera_io_w((val | 0x200), mux_device->rw_base);
+	}
+	mux_device->use_count++;
+	return 0;
+};
+
+static int msm_i2c_mux_release(struct i2c_mux_device *mux_device)
+{
+	int val = 0;
+	mux_device->use_count--;
+	if (mux_device->use_count == 0) {
+		val = msm_camera_io_r(mux_device->rw_base);
+		msm_camera_io_w((val & ~0x200), mux_device->rw_base);
+		iounmap(mux_device->rw_base);
+		iounmap(mux_device->ctl_base);
+	}
+	return 0;
+}
+
+static long msm_i2c_mux_subdev_ioctl(struct v4l2_subdev *sd,
+			unsigned int cmd, void *arg)
+{
+	struct i2c_mux_device *mux_device;
+	int rc = 0;
+	mux_device = v4l2_get_subdevdata(sd);
+	if (mux_device == NULL) {
+		rc = -ENOMEM;
+		return rc;
+	}
+	mutex_lock(&mux_device->mutex);
+	switch (cmd) {
+	case VIDIOC_MSM_I2C_MUX_CFG:
+		rc = msm_i2c_mux_config(mux_device, (uint8_t *) arg);
+		break;
+	case VIDIOC_MSM_I2C_MUX_INIT:
+		rc = msm_i2c_mux_init(mux_device);
+		break;
+	case VIDIOC_MSM_I2C_MUX_RELEASE:
+		rc = msm_i2c_mux_release(mux_device);
+		break;
+	default:
+		rc = -ENOIOCTLCMD;
+	}
+	mutex_unlock(&mux_device->mutex);
+	return rc;
+}
+
+static struct v4l2_subdev_core_ops msm_i2c_mux_subdev_core_ops = {
+	.ioctl = &msm_i2c_mux_subdev_ioctl,
+};
+
+static const struct v4l2_subdev_ops msm_i2c_mux_subdev_ops = {
+	.core = &msm_i2c_mux_subdev_core_ops,
+};
+
+static int __devinit i2c_mux_probe(struct platform_device *pdev)
+{
+	struct i2c_mux_device *mux_device;
+	int rc = 0;
+	CDBG("%s: device id = %d\n", __func__, pdev->id);
+	mux_device = kzalloc(sizeof(struct i2c_mux_device), GFP_KERNEL);
+	if (!mux_device) {
+		pr_err("%s: no enough memory\n", __func__);
+		return -ENOMEM;
+	}
+
+	v4l2_subdev_init(&mux_device->subdev, &msm_i2c_mux_subdev_ops);
+	v4l2_set_subdevdata(&mux_device->subdev, mux_device);
+	platform_set_drvdata(pdev, &mux_device->subdev);
+	mutex_init(&mux_device->mutex);
+
+	mux_device->ctl_mem = platform_get_resource_byname(pdev,
+					IORESOURCE_MEM, "i2c_mux_ctl");
+	if (!mux_device->ctl_mem) {
+		pr_err("%s: no mem resource?\n", __func__);
+		rc = -ENODEV;
+		goto i2c_mux_no_resource;
+	}
+	mux_device->ctl_io = request_mem_region(mux_device->ctl_mem->start,
+		resource_size(mux_device->ctl_mem), pdev->name);
+	if (!mux_device->ctl_io) {
+		pr_err("%s: no valid mem region\n", __func__);
+		rc = -EBUSY;
+		goto i2c_mux_no_resource;
+	}
+	mux_device->rw_mem = platform_get_resource_byname(pdev,
+					IORESOURCE_MEM, "i2c_mux_rw");
+	if (!mux_device->rw_mem) {
+		pr_err("%s: no mem resource?\n", __func__);
+		rc = -ENODEV;
+		goto i2c_mux_no_resource;
+	}
+	mux_device->rw_io = request_mem_region(mux_device->rw_mem->start,
+		resource_size(mux_device->rw_mem), pdev->name);
+	if (!mux_device->rw_io) {
+		pr_err("%s: no valid mem region\n", __func__);
+		rc = -EBUSY;
+		goto i2c_mux_no_resource;
+	}
+	mux_device->pdev = pdev;
+	return 0;
+
+i2c_mux_no_resource:
+	mutex_destroy(&mux_device->mutex);
+	kfree(mux_device);
+	return 0;
+}
+
+static struct platform_driver i2c_mux_driver = {
+	.probe = i2c_mux_probe,
+	.driver = {
+		.name = MSM_I2C_MUX_DRV_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init msm_camera_i2c_mux_init_module(void)
+{
+	return platform_driver_register(&i2c_mux_driver);
+}
+
+static void __exit msm_camera_i2c_mux_exit_module(void)
+{
+	platform_driver_unregister(&i2c_mux_driver);
+}
+
+module_init(msm_camera_i2c_mux_init_module);
+module_exit(msm_camera_i2c_mux_exit_module);
+MODULE_DESCRIPTION("MSM Camera I2C mux driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/io/msm_camera_i2c_mux.h b/drivers/media/video/msm/io/msm_camera_i2c_mux.h
new file mode 100644
index 0000000..94394fc
--- /dev/null
+++ b/drivers/media/video/msm/io/msm_camera_i2c_mux.h
@@ -0,0 +1,46 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MSM_I2C_MUX_H
+#define MSM_I2C_MUX_H
+
+#include <linux/io.h>
+#include <media/v4l2-subdev.h>
+
+struct i2c_mux_device {
+	struct platform_device *pdev;
+	struct v4l2_subdev subdev;
+	struct resource *ctl_mem;
+	struct resource *ctl_io;
+	void __iomem *ctl_base;
+	struct resource *rw_mem;
+	struct resource *rw_io;
+	void __iomem *rw_base;
+	struct mutex mutex;
+	unsigned use_count;
+};
+
+struct i2c_mux_cfg_params {
+	struct v4l2_subdev *subdev;
+	void *parms;
+};
+
+#define VIDIOC_MSM_I2C_MUX_CFG \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 13, struct i2c_mux_cfg_params)
+
+#define VIDIOC_MSM_I2C_MUX_INIT \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 14, struct v4l2_subdev*)
+
+#define VIDIOC_MSM_I2C_MUX_RELEASE \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 15, struct v4l2_subdev*)
+
+#endif
diff --git a/drivers/media/video/msm/io/msm_camera_io_util.c b/drivers/media/video/msm/io/msm_camera_io_util.c
new file mode 100644
index 0000000..4049266
--- /dev/null
+++ b/drivers/media/video/msm/io/msm_camera_io_util.c
@@ -0,0 +1,382 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/io.h>
+#include <mach/board.h>
+#include <mach/camera.h>
+#include <mach/gpiomux.h>
+
+#define BUFF_SIZE_128 128
+
+void msm_camera_io_w(u32 data, void __iomem *addr)
+{
+	CDBG("%s: %08x %08x\n", __func__, (int) (addr), (data));
+	writel_relaxed((data), (addr));
+}
+
+void msm_camera_io_w_mb(u32 data, void __iomem *addr)
+{
+	CDBG("%s: %08x %08x\n", __func__, (int) (addr), (data));
+	wmb();
+	writel_relaxed((data), (addr));
+	wmb();
+}
+
+u32 msm_camera_io_r(void __iomem *addr)
+{
+	uint32_t data = readl_relaxed(addr);
+	CDBG("%s: %08x %08x\n", __func__, (int) (addr), (data));
+	return data;
+}
+
+u32 msm_camera_io_r_mb(void __iomem *addr)
+{
+	uint32_t data;
+	rmb();
+	data = readl_relaxed(addr);
+	rmb();
+	CDBG("%s: %08x %08x\n", __func__, (int) (addr), (data));
+	return data;
+}
+
+void msm_camera_io_memcpy_toio(void __iomem *dest_addr,
+	void __iomem *src_addr, u32 len)
+{
+	int i;
+	u32 *d = (u32 *) dest_addr;
+	u32 *s = (u32 *) src_addr;
+
+	for (i = 0; i < len; i++)
+		writel_relaxed(*s++, d++);
+}
+
+void msm_camera_io_dump(void __iomem *addr, int size)
+{
+	char line_str[BUFF_SIZE_128], *p_str;
+	int i;
+	u32 *p = (u32 *) addr;
+	u32 data;
+	CDBG("%s: %p %d\n", __func__, addr, size);
+	line_str[0] = '\0';
+	p_str = line_str;
+	for (i = 0; i < size/4; i++) {
+		if (i % 4 == 0) {
+			snprintf(p_str, 12, "%08x: ", (u32) p);
+			p_str += 10;
+		}
+		data = readl_relaxed(p++);
+		snprintf(p_str, 12, "%08x ", data);
+		p_str += 9;
+		if ((i + 1) % 4 == 0) {
+			CDBG("%s\n", line_str);
+			line_str[0] = '\0';
+			p_str = line_str;
+		}
+	}
+	if (line_str[0] != '\0')
+		CDBG("%s\n", line_str);
+}
+
+void msm_camera_io_memcpy(void __iomem *dest_addr,
+	void __iomem *src_addr, u32 len)
+{
+	CDBG("%s: %p %p %d\n", __func__, dest_addr, src_addr, len);
+	msm_camera_io_memcpy_toio(dest_addr, src_addr, len / 4);
+	msm_camera_io_dump(dest_addr, len);
+}
+
+int msm_cam_clk_enable(struct device *dev, struct msm_cam_clk_info *clk_info,
+		struct clk **clk_ptr, int num_clk, int enable)
+{
+	int i;
+	int rc = 0;
+	if (enable) {
+		for (i = 0; i < num_clk; i++) {
+			clk_ptr[i] = clk_get(dev, clk_info[i].clk_name);
+			if (IS_ERR(clk_ptr[i])) {
+				pr_err("%s get failed\n", clk_info[i].clk_name);
+				rc = PTR_ERR(clk_ptr[i]);
+				goto cam_clk_get_err;
+			}
+			if (clk_info[i].clk_rate >= 0) {
+				rc = clk_set_rate(clk_ptr[i],
+							clk_info[i].clk_rate);
+				if (rc < 0) {
+					pr_err("%s set failed\n",
+						   clk_info[i].clk_name);
+					goto cam_clk_set_err;
+				}
+			}
+			rc = clk_prepare(clk_ptr[i]);
+			if (rc < 0) {
+				pr_err("%s prepare failed\n",
+					   clk_info[i].clk_name);
+				goto cam_clk_prepare_err;
+			}
+
+			rc = clk_enable(clk_ptr[i]);
+			if (rc < 0) {
+				pr_err("%s enable failed\n",
+					   clk_info[i].clk_name);
+				goto cam_clk_enable_err;
+			}
+		}
+	} else {
+		for (i = num_clk - 1; i >= 0; i--) {
+			if (clk_ptr[i] != NULL) {
+				clk_disable(clk_ptr[i]);
+				clk_unprepare(clk_ptr[i]);
+				clk_put(clk_ptr[i]);
+			}
+		}
+	}
+	return rc;
+
+
+cam_clk_enable_err:
+	clk_unprepare(clk_ptr[i]);
+cam_clk_prepare_err:
+cam_clk_set_err:
+	clk_put(clk_ptr[i]);
+cam_clk_get_err:
+	for (i--; i >= 0; i--) {
+		if (clk_ptr[i] != NULL) {
+			clk_disable(clk_ptr[i]);
+			clk_unprepare(clk_ptr[i]);
+			clk_put(clk_ptr[i]);
+		}
+	}
+	return rc;
+}
+
+int msm_camera_config_vreg(struct device *dev, struct camera_vreg_t *cam_vreg,
+		int num_vreg, struct regulator **reg_ptr, int config)
+{
+	int i = 0;
+	int rc = 0;
+	struct camera_vreg_t *curr_vreg;
+	if (config) {
+		for (i = 0; i < num_vreg; i++) {
+			curr_vreg = &cam_vreg[i];
+			reg_ptr[i] = regulator_get(dev,
+				curr_vreg->reg_name);
+			if (IS_ERR(reg_ptr[i])) {
+				pr_err("%s: %s get failed\n",
+					 __func__,
+					 curr_vreg->reg_name);
+				reg_ptr[i] = NULL;
+				goto vreg_get_fail;
+			}
+			if (curr_vreg->type == REG_LDO) {
+				rc = regulator_set_voltage(
+					reg_ptr[i],
+					curr_vreg->min_voltage,
+					curr_vreg->max_voltage);
+				if (rc < 0) {
+					pr_err("%s: %s set voltage failed\n",
+						__func__,
+						curr_vreg->reg_name);
+					goto vreg_set_voltage_fail;
+				}
+				if (curr_vreg->op_mode >= 0) {
+					rc = regulator_set_optimum_mode(
+						reg_ptr[i],
+						curr_vreg->op_mode);
+					if (rc < 0) {
+						pr_err(
+						"%s: %s set optimum mode failed\n",
+						__func__,
+						curr_vreg->reg_name);
+						goto vreg_set_opt_mode_fail;
+					}
+				}
+			}
+		}
+	} else {
+		for (i = num_vreg-1; i >= 0; i--) {
+			curr_vreg = &cam_vreg[i];
+			if (reg_ptr[i]) {
+				if (curr_vreg->type == REG_LDO) {
+					if (curr_vreg->op_mode >= 0) {
+						regulator_set_optimum_mode(
+							reg_ptr[i], 0);
+					}
+					regulator_set_voltage(
+						reg_ptr[i], 0, curr_vreg->
+						max_voltage);
+				}
+				regulator_put(reg_ptr[i]);
+				reg_ptr[i] = NULL;
+			}
+		}
+	}
+	return 0;
+
+vreg_unconfig:
+if (curr_vreg->type == REG_LDO)
+	regulator_set_optimum_mode(reg_ptr[i], 0);
+
+vreg_set_opt_mode_fail:
+if (curr_vreg->type == REG_LDO)
+	regulator_set_voltage(reg_ptr[i], 0,
+		curr_vreg->max_voltage);
+
+vreg_set_voltage_fail:
+	regulator_put(reg_ptr[i]);
+	reg_ptr[i] = NULL;
+
+vreg_get_fail:
+	for (i--; i >= 0; i--) {
+		curr_vreg = &cam_vreg[i];
+		goto vreg_unconfig;
+	}
+	return -ENODEV;
+}
+
+int msm_camera_enable_vreg(struct device *dev, struct camera_vreg_t *cam_vreg,
+		int num_vreg, struct regulator **reg_ptr, int enable)
+{
+	int i = 0, rc = 0;
+	if (enable) {
+		for (i = 0; i < num_vreg; i++) {
+			if (IS_ERR(reg_ptr[i])) {
+				pr_err("%s: %s null regulator\n",
+					__func__, cam_vreg[i].reg_name);
+				goto disable_vreg;
+			}
+			rc = regulator_enable(reg_ptr[i]);
+			if (rc < 0) {
+				pr_err("%s: %s enable failed\n",
+					__func__, cam_vreg[i].reg_name);
+				goto disable_vreg;
+			}
+		}
+	} else {
+		for (i = num_vreg-1; i >= 0; i--)
+			regulator_disable(reg_ptr[i]);
+	}
+	return rc;
+disable_vreg:
+	for (i--; i >= 0; i--) {
+		regulator_disable(reg_ptr[i]);
+		goto disable_vreg;
+	}
+	return rc;
+}
+
+static int config_gpio_table(struct msm_camera_gpio_conf *gpio)
+{
+	int rc = 0, i = 0;
+	uint32_t *table_on;
+	uint32_t *table_off;
+	uint32_t len;
+
+	table_on = gpio->camera_on_table;
+	table_off = gpio->camera_off_table;
+	len = gpio->camera_on_table_size;
+
+	for (i = 0; i < len; i++) {
+		rc = gpio_tlmm_config(table_on[i], GPIO_CFG_ENABLE);
+		if (rc) {
+			pr_err("%s not able to get gpio\n", __func__);
+			for (i--; i >= 0; i--)
+				gpio_tlmm_config(table_off[i],
+					GPIO_CFG_ENABLE);
+			break;
+		}
+	}
+	return rc;
+}
+
+int msm_camera_request_gpio_table(struct msm_camera_sensor_info *sinfo,
+	int gpio_en)
+{
+	int rc = 0;
+	struct msm_camera_gpio_conf *gpio_conf =
+		sinfo->sensor_platform_info->gpio_conf;
+
+	if (!gpio_conf->gpio_no_mux) {
+		if (gpio_conf->cam_gpio_req_tbl == NULL ||
+			gpio_conf->cam_gpio_common_tbl == NULL) {
+			pr_err("%s: NULL camera gpio table\n", __func__);
+			return -EFAULT;
+		}
+	}
+	if (gpio_conf->gpio_no_mux)
+		config_gpio_table(gpio_conf);
+
+	if (gpio_en) {
+		if (!gpio_conf->gpio_no_mux) {
+			if (gpio_conf->cam_gpiomux_conf_tbl != NULL) {
+				msm_gpiomux_install(
+					(struct msm_gpiomux_config *)
+					gpio_conf->cam_gpiomux_conf_tbl,
+					gpio_conf->cam_gpiomux_conf_tbl_size);
+			}
+			rc = gpio_request_array(gpio_conf->cam_gpio_common_tbl,
+				gpio_conf->cam_gpio_common_tbl_size);
+			if (rc < 0) {
+				pr_err("%s common gpio request failed\n"
+						, __func__);
+				return rc;
+			}
+		}
+		if (gpio_conf->cam_gpio_req_tbl_size) {
+			rc = gpio_request_array(gpio_conf->cam_gpio_req_tbl,
+				gpio_conf->cam_gpio_req_tbl_size);
+			if (rc < 0) {
+				pr_err("%s camera gpio"
+					"request failed\n", __func__);
+				gpio_free_array(gpio_conf->cam_gpio_common_tbl,
+					gpio_conf->cam_gpio_common_tbl_size);
+				return rc;
+			}
+		}
+	} else {
+		gpio_free_array(gpio_conf->cam_gpio_req_tbl,
+				gpio_conf->cam_gpio_req_tbl_size);
+		if (!gpio_conf->gpio_no_mux)
+			gpio_free_array(gpio_conf->cam_gpio_common_tbl,
+				gpio_conf->cam_gpio_common_tbl_size);
+	}
+	return rc;
+}
+
+int msm_camera_config_gpio_table(struct msm_camera_sensor_info *sinfo,
+	int gpio_en)
+{
+	struct msm_camera_gpio_conf *gpio_conf =
+		sinfo->sensor_platform_info->gpio_conf;
+	int rc = 0, i;
+
+	if (gpio_en) {
+		for (i = 0; i < gpio_conf->cam_gpio_set_tbl_size; i++) {
+			gpio_set_value_cansleep(
+				gpio_conf->cam_gpio_set_tbl[i].gpio,
+				gpio_conf->cam_gpio_set_tbl[i].flags);
+			usleep_range(gpio_conf->cam_gpio_set_tbl[i].delay,
+				gpio_conf->cam_gpio_set_tbl[i].delay + 1000);
+		}
+	} else {
+		for (i = gpio_conf->cam_gpio_set_tbl_size - 1; i >= 0; i--) {
+			if (gpio_conf->cam_gpio_set_tbl[i].flags)
+				gpio_set_value_cansleep(
+					gpio_conf->cam_gpio_set_tbl[i].gpio,
+					GPIOF_OUT_INIT_LOW);
+		}
+	}
+	return rc;
+}
diff --git a/drivers/media/video/msm/io/msm_io7x.c b/drivers/media/video/msm/io/msm_io7x.c
new file mode 100644
index 0000000..8804106
--- /dev/null
+++ b/drivers/media/video/msm/io/msm_io7x.c
@@ -0,0 +1,318 @@
+/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <mach/gpio.h>
+#include <mach/board.h>
+#include <mach/camera.h>
+#include <mach/clk.h>
+
+#define CAMIF_CFG_RMSK 0x1fffff
+#define CAM_SEL_BMSK 0x2
+#define CAM_PCLK_SRC_SEL_BMSK 0x60000
+#define CAM_PCLK_INVERT_BMSK 0x80000
+#define CAM_PAD_REG_SW_RESET_BMSK 0x100000
+
+#define EXT_CAM_HSYNC_POL_SEL_BMSK 0x10000
+#define EXT_CAM_VSYNC_POL_SEL_BMSK 0x8000
+#define MDDI_CLK_CHICKEN_BIT_BMSK  0x80
+
+#define CAM_SEL_SHFT 0x1
+#define CAM_PCLK_SRC_SEL_SHFT 0x11
+#define CAM_PCLK_INVERT_SHFT 0x13
+#define CAM_PAD_REG_SW_RESET_SHFT 0x14
+
+#define EXT_CAM_HSYNC_POL_SEL_SHFT 0x10
+#define EXT_CAM_VSYNC_POL_SEL_SHFT 0xF
+#define MDDI_CLK_CHICKEN_BIT_SHFT  0x7
+#define APPS_RESET_OFFSET 0x00000210
+
+static struct clk *camio_vfe_mdc_clk;
+static struct clk *camio_mdc_clk;
+static struct clk *camio_vfe_clk;
+
+static struct msm_camera_io_ext camio_ext;
+static struct resource *appio, *mdcio;
+void __iomem *appbase, *mdcbase;
+
+static struct resource *appio, *mdcio;
+void __iomem *appbase, *mdcbase;
+
+int msm_camio_clk_enable(enum msm_camio_clk_type clktype)
+{
+	int rc = -1;
+	struct clk *clk = NULL;
+
+	switch (clktype) {
+	case CAMIO_VFE_MDC_CLK:
+		clk = camio_vfe_mdc_clk = clk_get(NULL, "vfe_mdc_clk");
+		break;
+
+	case CAMIO_MDC_CLK:
+		clk = camio_mdc_clk = clk_get(NULL, "mdc_clk");
+		break;
+
+	case CAMIO_VFE_CLK:
+		clk = camio_vfe_clk = clk_get(NULL, "vfe_clk");
+		break;
+
+	default:
+		break;
+	}
+
+	if (!IS_ERR(clk)) {
+		clk_enable(clk);
+		rc = 0;
+	}
+
+	return rc;
+}
+
+int msm_camio_clk_disable(enum msm_camio_clk_type clktype)
+{
+	int rc = -1;
+	struct clk *clk = NULL;
+
+	switch (clktype) {
+	case CAMIO_VFE_MDC_CLK:
+		clk = camio_vfe_mdc_clk;
+		break;
+
+	case CAMIO_MDC_CLK:
+		clk = camio_mdc_clk;
+		break;
+
+	case CAMIO_VFE_CLK:
+		clk = camio_vfe_clk;
+		break;
+
+	default:
+		break;
+	}
+
+	if (!IS_ERR(clk)) {
+		clk_disable(clk);
+		clk_put(clk);
+		rc = 0;
+	}
+
+	return rc;
+}
+
+void msm_camio_clk_rate_set(int rate)
+{
+	struct clk *clk = camio_vfe_clk;
+
+	if (clk != ERR_PTR(-ENOENT))
+		clk_set_rate(clk, rate);
+}
+
+int msm_camio_enable(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+	struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+
+	camio_ext = camdev->ioext;
+
+	appio = request_mem_region(camio_ext.appphy,
+		camio_ext.appsz, pdev->name);
+	if (!appio) {
+		rc = -EBUSY;
+		goto enable_fail;
+	}
+
+	appbase = ioremap(camio_ext.appphy,
+		camio_ext.appsz);
+	if (!appbase) {
+		rc = -ENOMEM;
+		goto apps_no_mem;
+	}
+
+	msm_camio_clk_enable(CAMIO_VFE_CLK);
+	msm_camio_clk_enable(CAMIO_MDC_CLK);
+	return 0;
+apps_no_mem:
+	release_mem_region(camio_ext.appphy, camio_ext.appsz);
+enable_fail:
+	return rc;
+}
+
+int msm_camio_sensor_clk_on(struct platform_device *pdev)
+{
+	struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+	struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+	int32_t rc = 0;
+	camio_ext = camdev->ioext;
+	mdcio = request_mem_region(camio_ext.mdcphy,
+		camio_ext.mdcsz, pdev->name);
+	if (!mdcio)
+		rc = -EBUSY;
+	mdcbase = ioremap(camio_ext.mdcphy,
+		camio_ext.mdcsz);
+	if (!mdcbase) {
+		rc = -EINVAL;
+		goto mdc_no_mem;
+	}
+	camdev->camera_gpio_on();
+	return msm_camio_clk_enable(CAMIO_VFE_MDC_CLK);
+
+mdc_no_mem:
+	release_mem_region(camio_ext.mdcphy, camio_ext.mdcsz);
+	return rc;
+}
+
+int msm_camio_sensor_clk_off(struct platform_device *pdev)
+{
+	struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+	struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+	camdev->camera_gpio_off();
+	iounmap(mdcbase);
+	release_mem_region(camio_ext.mdcphy, camio_ext.mdcsz);
+	return msm_camio_clk_disable(CAMIO_VFE_MDC_CLK);
+}
+
+void msm_camio_disable(struct platform_device *pdev)
+{
+	iounmap(appbase);
+	release_mem_region(camio_ext.appphy, camio_ext.appsz);
+	msm_camio_clk_disable(CAMIO_VFE_CLK);
+	msm_camio_clk_disable(CAMIO_MDC_CLK);
+}
+
+void msm_disable_io_gpio_clk(struct platform_device *pdev)
+{
+	return;
+}
+
+void msm_camio_camif_pad_reg_reset(void)
+{
+	uint32_t reg;
+	uint32_t mask, value;
+
+	/* select CLKRGM_VFE_SRC_CAM_VFE_SRC:  internal source */
+	msm_camio_clk_sel(MSM_CAMIO_CLK_SRC_INTERNAL);
+
+	reg = (msm_camera_io_r_mb(mdcbase)) & CAMIF_CFG_RMSK;
+
+	mask = CAM_SEL_BMSK |
+		CAM_PCLK_SRC_SEL_BMSK |
+		CAM_PCLK_INVERT_BMSK;
+
+	value = 1 << CAM_SEL_SHFT |
+		3 << CAM_PCLK_SRC_SEL_SHFT |
+		0 << CAM_PCLK_INVERT_SHFT;
+
+	msm_camera_io_w_mb((reg & (~mask)) | (value & mask), mdcbase);
+	usleep_range(10000, 11000);
+
+	reg = (msm_camera_io_r_mb(mdcbase)) & CAMIF_CFG_RMSK;
+	mask = CAM_PAD_REG_SW_RESET_BMSK;
+	value = 1 << CAM_PAD_REG_SW_RESET_SHFT;
+	msm_camera_io_w_mb((reg & (~mask)) | (value & mask), mdcbase);
+	usleep_range(10000, 11000);
+
+	reg = (msm_camera_io_r_mb(mdcbase)) & CAMIF_CFG_RMSK;
+	mask = CAM_PAD_REG_SW_RESET_BMSK;
+	value = 0 << CAM_PAD_REG_SW_RESET_SHFT;
+	msm_camera_io_w_mb((reg & (~mask)) | (value & mask), mdcbase);
+	usleep_range(10000, 11000);
+
+	msm_camio_clk_sel(MSM_CAMIO_CLK_SRC_EXTERNAL);
+	usleep_range(10000, 11000);
+}
+
+void msm_camio_vfe_blk_reset(void)
+{
+	uint32_t val;
+
+	/* do apps reset */
+	val = msm_camera_io_r_mb(appbase + 0x00000210);
+	val |= 0x1;
+	msm_camera_io_w_mb(val, appbase + 0x00000210);
+	usleep_range(10000, 11000);
+
+	val = msm_camera_io_r_mb(appbase + 0x00000210);
+	val &= ~0x1;
+	msm_camera_io_w_mb(val, appbase + 0x00000210);
+	usleep_range(10000, 11000);
+
+	/* do axi reset */
+	val = msm_camera_io_r_mb(appbase + 0x00000208);
+	val |= 0x1;
+	msm_camera_io_w_mb(val, appbase + 0x00000208);
+	usleep_range(10000, 11000);
+
+	val = msm_camera_io_r_mb(appbase + 0x00000208);
+	val &= ~0x1;
+	msm_camera_io_w_mb(val, appbase + 0x00000208);
+	usleep_range(10000, 11000);
+}
+
+void msm_camio_camif_pad_reg_reset_2(void)
+{
+	uint32_t reg;
+	uint32_t mask, value;
+
+	reg = (msm_camera_io_r_mb(mdcbase)) & CAMIF_CFG_RMSK;
+	mask = CAM_PAD_REG_SW_RESET_BMSK;
+	value = 1 << CAM_PAD_REG_SW_RESET_SHFT;
+	msm_camera_io_w_mb((reg & (~mask)) | (value & mask), mdcbase);
+	usleep_range(10000, 11000);
+
+	reg = (msm_camera_io_r_mb(mdcbase)) & CAMIF_CFG_RMSK;
+	mask = CAM_PAD_REG_SW_RESET_BMSK;
+	value = 0 << CAM_PAD_REG_SW_RESET_SHFT;
+	msm_camera_io_w_mb((reg & (~mask)) | (value & mask), mdcbase);
+	usleep_range(10000, 11000);
+}
+
+void msm_camio_clk_sel(enum msm_camio_clk_src_type srctype)
+{
+	struct clk *clk = NULL;
+
+	clk = camio_vfe_clk;
+
+	if (clk != NULL && clk != ERR_PTR(-ENOENT)) {
+		switch (srctype) {
+		case MSM_CAMIO_CLK_SRC_INTERNAL:
+			clk_set_flags(clk, 0x00000100 << 1);
+			break;
+
+		case MSM_CAMIO_CLK_SRC_EXTERNAL:
+			clk_set_flags(clk, 0x00000100);
+			break;
+
+		default:
+			break;
+		}
+	}
+}
+
+int msm_camio_probe_on(struct platform_device *pdev)
+{
+	struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+	struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+	camdev->camera_gpio_on();
+	return msm_camio_clk_enable(CAMIO_VFE_CLK);
+}
+
+int msm_camio_probe_off(struct platform_device *pdev)
+{
+	struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+	struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+	camdev->camera_gpio_off();
+	return msm_camio_clk_disable(CAMIO_VFE_CLK);
+}
diff --git a/drivers/media/video/msm/io/msm_io8x.c b/drivers/media/video/msm/io/msm_io8x.c
new file mode 100644
index 0000000..5f1f34f
--- /dev/null
+++ b/drivers/media/video/msm/io/msm_io8x.c
@@ -0,0 +1,331 @@
+/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <mach/gpio.h>
+#include <mach/board.h>
+#include <mach/camera.h>
+#include <mach/clk.h>
+
+#define CAMIF_CFG_RMSK 0x1fffff
+#define CAM_SEL_BMSK 0x2
+#define CAM_PCLK_SRC_SEL_BMSK 0x60000
+#define CAM_PCLK_INVERT_BMSK 0x80000
+#define CAM_PAD_REG_SW_RESET_BMSK 0x100000
+
+#define EXT_CAM_HSYNC_POL_SEL_BMSK 0x10000
+#define EXT_CAM_VSYNC_POL_SEL_BMSK 0x8000
+#define MDDI_CLK_CHICKEN_BIT_BMSK  0x80
+
+#define CAM_SEL_SHFT 0x1
+#define CAM_PCLK_SRC_SEL_SHFT 0x11
+#define CAM_PCLK_INVERT_SHFT 0x13
+#define CAM_PAD_REG_SW_RESET_SHFT 0x14
+
+#define EXT_CAM_HSYNC_POL_SEL_SHFT 0x10
+#define EXT_CAM_VSYNC_POL_SEL_SHFT 0xF
+#define MDDI_CLK_CHICKEN_BIT_SHFT  0x7
+#define APPS_RESET_OFFSET 0x00000214
+
+static struct clk *camio_vfe_mdc_clk;
+static struct clk *camio_mdc_clk;
+static struct clk *camio_vfe_clk;
+static struct clk *camio_vfe_axi_clk;
+static struct msm_camera_io_ext camio_ext;
+static struct resource *appio, *mdcio;
+
+void __iomem *appbase, *mdcbase;
+
+
+int msm_camio_clk_enable(enum msm_camio_clk_type clktype)
+{
+	int rc = 0;
+	struct clk *clk = NULL;
+
+	switch (clktype) {
+	case CAMIO_VFE_MDC_CLK:
+		camio_vfe_mdc_clk = clk = clk_get(NULL, "vfe_mdc_clk");
+		break;
+
+	case CAMIO_MDC_CLK:
+		camio_mdc_clk = clk = clk_get(NULL, "mdc_clk");
+		break;
+
+	case CAMIO_VFE_CLK:
+		camio_vfe_clk = clk = clk_get(NULL, "vfe_clk");
+		break;
+
+	case CAMIO_VFE_AXI_CLK:
+		camio_vfe_axi_clk = clk = clk_get(NULL, "vfe_axi_clk");
+		break;
+
+	default:
+		break;
+	}
+
+	if (!IS_ERR(clk))
+		clk_enable(clk);
+	else
+		rc = -1;
+
+	return rc;
+}
+
+int msm_camio_clk_disable(enum msm_camio_clk_type clktype)
+{
+	int rc = 0;
+	struct clk *clk = NULL;
+
+	switch (clktype) {
+	case CAMIO_VFE_MDC_CLK:
+		clk = camio_vfe_mdc_clk;
+		break;
+
+	case CAMIO_MDC_CLK:
+		clk = camio_mdc_clk;
+		break;
+
+	case CAMIO_VFE_CLK:
+		clk = camio_vfe_clk;
+		break;
+
+	case CAMIO_VFE_AXI_CLK:
+		clk = camio_vfe_axi_clk;
+		break;
+
+	default:
+		break;
+	}
+
+	if (!IS_ERR(clk)) {
+		clk_disable(clk);
+		clk_put(clk);
+	} else
+		rc = -1;
+
+	return rc;
+}
+
+void msm_camio_clk_rate_set(int rate)
+{
+	struct clk *clk = camio_vfe_mdc_clk;
+
+	/* TODO: check return */
+	clk_set_rate(clk, rate);
+}
+
+int msm_camio_enable(struct platform_device *pdev)
+{
+	int rc = 0;
+
+	appio = request_mem_region(camio_ext.appphy,
+		camio_ext.appsz, pdev->name);
+	if (!appio) {
+		rc = -EBUSY;
+		goto enable_fail;
+	}
+
+	appbase = ioremap(camio_ext.appphy, camio_ext.appsz);
+	if (!appbase) {
+		rc = -ENOMEM;
+		goto apps_no_mem;
+	}
+	msm_camio_clk_enable(CAMIO_MDC_CLK);
+	msm_camio_clk_enable(CAMIO_VFE_AXI_CLK);
+	return 0;
+
+apps_no_mem:
+	release_mem_region(camio_ext.appphy, camio_ext.appsz);
+enable_fail:
+	return rc;
+}
+
+void msm_camio_disable(struct platform_device *pdev)
+{
+	iounmap(appbase);
+	release_mem_region(camio_ext.appphy, camio_ext.appsz);
+	msm_camio_clk_disable(CAMIO_MDC_CLK);
+	msm_camio_clk_disable(CAMIO_VFE_AXI_CLK);
+}
+
+int msm_camio_sensor_clk_on(struct platform_device *pdev)
+{
+
+	struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+	struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+	int32_t rc = 0;
+	camio_ext = camdev->ioext;
+
+	mdcio = request_mem_region(camio_ext.mdcphy,
+		camio_ext.mdcsz, pdev->name);
+	if (!mdcio)
+		rc = -EBUSY;
+	mdcbase = ioremap(camio_ext.mdcphy,
+		camio_ext.mdcsz);
+	if (!mdcbase)
+		goto mdc_no_mem;
+	camdev->camera_gpio_on();
+
+	msm_camio_clk_enable(CAMIO_VFE_CLK);
+	msm_camio_clk_enable(CAMIO_VFE_MDC_CLK);
+	return rc;
+
+
+mdc_no_mem:
+	release_mem_region(camio_ext.mdcphy, camio_ext.mdcsz);
+	return -EINVAL;
+}
+
+int msm_camio_sensor_clk_off(struct platform_device *pdev)
+{
+	struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+	struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+	camdev->camera_gpio_off();
+	iounmap(mdcbase);
+	release_mem_region(camio_ext.mdcphy, camio_ext.mdcsz);
+	msm_camio_clk_disable(CAMIO_VFE_CLK);
+	return msm_camio_clk_disable(CAMIO_VFE_MDC_CLK);
+
+}
+
+void msm_disable_io_gpio_clk(struct platform_device *pdev)
+{
+	return;
+}
+
+void msm_camio_camif_pad_reg_reset(void)
+{
+	uint32_t reg;
+	uint32_t mask, value;
+
+	/* select CLKRGM_VFE_SRC_CAM_VFE_SRC:  internal source */
+	msm_camio_clk_sel(MSM_CAMIO_CLK_SRC_INTERNAL);
+
+	reg = (msm_camera_io_r_mb(mdcbase)) & CAMIF_CFG_RMSK;
+
+	mask = CAM_SEL_BMSK |
+		CAM_PCLK_SRC_SEL_BMSK |
+		CAM_PCLK_INVERT_BMSK |
+		EXT_CAM_HSYNC_POL_SEL_BMSK |
+	    EXT_CAM_VSYNC_POL_SEL_BMSK | MDDI_CLK_CHICKEN_BIT_BMSK;
+
+	value = 1 << CAM_SEL_SHFT |
+		3 << CAM_PCLK_SRC_SEL_SHFT |
+		0 << CAM_PCLK_INVERT_SHFT |
+		0 << EXT_CAM_HSYNC_POL_SEL_SHFT |
+	    0 << EXT_CAM_VSYNC_POL_SEL_SHFT | 0 << MDDI_CLK_CHICKEN_BIT_SHFT;
+	msm_camera_io_w_mb((reg & (~mask)) | (value & mask), mdcbase);
+	usleep_range(10000, 11000);
+
+	reg = (msm_camera_io_r_mb(mdcbase)) & CAMIF_CFG_RMSK;
+	mask = CAM_PAD_REG_SW_RESET_BMSK;
+	value = 1 << CAM_PAD_REG_SW_RESET_SHFT;
+	msm_camera_io_w_mb((reg & (~mask)) | (value & mask), mdcbase);
+	usleep_range(10000, 11000);
+
+	reg = (msm_camera_io_r_mb(mdcbase)) & CAMIF_CFG_RMSK;
+	mask = CAM_PAD_REG_SW_RESET_BMSK;
+	value = 0 << CAM_PAD_REG_SW_RESET_SHFT;
+	msm_camera_io_w_mb((reg & (~mask)) | (value & mask), mdcbase);
+	usleep_range(10000, 11000);
+
+	msm_camio_clk_sel(MSM_CAMIO_CLK_SRC_EXTERNAL);
+
+	usleep_range(10000, 11000);
+
+	/* todo: check return */
+	if (camio_vfe_clk)
+		clk_set_rate(camio_vfe_clk, 96000000);
+}
+
+void msm_camio_vfe_blk_reset(void)
+{
+	uint32_t val;
+
+	val = msm_camera_io_r_mb(appbase + APPS_RESET_OFFSET);
+	val |= 0x1;
+	msm_camera_io_w_mb(val, appbase + APPS_RESET_OFFSET);
+	usleep_range(10000, 11000);
+
+	val = msm_camera_io_r_mb(appbase + APPS_RESET_OFFSET);
+	val &= ~0x1;
+	msm_camera_io_w_mb(val, appbase + APPS_RESET_OFFSET);
+	usleep_range(10000, 11000);
+}
+
+void msm_camio_camif_pad_reg_reset_2(void)
+{
+	uint32_t reg;
+	uint32_t mask, value;
+
+	reg = (msm_camera_io_r_mb(mdcbase)) & CAMIF_CFG_RMSK;
+	mask = CAM_PAD_REG_SW_RESET_BMSK;
+	value = 1 << CAM_PAD_REG_SW_RESET_SHFT;
+	msm_camera_io_w_mb((reg & (~mask)) | (value & mask), mdcbase);
+	usleep_range(10000, 11000);
+
+	reg = (msm_camera_io_r_mb(mdcbase)) & CAMIF_CFG_RMSK;
+	mask = CAM_PAD_REG_SW_RESET_BMSK;
+	value = 0 << CAM_PAD_REG_SW_RESET_SHFT;
+	msm_camera_io_w_mb((reg & (~mask)) | (value & mask), mdcbase);
+	usleep_range(10000, 11000);
+}
+
+void msm_camio_clk_sel(enum msm_camio_clk_src_type srctype)
+{
+	struct clk *clk = NULL;
+
+	clk = camio_vfe_clk;
+
+	if (clk != NULL) {
+		switch (srctype) {
+		case MSM_CAMIO_CLK_SRC_INTERNAL:
+			clk_set_flags(clk, 0x00000100 << 1);
+			break;
+
+		case MSM_CAMIO_CLK_SRC_EXTERNAL:
+			clk_set_flags(clk, 0x00000100);
+			break;
+
+		default:
+			break;
+		}
+	}
+}
+
+void msm_camio_clk_axi_rate_set(int rate)
+{
+	struct clk *clk = camio_vfe_axi_clk;
+	/* todo: check return */
+	clk_set_rate(clk, rate);
+}
+
+int msm_camio_probe_on(struct platform_device *pdev)
+{
+	struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+	struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+
+	camdev->camera_gpio_on();
+	return msm_camio_clk_enable(CAMIO_VFE_MDC_CLK);
+}
+
+int msm_camio_probe_off(struct platform_device *pdev)
+{
+	struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+	struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+
+	camdev->camera_gpio_off();
+	return msm_camio_clk_disable(CAMIO_VFE_MDC_CLK);
+}
diff --git a/drivers/media/video/msm/io/msm_io_7x27a.c b/drivers/media/video/msm/io/msm_io_7x27a.c
new file mode 100644
index 0000000..4d89c06
--- /dev/null
+++ b/drivers/media/video/msm/io/msm_io_7x27a.c
@@ -0,0 +1,595 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/pm_qos.h>
+#include <mach/board.h>
+#include <mach/camera.h>
+#include <mach/camera.h>
+#include <mach/clk.h>
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
+
+
+/* MIPI	CSI controller registers */
+#define	MIPI_PHY_CONTROL		0x00000000
+#define	MIPI_PROTOCOL_CONTROL		0x00000004
+#define	MIPI_INTERRUPT_STATUS		0x00000008
+#define	MIPI_INTERRUPT_MASK		0x0000000C
+#define	MIPI_CAMERA_CNTL		0x00000024
+#define	MIPI_CALIBRATION_CONTROL	0x00000018
+#define	MIPI_PHY_D0_CONTROL2		0x00000038
+#define	MIPI_PHY_D1_CONTROL2		0x0000003C
+#define	MIPI_PHY_D2_CONTROL2		0x00000040
+#define	MIPI_PHY_D3_CONTROL2		0x00000044
+#define	MIPI_PHY_CL_CONTROL		0x00000048
+#define	MIPI_PHY_D0_CONTROL		0x00000034
+#define	MIPI_PHY_D1_CONTROL		0x00000020
+#define	MIPI_PHY_D2_CONTROL		0x0000002C
+#define	MIPI_PHY_D3_CONTROL		0x00000030
+#define	MIPI_PWR_CNTL			0x00000054
+
+/*
+ * MIPI_PROTOCOL_CONTROL register bits to enable/disable the features of
+ * CSI Rx Block
+ */
+
+/* DPCM scheme */
+#define	MIPI_PROTOCOL_CONTROL_DPCM_SCHEME_SHFT			0x1e
+/* SW_RST to issue a SW reset to the CSI core */
+#define	MIPI_PROTOCOL_CONTROL_SW_RST_BMSK			0x8000000
+/* To Capture Long packet Header Info in MIPI_PROTOCOL_STATUS register */
+#define	MIPI_PROTOCOL_CONTROL_LONG_PACKET_HEADER_CAPTURE_BMSK	0x200000
+/* Data format for unpacking purpose */
+#define	MIPI_PROTOCOL_CONTROL_DATA_FORMAT_SHFT			0x13
+/* Enable decoding of payload based on data type filed of packet hdr */
+#define	MIPI_PROTOCOL_CONTROL_DECODE_ID_BMSK			0x00000
+/* Enable error correction on packet headers */
+#define	MIPI_PROTOCOL_CONTROL_ECC_EN_BMSK			0x20000
+
+/*
+ * MIPI_CALIBRATION_CONTROL register contains control info for
+ * calibration impledence controller
+*/
+
+/* Enable bit for calibration pad */
+#define	MIPI_CALIBRATION_CONTROL_SWCAL_CAL_EN_SHFT		0x16
+/* With SWCAL_STRENGTH_OVERRIDE_EN, SW_CAL_EN and MANUAL_OVERRIDE_EN
+ * the hardware calibration circuitry associated with CAL_SW_HW_MODE
+ * is bypassed
+*/
+#define	MIPI_CALIBRATION_CONTROL_SWCAL_STRENGTH_OVERRIDE_EN_SHFT	0x15
+/* To indicate the Calibration process is in the control of HW/SW */
+#define	MIPI_CALIBRATION_CONTROL_CAL_SW_HW_MODE_SHFT		0x14
+/* When this is set the strength value of the data and clk lane impedence
+ * termination is updated with MANUAL_STRENGTH settings and calibration
+ * sensing logic is idle.
+*/
+#define	MIPI_CALIBRATION_CONTROL_MANUAL_OVERRIDE_EN_SHFT	0x7
+
+/* Data lane0 control */
+/* T-hs Settle count value  for Rx */
+#define	MIPI_PHY_D0_CONTROL2_SETTLE_COUNT_SHFT			0x18
+/* Rx termination control */
+#define	MIPI_PHY_D0_CONTROL2_HS_TERM_IMP_SHFT			0x10
+/* LP Rx enable */
+#define	MIPI_PHY_D0_CONTROL2_LP_REC_EN_SHFT			0x4
+/*
+ * Enable for error in sync sequence
+ * 1 - one bit error in sync seq
+ * 0 - requires all 8 bit correct seq
+*/
+#define	MIPI_PHY_D0_CONTROL2_ERR_SOT_HS_EN_SHFT			0x3
+
+/* Comments are same as D0 */
+#define	MIPI_PHY_D1_CONTROL2_SETTLE_COUNT_SHFT			0x18
+#define	MIPI_PHY_D1_CONTROL2_HS_TERM_IMP_SHFT			0x10
+#define	MIPI_PHY_D1_CONTROL2_LP_REC_EN_SHFT			0x4
+#define	MIPI_PHY_D1_CONTROL2_ERR_SOT_HS_EN_SHFT			0x3
+
+/* Comments are same as D0 */
+#define	MIPI_PHY_D2_CONTROL2_SETTLE_COUNT_SHFT			0x18
+#define	MIPI_PHY_D2_CONTROL2_HS_TERM_IMP_SHFT			0x10
+#define	MIPI_PHY_D2_CONTROL2_LP_REC_EN_SHFT			0x4
+#define	MIPI_PHY_D2_CONTROL2_ERR_SOT_HS_EN_SHFT			0x3
+
+/* Comments are same as D0 */
+#define	MIPI_PHY_D3_CONTROL2_SETTLE_COUNT_SHFT			0x18
+#define	MIPI_PHY_D3_CONTROL2_HS_TERM_IMP_SHFT			0x10
+#define	MIPI_PHY_D3_CONTROL2_LP_REC_EN_SHFT			0x4
+#define	MIPI_PHY_D3_CONTROL2_ERR_SOT_HS_EN_SHFT			0x3
+
+/* PHY_CL_CTRL programs the parameters of clk lane of CSIRXPHY */
+/* HS Rx termination control */
+#define	MIPI_PHY_CL_CONTROL_HS_TERM_IMP_SHFT			0x18
+/* Start signal for T-hs delay */
+#define	MIPI_PHY_CL_CONTROL_LP_REC_EN_SHFT			0x2
+
+/* PHY DATA lane 0 control */
+/*
+ * HS RX equalizer strength control
+ * 00 - 0db 01 - 3db 10 - 5db 11 - 7db
+*/
+#define	MIPI_PHY_D0_CONTROL_HS_REC_EQ_SHFT			0x1c
+
+/* PHY DATA lane 1 control */
+/* Shutdown signal for MIPI clk phy line */
+#define	MIPI_PHY_D1_CONTROL_MIPI_CLK_PHY_SHUTDOWNB_SHFT		0x9
+/* Shutdown signal for MIPI data phy line */
+#define	MIPI_PHY_D1_CONTROL_MIPI_DATA_PHY_SHUTDOWNB_SHFT	0x8
+
+#define MSM_AXI_QOS_PREVIEW 200000
+#define MSM_AXI_QOS_SNAPSHOT 200000
+#define MSM_AXI_QOS_RECORDING 200000
+
+#define MIPI_PWR_CNTL_ENA	0x07
+#define MIPI_PWR_CNTL_DIS	0x0
+
+static struct clk *camio_cam_clk;
+static struct clk *camio_vfe_clk;
+static struct clk *camio_csi_src_clk;
+static struct clk *camio_csi0_vfe_clk;
+static struct clk *camio_csi1_vfe_clk;
+static struct clk *camio_csi0_clk;
+static struct clk *camio_csi1_clk;
+static struct clk *camio_csi0_pclk;
+static struct clk *camio_csi1_pclk;
+
+static struct msm_camera_io_ext camio_ext;
+static struct msm_camera_io_clk camio_clk;
+static struct platform_device *camio_dev;
+void __iomem *csibase;
+void __iomem *appbase;
+
+
+int msm_camio_vfe_clk_rate_set(int rate)
+{
+	int rc = 0;
+	struct clk *clk = camio_vfe_clk;
+	if (rate > clk_get_rate(clk))
+		rc = clk_set_rate(clk, rate);
+	return rc;
+}
+
+int msm_camio_clk_enable(enum msm_camio_clk_type clktype)
+{
+	int rc = 0;
+	struct clk *clk = NULL;
+
+	switch (clktype) {
+	case CAMIO_CAM_MCLK_CLK:
+		clk = clk_get(NULL, "cam_m_clk");
+		camio_cam_clk = clk;
+		msm_camio_clk_rate_set_2(clk, camio_clk.mclk_clk_rate);
+		break;
+	case CAMIO_VFE_CLK:
+		clk = clk_get(NULL, "vfe_clk");
+		camio_vfe_clk = clk;
+		msm_camio_clk_rate_set_2(clk, camio_clk.vfe_clk_rate);
+		break;
+	case CAMIO_CSI0_VFE_CLK:
+		clk = clk_get(&camio_dev->dev, "csi_vfe_clk");
+		camio_csi0_vfe_clk = clk;
+		break;
+	case CAMIO_CSI1_VFE_CLK:
+		clk = clk_get(NULL, "csi_vfe_clk");
+		camio_csi1_vfe_clk = clk;
+		break;
+	case CAMIO_CSI_SRC_CLK:
+		clk = clk_get(NULL, "csi_src_clk");
+		camio_csi_src_clk = clk;
+		break;
+	case CAMIO_CSI0_CLK:
+		clk = clk_get(&camio_dev->dev, "csi_clk");
+		camio_csi0_clk = clk;
+		msm_camio_clk_rate_set_2(clk, 400000000);
+		break;
+	case CAMIO_CSI1_CLK:
+		clk = clk_get(NULL, "csi_clk");
+		camio_csi1_clk = clk;
+		break;
+	case CAMIO_CSI0_PCLK:
+		clk = clk_get(&camio_dev->dev, "csi_pclk");
+		camio_csi0_pclk = clk;
+		break;
+	case CAMIO_CSI1_PCLK:
+		clk = clk_get(NULL, "csi_pclk");
+		camio_csi1_pclk = clk;
+		break;
+	default:
+		break;
+	}
+
+	if (!IS_ERR(clk))
+		clk_enable(clk);
+	else
+		rc = -1;
+	return rc;
+}
+
+int msm_camio_clk_disable(enum msm_camio_clk_type clktype)
+{
+	int rc = 0;
+	struct clk *clk = NULL;
+
+	switch (clktype) {
+	case CAMIO_CAM_MCLK_CLK:
+		clk = camio_cam_clk;
+		break;
+	case CAMIO_VFE_CLK:
+		clk = camio_vfe_clk;
+		break;
+	case CAMIO_CSI_SRC_CLK:
+		clk = camio_csi_src_clk;
+		break;
+	case CAMIO_CSI0_VFE_CLK:
+		clk = camio_csi0_vfe_clk;
+		break;
+	case CAMIO_CSI1_VFE_CLK:
+		clk = camio_csi1_vfe_clk;
+		break;
+	case CAMIO_CSI0_CLK:
+		clk = camio_csi0_clk;
+		break;
+	case CAMIO_CSI1_CLK:
+		clk = camio_csi1_clk;
+		break;
+	case CAMIO_CSI0_PCLK:
+		clk = camio_csi0_pclk;
+		break;
+	case CAMIO_CSI1_PCLK:
+		clk = camio_csi1_pclk;
+		break;
+	default:
+		break;
+	}
+
+	if (!IS_ERR(clk)) {
+		clk_disable(clk);
+		clk_put(clk);
+	} else
+		rc = -1;
+	return rc;
+}
+
+void msm_camio_clk_rate_set(int rate)
+{
+	struct clk *clk = camio_cam_clk;
+	clk_set_rate(clk, rate);
+}
+
+void msm_camio_clk_rate_set_2(struct clk *clk, int rate)
+{
+	clk_set_rate(clk, rate);
+}
+
+static irqreturn_t msm_io_csi_irq(int irq_num, void *data)
+{
+	uint32_t irq;
+
+	irq = msm_camera_io_r(csibase + MIPI_INTERRUPT_STATUS);
+	CDBG("%s MIPI_INTERRUPT_STATUS = 0x%x\n", __func__, irq);
+	msm_camera_io_w(irq, csibase + MIPI_INTERRUPT_STATUS);
+
+	/* TODO: Needs to send this info to upper layers */
+	if ((irq >> 19) & 0x1)
+		pr_info("Unsupported packet format is received\n");
+	return IRQ_HANDLED;
+}
+
+int msm_camio_enable(struct platform_device *pdev)
+{
+	int rc = 0;
+	const struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+	struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+	uint32_t val;
+
+	camio_dev = pdev;
+	camio_ext = camdev->ioext;
+	camio_clk = camdev->ioclk;
+
+	msm_camio_clk_enable(CAMIO_VFE_CLK);
+	msm_camio_clk_enable(CAMIO_CSI0_VFE_CLK);
+	msm_camio_clk_enable(CAMIO_CSI1_VFE_CLK);
+	msm_camio_clk_enable(CAMIO_CSI0_CLK);
+	msm_camio_clk_enable(CAMIO_CSI1_CLK);
+	msm_camio_clk_enable(CAMIO_CSI0_PCLK);
+	msm_camio_clk_enable(CAMIO_CSI1_PCLK);
+
+	csibase = ioremap(camio_ext.csiphy, camio_ext.csisz);
+	if (!csibase) {
+		rc = -ENOMEM;
+		goto csi_busy;
+	}
+	rc = request_irq(camio_ext.csiirq, msm_io_csi_irq,
+				IRQF_TRIGGER_RISING, "csi", 0);
+	if (rc < 0)
+		goto csi_irq_fail;
+
+	msleep(20);
+	val = (20 <<
+		MIPI_PHY_D0_CONTROL2_SETTLE_COUNT_SHFT) |
+		(0x0F << MIPI_PHY_D0_CONTROL2_HS_TERM_IMP_SHFT) |
+		(0x0 << MIPI_PHY_D0_CONTROL2_LP_REC_EN_SHFT) |
+		(0x1 << MIPI_PHY_D0_CONTROL2_ERR_SOT_HS_EN_SHFT);
+	CDBG("%s MIPI_PHY_D0_CONTROL2 val=0x%x\n", __func__, val);
+	msm_camera_io_w(val, csibase + MIPI_PHY_D0_CONTROL2);
+	msm_camera_io_w(val, csibase + MIPI_PHY_D1_CONTROL2);
+	msm_camera_io_w(val, csibase + MIPI_PHY_D2_CONTROL2);
+	msm_camera_io_w(val, csibase + MIPI_PHY_D3_CONTROL2);
+
+	val = (0x0F << MIPI_PHY_CL_CONTROL_HS_TERM_IMP_SHFT) |
+		(0x0 << MIPI_PHY_CL_CONTROL_LP_REC_EN_SHFT);
+	CDBG("%s MIPI_PHY_CL_CONTROL val=0x%x\n", __func__, val);
+	msm_camera_io_w(val, csibase + MIPI_PHY_CL_CONTROL);
+
+	appbase = ioremap(camio_ext.appphy,
+		camio_ext.appsz);
+	if (!appbase) {
+		rc = -ENOMEM;
+		goto csi_irq_fail;
+	}
+	return 0;
+
+csi_irq_fail:
+	iounmap(csibase);
+csi_busy:
+	msm_camio_clk_disable(CAMIO_CAM_MCLK_CLK);
+	msm_camio_clk_disable(CAMIO_VFE_CLK);
+	msm_camio_clk_disable(CAMIO_CSI0_VFE_CLK);
+	msm_camio_clk_disable(CAMIO_CSI1_VFE_CLK);
+	msm_camio_clk_disable(CAMIO_CSI0_CLK);
+	msm_camio_clk_disable(CAMIO_CSI1_CLK);
+	msm_camio_clk_disable(CAMIO_CSI0_PCLK);
+	msm_camio_clk_disable(CAMIO_CSI1_PCLK);
+	camdev->camera_gpio_off();
+	return rc;
+}
+
+void msm_camio_disable(struct platform_device *pdev)
+{
+	uint32_t val;
+
+	val = (20 <<
+		MIPI_PHY_D0_CONTROL2_SETTLE_COUNT_SHFT) |
+		(0x0F << MIPI_PHY_D0_CONTROL2_HS_TERM_IMP_SHFT) |
+		(0x0 << MIPI_PHY_D0_CONTROL2_LP_REC_EN_SHFT) |
+		(0x1 << MIPI_PHY_D0_CONTROL2_ERR_SOT_HS_EN_SHFT);
+	CDBG("%s MIPI_PHY_D0_CONTROL2 val=0x%x\n", __func__, val);
+	msm_camera_io_w(val, csibase + MIPI_PHY_D0_CONTROL2);
+	msm_camera_io_w(val, csibase + MIPI_PHY_D1_CONTROL2);
+	msm_camera_io_w(val, csibase + MIPI_PHY_D2_CONTROL2);
+	msm_camera_io_w(val, csibase + MIPI_PHY_D3_CONTROL2);
+
+	val = (0x0F << MIPI_PHY_CL_CONTROL_HS_TERM_IMP_SHFT) |
+		(0x0 << MIPI_PHY_CL_CONTROL_LP_REC_EN_SHFT);
+	CDBG("%s MIPI_PHY_CL_CONTROL val=0x%x\n", __func__, val);
+	msm_camera_io_w(val, csibase + MIPI_PHY_CL_CONTROL);
+	msleep(20);
+
+	free_irq(camio_ext.csiirq, 0);
+	iounmap(csibase);
+	iounmap(appbase);
+	CDBG("disable clocks\n");
+
+	msm_camio_clk_disable(CAMIO_VFE_CLK);
+	msm_camio_clk_disable(CAMIO_CSI0_CLK);
+	msm_camio_clk_disable(CAMIO_CSI1_CLK);
+	msm_camio_clk_disable(CAMIO_CSI0_VFE_CLK);
+	msm_camio_clk_disable(CAMIO_CSI1_VFE_CLK);
+	msm_camio_clk_disable(CAMIO_CSI0_PCLK);
+	msm_camio_clk_disable(CAMIO_CSI1_PCLK);
+}
+
+int msm_camio_sensor_clk_on(struct platform_device *pdev)
+{
+	int rc = 0;
+	const struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+	struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+	camio_dev = pdev;
+	camio_ext = camdev->ioext;
+	camio_clk = camdev->ioclk;
+
+	rc = camdev->camera_gpio_on();
+	if (rc < 0)
+		return rc;
+	return msm_camio_clk_enable(CAMIO_CAM_MCLK_CLK);
+}
+
+int msm_camio_sensor_clk_off(struct platform_device *pdev)
+{
+	const struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+	struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+	camdev->camera_gpio_off();
+	return msm_camio_clk_disable(CAMIO_CAM_MCLK_CLK);
+
+}
+
+void msm_camio_vfe_blk_reset(void)
+{
+	uint32_t val;
+
+	/* do apps reset */
+	val = msm_camera_io_r(appbase + 0x00000210);
+	val |= 0x1;
+	msm_camera_io_w(val, appbase + 0x00000210);
+	usleep_range(10000, 11000);
+
+	val = msm_camera_io_r(appbase + 0x00000210);
+	val &= ~0x1;
+	msm_camera_io_w(val, appbase + 0x00000210);
+	usleep_range(10000, 11000);
+
+	/* do axi reset */
+	val = msm_camera_io_r(appbase + 0x00000208);
+	val |= 0x1;
+	msm_camera_io_w(val, appbase + 0x00000208);
+	usleep_range(10000, 11000);
+
+	val = msm_camera_io_r(appbase + 0x00000208);
+	val &= ~0x1;
+	msm_camera_io_w(val, appbase + 0x00000208);
+	mb();
+	usleep_range(10000, 11000);
+	return;
+}
+
+int msm_camio_probe_on(struct platform_device *pdev)
+{
+	int rc = 0;
+	const struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+	struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+	camio_dev = pdev;
+	camio_ext = camdev->ioext;
+	camio_clk = camdev->ioclk;
+
+	msm_camio_clk_enable(CAMIO_CSI0_PCLK);
+	msm_camio_clk_enable(CAMIO_CSI1_PCLK);
+
+	rc = camdev->camera_gpio_on();
+	if (rc < 0)
+		return rc;
+	return msm_camio_clk_enable(CAMIO_CAM_MCLK_CLK);
+}
+
+int msm_camio_probe_off(struct platform_device *pdev)
+{
+	const struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+	struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+	camdev->camera_gpio_off();
+
+	csibase = ioremap(camdev->ioext.csiphy, camdev->ioext.csisz);
+	if (!csibase) {
+		pr_err("ioremap failed for CSIBASE\n");
+		goto ioremap_fail;
+	}
+	msm_camera_io_w(MIPI_PWR_CNTL_DIS, csibase + MIPI_PWR_CNTL);
+	iounmap(csibase);
+ioremap_fail:
+	msm_camio_clk_disable(CAMIO_CSI0_PCLK);
+	msm_camio_clk_disable(CAMIO_CSI1_PCLK);
+	return msm_camio_clk_disable(CAMIO_CAM_MCLK_CLK);
+}
+
+int msm_camio_csi_config(struct msm_camera_csi_params *csi_params)
+{
+	int rc = 0;
+	uint32_t val = 0;
+
+	CDBG("msm_camio_csi_config\n");
+
+	/* Enable error correction for DATA lane. Applies to all data lanes */
+	msm_camera_io_w(0x4, csibase + MIPI_PHY_CONTROL);
+
+	msm_camera_io_w(MIPI_PROTOCOL_CONTROL_SW_RST_BMSK,
+		csibase + MIPI_PROTOCOL_CONTROL);
+
+	val = MIPI_PROTOCOL_CONTROL_LONG_PACKET_HEADER_CAPTURE_BMSK |
+		MIPI_PROTOCOL_CONTROL_DECODE_ID_BMSK |
+		MIPI_PROTOCOL_CONTROL_ECC_EN_BMSK;
+	val |= (uint32_t)(csi_params->data_format) <<
+		MIPI_PROTOCOL_CONTROL_DATA_FORMAT_SHFT;
+	val |= csi_params->dpcm_scheme <<
+		MIPI_PROTOCOL_CONTROL_DPCM_SCHEME_SHFT;
+	CDBG("%s MIPI_PROTOCOL_CONTROL val=0x%x\n", __func__, val);
+	msm_camera_io_w(val, csibase + MIPI_PROTOCOL_CONTROL);
+
+	val = (0x1 << MIPI_CALIBRATION_CONTROL_SWCAL_CAL_EN_SHFT) |
+		(0x1 <<
+		MIPI_CALIBRATION_CONTROL_SWCAL_STRENGTH_OVERRIDE_EN_SHFT) |
+		(0x1 << MIPI_CALIBRATION_CONTROL_CAL_SW_HW_MODE_SHFT) |
+		(0x1 << MIPI_CALIBRATION_CONTROL_MANUAL_OVERRIDE_EN_SHFT);
+	CDBG("%s MIPI_CALIBRATION_CONTROL val=0x%x\n", __func__, val);
+	msm_camera_io_w(val, csibase + MIPI_CALIBRATION_CONTROL);
+
+	val = (csi_params->settle_cnt <<
+		MIPI_PHY_D0_CONTROL2_SETTLE_COUNT_SHFT) |
+		(0x0F << MIPI_PHY_D0_CONTROL2_HS_TERM_IMP_SHFT) |
+		(0x1 << MIPI_PHY_D0_CONTROL2_LP_REC_EN_SHFT) |
+		(0x1 << MIPI_PHY_D0_CONTROL2_ERR_SOT_HS_EN_SHFT);
+	CDBG("%s MIPI_PHY_D0_CONTROL2 val=0x%x\n", __func__, val);
+	msm_camera_io_w(val, csibase + MIPI_PHY_D0_CONTROL2);
+	msm_camera_io_w(val, csibase + MIPI_PHY_D1_CONTROL2);
+	msm_camera_io_w(val, csibase + MIPI_PHY_D2_CONTROL2);
+	msm_camera_io_w(val, csibase + MIPI_PHY_D3_CONTROL2);
+
+
+	val = (0x0F << MIPI_PHY_CL_CONTROL_HS_TERM_IMP_SHFT) |
+		(0x1 << MIPI_PHY_CL_CONTROL_LP_REC_EN_SHFT);
+	CDBG("%s MIPI_PHY_CL_CONTROL val=0x%x\n", __func__, val);
+	msm_camera_io_w(val, csibase + MIPI_PHY_CL_CONTROL);
+
+	val = 0 << MIPI_PHY_D0_CONTROL_HS_REC_EQ_SHFT;
+	msm_camera_io_w(val, csibase + MIPI_PHY_D0_CONTROL);
+
+	val = (0x1 << MIPI_PHY_D1_CONTROL_MIPI_CLK_PHY_SHUTDOWNB_SHFT) |
+		(0x1 << MIPI_PHY_D1_CONTROL_MIPI_DATA_PHY_SHUTDOWNB_SHFT);
+	CDBG("%s MIPI_PHY_D1_CONTROL val=0x%x\n", __func__, val);
+	msm_camera_io_w(val, csibase + MIPI_PHY_D1_CONTROL);
+
+	msm_camera_io_w(0x00000000, csibase + MIPI_PHY_D2_CONTROL);
+	msm_camera_io_w(0x00000000, csibase + MIPI_PHY_D3_CONTROL);
+
+	/* program number of lanes and lane mapping */
+	switch (csi_params->lane_cnt) {
+	case 1:
+		msm_camera_io_w(csi_params->lane_assign << 8 | 0x4,
+			csibase + MIPI_CAMERA_CNTL);
+		break;
+	case 2:
+		msm_camera_io_w(csi_params->lane_assign << 8 | 0x5,
+			csibase + MIPI_CAMERA_CNTL);
+		break;
+	case 3:
+		msm_camera_io_w(csi_params->lane_assign << 8 | 0x6,
+			csibase + MIPI_CAMERA_CNTL);
+		break;
+	case 4:
+		msm_camera_io_w(csi_params->lane_assign << 8 | 0x7,
+			csibase + MIPI_CAMERA_CNTL);
+		break;
+	}
+
+	msm_camera_io_w(0xFFFFF3FF, csibase + MIPI_INTERRUPT_MASK);
+	/*clear IRQ bits - write 1 clears the status*/
+	msm_camera_io_w(0xFFFFF3FF, csibase + MIPI_INTERRUPT_STATUS);
+
+	return rc;
+}
+
+void msm_camio_set_perf_lvl(enum msm_bus_perf_setting perf_setting)
+{
+	switch (perf_setting) {
+	case S_INIT:
+		add_axi_qos();
+		break;
+	case S_PREVIEW:
+		update_axi_qos(MSM_AXI_QOS_PREVIEW);
+		break;
+	case S_VIDEO:
+		update_axi_qos(MSM_AXI_QOS_RECORDING);
+		break;
+	case S_CAPTURE:
+		update_axi_qos(MSM_AXI_QOS_SNAPSHOT);
+		break;
+	case S_DEFAULT:
+		update_axi_qos(PM_QOS_DEFAULT_VALUE);
+		break;
+	case S_EXIT:
+		release_axi_qos();
+		break;
+	default:
+		CDBG("%s: INVALID CASE\n", __func__);
+	}
+}
diff --git a/drivers/media/video/msm/io/msm_io_7x27a_v4l2.c b/drivers/media/video/msm/io/msm_io_7x27a_v4l2.c
new file mode 100644
index 0000000..7e80145
--- /dev/null
+++ b/drivers/media/video/msm/io/msm_io_7x27a_v4l2.c
@@ -0,0 +1,219 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/pm_qos.h>
+#include <linux/module.h>
+#include <mach/board.h>
+#include <mach/camera.h>
+#include <mach/camera.h>
+#include <mach/clk.h>
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
+
+
+#define MSM_AXI_QOS_PREVIEW 200000
+#define MSM_AXI_QOS_SNAPSHOT 200000
+#define MSM_AXI_QOS_RECORDING 200000
+
+static struct clk *camio_cam_clk;
+static struct resource *clk_ctrl_mem;
+static struct msm_camera_io_clk camio_clk;
+static int apps_reset;
+void __iomem *appbase;
+
+void msm_camio_clk_rate_set_2(struct clk *clk, int rate)
+{
+	clk_set_rate(clk, rate);
+}
+int msm_camio_clk_enable(enum msm_camio_clk_type clktype)
+{
+	int rc = 0;
+	struct clk *clk = NULL;
+
+	switch (clktype) {
+	case CAMIO_CAM_MCLK_CLK:
+		clk = clk_get(NULL, "cam_m_clk");
+		camio_cam_clk = clk;
+		msm_camio_clk_rate_set_2(clk, camio_clk.mclk_clk_rate);
+		break;
+	default:
+		break;
+	}
+
+	if (!IS_ERR(clk))
+		clk_enable(clk);
+	else
+		rc = -1;
+	return rc;
+}
+
+int msm_camio_clk_disable(enum msm_camio_clk_type clktype)
+{
+	int rc = 0;
+	struct clk *clk = NULL;
+
+	switch (clktype) {
+	case CAMIO_CAM_MCLK_CLK:
+		clk = camio_cam_clk;
+		break;
+	default:
+		break;
+	}
+
+	if (!IS_ERR(clk)) {
+		clk_disable(clk);
+		clk_put(clk);
+	} else
+		rc = -1;
+	return rc;
+}
+
+void msm_camio_clk_rate_set(int rate)
+{
+	struct clk *clk = camio_cam_clk;
+	clk_set_rate(clk, rate);
+}
+
+void msm_camio_vfe_blk_reset_2(void)
+{
+	uint32_t val;
+
+	/* do apps reset */
+	val = readl_relaxed(appbase + 0x00000210);
+	val |= 0x1;
+	writel_relaxed(val, appbase + 0x00000210);
+	usleep_range(10000, 11000);
+
+	val = readl_relaxed(appbase + 0x00000210);
+	val &= ~0x1;
+	writel_relaxed(val, appbase + 0x00000210);
+	usleep_range(10000, 11000);
+
+	/* do axi reset */
+	val = readl_relaxed(appbase + 0x00000208);
+	val |= 0x1;
+	writel_relaxed(val, appbase + 0x00000208);
+	usleep_range(10000, 11000);
+
+	val = readl_relaxed(appbase + 0x00000208);
+	val &= ~0x1;
+	writel_relaxed(val, appbase + 0x00000208);
+	mb();
+	usleep_range(10000, 11000);
+}
+
+void msm_camio_vfe_blk_reset_3(void)
+{
+	uint32_t val;
+
+	if (!apps_reset)
+		return;
+
+	/* do apps reset */
+	val = readl_relaxed(appbase + 0x00000210);
+	val |= 0x10A0000;
+	writel_relaxed(val, appbase + 0x00000210);
+	usleep_range(10000, 11000);
+
+	val = readl_relaxed(appbase + 0x00000210);
+	val &= ~(0x10A0000);
+	writel_relaxed(val, appbase + 0x00000210);
+	usleep_range(10000, 11000);
+	mb();
+}
+
+void msm_camio_set_perf_lvl(enum msm_bus_perf_setting perf_setting)
+{
+	switch (perf_setting) {
+	case S_INIT:
+		add_axi_qos();
+		break;
+	case S_PREVIEW:
+		update_axi_qos(MSM_AXI_QOS_PREVIEW);
+		break;
+	case S_VIDEO:
+		update_axi_qos(MSM_AXI_QOS_RECORDING);
+		break;
+	case S_CAPTURE:
+		update_axi_qos(MSM_AXI_QOS_SNAPSHOT);
+		break;
+	case S_DEFAULT:
+		update_axi_qos(PM_QOS_DEFAULT_VALUE);
+		break;
+	case S_EXIT:
+		release_axi_qos();
+		break;
+	default:
+		CDBG("%s: INVALID CASE\n", __func__);
+	}
+}
+
+static int __devinit clkctl_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+
+	apps_reset = *(int *)pdev->dev.platform_data;
+	clk_ctrl_mem = platform_get_resource_byname(pdev,
+					IORESOURCE_MEM, "clk_ctl");
+	if (!clk_ctrl_mem) {
+		pr_err("%s: no mem resource:3?\n", __func__);
+		return -ENODEV;
+	}
+
+	appbase = ioremap(clk_ctrl_mem->start,
+		resource_size(clk_ctrl_mem));
+	if (!appbase) {
+		pr_err("clkctl_probe: appbase:err\n");
+		rc = -ENOMEM;
+		goto ioremap_fail;
+	}
+	return 0;
+
+ioremap_fail:
+	msm_camio_clk_disable(CAMIO_CAM_MCLK_CLK);
+	return rc;
+}
+
+static int clkctl_remove(struct platform_device *pdev)
+{
+	if (clk_ctrl_mem)
+		iounmap(clk_ctrl_mem);
+
+	return 0;
+}
+
+static struct platform_driver clkctl_driver = {
+	.probe  = clkctl_probe,
+	.remove = clkctl_remove,
+	.driver = {
+		.name = "msm_clk_ctl",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init msm_clkctl_init_module(void)
+{
+	return platform_driver_register(&clkctl_driver);
+}
+
+static void __exit msm_clkctl_exit_module(void)
+{
+	platform_driver_unregister(&clkctl_driver);
+}
+
+module_init(msm_clkctl_init_module);
+module_exit(msm_clkctl_exit_module);
+MODULE_DESCRIPTION("CAM IO driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/io/msm_io_8960.c b/drivers/media/video/msm/io/msm_io_8960.c
new file mode 100644
index 0000000..f9c454a
--- /dev/null
+++ b/drivers/media/video/msm/io/msm_io_8960.c
@@ -0,0 +1,95 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mfd/pm8xxx/pm8921.h>
+#include <mach/gpio.h>
+#include <mach/gpiomux.h>
+#include <mach/board.h>
+#include <mach/camera.h>
+#include <mach/vreg.h>
+#include <mach/camera.h>
+#include <mach/clk.h>
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
+
+#define BUFF_SIZE_128 128
+
+void msm_camio_clk_rate_set_2(struct clk *clk, int rate)
+{
+	clk_set_rate(clk, rate);
+}
+
+void msm_camio_bus_scale_cfg(struct msm_bus_scale_pdata *cam_bus_scale_table,
+		enum msm_bus_perf_setting perf_setting)
+{
+	static uint32_t bus_perf_client;
+	int rc = 0;
+	switch (perf_setting) {
+	case S_INIT:
+		bus_perf_client =
+			msm_bus_scale_register_client(cam_bus_scale_table);
+		if (!bus_perf_client) {
+			CDBG("%s: Registration Failed!!!\n", __func__);
+			bus_perf_client = 0;
+			return;
+		}
+		CDBG("%s: S_INIT rc = %u\n", __func__, bus_perf_client);
+		break;
+	case S_EXIT:
+		if (bus_perf_client) {
+			CDBG("%s: S_EXIT\n", __func__);
+			msm_bus_scale_unregister_client(bus_perf_client);
+		} else
+			CDBG("%s: Bus Client NOT Registered!!!\n", __func__);
+		break;
+	case S_PREVIEW:
+		if (bus_perf_client) {
+			rc = msm_bus_scale_client_update_request(
+				bus_perf_client, 1);
+			CDBG("%s: S_PREVIEW rc = %d\n", __func__, rc);
+		} else
+			CDBG("%s: Bus Client NOT Registered!!!\n", __func__);
+		break;
+	case S_VIDEO:
+		if (bus_perf_client) {
+			rc = msm_bus_scale_client_update_request(
+				bus_perf_client, 2);
+			CDBG("%s: S_VIDEO rc = %d\n", __func__, rc);
+		} else
+			CDBG("%s: Bus Client NOT Registered!!!\n", __func__);
+		break;
+	case S_CAPTURE:
+		if (bus_perf_client) {
+			rc = msm_bus_scale_client_update_request(
+				bus_perf_client, 3);
+			CDBG("%s: S_CAPTURE rc = %d\n", __func__, rc);
+		} else
+			CDBG("%s: Bus Client NOT Registered!!!\n", __func__);
+		break;
+	case S_ZSL:
+		if (bus_perf_client) {
+			rc = msm_bus_scale_client_update_request(
+				bus_perf_client, 4);
+			CDBG("%s: S_ZSL rc = %d\n", __func__, rc);
+		} else
+			CDBG("%s: Bus Client NOT Registered!!!\n", __func__);
+		break;
+	case S_DEFAULT:
+		break;
+	default:
+		pr_warning("%s: INVALID CASE\n", __func__);
+	}
+}
diff --git a/drivers/media/video/msm/io/msm_io_8x60.c b/drivers/media/video/msm/io/msm_io_8x60.c
new file mode 100644
index 0000000..baa1245
--- /dev/null
+++ b/drivers/media/video/msm/io/msm_io_8x60.c
@@ -0,0 +1,820 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/regulator/consumer.h>
+#include <mach/gpio.h>
+#include <mach/board.h>
+#include <mach/camera.h>
+#include <mach/vreg.h>
+#include <mach/camera.h>
+#include <mach/clk.h>
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
+
+
+/* MIPI	CSI	controller registers */
+#define	MIPI_PHY_CONTROL			0x00000000
+#define	MIPI_PROTOCOL_CONTROL		0x00000004
+#define	MIPI_INTERRUPT_STATUS		0x00000008
+#define	MIPI_INTERRUPT_MASK			0x0000000C
+#define	MIPI_CAMERA_CNTL			0x00000024
+#define	MIPI_CALIBRATION_CONTROL	0x00000018
+#define	MIPI_PHY_D0_CONTROL2		0x00000038
+#define	MIPI_PHY_D1_CONTROL2		0x0000003C
+#define	MIPI_PHY_D2_CONTROL2		0x00000040
+#define	MIPI_PHY_D3_CONTROL2		0x00000044
+#define	MIPI_PHY_CL_CONTROL			0x00000048
+#define	MIPI_PHY_D0_CONTROL			0x00000034
+#define	MIPI_PHY_D1_CONTROL			0x00000020
+#define	MIPI_PHY_D2_CONTROL			0x0000002C
+#define	MIPI_PHY_D3_CONTROL			0x00000030
+#define	MIPI_PROTOCOL_CONTROL_SW_RST_BMSK			0x8000000
+#define	MIPI_PROTOCOL_CONTROL_LONG_PACKET_HEADER_CAPTURE_BMSK	0x200000
+#define	MIPI_PROTOCOL_CONTROL_DATA_FORMAT_BMSK			0x180000
+#define	MIPI_PROTOCOL_CONTROL_DECODE_ID_BMSK			0x40000
+#define	MIPI_PROTOCOL_CONTROL_ECC_EN_BMSK			0x20000
+#define	MIPI_CALIBRATION_CONTROL_SWCAL_CAL_EN_SHFT		0x16
+#define	MIPI_CALIBRATION_CONTROL_SWCAL_STRENGTH_OVERRIDE_EN_SHFT	0x15
+#define	MIPI_CALIBRATION_CONTROL_CAL_SW_HW_MODE_SHFT		0x14
+#define	MIPI_CALIBRATION_CONTROL_MANUAL_OVERRIDE_EN_SHFT	0x7
+#define	MIPI_PROTOCOL_CONTROL_DATA_FORMAT_SHFT			0x13
+#define	MIPI_PROTOCOL_CONTROL_DPCM_SCHEME_SHFT			0x1e
+#define	MIPI_PHY_D0_CONTROL2_SETTLE_COUNT_SHFT			0x18
+#define	MIPI_PHY_D0_CONTROL2_HS_TERM_IMP_SHFT			0x10
+#define	MIPI_PHY_D0_CONTROL2_LP_REC_EN_SHFT				0x4
+#define	MIPI_PHY_D0_CONTROL2_ERR_SOT_HS_EN_SHFT			0x3
+#define	MIPI_PHY_D1_CONTROL2_SETTLE_COUNT_SHFT			0x18
+#define	MIPI_PHY_D1_CONTROL2_HS_TERM_IMP_SHFT			0x10
+#define	MIPI_PHY_D1_CONTROL2_LP_REC_EN_SHFT				0x4
+#define	MIPI_PHY_D1_CONTROL2_ERR_SOT_HS_EN_SHFT			0x3
+#define	MIPI_PHY_D2_CONTROL2_SETTLE_COUNT_SHFT			0x18
+#define	MIPI_PHY_D2_CONTROL2_HS_TERM_IMP_SHFT			0x10
+#define	MIPI_PHY_D2_CONTROL2_LP_REC_EN_SHFT				0x4
+#define	MIPI_PHY_D2_CONTROL2_ERR_SOT_HS_EN_SHFT			0x3
+#define	MIPI_PHY_D3_CONTROL2_SETTLE_COUNT_SHFT			0x18
+#define	MIPI_PHY_D3_CONTROL2_HS_TERM_IMP_SHFT			0x10
+#define	MIPI_PHY_D3_CONTROL2_LP_REC_EN_SHFT				0x4
+#define	MIPI_PHY_D3_CONTROL2_ERR_SOT_HS_EN_SHFT			0x3
+#define	MIPI_PHY_CL_CONTROL_HS_TERM_IMP_SHFT			0x18
+#define	MIPI_PHY_CL_CONTROL_LP_REC_EN_SHFT				0x2
+#define	MIPI_PHY_D0_CONTROL_HS_REC_EQ_SHFT				0x1c
+#define	MIPI_PHY_D1_CONTROL_MIPI_CLK_PHY_SHUTDOWNB_SHFT		0x9
+#define	MIPI_PHY_D1_CONTROL_MIPI_DATA_PHY_SHUTDOWNB_SHFT	0x8
+#define	DBG_CSI	0
+
+static struct clk *camio_cam_clk;
+static struct clk *camio_vfe_clk;
+static struct clk *camio_csi_src_clk;
+static struct clk *camio_csi0_vfe_clk;
+static struct clk *camio_csi1_vfe_clk;
+static struct clk *camio_csi0_clk;
+static struct clk *camio_csi1_clk;
+static struct clk *camio_csi0_pclk;
+static struct clk *camio_csi1_pclk;
+static struct clk *camio_vfe_pclk;
+static struct clk *camio_vpe_clk;
+static struct clk *camio_vpe_pclk;
+static struct regulator *fs_vfe;
+static struct regulator *fs_vpe;
+static struct regulator *ldo15;
+static struct regulator *lvs0;
+static struct regulator *ldo25;
+
+static struct msm_camera_io_ext camio_ext;
+static struct msm_camera_io_clk camio_clk;
+static struct platform_device *camio_dev;
+static struct resource *csiio;
+void __iomem *csibase;
+static int vpe_clk_rate;
+struct msm_bus_scale_pdata *cam_bus_scale_table;
+
+static void msm_camera_vreg_enable(void)
+{
+	ldo15 = regulator_get(NULL, "8058_l15");
+	if (IS_ERR(ldo15)) {
+		pr_err("%s: VREG LDO15 get failed\n", __func__);
+		ldo15 = NULL;
+		return;
+	}
+	if (regulator_set_voltage(ldo15, 2850000, 2850000)) {
+		pr_err("%s: VREG LDO15 set voltage failed\n",  __func__);
+		goto ldo15_disable;
+	}
+	if (regulator_enable(ldo15)) {
+		pr_err("%s: VREG LDO15 enable failed\n", __func__);
+		goto ldo15_put;
+	}
+
+	lvs0 = regulator_get(NULL, "8058_lvs0");
+	if (IS_ERR(lvs0)) {
+		pr_err("%s: VREG LVS0 get failed\n", __func__);
+		lvs0 = NULL;
+		goto ldo15_disable;
+	}
+	if (regulator_enable(lvs0)) {
+		pr_err("%s: VREG LVS0 enable failed\n", __func__);
+		goto lvs0_put;
+	}
+
+	ldo25 = regulator_get(NULL, "8058_l25");
+	if (IS_ERR(ldo25)) {
+		pr_err("%s: VREG LDO25 get failed\n", __func__);
+		ldo25 = NULL;
+		goto lvs0_disable;
+	}
+	if (regulator_set_voltage(ldo25, 1200000, 1200000)) {
+		pr_err("%s: VREG LDO25 set voltage failed\n",  __func__);
+		goto ldo25_disable;
+	}
+	if (regulator_enable(ldo25)) {
+		pr_err("%s: VREG LDO25 enable failed\n", __func__);
+		goto ldo25_put;
+	}
+
+	fs_vfe = regulator_get(NULL, "fs_vfe");
+	if (IS_ERR(fs_vfe)) {
+		CDBG("%s: Regulator FS_VFE get failed %ld\n", __func__,
+			PTR_ERR(fs_vfe));
+		fs_vfe = NULL;
+	} else if (regulator_enable(fs_vfe)) {
+		CDBG("%s: Regulator FS_VFE enable failed\n", __func__);
+		regulator_put(fs_vfe);
+	}
+	return;
+
+ldo25_disable:
+	regulator_disable(ldo25);
+ldo25_put:
+	regulator_put(ldo25);
+lvs0_disable:
+	regulator_disable(lvs0);
+lvs0_put:
+	regulator_put(lvs0);
+ldo15_disable:
+	regulator_disable(ldo15);
+ldo15_put:
+	regulator_put(ldo15);
+}
+
+static void msm_camera_vreg_disable(void)
+{
+	if (ldo15) {
+		regulator_disable(ldo15);
+		regulator_put(ldo15);
+	}
+
+	if (lvs0) {
+		regulator_disable(lvs0);
+		regulator_put(lvs0);
+	}
+
+	if (ldo25) {
+		regulator_disable(ldo25);
+		regulator_put(ldo25);
+	}
+
+	if (fs_vfe) {
+		regulator_disable(fs_vfe);
+		regulator_put(fs_vfe);
+	}
+}
+
+int msm_camio_clk_enable(enum msm_camio_clk_type clktype)
+{
+	int rc = 0;
+	struct clk *clk = NULL;
+
+	switch (clktype) {
+	case CAMIO_CAM_MCLK_CLK:
+		camio_cam_clk =
+		clk = clk_get(NULL, "cam_clk");
+		msm_camio_clk_rate_set_2(clk, camio_clk.mclk_clk_rate);
+		break;
+
+	case CAMIO_VFE_CLK:
+		camio_vfe_clk =
+		clk = clk_get(NULL, "vfe_clk");
+		msm_camio_clk_rate_set_2(clk, camio_clk.vfe_clk_rate);
+		break;
+
+	case CAMIO_CSI0_VFE_CLK:
+		camio_csi0_vfe_clk =
+		clk = clk_get(NULL, "csi_vfe_clk");
+		break;
+
+	case CAMIO_CSI1_VFE_CLK:
+		camio_csi1_vfe_clk =
+		clk = clk_get(&camio_dev->dev, "csi_vfe_clk");
+		break;
+
+	case CAMIO_CSI_SRC_CLK:
+		camio_csi_src_clk =
+		clk = clk_get(NULL, "csi_src_clk");
+		msm_camio_clk_rate_set_2(clk, 384000000);
+		break;
+
+	case CAMIO_CSI0_CLK:
+		camio_csi0_clk =
+		clk = clk_get(NULL, "csi_clk");
+		break;
+
+	case CAMIO_CSI1_CLK:
+		camio_csi1_clk =
+		clk = clk_get(&camio_dev->dev, "csi_clk");
+		break;
+
+	case CAMIO_VFE_PCLK:
+		camio_vfe_pclk =
+		clk = clk_get(NULL, "vfe_pclk");
+		break;
+
+	case CAMIO_CSI0_PCLK:
+		camio_csi0_pclk =
+		clk = clk_get(NULL, "csi_pclk");
+		break;
+
+	case CAMIO_CSI1_PCLK:
+		camio_csi1_pclk =
+		clk = clk_get(&camio_dev->dev, "csi_pclk");
+		break;
+
+	case CAMIO_VPE_CLK:
+		camio_vpe_clk =
+		clk = clk_get(NULL, "vpe_clk");
+		vpe_clk_rate = clk_round_rate(camio_vpe_clk, vpe_clk_rate);
+		clk_set_rate(camio_vpe_clk, vpe_clk_rate);
+		break;
+
+	case CAMIO_VPE_PCLK:
+		camio_vpe_pclk =
+		clk = clk_get(NULL, "vpe_pclk");
+		break;
+
+	default:
+		break;
+	}
+
+	if (!IS_ERR(clk))
+		clk_enable(clk);
+	else
+		rc = -1;
+	return rc;
+}
+
+int msm_camio_clk_disable(enum msm_camio_clk_type clktype)
+{
+	int rc = 0;
+	struct clk *clk = NULL;
+
+	switch (clktype) {
+	case CAMIO_CAM_MCLK_CLK:
+		clk = camio_cam_clk;
+		break;
+
+	case CAMIO_VFE_CLK:
+		clk = camio_vfe_clk;
+		break;
+
+	case CAMIO_CSI_SRC_CLK:
+		clk = camio_csi_src_clk;
+		break;
+
+	case CAMIO_CSI0_VFE_CLK:
+		clk = camio_csi0_vfe_clk;
+		break;
+
+	case CAMIO_CSI1_VFE_CLK:
+		clk = camio_csi1_vfe_clk;
+		break;
+
+	case CAMIO_CSI0_CLK:
+		clk = camio_csi0_clk;
+		break;
+
+	case CAMIO_CSI1_CLK:
+		clk = camio_csi1_clk;
+		break;
+
+	case CAMIO_VFE_PCLK:
+		clk = camio_vfe_pclk;
+		break;
+
+	case CAMIO_CSI0_PCLK:
+		clk = camio_csi0_pclk;
+		break;
+
+	case CAMIO_CSI1_PCLK:
+		clk = camio_csi1_pclk;
+		break;
+
+	case CAMIO_VPE_CLK:
+		clk = camio_vpe_clk;
+		break;
+
+	case CAMIO_VPE_PCLK:
+		clk = camio_vpe_pclk;
+		break;
+
+	default:
+		break;
+	}
+
+	if (!IS_ERR(clk)) {
+		clk_disable(clk);
+		clk_put(clk);
+	} else
+		rc = -1;
+	return rc;
+}
+
+int msm_camio_vfe_clk_rate_set(int rate)
+{
+	int rc = 0;
+	struct clk *clk = camio_vfe_clk;
+	if (rate > clk_get_rate(clk))
+		rc = clk_set_rate(clk, rate);
+	return rc;
+}
+
+void msm_camio_clk_rate_set(int rate)
+{
+	struct clk *clk = camio_cam_clk;
+	clk_set_rate(clk, rate);
+}
+
+void msm_camio_clk_rate_set_2(struct clk *clk, int rate)
+{
+	clk_set_rate(clk, rate);
+}
+
+static irqreturn_t msm_io_csi_irq(int irq_num, void *data)
+{
+	uint32_t irq = 0;
+	if (csibase != NULL)
+		irq = msm_camera_io_r(csibase + MIPI_INTERRUPT_STATUS);
+	CDBG("%s MIPI_INTERRUPT_STATUS = 0x%x\n", __func__, irq);
+	if (csibase != NULL)
+		msm_camera_io_w(irq, csibase + MIPI_INTERRUPT_STATUS);
+	return IRQ_HANDLED;
+}
+
+int msm_camio_vpe_clk_disable(void)
+{
+	int rc = 0;
+	if (fs_vpe) {
+		regulator_disable(fs_vpe);
+		regulator_put(fs_vpe);
+	}
+
+	rc = msm_camio_clk_disable(CAMIO_VPE_CLK);
+	if (rc < 0)
+		return rc;
+	rc = msm_camio_clk_disable(CAMIO_VPE_PCLK);
+	return rc;
+}
+
+int msm_camio_vpe_clk_enable(uint32_t clk_rate)
+{
+	int rc = 0;
+	fs_vpe = regulator_get(NULL, "fs_vpe");
+	if (IS_ERR(fs_vpe)) {
+		CDBG("%s: Regulator FS_VPE get failed %ld\n", __func__,
+			PTR_ERR(fs_vpe));
+		fs_vpe = NULL;
+	} else if (regulator_enable(fs_vpe)) {
+		CDBG("%s: Regulator FS_VPE enable failed\n", __func__);
+		regulator_put(fs_vpe);
+	}
+
+	vpe_clk_rate = clk_rate;
+	rc = msm_camio_clk_enable(CAMIO_VPE_CLK);
+	if (rc < 0)
+		return rc;
+
+	rc = msm_camio_clk_enable(CAMIO_VPE_PCLK);
+	return rc;
+}
+
+#ifdef DBG_CSI
+static int csi_request_irq(void)
+{
+	return request_irq(camio_ext.csiirq, msm_io_csi_irq,
+		IRQF_TRIGGER_HIGH, "csi", 0);
+}
+#else
+static int csi_request_irq(void) { return 0; }
+#endif
+
+#ifdef DBG_CSI
+static void csi_free_irq(void)
+{
+	free_irq(camio_ext.csiirq, 0);
+}
+#else
+static void csi_free_irq(void) { return 0; }
+#endif
+
+int msm_camio_enable(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+	struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+	camio_dev = pdev;
+	camio_ext = camdev->ioext;
+	camio_clk = camdev->ioclk;
+	cam_bus_scale_table = camdev->cam_bus_scale_table;
+
+	msm_camio_clk_enable(CAMIO_VFE_CLK);
+	msm_camio_clk_enable(CAMIO_CSI0_VFE_CLK);
+	msm_camio_clk_enable(CAMIO_CSI1_VFE_CLK);
+	msm_camio_clk_enable(CAMIO_CSI_SRC_CLK);
+	msm_camio_clk_enable(CAMIO_CSI0_CLK);
+	msm_camio_clk_enable(CAMIO_CSI1_CLK);
+	msm_camio_clk_enable(CAMIO_VFE_PCLK);
+	msm_camio_clk_enable(CAMIO_CSI0_PCLK);
+	msm_camio_clk_enable(CAMIO_CSI1_PCLK);
+
+	csiio = request_mem_region(camio_ext.csiphy,
+		camio_ext.csisz, pdev->name);
+	if (!csiio) {
+		rc = -EBUSY;
+		goto common_fail;
+	}
+	csibase = ioremap(camio_ext.csiphy,
+		camio_ext.csisz);
+	if (!csibase) {
+		rc = -ENOMEM;
+		goto csi_busy;
+	}
+	rc = csi_request_irq();
+	if (rc < 0)
+		goto csi_irq_fail;
+
+	return 0;
+
+csi_irq_fail:
+	iounmap(csibase);
+	csibase = NULL;
+csi_busy:
+	release_mem_region(camio_ext.csiphy, camio_ext.csisz);
+	csibase = NULL;
+common_fail:
+	msm_camio_clk_disable(CAMIO_CAM_MCLK_CLK);
+	msm_camio_clk_disable(CAMIO_CSI0_VFE_CLK);
+	msm_camio_clk_disable(CAMIO_CSI0_CLK);
+	msm_camio_clk_disable(CAMIO_CSI1_VFE_CLK);
+	msm_camio_clk_disable(CAMIO_CSI1_CLK);
+	msm_camio_clk_disable(CAMIO_VFE_PCLK);
+	msm_camio_clk_disable(CAMIO_CSI0_PCLK);
+	msm_camio_clk_disable(CAMIO_CSI1_PCLK);
+	msm_camera_vreg_disable();
+	camdev->camera_gpio_off();
+	return rc;
+}
+
+static void msm_camio_csi_disable(void)
+{
+	uint32_t val;
+
+	val = 0x0;
+	if (csibase != NULL) {
+		CDBG("%s MIPI_PHY_D0_CONTROL2 val=0x%x\n", __func__, val);
+		msm_camera_io_w(val, csibase + MIPI_PHY_D0_CONTROL2);
+		msm_camera_io_w(val, csibase + MIPI_PHY_D1_CONTROL2);
+		msm_camera_io_w(val, csibase + MIPI_PHY_D2_CONTROL2);
+		msm_camera_io_w(val, csibase + MIPI_PHY_D3_CONTROL2);
+		CDBG("%s MIPI_PHY_CL_CONTROL val=0x%x\n", __func__, val);
+		msm_camera_io_w(val, csibase + MIPI_PHY_CL_CONTROL);
+		msleep(20);
+		val = msm_camera_io_r(csibase + MIPI_PHY_D1_CONTROL);
+		val &=
+		~((0x1 << MIPI_PHY_D1_CONTROL_MIPI_CLK_PHY_SHUTDOWNB_SHFT)
+		|(0x1 << MIPI_PHY_D1_CONTROL_MIPI_DATA_PHY_SHUTDOWNB_SHFT));
+		CDBG("%s MIPI_PHY_D1_CONTROL val=0x%x\n", __func__, val);
+		msm_camera_io_w(val, csibase + MIPI_PHY_D1_CONTROL);
+		usleep_range(5000, 6000);
+		msm_camera_io_w(0x0, csibase + MIPI_INTERRUPT_MASK);
+		msm_camera_io_w(0x0, csibase + MIPI_INTERRUPT_STATUS);
+		csi_free_irq();
+		iounmap(csibase);
+		csibase = NULL;
+		release_mem_region(camio_ext.csiphy, camio_ext.csisz);
+	}
+}
+void msm_camio_disable(struct platform_device *pdev)
+{
+	CDBG("disable mipi\n");
+	msm_camio_csi_disable();
+	CDBG("disable clocks\n");
+	msm_camio_clk_disable(CAMIO_CSI0_VFE_CLK);
+	msm_camio_clk_disable(CAMIO_CSI0_CLK);
+	msm_camio_clk_disable(CAMIO_CSI1_VFE_CLK);
+	msm_camio_clk_disable(CAMIO_CSI1_CLK);
+	msm_camio_clk_disable(CAMIO_VFE_PCLK);
+	msm_camio_clk_disable(CAMIO_CSI0_PCLK);
+	msm_camio_clk_disable(CAMIO_CSI1_PCLK);
+	msm_camio_clk_disable(CAMIO_CSI_SRC_CLK);
+	msm_camio_clk_disable(CAMIO_VFE_CLK);
+}
+
+int msm_camio_sensor_clk_on(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+	struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+	camio_dev = pdev;
+	camio_ext = camdev->ioext;
+	camio_clk = camdev->ioclk;
+
+	msm_camera_vreg_enable();
+	usleep_range(10000, 11000);
+	rc = camdev->camera_gpio_on();
+	if (rc < 0)
+		return rc;
+	return msm_camio_clk_enable(CAMIO_CAM_MCLK_CLK);
+}
+
+int msm_camio_sensor_clk_off(struct platform_device *pdev)
+{
+	struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+	struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+	msm_camera_vreg_disable();
+	camdev->camera_gpio_off();
+	return msm_camio_clk_disable(CAMIO_CAM_MCLK_CLK);
+
+}
+
+void msm_camio_vfe_blk_reset(void)
+{
+	return;
+}
+
+int msm_camio_probe_on(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+	struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+	camio_dev = pdev;
+	camio_ext = camdev->ioext;
+	camio_clk = camdev->ioclk;
+
+	rc = camdev->camera_gpio_on();
+	if (rc < 0)
+		return rc;
+	msm_camera_vreg_enable();
+	return msm_camio_clk_enable(CAMIO_CAM_MCLK_CLK);
+}
+
+int msm_camio_probe_off(struct platform_device *pdev)
+{
+	struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+	struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+	msm_camera_vreg_disable();
+	camdev->camera_gpio_off();
+	return msm_camio_clk_disable(CAMIO_CAM_MCLK_CLK);
+}
+
+int msm_camio_csi_config(struct msm_camera_csi_params *csi_params)
+{
+	int rc = 0;
+	uint32_t val = 0;
+	int i;
+
+	CDBG("msm_camio_csi_config\n");
+	if (csibase != NULL) {
+		/* SOT_ECC_EN enable error correction for SYNC (data-lane) */
+		msm_camera_io_w(0x4, csibase + MIPI_PHY_CONTROL);
+
+		/* SW_RST to the CSI core */
+		msm_camera_io_w(MIPI_PROTOCOL_CONTROL_SW_RST_BMSK,
+		csibase + MIPI_PROTOCOL_CONTROL);
+
+		/* PROTOCOL CONTROL */
+		val = MIPI_PROTOCOL_CONTROL_LONG_PACKET_HEADER_CAPTURE_BMSK |
+			MIPI_PROTOCOL_CONTROL_DECODE_ID_BMSK |
+			MIPI_PROTOCOL_CONTROL_ECC_EN_BMSK;
+		val |= (uint32_t)(csi_params->data_format) <<
+			MIPI_PROTOCOL_CONTROL_DATA_FORMAT_SHFT;
+		val |= csi_params->dpcm_scheme <<
+			MIPI_PROTOCOL_CONTROL_DPCM_SCHEME_SHFT;
+		CDBG("%s MIPI_PROTOCOL_CONTROL val=0x%x\n", __func__, val);
+		msm_camera_io_w(val, csibase + MIPI_PROTOCOL_CONTROL);
+
+		/* settle_cnt is very sensitive to speed!
+		increase this value to run at higher speeds */
+		val = (csi_params->settle_cnt <<
+			MIPI_PHY_D0_CONTROL2_SETTLE_COUNT_SHFT) |
+			(0x0F << MIPI_PHY_D0_CONTROL2_HS_TERM_IMP_SHFT) |
+			(0x1 << MIPI_PHY_D0_CONTROL2_LP_REC_EN_SHFT) |
+			(0x1 << MIPI_PHY_D0_CONTROL2_ERR_SOT_HS_EN_SHFT);
+		CDBG("%s MIPI_PHY_D0_CONTROL2 val=0x%x\n", __func__, val);
+		for (i = 0; i < csi_params->lane_cnt; i++)
+			msm_camera_io_w(val,
+				csibase + MIPI_PHY_D0_CONTROL2 + i * 4);
+
+		val = (0x0F << MIPI_PHY_CL_CONTROL_HS_TERM_IMP_SHFT) |
+			(0x1 << MIPI_PHY_CL_CONTROL_LP_REC_EN_SHFT);
+		CDBG("%s MIPI_PHY_CL_CONTROL val=0x%x\n", __func__, val);
+		msm_camera_io_w(val, csibase + MIPI_PHY_CL_CONTROL);
+
+		val = 0 << MIPI_PHY_D0_CONTROL_HS_REC_EQ_SHFT;
+		msm_camera_io_w(val, csibase + MIPI_PHY_D0_CONTROL);
+
+		val =
+		(0x1 << MIPI_PHY_D1_CONTROL_MIPI_CLK_PHY_SHUTDOWNB_SHFT)
+		|(0x1 << MIPI_PHY_D1_CONTROL_MIPI_DATA_PHY_SHUTDOWNB_SHFT);
+		CDBG("%s MIPI_PHY_D1_CONTROL val=0x%x\n", __func__, val);
+		msm_camera_io_w(val, csibase + MIPI_PHY_D1_CONTROL);
+
+		msm_camera_io_w(0x00000000, csibase + MIPI_PHY_D2_CONTROL);
+		msm_camera_io_w(0x00000000, csibase + MIPI_PHY_D3_CONTROL);
+
+		/* halcyon only supports 1 or 2 lane */
+		switch (csi_params->lane_cnt) {
+		case 1:
+			msm_camera_io_w(csi_params->lane_assign << 8 | 0x4,
+				csibase + MIPI_CAMERA_CNTL);
+			break;
+		case 2:
+			msm_camera_io_w(csi_params->lane_assign << 8 | 0x5,
+				csibase + MIPI_CAMERA_CNTL);
+			break;
+		case 3:
+			msm_camera_io_w(csi_params->lane_assign << 8 | 0x6,
+				csibase + MIPI_CAMERA_CNTL);
+			break;
+		case 4:
+			msm_camera_io_w(csi_params->lane_assign << 8 | 0x7,
+				csibase + MIPI_CAMERA_CNTL);
+			break;
+		}
+
+		/* mask out ID_ERROR[19], DATA_CMM_ERR[11]
+		and CLK_CMM_ERR[10] - de-featured */
+		msm_camera_io_w(0xF017F3C0, csibase + MIPI_INTERRUPT_MASK);
+		/*clear IRQ bits*/
+		msm_camera_io_w(0xF017F3C0, csibase + MIPI_INTERRUPT_STATUS);
+	} else {
+		pr_info("CSIBASE is NULL");
+	}
+
+	return rc;
+}
+
+void msm_camio_set_perf_lvl(enum msm_bus_perf_setting perf_setting)
+{
+	static uint32_t bus_perf_client;
+	int rc = 0;
+	switch (perf_setting) {
+	case S_INIT:
+		bus_perf_client =
+			msm_bus_scale_register_client(cam_bus_scale_table);
+		if (!bus_perf_client) {
+			pr_err("%s: Registration Failed!!!\n", __func__);
+			bus_perf_client = 0;
+			return;
+		}
+		CDBG("%s: S_INIT rc = %u\n", __func__, bus_perf_client);
+		break;
+	case S_EXIT:
+		if (bus_perf_client) {
+			CDBG("%s: S_EXIT\n", __func__);
+			msm_bus_scale_unregister_client(bus_perf_client);
+		} else
+			pr_err("%s: Bus Client NOT Registered!!!\n", __func__);
+		break;
+	case S_PREVIEW:
+		if (bus_perf_client) {
+			rc = msm_bus_scale_client_update_request(
+				bus_perf_client, 1);
+			CDBG("%s: S_PREVIEW rc = %d\n", __func__, rc);
+		} else
+			pr_err("%s: Bus Client NOT Registered!!!\n", __func__);
+		break;
+	case S_VIDEO:
+		if (bus_perf_client) {
+			rc = msm_bus_scale_client_update_request(
+				bus_perf_client, 2);
+			CDBG("%s: S_VIDEO rc = %d\n", __func__, rc);
+		} else
+			pr_err("%s: Bus Client NOT Registered!!!\n", __func__);
+		break;
+	case S_CAPTURE:
+		if (bus_perf_client) {
+			rc = msm_bus_scale_client_update_request(
+				bus_perf_client, 3);
+			CDBG("%s: S_CAPTURE rc = %d\n", __func__, rc);
+		} else
+			pr_err("%s: Bus Client NOT Registered!!!\n", __func__);
+		break;
+
+	case S_ZSL:
+		if (bus_perf_client) {
+			rc = msm_bus_scale_client_update_request(
+				bus_perf_client, 4);
+			CDBG("%s: S_ZSL rc = %d\n", __func__, rc);
+		} else
+			pr_err("%s: Bus Client NOT Registered!!!\n", __func__);
+		break;
+	case S_STEREO_VIDEO:
+		if (bus_perf_client) {
+			rc = msm_bus_scale_client_update_request(
+				bus_perf_client, 5);
+			CDBG("%s: S_STEREO_VIDEO rc = %d\n", __func__, rc);
+		} else
+			pr_err("%s: Bus Client NOT Registered!!!\n", __func__);
+		break;
+	case S_STEREO_CAPTURE:
+		if (bus_perf_client) {
+			rc = msm_bus_scale_client_update_request(
+				bus_perf_client, 6);
+			CDBG("%s: S_STEREO_VIDEO rc = %d\n", __func__, rc);
+		} else
+			pr_err("%s: Bus Client NOT Registered!!!\n", __func__);
+		break;
+	case S_DEFAULT:
+		break;
+	default:
+		pr_warning("%s: INVALID CASE\n", __func__);
+	}
+}
+
+int msm_cam_core_reset(void)
+{
+	struct clk *clk1;
+	int rc = 0;
+	clk1 = clk_get(&camio_dev->dev, "csi_vfe_clk");
+	if (IS_ERR(clk1)) {
+		pr_err("%s: did not get csi_vfe_clk\n", __func__);
+		return PTR_ERR(clk1);
+	}
+	rc = clk_reset(clk1, CLK_RESET_ASSERT);
+	if (rc) {
+		pr_err("%s:csi_vfe_clk assert failed\n", __func__);
+		clk_put(clk1);
+		return rc;
+	}
+	usleep_range(1000, 1200);
+	rc = clk_reset(clk1, CLK_RESET_DEASSERT);
+	if (rc) {
+		pr_err("%s:csi_vfe_clk deassert failed\n", __func__);
+		clk_put(clk1);
+		return rc;
+	}
+	clk_put(clk1);
+
+	clk1 = clk_get(&camio_dev->dev, "csi_clk");
+	if (IS_ERR(clk1)) {
+		pr_err("%s: did not get csi_clk\n", __func__);
+		return PTR_ERR(clk1);
+	}
+	rc = clk_reset(clk1, CLK_RESET_ASSERT);
+	if (rc) {
+		pr_err("%s:csi_clk assert failed\n", __func__);
+		clk_put(clk1);
+		return rc;
+	}
+	usleep_range(1000, 1200);
+	rc = clk_reset(clk1, CLK_RESET_DEASSERT);
+	if (rc) {
+		pr_err("%s:csi_clk deassert failed\n", __func__);
+		clk_put(clk1);
+		return rc;
+	}
+	clk_put(clk1);
+
+	clk1 = clk_get(&camio_dev->dev, "csi_pclk");
+	if (IS_ERR(clk1)) {
+		pr_err("%s: did not get csi_pclk\n", __func__);
+		return PTR_ERR(clk1);
+	}
+	rc = clk_reset(clk1, CLK_RESET_ASSERT);
+	if (rc) {
+		pr_err("%s:csi_pclk assert failed\n", __func__);
+		clk_put(clk1);
+		return rc;
+	}
+	usleep_range(1000, 1200);
+	rc = clk_reset(clk1, CLK_RESET_DEASSERT);
+	if (rc) {
+		pr_err("%s:csi_pclk deassert failed\n", __func__);
+		clk_put(clk1);
+		return rc;
+	}
+	clk_put(clk1);
+
+	return rc;
+}
diff --git a/drivers/media/video/msm/io/msm_io_vfe31.c b/drivers/media/video/msm/io/msm_io_vfe31.c
new file mode 100644
index 0000000..0c0c450
--- /dev/null
+++ b/drivers/media/video/msm/io/msm_io_vfe31.c
@@ -0,0 +1,775 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/pm_qos.h>
+#include <linux/regulator/consumer.h>
+#include <mach/gpio.h>
+#include <mach/board.h>
+#include <mach/camera.h>
+#include <mach/vreg.h>
+#include <mach/clk.h>
+
+#define CAMIF_CFG_RMSK             0x1fffff
+#define CAM_SEL_BMSK               0x2
+#define CAM_PCLK_SRC_SEL_BMSK      0x60000
+#define CAM_PCLK_INVERT_BMSK       0x80000
+#define CAM_PAD_REG_SW_RESET_BMSK  0x100000
+
+#define EXT_CAM_HSYNC_POL_SEL_BMSK 0x10000
+#define EXT_CAM_VSYNC_POL_SEL_BMSK 0x8000
+#define MDDI_CLK_CHICKEN_BIT_BMSK  0x80
+
+#define CAM_SEL_SHFT               0x1
+#define CAM_PCLK_SRC_SEL_SHFT      0x11
+#define CAM_PCLK_INVERT_SHFT       0x13
+#define CAM_PAD_REG_SW_RESET_SHFT  0x14
+
+#define EXT_CAM_HSYNC_POL_SEL_SHFT 0x10
+#define EXT_CAM_VSYNC_POL_SEL_SHFT 0xF
+#define MDDI_CLK_CHICKEN_BIT_SHFT  0x7
+
+/* MIPI	CSI	controller registers */
+#define	MIPI_PHY_CONTROL			0x00000000
+#define	MIPI_PROTOCOL_CONTROL		0x00000004
+#define	MIPI_INTERRUPT_STATUS		0x00000008
+#define	MIPI_INTERRUPT_MASK			0x0000000C
+#define	MIPI_CAMERA_CNTL			0x00000024
+#define	MIPI_CALIBRATION_CONTROL	0x00000018
+#define	MIPI_PHY_D0_CONTROL2		0x00000038
+#define	MIPI_PHY_D1_CONTROL2		0x0000003C
+#define	MIPI_PHY_D2_CONTROL2		0x00000040
+#define	MIPI_PHY_D3_CONTROL2		0x00000044
+#define	MIPI_PHY_CL_CONTROL			0x00000048
+#define	MIPI_PHY_D0_CONTROL			0x00000034
+#define	MIPI_PHY_D1_CONTROL			0x00000020
+#define	MIPI_PHY_D2_CONTROL			0x0000002C
+#define	MIPI_PHY_D3_CONTROL			0x00000030
+#define	MIPI_PROTOCOL_CONTROL_SW_RST_BMSK			0x8000000
+#define	MIPI_PROTOCOL_CONTROL_LONG_PACKET_HEADER_CAPTURE_BMSK	0x200000
+#define	MIPI_PROTOCOL_CONTROL_DATA_FORMAT_BMSK			0x180000
+#define	MIPI_PROTOCOL_CONTROL_DECODE_ID_BMSK			0x40000
+#define	MIPI_PROTOCOL_CONTROL_ECC_EN_BMSK			0x20000
+#define	MIPI_CALIBRATION_CONTROL_SWCAL_CAL_EN_SHFT		0x16
+#define	MIPI_CALIBRATION_CONTROL_SWCAL_STRENGTH_OVERRIDE_EN_SHFT	0x15
+#define	MIPI_CALIBRATION_CONTROL_CAL_SW_HW_MODE_SHFT		0x14
+#define	MIPI_CALIBRATION_CONTROL_MANUAL_OVERRIDE_EN_SHFT	0x7
+#define	MIPI_PROTOCOL_CONTROL_DATA_FORMAT_SHFT			0x13
+#define	MIPI_PROTOCOL_CONTROL_DPCM_SCHEME_SHFT			0x1e
+#define	MIPI_PHY_D0_CONTROL2_SETTLE_COUNT_SHFT			0x18
+#define	MIPI_PHY_D0_CONTROL2_HS_TERM_IMP_SHFT			0x10
+#define	MIPI_PHY_D0_CONTROL2_LP_REC_EN_SHFT				0x4
+#define	MIPI_PHY_D0_CONTROL2_ERR_SOT_HS_EN_SHFT			0x3
+#define	MIPI_PHY_D1_CONTROL2_SETTLE_COUNT_SHFT			0x18
+#define	MIPI_PHY_D1_CONTROL2_HS_TERM_IMP_SHFT			0x10
+#define	MIPI_PHY_D1_CONTROL2_LP_REC_EN_SHFT				0x4
+#define	MIPI_PHY_D1_CONTROL2_ERR_SOT_HS_EN_SHFT			0x3
+#define	MIPI_PHY_D2_CONTROL2_SETTLE_COUNT_SHFT			0x18
+#define	MIPI_PHY_D2_CONTROL2_HS_TERM_IMP_SHFT			0x10
+#define	MIPI_PHY_D2_CONTROL2_LP_REC_EN_SHFT				0x4
+#define	MIPI_PHY_D2_CONTROL2_ERR_SOT_HS_EN_SHFT			0x3
+#define	MIPI_PHY_D3_CONTROL2_SETTLE_COUNT_SHFT			0x18
+#define	MIPI_PHY_D3_CONTROL2_HS_TERM_IMP_SHFT			0x10
+#define	MIPI_PHY_D3_CONTROL2_LP_REC_EN_SHFT				0x4
+#define	MIPI_PHY_D3_CONTROL2_ERR_SOT_HS_EN_SHFT			0x3
+#define	MIPI_PHY_CL_CONTROL_HS_TERM_IMP_SHFT			0x18
+#define	MIPI_PHY_CL_CONTROL_LP_REC_EN_SHFT				0x2
+#define	MIPI_PHY_D0_CONTROL_HS_REC_EQ_SHFT				0x1c
+#define	MIPI_PHY_D1_CONTROL_MIPI_CLK_PHY_SHUTDOWNB_SHFT		0x9
+#define	MIPI_PHY_D1_CONTROL_MIPI_DATA_PHY_SHUTDOWNB_SHFT	0x8
+
+#define	CAMIO_VFE_CLK_SNAP			122880000
+#define	CAMIO_VFE_CLK_PREV			122880000
+
+/* AXI rates in KHz */
+#define MSM_AXI_QOS_PREVIEW     192000
+#define MSM_AXI_QOS_SNAPSHOT    192000
+#define MSM_AXI_QOS_RECORDING   192000
+
+static struct clk *camio_vfe_mdc_clk;
+static struct clk *camio_mdc_clk;
+static struct clk *camio_vfe_clk;
+static struct clk *camio_vfe_camif_clk;
+static struct clk *camio_vfe_pbdg_clk;
+static struct clk *camio_cam_m_clk;
+static struct clk *camio_camif_pad_pbdg_clk;
+static struct clk *camio_csi_clk;
+static struct clk *camio_csi_pclk;
+static struct clk *camio_csi_vfe_clk;
+static struct clk *camio_vpe_clk;
+static struct regulator *fs_vpe;
+static struct msm_camera_io_ext camio_ext;
+static struct msm_camera_io_clk camio_clk;
+static struct resource *camifpadio, *csiio;
+void __iomem *camifpadbase, *csibase;
+static uint32_t vpe_clk_rate;
+
+static struct regulator_bulk_data regs[] = {
+	{ .supply = "gp2",  .min_uV = 2600000, .max_uV = 2600000 },
+	{ .supply = "lvsw1" },
+	{ .supply = "fs_vfe" },
+	/* sn12m0pz regulators */
+	{ .supply = "gp6",  .min_uV = 3050000, .max_uV = 3100000 },
+	{ .supply = "gp16", .min_uV = 1200000, .max_uV = 1200000 },
+};
+
+static int reg_count;
+
+static void msm_camera_vreg_enable(struct platform_device *pdev)
+{
+	int count, rc;
+
+	struct device *dev = &pdev->dev;
+
+	/* Use gp6 and gp16 if and only if dev name matches. */
+	if (!strncmp(pdev->name, "msm_camera_sn12m0pz", 20))
+		count = ARRAY_SIZE(regs);
+	else
+		count = ARRAY_SIZE(regs) - 2;
+
+	rc = regulator_bulk_get(dev, count, regs);
+
+	if (rc) {
+		dev_err(dev, "%s: could not get regulators: %d\n",
+				__func__, rc);
+		return;
+	}
+
+	rc = regulator_bulk_set_voltage(count, regs);
+
+	if (rc) {
+		dev_err(dev, "%s: could not set voltages: %d\n",
+				__func__, rc);
+		goto reg_free;
+	}
+
+	rc = regulator_bulk_enable(count, regs);
+
+	if (rc) {
+		dev_err(dev, "%s: could not enable regulators: %d\n",
+				__func__, rc);
+		goto reg_free;
+	}
+
+	reg_count = count;
+	return;
+
+reg_free:
+	regulator_bulk_free(count, regs);
+	return;
+}
+
+
+static void msm_camera_vreg_disable(void)
+{
+	regulator_bulk_disable(reg_count, regs);
+	regulator_bulk_free(reg_count, regs);
+	reg_count = 0;
+}
+
+int msm_camio_clk_enable(enum msm_camio_clk_type clktype)
+{
+	int rc = 0;
+	struct clk *clk = NULL;
+
+	switch (clktype) {
+	case CAMIO_VFE_MDC_CLK:
+		camio_vfe_mdc_clk =
+		clk = clk_get(NULL, "vfe_mdc_clk");
+		break;
+
+	case CAMIO_MDC_CLK:
+		camio_mdc_clk =
+		clk = clk_get(NULL, "mdc_clk");
+		break;
+
+	case CAMIO_VFE_CLK:
+		camio_vfe_clk =
+		clk = clk_get(NULL, "vfe_clk");
+		msm_camio_clk_rate_set_2(clk, camio_clk.vfe_clk_rate);
+		break;
+
+	case CAMIO_VFE_CAMIF_CLK:
+		camio_vfe_camif_clk =
+		clk = clk_get(NULL, "vfe_camif_clk");
+		break;
+
+	case CAMIO_VFE_PBDG_CLK:
+		camio_vfe_pbdg_clk =
+		clk = clk_get(NULL, "vfe_pclk");
+		break;
+
+	case CAMIO_CAM_MCLK_CLK:
+		camio_cam_m_clk =
+		clk = clk_get(NULL, "cam_m_clk");
+		msm_camio_clk_rate_set_2(clk, camio_clk.mclk_clk_rate);
+		break;
+
+	case CAMIO_CAMIF_PAD_PBDG_CLK:
+		camio_camif_pad_pbdg_clk =
+		clk = clk_get(NULL, "camif_pad_pclk");
+		break;
+
+	case CAMIO_CSI0_CLK:
+		camio_csi_clk =
+		clk = clk_get(NULL, "csi_clk");
+		msm_camio_clk_rate_set_2(clk, 153600000);
+		break;
+	case CAMIO_CSI0_VFE_CLK:
+		camio_csi_vfe_clk =
+		clk = clk_get(NULL, "csi_vfe_clk");
+		break;
+	case CAMIO_CSI0_PCLK:
+		camio_csi_pclk =
+		clk = clk_get(NULL, "csi_pclk");
+		break;
+
+	case CAMIO_VPE_CLK:
+		camio_vpe_clk =
+		clk = clk_get(NULL, "vpe_clk");
+		vpe_clk_rate = clk_round_rate(clk, vpe_clk_rate);
+		clk_set_rate(clk, vpe_clk_rate);
+		break;
+	default:
+		break;
+	}
+
+	if (!IS_ERR(clk))
+		clk_enable(clk);
+	else
+		rc = -1;
+	return rc;
+}
+
+int msm_camio_clk_disable(enum msm_camio_clk_type clktype)
+{
+	int rc = 0;
+	struct clk *clk = NULL;
+
+	switch (clktype) {
+	case CAMIO_VFE_MDC_CLK:
+		clk = camio_vfe_mdc_clk;
+		break;
+
+	case CAMIO_MDC_CLK:
+		clk = camio_mdc_clk;
+		break;
+
+	case CAMIO_VFE_CLK:
+		clk = camio_vfe_clk;
+		break;
+
+	case CAMIO_VFE_CAMIF_CLK:
+		clk = camio_vfe_camif_clk;
+		break;
+
+	case CAMIO_VFE_PBDG_CLK:
+		clk = camio_vfe_pbdg_clk;
+		break;
+
+	case CAMIO_CAM_MCLK_CLK:
+		clk = camio_cam_m_clk;
+		break;
+
+	case CAMIO_CAMIF_PAD_PBDG_CLK:
+		clk = camio_camif_pad_pbdg_clk;
+		break;
+	case CAMIO_CSI0_CLK:
+		clk = camio_csi_clk;
+		break;
+	case CAMIO_CSI0_VFE_CLK:
+		clk = camio_csi_vfe_clk;
+		break;
+	case CAMIO_CSI0_PCLK:
+		clk = camio_csi_pclk;
+		break;
+	case CAMIO_VPE_CLK:
+		clk = camio_vpe_clk;
+		break;
+	default:
+		break;
+	}
+
+	if (!IS_ERR(clk)) {
+		clk_disable(clk);
+		clk_put(clk);
+	} else
+		rc = -1;
+
+	return rc;
+}
+
+void msm_camio_clk_rate_set(int rate)
+{
+	struct clk *clk = camio_cam_m_clk;
+	clk_set_rate(clk, rate);
+}
+
+int msm_camio_vfe_clk_rate_set(int rate)
+{
+	struct clk *clk = camio_vfe_clk;
+	return clk_set_rate(clk, rate);
+}
+
+void msm_camio_clk_rate_set_2(struct clk *clk, int rate)
+{
+	clk_set_rate(clk, rate);
+}
+
+static irqreturn_t msm_io_csi_irq(int irq_num, void *data)
+{
+	uint32_t irq;
+	irq = msm_camera_io_r(csibase + MIPI_INTERRUPT_STATUS);
+	CDBG("%s MIPI_INTERRUPT_STATUS = 0x%x\n", __func__, irq);
+	msm_camera_io_w(irq, csibase + MIPI_INTERRUPT_STATUS);
+	return IRQ_HANDLED;
+}
+
+int msm_camio_vpe_clk_disable(void)
+{
+	msm_camio_clk_disable(CAMIO_VPE_CLK);
+
+	if (fs_vpe) {
+		regulator_disable(fs_vpe);
+		regulator_put(fs_vpe);
+	}
+
+	return 0;
+}
+
+int msm_camio_vpe_clk_enable(uint32_t clk_rate)
+{
+	fs_vpe = regulator_get(NULL, "fs_vpe");
+	if (IS_ERR(fs_vpe)) {
+		pr_err("%s: Regulator FS_VPE get failed %ld\n", __func__,
+			PTR_ERR(fs_vpe));
+		fs_vpe = NULL;
+	} else if (regulator_enable(fs_vpe)) {
+		pr_err("%s: Regulator FS_VPE enable failed\n", __func__);
+		regulator_put(fs_vpe);
+	}
+
+	vpe_clk_rate = clk_rate;
+	msm_camio_clk_enable(CAMIO_VPE_CLK);
+	return 0;
+}
+
+int msm_camio_enable(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+	msm_camio_clk_enable(CAMIO_VFE_PBDG_CLK);
+	if (!sinfo->csi_if)
+		msm_camio_clk_enable(CAMIO_VFE_CAMIF_CLK);
+	else {
+		msm_camio_clk_enable(CAMIO_VFE_CLK);
+		csiio = request_mem_region(camio_ext.csiphy,
+			camio_ext.csisz, pdev->name);
+		if (!csiio) {
+			rc = -EBUSY;
+			goto common_fail;
+		}
+		csibase = ioremap(camio_ext.csiphy,
+			camio_ext.csisz);
+		if (!csibase) {
+			rc = -ENOMEM;
+			goto csi_busy;
+		}
+		rc = request_irq(camio_ext.csiirq, msm_io_csi_irq,
+			IRQF_TRIGGER_RISING, "csi", 0);
+		if (rc < 0)
+			goto csi_irq_fail;
+		/* enable required clocks for CSI */
+		msm_camio_clk_enable(CAMIO_CSI0_PCLK);
+		msm_camio_clk_enable(CAMIO_CSI0_VFE_CLK);
+		msm_camio_clk_enable(CAMIO_CSI0_CLK);
+	}
+	return 0;
+csi_irq_fail:
+	iounmap(csibase);
+csi_busy:
+	release_mem_region(camio_ext.csiphy, camio_ext.csisz);
+common_fail:
+	msm_camio_clk_disable(CAMIO_VFE_PBDG_CLK);
+	msm_camio_clk_disable(CAMIO_VFE_CLK);
+	return rc;
+}
+
+static void msm_camio_csi_disable(void)
+{
+	uint32_t val;
+	val = 0x0;
+	CDBG("%s MIPI_PHY_D0_CONTROL2 val=0x%x\n", __func__, val);
+	msm_camera_io_w(val, csibase + MIPI_PHY_D0_CONTROL2);
+	msm_camera_io_w(val, csibase + MIPI_PHY_D1_CONTROL2);
+	msm_camera_io_w(val, csibase + MIPI_PHY_D2_CONTROL2);
+	msm_camera_io_w(val, csibase + MIPI_PHY_D3_CONTROL2);
+
+	CDBG("%s MIPI_PHY_CL_CONTROL val=0x%x\n", __func__, val);
+	msm_camera_io_w(val, csibase + MIPI_PHY_CL_CONTROL);
+	usleep_range(9000, 10000);
+	free_irq(camio_ext.csiirq, 0);
+	iounmap(csibase);
+	release_mem_region(camio_ext.csiphy, camio_ext.csisz);
+}
+
+void msm_camio_disable(struct platform_device *pdev)
+{
+	struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+	if (!sinfo->csi_if) {
+		msm_camio_clk_disable(CAMIO_VFE_CAMIF_CLK);
+	} else {
+		CDBG("disable mipi\n");
+		msm_camio_csi_disable();
+		CDBG("disable clocks\n");
+		msm_camio_clk_disable(CAMIO_CSI0_PCLK);
+		msm_camio_clk_disable(CAMIO_CSI0_VFE_CLK);
+		msm_camio_clk_disable(CAMIO_CSI0_CLK);
+		msm_camio_clk_disable(CAMIO_VFE_CLK);
+	}
+	msm_camio_clk_disable(CAMIO_VFE_PBDG_CLK);
+}
+
+void msm_camio_camif_pad_reg_reset(void)
+{
+	uint32_t reg;
+
+	msm_camio_clk_sel(MSM_CAMIO_CLK_SRC_INTERNAL);
+	usleep_range(10000, 11000);
+
+	reg = (msm_camera_io_r(camifpadbase)) & CAMIF_CFG_RMSK;
+	reg |= 0x3;
+	msm_camera_io_w(reg, camifpadbase);
+	usleep_range(10000, 11000);
+
+	reg = (msm_camera_io_r(camifpadbase)) & CAMIF_CFG_RMSK;
+	reg |= 0x10;
+	msm_camera_io_w(reg, camifpadbase);
+	usleep_range(10000, 11000);
+
+	reg = (msm_camera_io_r(camifpadbase)) & CAMIF_CFG_RMSK;
+	/* Need to be uninverted*/
+	reg &= 0x03;
+	msm_camera_io_w(reg, camifpadbase);
+	usleep_range(10000, 11000);
+}
+
+void msm_camio_vfe_blk_reset(void)
+{
+	return;
+
+
+}
+
+void msm_camio_camif_pad_reg_reset_2(void)
+{
+	uint32_t reg;
+	uint32_t mask, value;
+	reg = (msm_camera_io_r(camifpadbase)) & CAMIF_CFG_RMSK;
+	mask = CAM_PAD_REG_SW_RESET_BMSK;
+	value = 1 << CAM_PAD_REG_SW_RESET_SHFT;
+	msm_camera_io_w((reg & (~mask)) | (value & mask), camifpadbase);
+	usleep_range(10000, 11000);
+	reg = (msm_camera_io_r(camifpadbase)) & CAMIF_CFG_RMSK;
+	mask = CAM_PAD_REG_SW_RESET_BMSK;
+	value = 0 << CAM_PAD_REG_SW_RESET_SHFT;
+	msm_camera_io_w((reg & (~mask)) | (value & mask), camifpadbase);
+	usleep_range(10000, 11000);
+}
+
+void msm_camio_clk_sel(enum msm_camio_clk_src_type srctype)
+{
+	struct clk *clk = NULL;
+
+	clk = camio_vfe_clk;
+
+	if (clk != NULL) {
+		switch (srctype) {
+		case MSM_CAMIO_CLK_SRC_INTERNAL:
+			clk_set_flags(clk, 0x00000100 << 1);
+			break;
+
+		case MSM_CAMIO_CLK_SRC_EXTERNAL:
+			clk_set_flags(clk, 0x00000100);
+			break;
+
+		default:
+			break;
+		}
+	}
+}
+int msm_camio_probe_on(struct platform_device *pdev)
+{
+	struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+	struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+	camio_clk = camdev->ioclk;
+	camio_ext = camdev->ioext;
+	camdev->camera_gpio_on();
+	msm_camera_vreg_enable(pdev);
+	return msm_camio_clk_enable(CAMIO_CAM_MCLK_CLK);
+}
+
+int msm_camio_probe_off(struct platform_device *pdev)
+{
+	struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+	struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+	msm_camera_vreg_disable();
+	camdev->camera_gpio_off();
+	return msm_camio_clk_disable(CAMIO_CAM_MCLK_CLK);
+}
+
+int msm_camio_sensor_clk_on(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+	struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+	camio_clk = camdev->ioclk;
+	camio_ext = camdev->ioext;
+	camdev->camera_gpio_on();
+	msm_camera_vreg_enable(pdev);
+	msm_camio_clk_enable(CAMIO_CAM_MCLK_CLK);
+	msm_camio_clk_enable(CAMIO_CAMIF_PAD_PBDG_CLK);
+	if (!sinfo->csi_if) {
+		camifpadio = request_mem_region(camio_ext.camifpadphy,
+			camio_ext.camifpadsz, pdev->name);
+		msm_camio_clk_enable(CAMIO_VFE_CLK);
+		if (!camifpadio) {
+			rc = -EBUSY;
+			goto common_fail;
+		}
+		camifpadbase = ioremap(camio_ext.camifpadphy,
+			camio_ext.camifpadsz);
+		if (!camifpadbase) {
+			CDBG("msm_camio_sensor_clk_on fail\n");
+			rc = -ENOMEM;
+			goto parallel_busy;
+		}
+	}
+	return rc;
+parallel_busy:
+	release_mem_region(camio_ext.camifpadphy, camio_ext.camifpadsz);
+	goto common_fail;
+common_fail:
+	msm_camio_clk_disable(CAMIO_CAM_MCLK_CLK);
+	msm_camio_clk_disable(CAMIO_VFE_CLK);
+	msm_camio_clk_disable(CAMIO_CAMIF_PAD_PBDG_CLK);
+	msm_camera_vreg_disable();
+	camdev->camera_gpio_off();
+	return rc;
+}
+
+int msm_camio_sensor_clk_off(struct platform_device *pdev)
+{
+	uint32_t rc = 0;
+	struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+	struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+	camdev->camera_gpio_off();
+	msm_camera_vreg_disable();
+	rc = msm_camio_clk_disable(CAMIO_CAM_MCLK_CLK);
+	rc = msm_camio_clk_disable(CAMIO_CAMIF_PAD_PBDG_CLK);
+	if (!sinfo->csi_if) {
+		iounmap(camifpadbase);
+		release_mem_region(camio_ext.camifpadphy, camio_ext.camifpadsz);
+		rc = msm_camio_clk_disable(CAMIO_VFE_CLK);
+	}
+	return rc;
+}
+
+int msm_camio_csi_config(struct msm_camera_csi_params *csi_params)
+{
+	int rc = 0;
+	uint32_t val = 0;
+	int i;
+
+	CDBG("msm_camio_csi_config\n");
+
+	/* SOT_ECC_EN enable error correction for SYNC (data-lane) */
+	msm_camera_io_w(0x4, csibase + MIPI_PHY_CONTROL);
+
+	/* SW_RST to the CSI core */
+	msm_camera_io_w(MIPI_PROTOCOL_CONTROL_SW_RST_BMSK,
+		csibase + MIPI_PROTOCOL_CONTROL);
+
+	/* PROTOCOL CONTROL */
+	val = MIPI_PROTOCOL_CONTROL_LONG_PACKET_HEADER_CAPTURE_BMSK |
+		MIPI_PROTOCOL_CONTROL_DECODE_ID_BMSK |
+		MIPI_PROTOCOL_CONTROL_ECC_EN_BMSK;
+	val |= (uint32_t)(csi_params->data_format) <<
+		MIPI_PROTOCOL_CONTROL_DATA_FORMAT_SHFT;
+	val |= csi_params->dpcm_scheme <<
+		MIPI_PROTOCOL_CONTROL_DPCM_SCHEME_SHFT;
+	CDBG("%s MIPI_PROTOCOL_CONTROL val=0x%x\n", __func__, val);
+	msm_camera_io_w(val, csibase + MIPI_PROTOCOL_CONTROL);
+
+	/* SW CAL EN */
+	val = (0x1 << MIPI_CALIBRATION_CONTROL_SWCAL_CAL_EN_SHFT) |
+		(0x1 <<
+		MIPI_CALIBRATION_CONTROL_SWCAL_STRENGTH_OVERRIDE_EN_SHFT) |
+		(0x1 << MIPI_CALIBRATION_CONTROL_CAL_SW_HW_MODE_SHFT) |
+		(0x1 << MIPI_CALIBRATION_CONTROL_MANUAL_OVERRIDE_EN_SHFT);
+	CDBG("%s MIPI_CALIBRATION_CONTROL val=0x%x\n", __func__, val);
+	msm_camera_io_w(val, csibase + MIPI_CALIBRATION_CONTROL);
+
+	/* settle_cnt is very sensitive to speed!
+	increase this value to run at higher speeds */
+	val = (csi_params->settle_cnt <<
+			MIPI_PHY_D0_CONTROL2_SETTLE_COUNT_SHFT) |
+		(0x0F << MIPI_PHY_D0_CONTROL2_HS_TERM_IMP_SHFT) |
+		(0x1 << MIPI_PHY_D0_CONTROL2_LP_REC_EN_SHFT) |
+		(0x1 << MIPI_PHY_D0_CONTROL2_ERR_SOT_HS_EN_SHFT);
+	CDBG("%s MIPI_PHY_D0_CONTROL2 val=0x%x\n", __func__, val);
+	for (i = 0; i < csi_params->lane_cnt; i++)
+		msm_camera_io_w(val, csibase + MIPI_PHY_D0_CONTROL2 + i * 4);
+
+	val = (0x0F << MIPI_PHY_CL_CONTROL_HS_TERM_IMP_SHFT) |
+		(0x1 << MIPI_PHY_CL_CONTROL_LP_REC_EN_SHFT);
+	CDBG("%s MIPI_PHY_CL_CONTROL val=0x%x\n", __func__, val);
+	msm_camera_io_w(val, csibase + MIPI_PHY_CL_CONTROL);
+
+	val = 0 << MIPI_PHY_D0_CONTROL_HS_REC_EQ_SHFT;
+	msm_camera_io_w(val, csibase + MIPI_PHY_D0_CONTROL);
+
+	val = (0x1 << MIPI_PHY_D1_CONTROL_MIPI_CLK_PHY_SHUTDOWNB_SHFT) |
+		(0x1 << MIPI_PHY_D1_CONTROL_MIPI_DATA_PHY_SHUTDOWNB_SHFT);
+	CDBG("%s MIPI_PHY_D1_CONTROL val=0x%x\n", __func__, val);
+	msm_camera_io_w(val, csibase + MIPI_PHY_D1_CONTROL);
+
+	msm_camera_io_w(0x00000000, csibase + MIPI_PHY_D2_CONTROL);
+	msm_camera_io_w(0x00000000, csibase + MIPI_PHY_D3_CONTROL);
+
+	/* halcyon only supports 1 or 2 lane */
+	switch (csi_params->lane_cnt) {
+	case 1:
+		msm_camera_io_w(csi_params->lane_assign << 8 | 0x4,
+			csibase + MIPI_CAMERA_CNTL);
+		break;
+	case 2:
+		msm_camera_io_w(csi_params->lane_assign << 8 | 0x5,
+			csibase + MIPI_CAMERA_CNTL);
+		break;
+	case 3:
+		msm_camera_io_w(csi_params->lane_assign << 8 | 0x6,
+			csibase + MIPI_CAMERA_CNTL);
+		break;
+	case 4:
+		msm_camera_io_w(csi_params->lane_assign << 8 | 0x7,
+			csibase + MIPI_CAMERA_CNTL);
+		break;
+	}
+
+	/* mask out ID_ERROR[19], DATA_CMM_ERR[11]
+	and CLK_CMM_ERR[10] - de-featured */
+	msm_camera_io_w(0xFFF7F3FF, csibase + MIPI_INTERRUPT_MASK);
+	/*clear IRQ bits*/
+	msm_camera_io_w(0xFFF7F3FF, csibase + MIPI_INTERRUPT_STATUS);
+
+	return rc;
+}
+void msm_camio_set_perf_lvl(enum msm_bus_perf_setting perf_setting)
+{
+	switch (perf_setting) {
+	case S_INIT:
+		add_axi_qos();
+		break;
+	case S_PREVIEW:
+		update_axi_qos(MSM_AXI_QOS_PREVIEW);
+		break;
+	case S_VIDEO:
+		update_axi_qos(MSM_AXI_QOS_RECORDING);
+		break;
+	case S_CAPTURE:
+		update_axi_qos(MSM_AXI_QOS_SNAPSHOT);
+		break;
+	case S_DEFAULT:
+		update_axi_qos(PM_QOS_DEFAULT_VALUE);
+		break;
+	case S_EXIT:
+		release_axi_qos();
+		break;
+	default:
+		CDBG("%s: INVALID CASE\n", __func__);
+	}
+}
+
+int msm_cam_core_reset(void)
+{
+	struct clk *clk1;
+	int rc = 0;
+
+	clk1 = clk_get(NULL, "csi_vfe_clk");
+	if (IS_ERR(clk1)) {
+		pr_err("%s: did not get csi_vfe_clk\n", __func__);
+		return PTR_ERR(clk1);
+	}
+
+	rc = clk_reset(clk1, CLK_RESET_ASSERT);
+	if (rc) {
+		pr_err("%s:csi_vfe_clk assert failed\n", __func__);
+		clk_put(clk1);
+		return rc;
+	}
+	usleep_range(1000, 1200);
+	rc = clk_reset(clk1, CLK_RESET_DEASSERT);
+	if (rc) {
+		pr_err("%s:csi_vfe_clk deassert failed\n", __func__);
+		clk_put(clk1);
+		return rc;
+	}
+	clk_put(clk1);
+
+	clk1 = clk_get(NULL, "csi_clk");
+	if (IS_ERR(clk1)) {
+		pr_err("%s: did not get csi_clk\n", __func__);
+		return PTR_ERR(clk1);
+	}
+
+	rc = clk_reset(clk1, CLK_RESET_ASSERT);
+	if (rc) {
+		pr_err("%s:csi_clk assert failed\n", __func__);
+		clk_put(clk1);
+		return rc;
+	}
+	usleep_range(1000, 1200);
+	rc = clk_reset(clk1, CLK_RESET_DEASSERT);
+	if (rc) {
+		pr_err("%s:csi_clk deassert failed\n", __func__);
+		clk_put(clk1);
+		return rc;
+	}
+	clk_put(clk1);
+
+	clk1 = clk_get(NULL, "csi_pclk");
+	if (IS_ERR(clk1)) {
+		pr_err("%s: did not get csi_pclk\n", __func__);
+		return PTR_ERR(clk1);
+	}
+
+	rc = clk_reset(clk1, CLK_RESET_ASSERT);
+	if (rc) {
+		pr_err("%s:csi_pclk assert failed\n", __func__);
+		clk_put(clk1);
+		return rc;
+	}
+	usleep_range(1000, 1200);
+	rc = clk_reset(clk1, CLK_RESET_DEASSERT);
+	if (rc) {
+		pr_err("%s:csi_pclk deassert failed\n", __func__);
+		clk_put(clk1);
+		return rc;
+	}
+	clk_put(clk1);
+
+	return rc;
+}
diff --git a/drivers/media/video/msm/io/msm_io_vfe31_v4l2.c b/drivers/media/video/msm/io/msm_io_vfe31_v4l2.c
new file mode 100644
index 0000000..88a9316
--- /dev/null
+++ b/drivers/media/video/msm/io/msm_io_vfe31_v4l2.c
@@ -0,0 +1,274 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/regulator/consumer.h>
+#include <linux/export.h>
+#include <mach/gpio.h>
+#include <mach/board.h>
+#include <mach/camera.h>
+#include <mach/vreg.h>
+#include <mach/camera.h>
+#include <mach/clk.h>
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
+
+#include <linux/pm_qos.h>
+
+/* AXI rates in KHz */
+#define MSM_AXI_QOS_PREVIEW     192000
+#define MSM_AXI_QOS_SNAPSHOT    192000
+#define MSM_AXI_QOS_RECORDING   192000
+
+#define BUFF_SIZE_128 128
+
+static struct clk *camio_vfe_clk;
+static struct clk *camio_vpe_clk;
+static struct clk *camio_vpe_pclk;
+static struct regulator *fs_vpe;
+
+static int vpe_clk_rate;
+
+int msm_camio_clk_enable(enum msm_camio_clk_type clktype)
+{
+	int rc = 0;
+	struct clk *clk = NULL;
+
+	switch (clktype) {
+	case CAMIO_VPE_CLK:
+		camio_vpe_clk =
+		clk = clk_get(NULL, "vpe_clk");
+		vpe_clk_rate = clk_round_rate(camio_vpe_clk, vpe_clk_rate);
+		clk_set_rate(camio_vpe_clk, vpe_clk_rate);
+		break;
+
+	case CAMIO_VPE_PCLK:
+		camio_vpe_pclk =
+		clk = clk_get(NULL, "vpe_pclk");
+		break;
+
+	default:
+		break;
+	}
+
+	if (!IS_ERR(clk)) {
+		clk_prepare(clk);
+		clk_enable(clk);
+	} else {
+		rc = -1;
+	}
+	return rc;
+}
+
+int msm_camio_clk_disable(enum msm_camio_clk_type clktype)
+{
+	int rc = 0;
+	struct clk *clk = NULL;
+
+	switch (clktype) {
+	case CAMIO_VPE_CLK:
+		clk = camio_vpe_clk;
+		break;
+
+	case CAMIO_VPE_PCLK:
+		clk = camio_vpe_pclk;
+		break;
+
+	default:
+		break;
+	}
+
+	if (!IS_ERR(clk)) {
+		clk_disable(clk);
+		clk_unprepare(clk);
+		clk_put(clk);
+	} else {
+		rc = -1;
+	}
+
+	return rc;
+}
+
+int msm_camio_vfe_clk_rate_set(int rate)
+{
+	int rc = 0;
+	struct clk *clk = camio_vfe_clk;
+	if (rate > clk_get_rate(clk))
+		rc = clk_set_rate(clk, rate);
+	return rc;
+}
+
+void msm_camio_clk_rate_set_2(struct clk *clk, int rate)
+{
+	clk_set_rate(clk, rate);
+}
+
+int msm_camio_vpe_clk_disable(void)
+{
+	int rc = 0;
+	if (fs_vpe) {
+		regulator_disable(fs_vpe);
+		regulator_put(fs_vpe);
+	}
+
+	rc = msm_camio_clk_disable(CAMIO_VPE_CLK);
+	if (rc < 0)
+		return rc;
+	rc = msm_camio_clk_disable(CAMIO_VPE_PCLK);
+	return rc;
+}
+
+int msm_camio_vpe_clk_enable(uint32_t clk_rate)
+{
+	int rc = 0;
+	fs_vpe = regulator_get(NULL, "fs_vpe");
+	if (IS_ERR(fs_vpe)) {
+		CDBG("%s: Regulator FS_VPE get failed %ld\n", __func__,
+			PTR_ERR(fs_vpe));
+		fs_vpe = NULL;
+	} else if (regulator_enable(fs_vpe)) {
+		CDBG("%s: Regulator FS_VPE enable failed\n", __func__);
+		regulator_put(fs_vpe);
+	}
+
+	vpe_clk_rate = clk_rate;
+	rc = msm_camio_clk_enable(CAMIO_VPE_CLK);
+	if (rc < 0)
+		return rc;
+
+	rc = msm_camio_clk_enable(CAMIO_VPE_PCLK);
+	return rc;
+}
+
+void msm_camio_vfe_blk_reset(void)
+{
+	return;
+}
+
+void msm_camio_vfe_blk_reset_3(void)
+{
+	return;
+}
+
+static void msm_camio_axi_cfg(enum msm_bus_perf_setting perf_setting)
+{
+	switch (perf_setting) {
+	case S_INIT:
+		add_axi_qos();
+		break;
+	case S_PREVIEW:
+		update_axi_qos(MSM_AXI_QOS_PREVIEW);
+		break;
+	case S_VIDEO:
+		update_axi_qos(MSM_AXI_QOS_RECORDING);
+		break;
+	case S_CAPTURE:
+		update_axi_qos(MSM_AXI_QOS_SNAPSHOT);
+		break;
+	case S_DEFAULT:
+		update_axi_qos(PM_QOS_DEFAULT_VALUE);
+		break;
+	case S_EXIT:
+		release_axi_qos();
+		break;
+	default:
+		CDBG("%s: INVALID CASE\n", __func__);
+	}
+}
+
+void msm_camio_bus_scale_cfg(struct msm_bus_scale_pdata *cam_bus_scale_table,
+		enum msm_bus_perf_setting perf_setting)
+{
+	static uint32_t bus_perf_client;
+	int rc = 0;
+	if (cam_bus_scale_table == NULL) {
+		msm_camio_axi_cfg(perf_setting);
+		return;
+	}
+
+	switch (perf_setting) {
+	case S_INIT:
+		bus_perf_client =
+			msm_bus_scale_register_client(cam_bus_scale_table);
+		if (!bus_perf_client) {
+			pr_err("%s: Registration Failed!!!\n", __func__);
+			bus_perf_client = 0;
+			return;
+		}
+		CDBG("%s: S_INIT rc = %u\n", __func__, bus_perf_client);
+		break;
+	case S_EXIT:
+		if (bus_perf_client) {
+			CDBG("%s: S_EXIT\n", __func__);
+			msm_bus_scale_unregister_client(bus_perf_client);
+		} else
+			pr_err("%s: Bus Client NOT Registered!!!\n", __func__);
+		break;
+	case S_PREVIEW:
+		if (bus_perf_client) {
+			rc = msm_bus_scale_client_update_request(
+				bus_perf_client, 1);
+			CDBG("%s: S_PREVIEW rc = %d\n", __func__, rc);
+		} else
+			pr_err("%s: Bus Client NOT Registered!!!\n", __func__);
+		break;
+	case S_VIDEO:
+		if (bus_perf_client) {
+			rc = msm_bus_scale_client_update_request(
+				bus_perf_client, 2);
+			CDBG("%s: S_VIDEO rc = %d\n", __func__, rc);
+		} else
+			pr_err("%s: Bus Client NOT Registered!!!\n", __func__);
+		break;
+	case S_CAPTURE:
+		if (bus_perf_client) {
+			rc = msm_bus_scale_client_update_request(
+				bus_perf_client, 3);
+			CDBG("%s: S_CAPTURE rc = %d\n", __func__, rc);
+		} else
+			pr_err("%s: Bus Client NOT Registered!!!\n", __func__);
+		break;
+
+	case S_ZSL:
+		if (bus_perf_client) {
+			rc = msm_bus_scale_client_update_request(
+				bus_perf_client, 4);
+			CDBG("%s: S_ZSL rc = %d\n", __func__, rc);
+		} else
+			pr_err("%s: Bus Client NOT Registered!!!\n", __func__);
+		break;
+	case S_STEREO_VIDEO:
+		if (bus_perf_client) {
+			rc = msm_bus_scale_client_update_request(
+				bus_perf_client, 5);
+			CDBG("%s: S_STEREO_VIDEO rc = %d\n", __func__, rc);
+		} else
+			pr_err("%s: Bus Client NOT Registered!!!\n", __func__);
+		break;
+	case S_STEREO_CAPTURE:
+		if (bus_perf_client) {
+			rc = msm_bus_scale_client_update_request(
+				bus_perf_client, 6);
+			CDBG("%s: S_STEREO_VIDEO rc = %d\n", __func__, rc);
+		} else
+			pr_err("%s: Bus Client NOT Registered!!!\n", __func__);
+		break;
+	case S_DEFAULT:
+		break;
+	default:
+		pr_warning("%s: INVALID CASE\n", __func__);
+	}
+}
+
diff --git a/drivers/media/video/msm/msm.c b/drivers/media/video/msm/msm.c
new file mode 100644
index 0000000..07db742
--- /dev/null
+++ b/drivers/media/video/msm/msm.c
@@ -0,0 +1,3452 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/ioctl.h>
+#include <linux/spinlock.h>
+#include <linux/proc_fs.h>
+#include "msm.h"
+#include "msm_csid.h"
+#include "msm_csic.h"
+#include "msm_csiphy.h"
+#include "msm_ispif.h"
+#include "msm_sensor.h"
+#include "msm_actuator.h"
+#include "msm_vfe32.h"
+#include "msm_camera_eeprom.h"
+
+#define MSM_MAX_CAMERA_SENSORS 5
+
+#ifdef CONFIG_MSM_CAMERA_DEBUG
+#define D(fmt, args...) pr_debug("msm: " fmt, ##args)
+#else
+#define D(fmt, args...) do {} while (0)
+#endif
+
+static unsigned msm_camera_v4l2_nr = -1;
+static struct msm_cam_server_dev g_server_dev;
+static struct class *msm_class;
+static dev_t msm_devno;
+static int vnode_count;
+
+module_param(msm_camera_v4l2_nr, uint, 0644);
+MODULE_PARM_DESC(msm_camera_v4l2_nr, "videoX start number, -1 is autodetect");
+
+static long msm_server_send_v4l2_evt(void *evt);
+static void msm_cam_server_subdev_notify(struct v4l2_subdev *sd,
+	unsigned int notification, void *arg);
+
+static void msm_queue_init(struct msm_device_queue *queue, const char *name)
+{
+	D("%s\n", __func__);
+	spin_lock_init(&queue->lock);
+	queue->len = 0;
+	queue->max = 0;
+	queue->name = name;
+	INIT_LIST_HEAD(&queue->list);
+	init_waitqueue_head(&queue->wait);
+}
+
+static void msm_enqueue(struct msm_device_queue *queue,
+			struct list_head *entry)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&queue->lock, flags);
+	queue->len++;
+	if (queue->len > queue->max) {
+		queue->max = queue->len;
+		pr_info("%s: queue %s new max is %d\n", __func__,
+			queue->name, queue->max);
+	}
+	list_add_tail(entry, &queue->list);
+	wake_up(&queue->wait);
+	D("%s: woke up %s\n", __func__, queue->name);
+	spin_unlock_irqrestore(&queue->lock, flags);
+}
+
+static void msm_drain_eventq(struct msm_device_queue *queue)
+{
+	unsigned long flags;
+	struct msm_queue_cmd *qcmd;
+	spin_lock_irqsave(&queue->lock, flags);
+	while (!list_empty(&queue->list)) {
+		qcmd = list_first_entry(&queue->list,
+			struct msm_queue_cmd, list_eventdata);
+		list_del_init(&qcmd->list_eventdata);
+		kfree(qcmd->command);
+		free_qcmd(qcmd);
+	}
+	spin_unlock_irqrestore(&queue->lock, flags);
+}
+
+static int32_t msm_find_free_queue(void)
+{
+	int i;
+	for (i = 0; i < MAX_NUM_ACTIVE_CAMERA; i++) {
+		struct msm_cam_server_queue *queue;
+		queue = &g_server_dev.server_queue[i];
+		if (!queue->queue_active)
+			return i;
+	}
+	return -EINVAL;
+}
+
+uint32_t msm_camera_get_mctl_handle(void)
+{
+	uint32_t i;
+	if ((g_server_dev.mctl_handle_cnt << 8) == 0)
+		g_server_dev.mctl_handle_cnt++;
+	for (i = 0; i < MAX_NUM_ACTIVE_CAMERA; i++) {
+		if (g_server_dev.mctl[i].handle == 0) {
+			g_server_dev.mctl[i].handle =
+				(++g_server_dev.mctl_handle_cnt) << 8 | i;
+			memset(&g_server_dev.mctl[i].mctl,
+				   0, sizeof(g_server_dev.mctl[i].mctl));
+			return g_server_dev.mctl[i].handle;
+		}
+	}
+	return 0;
+}
+
+struct msm_cam_media_controller *msm_camera_get_mctl(uint32_t handle)
+{
+	uint32_t mctl_index;
+	mctl_index = handle & 0xff;
+	if ((mctl_index < MAX_NUM_ACTIVE_CAMERA) &&
+		(g_server_dev.mctl[mctl_index].handle == handle))
+		return &g_server_dev.mctl[mctl_index].mctl;
+	return NULL;
+}
+
+void msm_camera_free_mctl(uint32_t handle)
+{
+	uint32_t mctl_index;
+	mctl_index = handle & 0xff;
+	if ((mctl_index < MAX_NUM_ACTIVE_CAMERA) &&
+		(g_server_dev.mctl[mctl_index].handle == handle))
+		g_server_dev.mctl[mctl_index].handle = 0;
+	else
+		pr_err("%s: invalid free handle\n", __func__);
+}
+
+/* callback function from all subdevices of a msm_cam_v4l2_device */
+static void msm_cam_v4l2_subdev_notify(struct v4l2_subdev *sd,
+				unsigned int notification, void *arg)
+{
+	struct msm_cam_v4l2_device *pcam;
+	struct msm_cam_media_controller *pmctl;
+
+	if (sd == NULL)
+		return;
+
+	pcam = to_pcam(sd->v4l2_dev);
+
+	if (pcam == NULL)
+		return;
+
+	pmctl = msm_camera_get_mctl(pcam->mctl_handle);
+	if (pmctl == NULL)
+		return;
+}
+
+static int msm_ctrl_cmd_done(void *arg)
+{
+	void __user *uptr;
+	struct msm_queue_cmd *qcmd;
+	struct msm_camera_v4l2_ioctl_t *ioctl_ptr = arg;
+	struct msm_ctrl_cmd *command =
+		kzalloc(sizeof(struct msm_ctrl_cmd), GFP_KERNEL);
+	if (!command) {
+		pr_err("%s Insufficient memory. return", __func__);
+		return -ENOMEM;
+	}
+
+	D("%s\n", __func__);
+	if (copy_from_user(command, (void __user *)ioctl_ptr->ioctl_ptr,
+					   sizeof(struct msm_ctrl_cmd))) {
+		pr_err("%s: copy_from_user failed, size=%d\n",
+			   __func__, sizeof(struct msm_ctrl_cmd));
+		return -EINVAL;
+	}
+
+	D("%s qid %d evtid %d %d\n", __func__, command->queue_idx,
+		command->evt_id,
+		g_server_dev.server_queue[command->queue_idx].evt_id);
+	g_server_dev.server_queue[command->queue_idx].ctrl = command;
+	if (command->evt_id !=
+		g_server_dev.server_queue[command->queue_idx].evt_id) {
+		pr_err("%s Invalid event id from userspace cmd id %d %d qid %d\n",
+			__func__, command->evt_id,
+			g_server_dev.server_queue[command->queue_idx].evt_id,
+			command->queue_idx);
+		return -EINVAL;
+	}
+
+	mutex_lock(&g_server_dev.server_queue_lock);
+	qcmd = kzalloc(sizeof(struct msm_queue_cmd), GFP_KERNEL);
+	atomic_set(&qcmd->on_heap, 1);
+	uptr = command->value;
+	qcmd->command = command;
+
+	if (command->length > 0) {
+		command->value =
+			g_server_dev.server_queue[command->queue_idx].ctrl_data;
+		if (command->length > max_control_command_size) {
+			pr_err("%s: user data %d is too big (max %d)\n",
+				__func__, command->length,
+				max_control_command_size);
+			free_qcmd(qcmd);
+			return -EINVAL;
+		}
+		if (copy_from_user(command->value, uptr, command->length)) {
+			free_qcmd(qcmd);
+			return -EINVAL;
+		}
+	}
+	msm_enqueue(&g_server_dev.server_queue
+		[command->queue_idx].ctrl_q, &qcmd->list_control);
+	mutex_unlock(&g_server_dev.server_queue_lock);
+	return 0;
+}
+
+/* send control command to config and wait for results*/
+static int msm_server_control(struct msm_cam_server_dev *server_dev,
+				struct msm_ctrl_cmd *out)
+{
+	int rc = 0;
+	void *value;
+	struct msm_queue_cmd *rcmd;
+	struct msm_queue_cmd *event_qcmd;
+	struct msm_ctrl_cmd *ctrlcmd;
+	struct msm_device_queue *queue =
+		&server_dev->server_queue[out->queue_idx].ctrl_q;
+
+	struct v4l2_event v4l2_evt;
+	struct msm_isp_event_ctrl *isp_event;
+	isp_event = kzalloc(sizeof(struct msm_isp_event_ctrl), GFP_KERNEL);
+	if (!isp_event) {
+		pr_err("%s Insufficient memory. return", __func__);
+		return -ENOMEM;
+	}
+	event_qcmd = kzalloc(sizeof(struct msm_queue_cmd), GFP_KERNEL);
+	if (!event_qcmd) {
+		pr_err("%s Insufficient memory. return", __func__);
+		return -ENOMEM;
+	}
+
+	D("%s\n", __func__);
+	mutex_lock(&server_dev->server_queue_lock);
+	if (++server_dev->server_evt_id == 0)
+		server_dev->server_evt_id++;
+	D("%s qid %d evtid %d\n", __func__, out->queue_idx,
+		server_dev->server_evt_id);
+
+	server_dev->server_queue[out->queue_idx].evt_id =
+		server_dev->server_evt_id;
+	v4l2_evt.type = V4L2_EVENT_PRIVATE_START + MSM_CAM_RESP_V4L2;
+	v4l2_evt.id = 0;
+	v4l2_evt.u.data[0] = out->queue_idx;
+	/* setup event object to transfer the command; */
+	isp_event->resptype = MSM_CAM_RESP_V4L2;
+	isp_event->isp_data.ctrl = *out;
+	isp_event->isp_data.ctrl.evt_id = server_dev->server_evt_id;
+
+	atomic_set(&event_qcmd->on_heap, 1);
+	event_qcmd->command = isp_event;
+
+	msm_enqueue(&server_dev->server_queue[out->queue_idx].eventData_q,
+				&event_qcmd->list_eventdata);
+
+	/* now send command to config thread in userspace,
+	 * and wait for results */
+	v4l2_event_queue(server_dev->server_command_queue.pvdev,
+					  &v4l2_evt);
+	D("%s v4l2_event_queue: type = 0x%x\n", __func__, v4l2_evt.type);
+	mutex_unlock(&server_dev->server_queue_lock);
+
+	/* wait for config return status */
+	D("Waiting for config status\n");
+	rc = wait_event_interruptible_timeout(queue->wait,
+		!list_empty_careful(&queue->list),
+		msecs_to_jiffies(out->timeout_ms));
+	D("Waiting is over for config status\n");
+	if (list_empty_careful(&queue->list)) {
+		if (!rc)
+			rc = -ETIMEDOUT;
+		if (rc < 0) {
+			kfree(isp_event);
+			pr_err("%s: wait_event error %d\n", __func__, rc);
+			return rc;
+		}
+	}
+
+	rcmd = msm_dequeue(queue, list_control);
+	BUG_ON(!rcmd);
+	D("%s Finished servicing ioctl\n", __func__);
+
+	ctrlcmd = (struct msm_ctrl_cmd *)(rcmd->command);
+	value = out->value;
+	if (ctrlcmd->length > 0 && value != NULL &&
+	    ctrlcmd->length <= out->length)
+		memcpy(value, ctrlcmd->value, ctrlcmd->length);
+
+	memcpy(out, ctrlcmd, sizeof(struct msm_ctrl_cmd));
+	out->value = value;
+
+	kfree(ctrlcmd);
+	server_dev->server_queue[out->queue_idx].ctrl = NULL;
+
+	free_qcmd(rcmd);
+	kfree(isp_event);
+	D("%s: rc %d\n", __func__, rc);
+	/* rc is the time elapsed. */
+	if (rc >= 0) {
+		/* TODO: Refactor msm_ctrl_cmd::status field */
+		if (out->status == 0)
+			rc = -1;
+		else if (out->status == 1 || out->status == 4)
+			rc = 0;
+		else
+			rc = -EINVAL;
+	}
+	return rc;
+}
+
+/*send open command to server*/
+static int msm_send_open_server(struct msm_cam_v4l2_device *pcam)
+{
+	int rc = 0;
+	struct msm_ctrl_cmd ctrlcmd;
+	D("%s qid %d\n", __func__, pcam->server_queue_idx);
+	ctrlcmd.type	   = MSM_V4L2_OPEN;
+	ctrlcmd.timeout_ms = 10000;
+	ctrlcmd.length	 = strnlen(g_server_dev.config_info.config_dev_name[0],
+				MAX_DEV_NAME_LEN)+1;
+	ctrlcmd.value    = (char *)g_server_dev.config_info.config_dev_name[0];
+	ctrlcmd.vnode_id = pcam->vnode_id;
+	ctrlcmd.queue_idx = pcam->server_queue_idx;
+	ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
+
+	/* send command to config thread in usersspace, and get return value */
+	rc = msm_server_control(&g_server_dev, &ctrlcmd);
+
+	return rc;
+}
+
+static int msm_send_close_server(struct msm_cam_v4l2_device *pcam)
+{
+	int rc = 0;
+	struct msm_ctrl_cmd ctrlcmd;
+	D("%s qid %d\n", __func__, pcam->server_queue_idx);
+	ctrlcmd.type	   = MSM_V4L2_CLOSE;
+	ctrlcmd.timeout_ms = 10000;
+	ctrlcmd.length	 = strnlen(g_server_dev.config_info.config_dev_name[0],
+				MAX_DEV_NAME_LEN)+1;
+	ctrlcmd.value    = (char *)g_server_dev.config_info.config_dev_name[0];
+	ctrlcmd.vnode_id = pcam->vnode_id;
+	ctrlcmd.queue_idx = pcam->server_queue_idx;
+	ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
+
+	/* send command to config thread in usersspace, and get return value */
+	rc = msm_server_control(&g_server_dev, &ctrlcmd);
+
+	return rc;
+}
+
+static int msm_server_set_fmt(struct msm_cam_v4l2_device *pcam, int idx,
+				 struct v4l2_format *pfmt)
+{
+	int rc = 0;
+	int i = 0;
+	struct v4l2_pix_format *pix = &pfmt->fmt.pix;
+	struct msm_ctrl_cmd ctrlcmd;
+	struct img_plane_info plane_info;
+
+	plane_info.width = pix->width;
+	plane_info.height = pix->height;
+	plane_info.pixelformat = pix->pixelformat;
+	plane_info.buffer_type = pfmt->type;
+	plane_info.ext_mode = pcam->dev_inst[idx]->image_mode;
+	plane_info.num_planes = 1;
+	D("%s: %d, %d, 0x%x\n", __func__,
+		pfmt->fmt.pix.width, pfmt->fmt.pix.height,
+		pfmt->fmt.pix.pixelformat);
+
+	if (pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		D("%s, Attention! Wrong buf-type %d\n", __func__, pfmt->type);
+
+	for (i = 0; i < pcam->num_fmts; i++)
+		if (pcam->usr_fmts[i].fourcc == pix->pixelformat)
+			break;
+	if (i == pcam->num_fmts) {
+		pr_err("%s: User requested pixelformat %x not supported\n",
+						__func__, pix->pixelformat);
+		return -EINVAL;
+	}
+
+	ctrlcmd.type       = MSM_V4L2_VID_CAP_TYPE;
+	ctrlcmd.length     = sizeof(struct img_plane_info);
+	ctrlcmd.value      = (void *)&plane_info;
+	ctrlcmd.timeout_ms = 10000;
+	ctrlcmd.vnode_id   = pcam->vnode_id;
+	ctrlcmd.queue_idx = pcam->server_queue_idx;
+	ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
+
+	/* send command to config thread in usersspace, and get return value */
+	rc = msm_server_control(&g_server_dev, &ctrlcmd);
+
+	if (rc >= 0) {
+		pcam->dev_inst[idx]->vid_fmt = *pfmt;
+		pcam->dev_inst[idx]->sensor_pxlcode
+					= pcam->usr_fmts[i].pxlcode;
+		D("%s:inst=0x%x,idx=%d,width=%d,heigth=%d\n",
+			 __func__, (u32)pcam->dev_inst[idx], idx,
+			 pcam->dev_inst[idx]->vid_fmt.fmt.pix.width,
+			 pcam->dev_inst[idx]->vid_fmt.fmt.pix.height);
+		pcam->dev_inst[idx]->plane_info = plane_info;
+	}
+
+	return rc;
+}
+
+static int msm_server_set_fmt_mplane(struct msm_cam_v4l2_device *pcam, int idx,
+				 struct v4l2_format *pfmt)
+{
+	int rc = 0;
+	int i = 0;
+	struct v4l2_pix_format_mplane *pix_mp = &pfmt->fmt.pix_mp;
+	struct msm_ctrl_cmd ctrlcmd;
+	struct img_plane_info plane_info;
+
+	plane_info.width = pix_mp->width;
+	plane_info.height = pix_mp->height;
+	plane_info.pixelformat = pix_mp->pixelformat;
+	plane_info.buffer_type = pfmt->type;
+	plane_info.ext_mode = pcam->dev_inst[idx]->image_mode;
+	plane_info.num_planes = pix_mp->num_planes;
+	if (plane_info.num_planes <= 0 ||
+		plane_info.num_planes > VIDEO_MAX_PLANES) {
+		pr_err("%s Invalid number of planes set %d", __func__,
+				plane_info.num_planes);
+		return -EINVAL;
+	}
+	D("%s: %d, %d, 0x%x\n", __func__,
+		pfmt->fmt.pix_mp.width, pfmt->fmt.pix_mp.height,
+		pfmt->fmt.pix_mp.pixelformat);
+
+	if (pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		pr_err("%s, Attention! Wrong buf-type %d\n",
+			__func__, pfmt->type);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < pcam->num_fmts; i++)
+		if (pcam->usr_fmts[i].fourcc == pix_mp->pixelformat)
+			break;
+	if (i == pcam->num_fmts) {
+		pr_err("%s: User requested pixelformat %x not supported\n",
+						__func__, pix_mp->pixelformat);
+		return -EINVAL;
+	}
+
+	ctrlcmd.type       = MSM_V4L2_VID_CAP_TYPE;
+	ctrlcmd.length     = sizeof(struct img_plane_info);
+	ctrlcmd.value      = (void *)&plane_info;
+	ctrlcmd.timeout_ms = 10000;
+	ctrlcmd.vnode_id   = pcam->vnode_id;
+	ctrlcmd.queue_idx = pcam->server_queue_idx;
+
+	/* send command to config thread in usersspace, and get return value */
+	rc = msm_server_control(&g_server_dev, &ctrlcmd);
+	if (rc >= 0) {
+		pcam->dev_inst[idx]->vid_fmt = *pfmt;
+		pcam->dev_inst[idx]->sensor_pxlcode
+					= pcam->usr_fmts[i].pxlcode;
+		D("%s:inst=0x%x,idx=%d,width=%d,heigth=%d\n",
+			 __func__, (u32)pcam->dev_inst[idx], idx,
+			 pcam->dev_inst[idx]->vid_fmt.fmt.pix_mp.width,
+			 pcam->dev_inst[idx]->vid_fmt.fmt.pix_mp.height);
+		pcam->dev_inst[idx]->plane_info = plane_info;
+	}
+
+	return rc;
+}
+
+static int msm_server_streamon(struct msm_cam_v4l2_device *pcam, int idx)
+{
+	int rc = 0;
+	struct msm_ctrl_cmd ctrlcmd;
+	D("%s\n", __func__);
+	ctrlcmd.type	   = MSM_V4L2_STREAM_ON;
+	ctrlcmd.timeout_ms = 10000;
+	ctrlcmd.length	 = 0;
+	ctrlcmd.value    = NULL;
+	ctrlcmd.stream_type = pcam->dev_inst[idx]->image_mode;
+	ctrlcmd.vnode_id = pcam->vnode_id;
+	ctrlcmd.queue_idx = pcam->server_queue_idx;
+	ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
+
+
+	/* send command to config thread in usersspace, and get return value */
+	rc = msm_server_control(&g_server_dev, &ctrlcmd);
+
+	return rc;
+}
+
+static int msm_server_streamoff(struct msm_cam_v4l2_device *pcam, int idx)
+{
+	int rc = 0;
+	struct msm_ctrl_cmd ctrlcmd;
+
+	D("%s, pcam = 0x%x\n", __func__, (u32)pcam);
+	ctrlcmd.type        = MSM_V4L2_STREAM_OFF;
+	ctrlcmd.timeout_ms  = 10000;
+	ctrlcmd.length      = 0;
+	ctrlcmd.value       = NULL;
+	ctrlcmd.stream_type = pcam->dev_inst[idx]->image_mode;
+	ctrlcmd.vnode_id = pcam->vnode_id;
+	ctrlcmd.queue_idx = pcam->server_queue_idx;
+	ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
+
+	/* send command to config thread in usersspace, and get return value */
+	rc = msm_server_control(&g_server_dev, &ctrlcmd);
+
+	return rc;
+}
+
+static int msm_server_proc_ctrl_cmd(struct msm_cam_v4l2_device *pcam,
+				 struct v4l2_control *ctrl, int is_set_cmd)
+{
+	int rc = 0;
+	struct msm_ctrl_cmd ctrlcmd, *tmp_cmd;
+	uint8_t *ctrl_data = NULL;
+	void __user *uptr_cmd;
+	void __user *uptr_value;
+	uint32_t cmd_len = sizeof(struct msm_ctrl_cmd);
+	uint32_t value_len;
+
+	tmp_cmd = (struct msm_ctrl_cmd *)ctrl->value;
+	uptr_cmd = (void __user *)ctrl->value;
+	uptr_value = (void __user *)tmp_cmd->value;
+	value_len = tmp_cmd->length;
+
+	D("%s: cmd type = %d, up1=0x%x, ulen1=%d, up2=0x%x, ulen2=%d\n",
+		__func__, tmp_cmd->type, (uint32_t)uptr_cmd, cmd_len,
+		(uint32_t)uptr_value, tmp_cmd->length);
+
+	ctrl_data = kzalloc(value_len+cmd_len, GFP_KERNEL);
+	if (ctrl_data == 0) {
+		pr_err("%s could not allocate memory\n", __func__);
+		rc = -ENOMEM;
+		goto end;
+	}
+	tmp_cmd = (struct msm_ctrl_cmd *)ctrl_data;
+	if (copy_from_user((void *)ctrl_data, uptr_cmd,
+					cmd_len)) {
+		pr_err("%s: copy_from_user failed.\n", __func__);
+		rc = -EINVAL;
+		goto end;
+	}
+	tmp_cmd->value = (void *)(ctrl_data+cmd_len);
+	if (uptr_value && tmp_cmd->length > 0) {
+		if (copy_from_user((void *)tmp_cmd->value, uptr_value,
+						value_len)) {
+			pr_err("%s: copy_from_user failed, size=%d\n",
+				__func__, value_len);
+			rc = -EINVAL;
+			goto end;
+		}
+	} else
+	tmp_cmd->value = NULL;
+
+	ctrlcmd.type = MSM_V4L2_SET_CTRL_CMD;
+	ctrlcmd.length = cmd_len + value_len;
+	ctrlcmd.value = (void *)ctrl_data;
+	if (tmp_cmd->timeout_ms > 0)
+		ctrlcmd.timeout_ms = tmp_cmd->timeout_ms;
+	else
+		ctrlcmd.timeout_ms = 1000;
+	ctrlcmd.vnode_id = pcam->vnode_id;
+	ctrlcmd.queue_idx = pcam->server_queue_idx;
+	ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
+	/* send command to config thread in usersspace, and get return value */
+	rc = msm_server_control(&g_server_dev, &ctrlcmd);
+	D("%s: msm_server_control rc=%d\n", __func__, rc);
+	if (rc == 0) {
+		if (uptr_value && tmp_cmd->length > 0 &&
+			copy_to_user((void __user *)uptr_value,
+				(void *)(ctrl_data+cmd_len), tmp_cmd->length)) {
+			pr_err("%s: copy_to_user failed, size=%d\n",
+				__func__, tmp_cmd->length);
+			rc = -EINVAL;
+			goto end;
+		}
+		tmp_cmd->value = uptr_value;
+		if (copy_to_user((void __user *)uptr_cmd,
+			(void *)tmp_cmd, cmd_len)) {
+			pr_err("%s: copy_to_user failed in cpy, size=%d\n",
+				__func__, cmd_len);
+			rc = -EINVAL;
+			goto end;
+		}
+	}
+end:
+	D("%s: END, type = %d, vaddr = 0x%x, vlen = %d, status = %d, rc = %d\n",
+		__func__, tmp_cmd->type, (uint32_t)tmp_cmd->value,
+		tmp_cmd->length, tmp_cmd->status, rc);
+	kfree(ctrl_data);
+	return rc;
+}
+
+static int msm_server_s_ctrl(struct msm_cam_v4l2_device *pcam,
+				 struct v4l2_control *ctrl)
+{
+	int rc = 0;
+	struct msm_ctrl_cmd ctrlcmd;
+	uint8_t ctrl_data[max_control_command_size];
+
+	WARN_ON(ctrl == NULL);
+	if (ctrl == NULL) {
+		pr_err("%s Invalid control\n", __func__);
+		return -EINVAL;
+	}
+	if (ctrl->id == MSM_V4L2_PID_CTRL_CMD)
+		return msm_server_proc_ctrl_cmd(pcam, ctrl, 1);
+
+	memset(ctrl_data, 0, sizeof(ctrl_data));
+
+	ctrlcmd.type = MSM_V4L2_SET_CTRL;
+	ctrlcmd.length = sizeof(struct v4l2_control);
+	ctrlcmd.value = (void *)ctrl_data;
+	memcpy(ctrlcmd.value, ctrl, ctrlcmd.length);
+	ctrlcmd.timeout_ms = 1000;
+	ctrlcmd.vnode_id = pcam->vnode_id;
+	ctrlcmd.queue_idx = pcam->server_queue_idx;
+	ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
+
+	/* send command to config thread in usersspace, and get return value */
+	rc = msm_server_control(&g_server_dev, &ctrlcmd);
+
+	return rc;
+}
+
+static int msm_server_g_ctrl(struct msm_cam_v4l2_device *pcam,
+				 struct v4l2_control *ctrl)
+{
+	int rc = 0;
+	struct msm_ctrl_cmd ctrlcmd;
+	uint8_t ctrl_data[max_control_command_size];
+
+	WARN_ON(ctrl == NULL);
+	if (ctrl == NULL) {
+		pr_err("%s Invalid control\n", __func__);
+		return -EINVAL;
+	}
+	if (ctrl->id == MSM_V4L2_PID_CTRL_CMD)
+		return msm_server_proc_ctrl_cmd(pcam, ctrl, 0);
+
+	memset(ctrl_data, 0, sizeof(ctrl_data));
+
+	ctrlcmd.type = MSM_V4L2_GET_CTRL;
+	ctrlcmd.length = sizeof(struct v4l2_control);
+	ctrlcmd.value = (void *)ctrl_data;
+	memcpy(ctrlcmd.value, ctrl, ctrlcmd.length);
+	ctrlcmd.timeout_ms = 1000;
+	ctrlcmd.vnode_id = pcam->vnode_id;
+	ctrlcmd.queue_idx = pcam->server_queue_idx;
+	ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
+
+	/* send command to config thread in usersspace, and get return value */
+	rc = msm_server_control(&g_server_dev, &ctrlcmd);
+
+	ctrl->value = ((struct v4l2_control *)ctrlcmd.value)->value;
+
+	return rc;
+}
+
+static int msm_server_q_ctrl(struct msm_cam_v4l2_device *pcam,
+			struct v4l2_queryctrl *queryctrl)
+{
+	int rc = 0;
+	struct msm_ctrl_cmd ctrlcmd;
+	uint8_t ctrl_data[max_control_command_size];
+
+	WARN_ON(queryctrl == NULL);
+	memset(ctrl_data, 0, sizeof(ctrl_data));
+
+	ctrlcmd.type = MSM_V4L2_QUERY_CTRL;
+	ctrlcmd.length = sizeof(struct v4l2_queryctrl);
+	ctrlcmd.value = (void *)ctrl_data;
+	memcpy(ctrlcmd.value, queryctrl, ctrlcmd.length);
+	ctrlcmd.timeout_ms = 1000;
+	ctrlcmd.vnode_id = pcam->vnode_id;
+	ctrlcmd.queue_idx = pcam->server_queue_idx;
+	ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
+
+	/* send command to config thread in userspace, and get return value */
+	rc = msm_server_control(&g_server_dev, &ctrlcmd);
+	D("%s: rc = %d\n", __func__, rc);
+
+	if (rc >= 0)
+		memcpy(queryctrl, ctrlcmd.value, sizeof(struct v4l2_queryctrl));
+
+	return rc;
+}
+
+static int msm_server_get_fmt(struct msm_cam_v4l2_device *pcam,
+		 int idx, struct v4l2_format *pfmt)
+{
+	struct v4l2_pix_format *pix = &pfmt->fmt.pix;
+
+	pix->width        = pcam->dev_inst[idx]->vid_fmt.fmt.pix.width;
+	pix->height       = pcam->dev_inst[idx]->vid_fmt.fmt.pix.height;
+	pix->field        = pcam->dev_inst[idx]->vid_fmt.fmt.pix.field;
+	pix->pixelformat  = pcam->dev_inst[idx]->vid_fmt.fmt.pix.pixelformat;
+	pix->bytesperline = pcam->dev_inst[idx]->vid_fmt.fmt.pix.bytesperline;
+	pix->colorspace   = pcam->dev_inst[idx]->vid_fmt.fmt.pix.colorspace;
+	if (pix->bytesperline < 0)
+		return pix->bytesperline;
+
+	pix->sizeimage    = pix->height * pix->bytesperline;
+
+	return 0;
+}
+
+static int msm_server_get_fmt_mplane(struct msm_cam_v4l2_device *pcam,
+		 int idx, struct v4l2_format *pfmt)
+{
+	*pfmt = pcam->dev_inst[idx]->vid_fmt;
+	return 0;
+}
+
+static int msm_server_try_fmt(struct msm_cam_v4l2_device *pcam,
+				 struct v4l2_format *pfmt)
+{
+	int rc = 0;
+	int i = 0;
+	struct v4l2_pix_format *pix = &pfmt->fmt.pix;
+
+	D("%s: 0x%x\n", __func__, pix->pixelformat);
+	if (pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+		pr_err("%s: pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE!\n",
+							__func__);
+		return -EINVAL;
+	}
+
+	/* check if the format is supported by this host-sensor combo */
+	for (i = 0; i < pcam->num_fmts; i++) {
+		D("%s: usr_fmts.fourcc: 0x%x\n", __func__,
+			pcam->usr_fmts[i].fourcc);
+		if (pcam->usr_fmts[i].fourcc == pix->pixelformat)
+			break;
+	}
+
+	if (i == pcam->num_fmts) {
+		pr_err("%s: Format %x not found\n", __func__, pix->pixelformat);
+		return -EINVAL;
+	}
+	return rc;
+}
+
+static int msm_server_try_fmt_mplane(struct msm_cam_v4l2_device *pcam,
+				 struct v4l2_format *pfmt)
+{
+	int rc = 0;
+	int i = 0;
+	struct v4l2_pix_format_mplane *pix_mp = &pfmt->fmt.pix_mp;
+
+	D("%s: 0x%x\n", __func__, pix_mp->pixelformat);
+	if (pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		pr_err("%s: Incorrect format type %d ",
+			__func__, pfmt->type);
+		return -EINVAL;
+	}
+
+	/* check if the format is supported by this host-sensor combo */
+	for (i = 0; i < pcam->num_fmts; i++) {
+		D("%s: usr_fmts.fourcc: 0x%x\n", __func__,
+			pcam->usr_fmts[i].fourcc);
+		if (pcam->usr_fmts[i].fourcc == pix_mp->pixelformat)
+			break;
+	}
+
+	if (i == pcam->num_fmts) {
+		pr_err("%s: Format %x not found\n",
+			__func__, pix_mp->pixelformat);
+		return -EINVAL;
+	}
+	return rc;
+}
+
+static int msm_camera_get_crop(struct msm_cam_v4l2_device *pcam,
+				int idx, struct v4l2_crop *crop)
+{
+	int rc = 0;
+	struct msm_ctrl_cmd ctrlcmd;
+
+	BUG_ON(crop == NULL);
+
+	ctrlcmd.type = MSM_V4L2_GET_CROP;
+	ctrlcmd.length = sizeof(struct v4l2_crop);
+	ctrlcmd.value = (void *)crop;
+	ctrlcmd.timeout_ms = 1000;
+	ctrlcmd.vnode_id = pcam->vnode_id;
+	ctrlcmd.queue_idx = pcam->server_queue_idx;
+	ctrlcmd.stream_type = pcam->dev_inst[idx]->image_mode;
+	ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
+
+	/* send command to config thread in userspace, and get return value */
+	rc = msm_server_control(&g_server_dev, &ctrlcmd);
+	D("%s: rc = %d\n", __func__, rc);
+
+	return rc;
+}
+
+/*
+ *
+ * implementation of v4l2_ioctl_ops
+ *
+ */
+static int msm_camera_v4l2_querycap(struct file *f, void *pctx,
+				struct v4l2_capability *pcaps)
+{
+
+	D("%s\n", __func__);
+	WARN_ON(pctx != f->private_data);
+
+	/* some other day, some other time */
+	/*cap->version = LINUX_VERSION_CODE; */
+	pcaps->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+	return 0;
+}
+
+static int msm_camera_v4l2_queryctrl(struct file *f, void *pctx,
+				struct v4l2_queryctrl *pqctrl)
+{
+	int rc = 0;
+	struct msm_cam_v4l2_device *pcam  = video_drvdata(f);
+
+	D("%s\n", __func__);
+	WARN_ON(pctx != f->private_data);
+
+	mutex_lock(&pcam->vid_lock);
+	rc = msm_server_q_ctrl(pcam, pqctrl);
+	mutex_unlock(&pcam->vid_lock);
+	return rc;
+}
+
+static int msm_camera_v4l2_g_ctrl(struct file *f, void *pctx,
+					struct v4l2_control *c)
+{
+	int rc = 0;
+	struct msm_cam_v4l2_device *pcam  = video_drvdata(f);
+
+	D("%s\n", __func__);
+	WARN_ON(pctx != f->private_data);
+
+	mutex_lock(&pcam->vid_lock);
+	rc = msm_server_g_ctrl(pcam, c);
+	mutex_unlock(&pcam->vid_lock);
+
+	return rc;
+}
+
+static int msm_camera_v4l2_s_ctrl(struct file *f, void *pctx,
+					struct v4l2_control *ctrl)
+{
+	int rc = 0;
+	struct msm_cam_v4l2_device *pcam  = video_drvdata(f);
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	pcam_inst = container_of(f->private_data,
+		struct msm_cam_v4l2_dev_inst, eventHandle);
+
+	D("%s\n", __func__);
+
+	WARN_ON(pctx != f->private_data);
+	mutex_lock(&pcam->vid_lock);
+	switch (ctrl->id) {
+	case MSM_V4L2_PID_MMAP_INST:
+		D("%s: mmap_inst=(0x%p, %d)\n",
+			 __func__, pcam_inst, pcam_inst->my_index);
+		pcam_inst->is_mem_map_inst = 1;
+		break;
+	case MSM_V4L2_PID_MMAP_ENTRY:
+		if (copy_from_user(&pcam_inst->mem_map,
+			(void *)ctrl->value,
+			sizeof(struct msm_mem_map_info))) {
+			rc = -EFAULT;
+		} else
+			D("%s:mmap entry:cookie=0x%x,mem_type=%d,len=%d\n",
+				__func__, pcam_inst->mem_map.cookie,
+				pcam_inst->mem_map.mem_type,
+				pcam_inst->mem_map.length);
+		break;
+	default:
+		if (ctrl->id == MSM_V4L2_PID_CAM_MODE)
+			pcam->op_mode = ctrl->value;
+		rc = msm_server_s_ctrl(pcam, ctrl);
+		break;
+	}
+	mutex_unlock(&pcam->vid_lock);
+
+	return rc;
+}
+
+static int msm_camera_v4l2_reqbufs(struct file *f, void *pctx,
+				struct v4l2_requestbuffers *pb)
+{
+	int rc = 0, i, j;
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	pcam_inst = container_of(f->private_data,
+		struct msm_cam_v4l2_dev_inst, eventHandle);
+	D("%s\n", __func__);
+	WARN_ON(pctx != f->private_data);
+	rc = vb2_reqbufs(&pcam_inst->vid_bufq, pb);
+	if (rc < 0) {
+		pr_err("%s reqbufs failed %d ", __func__, rc);
+		return rc;
+	}
+	if (!pb->count) {
+		/* Deallocation. free buf_offset array */
+		D("%s Inst %p freeing buffer offsets array",
+			__func__, pcam_inst);
+		for (j = 0 ; j < pcam_inst->buf_count ; j++)
+			kfree(pcam_inst->buf_offset[j]);
+		kfree(pcam_inst->buf_offset);
+		pcam_inst->buf_offset = NULL;
+		/* If the userspace has deallocated all the
+		 * buffers, then release the vb2 queue */
+		if (pcam_inst->vbqueue_initialized) {
+			vb2_queue_release(&pcam_inst->vid_bufq);
+			pcam_inst->vbqueue_initialized = 0;
+		}
+	} else {
+		D("%s Inst %p Allocating buf_offset array",
+			__func__, pcam_inst);
+		/* Allocation. allocate buf_offset array */
+		pcam_inst->buf_offset = (struct msm_cam_buf_offset **)
+			kzalloc(pb->count * sizeof(struct msm_cam_buf_offset *),
+							GFP_KERNEL);
+		if (!pcam_inst->buf_offset) {
+			pr_err("%s out of memory ", __func__);
+			return -ENOMEM;
+		}
+		for (i = 0; i < pb->count; i++) {
+			pcam_inst->buf_offset[i] =
+				kzalloc(sizeof(struct msm_cam_buf_offset) *
+				pcam_inst->plane_info.num_planes, GFP_KERNEL);
+			if (!pcam_inst->buf_offset[i]) {
+				pr_err("%s out of memory ", __func__);
+				for (j = i-1 ; j >= 0; j--)
+					kfree(pcam_inst->buf_offset[j]);
+				kfree(pcam_inst->buf_offset);
+				pcam_inst->buf_offset = NULL;
+				return -ENOMEM;
+			}
+		}
+	}
+	pcam_inst->buf_count = pb->count;
+	return rc;
+}
+
+static int msm_camera_v4l2_querybuf(struct file *f, void *pctx,
+					struct v4l2_buffer *pb)
+{
+	/* get the video device */
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	pcam_inst = container_of(f->private_data,
+		struct msm_cam_v4l2_dev_inst, eventHandle);
+
+	D("%s\n", __func__);
+	WARN_ON(pctx != f->private_data);
+	return vb2_querybuf(&pcam_inst->vid_bufq, pb);
+}
+
+static int msm_camera_v4l2_qbuf(struct file *f, void *pctx,
+					struct v4l2_buffer *pb)
+{
+	int rc = 0, i = 0;
+	/* get the camera device */
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	pcam_inst = container_of(f->private_data,
+		struct msm_cam_v4l2_dev_inst, eventHandle);
+
+	D("%s Inst=%p, mode=%d, idx=%d\n", __func__, pcam_inst,
+		pcam_inst->image_mode, pb->index);
+	WARN_ON(pctx != f->private_data);
+
+	if (!pcam_inst->buf_offset) {
+		pr_err("%s Buffer is already released. Returning.\n", __func__);
+		return -EINVAL;
+	}
+
+	if (pb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		/* Reject the buffer if planes array was not allocated */
+		if (pb->m.planes == NULL) {
+			pr_err("%s Planes array is null\n", __func__);
+			return -EINVAL;
+		}
+		for (i = 0; i < pcam_inst->plane_info.num_planes; i++) {
+			D("%s stored offsets for plane %d as"
+				"addr offset %d, data offset %d\n",
+				__func__, i, pb->m.planes[i].reserved[0],
+				pb->m.planes[i].data_offset);
+			pcam_inst->buf_offset[pb->index][i].data_offset =
+				pb->m.planes[i].data_offset;
+			pcam_inst->buf_offset[pb->index][i].addr_offset =
+				pb->m.planes[i].reserved[0];
+		}
+	} else {
+		D("%s stored reserved info %d\n", __func__, pb->reserved);
+		pcam_inst->buf_offset[pb->index][0].addr_offset = pb->reserved;
+	}
+
+	rc = vb2_qbuf(&pcam_inst->vid_bufq, pb);
+	D("%s, videobuf_qbuf mode %d and idx %d returns %d\n", __func__,
+		pcam_inst->image_mode, pb->index, rc);
+
+	return rc;
+}
+
+static int msm_camera_v4l2_dqbuf(struct file *f, void *pctx,
+					struct v4l2_buffer *pb)
+{
+	int rc = 0, i = 0;
+	/* get the camera device */
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	pcam_inst = container_of(f->private_data,
+		struct msm_cam_v4l2_dev_inst, eventHandle);
+
+	D("%s\n", __func__);
+	WARN_ON(pctx != f->private_data);
+
+	rc = vb2_dqbuf(&pcam_inst->vid_bufq, pb,  f->f_flags & O_NONBLOCK);
+	D("%s, videobuf_dqbuf returns %d\n", __func__, rc);
+
+	if (pb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		/* Reject the buffer if planes array was not allocated */
+		if (pb->m.planes == NULL) {
+			pr_err("%s Planes array is null\n", __func__);
+			return -EINVAL;
+		}
+		for (i = 0; i < pcam_inst->plane_info.num_planes; i++) {
+			pb->m.planes[i].data_offset =
+				pcam_inst->buf_offset[pb->index][i].data_offset;
+			pb->m.planes[i].reserved[0] =
+				pcam_inst->buf_offset[pb->index][i].addr_offset;
+			D("%s stored offsets for plane %d as "
+				"addr offset %d, data offset %d\n",
+				__func__, i, pb->m.planes[i].reserved[0],
+				pb->m.planes[i].data_offset);
+		}
+	} else {
+		D("%s stored reserved info %d\n", __func__, pb->reserved);
+		pb->reserved = pcam_inst->buf_offset[pb->index][0].addr_offset;
+	}
+
+	return rc;
+}
+
+static int msm_camera_v4l2_streamon(struct file *f, void *pctx,
+					enum v4l2_buf_type buf_type)
+{
+	int rc = 0;
+	/* get the camera device */
+	struct msm_cam_v4l2_device *pcam  = video_drvdata(f);
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	pcam_inst = container_of(f->private_data,
+		struct msm_cam_v4l2_dev_inst, eventHandle);
+
+	D("%s Inst %p\n", __func__, pcam_inst);
+	WARN_ON(pctx != f->private_data);
+
+	if ((buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) &&
+		(buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) {
+		pr_err("%s Invalid buffer type ", __func__);
+		return -EINVAL;
+	}
+
+	D("%s Calling videobuf_streamon", __func__);
+	/* if HW streaming on is successful, start buffer streaming */
+	rc = vb2_streamon(&pcam_inst->vid_bufq, buf_type);
+	D("%s, videobuf_streamon returns %d\n", __func__, rc);
+
+	mutex_lock(&pcam->vid_lock);
+	/* turn HW (VFE/sensor) streaming */
+	pcam_inst->streamon = 1;
+	rc = msm_server_streamon(pcam, pcam_inst->my_index);
+	mutex_unlock(&pcam->vid_lock);
+	D("%s rc = %d\n", __func__, rc);
+	return rc;
+}
+
+static int msm_camera_v4l2_streamoff(struct file *f, void *pctx,
+					enum v4l2_buf_type buf_type)
+{
+	int rc = 0;
+	/* get the camera device */
+	struct msm_cam_v4l2_device *pcam  = video_drvdata(f);
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	pcam_inst = container_of(f->private_data,
+		struct msm_cam_v4l2_dev_inst, eventHandle);
+
+	D("%s Inst %p\n", __func__, pcam_inst);
+	WARN_ON(pctx != f->private_data);
+
+	if ((buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) &&
+		(buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) {
+		pr_err("%s Invalid buffer type ", __func__);
+		return -EINVAL;
+	}
+
+	/* first turn of HW (VFE/sensor) streaming so that buffers are
+		not in use when we free the buffers */
+	mutex_lock(&pcam->vid_lock);
+	pcam_inst->streamon = 0;
+	if (g_server_dev.use_count > 0)
+		rc = msm_server_streamoff(pcam, pcam_inst->my_index);
+	mutex_unlock(&pcam->vid_lock);
+	if (rc < 0)
+		pr_err("%s: hw failed to stop streaming\n", __func__);
+
+	/* stop buffer streaming */
+	rc = vb2_streamoff(&pcam_inst->vid_bufq, buf_type);
+	D("%s, videobuf_streamoff returns %d\n", __func__, rc);
+	return rc;
+}
+
+static int msm_camera_v4l2_enum_fmt_cap(struct file *f, void *pctx,
+					struct v4l2_fmtdesc *pfmtdesc)
+{
+	/* get the video device */
+	struct msm_cam_v4l2_device *pcam  = video_drvdata(f);
+	const struct msm_isp_color_fmt *isp_fmt;
+
+	D("%s\n", __func__);
+	WARN_ON(pctx != f->private_data);
+	if ((pfmtdesc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) &&
+		(pfmtdesc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE))
+		return -EINVAL;
+
+	if (pfmtdesc->index >= pcam->num_fmts)
+		return -EINVAL;
+
+	isp_fmt = &pcam->usr_fmts[pfmtdesc->index];
+
+	if (isp_fmt->name)
+		strlcpy(pfmtdesc->description, isp_fmt->name,
+						sizeof(pfmtdesc->description));
+
+	pfmtdesc->pixelformat = isp_fmt->fourcc;
+
+	D("%s: [%d] 0x%x, %s\n", __func__, pfmtdesc->index,
+		isp_fmt->fourcc, isp_fmt->name);
+	return 0;
+}
+
+static int msm_camera_v4l2_g_fmt_cap(struct file *f,
+		void *pctx, struct v4l2_format *pfmt)
+{
+	int rc = 0;
+	/* get the video device */
+	struct msm_cam_v4l2_device *pcam  = video_drvdata(f);
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	pcam_inst = container_of(f->private_data,
+		struct msm_cam_v4l2_dev_inst, eventHandle);
+
+	D("%s\n", __func__);
+	WARN_ON(pctx != f->private_data);
+
+	if (pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	rc = msm_server_get_fmt(pcam, pcam_inst->my_index, pfmt);
+	D("%s: current_fmt->fourcc: 0x%08x, rc = %d\n", __func__,
+				pfmt->fmt.pix.pixelformat, rc);
+	return rc;
+}
+
+static int msm_camera_v4l2_g_fmt_cap_mplane(struct file *f,
+		void *pctx, struct v4l2_format *pfmt)
+{
+	int rc = 0;
+	/* get the video device */
+	struct msm_cam_v4l2_device *pcam  = video_drvdata(f);
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	pcam_inst = container_of(f->private_data,
+		struct msm_cam_v4l2_dev_inst, eventHandle);
+
+	D("%s\n", __func__);
+	WARN_ON(pctx != f->private_data);
+
+	if (pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		return -EINVAL;
+
+	rc = msm_server_get_fmt_mplane(pcam, pcam_inst->my_index, pfmt);
+	D("%s: current_fmt->fourcc: 0x%08x, rc = %d\n", __func__,
+					pfmt->fmt.pix_mp.pixelformat, rc);
+	return rc;
+}
+
+/* This function will readjust the format parameters based in HW
+  capabilities. Called by s_fmt_cap
+*/
+static int msm_camera_v4l2_try_fmt_cap(struct file *f, void *pctx,
+					struct v4l2_format *pfmt)
+{
+	int rc = 0;
+	/* get the video device */
+	struct msm_cam_v4l2_device *pcam  = video_drvdata(f);
+
+	D("%s\n", __func__);
+	WARN_ON(pctx != f->private_data);
+
+	rc = msm_server_try_fmt(pcam, pfmt);
+	if (rc)
+		pr_err("Format %x not found, rc = %d\n",
+				pfmt->fmt.pix.pixelformat, rc);
+
+	return rc;
+}
+
+static int msm_camera_v4l2_try_fmt_cap_mplane(struct file *f, void *pctx,
+					struct v4l2_format *pfmt)
+{
+	int rc = 0;
+	/* get the video device */
+	struct msm_cam_v4l2_device *pcam  = video_drvdata(f);
+
+	D("%s\n", __func__);
+	WARN_ON(pctx != f->private_data);
+
+	rc = msm_server_try_fmt_mplane(pcam, pfmt);
+	if (rc)
+		pr_err("Format %x not found, rc = %d\n",
+				pfmt->fmt.pix_mp.pixelformat, rc);
+
+	return rc;
+}
+
+/* This function will reconfig the v4l2 driver and HW device, it should be
+   called after the streaming is stopped.
+*/
+static int msm_camera_v4l2_s_fmt_cap(struct file *f, void *pctx,
+					struct v4l2_format *pfmt)
+{
+	int rc;
+	/* get the video device */
+	struct msm_cam_v4l2_device *pcam  = video_drvdata(f);
+	struct msm_cam_media_controller *pmctl;
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	pcam_inst = container_of(f->private_data,
+		struct msm_cam_v4l2_dev_inst, eventHandle);
+
+	D("%s\n", __func__);
+	D("%s, inst=0x%x,idx=%d,priv = 0x%p\n",
+		__func__, (u32)pcam_inst, pcam_inst->my_index,
+		(void *)pfmt->fmt.pix.priv);
+	WARN_ON(pctx != f->private_data);
+
+	pmctl = msm_camera_get_mctl(pcam->mctl_handle);
+	if (pmctl == NULL)
+		return -EINVAL;
+
+	if (!pcam_inst->vbqueue_initialized) {
+		pmctl->mctl_vbqueue_init(pcam_inst, &pcam_inst->vid_bufq,
+					V4L2_BUF_TYPE_VIDEO_CAPTURE);
+		pcam_inst->vbqueue_initialized = 1;
+	}
+
+	mutex_lock(&pcam->vid_lock);
+
+	rc = msm_server_set_fmt(pcam, pcam_inst->my_index, pfmt);
+	if (rc < 0) {
+		pr_err("%s: msm_server_set_fmt Error: %d\n",
+				__func__, rc);
+	}
+	mutex_unlock(&pcam->vid_lock);
+
+	return rc;
+}
+
+static int msm_camera_v4l2_s_fmt_cap_mplane(struct file *f, void *pctx,
+				struct v4l2_format *pfmt)
+{
+	int rc;
+	struct msm_cam_v4l2_device *pcam = video_drvdata(f);
+	struct msm_cam_media_controller *pmctl;
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	pcam_inst = container_of(f->private_data,
+			struct msm_cam_v4l2_dev_inst, eventHandle);
+
+	D("%s Inst %p\n", __func__, pcam_inst);
+	WARN_ON(pctx != f->private_data);
+
+	pmctl = msm_camera_get_mctl(pcam->mctl_handle);
+	if (pmctl == NULL)
+		return -EINVAL;
+
+	if (!pcam_inst->vbqueue_initialized) {
+		pmctl->mctl_vbqueue_init(pcam_inst, &pcam_inst->vid_bufq,
+					V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+		pcam_inst->vbqueue_initialized = 1;
+	}
+
+	mutex_lock(&pcam->vid_lock);
+	rc = msm_server_set_fmt_mplane(pcam, pcam_inst->my_index, pfmt);
+	mutex_unlock(&pcam->vid_lock);
+
+	return rc;
+}
+static int msm_camera_v4l2_g_jpegcomp(struct file *f, void *pctx,
+				struct v4l2_jpegcompression *pcomp)
+{
+	int rc = -EINVAL;
+
+	D("%s\n", __func__);
+	WARN_ON(pctx != f->private_data);
+
+	return rc;
+}
+
+static int msm_camera_v4l2_s_jpegcomp(struct file *f, void *pctx,
+				struct v4l2_jpegcompression *pcomp)
+{
+	int rc = -EINVAL;
+
+	D("%s\n", __func__);
+	WARN_ON(pctx != f->private_data);
+
+	return rc;
+}
+
+
+static int msm_camera_v4l2_g_crop(struct file *f, void *pctx,
+					struct v4l2_crop *crop)
+{
+	int rc = -EINVAL;
+	struct msm_cam_v4l2_device *pcam  = video_drvdata(f);
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+
+	pcam_inst = container_of(f->private_data,
+		struct msm_cam_v4l2_dev_inst, eventHandle);
+
+	D("%s\n", __func__);
+	WARN_ON(pctx != f->private_data);
+
+	mutex_lock(&pcam->vid_lock);
+	rc = msm_camera_get_crop(pcam, pcam_inst->my_index, crop);
+	mutex_unlock(&pcam->vid_lock);
+	return rc;
+}
+
+static int msm_camera_v4l2_s_crop(struct file *f, void *pctx,
+					struct v4l2_crop *a)
+{
+	int rc = -EINVAL;
+
+	D("%s\n", __func__);
+	WARN_ON(pctx != f->private_data);
+
+	return rc;
+}
+
+/* Stream type-dependent parameter ioctls */
+static int msm_camera_v4l2_g_parm(struct file *f, void *pctx,
+				struct v4l2_streamparm *a)
+{
+	int rc = -EINVAL;
+	return rc;
+}
+static int msm_vidbuf_get_path(u32 extendedmode)
+{
+	switch (extendedmode) {
+	case MSM_V4L2_EXT_CAPTURE_MODE_THUMBNAIL:
+		return OUTPUT_TYPE_T;
+	case MSM_V4L2_EXT_CAPTURE_MODE_MAIN:
+		return OUTPUT_TYPE_S;
+	case MSM_V4L2_EXT_CAPTURE_MODE_VIDEO:
+		return OUTPUT_TYPE_V;
+	case MSM_V4L2_EXT_CAPTURE_MODE_DEFAULT:
+	case MSM_V4L2_EXT_CAPTURE_MODE_PREVIEW:
+	default:
+		return OUTPUT_TYPE_P;
+	}
+}
+
+static int msm_camera_v4l2_s_parm(struct file *f, void *pctx,
+				struct v4l2_streamparm *a)
+{
+	int rc = 0;
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	pcam_inst = container_of(f->private_data,
+		struct msm_cam_v4l2_dev_inst, eventHandle);
+	pcam_inst->image_mode = a->parm.capture.extendedmode;
+	pcam_inst->pcam->dev_inst_map[pcam_inst->image_mode] = pcam_inst;
+	pcam_inst->path = msm_vidbuf_get_path(pcam_inst->image_mode);
+	D("%spath=%d,rc=%d\n", __func__,
+		pcam_inst->path, rc);
+	return rc;
+}
+
+static int msm_camera_v4l2_subscribe_event(struct v4l2_fh *fh,
+			struct v4l2_event_subscription *sub)
+{
+	int rc = 0;
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	pcam_inst =
+		(struct msm_cam_v4l2_dev_inst *)container_of(fh,
+		struct msm_cam_v4l2_dev_inst, eventHandle);
+
+	D("%s:fh = 0x%x, type = 0x%x\n", __func__, (u32)fh, sub->type);
+	if (pcam_inst->my_index != 0)
+		return -EINVAL;
+	if (sub->type == V4L2_EVENT_ALL)
+		sub->type = V4L2_EVENT_PRIVATE_START+MSM_CAM_APP_NOTIFY_EVENT;
+	rc = v4l2_event_subscribe(fh, sub, 30);
+	if (rc < 0)
+		D("%s: failed for evtType = 0x%x, rc = %d\n",
+						__func__, sub->type, rc);
+	return rc;
+}
+
+static int msm_camera_v4l2_unsubscribe_event(struct v4l2_fh *fh,
+			struct v4l2_event_subscription *sub)
+{
+	int rc = 0;
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	pcam_inst =
+		(struct msm_cam_v4l2_dev_inst *)container_of(fh,
+		struct msm_cam_v4l2_dev_inst, eventHandle);
+
+	D("%s: fh = 0x%x\n", __func__, (u32)fh);
+	if (pcam_inst->my_index != 0)
+		return -EINVAL;
+
+	rc = v4l2_event_unsubscribe(fh, sub);
+	D("%s: rc = %d\n", __func__, rc);
+	return rc;
+}
+
+static int msm_server_v4l2_subscribe_event(struct v4l2_fh *fh,
+			struct v4l2_event_subscription *sub)
+{
+	int rc = 0;
+
+	D("%s: fh = 0x%x, type = 0x%x", __func__, (u32)fh, sub->type);
+	if (sub->type == V4L2_EVENT_ALL) {
+		/*sub->type = MSM_ISP_EVENT_START;*/
+		sub->type = V4L2_EVENT_PRIVATE_START + MSM_CAM_RESP_CTRL;
+		D("sub->type start = 0x%x\n", sub->type);
+		do {
+			rc = v4l2_event_subscribe(fh, sub, 30);
+			if (rc < 0) {
+				D("%s: failed for evtType = 0x%x, rc = %d\n",
+						__func__, sub->type, rc);
+			/* unsubscribe all events here and return */
+			sub->type = V4L2_EVENT_ALL;
+			v4l2_event_unsubscribe(fh, sub);
+			return rc;
+			} else
+				D("%s: subscribed evtType = 0x%x, rc = %d\n",
+						__func__, sub->type, rc);
+			sub->type++;
+			D("sub->type while = 0x%x\n", sub->type);
+		} while (sub->type !=
+			V4L2_EVENT_PRIVATE_START + MSM_SVR_RESP_MAX);
+	} else {
+		D("sub->type not V4L2_EVENT_ALL = 0x%x\n", sub->type);
+		rc = v4l2_event_subscribe(fh, sub, 30);
+		if (rc < 0)
+			D("%s: failed for evtType = 0x%x, rc = %d\n",
+						__func__, sub->type, rc);
+	}
+
+	D("%s: rc = %d\n", __func__, rc);
+	return rc;
+}
+
+static int msm_server_v4l2_unsubscribe_event(struct v4l2_fh *fh,
+			struct v4l2_event_subscription *sub)
+{
+	int rc = 0;
+
+	D("%s: fh = 0x%x\n", __func__, (u32)fh);
+	rc = v4l2_event_unsubscribe(fh, sub);
+	D("%s: rc = %d\n", __func__, rc);
+	return rc;
+}
+
+/* v4l2_ioctl_ops */
+static const struct v4l2_ioctl_ops g_msm_ioctl_ops = {
+	.vidioc_querycap = msm_camera_v4l2_querycap,
+
+	.vidioc_s_crop = msm_camera_v4l2_s_crop,
+	.vidioc_g_crop = msm_camera_v4l2_g_crop,
+
+	.vidioc_queryctrl = msm_camera_v4l2_queryctrl,
+	.vidioc_g_ctrl = msm_camera_v4l2_g_ctrl,
+	.vidioc_s_ctrl = msm_camera_v4l2_s_ctrl,
+
+	.vidioc_reqbufs = msm_camera_v4l2_reqbufs,
+	.vidioc_querybuf = msm_camera_v4l2_querybuf,
+	.vidioc_qbuf = msm_camera_v4l2_qbuf,
+	.vidioc_dqbuf = msm_camera_v4l2_dqbuf,
+
+	.vidioc_streamon = msm_camera_v4l2_streamon,
+	.vidioc_streamoff = msm_camera_v4l2_streamoff,
+
+	/* format ioctls */
+	.vidioc_enum_fmt_vid_cap = msm_camera_v4l2_enum_fmt_cap,
+	.vidioc_enum_fmt_vid_cap_mplane = msm_camera_v4l2_enum_fmt_cap,
+	.vidioc_try_fmt_vid_cap = msm_camera_v4l2_try_fmt_cap,
+	.vidioc_try_fmt_vid_cap_mplane = msm_camera_v4l2_try_fmt_cap_mplane,
+	.vidioc_g_fmt_vid_cap = msm_camera_v4l2_g_fmt_cap,
+	.vidioc_g_fmt_vid_cap_mplane = msm_camera_v4l2_g_fmt_cap_mplane,
+	.vidioc_s_fmt_vid_cap = msm_camera_v4l2_s_fmt_cap,
+	.vidioc_s_fmt_vid_cap_mplane = msm_camera_v4l2_s_fmt_cap_mplane,
+
+	.vidioc_g_jpegcomp = msm_camera_v4l2_g_jpegcomp,
+	.vidioc_s_jpegcomp = msm_camera_v4l2_s_jpegcomp,
+
+	/* Stream type-dependent parameter ioctls */
+	.vidioc_g_parm =  msm_camera_v4l2_g_parm,
+	.vidioc_s_parm =  msm_camera_v4l2_s_parm,
+
+	/* event subscribe/unsubscribe */
+	.vidioc_subscribe_event = msm_camera_v4l2_subscribe_event,
+	.vidioc_unsubscribe_event = msm_camera_v4l2_unsubscribe_event,
+};
+
+/* open an active camera session to manage the streaming logic */
+static int msm_cam_server_open_session(struct msm_cam_server_dev *ps,
+	struct msm_cam_v4l2_device *pcam)
+{
+	int rc = 0;
+	struct msm_cam_media_controller *pmctl;
+
+	D("%s\n", __func__);
+
+	if (!ps || !pcam) {
+		pr_err("%s NULL pointer passed in!\n", __func__);
+		return rc;
+	}
+
+	/* The number of camera instance should be controlled by the
+		resource manager. Currently supporting one active instance
+		until multiple instances are supported */
+	if (atomic_read(&ps->number_pcam_active) > 0) {
+		pr_err("%s Cannot have more than one active camera %d\n",
+			__func__, atomic_read(&ps->number_pcam_active));
+		return -EINVAL;
+	}
+	/* book keeping this camera session*/
+	ps->pcam_active = pcam;
+	atomic_inc(&ps->number_pcam_active);
+
+	D("config pcam = 0x%p\n", ps->pcam_active);
+
+	/* initialization the media controller module*/
+	msm_mctl_init(pcam);
+
+	/*for single VFE msms (8660, 8960v1), just populate the session
+	with our VFE devices that registered*/
+	pmctl = msm_camera_get_mctl(pcam->mctl_handle);
+	pmctl->axi_sdev = ps->axi_device[0];
+	pmctl->isp_sdev = ps->isp_subdev[0];
+	return rc;
+}
+
+/* close an active camera session to server */
+static int msm_cam_server_close_session(struct msm_cam_server_dev *ps,
+	struct msm_cam_v4l2_device *pcam)
+{
+	int rc = 0;
+	D("%s\n", __func__);
+
+	if (!ps || !pcam) {
+		D("%s NULL pointer passed in!\n", __func__);
+		return rc;
+	}
+
+
+	atomic_dec(&ps->number_pcam_active);
+	ps->pcam_active = NULL;
+
+	msm_mctl_free(pcam);
+	return rc;
+}
+
+int msm_server_open_client(int *p_qidx)
+{
+	int rc = 0;
+	int server_q_idx = 0;
+	struct msm_cam_server_queue *queue = NULL;
+
+	mutex_lock(&g_server_dev.server_lock);
+	server_q_idx = msm_find_free_queue();
+	if (server_q_idx < 0) {
+		mutex_unlock(&g_server_dev.server_lock);
+		return server_q_idx;
+	}
+
+	*p_qidx = server_q_idx;
+	queue = &g_server_dev.server_queue[server_q_idx];
+	queue->ctrl = NULL;
+	queue->ctrl_data = kzalloc(sizeof(uint8_t) *
+		max_control_command_size, GFP_KERNEL);
+	msm_queue_init(&queue->ctrl_q, "control");
+	msm_queue_init(&queue->eventData_q, "eventdata");
+	queue->queue_active = 1;
+	mutex_unlock(&g_server_dev.server_lock);
+	return rc;
+}
+
+int msm_server_send_ctrl(struct msm_ctrl_cmd *out,
+	int ctrl_id)
+{
+	int rc = 0;
+	void *value;
+	struct msm_queue_cmd *rcmd;
+	struct msm_queue_cmd *event_qcmd;
+	struct msm_ctrl_cmd *ctrlcmd;
+	struct msm_cam_server_dev *server_dev = &g_server_dev;
+	struct msm_device_queue *queue =
+		&server_dev->server_queue[out->queue_idx].ctrl_q;
+
+	struct v4l2_event v4l2_evt;
+	struct msm_isp_event_ctrl *isp_event;
+	isp_event = kzalloc(sizeof(struct msm_isp_event_ctrl), GFP_KERNEL);
+	if (!isp_event) {
+		pr_err("%s Insufficient memory. return", __func__);
+		return -ENOMEM;
+	}
+	event_qcmd = kzalloc(sizeof(struct msm_queue_cmd), GFP_KERNEL);
+	if (!event_qcmd) {
+		pr_err("%s Insufficient memory. return", __func__);
+		kfree(isp_event);
+		return -ENOMEM;
+	}
+
+	D("%s\n", __func__);
+	mutex_lock(&server_dev->server_queue_lock);
+	if (++server_dev->server_evt_id == 0)
+		server_dev->server_evt_id++;
+
+	D("%s qid %d evtid %d\n", __func__, out->queue_idx,
+		server_dev->server_evt_id);
+	server_dev->server_queue[out->queue_idx].evt_id =
+		server_dev->server_evt_id;
+	v4l2_evt.type = V4L2_EVENT_PRIVATE_START + ctrl_id;
+	v4l2_evt.id = 0;
+	v4l2_evt.u.data[0] = out->queue_idx;
+	/* setup event object to transfer the command; */
+	isp_event->resptype = MSM_CAM_RESP_V4L2;
+	isp_event->isp_data.ctrl = *out;
+	isp_event->isp_data.ctrl.evt_id = server_dev->server_evt_id;
+
+	atomic_set(&event_qcmd->on_heap, 1);
+	event_qcmd->command = isp_event;
+
+	msm_enqueue(&server_dev->server_queue[out->queue_idx].eventData_q,
+				&event_qcmd->list_eventdata);
+
+	/* now send command to config thread in userspace,
+	 * and wait for results */
+	v4l2_event_queue(server_dev->server_command_queue.pvdev,
+					  &v4l2_evt);
+	D("%s v4l2_event_queue: type = 0x%x\n", __func__, v4l2_evt.type);
+	mutex_unlock(&server_dev->server_queue_lock);
+
+	/* wait for config return status */
+	D("Waiting for config status\n");
+	rc = wait_event_interruptible_timeout(queue->wait,
+		!list_empty_careful(&queue->list),
+		msecs_to_jiffies(out->timeout_ms));
+	D("Waiting is over for config status\n");
+	if (list_empty_careful(&queue->list)) {
+		if (!rc)
+			rc = -ETIMEDOUT;
+		if (rc < 0) {
+			kfree(isp_event);
+			pr_err("%s: wait_event error %d\n", __func__, rc);
+			return rc;
+		}
+	}
+
+	rcmd = msm_dequeue(queue, list_control);
+	BUG_ON(!rcmd);
+	D("%s Finished servicing ioctl\n", __func__);
+
+	ctrlcmd = (struct msm_ctrl_cmd *)(rcmd->command);
+	value = out->value;
+	if (ctrlcmd->length > 0)
+		memcpy(value, ctrlcmd->value, ctrlcmd->length);
+
+	memcpy(out, ctrlcmd, sizeof(struct msm_ctrl_cmd));
+	out->value = value;
+
+	kfree(ctrlcmd);
+	server_dev->server_queue[out->queue_idx].ctrl = NULL;
+
+	free_qcmd(rcmd);
+	kfree(isp_event);
+	D("%s: rc %d\n", __func__, rc);
+	/* rc is the time elapsed. */
+	if (rc >= 0) {
+		/* TODO: Refactor msm_ctrl_cmd::status field */
+		if (out->status == 0)
+			rc = -1;
+		else if (out->status == 1 || out->status == 4)
+			rc = 0;
+		else
+			rc = -EINVAL;
+	}
+	return rc;
+}
+
+int msm_server_close_client(int idx)
+{
+	int rc = 0;
+	struct msm_cam_server_queue *queue = NULL;
+	mutex_lock(&g_server_dev.server_lock);
+	queue = &g_server_dev.server_queue[idx];
+	queue->queue_active = 0;
+	kfree(queue->ctrl);
+	queue->ctrl = NULL;
+	kfree(queue->ctrl_data);
+	queue->ctrl_data = NULL;
+	msm_queue_drain(&queue->ctrl_q, list_control);
+	msm_drain_eventq(&queue->eventData_q);
+	mutex_unlock(&g_server_dev.server_lock);
+	return rc;
+}
+/* v4l2_file_operations */
+static int msm_open(struct file *f)
+{
+	int i;
+	int rc = -EINVAL;
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+	int ion_client_created = 0;
+#endif
+	int server_q_idx = 0;
+	/*struct msm_isp_ops *p_isp = 0;*/
+	/* get the video device */
+	struct msm_cam_v4l2_device *pcam  = video_drvdata(f);
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	struct msm_cam_media_controller *pmctl = NULL;
+	struct msm_cam_server_queue *queue = NULL;
+
+	D("%s\n", __func__);
+
+	if (!pcam) {
+		pr_err("%s NULL pointer passed in!\n", __func__);
+		return rc;
+	}
+	if (!g_server_dev.use_count) {
+		pr_err("%s: error, daemon not yet started.", __func__);
+		return -EINVAL;
+	}
+	mutex_lock(&pcam->vid_lock);
+	for (i = 0; i < MSM_DEV_INST_MAX; i++) {
+		if (pcam->dev_inst[i] == NULL)
+			break;
+	}
+
+	server_q_idx = msm_find_free_queue();
+	if (server_q_idx < 0)
+		return server_q_idx;
+
+	/* if no instance is available, return error */
+	if (i == MSM_DEV_INST_MAX) {
+		mutex_unlock(&pcam->vid_lock);
+		return rc;
+	}
+	pcam_inst = kzalloc(sizeof(struct msm_cam_v4l2_dev_inst), GFP_KERNEL);
+	if (!pcam_inst) {
+		mutex_unlock(&pcam->vid_lock);
+		return rc;
+	}
+	pcam_inst->sensor_pxlcode = pcam->usr_fmts[0].pxlcode;
+	pcam_inst->my_index = i;
+	pcam_inst->pcam = pcam;
+	pcam->dev_inst[i] = pcam_inst;
+
+	D("%s index %d nodeid %d count %d\n", __func__,
+			pcam_inst->my_index,
+			pcam->vnode_id, pcam->use_count);
+	pcam->use_count++;
+	D("%s use_count %d\n", __func__, pcam->use_count);
+	if (pcam->use_count == 1) {
+		int ges_evt = MSM_V4L2_GES_CAM_OPEN;
+		pcam->server_queue_idx = server_q_idx;
+		queue = &g_server_dev.server_queue[server_q_idx];
+		queue->ctrl = NULL;
+		queue->ctrl_data = kzalloc(sizeof(uint8_t) *
+			max_control_command_size, GFP_KERNEL);
+		msm_queue_init(&queue->ctrl_q, "control");
+		msm_queue_init(&queue->eventData_q, "eventdata");
+		queue->queue_active = 1;
+
+		pr_err("%s send gesture evt\n", __func__);
+		msm_cam_server_subdev_notify(g_server_dev.gesture_device,
+			NOTIFY_GESTURE_CAM_EVT, &ges_evt);
+
+		rc = msm_cam_server_open_session(&g_server_dev, pcam);
+		if (rc < 0) {
+			pr_err("%s: cam_server_open_session failed %d\n",
+			__func__, rc);
+			goto msm_cam_server_open_session_failed;
+		}
+
+		pmctl = msm_camera_get_mctl(pcam->mctl_handle);
+
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+		pmctl->client = msm_ion_client_create(-1, "camera");
+		kref_init(&pmctl->refcount);
+		ion_client_created = 1;
+#endif
+
+		/* Should be set to sensor ops if any but right now its OK!! */
+		if (!pmctl->mctl_open) {
+			D("%s: media contoller is not inited\n", __func__);
+			rc = -ENODEV;
+			goto mctl_open_failed;
+		}
+
+		/* Now we really have to activate the camera */
+		D("%s: call mctl_open\n", __func__);
+		rc = pmctl->mctl_open(pmctl, MSM_APPS_ID_V4L2);
+		if (rc < 0) {
+			pr_err("%s: HW open failed rc = 0x%x\n",  __func__, rc);
+			goto mctl_open_failed;
+		}
+		pmctl->pcam_ptr = pcam;
+
+		rc = msm_setup_v4l2_event_queue(&pcam_inst->eventHandle,
+			pcam->pvdev);
+		if (rc < 0) {
+			pr_err("%s: msm_setup_v4l2_event_queue failed %d",
+				__func__, rc);
+			goto mctl_event_q_setup_failed;
+		}
+	}
+	pcam_inst->vbqueue_initialized = 0;
+	rc = 0;
+
+	f->private_data = &pcam_inst->eventHandle;
+
+	D("f->private_data = 0x%x, pcam = 0x%x\n",
+		(u32)f->private_data, (u32)pcam_inst);
+
+	if (pcam->use_count == 1) {
+		rc = msm_send_open_server(pcam);
+		if (rc < 0) {
+			pr_err("%s: msm_send_open_server failed %d\n",
+				__func__, rc);
+			goto msm_send_open_server_failed;
+		}
+	}
+	mutex_unlock(&pcam->vid_lock);
+	D("%s: end", __func__);
+	return rc;
+
+msm_send_open_server_failed:
+	v4l2_fh_del(&pcam_inst->eventHandle);
+	v4l2_fh_exit(&pcam_inst->eventHandle);
+mctl_event_q_setup_failed:
+	if (pmctl->mctl_release)
+		if (pmctl->mctl_release(pmctl) < 0)
+			pr_err("%s: mctl_release failed\n", __func__);
+mctl_open_failed:
+	if (pcam->use_count == 1) {
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+		if (ion_client_created) {
+			pr_err("%s: destroy ion client", __func__);
+			kref_put(&pmctl->refcount, msm_release_ion_client);
+		}
+#endif
+		if (msm_cam_server_close_session(&g_server_dev, pcam) < 0)
+			pr_err("%s: msm_cam_server_close_session failed\n",
+				__func__);
+	}
+msm_cam_server_open_session_failed:
+	if (pcam->use_count == 1) {
+		if (queue != NULL) {
+			queue->queue_active = 0;
+			msm_drain_eventq(&queue->eventData_q);
+			kfree(queue->ctrl_data);
+			queue->ctrl_data = NULL;
+			msm_queue_drain(&queue->ctrl_q, list_control);
+			msm_drain_eventq(&queue->eventData_q);
+			queue = NULL;
+		}
+		pcam->dev_inst[i] = NULL;
+		pcam->use_count = 0;
+	}
+	mutex_unlock(&pcam->vid_lock);
+	kfree(pcam_inst);
+	pr_err("%s: error end", __func__);
+	return rc;
+}
+
+int msm_cam_server_close_mctl_session(struct msm_cam_v4l2_device *pcam)
+{
+	int rc = 0;
+	struct msm_cam_media_controller *pmctl = NULL;
+
+	pmctl = msm_camera_get_mctl(pcam->mctl_handle);
+	if (!pmctl) {
+		D("%s: invalid handle\n", __func__);
+		return -ENODEV;
+	}
+
+	if (pmctl->mctl_release) {
+		rc = pmctl->mctl_release(pmctl);
+		if (rc < 0)
+			pr_err("mctl_release fails %d\n", rc);
+	}
+
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+	kref_put(&pmctl->refcount, msm_release_ion_client);
+#endif
+
+	rc = msm_cam_server_close_session(&g_server_dev, pcam);
+	if (rc < 0)
+		pr_err("msm_cam_server_close_session fails %d\n", rc);
+
+	return rc;
+}
+
+int msm_cam_server_open_mctl_session(struct msm_cam_v4l2_device *pcam,
+	int *p_active)
+{
+	int rc = 0;
+	struct msm_cam_media_controller *pmctl = NULL;
+	D("%s: %p", __func__, g_server_dev.pcam_active);
+	*p_active = 0;
+	if (g_server_dev.pcam_active) {
+		D("%s: Active camera present return", __func__);
+		return 0;
+	}
+	rc = msm_cam_server_open_session(&g_server_dev, pcam);
+	if (rc < 0) {
+		pr_err("%s: cam_server_open_session failed %d\n",
+		__func__, rc);
+		return rc;
+	}
+
+	pmctl = msm_camera_get_mctl(pcam->mctl_handle);
+	/* Should be set to sensor ops if any but right now its OK!! */
+	if (!pmctl->mctl_open) {
+		D("%s: media contoller is not inited\n",
+			 __func__);
+		rc = -ENODEV;
+		return rc;
+	}
+
+	D("%s: call mctl_open\n", __func__);
+	rc = pmctl->mctl_open(pmctl, MSM_APPS_ID_V4L2);
+
+	if (rc < 0) {
+		pr_err("%s: HW open failed rc = 0x%x\n",  __func__, rc);
+		return rc;
+	}
+	pmctl->pcam_ptr = pcam;
+	*p_active = 1;
+	return rc;
+}
+
+static int msm_addr_remap(struct msm_cam_v4l2_dev_inst *pcam_inst,
+				struct vm_area_struct *vma)
+{
+	int phyaddr;
+	int retval;
+	unsigned long size;
+	int rc = 0;
+	struct msm_cam_media_controller *mctl;
+
+	mctl = msm_camera_get_mctl(pcam_inst->pcam->mctl_handle);
+	if (!mctl) {
+		pr_err("%s: invalid mctl pointer", __func__);
+		return -EFAULT;
+	}
+
+	rc = msm_pmem_region_get_phy_addr(&mctl->stats_info.pmem_stats_list,
+			&pcam_inst->mem_map,
+			&phyaddr);
+	if (rc) {
+		pr_err("%s: cannot map vaddr", __func__);
+		return -EFAULT;
+	}
+	size = vma->vm_end - vma->vm_start;
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+	retval = remap_pfn_range(vma, vma->vm_start,
+			phyaddr >> PAGE_SHIFT,
+			size, vma->vm_page_prot);
+	if (retval) {
+		pr_err("%s:mmap: remap failed with error %d. ",
+			   __func__, retval);
+		memset(&pcam_inst->mem_map, 0, sizeof(pcam_inst->mem_map));
+		return -ENOMEM;
+	}
+	D("%s:mmap: phy_addr=0x%x: %08lx-%08lx, pgoff %08lx\n",
+		   __func__, (uint32_t)phyaddr,
+		   vma->vm_start, vma->vm_end, vma->vm_pgoff);
+	memset(&pcam_inst->mem_map, 0, sizeof(pcam_inst->mem_map));
+	return 0;
+}
+
+static int msm_mmap(struct file *f, struct vm_area_struct *vma)
+{
+	int rc = 0;
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	pcam_inst = container_of(f->private_data,
+		struct msm_cam_v4l2_dev_inst, eventHandle);
+
+	D("mmap called, vma=0x%08lx\n", (unsigned long)vma);
+
+	if (pcam_inst->is_mem_map_inst &&
+		pcam_inst->mem_map.cookie) {
+		rc = msm_addr_remap(pcam_inst, vma);
+		D("%s: msm_addr_remap ret=%d\n", __func__, rc);
+		return rc;
+	} else
+		rc = vb2_mmap(&pcam_inst->vid_bufq, vma);
+	D("vma start=0x%08lx, size=%ld, ret=%d\n",
+		(unsigned long)vma->vm_start,
+		(unsigned long)vma->vm_end - (unsigned long)vma->vm_start,
+		rc);
+
+	return rc;
+}
+
+void msm_release_ion_client(struct kref *ref)
+{
+	struct msm_cam_media_controller *mctl = container_of(ref,
+			struct msm_cam_media_controller, refcount);
+	pr_err("%s Calling ion_client_destroy ", __func__);
+	ion_client_destroy(mctl->client);
+}
+
+static int msm_close(struct file *f)
+{
+	int rc = 0;
+	struct msm_cam_v4l2_device *pcam;
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	struct msm_cam_server_queue *queue;
+	struct msm_cam_media_controller *pmctl;
+	pcam_inst = container_of(f->private_data,
+		struct msm_cam_v4l2_dev_inst, eventHandle);
+	pcam = pcam_inst->pcam;
+	if (!pcam) {
+		pr_err("%s NULL pointer of camera device!\n", __func__);
+		return -EINVAL;
+	}
+
+	pmctl = msm_camera_get_mctl(pcam->mctl_handle);
+	if (!pmctl) {
+		pr_err("%s NULL mctl pointer\n", __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&pcam->vid_lock);
+
+	if (pcam_inst->streamon) {
+		/*something went wrong since instance
+		is closing without streamoff*/
+		if (pmctl->mctl_release) {
+			rc = pmctl->mctl_release(pmctl);
+			if (rc < 0)
+				pr_err("mctl_release fails %d\n", rc);
+		}
+		pmctl->mctl_release = NULL;/*so that it isn't closed again*/
+	}
+
+	pcam_inst->streamon = 0;
+	pcam->use_count--;
+	pcam->dev_inst_map[pcam_inst->image_mode] = NULL;
+	if (pcam_inst->vbqueue_initialized)
+		vb2_queue_release(&pcam_inst->vid_bufq);
+	D("%s Closing down instance %p ", __func__, pcam_inst);
+	D("%s index %d nodeid %d count %d\n", __func__, pcam_inst->my_index,
+		pcam->vnode_id, pcam->use_count);
+	pcam->dev_inst[pcam_inst->my_index] = NULL;
+	if (pcam_inst->my_index == 0) {
+		v4l2_fh_del(&pcam_inst->eventHandle);
+		v4l2_fh_exit(&pcam_inst->eventHandle);
+	}
+	kfree(pcam_inst);
+	f->private_data = NULL;
+
+	if (pcam->use_count == 0) {
+		int ges_evt = MSM_V4L2_GES_CAM_CLOSE;
+		if (g_server_dev.use_count > 0) {
+			rc = msm_send_close_server(pcam);
+			if (rc < 0)
+				pr_err("msm_send_close_server failed %d\n", rc);
+		}
+		if (pmctl->mctl_release) {
+			rc = pmctl->mctl_release(pmctl);
+			if (rc < 0)
+				pr_err("mctl_release fails %d\n", rc);
+		}
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+		kref_put(&pmctl->refcount, msm_release_ion_client);
+#endif
+		queue = &g_server_dev.server_queue[pcam->server_queue_idx];
+		queue->queue_active = 0;
+		kfree(queue->ctrl);
+		queue->ctrl = NULL;
+		kfree(queue->ctrl_data);
+		queue->ctrl_data = NULL;
+		msm_queue_drain(&queue->ctrl_q, list_control);
+		msm_drain_eventq(&queue->eventData_q);
+		rc = msm_cam_server_close_session(&g_server_dev, pcam);
+		if (rc < 0)
+			pr_err("msm_cam_server_close_session fails %d\n", rc);
+
+		if (g_server_dev.use_count == 0)
+			mutex_unlock(&g_server_dev.server_lock);
+
+		msm_cam_server_subdev_notify(g_server_dev.gesture_device,
+			NOTIFY_GESTURE_CAM_EVT, &ges_evt);
+	}
+	mutex_unlock(&pcam->vid_lock);
+	return rc;
+}
+
+static unsigned int msm_poll(struct file *f, struct poll_table_struct *wait)
+{
+	int rc = 0;
+	struct msm_cam_v4l2_device *pcam;
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	pcam_inst = container_of(f->private_data,
+		struct msm_cam_v4l2_dev_inst, eventHandle);
+	pcam = pcam_inst->pcam;
+	D("%s\n", __func__);
+	if (!pcam) {
+		pr_err("%s NULL pointer of camera device!\n", __func__);
+		return -EINVAL;
+	}
+	if (pcam_inst->my_index == 0) {
+		poll_wait(f, &(pcam_inst->eventHandle.wait), wait);
+		if (v4l2_event_pending(&pcam_inst->eventHandle))
+			rc |= POLLPRI;
+	} else {
+		if (!pcam_inst->vid_bufq.streaming) {
+			D("%s vid_bufq.streaming is off, inst=0x%x\n",
+			__func__, (u32)pcam_inst);
+			return -EINVAL;
+		}
+		rc |= vb2_poll(&pcam_inst->vid_bufq, f, wait);
+	}
+	D("%s returns, rc  = 0x%x\n", __func__, rc);
+	return rc;
+}
+
+static unsigned int msm_poll_server(struct file *fp,
+					struct poll_table_struct *wait)
+{
+	int rc = 0;
+
+	D("%s\n", __func__);
+	poll_wait(fp,
+		 &g_server_dev.server_command_queue.eventHandle.wait,
+		 wait);
+	if (v4l2_event_pending(&g_server_dev.server_command_queue.eventHandle))
+		rc |= POLLPRI;
+
+	return rc;
+}
+static long msm_ioctl_server(struct file *file, void *fh,
+		bool valid_prio, int cmd, void *arg)
+{
+	int rc = -EINVAL;
+	struct msm_camera_v4l2_ioctl_t *ioctl_ptr = arg;
+	struct msm_camera_info temp_cam_info;
+	struct msm_cam_config_dev_info temp_config_info;
+	struct msm_mctl_node_info temp_mctl_info;
+	int i;
+
+	D("%s: cmd %d\n", __func__, _IOC_NR(cmd));
+
+	switch (cmd) {
+	case MSM_CAM_V4L2_IOCTL_GET_CAMERA_INFO:
+		if (copy_from_user(&temp_cam_info,
+			(void __user *)ioctl_ptr->ioctl_ptr,
+			sizeof(struct msm_camera_info))) {
+			rc = -EINVAL;
+			return rc;
+		}
+		for (i = 0; i < g_server_dev.camera_info.num_cameras; i++) {
+			if (copy_to_user((void __user *)
+				temp_cam_info.video_dev_name[i],
+				g_server_dev.camera_info.video_dev_name[i],
+				strnlen(
+				g_server_dev.camera_info.video_dev_name[i],
+				MAX_DEV_NAME_LEN))) {
+				rc = -EINVAL;
+				return rc;
+			}
+			temp_cam_info.has_3d_support[i] =
+				g_server_dev.camera_info.has_3d_support[i];
+			temp_cam_info.is_internal_cam[i] =
+				g_server_dev.camera_info.is_internal_cam[i];
+			temp_cam_info.s_mount_angle[i] =
+				g_server_dev.camera_info.s_mount_angle[i];
+			temp_cam_info.sensor_type[i] =
+				g_server_dev.camera_info.sensor_type[i];
+
+		}
+		temp_cam_info.num_cameras =
+			g_server_dev.camera_info.num_cameras;
+		if (copy_to_user((void __user *)ioctl_ptr->ioctl_ptr,
+				&temp_cam_info,
+				sizeof(struct msm_camera_info))) {
+					rc = -EINVAL;
+					return rc;
+		}
+		rc = 0;
+		break;
+
+	case MSM_CAM_V4L2_IOCTL_GET_CONFIG_INFO:
+		if (copy_from_user(&temp_config_info,
+				(void __user *)ioctl_ptr->ioctl_ptr,
+				sizeof(struct msm_cam_config_dev_info))) {
+
+			rc = -EINVAL;
+			return rc;
+		}
+		for (i = 0;
+		 i < g_server_dev.config_info.num_config_nodes; i++) {
+			if (copy_to_user(
+			(void __user *)temp_config_info.config_dev_name[i],
+			g_server_dev.config_info.config_dev_name[i],
+			strlen(g_server_dev.config_info.config_dev_name[i]))) {
+				rc = -EINVAL;
+				return rc;
+			}
+		}
+		temp_config_info.num_config_nodes =
+			g_server_dev.config_info.num_config_nodes;
+		if (copy_to_user((void __user *)ioctl_ptr->ioctl_ptr,
+							  &temp_config_info,
+				sizeof(struct msm_cam_config_dev_info))) {
+			rc = -EINVAL;
+			return rc;
+		}
+		rc = 0;
+		break;
+	case MSM_CAM_V4L2_IOCTL_GET_MCTL_INFO:
+		if (copy_from_user(&temp_mctl_info,
+				(void __user *)ioctl_ptr->ioctl_ptr,
+				sizeof(struct msm_mctl_node_info))) {
+			rc = -EINVAL;
+			return rc;
+		}
+		for (i = 0; i < g_server_dev.mctl_node_info.num_mctl_nodes;
+				i++) {
+			if (copy_to_user((void __user *)
+			temp_mctl_info.mctl_node_name[i],
+			g_server_dev.mctl_node_info.mctl_node_name[i], strnlen(
+			g_server_dev.mctl_node_info.mctl_node_name[i],
+			MAX_DEV_NAME_LEN))) {
+				rc = -EINVAL;
+				return rc;
+			}
+		}
+		temp_mctl_info.num_mctl_nodes =
+			g_server_dev.mctl_node_info.num_mctl_nodes;
+		if (copy_to_user((void __user *)ioctl_ptr->ioctl_ptr,
+							  &temp_mctl_info,
+				sizeof(struct msm_mctl_node_info))) {
+			rc = -EINVAL;
+			return rc;
+		}
+		rc = 0;
+	break;
+
+	case MSM_CAM_V4L2_IOCTL_CTRL_CMD_DONE:
+		D("%s: MSM_CAM_IOCTL_CTRL_CMD_DONE\n", __func__);
+		rc = msm_ctrl_cmd_done(arg);
+		break;
+
+	case MSM_CAM_V4L2_IOCTL_GET_EVENT_PAYLOAD: {
+		struct msm_queue_cmd *event_cmd;
+		struct msm_isp_event_ctrl u_isp_event;
+		struct msm_isp_event_ctrl *k_isp_event;
+		struct msm_device_queue *queue;
+		void __user *u_ctrl_value = NULL;
+		if (copy_from_user(&u_isp_event,
+			(void __user *)ioctl_ptr->ioctl_ptr,
+			sizeof(struct msm_isp_event_ctrl))) {
+			rc = -EINVAL;
+			return rc;
+		}
+		queue = &g_server_dev.server_queue
+			[u_isp_event.isp_data.ctrl.queue_idx].eventData_q;
+		event_cmd = msm_dequeue(queue, list_eventdata);
+		if (!event_cmd) {
+			pr_err("%s: No event payload\n", __func__);
+			rc = -EINVAL;
+			return rc;
+		}
+		k_isp_event = (struct msm_isp_event_ctrl *)
+				event_cmd->command;
+		free_qcmd(event_cmd);
+
+		/* Save the pointer of the user allocated command buffer*/
+		u_ctrl_value = u_isp_event.isp_data.ctrl.value;
+
+		/* Copy the event structure into user struct*/
+		u_isp_event = *k_isp_event;
+
+		/* Restore the saved pointer of the user
+		 * allocated command buffer. */
+		u_isp_event.isp_data.ctrl.value = u_ctrl_value;
+
+		/* Copy the ctrl cmd, if present*/
+		if (k_isp_event->isp_data.ctrl.length > 0) {
+			void *k_ctrl_value =
+				k_isp_event->isp_data.ctrl.value;
+			if (copy_to_user(u_ctrl_value, k_ctrl_value,
+				 k_isp_event->isp_data.ctrl.length)) {
+				rc = -EINVAL;
+				break;
+			}
+		}
+		if (copy_to_user((void __user *)ioctl_ptr->ioctl_ptr,
+							  &u_isp_event,
+				sizeof(struct msm_isp_event_ctrl))) {
+			rc = -EINVAL;
+			return rc;
+		}
+		rc = 0;
+		break;
+	}
+
+	case MSM_CAM_IOCTL_SEND_EVENT:
+		rc = msm_server_send_v4l2_evt(arg);
+		break;
+
+	default:
+		pr_err("%s: Invalid IOCTL = %d", __func__, cmd);
+		break;
+	}
+	return rc;
+}
+
+static int msm_open_server(struct file *fp)
+{
+	int rc = 0;
+	D("%s: open %s\n", __func__, fp->f_path.dentry->d_name.name);
+	mutex_lock(&g_server_dev.server_lock);
+	g_server_dev.use_count++;
+	if (g_server_dev.use_count == 1)
+		fp->private_data =
+			&g_server_dev.server_command_queue.eventHandle;
+	mutex_unlock(&g_server_dev.server_lock);
+	return rc;
+}
+
+static unsigned int msm_poll_config(struct file *fp,
+					struct poll_table_struct *wait)
+{
+	int rc = 0;
+	struct msm_cam_config_dev *config = fp->private_data;
+	if (config == NULL)
+		return -EINVAL;
+
+	D("%s\n", __func__);
+
+	poll_wait(fp,
+	&config->config_stat_event_queue.eventHandle.wait, wait);
+	if (v4l2_event_pending(&config->config_stat_event_queue.eventHandle))
+		rc |= POLLPRI;
+	return rc;
+}
+
+static int msm_close_server(struct file *fp)
+{
+	struct v4l2_event_subscription sub;
+	D("%s\n", __func__);
+	mutex_lock(&g_server_dev.server_lock);
+	if (g_server_dev.use_count > 0)
+		g_server_dev.use_count--;
+	mutex_unlock(&g_server_dev.server_lock);
+	if (g_server_dev.use_count == 0) {
+		if (g_server_dev.pcam_active) {
+			struct v4l2_event v4l2_ev;
+			mutex_lock(&g_server_dev.server_lock);
+
+			v4l2_ev.type = V4L2_EVENT_PRIVATE_START
+				+ MSM_CAM_APP_NOTIFY_ERROR_EVENT;
+			v4l2_ev.id = 0;
+			ktime_get_ts(&v4l2_ev.timestamp);
+			v4l2_event_queue(
+				g_server_dev.pcam_active->pvdev, &v4l2_ev);
+		}
+	sub.type = V4L2_EVENT_ALL;
+	msm_server_v4l2_unsubscribe_event(
+		&g_server_dev.server_command_queue.eventHandle, &sub);
+	}
+	return 0;
+}
+
+static long msm_server_send_v4l2_evt(void *evt)
+{
+	struct v4l2_event *v4l2_ev = (struct v4l2_event *)evt;
+	int rc = 0;
+	v4l2_ev->id = 0;
+	if (NULL == evt) {
+		pr_err("%s: evt is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	D("%s: evt type 0x%x\n", __func__, v4l2_ev->type);
+	if ((v4l2_ev->type >= MSM_GES_APP_EVT_MIN) &&
+		(v4l2_ev->type < MSM_GES_APP_EVT_MAX)) {
+		msm_cam_server_subdev_notify(g_server_dev.gesture_device,
+			NOTIFY_GESTURE_EVT, v4l2_ev);
+	} else {
+		pr_err("%s: Invalid evt %d\n", __func__, v4l2_ev->type);
+		rc = -EINVAL;
+	}
+	D("%s: end\n", __func__);
+
+	return rc;
+}
+
+static long msm_v4l2_evt_notify(struct msm_cam_media_controller *mctl,
+		unsigned int cmd, unsigned long evt)
+{
+	struct v4l2_event v4l2_ev;
+	struct msm_cam_v4l2_device *pcam = NULL;
+
+	if (!mctl) {
+		pr_err("%s: mctl is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	if (copy_from_user(&v4l2_ev, (void __user *)evt,
+		sizeof(struct v4l2_event))) {
+		ERR_COPY_FROM_USER();
+		return -EFAULT;
+	}
+
+	v4l2_ev.id = 0;
+	pcam = mctl->pcam_ptr;
+	ktime_get_ts(&v4l2_ev.timestamp);
+	v4l2_event_queue(pcam->pvdev, &v4l2_ev);
+	return 0;
+}
+
+static long msm_ioctl_config(struct file *fp, unsigned int cmd,
+	unsigned long arg)
+{
+
+	int rc = 0;
+	struct v4l2_event ev;
+	struct msm_cam_config_dev *config_cam = fp->private_data;
+	struct v4l2_event_subscription temp_sub;
+	ev.id = 0;
+	D("%s: cmd %d\n", __func__, _IOC_NR(cmd));
+
+	switch (cmd) {
+	/* memory management shall be handeld here*/
+	case MSM_CAM_IOCTL_REGISTER_PMEM:
+		return msm_register_pmem(
+			&config_cam->p_mctl->stats_info.pmem_stats_list,
+			(void __user *)arg, config_cam->p_mctl->client);
+		break;
+
+	case MSM_CAM_IOCTL_UNREGISTER_PMEM:
+		return msm_pmem_table_del(
+			&config_cam->p_mctl->stats_info.pmem_stats_list,
+			(void __user *)arg, config_cam->p_mctl->client);
+		break;
+
+	case VIDIOC_SUBSCRIBE_EVENT:
+		if (copy_from_user(&temp_sub,
+			(void __user *)arg,
+			sizeof(struct v4l2_event_subscription))) {
+			rc = -EINVAL;
+			return rc;
+		}
+		rc = msm_server_v4l2_subscribe_event
+			(&config_cam->config_stat_event_queue.eventHandle,
+			&temp_sub);
+		if (rc < 0) {
+			pr_err("%s: cam_v4l2_subscribe_event failed rc=%d\n",
+				__func__, rc);
+			return rc;
+		}
+		break;
+
+	case VIDIOC_UNSUBSCRIBE_EVENT:
+		if (copy_from_user(&temp_sub, (void __user *)arg,
+			sizeof(struct v4l2_event_subscription))) {
+			rc = -EINVAL;
+			return rc;
+		}
+		rc = msm_server_v4l2_unsubscribe_event
+			(&config_cam->config_stat_event_queue.eventHandle,
+			&temp_sub);
+		if (rc < 0) {
+			pr_err("%s: server_unsubscribe_event failed rc=%d\n",
+				__func__, rc);
+			return rc;
+		}
+		break;
+
+	case VIDIOC_DQEVENT: {
+		void __user *u_msg_value = NULL, *user_ptr = NULL;
+		struct msm_isp_event_ctrl u_isp_event;
+		struct msm_isp_event_ctrl *k_isp_event;
+
+		/* First, copy the v4l2 event structure from userspace */
+		D("%s: VIDIOC_DQEVENT\n", __func__);
+		if (copy_from_user(&ev, (void __user *)arg,
+				sizeof(struct v4l2_event)))
+			break;
+		/* Next, get the pointer to event_ctrl structure
+		 * embedded inside the v4l2_event.u.data array. */
+		user_ptr = (void __user *)(*((uint32_t *)ev.u.data));
+
+		/* Next, copy the userspace event ctrl structure */
+		if (copy_from_user((void *)&u_isp_event, user_ptr,
+				   sizeof(struct msm_isp_event_ctrl))) {
+			break;
+		}
+		/* Save the pointer of the user allocated command buffer*/
+		u_msg_value = u_isp_event.isp_data.isp_msg.data;
+
+		/* Dequeue the event queued into the v4l2 queue*/
+		rc = v4l2_event_dequeue(
+			&config_cam->config_stat_event_queue.eventHandle,
+			&ev, fp->f_flags & O_NONBLOCK);
+		if (rc < 0) {
+			pr_err("no pending events?");
+			break;
+		}
+		/* Use k_isp_event to point to the event_ctrl structure
+		 * embedded inside v4l2_event.u.data */
+		k_isp_event = (struct msm_isp_event_ctrl *)
+				(*((uint32_t *)ev.u.data));
+		/* Copy the event structure into user struct. */
+		u_isp_event = *k_isp_event;
+		if (ev.type != (V4L2_EVENT_PRIVATE_START +
+				MSM_CAM_RESP_DIV_FRAME_EVT_MSG) &&
+				ev.type != (V4L2_EVENT_PRIVATE_START +
+				MSM_CAM_RESP_MCTL_PP_EVENT)) {
+
+			/* Restore the saved pointer of the
+			 * user allocated command buffer. */
+			u_isp_event.isp_data.isp_msg.data = u_msg_value;
+
+			if (ev.type == (V4L2_EVENT_PRIVATE_START +
+					MSM_CAM_RESP_STAT_EVT_MSG)) {
+				if (k_isp_event->isp_data.isp_msg.len > 0) {
+					void *k_msg_value =
+					k_isp_event->isp_data.isp_msg.data;
+					if (copy_to_user(u_msg_value,
+							k_msg_value,
+					 k_isp_event->isp_data.isp_msg.len)) {
+						rc = -EINVAL;
+						break;
+					}
+					kfree(k_msg_value);
+				}
+			}
+		}
+		/* Copy the event ctrl structure back
+		 * into user's structure. */
+		if (copy_to_user(user_ptr,
+				(void *)&u_isp_event, sizeof(
+				struct msm_isp_event_ctrl))) {
+			rc = -EINVAL;
+			break;
+		}
+		kfree(k_isp_event);
+
+		/* Copy the v4l2_event structure back to the user*/
+		if (copy_to_user((void __user *)arg, &ev,
+				sizeof(struct v4l2_event))) {
+			rc = -EINVAL;
+			break;
+		}
+		}
+
+		break;
+
+	case MSM_CAM_IOCTL_V4L2_EVT_NOTIFY:
+		rc = msm_v4l2_evt_notify(config_cam->p_mctl, cmd, arg);
+		break;
+
+	case MSM_CAM_IOCTL_SET_MEM_MAP_INFO:
+		if (copy_from_user(&config_cam->mem_map, (void __user *)arg,
+				sizeof(struct msm_mem_map_info)))
+			rc = -EINVAL;
+		break;
+
+	default:{
+		/* For the rest of config command, forward to media controller*/
+		struct msm_cam_media_controller *p_mctl = config_cam->p_mctl;
+		if (p_mctl && p_mctl->mctl_cmd) {
+			rc = config_cam->p_mctl->mctl_cmd(p_mctl, cmd, arg);
+		} else {
+			rc = -EINVAL;
+			pr_err("%s: media controller is null\n", __func__);
+		}
+
+		break;
+	} /* end of default*/
+	} /* end of switch*/
+	return rc;
+}
+
+static int msm_mmap_config(struct file *fp, struct vm_area_struct *vma)
+{
+	struct msm_cam_config_dev *config_cam = fp->private_data;
+	int rc = 0;
+	int phyaddr;
+	int retval;
+	unsigned long size;
+
+	D("%s: phy_addr=0x%x", __func__, config_cam->mem_map.cookie);
+	phyaddr = (int)config_cam->mem_map.cookie;
+	if (!phyaddr) {
+		pr_err("%s: no physical memory to map", __func__);
+		return -EFAULT;
+	}
+	memset(&config_cam->mem_map, 0,
+		sizeof(struct msm_mem_map_info));
+	size = vma->vm_end - vma->vm_start;
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+	retval = remap_pfn_range(vma, vma->vm_start,
+					phyaddr >> PAGE_SHIFT,
+					size, vma->vm_page_prot);
+	if (retval) {
+		pr_err("%s: remap failed, rc = %d",
+					__func__, retval);
+		rc = -ENOMEM;
+		goto end;
+	}
+	D("%s: phy_addr=0x%x: %08lx-%08lx, pgoff %08lx\n",
+			__func__, (uint32_t)phyaddr,
+			vma->vm_start, vma->vm_end, vma->vm_pgoff);
+end:
+	return rc;
+}
+
+static int msm_open_config(struct inode *inode, struct file *fp)
+{
+	int rc;
+	struct msm_cam_config_dev *config_cam = container_of(inode->i_cdev,
+		struct msm_cam_config_dev, config_cdev);
+
+	D("%s: open %s\n", __func__, fp->f_path.dentry->d_name.name);
+
+	rc = nonseekable_open(inode, fp);
+	if (rc < 0) {
+		pr_err("%s: nonseekable_open error %d\n", __func__, rc);
+		return rc;
+	}
+	config_cam->use_count++;
+
+	/*config_cam->isp_subdev = g_server_dev.pcam_active->mctl.isp_sdev;*/
+	/* assume there is only one active camera possible*/
+	config_cam->p_mctl =
+		msm_camera_get_mctl(g_server_dev.pcam_active->mctl_handle);
+
+	INIT_HLIST_HEAD(&config_cam->p_mctl->stats_info.pmem_stats_list);
+	spin_lock_init(&config_cam->p_mctl->stats_info.pmem_stats_spinlock);
+
+	config_cam->p_mctl->config_device = config_cam;
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+	kref_get(&config_cam->p_mctl->refcount);
+#endif
+	fp->private_data = config_cam;
+	return rc;
+}
+
+static int msm_close_config(struct inode *node, struct file *f)
+{
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+	struct msm_cam_config_dev *config_cam = f->private_data;
+	D("%s Decrementing ref count of config node ", __func__);
+	kref_put(&config_cam->p_mctl->refcount, msm_release_ion_client);
+#endif
+	return 0;
+}
+
+static struct v4l2_file_operations g_msm_fops = {
+	.owner   = THIS_MODULE,
+	.open	= msm_open,
+	.poll	= msm_poll,
+	.mmap	= msm_mmap,
+	.release = msm_close,
+	.ioctl   = video_ioctl2,
+};
+
+/* Init a config node for ISP control,
+ * which will create a config device (/dev/config0/ and plug in
+ * ISP's operation "v4l2_ioctl_ops*"
+ */
+static const struct v4l2_file_operations msm_fops_server = {
+	.owner = THIS_MODULE,
+	.open  = msm_open_server,
+	.poll  = msm_poll_server,
+	.unlocked_ioctl = video_ioctl2,
+	.release = msm_close_server,
+};
+
+static const struct v4l2_ioctl_ops msm_ioctl_ops_server = {
+	.vidioc_subscribe_event = msm_server_v4l2_subscribe_event,
+	.vidioc_default = msm_ioctl_server,
+};
+
+static const struct file_operations msm_fops_config = {
+	.owner = THIS_MODULE,
+	.open  = msm_open_config,
+	.poll  = msm_poll_config,
+	.unlocked_ioctl = msm_ioctl_config,
+	.mmap	= msm_mmap_config,
+	.release = msm_close_config,
+};
+
+int msm_setup_v4l2_event_queue(struct v4l2_fh *eventHandle,
+	struct video_device *pvdev)
+{
+	int rc = 0;
+	/* v4l2_fh support */
+	spin_lock_init(&pvdev->fh_lock);
+	INIT_LIST_HEAD(&pvdev->fh_list);
+
+	v4l2_fh_init(eventHandle, pvdev);
+	v4l2_fh_add(eventHandle);
+	return rc;
+}
+
+static int msm_setup_config_dev(int node, char *device_name)
+{
+	int rc = -ENODEV;
+	struct device *device_config;
+	int dev_num = node;
+	dev_t devno;
+	struct msm_cam_config_dev *config_cam;
+
+	config_cam = kzalloc(sizeof(*config_cam), GFP_KERNEL);
+	if (!config_cam) {
+		pr_err("%s: could not allocate memory for msm_cam_config_device\n",
+			__func__);
+		return -ENOMEM;
+	}
+
+	D("%s\n", __func__);
+
+	devno = MKDEV(MAJOR(msm_devno), dev_num+1);
+	device_config = device_create(msm_class, NULL, devno, NULL, "%s%d",
+		device_name, dev_num);
+
+	if (IS_ERR(device_config)) {
+		rc = PTR_ERR(device_config);
+		pr_err("%s: error creating device: %d\n", __func__, rc);
+		goto config_setup_fail;
+	}
+
+	cdev_init(&config_cam->config_cdev, &msm_fops_config);
+	config_cam->config_cdev.owner = THIS_MODULE;
+
+	rc = cdev_add(&config_cam->config_cdev, devno, 1);
+	if (rc < 0) {
+		pr_err("%s: error adding cdev: %d\n", __func__, rc);
+		device_destroy(msm_class, devno);
+		goto config_setup_fail;
+	}
+
+	g_server_dev.config_info.config_dev_name[dev_num] =
+		dev_name(device_config);
+	D("%s Connected config device %s\n", __func__,
+		g_server_dev.config_info.config_dev_name[dev_num]);
+	g_server_dev.config_info.config_dev_id[dev_num] = dev_num;
+
+	config_cam->config_stat_event_queue.pvdev = video_device_alloc();
+	if (config_cam->config_stat_event_queue.pvdev == NULL) {
+		pr_err("%s: video_device_alloc failed\n", __func__);
+		goto config_setup_fail;
+	}
+
+	rc = msm_setup_v4l2_event_queue(
+		&config_cam->config_stat_event_queue.eventHandle,
+		config_cam->config_stat_event_queue.pvdev);
+	if (rc < 0) {
+		pr_err("%s failed to initialize event queue\n", __func__);
+		video_device_release(config_cam->config_stat_event_queue.pvdev);
+		goto config_setup_fail;
+	}
+
+	return rc;
+
+config_setup_fail:
+	kfree(config_cam);
+	return rc;
+}
+
+static void msm_cam_server_subdev_notify(struct v4l2_subdev *sd,
+				unsigned int notification, void *arg)
+{
+	int rc = -EINVAL;
+	struct msm_sensor_ctrl_t *s_ctrl;
+	struct msm_camera_sensor_info *sinfo;
+	struct msm_camera_device_platform_data *camdev;
+	uint8_t csid_core = 0;
+
+	if (notification == NOTIFY_CID_CHANGE ||
+		notification == NOTIFY_ISPIF_STREAM ||
+		notification == NOTIFY_PCLK_CHANGE ||
+		notification == NOTIFY_CSIPHY_CFG ||
+		notification == NOTIFY_CSID_CFG ||
+		notification == NOTIFY_CSIC_CFG) {
+		s_ctrl = get_sctrl(sd);
+		sinfo = (struct msm_camera_sensor_info *) s_ctrl->sensordata;
+		camdev = sinfo->pdata;
+		csid_core = camdev->csid_core;
+	}
+
+	switch (notification) {
+	case NOTIFY_CID_CHANGE:
+		/* reconfig the ISPIF*/
+		if (g_server_dev.ispif_device) {
+			struct msm_ispif_params_list ispif_params;
+			ispif_params.len = 1;
+			ispif_params.params[0].intftype = PIX0;
+			ispif_params.params[0].cid_mask = 0x0001;
+			ispif_params.params[0].csid = csid_core;
+
+			rc = v4l2_subdev_call(
+				g_server_dev.ispif_device, core, ioctl,
+				VIDIOC_MSM_ISPIF_CFG, &ispif_params);
+			if (rc < 0)
+				return;
+		}
+		break;
+	case NOTIFY_ISPIF_STREAM:
+		/* call ISPIF stream on/off */
+		rc = v4l2_subdev_call(g_server_dev.ispif_device, video,
+				s_stream, (int)arg);
+		if (rc < 0)
+			return;
+
+		break;
+	case NOTIFY_ISP_MSG_EVT:
+	case NOTIFY_VFE_MSG_OUT:
+	case NOTIFY_VFE_MSG_STATS:
+	case NOTIFY_VFE_MSG_COMP_STATS:
+	case NOTIFY_VFE_BUF_EVT:
+	case NOTIFY_VFE_BUF_FREE_EVT:
+		if (g_server_dev.isp_subdev[0] &&
+			g_server_dev.isp_subdev[0]->isp_notify) {
+			rc = g_server_dev.isp_subdev[0]->isp_notify(
+				g_server_dev.vfe_device[0], notification, arg);
+		}
+		break;
+	case NOTIFY_VPE_MSG_EVT: {
+		struct msm_cam_media_controller *pmctl =
+		(struct msm_cam_media_controller *)
+		v4l2_get_subdev_hostdata(sd);
+		struct msm_vpe_resp *vdata = (struct msm_vpe_resp *)arg;
+		msm_mctl_pp_notify(pmctl,
+		(struct msm_mctl_pp_frame_info *)
+		vdata->extdata);
+		break;
+	}
+	case NOTIFY_VFE_IRQ:{
+		struct msm_vfe_cfg_cmd cfg_cmd;
+		struct msm_camvfe_params vfe_params;
+		cfg_cmd.cmd_type = CMD_VFE_PROCESS_IRQ;
+		vfe_params.vfe_cfg = &cfg_cmd;
+		vfe_params.data = arg;
+		rc = v4l2_subdev_call(g_server_dev.vfe_device[0],
+			core, ioctl, 0, &vfe_params);
+	}
+		break;
+	case NOTIFY_AXI_IRQ:
+		rc = v4l2_subdev_call(g_server_dev.axi_device[0],
+			core, ioctl, VIDIOC_MSM_AXI_IRQ, arg);
+		break;
+	case NOTIFY_PCLK_CHANGE:
+		if (g_server_dev.axi_device[0])
+			rc = v4l2_subdev_call(g_server_dev.axi_device[0], video,
+				s_crystal_freq, *(uint32_t *)arg, 0);
+		else
+			rc = v4l2_subdev_call(g_server_dev.vfe_device[0], video,
+				s_crystal_freq, *(uint32_t *)arg, 0);
+		break;
+	case NOTIFY_CSIPHY_CFG:
+		rc = v4l2_subdev_call(g_server_dev.csiphy_device[csid_core],
+			core, ioctl, VIDIOC_MSM_CSIPHY_CFG, arg);
+		break;
+	case NOTIFY_CSID_CFG:
+		rc = v4l2_subdev_call(g_server_dev.csid_device[csid_core],
+			core, ioctl, VIDIOC_MSM_CSID_CFG, arg);
+		break;
+	case NOTIFY_CSIC_CFG:
+		rc = v4l2_subdev_call(g_server_dev.csic_device[csid_core],
+			core, ioctl, VIDIOC_MSM_CSIC_CFG, arg);
+		break;
+	case NOTIFY_GESTURE_EVT:
+		rc = v4l2_subdev_call(g_server_dev.gesture_device,
+			core, ioctl, VIDIOC_MSM_GESTURE_EVT, arg);
+		break;
+	case NOTIFY_GESTURE_CAM_EVT:
+		rc = v4l2_subdev_call(g_server_dev.gesture_device,
+			core, ioctl, VIDIOC_MSM_GESTURE_CAM_EVT, arg);
+		break;
+	default:
+		break;
+	}
+
+	return;
+}
+
+void msm_cam_release_subdev_node(struct video_device *vdev)
+{
+	struct v4l2_subdev *sd = video_get_drvdata(vdev);
+	sd->devnode = NULL;
+	kfree(vdev);
+}
+
+int msm_cam_register_subdev_node(struct v4l2_subdev *sd,
+	enum msm_cam_subdev_type sdev_type, uint8_t index)
+{
+	struct video_device *vdev;
+	int err = 0;
+
+	if (sdev_type == CSIPHY_DEV) {
+		if (index >= MAX_NUM_CSIPHY_DEV)
+			return -EINVAL;
+		g_server_dev.csiphy_device[index] = sd;
+	} else if (sdev_type == CSID_DEV) {
+		if (index >= MAX_NUM_CSID_DEV)
+			return -EINVAL;
+		g_server_dev.csid_device[index] = sd;
+	} else if (sdev_type == CSIC_DEV) {
+		if (index >= MAX_NUM_CSIC_DEV)
+			return -EINVAL;
+		g_server_dev.csic_device[index] = sd;
+	} else if (sdev_type == ISPIF_DEV) {
+		g_server_dev.ispif_device = sd;
+	} else if (sdev_type == VFE_DEV) {
+		if (index >= MAX_NUM_VFE_DEV)
+			return -EINVAL;
+		g_server_dev.vfe_device[index] = sd;
+	} else if (sdev_type == VPE_DEV) {
+		if (index >= MAX_NUM_VPE_DEV)
+			return -EINVAL;
+		g_server_dev.vpe_device[index] = sd;
+	} else if (sdev_type == AXI_DEV) {
+		if (index >= MAX_NUM_AXI_DEV)
+			return -EINVAL;
+		g_server_dev.axi_device[index] = sd;
+	} else if (sdev_type == GESTURE_DEV) {
+		g_server_dev.gesture_device = sd;
+	}
+
+	err = v4l2_device_register_subdev(&g_server_dev.v4l2_dev, sd);
+	if (err < 0)
+		return err;
+
+	/* Register a device node for every subdev marked with the
+	 * V4L2_SUBDEV_FL_HAS_DEVNODE flag.
+	 */
+	if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
+		return err;
+
+	vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
+	if (!vdev) {
+		err = -ENOMEM;
+		goto clean_up;
+	}
+
+	video_set_drvdata(vdev, sd);
+	strlcpy(vdev->name, sd->name, sizeof(vdev->name));
+	vdev->v4l2_dev = &g_server_dev.v4l2_dev;
+	vdev->fops = &v4l2_subdev_fops;
+	vdev->release = msm_cam_release_subdev_node;
+	err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
+						  sd->owner);
+	if (err < 0) {
+		kfree(vdev);
+		goto clean_up;
+	}
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	sd->entity.info.v4l.major = VIDEO_MAJOR;
+	sd->entity.info.v4l.minor = vdev->minor;
+#endif
+	sd->devnode = vdev;
+	return 0;
+
+clean_up:
+	if (sd->devnode)
+		video_unregister_device(sd->devnode);
+	return err;
+}
+
+static int msm_setup_server_dev(struct platform_device *pdev)
+{
+	int rc = -ENODEV, i;
+
+	D("%s\n", __func__);
+	g_server_dev.server_pdev = pdev;
+	g_server_dev.v4l2_dev.dev = &pdev->dev;
+	g_server_dev.v4l2_dev.notify = msm_cam_server_subdev_notify;
+	rc = v4l2_device_register(g_server_dev.v4l2_dev.dev,
+			&g_server_dev.v4l2_dev);
+	if (rc < 0)
+		return -EINVAL;
+
+	g_server_dev.video_dev = video_device_alloc();
+	if (g_server_dev.video_dev == NULL) {
+		pr_err("%s: video_device_alloc failed\n", __func__);
+		return rc;
+	}
+
+	strlcpy(g_server_dev.video_dev->name, pdev->name,
+			sizeof(g_server_dev.video_dev->name));
+
+	g_server_dev.video_dev->v4l2_dev = &g_server_dev.v4l2_dev;
+	g_server_dev.video_dev->fops = &msm_fops_server;
+	g_server_dev.video_dev->ioctl_ops = &msm_ioctl_ops_server;
+	g_server_dev.video_dev->release   = video_device_release;
+	g_server_dev.video_dev->minor = 100;
+	g_server_dev.video_dev->vfl_type = 1;
+
+	video_set_drvdata(g_server_dev.video_dev, &g_server_dev);
+
+	strlcpy(g_server_dev.media_dev.model, "qcamera",
+		sizeof(g_server_dev.media_dev.model));
+	g_server_dev.media_dev.dev = &pdev->dev;
+	rc = media_device_register(&g_server_dev.media_dev);
+	g_server_dev.v4l2_dev.mdev = &g_server_dev.media_dev;
+
+	rc = video_register_device(g_server_dev.video_dev,
+		VFL_TYPE_GRABBER, 100);
+
+	mutex_init(&g_server_dev.server_lock);
+	mutex_init(&g_server_dev.server_queue_lock);
+	g_server_dev.pcam_active = NULL;
+	g_server_dev.camera_info.num_cameras = 0;
+	atomic_set(&g_server_dev.number_pcam_active, 0);
+	g_server_dev.server_evt_id = 0;
+
+	/*initialize fake video device and event queue*/
+
+	g_server_dev.server_command_queue.pvdev = g_server_dev.video_dev;
+	rc = msm_setup_v4l2_event_queue(
+		&g_server_dev.server_command_queue.eventHandle,
+		g_server_dev.server_command_queue.pvdev);
+
+	if (rc < 0) {
+		pr_err("%s failed to initialize event queue\n", __func__);
+		video_device_release(g_server_dev.server_command_queue.pvdev);
+		return rc;
+	}
+
+	for (i = 0; i < MAX_NUM_ACTIVE_CAMERA; i++) {
+		struct msm_cam_server_queue *queue;
+		queue = &g_server_dev.server_queue[i];
+		queue->queue_active = 0;
+		msm_queue_init(&queue->ctrl_q, "control");
+		msm_queue_init(&queue->eventData_q, "eventdata");
+	}
+	return rc;
+}
+
+static int msm_cam_dev_init(struct msm_cam_v4l2_device *pcam)
+{
+	int rc = -ENOMEM;
+	struct video_device *pvdev = NULL;
+	struct i2c_client *client = v4l2_get_subdevdata(pcam->sensor_sdev);
+	D("%s\n", __func__);
+
+	/* first register the v4l2 device */
+	pcam->v4l2_dev.dev = &client->dev;
+	rc = v4l2_device_register(pcam->v4l2_dev.dev, &pcam->v4l2_dev);
+	if (rc < 0)
+		return -EINVAL;
+	else
+		pcam->v4l2_dev.notify = msm_cam_v4l2_subdev_notify;
+
+
+	/* now setup video device */
+	pvdev = video_device_alloc();
+	if (pvdev == NULL) {
+		pr_err("%s: video_device_alloc failed\n", __func__);
+		return rc;
+	}
+
+	strlcpy(pcam->media_dev.model, QCAMERA_NAME,
+			sizeof(pcam->media_dev.model));
+	pcam->media_dev.dev = &client->dev;
+	rc = media_device_register(&pcam->media_dev);
+	pvdev->v4l2_dev = &pcam->v4l2_dev;
+	pcam->v4l2_dev.mdev = &pcam->media_dev;
+
+	/* init video device's driver interface */
+	D("sensor name = %s, sizeof(pvdev->name)=%d\n",
+		pcam->sensor_sdev->name, sizeof(pvdev->name));
+
+	/* device info - strlcpy is safer than strncpy but
+	   only if architecture supports*/
+	strlcpy(pvdev->name, pcam->sensor_sdev->name, sizeof(pvdev->name));
+
+	pvdev->release   = video_device_release;
+	pvdev->fops	  = &g_msm_fops;
+	pvdev->ioctl_ops = &g_msm_ioctl_ops;
+	pvdev->minor	 = -1;
+	pvdev->vfl_type  = 1;
+
+	media_entity_init(&pvdev->entity, 0, NULL, 0);
+	pvdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
+	pvdev->entity.group_id = QCAMERA_VNODE_GROUP_ID;
+
+	/* register v4l2 video device to kernel as /dev/videoXX */
+	D("video_register_device\n");
+	rc = video_register_device(pvdev,
+					VFL_TYPE_GRABBER,
+					msm_camera_v4l2_nr);
+	if (rc) {
+		pr_err("%s: video_register_device failed\n", __func__);
+		goto reg_fail;
+	}
+	pvdev->entity.name = video_device_node_name(pvdev);
+	D("%s: video device registered as /dev/video%d\n",
+		__func__, pvdev->num);
+
+	/* connect pcam and video dev to each other */
+	pcam->pvdev	= pvdev;
+	video_set_drvdata(pcam->pvdev, pcam);
+
+	/* If isp HW registeration is successful,
+	 * then create event queue to
+	 * receievent event froms HW
+	*/
+	/* yyan: no global - each sensor will
+	 * create a new vidoe node! */
+	/* g_pmsm_camera_v4l2_dev = pmsm_camera_v4l2_dev; */
+	/* g_pmsm_camera_v4l2_dev->pvdev = pvdev; */
+
+	return rc ;
+
+reg_fail:
+	video_device_release(pvdev);
+	v4l2_device_unregister(&pcam->v4l2_dev);
+	pcam->v4l2_dev.dev = NULL;
+	return rc;
+}
+
+static struct v4l2_subdev *msm_actuator_probe(
+	struct msm_actuator_info *actuator_info)
+{
+	struct v4l2_subdev *act_sdev;
+	struct i2c_adapter *adapter = NULL;
+	struct msm_actuator_ctrl_t *actrl;
+	void *act_client = NULL;
+
+	D("%s called\n", __func__);
+
+	if (!actuator_info)
+		goto probe_fail;
+
+	adapter = i2c_get_adapter(actuator_info->bus_id);
+	if (!adapter)
+		goto probe_fail;
+
+	act_client = i2c_new_device(adapter, actuator_info->board_info);
+	if (!act_client)
+		goto device_fail;
+
+	act_sdev = (struct v4l2_subdev *)i2c_get_clientdata(act_client);
+	if (act_sdev == NULL)
+		goto client_fail;
+
+	if (actuator_info->vcm_enable) {
+		actrl = get_actrl(act_sdev);
+		if (actrl) {
+			actrl->vcm_enable = actuator_info->vcm_enable;
+			actrl->vcm_pwd = actuator_info->vcm_pwd;
+		}
+	}
+
+	return act_sdev;
+
+client_fail:
+	i2c_unregister_device(act_client);
+device_fail:
+	i2c_put_adapter(adapter);
+	adapter = NULL;
+probe_fail:
+	return NULL;
+}
+
+static struct v4l2_subdev *msm_eeprom_probe(
+	struct msm_eeprom_info *eeprom_info)
+{
+	struct v4l2_subdev *eeprom_sdev;
+	struct i2c_adapter *adapter = NULL;
+	void *eeprom_client = NULL;
+
+	D("%s called\n", __func__);
+
+	if (!eeprom_info)
+		goto probe_fail;
+
+	adapter = i2c_get_adapter(eeprom_info->bus_id);
+	if (!adapter)
+		goto probe_fail;
+
+	eeprom_client = i2c_new_device(adapter, eeprom_info->board_info);
+	if (!eeprom_client)
+		goto device_fail;
+
+	eeprom_sdev = (struct v4l2_subdev *)i2c_get_clientdata(eeprom_client);
+	if (eeprom_sdev == NULL)
+		goto client_fail;
+
+	return eeprom_sdev;
+client_fail:
+	pr_err("%s client_fail\n", __func__);
+	i2c_unregister_device(eeprom_client);
+device_fail:
+	pr_err("%s device_fail\n", __func__);
+	i2c_put_adapter(adapter);
+	adapter = NULL;
+probe_fail:
+	pr_err("%s probe_fail\n", __func__);
+	return NULL;
+}
+
+/* register a msm sensor into the msm device, which will probe the
+ * sensor HW. if the HW exist then create a video device (/dev/videoX/)
+ * to represent this sensor */
+int msm_sensor_register(struct v4l2_subdev *sensor_sd)
+{
+	int rc = -EINVAL;
+	struct msm_camera_sensor_info *sdata;
+	struct msm_cam_v4l2_device *pcam;
+	struct msm_sensor_ctrl_t *s_ctrl;
+
+	D("%s for %s\n", __func__, sensor_sd->name);
+
+	/* allocate the memory for the camera device first */
+	pcam = kzalloc(sizeof(*pcam), GFP_KERNEL);
+	if (!pcam) {
+		pr_err("%s: could not allocate mem for msm_cam_v4l2_device\n",
+			__func__);
+		return -ENOMEM;
+	}
+
+	pcam->sensor_sdev = sensor_sd;
+	s_ctrl = get_sctrl(sensor_sd);
+	sdata = (struct msm_camera_sensor_info *) s_ctrl->sensordata;
+
+	pcam->act_sdev = msm_actuator_probe(sdata->actuator_info);
+	pcam->eeprom_sdev = msm_eeprom_probe(sdata->eeprom_info);
+
+	D("%s: pcam =0x%p\n", __func__, pcam);
+
+	pcam->sdata = sdata;
+
+	/* init the user count and lock*/
+	pcam->use_count = 0;
+	mutex_init(&pcam->vid_lock);
+	mutex_init(&pcam->mctl_node.dev_lock);
+
+	/* Initialize the formats supported */
+	rc  = msm_mctl_init_user_formats(pcam);
+	if (rc < 0)
+		goto failure;
+
+	rc  = msm_cam_dev_init(pcam);
+	if (rc < 0)
+		goto failure;
+
+	rc = msm_setup_mctl_node(pcam);
+	if (rc < 0) {
+		pr_err("%s:failed to create mctl device: %d\n",
+			 __func__, rc);
+		goto failure;
+	}
+
+	g_server_dev.camera_info.video_dev_name
+	[g_server_dev.camera_info.num_cameras]
+	= video_device_node_name(pcam->pvdev);
+	D("%s Connected video device %s\n", __func__,
+		g_server_dev.camera_info.video_dev_name
+		[g_server_dev.camera_info.num_cameras]);
+
+	g_server_dev.camera_info.s_mount_angle
+	[g_server_dev.camera_info.num_cameras]
+	= sdata->sensor_platform_info->mount_angle;
+
+	g_server_dev.camera_info.is_internal_cam
+	[g_server_dev.camera_info.num_cameras]
+	= sdata->camera_type;
+
+	g_server_dev.mctl_node_info.mctl_node_name
+	[g_server_dev.mctl_node_info.num_mctl_nodes]
+	= video_device_node_name(pcam->mctl_node.pvdev);
+
+	pr_info("%s mctl_node_name[%d] = %s\n", __func__,
+		g_server_dev.mctl_node_info.num_mctl_nodes,
+		g_server_dev.mctl_node_info.mctl_node_name
+		[g_server_dev.mctl_node_info.num_mctl_nodes]);
+
+	/*Temporary solution to store info in media device structure
+	  until we can expand media device structure to support more
+	  device info*/
+	snprintf(pcam->media_dev.serial,
+			sizeof(pcam->media_dev.serial),
+			"%s-%d-%d", QCAMERA_NAME,
+			sdata->sensor_platform_info->mount_angle,
+			sdata->camera_type);
+
+	g_server_dev.camera_info.num_cameras++;
+	g_server_dev.mctl_node_info.num_mctl_nodes++;
+
+	D("%s done, rc = %d\n", __func__, rc);
+	D("%s number of sensors connected is %d\n", __func__,
+		g_server_dev.camera_info.num_cameras);
+
+	/* register the subdevice, must be done for callbacks */
+	rc = msm_cam_register_subdev_node(sensor_sd, SENSOR_DEV, vnode_count);
+	if (rc < 0) {
+		D("%s sensor sub device register failed\n",
+			__func__);
+		goto failure;
+	}
+
+	if (pcam->act_sdev) {
+		rc = v4l2_device_register_subdev(&pcam->v4l2_dev,
+				pcam->act_sdev);
+		if (rc < 0) {
+			D("%s actuator sub device register failed\n",
+			  __func__);
+			goto failure;
+		}
+	}
+
+	if (pcam->eeprom_sdev) {
+		rc = v4l2_device_register_subdev(&pcam->v4l2_dev,
+			pcam->eeprom_sdev);
+		if (rc < 0) {
+			D("%s eeprom sub device register failed\n", __func__);
+			goto failure;
+		}
+	}
+
+	pcam->vnode_id = vnode_count++;
+	return rc;
+
+failure:
+	kzfree(pcam);
+	return rc;
+}
+EXPORT_SYMBOL(msm_sensor_register);
+
+static int __devinit msm_camera_probe(struct platform_device *pdev)
+{
+	int rc = 0, i;
+	/*for now just create a config 0 node
+	  put logic here later to know how many configs to create*/
+	g_server_dev.config_info.num_config_nodes = 1;
+
+	rc = msm_isp_init_module(g_server_dev.config_info.num_config_nodes);
+	if (rc < 0) {
+		pr_err("Failed to initialize isp\n");
+		return rc;
+	}
+
+	if (!msm_class) {
+		rc = alloc_chrdev_region(&msm_devno, 0,
+		g_server_dev.config_info.num_config_nodes+1, "msm_camera");
+		if (rc < 0) {
+			pr_err("%s: failed to allocate chrdev: %d\n", __func__,
+			rc);
+			return rc;
+		}
+
+		msm_class = class_create(THIS_MODULE, "msm_camera");
+		if (IS_ERR(msm_class)) {
+			rc = PTR_ERR(msm_class);
+			pr_err("%s: create device class failed: %d\n",
+			__func__, rc);
+			return rc;
+		}
+	}
+
+	D("creating server and config nodes\n");
+	rc = msm_setup_server_dev(pdev);
+	if (rc < 0) {
+		pr_err("%s: failed to create server dev: %d\n", __func__,
+		rc);
+		return rc;
+	}
+
+	for (i = 0; i < g_server_dev.config_info.num_config_nodes; i++) {
+		rc = msm_setup_config_dev(i, "config");
+		if (rc < 0) {
+			pr_err("%s:failed to create config dev: %d\n",
+			 __func__, rc);
+			return rc;
+		}
+	}
+
+	msm_isp_register(&g_server_dev);
+	return rc;
+}
+
+static int __exit msm_camera_exit(struct platform_device *pdev)
+{
+	msm_isp_unregister(&g_server_dev);
+	return 0;
+}
+
+
+static struct platform_driver msm_cam_server_driver = {
+	.probe = msm_camera_probe,
+	.remove = msm_camera_exit,
+	.driver = {
+		.name = "msm_cam_server",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init msm_camera_init(void)
+{
+	return platform_driver_register(&msm_cam_server_driver);
+}
+
+static void __exit msm_cam_server_exit(void)
+{
+	platform_driver_unregister(&msm_cam_server_driver);
+}
+
+module_init(msm_camera_init);
+module_exit(msm_cam_server_exit);
diff --git a/drivers/media/video/msm/msm.h b/drivers/media/video/msm/msm.h
new file mode 100644
index 0000000..688339b
--- /dev/null
+++ b/drivers/media/video/msm/msm.h
@@ -0,0 +1,585 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _MSM_H
+#define _MSM_H
+
+#ifdef __KERNEL__
+
+/* Header files */
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <linux/pm_qos.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-mediabus.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-msm-mem.h>
+#include <media/msm_isp.h>
+#include <mach/camera.h>
+#include <media/msm_isp.h>
+#include <linux/ion.h>
+#include <media/msm_gestures.h>
+
+#define MSM_V4L2_DIMENSION_SIZE 96
+#define MAX_DEV_NAME_LEN 50
+
+#define ERR_USER_COPY(to) pr_debug("%s(%d): copy %s user\n", \
+				__func__, __LINE__, ((to) ? "to" : "from"))
+#define ERR_COPY_FROM_USER() ERR_USER_COPY(0)
+#define ERR_COPY_TO_USER() ERR_USER_COPY(1)
+
+#define MSM_CSIPHY_DRV_NAME "msm_csiphy"
+#define MSM_CSID_DRV_NAME "msm_csid"
+#define MSM_CSIC_DRV_NAME "msm_csic"
+#define MSM_ISPIF_DRV_NAME "msm_ispif"
+#define MSM_VFE_DRV_NAME "msm_vfe"
+#define MSM_VPE_DRV_NAME "msm_vpe"
+#define MSM_GEMINI_DRV_NAME "msm_gemini"
+#define MSM_I2C_MUX_DRV_NAME "msm_cam_i2c_mux"
+
+#define MAX_NUM_CSIPHY_DEV 3
+#define MAX_NUM_CSID_DEV 3
+#define MAX_NUM_CSIC_DEV 3
+#define MAX_NUM_ISPIF_DEV 1
+#define MAX_NUM_VFE_DEV 2
+#define MAX_NUM_AXI_DEV 2
+#define MAX_NUM_VPE_DEV 1
+
+enum msm_cam_subdev_type {
+	CSIPHY_DEV,
+	CSID_DEV,
+	CSIC_DEV,
+	ISPIF_DEV,
+	VFE_DEV,
+	AXI_DEV,
+	VPE_DEV,
+	SENSOR_DEV,
+	ACTUATOR_DEV,
+	EEPROM_DEV,
+	GESTURE_DEV,
+};
+
+/* msm queue management APIs*/
+
+#define msm_dequeue(queue, member) ({	   \
+	unsigned long flags;		  \
+	struct msm_device_queue *__q = (queue);	 \
+	struct msm_queue_cmd *qcmd = 0;	   \
+	spin_lock_irqsave(&__q->lock, flags);	 \
+	if (!list_empty(&__q->list)) {		\
+		__q->len--;		 \
+		qcmd = list_first_entry(&__q->list,   \
+		struct msm_queue_cmd, member);  \
+		list_del_init(&qcmd->member);	 \
+	}			 \
+	spin_unlock_irqrestore(&__q->lock, flags);  \
+	qcmd;			 \
+})
+
+#define msm_queue_drain(queue, member) do {	 \
+	unsigned long flags;		  \
+	struct msm_device_queue *__q = (queue);	 \
+	struct msm_queue_cmd *qcmd;	   \
+	spin_lock_irqsave(&__q->lock, flags);	 \
+	while (!list_empty(&__q->list)) {	 \
+		qcmd = list_first_entry(&__q->list,   \
+			struct msm_queue_cmd, member);	\
+			list_del_init(&qcmd->member);	 \
+			free_qcmd(qcmd);		\
+	 };			  \
+	spin_unlock_irqrestore(&__q->lock, flags);	\
+} while (0)
+
+static inline void free_qcmd(struct msm_queue_cmd *qcmd)
+{
+	if (!qcmd || !atomic_read(&qcmd->on_heap))
+		return;
+	if (!atomic_sub_return(1, &qcmd->on_heap))
+		kfree(qcmd);
+}
+
+struct isp_msg_stats {
+	uint32_t    id;
+	uint32_t    buffer;
+	uint32_t    frameCounter;
+};
+
+struct msm_free_buf {
+	uint8_t num_planes;
+	uint32_t ch_paddr[VIDEO_MAX_PLANES];
+	uint32_t vb;
+};
+
+struct isp_msg_event {
+	uint32_t msg_id;
+	uint32_t sof_count;
+};
+
+struct isp_msg_output {
+	uint8_t   output_id;
+	struct msm_free_buf buf;
+	uint32_t  frameCounter;
+};
+
+/* message id for v4l2_subdev_notify*/
+enum msm_camera_v4l2_subdev_notify {
+	NOTIFY_CID_CHANGE, /* arg = msm_camera_csid_params */
+	NOTIFY_ISP_MSG_EVT, /* arg = enum ISP_MESSAGE_ID */
+	NOTIFY_VFE_MSG_OUT, /* arg = struct isp_msg_output */
+	NOTIFY_VFE_MSG_STATS,  /* arg = struct isp_msg_stats */
+	NOTIFY_VFE_MSG_COMP_STATS, /* arg = struct msm_stats_buf */
+	NOTIFY_VFE_BUF_EVT, /* arg = struct msm_vfe_resp */
+	NOTIFY_ISPIF_STREAM, /* arg = enable parameter for s_stream */
+	NOTIFY_VPE_MSG_EVT,
+	NOTIFY_PCLK_CHANGE, /* arg = pclk */
+	NOTIFY_CSIPHY_CFG, /* arg = msm_camera_csiphy_params */
+	NOTIFY_CSID_CFG, /* arg = msm_camera_csid_params */
+	NOTIFY_CSIC_CFG, /* arg = msm_camera_csic_params */
+	NOTIFY_VFE_BUF_FREE_EVT, /* arg = msm_camera_csic_params */
+	NOTIFY_VFE_IRQ,
+	NOTIFY_AXI_IRQ,
+	NOTIFY_GESTURE_EVT, /* arg = v4l2_event */
+	NOTIFY_GESTURE_CAM_EVT, /* arg = int */
+	NOTIFY_INVALID
+};
+
+enum isp_vfe_cmd_id {
+	/*
+	*Important! Command_ID are arranged in order.
+	*Don't change!*/
+	ISP_VFE_CMD_ID_STREAM_ON,
+	ISP_VFE_CMD_ID_STREAM_OFF,
+	ISP_VFE_CMD_ID_FRAME_BUF_RELEASE
+};
+
+struct msm_cam_v4l2_device;
+struct msm_cam_v4l2_dev_inst;
+#define MSM_MAX_IMG_MODE                8
+
+enum msm_buffer_state {
+	MSM_BUFFER_STATE_UNUSED,
+	MSM_BUFFER_STATE_INITIALIZED,
+	MSM_BUFFER_STATE_PREPARED,
+	MSM_BUFFER_STATE_QUEUED,
+	MSM_BUFFER_STATE_RESERVED,
+	MSM_BUFFER_STATE_DEQUEUED
+};
+
+/* buffer for one video frame */
+struct msm_frame_buffer {
+	/* common v4l buffer stuff -- must be first */
+	struct vb2_buffer         vidbuf;
+	struct list_head		  list;
+	enum v4l2_mbus_pixelcode  pxlcode;
+	enum msm_buffer_state state;
+	int active;
+};
+
+struct msm_isp_color_fmt {
+	char *name;
+	int depth;
+	int bitsperpxl;
+	u32 fourcc;
+	enum v4l2_mbus_pixelcode pxlcode;
+	enum v4l2_colorspace colorspace;
+};
+
+struct msm_mctl_pp_frame_info {
+	int user_cmd;
+	struct msm_pp_frame src_frame;
+	struct msm_pp_frame dest_frame;
+	struct msm_mctl_pp_frame_cmd pp_frame_cmd;
+};
+
+struct msm_mctl_pp_ctrl {
+	int pp_msg_type;
+	struct msm_mctl_pp_frame_info *pp_frame_info;
+
+};
+struct msm_mctl_pp_info {
+	spinlock_t lock;
+	uint32_t cnt;
+	uint32_t pp_key;
+	uint32_t cur_frame_id[MSM_MAX_IMG_MODE];
+	struct msm_free_buf div_frame[MSM_MAX_IMG_MODE];
+	struct msm_mctl_pp_ctrl pp_ctrl;
+
+};
+/* "Media Controller" represents a camera steaming session,
+ * which consists of a "sensor" device and an "isp" device
+ * (such as VFE, if needed), connected via an "IO" device,
+ * (such as IPIF on 8960, or none on 8660) plus other extra
+ * sub devices such as VPE and flash.
+ */
+
+struct msm_cam_media_controller {
+
+	int (*mctl_open)(struct msm_cam_media_controller *p_mctl,
+					 const char *const apps_id);
+	int (*mctl_cb)(void);
+	int (*mctl_cmd)(struct msm_cam_media_controller *p_mctl,
+					unsigned int cmd, unsigned long arg);
+	int (*mctl_release)(struct msm_cam_media_controller *p_mctl);
+	int (*mctl_buf_init)(struct msm_cam_v4l2_dev_inst *pcam);
+	int (*mctl_vbqueue_init)(struct msm_cam_v4l2_dev_inst *pcam,
+				struct vb2_queue *q, enum v4l2_buf_type type);
+	int (*mctl_ufmt_init)(struct msm_cam_media_controller *p_mctl);
+
+	/* the following reflect the HW topology information*/
+	struct v4l2_subdev *sensor_sdev; /* sensor sub device */
+	struct v4l2_subdev *act_sdev; /* actuator sub device */
+	struct v4l2_subdev *csiphy_sdev; /*csiphy sub device*/
+	struct v4l2_subdev *csid_sdev; /*csid sub device*/
+	struct v4l2_subdev *csic_sdev; /*csid sub device*/
+	struct v4l2_subdev *ispif_sdev; /* ispif sub device */
+	struct v4l2_subdev *gemini_sdev; /* gemini sub device */
+	struct v4l2_subdev *vpe_sdev; /* vpe sub device */
+	struct v4l2_subdev *axi_sdev; /* axi sub device */
+	struct v4l2_subdev *eeprom_sdev; /* eeprom sub device */
+
+	struct msm_isp_ops *isp_sdev;    /* isp sub device : camif/VFE */
+	struct msm_cam_config_dev *config_device;
+
+	/*mctl session control information*/
+	uint8_t opencnt; /*mctl ref count*/
+	const char *apps_id; /*ID for app that open this session*/
+	struct mutex lock;
+	struct wake_lock wake_lock; /*avoid low power mode when active*/
+	struct pm_qos_request pm_qos_req_list;
+	struct msm_mctl_pp_info pp_info;
+	struct msm_mctl_stats_t stats_info; /*stats pmem info*/
+	uint32_t vfe_output_mode; /* VFE output mode */
+	struct ion_client *client;
+	struct kref refcount;
+
+	/*pcam ptr*/
+	struct msm_cam_v4l2_device *pcam_ptr;
+
+	/*sensor info*/
+	struct msm_camera_sensor_info *sdata;
+};
+
+/* abstract camera device represents a VFE and connected sensor */
+struct msm_isp_ops {
+	char *config_dev_name;
+
+	/*int (*isp_init)(struct msm_cam_v4l2_device *pcam);*/
+	int (*isp_open)(struct v4l2_subdev *sd,
+		struct msm_cam_media_controller *mctl);
+	int (*isp_config)(struct msm_cam_media_controller *pmctl,
+		 unsigned int cmd, unsigned long arg);
+	int (*isp_notify)(struct v4l2_subdev *sd,
+		unsigned int notification, void *arg);
+	void (*isp_release)(struct v4l2_subdev *sd);
+	int (*isp_pp_cmd)(struct msm_cam_media_controller *pmctl,
+		 struct msm_mctl_pp_cmd, void *data);
+
+	/* vfe subdevice */
+	struct v4l2_subdev *sd;
+	struct v4l2_subdev *sd_vpe;
+};
+
+struct msm_isp_buf_info {
+	int type;
+	unsigned long buffer;
+	int fd;
+};
+struct msm_cam_buf_offset {
+	uint32_t addr_offset;
+	uint32_t data_offset;
+};
+
+#define MSM_DEV_INST_MAX                    16
+struct msm_cam_v4l2_dev_inst {
+	struct v4l2_fh  eventHandle;
+	struct vb2_queue vid_bufq;
+	spinlock_t vq_irqlock;
+	struct list_head free_vq;
+	struct v4l2_format vid_fmt;
+	/* sensor pixel code*/
+	enum v4l2_mbus_pixelcode sensor_pxlcode;
+	struct msm_cam_v4l2_device *pcam;
+	int my_index;
+	int image_mode;
+	int path;
+	int buf_count;
+	/* buffer offsets, if any */
+	struct msm_cam_buf_offset **buf_offset;
+	struct v4l2_crop crop;
+	int streamon;
+	struct msm_mem_map_info mem_map;
+	int is_mem_map_inst;
+	struct img_plane_info plane_info;
+	int vbqueue_initialized;
+};
+
+struct msm_cam_mctl_node {
+	/* MCTL V4l2 device */
+	struct v4l2_device v4l2_dev;
+	struct video_device *pvdev;
+	struct msm_cam_v4l2_dev_inst *dev_inst[MSM_DEV_INST_MAX];
+	struct msm_cam_v4l2_dev_inst *dev_inst_map[MSM_MAX_IMG_MODE];
+	struct mutex dev_lock;
+	int active;
+	int use_count;
+};
+
+/* abstract camera device for each sensor successfully probed*/
+struct msm_cam_v4l2_device {
+
+	/* device node information */
+	int vnode_id;
+	struct v4l2_device v4l2_dev; /* V4l2 device */
+	struct video_device *pvdev; /* registered as /dev/video*/
+	struct msm_cam_mctl_node mctl_node; /* node for buffer management */
+	struct media_device media_dev; /* node to get video node info*/
+
+	/* device session information */
+	int use_count;
+	struct mutex vid_lock;
+	uint32_t server_queue_idx;
+	uint32_t mctl_handle;
+	struct msm_cam_v4l2_dev_inst *dev_inst[MSM_DEV_INST_MAX];
+	struct msm_cam_v4l2_dev_inst *dev_inst_map[MSM_MAX_IMG_MODE];
+	int op_mode;
+
+	/* v4l2 format support */
+	struct msm_isp_color_fmt *usr_fmts;
+	int num_fmts;
+
+	struct v4l2_subdev *sensor_sdev; /* sensor sub device */
+	struct v4l2_subdev *act_sdev; /* actuator sub device */
+	struct v4l2_subdev *eeprom_sdev; /* actuator sub device */
+	struct msm_camera_sensor_info *sdata;
+};
+
+static inline struct msm_cam_v4l2_device *to_pcam(
+	struct v4l2_device *v4l2_dev)
+{
+	return container_of(v4l2_dev, struct msm_cam_v4l2_device, v4l2_dev);
+}
+
+/*pseudo v4l2 device and v4l2 event queue
+  for server and config cdevs*/
+struct v4l2_queue_util {
+	struct video_device *pvdev;
+	struct v4l2_fh  eventHandle;
+};
+
+/* abstract config device for all sensor successfully probed*/
+struct msm_cam_config_dev {
+	struct cdev config_cdev;
+	struct v4l2_queue_util config_stat_event_queue;
+	int use_count;
+	/*struct msm_isp_ops* isp_subdev;*/
+	struct msm_cam_media_controller *p_mctl;
+	struct msm_mem_map_info mem_map;
+};
+
+/* 2 for camera, 1 for gesture */
+#define MAX_NUM_ACTIVE_CAMERA 3
+
+struct msm_cam_server_queue {
+	uint32_t queue_active;
+	struct msm_device_queue ctrl_q;
+	struct msm_device_queue eventData_q;
+	struct msm_ctrl_cmd *ctrl;
+	uint8_t *ctrl_data;
+	uint32_t evt_id;
+};
+
+struct msm_cam_server_mctl_inst {
+	struct msm_cam_media_controller mctl;
+	uint32_t handle;
+};
+
+/* abstract camera server device for all sensor successfully probed*/
+struct msm_cam_server_dev {
+
+	/* config node device*/
+	struct platform_device *server_pdev;
+	/* server node v4l2 device */
+	struct v4l2_device v4l2_dev;
+	struct video_device *video_dev;
+	struct media_device media_dev;
+
+	/* info of sensors successfully probed*/
+	struct msm_camera_info camera_info;
+	/* info of configs successfully created*/
+	struct msm_cam_config_dev_info config_info;
+	/* active working camera device - only one allowed at this time*/
+	struct msm_cam_v4l2_device *pcam_active;
+	/* number of camera devices opened*/
+	atomic_t number_pcam_active;
+	struct v4l2_queue_util server_command_queue;
+
+	/* This queue used by the config thread to send responses back to the
+	 * control thread.  It is accessed only from a process context.
+	 */
+	struct msm_cam_server_queue server_queue[MAX_NUM_ACTIVE_CAMERA];
+	uint32_t server_evt_id;
+
+	struct msm_cam_server_mctl_inst mctl[MAX_NUM_ACTIVE_CAMERA];
+	uint32_t mctl_handle_cnt;
+
+	int use_count;
+	/* all the registered ISP subdevice*/
+	struct msm_isp_ops *isp_subdev[MSM_MAX_CAMERA_CONFIGS];
+	/* info of MCTL nodes successfully probed*/
+	struct msm_mctl_node_info mctl_node_info;
+	struct mutex server_lock;
+	struct mutex server_queue_lock;
+	/*v4l2 subdevs*/
+	struct v4l2_subdev *csiphy_device[MAX_NUM_CSIPHY_DEV];
+	struct v4l2_subdev *csid_device[MAX_NUM_CSID_DEV];
+	struct v4l2_subdev *csic_device[MAX_NUM_CSIC_DEV];
+	struct v4l2_subdev *ispif_device;
+	struct v4l2_subdev *vfe_device[MAX_NUM_VFE_DEV];
+	struct v4l2_subdev *axi_device[MAX_NUM_AXI_DEV];
+	struct v4l2_subdev *vpe_device[MAX_NUM_VPE_DEV];
+	struct v4l2_subdev *gesture_device;
+};
+
+/* camera server related functions */
+
+
+/* ISP related functions */
+void msm_isp_vfe_dev_init(struct v4l2_subdev *vd);
+/*
+int msm_isp_register(struct msm_cam_v4l2_device *pcam);
+*/
+int msm_isp_register(struct msm_cam_server_dev *psvr);
+void msm_isp_unregister(struct msm_cam_server_dev *psvr);
+int msm_sensor_register(struct v4l2_subdev *);
+int msm_isp_init_module(int g_num_config_nodes);
+
+int msm_mctl_init(struct msm_cam_v4l2_device *pcam);
+int msm_mctl_free(struct msm_cam_v4l2_device *pcam);
+int msm_mctl_buf_init(struct msm_cam_v4l2_device *pcam);
+int msm_mctl_init_user_formats(struct msm_cam_v4l2_device *pcam);
+int msm_mctl_buf_done(struct msm_cam_media_controller *pmctl,
+			int msg_type, struct msm_free_buf *buf,
+			uint32_t frame_id);
+int msm_mctl_buf_done_pp(struct msm_cam_media_controller *pmctl,
+	int msg_type, struct msm_free_buf *frame, int dirty, int node_type);
+int msm_mctl_reserve_free_buf(struct msm_cam_media_controller *pmctl,
+				struct msm_cam_v4l2_dev_inst *pcam_inst,
+				int path, struct msm_free_buf *free_buf);
+int msm_mctl_release_free_buf(struct msm_cam_media_controller *pmctl,
+				struct msm_cam_v4l2_dev_inst *pcam_inst,
+				int path, struct msm_free_buf *free_buf);
+/*Memory(PMEM) functions*/
+int msm_register_pmem(struct hlist_head *ptype, void __user *arg,
+				struct ion_client *client);
+int msm_pmem_table_del(struct hlist_head *ptype, void __user *arg,
+				struct ion_client *client);
+int msm_pmem_region_get_phy_addr(struct hlist_head *ptype,
+	struct msm_mem_map_info *mem_map, int32_t *phyaddr);
+uint8_t msm_pmem_region_lookup(struct hlist_head *ptype,
+	int pmem_type, struct msm_pmem_region *reg, uint8_t maxcount);
+uint8_t msm_pmem_region_lookup_2(struct hlist_head *ptype,
+					int pmem_type,
+					struct msm_pmem_region *reg,
+					uint8_t maxcount);
+unsigned long msm_pmem_stats_vtop_lookup(
+				struct msm_cam_media_controller *mctl,
+				unsigned long buffer,
+				int fd);
+unsigned long msm_pmem_stats_ptov_lookup(
+	struct msm_cam_media_controller *mctl,
+	unsigned long addr, int *fd);
+
+int msm_vfe_subdev_init(struct v4l2_subdev *sd,
+			struct msm_cam_media_controller *mctl);
+void msm_vfe_subdev_release(struct v4l2_subdev *sd);
+
+int msm_isp_subdev_ioctl(struct v4l2_subdev *sd,
+	struct msm_vfe_cfg_cmd *cfgcmd, void *data);
+int msm_vpe_subdev_init(struct v4l2_subdev *sd,
+			struct msm_cam_media_controller *mctl);
+int msm_gemini_subdev_init(struct v4l2_subdev *gemini_sd);
+void msm_vpe_subdev_release(void);
+void msm_gemini_subdev_release(struct v4l2_subdev *gemini_sd);
+int msm_mctl_is_pp_msg_type(struct msm_cam_media_controller *p_mctl,
+	int msg_type);
+int msm_mctl_do_pp(struct msm_cam_media_controller *p_mctl,
+			int msg_type, uint32_t y_phy, uint32_t frame_id);
+int msm_mctl_pp_ioctl(struct msm_cam_media_controller *p_mctl,
+			unsigned int cmd, unsigned long arg);
+int msm_mctl_pp_notify(struct msm_cam_media_controller *pmctl,
+			struct msm_mctl_pp_frame_info *pp_frame_info);
+int msm_mctl_img_mode_to_inst_index(struct msm_cam_media_controller *pmctl,
+					int out_type, int node_type);
+struct msm_frame_buffer *msm_mctl_buf_find(
+	struct msm_cam_media_controller *pmctl,
+	struct msm_cam_v4l2_dev_inst *pcam_inst, int del_buf,
+	int msg_type, struct msm_free_buf *fbuf);
+void msm_mctl_gettimeofday(struct timeval *tv);
+struct msm_frame_buffer *msm_mctl_get_free_buf(
+		struct msm_cam_media_controller *pmctl,
+		int msg_type);
+int msm_mctl_put_free_buf(
+		struct msm_cam_media_controller *pmctl,
+		int msg_type, struct msm_frame_buffer *buf);
+int msm_mctl_check_pp(struct msm_cam_media_controller *p_mctl,
+		int msg_type, int *pp_divert_type, int *pp_type);
+int msm_mctl_do_pp_divert(
+	struct msm_cam_media_controller *p_mctl,
+	int msg_type, struct msm_free_buf *fbuf,
+	uint32_t frame_id, int pp_type);
+int msm_mctl_buf_del(struct msm_cam_media_controller *pmctl,
+	int msg_type,
+	struct msm_frame_buffer *my_buf);
+int msm_mctl_pp_release_free_frame(
+	struct msm_cam_media_controller *p_mctl,
+	void __user *arg);
+int msm_mctl_pp_reserve_free_frame(
+	struct msm_cam_media_controller *p_mctl,
+	void __user *arg);
+int msm_mctl_set_pp_key(struct msm_cam_media_controller *p_mctl,
+				void __user *arg);
+int msm_mctl_pp_done(
+	struct msm_cam_media_controller *p_mctl,
+	void __user *arg);
+int msm_mctl_pp_divert_done(
+	struct msm_cam_media_controller *p_mctl,
+	void __user *arg);
+int msm_setup_v4l2_event_queue(struct v4l2_fh *eventHandle,
+					struct video_device *pvdev);
+int msm_setup_mctl_node(struct msm_cam_v4l2_device *pcam);
+struct msm_cam_v4l2_dev_inst *msm_mctl_get_pcam_inst(
+		struct msm_cam_media_controller *pmctl,
+		int image_mode);
+int msm_mctl_buf_return_buf(struct msm_cam_media_controller *pmctl,
+			int image_mode, struct msm_frame_buffer *buf);
+int msm_mctl_pp_mctl_divert_done(struct msm_cam_media_controller *p_mctl,
+					void __user *arg);
+void msm_release_ion_client(struct kref *ref);
+int msm_cam_register_subdev_node(struct v4l2_subdev *sd,
+			enum msm_cam_subdev_type sdev_type, uint8_t index);
+uint32_t msm_camera_get_mctl_handle(void);
+struct msm_cam_media_controller *msm_camera_get_mctl(uint32_t handle);
+void msm_camera_free_mctl(uint32_t handle);
+int msm_server_open_client(int *p_qidx);
+int msm_server_send_ctrl(struct msm_ctrl_cmd *out, int ctrl_id);
+int msm_server_close_client(int idx);
+int msm_cam_server_open_mctl_session(struct msm_cam_v4l2_device *pcam,
+	int *p_active);
+int msm_cam_server_close_mctl_session(struct msm_cam_v4l2_device *pcam);
+#endif /* __KERNEL__ */
+
+#endif /* _MSM_H */
diff --git a/drivers/media/video/msm/msm_axi_qos.c b/drivers/media/video/msm/msm_axi_qos.c
new file mode 100644
index 0000000..eaceceb
--- /dev/null
+++ b/drivers/media/video/msm/msm_axi_qos.c
@@ -0,0 +1,50 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/clk.h>
+#include <mach/camera.h>
+#define MSM_AXI_QOS_NAME "msm_camera"
+
+static struct clk *ebi1_clk;
+
+int add_axi_qos(void)
+{
+	ebi1_clk = clk_get(NULL, "ebi1_vfe_clk");
+	if (IS_ERR(ebi1_clk))
+		ebi1_clk = NULL;
+	else {
+		clk_prepare(ebi1_clk);
+		clk_enable(ebi1_clk);
+	}
+
+	return 0;
+}
+
+int update_axi_qos(uint32_t rate)
+{
+	if (!ebi1_clk)
+		return 0;
+
+	return clk_set_rate(ebi1_clk, rate * 1000);
+}
+
+void release_axi_qos(void)
+{
+	if (!ebi1_clk)
+		return;
+
+	clk_disable(ebi1_clk);
+	clk_unprepare(ebi1_clk);
+	clk_put(ebi1_clk);
+	ebi1_clk = NULL;
+}
diff --git a/drivers/media/video/msm/msm_camera.c b/drivers/media/video/msm/msm_camera.c
new file mode 100644
index 0000000..e609d6e
--- /dev/null
+++ b/drivers/media/video/msm/msm_camera.c
@@ -0,0 +1,4124 @@
+/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+//FIXME: most allocations need not be GFP_ATOMIC
+/* FIXME: management of mutexes */
+/* FIXME: msm_pmem_region_lookup return values */
+/* FIXME: way too many copy to/from user */
+/* FIXME: does region->active mean free */
+/* FIXME: check limits on command lenghts passed from userspace */
+/* FIXME: __msm_release: which queues should we flush when opencnt != 0 */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <mach/board.h>
+
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/uaccess.h>
+#include <linux/android_pmem.h>
+#include <linux/poll.h>
+#include <media/msm_camera.h>
+#include <mach/camera.h>
+#include <linux/syscalls.h>
+#include <linux/hrtimer.h>
+#include <linux/ion.h>
+DEFINE_MUTEX(ctrl_cmd_lock);
+
+#define CAMERA_STOP_VIDEO 58
+spinlock_t pp_prev_spinlock;
+spinlock_t pp_stereocam_spinlock;
+spinlock_t st_frame_spinlock;
+
+#define ERR_USER_COPY(to) pr_err("%s(%d): copy %s user\n", \
+				__func__, __LINE__, ((to) ? "to" : "from"))
+#define ERR_COPY_FROM_USER() ERR_USER_COPY(0)
+#define ERR_COPY_TO_USER() ERR_USER_COPY(1)
+#define MAX_PMEM_CFG_BUFFERS 10
+
+static struct class *msm_class;
+static dev_t msm_devno;
+static LIST_HEAD(msm_sensors);
+struct  msm_control_device *g_v4l2_control_device;
+int g_v4l2_opencnt;
+static int camera_node;
+static enum msm_camera_type camera_type[MSM_MAX_CAMERA_SENSORS];
+static uint32_t sensor_mount_angle[MSM_MAX_CAMERA_SENSORS];
+
+struct ion_client *client_for_ion;
+
+static const char *vfe_config_cmd[] = {
+	"CMD_GENERAL",  /* 0 */
+	"CMD_AXI_CFG_OUT1",
+	"CMD_AXI_CFG_SNAP_O1_AND_O2",
+	"CMD_AXI_CFG_OUT2",
+	"CMD_PICT_T_AXI_CFG",
+	"CMD_PICT_M_AXI_CFG",  /* 5 */
+	"CMD_RAW_PICT_AXI_CFG",
+	"CMD_FRAME_BUF_RELEASE",
+	"CMD_PREV_BUF_CFG",
+	"CMD_SNAP_BUF_RELEASE",
+	"CMD_SNAP_BUF_CFG",  /* 10 */
+	"CMD_STATS_DISABLE",
+	"CMD_STATS_AEC_AWB_ENABLE",
+	"CMD_STATS_AF_ENABLE",
+	"CMD_STATS_AEC_ENABLE",
+	"CMD_STATS_AWB_ENABLE",  /* 15 */
+	"CMD_STATS_ENABLE",
+	"CMD_STATS_AXI_CFG",
+	"CMD_STATS_AEC_AXI_CFG",
+	"CMD_STATS_AF_AXI_CFG",
+	"CMD_STATS_AWB_AXI_CFG",  /* 20 */
+	"CMD_STATS_RS_AXI_CFG",
+	"CMD_STATS_CS_AXI_CFG",
+	"CMD_STATS_IHIST_AXI_CFG",
+	"CMD_STATS_SKIN_AXI_CFG",
+	"CMD_STATS_BUF_RELEASE",  /* 25 */
+	"CMD_STATS_AEC_BUF_RELEASE",
+	"CMD_STATS_AF_BUF_RELEASE",
+	"CMD_STATS_AWB_BUF_RELEASE",
+	"CMD_STATS_RS_BUF_RELEASE",
+	"CMD_STATS_CS_BUF_RELEASE",  /* 30 */
+	"CMD_STATS_IHIST_BUF_RELEASE",
+	"CMD_STATS_SKIN_BUF_RELEASE",
+	"UPDATE_STATS_INVALID",
+	"CMD_AXI_CFG_SNAP_GEMINI",
+	"CMD_AXI_CFG_SNAP",  /* 35 */
+	"CMD_AXI_CFG_PREVIEW",
+	"CMD_AXI_CFG_VIDEO",
+	"CMD_STATS_IHIST_ENABLE",
+	"CMD_STATS_RS_ENABLE",
+	"CMD_STATS_CS_ENABLE",  /* 40 */
+	"CMD_VPE",
+	"CMD_AXI_CFG_VPE",
+	"CMD_AXI_CFG_SNAP_VPE",
+	"CMD_AXI_CFG_SNAP_THUMB_VPE",
+};
+#define __CONTAINS(r, v, l, field) ({				\
+	typeof(r) __r = r;					\
+	typeof(v) __v = v;					\
+	typeof(v) __e = __v + l;				\
+	int res = __v >= __r->field &&				\
+		__e <= __r->field + __r->len;			\
+	res;							\
+})
+
+#define CONTAINS(r1, r2, field) ({				\
+	typeof(r2) __r2 = r2;					\
+	__CONTAINS(r1, __r2->field, __r2->len, field);		\
+})
+
+#define IN_RANGE(r, v, field) ({				\
+	typeof(r) __r = r;					\
+	typeof(v) __vv = v;					\
+	int res = ((__vv >= __r->field) &&			\
+		(__vv < (__r->field + __r->len)));		\
+	res;							\
+})
+
+#define OVERLAPS(r1, r2, field) ({				\
+	typeof(r1) __r1 = r1;					\
+	typeof(r2) __r2 = r2;					\
+	typeof(__r2->field) __v = __r2->field;			\
+	typeof(__v) __e = __v + __r2->len - 1;			\
+	int res = (IN_RANGE(__r1, __v, field) ||		\
+		   IN_RANGE(__r1, __e, field));                 \
+	res;							\
+})
+
+static inline void free_qcmd(struct msm_queue_cmd *qcmd)
+{
+	if (!qcmd || !atomic_read(&qcmd->on_heap))
+		return;
+	if (!atomic_sub_return(1, &qcmd->on_heap))
+		kfree(qcmd);
+}
+
+static void msm_region_init(struct msm_sync *sync)
+{
+	INIT_HLIST_HEAD(&sync->pmem_frames);
+	INIT_HLIST_HEAD(&sync->pmem_stats);
+	spin_lock_init(&sync->pmem_frame_spinlock);
+	spin_lock_init(&sync->pmem_stats_spinlock);
+}
+
+static void msm_queue_init(struct msm_device_queue *queue, const char *name)
+{
+	spin_lock_init(&queue->lock);
+	queue->len = 0;
+	queue->max = 0;
+	queue->name = name;
+	INIT_LIST_HEAD(&queue->list);
+	init_waitqueue_head(&queue->wait);
+}
+
+static void msm_enqueue(struct msm_device_queue *queue,
+		struct list_head *entry)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&queue->lock, flags);
+	queue->len++;
+	if (queue->len > queue->max) {
+		queue->max = queue->len;
+		CDBG("%s: queue %s new max is %d\n", __func__,
+			queue->name, queue->max);
+	}
+	list_add_tail(entry, &queue->list);
+	wake_up(&queue->wait);
+	CDBG("%s: woke up %s\n", __func__, queue->name);
+	spin_unlock_irqrestore(&queue->lock, flags);
+}
+
+static void msm_enqueue_vpe(struct msm_device_queue *queue,
+		struct list_head *entry)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&queue->lock, flags);
+	queue->len++;
+	if (queue->len > queue->max) {
+		queue->max = queue->len;
+		CDBG("%s: queue %s new max is %d\n", __func__,
+			queue->name, queue->max);
+	}
+	list_add_tail(entry, &queue->list);
+    CDBG("%s: woke up %s\n", __func__, queue->name);
+	spin_unlock_irqrestore(&queue->lock, flags);
+}
+
+#define msm_dequeue(queue, member) ({				\
+	unsigned long flags;					\
+	struct msm_device_queue *__q = (queue);			\
+	struct msm_queue_cmd *qcmd = 0;				\
+	spin_lock_irqsave(&__q->lock, flags);			\
+	if (!list_empty(&__q->list)) {				\
+		__q->len--;					\
+		qcmd = list_first_entry(&__q->list,		\
+				struct msm_queue_cmd, member);	\
+		if ((qcmd) && (&qcmd->member) && (&qcmd->member.next))	\
+			list_del_init(&qcmd->member);			\
+	}							\
+	spin_unlock_irqrestore(&__q->lock, flags);	\
+	qcmd;							\
+})
+
+#define msm_delete_entry(queue, member, q_cmd) ({		\
+	unsigned long flags;					\
+	struct msm_device_queue *__q = (queue);			\
+	struct msm_queue_cmd *qcmd = 0;				\
+	spin_lock_irqsave(&__q->lock, flags);			\
+	if (!list_empty(&__q->list)) {				\
+		list_for_each_entry(qcmd, &__q->list, member)	\
+		if (qcmd == q_cmd) {				\
+			__q->len--;				\
+			list_del_init(&qcmd->member);		\
+			CDBG("msm_delete_entry, match found\n");\
+			kfree(q_cmd);				\
+			q_cmd = NULL;				\
+			break;					\
+		}						\
+	}							\
+	spin_unlock_irqrestore(&__q->lock, flags);		\
+	q_cmd;		\
+})
+
+#define msm_queue_drain(queue, member) do {			\
+	unsigned long flags;					\
+	struct msm_device_queue *__q = (queue);			\
+	struct msm_queue_cmd *qcmd;				\
+	spin_lock_irqsave(&__q->lock, flags);			\
+	while (!list_empty(&__q->list)) {			\
+		__q->len--;					\
+		qcmd = list_first_entry(&__q->list,		\
+			struct msm_queue_cmd, member);		\
+		if (qcmd) {					\
+			if (&qcmd->member)	\
+				list_del_init(&qcmd->member);		\
+			free_qcmd(qcmd);				\
+		}							\
+	}							\
+	spin_unlock_irqrestore(&__q->lock, flags);		\
+} while (0)
+
+static int check_overlap(struct hlist_head *ptype,
+			unsigned long paddr,
+			unsigned long len)
+{
+	struct msm_pmem_region *region;
+	struct msm_pmem_region t = { .paddr = paddr, .len = len };
+	struct hlist_node *node;
+
+	hlist_for_each_entry(region, node, ptype, list) {
+		if (CONTAINS(region, &t, paddr) ||
+				CONTAINS(&t, region, paddr) ||
+				OVERLAPS(region, &t, paddr)) {
+			CDBG(" region (PHYS %p len %ld)"
+				" clashes with registered region"
+				" (paddr %p len %ld)\n",
+				(void *)t.paddr, t.len,
+				(void *)region->paddr, region->len);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int check_pmem_info(struct msm_pmem_info *info, int len)
+{
+	if (info->offset < len &&
+	    info->offset + info->len <= len &&
+	    info->planar0_off < len &&
+	    info->planar1_off < len &&
+	    info->planar2_off < len)
+		return 0;
+
+	pr_err("%s: check failed: off %d len %d y 0x%x cbcr_p1 0x%x p2_add 0x%x(total len %d)\n",
+		__func__,
+		info->offset,
+		info->len,
+		info->planar0_off,
+		info->planar1_off,
+		info->planar2_off,
+		len);
+	return -EINVAL;
+}
+static int msm_pmem_table_add(struct hlist_head *ptype,
+	struct msm_pmem_info *info, spinlock_t* pmem_spinlock,
+	struct msm_sync *sync)
+{
+	unsigned long paddr;
+#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION
+	struct file *file;
+	unsigned long kvstart;
+#endif
+	unsigned long len;
+	int rc = -ENOMEM;
+	struct msm_pmem_region *region;
+	unsigned long flags;
+
+	region = kmalloc(sizeof(struct msm_pmem_region), GFP_KERNEL);
+	if (!region)
+		goto out;
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+		region->handle = ion_import_fd(client_for_ion, info->fd);
+		if (IS_ERR_OR_NULL(region->handle))
+			goto out1;
+		ion_phys(client_for_ion, region->handle,
+			&paddr, (size_t *)&len);
+#else
+	rc = get_pmem_file(info->fd, &paddr, &kvstart, &len, &file);
+	if (rc < 0) {
+		pr_err("%s: get_pmem_file fd %d error %d\n",
+			__func__,
+			info->fd, rc);
+		goto out1;
+	}
+	region->file = file;
+#endif
+	if (!info->len)
+		info->len = len;
+
+	rc = check_pmem_info(info, len);
+	if (rc < 0)
+		goto out2;
+
+	paddr += info->offset;
+	len = info->len;
+
+	spin_lock_irqsave(pmem_spinlock, flags);
+	if (check_overlap(ptype, paddr, len) < 0) {
+		spin_unlock_irqrestore(pmem_spinlock, flags);
+		rc = -EINVAL;
+		goto out2;
+	}
+	spin_unlock_irqrestore(pmem_spinlock, flags);
+
+	spin_lock_irqsave(pmem_spinlock, flags);
+	INIT_HLIST_NODE(&region->list);
+
+	region->paddr = paddr;
+	region->len = len;
+	memcpy(&region->info, info, sizeof(region->info));
+
+	hlist_add_head(&(region->list), ptype);
+	spin_unlock_irqrestore(pmem_spinlock, flags);
+	CDBG("%s: type %d, paddr 0x%lx, vaddr 0x%lx p0_add = 0x%x"
+		"p1_addr = 0x%x p2_addr = 0x%x\n",
+		__func__, info->type, paddr, (unsigned long)info->vaddr,
+		info->planar0_off, info->planar1_off, info->planar2_off);
+	return 0;
+out2:
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+	ion_free(client_for_ion, region->handle);
+#else
+	put_pmem_file(region->file);
+#endif
+out1:
+	kfree(region);
+out:
+	return rc;
+}
+
+/* return of 0 means failure */
+static uint8_t msm_pmem_region_lookup(struct hlist_head *ptype,
+	int pmem_type, struct msm_pmem_region *reg, uint8_t maxcount,
+	spinlock_t *pmem_spinlock)
+{
+	struct msm_pmem_region *region;
+	struct msm_pmem_region *regptr;
+	struct hlist_node *node, *n;
+	unsigned long flags = 0;
+
+	uint8_t rc = 0;
+
+	regptr = reg;
+	spin_lock_irqsave(pmem_spinlock, flags);
+	hlist_for_each_entry_safe(region, node, n, ptype, list) {
+		if (region->info.type == pmem_type && region->info.active) {
+			*regptr = *region;
+			rc += 1;
+			if (rc >= maxcount)
+				break;
+			regptr++;
+		}
+	}
+	spin_unlock_irqrestore(pmem_spinlock, flags);
+	/* After lookup failure, dump all the list entries...*/
+	if (rc == 0) {
+		pr_err("%s: pmem_type = %d\n", __func__, pmem_type);
+		hlist_for_each_entry_safe(region, node, n, ptype, list) {
+			pr_err("listed region->info.type = %d, active = %d",
+				region->info.type, region->info.active);
+		}
+
+	}
+	return rc;
+}
+
+static uint8_t msm_pmem_region_lookup_2(struct hlist_head *ptype,
+					int pmem_type,
+					struct msm_pmem_region *reg,
+					uint8_t maxcount,
+					spinlock_t *pmem_spinlock)
+{
+	struct msm_pmem_region *region;
+	struct msm_pmem_region *regptr;
+	struct hlist_node *node, *n;
+	uint8_t rc = 0;
+	unsigned long flags = 0;
+	regptr = reg;
+	spin_lock_irqsave(pmem_spinlock, flags);
+	hlist_for_each_entry_safe(region, node, n, ptype, list) {
+		CDBG("%s:info.type=%d, pmem_type = %d,"
+						"info.active = %d\n",
+		__func__, region->info.type, pmem_type, region->info.active);
+
+		if (region->info.type == pmem_type && region->info.active) {
+			CDBG("%s:info.type=%d, pmem_type = %d,"
+							"info.active = %d,\n",
+				__func__, region->info.type, pmem_type,
+				region->info.active);
+			*regptr = *region;
+			region->info.type = MSM_PMEM_VIDEO;
+			rc += 1;
+			if (rc >= maxcount)
+				break;
+			regptr++;
+		}
+	}
+	spin_unlock_irqrestore(pmem_spinlock, flags);
+	return rc;
+}
+
+static int msm_pmem_frame_ptov_lookup(struct msm_sync *sync,
+		unsigned long p0addr,
+		unsigned long p1addr,
+		unsigned long p2addr,
+		struct msm_pmem_info *pmem_info,
+		int clear_active)
+{
+	struct msm_pmem_region *region;
+	struct hlist_node *node, *n;
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&sync->pmem_frame_spinlock, flags);
+	hlist_for_each_entry_safe(region, node, n, &sync->pmem_frames, list) {
+		if (p0addr == (region->paddr + region->info.planar0_off) &&
+			p1addr == (region->paddr + region->info.planar1_off) &&
+			p2addr == (region->paddr + region->info.planar2_off) &&
+			region->info.active) {
+			/* offset since we could pass vaddr inside
+			 * a registerd pmem buffer
+			 */
+			memcpy(pmem_info, &region->info, sizeof(*pmem_info));
+			if (clear_active)
+				region->info.active = 0;
+			spin_unlock_irqrestore(&sync->pmem_frame_spinlock,
+				flags);
+			return 0;
+		}
+	}
+	/* After lookup failure, dump all the list entries... */
+	pr_err("%s, for plane0 addr = 0x%lx, plane1 addr = 0x%lx  plane2 addr = 0x%lx\n",
+			__func__, p0addr, p1addr, p2addr);
+	hlist_for_each_entry_safe(region, node, n, &sync->pmem_frames, list) {
+		pr_err("listed p0addr 0x%lx, p1addr 0x%lx, p2addr 0x%lx, active = %d",
+				(region->paddr + region->info.planar0_off),
+				(region->paddr + region->info.planar1_off),
+				(region->paddr + region->info.planar2_off),
+				region->info.active);
+	}
+
+	spin_unlock_irqrestore(&sync->pmem_frame_spinlock, flags);
+	return -EINVAL;
+}
+
+static int msm_pmem_frame_ptov_lookup2(struct msm_sync *sync,
+		unsigned long p0_phy,
+		struct msm_pmem_info *pmem_info,
+		int clear_active)
+{
+	struct msm_pmem_region *region;
+	struct hlist_node *node, *n;
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&sync->pmem_frame_spinlock, flags);
+	hlist_for_each_entry_safe(region, node, n, &sync->pmem_frames, list) {
+		if (p0_phy == (region->paddr + region->info.planar0_off) &&
+				region->info.active) {
+			/* offset since we could pass vaddr inside
+			 * a registerd pmem buffer
+			 */
+			memcpy(pmem_info, &region->info, sizeof(*pmem_info));
+			if (clear_active)
+				region->info.active = 0;
+			spin_unlock_irqrestore(&sync->pmem_frame_spinlock,
+				flags);
+			return 0;
+		}
+	}
+
+	spin_unlock_irqrestore(&sync->pmem_frame_spinlock, flags);
+	return -EINVAL;
+}
+
+static unsigned long msm_pmem_stats_ptov_lookup(struct msm_sync *sync,
+		unsigned long addr, int *fd)
+{
+	struct msm_pmem_region *region;
+	struct hlist_node *node, *n;
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&sync->pmem_stats_spinlock, flags);
+	hlist_for_each_entry_safe(region, node, n, &sync->pmem_stats, list) {
+		if (addr == region->paddr && region->info.active) {
+			/* offset since we could pass vaddr inside a
+			 * registered pmem buffer */
+			*fd = region->info.fd;
+			region->info.active = 0;
+			spin_unlock_irqrestore(&sync->pmem_stats_spinlock,
+				flags);
+			return (unsigned long)(region->info.vaddr);
+		}
+	}
+	/* After lookup failure, dump all the list entries... */
+	pr_err("%s, lookup failure, for paddr 0x%lx\n",
+			__func__, addr);
+	hlist_for_each_entry_safe(region, node, n, &sync->pmem_stats, list) {
+		pr_err("listed paddr 0x%lx, active = %d",
+				region->paddr,
+				region->info.active);
+	}
+	spin_unlock_irqrestore(&sync->pmem_stats_spinlock, flags);
+
+	return 0;
+}
+
+static unsigned long msm_pmem_frame_vtop_lookup(struct msm_sync *sync,
+		unsigned long buffer, uint32_t p0_off, uint32_t p1_off,
+		uint32_t p2_off, int fd, int change_flag)
+{
+	struct msm_pmem_region *region;
+	struct hlist_node *node, *n;
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&sync->pmem_frame_spinlock, flags);
+	hlist_for_each_entry_safe(region,
+		node, n, &sync->pmem_frames, list) {
+		if (((unsigned long)(region->info.vaddr) == buffer) &&
+				(region->info.planar0_off == p0_off) &&
+				(region->info.planar1_off == p1_off) &&
+				(region->info.planar2_off == p2_off) &&
+				(region->info.fd == fd) &&
+				(region->info.active == 0)) {
+			if (change_flag)
+				region->info.active = 1;
+			spin_unlock_irqrestore(&sync->pmem_frame_spinlock,
+				flags);
+			return region->paddr;
+		}
+	}
+	/* After lookup failure, dump all the list entries... */
+	pr_err("%s, failed for vaddr 0x%lx, p0_off %d p1_off %d\n",
+			__func__, buffer, p0_off, p1_off);
+	hlist_for_each_entry_safe(region, node, n, &sync->pmem_frames, list) {
+		pr_err("%s, listed vaddr 0x%lx, r_p0 = 0x%x p0_off 0x%x"
+			"r_p1 = 0x%x, p1_off 0x%x, r_p2 = 0x%x, p2_off = 0x%x"
+			" active = %d\n", __func__, buffer,
+			region->info.planar0_off,
+			p0_off, region->info.planar1_off,
+			p1_off, region->info.planar2_off, p2_off,
+			region->info.active);
+	}
+
+	spin_unlock_irqrestore(&sync->pmem_frame_spinlock, flags);
+
+	return 0;
+}
+
+static unsigned long msm_pmem_stats_vtop_lookup(
+		struct msm_sync *sync,
+		unsigned long buffer,
+		int fd)
+{
+	struct msm_pmem_region *region;
+	struct hlist_node *node, *n;
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&sync->pmem_stats_spinlock, flags);
+	hlist_for_each_entry_safe(region, node, n, &sync->pmem_stats, list) {
+		if (((unsigned long)(region->info.vaddr) == buffer) &&
+				(region->info.fd == fd) &&
+				region->info.active == 0) {
+			region->info.active = 1;
+			spin_unlock_irqrestore(&sync->pmem_stats_spinlock,
+				flags);
+			return region->paddr;
+		}
+	}
+	/* After lookup failure, dump all the list entries... */
+	pr_err("%s,look up error for vaddr %ld\n",
+			__func__, buffer);
+	hlist_for_each_entry_safe(region, node, n, &sync->pmem_stats, list) {
+		pr_err("listed vaddr 0x%p, active = %d",
+				region->info.vaddr,
+				region->info.active);
+	}
+	spin_unlock_irqrestore(&sync->pmem_stats_spinlock, flags);
+
+	return 0;
+}
+
+static int __msm_pmem_table_del(struct msm_sync *sync,
+		struct msm_pmem_info *pinfo)
+{
+	int rc = 0;
+	struct msm_pmem_region *region;
+	struct hlist_node *node, *n;
+	unsigned long flags = 0;
+
+	switch (pinfo->type) {
+	case MSM_PMEM_PREVIEW:
+	case MSM_PMEM_THUMBNAIL:
+	case MSM_PMEM_MAINIMG:
+	case MSM_PMEM_RAW_MAINIMG:
+	case MSM_PMEM_C2D:
+	case MSM_PMEM_MAINIMG_VPE:
+	case MSM_PMEM_THUMBNAIL_VPE:
+		spin_lock_irqsave(&sync->pmem_frame_spinlock, flags);
+		hlist_for_each_entry_safe(region, node, n,
+			&sync->pmem_frames, list) {
+
+			if (pinfo->type == region->info.type &&
+					pinfo->vaddr == region->info.vaddr &&
+					pinfo->fd == region->info.fd) {
+				hlist_del(node);
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+				ion_free(client_for_ion, region->handle);
+#else
+				put_pmem_file(region->file);
+#endif
+				kfree(region);
+				CDBG("%s: type %d, vaddr  0x%p\n",
+					__func__, pinfo->type, pinfo->vaddr);
+			}
+		}
+		spin_unlock_irqrestore(&sync->pmem_frame_spinlock, flags);
+		break;
+
+	case MSM_PMEM_VIDEO:
+	case MSM_PMEM_VIDEO_VPE:
+		spin_lock_irqsave(&sync->pmem_frame_spinlock, flags);
+		hlist_for_each_entry_safe(region, node, n,
+			&sync->pmem_frames, list) {
+
+			if (((region->info.type == MSM_PMEM_VIDEO) ||
+				(region->info.type == MSM_PMEM_VIDEO_VPE)) &&
+				pinfo->vaddr == region->info.vaddr &&
+				pinfo->fd == region->info.fd) {
+				hlist_del(node);
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+				ion_free(client_for_ion, region->handle);
+#else
+				put_pmem_file(region->file);
+#endif
+				kfree(region);
+				CDBG("%s: type %d, vaddr  0x%p\n",
+					__func__, pinfo->type, pinfo->vaddr);
+			}
+		}
+		spin_unlock_irqrestore(&sync->pmem_frame_spinlock, flags);
+		break;
+
+	case MSM_PMEM_AEC_AWB:
+	case MSM_PMEM_AF:
+		spin_lock_irqsave(&sync->pmem_stats_spinlock, flags);
+		hlist_for_each_entry_safe(region, node, n,
+			&sync->pmem_stats, list) {
+
+			if (pinfo->type == region->info.type &&
+					pinfo->vaddr == region->info.vaddr &&
+					pinfo->fd == region->info.fd) {
+				hlist_del(node);
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+				ion_free(client_for_ion, region->handle);
+#else
+				put_pmem_file(region->file);
+#endif
+				kfree(region);
+				CDBG("%s: type %d, vaddr  0x%p\n",
+					__func__, pinfo->type, pinfo->vaddr);
+			}
+		}
+		spin_unlock_irqrestore(&sync->pmem_stats_spinlock, flags);
+		break;
+
+	default:
+		rc = -EINVAL;
+		break;
+	}
+
+	return rc;
+}
+
+static int msm_pmem_table_del(struct msm_sync *sync, void __user *arg)
+{
+	struct msm_pmem_info info;
+
+	if (copy_from_user(&info, arg, sizeof(info))) {
+		ERR_COPY_FROM_USER();
+		return -EFAULT;
+	}
+
+	return __msm_pmem_table_del(sync, &info);
+}
+
+static int __msm_get_frame(struct msm_sync *sync,
+		struct msm_frame *frame)
+{
+	int rc = 0;
+
+	struct msm_pmem_info pmem_info;
+	struct msm_queue_cmd *qcmd = NULL;
+	struct msm_vfe_resp *vdata;
+	struct msm_vfe_phy_info *pphy;
+
+	qcmd = msm_dequeue(&sync->frame_q, list_frame);
+
+	if (!qcmd) {
+		pr_err("%s: no preview frame.\n", __func__);
+		return -EAGAIN;
+	}
+
+	if ((!qcmd->command) && (qcmd->error_code & MSM_CAMERA_ERR_MASK)) {
+		frame->error_code = qcmd->error_code;
+		pr_err("%s: fake frame with camera error code = %d\n",
+			__func__, frame->error_code);
+		goto err;
+	}
+
+	vdata = (struct msm_vfe_resp *)(qcmd->command);
+	pphy = &vdata->phy;
+	CDBG("%s, pphy->p2_phy = 0x%x\n", __func__, pphy->p2_phy);
+
+	rc = msm_pmem_frame_ptov_lookup(sync,
+			pphy->p0_phy,
+			pphy->p1_phy,
+			pphy->p2_phy,
+			&pmem_info,
+			1); /* Clear the active flag */
+
+	if (rc < 0) {
+		pr_err("%s: cannot get frame, invalid lookup address"
+		"plane0 add %x plane1 add %x plane2 add%x\n",
+		__func__,
+		pphy->p0_phy,
+		pphy->p1_phy,
+		pphy->p2_phy);
+		goto err;
+	}
+
+	frame->ts = qcmd->ts;
+	frame->buffer = (unsigned long)pmem_info.vaddr;
+	frame->planar0_off = pmem_info.planar0_off;
+	frame->planar1_off = pmem_info.planar1_off;
+	frame->planar2_off = pmem_info.planar2_off;
+	frame->fd = pmem_info.fd;
+	frame->path = vdata->phy.output_id;
+	frame->frame_id = vdata->phy.frame_id;
+	CDBG("%s: plane0 %x, plane1 %x, plane2 %x,qcmd %x, virt_addr %x\n",
+		__func__, pphy->p0_phy, pphy->p1_phy, pphy->p2_phy,
+		(int) qcmd, (int) frame->buffer);
+
+err:
+	free_qcmd(qcmd);
+	return rc;
+}
+
+static int msm_get_frame(struct msm_sync *sync, void __user *arg)
+{
+	int rc = 0;
+	struct msm_frame frame;
+
+	if (copy_from_user(&frame,
+				arg,
+				sizeof(struct msm_frame))) {
+		ERR_COPY_FROM_USER();
+		return -EFAULT;
+	}
+
+	rc = __msm_get_frame(sync, &frame);
+	if (rc < 0)
+		return rc;
+
+	mutex_lock(&sync->lock);
+	if (sync->croplen && (!sync->stereocam_enabled)) {
+		if (frame.croplen != sync->croplen) {
+			pr_err("%s: invalid frame croplen %d,"
+				"expecting %d\n",
+				__func__,
+				frame.croplen,
+				sync->croplen);
+			mutex_unlock(&sync->lock);
+			return -EINVAL;
+		}
+
+		if (copy_to_user((void *)frame.cropinfo,
+				sync->cropinfo,
+				sync->croplen)) {
+			ERR_COPY_TO_USER();
+			mutex_unlock(&sync->lock);
+			return -EFAULT;
+		}
+	}
+
+	if (sync->fdroiinfo.info) {
+		if (copy_to_user((void *)frame.roi_info.info,
+			sync->fdroiinfo.info,
+			sync->fdroiinfo.info_len)) {
+			ERR_COPY_TO_USER();
+			mutex_unlock(&sync->lock);
+			return -EFAULT;
+		}
+	}
+
+	if (sync->stereocam_enabled) {
+		frame.stcam_conv_value = sync->stcam_conv_value;
+		frame.stcam_quality_ind = sync->stcam_quality_ind;
+	}
+
+	if (copy_to_user((void *)arg,
+				&frame, sizeof(struct msm_frame))) {
+		ERR_COPY_TO_USER();
+		rc = -EFAULT;
+	}
+
+	mutex_unlock(&sync->lock);
+	CDBG("%s: got frame\n", __func__);
+
+	return rc;
+}
+
+static int msm_enable_vfe(struct msm_sync *sync, void __user *arg)
+{
+	int rc = -EIO;
+	struct camera_enable_cmd cfg;
+
+	if (copy_from_user(&cfg,
+			arg,
+			sizeof(struct camera_enable_cmd))) {
+		ERR_COPY_FROM_USER();
+		return -EFAULT;
+	}
+
+	if (sync->vfefn.vfe_enable)
+		rc = sync->vfefn.vfe_enable(&cfg);
+
+	return rc;
+}
+
+static int msm_disable_vfe(struct msm_sync *sync, void __user *arg)
+{
+	int rc = -EIO;
+	struct camera_enable_cmd cfg;
+
+	if (copy_from_user(&cfg,
+			arg,
+			sizeof(struct camera_enable_cmd))) {
+		ERR_COPY_FROM_USER();
+		return -EFAULT;
+	}
+
+	if (sync->vfefn.vfe_disable)
+		rc = sync->vfefn.vfe_disable(&cfg, NULL);
+
+	return rc;
+}
+
+static struct msm_queue_cmd *__msm_control(struct msm_sync *sync,
+		struct msm_device_queue *queue,
+		struct msm_queue_cmd *qcmd,
+		int timeout)
+{
+	int rc;
+
+	CDBG("Inside __msm_control\n");
+	if (sync->event_q.len <= 100 && sync->frame_q.len <= 100) {
+		/* wake up config thread */
+		msm_enqueue(&sync->event_q, &qcmd->list_config);
+	} else {
+		pr_err("%s, Error Queue limit exceeded e_q = %d, f_q = %d\n",
+			__func__, sync->event_q.len, sync->frame_q.len);
+		free_qcmd(qcmd);
+		return NULL;
+	}
+	if (!queue)
+		return NULL;
+
+	/* wait for config status */
+	CDBG("Waiting for config status \n");
+	rc = wait_event_interruptible_timeout(
+			queue->wait,
+			!list_empty_careful(&queue->list),
+			timeout);
+	CDBG("Waiting over for config status\n");
+	if (list_empty_careful(&queue->list)) {
+		if (!rc) {
+			rc = -ETIMEDOUT;
+			pr_err("%s: wait_event error %d\n", __func__, rc);
+			return ERR_PTR(rc);
+		} else if (rc < 0) {
+			pr_err("%s: wait_event error %d\n", __func__, rc);
+			if (msm_delete_entry(&sync->event_q,
+				list_config, qcmd)) {
+				sync->ignore_qcmd = true;
+				sync->ignore_qcmd_type =
+					(int16_t)((struct msm_ctrl_cmd *)
+					(qcmd->command))->type;
+			}
+			return ERR_PTR(rc);
+		}
+	}
+	qcmd = msm_dequeue(queue, list_control);
+	BUG_ON(!qcmd);
+	CDBG("__msm_control done \n");
+	return qcmd;
+}
+
+static struct msm_queue_cmd *__msm_control_nb(struct msm_sync *sync,
+					struct msm_queue_cmd *qcmd_to_copy)
+{
+	/* Since this is a non-blocking command, we cannot use qcmd_to_copy and
+	 * its data, since they are on the stack.  We replicate them on the heap
+	 * and mark them on_heap so that they get freed when the config thread
+	 * dequeues them.
+	 */
+
+	struct msm_ctrl_cmd *udata;
+	struct msm_ctrl_cmd *udata_to_copy = qcmd_to_copy->command;
+
+	struct msm_queue_cmd *qcmd =
+			kmalloc(sizeof(*qcmd_to_copy) +
+				sizeof(*udata_to_copy) +
+				udata_to_copy->length,
+				GFP_KERNEL);
+	if (!qcmd) {
+		pr_err("%s: out of memory\n", __func__);
+		return ERR_PTR(-ENOMEM);
+	}
+	*qcmd = *qcmd_to_copy;
+	udata = qcmd->command = qcmd + 1;
+	memcpy(udata, udata_to_copy, sizeof(*udata));
+	udata->value = udata + 1;
+	memcpy(udata->value, udata_to_copy->value, udata_to_copy->length);
+
+	atomic_set(&qcmd->on_heap, 1);
+
+	/* qcmd_resp will be set to NULL */
+	return __msm_control(sync, NULL, qcmd, 0);
+}
+
+static int msm_control(struct msm_control_device *ctrl_pmsm,
+			int block,
+			void __user *arg)
+{
+	int rc = 0;
+
+	struct msm_sync *sync = ctrl_pmsm->pmsm->sync;
+	void __user *uptr;
+	struct msm_ctrl_cmd udata_resp;
+	struct msm_queue_cmd *qcmd_resp = NULL;
+	uint8_t data[max_control_command_size];
+	struct msm_ctrl_cmd *udata;
+	struct msm_queue_cmd *qcmd =
+		kmalloc(sizeof(struct msm_queue_cmd) +
+			sizeof(struct msm_ctrl_cmd), GFP_ATOMIC);
+	if (!qcmd) {
+		pr_err("%s: out of memory\n", __func__);
+		return -ENOMEM;
+	}
+	udata = (struct msm_ctrl_cmd *)(qcmd + 1);
+	atomic_set(&(qcmd->on_heap), 1);
+	CDBG("Inside msm_control\n");
+	if (copy_from_user(udata, arg, sizeof(struct msm_ctrl_cmd))) {
+		ERR_COPY_FROM_USER();
+		rc = -EFAULT;
+		goto end;
+	}
+
+	uptr = udata->value;
+	udata->value = data;
+	qcmd->type = MSM_CAM_Q_CTRL;
+	qcmd->command = udata;
+
+	if (udata->length) {
+		if (udata->length > sizeof(data)) {
+			pr_err("%s: user data too large (%d, max is %d)\n",
+					__func__,
+					udata->length,
+					sizeof(data));
+			rc = -EIO;
+			goto end;
+		}
+		if (copy_from_user(udata->value, uptr, udata->length)) {
+			ERR_COPY_FROM_USER();
+			rc = -EFAULT;
+			goto end;
+		}
+	}
+
+	if (unlikely(!block)) {
+		qcmd_resp = __msm_control_nb(sync, qcmd);
+		goto end;
+	}
+	msm_queue_drain(&ctrl_pmsm->ctrl_q, list_control);
+	qcmd_resp = __msm_control(sync,
+				  &ctrl_pmsm->ctrl_q,
+				  qcmd, msecs_to_jiffies(10000));
+
+	/* ownership of qcmd will be transfered to event queue */
+	qcmd = NULL;
+
+	if (!qcmd_resp || IS_ERR(qcmd_resp)) {
+		/* Do not free qcmd_resp here.  If the config thread read it,
+		 * then it has already been freed, and we timed out because
+		 * we did not receive a MSM_CAM_IOCTL_CTRL_CMD_DONE.  If the
+		 * config thread itself is blocked and not dequeueing commands,
+		 * then it will either eventually unblock and process them,
+		 * or when it is killed, qcmd will be freed in
+		 * msm_release_config.
+		 */
+		rc = PTR_ERR(qcmd_resp);
+		qcmd_resp = NULL;
+		goto end;
+	}
+
+	if (qcmd_resp->command) {
+		udata_resp = *(struct msm_ctrl_cmd *)qcmd_resp->command;
+		if (udata_resp.length > 0) {
+			if (copy_to_user(uptr,
+					 udata_resp.value,
+					 udata_resp.length)) {
+				ERR_COPY_TO_USER();
+				rc = -EFAULT;
+				goto end;
+			}
+		}
+		udata_resp.value = uptr;
+
+		if (copy_to_user((void *)arg, &udata_resp,
+				sizeof(struct msm_ctrl_cmd))) {
+			ERR_COPY_TO_USER();
+			rc = -EFAULT;
+			goto end;
+		}
+	}
+
+end:
+	free_qcmd(qcmd);
+	CDBG("%s: done rc = %d\n", __func__, rc);
+	return rc;
+}
+
+/* Divert frames for post-processing by delivering them to the config thread;
+ * when post-processing is done, it will return the frame to the frame thread.
+ */
+static int msm_divert_frame(struct msm_sync *sync,
+		struct msm_vfe_resp *data,
+		struct msm_stats_event_ctrl *se)
+{
+	struct msm_pmem_info pinfo;
+	struct msm_postproc buf;
+	int rc;
+
+	CDBG("%s: Frame PP sync->pp_mask %d\n", __func__, sync->pp_mask);
+
+	if (!(sync->pp_mask & PP_PREV)  && !(sync->pp_mask & PP_SNAP)) {
+		pr_err("%s: diverting frame, not in PP_PREV or PP_SNAP!\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	rc = msm_pmem_frame_ptov_lookup(sync, data->phy.p0_phy,
+			data->phy.p1_phy, data->phy.p2_phy, &pinfo,
+			0); /* do not clear the active flag */
+
+	if (rc < 0) {
+		pr_err("%s: msm_pmem_frame_ptov_lookup failed\n", __func__);
+		return rc;
+	}
+
+	buf.fmain.buffer = (unsigned long)pinfo.vaddr;
+	buf.fmain.planar0_off = pinfo.planar0_off;
+	buf.fmain.planar1_off = pinfo.planar1_off;
+	buf.fmain.fd = pinfo.fd;
+
+	CDBG("%s: buf 0x%x fd %d\n", __func__, (unsigned int)buf.fmain.buffer,
+		 buf.fmain.fd);
+	if (copy_to_user((void *)(se->stats_event.data),
+			&(buf.fmain), sizeof(struct msm_frame))) {
+		ERR_COPY_TO_USER();
+		return -EFAULT;
+	}
+	return 0;
+}
+
+/* Divert stereo frames for post-processing by delivering
+ * them to the config thread.
+ */
+static int msm_divert_st_frame(struct msm_sync *sync,
+	struct msm_vfe_resp *data, struct msm_stats_event_ctrl *se, int path)
+{
+	struct msm_pmem_info pinfo;
+	struct msm_st_frame buf;
+	struct video_crop_t *crop = NULL;
+	int rc = 0;
+
+	if (se->stats_event.msg_id == OUTPUT_TYPE_ST_L) {
+		buf.type = OUTPUT_TYPE_ST_L;
+	} else if (se->stats_event.msg_id == OUTPUT_TYPE_ST_R) {
+		buf.type = OUTPUT_TYPE_ST_R;
+	} else {
+		if (se->resptype == MSM_CAM_RESP_STEREO_OP_1) {
+			rc = msm_pmem_frame_ptov_lookup(sync, data->phy.p0_phy,
+				data->phy.p1_phy, data->phy.p2_phy, &pinfo,
+				1);  /* do clear the active flag */
+			buf.buf_info.path = path;
+		} else if (se->resptype == MSM_CAM_RESP_STEREO_OP_2) {
+			rc = msm_pmem_frame_ptov_lookup(sync, data->phy.p0_phy,
+				data->phy.p1_phy, data->phy.p2_phy, &pinfo,
+				0); /* do not clear the active flag */
+			buf.buf_info.path = path;
+		} else
+			CDBG("%s: Invalid resptype = %d\n", __func__,
+				se->resptype);
+
+		if (rc < 0) {
+			CDBG("%s: msm_pmem_frame_ptov_lookup failed\n",
+				__func__);
+			return rc;
+		}
+
+		buf.type = OUTPUT_TYPE_ST_D;
+
+		if (sync->cropinfo != NULL) {
+			crop = sync->cropinfo;
+			switch (path) {
+			case OUTPUT_TYPE_P:
+			case OUTPUT_TYPE_T: {
+				buf.L.stCropInfo.in_w = crop->in1_w;
+				buf.L.stCropInfo.in_h = crop->in1_h;
+				buf.L.stCropInfo.out_w = crop->out1_w;
+				buf.L.stCropInfo.out_h = crop->out1_h;
+				buf.R.stCropInfo = buf.L.stCropInfo;
+				break;
+			}
+
+			case OUTPUT_TYPE_V:
+			case OUTPUT_TYPE_S: {
+				buf.L.stCropInfo.in_w = crop->in2_w;
+				buf.L.stCropInfo.in_h = crop->in2_h;
+				buf.L.stCropInfo.out_w = crop->out2_w;
+				buf.L.stCropInfo.out_h = crop->out2_h;
+				buf.R.stCropInfo = buf.L.stCropInfo;
+				break;
+			}
+			default: {
+				pr_warning("%s: invalid frame path %d\n",
+					__func__, path);
+				break;
+			}
+			}
+		} else {
+			buf.L.stCropInfo.in_w = 0;
+			buf.L.stCropInfo.in_h = 0;
+			buf.L.stCropInfo.out_w = 0;
+			buf.L.stCropInfo.out_h = 0;
+			buf.R.stCropInfo = buf.L.stCropInfo;
+		}
+
+		/* hardcode for now. */
+		if ((path == OUTPUT_TYPE_S) || (path == OUTPUT_TYPE_T))
+			buf.packing = sync->sctrl.s_snap_packing;
+		else
+			buf.packing = sync->sctrl.s_video_packing;
+
+		buf.buf_info.buffer = (unsigned long)pinfo.vaddr;
+		buf.buf_info.phy_offset = pinfo.offset;
+		buf.buf_info.planar0_off = pinfo.planar0_off;
+		buf.buf_info.planar1_off = pinfo.planar1_off;
+		buf.buf_info.planar2_off = pinfo.planar2_off;
+		buf.buf_info.fd = pinfo.fd;
+
+		CDBG("%s: buf 0x%x fd %d\n", __func__,
+			(unsigned int)buf.buf_info.buffer, buf.buf_info.fd);
+	}
+
+	if (copy_to_user((void *)(se->stats_event.data),
+			&buf, sizeof(struct msm_st_frame))) {
+		ERR_COPY_TO_USER();
+		return -EFAULT;
+	}
+	return 0;
+}
+
+static int msm_get_stats(struct msm_sync *sync, void __user *arg)
+{
+	int rc = 0;
+
+	struct msm_stats_event_ctrl se;
+
+	struct msm_queue_cmd *qcmd = NULL;
+	struct msm_ctrl_cmd  *ctrl = NULL;
+	struct msm_vfe_resp  *data = NULL;
+	struct msm_vpe_resp  *vpe_data = NULL;
+	struct msm_stats_buf stats;
+
+	if (copy_from_user(&se, arg,
+			sizeof(struct msm_stats_event_ctrl))) {
+		ERR_COPY_FROM_USER();
+		pr_err("%s, ERR_COPY_FROM_USER\n", __func__);
+		return -EFAULT;
+	}
+
+	rc = 0;
+
+	qcmd = msm_dequeue(&sync->event_q, list_config);
+	if (!qcmd) {
+		/* Should be associated with wait_event
+			error -512 from __msm_control*/
+		pr_err("%s, qcmd is Null\n", __func__);
+		rc = -ETIMEDOUT;
+		return rc;
+	}
+
+	CDBG("%s: received from DSP %d\n", __func__, qcmd->type);
+
+	switch (qcmd->type) {
+	case MSM_CAM_Q_VPE_MSG:
+		/* Complete VPE response. */
+		vpe_data = (struct msm_vpe_resp *)(qcmd->command);
+		se.resptype = MSM_CAM_RESP_STEREO_OP_2;
+		se.stats_event.type   = vpe_data->evt_msg.type;
+		se.stats_event.msg_id = vpe_data->evt_msg.msg_id;
+		se.stats_event.len    = vpe_data->evt_msg.len;
+
+		if (vpe_data->type == VPE_MSG_OUTPUT_ST_L) {
+			CDBG("%s: Change msg_id to OUTPUT_TYPE_ST_L\n",
+				__func__);
+			se.stats_event.msg_id = OUTPUT_TYPE_ST_L;
+			rc = msm_divert_st_frame(sync, data, &se,
+				OUTPUT_TYPE_V);
+		} else if (vpe_data->type == VPE_MSG_OUTPUT_ST_R) {
+			CDBG("%s: Change msg_id to OUTPUT_TYPE_ST_R\n",
+				__func__);
+			se.stats_event.msg_id = OUTPUT_TYPE_ST_R;
+			rc = msm_divert_st_frame(sync, data, &se,
+				OUTPUT_TYPE_V);
+		} else {
+			pr_warning("%s: invalid vpe_data->type = %d\n",
+				__func__, vpe_data->type);
+		}
+		break;
+
+	case MSM_CAM_Q_VFE_EVT:
+	case MSM_CAM_Q_VFE_MSG:
+		data = (struct msm_vfe_resp *)(qcmd->command);
+
+		/* adsp event and message */
+		se.resptype = MSM_CAM_RESP_STAT_EVT_MSG;
+
+		/* 0 - msg from aDSP, 1 - event from mARM */
+		se.stats_event.type   = data->evt_msg.type;
+		se.stats_event.msg_id = data->evt_msg.msg_id;
+		se.stats_event.len    = data->evt_msg.len;
+		se.stats_event.frame_id = data->evt_msg.frame_id;
+
+		CDBG("%s: qcmd->type %d length %d msd_id %d\n", __func__,
+			qcmd->type,
+			se.stats_event.len,
+			se.stats_event.msg_id);
+
+		if (data->type == VFE_MSG_COMMON) {
+			stats.status_bits = data->stats_msg.status_bits;
+			stats.awb_ymin = data->stats_msg.awb_ymin;
+
+			if (data->stats_msg.aec_buff) {
+				stats.aec.buff =
+				msm_pmem_stats_ptov_lookup(sync,
+						data->stats_msg.aec_buff,
+						&(stats.aec.fd));
+				if (!stats.aec.buff) {
+					pr_err("%s: msm_pmem_stats_ptov_lookup error\n",
+						__func__);
+					rc = -EINVAL;
+					goto failure;
+				}
+
+			} else {
+				stats.aec.buff = 0;
+			}
+			if (data->stats_msg.awb_buff) {
+				stats.awb.buff =
+				msm_pmem_stats_ptov_lookup(sync,
+						data->stats_msg.awb_buff,
+						&(stats.awb.fd));
+				if (!stats.awb.buff) {
+					pr_err("%s: msm_pmem_stats_ptov_lookup error\n",
+						__func__);
+					rc = -EINVAL;
+					goto failure;
+				}
+
+			} else {
+				stats.awb.buff = 0;
+			}
+			if (data->stats_msg.af_buff) {
+				stats.af.buff =
+				msm_pmem_stats_ptov_lookup(sync,
+						data->stats_msg.af_buff,
+						&(stats.af.fd));
+				if (!stats.af.buff) {
+					pr_err("%s: msm_pmem_stats_ptov_lookup error\n",
+						__func__);
+					rc = -EINVAL;
+					goto failure;
+				}
+
+			} else {
+				stats.af.buff = 0;
+			}
+			if (data->stats_msg.ihist_buff) {
+				stats.ihist.buff =
+				msm_pmem_stats_ptov_lookup(sync,
+						data->stats_msg.ihist_buff,
+						&(stats.ihist.fd));
+				if (!stats.ihist.buff) {
+					pr_err("%s: msm_pmem_stats_ptov_lookup error\n",
+						__func__);
+					rc = -EINVAL;
+					goto failure;
+				}
+
+			} else {
+				stats.ihist.buff = 0;
+			}
+
+			if (data->stats_msg.rs_buff) {
+				stats.rs.buff =
+				msm_pmem_stats_ptov_lookup(sync,
+						data->stats_msg.rs_buff,
+						&(stats.rs.fd));
+				if (!stats.rs.buff) {
+					pr_err("%s: msm_pmem_stats_ptov_lookup error\n",
+						__func__);
+					rc = -EINVAL;
+					goto failure;
+				}
+
+			} else {
+				stats.rs.buff = 0;
+			}
+
+			if (data->stats_msg.cs_buff) {
+				stats.cs.buff =
+				msm_pmem_stats_ptov_lookup(sync,
+						data->stats_msg.cs_buff,
+						&(stats.cs.fd));
+				if (!stats.cs.buff) {
+					pr_err("%s: msm_pmem_stats_ptov_lookup error\n",
+						__func__);
+					rc = -EINVAL;
+					goto failure;
+				}
+			} else {
+				stats.cs.buff = 0;
+			}
+
+			se.stats_event.frame_id = data->phy.frame_id;
+			if (copy_to_user((void *)(se.stats_event.data),
+					&stats,
+					sizeof(struct msm_stats_buf))) {
+				ERR_COPY_TO_USER();
+				rc = -EFAULT;
+				goto failure;
+			}
+		} else if ((data->type >= VFE_MSG_STATS_AEC) &&
+			(data->type <=  VFE_MSG_STATS_WE)) {
+			/* the check above includes all stats type. */
+			stats.awb_ymin = data->stats_msg.awb_ymin;
+			stats.buffer =
+				msm_pmem_stats_ptov_lookup(sync,
+						data->phy.sbuf_phy,
+						&(stats.fd));
+			if (!stats.buffer) {
+					pr_err("%s: msm_pmem_stats_ptov_lookup error\n",
+						__func__);
+					rc = -EINVAL;
+					goto failure;
+			}
+			se.stats_event.frame_id = data->phy.frame_id;
+			if (copy_to_user((void *)(se.stats_event.data),
+					&stats,
+					sizeof(struct msm_stats_buf))) {
+				ERR_COPY_TO_USER();
+				rc = -EFAULT;
+				goto failure;
+			}
+		} else if ((data->evt_msg.len > 0) &&
+				(data->type == VFE_MSG_GENERAL)) {
+			if (copy_to_user((void *)(se.stats_event.data),
+					data->evt_msg.data,
+					data->evt_msg.len)) {
+				ERR_COPY_TO_USER();
+				rc = -EFAULT;
+				goto failure;
+			}
+		} else {
+			if (sync->stereocam_enabled) {
+				if (data->type == VFE_MSG_OUTPUT_P) {
+					CDBG("%s: Preview mark as st op 1\n",
+						__func__);
+					se.resptype = MSM_CAM_RESP_STEREO_OP_1;
+					rc = msm_divert_st_frame(sync, data,
+						&se, OUTPUT_TYPE_P);
+					break;
+				} else if (data->type == VFE_MSG_OUTPUT_V) {
+					CDBG("%s: Video mark as st op 2\n",
+						__func__);
+					se.resptype = MSM_CAM_RESP_STEREO_OP_2;
+					rc = msm_divert_st_frame(sync, data,
+						&se, OUTPUT_TYPE_V);
+					break;
+				} else if (data->type == VFE_MSG_OUTPUT_S) {
+					CDBG("%s: Main img mark as st op 2\n",
+						__func__);
+					se.resptype = MSM_CAM_RESP_STEREO_OP_2;
+					rc = msm_divert_st_frame(sync, data,
+						&se, OUTPUT_TYPE_S);
+					break;
+				} else if (data->type == VFE_MSG_OUTPUT_T) {
+					CDBG("%s: Thumb img mark as st op 2\n",
+						__func__);
+					se.resptype = MSM_CAM_RESP_STEREO_OP_2;
+					rc = msm_divert_st_frame(sync, data,
+						&se, OUTPUT_TYPE_T);
+					break;
+				} else
+					CDBG("%s: VFE_MSG Fall Through\n",
+						__func__);
+			}
+			if ((sync->pp_frame_avail == 1) &&
+				(sync->pp_mask & PP_PREV) &&
+				(data->type == VFE_MSG_OUTPUT_P)) {
+					CDBG("%s:%d:preiew PP\n",
+					__func__, __LINE__);
+					se.stats_event.frame_id =
+							data->phy.frame_id;
+					rc = msm_divert_frame(sync, data, &se);
+					sync->pp_frame_avail = 0;
+			} else {
+				if ((sync->pp_mask & PP_PREV) &&
+					(data->type == VFE_MSG_OUTPUT_P)) {
+					se.stats_event.frame_id =
+							data->phy.frame_id;
+					free_qcmd(qcmd);
+					return 0;
+				} else
+					CDBG("%s:indication type is %d\n",
+						__func__, data->type);
+			}
+			if (sync->pp_mask & PP_SNAP)
+				if (data->type == VFE_MSG_OUTPUT_S ||
+					data->type == VFE_MSG_OUTPUT_T)
+					rc = msm_divert_frame(sync, data, &se);
+		}
+		break;
+
+	case MSM_CAM_Q_CTRL:
+		/* control command from control thread */
+		ctrl = (struct msm_ctrl_cmd *)(qcmd->command);
+
+		CDBG("%s: qcmd->type %d length %d\n", __func__,
+			qcmd->type, ctrl->length);
+
+		if (ctrl->length > 0) {
+			if (copy_to_user((void *)(se.ctrl_cmd.value),
+						ctrl->value,
+						ctrl->length)) {
+				ERR_COPY_TO_USER();
+				rc = -EFAULT;
+				goto failure;
+			}
+		}
+
+		se.resptype = MSM_CAM_RESP_CTRL;
+
+		/* what to control */
+		se.ctrl_cmd.type = ctrl->type;
+		se.ctrl_cmd.length = ctrl->length;
+		se.ctrl_cmd.resp_fd = ctrl->resp_fd;
+		break;
+
+	case MSM_CAM_Q_V4L2_REQ:
+		/* control command from v4l2 client */
+		ctrl = (struct msm_ctrl_cmd *)(qcmd->command);
+		if (ctrl->length > 0) {
+			if (copy_to_user((void *)(se.ctrl_cmd.value),
+					ctrl->value, ctrl->length)) {
+				ERR_COPY_TO_USER();
+				rc = -EFAULT;
+				goto failure;
+			}
+		}
+
+		/* 2 tells config thread this is v4l2 request */
+		se.resptype = MSM_CAM_RESP_V4L2;
+
+		/* what to control */
+		se.ctrl_cmd.type   = ctrl->type;
+		se.ctrl_cmd.length = ctrl->length;
+		break;
+
+	default:
+		rc = -EFAULT;
+		goto failure;
+	} /* switch qcmd->type */
+	if (copy_to_user((void *)arg, &se, sizeof(se))) {
+		ERR_COPY_TO_USER();
+		rc = -EFAULT;
+		goto failure;
+	}
+
+failure:
+	free_qcmd(qcmd);
+
+	CDBG("%s: %d\n", __func__, rc);
+	return rc;
+}
+
+static int msm_ctrl_cmd_done(struct msm_control_device *ctrl_pmsm,
+		void __user *arg)
+{
+	void __user *uptr;
+	struct msm_queue_cmd *qcmd = &ctrl_pmsm->qcmd;
+	struct msm_ctrl_cmd *command = &ctrl_pmsm->ctrl;
+
+	if (copy_from_user(command, arg, sizeof(*command))) {
+		ERR_COPY_FROM_USER();
+		return -EFAULT;
+	}
+
+	atomic_set(&qcmd->on_heap, 0);
+	qcmd->command = command;
+	uptr = command->value;
+
+	if (command->length > 0) {
+		command->value = ctrl_pmsm->ctrl_data;
+		if (command->length > sizeof(ctrl_pmsm->ctrl_data)) {
+			pr_err("%s: user data %d is too big (max %d)\n",
+				__func__, command->length,
+				sizeof(ctrl_pmsm->ctrl_data));
+			return -EINVAL;
+		}
+
+		if (copy_from_user(command->value,
+					uptr,
+					command->length)) {
+			ERR_COPY_FROM_USER();
+			return -EFAULT;
+		}
+	} else
+		command->value = NULL;
+
+	/* Ignore the command if the ctrl cmd has
+	   return back due to signaling */
+	/* Should be associated with wait_event
+	   error -512 from __msm_control*/
+	if (ctrl_pmsm->pmsm->sync->ignore_qcmd == true &&
+	   ctrl_pmsm->pmsm->sync->ignore_qcmd_type == (int16_t)command->type) {
+		ctrl_pmsm->pmsm->sync->ignore_qcmd = false;
+		ctrl_pmsm->pmsm->sync->ignore_qcmd_type = -1;
+	} else /* wake up control thread */
+		msm_enqueue(&ctrl_pmsm->ctrl_q, &qcmd->list_control);
+
+	return 0;
+}
+
+static int msm_config_vpe(struct msm_sync *sync, void __user *arg)
+{
+	struct msm_vpe_cfg_cmd cfgcmd;
+	if (copy_from_user(&cfgcmd, arg, sizeof(cfgcmd))) {
+		ERR_COPY_FROM_USER();
+		return -EFAULT;
+	}
+	CDBG("%s: cmd_type %s\n", __func__, vfe_config_cmd[cfgcmd.cmd_type]);
+	switch (cfgcmd.cmd_type) {
+	case CMD_VPE:
+		return sync->vpefn.vpe_config(&cfgcmd, NULL);
+	default:
+		pr_err("%s: unknown command type %d\n",
+			__func__, cfgcmd.cmd_type);
+	}
+	return -EINVAL;
+}
+
+static int msm_config_vfe(struct msm_sync *sync, void __user *arg)
+{
+	struct msm_vfe_cfg_cmd cfgcmd;
+	struct msm_pmem_region region[8];
+	struct axidata axi_data;
+
+	if (!sync->vfefn.vfe_config) {
+		pr_err("%s: no vfe_config!\n", __func__);
+		return -EIO;
+	}
+
+	if (copy_from_user(&cfgcmd, arg, sizeof(cfgcmd))) {
+		ERR_COPY_FROM_USER();
+		return -EFAULT;
+	}
+
+	memset(&axi_data, 0, sizeof(axi_data));
+	CDBG("%s: cmd_type %s\n", __func__, vfe_config_cmd[cfgcmd.cmd_type]);
+	switch (cfgcmd.cmd_type) {
+	case CMD_STATS_ENABLE:
+		axi_data.bufnum1 =
+			msm_pmem_region_lookup(&sync->pmem_stats,
+				MSM_PMEM_AEC_AWB, &region[0],
+				NUM_STAT_OUTPUT_BUFFERS,
+				&sync->pmem_stats_spinlock);
+		axi_data.bufnum2 =
+			msm_pmem_region_lookup(&sync->pmem_stats,
+				MSM_PMEM_AF, &region[axi_data.bufnum1],
+				NUM_STAT_OUTPUT_BUFFERS,
+				&sync->pmem_stats_spinlock);
+		if (!axi_data.bufnum1 || !axi_data.bufnum2) {
+			pr_err("%s: pmem region lookup error\n", __func__);
+			return -EINVAL;
+		}
+		axi_data.region = &region[0];
+		return sync->vfefn.vfe_config(&cfgcmd, &axi_data);
+	case CMD_STATS_AF_ENABLE:
+		axi_data.bufnum1 =
+			msm_pmem_region_lookup(&sync->pmem_stats,
+				MSM_PMEM_AF, &region[0],
+				NUM_STAT_OUTPUT_BUFFERS,
+				&sync->pmem_stats_spinlock);
+		if (!axi_data.bufnum1) {
+			pr_err("%s %d: pmem region lookup error\n",
+				__func__, __LINE__);
+			return -EINVAL;
+		}
+		axi_data.region = &region[0];
+		return sync->vfefn.vfe_config(&cfgcmd, &axi_data);
+	case CMD_STATS_AEC_AWB_ENABLE:
+		axi_data.bufnum1 =
+			msm_pmem_region_lookup(&sync->pmem_stats,
+				MSM_PMEM_AEC_AWB, &region[0],
+				NUM_STAT_OUTPUT_BUFFERS,
+				&sync->pmem_stats_spinlock);
+		if (!axi_data.bufnum1) {
+			pr_err("%s %d: pmem region lookup error\n",
+				__func__, __LINE__);
+			return -EINVAL;
+		}
+		axi_data.region = &region[0];
+		return sync->vfefn.vfe_config(&cfgcmd, &axi_data);
+	case CMD_STATS_AEC_ENABLE:
+		axi_data.bufnum1 =
+			msm_pmem_region_lookup(&sync->pmem_stats,
+			MSM_PMEM_AEC, &region[0],
+			NUM_STAT_OUTPUT_BUFFERS,
+			&sync->pmem_stats_spinlock);
+		if (!axi_data.bufnum1) {
+			pr_err("%s %d: pmem region lookup error\n",
+				__func__, __LINE__);
+			return -EINVAL;
+		}
+		axi_data.region = &region[0];
+		return sync->vfefn.vfe_config(&cfgcmd, &axi_data);
+	case CMD_STATS_AWB_ENABLE:
+		axi_data.bufnum1 =
+			msm_pmem_region_lookup(&sync->pmem_stats,
+			MSM_PMEM_AWB, &region[0],
+			NUM_STAT_OUTPUT_BUFFERS,
+			&sync->pmem_stats_spinlock);
+		if (!axi_data.bufnum1) {
+			pr_err("%s %d: pmem region lookup error\n",
+				__func__, __LINE__);
+			return -EINVAL;
+		}
+		axi_data.region = &region[0];
+		return sync->vfefn.vfe_config(&cfgcmd, &axi_data);
+
+
+	case CMD_STATS_IHIST_ENABLE:
+		axi_data.bufnum1 =
+			msm_pmem_region_lookup(&sync->pmem_stats,
+			MSM_PMEM_IHIST, &region[0],
+			NUM_STAT_OUTPUT_BUFFERS,
+			&sync->pmem_stats_spinlock);
+		if (!axi_data.bufnum1) {
+			pr_err("%s %d: pmem region lookup error\n",
+				__func__, __LINE__);
+			return -EINVAL;
+		}
+		axi_data.region = &region[0];
+		return sync->vfefn.vfe_config(&cfgcmd, &axi_data);
+
+	case CMD_STATS_RS_ENABLE:
+		axi_data.bufnum1 =
+			msm_pmem_region_lookup(&sync->pmem_stats,
+			MSM_PMEM_RS, &region[0],
+			NUM_STAT_OUTPUT_BUFFERS,
+			&sync->pmem_stats_spinlock);
+		if (!axi_data.bufnum1) {
+			pr_err("%s %d: pmem region lookup error\n",
+				__func__, __LINE__);
+			return -EINVAL;
+		}
+		axi_data.region = &region[0];
+		return sync->vfefn.vfe_config(&cfgcmd, &axi_data);
+
+	case CMD_STATS_CS_ENABLE:
+		axi_data.bufnum1 =
+			msm_pmem_region_lookup(&sync->pmem_stats,
+			MSM_PMEM_CS, &region[0],
+			NUM_STAT_OUTPUT_BUFFERS,
+			&sync->pmem_stats_spinlock);
+		if (!axi_data.bufnum1) {
+			pr_err("%s %d: pmem region lookup error\n",
+				__func__, __LINE__);
+			return -EINVAL;
+		}
+		axi_data.region = &region[0];
+		return sync->vfefn.vfe_config(&cfgcmd, &axi_data);
+
+	case CMD_GENERAL:
+	case CMD_STATS_DISABLE:
+		return sync->vfefn.vfe_config(&cfgcmd, NULL);
+	default:
+		pr_err("%s: unknown command type %d\n",
+			__func__, cfgcmd.cmd_type);
+	}
+
+	return -EINVAL;
+}
+static int msm_vpe_frame_cfg(struct msm_sync *sync,
+				void *cfgcmdin)
+{
+	int rc = -EIO;
+	struct axidata axi_data;
+	void *data = &axi_data;
+	struct msm_pmem_region region[8];
+	int pmem_type;
+
+	struct msm_vpe_cfg_cmd *cfgcmd;
+	cfgcmd = (struct msm_vpe_cfg_cmd *)cfgcmdin;
+
+	memset(&axi_data, 0, sizeof(axi_data));
+	CDBG("In vpe_frame_cfg cfgcmd->cmd_type = %s\n",
+		vfe_config_cmd[cfgcmd->cmd_type]);
+	switch (cfgcmd->cmd_type) {
+	case CMD_AXI_CFG_VPE:
+		pmem_type = MSM_PMEM_VIDEO_VPE;
+		axi_data.bufnum1 =
+			msm_pmem_region_lookup_2(&sync->pmem_frames, pmem_type,
+				&region[0], 8, &sync->pmem_frame_spinlock);
+		CDBG("axi_data.bufnum1 = %d\n", axi_data.bufnum1);
+		if (!axi_data.bufnum1) {
+			pr_err("%s %d: pmem region lookup error\n",
+				__func__, __LINE__);
+			return -EINVAL;
+		}
+		pmem_type = MSM_PMEM_VIDEO;
+		break;
+	case CMD_AXI_CFG_SNAP_THUMB_VPE:
+		CDBG("%s: CMD_AXI_CFG_SNAP_THUMB_VPE", __func__);
+		pmem_type = MSM_PMEM_THUMBNAIL_VPE;
+		axi_data.bufnum1 =
+			msm_pmem_region_lookup(&sync->pmem_frames, pmem_type,
+			&region[0], 8, &sync->pmem_frame_spinlock);
+		if (!axi_data.bufnum1) {
+			pr_err("%s: THUMBNAIL_VPE pmem region lookup error\n",
+				__func__);
+			return -EINVAL;
+		}
+		break;
+	case CMD_AXI_CFG_SNAP_VPE:
+		CDBG("%s: CMD_AXI_CFG_SNAP_VPE", __func__);
+		pmem_type = MSM_PMEM_MAINIMG_VPE;
+		axi_data.bufnum1 =
+			msm_pmem_region_lookup(&sync->pmem_frames, pmem_type,
+				&region[0], 8, &sync->pmem_frame_spinlock);
+		if (!axi_data.bufnum1) {
+			pr_err("%s: MAINIMG_VPE pmem region lookup error\n",
+				__func__);
+			return -EINVAL;
+		}
+		break;
+	default:
+		pr_err("%s: unknown command type %d\n",
+			__func__, cfgcmd->cmd_type);
+		break;
+	}
+	axi_data.region = &region[0];
+	CDBG("out vpe_frame_cfg cfgcmd->cmd_type = %s\n",
+		vfe_config_cmd[cfgcmd->cmd_type]);
+	/* send the AXI configuration command to driver */
+	if (sync->vpefn.vpe_config)
+		rc = sync->vpefn.vpe_config(cfgcmd, data);
+	return rc;
+}
+
+static int msm_frame_axi_cfg(struct msm_sync *sync,
+		struct msm_vfe_cfg_cmd *cfgcmd)
+{
+	int rc = -EIO;
+	struct axidata axi_data;
+	void *data = &axi_data;
+	struct msm_pmem_region region[MAX_PMEM_CFG_BUFFERS];
+	int pmem_type;
+
+	memset(&axi_data, 0, sizeof(axi_data));
+
+	switch (cfgcmd->cmd_type) {
+
+	case CMD_AXI_CFG_PREVIEW:
+		pmem_type = MSM_PMEM_PREVIEW;
+		axi_data.bufnum2 =
+			msm_pmem_region_lookup(&sync->pmem_frames, pmem_type,
+				&region[0], MAX_PMEM_CFG_BUFFERS,
+				&sync->pmem_frame_spinlock);
+		if (!axi_data.bufnum2) {
+			pr_err("%s %d: pmem region lookup error (empty %d)\n",
+				__func__, __LINE__,
+				hlist_empty(&sync->pmem_frames));
+			return -EINVAL;
+		}
+		break;
+
+	case CMD_AXI_CFG_VIDEO_ALL_CHNLS:
+	case CMD_AXI_CFG_VIDEO:
+		pmem_type = MSM_PMEM_PREVIEW;
+		axi_data.bufnum1 =
+			msm_pmem_region_lookup(&sync->pmem_frames, pmem_type,
+				&region[0], MAX_PMEM_CFG_BUFFERS,
+				&sync->pmem_frame_spinlock);
+		if (!axi_data.bufnum1) {
+			pr_err("%s %d: pmem region lookup error\n",
+				__func__, __LINE__);
+			return -EINVAL;
+		}
+
+		pmem_type = MSM_PMEM_VIDEO;
+		axi_data.bufnum2 =
+			msm_pmem_region_lookup(&sync->pmem_frames, pmem_type,
+				&region[axi_data.bufnum1],
+				(MAX_PMEM_CFG_BUFFERS-(axi_data.bufnum1)),
+				&sync->pmem_frame_spinlock);
+		if (!axi_data.bufnum2) {
+			pr_err("%s %d: pmem region lookup error\n",
+				__func__, __LINE__);
+			return -EINVAL;
+		}
+		break;
+
+	case CMD_AXI_CFG_SNAP:
+		CDBG("%s, CMD_AXI_CFG_SNAP, type=%d\n", __func__,
+			cfgcmd->cmd_type);
+		pmem_type = MSM_PMEM_THUMBNAIL;
+		axi_data.bufnum1 =
+			msm_pmem_region_lookup(&sync->pmem_frames, pmem_type,
+				&region[0], MAX_PMEM_CFG_BUFFERS,
+				&sync->pmem_frame_spinlock);
+		if (!axi_data.bufnum1) {
+			pr_err("%s %d: pmem region lookup error\n",
+				__func__, __LINE__);
+			return -EINVAL;
+		}
+
+		pmem_type = MSM_PMEM_MAINIMG;
+		axi_data.bufnum2 =
+			msm_pmem_region_lookup(&sync->pmem_frames, pmem_type,
+				&region[axi_data.bufnum1],
+				(MAX_PMEM_CFG_BUFFERS-(axi_data.bufnum1)),
+				 &sync->pmem_frame_spinlock);
+		if (!axi_data.bufnum2) {
+			pr_err("%s %d: pmem region lookup error\n",
+				__func__, __LINE__);
+			return -EINVAL;
+		}
+		break;
+
+	case CMD_AXI_CFG_ZSL_ALL_CHNLS:
+	case CMD_AXI_CFG_ZSL:
+		CDBG("%s, CMD_AXI_CFG_ZSL, type = %d\n", __func__,
+			cfgcmd->cmd_type);
+		pmem_type = MSM_PMEM_PREVIEW;
+		axi_data.bufnum1 =
+			msm_pmem_region_lookup(&sync->pmem_frames, pmem_type,
+				&region[0], MAX_PMEM_CFG_BUFFERS,
+				&sync->pmem_frame_spinlock);
+		if (!axi_data.bufnum1) {
+			pr_err("%s %d: pmem region lookup error\n",
+				__func__, __LINE__);
+			return -EINVAL;
+		}
+
+		pmem_type = MSM_PMEM_THUMBNAIL;
+		axi_data.bufnum2 =
+			msm_pmem_region_lookup(&sync->pmem_frames, pmem_type,
+				&region[axi_data.bufnum1],
+				(MAX_PMEM_CFG_BUFFERS-(axi_data.bufnum1)),
+				 &sync->pmem_frame_spinlock);
+		if (!axi_data.bufnum2) {
+			pr_err("%s %d: pmem region lookup error\n",
+				__func__, __LINE__);
+			return -EINVAL;
+		}
+
+		pmem_type = MSM_PMEM_MAINIMG;
+		axi_data.bufnum3 =
+			msm_pmem_region_lookup(&sync->pmem_frames, pmem_type,
+				&region[axi_data.bufnum1 + axi_data.bufnum2],
+				(MAX_PMEM_CFG_BUFFERS - axi_data.bufnum1 -
+				axi_data.bufnum2), &sync->pmem_frame_spinlock);
+		if (!axi_data.bufnum3) {
+			pr_err("%s %d: pmem region lookup error\n",
+				__func__, __LINE__);
+			return -EINVAL;
+		}
+		break;
+
+	case CMD_RAW_PICT_AXI_CFG:
+		pmem_type = MSM_PMEM_RAW_MAINIMG;
+		axi_data.bufnum2 =
+			msm_pmem_region_lookup(&sync->pmem_frames, pmem_type,
+				&region[0], MAX_PMEM_CFG_BUFFERS,
+				&sync->pmem_frame_spinlock);
+		if (!axi_data.bufnum2) {
+			pr_err("%s %d: pmem region lookup error\n",
+				__func__, __LINE__);
+			return -EINVAL;
+		}
+		break;
+
+	case CMD_GENERAL:
+		data = NULL;
+		break;
+
+	default:
+		pr_err("%s: unknown command type %d\n",
+			__func__, cfgcmd->cmd_type);
+		return -EINVAL;
+	}
+
+	axi_data.region = &region[0];
+
+	/* send the AXI configuration command to driver */
+	if (sync->vfefn.vfe_config)
+		rc = sync->vfefn.vfe_config(cfgcmd, data);
+
+	return rc;
+}
+
+static int msm_get_sensor_info(struct msm_sync *sync, void __user *arg)
+{
+	int rc = 0;
+	struct msm_camsensor_info info;
+	struct msm_camera_sensor_info *sdata;
+
+	if (copy_from_user(&info,
+			arg,
+			sizeof(struct msm_camsensor_info))) {
+		ERR_COPY_FROM_USER();
+		return -EFAULT;
+	}
+
+	sdata = sync->pdev->dev.platform_data;
+	if (sync->sctrl.s_camera_type == BACK_CAMERA_3D)
+		info.support_3d = true;
+	else
+		info.support_3d = false;
+	memcpy(&info.name[0],
+		sdata->sensor_name,
+		MAX_SENSOR_NAME);
+	info.flash_enabled = sdata->flash_data->flash_type !=
+		MSM_CAMERA_FLASH_NONE;
+
+	/* copy back to user space */
+	if (copy_to_user((void *)arg,
+			&info,
+			sizeof(struct msm_camsensor_info))) {
+		ERR_COPY_TO_USER();
+		rc = -EFAULT;
+	}
+
+	return rc;
+}
+
+static int msm_get_camera_info(void __user *arg)
+{
+	int rc = 0;
+	int i = 0;
+	struct msm_camera_info info;
+
+	if (copy_from_user(&info, arg, sizeof(struct msm_camera_info))) {
+		ERR_COPY_FROM_USER();
+		return -EFAULT;
+	}
+
+	CDBG("%s: camera_node %d\n", __func__, camera_node);
+	info.num_cameras = camera_node;
+
+	for (i = 0; i < camera_node; i++) {
+		info.has_3d_support[i] = 0;
+		info.is_internal_cam[i] = 0;
+		info.s_mount_angle[i] = sensor_mount_angle[i];
+		switch (camera_type[i]) {
+		case FRONT_CAMERA_2D:
+			info.is_internal_cam[i] = 1;
+			break;
+		case BACK_CAMERA_3D:
+			info.has_3d_support[i] = 1;
+			break;
+		case BACK_CAMERA_2D:
+		default:
+			break;
+		}
+	}
+	/* copy back to user space */
+	if (copy_to_user((void *)arg, &info, sizeof(struct msm_camera_info))) {
+		ERR_COPY_TO_USER();
+		rc = -EFAULT;
+	}
+	return rc;
+}
+
+static int __msm_put_frame_buf(struct msm_sync *sync,
+		struct msm_frame *pb)
+{
+	unsigned long pphy;
+	struct msm_vfe_cfg_cmd cfgcmd;
+
+	int rc = -EIO;
+
+	/* Change the active flag. */
+	pphy = msm_pmem_frame_vtop_lookup(sync,
+		pb->buffer,
+		pb->planar0_off, pb->planar1_off, pb->planar2_off, pb->fd, 1);
+
+	if (pphy != 0) {
+		CDBG("%s: rel: vaddr %lx, paddr %lx\n",
+			__func__,
+			pb->buffer, pphy);
+		cfgcmd.cmd_type = CMD_FRAME_BUF_RELEASE;
+		cfgcmd.value    = (void *)pb;
+		if (sync->vfefn.vfe_config)
+			rc = sync->vfefn.vfe_config(&cfgcmd, &pphy);
+	} else {
+		pr_err("%s: msm_pmem_frame_vtop_lookup failed\n",
+			__func__);
+		rc = -EINVAL;
+	}
+
+	return rc;
+}
+static int __msm_put_pic_buf(struct msm_sync *sync,
+		struct msm_frame *pb)
+{
+	unsigned long pphy;
+	struct msm_vfe_cfg_cmd cfgcmd;
+
+	int rc = -EIO;
+
+	pphy = msm_pmem_frame_vtop_lookup(sync,
+		pb->buffer,
+		pb->planar0_off, pb->planar1_off, pb->planar2_off, pb->fd, 1);
+
+	if (pphy != 0) {
+		CDBG("%s: rel: vaddr %lx, paddr %lx\n",
+			__func__,
+			pb->buffer, pphy);
+		cfgcmd.cmd_type = CMD_SNAP_BUF_RELEASE;
+		cfgcmd.value    = (void *)pb;
+		if (sync->vfefn.vfe_config)
+			rc = sync->vfefn.vfe_config(&cfgcmd, &pphy);
+	} else {
+		pr_err("%s: msm_pmem_frame_vtop_lookup failed\n",
+			__func__);
+		rc = -EINVAL;
+	}
+
+	return rc;
+}
+
+
+static int msm_put_frame_buffer(struct msm_sync *sync, void __user *arg)
+{
+	struct msm_frame buf_t;
+
+	if (copy_from_user(&buf_t,
+				arg,
+				sizeof(struct msm_frame))) {
+		ERR_COPY_FROM_USER();
+		return -EFAULT;
+	}
+
+	return __msm_put_frame_buf(sync, &buf_t);
+}
+
+
+static int msm_put_pic_buffer(struct msm_sync *sync, void __user *arg)
+{
+	struct msm_frame buf_t;
+
+	if (copy_from_user(&buf_t,
+				arg,
+				sizeof(struct msm_frame))) {
+		ERR_COPY_FROM_USER();
+		return -EFAULT;
+	}
+
+	return __msm_put_pic_buf(sync, &buf_t);
+}
+
+static int __msm_register_pmem(struct msm_sync *sync,
+		struct msm_pmem_info *pinfo)
+{
+	int rc = 0;
+
+	switch (pinfo->type) {
+	case MSM_PMEM_VIDEO:
+	case MSM_PMEM_PREVIEW:
+	case MSM_PMEM_THUMBNAIL:
+	case MSM_PMEM_MAINIMG:
+	case MSM_PMEM_RAW_MAINIMG:
+	case MSM_PMEM_VIDEO_VPE:
+	case MSM_PMEM_C2D:
+	case MSM_PMEM_MAINIMG_VPE:
+	case MSM_PMEM_THUMBNAIL_VPE:
+		rc = msm_pmem_table_add(&sync->pmem_frames, pinfo,
+			&sync->pmem_frame_spinlock, sync);
+		break;
+
+	case MSM_PMEM_AEC_AWB:
+	case MSM_PMEM_AF:
+	case MSM_PMEM_AEC:
+	case MSM_PMEM_AWB:
+	case MSM_PMEM_RS:
+	case MSM_PMEM_CS:
+	case MSM_PMEM_IHIST:
+	case MSM_PMEM_SKIN:
+
+		rc = msm_pmem_table_add(&sync->pmem_stats, pinfo,
+			 &sync->pmem_stats_spinlock, sync);
+		break;
+
+	default:
+		rc = -EINVAL;
+		break;
+	}
+
+	return rc;
+}
+
+static int msm_register_pmem(struct msm_sync *sync, void __user *arg)
+{
+	struct msm_pmem_info info;
+
+	if (copy_from_user(&info, arg, sizeof(info))) {
+		ERR_COPY_FROM_USER();
+		return -EFAULT;
+	}
+
+	return __msm_register_pmem(sync, &info);
+}
+
+static int msm_stats_axi_cfg(struct msm_sync *sync,
+		struct msm_vfe_cfg_cmd *cfgcmd)
+{
+	int rc = -EIO;
+	struct axidata axi_data;
+	void *data = &axi_data;
+
+	struct msm_pmem_region region[3];
+	int pmem_type = MSM_PMEM_MAX;
+
+	memset(&axi_data, 0, sizeof(axi_data));
+
+	switch (cfgcmd->cmd_type) {
+	case CMD_STATS_AXI_CFG:
+		pmem_type = MSM_PMEM_AEC_AWB;
+		break;
+	case CMD_STATS_AF_AXI_CFG:
+		pmem_type = MSM_PMEM_AF;
+		break;
+	case CMD_GENERAL:
+		data = NULL;
+		break;
+	default:
+		pr_err("%s: unknown command type %d\n",
+			__func__, cfgcmd->cmd_type);
+		return -EINVAL;
+	}
+
+	if (cfgcmd->cmd_type != CMD_GENERAL) {
+		axi_data.bufnum1 =
+			msm_pmem_region_lookup(&sync->pmem_stats, pmem_type,
+				&region[0], NUM_STAT_OUTPUT_BUFFERS,
+				&sync->pmem_stats_spinlock);
+		if (!axi_data.bufnum1) {
+			pr_err("%s %d: pmem region lookup error\n",
+				__func__, __LINE__);
+			return -EINVAL;
+		}
+	axi_data.region = &region[0];
+	}
+
+	/* send the AEC/AWB STATS configuration command to driver */
+	if (sync->vfefn.vfe_config)
+		rc = sync->vfefn.vfe_config(cfgcmd, &axi_data);
+
+	return rc;
+}
+
+static int msm_put_stats_buffer(struct msm_sync *sync, void __user *arg)
+{
+	int rc = -EIO;
+
+	struct msm_stats_buf buf;
+	unsigned long pphy;
+	struct msm_vfe_cfg_cmd cfgcmd;
+
+	if (copy_from_user(&buf, arg,
+				sizeof(struct msm_stats_buf))) {
+		ERR_COPY_FROM_USER();
+		return -EFAULT;
+	}
+
+	CDBG("%s\n", __func__);
+	pphy = msm_pmem_stats_vtop_lookup(sync, buf.buffer, buf.fd);
+
+	if (pphy != 0) {
+		if (buf.type == STAT_AEAW)
+			cfgcmd.cmd_type = CMD_STATS_BUF_RELEASE;
+		else if (buf.type == STAT_AF)
+			cfgcmd.cmd_type = CMD_STATS_AF_BUF_RELEASE;
+		else if (buf.type == STAT_AEC)
+			cfgcmd.cmd_type = CMD_STATS_AEC_BUF_RELEASE;
+		else if (buf.type == STAT_AWB)
+			cfgcmd.cmd_type = CMD_STATS_AWB_BUF_RELEASE;
+		else if (buf.type == STAT_IHIST)
+			cfgcmd.cmd_type = CMD_STATS_IHIST_BUF_RELEASE;
+		else if (buf.type == STAT_RS)
+			cfgcmd.cmd_type = CMD_STATS_RS_BUF_RELEASE;
+		else if (buf.type == STAT_CS)
+			cfgcmd.cmd_type = CMD_STATS_CS_BUF_RELEASE;
+
+		else {
+			pr_err("%s: invalid buf type %d\n",
+				__func__,
+				buf.type);
+			rc = -EINVAL;
+			goto put_done;
+		}
+
+		cfgcmd.value = (void *)&buf;
+
+		if (sync->vfefn.vfe_config) {
+			rc = sync->vfefn.vfe_config(&cfgcmd, &pphy);
+			if (rc < 0)
+				pr_err("%s: vfe_config error %d\n",
+					__func__, rc);
+		} else
+			pr_err("%s: vfe_config is NULL\n", __func__);
+	} else {
+		pr_err("%s: NULL physical address\n", __func__);
+		rc = -EINVAL;
+	}
+
+put_done:
+	return rc;
+}
+
+static int msm_axi_config(struct msm_sync *sync, void __user *arg)
+{
+	struct msm_vfe_cfg_cmd cfgcmd;
+
+	if (copy_from_user(&cfgcmd, arg, sizeof(cfgcmd))) {
+		ERR_COPY_FROM_USER();
+		return -EFAULT;
+	}
+
+	switch (cfgcmd.cmd_type) {
+	case CMD_AXI_CFG_VIDEO:
+	case CMD_AXI_CFG_PREVIEW:
+	case CMD_AXI_CFG_SNAP:
+	case CMD_RAW_PICT_AXI_CFG:
+	case CMD_AXI_CFG_ZSL:
+	case CMD_AXI_CFG_VIDEO_ALL_CHNLS:
+	case CMD_AXI_CFG_ZSL_ALL_CHNLS:
+		CDBG("%s, cfgcmd.cmd_type = %d\n", __func__, cfgcmd.cmd_type);
+		return msm_frame_axi_cfg(sync, &cfgcmd);
+
+	case CMD_AXI_CFG_VPE:
+	case CMD_AXI_CFG_SNAP_VPE:
+	case CMD_AXI_CFG_SNAP_THUMB_VPE:
+		return msm_vpe_frame_cfg(sync, (void *)&cfgcmd);
+
+	case CMD_STATS_AXI_CFG:
+	case CMD_STATS_AF_AXI_CFG:
+		return msm_stats_axi_cfg(sync, &cfgcmd);
+
+	default:
+		pr_err("%s: unknown command type %d\n",
+			__func__,
+			cfgcmd.cmd_type);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int __msm_get_pic(struct msm_sync *sync,
+		struct msm_frame *frame)
+{
+
+	int rc = 0;
+	struct msm_queue_cmd *qcmd = NULL;
+	struct msm_vfe_resp *vdata;
+	struct msm_vfe_phy_info *pphy;
+	struct msm_pmem_info pmem_info;
+	struct msm_frame *pframe;
+
+	qcmd = msm_dequeue(&sync->pict_q, list_pict);
+
+	if (!qcmd) {
+		pr_err("%s: no pic frame.\n", __func__);
+		return -EAGAIN;
+	}
+
+	if (MSM_CAM_Q_PP_MSG != qcmd->type) {
+		vdata = (struct msm_vfe_resp *)(qcmd->command);
+		pphy = &vdata->phy;
+
+		rc = msm_pmem_frame_ptov_lookup2(sync,
+				pphy->p0_phy,
+				&pmem_info,
+				1); /* mark pic frame in use */
+
+		if (rc < 0) {
+			pr_err("%s: cannot get pic frame, invalid lookup"
+				" address p0_phy add  %x p1_phy add%x\n",
+				__func__, pphy->p0_phy, pphy->p1_phy);
+			goto err;
+		}
+
+		frame->ts = qcmd->ts;
+		frame->buffer = (unsigned long)pmem_info.vaddr;
+		frame->planar0_off = pmem_info.planar0_off;
+		frame->planar1_off = pmem_info.planar1_off;
+		frame->fd = pmem_info.fd;
+		if (sync->stereocam_enabled &&
+			sync->stereo_state != STEREO_RAW_SNAP_STARTED) {
+			if (pmem_info.type == MSM_PMEM_THUMBNAIL_VPE)
+				frame->path = OUTPUT_TYPE_T;
+			else
+				frame->path = OUTPUT_TYPE_S;
+		} else
+			frame->path = vdata->phy.output_id;
+
+		CDBG("%s:p0_phy add %x, p0_phy add %x, qcmd %x, virt_addr %x\n",
+			__func__, pphy->p0_phy,
+			pphy->p1_phy, (int) qcmd, (int) frame->buffer);
+	} else { /* PP */
+		pframe = (struct msm_frame *)(qcmd->command);
+		frame->ts = qcmd->ts;
+		frame->buffer = pframe->buffer;
+		frame->planar0_off = pframe->planar0_off;
+		frame->planar1_off = pframe->planar1_off;
+		frame->fd = pframe->fd;
+		frame->path = pframe->path;
+		CDBG("%s: PP y_off %x, cbcr_off %x, path %d vaddr 0x%x\n",
+		__func__, frame->planar0_off, frame->planar1_off, frame->path,
+		(int) frame->buffer);
+	}
+
+err:
+	free_qcmd(qcmd);
+
+	return rc;
+}
+
+static int msm_get_pic(struct msm_sync *sync, void __user *arg)
+{
+	int rc = 0;
+	struct msm_frame frame;
+
+	if (copy_from_user(&frame,
+				arg,
+				sizeof(struct msm_frame))) {
+		ERR_COPY_FROM_USER();
+		return -EFAULT;
+	}
+
+	rc = __msm_get_pic(sync, &frame);
+	if (rc < 0)
+		return rc;
+
+	if (sync->croplen && (!sync->stereocam_enabled)) {
+		if (frame.croplen != sync->croplen) {
+			pr_err("%s: invalid frame croplen %d,"
+				"expecting %d\n",
+				__func__,
+				frame.croplen,
+				sync->croplen);
+			return -EINVAL;
+		}
+
+		if (copy_to_user((void *)frame.cropinfo,
+				sync->cropinfo,
+				sync->croplen)) {
+			ERR_COPY_TO_USER();
+			return -EFAULT;
+		}
+	}
+	CDBG("%s: copy snapshot frame to user\n", __func__);
+	if (copy_to_user((void *)arg,
+				&frame, sizeof(struct msm_frame))) {
+		ERR_COPY_TO_USER();
+		rc = -EFAULT;
+	}
+
+	CDBG("%s: got pic frame\n", __func__);
+
+	return rc;
+}
+
+static int msm_set_crop(struct msm_sync *sync, void __user *arg)
+{
+	struct crop_info crop;
+
+	mutex_lock(&sync->lock);
+	if (copy_from_user(&crop,
+				arg,
+				sizeof(struct crop_info))) {
+		ERR_COPY_FROM_USER();
+		mutex_unlock(&sync->lock);
+		return -EFAULT;
+	}
+
+	if (crop.len != CROP_LEN) {
+		mutex_unlock(&sync->lock);
+		return -EINVAL;
+	}
+
+	if (!sync->croplen) {
+		sync->cropinfo = kmalloc(crop.len, GFP_KERNEL);
+		if (!sync->cropinfo) {
+			mutex_unlock(&sync->lock);
+			return -ENOMEM;
+		}
+	}
+
+	if (copy_from_user(sync->cropinfo,
+				crop.info,
+				crop.len)) {
+		ERR_COPY_FROM_USER();
+		sync->croplen = 0;
+		kfree(sync->cropinfo);
+		mutex_unlock(&sync->lock);
+		return -EFAULT;
+	}
+
+	sync->croplen = crop.len;
+
+	mutex_unlock(&sync->lock);
+	return 0;
+}
+
+static int msm_error_config(struct msm_sync *sync, void __user *arg)
+{
+	struct msm_queue_cmd *qcmd =
+		kmalloc(sizeof(struct msm_queue_cmd), GFP_KERNEL);
+
+	qcmd->command = NULL;
+
+	if (qcmd)
+		atomic_set(&(qcmd->on_heap), 1);
+
+	if (copy_from_user(&(qcmd->error_code), arg, sizeof(uint32_t))) {
+		ERR_COPY_FROM_USER();
+		free_qcmd(qcmd);
+		return -EFAULT;
+	}
+
+	pr_err("%s: Enqueue Fake Frame with error code = %d\n", __func__,
+		qcmd->error_code);
+	msm_enqueue(&sync->frame_q, &qcmd->list_frame);
+	return 0;
+}
+
+static int msm_set_fd_roi(struct msm_sync *sync, void __user *arg)
+{
+	struct fd_roi_info fd_roi;
+
+	mutex_lock(&sync->lock);
+	if (copy_from_user(&fd_roi,
+			arg,
+			sizeof(struct fd_roi_info))) {
+		ERR_COPY_FROM_USER();
+		mutex_unlock(&sync->lock);
+		return -EFAULT;
+	}
+	if (fd_roi.info_len <= 0) {
+		mutex_unlock(&sync->lock);
+		return -EFAULT;
+	}
+
+	if (!sync->fdroiinfo.info) {
+		sync->fdroiinfo.info = kmalloc(fd_roi.info_len, GFP_KERNEL);
+		if (!sync->fdroiinfo.info) {
+			mutex_unlock(&sync->lock);
+			return -ENOMEM;
+		}
+		sync->fdroiinfo.info_len = fd_roi.info_len;
+	} else if (sync->fdroiinfo.info_len < fd_roi.info_len) {
+		mutex_unlock(&sync->lock);
+		return -EINVAL;
+    }
+
+	if (copy_from_user(sync->fdroiinfo.info,
+			fd_roi.info,
+			fd_roi.info_len)) {
+		ERR_COPY_FROM_USER();
+		kfree(sync->fdroiinfo.info);
+		sync->fdroiinfo.info = NULL;
+		mutex_unlock(&sync->lock);
+		return -EFAULT;
+	}
+	mutex_unlock(&sync->lock);
+	return 0;
+}
+
+static int msm_pp_grab(struct msm_sync *sync, void __user *arg)
+{
+	uint32_t enable;
+	if (copy_from_user(&enable, arg, sizeof(enable))) {
+		ERR_COPY_FROM_USER();
+		return -EFAULT;
+	} else {
+		enable &= PP_MASK;
+		if (enable & (enable - 1)) {
+			CDBG("%s: more than one PP request!\n",
+				__func__);
+		}
+		if (sync->pp_mask) {
+			if (enable) {
+				CDBG("%s: postproc %x is already enabled\n",
+					__func__, sync->pp_mask & enable);
+			} else {
+				sync->pp_mask &= enable;
+				CDBG("%s: sync->pp_mask %d enable %d\n",
+					__func__, sync->pp_mask, enable);
+			}
+		}
+
+		CDBG("%s: sync->pp_mask %d enable %d\n", __func__,
+			sync->pp_mask, enable);
+		sync->pp_mask |= enable;
+	}
+
+	return 0;
+}
+
+static int msm_put_st_frame(struct msm_sync *sync, void __user *arg)
+{
+	unsigned long flags;
+	unsigned long st_pphy;
+	if (sync->stereocam_enabled) {
+		/* Make stereo frame ready for VPE. */
+		struct msm_st_frame stereo_frame_half;
+
+		if (copy_from_user(&stereo_frame_half, arg,
+			sizeof(stereo_frame_half))) {
+			ERR_COPY_FROM_USER();
+			return -EFAULT;
+		}
+
+		if (stereo_frame_half.type == OUTPUT_TYPE_ST_L) {
+			struct msm_vfe_resp *vfe_rp;
+			struct msm_queue_cmd *qcmd;
+
+			spin_lock_irqsave(&pp_stereocam_spinlock, flags);
+			if (!sync->pp_stereocam) {
+				pr_warning("%s: no stereo frame to deliver!\n",
+					__func__);
+				spin_unlock_irqrestore(&pp_stereocam_spinlock,
+					flags);
+				return -EINVAL;
+			}
+			CDBG("%s: delivering left frame to VPE\n", __func__);
+
+			qcmd = sync->pp_stereocam;
+			sync->pp_stereocam = NULL;
+			spin_unlock_irqrestore(&pp_stereocam_spinlock, flags);
+
+			vfe_rp = (struct msm_vfe_resp *)qcmd->command;
+
+			CDBG("%s: Left Py = 0x%x y_off = %d cbcr_off = %d\n",
+				__func__, vfe_rp->phy.p0_phy,
+				stereo_frame_half.L.buf_p0_off,
+				stereo_frame_half.L.buf_p1_off);
+
+			sync->vpefn.vpe_cfg_offset(stereo_frame_half.packing,
+			vfe_rp->phy.p0_phy + stereo_frame_half.L.buf_p0_off,
+			vfe_rp->phy.p1_phy + stereo_frame_half.L.buf_p1_off,
+			&(qcmd->ts), OUTPUT_TYPE_ST_L, stereo_frame_half.L,
+			stereo_frame_half.frame_id);
+
+			free_qcmd(qcmd);
+		} else if (stereo_frame_half.type == OUTPUT_TYPE_ST_R) {
+			CDBG("%s: delivering right frame to VPE\n", __func__);
+			spin_lock_irqsave(&st_frame_spinlock, flags);
+
+			sync->stcam_conv_value =
+				stereo_frame_half.buf_info.stcam_conv_value;
+			sync->stcam_quality_ind =
+				stereo_frame_half.buf_info.stcam_quality_ind;
+
+			st_pphy = msm_pmem_frame_vtop_lookup(sync,
+				stereo_frame_half.buf_info.buffer,
+				stereo_frame_half.buf_info.planar0_off,
+				stereo_frame_half.buf_info.planar1_off,
+				stereo_frame_half.buf_info.planar2_off,
+				stereo_frame_half.buf_info.fd,
+				0); /* Do not change the active flag. */
+
+			sync->vpefn.vpe_cfg_offset(stereo_frame_half.packing,
+				st_pphy + stereo_frame_half.R.buf_p0_off,
+				st_pphy + stereo_frame_half.R.buf_p1_off,
+				NULL, OUTPUT_TYPE_ST_R, stereo_frame_half.R,
+				stereo_frame_half.frame_id);
+
+			spin_unlock_irqrestore(&st_frame_spinlock, flags);
+		} else {
+			CDBG("%s: Invalid Msg\n", __func__);
+		}
+	}
+
+	return 0;
+}
+
+static struct msm_queue_cmd *msm_get_pp_qcmd(struct msm_frame* frame)
+{
+	struct msm_queue_cmd *qcmd =
+		kmalloc(sizeof(struct msm_queue_cmd) +
+			sizeof(struct msm_frame), GFP_ATOMIC);
+	qcmd->command = (struct msm_frame *)(qcmd + 1);
+
+	qcmd->type = MSM_CAM_Q_PP_MSG;
+
+	ktime_get_ts(&(qcmd->ts));
+	memcpy(qcmd->command, frame, sizeof(struct msm_frame));
+	atomic_set(&(qcmd->on_heap), 1);
+	return qcmd;
+}
+
+static int msm_pp_release(struct msm_sync *sync, void __user *arg)
+{
+	unsigned long flags;
+
+	if (!sync->pp_mask) {
+		pr_warning("%s: pp not in progress for\n", __func__);
+		return -EINVAL;
+	}
+	if (sync->pp_mask & PP_PREV) {
+
+
+		spin_lock_irqsave(&pp_prev_spinlock, flags);
+		if (!sync->pp_prev) {
+			pr_err("%s: no preview frame to deliver!\n",
+				__func__);
+			spin_unlock_irqrestore(&pp_prev_spinlock,
+				flags);
+			return -EINVAL;
+		}
+		CDBG("%s: delivering pp_prev\n", __func__);
+
+			if (sync->frame_q.len <= 100 &&
+				sync->event_q.len <= 100) {
+					msm_enqueue(&sync->frame_q,
+						&sync->pp_prev->list_frame);
+			} else {
+				pr_err("%s, Error Queue limit exceeded f_q=%d,\
+					e_q = %d\n",
+					__func__, sync->frame_q.len,
+					sync->event_q.len);
+				free_qcmd(sync->pp_prev);
+				goto done;
+			}
+
+			sync->pp_prev = NULL;
+			spin_unlock_irqrestore(&pp_prev_spinlock, flags);
+		goto done;
+	}
+
+	if ((sync->pp_mask & PP_SNAP) ||
+		(sync->pp_mask & PP_RAW_SNAP)) {
+		struct msm_frame frame;
+		struct msm_queue_cmd *qcmd;
+
+		if (copy_from_user(&frame,
+			arg,
+			sizeof(struct msm_frame))) {
+			ERR_COPY_FROM_USER();
+			return -EFAULT;
+		}
+		qcmd = msm_get_pp_qcmd(&frame);
+		if (!qcmd) {
+			pr_err("%s: no snapshot to deliver!\n", __func__);
+			return -EINVAL;
+		}
+		CDBG("%s: delivering pp snap\n", __func__);
+		msm_enqueue(&sync->pict_q, &qcmd->list_pict);
+	}
+
+done:
+	return 0;
+}
+
+static long msm_ioctl_common(struct msm_cam_device *pmsm,
+		unsigned int cmd,
+		void __user *argp)
+{
+	switch (cmd) {
+	case MSM_CAM_IOCTL_REGISTER_PMEM:
+		CDBG("%s cmd = MSM_CAM_IOCTL_REGISTER_PMEM\n", __func__);
+		return msm_register_pmem(pmsm->sync, argp);
+	case MSM_CAM_IOCTL_UNREGISTER_PMEM:
+		CDBG("%s cmd = MSM_CAM_IOCTL_UNREGISTER_PMEM\n", __func__);
+		return msm_pmem_table_del(pmsm->sync, argp);
+	case MSM_CAM_IOCTL_RELEASE_FRAME_BUFFER:
+		CDBG("%s cmd = MSM_CAM_IOCTL_RELEASE_FRAME_BUFFER\n", __func__);
+		return msm_put_frame_buffer(pmsm->sync, argp);
+		break;
+	default:
+		CDBG("%s cmd invalid\n", __func__);
+		return -EINVAL;
+	}
+}
+
+static long msm_ioctl_config(struct file *filep, unsigned int cmd,
+	unsigned long arg)
+{
+	int rc = -EINVAL;
+	void __user *argp = (void __user *)arg;
+	struct msm_cam_device *pmsm = filep->private_data;
+
+	CDBG("%s: cmd %d\n", __func__, _IOC_NR(cmd));
+
+	switch (cmd) {
+	case MSM_CAM_IOCTL_GET_SENSOR_INFO:
+		rc = msm_get_sensor_info(pmsm->sync, argp);
+		break;
+
+	case MSM_CAM_IOCTL_CONFIG_VFE:
+		/* Coming from config thread for update */
+		rc = msm_config_vfe(pmsm->sync, argp);
+		break;
+
+	case MSM_CAM_IOCTL_CONFIG_VPE:
+		/* Coming from config thread for update */
+		rc = msm_config_vpe(pmsm->sync, argp);
+		break;
+
+	case MSM_CAM_IOCTL_GET_STATS:
+		/* Coming from config thread wait
+		 * for vfe statistics and control requests */
+		rc = msm_get_stats(pmsm->sync, argp);
+		break;
+
+	case MSM_CAM_IOCTL_ENABLE_VFE:
+		/* This request comes from control thread:
+		 * enable either QCAMTASK or VFETASK */
+		rc = msm_enable_vfe(pmsm->sync, argp);
+		break;
+
+	case MSM_CAM_IOCTL_DISABLE_VFE:
+		/* This request comes from control thread:
+		 * disable either QCAMTASK or VFETASK */
+		rc = msm_disable_vfe(pmsm->sync, argp);
+		break;
+
+	case MSM_CAM_IOCTL_VFE_APPS_RESET:
+		msm_camio_vfe_blk_reset();
+		rc = 0;
+		break;
+
+	case MSM_CAM_IOCTL_RELEASE_STATS_BUFFER:
+		rc = msm_put_stats_buffer(pmsm->sync, argp);
+		break;
+
+	case MSM_CAM_IOCTL_AXI_CONFIG:
+	case MSM_CAM_IOCTL_AXI_VPE_CONFIG:
+		rc = msm_axi_config(pmsm->sync, argp);
+		break;
+
+	case MSM_CAM_IOCTL_SET_CROP:
+		rc = msm_set_crop(pmsm->sync, argp);
+		break;
+
+	case MSM_CAM_IOCTL_SET_FD_ROI:
+		rc = msm_set_fd_roi(pmsm->sync, argp);
+		break;
+
+	case MSM_CAM_IOCTL_PICT_PP:
+		/* Grab one preview frame or one snapshot
+		 * frame.
+		 */
+		rc = msm_pp_grab(pmsm->sync, argp);
+		break;
+
+	case MSM_CAM_IOCTL_PICT_PP_DONE:
+		/* Release the preview of snapshot frame
+		 * that was grabbed.
+		 */
+		rc = msm_pp_release(pmsm->sync, argp);
+		break;
+
+	case MSM_CAM_IOCTL_PUT_ST_FRAME:
+		/* Release the left or right frame
+		 * that was sent for stereo processing.
+		 */
+		rc = msm_put_st_frame(pmsm->sync, argp);
+		break;
+
+	case MSM_CAM_IOCTL_SENSOR_IO_CFG:
+		rc = pmsm->sync->sctrl.s_config(argp);
+		break;
+
+	case MSM_CAM_IOCTL_FLASH_LED_CFG: {
+		uint32_t led_state;
+		if (copy_from_user(&led_state, argp, sizeof(led_state))) {
+			ERR_COPY_FROM_USER();
+			rc = -EFAULT;
+		} else
+			rc = msm_camera_flash_set_led_state(pmsm->sync->
+					sdata->flash_data, led_state);
+		break;
+	}
+
+	case MSM_CAM_IOCTL_STROBE_FLASH_CFG: {
+		uint32_t flash_type;
+		if (copy_from_user(&flash_type, argp, sizeof(flash_type))) {
+			pr_err("msm_strobe_flash_init failed");
+			ERR_COPY_FROM_USER();
+			rc = -EFAULT;
+		} else {
+			CDBG("msm_strobe_flash_init enter");
+			rc = msm_strobe_flash_init(pmsm->sync, flash_type);
+		}
+		break;
+	}
+
+	case MSM_CAM_IOCTL_STROBE_FLASH_RELEASE:
+		if (pmsm->sync->sdata->strobe_flash_data) {
+			rc = pmsm->sync->sfctrl.strobe_flash_release(
+				pmsm->sync->sdata->strobe_flash_data, 0);
+		}
+		break;
+
+	case MSM_CAM_IOCTL_STROBE_FLASH_CHARGE: {
+		uint32_t charge_en;
+		if (copy_from_user(&charge_en, argp, sizeof(charge_en))) {
+			ERR_COPY_FROM_USER();
+			rc = -EFAULT;
+		} else
+			rc = pmsm->sync->sfctrl.strobe_flash_charge(
+			pmsm->sync->sdata->strobe_flash_data->flash_charge,
+			charge_en, pmsm->sync->sdata->strobe_flash_data->
+				flash_recharge_duration);
+		break;
+	}
+
+	case MSM_CAM_IOCTL_FLASH_CTRL: {
+		struct flash_ctrl_data flash_info;
+		if (copy_from_user(&flash_info, argp, sizeof(flash_info))) {
+			ERR_COPY_FROM_USER();
+			rc = -EFAULT;
+		} else
+			rc = msm_flash_ctrl(pmsm->sync->sdata, &flash_info);
+
+		break;
+	}
+
+	case MSM_CAM_IOCTL_ERROR_CONFIG:
+		rc = msm_error_config(pmsm->sync, argp);
+		break;
+
+	case MSM_CAM_IOCTL_ABORT_CAPTURE: {
+		unsigned long flags = 0;
+		CDBG("get_pic:MSM_CAM_IOCTL_ABORT_CAPTURE\n");
+		spin_lock_irqsave(&pmsm->sync->abort_pict_lock, flags);
+		pmsm->sync->get_pic_abort = 1;
+		spin_unlock_irqrestore(&pmsm->sync->abort_pict_lock, flags);
+		wake_up(&(pmsm->sync->pict_q.wait));
+		rc = 0;
+		break;
+	}
+
+	default:
+		rc = msm_ioctl_common(pmsm, cmd, argp);
+		break;
+	}
+
+	CDBG("%s: cmd %d DONE\n", __func__, _IOC_NR(cmd));
+	return rc;
+}
+
+static int msm_unblock_poll_frame(struct msm_sync *);
+
+static long msm_ioctl_frame(struct file *filep, unsigned int cmd,
+	unsigned long arg)
+{
+	int rc = -EINVAL;
+	void __user *argp = (void __user *)arg;
+	struct msm_cam_device *pmsm = filep->private_data;
+
+
+	switch (cmd) {
+	case MSM_CAM_IOCTL_GETFRAME:
+		/* Coming from frame thread to get frame
+		 * after SELECT is done */
+		rc = msm_get_frame(pmsm->sync, argp);
+		break;
+	case MSM_CAM_IOCTL_RELEASE_FRAME_BUFFER:
+		rc = msm_put_frame_buffer(pmsm->sync, argp);
+		break;
+	case MSM_CAM_IOCTL_UNBLOCK_POLL_FRAME:
+		rc = msm_unblock_poll_frame(pmsm->sync);
+		break;
+	default:
+		break;
+	}
+
+	return rc;
+}
+
+static int msm_unblock_poll_pic(struct msm_sync *sync);
+static long msm_ioctl_pic(struct file *filep, unsigned int cmd,
+	unsigned long arg)
+{
+	int rc = -EINVAL;
+	void __user *argp = (void __user *)arg;
+	struct msm_cam_device *pmsm = filep->private_data;
+
+
+	switch (cmd) {
+	case MSM_CAM_IOCTL_GET_PICTURE:
+		rc = msm_get_pic(pmsm->sync, argp);
+		break;
+	case MSM_CAM_IOCTL_RELEASE_PIC_BUFFER:
+		rc = msm_put_pic_buffer(pmsm->sync, argp);
+		break;
+	case MSM_CAM_IOCTL_UNBLOCK_POLL_PIC_FRAME:
+		rc = msm_unblock_poll_pic(pmsm->sync);
+		break;
+	default:
+		break;
+	}
+
+	return rc;
+}
+
+
+static long msm_ioctl_control(struct file *filep, unsigned int cmd,
+	unsigned long arg)
+{
+	int rc = -EINVAL;
+	void __user *argp = (void __user *)arg;
+	struct msm_control_device *ctrl_pmsm = filep->private_data;
+	struct msm_cam_device *pmsm = ctrl_pmsm->pmsm;
+
+	switch (cmd) {
+	case MSM_CAM_IOCTL_CTRL_COMMAND:
+		/* Coming from control thread, may need to wait for
+		 * command status */
+		CDBG("calling msm_control kernel msm_ioctl_control\n");
+		mutex_lock(&ctrl_cmd_lock);
+		rc = msm_control(ctrl_pmsm, 1, argp);
+		mutex_unlock(&ctrl_cmd_lock);
+		break;
+	case MSM_CAM_IOCTL_CTRL_COMMAND_2:
+		/* Sends a message, returns immediately */
+		rc = msm_control(ctrl_pmsm, 0, argp);
+		break;
+	case MSM_CAM_IOCTL_CTRL_CMD_DONE:
+		/* Config thread calls the control thread to notify it
+		 * of the result of a MSM_CAM_IOCTL_CTRL_COMMAND.
+		 */
+		rc = msm_ctrl_cmd_done(ctrl_pmsm, argp);
+		break;
+	case MSM_CAM_IOCTL_GET_SENSOR_INFO:
+		rc = msm_get_sensor_info(pmsm->sync, argp);
+		break;
+	case MSM_CAM_IOCTL_GET_CAMERA_INFO:
+		rc = msm_get_camera_info(argp);
+		break;
+	default:
+		rc = msm_ioctl_common(pmsm, cmd, argp);
+		break;
+	}
+
+	return rc;
+}
+
+static int __msm_release(struct msm_sync *sync)
+{
+	struct msm_pmem_region *region;
+	struct hlist_node *hnode;
+	struct hlist_node *n;
+
+	mutex_lock(&sync->lock);
+	if (sync->opencnt)
+		sync->opencnt--;
+	pr_info("%s, open count =%d\n", __func__, sync->opencnt);
+	if (!sync->opencnt) {
+		/* need to clean up system resource */
+		pr_info("%s, release VFE\n", __func__);
+		if (sync->core_powered_on) {
+			if (sync->vfefn.vfe_release)
+				sync->vfefn.vfe_release(sync->pdev);
+			/*sensor release */
+			pr_info("%s, release Sensor\n", __func__);
+			sync->sctrl.s_release();
+			CDBG("%s, msm_camio_sensor_clk_off\n", __func__);
+			msm_camio_sensor_clk_off(sync->pdev);
+			if (sync->sfctrl.strobe_flash_release) {
+				CDBG("%s, strobe_flash_release\n", __func__);
+				sync->sfctrl.strobe_flash_release(
+				sync->sdata->strobe_flash_data, 1);
+			}
+		}
+		kfree(sync->cropinfo);
+		sync->cropinfo = NULL;
+		sync->croplen = 0;
+		CDBG("%s, free frame pmem region\n", __func__);
+		hlist_for_each_entry_safe(region, hnode, n,
+				&sync->pmem_frames, list) {
+			hlist_del(hnode);
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+				ion_free(client_for_ion, region->handle);
+#else
+			put_pmem_file(region->file);
+#endif
+			kfree(region);
+		}
+		CDBG("%s, free stats pmem region\n", __func__);
+		hlist_for_each_entry_safe(region, hnode, n,
+				&sync->pmem_stats, list) {
+			hlist_del(hnode);
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+				ion_free(client_for_ion, region->handle);
+#else
+			put_pmem_file(region->file);
+#endif
+			kfree(region);
+		}
+		msm_queue_drain(&sync->pict_q, list_pict);
+		msm_queue_drain(&sync->event_q, list_config);
+
+		wake_unlock(&sync->wake_lock);
+		sync->apps_id = NULL;
+		sync->core_powered_on = 0;
+	}
+	mutex_unlock(&sync->lock);
+	ion_client_destroy(client_for_ion);
+
+	return 0;
+}
+
+static int msm_release_config(struct inode *node, struct file *filep)
+{
+	int rc;
+	struct msm_cam_device *pmsm = filep->private_data;
+	pr_info("%s: %s\n", __func__, filep->f_path.dentry->d_name.name);
+	rc = __msm_release(pmsm->sync);
+	if (!rc) {
+		msm_queue_drain(&pmsm->sync->event_q, list_config);
+		atomic_set(&pmsm->opened, 0);
+	}
+	return rc;
+}
+
+static int msm_release_control(struct inode *node, struct file *filep)
+{
+	int rc;
+	struct msm_control_device *ctrl_pmsm = filep->private_data;
+	struct msm_cam_device *pmsm = ctrl_pmsm->pmsm;
+	pr_info("%s: %s\n", __func__, filep->f_path.dentry->d_name.name);
+	g_v4l2_opencnt--;
+	mutex_lock(&pmsm->sync->lock);
+	if (pmsm->sync->core_powered_on && pmsm->sync->vfefn.vfe_stop) {
+		pr_info("%s, stop vfe if active\n", __func__);
+		pmsm->sync->vfefn.vfe_stop();
+	}
+	mutex_unlock(&pmsm->sync->lock);
+	rc = __msm_release(pmsm->sync);
+	if (!rc) {
+		msm_queue_drain(&ctrl_pmsm->ctrl_q, list_control);
+		kfree(ctrl_pmsm);
+	}
+	return rc;
+}
+
+static int msm_release_frame(struct inode *node, struct file *filep)
+{
+	int rc;
+	struct msm_cam_device *pmsm = filep->private_data;
+	pr_info("%s: %s\n", __func__, filep->f_path.dentry->d_name.name);
+	rc = __msm_release(pmsm->sync);
+	if (!rc) {
+		msm_queue_drain(&pmsm->sync->frame_q, list_frame);
+		atomic_set(&pmsm->opened, 0);
+	}
+	return rc;
+}
+
+
+static int msm_release_pic(struct inode *node, struct file *filep)
+{
+	int rc;
+	struct msm_cam_device *pmsm = filep->private_data;
+	CDBG("%s: %s\n", __func__, filep->f_path.dentry->d_name.name);
+	rc = __msm_release(pmsm->sync);
+	if (!rc) {
+		msm_queue_drain(&pmsm->sync->pict_q, list_pict);
+		atomic_set(&pmsm->opened, 0);
+	}
+	return rc;
+}
+
+static int msm_unblock_poll_pic(struct msm_sync *sync)
+{
+	unsigned long flags;
+	CDBG("%s\n", __func__);
+	spin_lock_irqsave(&sync->pict_q.lock, flags);
+	sync->unblock_poll_pic_frame = 1;
+	wake_up(&sync->pict_q.wait);
+	spin_unlock_irqrestore(&sync->pict_q.lock, flags);
+	return 0;
+}
+
+static int msm_unblock_poll_frame(struct msm_sync *sync)
+{
+	unsigned long flags;
+	CDBG("%s\n", __func__);
+	spin_lock_irqsave(&sync->frame_q.lock, flags);
+	sync->unblock_poll_frame = 1;
+	wake_up(&sync->frame_q.wait);
+	spin_unlock_irqrestore(&sync->frame_q.lock, flags);
+	return 0;
+}
+
+static unsigned int __msm_poll_frame(struct msm_sync *sync,
+		struct file *filep,
+		struct poll_table_struct *pll_table)
+{
+	int rc = 0;
+	unsigned long flags;
+
+	poll_wait(filep, &sync->frame_q.wait, pll_table);
+
+	spin_lock_irqsave(&sync->frame_q.lock, flags);
+	if (!list_empty_careful(&sync->frame_q.list))
+		/* frame ready */
+		rc = POLLIN | POLLRDNORM;
+	if (sync->unblock_poll_frame) {
+		CDBG("%s: sync->unblock_poll_frame is true\n", __func__);
+		rc |= POLLPRI;
+		sync->unblock_poll_frame = 0;
+	}
+	spin_unlock_irqrestore(&sync->frame_q.lock, flags);
+
+	return rc;
+}
+
+static unsigned int __msm_poll_pic(struct msm_sync *sync,
+		struct file *filep,
+		struct poll_table_struct *pll_table)
+{
+	int rc = 0;
+	unsigned long flags;
+
+	poll_wait(filep, &sync->pict_q.wait , pll_table);
+	spin_lock_irqsave(&sync->abort_pict_lock, flags);
+	if (sync->get_pic_abort == 1) {
+		/* TODO: need to pass an error case */
+		sync->get_pic_abort = 0;
+	}
+	spin_unlock_irqrestore(&sync->abort_pict_lock, flags);
+
+	spin_lock_irqsave(&sync->pict_q.lock, flags);
+	if (!list_empty_careful(&sync->pict_q.list))
+		/* frame ready */
+		rc = POLLIN | POLLRDNORM;
+	if (sync->unblock_poll_pic_frame) {
+		CDBG("%s: sync->unblock_poll_pic_frame is true\n", __func__);
+		rc |= POLLPRI;
+		sync->unblock_poll_pic_frame = 0;
+	}
+	spin_unlock_irqrestore(&sync->pict_q.lock, flags);
+
+	return rc;
+}
+
+static unsigned int msm_poll_frame(struct file *filep,
+	struct poll_table_struct *pll_table)
+{
+	struct msm_cam_device *pmsm = filep->private_data;
+	return __msm_poll_frame(pmsm->sync, filep, pll_table);
+}
+
+static unsigned int msm_poll_pic(struct file *filep,
+	struct poll_table_struct *pll_table)
+{
+	struct msm_cam_device *pmsm = filep->private_data;
+	return __msm_poll_pic(pmsm->sync, filep, pll_table);
+}
+
+static unsigned int __msm_poll_config(struct msm_sync *sync,
+		struct file *filep,
+		struct poll_table_struct *pll_table)
+{
+	int rc = 0;
+	unsigned long flags;
+
+	poll_wait(filep, &sync->event_q.wait, pll_table);
+
+	spin_lock_irqsave(&sync->event_q.lock, flags);
+	if (!list_empty_careful(&sync->event_q.list))
+		/* event ready */
+		rc = POLLIN | POLLRDNORM;
+	spin_unlock_irqrestore(&sync->event_q.lock, flags);
+
+	return rc;
+}
+
+static unsigned int msm_poll_config(struct file *filep,
+	struct poll_table_struct *pll_table)
+{
+	struct msm_cam_device *pmsm = filep->private_data;
+	return __msm_poll_config(pmsm->sync, filep, pll_table);
+}
+
+/*
+ * This function executes in interrupt context.
+ */
+
+static void *msm_vfe_sync_alloc(int size,
+			void *syncdata __attribute__((unused)),
+			gfp_t gfp)
+{
+	struct msm_queue_cmd *qcmd =
+		kzalloc(sizeof(struct msm_queue_cmd) + size, gfp);
+	if (qcmd) {
+		atomic_set(&qcmd->on_heap, 1);
+		return qcmd + 1;
+	}
+	return NULL;
+}
+
+static void *msm_vpe_sync_alloc(int size,
+			void *syncdata __attribute__((unused)),
+			gfp_t gfp)
+{
+	struct msm_queue_cmd *qcmd =
+		kzalloc(sizeof(struct msm_queue_cmd) + size, gfp);
+	if (qcmd) {
+		atomic_set(&qcmd->on_heap, 1);
+		return qcmd + 1;
+	}
+	return NULL;
+}
+
+static void msm_vfe_sync_free(void *ptr)
+{
+	if (ptr) {
+		struct msm_queue_cmd *qcmd =
+			(struct msm_queue_cmd *)ptr;
+		qcmd--;
+		if (atomic_read(&qcmd->on_heap))
+			kfree(qcmd);
+	}
+}
+
+static void msm_vpe_sync_free(void *ptr)
+{
+	if (ptr) {
+		struct msm_queue_cmd *qcmd =
+			(struct msm_queue_cmd *)ptr;
+		qcmd--;
+		if (atomic_read(&qcmd->on_heap))
+			kfree(qcmd);
+	}
+}
+
+/*
+ * This function executes in interrupt context.
+ */
+
+static void msm_vfe_sync(struct msm_vfe_resp *vdata,
+		enum msm_queue qtype, void *syncdata,
+		gfp_t gfp)
+{
+	struct msm_queue_cmd *qcmd = NULL;
+	struct msm_sync *sync = (struct msm_sync *)syncdata;
+	unsigned long flags;
+
+	if (!sync) {
+		pr_err("%s: no context in dsp callback.\n", __func__);
+		return;
+	}
+
+	qcmd = ((struct msm_queue_cmd *)vdata) - 1;
+	qcmd->type = qtype;
+	qcmd->command = vdata;
+
+	ktime_get_ts(&(qcmd->ts));
+
+	if (qtype != MSM_CAM_Q_VFE_MSG)
+		goto vfe_for_config;
+
+	CDBG("%s: vdata->type %d\n", __func__, vdata->type);
+
+	switch (vdata->type) {
+	case VFE_MSG_OUTPUT_P:
+		if (sync->pp_mask & PP_PREV) {
+			CDBG("%s: PP_PREV in progress: p0_add %x p1_add %x\n",
+				__func__,
+				vdata->phy.p0_phy,
+				vdata->phy.p1_phy);
+			spin_lock_irqsave(&pp_prev_spinlock, flags);
+			if (sync->pp_prev)
+				CDBG("%s: overwriting pp_prev!\n",
+					__func__);
+			CDBG("%s: sending preview to config\n", __func__);
+			sync->pp_prev = qcmd;
+			spin_unlock_irqrestore(&pp_prev_spinlock, flags);
+			sync->pp_frame_avail = 1;
+			if (atomic_read(&qcmd->on_heap))
+				atomic_add(1, &qcmd->on_heap);
+			break;
+		}
+		CDBG("%s: msm_enqueue frame_q\n", __func__);
+		if (sync->stereocam_enabled)
+			CDBG("%s: Enqueue VFE_MSG_OUTPUT_P to event_q for "
+				"stereo processing\n", __func__);
+		else {
+			if (sync->frame_q.len <= 100 &&
+				sync->event_q.len <= 100) {
+				if (atomic_read(&qcmd->on_heap))
+					atomic_add(1, &qcmd->on_heap);
+				msm_enqueue(&sync->frame_q, &qcmd->list_frame);
+			} else {
+				pr_err("%s, Error Queue limit exceeded "
+					"f_q = %d, e_q = %d\n",	__func__,
+					sync->frame_q.len, sync->event_q.len);
+				free_qcmd(qcmd);
+				return;
+			}
+		}
+		break;
+
+	case VFE_MSG_OUTPUT_T:
+		if (sync->stereocam_enabled) {
+			spin_lock_irqsave(&pp_stereocam_spinlock, flags);
+
+			/* if out1/2 is currently in progress, save the qcmd
+			and issue only ionce the 1st one completes the 3D
+			pipeline */
+			if (STEREO_SNAP_BUFFER1_PROCESSING ==
+				sync->stereo_state) {
+				sync->pp_stereocam2 = qcmd;
+				spin_unlock_irqrestore(&pp_stereocam_spinlock,
+					flags);
+				if (atomic_read(&qcmd->on_heap))
+					atomic_add(1, &qcmd->on_heap);
+				CDBG("%s: snapshot stereo in progress\n",
+					__func__);
+				return;
+			}
+
+			if (sync->pp_stereocam)
+				CDBG("%s: overwriting pp_stereocam!\n",
+					__func__);
+
+			CDBG("%s: sending stereo frame to config\n", __func__);
+			sync->pp_stereocam = qcmd;
+			sync->stereo_state =
+				STEREO_SNAP_BUFFER1_PROCESSING;
+
+			spin_unlock_irqrestore(&pp_stereocam_spinlock, flags);
+
+			/* Increament on_heap by one because the same qcmd will
+			be used for VPE in msm_pp_release. */
+			if (atomic_read(&qcmd->on_heap))
+				atomic_add(1, &qcmd->on_heap);
+			CDBG("%s: Enqueue VFE_MSG_OUTPUT_T to event_q for "
+				"stereo processing.\n", __func__);
+			break;
+		}
+		if (sync->pp_mask & PP_SNAP) {
+			CDBG("%s: pp sending thumbnail to config\n",
+				__func__);
+		} else {
+			msm_enqueue(&sync->pict_q, &qcmd->list_pict);
+			return;
+		}
+
+	case VFE_MSG_OUTPUT_S:
+		if (sync->stereocam_enabled &&
+			sync->stereo_state != STEREO_RAW_SNAP_STARTED) {
+			spin_lock_irqsave(&pp_stereocam_spinlock, flags);
+
+			/* if out1/2 is currently in progress, save the qcmd
+			and issue only once the 1st one completes the 3D
+			pipeline */
+			if (STEREO_SNAP_BUFFER1_PROCESSING ==
+				sync->stereo_state) {
+				sync->pp_stereocam2 = qcmd;
+				spin_unlock_irqrestore(&pp_stereocam_spinlock,
+					flags);
+				if (atomic_read(&qcmd->on_heap))
+					atomic_add(1, &qcmd->on_heap);
+				CDBG("%s: snapshot stereo in progress\n",
+					__func__);
+				return;
+			}
+			if (sync->pp_stereocam)
+				CDBG("%s: overwriting pp_stereocam!\n",
+					__func__);
+
+			CDBG("%s: sending stereo frame to config\n", __func__);
+			sync->pp_stereocam = qcmd;
+			sync->stereo_state =
+				STEREO_SNAP_BUFFER1_PROCESSING;
+
+			spin_unlock_irqrestore(&pp_stereocam_spinlock, flags);
+
+			/* Increament on_heap by one because the same qcmd will
+			be used for VPE in msm_pp_release. */
+			if (atomic_read(&qcmd->on_heap))
+				atomic_add(1, &qcmd->on_heap);
+			CDBG("%s: Enqueue VFE_MSG_OUTPUT_S to event_q for "
+				"stereo processing.\n", __func__);
+			break;
+		}
+		if (sync->pp_mask & PP_SNAP) {
+			CDBG("%s: pp sending main image to config\n",
+				__func__);
+		} else {
+			CDBG("%s: enqueue to picture queue\n", __func__);
+			msm_enqueue(&sync->pict_q, &qcmd->list_pict);
+			return;
+		}
+		break;
+
+	case VFE_MSG_OUTPUT_V:
+		if (sync->stereocam_enabled) {
+			spin_lock_irqsave(&pp_stereocam_spinlock, flags);
+
+			if (sync->pp_stereocam)
+				CDBG("%s: overwriting pp_stereocam!\n",
+					__func__);
+
+			CDBG("%s: sending stereo frame to config\n", __func__);
+			sync->pp_stereocam = qcmd;
+
+			spin_unlock_irqrestore(&pp_stereocam_spinlock, flags);
+
+			/* Increament on_heap by one because the same qcmd will
+			be used for VPE in msm_pp_release. */
+			if (atomic_read(&qcmd->on_heap))
+				atomic_add(1, &qcmd->on_heap);
+			CDBG("%s: Enqueue VFE_MSG_OUTPUT_V to event_q for "
+				"stereo processing.\n", __func__);
+			break;
+		}
+		if (sync->vpefn.vpe_cfg_update) {
+			CDBG("dis_en = %d\n", *sync->vpefn.dis);
+			if (*(sync->vpefn.dis)) {
+				memset(&(vdata->vpe_bf), 0,
+					sizeof(vdata->vpe_bf));
+
+				if (sync->cropinfo != NULL)
+					vdata->vpe_bf.vpe_crop =
+				*(struct video_crop_t *)(sync->cropinfo);
+
+				vdata->vpe_bf.p0_phy = vdata->phy.p0_phy;
+				vdata->vpe_bf.p1_phy = vdata->phy.p1_phy;
+				vdata->vpe_bf.ts = (qcmd->ts);
+				vdata->vpe_bf.frame_id = vdata->phy.frame_id;
+				qcmd->command = vdata;
+				msm_enqueue_vpe(&sync->vpe_q,
+					&qcmd->list_vpe_frame);
+				return;
+			} else if (sync->vpefn.vpe_cfg_update(sync->cropinfo)) {
+				CDBG("%s: msm_enqueue video frame to vpe time "
+					"= %ld\n", __func__, qcmd->ts.tv_nsec);
+
+				sync->vpefn.send_frame_to_vpe(
+					vdata->phy.p0_phy,
+					vdata->phy.p1_phy,
+					&(qcmd->ts), OUTPUT_TYPE_V);
+
+				free_qcmd(qcmd);
+				return;
+			} else {
+				CDBG("%s: msm_enqueue video frame_q\n",
+					__func__);
+				if (sync->liveshot_enabled) {
+					CDBG("%s: msm_enqueue liveshot\n",
+						__func__);
+					vdata->phy.output_id |= OUTPUT_TYPE_L;
+					sync->liveshot_enabled = false;
+				}
+				if (sync->frame_q.len <= 100 &&
+					sync->event_q.len <= 100) {
+						msm_enqueue(&sync->frame_q,
+							&qcmd->list_frame);
+				} else {
+					pr_err("%s, Error Queue limit exceeded\
+						f_q = %d, e_q = %d\n",
+						__func__, sync->frame_q.len,
+						sync->event_q.len);
+					free_qcmd(qcmd);
+				}
+
+				return;
+			}
+		} else {
+			CDBG("%s: msm_enqueue video frame_q\n",	__func__);
+			if (sync->frame_q.len <= 100 &&
+				sync->event_q.len <= 100) {
+				msm_enqueue(&sync->frame_q, &qcmd->list_frame);
+			} else {
+				pr_err("%s, Error Queue limit exceeded\
+					f_q = %d, e_q = %d\n",
+					__func__, sync->frame_q.len,
+					sync->event_q.len);
+				free_qcmd(qcmd);
+			}
+
+			return;
+		}
+
+	case VFE_MSG_SNAPSHOT:
+		if (sync->pp_mask & (PP_SNAP | PP_RAW_SNAP)) {
+			CDBG("%s: PP_SNAP in progress: pp_mask %x\n",
+				__func__, sync->pp_mask);
+		} else {
+			if (atomic_read(&qcmd->on_heap))
+				atomic_add(1, &qcmd->on_heap);
+			CDBG("%s: VFE_MSG_SNAPSHOT store\n",
+				__func__);
+			if (sync->stereocam_enabled &&
+				sync->stereo_state != STEREO_RAW_SNAP_STARTED) {
+				sync->pp_stereosnap = qcmd;
+				return;
+			}
+		}
+		break;
+
+	case VFE_MSG_COMMON:
+		CDBG("%s: qtype %d, comp stats, enqueue event_q.\n",
+			__func__, vdata->type);
+		break;
+
+	case VFE_MSG_GENERAL:
+		CDBG("%s: qtype %d, general msg, enqueue event_q.\n",
+			__func__, vdata->type);
+		break;
+
+	default:
+		CDBG("%s: qtype %d not handled\n", __func__, vdata->type);
+		/* fall through, send to config. */
+	}
+
+vfe_for_config:
+	CDBG("%s: msm_enqueue event_q\n", __func__);
+	if (sync->frame_q.len <= 100 && sync->event_q.len <= 100) {
+		msm_enqueue(&sync->event_q, &qcmd->list_config);
+	} else if (sync->event_q.len > 100) {
+		pr_err("%s, Error Event Queue limit exceeded f_q = %d, e_q = %d\n",
+			__func__, sync->frame_q.len, sync->event_q.len);
+		qcmd->error_code = 0xffffffff;
+		qcmd->command = NULL;
+		msm_enqueue(&sync->frame_q, &qcmd->list_frame);
+	} else {
+		pr_err("%s, Error Queue limit exceeded f_q = %d, e_q = %d\n",
+			__func__, sync->frame_q.len, sync->event_q.len);
+		free_qcmd(qcmd);
+	}
+
+}
+
+static void msm_vpe_sync(struct msm_vpe_resp *vdata,
+	enum msm_queue qtype, void *syncdata, void *ts, gfp_t gfp)
+{
+	struct msm_queue_cmd *qcmd = NULL;
+	unsigned long flags;
+
+	struct msm_sync *sync = (struct msm_sync *)syncdata;
+	if (!sync) {
+		pr_err("%s: no context in dsp callback.\n", __func__);
+		return;
+	}
+
+	qcmd = ((struct msm_queue_cmd *)vdata) - 1;
+	qcmd->type = qtype;
+	qcmd->command = vdata;
+	qcmd->ts = *((struct timespec *)ts);
+
+	if (qtype != MSM_CAM_Q_VPE_MSG) {
+		pr_err("%s: Invalid qcmd type = %d.\n", __func__, qcmd->type);
+		free_qcmd(qcmd);
+		return;
+	}
+
+	CDBG("%s: vdata->type %d\n", __func__, vdata->type);
+	switch (vdata->type) {
+	case VPE_MSG_OUTPUT_V:
+		if (sync->liveshot_enabled) {
+			CDBG("%s: msm_enqueue liveshot %d\n", __func__,
+				sync->liveshot_enabled);
+			vdata->phy.output_id |= OUTPUT_TYPE_L;
+			sync->liveshot_enabled = false;
+		}
+		vdata->phy.p2_phy = vdata->phy.p0_phy;
+		if (sync->frame_q.len <= 100 && sync->event_q.len <= 100) {
+			CDBG("%s: enqueue to frame_q from VPE\n", __func__);
+			msm_enqueue(&sync->frame_q, &qcmd->list_frame);
+		} else {
+			pr_err("%s, Error Queue limit exceeded f_q = %d, "
+				"e_q = %d\n", __func__, sync->frame_q.len,
+				sync->event_q.len);
+			free_qcmd(qcmd);
+		}
+		return;
+
+	case VPE_MSG_OUTPUT_ST_L:
+		CDBG("%s: enqueue left frame done msg to event_q from VPE\n",
+			__func__);
+		msm_enqueue(&sync->event_q, &qcmd->list_config);
+		return;
+
+	case VPE_MSG_OUTPUT_ST_R:
+		spin_lock_irqsave(&pp_stereocam_spinlock, flags);
+		CDBG("%s: received VPE_MSG_OUTPUT_ST_R state %d\n", __func__,
+			sync->stereo_state);
+
+		if (STEREO_SNAP_BUFFER1_PROCESSING == sync->stereo_state) {
+			msm_enqueue(&sync->pict_q, &qcmd->list_pict);
+			qcmd = sync->pp_stereocam2;
+			sync->pp_stereocam = sync->pp_stereocam2;
+			sync->pp_stereocam2 = NULL;
+			msm_enqueue(&sync->event_q, &qcmd->list_config);
+			sync->stereo_state =
+				STEREO_SNAP_BUFFER2_PROCESSING;
+		} else if (STEREO_SNAP_BUFFER2_PROCESSING ==
+				sync->stereo_state) {
+			sync->stereo_state = STEREO_SNAP_IDLE;
+			/* Send snapshot DONE */
+			msm_enqueue(&sync->pict_q, &qcmd->list_pict);
+			qcmd = sync->pp_stereosnap;
+			sync->pp_stereosnap = NULL;
+			CDBG("%s: send SNAPSHOT_DONE message\n", __func__);
+			msm_enqueue(&sync->event_q, &qcmd->list_config);
+		} else {
+			if (atomic_read(&qcmd->on_heap))
+				atomic_add(1, &qcmd->on_heap);
+			msm_enqueue(&sync->event_q, &qcmd->list_config);
+			if (sync->stereo_state == STEREO_VIDEO_ACTIVE) {
+				CDBG("%s: st frame to frame_q from VPE\n",
+					__func__);
+				msm_enqueue(&sync->frame_q, &qcmd->list_frame);
+			}
+		}
+		spin_unlock_irqrestore(&pp_stereocam_spinlock, flags);
+		return;
+
+	default:
+		pr_err("%s: qtype %d not handled\n", __func__, vdata->type);
+	}
+	pr_err("%s: Should not come here. Error.\n", __func__);
+}
+
+static struct msm_vpe_callback msm_vpe_s = {
+	.vpe_resp = msm_vpe_sync,
+	.vpe_alloc = msm_vpe_sync_alloc,
+	.vpe_free = msm_vpe_sync_free,
+};
+
+static struct msm_vfe_callback msm_vfe_s = {
+	.vfe_resp = msm_vfe_sync,
+	.vfe_alloc = msm_vfe_sync_alloc,
+	.vfe_free = msm_vfe_sync_free,
+};
+
+static int __msm_open(struct msm_cam_device *pmsm, const char *const apps_id,
+			int is_controlnode)
+{
+	int rc = 0;
+	struct msm_sync *sync = pmsm->sync;
+
+	mutex_lock(&sync->lock);
+	if (sync->apps_id && strcmp(sync->apps_id, apps_id)
+				&& (!strcmp(MSM_APPS_ID_V4L2, apps_id))) {
+		pr_err("%s(%s): sensor %s is already opened for %s\n",
+			__func__,
+			apps_id,
+			sync->sdata->sensor_name,
+			sync->apps_id);
+		rc = -EBUSY;
+		goto msm_open_done;
+	}
+
+	sync->apps_id = apps_id;
+
+	if (!sync->core_powered_on && !is_controlnode) {
+		wake_lock(&sync->wake_lock);
+
+		msm_camvfe_fn_init(&sync->vfefn, sync);
+		if (sync->vfefn.vfe_init) {
+			sync->pp_frame_avail = 0;
+			sync->get_pic_abort = 0;
+			rc = msm_camio_sensor_clk_on(sync->pdev);
+			if (rc < 0) {
+				pr_err("%s: setting sensor clocks failed: %d\n",
+					__func__, rc);
+				goto msm_open_err;
+			}
+			rc = sync->sctrl.s_init(sync->sdata);
+			if (rc < 0) {
+				pr_err("%s: sensor init failed: %d\n",
+					__func__, rc);
+				msm_camio_sensor_clk_off(sync->pdev);
+				goto msm_open_err;
+			}
+			rc = sync->vfefn.vfe_init(&msm_vfe_s,
+				sync->pdev);
+			if (rc < 0) {
+				pr_err("%s: vfe_init failed at %d\n",
+					__func__, rc);
+				sync->sctrl.s_release();
+				msm_camio_sensor_clk_off(sync->pdev);
+				goto msm_open_err;
+			}
+		} else {
+			pr_err("%s: no sensor init func\n", __func__);
+			rc = -ENODEV;
+			goto msm_open_err;
+		}
+		msm_camvpe_fn_init(&sync->vpefn, sync);
+
+		spin_lock_init(&sync->abort_pict_lock);
+		spin_lock_init(&pp_prev_spinlock);
+		spin_lock_init(&pp_stereocam_spinlock);
+		spin_lock_init(&st_frame_spinlock);
+		if (rc >= 0) {
+			msm_region_init(sync);
+			if (sync->vpefn.vpe_reg)
+				sync->vpefn.vpe_reg(&msm_vpe_s);
+			sync->unblock_poll_frame = 0;
+			sync->unblock_poll_pic_frame = 0;
+		}
+		sync->core_powered_on = 1;
+	}
+	sync->opencnt++;
+	client_for_ion = msm_ion_client_create(-1, "camera");
+
+msm_open_done:
+	mutex_unlock(&sync->lock);
+	return rc;
+
+msm_open_err:
+	atomic_set(&pmsm->opened, 0);
+	mutex_unlock(&sync->lock);
+	return rc;
+}
+
+static int msm_open_common(struct inode *inode, struct file *filep,
+			int once, int is_controlnode)
+{
+	int rc;
+	struct msm_cam_device *pmsm =
+		container_of(inode->i_cdev, struct msm_cam_device, cdev);
+
+	CDBG("%s: open %s\n", __func__, filep->f_path.dentry->d_name.name);
+
+	if (atomic_cmpxchg(&pmsm->opened, 0, 1) && once) {
+		pr_err("%s: %s is already opened.\n",
+			__func__,
+			filep->f_path.dentry->d_name.name);
+		return -EBUSY;
+	}
+
+	rc = nonseekable_open(inode, filep);
+	if (rc < 0) {
+		pr_err("%s: nonseekable_open error %d\n", __func__, rc);
+		return rc;
+	}
+
+	rc = __msm_open(pmsm, MSM_APPS_ID_PROP, is_controlnode);
+	if (rc < 0)
+		return rc;
+	filep->private_data = pmsm;
+	CDBG("%s: rc %d\n", __func__, rc);
+	return rc;
+}
+
+static int msm_open_frame(struct inode *inode, struct file *filep)
+{
+	struct msm_cam_device *pmsm =
+		container_of(inode->i_cdev, struct msm_cam_device, cdev);
+	msm_queue_drain(&pmsm->sync->frame_q, list_frame);
+	return msm_open_common(inode, filep, 1, 0);
+}
+
+static int msm_open(struct inode *inode, struct file *filep)
+{
+	return msm_open_common(inode, filep, 1, 0);
+}
+
+static int msm_open_control(struct inode *inode, struct file *filep)
+{
+	int rc;
+
+	struct msm_control_device *ctrl_pmsm =
+		kmalloc(sizeof(struct msm_control_device), GFP_KERNEL);
+	if (!ctrl_pmsm)
+		return -ENOMEM;
+
+	rc = msm_open_common(inode, filep, 0, 1);
+	if (rc < 0) {
+		kfree(ctrl_pmsm);
+		return rc;
+	}
+	ctrl_pmsm->pmsm = filep->private_data;
+	filep->private_data = ctrl_pmsm;
+
+	msm_queue_init(&ctrl_pmsm->ctrl_q, "control");
+
+	if (!g_v4l2_opencnt)
+		g_v4l2_control_device = ctrl_pmsm;
+	g_v4l2_opencnt++;
+	CDBG("%s: rc %d\n", __func__, rc);
+	return rc;
+}
+
+static const struct file_operations msm_fops_config = {
+	.owner = THIS_MODULE,
+	.open = msm_open,
+	.unlocked_ioctl = msm_ioctl_config,
+	.release = msm_release_config,
+	.poll = msm_poll_config,
+};
+
+static const struct file_operations msm_fops_control = {
+	.owner = THIS_MODULE,
+	.open = msm_open_control,
+	.unlocked_ioctl = msm_ioctl_control,
+	.release = msm_release_control,
+};
+
+static const struct file_operations msm_fops_frame = {
+	.owner = THIS_MODULE,
+	.open = msm_open_frame,
+	.unlocked_ioctl = msm_ioctl_frame,
+	.release = msm_release_frame,
+	.poll = msm_poll_frame,
+};
+
+static const struct file_operations msm_fops_pic = {
+	.owner = THIS_MODULE,
+	.open = msm_open,
+	.unlocked_ioctl = msm_ioctl_pic,
+	.release = msm_release_pic,
+	.poll = msm_poll_pic,
+};
+
+static int msm_setup_cdev(struct msm_cam_device *msm,
+			int node,
+			dev_t devno,
+			const char *suffix,
+			const struct file_operations *fops)
+{
+	int rc = -ENODEV;
+
+	struct device *device =
+		device_create(msm_class, NULL,
+			devno, NULL,
+			"%s%d", suffix, node);
+
+	if (IS_ERR(device)) {
+		rc = PTR_ERR(device);
+		pr_err("%s: error creating device: %d\n", __func__, rc);
+		return rc;
+	}
+
+	cdev_init(&msm->cdev, fops);
+	msm->cdev.owner = THIS_MODULE;
+
+	rc = cdev_add(&msm->cdev, devno, 1);
+	if (rc < 0) {
+		pr_err("%s: error adding cdev: %d\n", __func__, rc);
+		device_destroy(msm_class, devno);
+		return rc;
+	}
+
+	return rc;
+}
+
+static int msm_tear_down_cdev(struct msm_cam_device *msm, dev_t devno)
+{
+	cdev_del(&msm->cdev);
+	device_destroy(msm_class, devno);
+	return 0;
+}
+
+static int msm_sync_init(struct msm_sync *sync,
+		struct platform_device *pdev,
+		int (*sensor_probe)(const struct msm_camera_sensor_info *,
+				struct msm_sensor_ctrl *))
+{
+	int rc = 0;
+	struct msm_sensor_ctrl sctrl;
+	sync->sdata = pdev->dev.platform_data;
+
+	msm_queue_init(&sync->event_q, "event");
+	msm_queue_init(&sync->frame_q, "frame");
+	msm_queue_init(&sync->pict_q, "pict");
+	msm_queue_init(&sync->vpe_q, "vpe");
+
+	wake_lock_init(&sync->wake_lock, WAKE_LOCK_IDLE, "msm_camera");
+
+	rc = msm_camio_probe_on(pdev);
+	if (rc < 0) {
+		wake_lock_destroy(&sync->wake_lock);
+		return rc;
+	}
+	rc = sensor_probe(sync->sdata, &sctrl);
+	if (rc >= 0) {
+		sync->pdev = pdev;
+		sync->sctrl = sctrl;
+	}
+	msm_camio_probe_off(pdev);
+	if (rc < 0) {
+		pr_err("%s: failed to initialize %s\n",
+			__func__,
+			sync->sdata->sensor_name);
+		wake_lock_destroy(&sync->wake_lock);
+		return rc;
+	}
+
+	sync->opencnt = 0;
+	sync->core_powered_on = 0;
+	sync->ignore_qcmd = false;
+	sync->ignore_qcmd_type = -1;
+	mutex_init(&sync->lock);
+	if (sync->sdata->strobe_flash_data) {
+		sync->sdata->strobe_flash_data->state = 0;
+		spin_lock_init(&sync->sdata->strobe_flash_data->spin_lock);
+	}
+	CDBG("%s: initialized %s\n", __func__, sync->sdata->sensor_name);
+	return rc;
+}
+
+static int msm_sync_destroy(struct msm_sync *sync)
+{
+	wake_lock_destroy(&sync->wake_lock);
+	return 0;
+}
+
+static int msm_device_init(struct msm_cam_device *pmsm,
+		struct msm_sync *sync,
+		int node)
+{
+	int dev_num = 4 * node;
+	int rc = msm_setup_cdev(pmsm, node,
+		MKDEV(MAJOR(msm_devno), dev_num),
+		"control", &msm_fops_control);
+	if (rc < 0) {
+		pr_err("%s: error creating control node: %d\n", __func__, rc);
+		return rc;
+	}
+
+	rc = msm_setup_cdev(pmsm + 1, node,
+		MKDEV(MAJOR(msm_devno), dev_num + 1),
+		"config", &msm_fops_config);
+	if (rc < 0) {
+		pr_err("%s: error creating config node: %d\n", __func__, rc);
+		msm_tear_down_cdev(pmsm, MKDEV(MAJOR(msm_devno),
+				dev_num));
+		return rc;
+	}
+
+	rc = msm_setup_cdev(pmsm + 2, node,
+		MKDEV(MAJOR(msm_devno), dev_num + 2),
+		"frame", &msm_fops_frame);
+	if (rc < 0) {
+		pr_err("%s: error creating frame node: %d\n", __func__, rc);
+		msm_tear_down_cdev(pmsm,
+			MKDEV(MAJOR(msm_devno), dev_num));
+		msm_tear_down_cdev(pmsm + 1,
+			MKDEV(MAJOR(msm_devno), dev_num + 1));
+		return rc;
+	}
+
+	rc = msm_setup_cdev(pmsm + 3, node,
+		MKDEV(MAJOR(msm_devno), dev_num + 3),
+		"pic", &msm_fops_pic);
+	if (rc < 0) {
+		pr_err("%s: error creating pic node: %d\n", __func__, rc);
+		msm_tear_down_cdev(pmsm,
+			MKDEV(MAJOR(msm_devno), dev_num));
+		msm_tear_down_cdev(pmsm + 1,
+			MKDEV(MAJOR(msm_devno), dev_num + 1));
+		msm_tear_down_cdev(pmsm + 2,
+			MKDEV(MAJOR(msm_devno), dev_num + 2));
+		return rc;
+	}
+
+
+	atomic_set(&pmsm[0].opened, 0);
+	atomic_set(&pmsm[1].opened, 0);
+	atomic_set(&pmsm[2].opened, 0);
+	atomic_set(&pmsm[3].opened, 0);
+
+	pmsm[0].sync = sync;
+	pmsm[1].sync = sync;
+	pmsm[2].sync = sync;
+	pmsm[3].sync = sync;
+
+	return rc;
+}
+
+int msm_camera_drv_start(struct platform_device *dev,
+		int (*sensor_probe)(const struct msm_camera_sensor_info *,
+			struct msm_sensor_ctrl *))
+{
+	struct msm_cam_device *pmsm = NULL;
+	struct msm_sync *sync;
+	int rc = -ENODEV;
+
+	if (camera_node >= MSM_MAX_CAMERA_SENSORS) {
+		pr_err("%s: too many camera sensors\n", __func__);
+		return rc;
+	}
+
+	if (!msm_class) {
+		/* There are three device nodes per sensor */
+		rc = alloc_chrdev_region(&msm_devno, 0,
+				4 * MSM_MAX_CAMERA_SENSORS,
+				"msm_camera");
+		if (rc < 0) {
+			pr_err("%s: failed to allocate chrdev: %d\n", __func__,
+				rc);
+			return rc;
+		}
+
+		msm_class = class_create(THIS_MODULE, "msm_camera");
+		if (IS_ERR(msm_class)) {
+			rc = PTR_ERR(msm_class);
+			pr_err("%s: create device class failed: %d\n",
+				__func__, rc);
+			return rc;
+		}
+	}
+
+	pmsm = kzalloc(sizeof(struct msm_cam_device) * 4 +
+			sizeof(struct msm_sync), GFP_ATOMIC);
+	if (!pmsm)
+		return -ENOMEM;
+	sync = (struct msm_sync *)(pmsm + 4);
+
+	rc = msm_sync_init(sync, dev, sensor_probe);
+	if (rc < 0) {
+		kfree(pmsm);
+		return rc;
+	}
+
+	CDBG("%s: setting camera node %d\n", __func__, camera_node);
+	rc = msm_device_init(pmsm, sync, camera_node);
+	if (rc < 0) {
+		msm_sync_destroy(sync);
+		kfree(pmsm);
+		return rc;
+	}
+
+	camera_type[camera_node] = sync->sctrl.s_camera_type;
+	sensor_mount_angle[camera_node] = sync->sctrl.s_mount_angle;
+	camera_node++;
+
+	list_add(&sync->list, &msm_sensors);
+	return rc;
+}
+EXPORT_SYMBOL(msm_camera_drv_start);
diff --git a/drivers/media/video/msm/msm_gesture.c b/drivers/media/video/msm/msm_gesture.c
new file mode 100644
index 0000000..258c73d
--- /dev/null
+++ b/drivers/media/video/msm/msm_gesture.c
@@ -0,0 +1,497 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <media/v4l2-subdev.h>
+#include <media/msm_camera.h>
+#include <media/msm_gestures.h>
+#include <media/v4l2-ctrls.h>
+#include <mach/camera.h>
+#include "msm.h"
+
+#ifdef CONFIG_MSM_CAMERA_DEBUG
+#define D(fmt, args...) pr_debug("msm_gesture: " fmt, ##args)
+#else
+#define D(fmt, args...) do {} while (0)
+#endif
+
+struct msm_gesture_ctrl {
+	int queue_id;
+	atomic_t active;
+	struct v4l2_ctrl_handler ctrl_handler;
+	int num_ctrls;
+	struct v4l2_fh *p_eventHandle;
+	struct v4l2_subdev *sd;
+	struct msm_ges_evt event;
+	int camera_opened;
+};
+
+static struct msm_gesture_ctrl g_gesture_ctrl;
+
+int msm_gesture_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+	struct v4l2_event_subscription *sub)
+{
+	D("%s\n", __func__);
+	if (sub->type == V4L2_EVENT_ALL)
+		sub->type = MSM_GES_APP_NOTIFY_EVENT;
+	return v4l2_event_subscribe(fh, sub, 30);
+}
+
+static int msm_gesture_send_ctrl(struct msm_gesture_ctrl *p_gesture_ctrl,
+	int type, void *value, int length, uint32_t timeout)
+{
+	int rc = 0;
+	struct msm_ctrl_cmd ctrlcmd;
+	D("%s qid %d\n", __func__, p_gesture_ctrl->queue_id);
+	ctrlcmd.type = type;
+	ctrlcmd.timeout_ms = timeout;
+	ctrlcmd.length = length;
+	ctrlcmd.value = value;
+	ctrlcmd.vnode_id = 0;
+	ctrlcmd.queue_idx = p_gesture_ctrl->queue_id;
+	ctrlcmd.config_ident = 0;
+
+	rc = msm_server_send_ctrl(&ctrlcmd, MSM_GES_RESP_V4L2);
+	return rc;
+}
+
+static int msm_gesture_proc_ctrl_cmd(struct msm_gesture_ctrl *p_gesture_ctrl,
+	struct v4l2_control *ctrl)
+{
+	int rc = 0;
+	struct msm_ctrl_cmd *tmp_cmd = NULL;
+	uint8_t *ctrl_data = NULL;
+	void __user *uptr_cmd;
+	void __user *uptr_value;
+	uint32_t cmd_len = sizeof(struct msm_ctrl_cmd);
+	uint32_t value_len;
+
+	tmp_cmd = (struct msm_ctrl_cmd *)ctrl->value;
+	uptr_cmd = (void __user *)ctrl->value;
+	uptr_value = (void __user *)tmp_cmd->value;
+	value_len = tmp_cmd->length;
+
+	D("%s: cmd type = %d, up1=0x%x, ulen1=%d, up2=0x%x, ulen2=%d\n",
+		__func__, tmp_cmd->type, (uint32_t)uptr_cmd, cmd_len,
+		(uint32_t)uptr_value, tmp_cmd->length);
+
+	ctrl_data = kzalloc(value_len + cmd_len, GFP_KERNEL);
+	if (ctrl_data == 0) {
+		pr_err("%s could not allocate memory\n", __func__);
+		rc = -ENOMEM;
+		goto end;
+	}
+	tmp_cmd = (struct msm_ctrl_cmd *)ctrl_data;
+	if (copy_from_user((void *)ctrl_data, uptr_cmd,
+			cmd_len)) {
+		pr_err("%s: copy_from_user failed.\n", __func__);
+		rc = -EINVAL;
+		goto end;
+	}
+	tmp_cmd->value = (void *)(ctrl_data + cmd_len);
+	if (uptr_value && tmp_cmd->length > 0) {
+		if (copy_from_user((void *)tmp_cmd->value, uptr_value,
+			value_len)) {
+			pr_err("%s: copy_from_user failed, size=%d\n",
+				__func__, value_len);
+			rc = -EINVAL;
+			goto end;
+		}
+	} else
+		tmp_cmd->value = NULL;
+
+	/* send command to config thread in usersspace, and get return value */
+	rc = msm_server_send_ctrl((struct msm_ctrl_cmd *)ctrl_data,
+			MSM_GES_RESP_V4L2);
+	D("%s: msm_server_control rc=%d\n", __func__, rc);
+	if (rc == 0) {
+		if (uptr_value && tmp_cmd->length > 0 &&
+			copy_to_user((void __user *)uptr_value,
+				(void *)(ctrl_data + cmd_len),
+				tmp_cmd->length)) {
+			pr_err("%s: copy_to_user failed, size=%d\n",
+				__func__, tmp_cmd->length);
+			rc = -EINVAL;
+			goto end;
+		}
+		tmp_cmd->value = uptr_value;
+		if (copy_to_user((void __user *)uptr_cmd,
+			(void *)tmp_cmd, cmd_len)) {
+			pr_err("%s: copy_to_user failed in cpy, size=%d\n",
+				__func__, cmd_len);
+			rc = -EINVAL;
+			goto end;
+		}
+	}
+end:
+	D("%s: END, type = %d, vaddr = 0x%x, vlen = %d, status = %d, rc = %d\n",
+		__func__, tmp_cmd->type, (uint32_t)tmp_cmd->value,
+		tmp_cmd->length, tmp_cmd->status, rc);
+	kfree(ctrl_data);
+	return rc;
+}
+
+static int msm_gesture_s_ctrl(struct v4l2_subdev *sd,
+	struct v4l2_control *ctrl)
+{
+	int rc = 0;
+	struct msm_gesture_ctrl *p_gesture_ctrl = &g_gesture_ctrl;
+	D("%s ctrl->id %d\n", __func__, ctrl->id);
+	rc = msm_gesture_proc_ctrl_cmd(p_gesture_ctrl, ctrl);
+	if (rc != 0) {
+		pr_err("%s set ctrl failed %d\n", __func__, rc);
+		return -EINVAL;
+	}
+	return rc;
+}
+
+static int msm_gesture_s_ctrl_ops(struct v4l2_ctrl *ctrl)
+{
+	int rc = 0;
+	struct v4l2_control control;
+	struct msm_gesture_ctrl *p_gesture_ctrl = &g_gesture_ctrl;
+	control.id = ctrl->id;
+	control.value = ctrl->val;
+	D("%s ctrl->id 0x%x\n", __func__, ctrl->id);
+	rc = msm_gesture_proc_ctrl_cmd(p_gesture_ctrl, &control);
+	if (rc != 0) {
+		pr_err("%s proc ctrl failed %d\n", __func__, rc);
+		return -EINVAL;
+	}
+	return rc;
+}
+
+static int msm_gesture_s_ctrl_ext(struct v4l2_subdev *sd,
+	struct v4l2_ext_controls *ctrls)
+{
+	int rc = 0;
+	struct v4l2_control control;
+	struct msm_gesture_ctrl *p_gesture_ctrl = &g_gesture_ctrl;
+	if ((ctrls->count < 1) || (NULL == ctrls->controls)) {
+		pr_err("%s invalid ctrl failed\n", __func__);
+		return -EINVAL;
+	}
+	control.id = ctrls->controls->id;
+	control.value = ctrls->controls->value;
+	D("%s ctrl->id %d\n", __func__, control.id);
+	rc = msm_gesture_proc_ctrl_cmd(p_gesture_ctrl, &control);
+	if (rc != 0) {
+		pr_err("%s proc ctrl failed %d\n", __func__, rc);
+		return -EINVAL;
+	}
+	return rc;
+}
+
+static int msm_gesture_handle_event(struct v4l2_subdev *sd,
+	struct msm_gesture_ctrl *p_gesture_ctrl, void* arg)
+{
+	int rc = 0;
+	struct v4l2_event *evt = (struct v4l2_event *)arg;
+	struct msm_ges_evt *p_ges_evt = NULL;
+	D("%s: Received gesture evt 0x%x ", __func__, evt->type);
+	p_gesture_ctrl->event.evt_len = 0;
+	p_gesture_ctrl->event.evt_data = NULL;
+	if (0 != evt->u.data[0]) {
+		p_ges_evt = (struct msm_ges_evt *)evt->u.data;
+		D("%s: event data %p len %d", __func__,
+			p_ges_evt->evt_data,
+			p_ges_evt->evt_len);
+
+		if (p_ges_evt->evt_len > 0) {
+			p_gesture_ctrl->event.evt_data =
+				kzalloc(p_ges_evt->evt_len, GFP_KERNEL);
+
+			if (NULL == p_gesture_ctrl->event.evt_data) {
+				pr_err("%s: cannot allocate event", __func__);
+				rc = -ENOMEM;
+			} else {
+				if (copy_from_user(
+					(void *)p_gesture_ctrl->event.evt_data,
+					(void __user *)p_ges_evt->evt_data,
+					p_ges_evt->evt_len)) {
+					pr_err("%s: copy_from_user failed",
+							__func__);
+					rc = -EFAULT;
+				} else {
+					D("%s: copied the event", __func__);
+					p_gesture_ctrl->event.evt_len =
+						p_ges_evt->evt_len;
+				}
+			}
+		}
+	}
+
+	if (rc == 0) {
+		ktime_get_ts(&evt->timestamp);
+		v4l2_event_queue(sd->devnode, evt);
+	}
+	D("%s: exit rc %d ", __func__, rc);
+	return rc;
+}
+
+static int msm_gesture_get_evt_payload(struct v4l2_subdev *sd,
+	struct msm_gesture_ctrl *p_gesture_ctrl, void* arg)
+{
+	int rc = 0;
+	struct msm_ges_evt *p_ges_evt = (struct msm_ges_evt *)arg;
+	D("%s: enter ", __func__);
+	if (NULL != p_gesture_ctrl->event.evt_data) {
+		D("%s: event data %p len %d", __func__,
+			p_gesture_ctrl->event.evt_data,
+			p_gesture_ctrl->event.evt_len);
+
+		if (copy_to_user((void __user *)p_ges_evt->evt_data,
+			p_gesture_ctrl->event.evt_data,
+			p_gesture_ctrl->event.evt_len)) {
+			pr_err("%s: copy_to_user failed.\n", __func__);
+			rc = -EFAULT;
+		} else {
+			D("%s: copied the event", __func__);
+			p_ges_evt->evt_len = p_gesture_ctrl->event.evt_len;
+		}
+	}
+	D("%s: exit rc %d ", __func__, rc);
+	return rc;
+}
+
+static int msm_gesture_handle_cam_event(struct v4l2_subdev *sd,
+	struct msm_gesture_ctrl *p_gesture_ctrl, int cam_evt)
+{
+	int rc = 0;
+	D("%s: cam_evt %d ", __func__, cam_evt);
+
+	if ((cam_evt != MSM_V4L2_GES_CAM_OPEN)
+		&& (cam_evt != MSM_V4L2_GES_CAM_CLOSE)) {
+		pr_err("%s: error invalid event %d ", __func__, cam_evt);
+		return -EINVAL;
+	}
+
+	p_gesture_ctrl->camera_opened =
+		(cam_evt == MSM_V4L2_GES_CAM_OPEN);
+
+	if (atomic_read(&p_gesture_ctrl->active) == 0) {
+		D("%s gesture not active\n", __func__);
+		return 0;
+	}
+
+	rc = msm_gesture_send_ctrl(p_gesture_ctrl, cam_evt, NULL,
+		0, 2000);
+	if (rc != 0) {
+		pr_err("%s gesture ctrl failed %d\n", __func__, rc);
+		rc = -EINVAL;
+	}
+	D("%s exit rc %d\n", __func__, rc);
+	return rc;
+}
+
+long msm_gesture_ioctl(struct v4l2_subdev *sd,
+	 unsigned int cmd, void *arg)
+{
+	int rc = 0;
+	struct msm_gesture_ctrl *p_gesture_ctrl = &g_gesture_ctrl;
+	D("%s\n", __func__);
+	switch (cmd) {
+	case MSM_GES_IOCTL_CTRL_COMMAND: {
+		struct v4l2_control *ctrl = (struct v4l2_control *)arg;
+		D("%s MSM_GES_IOCTL_CTRL_COMMAND arg %p size %d\n", __func__,
+			arg, sizeof(ctrl));
+		rc = msm_gesture_s_ctrl(sd, ctrl);
+		break;
+	}
+	case VIDIOC_MSM_GESTURE_EVT: {
+		rc = msm_gesture_handle_event(sd, p_gesture_ctrl, arg);
+		break;
+	}
+	case VIDIOC_MSM_GESTURE_CAM_EVT: {
+		int cam_evt = *((int *)arg);
+		rc = msm_gesture_handle_cam_event(sd, p_gesture_ctrl, cam_evt);
+		break;
+	}
+	case MSM_GES_GET_EVT_PAYLOAD: {
+		rc = msm_gesture_get_evt_payload(sd, p_gesture_ctrl, arg);
+		break;
+	}
+	default:
+		pr_err("%s: Invalid ioctl %d", __func__, cmd);
+		break;
+	}
+	D("%s exit rc %d\n", __func__, rc);
+	return rc;
+}
+
+static const struct v4l2_ctrl_ops msm_gesture_ctrl_ops = {
+	.s_ctrl = msm_gesture_s_ctrl_ops,
+};
+
+static const struct v4l2_ctrl_config msm_gesture_ctrl_filter = {
+	.ops = &msm_gesture_ctrl_ops,
+	.id = MSM_GESTURE_CID_CTRL_CMD,
+	.name = "Gesture ctrl",
+	.type = V4L2_CTRL_TYPE_INTEGER,
+	.flags = V4L2_CTRL_FLAG_SLIDER,
+	.max = 0x7fffffff,
+	.step = 1,
+	.min = 0x80000000,
+};
+
+static int msm_gesture_init_ctrl(struct v4l2_subdev *sd,
+	struct msm_gesture_ctrl *p_gesture_ctrl)
+{
+	int rc = 0;
+	p_gesture_ctrl->num_ctrls = 1;
+	p_gesture_ctrl->ctrl_handler.error = 0;
+	v4l2_ctrl_handler_init(&p_gesture_ctrl->ctrl_handler,
+		p_gesture_ctrl->num_ctrls);
+	v4l2_ctrl_new_custom(&p_gesture_ctrl->ctrl_handler,
+		&msm_gesture_ctrl_filter, p_gesture_ctrl);
+	if (p_gesture_ctrl->ctrl_handler.error) {
+		int err = p_gesture_ctrl->ctrl_handler.error;
+		D("%s: error adding control %d", __func__, err);
+		p_gesture_ctrl->ctrl_handler.error = 0;
+	}
+	sd->ctrl_handler = &p_gesture_ctrl->ctrl_handler;
+	return rc;
+}
+
+static int msm_gesture_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	int rc = 0, rc_err = 0;
+	struct msm_gesture_ctrl *p_gesture_ctrl = &g_gesture_ctrl;
+	D("%s\n", __func__);
+	if (atomic_read(&p_gesture_ctrl->active) != 0) {
+		pr_err("%s already opened\n", __func__);
+		return -EINVAL;
+	}
+	memset(&p_gesture_ctrl->event, 0x0, sizeof(struct msm_ges_evt));
+	rc = msm_server_open_client(&p_gesture_ctrl->queue_id);
+	if (rc != 0) {
+		pr_err("%s open failed %d\n", __func__, rc);
+		rc = -EINVAL;
+		goto err;
+	}
+
+	rc = msm_gesture_init_ctrl(sd, p_gesture_ctrl);
+	if (rc != 0) {
+		pr_err("%s init ctrl failed %d\n", __func__, rc);
+		rc = -EINVAL;
+		goto err;
+	}
+
+	rc = msm_gesture_send_ctrl(p_gesture_ctrl, MSM_V4L2_GES_OPEN, NULL,
+		0, 10000);
+	if (rc != 0) {
+		pr_err("%s gesture ctrl failed %d\n", __func__, rc);
+		rc = -EINVAL;
+		goto err;
+	}
+
+	atomic_inc(&p_gesture_ctrl->active);
+
+	return rc;
+
+err:
+	rc_err = msm_server_close_client(p_gesture_ctrl->queue_id);
+	if (rc_err != 0)
+		pr_err("%s failed %d\n", __func__, rc);
+	return rc;
+}
+
+static int msm_gesture_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	int rc = 0;
+	struct msm_gesture_ctrl *p_gesture_ctrl = &g_gesture_ctrl;
+	D("%s\n", __func__);
+	if (atomic_read(&p_gesture_ctrl->active) == 0) {
+		pr_err("%s already closed\n", __func__);
+		return -EINVAL;
+	}
+
+	rc = msm_gesture_send_ctrl(p_gesture_ctrl, MSM_V4L2_GES_CLOSE, NULL,
+		0, 10000);
+	if (rc != 0)
+		pr_err("%s gesture ctrl failed %d\n", __func__, rc);
+
+	rc = msm_server_close_client(p_gesture_ctrl->queue_id);
+	if (rc != 0)
+		pr_err("%s failed %d\n", __func__, rc);
+
+	v4l2_ctrl_handler_free(&p_gesture_ctrl->ctrl_handler);
+	kfree(p_gesture_ctrl->event.evt_data);
+
+	atomic_dec(&p_gesture_ctrl->active);
+	g_gesture_ctrl.queue_id = -1;
+	return 0;
+}
+
+static struct v4l2_subdev_core_ops msm_gesture_core_ops = {
+	.s_ctrl = msm_gesture_s_ctrl,
+	.s_ext_ctrls = msm_gesture_s_ctrl_ext,
+	.ioctl = msm_gesture_ioctl,
+	.subscribe_event = msm_gesture_subscribe_event,
+};
+
+static struct v4l2_subdev_video_ops msm_gesture_video_ops;
+
+static struct v4l2_subdev_ops msm_gesture_subdev_ops = {
+	.core = &msm_gesture_core_ops,
+	.video  = &msm_gesture_video_ops,
+};
+
+static const struct v4l2_subdev_internal_ops msm_gesture_internal_ops = {
+	.open = msm_gesture_open,
+	.close = msm_gesture_close,
+};
+
+static int msm_gesture_node_register(void)
+{
+	struct msm_gesture_ctrl *p_gesture_ctrl = &g_gesture_ctrl;
+	struct v4l2_subdev *gesture_subdev =
+		kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+	D("%s\n", __func__);
+	if (!gesture_subdev) {
+		pr_err("%s: no enough memory\n", __func__);
+		return -ENOMEM;
+	};
+
+	v4l2_subdev_init(gesture_subdev, &msm_gesture_subdev_ops);
+	gesture_subdev->internal_ops = &msm_gesture_internal_ops;
+	gesture_subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	snprintf(gesture_subdev->name,
+			 sizeof(gesture_subdev->name), "gesture");
+
+	media_entity_init(&gesture_subdev->entity, 0, NULL, 0);
+	gesture_subdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
+	gesture_subdev->entity.group_id = GESTURE_DEV;
+	gesture_subdev->entity.name = gesture_subdev->name;
+
+	/* events */
+	gesture_subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS;
+
+	msm_cam_register_subdev_node(gesture_subdev, GESTURE_DEV, 0);
+
+	gesture_subdev->entity.revision = gesture_subdev->devnode->num;
+
+	atomic_set(&p_gesture_ctrl->active, 0);
+	p_gesture_ctrl->queue_id = -1;
+	p_gesture_ctrl->event.evt_data = NULL;
+	p_gesture_ctrl->event.evt_len = 0;
+	return 0;
+}
+
+static int __init msm_gesture_init_module(void)
+{
+	return msm_gesture_node_register();
+}
+
+module_init(msm_gesture_init_module);
+MODULE_DESCRIPTION("MSM Gesture driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/msm_isp.c b/drivers/media/video/msm/msm_isp.c
new file mode 100644
index 0000000..148f8b5
--- /dev/null
+++ b/drivers/media/video/msm/msm_isp.c
@@ -0,0 +1,790 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/export.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/ioctl.h>
+#include <linux/spinlock.h>
+#include <linux/videodev2.h>
+#include <linux/proc_fs.h>
+#include <linux/vmalloc.h>
+#include <linux/android_pmem.h>
+
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/msm_isp.h>
+#include <media/msm_gemini.h>
+
+#include "msm.h"
+
+#ifdef CONFIG_MSM_CAMERA_DEBUG
+#define D(fmt, args...) pr_debug("msm_isp: " fmt, ##args)
+#else
+#define D(fmt, args...) do {} while (0)
+#endif
+
+#define MSM_FRAME_AXI_MAX_BUF 32
+
+/*
+ * This function executes in interrupt context.
+ */
+
+void *msm_isp_sync_alloc(int size,
+	  gfp_t gfp)
+{
+	struct msm_queue_cmd *qcmd =
+		kmalloc(sizeof(struct msm_queue_cmd) + size, gfp);
+
+	if (qcmd) {
+		atomic_set(&qcmd->on_heap, 1);
+		return qcmd + 1;
+	}
+	return NULL;
+}
+
+void msm_isp_sync_free(void *ptr)
+{
+	if (ptr) {
+		struct msm_queue_cmd *qcmd =
+			(struct msm_queue_cmd *)ptr;
+		qcmd--;
+		if (atomic_read(&qcmd->on_heap))
+			kfree(qcmd);
+	}
+}
+
+static int msm_isp_notify_VFE_BUF_FREE_EVT(struct v4l2_subdev *sd, void *arg)
+{
+	struct msm_vfe_cfg_cmd cfgcmd;
+	struct msm_camvfe_params vfe_params;
+	int rc;
+
+	cfgcmd.cmd_type = CMD_VFE_BUFFER_RELEASE;
+	cfgcmd.value = NULL;
+	vfe_params.vfe_cfg = &cfgcmd;
+	vfe_params.data = NULL;
+	rc = v4l2_subdev_call(sd, core, ioctl, 0, &vfe_params);
+	return 0;
+}
+
+int msm_isp_vfe_msg_to_img_mode(struct msm_cam_media_controller *pmctl,
+				int vfe_msg)
+{
+	int image_mode;
+	if (vfe_msg == VFE_MSG_OUTPUT_PRIMARY) {
+		switch (pmctl->vfe_output_mode) {
+		case VFE_OUTPUTS_MAIN_AND_PREVIEW:
+		case VFE_OUTPUTS_MAIN_AND_VIDEO:
+		case VFE_OUTPUTS_MAIN_AND_THUMB:
+		case VFE_OUTPUTS_RAW:
+		case VFE_OUTPUTS_JPEG_AND_THUMB:
+			image_mode = MSM_V4L2_EXT_CAPTURE_MODE_MAIN;
+			break;
+		case VFE_OUTPUTS_THUMB_AND_MAIN:
+			image_mode = MSM_V4L2_EXT_CAPTURE_MODE_THUMBNAIL;
+			break;
+		case VFE_OUTPUTS_VIDEO:
+		case VFE_OUTPUTS_VIDEO_AND_PREVIEW:
+			image_mode = MSM_V4L2_EXT_CAPTURE_MODE_VIDEO;
+			break;
+		case VFE_OUTPUTS_PREVIEW:
+		case VFE_OUTPUTS_PREVIEW_AND_VIDEO:
+			image_mode = MSM_V4L2_EXT_CAPTURE_MODE_PREVIEW;
+			break;
+		default:
+			image_mode = -1;
+			break;
+		}
+	} else if (vfe_msg == VFE_MSG_OUTPUT_SECONDARY) {
+		switch (pmctl->vfe_output_mode) {
+		case VFE_OUTPUTS_MAIN_AND_PREVIEW:
+		case VFE_OUTPUTS_VIDEO_AND_PREVIEW:
+			image_mode = MSM_V4L2_EXT_CAPTURE_MODE_PREVIEW;
+			break;
+		case VFE_OUTPUTS_MAIN_AND_VIDEO:
+		case VFE_OUTPUTS_PREVIEW_AND_VIDEO:
+			image_mode = MSM_V4L2_EXT_CAPTURE_MODE_VIDEO;
+			break;
+		case VFE_OUTPUTS_MAIN_AND_THUMB:
+			image_mode = MSM_V4L2_EXT_CAPTURE_MODE_THUMBNAIL;
+			break;
+		case VFE_OUTPUTS_THUMB_AND_MAIN:
+			image_mode = MSM_V4L2_EXT_CAPTURE_MODE_MAIN;
+			break;
+		case VFE_OUTPUTS_JPEG_AND_THUMB:
+			image_mode = MSM_V4L2_EXT_CAPTURE_MODE_THUMBNAIL;
+			break;
+		case VFE_OUTPUTS_PREVIEW:
+		case VFE_OUTPUTS_VIDEO:
+			image_mode = MSM_V4L2_EXT_CAPTURE_MODE_PREVIEW;
+			break;
+		default:
+			image_mode = -1;
+			break;
+		}
+	} else
+		image_mode = -1;
+
+	D("%s Selected image mode %d vfe output mode %d, vfe msg %d\n",
+	  __func__, image_mode, pmctl->vfe_output_mode, vfe_msg);
+	return image_mode;
+}
+
+static int msm_isp_notify_VFE_BUF_EVT(struct v4l2_subdev *sd, void *arg)
+{
+	int rc = -EINVAL, image_mode;
+	struct msm_vfe_resp *vdata = (struct msm_vfe_resp *)arg;
+	struct msm_free_buf free_buf, temp_free_buf;
+	struct msm_camvfe_params vfe_params;
+	struct msm_vfe_cfg_cmd cfgcmd;
+	struct msm_cam_media_controller *pmctl =
+		(struct msm_cam_media_controller *)v4l2_get_subdev_hostdata(sd);
+	struct msm_cam_v4l2_device *pcam = pmctl->pcam_ptr;
+
+	int vfe_id = vdata->evt_msg.msg_id;
+	if (!pcam) {
+		pr_debug("%s pcam is null. return\n", __func__);
+		msm_isp_sync_free(vdata);
+		return rc;
+	}
+	/* Convert the vfe msg to the image mode */
+	image_mode = msm_isp_vfe_msg_to_img_mode(pmctl, vfe_id);
+	BUG_ON(image_mode < 0);
+	switch (vdata->type) {
+	case VFE_MSG_V32_START:
+	case VFE_MSG_V32_START_RECORDING:
+	case VFE_MSG_V2X_PREVIEW:
+		D("%s Got V32_START_*: Getting ping addr id = %d",
+						__func__, vfe_id);
+		msm_mctl_reserve_free_buf(pmctl, NULL,
+					image_mode, &free_buf);
+		cfgcmd.cmd_type = CMD_CONFIG_PING_ADDR;
+		cfgcmd.value = &vfe_id;
+		vfe_params.vfe_cfg = &cfgcmd;
+		vfe_params.data = (void *)&free_buf;
+		rc = v4l2_subdev_call(sd, core, ioctl, 0, &vfe_params);
+		msm_mctl_reserve_free_buf(pmctl, NULL,
+					image_mode, &free_buf);
+		cfgcmd.cmd_type = CMD_CONFIG_PONG_ADDR;
+		cfgcmd.value = &vfe_id;
+		vfe_params.vfe_cfg = &cfgcmd;
+		vfe_params.data = (void *)&free_buf;
+		rc = v4l2_subdev_call(sd, core, ioctl, 0, &vfe_params);
+		break;
+	case VFE_MSG_V32_CAPTURE:
+	case VFE_MSG_V2X_CAPTURE:
+		pr_debug("%s Got V32_CAPTURE: getting buffer for id = %d",
+						__func__, vfe_id);
+		msm_mctl_reserve_free_buf(pmctl, NULL,
+					image_mode, &free_buf);
+		cfgcmd.cmd_type = CMD_CONFIG_PING_ADDR;
+		cfgcmd.value = &vfe_id;
+		vfe_params.vfe_cfg = &cfgcmd;
+		vfe_params.data = (void *)&free_buf;
+		rc = v4l2_subdev_call(sd, core, ioctl, 0, &vfe_params);
+		temp_free_buf = free_buf;
+		if (msm_mctl_reserve_free_buf(pmctl, NULL,
+					image_mode, &free_buf)) {
+			/* Write the same buffer into PONG */
+			free_buf = temp_free_buf;
+		}
+		cfgcmd.cmd_type = CMD_CONFIG_PONG_ADDR;
+		cfgcmd.value = &vfe_id;
+		vfe_params.vfe_cfg = &cfgcmd;
+		vfe_params.data = (void *)&free_buf;
+		rc = v4l2_subdev_call(sd, core, ioctl, 0, &vfe_params);
+		break;
+	case VFE_MSG_V32_JPEG_CAPTURE:
+		D("%s:VFE_MSG_V32_JPEG_CAPTURE vdata->type %d\n", __func__,
+			vdata->type);
+		free_buf.num_planes = 1;
+		free_buf.ch_paddr[0] = IMEM_Y_PING_OFFSET;
+		free_buf.ch_paddr[1] = IMEM_CBCR_PING_OFFSET;
+		cfgcmd.cmd_type = CMD_CONFIG_PING_ADDR;
+		cfgcmd.value = &vfe_id;
+		vfe_params.vfe_cfg = &cfgcmd;
+		vfe_params.data = (void *)&free_buf;
+		D("%s:VFE_MSG_V32_JPEG_CAPTURE y_ping=%x cbcr_ping=%x\n",
+			__func__, free_buf.ch_paddr[0], free_buf.ch_paddr[1]);
+		rc = v4l2_subdev_call(sd, core, ioctl, 0, &vfe_params);
+		/* Write the same buffer into PONG */
+		free_buf.ch_paddr[0] = IMEM_Y_PONG_OFFSET;
+		free_buf.ch_paddr[1] = IMEM_CBCR_PONG_OFFSET;
+		cfgcmd.cmd_type = CMD_CONFIG_PONG_ADDR;
+		cfgcmd.value = &vfe_id;
+		vfe_params.vfe_cfg = &cfgcmd;
+		vfe_params.data = (void *)&free_buf;
+		D("%s:VFE_MSG_V32_JPEG_CAPTURE y_pong=%x cbcr_pong=%x\n",
+			__func__, free_buf.ch_paddr[0], free_buf.ch_paddr[1]);
+		rc = v4l2_subdev_call(sd, core, ioctl, 0, &vfe_params);
+		break;
+	case VFE_MSG_OUTPUT_IRQ:
+		D("%s Got OUTPUT_IRQ: Getting free buf id = %d",
+						__func__, vfe_id);
+		msm_mctl_reserve_free_buf(pmctl, NULL,
+					image_mode, &free_buf);
+		cfgcmd.cmd_type = CMD_CONFIG_FREE_BUF_ADDR;
+		cfgcmd.value = &vfe_id;
+		vfe_params.vfe_cfg = &cfgcmd;
+		vfe_params.data = (void *)&free_buf;
+		rc = v4l2_subdev_call(sd, core, ioctl, 0, &vfe_params);
+		break;
+	default:
+		pr_err("%s: Invalid vdata type: %d\n", __func__, vdata->type);
+		break;
+	}
+	return rc;
+}
+
+/*
+ * This function executes in interrupt context.
+ */
+static int msm_isp_notify_vfe(struct v4l2_subdev *sd,
+	unsigned int notification,  void *arg)
+{
+	int rc = 0;
+	struct v4l2_event v4l2_evt;
+	struct msm_isp_event_ctrl *isp_event;
+	struct msm_cam_media_controller *pmctl =
+		(struct msm_cam_media_controller *)v4l2_get_subdev_hostdata(sd);
+	struct msm_free_buf buf;
+
+	if (!pmctl) {
+		pr_err("%s: no context in dsp callback.\n", __func__);
+		rc = -EINVAL;
+		return rc;
+	}
+
+	if (notification == NOTIFY_VFE_BUF_EVT)
+		return msm_isp_notify_VFE_BUF_EVT(sd, arg);
+
+	if (notification == NOTIFY_VFE_BUF_FREE_EVT)
+		return msm_isp_notify_VFE_BUF_FREE_EVT(sd, arg);
+
+	isp_event = kzalloc(sizeof(struct msm_isp_event_ctrl), GFP_ATOMIC);
+	if (!isp_event) {
+		pr_err("%s Insufficient memory. return", __func__);
+		return -ENOMEM;
+	}
+
+	v4l2_evt.type = V4L2_EVENT_PRIVATE_START +
+					MSM_CAM_RESP_STAT_EVT_MSG;
+	v4l2_evt.id = 0;
+
+	*((uint32_t *)v4l2_evt.u.data) = (uint32_t)isp_event;
+
+	isp_event->resptype = MSM_CAM_RESP_STAT_EVT_MSG;
+	isp_event->isp_data.isp_msg.type = MSM_CAMERA_MSG;
+	isp_event->isp_data.isp_msg.len = 0;
+
+	switch (notification) {
+	case NOTIFY_ISP_MSG_EVT: {
+		struct isp_msg_event *isp_msg = (struct isp_msg_event *)arg;
+
+		isp_event->isp_data.isp_msg.msg_id = isp_msg->msg_id;
+		isp_event->isp_data.isp_msg.frame_id = isp_msg->sof_count;
+		getnstimeofday(&(isp_event->isp_data.isp_msg.timestamp));
+		break;
+	}
+	case NOTIFY_VFE_MSG_OUT: {
+		uint8_t msgid;
+		struct isp_msg_output *isp_output =
+				(struct isp_msg_output *)arg;
+		switch (isp_output->output_id) {
+		case MSG_ID_OUTPUT_P:
+			msgid = VFE_MSG_OUTPUT_P;
+			break;
+		case MSG_ID_OUTPUT_V:
+			msgid = VFE_MSG_OUTPUT_V;
+			break;
+		case MSG_ID_OUTPUT_T:
+			msgid = VFE_MSG_OUTPUT_T;
+			break;
+		case MSG_ID_OUTPUT_S:
+			msgid = VFE_MSG_OUTPUT_S;
+			break;
+		case MSG_ID_OUTPUT_PRIMARY:
+			msgid = VFE_MSG_OUTPUT_PRIMARY;
+			break;
+		case MSG_ID_OUTPUT_SECONDARY:
+			msgid = VFE_MSG_OUTPUT_SECONDARY;
+			break;
+		default:
+			pr_err("%s: Invalid VFE output id: %d\n",
+				__func__, isp_output->output_id);
+			rc = -EINVAL;
+			break;
+		}
+
+		if (!rc) {
+			isp_event->isp_data.isp_msg.msg_id =
+				isp_output->output_id;
+			isp_event->isp_data.isp_msg.frame_id =
+				isp_output->frameCounter;
+			buf = isp_output->buf;
+			msgid = msm_isp_vfe_msg_to_img_mode(pmctl, msgid);
+			BUG_ON(msgid < 0);
+			msm_mctl_buf_done(pmctl, msgid,
+				&buf, isp_output->frameCounter);
+		}
+		}
+		break;
+	case NOTIFY_VFE_MSG_COMP_STATS: {
+		struct msm_stats_buf *stats = (struct msm_stats_buf *)arg;
+		struct msm_stats_buf *stats_buf = NULL;
+
+		isp_event->isp_data.isp_msg.msg_id = MSG_ID_STATS_COMPOSITE;
+		stats->aec.buff = msm_pmem_stats_ptov_lookup(pmctl,
+					stats->aec.buff, &(stats->aec.fd));
+		stats->awb.buff = msm_pmem_stats_ptov_lookup(pmctl,
+					stats->awb.buff, &(stats->awb.fd));
+		stats->af.buff = msm_pmem_stats_ptov_lookup(pmctl,
+					stats->af.buff, &(stats->af.fd));
+		stats->ihist.buff = msm_pmem_stats_ptov_lookup(pmctl,
+					stats->ihist.buff, &(stats->ihist.fd));
+		stats->rs.buff = msm_pmem_stats_ptov_lookup(pmctl,
+					stats->rs.buff, &(stats->rs.fd));
+		stats->cs.buff = msm_pmem_stats_ptov_lookup(pmctl,
+					stats->cs.buff, &(stats->cs.fd));
+
+		stats_buf = kmalloc(sizeof(struct msm_stats_buf), GFP_ATOMIC);
+		if (!stats_buf) {
+			pr_err("%s: out of memory.\n", __func__);
+			rc = -ENOMEM;
+		} else {
+			*stats_buf = *stats;
+			isp_event->isp_data.isp_msg.len	=
+				sizeof(struct msm_stats_buf);
+			isp_event->isp_data.isp_msg.data = stats_buf;
+		}
+		}
+		break;
+	case NOTIFY_VFE_MSG_STATS: {
+		struct msm_stats_buf stats;
+		struct isp_msg_stats *isp_stats = (struct isp_msg_stats *)arg;
+
+		isp_event->isp_data.isp_msg.msg_id = isp_stats->id;
+		isp_event->isp_data.isp_msg.frame_id =
+			isp_stats->frameCounter;
+		stats.buffer = msm_pmem_stats_ptov_lookup(pmctl,
+						isp_stats->buffer,
+						&(stats.fd));
+		switch (isp_stats->id) {
+		case MSG_ID_STATS_AEC:
+			stats.aec.buff = stats.buffer;
+			stats.aec.fd = stats.fd;
+			break;
+		case MSG_ID_STATS_AF:
+			stats.af.buff = stats.buffer;
+			stats.af.fd = stats.fd;
+			break;
+		case MSG_ID_STATS_AWB:
+			stats.awb.buff = stats.buffer;
+			stats.awb.fd = stats.fd;
+			break;
+		case MSG_ID_STATS_IHIST:
+			stats.ihist.buff = stats.buffer;
+			stats.ihist.fd = stats.fd;
+			break;
+		case MSG_ID_STATS_RS:
+			stats.rs.buff = stats.buffer;
+			stats.rs.fd = stats.fd;
+			break;
+		case MSG_ID_STATS_CS:
+			stats.cs.buff = stats.buffer;
+			stats.cs.fd = stats.fd;
+			break;
+		case MSG_ID_STATS_AWB_AEC:
+			break;
+		default:
+			pr_err("%s: Invalid msg type", __func__);
+			break;
+		}
+		if (!stats.buffer) {
+			pr_err("%s: msm_pmem_stats_ptov_lookup error\n",
+							__func__);
+			isp_event->isp_data.isp_msg.len = 0;
+			rc = -EFAULT;
+		} else {
+			struct msm_stats_buf *stats_buf =
+				kmalloc(sizeof(struct msm_stats_buf),
+							GFP_ATOMIC);
+			if (!stats_buf) {
+				pr_err("%s: out of memory.\n",
+							__func__);
+				rc = -ENOMEM;
+			} else {
+				*stats_buf = stats;
+				isp_event->isp_data.isp_msg.len	=
+					sizeof(struct msm_stats_buf);
+				isp_event->isp_data.isp_msg.data = stats_buf;
+			}
+		}
+		}
+		break;
+	default:
+		pr_err("%s: Unsupport isp notification %d\n",
+			__func__, notification);
+		rc = -EINVAL;
+		break;
+	}
+
+	v4l2_event_queue(pmctl->config_device->config_stat_event_queue.pvdev,
+			 &v4l2_evt);
+
+	return rc;
+}
+
+static int msm_isp_notify(struct v4l2_subdev *sd,
+	unsigned int notification, void *arg)
+{
+	return msm_isp_notify_vfe(sd, notification, arg);
+}
+
+/* This function is called by open() function, so we need to init HW*/
+static int msm_isp_open(struct v4l2_subdev *sd,
+	struct msm_cam_media_controller *mctl)
+{
+	/* init vfe and senor, register sync callbacks for init*/
+	int rc = 0;
+	D("%s\n", __func__);
+	if (!mctl) {
+		pr_err("%s: param is NULL", __func__);
+		return -EINVAL;
+	}
+
+	rc = msm_vfe_subdev_init(sd, mctl);
+	if (rc < 0) {
+		pr_err("%s: vfe_init failed at %d\n",
+					__func__, rc);
+	}
+	return rc;
+}
+
+static void msm_isp_release(
+	struct v4l2_subdev *sd)
+{
+	D("%s\n", __func__);
+	msm_vfe_subdev_release(sd);
+}
+
+static int msm_config_vfe(struct v4l2_subdev *sd,
+	struct msm_cam_media_controller *mctl, void __user *arg)
+{
+	struct msm_vfe_cfg_cmd cfgcmd;
+	struct msm_pmem_region region[8];
+	struct axidata axi_data;
+
+	if (copy_from_user(&cfgcmd, arg, sizeof(cfgcmd))) {
+		ERR_COPY_FROM_USER();
+		return -EFAULT;
+	}
+
+	memset(&axi_data, 0, sizeof(axi_data));
+	CDBG("%s: cmd_type %d\n", __func__, cfgcmd.cmd_type);
+	switch (cfgcmd.cmd_type) {
+	case CMD_STATS_AF_ENABLE:
+		axi_data.bufnum1 =
+			msm_pmem_region_lookup(
+				&mctl->stats_info.pmem_stats_list,
+				MSM_PMEM_AF, &region[0],
+				NUM_STAT_OUTPUT_BUFFERS);
+		if (!axi_data.bufnum1) {
+			pr_err("%s %d: pmem region lookup error\n",
+				__func__, __LINE__);
+			return -EINVAL;
+		}
+		axi_data.region = &region[0];
+		return msm_isp_subdev_ioctl(sd, &cfgcmd,
+							&axi_data);
+	case CMD_STATS_AEC_ENABLE:
+		axi_data.bufnum1 =
+			msm_pmem_region_lookup(
+				&mctl->stats_info.pmem_stats_list,
+				MSM_PMEM_AEC, &region[0],
+				NUM_STAT_OUTPUT_BUFFERS);
+		if (!axi_data.bufnum1) {
+			pr_err("%s %d: pmem region lookup error\n",
+				__func__, __LINE__);
+			return -EINVAL;
+		}
+		axi_data.region = &region[0];
+		return msm_isp_subdev_ioctl(sd, &cfgcmd,
+							&axi_data);
+	case CMD_STATS_AWB_ENABLE:
+		axi_data.bufnum1 =
+			msm_pmem_region_lookup(
+				&mctl->stats_info.pmem_stats_list,
+				MSM_PMEM_AWB, &region[0],
+				NUM_STAT_OUTPUT_BUFFERS);
+		if (!axi_data.bufnum1) {
+			pr_err("%s %d: pmem region lookup error\n",
+				__func__, __LINE__);
+			return -EINVAL;
+		}
+		axi_data.region = &region[0];
+		return msm_isp_subdev_ioctl(sd, &cfgcmd,
+							&axi_data);
+	case CMD_STATS_AEC_AWB_ENABLE:
+		axi_data.bufnum1 =
+			msm_pmem_region_lookup(
+				&mctl->stats_info.pmem_stats_list,
+				MSM_PMEM_AEC_AWB, &region[0],
+				NUM_STAT_OUTPUT_BUFFERS);
+		if (!axi_data.bufnum1) {
+			pr_err("%s %d: pmem region lookup error\n",
+				__func__, __LINE__);
+			return -EINVAL;
+		}
+		axi_data.region = &region[0];
+		return msm_isp_subdev_ioctl(sd, &cfgcmd,
+							&axi_data);
+	case CMD_STATS_IHIST_ENABLE:
+		axi_data.bufnum1 =
+			msm_pmem_region_lookup(
+				&mctl->stats_info.pmem_stats_list,
+				MSM_PMEM_IHIST, &region[0],
+				NUM_STAT_OUTPUT_BUFFERS);
+		if (!axi_data.bufnum1) {
+			pr_err("%s %d: pmem region lookup error\n",
+				__func__, __LINE__);
+			return -EINVAL;
+		}
+		axi_data.region = &region[0];
+		return msm_isp_subdev_ioctl(sd, &cfgcmd,
+							&axi_data);
+	case CMD_STATS_RS_ENABLE:
+		axi_data.bufnum1 =
+			msm_pmem_region_lookup(
+				&mctl->stats_info.pmem_stats_list,
+				MSM_PMEM_RS, &region[0],
+				NUM_STAT_OUTPUT_BUFFERS);
+		if (!axi_data.bufnum1) {
+			pr_err("%s %d: pmem region lookup error\n",
+				__func__, __LINE__);
+			return -EINVAL;
+		}
+		axi_data.region = &region[0];
+		return msm_isp_subdev_ioctl(sd, &cfgcmd,
+							&axi_data);
+	case CMD_STATS_CS_ENABLE:
+		axi_data.bufnum1 =
+			msm_pmem_region_lookup(
+				&mctl->stats_info.pmem_stats_list,
+				MSM_PMEM_CS, &region[0],
+				NUM_STAT_OUTPUT_BUFFERS);
+		if (!axi_data.bufnum1) {
+			pr_err("%s %d: pmem region lookup error\n",
+				__func__, __LINE__);
+			return -EINVAL;
+		}
+		axi_data.region = &region[0];
+		return msm_isp_subdev_ioctl(sd, &cfgcmd,
+							&axi_data);
+	case CMD_GENERAL:
+	case CMD_STATS_DISABLE:
+		return msm_isp_subdev_ioctl(sd, &cfgcmd,
+							&axi_data);
+	default:
+		pr_err("%s: unknown command type %d\n",
+			__func__, cfgcmd.cmd_type);
+	}
+
+	return -EINVAL;
+}
+
+static int msm_axi_config(struct v4l2_subdev *sd,
+		struct msm_cam_media_controller *mctl, void __user *arg)
+{
+	struct msm_vfe_cfg_cmd cfgcmd;
+
+	if (copy_from_user(&cfgcmd, arg, sizeof(cfgcmd))) {
+		ERR_COPY_FROM_USER();
+		return -EFAULT;
+	}
+
+	switch (cfgcmd.cmd_type) {
+	case CMD_AXI_CFG_PRIM:
+	case CMD_AXI_CFG_SEC:
+	case CMD_AXI_CFG_ZSL:
+	case CMD_RAW_PICT_AXI_CFG:
+	case CMD_AXI_CFG_PRIM_ALL_CHNLS:
+	case CMD_AXI_CFG_PRIM|CMD_AXI_CFG_SEC:
+	case CMD_AXI_CFG_PRIM|CMD_AXI_CFG_SEC_ALL_CHNLS:
+	case CMD_AXI_CFG_PRIM_ALL_CHNLS|CMD_AXI_CFG_SEC:
+		/* Dont need to pass buffer information.
+		 * subdev will get the buffer from media
+		 * controller free queue.
+		 */
+		return msm_isp_subdev_ioctl(sd, &cfgcmd, NULL);
+
+	default:
+		pr_err("%s: unknown command type %d\n",
+			__func__,
+			cfgcmd.cmd_type);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int msm_put_stats_buffer(struct v4l2_subdev *sd,
+			struct msm_cam_media_controller *mctl, void __user *arg)
+{
+	int rc = -EIO;
+
+	struct msm_stats_buf buf;
+	unsigned long pphy;
+	struct msm_vfe_cfg_cmd cfgcmd;
+
+	if (copy_from_user(&buf, arg,
+				sizeof(struct msm_stats_buf))) {
+		ERR_COPY_FROM_USER();
+		return -EFAULT;
+	}
+
+	CDBG("%s\n", __func__);
+	pphy = msm_pmem_stats_vtop_lookup(mctl, buf.buffer, buf.fd);
+
+	if (pphy != 0) {
+		if (buf.type == STAT_AF)
+			cfgcmd.cmd_type = CMD_STATS_AF_BUF_RELEASE;
+		else if (buf.type == STAT_AEC)
+			cfgcmd.cmd_type = CMD_STATS_AEC_BUF_RELEASE;
+		else if (buf.type == STAT_AWB)
+			cfgcmd.cmd_type = CMD_STATS_AWB_BUF_RELEASE;
+		else if (buf.type == STAT_IHIST)
+			cfgcmd.cmd_type = CMD_STATS_IHIST_BUF_RELEASE;
+		else if (buf.type == STAT_RS)
+			cfgcmd.cmd_type = CMD_STATS_RS_BUF_RELEASE;
+		else if (buf.type == STAT_CS)
+			cfgcmd.cmd_type = CMD_STATS_CS_BUF_RELEASE;
+		else if (buf.type == STAT_AEAW)
+			cfgcmd.cmd_type = CMD_STATS_BUF_RELEASE;
+
+		else {
+			pr_err("%s: invalid buf type %d\n",
+				__func__,
+				buf.type);
+			rc = -EINVAL;
+			goto put_done;
+		}
+
+		cfgcmd.value = (void *)&buf;
+
+		rc = msm_isp_subdev_ioctl(sd, &cfgcmd, &pphy);
+	} else {
+		pr_err("%s: NULL physical address\n", __func__);
+		rc = -EINVAL;
+	}
+
+put_done:
+	return rc;
+}
+
+/* config function simliar to origanl msm_ioctl_config*/
+static int msm_isp_config(struct msm_cam_media_controller *pmctl,
+			 unsigned int cmd, unsigned long arg)
+{
+
+	int rc = -EINVAL;
+	void __user *argp = (void __user *)arg;
+	struct v4l2_subdev *sd = pmctl->isp_sdev->sd;
+
+	D("%s: cmd %d\n", __func__, _IOC_NR(cmd));
+	switch (cmd) {
+	case MSM_CAM_IOCTL_PICT_PP_DONE:
+		/* Release the preview of snapshot frame
+		 * that was grabbed.
+		 */
+		/*rc = msm_pp_release(pmsm->sync, arg);*/
+		break;
+
+	case MSM_CAM_IOCTL_CONFIG_VFE:
+		/* Coming from config thread for update */
+		rc = msm_config_vfe(sd, pmctl, argp);
+		break;
+
+	case MSM_CAM_IOCTL_AXI_CONFIG:
+		D("Received MSM_CAM_IOCTL_AXI_CONFIG\n");
+		rc = msm_axi_config(sd, pmctl, argp);
+		break;
+
+	case MSM_CAM_IOCTL_RELEASE_STATS_BUFFER:
+		rc = msm_put_stats_buffer(sd, pmctl, argp);
+		break;
+
+	default:
+		break;
+	}
+
+	D("%s: cmd %d DONE\n", __func__, _IOC_NR(cmd));
+
+	return rc;
+}
+
+static struct msm_isp_ops isp_subdev[MSM_MAX_CAMERA_CONFIGS];
+
+/**/
+int msm_isp_init_module(int g_num_config_nodes)
+{
+	int i = 0;
+
+	for (i = 0; i < g_num_config_nodes; i++) {
+		isp_subdev[i].isp_open = msm_isp_open;
+		isp_subdev[i].isp_config = msm_isp_config;
+		isp_subdev[i].isp_release  = msm_isp_release;
+		isp_subdev[i].isp_notify = msm_isp_notify;
+	}
+	return 0;
+}
+EXPORT_SYMBOL(msm_isp_init_module);
+
+/*
+*/
+int msm_isp_register(struct msm_cam_server_dev *psvr)
+{
+	int i = 0;
+
+	D("%s\n", __func__);
+
+	BUG_ON(!psvr);
+
+	/* Initialize notify function for v4l2_dev */
+	for (i = 0; i < psvr->config_info.num_config_nodes; i++)
+		psvr->isp_subdev[i] = &(isp_subdev[i]);
+
+	return 0;
+}
+EXPORT_SYMBOL(msm_isp_register);
+
+/**/
+void msm_isp_unregister(struct msm_cam_server_dev *psvr)
+{
+	int i = 0;
+	for (i = 0; i < psvr->config_info.num_config_nodes; i++)
+		psvr->isp_subdev[i] = NULL;
+}
+
+int msm_isp_subdev_ioctl(struct v4l2_subdev *isp_subdev,
+	struct msm_vfe_cfg_cmd *cfgcmd, void *data)
+{
+	struct msm_camvfe_params vfe_params;
+	vfe_params.vfe_cfg = cfgcmd;
+	vfe_params.data = data;
+	return v4l2_subdev_call(isp_subdev, core, ioctl, 0, &vfe_params);
+}
diff --git a/drivers/media/video/msm/msm_mctl.c b/drivers/media/video/msm/msm_mctl.c
new file mode 100644
index 0000000..cc3c59b
--- /dev/null
+++ b/drivers/media/video/msm/msm_mctl.c
@@ -0,0 +1,1743 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/ioctl.h>
+#include <linux/spinlock.h>
+#include <linux/videodev2.h>
+#include <linux/proc_fs.h>
+#include <linux/vmalloc.h>
+
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+
+#include <linux/android_pmem.h>
+
+#include "msm.h"
+#include "msm_csid.h"
+#include "msm_csic.h"
+#include "msm_csiphy.h"
+#include "msm_ispif.h"
+#include "msm_sensor.h"
+#include "msm_actuator.h"
+#include "msm_vpe.h"
+#include "msm_vfe32.h"
+#include "msm_camera_eeprom.h"
+
+#ifdef CONFIG_MSM_CAMERA_DEBUG
+#define D(fmt, args...) pr_debug("msm_mctl: " fmt, ##args)
+#else
+#define D(fmt, args...) do {} while (0)
+#endif
+
+#define MSM_V4L2_SWFI_LATENCY 3
+
+/* VFE required buffer number for streaming */
+static struct msm_isp_color_fmt msm_isp_formats[] = {
+	{
+	.name	   = "NV12YUV",
+	.depth	  = 12,
+	.bitsperpxl = 8,
+	.fourcc	 = V4L2_PIX_FMT_NV12,
+	.pxlcode	= V4L2_MBUS_FMT_YUYV8_2X8, /* YUV sensor */
+	.colorspace = V4L2_COLORSPACE_JPEG,
+	},
+	{
+	.name	   = "NV21YUV",
+	.depth	  = 12,
+	.bitsperpxl = 8,
+	.fourcc	 = V4L2_PIX_FMT_NV21,
+	.pxlcode	= V4L2_MBUS_FMT_YUYV8_2X8, /* YUV sensor */
+	.colorspace = V4L2_COLORSPACE_JPEG,
+	},
+	{
+	.name	   = "NV12BAYER",
+	.depth	  = 8,
+	.bitsperpxl = 8,
+	.fourcc	 = V4L2_PIX_FMT_NV12,
+	.pxlcode	= V4L2_MBUS_FMT_SBGGR10_1X10, /* Bayer sensor */
+	.colorspace = V4L2_COLORSPACE_JPEG,
+	},
+	{
+	.name	   = "NV21BAYER",
+	.depth	  = 8,
+	.bitsperpxl = 8,
+	.fourcc	 = V4L2_PIX_FMT_NV21,
+	.pxlcode	= V4L2_MBUS_FMT_SBGGR10_1X10, /* Bayer sensor */
+	.colorspace = V4L2_COLORSPACE_JPEG,
+	},
+	{
+	.name	   = "NV16BAYER",
+	.depth	  = 8,
+	.bitsperpxl = 8,
+	.fourcc	 = V4L2_PIX_FMT_NV16,
+	.pxlcode	= V4L2_MBUS_FMT_SBGGR10_1X10, /* Bayer sensor */
+	.colorspace = V4L2_COLORSPACE_JPEG,
+	},
+	{
+	.name	   = "NV61BAYER",
+	.depth	  = 8,
+	.bitsperpxl = 8,
+	.fourcc	 = V4L2_PIX_FMT_NV61,
+	.pxlcode	= V4L2_MBUS_FMT_SBGGR10_1X10, /* Bayer sensor */
+	.colorspace = V4L2_COLORSPACE_JPEG,
+	},
+	{
+	.name	   = "NV21BAYER",
+	.depth	  = 8,
+	.bitsperpxl = 8,
+	.fourcc	 = V4L2_PIX_FMT_NV21,
+	.pxlcode	= V4L2_MBUS_FMT_SGRBG10_1X10, /* Bayer sensor */
+	.colorspace = V4L2_COLORSPACE_JPEG,
+	},
+	{
+	.name	   = "YU12BAYER",
+	.depth	  = 8,
+	.bitsperpxl = 8,
+	.fourcc	 = V4L2_PIX_FMT_YUV420M,
+	.pxlcode	= V4L2_MBUS_FMT_SBGGR10_1X10, /* Bayer sensor */
+	.colorspace = V4L2_COLORSPACE_JPEG,
+	},
+	{
+	.name	   = "RAWBAYER",
+	.depth	  = 10,
+	.bitsperpxl = 10,
+	.fourcc	 = V4L2_PIX_FMT_SBGGR10,
+	.pxlcode	= V4L2_MBUS_FMT_SBGGR10_1X10, /* Bayer sensor */
+	.colorspace = V4L2_COLORSPACE_JPEG,
+	},
+	{
+	.name	   = "RAWBAYER",
+	.depth	  = 10,
+	.bitsperpxl = 10,
+	.fourcc	 = V4L2_PIX_FMT_SBGGR10,
+	.pxlcode	= V4L2_MBUS_FMT_SGRBG10_1X10, /* Bayer sensor */
+	.colorspace = V4L2_COLORSPACE_JPEG,
+	},
+
+};
+
+static int msm_get_sensor_info(
+	struct msm_cam_media_controller *mctl,
+	void __user *arg)
+{
+	int rc = 0;
+	struct msm_camsensor_info info;
+	struct msm_camera_sensor_info *sdata;
+	struct msm_cam_v4l2_device *pcam = mctl->pcam_ptr;
+	if (copy_from_user(&info,
+			arg,
+			sizeof(struct msm_camsensor_info))) {
+		ERR_COPY_FROM_USER();
+		return -EFAULT;
+	}
+
+	sdata = mctl->sdata;
+	D("%s: sensor_name %s\n", __func__, sdata->sensor_name);
+
+	memcpy(&info.name[0], sdata->sensor_name, MAX_SENSOR_NAME);
+	info.flash_enabled = sdata->flash_data->flash_type !=
+					MSM_CAMERA_FLASH_NONE;
+	info.pxlcode = pcam->usr_fmts[0].pxlcode;
+	info.flashtype = sdata->flash_type; /* two flash_types here? */
+	info.camera_type = sdata->camera_type;
+	/* sensor_type needed to add YUV/SOC in probing */
+	info.sensor_type = sdata->sensor_type;
+	info.mount_angle = sdata->sensor_platform_info->mount_angle;
+	info.actuator_enabled = sdata->actuator_info ? 1 : 0;
+	info.strobe_flash_enabled = sdata->strobe_flash_data ? 1 : 0;
+	/* copy back to user space */
+	if (copy_to_user((void *)arg,
+				&info,
+				sizeof(struct msm_camsensor_info))) {
+		ERR_COPY_TO_USER();
+		rc = -EFAULT;
+	}
+	return rc;
+}
+
+static int msm_mctl_set_vfe_output_mode(struct msm_cam_media_controller
+					*p_mctl, void __user *arg)
+{
+	int rc = 0;
+	if (copy_from_user(&p_mctl->vfe_output_mode,
+		(void __user *)arg, sizeof(p_mctl->vfe_output_mode))) {
+		pr_err("%s Copy from user failed ", __func__);
+		rc = -EFAULT;
+	} else {
+		pr_info("%s: mctl=0x%p, vfe output mode =0x%x",
+		  __func__, p_mctl, p_mctl->vfe_output_mode);
+	}
+	return rc;
+}
+
+/* called by the server or the config nodes to handle user space
+	commands*/
+static int msm_mctl_cmd(struct msm_cam_media_controller *p_mctl,
+			unsigned int cmd, unsigned long arg)
+{
+	int rc = -EINVAL;
+	void __user *argp = (void __user *)arg;
+	if (!p_mctl) {
+		pr_err("%s: param is NULL", __func__);
+		return -EINVAL;
+	}
+	D("%s:%d: cmd %d\n", __func__, __LINE__, cmd);
+
+	/* ... call sensor, ISPIF or VEF subdev*/
+	switch (cmd) {
+		/* sensor config*/
+	case MSM_CAM_IOCTL_GET_SENSOR_INFO:
+			rc = msm_get_sensor_info(p_mctl, argp);
+			break;
+
+	case MSM_CAM_IOCTL_SENSOR_IO_CFG:
+		rc = v4l2_subdev_call(p_mctl->sensor_sdev,
+			core, ioctl, VIDIOC_MSM_SENSOR_CFG, argp);
+			break;
+
+	case MSM_CAM_IOCTL_SENSOR_V4l2_S_CTRL: {
+			struct v4l2_control v4l2_ctrl;
+			CDBG("subdev call\n");
+			if (copy_from_user(&v4l2_ctrl,
+				(void *)argp,
+				sizeof(struct v4l2_control))) {
+				CDBG("copy fail\n");
+				return -EFAULT;
+			}
+			CDBG("subdev call ok\n");
+			rc = v4l2_subdev_call(p_mctl->sensor_sdev,
+				core, s_ctrl, &v4l2_ctrl);
+			break;
+	}
+
+	case MSM_CAM_IOCTL_SENSOR_V4l2_QUERY_CTRL: {
+			struct v4l2_queryctrl v4l2_qctrl;
+			CDBG("query called\n");
+			if (copy_from_user(&v4l2_qctrl,
+				(void *)argp,
+				sizeof(struct v4l2_queryctrl))) {
+				CDBG("copy fail\n");
+				rc = -EFAULT;
+				break;
+			}
+			rc = v4l2_subdev_call(p_mctl->sensor_sdev,
+				core, queryctrl, &v4l2_qctrl);
+			if (rc < 0) {
+				rc = -EFAULT;
+				break;
+			}
+			if (copy_to_user((void *)argp,
+					 &v4l2_qctrl,
+					 sizeof(struct v4l2_queryctrl))) {
+				rc = -EFAULT;
+			}
+			break;
+	}
+
+	case MSM_CAM_IOCTL_GET_ACTUATOR_INFO: {
+		struct msm_actuator_cfg_data cdata;
+		if (copy_from_user(&cdata,
+			(void *)argp,
+			sizeof(struct msm_actuator_cfg_data))) {
+			ERR_COPY_FROM_USER();
+			return -EFAULT;
+		}
+		cdata.is_af_supported = 0;
+		rc = 0;
+
+		if (p_mctl->act_sdev) {
+			struct msm_camera_sensor_info *sdata;
+
+			sdata = p_mctl->sdata;
+			CDBG("%s: Act_cam_Name %d\n", __func__,
+				sdata->actuator_info->cam_name);
+
+			cdata.is_af_supported = 1;
+			cdata.cfg.cam_name =
+				(enum af_camera_name)sdata->
+				actuator_info->cam_name;
+
+			CDBG("%s: Af Support:%d\n", __func__,
+				cdata.is_af_supported);
+			CDBG("%s: Act_name:%d\n", __func__, cdata.cfg.cam_name);
+
+		}
+		if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct msm_actuator_cfg_data))) {
+			ERR_COPY_TO_USER();
+			rc = -EFAULT;
+		}
+		break;
+	}
+
+	case MSM_CAM_IOCTL_ACTUATOR_IO_CFG: {
+		struct msm_actuator_cfg_data act_data;
+		if (p_mctl->act_sdev) {
+			rc = v4l2_subdev_call(p_mctl->act_sdev,
+				core, ioctl, VIDIOC_MSM_ACTUATOR_CFG, argp);
+		} else {
+			rc = copy_from_user(
+				&act_data,
+				(void *)argp,
+				sizeof(struct msm_actuator_cfg_data));
+			if (rc != 0) {
+				rc = -EFAULT;
+				break;
+			}
+			act_data.is_af_supported = 0;
+			rc = copy_to_user((void *)argp,
+					 &act_data,
+					 sizeof(struct msm_actuator_cfg_data));
+			if (rc != 0) {
+				rc = -EFAULT;
+				break;
+			}
+		}
+		break;
+	}
+
+	case MSM_CAM_IOCTL_EEPROM_IO_CFG: {
+		struct msm_eeprom_cfg_data eeprom_data;
+		if (p_mctl->eeprom_sdev) {
+			eeprom_data.is_eeprom_supported = 1;
+			rc = v4l2_subdev_call(p_mctl->eeprom_sdev,
+				core, ioctl, VIDIOC_MSM_EEPROM_CFG, argp);
+		} else {
+			rc = copy_from_user(
+				&eeprom_data,
+				(void *)argp,
+				sizeof(struct msm_eeprom_cfg_data));
+			if (rc != 0) {
+				rc = -EFAULT;
+				break;
+			}
+			eeprom_data.is_eeprom_supported = 0;
+			rc = copy_to_user((void *)argp,
+					 &eeprom_data,
+					 sizeof(struct msm_eeprom_cfg_data));
+			if (rc != 0) {
+				rc = -EFAULT;
+				break;
+			}
+		}
+		break;
+	}
+
+	case MSM_CAM_IOCTL_GET_KERNEL_SYSTEM_TIME: {
+		struct timeval timestamp;
+		if (copy_from_user(&timestamp, argp, sizeof(timestamp))) {
+			ERR_COPY_FROM_USER();
+			rc = -EFAULT;
+		} else {
+			msm_mctl_gettimeofday(&timestamp);
+			rc = copy_to_user((void *)argp,
+				 &timestamp, sizeof(timestamp));
+		}
+		break;
+	}
+
+	case MSM_CAM_IOCTL_FLASH_CTRL: {
+		struct flash_ctrl_data flash_info;
+		if (copy_from_user(&flash_info, argp, sizeof(flash_info))) {
+			ERR_COPY_FROM_USER();
+			rc = -EFAULT;
+		} else {
+			rc = msm_flash_ctrl(p_mctl->sdata, &flash_info);
+		}
+		break;
+	}
+	case MSM_CAM_IOCTL_PICT_PP:
+		rc = msm_mctl_set_pp_key(p_mctl, (void __user *)arg);
+		break;
+	case MSM_CAM_IOCTL_PICT_PP_DIVERT_DONE:
+		rc = msm_mctl_pp_divert_done(p_mctl, (void __user *)arg);
+		break;
+	case MSM_CAM_IOCTL_PICT_PP_DONE:
+		rc = msm_mctl_pp_done(p_mctl, (void __user *)arg);
+		break;
+	case MSM_CAM_IOCTL_MCTL_POST_PROC:
+		rc = msm_mctl_pp_ioctl(p_mctl, cmd, arg);
+		break;
+	case MSM_CAM_IOCTL_RESERVE_FREE_FRAME:
+		rc = msm_mctl_pp_reserve_free_frame(p_mctl,
+			(void __user *)arg);
+		break;
+	case MSM_CAM_IOCTL_RELEASE_FREE_FRAME:
+		rc = msm_mctl_pp_release_free_frame(p_mctl,
+			(void __user *)arg);
+		break;
+	case MSM_CAM_IOCTL_SET_VFE_OUTPUT_TYPE:
+		rc = msm_mctl_set_vfe_output_mode(p_mctl,
+		  (void __user *)arg);
+		break;
+	case MSM_CAM_IOCTL_MCTL_DIVERT_DONE:
+		rc = msm_mctl_pp_mctl_divert_done(p_mctl,
+			(void __user *)arg);
+		break;
+			/* ISFIF config*/
+	case MSM_CAM_IOCTL_AXI_CONFIG:
+		if (p_mctl->axi_sdev)
+			rc = v4l2_subdev_call(p_mctl->axi_sdev, core, ioctl,
+				VIDIOC_MSM_AXI_CFG, (void __user *)arg);
+		else
+			rc = p_mctl->isp_sdev->isp_config(p_mctl, cmd, arg);
+		break;
+	default:
+		/* ISP config*/
+		D("%s:%d: go to default. Calling msm_isp_config\n",
+			__func__, __LINE__);
+		rc = p_mctl->isp_sdev->isp_config(p_mctl, cmd, arg);
+		break;
+	}
+	D("%s: !!! cmd = %d, rc = %d\n",
+		__func__, _IOC_NR(cmd), rc);
+	return rc;
+}
+
+static int msm_mctl_subdev_match_core(struct device *dev, void *data)
+{
+	int core_index = (int)data;
+	struct platform_device *pdev = to_platform_device(dev);
+
+	if (pdev->id == core_index)
+		return 1;
+	else
+		return 0;
+}
+
+static int msm_mctl_register_subdevs(struct msm_cam_media_controller *p_mctl,
+	int core_index)
+{
+	struct device_driver *driver;
+	struct device *dev;
+	int rc = -ENODEV;
+
+	struct msm_sensor_ctrl_t *s_ctrl = get_sctrl(p_mctl->sensor_sdev);
+	struct msm_camera_sensor_info *sinfo =
+		(struct msm_camera_sensor_info *) s_ctrl->sensordata;
+	struct msm_camera_device_platform_data *pdata = sinfo->pdata;
+
+	if (pdata->is_csiphy) {
+		/* register csiphy subdev */
+		driver = driver_find(MSM_CSIPHY_DRV_NAME, &platform_bus_type);
+		if (!driver)
+			goto out;
+
+		dev = driver_find_device(driver, NULL, (void *)core_index,
+				msm_mctl_subdev_match_core);
+		if (!dev)
+			goto out;
+
+		p_mctl->csiphy_sdev = dev_get_drvdata(dev);
+	}
+
+	if (pdata->is_csic) {
+		/* register csic subdev */
+		driver = driver_find(MSM_CSIC_DRV_NAME, &platform_bus_type);
+		if (!driver)
+			goto out;
+
+		dev = driver_find_device(driver, NULL, (void *)core_index,
+				msm_mctl_subdev_match_core);
+		if (!dev)
+			goto out;
+
+		p_mctl->csic_sdev = dev_get_drvdata(dev);
+	}
+
+	if (pdata->is_csid) {
+		/* register csid subdev */
+		driver = driver_find(MSM_CSID_DRV_NAME, &platform_bus_type);
+		if (!driver)
+			goto out;
+
+		dev = driver_find_device(driver, NULL, (void *)core_index,
+				msm_mctl_subdev_match_core);
+		if (!dev)
+			goto out;
+
+		p_mctl->csid_sdev = dev_get_drvdata(dev);
+	}
+
+	if (pdata->is_ispif) {
+		/* register ispif subdev */
+		driver = driver_find(MSM_ISPIF_DRV_NAME, &platform_bus_type);
+		if (!driver)
+			goto out;
+
+		dev = driver_find_device(driver, NULL, 0,
+				msm_mctl_subdev_match_core);
+		if (!dev)
+			goto out;
+
+		p_mctl->ispif_sdev = dev_get_drvdata(dev);
+	}
+
+	/* register vfe subdev */
+	driver = driver_find(MSM_VFE_DRV_NAME, &platform_bus_type);
+	if (!driver)
+		goto out;
+
+	dev = driver_find_device(driver, NULL, 0,
+				msm_mctl_subdev_match_core);
+	if (!dev)
+		goto out;
+
+	p_mctl->isp_sdev->sd = dev_get_drvdata(dev);
+
+	if (pdata->is_vpe) {
+		/* register vfe subdev */
+		driver = driver_find(MSM_VPE_DRV_NAME, &platform_bus_type);
+		if (!driver)
+			goto out;
+
+		dev = driver_find_device(driver, NULL, 0,
+				msm_mctl_subdev_match_core);
+		if (!dev)
+			goto out;
+
+		p_mctl->vpe_sdev = dev_get_drvdata(dev);
+	}
+
+	rc = 0;
+
+
+	/* register gemini subdev */
+	driver = driver_find(MSM_GEMINI_DRV_NAME, &platform_bus_type);
+	if (!driver) {
+		pr_err("%s:%d:Gemini: Failure: goto out\n",
+			__func__, __LINE__);
+		goto out;
+	}
+	pr_debug("%s:%d:Gemini: driver_find_device Gemini driver 0x%x\n",
+		__func__, __LINE__, (uint32_t)driver);
+	dev = driver_find_device(driver, NULL, NULL,
+				msm_mctl_subdev_match_core);
+	if (!dev) {
+		pr_err("%s:%d:Gemini: Failure goto out\n",
+			__func__, __LINE__);
+		goto out;
+	}
+	p_mctl->gemini_sdev = dev_get_drvdata(dev);
+	pr_debug("%s:%d:Gemini: After dev_get_drvdata gemini_sdev=0x%x\n",
+		__func__, __LINE__, (uint32_t)p_mctl->gemini_sdev);
+
+	if (p_mctl->gemini_sdev == NULL) {
+		pr_err("%s:%d:Gemini: Failure gemini_sdev is null\n",
+			__func__, __LINE__);
+		goto out;
+	}
+	rc = 0;
+out:
+	return rc;
+}
+
+static int msm_mctl_open(struct msm_cam_media_controller *p_mctl,
+				 const char *const apps_id)
+{
+	int rc = 0;
+	struct msm_sensor_ctrl_t *s_ctrl = get_sctrl(p_mctl->sensor_sdev);
+	struct msm_camera_sensor_info *sinfo =
+		(struct msm_camera_sensor_info *) s_ctrl->sensordata;
+	struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+	uint8_t csid_core;
+	D("%s\n", __func__);
+	if (!p_mctl) {
+		pr_err("%s: param is NULL", __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&p_mctl->lock);
+	/* open sub devices - once only*/
+	if (!p_mctl->opencnt) {
+		uint32_t csid_version;
+		wake_lock(&p_mctl->wake_lock);
+
+		csid_core = camdev->csid_core;
+		rc = msm_mctl_register_subdevs(p_mctl, csid_core);
+		if (rc < 0) {
+			pr_err("%s: msm_mctl_register_subdevs failed:%d\n",
+				__func__, rc);
+			goto register_sdev_failed;
+		}
+
+		/* then sensor - move sub dev later */
+		rc = v4l2_subdev_call(p_mctl->sensor_sdev, core, s_power, 1);
+		if (rc < 0) {
+			pr_err("%s: sensor powerup failed: %d\n", __func__, rc);
+			goto sensor_sdev_failed;
+		}
+
+		if (p_mctl->act_sdev)
+			rc = v4l2_subdev_call(p_mctl->act_sdev,
+				core, s_power, 1);
+		if (rc < 0) {
+			pr_err("%s: act power failed:%d\n", __func__, rc);
+			goto act_power_up_failed;
+		}
+
+		if (camdev->is_csiphy) {
+			rc = v4l2_subdev_call(p_mctl->csiphy_sdev, core, ioctl,
+				VIDIOC_MSM_CSIPHY_INIT, NULL);
+			if (rc < 0) {
+				pr_err("%s: csiphy initialization failed %d\n",
+					__func__, rc);
+				goto csiphy_init_failed;
+			}
+		}
+
+		if (camdev->is_csid) {
+			rc = v4l2_subdev_call(p_mctl->csid_sdev, core, ioctl,
+				VIDIOC_MSM_CSID_INIT, &csid_version);
+			if (rc < 0) {
+				pr_err("%s: csid initialization failed %d\n",
+					__func__, rc);
+				goto csid_init_failed;
+			}
+		}
+
+		if (camdev->is_csic) {
+			rc = v4l2_subdev_call(p_mctl->csic_sdev, core, ioctl,
+				VIDIOC_MSM_CSIC_INIT, &csid_version);
+			if (rc < 0) {
+				pr_err("%s: csic initialization failed %d\n",
+					__func__, rc);
+				goto csic_init_failed;
+			}
+		}
+
+		/* ISP first*/
+		if (p_mctl->isp_sdev && p_mctl->isp_sdev->isp_open) {
+			rc = p_mctl->isp_sdev->isp_open(
+				p_mctl->isp_sdev->sd, p_mctl);
+			if (rc < 0) {
+				pr_err("%s: isp init failed: %d\n",
+					__func__, rc);
+				goto isp_open_failed;
+			}
+		}
+
+		if (p_mctl->axi_sdev) {
+			rc = v4l2_subdev_call(p_mctl->axi_sdev, core, ioctl,
+				VIDIOC_MSM_AXI_INIT, p_mctl);
+			if (rc < 0) {
+				pr_err("%s: axi initialization failed %d\n",
+					__func__, rc);
+				goto axi_init_failed;
+			}
+		}
+
+		if (camdev->is_vpe) {
+			rc = v4l2_subdev_call(p_mctl->vpe_sdev, core, ioctl,
+				VIDIOC_MSM_VPE_INIT, p_mctl);
+			if (rc < 0) {
+				pr_err("%s: vpe initialization failed %d\n",
+				__func__, rc);
+				goto vpe_init_failed;
+			}
+		}
+
+		if (camdev->is_ispif) {
+			rc = v4l2_subdev_call(p_mctl->ispif_sdev, core, ioctl,
+				VIDIOC_MSM_ISPIF_INIT, &csid_version);
+			if (rc < 0) {
+				pr_err("%s: ispif initialization failed %d\n",
+					__func__, rc);
+				goto ispif_init_failed;
+			}
+		}
+
+		if (camdev->is_ispif) {
+			pm_qos_add_request(&p_mctl->pm_qos_req_list,
+				PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);
+			pm_qos_update_request(&p_mctl->pm_qos_req_list,
+				MSM_V4L2_SWFI_LATENCY);
+		}
+		p_mctl->apps_id = apps_id;
+		p_mctl->opencnt++;
+	} else {
+		D("%s: camera is already open", __func__);
+	}
+	mutex_unlock(&p_mctl->lock);
+
+	return rc;
+
+ispif_init_failed:
+	if (camdev->is_vpe)
+		if (v4l2_subdev_call(p_mctl->vpe_sdev, core, ioctl,
+			VIDIOC_MSM_VPE_RELEASE, NULL) < 0)
+			pr_err("%s: vpe release failed %d\n", __func__, rc);
+vpe_init_failed:
+	if (p_mctl->axi_sdev)
+		if (v4l2_subdev_call(p_mctl->axi_sdev, core, ioctl,
+			VIDIOC_MSM_AXI_RELEASE, NULL) < 0)
+			pr_err("%s: axi release failed %d\n", __func__, rc);
+axi_init_failed:
+	if (p_mctl->isp_sdev && p_mctl->isp_sdev->isp_release)
+		p_mctl->isp_sdev->isp_release(p_mctl->isp_sdev->sd);
+isp_open_failed:
+	if (camdev->is_csic)
+		if (v4l2_subdev_call(p_mctl->csic_sdev, core, ioctl,
+			VIDIOC_MSM_CSIC_RELEASE, NULL) < 0)
+			pr_err("%s: csic release failed %d\n", __func__, rc);
+csic_init_failed:
+	if (camdev->is_csid)
+		if (v4l2_subdev_call(p_mctl->csid_sdev, core, ioctl,
+			VIDIOC_MSM_CSID_RELEASE, NULL) < 0)
+			pr_err("%s: csid release failed %d\n", __func__, rc);
+csid_init_failed:
+	if (camdev->is_csiphy)
+		if (v4l2_subdev_call(p_mctl->csiphy_sdev, core, ioctl,
+			VIDIOC_MSM_CSIPHY_RELEASE, NULL) < 0)
+			pr_err("%s: csiphy release failed %d\n", __func__, rc);
+csiphy_init_failed:
+	if (p_mctl->act_sdev)
+		if (v4l2_subdev_call(p_mctl->act_sdev, core,
+			s_power, 0) < 0)
+			pr_err("%s: act power down failed:%d\n", __func__, rc);
+act_power_up_failed:
+	if (v4l2_subdev_call(p_mctl->sensor_sdev, core, s_power, 0) < 0)
+		pr_err("%s: sensor powerdown failed: %d\n", __func__, rc);
+sensor_sdev_failed:
+register_sdev_failed:
+	wake_unlock(&p_mctl->wake_lock);
+	mutex_unlock(&p_mctl->lock);
+	return rc;
+}
+
+static int msm_mctl_release(struct msm_cam_media_controller *p_mctl)
+{
+	int rc = 0;
+	struct msm_sensor_ctrl_t *s_ctrl = get_sctrl(p_mctl->sensor_sdev);
+	struct msm_camera_sensor_info *sinfo =
+		(struct msm_camera_sensor_info *) s_ctrl->sensordata;
+	struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+
+	v4l2_subdev_call(p_mctl->sensor_sdev, core, ioctl,
+		VIDIOC_MSM_SENSOR_RELEASE, NULL);
+
+	if (camdev->is_ispif) {
+		v4l2_subdev_call(p_mctl->ispif_sdev, core, ioctl,
+			VIDIOC_MSM_ISPIF_RELEASE, NULL);
+	}
+
+	if (camdev->is_csic) {
+		v4l2_subdev_call(p_mctl->csic_sdev, core, ioctl,
+			VIDIOC_MSM_CSIC_RELEASE, NULL);
+	}
+
+	if (camdev->is_vpe) {
+		v4l2_subdev_call(p_mctl->vpe_sdev, core, ioctl,
+			VIDIOC_MSM_VPE_RELEASE, NULL);
+	}
+
+	if (p_mctl->axi_sdev) {
+		v4l2_subdev_call(p_mctl->axi_sdev, core, ioctl,
+			VIDIOC_MSM_AXI_RELEASE, NULL);
+	}
+
+	if (p_mctl->isp_sdev && p_mctl->isp_sdev->isp_release)
+		p_mctl->isp_sdev->isp_release(
+			p_mctl->isp_sdev->sd);
+
+	if (camdev->is_csid) {
+		v4l2_subdev_call(p_mctl->csid_sdev, core, ioctl,
+			VIDIOC_MSM_CSID_RELEASE, NULL);
+	}
+
+	if (camdev->is_csiphy) {
+		v4l2_subdev_call(p_mctl->csiphy_sdev, core, ioctl,
+			VIDIOC_MSM_CSIPHY_RELEASE, NULL);
+	}
+
+	if (camdev->is_ispif) {
+		pm_qos_update_request(&p_mctl->pm_qos_req_list,
+				PM_QOS_DEFAULT_VALUE);
+		pm_qos_remove_request(&p_mctl->pm_qos_req_list);
+	}
+
+	if (p_mctl->act_sdev)
+		v4l2_subdev_call(p_mctl->act_sdev, core, s_power, 0);
+
+	v4l2_subdev_call(p_mctl->sensor_sdev, core, s_power, 0);
+
+	wake_unlock(&p_mctl->wake_lock);
+	return rc;
+}
+
+int msm_mctl_init_user_formats(struct msm_cam_v4l2_device *pcam)
+{
+	struct v4l2_subdev *sd = pcam->sensor_sdev;
+	enum v4l2_mbus_pixelcode pxlcode;
+	int numfmt_sensor = 0;
+	int numfmt = 0;
+	int rc = 0;
+	int i, j;
+
+	D("%s\n", __func__);
+	while (!v4l2_subdev_call(sd, video, enum_mbus_fmt, numfmt_sensor,
+								&pxlcode))
+		numfmt_sensor++;
+
+	D("%s, numfmt_sensor = %d\n", __func__, numfmt_sensor);
+	if (!numfmt_sensor)
+		return -ENXIO;
+
+	pcam->usr_fmts = vmalloc(numfmt_sensor * ARRAY_SIZE(msm_isp_formats) *
+				sizeof(struct msm_isp_color_fmt));
+	if (!pcam->usr_fmts)
+		return -ENOMEM;
+
+	/* from sensor to ISP.. fill the data structure */
+	for (i = 0; i < numfmt_sensor; i++) {
+		rc = v4l2_subdev_call(sd, video, enum_mbus_fmt, i, &pxlcode);
+		D("rc is  %d\n", rc);
+		if (rc < 0) {
+			vfree(pcam->usr_fmts);
+			return rc;
+		}
+
+		for (j = 0; j < ARRAY_SIZE(msm_isp_formats); j++) {
+			/* find the corresponding format */
+			if (pxlcode == msm_isp_formats[j].pxlcode) {
+				pcam->usr_fmts[numfmt] = msm_isp_formats[j];
+				D("pcam->usr_fmts=0x%x\n", (u32)pcam->usr_fmts);
+				D("format pxlcode 0x%x (0x%x) found\n",
+					  pcam->usr_fmts[numfmt].pxlcode,
+					  pcam->usr_fmts[numfmt].fourcc);
+				numfmt++;
+			}
+		}
+	}
+
+	pcam->num_fmts = numfmt;
+
+	if (numfmt == 0) {
+		pr_err("%s: No supported formats.\n", __func__);
+		vfree(pcam->usr_fmts);
+		return -EINVAL;
+	}
+
+	D("Found %d supported formats.\n", pcam->num_fmts);
+	/* set the default pxlcode, in any case, it will be set through
+	 * setfmt */
+	return 0;
+}
+
+/* this function plug in the implementation of a v4l2_subdev */
+int msm_mctl_init(struct msm_cam_v4l2_device *pcam)
+{
+	struct msm_cam_media_controller *pmctl = NULL;
+	D("%s\n", __func__);
+	if (!pcam) {
+		pr_err("%s: param is NULL", __func__);
+		return -EINVAL;
+	}
+	pcam->mctl_handle = msm_camera_get_mctl_handle();
+	if (pcam->mctl_handle == 0) {
+		pr_err("%s: cannot get mctl handle", __func__);
+		return -EINVAL;
+	}
+
+	pmctl = msm_camera_get_mctl(pcam->mctl_handle);
+	if (!pmctl) {
+		pr_err("%s: invalid mctl controller", __func__);
+		return -EINVAL;
+	}
+
+	wake_lock_init(&pmctl->wake_lock, WAKE_LOCK_IDLE, "msm_camera");
+	mutex_init(&pmctl->lock);
+	pmctl->opencnt = 0;
+
+	/* init module operations*/
+	pmctl->mctl_open = msm_mctl_open;
+	pmctl->mctl_cmd = msm_mctl_cmd;
+	pmctl->mctl_release = msm_mctl_release;
+	/* init mctl buf */
+	msm_mctl_buf_init(pcam);
+	memset(&pmctl->pp_info, 0, sizeof(pmctl->pp_info));
+	pmctl->vfe_output_mode = 0;
+	spin_lock_init(&pmctl->pp_info.lock);
+
+	pmctl->act_sdev = pcam->act_sdev;
+	pmctl->eeprom_sdev = pcam->eeprom_sdev;
+	pmctl->sensor_sdev = pcam->sensor_sdev;
+	pmctl->sdata = pcam->sdata;
+
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+	pmctl->client = msm_ion_client_create(-1, "camera");
+	kref_init(&pmctl->refcount);
+#endif
+
+	return 0;
+}
+
+int msm_mctl_free(struct msm_cam_v4l2_device *pcam)
+{
+	int rc = 0;
+	struct msm_cam_media_controller *pmctl = NULL;
+	D("%s\n", __func__);
+
+	pmctl = msm_camera_get_mctl(pcam->mctl_handle);
+	if (!pmctl) {
+		pr_err("%s: invalid mctl controller", __func__);
+		return -EINVAL;
+	}
+
+	mutex_destroy(&pmctl->lock);
+	wake_lock_destroy(&pmctl->wake_lock);
+	msm_camera_free_mctl(pcam->mctl_handle);
+	return rc;
+}
+
+/* mctl node v4l2_file_operations */
+static int msm_mctl_dev_open(struct file *f)
+{
+	int rc = -EINVAL, i;
+	/* get the video device */
+	struct msm_cam_v4l2_device *pcam  = NULL;
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	struct msm_cam_media_controller *pmctl;
+
+	if (f == NULL) {
+		pr_err("%s :: cannot open video driver data", __func__);
+		return rc;
+	}
+	pcam = video_drvdata(f);
+
+	if (!pcam) {
+		pr_err("%s NULL pointer passed in!\n", __func__);
+		return rc;
+	}
+
+	D("%s : E use_count %d", __func__, pcam->mctl_node.use_count);
+	mutex_lock(&pcam->mctl_node.dev_lock);
+	for (i = 0; i < MSM_DEV_INST_MAX; i++) {
+		if (pcam->mctl_node.dev_inst[i] == NULL)
+			break;
+	}
+	/* if no instance is available, return error */
+	if (i == MSM_DEV_INST_MAX) {
+		mutex_unlock(&pcam->mctl_node.dev_lock);
+		return rc;
+	}
+	pcam_inst = kzalloc(sizeof(struct msm_cam_v4l2_dev_inst), GFP_KERNEL);
+	if (!pcam_inst) {
+		mutex_unlock(&pcam->mctl_node.dev_lock);
+		return rc;
+	}
+
+	pcam_inst->sensor_pxlcode = pcam->usr_fmts[0].pxlcode;
+	pcam_inst->my_index = i;
+	pcam_inst->pcam = pcam;
+	pcam->mctl_node.dev_inst[i] = pcam_inst;
+
+	D("%s pcam_inst %p my_index = %d\n", __func__,
+		pcam_inst, pcam_inst->my_index);
+	rc = msm_cam_server_open_mctl_session(pcam,
+		&pcam->mctl_node.active);
+	if (rc < 0) {
+		pr_err("%s: mctl session open failed %d", __func__, rc);
+		mutex_unlock(&pcam->mctl_node.dev_lock);
+		return rc;
+	}
+
+	pmctl = msm_camera_get_mctl(pcam->mctl_handle);
+	if (!pmctl) {
+		pr_err("%s mctl NULL!\n", __func__);
+		return rc;
+	}
+
+	D("%s active %d\n", __func__, pcam->mctl_node.active);
+	rc = msm_setup_v4l2_event_queue(&pcam_inst->eventHandle,
+					pcam->mctl_node.pvdev);
+	if (rc < 0) {
+		mutex_unlock(&pcam->mctl_node.dev_lock);
+		return rc;
+	}
+	pcam_inst->vbqueue_initialized = 0;
+	kref_get(&pmctl->refcount);
+	f->private_data = &pcam_inst->eventHandle;
+
+	D("f->private_data = 0x%x, pcam = 0x%x\n",
+		(u32)f->private_data, (u32)pcam_inst);
+
+	pcam->mctl_node.use_count++;
+	mutex_unlock(&pcam->mctl_node.dev_lock);
+	D("%s : X ", __func__);
+	return rc;
+}
+
+static unsigned int msm_mctl_dev_poll(struct file *f,
+				struct poll_table_struct *wait)
+{
+	int rc = 0;
+	struct msm_cam_v4l2_device *pcam;
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	pcam_inst = container_of(f->private_data,
+			struct msm_cam_v4l2_dev_inst, eventHandle);
+	pcam = pcam_inst->pcam;
+
+	D("%s : E pcam_inst = %p", __func__, pcam_inst);
+	if (!pcam) {
+		pr_err("%s NULL pointer of camera device!\n", __func__);
+		return -EINVAL;
+	}
+
+	poll_wait(f, &(pcam_inst->eventHandle.wait), wait);
+	if (v4l2_event_pending(&pcam_inst->eventHandle)) {
+		rc |= POLLPRI;
+		D("%s Event available on mctl node ", __func__);
+	}
+
+	D("%s poll on vb2\n", __func__);
+	if (!pcam_inst->vid_bufq.streaming) {
+		D("%s vid_bufq.streaming is off, inst=0x%x\n",
+				__func__, (u32)pcam_inst);
+		return rc;
+	}
+	rc |= vb2_poll(&pcam_inst->vid_bufq, f, wait);
+
+	D("%s : X ", __func__);
+	return rc;
+}
+
+static int msm_mctl_dev_close(struct file *f)
+{
+	int rc = 0;
+	struct msm_cam_v4l2_device *pcam;
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	struct msm_cam_media_controller *pmctl;
+	pcam_inst = container_of(f->private_data,
+		struct msm_cam_v4l2_dev_inst, eventHandle);
+	pcam = pcam_inst->pcam;
+
+	D("%s : E ", __func__);
+	if (!pcam) {
+		pr_err("%s NULL pointer of camera device!\n", __func__);
+		return -EINVAL;
+	}
+
+	pmctl = msm_camera_get_mctl(pcam->mctl_handle);
+	mutex_lock(&pcam->mctl_node.dev_lock);
+	D("%s : active %d ", __func__, pcam->mctl_node.active);
+	if (pcam->mctl_node.active == 1) {
+		rc = msm_cam_server_close_mctl_session(pcam);
+		if (rc < 0) {
+			pr_err("%s: mctl session close failed %d",
+				__func__, rc);
+			mutex_unlock(&pcam->mctl_node.dev_lock);
+			return rc;
+		}
+		pmctl = NULL;
+	}
+	pcam_inst->streamon = 0;
+	pcam->mctl_node.dev_inst_map[pcam_inst->image_mode] = NULL;
+	if (pcam_inst->vbqueue_initialized)
+		vb2_queue_release(&pcam_inst->vid_bufq);
+	D("%s Closing down instance %p ", __func__, pcam_inst);
+	pcam->mctl_node.dev_inst[pcam_inst->my_index] = NULL;
+	v4l2_fh_del(&pcam_inst->eventHandle);
+	v4l2_fh_exit(&pcam_inst->eventHandle);
+
+	kfree(pcam_inst);
+	if (NULL != pmctl) {
+		D("%s : release ion client", __func__);
+		kref_put(&pmctl->refcount, msm_release_ion_client);
+	}
+	f->private_data = NULL;
+	mutex_unlock(&pcam->mctl_node.dev_lock);
+	pcam->mctl_node.use_count--;
+	D("%s : use_count %d X ", __func__, pcam->mctl_node.use_count);
+	return rc;
+}
+
+static struct v4l2_file_operations g_msm_mctl_fops = {
+	.owner   = THIS_MODULE,
+	.open	= msm_mctl_dev_open,
+	.poll	= msm_mctl_dev_poll,
+	.release = msm_mctl_dev_close,
+	.unlocked_ioctl = video_ioctl2,
+};
+
+/*
+ *
+ * implementation of mctl node v4l2_ioctl_ops
+ *
+ */
+static int msm_mctl_v4l2_querycap(struct file *f, void *pctx,
+				struct v4l2_capability *pcaps)
+{
+	struct msm_cam_v4l2_device *pcam;
+
+	if (f == NULL) {
+		pr_err("%s :: NULL file pointer", __func__);
+		return -EINVAL;
+	}
+
+	pcam = video_drvdata(f);
+
+	D("%s\n", __func__);
+	WARN_ON(pctx != f->private_data);
+
+	if (!pcam) {
+		pr_err("%s NULL pointer passed in!\n", __func__);
+		return -EINVAL;
+	}
+
+	strlcpy(pcaps->driver, pcam->media_dev.dev->driver->name,
+			sizeof(pcaps->driver));
+	pcaps->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+	return 0;
+}
+
+static int msm_mctl_v4l2_queryctrl(struct file *f, void *pctx,
+				struct v4l2_queryctrl *pqctrl)
+{
+	int rc = 0;
+
+	D("%s\n", __func__);
+	WARN_ON(pctx != f->private_data);
+
+	return rc;
+}
+
+static int msm_mctl_v4l2_g_ctrl(struct file *f, void *pctx,
+					struct v4l2_control *c)
+{
+	int rc = 0;
+
+	D("%s\n", __func__);
+	WARN_ON(pctx != f->private_data);
+
+	return rc;
+}
+
+static int msm_mctl_v4l2_s_ctrl(struct file *f, void *pctx,
+					struct v4l2_control *ctrl)
+{
+	int rc = 0;
+	struct msm_cam_v4l2_device *pcam  = video_drvdata(f);
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	pcam_inst = container_of(f->private_data,
+			struct msm_cam_v4l2_dev_inst, eventHandle);
+
+	D("%s\n", __func__);
+
+	WARN_ON(pctx != f->private_data);
+	mutex_lock(&pcam->mctl_node.dev_lock);
+	if (ctrl->id == MSM_V4L2_PID_PP_PLANE_INFO) {
+		if (copy_from_user(&pcam_inst->plane_info,
+					(void *)ctrl->value,
+					sizeof(struct img_plane_info))) {
+			pr_err("%s inst %p Copying plane_info failed ",
+					__func__, pcam_inst);
+			rc = -EFAULT;
+		}
+		D("%s inst %p got plane info: num_planes = %d,"
+				"plane size = %ld %ld ", __func__, pcam_inst,
+				pcam_inst->plane_info.num_planes,
+				pcam_inst->plane_info.plane[0].size,
+				pcam_inst->plane_info.plane[1].size);
+	} else
+		pr_err("%s Unsupported S_CTRL Value ", __func__);
+
+	mutex_unlock(&pcam->mctl_node.dev_lock);
+
+	return rc;
+}
+
+static int msm_mctl_v4l2_reqbufs(struct file *f, void *pctx,
+				struct v4l2_requestbuffers *pb)
+{
+	int rc = 0, i, j;
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	pcam_inst = container_of(f->private_data,
+		struct msm_cam_v4l2_dev_inst, eventHandle);
+	D("%s\n", __func__);
+	WARN_ON(pctx != f->private_data);
+	rc = vb2_reqbufs(&pcam_inst->vid_bufq, pb);
+	if (rc < 0) {
+		pr_err("%s reqbufs failed %d ", __func__, rc);
+		return rc;
+	}
+	if (!pb->count) {
+		/* Deallocation. free buf_offset array */
+		D("%s Inst %p freeing buffer offsets array",
+			__func__, pcam_inst);
+		for (j = 0 ; j < pcam_inst->buf_count ; j++)
+			kfree(pcam_inst->buf_offset[j]);
+		kfree(pcam_inst->buf_offset);
+		pcam_inst->buf_offset = NULL;
+		/* If the userspace has deallocated all the
+		 * buffers, then release the vb2 queue */
+		if (pcam_inst->vbqueue_initialized) {
+			vb2_queue_release(&pcam_inst->vid_bufq);
+			pcam_inst->vbqueue_initialized = 0;
+		}
+	} else {
+		D("%s Inst %p Allocating buf_offset array",
+			__func__, pcam_inst);
+		/* Allocation. allocate buf_offset array */
+		pcam_inst->buf_offset = (struct msm_cam_buf_offset **)
+			kzalloc(pb->count * sizeof(struct msm_cam_buf_offset *),
+							GFP_KERNEL);
+		if (!pcam_inst->buf_offset) {
+			pr_err("%s out of memory ", __func__);
+			return -ENOMEM;
+		}
+		for (i = 0; i < pb->count; i++) {
+			pcam_inst->buf_offset[i] =
+				kzalloc(sizeof(struct msm_cam_buf_offset) *
+				pcam_inst->plane_info.num_planes, GFP_KERNEL);
+			if (!pcam_inst->buf_offset[i]) {
+				pr_err("%s out of memory ", __func__);
+				for (j = i-1 ; j >= 0; j--)
+					kfree(pcam_inst->buf_offset[j]);
+				kfree(pcam_inst->buf_offset);
+				pcam_inst->buf_offset = NULL;
+				return -ENOMEM;
+			}
+		}
+	}
+	pcam_inst->buf_count = pb->count;
+	D("%s inst %p, buf count %d ", __func__,
+		pcam_inst, pcam_inst->buf_count);
+	return rc;
+}
+
+static int msm_mctl_v4l2_querybuf(struct file *f, void *pctx,
+					struct v4l2_buffer *pb)
+{
+	/* get the video device */
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	pcam_inst = container_of(f->private_data,
+		struct msm_cam_v4l2_dev_inst, eventHandle);
+
+	D("%s\n", __func__);
+	WARN_ON(pctx != f->private_data);
+	return vb2_querybuf(&pcam_inst->vid_bufq, pb);
+}
+
+static int msm_mctl_v4l2_qbuf(struct file *f, void *pctx,
+					struct v4l2_buffer *pb)
+{
+	int rc = 0, i = 0;
+	/* get the camera device */
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	pcam_inst = container_of(f->private_data,
+		struct msm_cam_v4l2_dev_inst, eventHandle);
+
+	D("%s Inst = %p\n", __func__, pcam_inst);
+	WARN_ON(pctx != f->private_data);
+
+	if (!pcam_inst->buf_offset) {
+		pr_err("%s Buffer is already released. Returning. ", __func__);
+		return -EINVAL;
+	}
+
+	if (pb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		/* Reject the buffer if planes array was not allocated */
+		if (pb->m.planes == NULL) {
+			pr_err("%s Planes array is null ", __func__);
+			return -EINVAL;
+		}
+		for (i = 0; i < pcam_inst->plane_info.num_planes; i++) {
+			D("%s stored offsets for plane %d as"
+				"addr offset %d, data offset %d",
+				__func__, i, pb->m.planes[i].reserved[0],
+				pb->m.planes[i].data_offset);
+			pcam_inst->buf_offset[pb->index][i].data_offset =
+				pb->m.planes[i].data_offset;
+			pcam_inst->buf_offset[pb->index][i].addr_offset =
+				pb->m.planes[i].reserved[0];
+			pcam_inst->plane_info.plane[i].offset = 0;
+			D("%s, len %d user[%d] %p buf_len %d\n",
+				__func__, pb->length, i,
+				(void *)pb->m.planes[i].m.userptr,
+				pb->m.planes[i].length);
+		}
+	} else {
+		D("%s stored reserved info %d", __func__, pb->reserved);
+		pcam_inst->buf_offset[pb->index][0].addr_offset = pb->reserved;
+	}
+
+	rc = vb2_qbuf(&pcam_inst->vid_bufq, pb);
+	D("%s, videobuf_qbuf returns %d\n", __func__, rc);
+
+	return rc;
+}
+
+static int msm_mctl_v4l2_dqbuf(struct file *f, void *pctx,
+					struct v4l2_buffer *pb)
+{
+	int rc = 0;
+	/* get the camera device */
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	pcam_inst = container_of(f->private_data,
+		struct msm_cam_v4l2_dev_inst, eventHandle);
+
+	D("%s\n", __func__);
+	WARN_ON(pctx != f->private_data);
+
+	rc = vb2_dqbuf(&pcam_inst->vid_bufq, pb,  f->f_flags & O_NONBLOCK);
+	D("%s, videobuf_dqbuf returns %d\n", __func__, rc);
+
+	return rc;
+}
+
+static int msm_mctl_v4l2_streamon(struct file *f, void *pctx,
+					enum v4l2_buf_type buf_type)
+{
+	int rc = 0;
+	/* get the camera device */
+	struct msm_cam_v4l2_device *pcam  = video_drvdata(f);
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	pcam_inst = container_of(f->private_data,
+		struct msm_cam_v4l2_dev_inst, eventHandle);
+
+	D("%s Inst %p\n", __func__, pcam_inst);
+	WARN_ON(pctx != f->private_data);
+
+	if ((buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) &&
+		(buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) {
+		pr_err("%s Invalid buffer type ", __func__);
+		return -EINVAL;
+	}
+
+	D("%s Calling videobuf_streamon", __func__);
+	/* if HW streaming on is successful, start buffer streaming */
+	rc = vb2_streamon(&pcam_inst->vid_bufq, buf_type);
+	D("%s, videobuf_streamon returns %d\n", __func__, rc);
+
+	mutex_lock(&pcam->mctl_node.dev_lock);
+	/* turn HW (VFE/sensor) streaming */
+	pcam_inst->streamon = 1;
+	mutex_unlock(&pcam->mctl_node.dev_lock);
+	D("%s rc = %d\n", __func__, rc);
+	return rc;
+}
+
+static int msm_mctl_v4l2_streamoff(struct file *f, void *pctx,
+					enum v4l2_buf_type buf_type)
+{
+	int rc = 0;
+	/* get the camera device */
+	struct msm_cam_v4l2_device *pcam  = video_drvdata(f);
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	pcam_inst = container_of(f->private_data,
+		struct msm_cam_v4l2_dev_inst, eventHandle);
+
+	D("%s Inst %p\n", __func__, pcam_inst);
+	WARN_ON(pctx != f->private_data);
+
+	if ((buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) &&
+		(buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) {
+		pr_err("%s Invalid buffer type ", __func__);
+		return -EINVAL;
+	}
+
+	/* first turn of HW (VFE/sensor) streaming so that buffers are
+		not in use when we free the buffers */
+	mutex_lock(&pcam->mctl_node.dev_lock);
+	pcam_inst->streamon = 0;
+	mutex_unlock(&pcam->mctl_node.dev_lock);
+	if (rc < 0)
+		pr_err("%s: hw failed to stop streaming\n", __func__);
+
+	/* stop buffer streaming */
+	rc = vb2_streamoff(&pcam_inst->vid_bufq, buf_type);
+	D("%s, videobuf_streamoff returns %d\n", __func__, rc);
+	return rc;
+}
+
+static int msm_mctl_v4l2_enum_fmt_cap(struct file *f, void *pctx,
+					struct v4l2_fmtdesc *pfmtdesc)
+{
+	/* get the video device */
+	struct msm_cam_v4l2_device *pcam  = video_drvdata(f);
+	const struct msm_isp_color_fmt *isp_fmt;
+
+	D("%s\n", __func__);
+	WARN_ON(pctx != f->private_data);
+	if ((pfmtdesc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) &&
+		(pfmtdesc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE))
+		return -EINVAL;
+
+	if (pfmtdesc->index >= pcam->num_fmts)
+		return -EINVAL;
+
+	isp_fmt = &pcam->usr_fmts[pfmtdesc->index];
+
+	if (isp_fmt->name)
+		strlcpy(pfmtdesc->description, isp_fmt->name,
+						sizeof(pfmtdesc->description));
+
+	pfmtdesc->pixelformat = isp_fmt->fourcc;
+
+	D("%s: [%d] 0x%x, %s\n", __func__, pfmtdesc->index,
+		isp_fmt->fourcc, isp_fmt->name);
+	return 0;
+}
+
+static int msm_mctl_v4l2_g_fmt_cap(struct file *f,
+		void *pctx, struct v4l2_format *pfmt)
+{
+	int rc = 0;
+
+	D("%s\n", __func__);
+	WARN_ON(pctx != f->private_data);
+
+	if (pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	return rc;
+}
+
+static int msm_mctl_v4l2_g_fmt_cap_mplane(struct file *f,
+		void *pctx, struct v4l2_format *pfmt)
+{
+	int rc = 0;
+
+	D("%s\n", __func__);
+	WARN_ON(pctx != f->private_data);
+
+	if (pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		return -EINVAL;
+
+	return rc;
+}
+
+/* This function will readjust the format parameters based in HW
+  capabilities. Called by s_fmt_cap
+*/
+static int msm_mctl_v4l2_try_fmt_cap(struct file *f, void *pctx,
+					struct v4l2_format *pfmt)
+{
+	int rc = 0;
+
+	D("%s\n", __func__);
+	WARN_ON(pctx != f->private_data);
+
+	return rc;
+}
+
+static int msm_mctl_v4l2_try_fmt_cap_mplane(struct file *f, void *pctx,
+					struct v4l2_format *pfmt)
+{
+	int rc = 0;
+
+	D("%s\n", __func__);
+	WARN_ON(pctx != f->private_data);
+
+	return rc;
+}
+
+/* This function will reconfig the v4l2 driver and HW device, it should be
+   called after the streaming is stopped.
+*/
+static int msm_mctl_v4l2_s_fmt_cap(struct file *f, void *pctx,
+					struct v4l2_format *pfmt)
+{
+	int rc = 0;
+	/* get the video device */
+	struct msm_cam_v4l2_device *pcam  = video_drvdata(f);
+	struct msm_cam_media_controller *pmctl;
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	pcam_inst = container_of(f->private_data,
+		struct msm_cam_v4l2_dev_inst, eventHandle);
+
+	D("%s\n", __func__);
+	D("%s, inst=0x%x,idx=%d,priv = 0x%p\n",
+		__func__, (u32)pcam_inst, pcam_inst->my_index,
+		(void *)pfmt->fmt.pix.priv);
+	WARN_ON(pctx != f->private_data);
+
+	pmctl = msm_camera_get_mctl(pcam->mctl_handle);
+	if (!pcam_inst->vbqueue_initialized) {
+		pmctl->mctl_vbqueue_init(pcam_inst, &pcam_inst->vid_bufq,
+					V4L2_BUF_TYPE_VIDEO_CAPTURE);
+		pcam_inst->vbqueue_initialized = 1;
+	}
+
+	return rc;
+}
+
+static int msm_mctl_v4l2_s_fmt_cap_mplane(struct file *f, void *pctx,
+				struct v4l2_format *pfmt)
+{
+	int rc = 0, i;
+	struct msm_cam_v4l2_device *pcam = video_drvdata(f);
+	struct msm_cam_media_controller *pmctl;
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	pcam_inst = container_of(f->private_data,
+			struct msm_cam_v4l2_dev_inst, eventHandle);
+
+	D("%s Inst %p vbqueue %d\n", __func__,
+		pcam_inst, pcam_inst->vbqueue_initialized);
+	WARN_ON(pctx != f->private_data);
+
+	pmctl = msm_camera_get_mctl(pcam->mctl_handle);
+	if (!pcam_inst->vbqueue_initialized) {
+		pmctl->mctl_vbqueue_init(pcam_inst, &pcam_inst->vid_bufq,
+					V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+		pcam_inst->vbqueue_initialized = 1;
+	}
+	for (i = 0; i < pcam->num_fmts; i++)
+		if (pcam->usr_fmts[i].fourcc == pfmt->fmt.pix_mp.pixelformat)
+			break;
+	if (i == pcam->num_fmts) {
+		pr_err("%s: User requested pixelformat %x not supported\n",
+			__func__, pfmt->fmt.pix_mp.pixelformat);
+		return -EINVAL;
+	}
+	pcam_inst->vid_fmt = *pfmt;
+	pcam_inst->sensor_pxlcode =
+		pcam->usr_fmts[i].pxlcode;
+	D("%s: inst=%p, width=%d, heigth=%d\n",
+		__func__, pcam_inst,
+		pcam_inst->vid_fmt.fmt.pix_mp.width,
+		pcam_inst->vid_fmt.fmt.pix_mp.height);
+	return rc;
+}
+static int msm_mctl_v4l2_g_jpegcomp(struct file *f, void *pctx,
+				struct v4l2_jpegcompression *pcomp)
+{
+	int rc = -EINVAL;
+
+	D("%s\n", __func__);
+	WARN_ON(pctx != f->private_data);
+
+	return rc;
+}
+
+static int msm_mctl_v4l2_s_jpegcomp(struct file *f, void *pctx,
+				struct v4l2_jpegcompression *pcomp)
+{
+	int rc = -EINVAL;
+
+	D("%s\n", __func__);
+	WARN_ON(pctx != f->private_data);
+
+	return rc;
+}
+
+
+static int msm_mctl_v4l2_g_crop(struct file *f, void *pctx,
+					struct v4l2_crop *crop)
+{
+	int rc = -EINVAL;
+
+	D("%s\n", __func__);
+	WARN_ON(pctx != f->private_data);
+
+	return rc;
+}
+
+static int msm_mctl_v4l2_s_crop(struct file *f, void *pctx,
+					struct v4l2_crop *a)
+{
+	int rc = -EINVAL;
+
+	D("%s\n", __func__);
+	WARN_ON(pctx != f->private_data);
+
+	return rc;
+}
+
+/* Stream type-dependent parameter ioctls */
+static int msm_mctl_v4l2_g_parm(struct file *f, void *pctx,
+				struct v4l2_streamparm *a)
+{
+	int rc = -EINVAL;
+	return rc;
+}
+
+static int msm_mctl_vidbuf_get_path(u32 extendedmode)
+{
+	switch (extendedmode) {
+	case MSM_V4L2_EXT_CAPTURE_MODE_THUMBNAIL:
+		return OUTPUT_TYPE_T;
+	case MSM_V4L2_EXT_CAPTURE_MODE_MAIN:
+		return OUTPUT_TYPE_S;
+	case MSM_V4L2_EXT_CAPTURE_MODE_VIDEO:
+		return OUTPUT_TYPE_V;
+	case MSM_V4L2_EXT_CAPTURE_MODE_DEFAULT:
+	case MSM_V4L2_EXT_CAPTURE_MODE_PREVIEW:
+	default:
+		return OUTPUT_TYPE_P;
+	}
+}
+
+static int msm_mctl_v4l2_s_parm(struct file *f, void *pctx,
+				struct v4l2_streamparm *a)
+{
+	int rc = 0;
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	pcam_inst = container_of(f->private_data,
+		struct msm_cam_v4l2_dev_inst, eventHandle);
+	pcam_inst->image_mode = a->parm.capture.extendedmode;
+	pcam_inst->pcam->mctl_node.dev_inst_map[pcam_inst->image_mode] =
+		pcam_inst;
+	pcam_inst->path = msm_mctl_vidbuf_get_path(pcam_inst->image_mode);
+	D("%s path=%d, image mode = %d rc=%d\n", __func__,
+		pcam_inst->path, pcam_inst->image_mode, rc);
+	return rc;
+}
+
+static int msm_mctl_v4l2_subscribe_event(struct v4l2_fh *fh,
+			struct v4l2_event_subscription *sub)
+{
+	int rc = 0;
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	pcam_inst =
+		(struct msm_cam_v4l2_dev_inst *)container_of(fh,
+		struct msm_cam_v4l2_dev_inst, eventHandle);
+
+	D("%s:fh = 0x%x, type = 0x%x\n", __func__, (u32)fh, sub->type);
+
+	if (sub->type == V4L2_EVENT_ALL)
+		sub->type = V4L2_EVENT_PRIVATE_START+MSM_CAM_APP_NOTIFY_EVENT;
+	rc = v4l2_event_subscribe(fh, sub, 30);
+	if (rc < 0)
+		pr_err("%s: failed for evtType = 0x%x, rc = %d\n",
+						__func__, sub->type, rc);
+	return rc;
+}
+
+static int msm_mctl_v4l2_unsubscribe_event(struct v4l2_fh *fh,
+			struct v4l2_event_subscription *sub)
+{
+	int rc = 0;
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	pcam_inst =
+		(struct msm_cam_v4l2_dev_inst *)container_of(fh,
+		struct msm_cam_v4l2_dev_inst, eventHandle);
+
+	D("%s: fh = 0x%x\n", __func__, (u32)fh);
+
+	rc = v4l2_event_unsubscribe(fh, sub);
+	D("%s: rc = %d\n", __func__, rc);
+	return rc;
+}
+
+/* mctl node v4l2_ioctl_ops */
+static const struct v4l2_ioctl_ops g_msm_mctl_ioctl_ops = {
+	.vidioc_querycap = msm_mctl_v4l2_querycap,
+
+	.vidioc_s_crop = msm_mctl_v4l2_s_crop,
+	.vidioc_g_crop = msm_mctl_v4l2_g_crop,
+
+	.vidioc_queryctrl = msm_mctl_v4l2_queryctrl,
+	.vidioc_g_ctrl = msm_mctl_v4l2_g_ctrl,
+	.vidioc_s_ctrl = msm_mctl_v4l2_s_ctrl,
+
+	.vidioc_reqbufs = msm_mctl_v4l2_reqbufs,
+	.vidioc_querybuf = msm_mctl_v4l2_querybuf,
+	.vidioc_qbuf = msm_mctl_v4l2_qbuf,
+	.vidioc_dqbuf = msm_mctl_v4l2_dqbuf,
+
+	.vidioc_streamon = msm_mctl_v4l2_streamon,
+	.vidioc_streamoff = msm_mctl_v4l2_streamoff,
+
+	/* format ioctls */
+	.vidioc_enum_fmt_vid_cap = msm_mctl_v4l2_enum_fmt_cap,
+	.vidioc_enum_fmt_vid_cap_mplane = msm_mctl_v4l2_enum_fmt_cap,
+	.vidioc_try_fmt_vid_cap = msm_mctl_v4l2_try_fmt_cap,
+	.vidioc_try_fmt_vid_cap_mplane = msm_mctl_v4l2_try_fmt_cap_mplane,
+	.vidioc_g_fmt_vid_cap = msm_mctl_v4l2_g_fmt_cap,
+	.vidioc_g_fmt_vid_cap_mplane = msm_mctl_v4l2_g_fmt_cap_mplane,
+	.vidioc_s_fmt_vid_cap = msm_mctl_v4l2_s_fmt_cap,
+	.vidioc_s_fmt_vid_cap_mplane = msm_mctl_v4l2_s_fmt_cap_mplane,
+
+	.vidioc_g_jpegcomp = msm_mctl_v4l2_g_jpegcomp,
+	.vidioc_s_jpegcomp = msm_mctl_v4l2_s_jpegcomp,
+
+	/* Stream type-dependent parameter ioctls */
+	.vidioc_g_parm =  msm_mctl_v4l2_g_parm,
+	.vidioc_s_parm =  msm_mctl_v4l2_s_parm,
+
+	/* event subscribe/unsubscribe */
+	.vidioc_subscribe_event = msm_mctl_v4l2_subscribe_event,
+	.vidioc_unsubscribe_event = msm_mctl_v4l2_unsubscribe_event,
+};
+
+int msm_setup_mctl_node(struct msm_cam_v4l2_device *pcam)
+{
+	int rc = -EINVAL;
+	struct video_device *pvdev = NULL;
+	struct i2c_client *client = v4l2_get_subdevdata(pcam->sensor_sdev);
+
+	D("%s\n", __func__);
+
+	/* first register the v4l2 device */
+	pcam->mctl_node.v4l2_dev.dev = &client->dev;
+	rc = v4l2_device_register(pcam->mctl_node.v4l2_dev.dev,
+				&pcam->mctl_node.v4l2_dev);
+	if (rc < 0)
+		return -EINVAL;
+	/*	else
+			pcam->v4l2_dev.notify = msm_cam_v4l2_subdev_notify; */
+
+	/* now setup video device */
+	pvdev = video_device_alloc();
+	if (pvdev == NULL) {
+		pr_err("%s: video_device_alloc failed\n", __func__);
+		return rc;
+	}
+
+	/* init video device's driver interface */
+	D("sensor name = %s, sizeof(pvdev->name)=%d\n",
+			pcam->sensor_sdev->name, sizeof(pvdev->name));
+
+	/* device info - strlcpy is safer than strncpy but
+	   only if architecture supports*/
+	strlcpy(pvdev->name, pcam->sensor_sdev->name,
+			sizeof(pvdev->name));
+
+	pvdev->release   = video_device_release;
+	pvdev->fops	  = &g_msm_mctl_fops;
+	pvdev->ioctl_ops  = &g_msm_mctl_ioctl_ops;
+	pvdev->minor	  = -1;
+	pvdev->vfl_type   = 1;
+
+	/* register v4l2 video device to kernel as /dev/videoXX */
+	D("%s video_register_device\n", __func__);
+	rc = video_register_device(pvdev,
+			VFL_TYPE_GRABBER,
+			-1);
+	if (rc) {
+		pr_err("%s: video_register_device failed\n", __func__);
+		goto reg_fail;
+	}
+	D("%s: video device registered as /dev/video%d\n",
+			__func__, pvdev->num);
+
+	/* connect pcam and mctl video dev to each other */
+	pcam->mctl_node.pvdev	= pvdev;
+	video_set_drvdata(pcam->mctl_node.pvdev, pcam);
+
+	return rc ;
+
+reg_fail:
+	video_device_release(pvdev);
+	v4l2_device_unregister(&pcam->mctl_node.v4l2_dev);
+	pcam->mctl_node.v4l2_dev.dev = NULL;
+	return rc;
+}
diff --git a/drivers/media/video/msm/msm_mctl_buf.c b/drivers/media/video/msm/msm_mctl_buf.c
new file mode 100644
index 0000000..88e89a3
--- /dev/null
+++ b/drivers/media/video/msm/msm_mctl_buf.c
@@ -0,0 +1,842 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/ioctl.h>
+#include <linux/spinlock.h>
+#include <linux/videodev2.h>
+#include <linux/vmalloc.h>
+
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+
+#include <linux/android_pmem.h>
+
+#include "msm.h"
+#include "msm_ispif.h"
+
+#ifdef CONFIG_MSM_CAMERA_DEBUG
+#define D(fmt, args...) pr_debug("msm_mctl_buf: " fmt, ##args)
+#else
+#define D(fmt, args...) do {} while (0)
+#endif
+
+static int msm_vb2_ops_queue_setup(struct vb2_queue *vq,
+				const struct v4l2_format *fmt,
+				unsigned int *num_buffers,
+				unsigned int *num_planes,
+				unsigned int sizes[],
+				void *alloc_ctxs[])
+{
+	/* get the video device */
+	struct msm_cam_v4l2_dev_inst *pcam_inst = vb2_get_drv_priv(vq);
+	struct msm_cam_v4l2_device *pcam = pcam_inst->pcam;
+	int i;
+
+	D("%s\n", __func__);
+	if (!pcam || !(*num_buffers)) {
+		pr_err("%s error : invalid input\n", __func__);
+		return -EINVAL;
+	}
+
+	*num_planes = pcam_inst->plane_info.num_planes;
+	for (i = 0; i < pcam_inst->vid_fmt.fmt.pix_mp.num_planes; i++) {
+		sizes[i] = pcam_inst->plane_info.plane[i].size;
+		D("%s Inst %p : Plane %d Offset = %d Size = %ld "
+			"Aligned Size = %d\n", __func__, pcam_inst, i,
+			pcam_inst->plane_info.plane[i].offset,
+			pcam_inst->plane_info.plane[i].size, sizes[i]);
+	}
+	return 0;
+}
+
+static void msm_vb2_ops_wait_prepare(struct vb2_queue *q)
+{
+	/* we use polling so do not use this fn now */
+}
+static void msm_vb2_ops_wait_finish(struct vb2_queue *q)
+{
+	/* we use polling so do not use this fn now */
+}
+
+static int msm_vb2_ops_buf_init(struct vb2_buffer *vb)
+{
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	struct msm_cam_v4l2_device *pcam;
+	struct msm_cam_media_controller *pmctl;
+	struct videobuf2_contig_pmem *mem;
+	struct vb2_queue	*vq;
+	uint32_t buf_idx;
+	struct msm_frame_buffer *buf;
+	int rc = 0, i;
+	enum videobuf2_buffer_type buf_type;
+	struct videobuf2_msm_offset offset;
+	vq = vb->vb2_queue;
+	pcam_inst = vb2_get_drv_priv(vq);
+	pcam = pcam_inst->pcam;
+	D("%s\n", __func__);
+	D("%s, inst=0x%x,idx=%d, width = %d\n", __func__,
+		(u32)pcam_inst, pcam_inst->my_index,
+		pcam_inst->vid_fmt.fmt.pix.width);
+	D("%s, inst=0x%x,idx=%d, height = %d\n", __func__,
+		(u32)pcam_inst, pcam_inst->my_index,
+		pcam_inst->vid_fmt.fmt.pix.height);
+
+	buf = container_of(vb, struct msm_frame_buffer, vidbuf);
+	if (buf->state == MSM_BUFFER_STATE_INITIALIZED)
+		return rc;
+
+	if (pcam_inst->plane_info.buffer_type ==
+		V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		buf_type = VIDEOBUF2_MULTIPLE_PLANES;
+	else if (pcam_inst->plane_info.buffer_type ==
+		V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		buf_type = VIDEOBUF2_SINGLE_PLANE;
+	else
+		return -EINVAL;
+
+	if (buf_type == VIDEOBUF2_SINGLE_PLANE) {
+		offset.sp_off.y_off = pcam_inst->plane_info.sp_y_offset;
+		offset.sp_off.cbcr_off =
+			pcam_inst->plane_info.plane[0].offset;
+	}
+	buf_idx = vb->v4l2_buf.index;
+	pmctl = msm_camera_get_mctl(pcam->mctl_handle);
+	for (i = 0; i < vb->num_planes; i++) {
+		mem = vb2_plane_cookie(vb, i);
+		if (buf_type == VIDEOBUF2_MULTIPLE_PLANES)
+			offset.data_offset =
+				pcam_inst->plane_info.plane[i].offset;
+
+		if (vb->v4l2_buf.memory == V4L2_MEMORY_USERPTR)
+			rc = videobuf2_pmem_contig_user_get(mem, &offset,
+				buf_type,
+				pcam_inst->buf_offset[buf_idx][i].addr_offset,
+				pcam_inst->path, pmctl->client);
+		else
+			rc = videobuf2_pmem_contig_mmap_get(mem, &offset,
+				buf_type, pcam_inst->path);
+		if (rc < 0) {
+			pr_err("%s error initializing buffer ",
+				__func__);
+			return rc;
+		}
+	}
+	buf->state = MSM_BUFFER_STATE_INITIALIZED;
+	return rc;
+}
+
+static int msm_vb2_ops_buf_prepare(struct vb2_buffer *vb)
+{
+	int i, rc = 0;
+	uint32_t len;
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	struct msm_cam_v4l2_device *pcam;
+	struct msm_frame_buffer *buf;
+	struct vb2_queue	*vq = vb->vb2_queue;
+
+	D("%s\n", __func__);
+	if (!vb || !vq) {
+		pr_err("%s error : input is NULL\n", __func__);
+		return -EINVAL;
+	}
+	pcam_inst = vb2_get_drv_priv(vq);
+	pcam = pcam_inst->pcam;
+	buf = container_of(vb, struct msm_frame_buffer, vidbuf);
+
+	if (!pcam || !buf) {
+		pr_err("%s error : pointer is NULL\n", __func__);
+		return -EINVAL;
+	}
+	/* by this time vid_fmt should be already set.
+	 * return error if it is not. */
+	if ((pcam_inst->vid_fmt.fmt.pix.width == 0) ||
+		(pcam_inst->vid_fmt.fmt.pix.height == 0)) {
+		pr_err("%s error : pcam vid_fmt is not set\n", __func__);
+		return -EINVAL;
+	}
+	/* prefill in the byteused field */
+	for (i = 0; i < vb->num_planes; i++) {
+		len = vb2_plane_size(vb, i);
+		vb2_set_plane_payload(vb, i, len);
+	}
+	buf->state = MSM_BUFFER_STATE_PREPARED;
+	return rc;
+}
+
+static int msm_vb2_ops_buf_finish(struct vb2_buffer *vb)
+{
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	struct msm_cam_v4l2_device *pcam;
+	struct msm_frame_buffer *buf;
+
+	pcam_inst = vb2_get_drv_priv(vb->vb2_queue);
+	pcam = pcam_inst->pcam;
+	buf = container_of(vb, struct msm_frame_buffer, vidbuf);
+	buf->state = MSM_BUFFER_STATE_DEQUEUED;
+	D("%s: inst=0x%x, buf=0x%x, idx=%d\n", __func__,
+	(uint32_t)pcam_inst, (uint32_t)buf, vb->v4l2_buf.index);
+	return 0;
+}
+
+static void msm_vb2_ops_buf_cleanup(struct vb2_buffer *vb)
+{
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	struct msm_cam_media_controller *pmctl;
+	struct msm_cam_v4l2_device *pcam;
+	struct videobuf2_contig_pmem *mem;
+	struct msm_frame_buffer *buf, *tmp;
+	uint32_t i, vb_phyaddr = 0, buf_phyaddr = 0;
+	unsigned long flags = 0;
+
+	pcam_inst = vb2_get_drv_priv(vb->vb2_queue);
+	pcam = pcam_inst->pcam;
+	buf = container_of(vb, struct msm_frame_buffer, vidbuf);
+
+	if (pcam_inst->vid_fmt.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		for (i = 0; i < vb->num_planes; i++) {
+			mem = vb2_plane_cookie(vb, i);
+			if (!mem) {
+				D("%s Inst %p memory already freed up. return",
+					__func__, pcam_inst);
+				return;
+			}
+			D("%s: inst=%p, buf=0x%x, idx=%d plane id = %d\n",
+				__func__, pcam_inst,
+				(uint32_t)buf, vb->v4l2_buf.index, i);
+
+			spin_lock_irqsave(&pcam_inst->vq_irqlock, flags);
+			list_for_each_entry_safe(buf, tmp,
+					&pcam_inst->free_vq, list) {
+				if (&buf->vidbuf == vb) {
+					list_del_init(&buf->list);
+					break;
+				}
+			}
+			spin_unlock_irqrestore(&pcam_inst->vq_irqlock, flags);
+		}
+	} else {
+		mem = vb2_plane_cookie(vb, 0);
+		if (!mem)
+			return;
+		D("%s: inst=0x%x, buf=0x%x, idx=%d\n", __func__,
+		(uint32_t)pcam_inst, (uint32_t)buf, vb->v4l2_buf.index);
+		vb_phyaddr = (unsigned long) videobuf2_to_pmem_contig(vb, 0);
+		spin_lock_irqsave(&pcam_inst->vq_irqlock, flags);
+		list_for_each_entry_safe(buf, tmp,
+				&pcam_inst->free_vq, list) {
+			buf_phyaddr = (unsigned long)
+				videobuf2_to_pmem_contig(&buf->vidbuf, 0);
+			D("%s vb_idx=%d,vb_paddr=0x%x,phyaddr=0x%x\n",
+				__func__, buf->vidbuf.v4l2_buf.index,
+				buf_phyaddr, vb_phyaddr);
+			if (vb_phyaddr == buf_phyaddr) {
+				list_del_init(&buf->list);
+				break;
+			}
+		}
+		spin_unlock_irqrestore(&pcam_inst->vq_irqlock, flags);
+	}
+	pmctl = msm_camera_get_mctl(pcam->mctl_handle);
+	for (i = 0; i < vb->num_planes; i++) {
+		mem = vb2_plane_cookie(vb, i);
+		videobuf2_pmem_contig_user_put(mem, pmctl->client);
+	}
+	buf->state = MSM_BUFFER_STATE_UNUSED;
+}
+
+static int msm_vb2_ops_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+	return 0;
+}
+
+static int msm_vb2_ops_stop_streaming(struct vb2_queue *q)
+{
+	return 0;
+}
+
+static void msm_vb2_ops_buf_queue(struct vb2_buffer *vb)
+{
+	struct msm_cam_v4l2_dev_inst *pcam_inst = NULL;
+	struct msm_cam_v4l2_device *pcam = NULL;
+	unsigned long flags = 0;
+	struct vb2_queue *vq = vb->vb2_queue;
+	struct msm_frame_buffer *buf;
+	D("%s\n", __func__);
+	if (!vb || !vq) {
+		pr_err("%s error : input is NULL\n", __func__);
+		return ;
+	}
+	pcam_inst = vb2_get_drv_priv(vq);
+	pcam = pcam_inst->pcam;
+	D("%s pcam_inst=%p,(vb=0x%p),idx=%d,len=%d\n",
+		__func__, pcam_inst,
+	vb, vb->v4l2_buf.index, vb->v4l2_buf.length);
+	D("%s pcam_inst=%p, idx=%d\n", __func__, pcam_inst,
+		vb->v4l2_buf.index);
+	buf = container_of(vb, struct msm_frame_buffer, vidbuf);
+	spin_lock_irqsave(&pcam_inst->vq_irqlock, flags);
+	/* we are returning a buffer to the queue */
+	list_add_tail(&buf->list, &pcam_inst->free_vq);
+	spin_unlock_irqrestore(&pcam_inst->vq_irqlock, flags);
+	buf->state = MSM_BUFFER_STATE_QUEUED;
+}
+
+static struct vb2_ops msm_vb2_ops = {
+	.queue_setup = msm_vb2_ops_queue_setup,
+	.wait_prepare = msm_vb2_ops_wait_prepare,
+	.wait_finish = msm_vb2_ops_wait_finish,
+	.buf_init = msm_vb2_ops_buf_init,
+	.buf_prepare = msm_vb2_ops_buf_prepare,
+	.buf_finish = msm_vb2_ops_buf_finish,
+	.buf_cleanup = msm_vb2_ops_buf_cleanup,
+	.start_streaming = msm_vb2_ops_start_streaming,
+	.stop_streaming = msm_vb2_ops_stop_streaming,
+	.buf_queue = msm_vb2_ops_buf_queue,
+};
+
+
+/* prepare a video buffer queue for a vl42 device*/
+static int msm_vbqueue_init(struct msm_cam_v4l2_dev_inst *pcam_inst,
+			struct vb2_queue *q, enum v4l2_buf_type type)
+{
+	if (!q) {
+		pr_err("%s error : input is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	spin_lock_init(&pcam_inst->vq_irqlock);
+	INIT_LIST_HEAD(&pcam_inst->free_vq);
+	videobuf2_queue_pmem_contig_init(q, type,
+					&msm_vb2_ops,
+					sizeof(struct msm_frame_buffer),
+					(void *)pcam_inst);
+	return 0;
+}
+
+int msm_mctl_img_mode_to_inst_index(struct msm_cam_media_controller *pmctl,
+					int image_mode, int node_type)
+{
+	if ((image_mode >= 0) && node_type &&
+		pmctl->pcam_ptr->mctl_node.dev_inst_map[image_mode])
+		return pmctl->pcam_ptr->
+				mctl_node.dev_inst_map[image_mode]->my_index;
+	else if ((image_mode >= 0) &&
+		pmctl->pcam_ptr->dev_inst_map[image_mode])
+		return	pmctl->pcam_ptr->
+				dev_inst_map[image_mode]->my_index;
+	else
+		return -EINVAL;
+}
+
+void msm_mctl_gettimeofday(struct timeval *tv)
+{
+	struct timespec ts;
+
+	BUG_ON(!tv);
+
+	ktime_get_ts(&ts);
+	tv->tv_sec = ts.tv_sec;
+	tv->tv_usec = ts.tv_nsec/1000;
+}
+
+struct msm_frame_buffer *msm_mctl_buf_find(
+	struct msm_cam_media_controller *pmctl,
+	struct msm_cam_v4l2_dev_inst *pcam_inst, int del_buf,
+	int image_mode, struct msm_free_buf *fbuf)
+{
+	struct msm_frame_buffer *buf = NULL, *tmp;
+	uint32_t buf_phyaddr = 0;
+	unsigned long flags = 0;
+	uint32_t buf_idx, offset = 0;
+	struct videobuf2_contig_pmem *mem;
+
+	/* we actually need a list, not a queue */
+	spin_lock_irqsave(&pcam_inst->vq_irqlock, flags);
+	list_for_each_entry_safe(buf, tmp,
+			&pcam_inst->free_vq, list) {
+		buf_idx = buf->vidbuf.v4l2_buf.index;
+		mem = vb2_plane_cookie(&buf->vidbuf, 0);
+		if (mem->buffer_type ==	VIDEOBUF2_MULTIPLE_PLANES)
+			offset = mem->offset.data_offset +
+				pcam_inst->buf_offset[buf_idx][0].data_offset;
+		else
+			offset = mem->offset.sp_off.y_off;
+		buf_phyaddr = (unsigned long)
+				videobuf2_to_pmem_contig(&buf->vidbuf, 0) +
+				offset;
+		D("%s vb_idx=%d,vb_paddr=0x%x ch0=0x%x\n",
+			__func__, buf->vidbuf.v4l2_buf.index,
+			buf_phyaddr, fbuf->ch_paddr[0]);
+		if (fbuf->ch_paddr[0] == buf_phyaddr) {
+			if (del_buf)
+				list_del_init(&buf->list);
+			spin_unlock_irqrestore(&pcam_inst->vq_irqlock,
+								flags);
+			buf->state = MSM_BUFFER_STATE_RESERVED;
+			return buf;
+		}
+	}
+	spin_unlock_irqrestore(&pcam_inst->vq_irqlock, flags);
+	return NULL;
+}
+
+int msm_mctl_buf_done_proc(
+		struct msm_cam_media_controller *pmctl,
+		struct msm_cam_v4l2_dev_inst *pcam_inst,
+		int image_mode, struct msm_free_buf *fbuf,
+		uint32_t *frame_id, int gen_timestamp)
+{
+	struct msm_frame_buffer *buf = NULL;
+	int del_buf = 1;
+
+	buf = msm_mctl_buf_find(pmctl, pcam_inst, del_buf,
+					image_mode, fbuf);
+	if (!buf) {
+		pr_err("%s: buf=0x%x not found\n",
+			__func__, fbuf->ch_paddr[0]);
+		return -EINVAL;
+	}
+	if (gen_timestamp) {
+		if (frame_id)
+			buf->vidbuf.v4l2_buf.sequence = *frame_id;
+		msm_mctl_gettimeofday(
+			&buf->vidbuf.v4l2_buf.timestamp);
+	}
+	vb2_buffer_done(&buf->vidbuf, VB2_BUF_STATE_DONE);
+	return 0;
+}
+
+
+int msm_mctl_buf_done(struct msm_cam_media_controller *p_mctl,
+			int image_mode, struct msm_free_buf *fbuf,
+			uint32_t frame_id)
+{
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	int idx, rc;
+	int pp_divert_type = 0, pp_type = 0;
+
+	msm_mctl_check_pp(p_mctl, image_mode, &pp_divert_type, &pp_type);
+	D("%s: pp_type=%d, pp_divert_type = %d, frame_id = 0x%x image_mode %d",
+		__func__, pp_type, pp_divert_type, frame_id, image_mode);
+	if (pp_type || pp_divert_type)
+		rc = msm_mctl_do_pp_divert(p_mctl,
+		image_mode, fbuf, frame_id, pp_type);
+	else {
+		idx = msm_mctl_img_mode_to_inst_index(
+				p_mctl, image_mode, 0);
+		if (idx < 0) {
+			/* check mctl node */
+			if ((image_mode >= 0) &&
+				p_mctl->pcam_ptr->mctl_node.
+					dev_inst_map[image_mode]) {
+				int index = p_mctl->pcam_ptr->mctl_node.
+					   dev_inst_map[image_mode]->my_index;
+				pcam_inst = p_mctl->pcam_ptr->mctl_node.
+					dev_inst[index];
+				D("%s: Mctl node index %d inst %p",
+					__func__, index, pcam_inst);
+				rc = msm_mctl_buf_done_proc(p_mctl, pcam_inst,
+					image_mode, fbuf,
+					&frame_id, 1);
+				D("%s mctl node buf done %d\n", __func__, 0);
+				return -EINVAL;
+			} else {
+			  pr_err("%s Invalid instance, dropping buffer\n",
+				  __func__);
+			  return idx;
+			}
+		}
+		pcam_inst = p_mctl->pcam_ptr->dev_inst[idx];
+		rc = msm_mctl_buf_done_proc(p_mctl, pcam_inst,
+				image_mode, fbuf,
+				&frame_id, 1);
+	}
+	return rc;
+}
+
+int msm_mctl_buf_init(struct msm_cam_v4l2_device *pcam)
+{
+	struct msm_cam_media_controller *pmctl;
+	pmctl = msm_camera_get_mctl(pcam->mctl_handle);
+	pmctl->mctl_vbqueue_init = msm_vbqueue_init;
+	return 0;
+}
+
+static int is_buffer_queued(struct msm_cam_v4l2_device *pcam, int image_mode)
+{
+	int idx;
+	int ret = 0;
+	struct msm_frame_buffer *buf = NULL;
+	struct msm_cam_v4l2_dev_inst *pcam_inst = NULL;
+	idx = pcam->mctl_node.dev_inst_map[image_mode]->my_index;
+	pcam_inst = pcam->mctl_node.dev_inst[idx];
+	list_for_each_entry(buf, &pcam_inst->free_vq, list) {
+		if (buf->state != MSM_BUFFER_STATE_QUEUED)
+			continue;
+		ret = 1;
+	}
+	return ret;
+}
+
+struct msm_cam_v4l2_dev_inst *msm_mctl_get_pcam_inst(
+				struct msm_cam_media_controller *pmctl,
+				int image_mode)
+{
+	struct msm_cam_v4l2_dev_inst *pcam_inst = NULL;
+	struct msm_cam_v4l2_device *pcam = pmctl->pcam_ptr;
+	int idx;
+
+	if (image_mode >= 0) {
+		/* Valid image mode. Search the mctl node first.
+		 * If mctl node doesnt have the instance, then
+		 * search in the user's video node */
+		if (pmctl->vfe_output_mode == VFE_OUTPUTS_MAIN_AND_THUMB
+		|| pmctl->vfe_output_mode == VFE_OUTPUTS_THUMB_AND_MAIN) {
+			if (pcam->mctl_node.dev_inst_map[image_mode]
+			&& is_buffer_queued(pcam, image_mode)) {
+				idx =
+				pcam->mctl_node.dev_inst_map[image_mode]
+				->my_index;
+				pcam_inst = pcam->mctl_node.dev_inst[idx];
+				D("%s Found instance %p in mctl node device\n",
+				  __func__, pcam_inst);
+			} else if (pcam->dev_inst_map[image_mode]) {
+				idx = pcam->dev_inst_map[image_mode]->my_index;
+				pcam_inst = pcam->dev_inst[idx];
+				D("%s Found instance %p in video device\n",
+				__func__, pcam_inst);
+			}
+		} else {
+			if (pcam->mctl_node.dev_inst_map[image_mode]) {
+				idx = pcam->mctl_node.dev_inst_map[image_mode]
+				->my_index;
+				pcam_inst = pcam->mctl_node.dev_inst[idx];
+				D("%s Found instance %p in mctl node device\n",
+				__func__, pcam_inst);
+			} else if (pcam->dev_inst_map[image_mode]) {
+				idx = pcam->dev_inst_map[image_mode]->my_index;
+				pcam_inst = pcam->dev_inst[idx];
+				D("%s Found instance %p in video device\n",
+				__func__, pcam_inst);
+			}
+		}
+	} else
+		pr_err("%s Invalid image mode %d. Return NULL\n",
+			__func__, image_mode);
+	return pcam_inst;
+}
+
+int msm_mctl_reserve_free_buf(
+		struct msm_cam_media_controller *pmctl,
+		struct msm_cam_v4l2_dev_inst *pref_pcam_inst,
+		int image_mode, struct msm_free_buf *free_buf)
+{
+	struct msm_cam_v4l2_dev_inst *pcam_inst = pref_pcam_inst;
+	unsigned long flags = 0;
+	struct videobuf2_contig_pmem *mem;
+	struct msm_frame_buffer *buf = NULL;
+	int rc = -EINVAL, i;
+	uint32_t buf_idx, plane_offset = 0;
+
+	if (!free_buf || !pmctl) {
+		pr_err("%s: free_buf/pmctl is null\n", __func__);
+		return rc;
+	}
+	memset(free_buf, 0, sizeof(struct msm_free_buf));
+
+	/* If the caller wants to reserve a buffer from a particular
+	 * camera instance, he would send the preferred camera instance.
+	 * If the preferred camera instance is NULL, get the
+	 * camera instance using the image mode passed */
+	if (!pcam_inst)
+		pcam_inst = msm_mctl_get_pcam_inst(pmctl, image_mode);
+
+	if (!pcam_inst || !pcam_inst->streamon) {
+		pr_err("%s: stream is turned off\n", __func__);
+		return rc;
+	}
+	spin_lock_irqsave(&pcam_inst->vq_irqlock, flags);
+	list_for_each_entry(buf, &pcam_inst->free_vq, list) {
+		if (buf->state != MSM_BUFFER_STATE_QUEUED)
+			continue;
+
+		buf_idx = buf->vidbuf.v4l2_buf.index;
+		if (pcam_inst->vid_fmt.type ==
+				V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+			free_buf->num_planes =
+				pcam_inst->plane_info.num_planes;
+			for (i = 0; i < free_buf->num_planes; i++) {
+				mem = vb2_plane_cookie(&buf->vidbuf, i);
+				if (mem->buffer_type ==
+						VIDEOBUF2_MULTIPLE_PLANES)
+					plane_offset =
+					mem->offset.data_offset;
+				else
+					plane_offset =
+					mem->offset.sp_off.cbcr_off;
+
+				D("%s: data off %d plane off %d",
+					__func__,
+					pcam_inst->buf_offset[buf_idx][i].
+					data_offset, plane_offset);
+				free_buf->ch_paddr[i] =	(uint32_t)
+				videobuf2_to_pmem_contig(&buf->vidbuf, i) +
+				pcam_inst->buf_offset[buf_idx][i].data_offset +
+				plane_offset;
+
+			}
+		} else {
+			mem = vb2_plane_cookie(&buf->vidbuf, 0);
+			free_buf->ch_paddr[0] = (uint32_t)
+				videobuf2_to_pmem_contig(&buf->vidbuf, 0) +
+				mem->offset.sp_off.y_off;
+			free_buf->ch_paddr[1] =	free_buf->ch_paddr[0] +
+				mem->offset.sp_off.cbcr_off;
+		}
+		free_buf->vb = (uint32_t)buf;
+		buf->state = MSM_BUFFER_STATE_RESERVED;
+		D("%s inst=0x%p, idx=%d, paddr=0x%x, "
+			"ch1 addr=0x%x\n", __func__,
+			pcam_inst, buf->vidbuf.v4l2_buf.index,
+			free_buf->ch_paddr[0], free_buf->ch_paddr[1]);
+		rc = 0;
+		break;
+	}
+	if (rc != 0)
+		D("%s:No free buffer available: inst = 0x%p ",
+				__func__, pcam_inst);
+	spin_unlock_irqrestore(&pcam_inst->vq_irqlock, flags);
+	return rc;
+}
+
+int msm_mctl_release_free_buf(struct msm_cam_media_controller *pmctl,
+				struct msm_cam_v4l2_dev_inst *pcam_inst,
+				int image_mode, struct msm_free_buf *free_buf)
+{
+	unsigned long flags = 0;
+	struct msm_frame_buffer *buf = NULL;
+	uint32_t buf_phyaddr = 0;
+	int rc = -EINVAL;
+
+	if (!free_buf)
+		return rc;
+
+	if (!pcam_inst) {
+		pr_err("%s Invalid instance, buffer not released\n",
+			__func__);
+		return rc;
+	}
+
+	spin_lock_irqsave(&pcam_inst->vq_irqlock, flags);
+	list_for_each_entry(buf, &pcam_inst->free_vq, list) {
+		buf_phyaddr =
+			(uint32_t) videobuf2_to_pmem_contig(&buf->vidbuf, 0);
+		if (free_buf->ch_paddr[0] == buf_phyaddr) {
+			D("%s buf = 0x%x \n", __func__, free_buf->ch_paddr[0]);
+			buf->state = MSM_BUFFER_STATE_UNUSED;
+			rc = 0;
+			break;
+		}
+	}
+
+	if (rc != 0)
+		pr_err("%s invalid buffer address ", __func__);
+
+	spin_unlock_irqrestore(&pcam_inst->vq_irqlock, flags);
+	return rc;
+}
+
+int msm_mctl_buf_done_pp(struct msm_cam_media_controller *pmctl,
+	int image_mode, struct msm_free_buf *frame, int dirty, int node_type)
+{
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	int rc = 0, idx;
+
+	idx = msm_mctl_img_mode_to_inst_index(pmctl, image_mode, node_type);
+	if (idx < 0) {
+		pr_err("%s Invalid instance, buffer not released\n", __func__);
+		return idx;
+	}
+	if (node_type)
+		pcam_inst = pmctl->pcam_ptr->mctl_node.dev_inst[idx];
+	else
+		pcam_inst = pmctl->pcam_ptr->dev_inst[idx];
+	if (!pcam_inst) {
+		pr_err("%s Invalid instance, cannot send buf to user",
+			__func__);
+		return -EINVAL;
+	}
+
+	D("%s:inst=0x%p, paddr=0x%x, dirty=%d",
+		__func__, pcam_inst, frame->ch_paddr[0], dirty);
+	if (dirty)
+		/* the frame is dirty, not going to disptach to app */
+		rc = msm_mctl_release_free_buf(pmctl, pcam_inst,
+						image_mode, frame);
+	else
+		rc = msm_mctl_buf_done_proc(pmctl, pcam_inst,
+			image_mode, frame, NULL, 0);
+	return rc;
+}
+
+struct msm_frame_buffer *msm_mctl_get_free_buf(
+		struct msm_cam_media_controller *pmctl,
+		int image_mode)
+{
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	unsigned long flags = 0;
+	struct msm_frame_buffer *buf = NULL;
+	int rc = -EINVAL, idx;
+
+	idx = msm_mctl_img_mode_to_inst_index(pmctl,
+		image_mode, 0);
+	if (idx < 0) {
+		pr_err("%s Invalid instance, cant get buffer\n", __func__);
+		return NULL;
+	}
+	pcam_inst = pmctl->pcam_ptr->dev_inst[idx];
+	if (!pcam_inst->streamon) {
+		D("%s: stream 0x%p is off\n", __func__, pcam_inst);
+		return NULL;
+	}
+	spin_lock_irqsave(&pcam_inst->vq_irqlock, flags);
+	if (!list_empty(&pcam_inst->free_vq)) {
+		list_for_each_entry(buf, &pcam_inst->free_vq, list) {
+			if (buf->state == MSM_BUFFER_STATE_QUEUED) {
+				buf->state = MSM_BUFFER_STATE_RESERVED;
+				rc = 0;
+				break;
+			}
+		}
+	}
+	if (rc != 0) {
+		D("%s:No free buffer available: inst = 0x%p ",
+				__func__, pcam_inst);
+		buf = NULL;
+	}
+	spin_unlock_irqrestore(&pcam_inst->vq_irqlock, flags);
+	return buf;
+}
+
+int msm_mctl_put_free_buf(
+		struct msm_cam_media_controller *pmctl,
+		int image_mode, struct msm_frame_buffer *my_buf)
+{
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	unsigned long flags = 0;
+	int rc = 0, idx;
+	struct msm_frame_buffer *buf = NULL;
+
+	idx = msm_mctl_img_mode_to_inst_index(pmctl,
+		image_mode, 0);
+	if (idx < 0) {
+		pr_err("%s Invalid instance, cant put buffer\n", __func__);
+		return idx;
+	}
+	pcam_inst = pmctl->pcam_ptr->dev_inst[idx];
+	if (!pcam_inst->streamon) {
+		D("%s: stream 0x%p is off\n", __func__, pcam_inst);
+		return rc;
+	}
+	spin_lock_irqsave(&pcam_inst->vq_irqlock, flags);
+	if (!list_empty(&pcam_inst->free_vq)) {
+		list_for_each_entry(buf, &pcam_inst->free_vq, list) {
+			if (my_buf == buf) {
+				buf->state = MSM_BUFFER_STATE_QUEUED;
+				spin_unlock_irqrestore(&pcam_inst->vq_irqlock,
+					flags);
+				return 0;
+			}
+		}
+	}
+	spin_unlock_irqrestore(&pcam_inst->vq_irqlock, flags);
+	return rc;
+}
+
+int msm_mctl_buf_del(struct msm_cam_media_controller *pmctl,
+	int image_mode,
+	struct msm_frame_buffer *my_buf)
+{
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	struct msm_frame_buffer *buf = NULL;
+	unsigned long flags = 0;
+	int idx;
+
+	idx = msm_mctl_img_mode_to_inst_index(pmctl,
+		image_mode, 0);
+	if (idx < 0) {
+		pr_err("%s Invalid instance, cant delete buffer\n", __func__);
+		return idx;
+	}
+	pcam_inst = pmctl->pcam_ptr->dev_inst[idx];
+	D("%s: idx = %d, pinst=0x%p", __func__, idx, pcam_inst);
+	spin_lock_irqsave(&pcam_inst->vq_irqlock, flags);
+	if (!list_empty(&pcam_inst->free_vq)) {
+		list_for_each_entry(buf, &pcam_inst->free_vq, list) {
+			if (my_buf == buf) {
+				list_del_init(&buf->list);
+				spin_unlock_irqrestore(&pcam_inst->vq_irqlock,
+					flags);
+				return 0;
+			}
+		}
+	}
+	spin_unlock_irqrestore(&pcam_inst->vq_irqlock, flags);
+	pr_err("%s: buf 0x%p not found", __func__, my_buf);
+	return -EINVAL;
+}
+
+int msm_mctl_buf_return_buf(struct msm_cam_media_controller *pmctl,
+			int image_mode, struct msm_frame_buffer *rbuf)
+{
+	int idx = 0;
+	struct msm_frame_buffer *buf = NULL;
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	struct msm_cam_v4l2_device *pcam = pmctl->pcam_ptr;
+	unsigned long flags = 0;
+
+	if (pcam->mctl_node.dev_inst_map[image_mode]) {
+		idx = pcam->mctl_node.dev_inst_map[image_mode]->my_index;
+		pcam_inst = pcam->mctl_node.dev_inst[idx];
+		D("%s Found instance %p in mctl node device\n",
+			__func__, pcam_inst);
+	} else {
+		pr_err("%s Invalid image mode %d ", __func__, image_mode);
+		return -EINVAL;
+	}
+
+	if (!pcam_inst) {
+		pr_err("%s Invalid instance\n", __func__);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&pcam_inst->vq_irqlock, flags);
+	if (!list_empty(&pcam_inst->free_vq)) {
+		list_for_each_entry(buf, &pcam_inst->free_vq, list) {
+			if (rbuf == buf) {
+				D("%s Return buffer %x in pcam_inst %p ",
+				__func__, (int)rbuf, pcam_inst);
+				buf->state = MSM_BUFFER_STATE_QUEUED;
+				spin_unlock_irqrestore(&pcam_inst->vq_irqlock,
+					flags);
+				return 0;
+			}
+		}
+	}
+	spin_unlock_irqrestore(&pcam_inst->vq_irqlock, flags);
+	return -EINVAL;
+}
diff --git a/drivers/media/video/msm/msm_mctl_pp.c b/drivers/media/video/msm/msm_mctl_pp.c
new file mode 100644
index 0000000..f4c04bb
--- /dev/null
+++ b/drivers/media/video/msm/msm_mctl_pp.c
@@ -0,0 +1,1029 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/ioctl.h>
+#include <linux/spinlock.h>
+#include <linux/videodev2.h>
+#include <linux/proc_fs.h>
+#include <linux/vmalloc.h>
+
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+
+#include <linux/android_pmem.h>
+
+#include "msm.h"
+#include "msm_vpe.h"
+
+#ifdef CONFIG_MSM_CAMERA_DEBUG
+#define D(fmt, args...) pr_debug("msm_mctl: " fmt, ##args)
+#else
+#define D(fmt, args...) do {} while (0)
+#endif
+
+
+static int msm_mctl_pp_vpe_ioctl(struct v4l2_subdev *vpe_sd,
+	struct msm_mctl_pp_cmd *cmd, void *data)
+{
+	int rc = 0;
+	struct msm_mctl_pp_params parm;
+	parm.cmd = cmd;
+	parm.data = data;
+	rc = v4l2_subdev_call(vpe_sd, core, ioctl, VIDIOC_MSM_VPE_CFG, &parm);
+	return rc;
+}
+
+static int msm_mctl_pp_buf_divert(
+			struct msm_cam_media_controller *pmctl,
+			struct msm_cam_v4l2_dev_inst *pcam_inst,
+			struct msm_cam_evt_divert_frame *div)
+{
+	struct v4l2_event v4l2_evt;
+	struct msm_isp_event_ctrl *isp_event;
+	isp_event = kzalloc(sizeof(struct msm_isp_event_ctrl),
+						GFP_ATOMIC);
+	if (!isp_event) {
+		pr_err("%s Insufficient memory. return", __func__);
+		return -ENOMEM;
+	}
+	D("%s: msm_cam_evt_divert_frame=%d",
+		__func__, sizeof(struct msm_cam_evt_divert_frame));
+	memset(&v4l2_evt, 0, sizeof(v4l2_evt));
+	v4l2_evt.id = 0;
+	v4l2_evt.type = V4L2_EVENT_PRIVATE_START +
+			MSM_CAM_RESP_DIV_FRAME_EVT_MSG;
+	*((uint32_t *)v4l2_evt.u.data) = (uint32_t)isp_event;
+	/* Copy the divert frame struct into event ctrl struct. */
+	isp_event->isp_data.div_frame = *div;
+
+	D("%s inst=%p, img_mode=%d, frame_id=%d\n", __func__,
+		pcam_inst, pcam_inst->image_mode, div->frame.frame_id);
+	v4l2_event_queue(
+		pmctl->config_device->config_stat_event_queue.pvdev,
+		&v4l2_evt);
+	return 0;
+}
+
+int msm_mctl_check_pp(struct msm_cam_media_controller *p_mctl,
+	int image_mode, int *pp_divert_type, int *pp_type)
+{
+	int rc = 0;
+	unsigned long flags;
+	uint32_t pp_key = 0;
+
+	*pp_type = 0;
+	*pp_divert_type = 0;
+	spin_lock_irqsave(&p_mctl->pp_info.lock, flags);
+	switch (image_mode) {
+	case MSM_V4L2_EXT_CAPTURE_MODE_PREVIEW:
+		pp_key = PP_PREV;
+		if (p_mctl->pp_info.pp_key & pp_key)
+			*pp_divert_type = OUTPUT_TYPE_P;
+		if (p_mctl->pp_info.pp_ctrl.pp_msg_type & OUTPUT_TYPE_P)
+			*pp_type = OUTPUT_TYPE_P;
+		break;
+	case MSM_V4L2_EXT_CAPTURE_MODE_MAIN:
+		pp_key = PP_SNAP;
+		if (p_mctl->pp_info.pp_key & pp_key)
+			*pp_divert_type = OUTPUT_TYPE_S;
+		if (p_mctl->pp_info.pp_ctrl.pp_msg_type & OUTPUT_TYPE_S)
+			*pp_type = OUTPUT_TYPE_P;
+		break;
+	case MSM_V4L2_EXT_CAPTURE_MODE_VIDEO:
+		if (p_mctl->pp_info.pp_ctrl.pp_msg_type == OUTPUT_TYPE_V)
+			*pp_type = OUTPUT_TYPE_V;
+		break;
+	case MSM_V4L2_EXT_CAPTURE_MODE_THUMBNAIL:
+		pp_key = PP_THUMB;
+		if (p_mctl->pp_info.pp_key & pp_key)
+			*pp_divert_type = OUTPUT_TYPE_T;
+		if (p_mctl->pp_info.pp_ctrl.pp_msg_type == OUTPUT_TYPE_T)
+			*pp_type = OUTPUT_TYPE_T;
+		break;
+	default:
+		break;
+	}
+	if (p_mctl->vfe_output_mode != VFE_OUTPUTS_MAIN_AND_THUMB &&
+		p_mctl->vfe_output_mode != VFE_OUTPUTS_THUMB_AND_MAIN) {
+		if (p_mctl->pp_info.div_frame[image_mode].ch_paddr[0])
+			*pp_divert_type = 0;
+	}
+	spin_unlock_irqrestore(&p_mctl->pp_info.lock, flags);
+	D("%s: pp_type=%d, pp_divert_type = %d",
+	__func__, *pp_type, *pp_divert_type);
+	return rc;
+}
+
+static int is_buf_in_queue(struct msm_cam_v4l2_device *pcam,
+	struct msm_free_buf *fbuf, int image_mode)
+{
+	struct msm_frame_buffer *buf = NULL, *tmp;
+	struct msm_cam_v4l2_dev_inst *pcam_inst = NULL;
+	unsigned long flags = 0;
+	struct videobuf2_contig_pmem *mem;
+	uint32_t buf_idx, offset = 0;
+	uint32_t buf_phyaddr = 0;
+	int idx;
+	idx = pcam->mctl_node.dev_inst_map[image_mode]->my_index;
+	pcam_inst = pcam->mctl_node.dev_inst[idx];
+	spin_lock_irqsave(&pcam_inst->vq_irqlock, flags);
+	list_for_each_entry_safe(buf, tmp,
+	&pcam_inst->free_vq, list) {
+		buf_idx = buf->vidbuf.v4l2_buf.index;
+		mem = vb2_plane_cookie(&buf->vidbuf, 0);
+		if (mem->buffer_type ==	VIDEOBUF2_MULTIPLE_PLANES)
+			offset = mem->offset.data_offset +
+				pcam_inst->buf_offset[buf_idx][0].data_offset;
+		else
+			offset = mem->offset.sp_off.y_off;
+		buf_phyaddr = (unsigned long)
+			videobuf2_to_pmem_contig(&buf->vidbuf, 0) +
+			offset;
+		D("%s vb_idx=%d,vb_paddr=0x%x ch0=0x%x\n",
+		  __func__, buf->vidbuf.v4l2_buf.index,
+		  buf_phyaddr, fbuf->ch_paddr[0]);
+		if (fbuf->ch_paddr[0] == buf_phyaddr) {
+			spin_unlock_irqrestore(&pcam_inst->vq_irqlock, flags);
+			return 1;
+		}
+	}
+	spin_unlock_irqrestore(&pcam_inst->vq_irqlock, flags);
+	return 0;
+}
+static struct msm_cam_v4l2_dev_inst *msm_mctl_get_pcam_inst_for_divert(
+		struct msm_cam_media_controller *pmctl,
+		int image_mode, struct msm_free_buf *fbuf, int *node_type)
+{
+	struct msm_cam_v4l2_dev_inst *pcam_inst = NULL;
+	struct msm_cam_v4l2_device *pcam = pmctl->pcam_ptr;
+	int idx;
+
+	if (image_mode >= 0) {
+		/* Valid image mode. Search the mctl node first.
+		 * If mctl node doesnt have the instance, then
+		 * search in the user's video node */
+		if (pcam->mctl_node.dev_inst_map[image_mode]
+		&& is_buf_in_queue(pcam, fbuf, image_mode)) {
+			idx =
+			pcam->mctl_node.dev_inst_map[image_mode]->my_index;
+			pcam_inst = pcam->mctl_node.dev_inst[idx];
+			*node_type = MCTL_NODE;
+			D("%s Found instance %p in mctl node device\n",
+				__func__, pcam_inst);
+		} else if (pcam->dev_inst_map[image_mode]) {
+			idx = pcam->dev_inst_map[image_mode]->my_index;
+			pcam_inst = pcam->dev_inst[idx];
+			*node_type = VIDEO_NODE;
+			D("%s Found instance %p in video device",
+				__func__, pcam_inst);
+		} else
+			pr_err("%s Invalid image mode %d. Return NULL\n",
+				   __func__, image_mode);
+	}
+		return pcam_inst;
+}
+
+int msm_mctl_do_pp_divert(
+	struct msm_cam_media_controller *p_mctl,
+	int image_mode, struct msm_free_buf *fbuf,
+	uint32_t frame_id, int pp_type)
+{
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	int rc = 0, i, buf_idx;
+	int del_buf = 0; /* delete from free queue */
+	struct msm_cam_evt_divert_frame div;
+	struct msm_frame_buffer *vb = NULL;
+	struct videobuf2_contig_pmem *mem;
+	int node;
+
+	pcam_inst = msm_mctl_get_pcam_inst_for_divert
+		(p_mctl, image_mode, fbuf, &node);
+	if (!pcam_inst) {
+		pr_err("%s Invalid instance. Cannot divert frame.\n",
+			__func__);
+		return -EINVAL;
+	}
+	vb = msm_mctl_buf_find(p_mctl, pcam_inst,
+		  del_buf, image_mode, fbuf);
+	if (!vb)
+		return -EINVAL;
+
+	vb->vidbuf.v4l2_buf.sequence = frame_id;
+	buf_idx = vb->vidbuf.v4l2_buf.index;
+	D("%s Diverting frame %d %x Image mode %d\n", __func__, buf_idx,
+		(uint32_t)vb, pcam_inst->image_mode);
+	div.image_mode = pcam_inst->image_mode;
+	div.op_mode    = pcam_inst->pcam->op_mode;
+	div.inst_idx   = pcam_inst->my_index;
+	div.node_idx   = pcam_inst->pcam->vnode_id;
+	p_mctl->pp_info.cur_frame_id[pcam_inst->image_mode] = frame_id;
+	div.frame.frame_id =
+		p_mctl->pp_info.cur_frame_id[pcam_inst->image_mode];
+	div.frame.buf_idx  = buf_idx;
+	div.frame.handle = (uint32_t)vb;
+	msm_mctl_gettimeofday(&div.frame.timestamp);
+	vb->vidbuf.v4l2_buf.timestamp = div.frame.timestamp;
+	div.do_pp = pp_type;
+	D("%s Diverting frame %x id %d to userspace ", __func__,
+		(int)div.frame.handle, div.frame.frame_id);
+	/* Get the cookie for 1st plane and store the path.
+	 * Also use this to check the number of planes in
+	 * this buffer.*/
+	mem = vb2_plane_cookie(&vb->vidbuf, 0);
+	div.frame.path = mem->path;
+	div.frame.node_type = node;
+	if (mem->buffer_type == VIDEOBUF2_SINGLE_PLANE) {
+		/* This buffer contains only 1 plane. Use the
+		 * single planar structure to store the info.*/
+		div.frame.num_planes	= 1;
+		div.frame.sp.phy_addr	=
+			videobuf2_to_pmem_contig(&vb->vidbuf, 0);
+		div.frame.sp.addr_offset = mem->addr_offset;
+		div.frame.sp.y_off      = 0;
+		div.frame.sp.cbcr_off   = mem->offset.sp_off.cbcr_off;
+		div.frame.sp.fd         = (int)mem->vaddr;
+		div.frame.sp.length     = mem->size;
+		if (!pp_type)
+			p_mctl->pp_info.div_frame[pcam_inst->image_mode].
+			ch_paddr[0] = div.frame.sp.phy_addr;
+	} else {
+		/* This buffer contains multiple planes. Use the mutliplanar
+		 * structure to store the info. */
+		div.frame.num_planes	= pcam_inst->plane_info.num_planes;
+		/* Now traverse through all the planes of the buffer to
+		 * fill out the plane info. */
+		for (i = 0; i < div.frame.num_planes; i++) {
+			mem = vb2_plane_cookie(&vb->vidbuf, i);
+			div.frame.mp[i].phy_addr =
+				videobuf2_to_pmem_contig(&vb->vidbuf, i);
+			if (!pcam_inst->buf_offset)
+				div.frame.mp[i].data_offset = 0;
+			else
+				div.frame.mp[i].data_offset =
+				pcam_inst->buf_offset[buf_idx][i].data_offset;
+			div.frame.mp[i].addr_offset =
+				mem->addr_offset;
+			div.frame.mp[i].fd = (int)mem->vaddr;
+			div.frame.mp[i].length = mem->size;
+		}
+		if (!pp_type)
+			p_mctl->pp_info.div_frame[pcam_inst->image_mode].
+			ch_paddr[0] = div.frame.mp[0].phy_addr +
+					div.frame.mp[0].data_offset;
+	}
+	rc = msm_mctl_pp_buf_divert(p_mctl, pcam_inst, &div);
+	return rc;
+}
+
+static int msm_mctl_pp_get_phy_addr(
+	struct msm_cam_v4l2_dev_inst *pcam_inst,
+	uint32_t frame_handle,
+	struct msm_pp_frame *pp_frame)
+{
+	struct msm_frame_buffer *vb = NULL;
+	struct videobuf2_contig_pmem *mem;
+	int i, buf_idx = 0;
+
+	vb = (struct msm_frame_buffer *)frame_handle;
+	buf_idx = vb->vidbuf.v4l2_buf.index;
+	memset(pp_frame, 0, sizeof(struct msm_pp_frame));
+	pp_frame->handle = (uint32_t)vb;
+	pp_frame->frame_id = vb->vidbuf.v4l2_buf.sequence;
+	pp_frame->timestamp = vb->vidbuf.v4l2_buf.timestamp;
+	pp_frame->buf_idx = buf_idx;
+	/* Get the cookie for 1st plane and store the path.
+	 * Also use this to check the number of planes in
+	 * this buffer.*/
+	mem = vb2_plane_cookie(&vb->vidbuf, 0);
+	pp_frame->image_type = (unsigned short)mem->path;
+	if (mem->buffer_type == VIDEOBUF2_SINGLE_PLANE) {
+		pp_frame->num_planes = 1;
+		pp_frame->sp.addr_offset = mem->addr_offset;
+		pp_frame->sp.phy_addr =
+			videobuf2_to_pmem_contig(&vb->vidbuf, 0);
+		pp_frame->sp.y_off = 0;
+		pp_frame->sp.cbcr_off = mem->offset.sp_off.cbcr_off;
+		pp_frame->sp.length = mem->size;
+		pp_frame->sp.fd = (int)mem->vaddr;
+	} else {
+		pp_frame->num_planes = pcam_inst->plane_info.num_planes;
+		for (i = 0; i < pp_frame->num_planes; i++) {
+			mem = vb2_plane_cookie(&vb->vidbuf, i);
+			pp_frame->mp[i].addr_offset = mem->addr_offset;
+			pp_frame->mp[i].phy_addr =
+				videobuf2_to_pmem_contig(&vb->vidbuf, i);
+			pp_frame->mp[i].data_offset =
+			pcam_inst->buf_offset[buf_idx][i].data_offset;
+			pp_frame->mp[i].fd = (int)mem->vaddr;
+			pp_frame->mp[i].length = mem->size;
+			D("%s frame id %d buffer %d plane %d phy addr 0x%x"
+				" fd %d length %d\n", __func__,
+				pp_frame->frame_id, buf_idx, i,
+				(uint32_t)pp_frame->mp[i].phy_addr,
+				pp_frame->mp[i].fd, pp_frame->mp[i].length);
+		}
+	}
+	return 0;
+}
+
+static int msm_mctl_pp_copy_timestamp_and_frame_id(
+	uint32_t src_handle, uint32_t dest_handle)
+{
+	struct msm_frame_buffer *src_vb;
+	struct msm_frame_buffer *dest_vb;
+
+	src_vb = (struct msm_frame_buffer *)src_handle;
+	dest_vb = (struct msm_frame_buffer *)dest_handle;
+	dest_vb->vidbuf.v4l2_buf.timestamp =
+		src_vb->vidbuf.v4l2_buf.timestamp;
+	dest_vb->vidbuf.v4l2_buf.sequence =
+		src_vb->vidbuf.v4l2_buf.sequence;
+	D("%s: timestamp=%ld:%ld,frame_id=0x%x", __func__,
+		dest_vb->vidbuf.v4l2_buf.timestamp.tv_sec,
+		dest_vb->vidbuf.v4l2_buf.timestamp.tv_usec,
+		dest_vb->vidbuf.v4l2_buf.sequence);
+	return 0;
+}
+
+static int msm_mctl_pp_path_to_inst_index(struct msm_cam_v4l2_device *pcam,
+					int out_type)
+{
+	int image_mode;
+	switch (out_type) {
+	case OUTPUT_TYPE_P:
+		image_mode = MSM_V4L2_EXT_CAPTURE_MODE_PREVIEW;
+		break;
+	case OUTPUT_TYPE_V:
+		image_mode = MSM_V4L2_EXT_CAPTURE_MODE_VIDEO;
+		break;
+	case OUTPUT_TYPE_S:
+		image_mode = MSM_V4L2_EXT_CAPTURE_MODE_MAIN;
+		break;
+	default:
+		image_mode = -1;
+		break;
+	}
+	if ((image_mode >= 0) && pcam->dev_inst_map[image_mode])
+		return pcam->dev_inst_map[image_mode]->my_index;
+	else
+		return -EINVAL;
+}
+
+int msm_mctl_pp_proc_vpe_cmd(
+	struct msm_cam_media_controller *p_mctl,
+	struct msm_mctl_pp_cmd *pp_cmd)
+{
+	int rc = 0, idx;
+	void __user *argp = (void __user *)pp_cmd->value;
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+
+	switch (pp_cmd->id) {
+	case VPE_CMD_INIT:
+	case VPE_CMD_DEINIT:
+		rc = msm_mctl_pp_vpe_ioctl(
+			p_mctl->vpe_sdev, pp_cmd, NULL);
+		break;
+	case VPE_CMD_DISABLE:
+	case VPE_CMD_RESET:
+		rc = msm_mctl_pp_vpe_ioctl(
+			p_mctl->vpe_sdev, pp_cmd, NULL);
+		break;
+	case VPE_CMD_ENABLE: {
+		struct msm_vpe_clock_rate clk_rate;
+		if (sizeof(struct msm_vpe_clock_rate) !=
+			pp_cmd->length) {
+			pr_err("%s: vpe cmd size mismatch "
+				"(id=%d, length = %d, expect size = %d",
+				__func__, pp_cmd->id, pp_cmd->length,
+				sizeof(struct msm_vpe_clock_rate));
+				rc = -EINVAL;
+				break;
+		}
+		if (copy_from_user(&clk_rate, pp_cmd->value,
+			sizeof(struct msm_vpe_clock_rate))) {
+			pr_err("%s:clk_rate copy failed", __func__);
+			return -EFAULT;
+		}
+		pp_cmd->value = (void *)&clk_rate;
+		rc = msm_mctl_pp_vpe_ioctl(
+			p_mctl->vpe_sdev, pp_cmd, NULL);
+		pp_cmd->value = argp;
+		break;
+	}
+	case VPE_CMD_FLUSH: {
+		struct msm_vpe_flush_frame_buffer flush_buf;
+		if (sizeof(struct msm_vpe_flush_frame_buffer) !=
+			pp_cmd->length) {
+			D("%s: size mismatch(id=%d, len = %d, expected = %d",
+				__func__, pp_cmd->id, pp_cmd->length,
+				sizeof(struct msm_vpe_flush_frame_buffer));
+				rc = -EINVAL;
+				break;
+		}
+		if (copy_from_user(
+			&flush_buf, pp_cmd->value, sizeof(flush_buf)))
+			return -EFAULT;
+		pp_cmd->value = (void *)&flush_buf;
+		rc = msm_mctl_pp_vpe_ioctl(
+			p_mctl->vpe_sdev, pp_cmd, NULL);
+		if (rc == 0) {
+			if (copy_to_user((void *)argp,
+						&flush_buf,
+						sizeof(flush_buf))) {
+				ERR_COPY_TO_USER();
+				rc = -EFAULT;
+			}
+			pp_cmd->value = argp;
+		}
+	}
+	break;
+	case VPE_CMD_OPERATION_MODE_CFG: {
+		struct msm_vpe_op_mode_cfg op_mode_cfg;
+		if (sizeof(struct msm_vpe_op_mode_cfg) !=
+		pp_cmd->length) {
+			D("%s: size mismatch(id=%d, len = %d, expected = %d",
+				__func__, pp_cmd->id, pp_cmd->length,
+				sizeof(struct msm_vpe_op_mode_cfg));
+				rc = -EINVAL;
+				break;
+		}
+		if (copy_from_user(&op_mode_cfg,
+			pp_cmd->value,
+			sizeof(op_mode_cfg)))
+			return -EFAULT;
+		pp_cmd->value = (void *)&op_mode_cfg;
+		rc = msm_mctl_pp_vpe_ioctl(
+			p_mctl->vpe_sdev, pp_cmd, NULL);
+		break;
+	}
+	case VPE_CMD_INPUT_PLANE_CFG: {
+		struct msm_vpe_input_plane_cfg input_cfg;
+		if (sizeof(struct msm_vpe_input_plane_cfg) !=
+			pp_cmd->length) {
+			D("%s: mismatch(id=%d, len = %d, expected = %d",
+				__func__, pp_cmd->id, pp_cmd->length,
+				sizeof(struct msm_vpe_input_plane_cfg));
+				rc = -EINVAL;
+				break;
+		}
+		if (copy_from_user(
+			&input_cfg, pp_cmd->value, sizeof(input_cfg)))
+			return -EFAULT;
+		pp_cmd->value = (void *)&input_cfg;
+		rc = msm_mctl_pp_vpe_ioctl(
+			p_mctl->vpe_sdev, pp_cmd, NULL);
+		break;
+	}
+	case VPE_CMD_OUTPUT_PLANE_CFG: {
+		struct msm_vpe_output_plane_cfg output_cfg;
+		if (sizeof(struct msm_vpe_output_plane_cfg) !=
+			pp_cmd->length) {
+			D("%s: size mismatch(id=%d, len = %d, expected = %d",
+				__func__, pp_cmd->id, pp_cmd->length,
+				sizeof(struct msm_vpe_output_plane_cfg));
+				rc = -EINVAL;
+				break;
+		}
+		if (copy_from_user(&output_cfg, pp_cmd->value,
+			sizeof(output_cfg))) {
+			D("%s: cannot copy pp_cmd->value, size=%d",
+				__func__, pp_cmd->length);
+			return -EFAULT;
+		}
+		pp_cmd->value = (void *)&output_cfg;
+		rc = msm_mctl_pp_vpe_ioctl(
+			p_mctl->vpe_sdev, pp_cmd, NULL);
+		break;
+	}
+	case VPE_CMD_INPUT_PLANE_UPDATE: {
+		struct msm_vpe_input_plane_update_cfg input_update_cfg;
+		if (sizeof(struct msm_vpe_input_plane_update_cfg) !=
+			pp_cmd->length) {
+			D("%s: size mismatch(id=%d, len = %d, expected = %d",
+				__func__, pp_cmd->id, pp_cmd->length,
+				sizeof(struct msm_vpe_input_plane_update_cfg));
+				rc = -EINVAL;
+				break;
+		}
+		if (copy_from_user(&input_update_cfg, pp_cmd->value,
+			sizeof(input_update_cfg)))
+			return -EFAULT;
+		pp_cmd->value = (void *)&input_update_cfg;
+		rc = msm_mctl_pp_vpe_ioctl(
+			p_mctl->vpe_sdev, pp_cmd, NULL);
+		break;
+	}
+	case VPE_CMD_SCALE_CFG_TYPE: {
+		struct msm_vpe_scaler_cfg scaler_cfg;
+		if (sizeof(struct msm_vpe_scaler_cfg) !=
+			pp_cmd->length) {
+			D("%s: size mismatch(id=%d, len = %d, expected = %d",
+				__func__, pp_cmd->id, pp_cmd->length,
+				sizeof(struct msm_vpe_scaler_cfg));
+				rc = -EINVAL;
+				break;
+		}
+		if (copy_from_user(&scaler_cfg, pp_cmd->value,
+			sizeof(scaler_cfg)))
+			return -EFAULT;
+		pp_cmd->value = (void *)&scaler_cfg;
+		rc = msm_mctl_pp_vpe_ioctl(
+			p_mctl->vpe_sdev, pp_cmd, NULL);
+		break;
+	}
+	case VPE_CMD_ZOOM: {
+		struct msm_mctl_pp_frame_info *zoom;
+		zoom = kmalloc(sizeof(struct msm_mctl_pp_frame_info),
+					GFP_ATOMIC);
+		if (!zoom) {
+			rc = -ENOMEM;
+			break;
+		}
+		if (sizeof(zoom->pp_frame_cmd) != pp_cmd->length) {
+			D("%s: size mismatch(id=%d, len = %d, expected = %d",
+				__func__, pp_cmd->id, pp_cmd->length,
+				sizeof(zoom->pp_frame_cmd));
+				rc = -EINVAL;
+				kfree(zoom);
+				break;
+		}
+		if (copy_from_user(&zoom->pp_frame_cmd, pp_cmd->value,
+			sizeof(zoom->pp_frame_cmd))) {
+			kfree(zoom);
+			return -EFAULT;
+		}
+		D("%s: src=0x%x, dest=0x%x,cookie=0x%x,action=0x%x,path=0x%x",
+				__func__, zoom->pp_frame_cmd.src_buf_handle,
+				zoom->pp_frame_cmd.dest_buf_handle,
+				zoom->pp_frame_cmd.cookie,
+				zoom->pp_frame_cmd.vpe_output_action,
+				zoom->pp_frame_cmd.path);
+		idx = msm_mctl_pp_path_to_inst_index(p_mctl->pcam_ptr,
+			zoom->pp_frame_cmd.path);
+		if (idx < 0) {
+			pr_err("%s Invalid path, returning\n", __func__);
+			kfree(zoom);
+			return idx;
+		}
+		pcam_inst = p_mctl->pcam_ptr->dev_inst[idx];
+		if (!pcam_inst) {
+			pr_err("%s Invalid instance, returning\n", __func__);
+			kfree(zoom);
+			return -EINVAL;
+		}
+		zoom->user_cmd = pp_cmd->id;
+		rc = msm_mctl_pp_get_phy_addr(pcam_inst,
+			zoom->pp_frame_cmd.src_buf_handle, &zoom->src_frame);
+		if (rc) {
+			kfree(zoom);
+			break;
+		}
+		rc = msm_mctl_pp_get_phy_addr(pcam_inst,
+			zoom->pp_frame_cmd.dest_buf_handle, &zoom->dest_frame);
+		if (rc) {
+			kfree(zoom);
+			break;
+		}
+		rc = msm_mctl_pp_copy_timestamp_and_frame_id(
+			zoom->pp_frame_cmd.src_buf_handle,
+
+			zoom->pp_frame_cmd.dest_buf_handle);
+		if (rc) {
+			kfree(zoom);
+			break;
+		}
+		rc = msm_mctl_pp_vpe_ioctl(
+			p_mctl->vpe_sdev, pp_cmd, (void *)zoom);
+		if (rc) {
+			kfree(zoom);
+			break;
+		}
+		break;
+	}
+	default:
+		rc = -1;
+		break;
+	}
+	return rc;
+}
+
+static int msm_mctl_pp_path_to_img_mode(int path)
+{
+	switch (path) {
+	case OUTPUT_TYPE_P:
+		return MSM_V4L2_EXT_CAPTURE_MODE_PREVIEW;
+	case OUTPUT_TYPE_V:
+		return MSM_V4L2_EXT_CAPTURE_MODE_VIDEO;
+	case OUTPUT_TYPE_S:
+		return MSM_V4L2_EXT_CAPTURE_MODE_MAIN;
+	case OUTPUT_TYPE_T:
+		return MSM_V4L2_EXT_CAPTURE_MODE_THUMBNAIL;
+	default:
+		return -EINVAL;
+	}
+}
+
+int msm_mctl_pp_proc_cmd(struct msm_cam_media_controller *p_mctl,
+			struct msm_mctl_pp_cmd *pp_cmd)
+{
+	int rc = 0;
+	struct msm_mctl_pp_frame_buffer pp_buffer;
+	struct msm_frame_buffer *buf = NULL;
+	void __user *argp = (void __user *)pp_cmd->value;
+	int img_mode;
+	unsigned long flags;
+
+	switch (pp_cmd->id) {
+	case MCTL_CMD_GET_FRAME_BUFFER: {
+		if (copy_from_user(&pp_buffer, pp_cmd->value,
+				sizeof(pp_buffer)))
+			return -EFAULT;
+		img_mode = msm_mctl_pp_path_to_img_mode(pp_buffer.path);
+		if (img_mode < 0) {
+			pr_err("%s Invalid image mode\n", __func__);
+			return img_mode;
+		}
+		buf = msm_mctl_get_free_buf(p_mctl, img_mode);
+		pp_buffer.buf_handle = (uint32_t)buf;
+		if (copy_to_user((void *)argp,
+			&pp_buffer,
+			sizeof(struct msm_mctl_pp_frame_buffer))) {
+			ERR_COPY_TO_USER();
+			rc = -EFAULT;
+		}
+		break;
+	}
+	case MCTL_CMD_PUT_FRAME_BUFFER: {
+		if (copy_from_user(&pp_buffer, pp_cmd->value,
+				sizeof(pp_buffer)))
+			return -EFAULT;
+		img_mode = msm_mctl_pp_path_to_img_mode(pp_buffer.path);
+		if (img_mode < 0) {
+			pr_err("%s Invalid image mode\n", __func__);
+			return img_mode;
+		}
+		buf = (struct msm_frame_buffer *)pp_buffer.buf_handle;
+		msm_mctl_put_free_buf(p_mctl, img_mode, buf);
+		break;
+	}
+	case MCTL_CMD_DIVERT_FRAME_PP_PATH: {
+		struct msm_mctl_pp_divert_pp divert_pp;
+		if (copy_from_user(&divert_pp, pp_cmd->value,
+				sizeof(divert_pp)))
+			return -EFAULT;
+		D("%s: PP_PATH, path=%d",
+			__func__, divert_pp.path);
+		spin_lock_irqsave(&p_mctl->pp_info.lock, flags);
+		if (divert_pp.enable)
+			p_mctl->pp_info.pp_ctrl.pp_msg_type |= divert_pp.path;
+		else
+			p_mctl->pp_info.pp_ctrl.pp_msg_type &= ~divert_pp.path;
+		spin_unlock_irqrestore(&p_mctl->pp_info.lock, flags);
+		D("%s: pp path = 0x%x", __func__,
+			p_mctl->pp_info.pp_ctrl.pp_msg_type);
+		break;
+	}
+	default:
+		rc = -EPERM;
+	break;
+	}
+	return rc;
+}
+
+
+int msm_mctl_pp_ioctl(struct msm_cam_media_controller *p_mctl,
+			unsigned int cmd, unsigned long arg)
+{
+	int rc = -EINVAL;
+	struct msm_mctl_post_proc_cmd pp_cmd;
+	void __user *argp = (void __user *)arg;
+
+	if (copy_from_user(&pp_cmd, argp, sizeof(pp_cmd)))
+		return -EFAULT;
+
+	switch (pp_cmd.type) {
+	case MSM_PP_CMD_TYPE_VPE:
+		rc = msm_mctl_pp_proc_vpe_cmd(p_mctl, &pp_cmd.cmd);
+		break;
+	case MSM_PP_CMD_TYPE_MCTL:
+		rc = msm_mctl_pp_proc_cmd(p_mctl, &pp_cmd.cmd);
+		break;
+	default:
+		rc = -EPERM;
+		break;
+	}
+	if (!rc) {
+		/* deep copy back the return value */
+		if (copy_to_user((void *)arg,
+			&pp_cmd,
+			sizeof(struct msm_mctl_post_proc_cmd))) {
+			ERR_COPY_TO_USER();
+			rc = -EFAULT;
+		}
+	}
+	return rc;
+}
+
+int msm_mctl_pp_notify(struct msm_cam_media_controller *p_mctl,
+			struct msm_mctl_pp_frame_info *pp_frame_info)
+{
+		struct msm_mctl_pp_frame_cmd *pp_frame_cmd;
+		pp_frame_cmd = &pp_frame_info->pp_frame_cmd;
+
+		D("%s: msm_cam_evt_divert_frame=%d",
+			__func__, sizeof(struct msm_mctl_pp_event_info));
+		if ((MSM_MCTL_PP_VPE_FRAME_TO_APP &
+			pp_frame_cmd->vpe_output_action)) {
+			struct msm_free_buf done_frame;
+			int img_mode =
+				msm_mctl_pp_path_to_img_mode(
+					pp_frame_cmd->path);
+			if (img_mode < 0) {
+				pr_err("%s Invalid image mode\n", __func__);
+				return img_mode;
+			}
+			done_frame.ch_paddr[0] =
+				pp_frame_info->dest_frame.sp.phy_addr;
+			done_frame.vb =
+				pp_frame_info->dest_frame.handle;
+			msm_mctl_buf_done_pp(
+				p_mctl, img_mode, &done_frame, 0, 0);
+			D("%s: vpe done to app, vb=0x%x, path=%d, phy=0x%x",
+				__func__, done_frame.vb,
+				pp_frame_cmd->path, done_frame.ch_paddr[0]);
+		}
+		if ((MSM_MCTL_PP_VPE_FRAME_ACK &
+			pp_frame_cmd->vpe_output_action)) {
+			struct v4l2_event v4l2_evt;
+			struct msm_mctl_pp_event_info *pp_event_info;
+			struct msm_isp_event_ctrl *isp_event;
+			isp_event = kzalloc(sizeof(struct msm_isp_event_ctrl),
+								GFP_ATOMIC);
+			if (!isp_event) {
+				pr_err("%s Insufficient memory.", __func__);
+				return -ENOMEM;
+			}
+			memset(&v4l2_evt, 0, sizeof(v4l2_evt));
+			*((uint32_t *)v4l2_evt.u.data) = (uint32_t)isp_event;
+
+			/* Get hold of pp event info struct inside event ctrl.*/
+			pp_event_info = &(isp_event->isp_data.pp_event_info);
+
+			pp_event_info->event = MCTL_PP_EVENT_CMD_ACK;
+			pp_event_info->ack.cmd = pp_frame_info->user_cmd;
+			pp_event_info->ack.status = 0;
+			pp_event_info->ack.cookie = pp_frame_cmd->cookie;
+			v4l2_evt.id = 0;
+			v4l2_evt.type = V4L2_EVENT_PRIVATE_START +
+						MSM_CAM_RESP_MCTL_PP_EVENT;
+
+			v4l2_event_queue(
+				p_mctl->config_device->
+					config_stat_event_queue.pvdev,
+				&v4l2_evt);
+			D("%s: ack to daemon, cookie=0x%x, event = 0x%x",
+				__func__, pp_frame_info->pp_frame_cmd.cookie,
+				v4l2_evt.type);
+		}
+		kfree(pp_frame_info); /* free mem */
+		return 0;
+}
+
+int msm_mctl_pp_reserve_free_frame(
+	struct msm_cam_media_controller *p_mctl,
+	void __user *arg)
+{
+	struct msm_cam_evt_divert_frame frame;
+	int image_mode, rc = 0;
+	struct msm_free_buf free_buf;
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+
+	memset(&free_buf, 0, sizeof(struct msm_free_buf));
+	if (copy_from_user(&frame, arg,
+		sizeof(struct msm_cam_evt_divert_frame)))
+		return -EFAULT;
+
+	image_mode = frame.image_mode;
+	if (image_mode <= 0) {
+		pr_err("%s Invalid image mode %d", __func__, image_mode);
+		return -EINVAL;
+	}
+	/* Always reserve the buffer from user's video node */
+	pcam_inst = p_mctl->pcam_ptr->dev_inst[image_mode];
+	if (!pcam_inst) {
+		pr_err("%s Instance already closed ", __func__);
+		return -EINVAL;
+	}
+	rc = msm_mctl_reserve_free_buf(p_mctl, pcam_inst,
+					image_mode, &free_buf);
+	if (rc == 0) {
+		msm_mctl_pp_get_phy_addr(pcam_inst, free_buf.vb, &frame.frame);
+		if (copy_to_user((void *)arg, &frame, sizeof(frame))) {
+			ERR_COPY_TO_USER();
+			rc = -EFAULT;
+		}
+	}
+	D("%s: reserve free buf got buffer %d from %p rc = %d, phy = 0x%x",
+		__func__, frame.frame.buf_idx,
+		pcam_inst, rc, free_buf.ch_paddr[0]);
+	return rc;
+}
+
+int msm_mctl_pp_release_free_frame(
+	struct msm_cam_media_controller *p_mctl,
+	void __user *arg)
+{
+	struct msm_cam_evt_divert_frame div_frame;
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+	struct msm_pp_frame *frame;
+	int image_mode, rc = 0;
+	struct msm_free_buf free_buf;
+
+	if (copy_from_user(&div_frame, arg,
+		sizeof(struct msm_cam_evt_divert_frame)))
+		return -EFAULT;
+
+	image_mode = div_frame.image_mode;
+	if (image_mode < 0) {
+		pr_err("%s Invalid image mode %d\n", __func__, image_mode);
+		return -EINVAL;
+	}
+	frame = &div_frame.frame;
+	if (frame->num_planes > 1)
+		free_buf.ch_paddr[0] = frame->mp[0].phy_addr;
+	else
+		free_buf.ch_paddr[0] = frame->sp.phy_addr;
+
+	pcam_inst = msm_mctl_get_pcam_inst(p_mctl, image_mode);
+	if (!pcam_inst) {
+		pr_err("%s Invalid instance. Cannot release frame.\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	rc = msm_mctl_release_free_buf(p_mctl, pcam_inst,
+					image_mode, &free_buf);
+	D("%s: release free buf, rc = %d, phy = 0x%x",
+		__func__, rc, free_buf.ch_paddr[0]);
+
+	return rc;
+}
+
+int msm_mctl_set_pp_key(struct msm_cam_media_controller *p_mctl,
+				void __user *arg)
+{
+	int rc = 0;
+	unsigned long flags;
+	spin_lock_irqsave(&p_mctl->pp_info.lock, flags);
+	if (copy_from_user(&p_mctl->pp_info.pp_key,
+			arg, sizeof(p_mctl->pp_info.pp_key)))
+		rc = -EFAULT;
+	else
+		D("%s: mctl=0x%p, pp_key_setting=0x%x",
+			__func__, p_mctl, p_mctl->pp_info.pp_key);
+	spin_unlock_irqrestore(&p_mctl->pp_info.lock, flags);
+	return rc;
+}
+
+int msm_mctl_pp_done(
+	struct msm_cam_media_controller *p_mctl,
+	void __user *arg)
+{
+	struct msm_pp_frame frame;
+	int image_mode, rc = 0;
+	int dirty = 0;
+	struct msm_free_buf buf;
+	unsigned long flags;
+
+	if (copy_from_user(&frame, arg, sizeof(frame)))
+		return -EFAULT;
+
+	spin_lock_irqsave(&p_mctl->pp_info.lock, flags);
+	image_mode = msm_mctl_pp_path_to_img_mode(frame.path);
+	if (image_mode < 0) {
+		pr_err("%s Invalid image mode\n", __func__);
+		return image_mode;
+	}
+	D("%s Returning frame %x id %d to kernel ", __func__,
+		(int)frame.handle, frame.frame_id);
+	if (p_mctl->pp_info.div_frame[image_mode].ch_paddr[0]) {
+		memcpy(&buf,
+			&p_mctl->pp_info.div_frame[image_mode],
+			sizeof(buf));
+		memset(&p_mctl->pp_info.div_frame[image_mode],
+			0, sizeof(buf));
+		if (p_mctl->pp_info.cur_frame_id[image_mode] !=
+					frame.frame_id) {
+			/* dirty frame. should not pass to app */
+			dirty = 1;
+		}
+	} else {
+		if (frame.num_planes > 1)
+			buf.ch_paddr[0] = frame.mp[0].phy_addr +
+						frame.mp[0].data_offset;
+		else
+			buf.ch_paddr[0] = frame.sp.phy_addr + frame.sp.y_off;
+	}
+	spin_unlock_irqrestore(&p_mctl->pp_info.lock, flags);
+	/* here buf.addr is phy_addr */
+	rc = msm_mctl_buf_done_pp(p_mctl, image_mode, &buf, dirty, 0);
+	return rc;
+}
+
+int msm_mctl_pp_divert_done(
+	struct msm_cam_media_controller *p_mctl,
+	void __user *arg)
+{
+	struct msm_pp_frame frame;
+	int msg_type, image_mode, rc = 0;
+	int dirty = 0;
+	struct msm_free_buf buf;
+	unsigned long flags;
+	D("%s enter\n", __func__);
+
+	if (copy_from_user(&frame, arg, sizeof(frame)))
+		return -EFAULT;
+
+	spin_lock_irqsave(&p_mctl->pp_info.lock, flags);
+	D("%s Frame path: %d\n", __func__, frame.path);
+	switch (frame.path) {
+	case OUTPUT_TYPE_P:
+		msg_type = VFE_MSG_OUTPUT_P;
+		image_mode = MSM_V4L2_EXT_CAPTURE_MODE_PREVIEW;
+		break;
+	case OUTPUT_TYPE_S:
+		msg_type = VFE_MSG_OUTPUT_S;
+		image_mode = MSM_V4L2_EXT_CAPTURE_MODE_MAIN;
+		break;
+	case OUTPUT_TYPE_V:
+		msg_type = VFE_MSG_OUTPUT_V;
+		image_mode = MSM_V4L2_EXT_CAPTURE_MODE_VIDEO;
+		break;
+	case OUTPUT_TYPE_T:
+		msg_type = VFE_MSG_OUTPUT_T;
+		image_mode = MSM_V4L2_EXT_CAPTURE_MODE_THUMBNAIL;
+		break;
+	default:
+		rc = -EFAULT;
+		goto err;
+	}
+
+	if (frame.num_planes > 1)
+		buf.ch_paddr[0] = frame.mp[0].phy_addr +
+					frame.mp[0].data_offset;
+	else
+		buf.ch_paddr[0] = frame.sp.phy_addr + frame.sp.y_off;
+
+	spin_unlock_irqrestore(&p_mctl->pp_info.lock, flags);
+	D("%s Frame done id: %d\n", __func__, frame.frame_id);
+	rc = msm_mctl_buf_done_pp(p_mctl, image_mode,
+		&buf, dirty, frame.node_type);
+	return rc;
+err:
+	spin_unlock_irqrestore(&p_mctl->pp_info.lock, flags);
+	return rc;
+}
+
+int msm_mctl_pp_mctl_divert_done(
+	struct msm_cam_media_controller *p_mctl,
+	void __user *arg)
+{
+	struct msm_cam_evt_divert_frame div_frame;
+	struct msm_frame_buffer *buf;
+	int image_mode, rc = 0;
+
+	if (copy_from_user(&div_frame, arg,
+			sizeof(struct msm_cam_evt_divert_frame))) {
+		pr_err("%s copy from user failed ", __func__);
+		return -EFAULT;
+	}
+
+	if (!div_frame.frame.handle) {
+		pr_err("%s Invalid buffer handle ", __func__);
+		return -EINVAL;
+	}
+	image_mode = div_frame.image_mode;
+	buf = (struct msm_frame_buffer *)div_frame.frame.handle;
+	D("%s Returning buffer %x Image mode %d ", __func__,
+		(int)buf, image_mode);
+	rc = msm_mctl_buf_return_buf(p_mctl, image_mode, buf);
+	if (rc < 0)
+		pr_err("%s Error returning mctl buffer ", __func__);
+
+	return rc;
+}
diff --git a/drivers/media/video/msm/msm_mem.c b/drivers/media/video/msm/msm_mem.c
new file mode 100644
index 0000000..0043f72
--- /dev/null
+++ b/drivers/media/video/msm/msm_mem.c
@@ -0,0 +1,416 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/ioctl.h>
+#include <linux/spinlock.h>
+#include <linux/videodev2.h>
+#include <linux/proc_fs.h>
+#include <linux/vmalloc.h>
+
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+
+#include <linux/android_pmem.h>
+
+#include "msm.h"
+
+#ifdef CONFIG_MSM_CAMERA_DEBUG
+#define D(fmt, args...) pr_debug("msm_isp: " fmt, ##args)
+#else
+#define D(fmt, args...) do {} while (0)
+#endif
+
+#define PAD_TO_WORD(a)	  (((a) + 3) & ~3)
+
+#define __CONTAINS(r, v, l, field) ({			   \
+	typeof(r) __r = r;				  \
+	typeof(v) __v = v;				  \
+	typeof(v) __e = __v + l;				\
+	int res = __v >= __r->field &&			  \
+		__e <= __r->field + __r->len;		   \
+	res;							\
+})
+
+#define CONTAINS(r1, r2, field) ({			  \
+	typeof(r2) __r2 = r2;				   \
+	__CONTAINS(r1, __r2->field, __r2->len, field);	  \
+})
+
+#define IN_RANGE(r, v, field) ({				\
+	typeof(r) __r = r;				  \
+	typeof(v) __vv = v;				 \
+	int res = ((__vv >= __r->field) &&		  \
+		(__vv < (__r->field + __r->len)));	  \
+	res;							\
+})
+
+#define OVERLAPS(r1, r2, field) ({			  \
+	typeof(r1) __r1 = r1;				   \
+	typeof(r2) __r2 = r2;				   \
+	typeof(__r2->field) __v = __r2->field;		  \
+	typeof(__v) __e = __v + __r2->len - 1;		  \
+	int res = (IN_RANGE(__r1, __v, field) ||		\
+		IN_RANGE(__r1, __e, field));				 \
+	res;							\
+})
+
+static DEFINE_MUTEX(hlist_mut);
+
+#ifdef CONFIG_ANDROID_PMEM
+static int check_pmem_info(struct msm_pmem_info *info, int len)
+{
+	if (info->offset < len &&
+		info->offset + info->len <= len &&
+		info->planar0_off < len &&
+		info->planar1_off < len)
+		return 0;
+
+	pr_err("%s: check failed: off %d len %d y %d cbcr %d (total len %d)\n",
+						__func__,
+						info->offset,
+						info->len,
+						info->planar0_off,
+						info->planar1_off,
+						len);
+	return -EINVAL;
+}
+#endif
+
+static int check_overlap(struct hlist_head *ptype,
+				unsigned long paddr,
+				unsigned long len)
+{
+	struct msm_pmem_region *region;
+	struct msm_pmem_region t = { .paddr = paddr, .len = len };
+	struct hlist_node *node;
+
+	hlist_for_each_entry(region, node, ptype, list) {
+		if (CONTAINS(region, &t, paddr) ||
+			CONTAINS(&t, region, paddr) ||
+			OVERLAPS(region, &t, paddr)) {
+			CDBG(" region (PHYS %p len %ld)"
+				" clashes with registered region"
+				" (paddr %p len %ld)\n",
+				(void *)t.paddr, t.len,
+				(void *)region->paddr, region->len);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int msm_pmem_table_add(struct hlist_head *ptype,
+	struct msm_pmem_info *info, struct ion_client *client)
+{
+	unsigned long paddr;
+#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION
+	unsigned long kvstart;
+	struct file *file;
+#endif
+	int rc = -ENOMEM;
+
+	unsigned long len;
+	struct msm_pmem_region *region;
+
+	region = kmalloc(sizeof(struct msm_pmem_region), GFP_KERNEL);
+	if (!region)
+		goto out;
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+	region->handle = ion_import_fd(client, info->fd);
+	if (IS_ERR_OR_NULL(region->handle))
+		goto out1;
+	if (ion_map_iommu(client, region->handle, CAMERA_DOMAIN, GEN_POOL,
+				  SZ_4K, 0, &paddr, &len, UNCACHED, 0) < 0)
+		goto out2;
+#elif CONFIG_ANDROID_PMEM
+	rc = get_pmem_file(info->fd, &paddr, &kvstart, &len, &file);
+	if (rc < 0) {
+		pr_err("%s: get_pmem_file fd %d error %d\n",
+				__func__, info->fd, rc);
+		goto out1;
+	}
+	region->file = file;
+#else
+	paddr = 0;
+	file = NULL;
+	kvstart = 0;
+#endif
+	if (!info->len)
+		info->len = len;
+	rc = check_pmem_info(info, len);
+	if (rc < 0)
+		goto out3;
+	paddr += info->offset;
+	len = info->len;
+
+	if (check_overlap(ptype, paddr, len) < 0) {
+		rc = -EINVAL;
+		goto out3;
+	}
+
+	CDBG("%s: type %d, active flag %d, paddr 0x%lx, vaddr 0x%lx\n",
+		__func__, info->type, info->active, paddr,
+		(unsigned long)info->vaddr);
+
+	INIT_HLIST_NODE(&region->list);
+	region->paddr = paddr;
+	region->len = len;
+	memcpy(&region->info, info, sizeof(region->info));
+	D("%s Adding region to list with type %d\n", __func__,
+						region->info.type);
+	D("%s pmem_stats address is 0x%p\n", __func__, ptype);
+	hlist_add_head(&(region->list), ptype);
+
+	return 0;
+out3:
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+	ion_unmap_iommu(client, region->handle, CAMERA_DOMAIN, GEN_POOL);
+#endif
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+out2:
+	ion_free(client, region->handle);
+#elif CONFIG_ANDROID_PMEM
+	put_pmem_file(region->file);
+#endif
+out1:
+	kfree(region);
+out:
+	return rc;
+}
+
+static int __msm_register_pmem(struct hlist_head *ptype,
+			struct msm_pmem_info *pinfo, struct ion_client *client)
+{
+	int rc = 0;
+
+	switch (pinfo->type) {
+	case MSM_PMEM_AF:
+	case MSM_PMEM_AEC:
+	case MSM_PMEM_AWB:
+	case MSM_PMEM_RS:
+	case MSM_PMEM_CS:
+	case MSM_PMEM_IHIST:
+	case MSM_PMEM_SKIN:
+	case MSM_PMEM_AEC_AWB:
+		rc = msm_pmem_table_add(ptype, pinfo, client);
+		break;
+
+	default:
+		rc = -EINVAL;
+		break;
+	}
+
+	return rc;
+}
+
+static int __msm_pmem_table_del(struct hlist_head *ptype,
+			struct msm_pmem_info *pinfo, struct ion_client *client)
+{
+	int rc = 0;
+	struct msm_pmem_region *region;
+	struct hlist_node *node, *n;
+
+	switch (pinfo->type) {
+	case MSM_PMEM_AF:
+	case MSM_PMEM_AEC:
+	case MSM_PMEM_AWB:
+	case MSM_PMEM_RS:
+	case MSM_PMEM_CS:
+	case MSM_PMEM_IHIST:
+	case MSM_PMEM_SKIN:
+	case MSM_PMEM_AEC_AWB:
+		hlist_for_each_entry_safe(region, node, n,
+				ptype, list) {
+
+			if (pinfo->type == region->info.type &&
+				pinfo->vaddr == region->info.vaddr &&
+				pinfo->fd == region->info.fd) {
+				hlist_del(node);
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+				ion_unmap_iommu(client, region->handle,
+					CAMERA_DOMAIN, GEN_POOL);
+				ion_free(client, region->handle);
+#else
+				put_pmem_file(region->file);
+#endif
+				kfree(region);
+			}
+		}
+		break;
+
+	default:
+		rc = -EINVAL;
+		break;
+	}
+
+	return rc;
+}
+
+/* return of 0 means failure */
+uint8_t msm_pmem_region_lookup(struct hlist_head *ptype,
+	int pmem_type, struct msm_pmem_region *reg, uint8_t maxcount)
+{
+	struct msm_pmem_region *region;
+	struct msm_pmem_region *regptr;
+	struct hlist_node *node, *n;
+
+	uint8_t rc = 0;
+	D("%s\n", __func__);
+	regptr = reg;
+	mutex_lock(&hlist_mut);
+	hlist_for_each_entry_safe(region, node, n, ptype, list) {
+		if (region->info.type == pmem_type && region->info.active) {
+			*regptr = *region;
+			rc += 1;
+			if (rc >= maxcount)
+				break;
+			regptr++;
+		}
+	}
+	D("%s finished, rc=%d\n", __func__, rc);
+	mutex_unlock(&hlist_mut);
+	return rc;
+}
+
+int msm_pmem_region_get_phy_addr(struct hlist_head *ptype,
+	struct msm_mem_map_info *mem_map, int32_t *phyaddr)
+{
+	struct msm_pmem_region *region;
+	struct hlist_node *node, *n;
+	int pmem_type = mem_map->mem_type;
+	int rc = -EFAULT;
+
+	D("%s\n", __func__);
+	*phyaddr = 0;
+	mutex_lock(&hlist_mut);
+	hlist_for_each_entry_safe(region, node, n, ptype, list) {
+		if (region->info.type == pmem_type &&
+			(uint32_t)region->info.vaddr == mem_map->cookie) {
+			*phyaddr = (int32_t)region->paddr;
+			rc = 0;
+			break;
+		}
+	}
+	D("%s finished, phy_addr = 0x%x, rc=%d\n", __func__, *phyaddr, rc);
+	mutex_unlock(&hlist_mut);
+	return rc;
+}
+
+uint8_t msm_pmem_region_lookup_2(struct hlist_head *ptype,
+					int pmem_type,
+					struct msm_pmem_region *reg,
+					uint8_t maxcount)
+{
+	struct msm_pmem_region *region;
+	struct msm_pmem_region *regptr;
+	struct hlist_node *node, *n;
+	uint8_t rc = 0;
+	regptr = reg;
+	mutex_lock(&hlist_mut);
+	hlist_for_each_entry_safe(region, node, n, ptype, list) {
+		D("Mio: info.type=%d, pmem_type = %d,"
+						"info.active = %d\n",
+		region->info.type, pmem_type, region->info.active);
+
+		if (region->info.type == pmem_type && region->info.active) {
+			D("info.type=%d, pmem_type = %d,"
+							"info.active = %d,\n",
+				region->info.type, pmem_type,
+				region->info.active);
+			*regptr = *region;
+			region->info.type = MSM_PMEM_VIDEO;
+			rc += 1;
+			if (rc >= maxcount)
+				break;
+			regptr++;
+		}
+	}
+	mutex_unlock(&hlist_mut);
+	return rc;
+}
+
+unsigned long msm_pmem_stats_vtop_lookup(
+				struct msm_cam_media_controller *mctl,
+				unsigned long buffer,
+				int fd)
+{
+	struct msm_pmem_region *region;
+	struct hlist_node *node, *n;
+
+	hlist_for_each_entry_safe(region, node, n,
+	&mctl->stats_info.pmem_stats_list, list) {
+		if (((unsigned long)(region->info.vaddr) == buffer) &&
+						(region->info.fd == fd) &&
+						region->info.active == 0) {
+			region->info.active = 1;
+			return region->paddr;
+		}
+	}
+
+	return 0;
+}
+
+unsigned long msm_pmem_stats_ptov_lookup(
+		struct msm_cam_media_controller *mctl,
+		unsigned long addr, int *fd)
+{
+	struct msm_pmem_region *region;
+	struct hlist_node *node, *n;
+
+	hlist_for_each_entry_safe(region, node, n,
+	&mctl->stats_info.pmem_stats_list, list) {
+		if (addr == region->paddr && region->info.active) {
+			/* offset since we could pass vaddr inside a
+			 * registered pmem buffer */
+			*fd = region->info.fd;
+			region->info.active = 0;
+			return (unsigned long)(region->info.vaddr);
+		}
+	}
+
+	return 0;
+}
+
+int msm_register_pmem(struct hlist_head *ptype, void __user *arg,
+					  struct ion_client *client)
+{
+	struct msm_pmem_info info;
+
+	if (copy_from_user(&info, arg, sizeof(info))) {
+		ERR_COPY_FROM_USER();
+			return -EFAULT;
+	}
+
+	return __msm_register_pmem(ptype, &info, client);
+}
+//EXPORT_SYMBOL(msm_register_pmem);
+
+int msm_pmem_table_del(struct hlist_head *ptype, void __user *arg,
+					   struct ion_client *client)
+{
+	struct msm_pmem_info info;
+
+	if (copy_from_user(&info, arg, sizeof(info))) {
+		ERR_COPY_FROM_USER();
+		return -EFAULT;
+	}
+
+	return __msm_pmem_table_del(ptype, &info, client);
+}
+//EXPORT_SYMBOL(msm_pmem_table_del);
diff --git a/drivers/media/video/msm/msm_v4l2_video.c b/drivers/media/video/msm/msm_v4l2_video.c
new file mode 100644
index 0000000..c4e6c62
--- /dev/null
+++ b/drivers/media/video/msm/msm_v4l2_video.c
@@ -0,0 +1,947 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/fb.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/time.h>
+#include <linux/videodev2.h>
+#include <linux/platform_device.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/msm_mdp.h>
+#include <linux/sched.h>
+#include <linux/capability.h>
+
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf-dma-sg.h>
+#include <media/v4l2-dev.h>
+#include <media/msm_v4l2_overlay.h>
+
+#include <mach/board.h>
+#include <mach/msm_fb.h>
+
+#include "msm_v4l2_video.h"
+
+#define MSM_VIDEO -1
+
+static struct msm_v4l2_overlay_device	*saved_vout0;
+
+static struct mutex msmfb_lock;
+static char *v4l2_ram_phys;
+static unsigned int v4l2_ram_size;
+
+static int msm_v4l2_overlay_mapformat(uint32_t pixelformat);
+
+static int msm_v4l2_overlay_startstreaming(struct msm_v4l2_overlay_device *vout)
+{
+	vout->req.src.width = vout->pix.width;
+	vout->req.src.height = vout->pix.height;
+
+	vout->req.src_rect.x = vout->crop_rect.left;
+	vout->req.src_rect.y = vout->crop_rect.top;
+	vout->req.src_rect.w = vout->crop_rect.width;
+	vout->req.src_rect.h = vout->crop_rect.height;
+
+	vout->req.src.format =
+		msm_v4l2_overlay_mapformat(vout->pix.pixelformat);
+
+	vout->req.dst_rect.x = vout->win.w.left;
+	vout->req.dst_rect.y = vout->win.w.top;
+	vout->req.dst_rect.w = vout->win.w.width;
+	vout->req.dst_rect.h = vout->win.w.height;
+
+	vout->req.alpha = MDP_ALPHA_NOP;
+	vout->req.transp_mask = MDP_TRANSP_NOP;
+
+	pr_debug("msm_v4l2_overlay:startstreaming:enabling fb\n");
+	mutex_lock(&msmfb_lock);
+	msm_fb_v4l2_enable(&vout->req, true, &vout->par);
+	mutex_unlock(&msmfb_lock);
+
+	vout->streaming = 1;
+
+	return 0;
+}
+
+static int msm_v4l2_overlay_stopstreaming(struct msm_v4l2_overlay_device *vout)
+{
+	if (!vout->streaming)
+		return 0;
+
+	pr_debug("msm_v4l2_overlay:startstreaming:disabling fb\n");
+	mutex_lock(&msmfb_lock);
+	msm_fb_v4l2_enable(&vout->req, false, &vout->par);
+	mutex_unlock(&msmfb_lock);
+
+	vout->streaming = 0;
+
+	return 0;
+}
+
+static int msm_v4l2_overlay_mapformat(uint32_t pixelformat)
+{
+	int mdp_format;
+
+	switch (pixelformat) {
+	case V4L2_PIX_FMT_RGB565:
+		mdp_format = MDP_RGB_565;
+		break;
+	case V4L2_PIX_FMT_RGB32:
+		mdp_format = MDP_ARGB_8888;
+		break;
+	case V4L2_PIX_FMT_RGB24:
+		mdp_format = MDP_RGB_888;
+		break;
+	case V4L2_PIX_FMT_NV12:
+		mdp_format = MDP_Y_CRCB_H2V2;
+		break;
+	case V4L2_PIX_FMT_NV21:
+		mdp_format = MDP_Y_CBCR_H2V2;
+		break;
+	case V4L2_PIX_FMT_YUV420:
+		mdp_format = MDP_Y_CR_CB_H2V2;
+		break;
+	default:
+		pr_err("%s:Unrecognized format %u\n", __func__, pixelformat);
+		mdp_format = MDP_Y_CBCR_H2V2;
+		break;
+	}
+
+	return mdp_format;
+}
+
+static int
+msm_v4l2_overlay_fb_update(struct msm_v4l2_overlay_device *vout,
+	struct v4l2_buffer *buffer)
+{
+	int ret;
+	unsigned long src_addr, src_size;
+	struct msm_v4l2_overlay_userptr_buffer up_buffer;
+
+	if (!buffer ||
+		(buffer->memory == V4L2_MEMORY_MMAP &&
+		 buffer->index >= vout->numbufs))
+		return -EINVAL;
+
+	mutex_lock(&msmfb_lock);
+	switch (buffer->memory) {
+	case V4L2_MEMORY_MMAP:
+		src_addr = (unsigned long)v4l2_ram_phys
+		+ vout->bufs[buffer->index].offset;
+		src_size = buffer->bytesused;
+		ret = msm_fb_v4l2_update(vout->par, src_addr, src_size,
+		0, 0, 0, 0);
+		break;
+	case V4L2_MEMORY_USERPTR:
+		if (copy_from_user(&up_buffer,
+		(void __user *)buffer->m.userptr,
+		sizeof(struct msm_v4l2_overlay_userptr_buffer))) {
+			mutex_unlock(&msmfb_lock);
+			return -EINVAL;
+		}
+		ret = msm_fb_v4l2_update(vout->par,
+		(unsigned long)up_buffer.base[0], up_buffer.length[0],
+		(unsigned long)up_buffer.base[1], up_buffer.length[1],
+		(unsigned long)up_buffer.base[2], up_buffer.length[2]);
+		break;
+	default:
+		mutex_unlock(&msmfb_lock);
+		return -EINVAL;
+	}
+	mutex_unlock(&msmfb_lock);
+
+	if (buffer->memory == V4L2_MEMORY_MMAP) {
+		vout->bufs[buffer->index].queued = 1;
+		buffer->flags |= V4L2_BUF_FLAG_MAPPED;
+	}
+	buffer->flags |= V4L2_BUF_FLAG_QUEUED;
+
+	return ret;
+}
+
+static int
+msm_v4l2_overlay_vidioc_dqbuf(struct file *file,
+	struct msm_v4l2_overlay_fh *fh, void *arg)
+{
+	struct msm_v4l2_overlay_device *vout = fh->vout;
+	struct v4l2_buffer *buffer = arg;
+	int i;
+
+	if (!vout->streaming) {
+		pr_err("%s: Video Stream not enabled\n", __func__);
+		return -EINVAL;
+	}
+
+	if (!buffer || buffer->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+		return -EINVAL;
+
+	if (buffer->memory == V4L2_MEMORY_MMAP) {
+		for (i = 0; i < vout->numbufs; i++) {
+			if (vout->bufs[i].queued == 1)  {
+				vout->bufs[i].queued = 0;
+				/* Call into fb to remove this buffer? */
+				break;
+			}
+		}
+
+		/*
+		 * This should actually block, unless O_NONBLOCK was
+		 *  specified in open, but fine for now, especially
+		 *  since this is not a capturing device
+		 */
+		if (i == vout->numbufs)
+			return -EAGAIN;
+	}
+
+	buffer->flags &= ~V4L2_BUF_FLAG_QUEUED;
+
+	return 0;
+}
+
+
+static int
+msm_v4l2_overlay_vidioc_qbuf(struct file *file, struct msm_v4l2_overlay_fh* fh,
+	void *arg, bool bUserPtr)
+{
+	struct msm_v4l2_overlay_device *vout = fh->vout;
+	struct v4l2_buffer *buffer = arg;
+	int ret;
+
+	if (!bUserPtr && buffer->memory != V4L2_MEMORY_MMAP)
+		return -EINVAL;
+
+	if (!vout->streaming) {
+		pr_err("%s: Video Stream not enabled\n", __func__);
+		return -EINVAL;
+	}
+
+	if (!buffer || buffer->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+		return -EINVAL;
+
+	/* maybe allow only one qbuf at a time? */
+	ret =  msm_v4l2_overlay_fb_update(vout, buffer);
+
+	return 0;
+}
+
+static int
+msm_v4l2_overlay_vidioc_querycap(struct file *file, void *arg)
+{
+	struct v4l2_capability *buffer = arg;
+
+	memset(buffer, 0, sizeof(struct v4l2_capability));
+	strlcpy(buffer->driver, "msm_v4l2_video_overlay",
+		ARRAY_SIZE(buffer->driver));
+	strlcpy(buffer->card, "MSM MDP",
+		ARRAY_SIZE(buffer->card));
+	buffer->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT
+		| V4L2_CAP_VIDEO_OVERLAY;
+	return 0;
+}
+
+static int
+msm_v4l2_overlay_vidioc_fbuf(struct file *file,
+	struct msm_v4l2_overlay_device *vout, void *arg, bool get)
+{
+	struct v4l2_framebuffer *fb = arg;
+
+	if (fb == NULL)
+		return -EINVAL;
+
+	if (get) {
+		mutex_lock(&vout->update_lock);
+		memcpy(&fb->fmt, &vout->pix, sizeof(struct v4l2_pix_format));
+		mutex_unlock(&vout->update_lock);
+	}
+	/* The S_FBUF request does not store anything right now */
+	return 0;
+}
+
+static long msm_v4l2_overlay_calculate_bufsize(struct v4l2_pix_format *pix)
+{
+	int bpp;
+	long bufsize;
+	switch (pix->pixelformat) {
+	case V4L2_PIX_FMT_YUV420:
+	case V4L2_PIX_FMT_NV12:
+		bpp = 12;
+		break;
+
+	case V4L2_PIX_FMT_RGB565:
+		bpp = 16;
+		break;
+
+	case V4L2_PIX_FMT_RGB24:
+	case V4L2_PIX_FMT_BGR24:
+	case V4L2_PIX_FMT_YUV444:
+		bpp = 24;
+		break;
+
+	case V4L2_PIX_FMT_RGB32:
+	case V4L2_PIX_FMT_BGR32:
+		bpp = 32;
+		break;
+	default:
+		pr_err("%s: Unrecognized format %u\n", __func__,
+		pix->pixelformat);
+		bpp = 0;
+	}
+
+	bufsize = (pix->width * pix->height * bpp)/8;
+
+	return bufsize;
+}
+
+static long
+msm_v4l2_overlay_vidioc_reqbufs(struct file *file,
+	struct msm_v4l2_overlay_device *vout, void *arg)
+
+{
+	struct v4l2_requestbuffers *rqb = arg;
+	long bufsize;
+	int i;
+
+	if (rqb == NULL || rqb->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+		return -EINVAL;
+
+	if (rqb->memory == V4L2_MEMORY_MMAP) {
+		if (rqb->count == 0) {
+			/* Deallocate allocated buffers */
+			mutex_lock(&vout->update_lock);
+			vout->numbufs = 0;
+			kfree(vout->bufs);
+			/*
+			 * There should be a way to look at bufs[i]->mapped,
+			 * and prevent userspace from mmaping and directly
+			 * calling this ioctl without unmapping. Maybe kernel
+			 * handles for us, but needs to be checked out
+			 */
+			mutex_unlock(&vout->update_lock);
+		} else {
+			/*
+			 * Keep it simple for now - need to deallocate
+			 * before reallocate
+			 */
+			if (vout->bufs)
+				return -EINVAL;
+
+			mutex_lock(&vout->update_lock);
+			bufsize =
+				msm_v4l2_overlay_calculate_bufsize(&vout->pix);
+			mutex_unlock(&vout->update_lock);
+
+			if (bufsize == 0
+				|| (bufsize * rqb->count) > v4l2_ram_size) {
+				pr_err("%s: Unsupported format or buffer size too large\n",
+				__func__);
+				pr_err("%s: bufsize %lu ram_size %u count %u\n",
+				__func__, bufsize, v4l2_ram_size, rqb->count);
+				return -EINVAL;
+			}
+
+			/*
+			 * We don't support multiple open of one vout,
+			 * but there are probably still some MT problems here,
+			 * (what if same fh is shared between two userspace
+			 * threads and they both call REQBUFS etc)
+			 */
+
+			mutex_lock(&vout->update_lock);
+			vout->numbufs = rqb->count;
+			vout->bufs =
+				kmalloc(rqb->count *
+					sizeof(struct msm_v4l2_overlay_buffer),
+					GFP_KERNEL);
+
+			for (i = 0; i < rqb->count; i++) {
+				struct msm_v4l2_overlay_buffer *b =
+				(struct msm_v4l2_overlay_buffer *)vout->bufs
+				+ i;
+				b->mapped = 0;
+				b->queued = 0;
+				b->offset = PAGE_ALIGN(bufsize*i);
+				b->bufsize = bufsize;
+			}
+
+			mutex_unlock(&vout->update_lock);
+
+		}
+	}
+
+	return 0;
+}
+
+static long
+msm_v4l2_overlay_vidioc_querybuf(struct file *file,
+				 struct msm_v4l2_overlay_device *vout,
+				 void *arg)
+{
+	struct v4l2_buffer *buf = arg;
+	struct msm_v4l2_overlay_buffer *mbuf;
+
+	if (buf == NULL || buf->type != V4L2_BUF_TYPE_VIDEO_OUTPUT
+			|| buf->memory == V4L2_MEMORY_USERPTR
+			|| buf->index >= vout->numbufs)
+		return -EINVAL;
+
+	mutex_lock(&vout->update_lock);
+
+	mbuf = (struct msm_v4l2_overlay_buffer *)vout->bufs + buf->index;
+	buf->flags = 0;
+	if (mbuf->mapped)
+		buf->flags |= V4L2_BUF_FLAG_MAPPED;
+	if (mbuf->queued)
+		buf->flags |= V4L2_BUF_FLAG_QUEUED;
+
+	buf->memory = V4L2_MEMORY_MMAP;
+	buf->length = mbuf->bufsize;
+	buf->m.offset = mbuf->offset;
+
+	mutex_unlock(&vout->update_lock);
+
+	return 0;
+}
+
+static long
+msm_v4l2_overlay_do_ioctl(struct file *file,
+		       unsigned int cmd, void *arg)
+{
+	struct msm_v4l2_overlay_fh *fh = file->private_data;
+	struct msm_v4l2_overlay_device *vout = fh->vout;
+	int ret;
+
+	switch (cmd) {
+	case VIDIOC_QUERYCAP:
+		return msm_v4l2_overlay_vidioc_querycap(file, arg);
+
+	case VIDIOC_G_FBUF:
+		return msm_v4l2_overlay_vidioc_fbuf(file, vout, arg, true);
+
+	case VIDIOC_S_FBUF:
+		return msm_v4l2_overlay_vidioc_fbuf(file, vout, arg, false);
+
+	case VIDIOC_REQBUFS:
+		return msm_v4l2_overlay_vidioc_reqbufs(file, vout, arg);
+
+	case VIDIOC_QUERYBUF:
+		return msm_v4l2_overlay_vidioc_querybuf(file, vout, arg);
+
+	case VIDIOC_QBUF:
+		mutex_lock(&vout->update_lock);
+		ret = msm_v4l2_overlay_vidioc_qbuf(file, fh, arg, false);
+		mutex_unlock(&vout->update_lock);
+
+		return ret;
+
+	case VIDIOC_MSM_USERPTR_QBUF:
+		if (!capable(CAP_SYS_RAWIO))
+			return -EPERM;
+
+		mutex_lock(&vout->update_lock);
+		ret = msm_v4l2_overlay_vidioc_qbuf(file, fh, arg, true);
+		mutex_unlock(&vout->update_lock);
+
+		return ret;
+
+	case VIDIOC_DQBUF:
+		mutex_lock(&vout->update_lock);
+		ret = msm_v4l2_overlay_vidioc_dqbuf(file, fh, arg);
+		mutex_unlock(&vout->update_lock);
+		break;
+
+	case VIDIOC_S_FMT: {
+		struct v4l2_format *f = arg;
+
+		switch (f->type) {
+		case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+			mutex_lock(&vout->update_lock);
+			memcpy(&vout->win, &f->fmt.win,
+				sizeof(struct v4l2_window));
+			mutex_unlock(&vout->update_lock);
+			break;
+
+		case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+			mutex_lock(&vout->update_lock);
+			memcpy(&vout->pix, &f->fmt.pix,
+				sizeof(struct v4l2_pix_format));
+			mutex_unlock(&vout->update_lock);
+			break;
+
+		default:
+			return -EINVAL;
+		}
+		break;
+	}
+	case VIDIOC_G_FMT: {
+		struct v4l2_format *f = arg;
+
+		switch (f->type) {
+		case V4L2_BUF_TYPE_VIDEO_OUTPUT: {
+			struct v4l2_pix_format *pix = &f->fmt.pix;
+			memset(pix, 0, sizeof(*pix));
+			*pix = vout->pix;
+			break;
+		}
+
+		case V4L2_BUF_TYPE_VIDEO_OVERLAY: {
+			struct v4l2_window *win = &f->fmt.win;
+			memset(win, 0, sizeof(*win));
+			win->w = vout->win.w;
+			break;
+		}
+		default:
+			return -EINVAL;
+		}
+		break;
+	}
+
+	case VIDIOC_CROPCAP: {
+		struct v4l2_cropcap *cr = arg;
+		if (cr->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+			return -EINVAL;
+
+		cr->bounds.left =  0;
+		cr->bounds.top = 0;
+		cr->bounds.width = vout->crop_rect.width;
+		cr->bounds.height = vout->crop_rect.height;
+
+		cr->defrect.left =  0;
+		cr->defrect.top = 0;
+		cr->defrect.width = vout->crop_rect.width;
+		cr->defrect.height = vout->crop_rect.height;
+
+		cr->pixelaspect.numerator = 1;
+		cr->pixelaspect.denominator = 1;
+		break;
+	}
+
+	case VIDIOC_S_CROP: {
+		struct v4l2_crop *crop = arg;
+
+		switch (crop->type) {
+
+		case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+
+			mutex_lock(&vout->update_lock);
+			memcpy(&vout->crop_rect, &crop->c,
+				sizeof(struct v4l2_rect));
+			mutex_unlock(&vout->update_lock);
+
+			break;
+
+		default:
+
+			return -EINVAL;
+		}
+		break;
+	}
+	case VIDIOC_G_CROP: {
+		struct v4l2_crop *crop = arg;
+
+		switch (crop->type) {
+
+		case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+			memcpy(&crop->c, &vout->crop_rect,
+				sizeof(struct v4l2_rect));
+			break;
+
+		default:
+			return -EINVAL;
+		}
+		break;
+	}
+
+	case VIDIOC_S_CTRL: {
+		struct v4l2_control *ctrl = arg;
+		int32_t rotflag;
+
+		switch (ctrl->id) {
+
+		case V4L2_CID_ROTATE:
+			switch (ctrl->value) {
+			case 0:
+				rotflag = MDP_ROT_NOP;
+				break;
+			case 90:
+				rotflag = MDP_ROT_90;
+				break;
+			case 180:
+				rotflag = MDP_ROT_180;
+				break;
+			case 270:
+				rotflag = MDP_ROT_270;
+				break;
+			default:
+				pr_err("%s: V4L2_CID_ROTATE invalid rotation value %d.\n",
+						__func__, ctrl->value);
+				return -ERANGE;
+			}
+
+			mutex_lock(&vout->update_lock);
+			/* Clear the rotation flags */
+			vout->req.flags &= ~MDP_ROT_NOP;
+			vout->req.flags &= ~MDP_ROT_90;
+			vout->req.flags &= ~MDP_ROT_180;
+			vout->req.flags &= ~MDP_ROT_270;
+			/* Set the new rotation flag */
+			vout->req.flags |= rotflag;
+			mutex_unlock(&vout->update_lock);
+
+			break;
+
+		case V4L2_CID_HFLIP:
+			mutex_lock(&vout->update_lock);
+			/* Clear the flip flag */
+			vout->req.flags &= ~MDP_FLIP_LR;
+			if (true == ctrl->value)
+				vout->req.flags |= MDP_FLIP_LR;
+			mutex_unlock(&vout->update_lock);
+
+			break;
+
+		case V4L2_CID_VFLIP:
+			mutex_lock(&vout->update_lock);
+			/* Clear the flip flag */
+			vout->req.flags &= ~MDP_FLIP_UD;
+			if (true == ctrl->value)
+				vout->req.flags |= MDP_FLIP_UD;
+			mutex_unlock(&vout->update_lock);
+
+			break;
+
+		default:
+			pr_err("%s: VIDIOC_S_CTRL invalid control ID %d.\n",
+			__func__, ctrl->id);
+			return -EINVAL;
+		}
+		break;
+	}
+	case VIDIOC_G_CTRL: {
+		struct v4l2_control *ctrl = arg;
+		__s32 rotation;
+
+		switch (ctrl->id) {
+
+		case V4L2_CID_ROTATE:
+			if (MDP_ROT_NOP == (vout->req.flags & MDP_ROT_NOP))
+				rotation = 0;
+			if (MDP_ROT_90 == (vout->req.flags & MDP_ROT_90))
+				rotation = 90;
+			if (MDP_ROT_180 == (vout->req.flags & MDP_ROT_180))
+				rotation = 180;
+			if (MDP_ROT_270 == (vout->req.flags & MDP_ROT_270))
+				rotation = 270;
+
+			ctrl->value = rotation;
+			break;
+
+		case V4L2_CID_HFLIP:
+			if (MDP_FLIP_LR == (vout->req.flags & MDP_FLIP_LR))
+				ctrl->value = true;
+			break;
+
+		case V4L2_CID_VFLIP:
+			if (MDP_FLIP_UD == (vout->req.flags & MDP_FLIP_UD))
+				ctrl->value = true;
+			break;
+
+		default:
+			pr_err("%s: VIDIOC_G_CTRL invalid control ID %d.\n",
+			__func__, ctrl->id);
+			return -EINVAL;
+		}
+		break;
+	}
+
+	case VIDIOC_STREAMON: {
+
+		if (vout->streaming) {
+			pr_err("%s: VIDIOC_STREAMON: already streaming.\n",
+			__func__);
+			return -EBUSY;
+		}
+
+		mutex_lock(&vout->update_lock);
+		msm_v4l2_overlay_startstreaming(vout);
+		mutex_unlock(&vout->update_lock);
+
+		break;
+	}
+
+	case VIDIOC_STREAMOFF: {
+
+		if (!vout->streaming) {
+			pr_err("%s: VIDIOC_STREAMOFF: not currently streaming.\n",
+			__func__);
+			return -EINVAL;
+		}
+
+		mutex_lock(&vout->update_lock);
+		msm_v4l2_overlay_stopstreaming(vout);
+		mutex_unlock(&vout->update_lock);
+
+		break;
+	}
+
+	default:
+		return -ENOIOCTLCMD;
+
+	} /* switch */
+
+	return 0;
+}
+
+static long
+msm_v4l2_overlay_ioctl(struct file *file, unsigned int cmd,
+		    unsigned long arg)
+{
+	return video_usercopy(file, cmd, arg, msm_v4l2_overlay_do_ioctl);
+}
+
+static int
+msm_v4l2_overlay_mmap(struct file *filp, struct vm_area_struct * vma)
+{
+	unsigned long start = (unsigned long)v4l2_ram_phys;
+
+	/*
+	 * vm_pgoff is the offset (>>PAGE_SHIFT) that we provided
+	 * during REQBUFS. off therefore should equal the offset we
+	 * provided in REQBUFS, since last (PAGE_SHIFT) bits of off
+	 * should be 0
+	 */
+	unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
+	u32 len = PAGE_ALIGN((start & ~PAGE_MASK) + v4l2_ram_size);
+
+	/*
+	 * This is probably unnecessary now - the last PAGE_SHIFT
+	 * bits of start should be 0 now, since we are page aligning
+	 * v4l2_ram_phys
+	 */
+	start &= PAGE_MASK;
+
+	pr_debug("v4l2 map req for phys(%p,%p) offset %u to virt (%p,%p)\n",
+	(void *)(start+off), (void *)(start+off+(vma->vm_end - vma->vm_start)),
+	(unsigned int)off, (void *)vma->vm_start, (void *)vma->vm_end);
+
+	if ((vma->vm_end - vma->vm_start + off) > len) {
+		pr_err("v4l2 map request, memory requested too big\n");
+		return -EINVAL;
+	}
+
+	start += off;
+	vma->vm_pgoff = start >> PAGE_SHIFT;
+	/* This is an IO map - tell maydump to skip this VMA */
+	vma->vm_flags |= VM_IO | VM_RESERVED;
+
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+	/* Remap the frame buffer I/O range */
+	if (io_remap_pfn_range(vma, vma->vm_start, start >> PAGE_SHIFT,
+				vma->vm_end - vma->vm_start,
+				vma->vm_page_prot))
+		return -EAGAIN;
+
+	return 0;
+}
+
+static int
+msm_v4l2_overlay_release(struct file *file)
+{
+	struct msm_v4l2_overlay_fh *fh = file->private_data;
+	struct msm_v4l2_overlay_device *vout = fh->vout;
+
+	if (vout->streaming)
+		msm_v4l2_overlay_stopstreaming(vout);
+
+	vout->ref_count--;
+
+	kfree(vout->bufs);
+	vout->numbufs = 0;
+	kfree(fh);
+
+	return 0;
+}
+
+static int
+msm_v4l2_overlay_open(struct file *file)
+{
+	struct msm_v4l2_overlay_device	*vout = NULL;
+	struct v4l2_pix_format	*pix = NULL;
+	struct msm_v4l2_overlay_fh *fh;
+
+	vout = saved_vout0;
+	vout->id = 0;
+
+	if (vout->ref_count) {
+		pr_err("%s: multiple open currently is not"
+		"supported!\n", __func__);
+		return -EBUSY;
+	}
+
+	vout->ref_count++;
+
+	/* allocate per-filehandle data */
+	fh = kmalloc(sizeof(struct msm_v4l2_overlay_fh), GFP_KERNEL);
+	if (NULL == fh)
+		return -ENOMEM;
+
+	fh->vout = vout;
+	fh->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+
+	file->private_data = fh;
+
+	vout->streaming		= 0;
+	vout->crop_rect.left	= vout->crop_rect.top = 0;
+	vout->crop_rect.width	= vout->screen_width;
+	vout->crop_rect.height	= vout->screen_height;
+
+	pix				= &vout->pix;
+	pix->width			= vout->screen_width;
+	pix->height		= vout->screen_height;
+	pix->pixelformat	= V4L2_PIX_FMT_RGB32;
+	pix->field			= V4L2_FIELD_NONE;
+	pix->bytesperline	= pix->width * 4;
+	pix->sizeimage		= pix->bytesperline * pix->height;
+	pix->priv			= 0;
+	pix->colorspace		= V4L2_COLORSPACE_SRGB;
+
+	vout->win.w.left	= 0;
+	vout->win.w.top		= 0;
+	vout->win.w.width	= vout->screen_width;
+	vout->win.w.height	= vout->screen_height;
+
+	vout->fb.capability = V4L2_FBUF_CAP_EXTERNOVERLAY
+		| V4L2_FBUF_CAP_LOCAL_ALPHA;
+	vout->fb.flags = V4L2_FBUF_FLAG_LOCAL_ALPHA;
+	vout->fb.base = 0;
+	memcpy(&vout->fb.fmt, pix, sizeof(struct v4l2_format));
+
+	vout->bufs = 0;
+	vout->numbufs = 0;
+
+	mutex_init(&vout->update_lock);
+
+	return 0;
+}
+
+
+static int __devinit
+msm_v4l2_overlay_probe(struct platform_device *pdev)
+{
+	char *v4l2_ram_phys_unaligned;
+	if ((pdev->id == 0) && (pdev->num_resources > 0)) {
+		v4l2_ram_size =
+			pdev->resource[0].end - pdev->resource[0].start + 1;
+		v4l2_ram_phys_unaligned = (char *)pdev->resource[0].start;
+		v4l2_ram_phys =
+		(char *)PAGE_ALIGN((unsigned int)v4l2_ram_phys_unaligned);
+		/*
+		 * We are (fwd) page aligning the start of v4l2 memory.
+		 * Therefore we have that much less physical memory available
+		 */
+		v4l2_ram_size -= (unsigned int)v4l2_ram_phys
+			- (unsigned int)v4l2_ram_phys_unaligned;
+
+
+	}
+	return 0;
+}
+
+static int __devexit
+msm_v4l2_overlay_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static void msm_v4l2_overlay_videodev_release(struct video_device *vfd)
+{
+	return;
+}
+
+static const struct v4l2_file_operations msm_v4l2_overlay_fops = {
+	.owner		= THIS_MODULE,
+	.open		= msm_v4l2_overlay_open,
+	.release	= msm_v4l2_overlay_release,
+	.mmap		= msm_v4l2_overlay_mmap,
+	.ioctl		= msm_v4l2_overlay_ioctl,
+};
+
+static struct video_device msm_v4l2_overlay_vid_device0 = {
+	.name		= "msm_v4l2_overlay",
+	.fops       = &msm_v4l2_overlay_fops,
+	.minor		= -1,
+	.release	= msm_v4l2_overlay_videodev_release,
+};
+
+static struct platform_driver msm_v4l2_overlay_platform_driver = {
+	.probe   = msm_v4l2_overlay_probe,
+	.remove  = msm_v4l2_overlay_remove,
+	.driver  = {
+			 .name = "msm_v4l2_overlay_pd",
+		   },
+};
+
+static int __init msm_v4l2_overlay_init(void)
+{
+	int ret;
+
+
+	saved_vout0 = kzalloc(sizeof(struct msm_v4l2_overlay_device),
+		GFP_KERNEL);
+
+	if (!saved_vout0)
+		return -ENOMEM;
+
+	ret = platform_driver_register(&msm_v4l2_overlay_platform_driver);
+	if (ret < 0)
+		goto end;
+
+	/*
+	 * Register the device with videodev.
+	 * Videodev will make IOCTL calls on application requests
+	 */
+	ret = video_register_device(&msm_v4l2_overlay_vid_device0,
+		VFL_TYPE_GRABBER, MSM_VIDEO);
+
+	if (ret < 0) {
+		pr_err("%s: V4L2 video overlay device registration failure(%d)\n",
+				  __func__, ret);
+		goto end_unregister;
+	}
+
+	mutex_init(&msmfb_lock);
+
+	return 0;
+
+end_unregister:
+	platform_driver_unregister(&msm_v4l2_overlay_platform_driver);
+
+end:
+	kfree(saved_vout0);
+	return ret;
+}
+
+static void __exit msm_v4l2_overlay_exit(void)
+{
+	video_unregister_device(&msm_v4l2_overlay_vid_device0);
+	platform_driver_unregister(&msm_v4l2_overlay_platform_driver);
+	mutex_destroy(&msmfb_lock);
+	kfree(saved_vout0);
+}
+
+module_init(msm_v4l2_overlay_init);
+module_exit(msm_v4l2_overlay_exit);
+
+MODULE_DESCRIPTION("MSM V4L2 Video Overlay Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/msm_v4l2_video.h b/drivers/media/video/msm/msm_v4l2_video.h
new file mode 100644
index 0000000..a7baa75
--- /dev/null
+++ b/drivers/media/video/msm/msm_v4l2_video.h
@@ -0,0 +1,59 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef MSM_V4L2_VIDEO_H
+#define MSM_V4L2_VIDEO_H
+
+#include <linux/mm.h>
+#include <linux/msm_mdp.h>
+#include <linux/videodev2.h>
+
+
+struct msm_v4l2_overlay_buffer {
+	int mapped;
+	int queued;
+	int offset;
+	int bufsize;
+};
+
+struct msm_v4l2_overlay_device {
+	struct device dev;
+
+	int ref_count;
+	int id;
+
+	int screen_width;
+	int screen_height;
+	int streaming;
+
+	struct v4l2_pix_format pix;
+	struct v4l2_window win;
+	struct v4l2_rect crop_rect;
+	struct v4l2_framebuffer fb;
+	struct msm_v4l2_overlay_buffer *bufs;
+	int numbufs;
+	struct mdp_overlay req;
+	void *par;
+
+	struct mutex update_lock;
+};
+
+struct msm_v4l2_overlay_fh {
+	struct msm_v4l2_overlay_device *vout;
+	enum v4l2_buf_type type;
+};
+
+struct msm_v4l2_overlay_userptr_buffer {
+	uint base[3];
+	size_t length[3];
+};
+
+#endif
diff --git a/drivers/media/video/msm/msm_vfe31.c b/drivers/media/video/msm/msm_vfe31.c
new file mode 100644
index 0000000..2929538
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe31.c
@@ -0,0 +1,4012 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/uaccess.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <mach/irqs.h>
+#include <mach/camera.h>
+#include <asm/atomic.h>
+
+#include "msm_vfe31.h"
+#include "msm_vpe1.h"
+atomic_t irq_cnt;
+
+static struct vfe31_ctrl_type *vfe31_ctrl;
+static struct msm_camera_io_clk camio_clk;
+static void *vfe_syncdata;
+static void vfe31_send_msg_no_payload(enum VFE31_MESSAGE_ID id);
+static void vfe31_reset_hist_cfg(void);
+
+struct vfe31_isr_queue_cmd {
+	struct list_head list;
+	uint32_t                           vfeInterruptStatus0;
+	uint32_t                           vfeInterruptStatus1;
+	uint32_t                           vfePingPongStatus;
+	struct vfe_frame_asf_info          vfeAsfFrameInfo;
+	struct vfe_frame_bpc_info          vfeBpcFrameInfo;
+	struct vfe_msg_camif_status        vfeCamifStatusLocal;
+};
+
+static struct vfe31_cmd_type vfe31_cmd[] = {
+/* 0*/	{V31_DUMMY_0},
+		{V31_SET_CLK},
+		{V31_RESET},
+		{V31_START},
+		{V31_TEST_GEN_START},
+/* 5*/	{V31_OPERATION_CFG, V31_OPERATION_CFG_LEN},
+		{V31_AXI_OUT_CFG, V31_AXI_OUT_LEN, V31_AXI_OUT_OFF, 0xFF},
+		{V31_CAMIF_CFG, V31_CAMIF_LEN, V31_CAMIF_OFF, 0xFF},
+		{V31_AXI_INPUT_CFG},
+		{V31_BLACK_LEVEL_CFG, V31_BLACK_LEVEL_LEN, V31_BLACK_LEVEL_OFF,
+		0xFF},
+/*10*/  {V31_ROLL_OFF_CFG, V31_ROLL_OFF_CFG_LEN, V31_ROLL_OFF_CFG_OFF,
+		0xFF},
+		{V31_DEMUX_CFG, V31_DEMUX_LEN, V31_DEMUX_OFF, 0xFF},
+		{V31_DEMOSAIC_0_CFG, V31_DEMOSAIC_0_LEN, V31_DEMOSAIC_0_OFF,
+		0xFF},
+		{V31_DEMOSAIC_1_CFG, V31_DEMOSAIC_1_LEN, V31_DEMOSAIC_1_OFF,
+		0xFF},
+		{V31_DEMOSAIC_2_CFG, V31_DEMOSAIC_2_LEN, V31_DEMOSAIC_2_OFF,
+		0xFF},
+/*15*/	{V31_FOV_CFG, V31_FOV_LEN, V31_FOV_OFF, 0xFF},
+		{V31_MAIN_SCALER_CFG, V31_MAIN_SCALER_LEN, V31_MAIN_SCALER_OFF,
+		0xFF},
+		{V31_WB_CFG, V31_WB_LEN, V31_WB_OFF, 0xFF},
+		{V31_COLOR_COR_CFG, V31_COLOR_COR_LEN, V31_COLOR_COR_OFF, 0xFF},
+		{V31_RGB_G_CFG, V31_RGB_G_LEN, V31_RGB_G_OFF, 0xFF},
+/*20*/	{V31_LA_CFG, V31_LA_LEN, V31_LA_OFF, 0xFF },
+		{V31_CHROMA_EN_CFG, V31_CHROMA_EN_LEN, V31_CHROMA_EN_OFF, 0xFF},
+		{V31_CHROMA_SUP_CFG, V31_CHROMA_SUP_LEN, V31_CHROMA_SUP_OFF,
+		0xFF},
+		{V31_MCE_CFG, V31_MCE_LEN, V31_MCE_OFF, 0xFF},
+		{V31_SK_ENHAN_CFG, V31_SCE_LEN, V31_SCE_OFF, 0xFF},
+/*25*/	{V31_ASF_CFG, V31_ASF_LEN, V31_ASF_OFF, 0xFF},
+		{V31_S2Y_CFG, V31_S2Y_LEN, V31_S2Y_OFF, 0xFF},
+		{V31_S2CbCr_CFG, V31_S2CbCr_LEN, V31_S2CbCr_OFF, 0xFF},
+		{V31_CHROMA_SUBS_CFG, V31_CHROMA_SUBS_LEN, V31_CHROMA_SUBS_OFF,
+		0xFF},
+		{V31_OUT_CLAMP_CFG, V31_OUT_CLAMP_LEN, V31_OUT_CLAMP_OFF,
+		0xFF},
+/*30*/	{V31_FRAME_SKIP_CFG, V31_FRAME_SKIP_LEN, V31_FRAME_SKIP_OFF,
+		0xFF},
+		{V31_DUMMY_1},
+		{V31_DUMMY_2},
+		{V31_DUMMY_3},
+		{V31_UPDATE},
+/*35*/	{V31_BL_LVL_UPDATE, V31_BLACK_LEVEL_LEN, V31_BLACK_LEVEL_OFF,
+		0xFF},
+		{V31_DEMUX_UPDATE, V31_DEMUX_LEN, V31_DEMUX_OFF, 0xFF},
+		{V31_DEMOSAIC_1_UPDATE, V31_DEMOSAIC_1_LEN, V31_DEMOSAIC_1_OFF,
+		0xFF},
+		{V31_DEMOSAIC_2_UPDATE, V31_DEMOSAIC_2_LEN, V31_DEMOSAIC_2_OFF,
+		0xFF},
+		{V31_FOV_UPDATE, V31_FOV_LEN, V31_FOV_OFF, 0xFF},
+/*40*/	{V31_MAIN_SCALER_UPDATE, V31_MAIN_SCALER_LEN, V31_MAIN_SCALER_OFF,
+		0xFF},
+		{V31_WB_UPDATE, V31_WB_LEN, V31_WB_OFF, 0xFF},
+		{V31_COLOR_COR_UPDATE, V31_COLOR_COR_LEN, V31_COLOR_COR_OFF,
+		0xFF},
+		{V31_RGB_G_UPDATE, V31_RGB_G_LEN, V31_CHROMA_EN_OFF, 0xFF},
+		{V31_LA_UPDATE, V31_LA_LEN, V31_LA_OFF, 0xFF },
+/*45*/	{V31_CHROMA_EN_UPDATE, V31_CHROMA_EN_LEN, V31_CHROMA_EN_OFF,
+		0xFF},
+		{V31_CHROMA_SUP_UPDATE, V31_CHROMA_SUP_LEN, V31_CHROMA_SUP_OFF,
+		0xFF},
+		{V31_MCE_UPDATE, V31_MCE_LEN, V31_MCE_OFF, 0xFF},
+		{V31_SK_ENHAN_UPDATE, V31_SCE_LEN, V31_SCE_OFF, 0xFF},
+		{V31_S2CbCr_UPDATE, V31_S2CbCr_LEN, V31_S2CbCr_OFF, 0xFF},
+/*50*/	{V31_S2Y_UPDATE, V31_S2Y_LEN, V31_S2Y_OFF, 0xFF},
+		{V31_ASF_UPDATE, V31_ASF_UPDATE_LEN, V31_ASF_OFF, 0xFF},
+		{V31_FRAME_SKIP_UPDATE},
+		{V31_CAMIF_FRAME_UPDATE},
+		{V31_STATS_AF_UPDATE, V31_STATS_AF_LEN, V31_STATS_AF_OFF},
+/*55*/	{V31_STATS_AE_UPDATE, V31_STATS_AE_LEN, V31_STATS_AE_OFF},
+		{V31_STATS_AWB_UPDATE, V31_STATS_AWB_LEN, V31_STATS_AWB_OFF},
+		{V31_STATS_RS_UPDATE, V31_STATS_RS_LEN, V31_STATS_RS_OFF},
+		{V31_STATS_CS_UPDATE, V31_STATS_CS_LEN, V31_STATS_CS_OFF},
+		{V31_STATS_SKIN_UPDATE},
+/*60*/	{V31_STATS_IHIST_UPDATE, V31_STATS_IHIST_LEN, V31_STATS_IHIST_OFF},
+		{V31_DUMMY_4},
+		{V31_EPOCH1_ACK},
+		{V31_EPOCH2_ACK},
+		{V31_START_RECORDING},
+/*65*/	{V31_STOP_RECORDING},
+		{V31_DUMMY_5},
+		{V31_DUMMY_6},
+		{V31_CAPTURE, V31_CAPTURE_LEN, 0xFF},
+		{V31_DUMMY_7},
+/*70*/	{V31_STOP},
+		{V31_GET_HW_VERSION},
+		{V31_GET_FRAME_SKIP_COUNTS},
+		{V31_OUTPUT1_BUFFER_ENQ},
+		{V31_OUTPUT2_BUFFER_ENQ},
+/*75*/	{V31_OUTPUT3_BUFFER_ENQ},
+		{V31_JPEG_OUT_BUF_ENQ},
+		{V31_RAW_OUT_BUF_ENQ},
+		{V31_RAW_IN_BUF_ENQ},
+		{V31_STATS_AF_ENQ},
+/*80*/	{V31_STATS_AE_ENQ},
+		{V31_STATS_AWB_ENQ},
+		{V31_STATS_RS_ENQ},
+		{V31_STATS_CS_ENQ},
+		{V31_STATS_SKIN_ENQ},
+/*85*/	{V31_STATS_IHIST_ENQ},
+		{V31_DUMMY_8},
+		{V31_JPEG_ENC_CFG},
+		{V31_DUMMY_9},
+		{V31_STATS_AF_START, V31_STATS_AF_LEN, V31_STATS_AF_OFF},
+/*90*/	{V31_STATS_AF_STOP},
+		{V31_STATS_AE_START, V31_STATS_AE_LEN, V31_STATS_AE_OFF},
+		{V31_STATS_AE_STOP},
+		{V31_STATS_AWB_START, V31_STATS_AWB_LEN, V31_STATS_AWB_OFF},
+		{V31_STATS_AWB_STOP},
+/*95*/	{V31_STATS_RS_START, V31_STATS_RS_LEN, V31_STATS_RS_OFF},
+		{V31_STATS_RS_STOP},
+		{V31_STATS_CS_START, V31_STATS_CS_LEN, V31_STATS_CS_OFF},
+		{V31_STATS_CS_STOP},
+		{V31_STATS_SKIN_START},
+/*100*/	{V31_STATS_SKIN_STOP},
+		{V31_STATS_IHIST_START,
+		V31_STATS_IHIST_LEN, V31_STATS_IHIST_OFF},
+		{V31_STATS_IHIST_STOP},
+		{V31_DUMMY_10},
+		{V31_SYNC_TIMER_SETTING, V31_SYNC_TIMER_LEN,
+			V31_SYNC_TIMER_OFF},
+/*105*/	{V31_ASYNC_TIMER_SETTING, V31_ASYNC_TIMER_LEN, V31_ASYNC_TIMER_OFF},
+		{V31_LIVESHOT},
+		{V31_ZSL, V31_CAPTURE_LEN, 0xFF},
+		{V31_STEREOCAM},
+		{V31_LA_SETUP},
+/*110*/	{V31_XBAR_CFG, V31_XBAR_CFG_LEN, V31_XBAR_CFG_OFF},
+/*111*/	{V31_EZTUNE_CFG, V31_EZTUNE_CFG_LEN, V31_EZTUNE_CFG_OFF},
+};
+
+uint32_t vfe31_AXI_WM_CFG[] = {
+	0x0000004C,
+	0x00000064,
+	0x0000007C,
+	0x00000094,
+	0x000000AC,
+	0x000000C4,
+	0x000000DC,
+};
+
+static const char *vfe31_general_cmd[] = {
+	"DUMMY_0",  /* 0 */
+	"SET_CLK",
+	"RESET",
+	"START",
+	"TEST_GEN_START",
+	"OPERATION_CFG",  /* 5 */
+	"AXI_OUT_CFG",
+	"CAMIF_CFG",
+	"AXI_INPUT_CFG",
+	"BLACK_LEVEL_CFG",
+	"ROLL_OFF_CFG",  /* 10 */
+	"DEMUX_CFG",
+	"DEMOSAIC_0_CFG",  /* general */
+	"DEMOSAIC_1_CFG",  /* ABF     */
+	"DEMOSAIC_2_CFG",  /* BPC     */
+	"FOV_CFG",  /* 15  */
+	"MAIN_SCALER_CFG",
+	"WB_CFG",
+	"COLOR_COR_CFG",
+	"RGB_G_CFG",
+	"LA_CFG",  /* 20 */
+	"CHROMA_EN_CFG",
+	"CHROMA_SUP_CFG",
+	"MCE_CFG",
+	"SK_ENHAN_CFG",
+	"ASF_CFG",  /* 25 */
+	"S2Y_CFG",
+	"S2CbCr_CFG",
+	"CHROMA_SUBS_CFG",
+	"OUT_CLAMP_CFG",
+	"FRAME_SKIP_CFG",  /* 30 */
+	"DUMMY_1",
+	"DUMMY_2",
+	"DUMMY_3",
+	"UPDATE",
+	"BL_LVL_UPDATE",  /* 35 */
+	"DEMUX_UPDATE",
+	"DEMOSAIC_1_UPDATE",  /* BPC */
+	"DEMOSAIC_2_UPDATE",  /* ABF */
+	"FOV_UPDATE",
+	"MAIN_SCALER_UPDATE",  /* 40 */
+	"WB_UPDATE",
+	"COLOR_COR_UPDATE",
+	"RGB_G_UPDATE",
+	"LA_UPDATE",
+	"CHROMA_EN_UPDATE",  /* 45 */
+	"CHROMA_SUP_UPDATE",
+	"MCE_UPDATE",
+	"SK_ENHAN_UPDATE",
+	"S2CbCr_UPDATE",
+	"S2Y_UPDATE",  /* 50 */
+	"ASF_UPDATE",
+	"FRAME_SKIP_UPDATE",
+	"CAMIF_FRAME_UPDATE",
+	"STATS_AF_UPDATE",
+	"STATS_AE_UPDATE",  /* 55 */
+	"STATS_AWB_UPDATE",
+	"STATS_RS_UPDATE",
+	"STATS_CS_UPDATE",
+	"STATS_SKIN_UPDATE",
+	"STATS_IHIST_UPDATE",  /* 60 */
+	"DUMMY_4",
+	"EPOCH1_ACK",
+	"EPOCH2_ACK",
+	"START_RECORDING",
+	"STOP_RECORDING",  /* 65 */
+	"DUMMY_5",
+	"DUMMY_6",
+	"CAPTURE",
+	"DUMMY_7",
+	"STOP",  /* 70 */
+	"GET_HW_VERSION",
+	"GET_FRAME_SKIP_COUNTS",
+	"OUTPUT1_BUFFER_ENQ",
+	"OUTPUT2_BUFFER_ENQ",
+	"OUTPUT3_BUFFER_ENQ",  /* 75 */
+	"JPEG_OUT_BUF_ENQ",
+	"RAW_OUT_BUF_ENQ",
+	"RAW_IN_BUF_ENQ",
+	"STATS_AF_ENQ",
+	"STATS_AE_ENQ",  /* 80 */
+	"STATS_AWB_ENQ",
+	"STATS_RS_ENQ",
+	"STATS_CS_ENQ",
+	"STATS_SKIN_ENQ",
+	"STATS_IHIST_ENQ",  /* 85 */
+	"DUMMY_8",
+	"JPEG_ENC_CFG",
+	"DUMMY_9",
+	"STATS_AF_START",
+	"STATS_AF_STOP",  /* 90 */
+	"STATS_AE_START",
+	"STATS_AE_STOP",
+	"STATS_AWB_START",
+	"STATS_AWB_STOP",
+	"STATS_RS_START",  /* 95 */
+	"STATS_RS_STOP",
+	"STATS_CS_START",
+	"STATS_CS_STOP",
+	"STATS_SKIN_START",
+	"STATS_SKIN_STOP",  /* 100 */
+	"STATS_IHIST_START",
+	"STATS_IHIST_STOP",
+	"DUMMY_10",
+	"SYNC_TIMER_SETTING",
+	"ASYNC_TIMER_SETTING",  /* 105 */
+	"V31_LIVESHOT",
+	"V31_ZSL",
+	"V31_STEREOCAM",
+	"V31_LA_SETUP",
+	"V31_XBAR_CFG",
+};
+
+static void vfe_addr_convert(struct msm_vfe_phy_info *pinfo,
+	enum vfe_resp_msg type, void *data, void **ext, int32_t *elen)
+{
+	uint8_t outid;
+	switch (type) {
+	case VFE_MSG_OUTPUT_T:
+	case VFE_MSG_OUTPUT_P:
+	case VFE_MSG_OUTPUT_S:
+	case VFE_MSG_OUTPUT_V:
+	{
+		pinfo->output_id =
+			((struct vfe_message *)data)->_u.msgOut.output_id;
+
+		switch (type) {
+		case VFE_MSG_OUTPUT_P:
+			outid = OUTPUT_TYPE_P;
+			break;
+		case VFE_MSG_OUTPUT_V:
+			outid = OUTPUT_TYPE_V;
+			break;
+		case VFE_MSG_OUTPUT_T:
+			outid = OUTPUT_TYPE_T;
+			break;
+		case VFE_MSG_OUTPUT_S:
+			outid = OUTPUT_TYPE_S;
+			break;
+		default:
+			outid = 0xff;
+			break;
+		}
+		pinfo->output_id = outid;
+		pinfo->p0_phy =
+			((struct vfe_message *)data)->_u.msgOut.p0_addr;
+		pinfo->p1_phy =
+			((struct vfe_message *)data)->_u.msgOut.p1_addr;
+		pinfo->p2_phy =
+			((struct vfe_message *)data)->_u.msgOut.p2_addr;
+
+		pinfo->frame_id =
+		((struct vfe_message *)data)->_u.msgOut.frameCounter;
+
+		((struct vfe_msg_output *)(vfe31_ctrl->extdata))->bpcInfo =
+		((struct vfe_message *)data)->_u.msgOut.bpcInfo;
+		((struct vfe_msg_output *)(vfe31_ctrl->extdata))->asfInfo =
+		((struct vfe_message *)data)->_u.msgOut.asfInfo;
+		((struct vfe_msg_output *)(vfe31_ctrl->extdata))->frameCounter =
+		((struct vfe_message *)data)->_u.msgOut.frameCounter;
+		*ext  = vfe31_ctrl->extdata;
+		*elen = vfe31_ctrl->extlen;
+	}
+		break;
+
+	default:
+		break;
+	} /* switch */
+}
+
+
+static void vfe31_proc_ops(enum VFE31_MESSAGE_ID id, void *msg, size_t len)
+{
+	struct msm_vfe_resp *rp;
+
+	rp = vfe31_ctrl->resp->vfe_alloc(sizeof(struct msm_vfe_resp),
+		vfe31_ctrl->syncdata, GFP_ATOMIC);
+	if (!rp) {
+		CDBG("rp: cannot allocate buffer\n");
+		return;
+	}
+	CDBG("vfe31_proc_ops, msgId = %d\n", id);
+	rp->evt_msg.type   = MSM_CAMERA_MSG;
+	rp->evt_msg.msg_id = id;
+	rp->evt_msg.len    = len;
+	rp->evt_msg.data   = msg;
+
+	switch (rp->evt_msg.msg_id) {
+	case MSG_ID_SNAPSHOT_DONE:
+		rp->type = VFE_MSG_SNAPSHOT;
+		break;
+
+	case MSG_ID_OUTPUT_P:
+		rp->type = VFE_MSG_OUTPUT_P;
+		vfe_addr_convert(&(rp->phy), VFE_MSG_OUTPUT_P,
+			rp->evt_msg.data, &(rp->extdata),
+			&(rp->extlen));
+		break;
+
+	case MSG_ID_OUTPUT_T:
+		rp->type = VFE_MSG_OUTPUT_T;
+		vfe_addr_convert(&(rp->phy), VFE_MSG_OUTPUT_T,
+			rp->evt_msg.data, &(rp->extdata),
+			&(rp->extlen));
+		break;
+
+	case MSG_ID_OUTPUT_S:
+		rp->type = VFE_MSG_OUTPUT_S;
+		vfe_addr_convert(&(rp->phy), VFE_MSG_OUTPUT_S,
+			rp->evt_msg.data, &(rp->extdata),
+			&(rp->extlen));
+		break;
+
+	case MSG_ID_OUTPUT_V:
+		rp->type = VFE_MSG_OUTPUT_V;
+		vfe_addr_convert(&(rp->phy), VFE_MSG_OUTPUT_V,
+			rp->evt_msg.data, &(rp->extdata),
+			&(rp->extlen));
+		break;
+
+	case MSG_ID_COMMON:
+		rp->type = VFE_MSG_COMMON;
+		rp->stats_msg.status_bits = ((struct vfe_message *)
+			rp->evt_msg.data)->_u.msgStats.status_bits;
+		rp->stats_msg.frame_id = ((struct vfe_message *)
+			rp->evt_msg.data)->_u.msgStats.frameCounter;
+
+		rp->stats_msg.aec_buff = ((struct vfe_message *)
+			rp->evt_msg.data)->_u.msgStats.buff.aec;
+		rp->stats_msg.awb_buff = ((struct vfe_message *)
+			rp->evt_msg.data)->_u.msgStats.buff.awb;
+		rp->stats_msg.af_buff = ((struct vfe_message *)
+			rp->evt_msg.data)->_u.msgStats.buff.af;
+		rp->stats_msg.ihist_buff = ((struct vfe_message *)
+			rp->evt_msg.data)->_u.msgStats.buff.ihist;
+		rp->stats_msg.rs_buff = ((struct vfe_message *)
+			rp->evt_msg.data)->_u.msgStats.buff.rs;
+		rp->stats_msg.cs_buff = ((struct vfe_message *)
+			rp->evt_msg.data)->_u.msgStats.buff.cs;
+		rp->stats_msg.awb_ymin = ((struct vfe_message *)
+			rp->evt_msg.data)->_u.msgStats.buff.awb_ymin;
+		break;
+
+	case MSG_ID_SYNC_TIMER0_DONE:
+		rp->type = VFE_MSG_SYNC_TIMER0;
+		break;
+
+	case MSG_ID_SYNC_TIMER1_DONE:
+		rp->type = VFE_MSG_SYNC_TIMER1;
+		break;
+
+	case MSG_ID_SYNC_TIMER2_DONE:
+		rp->type = VFE_MSG_SYNC_TIMER2;
+		break;
+
+	default:
+		rp->type = VFE_MSG_GENERAL;
+		break;
+	}
+
+	/* save the frame id.*/
+	rp->evt_msg.frame_id = rp->phy.frame_id;
+
+	vfe31_ctrl->resp->vfe_resp(rp, MSM_CAM_Q_VFE_MSG, vfe31_ctrl->syncdata,
+		GFP_ATOMIC);
+}
+
+static void vfe_send_outmsg(uint8_t msgid, uint32_t p0_addr,
+	uint32_t p1_addr, uint32_t p2_addr)
+{
+	struct vfe_message msg;
+	uint8_t outid;
+
+	msg._d = msgid;   /* now the output mode is redundnat. */
+	msg._u.msgOut.frameCounter = vfe31_ctrl->vfeFrameId;
+
+	switch (msgid) {
+	case MSG_ID_OUTPUT_P:
+		outid = OUTPUT_TYPE_P;
+		break;
+	case MSG_ID_OUTPUT_V:
+		outid = OUTPUT_TYPE_V;
+		break;
+	case MSG_ID_OUTPUT_T:
+		outid = OUTPUT_TYPE_T;
+		break;
+	case MSG_ID_OUTPUT_S:
+		outid = OUTPUT_TYPE_S;
+		break;
+	default:
+		outid = 0xff;  /* -1 for error condition.*/
+		break;
+	}
+	msg._u.msgOut.output_id   = msgid;
+	msg._u.msgOut.p0_addr     = p0_addr;
+	msg._u.msgOut.p1_addr     = p1_addr;
+	msg._u.msgOut.p2_addr     = p2_addr;
+	CDBG("%s p2_addr = 0x%x\n", __func__, p2_addr);
+	vfe31_proc_ops(msgid, &msg, sizeof(struct vfe_message));
+	return;
+}
+static int vfe31_enable(struct camera_enable_cmd *enable)
+{
+	return 0;
+}
+
+static void vfe31_stop(void)
+{
+	atomic_set(&vfe31_ctrl->vstate, 0);
+	atomic_set(&vfe31_ctrl->stop_ack_pending, 1);
+
+	/* in either continuous or snapshot mode, stop command can be issued
+	 * at any time. stop camif immediately. */
+	msm_camera_io_w_mb(CAMIF_COMMAND_STOP_IMMEDIATELY,
+		vfe31_ctrl->vfebase + VFE_CAMIF_COMMAND);
+
+	/* disable all interrupts.  */
+	msm_camera_io_w(VFE_DISABLE_ALL_IRQS,
+		vfe31_ctrl->vfebase + VFE_IRQ_MASK_0);
+	msm_camera_io_w(VFE_DISABLE_ALL_IRQS,
+		vfe31_ctrl->vfebase + VFE_IRQ_MASK_1);
+
+	/* clear all pending interrupts*/
+	msm_camera_io_w(VFE_CLEAR_ALL_IRQS,
+		vfe31_ctrl->vfebase + VFE_IRQ_CLEAR_0);
+	msm_camera_io_w(VFE_CLEAR_ALL_IRQS,
+		vfe31_ctrl->vfebase + VFE_IRQ_CLEAR_1);
+	/* Ensure the write order while writing
+	to the command register using the barrier */
+	msm_camera_io_w_mb(1,
+		vfe31_ctrl->vfebase + VFE_IRQ_CMD);
+
+	/* now enable only halt_irq & reset_irq */
+	msm_camera_io_w(0xf0000000,          /* this is for async timer. */
+		vfe31_ctrl->vfebase + VFE_IRQ_MASK_0);
+	msm_camera_io_w(VFE_IMASK_AXI_HALT,
+		vfe31_ctrl->vfebase + VFE_IRQ_MASK_1);
+
+	/* then apply axi halt command. */
+	msm_camera_io_w_mb(AXI_HALT,
+		vfe31_ctrl->vfebase + VFE_AXI_CMD);
+}
+
+static int vfe31_disable(struct camera_enable_cmd *enable,
+	struct platform_device *dev)
+{
+	msm_camio_set_perf_lvl(S_EXIT);
+	msm_camio_disable(dev);
+	return 0;
+}
+
+static int vfe31_add_free_buf2(struct vfe31_output_ch *outch,
+	uint32_t paddr, uint32_t p0_off, uint32_t p1_off, uint32_t p2_off)
+{
+	struct vfe31_free_buf *free_buf = NULL;
+	unsigned long flags = 0;
+	free_buf = kmalloc(sizeof(struct vfe31_free_buf), GFP_KERNEL);
+	if (!free_buf)
+		return -ENOMEM;
+
+	spin_lock_irqsave(&outch->free_buf_lock, flags);
+	free_buf->paddr = paddr;
+	free_buf->planar0_off = p0_off;
+	free_buf->planar1_off = p1_off;
+	free_buf->planar2_off = p2_off;
+	list_add_tail(&free_buf->node, &outch->free_buf_head);
+
+	CDBG("%s: free_buf paddr = 0x%x, y_off = %d, cbcr_off = %d\n",
+		__func__, free_buf->paddr, free_buf->planar0_off,
+		free_buf->planar1_off);
+	spin_unlock_irqrestore(&outch->free_buf_lock, flags);
+	return 0;
+}
+
+#define vfe31_add_free_buf(outch, regptr) \
+	vfe31_add_free_buf2(outch, regptr->paddr, \
+	regptr->info.planar0_off,	\
+	regptr->info.planar1_off,	\
+	regptr->info.planar2_off)
+
+#define vfe31_free_buf_available(outch) \
+	(!list_empty(&outch.free_buf_head))
+
+static inline struct vfe31_free_buf *vfe31_get_free_buf(
+	struct vfe31_output_ch *outch)
+{
+	unsigned long flags = 0;
+	struct vfe31_free_buf *free_buf = NULL;
+	spin_lock_irqsave(&outch->free_buf_lock, flags);
+	if (!list_empty(&outch->free_buf_head)) {
+		free_buf = list_first_entry(&outch->free_buf_head,
+			struct vfe31_free_buf, node);
+		if (free_buf)
+			list_del_init(&free_buf->node);
+	}
+	spin_unlock_irqrestore(&outch->free_buf_lock, flags);
+	return free_buf;
+}
+
+static inline void vfe31_reset_free_buf_queue(
+	struct vfe31_output_ch *outch)
+{
+	unsigned long flags = 0;
+	struct vfe31_free_buf *free_buf = NULL;
+	spin_lock_irqsave(&outch->free_buf_lock, flags);
+	while (!list_empty(&outch->free_buf_head)) {
+		free_buf = list_first_entry(&outch->free_buf_head,
+			struct vfe31_free_buf, node);
+		if (free_buf) {
+			list_del_init(&free_buf->node);
+			kfree(free_buf);
+		}
+	}
+	spin_unlock_irqrestore(&outch->free_buf_lock, flags);
+}
+
+#define vfe31_init_free_buf_queue() do {	\
+	INIT_LIST_HEAD(&vfe31_ctrl->outpath.out0.free_buf_head);	\
+	INIT_LIST_HEAD(&vfe31_ctrl->outpath.out1.free_buf_head);	\
+	INIT_LIST_HEAD(&vfe31_ctrl->outpath.out2.free_buf_head);	\
+	spin_lock_init(&vfe31_ctrl->outpath.out0.free_buf_lock);	\
+	spin_lock_init(&vfe31_ctrl->outpath.out1.free_buf_lock);	\
+	spin_lock_init(&vfe31_ctrl->outpath.out2.free_buf_lock);	\
+} while (0)
+
+#define vfe31_reset_free_buf_queue_all() do {	\
+	vfe31_reset_free_buf_queue(&vfe31_ctrl->outpath.out0);	\
+	vfe31_reset_free_buf_queue(&vfe31_ctrl->outpath.out1);	\
+	vfe31_reset_free_buf_queue(&vfe31_ctrl->outpath.out2);	\
+} while (0)
+
+static int vfe31_config_axi(int mode, struct axidata *ad, uint32_t *ao)
+{
+	int i;
+	uint32_t *p, *p1, *p2, *p3;
+	int32_t *ch_info;
+	struct vfe31_output_ch *outp1, *outp2, *outp3;
+	struct msm_pmem_region *regp1 = NULL;
+	struct msm_pmem_region *regp2 = NULL;
+	struct msm_pmem_region *regp3 = NULL;
+	int ret;
+	struct msm_sync* p_sync = (struct msm_sync *)vfe_syncdata;
+
+	outp1 = NULL;
+	outp2 = NULL;
+	outp3 = NULL;
+
+	p = ao + 2;
+
+	/* Update the corresponding write masters for each output*/
+	ch_info = ao + V31_AXI_CFG_LEN;
+	vfe31_ctrl->outpath.out0.ch0 = 0x0000FFFF & *ch_info;
+	vfe31_ctrl->outpath.out0.ch1 = 0x0000FFFF & (*ch_info++ >> 16);
+	vfe31_ctrl->outpath.out0.ch2 = 0x0000FFFF & *ch_info++;
+	vfe31_ctrl->outpath.out1.ch0 = 0x0000FFFF & *ch_info;
+	vfe31_ctrl->outpath.out1.ch1 = 0x0000FFFF & (*ch_info++ >> 16);
+	vfe31_ctrl->outpath.out1.ch2 = 0x0000FFFF & *ch_info++;
+	vfe31_ctrl->outpath.out2.ch0 = 0x0000FFFF & *ch_info;
+	vfe31_ctrl->outpath.out2.ch1 = 0x0000FFFF & (*ch_info++ >> 16);
+	vfe31_ctrl->outpath.out2.ch2 = 0x0000FFFF & *ch_info++;
+
+	CDBG("vfe31_config_axi: mode = %d, bufnum1 = %d, bufnum2 = %d"
+		"bufnum3 = %d", mode, ad->bufnum1, ad->bufnum2, ad->bufnum3);
+
+	switch (mode) {
+
+	case OUTPUT_2: {
+		if (ad->bufnum2 != 3)
+			return -EINVAL;
+		regp1 = &(ad->region[ad->bufnum1]);
+		outp1 = &(vfe31_ctrl->outpath.out0);
+		vfe31_ctrl->outpath.output_mode |= VFE31_OUTPUT_MODE_PT;
+
+		for (i = 0; i < 2; i++) {
+			p1 = ao + 6 + i;    /* wm0 for y  */
+			*p1 = (regp1->paddr + regp1->info.planar0_off);
+
+			p1 = ao + 12 + i;  /* wm1 for cbcr */
+			*p1 = (regp1->paddr + regp1->info.planar1_off);
+			regp1++;
+		}
+		ret = vfe31_add_free_buf(outp1, regp1);
+		if (ret < 0)
+			return ret;
+	}
+		break;
+
+	case OUTPUT_1_AND_2:
+		/* use wm0& 4 for thumbnail, wm1&5 for main image.*/
+		if ((ad->bufnum1 < 1) || (ad->bufnum2 < 1))
+			return -EINVAL;
+		vfe31_ctrl->outpath.output_mode |=
+			VFE31_OUTPUT_MODE_S;  /* main image.*/
+		vfe31_ctrl->outpath.output_mode |=
+			VFE31_OUTPUT_MODE_PT;  /* thumbnail. */
+
+		/* this is thumbnail buffer. */
+		regp1 = &(ad->region[ad->bufnum1-1]);
+		/* this is main image buffer. */
+		regp2 = &(ad->region[ad->bufnum1+ad->bufnum2-1]);
+
+		outp1 = &(vfe31_ctrl->outpath.out0);
+		outp2 = &(vfe31_ctrl->outpath.out1); /* snapshot */
+
+		/*  Parse the buffers!!! */
+		if (ad->bufnum2 == 1) {	/* assuming bufnum1 = bufnum2 */
+			p1 = ao + 6;   /* wm0 ping */
+			*p1++ = (regp1->paddr + regp1->info.planar0_off);
+
+			/* this is to duplicate ping address to pong.*/
+			*p1 = (regp1->paddr + regp1->info.planar0_off);
+
+			p1 = ao + 30;  /* wm4 ping */
+			*p1++ = (regp1->paddr + regp1->info.planar1_off);
+			CDBG("%s: regp1->info.cbcr_off = 0x%x\n", __func__,
+						 regp1->info.planar1_off);
+
+			/* this is to duplicate ping address to pong.*/
+			*p1 = (regp1->paddr + regp1->info.planar1_off);
+
+			p1 = ao + 12;   /* wm1 ping */
+			*p1++ = (regp2->paddr + regp2->info.planar0_off);
+
+			/* pong = ping,*/
+			*p1 = (regp2->paddr + regp2->info.planar0_off);
+
+			p1 = ao + 36;  /* wm5 */
+			*p1++ = (regp2->paddr + regp2->info.planar1_off);
+			CDBG("%s: regp2->info.cbcr_off = 0x%x\n", __func__,
+						 regp2->info.planar1_off);
+
+			/* pong = ping,*/
+			*p1 = (regp2->paddr + regp2->info.planar1_off);
+		} else { /* more than one snapshot */
+			/* first fill ping & pong */
+			for (i = 0; i < 2; i++) {
+				p1 = ao + 6 + i;    /* wm0 for y  */
+				*p1 = (regp1->paddr + regp1->info.planar0_off);
+				p1 = ao + 30 + i;  /* wm4 for cbcr */
+				*p1 = (regp1->paddr + regp1->info.planar1_off);
+				regp1--;
+			}
+
+			for (i = 0; i < 2; i++) {
+				p2 = ao + 12 + i;    /* wm1 for y  */
+				*p2 = (regp2->paddr + regp2->info.planar0_off);
+				p2 = ao + 36 + i;  /* wm5 for cbcr */
+				*p2 = (regp2->paddr + regp2->info.planar1_off);
+				regp2--;
+			}
+
+			for (i = 2; i < ad->bufnum1; i++) {
+				ret = vfe31_add_free_buf(outp1, regp1);
+				if (ret < 0)
+					return ret;
+				regp1--;
+			}
+
+			for (i = 2; i < ad->bufnum2; i++) {
+				ret = vfe31_add_free_buf(outp2, regp2);
+				if (ret < 0)
+					return ret;
+				regp2--;
+			}
+		}
+		break;
+
+	case OUTPUT_1_2_AND_3:
+		CDBG("%s: OUTPUT_1_2_AND_3", __func__);
+		CDBG("%s: %d %d %d", __func__, ad->bufnum1, ad->bufnum2,
+			ad->bufnum3);
+		/* use wm0& 4 for postview, wm1&5 for preview.*/
+		/* use wm2& 6 for main img */
+		if ((ad->bufnum1 < 1) || (ad->bufnum2 < 1) || (ad->bufnum3 < 1))
+			return -EINVAL;
+		vfe31_ctrl->outpath.output_mode |=
+			VFE31_OUTPUT_MODE_S;  /* main image.*/
+		vfe31_ctrl->outpath.output_mode |=
+			VFE31_OUTPUT_MODE_P;  /* preview. */
+		vfe31_ctrl->outpath.output_mode |=
+			VFE31_OUTPUT_MODE_T;  /* thumbnail. */
+
+		/* this is preview buffer. */
+		regp1 = &(ad->region[0]);
+		/* this is thumbnail buffer. */
+		regp2 = &(ad->region[ad->bufnum1]);
+		/* this is main image buffer. */
+		regp3 = &(ad->region[ad->bufnum1+ad->bufnum2]);
+		outp1 = &(vfe31_ctrl->outpath.out0);
+		outp2 = &(vfe31_ctrl->outpath.out1);
+		outp3 = &(vfe31_ctrl->outpath.out2);
+
+		/*  Parse the buffers!!! */
+		/* first fill ping & pong */
+		for (i = 0; i < 2; i++) {
+			p1 = ao + 6 + i;    /* wm0 for y  */
+			*p1 = (regp1->paddr + regp1->info.planar0_off);
+			p1 = ao + 30 + i;  /* wm4 for cbcr */
+			*p1 = (regp1->paddr + regp1->info.planar1_off);
+			regp1++;
+		}
+
+		for (i = 0; i < 2; i++) {
+			p2 = ao + 12 + i;    /* wm1 for y  */
+			*p2 = (regp2->paddr + regp2->info.planar0_off);
+			p2 = ao + 36 + i;  /* wm5 for cbcr */
+			*p2 = (regp2->paddr + regp2->info.planar1_off);
+			regp2++;
+		}
+
+		for (i = 0; i < 2; i++) {
+			p3 = ao + 18 + i;    /* wm2 for y  */
+			*p3 = (regp3->paddr + regp3->info.planar0_off);
+			p3 = ao + 42 + i;  /* wm6 for cbcr */
+			*p3 = (regp3->paddr + regp3->info.planar1_off);
+			regp3++;
+		}
+
+		for (i = 2; i < ad->bufnum1; i++) {
+			ret = vfe31_add_free_buf(outp1, regp1);
+			if (ret < 0)
+				return ret;
+			regp1++;
+		}
+
+		for (i = 2; i < ad->bufnum2; i++) {
+			ret = vfe31_add_free_buf(outp2, regp2);
+			if (ret < 0)
+				return ret;
+			regp2++;
+		}
+
+		for (i = 2; i < ad->bufnum3; i++) {
+			ret = vfe31_add_free_buf(outp3, regp3);
+			if (ret < 0)
+				return ret;
+			regp3++;
+		}
+		break;
+
+	case OUTPUT_ZSL_ALL_CHNLS:
+		CDBG("%s: OUTPUT_ZSL_ALL_CHNLS", __func__);
+		CDBG("%s: %d %d %d", __func__, ad->bufnum1, ad->bufnum2,
+			ad->bufnum3);
+		/* use wm0& 4 for postview, wm1&5 for preview.*/
+		/* use wm2& 6 for main img */
+		if ((ad->bufnum1 < 1) || (ad->bufnum2 < 1) || (ad->bufnum3 < 1))
+			return -EINVAL;
+		vfe31_ctrl->outpath.output_mode |=
+			VFE31_OUTPUT_MODE_S;  /* main image.*/
+		vfe31_ctrl->outpath.output_mode |=
+			VFE31_OUTPUT_MODE_P_ALL_CHNLS;  /* preview. */
+		vfe31_ctrl->outpath.output_mode |=
+			VFE31_OUTPUT_MODE_T;  /* thumbnail. */
+
+		/* this is preview buffer. */
+		regp1 = &(ad->region[0]);
+		/* this is thumbnail buffer. */
+		regp2 = &(ad->region[ad->bufnum1]);
+		/* this is main image buffer. */
+		regp3 = &(ad->region[ad->bufnum1+ad->bufnum2]);
+		outp1 = &(vfe31_ctrl->outpath.out0);
+		outp2 = &(vfe31_ctrl->outpath.out1);
+		outp3 = &(vfe31_ctrl->outpath.out2);
+
+		/*  Parse the buffers!!! */
+		/* first fill ping & pong */
+		for (i = 0; i < 2; i++) {
+			p1 = ao + 6 + i;    /* wm0 for y  */
+			*p1 = (regp2->paddr + regp2->info.planar0_off);
+			p1 = ao + 12 + i;  /* wm1 for cbcr */
+			*p1 = (regp2->paddr + regp2->info.planar1_off);
+			regp2++;
+		}
+
+		for (i = 0; i < 2; i++) {
+			p2 = ao + 30 + i;    /* wm4 for y  */
+			*p2 = (regp1->paddr + regp1->info.planar0_off);
+			p2 = ao + 36 + i;  /* wm5 for cbcr */
+			*p2 = (regp1->paddr + regp1->info.planar1_off);
+			p2 = ao + 42 + i;  /* wm5 for cbcr */
+			*p2 = (regp1->paddr + regp1->info.planar2_off);
+			regp1++;
+		}
+
+		for (i = 0; i < 2; i++) {
+			p3 = ao + 18 + i;    /* wm2 for y  */
+			*p3 = (regp3->paddr + regp3->info.planar0_off);
+			p3 = ao + 24 + i;  /* wm3 for cbcr */
+			*p3 = (regp3->paddr + regp3->info.planar1_off);
+			regp3++;
+		}
+		for (i = 2; i < ad->bufnum1; i++) {
+			ret = vfe31_add_free_buf(outp1, regp1);
+			if (ret < 0)
+				return ret;
+			regp1++;
+		}
+
+		for (i = 2; i < ad->bufnum2; i++) {
+			ret = vfe31_add_free_buf(outp2, regp2);
+			if (ret < 0)
+				return ret;
+			regp2++;
+		}
+
+		for (i = 2; i < ad->bufnum3; i++) {
+			ret = vfe31_add_free_buf(outp3, regp3);
+			if (ret < 0)
+				return ret;
+			regp3++;
+		}
+		break;
+
+	case OUTPUT_1_AND_3: {
+		/* use wm0&4 for preview, wm1&5 for video.*/
+		if ((ad->bufnum1 < 2) || (ad->bufnum2 < 2))
+			return -EINVAL;
+
+#ifdef CONFIG_MSM_CAMERA_V4L2
+		*p++ = 0x1;    /* xbar cfg0 */
+		*p = 0x1a03;    /* xbar cfg1 */
+#endif
+		vfe31_ctrl->outpath.output_mode |=
+			VFE31_OUTPUT_MODE_V;  /* video*/
+		vfe31_ctrl->outpath.output_mode |=
+			VFE31_OUTPUT_MODE_PT;  /* preview */
+
+		regp1 = &(ad->region[0]); /* this is preview buffer. */
+		regp2 = &(ad->region[ad->bufnum1]);/* this is video buffer. */
+		outp1 = &(vfe31_ctrl->outpath.out0); /* preview */
+		outp2 = &(vfe31_ctrl->outpath.out2); /* video */
+
+
+		for (i = 0; i < 2; i++) {
+			p1 = ao + 6 + i;    /* wm0 for y  */
+			*p1 = (regp1->paddr + regp1->info.planar0_off);
+
+			p1 = ao + 30 + i;  /* wm4 for cbcr */
+			*p1 = (regp1->paddr + regp1->info.planar1_off);
+			regp1++;
+		}
+
+		for (i = 0; i < 2; i++) {
+			p2 = ao + 12 + i;    /* wm1 for y  */
+			*p2 = (regp2->paddr + regp2->info.planar0_off);
+
+			p2 = ao + 36 + i;  /* wm5 for cbcr */
+			*p2 = (regp2->paddr + regp2->info.planar1_off);
+			regp2++;
+		}
+		for (i = 2; i < ad->bufnum1; i++) {
+			ret = vfe31_add_free_buf(outp1, regp1);
+			if (ret < 0)
+				return ret;
+			regp1++;
+		}
+
+		for (i = 2; i < ad->bufnum2; i++) {
+			ret = vfe31_add_free_buf(outp2, regp2);
+			if (ret < 0)
+				return ret;
+			regp2++;
+		}
+	}
+		break;
+
+	case OUTPUT_VIDEO_ALL_CHNLS: {
+		/* use wm0&4 for preview, wm1&5 for video.*/
+		vfe31_ctrl->outpath.output_mode |=
+			VFE31_OUTPUT_MODE_V;  /* video*/
+		vfe31_ctrl->outpath.output_mode |=
+			VFE31_OUTPUT_MODE_P_ALL_CHNLS;
+		regp1 = &(ad->region[0]);
+		regp2 = &(ad->region[ad->bufnum1]);
+		outp1 = &(vfe31_ctrl->outpath.out0);
+		outp2 = &(vfe31_ctrl->outpath.out2);
+
+		for (i = 0; i < 2; i++) {
+			p1 = ao + 6 + i;    /* wm0 for y  */
+			*p1 = (regp1->paddr + regp1->info.planar0_off);
+
+			p1 = ao + 12 + i;  /* wm1 for cbcr */
+			*p1 = (regp1->paddr + regp1->info.planar1_off);
+
+			p1 = ao + 18 + i;  /* wm2 for cbcr */
+			*p1 = (regp1->paddr + regp1->info.planar2_off);
+			regp1++;
+		}
+		for (i = 0; i < 2; i++) {
+			p2 = ao + 30 + i;    /* wm4 for y  */
+			*p2 = (regp2->paddr + regp2->info.planar0_off);
+
+			p2 = ao + 36 + i;  /* wm5 for cbcr */
+			*p2 = (regp2->paddr + regp2->info.planar1_off);
+			regp2++;
+		}
+		for (i = 2; i < ad->bufnum1; i++) {
+			ret = vfe31_add_free_buf(outp1, regp1);
+			if (ret < 0)
+				return ret;
+			regp1++;
+		}
+		for (i = 2; i < ad->bufnum2; i++) {
+			ret = vfe31_add_free_buf(outp2, regp2);
+			if (ret < 0)
+				return ret;
+			regp2++;
+		}
+	}
+		break;
+
+	case CAMIF_TO_AXI_VIA_OUTPUT_2: {  /* use wm0 only */
+		if (ad->bufnum2 < 1)
+			return -EINVAL;
+		CDBG("config axi for raw snapshot.\n");
+		vfe31_ctrl->outpath.out1.ch0 = 0; /* raw */
+		regp1 = &(ad->region[ad->bufnum1]);
+		vfe31_ctrl->outpath.output_mode |= VFE31_OUTPUT_MODE_S;
+		p1 = ao + 6;    /* wm0 for y  */
+		*p1 = (regp1->paddr + regp1->info.planar0_off);
+		if (p_sync->stereocam_enabled)
+			p_sync->stereo_state = STEREO_RAW_SNAP_IDLE;
+	}
+		break;
+	default:
+		break;
+	}
+	msm_camera_io_memcpy(
+		vfe31_ctrl->vfebase + vfe31_cmd[V31_AXI_OUT_CFG].offset,
+		ao, vfe31_cmd[V31_AXI_OUT_CFG].length - V31_AXI_CH_INF_LEN);
+
+	return 0;
+}
+
+static void vfe31_reset_internal_variables(void)
+{
+	unsigned long flags;
+	vfe31_ctrl->vfeImaskCompositePacked = 0;
+	/* state control variables */
+	vfe31_ctrl->start_ack_pending = FALSE;
+	atomic_set(&irq_cnt, 0);
+
+	spin_lock_irqsave(&vfe31_ctrl->xbar_lock, flags);
+	vfe31_ctrl->xbar_update_pending = 0;
+	spin_unlock_irqrestore(&vfe31_ctrl->xbar_lock, flags);
+
+	atomic_set(&vfe31_ctrl->stop_ack_pending, 0);
+	atomic_set(&vfe31_ctrl->vstate, 0);
+
+	vfe31_ctrl->aec_ack_pending = FALSE;
+	vfe31_ctrl->af_ack_pending = FALSE;
+	vfe31_ctrl->awb_ack_pending = FALSE;
+	vfe31_ctrl->ihist_ack_pending = FALSE;
+	vfe31_ctrl->rs_ack_pending = FALSE;
+	vfe31_ctrl->cs_ack_pending = FALSE;
+
+	vfe31_ctrl->reset_ack_pending  = FALSE;
+
+	spin_lock_irqsave(&vfe31_ctrl->update_ack_lock, flags);
+	vfe31_ctrl->update_ack_pending = FALSE;
+	spin_unlock_irqrestore(&vfe31_ctrl->update_ack_lock, flags);
+
+	vfe31_ctrl->recording_state = VFE_REC_STATE_IDLE;
+
+	/* 0 for continuous mode, 1 for snapshot mode */
+	vfe31_ctrl->operation_mode = VFE_MODE_OF_OPERATION_CONTINUOUS;
+	vfe31_ctrl->outpath.output_mode = 0;
+	vfe31_ctrl->vfe_capture_count = 0;
+
+	/* this is unsigned 32 bit integer. */
+	vfe31_ctrl->vfeFrameId = 0;
+
+	vfe31_ctrl->output1Pattern = 0xffffffff;
+	vfe31_ctrl->output1Period  = 31;
+	vfe31_ctrl->output2Pattern = 0xffffffff;
+	vfe31_ctrl->output2Period  = 31;
+	vfe31_ctrl->vfeFrameSkipCount   = 0;
+	vfe31_ctrl->vfeFrameSkipPeriod  = 31;
+
+	/* Stats control variables. */
+	memset(&(vfe31_ctrl->afStatsControl), 0,
+		sizeof(struct vfe_stats_control));
+
+	memset(&(vfe31_ctrl->awbStatsControl), 0,
+		sizeof(struct vfe_stats_control));
+
+	memset(&(vfe31_ctrl->aecStatsControl), 0,
+		sizeof(struct vfe_stats_control));
+
+	memset(&(vfe31_ctrl->ihistStatsControl), 0,
+		sizeof(struct vfe_stats_control));
+
+	memset(&(vfe31_ctrl->rsStatsControl), 0,
+		sizeof(struct vfe_stats_control));
+
+	memset(&(vfe31_ctrl->csStatsControl), 0,
+		sizeof(struct vfe_stats_control));
+}
+
+static void vfe31_reset(void)
+{
+	uint32_t vfe_version;
+	vfe31_reset_free_buf_queue_all();
+	vfe31_reset_internal_variables();
+
+	vfe31_reset_hist_cfg();
+	vfe_version = msm_camera_io_r(vfe31_ctrl->vfebase);
+	CDBG("vfe_version = 0x%x\n", vfe_version);
+	/* disable all interrupts.  vfeImaskLocal is also reset to 0
+	* to begin with. */
+	msm_camera_io_w(VFE_DISABLE_ALL_IRQS,
+		vfe31_ctrl->vfebase + VFE_IRQ_MASK_0);
+
+	msm_camera_io_w(VFE_DISABLE_ALL_IRQS,
+		vfe31_ctrl->vfebase + VFE_IRQ_MASK_1);
+
+	/* clear all pending interrupts*/
+	msm_camera_io_w(VFE_CLEAR_ALL_IRQS,
+		vfe31_ctrl->vfebase + VFE_IRQ_CLEAR_0);
+	msm_camera_io_w(VFE_CLEAR_ALL_IRQS,
+		vfe31_ctrl->vfebase + VFE_IRQ_CLEAR_1);
+
+	/* Ensure the write order while writing
+	to the command register using the barrier */
+	msm_camera_io_w_mb(1, vfe31_ctrl->vfebase + VFE_IRQ_CMD);
+
+	/* enable reset_ack interrupt.  */
+	msm_camera_io_w(VFE_IMASK_RESET,
+		vfe31_ctrl->vfebase + VFE_IRQ_MASK_1);
+
+	/* Write to VFE_GLOBAL_RESET_CMD to reset the vfe hardware. Once reset
+	 * is done, hardware interrupt will be generated.  VFE ist processes
+	 * the interrupt to complete the function call.  Note that the reset
+	 * function is synchronous. */
+
+	/* Ensure the write order while writing
+	to the command register using the barrier */
+	msm_camera_io_w_mb(VFE_RESET_UPON_RESET_CMD,
+		vfe31_ctrl->vfebase + VFE_GLOBAL_RESET);
+}
+
+static int vfe31_operation_config(uint32_t *cmd)
+{
+	uint32_t *p = cmd;
+
+	vfe31_ctrl->operation_mode = *p;
+	vpe_ctrl->pad_2k_bool = (vfe31_ctrl->operation_mode & 1) ?
+		FALSE : TRUE;
+
+	vfe31_ctrl->stats_comp = *(++p);
+	vfe31_ctrl->hfr_mode = *(++p);
+
+	msm_camera_io_w(*(++p), vfe31_ctrl->vfebase + VFE_CFG_OFF);
+	msm_camera_io_w(*(++p), vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+	msm_camera_io_w(*(++p), vfe31_ctrl->vfebase + VFE_REALIGN_BUF);
+	msm_camera_io_w(*(++p), vfe31_ctrl->vfebase + VFE_CHROMA_UP);
+	msm_camera_io_w(*(++p), vfe31_ctrl->vfebase + VFE_STATS_CFG);
+	wmb();
+	return 0;
+}
+static uint32_t vfe_stats_awb_buf_init(struct vfe_cmd_stats_buf *in)
+{
+	uint32_t *ptr = in->statsBuf;
+	uint32_t addr;
+
+	addr = ptr[0];
+	msm_camera_io_w(addr,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PING_ADDR);
+	addr = ptr[1];
+	msm_camera_io_w(addr,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PONG_ADDR);
+	vfe31_ctrl->awbStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+	return 0;
+}
+
+
+static uint32_t vfe_stats_aec_buf_init(struct vfe_cmd_stats_buf *in)
+{
+	uint32_t *ptr = in->statsBuf;
+	uint32_t addr;
+
+	addr = ptr[0];
+	msm_camera_io_w(addr,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_AEC_WR_PING_ADDR);
+	addr = ptr[1];
+	msm_camera_io_w(addr,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_AEC_WR_PONG_ADDR);
+
+	vfe31_ctrl->aecStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+	return 0;
+}
+
+static uint32_t vfe_stats_af_buf_init(struct vfe_cmd_stats_buf *in)
+{
+	uint32_t *ptr = in->statsBuf;
+	uint32_t addr;
+
+	addr = ptr[0];
+	msm_camera_io_w(addr,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_AF_WR_PING_ADDR);
+	addr = ptr[1];
+	msm_camera_io_w(addr,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_AF_WR_PONG_ADDR);
+
+	vfe31_ctrl->afStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+	return 0;
+}
+
+static uint32_t vfe_stats_ihist_buf_init(struct vfe_cmd_stats_buf *in)
+{
+	uint32_t *ptr = in->statsBuf;
+	uint32_t addr;
+
+	addr = ptr[0];
+	msm_camera_io_w(addr,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_HIST_WR_PING_ADDR);
+	addr = ptr[1];
+	msm_camera_io_w(addr,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_HIST_WR_PONG_ADDR);
+
+	vfe31_ctrl->ihistStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+	return 0;
+}
+
+static uint32_t vfe_stats_rs_buf_init(struct vfe_cmd_stats_buf *in)
+{
+	uint32_t *ptr = in->statsBuf;
+	uint32_t addr;
+
+	addr = ptr[0];
+	msm_camera_io_w(addr,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_RS_WR_PING_ADDR);
+	addr = ptr[1];
+	msm_camera_io_w(addr,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_RS_WR_PONG_ADDR);
+
+	vfe31_ctrl->rsStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+	return 0;
+}
+static uint32_t vfe_stats_cs_buf_init(struct vfe_cmd_stats_buf *in)
+{
+	uint32_t *ptr = in->statsBuf;
+	uint32_t addr;
+
+	addr = ptr[0];
+	msm_camera_io_w(addr,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_CS_WR_PING_ADDR);
+	addr = ptr[1];
+	msm_camera_io_w(addr,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_CS_WR_PONG_ADDR);
+
+	vfe31_ctrl->csStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+	return 0;
+}
+
+static void vfe31_start_common(void)
+{
+	uint32_t irq_mask = 0x00E00021;
+	vfe31_ctrl->start_ack_pending = TRUE;
+	CDBG("VFE opertaion mode = 0x%x, output mode = 0x%x\n",
+		vfe31_ctrl->operation_mode, vfe31_ctrl->outpath.output_mode);
+	/* Enable IRQ for comp stats, Image master, SOF & Reg Update*/
+	if (vfe31_ctrl->stats_comp)
+		irq_mask |= 0x01000000;
+	else /* Enable IRQ for Image masters, AF stats, SOF & Reg Update */
+		irq_mask |= 0x00004000;
+
+	/* Enable EOF for video mode */
+	if (VFE_MODE_OF_OPERATION_VIDEO == vfe31_ctrl->operation_mode)
+		irq_mask |= 0x4;
+
+	msm_camera_io_w(irq_mask, vfe31_ctrl->vfebase + VFE_IRQ_MASK_0);
+
+	msm_camera_io_w(VFE_IMASK_RESET,
+		vfe31_ctrl->vfebase + VFE_IRQ_MASK_1);
+	/* enable out of order option */
+	msm_camera_io_w(0x80000000, vfe31_ctrl->vfebase + VFE_AXI_CFG);
+	/* enable performance monitor */
+	msm_camera_io_w(1, vfe31_ctrl->vfebase + VFE_BUS_PM_CFG);
+	msm_camera_io_w(1, vfe31_ctrl->vfebase + VFE_BUS_PM_CMD);
+
+
+	msm_camera_io_dump(vfe31_ctrl->vfebase, 0x600);
+
+	/* Ensure the write order while writing
+	to the command register using the barrier */
+	msm_camera_io_w_mb(1, vfe31_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+	msm_camera_io_w(1, vfe31_ctrl->vfebase + VFE_CAMIF_COMMAND);
+	wmb();
+
+	atomic_set(&vfe31_ctrl->vstate, 1);
+}
+
+static int vfe31_start_recording(void)
+{
+	msm_camio_set_perf_lvl(S_VIDEO);
+	usleep(1000);
+	vfe31_ctrl->recording_state = VFE_REC_STATE_START_REQUESTED;
+	msm_camera_io_w_mb(1, vfe31_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+	return 0;
+}
+
+static int vfe31_stop_recording(void)
+{
+	vfe31_ctrl->recording_state = VFE_REC_STATE_STOP_REQUESTED;
+	msm_camera_io_w_mb(1, vfe31_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+	msm_camio_set_perf_lvl(S_PREVIEW);
+	return 0;
+}
+
+static void vfe31_liveshot(void)
+{
+	struct msm_sync* p_sync = (struct msm_sync *)vfe_syncdata;
+	if (p_sync)
+		p_sync->liveshot_enabled = true;
+}
+
+static void vfe31_stereocam(uint32_t enable)
+{
+	struct msm_sync* p_sync = (struct msm_sync *)vfe_syncdata;
+	if (p_sync) {
+		CDBG("%s: Enable StereoCam %d!!!\n", __func__, enable);
+		p_sync->stereocam_enabled = enable;
+	}
+}
+
+static int vfe31_zsl(void)
+{
+	uint32_t irq_comp_mask = 0;
+	/* capture command is valid for both idle and active state. */
+	irq_comp_mask	=
+		msm_camera_io_r(vfe31_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+
+	CDBG("%s:op mode %d O/P Mode %d\n", __func__,
+		vfe31_ctrl->operation_mode, vfe31_ctrl->outpath.output_mode);
+	if ((vfe31_ctrl->operation_mode == VFE_MODE_OF_OPERATION_ZSL)) {
+		if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_P) {
+			irq_comp_mask |=
+				((0x1 << (vfe31_ctrl->outpath.out0.ch0)) |
+				(0x1 << (vfe31_ctrl->outpath.out0.ch1)));
+		} else if (vfe31_ctrl->outpath.output_mode &
+				VFE31_OUTPUT_MODE_P_ALL_CHNLS) {
+			irq_comp_mask |= (0x1 << vfe31_ctrl->outpath.out0.ch0 |
+				0x1 << vfe31_ctrl->outpath.out0.ch1 |
+				0x1 << vfe31_ctrl->outpath.out0.ch2);
+		}
+		if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_T) {
+			irq_comp_mask |=
+				((0x1 << (vfe31_ctrl->outpath.out1.ch0 + 8)) |
+				(0x1 << (vfe31_ctrl->outpath.out1.ch1 + 8)));
+		}
+		if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_S) {
+			irq_comp_mask |=
+			((0x1 << (vfe31_ctrl->outpath.out2.ch0 + 8)) |
+			(0x1 << (vfe31_ctrl->outpath.out2.ch1 + 8)));
+		}
+		if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_P) {
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+				vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch0]);
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+				vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch1]);
+		} else if (vfe31_ctrl->outpath.output_mode &
+				VFE31_OUTPUT_MODE_P_ALL_CHNLS) {
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+				vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch0]);
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+				vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch1]);
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+				vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch2]);
+		}
+		if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_T) {
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+				vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch0]);
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+				vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch1]);
+		}
+		if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_S) {
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+				vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out2.ch0]);
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+				vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out2.ch1]);
+		}
+	}
+	msm_camera_io_w(irq_comp_mask, vfe31_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+	vfe31_start_common();
+	msm_camio_set_perf_lvl(S_ZSL);
+	usleep(1000);
+	/* for debug */
+	msm_camera_io_w(1, vfe31_ctrl->vfebase + 0x18C);
+	msm_camera_io_w(1, vfe31_ctrl->vfebase + 0x188);
+	return 0;
+}
+
+static int vfe31_capture(uint32_t num_frames_capture)
+{
+	uint32_t irq_comp_mask = 0;
+	struct msm_sync* p_sync = (struct msm_sync *)vfe_syncdata;
+
+	/* capture command is valid for both idle and active state. */
+	vfe31_ctrl->vfe_capture_count = num_frames_capture;
+	if (p_sync) {
+		p_sync->snap_count = num_frames_capture;
+		p_sync->thumb_count = num_frames_capture;
+	}
+
+	irq_comp_mask	=
+		msm_camera_io_r(vfe31_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+
+	if ((vfe31_ctrl->operation_mode ==
+		 VFE_MODE_OF_OPERATION_SNAPSHOT) ||
+		(vfe31_ctrl->operation_mode ==
+		 VFE_MODE_OF_OPERATION_ZSL)){
+		if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_PT) {
+			irq_comp_mask |=
+				((0x1 << (vfe31_ctrl->outpath.out0.ch0 + 8)) |
+				(0x1 << (vfe31_ctrl->outpath.out0.ch1 + 8)));
+		}
+		if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_S) {
+			irq_comp_mask |=
+			((0x1 << (vfe31_ctrl->outpath.out1.ch0 + 8)) |
+			(0x1 << (vfe31_ctrl->outpath.out1.ch1 + 8)));
+		}
+		if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_PT) {
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+				vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch0]);
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+				vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch1]);
+		}
+		if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_S) {
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+				vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch0]);
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+				vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch1]);
+		}
+	} else {  /* this is raw snapshot mode. */
+		CDBG("config the comp imask for raw snapshot mode.\n");
+		if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_S) {
+			irq_comp_mask |=
+			(0x1 << (vfe31_ctrl->outpath.out1.ch0 + 8));
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+				vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch0]);
+		}
+	}
+	msm_camera_io_w(irq_comp_mask, vfe31_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+	if (p_sync->stereocam_enabled)
+		msm_camio_set_perf_lvl(S_STEREO_CAPTURE);
+	else
+		msm_camio_set_perf_lvl(S_CAPTURE);
+
+	usleep(1000);
+	vfe31_start_common();
+	return 0;
+}
+
+static int vfe31_start(void)
+{
+	uint32_t irq_comp_mask = 0;
+	struct msm_sync* p_sync = (struct msm_sync *)vfe_syncdata;
+	/* start command now is only good for continuous mode. */
+	if ((vfe31_ctrl->operation_mode != VFE_MODE_OF_OPERATION_CONTINUOUS) &&
+		(vfe31_ctrl->operation_mode != VFE_MODE_OF_OPERATION_VIDEO))
+		return 0;
+	irq_comp_mask =
+		msm_camera_io_r(vfe31_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+
+	if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_PT) {
+		irq_comp_mask |= (0x1 << vfe31_ctrl->outpath.out0.ch0 |
+			0x1 << vfe31_ctrl->outpath.out0.ch1);
+			if (vfe31_ctrl->outpath.out0.ch2 >= 0)
+				irq_comp_mask |=
+					(0x1 << vfe31_ctrl->outpath.out0.ch0 |
+					0x1 << vfe31_ctrl->outpath.out0.ch1 |
+					0x1 << vfe31_ctrl->outpath.out0.ch2);
+	} else if (vfe31_ctrl->outpath.output_mode &
+		VFE31_OUTPUT_MODE_P_ALL_CHNLS) {
+			irq_comp_mask |= (0x1 << vfe31_ctrl->outpath.out0.ch0 |
+				0x1 << vfe31_ctrl->outpath.out0.ch1 |
+				0x1 << vfe31_ctrl->outpath.out0.ch2);
+	}
+
+	if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_V) {
+		irq_comp_mask |= (0x1 << (vfe31_ctrl->outpath.out2.ch0 + 16)|
+			0x1 << (vfe31_ctrl->outpath.out2.ch1 + 16));
+	}
+
+	msm_camera_io_w(irq_comp_mask, vfe31_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+
+
+	if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_PT) {
+		msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch0]);
+		msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch1]);
+		if (vfe31_ctrl->outpath.out0.ch2 >= 0)
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch2]);
+	} else if (vfe31_ctrl->outpath.output_mode &
+		VFE31_OUTPUT_MODE_P_ALL_CHNLS) {
+		msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch0]);
+		msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch1]);
+		msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch2]);
+	}
+
+	if (p_sync->stereocam_enabled)
+		msm_camio_set_perf_lvl(S_STEREO_VIDEO);
+	else
+		msm_camio_set_perf_lvl(S_PREVIEW);
+
+	usleep(1000);
+	vfe31_start_common();
+	return 0;
+}
+
+static void vfe31_update(void)
+{
+	unsigned long flags;
+	CDBG("vfe31_update\n");
+
+	if (vfe31_ctrl->update_gamma) {
+		if (!msm_camera_io_r(vfe31_ctrl->vfebase + V31_GAMMA_CFG_OFF))
+			msm_camera_io_w(7,
+				vfe31_ctrl->vfebase+V31_GAMMA_CFG_OFF);
+		else
+			msm_camera_io_w(0,
+				vfe31_ctrl->vfebase+V31_GAMMA_CFG_OFF);
+		vfe31_ctrl->update_gamma = false;
+	}
+	if (vfe31_ctrl->update_luma) {
+		if (!msm_camera_io_r(vfe31_ctrl->vfebase + V31_LUMA_CFG_OFF))
+			msm_camera_io_w(1,
+				vfe31_ctrl->vfebase + V31_LUMA_CFG_OFF);
+		else
+			msm_camera_io_w(0,
+				vfe31_ctrl->vfebase + V31_LUMA_CFG_OFF);
+		vfe31_ctrl->update_luma = false;
+	}
+	spin_lock_irqsave(&vfe31_ctrl->update_ack_lock, flags);
+	vfe31_ctrl->update_ack_pending = TRUE;
+	spin_unlock_irqrestore(&vfe31_ctrl->update_ack_lock, flags);
+	/* Ensure the write order while writing
+	to the command register using the barrier */
+	msm_camera_io_w_mb(1, vfe31_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+	return;
+}
+
+static void vfe31_sync_timer_stop(void)
+{
+	uint32_t value = 0;
+	vfe31_ctrl->sync_timer_state = 0;
+	if (vfe31_ctrl->sync_timer_number == 0)
+		value = 0x10000;
+	else if (vfe31_ctrl->sync_timer_number == 1)
+		value = 0x20000;
+	else if (vfe31_ctrl->sync_timer_number == 2)
+		value = 0x40000;
+
+	/* Timer Stop */
+	msm_camera_io_w_mb(value, vfe31_ctrl->vfebase + V31_SYNC_TIMER_OFF);
+}
+
+static void vfe31_sync_timer_start(const uint32_t *tbl)
+{
+	/* set bit 8 for auto increment. */
+	uint32_t value = 1;
+	uint32_t val;
+
+	vfe31_ctrl->sync_timer_state = *tbl++;
+	vfe31_ctrl->sync_timer_repeat_count = *tbl++;
+	vfe31_ctrl->sync_timer_number = *tbl++;
+	CDBG("%s timer_state %d, repeat_cnt %d timer number %d\n",
+		 __func__, vfe31_ctrl->sync_timer_state,
+		 vfe31_ctrl->sync_timer_repeat_count,
+		 vfe31_ctrl->sync_timer_number);
+
+	if (vfe31_ctrl->sync_timer_state) { /* Start Timer */
+		value = value << vfe31_ctrl->sync_timer_number;
+	} else { /* Stop Timer */
+		CDBG("Failed to Start timer\n");
+		 return;
+	}
+
+	/* Timer Start */
+	msm_camera_io_w(value, vfe31_ctrl->vfebase + V31_SYNC_TIMER_OFF);
+	/* Sync Timer Line Start */
+	value = *tbl++;
+	msm_camera_io_w(value, vfe31_ctrl->vfebase + V31_SYNC_TIMER_OFF +
+		4 + ((vfe31_ctrl->sync_timer_number) * 12));
+	/* Sync Timer Pixel Start */
+	value = *tbl++;
+	msm_camera_io_w(value, vfe31_ctrl->vfebase + V31_SYNC_TIMER_OFF +
+			 8 + ((vfe31_ctrl->sync_timer_number) * 12));
+	/* Sync Timer Pixel Duration */
+	value = *tbl++;
+	val = camio_clk.vfe_clk_rate / 10000;
+	val = 10000000 / val;
+	val = value * 10000 / val;
+	CDBG("%s: Pixel Clk Cycles!!! %d\n", __func__, val);
+	msm_camera_io_w(val, vfe31_ctrl->vfebase + V31_SYNC_TIMER_OFF +
+		12 + ((vfe31_ctrl->sync_timer_number) * 12));
+	/* Timer0 Active High/LOW */
+	value = *tbl++;
+	msm_camera_io_w(value,
+		vfe31_ctrl->vfebase + V31_SYNC_TIMER_POLARITY_OFF);
+	/* Selects sync timer 0 output to drive onto timer1 port */
+	value = 0;
+	msm_camera_io_w(value, vfe31_ctrl->vfebase + V31_TIMER_SELECT_OFF);
+	wmb();
+}
+
+static void vfe31_program_dmi_cfg(enum VFE31_DMI_RAM_SEL bankSel)
+{
+	/* set bit 8 for auto increment. */
+	uint32_t value = VFE_DMI_CFG_DEFAULT;
+	value += (uint32_t)bankSel;
+
+	msm_camera_io_w_mb(value, vfe31_ctrl->vfebase + VFE_DMI_CFG);
+	/* by default, always starts with offset 0.*/
+	msm_camera_io_w(0, vfe31_ctrl->vfebase + VFE_DMI_ADDR);
+	wmb();
+}
+static void vfe31_write_gamma_cfg(enum VFE31_DMI_RAM_SEL channel_sel,
+						const uint32_t *tbl)
+{
+	int i;
+	uint32_t value, value1, value2;
+	vfe31_program_dmi_cfg(channel_sel);
+	/* for loop for extracting init table. */
+	for (i = 0 ; i < (VFE31_GAMMA_NUM_ENTRIES/2) ; i++) {
+		value = *tbl++;
+		value1 = value & 0x0000FFFF;
+		value2 = (value & 0xFFFF0000)>>16;
+		msm_camera_io_w((value1),
+			vfe31_ctrl->vfebase + VFE_DMI_DATA_LO);
+		msm_camera_io_w((value2),
+			vfe31_ctrl->vfebase + VFE_DMI_DATA_LO);
+	}
+	vfe31_program_dmi_cfg(NO_MEM_SELECTED);
+}
+
+static void vfe31_reset_hist_cfg()
+{
+	uint32_t i;
+	uint32_t value = 0;
+
+	vfe31_program_dmi_cfg(STATS_HIST_RAM);
+	for (i = 0 ; i < VFE31_HIST_TABLE_LENGTH ; i++)
+		msm_camera_io_w(value, vfe31_ctrl->vfebase + VFE_DMI_DATA_LO);
+	vfe31_program_dmi_cfg(NO_MEM_SELECTED);
+}
+
+static void vfe31_write_la_cfg(enum VFE31_DMI_RAM_SEL channel_sel,
+						const uint32_t *tbl)
+{
+	uint32_t i;
+	uint32_t value, value1, value2;
+
+	vfe31_program_dmi_cfg(channel_sel);
+	/* for loop for extracting init table. */
+	for (i = 0 ; i < (VFE31_LA_TABLE_LENGTH/2) ; i++) {
+		value = *tbl++;
+		value1 = value & 0x0000FFFF;
+		value2 = (value & 0xFFFF0000)>>16;
+		msm_camera_io_w((value1),
+			vfe31_ctrl->vfebase + VFE_DMI_DATA_LO);
+		msm_camera_io_w((value2),
+			vfe31_ctrl->vfebase + VFE_DMI_DATA_LO);
+	}
+	vfe31_program_dmi_cfg(NO_MEM_SELECTED);
+}
+
+static int vfe31_proc_general(struct msm_vfe31_cmd *cmd)
+{
+	int i , rc = 0;
+	uint32_t old_val = 0 , new_val = 0;
+	uint32_t *cmdp = NULL;
+	uint32_t *cmdp_local = NULL;
+	uint32_t snapshot_cnt = 0;
+	uint32_t stereo_cam_enable = 0;
+	struct msm_sync* p_sync = (struct msm_sync *)vfe_syncdata;
+
+	CDBG("vfe31_proc_general: cmdID = %s, length = %d\n",
+		vfe31_general_cmd[cmd->id], cmd->length);
+	switch (cmd->id) {
+	case V31_RESET:
+		pr_info("vfe31_proc_general: cmdID = %s\n",
+			vfe31_general_cmd[cmd->id]);
+		vfe31_reset();
+		break;
+	case V31_START:
+		pr_info("vfe31_proc_general: cmdID = %s\n",
+			vfe31_general_cmd[cmd->id]);
+		rc = vfe31_start();
+		break;
+	case V31_UPDATE:
+		vfe31_update();
+		break;
+	case V31_ZSL:
+		pr_info("vfe31_proc_general: cmdID = %s\n",
+			vfe31_general_cmd[cmd->id]);
+		vfe31_zsl();
+		break;
+	case V31_CAPTURE:
+		pr_info("vfe31_proc_general: cmdID = %s\n",
+			vfe31_general_cmd[cmd->id]);
+		if (copy_from_user(&snapshot_cnt, (void __user *)(cmd->value),
+				sizeof(uint32_t))) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		rc = vfe31_capture(snapshot_cnt);
+		break;
+	case V31_START_RECORDING:
+		pr_info("vfe31_proc_general: cmdID = %s\n",
+			vfe31_general_cmd[cmd->id]);
+		rc = vfe31_start_recording();
+		if (p_sync->stereocam_enabled)
+			p_sync->stereo_state = STEREO_VIDEO_ACTIVE;
+		break;
+	case V31_STOP_RECORDING:
+		pr_info("vfe31_proc_general: cmdID = %s\n",
+			vfe31_general_cmd[cmd->id]);
+		rc = vfe31_stop_recording();
+		if (p_sync->stereocam_enabled)
+			p_sync->stereo_state = STEREO_VIDEO_IDLE;
+		break;
+	case V31_OPERATION_CFG: {
+		if (cmd->length != V31_OPERATION_CFG_LEN) {
+			rc = -EINVAL;
+			goto proc_general_done;
+		}
+		cmdp = kmalloc(V31_OPERATION_CFG_LEN, GFP_ATOMIC);
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			V31_OPERATION_CFG_LEN)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		rc = vfe31_operation_config(cmdp);
+		}
+		break;
+
+	case V31_STATS_AE_START: {
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		old_val = msm_camera_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val |= AE_ENABLE_MASK;
+		msm_camera_io_w(old_val,
+			vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+		cmdp, (vfe31_cmd[cmd->id].length));
+		}
+		break;
+	case V31_STATS_AF_START: {
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		old_val = msm_camera_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val |= AF_ENABLE_MASK;
+		msm_camera_io_w(old_val,
+			vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+		cmdp, (vfe31_cmd[cmd->id].length));
+		}
+		break;
+	case V31_STATS_AWB_START: {
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		old_val = msm_camera_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val |= AWB_ENABLE_MASK;
+		msm_camera_io_w(old_val,
+			vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+			cmdp, (vfe31_cmd[cmd->id].length));
+		}
+		break;
+
+	case V31_STATS_IHIST_START: {
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		old_val = msm_camera_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val |= IHIST_ENABLE_MASK;
+		msm_camera_io_w(old_val,
+			vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+			cmdp, (vfe31_cmd[cmd->id].length));
+		}
+		break;
+
+	case V31_XBAR_CFG: {
+		unsigned long flags = 0;
+		spin_lock_irqsave(&vfe31_ctrl->xbar_lock, flags);
+		if ((cmd->length != V31_XBAR_CFG_LEN)
+			|| vfe31_ctrl->xbar_update_pending) {
+			rc = -EINVAL;
+			spin_unlock_irqrestore(&vfe31_ctrl->xbar_lock, flags);
+			goto proc_general_done;
+		}
+		spin_unlock_irqrestore(&vfe31_ctrl->xbar_lock, flags);
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		spin_lock_irqsave(&vfe31_ctrl->xbar_lock, flags);
+		vfe31_ctrl->xbar_cfg[0] = *cmdp;
+		vfe31_ctrl->xbar_cfg[1] = *(cmdp+1);
+		vfe31_ctrl->xbar_update_pending = 1;
+		spin_unlock_irqrestore(&vfe31_ctrl->xbar_lock, flags);
+		CDBG("%s: xbar0 0x%x xbar1 0x%x", __func__,
+			vfe31_ctrl->xbar_cfg[0],
+			vfe31_ctrl->xbar_cfg[1]);
+		}
+		break;
+
+	case V31_STATS_RS_START: {
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+			cmdp, (vfe31_cmd[cmd->id].length));
+		}
+		break;
+
+	case V31_STATS_CS_START: {
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+			cmdp, (vfe31_cmd[cmd->id].length));
+		}
+		break;
+
+	case V31_MCE_UPDATE:
+	case V31_MCE_CFG:{
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		/* Incrementing with 4 so as to point to the 2nd Register as
+		the 2nd register has the mce_enable bit */
+		old_val = msm_camera_io_r(vfe31_ctrl->vfebase +
+						V31_CHROMA_SUP_OFF + 4);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		cmdp_local = cmdp;
+		new_val = *cmdp_local;
+		old_val &= MCE_EN_MASK;
+		new_val = new_val | old_val;
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + V31_CHROMA_SUP_OFF + 4,
+			&new_val, 4);
+		cmdp_local += 1;
+
+		old_val = msm_camera_io_r(vfe31_ctrl->vfebase +
+						V31_CHROMA_SUP_OFF + 8);
+		new_val = *cmdp_local;
+		old_val &= MCE_Q_K_MASK;
+		new_val = new_val | old_val;
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + V31_CHROMA_SUP_OFF + 8,
+			&new_val, 4);
+		cmdp_local += 1;
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+			cmdp_local, (vfe31_cmd[cmd->id].length));
+		}
+		break;
+	case V31_DEMOSAIC_2_UPDATE: /* 38 BPC update   */
+	case V31_DEMOSAIC_2_CFG: {  /* 14 BPC config   */
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		cmdp_local = cmdp;
+		new_val = *cmdp_local;
+
+		old_val = msm_camera_io_r(
+				vfe31_ctrl->vfebase + V31_DEMOSAIC_0_OFF);
+		old_val &= BPC_MASK;
+
+		new_val = new_val | old_val;
+		*cmdp_local = new_val;
+		msm_camera_io_memcpy(vfe31_ctrl->vfebase + V31_DEMOSAIC_0_OFF,
+					cmdp_local, 4);
+		cmdp_local += 1;
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+			cmdp_local, (vfe31_cmd[cmd->id].length));
+		}
+		break;
+	case V31_DEMOSAIC_1_UPDATE:/* 37 ABF update  */
+	case V31_DEMOSAIC_1_CFG: { /* 13 ABF config  */
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		cmdp_local = cmdp;
+		new_val = *cmdp_local;
+
+		old_val = msm_camera_io_r(
+				vfe31_ctrl->vfebase + V31_DEMOSAIC_0_OFF);
+		old_val &= ABF_MASK;
+		new_val = new_val | old_val;
+		*cmdp_local = new_val;
+
+		msm_camera_io_memcpy(vfe31_ctrl->vfebase + V31_DEMOSAIC_0_OFF,
+		    cmdp_local, 4);
+
+		cmdp_local += 1;
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+			cmdp_local, (vfe31_cmd[cmd->id].length));
+		}
+		break;
+	case V31_ROLL_OFF_CFG: {
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value) , cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		cmdp_local = cmdp;
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+		cmdp_local, 16);
+		cmdp_local += 4;
+		vfe31_program_dmi_cfg(ROLLOFF_RAM);
+		/* for loop for extrcting init table. */
+		for (i = 0 ; i < (VFE31_ROLL_OFF_INIT_TABLE_SIZE * 2) ; i++) {
+			msm_camera_io_w(*cmdp_local ,
+			vfe31_ctrl->vfebase + VFE_DMI_DATA_LO);
+			cmdp_local++;
+		}
+		CDBG("done writing init table\n");
+		/* by default, always starts with offset 0. */
+		msm_camera_io_w(LENS_ROLL_OFF_DELTA_TABLE_OFFSET,
+		vfe31_ctrl->vfebase + VFE_DMI_ADDR);
+		/* for loop for extracting delta table. */
+		for (i = 0 ; i < (VFE31_ROLL_OFF_DELTA_TABLE_SIZE * 2) ; i++) {
+			msm_camera_io_w(*cmdp_local,
+			vfe31_ctrl->vfebase + VFE_DMI_DATA_LO);
+			cmdp_local++;
+		}
+		vfe31_program_dmi_cfg(NO_MEM_SELECTED);
+		}
+		break;
+
+	case V31_LA_CFG:{
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		/* Select Bank 0*/
+		*cmdp = 0;
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+			cmdp, (vfe31_cmd[cmd->id].length));
+		cmdp += 1;
+		vfe31_write_la_cfg(LUMA_ADAPT_LUT_RAM_BANK0 , cmdp);
+		cmdp -= 1;
+		}
+		break;
+
+	case V31_LA_UPDATE: {
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		old_val = msm_camera_io_r(
+				vfe31_ctrl->vfebase + V31_LUMA_CFG_OFF);
+		cmdp += 1;
+		if (old_val != 0x0)
+			vfe31_write_la_cfg(LUMA_ADAPT_LUT_RAM_BANK0 , cmdp);
+		else
+			vfe31_write_la_cfg(LUMA_ADAPT_LUT_RAM_BANK1 , cmdp);
+		vfe31_ctrl->update_luma = true;
+		cmdp -= 1;
+		}
+		break;
+
+	case V31_SK_ENHAN_CFG:
+	case V31_SK_ENHAN_UPDATE:{
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		msm_camera_io_memcpy(vfe31_ctrl->vfebase + V31_SCE_OFF,
+				cmdp, V31_SCE_LEN);
+		}
+		break;
+
+	case V31_LIVESHOT:
+		vfe31_liveshot();
+		break;
+
+	case V31_STEREOCAM:
+		if (copy_from_user(&stereo_cam_enable,
+			(void __user *)(cmd->value), sizeof(uint32_t))) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		vfe31_stereocam(stereo_cam_enable);
+		break;
+
+	case V31_RGB_G_CFG: {
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		/* Select Bank 0*/
+		*cmdp = 0;
+		msm_camera_io_memcpy(vfe31_ctrl->vfebase + V31_RGB_G_OFF,
+				cmdp, 4);
+		cmdp += 1;
+		vfe31_write_gamma_cfg(RGBLUT_CHX_BANK0, cmdp);
+		cmdp -= 1;
+		}
+		break;
+
+	case V31_RGB_G_UPDATE: {
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp, (void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		old_val = msm_camera_io_r(
+				vfe31_ctrl->vfebase + V31_GAMMA_CFG_OFF);
+		cmdp += 1;
+
+		if (!old_val) {
+			vfe31_write_gamma_cfg(RGBLUT_CHX_BANK1, cmdp);
+		} else {
+			vfe31_write_gamma_cfg(RGBLUT_CHX_BANK0, cmdp);
+			}
+		vfe31_ctrl->update_gamma = true;
+		cmdp -= 1;
+		}
+		break;
+
+	case V31_STATS_AWB_STOP: {
+		old_val = msm_camera_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val &= ~AWB_ENABLE_MASK;
+		msm_camera_io_w(old_val,
+			vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		}
+		break;
+	case V31_STATS_AE_STOP: {
+		old_val = msm_camera_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val &= ~AE_ENABLE_MASK;
+		msm_camera_io_w(old_val,
+			vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		}
+		break;
+	case V31_STATS_AF_STOP: {
+		old_val = msm_camera_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val &= ~AF_ENABLE_MASK;
+		msm_camera_io_w(old_val,
+			vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		}
+		break;
+
+	case V31_STATS_IHIST_STOP: {
+		old_val = msm_camera_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val &= ~IHIST_ENABLE_MASK;
+		msm_camera_io_w(old_val,
+			vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		}
+		break;
+
+	case V31_STATS_RS_STOP: {
+		old_val = msm_camera_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val &= ~RS_ENABLE_MASK;
+		msm_camera_io_w(old_val,
+			vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		}
+		break;
+
+	case V31_STATS_CS_STOP: {
+		old_val = msm_camera_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val &= ~CS_ENABLE_MASK;
+		msm_camera_io_w(old_val,
+			vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		}
+		break;
+	case V31_STOP:
+		pr_info("vfe31_proc_general: cmdID = %s\n",
+			vfe31_general_cmd[cmd->id]);
+		vfe31_stop();
+		break;
+
+	case V31_SYNC_TIMER_SETTING:
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp, (void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		vfe31_sync_timer_start(cmdp);
+		break;
+
+	case V31_EZTUNE_CFG: {
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		*cmdp &= ~STATS_ENABLE_MASK;
+		old_val = msm_camera_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val &= STATS_ENABLE_MASK;
+		*cmdp |= old_val;
+
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+			cmdp, (vfe31_cmd[cmd->id].length));
+		}
+		break;
+
+	default: {
+		if (cmd->length != vfe31_cmd[cmd->id].length)
+			return -EINVAL;
+
+		cmdp = kmalloc(vfe31_cmd[cmd->id].length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+
+		if (copy_from_user(cmdp, (void __user *)cmd->value,
+				cmd->length)) {
+			rc = -EFAULT;
+			pr_err("%s copy from user failed for cmd %d",
+				__func__, cmd->id);
+			goto proc_general_done;
+		}
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+			cmdp, (vfe31_cmd[cmd->id].length));
+	}
+	break;
+
+	}
+
+proc_general_done:
+	kfree(cmdp);
+
+	return rc;
+}
+
+static void vfe31_stats_af_ack(struct vfe_cmd_stats_ack *pAck)
+{
+	vfe31_ctrl->afStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+	vfe31_ctrl->af_ack_pending = FALSE;
+}
+
+static void vfe31_stats_awb_ack(struct vfe_cmd_stats_ack *pAck)
+{
+	vfe31_ctrl->awbStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+	vfe31_ctrl->awb_ack_pending = FALSE;
+}
+
+static void vfe31_stats_aec_ack(struct vfe_cmd_stats_ack *pAck)
+{
+	vfe31_ctrl->aecStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+	vfe31_ctrl->aec_ack_pending = FALSE;
+}
+
+static void vfe31_stats_ihist_ack(struct vfe_cmd_stats_ack *pAck)
+{
+	vfe31_ctrl->ihistStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+	vfe31_ctrl->ihist_ack_pending = FALSE;
+}
+
+static void vfe31_stats_rs_ack(struct vfe_cmd_stats_ack *pAck)
+{
+	vfe31_ctrl->rsStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+	vfe31_ctrl->rs_ack_pending = FALSE;
+}
+
+static void vfe31_stats_cs_ack(struct vfe_cmd_stats_ack *pAck)
+{
+	vfe31_ctrl->csStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+	vfe31_ctrl->cs_ack_pending = FALSE;
+}
+
+static int vfe31_config(struct msm_vfe_cfg_cmd *cmd, void *data)
+{
+	struct msm_vfe31_cmd vfecmd;
+
+	long rc = 0;
+	uint32_t i = 0;
+	struct vfe_cmd_stats_buf *scfg = NULL;
+	struct msm_pmem_region   *regptr = NULL;
+	struct vfe_cmd_stats_ack *sack = NULL;
+
+	if (cmd->cmd_type != CMD_FRAME_BUF_RELEASE &&
+		cmd->cmd_type != CMD_SNAP_BUF_RELEASE &&
+		cmd->cmd_type != CMD_STATS_AEC_BUF_RELEASE &&
+		cmd->cmd_type != CMD_STATS_AWB_BUF_RELEASE &&
+		cmd->cmd_type != CMD_STATS_IHIST_BUF_RELEASE &&
+		cmd->cmd_type != CMD_STATS_RS_BUF_RELEASE &&
+		cmd->cmd_type != CMD_STATS_CS_BUF_RELEASE &&
+		cmd->cmd_type != CMD_STATS_AF_BUF_RELEASE) {
+		if (copy_from_user(&vfecmd,
+				(void __user *)(cmd->value),
+				sizeof(vfecmd))) {
+			pr_err("%s %d: copy_from_user failed\n", __func__,
+				__LINE__);
+			return -EFAULT;
+		}
+	} else {
+	/* here eith stats release or frame release. */
+		if (cmd->cmd_type != CMD_FRAME_BUF_RELEASE &&
+			cmd->cmd_type != CMD_SNAP_BUF_RELEASE) {
+			/* then must be stats release. */
+			if (!data)
+				return -EFAULT;
+				sack = kmalloc(sizeof(struct vfe_cmd_stats_ack),
+				GFP_ATOMIC);
+				if (!sack)
+					return -ENOMEM;
+
+				sack->nextStatsBuf = *(uint32_t *)data;
+			}
+	}
+
+	CDBG("%s: cmdType = %d\n", __func__, cmd->cmd_type);
+
+	if ((cmd->cmd_type == CMD_STATS_AF_ENABLE) ||
+		(cmd->cmd_type == CMD_STATS_AWB_ENABLE) ||
+		(cmd->cmd_type == CMD_STATS_IHIST_ENABLE) ||
+		(cmd->cmd_type == CMD_STATS_RS_ENABLE) ||
+		(cmd->cmd_type == CMD_STATS_CS_ENABLE) ||
+		(cmd->cmd_type == CMD_STATS_AEC_ENABLE)) {
+		struct axidata *axid;
+		axid = data;
+		if (!axid) {
+			rc = -EFAULT;
+			goto vfe31_config_done;
+		}
+
+		scfg =
+			kmalloc(sizeof(struct vfe_cmd_stats_buf),
+				GFP_ATOMIC);
+		if (!scfg) {
+			rc = -ENOMEM;
+			goto vfe31_config_done;
+		}
+		regptr = axid->region;
+		if (axid->bufnum1 > 0) {
+			for (i = 0; i < axid->bufnum1; i++) {
+				scfg->statsBuf[i] =
+					(uint32_t)(regptr->paddr);
+				regptr++;
+			}
+		}
+		/* individual */
+		switch (cmd->cmd_type) {
+		case CMD_STATS_AEC_ENABLE:
+			rc = vfe_stats_aec_buf_init(scfg);
+			break;
+		case CMD_STATS_AF_ENABLE:
+			rc = vfe_stats_af_buf_init(scfg);
+			break;
+		case CMD_STATS_AWB_ENABLE:
+			rc = vfe_stats_awb_buf_init(scfg);
+			break;
+		case CMD_STATS_IHIST_ENABLE:
+			rc = vfe_stats_ihist_buf_init(scfg);
+			break;
+		case CMD_STATS_RS_ENABLE:
+			rc = vfe_stats_rs_buf_init(scfg);
+			break;
+		case CMD_STATS_CS_ENABLE:
+			rc = vfe_stats_cs_buf_init(scfg);
+			break;
+		}
+	}
+
+	switch (cmd->cmd_type) {
+	case CMD_GENERAL:
+		rc = vfe31_proc_general(&vfecmd);
+		break;
+
+	case CMD_FRAME_BUF_RELEASE: {
+		struct msm_frame *b;
+		unsigned long p;
+		int ret;
+		struct vfe31_output_ch *outch = NULL;
+		if (!data) {
+			rc = -EFAULT;
+			break;
+		}
+
+		b = (struct msm_frame *)(cmd->value);
+		p = *(unsigned long *)data;
+
+		CDBG("CMD_FRAME_BUF_RELEASE b->path = %d\n", b->path);
+
+		if (b->path & OUTPUT_TYPE_P) {
+			CDBG("CMD_FRAME_BUF_RELEASE got free buffer\n");
+			outch = &vfe31_ctrl->outpath.out0;
+		} else if (b->path & OUTPUT_TYPE_S) {
+			outch = &vfe31_ctrl->outpath.out1;
+		} else if (b->path & OUTPUT_TYPE_V) {
+			outch = &vfe31_ctrl->outpath.out2;
+		} else {
+			rc = -EFAULT;
+			break;
+		}
+
+		ret = vfe31_add_free_buf2(outch, p, b->planar0_off,
+			b->planar1_off, b->planar2_off);
+		if (ret < 0)
+			return ret;
+		break;
+	}
+
+	case CMD_SNAP_BUF_RELEASE: {
+		struct msm_frame *b;
+		unsigned long p;
+		int ret;
+		struct vfe31_output_ch *outch = NULL;
+		if (!data)
+			return -EFAULT;
+
+		b = (struct msm_frame *)(cmd->value);
+		p = *(unsigned long *)data;
+
+		CDBG("CMD_PIC_BUF_RELEASE b->path = %d\n", b->path);
+
+		if (b->path & OUTPUT_TYPE_T) {
+			CDBG("CMD_FRAME_BUF_RELEASE got free buffer\n");
+			outch = &vfe31_ctrl->outpath.out1;
+		} else if (b->path & OUTPUT_TYPE_S) {
+			outch = &vfe31_ctrl->outpath.out2;
+		} else
+			return -EFAULT;
+
+		ret = vfe31_add_free_buf2(outch, p, b->planar0_off,
+			b->planar1_off,	b->planar2_off);
+		if (ret < 0)
+			return ret;
+		break;
+	}
+
+	case CMD_STATS_AEC_BUF_RELEASE:
+		vfe31_stats_aec_ack(sack);
+		break;
+
+	case CMD_STATS_AF_BUF_RELEASE:
+		vfe31_stats_af_ack(sack);
+		break;
+
+	case CMD_STATS_AWB_BUF_RELEASE:
+		vfe31_stats_awb_ack(sack);
+		break;
+
+	case CMD_STATS_IHIST_BUF_RELEASE:
+		vfe31_stats_ihist_ack(sack);
+		break;
+
+	case CMD_STATS_RS_BUF_RELEASE:
+		vfe31_stats_rs_ack(sack);
+		break;
+
+	case CMD_STATS_CS_BUF_RELEASE:
+		vfe31_stats_cs_ack(sack);
+		break;
+
+	case CMD_AXI_CFG_PREVIEW: {
+		struct axidata *axid;
+		uint32_t *axio = NULL;
+		axid = data;
+		if (!axid) {
+			rc = -EFAULT;
+			break;
+		}
+		axio =
+			kmalloc(vfe31_cmd[V31_AXI_OUT_CFG].length,
+				GFP_ATOMIC);
+		if (!axio) {
+			rc = -ENOMEM;
+			break;
+		}
+
+		if (copy_from_user(axio, (void __user *)(vfecmd.value),
+				vfe31_cmd[V31_AXI_OUT_CFG].length)) {
+			kfree(axio);
+			rc = -EFAULT;
+			break;
+		}
+		vfe31_config_axi(OUTPUT_2, axid, axio);
+		kfree(axio);
+		break;
+	}
+
+	case CMD_RAW_PICT_AXI_CFG: {
+		struct axidata *axid;
+		uint32_t *axio = NULL;
+		axid = data;
+		if (!axid) {
+			rc = -EFAULT;
+			break;
+		}
+		axio =
+			kmalloc(vfe31_cmd[V31_AXI_OUT_CFG].length,
+				GFP_ATOMIC);
+		if (!axio) {
+			rc = -ENOMEM;
+			break;
+		}
+
+		if (copy_from_user(axio, (void __user *)(vfecmd.value),
+				vfe31_cmd[V31_AXI_OUT_CFG].length)) {
+			kfree(axio);
+			rc = -EFAULT;
+			break;
+		}
+		vfe31_config_axi(CAMIF_TO_AXI_VIA_OUTPUT_2, axid, axio);
+		kfree(axio);
+		break;
+	}
+
+	case CMD_AXI_CFG_SNAP: {
+		struct axidata *axid;
+		uint32_t *axio = NULL;
+		CDBG("%s, CMD_AXI_CFG_SNAP\n", __func__);
+		axid = data;
+		if (!axid)
+			return -EFAULT;
+		axio =
+			kmalloc(vfe31_cmd[V31_AXI_OUT_CFG].length,
+				GFP_ATOMIC);
+		if (!axio) {
+			rc = -ENOMEM;
+			break;
+		}
+
+		if (copy_from_user(axio, (void __user *)(vfecmd.value),
+				vfe31_cmd[V31_AXI_OUT_CFG].length)) {
+			kfree(axio);
+			rc = -EFAULT;
+			break;
+		}
+		vfe31_config_axi(OUTPUT_1_AND_2, axid, axio);
+		kfree(axio);
+		break;
+	}
+
+	case CMD_AXI_CFG_ZSL: {
+		struct axidata *axid;
+		uint32_t *axio = NULL;
+		CDBG("%s, CMD_AXI_CFG_ZSL\n", __func__);
+		axid = data;
+		if (!axid)
+			return -EFAULT;
+		axio =
+			kmalloc(vfe31_cmd[V31_AXI_OUT_CFG].length,
+				GFP_ATOMIC);
+		if (!axio) {
+			rc = -ENOMEM;
+			break;
+		}
+
+		if (copy_from_user(axio, (void __user *)(vfecmd.value),
+				vfe31_cmd[V31_AXI_OUT_CFG].length)) {
+			kfree(axio);
+			rc = -EFAULT;
+			break;
+		}
+		vfe31_config_axi(OUTPUT_1_2_AND_3, axid, axio);
+		kfree(axio);
+	}
+		break;
+
+	case CMD_AXI_CFG_ZSL_ALL_CHNLS: {
+		struct axidata *axid;
+		uint32_t *axio;
+		CDBG("%s, CMD_AXI_CFG_ZSL\n", __func__);
+		axid = data;
+		if (!axid)
+			return -EFAULT;
+		axio =
+			kmalloc(vfe31_cmd[V31_AXI_OUT_CFG].length,
+				GFP_ATOMIC);
+		if (!axio) {
+			rc = -ENOMEM;
+			break;
+		}
+
+		if (copy_from_user(axio, (void __user *)(vfecmd.value),
+				vfe31_cmd[V31_AXI_OUT_CFG].length)) {
+			kfree(axio);
+			rc = -EFAULT;
+			break;
+		}
+		vfe31_config_axi(OUTPUT_ZSL_ALL_CHNLS, axid, axio);
+		kfree(axio);
+	}
+		break;
+
+	case CMD_AXI_CFG_VIDEO: {
+		struct axidata *axid;
+		uint32_t *axio = NULL;
+		axid = data;
+		if (!axid) {
+			rc = -EFAULT;
+			break;
+		}
+
+		axio =
+			kmalloc(vfe31_cmd[V31_AXI_OUT_CFG].length,
+				GFP_ATOMIC);
+		if (!axio) {
+			rc = -ENOMEM;
+			break;
+		}
+
+		if (copy_from_user(axio, (void __user *)(vfecmd.value),
+				vfe31_cmd[V31_AXI_OUT_CFG].length)) {
+			kfree(axio);
+			rc = -EFAULT;
+			break;
+		}
+		vfe31_config_axi(OUTPUT_1_AND_3, axid, axio);
+		kfree(axio);
+		break;
+	}
+
+	case CMD_AXI_CFG_VIDEO_ALL_CHNLS: {
+		struct axidata *axid;
+		uint32_t *axio = NULL;
+		axid = data;
+		if (!axid) {
+			rc = -EFAULT;
+			break;
+		}
+
+		axio =
+			kmalloc(vfe31_cmd[V31_AXI_OUT_CFG].length,
+				GFP_ATOMIC);
+		if (!axio) {
+			rc = -ENOMEM;
+			break;
+		}
+
+		if (copy_from_user(axio, (void __user *)(vfecmd.value),
+				vfe31_cmd[V31_AXI_OUT_CFG].length)) {
+			kfree(axio);
+			rc = -EFAULT;
+			break;
+		}
+		vfe31_config_axi(OUTPUT_VIDEO_ALL_CHNLS, axid, axio);
+		kfree(axio);
+		break;
+	}
+
+	default:
+		break;
+	}
+vfe31_config_done:
+	kfree(scfg);
+	kfree(sack);
+	CDBG("%s done: rc = %d\n", __func__, (int) rc);
+	return rc;
+}
+
+static void vfe31_send_msg_no_payload(enum VFE31_MESSAGE_ID id)
+{
+	struct vfe_message msg;
+
+	CDBG("vfe31_send_msg_no_payload\n");
+	msg._d = id;
+	vfe31_proc_ops(id, &msg, 0);
+}
+
+static void vfe31_process_reg_update_irq(void)
+{
+	uint32_t  temp, old_val;
+	unsigned long flags;
+	if (vfe31_ctrl->recording_state == VFE_REC_STATE_START_REQUESTED) {
+		if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_V) {
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+				vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out2.ch0]);
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+				vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out2.ch1]);
+		}
+		vfe31_ctrl->recording_state = VFE_REC_STATE_STARTED;
+		if (vpe_ctrl->dis_en) {
+			old_val = msm_camera_io_r(
+				vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+			old_val |= RS_CS_ENABLE_MASK;
+			msm_camera_io_w(old_val,
+				vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		}
+		msm_camera_io_w_mb(1, vfe31_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+		CDBG("start video triggered .\n");
+	} else if (vfe31_ctrl->recording_state
+			== VFE_REC_STATE_STOP_REQUESTED) {
+		if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_V) {
+			msm_camera_io_w(0, vfe31_ctrl->vfebase +
+				vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out2.ch0]);
+			msm_camera_io_w(0, vfe31_ctrl->vfebase +
+				vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out2.ch1]);
+		}
+
+		/*disable rs& cs when stop recording. */
+		old_val = msm_camera_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val &= (~RS_CS_ENABLE_MASK);
+		msm_camera_io_w(old_val,
+				vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		CDBG("stop video triggered\n");
+	}
+	if (vfe31_ctrl->start_ack_pending == TRUE) {
+		vfe31_send_msg_no_payload(MSG_ID_START_ACK);
+		vfe31_ctrl->start_ack_pending = FALSE;
+	} else {
+		if (vfe31_ctrl->recording_state ==
+			VFE_REC_STATE_STOP_REQUESTED) {
+			vfe31_ctrl->recording_state = VFE_REC_STATE_STOPPED;
+			msm_camera_io_w_mb(1, vfe31_ctrl->vfebase +
+						VFE_REG_UPDATE_CMD);
+		} else if (vfe31_ctrl->recording_state ==
+			VFE_REC_STATE_STOPPED) {
+			CDBG("sent stop video rec ACK");
+			vfe31_send_msg_no_payload(MSG_ID_STOP_REC_ACK);
+			vfe31_ctrl->recording_state = VFE_REC_STATE_IDLE;
+		}
+		spin_lock_irqsave(&vfe31_ctrl->update_ack_lock, flags);
+		if (vfe31_ctrl->update_ack_pending == TRUE) {
+			vfe31_ctrl->update_ack_pending = FALSE;
+			spin_unlock_irqrestore(
+				&vfe31_ctrl->update_ack_lock, flags);
+			vfe31_send_msg_no_payload(MSG_ID_UPDATE_ACK);
+		} else {
+			spin_unlock_irqrestore(
+				&vfe31_ctrl->update_ack_lock, flags);
+		}
+	}
+	/* in snapshot mode */
+	if (vfe31_ctrl->operation_mode ==
+		VFE_MODE_OF_OPERATION_SNAPSHOT) {
+		/* later we need to add check for live snapshot mode. */
+
+		if (vfe31_ctrl->vfe_capture_count)
+			vfe31_ctrl->vfe_capture_count--;
+		/* if last frame to be captured: */
+		if (vfe31_ctrl->vfe_capture_count == 0) {
+			/* stop the bus output:  write master enable = 0*/
+			if (vfe31_ctrl->outpath.output_mode &
+					VFE31_OUTPUT_MODE_PT) {
+				msm_camera_io_w(0, vfe31_ctrl->vfebase +
+					vfe31_AXI_WM_CFG[
+						vfe31_ctrl->outpath.out0.ch0]);
+				msm_camera_io_w(0, vfe31_ctrl->vfebase +
+					vfe31_AXI_WM_CFG[vfe31_ctrl->
+						outpath.out0.ch1]);
+			}
+			if (vfe31_ctrl->outpath.output_mode &
+					VFE31_OUTPUT_MODE_S) {
+				msm_camera_io_w(0, vfe31_ctrl->vfebase +
+					vfe31_AXI_WM_CFG[vfe31_ctrl->
+						outpath.out1.ch0]);
+				msm_camera_io_w(0, vfe31_ctrl->vfebase +
+					vfe31_AXI_WM_CFG[vfe31_ctrl->
+						outpath.out1.ch1]);
+			}
+
+			/* Ensure the write order while writing
+			to the command register using the barrier */
+			msm_camera_io_w_mb(CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY,
+				vfe31_ctrl->vfebase + VFE_CAMIF_COMMAND);
+
+			/* Ensure the read order while reading
+			to the command register using the barrier */
+			temp = msm_camera_io_r_mb(vfe31_ctrl->vfebase +
+				VFE_CAMIF_COMMAND);
+		}
+		/* then do reg_update. */
+		msm_camera_io_w_mb(1, vfe31_ctrl->vfebase +
+			VFE_REG_UPDATE_CMD);
+	} /* if snapshot mode. */
+}
+
+static void vfe31_set_default_reg_values(void)
+{
+	msm_camera_io_w(0x800080, vfe31_ctrl->vfebase + VFE_DEMUX_GAIN_0);
+	msm_camera_io_w(0x800080, vfe31_ctrl->vfebase + VFE_DEMUX_GAIN_1);
+	msm_camera_io_w(0xFFFFF, vfe31_ctrl->vfebase + VFE_CGC_OVERRIDE);
+
+	/* default frame drop period and pattern */
+	msm_camera_io_w(0x1f, vfe31_ctrl->vfebase + VFE_FRAMEDROP_ENC_Y_CFG);
+	msm_camera_io_w(0x1f, vfe31_ctrl->vfebase + VFE_FRAMEDROP_ENC_CBCR_CFG);
+	msm_camera_io_w(0xFFFFFFFF,
+		vfe31_ctrl->vfebase + VFE_FRAMEDROP_ENC_Y_PATTERN);
+	msm_camera_io_w(0xFFFFFFFF,
+		vfe31_ctrl->vfebase + VFE_FRAMEDROP_ENC_CBCR_PATTERN);
+	msm_camera_io_w(0x1f, vfe31_ctrl->vfebase + VFE_FRAMEDROP_VIEW_Y);
+	msm_camera_io_w(0x1f, vfe31_ctrl->vfebase + VFE_FRAMEDROP_VIEW_CBCR);
+	msm_camera_io_w(0xFFFFFFFF,
+		vfe31_ctrl->vfebase + VFE_FRAMEDROP_VIEW_Y_PATTERN);
+	msm_camera_io_w(0xFFFFFFFF,
+		vfe31_ctrl->vfebase + VFE_FRAMEDROP_VIEW_CBCR_PATTERN);
+	msm_camera_io_w(0, vfe31_ctrl->vfebase + VFE_CLAMP_MIN);
+	msm_camera_io_w(0xFFFFFF, vfe31_ctrl->vfebase + VFE_CLAMP_MAX);
+
+	/* stats UB config */
+	msm_camera_io_w(0x3980007,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_AEC_UB_CFG);
+	msm_camera_io_w(0x3A00007,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_AF_UB_CFG);
+	msm_camera_io_w(0x3A8000F,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_AWB_UB_CFG);
+	msm_camera_io_w(0x3B80007,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_RS_UB_CFG);
+	msm_camera_io_w(0x3C0001F,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_CS_UB_CFG);
+	msm_camera_io_w(0x3E0001F,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_HIST_UB_CFG);
+}
+
+static void vfe31_process_reset_irq(void)
+{
+	atomic_set(&vfe31_ctrl->vstate, 0);
+	vfe31_ctrl->while_stopping_mask = VFE_IMASK_WHILE_STOPPING_1;
+	if (atomic_read(&vfe31_ctrl->stop_ack_pending)) {
+		/* this is from the stop command. */
+		atomic_set(&vfe31_ctrl->stop_ack_pending, 0);
+		vfe31_send_msg_no_payload(MSG_ID_STOP_ACK);
+	} else {
+		/* this is from reset command. */
+		vfe31_set_default_reg_values();
+
+		/* reload all write masters. (frame & line)*/
+		msm_camera_io_w_mb(0x7FFF, vfe31_ctrl->vfebase + VFE_BUS_CMD);
+		vfe31_send_msg_no_payload(MSG_ID_RESET_ACK);
+	}
+}
+
+
+static void vfe31_process_axi_halt_irq(void)
+{
+	/* Ensure the write order while writing
+	to the command register using the barrier */
+	msm_camera_io_w_mb(AXI_HALT_CLEAR,
+		vfe31_ctrl->vfebase + VFE_AXI_CMD);
+	vfe31_ctrl->while_stopping_mask = VFE_IMASK_RESET;
+
+	/* disable all interrupts.  */
+	msm_camera_io_w(VFE_DISABLE_ALL_IRQS,
+		vfe31_ctrl->vfebase + VFE_IRQ_MASK_0);
+	msm_camera_io_w(VFE_DISABLE_ALL_IRQS,
+		vfe31_ctrl->vfebase + VFE_IRQ_MASK_1);
+
+	/* clear all pending interrupts*/
+	msm_camera_io_w(VFE_CLEAR_ALL_IRQS,
+		vfe31_ctrl->vfebase + VFE_IRQ_CLEAR_0);
+	msm_camera_io_w(VFE_CLEAR_ALL_IRQS,
+		vfe31_ctrl->vfebase + VFE_IRQ_CLEAR_1);
+	/* Ensure the write order while writing
+	to the command register using the barrier */
+	msm_camera_io_w_mb(1,
+		vfe31_ctrl->vfebase + VFE_IRQ_CMD);
+
+	/* now enable only halt_irq & reset_irq */
+	msm_camera_io_w(0xf0000000,          /* this is for async timer. */
+		vfe31_ctrl->vfebase + VFE_IRQ_MASK_0);
+	msm_camera_io_w(VFE_IMASK_RESET,
+		vfe31_ctrl->vfebase + VFE_IRQ_MASK_1);
+
+	/* Ensure the write order while writing
+	to the command register using the barrier */
+	CDBG("%s: about to reset vfe...\n", __func__);
+	msm_camera_io_w_mb(VFE_RESET_UPON_STOP_CMD,
+		vfe31_ctrl->vfebase + VFE_GLOBAL_RESET);
+
+}
+
+static void vfe31_process_camif_sof_irq(void)
+{
+	uint32_t  temp;
+
+	/* in raw snapshot mode */
+	if (vfe31_ctrl->operation_mode ==
+		VFE_MODE_OF_OPERATION_RAW_SNAPSHOT) {
+		if (vfe31_ctrl->start_ack_pending) {
+			vfe31_send_msg_no_payload(MSG_ID_START_ACK);
+			vfe31_ctrl->start_ack_pending = FALSE;
+		}
+		if (vfe31_ctrl->vfe_capture_count)
+			vfe31_ctrl->vfe_capture_count--;
+		/* if last frame to be captured: */
+		if (vfe31_ctrl->vfe_capture_count == 0) {
+			/* Ensure the write order while writing
+			to the command register using the barrier */
+			msm_camera_io_w_mb(CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY,
+				vfe31_ctrl->vfebase + VFE_CAMIF_COMMAND);
+			temp = msm_camera_io_r_mb(vfe31_ctrl->vfebase +
+				VFE_CAMIF_COMMAND);
+		}
+	} /* if raw snapshot mode. */
+
+	if ((vfe31_ctrl->hfr_mode != HFR_MODE_OFF) &&
+		(vfe31_ctrl->operation_mode == VFE_MODE_OF_OPERATION_VIDEO) &&
+		(vfe31_ctrl->vfeFrameId % vfe31_ctrl->hfr_mode != 0)) {
+		vfe31_ctrl->vfeFrameId++;
+		CDBG("Skip the SOF notification when HFR enabled\n");
+		return;
+	}
+	vfe31_send_msg_no_payload(MSG_ID_SOF_ACK);
+	vfe31_ctrl->vfeFrameId++;
+	CDBG("camif_sof_irq, frameId = %d\n", vfe31_ctrl->vfeFrameId);
+
+	if (vfe31_ctrl->sync_timer_state) {
+		if (vfe31_ctrl->sync_timer_repeat_count == 0)
+			vfe31_sync_timer_stop();
+		else
+		vfe31_ctrl->sync_timer_repeat_count--;
+	}
+}
+
+static void vfe31_process_error_irq(uint32_t errStatus)
+{
+	uint32_t camifStatus, read_val;
+	uint32_t *temp;
+
+	if (errStatus & VFE31_IMASK_CAMIF_ERROR) {
+		pr_err("vfe31_irq: camif errors\n");
+		temp = (uint32_t *)(vfe31_ctrl->vfebase + VFE_CAMIF_STATUS);
+		camifStatus = msm_camera_io_r(temp);
+		pr_err("camifStatus  = 0x%x\n", camifStatus);
+		vfe31_send_msg_no_payload(MSG_ID_CAMIF_ERROR);
+	}
+
+	if (errStatus & VFE31_IMASK_STATS_CS_OVWR)
+		pr_err("vfe31_irq: stats cs overwrite\n");
+
+	if (errStatus & VFE31_IMASK_STATS_IHIST_OVWR)
+		pr_err("vfe31_irq: stats ihist overwrite\n");
+
+	if (errStatus & VFE31_IMASK_REALIGN_BUF_Y_OVFL)
+		pr_err("vfe31_irq: realign bug Y overflow\n");
+
+	if (errStatus & VFE31_IMASK_REALIGN_BUF_CB_OVFL)
+		pr_err("vfe31_irq: realign bug CB overflow\n");
+
+	if (errStatus & VFE31_IMASK_REALIGN_BUF_CR_OVFL)
+		pr_err("vfe31_irq: realign bug CR overflow\n");
+
+	if (errStatus & VFE31_IMASK_VIOLATION)
+		pr_err("vfe31_irq: violation interrupt\n");
+
+	if (errStatus & VFE31_IMASK_IMG_MAST_0_BUS_OVFL)
+		pr_err("vfe31_irq: image master 0 bus overflow\n");
+
+	if (errStatus & VFE31_IMASK_IMG_MAST_1_BUS_OVFL)
+		pr_err("vfe31_irq: image master 1 bus overflow\n");
+
+	if (errStatus & VFE31_IMASK_IMG_MAST_2_BUS_OVFL)
+		pr_err("vfe31_irq: image master 2 bus overflow\n");
+
+	if (errStatus & VFE31_IMASK_IMG_MAST_3_BUS_OVFL)
+		pr_err("vfe31_irq: image master 3 bus overflow\n");
+
+	if (errStatus & VFE31_IMASK_IMG_MAST_4_BUS_OVFL)
+		pr_err("vfe31_irq: image master 4 bus overflow\n");
+
+	if (errStatus & VFE31_IMASK_IMG_MAST_5_BUS_OVFL)
+		pr_err("vfe31_irq: image master 5 bus overflow\n");
+
+	if (errStatus & VFE31_IMASK_IMG_MAST_6_BUS_OVFL)
+		pr_err("vfe31_irq: image master 6 bus overflow\n");
+
+	if (errStatus & VFE31_IMASK_STATS_AE_BUS_OVFL)
+		pr_err("vfe31_irq: ae stats bus overflow\n");
+
+	if (errStatus & VFE31_IMASK_STATS_AF_BUS_OVFL)
+		pr_err("vfe31_irq: af stats bus overflow\n");
+
+	if (errStatus & VFE31_IMASK_STATS_AWB_BUS_OVFL)
+		pr_err("vfe31_irq: awb stats bus overflow\n");
+
+	if (errStatus & VFE31_IMASK_STATS_RS_BUS_OVFL)
+		pr_err("vfe31_irq: rs stats bus overflow\n");
+
+	if (errStatus & VFE31_IMASK_STATS_CS_BUS_OVFL)
+		pr_err("vfe31_irq: cs stats bus overflow\n");
+
+	if (errStatus & VFE31_IMASK_STATS_IHIST_BUS_OVFL)
+		pr_err("vfe31_irq: ihist stats bus overflow\n");
+
+	if (errStatus & VFE31_IMASK_STATS_SKIN_BUS_OVFL)
+		pr_err("vfe31_irq: skin stats bus overflow\n");
+
+	if (errStatus & VFE31_IMASK_AXI_ERROR) {
+		pr_err("vfe31_irq: axi error\n");
+		/* read status too when overflow happens.*/
+		read_val = msm_camera_io_r(vfe31_ctrl->vfebase +
+			VFE_BUS_PING_PONG_STATUS);
+		pr_debug("VFE_BUS_PING_PONG_STATUS = 0x%x\n", read_val);
+		read_val = msm_camera_io_r(vfe31_ctrl->vfebase +
+			VFE_BUS_OPERATION_STATUS);
+		pr_debug("VFE_BUS_OPERATION_STATUS = 0x%x\n", read_val);
+		read_val = msm_camera_io_r(vfe31_ctrl->vfebase +
+			VFE_BUS_IMAGE_MASTER_0_WR_PM_STATS_0);
+		pr_debug("VFE_BUS_IMAGE_MASTER_0_WR_PM_STATS_0 = 0x%x\n",
+			read_val);
+		read_val = msm_camera_io_r(vfe31_ctrl->vfebase +
+			VFE_BUS_IMAGE_MASTER_0_WR_PM_STATS_1);
+		pr_debug("VFE_BUS_IMAGE_MASTER_0_WR_PM_STATS_1 = 0x%x\n",
+			read_val);
+		read_val = msm_camera_io_r(vfe31_ctrl->vfebase +
+			VFE_AXI_STATUS);
+		pr_debug("VFE_AXI_STATUS = 0x%x\n", read_val);
+	}
+}
+
+#define VFE31_AXI_OFFSET 0x0050
+#define vfe31_get_ch_ping_addr(chn) \
+	(msm_camera_io_r(vfe31_ctrl->vfebase + 0x0050 + 0x18 * (chn)))
+#define vfe31_get_ch_pong_addr(chn) \
+	(msm_camera_io_r(vfe31_ctrl->vfebase + 0x0050 + 0x18 * (chn) + 4))
+#define vfe31_get_ch_addr(ping_pong, chn) \
+	(((ping_pong) & (1 << (chn))) == 0 ? \
+	vfe31_get_ch_pong_addr(chn) : vfe31_get_ch_ping_addr(chn))
+
+#define vfe31_put_ch_ping_addr(chn, addr) \
+	(msm_camera_io_w((addr), vfe31_ctrl->vfebase + 0x0050 + 0x18 * (chn)))
+#define vfe31_put_ch_pong_addr(chn, addr) \
+	(msm_camera_io_w((addr), \
+	vfe31_ctrl->vfebase + 0x0050 + 0x18 * (chn) + 4))
+#define vfe31_put_ch_addr(ping_pong, chn, addr) \
+	(((ping_pong) & (1 << (chn))) == 0 ?   \
+	vfe31_put_ch_pong_addr((chn), (addr)) : \
+	vfe31_put_ch_ping_addr((chn), (addr)))
+
+static void vfe31_process_output_path_irq_0(uint32_t ping_pong)
+{
+	uint32_t p0_addr, p1_addr, p2_addr;
+#ifdef CONFIG_MSM_CAMERA_V4L2
+	uint32_t pyaddr_ping, pcbcraddr_ping, pyaddr_pong, pcbcraddr_pong;
+#endif
+	struct vfe31_free_buf *free_buf = NULL;
+	/* we render frames in the following conditions:
+	1. Continuous mode and the free buffer is avaialable.
+	*/
+	if (vfe31_ctrl->outpath.output_mode &
+		VFE31_OUTPUT_MODE_P_ALL_CHNLS) {
+		if (!(((ping_pong & PINGPONG_LOWER) == PINGPONG_LOWER) ||
+			((ping_pong & PINGPONG_LOWER) == 0x0))) {
+			pr_err(" Irq_2 - skip the frame pp_status is not proper"
+				"PP_status = 0x%x\n", ping_pong);
+			return;
+		}
+	}
+	free_buf = vfe31_get_free_buf(&vfe31_ctrl->outpath.out0);
+
+	if (free_buf) {
+		/* Y channel */
+		p0_addr = vfe31_get_ch_addr(ping_pong,
+			vfe31_ctrl->outpath.out0.ch0);
+		/* Chroma channel */
+		p1_addr = vfe31_get_ch_addr(ping_pong,
+			vfe31_ctrl->outpath.out0.ch1);
+		if (vfe31_ctrl->outpath.output_mode &
+			VFE31_OUTPUT_MODE_P_ALL_CHNLS) {
+			p2_addr = vfe31_get_ch_addr(ping_pong,
+				vfe31_ctrl->outpath.out0.ch2);
+		} else {
+			p2_addr = p0_addr;
+		}
+		CDBG("Output path 0, p0_addr = 0x%x, p1_addr = 0x%x,"
+			 "p2_addr = 0x%x\n", p0_addr, p1_addr, p2_addr);
+		/* Y channel */
+		vfe31_put_ch_addr(ping_pong,
+			vfe31_ctrl->outpath.out0.ch0,
+			free_buf->paddr + free_buf->planar0_off);
+		/* Chroma channel */
+		vfe31_put_ch_addr(ping_pong,
+			vfe31_ctrl->outpath.out0.ch1,
+			free_buf->paddr + free_buf->planar1_off);
+		if (vfe31_ctrl->outpath.output_mode &
+			VFE31_OUTPUT_MODE_P_ALL_CHNLS)
+			vfe31_put_ch_addr(ping_pong,
+				vfe31_ctrl->outpath.out0.ch2,
+			free_buf->paddr + free_buf->planar2_off);
+			kfree(free_buf);
+			/* if continuous mode, for display. (preview) */
+			vfe_send_outmsg(MSG_ID_OUTPUT_P,  p0_addr, p1_addr,
+				p2_addr);
+	} else {
+		vfe31_ctrl->outpath.out0.frame_drop_cnt++;
+		pr_warning("path_irq_0 - no free buffer!\n");
+#ifdef CONFIG_MSM_CAMERA_V4L2
+		pr_info("Swapping ping and pong\n");
+
+		/*get addresses*/
+		/* Y channel */
+		pyaddr_ping = vfe31_get_ch_ping_addr(
+			vfe31_ctrl->outpath.out0.ch0);
+		/* Chroma channel */
+		pcbcraddr_ping = vfe31_get_ch_ping_addr(
+			vfe31_ctrl->outpath.out0.ch1);
+		/* Y channel */
+		pyaddr_pong = vfe31_get_ch_pong_addr(
+			vfe31_ctrl->outpath.out0.ch0);
+		/* Chroma channel */
+		pcbcraddr_pong = vfe31_get_ch_pong_addr(
+			vfe31_ctrl->outpath.out0.ch1);
+
+		CDBG("ping = 0x%p, pong = 0x%p\n", (void *)pyaddr_ping,
+			(void *)pyaddr_pong);
+		CDBG("ping_cbcr = 0x%p, pong_cbcr = 0x%p\n",
+			(void *)pcbcraddr_ping, (void *)pcbcraddr_pong);
+
+		/*put addresses*/
+		/* SWAP y channel*/
+		vfe31_put_ch_ping_addr(vfe31_ctrl->outpath.out0.ch0,
+			pyaddr_pong);
+		vfe31_put_ch_pong_addr(vfe31_ctrl->outpath.out0.ch0,
+			pyaddr_ping);
+		/* SWAP chroma channel*/
+		vfe31_put_ch_ping_addr(vfe31_ctrl->outpath.out0.ch1,
+			pcbcraddr_pong);
+		vfe31_put_ch_pong_addr(vfe31_ctrl->outpath.out0.ch1,
+			pcbcraddr_ping);
+		CDBG("after swap: ping = 0x%p, pong = 0x%p\n",
+			(void *)pyaddr_pong, (void *)pyaddr_ping);
+#endif
+	}
+}
+
+static void vfe31_process_snapshot_frame(uint32_t ping_pong)
+{
+	uint32_t p0_addr, p1_addr;
+	struct vfe31_free_buf *free_buf = NULL;
+	/* Y channel- Main Image */
+	p0_addr = vfe31_get_ch_addr(ping_pong,
+		vfe31_ctrl->outpath.out1.ch0);
+	/* Chroma channel - TN Image */
+	p1_addr = vfe31_get_ch_addr(ping_pong,
+		vfe31_ctrl->outpath.out1.ch1);
+
+	free_buf = vfe31_get_free_buf(&vfe31_ctrl->outpath.out1);
+	CDBG("%s: snapshot main, p0_addr = 0x%x, p1_addr = 0x%x\n",
+		__func__, p0_addr, p1_addr);
+	if (free_buf) {
+		/* Y channel */
+		vfe31_put_ch_addr(ping_pong,
+			vfe31_ctrl->outpath.out1.ch0,
+			free_buf->paddr + free_buf->planar0_off);
+		/* Chroma channel */
+		vfe31_put_ch_addr(ping_pong,
+			vfe31_ctrl->outpath.out1.ch1,
+			free_buf->paddr + free_buf->planar1_off);
+		kfree(free_buf);
+	}
+	vfe_send_outmsg(MSG_ID_OUTPUT_S, p0_addr, p1_addr, p0_addr);
+
+	/* Y channel- TN Image */
+	p0_addr = vfe31_get_ch_addr(ping_pong,
+		vfe31_ctrl->outpath.out0.ch0);
+	/* Chroma channel - TN Image */
+	p1_addr = vfe31_get_ch_addr(ping_pong,
+		vfe31_ctrl->outpath.out0.ch1);
+
+	free_buf = vfe31_get_free_buf(&vfe31_ctrl->outpath.out0);
+	CDBG("%s: snapshot TN, p0_addr = 0x%x, p1_addr = 0x%x\n",
+		__func__, p0_addr, p1_addr);
+	if (free_buf) {
+		/* Y channel */
+		vfe31_put_ch_addr(ping_pong,
+			vfe31_ctrl->outpath.out0.ch0,
+			free_buf->paddr + free_buf->planar0_off);
+		/* Chroma channel */
+		vfe31_put_ch_addr(ping_pong,
+			vfe31_ctrl->outpath.out0.ch1,
+			free_buf->paddr + free_buf->planar1_off);
+		kfree(free_buf);
+	}
+
+	vfe_send_outmsg(MSG_ID_OUTPUT_T, p0_addr, p1_addr, p0_addr);
+
+	/* in snapshot mode if done then send
+		snapshot done message */
+	if (vfe31_ctrl->vfe_capture_count == 0) {
+		vfe31_send_msg_no_payload(MSG_ID_SNAPSHOT_DONE);
+		/* Ensure the write order while writing
+			to the cmd register using barrier */
+		msm_camera_io_w_mb(CAMIF_COMMAND_STOP_IMMEDIATELY,
+			vfe31_ctrl->vfebase +
+			VFE_CAMIF_COMMAND);
+	}
+}
+
+static void vfe31_process_raw_snapshot_frame(uint32_t ping_pong)
+{
+	uint32_t pyaddr, pcbcraddr;
+	struct vfe31_free_buf *free_buf = NULL;
+	struct msm_sync* p_sync = (struct msm_sync *)vfe_syncdata;
+
+	if (p_sync->stereocam_enabled)
+		p_sync->stereo_state = STEREO_RAW_SNAP_STARTED;
+
+	/* Y channel- Main Image */
+	pyaddr = vfe31_get_ch_addr(ping_pong,
+		vfe31_ctrl->outpath.out1.ch0);
+	/* Chroma channel - Main Image */
+	pcbcraddr = vfe31_get_ch_addr(ping_pong,
+		vfe31_ctrl->outpath.out1.ch1);
+
+	free_buf = vfe31_get_free_buf(&vfe31_ctrl->outpath.out1);
+	CDBG("%s: snapshot raw, pyaddr = 0x%x, pcbcraddr = 0x%x\n",
+		__func__, pyaddr, pcbcraddr);
+	if (free_buf) {
+		/* Y channel */
+		vfe31_put_ch_addr(ping_pong,
+			vfe31_ctrl->outpath.out1.ch0,
+			free_buf->paddr + free_buf->planar0_off);
+		/* Chroma channel */
+		vfe31_put_ch_addr(ping_pong,
+			vfe31_ctrl->outpath.out1.ch1,
+			free_buf->paddr + free_buf->planar1_off);
+		kfree(free_buf);
+	}
+	 vfe_send_outmsg(MSG_ID_OUTPUT_S, pyaddr, pcbcraddr, 0);
+
+	/* in snapshot mode if done then send
+		snapshot done message */
+	if (vfe31_ctrl->vfe_capture_count == 0) {
+		vfe31_send_msg_no_payload(MSG_ID_SNAPSHOT_DONE);
+		/* Ensure the write order while writing
+		to the cmd register using barrier */
+		msm_camera_io_w_mb(CAMIF_COMMAND_STOP_IMMEDIATELY,
+			vfe31_ctrl->vfebase +
+			VFE_CAMIF_COMMAND);
+	}
+}
+static void vfe31_process_zsl_frame(uint32_t ping_pong)
+{
+	uint32_t p0_addr, p1_addr;
+	struct vfe31_free_buf *free_buf = NULL;
+	/* Y channel- Main Image */
+	p0_addr = vfe31_get_ch_addr(ping_pong,
+		vfe31_ctrl->outpath.out2.ch0);
+	/* Chroma channel - Main Image */
+	p1_addr = vfe31_get_ch_addr(ping_pong,
+		vfe31_ctrl->outpath.out2.ch1);
+
+	free_buf = vfe31_get_free_buf(&vfe31_ctrl->outpath.out2);
+	CDBG("%s: snapshot main, pyaddr = 0x%x, pcbcraddr = 0x%x\n",
+		__func__, p0_addr, p1_addr);
+	if (free_buf) {
+		/* Y channel */
+		vfe31_put_ch_addr(ping_pong,
+			vfe31_ctrl->outpath.out2.ch0,
+			free_buf->paddr + free_buf->planar0_off);
+		/* Chroma channel */
+		vfe31_put_ch_addr(ping_pong,
+			vfe31_ctrl->outpath.out2.ch1,
+			free_buf->paddr + free_buf->planar1_off);
+		kfree(free_buf);
+	}
+	 vfe_send_outmsg(MSG_ID_OUTPUT_S, p0_addr, p1_addr, p0_addr);
+
+	/* Y channel- TN Image */
+	p0_addr = vfe31_get_ch_addr(ping_pong,
+		vfe31_ctrl->outpath.out1.ch0);
+	/* Chroma channel - TN Image */
+	p1_addr = vfe31_get_ch_addr(ping_pong,
+		vfe31_ctrl->outpath.out1.ch1);
+
+	free_buf = vfe31_get_free_buf(&vfe31_ctrl->outpath.out1);
+	CDBG("%s: snapshot TN, pyaddr = 0x%x, pcbcraddr = 0x%x\n",
+		__func__, p0_addr, p1_addr);
+	if (free_buf) {
+		/* Y channel */
+		vfe31_put_ch_addr(ping_pong,
+			vfe31_ctrl->outpath.out1.ch0,
+			free_buf->paddr + free_buf->planar0_off);
+		/* Chroma channel */
+		vfe31_put_ch_addr(ping_pong,
+			vfe31_ctrl->outpath.out1.ch1,
+			free_buf->paddr + free_buf->planar1_off);
+		kfree(free_buf);
+	}
+
+	vfe_send_outmsg(MSG_ID_OUTPUT_T, p0_addr, p1_addr, p0_addr);
+}
+
+static void vfe31_process_output_path_irq_1(uint32_t ping_pong)
+{
+
+#ifdef CONFIG_MSM_CAMERA_V4L2
+	uint32_t pyaddr_ping, pcbcraddr_ping, pyaddr_pong, pcbcraddr_pong;
+#endif
+	CDBG("%s, operation_mode = %d, cap_cnt = %d\n", __func__,
+		vfe31_ctrl->operation_mode, vfe31_ctrl->vfe_capture_count);
+
+	/* In Snapshot mode */
+	if ((VFE_MODE_OF_OPERATION_SNAPSHOT == vfe31_ctrl->operation_mode)
+		&& ((vfe31_ctrl->vfe_capture_count <= 1)
+		|| (vfe31_free_buf_available(vfe31_ctrl->outpath.out0) &&
+		vfe31_free_buf_available(vfe31_ctrl->outpath.out1)))) {
+		vfe31_process_snapshot_frame(ping_pong);
+	} else if ((VFE_MODE_OF_OPERATION_RAW_SNAPSHOT ==
+		vfe31_ctrl->operation_mode) &&
+		((vfe31_ctrl->vfe_capture_count <= 1) ||
+		vfe31_free_buf_available(vfe31_ctrl->outpath.out1))) {
+		vfe31_process_raw_snapshot_frame(ping_pong);
+	} else if ((VFE_MODE_OF_OPERATION_ZSL == vfe31_ctrl->operation_mode)
+		&& (vfe31_free_buf_available(vfe31_ctrl->outpath.out1)
+		&& vfe31_free_buf_available(vfe31_ctrl->outpath.out2))) {
+		vfe31_process_zsl_frame(ping_pong);
+	} else {
+		vfe31_ctrl->outpath.out1.frame_drop_cnt++;
+		pr_info("path_irq_1 - no free buffer!\n");
+#ifdef CONFIG_MSM_CAMERA_V4L2
+		pr_info("Swapping ping and pong\n");
+
+		/*get addresses*/
+		/* Y channel */
+		pyaddr_ping = vfe31_get_ch_ping_addr(
+			vfe31_ctrl->outpath.out1.ch0);
+		/* Chroma channel */
+		pcbcraddr_ping = vfe31_get_ch_ping_addr(
+			vfe31_ctrl->outpath.out1.ch1);
+		/* Y channel */
+		pyaddr_pong = vfe31_get_ch_pong_addr(
+			vfe31_ctrl->outpath.out1.ch0);
+		/* Chroma channel */
+		pcbcraddr_pong = vfe31_get_ch_pong_addr(
+			vfe31_ctrl->outpath.out1.ch1);
+
+		CDBG("ping = 0x%p, pong = 0x%p\n", (void *)pyaddr_ping,
+			(void *)pyaddr_pong);
+		CDBG("ping_cbcr = 0x%p, pong_cbcr = 0x%p\n",
+			(void *)pcbcraddr_ping, (void *)pcbcraddr_pong);
+
+		/*put addresses*/
+		/* SWAP y channel*/
+		vfe31_put_ch_ping_addr(vfe31_ctrl->outpath.out1.ch0,
+			pyaddr_pong);
+		vfe31_put_ch_pong_addr(vfe31_ctrl->outpath.out1.ch0,
+			pyaddr_ping);
+		/* SWAP chroma channel*/
+		vfe31_put_ch_ping_addr(vfe31_ctrl->outpath.out1.ch1,
+			pcbcraddr_pong);
+		vfe31_put_ch_pong_addr(vfe31_ctrl->outpath.out1.ch1,
+			pcbcraddr_ping);
+		CDBG("after swap: ping = 0x%p, pong = 0x%p\n",
+			(void *)pyaddr_pong, (void *)pyaddr_ping);
+#endif
+	}
+
+}
+
+static void vfe31_process_output_path_irq_2(uint32_t ping_pong)
+{
+	uint32_t p0_addr, p1_addr, p2_addr;
+	struct vfe31_free_buf *free_buf = NULL;
+
+#ifdef CONFIG_MSM_CAMERA_V4L2
+	uint32_t pyaddr_ping, pcbcraddr_ping, pyaddr_pong, pcbcraddr_pong;
+#endif
+	/* we render frames in the following conditions:
+	1. Continuous mode and the free buffer is avaialable.
+	*/
+	CDBG("%s, operation_mode = %d, state %d\n", __func__,
+		vfe31_ctrl->operation_mode,
+		vfe31_ctrl->recording_state);
+	/* Ensure that both wm1 and wm5 ping and pong buffers are active*/
+	if (!(((ping_pong & 0x22) == 0x22) ||
+		((ping_pong & 0x22) == 0x0))) {
+		pr_err(" Irq_2 - skip the frame pp_status is not proper"
+			"PP_status = 0x%x\n", ping_pong);
+		return;
+	}
+	if ((vfe31_ctrl->recording_state == VFE_REC_STATE_STOP_REQUESTED)
+		|| (vfe31_ctrl->recording_state == VFE_REC_STATE_STOPPED)) {
+		vfe31_ctrl->outpath.out2.frame_drop_cnt++;
+		pr_warning("path_irq_2 - recording stopped\n");
+		return;
+	}
+
+	free_buf = vfe31_get_free_buf(&vfe31_ctrl->outpath.out2);
+
+	if (free_buf) {
+		/* Y channel */
+		p0_addr = vfe31_get_ch_addr(ping_pong,
+			vfe31_ctrl->outpath.out2.ch0);
+		/* Chroma channel */
+		p1_addr = vfe31_get_ch_addr(ping_pong,
+			vfe31_ctrl->outpath.out2.ch1);
+		p2_addr = p0_addr;
+		CDBG("video output, pyaddr = 0x%x, pcbcraddr = 0x%x\n",
+			p0_addr, p1_addr);
+
+		/* Y channel */
+		vfe31_put_ch_addr(ping_pong,
+		vfe31_ctrl->outpath.out2.ch0,
+		free_buf->paddr + free_buf->planar0_off);
+		/* Chroma channel */
+		vfe31_put_ch_addr(ping_pong,
+		vfe31_ctrl->outpath.out2.ch1,
+		free_buf->paddr + free_buf->planar1_off);
+		kfree(free_buf);
+		vfe_send_outmsg(MSG_ID_OUTPUT_V, p0_addr, p1_addr, p2_addr);
+	} else {
+		vfe31_ctrl->outpath.out2.frame_drop_cnt++;
+		pr_warning("path_irq_2 - no free buffer!\n");
+
+#ifdef CONFIG_MSM_CAMERA_V4L2
+		pr_info("Swapping ping and pong\n");
+
+		/*get addresses*/
+		/* Y channel */
+		pyaddr_ping = vfe31_get_ch_ping_addr(
+			vfe31_ctrl->outpath.out2.ch0);
+		/* Chroma channel */
+		pcbcraddr_ping = vfe31_get_ch_ping_addr(
+			vfe31_ctrl->outpath.out2.ch1);
+		/* Y channel */
+		pyaddr_pong = vfe31_get_ch_pong_addr(
+			vfe31_ctrl->outpath.out2.ch0);
+		/* Chroma channel */
+		pcbcraddr_pong = vfe31_get_ch_pong_addr(
+			vfe31_ctrl->outpath.out2.ch1);
+
+		CDBG("ping = 0x%p, pong = 0x%p\n", (void *)pyaddr_ping,
+			(void *)pyaddr_pong);
+		CDBG("ping_cbcr = 0x%p, pong_cbcr = 0x%p\n",
+			(void *)pcbcraddr_ping, (void *)pcbcraddr_pong);
+
+		/*put addresses*/
+		/* SWAP y channel*/
+		vfe31_put_ch_ping_addr(vfe31_ctrl->outpath.out2.ch0,
+			pyaddr_pong);
+		vfe31_put_ch_pong_addr(vfe31_ctrl->outpath.out2.ch0,
+			pyaddr_ping);
+		/* SWAP chroma channel*/
+		vfe31_put_ch_ping_addr(vfe31_ctrl->outpath.out2.ch1,
+			pcbcraddr_pong);
+		vfe31_put_ch_pong_addr(vfe31_ctrl->outpath.out2.ch1,
+			pcbcraddr_ping);
+		CDBG("after swap: ping = 0x%p, pong = 0x%p\n",
+			(void *)pyaddr_pong, (void *)pyaddr_ping);
+#endif
+	}
+}
+
+
+static uint32_t  vfe31_process_stats_irq_common(uint32_t statsNum,
+						uint32_t newAddr) {
+
+	uint32_t pingpongStatus;
+	uint32_t returnAddr;
+	uint32_t pingpongAddr;
+
+	/* must be 0=ping, 1=pong */
+	pingpongStatus =
+		((msm_camera_io_r(vfe31_ctrl->vfebase +
+		VFE_BUS_PING_PONG_STATUS))
+	& ((uint32_t)(1<<(statsNum + 7)))) >> (statsNum + 7);
+	/* stats bits starts at 7 */
+	CDBG("statsNum %d, pingpongStatus %d\n", statsNum, pingpongStatus);
+	pingpongAddr =
+		((uint32_t)(vfe31_ctrl->vfebase +
+				VFE_BUS_STATS_PING_PONG_BASE)) +
+				(3*statsNum)*4 + (1-pingpongStatus)*4;
+	returnAddr = msm_camera_io_r((uint32_t *)pingpongAddr);
+	msm_camera_io_w(newAddr, (uint32_t *)pingpongAddr);
+	return returnAddr;
+}
+
+static void vfe_send_stats_msg(void)
+{
+	struct  vfe_message msg;
+	uint32_t temp;
+
+	/* fill message with right content. */
+	msg._u.msgStats.frameCounter = vfe31_ctrl->vfeFrameId;
+	msg._u.msgStats.status_bits = vfe31_ctrl->status_bits;
+	msg._d = MSG_ID_COMMON;
+
+	msg._u.msgStats.buff.aec = vfe31_ctrl->aecStatsControl.bufToRender;
+	msg._u.msgStats.buff.awb = vfe31_ctrl->awbStatsControl.bufToRender;
+	msg._u.msgStats.buff.af = vfe31_ctrl->afStatsControl.bufToRender;
+
+	msg._u.msgStats.buff.ihist = vfe31_ctrl->ihistStatsControl.bufToRender;
+	msg._u.msgStats.buff.rs = vfe31_ctrl->rsStatsControl.bufToRender;
+	msg._u.msgStats.buff.cs = vfe31_ctrl->csStatsControl.bufToRender;
+
+	temp = msm_camera_io_r(vfe31_ctrl->vfebase + VFE_STATS_AWB_SGW_CFG);
+	msg._u.msgStats.buff.awb_ymin = (0xFF00 & temp) >> 8;
+
+	vfe31_proc_ops(msg._d,
+		&msg, sizeof(struct vfe_message));
+	return;
+}
+
+static void vfe31_process_stats(void)
+{
+	int32_t process_stats = false;
+
+	CDBG("%s, stats = 0x%x\n", __func__, vfe31_ctrl->status_bits);
+
+	if (vfe31_ctrl->status_bits & VFE_IRQ_STATUS0_STATS_AEC) {
+		if (!vfe31_ctrl->aec_ack_pending) {
+			vfe31_ctrl->aec_ack_pending = TRUE;
+			vfe31_ctrl->aecStatsControl.bufToRender =
+				vfe31_process_stats_irq_common(statsAeNum,
+				vfe31_ctrl->aecStatsControl.nextFrameAddrBuf);
+			process_stats = true;
+		} else{
+			vfe31_ctrl->aecStatsControl.bufToRender = 0;
+			vfe31_ctrl->aecStatsControl.droppedStatsFrameCount++;
+		}
+	} else {
+		vfe31_ctrl->aecStatsControl.bufToRender = 0;
+	}
+
+	if (vfe31_ctrl->status_bits & VFE_IRQ_STATUS0_STATS_AWB) {
+		if (!vfe31_ctrl->awb_ack_pending) {
+			vfe31_ctrl->awb_ack_pending = TRUE;
+			vfe31_ctrl->awbStatsControl.bufToRender =
+				vfe31_process_stats_irq_common(statsAwbNum,
+				vfe31_ctrl->awbStatsControl.nextFrameAddrBuf);
+			process_stats = true;
+		} else{
+			vfe31_ctrl->awbStatsControl.droppedStatsFrameCount++;
+			vfe31_ctrl->awbStatsControl.bufToRender = 0;
+		}
+	} else {
+		vfe31_ctrl->awbStatsControl.bufToRender = 0;
+	}
+
+
+	if (vfe31_ctrl->status_bits & VFE_IRQ_STATUS0_STATS_AF) {
+		if (!vfe31_ctrl->af_ack_pending) {
+			vfe31_ctrl->af_ack_pending = TRUE;
+			vfe31_ctrl->afStatsControl.bufToRender =
+				vfe31_process_stats_irq_common(statsAfNum,
+				vfe31_ctrl->afStatsControl.nextFrameAddrBuf);
+			process_stats = true;
+		} else {
+			vfe31_ctrl->afStatsControl.bufToRender = 0;
+			vfe31_ctrl->afStatsControl.droppedStatsFrameCount++;
+		}
+	} else {
+		vfe31_ctrl->afStatsControl.bufToRender = 0;
+	}
+
+	if (vfe31_ctrl->status_bits & VFE_IRQ_STATUS0_STATS_IHIST) {
+		if (!vfe31_ctrl->ihist_ack_pending) {
+			vfe31_ctrl->ihist_ack_pending = TRUE;
+			vfe31_ctrl->ihistStatsControl.bufToRender =
+				vfe31_process_stats_irq_common(statsIhistNum,
+				vfe31_ctrl->ihistStatsControl.nextFrameAddrBuf);
+			process_stats = true;
+		} else {
+			vfe31_ctrl->ihistStatsControl.droppedStatsFrameCount++;
+			vfe31_ctrl->ihistStatsControl.bufToRender = 0;
+		}
+	} else {
+		vfe31_ctrl->ihistStatsControl.bufToRender = 0;
+	}
+
+	if (vfe31_ctrl->status_bits & VFE_IRQ_STATUS0_STATS_RS) {
+		if (!vfe31_ctrl->rs_ack_pending) {
+			vfe31_ctrl->rs_ack_pending = TRUE;
+			vfe31_ctrl->rsStatsControl.bufToRender =
+				vfe31_process_stats_irq_common(statsRsNum,
+				vfe31_ctrl->rsStatsControl.nextFrameAddrBuf);
+			process_stats = true;
+		} else {
+			vfe31_ctrl->rsStatsControl.droppedStatsFrameCount++;
+			vfe31_ctrl->rsStatsControl.bufToRender = 0;
+		}
+	} else {
+		vfe31_ctrl->rsStatsControl.bufToRender = 0;
+	}
+
+
+	if (vfe31_ctrl->status_bits & VFE_IRQ_STATUS0_STATS_CS) {
+		if (!vfe31_ctrl->cs_ack_pending) {
+			vfe31_ctrl->cs_ack_pending = TRUE;
+			vfe31_ctrl->csStatsControl.bufToRender =
+				vfe31_process_stats_irq_common(statsCsNum,
+				vfe31_ctrl->csStatsControl.nextFrameAddrBuf);
+			process_stats = true;
+		} else {
+			vfe31_ctrl->csStatsControl.droppedStatsFrameCount++;
+			vfe31_ctrl->csStatsControl.bufToRender = 0;
+		}
+	} else {
+		vfe31_ctrl->csStatsControl.bufToRender = 0;
+	}
+
+	if (process_stats)
+		vfe_send_stats_msg();
+
+	return;
+}
+
+static void vfe31_process_stats_irq(uint32_t *irqstatus)
+{
+	/* Subsample the stats according to the hfr speed*/
+	if ((vfe31_ctrl->hfr_mode != HFR_MODE_OFF) &&
+		(vfe31_ctrl->vfeFrameId % vfe31_ctrl->hfr_mode != 0)) {
+		CDBG("Skip the stats when HFR enabled\n");
+		return;
+	}
+
+	vfe31_ctrl->status_bits = VFE_COM_STATUS & *irqstatus;
+	vfe31_process_stats();
+	return;
+}
+
+static void vfe31_do_tasklet(unsigned long data)
+{
+	unsigned long flags;
+
+	struct vfe31_isr_queue_cmd *qcmd = NULL;
+
+	CDBG("=== vfe31_do_tasklet start === \n");
+
+	while (atomic_read(&irq_cnt)) {
+		spin_lock_irqsave(&vfe31_ctrl->tasklet_lock, flags);
+		qcmd = list_first_entry(&vfe31_ctrl->tasklet_q,
+			struct vfe31_isr_queue_cmd, list);
+		atomic_sub(1, &irq_cnt);
+
+		if (!qcmd) {
+			spin_unlock_irqrestore(&vfe31_ctrl->tasklet_lock,
+				flags);
+			return;
+		}
+
+		list_del(&qcmd->list);
+		spin_unlock_irqrestore(&vfe31_ctrl->tasklet_lock,
+			flags);
+
+		/* interrupt to be processed,  *qcmd has the payload.  */
+		if (qcmd->vfeInterruptStatus0 &
+			VFE_IRQ_STATUS0_REG_UPDATE_MASK) {
+			CDBG("irq regUpdateIrq\n");
+			vfe31_process_reg_update_irq();
+		}
+
+		if (qcmd->vfeInterruptStatus1 &
+			VFE_IMASK_RESET) {
+			CDBG("irq resetAckIrq\n");
+			vfe31_process_reset_irq();
+		}
+
+
+		if (qcmd->vfeInterruptStatus1 &
+			VFE_IMASK_AXI_HALT) {
+			CDBG("irq axi halt irq\n");
+			vfe31_process_axi_halt_irq();
+		}
+
+		if (atomic_read(&vfe31_ctrl->vstate)) {
+			if (qcmd->vfeInterruptStatus1 &
+					VFE31_IMASK_ERROR_ONLY_1) {
+				pr_err("irq	errorIrq\n");
+				vfe31_process_error_irq(
+					qcmd->vfeInterruptStatus1 &
+					VFE31_IMASK_ERROR_ONLY_1);
+			}
+
+			/* irqs below are only valid when in active state. */
+			/* next, check output path related interrupts. */
+			if (qcmd->vfeInterruptStatus0 &
+				VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE0_MASK) {
+				CDBG("Image composite done 0 irq occured.\n");
+				vfe31_process_output_path_irq_0(
+					qcmd->vfePingPongStatus);
+			}
+
+			if (qcmd->vfeInterruptStatus0 &
+				VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE1_MASK) {
+				CDBG("Image composite done 1 irq occured.\n");
+				vfe31_process_output_path_irq_1(
+					qcmd->vfePingPongStatus);
+			}
+
+			if (qcmd->vfeInterruptStatus0 &
+				VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE2_MASK) {
+				CDBG("Image composite done 2 irq occured.\n");
+				vfe31_process_output_path_irq_2(
+					qcmd->vfePingPongStatus);
+			}
+
+			/* then process stats irq. */
+			if (vfe31_ctrl->stats_comp) {
+				/* process stats comb interrupt. */
+				if (qcmd->vfeInterruptStatus0 &
+					VFE_IRQ_STATUS0_STATS_COMPOSIT_MASK) {
+					CDBG("Stats composite irq occured.\n");
+					vfe31_process_stats_irq(
+						&qcmd->vfeInterruptStatus0);
+				}
+			} else {
+				/* process individual stats interrupt. */
+				if (qcmd->vfeInterruptStatus0 &
+						VFE_COM_STATUS) {
+					CDBG("VFE stats occured.\n");
+					vfe31_process_stats_irq(
+						&qcmd->vfeInterruptStatus0);
+				}
+				if (qcmd->vfeInterruptStatus0 &
+						VFE_IRQ_STATUS0_SYNC_TIMER0) {
+					CDBG("SYNC_TIMER 0 irq occured.\n");
+					vfe31_send_msg_no_payload(
+						MSG_ID_SYNC_TIMER0_DONE);
+				}
+				if (qcmd->vfeInterruptStatus0 &
+						VFE_IRQ_STATUS0_SYNC_TIMER1) {
+					CDBG("SYNC_TIMER 1 irq occured.\n");
+					vfe31_send_msg_no_payload(
+						MSG_ID_SYNC_TIMER1_DONE);
+				}
+				if (qcmd->vfeInterruptStatus0 &
+						VFE_IRQ_STATUS0_SYNC_TIMER2) {
+					CDBG("SYNC_TIMER 2 irq occured.\n");
+					vfe31_send_msg_no_payload(
+						MSG_ID_SYNC_TIMER2_DONE);
+				}
+			}
+		}
+		if (qcmd->vfeInterruptStatus0 &
+				VFE_IRQ_STATUS0_CAMIF_SOF_MASK) {
+			CDBG("irq	camifSofIrq\n");
+			vfe31_process_camif_sof_irq();
+		}
+		kfree(qcmd);
+	}
+	CDBG("=== vfe31_do_tasklet end === \n");
+}
+
+DECLARE_TASKLET(vfe31_tasklet, vfe31_do_tasklet, 0);
+
+static irqreturn_t vfe31_parse_irq(int irq_num, void *data)
+{
+	unsigned long flags;
+	struct vfe31_irq_status irq;
+	struct vfe31_isr_queue_cmd *qcmd;
+	uint32_t *val;
+	CDBG("vfe_parse_irq\n");
+	memset(&irq, 0, sizeof(struct vfe31_irq_status));
+
+	val = (uint32_t *)(vfe31_ctrl->vfebase + VFE_IRQ_STATUS_0);
+	irq.vfeIrqStatus0 = msm_camera_io_r(val);
+
+	val = (uint32_t *)(vfe31_ctrl->vfebase + VFE_IRQ_STATUS_1);
+	irq.vfeIrqStatus1 = msm_camera_io_r(val);
+
+	if (irq.vfeIrqStatus1 & VFE_IMASK_AXI_HALT) {
+		msm_camera_io_w(VFE_IMASK_RESET,
+			vfe31_ctrl->vfebase + VFE_IRQ_MASK_1);
+		msm_camera_io_w_mb(AXI_HALT_CLEAR,
+			vfe31_ctrl->vfebase + VFE_AXI_CMD);
+	}
+
+	val = (uint32_t *)(vfe31_ctrl->vfebase + VFE_CAMIF_STATUS);
+	irq.camifStatus = msm_camera_io_r(val);
+	CDBG("camifStatus  = 0x%x\n", irq.camifStatus);
+
+	val = (uint32_t *)(vfe31_ctrl->vfebase + VFE_BUS_PING_PONG_STATUS);
+	irq.vfePingPongStatus = msm_camera_io_r(val);
+
+	/* clear the pending interrupt of the same kind.*/
+	msm_camera_io_w(irq.vfeIrqStatus0,
+		vfe31_ctrl->vfebase + VFE_IRQ_CLEAR_0);
+	msm_camera_io_w(irq.vfeIrqStatus1,
+		vfe31_ctrl->vfebase + VFE_IRQ_CLEAR_1);
+
+	/* Ensure the write order while writing
+	to the command register using the barrier */
+	msm_camera_io_w_mb(1, vfe31_ctrl->vfebase + VFE_IRQ_CMD);
+
+	if ((irq.vfeIrqStatus0 == 0) && (irq.vfeIrqStatus1 == 0)) {
+		CDBG("vfe_parse_irq: vfeIrqStatus0 & 1 are both 0!\n");
+		return IRQ_HANDLED;
+	}
+
+	qcmd = kzalloc(sizeof(struct vfe31_isr_queue_cmd),
+		GFP_ATOMIC);
+	if (!qcmd) {
+		pr_err("vfe_parse_irq: qcmd malloc failed!\n");
+		return IRQ_HANDLED;
+	}
+
+	if (atomic_read(&vfe31_ctrl->stop_ack_pending)) {
+		irq.vfeIrqStatus0 &= VFE_IMASK_WHILE_STOPPING_0;
+		irq.vfeIrqStatus1 &= vfe31_ctrl->while_stopping_mask;
+	}
+
+	spin_lock_irqsave(&vfe31_ctrl->xbar_lock, flags);
+	if ((irq.vfeIrqStatus0 &
+		VFE_IRQ_STATUS0_CAMIF_EOF_MASK) &&
+		vfe31_ctrl->xbar_update_pending) {
+		CDBG("irq camifEofIrq\n");
+		msm_camera_io_memcpy(vfe31_ctrl->vfebase + V31_XBAR_CFG_OFF,
+			(void *)vfe31_ctrl->xbar_cfg, V31_XBAR_CFG_LEN);
+		vfe31_ctrl->xbar_update_pending = 0;
+	}
+	spin_unlock_irqrestore(&vfe31_ctrl->xbar_lock, flags);
+	CDBG("vfe_parse_irq: Irq_status0 = 0x%x, Irq_status1 = 0x%x.\n",
+		irq.vfeIrqStatus0, irq.vfeIrqStatus1);
+
+	qcmd->vfeInterruptStatus0 = irq.vfeIrqStatus0;
+	qcmd->vfeInterruptStatus1 = irq.vfeIrqStatus1;
+	qcmd->vfePingPongStatus = irq.vfePingPongStatus;
+
+	spin_lock_irqsave(&vfe31_ctrl->tasklet_lock, flags);
+	list_add_tail(&qcmd->list, &vfe31_ctrl->tasklet_q);
+
+	atomic_add(1, &irq_cnt);
+	spin_unlock_irqrestore(&vfe31_ctrl->tasklet_lock, flags);
+	tasklet_schedule(&vfe31_tasklet);
+	return IRQ_HANDLED;
+}
+
+static void vfe31_release(struct platform_device *pdev)
+{
+	struct resource	*vfemem, *vfeio;
+
+	vfe31_reset_free_buf_queue_all();
+	CDBG("%s, free_irq\n", __func__);
+	free_irq(vfe31_ctrl->vfeirq, 0);
+	tasklet_kill(&vfe31_tasklet);
+
+	if (atomic_read(&irq_cnt))
+		pr_warning("%s, Warning IRQ Count not ZERO\n", __func__);
+
+	vfemem = vfe31_ctrl->vfemem;
+	vfeio  = vfe31_ctrl->vfeio;
+
+	msm_vpe_release();
+
+	kfree(vfe31_ctrl->extdata);
+	iounmap(vfe31_ctrl->vfebase);
+	kfree(vfe31_ctrl);
+	vfe31_ctrl = NULL;
+	release_mem_region(vfemem->start, (vfemem->end - vfemem->start) + 1);
+	CDBG("%s, msm_camio_disable\n", __func__);
+	msm_camio_disable(pdev);
+	msm_camio_set_perf_lvl(S_EXIT);
+
+	vfe_syncdata = NULL;
+}
+
+static int vfe31_resource_init(struct msm_vfe_callback *presp,
+	struct platform_device *pdev, void *sdata)
+{
+	struct resource	*vfemem, *vfeirq, *vfeio;
+	int rc;
+	struct msm_camera_sensor_info *s_info;
+	s_info = pdev->dev.platform_data;
+
+	pdev->resource = s_info->resource;
+	pdev->num_resources = s_info->num_resources;
+
+	vfemem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!vfemem) {
+		pr_err("%s: no mem resource?\n", __func__);
+		return -ENODEV;
+	}
+
+	vfeirq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!vfeirq) {
+		pr_err("%s: no irq resource?\n", __func__);
+		return -ENODEV;
+	}
+
+	vfeio = request_mem_region(vfemem->start,
+		resource_size(vfemem), pdev->name);
+	if (!vfeio) {
+		pr_err("%s: VFE region already claimed\n", __func__);
+		return -EBUSY;
+	}
+
+	vfe31_ctrl = kzalloc(sizeof(struct vfe31_ctrl_type), GFP_KERNEL);
+	if (!vfe31_ctrl) {
+		rc = -ENOMEM;
+		goto cmd_init_failed1;
+	}
+
+	vfe31_ctrl->vfeirq = vfeirq->start;
+
+	vfe31_ctrl->vfebase =
+		ioremap(vfemem->start, (vfemem->end - vfemem->start) + 1);
+	if (!vfe31_ctrl->vfebase) {
+		rc = -ENOMEM;
+		pr_err("%s: vfe ioremap failed\n", __func__);
+		goto cmd_init_failed2;
+	}
+
+	if (presp && presp->vfe_resp)
+		vfe31_ctrl->resp = presp;
+	else {
+		rc = -EINVAL;
+		goto cmd_init_failed3;
+	}
+
+	vfe31_ctrl->extdata =
+		kmalloc(sizeof(struct vfe31_frame_extra), GFP_KERNEL);
+	if (!vfe31_ctrl->extdata) {
+		rc = -ENOMEM;
+		goto cmd_init_failed3;
+	}
+
+	vfe31_ctrl->extlen = sizeof(struct vfe31_frame_extra);
+
+	spin_lock_init(&vfe31_ctrl->io_lock);
+	spin_lock_init(&vfe31_ctrl->update_ack_lock);
+	spin_lock_init(&vfe31_ctrl->tasklet_lock);
+	spin_lock_init(&vfe31_ctrl->xbar_lock);
+
+	INIT_LIST_HEAD(&vfe31_ctrl->tasklet_q);
+	vfe31_init_free_buf_queue();
+
+	vfe31_ctrl->syncdata = sdata;
+	vfe31_ctrl->vfemem = vfemem;
+	vfe31_ctrl->vfeio  = vfeio;
+	vfe31_ctrl->update_gamma = false;
+	vfe31_ctrl->update_luma = false;
+	vfe31_ctrl->s_info = s_info;
+	vfe31_ctrl->stats_comp = 0;
+	vfe31_ctrl->hfr_mode = HFR_MODE_OFF;
+	return 0;
+
+cmd_init_failed3:
+	free_irq(vfe31_ctrl->vfeirq, 0);
+	iounmap(vfe31_ctrl->vfebase);
+cmd_init_failed2:
+	kfree(vfe31_ctrl);
+cmd_init_failed1:
+	release_mem_region(vfemem->start, (vfemem->end - vfemem->start) + 1);
+	return rc;
+}
+
+static int vfe31_init(struct msm_vfe_callback *presp,
+	struct platform_device *pdev)
+{
+	int rc = 0;
+	struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+	struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+
+	camio_clk = camdev->ioclk;
+
+	rc = vfe31_resource_init(presp, pdev, vfe_syncdata);
+	if (rc < 0)
+		return rc;
+	/* Bring up all the required GPIOs and Clocks */
+	rc = msm_camio_enable(pdev);
+	msm_camio_set_perf_lvl(S_INIT);
+	if (msm_vpe_open() < 0)
+		CDBG("%s: vpe_open failed\n", __func__);
+
+	/* TO DO: Need to release the VFE resources */
+	rc = request_irq(vfe31_ctrl->vfeirq, vfe31_parse_irq,
+			IRQF_TRIGGER_RISING, "vfe", 0);
+
+	return rc;
+}
+
+void msm_camvfe_fn_init(struct msm_camvfe_fn *fptr, void *data)
+{
+	fptr->vfe_init    = vfe31_init;
+	fptr->vfe_enable  = vfe31_enable;
+	fptr->vfe_config  = vfe31_config;
+	fptr->vfe_disable = vfe31_disable;
+	fptr->vfe_release = vfe31_release;
+	fptr->vfe_stop = vfe31_stop;
+	vfe_syncdata = data;
+}
+
+void msm_camvpe_fn_init(struct msm_camvpe_fn *fptr, void *data)
+{
+	fptr->vpe_reg		= msm_vpe_reg;
+	fptr->send_frame_to_vpe	= msm_send_frame_to_vpe;
+	fptr->vpe_config	= msm_vpe_config;
+	fptr->vpe_cfg_update	= msm_vpe_cfg_update;
+	fptr->dis		= &(vpe_ctrl->dis_en);
+	fptr->vpe_cfg_offset = msm_vpe_offset_update;
+	vpe_ctrl->syncdata = data;
+}
diff --git a/drivers/media/video/msm/msm_vfe31.h b/drivers/media/video/msm/msm_vfe31.h
new file mode 100644
index 0000000..1d66621
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe31.h
@@ -0,0 +1,1119 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __MSM_VFE31_H__
+#define __MSM_VFE31_H__
+
+#define TRUE  1
+#define FALSE 0
+
+/* at start of camif,  bit 1:0 = 0x01:enable
+ * image data capture at frame boundary. */
+#define CAMIF_COMMAND_START  0x00000005
+
+/* bit 2= 0x1:clear the CAMIF_STATUS register
+ * value. */
+#define CAMIF_COMMAND_CLEAR  0x00000004
+
+/* at stop of vfe pipeline, for now it is assumed
+ * that camif will stop at any time. Bit 1:0 = 0x10:
+ * disable image data capture immediately. */
+#define CAMIF_COMMAND_STOP_IMMEDIATELY  0x00000002
+
+/* at stop of vfe pipeline, for now it is assumed
+ * that camif will stop at any time. Bit 1:0 = 0x00:
+ * disable image data capture at frame boundary */
+#define CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY  0x00000000
+
+/* to halt axi bridge */
+#define AXI_HALT  0x00000001
+
+/* clear the halt bit. */
+#define AXI_HALT_CLEAR  0x00000000
+
+/* clear axi_halt_irq */
+#define MASK_AXI_HALT_IRQ	0xFF7FFFFF
+
+/* reset the pipeline when stop command is issued.
+ * (without reset the register.) bit 26-31 = 0,
+ * domain reset, bit 0-9 = 1 for module reset, except
+ * register module. */
+#define VFE_RESET_UPON_STOP_CMD  0x000003ef
+
+/* reset the pipeline when reset command.
+ * bit 26-31 = 0, domain reset, bit 0-9 = 1 for module reset. */
+#define VFE_RESET_UPON_RESET_CMD  0x000003ff
+
+/* bit 5 is for axi status idle or busy.
+ * 1 =  halted,  0 = busy */
+#define AXI_STATUS_BUSY_MASK 0x00000020
+
+/* bit 0 & bit 1 = 1, both y and cbcr irqs need to be present
+ * for frame done interrupt */
+#define VFE_COMP_IRQ_BOTH_Y_CBCR 3
+
+/* bit 1 = 1, only cbcr irq triggers frame done interrupt */
+#define VFE_COMP_IRQ_CBCR_ONLY 2
+
+/* bit 0 = 1, only y irq triggers frame done interrupt */
+#define VFE_COMP_IRQ_Y_ONLY 1
+
+/* bit 0 = 1, PM go;   bit1 = 1, PM stop */
+#define VFE_PERFORMANCE_MONITOR_GO   0x00000001
+#define VFE_PERFORMANCE_MONITOR_STOP 0x00000002
+
+/* bit 0 = 1, test gen go;   bit1 = 1, test gen stop */
+#define VFE_TEST_GEN_GO   0x00000001
+#define VFE_TEST_GEN_STOP 0x00000002
+
+/* the chroma is assumed to be interpolated between
+ * the luma samples.  JPEG 4:2:2 */
+#define VFE_CHROMA_UPSAMPLE_INTERPOLATED 0
+
+/* constants for irq registers */
+#define VFE_DISABLE_ALL_IRQS 0
+/* bit =1 is to clear the corresponding bit in VFE_IRQ_STATUS.  */
+#define VFE_CLEAR_ALL_IRQS   0xffffffff
+
+#define VFE_IRQ_STATUS0_CAMIF_SOF_MASK            0x00000001
+#define VFE_IRQ_STATUS0_CAMIF_EOF_MASK            0x00000004
+#define VFE_IRQ_STATUS0_REG_UPDATE_MASK           0x00000020
+#define VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE0_MASK 0x00200000
+#define VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE1_MASK 0x00400000
+#define VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE2_MASK 0x00800000
+#define VFE_IRQ_STATUS1_RESET_AXI_HALT_ACK_MASK   0x00800000
+#define VFE_IRQ_STATUS0_STATS_COMPOSIT_MASK       0x01000000
+
+#define VFE_IRQ_STATUS0_STATS_AEC     0x2000  /* bit 13 */
+#define VFE_IRQ_STATUS0_STATS_AF      0x4000  /* bit 14 */
+#define VFE_IRQ_STATUS0_STATS_AWB     0x8000  /* bit 15 */
+#define VFE_IRQ_STATUS0_STATS_RS      0x10000  /* bit 16 */
+#define VFE_IRQ_STATUS0_STATS_CS      0x20000  /* bit 17 */
+#define VFE_IRQ_STATUS0_STATS_IHIST   0x40000  /* bit 18 */
+
+#define VFE_IRQ_STATUS0_SYNC_TIMER0   0x2000000  /* bit 25 */
+#define VFE_IRQ_STATUS0_SYNC_TIMER1   0x4000000  /* bit 26 */
+#define VFE_IRQ_STATUS0_SYNC_TIMER2   0x8000000  /* bit 27 */
+#define VFE_IRQ_STATUS0_ASYNC_TIMER0  0x10000000  /* bit 28 */
+#define VFE_IRQ_STATUS0_ASYNC_TIMER1  0x20000000  /* bit 29 */
+#define VFE_IRQ_STATUS0_ASYNC_TIMER2  0x40000000  /* bit 30 */
+#define VFE_IRQ_STATUS0_ASYNC_TIMER3  0x80000000  /* bit 31 */
+
+/* imask for while waiting for stop ack,  driver has already
+ * requested stop, waiting for reset irq, and async timer irq.
+ * For irq_status_0, bit 28-31 are for async timer. For
+ * irq_status_1, bit 22 for reset irq, bit 23 for axi_halt_ack
+   irq */
+#define VFE_IMASK_WHILE_STOPPING_0  0xF0000000
+#define VFE_IMASK_WHILE_STOPPING_1  0x00C00000
+#define VFE_IMASK_RESET             0x00400000
+#define VFE_IMASK_AXI_HALT          0x00800000
+
+
+/* no error irq in mask 0 */
+#define VFE_IMASK_ERROR_ONLY_0  0x0
+/* when normal case, don't want to block error status. */
+/* bit 0-21 are error irq bits */
+#define VFE_IMASK_ERROR_ONLY_1  0x003fffff
+
+/* For BPC bit 0,bit 12-17 and bit 26 -20 are set to zero and other's 1 */
+#define BPC_MASK 0xF80C0FFE
+
+/* For BPC bit 1 and 2 are set to zero and other's 1 */
+#define ABF_MASK 0xFFFFFFF9
+
+/* For MCE enable bit 28 set to zero and other's 1 */
+#define MCE_EN_MASK 0xEFFFFFFF
+
+/* For MCE Q_K bit 28 to 31 set to zero and other's 1 */
+#define MCE_Q_K_MASK 0x0FFFFFFF
+
+#define AWB_ENABLE_MASK 0x00000080     /* bit 7 */
+#define AF_ENABLE_MASK 0x00000040      /* bit 6 */
+#define AE_ENABLE_MASK 0x00000020      /* bit 5 */
+#define IHIST_ENABLE_MASK 0x00008000   /* bit 15 */
+#define RS_ENABLE_MASK 0x00000100      /* bit 8  */
+#define CS_ENABLE_MASK 0x00000200      /* bit 9  */
+#define RS_CS_ENABLE_MASK 0x00000300   /* bit 8,9  */
+#define STATS_ENABLE_MASK 0x000483E0   /* bit 18,15,9,8,7,6,5*/
+
+#define VFE_REG_UPDATE_TRIGGER           1
+#define VFE_PM_BUF_MAX_CNT_MASK          0xFF
+#define VFE_DMI_CFG_DEFAULT              0x00000100
+#define LENS_ROLL_OFF_DELTA_TABLE_OFFSET 32
+#define VFE_AE_PINGPONG_STATUS_BIT       0x80
+#define VFE_AF_PINGPONG_STATUS_BIT       0x100
+#define VFE_AWB_PINGPONG_STATUS_BIT      0x200
+#define PINGPONG_LOWER                   0x7
+
+#define HFR_MODE_OFF 1
+
+enum VFE31_DMI_RAM_SEL {
+	 NO_MEM_SELECTED          = 0,
+	 ROLLOFF_RAM              = 0x1,
+	 RGBLUT_RAM_CH0_BANK0     = 0x2,
+	 RGBLUT_RAM_CH0_BANK1     = 0x3,
+	 RGBLUT_RAM_CH1_BANK0     = 0x4,
+	 RGBLUT_RAM_CH1_BANK1     = 0x5,
+	 RGBLUT_RAM_CH2_BANK0     = 0x6,
+	 RGBLUT_RAM_CH2_BANK1     = 0x7,
+	 STATS_HIST_RAM           = 0x8,
+	 RGBLUT_CHX_BANK0         = 0x9,
+	 RGBLUT_CHX_BANK1         = 0xa,
+	 LUMA_ADAPT_LUT_RAM_BANK0 = 0xb,
+	 LUMA_ADAPT_LUT_RAM_BANK1 = 0xc
+};
+
+enum  VFE_STATE {
+	VFE_STATE_IDLE,
+	VFE_STATE_ACTIVE
+};
+
+enum  vfe_recording_state {
+	VFE_REC_STATE_IDLE,
+	VFE_REC_STATE_START_REQUESTED,
+	VFE_REC_STATE_STARTED,
+	VFE_REC_STATE_STOP_REQUESTED,
+	VFE_REC_STATE_STOPPED,
+};
+
+#define V31_DUMMY_0               0
+#define V31_SET_CLK               1
+#define V31_RESET                 2
+#define V31_START                 3
+#define V31_TEST_GEN_START        4
+#define V31_OPERATION_CFG         5
+#define V31_AXI_OUT_CFG           6
+#define V31_CAMIF_CFG             7
+#define V31_AXI_INPUT_CFG         8
+#define V31_BLACK_LEVEL_CFG       9
+#define V31_ROLL_OFF_CFG          10
+#define V31_DEMUX_CFG             11
+#define V31_DEMOSAIC_0_CFG        12 /* general */
+#define V31_DEMOSAIC_1_CFG        13 /* ABF     */
+#define V31_DEMOSAIC_2_CFG        14 /* BPC     */
+#define V31_FOV_CFG               15
+#define V31_MAIN_SCALER_CFG       16
+#define V31_WB_CFG                17
+#define V31_COLOR_COR_CFG         18
+#define V31_RGB_G_CFG             19
+#define V31_LA_CFG                20
+#define V31_CHROMA_EN_CFG         21
+#define V31_CHROMA_SUP_CFG        22
+#define V31_MCE_CFG               23
+#define V31_SK_ENHAN_CFG          24
+#define V31_ASF_CFG               25
+#define V31_S2Y_CFG               26
+#define V31_S2CbCr_CFG            27
+#define V31_CHROMA_SUBS_CFG       28
+#define V31_OUT_CLAMP_CFG         29
+#define V31_FRAME_SKIP_CFG        30
+#define V31_DUMMY_1               31
+#define V31_DUMMY_2               32
+#define V31_DUMMY_3               33
+#define V31_UPDATE                34
+#define V31_BL_LVL_UPDATE         35
+#define V31_DEMUX_UPDATE          36
+#define V31_DEMOSAIC_1_UPDATE     37 /* BPC */
+#define V31_DEMOSAIC_2_UPDATE     38 /* ABF */
+#define V31_FOV_UPDATE            39
+#define V31_MAIN_SCALER_UPDATE    40
+#define V31_WB_UPDATE             41
+#define V31_COLOR_COR_UPDATE      42
+#define V31_RGB_G_UPDATE          43
+#define V31_LA_UPDATE             44
+#define V31_CHROMA_EN_UPDATE      45
+#define V31_CHROMA_SUP_UPDATE     46
+#define V31_MCE_UPDATE            47
+#define V31_SK_ENHAN_UPDATE       48
+#define V31_S2CbCr_UPDATE         49
+#define V31_S2Y_UPDATE            50
+#define V31_ASF_UPDATE            51
+#define V31_FRAME_SKIP_UPDATE     52
+#define V31_CAMIF_FRAME_UPDATE    53
+#define V31_STATS_AF_UPDATE       54
+#define V31_STATS_AE_UPDATE       55
+#define V31_STATS_AWB_UPDATE      56
+#define V31_STATS_RS_UPDATE       57
+#define V31_STATS_CS_UPDATE       58
+#define V31_STATS_SKIN_UPDATE     59
+#define V31_STATS_IHIST_UPDATE    60
+#define V31_DUMMY_4               61
+#define V31_EPOCH1_ACK            62
+#define V31_EPOCH2_ACK            63
+#define V31_START_RECORDING       64
+#define V31_STOP_RECORDING        65
+#define V31_DUMMY_5               66
+#define V31_DUMMY_6               67
+#define V31_CAPTURE               68
+#define V31_DUMMY_7               69
+#define V31_STOP                  70
+#define V31_GET_HW_VERSION        71
+#define V31_GET_FRAME_SKIP_COUNTS 72
+#define V31_OUTPUT1_BUFFER_ENQ    73
+#define V31_OUTPUT2_BUFFER_ENQ    74
+#define V31_OUTPUT3_BUFFER_ENQ    75
+#define V31_JPEG_OUT_BUF_ENQ      76
+#define V31_RAW_OUT_BUF_ENQ       77
+#define V31_RAW_IN_BUF_ENQ        78
+#define V31_STATS_AF_ENQ          79
+#define V31_STATS_AE_ENQ          80
+#define V31_STATS_AWB_ENQ         81
+#define V31_STATS_RS_ENQ          82
+#define V31_STATS_CS_ENQ          83
+#define V31_STATS_SKIN_ENQ        84
+#define V31_STATS_IHIST_ENQ       85
+#define V31_DUMMY_8               86
+#define V31_JPEG_ENC_CFG          87
+#define V31_DUMMY_9               88
+#define V31_STATS_AF_START        89
+#define V31_STATS_AF_STOP         90
+#define V31_STATS_AE_START        91
+#define V31_STATS_AE_STOP         92
+#define V31_STATS_AWB_START       93
+#define V31_STATS_AWB_STOP        94
+#define V31_STATS_RS_START        95
+#define V31_STATS_RS_STOP         96
+#define V31_STATS_CS_START        97
+#define V31_STATS_CS_STOP         98
+#define V31_STATS_SKIN_START      99
+#define V31_STATS_SKIN_STOP       100
+#define V31_STATS_IHIST_START     101
+#define V31_STATS_IHIST_STOP      102
+#define V31_DUMMY_10              103
+#define V31_SYNC_TIMER_SETTING    104
+#define V31_ASYNC_TIMER_SETTING   105
+#define V31_LIVESHOT              106
+#define V31_ZSL                   107
+#define V31_STEREOCAM             108
+#define V31_LA_SETUP              109
+#define V31_XBAR_CFG              110
+#define V31_EZTUNE_CFG            111
+
+#define V31_CAMIF_OFF             0x000001E4
+#define V31_CAMIF_LEN             32
+
+#define V31_DEMUX_OFF             0x00000284
+#define V31_DEMUX_LEN             20
+
+#define V31_DEMOSAIC_0_OFF        0x00000298
+#define V31_DEMOSAIC_0_LEN        4
+/* ABF     */
+#define V31_DEMOSAIC_1_OFF        0x000002A4
+#define V31_DEMOSAIC_1_LEN        180
+/* BPC     */
+#define V31_DEMOSAIC_2_OFF        0x0000029C
+#define V31_DEMOSAIC_2_LEN        8
+
+/* gamma VFE_LUT_BANK_SEL*/
+#define V31_GAMMA_CFG_OFF         0x000003BC
+#define V31_LUMA_CFG_OFF          0x000003C0
+
+#define V31_OUT_CLAMP_OFF         0x00000524
+#define V31_OUT_CLAMP_LEN         8
+
+#define V31_OPERATION_CFG_LEN     32
+
+#define V31_AXI_OUT_OFF           0x00000038
+#define V31_AXI_OUT_LEN           212
+#define V31_AXI_CH_INF_LEN        24
+#define V31_AXI_CFG_LEN           47
+
+#define V31_FRAME_SKIP_OFF        0x00000504
+#define V31_FRAME_SKIP_LEN        32
+
+#define V31_CHROMA_SUBS_OFF       0x000004F8
+#define V31_CHROMA_SUBS_LEN       12
+
+#define V31_FOV_OFF           0x00000360
+#define V31_FOV_LEN           8
+
+#define V31_MAIN_SCALER_OFF 0x00000368
+#define V31_MAIN_SCALER_LEN 28
+
+#define V31_S2Y_OFF 0x000004D0
+#define V31_S2Y_LEN 20
+
+#define V31_S2CbCr_OFF 0x000004E4
+#define V31_S2CbCr_LEN 20
+
+#define V31_CHROMA_EN_OFF 0x000003C4
+#define V31_CHROMA_EN_LEN 36
+
+#define V31_SYNC_TIMER_OFF      0x0000020C
+#define V31_SYNC_TIMER_POLARITY_OFF 0x00000234
+#define V31_TIMER_SELECT_OFF        0x0000025C
+#define V31_SYNC_TIMER_LEN 28
+
+#define V31_ASYNC_TIMER_OFF 0x00000238
+#define V31_ASYNC_TIMER_LEN 28
+
+#define V31_BLACK_LEVEL_OFF 0x00000264
+#define V31_BLACK_LEVEL_LEN 16
+
+#define V31_ROLL_OFF_CFG_OFF 0x00000274
+#define V31_ROLL_OFF_CFG_LEN 16
+
+#define V31_COLOR_COR_OFF 0x00000388
+#define V31_COLOR_COR_LEN 52
+
+#define V31_WB_OFF 0x00000384
+#define V31_WB_LEN 4
+
+#define V31_RGB_G_OFF 0x000003BC
+#define V31_RGB_G_LEN 4
+
+#define V31_LA_OFF 0x000003C0
+#define V31_LA_LEN 4
+
+#define V31_SCE_OFF 0x00000418
+#define V31_SCE_LEN 136
+
+#define V31_CHROMA_SUP_OFF 0x000003E8
+#define V31_CHROMA_SUP_LEN 12
+
+#define V31_MCE_OFF 0x000003F4
+#define V31_MCE_LEN 36
+#define V31_STATS_AF_OFF 0x0000053c
+#define V31_STATS_AF_LEN 16
+
+#define V31_STATS_AE_OFF 0x00000534
+#define V31_STATS_AE_LEN 8
+
+#define V31_STATS_AWB_OFF 0x0000054c
+#define V31_STATS_AWB_LEN 32
+
+#define V31_STATS_IHIST_OFF 0x0000057c
+#define V31_STATS_IHIST_LEN 8
+
+#define V31_STATS_RS_OFF 0x0000056c
+#define V31_STATS_RS_LEN 8
+
+#define V31_STATS_CS_OFF 0x00000574
+#define V31_STATS_CS_LEN 8
+
+#define V31_XBAR_CFG_OFF 0x00000040
+#define V31_XBAR_CFG_LEN 8
+
+#define V31_EZTUNE_CFG_OFF 0x00000010
+#define V31_EZTUNE_CFG_LEN 4
+
+#define V31_ASF_OFF 0x000004A0
+#define V31_ASF_LEN 48
+#define V31_ASF_UPDATE_LEN 36
+
+#define V31_CAPTURE_LEN 4
+
+struct vfe_cmd_hw_version {
+	uint32_t minorVersion;
+	uint32_t majorVersion;
+	uint32_t coreVersion;
+};
+
+enum VFE_AXI_OUTPUT_MODE {
+	VFE_AXI_OUTPUT_MODE_Output1,
+	VFE_AXI_OUTPUT_MODE_Output2,
+	VFE_AXI_OUTPUT_MODE_Output1AndOutput2,
+	VFE_AXI_OUTPUT_MODE_CAMIFToAXIViaOutput2,
+	VFE_AXI_OUTPUT_MODE_Output2AndCAMIFToAXIViaOutput1,
+	VFE_AXI_OUTPUT_MODE_Output1AndCAMIFToAXIViaOutput2,
+	VFE_AXI_LAST_OUTPUT_MODE_ENUM
+};
+
+enum VFE_RAW_WR_PATH_SEL {
+	VFE_RAW_OUTPUT_DISABLED,
+	VFE_RAW_OUTPUT_ENC_CBCR_PATH,
+	VFE_RAW_OUTPUT_VIEW_CBCR_PATH,
+	VFE_RAW_OUTPUT_PATH_INVALID
+};
+
+
+#define VFE_AXI_OUTPUT_BURST_LENGTH     4
+#define VFE_MAX_NUM_FRAGMENTS_PER_FRAME 4
+#define VFE_AXI_OUTPUT_CFG_FRAME_COUNT  3
+
+struct vfe_cmds_per_write_master {
+	uint16_t imageWidth;
+	uint16_t imageHeight;
+	uint16_t outRowCount;
+	uint16_t outRowIncrement;
+	uint32_t outFragments[VFE_AXI_OUTPUT_CFG_FRAME_COUNT]
+		[VFE_MAX_NUM_FRAGMENTS_PER_FRAME];
+};
+
+struct vfe_cmds_axi_per_output_path {
+	uint8_t fragmentCount;
+	struct vfe_cmds_per_write_master firstWM;
+	struct vfe_cmds_per_write_master secondWM;
+};
+
+enum VFE_AXI_BURST_LENGTH {
+	VFE_AXI_BURST_LENGTH_IS_2  = 2,
+	VFE_AXI_BURST_LENGTH_IS_4  = 4,
+	VFE_AXI_BURST_LENGTH_IS_8  = 8,
+	VFE_AXI_BURST_LENGTH_IS_16 = 16
+};
+
+
+struct vfe_cmd_fov_crop_config {
+	uint8_t enable;
+	uint16_t firstPixel;
+	uint16_t lastPixel;
+	uint16_t firstLine;
+	uint16_t lastLine;
+};
+
+struct vfe_cmds_main_scaler_stripe_init {
+	uint16_t MNCounterInit;
+	uint16_t phaseInit;
+};
+
+struct vfe_cmds_scaler_one_dimension {
+	uint8_t  enable;
+	uint16_t inputSize;
+	uint16_t outputSize;
+	uint32_t phaseMultiplicationFactor;
+	uint8_t  interpolationResolution;
+};
+
+struct vfe_cmd_main_scaler_config {
+	uint8_t enable;
+	struct vfe_cmds_scaler_one_dimension    hconfig;
+	struct vfe_cmds_scaler_one_dimension    vconfig;
+	struct vfe_cmds_main_scaler_stripe_init MNInitH;
+	struct vfe_cmds_main_scaler_stripe_init MNInitV;
+};
+
+struct vfe_cmd_scaler2_config {
+	uint8_t enable;
+	struct vfe_cmds_scaler_one_dimension hconfig;
+	struct vfe_cmds_scaler_one_dimension vconfig;
+};
+
+
+struct vfe_cmd_frame_skip_update {
+	uint32_t output1Pattern;
+	uint32_t output2Pattern;
+};
+
+struct vfe_cmd_output_clamp_config {
+	uint8_t minCh0;
+	uint8_t minCh1;
+	uint8_t minCh2;
+	uint8_t maxCh0;
+	uint8_t maxCh1;
+	uint8_t maxCh2;
+};
+
+struct vfe_cmd_chroma_subsample_config {
+	uint8_t enable;
+	uint8_t cropEnable;
+	uint8_t vsubSampleEnable;
+	uint8_t hsubSampleEnable;
+	uint8_t vCosited;
+	uint8_t hCosited;
+	uint8_t vCositedPhase;
+	uint8_t hCositedPhase;
+	uint16_t cropWidthFirstPixel;
+	uint16_t cropWidthLastPixel;
+	uint16_t cropHeightFirstLine;
+	uint16_t cropHeightLastLine;
+};
+
+enum VFE_START_INPUT_SOURCE {
+	VFE_START_INPUT_SOURCE_CAMIF,
+	VFE_START_INPUT_SOURCE_TESTGEN,
+	VFE_START_INPUT_SOURCE_AXI,
+	VFE_START_INPUT_SOURCE_INVALID
+};
+
+enum VFE_START_PIXEL_PATTERN {
+	VFE_BAYER_RGRGRG,
+	VFE_BAYER_GRGRGR,
+	VFE_BAYER_BGBGBG,
+	VFE_BAYER_GBGBGB,
+	VFE_YUV_YCbYCr,
+	VFE_YUV_YCrYCb,
+	VFE_YUV_CbYCrY,
+	VFE_YUV_CrYCbY
+};
+
+enum VFE_BUS_RD_INPUT_PIXEL_PATTERN {
+	VFE_BAYER_RAW,
+	VFE_YUV_INTERLEAVED,
+	VFE_YUV_PSEUDO_PLANAR_Y,
+	VFE_YUV_PSEUDO_PLANAR_CBCR
+};
+
+enum VFE_YUV_INPUT_COSITING_MODE {
+	VFE_YUV_COSITED,
+	VFE_YUV_INTERPOLATED
+};
+
+
+/* 13*1  */
+#define VFE31_ROLL_OFF_INIT_TABLE_SIZE  13
+/* 13*16 */
+#define VFE31_ROLL_OFF_DELTA_TABLE_SIZE 208
+
+#define VFE31_GAMMA_NUM_ENTRIES  64
+
+#define VFE31_LA_TABLE_LENGTH    64
+
+#define VFE31_HIST_TABLE_LENGTH  256
+
+struct vfe_cmds_demosaic_abf {
+	uint8_t   enable;
+	uint8_t   forceOn;
+	uint8_t   shift;
+	uint16_t  lpThreshold;
+	uint16_t  max;
+	uint16_t  min;
+	uint8_t   ratio;
+};
+
+struct vfe_cmds_demosaic_bpc {
+	uint8_t   enable;
+	uint16_t  fmaxThreshold;
+	uint16_t  fminThreshold;
+	uint16_t  redDiffThreshold;
+	uint16_t  blueDiffThreshold;
+	uint16_t  greenDiffThreshold;
+};
+
+struct vfe_cmd_demosaic_config {
+	uint8_t   enable;
+	uint8_t   slopeShift;
+	struct vfe_cmds_demosaic_abf abfConfig;
+	struct vfe_cmds_demosaic_bpc bpcConfig;
+};
+
+struct vfe_cmd_demosaic_bpc_update {
+	struct vfe_cmds_demosaic_bpc bpcUpdate;
+};
+
+struct vfe_cmd_demosaic_abf_update {
+	struct vfe_cmds_demosaic_abf abfUpdate;
+};
+
+struct vfe_cmd_white_balance_config {
+	uint8_t  enable;
+	uint16_t ch2Gain;
+	uint16_t ch1Gain;
+	uint16_t ch0Gain;
+};
+
+enum VFE_COLOR_CORRECTION_COEF_QFACTOR {
+	COEF_IS_Q7_SIGNED,
+	COEF_IS_Q8_SIGNED,
+	COEF_IS_Q9_SIGNED,
+	COEF_IS_Q10_SIGNED
+};
+
+struct vfe_cmd_color_correction_config {
+	uint8_t     enable;
+	enum VFE_COLOR_CORRECTION_COEF_QFACTOR coefQFactor;
+	int16_t  C0;
+	int16_t  C1;
+	int16_t  C2;
+	int16_t  C3;
+	int16_t  C4;
+	int16_t  C5;
+	int16_t  C6;
+	int16_t  C7;
+	int16_t  C8;
+	int16_t  K0;
+	int16_t  K1;
+	int16_t  K2;
+};
+
+#define VFE_LA_TABLE_LENGTH 64
+
+struct vfe_cmd_la_config {
+	uint8_t enable;
+	int16_t table[VFE_LA_TABLE_LENGTH];
+};
+
+#define VFE_GAMMA_TABLE_LENGTH 256
+enum VFE_RGB_GAMMA_TABLE_SELECT {
+	RGB_GAMMA_CH0_SELECTED,
+	RGB_GAMMA_CH1_SELECTED,
+	RGB_GAMMA_CH2_SELECTED,
+	RGB_GAMMA_CH0_CH1_SELECTED,
+	RGB_GAMMA_CH0_CH2_SELECTED,
+	RGB_GAMMA_CH1_CH2_SELECTED,
+	RGB_GAMMA_CH0_CH1_CH2_SELECTED
+};
+
+struct vfe_cmd_rgb_gamma_config {
+	uint8_t enable;
+	enum VFE_RGB_GAMMA_TABLE_SELECT channelSelect;
+	int16_t table[VFE_GAMMA_TABLE_LENGTH];
+};
+
+struct vfe_cmd_chroma_enhan_config {
+	uint8_t  enable;
+	int16_t am;
+	int16_t ap;
+	int16_t bm;
+	int16_t bp;
+	int16_t cm;
+	int16_t cp;
+	int16_t dm;
+	int16_t dp;
+	int16_t kcr;
+	int16_t kcb;
+	int16_t RGBtoYConversionV0;
+	int16_t RGBtoYConversionV1;
+	int16_t RGBtoYConversionV2;
+	uint8_t RGBtoYConversionOffset;
+};
+
+struct vfe_cmd_chroma_suppression_config {
+	uint8_t enable;
+	uint8_t m1;
+	uint8_t m3;
+	uint8_t n1;
+	uint8_t n3;
+	uint8_t nn1;
+	uint8_t mm1;
+};
+
+struct vfe_cmd_asf_config {
+	uint8_t enable;
+	uint8_t smoothFilterEnabled;
+	uint8_t sharpMode;
+	uint8_t smoothCoefCenter;
+	uint8_t smoothCoefSurr;
+	uint8_t normalizeFactor;
+	uint8_t sharpK1;
+	uint8_t sharpK2;
+	uint8_t sharpThreshE1;
+	int8_t sharpThreshE2;
+	int8_t sharpThreshE3;
+	int8_t sharpThreshE4;
+	int8_t sharpThreshE5;
+	int8_t filter1Coefficients[9];
+	int8_t filter2Coefficients[9];
+	uint8_t  cropEnable;
+	uint16_t cropFirstPixel;
+	uint16_t cropLastPixel;
+	uint16_t cropFirstLine;
+	uint16_t cropLastLine;
+};
+
+struct vfe_cmd_asf_update {
+	uint8_t enable;
+	uint8_t smoothFilterEnabled;
+	uint8_t sharpMode;
+	uint8_t smoothCoefCenter;
+	uint8_t smoothCoefSurr;
+	uint8_t normalizeFactor;
+	uint8_t sharpK1;
+	uint8_t sharpK2;
+	uint8_t sharpThreshE1;
+	int8_t  sharpThreshE2;
+	int8_t  sharpThreshE3;
+	int8_t  sharpThreshE4;
+	int8_t  sharpThreshE5;
+	int8_t  filter1Coefficients[9];
+	int8_t  filter2Coefficients[9];
+	uint8_t cropEnable;
+};
+
+enum VFE_TEST_GEN_SYNC_EDGE {
+	VFE_TEST_GEN_SYNC_EDGE_ActiveHigh,
+	VFE_TEST_GEN_SYNC_EDGE_ActiveLow
+};
+
+
+struct vfe_cmd_bus_pm_start {
+	uint8_t output2YWrPmEnable;
+	uint8_t output2CbcrWrPmEnable;
+	uint8_t output1YWrPmEnable;
+	uint8_t output1CbcrWrPmEnable;
+};
+
+struct  vfe_frame_skip_counts {
+	uint32_t  totalFrameCount;
+	uint32_t  output1Count;
+	uint32_t  output2Count;
+};
+
+enum VFE_AXI_RD_UNPACK_HBI_SEL {
+	VFE_AXI_RD_HBI_32_CLOCK_CYCLES,
+	VFE_AXI_RD_HBI_64_CLOCK_CYCLES,
+	VFE_AXI_RD_HBI_128_CLOCK_CYCLES,
+	VFE_AXI_RD_HBI_256_CLOCK_CYCLES,
+	VFE_AXI_RD_HBI_512_CLOCK_CYCLES,
+	VFE_AXI_RD_HBI_1024_CLOCK_CYCLES,
+	VFE_AXI_RD_HBI_2048_CLOCK_CYCLES,
+	VFE_AXI_RD_HBI_4096_CLOCK_CYCLES
+};
+
+enum VFE31_MESSAGE_ID {
+	MSG_ID_RESET_ACK, /* 0 */
+	MSG_ID_START_ACK,
+	MSG_ID_STOP_ACK,
+	MSG_ID_UPDATE_ACK,
+	MSG_ID_OUTPUT_P,
+	MSG_ID_OUTPUT_T,
+	MSG_ID_OUTPUT_S,
+	MSG_ID_OUTPUT_V,
+	MSG_ID_SNAPSHOT_DONE,
+	MSG_ID_COMMON,
+	MSG_ID_EPOCH1, /* 10 */
+	MSG_ID_EPOCH2,
+	MSG_ID_SYNC_TIMER0_DONE,
+	MSG_ID_SYNC_TIMER1_DONE,
+	MSG_ID_SYNC_TIMER2_DONE,
+	MSG_ID_ASYNC_TIMER0_DONE,
+	MSG_ID_ASYNC_TIMER1_DONE,
+	MSG_ID_ASYNC_TIMER2_DONE,
+	MSG_ID_ASYNC_TIMER3_DONE,
+	MSG_ID_AE_OVERFLOW,
+	MSG_ID_AF_OVERFLOW, /* 20 */
+	MSG_ID_AWB_OVERFLOW,
+	MSG_ID_RS_OVERFLOW,
+	MSG_ID_CS_OVERFLOW,
+	MSG_ID_IHIST_OVERFLOW,
+	MSG_ID_SKIN_OVERFLOW,
+	MSG_ID_AXI_ERROR,
+	MSG_ID_CAMIF_OVERFLOW,
+	MSG_ID_VIOLATION,
+	MSG_ID_CAMIF_ERROR,
+	MSG_ID_BUS_OVERFLOW, /* 30 */
+	MSG_ID_SOF_ACK,
+	MSG_ID_STOP_REC_ACK,
+};
+
+struct stats_buffer {
+	uint8_t awb_ymin;
+	uint32_t aec;
+	uint32_t awb;
+	uint32_t af;
+	uint32_t ihist;
+	uint32_t rs;
+	uint32_t cs;
+	uint32_t skin;
+};
+
+struct vfe_msg_stats {
+	struct stats_buffer buff;
+	uint32_t    frameCounter;
+	uint32_t    status_bits;
+};
+
+
+struct vfe_frame_bpc_info {
+	uint32_t greenDefectPixelCount;
+	uint32_t redBlueDefectPixelCount;
+};
+
+struct vfe_frame_asf_info {
+	uint32_t  asfMaxEdge;
+	uint32_t  asfHbiCount;
+};
+
+struct vfe_msg_camif_status {
+	uint8_t  camifState;
+	uint32_t pixelCount;
+	uint32_t lineCount;
+};
+
+
+struct vfe31_irq_status {
+	uint32_t vfeIrqStatus0;
+	uint32_t vfeIrqStatus1;
+	uint32_t camifStatus;
+	uint32_t demosaicStatus;
+	uint32_t asfMaxEdge;
+	uint32_t vfePingPongStatus;
+};
+
+struct vfe_msg_output {
+	uint8_t   output_id;
+	uint32_t  p0_addr;
+	uint32_t  p1_addr;
+	uint32_t  p2_addr;
+	struct vfe_frame_bpc_info bpcInfo;
+	struct vfe_frame_asf_info asfInfo;
+	uint32_t  frameCounter;
+};
+
+struct vfe_message {
+	enum VFE31_MESSAGE_ID _d;
+	union {
+		struct vfe_msg_output              msgOut;
+		struct vfe_msg_stats               msgStats;
+		struct vfe_msg_camif_status        msgCamifError;
+   } _u;
+};
+
+/* New one for 7x30 */
+struct msm_vfe31_cmd {
+	int32_t  id;
+	uint16_t length;
+	void     *value;
+};
+
+#define V31_PREVIEW_AXI_FLAG  0x00000001
+#define V31_SNAPSHOT_AXI_FLAG (0x00000001<<1)
+
+struct vfe31_cmd_type {
+	uint16_t id;
+	uint32_t length;
+	uint32_t offset;
+	uint32_t flag;
+};
+
+struct vfe31_free_buf {
+	struct list_head node;
+	uint32_t paddr;
+	uint32_t planar0_off;
+	uint32_t planar1_off;
+	uint32_t planar2_off;
+	uint32_t cbcr_off;
+};
+
+struct vfe31_output_ch {
+	struct list_head free_buf_head;
+	spinlock_t free_buf_lock;
+	uint16_t output_fmt;
+	int8_t ch0;
+	int8_t ch1;
+	int8_t ch2;
+	uint32_t  frame_drop_cnt;
+};
+
+/* no error irq in mask 0 */
+#define VFE31_IMASK_ERROR_ONLY_0  0x0
+/* when normal case, don't want to block error status. */
+/* bit 0-21 are error irq bits */
+#define VFE31_IMASK_ERROR_ONLY_1               0x003FFFFF
+#define VFE31_IMASK_CAMIF_ERROR               (0x00000001<<0)
+#define VFE31_IMASK_STATS_CS_OVWR             (0x00000001<<1)
+#define VFE31_IMASK_STATS_IHIST_OVWR          (0x00000001<<2)
+#define VFE31_IMASK_REALIGN_BUF_Y_OVFL        (0x00000001<<3)
+#define VFE31_IMASK_REALIGN_BUF_CB_OVFL       (0x00000001<<4)
+#define VFE31_IMASK_REALIGN_BUF_CR_OVFL       (0x00000001<<5)
+#define VFE31_IMASK_VIOLATION                 (0x00000001<<6)
+#define VFE31_IMASK_IMG_MAST_0_BUS_OVFL       (0x00000001<<7)
+#define VFE31_IMASK_IMG_MAST_1_BUS_OVFL       (0x00000001<<8)
+#define VFE31_IMASK_IMG_MAST_2_BUS_OVFL       (0x00000001<<9)
+#define VFE31_IMASK_IMG_MAST_3_BUS_OVFL       (0x00000001<<10)
+#define VFE31_IMASK_IMG_MAST_4_BUS_OVFL       (0x00000001<<11)
+#define VFE31_IMASK_IMG_MAST_5_BUS_OVFL       (0x00000001<<12)
+#define VFE31_IMASK_IMG_MAST_6_BUS_OVFL       (0x00000001<<13)
+#define VFE31_IMASK_STATS_AE_BUS_OVFL         (0x00000001<<14)
+#define VFE31_IMASK_STATS_AF_BUS_OVFL         (0x00000001<<15)
+#define VFE31_IMASK_STATS_AWB_BUS_OVFL        (0x00000001<<16)
+#define VFE31_IMASK_STATS_RS_BUS_OVFL         (0x00000001<<17)
+#define VFE31_IMASK_STATS_CS_BUS_OVFL         (0x00000001<<18)
+#define VFE31_IMASK_STATS_IHIST_BUS_OVFL      (0x00000001<<19)
+#define VFE31_IMASK_STATS_SKIN_BUS_OVFL       (0x00000001<<20)
+#define VFE31_IMASK_AXI_ERROR                 (0x00000001<<21)
+
+#define VFE_COM_STATUS 0x000FE000
+
+struct vfe31_output_path {
+	uint16_t output_mode;     /* bitmask  */
+
+	struct vfe31_output_ch out0; /* preview and thumbnail */
+	struct vfe31_output_ch out1; /* snapshot */
+	struct vfe31_output_ch out2; /* video    */
+};
+
+struct vfe31_frame_extra {
+	uint32_t greenDefectPixelCount;
+	uint32_t redBlueDefectPixelCount;
+
+	uint32_t  asfMaxEdge;
+	uint32_t  asfHbiCount;
+
+	uint32_t yWrPmStats0;
+	uint32_t yWrPmStats1;
+	uint32_t cbcrWrPmStats0;
+	uint32_t cbcrWrPmStats1;
+
+	uint32_t  frameCounter;
+};
+
+#define VFE_DISABLE_ALL_IRQS             0
+#define VFE_CLEAR_ALL_IRQS               0xffffffff
+
+#define VFE_GLOBAL_RESET                 0x00000004
+#define VFE_CGC_OVERRIDE                 0x0000000C
+#define VFE_MODULE_CFG                   0x00000010
+#define VFE_CFG_OFF                      0x00000014
+#define VFE_IRQ_CMD                      0x00000018
+#define VFE_IRQ_MASK_0                   0x0000001C
+#define VFE_IRQ_MASK_1                   0x00000020
+#define VFE_IRQ_CLEAR_0                  0x00000024
+#define VFE_IRQ_CLEAR_1                  0x00000028
+#define VFE_IRQ_STATUS_0                 0x0000002C
+#define VFE_IRQ_STATUS_1                 0x00000030
+#define VFE_IRQ_COMP_MASK                0x00000034
+#define VFE_BUS_CMD                      0x00000038
+#define VFE_BUS_PING_PONG_STATUS         0x00000180
+#define VFE_BUS_OPERATION_STATUS         0x00000184
+
+#define VFE_BUS_IMAGE_MASTER_0_WR_PM_STATS_0        0x00000190
+#define VFE_BUS_IMAGE_MASTER_0_WR_PM_STATS_1        0x00000194
+
+#define VFE_AXI_CMD                      0x000001D8
+#define VFE_AXI_STATUS                   0x000001DC
+#define VFE_BUS_STATS_PING_PONG_BASE     0x000000F4
+
+#define VFE_BUS_STATS_AEC_WR_PING_ADDR   0x000000F4
+#define VFE_BUS_STATS_AEC_WR_PONG_ADDR   0x000000F8
+#define VFE_BUS_STATS_AEC_UB_CFG         0x000000FC
+#define VFE_BUS_STATS_AF_WR_PING_ADDR    0x00000100
+#define VFE_BUS_STATS_AF_WR_PONG_ADDR    0x00000104
+#define VFE_BUS_STATS_AF_UB_CFG          0x00000108
+#define VFE_BUS_STATS_AWB_WR_PING_ADDR   0x0000010C
+#define VFE_BUS_STATS_AWB_WR_PONG_ADDR   0x00000110
+#define VFE_BUS_STATS_AWB_UB_CFG         0x00000114
+#define VFE_BUS_STATS_RS_WR_PING_ADDR    0x00000118
+#define VFE_BUS_STATS_RS_WR_PONG_ADDR    0x0000011C
+#define VFE_BUS_STATS_RS_UB_CFG          0x00000120
+
+#define VFE_BUS_STATS_CS_WR_PING_ADDR    0x00000124
+#define VFE_BUS_STATS_CS_WR_PONG_ADDR    0x00000128
+#define VFE_BUS_STATS_CS_UB_CFG          0x0000012C
+#define VFE_BUS_STATS_HIST_WR_PING_ADDR  0x00000130
+#define VFE_BUS_STATS_HIST_WR_PONG_ADDR  0x00000134
+#define VFE_BUS_STATS_HIST_UB_CFG        0x00000138
+#define VFE_BUS_STATS_SKIN_WR_PING_ADDR  0x0000013C
+#define VFE_BUS_STATS_SKIN_WR_PONG_ADDR  0x00000140
+#define VFE_BUS_STATS_SKIN_UB_CFG        0x00000144
+#define VFE_BUS_PM_CMD                   0x00000188
+#define VFE_BUS_PM_CFG                   0x0000018C
+#define VFE_CAMIF_COMMAND                0x000001E0
+#define VFE_CAMIF_STATUS                 0x00000204
+#define VFE_REG_UPDATE_CMD               0x00000260
+#define VFE_DEMUX_GAIN_0                 0x00000288
+#define VFE_DEMUX_GAIN_1                 0x0000028C
+#define VFE_CHROMA_UP                    0x0000035C
+#define VFE_FRAMEDROP_ENC_Y_CFG          0x00000504
+#define VFE_FRAMEDROP_ENC_CBCR_CFG       0x00000508
+#define VFE_FRAMEDROP_ENC_Y_PATTERN      0x0000050C
+#define VFE_FRAMEDROP_ENC_CBCR_PATTERN   0x00000510
+#define VFE_FRAMEDROP_VIEW_Y             0x00000514
+#define VFE_FRAMEDROP_VIEW_CBCR          0x00000518
+#define VFE_FRAMEDROP_VIEW_Y_PATTERN     0x0000051C
+#define VFE_FRAMEDROP_VIEW_CBCR_PATTERN  0x00000520
+#define VFE_CLAMP_MAX                    0x00000524
+#define VFE_CLAMP_MIN                    0x00000528
+#define VFE_REALIGN_BUF                  0x0000052C
+#define VFE_STATS_CFG                    0x00000530
+#define VFE_STATS_AWB_SGW_CFG            0x00000554
+#define VFE_DMI_CFG                      0x00000598
+#define VFE_DMI_ADDR                     0x0000059C
+#define VFE_DMI_DATA_LO                  0x000005A4
+#define VFE_AXI_CFG                      0x00000600
+
+struct vfe_stats_control {
+	uint8_t  ackPending;
+	uint32_t nextFrameAddrBuf;
+	uint32_t droppedStatsFrameCount;
+	uint32_t bufToRender;
+};
+
+struct vfe31_ctrl_type {
+	uint16_t operation_mode;     /* streaming or snapshot */
+	struct vfe31_output_path outpath;
+
+	uint32_t vfeImaskCompositePacked;
+
+	spinlock_t  update_ack_lock;
+	spinlock_t  io_lock;
+
+	int8_t aec_ack_pending;
+	int8_t awb_ack_pending;
+	int8_t af_ack_pending;
+	int8_t ihist_ack_pending;
+	int8_t rs_ack_pending;
+	int8_t cs_ack_pending;
+
+	struct msm_vfe_callback *resp;
+	uint32_t extlen;
+	void *extdata;
+
+	int8_t start_ack_pending;
+	atomic_t stop_ack_pending;
+	int8_t reset_ack_pending;
+	int8_t update_ack_pending;
+	enum vfe_recording_state recording_state;
+	int8_t output0_available;
+	int8_t output1_available;
+	int8_t update_gamma;
+	int8_t update_luma;
+	spinlock_t  tasklet_lock;
+	struct list_head tasklet_q;
+	int vfeirq;
+	void __iomem *vfebase;
+	void *syncdata;
+
+	struct resource	*vfemem;
+	struct resource *vfeio;
+
+	uint32_t stats_comp;
+	uint32_t hfr_mode;
+	atomic_t vstate;
+	uint32_t vfe_capture_count;
+	uint32_t sync_timer_repeat_count;
+	uint32_t sync_timer_state;
+	uint32_t sync_timer_number;
+
+	uint32_t vfeFrameId;
+	uint32_t output1Pattern;
+	uint32_t output1Period;
+	uint32_t output2Pattern;
+	uint32_t output2Period;
+	uint32_t vfeFrameSkipCount;
+	uint32_t vfeFrameSkipPeriod;
+	uint32_t status_bits;
+	struct vfe_stats_control afStatsControl;
+	struct vfe_stats_control awbStatsControl;
+	struct vfe_stats_control aecStatsControl;
+	struct vfe_stats_control ihistStatsControl;
+	struct vfe_stats_control rsStatsControl;
+	struct vfe_stats_control csStatsControl;
+	struct msm_camera_sensor_info *s_info;
+	struct vfe_message vMsgHold_Snap;
+	struct vfe_message vMsgHold_Thumb;
+	int8_t xbar_update_pending;
+	uint32_t xbar_cfg[2];
+	spinlock_t xbar_lock;
+	uint32_t while_stopping_mask;
+};
+
+#define statsAeNum      0
+#define statsAfNum      1
+#define statsAwbNum     2
+#define statsRsNum      3
+#define statsCsNum      4
+#define statsIhistNum   5
+#define statsSkinNum    6
+
+struct vfe_cmd_stats_ack{
+  uint32_t  nextStatsBuf;
+};
+
+#define VFE_STATS_BUFFER_COUNT            3
+
+struct vfe_cmd_stats_buf{
+   uint32_t statsBuf[VFE_STATS_BUFFER_COUNT];
+};
+#endif /* __MSM_VFE31_H__ */
diff --git a/drivers/media/video/msm/msm_vfe31_v4l2.c b/drivers/media/video/msm/msm_vfe31_v4l2.c
new file mode 100644
index 0000000..be8d84b
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe31_v4l2.c
@@ -0,0 +1,3917 @@
+/* Copyright (c) 2012 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/uaccess.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/atomic.h>
+#include <linux/regulator/consumer.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <mach/clk.h>
+#include <mach/irqs.h>
+#include <mach/camera.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/msm_isp.h>
+
+#include "msm.h"
+#include "msm_vfe31_v4l2.h"
+
+atomic_t irq_cnt;
+
+#define BUFF_SIZE_128 128
+
+#define VFE31_AXI_OFFSET 0x0050
+#define vfe31_get_ch_ping_addr(chn) \
+	(msm_camera_io_r(vfe31_ctrl->vfebase + 0x0050 + 0x18 * (chn)))
+#define vfe31_get_ch_pong_addr(chn) \
+	(msm_camera_io_r(vfe31_ctrl->vfebase + 0x0050 + 0x18 * (chn) + 4))
+#define vfe31_get_ch_addr(ping_pong, chn) \
+	(((ping_pong) & (1 << (chn))) == 0 ? \
+	vfe31_get_ch_pong_addr(chn) : vfe31_get_ch_ping_addr(chn))
+
+#define vfe31_put_ch_ping_addr(chn, addr) \
+	(msm_camera_io_w((addr), vfe31_ctrl->vfebase + 0x0050 + 0x18 * (chn)))
+#define vfe31_put_ch_pong_addr(chn, addr) \
+	(msm_camera_io_w((addr), \
+	vfe31_ctrl->vfebase + 0x0050 + 0x18 * (chn) + 4))
+#define vfe31_put_ch_addr(ping_pong, chn, addr) \
+	(((ping_pong) & (1 << (chn))) == 0 ?   \
+	vfe31_put_ch_pong_addr((chn), (addr)) : \
+	vfe31_put_ch_ping_addr((chn), (addr)))
+
+#define VFE_CLK_RATE	153600000
+#define CAMIF_CFG_RMSK             0x1fffff
+
+static struct vfe31_ctrl_type *vfe31_ctrl;
+static uint32_t vfe_clk_rate;
+
+struct vfe31_isr_queue_cmd {
+	struct list_head	list;
+	uint32_t		vfeInterruptStatus0;
+	uint32_t		vfeInterruptStatus1;
+};
+
+/*TODO: Why is V32 reference in arch/arm/mach-msm/include/mach/camera.h?*/
+#define VFE_MSG_V31_START VFE_MSG_V32_START
+#define VFE_MSG_V31_CAPTURE VFE_MSG_V32_CAPTURE
+#define VFE_MSG_V31_JPEG_CAPTURE VFE_MSG_V32_JPEG_CAPTURE
+#define VFE_MSG_V31_START_RECORDING VFE_MSG_V32_START_RECORDING
+
+static struct vfe31_cmd_type vfe31_cmd[] = {
+/* 0*/	{VFE_CMD_DUMMY_0},
+		{VFE_CMD_SET_CLK},
+		{VFE_CMD_RESET},
+		{VFE_CMD_START},
+		{VFE_CMD_TEST_GEN_START},
+/* 5*/	{VFE_CMD_OPERATION_CFG, V31_OPERATION_CFG_LEN},
+		{VFE_CMD_AXI_OUT_CFG, V31_AXI_OUT_LEN, V31_AXI_OUT_OFF, 0xFF},
+		{VFE_CMD_CAMIF_CFG, V31_CAMIF_LEN, V31_CAMIF_OFF, 0xFF},
+		{VFE_CMD_AXI_INPUT_CFG},
+		{VFE_CMD_BLACK_LEVEL_CFG, V31_BLACK_LEVEL_LEN,
+		V31_BLACK_LEVEL_OFF,
+		0xFF},
+/*10*/  {VFE_CMD_MESH_ROLL_OFF_CFG, V31_MESH_ROLL_OFF_CFG_LEN,
+		V31_MESH_ROLL_OFF_CFG_OFF, 0xFF},
+		{VFE_CMD_DEMUX_CFG, V31_DEMUX_LEN, V31_DEMUX_OFF, 0xFF},
+		{VFE_CMD_FOV_CFG, V31_FOV_LEN, V31_FOV_OFF, 0xFF},
+		{VFE_CMD_MAIN_SCALER_CFG, V31_MAIN_SCALER_LEN,
+		V31_MAIN_SCALER_OFF, 0xFF},
+		{VFE_CMD_WB_CFG, V31_WB_LEN, V31_WB_OFF, 0xFF},
+/*15*/	{VFE_CMD_COLOR_COR_CFG, V31_COLOR_COR_LEN, V31_COLOR_COR_OFF, 0xFF},
+		{VFE_CMD_RGB_G_CFG, V31_RGB_G_LEN, V31_RGB_G_OFF, 0xFF},
+		{VFE_CMD_LA_CFG, V31_LA_LEN, V31_LA_OFF, 0xFF },
+		{VFE_CMD_CHROMA_EN_CFG, V31_CHROMA_EN_LEN, V31_CHROMA_EN_OFF,
+		0xFF},
+		{VFE_CMD_CHROMA_SUP_CFG, V31_CHROMA_SUP_LEN, V31_CHROMA_SUP_OFF,
+		0xFF},
+/*20*/	{VFE_CMD_MCE_CFG, V31_MCE_LEN, V31_MCE_OFF, 0xFF},
+		{VFE_CMD_SK_ENHAN_CFG, V31_SCE_LEN, V31_SCE_OFF, 0xFF},
+		{VFE_CMD_ASF_CFG, V31_ASF_LEN, V31_ASF_OFF, 0xFF},
+		{VFE_CMD_S2Y_CFG, V31_S2Y_LEN, V31_S2Y_OFF, 0xFF},
+		{VFE_CMD_S2CbCr_CFG, V31_S2CbCr_LEN, V31_S2CbCr_OFF, 0xFF},
+/*25*/	{VFE_CMD_CHROMA_SUBS_CFG, V31_CHROMA_SUBS_LEN, V31_CHROMA_SUBS_OFF,
+		0xFF},
+		{VFE_CMD_OUT_CLAMP_CFG, V31_OUT_CLAMP_LEN, V31_OUT_CLAMP_OFF,
+		0xFF},
+		{VFE_CMD_FRAME_SKIP_CFG, V31_FRAME_SKIP_LEN, V31_FRAME_SKIP_OFF,
+		0xFF},
+		{VFE_CMD_DUMMY_1},
+		{VFE_CMD_DUMMY_2},
+/*30*/	{VFE_CMD_DUMMY_3},
+		{VFE_CMD_UPDATE},
+		{VFE_CMD_BL_LVL_UPDATE, V31_BLACK_LEVEL_LEN,
+		V31_BLACK_LEVEL_OFF, 0xFF},
+		{VFE_CMD_DEMUX_UPDATE, V31_DEMUX_LEN, V31_DEMUX_OFF, 0xFF},
+		{VFE_CMD_FOV_UPDATE, V31_FOV_LEN, V31_FOV_OFF, 0xFF},
+/*35*/	{VFE_CMD_MAIN_SCALER_UPDATE, V31_MAIN_SCALER_LEN, V31_MAIN_SCALER_OFF,
+		0xFF},
+		{VFE_CMD_WB_UPDATE, V31_WB_LEN, V31_WB_OFF, 0xFF},
+		{VFE_CMD_COLOR_COR_UPDATE, V31_COLOR_COR_LEN, V31_COLOR_COR_OFF,
+		0xFF},
+		{VFE_CMD_RGB_G_UPDATE, V31_RGB_G_LEN, V31_CHROMA_EN_OFF, 0xFF},
+		{VFE_CMD_LA_UPDATE, V31_LA_LEN, V31_LA_OFF, 0xFF },
+/*40*/	{VFE_CMD_CHROMA_EN_UPDATE, V31_CHROMA_EN_LEN, V31_CHROMA_EN_OFF,
+		0xFF},
+		{VFE_CMD_CHROMA_SUP_UPDATE, V31_CHROMA_SUP_LEN,
+		V31_CHROMA_SUP_OFF, 0xFF},
+		{VFE_CMD_MCE_UPDATE, V31_MCE_LEN, V31_MCE_OFF, 0xFF},
+		{VFE_CMD_SK_ENHAN_UPDATE, V31_SCE_LEN, V31_SCE_OFF, 0xFF},
+		{VFE_CMD_S2CbCr_UPDATE, V31_S2CbCr_LEN, V31_S2CbCr_OFF, 0xFF},
+/*45*/	{VFE_CMD_S2Y_UPDATE, V31_S2Y_LEN, V31_S2Y_OFF, 0xFF},
+		{VFE_CMD_ASF_UPDATE, V31_ASF_UPDATE_LEN, V31_ASF_OFF, 0xFF},
+		{VFE_CMD_FRAME_SKIP_UPDATE},
+		{VFE_CMD_CAMIF_FRAME_UPDATE},
+		{VFE_CMD_STATS_AF_UPDATE, V31_STATS_AF_LEN, V31_STATS_AF_OFF},
+/*50*/	{VFE_CMD_STATS_AE_UPDATE, V31_STATS_AE_LEN, V31_STATS_AE_OFF},
+		{VFE_CMD_STATS_AWB_UPDATE, V31_STATS_AWB_LEN,
+		V31_STATS_AWB_OFF},
+		{VFE_CMD_STATS_RS_UPDATE, V31_STATS_RS_LEN, V31_STATS_RS_OFF},
+		{VFE_CMD_STATS_CS_UPDATE, V31_STATS_CS_LEN, V31_STATS_CS_OFF},
+		{VFE_CMD_STATS_SKIN_UPDATE},
+/*55*/	{VFE_CMD_STATS_IHIST_UPDATE, V31_STATS_IHIST_LEN, V31_STATS_IHIST_OFF},
+		{VFE_CMD_DUMMY_4},
+		{VFE_CMD_EPOCH1_ACK},
+		{VFE_CMD_EPOCH2_ACK},
+		{VFE_CMD_START_RECORDING},
+/*60*/	{VFE_CMD_STOP_RECORDING},
+		{VFE_CMD_DUMMY_5},
+		{VFE_CMD_DUMMY_6},
+		{VFE_CMD_CAPTURE, V31_CAPTURE_LEN, 0xFF},
+		{VFE_CMD_DUMMY_7},
+/*65*/	{VFE_CMD_STOP},
+		{VFE_CMD_GET_HW_VERSION, V31_GET_HW_VERSION_LEN,
+		V31_GET_HW_VERSION_OFF},
+		{VFE_CMD_GET_FRAME_SKIP_COUNTS},
+		{VFE_CMD_OUTPUT1_BUFFER_ENQ},
+		{VFE_CMD_OUTPUT2_BUFFER_ENQ},
+/*70*/	{VFE_CMD_OUTPUT3_BUFFER_ENQ},
+		{VFE_CMD_JPEG_OUT_BUF_ENQ},
+		{VFE_CMD_RAW_OUT_BUF_ENQ},
+		{VFE_CMD_RAW_IN_BUF_ENQ},
+		{VFE_CMD_STATS_AF_ENQ},
+/*75*/	{VFE_CMD_STATS_AE_ENQ},
+		{VFE_CMD_STATS_AWB_ENQ},
+		{VFE_CMD_STATS_RS_ENQ},
+		{VFE_CMD_STATS_CS_ENQ},
+		{VFE_CMD_STATS_SKIN_ENQ},
+/*80*/	{VFE_CMD_STATS_IHIST_ENQ},
+		{VFE_CMD_DUMMY_8},
+		{VFE_CMD_JPEG_ENC_CFG},
+		{VFE_CMD_DUMMY_9},
+		{VFE_CMD_STATS_AF_START, V31_STATS_AF_LEN, V31_STATS_AF_OFF},
+/*85*/	{VFE_CMD_STATS_AF_STOP},
+		{VFE_CMD_STATS_AE_START, V31_STATS_AE_LEN, V31_STATS_AE_OFF},
+		{VFE_CMD_STATS_AE_STOP},
+		{VFE_CMD_STATS_AWB_START, V31_STATS_AWB_LEN, V31_STATS_AWB_OFF},
+		{VFE_CMD_STATS_AWB_STOP},
+/*90*/	{VFE_CMD_STATS_RS_START, V31_STATS_RS_LEN, V31_STATS_RS_OFF},
+		{VFE_CMD_STATS_RS_STOP},
+		{VFE_CMD_STATS_CS_START, V31_STATS_CS_LEN, V31_STATS_CS_OFF},
+		{VFE_CMD_STATS_CS_STOP},
+		{VFE_CMD_STATS_SKIN_START},
+/*95*/	{VFE_CMD_STATS_SKIN_STOP},
+		{VFE_CMD_STATS_IHIST_START,
+		V31_STATS_IHIST_LEN, V31_STATS_IHIST_OFF},
+		{VFE_CMD_STATS_IHIST_STOP},
+		{VFE_CMD_DUMMY_10},
+		{VFE_CMD_SYNC_TIMER_SETTING, V31_SYNC_TIMER_LEN,
+			V31_SYNC_TIMER_OFF},
+/*100*/	{VFE_CMD_ASYNC_TIMER_SETTING, V31_ASYNC_TIMER_LEN, V31_ASYNC_TIMER_OFF},
+		{VFE_CMD_LIVESHOT},
+		{VFE_CMD_LA_SETUP},
+		{VFE_CMD_LINEARIZATION_CFG},
+		{VFE_CMD_DEMOSAICV3},
+/*105*/	{VFE_CMD_DEMOSAICV3_ABCC_CFG},
+	{VFE_CMD_DEMOSAICV3_DBCC_CFG},
+		{VFE_CMD_DEMOSAICV3_DBPC_CFG, V31_DEMOSAICV3_DBPC_LEN,
+			V31_DEMOSAICV3_DBPC_CFG_OFF},
+		{VFE_CMD_DEMOSAICV3_ABF_CFG, V31_DEMOSAICV3_ABF_LEN,
+			V31_DEMOSAICV3_ABF_OFF},
+		{VFE_CMD_DEMOSAICV3_ABCC_UPDATE},
+/*110*/	{VFE_CMD_DEMOSAICV3_DBCC_UPDATE},
+		{VFE_CMD_DEMOSAICV3_DBPC_UPDATE, V31_DEMOSAICV3_DBPC_LEN,
+			V31_DEMOSAICV3_DBPC_CFG_OFF},
+		{VFE_CMD_XBAR_CFG},
+		{VFE_CMD_MODULE_CFG, V31_MODULE_CFG_LEN, V31_MODULE_CFG_OFF},
+		{VFE_CMD_ZSL},
+/*115*/	{VFE_CMD_LINEARIZATION_UPDATE},
+		{VFE_CMD_DEMOSAICV3_ABF_UPDATE, V31_DEMOSAICV3_ABF_LEN,
+			V31_DEMOSAICV3_ABF_OFF},
+		{VFE_CMD_CLF_CFG},
+		{VFE_CMD_CLF_LUMA_UPDATE},
+		{VFE_CMD_CLF_CHROMA_UPDATE},
+/*120*/ {VFE_CMD_PCA_ROLL_OFF_CFG},
+		{VFE_CMD_PCA_ROLL_OFF_UPDATE},
+		{VFE_CMD_GET_REG_DUMP},
+		{VFE_CMD_GET_LINEARIZATON_TABLE},
+		{VFE_CMD_GET_MESH_ROLLOFF_TABLE},
+/*125*/ {VFE_CMD_GET_PCA_ROLLOFF_TABLE},
+		{VFE_CMD_GET_RGB_G_TABLE},
+		{VFE_CMD_GET_LA_TABLE},
+		{VFE_CMD_DEMOSAICV3_UPDATE},
+};
+
+uint32_t vfe31_AXI_WM_CFG[] = {
+	0x0000004C,
+	0x00000064,
+	0x0000007C,
+	0x00000094,
+	0x000000AC,
+	0x000000C4,
+	0x000000DC,
+};
+
+static const char * const vfe31_general_cmd[] = {
+	"DUMMY_0",  /* 0 */
+	"SET_CLK",
+	"RESET",
+	"START",
+	"TEST_GEN_START",
+	"OPERATION_CFG",  /* 5 */
+	"AXI_OUT_CFG",
+	"CAMIF_CFG",
+	"AXI_INPUT_CFG",
+	"BLACK_LEVEL_CFG",
+	"ROLL_OFF_CFG",  /* 10 */
+	"DEMUX_CFG",
+	"FOV_CFG",
+	"MAIN_SCALER_CFG",
+	"WB_CFG",
+	"COLOR_COR_CFG", /* 15 */
+	"RGB_G_CFG",
+	"LA_CFG",
+	"CHROMA_EN_CFG",
+	"CHROMA_SUP_CFG",
+	"MCE_CFG", /* 20 */
+	"SK_ENHAN_CFG",
+	"ASF_CFG",
+	"S2Y_CFG",
+	"S2CbCr_CFG",
+	"CHROMA_SUBS_CFG",  /* 25 */
+	"OUT_CLAMP_CFG",
+	"FRAME_SKIP_CFG",
+	"DUMMY_1",
+	"DUMMY_2",
+	"DUMMY_3",  /* 30 */
+	"UPDATE",
+	"BL_LVL_UPDATE",
+	"DEMUX_UPDATE",
+	"FOV_UPDATE",
+	"MAIN_SCALER_UPDATE",  /* 35 */
+	"WB_UPDATE",
+	"COLOR_COR_UPDATE",
+	"RGB_G_UPDATE",
+	"LA_UPDATE",
+	"CHROMA_EN_UPDATE",  /* 40 */
+	"CHROMA_SUP_UPDATE",
+	"MCE_UPDATE",
+	"SK_ENHAN_UPDATE",
+	"S2CbCr_UPDATE",
+	"S2Y_UPDATE",  /* 45 */
+	"ASF_UPDATE",
+	"FRAME_SKIP_UPDATE",
+	"CAMIF_FRAME_UPDATE",
+	"STATS_AF_UPDATE",
+	"STATS_AE_UPDATE",  /* 50 */
+	"STATS_AWB_UPDATE",
+	"STATS_RS_UPDATE",
+	"STATS_CS_UPDATE",
+	"STATS_SKIN_UPDATE",
+	"STATS_IHIST_UPDATE",  /* 55 */
+	"DUMMY_4",
+	"EPOCH1_ACK",
+	"EPOCH2_ACK",
+	"START_RECORDING",
+	"STOP_RECORDING",  /* 60 */
+	"DUMMY_5",
+	"DUMMY_6",
+	"CAPTURE",
+	"DUMMY_7",
+	"STOP",  /* 65 */
+	"GET_HW_VERSION",
+	"GET_FRAME_SKIP_COUNTS",
+	"OUTPUT1_BUFFER_ENQ",
+	"OUTPUT2_BUFFER_ENQ",
+	"OUTPUT3_BUFFER_ENQ",  /* 70 */
+	"JPEG_OUT_BUF_ENQ",
+	"RAW_OUT_BUF_ENQ",
+	"RAW_IN_BUF_ENQ",
+	"STATS_AF_ENQ",
+	"STATS_AE_ENQ",  /* 75 */
+	"STATS_AWB_ENQ",
+	"STATS_RS_ENQ",
+	"STATS_CS_ENQ",
+	"STATS_SKIN_ENQ",
+	"STATS_IHIST_ENQ",  /* 80 */
+	"DUMMY_8",
+	"JPEG_ENC_CFG",
+	"DUMMY_9",
+	"STATS_AF_START",
+	"STATS_AF_STOP",  /* 85 */
+	"STATS_AE_START",
+	"STATS_AE_STOP",
+	"STATS_AWB_START",
+	"STATS_AWB_STOP",
+	"STATS_RS_START",  /* 90 */
+	"STATS_RS_STOP",
+	"STATS_CS_START",
+	"STATS_CS_STOP",
+	"STATS_SKIN_START",
+	"STATS_SKIN_STOP",  /* 95 */
+	"STATS_IHIST_START",
+	"STATS_IHIST_STOP",
+	"DUMMY_10",
+	"SYNC_TIMER_SETTING",
+	"ASYNC_TIMER_SETTING",  /* 100 */
+	"LIVESHOT",
+	"LA_SETUP",
+	"LINEARIZATION_CFG",
+	"DEMOSAICV3",
+	"DEMOSAICV3_ABCC_CFG", /* 105 */
+	"DEMOSAICV3_DBCC_CFG",
+	"DEMOSAICV3_DBPC_CFG",
+	"DEMOSAICV3_ABF_CFG",
+	"DEMOSAICV3_ABCC_UPDATE",
+	"DEMOSAICV3_DBCC_UPDATE", /* 110 */
+	"DEMOSAICV3_DBPC_UPDATE",
+	"XBAR_CFG",
+	"EZTUNE_CFG",
+	"V31_ZSL",
+	"LINEARIZATION_UPDATE", /*115*/
+	"DEMOSAICV3_ABF_UPDATE",
+	"CLF_CFG",
+	"CLF_LUMA_UPDATE",
+	"CLF_CHROMA_UPDATE",
+	"PCA_ROLL_OFF_CFG", /*120*/
+	"PCA_ROLL_OFF_UPDATE",
+	"GET_REG_DUMP",
+	"GET_LINEARIZATON_TABLE",
+	"GET_MESH_ROLLOFF_TABLE",
+	"GET_PCA_ROLLOFF_TABLE", /*125*/
+	"GET_RGB_G_TABLE",
+	"GET_LA_TABLE",
+	"DEMOSAICV3_UPDATE",
+};
+
+static void vfe31_stop(void)
+{
+
+	uint8_t  axiBusyFlag = true;
+	unsigned long flags;
+
+	atomic_set(&vfe31_ctrl->vstate, 0);
+
+	/* for reset hw modules, and send msg when reset_irq comes.*/
+	spin_lock_irqsave(&vfe31_ctrl->stop_flag_lock, flags);
+	vfe31_ctrl->stop_ack_pending = TRUE;
+	spin_unlock_irqrestore(&vfe31_ctrl->stop_flag_lock, flags);
+
+	/* disable all interrupts.  */
+	msm_camera_io_w(VFE_DISABLE_ALL_IRQS,
+		vfe31_ctrl->vfebase + VFE_IRQ_MASK_0);
+	msm_camera_io_w(VFE_DISABLE_ALL_IRQS,
+		vfe31_ctrl->vfebase + VFE_IRQ_MASK_1);
+
+	/* clear all pending interrupts*/
+	msm_camera_io_w(VFE_CLEAR_ALL_IRQS,
+		vfe31_ctrl->vfebase + VFE_IRQ_CLEAR_0);
+	msm_camera_io_w(VFE_CLEAR_ALL_IRQS,
+		vfe31_ctrl->vfebase + VFE_IRQ_CLEAR_1);
+	/* Ensure the write order while writing
+	to the command register using the barrier */
+	msm_camera_io_w_mb(1,
+		vfe31_ctrl->vfebase + VFE_IRQ_CMD);
+
+	/* in either continuous or snapshot mode, stop command can be issued
+	 * at any time. stop camif immediately. */
+	msm_camera_io_w_mb(CAMIF_COMMAND_STOP_IMMEDIATELY,
+		vfe31_ctrl->vfebase + VFE_CAMIF_COMMAND);
+
+	/* axi halt command. */
+	msm_camera_io_w(AXI_HALT,
+		vfe31_ctrl->vfebase + VFE_AXI_CMD);
+	wmb();
+	while (axiBusyFlag) {
+		if (msm_camera_io_r(vfe31_ctrl->vfebase + VFE_AXI_STATUS) & 0x1)
+			axiBusyFlag = false;
+	}
+	/* Ensure the write order while writing
+	to the command register using the barrier */
+	msm_camera_io_w_mb(AXI_HALT_CLEAR,
+		vfe31_ctrl->vfebase + VFE_AXI_CMD);
+
+	/* now enable only halt_irq & reset_irq */
+	msm_camera_io_w(0xf0000000,          /* this is for async timer. */
+		vfe31_ctrl->vfebase + VFE_IRQ_MASK_0);
+	msm_camera_io_w(VFE_IMASK_WHILE_STOPPING_1,
+		vfe31_ctrl->vfebase + VFE_IRQ_MASK_1);
+
+	msm_camera_io_w_mb(VFE_RESET_UPON_STOP_CMD,
+		vfe31_ctrl->vfebase + VFE_GLOBAL_RESET);
+}
+
+static void vfe31_subdev_notify(int id, int path)
+{
+	struct msm_vfe_resp rp;
+	unsigned long flags;
+	spin_lock_irqsave(&vfe31_ctrl->sd_notify_lock, flags);
+	memset(&rp, 0, sizeof(struct msm_vfe_resp));
+	CDBG("vfe31_subdev_notify : msgId = %d\n", id);
+	rp.evt_msg.type   = MSM_CAMERA_MSG;
+	rp.evt_msg.msg_id = path;
+	rp.type	   = id;
+	v4l2_subdev_notify(&vfe31_ctrl->subdev, NOTIFY_VFE_BUF_EVT, &rp);
+	spin_unlock_irqrestore(&vfe31_ctrl->sd_notify_lock, flags);
+}
+
+static int vfe31_config_axi(int mode, uint32_t *ao)
+{
+	uint32_t *ch_info;
+	uint32_t *axi_cfg = ao+V31_AXI_RESERVED;
+	/* Update the corresponding write masters for each output*/
+	ch_info = axi_cfg + V31_AXI_CFG_LEN;
+	vfe31_ctrl->outpath.out0.ch0 = 0x0000FFFF & *ch_info;
+	vfe31_ctrl->outpath.out0.ch1 = 0x0000FFFF & (*ch_info++ >> 16);
+	vfe31_ctrl->outpath.out0.ch2 = 0x0000FFFF & *ch_info++;
+	vfe31_ctrl->outpath.out1.ch0 = 0x0000FFFF & *ch_info;
+	vfe31_ctrl->outpath.out1.ch1 = 0x0000FFFF & (*ch_info++ >> 16);
+	vfe31_ctrl->outpath.out1.ch2 = 0x0000FFFF & *ch_info++;
+	vfe31_ctrl->outpath.out2.ch0 = 0x0000FFFF & *ch_info;
+	vfe31_ctrl->outpath.out2.ch1 = 0x0000FFFF & (*ch_info++ >> 16);
+	vfe31_ctrl->outpath.out2.ch2 = 0x0000FFFF & *ch_info++;
+
+	switch (mode) {
+	case OUTPUT_PRIM:
+		vfe31_ctrl->outpath.output_mode =
+			VFE31_OUTPUT_MODE_PRIMARY;
+		break;
+	case OUTPUT_PRIM_ALL_CHNLS:
+		vfe31_ctrl->outpath.output_mode =
+			VFE31_OUTPUT_MODE_PRIMARY_ALL_CHNLS;
+		break;
+	case OUTPUT_PRIM|OUTPUT_SEC:
+		vfe31_ctrl->outpath.output_mode =
+			VFE31_OUTPUT_MODE_PRIMARY;
+		vfe31_ctrl->outpath.output_mode |=
+			VFE31_OUTPUT_MODE_SECONDARY;
+		break;
+	case OUTPUT_PRIM|OUTPUT_SEC_ALL_CHNLS:
+		vfe31_ctrl->outpath.output_mode =
+			VFE31_OUTPUT_MODE_PRIMARY;
+		vfe31_ctrl->outpath.output_mode |=
+			VFE31_OUTPUT_MODE_SECONDARY_ALL_CHNLS;
+		break;
+	case OUTPUT_PRIM_ALL_CHNLS|OUTPUT_SEC:
+		vfe31_ctrl->outpath.output_mode =
+			VFE31_OUTPUT_MODE_PRIMARY_ALL_CHNLS;
+		vfe31_ctrl->outpath.output_mode |=
+			VFE31_OUTPUT_MODE_SECONDARY;
+		break;
+	default:
+		pr_err("%s Invalid AXI mode %d ", __func__, mode);
+		return -EINVAL;
+	}
+
+	msm_camera_io_memcpy(vfe31_ctrl->vfebase +
+		vfe31_cmd[VFE_CMD_AXI_OUT_CFG].offset, axi_cfg,
+		vfe31_cmd[VFE_CMD_AXI_OUT_CFG].length - V31_AXI_CH_INF_LEN -
+			V31_AXI_RESERVED);
+	return 0;
+}
+
+static void vfe31_reset_internal_variables(void)
+{
+	unsigned long flags;
+	vfe31_ctrl->vfeImaskCompositePacked = 0;
+	/* state control variables */
+	vfe31_ctrl->start_ack_pending = FALSE;
+	atomic_set(&irq_cnt, 0);
+
+	spin_lock_irqsave(&vfe31_ctrl->stop_flag_lock, flags);
+	vfe31_ctrl->stop_ack_pending  = FALSE;
+	spin_unlock_irqrestore(&vfe31_ctrl->stop_flag_lock, flags);
+
+	vfe31_ctrl->reset_ack_pending  = FALSE;
+
+	spin_lock_irqsave(&vfe31_ctrl->update_ack_lock, flags);
+	vfe31_ctrl->update_ack_pending = FALSE;
+	spin_unlock_irqrestore(&vfe31_ctrl->update_ack_lock, flags);
+
+	vfe31_ctrl->recording_state = VFE_STATE_IDLE;
+	vfe31_ctrl->liveshot_state = VFE_STATE_IDLE;
+
+	atomic_set(&vfe31_ctrl->vstate, 0);
+
+	/* 0 for continuous mode, 1 for snapshot mode */
+	vfe31_ctrl->operation_mode = 0;
+	vfe31_ctrl->outpath.output_mode = 0;
+	vfe31_ctrl->vfe_capture_count = 0;
+
+	/* this is unsigned 32 bit integer. */
+	vfe31_ctrl->vfeFrameId = 0;
+	/* Stats control variables. */
+	memset(&(vfe31_ctrl->afStatsControl), 0,
+		sizeof(struct vfe_stats_control));
+
+	memset(&(vfe31_ctrl->awbStatsControl), 0,
+		sizeof(struct vfe_stats_control));
+
+	memset(&(vfe31_ctrl->aecStatsControl), 0,
+		sizeof(struct vfe_stats_control));
+
+	memset(&(vfe31_ctrl->ihistStatsControl), 0,
+		sizeof(struct vfe_stats_control));
+
+	memset(&(vfe31_ctrl->rsStatsControl), 0,
+		sizeof(struct vfe_stats_control));
+
+	memset(&(vfe31_ctrl->csStatsControl), 0,
+		sizeof(struct vfe_stats_control));
+
+	vfe31_ctrl->frame_skip_cnt = 31;
+	vfe31_ctrl->frame_skip_pattern = 0xffffffff;
+	vfe31_ctrl->snapshot_frame_cnt = 0;
+}
+
+static void vfe31_reset(void)
+{
+	vfe31_reset_internal_variables();
+	/* disable all interrupts.  vfeImaskLocal is also reset to 0
+	* to begin with. */
+	msm_camera_io_w(VFE_DISABLE_ALL_IRQS,
+		vfe31_ctrl->vfebase + VFE_IRQ_MASK_0);
+
+	msm_camera_io_w(VFE_DISABLE_ALL_IRQS,
+		vfe31_ctrl->vfebase + VFE_IRQ_MASK_1);
+
+	/* clear all pending interrupts*/
+	msm_camera_io_w(VFE_CLEAR_ALL_IRQS,
+		vfe31_ctrl->vfebase + VFE_IRQ_CLEAR_0);
+	msm_camera_io_w(VFE_CLEAR_ALL_IRQS,
+		vfe31_ctrl->vfebase + VFE_IRQ_CLEAR_1);
+
+	/* Ensure the write order while writing
+	to the command register using the barrier */
+	msm_camera_io_w_mb(1, vfe31_ctrl->vfebase + VFE_IRQ_CMD);
+
+	/* enable reset_ack interrupt.  */
+	msm_camera_io_w(VFE_IMASK_WHILE_STOPPING_1,
+	vfe31_ctrl->vfebase + VFE_IRQ_MASK_1);
+
+	/* Write to VFE_GLOBAL_RESET_CMD to reset the vfe hardware. Once reset
+	 * is done, hardware interrupt will be generated.  VFE ist processes
+	 * the interrupt to complete the function call.  Note that the reset
+	 * function is synchronous. */
+
+	/* Ensure the write order while writing
+	to the command register using the barrier */
+	msm_camera_io_w_mb(VFE_RESET_UPON_RESET_CMD,
+		vfe31_ctrl->vfebase + VFE_GLOBAL_RESET);
+}
+
+static int vfe31_operation_config(uint32_t *cmd)
+{
+	uint32_t *p = cmd;
+
+	vfe31_ctrl->operation_mode = *p;
+	vfe31_ctrl->stats_comp = *(++p);
+	vfe31_ctrl->hfr_mode = *(++p);
+
+	msm_camera_io_w(*(++p), vfe31_ctrl->vfebase + VFE_CFG);
+	msm_camera_io_w(*(++p), vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+	msm_camera_io_w(*(++p), vfe31_ctrl->vfebase + VFE_REALIGN_BUF);
+	msm_camera_io_w(*(++p), vfe31_ctrl->vfebase + VFE_CHROMA_UP);
+	msm_camera_io_w(*(++p), vfe31_ctrl->vfebase + VFE_STATS_CFG);
+	return 0;
+}
+
+static uint32_t vfe_stats_awb_buf_init(struct vfe_cmd_stats_buf *in)
+{
+	uint32_t *ptr = in->statsBuf;
+	uint32_t addr;
+
+	addr = ptr[0];
+	msm_camera_io_w(addr,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PING_ADDR);
+	addr = ptr[1];
+	msm_camera_io_w(addr,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PONG_ADDR);
+	vfe31_ctrl->awbStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+	return 0;
+}
+
+static uint32_t vfe_stats_aec_buf_init(struct vfe_cmd_stats_buf *in)
+{
+	uint32_t *ptr = in->statsBuf;
+	uint32_t addr;
+
+	addr = ptr[0];
+	msm_camera_io_w(addr,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_AEC_WR_PING_ADDR);
+	addr = ptr[1];
+	msm_camera_io_w(addr,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_AEC_WR_PONG_ADDR);
+
+	vfe31_ctrl->aecStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+	return 0;
+}
+
+static uint32_t vfe_stats_af_buf_init(struct vfe_cmd_stats_buf *in)
+{
+	uint32_t *ptr = in->statsBuf;
+	uint32_t addr;
+
+	addr = ptr[0];
+	msm_camera_io_w(addr,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_AF_WR_PING_ADDR);
+	addr = ptr[1];
+	msm_camera_io_w(addr,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_AF_WR_PONG_ADDR);
+
+	vfe31_ctrl->afStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+	return 0;
+}
+
+static uint32_t vfe_stats_ihist_buf_init(struct vfe_cmd_stats_buf *in)
+{
+	uint32_t *ptr = in->statsBuf;
+	uint32_t addr;
+
+	addr = ptr[0];
+	msm_camera_io_w(addr,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_HIST_WR_PING_ADDR);
+	addr = ptr[1];
+	msm_camera_io_w(addr,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_HIST_WR_PONG_ADDR);
+
+	vfe31_ctrl->ihistStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+	return 0;
+}
+
+static uint32_t vfe_stats_rs_buf_init(struct vfe_cmd_stats_buf *in)
+{
+	uint32_t *ptr = in->statsBuf;
+	uint32_t addr;
+
+	addr = ptr[0];
+	msm_camera_io_w(addr,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_RS_WR_PING_ADDR);
+	addr = ptr[1];
+	msm_camera_io_w(addr,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_RS_WR_PONG_ADDR);
+
+	vfe31_ctrl->rsStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+	return 0;
+}
+
+static uint32_t vfe_stats_cs_buf_init(struct vfe_cmd_stats_buf *in)
+{
+	uint32_t *ptr = in->statsBuf;
+	uint32_t addr;
+
+	addr = ptr[0];
+	msm_camera_io_w(addr,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_CS_WR_PING_ADDR);
+	addr = ptr[1];
+	msm_camera_io_w(addr,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_CS_WR_PONG_ADDR);
+
+	vfe31_ctrl->csStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+	return 0;
+}
+
+static void msm_camera_io_dump2(void __iomem *addr, int size)
+{
+	char line_str[BUFF_SIZE_128], *p_str;
+	int i;
+	u32 *p = (u32 *) addr;
+	u32 data;
+	CDBG("%s: %p %d\n", __func__, addr, size);
+	line_str[0] = '\0';
+	p_str = line_str;
+	for (i = 0; i < size/4; i++) {
+		if (i % 4 == 0) {
+			snprintf(p_str, 12, "%08x: ", (u32) p);
+			p_str += 10;
+		}
+		data = readl_relaxed(p++);
+		snprintf(p_str, 12, "%08x ", data);
+		p_str += 9;
+		if ((i + 1) % 4 == 0) {
+			CDBG("%s\n", line_str);
+			line_str[0] = '\0';
+			p_str = line_str;
+		}
+	}
+	if (line_str[0] != '\0')
+		CDBG("%s\n", line_str);
+}
+
+static void vfe31_start_common(void)
+{
+	uint32_t irq_mask = 0x00E00021;
+	vfe31_ctrl->start_ack_pending = TRUE;
+	CDBG("VFE opertaion mode = 0x%x, output mode = 0x%x\n",
+		vfe31_ctrl->operation_mode, vfe31_ctrl->outpath.output_mode);
+	if (vfe31_ctrl->stats_comp)
+		irq_mask |= VFE_IRQ_STATUS0_STATS_COMPOSIT_MASK;
+	else
+		irq_mask |= 0x000FE000;
+
+	msm_camera_io_w(irq_mask, vfe31_ctrl->vfebase + VFE_IRQ_MASK_0);
+	msm_camera_io_w(VFE_IMASK_WHILE_STOPPING_1,
+		vfe31_ctrl->vfebase + VFE_IRQ_MASK_1);
+
+	/* Ensure the write order while writing
+	to the command register using the barrier */
+	msm_camera_io_w_mb(1, vfe31_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+	msm_camera_io_w_mb(1, vfe31_ctrl->vfebase + VFE_CAMIF_COMMAND);
+
+	msm_camera_io_dump2(vfe31_ctrl->vfebase, vfe31_ctrl->register_total*4);
+	atomic_set(&vfe31_ctrl->vstate, 1);
+}
+
+static int vfe31_start_recording(struct msm_cam_media_controller *pmctl)
+{
+	msm_camio_bus_scale_cfg(
+		pmctl->sdata->pdata->cam_bus_scale_table, S_VIDEO);
+	vfe31_ctrl->recording_state = VFE_STATE_START_REQUESTED;
+	msm_camera_io_w_mb(1, vfe31_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+	return 0;
+}
+
+static int vfe31_stop_recording(struct msm_cam_media_controller *pmctl)
+{
+	vfe31_ctrl->recording_state = VFE_STATE_STOP_REQUESTED;
+	msm_camera_io_w_mb(1, vfe31_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+	msm_camio_bus_scale_cfg(
+		pmctl->sdata->pdata->cam_bus_scale_table, S_PREVIEW);
+	return 0;
+}
+
+static void vfe31_start_liveshot(struct msm_cam_media_controller *pmctl)
+{
+	/* Hardcode 1 live snapshot for now. */
+	vfe31_ctrl->outpath.out0.capture_cnt = 1;
+	vfe31_ctrl->vfe_capture_count = vfe31_ctrl->outpath.out0.capture_cnt;
+
+	vfe31_ctrl->liveshot_state = VFE_STATE_START_REQUESTED;
+	msm_camera_io_w_mb(1, vfe31_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+}
+
+static int vfe31_zsl(struct msm_cam_media_controller *pmctl)
+{
+	uint32_t irq_comp_mask = 0;
+	/* capture command is valid for both idle and active state. */
+	irq_comp_mask	=
+		msm_camera_io_r(vfe31_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+
+	CDBG("%s:op mode %d O/P Mode %d\n", __func__,
+		vfe31_ctrl->operation_mode, vfe31_ctrl->outpath.output_mode);
+
+	if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_PRIMARY) {
+		irq_comp_mask |= ((0x1 << (vfe31_ctrl->outpath.out0.ch0)) |
+			(0x1 << (vfe31_ctrl->outpath.out0.ch1)));
+	} else if (vfe31_ctrl->outpath.output_mode &
+		VFE31_OUTPUT_MODE_PRIMARY_ALL_CHNLS) {
+		irq_comp_mask |= ((0x1 << (vfe31_ctrl->outpath.out0.ch0)) |
+			(0x1 << (vfe31_ctrl->outpath.out0.ch1)) |
+			(0x1 << (vfe31_ctrl->outpath.out0.ch2)));
+	}
+
+	if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_SECONDARY) {
+		irq_comp_mask |= ((0x1 << (vfe31_ctrl->outpath.out1.ch0 + 8)) |
+			(0x1 << (vfe31_ctrl->outpath.out1.ch1 + 8)));
+	} else if (vfe31_ctrl->outpath.output_mode &
+		VFE31_OUTPUT_MODE_SECONDARY_ALL_CHNLS) {
+		irq_comp_mask |= ((0x1 << (vfe31_ctrl->outpath.out1.ch0 + 8)) |
+			(0x1 << (vfe31_ctrl->outpath.out1.ch1 + 8)) |
+			(0x1 << (vfe31_ctrl->outpath.out1.ch2 + 8)));
+	}
+
+	if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_PRIMARY) {
+		msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch0]);
+		msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch1]);
+	} else if (vfe31_ctrl->outpath.output_mode &
+		VFE31_OUTPUT_MODE_PRIMARY_ALL_CHNLS) {
+		msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch0]);
+		msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch1]);
+		msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch2]);
+	}
+
+	if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_SECONDARY) {
+		msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch0]);
+		msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch1]);
+	} else if (vfe31_ctrl->outpath.output_mode &
+		VFE31_OUTPUT_MODE_SECONDARY_ALL_CHNLS) {
+		msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch0]);
+		msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch1]);
+		msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch2]);
+	}
+
+	msm_camera_io_w(irq_comp_mask, vfe31_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+	vfe31_start_common();
+	msm_camio_bus_scale_cfg(
+		pmctl->sdata->pdata->cam_bus_scale_table, S_ZSL);
+
+	msm_camera_io_w(1, vfe31_ctrl->vfebase + 0x18C);
+	msm_camera_io_w(1, vfe31_ctrl->vfebase + 0x188);
+	return 0;
+}
+static int vfe31_capture_raw(
+	struct msm_cam_media_controller *pmctl,
+	uint32_t num_frames_capture)
+{
+	uint32_t irq_comp_mask = 0;
+
+	vfe31_ctrl->outpath.out0.capture_cnt = num_frames_capture;
+	vfe31_ctrl->vfe_capture_count = num_frames_capture;
+
+	irq_comp_mask =
+		msm_camera_io_r(vfe31_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+
+	if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_PRIMARY) {
+		irq_comp_mask |= (0x1 << (vfe31_ctrl->outpath.out0.ch0));
+		msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch0]);
+	}
+
+	msm_camera_io_w(irq_comp_mask, vfe31_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+	msm_camio_bus_scale_cfg(
+		pmctl->sdata->pdata->cam_bus_scale_table, S_CAPTURE);
+	vfe31_start_common();
+	return 0;
+}
+
+static int vfe31_capture(
+	struct msm_cam_media_controller *pmctl,
+	uint32_t num_frames_capture)
+{
+	uint32_t irq_comp_mask = 0;
+	/* capture command is valid for both idle and active state. */
+	vfe31_ctrl->outpath.out1.capture_cnt = num_frames_capture;
+	if (vfe31_ctrl->operation_mode == VFE_OUTPUTS_MAIN_AND_THUMB ||
+		vfe31_ctrl->operation_mode == VFE_OUTPUTS_THUMB_AND_MAIN ||
+		vfe31_ctrl->operation_mode == VFE_OUTPUTS_JPEG_AND_THUMB ||
+		vfe31_ctrl->operation_mode == VFE_OUTPUTS_THUMB_AND_JPEG) {
+		vfe31_ctrl->outpath.out0.capture_cnt =
+			num_frames_capture;
+	}
+
+	vfe31_ctrl->vfe_capture_count = num_frames_capture;
+	irq_comp_mask = msm_camera_io_r(
+				vfe31_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+
+	if (vfe31_ctrl->operation_mode == VFE_OUTPUTS_MAIN_AND_THUMB ||
+		vfe31_ctrl->operation_mode == VFE_OUTPUTS_JPEG_AND_THUMB ||
+		vfe31_ctrl->operation_mode == VFE_OUTPUTS_THUMB_AND_MAIN) {
+		if (vfe31_ctrl->outpath.output_mode &
+			VFE31_OUTPUT_MODE_PRIMARY) {
+			irq_comp_mask |= (0x1 << vfe31_ctrl->outpath.out0.ch0 |
+				0x1 << vfe31_ctrl->outpath.out0.ch1);
+		}
+		if (vfe31_ctrl->outpath.output_mode &
+			VFE31_OUTPUT_MODE_SECONDARY) {
+			irq_comp_mask |=
+				(0x1 << (vfe31_ctrl->outpath.out1.ch0 + 8) |
+				0x1 << (vfe31_ctrl->outpath.out1.ch1 + 8));
+		}
+		if (vfe31_ctrl->outpath.output_mode &
+			VFE31_OUTPUT_MODE_PRIMARY) {
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+				vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch0]);
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+				vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch1]);
+		}
+		if (vfe31_ctrl->outpath.output_mode &
+			VFE31_OUTPUT_MODE_SECONDARY) {
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+				vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch0]);
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+				vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch1]);
+		}
+	}
+
+	vfe31_ctrl->vfe_capture_count = num_frames_capture;
+
+	msm_camera_io_w(irq_comp_mask, vfe31_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+	msm_camera_io_r(vfe31_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+	msm_camio_bus_scale_cfg(
+		pmctl->sdata->pdata->cam_bus_scale_table, S_CAPTURE);
+
+	vfe31_start_common();
+	/* for debug */
+	msm_camera_io_w(1, vfe31_ctrl->vfebase + 0x18C);
+	msm_camera_io_w(1, vfe31_ctrl->vfebase + 0x188);
+	return 0;
+}
+
+static int vfe31_start(struct msm_cam_media_controller *pmctl)
+{
+	uint32_t irq_comp_mask = 0;
+
+	irq_comp_mask	=
+		msm_camera_io_r(vfe31_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+
+	if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_PRIMARY) {
+		irq_comp_mask |= (0x1 << vfe31_ctrl->outpath.out0.ch0 |
+			0x1 << vfe31_ctrl->outpath.out0.ch1);
+	} else if (vfe31_ctrl->outpath.output_mode &
+		VFE31_OUTPUT_MODE_PRIMARY_ALL_CHNLS) {
+		irq_comp_mask |= (0x1 << vfe31_ctrl->outpath.out0.ch0 |
+			0x1 << vfe31_ctrl->outpath.out0.ch1 |
+			0x1 << vfe31_ctrl->outpath.out0.ch2);
+	}
+	if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_SECONDARY) {
+		irq_comp_mask |= (0x1 << (vfe31_ctrl->outpath.out1.ch0 + 8) |
+			0x1 << (vfe31_ctrl->outpath.out1.ch1 + 8));
+	} else if (vfe31_ctrl->outpath.output_mode &
+		VFE31_OUTPUT_MODE_SECONDARY_ALL_CHNLS) {
+		irq_comp_mask |= (0x1 << (vfe31_ctrl->outpath.out1.ch0 + 8) |
+			0x1 << (vfe31_ctrl->outpath.out1.ch1 + 8) |
+			0x1 << (vfe31_ctrl->outpath.out1.ch2 + 8));
+	}
+	msm_camera_io_w(irq_comp_mask, vfe31_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+
+	switch (vfe31_ctrl->operation_mode) {
+	case VFE_OUTPUTS_PREVIEW:
+	case VFE_OUTPUTS_PREVIEW_AND_VIDEO:
+		if (vfe31_ctrl->outpath.output_mode &
+			VFE31_OUTPUT_MODE_PRIMARY) {
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch0]);
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch1]);
+		} else if (vfe31_ctrl->outpath.output_mode &
+			VFE31_OUTPUT_MODE_PRIMARY_ALL_CHNLS) {
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch0]);
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch1]);
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch2]);
+		}
+		break;
+	default:
+		if (vfe31_ctrl->outpath.output_mode &
+			VFE31_OUTPUT_MODE_SECONDARY) {
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch0]);
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch1]);
+		} else if (vfe31_ctrl->outpath.output_mode &
+			VFE31_OUTPUT_MODE_SECONDARY_ALL_CHNLS) {
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch0]);
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch1]);
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch2]);
+		}
+		break;
+	}
+	msm_camio_bus_scale_cfg(
+		pmctl->sdata->pdata->cam_bus_scale_table, S_PREVIEW);
+	vfe31_start_common();
+	return 0;
+}
+
+static void vfe31_update(void)
+{
+	unsigned long flags;
+
+	if (vfe31_ctrl->update_la) {
+		if (!msm_camera_io_r(vfe31_ctrl->vfebase + V31_LA_OFF))
+			msm_camera_io_w(1, vfe31_ctrl->vfebase + V31_LA_OFF);
+		else
+			msm_camera_io_w(0, vfe31_ctrl->vfebase + V31_LA_OFF);
+		vfe31_ctrl->update_la = false;
+	}
+
+	if (vfe31_ctrl->update_gamma) {
+		if (!msm_camera_io_r(vfe31_ctrl->vfebase + V31_RGB_G_OFF))
+			msm_camera_io_w(7, vfe31_ctrl->vfebase+V31_RGB_G_OFF);
+		else
+			msm_camera_io_w(0, vfe31_ctrl->vfebase+V31_RGB_G_OFF);
+		vfe31_ctrl->update_gamma = false;
+	}
+
+	spin_lock_irqsave(&vfe31_ctrl->update_ack_lock, flags);
+	vfe31_ctrl->update_ack_pending = TRUE;
+	spin_unlock_irqrestore(&vfe31_ctrl->update_ack_lock, flags);
+	/* Ensure the write order while writing
+	to the command register using the barrier */
+	msm_camera_io_w_mb(1, vfe31_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+	return;
+}
+
+static void vfe31_sync_timer_stop(void)
+{
+	uint32_t value = 0;
+	vfe31_ctrl->sync_timer_state = 0;
+	if (vfe31_ctrl->sync_timer_number == 0)
+		value = 0x10000;
+	else if (vfe31_ctrl->sync_timer_number == 1)
+		value = 0x20000;
+	else if (vfe31_ctrl->sync_timer_number == 2)
+		value = 0x40000;
+
+	/* Timer Stop */
+	msm_camera_io_w(value, vfe31_ctrl->vfebase + V31_SYNC_TIMER_OFF);
+}
+
+static void vfe31_sync_timer_start(const uint32_t *tbl)
+{
+	/* set bit 8 for auto increment. */
+	uint32_t value = 1;
+	uint32_t val;
+
+	vfe31_ctrl->sync_timer_state = *tbl++;
+	vfe31_ctrl->sync_timer_repeat_count = *tbl++;
+	vfe31_ctrl->sync_timer_number = *tbl++;
+	CDBG("%s timer_state %d, repeat_cnt %d timer number %d\n",
+		 __func__, vfe31_ctrl->sync_timer_state,
+		 vfe31_ctrl->sync_timer_repeat_count,
+		 vfe31_ctrl->sync_timer_number);
+
+	if (vfe31_ctrl->sync_timer_state) { /* Start Timer */
+		value = value << vfe31_ctrl->sync_timer_number;
+	} else { /* Stop Timer */
+		CDBG("Failed to Start timer\n");
+		return;
+	}
+
+	/* Timer Start */
+	msm_camera_io_w(value, vfe31_ctrl->vfebase + V31_SYNC_TIMER_OFF);
+	/* Sync Timer Line Start */
+	value = *tbl++;
+	msm_camera_io_w(value, vfe31_ctrl->vfebase + V31_SYNC_TIMER_OFF +
+		4 + ((vfe31_ctrl->sync_timer_number) * 12));
+	/* Sync Timer Pixel Start */
+	value = *tbl++;
+	msm_camera_io_w(value, vfe31_ctrl->vfebase + V31_SYNC_TIMER_OFF +
+		 8 + ((vfe31_ctrl->sync_timer_number) * 12));
+	/* Sync Timer Pixel Duration */
+	value = *tbl++;
+	val = vfe_clk_rate / 10000;
+	val = 10000000 / val;
+	val = value * 10000 / val;
+	CDBG("%s: Pixel Clk Cycles!!! %d\n", __func__, val);
+	msm_camera_io_w(val, vfe31_ctrl->vfebase + V31_SYNC_TIMER_OFF +
+		12 + ((vfe31_ctrl->sync_timer_number) * 12));
+	/* Timer0 Active High/LOW */
+	value = *tbl++;
+	msm_camera_io_w(value,
+		vfe31_ctrl->vfebase + V31_SYNC_TIMER_POLARITY_OFF);
+	/* Selects sync timer 0 output to drive onto timer1 port */
+	value = 0;
+	msm_camera_io_w(value, vfe31_ctrl->vfebase + V31_TIMER_SELECT_OFF);
+}
+
+static void vfe31_program_dmi_cfg(enum VFE31_DMI_RAM_SEL bankSel)
+{
+	/* set bit 8 for auto increment. */
+	uint32_t value = VFE_DMI_CFG_DEFAULT;
+	value += (uint32_t)bankSel;
+	CDBG("%s: banksel = %d\n", __func__, bankSel);
+
+	msm_camera_io_w(value, vfe31_ctrl->vfebase + VFE_DMI_CFG);
+	/* by default, always starts with offset 0.*/
+	msm_camera_io_w(0, vfe31_ctrl->vfebase + VFE_DMI_ADDR);
+}
+static void vfe31_write_gamma_cfg(enum VFE31_DMI_RAM_SEL channel_sel,
+						const uint32_t *tbl)
+{
+	int i;
+	uint32_t value, value1, value2;
+	vfe31_program_dmi_cfg(channel_sel);
+	for (i = 0 ; i < (VFE31_GAMMA_NUM_ENTRIES/2) ; i++) {
+		value = *tbl++;
+		value1 = value & 0x0000FFFF;
+		value2 = (value & 0xFFFF0000)>>16;
+		msm_camera_io_w((value1),
+			vfe31_ctrl->vfebase + VFE_DMI_DATA_LO);
+		msm_camera_io_w((value2),
+			vfe31_ctrl->vfebase + VFE_DMI_DATA_LO);
+	}
+	vfe31_program_dmi_cfg(NO_MEM_SELECTED);
+}
+
+static void vfe31_read_gamma_cfg(enum VFE31_DMI_RAM_SEL channel_sel,
+	uint32_t *tbl)
+{
+	int i;
+	vfe31_program_dmi_cfg(channel_sel);
+	CDBG("%s: Gamma table channel: %d\n", __func__, channel_sel);
+	for (i = 0 ; i < VFE31_GAMMA_NUM_ENTRIES ; i++) {
+		*tbl = msm_camera_io_r(vfe31_ctrl->vfebase + VFE_DMI_DATA_LO);
+		CDBG("%s: %08x\n", __func__, *tbl);
+		tbl++;
+	}
+	vfe31_program_dmi_cfg(NO_MEM_SELECTED);
+}
+
+static void vfe31_write_la_cfg(enum VFE31_DMI_RAM_SEL channel_sel,
+						const uint32_t *tbl)
+{
+	uint32_t i;
+	uint32_t value, value1, value2;
+
+	vfe31_program_dmi_cfg(channel_sel);
+	for (i = 0 ; i < (VFE31_LA_TABLE_LENGTH/2) ; i++) {
+		value = *tbl++;
+		value1 = value & 0x0000FFFF;
+		value2 = (value & 0xFFFF0000)>>16;
+		msm_camera_io_w((value1),
+			vfe31_ctrl->vfebase + VFE_DMI_DATA_LO);
+		msm_camera_io_w((value2),
+			vfe31_ctrl->vfebase + VFE_DMI_DATA_LO);
+	}
+	vfe31_program_dmi_cfg(NO_MEM_SELECTED);
+}
+
+static struct vfe31_output_ch *vfe31_get_ch(int path)
+{
+	struct vfe31_output_ch *ch = NULL;
+
+	if (path == VFE_MSG_OUTPUT_PRIMARY)
+		ch = &vfe31_ctrl->outpath.out0;
+	else if (path == VFE_MSG_OUTPUT_SECONDARY)
+		ch = &vfe31_ctrl->outpath.out1;
+	else
+		pr_err("%s: Invalid path %d\n", __func__, path);
+
+	BUG_ON(ch == NULL);
+	return ch;
+}
+static struct msm_free_buf *vfe31_check_free_buffer(int id, int path)
+{
+	struct vfe31_output_ch *outch = NULL;
+	struct msm_free_buf *b = NULL;
+	vfe31_subdev_notify(id, path);
+	outch = vfe31_get_ch(path);
+	if (outch->free_buf.ch_paddr[0])
+		b = &outch->free_buf;
+	return b;
+}
+static int vfe31_configure_pingpong_buffers(int id, int path)
+{
+	struct vfe31_output_ch *outch = NULL;
+	int rc = 0;
+	vfe31_subdev_notify(id, path);
+	outch = vfe31_get_ch(path);
+	if (outch->ping.ch_paddr[0] && outch->pong.ch_paddr[0]) {
+		/* Configure Preview Ping Pong */
+		CDBG("%s Configure ping/pong address for %d",
+			__func__, path);
+		vfe31_put_ch_ping_addr(outch->ch0,
+			outch->ping.ch_paddr[0]);
+		vfe31_put_ch_pong_addr(outch->ch0,
+			outch->pong.ch_paddr[0]);
+
+		if (vfe31_ctrl->operation_mode !=
+			VFE_OUTPUTS_RAW) {
+			vfe31_put_ch_ping_addr(outch->ch1,
+				outch->ping.ch_paddr[1]);
+			vfe31_put_ch_pong_addr(outch->ch1,
+				outch->pong.ch_paddr[1]);
+		}
+
+		if (outch->ping.num_planes > 2)
+			vfe31_put_ch_ping_addr(outch->ch2,
+				outch->ping.ch_paddr[2]);
+		if (outch->pong.num_planes > 2)
+			vfe31_put_ch_pong_addr(outch->ch2,
+				outch->pong.ch_paddr[2]);
+
+		/* avoid stale info */
+		memset(&outch->ping, 0, sizeof(struct msm_free_buf));
+		memset(&outch->pong, 0, sizeof(struct msm_free_buf));
+	} else {
+		pr_err("%s ping/pong addr is null!!", __func__);
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+static void vfe31_send_isp_msg(struct vfe31_ctrl_type *vctrl,
+	uint32_t isp_msg_id)
+{
+	struct isp_msg_event isp_msg_evt;
+
+	isp_msg_evt.msg_id = isp_msg_id;
+	isp_msg_evt.sof_count = vfe31_ctrl->vfeFrameId;
+	v4l2_subdev_notify(&vctrl->subdev,
+		NOTIFY_ISP_MSG_EVT, (void *)&isp_msg_evt);
+}
+
+static int vfe31_proc_general(
+	struct msm_cam_media_controller *pmctl,
+	struct msm_isp_cmd *cmd)
+{
+	int i , rc = 0;
+	uint32_t old_val = 0 , new_val = 0;
+	uint32_t *cmdp = NULL;
+	uint32_t *cmdp_local = NULL;
+	uint32_t snapshot_cnt = 0;
+	uint32_t temp1 = 0, temp2 = 0;
+
+	CDBG("vfe31_proc_general: cmdID = %s, length = %d\n",
+		vfe31_general_cmd[cmd->id], cmd->length);
+	switch (cmd->id) {
+	case VFE_CMD_RESET:
+		pr_info("vfe31_proc_general: cmdID = %s\n",
+			vfe31_general_cmd[cmd->id]);
+		vfe31_reset();
+		break;
+	case VFE_CMD_START:
+		pr_info("vfe31_proc_general: cmdID = %s\n",
+			vfe31_general_cmd[cmd->id]);
+		if ((vfe31_ctrl->operation_mode ==
+				VFE_OUTPUTS_PREVIEW_AND_VIDEO) ||
+			(vfe31_ctrl->operation_mode ==
+				VFE_OUTPUTS_PREVIEW))
+			/* Configure primary channel */
+			rc = vfe31_configure_pingpong_buffers(
+				VFE_MSG_V31_START, VFE_MSG_OUTPUT_PRIMARY);
+		else
+			/* Configure secondary channel */
+			rc = vfe31_configure_pingpong_buffers(
+				VFE_MSG_V31_START, VFE_MSG_OUTPUT_SECONDARY);
+		if (rc < 0) {
+			pr_err("%s error configuring pingpong buffers"
+				" for preview", __func__);
+			rc = -EINVAL;
+			goto proc_general_done;
+		}
+		rc = vfe31_start(pmctl);
+		break;
+	case VFE_CMD_UPDATE:
+		vfe31_update();
+		break;
+	case VFE_CMD_CAPTURE_RAW:
+		pr_info("%s: cmdID = VFE_CMD_CAPTURE_RAW\n", __func__);
+		if (copy_from_user(&snapshot_cnt, (void __user *)(cmd->value),
+			sizeof(uint32_t))) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		rc = vfe31_configure_pingpong_buffers(VFE_MSG_V31_CAPTURE,
+			VFE_MSG_OUTPUT_PRIMARY);
+		if (rc < 0) {
+			pr_err("%s error configuring pingpong buffers"
+				" for snapshot", __func__);
+			rc = -EINVAL;
+			goto proc_general_done;
+		}
+		rc = vfe31_capture_raw(pmctl, snapshot_cnt);
+		break;
+	case VFE_CMD_CAPTURE:
+		if (copy_from_user(&snapshot_cnt, (void __user *)(cmd->value),
+			sizeof(uint32_t))) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+
+		if (vfe31_ctrl->operation_mode == VFE_OUTPUTS_JPEG_AND_THUMB ||
+		vfe31_ctrl->operation_mode == VFE_OUTPUTS_THUMB_AND_JPEG) {
+			if (snapshot_cnt != 1) {
+				pr_err("only support 1 inline snapshot\n");
+				rc = -EINVAL;
+				goto proc_general_done;
+			}
+			/* Configure primary channel for JPEG */
+			rc = vfe31_configure_pingpong_buffers(
+				VFE_MSG_V31_JPEG_CAPTURE,
+				VFE_MSG_OUTPUT_PRIMARY);
+		} else {
+			/* Configure primary channel */
+			rc = vfe31_configure_pingpong_buffers(
+				VFE_MSG_V31_CAPTURE,
+				VFE_MSG_OUTPUT_PRIMARY);
+		}
+		if (rc < 0) {
+			pr_err("%s error configuring pingpong buffers"
+				" for primary output", __func__);
+			rc = -EINVAL;
+			goto proc_general_done;
+		}
+		/* Configure secondary channel */
+		rc = vfe31_configure_pingpong_buffers(VFE_MSG_V31_CAPTURE,
+			VFE_MSG_OUTPUT_SECONDARY);
+		if (rc < 0) {
+			pr_err("%s error configuring pingpong buffers"
+				" for secondary output", __func__);
+			rc = -EINVAL;
+			goto proc_general_done;
+		}
+		rc = vfe31_capture(pmctl, snapshot_cnt);
+		break;
+	case VFE_CMD_START_RECORDING:
+		pr_info("vfe31_proc_general: cmdID = %s\n",
+			vfe31_general_cmd[cmd->id]);
+		if (vfe31_ctrl->operation_mode ==
+			VFE_OUTPUTS_PREVIEW_AND_VIDEO)
+			rc = vfe31_configure_pingpong_buffers(
+				VFE_MSG_V31_START_RECORDING,
+				VFE_MSG_OUTPUT_SECONDARY);
+		else if (vfe31_ctrl->operation_mode ==
+			VFE_OUTPUTS_VIDEO_AND_PREVIEW)
+			rc = vfe31_configure_pingpong_buffers(
+				VFE_MSG_V31_START_RECORDING,
+				VFE_MSG_OUTPUT_PRIMARY);
+		if (rc < 0) {
+			pr_err("%s error configuring pingpong buffers"
+				" for video", __func__);
+			rc = -EINVAL;
+			goto proc_general_done;
+		}
+		rc = vfe31_start_recording(pmctl);
+		break;
+	case VFE_CMD_STOP_RECORDING:
+		pr_info("vfe31_proc_general: cmdID = %s\n",
+			vfe31_general_cmd[cmd->id]);
+		rc = vfe31_stop_recording(pmctl);
+		break;
+	case VFE_CMD_OPERATION_CFG:
+		if (cmd->length != V31_OPERATION_CFG_LEN) {
+			rc = -EINVAL;
+			goto proc_general_done;
+		}
+		cmdp = kmalloc(V31_OPERATION_CFG_LEN, GFP_ATOMIC);
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			V31_OPERATION_CFG_LEN)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		rc = vfe31_operation_config(cmdp);
+		break;
+
+	case VFE_CMD_STATS_AE_START:
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		old_val = msm_camera_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val |= AE_BG_ENABLE_MASK;
+		msm_camera_io_w(old_val,
+			vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+		cmdp, (vfe31_cmd[cmd->id].length));
+		break;
+	case VFE_CMD_STATS_AF_START:
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		old_val = msm_camera_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val |= AF_BF_ENABLE_MASK;
+		msm_camera_io_w(old_val,
+			vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+		cmdp, (vfe31_cmd[cmd->id].length));
+		break;
+	case VFE_CMD_STATS_AWB_START:
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		old_val = msm_camera_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val |= AWB_ENABLE_MASK;
+		msm_camera_io_w(old_val,
+			vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+			cmdp, (vfe31_cmd[cmd->id].length));
+		break;
+
+	case VFE_CMD_STATS_IHIST_START:
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		old_val = msm_camera_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val |= IHIST_ENABLE_MASK;
+		msm_camera_io_w(old_val,
+			vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+			cmdp, (vfe31_cmd[cmd->id].length));
+		break;
+
+	case VFE_CMD_STATS_RS_START:
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+			cmdp, (vfe31_cmd[cmd->id].length));
+		break;
+
+	case VFE_CMD_STATS_CS_START:
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+			cmdp, (vfe31_cmd[cmd->id].length));
+		break;
+
+	case VFE_CMD_MCE_UPDATE:
+	case VFE_CMD_MCE_CFG:
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		/* Incrementing with 4 so as to point to the 2nd Register as
+		the 2nd register has the mce_enable bit */
+		old_val = msm_camera_io_r(vfe31_ctrl->vfebase +
+			V31_CHROMA_SUP_OFF + 4);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		cmdp_local = cmdp;
+		new_val = *cmdp_local;
+		old_val &= MCE_EN_MASK;
+		new_val = new_val | old_val;
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + V31_CHROMA_SUP_OFF + 4,
+			&new_val, 4);
+		cmdp_local += 1;
+
+		old_val = msm_camera_io_r(vfe31_ctrl->vfebase +
+			V31_CHROMA_SUP_OFF + 8);
+		new_val = *cmdp_local;
+		old_val &= MCE_Q_K_MASK;
+		new_val = new_val | old_val;
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + V31_CHROMA_SUP_OFF + 8,
+			&new_val, 4);
+		cmdp_local += 1;
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+			cmdp_local, (vfe31_cmd[cmd->id].length));
+		break;
+	case VFE_CMD_CHROMA_SUP_UPDATE:
+	case VFE_CMD_CHROMA_SUP_CFG:
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		cmdp_local = cmdp;
+		msm_camera_io_memcpy(vfe31_ctrl->vfebase + V31_CHROMA_SUP_OFF,
+			cmdp_local, 4);
+
+		cmdp_local += 1;
+		new_val = *cmdp_local;
+		/* Incrementing with 4 so as to point to the 2nd Register as
+		 * the 2nd register has the mce_enable bit
+		 */
+		old_val = msm_camera_io_r(vfe31_ctrl->vfebase +
+			V31_CHROMA_SUP_OFF + 4);
+		old_val &= ~MCE_EN_MASK;
+		new_val = new_val | old_val;
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + V31_CHROMA_SUP_OFF + 4,
+			&new_val, 4);
+		cmdp_local += 1;
+
+		old_val = msm_camera_io_r(vfe31_ctrl->vfebase +
+			V31_CHROMA_SUP_OFF + 8);
+		new_val = *cmdp_local;
+		old_val &= ~MCE_Q_K_MASK;
+		new_val = new_val | old_val;
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + V31_CHROMA_SUP_OFF + 8,
+			&new_val, 4);
+		break;
+
+	case VFE_CMD_MESH_ROLL_OFF_CFG:
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value) , cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		cmdp_local = cmdp;
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+			cmdp_local, 16);
+		cmdp_local += 4;
+		vfe31_program_dmi_cfg(ROLLOFF_RAM);
+		/* for loop for extrcting init table. */
+		for (i = 0; i < (V31_MESH_ROLL_OFF_INIT_TABLE_SIZE * 2); i++) {
+			msm_camera_io_w(*cmdp_local ,
+			vfe31_ctrl->vfebase + VFE_DMI_DATA_LO);
+			cmdp_local++;
+		}
+		CDBG("done writing init table\n");
+		/* by default, always starts with offset 0. */
+		msm_camera_io_w(V31_MESH_ROLL_OFF_DELTA_TABLE_OFFSET,
+		vfe31_ctrl->vfebase + VFE_DMI_ADDR);
+		/* for loop for extracting delta table. */
+		for (i = 0; i < (V31_MESH_ROLL_OFF_DELTA_TABLE_SIZE * 2); i++) {
+			msm_camera_io_w(*cmdp_local,
+			vfe31_ctrl->vfebase + VFE_DMI_DATA_LO);
+			cmdp_local++;
+		}
+		vfe31_program_dmi_cfg(NO_MEM_SELECTED);
+		break;
+
+	case VFE_CMD_GET_MESH_ROLLOFF_TABLE:
+		temp1 = sizeof(uint32_t) * ((V31_MESH_ROLL_OFF_INIT_TABLE_SIZE *
+			2) + (V31_MESH_ROLL_OFF_DELTA_TABLE_SIZE * 2));
+		if (cmd->length != temp1) {
+			rc = -EINVAL;
+			goto proc_general_done;
+		}
+		cmdp = kzalloc(temp1, GFP_KERNEL);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		cmdp_local = cmdp;
+		vfe31_program_dmi_cfg(ROLLOFF_RAM);
+		CDBG("%s: Mesh Rolloff init Table\n", __func__);
+		for (i = 0; i < (V31_MESH_ROLL_OFF_INIT_TABLE_SIZE * 2); i++) {
+			*cmdp_local = msm_camera_io_r(
+					vfe31_ctrl->vfebase + VFE_DMI_DATA_LO);
+			CDBG("%s: %08x\n", __func__, *cmdp_local);
+			cmdp_local++;
+		}
+		msm_camera_io_w(V31_MESH_ROLL_OFF_DELTA_TABLE_OFFSET,
+			vfe31_ctrl->vfebase + VFE_DMI_ADDR);
+		CDBG("%s: Mesh Rolloff Delta Table\n", __func__);
+		for (i = 0; i < (V31_MESH_ROLL_OFF_DELTA_TABLE_SIZE * 2); i++) {
+			*cmdp_local = msm_camera_io_r(
+					vfe31_ctrl->vfebase + VFE_DMI_DATA_LO);
+			CDBG("%s: %08x\n", __func__, *cmdp_local);
+			cmdp_local++;
+		}
+		CDBG("done reading delta table\n");
+		vfe31_program_dmi_cfg(NO_MEM_SELECTED);
+		if (copy_to_user((void __user *)(cmd->value), cmdp,
+			temp1)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		break;
+	case VFE_CMD_LA_CFG:
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		cmdp_local = cmdp;
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+			cmdp_local, (vfe31_cmd[cmd->id].length));
+
+		cmdp_local += 1;
+		vfe31_write_la_cfg(LUMA_ADAPT_LUT_RAM_BANK0, cmdp_local);
+		break;
+
+	case VFE_CMD_LA_UPDATE:
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+
+		cmdp_local = cmdp + 1;
+		old_val = msm_camera_io_r(vfe31_ctrl->vfebase + V31_LA_OFF);
+		if (old_val != 0x0)
+			vfe31_write_la_cfg(LUMA_ADAPT_LUT_RAM_BANK0,
+				cmdp_local);
+		else
+			vfe31_write_la_cfg(LUMA_ADAPT_LUT_RAM_BANK1,
+				cmdp_local);
+		vfe31_ctrl->update_la = true;
+		break;
+
+	case VFE_CMD_GET_LA_TABLE:
+		temp1 = sizeof(uint32_t) * VFE31_LA_TABLE_LENGTH / 2;
+		if (cmd->length != temp1) {
+			rc = -EINVAL;
+			goto proc_general_done;
+		}
+		cmdp = kzalloc(temp1, GFP_KERNEL);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		cmdp_local = cmdp;
+		if (msm_camera_io_r(vfe31_ctrl->vfebase + V31_LA_OFF))
+			vfe31_program_dmi_cfg(LUMA_ADAPT_LUT_RAM_BANK1);
+		else
+			vfe31_program_dmi_cfg(LUMA_ADAPT_LUT_RAM_BANK0);
+		for (i = 0 ; i < (VFE31_LA_TABLE_LENGTH / 2) ; i++) {
+			*cmdp_local = msm_camera_io_r(
+					vfe31_ctrl->vfebase + VFE_DMI_DATA_LO);
+			*cmdp_local |= (msm_camera_io_r(vfe31_ctrl->vfebase +
+				VFE_DMI_DATA_LO)) << 16;
+			cmdp_local++;
+		}
+		vfe31_program_dmi_cfg(NO_MEM_SELECTED);
+		if (copy_to_user((void __user *)(cmd->value), cmdp,
+			temp1)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		break;
+	case VFE_CMD_SK_ENHAN_CFG:
+	case VFE_CMD_SK_ENHAN_UPDATE:
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		msm_camera_io_memcpy(vfe31_ctrl->vfebase + V31_SCE_OFF,
+			cmdp, V31_SCE_LEN);
+		break;
+
+	case VFE_CMD_LIVESHOT:
+		/* Configure primary channel */
+		rc = vfe31_configure_pingpong_buffers(VFE_MSG_V31_CAPTURE,
+			VFE_MSG_OUTPUT_PRIMARY);
+		if (rc < 0) {
+			pr_err("%s error configuring pingpong buffers"
+				" for primary output", __func__);
+			rc = -EINVAL;
+			goto proc_general_done;
+		}
+		vfe31_start_liveshot(pmctl);
+		break;
+
+	case VFE_CMD_DEMOSAICV3:
+		if (cmd->length != V31_DEMOSAICV3_LEN) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		cmdp_local = cmdp;
+		new_val = *cmdp_local;
+
+		old_val = msm_camera_io_r(
+				vfe31_ctrl->vfebase + V31_DEMOSAICV3_OFF);
+		old_val &= DEMOSAIC_MASK;
+		new_val = new_val | old_val;
+		*cmdp_local = new_val;
+
+		msm_camera_io_memcpy(vfe31_ctrl->vfebase + V31_DEMOSAICV3_OFF,
+			cmdp_local, V31_DEMOSAICV3_LEN);
+		break;
+
+	case VFE_CMD_DEMOSAICV3_UPDATE:
+		if (cmd->length !=
+			V31_DEMOSAICV3_LEN * V31_DEMOSAICV3_UP_REG_CNT) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		cmdp_local = cmdp;
+		new_val = *cmdp_local;
+
+		old_val = msm_camera_io_r(
+				vfe31_ctrl->vfebase + V31_DEMOSAICV3_OFF);
+		old_val &= DEMOSAIC_MASK;
+		new_val = new_val | old_val;
+		*cmdp_local = new_val;
+
+		msm_camera_io_memcpy(vfe31_ctrl->vfebase + V31_DEMOSAICV3_OFF,
+			cmdp_local, V31_DEMOSAICV3_LEN);
+
+		break;
+
+	case VFE_CMD_DEMOSAICV3_ABCC_CFG:
+		rc = -EFAULT;
+		break;
+
+	case VFE_CMD_DEMOSAICV3_ABF_UPDATE:/* 116 ABF update  */
+	case VFE_CMD_DEMOSAICV3_ABF_CFG: /* 108 ABF config  */
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		cmdp_local = cmdp;
+		new_val = *cmdp_local;
+
+		old_val = msm_camera_io_r(
+				vfe31_ctrl->vfebase + V31_DEMOSAICV3_OFF);
+		old_val &= ABF_MASK;
+		new_val = new_val | old_val;
+		*cmdp_local = new_val;
+
+		msm_camera_io_memcpy(vfe31_ctrl->vfebase + V31_DEMOSAICV3_OFF,
+		    cmdp_local, 4);
+
+		cmdp_local += 1;
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+			cmdp_local, (vfe31_cmd[cmd->id].length));
+		break;
+
+	case VFE_CMD_DEMOSAICV3_DBCC_CFG:
+	case VFE_CMD_DEMOSAICV3_DBCC_UPDATE:
+		return -EINVAL;
+
+	case VFE_CMD_DEMOSAICV3_DBPC_CFG:
+	case VFE_CMD_DEMOSAICV3_DBPC_UPDATE:
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		cmdp_local = cmdp;
+		new_val = *cmdp_local;
+
+		old_val = msm_camera_io_r(
+				vfe31_ctrl->vfebase + V31_DEMOSAICV3_OFF);
+		old_val &= BPC_MASK;
+
+		new_val = new_val | old_val;
+		*cmdp_local = new_val;
+		msm_camera_io_memcpy(vfe31_ctrl->vfebase + V31_DEMOSAICV3_OFF,
+					cmdp_local, V31_DEMOSAICV3_LEN);
+		cmdp_local += 1;
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + V31_DEMOSAICV3_DBPC_CFG_OFF,
+			cmdp_local, V31_DEMOSAICV3_DBPC_LEN);
+		break;
+
+	case VFE_CMD_RGB_G_CFG:
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		msm_camera_io_memcpy(vfe31_ctrl->vfebase + V31_RGB_G_OFF,
+			cmdp, 4);
+		cmdp += 1;
+
+		vfe31_write_gamma_cfg(RGBLUT_RAM_CH0_BANK0, cmdp);
+		vfe31_write_gamma_cfg(RGBLUT_RAM_CH1_BANK0, cmdp);
+		vfe31_write_gamma_cfg(RGBLUT_RAM_CH2_BANK0, cmdp);
+		cmdp -= 1;
+		break;
+
+	case VFE_CMD_RGB_G_UPDATE:
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp, (void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+
+		old_val = msm_camera_io_r(vfe31_ctrl->vfebase + V31_RGB_G_OFF);
+		cmdp += 1;
+		if (old_val != 0x0) {
+			vfe31_write_gamma_cfg(RGBLUT_RAM_CH0_BANK0, cmdp);
+			vfe31_write_gamma_cfg(RGBLUT_RAM_CH1_BANK0, cmdp);
+			vfe31_write_gamma_cfg(RGBLUT_RAM_CH2_BANK0, cmdp);
+		} else {
+			vfe31_write_gamma_cfg(RGBLUT_RAM_CH0_BANK1, cmdp);
+			vfe31_write_gamma_cfg(RGBLUT_RAM_CH1_BANK1, cmdp);
+			vfe31_write_gamma_cfg(RGBLUT_RAM_CH2_BANK1, cmdp);
+		}
+		vfe31_ctrl->update_gamma = TRUE;
+		cmdp -= 1;
+		break;
+
+	case VFE_CMD_GET_RGB_G_TABLE:
+		temp1 = sizeof(uint32_t) * VFE31_GAMMA_NUM_ENTRIES * 3;
+		if (cmd->length != temp1) {
+			rc = -EINVAL;
+			goto proc_general_done;
+		}
+		cmdp = kzalloc(temp1, GFP_KERNEL);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		cmdp_local = cmdp;
+
+		old_val = msm_camera_io_r(vfe31_ctrl->vfebase + V31_RGB_G_OFF);
+		temp2 = old_val ? RGBLUT_RAM_CH0_BANK1 :
+			RGBLUT_RAM_CH0_BANK0;
+		for (i = 0; i < 3; i++) {
+			vfe31_read_gamma_cfg(temp2,
+				cmdp_local + (VFE31_GAMMA_NUM_ENTRIES * i));
+			temp2 += 2;
+		}
+		if (copy_to_user((void __user *)(cmd->value), cmdp,
+			temp1)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		break;
+
+	case VFE_CMD_STATS_AWB_STOP:
+		old_val = msm_camera_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val &= ~AWB_ENABLE_MASK;
+		msm_camera_io_w(old_val,
+			vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		break;
+	case VFE_CMD_STATS_AE_STOP:
+		old_val = msm_camera_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val &= ~AE_BG_ENABLE_MASK;
+		msm_camera_io_w(old_val,
+			vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		break;
+	case VFE_CMD_STATS_AF_STOP:
+		old_val = msm_camera_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val &= ~AF_BF_ENABLE_MASK;
+		msm_camera_io_w(old_val,
+			vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		break;
+
+	case VFE_CMD_STATS_IHIST_STOP:
+		old_val = msm_camera_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val &= ~IHIST_ENABLE_MASK;
+		msm_camera_io_w(old_val,
+			vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		break;
+
+	case VFE_CMD_STATS_RS_STOP:
+		old_val = msm_camera_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val &= ~RS_ENABLE_MASK;
+		msm_camera_io_w(old_val,
+			vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		break;
+
+	case VFE_CMD_STATS_CS_STOP:
+		old_val = msm_camera_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val &= ~CS_ENABLE_MASK;
+		msm_camera_io_w(old_val,
+			vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		break;
+
+	case VFE_CMD_STOP:
+		pr_info("vfe31_proc_general: cmdID = %s\n",
+			vfe31_general_cmd[cmd->id]);
+		vfe31_stop();
+		break;
+
+	case VFE_CMD_SYNC_TIMER_SETTING:
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp, (void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		vfe31_sync_timer_start(cmdp);
+		break;
+
+	case VFE_CMD_MODULE_CFG:
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		*cmdp &= ~STATS_ENABLE_MASK;
+		old_val = msm_camera_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val &= STATS_ENABLE_MASK;
+		*cmdp |= old_val;
+
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+			cmdp, (vfe31_cmd[cmd->id].length));
+		break;
+
+	case VFE_CMD_ZSL:
+		rc = vfe31_configure_pingpong_buffers(VFE_MSG_V31_START,
+			VFE_MSG_OUTPUT_PRIMARY);
+		if (rc < 0)
+			goto proc_general_done;
+		rc = vfe31_configure_pingpong_buffers(VFE_MSG_V31_START,
+			VFE_MSG_OUTPUT_SECONDARY);
+		if (rc < 0)
+			goto proc_general_done;
+
+		rc = vfe31_zsl(pmctl);
+		break;
+
+	case VFE_CMD_ASF_CFG:
+	case VFE_CMD_ASF_UPDATE:
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp, (void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+			cmdp, (vfe31_cmd[cmd->id].length));
+		cmdp_local = cmdp + V31_ASF_LEN/4;
+		break;
+
+	case VFE_CMD_GET_HW_VERSION:
+		if (cmd->length != V31_GET_HW_VERSION_LEN) {
+			rc = -EINVAL;
+			goto proc_general_done;
+		}
+		cmdp = kmalloc(V31_GET_HW_VERSION_LEN, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		*cmdp = msm_camera_io_r(
+				vfe31_ctrl->vfebase+V31_GET_HW_VERSION_OFF);
+		if (copy_to_user((void __user *)(cmd->value), cmdp,
+			V31_GET_HW_VERSION_LEN)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		break;
+	case VFE_CMD_GET_REG_DUMP:
+		temp1 = sizeof(uint32_t) * vfe31_ctrl->register_total;
+		if (cmd->length != temp1) {
+			rc = -EINVAL;
+			goto proc_general_done;
+		}
+		cmdp = kmalloc(temp1, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		msm_camera_io_dump(
+			vfe31_ctrl->vfebase, vfe31_ctrl->register_total*4);
+		CDBG("%s: %p %p %d\n", __func__, (void *)cmdp,
+			vfe31_ctrl->vfebase, temp1);
+		memcpy_fromio((void *)cmdp, vfe31_ctrl->vfebase, temp1);
+		if (copy_to_user((void __user *)(cmd->value), cmdp, temp1)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		break;
+	case VFE_CMD_FRAME_SKIP_CFG:
+		if (cmd->length != vfe31_cmd[cmd->id].length)
+			return -EINVAL;
+
+		cmdp = kmalloc(vfe31_cmd[cmd->id].length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+
+		if (copy_from_user(cmdp, (void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+			cmdp, (vfe31_cmd[cmd->id].length));
+		vfe31_ctrl->frame_skip_cnt = ((uint32_t)
+			*cmdp & VFE_FRAME_SKIP_PERIOD_MASK) + 1;
+		vfe31_ctrl->frame_skip_pattern = (uint32_t)(*(cmdp + 2));
+		break;
+	default:
+		if (cmd->length != vfe31_cmd[cmd->id].length)
+			return -EINVAL;
+
+		cmdp = kmalloc(vfe31_cmd[cmd->id].length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+
+		if (copy_from_user(cmdp, (void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		msm_camera_io_memcpy(
+			vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+			cmdp, (vfe31_cmd[cmd->id].length));
+		break;
+
+	}
+
+proc_general_done:
+	kfree(cmdp);
+
+	return rc;
+}
+
+static void vfe31_stats_af_ack(struct vfe_cmd_stats_ack *pAck)
+{
+	unsigned long flags;
+	spinlock_t *lock = (vfe31_ctrl->stats_comp ?
+		&vfe31_ctrl->comp_stats_ack_lock :
+		&vfe31_ctrl->af_ack_lock);
+	spin_lock_irqsave(lock, flags);
+	vfe31_ctrl->afStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+	vfe31_ctrl->afStatsControl.ackPending = FALSE;
+	spin_unlock_irqrestore(lock, flags);
+}
+
+static void vfe31_stats_awb_ack(struct vfe_cmd_stats_ack *pAck)
+{
+	unsigned long flags;
+	spinlock_t *lock = (vfe31_ctrl->stats_comp ?
+		&vfe31_ctrl->comp_stats_ack_lock :
+		&vfe31_ctrl->awb_ack_lock);
+	spin_lock_irqsave(lock, flags);
+	vfe31_ctrl->awbStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+	vfe31_ctrl->awbStatsControl.ackPending = FALSE;
+	spin_unlock_irqrestore(lock, flags);
+}
+
+static void vfe31_stats_aec_ack(struct vfe_cmd_stats_ack *pAck)
+{
+	unsigned long flags;
+	spinlock_t *lock = (vfe31_ctrl->stats_comp ?
+		&vfe31_ctrl->comp_stats_ack_lock :
+		&vfe31_ctrl->aec_ack_lock);
+	spin_lock_irqsave(lock, flags);
+	vfe31_ctrl->aecStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+	vfe31_ctrl->aecStatsControl.ackPending = FALSE;
+	spin_unlock_irqrestore(lock, flags);
+}
+
+static void vfe31_stats_ihist_ack(struct vfe_cmd_stats_ack *pAck)
+{
+	unsigned long flags;
+	spinlock_t *lock = (vfe31_ctrl->stats_comp ?
+		&vfe31_ctrl->comp_stats_ack_lock :
+		&vfe31_ctrl->ihist_ack_lock);
+	spin_lock_irqsave(lock, flags);
+	vfe31_ctrl->ihistStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+	vfe31_ctrl->ihistStatsControl.ackPending = FALSE;
+	spin_unlock_irqrestore(lock, flags);
+}
+static void vfe31_stats_rs_ack(struct vfe_cmd_stats_ack *pAck)
+{
+	unsigned long flags;
+	spinlock_t *lock = (vfe31_ctrl->stats_comp ?
+		&vfe31_ctrl->comp_stats_ack_lock :
+		&vfe31_ctrl->rs_ack_lock);
+	spin_lock_irqsave(lock, flags);
+	vfe31_ctrl->rsStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+	vfe31_ctrl->rsStatsControl.ackPending = FALSE;
+	spin_unlock_irqrestore(lock, flags);
+}
+static void vfe31_stats_cs_ack(struct vfe_cmd_stats_ack *pAck)
+{
+	unsigned long flags;
+	spinlock_t *lock = (vfe31_ctrl->stats_comp ?
+		&vfe31_ctrl->comp_stats_ack_lock :
+		&vfe31_ctrl->cs_ack_lock);
+	spin_lock_irqsave(lock, flags);
+	vfe31_ctrl->csStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+	vfe31_ctrl->csStatsControl.ackPending = FALSE;
+	spin_unlock_irqrestore(lock, flags);
+}
+
+static inline void vfe31_read_irq_status(struct vfe31_irq_status *out)
+{
+	uint32_t *temp;
+	memset(out, 0, sizeof(struct vfe31_irq_status));
+	temp = (uint32_t *)(vfe31_ctrl->vfebase + VFE_IRQ_STATUS_0);
+	out->vfeIrqStatus0 = msm_camera_io_r(temp);
+
+	temp = (uint32_t *)(vfe31_ctrl->vfebase + VFE_IRQ_STATUS_1);
+	out->vfeIrqStatus1 = msm_camera_io_r(temp);
+
+	temp = (uint32_t *)(vfe31_ctrl->vfebase + VFE_CAMIF_STATUS);
+	out->camifStatus = msm_camera_io_r(temp);
+	CDBG("camifStatus  = 0x%x\n", out->camifStatus);
+
+	/* clear the pending interrupt of the same kind.*/
+	msm_camera_io_w(out->vfeIrqStatus0,
+		vfe31_ctrl->vfebase + VFE_IRQ_CLEAR_0);
+	msm_camera_io_w(out->vfeIrqStatus1,
+		vfe31_ctrl->vfebase + VFE_IRQ_CLEAR_1);
+
+	/* Ensure the write order while writing
+	to the command register using the barrier */
+	msm_camera_io_w_mb(1, vfe31_ctrl->vfebase + VFE_IRQ_CMD);
+
+}
+
+static void vfe31_process_reg_update_irq(void)
+{
+	unsigned long flags;
+
+	if (vfe31_ctrl->recording_state == VFE_STATE_START_REQUESTED) {
+		if (vfe31_ctrl->operation_mode ==
+			VFE_OUTPUTS_VIDEO_AND_PREVIEW) {
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch0]);
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch1]);
+		} else if (vfe31_ctrl->operation_mode ==
+			VFE_OUTPUTS_PREVIEW_AND_VIDEO) {
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch0]);
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch1]);
+		}
+		vfe31_ctrl->recording_state = VFE_STATE_STARTED;
+		msm_camera_io_w_mb(1, vfe31_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+		CDBG("start video triggered .\n");
+	} else if (vfe31_ctrl->recording_state ==
+		VFE_STATE_STOP_REQUESTED) {
+		if (vfe31_ctrl->operation_mode ==
+			VFE_OUTPUTS_VIDEO_AND_PREVIEW) {
+			msm_camera_io_w(0, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch0]);
+			msm_camera_io_w(0, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch1]);
+		} else if (vfe31_ctrl->operation_mode ==
+			VFE_OUTPUTS_PREVIEW_AND_VIDEO) {
+			msm_camera_io_w(0, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch0]);
+			msm_camera_io_w(0, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch1]);
+		}
+		CDBG("stop video triggered .\n");
+	}
+
+	if (vfe31_ctrl->start_ack_pending == TRUE) {
+		vfe31_send_isp_msg(vfe31_ctrl, MSG_ID_START_ACK);
+		vfe31_ctrl->start_ack_pending = FALSE;
+	} else {
+		if (vfe31_ctrl->recording_state ==
+			VFE_STATE_STOP_REQUESTED) {
+			vfe31_ctrl->recording_state = VFE_STATE_STOPPED;
+			/* request a reg update and send STOP_REC_ACK
+			 * when we process the next reg update irq.
+			 */
+			msm_camera_io_w_mb(1,
+			vfe31_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+		} else if (vfe31_ctrl->recording_state ==
+			VFE_STATE_STOPPED) {
+			vfe31_send_isp_msg(vfe31_ctrl, MSG_ID_STOP_REC_ACK);
+			vfe31_ctrl->recording_state = VFE_STATE_IDLE;
+		}
+		spin_lock_irqsave(&vfe31_ctrl->update_ack_lock, flags);
+		if (vfe31_ctrl->update_ack_pending == TRUE) {
+			vfe31_ctrl->update_ack_pending = FALSE;
+			spin_unlock_irqrestore(
+				&vfe31_ctrl->update_ack_lock, flags);
+			vfe31_send_isp_msg(vfe31_ctrl, MSG_ID_UPDATE_ACK);
+		} else {
+			spin_unlock_irqrestore(
+				&vfe31_ctrl->update_ack_lock, flags);
+		}
+	}
+
+	if (vfe31_ctrl->liveshot_state == VFE_STATE_START_REQUESTED) {
+		pr_info("%s enabling liveshot output\n", __func__);
+		if (vfe31_ctrl->outpath.output_mode &
+			VFE31_OUTPUT_MODE_PRIMARY) {
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch0]);
+			msm_camera_io_w(1, vfe31_ctrl->vfebase +
+			vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch1]);
+			vfe31_ctrl->liveshot_state = VFE_STATE_STARTED;
+		}
+	}
+
+	if (vfe31_ctrl->liveshot_state == VFE_STATE_STARTED) {
+		vfe31_ctrl->vfe_capture_count--;
+		if (!vfe31_ctrl->vfe_capture_count)
+			vfe31_ctrl->liveshot_state = VFE_STATE_STOP_REQUESTED;
+		msm_camera_io_w_mb(1, vfe31_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+	} else if (vfe31_ctrl->liveshot_state == VFE_STATE_STOP_REQUESTED) {
+		CDBG("%s: disabling liveshot output\n", __func__);
+		if (vfe31_ctrl->outpath.output_mode &
+			VFE31_OUTPUT_MODE_PRIMARY) {
+			msm_camera_io_w(0, vfe31_ctrl->vfebase +
+				vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch0]);
+			msm_camera_io_w(0, vfe31_ctrl->vfebase +
+				vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch1]);
+			vfe31_ctrl->liveshot_state = VFE_STATE_STOPPED;
+			msm_camera_io_w_mb(1, vfe31_ctrl->vfebase +
+				VFE_REG_UPDATE_CMD);
+		}
+	} else if (vfe31_ctrl->liveshot_state == VFE_STATE_STOPPED) {
+		vfe31_ctrl->liveshot_state = VFE_STATE_IDLE;
+	}
+
+	if ((vfe31_ctrl->operation_mode == VFE_OUTPUTS_THUMB_AND_MAIN) ||
+		(vfe31_ctrl->operation_mode == VFE_OUTPUTS_MAIN_AND_THUMB) ||
+		(vfe31_ctrl->operation_mode == VFE_OUTPUTS_THUMB_AND_JPEG) ||
+		(vfe31_ctrl->operation_mode == VFE_OUTPUTS_JPEG_AND_THUMB)) {
+		/* in snapshot mode */
+		/* later we need to add check for live snapshot mode. */
+		if (vfe31_ctrl->frame_skip_pattern & (0x1 <<
+			(vfe31_ctrl->snapshot_frame_cnt %
+				vfe31_ctrl->frame_skip_cnt))) {
+			/* if last frame to be captured: */
+			if (vfe31_ctrl->vfe_capture_count == 0) {
+				/* stop the bus output:write master enable = 0*/
+				if (vfe31_ctrl->outpath.output_mode &
+					VFE31_OUTPUT_MODE_PRIMARY) {
+					msm_camera_io_w(0, vfe31_ctrl->vfebase +
+						vfe31_AXI_WM_CFG[vfe31_ctrl->
+						outpath.out0.ch0]);
+					msm_camera_io_w(0, vfe31_ctrl->vfebase +
+						vfe31_AXI_WM_CFG[vfe31_ctrl->
+						outpath.out0.ch1]);
+				}
+				if (vfe31_ctrl->outpath.output_mode &
+					VFE31_OUTPUT_MODE_SECONDARY) {
+					msm_camera_io_w(0, vfe31_ctrl->vfebase +
+						vfe31_AXI_WM_CFG[vfe31_ctrl->
+						outpath.out1.ch0]);
+					msm_camera_io_w(0, vfe31_ctrl->vfebase +
+						vfe31_AXI_WM_CFG[vfe31_ctrl->
+						outpath.out1.ch1]);
+				}
+				msm_camera_io_w_mb
+				(CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY,
+				vfe31_ctrl->vfebase + VFE_CAMIF_COMMAND);
+				vfe31_ctrl->snapshot_frame_cnt = -1;
+				vfe31_ctrl->frame_skip_cnt = 31;
+				vfe31_ctrl->frame_skip_pattern = 0xffffffff;
+			} /*if snapshot count is 0*/
+		} /*if frame is not being dropped*/
+		/* then do reg_update. */
+		msm_camera_io_w(1, vfe31_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+	} /* if snapshot mode. */
+}
+
+static void vfe31_set_default_reg_values(void)
+{
+	msm_camera_io_w(0x800080, vfe31_ctrl->vfebase + VFE_DEMUX_GAIN_0);
+	msm_camera_io_w(0x800080, vfe31_ctrl->vfebase + VFE_DEMUX_GAIN_1);
+	/* What value should we program CGC_OVERRIDE to? */
+	msm_camera_io_w(0xFFFFF, vfe31_ctrl->vfebase + VFE_CGC_OVERRIDE);
+
+	/* default frame drop period and pattern */
+	msm_camera_io_w(0x1f, vfe31_ctrl->vfebase + VFE_FRAMEDROP_ENC_Y_CFG);
+	msm_camera_io_w(0x1f, vfe31_ctrl->vfebase + VFE_FRAMEDROP_ENC_CBCR_CFG);
+	msm_camera_io_w(0xFFFFFFFF,
+		vfe31_ctrl->vfebase + VFE_FRAMEDROP_ENC_Y_PATTERN);
+	msm_camera_io_w(0xFFFFFFFF,
+		vfe31_ctrl->vfebase + VFE_FRAMEDROP_ENC_CBCR_PATTERN);
+	msm_camera_io_w(0x1f, vfe31_ctrl->vfebase + VFE_FRAMEDROP_VIEW_Y);
+	msm_camera_io_w(0x1f, vfe31_ctrl->vfebase + VFE_FRAMEDROP_VIEW_CBCR);
+	msm_camera_io_w(0xFFFFFFFF,
+		vfe31_ctrl->vfebase + VFE_FRAMEDROP_VIEW_Y_PATTERN);
+	msm_camera_io_w(0xFFFFFFFF,
+		vfe31_ctrl->vfebase + VFE_FRAMEDROP_VIEW_CBCR_PATTERN);
+	msm_camera_io_w(0, vfe31_ctrl->vfebase + VFE_CLAMP_MIN);
+	msm_camera_io_w(0xFFFFFF, vfe31_ctrl->vfebase + VFE_CLAMP_MAX);
+
+	/* stats UB config */
+	msm_camera_io_w(0x3980007,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_AEC_UB_CFG);
+	msm_camera_io_w(0x3A00007,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_AF_UB_CFG);
+	msm_camera_io_w(0x3A8000F,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_AWB_UB_CFG);
+	msm_camera_io_w(0x3B80007,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_RS_UB_CFG);
+	msm_camera_io_w(0x3C0001F,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_CS_UB_CFG);
+	msm_camera_io_w(0x3E0001F,
+		vfe31_ctrl->vfebase + VFE_BUS_STATS_HIST_UB_CFG);
+}
+
+static void vfe31_process_reset_irq(void)
+{
+	unsigned long flags;
+
+	atomic_set(&vfe31_ctrl->vstate, 0);
+
+	spin_lock_irqsave(&vfe31_ctrl->stop_flag_lock, flags);
+	if (vfe31_ctrl->stop_ack_pending) {
+		vfe31_ctrl->stop_ack_pending = FALSE;
+		spin_unlock_irqrestore(&vfe31_ctrl->stop_flag_lock, flags);
+		vfe31_send_isp_msg(vfe31_ctrl, MSG_ID_STOP_ACK);
+	} else {
+		spin_unlock_irqrestore(&vfe31_ctrl->stop_flag_lock, flags);
+		/* this is from reset command. */
+		vfe31_set_default_reg_values();
+
+		/* reload all write masters. (frame & line)*/
+		msm_camera_io_w(0x7FFF, vfe31_ctrl->vfebase + VFE_BUS_CMD);
+		vfe31_send_isp_msg(vfe31_ctrl, MSG_ID_RESET_ACK);
+	}
+}
+
+static void vfe31_process_camif_sof_irq(void)
+{
+	if (vfe31_ctrl->operation_mode ==
+		VFE_OUTPUTS_RAW) {
+		if (vfe31_ctrl->start_ack_pending) {
+			vfe31_send_isp_msg(vfe31_ctrl, MSG_ID_START_ACK);
+			vfe31_ctrl->start_ack_pending = FALSE;
+		}
+		vfe31_ctrl->vfe_capture_count--;
+		/* if last frame to be captured: */
+		if (vfe31_ctrl->vfe_capture_count == 0) {
+			/* Ensure the write order while writing
+			 to the command register using the barrier */
+			msm_camera_io_w_mb(CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY,
+				vfe31_ctrl->vfebase + VFE_CAMIF_COMMAND);
+		}
+	} /* if raw snapshot mode. */
+	if ((vfe31_ctrl->hfr_mode != HFR_MODE_OFF) &&
+		(vfe31_ctrl->operation_mode == VFE_MODE_OF_OPERATION_VIDEO) &&
+		(vfe31_ctrl->vfeFrameId % vfe31_ctrl->hfr_mode != 0)) {
+		vfe31_ctrl->vfeFrameId++;
+		CDBG("Skip the SOF notification when HFR enabled\n");
+		return;
+	}
+	vfe31_ctrl->vfeFrameId++;
+	vfe31_send_isp_msg(vfe31_ctrl, MSG_ID_SOF_ACK);
+	CDBG("camif_sof_irq, frameId = %d\n", vfe31_ctrl->vfeFrameId);
+
+	if (vfe31_ctrl->sync_timer_state) {
+		if (vfe31_ctrl->sync_timer_repeat_count == 0)
+			vfe31_sync_timer_stop();
+		else
+			vfe31_ctrl->sync_timer_repeat_count--;
+	}
+	if ((vfe31_ctrl->operation_mode == VFE_OUTPUTS_THUMB_AND_MAIN) ||
+		(vfe31_ctrl->operation_mode == VFE_OUTPUTS_MAIN_AND_THUMB) ||
+		(vfe31_ctrl->operation_mode == VFE_OUTPUTS_THUMB_AND_JPEG) ||
+		(vfe31_ctrl->operation_mode == VFE_OUTPUTS_JPEG_AND_THUMB)) {
+		if (vfe31_ctrl->frame_skip_pattern & (0x1 <<
+			(vfe31_ctrl->snapshot_frame_cnt %
+				vfe31_ctrl->frame_skip_cnt))) {
+			vfe31_ctrl->vfe_capture_count--;
+		}
+		vfe31_ctrl->snapshot_frame_cnt++;
+	}
+}
+
+static void vfe31_process_error_irq(uint32_t errStatus)
+{
+	uint32_t reg_value, read_val;
+
+	if (errStatus & VFE31_IMASK_CAMIF_ERROR) {
+		pr_err("vfe31_irq: camif errors\n");
+		reg_value = msm_camera_io_r(
+				vfe31_ctrl->vfebase + VFE_CAMIF_STATUS);
+		pr_err("camifStatus  = 0x%x\n", reg_value);
+		vfe31_send_isp_msg(vfe31_ctrl, MSG_ID_CAMIF_ERROR);
+	}
+
+	if (errStatus & VFE31_IMASK_STATS_CS_OVWR)
+		pr_err("vfe31_irq: stats cs overwrite\n");
+
+	if (errStatus & VFE31_IMASK_STATS_IHIST_OVWR)
+		pr_err("vfe31_irq: stats ihist overwrite\n");
+
+	if (errStatus & VFE31_IMASK_REALIGN_BUF_Y_OVFL)
+		pr_err("vfe31_irq: realign bug Y overflow\n");
+
+	if (errStatus & VFE31_IMASK_REALIGN_BUF_CB_OVFL)
+		pr_err("vfe31_irq: realign bug CB overflow\n");
+
+	if (errStatus & VFE31_IMASK_REALIGN_BUF_CR_OVFL)
+		pr_err("vfe31_irq: realign bug CR overflow\n");
+
+	if (errStatus & VFE31_IMASK_VIOLATION)
+		pr_err("vfe31_irq: violation interrupt\n");
+
+	if (errStatus & VFE31_IMASK_IMG_MAST_0_BUS_OVFL)
+		pr_err("vfe31_irq: image master 0 bus overflow\n");
+
+	if (errStatus & VFE31_IMASK_IMG_MAST_1_BUS_OVFL)
+		pr_err("vfe31_irq: image master 1 bus overflow\n");
+
+	if (errStatus & VFE31_IMASK_IMG_MAST_2_BUS_OVFL)
+		pr_err("vfe31_irq: image master 2 bus overflow\n");
+
+	if (errStatus & VFE31_IMASK_IMG_MAST_3_BUS_OVFL)
+		pr_err("vfe31_irq: image master 3 bus overflow\n");
+
+	if (errStatus & VFE31_IMASK_IMG_MAST_4_BUS_OVFL)
+		pr_err("vfe31_irq: image master 4 bus overflow\n");
+
+	if (errStatus & VFE31_IMASK_IMG_MAST_5_BUS_OVFL)
+		pr_err("vfe31_irq: image master 5 bus overflow\n");
+
+	if (errStatus & VFE31_IMASK_IMG_MAST_6_BUS_OVFL)
+		pr_err("vfe31_irq: image master 6 bus overflow\n");
+
+	if (errStatus & VFE31_IMASK_STATS_AE_BG_BUS_OVFL)
+		pr_err("vfe31_irq: ae/bg stats bus overflow\n");
+
+	if (errStatus & VFE31_IMASK_STATS_AF_BF_BUS_OVFL)
+		pr_err("vfe31_irq: af/bf stats bus overflow\n");
+
+	if (errStatus & VFE31_IMASK_STATS_AWB_BUS_OVFL)
+		pr_err("vfe31_irq: awb stats bus overflow\n");
+
+	if (errStatus & VFE31_IMASK_STATS_RS_BUS_OVFL)
+		pr_err("vfe31_irq: rs stats bus overflow\n");
+
+	if (errStatus & VFE31_IMASK_STATS_CS_BUS_OVFL)
+		pr_err("vfe31_irq: cs stats bus overflow\n");
+
+	if (errStatus & VFE31_IMASK_STATS_IHIST_BUS_OVFL)
+		pr_err("vfe31_irq: ihist stats bus overflow\n");
+
+	if (errStatus & VFE31_IMASK_STATS_SKIN_BHIST_BUS_OVFL)
+		pr_err("vfe31_irq: skin/bhist stats bus overflow\n");
+
+	if (errStatus & VFE31_IMASK_AXI_ERROR) {
+		pr_err("vfe31_irq: axi error\n");
+		/* read status too when overflow happens.*/
+		read_val = msm_camera_io_r(vfe31_ctrl->vfebase +
+			VFE_BUS_PING_PONG_STATUS);
+		pr_debug("VFE_BUS_PING_PONG_STATUS = 0x%x\n", read_val);
+		read_val = msm_camera_io_r(vfe31_ctrl->vfebase +
+			VFE_BUS_OPERATION_STATUS);
+		pr_debug("VFE_BUS_OPERATION_STATUS = 0x%x\n", read_val);
+		read_val = msm_camera_io_r(vfe31_ctrl->vfebase +
+			VFE_BUS_IMAGE_MASTER_0_WR_PM_STATS_0);
+		pr_debug("VFE_BUS_IMAGE_MASTER_0_WR_PM_STATS_0 = 0x%x\n",
+			read_val);
+		read_val = msm_camera_io_r(vfe31_ctrl->vfebase +
+			VFE_BUS_IMAGE_MASTER_0_WR_PM_STATS_1);
+		pr_debug("VFE_BUS_IMAGE_MASTER_0_WR_PM_STATS_1 = 0x%x\n",
+			read_val);
+		read_val = msm_camera_io_r(vfe31_ctrl->vfebase +
+			VFE_AXI_STATUS);
+		pr_debug("VFE_AXI_STATUS = 0x%x\n", read_val);
+	}
+}
+static void vfe_send_outmsg(struct v4l2_subdev *sd, uint8_t msgid,
+	uint32_t ch0_paddr, uint32_t ch1_paddr, uint32_t ch2_paddr)
+{
+	struct isp_msg_output msg;
+
+	msg.output_id		= msgid;
+	msg.buf.ch_paddr[0]	= ch0_paddr;
+	msg.buf.ch_paddr[1]	= ch1_paddr;
+	msg.buf.ch_paddr[2]	= ch2_paddr;
+	msg.frameCounter	= vfe31_ctrl->vfeFrameId;
+
+	v4l2_subdev_notify(&vfe31_ctrl->subdev,
+		NOTIFY_VFE_MSG_OUT, &msg);
+	return;
+}
+
+static void vfe31_process_output_path_irq_0(void)
+{
+	uint32_t ping_pong;
+	uint32_t ch0_paddr, ch1_paddr, ch2_paddr;
+	uint8_t out_bool = 0;
+	struct msm_free_buf *free_buf = NULL;
+
+	free_buf = vfe31_check_free_buffer(VFE_MSG_OUTPUT_IRQ,
+		VFE_MSG_OUTPUT_PRIMARY);
+
+	/* we render frames in the following conditions:
+	 * 1. Continuous mode and the free buffer is avaialable.
+	 * 2. In snapshot shot mode, free buffer is not always available.
+	 * when pending snapshot count is <=1,  then no need to use
+	 * free buffer.
+	 */
+	out_bool = ((vfe31_ctrl->operation_mode == VFE_OUTPUTS_THUMB_AND_MAIN ||
+		vfe31_ctrl->operation_mode == VFE_OUTPUTS_MAIN_AND_THUMB ||
+		vfe31_ctrl->operation_mode == VFE_OUTPUTS_THUMB_AND_JPEG ||
+		vfe31_ctrl->operation_mode == VFE_OUTPUTS_JPEG_AND_THUMB ||
+		vfe31_ctrl->operation_mode == VFE_OUTPUTS_RAW ||
+		vfe31_ctrl->liveshot_state == VFE_STATE_STARTED ||
+		vfe31_ctrl->liveshot_state == VFE_STATE_STOP_REQUESTED ||
+		vfe31_ctrl->liveshot_state == VFE_STATE_STOPPED) &&
+		(vfe31_ctrl->vfe_capture_count <= 1)) || free_buf;
+
+	if (out_bool) {
+		ping_pong = msm_camera_io_r(vfe31_ctrl->vfebase +
+			VFE_BUS_PING_PONG_STATUS);
+
+		/* Channel 0*/
+		ch0_paddr = vfe31_get_ch_addr(ping_pong,
+			vfe31_ctrl->outpath.out0.ch0);
+		/* Channel 1*/
+		ch1_paddr = vfe31_get_ch_addr(ping_pong,
+			vfe31_ctrl->outpath.out0.ch1);
+		/* Channel 2*/
+		ch2_paddr = vfe31_get_ch_addr(ping_pong,
+			vfe31_ctrl->outpath.out0.ch2);
+
+		CDBG("output path 0, ch0 = 0x%x, ch1 = 0x%x, ch2 = 0x%x\n",
+			ch0_paddr, ch1_paddr, ch2_paddr);
+		if (free_buf) {
+			/* Y channel */
+			vfe31_put_ch_addr(ping_pong,
+			vfe31_ctrl->outpath.out0.ch0,
+			free_buf->ch_paddr[0]);
+			/* Chroma channel */
+			vfe31_put_ch_addr(ping_pong,
+			vfe31_ctrl->outpath.out0.ch1,
+			free_buf->ch_paddr[1]);
+			if (free_buf->num_planes > 2)
+				vfe31_put_ch_addr(ping_pong,
+					vfe31_ctrl->outpath.out0.ch2,
+					free_buf->ch_paddr[2]);
+		}
+		if (vfe31_ctrl->operation_mode ==
+				VFE_OUTPUTS_THUMB_AND_MAIN ||
+			vfe31_ctrl->operation_mode ==
+				VFE_OUTPUTS_MAIN_AND_THUMB ||
+			vfe31_ctrl->operation_mode ==
+				VFE_OUTPUTS_THUMB_AND_JPEG ||
+			vfe31_ctrl->operation_mode ==
+				VFE_OUTPUTS_JPEG_AND_THUMB ||
+			vfe31_ctrl->operation_mode ==
+				VFE_OUTPUTS_RAW ||
+			vfe31_ctrl->liveshot_state == VFE_STATE_STOPPED)
+			vfe31_ctrl->outpath.out0.capture_cnt--;
+
+		vfe_send_outmsg(&vfe31_ctrl->subdev,
+			MSG_ID_OUTPUT_PRIMARY, ch0_paddr,
+			ch1_paddr, ch2_paddr);
+
+		if (vfe31_ctrl->liveshot_state == VFE_STATE_STOPPED)
+			vfe31_ctrl->liveshot_state = VFE_STATE_IDLE;
+
+	} else {
+		vfe31_ctrl->outpath.out0.frame_drop_cnt++;
+		CDBG("path_irq_0 - no free buffer!\n");
+	}
+}
+
+static void vfe31_process_output_path_irq_1(void)
+{
+	uint32_t ping_pong;
+	uint32_t ch0_paddr, ch1_paddr, ch2_paddr;
+	/* this must be snapshot main image output. */
+	uint8_t out_bool = 0;
+	struct msm_free_buf *free_buf = NULL;
+
+	free_buf = vfe31_check_free_buffer(VFE_MSG_OUTPUT_IRQ,
+		VFE_MSG_OUTPUT_SECONDARY);
+	out_bool = ((vfe31_ctrl->operation_mode ==
+				VFE_OUTPUTS_THUMB_AND_MAIN ||
+			vfe31_ctrl->operation_mode ==
+				VFE_OUTPUTS_MAIN_AND_THUMB ||
+			vfe31_ctrl->operation_mode ==
+				VFE_OUTPUTS_RAW ||
+			vfe31_ctrl->operation_mode ==
+				VFE_OUTPUTS_JPEG_AND_THUMB) &&
+			(vfe31_ctrl->vfe_capture_count <= 1)) || free_buf;
+
+	if (out_bool) {
+		ping_pong = msm_camera_io_r(vfe31_ctrl->vfebase +
+			VFE_BUS_PING_PONG_STATUS);
+
+		/* Y channel */
+		ch0_paddr = vfe31_get_ch_addr(ping_pong,
+			vfe31_ctrl->outpath.out1.ch0);
+		/* Chroma channel */
+		ch1_paddr = vfe31_get_ch_addr(ping_pong,
+			vfe31_ctrl->outpath.out1.ch1);
+		ch2_paddr = vfe31_get_ch_addr(ping_pong,
+			vfe31_ctrl->outpath.out1.ch2);
+
+		pr_debug("%s ch0 = 0x%x, ch1 = 0x%x, ch2 = 0x%x\n",
+			__func__, ch0_paddr, ch1_paddr, ch2_paddr);
+		if (free_buf) {
+			/* Y channel */
+			vfe31_put_ch_addr(ping_pong,
+			vfe31_ctrl->outpath.out1.ch0,
+			free_buf->ch_paddr[0]);
+			/* Chroma channel */
+			vfe31_put_ch_addr(ping_pong,
+			vfe31_ctrl->outpath.out1.ch1,
+			free_buf->ch_paddr[1]);
+			if (free_buf->num_planes > 2)
+				vfe31_put_ch_addr(ping_pong,
+					vfe31_ctrl->outpath.out1.ch2,
+					free_buf->ch_paddr[2]);
+		}
+		if (vfe31_ctrl->operation_mode ==
+				VFE_OUTPUTS_THUMB_AND_MAIN ||
+			vfe31_ctrl->operation_mode ==
+				VFE_OUTPUTS_MAIN_AND_THUMB ||
+			vfe31_ctrl->operation_mode ==
+				VFE_OUTPUTS_RAW ||
+			vfe31_ctrl->operation_mode ==
+				VFE_OUTPUTS_JPEG_AND_THUMB)
+			vfe31_ctrl->outpath.out1.capture_cnt--;
+
+		vfe_send_outmsg(&vfe31_ctrl->subdev,
+			MSG_ID_OUTPUT_SECONDARY, ch0_paddr,
+			ch1_paddr, ch2_paddr);
+	} else {
+		vfe31_ctrl->outpath.out1.frame_drop_cnt++;
+		CDBG("path_irq_1 - no free buffer!\n");
+	}
+}
+
+static uint32_t  vfe31_process_stats_irq_common(uint32_t statsNum,
+	uint32_t newAddr)
+{
+
+	uint32_t pingpongStatus;
+	uint32_t returnAddr;
+	uint32_t pingpongAddr;
+
+	/* must be 0=ping, 1=pong */
+	pingpongStatus =
+		((msm_camera_io_r(vfe31_ctrl->vfebase +
+		VFE_BUS_PING_PONG_STATUS))
+		& ((uint32_t)(1<<(statsNum + 7)))) >> (statsNum + 7);
+	/* stats bits starts at 7 */
+	CDBG("statsNum %d, pingpongStatus %d\n", statsNum, pingpongStatus);
+	pingpongAddr =
+		((uint32_t)(vfe31_ctrl->vfebase +
+		VFE_BUS_STATS_PING_PONG_BASE)) +
+		(3*statsNum)*4 + (1-pingpongStatus)*4;
+	returnAddr = msm_camera_io_r((uint32_t *)pingpongAddr);
+	msm_camera_io_w(newAddr, (uint32_t *)pingpongAddr);
+	return returnAddr;
+}
+
+static void
+vfe_send_stats_msg(uint32_t bufAddress, uint32_t statsNum)
+{
+	unsigned long flags;
+	/* fill message with right content. */
+	/* @todo This is causing issues, need further investigate */
+	/* spin_lock_irqsave(&ctrl->state_lock, flags); */
+	struct isp_msg_stats msgStats;
+	msgStats.frameCounter = vfe31_ctrl->vfeFrameId;
+	msgStats.buffer = bufAddress;
+
+	switch (statsNum) {
+	case STATS_AE_NUM:{
+		msgStats.id = MSG_ID_STATS_AEC;
+		spin_lock_irqsave(&vfe31_ctrl->aec_ack_lock, flags);
+		vfe31_ctrl->aecStatsControl.ackPending = TRUE;
+		spin_unlock_irqrestore(&vfe31_ctrl->aec_ack_lock, flags);
+		}
+		break;
+	case STATS_AF_NUM:{
+		msgStats.id = MSG_ID_STATS_AF;
+		spin_lock_irqsave(&vfe31_ctrl->af_ack_lock, flags);
+		vfe31_ctrl->afStatsControl.ackPending = TRUE;
+		spin_unlock_irqrestore(&vfe31_ctrl->af_ack_lock, flags);
+		}
+		break;
+	case STATS_AWB_NUM: {
+		msgStats.id = MSG_ID_STATS_AWB;
+		spin_lock_irqsave(&vfe31_ctrl->awb_ack_lock, flags);
+		vfe31_ctrl->awbStatsControl.ackPending = TRUE;
+		spin_unlock_irqrestore(&vfe31_ctrl->awb_ack_lock, flags);
+		}
+		break;
+
+	case STATS_IHIST_NUM: {
+		msgStats.id = MSG_ID_STATS_IHIST;
+		spin_lock_irqsave(&vfe31_ctrl->ihist_ack_lock, flags);
+		vfe31_ctrl->ihistStatsControl.ackPending = TRUE;
+		spin_unlock_irqrestore(&vfe31_ctrl->ihist_ack_lock, flags);
+		}
+		break;
+	case STATS_RS_NUM: {
+		msgStats.id = MSG_ID_STATS_RS;
+		spin_lock_irqsave(&vfe31_ctrl->rs_ack_lock, flags);
+		vfe31_ctrl->rsStatsControl.ackPending = TRUE;
+		spin_unlock_irqrestore(&vfe31_ctrl->rs_ack_lock, flags);
+		}
+		break;
+	case STATS_CS_NUM: {
+		msgStats.id = MSG_ID_STATS_CS;
+		spin_lock_irqsave(&vfe31_ctrl->cs_ack_lock, flags);
+		vfe31_ctrl->csStatsControl.ackPending = TRUE;
+		spin_unlock_irqrestore(&vfe31_ctrl->cs_ack_lock, flags);
+		}
+		break;
+
+	default:
+		goto stats_done;
+	}
+
+	v4l2_subdev_notify(&vfe31_ctrl->subdev,
+				NOTIFY_VFE_MSG_STATS,
+				&msgStats);
+stats_done:
+	/* spin_unlock_irqrestore(&ctrl->state_lock, flags); */
+	return;
+}
+
+static void vfe_send_comp_stats_msg(uint32_t status_bits)
+{
+	struct msm_stats_buf msgStats;
+	uint32_t temp;
+
+	msgStats.frame_id = vfe31_ctrl->vfeFrameId;
+	msgStats.status_bits = status_bits;
+
+	msgStats.aec.buff = vfe31_ctrl->aecStatsControl.bufToRender;
+	msgStats.awb.buff = vfe31_ctrl->awbStatsControl.bufToRender;
+	msgStats.af.buff = vfe31_ctrl->afStatsControl.bufToRender;
+
+	msgStats.ihist.buff = vfe31_ctrl->ihistStatsControl.bufToRender;
+	msgStats.rs.buff = vfe31_ctrl->rsStatsControl.bufToRender;
+	msgStats.cs.buff = vfe31_ctrl->csStatsControl.bufToRender;
+
+	temp = msm_camera_io_r(vfe31_ctrl->vfebase + VFE_STATS_AWB_SGW_CFG);
+	msgStats.awb_ymin = (0xFF00 & temp) >> 8;
+
+	v4l2_subdev_notify(&vfe31_ctrl->subdev,
+		NOTIFY_VFE_MSG_COMP_STATS, &msgStats);
+}
+
+static void vfe31_process_stats_ae_irq(void)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&vfe31_ctrl->aec_ack_lock, flags);
+	if (!(vfe31_ctrl->aecStatsControl.ackPending)) {
+		spin_unlock_irqrestore(&vfe31_ctrl->aec_ack_lock, flags);
+		vfe31_ctrl->aecStatsControl.bufToRender =
+			vfe31_process_stats_irq_common(STATS_AE_NUM,
+			vfe31_ctrl->aecStatsControl.nextFrameAddrBuf);
+
+		vfe_send_stats_msg(vfe31_ctrl->aecStatsControl.bufToRender,
+			STATS_AE_NUM);
+	} else{
+		spin_unlock_irqrestore(&vfe31_ctrl->aec_ack_lock, flags);
+		vfe31_ctrl->aecStatsControl.droppedStatsFrameCount++;
+		CDBG("%s: droppedStatsFrameCount = %d", __func__,
+			vfe31_ctrl->aecStatsControl.droppedStatsFrameCount);
+	}
+}
+
+static void vfe31_process_stats_awb_irq(void)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&vfe31_ctrl->awb_ack_lock, flags);
+	if (!(vfe31_ctrl->awbStatsControl.ackPending)) {
+		spin_unlock_irqrestore(&vfe31_ctrl->awb_ack_lock, flags);
+		vfe31_ctrl->awbStatsControl.bufToRender =
+			vfe31_process_stats_irq_common(STATS_AWB_NUM,
+			vfe31_ctrl->awbStatsControl.nextFrameAddrBuf);
+
+		vfe_send_stats_msg(vfe31_ctrl->awbStatsControl.bufToRender,
+			STATS_AWB_NUM);
+	} else{
+		spin_unlock_irqrestore(&vfe31_ctrl->awb_ack_lock, flags);
+		vfe31_ctrl->awbStatsControl.droppedStatsFrameCount++;
+		CDBG("%s: droppedStatsFrameCount = %d", __func__,
+			vfe31_ctrl->awbStatsControl.droppedStatsFrameCount);
+	}
+}
+
+static void vfe31_process_stats_af_irq(void)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&vfe31_ctrl->af_ack_lock, flags);
+	if (!(vfe31_ctrl->afStatsControl.ackPending)) {
+		spin_unlock_irqrestore(&vfe31_ctrl->af_ack_lock, flags);
+		vfe31_ctrl->afStatsControl.bufToRender =
+			vfe31_process_stats_irq_common(STATS_AF_NUM,
+			vfe31_ctrl->afStatsControl.nextFrameAddrBuf);
+
+		vfe_send_stats_msg(vfe31_ctrl->afStatsControl.bufToRender,
+			STATS_AF_NUM);
+	} else{
+		spin_unlock_irqrestore(&vfe31_ctrl->af_ack_lock, flags);
+		vfe31_ctrl->afStatsControl.droppedStatsFrameCount++;
+		CDBG("%s: droppedStatsFrameCount = %d", __func__,
+			vfe31_ctrl->afStatsControl.droppedStatsFrameCount);
+	}
+}
+
+static void vfe31_process_stats_ihist_irq(void)
+{
+	if (!(vfe31_ctrl->ihistStatsControl.ackPending)) {
+		vfe31_ctrl->ihistStatsControl.bufToRender =
+			vfe31_process_stats_irq_common(STATS_IHIST_NUM,
+			vfe31_ctrl->ihistStatsControl.nextFrameAddrBuf);
+
+		vfe_send_stats_msg(vfe31_ctrl->ihistStatsControl.bufToRender,
+			STATS_IHIST_NUM);
+	} else {
+		vfe31_ctrl->ihistStatsControl.droppedStatsFrameCount++;
+		CDBG("%s: droppedStatsFrameCount = %d", __func__,
+			vfe31_ctrl->ihistStatsControl.droppedStatsFrameCount);
+	}
+}
+
+static void vfe31_process_stats_rs_irq(void)
+{
+	if (!(vfe31_ctrl->rsStatsControl.ackPending)) {
+		vfe31_ctrl->rsStatsControl.bufToRender =
+			vfe31_process_stats_irq_common(STATS_RS_NUM,
+			vfe31_ctrl->rsStatsControl.nextFrameAddrBuf);
+
+		vfe_send_stats_msg(vfe31_ctrl->rsStatsControl.bufToRender,
+			STATS_RS_NUM);
+	} else {
+		vfe31_ctrl->rsStatsControl.droppedStatsFrameCount++;
+		CDBG("%s: droppedStatsFrameCount = %d", __func__,
+			vfe31_ctrl->rsStatsControl.droppedStatsFrameCount);
+	}
+}
+
+static void vfe31_process_stats_cs_irq(void)
+{
+	if (!(vfe31_ctrl->csStatsControl.ackPending)) {
+		vfe31_ctrl->csStatsControl.bufToRender =
+			vfe31_process_stats_irq_common(STATS_CS_NUM,
+			vfe31_ctrl->csStatsControl.nextFrameAddrBuf);
+
+		vfe_send_stats_msg(vfe31_ctrl->csStatsControl.bufToRender,
+			STATS_CS_NUM);
+	} else {
+		vfe31_ctrl->csStatsControl.droppedStatsFrameCount++;
+		CDBG("%s: droppedStatsFrameCount = %d", __func__,
+			vfe31_ctrl->csStatsControl.droppedStatsFrameCount);
+	}
+}
+
+static void vfe31_process_stats(uint32_t status_bits)
+{
+	unsigned long flags;
+	int32_t process_stats = false;
+	CDBG("%s, stats = 0x%x\n", __func__, status_bits);
+
+	spin_lock_irqsave(&vfe31_ctrl->comp_stats_ack_lock, flags);
+	if (status_bits & VFE_IRQ_STATUS0_STATS_AEC) {
+		if (!vfe31_ctrl->aecStatsControl.ackPending) {
+			vfe31_ctrl->aecStatsControl.ackPending = TRUE;
+			vfe31_ctrl->aecStatsControl.bufToRender =
+				vfe31_process_stats_irq_common(STATS_AE_NUM,
+				vfe31_ctrl->aecStatsControl.nextFrameAddrBuf);
+			process_stats = true;
+		} else{
+			vfe31_ctrl->aecStatsControl.bufToRender = 0;
+			vfe31_ctrl->aecStatsControl.droppedStatsFrameCount++;
+		}
+	} else {
+		vfe31_ctrl->aecStatsControl.bufToRender = 0;
+	}
+
+	if (status_bits & VFE_IRQ_STATUS0_STATS_AWB) {
+		if (!vfe31_ctrl->awbStatsControl.ackPending) {
+			vfe31_ctrl->awbStatsControl.ackPending = TRUE;
+			vfe31_ctrl->awbStatsControl.bufToRender =
+				vfe31_process_stats_irq_common(STATS_AWB_NUM,
+				vfe31_ctrl->awbStatsControl.nextFrameAddrBuf);
+			process_stats = true;
+		} else{
+			vfe31_ctrl->awbStatsControl.droppedStatsFrameCount++;
+			vfe31_ctrl->awbStatsControl.bufToRender = 0;
+		}
+	} else {
+		vfe31_ctrl->awbStatsControl.bufToRender = 0;
+	}
+
+
+	if (status_bits & VFE_IRQ_STATUS0_STATS_AF) {
+		if (!vfe31_ctrl->afStatsControl.ackPending) {
+			vfe31_ctrl->afStatsControl.ackPending = TRUE;
+			vfe31_ctrl->afStatsControl.bufToRender =
+				vfe31_process_stats_irq_common(STATS_AF_NUM,
+				vfe31_ctrl->afStatsControl.nextFrameAddrBuf);
+			process_stats = true;
+		} else {
+			vfe31_ctrl->afStatsControl.bufToRender = 0;
+			vfe31_ctrl->afStatsControl.droppedStatsFrameCount++;
+		}
+	} else {
+		vfe31_ctrl->afStatsControl.bufToRender = 0;
+	}
+
+	if (status_bits & VFE_IRQ_STATUS0_STATS_IHIST) {
+		if (!vfe31_ctrl->ihistStatsControl.ackPending) {
+			vfe31_ctrl->ihistStatsControl.ackPending = TRUE;
+			vfe31_ctrl->ihistStatsControl.bufToRender =
+				vfe31_process_stats_irq_common(STATS_IHIST_NUM,
+				vfe31_ctrl->ihistStatsControl.nextFrameAddrBuf);
+			process_stats = true;
+		} else {
+			vfe31_ctrl->ihistStatsControl.droppedStatsFrameCount++;
+			vfe31_ctrl->ihistStatsControl.bufToRender = 0;
+		}
+	} else {
+		vfe31_ctrl->ihistStatsControl.bufToRender = 0;
+	}
+
+	if (status_bits & VFE_IRQ_STATUS0_STATS_RS) {
+		if (!vfe31_ctrl->rsStatsControl.ackPending) {
+			vfe31_ctrl->rsStatsControl.ackPending = TRUE;
+			vfe31_ctrl->rsStatsControl.bufToRender =
+				vfe31_process_stats_irq_common(STATS_RS_NUM,
+				vfe31_ctrl->rsStatsControl.nextFrameAddrBuf);
+			process_stats = true;
+		} else {
+			vfe31_ctrl->rsStatsControl.droppedStatsFrameCount++;
+			vfe31_ctrl->rsStatsControl.bufToRender = 0;
+		}
+	} else {
+		vfe31_ctrl->rsStatsControl.bufToRender = 0;
+	}
+
+
+	if (status_bits & VFE_IRQ_STATUS0_STATS_CS) {
+		if (!vfe31_ctrl->csStatsControl.ackPending) {
+			vfe31_ctrl->csStatsControl.ackPending = TRUE;
+			vfe31_ctrl->csStatsControl.bufToRender =
+				vfe31_process_stats_irq_common(STATS_CS_NUM,
+				vfe31_ctrl->csStatsControl.nextFrameAddrBuf);
+			process_stats = true;
+		} else {
+			vfe31_ctrl->csStatsControl.droppedStatsFrameCount++;
+			vfe31_ctrl->csStatsControl.bufToRender = 0;
+		}
+	} else {
+		vfe31_ctrl->csStatsControl.bufToRender = 0;
+	}
+
+	spin_unlock_irqrestore(&vfe31_ctrl->comp_stats_ack_lock, flags);
+	if (process_stats)
+		vfe_send_comp_stats_msg(status_bits);
+
+	return;
+}
+
+static void vfe31_process_stats_irq(uint32_t *irqstatus)
+{
+	uint32_t status_bits = VFE_COM_STATUS & *irqstatus;
+
+	if ((vfe31_ctrl->hfr_mode != HFR_MODE_OFF) &&
+		(vfe31_ctrl->vfeFrameId % vfe31_ctrl->hfr_mode != 0)) {
+		CDBG("Skip the stats when HFR enabled\n");
+		return;
+	}
+
+	vfe31_process_stats(status_bits);
+	return;
+}
+
+static void vfe31_do_tasklet(unsigned long data)
+{
+	unsigned long flags;
+
+	struct vfe31_isr_queue_cmd *qcmd = NULL;
+
+	CDBG("=== vfe31_do_tasklet start ===\n");
+
+	while (atomic_read(&irq_cnt)) {
+		spin_lock_irqsave(&vfe31_ctrl->tasklet_lock, flags);
+		qcmd = list_first_entry(&vfe31_ctrl->tasklet_q,
+			struct vfe31_isr_queue_cmd, list);
+		atomic_sub(1, &irq_cnt);
+
+		if (!qcmd) {
+			spin_unlock_irqrestore(&vfe31_ctrl->tasklet_lock,
+				flags);
+			return;
+		}
+
+		list_del(&qcmd->list);
+		spin_unlock_irqrestore(&vfe31_ctrl->tasklet_lock,
+			flags);
+
+		if (qcmd->vfeInterruptStatus0 &
+			VFE_IRQ_STATUS0_CAMIF_SOF_MASK) {
+			CDBG("irq	camifSofIrq\n");
+			vfe31_process_camif_sof_irq();
+		}
+		/* interrupt to be processed,  *qcmd has the payload.  */
+		if (qcmd->vfeInterruptStatus0 &
+			VFE_IRQ_STATUS0_REG_UPDATE_MASK) {
+			CDBG("irq	regUpdateIrq\n");
+			vfe31_process_reg_update_irq();
+		}
+
+		if (qcmd->vfeInterruptStatus1 &
+			VFE_IMASK_WHILE_STOPPING_1) {
+			CDBG("irq	resetAckIrq\n");
+			vfe31_process_reset_irq();
+		}
+
+		if (atomic_read(&vfe31_ctrl->vstate)) {
+			if (qcmd->vfeInterruptStatus1 &
+				VFE31_IMASK_ERROR_ONLY_1) {
+				pr_err("irq	errorIrq\n");
+				vfe31_process_error_irq(
+					qcmd->vfeInterruptStatus1 &
+					VFE31_IMASK_ERROR_ONLY_1);
+			}
+			/* next, check output path related interrupts. */
+			if (qcmd->vfeInterruptStatus0 &
+				VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE0_MASK) {
+				CDBG("Image composite done 0 irq occured.\n");
+				vfe31_process_output_path_irq_0();
+			}
+			if (qcmd->vfeInterruptStatus0 &
+				VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE1_MASK) {
+				CDBG("Image composite done 1 irq occured.\n");
+				vfe31_process_output_path_irq_1();
+			}
+			/* in snapshot mode if done then send
+			snapshot done message */
+			if (vfe31_ctrl->operation_mode ==
+					VFE_OUTPUTS_THUMB_AND_MAIN ||
+				vfe31_ctrl->operation_mode ==
+					VFE_OUTPUTS_MAIN_AND_THUMB ||
+				vfe31_ctrl->operation_mode ==
+					VFE_OUTPUTS_THUMB_AND_JPEG ||
+				vfe31_ctrl->operation_mode ==
+					VFE_OUTPUTS_JPEG_AND_THUMB ||
+				vfe31_ctrl->operation_mode ==
+					VFE_OUTPUTS_RAW) {
+				if ((vfe31_ctrl->outpath.out0.capture_cnt == 0)
+					&& (vfe31_ctrl->outpath.out1.
+					capture_cnt == 0)) {
+					msm_camera_io_w_mb(
+						CAMIF_COMMAND_STOP_IMMEDIATELY,
+						vfe31_ctrl->vfebase +
+						VFE_CAMIF_COMMAND);
+					vfe31_send_isp_msg(vfe31_ctrl,
+						MSG_ID_SNAPSHOT_DONE);
+				}
+			}
+			/* then process stats irq. */
+			if (vfe31_ctrl->stats_comp) {
+				/* process stats comb interrupt. */
+				if (qcmd->vfeInterruptStatus0 &
+					VFE_IRQ_STATUS0_STATS_COMPOSIT_MASK) {
+					CDBG("Stats composite irq occured.\n");
+					vfe31_process_stats_irq(
+						&qcmd->vfeInterruptStatus0);
+				}
+			} else {
+				/* process individual stats interrupt. */
+				if (qcmd->vfeInterruptStatus0 &
+					VFE_IRQ_STATUS0_STATS_AEC) {
+					CDBG("Stats AEC irq occured.\n");
+					vfe31_process_stats_ae_irq();
+				}
+				if (qcmd->vfeInterruptStatus0 &
+					VFE_IRQ_STATUS0_STATS_AWB) {
+					CDBG("Stats AWB irq occured.\n");
+					vfe31_process_stats_awb_irq();
+				}
+				if (qcmd->vfeInterruptStatus0 &
+					VFE_IRQ_STATUS0_STATS_AF) {
+					CDBG("Stats AF irq occured.\n");
+					vfe31_process_stats_af_irq();
+				}
+				if (qcmd->vfeInterruptStatus0 &
+					VFE_IRQ_STATUS0_STATS_IHIST) {
+					CDBG("Stats IHIST irq occured.\n");
+					vfe31_process_stats_ihist_irq();
+				}
+				if (qcmd->vfeInterruptStatus0 &
+					VFE_IRQ_STATUS0_STATS_RS) {
+					CDBG("Stats RS irq occured.\n");
+					vfe31_process_stats_rs_irq();
+				}
+				if (qcmd->vfeInterruptStatus0 &
+					VFE_IRQ_STATUS0_STATS_CS) {
+					CDBG("Stats CS irq occured.\n");
+					vfe31_process_stats_cs_irq();
+				}
+				if (qcmd->vfeInterruptStatus0 &
+					VFE_IRQ_STATUS0_SYNC_TIMER0) {
+					CDBG("SYNC_TIMER 0 irq occured.\n");
+					vfe31_send_isp_msg(vfe31_ctrl,
+						MSG_ID_SYNC_TIMER0_DONE);
+				}
+				if (qcmd->vfeInterruptStatus0 &
+					VFE_IRQ_STATUS0_SYNC_TIMER1) {
+					CDBG("SYNC_TIMER 1 irq occured.\n");
+					vfe31_send_isp_msg(vfe31_ctrl,
+						MSG_ID_SYNC_TIMER1_DONE);
+				}
+				if (qcmd->vfeInterruptStatus0 &
+					VFE_IRQ_STATUS0_SYNC_TIMER2) {
+					CDBG("SYNC_TIMER 2 irq occured.\n");
+					vfe31_send_isp_msg(vfe31_ctrl,
+						MSG_ID_SYNC_TIMER2_DONE);
+				}
+			}
+		}
+		kfree(qcmd);
+	}
+	CDBG("=== vfe31_do_tasklet end ===\n");
+}
+
+DECLARE_TASKLET(vfe31_tasklet, vfe31_do_tasklet, 0);
+
+static irqreturn_t vfe31_parse_irq(int irq_num, void *data)
+{
+	unsigned long flags;
+	struct vfe31_irq_status irq;
+	struct vfe31_isr_queue_cmd *qcmd;
+
+	CDBG("vfe_parse_irq\n");
+
+	vfe31_read_irq_status(&irq);
+
+	if ((irq.vfeIrqStatus0 == 0) && (irq.vfeIrqStatus1 == 0)) {
+		CDBG("vfe_parse_irq: vfeIrqStatus0 & 1 are both 0!\n");
+		return IRQ_HANDLED;
+	}
+
+	qcmd = kzalloc(sizeof(struct vfe31_isr_queue_cmd),
+		GFP_ATOMIC);
+	if (!qcmd) {
+		pr_err("vfe_parse_irq: qcmd malloc failed!\n");
+		return IRQ_HANDLED;
+	}
+
+	spin_lock_irqsave(&vfe31_ctrl->stop_flag_lock, flags);
+	if (vfe31_ctrl->stop_ack_pending) {
+		irq.vfeIrqStatus0 &= VFE_IMASK_WHILE_STOPPING_0;
+		irq.vfeIrqStatus1 &= VFE_IMASK_WHILE_STOPPING_1;
+	}
+	spin_unlock_irqrestore(&vfe31_ctrl->stop_flag_lock, flags);
+
+	CDBG("vfe_parse_irq: Irq_status0 = 0x%x, Irq_status1 = 0x%x.\n",
+		irq.vfeIrqStatus0, irq.vfeIrqStatus1);
+
+	qcmd->vfeInterruptStatus0 = irq.vfeIrqStatus0;
+	qcmd->vfeInterruptStatus1 = irq.vfeIrqStatus1;
+
+	spin_lock_irqsave(&vfe31_ctrl->tasklet_lock, flags);
+	list_add_tail(&qcmd->list, &vfe31_ctrl->tasklet_q);
+
+	atomic_add(1, &irq_cnt);
+	spin_unlock_irqrestore(&vfe31_ctrl->tasklet_lock, flags);
+	tasklet_schedule(&vfe31_tasklet);
+	return IRQ_HANDLED;
+}
+
+static long msm_vfe_subdev_ioctl(struct v4l2_subdev *sd,
+			unsigned int subdev_cmd, void *arg)
+{
+	struct msm_cam_media_controller *pmctl =
+		(struct msm_cam_media_controller *)v4l2_get_subdev_hostdata(sd);
+	struct msm_isp_cmd vfecmd;
+	struct msm_camvfe_params *vfe_params =
+		(struct msm_camvfe_params *)arg;
+	struct msm_vfe_cfg_cmd *cmd = vfe_params->vfe_cfg;
+	void *data = vfe_params->data;
+
+	long rc = 0;
+	uint32_t i = 0;
+	struct vfe_cmd_stats_buf *scfg = NULL;
+	struct msm_pmem_region   *regptr = NULL;
+	struct vfe_cmd_stats_ack *sack = NULL;
+	if (cmd->cmd_type != CMD_CONFIG_PING_ADDR &&
+		cmd->cmd_type != CMD_CONFIG_PONG_ADDR &&
+		cmd->cmd_type != CMD_CONFIG_FREE_BUF_ADDR &&
+		cmd->cmd_type != CMD_STATS_AEC_BUF_RELEASE &&
+		cmd->cmd_type != CMD_STATS_AWB_BUF_RELEASE &&
+		cmd->cmd_type != CMD_STATS_IHIST_BUF_RELEASE &&
+		cmd->cmd_type != CMD_STATS_RS_BUF_RELEASE &&
+		cmd->cmd_type != CMD_STATS_CS_BUF_RELEASE &&
+		cmd->cmd_type != CMD_STATS_AF_BUF_RELEASE) {
+		if (copy_from_user(&vfecmd,
+			(void __user *)(cmd->value),
+			sizeof(vfecmd))) {
+			pr_err("%s %d: copy_from_user failed\n", __func__,
+				__LINE__);
+			return -EFAULT;
+		}
+	} else {
+	/* here eith stats release or frame release. */
+		if (cmd->cmd_type != CMD_CONFIG_PING_ADDR &&
+			cmd->cmd_type != CMD_CONFIG_PONG_ADDR &&
+			cmd->cmd_type != CMD_CONFIG_FREE_BUF_ADDR) {
+			/* then must be stats release. */
+			if (!data)
+				return -EFAULT;
+			sack = kmalloc(sizeof(struct vfe_cmd_stats_ack),
+							GFP_ATOMIC);
+			if (!sack)
+				return -ENOMEM;
+
+			sack->nextStatsBuf = *(uint32_t *)data;
+		}
+	}
+
+	CDBG("%s: cmdType = %d\n", __func__, cmd->cmd_type);
+
+	if ((cmd->cmd_type == CMD_STATS_AF_ENABLE)    ||
+		(cmd->cmd_type == CMD_STATS_AWB_ENABLE)   ||
+		(cmd->cmd_type == CMD_STATS_IHIST_ENABLE) ||
+		(cmd->cmd_type == CMD_STATS_RS_ENABLE)    ||
+		(cmd->cmd_type == CMD_STATS_CS_ENABLE)    ||
+		(cmd->cmd_type == CMD_STATS_AEC_ENABLE)) {
+		struct axidata *axid;
+		axid = data;
+		if (!axid) {
+			rc = -EFAULT;
+			goto vfe31_config_done;
+		}
+
+		scfg =
+			kmalloc(sizeof(struct vfe_cmd_stats_buf),
+				GFP_ATOMIC);
+		if (!scfg) {
+			rc = -ENOMEM;
+			goto vfe31_config_done;
+		}
+		regptr = axid->region;
+		if (axid->bufnum1 > 0) {
+			for (i = 0; i < axid->bufnum1; i++) {
+				scfg->statsBuf[i] =
+					(uint32_t)(regptr->paddr);
+				regptr++;
+			}
+		}
+		/* individual */
+		switch (cmd->cmd_type) {
+		case CMD_STATS_AEC_ENABLE:
+			rc = vfe_stats_aec_buf_init(scfg);
+			break;
+		case CMD_STATS_AF_ENABLE:
+			rc = vfe_stats_af_buf_init(scfg);
+			break;
+		case CMD_STATS_AWB_ENABLE:
+			rc = vfe_stats_awb_buf_init(scfg);
+			break;
+		case CMD_STATS_IHIST_ENABLE:
+			rc = vfe_stats_ihist_buf_init(scfg);
+			break;
+		case CMD_STATS_RS_ENABLE:
+			rc = vfe_stats_rs_buf_init(scfg);
+			break;
+		case CMD_STATS_CS_ENABLE:
+			rc = vfe_stats_cs_buf_init(scfg);
+			break;
+		default:
+			pr_err("%s Unsupported cmd type %d",
+				__func__, cmd->cmd_type);
+			break;
+		}
+		goto vfe31_config_done;
+	}
+	switch (cmd->cmd_type) {
+	case CMD_GENERAL: {
+		rc = vfe31_proc_general(pmctl, &vfecmd);
+		}
+		break;
+	case CMD_CONFIG_PING_ADDR: {
+		int path = *((int *)cmd->value);
+		struct vfe31_output_ch *outch = vfe31_get_ch(path);
+		outch->ping = *((struct msm_free_buf *)data);
+		}
+		break;
+
+	case CMD_CONFIG_PONG_ADDR: {
+		int path = *((int *)cmd->value);
+		struct vfe31_output_ch *outch = vfe31_get_ch(path);
+		outch->pong = *((struct msm_free_buf *)data);
+		}
+		break;
+
+	case CMD_CONFIG_FREE_BUF_ADDR: {
+		int path = *((int *)cmd->value);
+		struct vfe31_output_ch *outch = vfe31_get_ch(path);
+		outch->free_buf = *((struct msm_free_buf *)data);
+		}
+		break;
+
+	case CMD_SNAP_BUF_RELEASE:
+		break;
+
+	case CMD_STATS_AEC_BUF_RELEASE: {
+		vfe31_stats_aec_ack(sack);
+		}
+		break;
+
+	case CMD_STATS_AF_BUF_RELEASE: {
+		vfe31_stats_af_ack(sack);
+		}
+		break;
+
+	case CMD_STATS_AWB_BUF_RELEASE: {
+		vfe31_stats_awb_ack(sack);
+		}
+		break;
+
+	case CMD_STATS_IHIST_BUF_RELEASE: {
+		vfe31_stats_ihist_ack(sack);
+		}
+		break;
+
+	case CMD_STATS_RS_BUF_RELEASE: {
+		vfe31_stats_rs_ack(sack);
+		}
+		break;
+
+	case CMD_STATS_CS_BUF_RELEASE: {
+		vfe31_stats_cs_ack(sack);
+		}
+		break;
+
+	case CMD_AXI_CFG_PRIM: {
+		uint32_t *axio = NULL;
+		axio = kmalloc(vfe31_cmd[VFE_CMD_AXI_OUT_CFG].length,
+				GFP_ATOMIC);
+		if (!axio) {
+			rc = -ENOMEM;
+			break;
+		}
+
+		if (copy_from_user(axio, (void __user *)(vfecmd.value),
+				vfe31_cmd[VFE_CMD_AXI_OUT_CFG].length)) {
+			kfree(axio);
+			rc = -EFAULT;
+			break;
+		}
+		vfe31_config_axi(OUTPUT_PRIM, axio);
+		kfree(axio);
+		}
+		break;
+
+	case CMD_AXI_CFG_PRIM_ALL_CHNLS: {
+		uint32_t *axio = NULL;
+		axio = kmalloc(vfe31_cmd[VFE_CMD_AXI_OUT_CFG].length,
+				GFP_ATOMIC);
+		if (!axio) {
+			rc = -ENOMEM;
+			break;
+		}
+
+		if (copy_from_user(axio, (void __user *)(vfecmd.value),
+				vfe31_cmd[VFE_CMD_AXI_OUT_CFG].length)) {
+			kfree(axio);
+			rc = -EFAULT;
+			break;
+		}
+		vfe31_config_axi(OUTPUT_PRIM_ALL_CHNLS, axio);
+		kfree(axio);
+		}
+		break;
+
+	case CMD_AXI_CFG_PRIM|CMD_AXI_CFG_SEC: {
+		uint32_t *axio = NULL;
+		axio = kmalloc(vfe31_cmd[VFE_CMD_AXI_OUT_CFG].length,
+				GFP_ATOMIC);
+		if (!axio) {
+			rc = -ENOMEM;
+			break;
+		}
+
+		if (copy_from_user(axio, (void __user *)(vfecmd.value),
+				vfe31_cmd[VFE_CMD_AXI_OUT_CFG].length)) {
+			kfree(axio);
+			rc = -EFAULT;
+			break;
+		}
+		vfe31_config_axi(OUTPUT_PRIM|OUTPUT_SEC, axio);
+		kfree(axio);
+		}
+		break;
+
+	case CMD_AXI_CFG_PRIM|CMD_AXI_CFG_SEC_ALL_CHNLS: {
+		uint32_t *axio = NULL;
+		axio = kmalloc(vfe31_cmd[VFE_CMD_AXI_OUT_CFG].length,
+				GFP_ATOMIC);
+		if (!axio) {
+			rc = -ENOMEM;
+			break;
+		}
+
+		if (copy_from_user(axio, (void __user *)(vfecmd.value),
+				vfe31_cmd[VFE_CMD_AXI_OUT_CFG].length)) {
+			kfree(axio);
+			rc = -EFAULT;
+			break;
+		}
+		vfe31_config_axi(OUTPUT_PRIM|OUTPUT_SEC_ALL_CHNLS, axio);
+		kfree(axio);
+		}
+		break;
+
+	case CMD_AXI_CFG_PRIM_ALL_CHNLS|CMD_AXI_CFG_SEC: {
+		uint32_t *axio = NULL;
+		axio = kmalloc(vfe31_cmd[VFE_CMD_AXI_OUT_CFG].length,
+				GFP_ATOMIC);
+		if (!axio) {
+			rc = -ENOMEM;
+			break;
+		}
+
+		if (copy_from_user(axio, (void __user *)(vfecmd.value),
+				vfe31_cmd[VFE_CMD_AXI_OUT_CFG].length)) {
+			kfree(axio);
+			rc = -EFAULT;
+			break;
+		}
+		vfe31_config_axi(OUTPUT_PRIM_ALL_CHNLS|OUTPUT_SEC, axio);
+		kfree(axio);
+		}
+		break;
+
+	case CMD_AXI_CFG_PRIM_ALL_CHNLS|CMD_AXI_CFG_SEC_ALL_CHNLS: {
+		pr_err("%s Invalid/Unsupported AXI configuration %x",
+			__func__, cmd->cmd_type);
+		}
+		break;
+
+	default:
+		pr_err("%s Unsupported AXI configuration %x ", __func__,
+			cmd->cmd_type);
+		break;
+	}
+vfe31_config_done:
+	kfree(scfg);
+	kfree(sack);
+	CDBG("%s done: rc = %d\n", __func__, (int) rc);
+	return rc;
+}
+
+static int msm_vfe_subdev_s_crystal_freq(struct v4l2_subdev *sd,
+	u32 freq, u32 flags)
+{
+	int rc = 0;
+	int round_rate;
+
+	round_rate = clk_round_rate(vfe31_ctrl->vfe_clk[0], freq);
+	if (rc < 0) {
+		pr_err("%s: clk_round_rate failed %d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	vfe_clk_rate = round_rate;
+	rc = clk_set_rate(vfe31_ctrl->vfe_clk[0], round_rate);
+	if (rc < 0)
+		pr_err("%s: clk_set_rate failed %d\n",
+			__func__, rc);
+
+	return rc;
+}
+
+static const struct v4l2_subdev_video_ops msm_vfe_subdev_video_ops = {
+	.s_crystal_freq = msm_vfe_subdev_s_crystal_freq,
+};
+
+static const struct v4l2_subdev_core_ops msm_vfe_subdev_core_ops = {
+	.ioctl = msm_vfe_subdev_ioctl,
+};
+
+static const struct v4l2_subdev_ops msm_vfe_subdev_ops = {
+	.core = &msm_vfe_subdev_core_ops,
+	.video = &msm_vfe_subdev_video_ops,
+};
+
+static struct msm_cam_clk_info vfe_clk_info[] = {
+	{"vfe_clk", VFE_CLK_RATE},
+	{"vfe_pclk", -1},
+};
+
+static struct msm_cam_clk_info vfe_camif_clk_info[] = {
+	{"camif_pad_pclk", -1},
+	{"vfe_camif_clk", -1},
+};
+
+static void msm_vfe_camio_clk_sel(enum msm_camio_clk_src_type srctype)
+{
+	struct clk *clk = NULL;
+
+	clk = vfe31_ctrl->vfe_clk[0];
+
+	if (clk != NULL) {
+		switch (srctype) {
+		case MSM_CAMIO_CLK_SRC_INTERNAL:
+			clk_set_flags(clk, 0x00000100 << 1);
+			break;
+
+		case MSM_CAMIO_CLK_SRC_EXTERNAL:
+			clk_set_flags(clk, 0x00000100);
+			break;
+
+		default:
+			break;
+		}
+	}
+}
+
+static void msm_vfe_camif_pad_reg_reset(void)
+{
+	uint32_t reg;
+
+	msm_vfe_camio_clk_sel(MSM_CAMIO_CLK_SRC_INTERNAL);
+	usleep_range(10000, 15000);
+
+	reg = (msm_camera_io_r(vfe31_ctrl->camifbase)) & CAMIF_CFG_RMSK;
+	reg |= 0x3;
+	msm_camera_io_w(reg, vfe31_ctrl->camifbase);
+	usleep_range(10000, 15000);
+
+	reg = (msm_camera_io_r(vfe31_ctrl->camifbase)) & CAMIF_CFG_RMSK;
+	reg |= 0x10;
+	msm_camera_io_w(reg, vfe31_ctrl->camifbase);
+	usleep_range(10000, 15000);
+
+	reg = (msm_camera_io_r(vfe31_ctrl->camifbase)) & CAMIF_CFG_RMSK;
+	/* Need to be uninverted*/
+	reg &= 0x03;
+	msm_camera_io_w(reg, vfe31_ctrl->camifbase);
+	usleep_range(10000, 15000);
+}
+
+int msm_vfe_subdev_init(struct v4l2_subdev *sd,
+		struct msm_cam_media_controller *mctl)
+{
+	int rc = 0;
+	v4l2_set_subdev_hostdata(sd, mctl);
+
+	spin_lock_init(&vfe31_ctrl->stop_flag_lock);
+	spin_lock_init(&vfe31_ctrl->state_lock);
+	spin_lock_init(&vfe31_ctrl->io_lock);
+	spin_lock_init(&vfe31_ctrl->update_ack_lock);
+	spin_lock_init(&vfe31_ctrl->tasklet_lock);
+
+	spin_lock_init(&vfe31_ctrl->aec_ack_lock);
+	spin_lock_init(&vfe31_ctrl->awb_ack_lock);
+	spin_lock_init(&vfe31_ctrl->af_ack_lock);
+	spin_lock_init(&vfe31_ctrl->ihist_ack_lock);
+	spin_lock_init(&vfe31_ctrl->rs_ack_lock);
+	spin_lock_init(&vfe31_ctrl->cs_ack_lock);
+	spin_lock_init(&vfe31_ctrl->comp_stats_ack_lock);
+	spin_lock_init(&vfe31_ctrl->sd_notify_lock);
+	INIT_LIST_HEAD(&vfe31_ctrl->tasklet_q);
+
+	vfe31_ctrl->update_linear = false;
+	vfe31_ctrl->update_rolloff = false;
+	vfe31_ctrl->update_la = false;
+	vfe31_ctrl->update_gamma = false;
+	vfe31_ctrl->hfr_mode = HFR_MODE_OFF;
+
+	vfe31_ctrl->vfebase = ioremap(vfe31_ctrl->vfemem->start,
+		resource_size(vfe31_ctrl->vfemem));
+	if (!vfe31_ctrl->vfebase) {
+		rc = -ENOMEM;
+		pr_err("%s: vfe ioremap failed\n", __func__);
+		goto vfe_remap_failed;
+	}
+	if (!mctl->sdata->csi_if) {
+		vfe31_ctrl->camifbase = ioremap(vfe31_ctrl->camifmem->start,
+			resource_size(vfe31_ctrl->camifmem));
+		if (!vfe31_ctrl->camifbase) {
+			rc = -ENOMEM;
+			pr_err("%s: camif ioremap failed\n", __func__);
+			goto camif_remap_failed;
+		}
+	}
+
+	if (vfe31_ctrl->fs_vfe == NULL) {
+		vfe31_ctrl->fs_vfe =
+			regulator_get(&vfe31_ctrl->pdev->dev, "fs_vfe");
+		if (IS_ERR(vfe31_ctrl->fs_vfe)) {
+			pr_err("%s: Regulator FS_VFE get failed %ld\n",
+				__func__, PTR_ERR(vfe31_ctrl->fs_vfe));
+			vfe31_ctrl->fs_vfe = NULL;
+			goto vfe_fs_failed;
+		} else if (regulator_enable(vfe31_ctrl->fs_vfe)) {
+			pr_err("%s: Regulator FS_VFE enable failed\n",
+							__func__);
+			regulator_put(vfe31_ctrl->fs_vfe);
+			vfe31_ctrl->fs_vfe = NULL;
+			goto vfe_fs_failed;
+		}
+	}
+
+	rc = msm_cam_clk_enable(&vfe31_ctrl->pdev->dev, vfe_clk_info,
+		vfe31_ctrl->vfe_clk, ARRAY_SIZE(vfe_clk_info), 1);
+	if (rc < 0)
+		goto vfe_clk_enable_failed;
+
+	if (!mctl->sdata->csi_if) {
+		rc = msm_cam_clk_enable(&vfe31_ctrl->pdev->dev,
+			vfe_camif_clk_info,
+			vfe31_ctrl->vfe_camif_clk,
+			ARRAY_SIZE(vfe_camif_clk_info), 1);
+		if (rc < 0)
+			goto vfe_clk_enable_failed;
+		msm_vfe_camif_pad_reg_reset();
+	}
+
+	msm_camio_bus_scale_cfg(
+		mctl->sdata->pdata->cam_bus_scale_table, S_INIT);
+	msm_camio_bus_scale_cfg(
+		mctl->sdata->pdata->cam_bus_scale_table, S_PREVIEW);
+	vfe31_ctrl->register_total = VFE31_REGISTER_TOTAL;
+
+	enable_irq(vfe31_ctrl->vfeirq->start);
+
+	return rc;
+
+vfe_clk_enable_failed:
+	regulator_disable(vfe31_ctrl->fs_vfe);
+	regulator_put(vfe31_ctrl->fs_vfe);
+	vfe31_ctrl->fs_vfe = NULL;
+vfe_fs_failed:
+	if (!mctl->sdata->csi_if)
+		iounmap(vfe31_ctrl->camifbase);
+camif_remap_failed:
+	iounmap(vfe31_ctrl->vfebase);
+vfe_remap_failed:
+	disable_irq(vfe31_ctrl->vfeirq->start);
+	return rc;
+}
+
+void msm_vfe_subdev_release(struct v4l2_subdev *sd)
+{
+	struct msm_cam_media_controller *pmctl =
+		(struct msm_cam_media_controller *)v4l2_get_subdev_hostdata(sd);
+	disable_irq(vfe31_ctrl->vfeirq->start);
+	tasklet_kill(&vfe31_tasklet);
+
+	if (!pmctl->sdata->csi_if)
+		msm_cam_clk_enable(&vfe31_ctrl->pdev->dev,
+			vfe_camif_clk_info,
+			vfe31_ctrl->vfe_camif_clk,
+			ARRAY_SIZE(vfe_camif_clk_info), 0);
+
+	msm_cam_clk_enable(&vfe31_ctrl->pdev->dev, vfe_clk_info,
+		vfe31_ctrl->vfe_clk, ARRAY_SIZE(vfe_clk_info), 0);
+	if (vfe31_ctrl->fs_vfe) {
+		regulator_disable(vfe31_ctrl->fs_vfe);
+		regulator_put(vfe31_ctrl->fs_vfe);
+		vfe31_ctrl->fs_vfe = NULL;
+	}
+	CDBG("%s, 31ee_irq\n", __func__);
+	if (!pmctl->sdata->csi_if)
+		iounmap(vfe31_ctrl->camifbase);
+	iounmap(vfe31_ctrl->vfebase);
+
+	if (atomic_read(&irq_cnt))
+		pr_warning("%s, Warning IRQ Count not ZERO\n", __func__);
+
+	msm_camio_bus_scale_cfg(
+		pmctl->sdata->pdata->cam_bus_scale_table, S_EXIT);
+}
+
+static const struct v4l2_subdev_internal_ops msm_vfe_internal_ops;
+
+static int __devinit vfe31_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	CDBG("%s: device id = %d\n", __func__, pdev->id);
+	vfe31_ctrl = kzalloc(sizeof(struct vfe31_ctrl_type), GFP_KERNEL);
+	if (!vfe31_ctrl) {
+		pr_err("%s: no enough memory\n", __func__);
+		return -ENOMEM;
+	}
+
+	v4l2_subdev_init(&vfe31_ctrl->subdev, &msm_vfe_subdev_ops);
+	vfe31_ctrl->subdev.internal_ops = &msm_vfe_internal_ops;
+	vfe31_ctrl->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	snprintf(vfe31_ctrl->subdev.name,
+			 sizeof(vfe31_ctrl->subdev.name), "vfe3.1");
+	v4l2_set_subdevdata(&vfe31_ctrl->subdev, vfe31_ctrl);
+	platform_set_drvdata(pdev, &vfe31_ctrl->subdev);
+
+	vfe31_ctrl->vfemem = platform_get_resource_byname(pdev,
+		IORESOURCE_MEM, "msm_vfe");
+	if (!vfe31_ctrl->vfemem) {
+		pr_err("%s: no mem resource?\n", __func__);
+		rc = -ENODEV;
+		goto vfe31_no_resource;
+	}
+	vfe31_ctrl->vfeirq = platform_get_resource_byname(pdev,
+		IORESOURCE_IRQ, "msm_vfe");
+	if (!vfe31_ctrl->vfeirq) {
+		pr_err("%s: no irq resource?\n", __func__);
+		rc = -ENODEV;
+		goto vfe31_no_resource;
+	}
+	vfe31_ctrl->camifmem = platform_get_resource_byname(pdev,
+		IORESOURCE_MEM, "msm_camif");
+	if (!vfe31_ctrl->camifmem)
+		pr_err("%s: camif not supported\n", __func__);
+
+	vfe31_ctrl->vfeio = request_mem_region(vfe31_ctrl->vfemem->start,
+		resource_size(vfe31_ctrl->vfemem), pdev->name);
+	if (!vfe31_ctrl->vfeio) {
+		pr_err("%s: no valid mem region\n", __func__);
+		rc = -EBUSY;
+		goto vfe31_no_resource;
+	}
+
+	if (vfe31_ctrl->camifmem) {
+		vfe31_ctrl->camifio = request_mem_region(
+			vfe31_ctrl->camifmem->start,
+			resource_size(vfe31_ctrl->camifmem), pdev->name);
+		if (!vfe31_ctrl->camifio) {
+			release_mem_region(vfe31_ctrl->vfemem->start,
+				resource_size(vfe31_ctrl->vfemem));
+			pr_err("%s: no valid mem region\n", __func__);
+			rc = -EBUSY;
+			goto vfe31_no_resource;
+		}
+	}
+
+	rc = request_irq(vfe31_ctrl->vfeirq->start, vfe31_parse_irq,
+		IRQF_TRIGGER_RISING, "vfe", 0);
+	if (rc < 0) {
+		if (vfe31_ctrl->camifmem) {
+			release_mem_region(vfe31_ctrl->camifmem->start,
+				resource_size(vfe31_ctrl->camifmem));
+		}
+		release_mem_region(vfe31_ctrl->vfemem->start,
+			resource_size(vfe31_ctrl->vfemem));
+		pr_err("%s: irq request fail\n", __func__);
+		rc = -EBUSY;
+		goto vfe31_no_resource;
+	}
+
+	disable_irq(vfe31_ctrl->vfeirq->start);
+
+	vfe31_ctrl->pdev = pdev;
+	msm_cam_register_subdev_node(&vfe31_ctrl->subdev, VFE_DEV, 0);
+	return 0;
+
+vfe31_no_resource:
+	kfree(vfe31_ctrl);
+	return 0;
+}
+
+static struct platform_driver vfe31_driver = {
+	.probe = vfe31_probe,
+	.driver = {
+	.name = MSM_VFE_DRV_NAME,
+	.owner = THIS_MODULE,
+	},
+};
+
+static int __init msm_vfe31_init_module(void)
+{
+	return platform_driver_register(&vfe31_driver);
+}
+
+static void __exit msm_vfe31_exit_module(void)
+{
+	platform_driver_unregister(&vfe31_driver);
+}
+
+module_init(msm_vfe31_init_module);
+module_exit(msm_vfe31_exit_module);
+MODULE_DESCRIPTION("VFE 3.1 driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/msm_vfe31_v4l2.h b/drivers/media/video/msm/msm_vfe31_v4l2.h
new file mode 100644
index 0000000..e94f286
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe31_v4l2.h
@@ -0,0 +1,955 @@
+/* Copyright (c) 2012 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __MSM_VFE31_V4L2_H__
+#define __MSM_VFE31_V4L2_H__
+
+#include <linux/bitops.h>
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+/* This defines total number registers in VFE.
+ * Each register is 4 bytes so to get the range,
+ * multiply this number with 4. */
+#define VFE31_REGISTER_TOTAL 0x0000017F
+
+/* at start of camif,  bit 1:0 = 0x01:enable
+ * image data capture at frame boundary. */
+#define CAMIF_COMMAND_START  0x00000005
+
+/* bit 2= 0x1:clear the CAMIF_STATUS register
+ * value. */
+#define CAMIF_COMMAND_CLEAR  0x00000004
+
+/* at stop of vfe pipeline, for now it is assumed
+ * that camif will stop at any time. Bit 1:0 = 0x10:
+ * disable image data capture immediately. */
+#define CAMIF_COMMAND_STOP_IMMEDIATELY  0x00000002
+
+/* at stop of vfe pipeline, for now it is assumed
+ * that camif will stop at any time. Bit 1:0 = 0x00:
+ * disable image data capture at frame boundary */
+#define CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY  0x00000000
+
+/* to halt axi bridge */
+#define AXI_HALT  0x00000001
+
+/* clear the halt bit. */
+#define AXI_HALT_CLEAR  0x00000000
+
+/* clear axi_halt_irq */
+#define MASK_AXI_HALT_IRQ	0xFF7FFFFF
+
+/* reset the pipeline when stop command is issued.
+ * (without reset the register.) bit 26-31 = 0,
+ * domain reset, bit 0-9 = 1 for module reset, except
+ * register module. */
+#define VFE_RESET_UPON_STOP_CMD  0x000003ef
+
+/* reset the pipeline when reset command.
+ * bit 26-31 = 0, domain reset, bit 0-9 = 1 for module reset. */
+#define VFE_RESET_UPON_RESET_CMD  0x000003ff
+
+/* bit 5 is for axi status idle or busy.
+ * 1 =  halted,  0 = busy */
+#define AXI_STATUS_BUSY_MASK 0x00000020
+
+/* bit 0 & bit 1 = 1, both y and cbcr irqs need to be present
+ * for frame done interrupt */
+#define VFE_COMP_IRQ_BOTH_Y_CBCR 3
+
+/* bit 1 = 1, only cbcr irq triggers frame done interrupt */
+#define VFE_COMP_IRQ_CBCR_ONLY 2
+
+/* bit 0 = 1, only y irq triggers frame done interrupt */
+#define VFE_COMP_IRQ_Y_ONLY 1
+
+/* bit 0 = 1, PM go;   bit1 = 1, PM stop */
+#define VFE_PERFORMANCE_MONITOR_GO   0x00000001
+#define VFE_PERFORMANCE_MONITOR_STOP 0x00000002
+
+/* bit 0 = 1, test gen go;   bit1 = 1, test gen stop */
+#define VFE_TEST_GEN_GO   0x00000001
+#define VFE_TEST_GEN_STOP 0x00000002
+
+/* the chroma is assumed to be interpolated between
+ * the luma samples.  JPEG 4:2:2 */
+#define VFE_CHROMA_UPSAMPLE_INTERPOLATED 0
+
+/* constants for irq registers */
+#define VFE_DISABLE_ALL_IRQS 0
+/* bit =1 is to clear the corresponding bit in VFE_IRQ_STATUS.  */
+#define VFE_CLEAR_ALL_IRQS   0xffffffff
+
+#define VFE_IRQ_STATUS0_CAMIF_SOF_MASK            0x00000001
+#define VFE_IRQ_STATUS0_CAMIF_EOF_MASK            0x00000004
+#define VFE_IRQ_STATUS0_REG_UPDATE_MASK           0x00000020
+#define VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE0_MASK 0x00200000
+#define VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE1_MASK 0x00400000
+#define VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE2_MASK 0x00800000
+#define VFE_IRQ_STATUS1_RESET_AXI_HALT_ACK_MASK   0x00800000
+#define VFE_IRQ_STATUS0_STATS_COMPOSIT_MASK       0x01000000
+
+#define VFE_IRQ_STATUS0_STATS_AEC     0x2000  /* bit 13 */
+#define VFE_IRQ_STATUS0_STATS_AF      0x4000  /* bit 14 */
+#define VFE_IRQ_STATUS0_STATS_AWB     0x8000  /* bit 15 */
+#define VFE_IRQ_STATUS0_STATS_RS      0x10000  /* bit 16 */
+#define VFE_IRQ_STATUS0_STATS_CS      0x20000  /* bit 17 */
+#define VFE_IRQ_STATUS0_STATS_IHIST   0x40000  /* bit 18 */
+
+#define VFE_IRQ_STATUS0_SYNC_TIMER0   0x2000000  /* bit 25 */
+#define VFE_IRQ_STATUS0_SYNC_TIMER1   0x4000000  /* bit 26 */
+#define VFE_IRQ_STATUS0_SYNC_TIMER2   0x8000000  /* bit 27 */
+#define VFE_IRQ_STATUS0_ASYNC_TIMER0  0x10000000  /* bit 28 */
+#define VFE_IRQ_STATUS0_ASYNC_TIMER1  0x20000000  /* bit 29 */
+#define VFE_IRQ_STATUS0_ASYNC_TIMER2  0x40000000  /* bit 30 */
+#define VFE_IRQ_STATUS0_ASYNC_TIMER3  0x80000000  /* bit 31 */
+
+/* imask for while waiting for stop ack,  driver has already
+ * requested stop, waiting for reset irq, and async timer irq.
+ * For irq_status_0, bit 28-31 are for async timer. For
+ * irq_status_1, bit 22 for reset irq, bit 23 for axi_halt_ack
+   irq */
+#define VFE_IMASK_WHILE_STOPPING_0  0xF0000000
+#define VFE_IMASK_WHILE_STOPPING_1  0x00C00000
+#define VFE_IMASK_RESET             0x00400000
+#define VFE_IMASK_AXI_HALT          0x00800000
+
+
+/* no error irq in mask 0 */
+#define VFE_IMASK_ERROR_ONLY_0  0x0
+/* when normal case, don't want to block error status. */
+/* bit 0-21 are error irq bits */
+#define VFE_IMASK_ERROR_ONLY_1  0x003fffff
+
+/* For BPC bit 0,bit 12-17 and bit 26 -20 are set to zero and other's 1 */
+#define BPC_MASK 0xF80C0FFE
+
+/* For BPC bit 1 and 2 are set to zero and other's 1 */
+#define ABF_MASK 0xFFFFFFF9
+
+/* For DBPC bit 0 is set to zero and other's 1 */
+#define DBPC_MASK 0xFFFFFFFE
+
+/* For DBCC bit 1 is set to zero and other's 1 */
+#define DBCC_MASK 0xFFFFFFFD
+
+/* For DBPC/ABF/DBCC/ABCC bits are set to 1 all others 0 */
+#define DEMOSAIC_MASK 0x8FFFFFFF
+/* For MCE enable bit 28 set to zero and other's 1 */
+#define MCE_EN_MASK 0xEFFFFFFF
+
+/* For MCE Q_K bit 28 to 31 set to zero and other's 1 */
+#define MCE_Q_K_MASK 0x0FFFFFFF
+
+#define AE_BG_ENABLE_MASK 0x00000020      /* bit 5 */
+#define AF_BF_ENABLE_MASK 0x00000040      /* bit 6 */
+#define AWB_ENABLE_MASK 0x00000080     /* bit 7 */
+
+#define RS_ENABLE_MASK 0x00000100      /* bit 8  */
+#define CS_ENABLE_MASK 0x00000200      /* bit 9  */
+#define RS_CS_ENABLE_MASK 0x00000300   /* bit 8,9  */
+#define IHIST_ENABLE_MASK 0x00008000   /* bit 15 */
+#define STATS_ENABLE_MASK 0x000483E0   /* bit 18,15,9,8,7,6,5*/
+
+#define VFE_REG_UPDATE_TRIGGER           1
+#define VFE_PM_BUF_MAX_CNT_MASK          0xFF
+#define VFE_DMI_CFG_DEFAULT              0x00000100
+#define VFE_AE_PINGPONG_STATUS_BIT       0x80
+#define VFE_AF_PINGPONG_STATUS_BIT       0x100
+#define VFE_AWB_PINGPONG_STATUS_BIT      0x200
+
+#define HFR_MODE_OFF 1
+#define VFE_FRAME_SKIP_PERIOD_MASK 0x0000001F /*bits 0 -4*/
+
+enum VFE31_DMI_RAM_SEL {
+	 NO_MEM_SELECTED          = 0,
+	 ROLLOFF_RAM              = 0x1,
+	 RGBLUT_RAM_CH0_BANK0     = 0x2,
+	 RGBLUT_RAM_CH0_BANK1     = 0x3,
+	 RGBLUT_RAM_CH1_BANK0     = 0x4,
+	 RGBLUT_RAM_CH1_BANK1     = 0x5,
+	 RGBLUT_RAM_CH2_BANK0     = 0x6,
+	 RGBLUT_RAM_CH2_BANK1     = 0x7,
+	 STATS_HIST_RAM           = 0x8,
+	 RGBLUT_CHX_BANK0         = 0x9,
+	 RGBLUT_CHX_BANK1         = 0xa,
+	 LUMA_ADAPT_LUT_RAM_BANK0 = 0xb,
+	 LUMA_ADAPT_LUT_RAM_BANK1 = 0xc
+};
+
+enum vfe_output_state {
+	VFE_STATE_IDLE,
+	VFE_STATE_START_REQUESTED,
+	VFE_STATE_STARTED,
+	VFE_STATE_STOP_REQUESTED,
+	VFE_STATE_STOPPED,
+};
+
+#define V31_CAMIF_OFF             0x000001E4
+#define V31_CAMIF_LEN             32
+
+#define V31_DEMUX_OFF             0x00000284
+#define V31_DEMUX_LEN             20
+
+#define V31_DEMOSAICV3_UP_REG_CNT 5
+
+#define V31_OUT_CLAMP_OFF         0x00000524
+#define V31_OUT_CLAMP_LEN         8
+
+#define V31_OPERATION_CFG_LEN     32
+
+#define V31_AXI_OUT_OFF           0x00000038
+#define V31_AXI_OUT_LEN           212
+#define V31_AXI_CH_INF_LEN        24
+#define V31_AXI_CFG_LEN           47
+#define V31_AXI_RESERVED            1
+
+#define V31_FRAME_SKIP_OFF        0x00000504
+#define V31_FRAME_SKIP_LEN        32
+
+#define V31_CHROMA_SUBS_OFF       0x000004F8
+#define V31_CHROMA_SUBS_LEN       12
+
+#define V31_FOV_OFF           0x00000360
+#define V31_FOV_LEN           8
+
+#define V31_MAIN_SCALER_OFF 0x00000368
+#define V31_MAIN_SCALER_LEN 28
+
+#define V31_S2Y_OFF 0x000004D0
+#define V31_S2Y_LEN 20
+
+#define V31_S2CbCr_OFF 0x000004E4
+#define V31_S2CbCr_LEN 20
+
+#define V31_CHROMA_EN_OFF 0x000003C4
+#define V31_CHROMA_EN_LEN 36
+
+#define V31_SYNC_TIMER_OFF      0x0000020C
+#define V31_SYNC_TIMER_POLARITY_OFF 0x00000234
+#define V31_TIMER_SELECT_OFF        0x0000025C
+#define V31_SYNC_TIMER_LEN 28
+
+#define V31_ASYNC_TIMER_OFF 0x00000238
+#define V31_ASYNC_TIMER_LEN 28
+
+#define V31_BLACK_LEVEL_OFF 0x00000264
+#define V31_BLACK_LEVEL_LEN 16
+
+#define V31_MESH_ROLL_OFF_CFG_OFF             0x00000274
+#define V31_MESH_ROLL_OFF_CFG_LEN             16
+#define V31_MESH_ROLL_OFF_INIT_TABLE_SIZE     13
+#define V31_MESH_ROLL_OFF_DELTA_TABLE_SIZE    208
+#define V31_MESH_ROLL_OFF_DELTA_TABLE_OFFSET  32
+
+#define V31_COLOR_COR_OFF 0x00000388
+#define V31_COLOR_COR_LEN 52
+
+#define V31_WB_OFF 0x00000384
+#define V31_WB_LEN 4
+
+#define V31_RGB_G_OFF 0x000003BC
+#define V31_RGB_G_LEN 4
+
+#define V31_LA_OFF 0x000003C0
+#define V31_LA_LEN 4
+
+#define V31_SCE_OFF 0x00000418
+#define V31_SCE_LEN 136
+
+#define V31_CHROMA_SUP_OFF 0x000003E8
+#define V31_CHROMA_SUP_LEN 12
+
+#define V31_MCE_OFF 0x000003F4
+#define V31_MCE_LEN 36
+#define V31_STATS_AF_OFF 0x0000053c
+#define V31_STATS_AF_LEN 16
+
+#define V31_STATS_AE_OFF 0x00000534
+#define V31_STATS_AE_LEN 8
+
+#define V31_STATS_AWB_OFF 0x0000054c
+#define V31_STATS_AWB_LEN 32
+
+#define V31_STATS_IHIST_OFF 0x0000057c
+#define V31_STATS_IHIST_LEN 8
+
+#define V31_STATS_RS_OFF 0x0000056c
+#define V31_STATS_RS_LEN 8
+
+#define V31_STATS_CS_OFF 0x00000574
+#define V31_STATS_CS_LEN 8
+
+#define V31_ASF_OFF 0x000004A0
+#define V31_ASF_LEN 48
+#define V31_ASF_UPDATE_LEN 36
+#define V31_CAPTURE_LEN 4
+#define V31_GET_HW_VERSION_OFF 0
+#define V31_GET_HW_VERSION_LEN 4
+#define V31_DEMOSAICV3_OFF 0x00000298
+#define V31_DEMOSAICV3_LEN 4
+/* BPC     */
+#define V31_DEMOSAICV3_DBPC_CFG_OFF  0x0000029C
+#define V31_DEMOSAICV3_DBPC_LEN 8
+#define V31_XBAR_CFG_OFF 0x00000040
+/* ABF     */
+#define V31_DEMOSAICV3_ABF_OFF 0x000002A4
+#define V31_DEMOSAICV3_ABF_LEN 180
+#define V31_XBAR_CFG_LEN 8
+
+#define V31_MODULE_CFG_OFF 0x00000010
+#define V31_MODULE_CFG_LEN 4
+#define V31_EZTUNE_CFG_OFF 0x00000010
+#define V31_EZTUNE_CFG_LEN 4
+
+struct vfe_cmd_hw_version {
+	uint32_t minorVersion;
+	uint32_t majorVersion;
+	uint32_t coreVersion;
+};
+
+enum VFE_AXI_OUTPUT_MODE {
+	VFE_AXI_OUTPUT_MODE_Output1,
+	VFE_AXI_OUTPUT_MODE_Output2,
+	VFE_AXI_OUTPUT_MODE_Output1AndOutput2,
+	VFE_AXI_OUTPUT_MODE_CAMIFToAXIViaOutput2,
+	VFE_AXI_OUTPUT_MODE_Output2AndCAMIFToAXIViaOutput1,
+	VFE_AXI_OUTPUT_MODE_Output1AndCAMIFToAXIViaOutput2,
+	VFE_AXI_LAST_OUTPUT_MODE_ENUM
+};
+
+enum VFE_RAW_WR_PATH_SEL {
+	VFE_RAW_OUTPUT_DISABLED,
+	VFE_RAW_OUTPUT_ENC_CBCR_PATH,
+	VFE_RAW_OUTPUT_VIEW_CBCR_PATH,
+	VFE_RAW_OUTPUT_PATH_INVALID
+};
+
+#define VFE_AXI_OUTPUT_BURST_LENGTH     4
+#define VFE_MAX_NUM_FRAGMENTS_PER_FRAME 4
+#define VFE_AXI_OUTPUT_CFG_FRAME_COUNT  3
+
+struct vfe_cmds_per_write_master {
+	uint16_t imageWidth;
+	uint16_t imageHeight;
+	uint16_t outRowCount;
+	uint16_t outRowIncrement;
+	uint32_t outFragments[VFE_AXI_OUTPUT_CFG_FRAME_COUNT]
+		[VFE_MAX_NUM_FRAGMENTS_PER_FRAME];
+};
+
+struct vfe_cmds_axi_per_output_path {
+	uint8_t fragmentCount;
+	struct vfe_cmds_per_write_master firstWM;
+	struct vfe_cmds_per_write_master secondWM;
+};
+
+enum VFE_AXI_BURST_LENGTH {
+	VFE_AXI_BURST_LENGTH_IS_2  = 2,
+	VFE_AXI_BURST_LENGTH_IS_4  = 4,
+	VFE_AXI_BURST_LENGTH_IS_8  = 8,
+	VFE_AXI_BURST_LENGTH_IS_16 = 16
+};
+
+struct vfe_cmd_fov_crop_config {
+	uint8_t enable;
+	uint16_t firstPixel;
+	uint16_t lastPixel;
+	uint16_t firstLine;
+	uint16_t lastLine;
+};
+
+struct vfe_cmds_main_scaler_stripe_init {
+	uint16_t MNCounterInit;
+	uint16_t phaseInit;
+};
+
+struct vfe_cmds_scaler_one_dimension {
+	uint8_t  enable;
+	uint16_t inputSize;
+	uint16_t outputSize;
+	uint32_t phaseMultiplicationFactor;
+	uint8_t  interpolationResolution;
+};
+
+struct vfe_cmd_main_scaler_config {
+	uint8_t enable;
+	struct vfe_cmds_scaler_one_dimension    hconfig;
+	struct vfe_cmds_scaler_one_dimension    vconfig;
+	struct vfe_cmds_main_scaler_stripe_init MNInitH;
+	struct vfe_cmds_main_scaler_stripe_init MNInitV;
+};
+
+struct vfe_cmd_scaler2_config {
+	uint8_t enable;
+	struct vfe_cmds_scaler_one_dimension hconfig;
+	struct vfe_cmds_scaler_one_dimension vconfig;
+};
+
+
+struct vfe_cmd_frame_skip_update {
+	uint32_t output1Pattern;
+	uint32_t output2Pattern;
+};
+
+struct vfe_cmd_output_clamp_config {
+	uint8_t minCh0;
+	uint8_t minCh1;
+	uint8_t minCh2;
+	uint8_t maxCh0;
+	uint8_t maxCh1;
+	uint8_t maxCh2;
+};
+
+struct vfe_cmd_chroma_subsample_config {
+	uint8_t enable;
+	uint8_t cropEnable;
+	uint8_t vsubSampleEnable;
+	uint8_t hsubSampleEnable;
+	uint8_t vCosited;
+	uint8_t hCosited;
+	uint8_t vCositedPhase;
+	uint8_t hCositedPhase;
+	uint16_t cropWidthFirstPixel;
+	uint16_t cropWidthLastPixel;
+	uint16_t cropHeightFirstLine;
+	uint16_t cropHeightLastLine;
+};
+
+enum VFE_START_PIXEL_PATTERN {
+	VFE_BAYER_RGRGRG,
+	VFE_BAYER_GRGRGR,
+	VFE_BAYER_BGBGBG,
+	VFE_BAYER_GBGBGB,
+	VFE_YUV_YCbYCr,
+	VFE_YUV_YCrYCb,
+	VFE_YUV_CbYCrY,
+	VFE_YUV_CrYCbY
+};
+
+enum VFE_BUS_RD_INPUT_PIXEL_PATTERN {
+	VFE_BAYER_RAW,
+	VFE_YUV_INTERLEAVED,
+	VFE_YUV_PSEUDO_PLANAR_Y,
+	VFE_YUV_PSEUDO_PLANAR_CBCR
+};
+
+enum VFE_YUV_INPUT_COSITING_MODE {
+	VFE_YUV_COSITED,
+	VFE_YUV_INTERPOLATED
+};
+
+#define VFE31_GAMMA_NUM_ENTRIES  64
+
+#define VFE31_LA_TABLE_LENGTH    64
+
+#define VFE31_HIST_TABLE_LENGTH  256
+
+struct vfe_cmds_demosaic_abf {
+	uint8_t   enable;
+	uint8_t   forceOn;
+	uint8_t   shift;
+	uint16_t  lpThreshold;
+	uint16_t  max;
+	uint16_t  min;
+	uint8_t   ratio;
+};
+
+struct vfe_cmds_demosaic_bpc {
+	uint8_t   enable;
+	uint16_t  fmaxThreshold;
+	uint16_t  fminThreshold;
+	uint16_t  redDiffThreshold;
+	uint16_t  blueDiffThreshold;
+	uint16_t  greenDiffThreshold;
+};
+
+struct vfe_cmd_demosaic_config {
+	uint8_t   enable;
+	uint8_t   slopeShift;
+	struct vfe_cmds_demosaic_abf abfConfig;
+	struct vfe_cmds_demosaic_bpc bpcConfig;
+};
+
+struct vfe_cmd_demosaic_bpc_update {
+	struct vfe_cmds_demosaic_bpc bpcUpdate;
+};
+
+struct vfe_cmd_demosaic_abf_update {
+	struct vfe_cmds_demosaic_abf abfUpdate;
+};
+
+struct vfe_cmd_white_balance_config {
+	uint8_t  enable;
+	uint16_t ch2Gain;
+	uint16_t ch1Gain;
+	uint16_t ch0Gain;
+};
+
+enum VFE_COLOR_CORRECTION_COEF_QFACTOR {
+	COEF_IS_Q7_SIGNED,
+	COEF_IS_Q8_SIGNED,
+	COEF_IS_Q9_SIGNED,
+	COEF_IS_Q10_SIGNED
+};
+
+struct vfe_cmd_color_correction_config {
+	uint8_t     enable;
+	enum VFE_COLOR_CORRECTION_COEF_QFACTOR coefQFactor;
+	int16_t  C0;
+	int16_t  C1;
+	int16_t  C2;
+	int16_t  C3;
+	int16_t  C4;
+	int16_t  C5;
+	int16_t  C6;
+	int16_t  C7;
+	int16_t  C8;
+	int16_t  K0;
+	int16_t  K1;
+	int16_t  K2;
+};
+
+#define VFE_LA_TABLE_LENGTH 64
+
+struct vfe_cmd_la_config {
+	uint8_t enable;
+	int16_t table[VFE_LA_TABLE_LENGTH];
+};
+
+#define VFE_GAMMA_TABLE_LENGTH 256
+enum VFE_RGB_GAMMA_TABLE_SELECT {
+	RGB_GAMMA_CH0_SELECTED,
+	RGB_GAMMA_CH1_SELECTED,
+	RGB_GAMMA_CH2_SELECTED,
+	RGB_GAMMA_CH0_CH1_SELECTED,
+	RGB_GAMMA_CH0_CH2_SELECTED,
+	RGB_GAMMA_CH1_CH2_SELECTED,
+	RGB_GAMMA_CH0_CH1_CH2_SELECTED
+};
+
+struct vfe_cmd_rgb_gamma_config {
+	uint8_t enable;
+	enum VFE_RGB_GAMMA_TABLE_SELECT channelSelect;
+	int16_t table[VFE_GAMMA_TABLE_LENGTH];
+};
+
+struct vfe_cmd_chroma_enhan_config {
+	uint8_t  enable;
+	int16_t am;
+	int16_t ap;
+	int16_t bm;
+	int16_t bp;
+	int16_t cm;
+	int16_t cp;
+	int16_t dm;
+	int16_t dp;
+	int16_t kcr;
+	int16_t kcb;
+	int16_t RGBtoYConversionV0;
+	int16_t RGBtoYConversionV1;
+	int16_t RGBtoYConversionV2;
+	uint8_t RGBtoYConversionOffset;
+};
+
+struct vfe_cmd_chroma_suppression_config {
+	uint8_t enable;
+	uint8_t m1;
+	uint8_t m3;
+	uint8_t n1;
+	uint8_t n3;
+	uint8_t nn1;
+	uint8_t mm1;
+};
+
+struct vfe_cmd_asf_config {
+	uint8_t enable;
+	uint8_t smoothFilterEnabled;
+	uint8_t sharpMode;
+	uint8_t smoothCoefCenter;
+	uint8_t smoothCoefSurr;
+	uint8_t normalizeFactor;
+	uint8_t sharpK1;
+	uint8_t sharpK2;
+	uint8_t sharpThreshE1;
+	int8_t sharpThreshE2;
+	int8_t sharpThreshE3;
+	int8_t sharpThreshE4;
+	int8_t sharpThreshE5;
+	int8_t filter1Coefficients[9];
+	int8_t filter2Coefficients[9];
+	uint8_t  cropEnable;
+	uint16_t cropFirstPixel;
+	uint16_t cropLastPixel;
+	uint16_t cropFirstLine;
+	uint16_t cropLastLine;
+};
+
+struct vfe_cmd_asf_update {
+	uint8_t enable;
+	uint8_t smoothFilterEnabled;
+	uint8_t sharpMode;
+	uint8_t smoothCoefCenter;
+	uint8_t smoothCoefSurr;
+	uint8_t normalizeFactor;
+	uint8_t sharpK1;
+	uint8_t sharpK2;
+	uint8_t sharpThreshE1;
+	int8_t  sharpThreshE2;
+	int8_t  sharpThreshE3;
+	int8_t  sharpThreshE4;
+	int8_t  sharpThreshE5;
+	int8_t  filter1Coefficients[9];
+	int8_t  filter2Coefficients[9];
+	uint8_t cropEnable;
+};
+
+enum VFE_TEST_GEN_SYNC_EDGE {
+	VFE_TEST_GEN_SYNC_EDGE_ActiveHigh,
+	VFE_TEST_GEN_SYNC_EDGE_ActiveLow
+};
+
+
+struct vfe_cmd_bus_pm_start {
+	uint8_t output2YWrPmEnable;
+	uint8_t output2CbcrWrPmEnable;
+	uint8_t output1YWrPmEnable;
+	uint8_t output1CbcrWrPmEnable;
+};
+
+struct  vfe_frame_skip_counts {
+	uint32_t  totalFrameCount;
+	uint32_t  output1Count;
+	uint32_t  output2Count;
+};
+
+enum VFE_AXI_RD_UNPACK_HBI_SEL {
+	VFE_AXI_RD_HBI_32_CLOCK_CYCLES,
+	VFE_AXI_RD_HBI_64_CLOCK_CYCLES,
+	VFE_AXI_RD_HBI_128_CLOCK_CYCLES,
+	VFE_AXI_RD_HBI_256_CLOCK_CYCLES,
+	VFE_AXI_RD_HBI_512_CLOCK_CYCLES,
+	VFE_AXI_RD_HBI_1024_CLOCK_CYCLES,
+	VFE_AXI_RD_HBI_2048_CLOCK_CYCLES,
+	VFE_AXI_RD_HBI_4096_CLOCK_CYCLES
+};
+
+struct vfe_frame_bpc_info {
+	uint32_t greenDefectPixelCount;
+	uint32_t redBlueDefectPixelCount;
+};
+
+struct vfe_frame_asf_info {
+	uint32_t  asfMaxEdge;
+	uint32_t  asfHbiCount;
+};
+
+struct vfe_msg_camif_status {
+	uint8_t  camifState;
+	uint32_t pixelCount;
+	uint32_t lineCount;
+};
+
+struct vfe31_irq_status {
+	uint32_t vfeIrqStatus0;
+	uint32_t vfeIrqStatus1;
+	uint32_t camifStatus;
+	uint32_t demosaicStatus;
+	uint32_t asfMaxEdge;
+};
+
+#define V31_PREVIEW_AXI_FLAG  0x00000001
+#define V31_SNAPSHOT_AXI_FLAG (0x00000001<<1)
+
+struct vfe31_cmd_type {
+	uint16_t id;
+	uint32_t length;
+	uint32_t offset;
+	uint32_t flag;
+};
+
+struct vfe31_free_buf {
+	struct list_head node;
+	uint32_t paddr;
+	uint32_t y_off;
+	uint32_t cbcr_off;
+};
+
+struct vfe31_output_ch {
+	struct list_head free_buf_queue;
+	spinlock_t free_buf_lock;
+	uint16_t output_fmt;
+	int8_t ch0;
+	int8_t ch1;
+	int8_t ch2;
+	uint32_t  capture_cnt;
+	uint32_t  frame_drop_cnt;
+	struct msm_free_buf ping;
+	struct msm_free_buf pong;
+	struct msm_free_buf free_buf;
+};
+
+/* no error irq in mask 0 */
+#define VFE31_IMASK_ERROR_ONLY_0  0x0
+/* when normal case, don't want to block error status. */
+/* bit 0-21 are error irq bits */
+#define VFE31_IMASK_ERROR_ONLY_1               0x003FFFFF
+#define VFE31_IMASK_CAMIF_ERROR               (0x00000001<<0)
+#define VFE31_IMASK_STATS_CS_OVWR             (0x00000001<<1)
+#define VFE31_IMASK_STATS_IHIST_OVWR          (0x00000001<<2)
+#define VFE31_IMASK_REALIGN_BUF_Y_OVFL        (0x00000001<<3)
+#define VFE31_IMASK_REALIGN_BUF_CB_OVFL       (0x00000001<<4)
+#define VFE31_IMASK_REALIGN_BUF_CR_OVFL       (0x00000001<<5)
+#define VFE31_IMASK_VIOLATION                 (0x00000001<<6)
+#define VFE31_IMASK_IMG_MAST_0_BUS_OVFL       (0x00000001<<7)
+#define VFE31_IMASK_IMG_MAST_1_BUS_OVFL       (0x00000001<<8)
+#define VFE31_IMASK_IMG_MAST_2_BUS_OVFL       (0x00000001<<9)
+#define VFE31_IMASK_IMG_MAST_3_BUS_OVFL       (0x00000001<<10)
+#define VFE31_IMASK_IMG_MAST_4_BUS_OVFL       (0x00000001<<11)
+#define VFE31_IMASK_IMG_MAST_5_BUS_OVFL       (0x00000001<<12)
+#define VFE31_IMASK_IMG_MAST_6_BUS_OVFL       (0x00000001<<13)
+#define VFE31_IMASK_STATS_AE_BG_BUS_OVFL         (0x00000001<<14)
+#define VFE31_IMASK_STATS_AF_BF_BUS_OVFL         (0x00000001<<15)
+#define VFE31_IMASK_STATS_AWB_BUS_OVFL        (0x00000001<<16)
+#define VFE31_IMASK_STATS_RS_BUS_OVFL         (0x00000001<<17)
+#define VFE31_IMASK_STATS_CS_BUS_OVFL         (0x00000001<<18)
+#define VFE31_IMASK_STATS_IHIST_BUS_OVFL      (0x00000001<<19)
+#define VFE31_IMASK_STATS_SKIN_BHIST_BUS_OVFL       (0x00000001<<20)
+#define VFE31_IMASK_AXI_ERROR                 (0x00000001<<21)
+
+#define VFE_COM_STATUS 0x000FE000
+
+struct vfe31_output_path {
+	uint16_t output_mode;     /* bitmask  */
+
+	struct vfe31_output_ch out0; /* preview and thumbnail */
+	struct vfe31_output_ch out1; /* snapshot */
+	struct vfe31_output_ch out2; /* video    */
+};
+
+struct vfe31_frame_extra {
+	uint32_t greenDefectPixelCount;
+	uint32_t redBlueDefectPixelCount;
+
+	uint32_t  asfMaxEdge;
+	uint32_t  asfHbiCount;
+
+	uint32_t yWrPmStats0;
+	uint32_t yWrPmStats1;
+	uint32_t cbcrWrPmStats0;
+	uint32_t cbcrWrPmStats1;
+
+	uint32_t  frameCounter;
+};
+
+#define VFE_DISABLE_ALL_IRQS             0
+#define VFE_CLEAR_ALL_IRQS               0xffffffff
+
+#define VFE_HW_VERSION					 0x00000000
+#define VFE_GLOBAL_RESET                 0x00000004
+#define VFE_MODULE_RESET				 0x00000008
+#define VFE_CGC_OVERRIDE                 0x0000000C
+#define VFE_MODULE_CFG                   0x00000010
+#define VFE_CFG				 0x00000014
+#define VFE_IRQ_CMD                      0x00000018
+#define VFE_IRQ_MASK_0                   0x0000001C
+#define VFE_IRQ_MASK_1                   0x00000020
+#define VFE_IRQ_CLEAR_0                  0x00000024
+#define VFE_IRQ_CLEAR_1                  0x00000028
+#define VFE_IRQ_STATUS_0                 0x0000002C
+#define VFE_IRQ_STATUS_1                 0x00000030
+#define VFE_IRQ_COMP_MASK                0x00000034
+#define VFE_BUS_CMD                      0x00000038
+#define VFE_BUS_PING_PONG_STATUS         0x00000180
+#define VFE_BUS_OPERATION_STATUS         0x00000184
+
+#define VFE_BUS_IMAGE_MASTER_0_WR_PM_STATS_0        0x00000190
+#define VFE_BUS_IMAGE_MASTER_0_WR_PM_STATS_1        0x00000194
+
+#define VFE_AXI_CMD                      0x000001D8
+#define VFE_AXI_STATUS                   0x000001DC
+#define VFE_BUS_STATS_PING_PONG_BASE     0x000000F4
+
+#define VFE_BUS_STATS_AEC_WR_PING_ADDR   0x000000F4
+#define VFE_BUS_STATS_AEC_WR_PONG_ADDR   0x000000F8
+#define VFE_BUS_STATS_AEC_UB_CFG         0x000000FC
+#define VFE_BUS_STATS_AF_WR_PING_ADDR    0x00000100
+#define VFE_BUS_STATS_AF_WR_PONG_ADDR    0x00000104
+#define VFE_BUS_STATS_AF_UB_CFG          0x00000108
+#define VFE_BUS_STATS_AWB_WR_PING_ADDR   0x0000010C
+#define VFE_BUS_STATS_AWB_WR_PONG_ADDR   0x00000110
+#define VFE_BUS_STATS_AWB_UB_CFG         0x00000114
+#define VFE_BUS_STATS_RS_WR_PING_ADDR    0x00000118
+#define VFE_BUS_STATS_RS_WR_PONG_ADDR    0x0000011C
+#define VFE_BUS_STATS_RS_UB_CFG          0x00000120
+
+#define VFE_BUS_STATS_CS_WR_PING_ADDR    0x00000124
+#define VFE_BUS_STATS_CS_WR_PONG_ADDR    0x00000128
+#define VFE_BUS_STATS_CS_UB_CFG          0x0000012C
+#define VFE_BUS_STATS_HIST_WR_PING_ADDR  0x00000130
+#define VFE_BUS_STATS_HIST_WR_PONG_ADDR  0x00000134
+#define VFE_BUS_STATS_HIST_UB_CFG        0x00000138
+#define VFE_BUS_STATS_SKIN_WR_PING_ADDR  0x0000013C
+#define VFE_BUS_STATS_SKIN_WR_PONG_ADDR  0x00000140
+#define VFE_BUS_STATS_SKIN_UB_CFG        0x00000144
+#define VFE_BUS_PM_CMD                   0x00000188
+#define VFE_BUS_PM_CFG                   0x0000018C
+#define VFE_CAMIF_COMMAND                0x000001E0
+#define VFE_CAMIF_STATUS                 0x00000204
+#define VFE_REG_UPDATE_CMD               0x00000260
+#define VFE_DEMUX_GAIN_0                 0x00000288
+#define VFE_DEMUX_GAIN_1                 0x0000028C
+#define VFE_CHROMA_UP                    0x0000035C
+#define VFE_FRAMEDROP_ENC_Y_CFG          0x00000504
+#define VFE_FRAMEDROP_ENC_CBCR_CFG       0x00000508
+#define VFE_FRAMEDROP_ENC_Y_PATTERN      0x0000050C
+#define VFE_FRAMEDROP_ENC_CBCR_PATTERN   0x00000510
+#define VFE_FRAMEDROP_VIEW_Y             0x00000514
+#define VFE_FRAMEDROP_VIEW_CBCR          0x00000518
+#define VFE_FRAMEDROP_VIEW_Y_PATTERN     0x0000051C
+#define VFE_FRAMEDROP_VIEW_CBCR_PATTERN  0x00000520
+#define VFE_CLAMP_MAX                    0x00000524
+#define VFE_CLAMP_MIN                    0x00000528
+#define VFE_REALIGN_BUF                  0x0000052C
+#define VFE_STATS_CFG                    0x00000530
+#define VFE_STATS_AWB_SGW_CFG            0x00000554
+#define VFE_DMI_CFG                      0x00000598
+#define VFE_DMI_ADDR                     0x0000059C
+#define VFE_DMI_DATA_LO                  0x000005A4
+#define VFE_AXI_CFG                      0x00000600
+
+#define VFE31_OUTPUT_MODE_PT		BIT(0)
+#define VFE31_OUTPUT_MODE_S			BIT(1)
+#define VFE31_OUTPUT_MODE_V			BIT(2)
+#define VFE31_OUTPUT_MODE_P			BIT(3)
+#define VFE31_OUTPUT_MODE_T			BIT(4)
+#define VFE31_OUTPUT_MODE_P_ALL_CHNLS		BIT(5)
+#define VFE31_OUTPUT_MODE_PRIMARY		BIT(6)
+#define VFE31_OUTPUT_MODE_PRIMARY_ALL_CHNLS	BIT(7)
+#define VFE31_OUTPUT_MODE_SECONDARY		BIT(8)
+#define VFE31_OUTPUT_MODE_SECONDARY_ALL_CHNLS	BIT(9)
+struct vfe_stats_control {
+	uint8_t  ackPending;
+	uint32_t nextFrameAddrBuf;
+	uint32_t droppedStatsFrameCount;
+	uint32_t bufToRender;
+};
+
+struct vfe31_ctrl_type {
+	uint16_t operation_mode;     /* streaming or snapshot */
+	struct vfe31_output_path outpath;
+
+	uint32_t vfeImaskCompositePacked;
+
+	spinlock_t  stop_flag_lock;
+	spinlock_t  update_ack_lock;
+	spinlock_t  state_lock;
+	spinlock_t  io_lock;
+
+	spinlock_t  aec_ack_lock;
+	spinlock_t  awb_ack_lock;
+	spinlock_t  af_ack_lock;
+	spinlock_t  ihist_ack_lock;
+	spinlock_t  rs_ack_lock;
+	spinlock_t  cs_ack_lock;
+	spinlock_t  comp_stats_ack_lock;
+
+	uint32_t extlen;
+	void *extdata;
+
+	int8_t start_ack_pending;
+	int8_t stop_ack_pending;
+	int8_t reset_ack_pending;
+	int8_t update_ack_pending;
+	enum vfe_output_state recording_state;
+	int8_t update_linear;
+	int8_t update_rolloff;
+	int8_t update_la;
+	int8_t update_gamma;
+	enum vfe_output_state liveshot_state;
+
+	spinlock_t  tasklet_lock;
+	struct list_head tasklet_q;
+	void __iomem *vfebase;
+	void __iomem *camifbase;
+	void *syncdata;
+	uint32_t register_total;
+
+	struct resource	*vfemem;
+	struct resource	*camifmem;
+	struct resource *vfeio;
+	struct resource *camifio;
+	struct resource *vfeirq;
+	struct regulator *fs_vfe;
+
+	uint32_t stats_comp;
+	atomic_t vstate;
+	uint32_t vfe_capture_count;
+	uint32_t sync_timer_repeat_count;
+	uint32_t sync_timer_state;
+	uint32_t sync_timer_number;
+
+	uint32_t vfeFrameId;
+	uint32_t output1Pattern;
+	uint32_t output1Period;
+	uint32_t output2Pattern;
+	uint32_t output2Period;
+	uint32_t vfeFrameSkipCount;
+	uint32_t vfeFrameSkipPeriod;
+	struct vfe_stats_control afStatsControl;
+	struct vfe_stats_control awbStatsControl;
+	struct vfe_stats_control aecStatsControl;
+	struct vfe_stats_control ihistStatsControl;
+	struct vfe_stats_control rsStatsControl;
+	struct vfe_stats_control csStatsControl;
+
+	/* v4l2 subdev */
+	struct v4l2_subdev subdev;
+	struct platform_device *pdev;
+	struct clk *vfe_clk[5];
+	struct clk *vfe_camif_clk[2];
+	spinlock_t  sd_notify_lock;
+	uint32_t hfr_mode;
+	uint32_t frame_skip_cnt;
+	uint32_t frame_skip_pattern;
+	uint32_t snapshot_frame_cnt;
+};
+
+enum VFE31_STATS_NUM {
+	STATS_AE_NUM,
+	STATS_AF_NUM,
+	STATS_AWB_NUM,
+	STATS_RS_NUM,
+	STATS_CS_NUM,
+	STATS_IHIST_NUM,
+	STATS_SKIN_NUM,
+	STATS_MAX_NUM,
+};
+
+struct vfe_cmd_stats_ack {
+	uint32_t  nextStatsBuf;
+};
+
+#define VFE_STATS_BUFFER_COUNT            3
+
+struct vfe_cmd_stats_buf {
+	uint32_t statsBuf[VFE_STATS_BUFFER_COUNT];
+};
+#endif /* __MSM_VFE31_H__ */
diff --git a/drivers/media/video/msm/msm_vfe32.c b/drivers/media/video/msm/msm_vfe32.c
new file mode 100644
index 0000000..53fd6f1
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe32.c
@@ -0,0 +1,4309 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/atomic.h>
+#include <linux/regulator/consumer.h>
+#include <linux/clk.h>
+#include <mach/irqs.h>
+#include <mach/camera.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/msm_isp.h>
+
+#include "msm.h"
+#include "msm_vfe32.h"
+
+atomic_t irq_cnt;
+
+#define VFE32_AXI_OFFSET 0x0050
+#define vfe32_get_ch_ping_addr(chn) \
+	(msm_camera_io_r(vfe32_ctrl->vfebase + 0x0050 + 0x18 * (chn)))
+#define vfe32_get_ch_pong_addr(chn) \
+	(msm_camera_io_r(vfe32_ctrl->vfebase + 0x0050 + 0x18 * (chn) + 4))
+#define vfe32_get_ch_addr(ping_pong, chn) \
+	(((ping_pong) & (1 << (chn))) == 0 ? \
+	vfe32_get_ch_pong_addr(chn) : vfe32_get_ch_ping_addr(chn))
+
+#define vfe32_put_ch_ping_addr(chn, addr) \
+	(msm_camera_io_w((addr), vfe32_ctrl->vfebase + 0x0050 + 0x18 * (chn)))
+#define vfe32_put_ch_pong_addr(chn, addr) \
+	(msm_camera_io_w((addr), \
+	vfe32_ctrl->vfebase + 0x0050 + 0x18 * (chn) + 4))
+#define vfe32_put_ch_addr(ping_pong, chn, addr) \
+	(((ping_pong) & (1 << (chn))) == 0 ?   \
+	vfe32_put_ch_pong_addr((chn), (addr)) : \
+	vfe32_put_ch_ping_addr((chn), (addr)))
+
+static struct vfe32_ctrl_type *vfe32_ctrl;
+static uint32_t vfe_clk_rate;
+
+struct vfe32_isr_queue_cmd {
+	struct list_head list;
+	uint32_t                           vfeInterruptStatus0;
+	uint32_t                           vfeInterruptStatus1;
+};
+
+static struct vfe32_cmd_type vfe32_cmd[] = {
+/* 0*/	{VFE_CMD_DUMMY_0},
+		{VFE_CMD_SET_CLK},
+		{VFE_CMD_RESET},
+		{VFE_CMD_START},
+		{VFE_CMD_TEST_GEN_START},
+/* 5*/	{VFE_CMD_OPERATION_CFG, V32_OPERATION_CFG_LEN},
+		{VFE_CMD_AXI_OUT_CFG, V32_AXI_OUT_LEN, V32_AXI_OUT_OFF, 0xFF},
+		{VFE_CMD_CAMIF_CFG, V32_CAMIF_LEN, V32_CAMIF_OFF, 0xFF},
+		{VFE_CMD_AXI_INPUT_CFG},
+		{VFE_CMD_BLACK_LEVEL_CFG, V32_BLACK_LEVEL_LEN,
+		V32_BLACK_LEVEL_OFF,
+		0xFF},
+/*10*/  {VFE_CMD_MESH_ROLL_OFF_CFG, V32_MESH_ROLL_OFF_CFG_LEN,
+		V32_MESH_ROLL_OFF_CFG_OFF, 0xFF},
+		{VFE_CMD_DEMUX_CFG, V32_DEMUX_LEN, V32_DEMUX_OFF, 0xFF},
+		{VFE_CMD_FOV_CFG, V32_FOV_LEN, V32_FOV_OFF, 0xFF},
+		{VFE_CMD_MAIN_SCALER_CFG, V32_MAIN_SCALER_LEN,
+		V32_MAIN_SCALER_OFF, 0xFF},
+		{VFE_CMD_WB_CFG, V32_WB_LEN, V32_WB_OFF, 0xFF},
+/*15*/	{VFE_CMD_COLOR_COR_CFG, V32_COLOR_COR_LEN, V32_COLOR_COR_OFF, 0xFF},
+		{VFE_CMD_RGB_G_CFG, V32_RGB_G_LEN, V32_RGB_G_OFF, 0xFF},
+		{VFE_CMD_LA_CFG, V32_LA_LEN, V32_LA_OFF, 0xFF },
+		{VFE_CMD_CHROMA_EN_CFG, V32_CHROMA_EN_LEN, V32_CHROMA_EN_OFF,
+		0xFF},
+		{VFE_CMD_CHROMA_SUP_CFG, V32_CHROMA_SUP_LEN, V32_CHROMA_SUP_OFF,
+		0xFF},
+/*20*/	{VFE_CMD_MCE_CFG, V32_MCE_LEN, V32_MCE_OFF, 0xFF},
+		{VFE_CMD_SK_ENHAN_CFG, V32_SCE_LEN, V32_SCE_OFF, 0xFF},
+		{VFE_CMD_ASF_CFG, V32_ASF_LEN, V32_ASF_OFF, 0xFF},
+		{VFE_CMD_S2Y_CFG, V32_S2Y_LEN, V32_S2Y_OFF, 0xFF},
+		{VFE_CMD_S2CbCr_CFG, V32_S2CbCr_LEN, V32_S2CbCr_OFF, 0xFF},
+/*25*/	{VFE_CMD_CHROMA_SUBS_CFG, V32_CHROMA_SUBS_LEN, V32_CHROMA_SUBS_OFF,
+		0xFF},
+		{VFE_CMD_OUT_CLAMP_CFG, V32_OUT_CLAMP_LEN, V32_OUT_CLAMP_OFF,
+		0xFF},
+		{VFE_CMD_FRAME_SKIP_CFG, V32_FRAME_SKIP_LEN, V32_FRAME_SKIP_OFF,
+		0xFF},
+		{VFE_CMD_DUMMY_1},
+		{VFE_CMD_DUMMY_2},
+/*30*/	{VFE_CMD_DUMMY_3},
+		{VFE_CMD_UPDATE},
+		{VFE_CMD_BL_LVL_UPDATE, V32_BLACK_LEVEL_LEN,
+		V32_BLACK_LEVEL_OFF, 0xFF},
+		{VFE_CMD_DEMUX_UPDATE, V32_DEMUX_LEN, V32_DEMUX_OFF, 0xFF},
+		{VFE_CMD_FOV_UPDATE, V32_FOV_LEN, V32_FOV_OFF, 0xFF},
+/*35*/	{VFE_CMD_MAIN_SCALER_UPDATE, V32_MAIN_SCALER_LEN, V32_MAIN_SCALER_OFF,
+		0xFF},
+		{VFE_CMD_WB_UPDATE, V32_WB_LEN, V32_WB_OFF, 0xFF},
+		{VFE_CMD_COLOR_COR_UPDATE, V32_COLOR_COR_LEN, V32_COLOR_COR_OFF,
+		0xFF},
+		{VFE_CMD_RGB_G_UPDATE, V32_RGB_G_LEN, V32_CHROMA_EN_OFF, 0xFF},
+		{VFE_CMD_LA_UPDATE, V32_LA_LEN, V32_LA_OFF, 0xFF },
+/*40*/	{VFE_CMD_CHROMA_EN_UPDATE, V32_CHROMA_EN_LEN, V32_CHROMA_EN_OFF,
+		0xFF},
+		{VFE_CMD_CHROMA_SUP_UPDATE, V32_CHROMA_SUP_LEN,
+		V32_CHROMA_SUP_OFF, 0xFF},
+		{VFE_CMD_MCE_UPDATE, V32_MCE_LEN, V32_MCE_OFF, 0xFF},
+		{VFE_CMD_SK_ENHAN_UPDATE, V32_SCE_LEN, V32_SCE_OFF, 0xFF},
+		{VFE_CMD_S2CbCr_UPDATE, V32_S2CbCr_LEN, V32_S2CbCr_OFF, 0xFF},
+/*45*/	{VFE_CMD_S2Y_UPDATE, V32_S2Y_LEN, V32_S2Y_OFF, 0xFF},
+		{VFE_CMD_ASF_UPDATE, V32_ASF_UPDATE_LEN, V32_ASF_OFF, 0xFF},
+		{VFE_CMD_FRAME_SKIP_UPDATE},
+		{VFE_CMD_CAMIF_FRAME_UPDATE},
+		{VFE_CMD_STATS_AF_UPDATE, V32_STATS_AF_LEN, V32_STATS_AF_OFF},
+/*50*/	{VFE_CMD_STATS_AE_UPDATE, V32_STATS_AE_LEN, V32_STATS_AE_OFF},
+		{VFE_CMD_STATS_AWB_UPDATE, V32_STATS_AWB_LEN,
+		V32_STATS_AWB_OFF},
+		{VFE_CMD_STATS_RS_UPDATE, V32_STATS_RS_LEN, V32_STATS_RS_OFF},
+		{VFE_CMD_STATS_CS_UPDATE, V32_STATS_CS_LEN, V32_STATS_CS_OFF},
+		{VFE_CMD_STATS_SKIN_UPDATE},
+/*55*/	{VFE_CMD_STATS_IHIST_UPDATE, V32_STATS_IHIST_LEN, V32_STATS_IHIST_OFF},
+		{VFE_CMD_DUMMY_4},
+		{VFE_CMD_EPOCH1_ACK},
+		{VFE_CMD_EPOCH2_ACK},
+		{VFE_CMD_START_RECORDING},
+/*60*/	{VFE_CMD_STOP_RECORDING},
+		{VFE_CMD_DUMMY_5},
+		{VFE_CMD_DUMMY_6},
+		{VFE_CMD_CAPTURE, V32_CAPTURE_LEN, 0xFF},
+		{VFE_CMD_DUMMY_7},
+/*65*/	{VFE_CMD_STOP},
+		{VFE_CMD_GET_HW_VERSION, V32_GET_HW_VERSION_LEN,
+		V32_GET_HW_VERSION_OFF},
+		{VFE_CMD_GET_FRAME_SKIP_COUNTS},
+		{VFE_CMD_OUTPUT1_BUFFER_ENQ},
+		{VFE_CMD_OUTPUT2_BUFFER_ENQ},
+/*70*/	{VFE_CMD_OUTPUT3_BUFFER_ENQ},
+		{VFE_CMD_JPEG_OUT_BUF_ENQ},
+		{VFE_CMD_RAW_OUT_BUF_ENQ},
+		{VFE_CMD_RAW_IN_BUF_ENQ},
+		{VFE_CMD_STATS_AF_ENQ},
+/*75*/	{VFE_CMD_STATS_AE_ENQ},
+		{VFE_CMD_STATS_AWB_ENQ},
+		{VFE_CMD_STATS_RS_ENQ},
+		{VFE_CMD_STATS_CS_ENQ},
+		{VFE_CMD_STATS_SKIN_ENQ},
+/*80*/	{VFE_CMD_STATS_IHIST_ENQ},
+		{VFE_CMD_DUMMY_8},
+		{VFE_CMD_JPEG_ENC_CFG},
+		{VFE_CMD_DUMMY_9},
+		{VFE_CMD_STATS_AF_START, V32_STATS_AF_LEN, V32_STATS_AF_OFF},
+/*85*/	{VFE_CMD_STATS_AF_STOP},
+		{VFE_CMD_STATS_AE_START, V32_STATS_AE_LEN, V32_STATS_AE_OFF},
+		{VFE_CMD_STATS_AE_STOP},
+		{VFE_CMD_STATS_AWB_START, V32_STATS_AWB_LEN, V32_STATS_AWB_OFF},
+		{VFE_CMD_STATS_AWB_STOP},
+/*90*/	{VFE_CMD_STATS_RS_START, V32_STATS_RS_LEN, V32_STATS_RS_OFF},
+		{VFE_CMD_STATS_RS_STOP},
+		{VFE_CMD_STATS_CS_START, V32_STATS_CS_LEN, V32_STATS_CS_OFF},
+		{VFE_CMD_STATS_CS_STOP},
+		{VFE_CMD_STATS_SKIN_START},
+/*95*/	{VFE_CMD_STATS_SKIN_STOP},
+		{VFE_CMD_STATS_IHIST_START,
+		V32_STATS_IHIST_LEN, V32_STATS_IHIST_OFF},
+		{VFE_CMD_STATS_IHIST_STOP},
+		{VFE_CMD_DUMMY_10},
+		{VFE_CMD_SYNC_TIMER_SETTING, V32_SYNC_TIMER_LEN,
+			V32_SYNC_TIMER_OFF},
+/*100*/	{VFE_CMD_ASYNC_TIMER_SETTING, V32_ASYNC_TIMER_LEN, V32_ASYNC_TIMER_OFF},
+		{VFE_CMD_LIVESHOT},
+		{VFE_CMD_LA_SETUP},
+		{VFE_CMD_LINEARIZATION_CFG, V32_LINEARIZATION_LEN1,
+			V32_LINEARIZATION_OFF1},
+		{VFE_CMD_DEMOSAICV3},
+/*105*/	{VFE_CMD_DEMOSAICV3_ABCC_CFG},
+		{VFE_CMD_DEMOSAICV3_DBCC_CFG, V32_DEMOSAICV3_DBCC_LEN,
+			V32_DEMOSAICV3_DBCC_OFF},
+		{VFE_CMD_DEMOSAICV3_DBPC_CFG},
+		{VFE_CMD_DEMOSAICV3_ABF_CFG, V32_DEMOSAICV3_ABF_LEN,
+			V32_DEMOSAICV3_ABF_OFF},
+		{VFE_CMD_DEMOSAICV3_ABCC_UPDATE},
+/*110*/	{VFE_CMD_DEMOSAICV3_DBCC_UPDATE, V32_DEMOSAICV3_DBCC_LEN,
+			V32_DEMOSAICV3_DBCC_OFF},
+		{VFE_CMD_DEMOSAICV3_DBPC_UPDATE},
+		{VFE_CMD_XBAR_CFG},
+		{VFE_CMD_MODULE_CFG, V32_MODULE_CFG_LEN, V32_MODULE_CFG_OFF},
+		{VFE_CMD_ZSL},
+/*115*/	{VFE_CMD_LINEARIZATION_UPDATE, V32_LINEARIZATION_LEN1,
+			V32_LINEARIZATION_OFF1},
+		{VFE_CMD_DEMOSAICV3_ABF_UPDATE, V32_DEMOSAICV3_ABF_LEN,
+			V32_DEMOSAICV3_ABF_OFF},
+		{VFE_CMD_CLF_CFG, V32_CLF_CFG_LEN, V32_CLF_CFG_OFF},
+		{VFE_CMD_CLF_LUMA_UPDATE, V32_CLF_LUMA_UPDATE_LEN,
+			V32_CLF_LUMA_UPDATE_OFF},
+		{VFE_CMD_CLF_CHROMA_UPDATE, V32_CLF_CHROMA_UPDATE_LEN,
+			V32_CLF_CHROMA_UPDATE_OFF},
+/*120*/ {VFE_CMD_PCA_ROLL_OFF_CFG},
+		{VFE_CMD_PCA_ROLL_OFF_UPDATE},
+		{VFE_CMD_GET_REG_DUMP},
+		{VFE_CMD_GET_LINEARIZATON_TABLE},
+		{VFE_CMD_GET_MESH_ROLLOFF_TABLE},
+/*125*/ {VFE_CMD_GET_PCA_ROLLOFF_TABLE},
+		{VFE_CMD_GET_RGB_G_TABLE},
+		{VFE_CMD_GET_LA_TABLE},
+		{VFE_CMD_DEMOSAICV3_UPDATE},
+};
+
+uint32_t vfe32_AXI_WM_CFG[] = {
+	0x0000004C,
+	0x00000064,
+	0x0000007C,
+	0x00000094,
+	0x000000AC,
+	0x000000C4,
+	0x000000DC,
+};
+
+static const char * const vfe32_general_cmd[] = {
+	"DUMMY_0",  /* 0 */
+	"SET_CLK",
+	"RESET",
+	"START",
+	"TEST_GEN_START",
+	"OPERATION_CFG",  /* 5 */
+	"AXI_OUT_CFG",
+	"CAMIF_CFG",
+	"AXI_INPUT_CFG",
+	"BLACK_LEVEL_CFG",
+	"ROLL_OFF_CFG",  /* 10 */
+	"DEMUX_CFG",
+	"FOV_CFG",
+	"MAIN_SCALER_CFG",
+	"WB_CFG",
+	"COLOR_COR_CFG", /* 15 */
+	"RGB_G_CFG",
+	"LA_CFG",
+	"CHROMA_EN_CFG",
+	"CHROMA_SUP_CFG",
+	"MCE_CFG", /* 20 */
+	"SK_ENHAN_CFG",
+	"ASF_CFG",
+	"S2Y_CFG",
+	"S2CbCr_CFG",
+	"CHROMA_SUBS_CFG",  /* 25 */
+	"OUT_CLAMP_CFG",
+	"FRAME_SKIP_CFG",
+	"DUMMY_1",
+	"DUMMY_2",
+	"DUMMY_3",  /* 30 */
+	"UPDATE",
+	"BL_LVL_UPDATE",
+	"DEMUX_UPDATE",
+	"FOV_UPDATE",
+	"MAIN_SCALER_UPDATE",  /* 35 */
+	"WB_UPDATE",
+	"COLOR_COR_UPDATE",
+	"RGB_G_UPDATE",
+	"LA_UPDATE",
+	"CHROMA_EN_UPDATE",  /* 40 */
+	"CHROMA_SUP_UPDATE",
+	"MCE_UPDATE",
+	"SK_ENHAN_UPDATE",
+	"S2CbCr_UPDATE",
+	"S2Y_UPDATE",  /* 45 */
+	"ASF_UPDATE",
+	"FRAME_SKIP_UPDATE",
+	"CAMIF_FRAME_UPDATE",
+	"STATS_AF_UPDATE",
+	"STATS_AE_UPDATE",  /* 50 */
+	"STATS_AWB_UPDATE",
+	"STATS_RS_UPDATE",
+	"STATS_CS_UPDATE",
+	"STATS_SKIN_UPDATE",
+	"STATS_IHIST_UPDATE",  /* 55 */
+	"DUMMY_4",
+	"EPOCH1_ACK",
+	"EPOCH2_ACK",
+	"START_RECORDING",
+	"STOP_RECORDING",  /* 60 */
+	"DUMMY_5",
+	"DUMMY_6",
+	"CAPTURE",
+	"DUMMY_7",
+	"STOP",  /* 65 */
+	"GET_HW_VERSION",
+	"GET_FRAME_SKIP_COUNTS",
+	"OUTPUT1_BUFFER_ENQ",
+	"OUTPUT2_BUFFER_ENQ",
+	"OUTPUT3_BUFFER_ENQ",  /* 70 */
+	"JPEG_OUT_BUF_ENQ",
+	"RAW_OUT_BUF_ENQ",
+	"RAW_IN_BUF_ENQ",
+	"STATS_AF_ENQ",
+	"STATS_AE_ENQ",  /* 75 */
+	"STATS_AWB_ENQ",
+	"STATS_RS_ENQ",
+	"STATS_CS_ENQ",
+	"STATS_SKIN_ENQ",
+	"STATS_IHIST_ENQ",  /* 80 */
+	"DUMMY_8",
+	"JPEG_ENC_CFG",
+	"DUMMY_9",
+	"STATS_AF_START",
+	"STATS_AF_STOP",  /* 85 */
+	"STATS_AE_START",
+	"STATS_AE_STOP",
+	"STATS_AWB_START",
+	"STATS_AWB_STOP",
+	"STATS_RS_START",  /* 90 */
+	"STATS_RS_STOP",
+	"STATS_CS_START",
+	"STATS_CS_STOP",
+	"STATS_SKIN_START",
+	"STATS_SKIN_STOP",  /* 95 */
+	"STATS_IHIST_START",
+	"STATS_IHIST_STOP",
+	"DUMMY_10",
+	"SYNC_TIMER_SETTING",
+	"ASYNC_TIMER_SETTING",  /* 100 */
+	"LIVESHOT",
+	"LA_SETUP",
+	"LINEARIZATION_CFG",
+	"DEMOSAICV3",
+	"DEMOSAICV3_ABCC_CFG", /* 105 */
+	"DEMOSAICV3_DBCC_CFG",
+	"DEMOSAICV3_DBPC_CFG",
+	"DEMOSAICV3_ABF_CFG",
+	"DEMOSAICV3_ABCC_UPDATE",
+	"DEMOSAICV3_DBCC_UPDATE", /* 110 */
+	"DEMOSAICV3_DBPC_UPDATE",
+	"XBAR_CFG",
+	"EZTUNE_CFG",
+	"V32_ZSL",
+	"LINEARIZATION_UPDATE", /*115*/
+	"DEMOSAICV3_ABF_UPDATE",
+	"CLF_CFG",
+	"CLF_LUMA_UPDATE",
+	"CLF_CHROMA_UPDATE",
+	"PCA_ROLL_OFF_CFG", /*120*/
+	"PCA_ROLL_OFF_UPDATE",
+	"GET_REG_DUMP",
+	"GET_LINEARIZATON_TABLE",
+	"GET_MESH_ROLLOFF_TABLE",
+	"GET_PCA_ROLLOFF_TABLE", /*125*/
+	"GET_RGB_G_TABLE",
+	"GET_LA_TABLE",
+	"DEMOSAICV3_UPDATE",
+};
+
+static void vfe32_stop(void)
+{
+	uint8_t  axiBusyFlag = true;
+	unsigned long flags;
+
+	atomic_set(&vfe32_ctrl->vstate, 0);
+
+	/* for reset hw modules, and send msg when reset_irq comes.*/
+	spin_lock_irqsave(&vfe32_ctrl->stop_flag_lock, flags);
+	vfe32_ctrl->stop_ack_pending = TRUE;
+	spin_unlock_irqrestore(&vfe32_ctrl->stop_flag_lock, flags);
+
+	/* disable all interrupts.  */
+	msm_camera_io_w(VFE_DISABLE_ALL_IRQS,
+		vfe32_ctrl->vfebase + VFE_IRQ_MASK_0);
+	msm_camera_io_w(VFE_DISABLE_ALL_IRQS,
+			vfe32_ctrl->vfebase + VFE_IRQ_MASK_1);
+
+	/* clear all pending interrupts*/
+	msm_camera_io_w(VFE_CLEAR_ALL_IRQS,
+		vfe32_ctrl->vfebase + VFE_IRQ_CLEAR_0);
+	msm_camera_io_w(VFE_CLEAR_ALL_IRQS,
+		vfe32_ctrl->vfebase + VFE_IRQ_CLEAR_1);
+	/* Ensure the write order while writing
+	to the command register using the barrier */
+	msm_camera_io_w_mb(1,
+		vfe32_ctrl->vfebase + VFE_IRQ_CMD);
+
+	/* in either continuous or snapshot mode, stop command can be issued
+	 * at any time. stop camif immediately. */
+	msm_camera_io_w(CAMIF_COMMAND_STOP_IMMEDIATELY,
+		vfe32_ctrl->vfebase + VFE_CAMIF_COMMAND);
+
+	/* axi halt command. */
+	msm_camera_io_w(AXI_HALT,
+		vfe32_ctrl->vfebase + VFE_AXI_CMD);
+	wmb();
+	while (axiBusyFlag) {
+		if (msm_camera_io_r(vfe32_ctrl->vfebase + VFE_AXI_STATUS) & 0x1)
+			axiBusyFlag = false;
+	}
+	/* Ensure the write order while writing
+	to the command register using the barrier */
+	msm_camera_io_w_mb(AXI_HALT_CLEAR,
+		vfe32_ctrl->vfebase + VFE_AXI_CMD);
+
+	/* after axi halt, then ok to apply global reset. */
+	/* enable reset_ack and async timer interrupt only while
+	stopping the pipeline.*/
+	msm_camera_io_w(0xf0000000,
+		vfe32_ctrl->vfebase + VFE_IRQ_MASK_0);
+	msm_camera_io_w(VFE_IMASK_WHILE_STOPPING_1,
+		vfe32_ctrl->vfebase + VFE_IRQ_MASK_1);
+
+	/* Ensure the write order while writing
+	to the command register using the barrier */
+	msm_camera_io_w_mb(VFE_RESET_UPON_STOP_CMD,
+		vfe32_ctrl->vfebase + VFE_GLOBAL_RESET);
+}
+
+static void vfe32_subdev_notify(int id, int path)
+{
+	struct msm_vfe_resp rp;
+	unsigned long flags = 0;
+	spin_lock_irqsave(&vfe32_ctrl->sd_notify_lock, flags);
+	CDBG("vfe32_subdev_notify : msgId = %d\n", id);
+	memset(&rp, 0, sizeof(struct msm_vfe_resp));
+	rp.evt_msg.type   = MSM_CAMERA_MSG;
+	rp.evt_msg.msg_id = path;
+	rp.type	   = id;
+	v4l2_subdev_notify(&vfe32_ctrl->subdev, NOTIFY_VFE_BUF_EVT, &rp);
+	spin_unlock_irqrestore(&vfe32_ctrl->sd_notify_lock, flags);
+}
+
+static int vfe32_config_axi(int mode, uint32_t *ao)
+{
+	uint32_t *ch_info;
+	uint32_t *axi_cfg = ao+V32_AXI_BUS_FMT_OFF;
+
+	/* Update the corresponding write masters for each output*/
+	ch_info = axi_cfg + V32_AXI_CFG_LEN;
+	vfe32_ctrl->outpath.out0.ch0 = 0x0000FFFF & *ch_info;
+	vfe32_ctrl->outpath.out0.ch1 = 0x0000FFFF & (*ch_info++ >> 16);
+	vfe32_ctrl->outpath.out0.ch2 = 0x0000FFFF & *ch_info++;
+	vfe32_ctrl->outpath.out1.ch0 = 0x0000FFFF & *ch_info;
+	vfe32_ctrl->outpath.out1.ch1 = 0x0000FFFF & (*ch_info++ >> 16);
+	vfe32_ctrl->outpath.out1.ch2 = 0x0000FFFF & *ch_info++;
+	vfe32_ctrl->outpath.out2.ch0 = 0x0000FFFF & *ch_info;
+	vfe32_ctrl->outpath.out2.ch1 = 0x0000FFFF & (*ch_info++ >> 16);
+	vfe32_ctrl->outpath.out2.ch2 = 0x0000FFFF & *ch_info++;
+
+	switch (mode) {
+	case OUTPUT_PRIM:
+		vfe32_ctrl->outpath.output_mode =
+			VFE32_OUTPUT_MODE_PRIMARY;
+		break;
+	case OUTPUT_PRIM_ALL_CHNLS:
+		vfe32_ctrl->outpath.output_mode =
+			VFE32_OUTPUT_MODE_PRIMARY_ALL_CHNLS;
+		break;
+	case OUTPUT_PRIM|OUTPUT_SEC:
+		vfe32_ctrl->outpath.output_mode =
+			VFE32_OUTPUT_MODE_PRIMARY;
+		vfe32_ctrl->outpath.output_mode |=
+			VFE32_OUTPUT_MODE_SECONDARY;
+		break;
+	case OUTPUT_PRIM|OUTPUT_SEC_ALL_CHNLS:
+		vfe32_ctrl->outpath.output_mode =
+			VFE32_OUTPUT_MODE_PRIMARY;
+		vfe32_ctrl->outpath.output_mode |=
+			VFE32_OUTPUT_MODE_SECONDARY_ALL_CHNLS;
+		break;
+	case OUTPUT_PRIM_ALL_CHNLS|OUTPUT_SEC:
+		vfe32_ctrl->outpath.output_mode =
+			VFE32_OUTPUT_MODE_PRIMARY_ALL_CHNLS;
+		vfe32_ctrl->outpath.output_mode |=
+			VFE32_OUTPUT_MODE_SECONDARY;
+		break;
+	default:
+		pr_err("%s Invalid AXI mode %d ", __func__, mode);
+		return -EINVAL;
+	}
+	msm_camera_io_w(*ao, vfe32_ctrl->vfebase +
+		VFE_BUS_IO_FORMAT_CFG);
+	msm_camera_io_memcpy(vfe32_ctrl->vfebase +
+		vfe32_cmd[VFE_CMD_AXI_OUT_CFG].offset, axi_cfg,
+		vfe32_cmd[VFE_CMD_AXI_OUT_CFG].length - V32_AXI_CH_INF_LEN
+		- V32_AXI_BUS_FMT_LEN);
+	return 0;
+}
+
+static void vfe32_reset_internal_variables(void)
+{
+	unsigned long flags;
+	vfe32_ctrl->vfeImaskCompositePacked = 0;
+	/* state control variables */
+	vfe32_ctrl->start_ack_pending = FALSE;
+	atomic_set(&irq_cnt, 0);
+
+	spin_lock_irqsave(&vfe32_ctrl->stop_flag_lock, flags);
+	vfe32_ctrl->stop_ack_pending  = FALSE;
+	spin_unlock_irqrestore(&vfe32_ctrl->stop_flag_lock, flags);
+
+	vfe32_ctrl->reset_ack_pending  = FALSE;
+
+	spin_lock_irqsave(&vfe32_ctrl->update_ack_lock, flags);
+	vfe32_ctrl->update_ack_pending = FALSE;
+	spin_unlock_irqrestore(&vfe32_ctrl->update_ack_lock, flags);
+
+	vfe32_ctrl->recording_state = VFE_STATE_IDLE;
+	vfe32_ctrl->liveshot_state = VFE_STATE_IDLE;
+
+	atomic_set(&vfe32_ctrl->vstate, 0);
+
+	/* 0 for continuous mode, 1 for snapshot mode */
+	vfe32_ctrl->operation_mode = 0;
+	vfe32_ctrl->outpath.output_mode = 0;
+	vfe32_ctrl->vfe_capture_count = 0;
+
+	/* this is unsigned 32 bit integer. */
+	vfe32_ctrl->vfeFrameId = 0;
+	/* Stats control variables. */
+	memset(&(vfe32_ctrl->afStatsControl), 0,
+		sizeof(struct vfe_stats_control));
+
+	memset(&(vfe32_ctrl->awbStatsControl), 0,
+		sizeof(struct vfe_stats_control));
+
+	memset(&(vfe32_ctrl->aecStatsControl), 0,
+		sizeof(struct vfe_stats_control));
+
+	memset(&(vfe32_ctrl->ihistStatsControl), 0,
+		sizeof(struct vfe_stats_control));
+
+	memset(&(vfe32_ctrl->rsStatsControl), 0,
+		sizeof(struct vfe_stats_control));
+
+	memset(&(vfe32_ctrl->csStatsControl), 0,
+		sizeof(struct vfe_stats_control));
+
+	vfe32_ctrl->frame_skip_cnt = 31;
+	vfe32_ctrl->frame_skip_pattern = 0xffffffff;
+	vfe32_ctrl->snapshot_frame_cnt = 0;
+}
+
+static void vfe32_reset(void)
+{
+	vfe32_reset_internal_variables();
+	/* disable all interrupts.  vfeImaskLocal is also reset to 0
+	* to begin with. */
+	msm_camera_io_w(VFE_DISABLE_ALL_IRQS,
+		vfe32_ctrl->vfebase + VFE_IRQ_MASK_0);
+
+	msm_camera_io_w(VFE_DISABLE_ALL_IRQS,
+		vfe32_ctrl->vfebase + VFE_IRQ_MASK_1);
+
+	/* clear all pending interrupts*/
+	msm_camera_io_w(VFE_CLEAR_ALL_IRQS,
+		vfe32_ctrl->vfebase + VFE_IRQ_CLEAR_0);
+	msm_camera_io_w(VFE_CLEAR_ALL_IRQS,
+		vfe32_ctrl->vfebase + VFE_IRQ_CLEAR_1);
+
+	/* Ensure the write order while writing
+	to the command register using the barrier */
+	msm_camera_io_w_mb(1, vfe32_ctrl->vfebase + VFE_IRQ_CMD);
+
+	/* enable reset_ack interrupt.  */
+	msm_camera_io_w(VFE_IMASK_WHILE_STOPPING_1,
+	vfe32_ctrl->vfebase + VFE_IRQ_MASK_1);
+
+	/* Write to VFE_GLOBAL_RESET_CMD to reset the vfe hardware. Once reset
+	 * is done, hardware interrupt will be generated.  VFE ist processes
+	 * the interrupt to complete the function call.  Note that the reset
+	 * function is synchronous. */
+
+	/* Ensure the write order while writing
+	to the command register using the barrier */
+	msm_camera_io_w_mb(VFE_RESET_UPON_RESET_CMD,
+		vfe32_ctrl->vfebase + VFE_GLOBAL_RESET);
+}
+
+static int vfe32_operation_config(uint32_t *cmd)
+{
+	uint32_t *p = cmd;
+
+	vfe32_ctrl->operation_mode = *p;
+	vfe32_ctrl->stats_comp = *(++p);
+	vfe32_ctrl->hfr_mode = *(++p);
+
+	msm_camera_io_w(*(++p), vfe32_ctrl->vfebase + VFE_CFG);
+	msm_camera_io_w(*(++p), vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+	msm_camera_io_w(*(++p), vfe32_ctrl->vfebase + VFE_PIXEL_IF_CFG);
+	if (msm_camera_io_r(vfe32_ctrl->vfebase + V32_GET_HW_VERSION_OFF) ==
+		VFE33_HW_NUMBER) {
+		msm_camera_io_w(*(++p), vfe32_ctrl->vfebase + VFE_RDI0_CFG);
+		msm_camera_io_w(*(++p), vfe32_ctrl->vfebase + VFE_RDI1_CFG);
+	}  else {
+		++p;
+		++p;
+	}
+	msm_camera_io_w(*(++p), vfe32_ctrl->vfebase + VFE_REALIGN_BUF);
+	msm_camera_io_w(*(++p), vfe32_ctrl->vfebase + VFE_CHROMA_UP);
+	msm_camera_io_w(*(++p), vfe32_ctrl->vfebase + VFE_STATS_CFG);
+	return 0;
+}
+
+static uint32_t vfe_stats_awb_buf_init(struct vfe_cmd_stats_buf *in)
+{
+	uint32_t *ptr = in->statsBuf;
+	uint32_t addr;
+
+	addr = ptr[0];
+	msm_camera_io_w(addr,
+		vfe32_ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PING_ADDR);
+	addr = ptr[1];
+	msm_camera_io_w(addr,
+		vfe32_ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PONG_ADDR);
+	vfe32_ctrl->awbStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+	return 0;
+}
+
+static uint32_t vfe_stats_aec_buf_init(struct vfe_cmd_stats_buf *in)
+{
+	uint32_t *ptr = in->statsBuf;
+	uint32_t addr;
+
+	addr = ptr[0];
+	msm_camera_io_w(addr,
+		vfe32_ctrl->vfebase + VFE_BUS_STATS_AEC_WR_PING_ADDR);
+	addr = ptr[1];
+	msm_camera_io_w(addr,
+		vfe32_ctrl->vfebase + VFE_BUS_STATS_AEC_WR_PONG_ADDR);
+
+	vfe32_ctrl->aecStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+	return 0;
+}
+
+static uint32_t vfe_stats_af_buf_init(struct vfe_cmd_stats_buf *in)
+{
+	uint32_t *ptr = in->statsBuf;
+	uint32_t addr;
+
+	addr = ptr[0];
+	msm_camera_io_w(addr,
+		vfe32_ctrl->vfebase + VFE_BUS_STATS_AF_WR_PING_ADDR);
+	addr = ptr[1];
+	msm_camera_io_w(addr,
+		vfe32_ctrl->vfebase + VFE_BUS_STATS_AF_WR_PONG_ADDR);
+
+	vfe32_ctrl->afStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+	return 0;
+}
+
+static uint32_t vfe_stats_ihist_buf_init(struct vfe_cmd_stats_buf *in)
+{
+	uint32_t *ptr = in->statsBuf;
+	uint32_t addr;
+
+	addr = ptr[0];
+	msm_camera_io_w(addr,
+		vfe32_ctrl->vfebase + VFE_BUS_STATS_HIST_WR_PING_ADDR);
+	addr = ptr[1];
+	msm_camera_io_w(addr,
+		vfe32_ctrl->vfebase + VFE_BUS_STATS_HIST_WR_PONG_ADDR);
+
+	vfe32_ctrl->ihistStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+	return 0;
+}
+
+static uint32_t vfe_stats_rs_buf_init(struct vfe_cmd_stats_buf *in)
+{
+	uint32_t *ptr = in->statsBuf;
+	uint32_t addr;
+
+	addr = ptr[0];
+	msm_camera_io_w(addr,
+		vfe32_ctrl->vfebase + VFE_BUS_STATS_RS_WR_PING_ADDR);
+	addr = ptr[1];
+	msm_camera_io_w(addr,
+		vfe32_ctrl->vfebase + VFE_BUS_STATS_RS_WR_PONG_ADDR);
+
+	vfe32_ctrl->rsStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+	return 0;
+}
+
+static uint32_t vfe_stats_cs_buf_init(struct vfe_cmd_stats_buf *in)
+{
+	uint32_t *ptr = in->statsBuf;
+	uint32_t addr;
+
+	addr = ptr[0];
+	msm_camera_io_w(addr,
+		vfe32_ctrl->vfebase + VFE_BUS_STATS_CS_WR_PING_ADDR);
+	addr = ptr[1];
+	msm_camera_io_w(addr,
+		vfe32_ctrl->vfebase + VFE_BUS_STATS_CS_WR_PONG_ADDR);
+
+	vfe32_ctrl->csStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+	return 0;
+}
+
+static void vfe32_start_common(void)
+{
+	uint32_t irq_mask = 0x00E00021;
+	vfe32_ctrl->start_ack_pending = TRUE;
+	CDBG("VFE opertaion mode = 0x%x, output mode = 0x%x\n",
+		vfe32_ctrl->operation_mode, vfe32_ctrl->outpath.output_mode);
+	if (vfe32_ctrl->stats_comp)
+		irq_mask |= VFE_IRQ_STATUS0_STATS_COMPOSIT_MASK;
+	else
+		irq_mask |= 0x000FE000;
+
+	msm_camera_io_w(irq_mask, vfe32_ctrl->vfebase + VFE_IRQ_MASK_0);
+	msm_camera_io_w(VFE_IMASK_WHILE_STOPPING_1,
+		vfe32_ctrl->vfebase + VFE_IRQ_MASK_1);
+
+	/* Ensure the write order while writing
+	to the command register using the barrier */
+	msm_camera_io_w_mb(1, vfe32_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+	msm_camera_io_w_mb(1, vfe32_ctrl->vfebase + VFE_CAMIF_COMMAND);
+
+
+	atomic_set(&vfe32_ctrl->vstate, 1);
+}
+
+static int vfe32_start_recording(struct msm_cam_media_controller *pmctl)
+{
+	msm_camio_bus_scale_cfg(
+		pmctl->sdata->pdata->cam_bus_scale_table, S_VIDEO);
+	vfe32_ctrl->recording_state = VFE_STATE_START_REQUESTED;
+	msm_camera_io_w_mb(1, vfe32_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+	return 0;
+}
+
+static int vfe32_stop_recording(struct msm_cam_media_controller *pmctl)
+{
+	vfe32_ctrl->recording_state = VFE_STATE_STOP_REQUESTED;
+	msm_camera_io_w_mb(1, vfe32_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+	msm_camio_bus_scale_cfg(
+		pmctl->sdata->pdata->cam_bus_scale_table, S_PREVIEW);
+	return 0;
+}
+
+static void vfe32_start_liveshot(struct msm_cam_media_controller *pmctl)
+{
+	/* Hardcode 1 live snapshot for now. */
+	vfe32_ctrl->outpath.out0.capture_cnt = 1;
+	vfe32_ctrl->vfe_capture_count = vfe32_ctrl->outpath.out0.capture_cnt;
+
+	vfe32_ctrl->liveshot_state = VFE_STATE_START_REQUESTED;
+	msm_camera_io_w_mb(1, vfe32_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+}
+
+static int vfe32_zsl(struct msm_cam_media_controller *pmctl)
+{
+	uint32_t irq_comp_mask = 0;
+	/* capture command is valid for both idle and active state. */
+	irq_comp_mask	=
+		msm_camera_io_r(vfe32_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+
+	CDBG("%s:op mode %d O/P Mode %d\n", __func__,
+		vfe32_ctrl->operation_mode, vfe32_ctrl->outpath.output_mode);
+
+	if (vfe32_ctrl->outpath.output_mode & VFE32_OUTPUT_MODE_PRIMARY) {
+		irq_comp_mask |= ((0x1 << (vfe32_ctrl->outpath.out0.ch0)) |
+				(0x1 << (vfe32_ctrl->outpath.out0.ch1)));
+	} else if (vfe32_ctrl->outpath.output_mode &
+			VFE32_OUTPUT_MODE_PRIMARY_ALL_CHNLS) {
+		irq_comp_mask |= ((0x1 << (vfe32_ctrl->outpath.out0.ch0)) |
+				(0x1 << (vfe32_ctrl->outpath.out0.ch1)) |
+				(0x1 << (vfe32_ctrl->outpath.out0.ch2)));
+	}
+
+	if (vfe32_ctrl->outpath.output_mode & VFE32_OUTPUT_MODE_SECONDARY) {
+		irq_comp_mask |= ((0x1 << (vfe32_ctrl->outpath.out1.ch0 + 8)) |
+				(0x1 << (vfe32_ctrl->outpath.out1.ch1 + 8)));
+	} else if (vfe32_ctrl->outpath.output_mode &
+			   VFE32_OUTPUT_MODE_SECONDARY_ALL_CHNLS) {
+		irq_comp_mask |= ((0x1 << (vfe32_ctrl->outpath.out1.ch0 + 8)) |
+				(0x1 << (vfe32_ctrl->outpath.out1.ch1 + 8)) |
+				(0x1 << (vfe32_ctrl->outpath.out1.ch2 + 8)));
+	}
+
+	if (vfe32_ctrl->outpath.output_mode & VFE32_OUTPUT_MODE_PRIMARY) {
+		msm_camera_io_w(1, vfe32_ctrl->vfebase +
+			vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch0]);
+		msm_camera_io_w(1, vfe32_ctrl->vfebase +
+			vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch1]);
+	} else if (vfe32_ctrl->outpath.output_mode &
+				VFE32_OUTPUT_MODE_PRIMARY_ALL_CHNLS) {
+		msm_camera_io_w(1, vfe32_ctrl->vfebase +
+			vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch0]);
+		msm_camera_io_w(1, vfe32_ctrl->vfebase +
+			vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch1]);
+		msm_camera_io_w(1, vfe32_ctrl->vfebase +
+			vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch2]);
+	}
+
+	if (vfe32_ctrl->outpath.output_mode & VFE32_OUTPUT_MODE_SECONDARY) {
+		msm_camera_io_w(1, vfe32_ctrl->vfebase +
+			vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out1.ch0]);
+		msm_camera_io_w(1, vfe32_ctrl->vfebase +
+			vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out1.ch1]);
+	} else if (vfe32_ctrl->outpath.output_mode &
+				VFE32_OUTPUT_MODE_SECONDARY_ALL_CHNLS) {
+		msm_camera_io_w(1, vfe32_ctrl->vfebase +
+			vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out1.ch0]);
+		msm_camera_io_w(1, vfe32_ctrl->vfebase +
+			vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out1.ch1]);
+		msm_camera_io_w(1, vfe32_ctrl->vfebase +
+			vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out1.ch2]);
+	}
+
+	msm_camera_io_w(irq_comp_mask, vfe32_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+	vfe32_start_common();
+	msm_camio_bus_scale_cfg(
+		pmctl->sdata->pdata->cam_bus_scale_table, S_ZSL);
+
+	msm_camera_io_w(1, vfe32_ctrl->vfebase + 0x18C);
+	msm_camera_io_w(1, vfe32_ctrl->vfebase + 0x188);
+	return 0;
+}
+static int vfe32_capture_raw(
+	struct msm_cam_media_controller *pmctl,
+	uint32_t num_frames_capture)
+{
+	uint32_t irq_comp_mask = 0;
+
+	vfe32_ctrl->outpath.out0.capture_cnt = num_frames_capture;
+	vfe32_ctrl->vfe_capture_count = num_frames_capture;
+
+	irq_comp_mask	=
+		msm_camera_io_r(vfe32_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+
+	if (vfe32_ctrl->outpath.output_mode & VFE32_OUTPUT_MODE_PRIMARY) {
+		irq_comp_mask |= (0x1 << (vfe32_ctrl->outpath.out0.ch0));
+		msm_camera_io_w(1, vfe32_ctrl->vfebase +
+			vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch0]);
+	}
+
+	msm_camera_io_w(irq_comp_mask, vfe32_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+	msm_camio_bus_scale_cfg(
+		pmctl->sdata->pdata->cam_bus_scale_table, S_CAPTURE);
+	vfe32_start_common();
+	return 0;
+}
+
+static int vfe32_capture(
+	struct msm_cam_media_controller *pmctl,
+	uint32_t num_frames_capture)
+{
+	uint32_t irq_comp_mask = 0;
+
+	/* capture command is valid for both idle and active state. */
+	vfe32_ctrl->outpath.out1.capture_cnt = num_frames_capture;
+	if (vfe32_ctrl->operation_mode == VFE_OUTPUTS_MAIN_AND_THUMB ||
+		vfe32_ctrl->operation_mode == VFE_OUTPUTS_THUMB_AND_MAIN ||
+		vfe32_ctrl->operation_mode == VFE_OUTPUTS_JPEG_AND_THUMB ||
+		vfe32_ctrl->operation_mode == VFE_OUTPUTS_THUMB_AND_JPEG) {
+		vfe32_ctrl->outpath.out0.capture_cnt =
+			num_frames_capture;
+	}
+
+	vfe32_ctrl->vfe_capture_count = num_frames_capture;
+	irq_comp_mask = msm_camera_io_r(
+				vfe32_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+
+	if (vfe32_ctrl->operation_mode == VFE_OUTPUTS_MAIN_AND_THUMB ||
+		vfe32_ctrl->operation_mode == VFE_OUTPUTS_JPEG_AND_THUMB ||
+		vfe32_ctrl->operation_mode == VFE_OUTPUTS_THUMB_AND_MAIN) {
+		if (vfe32_ctrl->outpath.output_mode &
+			VFE32_OUTPUT_MODE_PRIMARY) {
+			irq_comp_mask |= (0x1 << vfe32_ctrl->outpath.out0.ch0 |
+					0x1 << vfe32_ctrl->outpath.out0.ch1);
+		}
+		if (vfe32_ctrl->outpath.output_mode &
+			VFE32_OUTPUT_MODE_SECONDARY) {
+			irq_comp_mask |=
+				(0x1 << (vfe32_ctrl->outpath.out1.ch0 + 8) |
+				0x1 << (vfe32_ctrl->outpath.out1.ch1 + 8));
+		}
+		if (vfe32_ctrl->outpath.output_mode &
+			VFE32_OUTPUT_MODE_PRIMARY) {
+			msm_camera_io_w(1, vfe32_ctrl->vfebase +
+				vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch0]);
+			msm_camera_io_w(1, vfe32_ctrl->vfebase +
+				vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch1]);
+		}
+		if (vfe32_ctrl->outpath.output_mode &
+			VFE32_OUTPUT_MODE_SECONDARY) {
+			msm_camera_io_w(1, vfe32_ctrl->vfebase +
+				vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out1.ch0]);
+			msm_camera_io_w(1, vfe32_ctrl->vfebase +
+				vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out1.ch1]);
+		}
+	}
+
+	vfe32_ctrl->vfe_capture_count = num_frames_capture;
+
+	msm_camera_io_w(irq_comp_mask, vfe32_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+	msm_camera_io_r(vfe32_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+	msm_camio_bus_scale_cfg(
+		pmctl->sdata->pdata->cam_bus_scale_table, S_CAPTURE);
+
+	vfe32_start_common();
+	/* for debug */
+	msm_camera_io_w(1, vfe32_ctrl->vfebase + 0x18C);
+	msm_camera_io_w(1, vfe32_ctrl->vfebase + 0x188);
+	return 0;
+}
+
+static int vfe32_start(struct msm_cam_media_controller *pmctl)
+{
+	uint32_t irq_comp_mask = 0;
+
+	irq_comp_mask	=
+		msm_camera_io_r(vfe32_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+
+	if (vfe32_ctrl->outpath.output_mode & VFE32_OUTPUT_MODE_PRIMARY) {
+		irq_comp_mask |= (0x1 << vfe32_ctrl->outpath.out0.ch0 |
+			0x1 << vfe32_ctrl->outpath.out0.ch1);
+	} else if (vfe32_ctrl->outpath.output_mode &
+			   VFE32_OUTPUT_MODE_PRIMARY_ALL_CHNLS) {
+		irq_comp_mask |= (0x1 << vfe32_ctrl->outpath.out0.ch0 |
+			0x1 << vfe32_ctrl->outpath.out0.ch1 |
+			0x1 << vfe32_ctrl->outpath.out0.ch2);
+	}
+	if (vfe32_ctrl->outpath.output_mode & VFE32_OUTPUT_MODE_SECONDARY) {
+		irq_comp_mask |= (0x1 << (vfe32_ctrl->outpath.out1.ch0 + 8) |
+			0x1 << (vfe32_ctrl->outpath.out1.ch1 + 8));
+	} else if (vfe32_ctrl->outpath.output_mode &
+			VFE32_OUTPUT_MODE_SECONDARY_ALL_CHNLS) {
+		irq_comp_mask |= (0x1 << (vfe32_ctrl->outpath.out1.ch0 + 8) |
+			0x1 << (vfe32_ctrl->outpath.out1.ch1 + 8) |
+			0x1 << (vfe32_ctrl->outpath.out1.ch2 + 8));
+	}
+	msm_camera_io_w(irq_comp_mask, vfe32_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+
+	switch (vfe32_ctrl->operation_mode) {
+	case VFE_OUTPUTS_PREVIEW:
+	case VFE_OUTPUTS_PREVIEW_AND_VIDEO:
+		if (vfe32_ctrl->outpath.output_mode &
+			VFE32_OUTPUT_MODE_PRIMARY) {
+			msm_camera_io_w(1, vfe32_ctrl->vfebase +
+			vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch0]);
+			msm_camera_io_w(1, vfe32_ctrl->vfebase +
+			vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch1]);
+		} else if (vfe32_ctrl->outpath.output_mode &
+				VFE32_OUTPUT_MODE_PRIMARY_ALL_CHNLS) {
+			msm_camera_io_w(1, vfe32_ctrl->vfebase +
+			vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch0]);
+			msm_camera_io_w(1, vfe32_ctrl->vfebase +
+			vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch1]);
+			msm_camera_io_w(1, vfe32_ctrl->vfebase +
+			vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch2]);
+		}
+		break;
+	default:
+		if (vfe32_ctrl->outpath.output_mode &
+			VFE32_OUTPUT_MODE_SECONDARY) {
+			msm_camera_io_w(1, vfe32_ctrl->vfebase +
+			vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out1.ch0]);
+			msm_camera_io_w(1, vfe32_ctrl->vfebase +
+			vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out1.ch1]);
+		} else if (vfe32_ctrl->outpath.output_mode &
+			VFE32_OUTPUT_MODE_SECONDARY_ALL_CHNLS) {
+			msm_camera_io_w(1, vfe32_ctrl->vfebase +
+			vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out1.ch0]);
+			msm_camera_io_w(1, vfe32_ctrl->vfebase +
+			vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out1.ch1]);
+			msm_camera_io_w(1, vfe32_ctrl->vfebase +
+			vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out1.ch2]);
+		}
+		break;
+	}
+
+	msm_camio_bus_scale_cfg(
+		pmctl->sdata->pdata->cam_bus_scale_table, S_PREVIEW);
+	vfe32_start_common();
+	return 0;
+}
+
+static void vfe32_update(void)
+{
+	unsigned long flags;
+	uint32_t value = 0;
+	if (vfe32_ctrl->update_linear) {
+		if (!msm_camera_io_r(
+			vfe32_ctrl->vfebase + V32_LINEARIZATION_OFF1))
+			msm_camera_io_w(1,
+				vfe32_ctrl->vfebase + V32_LINEARIZATION_OFF1);
+		else
+			msm_camera_io_w(0,
+				vfe32_ctrl->vfebase + V32_LINEARIZATION_OFF1);
+		vfe32_ctrl->update_linear = false;
+	}
+
+	if (vfe32_ctrl->update_rolloff) {
+		value = msm_camera_io_r(vfe32_ctrl->vfebase +
+			V33_PCA_ROLL_OFF_CFG_OFF1);
+		value ^= V33_PCA_ROLL_OFF_LUT_BANK_SEL_MASK;
+		msm_camera_io_w(value, vfe32_ctrl->vfebase +
+			V33_PCA_ROLL_OFF_CFG_OFF1);
+		vfe32_ctrl->update_rolloff = false;
+	}
+
+	if (vfe32_ctrl->update_la) {
+		if (!msm_camera_io_r(vfe32_ctrl->vfebase + V32_LA_OFF))
+			msm_camera_io_w(1,
+				vfe32_ctrl->vfebase + V32_LA_OFF);
+		else
+			msm_camera_io_w(0,
+				vfe32_ctrl->vfebase + V32_LA_OFF);
+		vfe32_ctrl->update_la = false;
+	}
+
+	if (vfe32_ctrl->update_gamma) {
+		value = msm_camera_io_r(vfe32_ctrl->vfebase + V32_RGB_G_OFF);
+		value ^= V32_GAMMA_LUT_BANK_SEL_MASK;
+		msm_camera_io_w(value, vfe32_ctrl->vfebase + V32_RGB_G_OFF);
+		vfe32_ctrl->update_gamma = false;
+	}
+
+	spin_lock_irqsave(&vfe32_ctrl->update_ack_lock, flags);
+	vfe32_ctrl->update_ack_pending = TRUE;
+	spin_unlock_irqrestore(&vfe32_ctrl->update_ack_lock, flags);
+	/* Ensure the write order while writing
+	to the command register using the barrier */
+	msm_camera_io_w_mb(1, vfe32_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+	return;
+}
+
+static void vfe32_sync_timer_stop(void)
+{
+	uint32_t value = 0;
+	vfe32_ctrl->sync_timer_state = 0;
+	if (vfe32_ctrl->sync_timer_number == 0)
+		value = 0x10000;
+	else if (vfe32_ctrl->sync_timer_number == 1)
+		value = 0x20000;
+	else if (vfe32_ctrl->sync_timer_number == 2)
+		value = 0x40000;
+
+	/* Timer Stop */
+	msm_camera_io_w(value, vfe32_ctrl->vfebase + V32_SYNC_TIMER_OFF);
+}
+
+static void vfe32_sync_timer_start(const uint32_t *tbl)
+{
+	/* set bit 8 for auto increment. */
+	uint32_t value = 1;
+	uint32_t val;
+
+	vfe32_ctrl->sync_timer_state = *tbl++;
+	vfe32_ctrl->sync_timer_repeat_count = *tbl++;
+	vfe32_ctrl->sync_timer_number = *tbl++;
+	CDBG("%s timer_state %d, repeat_cnt %d timer number %d\n",
+		 __func__, vfe32_ctrl->sync_timer_state,
+		 vfe32_ctrl->sync_timer_repeat_count,
+		 vfe32_ctrl->sync_timer_number);
+
+	if (vfe32_ctrl->sync_timer_state) { /* Start Timer */
+		value = value << vfe32_ctrl->sync_timer_number;
+	} else { /* Stop Timer */
+		CDBG("Failed to Start timer\n");
+		return;
+	}
+
+	/* Timer Start */
+	msm_camera_io_w(value, vfe32_ctrl->vfebase + V32_SYNC_TIMER_OFF);
+	/* Sync Timer Line Start */
+	value = *tbl++;
+	msm_camera_io_w(value, vfe32_ctrl->vfebase + V32_SYNC_TIMER_OFF +
+		4 + ((vfe32_ctrl->sync_timer_number) * 12));
+	/* Sync Timer Pixel Start */
+	value = *tbl++;
+	msm_camera_io_w(value, vfe32_ctrl->vfebase + V32_SYNC_TIMER_OFF +
+			 8 + ((vfe32_ctrl->sync_timer_number) * 12));
+	/* Sync Timer Pixel Duration */
+	value = *tbl++;
+	val = vfe_clk_rate / 10000;
+	val = 10000000 / val;
+	val = value * 10000 / val;
+	CDBG("%s: Pixel Clk Cycles!!! %d\n", __func__, val);
+	msm_camera_io_w(val, vfe32_ctrl->vfebase + V32_SYNC_TIMER_OFF +
+		12 + ((vfe32_ctrl->sync_timer_number) * 12));
+	/* Timer0 Active High/LOW */
+	value = *tbl++;
+	msm_camera_io_w(value,
+		vfe32_ctrl->vfebase + V32_SYNC_TIMER_POLARITY_OFF);
+	/* Selects sync timer 0 output to drive onto timer1 port */
+	value = 0;
+	msm_camera_io_w(value, vfe32_ctrl->vfebase + V32_TIMER_SELECT_OFF);
+}
+
+static void vfe32_program_dmi_cfg(enum VFE32_DMI_RAM_SEL bankSel)
+{
+	/* set bit 8 for auto increment. */
+	uint32_t value = VFE_DMI_CFG_DEFAULT;
+	value += (uint32_t)bankSel;
+	CDBG("%s: banksel = %d\n", __func__, bankSel);
+
+	msm_camera_io_w(value, vfe32_ctrl->vfebase + VFE_DMI_CFG);
+	/* by default, always starts with offset 0.*/
+	msm_camera_io_w(0, vfe32_ctrl->vfebase + VFE_DMI_ADDR);
+}
+static void vfe32_write_gamma_cfg(enum VFE32_DMI_RAM_SEL channel_sel,
+						const uint32_t *tbl)
+{
+	int i;
+	uint32_t value, value1, value2;
+	vfe32_program_dmi_cfg(channel_sel);
+	for (i = 0 ; i < (VFE32_GAMMA_NUM_ENTRIES/2) ; i++) {
+		value = *tbl++;
+		value1 = value & 0x0000FFFF;
+		value2 = (value & 0xFFFF0000)>>16;
+		msm_camera_io_w((value1),
+			vfe32_ctrl->vfebase + VFE_DMI_DATA_LO);
+		msm_camera_io_w((value2),
+			vfe32_ctrl->vfebase + VFE_DMI_DATA_LO);
+	}
+	vfe32_program_dmi_cfg(NO_MEM_SELECTED);
+}
+
+static void vfe32_read_gamma_cfg(enum VFE32_DMI_RAM_SEL channel_sel,
+	uint32_t *tbl)
+{
+	int i;
+	vfe32_program_dmi_cfg(channel_sel);
+	CDBG("%s: Gamma table channel: %d\n", __func__, channel_sel);
+	for (i = 0 ; i < VFE32_GAMMA_NUM_ENTRIES ; i++) {
+		*tbl = msm_camera_io_r(vfe32_ctrl->vfebase + VFE_DMI_DATA_LO);
+		CDBG("%s: %08x\n", __func__, *tbl);
+		tbl++;
+	}
+	vfe32_program_dmi_cfg(NO_MEM_SELECTED);
+}
+
+static void vfe32_write_la_cfg(enum VFE32_DMI_RAM_SEL channel_sel,
+						const uint32_t *tbl)
+{
+	uint32_t i;
+	uint32_t value, value1, value2;
+
+	vfe32_program_dmi_cfg(channel_sel);
+	for (i = 0 ; i < (VFE32_LA_TABLE_LENGTH/2) ; i++) {
+		value = *tbl++;
+		value1 = value & 0x0000FFFF;
+		value2 = (value & 0xFFFF0000)>>16;
+		msm_camera_io_w((value1),
+			vfe32_ctrl->vfebase + VFE_DMI_DATA_LO);
+		msm_camera_io_w((value2),
+			vfe32_ctrl->vfebase + VFE_DMI_DATA_LO);
+	}
+	vfe32_program_dmi_cfg(NO_MEM_SELECTED);
+}
+
+static struct vfe32_output_ch *vfe32_get_ch(int path)
+{
+	struct vfe32_output_ch *ch = NULL;
+
+	if (path == VFE_MSG_OUTPUT_PRIMARY)
+		ch = &vfe32_ctrl->outpath.out0;
+	else if (path == VFE_MSG_OUTPUT_SECONDARY)
+		ch = &vfe32_ctrl->outpath.out1;
+	else
+		pr_err("%s: Invalid path %d\n", __func__,
+			path);
+
+	BUG_ON(ch == NULL);
+	return ch;
+}
+static struct msm_free_buf *vfe32_check_free_buffer(int id, int path)
+{
+	struct vfe32_output_ch *outch = NULL;
+	struct msm_free_buf *b = NULL;
+	vfe32_subdev_notify(id, path);
+	outch = vfe32_get_ch(path);
+	if (outch->free_buf.ch_paddr[0])
+		b = &outch->free_buf;
+	return b;
+}
+static int vfe32_configure_pingpong_buffers(int id, int path)
+{
+	struct vfe32_output_ch *outch = NULL;
+	int rc = 0;
+	vfe32_subdev_notify(id, path);
+	outch = vfe32_get_ch(path);
+	if (outch->ping.ch_paddr[0] && outch->pong.ch_paddr[0]) {
+		/* Configure Preview Ping Pong */
+		pr_info("%s Configure ping/pong address for %d",
+						__func__, path);
+		vfe32_put_ch_ping_addr(outch->ch0,
+			outch->ping.ch_paddr[0]);
+		vfe32_put_ch_pong_addr(outch->ch0,
+			outch->pong.ch_paddr[0]);
+
+		if (vfe32_ctrl->operation_mode !=
+			VFE_OUTPUTS_RAW) {
+			vfe32_put_ch_ping_addr(outch->ch1,
+				outch->ping.ch_paddr[1]);
+			vfe32_put_ch_pong_addr(outch->ch1,
+				outch->pong.ch_paddr[1]);
+		}
+
+		if (outch->ping.num_planes > 2)
+			vfe32_put_ch_ping_addr(outch->ch2,
+				outch->ping.ch_paddr[2]);
+		if (outch->pong.num_planes > 2)
+			vfe32_put_ch_pong_addr(outch->ch2,
+				outch->pong.ch_paddr[2]);
+
+		/* avoid stale info */
+		memset(&outch->ping, 0, sizeof(struct msm_free_buf));
+		memset(&outch->pong, 0, sizeof(struct msm_free_buf));
+	} else {
+		pr_err("%s ping/pong addr is null!!", __func__);
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+static void vfe32_write_linear_cfg(enum VFE32_DMI_RAM_SEL channel_sel,
+	const uint32_t *tbl)
+{
+	uint32_t i;
+
+	vfe32_program_dmi_cfg(channel_sel);
+	/* for loop for configuring LUT. */
+	for (i = 0 ; i < VFE32_LINEARIZATON_TABLE_LENGTH ; i++) {
+		msm_camera_io_w(*tbl, vfe32_ctrl->vfebase + VFE_DMI_DATA_LO);
+		tbl++;
+	}
+	CDBG("done writing to linearization table\n");
+	vfe32_program_dmi_cfg(NO_MEM_SELECTED);
+}
+
+static void vfe32_send_isp_msg(
+	struct vfe32_ctrl_type *vctrl,
+	uint32_t isp_msg_id)
+{
+	struct isp_msg_event isp_msg_evt;
+
+	isp_msg_evt.msg_id = isp_msg_id;
+	isp_msg_evt.sof_count = vfe32_ctrl->vfeFrameId;
+	v4l2_subdev_notify(&vctrl->subdev,
+			NOTIFY_ISP_MSG_EVT,
+			(void *)&isp_msg_evt);
+}
+
+static int vfe32_proc_general(
+	struct msm_cam_media_controller *pmctl,
+	struct msm_isp_cmd *cmd)
+{
+	int i , rc = 0;
+	uint32_t old_val = 0 , new_val = 0;
+	uint32_t *cmdp = NULL;
+	uint32_t *cmdp_local = NULL;
+	uint32_t snapshot_cnt = 0;
+	uint32_t temp1 = 0, temp2 = 0;
+
+	CDBG("vfe32_proc_general: cmdID = %s, length = %d\n",
+		vfe32_general_cmd[cmd->id], cmd->length);
+	switch (cmd->id) {
+	case VFE_CMD_RESET:
+		pr_info("vfe32_proc_general: cmdID = %s\n",
+			vfe32_general_cmd[cmd->id]);
+		vfe32_reset();
+		break;
+	case VFE_CMD_START:
+		pr_info("vfe32_proc_general: cmdID = %s\n",
+			vfe32_general_cmd[cmd->id]);
+		if ((vfe32_ctrl->operation_mode ==
+				VFE_OUTPUTS_PREVIEW_AND_VIDEO) ||
+				(vfe32_ctrl->operation_mode ==
+				VFE_OUTPUTS_PREVIEW))
+			/* Configure primary channel */
+			rc = vfe32_configure_pingpong_buffers(
+				VFE_MSG_V32_START, VFE_MSG_OUTPUT_PRIMARY);
+		else
+			/* Configure secondary channel */
+			rc = vfe32_configure_pingpong_buffers(
+				VFE_MSG_V32_START, VFE_MSG_OUTPUT_SECONDARY);
+		if (rc < 0) {
+			pr_err("%s error configuring pingpong buffers"
+				   " for preview", __func__);
+			rc = -EINVAL;
+			goto proc_general_done;
+		}
+		rc = vfe32_start(pmctl);
+		break;
+	case VFE_CMD_UPDATE:
+		vfe32_update();
+		break;
+	case VFE_CMD_CAPTURE_RAW:
+		pr_info("%s: cmdID = VFE_CMD_CAPTURE_RAW\n", __func__);
+		if (copy_from_user(&snapshot_cnt, (void __user *)(cmd->value),
+				sizeof(uint32_t))) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		rc = vfe32_configure_pingpong_buffers(VFE_MSG_V32_CAPTURE,
+							VFE_MSG_OUTPUT_PRIMARY);
+		if (rc < 0) {
+			pr_err("%s error configuring pingpong buffers"
+				   " for snapshot", __func__);
+			rc = -EINVAL;
+			goto proc_general_done;
+		}
+		rc = vfe32_capture_raw(pmctl, snapshot_cnt);
+		break;
+	case VFE_CMD_CAPTURE:
+		if (copy_from_user(&snapshot_cnt, (void __user *)(cmd->value),
+				sizeof(uint32_t))) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+
+		if (vfe32_ctrl->operation_mode == VFE_OUTPUTS_JPEG_AND_THUMB ||
+		vfe32_ctrl->operation_mode == VFE_OUTPUTS_THUMB_AND_JPEG) {
+			if (snapshot_cnt != 1) {
+				pr_err("only support 1 inline snapshot\n");
+				rc = -EINVAL;
+				goto proc_general_done;
+			}
+			/* Configure primary channel for JPEG */
+			rc = vfe32_configure_pingpong_buffers(
+				VFE_MSG_V32_JPEG_CAPTURE,
+				VFE_MSG_OUTPUT_PRIMARY);
+		} else {
+			/* Configure primary channel */
+			rc = vfe32_configure_pingpong_buffers(
+				VFE_MSG_V32_CAPTURE,
+				VFE_MSG_OUTPUT_PRIMARY);
+		}
+		if (rc < 0) {
+			pr_err("%s error configuring pingpong buffers"
+				   " for primary output", __func__);
+			rc = -EINVAL;
+			goto proc_general_done;
+		}
+		/* Configure secondary channel */
+		rc = vfe32_configure_pingpong_buffers(VFE_MSG_V32_CAPTURE,
+						  VFE_MSG_OUTPUT_SECONDARY);
+		if (rc < 0) {
+			pr_err("%s error configuring pingpong buffers"
+				   " for secondary output", __func__);
+			rc = -EINVAL;
+			goto proc_general_done;
+		}
+		rc = vfe32_capture(pmctl, snapshot_cnt);
+		break;
+	case VFE_CMD_START_RECORDING:
+		pr_info("vfe32_proc_general: cmdID = %s\n",
+			vfe32_general_cmd[cmd->id]);
+		if (vfe32_ctrl->operation_mode ==
+			VFE_OUTPUTS_PREVIEW_AND_VIDEO)
+			rc = vfe32_configure_pingpong_buffers(
+				VFE_MSG_V32_START_RECORDING,
+				VFE_MSG_OUTPUT_SECONDARY);
+		else if (vfe32_ctrl->operation_mode ==
+			VFE_OUTPUTS_VIDEO_AND_PREVIEW)
+			rc = vfe32_configure_pingpong_buffers(
+				VFE_MSG_V32_START_RECORDING,
+				VFE_MSG_OUTPUT_PRIMARY);
+		if (rc < 0) {
+			pr_err("%s error configuring pingpong buffers"
+				" for video", __func__);
+			rc = -EINVAL;
+			goto proc_general_done;
+		}
+		rc = vfe32_start_recording(pmctl);
+		break;
+	case VFE_CMD_STOP_RECORDING:
+		pr_info("vfe32_proc_general: cmdID = %s\n",
+			vfe32_general_cmd[cmd->id]);
+		rc = vfe32_stop_recording(pmctl);
+		break;
+	case VFE_CMD_OPERATION_CFG: {
+		if (cmd->length != V32_OPERATION_CFG_LEN) {
+			rc = -EINVAL;
+			goto proc_general_done;
+		}
+		cmdp = kmalloc(V32_OPERATION_CFG_LEN, GFP_ATOMIC);
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			V32_OPERATION_CFG_LEN)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		rc = vfe32_operation_config(cmdp);
+		}
+		break;
+
+	case VFE_CMD_STATS_AE_START: {
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		old_val = msm_camera_io_r(vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val |= AE_BG_ENABLE_MASK;
+		msm_camera_io_w(old_val,
+			vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+		msm_camera_io_memcpy(
+			vfe32_ctrl->vfebase + vfe32_cmd[cmd->id].offset,
+			cmdp, (vfe32_cmd[cmd->id].length));
+		}
+		break;
+	case VFE_CMD_STATS_AF_START: {
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		old_val = msm_camera_io_r(vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val |= AF_BF_ENABLE_MASK;
+		msm_camera_io_w(old_val,
+			vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+		msm_camera_io_memcpy(
+			vfe32_ctrl->vfebase + vfe32_cmd[cmd->id].offset,
+			cmdp, (vfe32_cmd[cmd->id].length));
+		}
+		break;
+	case VFE_CMD_STATS_AWB_START: {
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		old_val = msm_camera_io_r(vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val |= AWB_ENABLE_MASK;
+		msm_camera_io_w(old_val,
+			vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+		msm_camera_io_memcpy(
+			vfe32_ctrl->vfebase + vfe32_cmd[cmd->id].offset,
+			cmdp, (vfe32_cmd[cmd->id].length));
+		}
+		break;
+
+	case VFE_CMD_STATS_IHIST_START: {
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		old_val = msm_camera_io_r(vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val |= IHIST_ENABLE_MASK;
+		msm_camera_io_w(old_val,
+			vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+		msm_camera_io_memcpy(
+			vfe32_ctrl->vfebase + vfe32_cmd[cmd->id].offset,
+			cmdp, (vfe32_cmd[cmd->id].length));
+		}
+		break;
+
+
+	case VFE_CMD_STATS_RS_START: {
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		msm_camera_io_memcpy(
+			vfe32_ctrl->vfebase + vfe32_cmd[cmd->id].offset,
+			cmdp, (vfe32_cmd[cmd->id].length));
+		}
+		break;
+
+	case VFE_CMD_STATS_CS_START: {
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		msm_camera_io_memcpy(
+			vfe32_ctrl->vfebase + vfe32_cmd[cmd->id].offset,
+			cmdp, (vfe32_cmd[cmd->id].length));
+		}
+		break;
+
+	case VFE_CMD_MCE_UPDATE:
+	case VFE_CMD_MCE_CFG:{
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		/* Incrementing with 4 so as to point to the 2nd Register as
+		the 2nd register has the mce_enable bit */
+		old_val = msm_camera_io_r(vfe32_ctrl->vfebase +
+			V32_CHROMA_SUP_OFF + 4);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		cmdp_local = cmdp;
+		new_val = *cmdp_local;
+		old_val &= MCE_EN_MASK;
+		new_val = new_val | old_val;
+		msm_camera_io_memcpy(
+			vfe32_ctrl->vfebase + V32_CHROMA_SUP_OFF + 4,
+			&new_val, 4);
+		cmdp_local += 1;
+
+		old_val = msm_camera_io_r(vfe32_ctrl->vfebase +
+			V32_CHROMA_SUP_OFF + 8);
+		new_val = *cmdp_local;
+		old_val &= MCE_Q_K_MASK;
+		new_val = new_val | old_val;
+		msm_camera_io_memcpy(
+			vfe32_ctrl->vfebase + V32_CHROMA_SUP_OFF + 8,
+			&new_val, 4);
+		cmdp_local += 1;
+		msm_camera_io_memcpy(
+			vfe32_ctrl->vfebase + vfe32_cmd[cmd->id].offset,
+			cmdp_local, (vfe32_cmd[cmd->id].length));
+		}
+		break;
+	case VFE_CMD_CHROMA_SUP_UPDATE:
+	case VFE_CMD_CHROMA_SUP_CFG:{
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		cmdp_local = cmdp;
+		msm_camera_io_memcpy(vfe32_ctrl->vfebase + V32_CHROMA_SUP_OFF,
+			cmdp_local, 4);
+
+		cmdp_local += 1;
+		new_val = *cmdp_local;
+		/* Incrementing with 4 so as to point to the 2nd Register as
+		 * the 2nd register has the mce_enable bit
+		 */
+		old_val = msm_camera_io_r(vfe32_ctrl->vfebase +
+			V32_CHROMA_SUP_OFF + 4);
+		old_val &= ~MCE_EN_MASK;
+		new_val = new_val | old_val;
+		msm_camera_io_memcpy(
+			vfe32_ctrl->vfebase + V32_CHROMA_SUP_OFF + 4,
+			&new_val, 4);
+		cmdp_local += 1;
+
+		old_val = msm_camera_io_r(vfe32_ctrl->vfebase +
+			V32_CHROMA_SUP_OFF + 8);
+		new_val = *cmdp_local;
+		old_val &= ~MCE_Q_K_MASK;
+		new_val = new_val | old_val;
+		msm_camera_io_memcpy(
+			vfe32_ctrl->vfebase + V32_CHROMA_SUP_OFF + 8,
+			&new_val, 4);
+		}
+		break;
+	case VFE_CMD_BLACK_LEVEL_CFG:
+		rc = -EFAULT;
+		goto proc_general_done;
+
+	case VFE_CMD_MESH_ROLL_OFF_CFG: {
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value) , cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		cmdp_local = cmdp;
+		msm_camera_io_memcpy(
+			vfe32_ctrl->vfebase + vfe32_cmd[cmd->id].offset,
+			cmdp_local, 16);
+		cmdp_local += 4;
+		vfe32_program_dmi_cfg(ROLLOFF_RAM0_BANK0);
+		/* for loop for extrcting init table. */
+		for (i = 0; i < (V32_MESH_ROLL_OFF_INIT_TABLE_SIZE * 2); i++) {
+			msm_camera_io_w(*cmdp_local ,
+			vfe32_ctrl->vfebase + VFE_DMI_DATA_LO);
+			cmdp_local++;
+		}
+		CDBG("done writing init table\n");
+		/* by default, always starts with offset 0. */
+		msm_camera_io_w(V32_MESH_ROLL_OFF_DELTA_TABLE_OFFSET,
+		vfe32_ctrl->vfebase + VFE_DMI_ADDR);
+		/* for loop for extracting delta table. */
+		for (i = 0; i < (V32_MESH_ROLL_OFF_DELTA_TABLE_SIZE * 2); i++) {
+			msm_camera_io_w(*cmdp_local,
+			vfe32_ctrl->vfebase + VFE_DMI_DATA_LO);
+			cmdp_local++;
+		}
+		vfe32_program_dmi_cfg(NO_MEM_SELECTED);
+		}
+		break;
+
+	case VFE_CMD_GET_MESH_ROLLOFF_TABLE:
+		temp1 = sizeof(uint32_t) * ((V32_MESH_ROLL_OFF_INIT_TABLE_SIZE *
+			2) + (V32_MESH_ROLL_OFF_DELTA_TABLE_SIZE * 2));
+		if (cmd->length != temp1) {
+			rc = -EINVAL;
+			goto proc_general_done;
+		}
+		cmdp = kzalloc(temp1, GFP_KERNEL);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		cmdp_local = cmdp;
+		vfe32_program_dmi_cfg(ROLLOFF_RAM0_BANK0);
+		CDBG("%s: Mesh Rolloff init Table\n", __func__);
+		for (i = 0; i < (V32_MESH_ROLL_OFF_INIT_TABLE_SIZE * 2); i++) {
+			*cmdp_local =
+				msm_camera_io_r(
+					vfe32_ctrl->vfebase + VFE_DMI_DATA_LO);
+			CDBG("%s: %08x\n", __func__, *cmdp_local);
+			cmdp_local++;
+		}
+		msm_camera_io_w(V32_MESH_ROLL_OFF_DELTA_TABLE_OFFSET,
+			vfe32_ctrl->vfebase + VFE_DMI_ADDR);
+		CDBG("%s: Mesh Rolloff Delta Table\n", __func__);
+		for (i = 0; i < (V32_MESH_ROLL_OFF_DELTA_TABLE_SIZE * 2); i++) {
+			*cmdp_local =
+				msm_camera_io_r(
+					vfe32_ctrl->vfebase + VFE_DMI_DATA_LO);
+			CDBG("%s: %08x\n", __func__, *cmdp_local);
+			cmdp_local++;
+		}
+		CDBG("done reading delta table\n");
+		vfe32_program_dmi_cfg(NO_MEM_SELECTED);
+		if (copy_to_user((void __user *)(cmd->value), cmdp,
+			temp1)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		break;
+	case VFE_CMD_LA_CFG:
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		cmdp_local = cmdp;
+		msm_camera_io_memcpy(
+			vfe32_ctrl->vfebase + vfe32_cmd[cmd->id].offset,
+			cmdp_local, (vfe32_cmd[cmd->id].length));
+
+		cmdp_local += 1;
+		vfe32_write_la_cfg(LUMA_ADAPT_LUT_RAM_BANK0, cmdp_local);
+		break;
+
+	case VFE_CMD_LA_UPDATE: {
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+
+		cmdp_local = cmdp + 1;
+		old_val = msm_camera_io_r(vfe32_ctrl->vfebase + V32_LA_OFF);
+		if (old_val != 0x0)
+			vfe32_write_la_cfg(LUMA_ADAPT_LUT_RAM_BANK0,
+				cmdp_local);
+		else
+			vfe32_write_la_cfg(LUMA_ADAPT_LUT_RAM_BANK1,
+				cmdp_local);
+		}
+		vfe32_ctrl->update_la = true;
+		break;
+
+	case VFE_CMD_GET_LA_TABLE:
+		temp1 = sizeof(uint32_t) * VFE32_LA_TABLE_LENGTH / 2;
+		if (cmd->length != temp1) {
+			rc = -EINVAL;
+			goto proc_general_done;
+		}
+		cmdp = kzalloc(temp1, GFP_KERNEL);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		cmdp_local = cmdp;
+		if (msm_camera_io_r(vfe32_ctrl->vfebase + V32_LA_OFF))
+			vfe32_program_dmi_cfg(LUMA_ADAPT_LUT_RAM_BANK1);
+		else
+			vfe32_program_dmi_cfg(LUMA_ADAPT_LUT_RAM_BANK0);
+		for (i = 0 ; i < (VFE32_LA_TABLE_LENGTH / 2) ; i++) {
+			*cmdp_local =
+				msm_camera_io_r(
+					vfe32_ctrl->vfebase + VFE_DMI_DATA_LO);
+			*cmdp_local |= (msm_camera_io_r(vfe32_ctrl->vfebase +
+				VFE_DMI_DATA_LO)) << 16;
+			cmdp_local++;
+		}
+		vfe32_program_dmi_cfg(NO_MEM_SELECTED);
+		if (copy_to_user((void __user *)(cmd->value), cmdp,
+			temp1)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		break;
+	case VFE_CMD_SK_ENHAN_CFG:
+	case VFE_CMD_SK_ENHAN_UPDATE:{
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		msm_camera_io_memcpy(vfe32_ctrl->vfebase + V32_SCE_OFF,
+				cmdp, V32_SCE_LEN);
+		}
+		break;
+
+	case VFE_CMD_LIVESHOT:
+		/* Configure primary channel */
+		rc = vfe32_configure_pingpong_buffers(VFE_MSG_V32_CAPTURE,
+						VFE_MSG_OUTPUT_PRIMARY);
+		if (rc < 0) {
+			pr_err("%s error configuring pingpong buffers"
+				   " for primary output", __func__);
+			rc = -EINVAL;
+			goto proc_general_done;
+		}
+		vfe32_start_liveshot(pmctl);
+		break;
+
+	case VFE_CMD_LINEARIZATION_CFG:
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp, (void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		cmdp_local = cmdp;
+		msm_camera_io_memcpy(
+			vfe32_ctrl->vfebase + V32_LINEARIZATION_OFF1,
+			cmdp_local, V32_LINEARIZATION_LEN1);
+		cmdp_local += 4;
+		msm_camera_io_memcpy(
+			vfe32_ctrl->vfebase + V32_LINEARIZATION_OFF2,
+			cmdp_local, V32_LINEARIZATION_LEN2);
+
+		cmdp_local = cmdp + 17;
+		vfe32_write_linear_cfg(BLACK_LUT_RAM_BANK0, cmdp_local);
+		break;
+
+	case VFE_CMD_LINEARIZATION_UPDATE:
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp, (void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		cmdp_local = cmdp;
+		cmdp_local++;
+		msm_camera_io_memcpy(
+			vfe32_ctrl->vfebase + V32_LINEARIZATION_OFF1 + 4,
+			cmdp_local, (V32_LINEARIZATION_LEN1 - 4));
+		cmdp_local += 3;
+		msm_camera_io_memcpy(
+			vfe32_ctrl->vfebase + V32_LINEARIZATION_OFF2,
+			cmdp_local, V32_LINEARIZATION_LEN2);
+		cmdp_local = cmdp + 17;
+		/*extracting the bank select*/
+		old_val = msm_camera_io_r(
+				vfe32_ctrl->vfebase + V32_LINEARIZATION_OFF1);
+
+		if (old_val != 0x0)
+			vfe32_write_linear_cfg(BLACK_LUT_RAM_BANK0, cmdp_local);
+		else
+			vfe32_write_linear_cfg(BLACK_LUT_RAM_BANK1, cmdp_local);
+		vfe32_ctrl->update_linear = true;
+		break;
+
+	case VFE_CMD_GET_LINEARIZATON_TABLE:
+		temp1 = sizeof(uint32_t) * VFE32_LINEARIZATON_TABLE_LENGTH;
+		if (cmd->length != temp1) {
+			rc = -EINVAL;
+			goto proc_general_done;
+		}
+		cmdp = kzalloc(temp1, GFP_KERNEL);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		cmdp_local = cmdp;
+		if (msm_camera_io_r(
+			vfe32_ctrl->vfebase + V32_LINEARIZATION_OFF1))
+			vfe32_program_dmi_cfg(BLACK_LUT_RAM_BANK1);
+		else
+			vfe32_program_dmi_cfg(BLACK_LUT_RAM_BANK0);
+		CDBG("%s: Linearization Table\n", __func__);
+		for (i = 0 ; i < VFE32_LINEARIZATON_TABLE_LENGTH ; i++) {
+			*cmdp_local = msm_camera_io_r(
+					vfe32_ctrl->vfebase + VFE_DMI_DATA_LO);
+			CDBG("%s: %08x\n", __func__, *cmdp_local);
+			cmdp_local++;
+		}
+		vfe32_program_dmi_cfg(NO_MEM_SELECTED);
+		if (copy_to_user((void __user *)(cmd->value), cmdp,
+			temp1)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		break;
+	case VFE_CMD_DEMOSAICV3:
+		if (cmd->length !=
+			V32_DEMOSAICV3_0_LEN+V32_DEMOSAICV3_1_LEN) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		cmdp_local = cmdp;
+		new_val = *cmdp_local;
+
+		old_val = msm_camera_io_r(
+				vfe32_ctrl->vfebase + V32_DEMOSAICV3_0_OFF);
+		old_val &= DEMOSAIC_MASK;
+		new_val = new_val | old_val;
+		*cmdp_local = new_val;
+
+		msm_camera_io_memcpy(vfe32_ctrl->vfebase + V32_DEMOSAICV3_0_OFF,
+			cmdp_local, V32_DEMOSAICV3_0_LEN);
+		cmdp_local += 1;
+		msm_camera_io_memcpy(vfe32_ctrl->vfebase + V32_DEMOSAICV3_1_OFF,
+			cmdp_local, V32_DEMOSAICV3_1_LEN);
+		break;
+
+	case VFE_CMD_DEMOSAICV3_UPDATE:
+		if (cmd->length !=
+			V32_DEMOSAICV3_0_LEN * V32_DEMOSAICV3_UP_REG_CNT) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		cmdp_local = cmdp;
+		new_val = *cmdp_local;
+
+		old_val = msm_camera_io_r(
+				vfe32_ctrl->vfebase + V32_DEMOSAICV3_0_OFF);
+		old_val &= DEMOSAIC_MASK;
+		new_val = new_val | old_val;
+		*cmdp_local = new_val;
+
+		msm_camera_io_memcpy(vfe32_ctrl->vfebase + V32_DEMOSAICV3_0_OFF,
+			cmdp_local, V32_DEMOSAICV3_0_LEN);
+		/* As the address space is not contiguous increment by 2
+		 * before copying to next address space */
+		cmdp_local += 1;
+		msm_camera_io_memcpy(vfe32_ctrl->vfebase + V32_DEMOSAICV3_1_OFF,
+			cmdp_local, 2 * V32_DEMOSAICV3_0_LEN);
+		/* As the address space is not contiguous increment by 2
+		 * before copying to next address space */
+		cmdp_local += 2;
+		msm_camera_io_memcpy(vfe32_ctrl->vfebase + V32_DEMOSAICV3_2_OFF,
+			cmdp_local, 2 * V32_DEMOSAICV3_0_LEN);
+		break;
+
+	case VFE_CMD_DEMOSAICV3_ABCC_CFG:
+		rc = -EFAULT;
+		break;
+
+	case VFE_CMD_DEMOSAICV3_ABF_UPDATE:/* 116 ABF update  */
+	case VFE_CMD_DEMOSAICV3_ABF_CFG: { /* 108 ABF config  */
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		cmdp_local = cmdp;
+		new_val = *cmdp_local;
+
+		old_val = msm_camera_io_r(
+				vfe32_ctrl->vfebase + V32_DEMOSAICV3_0_OFF);
+		old_val &= ABF_MASK;
+		new_val = new_val | old_val;
+		*cmdp_local = new_val;
+
+		msm_camera_io_memcpy(vfe32_ctrl->vfebase + V32_DEMOSAICV3_0_OFF,
+		    cmdp_local, 4);
+
+		cmdp_local += 1;
+		msm_camera_io_memcpy(
+			vfe32_ctrl->vfebase + vfe32_cmd[cmd->id].offset,
+			cmdp_local, (vfe32_cmd[cmd->id].length));
+		}
+		break;
+
+	case VFE_CMD_DEMOSAICV3_DBCC_CFG:
+	case VFE_CMD_DEMOSAICV3_DBCC_UPDATE:
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		cmdp_local = cmdp;
+		new_val = *cmdp_local;
+
+		old_val = msm_camera_io_r(
+				vfe32_ctrl->vfebase + V32_DEMOSAICV3_0_OFF);
+		old_val &= DBCC_MASK;
+
+		new_val = new_val | old_val;
+		*cmdp_local = new_val;
+		msm_camera_io_memcpy(vfe32_ctrl->vfebase + V32_DEMOSAICV3_0_OFF,
+					cmdp_local, 4);
+		cmdp_local += 1;
+		msm_camera_io_memcpy(
+			vfe32_ctrl->vfebase + vfe32_cmd[cmd->id].offset,
+			cmdp_local, (vfe32_cmd[cmd->id].length));
+		break;
+
+	case VFE_CMD_DEMOSAICV3_DBPC_CFG:
+	case VFE_CMD_DEMOSAICV3_DBPC_UPDATE:
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		cmdp_local = cmdp;
+		new_val = *cmdp_local;
+
+		old_val = msm_camera_io_r(
+				vfe32_ctrl->vfebase + V32_DEMOSAICV3_0_OFF);
+		old_val &= DBPC_MASK;
+
+		new_val = new_val | old_val;
+		*cmdp_local = new_val;
+		msm_camera_io_memcpy(vfe32_ctrl->vfebase +
+			V32_DEMOSAICV3_0_OFF,
+			cmdp_local, V32_DEMOSAICV3_LEN);
+		cmdp_local += 1;
+		msm_camera_io_memcpy(vfe32_ctrl->vfebase +
+			V32_DEMOSAICV3_DBPC_CFG_OFF,
+			cmdp_local, V32_DEMOSAICV3_DBPC_LEN);
+		cmdp_local += 1;
+		msm_camera_io_memcpy(vfe32_ctrl->vfebase +
+			V32_DEMOSAICV3_DBPC_CFG_OFF0,
+			cmdp_local, V32_DEMOSAICV3_DBPC_LEN);
+		cmdp_local += 1;
+		msm_camera_io_memcpy(vfe32_ctrl->vfebase +
+			V32_DEMOSAICV3_DBPC_CFG_OFF1,
+			cmdp_local, V32_DEMOSAICV3_DBPC_LEN);
+		cmdp_local += 1;
+		msm_camera_io_memcpy(vfe32_ctrl->vfebase +
+			V32_DEMOSAICV3_DBPC_CFG_OFF2,
+			cmdp_local, V32_DEMOSAICV3_DBPC_LEN);
+		break;
+
+	case VFE_CMD_RGB_G_CFG: {
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		msm_camera_io_memcpy(vfe32_ctrl->vfebase + V32_RGB_G_OFF,
+			cmdp, 4);
+		cmdp += 1;
+
+		vfe32_write_gamma_cfg(RGBLUT_RAM_CH0_BANK0, cmdp);
+		vfe32_write_gamma_cfg(RGBLUT_RAM_CH1_BANK0, cmdp);
+		vfe32_write_gamma_cfg(RGBLUT_RAM_CH2_BANK0, cmdp);
+		}
+	    cmdp -= 1;
+		break;
+
+	case VFE_CMD_RGB_G_UPDATE: {
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp, (void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+
+		old_val = msm_camera_io_r(vfe32_ctrl->vfebase + V32_RGB_G_OFF);
+		cmdp += 1;
+		if (old_val != 0x0) {
+			vfe32_write_gamma_cfg(RGBLUT_RAM_CH0_BANK0, cmdp);
+			vfe32_write_gamma_cfg(RGBLUT_RAM_CH1_BANK0, cmdp);
+			vfe32_write_gamma_cfg(RGBLUT_RAM_CH2_BANK0, cmdp);
+		} else {
+			vfe32_write_gamma_cfg(RGBLUT_RAM_CH0_BANK1, cmdp);
+			vfe32_write_gamma_cfg(RGBLUT_RAM_CH1_BANK1, cmdp);
+			vfe32_write_gamma_cfg(RGBLUT_RAM_CH2_BANK1, cmdp);
+		}
+		}
+		vfe32_ctrl->update_gamma = TRUE;
+		cmdp -= 1;
+		break;
+
+	case VFE_CMD_GET_RGB_G_TABLE:
+		temp1 = sizeof(uint32_t) * VFE32_GAMMA_NUM_ENTRIES * 3;
+		if (cmd->length != temp1) {
+			rc = -EINVAL;
+			goto proc_general_done;
+		}
+		cmdp = kzalloc(temp1, GFP_KERNEL);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		cmdp_local = cmdp;
+
+		old_val = msm_camera_io_r(vfe32_ctrl->vfebase + V32_RGB_G_OFF);
+		temp2 = old_val ? RGBLUT_RAM_CH0_BANK1 :
+			RGBLUT_RAM_CH0_BANK0;
+		for (i = 0; i < 3; i++) {
+			vfe32_read_gamma_cfg(temp2,
+				cmdp_local + (VFE32_GAMMA_NUM_ENTRIES * i));
+			temp2 += 2;
+		}
+		if (copy_to_user((void __user *)(cmd->value), cmdp,
+			temp1)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		break;
+
+	case VFE_CMD_STATS_AWB_STOP: {
+		old_val = msm_camera_io_r(vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val &= ~AWB_ENABLE_MASK;
+		msm_camera_io_w(old_val,
+			vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+		}
+		break;
+	case VFE_CMD_STATS_AE_STOP: {
+		old_val = msm_camera_io_r(vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val &= ~AE_BG_ENABLE_MASK;
+		msm_camera_io_w(old_val,
+			vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+		}
+		break;
+	case VFE_CMD_STATS_AF_STOP: {
+		old_val = msm_camera_io_r(vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val &= ~AF_BF_ENABLE_MASK;
+		msm_camera_io_w(old_val,
+			vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+		}
+		break;
+
+	case VFE_CMD_STATS_IHIST_STOP: {
+		old_val = msm_camera_io_r(vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val &= ~IHIST_ENABLE_MASK;
+		msm_camera_io_w(old_val,
+			vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+		}
+		break;
+
+	case VFE_CMD_STATS_RS_STOP: {
+		old_val = msm_camera_io_r(vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val &= ~RS_ENABLE_MASK;
+		msm_camera_io_w(old_val,
+			vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+		}
+		break;
+
+	case VFE_CMD_STATS_CS_STOP: {
+		old_val = msm_camera_io_r(vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val &= ~CS_ENABLE_MASK;
+		msm_camera_io_w(old_val,
+			vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+		}
+		break;
+	case VFE_CMD_STOP:
+		pr_info("vfe32_proc_general: cmdID = %s\n",
+			vfe32_general_cmd[cmd->id]);
+		vfe32_stop();
+		break;
+
+	case VFE_CMD_SYNC_TIMER_SETTING:
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp, (void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		vfe32_sync_timer_start(cmdp);
+		break;
+
+	case VFE_CMD_MODULE_CFG: {
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		*cmdp &= ~STATS_ENABLE_MASK;
+		old_val = msm_camera_io_r(vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val &= STATS_ENABLE_MASK;
+		*cmdp |= old_val;
+
+		msm_camera_io_memcpy(
+			vfe32_ctrl->vfebase + vfe32_cmd[cmd->id].offset,
+			cmdp, (vfe32_cmd[cmd->id].length));
+		}
+		break;
+
+	case VFE_CMD_ZSL:
+		rc = vfe32_configure_pingpong_buffers(VFE_MSG_V32_START,
+			VFE_MSG_OUTPUT_PRIMARY);
+		if (rc < 0)
+			goto proc_general_done;
+		rc = vfe32_configure_pingpong_buffers(VFE_MSG_V32_START,
+			VFE_MSG_OUTPUT_SECONDARY);
+		if (rc < 0)
+			goto proc_general_done;
+
+		rc = vfe32_zsl(pmctl);
+		break;
+
+	case VFE_CMD_ASF_CFG:
+	case VFE_CMD_ASF_UPDATE:
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp, (void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		msm_camera_io_memcpy(
+			vfe32_ctrl->vfebase + vfe32_cmd[cmd->id].offset,
+			cmdp, (vfe32_cmd[cmd->id].length));
+		cmdp_local = cmdp + V32_ASF_LEN/4;
+		msm_camera_io_memcpy(
+			vfe32_ctrl->vfebase + V32_ASF_SPECIAL_EFX_CFG_OFF,
+			cmdp_local, V32_ASF_SPECIAL_EFX_CFG_LEN);
+		break;
+
+	case VFE_CMD_PCA_ROLL_OFF_CFG:
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value) , cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+
+		cmdp_local = cmdp;
+
+		temp1 = *cmdp_local;
+		cmdp_local++;
+
+		msm_camera_io_memcpy(
+			vfe32_ctrl->vfebase + V33_PCA_ROLL_OFF_CFG_OFF1,
+			cmdp_local, V33_PCA_ROLL_OFF_CFG_LEN1);
+		cmdp_local += 4;
+		msm_camera_io_memcpy(
+			vfe32_ctrl->vfebase + V33_PCA_ROLL_OFF_CFG_OFF2,
+			cmdp_local, V33_PCA_ROLL_OFF_CFG_LEN2);
+
+		cmdp_local += 3;
+		CDBG("%s: start writing RollOff Ram0 table\n", __func__);
+		vfe32_program_dmi_cfg(ROLLOFF_RAM0_BANK0);
+		msm_camera_io_w(temp1, vfe32_ctrl->vfebase + VFE_DMI_ADDR);
+		for (i = 0 ; i < V33_PCA_ROLL_OFF_TABLE_SIZE ; i++) {
+			msm_camera_io_w(*(cmdp_local + 1),
+				vfe32_ctrl->vfebase + VFE33_DMI_DATA_HI);
+			msm_camera_io_w(*cmdp_local,
+				vfe32_ctrl->vfebase + VFE33_DMI_DATA_LO);
+			cmdp_local += 2;
+		}
+		CDBG("%s: end writing RollOff Ram0 table\n", __func__);
+
+		CDBG("%s: start writing RollOff Ram1 table\n", __func__);
+		vfe32_program_dmi_cfg(ROLLOFF_RAM1_BANK0);
+		msm_camera_io_w(temp1, vfe32_ctrl->vfebase + VFE_DMI_ADDR);
+		for (i = 0 ; i < V33_PCA_ROLL_OFF_TABLE_SIZE ; i++) {
+			msm_camera_io_w(*cmdp_local,
+				vfe32_ctrl->vfebase + VFE33_DMI_DATA_LO);
+			cmdp_local += 2;
+		}
+		CDBG("%s: end writing RollOff Ram1 table\n", __func__);
+
+		vfe32_program_dmi_cfg(NO_MEM_SELECTED);
+		break;
+
+	case VFE_CMD_PCA_ROLL_OFF_UPDATE:
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value), cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+
+		cmdp_local = cmdp;
+
+		temp1 = *cmdp_local;
+		cmdp_local += 8;
+
+		temp2 = msm_camera_io_r(vfe32_ctrl->vfebase +
+			V33_PCA_ROLL_OFF_CFG_OFF1)
+			& V33_PCA_ROLL_OFF_LUT_BANK_SEL_MASK;
+
+		CDBG("%s: start writing RollOff Ram0 table\n", __func__);
+		if (temp2)
+			vfe32_program_dmi_cfg(ROLLOFF_RAM0_BANK0);
+		else
+			vfe32_program_dmi_cfg(ROLLOFF_RAM0_BANK1);
+
+		msm_camera_io_w(temp1, vfe32_ctrl->vfebase + VFE_DMI_ADDR);
+		for (i = 0 ; i < V33_PCA_ROLL_OFF_TABLE_SIZE ; i++) {
+			msm_camera_io_w(*(cmdp_local + 1),
+				vfe32_ctrl->vfebase + VFE33_DMI_DATA_HI);
+			msm_camera_io_w(*cmdp_local,
+				vfe32_ctrl->vfebase + VFE33_DMI_DATA_LO);
+			cmdp_local += 2;
+		}
+		CDBG("%s: end writing RollOff Ram0 table\n", __func__);
+
+		CDBG("%s: start writing RollOff Ram1 table\n", __func__);
+		if (temp2)
+			vfe32_program_dmi_cfg(ROLLOFF_RAM1_BANK0);
+		else
+			vfe32_program_dmi_cfg(ROLLOFF_RAM1_BANK1);
+
+		msm_camera_io_w(temp1, vfe32_ctrl->vfebase + VFE_DMI_ADDR);
+		for (i = 0 ; i < V33_PCA_ROLL_OFF_TABLE_SIZE ; i++) {
+			msm_camera_io_w(*cmdp_local,
+				vfe32_ctrl->vfebase + VFE33_DMI_DATA_LO);
+			cmdp_local += 2;
+		}
+		CDBG("%s: end writing RollOff Ram1 table\n", __func__);
+
+		vfe32_program_dmi_cfg(NO_MEM_SELECTED);
+		vfe32_ctrl->update_rolloff = true;
+		break;
+	case VFE_CMD_GET_PCA_ROLLOFF_TABLE:
+		temp1 = sizeof(uint64_t) * V33_PCA_ROLL_OFF_TABLE_SIZE * 2;
+		if (cmd->length != temp1) {
+			rc = -EINVAL;
+			goto proc_general_done;
+		}
+		cmdp = kzalloc(temp1, GFP_KERNEL);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		cmdp_local = cmdp;
+		old_val = msm_camera_io_r(vfe32_ctrl->vfebase +
+			V33_PCA_ROLL_OFF_CFG_OFF1) &
+			V33_PCA_ROLL_OFF_LUT_BANK_SEL_MASK;
+
+		if (old_val)
+			vfe32_program_dmi_cfg(ROLLOFF_RAM0_BANK1);
+		else
+			vfe32_program_dmi_cfg(ROLLOFF_RAM0_BANK0);
+
+		CDBG("%s: PCA Rolloff Ram0\n", __func__);
+		for (i = 0 ; i < V33_PCA_ROLL_OFF_TABLE_SIZE * 2; i++) {
+			temp2 = (i == (V33_PCA_ROLL_OFF_TABLE_SIZE));
+			if (old_val && temp2)
+				vfe32_program_dmi_cfg(ROLLOFF_RAM1_BANK1);
+			else if (!old_val && temp2)
+				vfe32_program_dmi_cfg(ROLLOFF_RAM1_BANK0);
+
+			*cmdp_local = msm_camera_io_r(vfe32_ctrl->vfebase +
+				VFE33_DMI_DATA_LO);
+			*(cmdp_local + 1) =
+				msm_camera_io_r(vfe32_ctrl->vfebase +
+				VFE33_DMI_DATA_HI);
+			CDBG("%s: %08x%08x\n", __func__,
+				*(cmdp_local + 1), *cmdp_local);
+			cmdp_local += 2;
+		}
+		vfe32_program_dmi_cfg(NO_MEM_SELECTED);
+		if (copy_to_user((void __user *)(cmd->value), cmdp,
+			temp1)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		break;
+	case VFE_CMD_GET_HW_VERSION:
+		if (cmd->length != V32_GET_HW_VERSION_LEN) {
+			rc = -EINVAL;
+			goto proc_general_done;
+		}
+		cmdp = kmalloc(V32_GET_HW_VERSION_LEN, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		*cmdp = msm_camera_io_r(
+				vfe32_ctrl->vfebase+V32_GET_HW_VERSION_OFF);
+		if (copy_to_user((void __user *)(cmd->value), cmdp,
+			V32_GET_HW_VERSION_LEN)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		break;
+	case VFE_CMD_GET_REG_DUMP:
+		temp1 = sizeof(uint32_t) * vfe32_ctrl->register_total;
+		if (cmd->length != temp1) {
+			rc = -EINVAL;
+			goto proc_general_done;
+		}
+		cmdp = kmalloc(temp1, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		msm_camera_io_dump(
+			vfe32_ctrl->vfebase, vfe32_ctrl->register_total*4);
+		CDBG("%s: %p %p %d\n", __func__, (void *)cmdp,
+			vfe32_ctrl->vfebase, temp1);
+		memcpy_fromio((void *)cmdp, vfe32_ctrl->vfebase, temp1);
+		if (copy_to_user((void __user *)(cmd->value), cmdp, temp1)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		break;
+	case VFE_CMD_FRAME_SKIP_CFG:
+		if (cmd->length != vfe32_cmd[cmd->id].length)
+			return -EINVAL;
+
+		cmdp = kmalloc(vfe32_cmd[cmd->id].length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+
+		if (copy_from_user((cmdp), (void __user *)cmd->value,
+				cmd->length)) {
+			rc = -EFAULT;
+			pr_err("%s copy from user failed for cmd %d",
+				__func__, cmd->id);
+			break;
+		}
+
+		msm_camera_io_memcpy(
+			vfe32_ctrl->vfebase + vfe32_cmd[cmd->id].offset,
+			cmdp, (vfe32_cmd[cmd->id].length));
+		vfe32_ctrl->frame_skip_cnt = ((uint32_t)
+			*cmdp & VFE_FRAME_SKIP_PERIOD_MASK) + 1;
+		vfe32_ctrl->frame_skip_pattern = (uint32_t)(*(cmdp + 2));
+		break;
+	default:
+		if (cmd->length != vfe32_cmd[cmd->id].length)
+			return -EINVAL;
+
+		cmdp = kmalloc(vfe32_cmd[cmd->id].length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+
+		if (copy_from_user((cmdp), (void __user *)cmd->value,
+				cmd->length)) {
+			rc = -EFAULT;
+			pr_err("%s copy from user failed for cmd %d",
+				__func__, cmd->id);
+			goto proc_general_done;
+		}
+		msm_camera_io_memcpy(
+			vfe32_ctrl->vfebase + vfe32_cmd[cmd->id].offset,
+			cmdp, (vfe32_cmd[cmd->id].length));
+		break;
+
+	}
+
+proc_general_done:
+	kfree(cmdp);
+
+	return rc;
+}
+
+static void vfe32_stats_af_ack(struct vfe_cmd_stats_ack *pAck)
+{
+	unsigned long flags;
+	spinlock_t *lock = (vfe32_ctrl->stats_comp ?
+		&vfe32_ctrl->comp_stats_ack_lock :
+		&vfe32_ctrl->af_ack_lock);
+	spin_lock_irqsave(lock, flags);
+	vfe32_ctrl->afStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+	vfe32_ctrl->afStatsControl.ackPending = FALSE;
+	spin_unlock_irqrestore(lock, flags);
+}
+
+static void vfe32_stats_awb_ack(struct vfe_cmd_stats_ack *pAck)
+{
+	unsigned long flags;
+	spinlock_t *lock = (vfe32_ctrl->stats_comp ?
+		&vfe32_ctrl->comp_stats_ack_lock :
+		&vfe32_ctrl->awb_ack_lock);
+	spin_lock_irqsave(lock, flags);
+	vfe32_ctrl->awbStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+	vfe32_ctrl->awbStatsControl.ackPending = FALSE;
+	spin_unlock_irqrestore(lock, flags);
+}
+
+static void vfe32_stats_aec_ack(struct vfe_cmd_stats_ack *pAck)
+{
+	unsigned long flags;
+	spinlock_t *lock = (vfe32_ctrl->stats_comp ?
+		&vfe32_ctrl->comp_stats_ack_lock :
+		&vfe32_ctrl->aec_ack_lock);
+	spin_lock_irqsave(lock, flags);
+	vfe32_ctrl->aecStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+	vfe32_ctrl->aecStatsControl.ackPending = FALSE;
+	spin_unlock_irqrestore(lock, flags);
+}
+
+static void vfe32_stats_ihist_ack(struct vfe_cmd_stats_ack *pAck)
+{
+	unsigned long flags;
+	spinlock_t *lock = (vfe32_ctrl->stats_comp ?
+		&vfe32_ctrl->comp_stats_ack_lock :
+		&vfe32_ctrl->ihist_ack_lock);
+	spin_lock_irqsave(lock, flags);
+	vfe32_ctrl->ihistStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+	vfe32_ctrl->ihistStatsControl.ackPending = FALSE;
+	spin_unlock_irqrestore(lock, flags);
+}
+static void vfe32_stats_rs_ack(struct vfe_cmd_stats_ack *pAck)
+{
+	unsigned long flags;
+	spinlock_t *lock = (vfe32_ctrl->stats_comp ?
+		&vfe32_ctrl->comp_stats_ack_lock :
+		&vfe32_ctrl->rs_ack_lock);
+	spin_lock_irqsave(lock, flags);
+	vfe32_ctrl->rsStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+	vfe32_ctrl->rsStatsControl.ackPending = FALSE;
+	spin_unlock_irqrestore(lock, flags);
+}
+static void vfe32_stats_cs_ack(struct vfe_cmd_stats_ack *pAck)
+{
+	unsigned long flags;
+	spinlock_t *lock = (vfe32_ctrl->stats_comp ?
+		&vfe32_ctrl->comp_stats_ack_lock :
+		&vfe32_ctrl->cs_ack_lock);
+	spin_lock_irqsave(lock, flags);
+	vfe32_ctrl->csStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+	vfe32_ctrl->csStatsControl.ackPending = FALSE;
+	spin_unlock_irqrestore(lock, flags);
+}
+
+static inline void vfe32_read_irq_status(struct vfe32_irq_status *out)
+{
+	uint32_t *temp;
+	memset(out, 0, sizeof(struct vfe32_irq_status));
+	temp = (uint32_t *)(vfe32_ctrl->vfebase + VFE_IRQ_STATUS_0);
+	out->vfeIrqStatus0 = msm_camera_io_r(temp);
+
+	temp = (uint32_t *)(vfe32_ctrl->vfebase + VFE_IRQ_STATUS_1);
+	out->vfeIrqStatus1 = msm_camera_io_r(temp);
+
+	temp = (uint32_t *)(vfe32_ctrl->vfebase + VFE_CAMIF_STATUS);
+	out->camifStatus = msm_camera_io_r(temp);
+	CDBG("camifStatus  = 0x%x\n", out->camifStatus);
+
+	/* clear the pending interrupt of the same kind.*/
+	msm_camera_io_w(out->vfeIrqStatus0,
+		vfe32_ctrl->vfebase + VFE_IRQ_CLEAR_0);
+	msm_camera_io_w(out->vfeIrqStatus1,
+		vfe32_ctrl->vfebase + VFE_IRQ_CLEAR_1);
+
+	/* Ensure the write order while writing
+	to the command register using the barrier */
+	msm_camera_io_w_mb(1, vfe32_ctrl->vfebase + VFE_IRQ_CMD);
+
+}
+
+static void vfe32_process_reg_update_irq(void)
+{
+	unsigned long flags;
+
+	if (vfe32_ctrl->recording_state == VFE_STATE_START_REQUESTED) {
+		if (vfe32_ctrl->operation_mode ==
+				VFE_OUTPUTS_VIDEO_AND_PREVIEW) {
+			msm_camera_io_w(1, vfe32_ctrl->vfebase +
+			vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch0]);
+			msm_camera_io_w(1, vfe32_ctrl->vfebase +
+			vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch1]);
+		} else if (vfe32_ctrl->operation_mode ==
+				VFE_OUTPUTS_PREVIEW_AND_VIDEO) {
+			msm_camera_io_w(1, vfe32_ctrl->vfebase +
+			vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out1.ch0]);
+			msm_camera_io_w(1, vfe32_ctrl->vfebase +
+			vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out1.ch1]);
+		}
+		vfe32_ctrl->recording_state = VFE_STATE_STARTED;
+		msm_camera_io_w_mb(1, vfe32_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+		CDBG("start video triggered .\n");
+	} else if (vfe32_ctrl->recording_state ==
+			VFE_STATE_STOP_REQUESTED) {
+		if (vfe32_ctrl->operation_mode ==
+				VFE_OUTPUTS_VIDEO_AND_PREVIEW) {
+			msm_camera_io_w(0, vfe32_ctrl->vfebase +
+			vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch0]);
+			msm_camera_io_w(0, vfe32_ctrl->vfebase +
+			vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch1]);
+		} else if (vfe32_ctrl->operation_mode ==
+				VFE_OUTPUTS_PREVIEW_AND_VIDEO) {
+			msm_camera_io_w(0, vfe32_ctrl->vfebase +
+			vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out1.ch0]);
+			msm_camera_io_w(0, vfe32_ctrl->vfebase +
+			vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out1.ch1]);
+		}
+		CDBG("stop video triggered .\n");
+	}
+
+	if (vfe32_ctrl->start_ack_pending == TRUE) {
+		vfe32_send_isp_msg(vfe32_ctrl, MSG_ID_START_ACK);
+		vfe32_ctrl->start_ack_pending = FALSE;
+	} else {
+		if (vfe32_ctrl->recording_state ==
+				VFE_STATE_STOP_REQUESTED) {
+			vfe32_ctrl->recording_state = VFE_STATE_STOPPED;
+			/* request a reg update and send STOP_REC_ACK
+			 * when we process the next reg update irq.
+			 */
+			msm_camera_io_w_mb(1,
+			vfe32_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+		} else if (vfe32_ctrl->recording_state ==
+					VFE_STATE_STOPPED) {
+			vfe32_send_isp_msg(vfe32_ctrl, MSG_ID_STOP_REC_ACK);
+			vfe32_ctrl->recording_state = VFE_STATE_IDLE;
+		}
+		spin_lock_irqsave(&vfe32_ctrl->update_ack_lock, flags);
+		if (vfe32_ctrl->update_ack_pending == TRUE) {
+			vfe32_ctrl->update_ack_pending = FALSE;
+			spin_unlock_irqrestore(
+				&vfe32_ctrl->update_ack_lock, flags);
+			vfe32_send_isp_msg(vfe32_ctrl, MSG_ID_UPDATE_ACK);
+		} else {
+			spin_unlock_irqrestore(
+				&vfe32_ctrl->update_ack_lock, flags);
+		}
+	}
+
+	if (vfe32_ctrl->liveshot_state == VFE_STATE_START_REQUESTED) {
+		pr_info("%s enabling liveshot output\n", __func__);
+		if (vfe32_ctrl->outpath.output_mode &
+				VFE32_OUTPUT_MODE_PRIMARY) {
+			msm_camera_io_w(1, vfe32_ctrl->vfebase +
+			vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch0]);
+			msm_camera_io_w(1, vfe32_ctrl->vfebase +
+			vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch1]);
+			vfe32_ctrl->liveshot_state = VFE_STATE_STARTED;
+		}
+	}
+
+	if (vfe32_ctrl->liveshot_state == VFE_STATE_STARTED) {
+		vfe32_ctrl->vfe_capture_count--;
+		if (!vfe32_ctrl->vfe_capture_count)
+			vfe32_ctrl->liveshot_state = VFE_STATE_STOP_REQUESTED;
+		msm_camera_io_w_mb(1, vfe32_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+	} else if (vfe32_ctrl->liveshot_state == VFE_STATE_STOP_REQUESTED) {
+		CDBG("%s: disabling liveshot output\n", __func__);
+		if (vfe32_ctrl->outpath.output_mode &
+			VFE32_OUTPUT_MODE_PRIMARY) {
+			msm_camera_io_w(0, vfe32_ctrl->vfebase +
+				vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch0]);
+			msm_camera_io_w(0, vfe32_ctrl->vfebase +
+				vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch1]);
+			vfe32_ctrl->liveshot_state = VFE_STATE_STOPPED;
+			msm_camera_io_w_mb(1, vfe32_ctrl->vfebase +
+				VFE_REG_UPDATE_CMD);
+		}
+	} else if (vfe32_ctrl->liveshot_state == VFE_STATE_STOPPED) {
+		vfe32_ctrl->liveshot_state = VFE_STATE_IDLE;
+	}
+
+	if ((vfe32_ctrl->operation_mode == VFE_OUTPUTS_THUMB_AND_MAIN) ||
+		(vfe32_ctrl->operation_mode == VFE_OUTPUTS_MAIN_AND_THUMB) ||
+		(vfe32_ctrl->operation_mode == VFE_OUTPUTS_THUMB_AND_JPEG) ||
+		(vfe32_ctrl->operation_mode == VFE_OUTPUTS_JPEG_AND_THUMB)) {
+		/* in snapshot mode */
+		/* later we need to add check for live snapshot mode. */
+		if (vfe32_ctrl->frame_skip_pattern & (0x1 <<
+			(vfe32_ctrl->snapshot_frame_cnt %
+				vfe32_ctrl->frame_skip_cnt))) {
+			vfe32_ctrl->vfe_capture_count--;
+			/* if last frame to be captured: */
+			if (vfe32_ctrl->vfe_capture_count == 0) {
+				/* stop the bus output:write master enable = 0*/
+				if (vfe32_ctrl->outpath.output_mode &
+						VFE32_OUTPUT_MODE_PRIMARY) {
+					msm_camera_io_w(0, vfe32_ctrl->vfebase +
+						vfe32_AXI_WM_CFG[vfe32_ctrl->
+							outpath.out0.ch0]);
+					msm_camera_io_w(0, vfe32_ctrl->vfebase +
+						vfe32_AXI_WM_CFG[vfe32_ctrl->
+							outpath.out0.ch1]);
+				}
+				if (vfe32_ctrl->outpath.output_mode &
+						VFE32_OUTPUT_MODE_SECONDARY) {
+					msm_camera_io_w(0, vfe32_ctrl->vfebase +
+						vfe32_AXI_WM_CFG[vfe32_ctrl->
+							outpath.out1.ch0]);
+					msm_camera_io_w(0, vfe32_ctrl->vfebase +
+						vfe32_AXI_WM_CFG[vfe32_ctrl->
+							outpath.out1.ch1]);
+				}
+				msm_camera_io_w_mb
+				(CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY,
+				vfe32_ctrl->vfebase + VFE_CAMIF_COMMAND);
+				vfe32_ctrl->snapshot_frame_cnt = -1;
+				vfe32_ctrl->frame_skip_cnt = 31;
+				vfe32_ctrl->frame_skip_pattern = 0xffffffff;
+			} /*if snapshot count is 0*/
+		} /*if frame is not being dropped*/
+		vfe32_ctrl->snapshot_frame_cnt++;
+		/* then do reg_update. */
+		msm_camera_io_w(1, vfe32_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+	} /* if snapshot mode. */
+}
+
+static void vfe32_set_default_reg_values(void)
+{
+	msm_camera_io_w(0x800080, vfe32_ctrl->vfebase + VFE_DEMUX_GAIN_0);
+	msm_camera_io_w(0x800080, vfe32_ctrl->vfebase + VFE_DEMUX_GAIN_1);
+	/* What value should we program CGC_OVERRIDE to? */
+	msm_camera_io_w(0xFFFFF, vfe32_ctrl->vfebase + VFE_CGC_OVERRIDE);
+
+	/* default frame drop period and pattern */
+	msm_camera_io_w(0x1f, vfe32_ctrl->vfebase + VFE_FRAMEDROP_ENC_Y_CFG);
+	msm_camera_io_w(0x1f, vfe32_ctrl->vfebase + VFE_FRAMEDROP_ENC_CBCR_CFG);
+	msm_camera_io_w(0xFFFFFFFF,
+		vfe32_ctrl->vfebase + VFE_FRAMEDROP_ENC_Y_PATTERN);
+	msm_camera_io_w(0xFFFFFFFF,
+		vfe32_ctrl->vfebase + VFE_FRAMEDROP_ENC_CBCR_PATTERN);
+	msm_camera_io_w(0x1f, vfe32_ctrl->vfebase + VFE_FRAMEDROP_VIEW_Y);
+	msm_camera_io_w(0x1f, vfe32_ctrl->vfebase + VFE_FRAMEDROP_VIEW_CBCR);
+	msm_camera_io_w(0xFFFFFFFF,
+		vfe32_ctrl->vfebase + VFE_FRAMEDROP_VIEW_Y_PATTERN);
+	msm_camera_io_w(0xFFFFFFFF,
+		vfe32_ctrl->vfebase + VFE_FRAMEDROP_VIEW_CBCR_PATTERN);
+	msm_camera_io_w(0, vfe32_ctrl->vfebase + VFE_CLAMP_MIN);
+	msm_camera_io_w(0xFFFFFF, vfe32_ctrl->vfebase + VFE_CLAMP_MAX);
+
+	/* stats UB config */
+	msm_camera_io_w(0x3980007,
+		vfe32_ctrl->vfebase + VFE_BUS_STATS_AEC_UB_CFG);
+	msm_camera_io_w(0x3A00007,
+		vfe32_ctrl->vfebase + VFE_BUS_STATS_AF_UB_CFG);
+	msm_camera_io_w(0x3A8000F,
+		vfe32_ctrl->vfebase + VFE_BUS_STATS_AWB_UB_CFG);
+	msm_camera_io_w(0x3B80007,
+		vfe32_ctrl->vfebase + VFE_BUS_STATS_RS_UB_CFG);
+	msm_camera_io_w(0x3C0001F,
+		vfe32_ctrl->vfebase + VFE_BUS_STATS_CS_UB_CFG);
+	msm_camera_io_w(0x3E0001F,
+		vfe32_ctrl->vfebase + VFE_BUS_STATS_HIST_UB_CFG);
+}
+
+static void vfe32_process_reset_irq(void)
+{
+	unsigned long flags;
+
+	atomic_set(&vfe32_ctrl->vstate, 0);
+
+	spin_lock_irqsave(&vfe32_ctrl->stop_flag_lock, flags);
+	if (vfe32_ctrl->stop_ack_pending) {
+		vfe32_ctrl->stop_ack_pending = FALSE;
+		spin_unlock_irqrestore(&vfe32_ctrl->stop_flag_lock, flags);
+		vfe32_send_isp_msg(vfe32_ctrl, MSG_ID_STOP_ACK);
+	} else {
+		spin_unlock_irqrestore(&vfe32_ctrl->stop_flag_lock, flags);
+		/* this is from reset command. */
+		vfe32_set_default_reg_values();
+
+		/* reload all write masters. (frame & line)*/
+		msm_camera_io_w(0x7FFF, vfe32_ctrl->vfebase + VFE_BUS_CMD);
+		vfe32_send_isp_msg(vfe32_ctrl, MSG_ID_RESET_ACK);
+	}
+}
+
+static void vfe32_process_camif_sof_irq(void)
+{
+	if (vfe32_ctrl->operation_mode ==
+		VFE_OUTPUTS_RAW) {
+		if (vfe32_ctrl->start_ack_pending) {
+			vfe32_send_isp_msg(vfe32_ctrl, MSG_ID_START_ACK);
+			vfe32_ctrl->start_ack_pending = FALSE;
+		}
+		vfe32_ctrl->vfe_capture_count--;
+		/* if last frame to be captured: */
+		if (vfe32_ctrl->vfe_capture_count == 0) {
+			/* Ensure the write order while writing
+			 to the command register using the barrier */
+			msm_camera_io_w_mb(CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY,
+				vfe32_ctrl->vfebase + VFE_CAMIF_COMMAND);
+		}
+	} /* if raw snapshot mode. */
+	if ((vfe32_ctrl->hfr_mode != HFR_MODE_OFF) &&
+		(vfe32_ctrl->operation_mode == VFE_MODE_OF_OPERATION_VIDEO) &&
+		(vfe32_ctrl->vfeFrameId % vfe32_ctrl->hfr_mode != 0)) {
+		vfe32_ctrl->vfeFrameId++;
+		CDBG("Skip the SOF notification when HFR enabled\n");
+		return;
+	}
+	vfe32_ctrl->vfeFrameId++;
+	vfe32_send_isp_msg(vfe32_ctrl, MSG_ID_SOF_ACK);
+	CDBG("camif_sof_irq, frameId = %d\n", vfe32_ctrl->vfeFrameId);
+
+	if (vfe32_ctrl->sync_timer_state) {
+		if (vfe32_ctrl->sync_timer_repeat_count == 0)
+			vfe32_sync_timer_stop();
+		else
+			vfe32_ctrl->sync_timer_repeat_count--;
+	}
+}
+
+static void vfe32_process_error_irq(uint32_t errStatus)
+{
+	uint32_t reg_value;
+
+	if (errStatus & VFE32_IMASK_CAMIF_ERROR) {
+		pr_err("vfe32_irq: camif errors\n");
+		reg_value = msm_camera_io_r(
+				vfe32_ctrl->vfebase + VFE_CAMIF_STATUS);
+		pr_err("camifStatus  = 0x%x\n", reg_value);
+		vfe32_send_isp_msg(vfe32_ctrl, MSG_ID_CAMIF_ERROR);
+	}
+
+	if (errStatus & VFE32_IMASK_BHIST_OVWR)
+		pr_err("vfe32_irq: stats bhist overwrite\n");
+
+	if (errStatus & VFE32_IMASK_STATS_CS_OVWR)
+		pr_err("vfe32_irq: stats cs overwrite\n");
+
+	if (errStatus & VFE32_IMASK_STATS_IHIST_OVWR)
+		pr_err("vfe32_irq: stats ihist overwrite\n");
+
+	if (errStatus & VFE32_IMASK_REALIGN_BUF_Y_OVFL)
+		pr_err("vfe32_irq: realign bug Y overflow\n");
+
+	if (errStatus & VFE32_IMASK_REALIGN_BUF_CB_OVFL)
+		pr_err("vfe32_irq: realign bug CB overflow\n");
+
+	if (errStatus & VFE32_IMASK_REALIGN_BUF_CR_OVFL)
+		pr_err("vfe32_irq: realign bug CR overflow\n");
+
+	if (errStatus & VFE32_IMASK_VIOLATION) {
+		pr_err("vfe32_irq: violation interrupt\n");
+		reg_value = msm_camera_io_r(
+				vfe32_ctrl->vfebase + VFE_VIOLATION_STATUS);
+		pr_err("%s: violationStatus  = 0x%x\n", __func__, reg_value);
+	}
+
+	if (errStatus & VFE32_IMASK_IMG_MAST_0_BUS_OVFL)
+		pr_err("vfe32_irq: image master 0 bus overflow\n");
+
+	if (errStatus & VFE32_IMASK_IMG_MAST_1_BUS_OVFL)
+		pr_err("vfe32_irq: image master 1 bus overflow\n");
+
+	if (errStatus & VFE32_IMASK_IMG_MAST_2_BUS_OVFL)
+		pr_err("vfe32_irq: image master 2 bus overflow\n");
+
+	if (errStatus & VFE32_IMASK_IMG_MAST_3_BUS_OVFL)
+		pr_err("vfe32_irq: image master 3 bus overflow\n");
+
+	if (errStatus & VFE32_IMASK_IMG_MAST_4_BUS_OVFL)
+		pr_err("vfe32_irq: image master 4 bus overflow\n");
+
+	if (errStatus & VFE32_IMASK_IMG_MAST_5_BUS_OVFL)
+		pr_err("vfe32_irq: image master 5 bus overflow\n");
+
+	if (errStatus & VFE32_IMASK_IMG_MAST_6_BUS_OVFL)
+		pr_err("vfe32_irq: image master 6 bus overflow\n");
+
+	if (errStatus & VFE32_IMASK_STATS_AE_BG_BUS_OVFL)
+		pr_err("vfe32_irq: ae/bg stats bus overflow\n");
+
+	if (errStatus & VFE32_IMASK_STATS_AF_BF_BUS_OVFL)
+		pr_err("vfe32_irq: af/bf stats bus overflow\n");
+
+	if (errStatus & VFE32_IMASK_STATS_AWB_BUS_OVFL)
+		pr_err("vfe32_irq: awb stats bus overflow\n");
+
+	if (errStatus & VFE32_IMASK_STATS_RS_BUS_OVFL)
+		pr_err("vfe32_irq: rs stats bus overflow\n");
+
+	if (errStatus & VFE32_IMASK_STATS_CS_BUS_OVFL)
+		pr_err("vfe32_irq: cs stats bus overflow\n");
+
+	if (errStatus & VFE32_IMASK_STATS_IHIST_BUS_OVFL)
+		pr_err("vfe32_irq: ihist stats bus overflow\n");
+
+	if (errStatus & VFE32_IMASK_STATS_SKIN_BHIST_BUS_OVFL)
+		pr_err("vfe32_irq: skin/bhist stats bus overflow\n");
+
+	if (errStatus & VFE32_IMASK_AXI_ERROR)
+		pr_err("vfe32_irq: axi error\n");
+}
+
+static void vfe_send_outmsg(struct v4l2_subdev *sd, uint8_t msgid,
+	uint32_t ch0_paddr, uint32_t ch1_paddr, uint32_t ch2_paddr)
+{
+	struct isp_msg_output msg;
+
+	msg.output_id = msgid;
+	msg.buf.ch_paddr[0]	= ch0_paddr;
+	msg.buf.ch_paddr[1]	= ch1_paddr;
+	msg.buf.ch_paddr[2]	= ch2_paddr;
+	msg.frameCounter = vfe32_ctrl->vfeFrameId;
+
+	v4l2_subdev_notify(&vfe32_ctrl->subdev,
+			NOTIFY_VFE_MSG_OUT,
+			&msg);
+	return;
+}
+
+static void vfe32_process_output_path_irq_0(void)
+{
+	uint32_t ping_pong;
+	uint32_t ch0_paddr, ch1_paddr, ch2_paddr;
+	uint8_t out_bool = 0;
+	struct msm_free_buf *free_buf = NULL;
+
+	free_buf = vfe32_check_free_buffer(VFE_MSG_OUTPUT_IRQ,
+		VFE_MSG_OUTPUT_PRIMARY);
+
+	/* we render frames in the following conditions:
+	1. Continuous mode and the free buffer is avaialable.
+	2. In snapshot shot mode, free buffer is not always available.
+	when pending snapshot count is <=1,  then no need to use
+	free buffer.
+	*/
+	out_bool = ((vfe32_ctrl->operation_mode == VFE_OUTPUTS_THUMB_AND_MAIN ||
+		vfe32_ctrl->operation_mode == VFE_OUTPUTS_MAIN_AND_THUMB ||
+		vfe32_ctrl->operation_mode == VFE_OUTPUTS_THUMB_AND_JPEG ||
+		vfe32_ctrl->operation_mode == VFE_OUTPUTS_JPEG_AND_THUMB ||
+		vfe32_ctrl->operation_mode == VFE_OUTPUTS_RAW ||
+		vfe32_ctrl->liveshot_state == VFE_STATE_STARTED ||
+		vfe32_ctrl->liveshot_state == VFE_STATE_STOP_REQUESTED ||
+		vfe32_ctrl->liveshot_state == VFE_STATE_STOPPED) &&
+		(vfe32_ctrl->vfe_capture_count <= 1)) || free_buf;
+
+	if (out_bool) {
+		ping_pong = msm_camera_io_r(vfe32_ctrl->vfebase +
+			VFE_BUS_PING_PONG_STATUS);
+
+		/* Channel 0*/
+		ch0_paddr = vfe32_get_ch_addr(ping_pong,
+			vfe32_ctrl->outpath.out0.ch0);
+		/* Channel 1*/
+		ch1_paddr = vfe32_get_ch_addr(ping_pong,
+			vfe32_ctrl->outpath.out0.ch1);
+		/* Channel 2*/
+		ch2_paddr = vfe32_get_ch_addr(ping_pong,
+			vfe32_ctrl->outpath.out0.ch2);
+
+		CDBG("output path 0, ch0 = 0x%x, ch1 = 0x%x, ch2 = 0x%x\n",
+			ch0_paddr, ch1_paddr, ch2_paddr);
+		if (free_buf) {
+			/* Y channel */
+			vfe32_put_ch_addr(ping_pong,
+			vfe32_ctrl->outpath.out0.ch0,
+			free_buf->ch_paddr[0]);
+			/* Chroma channel */
+			vfe32_put_ch_addr(ping_pong,
+			vfe32_ctrl->outpath.out0.ch1,
+			free_buf->ch_paddr[1]);
+			if (free_buf->num_planes > 2)
+				vfe32_put_ch_addr(ping_pong,
+					vfe32_ctrl->outpath.out0.ch2,
+					free_buf->ch_paddr[2]);
+		}
+		if (vfe32_ctrl->operation_mode ==
+				VFE_OUTPUTS_THUMB_AND_MAIN ||
+			vfe32_ctrl->operation_mode ==
+				VFE_OUTPUTS_MAIN_AND_THUMB ||
+			vfe32_ctrl->operation_mode ==
+				VFE_OUTPUTS_THUMB_AND_JPEG ||
+			vfe32_ctrl->operation_mode ==
+				VFE_OUTPUTS_JPEG_AND_THUMB ||
+			vfe32_ctrl->operation_mode ==
+				VFE_OUTPUTS_RAW ||
+			vfe32_ctrl->liveshot_state == VFE_STATE_STOPPED)
+			vfe32_ctrl->outpath.out0.capture_cnt--;
+
+		vfe_send_outmsg(&vfe32_ctrl->subdev,
+			MSG_ID_OUTPUT_PRIMARY, ch0_paddr,
+			ch1_paddr, ch2_paddr);
+
+		if (vfe32_ctrl->liveshot_state == VFE_STATE_STOPPED)
+			vfe32_ctrl->liveshot_state = VFE_STATE_IDLE;
+
+	} else {
+		vfe32_ctrl->outpath.out0.frame_drop_cnt++;
+		CDBG("path_irq_0 - no free buffer!\n");
+	}
+}
+
+static void vfe32_process_output_path_irq_1(void)
+{
+	uint32_t ping_pong;
+	uint32_t ch0_paddr, ch1_paddr, ch2_paddr;
+	/* this must be snapshot main image output. */
+	uint8_t out_bool = 0;
+	struct msm_free_buf *free_buf = NULL;
+
+	free_buf = vfe32_check_free_buffer(VFE_MSG_OUTPUT_IRQ,
+		VFE_MSG_OUTPUT_SECONDARY);
+	out_bool = ((vfe32_ctrl->operation_mode ==
+				VFE_OUTPUTS_THUMB_AND_MAIN ||
+			vfe32_ctrl->operation_mode ==
+				VFE_OUTPUTS_MAIN_AND_THUMB ||
+			vfe32_ctrl->operation_mode ==
+				VFE_OUTPUTS_RAW ||
+			vfe32_ctrl->operation_mode ==
+				VFE_OUTPUTS_JPEG_AND_THUMB) &&
+			(vfe32_ctrl->vfe_capture_count <= 1)) || free_buf;
+
+	if (out_bool) {
+		ping_pong = msm_camera_io_r(vfe32_ctrl->vfebase +
+			VFE_BUS_PING_PONG_STATUS);
+
+		/* Y channel */
+		ch0_paddr = vfe32_get_ch_addr(ping_pong,
+			vfe32_ctrl->outpath.out1.ch0);
+		/* Chroma channel */
+		ch1_paddr = vfe32_get_ch_addr(ping_pong,
+			vfe32_ctrl->outpath.out1.ch1);
+		ch2_paddr = vfe32_get_ch_addr(ping_pong,
+			vfe32_ctrl->outpath.out1.ch2);
+
+		CDBG("%s ch0 = 0x%x, ch1 = 0x%x, ch2 = 0x%x\n",
+			__func__, ch0_paddr, ch1_paddr, ch2_paddr);
+		if (free_buf) {
+			/* Y channel */
+			vfe32_put_ch_addr(ping_pong,
+			vfe32_ctrl->outpath.out1.ch0,
+			free_buf->ch_paddr[0]);
+			/* Chroma channel */
+			vfe32_put_ch_addr(ping_pong,
+			vfe32_ctrl->outpath.out1.ch1,
+			free_buf->ch_paddr[1]);
+			if (free_buf->num_planes > 2)
+				vfe32_put_ch_addr(ping_pong,
+					vfe32_ctrl->outpath.out1.ch2,
+					free_buf->ch_paddr[2]);
+		}
+		if (vfe32_ctrl->operation_mode ==
+				VFE_OUTPUTS_THUMB_AND_MAIN ||
+			vfe32_ctrl->operation_mode ==
+				VFE_OUTPUTS_MAIN_AND_THUMB ||
+			vfe32_ctrl->operation_mode ==
+				VFE_OUTPUTS_RAW ||
+			vfe32_ctrl->operation_mode ==
+				VFE_OUTPUTS_JPEG_AND_THUMB)
+			vfe32_ctrl->outpath.out1.capture_cnt--;
+
+		vfe_send_outmsg(&vfe32_ctrl->subdev,
+			MSG_ID_OUTPUT_SECONDARY, ch0_paddr,
+			ch1_paddr, ch2_paddr);
+	} else {
+		vfe32_ctrl->outpath.out1.frame_drop_cnt++;
+		CDBG("path_irq_1 - no free buffer!\n");
+	}
+}
+
+static uint32_t  vfe32_process_stats_irq_common(uint32_t statsNum,
+						uint32_t newAddr) {
+
+	uint32_t pingpongStatus;
+	uint32_t returnAddr;
+	uint32_t pingpongAddr;
+
+	/* must be 0=ping, 1=pong */
+	pingpongStatus =
+		((msm_camera_io_r(vfe32_ctrl->vfebase +
+		VFE_BUS_PING_PONG_STATUS))
+	& ((uint32_t)(1<<(statsNum + 7)))) >> (statsNum + 7);
+	/* stats bits starts at 7 */
+	CDBG("statsNum %d, pingpongStatus %d\n", statsNum, pingpongStatus);
+	pingpongAddr =
+		((uint32_t)(vfe32_ctrl->vfebase +
+				VFE_BUS_STATS_PING_PONG_BASE)) +
+				(3*statsNum)*4 + (1-pingpongStatus)*4;
+	returnAddr = msm_camera_io_r((uint32_t *)pingpongAddr);
+	msm_camera_io_w(newAddr, (uint32_t *)pingpongAddr);
+	return returnAddr;
+}
+
+static void
+vfe_send_stats_msg(uint32_t bufAddress, uint32_t statsNum)
+{
+	unsigned long flags;
+	/* fill message with right content. */
+	/* @todo This is causing issues, need further investigate */
+	/* spin_lock_irqsave(&ctrl->state_lock, flags); */
+	struct isp_msg_stats msgStats;
+	msgStats.frameCounter = vfe32_ctrl->vfeFrameId;
+	msgStats.buffer = bufAddress;
+
+	switch (statsNum) {
+	case statsAeNum:{
+		msgStats.id = MSG_ID_STATS_AEC;
+		spin_lock_irqsave(&vfe32_ctrl->aec_ack_lock, flags);
+		vfe32_ctrl->aecStatsControl.ackPending = TRUE;
+		spin_unlock_irqrestore(&vfe32_ctrl->aec_ack_lock, flags);
+		}
+		break;
+	case statsAfNum:{
+		msgStats.id = MSG_ID_STATS_AF;
+		spin_lock_irqsave(&vfe32_ctrl->af_ack_lock, flags);
+		vfe32_ctrl->afStatsControl.ackPending = TRUE;
+		spin_unlock_irqrestore(&vfe32_ctrl->af_ack_lock, flags);
+		}
+		break;
+	case statsAwbNum: {
+		msgStats.id = MSG_ID_STATS_AWB;
+		spin_lock_irqsave(&vfe32_ctrl->awb_ack_lock, flags);
+		vfe32_ctrl->awbStatsControl.ackPending = TRUE;
+		spin_unlock_irqrestore(&vfe32_ctrl->awb_ack_lock, flags);
+		}
+		break;
+
+	case statsIhistNum: {
+		msgStats.id = MSG_ID_STATS_IHIST;
+		spin_lock_irqsave(&vfe32_ctrl->ihist_ack_lock, flags);
+		vfe32_ctrl->ihistStatsControl.ackPending = TRUE;
+		spin_unlock_irqrestore(&vfe32_ctrl->ihist_ack_lock, flags);
+		}
+		break;
+	case statsRsNum: {
+		msgStats.id = MSG_ID_STATS_RS;
+		spin_lock_irqsave(&vfe32_ctrl->rs_ack_lock, flags);
+		vfe32_ctrl->rsStatsControl.ackPending = TRUE;
+		spin_unlock_irqrestore(&vfe32_ctrl->rs_ack_lock, flags);
+		}
+		break;
+	case statsCsNum: {
+		msgStats.id = MSG_ID_STATS_CS;
+		spin_lock_irqsave(&vfe32_ctrl->cs_ack_lock, flags);
+		vfe32_ctrl->csStatsControl.ackPending = TRUE;
+		spin_unlock_irqrestore(&vfe32_ctrl->cs_ack_lock, flags);
+		}
+		break;
+
+	default:
+		goto stats_done;
+	}
+
+	v4l2_subdev_notify(&vfe32_ctrl->subdev,
+				NOTIFY_VFE_MSG_STATS,
+				&msgStats);
+stats_done:
+	/* spin_unlock_irqrestore(&ctrl->state_lock, flags); */
+	return;
+}
+
+static void vfe_send_comp_stats_msg(uint32_t status_bits)
+{
+	struct msm_stats_buf msgStats;
+	uint32_t temp;
+
+	msgStats.frame_id = vfe32_ctrl->vfeFrameId;
+	msgStats.status_bits = status_bits;
+
+	msgStats.aec.buff = vfe32_ctrl->aecStatsControl.bufToRender;
+	msgStats.awb.buff = vfe32_ctrl->awbStatsControl.bufToRender;
+	msgStats.af.buff = vfe32_ctrl->afStatsControl.bufToRender;
+
+	msgStats.ihist.buff = vfe32_ctrl->ihistStatsControl.bufToRender;
+	msgStats.rs.buff = vfe32_ctrl->rsStatsControl.bufToRender;
+	msgStats.cs.buff = vfe32_ctrl->csStatsControl.bufToRender;
+
+	temp = msm_camera_io_r(vfe32_ctrl->vfebase + VFE_STATS_AWB_SGW_CFG);
+	msgStats.awb_ymin = (0xFF00 & temp) >> 8;
+
+	v4l2_subdev_notify(&vfe32_ctrl->subdev,
+				NOTIFY_VFE_MSG_COMP_STATS,
+				&msgStats);
+}
+
+static void vfe32_process_stats_ae_irq(void)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&vfe32_ctrl->aec_ack_lock, flags);
+	if (!(vfe32_ctrl->aecStatsControl.ackPending)) {
+		spin_unlock_irqrestore(&vfe32_ctrl->aec_ack_lock, flags);
+		vfe32_ctrl->aecStatsControl.bufToRender =
+			vfe32_process_stats_irq_common(statsAeNum,
+			vfe32_ctrl->aecStatsControl.nextFrameAddrBuf);
+
+		vfe_send_stats_msg(vfe32_ctrl->aecStatsControl.bufToRender,
+						statsAeNum);
+	} else{
+		spin_unlock_irqrestore(&vfe32_ctrl->aec_ack_lock, flags);
+		vfe32_ctrl->aecStatsControl.droppedStatsFrameCount++;
+		CDBG("%s: droppedStatsFrameCount = %d", __func__,
+			vfe32_ctrl->aecStatsControl.droppedStatsFrameCount);
+	}
+}
+
+static void vfe32_process_stats_awb_irq(void)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&vfe32_ctrl->awb_ack_lock, flags);
+	if (!(vfe32_ctrl->awbStatsControl.ackPending)) {
+		spin_unlock_irqrestore(&vfe32_ctrl->awb_ack_lock, flags);
+		vfe32_ctrl->awbStatsControl.bufToRender =
+			vfe32_process_stats_irq_common(statsAwbNum,
+			vfe32_ctrl->awbStatsControl.nextFrameAddrBuf);
+
+		vfe_send_stats_msg(vfe32_ctrl->awbStatsControl.bufToRender,
+						statsAwbNum);
+	} else{
+		spin_unlock_irqrestore(&vfe32_ctrl->awb_ack_lock, flags);
+		vfe32_ctrl->awbStatsControl.droppedStatsFrameCount++;
+		CDBG("%s: droppedStatsFrameCount = %d", __func__,
+			vfe32_ctrl->awbStatsControl.droppedStatsFrameCount);
+	}
+}
+
+static void vfe32_process_stats_af_irq(void)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&vfe32_ctrl->af_ack_lock, flags);
+	if (!(vfe32_ctrl->afStatsControl.ackPending)) {
+		spin_unlock_irqrestore(&vfe32_ctrl->af_ack_lock, flags);
+		vfe32_ctrl->afStatsControl.bufToRender =
+			vfe32_process_stats_irq_common(statsAfNum,
+			vfe32_ctrl->afStatsControl.nextFrameAddrBuf);
+
+		vfe_send_stats_msg(vfe32_ctrl->afStatsControl.bufToRender,
+						statsAfNum);
+	} else{
+		spin_unlock_irqrestore(&vfe32_ctrl->af_ack_lock, flags);
+		vfe32_ctrl->afStatsControl.droppedStatsFrameCount++;
+		CDBG("%s: droppedStatsFrameCount = %d", __func__,
+			vfe32_ctrl->afStatsControl.droppedStatsFrameCount);
+	}
+}
+
+static void vfe32_process_stats_ihist_irq(void)
+{
+	if (!(vfe32_ctrl->ihistStatsControl.ackPending)) {
+		vfe32_ctrl->ihistStatsControl.bufToRender =
+			vfe32_process_stats_irq_common(statsIhistNum,
+			vfe32_ctrl->ihistStatsControl.nextFrameAddrBuf);
+
+		vfe_send_stats_msg(vfe32_ctrl->ihistStatsControl.bufToRender,
+						statsIhistNum);
+	} else {
+		vfe32_ctrl->ihistStatsControl.droppedStatsFrameCount++;
+		CDBG("%s: droppedStatsFrameCount = %d", __func__,
+			vfe32_ctrl->ihistStatsControl.droppedStatsFrameCount);
+	}
+}
+
+static void vfe32_process_stats_rs_irq(void)
+{
+	if (!(vfe32_ctrl->rsStatsControl.ackPending)) {
+		vfe32_ctrl->rsStatsControl.bufToRender =
+			vfe32_process_stats_irq_common(statsRsNum,
+			vfe32_ctrl->rsStatsControl.nextFrameAddrBuf);
+
+		vfe_send_stats_msg(vfe32_ctrl->rsStatsControl.bufToRender,
+						statsRsNum);
+	} else {
+		vfe32_ctrl->rsStatsControl.droppedStatsFrameCount++;
+		CDBG("%s: droppedStatsFrameCount = %d", __func__,
+			vfe32_ctrl->rsStatsControl.droppedStatsFrameCount);
+	}
+}
+
+static void vfe32_process_stats_cs_irq(void)
+{
+	if (!(vfe32_ctrl->csStatsControl.ackPending)) {
+		vfe32_ctrl->csStatsControl.bufToRender =
+			vfe32_process_stats_irq_common(statsCsNum,
+			vfe32_ctrl->csStatsControl.nextFrameAddrBuf);
+
+		vfe_send_stats_msg(vfe32_ctrl->csStatsControl.bufToRender,
+						statsCsNum);
+	} else {
+		vfe32_ctrl->csStatsControl.droppedStatsFrameCount++;
+		CDBG("%s: droppedStatsFrameCount = %d", __func__,
+			vfe32_ctrl->csStatsControl.droppedStatsFrameCount);
+	}
+}
+
+static void vfe32_process_stats(uint32_t status_bits)
+{
+	unsigned long flags;
+	int32_t process_stats = false;
+	CDBG("%s, stats = 0x%x\n", __func__, status_bits);
+
+	spin_lock_irqsave(&vfe32_ctrl->comp_stats_ack_lock, flags);
+	if (status_bits & VFE_IRQ_STATUS0_STATS_AEC) {
+		if (!vfe32_ctrl->aecStatsControl.ackPending) {
+			vfe32_ctrl->aecStatsControl.ackPending = TRUE;
+			vfe32_ctrl->aecStatsControl.bufToRender =
+				vfe32_process_stats_irq_common(statsAeNum,
+				vfe32_ctrl->aecStatsControl.nextFrameAddrBuf);
+			process_stats = true;
+		} else{
+			vfe32_ctrl->aecStatsControl.bufToRender = 0;
+			vfe32_ctrl->aecStatsControl.droppedStatsFrameCount++;
+		}
+	} else {
+		vfe32_ctrl->aecStatsControl.bufToRender = 0;
+	}
+
+	if (status_bits & VFE_IRQ_STATUS0_STATS_AWB) {
+		if (!vfe32_ctrl->awbStatsControl.ackPending) {
+			vfe32_ctrl->awbStatsControl.ackPending = TRUE;
+			vfe32_ctrl->awbStatsControl.bufToRender =
+				vfe32_process_stats_irq_common(statsAwbNum,
+				vfe32_ctrl->awbStatsControl.nextFrameAddrBuf);
+			process_stats = true;
+		} else{
+			vfe32_ctrl->awbStatsControl.droppedStatsFrameCount++;
+			vfe32_ctrl->awbStatsControl.bufToRender = 0;
+		}
+	} else {
+		vfe32_ctrl->awbStatsControl.bufToRender = 0;
+	}
+
+
+	if (status_bits & VFE_IRQ_STATUS0_STATS_AF) {
+		if (!vfe32_ctrl->afStatsControl.ackPending) {
+			vfe32_ctrl->afStatsControl.ackPending = TRUE;
+			vfe32_ctrl->afStatsControl.bufToRender =
+				vfe32_process_stats_irq_common(statsAfNum,
+				vfe32_ctrl->afStatsControl.nextFrameAddrBuf);
+			process_stats = true;
+		} else {
+			vfe32_ctrl->afStatsControl.bufToRender = 0;
+			vfe32_ctrl->afStatsControl.droppedStatsFrameCount++;
+		}
+	} else {
+		vfe32_ctrl->afStatsControl.bufToRender = 0;
+	}
+
+	if (status_bits & VFE_IRQ_STATUS0_STATS_IHIST) {
+		if (!vfe32_ctrl->ihistStatsControl.ackPending) {
+			vfe32_ctrl->ihistStatsControl.ackPending = TRUE;
+			vfe32_ctrl->ihistStatsControl.bufToRender =
+				vfe32_process_stats_irq_common(statsIhistNum,
+				vfe32_ctrl->ihistStatsControl.nextFrameAddrBuf);
+			process_stats = true;
+		} else {
+			vfe32_ctrl->ihistStatsControl.droppedStatsFrameCount++;
+			vfe32_ctrl->ihistStatsControl.bufToRender = 0;
+		}
+	} else {
+		vfe32_ctrl->ihistStatsControl.bufToRender = 0;
+	}
+
+	if (status_bits & VFE_IRQ_STATUS0_STATS_RS) {
+		if (!vfe32_ctrl->rsStatsControl.ackPending) {
+			vfe32_ctrl->rsStatsControl.ackPending = TRUE;
+			vfe32_ctrl->rsStatsControl.bufToRender =
+				vfe32_process_stats_irq_common(statsRsNum,
+				vfe32_ctrl->rsStatsControl.nextFrameAddrBuf);
+			process_stats = true;
+		} else {
+			vfe32_ctrl->rsStatsControl.droppedStatsFrameCount++;
+			vfe32_ctrl->rsStatsControl.bufToRender = 0;
+		}
+	} else {
+		vfe32_ctrl->rsStatsControl.bufToRender = 0;
+	}
+
+
+	if (status_bits & VFE_IRQ_STATUS0_STATS_CS) {
+		if (!vfe32_ctrl->csStatsControl.ackPending) {
+			vfe32_ctrl->csStatsControl.ackPending = TRUE;
+			vfe32_ctrl->csStatsControl.bufToRender =
+				vfe32_process_stats_irq_common(statsCsNum,
+				vfe32_ctrl->csStatsControl.nextFrameAddrBuf);
+			process_stats = true;
+		} else {
+			vfe32_ctrl->csStatsControl.droppedStatsFrameCount++;
+			vfe32_ctrl->csStatsControl.bufToRender = 0;
+		}
+	} else {
+		vfe32_ctrl->csStatsControl.bufToRender = 0;
+	}
+
+	spin_unlock_irqrestore(&vfe32_ctrl->comp_stats_ack_lock, flags);
+	if (process_stats)
+		vfe_send_comp_stats_msg(status_bits);
+
+	return;
+}
+
+static void vfe32_process_stats_irq(uint32_t irqstatus)
+{
+	uint32_t status_bits = VFE_COM_STATUS & irqstatus;
+
+	if ((vfe32_ctrl->hfr_mode != HFR_MODE_OFF) &&
+		(vfe32_ctrl->vfeFrameId % vfe32_ctrl->hfr_mode != 0)) {
+		CDBG("Skip the stats when HFR enabled\n");
+		return;
+	}
+
+	vfe32_process_stats(status_bits);
+	return;
+}
+
+static void vfe32_process_irq(uint32_t irqstatus)
+{
+	if (irqstatus &
+		VFE_IRQ_STATUS0_STATS_COMPOSIT_MASK) {
+		vfe32_process_stats_irq(irqstatus);
+		return;
+	}
+
+	switch (irqstatus) {
+	case VFE_IRQ_STATUS0_CAMIF_SOF_MASK:
+		CDBG("irq	camifSofIrq\n");
+		vfe32_process_camif_sof_irq();
+		break;
+	case VFE_IRQ_STATUS0_REG_UPDATE_MASK:
+		CDBG("irq	regUpdateIrq\n");
+		vfe32_process_reg_update_irq();
+		break;
+	case VFE_IMASK_WHILE_STOPPING_1:
+		CDBG("irq	resetAckIrq\n");
+		vfe32_process_reset_irq();
+		break;
+	case VFE_IRQ_STATUS0_STATS_AEC:
+		CDBG("Stats AEC irq occured.\n");
+		vfe32_process_stats_ae_irq();
+		break;
+	case VFE_IRQ_STATUS0_STATS_AWB:
+		CDBG("Stats AWB irq occured.\n");
+		vfe32_process_stats_awb_irq();
+		break;
+	case VFE_IRQ_STATUS0_STATS_AF:
+		CDBG("Stats AF irq occured.\n");
+		vfe32_process_stats_af_irq();
+		break;
+	case VFE_IRQ_STATUS0_STATS_IHIST:
+		CDBG("Stats IHIST irq occured.\n");
+		vfe32_process_stats_ihist_irq();
+		break;
+	case VFE_IRQ_STATUS0_STATS_RS:
+		CDBG("Stats RS irq occured.\n");
+		vfe32_process_stats_rs_irq();
+		break;
+	case VFE_IRQ_STATUS0_STATS_CS:
+		CDBG("Stats CS irq occured.\n");
+		vfe32_process_stats_cs_irq();
+		break;
+	case VFE_IRQ_STATUS0_SYNC_TIMER0:
+		CDBG("SYNC_TIMER 0 irq occured.\n");
+		vfe32_send_isp_msg(vfe32_ctrl,
+			MSG_ID_SYNC_TIMER0_DONE);
+		break;
+	case VFE_IRQ_STATUS0_SYNC_TIMER1:
+		CDBG("SYNC_TIMER 1 irq occured.\n");
+		vfe32_send_isp_msg(vfe32_ctrl,
+			MSG_ID_SYNC_TIMER1_DONE);
+		break;
+	case VFE_IRQ_STATUS0_SYNC_TIMER2:
+		CDBG("SYNC_TIMER 2 irq occured.\n");
+		vfe32_send_isp_msg(vfe32_ctrl,
+			MSG_ID_SYNC_TIMER2_DONE);
+		break;
+	default:
+		pr_err("Invalid IRQ status\n");
+	}
+}
+
+static void axi32_do_tasklet(unsigned long data)
+{
+	unsigned long flags;
+	struct axi_ctrl_t *axi_ctrl = (struct axi_ctrl_t *)data;
+	struct vfe32_isr_queue_cmd *qcmd = NULL;
+
+	CDBG("=== axi32_do_tasklet start ===\n");
+
+	while (atomic_read(&irq_cnt)) {
+		spin_lock_irqsave(&axi_ctrl->tasklet_lock, flags);
+		qcmd = list_first_entry(&axi_ctrl->tasklet_q,
+			struct vfe32_isr_queue_cmd, list);
+		atomic_sub(1, &irq_cnt);
+
+		if (!qcmd) {
+			spin_unlock_irqrestore(&axi_ctrl->tasklet_lock,
+				flags);
+			return;
+		}
+
+		list_del(&qcmd->list);
+		spin_unlock_irqrestore(&axi_ctrl->tasklet_lock,
+			flags);
+
+		if (qcmd->vfeInterruptStatus0 &
+				VFE_IRQ_STATUS0_CAMIF_SOF_MASK)
+			v4l2_subdev_notify(&axi_ctrl->subdev,
+				NOTIFY_VFE_IRQ,
+				(void *)VFE_IRQ_STATUS0_CAMIF_SOF_MASK);
+
+		/* interrupt to be processed,  *qcmd has the payload.  */
+		if (qcmd->vfeInterruptStatus0 &
+				VFE_IRQ_STATUS0_REG_UPDATE_MASK)
+			v4l2_subdev_notify(&axi_ctrl->subdev,
+				NOTIFY_VFE_IRQ,
+				(void *)VFE_IRQ_STATUS0_REG_UPDATE_MASK);
+
+		if (qcmd->vfeInterruptStatus1 &
+				VFE_IMASK_WHILE_STOPPING_1)
+			v4l2_subdev_notify(&axi_ctrl->subdev,
+				NOTIFY_VFE_IRQ,
+				(void *)VFE_IMASK_WHILE_STOPPING_1);
+
+		if (atomic_read(&vfe32_ctrl->vstate)) {
+			if (qcmd->vfeInterruptStatus1 &
+					VFE32_IMASK_ERROR_ONLY_1) {
+				pr_err("irq	errorIrq\n");
+				vfe32_process_error_irq(
+					qcmd->vfeInterruptStatus1 &
+					VFE32_IMASK_ERROR_ONLY_1);
+			}
+			v4l2_subdev_notify(&axi_ctrl->subdev,
+				NOTIFY_AXI_IRQ,
+				(void *)qcmd->vfeInterruptStatus0);
+
+			/* then process stats irq. */
+			if (vfe32_ctrl->stats_comp) {
+				/* process stats comb interrupt. */
+				if (qcmd->vfeInterruptStatus0 &
+					VFE_IRQ_STATUS0_STATS_COMPOSIT_MASK) {
+					CDBG("Stats composite irq occured.\n");
+					v4l2_subdev_notify(&axi_ctrl->subdev,
+					NOTIFY_VFE_IRQ,
+					(void *)qcmd->vfeInterruptStatus0);
+				}
+			} else {
+				/* process individual stats interrupt. */
+				if (qcmd->vfeInterruptStatus0 &
+						VFE_IRQ_STATUS0_STATS_AEC)
+					v4l2_subdev_notify(&axi_ctrl->subdev,
+					NOTIFY_VFE_IRQ,
+					(void *)VFE_IRQ_STATUS0_STATS_AEC);
+
+				if (qcmd->vfeInterruptStatus0 &
+						VFE_IRQ_STATUS0_STATS_AWB)
+					v4l2_subdev_notify(&axi_ctrl->subdev,
+					NOTIFY_VFE_IRQ,
+					(void *)VFE_IRQ_STATUS0_STATS_AWB);
+
+				if (qcmd->vfeInterruptStatus0 &
+						VFE_IRQ_STATUS0_STATS_AF)
+					v4l2_subdev_notify(&axi_ctrl->subdev,
+					NOTIFY_VFE_IRQ,
+					(void *)VFE_IRQ_STATUS0_STATS_AF);
+
+				if (qcmd->vfeInterruptStatus0 &
+						VFE_IRQ_STATUS0_STATS_IHIST)
+					v4l2_subdev_notify(&axi_ctrl->subdev,
+					NOTIFY_VFE_IRQ,
+					(void *)VFE_IRQ_STATUS0_STATS_IHIST);
+
+				if (qcmd->vfeInterruptStatus0 &
+						VFE_IRQ_STATUS0_STATS_RS)
+					v4l2_subdev_notify(&axi_ctrl->subdev,
+					NOTIFY_VFE_IRQ,
+					(void *)VFE_IRQ_STATUS0_STATS_RS);
+
+				if (qcmd->vfeInterruptStatus0 &
+						VFE_IRQ_STATUS0_STATS_CS)
+					v4l2_subdev_notify(&axi_ctrl->subdev,
+					NOTIFY_VFE_IRQ,
+					(void *)VFE_IRQ_STATUS0_STATS_CS);
+
+				if (qcmd->vfeInterruptStatus0 &
+						VFE_IRQ_STATUS0_SYNC_TIMER0)
+					v4l2_subdev_notify(&axi_ctrl->subdev,
+					NOTIFY_VFE_IRQ,
+					(void *)VFE_IRQ_STATUS0_SYNC_TIMER0);
+
+				if (qcmd->vfeInterruptStatus0 &
+						VFE_IRQ_STATUS0_SYNC_TIMER1)
+					v4l2_subdev_notify(&axi_ctrl->subdev,
+					NOTIFY_VFE_IRQ,
+					(void *)VFE_IRQ_STATUS0_SYNC_TIMER1);
+
+				if (qcmd->vfeInterruptStatus0 &
+						VFE_IRQ_STATUS0_SYNC_TIMER2)
+					v4l2_subdev_notify(&axi_ctrl->subdev,
+					NOTIFY_VFE_IRQ,
+					(void *)VFE_IRQ_STATUS0_SYNC_TIMER2);
+			}
+		}
+		kfree(qcmd);
+	}
+	CDBG("=== axi32_do_tasklet end ===\n");
+}
+
+static irqreturn_t vfe32_parse_irq(int irq_num, void *data)
+{
+	unsigned long flags;
+	struct vfe32_irq_status irq;
+	struct vfe32_isr_queue_cmd *qcmd;
+	struct axi_ctrl_t *axi_ctrl = data;
+
+	CDBG("vfe_parse_irq\n");
+
+	vfe32_read_irq_status(&irq);
+
+	if ((irq.vfeIrqStatus0 == 0) && (irq.vfeIrqStatus1 == 0)) {
+		CDBG("vfe_parse_irq: vfeIrqStatus0 & 1 are both 0!\n");
+		return IRQ_HANDLED;
+	}
+
+	qcmd = kzalloc(sizeof(struct vfe32_isr_queue_cmd),
+		GFP_ATOMIC);
+	if (!qcmd) {
+		pr_err("vfe_parse_irq: qcmd malloc failed!\n");
+		return IRQ_HANDLED;
+	}
+
+	spin_lock_irqsave(&vfe32_ctrl->stop_flag_lock, flags);
+	if (vfe32_ctrl->stop_ack_pending) {
+		irq.vfeIrqStatus0 &= VFE_IMASK_WHILE_STOPPING_0;
+		irq.vfeIrqStatus1 &= VFE_IMASK_WHILE_STOPPING_1;
+	}
+	spin_unlock_irqrestore(&vfe32_ctrl->stop_flag_lock, flags);
+
+	CDBG("vfe_parse_irq: Irq_status0 = 0x%x, Irq_status1 = 0x%x.\n",
+		irq.vfeIrqStatus0, irq.vfeIrqStatus1);
+
+	qcmd->vfeInterruptStatus0 = irq.vfeIrqStatus0;
+	qcmd->vfeInterruptStatus1 = irq.vfeIrqStatus1;
+
+	spin_lock_irqsave(&axi_ctrl->tasklet_lock, flags);
+	list_add_tail(&qcmd->list, &axi_ctrl->tasklet_q);
+
+	atomic_add(1, &irq_cnt);
+	spin_unlock_irqrestore(&axi_ctrl->tasklet_lock, flags);
+	tasklet_schedule(&axi_ctrl->vfe32_tasklet);
+	return IRQ_HANDLED;
+}
+
+static long msm_vfe_subdev_ioctl(struct v4l2_subdev *sd,
+			unsigned int subdev_cmd, void *arg)
+{
+	struct msm_cam_media_controller *pmctl =
+		(struct msm_cam_media_controller *)v4l2_get_subdev_hostdata(sd);
+	struct msm_isp_cmd vfecmd;
+	struct msm_camvfe_params *vfe_params =
+		(struct msm_camvfe_params *)arg;
+	struct msm_vfe_cfg_cmd *cmd = vfe_params->vfe_cfg;
+	void *data = vfe_params->data;
+
+	long rc = 0;
+	uint32_t i = 0;
+	struct vfe_cmd_stats_buf *scfg = NULL;
+	struct msm_pmem_region   *regptr = NULL;
+	struct vfe_cmd_stats_ack *sack = NULL;
+	if (cmd->cmd_type == CMD_VFE_PROCESS_IRQ) {
+		vfe32_process_irq((uint32_t) data);
+		return rc;
+	} else if (cmd->cmd_type != CMD_CONFIG_PING_ADDR &&
+		cmd->cmd_type != CMD_CONFIG_PONG_ADDR &&
+		cmd->cmd_type != CMD_CONFIG_FREE_BUF_ADDR &&
+		cmd->cmd_type != CMD_STATS_AEC_BUF_RELEASE &&
+		cmd->cmd_type != CMD_STATS_AWB_BUF_RELEASE &&
+		cmd->cmd_type != CMD_STATS_IHIST_BUF_RELEASE &&
+		cmd->cmd_type != CMD_STATS_RS_BUF_RELEASE &&
+		cmd->cmd_type != CMD_STATS_CS_BUF_RELEASE &&
+		cmd->cmd_type != CMD_STATS_AF_BUF_RELEASE) {
+		if (copy_from_user(&vfecmd,
+				(void __user *)(cmd->value),
+				sizeof(vfecmd))) {
+			pr_err("%s %d: copy_from_user failed\n", __func__,
+				__LINE__);
+			return -EFAULT;
+		}
+	} else {
+	/* here eith stats release or frame release. */
+		if (cmd->cmd_type != CMD_CONFIG_PING_ADDR &&
+			cmd->cmd_type != CMD_CONFIG_PONG_ADDR &&
+			cmd->cmd_type != CMD_CONFIG_FREE_BUF_ADDR) {
+			/* then must be stats release. */
+			if (!data)
+				return -EFAULT;
+			sack = kmalloc(sizeof(struct vfe_cmd_stats_ack),
+							GFP_ATOMIC);
+			if (!sack)
+				return -ENOMEM;
+
+			sack->nextStatsBuf = *(uint32_t *)data;
+		}
+	}
+
+	CDBG("%s: cmdType = %d\n", __func__, cmd->cmd_type);
+
+	if ((cmd->cmd_type == CMD_STATS_AF_ENABLE)    ||
+		(cmd->cmd_type == CMD_STATS_AWB_ENABLE)   ||
+		(cmd->cmd_type == CMD_STATS_IHIST_ENABLE) ||
+		(cmd->cmd_type == CMD_STATS_RS_ENABLE)    ||
+		(cmd->cmd_type == CMD_STATS_CS_ENABLE)    ||
+		(cmd->cmd_type == CMD_STATS_AEC_ENABLE)) {
+		struct axidata *axid;
+		axid = data;
+		if (!axid) {
+			rc = -EFAULT;
+			goto vfe32_config_done;
+		}
+
+		scfg =
+			kmalloc(sizeof(struct vfe_cmd_stats_buf),
+				GFP_ATOMIC);
+		if (!scfg) {
+			rc = -ENOMEM;
+			goto vfe32_config_done;
+		}
+		regptr = axid->region;
+		if (axid->bufnum1 > 0) {
+			for (i = 0; i < axid->bufnum1; i++) {
+				scfg->statsBuf[i] =
+					(uint32_t)(regptr->paddr);
+				regptr++;
+			}
+		}
+		/* individual */
+		switch (cmd->cmd_type) {
+		case CMD_STATS_AEC_ENABLE:
+			rc = vfe_stats_aec_buf_init(scfg);
+			break;
+		case CMD_STATS_AF_ENABLE:
+			rc = vfe_stats_af_buf_init(scfg);
+			break;
+		case CMD_STATS_AWB_ENABLE:
+			rc = vfe_stats_awb_buf_init(scfg);
+			break;
+		case CMD_STATS_IHIST_ENABLE:
+			rc = vfe_stats_ihist_buf_init(scfg);
+			break;
+		case CMD_STATS_RS_ENABLE:
+			rc = vfe_stats_rs_buf_init(scfg);
+			break;
+		case CMD_STATS_CS_ENABLE:
+			rc = vfe_stats_cs_buf_init(scfg);
+			break;
+		default:
+			pr_err("%s Unsupported cmd type %d",
+				__func__, cmd->cmd_type);
+			break;
+		}
+		goto vfe32_config_done;
+	}
+	switch (cmd->cmd_type) {
+	case CMD_GENERAL:
+		rc = vfe32_proc_general(pmctl, &vfecmd);
+		break;
+
+	case CMD_CONFIG_PING_ADDR: {
+		int path = *((int *)cmd->value);
+		struct vfe32_output_ch *outch = vfe32_get_ch(path);
+		outch->ping = *((struct msm_free_buf *)data);
+	}
+		break;
+
+	case CMD_CONFIG_PONG_ADDR: {
+		int path = *((int *)cmd->value);
+		struct vfe32_output_ch *outch = vfe32_get_ch(path);
+		outch->pong = *((struct msm_free_buf *)data);
+	}
+		break;
+
+	case CMD_CONFIG_FREE_BUF_ADDR: {
+		int path = *((int *)cmd->value);
+		struct vfe32_output_ch *outch = vfe32_get_ch(path);
+		outch->free_buf = *((struct msm_free_buf *)data);
+	}
+		break;
+
+	case CMD_SNAP_BUF_RELEASE:
+		break;
+	case CMD_STATS_AEC_BUF_RELEASE:
+		vfe32_stats_aec_ack(sack);
+		break;
+	case CMD_STATS_AF_BUF_RELEASE:
+		vfe32_stats_af_ack(sack);
+		break;
+	case CMD_STATS_AWB_BUF_RELEASE:
+		vfe32_stats_awb_ack(sack);
+		break;
+
+	case CMD_STATS_IHIST_BUF_RELEASE:
+		vfe32_stats_ihist_ack(sack);
+		break;
+	case CMD_STATS_RS_BUF_RELEASE:
+		vfe32_stats_rs_ack(sack);
+		break;
+	case CMD_STATS_CS_BUF_RELEASE:
+		vfe32_stats_cs_ack(sack);
+		break;
+	default:
+		pr_err("%s Unsupported AXI configuration %x ", __func__,
+			cmd->cmd_type);
+		break;
+	}
+vfe32_config_done:
+	kfree(scfg);
+	kfree(sack);
+	CDBG("%s done: rc = %d\n", __func__, (int) rc);
+	return rc;
+}
+
+static struct msm_cam_clk_info vfe32_clk_info[] = {
+	{"vfe_clk", 228570000},
+	{"vfe_pclk", -1},
+	{"csi_vfe_clk", -1},
+};
+
+static int msm_axi_subdev_s_crystal_freq(struct v4l2_subdev *sd,
+						u32 freq, u32 flags)
+{
+	int rc = 0;
+	int round_rate;
+	struct axi_ctrl_t *axi_ctrl = v4l2_get_subdevdata(sd);
+
+	round_rate = clk_round_rate(axi_ctrl->vfe_clk[0], freq);
+	if (rc < 0) {
+		pr_err("%s: clk_round_rate failed %d\n",
+					__func__, rc);
+		return rc;
+	}
+
+	vfe_clk_rate = round_rate;
+	rc = clk_set_rate(axi_ctrl->vfe_clk[0], round_rate);
+	if (rc < 0)
+		pr_err("%s: clk_set_rate failed %d\n",
+					__func__, rc);
+
+	return rc;
+}
+
+static const struct v4l2_subdev_core_ops msm_vfe_subdev_core_ops = {
+	.ioctl = msm_vfe_subdev_ioctl,
+};
+
+static const struct v4l2_subdev_ops msm_vfe_subdev_ops = {
+	.core = &msm_vfe_subdev_core_ops,
+};
+
+int msm_axi_subdev_init(struct v4l2_subdev *sd,
+			struct msm_cam_media_controller *mctl)
+{
+	int rc = 0;
+	struct axi_ctrl_t *axi_ctrl = v4l2_get_subdevdata(sd);
+	v4l2_set_subdev_hostdata(sd, mctl);
+	spin_lock_init(&axi_ctrl->tasklet_lock);
+	INIT_LIST_HEAD(&axi_ctrl->tasklet_q);
+
+	axi_ctrl->vfebase = ioremap(axi_ctrl->vfemem->start,
+		resource_size(axi_ctrl->vfemem));
+	if (!axi_ctrl->vfebase) {
+		rc = -ENOMEM;
+		pr_err("%s: vfe ioremap failed\n", __func__);
+		goto remap_failed;
+	}
+
+	vfe32_ctrl->vfebase = axi_ctrl->vfebase;
+
+	if (axi_ctrl->fs_vfe == NULL) {
+		axi_ctrl->fs_vfe =
+			regulator_get(&axi_ctrl->pdev->dev, "fs_vfe");
+		if (IS_ERR(axi_ctrl->fs_vfe)) {
+			pr_err("%s: Regulator FS_VFE get failed %ld\n",
+				__func__, PTR_ERR(axi_ctrl->fs_vfe));
+			axi_ctrl->fs_vfe = NULL;
+			goto fs_failed;
+		} else if (regulator_enable(axi_ctrl->fs_vfe)) {
+			pr_err("%s: Regulator FS_VFE enable failed\n",
+							__func__);
+			regulator_put(axi_ctrl->fs_vfe);
+			axi_ctrl->fs_vfe = NULL;
+			goto fs_failed;
+		}
+	}
+
+	rc = msm_cam_clk_enable(&axi_ctrl->pdev->dev, vfe32_clk_info,
+			axi_ctrl->vfe_clk, ARRAY_SIZE(vfe32_clk_info), 1);
+	if (rc < 0)
+		goto clk_enable_failed;
+
+	msm_camio_bus_scale_cfg(
+		mctl->sdata->pdata->cam_bus_scale_table, S_INIT);
+	msm_camio_bus_scale_cfg(
+		mctl->sdata->pdata->cam_bus_scale_table, S_PREVIEW);
+
+	if (msm_camera_io_r(vfe32_ctrl->vfebase + V32_GET_HW_VERSION_OFF) ==
+		VFE32_HW_NUMBER)
+		vfe32_ctrl->register_total = VFE32_REGISTER_TOTAL;
+	else
+		vfe32_ctrl->register_total = VFE33_REGISTER_TOTAL;
+
+	enable_irq(axi_ctrl->vfeirq->start);
+
+	return rc;
+clk_enable_failed:
+	regulator_disable(axi_ctrl->fs_vfe);
+	regulator_put(axi_ctrl->fs_vfe);
+	axi_ctrl->fs_vfe = NULL;
+fs_failed:
+	iounmap(axi_ctrl->vfebase);
+	axi_ctrl->vfebase = NULL;
+remap_failed:
+	disable_irq(axi_ctrl->vfeirq->start);
+	return rc;
+}
+
+int msm_vfe_subdev_init(struct v4l2_subdev *sd,
+			struct msm_cam_media_controller *mctl)
+{
+	int rc = 0;
+	v4l2_set_subdev_hostdata(sd, mctl);
+
+	spin_lock_init(&vfe32_ctrl->stop_flag_lock);
+	spin_lock_init(&vfe32_ctrl->state_lock);
+	spin_lock_init(&vfe32_ctrl->io_lock);
+	spin_lock_init(&vfe32_ctrl->update_ack_lock);
+
+	spin_lock_init(&vfe32_ctrl->aec_ack_lock);
+	spin_lock_init(&vfe32_ctrl->awb_ack_lock);
+	spin_lock_init(&vfe32_ctrl->af_ack_lock);
+	spin_lock_init(&vfe32_ctrl->ihist_ack_lock);
+	spin_lock_init(&vfe32_ctrl->rs_ack_lock);
+	spin_lock_init(&vfe32_ctrl->cs_ack_lock);
+	spin_lock_init(&vfe32_ctrl->comp_stats_ack_lock);
+	spin_lock_init(&vfe32_ctrl->sd_notify_lock);
+
+	vfe32_ctrl->update_linear = false;
+	vfe32_ctrl->update_rolloff = false;
+	vfe32_ctrl->update_la = false;
+	vfe32_ctrl->update_gamma = false;
+	vfe32_ctrl->hfr_mode = HFR_MODE_OFF;
+
+	return rc;
+}
+
+void msm_axi_subdev_release(struct v4l2_subdev *sd)
+{
+	struct msm_cam_media_controller *pmctl =
+		(struct msm_cam_media_controller *)v4l2_get_subdev_hostdata(sd);
+	struct axi_ctrl_t *axi_ctrl = v4l2_get_subdevdata(sd);
+	CDBG("%s, free_irq\n", __func__);
+	disable_irq(axi_ctrl->vfeirq->start);
+	tasklet_kill(&axi_ctrl->vfe32_tasklet);
+	msm_cam_clk_enable(&axi_ctrl->pdev->dev, vfe32_clk_info,
+			axi_ctrl->vfe_clk, ARRAY_SIZE(vfe32_clk_info), 0);
+	if (axi_ctrl->fs_vfe) {
+		regulator_disable(axi_ctrl->fs_vfe);
+		regulator_put(axi_ctrl->fs_vfe);
+		axi_ctrl->fs_vfe = NULL;
+	}
+	iounmap(axi_ctrl->vfebase);
+	axi_ctrl->vfebase = NULL;
+
+	if (atomic_read(&irq_cnt))
+		pr_warning("%s, Warning IRQ Count not ZERO\n", __func__);
+
+	msm_camio_bus_scale_cfg(
+		pmctl->sdata->pdata->cam_bus_scale_table, S_EXIT);
+}
+
+void msm_vfe_subdev_release(struct v4l2_subdev *sd)
+{
+	vfe32_ctrl->vfebase = 0;
+}
+
+static int msm_axi_config(struct v4l2_subdev *sd, void __user *arg)
+{
+	struct msm_vfe_cfg_cmd cfgcmd;
+	struct msm_isp_cmd vfecmd;
+	int rc = 0;
+
+	if (copy_from_user(&cfgcmd, arg, sizeof(cfgcmd))) {
+		ERR_COPY_FROM_USER();
+		return -EFAULT;
+	}
+
+	if (copy_from_user(&vfecmd,
+			(void __user *)(cfgcmd.value),
+			sizeof(vfecmd))) {
+		pr_err("%s %d: copy_from_user failed\n", __func__,
+			__LINE__);
+		return -EFAULT;
+	}
+
+	switch (cfgcmd.cmd_type) {
+	case CMD_AXI_CFG_PRIM: {
+		uint32_t *axio = NULL;
+		axio = kmalloc(vfe32_cmd[VFE_CMD_AXI_OUT_CFG].length,
+				GFP_ATOMIC);
+		if (!axio) {
+			rc = -ENOMEM;
+			break;
+		}
+
+		if (copy_from_user(axio, (void __user *)(vfecmd.value),
+				vfe32_cmd[VFE_CMD_AXI_OUT_CFG].length)) {
+			kfree(axio);
+			rc = -EFAULT;
+			break;
+		}
+		vfe32_config_axi(OUTPUT_PRIM, axio);
+		kfree(axio);
+	}
+		break;
+	case CMD_AXI_CFG_PRIM_ALL_CHNLS: {
+		uint32_t *axio = NULL;
+		axio = kmalloc(vfe32_cmd[VFE_CMD_AXI_OUT_CFG].length,
+				GFP_ATOMIC);
+		if (!axio) {
+			rc = -ENOMEM;
+			break;
+		}
+
+		if (copy_from_user(axio, (void __user *)(vfecmd.value),
+				vfe32_cmd[VFE_CMD_AXI_OUT_CFG].length)) {
+			kfree(axio);
+			rc = -EFAULT;
+			break;
+		}
+		vfe32_config_axi(OUTPUT_PRIM_ALL_CHNLS, axio);
+		kfree(axio);
+	}
+		break;
+	case CMD_AXI_CFG_PRIM|CMD_AXI_CFG_SEC: {
+		uint32_t *axio = NULL;
+		axio = kmalloc(vfe32_cmd[VFE_CMD_AXI_OUT_CFG].length,
+				GFP_ATOMIC);
+		if (!axio) {
+			rc = -ENOMEM;
+			break;
+		}
+
+		if (copy_from_user(axio, (void __user *)(vfecmd.value),
+				vfe32_cmd[VFE_CMD_AXI_OUT_CFG].length)) {
+			kfree(axio);
+			rc = -EFAULT;
+			break;
+		}
+		vfe32_config_axi(OUTPUT_PRIM|OUTPUT_SEC, axio);
+		kfree(axio);
+	}
+		break;
+	case CMD_AXI_CFG_PRIM|CMD_AXI_CFG_SEC_ALL_CHNLS: {
+		uint32_t *axio = NULL;
+		axio = kmalloc(vfe32_cmd[VFE_CMD_AXI_OUT_CFG].length,
+				GFP_ATOMIC);
+		if (!axio) {
+			rc = -ENOMEM;
+			break;
+		}
+
+		if (copy_from_user(axio, (void __user *)(vfecmd.value),
+				vfe32_cmd[VFE_CMD_AXI_OUT_CFG].length)) {
+			kfree(axio);
+			rc = -EFAULT;
+			break;
+		}
+		vfe32_config_axi(OUTPUT_PRIM|OUTPUT_SEC_ALL_CHNLS, axio);
+		kfree(axio);
+	}
+		break;
+	case CMD_AXI_CFG_PRIM_ALL_CHNLS|CMD_AXI_CFG_SEC: {
+		uint32_t *axio = NULL;
+		axio = kmalloc(vfe32_cmd[VFE_CMD_AXI_OUT_CFG].length,
+				GFP_ATOMIC);
+		if (!axio) {
+			rc = -ENOMEM;
+			break;
+		}
+
+		if (copy_from_user(axio, (void __user *)(vfecmd.value),
+				vfe32_cmd[VFE_CMD_AXI_OUT_CFG].length)) {
+			kfree(axio);
+			rc = -EFAULT;
+			break;
+		}
+		vfe32_config_axi(OUTPUT_PRIM_ALL_CHNLS|OUTPUT_SEC, axio);
+		kfree(axio);
+	}
+		break;
+	case CMD_AXI_CFG_PRIM_ALL_CHNLS|CMD_AXI_CFG_SEC_ALL_CHNLS:
+		pr_err("%s Invalid/Unsupported AXI configuration %x",
+			__func__, cfgcmd.cmd_type);
+		break;
+	default:
+		pr_err("%s Unsupported AXI configuration %x ", __func__,
+			cfgcmd.cmd_type);
+		break;
+	}
+	return rc;
+}
+
+static void msm_axi_process_irq(struct v4l2_subdev *sd, void *arg)
+{
+	uint32_t irqstatus = (uint32_t) arg;
+	/* next, check output path related interrupts. */
+	if (irqstatus &
+		VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE0_MASK) {
+		CDBG("Image composite done 0 irq occured.\n");
+		vfe32_process_output_path_irq_0();
+	}
+	if (irqstatus &
+		VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE1_MASK) {
+		CDBG("Image composite done 1 irq occured.\n");
+		vfe32_process_output_path_irq_1();
+	}
+	/* in snapshot mode if done then send
+	snapshot done message */
+	if (vfe32_ctrl->operation_mode ==
+			VFE_OUTPUTS_THUMB_AND_MAIN ||
+		vfe32_ctrl->operation_mode ==
+			VFE_OUTPUTS_MAIN_AND_THUMB ||
+		vfe32_ctrl->operation_mode ==
+			VFE_OUTPUTS_THUMB_AND_JPEG ||
+		vfe32_ctrl->operation_mode ==
+			VFE_OUTPUTS_JPEG_AND_THUMB ||
+		vfe32_ctrl->operation_mode ==
+			VFE_OUTPUTS_RAW) {
+		if ((vfe32_ctrl->outpath.out0.capture_cnt == 0)
+				&& (vfe32_ctrl->outpath.out1.
+				capture_cnt == 0)) {
+			msm_camera_io_w_mb(
+				CAMIF_COMMAND_STOP_IMMEDIATELY,
+				vfe32_ctrl->vfebase +
+				VFE_CAMIF_COMMAND);
+			vfe32_send_isp_msg(vfe32_ctrl,
+				MSG_ID_SNAPSHOT_DONE);
+		}
+	}
+}
+
+static const struct v4l2_subdev_internal_ops msm_vfe_internal_ops;
+
+static long msm_axi_subdev_ioctl(struct v4l2_subdev *sd,
+			unsigned int cmd, void *arg)
+{
+	int rc = -ENOIOCTLCMD;
+	switch (cmd) {
+	case VIDIOC_MSM_AXI_INIT:
+		rc = msm_axi_subdev_init(sd,
+			(struct msm_cam_media_controller *)arg);
+		break;
+	case VIDIOC_MSM_AXI_CFG:
+		rc = msm_axi_config(sd, arg);
+		break;
+	case VIDIOC_MSM_AXI_IRQ:
+		msm_axi_process_irq(sd, arg);
+		rc = 0;
+		break;
+	case VIDIOC_MSM_AXI_RELEASE:
+		msm_axi_subdev_release(sd);
+		rc = 0;
+		break;
+	default:
+		pr_err("%s: command not found\n", __func__);
+	}
+	return rc;
+}
+
+static const struct v4l2_subdev_core_ops msm_axi_subdev_core_ops = {
+	.ioctl = msm_axi_subdev_ioctl,
+};
+
+static const struct v4l2_subdev_video_ops msm_axi_subdev_video_ops = {
+	.s_crystal_freq = msm_axi_subdev_s_crystal_freq,
+};
+
+static const struct v4l2_subdev_ops msm_axi_subdev_ops = {
+	.core = &msm_axi_subdev_core_ops,
+	.video = &msm_axi_subdev_video_ops,
+};
+
+static const struct v4l2_subdev_internal_ops msm_axi_internal_ops;
+
+static int __devinit vfe32_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct axi_ctrl_t *axi_ctrl;
+	CDBG("%s: device id = %d\n", __func__, pdev->id);
+	vfe32_ctrl = kzalloc(sizeof(struct vfe32_ctrl_type), GFP_KERNEL);
+	if (!vfe32_ctrl) {
+		pr_err("%s: no enough memory\n", __func__);
+		return -ENOMEM;
+	}
+
+	axi_ctrl = kzalloc(sizeof(struct axi_ctrl_t), GFP_KERNEL);
+	v4l2_subdev_init(&axi_ctrl->subdev, &msm_axi_subdev_ops);
+	axi_ctrl->subdev.internal_ops = &msm_axi_internal_ops;
+	axi_ctrl->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	snprintf(axi_ctrl->subdev.name,
+			 sizeof(axi_ctrl->subdev.name), "axi");
+	v4l2_set_subdevdata(&axi_ctrl->subdev, axi_ctrl);
+	axi_ctrl->pdev = pdev;
+	msm_cam_register_subdev_node(&axi_ctrl->subdev, AXI_DEV, 0);
+
+	v4l2_subdev_init(&vfe32_ctrl->subdev, &msm_vfe_subdev_ops);
+	vfe32_ctrl->subdev.internal_ops = &msm_vfe_internal_ops;
+	vfe32_ctrl->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	snprintf(vfe32_ctrl->subdev.name,
+			 sizeof(vfe32_ctrl->subdev.name), "vfe3.2");
+	v4l2_set_subdevdata(&vfe32_ctrl->subdev, vfe32_ctrl);
+	platform_set_drvdata(pdev, &vfe32_ctrl->subdev);
+
+	axi_ctrl->vfemem = platform_get_resource_byname(pdev,
+					IORESOURCE_MEM, "vfe32");
+	if (!axi_ctrl->vfemem) {
+		pr_err("%s: no mem resource?\n", __func__);
+		rc = -ENODEV;
+		goto vfe32_no_resource;
+	}
+	axi_ctrl->vfeirq = platform_get_resource_byname(pdev,
+					IORESOURCE_IRQ, "vfe32");
+	if (!axi_ctrl->vfeirq) {
+		pr_err("%s: no irq resource?\n", __func__);
+		rc = -ENODEV;
+		goto vfe32_no_resource;
+	}
+
+	axi_ctrl->vfeio = request_mem_region(axi_ctrl->vfemem->start,
+		resource_size(axi_ctrl->vfemem), pdev->name);
+	if (!axi_ctrl->vfeio) {
+		pr_err("%s: no valid mem region\n", __func__);
+		rc = -EBUSY;
+		goto vfe32_no_resource;
+	}
+
+	rc = request_irq(axi_ctrl->vfeirq->start, vfe32_parse_irq,
+		IRQF_TRIGGER_RISING, "vfe", axi_ctrl);
+	if (rc < 0) {
+		release_mem_region(axi_ctrl->vfemem->start,
+			resource_size(axi_ctrl->vfemem));
+		pr_err("%s: irq request fail\n", __func__);
+		rc = -EBUSY;
+		goto vfe32_no_resource;
+	}
+
+	disable_irq(axi_ctrl->vfeirq->start);
+
+	tasklet_init(&axi_ctrl->vfe32_tasklet,
+		axi32_do_tasklet, (unsigned long)axi_ctrl);
+
+	vfe32_ctrl->pdev = pdev;
+	msm_cam_register_subdev_node(&vfe32_ctrl->subdev, VFE_DEV, 0);
+	return 0;
+
+vfe32_no_resource:
+	kfree(vfe32_ctrl);
+	kfree(axi_ctrl);
+	return 0;
+}
+
+static struct platform_driver vfe32_driver = {
+	.probe = vfe32_probe,
+	.driver = {
+		.name = MSM_VFE_DRV_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init msm_vfe32_init_module(void)
+{
+	return platform_driver_register(&vfe32_driver);
+}
+
+static void __exit msm_vfe32_exit_module(void)
+{
+	platform_driver_unregister(&vfe32_driver);
+}
+
+module_init(msm_vfe32_init_module);
+module_exit(msm_vfe32_exit_module);
+MODULE_DESCRIPTION("VFE 3.2 driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/msm_vfe32.h b/drivers/media/video/msm/msm_vfe32.h
new file mode 100644
index 0000000..d1faded
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe32.h
@@ -0,0 +1,1018 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MSM_VFE32_H__
+#define __MSM_VFE32_H__
+
+#include <linux/bitops.h>
+
+#define TRUE  1
+#define FALSE 0
+
+#define VFE32_HW_NUMBER 0x3030B
+#define VFE33_HW_NUMBER 0x30408
+
+/* This defines total number registers in VFE.
+ * Each register is 4 bytes so to get the range,
+ * multiply this number with 4. */
+#define VFE32_REGISTER_TOTAL 0x000001CD
+#define VFE33_REGISTER_TOTAL 0x000001EE
+
+/* at start of camif,  bit 1:0 = 0x01:enable
+ * image data capture at frame boundary. */
+#define CAMIF_COMMAND_START  0x00000005
+
+/* bit 2= 0x1:clear the CAMIF_STATUS register
+ * value. */
+#define CAMIF_COMMAND_CLEAR  0x00000004
+
+/* at stop of vfe pipeline, for now it is assumed
+ * that camif will stop at any time. Bit 1:0 = 0x10:
+ * disable image data capture immediately. */
+#define CAMIF_COMMAND_STOP_IMMEDIATELY  0x00000002
+
+/* at stop of vfe pipeline, for now it is assumed
+ * that camif will stop at any time. Bit 1:0 = 0x00:
+ * disable image data capture at frame boundary */
+#define CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY  0x00000000
+
+/* to halt axi bridge */
+#define AXI_HALT  0x00000001
+
+/* clear the halt bit. */
+#define AXI_HALT_CLEAR  0x00000000
+
+/* reset the pipeline when stop command is issued.
+ * (without reset the register.) bit 26-32 = 0,
+ * domain reset, bit 0-9 = 1 for module reset, except
+ * register module. */
+#define VFE_RESET_UPON_STOP_CMD  0x000003ef
+
+/* reset the pipeline when reset command.
+ * bit 26-32 = 0, domain reset, bit 0-9 = 1 for module reset. */
+#define VFE_RESET_UPON_RESET_CMD  0x000003ff
+
+/* bit 5 is for axi status idle or busy.
+ * 1 =  halted,  0 = busy */
+#define AXI_STATUS_BUSY_MASK 0x00000020
+
+/* bit 0 & bit 1 = 1, both y and cbcr irqs need to be present
+ * for frame done interrupt */
+#define VFE_COMP_IRQ_BOTH_Y_CBCR 3
+
+/* bit 1 = 1, only cbcr irq triggers frame done interrupt */
+#define VFE_COMP_IRQ_CBCR_ONLY 2
+
+/* bit 0 = 1, only y irq triggers frame done interrupt */
+#define VFE_COMP_IRQ_Y_ONLY 1
+
+/* bit 0 = 1, PM go;   bit1 = 1, PM stop */
+#define VFE_PERFORMANCE_MONITOR_GO   0x00000001
+#define VFE_PERFORMANCE_MONITOR_STOP 0x00000002
+
+/* bit 0 = 1, test gen go;   bit1 = 1, test gen stop */
+#define VFE_TEST_GEN_GO   0x00000001
+#define VFE_TEST_GEN_STOP 0x00000002
+
+/* the chroma is assumed to be interpolated between
+ * the luma samples.  JPEG 4:2:2 */
+#define VFE_CHROMA_UPSAMPLE_INTERPOLATED 0
+
+/* constants for irq registers */
+#define VFE_DISABLE_ALL_IRQS 0
+/* bit =1 is to clear the corresponding bit in VFE_IRQ_STATUS.  */
+#define VFE_CLEAR_ALL_IRQS   0xffffffff
+
+#define VFE_IRQ_STATUS0_CAMIF_SOF_MASK            0x00000001
+#define VFE_IRQ_STATUS0_REG_UPDATE_MASK           0x00000020
+#define VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE0_MASK 0x00200000
+#define VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE1_MASK 0x00400000
+#define VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE2_MASK 0x00800000
+#define VFE_IRQ_STATUS1_RESET_AXI_HALT_ACK_MASK   0x00800000
+#define VFE_IRQ_STATUS0_STATS_COMPOSIT_MASK       0x01000000
+
+#define VFE_IRQ_STATUS0_STATS_AEC     0x2000  /* bit 13 */
+#define VFE_IRQ_STATUS0_STATS_AF      0x4000  /* bit 14 */
+#define VFE_IRQ_STATUS0_STATS_AWB     0x8000  /* bit 15 */
+#define VFE_IRQ_STATUS0_STATS_RS      0x10000  /* bit 16 */
+#define VFE_IRQ_STATUS0_STATS_CS      0x20000  /* bit 17 */
+#define VFE_IRQ_STATUS0_STATS_IHIST   0x40000  /* bit 18 */
+
+#define VFE_IRQ_STATUS0_SYNC_TIMER0   0x2000000  /* bit 25 */
+#define VFE_IRQ_STATUS0_SYNC_TIMER1   0x4000000  /* bit 26 */
+#define VFE_IRQ_STATUS0_SYNC_TIMER2   0x8000000  /* bit 27 */
+#define VFE_IRQ_STATUS0_ASYNC_TIMER0  0x10000000  /* bit 28 */
+#define VFE_IRQ_STATUS0_ASYNC_TIMER1  0x20000000  /* bit 29 */
+#define VFE_IRQ_STATUS0_ASYNC_TIMER2  0x40000000  /* bit 30 */
+#define VFE_IRQ_STATUS0_ASYNC_TIMER3  0x80000000  /* bit 32 */
+
+/* imask for while waiting for stop ack,  driver has already
+ * requested stop, waiting for reset irq, and async timer irq.
+ * For irq_status_0, bit 28-32 are for async timer. For
+ * irq_status_1, bit 22 for reset irq, bit 23 for axi_halt_ack
+   irq */
+#define VFE_IMASK_WHILE_STOPPING_0  0xF0000000
+#define VFE_IMASK_WHILE_STOPPING_1  0x00800000
+
+/* no error irq in mask 0 */
+#define VFE_IMASK_ERROR_ONLY_0  0x0
+/* when normal case, don't want to block error status. */
+/* bit 0-21 are error irq bits */
+#define VFE_IMASK_ERROR_ONLY_1  0x003fffff
+
+/* For BPC bit 0,bit 12-17 and bit 26 -20 are set to zero and other's 1 */
+#define BPC_MASK 0xF80C0FFE
+
+/* For ABF bit 4 is set to zero and other's 1 */
+#define ABF_MASK 0xFFFFFFF7
+
+
+/* For DBPC bit 0 is set to zero and other's 1 */
+#define DBPC_MASK 0xFFFFFFFE
+
+/* For DBPC bit 1 is set to zero and other's 1 */
+#define DBCC_MASK 0xFFFFFFFD
+
+/* For DBPC/ABF/DBCC/ABCC bits are set to 1 all others 0 */
+#define DEMOSAIC_MASK 0xF
+
+/* For MCE enable bit 28 set to zero and other's 1 */
+#define MCE_EN_MASK 0xEFFFFFFF
+
+/* For MCE Q_K bit 28 to 32 set to zero and other's 1 */
+#define MCE_Q_K_MASK 0x0FFFFFFF
+
+#define AE_BG_ENABLE_MASK 0x00000020      /* bit 5 */
+#define AF_BF_ENABLE_MASK 0x00000040      /* bit 6 */
+#define AWB_ENABLE_MASK 0x00000080     /* bit 7 */
+#define RS_ENABLE_MASK 0x00000100      /* bit 8  */
+#define CS_ENABLE_MASK 0x00000200      /* bit 9  */
+#define RS_CS_ENABLE_MASK 0x00000300   /* bit 8,9  */
+#define CLF_ENABLE_MASK 0x00002000     /* bit 13 */
+#define IHIST_ENABLE_MASK 0x00010000   /* bit 16 */
+#define STATS_ENABLE_MASK 0x000903E0   /* bit 19,16,9,8,7,6,5*/
+
+#define VFE_REG_UPDATE_TRIGGER           1
+#define VFE_PM_BUF_MAX_CNT_MASK          0xFF
+#define VFE_DMI_CFG_DEFAULT              0x00000100
+#define VFE_AE_PINGPONG_STATUS_BIT       0x80
+#define VFE_AF_PINGPONG_STATUS_BIT       0x100
+#define VFE_AWB_PINGPONG_STATUS_BIT      0x200
+
+#define HFR_MODE_OFF 1
+#define VFE_FRAME_SKIP_PERIOD_MASK 0x0000001F /*bits 0 -4*/
+
+enum VFE32_DMI_RAM_SEL {
+	NO_MEM_SELECTED          = 0,
+	BLACK_LUT_RAM_BANK0      = 0x1,
+	BLACK_LUT_RAM_BANK1      = 0x2,
+	ROLLOFF_RAM0_BANK0       = 0x3,
+	DEMOSAIC_LUT_RAM_BANK0   = 0x4,
+	DEMOSAIC_LUT_RAM_BANK1   = 0x5,
+	STATS_BHIST_RAM0         = 0x6,
+	STATS_BHIST_RAM1         = 0x7,
+	RGBLUT_RAM_CH0_BANK0     = 0x8,
+	RGBLUT_RAM_CH0_BANK1     = 0x9,
+	RGBLUT_RAM_CH1_BANK0     = 0xa,
+	RGBLUT_RAM_CH1_BANK1     = 0xb,
+	RGBLUT_RAM_CH2_BANK0     = 0xc,
+	RGBLUT_RAM_CH2_BANK1     = 0xd,
+	RGBLUT_CHX_BANK0         = 0xe,
+	RGBLUT_CHX_BANK1         = 0xf,
+	STATS_IHIST_RAM          = 0x10,
+	LUMA_ADAPT_LUT_RAM_BANK0 = 0x11,
+	LUMA_ADAPT_LUT_RAM_BANK1 = 0x12,
+	ROLLOFF_RAM1_BANK0       = 0x13,
+	ROLLOFF_RAM0_BANK1       = 0x14,
+	ROLLOFF_RAM1_BANK1       = 0x15,
+};
+
+enum vfe_output_state {
+	VFE_STATE_IDLE,
+	VFE_STATE_START_REQUESTED,
+	VFE_STATE_STARTED,
+	VFE_STATE_STOP_REQUESTED,
+	VFE_STATE_STOPPED,
+};
+
+#define V32_CAMIF_OFF             0x000001E4
+#define V32_CAMIF_LEN             32
+
+#define V32_DEMUX_OFF             0x00000284
+#define V32_DEMUX_LEN             20
+
+#define V32_DEMOSAICV3_0_OFF      0x00000298
+#define V32_DEMOSAICV3_0_LEN      4
+#define V32_DEMOSAICV3_1_OFF      0x0000061C
+#define V32_DEMOSAICV3_1_LEN      88
+#define V32_DEMOSAICV3_2_OFF      0x0000066C
+#define V32_DEMOSAICV3_UP_REG_CNT 5
+/* BPC     */
+#define V32_DEMOSAIC_2_OFF        0x0000029C
+#define V32_DEMOSAIC_2_LEN        8
+
+#define V32_OUT_CLAMP_OFF         0x00000524
+#define V32_OUT_CLAMP_LEN         8
+
+#define V32_OPERATION_CFG_LEN     44
+
+#define V32_AXI_OUT_OFF           0x00000038
+#define V32_AXI_OUT_LEN           216
+#define V32_AXI_CH_INF_LEN        24
+#define V32_AXI_CFG_LEN           47
+#define V32_AXI_BUS_FMT_OFF    1
+#define V32_AXI_BUS_FMT_LEN    4
+
+#define V32_FRAME_SKIP_OFF        0x00000504
+#define V32_FRAME_SKIP_LEN        32
+
+#define V32_CHROMA_SUBS_OFF       0x000004F8
+#define V32_CHROMA_SUBS_LEN       12
+
+#define V32_FOV_OFF           0x00000360
+#define V32_FOV_LEN           8
+
+#define V32_MAIN_SCALER_OFF 0x00000368
+#define V32_MAIN_SCALER_LEN 28
+
+#define V32_S2Y_OFF 0x000004D0
+#define V32_S2Y_LEN 20
+
+#define V32_S2CbCr_OFF 0x000004E4
+#define V32_S2CbCr_LEN 20
+
+#define V32_CHROMA_EN_OFF 0x000003C4
+#define V32_CHROMA_EN_LEN 36
+
+#define V32_SYNC_TIMER_OFF      0x0000020C
+#define V32_SYNC_TIMER_POLARITY_OFF 0x00000234
+#define V32_TIMER_SELECT_OFF        0x0000025C
+#define V32_SYNC_TIMER_LEN 28
+
+#define V32_ASYNC_TIMER_OFF 0x00000238
+#define V32_ASYNC_TIMER_LEN 28
+
+#define V32_BLACK_LEVEL_OFF 0x00000264
+#define V32_BLACK_LEVEL_LEN 16
+
+#define V32_MESH_ROLL_OFF_CFG_OFF             0x00000274
+#define V32_MESH_ROLL_OFF_CFG_LEN             16
+#define V32_MESH_ROLL_OFF_INIT_TABLE_SIZE     13
+#define V32_MESH_ROLL_OFF_DELTA_TABLE_SIZE    208
+#define V32_MESH_ROLL_OFF_DELTA_TABLE_OFFSET  32
+#define V32_GAMMA_LUT_BANK_SEL_MASK           0x00000007
+
+#define V33_PCA_ROLL_OFF_CFG_LEN1             16
+#define V33_PCA_ROLL_OFF_CFG_OFF1             0x00000274
+#define V33_PCA_ROLL_OFF_CFG_LEN2             12
+#define V33_PCA_ROLL_OFF_CFG_OFF2             0x000007A8
+#define V33_PCA_ROLL_OFF_TABLE_SIZE           (17 + (13*4))
+#define V33_PCA_ROLL_OFF_LUT_BANK_SEL_MASK    0x00010000
+
+#define V32_COLOR_COR_OFF 0x00000388
+#define V32_COLOR_COR_LEN 52
+
+#define V32_WB_OFF 0x00000384
+#define V32_WB_LEN 4
+
+#define V32_RGB_G_OFF 0x000003BC
+#define V32_RGB_G_LEN 4
+
+#define V32_LA_OFF 0x000003C0
+#define V32_LA_LEN 4
+
+#define V32_SCE_OFF 0x00000418
+#define V32_SCE_LEN 136
+
+#define V32_CHROMA_SUP_OFF 0x000003E8
+#define V32_CHROMA_SUP_LEN 12
+
+#define V32_MCE_OFF 0x000003F4
+#define V32_MCE_LEN 36
+#define V32_STATS_AF_OFF 0x0000053c
+#define V32_STATS_AF_LEN 16
+
+#define V32_STATS_AE_OFF 0x00000534
+#define V32_STATS_AE_LEN 8
+
+#define V32_STATS_AWB_OFF 0x0000054c
+#define V32_STATS_AWB_LEN 32
+
+#define V32_STATS_IHIST_OFF 0x0000057c
+#define V32_STATS_IHIST_LEN 8
+
+#define V32_STATS_RS_OFF 0x0000056c
+#define V32_STATS_RS_LEN 8
+
+#define V32_STATS_CS_OFF 0x00000574
+#define V32_STATS_CS_LEN 8
+
+
+#define V32_ASF_OFF 0x000004A0
+#define V32_ASF_LEN 48
+#define V32_ASF_UPDATE_LEN 36
+
+#define V32_CAPTURE_LEN 4
+
+#define V32_GET_HW_VERSION_OFF 0
+#define V32_GET_HW_VERSION_LEN 4
+
+#define V32_LINEARIZATION_OFF1 0x00000264
+#define V32_LINEARIZATION_LEN1 16
+
+#define V32_LINEARIZATION_OFF2 0x0000067C
+#define V32_LINEARIZATION_LEN2 52
+
+#define V32_DEMOSAICV3_OFF 0x00000298
+#define V32_DEMOSAICV3_LEN 4
+
+#define V32_DEMOSAICV3_DBPC_CFG_OFF  0x0000029C
+#define V32_DEMOSAICV3_DBPC_LEN 4
+
+#define V32_DEMOSAICV3_DBPC_CFG_OFF0 0x000002a0
+#define V32_DEMOSAICV3_DBPC_CFG_OFF1 0x00000604
+#define V32_DEMOSAICV3_DBPC_CFG_OFF2 0x00000608
+
+#define V32_DEMOSAICV3_DBCC_OFF 0x0000060C
+#define V32_DEMOSAICV3_DBCC_LEN 16
+
+#define V32_DEMOSAICV3_ABF_OFF 0x000002A4
+#define V32_DEMOSAICV3_ABF_LEN 180
+
+#define V32_MODULE_CFG_OFF 0x00000010
+#define V32_MODULE_CFG_LEN 4
+
+#define V32_ASF_SPECIAL_EFX_CFG_OFF 0x000005FC
+#define V32_ASF_SPECIAL_EFX_CFG_LEN 4
+
+#define V32_CLF_CFG_OFF 0x000006B0
+#define V32_CLF_CFG_LEN 72
+
+#define V32_CLF_LUMA_UPDATE_OFF 0x000006B4
+#define V32_CLF_LUMA_UPDATE_LEN 60
+
+#define V32_CLF_CHROMA_UPDATE_OFF 0x000006F0
+#define V32_CLF_CHROMA_UPDATE_LEN 8
+
+struct vfe_cmd_hw_version {
+	uint32_t minorVersion;
+	uint32_t majorVersion;
+	uint32_t coreVersion;
+};
+
+enum VFE_AXI_OUTPUT_MODE {
+	VFE_AXI_OUTPUT_MODE_Output1,
+	VFE_AXI_OUTPUT_MODE_Output2,
+	VFE_AXI_OUTPUT_MODE_Output1AndOutput2,
+	VFE_AXI_OUTPUT_MODE_CAMIFToAXIViaOutput2,
+	VFE_AXI_OUTPUT_MODE_Output2AndCAMIFToAXIViaOutput1,
+	VFE_AXI_OUTPUT_MODE_Output1AndCAMIFToAXIViaOutput2,
+	VFE_AXI_LAST_OUTPUT_MODE_ENUM
+};
+
+enum VFE_RAW_WR_PATH_SEL {
+	VFE_RAW_OUTPUT_DISABLED,
+	VFE_RAW_OUTPUT_ENC_CBCR_PATH,
+	VFE_RAW_OUTPUT_VIEW_CBCR_PATH,
+	VFE_RAW_OUTPUT_PATH_INVALID
+};
+
+
+#define VFE_AXI_OUTPUT_BURST_LENGTH     4
+#define VFE_MAX_NUM_FRAGMENTS_PER_FRAME 4
+#define VFE_AXI_OUTPUT_CFG_FRAME_COUNT  3
+
+struct vfe_cmds_per_write_master {
+	uint16_t imageWidth;
+	uint16_t imageHeight;
+	uint16_t outRowCount;
+	uint16_t outRowIncrement;
+	uint32_t outFragments[VFE_AXI_OUTPUT_CFG_FRAME_COUNT]
+		[VFE_MAX_NUM_FRAGMENTS_PER_FRAME];
+};
+
+struct vfe_cmds_axi_per_output_path {
+	uint8_t fragmentCount;
+	struct vfe_cmds_per_write_master firstWM;
+	struct vfe_cmds_per_write_master secondWM;
+};
+
+enum VFE_AXI_BURST_LENGTH {
+	VFE_AXI_BURST_LENGTH_IS_2  = 2,
+	VFE_AXI_BURST_LENGTH_IS_4  = 4,
+	VFE_AXI_BURST_LENGTH_IS_8  = 8,
+	VFE_AXI_BURST_LENGTH_IS_16 = 16
+};
+
+
+struct vfe_cmd_fov_crop_config {
+	uint8_t enable;
+	uint16_t firstPixel;
+	uint16_t lastPixel;
+	uint16_t firstLine;
+	uint16_t lastLine;
+};
+
+struct vfe_cmds_main_scaler_stripe_init {
+	uint16_t MNCounterInit;
+	uint16_t phaseInit;
+};
+
+struct vfe_cmds_scaler_one_dimension {
+	uint8_t  enable;
+	uint16_t inputSize;
+	uint16_t outputSize;
+	uint32_t phaseMultiplicationFactor;
+	uint8_t  interpolationResolution;
+};
+
+struct vfe_cmd_main_scaler_config {
+	uint8_t enable;
+	struct vfe_cmds_scaler_one_dimension    hconfig;
+	struct vfe_cmds_scaler_one_dimension    vconfig;
+	struct vfe_cmds_main_scaler_stripe_init MNInitH;
+	struct vfe_cmds_main_scaler_stripe_init MNInitV;
+};
+
+struct vfe_cmd_scaler2_config {
+	uint8_t enable;
+	struct vfe_cmds_scaler_one_dimension hconfig;
+	struct vfe_cmds_scaler_one_dimension vconfig;
+};
+
+
+struct vfe_cmd_frame_skip_update {
+	uint32_t output1Pattern;
+	uint32_t output2Pattern;
+};
+
+struct vfe_cmd_output_clamp_config {
+	uint8_t minCh0;
+	uint8_t minCh1;
+	uint8_t minCh2;
+	uint8_t maxCh0;
+	uint8_t maxCh1;
+	uint8_t maxCh2;
+};
+
+struct vfe_cmd_chroma_subsample_config {
+	uint8_t enable;
+	uint8_t cropEnable;
+	uint8_t vsubSampleEnable;
+	uint8_t hsubSampleEnable;
+	uint8_t vCosited;
+	uint8_t hCosited;
+	uint8_t vCositedPhase;
+	uint8_t hCositedPhase;
+	uint16_t cropWidthFirstPixel;
+	uint16_t cropWidthLastPixel;
+	uint16_t cropHeightFirstLine;
+	uint16_t cropHeightLastLine;
+};
+
+enum VFE_START_PIXEL_PATTERN {
+	VFE_BAYER_RGRGRG,
+	VFE_BAYER_GRGRGR,
+	VFE_BAYER_BGBGBG,
+	VFE_BAYER_GBGBGB,
+	VFE_YUV_YCbYCr,
+	VFE_YUV_YCrYCb,
+	VFE_YUV_CbYCrY,
+	VFE_YUV_CrYCbY
+};
+
+enum VFE_BUS_RD_INPUT_PIXEL_PATTERN {
+	VFE_BAYER_RAW,
+	VFE_YUV_INTERLEAVED,
+	VFE_YUV_PSEUDO_PLANAR_Y,
+	VFE_YUV_PSEUDO_PLANAR_CBCR
+};
+
+enum VFE_YUV_INPUT_COSITING_MODE {
+	VFE_YUV_COSITED,
+	VFE_YUV_INTERPOLATED
+};
+
+#define VFE32_GAMMA_NUM_ENTRIES  64
+
+#define VFE32_LA_TABLE_LENGTH    64
+
+#define VFE32_LINEARIZATON_TABLE_LENGTH    36
+
+struct vfe_cmds_demosaic_abf {
+	uint8_t   enable;
+	uint8_t   forceOn;
+	uint8_t   shift;
+	uint16_t  lpThreshold;
+	uint16_t  max;
+	uint16_t  min;
+	uint8_t   ratio;
+};
+
+struct vfe_cmds_demosaic_bpc {
+	uint8_t   enable;
+	uint16_t  fmaxThreshold;
+	uint16_t  fminThreshold;
+	uint16_t  redDiffThreshold;
+	uint16_t  blueDiffThreshold;
+	uint16_t  greenDiffThreshold;
+};
+
+struct vfe_cmd_demosaic_config {
+	uint8_t   enable;
+	uint8_t   slopeShift;
+	struct vfe_cmds_demosaic_abf abfConfig;
+	struct vfe_cmds_demosaic_bpc bpcConfig;
+};
+
+struct vfe_cmd_demosaic_bpc_update {
+	struct vfe_cmds_demosaic_bpc bpcUpdate;
+};
+
+struct vfe_cmd_demosaic_abf_update {
+	struct vfe_cmds_demosaic_abf abfUpdate;
+};
+
+struct vfe_cmd_white_balance_config {
+	uint8_t  enable;
+	uint16_t ch2Gain;
+	uint16_t ch1Gain;
+	uint16_t ch0Gain;
+};
+
+enum VFE_COLOR_CORRECTION_COEF_QFACTOR {
+	COEF_IS_Q7_SIGNED,
+	COEF_IS_Q8_SIGNED,
+	COEF_IS_Q9_SIGNED,
+	COEF_IS_Q10_SIGNED
+};
+
+struct vfe_cmd_color_correction_config {
+	uint8_t     enable;
+	enum VFE_COLOR_CORRECTION_COEF_QFACTOR coefQFactor;
+	int16_t  C0;
+	int16_t  C1;
+	int16_t  C2;
+	int16_t  C3;
+	int16_t  C4;
+	int16_t  C5;
+	int16_t  C6;
+	int16_t  C7;
+	int16_t  C8;
+	int16_t  K0;
+	int16_t  K1;
+	int16_t  K2;
+};
+
+#define VFE_LA_TABLE_LENGTH 64
+
+struct vfe_cmd_la_config {
+	uint8_t enable;
+	int16_t table[VFE_LA_TABLE_LENGTH];
+};
+
+#define VFE_GAMMA_TABLE_LENGTH 256
+enum VFE_RGB_GAMMA_TABLE_SELECT {
+	RGB_GAMMA_CH0_SELECTED,
+	RGB_GAMMA_CH1_SELECTED,
+	RGB_GAMMA_CH2_SELECTED,
+	RGB_GAMMA_CH0_CH1_SELECTED,
+	RGB_GAMMA_CH0_CH2_SELECTED,
+	RGB_GAMMA_CH1_CH2_SELECTED,
+	RGB_GAMMA_CH0_CH1_CH2_SELECTED
+};
+
+struct vfe_cmd_rgb_gamma_config {
+	uint8_t enable;
+	enum VFE_RGB_GAMMA_TABLE_SELECT channelSelect;
+	int16_t table[VFE_GAMMA_TABLE_LENGTH];
+};
+
+struct vfe_cmd_chroma_enhan_config {
+	uint8_t  enable;
+	int16_t am;
+	int16_t ap;
+	int16_t bm;
+	int16_t bp;
+	int16_t cm;
+	int16_t cp;
+	int16_t dm;
+	int16_t dp;
+	int16_t kcr;
+	int16_t kcb;
+	int16_t RGBtoYConversionV0;
+	int16_t RGBtoYConversionV1;
+	int16_t RGBtoYConversionV2;
+	uint8_t RGBtoYConversionOffset;
+};
+
+struct vfe_cmd_chroma_suppression_config {
+	uint8_t enable;
+	uint8_t m1;
+	uint8_t m3;
+	uint8_t n1;
+	uint8_t n3;
+	uint8_t nn1;
+	uint8_t mm1;
+};
+
+struct vfe_cmd_asf_config {
+	uint8_t enable;
+	uint8_t smoothFilterEnabled;
+	uint8_t sharpMode;
+	uint8_t smoothCoefCenter;
+	uint8_t smoothCoefSurr;
+	uint8_t normalizeFactor;
+	uint8_t sharpK1;
+	uint8_t sharpK2;
+	uint8_t sharpThreshE1;
+	int8_t sharpThreshE2;
+	int8_t sharpThreshE3;
+	int8_t sharpThreshE4;
+	int8_t sharpThreshE5;
+	int8_t filter1Coefficients[9];
+	int8_t filter2Coefficients[9];
+	uint8_t  cropEnable;
+	uint16_t cropFirstPixel;
+	uint16_t cropLastPixel;
+	uint16_t cropFirstLine;
+	uint16_t cropLastLine;
+};
+
+struct vfe_cmd_asf_update {
+	uint8_t enable;
+	uint8_t smoothFilterEnabled;
+	uint8_t sharpMode;
+	uint8_t smoothCoefCenter;
+	uint8_t smoothCoefSurr;
+	uint8_t normalizeFactor;
+	uint8_t sharpK1;
+	uint8_t sharpK2;
+	uint8_t sharpThreshE1;
+	int8_t  sharpThreshE2;
+	int8_t  sharpThreshE3;
+	int8_t  sharpThreshE4;
+	int8_t  sharpThreshE5;
+	int8_t  filter1Coefficients[9];
+	int8_t  filter2Coefficients[9];
+	uint8_t cropEnable;
+};
+
+enum VFE_TEST_GEN_SYNC_EDGE {
+	VFE_TEST_GEN_SYNC_EDGE_ActiveHigh,
+	VFE_TEST_GEN_SYNC_EDGE_ActiveLow
+};
+
+
+struct vfe_cmd_bus_pm_start {
+	uint8_t output2YWrPmEnable;
+	uint8_t output2CbcrWrPmEnable;
+	uint8_t output1YWrPmEnable;
+	uint8_t output1CbcrWrPmEnable;
+};
+
+struct  vfe_frame_skip_counts {
+	uint32_t  totalFrameCount;
+	uint32_t  output1Count;
+	uint32_t  output2Count;
+};
+
+enum VFE_AXI_RD_UNPACK_HBI_SEL {
+	VFE_AXI_RD_HBI_32_CLOCK_CYCLES,
+	VFE_AXI_RD_HBI_64_CLOCK_CYCLES,
+	VFE_AXI_RD_HBI_128_CLOCK_CYCLES,
+	VFE_AXI_RD_HBI_256_CLOCK_CYCLES,
+	VFE_AXI_RD_HBI_512_CLOCK_CYCLES,
+	VFE_AXI_RD_HBI_1024_CLOCK_CYCLES,
+	VFE_AXI_RD_HBI_2048_CLOCK_CYCLES,
+	VFE_AXI_RD_HBI_4096_CLOCK_CYCLES
+};
+
+struct vfe_frame_bpc_info {
+	uint32_t greenDefectPixelCount;
+	uint32_t redBlueDefectPixelCount;
+};
+
+struct vfe_frame_asf_info {
+	uint32_t  asfMaxEdge;
+	uint32_t  asfHbiCount;
+};
+
+struct vfe_msg_camif_status {
+	uint8_t  camifState;
+	uint32_t pixelCount;
+	uint32_t lineCount;
+};
+
+struct vfe32_irq_status {
+	uint32_t vfeIrqStatus0;
+	uint32_t vfeIrqStatus1;
+	uint32_t camifStatus;
+	uint32_t demosaicStatus;
+	uint32_t asfMaxEdge;
+};
+
+#define V32_PREVIEW_AXI_FLAG  0x00000001
+#define V32_SNAPSHOT_AXI_FLAG (0x00000001<<1)
+
+struct vfe32_cmd_type {
+	uint16_t id;
+	uint32_t length;
+	uint32_t offset;
+	uint32_t flag;
+};
+
+struct vfe32_free_buf {
+	struct list_head node;
+	uint32_t paddr;
+	uint32_t y_off;
+	uint32_t cbcr_off;
+};
+
+struct vfe32_output_ch {
+	struct list_head free_buf_queue;
+	spinlock_t free_buf_lock;
+	uint16_t output_fmt;
+	int8_t ch0;
+	int8_t ch1;
+	int8_t ch2;
+	uint32_t  capture_cnt;
+	uint32_t  frame_drop_cnt;
+	struct msm_free_buf ping;
+	struct msm_free_buf pong;
+	struct msm_free_buf free_buf;
+};
+
+/* no error irq in mask 0 */
+#define VFE32_IMASK_ERROR_ONLY_0  0x0
+/* when normal case, don't want to block error status. */
+/* bit 0-21 are error irq bits */
+#define VFE32_IMASK_ERROR_ONLY_1               0x005FFFFF
+#define VFE32_IMASK_CAMIF_ERROR               (0x00000001<<0)
+#define VFE32_IMASK_BHIST_OVWR                (0x00000001<<1)
+#define VFE32_IMASK_STATS_CS_OVWR             (0x00000001<<2)
+#define VFE32_IMASK_STATS_IHIST_OVWR          (0x00000001<<3)
+#define VFE32_IMASK_REALIGN_BUF_Y_OVFL        (0x00000001<<4)
+#define VFE32_IMASK_REALIGN_BUF_CB_OVFL       (0x00000001<<5)
+#define VFE32_IMASK_REALIGN_BUF_CR_OVFL       (0x00000001<<6)
+#define VFE32_IMASK_VIOLATION                 (0x00000001<<7)
+#define VFE32_IMASK_IMG_MAST_0_BUS_OVFL       (0x00000001<<8)
+#define VFE32_IMASK_IMG_MAST_1_BUS_OVFL       (0x00000001<<9)
+#define VFE32_IMASK_IMG_MAST_2_BUS_OVFL       (0x00000001<<10)
+#define VFE32_IMASK_IMG_MAST_3_BUS_OVFL       (0x00000001<<11)
+#define VFE32_IMASK_IMG_MAST_4_BUS_OVFL       (0x00000001<<12)
+#define VFE32_IMASK_IMG_MAST_5_BUS_OVFL       (0x00000001<<13)
+#define VFE32_IMASK_IMG_MAST_6_BUS_OVFL       (0x00000001<<14)
+#define VFE32_IMASK_STATS_AE_BG_BUS_OVFL      (0x00000001<<15)
+#define VFE32_IMASK_STATS_AF_BF_BUS_OVFL      (0x00000001<<16)
+#define VFE32_IMASK_STATS_AWB_BUS_OVFL        (0x00000001<<17)
+#define VFE32_IMASK_STATS_RS_BUS_OVFL         (0x00000001<<18)
+#define VFE32_IMASK_STATS_CS_BUS_OVFL         (0x00000001<<19)
+#define VFE32_IMASK_STATS_IHIST_BUS_OVFL      (0x00000001<<20)
+#define VFE32_IMASK_STATS_SKIN_BHIST_BUS_OVFL (0x00000001<<21)
+#define VFE32_IMASK_AXI_ERROR                 (0x00000001<<22)
+
+#define VFE_COM_STATUS 0x000FE000
+
+struct vfe32_output_path {
+	uint16_t output_mode;     /* bitmask  */
+
+	struct vfe32_output_ch out0; /* preview and thumbnail */
+	struct vfe32_output_ch out1; /* snapshot */
+	struct vfe32_output_ch out2; /* video    */
+};
+
+struct vfe32_frame_extra {
+	uint32_t greenDefectPixelCount;
+	uint32_t redBlueDefectPixelCount;
+
+	uint32_t  asfMaxEdge;
+	uint32_t  asfHbiCount;
+
+	uint32_t yWrPmStats0;
+	uint32_t yWrPmStats1;
+	uint32_t cbcrWrPmStats0;
+	uint32_t cbcrWrPmStats1;
+
+	uint32_t  frameCounter;
+};
+
+#define VFE_DISABLE_ALL_IRQS            0
+#define VFE_CLEAR_ALL_IRQS              0xffffffff
+
+#define VFE_HW_VERSION			0x00000000
+#define VFE_GLOBAL_RESET                0x00000004
+#define VFE_MODULE_RESET		0x00000008
+#define VFE_CGC_OVERRIDE                0x0000000C
+#define VFE_MODULE_CFG                  0x00000010
+#define VFE_CFG				0x00000014
+#define VFE_IRQ_CMD                     0x00000018
+#define VFE_IRQ_MASK_0                  0x0000001C
+#define VFE_IRQ_MASK_1                  0x00000020
+#define VFE_IRQ_CLEAR_0                 0x00000024
+#define VFE_IRQ_CLEAR_1                 0x00000028
+#define VFE_IRQ_STATUS_0                0x0000002C
+#define VFE_IRQ_STATUS_1                0x00000030
+#define VFE_IRQ_COMP_MASK               0x00000034
+#define VFE_BUS_CMD                     0x00000038
+#define VFE_BUS_PING_PONG_STATUS        0x00000180
+#define VFE_AXI_CMD                     0x000001D8
+#define VFE_AXI_STATUS        0x000001DC
+#define VFE_BUS_STATS_PING_PONG_BASE    0x000000F4
+
+#define VFE_BUS_STATS_AEC_WR_PING_ADDR    0x000000F4
+#define VFE_BUS_STATS_AEC_WR_PONG_ADDR    0x000000F8
+#define VFE_BUS_STATS_AEC_UB_CFG          0x000000FC
+#define VFE_BUS_STATS_AF_WR_PING_ADDR     0x00000100
+#define VFE_BUS_STATS_AF_WR_PONG_ADDR     0x00000104
+#define VFE_BUS_STATS_AF_UB_CFG           0x00000108
+#define VFE_BUS_STATS_AWB_WR_PING_ADDR    0x0000010C
+#define VFE_BUS_STATS_AWB_WR_PONG_ADDR    0x00000110
+#define VFE_BUS_STATS_AWB_UB_CFG          0x00000114
+#define VFE_BUS_STATS_RS_WR_PING_ADDR    0x00000118
+#define VFE_BUS_STATS_RS_WR_PONG_ADDR    0x0000011C
+#define VFE_BUS_STATS_RS_UB_CFG          0x00000120
+
+#define VFE_BUS_STATS_CS_WR_PING_ADDR    0x00000124
+#define VFE_BUS_STATS_CS_WR_PONG_ADDR    0x00000128
+#define VFE_BUS_STATS_CS_UB_CFG          0x0000012C
+#define VFE_BUS_STATS_HIST_WR_PING_ADDR   0x00000130
+#define VFE_BUS_STATS_HIST_WR_PONG_ADDR   0x00000134
+#define VFE_BUS_STATS_HIST_UB_CFG          0x00000138
+#define VFE_BUS_STATS_SKIN_WR_PING_ADDR    0x0000013C
+#define VFE_BUS_STATS_SKIN_WR_PONG_ADDR    0x00000140
+#define VFE_BUS_STATS_SKIN_UB_CFG          0x00000144
+#define VFE_CAMIF_COMMAND               0x000001E0
+#define VFE_CAMIF_STATUS                0x00000204
+#define VFE_REG_UPDATE_CMD              0x00000260
+#define VFE_DEMUX_GAIN_0                0x00000288
+#define VFE_DEMUX_GAIN_1                0x0000028C
+#define VFE_CHROMA_UP                   0x0000035C
+#define VFE_FRAMEDROP_ENC_Y_CFG         0x00000504
+#define VFE_FRAMEDROP_ENC_CBCR_CFG      0x00000508
+#define VFE_FRAMEDROP_ENC_Y_PATTERN     0x0000050C
+#define VFE_FRAMEDROP_ENC_CBCR_PATTERN  0x00000510
+#define VFE_FRAMEDROP_VIEW_Y            0x00000514
+#define VFE_FRAMEDROP_VIEW_CBCR         0x00000518
+#define VFE_FRAMEDROP_VIEW_Y_PATTERN    0x0000051C
+#define VFE_FRAMEDROP_VIEW_CBCR_PATTERN 0x00000520
+#define VFE_CLAMP_MAX                   0x00000524
+#define VFE_CLAMP_MIN                   0x00000528
+#define VFE_REALIGN_BUF                 0x0000052C
+#define VFE_STATS_CFG                   0x00000530
+#define VFE_STATS_AWB_SGW_CFG           0x00000554
+#define VFE_DMI_CFG                     0x00000598
+#define VFE_DMI_ADDR                    0x0000059C
+#define VFE_DMI_DATA_LO                 0x000005A4
+#define VFE_BUS_IO_FORMAT_CFG           0x000006F8
+#define VFE_PIXEL_IF_CFG                0x000006FC
+#define VFE_RDI0_CFG                    0x00000734
+#define VFE_RDI1_CFG                    0x000007A4
+
+#define VFE_VIOLATION_STATUS            0x000007B4
+
+#define VFE33_DMI_DATA_HI               0x000005A0
+#define VFE33_DMI_DATA_LO               0x000005A4
+
+#define VFE32_OUTPUT_MODE_PT			BIT(0)
+#define VFE32_OUTPUT_MODE_S			BIT(1)
+#define VFE32_OUTPUT_MODE_V			BIT(2)
+#define VFE32_OUTPUT_MODE_P			BIT(3)
+#define VFE32_OUTPUT_MODE_T			BIT(4)
+#define VFE32_OUTPUT_MODE_P_ALL_CHNLS		BIT(5)
+#define VFE32_OUTPUT_MODE_PRIMARY		BIT(6)
+#define VFE32_OUTPUT_MODE_PRIMARY_ALL_CHNLS	BIT(7)
+#define VFE32_OUTPUT_MODE_SECONDARY		BIT(8)
+#define VFE32_OUTPUT_MODE_SECONDARY_ALL_CHNLS	BIT(9)
+
+struct vfe_stats_control {
+	uint8_t  ackPending;
+	uint32_t nextFrameAddrBuf;
+	uint32_t droppedStatsFrameCount;
+	uint32_t bufToRender;
+};
+
+struct axi_ctrl_t {
+	struct v4l2_subdev subdev;
+	struct platform_device *pdev;
+	struct resource *vfeirq;
+	spinlock_t  tasklet_lock;
+	struct list_head tasklet_q;
+
+	void __iomem *vfebase;
+	void *syncdata;
+
+	struct resource	*vfemem;
+	struct resource *vfeio;
+	struct regulator *fs_vfe;
+	struct clk *vfe_clk[3];
+	struct tasklet_struct vfe32_tasklet;
+};
+
+struct vfe32_ctrl_type {
+	uint16_t operation_mode;     /* streaming or snapshot */
+	struct vfe32_output_path outpath;
+
+	uint32_t vfeImaskCompositePacked;
+
+	spinlock_t  stop_flag_lock;
+	spinlock_t  update_ack_lock;
+	spinlock_t  state_lock;
+	spinlock_t  io_lock;
+
+	spinlock_t  aec_ack_lock;
+	spinlock_t  awb_ack_lock;
+	spinlock_t  af_ack_lock;
+	spinlock_t  ihist_ack_lock;
+	spinlock_t  rs_ack_lock;
+	spinlock_t  cs_ack_lock;
+	spinlock_t  comp_stats_ack_lock;
+
+	uint32_t extlen;
+	void *extdata;
+
+	int8_t start_ack_pending;
+	int8_t stop_ack_pending;
+	int8_t reset_ack_pending;
+	int8_t update_ack_pending;
+	enum vfe_output_state recording_state;
+	int8_t update_linear;
+	int8_t update_rolloff;
+	int8_t update_la;
+	int8_t update_gamma;
+	enum vfe_output_state liveshot_state;
+
+	void __iomem *vfebase;
+	uint32_t register_total;
+
+	uint32_t stats_comp;
+	atomic_t vstate;
+	uint32_t vfe_capture_count;
+	uint32_t sync_timer_repeat_count;
+	uint32_t sync_timer_state;
+	uint32_t sync_timer_number;
+
+	uint32_t vfeFrameId;
+	uint32_t output1Pattern;
+	uint32_t output1Period;
+	uint32_t output2Pattern;
+	uint32_t output2Period;
+	uint32_t vfeFrameSkipCount;
+	uint32_t vfeFrameSkipPeriod;
+	struct vfe_stats_control afStatsControl;
+	struct vfe_stats_control awbStatsControl;
+	struct vfe_stats_control aecStatsControl;
+	struct vfe_stats_control ihistStatsControl;
+	struct vfe_stats_control rsStatsControl;
+	struct vfe_stats_control csStatsControl;
+
+	/* v4l2 subdev */
+	struct v4l2_subdev subdev;
+	struct platform_device *pdev;
+	spinlock_t  sd_notify_lock;
+	uint32_t hfr_mode;
+	uint32_t frame_skip_cnt;
+	uint32_t frame_skip_pattern;
+	uint32_t snapshot_frame_cnt;
+};
+
+#define statsAeNum      0
+#define statsAfNum      1
+#define statsAwbNum     2
+#define statsRsNum      3
+#define statsCsNum      4
+#define statsIhistNum   5
+#define statsSkinNum    6
+
+struct vfe_cmd_stats_ack {
+	uint32_t  nextStatsBuf;
+};
+
+#define VFE_STATS_BUFFER_COUNT            3
+
+struct vfe_cmd_stats_buf {
+	uint32_t statsBuf[VFE_STATS_BUFFER_COUNT];
+};
+
+#define VIDIOC_MSM_AXI_INIT \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 18, struct msm_cam_media_controller *)
+
+#define VIDIOC_MSM_AXI_RELEASE \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 19, struct msm_cam_media_controller *)
+
+#define VIDIOC_MSM_AXI_CFG \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 20, void *)
+
+#define VIDIOC_MSM_AXI_IRQ \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 21, void *)
+
+#endif /* __MSM_VFE32_H__ */
diff --git a/drivers/media/video/msm/msm_vfe7x.c b/drivers/media/video/msm/msm_vfe7x.c
new file mode 100644
index 0000000..6a6eeb7
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe7x.c
@@ -0,0 +1,786 @@
+/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/msm_adsp.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/android_pmem.h>
+#include <linux/slab.h>
+#include <mach/msm_adsp.h>
+#include <mach/clk.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include "msm_vfe7x.h"
+#include <linux/pm_qos.h>
+
+#define QDSP_CMDQUEUE 25
+
+#define VFE_RESET_CMD 0
+#define VFE_START_CMD 1
+#define VFE_STOP_CMD  2
+#define VFE_FRAME_ACK 20
+#define STATS_AF_ACK  21
+#define STATS_WE_ACK  22
+
+#define MSG_STOP_ACK  1
+#define MSG_SNAPSHOT  2
+#define MSG_OUTPUT1   6
+#define MSG_OUTPUT2   7
+#define MSG_STATS_AF  8
+#define MSG_STATS_WE  9
+#define MSG_OUTPUT_S  10
+#define MSG_OUTPUT_T  11
+
+#define VFE_ADSP_EVENT 0xFFFF
+#define SNAPSHOT_MASK_MODE 0x00000002
+#define MSM_AXI_QOS_PREVIEW	192000
+#define MSM_AXI_QOS_SNAPSHOT	192000
+
+
+static struct msm_adsp_module *qcam_mod;
+static struct msm_adsp_module *vfe_mod;
+static struct msm_vfe_callback *resp;
+static void *extdata;
+static uint32_t extlen;
+
+struct mutex vfe_lock;
+static void     *vfe_syncdata;
+static uint8_t vfestopped;
+static uint32_t vfetask_state;
+static int cnt;
+
+static struct stop_event stopevent;
+
+unsigned long paddr_s_y;
+unsigned long paddr_s_cbcr;
+unsigned long paddr_t_y;
+unsigned long paddr_t_cbcr;
+
+static void vfe_7x_convert(struct msm_vfe_phy_info *pinfo,
+		enum vfe_resp_msg type,
+		void *data, void **ext, int32_t *elen)
+{
+	switch (type) {
+	case VFE_MSG_OUTPUT_P: {
+		pinfo->p0_phy = ((struct vfe_endframe *)data)->y_address;
+		pinfo->p1_phy =
+			((struct vfe_endframe *)data)->cbcr_address;
+		pinfo->p2_phy = pinfo->p0_phy;
+		pinfo->output_id = OUTPUT_TYPE_P;
+
+		CDBG("vfe_7x_convert, y_phy = 0x%x, cbcr_phy = 0x%x\n",
+				 pinfo->p0_phy, pinfo->p1_phy);
+
+		((struct vfe_frame_extra *)extdata)->bl_evencol =
+		((struct vfe_endframe *)data)->blacklevelevencolumn;
+
+		((struct vfe_frame_extra *)extdata)->bl_oddcol =
+		((struct vfe_endframe *)data)->blackleveloddcolumn;
+
+		((struct vfe_frame_extra *)extdata)->g_def_p_cnt =
+		((struct vfe_endframe *)data)->greendefectpixelcount;
+
+		((struct vfe_frame_extra *)extdata)->r_b_def_p_cnt =
+		((struct vfe_endframe *)data)->redbluedefectpixelcount;
+
+		*ext  = extdata;
+		*elen = extlen;
+	}
+		break;
+
+	case VFE_MSG_OUTPUT_S: {
+		pinfo->p0_phy = paddr_s_y;
+		pinfo->p1_phy = paddr_s_cbcr;
+		pinfo->p2_phy = pinfo->p0_phy;
+		pinfo->output_id = OUTPUT_TYPE_S;
+		CDBG("vfe_7x_convert: y_phy = 0x%x cbcr_phy = 0x%x\n",
+					pinfo->p0_phy, pinfo->p1_phy);
+	}
+		break;
+
+	case VFE_MSG_OUTPUT_T: {
+		pinfo->p0_phy = paddr_t_y;
+		pinfo->p1_phy = paddr_t_cbcr;
+		pinfo->p2_phy = pinfo->p0_phy;
+		pinfo->output_id = OUTPUT_TYPE_T;
+		CDBG("vfe_7x_convert: y_phy = 0x%x cbcr_phy = 0x%x\n",
+					pinfo->p0_phy, pinfo->p1_phy);
+	}
+		break;
+
+	case VFE_MSG_STATS_AF:
+	case VFE_MSG_STATS_WE:
+		pinfo->sbuf_phy = *(uint32_t *)data;
+		break;
+
+	default:
+		break;
+	} /* switch */
+}
+
+static void vfe_7x_ops(void *driver_data, unsigned id, size_t len,
+		void (*getevent)(void *ptr, size_t len))
+{
+	uint32_t evt_buf[3];
+	struct msm_vfe_resp *rp;
+	void *data;
+	CDBG("%s:id=%d\n", __func__, id);
+
+	len = (id == VFE_ADSP_EVENT) ? 0 : len;
+	data = resp->vfe_alloc(sizeof(struct msm_vfe_resp) + len,
+		vfe_syncdata,  GFP_ATOMIC);
+
+	if (!data) {
+		pr_err("%s: rp: cannot allocate buffer\n", __func__);
+		return;
+	}
+	rp = (struct msm_vfe_resp *)data;
+	rp->evt_msg.len = len;
+
+	if (id == VFE_ADSP_EVENT) {
+		/* event */
+		rp->type           = VFE_EVENT;
+		rp->evt_msg.type   = MSM_CAMERA_EVT;
+		getevent(evt_buf, sizeof(evt_buf));
+		rp->evt_msg.msg_id = evt_buf[0];
+	CDBG("%s:event:msg_id=%d\n", __func__, rp->evt_msg.msg_id);
+		resp->vfe_resp(rp, MSM_CAM_Q_VFE_EVT, vfe_syncdata,
+		GFP_ATOMIC);
+	} else {
+		/* messages */
+		rp->evt_msg.type   = MSM_CAMERA_MSG;
+		rp->evt_msg.msg_id = id;
+		rp->evt_msg.data = rp + 1;
+		getevent(rp->evt_msg.data, len);
+	CDBG("%s:messages:msg_id=%d\n", __func__, rp->evt_msg.msg_id);
+
+		switch (rp->evt_msg.msg_id) {
+		case MSG_SNAPSHOT:
+			update_axi_qos(MSM_AXI_QOS_PREVIEW);
+			vfe_7x_ops(driver_data, MSG_OUTPUT_S, len, getevent);
+			vfe_7x_ops(driver_data, MSG_OUTPUT_T, len, getevent);
+			rp->type = VFE_MSG_SNAPSHOT;
+			break;
+
+		case MSG_OUTPUT_S:
+			rp->type = VFE_MSG_OUTPUT_S;
+			vfe_7x_convert(&(rp->phy), VFE_MSG_OUTPUT_S,
+				rp->evt_msg.data, &(rp->extdata),
+				&(rp->extlen));
+			break;
+
+		case MSG_OUTPUT_T:
+			rp->type = VFE_MSG_OUTPUT_T;
+			vfe_7x_convert(&(rp->phy), VFE_MSG_OUTPUT_T,
+				rp->evt_msg.data, &(rp->extdata),
+				&(rp->extlen));
+			break;
+
+		case MSG_OUTPUT1:
+		case MSG_OUTPUT2:
+			rp->type = VFE_MSG_OUTPUT_P;
+			vfe_7x_convert(&(rp->phy), VFE_MSG_OUTPUT_P,
+				rp->evt_msg.data, &(rp->extdata),
+				&(rp->extlen));
+			break;
+
+		case MSG_STATS_AF:
+			rp->type = VFE_MSG_STATS_AF;
+			vfe_7x_convert(&(rp->phy), VFE_MSG_STATS_AF,
+					rp->evt_msg.data, NULL, NULL);
+			break;
+
+		case MSG_STATS_WE:
+			rp->type = VFE_MSG_STATS_WE;
+			vfe_7x_convert(&(rp->phy), VFE_MSG_STATS_WE,
+					rp->evt_msg.data, NULL, NULL);
+
+			CDBG("MSG_STATS_WE: phy = 0x%x\n", rp->phy.sbuf_phy);
+			break;
+
+		case MSG_STOP_ACK:
+			rp->type = VFE_MSG_GENERAL;
+			stopevent.state = 1;
+			wake_up(&stopevent.wait);
+			break;
+
+
+		default:
+			rp->type = VFE_MSG_GENERAL;
+			break;
+		}
+		resp->vfe_resp(rp, MSM_CAM_Q_VFE_MSG, vfe_syncdata, GFP_ATOMIC);
+	}
+}
+
+static struct msm_adsp_ops vfe_7x_sync = {
+	.event = vfe_7x_ops,
+};
+
+static int vfe_7x_enable(struct camera_enable_cmd *enable)
+{
+	int rc = -EFAULT;
+
+	if (!strcmp(enable->name, "QCAMTASK"))
+		rc = msm_adsp_enable(qcam_mod);
+	else if (!strcmp(enable->name, "VFETASK")) {
+		rc = msm_adsp_enable(vfe_mod);
+		vfetask_state = 1;
+	}
+
+	if (!cnt) {
+		add_axi_qos();
+		cnt++;
+	}
+	return rc;
+}
+
+static int vfe_7x_disable(struct camera_enable_cmd *enable,
+		struct platform_device *dev __attribute__((unused)))
+{
+	int rc = -EFAULT;
+
+	if (!strcmp(enable->name, "QCAMTASK"))
+		rc = msm_adsp_disable(qcam_mod);
+	else if (!strcmp(enable->name, "VFETASK")) {
+		rc = msm_adsp_disable(vfe_mod);
+		vfetask_state = 0;
+	}
+
+	return rc;
+}
+
+static int vfe_7x_stop(void)
+{
+	int rc = 0;
+	uint32_t stopcmd = VFE_STOP_CMD;
+	rc = msm_adsp_write(vfe_mod, QDSP_CMDQUEUE,
+				&stopcmd, sizeof(uint32_t));
+	if (rc < 0) {
+		CDBG("%s:%d: failed rc = %d \n", __func__, __LINE__, rc);
+		return rc;
+	}
+
+	stopevent.state = 0;
+	rc = wait_event_timeout(stopevent.wait,
+		stopevent.state != 0,
+		msecs_to_jiffies(stopevent.timeout));
+
+	return rc;
+}
+
+static void vfe_7x_release(struct platform_device *pdev)
+{
+	mutex_lock(&vfe_lock);
+	vfe_syncdata = NULL;
+	mutex_unlock(&vfe_lock);
+
+	if (!vfestopped) {
+		CDBG("%s:%d:Calling vfe_7x_stop()\n", __func__, __LINE__);
+		vfe_7x_stop();
+	} else
+		vfestopped = 0;
+
+	msm_adsp_disable(qcam_mod);
+	msm_adsp_disable(vfe_mod);
+	vfetask_state = 0;
+
+	msm_adsp_put(qcam_mod);
+	msm_adsp_put(vfe_mod);
+
+	msm_camio_disable(pdev);
+
+	kfree(extdata);
+	extlen = 0;
+
+	/* Release AXI */
+	release_axi_qos();
+	cnt = 0;
+}
+
+static int vfe_7x_init(struct msm_vfe_callback *presp,
+	struct platform_device *dev)
+{
+	int rc = 0;
+
+	init_waitqueue_head(&stopevent.wait);
+	stopevent.timeout = 200;
+	stopevent.state = 0;
+
+	if (presp && presp->vfe_resp)
+		resp = presp;
+	else
+		return -EFAULT;
+
+	/* Bring up all the required GPIOs and Clocks */
+	rc = msm_camio_enable(dev);
+	if (rc < 0)
+		return rc;
+	msm_camio_camif_pad_reg_reset();
+
+	extlen = sizeof(struct vfe_frame_extra);
+
+	extdata =
+		kmalloc(extlen, GFP_ATOMIC);
+	if (!extdata) {
+		rc = -ENOMEM;
+		goto init_fail;
+	}
+
+	rc = msm_adsp_get("QCAMTASK", &qcam_mod, &vfe_7x_sync, NULL);
+	if (rc) {
+		rc = -EBUSY;
+		goto get_qcam_fail;
+	}
+
+	rc = msm_adsp_get("VFETASK", &vfe_mod, &vfe_7x_sync, NULL);
+	if (rc) {
+		rc = -EBUSY;
+		goto get_vfe_fail;
+	}
+
+	return 0;
+
+get_vfe_fail:
+	msm_adsp_put(qcam_mod);
+get_qcam_fail:
+	kfree(extdata);
+init_fail:
+	extlen = 0;
+	return rc;
+}
+
+static int vfe_7x_config_axi(int mode,
+	struct axidata *ad, struct axiout *ao)
+{
+	struct msm_pmem_region *regptr;
+	unsigned long *bptr;
+	int    cnt;
+
+	int rc = 0;
+
+	if (mode == OUTPUT_1 || mode == OUTPUT_1_AND_2) {
+		regptr = ad->region;
+
+		CDBG("bufnum1 = %d\n", ad->bufnum1);
+		if (mode == OUTPUT_1_AND_2) {
+			paddr_t_y = regptr->paddr + regptr->info.planar0_off;
+			paddr_t_cbcr = regptr->paddr + regptr->info.planar1_off;
+		}
+
+		CDBG("config_axi1: O1, phy = 0x%lx, y_off = %d, cbcr_off =%d\n",
+			regptr->paddr, regptr->info.planar0_off,
+			regptr->info.planar1_off);
+
+		bptr = &ao->output1buffer1_y_phy;
+		for (cnt = 0; cnt < ad->bufnum1; cnt++) {
+			*bptr = regptr->paddr + regptr->info.planar0_off;
+			bptr++;
+			*bptr = regptr->paddr + regptr->info.planar1_off;
+
+			bptr++;
+			regptr++;
+		}
+
+		regptr--;
+		for (cnt = 0; cnt < (8 - ad->bufnum1); cnt++) {
+			*bptr = regptr->paddr + regptr->info.planar0_off;
+			bptr++;
+			*bptr = regptr->paddr + regptr->info.planar1_off;
+			bptr++;
+		}
+	} /* if OUTPUT1 or Both */
+
+	if (mode == OUTPUT_2 || mode == OUTPUT_1_AND_2) {
+		regptr = &(ad->region[ad->bufnum1]);
+
+		CDBG("bufnum2 = %d\n", ad->bufnum2);
+		paddr_s_y = regptr->paddr +  regptr->info.planar0_off;
+		paddr_s_cbcr = regptr->paddr +  regptr->info.planar1_off;
+		CDBG("config_axi2: O2, phy = 0x%lx, y_off = %d, cbcr_off =%d\n",
+			regptr->paddr, regptr->info.planar0_off,
+			regptr->info.planar1_off);
+
+		bptr = &ao->output2buffer1_y_phy;
+		for (cnt = 0; cnt < ad->bufnum2; cnt++) {
+			*bptr = regptr->paddr + regptr->info.planar0_off;
+			bptr++;
+			*bptr = regptr->paddr + regptr->info.planar1_off;
+
+			bptr++;
+			regptr++;
+		}
+
+		regptr--;
+		for (cnt = 0; cnt < (8 - ad->bufnum2); cnt++) {
+			*bptr = regptr->paddr + regptr->info.planar0_off;
+			bptr++;
+			*bptr = regptr->paddr + regptr->info.planar1_off;
+			bptr++;
+		}
+	}
+
+	return rc;
+}
+
+static int vfe_7x_config(struct msm_vfe_cfg_cmd *cmd, void *data)
+{
+	struct msm_pmem_region *regptr;
+	unsigned char buf[256];
+
+	struct vfe_stats_ack sack;
+	struct axidata *axid;
+	uint32_t i, op_mode;
+	uint32_t *_mode;
+
+	struct vfe_stats_we_cfg *scfg = NULL;
+	struct vfe_stats_af_cfg *sfcfg = NULL;
+
+	struct axiout *axio = NULL;
+	void   *cmd_data = NULL;
+	void   *cmd_data_alloc = NULL;
+	long rc = 0;
+	struct msm_vfe_command_7k *vfecmd;
+
+	vfecmd =
+			kmalloc(sizeof(struct msm_vfe_command_7k),
+				GFP_ATOMIC);
+	if (!vfecmd) {
+		pr_err("vfecmd alloc failed!\n");
+		return -ENOMEM;
+	}
+
+	if (cmd->cmd_type != CMD_FRAME_BUF_RELEASE &&
+	    cmd->cmd_type != CMD_STATS_BUF_RELEASE &&
+	    cmd->cmd_type != CMD_STATS_AF_BUF_RELEASE) {
+		if (copy_from_user(vfecmd,
+				(void __user *)(cmd->value),
+				sizeof(struct msm_vfe_command_7k))) {
+			rc = -EFAULT;
+			goto config_failure;
+		}
+	}
+
+	switch (cmd->cmd_type) {
+	case CMD_STATS_AEC_AWB_ENABLE:
+	case CMD_STATS_AXI_CFG: {
+		axid = data;
+		if (!axid) {
+			rc = -EFAULT;
+			goto config_failure;
+		}
+
+		scfg =
+			kmalloc(sizeof(struct vfe_stats_we_cfg),
+				GFP_ATOMIC);
+		if (!scfg) {
+			rc = -ENOMEM;
+			goto config_failure;
+		}
+
+		if (copy_from_user(scfg,
+					(void __user *)(vfecmd->value),
+					vfecmd->length)) {
+
+			rc = -EFAULT;
+			goto config_done;
+		}
+
+		CDBG("STATS_ENABLE: bufnum = %d, enabling = %d\n",
+			axid->bufnum1, scfg->wb_expstatsenable);
+
+		if (axid->bufnum1 > 0) {
+			regptr = axid->region;
+
+			for (i = 0; i < axid->bufnum1; i++) {
+
+				CDBG("STATS_ENABLE, phy = 0x%lx\n",
+					regptr->paddr);
+
+				scfg->wb_expstatoutputbuffer[i] =
+					(void *)regptr->paddr;
+				regptr++;
+			}
+
+			cmd_data = scfg;
+
+		} else {
+			rc = -EINVAL;
+			goto config_done;
+		}
+	}
+		break;
+
+	case CMD_STATS_AF_ENABLE:
+	case CMD_STATS_AF_AXI_CFG: {
+		axid = data;
+		if (!axid) {
+			rc = -EFAULT;
+			goto config_failure;
+		}
+
+		sfcfg =
+			kmalloc(sizeof(struct vfe_stats_af_cfg),
+				GFP_ATOMIC);
+
+		if (!sfcfg) {
+			rc = -ENOMEM;
+			goto config_failure;
+		}
+
+		if (copy_from_user(sfcfg,
+					(void __user *)(vfecmd->value),
+					vfecmd->length)) {
+
+			rc = -EFAULT;
+			goto config_done;
+		}
+
+		CDBG("AF_ENABLE: bufnum = %d, enabling = %d\n",
+			axid->bufnum1, sfcfg->af_enable);
+
+		if (axid->bufnum1 > 0) {
+			regptr = &axid->region[0];
+
+			for (i = 0; i < axid->bufnum1; i++) {
+
+				CDBG("STATS_ENABLE, phy = 0x%lx\n",
+					regptr->paddr);
+
+				sfcfg->af_outbuf[i] =
+					(void *)regptr->paddr;
+
+				regptr++;
+			}
+
+			cmd_data = sfcfg;
+
+		} else {
+			rc = -EINVAL;
+			goto config_done;
+		}
+	}
+		break;
+
+	case CMD_FRAME_BUF_RELEASE: {
+		struct msm_frame *b;
+		unsigned long p;
+		struct vfe_outputack fack;
+		if (!data)  {
+			rc = -EFAULT;
+			goto config_failure;
+		}
+
+		b = (struct msm_frame *)(cmd->value);
+		p = *(unsigned long *)data;
+
+		fack.header = VFE_FRAME_ACK;
+
+		fack.output2newybufferaddress =
+			(void *)(p + b->planar0_off);
+
+		fack.output2newcbcrbufferaddress =
+			(void *)(p + b->planar1_off);
+
+		vfecmd->queue = QDSP_CMDQUEUE;
+		vfecmd->length = sizeof(struct vfe_outputack);
+		cmd_data = &fack;
+	}
+		break;
+
+	case CMD_SNAP_BUF_RELEASE:
+		break;
+
+	case CMD_STATS_BUF_RELEASE: {
+		CDBG("vfe_7x_config: CMD_STATS_BUF_RELEASE\n");
+		if (!data) {
+			rc = -EFAULT;
+			goto config_failure;
+		}
+
+		sack.header = STATS_WE_ACK;
+		sack.bufaddr = (void *)*(uint32_t *)data;
+
+		vfecmd->queue  = QDSP_CMDQUEUE;
+		vfecmd->length = sizeof(struct vfe_stats_ack);
+		cmd_data = &sack;
+	}
+		break;
+
+	case CMD_STATS_AF_BUF_RELEASE: {
+		CDBG("vfe_7x_config: CMD_STATS_AF_BUF_RELEASE\n");
+		if (!data) {
+			rc = -EFAULT;
+			goto config_failure;
+		}
+
+		sack.header = STATS_AF_ACK;
+		sack.bufaddr = (void *)*(uint32_t *)data;
+
+		vfecmd->queue  = QDSP_CMDQUEUE;
+		vfecmd->length = sizeof(struct vfe_stats_ack);
+		cmd_data = &sack;
+	}
+		break;
+
+	case CMD_GENERAL:
+	case CMD_STATS_DISABLE: {
+		if (vfecmd->length > 256) {
+			cmd_data_alloc =
+			cmd_data = kmalloc(vfecmd->length, GFP_ATOMIC);
+			if (!cmd_data) {
+				rc = -ENOMEM;
+				goto config_failure;
+			}
+		} else
+			cmd_data = buf;
+
+		if (copy_from_user(cmd_data,
+					(void __user *)(vfecmd->value),
+					vfecmd->length)) {
+
+			rc = -EFAULT;
+			goto config_done;
+		}
+
+		if (vfecmd->queue == QDSP_CMDQUEUE) {
+			switch (*(uint32_t *)cmd_data) {
+			case VFE_RESET_CMD:
+				msm_camio_vfe_blk_reset();
+				vfestopped = 0;
+				break;
+
+			case VFE_START_CMD:
+				_mode = (uint32_t *)cmd_data;
+				op_mode = *(++_mode);
+				if (op_mode & SNAPSHOT_MASK_MODE) {
+					/* request AXI bus for snapshot */
+					if (update_axi_qos(MSM_AXI_QOS_SNAPSHOT)
+						< 0) {
+						rc = -EFAULT;
+						goto config_failure;
+					}
+				} else {
+					/* request AXI bus for snapshot */
+					if (update_axi_qos(MSM_AXI_QOS_PREVIEW)
+						< 0) {
+						rc = -EFAULT;
+						goto config_failure;
+					}
+				}
+				msm_camio_camif_pad_reg_reset_2();
+				vfestopped = 0;
+				break;
+
+			case VFE_STOP_CMD:
+				vfestopped = 1;
+				goto config_send;
+
+			default:
+				break;
+			}
+		} /* QDSP_CMDQUEUE */
+	}
+		break;
+	case CMD_AXI_CFG_PREVIEW:
+	case CMD_RAW_PICT_AXI_CFG: {
+		axid = data;
+		if (!axid) {
+			rc = -EFAULT;
+			goto config_failure;
+		}
+
+		axio = kmalloc(sizeof(struct axiout), GFP_ATOMIC);
+		if (!axio) {
+			rc = -ENOMEM;
+			goto config_failure;
+		}
+
+		if (copy_from_user(axio, (void __user *)(vfecmd->value),
+					sizeof(struct axiout))) {
+			rc = -EFAULT;
+			goto config_done;
+		}
+
+		vfe_7x_config_axi(OUTPUT_2, axid, axio);
+		cmd_data = axio;
+	}
+		break;
+
+	case CMD_AXI_CFG_SNAP: {
+		axid = data;
+		if (!axid) {
+			rc = -EFAULT;
+			goto config_failure;
+		}
+
+		axio = kmalloc(sizeof(struct axiout), GFP_ATOMIC);
+		if (!axio) {
+			rc = -ENOMEM;
+			goto config_failure;
+		}
+
+		if (copy_from_user(axio, (void __user *)(vfecmd->value),
+					sizeof(struct axiout))) {
+			rc = -EFAULT;
+			goto config_done;
+		}
+
+		vfe_7x_config_axi(OUTPUT_1_AND_2, axid, axio);
+
+		cmd_data = axio;
+	}
+		break;
+
+	default:
+		break;
+	} /* switch */
+
+	if (vfestopped)
+		goto config_done;
+
+config_send:
+	CDBG("send adsp command = %d\n", *(uint32_t *)cmd_data);
+	if (vfetask_state)
+		rc = msm_adsp_write(vfe_mod, vfecmd->queue,
+					cmd_data, vfecmd->length);
+config_done:
+	if (cmd_data_alloc != NULL)
+		kfree(cmd_data_alloc);
+
+config_failure:
+	kfree(scfg);
+	kfree(axio);
+	kfree(vfecmd);
+	return rc;
+}
+
+void msm_camvfe_fn_init(struct msm_camvfe_fn *fptr, void *data)
+{
+	mutex_init(&vfe_lock);
+	fptr->vfe_init    = vfe_7x_init;
+	fptr->vfe_enable  = vfe_7x_enable;
+	fptr->vfe_config  = vfe_7x_config;
+	fptr->vfe_disable = vfe_7x_disable;
+	fptr->vfe_release = vfe_7x_release;
+	vfe_syncdata = data;
+}
+
+void msm_camvpe_fn_init(struct msm_camvpe_fn *fptr, void *data)
+{
+	fptr->vpe_reg		= NULL;
+	fptr->send_frame_to_vpe	= NULL;
+	fptr->vpe_config	= NULL;
+	fptr->vpe_cfg_update	= NULL;
+	fptr->dis		= NULL;
+}
diff --git a/drivers/media/video/msm/msm_vfe7x.h b/drivers/media/video/msm/msm_vfe7x.h
new file mode 100644
index 0000000..dd3571f
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe7x.h
@@ -0,0 +1,265 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __MSM_VFE7X_H__
+#define __MSM_VFE7X_H__
+#include <media/msm_camera.h>
+#include <mach/camera.h>
+
+struct vfe_frame_extra {
+	uint32_t  bl_evencol;
+	uint32_t  bl_oddcol;
+	uint16_t  g_def_p_cnt;
+	uint16_t  r_b_def_p_cnt;
+};
+
+struct vfe_endframe {
+	uint32_t      y_address;
+	uint32_t      cbcr_address;
+
+	unsigned int  blacklevelevencolumn:23;
+	uint16_t      reserved1:9;
+	unsigned int  blackleveloddcolumn:23;
+	uint16_t      reserved2:9;
+
+	uint16_t      greendefectpixelcount:8;
+	uint16_t      reserved3:8;
+	uint16_t      redbluedefectpixelcount:8;
+	uint16_t      reserved4:8;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_outputack {
+	uint32_t  header;
+	void      *output2newybufferaddress;
+	void      *output2newcbcrbufferaddress;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_stats_ack {
+	uint32_t header;
+	/* MUST BE 64 bit ALIGNED */
+	void     *bufaddr;
+} __attribute__((packed, aligned(4)));
+
+/* AXI Output Config Command sent to DSP */
+struct axiout {
+	uint32_t            cmdheader:32;
+	int 		    outputmode:3;
+	uint8_t             format:2;
+	uint32_t            /* reserved */ : 27;
+
+	/* AXI Output 1 Y Configuration, Part 1 */
+	uint32_t            out1yimageheight:12;
+	uint32_t            /* reserved */ : 4;
+	uint32_t            out1yimagewidthin64bitwords:10;
+	uint32_t            /* reserved */ : 6;
+
+	/* AXI Output 1 Y Configuration, Part 2 */
+	uint8_t             out1yburstlen:2;
+	uint32_t            out1ynumrows:12;
+	uint32_t            out1yrowincin64bitincs:12;
+	uint32_t            /* reserved */ : 6;
+
+	/* AXI Output 1 CbCr Configuration, Part 1 */
+	uint32_t            out1cbcrimageheight:12;
+	uint32_t            /* reserved */ : 4;
+	uint32_t            out1cbcrimagewidthin64bitwords:10;
+	uint32_t            /* reserved */ : 6;
+
+	/* AXI Output 1 CbCr Configuration, Part 2 */
+	uint8_t             out1cbcrburstlen:2;
+	uint32_t            out1cbcrnumrows:12;
+	uint32_t            out1cbcrrowincin64bitincs:12;
+	uint32_t            /* reserved */ : 6;
+
+	/* AXI Output 2 Y Configuration, Part 1 */
+	uint32_t            out2yimageheight:12;
+	uint32_t            /* reserved */ : 4;
+	uint32_t            out2yimagewidthin64bitwords:10;
+	uint32_t            /* reserved */ : 6;
+
+	/* AXI Output 2 Y Configuration, Part 2 */
+	uint8_t             out2yburstlen:2;
+	uint32_t            out2ynumrows:12;
+	uint32_t            out2yrowincin64bitincs:12;
+	uint32_t            /* reserved */ : 6;
+
+	/* AXI Output 2 CbCr Configuration, Part 1 */
+	uint32_t            out2cbcrimageheight:12;
+	uint32_t            /* reserved */ : 4;
+	uint32_t            out2cbcrimagewidtein64bitwords:10;
+	uint32_t            /* reserved */ : 6;
+
+	/* AXI Output 2 CbCr Configuration, Part 2 */
+	uint8_t             out2cbcrburstlen:2;
+	uint32_t            out2cbcrnumrows:12;
+	uint32_t            out2cbcrrowincin64bitincs:12;
+	uint32_t            /* reserved */ : 6;
+
+	/* Address configuration:
+	 * output1 phisycal address */
+	unsigned long   output1buffer1_y_phy;
+	unsigned long   output1buffer1_cbcr_phy;
+	unsigned long   output1buffer2_y_phy;
+	unsigned long   output1buffer2_cbcr_phy;
+	unsigned long   output1buffer3_y_phy;
+	unsigned long   output1buffer3_cbcr_phy;
+	unsigned long   output1buffer4_y_phy;
+	unsigned long   output1buffer4_cbcr_phy;
+	unsigned long   output1buffer5_y_phy;
+	unsigned long   output1buffer5_cbcr_phy;
+	unsigned long   output1buffer6_y_phy;
+	unsigned long   output1buffer6_cbcr_phy;
+	unsigned long   output1buffer7_y_phy;
+	unsigned long   output1buffer7_cbcr_phy;
+	unsigned long   output1buffer8_y_phy;
+	unsigned long   output1buffer8_cbcr_phy;
+
+	/* output2 phisycal address */
+	unsigned long   output2buffer1_y_phy;
+	unsigned long   output2buffer1_cbcr_phy;
+	unsigned long   output2buffer2_y_phy;
+	unsigned long   output2buffer2_cbcr_phy;
+	unsigned long   output2buffer3_y_phy;
+	unsigned long   output2buffer3_cbcr_phy;
+	unsigned long   output2buffer4_y_phy;
+	unsigned long   output2buffer4_cbcr_phy;
+	unsigned long   output2buffer5_y_phy;
+	unsigned long   output2buffer5_cbcr_phy;
+	unsigned long   output2buffer6_y_phy;
+	unsigned long   output2buffer6_cbcr_phy;
+	unsigned long   output2buffer7_y_phy;
+	unsigned long   output2buffer7_cbcr_phy;
+	unsigned long   output2buffer8_y_phy;
+	unsigned long   output2buffer8_cbcr_phy;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_stats_we_cfg {
+	uint32_t       header;
+
+	/* White Balance/Exposure Statistic Selection */
+	uint8_t        wb_expstatsenable:1;
+	uint8_t        wb_expstatbuspriorityselection:1;
+	unsigned int   wb_expstatbuspriorityvalue:4;
+	unsigned int   /* reserved */ : 26;
+
+	/* White Balance/Exposure Statistic Configuration, Part 1 */
+	uint8_t        exposurestatregions:1;
+	uint8_t        exposurestatsubregions:1;
+	unsigned int   /* reserved */ : 14;
+
+	unsigned int   whitebalanceminimumy:8;
+	unsigned int   whitebalancemaximumy:8;
+
+	/* White Balance/Exposure Statistic Configuration, Part 2 */
+	uint8_t wb_expstatslopeofneutralregionline[
+		NUM_WB_EXP_NEUTRAL_REGION_LINES];
+
+	/* White Balance/Exposure Statistic Configuration, Part 3 */
+	unsigned int   wb_expstatcrinterceptofneutralregionline2:12;
+	unsigned int   /* reserved */ : 4;
+	unsigned int   wb_expstatcbinterceptofneutralreginnline1:12;
+	unsigned int    /* reserved */ : 4;
+
+	/* White Balance/Exposure Statistic Configuration, Part 4 */
+	unsigned int   wb_expstatcrinterceptofneutralregionline4:12;
+	unsigned int   /* reserved */ : 4;
+	unsigned int   wb_expstatcbinterceptofneutralregionline3:12;
+	unsigned int   /* reserved */ : 4;
+
+	/* White Balance/Exposure Statistic Output Buffer Header */
+	unsigned int   wb_expmetricheaderpattern:8;
+	unsigned int   /* reserved */ : 24;
+
+	/* White Balance/Exposure Statistic Output Buffers-MUST
+	* BE 64 bit ALIGNED */
+	void  *wb_expstatoutputbuffer[NUM_WB_EXP_STAT_OUTPUT_BUFFERS];
+} __attribute__((packed, aligned(4)));
+
+struct vfe_stats_af_cfg {
+	uint32_t header;
+
+	/* Autofocus Statistic Selection */
+	uint8_t       af_enable:1;
+	uint8_t       af_busprioritysel:1;
+	unsigned int  af_buspriorityval:4;
+	unsigned int  /* reserved */ : 26;
+
+	/* Autofocus Statistic Configuration, Part 1 */
+	unsigned int  af_singlewinvoffset:12;
+	unsigned int  /* reserved */ : 4;
+	unsigned int  af_singlewinhoffset:12;
+	unsigned int  /* reserved */ : 3;
+	uint8_t       af_winmode:1;
+
+	/* Autofocus Statistic Configuration, Part 2 */
+	unsigned int  af_singglewinvh:11;
+	unsigned int  /* reserved */ : 5;
+	unsigned int  af_singlewinhw:11;
+	unsigned int  /* reserved */ : 5;
+
+	/* Autofocus Statistic Configuration, Parts 3-6 */
+	uint8_t       af_multiwingrid[NUM_AUTOFOCUS_MULTI_WINDOW_GRIDS];
+
+	/* Autofocus Statistic Configuration, Part 7 */
+	signed int    af_metrichpfcoefa00:5;
+	signed int    af_metrichpfcoefa04:5;
+	unsigned int  af_metricmaxval:11;
+	uint8_t       af_metricsel:1;
+	unsigned int  /* reserved */ : 10;
+
+	/* Autofocus Statistic Configuration, Part 8 */
+	signed int    af_metrichpfcoefa20:5;
+	signed int    af_metrichpfcoefa21:5;
+	signed int    af_metrichpfcoefa22:5;
+	signed int    af_metrichpfcoefa23:5;
+	signed int    af_metrichpfcoefa24:5;
+	unsigned int  /* reserved */ : 7;
+
+	/* Autofocus Statistic Output Buffer Header */
+	unsigned int  af_metrichp:8;
+	unsigned int  /* reserved */ : 24;
+
+	/* Autofocus Statistic Output Buffers - MUST BE 64 bit ALIGNED!!! */
+	void *af_outbuf[NUM_AF_STAT_OUTPUT_BUFFERS];
+} __attribute__((packed, aligned(4))); /* VFE_StatsAutofocusConfigCmdType */
+
+struct msm_camera_frame_msg {
+	unsigned long   output_y_address;
+	unsigned long   output_cbcr_address;
+
+	unsigned int    blacklevelevenColumn:23;
+	uint16_t        reserved1:9;
+	unsigned int    blackleveloddColumn:23;
+	uint16_t        reserved2:9;
+
+	uint16_t        greendefectpixelcount:8;
+	uint16_t        reserved3:8;
+	uint16_t        redbluedefectpixelcount:8;
+	uint16_t        reserved4:8;
+} __attribute__((packed, aligned(4)));
+
+/* New one for 7k */
+struct msm_vfe_command_7k {
+	uint16_t queue;
+	uint16_t length;
+	void     *value;
+};
+
+struct stop_event {
+  wait_queue_head_t wait;
+	int state;
+  int timeout;
+};
+
+
+#endif /* __MSM_VFE7X_H__ */
diff --git a/drivers/media/video/msm/msm_vfe7x27a.c b/drivers/media/video/msm/msm_vfe7x27a.c
new file mode 100644
index 0000000..825b7cb
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe7x27a.c
@@ -0,0 +1,745 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/msm_adsp.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/android_pmem.h>
+#include <linux/slab.h>
+#include <linux/pm_qos.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <mach/msm_adsp.h>
+#include <mach/clk.h>
+#include <mach/camera.h>
+#include "msm_vfe7x27a.h"
+
+#define QDSP_CMDQUEUE 25
+
+#define VFE_RESET_CMD 0
+#define VFE_START_CMD 1
+#define VFE_STOP_CMD  2
+#define VFE_FRAME_ACK 20
+#define STATS_AF_ACK  21
+#define STATS_WE_ACK  22
+
+#define MSG_STOP_ACK  1
+#define MSG_SNAPSHOT  2
+#define MSG_OUTPUT1   6
+#define MSG_OUTPUT2   7
+#define MSG_STATS_AF  8
+#define MSG_STATS_WE  9
+#define MSG_OUTPUT_S  23
+#define MSG_OUTPUT_T  22
+#define MSG_SOF       15
+
+#define VFE_ADSP_EVENT 0xFFFF
+#define SNAPSHOT_MASK_MODE 0x00000002
+#define MSM_AXI_QOS_PREVIEW	122000
+#define MSM_AXI_QOS_SNAPSHOT	192000
+
+
+static struct msm_adsp_module *qcam_mod;
+static struct msm_adsp_module *vfe_mod;
+static struct msm_vfe_callback *resp;
+static void *extdata;
+static uint32_t extlen;
+
+struct mutex vfe_lock;
+static void     *vfe_syncdata;
+static uint8_t vfestopped;
+
+static struct stop_event stopevent;
+
+unsigned long paddr_s_y;
+unsigned long paddr_s_cbcr;
+unsigned long paddr_t_y;
+unsigned long paddr_t_cbcr;
+static uint32_t op_mode;
+
+static void vfe_7x_convert(struct msm_vfe_phy_info *pinfo,
+		enum vfe_resp_msg type,
+		void *data, void **ext, int32_t *elen)
+{
+	switch (type) {
+	case VFE_MSG_OUTPUT_P: {
+		pinfo->p0_phy = ((struct vfe_endframe *)data)->y_address;
+		pinfo->p1_phy =
+			((struct vfe_endframe *)data)->cbcr_address;
+		pinfo->p2_phy = pinfo->p0_phy;
+		pinfo->output_id = OUTPUT_TYPE_P;
+
+		CDBG("vfe_7x_convert, y_phy = 0x%x, cbcr_phy = 0x%x\n",
+				 pinfo->p0_phy, pinfo->p1_phy);
+
+		memcpy(((struct vfe_frame_extra *)extdata),
+			&((struct vfe_endframe *)data)->extra,
+			sizeof(struct vfe_frame_extra));
+
+		*ext  = extdata;
+		*elen = extlen;
+		pinfo->frame_id =
+				((struct vfe_frame_extra *)extdata)->frame_id;
+	}
+		break;
+	case VFE_MSG_OUTPUT_S: {
+		pinfo->p0_phy = paddr_s_y;
+		pinfo->p1_phy = paddr_s_cbcr;
+		pinfo->p2_phy = pinfo->p0_phy;
+		pinfo->output_id = OUTPUT_TYPE_S;
+		CDBG("vfe_7x_convert: y_phy = 0x%x cbcr_phy = 0x%x\n",
+					pinfo->p0_phy, pinfo->p1_phy);
+	}
+		break;
+	case VFE_MSG_OUTPUT_T: {
+		pinfo->p0_phy = paddr_t_y;
+		pinfo->p1_phy = paddr_t_cbcr;
+		pinfo->p2_phy = pinfo->p0_phy;
+		pinfo->output_id = OUTPUT_TYPE_T;
+		CDBG("vfe_7x_convert: y_phy = 0x%x cbcr_phy = 0x%x\n",
+					pinfo->p0_phy, pinfo->p1_phy);
+	}
+		break;
+	case VFE_MSG_STATS_AF:
+	case VFE_MSG_STATS_WE:
+		pinfo->sbuf_phy = *(uint32_t *)data;
+		pinfo->frame_id = *(((uint32_t *)data) + 1);
+		CDBG("frame id = %d\n", pinfo->frame_id);
+		break;
+	default:
+		break;
+	}
+}
+
+static void vfe_7x_ops(void *driver_data, unsigned id, size_t len,
+		void (*getevent)(void *ptr, size_t len))
+{
+	uint32_t evt_buf[3];
+	struct msm_vfe_resp *rp;
+	void *data;
+	CDBG("%s:id=%d\n", __func__, id);
+
+	len = (id == VFE_ADSP_EVENT) ? 0 : len;
+	data = resp->vfe_alloc(sizeof(struct msm_vfe_resp) + len,
+		vfe_syncdata,  GFP_ATOMIC);
+
+	if (!data) {
+		pr_err("%s: rp: cannot allocate buffer\n", __func__);
+		return;
+	}
+	rp = data;
+	rp->evt_msg.len = len;
+
+	if (id == VFE_ADSP_EVENT) {
+		/* event */
+		rp->type           = VFE_EVENT;
+		rp->evt_msg.type   = MSM_CAMERA_EVT;
+		getevent(evt_buf, sizeof(evt_buf));
+		rp->evt_msg.msg_id = evt_buf[0];
+		CDBG("%s:event:msg_id=%d\n", __func__, rp->evt_msg.msg_id);
+		resp->vfe_resp(rp, MSM_CAM_Q_VFE_EVT, vfe_syncdata,
+		GFP_ATOMIC);
+	} else {
+		/* messages */
+		rp->evt_msg.type   = MSM_CAMERA_MSG;
+		rp->evt_msg.msg_id = id;
+		rp->evt_msg.data = rp + 1;
+		getevent(rp->evt_msg.data, len);
+		CDBG("%s:messages:msg_id=%d\n", __func__, rp->evt_msg.msg_id);
+
+		switch (rp->evt_msg.msg_id) {
+		case MSG_SNAPSHOT:
+			msm_camio_set_perf_lvl(S_PREVIEW);
+			vfe_7x_ops(driver_data, MSG_OUTPUT_S, len, getevent);
+			vfe_7x_ops(driver_data, MSG_OUTPUT_T, len, getevent);
+			rp->type = VFE_MSG_SNAPSHOT;
+			break;
+		case MSG_OUTPUT_S:
+			rp->type = VFE_MSG_OUTPUT_S;
+			vfe_7x_convert(&(rp->phy), VFE_MSG_OUTPUT_S,
+					rp->evt_msg.data, &(rp->extdata),
+					&(rp->extlen));
+			break;
+		case MSG_OUTPUT_T:
+			rp->type = VFE_MSG_OUTPUT_T;
+			vfe_7x_convert(&(rp->phy), VFE_MSG_OUTPUT_T,
+					rp->evt_msg.data, &(rp->extdata),
+					&(rp->extlen));
+			break;
+		case MSG_OUTPUT1:
+		case MSG_OUTPUT2:
+			if (op_mode & SNAPSHOT_MASK_MODE) {
+				resp->vfe_free(data);
+				return;
+			}
+			rp->type = VFE_MSG_OUTPUT_P;
+			vfe_7x_convert(&(rp->phy), VFE_MSG_OUTPUT_P,
+				rp->evt_msg.data, &(rp->extdata),
+				&(rp->extlen));
+			break;
+		case MSG_STATS_AF:
+			rp->type = VFE_MSG_STATS_AF;
+			vfe_7x_convert(&(rp->phy), VFE_MSG_STATS_AF,
+					rp->evt_msg.data, NULL, NULL);
+			break;
+		case MSG_STATS_WE:
+			rp->type = VFE_MSG_STATS_WE;
+			vfe_7x_convert(&(rp->phy), VFE_MSG_STATS_WE,
+					rp->evt_msg.data, NULL, NULL);
+
+			CDBG("MSG_STATS_WE: phy = 0x%x\n", rp->phy.sbuf_phy);
+			break;
+		case MSG_STOP_ACK:
+			rp->type = VFE_MSG_GENERAL;
+			stopevent.state = 1;
+			wake_up(&stopevent.wait);
+			break;
+		default:
+			rp->type = VFE_MSG_GENERAL;
+			break;
+		}
+		if (id != MSG_SOF)
+			resp->vfe_resp(rp, MSM_CAM_Q_VFE_MSG,
+					vfe_syncdata, GFP_ATOMIC);
+	}
+}
+
+static struct msm_adsp_ops vfe_7x_sync = {
+	.event = vfe_7x_ops,
+};
+
+static int vfe_7x_enable(struct camera_enable_cmd *enable)
+{
+	int rc = -EFAULT;
+	static int cnt;
+
+	if (!strcmp(enable->name, "QCAMTASK"))
+		rc = msm_adsp_enable(qcam_mod);
+	else if (!strcmp(enable->name, "VFETASK"))
+		rc = msm_adsp_enable(vfe_mod);
+
+	if (!cnt) {
+		msm_camio_set_perf_lvl(S_INIT);
+		cnt++;
+	}
+	return rc;
+}
+
+static int vfe_7x_disable(struct camera_enable_cmd *enable,
+		struct platform_device *dev __attribute__((unused)))
+{
+	int rc = -EFAULT;
+
+	if (!strcmp(enable->name, "QCAMTASK"))
+		rc = msm_adsp_disable(qcam_mod);
+	else if (!strcmp(enable->name, "VFETASK"))
+		rc = msm_adsp_disable(vfe_mod);
+
+	return rc;
+}
+
+static int vfe_7x_stop(void)
+{
+	int rc = 0;
+	uint32_t stopcmd = VFE_STOP_CMD;
+	rc = msm_adsp_write(vfe_mod, QDSP_CMDQUEUE,
+				&stopcmd, sizeof(uint32_t));
+	if (rc < 0) {
+		CDBG("%s:%d: failed rc = %d\n", __func__, __LINE__, rc);
+		return rc;
+	}
+
+	stopevent.state = 0;
+	rc = wait_event_timeout(stopevent.wait,
+		stopevent.state != 0,
+		msecs_to_jiffies(stopevent.timeout));
+
+	return rc;
+}
+
+static void vfe_7x_release(struct platform_device *pdev)
+{
+	mutex_lock(&vfe_lock);
+	vfe_syncdata = NULL;
+	mutex_unlock(&vfe_lock);
+
+	if (!vfestopped) {
+		CDBG("%s:%d:Calling vfe_7x_stop()\n", __func__, __LINE__);
+		vfe_7x_stop();
+	} else
+		vfestopped = 0;
+
+	msm_adsp_disable(qcam_mod);
+	msm_adsp_disable(vfe_mod);
+
+	msm_adsp_put(qcam_mod);
+	msm_adsp_put(vfe_mod);
+
+	msm_camio_disable(pdev);
+
+	kfree(extdata);
+	extlen = 0;
+
+	msm_camio_set_perf_lvl(S_EXIT);
+}
+
+static int vfe_7x_init(struct msm_vfe_callback *presp,
+	struct platform_device *dev)
+{
+	int rc = 0;
+
+	init_waitqueue_head(&stopevent.wait);
+	stopevent.timeout = 200;
+	stopevent.state = 0;
+
+	if (presp && presp->vfe_resp)
+		resp = presp;
+	else
+		return -EFAULT;
+
+	/* Bring up all the required GPIOs and Clocks */
+	rc = msm_camio_enable(dev);
+	if (rc < 0)
+		return rc;
+
+	extlen = sizeof(struct vfe_frame_extra);
+
+	extdata = kmalloc(extlen, GFP_ATOMIC);
+	if (!extdata) {
+		rc = -ENOMEM;
+		goto init_fail;
+	}
+
+	rc = msm_adsp_get("QCAMTASK", &qcam_mod, &vfe_7x_sync, NULL);
+	if (rc) {
+		rc = -EBUSY;
+		goto get_qcam_fail;
+	}
+
+	rc = msm_adsp_get("VFETASK", &vfe_mod, &vfe_7x_sync, NULL);
+	if (rc) {
+		rc = -EBUSY;
+		goto get_vfe_fail;
+	}
+
+	return 0;
+
+get_vfe_fail:
+	msm_adsp_put(qcam_mod);
+get_qcam_fail:
+	kfree(extdata);
+init_fail:
+	extlen = 0;
+	return rc;
+}
+
+static int vfe_7x_config_axi(int mode,
+	struct axidata *ad, struct axiout *ao)
+{
+	struct msm_pmem_region *regptr;
+	unsigned long *bptr;
+	int    cnt;
+
+	int rc = 0;
+
+	if (mode == OUTPUT_1 || mode == OUTPUT_1_AND_2) {
+		regptr = ad->region;
+
+		CDBG("bufnum1 = %d\n", ad->bufnum1);
+		if (mode == OUTPUT_1_AND_2) {
+			paddr_t_y = regptr->paddr + regptr->info.planar0_off;
+			paddr_t_cbcr = regptr->paddr +
+			regptr->info.planar1_off;
+		}
+
+		CDBG("config_axi1: O1, phy = 0x%lx, y_off = %d, cbcr_off =%d\n",
+			regptr->paddr, regptr->info.planar0_off,
+			regptr->info.planar1_off);
+
+		bptr = &ao->output1buffer1_y_phy;
+		for (cnt = 0; cnt < ad->bufnum1; cnt++) {
+			*bptr = regptr->paddr + regptr->info.planar0_off;
+			bptr++;
+			*bptr = regptr->paddr + regptr->info.planar1_off;
+
+			bptr++;
+			regptr++;
+		}
+
+		regptr--;
+		for (cnt = 0; cnt < (8 - ad->bufnum1); cnt++) {
+			*bptr = regptr->paddr + regptr->info.planar0_off;
+			bptr++;
+			*bptr = regptr->paddr + regptr->info.planar1_off;
+			bptr++;
+		}
+	}
+
+	if (mode == OUTPUT_2 || mode == OUTPUT_1_AND_2) {
+		regptr = &(ad->region[ad->bufnum1]);
+
+		CDBG("bufnum2 = %d\n", ad->bufnum2);
+		paddr_s_y = regptr->paddr +  regptr->info.planar0_off;
+		paddr_s_cbcr = regptr->paddr +  regptr->info.planar1_off;
+
+		CDBG("config_axi2: O2, phy = 0x%lx, y_off = %d, cbcr_off =%d\n",
+			regptr->paddr, regptr->info.planar0_off,
+			regptr->info.planar1_off);
+
+		bptr = &ao->output2buffer1_y_phy;
+		for (cnt = 0; cnt < ad->bufnum2; cnt++) {
+			*bptr = regptr->paddr + regptr->info.planar0_off;
+			bptr++;
+			*bptr = regptr->paddr + regptr->info.planar1_off;
+
+			bptr++;
+			regptr++;
+		}
+
+		regptr--;
+		for (cnt = 0; cnt < (8 - ad->bufnum2); cnt++) {
+			*bptr = regptr->paddr + regptr->info.planar0_off;
+			bptr++;
+			*bptr = regptr->paddr + regptr->info.planar1_off;
+			bptr++;
+		}
+	}
+
+	return rc;
+}
+
+static int vfe_7x_config(struct msm_vfe_cfg_cmd *cmd, void *data)
+{
+	struct msm_pmem_region *regptr;
+	unsigned char buf[256];
+
+	struct vfe_stats_ack sack;
+	struct axidata *axid;
+	uint32_t i;
+	uint32_t *_mode;
+
+	struct vfe_stats_we_cfg *scfg = NULL;
+	struct vfe_stats_af_cfg *sfcfg = NULL;
+
+	struct axiout *axio = NULL;
+	void   *cmd_data = NULL;
+	void   *cmd_data_alloc = NULL;
+	long rc = 0;
+	struct msm_vfe_command_7k *vfecmd;
+
+	vfecmd = kmalloc(sizeof(struct msm_vfe_command_7k), GFP_ATOMIC);
+	if (!vfecmd) {
+		pr_err("vfecmd alloc failed!\n");
+		return -ENOMEM;
+	}
+
+	if (cmd->cmd_type != CMD_FRAME_BUF_RELEASE &&
+	    cmd->cmd_type != CMD_STATS_BUF_RELEASE &&
+	    cmd->cmd_type != CMD_STATS_AF_BUF_RELEASE) {
+		if (copy_from_user(vfecmd,
+				(void __user *)(cmd->value),
+				sizeof(struct msm_vfe_command_7k))) {
+			rc = -EFAULT;
+			goto config_failure;
+		}
+	}
+
+	switch (cmd->cmd_type) {
+	case CMD_STATS_AEC_AWB_ENABLE:
+	case CMD_STATS_AXI_CFG: {
+		axid = data;
+		if (!axid) {
+			rc = -EFAULT;
+			goto config_failure;
+		}
+
+		scfg =
+			kmalloc(sizeof(struct vfe_stats_we_cfg),
+				GFP_ATOMIC);
+		if (!scfg) {
+			rc = -ENOMEM;
+			goto config_failure;
+		}
+
+		if (copy_from_user(scfg,
+					(void __user *)(vfecmd->value),
+					vfecmd->length)) {
+
+			rc = -EFAULT;
+			goto config_done;
+		}
+
+		CDBG("STATS_ENABLE: bufnum = %d, enabling = %d\n",
+			axid->bufnum1, scfg->wb_expstatsenable);
+
+		if (axid->bufnum1 > 0) {
+			regptr = axid->region;
+
+			for (i = 0; i < axid->bufnum1; i++) {
+
+				CDBG("STATS_ENABLE, phy = 0x%lx\n",
+					regptr->paddr);
+
+				scfg->wb_expstatoutputbuffer[i] =
+					(void *)regptr->paddr;
+				regptr++;
+			}
+
+			cmd_data = scfg;
+
+		} else {
+			rc = -EINVAL;
+			goto config_done;
+		}
+	}
+		break;
+	case CMD_STATS_AF_ENABLE:
+	case CMD_STATS_AF_AXI_CFG: {
+		axid = data;
+		if (!axid) {
+			rc = -EFAULT;
+			goto config_failure;
+		}
+
+		sfcfg =
+			kmalloc(sizeof(struct vfe_stats_af_cfg),
+				GFP_ATOMIC);
+
+		if (!sfcfg) {
+			rc = -ENOMEM;
+			goto config_failure;
+		}
+
+		if (copy_from_user(sfcfg,
+					(void __user *)(vfecmd->value),
+					vfecmd->length)) {
+
+			rc = -EFAULT;
+			goto config_done;
+		}
+
+		CDBG("AF_ENABLE: bufnum = %d, enabling = %d\n",
+			axid->bufnum1, sfcfg->af_enable);
+
+		if (axid->bufnum1 > 0) {
+			regptr = &axid->region[0];
+
+			for (i = 0; i < axid->bufnum1; i++) {
+
+				CDBG("STATS_ENABLE, phy = 0x%lx\n",
+					regptr->paddr);
+
+				sfcfg->af_outbuf[i] =
+					(void *)regptr->paddr;
+
+				regptr++;
+			}
+
+			cmd_data = sfcfg;
+
+		} else {
+			rc = -EINVAL;
+			goto config_done;
+		}
+	}
+		break;
+	case CMD_FRAME_BUF_RELEASE: {
+		struct msm_frame *b;
+		unsigned long p;
+		struct vfe_outputack fack;
+		if (!data)  {
+			rc = -EFAULT;
+			goto config_failure;
+		}
+
+		b = (struct msm_frame *)(cmd->value);
+		p = *(unsigned long *)data;
+
+		fack.header = VFE_FRAME_ACK;
+
+		fack.output2newybufferaddress =
+			(void *)(p + b->planar0_off);
+
+		fack.output2newcbcrbufferaddress =
+			(void *)(p + b->planar1_off);
+
+		vfecmd->queue = QDSP_CMDQUEUE;
+		vfecmd->length = sizeof(struct vfe_outputack);
+		cmd_data = &fack;
+	}
+		break;
+	case CMD_SNAP_BUF_RELEASE:
+		break;
+	case CMD_STATS_BUF_RELEASE: {
+		CDBG("vfe_7x_config: CMD_STATS_BUF_RELEASE\n");
+		if (!data) {
+			rc = -EFAULT;
+			goto config_failure;
+		}
+
+		sack.header = STATS_WE_ACK;
+		sack.bufaddr = (void *)*(uint32_t *)data;
+
+		vfecmd->queue  = QDSP_CMDQUEUE;
+		vfecmd->length = sizeof(struct vfe_stats_ack);
+		cmd_data = &sack;
+	}
+		break;
+	case CMD_STATS_AF_BUF_RELEASE: {
+		CDBG("vfe_7x_config: CMD_STATS_AF_BUF_RELEASE\n");
+		if (!data) {
+			rc = -EFAULT;
+			goto config_failure;
+		}
+
+		sack.header = STATS_AF_ACK;
+		sack.bufaddr = (void *)*(uint32_t *)data;
+
+		vfecmd->queue  = QDSP_CMDQUEUE;
+		vfecmd->length = sizeof(struct vfe_stats_ack);
+		cmd_data = &sack;
+	}
+		break;
+	case CMD_GENERAL:
+	case CMD_STATS_DISABLE: {
+		if (vfecmd->length > 256) {
+			cmd_data_alloc =
+			cmd_data = kmalloc(vfecmd->length, GFP_ATOMIC);
+			if (!cmd_data) {
+				rc = -ENOMEM;
+				goto config_failure;
+			}
+		} else
+			cmd_data = buf;
+
+		if (copy_from_user(cmd_data,
+					(void __user *)(vfecmd->value),
+					vfecmd->length)) {
+
+			rc = -EFAULT;
+			goto config_done;
+		}
+
+		if (vfecmd->queue == QDSP_CMDQUEUE) {
+			switch (*(uint32_t *)cmd_data) {
+			case VFE_RESET_CMD:
+				msm_camio_vfe_blk_reset();
+				vfestopped = 0;
+				break;
+			case VFE_START_CMD:
+				_mode = (uint32_t *)cmd_data;
+				op_mode = *(++_mode);
+				if (op_mode & SNAPSHOT_MASK_MODE)
+					msm_camio_set_perf_lvl(S_CAPTURE);
+				else
+					msm_camio_set_perf_lvl(S_PREVIEW);
+				vfestopped = 0;
+				break;
+			case VFE_STOP_CMD:
+				vfestopped = 1;
+				goto config_send;
+
+			default:
+				break;
+			}
+		} /* QDSP_CMDQUEUE */
+	}
+		break;
+	case CMD_AXI_CFG_PREVIEW:
+	case CMD_RAW_PICT_AXI_CFG: {
+		axid = data;
+		if (!axid) {
+			rc = -EFAULT;
+			goto config_failure;
+		}
+
+		axio = kmalloc(sizeof(struct axiout), GFP_ATOMIC);
+		if (!axio) {
+			rc = -ENOMEM;
+			goto config_failure;
+		}
+
+		if (copy_from_user(axio, (void __user *)(vfecmd->value),
+					sizeof(struct axiout))) {
+			rc = -EFAULT;
+			goto config_done;
+		}
+
+		vfe_7x_config_axi(OUTPUT_2, axid, axio);
+		cmd_data = axio;
+	}
+		break;
+	case CMD_AXI_CFG_SNAP: {
+		axid = data;
+		if (!axid) {
+			rc = -EFAULT;
+			goto config_failure;
+		}
+
+		axio = kmalloc(sizeof(struct axiout), GFP_ATOMIC);
+		if (!axio) {
+			rc = -ENOMEM;
+			goto config_failure;
+		}
+
+		if (copy_from_user(axio, (void __user *)(vfecmd->value),
+					sizeof(struct axiout))) {
+			rc = -EFAULT;
+			goto config_done;
+		}
+
+		vfe_7x_config_axi(OUTPUT_1_AND_2, axid, axio);
+
+		cmd_data = axio;
+	}
+		break;
+	default:
+		break;
+	}
+
+	if (vfestopped)
+		goto config_done;
+
+config_send:
+	CDBG("send adsp command = %d\n", *(uint32_t *)cmd_data);
+	rc = msm_adsp_write(vfe_mod, vfecmd->queue,
+				cmd_data, vfecmd->length);
+
+config_done:
+	kfree(cmd_data_alloc);
+
+config_failure:
+	kfree(scfg);
+	kfree(axio);
+	kfree(vfecmd);
+	return rc;
+}
+
+void msm_camvfe_fn_init(struct msm_camvfe_fn *fptr, void *data)
+{
+	mutex_init(&vfe_lock);
+	fptr->vfe_init    = vfe_7x_init;
+	fptr->vfe_enable  = vfe_7x_enable;
+	fptr->vfe_config  = vfe_7x_config;
+	fptr->vfe_disable = vfe_7x_disable;
+	fptr->vfe_release = vfe_7x_release;
+	vfe_syncdata = data;
+}
+
+void msm_camvpe_fn_init(struct msm_camvpe_fn *fptr, void *data)
+{
+	fptr->vpe_reg		= NULL;
+	fptr->send_frame_to_vpe	= NULL;
+	fptr->vpe_config	= NULL;
+	fptr->vpe_cfg_update	= NULL;
+	fptr->dis		= NULL;
+}
diff --git a/drivers/media/video/msm/msm_vfe7x27a.h b/drivers/media/video/msm/msm_vfe7x27a.h
new file mode 100644
index 0000000..a488206
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe7x27a.h
@@ -0,0 +1,300 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MSM_VFE7X_H__
+#define __MSM_VFE7X_H__
+#include <media/msm_camera.h>
+#include <mach/camera.h>
+
+struct vfe_frame_extra {
+	uint32_t	bl_evencol:23;
+	uint32_t	rvd1:9;
+	uint32_t	bl_oddcol:23;
+	uint32_t	rvd2:9;
+
+	uint32_t	d_dbpc_stats_hot:16;
+	uint32_t	d_dbpc_stats_cold:16;
+
+	uint32_t	d_dbpc_stats_0_hot:10;
+	uint32_t	rvd3:6;
+	uint32_t	d_dbpc_stats_0_cold:10;
+	uint32_t	rvd4:6;
+	uint32_t	d_dbpc_stats_1_hot:10;
+	uint32_t	rvd5:6;
+	uint32_t	d_dbpc_stats_1_cold:10;
+	uint32_t	rvd6:6;
+
+	uint32_t	asf_max_edge;
+
+	uint32_t	e_y_wm_pm_stats_0:21;
+	uint32_t	rvd7:11;
+	uint32_t	e_y_wm_pm_stats_1_bl:8;
+	uint32_t	rvd8:8;
+	uint32_t	e_y_wm_pm_stats_1_nl:12;
+	uint32_t	rvd9:4;
+
+	uint32_t	e_cbcr_wm_pm_stats_0:21;
+	uint32_t	rvd10:11;
+	uint32_t	e_cbcr_wm_pm_stats_1_bl:8;
+	uint32_t	rvd11:8;
+	uint32_t	e_cbcr_wm_pm_stats_1_nl:12;
+	uint32_t	rvd12:4;
+
+	uint32_t	v_y_wm_pm_stats_0:21;
+	uint32_t	rvd13:11;
+	uint32_t	v_y_wm_pm_stats_1_bl:8;
+	uint32_t	rvd14:8;
+	uint32_t	v_y_wm_pm_stats_1_nl:12;
+	uint32_t	rvd15:4;
+
+	uint32_t	v_cbcr_wm_pm_stats_0:21;
+	uint32_t	rvd16:11;
+	uint32_t	v_cbcr_wm_pm_stats_1_bl:8;
+	uint32_t	rvd17:8;
+	uint32_t	v_cbcr_wm_pm_stats_1_nl:12;
+	uint32_t	rvd18:4;
+
+	uint32_t      frame_id;
+};
+
+struct vfe_endframe {
+	uint32_t      y_address;
+	uint32_t      cbcr_address;
+
+	struct vfe_frame_extra extra;
+} __packed;
+
+struct vfe_outputack {
+	uint32_t  header;
+	void      *output2newybufferaddress;
+	void      *output2newcbcrbufferaddress;
+} __packed;
+
+struct vfe_stats_ack {
+	uint32_t header;
+	/* MUST BE 64 bit ALIGNED */
+	void     *bufaddr;
+} __packed;
+
+/* AXI Output Config Command sent to DSP */
+struct axiout {
+	uint32_t            cmdheader:32;
+	int                 outputmode:3;
+	uint8_t             format:2;
+	uint32_t            /* reserved */ : 27;
+
+	/* AXI Output 1 Y Configuration, Part 1 */
+	uint32_t            out1yimageheight:12;
+	uint32_t            /* reserved */ : 4;
+	uint32_t            out1yimagewidthin64bitwords:10;
+	uint32_t            /* reserved */ : 6;
+
+	/* AXI Output 1 Y Configuration, Part 2 */
+	uint8_t             out1yburstlen:2;
+	uint32_t            out1ynumrows:12;
+	uint32_t            out1yrowincin64bitincs:12;
+	uint32_t            /* reserved */ : 6;
+
+	/* AXI Output 1 CbCr Configuration, Part 1 */
+	uint32_t            out1cbcrimageheight:12;
+	uint32_t            /* reserved */ : 4;
+	uint32_t            out1cbcrimagewidthin64bitwords:10;
+	uint32_t            /* reserved */ : 6;
+
+	/* AXI Output 1 CbCr Configuration, Part 2 */
+	uint8_t             out1cbcrburstlen:2;
+	uint32_t            out1cbcrnumrows:12;
+	uint32_t            out1cbcrrowincin64bitincs:12;
+	uint32_t            /* reserved */ : 6;
+
+	/* AXI Output 2 Y Configuration, Part 1 */
+	uint32_t            out2yimageheight:12;
+	uint32_t            /* reserved */ : 4;
+	uint32_t            out2yimagewidthin64bitwords:10;
+	uint32_t            /* reserved */ : 6;
+
+	/* AXI Output 2 Y Configuration, Part 2 */
+	uint8_t             out2yburstlen:2;
+	uint32_t            out2ynumrows:12;
+	uint32_t            out2yrowincin64bitincs:12;
+	uint32_t            /* reserved */ : 6;
+
+	/* AXI Output 2 CbCr Configuration, Part 1 */
+	uint32_t            out2cbcrimageheight:12;
+	uint32_t            /* reserved */ : 4;
+	uint32_t            out2cbcrimagewidtein64bitwords:10;
+	uint32_t            /* reserved */ : 6;
+
+	/* AXI Output 2 CbCr Configuration, Part 2 */
+	uint8_t             out2cbcrburstlen:2;
+	uint32_t            out2cbcrnumrows:12;
+	uint32_t            out2cbcrrowincin64bitincs:12;
+	uint32_t            /* reserved */ : 6;
+
+	/* Address configuration:
+	 * output1 phisycal address */
+	unsigned long   output1buffer1_y_phy;
+	unsigned long   output1buffer1_cbcr_phy;
+	unsigned long   output1buffer2_y_phy;
+	unsigned long   output1buffer2_cbcr_phy;
+	unsigned long   output1buffer3_y_phy;
+	unsigned long   output1buffer3_cbcr_phy;
+	unsigned long   output1buffer4_y_phy;
+	unsigned long   output1buffer4_cbcr_phy;
+	unsigned long   output1buffer5_y_phy;
+	unsigned long   output1buffer5_cbcr_phy;
+	unsigned long   output1buffer6_y_phy;
+	unsigned long   output1buffer6_cbcr_phy;
+	unsigned long   output1buffer7_y_phy;
+	unsigned long   output1buffer7_cbcr_phy;
+	unsigned long   output1buffer8_y_phy;
+	unsigned long   output1buffer8_cbcr_phy;
+
+	/* output2 phisycal address */
+	unsigned long   output2buffer1_y_phy;
+	unsigned long   output2buffer1_cbcr_phy;
+	unsigned long   output2buffer2_y_phy;
+	unsigned long   output2buffer2_cbcr_phy;
+	unsigned long   output2buffer3_y_phy;
+	unsigned long   output2buffer3_cbcr_phy;
+	unsigned long   output2buffer4_y_phy;
+	unsigned long   output2buffer4_cbcr_phy;
+	unsigned long   output2buffer5_y_phy;
+	unsigned long   output2buffer5_cbcr_phy;
+	unsigned long   output2buffer6_y_phy;
+	unsigned long   output2buffer6_cbcr_phy;
+	unsigned long   output2buffer7_y_phy;
+	unsigned long   output2buffer7_cbcr_phy;
+	unsigned long   output2buffer8_y_phy;
+	unsigned long   output2buffer8_cbcr_phy;
+} __packed;
+
+struct vfe_stats_we_cfg {
+	uint32_t       header;
+
+	/* White Balance/Exposure Statistic Selection */
+	uint8_t        wb_expstatsenable:1;
+	uint8_t        wb_expstatbuspriorityselection:1;
+	unsigned int   wb_expstatbuspriorityvalue:4;
+	unsigned int   /* reserved */ : 26;
+
+	/* White Balance/Exposure Statistic Configuration, Part 1 */
+	uint8_t        exposurestatregions:1;
+	uint8_t        exposurestatsubregions:1;
+	unsigned int   /* reserved */ : 14;
+
+	unsigned int   whitebalanceminimumy:8;
+	unsigned int   whitebalancemaximumy:8;
+
+	/* White Balance/Exposure Statistic Configuration, Part 2 */
+	uint8_t wb_expstatslopeofneutralregionline[
+		NUM_WB_EXP_NEUTRAL_REGION_LINES];
+
+	/* White Balance/Exposure Statistic Configuration, Part 3 */
+	unsigned int   wb_expstatcrinterceptofneutralregionline2:12;
+	unsigned int   /* reserved */ : 4;
+	unsigned int   wb_expstatcbinterceptofneutralreginnline1:12;
+	unsigned int    /* reserved */ : 4;
+
+	/* White Balance/Exposure Statistic Configuration, Part 4 */
+	unsigned int   wb_expstatcrinterceptofneutralregionline4:12;
+	unsigned int   /* reserved */ : 4;
+	unsigned int   wb_expstatcbinterceptofneutralregionline3:12;
+	unsigned int   /* reserved */ : 4;
+
+	/* White Balance/Exposure Statistic Output Buffer Header */
+	unsigned int   wb_expmetricheaderpattern:8;
+	unsigned int   /* reserved */ : 24;
+
+	/* White Balance/Exposure Statistic Output Buffers-MUST
+	* BE 64 bit ALIGNED */
+	void  *wb_expstatoutputbuffer[NUM_WB_EXP_STAT_OUTPUT_BUFFERS];
+} __packed;
+
+struct vfe_stats_af_cfg {
+	uint32_t header;
+
+	/* Autofocus Statistic Selection */
+	uint8_t       af_enable:1;
+	uint8_t       af_busprioritysel:1;
+	unsigned int  af_buspriorityval:4;
+	unsigned int  /* reserved */ : 26;
+
+	/* Autofocus Statistic Configuration, Part 1 */
+	unsigned int  af_singlewinvoffset:12;
+	unsigned int  /* reserved */ : 4;
+	unsigned int  af_singlewinhoffset:12;
+	unsigned int  /* reserved */ : 3;
+	uint8_t       af_winmode:1;
+
+	/* Autofocus Statistic Configuration, Part 2 */
+	unsigned int  af_singglewinvh:11;
+	unsigned int  /* reserved */ : 5;
+	unsigned int  af_singlewinhw:11;
+	unsigned int  /* reserved */ : 5;
+
+	/* Autofocus Statistic Configuration, Parts 3-6 */
+	uint8_t       af_multiwingrid[NUM_AUTOFOCUS_MULTI_WINDOW_GRIDS];
+
+	/* Autofocus Statistic Configuration, Part 7 */
+	signed int    af_metrichpfcoefa00:5;
+	signed int    af_metrichpfcoefa04:5;
+	unsigned int  af_metricmaxval:11;
+	uint8_t       af_metricsel:1;
+	unsigned int  /* reserved */ : 10;
+
+	/* Autofocus Statistic Configuration, Part 8 */
+	signed int    af_metrichpfcoefa20:5;
+	signed int    af_metrichpfcoefa21:5;
+	signed int    af_metrichpfcoefa22:5;
+	signed int    af_metrichpfcoefa23:5;
+	signed int    af_metrichpfcoefa24:5;
+	unsigned int  /* reserved */ : 7;
+
+	/* Autofocus Statistic Output Buffer Header */
+	unsigned int  af_metrichp:8;
+	unsigned int  /* reserved */ : 24;
+
+	/* Autofocus Statistic Output Buffers - MUST BE 64 bit ALIGNED!!! */
+	void *af_outbuf[NUM_AF_STAT_OUTPUT_BUFFERS];
+} __packed; /* VFE_StatsAutofocusConfigCmdType */
+
+struct msm_camera_frame_msg {
+	unsigned long   output_y_address;
+	unsigned long   output_cbcr_address;
+
+	unsigned int    blacklevelevenColumn:23;
+	uint16_t        reserved1:9;
+	unsigned int    blackleveloddColumn:23;
+	uint16_t        reserved2:9;
+
+	uint16_t        greendefectpixelcount:8;
+	uint16_t        reserved3:8;
+	uint16_t        redbluedefectpixelcount:8;
+	uint16_t        reserved4:8;
+} __packed;
+
+/* New one for 7k */
+struct msm_vfe_command_7k {
+	uint16_t queue;
+	uint16_t length;
+	void     *value;
+};
+
+struct stop_event {
+	wait_queue_head_t wait;
+	int state;
+	int timeout;
+};
+
+
+#endif /* __MSM_VFE7X_H__ */
diff --git a/drivers/media/video/msm/msm_vfe7x27a_v4l2.c b/drivers/media/video/msm/msm_vfe7x27a_v4l2.c
new file mode 100644
index 0000000..135ad20
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe7x27a_v4l2.c
@@ -0,0 +1,1827 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/msm_adsp.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/android_pmem.h>
+#include <linux/slab.h>
+#include <linux/pm_qos.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/module.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/msm_isp.h>
+#include <mach/msm_adsp.h>
+#include <linux/clk.h>
+#include <mach/clk.h>
+#include <mach/camera.h>
+#include "msm_vfe7x27a_v4l2.h"
+#include "msm.h"
+
+/* ADSP Messages */
+#define MSG_RESET_ACK  0
+#define MSG_STOP_ACK  1
+#define MSG_SNAPSHOT  2
+#define MSG_ILLEGAL_COMMAND  3
+#define MSG_START_ACK  4
+#define MSG_UPDATE_ACK  5
+#define MSG_OUTPUT1  6
+#define MSG_OUTPUT2  7
+#define MSG_STATS_AF  8
+#define MSG_STATS_WE  9
+#define MSG_STATS_HISTOGRAM  10
+#define MSG_EPOCH1  11
+#define MSG_EPOCH2  12
+#define MSG_VFE_ERROR 13
+#define MSG_SYNC_TIMER1_DONE  14
+#define MSG_SYNC_TIMER2_DONE  15
+#define MSG_ASYNC_TIMER1_DONE  16
+#define MSG_ASYNC_TIMER2_DONE  17
+#define MSG_CAPTURE_COMPLETE  18
+#define MSG_TABLE_CMD_ACK  19
+#define MSG_EXP_TIMEOUT_ACK  20
+#define MSG_SOF  21
+#define MSG_OUTPUT_T  22
+#define MSG_OUTPUT_S  23
+
+#define VFE_ADSP_EVENT 0xFFFF
+#define SNAPSHOT_MASK_MODE 0x00000001
+#define MSM_AXI_QOS_PREVIEW	122000
+#define MSM_AXI_QOS_SNAPSHOT	192000
+
+
+#define QDSP_CMDQUEUE 25
+#define QDSP_SCALEQUEUE 26
+#define QDSP_TABLEQUEUE 27
+
+/* ADSP Scler queue Cmd IDs */
+#define VFE_SCALE_OUTPUT1_CONFIG  0
+#define VFE_SCALE_OUTPUT2_CONFIG  1
+#define VFE_SCALE_MAX  0xFFFFFFFF
+
+/* ADSP table queue Cmd IDs */
+#define VFE_AXI_INPUT_CONFIG  0
+#define VFE_AXI_OUTPUT_CONFIG  1
+#define VFE_RGB_GAMMA_CONFIG  2
+#define VFE_Y_GAMMA_CONFIG  3
+#define VFE_ROLL_OFF_CONFIG  4
+#define VFE_DEMOSAICv3_BPC_CFG  6
+#define VFE_DEMOSAICv3_ABF_CFG  7
+#define VFE_DEMOSAICv3_CFG  8
+#define VFE_MAX  0xFFFFFFFF
+
+/* ADSP cfg queue cmd IDs */
+#define VFE_RESET  0
+#define VFE_START  1
+#define VFE_STOP  2
+#define VFE_UPDATE  3
+#define VFE_CAMIF_CONFIG  4
+#define VFE_ACTIVE_REGION_CONFIG  5
+#define VFE_DEMOSAIC_CONFIG  6
+#define VFE_INPUT_FORMAT_CONFIG  7
+#define VFE_OUTPUT_CLAMP_CONFIG  8
+#define VFE_CHROMA_SUBSAMPLE_CONFIG  9
+#define VFE_BLACK_LEVEL_CONFIG  10
+#define VFE_WHITE_BALANCE_CONFIG  11
+#define VFE_COLOR_PROCESSING_CONFIG  12
+#define VFE_ADAPTIVE_FILTER_CONFIG  13
+#define VFE_FRAME_SKIP_CONFIG  14
+#define VFE_FOV_CROP  15
+#define VFE_STATS_AUTOFOCUS_CONFIG  16
+#define VFE_STATS_WB_EXP_CONFIG  17
+#define VFE_STATS_HISTOGRAM_CONFIG  18
+#define VFE_OUTPUT1_ACK  19
+#define VFE_OUTPUT2_ACK  20
+#define VFE_STATS_AUTOFOCUS_ACK  21
+#define VFE_STATS_WB_EXP_ACK  22
+#define VFE_EPOCH1_ACK  23
+#define VFE_EPOCH2_ACK  24
+#define VFE_UPDATE_CAMIF_FRAME_CONFIG  25
+#define VFE_SYNC_TIMER1_CONFIG  26
+#define VFE_SYNC_TIMER2_CONFIG  27
+#define VFE_ASYNC_TIMER1_START  28
+#define VFE_ASYNC_TIMER2_START  29
+#define VFE_STATS_AUTOFOCUS_UPDATE  30
+#define VFE_STATS_WB_EXP_UPDATE  31
+#define VFE_ROLL_OFF_UPDATE  33
+#define VFE_DEMOSAICv3_BPC_UPDATE  34
+#define VFE_TESTGEN_START  35
+#define VFE_STATS_MA  0xFFFFFFFF
+
+struct msg_id_map msgs_map[] = {
+	{MSG_RESET_ACK, MSG_ID_RESET_ACK},
+	{MSG_STOP_ACK, MSG_ID_STOP_ACK},
+	{MSG_SNAPSHOT, MSG_ID_SNAPSHOT_DONE},
+	{MSG_ILLEGAL_COMMAND, VFE_MAX},
+	{MSG_START_ACK, MSG_ID_START_ACK},
+	{MSG_UPDATE_ACK, MSG_ID_UPDATE_ACK},
+	{MSG_OUTPUT1, VFE_MAX},
+	{MSG_OUTPUT2, VFE_MAX},
+	{MSG_STATS_AF, MSG_ID_STATS_AF},
+	{MSG_STATS_WE, MSG_ID_STATS_AWB_AEC},
+	{MSG_STATS_HISTOGRAM, MSG_ID_STATS_IHIST},
+	{MSG_EPOCH1, MSG_ID_EPOCH1},
+	{MSG_EPOCH2, MSG_ID_EPOCH2},
+	{MSG_VFE_ERROR, MSG_ID_CAMIF_ERROR},
+	{MSG_SYNC_TIMER1_DONE, MSG_ID_SYNC_TIMER1_DONE},
+	{MSG_SYNC_TIMER2_DONE, MSG_ID_SYNC_TIMER2_DONE},
+	{MSG_ASYNC_TIMER1_DONE, MSG_ID_ASYNC_TIMER1_DONE},
+	{MSG_ASYNC_TIMER2_DONE, MSG_ID_ASYNC_TIMER2_DONE},
+	{MSG_CAPTURE_COMPLETE, MSG_CAPTURE_COMPLETE},
+	{MSG_TABLE_CMD_ACK, MSG_TABLE_CMD_ACK},
+	{MSG_EXP_TIMEOUT_ACK, MSG_EXP_TIMEOUT_ACK},
+	{MSG_SOF, MSG_ID_SOF_ACK},
+	{MSG_OUTPUT_T, MSG_ID_OUTPUT_T},
+	{MSG_OUTPUT_S, MSG_ID_OUTPUT_S},
+};
+
+struct cmd_id_map cmds_map[] = {
+	{VFE_CMD_DUMMY_0, VFE_MAX, VFE_MAX},
+	{VFE_CMD_SET_CLK, VFE_MAX, VFE_MAX},
+	{VFE_CMD_RESET, VFE_RESET, QDSP_CMDQUEUE,
+			"VFE_CMD_RESET", "VFE_RESET"},
+	{VFE_CMD_START, VFE_START, QDSP_CMDQUEUE,
+			"VFE_CMD_START", "VFE_START"},
+	{VFE_CMD_TEST_GEN_START, VFE_TESTGEN_START, QDSP_CMDQUEUE,
+		"VFE_CMD_TEST_GEN_START", "VFE_TESTGEN_START"},
+	{VFE_CMD_OPERATION_CFG, VFE_MAX , VFE_MAX},
+	{VFE_CMD_AXI_OUT_CFG, VFE_AXI_OUTPUT_CONFIG, QDSP_TABLEQUEUE,
+		"VFE_CMD_AXI_OUT_CFG", "VFE_AXI_OUTPUT_CONFIG"},
+	{VFE_CMD_CAMIF_CFG, VFE_CAMIF_CONFIG, QDSP_CMDQUEUE,
+			"VFE_CMD_CAMIF_CFG", "VFE_CAMIF_CONFIG"},
+	{VFE_CMD_AXI_INPUT_CFG, VFE_AXI_INPUT_CONFIG, QDSP_TABLEQUEUE,
+		"VFE_CMD_AXI_INPUT_CFG", "VFE_AXI_INPUT_CONFIG"},
+	{VFE_CMD_BLACK_LEVEL_CFG, VFE_BLACK_LEVEL_CONFIG, QDSP_CMDQUEUE,
+		"VFE_CMD_BLACK_LEVEL_CFG", "VFE_BLACK_LEVEL_CONFIG"},
+	{VFE_CMD_MESH_ROLL_OFF_CFG, VFE_ROLL_OFF_CONFIG, QDSP_TABLEQUEUE,
+		"VFE_CMD_MESH_ROLL_OFF_CFG", "VFE_ROLL_OFF_CONFIG"},
+	{VFE_CMD_DEMUX_CFG, VFE_INPUT_FORMAT_CONFIG, QDSP_CMDQUEUE,
+		"VFE_CMD_DEMUX_CFG", "VFE_INPUT_FORMAT_CONFIG"},
+	{VFE_CMD_FOV_CFG, VFE_FOV_CROP, QDSP_CMDQUEUE,
+		"VFE_CMD_FOV_CFG", "VFE_FOV_CROP"},
+	{VFE_CMD_MAIN_SCALER_CFG, VFE_MAX, VFE_MAX},
+	{VFE_CMD_WB_CFG, VFE_WHITE_BALANCE_CONFIG, QDSP_CMDQUEUE,
+		"VFE_CMD_WB_CFG", "VFE_WHITE_BALANCE_CONFIG"},
+	{VFE_CMD_COLOR_COR_CFG, VFE_MAX, VFE_MAX},
+	{VFE_CMD_RGB_G_CFG, VFE_RGB_GAMMA_CONFIG, QDSP_TABLEQUEUE,
+		"VFE_CMD_RGB_G_CFG", "VFE_RGB_GAMMA_CONFIG"},
+	{VFE_CMD_LA_CFG, VFE_MAX, VFE_MAX},
+	{VFE_CMD_CHROMA_EN_CFG, VFE_MAX, VFE_MAX},
+	{VFE_CMD_CHROMA_SUP_CFG, VFE_MAX, VFE_MAX},
+	{VFE_CMD_MCE_CFG, VFE_MAX, VFE_MAX},
+	{VFE_CMD_SK_ENHAN_CFG, VFE_MAX, VFE_MAX},
+	{VFE_CMD_ASF_CFG, VFE_ADAPTIVE_FILTER_CONFIG, QDSP_CMDQUEUE,
+		"VFE_CMD_ASF_CFG", "VFE_ADAPTIVE_FILTER_CONFIG"},
+	{VFE_CMD_S2Y_CFG, VFE_MAX, VFE_MAX},
+	{VFE_CMD_S2CbCr_CFG, VFE_MAX, VFE_MAX},
+	{VFE_CMD_CHROMA_SUBS_CFG, VFE_CHROMA_SUBSAMPLE_CONFIG, QDSP_CMDQUEUE,
+		"VFE_CMD_CHROMA_SUBS_CFG", "VFE_CHROMA_SUBSAMPLE_CONFIG"},
+	{VFE_CMD_OUT_CLAMP_CFG, VFE_OUTPUT_CLAMP_CONFIG, QDSP_CMDQUEUE,
+		"VFE_CMD_OUT_CLAMP_CFG", "VFE_OUTPUT_CLAMP_CONFIG"},
+	{VFE_CMD_FRAME_SKIP_CFG, VFE_FRAME_SKIP_CONFIG, QDSP_CMDQUEUE,
+		"VFE_CMD_FRAME_SKIP_CFG", "VFE_FRAME_SKIP_CONFIG"},
+	{VFE_CMD_DUMMY_1, VFE_MAX, VFE_MAX},
+	{VFE_CMD_DUMMY_2, VFE_MAX, VFE_MAX},
+	{VFE_CMD_DUMMY_3, VFE_MAX, VFE_MAX},
+	{VFE_CMD_UPDATE, VFE_UPDATE, QDSP_CMDQUEUE,
+		"VFE_CMD_UPDATE", "VFE_UPDATE"},
+	{VFE_CMD_BL_LVL_UPDATE, VFE_MAX, VFE_MAX},
+	{VFE_CMD_DEMUX_UPDATE, VFE_MAX, VFE_MAX},
+	{VFE_CMD_FOV_UPDATE, VFE_MAX, VFE_MAX},
+	{VFE_CMD_MAIN_SCALER_UPDATE, VFE_MAX, VFE_MAX},
+	{VFE_CMD_WB_UPDATE, VFE_WHITE_BALANCE_CONFIG, QDSP_CMDQUEUE,
+		"VFE_CMD_WB_UPDATE", "VFE_WHITE_BALANCE_CONFIG"},
+	{VFE_CMD_COLOR_COR_UPDATE, VFE_MAX, VFE_MAX},
+	{VFE_CMD_RGB_G_UPDATE, VFE_RGB_GAMMA_CONFIG, QDSP_TABLEQUEUE,
+		"VFE_CMD_RGB_G_UPDATE", "VFE_RGB_GAMMA_CONFIG"},
+	{VFE_CMD_LA_UPDATE, VFE_MAX, VFE_MAX},
+	{VFE_CMD_CHROMA_EN_UPDATE, VFE_MAX, VFE_MAX},
+	{VFE_CMD_CHROMA_SUP_UPDATE, VFE_MAX, VFE_MAX},
+	{VFE_CMD_MCE_UPDATE, VFE_MAX, VFE_MAX},
+	{VFE_CMD_SK_ENHAN_UPDATE, VFE_MAX, VFE_MAX},
+	{VFE_CMD_S2CbCr_UPDATE, VFE_MAX, VFE_MAX},
+	{VFE_CMD_S2Y_UPDATE, VFE_MAX, VFE_MAX},
+	{VFE_CMD_ASF_UPDATE, VFE_ADAPTIVE_FILTER_CONFIG, QDSP_CMDQUEUE,
+		"VFE_CMD_ASF_UPDATE", "VFE_ADAPTIVE_FILTER_CONFIG"},
+	{VFE_CMD_FRAME_SKIP_UPDATE, VFE_MAX, VFE_MAX},
+	{VFE_CMD_CAMIF_FRAME_UPDATE, VFE_UPDATE_CAMIF_FRAME_CONFIG,
+		QDSP_CMDQUEUE, "VFE_CMD_CAMIF_FRAME_UPDATE",
+		"VFE_UPDATE_CAMIF_FRAME_CONFIG"},
+	{VFE_CMD_STATS_AF_UPDATE, VFE_STATS_AUTOFOCUS_UPDATE, QDSP_CMDQUEUE,
+		"VFE_CMD_STATS_AF_UPDATE", "VFE_STATS_AUTOFOCUS_UPDATE"},
+	{VFE_CMD_STATS_AE_UPDATE, VFE_MAX, VFE_MAX},
+	{VFE_CMD_STATS_AWB_UPDATE, VFE_MAX, VFE_MAX},
+	{VFE_CMD_STATS_RS_UPDATE, VFE_MAX, VFE_MAX},
+	{VFE_CMD_STATS_CS_UPDATE, VFE_MAX, VFE_MAX},
+	{VFE_CMD_STATS_SKIN_UPDATE, VFE_MAX, VFE_MAX},
+	{VFE_CMD_STATS_IHIST_UPDATE, VFE_MAX, VFE_MAX},
+	{VFE_CMD_DUMMY_4, VFE_MAX, VFE_MAX},
+	{VFE_CMD_EPOCH1_ACK, VFE_EPOCH1_ACK, QDSP_CMDQUEUE,
+			"VFE_CMD_EPOCH1_ACK", "VFE_EPOCH1_ACK"},
+	{VFE_CMD_EPOCH2_ACK, VFE_EPOCH2_ACK, QDSP_CMDQUEUE,
+			"VFE_CMD_EPOCH2_ACK", "VFE_EPOCH2_ACK"},
+	{VFE_CMD_START_RECORDING, VFE_MAX, VFE_MAX},
+	{VFE_CMD_STOP_RECORDING, VFE_MAX , VFE_MAX},
+	{VFE_CMD_DUMMY_5, VFE_MAX, VFE_MAX},
+	{VFE_CMD_DUMMY_6, VFE_MAX, VFE_MAX},
+	{VFE_CMD_CAPTURE, VFE_START, QDSP_CMDQUEUE,
+			"VFE_CMD_CAPTURE", "VFE_START"},
+	{VFE_CMD_DUMMY_7, VFE_MAX, VFE_MAX},
+	{VFE_CMD_STOP, VFE_STOP, QDSP_CMDQUEUE, "VFE_CMD_STOP", "VFE_STOP"},
+	{VFE_CMD_GET_HW_VERSION, VFE_MAX, VFE_MAX},
+	{VFE_CMD_GET_FRAME_SKIP_COUNTS, VFE_MAX, VFE_MAX},
+	{VFE_CMD_OUTPUT1_BUFFER_ENQ, VFE_MAX, VFE_MAX},
+	{VFE_CMD_OUTPUT2_BUFFER_ENQ, VFE_MAX, VFE_MAX},
+	{VFE_CMD_OUTPUT3_BUFFER_ENQ, VFE_MAX, VFE_MAX},
+	{VFE_CMD_JPEG_OUT_BUF_ENQ, VFE_MAX, VFE_MAX},
+	{VFE_CMD_RAW_OUT_BUF_ENQ, VFE_MAX, VFE_MAX},
+	{VFE_CMD_RAW_IN_BUF_ENQ, VFE_MAX, VFE_MAX},
+	{VFE_CMD_STATS_AF_ENQ, VFE_MAX, VFE_MAX},
+	{VFE_CMD_STATS_AE_ENQ, VFE_MAX, VFE_MAX},
+	{VFE_CMD_STATS_AWB_ENQ, VFE_MAX, VFE_MAX},
+	{VFE_CMD_STATS_RS_ENQ, VFE_MAX, VFE_MAX},
+	{VFE_CMD_STATS_CS_ENQ, VFE_MAX, VFE_MAX},
+	{VFE_CMD_STATS_SKIN_ENQ, VFE_MAX, VFE_MAX},
+	{VFE_CMD_STATS_IHIST_ENQ, VFE_MAX, VFE_MAX},
+	{VFE_CMD_DUMMY_8, VFE_MAX, VFE_MAX},
+	{VFE_CMD_JPEG_ENC_CFG, VFE_MAX, VFE_MAX},
+	{VFE_CMD_DUMMY_9, VFE_MAX, VFE_MAX},
+	{VFE_CMD_STATS_AF_START, VFE_STATS_AUTOFOCUS_CONFIG, QDSP_CMDQUEUE,
+		"VFE_CMD_STATS_AF_START", "VFE_STATS_AUTOFOCUS_CONFIG"},
+	{VFE_CMD_STATS_AF_STOP, VFE_STATS_AUTOFOCUS_CONFIG, QDSP_CMDQUEUE,
+		"VFE_CMD_STATS_AF_STOP", "VFE_STATS_AUTOFOCUS_CONFIG"},
+	{VFE_CMD_STATS_AE_START, VFE_MAX, VFE_MAX},
+	{VFE_CMD_STATS_AE_STOP, VFE_MAX, VFE_MAX},
+	{VFE_CMD_STATS_AWB_START, VFE_MAX, VFE_MAX},
+	{VFE_CMD_STATS_AWB_STOP, VFE_MAX, VFE_MAX},
+	{VFE_CMD_STATS_RS_START, VFE_MAX, VFE_MAX},
+	{VFE_CMD_STATS_RS_STOP, VFE_MAX, VFE_MAX},
+	{VFE_CMD_STATS_CS_START, VFE_MAX, VFE_MAX},
+	{VFE_CMD_STATS_CS_STOP, VFE_MAX, VFE_MAX},
+	{VFE_CMD_STATS_SKIN_START, VFE_MAX, VFE_MAX},
+	{VFE_CMD_STATS_SKIN_STOP, VFE_MAX, VFE_MAX},
+	{VFE_CMD_STATS_IHIST_START, VFE_STATS_HISTOGRAM_CONFIG, QDSP_CMDQUEUE,
+		"VFE_CMD_STATS_IHIST_START", "VFE_STATS_HISTOGRAM_CONFIG"},
+	{VFE_CMD_STATS_IHIST_STOP, VFE_MAX, VFE_MAX},
+	{VFE_CMD_DUMMY_10, VFE_MAX, VFE_MAX},
+	{VFE_CMD_SYNC_TIMER_SETTING, VFE_MAX, VFE_MAX},
+	{VFE_CMD_ASYNC_TIMER_SETTING, VFE_MAX, VFE_MAX},
+	{VFE_CMD_LIVESHOT, VFE_MAX, VFE_MAX},
+	{VFE_CMD_LA_SETUP, VFE_MAX, VFE_MAX},
+	{VFE_CMD_LINEARIZATION_CFG, VFE_MAX, VFE_MAX},
+	{VFE_CMD_DEMOSAICV3, VFE_DEMOSAICv3_CFG, QDSP_TABLEQUEUE,
+		"VFE_CMD_DEMOSAICV3", "VFE_DEMOSAICv3_CFG"},
+	{VFE_CMD_DEMOSAICV3_ABCC_CFG, VFE_MAX, VFE_MAX},
+	{VFE_CMD_DEMOSAICV3_DBCC_CFG, VFE_MAX, VFE_MAX},
+	{VFE_CMD_DEMOSAICV3_DBPC_CFG, VFE_DEMOSAICv3_BPC_CFG, QDSP_TABLEQUEUE,
+		"VFE_CMD_DEMOSAICV3_DBPC_CFG", "VFE_DEMOSAICv3_BPC_CFG"},
+	{VFE_CMD_DEMOSAICV3_ABF_CFG, VFE_DEMOSAICv3_ABF_CFG, QDSP_TABLEQUEUE,
+		"VFE_CMD_DEMOSAICV3_ABF_CFG", "VFE_DEMOSAICv3_ABF_CFG"},
+	{VFE_CMD_DEMOSAICV3_ABCC_UPDATE, VFE_MAX, VFE_MAX},
+	{VFE_CMD_DEMOSAICV3_DBCC_UPDATE, VFE_MAX, VFE_MAX},
+	{VFE_CMD_DEMOSAICV3_DBPC_UPDATE, VFE_DEMOSAICv3_BPC_UPDATE,
+		QDSP_CMDQUEUE, "VFE_CMD_DEMOSAICV3_DBPC_UPDATE",
+		"VFE_DEMOSAICv3_BPC_UPDATE"},
+	{VFE_CMD_XBAR_CFG, VFE_MAX, VFE_MAX},
+	{VFE_CMD_MODULE_CFG, VFE_MAX, VFE_MAX},
+	{VFE_CMD_ZSL, VFE_START, QDSP_CMDQUEUE,
+			"VFE_CMD_ZSL", "VFE_START"},
+	{VFE_CMD_LINEARIZATION_UPDATE, VFE_MAX, VFE_MAX},
+	{VFE_CMD_DEMOSAICV3_ABF_UPDATE, VFE_DEMOSAICv3_ABF_CFG,
+		QDSP_TABLEQUEUE, "VFE_CMD_DEMOSAICV3_ABF_UPDATE",
+		"VFE_DEMOSAICv3_ABF_CFG"},
+	{VFE_CMD_CLF_CFG, VFE_MAX, VFE_MAX},
+	{VFE_CMD_CLF_LUMA_UPDATE, VFE_MAX, VFE_MAX},
+	{VFE_CMD_CLF_CHROMA_UPDATE, VFE_MAX, VFE_MAX},
+	{VFE_CMD_PCA_ROLL_OFF_CFG, VFE_MAX, VFE_MAX},
+	{VFE_CMD_PCA_ROLL_OFF_UPDATE, VFE_MAX, VFE_MAX},
+	{VFE_CMD_GET_REG_DUMP, VFE_MAX, VFE_MAX},
+	{VFE_CMD_GET_LINEARIZATON_TABLE, VFE_MAX, VFE_MAX},
+	{VFE_CMD_GET_MESH_ROLLOFF_TABLE, VFE_MAX, VFE_MAX},
+	{VFE_CMD_GET_PCA_ROLLOFF_TABLE, VFE_MAX, VFE_MAX},
+	{VFE_CMD_GET_RGB_G_TABLE, VFE_MAX, VFE_MAX},
+	{VFE_CMD_GET_LA_TABLE, VFE_MAX, VFE_MAX},
+	{VFE_CMD_DEMOSAICV3_UPDATE, VFE_DEMOSAICv3_CFG, QDSP_TABLEQUEUE,
+		"VFE_CMD_DEMOSAICV3_UPDATE", "VFE_DEMOSAICv3_CFG"},
+	{VFE_CMD_ACTIVE_REGION_CFG, VFE_ACTIVE_REGION_CONFIG, QDSP_CMDQUEUE,
+		"VFE_CMD_ACTIVE_REGION_CFG", "VFE_ACTIVE_REGION_CONFIG"},
+	{VFE_CMD_COLOR_PROCESSING_CONFIG, VFE_COLOR_PROCESSING_CONFIG,
+		QDSP_CMDQUEUE, "VFE_CMD_COLOR_PROCESSING_CONFIG",
+		"VFE_COLOR_PROCESSING_CONFIG"},
+	{VFE_CMD_STATS_WB_AEC_CONFIG, VFE_STATS_WB_EXP_CONFIG, QDSP_CMDQUEUE,
+		"VFE_CMD_STATS_WB_AEC_CONFIG", "VFE_STATS_WB_EXP_CONFIG"},
+	{VFE_CMD_STATS_WB_AEC_UPDATE, VFE_STATS_WB_EXP_UPDATE, QDSP_CMDQUEUE,
+		"VFE_CMD_STATS_WB_AEC_UPDATE", "VFE_STATS_WB_EXP_UPDATE"},
+	{VFE_CMD_Y_GAMMA_CONFIG, VFE_Y_GAMMA_CONFIG, QDSP_TABLEQUEUE,
+		"VFE_CMD_Y_GAMMA_CONFIG", "VFE_Y_GAMMA_CONFIG"},
+	{VFE_CMD_SCALE_OUTPUT1_CONFIG, VFE_SCALE_OUTPUT1_CONFIG,
+		QDSP_SCALEQUEUE, "VFE_CMD_SCALE_OUTPUT1_CONFIG",
+		"VFE_SCALE_OUTPUT1_CONFIG"},
+	{VFE_CMD_SCALE_OUTPUT2_CONFIG, VFE_SCALE_OUTPUT2_CONFIG,
+		QDSP_SCALEQUEUE, "VFE_CMD_SCALE_OUTPUT2_CONFIG",
+		"VFE_SCALE_OUTPUT2_CONFIG"},
+	{VFE_CMD_CAPTURE_RAW, VFE_START, QDSP_CMDQUEUE,
+			"VFE_CMD_CAPTURE_RAW", "VFE_START"},
+	{VFE_CMD_RECONFIG_VFE, VFE_MAX, VFE_MAX},
+};
+
+
+static struct msm_adsp_module *qcam_mod;
+static struct msm_adsp_module *vfe_mod;
+static void *extdata;
+static uint32_t extlen;
+
+struct mutex vfe_lock;
+static uint8_t vfestopped;
+
+static struct stop_event stopevent;
+
+static uint32_t op_mode;
+static uint32_t raw_mode;
+static struct vfe2x_ctrl_type *vfe2x_ctrl;
+
+static void vfe2x_send_isp_msg(
+	struct vfe2x_ctrl_type *vctrl,
+	uint32_t isp_msg_id)
+{
+	struct isp_msg_event isp_msg_evt;
+
+	isp_msg_evt.msg_id = isp_msg_id;
+	isp_msg_evt.sof_count = vfe2x_ctrl->vfeFrameId;
+	v4l2_subdev_notify(&vctrl->subdev,
+			NOTIFY_ISP_MSG_EVT,
+			(void *)&isp_msg_evt);
+}
+
+static void vfe_send_outmsg(struct v4l2_subdev *sd, uint8_t msgid,
+		uint32_t ch0_paddr, uint32_t ch1_paddr)
+{
+	struct isp_msg_output msg;
+
+	msg.output_id = msgid;
+	msg.buf.ch_paddr[0]     = ch0_paddr;
+	msg.buf.ch_paddr[1]     = ch1_paddr;
+	msg.frameCounter = vfe2x_ctrl->vfeFrameId;
+
+	v4l2_subdev_notify(&vfe2x_ctrl->subdev,
+			NOTIFY_VFE_MSG_OUT,
+			&msg);
+	return;
+}
+
+static void vfe_send_stats_msg(uint32_t buf_addr, uint32_t msg_id)
+{
+	struct isp_msg_stats msg_stats;
+
+	msg_stats.frameCounter = vfe2x_ctrl->vfeFrameId;
+	msg_stats.buffer       = buf_addr;
+	msg_stats.id           = msg_id;
+
+	v4l2_subdev_notify(&vfe2x_ctrl->subdev,
+				NOTIFY_VFE_MSG_STATS,
+				&msg_stats);
+}
+
+static void vfe_7x_ops(void *driver_data, unsigned id, size_t len,
+		void (*getevent)(void *ptr, size_t len))
+{
+	uint32_t evt_buf[3];
+	void *data = NULL;
+	struct buf_info *outch = NULL;
+	uint32_t y_phy, cbcr_phy;
+	struct table_cmd *table_pending = NULL;
+	unsigned long flags;
+	void   *cmd_data = NULL;
+	unsigned char buf[256];
+	struct msm_free_buf *free_buf = NULL;
+	struct vfe_outputack fack;
+	int i;
+
+	CDBG("%s:id=%d\n", __func__, id);
+	if (id != VFE_ADSP_EVENT) {
+		data = kzalloc(len, GFP_ATOMIC);
+		if (!data) {
+			pr_err("%s: rp: cannot allocate buffer\n", __func__);
+			return;
+		}
+	}
+	if (id == VFE_ADSP_EVENT) {
+		/* event */
+		getevent(evt_buf, sizeof(evt_buf));
+		CDBG("%s:event:msg_id=%d\n", __func__, id);
+	} else {
+		/* messages */
+		getevent(data, len);
+		CDBG("%s:messages:msg_id=%d\n", __func__, id);
+
+		switch (id) {
+		case MSG_SNAPSHOT:
+			msm_camio_set_perf_lvl(S_PREVIEW);
+			vfe_7x_ops(driver_data, MSG_OUTPUT_S, len, getevent);
+			if (!raw_mode)
+				vfe_7x_ops(driver_data, MSG_OUTPUT_T,
+						len, getevent);
+			vfe2x_send_isp_msg(vfe2x_ctrl, MSG_ID_SNAPSHOT_DONE);
+			kfree(data);
+			return;
+		case MSG_OUTPUT_S:
+			outch = &vfe2x_ctrl->snap;
+			y_phy = outch->ping.ch_paddr[0];
+			cbcr_phy = outch->ping.ch_paddr[1];
+			CDBG("MSG_OUTPUT_S: %x %x\n",
+				(unsigned int)y_phy, (unsigned int)cbcr_phy);
+			vfe_send_outmsg(&vfe2x_ctrl->subdev,
+					MSG_ID_OUTPUT_PRIMARY,
+						y_phy, cbcr_phy);
+			break;
+		case MSG_OUTPUT_T:
+			outch = &vfe2x_ctrl->thumb;
+			y_phy = outch->ping.ch_paddr[0];
+			cbcr_phy = outch->ping.ch_paddr[1];
+			CDBG("MSG_OUTPUT_T: %x %x\n",
+				(unsigned int)y_phy, (unsigned int)cbcr_phy);
+			vfe_send_outmsg(&vfe2x_ctrl->subdev,
+						MSG_ID_OUTPUT_SECONDARY,
+							y_phy, cbcr_phy);
+			break;
+		case MSG_OUTPUT1:
+			if (op_mode & SNAPSHOT_MASK_MODE) {
+				kfree(data);
+				return;
+			} else {
+				free_buf = vfe2x_check_free_buffer(
+							VFE_MSG_OUTPUT_IRQ,
+							VFE_MSG_OUTPUT_SECONDARY
+							);
+				CDBG("free_buf = %x\n",
+						(unsigned int) free_buf);
+				if (free_buf) {
+					fack.header = VFE_OUTPUT1_ACK;
+
+					fack.output2newybufferaddress =
+						(void *)(free_buf->ch_paddr[0]);
+
+					fack.output2newcbcrbufferaddress =
+						(void *)(free_buf->ch_paddr[1]);
+
+					cmd_data = &fack;
+					len = sizeof(fack);
+					msm_adsp_write(vfe_mod, QDSP_CMDQUEUE,
+							cmd_data, len);
+			      } else {
+					fack.header = VFE_OUTPUT1_ACK;
+					fack.output2newybufferaddress =
+					(void *)
+				((struct vfe_endframe *)data)->y_address;
+					fack.output2newcbcrbufferaddress =
+					(void *)
+				((struct vfe_endframe *)data)->cbcr_address;
+					cmd_data = &fack;
+					len = sizeof(fack);
+					msm_adsp_write(vfe_mod, QDSP_CMDQUEUE,
+						cmd_data, len);
+					if (!vfe2x_ctrl->zsl_mode) {
+						kfree(data);
+						return;
+					}
+				}
+			}
+			y_phy = ((struct vfe_endframe *)data)->y_address;
+			cbcr_phy = ((struct vfe_endframe *)data)->cbcr_address;
+
+
+			CDBG("vfe_7x_convert, y_phy = 0x%x, cbcr_phy = 0x%x\n",
+				 y_phy, cbcr_phy);
+			if (free_buf) {
+				for (i = 0; i < 3; i++) {
+					if (vfe2x_ctrl->free_buf.buf[i].
+							ch_paddr[0] == y_phy) {
+						vfe2x_ctrl->free_buf.
+							buf[i].ch_paddr[0] =
+							free_buf->ch_paddr[0];
+						vfe2x_ctrl->free_buf.
+							buf[i].ch_paddr[1] =
+							free_buf->ch_paddr[1];
+						break;
+					}
+				}
+				if (i == 3)
+					CDBG("Address doesnt match\n");
+			}
+			memcpy(((struct vfe_frame_extra *)extdata),
+				&((struct vfe_endframe *)data)->extra,
+				sizeof(struct vfe_frame_extra));
+
+			vfe2x_ctrl->vfeFrameId =
+				((struct vfe_frame_extra *)extdata)->frame_id;
+			vfe_send_outmsg(&vfe2x_ctrl->subdev,
+						MSG_ID_OUTPUT_SECONDARY,
+						y_phy, cbcr_phy);
+			break;
+		case MSG_OUTPUT2:
+			if (op_mode & SNAPSHOT_MASK_MODE) {
+				kfree(data);
+				return;
+			} else {
+				free_buf = vfe2x_check_free_buffer(
+					VFE_MSG_OUTPUT_IRQ,
+					VFE_MSG_OUTPUT_PRIMARY);
+			      CDBG("free_buf = %x\n", (unsigned int) free_buf);
+			      if (free_buf) {
+					fack.header = VFE_OUTPUT2_ACK;
+
+					fack.output2newybufferaddress =
+						(void *)(free_buf->ch_paddr[0]);
+
+					fack.output2newcbcrbufferaddress =
+						(void *)(free_buf->ch_paddr[1]);
+
+					cmd_data = &fack;
+					len = sizeof(fack);
+					msm_adsp_write(vfe_mod, QDSP_CMDQUEUE,
+							cmd_data, len);
+			      } else {
+					fack.header = VFE_OUTPUT2_ACK;
+					fack.output2newybufferaddress =
+					(void *)
+				((struct vfe_endframe *)data)->y_address;
+					fack.output2newcbcrbufferaddress =
+					(void *)
+				((struct vfe_endframe *)data)->cbcr_address;
+					cmd_data = &fack;
+					len = sizeof(fack);
+					msm_adsp_write(vfe_mod, QDSP_CMDQUEUE,
+						cmd_data, len);
+					if (!vfe2x_ctrl->zsl_mode) {
+						kfree(data);
+						return;
+					}
+				}
+			}
+			y_phy = ((struct vfe_endframe *)data)->y_address;
+			cbcr_phy = ((struct vfe_endframe *)data)->cbcr_address;
+
+
+			CDBG("vfe_7x_convert, y_phy = 0x%x, cbcr_phy = 0x%x\n",
+				 y_phy, cbcr_phy);
+			if (free_buf) {
+				for (i = 0; i < 3; i++) {
+					if (vfe2x_ctrl->free_buf.buf[i].
+							ch_paddr[0] == y_phy) {
+						vfe2x_ctrl->free_buf.
+							buf[i].ch_paddr[0] =
+							free_buf->ch_paddr[0];
+						vfe2x_ctrl->free_buf.
+							buf[i].ch_paddr[1] =
+							free_buf->ch_paddr[1];
+						break;
+					}
+				}
+				if (i == 3)
+					CDBG("Address doesnt match\n");
+			}
+			memcpy(((struct vfe_frame_extra *)extdata),
+				&((struct vfe_endframe *)data)->extra,
+				sizeof(struct vfe_frame_extra));
+
+			vfe2x_ctrl->vfeFrameId =
+				((struct vfe_frame_extra *)extdata)->frame_id;
+			vfe_send_outmsg(&vfe2x_ctrl->subdev,
+						MSG_ID_OUTPUT_PRIMARY,
+						y_phy, cbcr_phy);
+			break;
+		case MSG_RESET_ACK:
+		case MSG_START_ACK:
+		case MSG_UPDATE_ACK:
+		case MSG_VFE_ERROR:
+		case MSG_SYNC_TIMER1_DONE:
+		case MSG_SYNC_TIMER2_DONE:
+			vfe2x_send_isp_msg(vfe2x_ctrl, msgs_map[id].isp_id);
+			if (id == MSG_START_ACK)
+				vfe2x_ctrl->vfe_started = 1;
+			if (id == MSG_VFE_ERROR) {
+				uint16_t *ptr;
+				struct vfe_error_msg *VFE_ErrorMessageBuffer
+					= data;
+				ptr = data;
+				CDBG("Error: %x %x\n", ptr[0], ptr[1]);
+				CDBG("CAMIF_Error              = %d\n",
+					VFE_ErrorMessageBuffer->camif_error);
+				CDBG("output1YBusOverflow      = %d\n",
+					VFE_ErrorMessageBuffer->
+					output1ybusoverflow);
+				CDBG("output1CbCrBusOverflow   = %d\n",
+					VFE_ErrorMessageBuffer->
+					output1cbcrbusoverflow);
+				CDBG("output2YBusOverflow      = %d\n",
+					VFE_ErrorMessageBuffer->
+					output2ybusoverflow);
+				CDBG("output2CbCrBusOverflow   = %d\n",
+						VFE_ErrorMessageBuffer->
+						output2cbcrbusoverflow);
+				CDBG("autofocusStatBusOverflow = %d\n",
+						VFE_ErrorMessageBuffer->
+						autofocusstatbusoverflow);
+				CDBG("WB_EXPStatBusOverflow    = %d\n",
+						VFE_ErrorMessageBuffer->
+						wb_expstatbusoverflow);
+				CDBG("AXIError                 = %d\n",
+						VFE_ErrorMessageBuffer->
+						axierror);
+				CDBG("CAMIF_Staus              = %d\n",
+						VFE_ErrorMessageBuffer->
+						camif_staus);
+				CDBG("pixel_count              = %d\n",
+						VFE_ErrorMessageBuffer->
+						pixel_count);
+				CDBG("line_count               = %d\n",
+						VFE_ErrorMessageBuffer->
+						line_count);
+			}
+			break;
+		case MSG_SOF:
+			vfe2x_ctrl->vfeFrameId++;
+			if (vfe2x_ctrl->vfeFrameId == 0)
+				vfe2x_ctrl->vfeFrameId = 1; /* wrapped back */
+			if ((op_mode & SNAPSHOT_MASK_MODE) && !raw_mode) {
+				pr_err("Ignore SOF for snapshot\n");
+				kfree(data);
+				return;
+			}
+			vfe2x_send_isp_msg(vfe2x_ctrl, MSG_ID_SOF_ACK);
+			if (raw_mode)
+				vfe2x_send_isp_msg(vfe2x_ctrl,
+						MSG_ID_START_ACK);
+			break;
+		case MSG_STOP_ACK:
+			stopevent.state = 1;
+			vfe2x_ctrl->vfe_started = 0;
+			wake_up(&stopevent.wait);
+			vfe2x_send_isp_msg(vfe2x_ctrl, MSG_ID_STOP_ACK);
+			break;
+		case MSG_STATS_AF:
+		case MSG_STATS_WE:
+			vfe_send_stats_msg(*(uint32_t *)data,
+						msgs_map[id].isp_id);
+			break;
+		default:
+			if (MSG_TABLE_CMD_ACK != id)
+				vfe2x_send_isp_msg(vfe2x_ctrl,
+						msgs_map[id].isp_id);
+			break;
+		}
+	}
+	if (MSG_TABLE_CMD_ACK == id) {
+		spin_lock_irqsave(&vfe2x_ctrl->table_lock, flags);
+		vfe2x_ctrl->tableack_pending = 0;
+		if (list_empty(&vfe2x_ctrl->table_q)) {
+			if (vfe2x_ctrl->start_pending) {
+				CDBG("Send START\n");
+				cmd_data = buf;
+				*(uint32_t *)cmd_data = VFE_START;
+				memcpy(((char *)cmd_data) + 4,
+					&vfe2x_ctrl->start_cmd,
+					sizeof(vfe2x_ctrl->start_cmd));
+				/* Send Start cmd here */
+				len  = sizeof(vfe2x_ctrl->start_cmd) + 4;
+				msm_adsp_write(vfe_mod, QDSP_CMDQUEUE,
+						cmd_data, len);
+				vfe2x_ctrl->start_pending = 0;
+			} else if (vfe2x_ctrl->stop_pending) {
+				CDBG("Send STOP\n");
+				cmd_data = buf;
+				*(uint32_t *)cmd_data = VFE_STOP;
+				/* Send Stop cmd here */
+				len  = 4;
+				msm_adsp_write(vfe_mod, QDSP_CMDQUEUE,
+						cmd_data, len);
+				vfe2x_ctrl->stop_pending = 0;
+			} else if (vfe2x_ctrl->update_pending) {
+				CDBG("Send Update\n");
+				cmd_data = buf;
+				*(uint32_t *)cmd_data = VFE_UPDATE;
+				/* Send Update cmd here */
+				len  = 4;
+				msm_adsp_write(vfe_mod, QDSP_CMDQUEUE,
+						cmd_data, len);
+				vfe2x_ctrl->update_pending = 0;
+			}
+			spin_unlock_irqrestore(&vfe2x_ctrl->table_lock, flags);
+			kfree(data);
+			return;
+		}
+		table_pending = list_first_entry(&vfe2x_ctrl->table_q,
+					struct table_cmd, list);
+		if (!table_pending) {
+			spin_unlock_irqrestore(&vfe2x_ctrl->table_lock, flags);
+			kfree(data);
+			return;
+		}
+		msm_adsp_write(vfe_mod, table_pending->queue,
+				table_pending->cmd, table_pending->size);
+		list_del(&table_pending->list);
+		kfree(table_pending->cmd);
+		kfree(table_pending);
+		vfe2x_ctrl->tableack_pending = 1;
+		spin_unlock_irqrestore(&vfe2x_ctrl->table_lock, flags);
+	} else if (!vfe2x_ctrl->tableack_pending) {
+		if (!list_empty(&vfe2x_ctrl->table_q)) {
+			kfree(data);
+			return;
+		}
+	}
+	kfree(data);
+}
+
+static struct msm_adsp_ops vfe_7x_sync = {
+	.event = vfe_7x_ops,
+};
+
+static int vfe_7x_config_axi(int mode,
+	struct buf_info *ad, struct axiout *ao)
+{
+	unsigned long *bptr;
+	int    cnt;
+	int rc = 0;
+	int o_mode = 0;
+
+	if (op_mode & SNAPSHOT_MASK_MODE)
+		o_mode = SNAPSHOT_MASK_MODE;
+
+	if (mode == OUTPUT_SEC) {
+		/* Thumbnail */
+		if (vfe2x_ctrl->zsl_mode) {
+			ao->output1buffer1_y_phy = ad->ping.ch_paddr[0];
+			ao->output1buffer1_cbcr_phy = ad->ping.ch_paddr[1];
+			ao->output1buffer2_y_phy = ad->pong.ch_paddr[0];
+			ao->output1buffer2_cbcr_phy = ad->pong.ch_paddr[1];
+			ao->output1buffer3_y_phy = ad->free_buf.ch_paddr[0];
+			ao->output1buffer3_cbcr_phy = ad->free_buf.ch_paddr[1];
+			bptr = &ao->output1buffer4_y_phy;
+			for (cnt = 0; cnt < 5; cnt++) {
+				*bptr = ad->pong.ch_paddr[0];
+				bptr++;
+				*bptr = ad->pong.ch_paddr[1];
+				bptr++;
+			}
+		} else {
+			ao->output1buffer1_y_phy = ad->ping.ch_paddr[0];
+			ao->output1buffer1_cbcr_phy = ad->ping.ch_paddr[1];
+			ao->output1buffer2_y_phy = ad->pong.ch_paddr[0];
+			ao->output1buffer2_cbcr_phy = ad->pong.ch_paddr[1];
+			bptr = &ao->output1buffer3_y_phy;
+			for (cnt = 0; cnt < 6; cnt++) {
+				*bptr = ad->pong.ch_paddr[0];
+				bptr++;
+				*bptr = ad->pong.ch_paddr[1];
+				bptr++;
+			}
+		}
+	} else if (mode == OUTPUT_PRIM && o_mode != SNAPSHOT_MASK_MODE) {
+		/* Preview */
+		ao->output2buffer1_y_phy = ad->ping.ch_paddr[0];
+		ao->output2buffer1_cbcr_phy = ad->ping.ch_paddr[1];
+		ao->output2buffer2_y_phy = ad->pong.ch_paddr[0];
+		ao->output2buffer2_cbcr_phy = ad->pong.ch_paddr[1];
+		ao->output2buffer3_y_phy = ad->free_buf.ch_paddr[0];
+		ao->output2buffer3_cbcr_phy = ad->free_buf.ch_paddr[1];
+		bptr = &ao->output2buffer4_y_phy;
+		for (cnt = 0; cnt < 5; cnt++) {
+			*bptr = ad->pong.ch_paddr[0];
+			bptr++;
+			*bptr = ad->pong.ch_paddr[1];
+			bptr++;
+		}
+		CDBG("%x %x\n", (unsigned int)ao->output2buffer1_y_phy,
+			(unsigned int)ao->output2buffer1_cbcr_phy);
+		CDBG("%x %x\n", (unsigned int)ao->output2buffer2_y_phy,
+			(unsigned int)ao->output2buffer2_cbcr_phy);
+		CDBG("%x %x\n", (unsigned int)ao->output2buffer3_y_phy,
+			(unsigned int)ao->output2buffer3_cbcr_phy);
+		CDBG("%x %x\n", (unsigned int)ao->output2buffer4_y_phy,
+			(unsigned int)ao->output2buffer4_cbcr_phy);
+		CDBG("%x %x\n", (unsigned int)ao->output2buffer5_y_phy,
+			(unsigned int)ao->output2buffer5_cbcr_phy);
+		CDBG("%x %x\n", (unsigned int)ao->output2buffer6_y_phy,
+			(unsigned int)ao->output2buffer6_cbcr_phy);
+		CDBG("%x %x\n", (unsigned int)ao->output2buffer7_y_phy,
+			(unsigned int)ao->output2buffer7_cbcr_phy);
+		vfe2x_ctrl->free_buf.buf[0].ch_paddr[0] = ad->ping.ch_paddr[0];
+		vfe2x_ctrl->free_buf.buf[0].ch_paddr[1] = ad->ping.ch_paddr[1];
+		vfe2x_ctrl->free_buf.buf[1].ch_paddr[0] = ad->pong.ch_paddr[0];
+		vfe2x_ctrl->free_buf.buf[1].ch_paddr[1] = ad->pong.ch_paddr[1];
+		vfe2x_ctrl->free_buf.buf[2].ch_paddr[0] =
+			ad->free_buf.ch_paddr[0];
+		vfe2x_ctrl->free_buf.buf[2].ch_paddr[1] =
+			ad->free_buf.ch_paddr[1];
+	} else if (mode == OUTPUT_PRIM && o_mode == SNAPSHOT_MASK_MODE) {
+		vfe2x_ctrl->reconfig_vfe = 0;
+		if (raw_mode) {
+			ao->output2buffer1_y_phy = ad->ping.ch_paddr[0];
+			ao->output2buffer1_cbcr_phy = ad->ping.ch_paddr[0];
+			ao->output2buffer2_y_phy = ad->pong.ch_paddr[0];
+			ao->output2buffer2_cbcr_phy = ad->pong.ch_paddr[0];
+		} else {
+			ao->output2buffer1_y_phy = ad->ping.ch_paddr[0];
+			ao->output2buffer1_cbcr_phy = ad->ping.ch_paddr[1];
+			ao->output2buffer2_y_phy = ad->pong.ch_paddr[0];
+			ao->output2buffer2_cbcr_phy = ad->pong.ch_paddr[1];
+	}
+		bptr = &ao->output2buffer3_y_phy;
+		for (cnt = 0; cnt < 6; cnt++) {
+			*bptr = ad->pong.ch_paddr[0];
+			bptr++;
+			*bptr = ad->pong.ch_paddr[1];
+			bptr++;
+		}
+	}
+
+	return rc;
+}
+
+static void vfe2x_subdev_notify(int id, int path)
+{
+	struct msm_vfe_resp rp;
+	unsigned long flags = 0;
+	spin_lock_irqsave(&vfe2x_ctrl->sd_notify_lock, flags);
+	memset(&rp, 0, sizeof(struct msm_vfe_resp));
+	CDBG("vfe2x_subdev_notify : msgId = %d\n", id);
+	rp.evt_msg.type   = MSM_CAMERA_MSG;
+	rp.evt_msg.msg_id = path;
+	rp.type	   = id;
+	v4l2_subdev_notify(&vfe2x_ctrl->subdev, NOTIFY_VFE_BUF_EVT, &rp);
+	spin_unlock_irqrestore(&vfe2x_ctrl->sd_notify_lock, flags);
+}
+
+static struct msm_free_buf *vfe2x_check_free_buffer(int id, int path)
+{
+	struct buf_info *outch = NULL;
+
+	vfe2x_subdev_notify(id, path);
+	if (op_mode & SNAPSHOT_MASK_MODE) {
+		if (path == VFE_MSG_OUTPUT_PRIMARY)
+			outch = &vfe2x_ctrl->snap;
+		else if (path == VFE_MSG_OUTPUT_SECONDARY)
+			outch = &vfe2x_ctrl->thumb;
+	} else {
+		if (path == VFE_MSG_OUTPUT_PRIMARY) {
+			if (vfe2x_ctrl->zsl_mode)
+				outch = &vfe2x_ctrl->zsl_prim;
+			else
+				outch = &vfe2x_ctrl->prev;
+		} else if (path == VFE_MSG_OUTPUT_SECONDARY)
+				outch = &vfe2x_ctrl->zsl_sec;
+	}
+	if (outch->free_buf.ch_paddr[0])
+		return &outch->free_buf;
+
+	return NULL;
+}
+
+static int vfe2x_configure_pingpong_buffers(int id, int path)
+{
+	struct buf_info *outch = NULL;
+	int rc = 0;
+
+	vfe2x_subdev_notify(id, path);
+	CDBG("Opmode = %d\n", op_mode);
+	if (op_mode & SNAPSHOT_MASK_MODE) {
+		if (path == VFE_MSG_OUTPUT_PRIMARY)
+			outch = &vfe2x_ctrl->snap;
+		else if (path == VFE_MSG_OUTPUT_SECONDARY)
+			outch = &vfe2x_ctrl->thumb;
+	} else {
+		if (path == VFE_MSG_OUTPUT_PRIMARY) {
+			if (vfe2x_ctrl->zsl_mode)
+				outch = &vfe2x_ctrl->zsl_prim;
+			else
+				outch = &vfe2x_ctrl->prev;
+		} else if (path == VFE_MSG_OUTPUT_SECONDARY)
+			outch = &vfe2x_ctrl->zsl_sec;
+	}
+	if (outch->ping.ch_paddr[0] && outch->pong.ch_paddr[0]) {
+		/* Configure Preview Ping Pong */
+		CDBG("%s Configure ping/pong address for %d",
+						__func__, path);
+	} else {
+		pr_err("%s ping/pong addr is null!!", __func__);
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+static struct buf_info *vfe2x_get_ch(int path)
+{
+	struct buf_info *ch = NULL;
+
+	CDBG("path = %d op_mode = %d\n", path, op_mode);
+	/* TODO: Remove Mode specific stuff */
+	if (op_mode & SNAPSHOT_MASK_MODE) {
+		if (path == VFE_MSG_OUTPUT_SECONDARY)
+			ch = &vfe2x_ctrl->thumb;
+		else if (path == VFE_MSG_OUTPUT_PRIMARY)
+			ch = &vfe2x_ctrl->snap;
+	} else {
+		if (path == VFE_MSG_OUTPUT_PRIMARY) {
+			if (vfe2x_ctrl->zsl_mode)
+				ch = &vfe2x_ctrl->zsl_prim;
+			else
+				ch = &vfe2x_ctrl->prev;
+		} else if (path == VFE_MSG_OUTPUT_SECONDARY)
+			ch = &vfe2x_ctrl->zsl_sec;
+	}
+
+	BUG_ON(ch == NULL);
+	return ch;
+}
+
+static long msm_vfe_subdev_ioctl(struct v4l2_subdev *sd,
+			unsigned int subdev_cmd, void *arg)
+{
+	struct msm_isp_cmd vfecmd;
+	struct msm_camvfe_params *vfe_params =
+		(struct msm_camvfe_params *)arg;
+	struct msm_vfe_cfg_cmd *cmd = vfe_params->vfe_cfg;
+	struct table_cmd *table_pending;
+	long rc = 0;
+	void *data = vfe_params->data;
+
+	struct msm_pmem_region *regptr;
+	unsigned char buf[256];
+
+	struct vfe_stats_ack sack;
+	struct axidata *axid;
+	uint32_t i;
+	uint32_t header = 0;
+	uint32_t queue = 0;
+	struct vfe_stats_we_cfg *scfg = NULL;
+	struct vfe_stats_af_cfg *sfcfg = NULL;
+
+	struct axiout *axio = NULL;
+	void   *cmd_data = NULL;
+	void   *cmd_data_alloc = NULL;
+	unsigned long flags;
+	struct msm_free_buf *free_buf = NULL;
+	struct vfe_outputack fack;
+
+	CDBG("msm_vfe_subdev_ioctl is called\n");
+	if (cmd->cmd_type != CMD_FRAME_BUF_RELEASE &&
+	    cmd->cmd_type != CMD_STATS_BUF_RELEASE &&
+	    cmd->cmd_type != CMD_STATS_AF_BUF_RELEASE &&
+		cmd->cmd_type != CMD_CONFIG_PING_ADDR &&
+		cmd->cmd_type != CMD_CONFIG_PONG_ADDR &&
+		cmd->cmd_type != CMD_CONFIG_FREE_BUF_ADDR &&
+		cmd->cmd_type != CMD_VFE_BUFFER_RELEASE) {
+		if (copy_from_user(&vfecmd,
+				(void __user *)(cmd->value),
+				sizeof(vfecmd))) {
+			pr_err("copy_from_user in msm_vfe_subdev_ioctl fail\n");
+			return -EFAULT;
+		}
+	}
+
+	switch (cmd->cmd_type) {
+	case CMD_VFE_BUFFER_RELEASE: {
+		if (!(vfe2x_ctrl->vfe_started) || op_mode == 1)
+			return 0;
+		if (op_mode & SNAPSHOT_MASK_MODE) {
+			free_buf = vfe2x_check_free_buffer(
+					VFE_MSG_OUTPUT_IRQ,
+					VFE_MSG_OUTPUT_SECONDARY);
+		} else {
+			free_buf = vfe2x_check_free_buffer(
+					VFE_MSG_OUTPUT_IRQ,
+					VFE_MSG_OUTPUT_PRIMARY);
+			if (free_buf) {
+				fack.header = VFE_OUTPUT2_ACK;
+
+				fack.output2newybufferaddress =
+						(void *)(free_buf->ch_paddr[0]);
+
+				fack.output2newcbcrbufferaddress =
+						(void *)(free_buf->ch_paddr[1]);
+
+				cmd_data = &fack;
+				vfecmd.length = sizeof(fack) - 4;
+				queue = QDSP_CMDQUEUE;
+			}
+		}
+	}
+	break;
+	case CMD_CONFIG_PING_ADDR: {
+		int path = *((int *)cmd->value);
+		struct buf_info *outch = vfe2x_get_ch(path);
+		outch->ping = *((struct msm_free_buf *)data);
+	}
+		return 0;
+	case CMD_CONFIG_PONG_ADDR: {
+		int path = *((int *)cmd->value);
+		struct buf_info *outch = vfe2x_get_ch(path);
+		outch->pong = *((struct msm_free_buf *)data);
+	}
+		return 0;
+
+	case CMD_CONFIG_FREE_BUF_ADDR: {
+		int path = *((int *)cmd->value);
+		struct buf_info *outch = vfe2x_get_ch(path);
+		outch->free_buf = *((struct msm_free_buf *)data);
+	}
+		return 0;
+
+	case CMD_STATS_AEC_AWB_ENABLE:
+	case CMD_STATS_AXI_CFG: {
+		axid = data;
+		if (!axid) {
+			rc = -EFAULT;
+			goto config_failure;
+		}
+
+		scfg =
+			kmalloc(sizeof(struct vfe_stats_we_cfg),
+				GFP_ATOMIC);
+		if (!scfg) {
+			rc = -ENOMEM;
+			goto config_failure;
+		}
+
+		if (copy_from_user((char *)scfg + 4,
+					(void __user *)(vfecmd.value),
+					vfecmd.length)) {
+
+			rc = -EFAULT;
+			goto config_done;
+		}
+
+		CDBG("STATS_ENABLE: bufnum = %d, enabling = %d\n",
+			axid->bufnum1, scfg->wb_expstatsenable);
+
+		header = cmds_map[vfecmd.id].vfe_id;
+		queue = cmds_map[vfecmd.id].queue;
+		if (header == -1 && queue == -1) {
+			rc = -EFAULT;
+			goto config_failure;
+		}
+		*(uint32_t *)scfg = header;
+		if (axid->bufnum1 > 0) {
+			regptr = axid->region;
+
+			for (i = 0; i < axid->bufnum1; i++) {
+
+				CDBG("STATS_ENABLE, phy = 0x%lx\n",
+					regptr->paddr);
+
+				scfg->wb_expstatoutputbuffer[i] =
+					(void *)regptr->paddr;
+				regptr++;
+			}
+
+			cmd_data = scfg;
+
+		} else {
+			rc = -EINVAL;
+			goto config_done;
+		}
+	}
+		break;
+	case CMD_STATS_AF_ENABLE:
+	case CMD_STATS_AF_AXI_CFG: {
+		CDBG("CMD_STATS_AF_ENABLE CMD_STATS_AF_AXI_CFG\n");
+		axid = data;
+		if (!axid) {
+			rc = -EFAULT;
+			goto config_failure;
+		}
+
+		sfcfg =
+			kmalloc(sizeof(struct vfe_stats_af_cfg),
+				GFP_ATOMIC);
+
+		if (!sfcfg) {
+			rc = -ENOMEM;
+			goto config_failure;
+		}
+
+		if (copy_from_user((char *)sfcfg + 4,
+					(void __user *)(vfecmd.value),
+					vfecmd.length)) {
+
+			rc = -EFAULT;
+			goto config_done;
+		}
+
+		CDBG("AF_ENABLE: bufnum = %d, enabling = %d\n",
+			axid->bufnum1, sfcfg->af_enable);
+
+		header = cmds_map[vfecmd.id].vfe_id;
+		queue = cmds_map[vfecmd.id].queue;
+		if (header == -1 && queue == -1) {
+			rc = -EFAULT;
+			goto config_failure;
+		}
+		*(uint32_t *)sfcfg = header;
+		CDBG("Number of buffers = %d\n", axid->bufnum1);
+		if (axid->bufnum1 > 0) {
+			regptr = &axid->region[0];
+
+			for (i = 0; i < axid->bufnum1; i++) {
+
+				CDBG("STATS_ENABLE, phy = 0x%lx\n",
+					regptr->paddr);
+
+				sfcfg->af_outbuf[i] =
+					(void *)regptr->paddr;
+
+				regptr++;
+			}
+
+			cmd_data = sfcfg;
+
+		} else {
+			rc = -EINVAL;
+			goto config_done;
+		}
+	}
+		break;
+	case CMD_SNAP_BUF_RELEASE:
+		break;
+	case CMD_STATS_BUF_RELEASE: {
+		CDBG("vfe_7x_config: CMD_STATS_BUF_RELEASE\n");
+		if (!data) {
+			rc = -EFAULT;
+			goto config_failure;
+		}
+
+		sack.header = VFE_STATS_WB_EXP_ACK;
+		sack.bufaddr = (void *)*(uint32_t *)data;
+
+		queue  = QDSP_CMDQUEUE;
+		vfecmd.length = sizeof(struct vfe_stats_ack) - 4;
+		cmd_data = &sack;
+	}
+		break;
+	case CMD_STATS_AF_BUF_RELEASE: {
+		CDBG("vfe_7x_config: CMD_STATS_AF_BUF_RELEASE\n");
+		if (!data) {
+			rc = -EFAULT;
+			goto config_failure;
+		}
+
+		sack.header = VFE_STATS_AUTOFOCUS_ACK;
+		sack.bufaddr = (void *)*(uint32_t *)data;
+
+		queue  = QDSP_CMDQUEUE;
+		vfecmd.length = sizeof(struct vfe_stats_ack) - 4;
+		cmd_data = &sack;
+	}
+		break;
+	case CMD_GENERAL:
+	case CMD_STATS_DISABLE: {
+		CDBG("CMD_GENERAL:%d %d\n", vfecmd.id, vfecmd.length);
+		if (vfecmd.id == VFE_CMD_OPERATION_CFG) {
+			if (copy_from_user(&vfe2x_ctrl->start_cmd,
+						(void __user *)(vfecmd.value),
+							vfecmd.length))
+				rc = -EFAULT;
+			op_mode = vfe2x_ctrl->start_cmd.mode_of_operation;
+			return rc;
+		}
+		if (vfecmd.id == VFE_CMD_RECONFIG_VFE) {
+			CDBG("VFE is RECONFIGURED\n");
+			vfe2x_ctrl->reconfig_vfe = 1;
+			return 0;
+		}
+		if (vfecmd.length > 256 - 4) {
+			cmd_data_alloc =
+			cmd_data = kmalloc(vfecmd.length + 4, GFP_ATOMIC);
+			if (!cmd_data) {
+				rc = -ENOMEM;
+				goto config_failure;
+			}
+		} else
+			cmd_data = buf;
+
+		if (copy_from_user(((char *)cmd_data) + 4,
+					(void __user *)(vfecmd.value),
+					vfecmd.length)) {
+
+			rc = -EFAULT;
+			goto config_done;
+		}
+		header = cmds_map[vfecmd.id].vfe_id;
+		queue = cmds_map[vfecmd.id].queue;
+		if (header == -1 && queue == -1) {
+			rc = -EFAULT;
+			goto config_done;
+		}
+		CDBG("%s %s\n", cmds_map[vfecmd.id].isp_id_name,
+			cmds_map[vfecmd.id].vfe_id_name);
+		*(uint32_t *)cmd_data = header;
+		if (queue == QDSP_CMDQUEUE) {
+			switch (vfecmd.id) {
+			case VFE_CMD_RESET:
+				msm_camio_vfe_blk_reset_2();
+				vfestopped = 0;
+				break;
+			case VFE_CMD_START:
+			case VFE_CMD_CAPTURE:
+			case VFE_CMD_CAPTURE_RAW:
+			case VFE_CMD_ZSL:
+				spin_lock_irqsave(&vfe2x_ctrl->table_lock,
+									flags);
+				if ((!list_empty(&vfe2x_ctrl->table_q)) ||
+						vfe2x_ctrl->tableack_pending) {
+					CDBG("start pending\n");
+					vfe2x_ctrl->start_pending = 1;
+					spin_unlock_irqrestore(
+						&vfe2x_ctrl->table_lock,
+								flags);
+					return 0;
+				}
+				spin_unlock_irqrestore(&vfe2x_ctrl->table_lock,
+									flags);
+				vfecmd.length = sizeof(vfe2x_ctrl->start_cmd);
+				memcpy(((char *)cmd_data) + 4,
+					&vfe2x_ctrl->start_cmd,
+					sizeof(vfe2x_ctrl->start_cmd));
+				if (op_mode & SNAPSHOT_MASK_MODE)
+					msm_camio_set_perf_lvl(S_CAPTURE);
+				else
+					msm_camio_set_perf_lvl(S_PREVIEW);
+				vfestopped = 0;
+				break;
+			case VFE_CMD_STOP:
+				vfestopped = 1;
+				spin_lock_irqsave(&vfe2x_ctrl->table_lock,
+						flags);
+				if ((!list_empty(&vfe2x_ctrl->table_q)) ||
+						vfe2x_ctrl->tableack_pending) {
+					CDBG("stop pending\n");
+					vfe2x_ctrl->stop_pending = 1;
+					spin_unlock_irqrestore(
+							&vfe2x_ctrl->table_lock,
+							flags);
+					return 0;
+				}
+				spin_unlock_irqrestore(&vfe2x_ctrl->table_lock,
+						flags);
+				vfe2x_ctrl->vfe_started = 0;
+				goto config_send;
+			case VFE_CMD_UPDATE:
+				spin_lock_irqsave(&vfe2x_ctrl->table_lock,
+						flags);
+				if ((!list_empty(&vfe2x_ctrl->table_q)) ||
+						vfe2x_ctrl->tableack_pending) {
+					CDBG("update pending\n");
+					vfe2x_ctrl->update_pending = 0;
+					vfe2x_send_isp_msg(vfe2x_ctrl,
+						msgs_map[MSG_UPDATE_ACK].
+						isp_id);
+					spin_unlock_irqrestore(
+							&vfe2x_ctrl->table_lock,
+							flags);
+					return 0;
+				}
+				spin_unlock_irqrestore(&vfe2x_ctrl->table_lock,
+						flags);
+				goto config_send;
+			default:
+				break;
+			}
+		} /* QDSP_CMDQUEUE */
+	}
+		break;
+	case CMD_AXI_CFG_SEC: {
+		CDBG("CMD_AXI_CFG_SEC\n");
+		raw_mode = 0;
+		vfe2x_ctrl->zsl_mode = 0;
+		axio = kmalloc(sizeof(struct axiout), GFP_ATOMIC);
+		if (!axio) {
+			pr_err("NULL axio\n");
+			rc = -ENOMEM;
+			goto config_failure;
+		}
+
+		if (copy_from_user((char *)axio + 4,
+					(void __user *)(vfecmd.value),
+					sizeof(struct axiout))) {
+			CDBG("copy_from_user failed\n");
+			rc = -EFAULT;
+			goto config_done;
+		}
+		if (op_mode & SNAPSHOT_MASK_MODE)
+			rc = vfe2x_configure_pingpong_buffers(
+						VFE_MSG_V2X_CAPTURE,
+						VFE_MSG_OUTPUT_SECONDARY);
+		else
+			rc = vfe2x_configure_pingpong_buffers(
+						VFE_MSG_V2X_PREVIEW,
+						VFE_MSG_OUTPUT_SECONDARY);
+		if (rc < 0) {
+			pr_err("%s error configuring pingpong buffers"
+				" for preview", __func__);
+			rc = -EINVAL;
+			goto config_done;
+		}
+
+		if (!(op_mode & SNAPSHOT_MASK_MODE))
+			free_buf = vfe2x_check_free_buffer(
+					VFE_MSG_OUTPUT_IRQ,
+					VFE_MSG_OUTPUT_SECONDARY);
+		header = cmds_map[vfecmd.id].vfe_id;
+		queue = cmds_map[vfecmd.id].queue;
+		if (header == -1 && queue == -1) {
+			rc = -EFAULT;
+			goto config_done;
+		}
+		*(uint32_t *)axio = header;
+		if (op_mode & SNAPSHOT_MASK_MODE)
+			vfe_7x_config_axi(OUTPUT_SEC,
+					&vfe2x_ctrl->thumb, axio);
+		else
+			vfe_7x_config_axi(OUTPUT_SEC,
+					&vfe2x_ctrl->video, axio);
+		cmd_data = axio;
+	}
+		break;
+	case CMD_AXI_CFG_PRIM: {
+		CDBG("CMD_AXI_CFG_PRIM : %d\n", op_mode);
+		raw_mode = 0;
+		vfe2x_ctrl->zsl_mode = 0;
+		axio = kmalloc(sizeof(struct axiout), GFP_ATOMIC);
+		if (!axio) {
+			pr_err("NULL axio\n");
+			rc = -ENOMEM;
+			goto config_failure;
+		}
+
+		if (copy_from_user((char *)axio + 4,
+					(void __user *)(vfecmd.value),
+					sizeof(struct axiout))) {
+			pr_err("copy_from_user failed\n");
+			rc = -EFAULT;
+			goto config_done;
+		}
+		if (!vfe2x_ctrl->reconfig_vfe) {
+			if (op_mode & SNAPSHOT_MASK_MODE)
+				rc = vfe2x_configure_pingpong_buffers(
+						VFE_MSG_V2X_CAPTURE,
+						VFE_MSG_OUTPUT_PRIMARY);
+			else
+				rc = vfe2x_configure_pingpong_buffers(
+						VFE_MSG_V2X_PREVIEW,
+						VFE_MSG_OUTPUT_PRIMARY);
+			if (rc < 0) {
+				pr_err("%s error configuring pingpong buffers"
+					" for preview", __func__);
+				rc = -EINVAL;
+				goto config_done;
+			}
+			if (!(op_mode & SNAPSHOT_MASK_MODE))
+				free_buf = vfe2x_check_free_buffer(
+					VFE_MSG_OUTPUT_IRQ,
+					VFE_MSG_OUTPUT_PRIMARY);
+		} else {
+			vfe2x_ctrl->prev.ping.ch_paddr[0] =
+				vfe2x_ctrl->free_buf.buf[0].ch_paddr[0];
+			vfe2x_ctrl->prev.ping.ch_paddr[1] =
+				vfe2x_ctrl->free_buf.buf[0].ch_paddr[1];
+			vfe2x_ctrl->prev.pong.ch_paddr[0] =
+				vfe2x_ctrl->free_buf.buf[1].ch_paddr[0];
+			vfe2x_ctrl->prev.pong.ch_paddr[1] =
+				vfe2x_ctrl->free_buf.buf[1].ch_paddr[1];
+			vfe2x_ctrl->prev.free_buf.ch_paddr[0] =
+				vfe2x_ctrl->free_buf.buf[2].ch_paddr[0];
+			vfe2x_ctrl->prev.free_buf.ch_paddr[1] =
+				vfe2x_ctrl->free_buf.buf[2].ch_paddr[1];
+			vfe2x_ctrl->reconfig_vfe = 0;
+		}
+		header = cmds_map[vfecmd.id].vfe_id;
+		queue = cmds_map[vfecmd.id].queue;
+		if (header == -1 && queue == -1) {
+			rc = -EFAULT;
+			goto config_done;
+		}
+		*(uint32_t *)axio = header;
+		if (op_mode & SNAPSHOT_MASK_MODE)
+			vfe_7x_config_axi(OUTPUT_PRIM, &vfe2x_ctrl->snap, axio);
+		else
+			vfe_7x_config_axi(OUTPUT_PRIM, &vfe2x_ctrl->prev, axio);
+		cmd_data = axio;
+	}
+		break;
+	case CMD_AXI_CFG_ZSL: {
+		CDBG("CMD_AXI_CFG_ZSL: %d\n", op_mode);
+		raw_mode = 0;
+		vfe2x_ctrl->zsl_mode = 1;
+		axio = kmalloc(sizeof(struct axiout), GFP_ATOMIC);
+		if (!axio) {
+			pr_err("NULL axio\n");
+			rc = -ENOMEM;
+			goto config_failure;
+		}
+
+		if (copy_from_user((char *)axio + 4,
+					(void __user *)(vfecmd.value),
+					sizeof(struct axiout))) {
+			pr_err("copy_from_user failed\n");
+			rc = -EFAULT;
+			goto config_done;
+		}
+		if (!vfe2x_ctrl->reconfig_vfe) {
+				rc = vfe2x_configure_pingpong_buffers(
+						VFE_MSG_V2X_PREVIEW,
+						VFE_MSG_OUTPUT_PRIMARY);
+				rc = vfe2x_configure_pingpong_buffers(
+						VFE_MSG_V2X_PREVIEW,
+						VFE_MSG_OUTPUT_SECONDARY);
+			if (rc < 0) {
+				pr_err("%s error configuring pingpong buffers"
+					" for preview", __func__);
+				rc = -EINVAL;
+				goto config_done;
+			}
+				free_buf = vfe2x_check_free_buffer(
+					VFE_MSG_OUTPUT_IRQ,
+					VFE_MSG_OUTPUT_PRIMARY);
+				free_buf = vfe2x_check_free_buffer(
+					VFE_MSG_OUTPUT_IRQ,
+					VFE_MSG_OUTPUT_SECONDARY);
+		} else {
+			vfe2x_ctrl->prev.ping.ch_paddr[0] =
+				vfe2x_ctrl->free_buf.buf[0].ch_paddr[0];
+			vfe2x_ctrl->prev.ping.ch_paddr[1] =
+				vfe2x_ctrl->free_buf.buf[0].ch_paddr[1];
+			vfe2x_ctrl->prev.pong.ch_paddr[0] =
+				vfe2x_ctrl->free_buf.buf[1].ch_paddr[0];
+			vfe2x_ctrl->prev.pong.ch_paddr[1] =
+				vfe2x_ctrl->free_buf.buf[1].ch_paddr[1];
+			vfe2x_ctrl->prev.free_buf.ch_paddr[0] =
+				vfe2x_ctrl->free_buf.buf[2].ch_paddr[0];
+			vfe2x_ctrl->prev.free_buf.ch_paddr[1] =
+				vfe2x_ctrl->free_buf.buf[2].ch_paddr[1];
+			vfe2x_ctrl->reconfig_vfe = 0;
+		}
+		header = cmds_map[vfecmd.id].vfe_id;
+		queue = cmds_map[vfecmd.id].queue;
+		if (header == -1 && queue == -1) {
+			rc = -EFAULT;
+			goto config_done;
+		}
+		*(uint32_t *)axio = header;
+		vfe_7x_config_axi(OUTPUT_PRIM, &vfe2x_ctrl->zsl_prim, axio);
+		vfe_7x_config_axi(OUTPUT_SEC, &vfe2x_ctrl->zsl_sec, axio);
+		cmd_data = axio;
+	}
+		break;
+	case CMD_AXI_CFG_SEC|CMD_AXI_CFG_PRIM: {
+		CDBG("CMD_AXI_CFG_SEC|PRIM\n");
+		raw_mode = 0;
+		vfe2x_ctrl->zsl_mode = 0;
+		axio = kmalloc(sizeof(struct axiout), GFP_ATOMIC);
+		if (!axio) {
+			pr_err("NULL axio\n");
+			rc = -ENOMEM;
+			goto config_failure;
+		}
+
+		if (copy_from_user((char *)axio + 4,
+					(void __user *)(vfecmd.value),
+					sizeof(struct axiout))) {
+			pr_err("copy_from_user failed\n");
+			rc = -EFAULT;
+			goto config_done;
+		}
+		if (op_mode & SNAPSHOT_MASK_MODE)
+			rc = vfe2x_configure_pingpong_buffers(
+						VFE_MSG_V2X_CAPTURE,
+						VFE_MSG_OUTPUT_SECONDARY);
+		else
+			rc = vfe2x_configure_pingpong_buffers(
+						VFE_MSG_V2X_PREVIEW,
+						VFE_MSG_OUTPUT_SECONDARY);
+		if (rc < 0) {
+			pr_err("%s error configuring pingpong buffers"
+				" for preview", __func__);
+			rc = -EINVAL;
+			goto config_done;
+		}
+
+		if (!(op_mode & SNAPSHOT_MASK_MODE))
+			free_buf = vfe2x_check_free_buffer(
+					VFE_MSG_OUTPUT_IRQ,
+					VFE_MSG_OUTPUT_SECONDARY);
+		header = cmds_map[vfecmd.id].vfe_id;
+		queue = cmds_map[vfecmd.id].queue;
+		if (header == -1 && queue == -1) {
+			rc = -EFAULT;
+			goto config_done;
+		}
+		*(uint32_t *)axio = header;
+		if (op_mode & SNAPSHOT_MASK_MODE)
+			vfe_7x_config_axi(OUTPUT_SEC, &vfe2x_ctrl->thumb, axio);
+		else
+			vfe_7x_config_axi(OUTPUT_SEC, &vfe2x_ctrl->prev, axio);
+
+		if (op_mode & SNAPSHOT_MASK_MODE)
+			rc = vfe2x_configure_pingpong_buffers(
+						VFE_MSG_V2X_CAPTURE,
+						VFE_MSG_OUTPUT_PRIMARY);
+		else
+			rc = vfe2x_configure_pingpong_buffers(
+						VFE_MSG_V2X_PREVIEW,
+						VFE_MSG_OUTPUT_PRIMARY);
+		if (rc < 0) {
+			pr_err("%s error configuring pingpong buffers"
+				" for preview", __func__);
+			rc = -EINVAL;
+			goto config_done;
+		}
+
+		if (!(op_mode & SNAPSHOT_MASK_MODE))
+			free_buf = vfe2x_check_free_buffer(
+					VFE_MSG_OUTPUT_IRQ,
+					VFE_MSG_OUTPUT_PRIMARY);
+		if (op_mode & SNAPSHOT_MASK_MODE)
+			vfe_7x_config_axi(OUTPUT_PRIM,
+					&vfe2x_ctrl->snap, axio);
+		else
+			vfe_7x_config_axi(OUTPUT_PRIM,
+					&vfe2x_ctrl->prev, axio);
+		cmd_data = axio;
+	}
+		break;
+	case CMD_RAW_PICT_AXI_CFG: {
+		CDBG("CMD_RAW_PICT_AXI_CFG:%d\n", op_mode);
+		raw_mode = 1;
+		axio = kmalloc(sizeof(struct axiout), GFP_ATOMIC);
+		if (!axio) {
+			rc = -ENOMEM;
+			goto config_failure;
+		}
+
+		if (copy_from_user((char *)axio + 4,
+					(void __user *)(vfecmd.value),
+					sizeof(struct axiout))) {
+			rc = -EFAULT;
+			goto config_done;
+		}
+		header = cmds_map[vfecmd.id].vfe_id;
+		queue = cmds_map[vfecmd.id].queue;
+		rc = vfe2x_configure_pingpong_buffers(VFE_MSG_V2X_CAPTURE,
+						VFE_MSG_OUTPUT_PRIMARY);
+		if (rc < 0) {
+			pr_err("%s error configuring pingpong buffers"
+				" for preview", __func__);
+			rc = -EINVAL;
+			goto config_done;
+		}
+		if (header == -1 && queue == -1) {
+			rc = -EFAULT;
+			goto config_done;
+		}
+		*(uint32_t *)axio = header;
+		vfe_7x_config_axi(OUTPUT_PRIM, &vfe2x_ctrl->snap, axio);
+		cmd_data = axio;
+	}
+		break;
+	default:
+		break;
+	}
+
+	if (vfestopped)
+		goto config_done;
+
+config_send:
+	CDBG("send adsp command = %d\n", *(uint32_t *)cmd_data);
+	spin_lock_irqsave(&vfe2x_ctrl->table_lock, flags);
+	if (queue == QDSP_TABLEQUEUE &&
+			vfe2x_ctrl->tableack_pending) {
+		CDBG("store table cmd\n");
+		table_pending = kzalloc(sizeof(struct table_cmd), GFP_ATOMIC);
+		if (!table_pending) {
+			rc = -ENOMEM;
+			spin_unlock_irqrestore(&vfe2x_ctrl->table_lock, flags);
+			goto config_done;
+		}
+		table_pending->cmd = kzalloc(vfecmd.length + 4, GFP_ATOMIC);
+		if (!table_pending->cmd) {
+			kfree(table_pending);
+			rc = -ENOMEM;
+			spin_unlock_irqrestore(&vfe2x_ctrl->table_lock, flags);
+			goto config_done;
+		}
+		memcpy(table_pending->cmd, cmd_data, vfecmd.length + 4);
+		table_pending->queue = queue;
+		table_pending->size = vfecmd.length + 4;
+		list_add_tail(&table_pending->list, &vfe2x_ctrl->table_q);
+		spin_unlock_irqrestore(&vfe2x_ctrl->table_lock, flags);
+	} else {
+		if (queue == QDSP_TABLEQUEUE) {
+			CDBG("sending table cmd\n");
+			vfe2x_ctrl->tableack_pending = 1;
+			rc = msm_adsp_write(vfe_mod, queue,
+				cmd_data, vfecmd.length + 4);
+			spin_unlock_irqrestore(&vfe2x_ctrl->table_lock, flags);
+		} else {
+			if (*(uint32_t *)cmd_data == VFE_OUTPUT2_ACK) {
+				uint32_t *ptr = cmd_data;
+				CDBG("%x %x %x\n", ptr[0], ptr[1], ptr[2]);
+			}
+			CDBG("send n-table cmd\n");
+			rc = msm_adsp_write(vfe_mod, queue,
+				cmd_data, vfecmd.length + 4);
+			spin_unlock_irqrestore(&vfe2x_ctrl->table_lock, flags);
+			CDBG("%x\n", vfecmd.length + 4);
+		}
+	}
+
+config_done:
+	kfree(cmd_data_alloc);
+
+config_failure:
+	kfree(scfg);
+	kfree(axio);
+	kfree(sfcfg);
+	return rc;
+}
+
+static struct msm_cam_clk_info vfe2x_clk_info[] = {
+	{"vfe_clk", 192000000},
+};
+
+int msm_vfe_subdev_init(struct v4l2_subdev *sd,
+		struct msm_cam_media_controller *mctl)
+{
+	int rc = 0;
+	v4l2_set_subdev_hostdata(sd, mctl);
+
+	spin_lock_init(&vfe2x_ctrl->sd_notify_lock);
+	spin_lock_init(&vfe2x_ctrl->table_lock);
+	spin_lock_init(&vfe2x_ctrl->vfe_msg_lock);
+	init_waitqueue_head(&stopevent.wait);
+	INIT_LIST_HEAD(&vfe2x_ctrl->table_q);
+	INIT_LIST_HEAD(&vfe2x_ctrl->vfe_msg_q);
+	stopevent.timeout = 200;
+	stopevent.state = 0;
+	vfe2x_ctrl->vfe_started = 0;
+
+
+	CDBG("msm_cam_clk_enable: enable vfe_clk\n");
+	rc = msm_cam_clk_enable(&vfe2x_ctrl->pdev->dev, vfe2x_clk_info,
+			vfe2x_ctrl->vfe_clk, ARRAY_SIZE(vfe2x_clk_info), 1);
+	if (rc < 0)
+		return rc;
+
+	msm_camio_set_perf_lvl(S_INIT);
+
+	/* TODO : check is it required */
+	extlen = sizeof(struct vfe_frame_extra);
+
+	extdata = kmalloc(extlen, GFP_ATOMIC);
+	if (!extdata) {
+		rc = -ENOMEM;
+		goto init_fail;
+	}
+
+	rc = msm_adsp_get("QCAMTASK", &qcam_mod, &vfe_7x_sync, NULL);
+	if (rc) {
+		rc = -EBUSY;
+		goto get_qcam_fail;
+	}
+
+	rc = msm_adsp_get("VFETASK", &vfe_mod, &vfe_7x_sync, NULL);
+	if (rc) {
+		rc = -EBUSY;
+		goto get_vfe_fail;
+	}
+	msm_adsp_enable(qcam_mod);
+	msm_adsp_enable(vfe_mod);
+	return 0;
+
+get_vfe_fail:
+	msm_adsp_put(qcam_mod);
+get_qcam_fail:
+	kfree(extdata);
+init_fail:
+	extlen = 0;
+	return rc;
+}
+
+int msm_vpe_subdev_init(struct v4l2_subdev *sd,
+			struct msm_cam_media_controller *mctl)
+{
+	return 0;
+}
+
+void msm_vpe_subdev_release(void)
+{
+	return;
+}
+
+void msm_vfe_subdev_release(struct v4l2_subdev *sd)
+{
+	CDBG("msm_cam_clk_enable: disable vfe_clk\n");
+	msm_cam_clk_enable(&vfe2x_ctrl->pdev->dev, vfe2x_clk_info,
+			vfe2x_ctrl->vfe_clk, ARRAY_SIZE(vfe2x_clk_info), 0);
+	msm_adsp_disable(qcam_mod);
+	msm_adsp_disable(vfe_mod);
+
+	msm_adsp_put(qcam_mod);
+	msm_adsp_put(vfe_mod);
+
+	kfree(extdata);
+	msm_camio_set_perf_lvl(S_EXIT);
+	return;
+}
+
+static int msm_vfe_subdev_s_crystal_freq(struct v4l2_subdev *sd,
+	u32 freq, u32 flags)
+{
+	int rc = 0;
+	int round_rate;
+
+	round_rate = clk_round_rate(vfe2x_ctrl->vfe_clk[0], freq);
+	if (rc < 0) {
+		pr_err("%s: clk_round_rate failed %d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	rc = clk_set_rate(vfe2x_ctrl->vfe_clk[0], round_rate);
+	if (rc < 0)
+		pr_err("%s: clk_set_rate failed %d\n",
+			__func__, rc);
+
+	return rc;
+}
+
+static const struct v4l2_subdev_video_ops msm_vfe_subdev_video_ops = {
+	.s_crystal_freq = msm_vfe_subdev_s_crystal_freq,
+};
+
+static const struct v4l2_subdev_core_ops msm_vfe_subdev_core_ops = {
+	.ioctl = msm_vfe_subdev_ioctl,
+};
+
+static const struct v4l2_subdev_ops msm_vfe_subdev_ops = {
+	.core = &msm_vfe_subdev_core_ops,
+	.video = &msm_vfe_subdev_video_ops,
+};
+
+static const struct v4l2_subdev_internal_ops msm_vfe_internal_ops;
+
+static int __devinit vfe2x_probe(struct platform_device *pdev)
+{
+	CDBG("%s: device id = %d\n", __func__, pdev->id);
+	vfe2x_ctrl = kzalloc(sizeof(struct vfe2x_ctrl_type), GFP_KERNEL);
+	if (!vfe2x_ctrl) {
+		pr_err("%s: no enough memory\n", __func__);
+		return -ENOMEM;
+	}
+
+	v4l2_subdev_init(&vfe2x_ctrl->subdev, &msm_vfe_subdev_ops);
+	vfe2x_ctrl->subdev.internal_ops = &msm_vfe_internal_ops;
+	vfe2x_ctrl->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	snprintf(vfe2x_ctrl->subdev.name,
+			 sizeof(vfe2x_ctrl->subdev.name), "vfe2.x");
+	v4l2_set_subdevdata(&vfe2x_ctrl->subdev, vfe2x_ctrl);
+	platform_set_drvdata(pdev, &vfe2x_ctrl->subdev);
+
+	vfe2x_ctrl->pdev = pdev;
+	msm_cam_register_subdev_node(&vfe2x_ctrl->subdev, VFE_DEV, 0);
+	return 0;
+}
+
+static struct platform_driver vfe2x_driver = {
+	.probe = vfe2x_probe,
+	.driver = {
+		.name = MSM_VFE_DRV_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init msm_vfe2x_init_module(void)
+{
+	return platform_driver_register(&vfe2x_driver);
+}
+
+static void __exit msm_vfe2x_exit_module(void)
+{
+	platform_driver_unregister(&vfe2x_driver);
+}
+
+module_init(msm_vfe2x_init_module);
+module_exit(msm_vfe2x_exit_module);
+MODULE_DESCRIPTION("VFE 2.x driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/msm_vfe7x27a_v4l2.h b/drivers/media/video/msm/msm_vfe7x27a_v4l2.h
new file mode 100644
index 0000000..2f2d3c6
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe7x27a_v4l2.h
@@ -0,0 +1,414 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MSM_VFE7X_H__
+#define __MSM_VFE7X_H__
+#include <media/msm_camera.h>
+#include <mach/camera.h>
+#include <linux/list.h>
+#include "msm.h"
+
+struct cmd_id_map {
+	uint32_t isp_id;
+	uint32_t vfe_id;
+	uint32_t queue;
+	char isp_id_name[64];
+	char vfe_id_name[64];
+} __packed;
+
+struct msg_id_map {
+	uint32_t vfe_id;
+	uint32_t isp_id;
+} __packed;
+
+struct table_cmd {
+	struct list_head list;
+	void *cmd;
+	int size;
+	int queue;
+} __packed;
+
+struct vfe_msg {
+	struct list_head list;
+	void *cmd;
+	int len;
+	int id;
+} __packed;
+
+struct buf_info {
+	/* Buffer */
+	struct msm_free_buf ping;
+	struct msm_free_buf pong;
+	struct msm_free_buf free_buf;
+} __packed;
+
+struct prev_free_buf_info {
+	struct msm_free_buf buf[3];
+};
+
+struct vfe_cmd_start {
+	uint32_t input_source:1;
+	uint32_t mode_of_operation:1;
+	uint32_t snap_number:4;
+	uint32_t /* reserved */ : 26;
+
+	/* Image Pipeline Modules */
+	uint32_t blacklevel_correction_enable:1;
+	uint32_t lens_rolloff_correction_enable:1;
+	uint32_t white_balance_enable:1;
+	uint32_t rgb_gamma_enable:1;
+	uint32_t luma_noise_reductionpath_enable:1;
+	uint32_t adaptive_spatialfilter_enable:1;
+	uint32_t chroma_subsample_enable:1;
+	uint32_t /* reserved */ : 25;
+
+	/* The dimension fed to the statistics module */
+	uint32_t last_pixel:12;
+	uint32_t /* reserved */ : 4;
+	uint32_t last_line:12;
+	uint32_t /* reserved */ : 4;
+} __packed;
+
+struct vfe2x_ctrl_type {
+	struct buf_info prev;
+	struct buf_info video;
+	struct buf_info snap;
+	struct buf_info raw;
+	struct buf_info thumb;
+	struct prev_free_buf_info free_buf;
+	struct buf_info zsl_prim;
+	struct buf_info zsl_sec;
+	struct prev_free_buf_info zsl_free_buf[2];
+
+
+	spinlock_t  table_lock;
+	struct list_head table_q;
+	uint32_t tableack_pending;
+	uint32_t vfeFrameId;
+
+	spinlock_t vfe_msg_lock;
+	struct list_head vfe_msg_q;
+
+	struct vfe_cmd_start start_cmd;
+	uint32_t start_pending;
+	uint32_t vfe_started;
+	uint32_t stop_pending;
+	uint32_t update_pending;
+
+	/* v4l2 subdev */
+	struct v4l2_subdev subdev;
+	struct platform_device *pdev;
+	struct clk *vfe_clk[3];
+	spinlock_t  sd_notify_lock;
+	uint32_t    reconfig_vfe;
+	uint32_t    zsl_mode;
+} __packed;
+
+struct vfe_frame_extra {
+	uint32_t	bl_evencol:23;
+	uint32_t	rvd1:9;
+	uint32_t	bl_oddcol:23;
+	uint32_t	rvd2:9;
+
+	uint32_t	d_dbpc_stats_hot:16;
+	uint32_t	d_dbpc_stats_cold:16;
+
+	uint32_t	d_dbpc_stats_0_hot:10;
+	uint32_t	rvd3:6;
+	uint32_t	d_dbpc_stats_0_cold:10;
+	uint32_t	rvd4:6;
+	uint32_t	d_dbpc_stats_1_hot:10;
+	uint32_t	rvd5:6;
+	uint32_t	d_dbpc_stats_1_cold:10;
+	uint32_t	rvd6:6;
+
+	uint32_t	asf_max_edge;
+
+	uint32_t	e_y_wm_pm_stats_0:21;
+	uint32_t	rvd7:11;
+	uint32_t	e_y_wm_pm_stats_1_bl:8;
+	uint32_t	rvd8:8;
+	uint32_t	e_y_wm_pm_stats_1_nl:12;
+	uint32_t	rvd9:4;
+
+	uint32_t	e_cbcr_wm_pm_stats_0:21;
+	uint32_t	rvd10:11;
+	uint32_t	e_cbcr_wm_pm_stats_1_bl:8;
+	uint32_t	rvd11:8;
+	uint32_t	e_cbcr_wm_pm_stats_1_nl:12;
+	uint32_t	rvd12:4;
+
+	uint32_t	v_y_wm_pm_stats_0:21;
+	uint32_t	rvd13:11;
+	uint32_t	v_y_wm_pm_stats_1_bl:8;
+	uint32_t	rvd14:8;
+	uint32_t	v_y_wm_pm_stats_1_nl:12;
+	uint32_t	rvd15:4;
+
+	uint32_t	v_cbcr_wm_pm_stats_0:21;
+	uint32_t	rvd16:11;
+	uint32_t	v_cbcr_wm_pm_stats_1_bl:8;
+	uint32_t	rvd17:8;
+	uint32_t	v_cbcr_wm_pm_stats_1_nl:12;
+	uint32_t	rvd18:4;
+
+	uint32_t      frame_id;
+} __packed;
+
+struct vfe_endframe {
+	uint32_t      y_address;
+	uint32_t      cbcr_address;
+
+	struct vfe_frame_extra extra;
+} __packed;
+
+struct vfe_outputack {
+	uint32_t  header;
+	void      *output2newybufferaddress;
+	void      *output2newcbcrbufferaddress;
+} __packed;
+
+struct vfe_stats_ack {
+	uint32_t header;
+	/* MUST BE 64 bit ALIGNED */
+	void     *bufaddr;
+} __packed;
+
+/* AXI Output Config Command sent to DSP */
+struct axiout {
+	uint32_t            cmdheader:32;
+	int                 outputmode:3;
+	uint8_t             format:2;
+	uint32_t            /* reserved */ : 27;
+
+	/* AXI Output 1 Y Configuration, Part 1 */
+	uint32_t            out1yimageheight:12;
+	uint32_t            /* reserved */ : 4;
+	uint32_t            out1yimagewidthin64bitwords:10;
+	uint32_t            /* reserved */ : 6;
+
+	/* AXI Output 1 Y Configuration, Part 2 */
+	uint8_t             out1yburstlen:2;
+	uint32_t            out1ynumrows:12;
+	uint32_t            out1yrowincin64bitincs:12;
+	uint32_t            /* reserved */ : 6;
+
+	/* AXI Output 1 CbCr Configuration, Part 1 */
+	uint32_t            out1cbcrimageheight:12;
+	uint32_t            /* reserved */ : 4;
+	uint32_t            out1cbcrimagewidthin64bitwords:10;
+	uint32_t            /* reserved */ : 6;
+
+	/* AXI Output 1 CbCr Configuration, Part 2 */
+	uint8_t             out1cbcrburstlen:2;
+	uint32_t            out1cbcrnumrows:12;
+	uint32_t            out1cbcrrowincin64bitincs:12;
+	uint32_t            /* reserved */ : 6;
+
+	/* AXI Output 2 Y Configuration, Part 1 */
+	uint32_t            out2yimageheight:12;
+	uint32_t            /* reserved */ : 4;
+	uint32_t            out2yimagewidthin64bitwords:10;
+	uint32_t            /* reserved */ : 6;
+
+	/* AXI Output 2 Y Configuration, Part 2 */
+	uint8_t             out2yburstlen:2;
+	uint32_t            out2ynumrows:12;
+	uint32_t            out2yrowincin64bitincs:12;
+	uint32_t            /* reserved */ : 6;
+
+	/* AXI Output 2 CbCr Configuration, Part 1 */
+	uint32_t            out2cbcrimageheight:12;
+	uint32_t            /* reserved */ : 4;
+	uint32_t            out2cbcrimagewidtein64bitwords:10;
+	uint32_t            /* reserved */ : 6;
+
+	/* AXI Output 2 CbCr Configuration, Part 2 */
+	uint8_t             out2cbcrburstlen:2;
+	uint32_t            out2cbcrnumrows:12;
+	uint32_t            out2cbcrrowincin64bitincs:12;
+	uint32_t            /* reserved */ : 6;
+
+	/* Address configuration:
+	 * output1 phisycal address */
+	unsigned long   output1buffer1_y_phy;
+	unsigned long   output1buffer1_cbcr_phy;
+	unsigned long   output1buffer2_y_phy;
+	unsigned long   output1buffer2_cbcr_phy;
+	unsigned long   output1buffer3_y_phy;
+	unsigned long   output1buffer3_cbcr_phy;
+	unsigned long   output1buffer4_y_phy;
+	unsigned long   output1buffer4_cbcr_phy;
+	unsigned long   output1buffer5_y_phy;
+	unsigned long   output1buffer5_cbcr_phy;
+	unsigned long   output1buffer6_y_phy;
+	unsigned long   output1buffer6_cbcr_phy;
+	unsigned long   output1buffer7_y_phy;
+	unsigned long   output1buffer7_cbcr_phy;
+	unsigned long   output1buffer8_y_phy;
+	unsigned long   output1buffer8_cbcr_phy;
+
+	/* output2 phisycal address */
+	unsigned long   output2buffer1_y_phy;
+	unsigned long   output2buffer1_cbcr_phy;
+	unsigned long   output2buffer2_y_phy;
+	unsigned long   output2buffer2_cbcr_phy;
+	unsigned long   output2buffer3_y_phy;
+	unsigned long   output2buffer3_cbcr_phy;
+	unsigned long   output2buffer4_y_phy;
+	unsigned long   output2buffer4_cbcr_phy;
+	unsigned long   output2buffer5_y_phy;
+	unsigned long   output2buffer5_cbcr_phy;
+	unsigned long   output2buffer6_y_phy;
+	unsigned long   output2buffer6_cbcr_phy;
+	unsigned long   output2buffer7_y_phy;
+	unsigned long   output2buffer7_cbcr_phy;
+	unsigned long   output2buffer8_y_phy;
+	unsigned long   output2buffer8_cbcr_phy;
+} __packed;
+
+struct vfe_stats_we_cfg {
+	uint32_t       header;
+
+	/* White Balance/Exposure Statistic Selection */
+	uint8_t        wb_expstatsenable:1;
+	uint8_t        wb_expstatbuspriorityselection:1;
+	unsigned int   wb_expstatbuspriorityvalue:4;
+	unsigned int   /* reserved */ : 26;
+
+	/* White Balance/Exposure Statistic Configuration, Part 1 */
+	uint8_t        exposurestatregions:1;
+	uint8_t        exposurestatsubregions:1;
+	unsigned int   /* reserved */ : 14;
+
+	unsigned int   whitebalanceminimumy:8;
+	unsigned int   whitebalancemaximumy:8;
+
+	/* White Balance/Exposure Statistic Configuration, Part 2 */
+	uint8_t wb_expstatslopeofneutralregionline[
+		NUM_WB_EXP_NEUTRAL_REGION_LINES];
+
+	/* White Balance/Exposure Statistic Configuration, Part 3 */
+	unsigned int   wb_expstatcrinterceptofneutralregionline2:12;
+	unsigned int   /* reserved */ : 4;
+	unsigned int   wb_expstatcbinterceptofneutralreginnline1:12;
+	unsigned int    /* reserved */ : 4;
+
+	/* White Balance/Exposure Statistic Configuration, Part 4 */
+	unsigned int   wb_expstatcrinterceptofneutralregionline4:12;
+	unsigned int   /* reserved */ : 4;
+	unsigned int   wb_expstatcbinterceptofneutralregionline3:12;
+	unsigned int   /* reserved */ : 4;
+
+	/* White Balance/Exposure Statistic Output Buffer Header */
+	unsigned int   wb_expmetricheaderpattern:8;
+	unsigned int   /* reserved */ : 24;
+
+	/* White Balance/Exposure Statistic Output Buffers-MUST
+	* BE 64 bit ALIGNED */
+	void  *wb_expstatoutputbuffer[NUM_WB_EXP_STAT_OUTPUT_BUFFERS];
+} __packed;
+
+struct vfe_stats_af_cfg {
+	uint32_t header;
+
+	/* Autofocus Statistic Selection */
+	uint8_t       af_enable:1;
+	uint8_t       af_busprioritysel:1;
+	unsigned int  af_buspriorityval:4;
+	unsigned int  /* reserved */ : 26;
+
+	/* Autofocus Statistic Configuration, Part 1 */
+	unsigned int  af_singlewinvoffset:12;
+	unsigned int  /* reserved */ : 4;
+	unsigned int  af_singlewinhoffset:12;
+	unsigned int  /* reserved */ : 3;
+	uint8_t       af_winmode:1;
+
+	/* Autofocus Statistic Configuration, Part 2 */
+	unsigned int  af_singglewinvh:11;
+	unsigned int  /* reserved */ : 5;
+	unsigned int  af_singlewinhw:11;
+	unsigned int  /* reserved */ : 5;
+
+	/* Autofocus Statistic Configuration, Parts 3-6 */
+	uint8_t       af_multiwingrid[NUM_AUTOFOCUS_MULTI_WINDOW_GRIDS];
+
+	/* Autofocus Statistic Configuration, Part 7 */
+	signed int    af_metrichpfcoefa00:5;
+	signed int    af_metrichpfcoefa04:5;
+	unsigned int  af_metricmaxval:11;
+	uint8_t       af_metricsel:1;
+	unsigned int  /* reserved */ : 10;
+
+	/* Autofocus Statistic Configuration, Part 8 */
+	signed int    af_metrichpfcoefa20:5;
+	signed int    af_metrichpfcoefa21:5;
+	signed int    af_metrichpfcoefa22:5;
+	signed int    af_metrichpfcoefa23:5;
+	signed int    af_metrichpfcoefa24:5;
+	unsigned int  /* reserved */ : 7;
+
+	/* Autofocus Statistic Output Buffer Header */
+	unsigned int  af_metrichp:8;
+	unsigned int  /* reserved */ : 24;
+
+	/* Autofocus Statistic Output Buffers - MUST BE 64 bit ALIGNED!!! */
+	void *af_outbuf[NUM_AF_STAT_OUTPUT_BUFFERS];
+} __packed; /* VFE_StatsAutofocusConfigCmdType */
+
+struct msm_camera_frame_msg {
+	unsigned long   output_y_address;
+	unsigned long   output_cbcr_address;
+
+	unsigned int    blacklevelevenColumn:23;
+	uint16_t        reserved1:9;
+	unsigned int    blackleveloddColumn:23;
+	uint16_t        reserved2:9;
+
+	uint16_t        greendefectpixelcount:8;
+	uint16_t        reserved3:8;
+	uint16_t        redbluedefectpixelcount:8;
+	uint16_t        reserved4:8;
+} __packed;
+
+/* New one for 7k */
+struct msm_vfe_command_7k {
+	uint16_t queue;
+	uint16_t length;
+	void     *value;
+};
+
+struct stop_event {
+	wait_queue_head_t wait;
+	int state;
+	int timeout;
+};
+struct vfe_error_msg {
+	unsigned int camif_error:1;
+	unsigned int output1ybusoverflow:1;
+	unsigned int output1cbcrbusoverflow:1;
+	unsigned int output2ybusoverflow:1;
+	unsigned int output2cbcrbusoverflow:1;
+	unsigned int autofocusstatbusoverflow:1;
+	unsigned int wb_expstatbusoverflow:1;
+	unsigned int axierror:1;
+	unsigned int /* reserved */ : 24;
+	unsigned int camif_staus:1;
+	unsigned int pixel_count:14;
+	unsigned int line_count:14;
+	unsigned int /*reserved */ : 3;
+} __packed;
+
+static struct msm_free_buf *vfe2x_check_free_buffer(int id, int path);
+
+#endif /* __MSM_VFE7X_H__ */
diff --git a/drivers/media/video/msm/msm_vfe8x.c b/drivers/media/video/msm/msm_vfe8x.c
new file mode 100644
index 0000000..38011ba
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe8x.c
@@ -0,0 +1,843 @@
+/* Copyright (c) 2009, 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/interrupt.h>
+#include <mach/irqs.h>
+#include "msm_vfe8x_proc.h"
+#include <linux/pm_qos.h>
+
+#define ON  1
+#define OFF 0
+
+static const char *vfe_general_cmd[] = {
+	"START",  /* 0 */
+	"RESET",
+	"AXI_INPUT_CONFIG",
+	"CAMIF_CONFIG",
+	"AXI_OUTPUT_CONFIG",
+	"BLACK_LEVEL_CONFIG",  /* 5 */
+	"ROLL_OFF_CONFIG",
+	"DEMUX_CHANNEL_GAIN_CONFIG",
+	"DEMOSAIC_CONFIG",
+	"FOV_CROP_CONFIG",
+	"MAIN_SCALER_CONFIG",  /* 10 */
+	"WHITE_BALANCE_CONFIG",
+	"COLOR_CORRECTION_CONFIG",
+	"LA_CONFIG",
+	"RGB_GAMMA_CONFIG",
+	"CHROMA_ENHAN_CONFIG",  /* 15 */
+	"CHROMA_SUPPRESSION_CONFIG",
+	"ASF_CONFIG",
+	"SCALER2Y_CONFIG",
+	"SCALER2CbCr_CONFIG",
+	"CHROMA_SUBSAMPLE_CONFIG",  /* 20 */
+	"FRAME_SKIP_CONFIG",
+	"OUTPUT_CLAMP_CONFIG",
+	"TEST_GEN_START",
+	"UPDATE",
+	"OUTPUT1_ACK",  /* 25 */
+	"OUTPUT2_ACK",
+	"EPOCH1_ACK",
+	"EPOCH2_ACK",
+	"STATS_AUTOFOCUS_ACK",
+	"STATS_WB_EXP_ACK",  /* 30 */
+	"BLACK_LEVEL_UPDATE",
+	"DEMUX_CHANNEL_GAIN_UPDATE",
+	"DEMOSAIC_BPC_UPDATE",
+	"DEMOSAIC_ABF_UPDATE",
+	"FOV_CROP_UPDATE",  /* 35 */
+	"WHITE_BALANCE_UPDATE",
+	"COLOR_CORRECTION_UPDATE",
+	"LA_UPDATE",
+	"RGB_GAMMA_UPDATE",
+	"CHROMA_ENHAN_UPDATE",  /* 40 */
+	"CHROMA_SUPPRESSION_UPDATE",
+	"MAIN_SCALER_UPDATE",
+	"SCALER2CbCr_UPDATE",
+	"SCALER2Y_UPDATE",
+	"ASF_UPDATE",  /* 45 */
+	"FRAME_SKIP_UPDATE",
+	"CAMIF_FRAME_UPDATE",
+	"STATS_AUTOFOCUS_UPDATE",
+	"STATS_WB_EXP_UPDATE",
+	"STOP",  /* 50 */
+	"GET_HW_VERSION",
+	"STATS_SETTING",
+	"STATS_AUTOFOCUS_START",
+	"STATS_AUTOFOCUS_STOP",
+	"STATS_WB_EXP_START",  /* 55 */
+	"STATS_WB_EXP_STOP",
+	"ASYNC_TIMER_SETTING",
+};
+
+static void     *vfe_syncdata;
+
+static int vfe_enable(struct camera_enable_cmd *enable)
+{
+	return 0;
+}
+
+static int vfe_disable(struct camera_enable_cmd *enable,
+	struct platform_device *dev)
+{
+	vfe_stop();
+	msm_camio_disable(dev);
+	return 0;
+}
+
+static void vfe_release(struct platform_device *dev)
+{
+	msm_camio_disable(dev);
+	vfe_cmd_release(dev);
+	update_axi_qos(PM_QOS_DEFAULT_VALUE);
+	vfe_syncdata = NULL;
+}
+
+static void vfe_config_axi(int mode,
+			   struct axidata *ad,
+			   struct vfe_cmd_axi_output_config *ao)
+{
+	struct msm_pmem_region *regptr, *regptr1;
+	int i, j;
+	uint32_t *p1, *p2;
+
+	if (mode == OUTPUT_1 || mode == OUTPUT_1_AND_2) {
+		regptr = ad->region;
+		for (i = 0; i < ad->bufnum1; i++) {
+
+			p1 = &(ao->output1.outputY.outFragments[i][0]);
+			p2 = &(ao->output1.outputCbcr.outFragments[i][0]);
+
+			for (j = 0; j < ao->output1.fragmentCount; j++) {
+
+				*p1 = regptr->paddr + regptr->info.planar0_off;
+				p1++;
+
+				*p2 = regptr->paddr + regptr->info.planar1_off;
+				p2++;
+			}
+			regptr++;
+		}
+	} /* if OUTPUT1 or Both */
+
+	if (mode == OUTPUT_2 || mode == OUTPUT_1_AND_2) {
+
+		regptr = &(ad->region[ad->bufnum1]);
+		CDBG("bufnum2 = %d\n", ad->bufnum2);
+
+		for (i = 0; i < ad->bufnum2; i++) {
+
+			p1 = &(ao->output2.outputY.outFragments[i][0]);
+			p2 = &(ao->output2.outputCbcr.outFragments[i][0]);
+
+			CDBG("config_axi: O2, phy = 0x%lx, y_off = %d, "\
+			     "cbcr_off = %d\n", regptr->paddr,
+				regptr->info.planar0_off,
+				regptr->info.planar1_off);
+
+			for (j = 0; j < ao->output2.fragmentCount; j++) {
+
+				*p1 = regptr->paddr + regptr->info.planar0_off;
+				CDBG("vfe_config_axi: p1 = 0x%x\n", *p1);
+				p1++;
+
+				*p2 = regptr->paddr + regptr->info.planar1_off;
+				CDBG("vfe_config_axi: p2 = 0x%x\n", *p2);
+				p2++;
+			}
+			regptr++;
+		}
+	}
+	/* For video configuration */
+	if (mode == OUTPUT_1_AND_3) {
+		/* this is preview buffer. */
+		regptr =  &(ad->region[0]);
+		/* this is video buffer. */
+		regptr1 = &(ad->region[ad->bufnum1]);
+		CDBG("bufnum1 = %d\n", ad->bufnum1);
+		CDBG("bufnum2 = %d\n", ad->bufnum2);
+
+	for (i = 0; i < ad->bufnum1; i++) {
+		p1 = &(ao->output1.outputY.outFragments[i][0]);
+		p2 = &(ao->output1.outputCbcr.outFragments[i][0]);
+
+		CDBG("config_axi: O1, phy = 0x%lx, y_off = %d, "\
+			 "cbcr_off = %d\n", regptr->paddr,
+			 regptr->info.planar0_off, regptr->info.planar1_off);
+
+			for (j = 0; j < ao->output1.fragmentCount; j++) {
+
+				*p1 = regptr->paddr + regptr->info.planar0_off;
+				CDBG("vfe_config_axi: p1 = 0x%x\n", *p1);
+				p1++;
+
+				*p2 = regptr->paddr + regptr->info.planar1_off;
+				CDBG("vfe_config_axi: p2 = 0x%x\n", *p2);
+				p2++;
+			}
+			regptr++;
+		}
+	for (i = 0; i < ad->bufnum2; i++) {
+		p1 = &(ao->output2.outputY.outFragments[i][0]);
+		p2 = &(ao->output2.outputCbcr.outFragments[i][0]);
+
+		CDBG("config_axi: O2, phy = 0x%lx, y_off = %d, "\
+			 "cbcr_off = %d\n", regptr1->paddr,
+			 regptr1->info.planar0_off, regptr1->info.planar1_off);
+
+			for (j = 0; j < ao->output2.fragmentCount; j++) {
+				*p1 = regptr1->paddr +
+					regptr1->info.planar0_off;
+				CDBG("vfe_config_axi: p1 = 0x%x\n", *p1);
+				p1++;
+				*p2 = regptr1->paddr +
+					r1->info.planar1_off;
+				CDBG("vfe_config_axi: p2 = 0x%x\n", *p2);
+				p2++;
+			}
+			regptr1++;
+		}
+	}
+
+}
+
+#define CHECKED_COPY_FROM_USER(in) {					\
+	if (cmd->length != sizeof(*(in))) {				\
+		pr_err("msm_camera: %s:%d cmd %d: user data size %d "	\
+			"!= kernel data size %d\n",			\
+			__func__, __LINE__,				\
+			cmd->id, cmd->length, sizeof(*(in)));		\
+		rc = -EIO;						\
+		break;							\
+	}								\
+	if (copy_from_user((in), (void __user *)cmd->value,		\
+			sizeof(*(in)))) {				\
+		rc = -EFAULT;						\
+		break;							\
+	}								\
+}
+
+static int vfe_proc_general(struct msm_vfe_command_8k *cmd)
+{
+	int rc = 0;
+
+	CDBG("%s: cmdID = %s\n", __func__, vfe_general_cmd[cmd->id]);
+
+	switch (cmd->id) {
+	case VFE_CMD_ID_RESET:
+		msm_camio_vfe_blk_reset();
+		msm_camio_camif_pad_reg_reset_2();
+		vfe_reset();
+		break;
+
+	case VFE_CMD_ID_START: {
+		struct vfe_cmd_start start;
+			CHECKED_COPY_FROM_USER(&start);
+
+		/* msm_camio_camif_pad_reg_reset_2(); */
+		msm_camio_camif_pad_reg_reset();
+		vfe_start(&start);
+	}
+		break;
+
+	case VFE_CMD_ID_CAMIF_CONFIG: {
+		struct vfe_cmd_camif_config camif;
+			CHECKED_COPY_FROM_USER(&camif);
+
+		vfe_camif_config(&camif);
+	}
+		break;
+
+	case VFE_CMD_ID_BLACK_LEVEL_CONFIG: {
+		struct vfe_cmd_black_level_config bl;
+			CHECKED_COPY_FROM_USER(&bl);
+
+		vfe_black_level_config(&bl);
+	}
+		break;
+
+	case VFE_CMD_ID_ROLL_OFF_CONFIG:{
+			/* rolloff is too big to be on the stack */
+			struct vfe_cmd_roll_off_config *rolloff =
+			    kmalloc(sizeof(struct vfe_cmd_roll_off_config),
+				    GFP_KERNEL);
+			if (!rolloff) {
+				pr_err("%s: out of memory\n", __func__);
+				rc = -ENOMEM;
+				break;
+			}
+			/* Wrap CHECKED_COPY_FROM_USER() in a do-while(0) loop
+			 * to make sure we free rolloff when copy_from_user()
+			 * fails.
+			 */
+			do {
+				CHECKED_COPY_FROM_USER(rolloff);
+				vfe_roll_off_config(rolloff);
+			} while (0);
+			kfree(rolloff);
+	}
+		break;
+
+	case VFE_CMD_ID_DEMUX_CHANNEL_GAIN_CONFIG: {
+		struct vfe_cmd_demux_channel_gain_config demuxc;
+			CHECKED_COPY_FROM_USER(&demuxc);
+
+		/* demux is always enabled.  */
+		vfe_demux_channel_gain_config(&demuxc);
+	}
+		break;
+
+	case VFE_CMD_ID_DEMOSAIC_CONFIG: {
+		struct vfe_cmd_demosaic_config demosaic;
+			CHECKED_COPY_FROM_USER(&demosaic);
+
+		vfe_demosaic_config(&demosaic);
+	}
+		break;
+
+	case VFE_CMD_ID_FOV_CROP_CONFIG:
+	case VFE_CMD_ID_FOV_CROP_UPDATE: {
+		struct vfe_cmd_fov_crop_config fov;
+			CHECKED_COPY_FROM_USER(&fov);
+
+		vfe_fov_crop_config(&fov);
+	}
+		break;
+
+	case VFE_CMD_ID_MAIN_SCALER_CONFIG:
+	case VFE_CMD_ID_MAIN_SCALER_UPDATE: {
+		struct vfe_cmd_main_scaler_config mainds;
+			CHECKED_COPY_FROM_USER(&mainds);
+
+		vfe_main_scaler_config(&mainds);
+	}
+		break;
+
+	case VFE_CMD_ID_WHITE_BALANCE_CONFIG:
+	case VFE_CMD_ID_WHITE_BALANCE_UPDATE: {
+		struct vfe_cmd_white_balance_config wb;
+			CHECKED_COPY_FROM_USER(&wb);
+
+		vfe_white_balance_config(&wb);
+	}
+		break;
+
+	case VFE_CMD_ID_COLOR_CORRECTION_CONFIG:
+	case VFE_CMD_ID_COLOR_CORRECTION_UPDATE: {
+		struct vfe_cmd_color_correction_config cc;
+			CHECKED_COPY_FROM_USER(&cc);
+
+		vfe_color_correction_config(&cc);
+	}
+		break;
+
+	case VFE_CMD_ID_LA_CONFIG: {
+		struct vfe_cmd_la_config la;
+			CHECKED_COPY_FROM_USER(&la);
+
+		vfe_la_config(&la);
+	}
+		break;
+
+	case VFE_CMD_ID_RGB_GAMMA_CONFIG: {
+		struct vfe_cmd_rgb_gamma_config rgb;
+			CHECKED_COPY_FROM_USER(&rgb);
+
+		rc = vfe_rgb_gamma_config(&rgb);
+	}
+		break;
+
+	case VFE_CMD_ID_CHROMA_ENHAN_CONFIG:
+	case VFE_CMD_ID_CHROMA_ENHAN_UPDATE: {
+		struct vfe_cmd_chroma_enhan_config chrom;
+			CHECKED_COPY_FROM_USER(&chrom);
+
+		vfe_chroma_enhan_config(&chrom);
+	}
+		break;
+
+	case VFE_CMD_ID_CHROMA_SUPPRESSION_CONFIG:
+	case VFE_CMD_ID_CHROMA_SUPPRESSION_UPDATE: {
+		struct vfe_cmd_chroma_suppression_config chromsup;
+			CHECKED_COPY_FROM_USER(&chromsup);
+
+		vfe_chroma_sup_config(&chromsup);
+	}
+		break;
+
+	case VFE_CMD_ID_ASF_CONFIG: {
+		struct vfe_cmd_asf_config asf;
+			CHECKED_COPY_FROM_USER(&asf);
+
+		vfe_asf_config(&asf);
+	}
+		break;
+
+	case VFE_CMD_ID_SCALER2Y_CONFIG:
+	case VFE_CMD_ID_SCALER2Y_UPDATE: {
+		struct vfe_cmd_scaler2_config ds2y;
+			CHECKED_COPY_FROM_USER(&ds2y);
+
+		vfe_scaler2y_config(&ds2y);
+	}
+		break;
+
+	case VFE_CMD_ID_SCALER2CbCr_CONFIG:
+	case VFE_CMD_ID_SCALER2CbCr_UPDATE: {
+		struct vfe_cmd_scaler2_config ds2cbcr;
+			CHECKED_COPY_FROM_USER(&ds2cbcr);
+
+		vfe_scaler2cbcr_config(&ds2cbcr);
+	}
+		break;
+
+	case VFE_CMD_ID_CHROMA_SUBSAMPLE_CONFIG: {
+		struct vfe_cmd_chroma_subsample_config sub;
+			CHECKED_COPY_FROM_USER(&sub);
+
+		vfe_chroma_subsample_config(&sub);
+	}
+		break;
+
+	case VFE_CMD_ID_FRAME_SKIP_CONFIG: {
+		struct vfe_cmd_frame_skip_config fskip;
+			CHECKED_COPY_FROM_USER(&fskip);
+
+		vfe_frame_skip_config(&fskip);
+	}
+		break;
+
+	case VFE_CMD_ID_OUTPUT_CLAMP_CONFIG: {
+		struct vfe_cmd_output_clamp_config clamp;
+			CHECKED_COPY_FROM_USER(&clamp);
+
+		vfe_output_clamp_config(&clamp);
+	}
+		break;
+
+	/* module update commands */
+	case VFE_CMD_ID_BLACK_LEVEL_UPDATE: {
+		struct vfe_cmd_black_level_config blk;
+			CHECKED_COPY_FROM_USER(&blk);
+
+		vfe_black_level_update(&blk);
+	}
+		break;
+
+	case VFE_CMD_ID_DEMUX_CHANNEL_GAIN_UPDATE: {
+		struct vfe_cmd_demux_channel_gain_config dmu;
+			CHECKED_COPY_FROM_USER(&dmu);
+
+		vfe_demux_channel_gain_update(&dmu);
+	}
+		break;
+
+	case VFE_CMD_ID_DEMOSAIC_BPC_UPDATE: {
+		struct vfe_cmd_demosaic_bpc_update demo_bpc;
+			CHECKED_COPY_FROM_USER(&demo_bpc);
+
+		vfe_demosaic_bpc_update(&demo_bpc);
+	}
+		break;
+
+	case VFE_CMD_ID_DEMOSAIC_ABF_UPDATE: {
+		struct vfe_cmd_demosaic_abf_update demo_abf;
+			CHECKED_COPY_FROM_USER(&demo_abf);
+
+		vfe_demosaic_abf_update(&demo_abf);
+	}
+		break;
+
+	case VFE_CMD_ID_LA_UPDATE: {
+		struct vfe_cmd_la_config la;
+			CHECKED_COPY_FROM_USER(&la);
+
+		vfe_la_update(&la);
+	}
+		break;
+
+	case VFE_CMD_ID_RGB_GAMMA_UPDATE: {
+		struct vfe_cmd_rgb_gamma_config rgb;
+			CHECKED_COPY_FROM_USER(&rgb);
+
+		rc = vfe_rgb_gamma_update(&rgb);
+	}
+		break;
+
+	case VFE_CMD_ID_ASF_UPDATE: {
+		struct vfe_cmd_asf_update asf;
+			CHECKED_COPY_FROM_USER(&asf);
+
+		vfe_asf_update(&asf);
+	}
+		break;
+
+	case VFE_CMD_ID_FRAME_SKIP_UPDATE: {
+		struct vfe_cmd_frame_skip_update fskip;
+			CHECKED_COPY_FROM_USER(&fskip);
+			/* Start recording */
+			if (fskip.output2Pattern == 0xffffffff)
+				update_axi_qos(MSM_AXI_QOS_RECORDING);
+			 else if (fskip.output2Pattern == 0)
+				update_axi_qos(MSM_AXI_QOS_PREVIEW);
+
+		vfe_frame_skip_update(&fskip);
+	}
+		break;
+
+	case VFE_CMD_ID_CAMIF_FRAME_UPDATE: {
+		struct vfe_cmds_camif_frame fup;
+			CHECKED_COPY_FROM_USER(&fup);
+
+		vfe_camif_frame_update(&fup);
+	}
+		break;
+
+	/* stats update commands */
+	case VFE_CMD_ID_STATS_AUTOFOCUS_UPDATE: {
+		struct vfe_cmd_stats_af_update afup;
+			CHECKED_COPY_FROM_USER(&afup);
+
+		vfe_stats_update_af(&afup);
+	}
+		break;
+
+	case VFE_CMD_ID_STATS_WB_EXP_UPDATE: {
+		struct vfe_cmd_stats_wb_exp_update wbexp;
+			CHECKED_COPY_FROM_USER(&wbexp);
+
+		vfe_stats_update_wb_exp(&wbexp);
+	}
+		break;
+
+	/* control of start, stop, update, etc... */
+	case VFE_CMD_ID_STOP:
+		vfe_stop();
+		break;
+
+	case VFE_CMD_ID_GET_HW_VERSION:
+		break;
+
+	/* stats */
+	case VFE_CMD_ID_STATS_SETTING: {
+		struct vfe_cmd_stats_setting stats;
+			CHECKED_COPY_FROM_USER(&stats);
+
+		vfe_stats_setting(&stats);
+	}
+		break;
+
+	case VFE_CMD_ID_STATS_AUTOFOCUS_START: {
+		struct vfe_cmd_stats_af_start af;
+			CHECKED_COPY_FROM_USER(&af);
+
+		vfe_stats_start_af(&af);
+	}
+		break;
+
+	case VFE_CMD_ID_STATS_AUTOFOCUS_STOP:
+		vfe_stats_af_stop();
+		break;
+
+	case VFE_CMD_ID_STATS_WB_EXP_START: {
+		struct vfe_cmd_stats_wb_exp_start awexp;
+			CHECKED_COPY_FROM_USER(&awexp);
+
+		vfe_stats_start_wb_exp(&awexp);
+	}
+		break;
+
+	case VFE_CMD_ID_STATS_WB_EXP_STOP:
+		vfe_stats_wb_exp_stop();
+		break;
+
+	case VFE_CMD_ID_ASYNC_TIMER_SETTING:
+		break;
+
+	case VFE_CMD_ID_UPDATE:
+		vfe_update();
+		break;
+
+	/* test gen */
+	case VFE_CMD_ID_TEST_GEN_START:
+		break;
+
+/*
+  acknowledge from upper layer
+	these are not in general command.
+
+	case VFE_CMD_ID_OUTPUT1_ACK:
+		break;
+	case VFE_CMD_ID_OUTPUT2_ACK:
+		break;
+	case VFE_CMD_ID_EPOCH1_ACK:
+		break;
+	case VFE_CMD_ID_EPOCH2_ACK:
+		break;
+	case VFE_CMD_ID_STATS_AUTOFOCUS_ACK:
+		break;
+	case VFE_CMD_ID_STATS_WB_EXP_ACK:
+		break;
+*/
+
+	default:
+		pr_err("%s: invalid cmd id %d\n", __func__, cmd->id);
+		rc = -EINVAL;
+		break;
+	} /* switch */
+
+	return rc;
+}
+
+static int vfe_config(struct msm_vfe_cfg_cmd *cmd, void *data)
+{
+	struct msm_pmem_region *regptr;
+	struct msm_vfe_command_8k vfecmd;
+	struct vfe_cmd_axi_output_config axio;
+	struct axidata *axid = data;
+
+	int rc = 0;
+
+
+	if (cmd->cmd_type != CMD_FRAME_BUF_RELEASE &&
+		cmd->cmd_type != CMD_STATS_BUF_RELEASE &&
+		cmd->cmd_type != CMD_STATS_AF_BUF_RELEASE) {
+
+		if (copy_from_user(&vfecmd,
+			(void __user *)(cmd->value), sizeof(vfecmd))) {
+			pr_err("%s %d: copy_from_user failed\n",
+				__func__, __LINE__);
+			return -EFAULT;
+		}
+	}
+
+	CDBG("%s: cmdType = %d\n", __func__, cmd->cmd_type);
+
+	switch (cmd->cmd_type) {
+	case CMD_GENERAL:
+		rc = vfe_proc_general(&vfecmd);
+		break;
+
+	case CMD_STATS_ENABLE:
+	case CMD_STATS_AXI_CFG: {
+			int i;
+			struct vfe_cmd_stats_setting scfg;
+
+			BUG_ON(!axid);
+
+			if (vfecmd.length != sizeof(scfg)) {
+				pr_err
+				("msm_camera: %s: cmd %d: user-space "\
+				"data size %d != kernel data size %d\n",
+				__func__,
+				cmd->cmd_type, vfecmd.length,
+				sizeof(scfg));
+				return -EIO;
+			}
+
+			if (copy_from_user(&scfg,
+				(void __user *)(vfecmd.value),
+				sizeof(scfg))) {
+				pr_err("%s %d: copy_from_user failed\n",
+				__func__, __LINE__);
+			return -EFAULT;
+		}
+
+		regptr = axid->region;
+		if (axid->bufnum1 > 0) {
+			for (i = 0; i < axid->bufnum1; i++) {
+					scfg.awbBuffer[i] =
+					(uint32_t)(regptr->paddr);
+				regptr++;
+			}
+		}
+
+		if (axid->bufnum2 > 0) {
+			for (i = 0; i < axid->bufnum2; i++) {
+					scfg.afBuffer[i] =
+					(uint32_t)(regptr->paddr);
+				regptr++;
+			}
+		}
+
+			vfe_stats_setting(&scfg);
+	}
+		break;
+
+	case CMD_STATS_AF_AXI_CFG:
+		break;
+
+	case CMD_FRAME_BUF_RELEASE: {
+		/* preview buffer release */
+		struct msm_frame *b;
+		unsigned long p;
+		struct vfe_cmd_output_ack fack;
+
+			BUG_ON(!data);
+
+		b = (struct msm_frame *)(cmd->value);
+		p = *(unsigned long *)data;
+
+			fack.ybufaddr[0] = (uint32_t) (p + b->planar0_off);
+
+			fack.chromabufaddr[0] = (uint32_t) (p + b->planar1_off);
+
+		if (b->path == OUTPUT_TYPE_P)
+			vfe_output_p_ack(&fack);
+
+		if ((b->path == OUTPUT_TYPE_V)
+			 || (b->path == OUTPUT_TYPE_S))
+			vfe_output_v_ack(&fack);
+	}
+		break;
+
+	case CMD_SNAP_BUF_RELEASE:
+		break;
+
+	case CMD_STATS_BUF_RELEASE: {
+		struct vfe_cmd_stats_wb_exp_ack sack;
+
+			BUG_ON(!data);
+
+		sack.nextWbExpOutputBufferAddr = *(uint32_t *)data;
+		vfe_stats_wb_exp_ack(&sack);
+	}
+		break;
+
+	case CMD_STATS_AF_BUF_RELEASE: {
+		struct vfe_cmd_stats_af_ack ack;
+
+			BUG_ON(!data);
+
+		ack.nextAFOutputBufferAddr = *(uint32_t *)data;
+		vfe_stats_af_ack(&ack);
+	}
+		break;
+
+	case CMD_AXI_CFG_PREVIEW:
+	case CMD_RAW_PICT_AXI_CFG: {
+
+			BUG_ON(!axid);
+
+			if (copy_from_user(&axio, (void __user *)(vfecmd.value),
+				sizeof(axio))) {
+				pr_err("%s %d: copy_from_user failed\n",
+					__func__, __LINE__);
+			return -EFAULT;
+		}
+			/* Validate the data from user space */
+			if (axio.output2.fragmentCount <
+				VFE_MIN_NUM_FRAGMENTS_PER_FRAME ||
+				axio.output2.fragmentCount >
+				VFE_MAX_NUM_FRAGMENTS_PER_FRAME)
+				return -EINVAL;
+
+			vfe_config_axi(OUTPUT_2, axid, &axio);
+			axio.outputDataSize = 0;
+			vfe_axi_output_config(&axio);
+	}
+		break;
+
+	case CMD_AXI_CFG_SNAP: {
+
+			BUG_ON(!axid);
+
+			if (copy_from_user(&axio, (void __user *)(vfecmd.value),
+				sizeof(axio))) {
+				pr_err("%s %d: copy_from_user failed\n",
+					__func__, __LINE__);
+			return -EFAULT;
+		}
+			/* Validate the data from user space */
+			if (axio.output1.fragmentCount <
+				VFE_MIN_NUM_FRAGMENTS_PER_FRAME ||
+				axio.output1.fragmentCount >
+				VFE_MAX_NUM_FRAGMENTS_PER_FRAME ||
+				axio.output2.fragmentCount <
+				VFE_MIN_NUM_FRAGMENTS_PER_FRAME ||
+				axio.output2.fragmentCount >
+				VFE_MAX_NUM_FRAGMENTS_PER_FRAME)
+				return -EINVAL;
+
+			vfe_config_axi(OUTPUT_1_AND_2, axid, &axio);
+			vfe_axi_output_config(&axio);
+	}
+		break;
+
+	case CMD_AXI_CFG_VIDEO: {
+			BUG_ON(!axid);
+
+			if (copy_from_user(&axio, (void __user *)(vfecmd.value),
+				sizeof(axio))) {
+				pr_err("%s %d: copy_from_user failed\n",
+					__func__, __LINE__);
+			return -EFAULT;
+		}
+			/* Validate the data from user space */
+			if (axio.output1.fragmentCount <
+				VFE_MIN_NUM_FRAGMENTS_PER_FRAME ||
+				axio.output1.fragmentCount >
+				VFE_MAX_NUM_FRAGMENTS_PER_FRAME ||
+				axio.output2.fragmentCount <
+				VFE_MIN_NUM_FRAGMENTS_PER_FRAME ||
+				axio.output2.fragmentCount >
+				VFE_MAX_NUM_FRAGMENTS_PER_FRAME)
+				return -EINVAL;
+
+			vfe_config_axi(OUTPUT_1_AND_3, axid, &axio);
+			axio.outputDataSize = 0;
+			vfe_axi_output_config(&axio);
+	}
+		break;
+
+	default:
+		break;
+	} /* switch */
+
+	return rc;
+}
+
+static int vfe_init(struct msm_vfe_callback *presp, struct platform_device *dev)
+{
+	int rc = 0;
+
+	rc = vfe_cmd_init(presp, dev, vfe_syncdata);
+	if (rc < 0)
+		return rc;
+
+	/* Bring up all the required GPIOs and Clocks */
+	rc = msm_camio_enable(dev);
+
+	return rc;
+}
+
+void msm_camvfe_fn_init(struct msm_camvfe_fn *fptr, void *data)
+{
+	fptr->vfe_init    = vfe_init;
+	fptr->vfe_enable  = vfe_enable;
+	fptr->vfe_config  = vfe_config;
+	fptr->vfe_disable = vfe_disable;
+	fptr->vfe_release = vfe_release;
+	vfe_syncdata = data;
+}
+
+void msm_camvpe_fn_init(struct msm_camvpe_fn *fptr, void *data)
+{
+	fptr->vpe_reg		= NULL;
+	fptr->send_frame_to_vpe	= NULL;
+	fptr->vpe_config	= NULL;
+	fptr->vpe_cfg_update	= NULL;
+	fptr->dis		= NULL;
+}
diff --git a/drivers/media/video/msm/msm_vfe8x.h b/drivers/media/video/msm/msm_vfe8x.h
new file mode 100644
index 0000000..1b3148f
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe8x.h
@@ -0,0 +1,909 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __MSM_VFE8X_H__
+#define __MSM_VFE8X_H__
+
+#define TRUE  1
+#define FALSE 0
+#define boolean uint8_t
+
+enum  VFE_STATE {
+	VFE_STATE_IDLE,
+	VFE_STATE_ACTIVE
+};
+
+enum vfe_cmd_id {
+	/*
+	*Important! Command_ID are arranged in order.
+	*Don't change!*/
+	VFE_CMD_ID_START,
+	VFE_CMD_ID_RESET,
+
+	/* bus and camif config */
+	VFE_CMD_ID_AXI_INPUT_CONFIG,
+	VFE_CMD_ID_CAMIF_CONFIG,
+	VFE_CMD_ID_AXI_OUTPUT_CONFIG,
+
+	/* module config  */
+	VFE_CMD_ID_BLACK_LEVEL_CONFIG,
+	VFE_CMD_ID_ROLL_OFF_CONFIG,
+	VFE_CMD_ID_DEMUX_CHANNEL_GAIN_CONFIG,
+	VFE_CMD_ID_DEMOSAIC_CONFIG,
+	VFE_CMD_ID_FOV_CROP_CONFIG,
+	VFE_CMD_ID_MAIN_SCALER_CONFIG,
+	VFE_CMD_ID_WHITE_BALANCE_CONFIG,
+	VFE_CMD_ID_COLOR_CORRECTION_CONFIG,
+	VFE_CMD_ID_LA_CONFIG,
+	VFE_CMD_ID_RGB_GAMMA_CONFIG,
+	VFE_CMD_ID_CHROMA_ENHAN_CONFIG,
+	VFE_CMD_ID_CHROMA_SUPPRESSION_CONFIG,
+	VFE_CMD_ID_ASF_CONFIG,
+	VFE_CMD_ID_SCALER2Y_CONFIG,
+	VFE_CMD_ID_SCALER2CbCr_CONFIG,
+	VFE_CMD_ID_CHROMA_SUBSAMPLE_CONFIG,
+	VFE_CMD_ID_FRAME_SKIP_CONFIG,
+	VFE_CMD_ID_OUTPUT_CLAMP_CONFIG,
+
+	/* test gen */
+	VFE_CMD_ID_TEST_GEN_START,
+
+	VFE_CMD_ID_UPDATE,
+
+	/* ackownledge from upper layer */
+	VFE_CMD_ID_OUTPUT1_ACK,
+	VFE_CMD_ID_OUTPUT2_ACK,
+	VFE_CMD_ID_EPOCH1_ACK,
+	VFE_CMD_ID_EPOCH2_ACK,
+	VFE_CMD_ID_STATS_AUTOFOCUS_ACK,
+	VFE_CMD_ID_STATS_WB_EXP_ACK,
+
+	/* module update commands */
+	VFE_CMD_ID_BLACK_LEVEL_UPDATE,
+	VFE_CMD_ID_DEMUX_CHANNEL_GAIN_UPDATE,
+	VFE_CMD_ID_DEMOSAIC_BPC_UPDATE,
+	VFE_CMD_ID_DEMOSAIC_ABF_UPDATE,
+	VFE_CMD_ID_FOV_CROP_UPDATE,
+	VFE_CMD_ID_WHITE_BALANCE_UPDATE,
+	VFE_CMD_ID_COLOR_CORRECTION_UPDATE,
+	VFE_CMD_ID_LA_UPDATE,
+	VFE_CMD_ID_RGB_GAMMA_UPDATE,
+	VFE_CMD_ID_CHROMA_ENHAN_UPDATE,
+	VFE_CMD_ID_CHROMA_SUPPRESSION_UPDATE,
+	VFE_CMD_ID_MAIN_SCALER_UPDATE,
+	VFE_CMD_ID_SCALER2CbCr_UPDATE,
+	VFE_CMD_ID_SCALER2Y_UPDATE,
+	VFE_CMD_ID_ASF_UPDATE,
+	VFE_CMD_ID_FRAME_SKIP_UPDATE,
+	VFE_CMD_ID_CAMIF_FRAME_UPDATE,
+
+	/* stats update commands */
+	VFE_CMD_ID_STATS_AUTOFOCUS_UPDATE,
+	VFE_CMD_ID_STATS_WB_EXP_UPDATE,
+
+	/* control of start, stop, update, etc... */
+  VFE_CMD_ID_STOP,
+	VFE_CMD_ID_GET_HW_VERSION,
+
+	/* stats */
+	VFE_CMD_ID_STATS_SETTING,
+	VFE_CMD_ID_STATS_AUTOFOCUS_START,
+	VFE_CMD_ID_STATS_AUTOFOCUS_STOP,
+	VFE_CMD_ID_STATS_WB_EXP_START,
+	VFE_CMD_ID_STATS_WB_EXP_STOP,
+
+	VFE_CMD_ID_ASYNC_TIMER_SETTING,
+
+	/* max id  */
+	VFE_CMD_ID_MAX
+};
+
+struct vfe_cmd_hw_version {
+	uint32_t minorVersion;
+	uint32_t majorVersion;
+	uint32_t coreVersion;
+};
+
+enum VFE_CAMIF_SYNC_EDGE {
+	VFE_CAMIF_SYNC_EDGE_ActiveHigh,
+	VFE_CAMIF_SYNC_EDGE_ActiveLow
+};
+
+enum VFE_CAMIF_SYNC_MODE {
+	VFE_CAMIF_SYNC_MODE_APS,
+	VFE_CAMIF_SYNC_MODE_EFS,
+	VFE_CAMIF_SYNC_MODE_ELS,
+	VFE_CAMIF_SYNC_MODE_ILLEGAL
+};
+
+struct vfe_cmds_camif_efs {
+	uint8_t efsendofline;
+	uint8_t efsstartofline;
+	uint8_t efsendofframe;
+	uint8_t efsstartofframe;
+};
+
+struct vfe_cmds_camif_frame {
+	uint16_t pixelsPerLine;
+	uint16_t linesPerFrame;
+};
+
+struct vfe_cmds_camif_window {
+	uint16_t firstpixel;
+	uint16_t lastpixel;
+	uint16_t firstline;
+	uint16_t lastline;
+};
+
+enum CAMIF_SUBSAMPLE_FRAME_SKIP {
+	CAMIF_SUBSAMPLE_FRAME_SKIP_0,
+	CAMIF_SUBSAMPLE_FRAME_SKIP_AllFrames,
+	CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_2Frame,
+	CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_3Frame,
+	CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_4Frame,
+	CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_5Frame,
+	CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_6Frame,
+	CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_7Frame,
+	CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_8Frame,
+	CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_9Frame,
+	CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_10Frame,
+	CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_11Frame,
+	CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_12Frame,
+	CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_13Frame,
+	CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_14Frame,
+	CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_15Frame
+};
+
+struct vfe_cmds_camif_subsample {
+	uint16_t pixelskipmask;
+	uint16_t lineskipmask;
+	enum CAMIF_SUBSAMPLE_FRAME_SKIP frameskip;
+	uint8_t frameskipmode;
+	uint8_t pixelskipwrap;
+};
+
+struct vfe_cmds_camif_epoch {
+	uint8_t  enable;
+	uint16_t lineindex;
+};
+
+struct vfe_cmds_camif_cfg {
+	enum VFE_CAMIF_SYNC_EDGE  vSyncEdge;
+	enum VFE_CAMIF_SYNC_EDGE  hSyncEdge;
+	enum VFE_CAMIF_SYNC_MODE  syncMode;
+	uint8_t vfeSubSampleEnable;
+	uint8_t busSubSampleEnable;
+	uint8_t irqSubSampleEnable;
+	uint8_t binningEnable;
+	uint8_t misrEnable;
+};
+
+struct vfe_cmd_camif_config {
+	struct vfe_cmds_camif_cfg camifConfig;
+	struct vfe_cmds_camif_efs EFS;
+	struct vfe_cmds_camif_frame     frame;
+	struct vfe_cmds_camif_window    window;
+	struct vfe_cmds_camif_subsample subsample;
+	struct vfe_cmds_camif_epoch     epoch1;
+	struct vfe_cmds_camif_epoch     epoch2;
+};
+
+enum VFE_AXI_OUTPUT_MODE {
+	VFE_AXI_OUTPUT_MODE_Output1,
+	VFE_AXI_OUTPUT_MODE_Output2,
+	VFE_AXI_OUTPUT_MODE_Output1AndOutput2,
+	VFE_AXI_OUTPUT_MODE_CAMIFToAXIViaOutput2,
+	VFE_AXI_OUTPUT_MODE_Output2AndCAMIFToAXIViaOutput1,
+	VFE_AXI_OUTPUT_MODE_Output1AndCAMIFToAXIViaOutput2,
+	VFE_AXI_LAST_OUTPUT_MODE_ENUM
+};
+
+enum VFE_RAW_WR_PATH_SEL {
+	VFE_RAW_OUTPUT_DISABLED,
+	VFE_RAW_OUTPUT_ENC_CBCR_PATH,
+	VFE_RAW_OUTPUT_VIEW_CBCR_PATH,
+	VFE_RAW_OUTPUT_PATH_INVALID
+};
+
+enum VFE_RAW_PIXEL_DATA_SIZE {
+	VFE_RAW_PIXEL_DATA_SIZE_8BIT,
+	VFE_RAW_PIXEL_DATA_SIZE_10BIT,
+	VFE_RAW_PIXEL_DATA_SIZE_12BIT,
+};
+
+#define VFE_AXI_OUTPUT_BURST_LENGTH     4
+#define VFE_MAX_NUM_FRAGMENTS_PER_FRAME 4
+#define VFE_MIN_NUM_FRAGMENTS_PER_FRAME 1
+#define VFE_AXI_OUTPUT_CFG_FRAME_COUNT  3
+
+struct vfe_cmds_axi_out_per_component {
+	uint16_t imageWidth;
+	uint16_t imageHeight;
+	uint16_t outRowCount;
+	uint16_t outRowIncrement;
+	uint32_t outFragments[VFE_AXI_OUTPUT_CFG_FRAME_COUNT]
+		[VFE_MAX_NUM_FRAGMENTS_PER_FRAME];
+};
+
+struct vfe_cmds_axi_per_output_path {
+	uint8_t fragmentCount;
+	struct vfe_cmds_axi_out_per_component outputY;
+	struct vfe_cmds_axi_out_per_component outputCbcr;
+};
+
+enum VFE_AXI_BURST_LENGTH {
+	VFE_AXI_BURST_LENGTH_IS_2  = 2,
+	VFE_AXI_BURST_LENGTH_IS_4  = 4,
+	VFE_AXI_BURST_LENGTH_IS_8  = 8,
+	VFE_AXI_BURST_LENGTH_IS_16 = 16
+};
+
+struct vfe_cmd_axi_output_config {
+	enum VFE_AXI_BURST_LENGTH burstLength;
+	enum VFE_AXI_OUTPUT_MODE outputMode;
+	enum VFE_RAW_PIXEL_DATA_SIZE outputDataSize;
+	struct vfe_cmds_axi_per_output_path output1;
+	struct vfe_cmds_axi_per_output_path output2;
+};
+
+struct vfe_cmd_fov_crop_config {
+	uint8_t enable;
+	uint16_t firstPixel;
+	uint16_t lastPixel;
+	uint16_t firstLine;
+	uint16_t lastLine;
+};
+
+struct vfe_cmds_main_scaler_stripe_init {
+	uint16_t MNCounterInit;
+	uint16_t phaseInit;
+};
+
+struct vfe_cmds_scaler_one_dimension {
+	uint8_t  enable;
+	uint16_t inputSize;
+	uint16_t outputSize;
+	uint32_t phaseMultiplicationFactor;
+	uint8_t  interpolationResolution;
+};
+
+struct vfe_cmd_main_scaler_config {
+	uint8_t enable;
+	struct vfe_cmds_scaler_one_dimension    hconfig;
+	struct vfe_cmds_scaler_one_dimension    vconfig;
+	struct vfe_cmds_main_scaler_stripe_init MNInitH;
+	struct vfe_cmds_main_scaler_stripe_init MNInitV;
+};
+
+struct vfe_cmd_scaler2_config {
+	uint8_t enable;
+	struct vfe_cmds_scaler_one_dimension hconfig;
+	struct vfe_cmds_scaler_one_dimension vconfig;
+};
+
+struct vfe_cmd_frame_skip_config {
+	uint8_t output1Period;
+	uint32_t output1Pattern;
+	uint8_t output2Period;
+	uint32_t output2Pattern;
+};
+
+struct vfe_cmd_frame_skip_update {
+	uint32_t output1Pattern;
+	uint32_t output2Pattern;
+};
+
+struct vfe_cmd_output_clamp_config {
+	uint8_t minCh0;
+	uint8_t minCh1;
+	uint8_t minCh2;
+	uint8_t maxCh0;
+	uint8_t maxCh1;
+	uint8_t maxCh2;
+};
+
+struct vfe_cmd_chroma_subsample_config {
+	uint8_t enable;
+	uint8_t cropEnable;
+	uint8_t vsubSampleEnable;
+	uint8_t hsubSampleEnable;
+	uint8_t vCosited;
+	uint8_t hCosited;
+	uint8_t vCositedPhase;
+	uint8_t hCositedPhase;
+	uint16_t cropWidthFirstPixel;
+	uint16_t cropWidthLastPixel;
+	uint16_t cropHeightFirstLine;
+	uint16_t cropHeightLastLine;
+};
+
+enum VFE_START_INPUT_SOURCE {
+	VFE_START_INPUT_SOURCE_CAMIF,
+	VFE_START_INPUT_SOURCE_TESTGEN,
+	VFE_START_INPUT_SOURCE_AXI,
+	VFE_START_INPUT_SOURCE_INVALID
+};
+
+enum VFE_START_OPERATION_MODE {
+	VFE_START_OPERATION_MODE_CONTINUOUS,
+	VFE_START_OPERATION_MODE_SNAPSHOT
+};
+
+enum VFE_START_PIXEL_PATTERN {
+	VFE_BAYER_RGRGRG,
+	VFE_BAYER_GRGRGR,
+	VFE_BAYER_BGBGBG,
+	VFE_BAYER_GBGBGB,
+	VFE_YUV_YCbYCr,
+	VFE_YUV_YCrYCb,
+	VFE_YUV_CbYCrY,
+	VFE_YUV_CrYCbY
+};
+
+enum VFE_BUS_RD_INPUT_PIXEL_PATTERN {
+	VFE_BAYER_RAW,
+	VFE_YUV_INTERLEAVED,
+	VFE_YUV_PSEUDO_PLANAR_Y,
+	VFE_YUV_PSEUDO_PLANAR_CBCR
+};
+
+enum VFE_YUV_INPUT_COSITING_MODE {
+	VFE_YUV_COSITED,
+	VFE_YUV_INTERPOLATED
+};
+
+struct vfe_cmd_start {
+	enum VFE_START_INPUT_SOURCE inputSource;
+	enum VFE_START_OPERATION_MODE operationMode;
+	uint8_t     snapshotCount;
+	enum VFE_START_PIXEL_PATTERN pixel;
+	enum VFE_YUV_INPUT_COSITING_MODE yuvInputCositingMode;
+};
+
+struct vfe_cmd_output_ack {
+	uint32_t ybufaddr[VFE_MAX_NUM_FRAGMENTS_PER_FRAME];
+	uint32_t chromabufaddr[VFE_MAX_NUM_FRAGMENTS_PER_FRAME];
+};
+
+#define VFE_STATS_BUFFER_COUNT 3
+
+struct vfe_cmd_stats_setting {
+	uint16_t frameHDimension;
+	uint16_t frameVDimension;
+	uint8_t  afBusPrioritySelection;
+	uint8_t  afBusPriority;
+	uint8_t  awbBusPrioritySelection;
+	uint8_t  awbBusPriority;
+	uint8_t  histBusPrioritySelection;
+	uint8_t  histBusPriority;
+	uint32_t afBuffer[VFE_STATS_BUFFER_COUNT];
+	uint32_t awbBuffer[VFE_STATS_BUFFER_COUNT];
+	uint32_t histBuffer[VFE_STATS_BUFFER_COUNT];
+};
+
+struct vfe_cmd_stats_af_start {
+	uint8_t  enable;
+	uint8_t  windowMode;
+	uint16_t windowHOffset;
+	uint16_t windowVOffset;
+	uint16_t windowWidth;
+	uint16_t windowHeight;
+	uint8_t  gridForMultiWindows[16];
+	uint8_t     metricSelection;
+	int16_t  metricMax;
+	int8_t   highPassCoef[7];
+	int8_t   bufferHeader;
+};
+
+struct vfe_cmd_stats_af_update {
+	uint8_t  windowMode;
+	uint16_t windowHOffset;
+	uint16_t windowVOffset;
+	uint16_t windowWidth;
+	uint16_t windowHeight;
+};
+
+struct vfe_cmd_stats_wb_exp_start {
+	uint8_t   enable;
+	uint8_t   wbExpRegions;
+	uint8_t   wbExpSubRegion;
+	uint8_t   awbYMin;
+	uint8_t   awbYMax;
+	int8_t    awbMCFG[4];
+	int16_t   awbCCFG[4];
+	int8_t    axwHeader;
+};
+
+struct vfe_cmd_stats_wb_exp_update {
+	uint8_t wbExpRegions;
+	uint8_t wbExpSubRegion;
+	int8_t  awbYMin;
+	int8_t  awbYMax;
+	int8_t  awbMCFG[4];
+	int16_t awbCCFG[4];
+};
+
+struct vfe_cmd_stats_af_ack {
+	uint32_t nextAFOutputBufferAddr;
+};
+
+struct vfe_cmd_stats_wb_exp_ack {
+	uint32_t  nextWbExpOutputBufferAddr;
+};
+
+struct vfe_cmd_black_level_config {
+	uint8_t  enable;
+	uint16_t evenEvenAdjustment;
+	uint16_t evenOddAdjustment;
+	uint16_t oddEvenAdjustment;
+	uint16_t oddOddAdjustment;
+};
+
+/* 13*1  */
+#define  VFE_ROLL_OFF_INIT_TABLE_SIZE  13
+/* 13*16 */
+#define  VFE_ROLL_OFF_DELTA_TABLE_SIZE 208
+
+struct vfe_cmd_roll_off_config {
+	uint8_t  enable;
+	uint16_t gridWidth;
+	uint16_t gridHeight;
+	uint16_t  yDelta;
+	uint8_t  gridXIndex;
+	uint8_t  gridYIndex;
+	uint16_t gridPixelXIndex;
+	uint16_t gridPixelYIndex;
+	uint16_t yDeltaAccum;
+	uint16_t initTableR[VFE_ROLL_OFF_INIT_TABLE_SIZE];
+	uint16_t initTableGr[VFE_ROLL_OFF_INIT_TABLE_SIZE];
+	uint16_t initTableB[VFE_ROLL_OFF_INIT_TABLE_SIZE];
+	uint16_t initTableGb[VFE_ROLL_OFF_INIT_TABLE_SIZE];
+	int16_t  deltaTableR[VFE_ROLL_OFF_DELTA_TABLE_SIZE];
+	int16_t  deltaTableGr[VFE_ROLL_OFF_DELTA_TABLE_SIZE];
+	int16_t  deltaTableB[VFE_ROLL_OFF_DELTA_TABLE_SIZE];
+	int16_t  deltaTableGb[VFE_ROLL_OFF_DELTA_TABLE_SIZE];
+};
+
+struct vfe_cmd_demux_channel_gain_config {
+	uint16_t ch0EvenGain;
+	uint16_t ch0OddGain;
+	uint16_t ch1Gain;
+	uint16_t ch2Gain;
+};
+
+struct vfe_cmds_demosaic_abf {
+	uint8_t   enable;
+	uint8_t   forceOn;
+	uint8_t   shift;
+	uint16_t  lpThreshold;
+	uint16_t  max;
+	uint16_t  min;
+	uint8_t   ratio;
+};
+
+struct vfe_cmds_demosaic_bpc {
+	uint8_t   enable;
+	uint16_t  fmaxThreshold;
+	uint16_t  fminThreshold;
+	uint16_t  redDiffThreshold;
+	uint16_t  blueDiffThreshold;
+	uint16_t  greenDiffThreshold;
+};
+
+struct vfe_cmd_demosaic_config {
+	uint8_t   enable;
+	uint8_t   slopeShift;
+	struct vfe_cmds_demosaic_abf abfConfig;
+	struct vfe_cmds_demosaic_bpc bpcConfig;
+};
+
+struct vfe_cmd_demosaic_bpc_update {
+	struct vfe_cmds_demosaic_bpc bpcUpdate;
+};
+
+struct vfe_cmd_demosaic_abf_update {
+	struct vfe_cmds_demosaic_abf abfUpdate;
+};
+
+struct vfe_cmd_white_balance_config {
+	uint8_t  enable;
+	uint16_t ch2Gain;
+	uint16_t ch1Gain;
+	uint16_t ch0Gain;
+};
+
+enum VFE_COLOR_CORRECTION_COEF_QFACTOR {
+	COEF_IS_Q7_SIGNED,
+	COEF_IS_Q8_SIGNED,
+	COEF_IS_Q9_SIGNED,
+	COEF_IS_Q10_SIGNED
+};
+
+struct vfe_cmd_color_correction_config {
+	uint8_t     enable;
+	enum VFE_COLOR_CORRECTION_COEF_QFACTOR coefQFactor;
+	int16_t  C0;
+	int16_t  C1;
+	int16_t  C2;
+	int16_t  C3;
+	int16_t  C4;
+	int16_t  C5;
+	int16_t  C6;
+	int16_t  C7;
+	int16_t  C8;
+	int16_t  K0;
+	int16_t  K1;
+	int16_t  K2;
+};
+
+#define VFE_LA_TABLE_LENGTH 256
+struct vfe_cmd_la_config {
+	uint8_t enable;
+	int16_t table[VFE_LA_TABLE_LENGTH];
+};
+
+#define VFE_GAMMA_TABLE_LENGTH 256
+enum VFE_RGB_GAMMA_TABLE_SELECT {
+	RGB_GAMMA_CH0_SELECTED,
+	RGB_GAMMA_CH1_SELECTED,
+	RGB_GAMMA_CH2_SELECTED,
+	RGB_GAMMA_CH0_CH1_SELECTED,
+	RGB_GAMMA_CH0_CH2_SELECTED,
+	RGB_GAMMA_CH1_CH2_SELECTED,
+	RGB_GAMMA_CH0_CH1_CH2_SELECTED
+};
+
+struct vfe_cmd_rgb_gamma_config {
+	uint8_t enable;
+	enum VFE_RGB_GAMMA_TABLE_SELECT channelSelect;
+	int16_t table[VFE_GAMMA_TABLE_LENGTH];
+};
+
+struct vfe_cmd_chroma_enhan_config {
+	uint8_t  enable;
+	int16_t am;
+	int16_t ap;
+	int16_t bm;
+	int16_t bp;
+	int16_t cm;
+	int16_t cp;
+	int16_t dm;
+	int16_t dp;
+	int16_t kcr;
+	int16_t kcb;
+	int16_t RGBtoYConversionV0;
+	int16_t RGBtoYConversionV1;
+	int16_t RGBtoYConversionV2;
+	uint8_t RGBtoYConversionOffset;
+};
+
+struct vfe_cmd_chroma_suppression_config {
+	uint8_t enable;
+	uint8_t m1;
+	uint8_t m3;
+	uint8_t n1;
+	uint8_t n3;
+	uint8_t nn1;
+	uint8_t mm1;
+};
+
+struct vfe_cmd_asf_config {
+	uint8_t enable;
+	uint8_t smoothFilterEnabled;
+	uint8_t sharpMode;
+	uint8_t smoothCoefCenter;
+	uint8_t smoothCoefSurr;
+	uint8_t normalizeFactor;
+	uint8_t sharpK1;
+	uint8_t sharpK2;
+	uint8_t sharpThreshE1;
+	int8_t sharpThreshE2;
+	int8_t sharpThreshE3;
+	int8_t sharpThreshE4;
+	int8_t sharpThreshE5;
+	int8_t filter1Coefficients[9];
+	int8_t filter2Coefficients[9];
+	uint8_t  cropEnable;
+	uint16_t cropFirstPixel;
+	uint16_t cropLastPixel;
+	uint16_t cropFirstLine;
+	uint16_t cropLastLine;
+};
+
+struct vfe_cmd_asf_update {
+	uint8_t enable;
+	uint8_t smoothFilterEnabled;
+	uint8_t sharpMode;
+	uint8_t smoothCoefCenter;
+	uint8_t smoothCoefSurr;
+	uint8_t normalizeFactor;
+	uint8_t sharpK1;
+	uint8_t sharpK2;
+	uint8_t sharpThreshE1;
+	int8_t  sharpThreshE2;
+	int8_t  sharpThreshE3;
+	int8_t  sharpThreshE4;
+	int8_t  sharpThreshE5;
+	int8_t  filter1Coefficients[9];
+	int8_t  filter2Coefficients[9];
+	uint8_t cropEnable;
+};
+
+enum VFE_TEST_GEN_SYNC_EDGE {
+	VFE_TEST_GEN_SYNC_EDGE_ActiveHigh,
+	VFE_TEST_GEN_SYNC_EDGE_ActiveLow
+};
+
+struct vfe_cmd_test_gen_start {
+	uint8_t pixelDataSelect;
+	uint8_t systematicDataSelect;
+	enum VFE_TEST_GEN_SYNC_EDGE  hsyncEdge;
+	enum VFE_TEST_GEN_SYNC_EDGE  vsyncEdge;
+	uint16_t numFrame;
+	enum VFE_RAW_PIXEL_DATA_SIZE pixelDataSize;
+	uint16_t imageWidth;
+	uint16_t imageHeight;
+	uint32_t startOfFrameOffset;
+	uint32_t endOfFrameNOffset;
+	uint16_t startOfLineOffset;
+	uint16_t endOfLineNOffset;
+	uint16_t hbi;
+	uint8_t  vblEnable;
+	uint16_t vbl;
+	uint8_t  startOfFrameDummyLine;
+	uint8_t  endOfFrameDummyLine;
+	uint8_t  unicolorBarEnable;
+	uint8_t  colorBarsSplitEnable;
+	uint8_t  unicolorBarSelect;
+	enum VFE_START_PIXEL_PATTERN  colorBarsPixelPattern;
+	uint8_t  colorBarsRotatePeriod;
+	uint16_t testGenRandomSeed;
+};
+
+struct vfe_cmd_bus_pm_start {
+	uint8_t output2YWrPmEnable;
+	uint8_t output2CbcrWrPmEnable;
+	uint8_t output1YWrPmEnable;
+	uint8_t output1CbcrWrPmEnable;
+};
+
+struct vfe_cmd_camif_frame_update {
+	struct vfe_cmds_camif_frame camifFrame;
+};
+
+struct vfe_cmd_sync_timer_setting {
+	uint8_t  whichSyncTimer;
+	uint8_t  operation;
+	uint8_t  polarity;
+	uint16_t repeatCount;
+	uint16_t hsyncCount;
+	uint32_t pclkCount;
+	uint32_t outputDuration;
+};
+
+struct vfe_cmd_async_timer_setting {
+	uint8_t  whichAsyncTimer;
+	uint8_t  operation;
+	uint8_t  polarity;
+	uint16_t repeatCount;
+	uint16_t inactiveCount;
+	uint32_t activeCount;
+};
+
+struct  vfe_frame_skip_counts {
+	uint32_t  totalFrameCount;
+	uint32_t  output1Count;
+	uint32_t  output2Count;
+};
+
+enum VFE_AXI_RD_UNPACK_HBI_SEL {
+	VFE_AXI_RD_HBI_32_CLOCK_CYCLES,
+	VFE_AXI_RD_HBI_64_CLOCK_CYCLES,
+	VFE_AXI_RD_HBI_128_CLOCK_CYCLES,
+	VFE_AXI_RD_HBI_256_CLOCK_CYCLES,
+	VFE_AXI_RD_HBI_512_CLOCK_CYCLES,
+	VFE_AXI_RD_HBI_1024_CLOCK_CYCLES,
+	VFE_AXI_RD_HBI_2048_CLOCK_CYCLES,
+	VFE_AXI_RD_HBI_4096_CLOCK_CYCLES
+};
+
+struct vfe_cmd_axi_input_config {
+	uint32_t  fragAddr[4];
+	uint8_t   totalFragmentCount;
+	uint16_t  ySize;
+	uint16_t  xOffset;
+	uint16_t  xSize;
+	uint16_t  rowIncrement;
+	uint16_t  numOfRows;
+	enum VFE_AXI_BURST_LENGTH burstLength;
+	uint8_t   unpackPhase;
+	enum VFE_AXI_RD_UNPACK_HBI_SEL unpackHbi;
+	enum VFE_RAW_PIXEL_DATA_SIZE   pixelSize;
+	uint8_t   padRepeatCountLeft;
+	uint8_t   padRepeatCountRight;
+	uint8_t   padRepeatCountTop;
+	uint8_t   padRepeatCountBottom;
+	uint8_t   padLeftComponentSelectCycle0;
+	uint8_t   padLeftComponentSelectCycle1;
+	uint8_t   padLeftComponentSelectCycle2;
+	uint8_t   padLeftComponentSelectCycle3;
+	uint8_t   padLeftStopCycle0;
+	uint8_t   padLeftStopCycle1;
+	uint8_t   padLeftStopCycle2;
+	uint8_t   padLeftStopCycle3;
+	uint8_t   padRightComponentSelectCycle0;
+	uint8_t   padRightComponentSelectCycle1;
+	uint8_t   padRightComponentSelectCycle2;
+	uint8_t   padRightComponentSelectCycle3;
+	uint8_t   padRightStopCycle0;
+	uint8_t   padRightStopCycle1;
+	uint8_t   padRightStopCycle2;
+	uint8_t   padRightStopCycle3;
+	uint8_t   padTopLineCount;
+	uint8_t   padBottomLineCount;
+};
+
+struct vfe_interrupt_status {
+	uint8_t camifErrorIrq;
+	uint8_t camifSofIrq;
+	uint8_t camifEolIrq;
+	uint8_t camifEofIrq;
+	uint8_t camifEpoch1Irq;
+	uint8_t camifEpoch2Irq;
+	uint8_t camifOverflowIrq;
+	uint8_t ceIrq;
+	uint8_t regUpdateIrq;
+	uint8_t resetAckIrq;
+	uint8_t encYPingpongIrq;
+	uint8_t encCbcrPingpongIrq;
+	uint8_t viewYPingpongIrq;
+	uint8_t viewCbcrPingpongIrq;
+	uint8_t rdPingpongIrq;
+	uint8_t afPingpongIrq;
+	uint8_t awbPingpongIrq;
+	uint8_t histPingpongIrq;
+	uint8_t encIrq;
+	uint8_t viewIrq;
+	uint8_t busOverflowIrq;
+	uint8_t afOverflowIrq;
+	uint8_t awbOverflowIrq;
+	uint8_t syncTimer0Irq;
+	uint8_t syncTimer1Irq;
+	uint8_t syncTimer2Irq;
+	uint8_t asyncTimer0Irq;
+	uint8_t asyncTimer1Irq;
+	uint8_t asyncTimer2Irq;
+	uint8_t asyncTimer3Irq;
+	uint8_t axiErrorIrq;
+	uint8_t violationIrq;
+	uint8_t anyErrorIrqs;
+	uint8_t anyOutput1PathIrqs;
+	uint8_t anyOutput2PathIrqs;
+	uint8_t anyOutputPathIrqs;
+	uint8_t anyAsyncTimerIrqs;
+	uint8_t anySyncTimerIrqs;
+	uint8_t anyIrqForActiveStatesOnly;
+};
+
+enum VFE_MESSAGE_ID {
+	VFE_MSG_ID_RESET_ACK,
+	VFE_MSG_ID_START_ACK,
+	VFE_MSG_ID_STOP_ACK,
+	VFE_MSG_ID_UPDATE_ACK,
+	VFE_MSG_ID_OUTPUT_P,
+	VFE_MSG_ID_OUTPUT_V,
+	VFE_MSG_ID_OUTPUT_S,
+	VFE_MSG_ID_OUTPUT_T,
+	VFE_MSG_ID_SNAPSHOT_DONE,
+	VFE_MSG_ID_STATS_AUTOFOCUS,
+	VFE_MSG_ID_STATS_WB_EXP,
+	VFE_MSG_ID_EPOCH1,
+	VFE_MSG_ID_EPOCH2,
+	VFE_MSG_ID_SYNC_TIMER0_DONE,
+	VFE_MSG_ID_SYNC_TIMER1_DONE,
+	VFE_MSG_ID_SYNC_TIMER2_DONE,
+	VFE_MSG_ID_ASYNC_TIMER0_DONE,
+	VFE_MSG_ID_ASYNC_TIMER1_DONE,
+	VFE_MSG_ID_ASYNC_TIMER2_DONE,
+	VFE_MSG_ID_ASYNC_TIMER3_DONE,
+	VFE_MSG_ID_AF_OVERFLOW,
+	VFE_MSG_ID_AWB_OVERFLOW,
+	VFE_MSG_ID_AXI_ERROR,
+	VFE_MSG_ID_CAMIF_OVERFLOW,
+	VFE_MSG_ID_VIOLATION,
+	VFE_MSG_ID_CAMIF_ERROR,
+	VFE_MSG_ID_BUS_OVERFLOW,
+	VFE_MSG_ID_SOF_ACK,
+};
+
+struct vfe_msg_stats_autofocus {
+	uint32_t    afBuffer;
+	uint32_t    frameCounter;
+};
+
+struct vfe_msg_stats_wb_exp {
+	uint32_t awbBuffer;
+	uint32_t frameCounter;
+};
+
+struct vfe_frame_bpc_info {
+	uint32_t greenDefectPixelCount;
+	uint32_t redBlueDefectPixelCount;
+};
+
+struct vfe_frame_asf_info {
+	uint32_t  asfMaxEdge;
+	uint32_t  asfHbiCount;
+};
+
+struct vfe_msg_camif_status {
+	uint8_t  camifState;
+	uint32_t pixelCount;
+	uint32_t lineCount;
+};
+
+struct vfe_bus_pm_per_path {
+	uint32_t yWrPmStats0;
+	uint32_t yWrPmStats1;
+	uint32_t cbcrWrPmStats0;
+	uint32_t cbcrWrPmStats1;
+};
+
+struct vfe_bus_performance_monitor {
+	struct vfe_bus_pm_per_path encPathPmInfo;
+	struct vfe_bus_pm_per_path viewPathPmInfo;
+};
+
+struct vfe_irq_thread_msg {
+	uint32_t  vfeIrqStatus;
+	uint32_t  camifStatus;
+	uint32_t  demosaicStatus;
+	uint32_t  asfMaxEdge;
+	struct vfe_bus_performance_monitor pmInfo;
+};
+
+struct vfe_msg_output {
+	uint32_t  yBuffer;
+	uint32_t  cbcrBuffer;
+	struct vfe_frame_bpc_info bpcInfo;
+	struct vfe_frame_asf_info asfInfo;
+	uint32_t  frameCounter;
+	struct vfe_bus_pm_per_path pmData;
+};
+
+struct vfe_message {
+	enum VFE_MESSAGE_ID _d;
+	union {
+		struct vfe_msg_output              msgOutput1;
+		struct vfe_msg_output              msgOutput2;
+		struct vfe_msg_stats_autofocus     msgStatsAf;
+		struct vfe_msg_stats_wb_exp        msgStatsWbExp;
+		struct vfe_msg_camif_status        msgCamifError;
+		struct vfe_bus_performance_monitor msgBusOverflow;
+   } _u;
+};
+
+/* New one for 8k */
+struct msm_vfe_command_8k {
+	int id;
+	uint16_t length;
+	void     *value;
+};
+
+struct vfe_frame_extra {
+	struct vfe_frame_bpc_info bpcInfo;
+	struct vfe_frame_asf_info asfInfo;
+	uint32_t  frameCounter;
+	struct vfe_bus_pm_per_path pmData;
+};
+#endif /* __MSM_VFE8X_H__ */
diff --git a/drivers/media/video/msm/msm_vfe8x_proc.c b/drivers/media/video/msm/msm_vfe8x_proc.c
new file mode 100644
index 0000000..055b244
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe8x_proc.c
@@ -0,0 +1,3889 @@
+/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include "msm_vfe8x_proc.h"
+#include <media/msm_camera.h>
+#include <mach/board.h>
+
+struct isr_queue_cmd {
+	struct list_head list;
+	struct vfe_interrupt_status vfeInterruptStatus;
+	struct vfe_frame_asf_info vfeAsfFrameInfo;
+	struct vfe_frame_bpc_info vfeBpcFrameInfo;
+	struct vfe_msg_camif_status vfeCamifStatusLocal;
+	struct vfe_bus_performance_monitor vfePmData;
+};
+
+struct msm_vfe8x_ctrl {
+	/* bit 1:0 ENC_IRQ_MASK = 0x11:
+	 * generate IRQ when both y and cbcr frame is ready. */
+
+	/* bit 1:0 VIEW_IRQ_MASK= 0x11:
+	 * generate IRQ when both y and cbcr frame is ready. */
+	struct vfe_irq_composite_mask_config vfeIrqCompositeMaskLocal;
+	struct vfe_module_enable vfeModuleEnableLocal;
+	struct vfe_camif_cfg_data   vfeCamifConfigLocal;
+	struct vfe_interrupt_mask   vfeImaskLocal;
+	struct vfe_stats_cmd_data   vfeStatsCmdLocal;
+	struct vfe_bus_cfg_data     vfeBusConfigLocal;
+	struct vfe_cmd_bus_pm_start vfeBusPmConfigLocal;
+	struct vfe_bus_cmd_data     vfeBusCmdLocal;
+	enum vfe_interrupt_name     vfeInterruptNameLocal;
+	uint32_t vfeLaBankSel;
+	struct vfe_gamma_lut_sel  vfeGammaLutSel;
+
+	boolean vfeStartAckPendingFlag;
+	boolean vfeStopAckPending;
+	boolean vfeResetAckPending;
+	boolean vfeUpdateAckPending;
+
+	enum VFE_AXI_OUTPUT_MODE        axiOutputMode;
+	enum VFE_START_OPERATION_MODE   vfeOperationMode;
+
+	atomic_t vfe_serv_interrupt;
+
+	uint32_t            vfeSnapShotCount;
+	uint32_t            vfeRequestedSnapShotCount;
+	boolean             vfeStatsPingPongReloadFlag;
+	uint32_t            vfeFrameId;
+
+	struct vfe_cmd_frame_skip_config vfeFrameSkip;
+	uint32_t vfeFrameSkipPattern;
+	uint8_t  vfeFrameSkipCount;
+	uint8_t  vfeFrameSkipPeriod;
+
+	boolean  vfeTestGenStartFlag;
+	uint32_t vfeImaskPacked;
+	uint32_t vfeImaskCompositePacked;
+	enum VFE_RAW_PIXEL_DATA_SIZE       axiInputDataSize;
+	struct vfe_irq_thread_msg          vfeIrqThreadMsgLocal;
+
+	struct vfe_output_path_combo  viewPath;
+	struct vfe_output_path_combo  encPath;
+	struct vfe_frame_skip_counts vfeDroppedFrameCounts;
+	struct vfe_stats_control afStatsControl;
+	struct vfe_stats_control awbStatsControl;
+
+	enum VFE_STATE  vstate;
+
+	struct msm_vfe_callback *resp;
+	struct vfe_frame_extra extdata;
+
+	struct isr_queue_cmd irqs[10];
+	spinlock_t irqs_lock;
+	int irq_get;
+	int irq_put;
+
+	int vfeirq;
+	void __iomem *vfebase;
+
+	void *syncdata;
+};
+
+static struct msm_vfe8x_ctrl *ctrl;
+static spinlock_t msm_vfe_ctrl_lock;
+
+static void vfe_prog_hw(uint8_t *hwreg, uint32_t *inptr, uint32_t regcnt)
+{
+	/* unsigned long flags; */
+	uint32_t i;
+	uint32_t *p;
+
+	/* @todo This is causing issues, need further investigate */
+	/* spin_lock_irqsave(&ctrl->io_lock, flags); */
+
+	p = (uint32_t *)(hwreg);
+	for (i = 0; i < (regcnt >> 2); i++)
+		writel(*inptr++, p++);
+		/* *p++ = *inptr++; */
+
+	/* spin_unlock_irqrestore(&ctrl->io_lock, flags); */
+}
+
+static void
+vfe_set_bus_pipo_addr(struct vfe_output_path_combo *vpath,
+	struct vfe_output_path_combo *epath)
+{
+	vpath->yPath.hwRegPingAddress = (uint8_t *)
+		(ctrl->vfebase + VFE_BUS_VIEW_Y_WR_PING_ADDR);
+	vpath->yPath.hwRegPongAddress = (uint8_t *)
+		(ctrl->vfebase + VFE_BUS_VIEW_Y_WR_PONG_ADDR);
+	vpath->cbcrPath.hwRegPingAddress = (uint8_t *)
+		(ctrl->vfebase + VFE_BUS_VIEW_CBCR_WR_PING_ADDR);
+	vpath->cbcrPath.hwRegPongAddress = (uint8_t *)
+		(ctrl->vfebase + VFE_BUS_VIEW_CBCR_WR_PONG_ADDR);
+
+	epath->yPath.hwRegPingAddress = (uint8_t *)
+		(ctrl->vfebase + VFE_BUS_ENC_Y_WR_PING_ADDR);
+	epath->yPath.hwRegPongAddress = (uint8_t *)
+		(ctrl->vfebase + VFE_BUS_ENC_Y_WR_PONG_ADDR);
+	epath->cbcrPath.hwRegPingAddress = (uint8_t *)
+		(ctrl->vfebase + VFE_BUS_ENC_CBCR_WR_PING_ADDR);
+	epath->cbcrPath.hwRegPongAddress = (uint8_t *)
+		(ctrl->vfebase + VFE_BUS_ENC_CBCR_WR_PONG_ADDR);
+}
+
+static void vfe_axi_output(struct vfe_cmd_axi_output_config *in,
+	struct vfe_output_path_combo *out1,
+	struct vfe_output_path_combo *out2, uint16_t out)
+{
+	struct vfe_axi_out_cfg cmd;
+
+	uint16_t temp;
+	uint32_t burstLength;
+
+	memset(&cmd, 0, sizeof(cmd));
+	/* force it to burst length 4, hardware does not support it. */
+	burstLength = 1;
+
+	/* AXI Output 2 Y Configuration*/
+	/* VFE_BUS_ENC_Y_WR_PING_ADDR  */
+	cmd.out2YPingAddr = out2->yPath.addressBuffer[0];
+
+	/* VFE_BUS_ENC_Y_WR_PONG_ADDR  */
+	cmd.out2YPongAddr = out2->yPath.addressBuffer[1];
+
+	/* VFE_BUS_ENC_Y_WR_IMAGE_SIZE */
+	cmd.out2YImageHeight = in->output2.outputY.imageHeight;
+	/* convert the image width and row increment to be in
+	 * unit of 64bit (8 bytes) */
+	temp = (in->output2.outputY.imageWidth + (out - 1)) / out;
+	cmd.out2YImageWidthin64bit = temp;
+
+	/* VFE_BUS_ENC_Y_WR_BUFFER_CFG */
+	cmd.out2YBurstLength = burstLength;
+	cmd.out2YNumRows = in->output2.outputY.outRowCount;
+	temp = (in->output2.outputY.outRowIncrement + (out - 1)) / out;
+	cmd.out2YRowIncrementIn64bit = temp;
+
+	/* AXI Output 2 Cbcr Configuration*/
+	/* VFE_BUS_ENC_Cbcr_WR_PING_ADDR  */
+	cmd.out2CbcrPingAddr = out2->cbcrPath.addressBuffer[0];
+
+	/* VFE_BUS_ENC_Cbcr_WR_PONG_ADDR  */
+	cmd.out2CbcrPongAddr = out2->cbcrPath.addressBuffer[1];
+
+	/* VFE_BUS_ENC_Cbcr_WR_IMAGE_SIZE */
+	cmd.out2CbcrImageHeight = in->output2.outputCbcr.imageHeight;
+	temp = (in->output2.outputCbcr.imageWidth + (out - 1)) / out;
+	cmd.out2CbcrImageWidthIn64bit = temp;
+
+	/* VFE_BUS_ENC_Cbcr_WR_BUFFER_CFG */
+	cmd.out2CbcrBurstLength = burstLength;
+	cmd.out2CbcrNumRows = in->output2.outputCbcr.outRowCount;
+	temp = (in->output2.outputCbcr.outRowIncrement + (out - 1)) / out;
+	cmd.out2CbcrRowIncrementIn64bit = temp;
+
+	/* AXI Output 1 Y Configuration */
+	/* VFE_BUS_VIEW_Y_WR_PING_ADDR  */
+	cmd.out1YPingAddr = out1->yPath.addressBuffer[0];
+
+	/* VFE_BUS_VIEW_Y_WR_PONG_ADDR */
+	cmd.out1YPongAddr = out1->yPath.addressBuffer[1];
+
+	/* VFE_BUS_VIEW_Y_WR_IMAGE_SIZE */
+	cmd.out1YImageHeight = in->output1.outputY.imageHeight;
+	temp = (in->output1.outputY.imageWidth + (out - 1)) / out;
+	cmd.out1YImageWidthin64bit = temp;
+
+	/* VFE_BUS_VIEW_Y_WR_BUFFER_CFG     */
+	cmd.out1YBurstLength = burstLength;
+	cmd.out1YNumRows = in->output1.outputY.outRowCount;
+
+	temp = (in->output1.outputY.outRowIncrement + (out - 1)) / out;
+	cmd.out1YRowIncrementIn64bit = temp;
+
+	/* AXI Output 1 Cbcr Configuration*/
+	cmd.out1CbcrPingAddr = out1->cbcrPath.addressBuffer[0];
+
+	/* VFE_BUS_VIEW_Cbcr_WR_PONG_ADDR  */
+	cmd.out1CbcrPongAddr = out1->cbcrPath.addressBuffer[1];
+
+	/* VFE_BUS_VIEW_Cbcr_WR_IMAGE_SIZE */
+	cmd.out1CbcrImageHeight = in->output1.outputCbcr.imageHeight;
+	temp = (in->output1.outputCbcr.imageWidth + (out - 1)) / out;
+	cmd.out1CbcrImageWidthIn64bit = temp;
+
+	cmd.out1CbcrBurstLength = burstLength;
+	cmd.out1CbcrNumRows = in->output1.outputCbcr.outRowCount;
+	temp = (in->output1.outputCbcr.outRowIncrement + (out - 1)) / out;
+
+	cmd.out1CbcrRowIncrementIn64bit = temp;
+
+	vfe_prog_hw(ctrl->vfebase + VFE_BUS_ENC_Y_WR_PING_ADDR,
+		(uint32_t *)&cmd, sizeof(cmd));
+}
+
+static void vfe_reg_bus_cfg(struct vfe_bus_cfg_data *in)
+{
+	struct vfe_axi_bus_cfg cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.stripeRdPathEn      = in->stripeRdPathEn;
+	cmd.encYWrPathEn        = in->encYWrPathEn;
+	cmd.encCbcrWrPathEn     = in->encCbcrWrPathEn;
+	cmd.viewYWrPathEn       = in->viewYWrPathEn;
+	cmd.viewCbcrWrPathEn    = in->viewCbcrWrPathEn;
+	cmd.rawPixelDataSize    = (uint32_t)in->rawPixelDataSize;
+	cmd.rawWritePathSelect  = (uint32_t)in->rawWritePathSelect;
+
+	/*  program vfe_bus_cfg */
+	writel(*((uint32_t *)&cmd), ctrl->vfebase + VFE_BUS_CFG);
+}
+
+static void vfe_reg_camif_config(struct vfe_camif_cfg_data *in)
+{
+	struct VFE_CAMIFConfigType cfg;
+
+	memset(&cfg, 0, sizeof(cfg));
+
+	cfg.VSyncEdge = in->camifCfgFromCmd.vSyncEdge;
+
+	cfg.HSyncEdge = in->camifCfgFromCmd.hSyncEdge;
+
+	cfg.syncMode = in->camifCfgFromCmd.syncMode;
+
+	cfg.vfeSubsampleEnable = in->camifCfgFromCmd.vfeSubSampleEnable;
+
+	cfg.busSubsampleEnable = in->camifCfgFromCmd.busSubSampleEnable;
+
+	cfg.camif2vfeEnable = in->camif2OutputEnable;
+
+	cfg.camif2busEnable = in->camif2BusEnable;
+
+	cfg.irqSubsampleEnable = in->camifCfgFromCmd.irqSubSampleEnable;
+
+	cfg.binningEnable = in->camifCfgFromCmd.binningEnable;
+
+	cfg.misrEnable = in->camifCfgFromCmd.misrEnable;
+
+	/*  program camif_config */
+	writel(*((uint32_t *)&cfg), ctrl->vfebase + CAMIF_CONFIG);
+}
+
+static void vfe_reg_bus_cmd(struct vfe_bus_cmd_data *in)
+{
+	struct vfe_buscmd cmd;
+	memset(&cmd, 0, sizeof(cmd));
+
+	cmd.stripeReload        = in->stripeReload;
+	cmd.busPingpongReload   = in->busPingpongReload;
+	cmd.statsPingpongReload = in->statsPingpongReload;
+
+	writel(*((uint32_t *)&cmd), ctrl->vfebase + VFE_BUS_CMD);
+
+	CDBG("bus command = 0x%x\n", (*((uint32_t *)&cmd)));
+
+	/* this is needed, as the control bits are pulse based.
+	 * Don't want to reload bus pingpong again. */
+	in->busPingpongReload = 0;
+	in->statsPingpongReload = 0;
+	in->stripeReload = 0;
+}
+
+static void vfe_reg_module_cfg(struct vfe_module_enable *in)
+{
+	struct vfe_mod_enable ena;
+
+	memset(&ena, 0, sizeof(ena));
+
+	ena.blackLevelCorrectionEnable = in->blackLevelCorrectionEnable;
+	ena.lensRollOffEnable          = in->lensRollOffEnable;
+	ena.demuxEnable                = in->demuxEnable;
+	ena.chromaUpsampleEnable       = in->chromaUpsampleEnable;
+	ena.demosaicEnable             = in->demosaicEnable;
+	ena.statsEnable                = in->statsEnable;
+	ena.cropEnable                 = in->cropEnable;
+	ena.mainScalerEnable           = in->mainScalerEnable;
+	ena.whiteBalanceEnable         = in->whiteBalanceEnable;
+	ena.colorCorrectionEnable      = in->colorCorrectionEnable;
+	ena.yHistEnable                = in->yHistEnable;
+	ena.skinToneEnable             = in->skinToneEnable;
+	ena.lumaAdaptationEnable       = in->lumaAdaptationEnable;
+	ena.rgbLUTEnable               = in->rgbLUTEnable;
+	ena.chromaEnhanEnable          = in->chromaEnhanEnable;
+	ena.asfEnable                  = in->asfEnable;
+	ena.chromaSuppressionEnable    = in->chromaSuppressionEnable;
+	ena.chromaSubsampleEnable      = in->chromaSubsampleEnable;
+	ena.scaler2YEnable             = in->scaler2YEnable;
+	ena.scaler2CbcrEnable          = in->scaler2CbcrEnable;
+
+	writel(*((uint32_t *)&ena), ctrl->vfebase + VFE_MODULE_CFG);
+}
+
+static void vfe_program_dmi_cfg(enum VFE_DMI_RAM_SEL bankSel)
+{
+	/* set bit 8 for auto increment. */
+	uint32_t value = (uint32_t) ctrl->vfebase + VFE_DMI_CFG_DEFAULT;
+
+	value += (uint32_t)bankSel;
+	/* CDBG("dmi cfg input bank is  0x%x\n", bankSel); */
+
+	writel(value, ctrl->vfebase + VFE_DMI_CFG);
+	writel(0, ctrl->vfebase + VFE_DMI_ADDR);
+}
+
+static void vfe_write_lens_roll_off_table(struct vfe_cmd_roll_off_config *in)
+{
+	uint16_t i;
+	uint32_t data;
+
+	uint16_t *initGr = in->initTableGr;
+	uint16_t *initGb = in->initTableGb;
+	uint16_t *initB =  in->initTableB;
+	uint16_t *initR =  in->initTableR;
+
+	int16_t *pDeltaGr = in->deltaTableGr;
+	int16_t *pDeltaGb = in->deltaTableGb;
+	int16_t *pDeltaB =  in->deltaTableB;
+	int16_t *pDeltaR =  in->deltaTableR;
+
+	vfe_program_dmi_cfg(ROLLOFF_RAM);
+
+	/* first pack and write init table */
+	for (i = 0; i < VFE_ROLL_OFF_INIT_TABLE_SIZE; i++) {
+		data = (((uint32_t)(*initR)) & 0x0000FFFF) |
+			(((uint32_t)(*initGr)) << 16);
+		initR++;
+		initGr++;
+
+		writel(data, ctrl->vfebase + VFE_DMI_DATA_LO);
+
+		data = (((uint32_t)(*initB)) & 0x0000FFFF) |
+			(((uint32_t)(*initGb))<<16);
+		initB++;
+		initGb++;
+
+		writel(data, ctrl->vfebase + VFE_DMI_DATA_LO);
+	}
+
+	/* there are gaps between the init table and delta table,
+	 * set the offset for delta table. */
+	writel(LENS_ROLL_OFF_DELTA_TABLE_OFFSET, ctrl->vfebase + VFE_DMI_ADDR);
+
+	/* pack and write delta table */
+	for (i = 0; i < VFE_ROLL_OFF_DELTA_TABLE_SIZE; i++) {
+		data = (((int)(*pDeltaR)) & 0x0000FFFF) |
+			(((int)(*pDeltaGr))<<16);
+		pDeltaR++;
+		pDeltaGr++;
+
+		writel(data, ctrl->vfebase + VFE_DMI_DATA_LO);
+
+		data = (((int)(*pDeltaB)) & 0x0000FFFF) |
+			(((int)(*pDeltaGb))<<16);
+		pDeltaB++;
+		pDeltaGb++;
+
+		writel(data, ctrl->vfebase + VFE_DMI_DATA_LO);
+	}
+
+	/* After DMI transfer, to make it safe, need to set the
+	 * DMI_CFG to unselect any SRAM
+	 */
+	/* unselect the SRAM Bank. */
+	writel(VFE_DMI_CFG_DEFAULT, ctrl->vfebase + VFE_DMI_CFG);
+}
+
+static void vfe_set_default_reg_values(void)
+{
+	writel(0x800080, ctrl->vfebase + VFE_DEMUX_GAIN_0);
+	writel(0x800080, ctrl->vfebase + VFE_DEMUX_GAIN_1);
+	writel(0xFFFFF, ctrl->vfebase + VFE_CGC_OVERRIDE);
+
+	/* default frame drop period and pattern */
+	writel(0x1f, ctrl->vfebase + VFE_FRAMEDROP_ENC_Y_CFG);
+	writel(0x1f, ctrl->vfebase + VFE_FRAMEDROP_ENC_CBCR_CFG);
+	writel(0xFFFFFFFF, ctrl->vfebase + VFE_FRAMEDROP_ENC_Y_PATTERN);
+	writel(0xFFFFFFFF, ctrl->vfebase + VFE_FRAMEDROP_ENC_CBCR_PATTERN);
+	writel(0x1f, ctrl->vfebase + VFE_FRAMEDROP_VIEW_Y_CFG);
+	writel(0x1f, ctrl->vfebase + VFE_FRAMEDROP_VIEW_CBCR_CFG);
+	writel(0xFFFFFFFF, ctrl->vfebase + VFE_FRAMEDROP_VIEW_Y_PATTERN);
+	writel(0xFFFFFFFF, ctrl->vfebase + VFE_FRAMEDROP_VIEW_CBCR_PATTERN);
+	writel(0, ctrl->vfebase + VFE_CLAMP_MIN_CFG);
+	writel(0xFFFFFF, ctrl->vfebase + VFE_CLAMP_MAX_CFG);
+}
+
+static void vfe_config_demux(uint32_t period, uint32_t even, uint32_t odd)
+{
+	writel(period, ctrl->vfebase + VFE_DEMUX_CFG);
+	writel(even, ctrl->vfebase + VFE_DEMUX_EVEN_CFG);
+	writel(odd, ctrl->vfebase + VFE_DEMUX_ODD_CFG);
+}
+
+static void vfe_pm_stop(void)
+{
+	writel(VFE_PERFORMANCE_MONITOR_STOP, ctrl->vfebase + VFE_BUS_PM_CMD);
+}
+
+static void vfe_camif_stop_immediately(void)
+{
+	writel(CAMIF_COMMAND_STOP_IMMEDIATELY, ctrl->vfebase + CAMIF_COMMAND);
+	writel(0, ctrl->vfebase + VFE_CGC_OVERRIDE);
+}
+
+static void vfe_program_reg_update_cmd(uint32_t value)
+{
+	writel(value, ctrl->vfebase + VFE_REG_UPDATE_CMD);
+}
+
+static void vfe_program_global_reset_cmd(uint32_t value)
+{
+	writel(value, ctrl->vfebase + VFE_GLOBAL_RESET_CMD);
+}
+
+static void vfe_program_axi_cmd(uint32_t value)
+{
+	writel(value, ctrl->vfebase + VFE_AXI_CMD);
+}
+
+static void vfe_program_irq_composite_mask(uint32_t value)
+{
+	writel(value, ctrl->vfebase + VFE_IRQ_COMPOSITE_MASK);
+}
+
+static inline void vfe_program_irq_mask(uint32_t value)
+{
+	writel(value, ctrl->vfebase + VFE_IRQ_MASK);
+}
+
+static uint32_t vfe_read_axi_status(void)
+{
+	return readl(ctrl->vfebase + VFE_AXI_STATUS);
+}
+
+static void
+vfe_set_stats_pingpong_address(struct vfe_stats_control *afControl,
+	struct vfe_stats_control *awbControl)
+{
+	afControl->hwRegPingAddress = (uint8_t *)
+		(ctrl->vfebase + VFE_BUS_STATS_AF_WR_PING_ADDR);
+	afControl->hwRegPongAddress = (uint8_t *)
+		(ctrl->vfebase + VFE_BUS_STATS_AF_WR_PONG_ADDR);
+
+	awbControl->hwRegPingAddress = (uint8_t *)
+		(ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PING_ADDR);
+	awbControl->hwRegPongAddress = (uint8_t *)
+		(ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PONG_ADDR);
+}
+
+static void vfe_program_lut_bank_sel(struct vfe_gamma_lut_sel *in)
+{
+	struct VFE_GammaLutSelect_ConfigCmdType cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	cmd.ch0BankSelect = in->ch0BankSelect;
+	cmd.ch1BankSelect = in->ch1BankSelect;
+	cmd.ch2BankSelect = in->ch2BankSelect;
+	CDBG("VFE gamma lut bank selection is 0x%x\n", *((uint32_t *)&cmd));
+	vfe_prog_hw(ctrl->vfebase + VFE_LUT_BANK_SEL,
+		(uint32_t *)&cmd, sizeof(cmd));
+}
+
+static void vfe_program_stats_cmd(struct vfe_stats_cmd_data *in)
+{
+	struct VFE_StatsCmdType stats;
+	memset(&stats, 0, sizeof(stats));
+
+	stats.autoFocusEnable        = in->autoFocusEnable;
+	stats.axwEnable              = in->axwEnable;
+	stats.histEnable             = in->histEnable;
+	stats.clearHistEnable        = in->clearHistEnable;
+	stats.histAutoClearEnable    = in->histAutoClearEnable;
+	stats.colorConversionEnable  = in->colorConversionEnable;
+
+	writel(*((uint32_t *)&stats), ctrl->vfebase + VFE_STATS_CMD);
+}
+
+static void vfe_pm_start(struct vfe_cmd_bus_pm_start *in)
+{
+	struct VFE_Bus_Pm_ConfigCmdType cmd;
+	memset(&cmd, 0, sizeof(struct VFE_Bus_Pm_ConfigCmdType));
+
+	cmd.output2YWrPmEnable     = in->output2YWrPmEnable;
+	cmd.output2CbcrWrPmEnable  = in->output2CbcrWrPmEnable;
+	cmd.output1YWrPmEnable     = in->output1YWrPmEnable;
+	cmd.output1CbcrWrPmEnable  = in->output1CbcrWrPmEnable;
+
+	vfe_prog_hw(ctrl->vfebase + VFE_BUS_PM_CFG,
+		(uint32_t *)&cmd, sizeof(cmd));
+}
+
+static void vfe_8k_pm_start(struct vfe_cmd_bus_pm_start *in)
+{
+	in->output1CbcrWrPmEnable = ctrl->vfeBusConfigLocal.viewCbcrWrPathEn;
+	in->output1YWrPmEnable    = ctrl->vfeBusConfigLocal.viewYWrPathEn;
+	in->output2CbcrWrPmEnable = ctrl->vfeBusConfigLocal.encCbcrWrPathEn;
+	in->output2YWrPmEnable    = ctrl->vfeBusConfigLocal.encYWrPathEn;
+
+	if (in->output1CbcrWrPmEnable || in->output1YWrPmEnable)
+		ctrl->viewPath.pmEnabled = TRUE;
+
+	if (in->output2CbcrWrPmEnable || in->output2YWrPmEnable)
+		ctrl->encPath.pmEnabled = TRUE;
+
+	vfe_pm_start(in);
+
+	writel(VFE_PERFORMANCE_MONITOR_GO, ctrl->vfebase + VFE_BUS_PM_CMD);
+}
+
+static uint32_t vfe_irq_pack(struct vfe_interrupt_mask data)
+{
+	struct vfe_irqenable packedData;
+
+	memset(&packedData, 0, sizeof(packedData));
+
+	packedData.camifErrorIrq          = data.camifErrorIrq;
+	packedData.camifSofIrq            = data.camifSofIrq;
+	packedData.camifEolIrq            = data.camifEolIrq;
+	packedData.camifEofIrq            = data.camifEofIrq;
+	packedData.camifEpoch1Irq         = data.camifEpoch1Irq;
+	packedData.camifEpoch2Irq         = data.camifEpoch2Irq;
+	packedData.camifOverflowIrq       = data.camifOverflowIrq;
+	packedData.ceIrq                  = data.ceIrq;
+	packedData.regUpdateIrq           = data.regUpdateIrq;
+	packedData.resetAckIrq            = data.resetAckIrq;
+	packedData.encYPingpongIrq        = data.encYPingpongIrq;
+	packedData.encCbcrPingpongIrq     = data.encCbcrPingpongIrq;
+	packedData.viewYPingpongIrq       = data.viewYPingpongIrq;
+	packedData.viewCbcrPingpongIrq    = data.viewCbcrPingpongIrq;
+	packedData.rdPingpongIrq          = data.rdPingpongIrq;
+	packedData.afPingpongIrq          = data.afPingpongIrq;
+	packedData.awbPingpongIrq         = data.awbPingpongIrq;
+	packedData.histPingpongIrq        = data.histPingpongIrq;
+	packedData.encIrq                 = data.encIrq;
+	packedData.viewIrq                = data.viewIrq;
+	packedData.busOverflowIrq         = data.busOverflowIrq;
+	packedData.afOverflowIrq          = data.afOverflowIrq;
+	packedData.awbOverflowIrq         = data.awbOverflowIrq;
+	packedData.syncTimer0Irq          = data.syncTimer0Irq;
+	packedData.syncTimer1Irq          = data.syncTimer1Irq;
+	packedData.syncTimer2Irq          = data.syncTimer2Irq;
+	packedData.asyncTimer0Irq         = data.asyncTimer0Irq;
+	packedData.asyncTimer1Irq         = data.asyncTimer1Irq;
+	packedData.asyncTimer2Irq         = data.asyncTimer2Irq;
+	packedData.asyncTimer3Irq         = data.asyncTimer3Irq;
+	packedData.axiErrorIrq            = data.axiErrorIrq;
+	packedData.violationIrq           = data.violationIrq;
+
+	return *((uint32_t *)&packedData);
+}
+
+static uint32_t
+vfe_irq_composite_pack(struct vfe_irq_composite_mask_config data)
+{
+	struct VFE_Irq_Composite_MaskType packedData;
+
+	memset(&packedData, 0, sizeof(packedData));
+
+	packedData.encIrqComMaskBits   = data.encIrqComMask;
+	packedData.viewIrqComMaskBits  = data.viewIrqComMask;
+	packedData.ceDoneSelBits       = data.ceDoneSel;
+
+	return *((uint32_t *)&packedData);
+}
+
+static void vfe_addr_convert(struct msm_vfe_phy_info *pinfo,
+				enum vfe_resp_msg type, void *data, void **ext,
+				int *elen)
+{
+	switch (type) {
+	case VFE_MSG_OUTPUT_P:
+	case VFE_MSG_OUTPUT_V:{
+		pinfo->planar0_off =
+			((struct vfe_message *)data)->_u.msgOutput2.yBuffer;
+		pinfo->planar1_off =
+			((struct vfe_message *)data)->_u.msgOutput2.
+			cbcrBuffer;
+		pinfo->planar2_off = pinfo->planar0_off;
+		ctrl->extdata.bpcInfo =
+			((struct vfe_message *)data)->_u.msgOutput2.bpcInfo;
+		ctrl->extdata.asfInfo =
+			((struct vfe_message *)data)->_u.msgOutput2.asfInfo;
+		ctrl->extdata.frameCounter =
+			((struct vfe_message *)data)->_u.msgOutput2.
+			frameCounter;
+		ctrl->extdata.pmData =
+		((struct vfe_message *)data)->_u.msgOutput2.pmData;
+		*ext = &ctrl->extdata;
+		*elen = sizeof(ctrl->extdata);
+	}
+		break;
+
+	case VFE_MSG_STATS_AF:
+		pinfo->sbuf_phy =
+		((struct vfe_message *)data)->_u.msgStatsAf.afBuffer;
+		break;
+
+	case VFE_MSG_STATS_WE:
+		pinfo->sbuf_phy =
+		((struct vfe_message *)data)->_u.msgStatsWbExp.awbBuffer;
+		break;
+
+	default:
+		break;
+	} /* switch */
+}
+
+static boolean vfe_send_preview_msg(struct msm_vfe_resp *rp,
+			struct vfe_message *msg, void *data);
+static boolean vfe_send_video_msg(struct msm_vfe_resp *rp,
+			struct vfe_message *msg, void *data);
+static boolean vfe_send_mainimage_msg(struct msm_vfe_resp *rp,
+			struct vfe_message *msg, void *data);
+static boolean vfe_send_thumbnail_msg(struct msm_vfe_resp *rp,
+			struct vfe_message *msg, void *data);
+static boolean vfe_send_af_stats_msg(struct msm_vfe_resp *rp,
+			struct vfe_message *msg, void *data);
+static boolean vfe_send_awb_stats_msg(struct msm_vfe_resp *rp,
+			struct vfe_message *msg, void *data);
+static boolean vfe_send_camif_error_msg(struct msm_vfe_resp *rp,
+			struct vfe_message *msg, void *data);
+static boolean vfe_send_bus_overflow_msg(struct msm_vfe_resp *rp,
+			struct vfe_message *msg, void *data);
+static boolean vfe_send_sof_msg(struct msm_vfe_resp *rp,
+			struct vfe_message *msg, void *data);
+
+static boolean invalid(struct msm_vfe_resp *rp,
+		struct vfe_message *_m, void *_d)
+{
+	BUG_ON(1); /* this function should not be called. */
+	return FALSE;
+}
+
+static struct {
+	boolean (*fn)(struct msm_vfe_resp *rp, struct vfe_message *msg,
+		void *data);
+	enum vfe_resp_msg rt; /* reponse type */
+} vfe_funcs[] = {
+	[VFE_MSG_ID_RESET_ACK] = { NULL, VFE_MSG_GENERAL },
+	[VFE_MSG_ID_START_ACK] = { NULL, VFE_MSG_GENERAL },
+	[VFE_MSG_ID_STOP_ACK] = { NULL, VFE_MSG_GENERAL },
+	[VFE_MSG_ID_UPDATE_ACK] = { NULL, VFE_MSG_GENERAL },
+	[VFE_MSG_ID_OUTPUT_P] = { vfe_send_preview_msg, VFE_MSG_OUTPUT_P },
+	[VFE_MSG_ID_OUTPUT_V] = { vfe_send_video_msg, VFE_MSG_OUTPUT_V },
+	[VFE_MSG_ID_OUTPUT_S] = { vfe_send_mainimage_msg, VFE_MSG_OUTPUT_S },
+	[VFE_MSG_ID_OUTPUT_T] = { vfe_send_thumbnail_msg, VFE_MSG_OUTPUT_T },
+	[VFE_MSG_ID_SNAPSHOT_DONE] = { NULL, VFE_MSG_SNAPSHOT },
+	[VFE_MSG_ID_STATS_AUTOFOCUS] = { vfe_send_af_stats_msg,
+		VFE_MSG_STATS_AF },
+	[VFE_MSG_ID_STATS_WB_EXP] = { vfe_send_awb_stats_msg,
+		VFE_MSG_STATS_WE },
+	[VFE_MSG_ID_EPOCH1] = { NULL, VFE_MSG_GENERAL },
+	[VFE_MSG_ID_EPOCH2] = { NULL, VFE_MSG_GENERAL },
+	[VFE_MSG_ID_SYNC_TIMER0_DONE] = { invalid },
+	[VFE_MSG_ID_SYNC_TIMER1_DONE] = { invalid },
+	[VFE_MSG_ID_SYNC_TIMER2_DONE] = { invalid },
+	[VFE_MSG_ID_ASYNC_TIMER0_DONE] = { invalid },
+	[VFE_MSG_ID_ASYNC_TIMER1_DONE] = { invalid },
+	[VFE_MSG_ID_ASYNC_TIMER2_DONE] = { invalid },
+	[VFE_MSG_ID_ASYNC_TIMER3_DONE] = { invalid },
+	[VFE_MSG_ID_AF_OVERFLOW] = { NULL, VFE_MSG_GENERAL },
+	[VFE_MSG_ID_AWB_OVERFLOW] = { NULL, VFE_MSG_GENERAL },
+	[VFE_MSG_ID_AXI_ERROR] = { NULL, VFE_MSG_GENERAL },
+	[VFE_MSG_ID_CAMIF_OVERFLOW] = { NULL, VFE_MSG_GENERAL },
+	[VFE_MSG_ID_VIOLATION] = { invalid },
+	[VFE_MSG_ID_CAMIF_ERROR] = { vfe_send_camif_error_msg,
+		VFE_MSG_GENERAL },
+	[VFE_MSG_ID_BUS_OVERFLOW] = { vfe_send_bus_overflow_msg,
+		VFE_MSG_GENERAL },
+	[VFE_MSG_ID_SOF_ACK] = { vfe_send_sof_msg,
+		VFE_MSG_GENERAL },
+};
+
+static void vfe_proc_ops(enum VFE_MESSAGE_ID id, void *data)
+{
+	struct msm_vfe_resp *rp;
+	struct vfe_message *msg;
+
+	if (id >= ARRAY_SIZE(vfe_funcs) || vfe_funcs[id].fn == invalid) {
+		pr_err("%s: invalid VFE message id %d\n", __func__, id);
+		return;
+	}
+
+	/* In 8k, OUTPUT1 & OUTPUT2 messages arrive before SNAPSHOT_DONE.
+	 * We don't send such messages to the user.  Note that we can do
+	 * this in the vfe_func[] callback, but that would cause us to
+	 * allocate and then immediately free the msm_vfe_resp structure,
+	 * which is wasteful.
+	 */
+	if ((ctrl->vfeOperationMode == VFE_START_OPERATION_MODE_SNAPSHOT) &&
+			(id == VFE_MSG_ID_OUTPUT_T ||
+			 id == VFE_MSG_ID_OUTPUT_S))
+		return;
+
+	rp = ctrl->resp->vfe_alloc(sizeof(*rp) +
+					(vfe_funcs[id].fn ? sizeof(*msg) : 0),
+					ctrl->syncdata,
+					GFP_ATOMIC);
+	if (!rp) {
+		pr_err("%s: out of memory\n", __func__);
+		return;
+	}
+
+	rp->type = vfe_funcs[id].rt;
+	rp->evt_msg.type = MSM_CAMERA_MSG;
+	rp->evt_msg.msg_id = id;
+
+	if (!vfe_funcs[id].fn) {
+		rp->evt_msg.len = 0;
+		rp->evt_msg.data = 0;
+	} else {
+		/* populate the message accordingly */
+		if (vfe_funcs[id].fn)
+			rp->evt_msg.data = msg =
+				(struct vfe_message *)(rp + 1);
+		else
+			rp->evt_msg.data = msg = 0;
+		rp->evt_msg.len = sizeof(*msg);
+		msg->_d = id;
+		if (vfe_funcs[id].fn(rp, msg, data) == FALSE) {
+			pr_warning("%s: freeing memory: handler for %d "
+				"returned false\n", __func__, id);
+			ctrl->resp->vfe_free(rp);
+			return;
+		}
+}
+
+	ctrl->resp->vfe_resp(rp, MSM_CAM_Q_VFE_MSG, ctrl->syncdata, GFP_KERNEL);
+}
+
+static boolean vfe_send_bus_overflow_msg(struct msm_vfe_resp *rp,
+			struct vfe_message *msg,
+			void *data)
+{
+#if 0
+	memcpy(&(msg->_u.msgBusOverflow),
+		&ctrl->vfePmData, sizeof(ctrl->vfePmData));
+#endif
+	return TRUE;
+}
+
+static boolean vfe_send_sof_msg(struct msm_vfe_resp *rp,
+			struct vfe_message *msg,
+			void *data)
+{
+	return TRUE;
+}
+static boolean vfe_send_camif_error_msg(struct msm_vfe_resp *rp,
+			struct vfe_message *msg,
+			void *data)
+{
+#if 0
+	memcpy(&(msg->_u.msgCamifError),
+	       &ctrl->vfeCamifStatusLocal, sizeof(ctrl->vfeCamifStatusLocal));
+#endif
+	return TRUE;
+}
+
+static void vfe_process_error_irq(struct vfe_interrupt_status *irqstatus)
+{
+	/* all possible error irq.  Note error irqs are not enabled, it is
+	 * checked only when other interrupts are present. */
+	if (irqstatus->afOverflowIrq)
+		vfe_proc_ops(VFE_MSG_ID_AF_OVERFLOW, NULL);
+
+	if (irqstatus->awbOverflowIrq)
+		vfe_proc_ops(VFE_MSG_ID_AWB_OVERFLOW, NULL);
+
+	if (irqstatus->axiErrorIrq)
+		vfe_proc_ops(VFE_MSG_ID_AXI_ERROR, NULL);
+
+	if (irqstatus->busOverflowIrq)
+		vfe_proc_ops(VFE_MSG_ID_BUS_OVERFLOW, NULL);
+
+	if (irqstatus->camifErrorIrq) {
+		CDBG("vfe_irq: camif errors\n");
+		vfe_proc_ops(VFE_MSG_ID_CAMIF_ERROR, NULL);
+	}
+
+	if (irqstatus->camifOverflowIrq)
+		vfe_proc_ops(VFE_MSG_ID_CAMIF_OVERFLOW, NULL);
+
+	if (irqstatus->violationIrq)
+		pr_err("%s: violation irq\n", __func__);
+}
+
+static void vfe_process_camif_sof_irq(void)
+{
+	/* increment the frame id number. */
+	ctrl->vfeFrameId++;
+
+	CDBG("camif_sof_irq, frameId = %d\n", ctrl->vfeFrameId);
+
+	/* In snapshot mode, if frame skip is programmed,
+	* need to check it accordingly to stop camif at
+	* correct frame boundary. For the dropped frames,
+	* there won't be any output path irqs, but there is
+	* still SOF irq, which can help us determine when
+	* to stop the camif.
+	*/
+	if (ctrl->vfeOperationMode) {
+		if ((1 << ctrl->vfeFrameSkipCount)&ctrl->vfeFrameSkipPattern) {
+
+			ctrl->vfeSnapShotCount--;
+			if (ctrl->vfeSnapShotCount == 0)
+				/* terminate vfe pipeline at frame boundary. */
+				writel(CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY,
+					ctrl->vfebase + CAMIF_COMMAND);
+		}
+
+		/* update frame skip counter for bit checking. */
+		ctrl->vfeFrameSkipCount++;
+		if (ctrl->vfeFrameSkipCount == (ctrl->vfeFrameSkipPeriod + 1))
+			ctrl->vfeFrameSkipCount = 0;
+	}
+	vfe_proc_ops(VFE_MSG_ID_SOF_ACK, NULL);
+}
+
+static boolean vfe_get_af_pingpong_status(void)
+{
+	uint32_t busPingPongStatus =
+		readl(ctrl->vfebase + VFE_BUS_PINGPONG_STATUS);
+	return !!(busPingPongStatus & VFE_AF_PINGPONG_STATUS_BIT);
+}
+
+static uint32_t vfe_read_af_buf_addr(boolean pipo)
+{
+	if (pipo == FALSE)
+		return readl(ctrl->vfebase + VFE_BUS_STATS_AF_WR_PING_ADDR);
+	else
+		return readl(ctrl->vfebase + VFE_BUS_STATS_AF_WR_PONG_ADDR);
+}
+
+static void vfe_update_af_buf_addr(boolean pipo, uint32_t addr)
+{
+	if (pipo == FALSE)
+		writel(addr, ctrl->vfebase + VFE_BUS_STATS_AF_WR_PING_ADDR);
+	else
+		writel(addr, ctrl->vfebase + VFE_BUS_STATS_AF_WR_PONG_ADDR);
+}
+
+static boolean vfe_send_af_stats_msg(struct msm_vfe_resp *rp,
+		struct vfe_message *msg, void *data)
+{
+	uint32_t afBufAddress = (uint32_t)data;
+
+	/* fill message with right content. */
+	/* @todo This is causing issues, need further investigate */
+	/* spin_lock_irqsave(&ctrl->state_lock, flags); */
+	if (ctrl->vstate != VFE_STATE_ACTIVE)
+		return FALSE;
+
+	msg->_u.msgStatsAf.afBuffer = afBufAddress;
+	msg->_u.msgStatsAf.frameCounter = ctrl->vfeFrameId;
+
+	ctrl->afStatsControl.ackPending = TRUE;
+
+	vfe_addr_convert(&(rp->phy), rp->type, msg, NULL, NULL);
+	/* spin_unlock_irqrestore(&ctrl->state_lock, flags); */
+	return TRUE;
+}
+
+static void vfe_process_stats_af_irq(void)
+{
+	boolean bufferAvailable;
+
+	if (!(ctrl->afStatsControl.ackPending)) {
+
+		/* read hardware status. */
+		ctrl->afStatsControl.pingPongStatus =
+			vfe_get_af_pingpong_status();
+
+		bufferAvailable = (ctrl->afStatsControl.pingPongStatus) ^ 1;
+
+		ctrl->afStatsControl.bufToRender =
+			vfe_read_af_buf_addr(bufferAvailable);
+
+		/* update the same buffer address (ping or pong) */
+		vfe_update_af_buf_addr(bufferAvailable,
+			ctrl->afStatsControl.nextFrameAddrBuf);
+
+		vfe_proc_ops(VFE_MSG_ID_STATS_AUTOFOCUS,
+			(void *)ctrl->afStatsControl.bufToRender);
+	} else
+		ctrl->afStatsControl.droppedStatsFrameCount++;
+}
+
+static boolean vfe_get_awb_pingpong_status(void)
+{
+	uint32_t busPingPongStatus =
+
+		readl(ctrl->vfebase + VFE_BUS_PINGPONG_STATUS);
+
+	return !!(busPingPongStatus & VFE_AWB_PINGPONG_STATUS_BIT);
+
+}
+
+static uint32_t vfe_read_awb_buf_addr(boolean pingpong)
+{
+	if (pingpong == FALSE)
+		return readl(ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PING_ADDR);
+	else
+		return readl(ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PONG_ADDR);
+}
+
+static void vfe_update_awb_buf_addr(boolean pingpong, uint32_t addr)
+{
+	if (pingpong == FALSE)
+		writel(addr, ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PING_ADDR);
+	else
+		writel(addr, ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PONG_ADDR);
+}
+
+static boolean vfe_send_awb_stats_msg(struct msm_vfe_resp *rp,
+		struct vfe_message *msg, void *data)
+{
+	uint32_t awbBufAddress = (uint32_t)data;
+
+	/* fill message with right content. */
+	/* @todo This is causing issues, need further investigate */
+	/* spin_lock_irqsave(&ctrl->state_lock, flags); */
+	if (ctrl->vstate != VFE_STATE_ACTIVE)
+		return FALSE;
+
+	msg->_u.msgStatsWbExp.awbBuffer = awbBufAddress;
+	msg->_u.msgStatsWbExp.frameCounter = ctrl->vfeFrameId;
+
+
+	ctrl->awbStatsControl.ackPending = TRUE;
+
+	vfe_addr_convert(&(rp->phy),
+			rp->type, msg,
+			NULL, NULL);
+
+	return TRUE;
+}
+
+static void vfe_process_stats_awb_irq(void)
+{
+	boolean bufferAvailable;
+
+	if (!(ctrl->awbStatsControl.ackPending)) {
+
+		ctrl->awbStatsControl.pingPongStatus =
+			vfe_get_awb_pingpong_status();
+
+		bufferAvailable = (ctrl->awbStatsControl.pingPongStatus) ^ 1;
+
+		ctrl->awbStatsControl.bufToRender =
+			vfe_read_awb_buf_addr(bufferAvailable);
+
+		vfe_update_awb_buf_addr(bufferAvailable,
+			ctrl->awbStatsControl.nextFrameAddrBuf);
+
+		vfe_proc_ops(VFE_MSG_ID_STATS_WB_EXP,
+			(void *)ctrl->awbStatsControl.bufToRender);
+
+	} else
+		ctrl->awbStatsControl.droppedStatsFrameCount++;
+}
+
+static void vfe_write_gamma_table(uint8_t channel,
+	boolean bank, int16_t *pTable)
+{
+	uint16_t i;
+
+	enum VFE_DMI_RAM_SEL dmiRamSel = NO_MEM_SELECTED;
+
+	switch (channel) {
+	case 0:
+		if (bank == 0)
+			dmiRamSel = RGBLUT_RAM_CH0_BANK0;
+		else
+			dmiRamSel = RGBLUT_RAM_CH0_BANK1;
+		break;
+
+	case 1:
+		if (bank == 0)
+			dmiRamSel = RGBLUT_RAM_CH1_BANK0;
+		else
+			dmiRamSel = RGBLUT_RAM_CH1_BANK1;
+		break;
+
+	case 2:
+		if (bank == 0)
+			dmiRamSel = RGBLUT_RAM_CH2_BANK0;
+		else
+			dmiRamSel = RGBLUT_RAM_CH2_BANK1;
+		break;
+
+	default:
+		break;
+	}
+
+	vfe_program_dmi_cfg(dmiRamSel);
+
+	for (i = 0; i < VFE_GAMMA_TABLE_LENGTH; i++) {
+		writel((uint32_t)(*pTable), ctrl->vfebase + VFE_DMI_DATA_LO);
+		pTable++;
+	}
+
+	/* After DMI transfer, need to set the DMI_CFG to unselect any SRAM
+	unselect the SRAM Bank. */
+	writel(VFE_DMI_CFG_DEFAULT, ctrl->vfebase + VFE_DMI_CFG);
+}
+
+static void vfe_prog_hw_testgen_cmd(uint32_t value)
+{
+	writel(value, ctrl->vfebase + VFE_HW_TESTGEN_CMD);
+}
+
+static inline void vfe_read_irq_status(struct vfe_irq_thread_msg *out)
+{
+	uint32_t *temp;
+
+	memset(out, 0, sizeof(struct vfe_irq_thread_msg));
+
+	temp = (uint32_t *)(ctrl->vfebase + VFE_IRQ_STATUS);
+	out->vfeIrqStatus = readl(temp);
+
+	temp = (uint32_t *)(ctrl->vfebase + CAMIF_STATUS);
+	out->camifStatus = readl(temp);
+
+/*	this for YUV performance tuning
+	writel(0x7, ctrl->vfebase + CAMIF_COMMAND);
+	writel(0x3, ctrl->vfebase + CAMIF_COMMAND);
+	CDBG("camifStatus  = 0x%x\n", out->camifStatus);
+*/
+/*
+	temp = (uint32_t *)(ctrl->vfebase + VFE_DEMOSAIC_STATUS);
+	out->demosaicStatus = readl(temp);
+
+	temp = (uint32_t *)(ctrl->vfebase + VFE_ASF_MAX_EDGE);
+	out->asfMaxEdge = readl(temp);
+
+	temp = (uint32_t *)(ctrl->vfebase + VFE_BUS_ENC_Y_WR_PM_STATS_0);
+*/
+
+#if 0
+	out->pmInfo.encPathPmInfo.yWrPmStats0      = readl(temp++);
+	out->pmInfo.encPathPmInfo.yWrPmStats1      = readl(temp++);
+	out->pmInfo.encPathPmInfo.cbcrWrPmStats0   = readl(temp++);
+	out->pmInfo.encPathPmInfo.cbcrWrPmStats1   = readl(temp++);
+	out->pmInfo.viewPathPmInfo.yWrPmStats0     = readl(temp++);
+	out->pmInfo.viewPathPmInfo.yWrPmStats1     = readl(temp++);
+	out->pmInfo.viewPathPmInfo.cbcrWrPmStats0  = readl(temp++);
+	out->pmInfo.viewPathPmInfo.cbcrWrPmStats1  = readl(temp);
+#endif /* if 0 Jeff */
+}
+
+static void
+vfe_parse_interrupt_status(struct vfe_interrupt_status *ret,
+uint32_t irqStatusIn)
+{
+	struct vfe_irqenable hwstat;
+	boolean temp;
+
+	memset(&hwstat, 0, sizeof(hwstat));
+	memset(ret, 0, sizeof(*ret));
+
+	hwstat = *((struct vfe_irqenable *)(&irqStatusIn));
+
+	ret->camifErrorIrq = hwstat.camifErrorIrq;
+	ret->camifSofIrq = hwstat.camifSofIrq;
+	ret->camifEolIrq = hwstat.camifEolIrq;
+	ret->camifEofIrq = hwstat.camifEofIrq;
+	ret->camifEpoch1Irq = hwstat.camifEpoch1Irq;
+	ret->camifEpoch2Irq = hwstat.camifEpoch2Irq;
+	ret->camifOverflowIrq = hwstat.camifOverflowIrq;
+	ret->ceIrq = hwstat.ceIrq;
+	ret->regUpdateIrq = hwstat.regUpdateIrq;
+	ret->resetAckIrq = hwstat.resetAckIrq;
+	ret->encYPingpongIrq = hwstat.encYPingpongIrq;
+	ret->encCbcrPingpongIrq = hwstat.encCbcrPingpongIrq;
+	ret->viewYPingpongIrq = hwstat.viewYPingpongIrq;
+	ret->viewCbcrPingpongIrq = hwstat.viewCbcrPingpongIrq;
+	ret->rdPingpongIrq = hwstat.rdPingpongIrq;
+	ret->afPingpongIrq = hwstat.afPingpongIrq;
+	ret->awbPingpongIrq = hwstat.awbPingpongIrq;
+	ret->histPingpongIrq = hwstat.histPingpongIrq;
+	ret->encIrq = hwstat.encIrq;
+	ret->viewIrq = hwstat.viewIrq;
+	ret->busOverflowIrq = hwstat.busOverflowIrq;
+	ret->afOverflowIrq = hwstat.afOverflowIrq;
+	ret->awbOverflowIrq = hwstat.awbOverflowIrq;
+	ret->syncTimer0Irq = hwstat.syncTimer0Irq;
+	ret->syncTimer1Irq = hwstat.syncTimer1Irq;
+	ret->syncTimer2Irq = hwstat.syncTimer2Irq;
+	ret->asyncTimer0Irq = hwstat.asyncTimer0Irq;
+	ret->asyncTimer1Irq = hwstat.asyncTimer1Irq;
+	ret->asyncTimer2Irq = hwstat.asyncTimer2Irq;
+	ret->asyncTimer3Irq = hwstat.asyncTimer3Irq;
+	ret->axiErrorIrq = hwstat.axiErrorIrq;
+	ret->violationIrq = hwstat.violationIrq;
+
+	/* logic OR of any error bits
+	 * although each irq corresponds to a bit, the data type here is a
+	 * boolean already. hence use logic operation.
+	 */
+	temp =
+	    ret->camifErrorIrq ||
+	    ret->camifOverflowIrq ||
+	    ret->afOverflowIrq ||
+	    ret->awbOverflowIrq ||
+	    ret->awbPingpongIrq ||
+	    ret->afPingpongIrq ||
+	    ret->busOverflowIrq || ret->axiErrorIrq || ret->violationIrq;
+
+	ret->anyErrorIrqs = temp;
+
+	/* logic OR of any output path bits*/
+	temp = ret->encYPingpongIrq || ret->encCbcrPingpongIrq || ret->encIrq;
+
+	ret->anyOutput2PathIrqs = temp;
+
+	temp = ret->viewYPingpongIrq || ret->viewCbcrPingpongIrq ||
+		ret->viewIrq;
+
+	ret->anyOutput1PathIrqs = temp;
+
+	ret->anyOutputPathIrqs =
+	    ret->anyOutput1PathIrqs || ret->anyOutput2PathIrqs;
+
+	/* logic OR of any sync timer bits*/
+	temp = ret->syncTimer0Irq || ret->syncTimer1Irq || ret->syncTimer2Irq;
+
+	ret->anySyncTimerIrqs = temp;
+
+	/* logic OR of any async timer bits*/
+	temp =
+	    ret->asyncTimer0Irq ||
+	    ret->asyncTimer1Irq || ret->asyncTimer2Irq || ret->asyncTimer3Irq;
+
+	ret->anyAsyncTimerIrqs = temp;
+
+	/* bool for all interrupts that are not allowed in idle state */
+	temp =
+	    ret->anyErrorIrqs ||
+	    ret->anyOutputPathIrqs ||
+	    ret->anySyncTimerIrqs ||
+	    ret->regUpdateIrq ||
+	    ret->awbPingpongIrq ||
+	    ret->afPingpongIrq ||
+	    ret->camifSofIrq || ret->camifEpoch2Irq || ret->camifEpoch1Irq;
+
+	ret->anyIrqForActiveStatesOnly = temp;
+}
+
+static void
+vfe_get_asf_frame_info(struct vfe_frame_asf_info *rc,
+struct vfe_irq_thread_msg *in)
+{
+	struct vfe_asf_info     asfInfoTemp;
+
+	memset(rc, 0, sizeof(*rc));
+	memset(&asfInfoTemp, 0, sizeof(asfInfoTemp));
+
+	asfInfoTemp = *((struct vfe_asf_info *)(&(in->asfMaxEdge)));
+
+	rc->asfHbiCount = asfInfoTemp.HBICount;
+	rc->asfMaxEdge = asfInfoTemp.maxEdge;
+}
+
+static void
+vfe_get_demosaic_frame_info(struct vfe_frame_bpc_info *rc,
+struct vfe_irq_thread_msg *in)
+{
+	struct vfe_bps_info     bpcInfoTemp;
+
+	memset(rc, 0, sizeof(*rc));
+	memset(&bpcInfoTemp, 0, sizeof(bpcInfoTemp));
+
+	bpcInfoTemp = *((struct vfe_bps_info *)(&(in->demosaicStatus)));
+
+	rc->greenDefectPixelCount = bpcInfoTemp.greenBadPixelCount;
+
+	rc->redBlueDefectPixelCount = bpcInfoTemp.RedBlueBadPixelCount;
+}
+
+static void
+vfe_get_camif_status(struct vfe_msg_camif_status *rc,
+struct vfe_irq_thread_msg *in)
+{
+	struct vfe_camif_stats camifStatusTemp;
+
+	memset(rc, 0, sizeof(*rc));
+	memset(&camifStatusTemp, 0, sizeof(camifStatusTemp));
+
+	camifStatusTemp = *((struct vfe_camif_stats *)(&(in->camifStatus)));
+
+	rc->camifState = (boolean) camifStatusTemp.camifHalt;
+	rc->lineCount = camifStatusTemp.lineCount;
+	rc->pixelCount = camifStatusTemp.pixelCount;
+}
+
+static void
+vfe_get_performance_monitor_data(struct vfe_bus_performance_monitor *rc,
+		struct vfe_irq_thread_msg *in)
+{
+	memset(rc, 0, sizeof(*rc));
+
+	rc->encPathPmInfo.yWrPmStats0 = in->pmInfo.encPathPmInfo.yWrPmStats0;
+	rc->encPathPmInfo.yWrPmStats1 = in->pmInfo.encPathPmInfo.yWrPmStats1;
+	rc->encPathPmInfo.cbcrWrPmStats0 =
+		in->pmInfo.encPathPmInfo.cbcrWrPmStats0;
+	rc->encPathPmInfo.cbcrWrPmStats1 =
+		in->pmInfo.encPathPmInfo.cbcrWrPmStats1;
+	rc->viewPathPmInfo.yWrPmStats0 = in->pmInfo.viewPathPmInfo.yWrPmStats0;
+	rc->viewPathPmInfo.yWrPmStats1 = in->pmInfo.viewPathPmInfo.yWrPmStats1;
+	rc->viewPathPmInfo.cbcrWrPmStats0 =
+		in->pmInfo.viewPathPmInfo.cbcrWrPmStats0;
+	rc->viewPathPmInfo.cbcrWrPmStats1 =
+	    in->pmInfo.viewPathPmInfo.cbcrWrPmStats1;
+}
+
+static void vfe_process_reg_update_irq(void)
+{
+	CDBG("vfe_process_reg_update_irq: ackPendingFlag is %d\n",
+	ctrl->vfeStartAckPendingFlag);
+	if (ctrl->vfeStartAckPendingFlag == TRUE) {
+		vfe_proc_ops(VFE_MSG_ID_START_ACK, NULL);
+		ctrl->vfeStartAckPendingFlag = FALSE;
+	} else
+		vfe_proc_ops(VFE_MSG_ID_UPDATE_ACK, NULL);
+}
+
+static void vfe_process_reset_irq(void)
+{
+	/* unsigned long flags; */
+
+	/* @todo This is causing issues, need further investigate */
+	/* spin_lock_irqsave(&ctrl->state_lock, flags); */
+	ctrl->vstate = VFE_STATE_IDLE;
+	/* spin_unlock_irqrestore(&ctrl->state_lock, flags); */
+
+	if (ctrl->vfeStopAckPending == TRUE) {
+		ctrl->vfeStopAckPending = FALSE;
+		vfe_proc_ops(VFE_MSG_ID_STOP_ACK, NULL);
+	} else {
+		vfe_set_default_reg_values();
+		vfe_proc_ops(VFE_MSG_ID_RESET_ACK, NULL);
+	}
+}
+
+static void vfe_process_pingpong_irq(struct vfe_output_path *in,
+	uint8_t fragmentCount)
+{
+	uint16_t circularIndex;
+	uint32_t nextFragmentAddr;
+
+	/* get next fragment address from circular buffer */
+	circularIndex    = (in->fragIndex) % (2 * fragmentCount);
+	nextFragmentAddr = in->addressBuffer[circularIndex];
+
+	in->fragIndex = circularIndex + 1;
+
+	/* use next fragment to program hardware ping/pong address. */
+	if (in->hwCurrentFlag == ping) {
+		writel(nextFragmentAddr, in->hwRegPingAddress);
+		in->hwCurrentFlag = pong;
+
+	} else {
+		writel(nextFragmentAddr, in->hwRegPongAddress);
+		in->hwCurrentFlag = ping;
+	}
+}
+
+static boolean vfe_send_video_msg(struct msm_vfe_resp *rp,
+		struct vfe_message *msg, void *data)
+{
+	struct vfe_msg_output *pPayload = data;
+
+	if (ctrl->vstate != VFE_STATE_ACTIVE)
+		return FALSE;
+	memcpy(&(msg->_u),
+		(void *)pPayload, sizeof(struct vfe_msg_output));
+
+	rp->phy.output_id = OUTPUT_TYPE_V;
+	CDBG("vfe_send_video_msg rp->type= %d\n", rp->type);
+
+	vfe_addr_convert(&(rp->phy),
+			rp->type, msg,
+			&(rp->extdata), &(rp->extlen));
+	return TRUE;
+}
+
+static boolean vfe_send_preview_msg(struct msm_vfe_resp *rp,
+		struct vfe_message *msg, void *data)
+{
+	struct vfe_msg_output *pPayload = data;
+
+	if (ctrl->vstate != VFE_STATE_ACTIVE)
+		return FALSE;
+
+	memcpy(&(msg->_u), (void *)pPayload, sizeof(struct vfe_msg_output));
+
+	rp->phy.output_id = OUTPUT_TYPE_P;
+	CDBG("vfe_send_preview_msg rp->type= %d\n", rp->type);
+
+	vfe_addr_convert(&(rp->phy),
+			rp->type, msg,
+			&(rp->extdata), &(rp->extlen));
+
+	return TRUE;
+}
+
+
+static boolean vfe_send_thumbnail_msg(struct msm_vfe_resp *rp,
+		struct vfe_message *msg, void *data)
+{
+	struct vfe_msg_output *pPayload = data;
+
+	if (ctrl->vstate != VFE_STATE_ACTIVE)
+		return FALSE;
+
+	memcpy(&(msg->_u), (void *)pPayload, sizeof(struct vfe_msg_output));
+
+	rp->phy.output_id = OUTPUT_TYPE_T;
+	CDBG("vfe_send_thumbnail_msg rp->type= %d\n", rp->type);
+
+	if (ctrl->viewPath.snapshotPendingCount <= 1)
+		ctrl->viewPath.ackPending = FALSE;
+
+	vfe_addr_convert(&(rp->phy),
+			rp->type, msg,
+			&(rp->extdata), &(rp->extlen));
+	return TRUE;
+}
+
+static boolean vfe_send_mainimage_msg(struct msm_vfe_resp *rp,
+		struct vfe_message *msg, void *data)
+{
+	struct vfe_msg_output *pPayload = data;
+
+	if (ctrl->vstate != VFE_STATE_ACTIVE)
+		return FALSE;
+
+	memcpy(&(msg->_u), (void *)pPayload, sizeof(struct vfe_msg_output));
+
+	rp->phy.output_id = OUTPUT_TYPE_S;
+	CDBG("vfe_send_mainimage_msg rp->type= %d\n", rp->type);
+
+	if (ctrl->encPath.snapshotPendingCount <= 1) {
+		ctrl->encPath.ackPending = FALSE;
+	}
+
+	vfe_addr_convert(&(rp->phy),
+			rp->type, msg,
+			&(rp->extdata), &(rp->extlen));
+
+	return TRUE;
+}
+
+static void vfe_send_output_msg(boolean whichOutputPath,
+	uint32_t yPathAddr, uint32_t cbcrPathAddr)
+{
+	struct vfe_msg_output msgPayload;
+
+	msgPayload.yBuffer = yPathAddr;
+	msgPayload.cbcrBuffer = cbcrPathAddr;
+
+	/* asf info is common for both output1 and output2 */
+#if 0
+	msgPayload.asfInfo.asfHbiCount = ctrl->vfeAsfFrameInfo.asfHbiCount;
+	msgPayload.asfInfo.asfMaxEdge = ctrl->vfeAsfFrameInfo.asfMaxEdge;
+
+	/* demosaic info is common for both output1 and output2 */
+	msgPayload.bpcInfo.greenDefectPixelCount =
+		ctrl->vfeBpcFrameInfo.greenDefectPixelCount;
+	msgPayload.bpcInfo.redBlueDefectPixelCount =
+		ctrl->vfeBpcFrameInfo.redBlueDefectPixelCount;
+#endif /* if 0 */
+
+	/* frame ID is common for both paths. */
+	msgPayload.frameCounter = ctrl->vfeFrameId;
+
+	if (whichOutputPath) {
+		/* msgPayload.pmData = ctrl->vfePmData.encPathPmInfo; */
+		ctrl->encPath.ackPending = TRUE;
+
+		if (ctrl->vfeOperationMode == 0) {
+			if (ctrl->axiOutputMode ==
+				VFE_AXI_OUTPUT_MODE_Output1AndOutput2) {
+				/* video mode */
+				vfe_proc_ops(VFE_MSG_ID_OUTPUT_V, &msgPayload);
+			} else{
+				/* preview mode */
+				vfe_proc_ops(VFE_MSG_ID_OUTPUT_P, &msgPayload);
+			}
+		} else {
+			vfe_proc_ops(VFE_MSG_ID_OUTPUT_S, &msgPayload);
+		}
+
+	} else {
+		/* physical output1 path from vfe */
+		ctrl->viewPath.ackPending = TRUE;
+
+		if (ctrl->vfeOperationMode == 0) {
+			vfe_proc_ops(VFE_MSG_ID_OUTPUT_P, &msgPayload);
+			CDBG(" video mode display output.\n");
+
+		} else{
+			vfe_proc_ops(VFE_MSG_ID_OUTPUT_T, &msgPayload);
+			CDBG(" snapshot mode thumbnail output.\n");
+		}
+	}
+}
+
+static void vfe_process_frame_done_irq_multi_frag(struct vfe_output_path_combo
+						  *in)
+{
+	uint32_t yAddress, cbcrAddress;
+	uint16_t idx;
+	uint32_t *ptrY;
+	uint32_t *ptrCbcr;
+	const uint32_t *ptrSrc;
+	uint8_t i;
+
+	if (!in->ackPending) {
+
+		idx = (in->currentFrame) * (in->fragCount);
+
+		/* Send output message. */
+		yAddress = in->yPath.addressBuffer[idx];
+		cbcrAddress = in->cbcrPath.addressBuffer[idx];
+
+		/* copy next frame to current frame. */
+		ptrSrc  = in->nextFrameAddrBuf;
+		ptrY = (uint32_t *)&in->yPath.addressBuffer[idx];
+		ptrCbcr = (uint32_t *)&in->cbcrPath.addressBuffer[idx];
+
+		/* Copy Y address */
+		for (i = 0; i < in->fragCount; i++)
+			*ptrY++ = *ptrSrc++;
+
+		/* Copy Cbcr address */
+		for (i = 0; i < in->fragCount; i++)
+			*ptrCbcr++ = *ptrSrc++;
+
+		vfe_send_output_msg(in->whichOutputPath, yAddress, cbcrAddress);
+
+	} else {
+		if (in->whichOutputPath == 0)
+			ctrl->vfeDroppedFrameCounts.output1Count++;
+
+		if (in->whichOutputPath == 1)
+			ctrl->vfeDroppedFrameCounts.output2Count++;
+	}
+
+	/* toggle current frame. */
+	in->currentFrame = in->currentFrame^1;
+
+	if (ctrl->vfeOperationMode)
+		in->snapshotPendingCount--;
+}
+
+static void vfe_process_frame_done_irq_no_frag_io(
+		struct vfe_output_path_combo *in,
+		uint32_t *pNextAddr,
+	uint32_t *pdestRenderAddr)
+{
+	uint32_t busPingPongStatus;
+	uint32_t tempAddress;
+
+	/* 1. read hw status register. */
+	busPingPongStatus = readl(ctrl->vfebase + VFE_BUS_PINGPONG_STATUS);
+
+	CDBG("hardware status is 0x%x\n", busPingPongStatus);
+
+	/* 2. determine ping or pong */
+	/* use cbcr status */
+	busPingPongStatus = busPingPongStatus & (1<<(in->cbcrStatusBit));
+
+	/* 3. read out address and update address */
+	if (busPingPongStatus == 0) {
+		/* hw is working on ping, render pong buffer */
+		/* a. read out pong address */
+		/* read out y address. */
+		tempAddress = readl(in->yPath.hwRegPongAddress);
+
+		CDBG("pong 1 addr = 0x%x\n", tempAddress);
+		*pdestRenderAddr++ = tempAddress;
+		/* read out cbcr address. */
+		tempAddress = readl(in->cbcrPath.hwRegPongAddress);
+
+		CDBG("pong 2 addr = 0x%x\n", tempAddress);
+		*pdestRenderAddr = tempAddress;
+
+		/* b. update pong address */
+		writel(*pNextAddr++, in->yPath.hwRegPongAddress);
+		writel(*pNextAddr, in->cbcrPath.hwRegPongAddress);
+	} else {
+		/* hw is working on pong, render ping buffer */
+
+		/* a. read out ping address */
+		tempAddress = readl(in->yPath.hwRegPingAddress);
+		CDBG("ping 1 addr = 0x%x\n", tempAddress);
+		*pdestRenderAddr++ = tempAddress;
+		tempAddress = readl(in->cbcrPath.hwRegPingAddress);
+
+		CDBG("ping 2 addr = 0x%x\n", tempAddress);
+		*pdestRenderAddr = tempAddress;
+
+		/* b. update ping address */
+		writel(*pNextAddr++, in->yPath.hwRegPingAddress);
+		CDBG("NextAddress = 0x%x\n", *pNextAddr);
+		writel(*pNextAddr, in->cbcrPath.hwRegPingAddress);
+	}
+}
+
+static void vfe_process_frame_done_irq_no_frag(struct vfe_output_path_combo *in)
+{
+	uint32_t addressToRender[2];
+
+	if (!in->ackPending) {
+		vfe_process_frame_done_irq_no_frag_io(in,
+						      in->nextFrameAddrBuf,
+						      addressToRender);
+
+		/* use addressToRender to send out message. */
+		vfe_send_output_msg(in->whichOutputPath,
+				addressToRender[0], addressToRender[1]);
+
+	} else {
+		/* ackPending is still there, accumulate dropped frame count.
+		 * These count can be read through ioctrl command. */
+		CDBG("waiting frame ACK\n");
+
+		if (in->whichOutputPath == 0)
+			ctrl->vfeDroppedFrameCounts.output1Count++;
+
+		if (in->whichOutputPath == 1)
+			ctrl->vfeDroppedFrameCounts.output2Count++;
+	}
+
+	/* in case of multishot when upper layer did not ack, there will still
+	 * be a snapshot done msg sent out, even though the number of frames
+	 * sent out may be less than the desired number of frames.  snapshot
+	 * done msg would be helpful to indicate that vfe pipeline has stop,
+	 * and in good known state.
+	 */
+	if (ctrl->vfeOperationMode)
+		in->snapshotPendingCount--;
+}
+
+static void vfe_process_output_path_irq(struct vfe_interrupt_status *irqstatus)
+{
+	/* unsigned long flags; */
+
+	/* process the view path interrupts */
+	if (irqstatus->anyOutput1PathIrqs) {
+		if (ctrl->viewPath.multiFrag) {
+
+			if (irqstatus->viewCbcrPingpongIrq)
+				vfe_process_pingpong_irq(&
+							 (ctrl->viewPath.
+							  cbcrPath),
+							 ctrl->viewPath.
+							 fragCount);
+
+			if (irqstatus->viewYPingpongIrq)
+				vfe_process_pingpong_irq(&
+							 (ctrl->viewPath.yPath),
+							 ctrl->viewPath.
+							 fragCount);
+
+			if (irqstatus->viewIrq)
+				vfe_process_frame_done_irq_multi_frag(&ctrl->
+								      viewPath);
+
+		} else {
+			/* typical case for no fragment,
+			 only frame done irq is enabled. */
+			if (irqstatus->viewIrq)
+				vfe_process_frame_done_irq_no_frag(&ctrl->
+								   viewPath);
+		}
+	}
+
+	/* process the encoder path interrupts */
+	if (irqstatus->anyOutput2PathIrqs) {
+		if (ctrl->encPath.multiFrag) {
+			if (irqstatus->encCbcrPingpongIrq)
+				vfe_process_pingpong_irq(&
+							 (ctrl->encPath.
+							  cbcrPath),
+							 ctrl->encPath.
+							 fragCount);
+
+			if (irqstatus->encYPingpongIrq)
+				vfe_process_pingpong_irq(&(ctrl->encPath.yPath),
+							 ctrl->encPath.
+							 fragCount);
+
+			if (irqstatus->encIrq)
+				vfe_process_frame_done_irq_multi_frag(&ctrl->
+								      encPath);
+
+		} else {
+			if (irqstatus->encIrq)
+				vfe_process_frame_done_irq_no_frag(&ctrl->
+								   encPath);
+		}
+	}
+
+	if (ctrl->vfeOperationMode) {
+		if ((ctrl->encPath.snapshotPendingCount == 0) &&
+				(ctrl->viewPath.snapshotPendingCount == 0)) {
+
+			/* @todo This is causing issues, further investigate */
+			/* spin_lock_irqsave(&ctrl->state_lock, flags); */
+			ctrl->vstate = VFE_STATE_IDLE;
+			/* spin_unlock_irqrestore(&ctrl->state_lock, flags); */
+
+			vfe_proc_ops(VFE_MSG_ID_SNAPSHOT_DONE, NULL);
+			vfe_camif_stop_immediately();
+			vfe_prog_hw_testgen_cmd(VFE_TEST_GEN_STOP);
+			vfe_pm_stop();
+		}
+	}
+}
+
+static void __vfe_do_tasklet(struct isr_queue_cmd *qcmd)
+{
+	if (qcmd->vfeInterruptStatus.regUpdateIrq) {
+		CDBG("irq regUpdateIrq\n");
+		vfe_process_reg_update_irq();
+	}
+
+	if (qcmd->vfeInterruptStatus.resetAckIrq) {
+		CDBG("%s: process resetAckIrq\n", __func__);
+		vfe_process_reset_irq();
+	}
+
+	if (ctrl->vstate != VFE_STATE_ACTIVE)
+		return;
+
+#if 0
+	if (qcmd->vfeInterruptStatus.camifEpoch1Irq)
+		vfe_proc_ops(VFE_MSG_ID_EPOCH1);
+
+	if (qcmd->vfeInterruptStatus.camifEpoch2Irq)
+		vfe_proc_ops(VFE_MSG_ID_EPOCH2);
+#endif /* Jeff */
+
+	/* next, check output path related interrupts. */
+	if (qcmd->vfeInterruptStatus.anyOutputPathIrqs) {
+		CDBG("irq: anyOutputPathIrqs\n");
+		vfe_process_output_path_irq(&qcmd->vfeInterruptStatus);
+	}
+
+	if (qcmd->vfeInterruptStatus.afPingpongIrq)
+		vfe_process_stats_af_irq();
+
+	if (qcmd->vfeInterruptStatus.awbPingpongIrq)
+		vfe_process_stats_awb_irq();
+
+	/* any error irqs*/
+	if (qcmd->vfeInterruptStatus.anyErrorIrqs)
+		vfe_process_error_irq(&qcmd->vfeInterruptStatus);
+
+#if 0
+	if (qcmd->vfeInterruptStatus.anySyncTimerIrqs)
+		vfe_process_sync_timer_irq();
+
+	if (qcmd->vfeInterruptStatus.anyAsyncTimerIrqs)
+		vfe_process_async_timer_irq();
+#endif /* Jeff */
+
+	if (qcmd->vfeInterruptStatus.camifSofIrq) {
+		CDBG("irq: camifSofIrq\n");
+		vfe_process_camif_sof_irq();
+	}
+}
+
+static struct isr_queue_cmd *get_irq_cmd_nosync(void)
+{
+	int old_get = ctrl->irq_get++;
+	ctrl->irq_get = ctrl->irq_get % ARRAY_SIZE(ctrl->irqs);
+	if (ctrl->irq_get == ctrl->irq_put) {
+		pr_err("%s: out of irq command packets\n", __func__);
+		ctrl->irq_get = old_get;
+		return NULL;
+	}
+
+	return ctrl->irqs + old_get;
+}
+
+static struct isr_queue_cmd *next_irq_cmd(void)
+{
+	unsigned long flags;
+	struct isr_queue_cmd *cmd;
+	spin_lock_irqsave(&ctrl->irqs_lock, flags);
+	if (ctrl->irq_get == ctrl->irq_put) {
+		spin_unlock_irqrestore(&ctrl->irqs_lock, flags);
+		return NULL; /* already empty */
+	}
+	cmd = ctrl->irqs + ctrl->irq_put;
+	spin_unlock_irqrestore(&ctrl->irqs_lock, flags);
+	return cmd;
+}
+
+static void put_irq_cmd(void)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&ctrl->irqs_lock, flags);
+	if (ctrl->irq_get == ctrl->irq_put) {
+		spin_unlock_irqrestore(&ctrl->irqs_lock, flags);
+		return; /* already empty */
+	}
+	ctrl->irq_put++;
+	ctrl->irq_put %= ARRAY_SIZE(ctrl->irqs);
+	spin_unlock_irqrestore(&ctrl->irqs_lock, flags);
+}
+
+static void vfe_do_tasklet(unsigned long data)
+{
+	int cnt = 0;
+	unsigned long flags;
+	struct isr_queue_cmd *qcmd = NULL;
+
+	spin_lock_irqsave(&msm_vfe_ctrl_lock, flags);
+	if (!ctrl) {
+		spin_unlock_irqrestore(&msm_vfe_ctrl_lock, flags);
+		return;
+	}
+
+	CDBG("%s\n", __func__);
+
+	while ((qcmd = next_irq_cmd())) {
+		__vfe_do_tasklet(qcmd);
+		put_irq_cmd();
+		cnt++;
+	}
+
+	if (cnt > ARRAY_SIZE(ctrl->irqs)/2)
+		CDBG("%s: serviced %d vfe interrupts\n", __func__, cnt);
+
+	spin_unlock_irqrestore(&msm_vfe_ctrl_lock, flags);
+}
+
+DECLARE_TASKLET(vfe_tasklet, vfe_do_tasklet, 0);
+
+static irqreturn_t vfe_parse_irq(int irq_num, void *data)
+{
+	unsigned long flags;
+	uint32_t irqStatusLocal;
+	struct vfe_irq_thread_msg irq;
+	struct isr_queue_cmd *qcmd;
+
+	CDBG("vfe_parse_irq\n");
+
+	if (!atomic_read(&ctrl->vfe_serv_interrupt))
+		return IRQ_HANDLED;
+
+	vfe_read_irq_status(&irq);
+
+	if (irq.vfeIrqStatus == 0) {
+		CDBG("vfe_parse_irq: irq.vfeIrqStatus is 0\n");
+		return IRQ_HANDLED;
+	}
+
+	if (ctrl->vfeStopAckPending)
+		irqStatusLocal = (VFE_IMASK_WHILE_STOPPING & irq.vfeIrqStatus);
+	else
+		irqStatusLocal =
+			((ctrl->vfeImaskPacked | VFE_IMASK_ERROR_ONLY) &
+				irq.vfeIrqStatus);
+
+	spin_lock_irqsave(&ctrl->irqs_lock, flags);
+	qcmd = get_irq_cmd_nosync();
+	if (!qcmd) {
+		spin_unlock_irqrestore(&ctrl->irqs_lock, flags);
+		goto done;
+	}
+	/* first parse the interrupt status to local data structures. */
+	vfe_parse_interrupt_status(&qcmd->vfeInterruptStatus, irqStatusLocal);
+	vfe_get_asf_frame_info(&qcmd->vfeAsfFrameInfo, &irq);
+	vfe_get_demosaic_frame_info(&qcmd->vfeBpcFrameInfo, &irq);
+	vfe_get_camif_status(&qcmd->vfeCamifStatusLocal, &irq);
+	vfe_get_performance_monitor_data(&qcmd->vfePmData, &irq);
+	spin_unlock_irqrestore(&ctrl->irqs_lock, flags);
+	tasklet_schedule(&vfe_tasklet);
+
+done:
+	/* clear the pending interrupt of the same kind.*/
+	writel(irq.vfeIrqStatus, ctrl->vfebase + VFE_IRQ_CLEAR);
+
+	return IRQ_HANDLED;
+}
+
+int vfe_cmd_init(struct msm_vfe_callback *presp,
+	struct platform_device *pdev, void *sdata)
+{
+	struct resource	*vfemem, *vfeirq, *vfeio;
+	int rc;
+	struct msm_camera_sensor_info *s_info;
+	s_info = pdev->dev.platform_data;
+
+	pdev->resource = s_info->resource;
+	pdev->num_resources = s_info->num_resources;
+
+	vfemem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!vfemem) {
+		pr_err("%s: no mem resource\n", __func__);
+		return -ENODEV;
+	}
+
+	vfeirq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!vfeirq) {
+		pr_err("%s: no irq resource\n", __func__);
+		return -ENODEV;
+	}
+
+	vfeio = request_mem_region(vfemem->start,
+		resource_size(vfemem), pdev->name);
+	if (!vfeio) {
+		pr_err("%s: VFE region already claimed\n", __func__);
+		return -EBUSY;
+	}
+
+	ctrl = kzalloc(sizeof(struct msm_vfe8x_ctrl), GFP_KERNEL);
+	if (!ctrl) {
+		pr_err("%s: out of memory\n", __func__);
+		rc = -ENOMEM;
+		goto cmd_init_failed1;
+	}
+	atomic_set(&ctrl->vfe_serv_interrupt, 0);
+	ctrl->vfeirq  = vfeirq->start;
+
+	ctrl->vfebase =
+		ioremap(vfemem->start, (vfemem->end - vfemem->start) + 1);
+	if (!ctrl->vfebase) {
+		pr_err("%s: ioremap failed\n", __func__);
+		rc = -ENOMEM;
+		goto cmd_init_failed2;
+	}
+
+	rc = request_irq(ctrl->vfeirq, vfe_parse_irq,
+		IRQF_TRIGGER_RISING, "vfe", 0);
+	if (rc < 0) {
+		pr_err("%s: request_irq(%d) failed\n", __func__, ctrl->vfeirq);
+		goto cmd_init_failed2;
+	}
+
+	if (presp && presp->vfe_resp)
+		ctrl->resp = presp;
+	else {
+		pr_err("%s: no vfe_resp function\n", __func__);
+
+		rc = -EIO;
+		goto cmd_init_failed3;
+	}
+
+	ctrl->syncdata = sdata;
+	return 0;
+
+cmd_init_failed3:
+	disable_irq(ctrl->vfeirq);
+	free_irq(ctrl->vfeirq, 0);
+	iounmap(ctrl->vfebase);
+cmd_init_failed2:
+	kfree(ctrl);
+cmd_init_failed1:
+	release_mem_region(vfemem->start, (vfemem->end - vfemem->start) + 1);
+	return rc;
+}
+
+void vfe_cmd_release(struct platform_device *dev)
+{
+	struct resource	*mem;
+	unsigned long flags;
+	atomic_set(&ctrl->vfe_serv_interrupt, 0);
+	disable_irq(ctrl->vfeirq);
+	free_irq(ctrl->vfeirq, 0);
+
+	iounmap(ctrl->vfebase);
+	mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
+	release_mem_region(mem->start, (mem->end - mem->start) + 1);
+
+	spin_lock_irqsave(&msm_vfe_ctrl_lock, flags);
+	kfree(ctrl);
+	ctrl = 0;
+	spin_unlock_irqrestore(&msm_vfe_ctrl_lock, flags);
+}
+
+void vfe_stats_af_stop(void)
+{
+	ctrl->vfeStatsCmdLocal.autoFocusEnable = FALSE;
+	ctrl->vfeImaskLocal.afPingpongIrq = FALSE;
+}
+
+void vfe_stop(void)
+{
+	int spin_cnt = 0;
+	uint32_t vfeAxiStauts;
+
+	/* for reset hw modules, and send msg when reset_irq comes.*/
+	ctrl->vfeStopAckPending = TRUE;
+
+	ctrl->vfeStatsPingPongReloadFlag = FALSE;
+	vfe_pm_stop();
+
+	/* disable all interrupts.  */
+	vfe_program_irq_mask(VFE_DISABLE_ALL_IRQS);
+
+	/* in either continuous or snapshot mode, stop command can be issued
+	 * at any time.
+	 */
+	vfe_camif_stop_immediately();
+	vfe_program_axi_cmd(AXI_HALT);
+	vfe_prog_hw_testgen_cmd(VFE_TEST_GEN_STOP);
+
+	do {
+		vfeAxiStauts = vfe_read_axi_status();
+		spin_cnt++;
+	} while (!(vfeAxiStauts & AXI_STATUS_BUSY_MASK));
+	if (spin_cnt > 1)
+		pr_warning("%s: spin_cnt %d\n", __func__, spin_cnt);
+
+	vfe_program_axi_cmd(AXI_HALT_CLEAR);
+
+	/* clear all pending interrupts */
+	writel(VFE_CLEAR_ALL_IRQS, ctrl->vfebase + VFE_IRQ_CLEAR);
+
+	/* enable reset_ack and async timer interrupt only while stopping
+	 * the pipeline.
+	 */
+	vfe_program_irq_mask(VFE_IMASK_WHILE_STOPPING);
+
+	vfe_program_global_reset_cmd(VFE_RESET_UPON_STOP_CMD);
+}
+
+void vfe_update(void)
+{
+	ctrl->vfeModuleEnableLocal.statsEnable =
+		ctrl->vfeStatsCmdLocal.autoFocusEnable |
+		ctrl->vfeStatsCmdLocal.axwEnable;
+
+	vfe_reg_module_cfg(&ctrl->vfeModuleEnableLocal);
+
+	vfe_program_stats_cmd(&ctrl->vfeStatsCmdLocal);
+
+	ctrl->vfeImaskPacked = vfe_irq_pack(ctrl->vfeImaskLocal);
+	vfe_program_irq_mask(ctrl->vfeImaskPacked);
+
+	if ((ctrl->vfeModuleEnableLocal.statsEnable == TRUE) &&
+			(ctrl->vfeStatsPingPongReloadFlag == FALSE)) {
+		ctrl->vfeStatsPingPongReloadFlag = TRUE;
+
+		ctrl->vfeBusCmdLocal.statsPingpongReload = TRUE;
+		vfe_reg_bus_cmd(&ctrl->vfeBusCmdLocal);
+	}
+
+	vfe_program_reg_update_cmd(VFE_REG_UPDATE_TRIGGER);
+}
+
+int vfe_rgb_gamma_update(struct vfe_cmd_rgb_gamma_config *in)
+{
+	int rc = 0;
+
+	ctrl->vfeModuleEnableLocal.rgbLUTEnable = in->enable;
+
+	switch (in->channelSelect) {
+	case RGB_GAMMA_CH0_SELECTED:
+		ctrl->vfeGammaLutSel.ch0BankSelect ^= 1;
+		vfe_write_gamma_table(0,
+				      ctrl->vfeGammaLutSel.ch0BankSelect,
+				      in->table);
+		break;
+
+	case RGB_GAMMA_CH1_SELECTED:
+		ctrl->vfeGammaLutSel.ch1BankSelect ^= 1;
+		vfe_write_gamma_table(1,
+				      ctrl->vfeGammaLutSel.ch1BankSelect,
+				      in->table);
+		break;
+
+	case RGB_GAMMA_CH2_SELECTED:
+		ctrl->vfeGammaLutSel.ch2BankSelect ^= 1;
+		vfe_write_gamma_table(2,
+				      ctrl->vfeGammaLutSel.ch2BankSelect,
+				      in->table);
+		break;
+
+	case RGB_GAMMA_CH0_CH1_SELECTED:
+		ctrl->vfeGammaLutSel.ch0BankSelect ^= 1;
+		ctrl->vfeGammaLutSel.ch1BankSelect ^= 1;
+		vfe_write_gamma_table(0, ctrl->vfeGammaLutSel.ch0BankSelect,
+			in->table);
+		vfe_write_gamma_table(1, ctrl->vfeGammaLutSel.ch1BankSelect,
+			in->table);
+		break;
+
+	case RGB_GAMMA_CH0_CH2_SELECTED:
+		ctrl->vfeGammaLutSel.ch0BankSelect ^= 1;
+		ctrl->vfeGammaLutSel.ch2BankSelect ^= 1;
+		vfe_write_gamma_table(0, ctrl->vfeGammaLutSel.ch0BankSelect,
+			in->table);
+		vfe_write_gamma_table(2, ctrl->vfeGammaLutSel.ch2BankSelect,
+			in->table);
+		break;
+
+	case RGB_GAMMA_CH1_CH2_SELECTED:
+		ctrl->vfeGammaLutSel.ch1BankSelect ^= 1;
+		ctrl->vfeGammaLutSel.ch2BankSelect ^= 1;
+		vfe_write_gamma_table(1, ctrl->vfeGammaLutSel.ch1BankSelect,
+			in->table);
+		vfe_write_gamma_table(2, ctrl->vfeGammaLutSel.ch2BankSelect,
+			in->table);
+		break;
+
+	case RGB_GAMMA_CH0_CH1_CH2_SELECTED:
+		ctrl->vfeGammaLutSel.ch0BankSelect ^= 1;
+		ctrl->vfeGammaLutSel.ch1BankSelect ^= 1;
+		ctrl->vfeGammaLutSel.ch2BankSelect ^= 1;
+		vfe_write_gamma_table(0, ctrl->vfeGammaLutSel.ch0BankSelect,
+			in->table);
+		vfe_write_gamma_table(1, ctrl->vfeGammaLutSel.ch1BankSelect,
+			in->table);
+		vfe_write_gamma_table(2, ctrl->vfeGammaLutSel.ch2BankSelect,
+			in->table);
+		break;
+
+	default:
+		pr_err("%s: invalid gamma channel %d\n", __func__,
+			in->channelSelect);
+		return -EINVAL;
+	} /* switch */
+
+	/* update the gammaLutSel register. */
+	vfe_program_lut_bank_sel(&ctrl->vfeGammaLutSel);
+
+	return rc;
+}
+
+int vfe_rgb_gamma_config(struct vfe_cmd_rgb_gamma_config *in)
+{
+	int rc = 0;
+
+	ctrl->vfeModuleEnableLocal.rgbLUTEnable = in->enable;
+
+	switch (in->channelSelect) {
+	case RGB_GAMMA_CH0_SELECTED:
+vfe_write_gamma_table(0, 0, in->table);
+break;
+
+	case RGB_GAMMA_CH1_SELECTED:
+		vfe_write_gamma_table(1, 0, in->table);
+		break;
+
+	case RGB_GAMMA_CH2_SELECTED:
+		vfe_write_gamma_table(2, 0, in->table);
+		break;
+
+	case RGB_GAMMA_CH0_CH1_SELECTED:
+		vfe_write_gamma_table(0, 0, in->table);
+		vfe_write_gamma_table(1, 0, in->table);
+		break;
+
+	case RGB_GAMMA_CH0_CH2_SELECTED:
+		vfe_write_gamma_table(0, 0, in->table);
+		vfe_write_gamma_table(2, 0, in->table);
+		break;
+
+	case RGB_GAMMA_CH1_CH2_SELECTED:
+		vfe_write_gamma_table(1, 0, in->table);
+		vfe_write_gamma_table(2, 0, in->table);
+		break;
+
+	case RGB_GAMMA_CH0_CH1_CH2_SELECTED:
+		vfe_write_gamma_table(0, 0, in->table);
+		vfe_write_gamma_table(1, 0, in->table);
+		vfe_write_gamma_table(2, 0, in->table);
+		break;
+
+	default:
+		pr_err("%s: invalid gamma channel %d\n", __func__,
+			in->channelSelect);
+		rc = -EINVAL;
+		break;
+	} /* switch */
+
+	return rc;
+}
+
+void vfe_stats_af_ack(struct vfe_cmd_stats_af_ack *in)
+{
+	ctrl->afStatsControl.nextFrameAddrBuf = in->nextAFOutputBufferAddr;
+	ctrl->afStatsControl.ackPending = FALSE;
+}
+
+void vfe_stats_wb_exp_ack(struct vfe_cmd_stats_wb_exp_ack *in)
+{
+	ctrl->awbStatsControl.nextFrameAddrBuf = in->nextWbExpOutputBufferAddr;
+	ctrl->awbStatsControl.ackPending = FALSE;
+}
+
+
+void vfe_output_v_ack(struct vfe_cmd_output_ack *in)
+{
+	const uint32_t *psrc;
+	uint32_t *pdest;
+	uint8_t i;
+
+	pdest = ctrl->encPath.nextFrameAddrBuf;
+
+	CDBG("video_frame_ack: ack addr = 0x%x\n", in->ybufaddr[0]);
+
+	psrc = in->ybufaddr;
+	for (i = 0; i < ctrl->encPath.fragCount; i++)
+		*pdest++ = *psrc++;
+
+	psrc = in->chromabufaddr;
+	for (i = 0; i < ctrl->encPath.fragCount; i++)
+		*pdest++ = *psrc++;
+
+	ctrl->encPath.ackPending = FALSE;
+}
+
+void vfe_output_p_ack(struct vfe_cmd_output_ack *in)
+{
+	const uint32_t *psrc;
+	uint32_t *pdest;
+	uint8_t i;
+
+	if (ctrl->axiOutputMode == VFE_AXI_OUTPUT_MODE_Output1AndOutput2) {
+		/* video mode, preview comes from output1 path */
+
+	pdest = ctrl->viewPath.nextFrameAddrBuf;
+
+	psrc = in->ybufaddr;
+	for (i = 0; i < ctrl->viewPath.fragCount; i++)
+		*pdest++ = *psrc++;
+
+	psrc = in->chromabufaddr;
+	for (i = 0; i < ctrl->viewPath.fragCount; i++)
+		*pdest++ = *psrc++;
+
+	ctrl->viewPath.ackPending = FALSE;
+
+	} else { /* preview mode, preview comes from output2 path. */
+		pdest = ctrl->encPath.nextFrameAddrBuf;
+
+		psrc = in->ybufaddr;
+		for (i = 0; i < ctrl->encPath.fragCount; i++)
+			*pdest++ = *psrc++;
+
+		psrc = in->chromabufaddr;
+		for (i = 0; i < ctrl->encPath.fragCount; i++)
+			*pdest++ = *psrc++;
+
+		ctrl->encPath.ackPending = FALSE;
+
+	}
+}
+
+void vfe_start(struct vfe_cmd_start *in)
+{
+	uint32_t  pmstatus = 0;
+	boolean rawmode;
+	uint32_t  demperiod = 0;
+	uint32_t  demeven = 0;
+	uint32_t  demodd = 0;
+
+	/* derived from other commands.  (camif config, axi output config,
+	 * etc)
+	*/
+	struct vfe_cfg hwcfg;
+	struct vfe_upsample_cfg chromupcfg;
+
+	CDBG("vfe_start operationMode = %d\n", in->operationMode);
+
+	memset(&hwcfg, 0, sizeof(hwcfg));
+	memset(&chromupcfg, 0, sizeof(chromupcfg));
+
+	switch (in->pixel) {
+	case VFE_BAYER_RGRGRG:
+		demperiod = 1;
+		demeven = 0xC9;
+		demodd = 0xAC;
+		break;
+
+	case VFE_BAYER_GRGRGR:
+		demperiod = 1;
+		demeven = 0x9C;
+		demodd = 0xCA;
+		break;
+
+	case VFE_BAYER_BGBGBG:
+		demperiod = 1;
+		demeven = 0xCA;
+		demodd = 0x9C;
+		break;
+
+	case VFE_BAYER_GBGBGB:
+		demperiod = 1;
+		demeven = 0xAC;
+		demodd = 0xC9;
+		break;
+
+	case VFE_YUV_YCbYCr:
+		demperiod = 3;
+		demeven = 0x9CAC;
+		demodd = 0x9CAC;
+		break;
+
+	case VFE_YUV_YCrYCb:
+		demperiod = 3;
+		demeven = 0xAC9C;
+		demodd = 0xAC9C;
+		break;
+
+	case VFE_YUV_CbYCrY:
+		demperiod = 3;
+		demeven = 0xC9CA;
+		demodd = 0xC9CA;
+		break;
+
+	case VFE_YUV_CrYCbY:
+		demperiod = 3;
+		demeven = 0xCAC9;
+		demodd = 0xCAC9;
+		break;
+
+	default:
+		return;
+	}
+
+	vfe_config_demux(demperiod, demeven, demodd);
+
+	vfe_program_lut_bank_sel(&ctrl->vfeGammaLutSel);
+
+	/* save variables to local. */
+	ctrl->vfeOperationMode = in->operationMode;
+	if (ctrl->vfeOperationMode == VFE_START_OPERATION_MODE_SNAPSHOT) {
+
+		update_axi_qos(MSM_AXI_QOS_SNAPSHOT);
+		/* in snapshot mode, initialize snapshot count*/
+		ctrl->vfeSnapShotCount = in->snapshotCount;
+
+		/* save the requested count, this is temporarily done, to
+		help with HJR / multishot. */
+		ctrl->vfeRequestedSnapShotCount = ctrl->vfeSnapShotCount;
+
+		CDBG("requested snapshot count = %d\n", ctrl->vfeSnapShotCount);
+
+		/* Assumption is to have the same pattern and period for both
+		paths, if both paths are used. */
+		if (ctrl->viewPath.pathEnabled) {
+			ctrl->viewPath.snapshotPendingCount = in->snapshotCount;
+
+			ctrl->vfeFrameSkipPattern =
+				ctrl->vfeFrameSkip.output1Pattern;
+			ctrl->vfeFrameSkipPeriod =
+				ctrl->vfeFrameSkip.output1Period;
+		}
+
+		if (ctrl->encPath.pathEnabled) {
+			ctrl->encPath.snapshotPendingCount = in->snapshotCount;
+
+			ctrl->vfeFrameSkipPattern =
+				ctrl->vfeFrameSkip.output2Pattern;
+			ctrl->vfeFrameSkipPeriod =
+				ctrl->vfeFrameSkip.output2Period;
+		}
+	} else
+		update_axi_qos(MSM_AXI_QOS_PREVIEW);
+
+	/* enable color conversion for bayer sensor
+	if stats enabled, need to do color conversion. */
+	if (in->pixel <= VFE_BAYER_GBGBGB)
+		ctrl->vfeStatsCmdLocal.colorConversionEnable = TRUE;
+
+	vfe_program_stats_cmd(&ctrl->vfeStatsCmdLocal);
+
+	if (in->pixel >= VFE_YUV_YCbYCr)
+		ctrl->vfeModuleEnableLocal.chromaUpsampleEnable = TRUE;
+
+	ctrl->vfeModuleEnableLocal.demuxEnable = TRUE;
+
+	/* if any stats module is enabled, the main bit is enabled. */
+	ctrl->vfeModuleEnableLocal.statsEnable =
+		ctrl->vfeStatsCmdLocal.autoFocusEnable |
+		ctrl->vfeStatsCmdLocal.axwEnable;
+
+	vfe_reg_module_cfg(&ctrl->vfeModuleEnableLocal);
+
+	/* in case of offline processing, do not need to config camif. Having
+	 * bus output enabled in camif_config register might confuse the
+	 * hardware?
+	 */
+	if (in->inputSource != VFE_START_INPUT_SOURCE_AXI) {
+		vfe_reg_camif_config(&ctrl->vfeCamifConfigLocal);
+	} else {
+		/* offline processing, enable axi read */
+		ctrl->vfeBusConfigLocal.stripeRdPathEn = TRUE;
+		ctrl->vfeBusCmdLocal.stripeReload = TRUE;
+		ctrl->vfeBusConfigLocal.rawPixelDataSize =
+			ctrl->axiInputDataSize;
+	}
+
+	vfe_reg_bus_cfg(&ctrl->vfeBusConfigLocal);
+
+	/* directly from start command */
+	hwcfg.pixelPattern = in->pixel;
+	hwcfg.inputSource = in->inputSource;
+	writel(*(uint32_t *)&hwcfg, ctrl->vfebase + VFE_CFG);
+
+	/* regardless module enabled or not, it does not hurt
+	 * to program the cositing mode. */
+	chromupcfg.chromaCositingForYCbCrInputs = in->yuvInputCositingMode;
+
+	writel(*(uint32_t *)&chromupcfg,
+		ctrl->vfebase + VFE_CHROMA_UPSAMPLE_CFG);
+
+	/* MISR to monitor the axi read. */
+	writel(0xd8, ctrl->vfebase + VFE_BUS_MISR_MAST_CFG_0);
+
+	/* clear all pending interrupts. */
+	writel(VFE_CLEAR_ALL_IRQS, ctrl->vfebase + VFE_IRQ_CLEAR);
+
+	/*  define how composite interrupt work.  */
+	ctrl->vfeImaskCompositePacked =
+		vfe_irq_composite_pack(ctrl->vfeIrqCompositeMaskLocal);
+
+	vfe_program_irq_composite_mask(ctrl->vfeImaskCompositePacked);
+
+	/*  enable all necessary interrupts.      */
+	ctrl->vfeImaskLocal.camifSofIrq  = TRUE;
+	ctrl->vfeImaskLocal.regUpdateIrq = TRUE;
+	ctrl->vfeImaskLocal.resetAckIrq  = TRUE;
+
+	ctrl->vfeImaskPacked = vfe_irq_pack(ctrl->vfeImaskLocal);
+	vfe_program_irq_mask(ctrl->vfeImaskPacked);
+
+	/* enable bus performance monitor */
+	vfe_8k_pm_start(&ctrl->vfeBusPmConfigLocal);
+
+	/* trigger vfe reg update */
+	ctrl->vfeStartAckPendingFlag = TRUE;
+
+	/* write bus command to trigger reload of ping pong buffer. */
+	ctrl->vfeBusCmdLocal.busPingpongReload = TRUE;
+
+	if (ctrl->vfeModuleEnableLocal.statsEnable == TRUE) {
+		ctrl->vfeBusCmdLocal.statsPingpongReload = TRUE;
+		ctrl->vfeStatsPingPongReloadFlag = TRUE;
+	}
+
+	writel(VFE_REG_UPDATE_TRIGGER, ctrl->vfebase + VFE_REG_UPDATE_CMD);
+
+	/* program later than the reg update. */
+	vfe_reg_bus_cmd(&ctrl->vfeBusCmdLocal);
+
+	if ((in->inputSource ==
+			 VFE_START_INPUT_SOURCE_CAMIF) ||
+	    (in->inputSource == VFE_START_INPUT_SOURCE_TESTGEN))
+		writel(CAMIF_COMMAND_START, ctrl->vfebase + CAMIF_COMMAND);
+
+	/* start test gen if it is enabled */
+	if (ctrl->vfeTestGenStartFlag == TRUE) {
+		ctrl->vfeTestGenStartFlag = FALSE;
+		vfe_prog_hw_testgen_cmd(VFE_TEST_GEN_GO);
+	}
+
+	CDBG("ctrl->axiOutputMode = %d\n", ctrl->axiOutputMode);
+	if (ctrl->axiOutputMode == VFE_AXI_OUTPUT_MODE_CAMIFToAXIViaOutput2) {
+		/* raw dump mode */
+		rawmode = TRUE;
+
+		while (rawmode) {
+			pmstatus =
+				readl(ctrl->vfebase +
+					VFE_BUS_ENC_CBCR_WR_PM_STATS_1);
+
+			if ((pmstatus & VFE_PM_BUF_MAX_CNT_MASK) != 0)
+				rawmode = FALSE;
+		}
+
+		vfe_proc_ops(VFE_MSG_ID_START_ACK, NULL);
+		ctrl->vfeStartAckPendingFlag = FALSE;
+	}
+
+	ctrl->vstate = VFE_STATE_ACTIVE;
+}
+
+void vfe_la_update(struct vfe_cmd_la_config *in)
+{
+	int16_t *pTable;
+	enum VFE_DMI_RAM_SEL dmiRamSel;
+	int i;
+
+	pTable = in->table;
+	ctrl->vfeModuleEnableLocal.lumaAdaptationEnable = in->enable;
+
+	/* toggle the bank to be used. */
+	ctrl->vfeLaBankSel ^= 1;
+
+	if (ctrl->vfeLaBankSel == 0)
+		dmiRamSel = LUMA_ADAPT_LUT_RAM_BANK0;
+	else
+		dmiRamSel = LUMA_ADAPT_LUT_RAM_BANK1;
+
+	/* configure the DMI_CFG to select right sram */
+	vfe_program_dmi_cfg(dmiRamSel);
+
+	for (i = 0; i < VFE_LA_TABLE_LENGTH; i++) {
+		writel((uint32_t)(*pTable), ctrl->vfebase + VFE_DMI_DATA_LO);
+		pTable++;
+	}
+
+	/* After DMI transfer, to make it safe, need to set
+	 * the DMI_CFG to unselect any SRAM */
+	writel(VFE_DMI_CFG_DEFAULT, ctrl->vfebase + VFE_DMI_CFG);
+	writel(ctrl->vfeLaBankSel, ctrl->vfebase + VFE_LA_CFG);
+}
+
+void vfe_la_config(struct vfe_cmd_la_config *in)
+{
+	uint16_t i;
+	int16_t  *pTable;
+	enum VFE_DMI_RAM_SEL dmiRamSel;
+
+	pTable = in->table;
+	ctrl->vfeModuleEnableLocal.lumaAdaptationEnable = in->enable;
+
+	if (ctrl->vfeLaBankSel == 0)
+		dmiRamSel = LUMA_ADAPT_LUT_RAM_BANK0;
+	else
+		dmiRamSel = LUMA_ADAPT_LUT_RAM_BANK1;
+
+	/* configure the DMI_CFG to select right sram */
+	vfe_program_dmi_cfg(dmiRamSel);
+
+	for (i = 0; i < VFE_LA_TABLE_LENGTH; i++) {
+		writel((uint32_t)(*pTable), ctrl->vfebase + VFE_DMI_DATA_LO);
+		pTable++;
+	}
+
+	/* After DMI transfer, to make it safe, need to set the
+	 * DMI_CFG to unselect any SRAM */
+	writel(VFE_DMI_CFG_DEFAULT, ctrl->vfebase + VFE_DMI_CFG);
+
+	/* can only be bank 0 or bank 1 for now. */
+	writel(ctrl->vfeLaBankSel, ctrl->vfebase + VFE_LA_CFG);
+	CDBG("VFE Luma adaptation bank selection is 0x%x\n",
+			 *(uint32_t *)&ctrl->vfeLaBankSel);
+}
+
+void vfe_test_gen_start(struct vfe_cmd_test_gen_start *in)
+{
+	struct VFE_TestGen_ConfigCmdType cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	cmd.numFrame              = in->numFrame;
+	cmd.pixelDataSelect       = in->pixelDataSelect;
+	cmd.systematicDataSelect  = in->systematicDataSelect;
+	cmd.pixelDataSize         = (uint32_t)in->pixelDataSize;
+	cmd.hsyncEdge             = (uint32_t)in->hsyncEdge;
+	cmd.vsyncEdge             = (uint32_t)in->vsyncEdge;
+	cmd.imageWidth            = in->imageWidth;
+	cmd.imageHeight           = in->imageHeight;
+	cmd.sofOffset             = in->startOfFrameOffset;
+	cmd.eofNOffset            = in->endOfFrameNOffset;
+	cmd.solOffset             = in->startOfLineOffset;
+	cmd.eolNOffset            = in->endOfLineNOffset;
+	cmd.hBlankInterval        = in->hbi;
+	cmd.vBlankInterval        = in->vbl;
+	cmd.vBlankIntervalEnable  = in->vblEnable;
+	cmd.sofDummy              = in->startOfFrameDummyLine;
+	cmd.eofDummy              = in->endOfFrameDummyLine;
+	cmd.unicolorBarSelect     = in->unicolorBarSelect;
+	cmd.unicolorBarEnable     = in->unicolorBarEnable;
+	cmd.splitEnable           = in->colorBarsSplitEnable;
+	cmd.pixelPattern          = (uint32_t)in->colorBarsPixelPattern;
+	cmd.rotatePeriod          = in->colorBarsRotatePeriod;
+	cmd.randomSeed            = in->testGenRandomSeed;
+
+	vfe_prog_hw(ctrl->vfebase + VFE_HW_TESTGEN_CFG,
+		(uint32_t *) &cmd, sizeof(cmd));
+}
+
+void vfe_frame_skip_update(struct vfe_cmd_frame_skip_update *in)
+{
+	struct VFE_FRAME_SKIP_UpdateCmdType cmd;
+
+	cmd.yPattern    = in->output1Pattern;
+	cmd.cbcrPattern = in->output1Pattern;
+	vfe_prog_hw(ctrl->vfebase + VFE_FRAMEDROP_VIEW_Y_PATTERN,
+		(uint32_t *)&cmd, sizeof(cmd));
+
+	cmd.yPattern    = in->output2Pattern;
+	cmd.cbcrPattern = in->output2Pattern;
+	vfe_prog_hw(ctrl->vfebase + VFE_FRAMEDROP_ENC_Y_PATTERN,
+		(uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_frame_skip_config(struct vfe_cmd_frame_skip_config *in)
+{
+	struct vfe_frame_skip_cfg cmd;
+	memset(&cmd, 0, sizeof(cmd));
+
+	ctrl->vfeFrameSkip = *in;
+
+	cmd.output2YPeriod     = in->output2Period;
+	cmd.output2CbCrPeriod  = in->output2Period;
+	cmd.output2YPattern    = in->output2Pattern;
+	cmd.output2CbCrPattern = in->output2Pattern;
+	cmd.output1YPeriod     = in->output1Period;
+	cmd.output1CbCrPeriod  = in->output1Period;
+	cmd.output1YPattern    = in->output1Pattern;
+	cmd.output1CbCrPattern = in->output1Pattern;
+
+	vfe_prog_hw(ctrl->vfebase + VFE_FRAMEDROP_ENC_Y_CFG,
+		(uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_output_clamp_config(struct vfe_cmd_output_clamp_config *in)
+{
+	struct vfe_output_clamp_cfg cmd;
+	memset(&cmd, 0, sizeof(cmd));
+
+	cmd.yChanMax  = in->maxCh0;
+	cmd.cbChanMax = in->maxCh1;
+	cmd.crChanMax = in->maxCh2;
+
+	cmd.yChanMin  = in->minCh0;
+	cmd.cbChanMin = in->minCh1;
+	cmd.crChanMin = in->minCh2;
+
+	vfe_prog_hw(ctrl->vfebase + VFE_CLAMP_MAX_CFG, (uint32_t *)&cmd,
+		sizeof(cmd));
+}
+
+void vfe_camif_frame_update(struct vfe_cmds_camif_frame *in)
+{
+	struct vfe_camifframe_update cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	cmd.pixelsPerLine = in->pixelsPerLine;
+	cmd.linesPerFrame = in->linesPerFrame;
+
+	vfe_prog_hw(ctrl->vfebase + CAMIF_FRAME_CONFIG, (uint32_t *)&cmd,
+		sizeof(cmd));
+}
+
+void vfe_color_correction_config(struct vfe_cmd_color_correction_config *in)
+{
+	struct vfe_color_correction_cfg cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	ctrl->vfeModuleEnableLocal.colorCorrectionEnable = in->enable;
+
+	cmd.c0 = in->C0;
+	cmd.c1 = in->C1;
+	cmd.c2 = in->C2;
+	cmd.c3 = in->C3;
+	cmd.c4 = in->C4;
+	cmd.c5 = in->C5;
+	cmd.c6 = in->C6;
+	cmd.c7 = in->C7;
+	cmd.c8 = in->C8;
+
+	cmd.k0 = in->K0;
+	cmd.k1 = in->K1;
+	cmd.k2 = in->K2;
+
+	cmd.coefQFactor = in->coefQFactor;
+
+	vfe_prog_hw(ctrl->vfebase + VFE_COLOR_CORRECT_COEFF_0,
+		(uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_demosaic_abf_update(struct vfe_cmd_demosaic_abf_update *in)
+{
+struct vfe_demosaic_cfg cmd;
+	struct vfe_demosaic_abf_cfg cmdabf;
+	uint32_t temp;
+
+	memset(&cmd, 0, sizeof(cmd));
+	temp = readl(ctrl->vfebase + VFE_DEMOSAIC_CFG);
+
+	cmd = *((struct vfe_demosaic_cfg *)(&temp));
+	cmd.abfEnable       = in->abfUpdate.enable;
+	cmd.forceAbfOn      = in->abfUpdate.forceOn;
+	cmd.abfShift        = in->abfUpdate.shift;
+	vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_CFG,
+		(uint32_t *)&cmd, sizeof(cmd));
+
+	cmdabf.lpThreshold  = in->abfUpdate.lpThreshold;
+	cmdabf.ratio        = in->abfUpdate.ratio;
+	cmdabf.minValue     = in->abfUpdate.min;
+	cmdabf.maxValue     = in->abfUpdate.max;
+	vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_ABF_CFG_0,
+		(uint32_t *)&cmdabf, sizeof(cmdabf));
+}
+
+void vfe_demosaic_bpc_update(struct vfe_cmd_demosaic_bpc_update *in)
+{
+	struct vfe_demosaic_cfg cmd;
+	struct vfe_demosaic_bpc_cfg cmdbpc;
+	uint32_t temp;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	temp = readl(ctrl->vfebase + VFE_DEMOSAIC_CFG);
+
+	cmd = *((struct vfe_demosaic_cfg *)(&temp));
+	cmd.badPixelCorrEnable = in->bpcUpdate.enable;
+	cmd.fminThreshold      = in->bpcUpdate.fminThreshold;
+	cmd.fmaxThreshold      = in->bpcUpdate.fmaxThreshold;
+
+	vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_CFG,
+		(uint32_t *)&cmd, sizeof(cmd));
+
+	cmdbpc.blueDiffThreshold  = in->bpcUpdate.blueDiffThreshold;
+	cmdbpc.redDiffThreshold   = in->bpcUpdate.redDiffThreshold;
+	cmdbpc.greenDiffThreshold = in->bpcUpdate.greenDiffThreshold;
+
+	vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_BPC_CFG_0,
+		(uint32_t *)&cmdbpc, sizeof(cmdbpc));
+}
+
+void vfe_demosaic_config(struct vfe_cmd_demosaic_config *in)
+{
+	struct vfe_demosaic_cfg cmd;
+	struct vfe_demosaic_bpc_cfg cmd_bpc;
+	struct vfe_demosaic_abf_cfg cmd_abf;
+
+	memset(&cmd, 0, sizeof(cmd));
+	memset(&cmd_bpc, 0, sizeof(cmd_bpc));
+	memset(&cmd_abf, 0, sizeof(cmd_abf));
+
+	ctrl->vfeModuleEnableLocal.demosaicEnable = in->enable;
+
+	cmd.abfEnable          = in->abfConfig.enable;
+	cmd.badPixelCorrEnable = in->bpcConfig.enable;
+	cmd.forceAbfOn         = in->abfConfig.forceOn;
+	cmd.abfShift           = in->abfConfig.shift;
+	cmd.fminThreshold      = in->bpcConfig.fminThreshold;
+	cmd.fmaxThreshold      = in->bpcConfig.fmaxThreshold;
+	cmd.slopeShift         = in->slopeShift;
+
+	vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_CFG,
+		(uint32_t *)&cmd, sizeof(cmd));
+
+	cmd_abf.lpThreshold = in->abfConfig.lpThreshold;
+	cmd_abf.ratio       = in->abfConfig.ratio;
+	cmd_abf.minValue    = in->abfConfig.min;
+	cmd_abf.maxValue    = in->abfConfig.max;
+
+	vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_ABF_CFG_0,
+		(uint32_t *)&cmd_abf, sizeof(cmd_abf));
+
+	cmd_bpc.blueDiffThreshold   = in->bpcConfig.blueDiffThreshold;
+	cmd_bpc.redDiffThreshold    = in->bpcConfig.redDiffThreshold;
+	cmd_bpc.greenDiffThreshold  = in->bpcConfig.greenDiffThreshold;
+
+	vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_BPC_CFG_0,
+		(uint32_t *)&cmd_bpc, sizeof(cmd_bpc));
+}
+
+void vfe_demux_channel_gain_update(struct vfe_cmd_demux_channel_gain_config *in)
+{
+	struct vfe_demux_cfg cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	cmd.ch0EvenGain  = in->ch0EvenGain;
+	cmd.ch0OddGain   = in->ch0OddGain;
+	cmd.ch1Gain      = in->ch1Gain;
+	cmd.ch2Gain      = in->ch2Gain;
+
+	vfe_prog_hw(ctrl->vfebase + VFE_DEMUX_GAIN_0,
+		(uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_demux_channel_gain_config(struct vfe_cmd_demux_channel_gain_config *in)
+{
+	struct vfe_demux_cfg cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	cmd.ch0EvenGain = in->ch0EvenGain;
+	cmd.ch0OddGain  = in->ch0OddGain;
+	cmd.ch1Gain     = in->ch1Gain;
+	cmd.ch2Gain     = in->ch2Gain;
+
+	vfe_prog_hw(ctrl->vfebase + VFE_DEMUX_GAIN_0,
+		(uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_black_level_update(struct vfe_cmd_black_level_config *in)
+{
+	struct vfe_blacklevel_cfg cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	ctrl->vfeModuleEnableLocal.blackLevelCorrectionEnable = in->enable;
+
+	cmd.evenEvenAdjustment = in->evenEvenAdjustment;
+	cmd.evenOddAdjustment  = in->evenOddAdjustment;
+	cmd.oddEvenAdjustment  = in->oddEvenAdjustment;
+	cmd.oddOddAdjustment   = in->oddOddAdjustment;
+
+	vfe_prog_hw(ctrl->vfebase + VFE_BLACK_EVEN_EVEN_VALUE,
+		(uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_black_level_config(struct vfe_cmd_black_level_config *in)
+{
+	struct vfe_blacklevel_cfg cmd;
+	memset(&cmd, 0, sizeof(cmd));
+
+	ctrl->vfeModuleEnableLocal.blackLevelCorrectionEnable = in->enable;
+
+	cmd.evenEvenAdjustment = in->evenEvenAdjustment;
+	cmd.evenOddAdjustment  = in->evenOddAdjustment;
+	cmd.oddEvenAdjustment  = in->oddEvenAdjustment;
+	cmd.oddOddAdjustment   = in->oddOddAdjustment;
+
+	vfe_prog_hw(ctrl->vfebase + VFE_BLACK_EVEN_EVEN_VALUE,
+		(uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_asf_update(struct vfe_cmd_asf_update *in)
+{
+	struct vfe_asf_update cmd;
+	memset(&cmd, 0, sizeof(cmd));
+
+	ctrl->vfeModuleEnableLocal.asfEnable = in->enable;
+
+	cmd.smoothEnable     = in->smoothFilterEnabled;
+	cmd.sharpMode        = in->sharpMode;
+	cmd.smoothCoeff0     = in->smoothCoefCenter;
+	cmd.smoothCoeff1     = in->smoothCoefSurr;
+	cmd.cropEnable       = in->cropEnable;
+	cmd.sharpThresholdE1 = in->sharpThreshE1;
+	cmd.sharpDegreeK1    = in->sharpK1;
+	cmd.sharpDegreeK2    = in->sharpK2;
+	cmd.normalizeFactor  = in->normalizeFactor;
+	cmd.sharpThresholdE2 = in->sharpThreshE2;
+	cmd.sharpThresholdE3 = in->sharpThreshE3;
+	cmd.sharpThresholdE4 = in->sharpThreshE4;
+	cmd.sharpThresholdE5 = in->sharpThreshE5;
+	cmd.F1Coeff0         = in->filter1Coefficients[0];
+	cmd.F1Coeff1         = in->filter1Coefficients[1];
+	cmd.F1Coeff2         = in->filter1Coefficients[2];
+	cmd.F1Coeff3         = in->filter1Coefficients[3];
+	cmd.F1Coeff4         = in->filter1Coefficients[4];
+	cmd.F1Coeff5         = in->filter1Coefficients[5];
+	cmd.F1Coeff6         = in->filter1Coefficients[6];
+	cmd.F1Coeff7         = in->filter1Coefficients[7];
+	cmd.F1Coeff8         = in->filter1Coefficients[8];
+	cmd.F2Coeff0         = in->filter2Coefficients[0];
+	cmd.F2Coeff1         = in->filter2Coefficients[1];
+	cmd.F2Coeff2         = in->filter2Coefficients[2];
+	cmd.F2Coeff3         = in->filter2Coefficients[3];
+	cmd.F2Coeff4         = in->filter2Coefficients[4];
+	cmd.F2Coeff5         = in->filter2Coefficients[5];
+	cmd.F2Coeff6         = in->filter2Coefficients[6];
+	cmd.F2Coeff7         = in->filter2Coefficients[7];
+	cmd.F2Coeff8         = in->filter2Coefficients[8];
+
+	vfe_prog_hw(ctrl->vfebase + VFE_ASF_CFG,
+		(uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_asf_config(struct vfe_cmd_asf_config *in)
+{
+	struct vfe_asf_update     cmd;
+	struct vfe_asfcrop_cfg cmd2;
+
+	memset(&cmd, 0, sizeof(cmd));
+	memset(&cmd2, 0, sizeof(cmd2));
+
+	ctrl->vfeModuleEnableLocal.asfEnable = in->enable;
+
+	cmd.smoothEnable       = in->smoothFilterEnabled;
+	cmd.sharpMode          = in->sharpMode;
+	cmd.smoothCoeff0       = in->smoothCoefCenter;
+	cmd.smoothCoeff1       = in->smoothCoefSurr;
+	cmd.cropEnable         = in->cropEnable;
+	cmd.sharpThresholdE1   = in->sharpThreshE1;
+	cmd.sharpDegreeK1      = in->sharpK1;
+	cmd.sharpDegreeK2      = in->sharpK2;
+	cmd.normalizeFactor    = in->normalizeFactor;
+	cmd.sharpThresholdE2   = in->sharpThreshE2;
+	cmd.sharpThresholdE3   = in->sharpThreshE3;
+	cmd.sharpThresholdE4   = in->sharpThreshE4;
+	cmd.sharpThresholdE5   = in->sharpThreshE5;
+	cmd.F1Coeff0           = in->filter1Coefficients[0];
+	cmd.F1Coeff1           = in->filter1Coefficients[1];
+	cmd.F1Coeff2           = in->filter1Coefficients[2];
+	cmd.F1Coeff3           = in->filter1Coefficients[3];
+	cmd.F1Coeff4           = in->filter1Coefficients[4];
+	cmd.F1Coeff5           = in->filter1Coefficients[5];
+	cmd.F1Coeff6           = in->filter1Coefficients[6];
+	cmd.F1Coeff7           = in->filter1Coefficients[7];
+	cmd.F1Coeff8           = in->filter1Coefficients[8];
+	cmd.F2Coeff0           = in->filter2Coefficients[0];
+	cmd.F2Coeff1           = in->filter2Coefficients[1];
+	cmd.F2Coeff2           = in->filter2Coefficients[2];
+	cmd.F2Coeff3           = in->filter2Coefficients[3];
+	cmd.F2Coeff4           = in->filter2Coefficients[4];
+	cmd.F2Coeff5           = in->filter2Coefficients[5];
+	cmd.F2Coeff6           = in->filter2Coefficients[6];
+	cmd.F2Coeff7           = in->filter2Coefficients[7];
+	cmd.F2Coeff8           = in->filter2Coefficients[8];
+
+	vfe_prog_hw(ctrl->vfebase + VFE_ASF_CFG,
+		(uint32_t *)&cmd, sizeof(cmd));
+
+	cmd2.firstLine  = in->cropFirstLine;
+	cmd2.lastLine   = in->cropLastLine;
+	cmd2.firstPixel = in->cropFirstPixel;
+	cmd2.lastPixel  = in->cropLastPixel;
+
+	vfe_prog_hw(ctrl->vfebase + VFE_ASF_CROP_WIDTH_CFG,
+		(uint32_t *)&cmd2, sizeof(cmd2));
+}
+
+void vfe_white_balance_config(struct vfe_cmd_white_balance_config *in)
+{
+	struct vfe_wb_cfg cmd;
+	memset(&cmd, 0, sizeof(cmd));
+
+	ctrl->vfeModuleEnableLocal.whiteBalanceEnable = in->enable;
+
+	cmd.ch0Gain = in->ch0Gain;
+	cmd.ch1Gain = in->ch1Gain;
+	cmd.ch2Gain = in->ch2Gain;
+
+	vfe_prog_hw(ctrl->vfebase + VFE_WB_CFG,
+		(uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_chroma_sup_config(struct vfe_cmd_chroma_suppression_config *in)
+{
+	struct vfe_chroma_suppress_cfg cmd;
+	memset(&cmd, 0, sizeof(cmd));
+
+	ctrl->vfeModuleEnableLocal.chromaSuppressionEnable = in->enable;
+
+	cmd.m1  = in->m1;
+	cmd.m3  = in->m3;
+	cmd.n1  = in->n1;
+	cmd.n3  = in->n3;
+	cmd.mm1 = in->mm1;
+	cmd.nn1 = in->nn1;
+
+	vfe_prog_hw(ctrl->vfebase + VFE_CHROMA_SUPPRESS_CFG_0,
+		(uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_roll_off_config(struct vfe_cmd_roll_off_config *in)
+{
+	struct vfe_rolloff_cfg cmd;
+	memset(&cmd, 0, sizeof(cmd));
+
+	ctrl->vfeModuleEnableLocal.lensRollOffEnable = in->enable;
+
+	cmd.gridWidth   = in->gridWidth;
+	cmd.gridHeight  = in->gridHeight;
+	cmd.yDelta      = in->yDelta;
+	cmd.gridX       = in->gridXIndex;
+	cmd.gridY       = in->gridYIndex;
+	cmd.pixelX      = in->gridPixelXIndex;
+	cmd.pixelY      = in->gridPixelYIndex;
+	cmd.yDeltaAccum = in->yDeltaAccum;
+
+	vfe_prog_hw(ctrl->vfebase + VFE_ROLLOFF_CFG_0,
+		(uint32_t *)&cmd, sizeof(cmd));
+
+	vfe_write_lens_roll_off_table(in);
+}
+
+void vfe_chroma_subsample_config(struct vfe_cmd_chroma_subsample_config *in)
+{
+	struct vfe_chromasubsample_cfg cmd;
+	memset(&cmd, 0, sizeof(cmd));
+
+	ctrl->vfeModuleEnableLocal.chromaSubsampleEnable = in->enable;
+
+	cmd.hCositedPhase       = in->hCositedPhase;
+	cmd.vCositedPhase       = in->vCositedPhase;
+	cmd.hCosited            = in->hCosited;
+	cmd.vCosited            = in->vCosited;
+	cmd.hsubSampleEnable    = in->hsubSampleEnable;
+	cmd.vsubSampleEnable    = in->vsubSampleEnable;
+	cmd.cropEnable          = in->cropEnable;
+	cmd.cropWidthLastPixel  = in->cropWidthLastPixel;
+	cmd.cropWidthFirstPixel = in->cropWidthFirstPixel;
+	cmd.cropHeightLastLine  = in->cropHeightLastLine;
+	cmd.cropHeightFirstLine = in->cropHeightFirstLine;
+
+	vfe_prog_hw(ctrl->vfebase + VFE_CHROMA_SUBSAMPLE_CFG,
+		(uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_chroma_enhan_config(struct vfe_cmd_chroma_enhan_config *in)
+{
+	struct vfe_chroma_enhance_cfg cmd;
+	struct vfe_color_convert_cfg cmd2;
+
+	memset(&cmd, 0, sizeof(cmd));
+	memset(&cmd2, 0, sizeof(cmd2));
+
+	ctrl->vfeModuleEnableLocal.chromaEnhanEnable = in->enable;
+
+	cmd.ap             = in->ap;
+	cmd.am             = in->am;
+	cmd.bp             = in->bp;
+	cmd.bm             = in->bm;
+	cmd.cp             = in->cp;
+	cmd.cm             = in->cm;
+	cmd.dp             = in->dp;
+	cmd.dm             = in->dm;
+	cmd.kcb            = in->kcb;
+	cmd.kcr            = in->kcr;
+
+	cmd2.v0            = in->RGBtoYConversionV0;
+	cmd2.v1            = in->RGBtoYConversionV1;
+	cmd2.v2            = in->RGBtoYConversionV2;
+	cmd2.ConvertOffset = in->RGBtoYConversionOffset;
+
+	vfe_prog_hw(ctrl->vfebase + VFE_CHROMA_ENHAN_A,
+		(uint32_t *)&cmd, sizeof(cmd));
+
+	vfe_prog_hw(ctrl->vfebase + VFE_COLOR_CONVERT_COEFF_0,
+		(uint32_t *)&cmd2, sizeof(cmd2));
+}
+
+void vfe_scaler2cbcr_config(struct vfe_cmd_scaler2_config *in)
+{
+	struct vfe_scaler2_cfg cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	ctrl->vfeModuleEnableLocal.scaler2CbcrEnable = in->enable;
+
+	cmd.hEnable              = in->hconfig.enable;
+	cmd.vEnable              = in->vconfig.enable;
+	cmd.inWidth              = in->hconfig.inputSize;
+	cmd.outWidth             = in->hconfig.outputSize;
+	cmd.horizPhaseMult       = in->hconfig.phaseMultiplicationFactor;
+	cmd.horizInterResolution = in->hconfig.interpolationResolution;
+	cmd.inHeight             = in->vconfig.inputSize;
+	cmd.outHeight            = in->vconfig.outputSize;
+	cmd.vertPhaseMult        = in->vconfig.phaseMultiplicationFactor;
+	cmd.vertInterResolution  = in->vconfig.interpolationResolution;
+
+	vfe_prog_hw(ctrl->vfebase + VFE_SCALE_CBCR_CFG,
+		(uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_scaler2y_config(struct vfe_cmd_scaler2_config *in)
+{
+	struct vfe_scaler2_cfg cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	ctrl->vfeModuleEnableLocal.scaler2YEnable = in->enable;
+
+	cmd.hEnable               = in->hconfig.enable;
+	cmd.vEnable               = in->vconfig.enable;
+	cmd.inWidth               = in->hconfig.inputSize;
+	cmd.outWidth              = in->hconfig.outputSize;
+	cmd.horizPhaseMult        = in->hconfig.phaseMultiplicationFactor;
+	cmd.horizInterResolution  = in->hconfig.interpolationResolution;
+	cmd.inHeight              = in->vconfig.inputSize;
+	cmd.outHeight             = in->vconfig.outputSize;
+	cmd.vertPhaseMult         = in->vconfig.phaseMultiplicationFactor;
+	cmd.vertInterResolution   = in->vconfig.interpolationResolution;
+
+	vfe_prog_hw(ctrl->vfebase + VFE_SCALE_Y_CFG,
+		(uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_main_scaler_config(struct vfe_cmd_main_scaler_config *in)
+{
+	struct vfe_main_scaler_cfg cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	ctrl->vfeModuleEnableLocal.mainScalerEnable = in->enable;
+
+	cmd.hEnable              = in->hconfig.enable;
+	cmd.vEnable              = in->vconfig.enable;
+	cmd.inWidth              = in->hconfig.inputSize;
+	cmd.outWidth             = in->hconfig.outputSize;
+	cmd.horizPhaseMult       = in->hconfig.phaseMultiplicationFactor;
+	cmd.horizInterResolution = in->hconfig.interpolationResolution;
+	cmd.horizMNInit          = in->MNInitH.MNCounterInit;
+	cmd.horizPhaseInit       = in->MNInitH.phaseInit;
+	cmd.inHeight             = in->vconfig.inputSize;
+	cmd.outHeight            = in->vconfig.outputSize;
+	cmd.vertPhaseMult        = in->vconfig.phaseMultiplicationFactor;
+	cmd.vertInterResolution  = in->vconfig.interpolationResolution;
+	cmd.vertMNInit           = in->MNInitV.MNCounterInit;
+	cmd.vertPhaseInit        = in->MNInitV.phaseInit;
+
+	vfe_prog_hw(ctrl->vfebase + VFE_SCALE_CFG,
+		(uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_stats_wb_exp_stop(void)
+{
+	ctrl->vfeStatsCmdLocal.axwEnable = FALSE;
+	ctrl->vfeImaskLocal.awbPingpongIrq = FALSE;
+}
+
+void vfe_stats_update_wb_exp(struct vfe_cmd_stats_wb_exp_update *in)
+{
+	struct vfe_statsawb_update   cmd;
+	struct vfe_statsawbae_update cmd2;
+
+	memset(&cmd, 0, sizeof(cmd));
+	memset(&cmd2, 0, sizeof(cmd2));
+
+	cmd.m1  = in->awbMCFG[0];
+	cmd.m2  = in->awbMCFG[1];
+	cmd.m3  = in->awbMCFG[2];
+	cmd.m4  = in->awbMCFG[3];
+	cmd.c1  = in->awbCCFG[0];
+	cmd.c2  = in->awbCCFG[1];
+	cmd.c3  = in->awbCCFG[2];
+	cmd.c4  = in->awbCCFG[3];
+	vfe_prog_hw(ctrl->vfebase + VFE_STATS_AWB_MCFG,
+		(uint32_t *)&cmd, sizeof(cmd));
+
+	cmd2.aeRegionCfg    = in->wbExpRegions;
+	cmd2.aeSubregionCfg = in->wbExpSubRegion;
+	cmd2.awbYMin        = in->awbYMin;
+	cmd2.awbYMax        = in->awbYMax;
+	vfe_prog_hw(ctrl->vfebase + VFE_STATS_AWBAE_CFG,
+		(uint32_t *)&cmd2, sizeof(cmd2));
+}
+
+void vfe_stats_update_af(struct vfe_cmd_stats_af_update *in)
+{
+	struct vfe_statsaf_update cmd;
+	memset(&cmd, 0, sizeof(cmd));
+
+	cmd.windowVOffset = in->windowVOffset;
+	cmd.windowHOffset = in->windowHOffset;
+	cmd.windowMode    = in->windowMode;
+	cmd.windowHeight  = in->windowHeight;
+	cmd.windowWidth   = in->windowWidth;
+
+	vfe_prog_hw(ctrl->vfebase + VFE_STATS_AF_CFG,
+		(uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_stats_start_wb_exp(struct vfe_cmd_stats_wb_exp_start *in)
+{
+	struct vfe_statsawb_update   cmd;
+	struct vfe_statsawbae_update cmd2;
+	struct vfe_statsaxw_hdr_cfg  cmd3;
+
+	ctrl->vfeStatsCmdLocal.axwEnable   =  in->enable;
+	ctrl->vfeImaskLocal.awbPingpongIrq = TRUE;
+
+	memset(&cmd, 0, sizeof(cmd));
+	memset(&cmd2, 0, sizeof(cmd2));
+	memset(&cmd3, 0, sizeof(cmd3));
+
+	cmd.m1  = in->awbMCFG[0];
+	cmd.m2  = in->awbMCFG[1];
+	cmd.m3  = in->awbMCFG[2];
+	cmd.m4  = in->awbMCFG[3];
+	cmd.c1  = in->awbCCFG[0];
+	cmd.c2  = in->awbCCFG[1];
+	cmd.c3  = in->awbCCFG[2];
+	cmd.c4  = in->awbCCFG[3];
+	vfe_prog_hw(ctrl->vfebase + VFE_STATS_AWB_MCFG,
+		(uint32_t *)&cmd, sizeof(cmd));
+
+	cmd2.aeRegionCfg     = in->wbExpRegions;
+	cmd2.aeSubregionCfg  = in->wbExpSubRegion;
+	cmd2.awbYMin         = in->awbYMin;
+	cmd2.awbYMax         = in->awbYMax;
+	vfe_prog_hw(ctrl->vfebase + VFE_STATS_AWBAE_CFG,
+		(uint32_t *)&cmd2, sizeof(cmd2));
+
+	cmd3.axwHeader       = in->axwHeader;
+	vfe_prog_hw(ctrl->vfebase + VFE_STATS_AXW_HEADER,
+		(uint32_t *)&cmd3, sizeof(cmd3));
+}
+
+void vfe_stats_start_af(struct vfe_cmd_stats_af_start *in)
+{
+	struct vfe_statsaf_update cmd;
+	struct vfe_statsaf_cfg    cmd2;
+
+	memset(&cmd, 0, sizeof(cmd));
+	memset(&cmd2, 0, sizeof(cmd2));
+
+	ctrl->vfeStatsCmdLocal.autoFocusEnable = in->enable;
+	ctrl->vfeImaskLocal.afPingpongIrq = TRUE;
+
+	cmd.windowVOffset = in->windowVOffset;
+	cmd.windowHOffset = in->windowHOffset;
+	cmd.windowMode    = in->windowMode;
+	cmd.windowHeight  = in->windowHeight;
+	cmd.windowWidth   = in->windowWidth;
+
+	vfe_prog_hw(ctrl->vfebase + VFE_STATS_AF_CFG,
+		(uint32_t *)&cmd, sizeof(cmd));
+
+	cmd2.a00       = in->highPassCoef[0];
+	cmd2.a04       = in->highPassCoef[1];
+	cmd2.a20       = in->highPassCoef[2];
+	cmd2.a21       = in->highPassCoef[3];
+	cmd2.a22       = in->highPassCoef[4];
+	cmd2.a23       = in->highPassCoef[5];
+	cmd2.a24       = in->highPassCoef[6];
+	cmd2.fvMax     = in->metricMax;
+	cmd2.fvMetric  = in->metricSelection;
+	cmd2.afHeader  = in->bufferHeader;
+	cmd2.entry00   = in->gridForMultiWindows[0];
+	cmd2.entry01   = in->gridForMultiWindows[1];
+	cmd2.entry02   = in->gridForMultiWindows[2];
+	cmd2.entry03   = in->gridForMultiWindows[3];
+	cmd2.entry10   = in->gridForMultiWindows[4];
+	cmd2.entry11   = in->gridForMultiWindows[5];
+	cmd2.entry12   = in->gridForMultiWindows[6];
+	cmd2.entry13   = in->gridForMultiWindows[7];
+	cmd2.entry20   = in->gridForMultiWindows[8];
+	cmd2.entry21   = in->gridForMultiWindows[9];
+	cmd2.entry22   = in->gridForMultiWindows[10];
+	cmd2.entry23   = in->gridForMultiWindows[11];
+	cmd2.entry30   = in->gridForMultiWindows[12];
+	cmd2.entry31   = in->gridForMultiWindows[13];
+	cmd2.entry32   = in->gridForMultiWindows[14];
+	cmd2.entry33   = in->gridForMultiWindows[15];
+
+	vfe_prog_hw(ctrl->vfebase + VFE_STATS_AF_GRID_0,
+		(uint32_t *)&cmd2, sizeof(cmd2));
+}
+
+void vfe_stats_setting(struct vfe_cmd_stats_setting *in)
+{
+	struct vfe_statsframe cmd1;
+	struct vfe_busstats_wrprio cmd2;
+
+	memset(&cmd1, 0, sizeof(cmd1));
+	memset(&cmd2, 0, sizeof(cmd2));
+
+	ctrl->afStatsControl.addressBuffer[0] = in->afBuffer[0];
+	ctrl->afStatsControl.addressBuffer[1] = in->afBuffer[1];
+	ctrl->afStatsControl.nextFrameAddrBuf = in->afBuffer[2];
+
+	ctrl->awbStatsControl.addressBuffer[0] = in->awbBuffer[0];
+	ctrl->awbStatsControl.addressBuffer[1] = in->awbBuffer[1];
+	ctrl->awbStatsControl.nextFrameAddrBuf = in->awbBuffer[2];
+
+	cmd1.lastPixel = in->frameHDimension;
+	cmd1.lastLine  = in->frameVDimension;
+	vfe_prog_hw(ctrl->vfebase + VFE_STATS_FRAME_SIZE,
+		(uint32_t *)&cmd1, sizeof(cmd1));
+
+	cmd2.afBusPriority    = in->afBusPriority;
+	cmd2.awbBusPriority   = in->awbBusPriority;
+	cmd2.histBusPriority  = in->histBusPriority;
+	cmd2.afBusPriorityEn  = in->afBusPrioritySelection;
+	cmd2.awbBusPriorityEn = in->awbBusPrioritySelection;
+	cmd2.histBusPriorityEn = in->histBusPrioritySelection;
+
+	vfe_prog_hw(ctrl->vfebase + VFE_BUS_STATS_WR_PRIORITY,
+		(uint32_t *)&cmd2, sizeof(cmd2));
+
+	/* Program the bus ping pong address for statistics modules. */
+	writel(in->afBuffer[0], ctrl->vfebase + VFE_BUS_STATS_AF_WR_PING_ADDR);
+	writel(in->afBuffer[1], ctrl->vfebase + VFE_BUS_STATS_AF_WR_PONG_ADDR);
+	writel(in->awbBuffer[0],
+		ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PING_ADDR);
+	writel(in->awbBuffer[1],
+		ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PONG_ADDR);
+	writel(in->histBuffer[0],
+		ctrl->vfebase + VFE_BUS_STATS_HIST_WR_PING_ADDR);
+	writel(in->histBuffer[1],
+		ctrl->vfebase + VFE_BUS_STATS_HIST_WR_PONG_ADDR);
+}
+
+void vfe_axi_input_config(struct vfe_cmd_axi_input_config *in)
+{
+	struct VFE_AxiInputCmdType cmd;
+	uint32_t xSizeWord, axiRdUnpackPattern;
+	uint8_t  axiInputPpw;
+	uint32_t busPingpongRdIrqEnable;
+
+	ctrl->vfeImaskLocal.rdPingpongIrq = TRUE;
+
+	switch (in->pixelSize) {
+	case VFE_RAW_PIXEL_DATA_SIZE_10BIT:
+		ctrl->axiInputDataSize = VFE_RAW_PIXEL_DATA_SIZE_10BIT;
+		break;
+
+	case VFE_RAW_PIXEL_DATA_SIZE_12BIT:
+		ctrl->axiInputDataSize = VFE_RAW_PIXEL_DATA_SIZE_12BIT;
+		break;
+
+	case VFE_RAW_PIXEL_DATA_SIZE_8BIT:
+	default:
+		ctrl->axiInputDataSize = VFE_RAW_PIXEL_DATA_SIZE_8BIT;
+		break;
+	}
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	switch (in->pixelSize) {
+	case VFE_RAW_PIXEL_DATA_SIZE_10BIT:
+		axiInputPpw = 6;
+		axiRdUnpackPattern = 0xD43210;
+		break;
+
+	case VFE_RAW_PIXEL_DATA_SIZE_12BIT:
+		axiInputPpw = 5;
+		axiRdUnpackPattern = 0xC3210;
+		break;
+
+	case VFE_RAW_PIXEL_DATA_SIZE_8BIT:
+	default:
+		axiInputPpw = 8;
+		axiRdUnpackPattern = 0xF6543210;
+		break;
+	}
+
+	xSizeWord =
+		((((in->xOffset % axiInputPpw) + in->xSize) +
+			(axiInputPpw-1)) / axiInputPpw) - 1;
+
+	cmd.stripeStartAddr0  = in->fragAddr[0];
+	cmd.stripeStartAddr1  = in->fragAddr[1];
+	cmd.stripeStartAddr2  = in->fragAddr[2];
+	cmd.stripeStartAddr3  = in->fragAddr[3];
+	cmd.ySize             = in->ySize;
+	cmd.yOffsetDelta      = 0;
+	cmd.xSizeWord         = xSizeWord;
+	cmd.burstLength       = 1;
+	cmd.NumOfRows         = in->numOfRows;
+	cmd.RowIncrement = (in->rowIncrement + (axiInputPpw - 1)) / axiInputPpw;
+	cmd.mainUnpackHeight  = in->ySize;
+	cmd.mainUnpackWidth   = in->xSize - 1;
+	cmd.mainUnpackHbiSel  = (uint32_t)in->unpackHbi;
+	cmd.mainUnpackPhase   = in->unpackPhase;
+	cmd.unpackPattern     = axiRdUnpackPattern;
+	cmd.padLeft           = in->padRepeatCountLeft;
+	cmd.padRight          = in->padRepeatCountRight;
+	cmd.padTop            = in->padRepeatCountTop;
+	cmd.padBottom         = in->padRepeatCountBottom;
+	cmd.leftUnpackPattern0   = in->padLeftComponentSelectCycle0;
+	cmd.leftUnpackPattern1   = in->padLeftComponentSelectCycle1;
+	cmd.leftUnpackPattern2   = in->padLeftComponentSelectCycle2;
+	cmd.leftUnpackPattern3   = in->padLeftComponentSelectCycle3;
+	cmd.leftUnpackStop0      = in->padLeftStopCycle0;
+	cmd.leftUnpackStop1      = in->padLeftStopCycle1;
+	cmd.leftUnpackStop2      = in->padLeftStopCycle2;
+	cmd.leftUnpackStop3      = in->padLeftStopCycle3;
+	cmd.rightUnpackPattern0  = in->padRightComponentSelectCycle0;
+	cmd.rightUnpackPattern1  = in->padRightComponentSelectCycle1;
+	cmd.rightUnpackPattern2  = in->padRightComponentSelectCycle2;
+	cmd.rightUnpackPattern3  = in->padRightComponentSelectCycle3;
+	cmd.rightUnpackStop0     = in->padRightStopCycle0;
+	cmd.rightUnpackStop1     = in->padRightStopCycle1;
+	cmd.rightUnpackStop2     = in->padRightStopCycle2;
+	cmd.rightUnpackStop3     = in->padRightStopCycle3;
+	cmd.topUnapckPattern     = in->padTopLineCount;
+	cmd.bottomUnapckPattern  = in->padBottomLineCount;
+
+	/*  program vfe_bus_cfg */
+	vfe_prog_hw(ctrl->vfebase + VFE_BUS_STRIPE_RD_ADDR_0,
+		(uint32_t *)&cmd, sizeof(cmd));
+
+	/* hacking code, put it to default value */
+	busPingpongRdIrqEnable = 0xf;
+
+	writel(busPingpongRdIrqEnable, ctrl->vfebase + VFE_BUS_PINGPONG_IRQ_EN);
+}
+
+void vfe_axi_output_config(struct vfe_cmd_axi_output_config *in)
+{
+	/* local variable  */
+	uint32_t *pcircle;
+	uint32_t *pdest;
+	uint32_t *psrc;
+	uint8_t  i;
+	uint8_t  fcnt;
+	uint16_t axioutpw = 8;
+
+	/* parameters check, condition and usage mode check */
+	ctrl->encPath.fragCount = in->output2.fragmentCount;
+	if (ctrl->encPath.fragCount > 1)
+		ctrl->encPath.multiFrag = TRUE;
+
+	ctrl->viewPath.fragCount = in->output1.fragmentCount;
+	if (ctrl->viewPath.fragCount > 1)
+		ctrl->viewPath.multiFrag = TRUE;
+
+	/* VFE_BUS_CFG.  raw data size */
+	ctrl->vfeBusConfigLocal.rawPixelDataSize = in->outputDataSize;
+
+	switch (in->outputDataSize) {
+	case VFE_RAW_PIXEL_DATA_SIZE_8BIT:
+		axioutpw = 8;
+		break;
+
+	case VFE_RAW_PIXEL_DATA_SIZE_10BIT:
+		axioutpw = 6;
+		break;
+
+	case VFE_RAW_PIXEL_DATA_SIZE_12BIT:
+		axioutpw = 5;
+		break;
+	}
+
+	ctrl->axiOutputMode = in->outputMode;
+
+	CDBG("axiOutputMode = %d\n", ctrl->axiOutputMode);
+
+	switch (ctrl->axiOutputMode) {
+	case VFE_AXI_OUTPUT_MODE_Output1: {
+		ctrl->vfeCamifConfigLocal.camif2BusEnable   = FALSE;
+		ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE;
+		ctrl->vfeBusConfigLocal.rawWritePathSelect  =
+			VFE_RAW_OUTPUT_DISABLED;
+
+		ctrl->encPath.pathEnabled                   = FALSE;
+		ctrl->vfeImaskLocal.encIrq                  = FALSE;
+		ctrl->vfeIrqCompositeMaskLocal.encIrqComMask =
+			VFE_COMP_IRQ_BOTH_Y_CBCR;
+
+		ctrl->vfeBusConfigLocal.encYWrPathEn          = FALSE;
+		ctrl->vfeBusConfigLocal.encCbcrWrPathEn       = FALSE;
+		ctrl->viewPath.pathEnabled                    = TRUE;
+		ctrl->vfeImaskLocal.viewIrq                   = TRUE;
+		ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask =
+			VFE_COMP_IRQ_BOTH_Y_CBCR;
+
+		ctrl->vfeBusConfigLocal.viewYWrPathEn    = TRUE;
+		ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = TRUE;
+
+		if (ctrl->vfeBusConfigLocal.encYWrPathEn &&
+				ctrl->encPath.multiFrag)
+			ctrl->vfeImaskLocal.encYPingpongIrq    = TRUE;
+
+		if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn &&
+				ctrl->encPath.multiFrag)
+			ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE;
+
+		if (ctrl->vfeBusConfigLocal.viewYWrPathEn &&
+				ctrl->viewPath.multiFrag)
+			ctrl->vfeImaskLocal.viewYPingpongIrq   = TRUE;
+
+		if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn &&
+				ctrl->viewPath.multiFrag)
+			ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE;
+	} /* VFE_AXI_OUTPUT_MODE_Output1 */
+		break;
+
+	case VFE_AXI_OUTPUT_MODE_Output2: {
+		ctrl->vfeCamifConfigLocal.camif2BusEnable   = FALSE;
+		ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE;
+		ctrl->vfeBusConfigLocal.rawWritePathSelect  =
+			VFE_RAW_OUTPUT_DISABLED;
+
+		ctrl->encPath.pathEnabled                   = TRUE;
+		ctrl->vfeImaskLocal.encIrq                  = TRUE;
+		ctrl->vfeIrqCompositeMaskLocal.encIrqComMask =
+			VFE_COMP_IRQ_BOTH_Y_CBCR;
+
+		ctrl->vfeBusConfigLocal.encYWrPathEn        = TRUE;
+		ctrl->vfeBusConfigLocal.encCbcrWrPathEn     = TRUE;
+
+		ctrl->viewPath.pathEnabled                   = FALSE;
+		ctrl->vfeImaskLocal.viewIrq                  = FALSE;
+		ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask =
+			VFE_COMP_IRQ_BOTH_Y_CBCR;
+
+		ctrl->vfeBusConfigLocal.viewYWrPathEn        = FALSE;
+		ctrl->vfeBusConfigLocal.viewCbcrWrPathEn     = FALSE;
+
+		if (ctrl->vfeBusConfigLocal.encYWrPathEn &&
+				ctrl->encPath.multiFrag)
+			ctrl->vfeImaskLocal.encYPingpongIrq    = TRUE;
+
+		if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn &&
+				ctrl->encPath.multiFrag)
+			ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE;
+
+		if (ctrl->vfeBusConfigLocal.viewYWrPathEn &&
+				ctrl->viewPath.multiFrag)
+			ctrl->vfeImaskLocal.viewYPingpongIrq   = TRUE;
+
+		if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn &&
+				ctrl->viewPath.multiFrag)
+			ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE;
+	} /* VFE_AXI_OUTPUT_MODE_Output2 */
+			break;
+
+	case VFE_AXI_OUTPUT_MODE_Output1AndOutput2: {
+		ctrl->vfeCamifConfigLocal.camif2BusEnable    = FALSE;
+		ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE;
+		ctrl->vfeBusConfigLocal.rawWritePathSelect   =
+			VFE_RAW_OUTPUT_DISABLED;
+
+		ctrl->encPath.pathEnabled                    = TRUE;
+		ctrl->vfeImaskLocal.encIrq                   = TRUE;
+		ctrl->vfeIrqCompositeMaskLocal.encIrqComMask =
+			VFE_COMP_IRQ_BOTH_Y_CBCR;
+
+		ctrl->vfeBusConfigLocal.encYWrPathEn         = TRUE;
+		ctrl->vfeBusConfigLocal.encCbcrWrPathEn      = TRUE;
+		ctrl->viewPath.pathEnabled                   = TRUE;
+		ctrl->vfeImaskLocal.viewIrq                  = TRUE;
+		ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask =
+			VFE_COMP_IRQ_BOTH_Y_CBCR;
+
+		ctrl->vfeBusConfigLocal.viewYWrPathEn        = TRUE;
+		ctrl->vfeBusConfigLocal.viewCbcrWrPathEn     = TRUE;
+
+		if (ctrl->vfeBusConfigLocal.encYWrPathEn &&
+				ctrl->encPath.multiFrag)
+			ctrl->vfeImaskLocal.encYPingpongIrq    = TRUE;
+
+		if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn &&
+				ctrl->encPath.multiFrag)
+			ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE;
+
+		if (ctrl->vfeBusConfigLocal.viewYWrPathEn &&
+				ctrl->viewPath.multiFrag)
+			ctrl->vfeImaskLocal.viewYPingpongIrq   = TRUE;
+
+		if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn &&
+				ctrl->viewPath.multiFrag)
+			ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE;
+	} /* VFE_AXI_OUTPUT_MODE_Output1AndOutput2 */
+		break;
+
+	case VFE_AXI_OUTPUT_MODE_CAMIFToAXIViaOutput2: {
+		/* For raw snapshot, we need both ping and pong buffer
+		 * initialized to the same address. Otherwise, if we
+		 * leave the pong buffer to NULL, there will be axi_error.
+		 * Note that ideally we should deal with this at upper layer,
+		 * which is in msm_vfe8x.c */
+		if (!in->output2.outputCbcr.outFragments[1][0]) {
+			in->output2.outputCbcr.outFragments[1][0] =
+				in->output2.outputCbcr.outFragments[0][0];
+		}
+
+		ctrl->vfeCamifConfigLocal.camif2BusEnable   = TRUE;
+		ctrl->vfeCamifConfigLocal.camif2OutputEnable = FALSE;
+		ctrl->vfeBusConfigLocal.rawWritePathSelect  =
+			VFE_RAW_OUTPUT_ENC_CBCR_PATH;
+
+		ctrl->encPath.pathEnabled                   = TRUE;
+		ctrl->vfeImaskLocal.encIrq                  = TRUE;
+		ctrl->vfeIrqCompositeMaskLocal.encIrqComMask =
+			VFE_COMP_IRQ_CBCR_ONLY;
+
+		ctrl->vfeBusConfigLocal.encYWrPathEn        = FALSE;
+		ctrl->vfeBusConfigLocal.encCbcrWrPathEn     = TRUE;
+
+		ctrl->viewPath.pathEnabled                   = FALSE;
+		ctrl->vfeImaskLocal.viewIrq                  = FALSE;
+		ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask =
+			VFE_COMP_IRQ_BOTH_Y_CBCR;
+
+		ctrl->vfeBusConfigLocal.viewYWrPathEn        = FALSE;
+		ctrl->vfeBusConfigLocal.viewCbcrWrPathEn     = FALSE;
+
+		if (ctrl->vfeBusConfigLocal.encYWrPathEn &&
+				ctrl->encPath.multiFrag)
+			ctrl->vfeImaskLocal.encYPingpongIrq    = TRUE;
+
+		if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn &&
+				ctrl->encPath.multiFrag)
+			ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE;
+
+		if (ctrl->vfeBusConfigLocal.viewYWrPathEn &&
+				ctrl->viewPath.multiFrag)
+			ctrl->vfeImaskLocal.viewYPingpongIrq   = TRUE;
+
+		if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn &&
+				ctrl->viewPath.multiFrag)
+			ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE;
+	} /* VFE_AXI_OUTPUT_MODE_CAMIFToAXIViaOutput2 */
+		break;
+
+	case VFE_AXI_OUTPUT_MODE_Output2AndCAMIFToAXIViaOutput1: {
+		ctrl->vfeCamifConfigLocal.camif2BusEnable   = TRUE;
+		ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE;
+		ctrl->vfeBusConfigLocal.rawWritePathSelect  =
+			VFE_RAW_OUTPUT_VIEW_CBCR_PATH;
+
+		ctrl->encPath.pathEnabled                   = TRUE;
+		ctrl->vfeImaskLocal.encIrq                  = TRUE;
+		ctrl->vfeIrqCompositeMaskLocal.encIrqComMask =
+			VFE_COMP_IRQ_BOTH_Y_CBCR;
+
+		ctrl->vfeBusConfigLocal.encYWrPathEn        = TRUE;
+		ctrl->vfeBusConfigLocal.encCbcrWrPathEn     = TRUE;
+
+		ctrl->viewPath.pathEnabled                   = TRUE;
+		ctrl->vfeImaskLocal.viewIrq                  = TRUE;
+		ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask =
+			VFE_COMP_IRQ_CBCR_ONLY;
+
+		ctrl->vfeBusConfigLocal.viewYWrPathEn        = FALSE;
+		ctrl->vfeBusConfigLocal.viewCbcrWrPathEn     = TRUE;
+
+		if (ctrl->vfeBusConfigLocal.encYWrPathEn &&
+				ctrl->encPath.multiFrag)
+			ctrl->vfeImaskLocal.encYPingpongIrq    = TRUE;
+
+		if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn &&
+				ctrl->encPath.multiFrag)
+			ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE;
+
+		if (ctrl->vfeBusConfigLocal.viewYWrPathEn &&
+				ctrl->viewPath.multiFrag)
+			ctrl->vfeImaskLocal.viewYPingpongIrq   = TRUE;
+
+		if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn &&
+				ctrl->viewPath.multiFrag)
+			ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE;
+	} /* VFE_AXI_OUTPUT_MODE_Output2AndCAMIFToAXIViaOutput1 */
+		break;
+
+	case VFE_AXI_OUTPUT_MODE_Output1AndCAMIFToAXIViaOutput2: {
+		ctrl->vfeCamifConfigLocal.camif2BusEnable   = TRUE;
+		ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE;
+		ctrl->vfeBusConfigLocal.rawWritePathSelect  =
+			VFE_RAW_OUTPUT_ENC_CBCR_PATH;
+
+		ctrl->encPath.pathEnabled                     = TRUE;
+		ctrl->vfeImaskLocal.encIrq                    = TRUE;
+		ctrl->vfeIrqCompositeMaskLocal.encIrqComMask  =
+			VFE_COMP_IRQ_CBCR_ONLY;
+
+		ctrl->vfeBusConfigLocal.encYWrPathEn          = FALSE;
+		ctrl->vfeBusConfigLocal.encCbcrWrPathEn       = TRUE;
+
+		ctrl->viewPath.pathEnabled                    = TRUE;
+		ctrl->vfeImaskLocal.viewIrq                   = TRUE;
+
+		ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask =
+			VFE_COMP_IRQ_BOTH_Y_CBCR;
+
+		ctrl->vfeBusConfigLocal.viewYWrPathEn         = TRUE;
+		ctrl->vfeBusConfigLocal.viewCbcrWrPathEn      = TRUE;
+
+		if (ctrl->vfeBusConfigLocal.encYWrPathEn &&
+				ctrl->encPath.multiFrag)
+			ctrl->vfeImaskLocal.encYPingpongIrq       = TRUE;
+
+		if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn &&
+				ctrl->encPath.multiFrag)
+			ctrl->vfeImaskLocal.encCbcrPingpongIrq    = TRUE;
+
+		if (ctrl->vfeBusConfigLocal.viewYWrPathEn &&
+				ctrl->viewPath.multiFrag)
+			ctrl->vfeImaskLocal.viewYPingpongIrq      = TRUE;
+
+		if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn &&
+				ctrl->viewPath.multiFrag)
+			ctrl->vfeImaskLocal.viewCbcrPingpongIrq   = TRUE;
+	} /* VFE_AXI_OUTPUT_MODE_Output1AndCAMIFToAXIViaOutput2 */
+		break;
+
+	case VFE_AXI_LAST_OUTPUT_MODE_ENUM:
+		break;
+	} /* switch */
+
+	/* Save the addresses for each path. */
+	/* output2 path */
+	fcnt = ctrl->encPath.fragCount;
+
+	pcircle = ctrl->encPath.yPath.addressBuffer;
+	pdest = ctrl->encPath.nextFrameAddrBuf;
+
+	psrc = &(in->output2.outputY.outFragments[0][0]);
+	for (i = 0; i < fcnt; i++)
+		*pcircle++ = *psrc++;
+
+	psrc = &(in->output2.outputY.outFragments[1][0]);
+	for (i = 0; i < fcnt; i++)
+		*pcircle++ = *psrc++;
+
+	psrc = &(in->output2.outputY.outFragments[2][0]);
+	for (i = 0; i < fcnt; i++)
+		*pdest++ = *psrc++;
+
+	pcircle = ctrl->encPath.cbcrPath.addressBuffer;
+
+	psrc = &(in->output2.outputCbcr.outFragments[0][0]);
+	for (i = 0; i < fcnt; i++)
+		*pcircle++ = *psrc++;
+
+	psrc = &(in->output2.outputCbcr.outFragments[1][0]);
+	for (i = 0; i < fcnt; i++)
+		*pcircle++ = *psrc++;
+
+	psrc = &(in->output2.outputCbcr.outFragments[2][0]);
+	for (i = 0; i < fcnt; i++)
+		*pdest++ = *psrc++;
+
+	vfe_set_bus_pipo_addr(&ctrl->viewPath, &ctrl->encPath);
+
+	ctrl->encPath.ackPending = FALSE;
+	ctrl->encPath.currentFrame = ping;
+	ctrl->encPath.whichOutputPath = 1;
+	ctrl->encPath.yPath.fragIndex = 2;
+	ctrl->encPath.cbcrPath.fragIndex = 2;
+	ctrl->encPath.yPath.hwCurrentFlag = ping;
+	ctrl->encPath.cbcrPath.hwCurrentFlag = ping;
+
+	/* output1 path */
+	pcircle = ctrl->viewPath.yPath.addressBuffer;
+	pdest = ctrl->viewPath.nextFrameAddrBuf;
+	fcnt = ctrl->viewPath.fragCount;
+
+	psrc = &(in->output1.outputY.outFragments[0][0]);
+	for (i = 0; i < fcnt; i++)
+		*pcircle++ = *psrc++;
+
+	psrc = &(in->output1.outputY.outFragments[1][0]);
+	for (i = 0; i < fcnt; i++)
+		*pcircle++ = *psrc++;
+
+	psrc = &(in->output1.outputY.outFragments[2][0]);
+	for (i = 0; i < fcnt; i++)
+		*pdest++ = *psrc++;
+
+	pcircle = ctrl->viewPath.cbcrPath.addressBuffer;
+
+	psrc = &(in->output1.outputCbcr.outFragments[0][0]);
+	for (i = 0; i < fcnt; i++)
+		*pcircle++ = *psrc++;
+
+	psrc = &(in->output1.outputCbcr.outFragments[1][0]);
+	for (i = 0; i < fcnt; i++)
+		*pcircle++ = *psrc++;
+
+	psrc = &(in->output1.outputCbcr.outFragments[2][0]);
+	for (i = 0; i < fcnt; i++)
+		*pdest++ = *psrc++;
+
+	ctrl->viewPath.ackPending = FALSE;
+	ctrl->viewPath.currentFrame = ping;
+	ctrl->viewPath.whichOutputPath = 0;
+	ctrl->viewPath.yPath.fragIndex = 2;
+	ctrl->viewPath.cbcrPath.fragIndex = 2;
+	ctrl->viewPath.yPath.hwCurrentFlag = ping;
+	ctrl->viewPath.cbcrPath.hwCurrentFlag = ping;
+
+	/* call to program the registers. */
+	vfe_axi_output(in, &ctrl->viewPath, &ctrl->encPath, axioutpw);
+}
+
+void vfe_camif_config(struct vfe_cmd_camif_config *in)
+{
+	struct vfe_camifcfg cmd;
+	memset(&cmd, 0, sizeof(cmd));
+
+	CDBG("camif.frame pixelsPerLine = %d\n", in->frame.pixelsPerLine);
+	CDBG("camif.frame linesPerFrame = %d\n", in->frame.linesPerFrame);
+	CDBG("camif.window firstpixel = %d\n", in->window.firstpixel);
+	CDBG("camif.window lastpixel = %d\n",  in->window.lastpixel);
+	CDBG("camif.window firstline = %d\n",  in->window.firstline);
+	CDBG("camif.window lastline = %d\n",   in->window.lastline);
+
+	/* determine if epoch interrupt needs to be enabled.  */
+	if ((in->epoch1.enable == TRUE) &&
+	    (in->epoch1.lineindex <= in->frame.linesPerFrame))
+		ctrl->vfeImaskLocal.camifEpoch1Irq = 1;
+
+	if ((in->epoch2.enable == TRUE) &&
+	    (in->epoch2.lineindex <= in->frame.linesPerFrame)) {
+		ctrl->vfeImaskLocal.camifEpoch2Irq = 1;
+	}
+
+	/*  save the content to program CAMIF_CONFIG seperately. */
+	ctrl->vfeCamifConfigLocal.camifCfgFromCmd = in->camifConfig;
+
+	/* EFS_Config */
+	cmd.efsEndOfLine     = in->EFS.efsendofline;
+	cmd.efsStartOfLine   = in->EFS.efsstartofline;
+	cmd.efsEndOfFrame    = in->EFS.efsendofframe;
+	cmd.efsStartOfFrame  = in->EFS.efsstartofframe;
+
+	/* Frame Config */
+	cmd.frameConfigPixelsPerLine = in->frame.pixelsPerLine;
+	cmd.frameConfigLinesPerFrame = in->frame.linesPerFrame;
+
+	/* Window Width Config */
+	cmd.windowWidthCfgLastPixel  = in->window.lastpixel;
+	cmd.windowWidthCfgFirstPixel = in->window.firstpixel;
+
+	/* Window Height Config */
+	cmd.windowHeightCfglastLine   = in->window.lastline;
+	cmd.windowHeightCfgfirstLine  = in->window.firstline;
+
+	/* Subsample 1 Config */
+	cmd.subsample1CfgPixelSkip = in->subsample.pixelskipmask;
+	cmd.subsample1CfgLineSkip  = in->subsample.lineskipmask;
+
+	/* Subsample 2 Config */
+	cmd.subsample2CfgFrameSkip      = in->subsample.frameskip;
+	cmd.subsample2CfgFrameSkipMode  = in->subsample.frameskipmode;
+	cmd.subsample2CfgPixelSkipWrap  = in->subsample.pixelskipwrap;
+
+	/* Epoch Interrupt */
+	cmd.epoch1Line = in->epoch1.lineindex;
+	cmd.epoch2Line = in->epoch2.lineindex;
+
+	vfe_prog_hw(ctrl->vfebase + CAMIF_EFS_CONFIG,
+		(uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_fov_crop_config(struct vfe_cmd_fov_crop_config *in)
+{
+	struct vfe_fov_crop_cfg cmd;
+	memset(&cmd, 0, sizeof(cmd));
+
+	ctrl->vfeModuleEnableLocal.cropEnable = in->enable;
+
+	/* FOV Corp, Part 1 */
+	cmd.lastPixel  = in->lastPixel;
+	cmd.firstPixel = in->firstPixel;
+
+	/* FOV Corp, Part 2 */
+	cmd.lastLine   = in->lastLine;
+	cmd.firstLine  = in->firstLine;
+
+	vfe_prog_hw(ctrl->vfebase + VFE_CROP_WIDTH_CFG,
+		(uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_get_hw_version(struct vfe_cmd_hw_version *out)
+{
+	uint32_t vfeHwVersionPacked;
+	struct vfe_hw_ver ver;
+
+	vfeHwVersionPacked = readl(ctrl->vfebase + VFE_HW_VERSION);
+
+	ver = *((struct vfe_hw_ver *)&vfeHwVersionPacked);
+
+	out->coreVersion  = ver.coreVersion;
+	out->minorVersion = ver.minorVersion;
+	out->majorVersion = ver.majorVersion;
+}
+
+static void vfe_reset_internal_variables(void)
+{
+	/* local variables to program the hardware. */
+	ctrl->vfeImaskPacked = 0;
+	ctrl->vfeImaskCompositePacked = 0;
+
+	/* FALSE = disable,  1 = enable. */
+	memset(&ctrl->vfeModuleEnableLocal, 0,
+		sizeof(ctrl->vfeModuleEnableLocal));
+
+	/* 0 = disable, 1 = enable */
+	memset(&ctrl->vfeCamifConfigLocal, 0,
+		sizeof(ctrl->vfeCamifConfigLocal));
+	/* 0 = disable, 1 = enable */
+	memset(&ctrl->vfeImaskLocal, 0, sizeof(ctrl->vfeImaskLocal));
+	memset(&ctrl->vfeStatsCmdLocal, 0, sizeof(ctrl->vfeStatsCmdLocal));
+	memset(&ctrl->vfeBusConfigLocal, 0, sizeof(ctrl->vfeBusConfigLocal));
+	memset(&ctrl->vfeBusPmConfigLocal, 0,
+		sizeof(ctrl->vfeBusPmConfigLocal));
+	memset(&ctrl->vfeBusCmdLocal, 0, sizeof(ctrl->vfeBusCmdLocal));
+	memset(&ctrl->vfeInterruptNameLocal, 0,
+		sizeof(ctrl->vfeInterruptNameLocal));
+	memset(&ctrl->vfeDroppedFrameCounts, 0,
+		sizeof(ctrl->vfeDroppedFrameCounts));
+	memset(&ctrl->vfeIrqThreadMsgLocal, 0,
+		sizeof(ctrl->vfeIrqThreadMsgLocal));
+
+	/* state control variables */
+	ctrl->vfeStartAckPendingFlag = FALSE;
+	ctrl->vfeStopAckPending = FALSE;
+	ctrl->vfeIrqCompositeMaskLocal.ceDoneSel = 0;
+	ctrl->vfeIrqCompositeMaskLocal.encIrqComMask = VFE_COMP_IRQ_BOTH_Y_CBCR;
+	ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask =
+		VFE_COMP_IRQ_BOTH_Y_CBCR;
+
+	ctrl->vstate = VFE_STATE_IDLE;
+
+	ctrl->axiOutputMode = VFE_AXI_LAST_OUTPUT_MODE_ENUM;
+	/* 0 for continuous mode, 1 for snapshot mode */
+	ctrl->vfeOperationMode = VFE_START_OPERATION_MODE_CONTINUOUS;
+	ctrl->vfeSnapShotCount = 0;
+	ctrl->vfeStatsPingPongReloadFlag = FALSE;
+	/* this is unsigned 32 bit integer. */
+	ctrl->vfeFrameId = 0;
+	ctrl->vfeFrameSkip.output1Pattern = 0xffffffff;
+	ctrl->vfeFrameSkip.output1Period  = 31;
+	ctrl->vfeFrameSkip.output2Pattern = 0xffffffff;
+	ctrl->vfeFrameSkip.output2Period  = 31;
+	ctrl->vfeFrameSkipPattern = 0xffffffff;
+	ctrl->vfeFrameSkipCount   = 0;
+	ctrl->vfeFrameSkipPeriod  = 31;
+
+	memset((void *)&ctrl->encPath, 0, sizeof(ctrl->encPath));
+	memset((void *)&ctrl->viewPath, 0, sizeof(ctrl->viewPath));
+
+	ctrl->encPath.whichOutputPath  = 1;
+	ctrl->encPath.cbcrStatusBit    = 5;
+	ctrl->viewPath.whichOutputPath = 0;
+	ctrl->viewPath.cbcrStatusBit   = 7;
+
+	ctrl->vfeTestGenStartFlag = FALSE;
+
+	/* default to bank 0. */
+	ctrl->vfeLaBankSel = 0;
+
+	/* default to bank 0 for all channels. */
+	memset(&ctrl->vfeGammaLutSel, 0, sizeof(ctrl->vfeGammaLutSel));
+
+	/* Stats control variables. */
+	memset(&ctrl->afStatsControl, 0, sizeof(ctrl->afStatsControl));
+	memset(&ctrl->awbStatsControl, 0, sizeof(ctrl->awbStatsControl));
+	vfe_set_stats_pingpong_address(&ctrl->afStatsControl,
+		&ctrl->awbStatsControl);
+}
+
+void vfe_reset(void)
+{
+	spin_lock_init(&msm_vfe_ctrl_lock);
+	vfe_reset_internal_variables();
+
+	atomic_set(&ctrl->vfe_serv_interrupt, 1);
+	ctrl->vfeImaskLocal.resetAckIrq = TRUE;
+	ctrl->vfeImaskPacked = vfe_irq_pack(ctrl->vfeImaskLocal);
+
+	/* disable all interrupts. */
+	writel(VFE_DISABLE_ALL_IRQS, ctrl->vfebase + VFE_IRQ_COMPOSITE_MASK);
+
+	/* clear all pending interrupts*/
+	writel(VFE_CLEAR_ALL_IRQS, ctrl->vfebase + VFE_IRQ_CLEAR);
+
+	/* enable reset_ack interrupt.  */
+	writel(ctrl->vfeImaskPacked, ctrl->vfebase + VFE_IRQ_MASK);
+
+	writel(VFE_RESET_UPON_RESET_CMD, ctrl->vfebase + VFE_GLOBAL_RESET_CMD);
+}
diff --git a/drivers/media/video/msm/msm_vfe8x_proc.h b/drivers/media/video/msm/msm_vfe8x_proc.h
new file mode 100644
index 0000000..da00e8f
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe8x_proc.h
@@ -0,0 +1,1563 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __MSM_VFE8X_REG_H__
+#define __MSM_VFE8X_REG_H__
+
+#include <mach/msm_iomap.h>
+#include <mach/camera.h>
+#include "msm_vfe8x.h"
+
+
+#define MSM_AXI_QOS_PREVIEW		128000
+#define MSM_AXI_QOS_SNAPSHOT	128000
+#define MSM_AXI_QOS_RECORDING	128000
+
+
+/* at start of camif,  bit 1:0 = 0x01:enable
+ * image data capture at frame boundary. */
+#define CAMIF_COMMAND_START  0x00000005
+
+/* bit 2= 0x1:clear the CAMIF_STATUS register
+ * value. */
+#define CAMIF_COMMAND_CLEAR  0x00000004
+
+/* at stop of vfe pipeline, for now it is assumed
+ * that camif will stop at any time. Bit 1:0 = 0x10:
+ * disable image data capture immediately. */
+#define CAMIF_COMMAND_STOP_IMMEDIATELY  0x00000002
+
+/* at stop of vfe pipeline, for now it is assumed
+ * that camif will stop at any time. Bit 1:0 = 0x00:
+ * disable image data capture at frame boundary */
+#define CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY  0x00000000
+
+/* to halt axi bridge */
+#define AXI_HALT  0x00000001
+
+/* clear the halt bit. */
+#define AXI_HALT_CLEAR  0x00000000
+
+/* reset the pipeline when stop command is issued.
+ * (without reset the register.) bit 26-31 = 0,
+ * domain reset, bit 0-9 = 1 for module reset, except
+ * register module. */
+#define VFE_RESET_UPON_STOP_CMD  0x000003ef
+
+/* reset the pipeline when reset command.
+ * bit 26-31 = 0, domain reset, bit 0-9 = 1 for module reset. */
+#define VFE_RESET_UPON_RESET_CMD  0x000003ff
+
+/* bit 5 is for axi status idle or busy.
+ * 1 =  halted,  0 = busy */
+#define AXI_STATUS_BUSY_MASK 0x00000020
+
+/* bit 0 & bit 1 = 1, both y and cbcr irqs need to be present
+ * for frame done interrupt */
+#define VFE_COMP_IRQ_BOTH_Y_CBCR 3
+
+/* bit 1 = 1, only cbcr irq triggers frame done interrupt */
+#define VFE_COMP_IRQ_CBCR_ONLY 2
+
+/* bit 0 = 1, only y irq triggers frame done interrupt */
+#define VFE_COMP_IRQ_Y_ONLY 1
+
+/* bit 0 = 1, PM go;   bit1 = 1, PM stop */
+#define VFE_PERFORMANCE_MONITOR_GO   0x00000001
+#define VFE_PERFORMANCE_MONITOR_STOP 0x00000002
+
+/* bit 0 = 1, test gen go;   bit1 = 1, test gen stop */
+#define VFE_TEST_GEN_GO   0x00000001
+#define VFE_TEST_GEN_STOP 0x00000002
+
+/* the chroma is assumed to be interpolated between
+ * the luma samples.  JPEG 4:2:2 */
+#define VFE_CHROMA_UPSAMPLE_INTERPOLATED 0
+
+/* constants for irq registers */
+#define VFE_DISABLE_ALL_IRQS 0
+/* bit =1 is to clear the corresponding bit in VFE_IRQ_STATUS.  */
+#define VFE_CLEAR_ALL_IRQS   0xffffffff
+/* imask for while waiting for stop ack,  driver has already
+ * requested stop, waiting for reset irq,
+ * bit 29,28,27,26 for async timer, bit 9 for reset */
+#define VFE_IMASK_WHILE_STOPPING  0x3c000200
+
+/* when normal case, don't want to block error status.
+ * bit 0,6,20,21,22,30,31 */
+#define VFE_IMASK_ERROR_ONLY             0xC0700041
+#define VFE_REG_UPDATE_TRIGGER           1
+#define VFE_PM_BUF_MAX_CNT_MASK          0xFF
+#define VFE_DMI_CFG_DEFAULT              0x00000100
+#define LENS_ROLL_OFF_DELTA_TABLE_OFFSET 32
+#define VFE_AF_PINGPONG_STATUS_BIT       0x100
+#define VFE_AWB_PINGPONG_STATUS_BIT      0x200
+
+/* VFE I/O registers */
+enum {
+	VFE_HW_VERSION                    = 0x00000000,
+	VFE_GLOBAL_RESET_CMD              = 0x00000004,
+	VFE_MODULE_RESET                  = 0x00000008,
+	VFE_CGC_OVERRIDE                  = 0x0000000C,
+	VFE_MODULE_CFG                    = 0x00000010,
+	VFE_CFG                           = 0x00000014,
+	VFE_IRQ_MASK                      = 0x00000018,
+	VFE_IRQ_CLEAR                     = 0x0000001C,
+VFE_IRQ_STATUS                    = 0x00000020,
+VFE_IRQ_COMPOSITE_MASK            = 0x00000024,
+VFE_BUS_CMD                       = 0x00000028,
+VFE_BUS_CFG                       = 0x0000002C,
+VFE_BUS_ENC_Y_WR_PING_ADDR        = 0x00000030,
+VFE_BUS_ENC_Y_WR_PONG_ADDR        = 0x00000034,
+VFE_BUS_ENC_Y_WR_IMAGE_SIZE       = 0x00000038,
+VFE_BUS_ENC_Y_WR_BUFFER_CFG       = 0x0000003C,
+VFE_BUS_ENC_CBCR_WR_PING_ADDR     = 0x00000040,
+VFE_BUS_ENC_CBCR_WR_PONG_ADDR     = 0x00000044,
+VFE_BUS_ENC_CBCR_WR_IMAGE_SIZE    = 0x00000048,
+VFE_BUS_ENC_CBCR_WR_BUFFER_CFG    = 0x0000004C,
+VFE_BUS_VIEW_Y_WR_PING_ADDR       = 0x00000050,
+VFE_BUS_VIEW_Y_WR_PONG_ADDR       = 0x00000054,
+VFE_BUS_VIEW_Y_WR_IMAGE_SIZE      = 0x00000058,
+VFE_BUS_VIEW_Y_WR_BUFFER_CFG      = 0x0000005C,
+VFE_BUS_VIEW_CBCR_WR_PING_ADDR    = 0x00000060,
+VFE_BUS_VIEW_CBCR_WR_PONG_ADDR    = 0x00000064,
+VFE_BUS_VIEW_CBCR_WR_IMAGE_SIZE   = 0x00000068,
+VFE_BUS_VIEW_CBCR_WR_BUFFER_CFG   = 0x0000006C,
+VFE_BUS_STATS_AF_WR_PING_ADDR     = 0x00000070,
+VFE_BUS_STATS_AF_WR_PONG_ADDR     = 0x00000074,
+VFE_BUS_STATS_AWB_WR_PING_ADDR    = 0x00000078,
+VFE_BUS_STATS_AWB_WR_PONG_ADDR    = 0x0000007C,
+VFE_BUS_STATS_HIST_WR_PING_ADDR   = 0x00000080,
+VFE_BUS_STATS_HIST_WR_PONG_ADDR   = 0x00000084,
+VFE_BUS_STATS_WR_PRIORITY         = 0x00000088,
+VFE_BUS_STRIPE_RD_ADDR_0          = 0x0000008C,
+VFE_BUS_STRIPE_RD_ADDR_1          = 0x00000090,
+VFE_BUS_STRIPE_RD_ADDR_2          = 0x00000094,
+VFE_BUS_STRIPE_RD_ADDR_3          = 0x00000098,
+VFE_BUS_STRIPE_RD_VSIZE           = 0x0000009C,
+VFE_BUS_STRIPE_RD_HSIZE           = 0x000000A0,
+VFE_BUS_STRIPE_RD_BUFFER_CFG      = 0x000000A4,
+VFE_BUS_STRIPE_RD_UNPACK_CFG      = 0x000000A8,
+VFE_BUS_STRIPE_RD_UNPACK          = 0x000000AC,
+VFE_BUS_STRIPE_RD_PAD_SIZE        = 0x000000B0,
+VFE_BUS_STRIPE_RD_PAD_L_UNPACK    = 0x000000B4,
+VFE_BUS_STRIPE_RD_PAD_R_UNPACK    = 0x000000B8,
+VFE_BUS_STRIPE_RD_PAD_TB_UNPACK   = 0x000000BC,
+VFE_BUS_PINGPONG_IRQ_EN           = 0x000000C0,
+VFE_BUS_PINGPONG_STATUS           = 0x000000C4,
+VFE_BUS_PM_CMD                    = 0x000000C8,
+VFE_BUS_PM_CFG                    = 0x000000CC,
+VFE_BUS_ENC_Y_WR_PM_STATS_0       = 0x000000D0,
+VFE_BUS_ENC_Y_WR_PM_STATS_1       = 0x000000D4,
+VFE_BUS_ENC_CBCR_WR_PM_STATS_0    = 0x000000D8,
+VFE_BUS_ENC_CBCR_WR_PM_STATS_1    = 0x000000DC,
+VFE_BUS_VIEW_Y_WR_PM_STATS_0      = 0x000000E0,
+VFE_BUS_VIEW_Y_WR_PM_STATS_1      = 0x000000E4,
+VFE_BUS_VIEW_CBCR_WR_PM_STATS_0   = 0x000000E8,
+VFE_BUS_VIEW_CBCR_WR_PM_STATS_1   = 0x000000EC,
+VFE_BUS_MISR_CFG                  = 0x000000F4,
+VFE_BUS_MISR_MAST_CFG_0           = 0x000000F8,
+VFE_BUS_MISR_MAST_CFG_1           = 0x000000FC,
+VFE_BUS_MISR_RD_VAL               = 0x00000100,
+VFE_AXI_CMD                       = 0x00000104,
+VFE_AXI_CFG                       = 0x00000108,
+VFE_AXI_STATUS                    = 0x0000010C,
+CAMIF_COMMAND                     = 0x00000110,
+CAMIF_CONFIG                      = 0x00000114,
+CAMIF_EFS_CONFIG                  = 0x00000118,
+CAMIF_FRAME_CONFIG                = 0x0000011C,
+CAMIF_WINDOW_WIDTH_CONFIG         = 0x00000120,
+CAMIF_WINDOW_HEIGHT_CONFIG        = 0x00000124,
+CAMIF_SUBSAMPLE1_CONFIG           = 0x00000128,
+CAMIF_SUBSAMPLE2_CONFIG           = 0x0000012C,
+CAMIF_EPOCH_IRQ                   = 0x00000130,
+CAMIF_STATUS                      = 0x00000134,
+CAMIF_MISR                        = 0x00000138,
+VFE_SYNC_TIMER_CMD                = 0x0000013C,
+VFE_SYNC_TIMER0_LINE_START        = 0x00000140,
+VFE_SYNC_TIMER0_PIXEL_START       = 0x00000144,
+VFE_SYNC_TIMER0_PIXEL_DURATION    = 0x00000148,
+VFE_SYNC_TIMER1_LINE_START        = 0x0000014C,
+VFE_SYNC_TIMER1_PIXEL_START       = 0x00000150,
+VFE_SYNC_TIMER1_PIXEL_DURATION    = 0x00000154,
+VFE_SYNC_TIMER2_LINE_START        = 0x00000158,
+VFE_SYNC_TIMER2_PIXEL_START       = 0x0000015C,
+VFE_SYNC_TIMER2_PIXEL_DURATION    = 0x00000160,
+VFE_SYNC_TIMER_POLARITY           = 0x00000164,
+VFE_ASYNC_TIMER_CMD               = 0x00000168,
+VFE_ASYNC_TIMER0_CFG_0            = 0x0000016C,
+VFE_ASYNC_TIMER0_CFG_1            = 0x00000170,
+VFE_ASYNC_TIMER1_CFG_0            = 0x00000174,
+VFE_ASYNC_TIMER1_CFG_1            = 0x00000178,
+VFE_ASYNC_TIMER2_CFG_0            = 0x0000017C,
+VFE_ASYNC_TIMER2_CFG_1            = 0x00000180,
+VFE_ASYNC_TIMER3_CFG_0            = 0x00000184,
+VFE_ASYNC_TIMER3_CFG_1            = 0x00000188,
+VFE_TIMER_SEL                     = 0x0000018C,
+VFE_REG_UPDATE_CMD                = 0x00000190,
+VFE_BLACK_EVEN_EVEN_VALUE         = 0x00000194,
+VFE_BLACK_EVEN_ODD_VALUE          = 0x00000198,
+VFE_BLACK_ODD_EVEN_VALUE          = 0x0000019C,
+VFE_BLACK_ODD_ODD_VALUE           = 0x000001A0,
+VFE_ROLLOFF_CFG_0                 = 0x000001A4,
+VFE_ROLLOFF_CFG_1                 = 0x000001A8,
+VFE_ROLLOFF_CFG_2                 = 0x000001AC,
+VFE_DEMUX_CFG                     = 0x000001B0,
+VFE_DEMUX_GAIN_0                  = 0x000001B4,
+VFE_DEMUX_GAIN_1                  = 0x000001B8,
+VFE_DEMUX_EVEN_CFG                = 0x000001BC,
+VFE_DEMUX_ODD_CFG                 = 0x000001C0,
+VFE_DEMOSAIC_CFG                  = 0x000001C4,
+VFE_DEMOSAIC_ABF_CFG_0            = 0x000001C8,
+VFE_DEMOSAIC_ABF_CFG_1            = 0x000001CC,
+VFE_DEMOSAIC_BPC_CFG_0            = 0x000001D0,
+VFE_DEMOSAIC_BPC_CFG_1            = 0x000001D4,
+VFE_DEMOSAIC_STATUS               = 0x000001D8,
+VFE_CHROMA_UPSAMPLE_CFG           = 0x000001DC,
+VFE_CROP_WIDTH_CFG                = 0x000001E0,
+VFE_CROP_HEIGHT_CFG               = 0x000001E4,
+VFE_COLOR_CORRECT_COEFF_0         = 0x000001E8,
+VFE_COLOR_CORRECT_COEFF_1         = 0x000001EC,
+VFE_COLOR_CORRECT_COEFF_2         = 0x000001F0,
+VFE_COLOR_CORRECT_COEFF_3         = 0x000001F4,
+VFE_COLOR_CORRECT_COEFF_4         = 0x000001F8,
+VFE_COLOR_CORRECT_COEFF_5         = 0x000001FC,
+VFE_COLOR_CORRECT_COEFF_6         = 0x00000200,
+VFE_COLOR_CORRECT_COEFF_7         = 0x00000204,
+VFE_COLOR_CORRECT_COEFF_8         = 0x00000208,
+VFE_COLOR_CORRECT_OFFSET_0        = 0x0000020C,
+VFE_COLOR_CORRECT_OFFSET_1        = 0x00000210,
+VFE_COLOR_CORRECT_OFFSET_2        = 0x00000214,
+VFE_COLOR_CORRECT_COEFF_Q         = 0x00000218,
+VFE_LA_CFG                        = 0x0000021C,
+VFE_LUT_BANK_SEL                  = 0x00000220,
+VFE_CHROMA_ENHAN_A                = 0x00000224,
+VFE_CHROMA_ENHAN_B                = 0x00000228,
+VFE_CHROMA_ENHAN_C                = 0x0000022C,
+VFE_CHROMA_ENHAN_D                = 0x00000230,
+VFE_CHROMA_ENHAN_K                = 0x00000234,
+VFE_COLOR_CONVERT_COEFF_0         = 0x00000238,
+VFE_COLOR_CONVERT_COEFF_1         = 0x0000023C,
+VFE_COLOR_CONVERT_COEFF_2         = 0x00000240,
+VFE_COLOR_CONVERT_OFFSET          = 0x00000244,
+VFE_ASF_CFG                       = 0x00000248,
+VFE_ASF_SHARP_CFG_0               = 0x0000024C,
+VFE_ASF_SHARP_CFG_1               = 0x00000250,
+VFE_ASF_SHARP_COEFF_0             = 0x00000254,
+VFE_ASF_SHARP_COEFF_1             = 0x00000258,
+VFE_ASF_SHARP_COEFF_2             = 0x0000025C,
+VFE_ASF_SHARP_COEFF_3             = 0x00000260,
+VFE_ASF_MAX_EDGE                  = 0x00000264,
+VFE_ASF_CROP_WIDTH_CFG            = 0x00000268,
+VFE_ASF_CROP_HEIGHT_CFG           = 0x0000026C,
+VFE_SCALE_CFG                     = 0x00000270,
+VFE_SCALE_H_IMAGE_SIZE_CFG        = 0x00000274,
+VFE_SCALE_H_PHASE_CFG             = 0x00000278,
+VFE_SCALE_H_STRIPE_CFG            = 0x0000027C,
+VFE_SCALE_V_IMAGE_SIZE_CFG        = 0x00000280,
+VFE_SCALE_V_PHASE_CFG             = 0x00000284,
+VFE_SCALE_V_STRIPE_CFG            = 0x00000288,
+VFE_SCALE_Y_CFG                   = 0x0000028C,
+VFE_SCALE_Y_H_IMAGE_SIZE_CFG      = 0x00000290,
+VFE_SCALE_Y_H_PHASE_CFG           = 0x00000294,
+VFE_SCALE_Y_V_IMAGE_SIZE_CFG      = 0x00000298,
+VFE_SCALE_Y_V_PHASE_CFG           = 0x0000029C,
+VFE_SCALE_CBCR_CFG                = 0x000002A0,
+VFE_SCALE_CBCR_H_IMAGE_SIZE_CFG   = 0x000002A4,
+VFE_SCALE_CBCR_H_PHASE_CFG        = 0x000002A8,
+VFE_SCALE_CBCR_V_IMAGE_SIZE_CFG   = 0x000002AC,
+VFE_SCALE_CBCR_V_PHASE_CFG        = 0x000002B0,
+VFE_WB_CFG                        = 0x000002B4,
+VFE_CHROMA_SUPPRESS_CFG_0         = 0x000002B8,
+VFE_CHROMA_SUPPRESS_CFG_1         = 0x000002BC,
+VFE_CHROMA_SUBSAMPLE_CFG          = 0x000002C0,
+VFE_CHROMA_SUB_CROP_WIDTH_CFG     = 0x000002C4,
+VFE_CHROMA_SUB_CROP_HEIGHT_CFG    = 0x000002C8,
+VFE_FRAMEDROP_ENC_Y_CFG           = 0x000002CC,
+VFE_FRAMEDROP_ENC_CBCR_CFG        = 0x000002D0,
+VFE_FRAMEDROP_ENC_Y_PATTERN       = 0x000002D4,
+VFE_FRAMEDROP_ENC_CBCR_PATTERN    = 0x000002D8,
+VFE_FRAMEDROP_VIEW_Y_CFG          = 0x000002DC,
+VFE_FRAMEDROP_VIEW_CBCR_CFG       = 0x000002E0,
+VFE_FRAMEDROP_VIEW_Y_PATTERN      = 0x000002E4,
+VFE_FRAMEDROP_VIEW_CBCR_PATTERN   = 0x000002E8,
+VFE_CLAMP_MAX_CFG                 = 0x000002EC,
+VFE_CLAMP_MIN_CFG                 = 0x000002F0,
+VFE_STATS_CMD                     = 0x000002F4,
+VFE_STATS_AF_CFG                  = 0x000002F8,
+VFE_STATS_AF_DIM                  = 0x000002FC,
+VFE_STATS_AF_GRID_0               = 0x00000300,
+VFE_STATS_AF_GRID_1               = 0x00000304,
+VFE_STATS_AF_GRID_2               = 0x00000308,
+VFE_STATS_AF_GRID_3               = 0x0000030C,
+VFE_STATS_AF_HEADER               = 0x00000310,
+VFE_STATS_AF_COEF0                = 0x00000314,
+VFE_STATS_AF_COEF1                = 0x00000318,
+VFE_STATS_AWBAE_CFG               = 0x0000031C,
+VFE_STATS_AXW_HEADER              = 0x00000320,
+VFE_STATS_AWB_MCFG                = 0x00000324,
+VFE_STATS_AWB_CCFG1               = 0x00000328,
+VFE_STATS_AWB_CCFG2               = 0x0000032C,
+VFE_STATS_HIST_HEADER             = 0x00000330,
+VFE_STATS_HIST_INNER_OFFSET       = 0x00000334,
+VFE_STATS_HIST_INNER_DIM          = 0x00000338,
+VFE_STATS_FRAME_SIZE              = 0x0000033C,
+VFE_DMI_CFG                       = 0x00000340,
+VFE_DMI_ADDR                      = 0x00000344,
+VFE_DMI_DATA_HI                   = 0x00000348,
+VFE_DMI_DATA_LO                   = 0x0000034C,
+VFE_DMI_RAM_AUTO_LOAD_CMD         = 0x00000350,
+VFE_DMI_RAM_AUTO_LOAD_STATUS      = 0x00000354,
+VFE_DMI_RAM_AUTO_LOAD_CFG         = 0x00000358,
+VFE_DMI_RAM_AUTO_LOAD_SEED        = 0x0000035C,
+VFE_TESTBUS_SEL                   = 0x00000360,
+VFE_TESTGEN_CFG                   = 0x00000364,
+VFE_SW_TESTGEN_CMD                = 0x00000368,
+VFE_HW_TESTGEN_CMD                = 0x0000036C,
+VFE_HW_TESTGEN_CFG                = 0x00000370,
+VFE_HW_TESTGEN_IMAGE_CFG          = 0x00000374,
+VFE_HW_TESTGEN_SOF_OFFSET_CFG     = 0x00000378,
+VFE_HW_TESTGEN_EOF_NOFFSET_CFG    = 0x0000037C,
+VFE_HW_TESTGEN_SOL_OFFSET_CFG     = 0x00000380,
+VFE_HW_TESTGEN_EOL_NOFFSET_CFG    = 0x00000384,
+VFE_HW_TESTGEN_HBI_CFG            = 0x00000388,
+VFE_HW_TESTGEN_VBL_CFG            = 0x0000038C,
+VFE_HW_TESTGEN_SOF_DUMMY_LINE_CFG2 = 0x00000390,
+VFE_HW_TESTGEN_EOF_DUMMY_LINE_CFG2 = 0x00000394,
+VFE_HW_TESTGEN_COLOR_BARS_CFG     = 0x00000398,
+VFE_HW_TESTGEN_RANDOM_CFG         = 0x0000039C,
+VFE_SPARE                         = 0x000003A0,
+};
+
+#define ping 0x0
+#define pong 0x1
+
+struct vfe_bus_cfg_data {
+	boolean                  stripeRdPathEn;
+	boolean                  encYWrPathEn;
+	boolean                  encCbcrWrPathEn;
+	boolean                  viewYWrPathEn;
+	boolean                  viewCbcrWrPathEn;
+	enum VFE_RAW_PIXEL_DATA_SIZE rawPixelDataSize;
+	enum VFE_RAW_WR_PATH_SEL     rawWritePathSelect;
+};
+
+struct vfe_camif_cfg_data {
+	boolean camif2OutputEnable;
+	boolean camif2BusEnable;
+	struct vfe_cmds_camif_cfg camifCfgFromCmd;
+};
+
+struct vfe_irq_composite_mask_config {
+	uint8_t encIrqComMask;
+	uint8_t viewIrqComMask;
+	uint8_t ceDoneSel;
+};
+
+/* define a structure for each output path.*/
+struct vfe_output_path {
+	uint32_t addressBuffer[8];
+	uint16_t fragIndex;
+	boolean  hwCurrentFlag;
+	uint8_t  *hwRegPingAddress;
+	uint8_t  *hwRegPongAddress;
+};
+
+struct vfe_output_path_combo {
+	boolean           whichOutputPath;
+	boolean           pathEnabled;
+	boolean           multiFrag;
+	uint8_t           fragCount;
+	boolean           ackPending;
+	uint8_t           currentFrame;
+	uint32_t          nextFrameAddrBuf[8];
+	struct vfe_output_path   yPath;
+	struct vfe_output_path   cbcrPath;
+	uint8_t           snapshotPendingCount;
+	boolean           pmEnabled;
+	uint8_t           cbcrStatusBit;
+};
+
+struct vfe_stats_control {
+	boolean  ackPending;
+	uint32_t addressBuffer[2];
+	uint32_t nextFrameAddrBuf;
+	boolean  pingPongStatus;
+	uint8_t  *hwRegPingAddress;
+	uint8_t  *hwRegPongAddress;
+	uint32_t droppedStatsFrameCount;
+	uint32_t bufToRender;
+};
+
+struct vfe_gamma_lut_sel {
+	boolean  ch0BankSelect;
+	boolean  ch1BankSelect;
+	boolean  ch2BankSelect;
+};
+
+struct vfe_interrupt_mask {
+	boolean  camifErrorIrq;
+	boolean  camifSofIrq;
+	boolean  camifEolIrq;
+	boolean  camifEofIrq;
+	boolean  camifEpoch1Irq;
+	boolean  camifEpoch2Irq;
+	boolean  camifOverflowIrq;
+	boolean  ceIrq;
+	boolean  regUpdateIrq;
+	boolean  resetAckIrq;
+	boolean  encYPingpongIrq;
+	boolean  encCbcrPingpongIrq;
+	boolean  viewYPingpongIrq;
+	boolean  viewCbcrPingpongIrq;
+	boolean  rdPingpongIrq;
+	boolean  afPingpongIrq;
+	boolean  awbPingpongIrq;
+	boolean  histPingpongIrq;
+	boolean  encIrq;
+	boolean  viewIrq;
+	boolean  busOverflowIrq;
+	boolean  afOverflowIrq;
+	boolean  awbOverflowIrq;
+	boolean  syncTimer0Irq;
+	boolean  syncTimer1Irq;
+	boolean  syncTimer2Irq;
+	boolean  asyncTimer0Irq;
+	boolean  asyncTimer1Irq;
+	boolean  asyncTimer2Irq;
+	boolean  asyncTimer3Irq;
+	boolean  axiErrorIrq;
+	boolean  violationIrq;
+};
+
+enum vfe_interrupt_name {
+	CAMIF_ERROR_IRQ,
+	CAMIF_SOF_IRQ,
+	CAMIF_EOL_IRQ,
+	CAMIF_EOF_IRQ,
+	CAMIF_EPOCH1_IRQ,
+	CAMIF_EPOCH2_IRQ,
+	CAMIF_OVERFLOW_IRQ,
+	CE_IRQ,
+	REG_UPDATE_IRQ,
+	RESET_ACK_IRQ,
+	ENC_Y_PINGPONG_IRQ,
+	ENC_CBCR_PINGPONG_IRQ,
+	VIEW_Y_PINGPONG_IRQ,
+	VIEW_CBCR_PINGPONG_IRQ,
+	RD_PINGPONG_IRQ,
+	AF_PINGPONG_IRQ,
+	AWB_PINGPONG_IRQ,
+	HIST_PINGPONG_IRQ,
+	ENC_IRQ,
+	VIEW_IRQ,
+	BUS_OVERFLOW_IRQ,
+	AF_OVERFLOW_IRQ,
+	AWB_OVERFLOW_IRQ,
+	SYNC_TIMER0_IRQ,
+	SYNC_TIMER1_IRQ,
+	SYNC_TIMER2_IRQ,
+	ASYNC_TIMER0_IRQ,
+	ASYNC_TIMER1_IRQ,
+	ASYNC_TIMER2_IRQ,
+	ASYNC_TIMER3_IRQ,
+	AXI_ERROR_IRQ,
+	VIOLATION_IRQ
+};
+
+enum VFE_DMI_RAM_SEL {
+	NO_MEM_SELECTED          = 0,
+	ROLLOFF_RAM              = 0x1,
+	RGBLUT_RAM_CH0_BANK0     = 0x2,
+	RGBLUT_RAM_CH0_BANK1     = 0x3,
+	RGBLUT_RAM_CH1_BANK0     = 0x4,
+	RGBLUT_RAM_CH1_BANK1     = 0x5,
+	RGBLUT_RAM_CH2_BANK0     = 0x6,
+	RGBLUT_RAM_CH2_BANK1     = 0x7,
+	STATS_HIST_CB_EVEN_RAM   = 0x8,
+	STATS_HIST_CB_ODD_RAM    = 0x9,
+	STATS_HIST_CR_EVEN_RAM   = 0xa,
+	STATS_HIST_CR_ODD_RAM    = 0xb,
+	RGBLUT_CHX_BANK0         = 0xc,
+	RGBLUT_CHX_BANK1         = 0xd,
+	LUMA_ADAPT_LUT_RAM_BANK0 = 0xe,
+	LUMA_ADAPT_LUT_RAM_BANK1 = 0xf
+};
+
+struct vfe_module_enable {
+	boolean  blackLevelCorrectionEnable;
+	boolean  lensRollOffEnable;
+	boolean  demuxEnable;
+	boolean  chromaUpsampleEnable;
+	boolean  demosaicEnable;
+	boolean  statsEnable;
+	boolean  cropEnable;
+	boolean  mainScalerEnable;
+	boolean  whiteBalanceEnable;
+	boolean  colorCorrectionEnable;
+	boolean  yHistEnable;
+	boolean  skinToneEnable;
+	boolean  lumaAdaptationEnable;
+	boolean  rgbLUTEnable;
+	boolean  chromaEnhanEnable;
+	boolean  asfEnable;
+	boolean  chromaSuppressionEnable;
+	boolean  chromaSubsampleEnable;
+	boolean  scaler2YEnable;
+	boolean  scaler2CbcrEnable;
+};
+
+struct vfe_bus_cmd_data {
+	boolean  stripeReload;
+	boolean  busPingpongReload;
+	boolean  statsPingpongReload;
+};
+
+struct vfe_stats_cmd_data {
+	boolean  autoFocusEnable;
+	boolean  axwEnable;
+	boolean  histEnable;
+	boolean  clearHistEnable;
+	boolean  histAutoClearEnable;
+	boolean  colorConversionEnable;
+};
+
+struct vfe_hw_ver {
+	uint32_t minorVersion:8;
+	uint32_t majorVersion:8;
+	uint32_t coreVersion:4;
+	uint32_t /* reserved */ : 12;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_cfg {
+	uint32_t pixelPattern:3;
+	uint32_t /* reserved */ : 13;
+	uint32_t inputSource:2;
+	uint32_t /* reserved */ : 14;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_buscmd {
+	uint32_t  stripeReload:1;
+	uint32_t  /* reserved */ : 3;
+	uint32_t  busPingpongReload:1;
+	uint32_t  statsPingpongReload:1;
+	uint32_t  /* reserved */ : 26;
+} __attribute__((packed, aligned(4)));
+
+struct VFE_Irq_Composite_MaskType {
+	uint32_t  encIrqComMaskBits:2;
+	uint32_t  viewIrqComMaskBits:2;
+	uint32_t  ceDoneSelBits:5;
+	uint32_t  /* reserved */ : 23;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_mod_enable {
+	uint32_t blackLevelCorrectionEnable:1;
+	uint32_t lensRollOffEnable:1;
+	uint32_t demuxEnable:1;
+	uint32_t chromaUpsampleEnable:1;
+	uint32_t demosaicEnable:1;
+	uint32_t statsEnable:1;
+	uint32_t cropEnable:1;
+	uint32_t mainScalerEnable:1;
+	uint32_t whiteBalanceEnable:1;
+	uint32_t colorCorrectionEnable:1;
+	uint32_t yHistEnable:1;
+	uint32_t skinToneEnable:1;
+	uint32_t lumaAdaptationEnable:1;
+	uint32_t rgbLUTEnable:1;
+	uint32_t chromaEnhanEnable:1;
+	uint32_t asfEnable:1;
+	uint32_t chromaSuppressionEnable:1;
+	uint32_t chromaSubsampleEnable:1;
+	uint32_t scaler2YEnable:1;
+	uint32_t scaler2CbcrEnable:1;
+	uint32_t /* reserved */ : 14;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_irqenable {
+	uint32_t camifErrorIrq:1;
+	uint32_t camifSofIrq:1;
+	uint32_t camifEolIrq:1;
+	uint32_t camifEofIrq:1;
+	uint32_t camifEpoch1Irq:1;
+	uint32_t camifEpoch2Irq:1;
+	uint32_t camifOverflowIrq:1;
+	uint32_t ceIrq:1;
+	uint32_t regUpdateIrq:1;
+	uint32_t resetAckIrq:1;
+	uint32_t encYPingpongIrq:1;
+	uint32_t encCbcrPingpongIrq:1;
+	uint32_t viewYPingpongIrq:1;
+	uint32_t viewCbcrPingpongIrq:1;
+	uint32_t rdPingpongIrq:1;
+	uint32_t afPingpongIrq:1;
+	uint32_t awbPingpongIrq:1;
+	uint32_t histPingpongIrq:1;
+	uint32_t encIrq:1;
+	uint32_t viewIrq:1;
+	uint32_t busOverflowIrq:1;
+	uint32_t afOverflowIrq:1;
+	uint32_t awbOverflowIrq:1;
+	uint32_t syncTimer0Irq:1;
+	uint32_t syncTimer1Irq:1;
+	uint32_t syncTimer2Irq:1;
+	uint32_t asyncTimer0Irq:1;
+	uint32_t asyncTimer1Irq:1;
+	uint32_t asyncTimer2Irq:1;
+	uint32_t asyncTimer3Irq:1;
+	uint32_t axiErrorIrq:1;
+	uint32_t violationIrq:1;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_upsample_cfg {
+	uint32_t chromaCositingForYCbCrInputs:1;
+	uint32_t /* reserved */ : 31;
+} __attribute__((packed, aligned(4)));
+
+struct VFE_CAMIFConfigType {
+	/* CAMIF Config */
+	uint32_t  /* reserved */ : 1;
+	uint32_t  VSyncEdge:1;
+	uint32_t  HSyncEdge:1;
+	uint32_t  syncMode:2;
+	uint32_t  vfeSubsampleEnable:1;
+	uint32_t  /* reserved */ : 1;
+	uint32_t  busSubsampleEnable:1;
+	uint32_t  camif2vfeEnable:1;
+	uint32_t  /* reserved */ : 1;
+	uint32_t  camif2busEnable:1;
+	uint32_t  irqSubsampleEnable:1;
+	uint32_t  binningEnable:1;
+	uint32_t  /* reserved */ : 18;
+	uint32_t  misrEnable:1;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_camifcfg {
+	/* EFS_Config */
+	uint32_t efsEndOfLine:8;
+	uint32_t efsStartOfLine:8;
+	uint32_t efsEndOfFrame:8;
+	uint32_t efsStartOfFrame:8;
+	/* Frame Config */
+	uint32_t frameConfigPixelsPerLine:14;
+	uint32_t /* reserved */ : 2;
+	uint32_t frameConfigLinesPerFrame:14;
+	uint32_t /* reserved */ : 2;
+	/* Window Width Config */
+	uint32_t windowWidthCfgLastPixel:14;
+	uint32_t /* reserved */ : 2;
+	uint32_t windowWidthCfgFirstPixel:14;
+	uint32_t /* reserved */ : 2;
+	/* Window Height Config */
+	uint32_t windowHeightCfglastLine:14;
+	uint32_t /* reserved */ : 2;
+	uint32_t windowHeightCfgfirstLine:14;
+	uint32_t /* reserved */ : 2;
+	/* Subsample 1 Config */
+	uint32_t subsample1CfgPixelSkip:16;
+	uint32_t subsample1CfgLineSkip:16;
+	/* Subsample 2 Config */
+	uint32_t subsample2CfgFrameSkip:4;
+	uint32_t subsample2CfgFrameSkipMode:1;
+	uint32_t subsample2CfgPixelSkipWrap:1;
+	uint32_t /* reserved */ : 26;
+	/* Epoch Interrupt */
+	uint32_t epoch1Line:14;
+	uint32_t /* reserved */ : 2;
+	uint32_t epoch2Line:14;
+	uint32_t /* reserved */ : 2;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_camifframe_update {
+	uint32_t pixelsPerLine:14;
+	uint32_t /* reserved */ : 2;
+	uint32_t linesPerFrame:14;
+	uint32_t /* reserved */ : 2;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_axi_bus_cfg {
+	uint32_t  stripeRdPathEn:1;
+	uint32_t  /* reserved */ : 3;
+	uint32_t  encYWrPathEn:1;
+	uint32_t  encCbcrWrPathEn:1;
+	uint32_t  viewYWrPathEn:1;
+	uint32_t  viewCbcrWrPathEn:1;
+	uint32_t  rawPixelDataSize:2;
+	uint32_t  rawWritePathSelect:2;
+	uint32_t  /* reserved */ : 20;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_axi_out_cfg {
+	uint32_t  out2YPingAddr:32;
+	uint32_t  out2YPongAddr:32;
+	uint32_t  out2YImageHeight:12;
+	uint32_t  /* reserved */ : 4;
+	uint32_t  out2YImageWidthin64bit:10;
+	uint32_t  /* reserved */ : 6;
+	uint32_t  out2YBurstLength:2;
+	uint32_t  /* reserved */ : 2;
+	uint32_t  out2YNumRows:12;
+	uint32_t  out2YRowIncrementIn64bit:12;
+	uint32_t  /* reserved */ : 4;
+	uint32_t  out2CbcrPingAddr:32;
+	uint32_t  out2CbcrPongAddr:32;
+	uint32_t  out2CbcrImageHeight:12;
+	uint32_t  /* reserved */ : 4;
+	uint32_t  out2CbcrImageWidthIn64bit:10;
+	uint32_t  /* reserved */ : 6;
+	uint32_t  out2CbcrBurstLength:2;
+	uint32_t  /* reserved */ : 2;
+	uint32_t  out2CbcrNumRows:12;
+	uint32_t  out2CbcrRowIncrementIn64bit:12;
+	uint32_t  /* reserved */ : 4;
+	uint32_t  out1YPingAddr:32;
+	uint32_t  out1YPongAddr:32;
+	uint32_t  out1YImageHeight:12;
+	uint32_t  /* reserved */ : 4;
+	uint32_t  out1YImageWidthin64bit:10;
+	uint32_t  /* reserved */ : 6;
+	uint32_t  out1YBurstLength:2;
+	uint32_t  /* reserved */ : 2;
+	uint32_t  out1YNumRows:12;
+	uint32_t  out1YRowIncrementIn64bit:12;
+	uint32_t  /* reserved */ : 4;
+	uint32_t  out1CbcrPingAddr:32;
+	uint32_t  out1CbcrPongAddr:32;
+	uint32_t  out1CbcrImageHeight:12;
+	uint32_t  /* reserved */ : 4;
+	uint32_t  out1CbcrImageWidthIn64bit:10;
+	uint32_t  /* reserved */ : 6;
+	uint32_t  out1CbcrBurstLength:2;
+	uint32_t  /* reserved */ : 2;
+	uint32_t  out1CbcrNumRows:12;
+	uint32_t  out1CbcrRowIncrementIn64bit:12;
+	uint32_t  /* reserved */ : 4;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_output_clamp_cfg {
+	/* Output Clamp Maximums */
+	uint32_t yChanMax:8;
+	uint32_t cbChanMax:8;
+	uint32_t crChanMax:8;
+	uint32_t /* reserved */ : 8;
+	/* Output Clamp Minimums */
+	uint32_t yChanMin:8;
+	uint32_t cbChanMin:8;
+	uint32_t crChanMin:8;
+	uint32_t /* reserved */ : 8;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_fov_crop_cfg {
+	uint32_t lastPixel:12;
+	uint32_t /* reserved */ : 4;
+	uint32_t firstPixel:12;
+	uint32_t /* reserved */ : 4;
+
+	/* FOV Corp, Part 2 */
+	uint32_t lastLine:12;
+	uint32_t /* reserved */ : 4;
+	uint32_t firstLine:12;
+	uint32_t /* reserved */ : 4;
+} __attribute__((packed, aligned(4)));
+
+struct VFE_FRAME_SKIP_UpdateCmdType {
+	uint32_t  yPattern:32;
+	uint32_t  cbcrPattern:32;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_frame_skip_cfg {
+	/* Frame Drop Enc (output2) */
+	uint32_t output2YPeriod:5;
+	uint32_t /* reserved */	: 27;
+	uint32_t output2CbCrPeriod:5;
+	uint32_t /* reserved */ : 27;
+	uint32_t output2YPattern:32;
+	uint32_t output2CbCrPattern:32;
+	/* Frame Drop View (output1) */
+	uint32_t output1YPeriod:5;
+	uint32_t /* reserved */ : 27;
+	uint32_t output1CbCrPeriod:5;
+	uint32_t /* reserved */ : 27;
+	uint32_t output1YPattern:32;
+	uint32_t output1CbCrPattern:32;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_main_scaler_cfg {
+	/* Scaler Enable Config */
+	uint32_t hEnable:1;
+	uint32_t vEnable:1;
+	uint32_t /* reserved */ : 30;
+	/* Scale H Image Size Config */
+	uint32_t inWidth:12;
+	uint32_t /* reserved */ : 4;
+	uint32_t outWidth:12;
+	uint32_t /* reserved */ : 4;
+	/* Scale H Phase Config */
+	uint32_t horizPhaseMult:18;
+	uint32_t /* reserved */ : 2;
+	uint32_t horizInterResolution:2;
+	uint32_t /* reserved */ : 10;
+	/* Scale H Stripe Config */
+	uint32_t horizMNInit:12;
+	uint32_t /* reserved */ : 4;
+	uint32_t horizPhaseInit:15;
+	uint32_t /* reserved */ : 1;
+	/* Scale V Image Size Config */
+	uint32_t inHeight:12;
+	uint32_t /* reserved */ : 4;
+	uint32_t outHeight:12;
+	uint32_t /* reserved */ : 4;
+	/* Scale V Phase Config */
+	uint32_t vertPhaseMult:18;
+	uint32_t /* reserved */ : 2;
+	uint32_t vertInterResolution:2;
+	uint32_t /* reserved */ : 10;
+	/* Scale V Stripe Config */
+	uint32_t vertMNInit:12;
+	uint32_t /* reserved */ : 4;
+	uint32_t vertPhaseInit:15;
+	uint32_t /* reserved */ : 1;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_scaler2_cfg {
+	/* Scaler   Enable Config */
+	uint32_t  hEnable:1;
+	uint32_t  vEnable:1;
+	uint32_t  /* reserved */ : 30;
+	/* Scaler   H Image Size Config */
+	uint32_t  inWidth:12;
+	uint32_t  /* reserved */ : 4;
+	uint32_t  outWidth:12;
+	uint32_t  /* reserved */ : 4;
+	/* Scaler   H Phase Config */
+	uint32_t  horizPhaseMult:18;
+	uint32_t  /* reserved */ : 2;
+	uint32_t  horizInterResolution:2;
+	uint32_t  /* reserved */ : 10;
+	/* Scaler   V Image Size Config */
+	uint32_t  inHeight:12;
+	uint32_t  /* reserved */ : 4;
+	uint32_t  outHeight:12;
+	uint32_t  /* reserved */ : 4;
+	/* Scaler   V Phase Config */
+	uint32_t  vertPhaseMult:18;
+	uint32_t  /* reserved */ : 2;
+	uint32_t  vertInterResolution:2;
+	uint32_t  /* reserved */ : 10;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_rolloff_cfg {
+	/* Rolloff 0 Config */
+	uint32_t  gridWidth:9;
+	uint32_t  gridHeight:9;
+	uint32_t  yDelta:9;
+	uint32_t  /* reserved */ : 5;
+	/* Rolloff 1 Config*/
+	uint32_t  gridX:4;
+	uint32_t  gridY:4;
+	uint32_t  pixelX:9;
+	uint32_t  /* reserved */ : 3;
+	uint32_t  pixelY:9;
+	uint32_t  /* reserved */ : 3;
+	/* Rolloff 2 Config */
+	uint32_t  yDeltaAccum:12;
+	uint32_t  /* reserved */ : 20;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_asf_update {
+	/* ASF Config Command */
+	uint32_t smoothEnable:1;
+	uint32_t sharpMode:2;
+	uint32_t /* reserved */ : 1;
+	uint32_t smoothCoeff1:4;
+	uint32_t smoothCoeff0:8;
+	uint32_t pipeFlushCount:12;
+	uint32_t pipeFlushOvd:1;
+	uint32_t flushHaltOvd:1;
+	uint32_t cropEnable:1;
+	uint32_t /* reserved */ : 1;
+	/* Sharpening Config 0 */
+	uint32_t sharpThresholdE1:7;
+	uint32_t /* reserved */ : 1;
+	uint32_t sharpDegreeK1:5;
+	uint32_t /* reserved */ : 3;
+	uint32_t sharpDegreeK2:5;
+	uint32_t /* reserved */ : 3;
+	uint32_t normalizeFactor:7;
+	uint32_t /* reserved */ : 1;
+	/* Sharpening Config 1 */
+	uint32_t sharpThresholdE2:8;
+	uint32_t sharpThresholdE3:8;
+	uint32_t sharpThresholdE4:8;
+	uint32_t sharpThresholdE5:8;
+	/* Sharpening Coefficients 0 */
+	uint32_t F1Coeff0:6;
+	uint32_t F1Coeff1:6;
+	uint32_t F1Coeff2:6;
+	uint32_t F1Coeff3:6;
+	uint32_t F1Coeff4:6;
+	uint32_t /* reserved */ : 2;
+	/* Sharpening Coefficients 1 */
+	uint32_t F1Coeff5:6;
+	uint32_t F1Coeff6:6;
+	uint32_t F1Coeff7:6;
+	uint32_t F1Coeff8:7;
+	uint32_t /* reserved */ : 7;
+	/* Sharpening Coefficients 2 */
+	uint32_t F2Coeff0:6;
+	uint32_t F2Coeff1:6;
+	uint32_t F2Coeff2:6;
+	uint32_t F2Coeff3:6;
+	uint32_t F2Coeff4:6;
+	uint32_t /* reserved */ : 2;
+	/* Sharpening Coefficients 3 */
+	uint32_t F2Coeff5:6;
+	uint32_t F2Coeff6:6;
+	uint32_t F2Coeff7:6;
+	uint32_t F2Coeff8:7;
+	uint32_t /* reserved */ : 7;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_asfcrop_cfg {
+	/* ASF Crop Width Config */
+	uint32_t lastPixel:12;
+	uint32_t /* reserved */ : 4;
+	uint32_t firstPixel:12;
+	uint32_t /* reserved */ : 4;
+	/* ASP Crop Height Config */
+	uint32_t lastLine:12;
+	uint32_t /* reserved */ : 4;
+	uint32_t firstLine:12;
+	uint32_t /* reserved */ : 4;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_chroma_suppress_cfg {
+	/* Chroma Suppress 0 Config */
+	uint32_t m1:8;
+	uint32_t m3:8;
+	uint32_t n1:3;
+	uint32_t /* reserved */ : 1;
+	uint32_t n3:3;
+	uint32_t /* reserved */ : 9;
+	/* Chroma Suppress 1 Config */
+	uint32_t mm1:8;
+	uint32_t nn1:3;
+	uint32_t /* reserved */ : 21;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_chromasubsample_cfg {
+	/* Chroma Subsample Selection */
+	uint32_t  hCositedPhase:1;
+	uint32_t  vCositedPhase:1;
+	uint32_t  hCosited:1;
+	uint32_t  vCosited:1;
+	uint32_t  hsubSampleEnable:1;
+	uint32_t  vsubSampleEnable:1;
+	uint32_t  cropEnable:1;
+	uint32_t  /* reserved */ : 25;
+	uint32_t  cropWidthLastPixel:12;
+	uint32_t  /* reserved */ : 4;
+	uint32_t  cropWidthFirstPixel:12;
+	uint32_t  /* reserved */ : 4;
+	uint32_t  cropHeightLastLine:12;
+	uint32_t  /* reserved */ : 4;
+	uint32_t  cropHeightFirstLine:12;
+	uint32_t  /* reserved */ : 4;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_blacklevel_cfg {
+	/* Black Even-Even Value Config */
+	uint32_t    evenEvenAdjustment:9;
+	uint32_t   /* reserved */ : 23;
+	/* Black Even-Odd Value Config */
+	uint32_t    evenOddAdjustment:9;
+	uint32_t   /* reserved */ : 23;
+	/* Black Odd-Even Value Config */
+	uint32_t    oddEvenAdjustment:9;
+	uint32_t   /* reserved */ : 23;
+	/* Black Odd-Odd Value Config */
+	uint32_t    oddOddAdjustment:9;
+	uint32_t   /* reserved */ : 23;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_demux_cfg {
+	/* Demux Gain 0 Config */
+	uint32_t  ch0EvenGain:10;
+	uint32_t  /* reserved */ : 6;
+	uint32_t  ch0OddGain:10;
+	uint32_t  /* reserved */ : 6;
+	/* Demux Gain 1 Config */
+	uint32_t  ch1Gain:10;
+	uint32_t  /* reserved */ : 6;
+	uint32_t  ch2Gain:10;
+	uint32_t  /* reserved */ : 6;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_bps_info {
+  uint32_t greenBadPixelCount:8;
+  uint32_t /* reserved */ : 8;
+  uint32_t RedBlueBadPixelCount:8;
+  uint32_t /* reserved */ : 8;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_demosaic_cfg {
+	/* Demosaic Config */
+	uint32_t abfEnable:1;
+	uint32_t badPixelCorrEnable:1;
+	uint32_t forceAbfOn:1;
+	uint32_t /* reserved */ : 1;
+	uint32_t abfShift:4;
+	uint32_t fminThreshold:7;
+	uint32_t /* reserved */ : 1;
+	uint32_t fmaxThreshold:7;
+	uint32_t /* reserved */ : 5;
+	uint32_t slopeShift:3;
+	uint32_t /* reserved */ : 1;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_demosaic_bpc_cfg {
+	/* Demosaic BPC Config 0 */
+	uint32_t blueDiffThreshold:12;
+	uint32_t redDiffThreshold:12;
+	uint32_t /* reserved */ : 8;
+	/* Demosaic BPC Config 1 */
+	uint32_t greenDiffThreshold:12;
+	uint32_t /* reserved */ : 20;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_demosaic_abf_cfg {
+	/* Demosaic ABF Config 0 */
+	uint32_t lpThreshold:10;
+	uint32_t /* reserved */ : 22;
+	/* Demosaic ABF Config 1 */
+	uint32_t ratio:4;
+	uint32_t minValue:10;
+	uint32_t /* reserved */ : 2;
+	uint32_t maxValue:10;
+	uint32_t /* reserved */ : 6;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_color_correction_cfg {
+	/* Color Corr. Coefficient 0 Config */
+	uint32_t   c0:12;
+	uint32_t   /* reserved */ : 20;
+	/* Color Corr. Coefficient 1 Config */
+	uint32_t   c1:12;
+	uint32_t   /* reserved */ : 20;
+	/* Color Corr. Coefficient 2 Config */
+	uint32_t   c2:12;
+	uint32_t   /* reserved */ : 20;
+	/* Color Corr. Coefficient 3 Config */
+	uint32_t   c3:12;
+	uint32_t   /* reserved */ : 20;
+	/* Color Corr. Coefficient 4 Config */
+	uint32_t   c4:12;
+	uint32_t   /* reserved */ : 20;
+	/* Color Corr. Coefficient 5 Config */
+	uint32_t   c5:12;
+	uint32_t   /* reserved */ : 20;
+	/* Color Corr. Coefficient 6 Config */
+	uint32_t   c6:12;
+	uint32_t   /* reserved */ : 20;
+	/* Color Corr. Coefficient 7 Config */
+	uint32_t   c7:12;
+	uint32_t   /* reserved */ : 20;
+	/* Color Corr. Coefficient 8 Config */
+	uint32_t   c8:12;
+	uint32_t   /* reserved */ : 20;
+	/* Color Corr. Offset 0 Config */
+	uint32_t   k0:11;
+	uint32_t   /* reserved */ : 21;
+	/* Color Corr. Offset 1 Config */
+	uint32_t   k1:11;
+	uint32_t   /* reserved */ : 21;
+	/* Color Corr. Offset 2 Config */
+	uint32_t   k2:11;
+	uint32_t   /* reserved */ : 21;
+	/* Color Corr. Coefficient Q Config */
+	uint32_t   coefQFactor:2;
+	uint32_t   /* reserved */ : 30;
+} __attribute__((packed, aligned(4)));
+
+struct VFE_LumaAdaptation_ConfigCmdType {
+	/* LA Config */
+	uint32_t   lutBankSelect:1;
+	uint32_t   /* reserved */ : 31;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_wb_cfg {
+	/* WB Config */
+	uint32_t ch0Gain:9;
+	uint32_t ch1Gain:9;
+	uint32_t ch2Gain:9;
+	uint32_t /* reserved */ : 5;
+} __attribute__((packed, aligned(4)));
+
+struct VFE_GammaLutSelect_ConfigCmdType {
+	/* LUT Bank Select Config */
+	uint32_t   ch0BankSelect:1;
+	uint32_t   ch1BankSelect:1;
+	uint32_t   ch2BankSelect:1;
+	uint32_t   /* reserved */ : 29;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_chroma_enhance_cfg {
+	/* Chroma Enhance A Config */
+	uint32_t ap:11;
+	uint32_t /* reserved */ : 5;
+	uint32_t am:11;
+	uint32_t /* reserved */ : 5;
+	/* Chroma Enhance B Config */
+	uint32_t bp:11;
+	uint32_t /* reserved */ : 5;
+	uint32_t bm:11;
+	uint32_t /* reserved */ : 5;
+	/* Chroma Enhance C Config */
+	uint32_t cp:11;
+	uint32_t /* reserved */ : 5;
+	uint32_t cm:11;
+	uint32_t /* reserved */ : 5;
+	/* Chroma Enhance D Config */
+	uint32_t dp:11;
+	uint32_t /* reserved */ : 5;
+	uint32_t dm:11;
+	uint32_t /* reserved */ : 5;
+	/* Chroma Enhance K Config */
+	uint32_t kcb:11;
+	uint32_t /* reserved */ : 5;
+	uint32_t kcr:11;
+	uint32_t /* reserved */ : 5;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_color_convert_cfg {
+	/* Conversion Coefficient 0 */
+	uint32_t v0:12;
+	uint32_t /* reserved */ : 20;
+	/* Conversion Coefficient 1 */
+	uint32_t v1:12;
+	uint32_t /* reserved */ : 20;
+	/* Conversion Coefficient 2 */
+	uint32_t v2:12;
+	uint32_t /* reserved */ : 20;
+	/* Conversion Offset */
+	uint32_t ConvertOffset:8;
+	uint32_t /* reserved */ : 24;
+} __attribute__((packed, aligned(4)));
+
+struct VFE_SyncTimer_ConfigCmdType {
+	/* Timer Line Start Config */
+	uint32_t       timerLineStart:12;
+	uint32_t       /* reserved */ : 20;
+	/* Timer Pixel Start Config */
+	uint32_t       timerPixelStart:18;
+	uint32_t       /* reserved */ : 14;
+	/* Timer Pixel Duration Config */
+	uint32_t       timerPixelDuration:28;
+	uint32_t       /* reserved */ : 4;
+	/* Sync Timer Polarity Config */
+	uint32_t       timer0Polarity:1;
+	uint32_t       timer1Polarity:1;
+	uint32_t       timer2Polarity:1;
+	uint32_t       /* reserved */ : 29;
+} __attribute__((packed, aligned(4)));
+
+struct VFE_AsyncTimer_ConfigCmdType {
+	/* Async Timer Config 0 */
+	uint32_t     inactiveLength:20;
+	uint32_t     numRepetition:10;
+	uint32_t     /* reserved */ : 1;
+	uint32_t     polarity:1;
+	/* Async Timer Config 1 */
+	uint32_t     activeLength:20;
+	uint32_t     /* reserved */ : 12;
+} __attribute__((packed, aligned(4)));
+
+struct VFE_AWBAEStatistics_ConfigCmdType {
+	/* AWB autoexposure Config */
+	uint32_t    aeRegionConfig:1;
+	uint32_t    aeSubregionConfig:1;
+	uint32_t    /* reserved */ : 14;
+	uint32_t    awbYMin:8;
+	uint32_t    awbYMax:8;
+	/* AXW Header */
+	uint32_t    axwHeader:8;
+	uint32_t    /* reserved */ : 24;
+	/* AWB Mconfig */
+	uint32_t    m4:8;
+	uint32_t    m3:8;
+	uint32_t    m2:8;
+	uint32_t    m1:8;
+	/* AWB Cconfig */
+	uint32_t    c2:12;
+	uint32_t    /* reserved */ : 4;
+	uint32_t    c1:12;
+	uint32_t    /* reserved */ : 4;
+	/* AWB Cconfig 2 */
+	uint32_t    c4:12;
+	uint32_t    /* reserved */ : 4;
+	uint32_t    c3:12;
+	uint32_t    /* reserved */ : 4;
+} __attribute__((packed, aligned(4)));
+
+struct VFE_TestGen_ConfigCmdType {
+	/* HW Test Gen Config */
+	uint32_t   numFrame:10;
+	uint32_t   /* reserved */ : 2;
+	uint32_t   pixelDataSelect:1;
+	uint32_t   systematicDataSelect:1;
+	uint32_t   /* reserved */ : 2;
+	uint32_t   pixelDataSize:2;
+	uint32_t   hsyncEdge:1;
+	uint32_t   vsyncEdge:1;
+	uint32_t   /* reserved */ : 12;
+	/* HW Test Gen Image Config */
+	uint32_t   imageWidth:14;
+	uint32_t   /* reserved */ : 2;
+	uint32_t   imageHeight:14;
+	uint32_t   /* reserved */ : 2;
+	/* SOF Offset Config */
+	uint32_t   sofOffset:24;
+	uint32_t   /* reserved */ : 8;
+	/* EOF NOffset Config */
+	uint32_t   eofNOffset:24;
+	uint32_t   /* reserved */ : 8;
+	/* SOL Offset Config */
+	uint32_t   solOffset:9;
+	uint32_t   /* reserved */ : 23;
+	/* EOL NOffset Config */
+	uint32_t   eolNOffset:9;
+	uint32_t   /* reserved */ : 23;
+	/* HBI Config */
+	uint32_t   hBlankInterval:14;
+	uint32_t   /* reserved */ : 18;
+	/* VBL Config */
+	uint32_t   vBlankInterval:14;
+	uint32_t   /* reserved */ : 2;
+	uint32_t   vBlankIntervalEnable:1;
+	uint32_t   /* reserved */ : 15;
+	/* SOF Dummy Line Config */
+	uint32_t   sofDummy:8;
+	uint32_t   /* reserved */ : 24;
+	/* EOF Dummy Line Config */
+	uint32_t   eofDummy:8;
+	uint32_t   /* reserved */ : 24;
+	/* Color Bars Config */
+	uint32_t   unicolorBarSelect:3;
+	uint32_t   /* reserved */ : 1;
+	uint32_t   unicolorBarEnable:1;
+	uint32_t   splitEnable:1;
+	uint32_t   pixelPattern:2;
+	uint32_t   rotatePeriod:6;
+	uint32_t   /* reserved */ : 18;
+	/* Random Config */
+	uint32_t   randomSeed:16;
+	uint32_t   /* reserved */ : 16;
+} __attribute__((packed, aligned(4)));
+
+struct VFE_Bus_Pm_ConfigCmdType {
+	/* VFE Bus Performance Monitor Config */
+	uint32_t  output2YWrPmEnable:1;
+	uint32_t  output2CbcrWrPmEnable:1;
+	uint32_t  output1YWrPmEnable:1;
+	uint32_t  output1CbcrWrPmEnable:1;
+	uint32_t  /* reserved */ : 28;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_asf_info {
+	/* asf max edge  */
+	uint32_t maxEdge:13;
+	uint32_t /* reserved */ : 3;
+	/* HBi count  */
+	uint32_t HBICount:12;
+	uint32_t /* reserved */ : 4;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_camif_stats {
+  uint32_t  pixelCount:14;
+  uint32_t  /* reserved */ : 2;
+  uint32_t  lineCount:14;
+  uint32_t  /* reserved */ : 1;
+  uint32_t  camifHalt:1;
+} __attribute__((packed, aligned(4)));
+
+struct VFE_StatsCmdType {
+	uint32_t  autoFocusEnable:1;
+	uint32_t  axwEnable:1;
+	uint32_t  histEnable:1;
+	uint32_t  clearHistEnable:1;
+	uint32_t  histAutoClearEnable:1;
+	uint32_t  colorConversionEnable:1;
+	uint32_t  /* reserved */ : 26;
+} __attribute__((packed, aligned(4)));
+
+
+struct vfe_statsframe {
+	uint32_t lastPixel:12;
+	uint32_t /* reserved */ : 4;
+	uint32_t lastLine:12;
+	uint32_t /* reserved */ : 4;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_busstats_wrprio {
+	uint32_t afBusPriority:4;
+	uint32_t awbBusPriority:4;
+	uint32_t histBusPriority:4;
+	uint32_t afBusPriorityEn:1;
+	uint32_t awbBusPriorityEn:1;
+	uint32_t histBusPriorityEn:1;
+	uint32_t /* reserved */ : 17;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_statsaf_update {
+	/* VFE_STATS_AF_CFG */
+	uint32_t windowVOffset:12;
+	uint32_t /* reserved */ : 4;
+	uint32_t windowHOffset:12;
+	uint32_t /* reserved */ : 3;
+	uint32_t windowMode:1;
+
+	/* VFE_STATS_AF_DIM */
+	uint32_t windowHeight:12;
+	uint32_t /* reserved */ : 4;
+	uint32_t windowWidth:12;
+	uint32_t /* reserved */ : 4;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_statsaf_cfg {
+	/* VFE_STATS_AF_GRID_0 */
+	uint32_t  entry00:8;
+	uint32_t  entry01:8;
+	uint32_t  entry02:8;
+	uint32_t  entry03:8;
+
+	/* VFE_STATS_AF_GRID_1 */
+	uint32_t  entry10:8;
+	uint32_t  entry11:8;
+	uint32_t  entry12:8;
+	uint32_t  entry13:8;
+
+	/* VFE_STATS_AF_GRID_2 */
+	uint32_t  entry20:8;
+	uint32_t  entry21:8;
+	uint32_t  entry22:8;
+	uint32_t  entry23:8;
+
+	/* VFE_STATS_AF_GRID_3 */
+	uint32_t  entry30:8;
+	uint32_t  entry31:8;
+	uint32_t  entry32:8;
+	uint32_t  entry33:8;
+
+	/* VFE_STATS_AF_HEADER */
+	uint32_t  afHeader:8;
+	uint32_t  /* reserved */ : 24;
+	/*  VFE_STATS_AF_COEF0 */
+	uint32_t  a00:5;
+	uint32_t  a04:5;
+	uint32_t  fvMax:11;
+	uint32_t  fvMetric:1;
+	uint32_t  /* reserved */ : 10;
+
+	/* VFE_STATS_AF_COEF1 */
+	uint32_t  a20:5;
+	uint32_t  a21:5;
+	uint32_t  a22:5;
+	uint32_t  a23:5;
+	uint32_t  a24:5;
+	uint32_t  /* reserved */ : 7;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_statsawbae_update {
+	uint32_t  aeRegionCfg:1;
+	uint32_t  aeSubregionCfg:1;
+	uint32_t  /* reserved */ : 14;
+	uint32_t  awbYMin:8;
+	uint32_t  awbYMax:8;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_statsaxw_hdr_cfg {
+	/* Stats AXW Header Config */
+	uint32_t axwHeader:8;
+	uint32_t /* reserved */ : 24;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_statsawb_update {
+	/* AWB MConfig */
+	uint32_t  m4:8;
+	uint32_t  m3:8;
+	uint32_t  m2:8;
+	uint32_t  m1:8;
+
+	/* AWB CConfig1 */
+	uint32_t  c2:12;
+	uint32_t  /* reserved */ : 4;
+	uint32_t  c1:12;
+	uint32_t  /* reserved */ : 4;
+
+	/* AWB CConfig2 */
+	uint32_t  c4:12;
+	uint32_t  /* reserved */ : 4;
+	uint32_t  c3:12;
+	uint32_t  /* reserved */ : 4;
+} __attribute__((packed, aligned(4)));
+
+struct VFE_SyncTimerCmdType {
+	uint32_t  hsyncCount:12;
+	uint32_t  /* reserved */ : 20;
+	uint32_t  pclkCount:18;
+	uint32_t  /* reserved */ : 14;
+	uint32_t  outputDuration:28;
+	uint32_t  /* reserved */ : 4;
+} __attribute__((packed, aligned(4)));
+
+struct VFE_AsyncTimerCmdType {
+	/*  config 0 */
+	uint32_t    inactiveCount:20;
+	uint32_t    repeatCount:10;
+	uint32_t    /* reserved */ : 1;
+	uint32_t    polarity:1;
+	/*  config 1 */
+	uint32_t    activeCount:20;
+	uint32_t    /* reserved */ : 12;
+} __attribute__((packed, aligned(4)));
+
+struct VFE_AxiInputCmdType {
+	uint32_t   stripeStartAddr0:32;
+	uint32_t   stripeStartAddr1:32;
+	uint32_t   stripeStartAddr2:32;
+	uint32_t   stripeStartAddr3:32;
+
+	uint32_t   ySize:12;
+	uint32_t   yOffsetDelta:12;
+	uint32_t   /* reserved */ : 8;
+
+	/* bus_stripe_rd_hSize */
+	uint32_t   /* reserved */ : 16;
+	uint32_t   xSizeWord:10;
+	uint32_t   /* reserved */ : 6;
+
+	/* bus_stripe_rd_buffer_cfg */
+	uint32_t   burstLength:2;
+	uint32_t   /* reserved */ : 2;
+	uint32_t   NumOfRows:12;
+	uint32_t   RowIncrement:12;
+	uint32_t   /* reserved */ : 4;
+
+	/* bus_stripe_rd_unpack_cfg */
+	uint32_t   mainUnpackHeight:12;
+	uint32_t   mainUnpackWidth:13;
+	uint32_t   mainUnpackHbiSel:3;
+	uint32_t   mainUnpackPhase:3;
+	uint32_t   /* reserved */ : 1;
+
+	/* bus_stripe_rd_unpack */
+	uint32_t   unpackPattern:32;
+
+	/* bus_stripe_rd_pad_size */
+	uint32_t   padLeft:7;
+	uint32_t   /* reserved */ : 1;
+	uint32_t   padRight:7;
+	uint32_t   /* reserved */ : 1;
+	uint32_t   padTop:7;
+	uint32_t   /* reserved */ : 1;
+	uint32_t   padBottom:7;
+	uint32_t   /* reserved */ : 1;
+
+	/* bus_stripe_rd_pad_L_unpack */
+	uint32_t   leftUnpackPattern0:4;
+	uint32_t   leftUnpackPattern1:4;
+	uint32_t   leftUnpackPattern2:4;
+	uint32_t   leftUnpackPattern3:4;
+	uint32_t   leftUnpackStop0:1;
+	uint32_t   leftUnpackStop1:1;
+	uint32_t   leftUnpackStop2:1;
+	uint32_t   leftUnpackStop3:1;
+	uint32_t   /* reserved */ : 12;
+
+	/* bus_stripe_rd_pad_R_unpack */
+	uint32_t   rightUnpackPattern0:4;
+	uint32_t   rightUnpackPattern1:4;
+	uint32_t   rightUnpackPattern2:4;
+	uint32_t   rightUnpackPattern3:4;
+	uint32_t   rightUnpackStop0:1;
+	uint32_t   rightUnpackStop1:1;
+	uint32_t   rightUnpackStop2:1;
+	uint32_t   rightUnpackStop3:1;
+	uint32_t   /* reserved */ : 12;
+
+	/* bus_stripe_rd_pad_tb_unpack */
+	uint32_t   topUnapckPattern:4;
+	uint32_t   /* reserved */ : 12;
+	uint32_t   bottomUnapckPattern:4;
+	uint32_t   /* reserved */ : 12;
+} __attribute__((packed, aligned(4)));
+
+struct VFE_AxiRdFragIrqEnable {
+	uint32_t stripeRdFragirq0Enable:1;
+	uint32_t stripeRdFragirq1Enable:1;
+	uint32_t stripeRdFragirq2Enable:1;
+	uint32_t stripeRdFragirq3Enable:1;
+	uint32_t   /* reserved */ : 28;
+} __attribute__((packed, aligned(4)));
+
+int vfe_cmd_init(struct msm_vfe_callback *, struct platform_device *, void *);
+void vfe_stats_af_stop(void);
+void vfe_stop(void);
+void vfe_update(void);
+int vfe_rgb_gamma_update(struct vfe_cmd_rgb_gamma_config *);
+int vfe_rgb_gamma_config(struct vfe_cmd_rgb_gamma_config *);
+void vfe_stats_wb_exp_ack(struct vfe_cmd_stats_wb_exp_ack *);
+void vfe_stats_af_ack(struct vfe_cmd_stats_af_ack *);
+void vfe_start(struct vfe_cmd_start *);
+void vfe_la_update(struct vfe_cmd_la_config *);
+void vfe_la_config(struct vfe_cmd_la_config *);
+void vfe_test_gen_start(struct vfe_cmd_test_gen_start *);
+void vfe_frame_skip_update(struct vfe_cmd_frame_skip_update *);
+void vfe_frame_skip_config(struct vfe_cmd_frame_skip_config *);
+void vfe_output_clamp_config(struct vfe_cmd_output_clamp_config *);
+void vfe_camif_frame_update(struct vfe_cmds_camif_frame *);
+void vfe_color_correction_config(struct vfe_cmd_color_correction_config *);
+void vfe_demosaic_abf_update(struct vfe_cmd_demosaic_abf_update *);
+void vfe_demosaic_bpc_update(struct vfe_cmd_demosaic_bpc_update *);
+void vfe_demosaic_config(struct vfe_cmd_demosaic_config *);
+void vfe_demux_channel_gain_update(struct vfe_cmd_demux_channel_gain_config *);
+void vfe_demux_channel_gain_config(struct vfe_cmd_demux_channel_gain_config *);
+void vfe_black_level_update(struct vfe_cmd_black_level_config *);
+void vfe_black_level_config(struct vfe_cmd_black_level_config *);
+void vfe_asf_update(struct vfe_cmd_asf_update *);
+void vfe_asf_config(struct vfe_cmd_asf_config *);
+void vfe_white_balance_config(struct vfe_cmd_white_balance_config *);
+void vfe_chroma_sup_config(struct vfe_cmd_chroma_suppression_config *);
+void vfe_roll_off_config(struct vfe_cmd_roll_off_config *);
+void vfe_chroma_subsample_config(struct vfe_cmd_chroma_subsample_config *);
+void vfe_chroma_enhan_config(struct vfe_cmd_chroma_enhan_config *);
+void vfe_scaler2cbcr_config(struct vfe_cmd_scaler2_config *);
+void vfe_scaler2y_config(struct vfe_cmd_scaler2_config *);
+void vfe_main_scaler_config(struct vfe_cmd_main_scaler_config *);
+void vfe_stats_wb_exp_stop(void);
+void vfe_stats_update_wb_exp(struct vfe_cmd_stats_wb_exp_update *);
+void vfe_stats_update_af(struct vfe_cmd_stats_af_update *);
+void vfe_stats_start_wb_exp(struct vfe_cmd_stats_wb_exp_start *);
+void vfe_stats_start_af(struct vfe_cmd_stats_af_start *);
+void vfe_stats_setting(struct vfe_cmd_stats_setting *);
+void vfe_axi_input_config(struct vfe_cmd_axi_input_config *);
+void vfe_axi_output_config(struct vfe_cmd_axi_output_config *);
+void vfe_camif_config(struct vfe_cmd_camif_config *);
+void vfe_fov_crop_config(struct vfe_cmd_fov_crop_config *);
+void vfe_get_hw_version(struct vfe_cmd_hw_version *);
+void vfe_reset(void);
+void vfe_cmd_release(struct platform_device *);
+void vfe_output_p_ack(struct vfe_cmd_output_ack *);
+void vfe_output_v_ack(struct vfe_cmd_output_ack *);
+#endif /* __MSM_VFE8X_REG_H__ */
diff --git a/drivers/media/video/msm/msm_vpe.c b/drivers/media/video/msm/msm_vpe.c
new file mode 100644
index 0000000..f9ce74b
--- /dev/null
+++ b/drivers/media/video/msm/msm_vpe.c
@@ -0,0 +1,796 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/interrupt.h>
+#include <mach/irqs.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/clk.h>
+#include <mach/clk.h>
+#include <asm/div64.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/ioctl.h>
+#include <linux/spinlock.h>
+#include "msm.h"
+#include "msm_vpe.h"
+
+static int vpe_enable(uint32_t);
+static int vpe_disable(void);
+static int vpe_update_scaler(struct msm_pp_crop *pcrop);
+struct vpe_ctrl_type *vpe_ctrl;
+static atomic_t vpe_init_done = ATOMIC_INIT(0);
+
+static int msm_vpe_do_pp(struct msm_mctl_pp_cmd *cmd,
+	struct msm_mctl_pp_frame_info *pp_frame_info);
+
+static long long vpe_do_div(long long num, long long den)
+{
+	do_div(num, den);
+	return num;
+}
+
+static int vpe_start(void)
+{
+	/*  enable the frame irq, bit 0 = Display list 0 ROI done */
+	msm_camera_io_w_mb(1, vpe_ctrl->vpebase + VPE_INTR_ENABLE_OFFSET);
+	msm_camera_io_dump(vpe_ctrl->vpebase, 0x120);
+	msm_camera_io_dump(vpe_ctrl->vpebase + 0x00400, 0x18);
+	msm_camera_io_dump(vpe_ctrl->vpebase + 0x10000, 0x250);
+	msm_camera_io_dump(vpe_ctrl->vpebase + 0x30000, 0x20);
+	msm_camera_io_dump(vpe_ctrl->vpebase + 0x50000, 0x30);
+	msm_camera_io_dump(vpe_ctrl->vpebase + 0x50400, 0x10);
+
+	/* this triggers the operation. */
+	msm_camera_io_w(1, vpe_ctrl->vpebase + VPE_DL0_START_OFFSET);
+	wmb();
+	return 0;
+}
+
+void vpe_reset_state_variables(void)
+{
+	/* initialize local variables for state control, etc.*/
+	vpe_ctrl->op_mode = 0;
+	vpe_ctrl->state = VPE_STATE_INIT;
+}
+
+static void vpe_config_axi_default(void)
+{
+	msm_camera_io_w(0x25, vpe_ctrl->vpebase + VPE_AXI_ARB_2_OFFSET);
+	CDBG("%s: yaddr %ld cbcraddr %ld", __func__,
+		 vpe_ctrl->out_y_addr, vpe_ctrl->out_cbcr_addr);
+	if (!vpe_ctrl->out_y_addr || !vpe_ctrl->out_cbcr_addr)
+		return;
+	msm_camera_io_w(vpe_ctrl->out_y_addr,
+		vpe_ctrl->vpebase + VPE_OUTP0_ADDR_OFFSET);
+	/* for video  CbCr address */
+	msm_camera_io_w(vpe_ctrl->out_cbcr_addr,
+		vpe_ctrl->vpebase + VPE_OUTP1_ADDR_OFFSET);
+
+}
+
+static int vpe_reset(void)
+{
+	uint32_t vpe_version;
+	uint32_t rc = 0;
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&vpe_ctrl->lock, flags);
+	if (vpe_ctrl->state == VPE_STATE_IDLE) {
+		CDBG("%s: VPE already disabled.", __func__);
+		spin_unlock_irqrestore(&vpe_ctrl->lock, flags);
+		return rc;
+	}
+	spin_unlock_irqrestore(&vpe_ctrl->lock, flags);
+
+	vpe_reset_state_variables();
+	vpe_version = msm_camera_io_r(
+			vpe_ctrl->vpebase + VPE_HW_VERSION_OFFSET);
+	CDBG("vpe_version = 0x%x\n", vpe_version);
+	/* disable all interrupts.*/
+	msm_camera_io_w(0, vpe_ctrl->vpebase + VPE_INTR_ENABLE_OFFSET);
+	/* clear all pending interrupts*/
+	msm_camera_io_w(0x1fffff, vpe_ctrl->vpebase + VPE_INTR_CLEAR_OFFSET);
+	/* write sw_reset to reset the core. */
+	msm_camera_io_w(0x10, vpe_ctrl->vpebase + VPE_SW_RESET_OFFSET);
+	/* then poll the reset bit, it should be self-cleared. */
+	while (1) {
+		rc =
+		msm_camera_io_r(vpe_ctrl->vpebase + VPE_SW_RESET_OFFSET) & 0x10;
+		if (rc == 0)
+			break;
+	}
+	/*  at this point, hardware is reset. Then pogram to default
+		values. */
+	msm_camera_io_w(VPE_AXI_RD_ARB_CONFIG_VALUE,
+			vpe_ctrl->vpebase + VPE_AXI_RD_ARB_CONFIG_OFFSET);
+
+	msm_camera_io_w(VPE_CGC_ENABLE_VALUE,
+			vpe_ctrl->vpebase + VPE_CGC_EN_OFFSET);
+	msm_camera_io_w(1, vpe_ctrl->vpebase + VPE_CMD_MODE_OFFSET);
+	msm_camera_io_w(VPE_DEFAULT_OP_MODE_VALUE,
+			vpe_ctrl->vpebase + VPE_OP_MODE_OFFSET);
+	msm_camera_io_w(VPE_DEFAULT_SCALE_CONFIG,
+			vpe_ctrl->vpebase + VPE_SCALE_CONFIG_OFFSET);
+	vpe_config_axi_default();
+	return rc;
+}
+
+static int msm_vpe_cfg_update(void *pinfo)
+{
+	uint32_t  rot_flag, rc = 0;
+	struct msm_pp_crop *pcrop = (struct msm_pp_crop *)pinfo;
+
+	rot_flag = msm_camera_io_r(vpe_ctrl->vpebase +
+						VPE_OP_MODE_OFFSET) & 0xE00;
+	if (pinfo != NULL) {
+		CDBG("%s: Crop info in2_w = %d, in2_h = %d "
+			"out2_w = %d out2_h = %d\n",
+			__func__, pcrop->src_w, pcrop->src_h,
+			pcrop->dst_w, pcrop->dst_h);
+		rc = vpe_update_scaler(pcrop);
+	}
+	CDBG("return rc = %d rot_flag = %d\n", rc, rot_flag);
+	rc |= rot_flag;
+
+	return rc;
+}
+
+void vpe_update_scale_coef(uint32_t *p)
+{
+	uint32_t i, offset;
+	offset = *p;
+	for (i = offset; i < (VPE_SCALE_COEFF_NUM + offset); i++) {
+		msm_camera_io_w(*(++p),
+			vpe_ctrl->vpebase + VPE_SCALE_COEFF_LSBn(i));
+		msm_camera_io_w(*(++p),
+			vpe_ctrl->vpebase + VPE_SCALE_COEFF_MSBn(i));
+	}
+}
+
+void vpe_input_plane_config(uint32_t *p)
+{
+	msm_camera_io_w(*p, vpe_ctrl->vpebase + VPE_SRC_FORMAT_OFFSET);
+	msm_camera_io_w(*(++p),
+		vpe_ctrl->vpebase + VPE_SRC_UNPACK_PATTERN1_OFFSET);
+	msm_camera_io_w(*(++p), vpe_ctrl->vpebase + VPE_SRC_IMAGE_SIZE_OFFSET);
+	msm_camera_io_w(*(++p), vpe_ctrl->vpebase + VPE_SRC_YSTRIDE1_OFFSET);
+	msm_camera_io_w(*(++p), vpe_ctrl->vpebase + VPE_SRC_SIZE_OFFSET);
+	msm_camera_io_w(*(++p), vpe_ctrl->vpebase + VPE_SRC_XY_OFFSET);
+}
+
+void vpe_output_plane_config(uint32_t *p)
+{
+	msm_camera_io_w(*p, vpe_ctrl->vpebase + VPE_OUT_FORMAT_OFFSET);
+	msm_camera_io_w(*(++p),
+		vpe_ctrl->vpebase + VPE_OUT_PACK_PATTERN1_OFFSET);
+	msm_camera_io_w(*(++p), vpe_ctrl->vpebase + VPE_OUT_YSTRIDE1_OFFSET);
+	msm_camera_io_w(*(++p), vpe_ctrl->vpebase + VPE_OUT_SIZE_OFFSET);
+	msm_camera_io_w(*(++p), vpe_ctrl->vpebase + VPE_OUT_XY_OFFSET);
+}
+
+static int vpe_operation_config(uint32_t *p)
+{
+	uint32_t w, h, temp;
+	msm_camera_io_w(*p, vpe_ctrl->vpebase + VPE_OP_MODE_OFFSET);
+
+	temp = msm_camera_io_r(vpe_ctrl->vpebase + VPE_OUT_SIZE_OFFSET);
+	w = temp & 0xFFF;
+	h = (temp & 0xFFF0000) >> 16;
+	if (*p++ & 0xE00) {
+		/* rotation enabled. */
+		vpe_ctrl->out_w = h;
+		vpe_ctrl->out_h = w;
+	} else {
+		vpe_ctrl->out_w = w;
+		vpe_ctrl->out_h = h;
+	}
+	CDBG("%s: out_w=%d, out_h=%d", __func__, vpe_ctrl->out_w,
+		vpe_ctrl->out_h);
+	return 0;
+}
+
+/* Later we can separate the rotation and scaler calc. If
+*  rotation is enabled, simply swap the destination dimension.
+*  And then pass the already swapped output size to this
+*  function. */
+static int vpe_update_scaler(struct msm_pp_crop *pcrop)
+{
+	uint32_t out_ROI_width, out_ROI_height;
+	uint32_t src_ROI_width, src_ROI_height;
+
+	/*
+	* phase_step_x, phase_step_y, phase_init_x and phase_init_y
+	* are represented in fixed-point, unsigned 3.29 format
+	*/
+	uint32_t phase_step_x = 0;
+	uint32_t phase_step_y = 0;
+	uint32_t phase_init_x = 0;
+	uint32_t phase_init_y = 0;
+
+	uint32_t src_roi, src_x, src_y, src_xy, temp;
+	uint32_t yscale_filter_sel, xscale_filter_sel;
+	uint32_t scale_unit_sel_x, scale_unit_sel_y;
+	uint64_t numerator, denominator;
+
+	/* assumption is both direction need zoom. this can be
+	improved. */
+	temp =
+		msm_camera_io_r(vpe_ctrl->vpebase + VPE_OP_MODE_OFFSET) | 0x3;
+	msm_camera_io_w(temp, vpe_ctrl->vpebase + VPE_OP_MODE_OFFSET);
+
+	src_ROI_width = pcrop->src_w;
+	src_ROI_height = pcrop->src_h;
+	out_ROI_width = pcrop->dst_w;
+	out_ROI_height = pcrop->dst_h;
+
+	CDBG("src w = 0x%x, h=0x%x, dst w = 0x%x, h =0x%x.\n",
+		src_ROI_width, src_ROI_height, out_ROI_width,
+		out_ROI_height);
+	src_roi = (src_ROI_height << 16) + src_ROI_width;
+
+	msm_camera_io_w(src_roi, vpe_ctrl->vpebase + VPE_SRC_SIZE_OFFSET);
+
+	src_x = pcrop->src_x;
+	src_y = pcrop->src_y;
+
+	CDBG("src_x = %d, src_y=%d.\n", src_x, src_y);
+
+	src_xy = src_y*(1<<16) + src_x;
+	msm_camera_io_w(src_xy, vpe_ctrl->vpebase +
+			VPE_SRC_XY_OFFSET);
+	CDBG("src_xy = %d, src_roi=%d.\n", src_xy, src_roi);
+
+	/* decide whether to use FIR or M/N for scaling */
+	if ((out_ROI_width == 1 && src_ROI_width < 4) ||
+		(src_ROI_width < 4 * out_ROI_width - 3))
+		scale_unit_sel_x = 0;/* use FIR scalar */
+	else
+		scale_unit_sel_x = 1;/* use M/N scalar */
+
+	if ((out_ROI_height == 1 && src_ROI_height < 4) ||
+		(src_ROI_height < 4 * out_ROI_height - 3))
+		scale_unit_sel_y = 0;/* use FIR scalar */
+	else
+		scale_unit_sel_y = 1;/* use M/N scalar */
+
+	/* calculate phase step for the x direction */
+
+	/* if destination is only 1 pixel wide,
+	the value of phase_step_x
+	is unimportant. Assigning phase_step_x to
+	src ROI width as an arbitrary value. */
+	if (out_ROI_width == 1)
+		phase_step_x = (uint32_t) ((src_ROI_width) <<
+						SCALER_PHASE_BITS);
+
+		/* if using FIR scalar */
+	else if (scale_unit_sel_x == 0) {
+
+		/* Calculate the quotient ( src_ROI_width - 1 )
+			( out_ROI_width - 1)
+			with u3.29 precision. Quotient is rounded up to
+			the larger 29th decimal point*/
+		numerator = (uint64_t)(src_ROI_width - 1) <<
+			SCALER_PHASE_BITS;
+		/* never equals to 0 because of the
+			"(out_ROI_width == 1 )"*/
+		denominator = (uint64_t)(out_ROI_width - 1);
+		/* divide and round up to the larger 29th
+			decimal point.*/
+		phase_step_x = (uint32_t) vpe_do_div((numerator +
+					denominator - 1), denominator);
+	} else if (scale_unit_sel_x == 1) { /* if M/N scalar */
+		/* Calculate the quotient ( src_ROI_width ) /
+			( out_ROI_width)
+			with u3.29 precision. Quotient is rounded down to the
+			smaller 29th decimal point.*/
+		numerator = (uint64_t)(src_ROI_width) <<
+			SCALER_PHASE_BITS;
+		denominator = (uint64_t)(out_ROI_width);
+		phase_step_x =
+			(uint32_t) vpe_do_div(numerator, denominator);
+	}
+	/* calculate phase step for the y direction */
+
+	/* if destination is only 1 pixel wide, the value of
+		phase_step_x is unimportant. Assigning phase_step_x
+		to src ROI width as an arbitrary value. */
+	if (out_ROI_height == 1)
+		phase_step_y =
+		(uint32_t) ((src_ROI_height) << SCALER_PHASE_BITS);
+
+	/* if FIR scalar */
+	else if (scale_unit_sel_y == 0) {
+		/* Calculate the quotient ( src_ROI_height - 1 ) /
+		( out_ROI_height - 1)
+		with u3.29 precision. Quotient is rounded up to the
+		larger 29th decimal point. */
+		numerator = (uint64_t)(src_ROI_height - 1) <<
+			SCALER_PHASE_BITS;
+		/* never equals to 0 because of the "
+		( out_ROI_height == 1 )" case */
+		denominator = (uint64_t)(out_ROI_height - 1);
+		/* Quotient is rounded up to the larger
+		29th decimal point. */
+		phase_step_y =
+		(uint32_t) vpe_do_div(
+			(numerator + denominator - 1), denominator);
+	} else if (scale_unit_sel_y == 1) { /* if M/N scalar */
+		/* Calculate the quotient ( src_ROI_height )
+			( out_ROI_height)
+			with u3.29 precision. Quotient is rounded down
+			to the smaller 29th decimal point. */
+		numerator = (uint64_t)(src_ROI_height) <<
+			SCALER_PHASE_BITS;
+		denominator = (uint64_t)(out_ROI_height);
+		phase_step_y = (uint32_t) vpe_do_div(
+			numerator, denominator);
+	}
+
+	/* decide which set of FIR coefficients to use */
+	if (phase_step_x > HAL_MDP_PHASE_STEP_2P50)
+		xscale_filter_sel = 0;
+	else if (phase_step_x > HAL_MDP_PHASE_STEP_1P66)
+		xscale_filter_sel = 1;
+	else if (phase_step_x > HAL_MDP_PHASE_STEP_1P25)
+		xscale_filter_sel = 2;
+	else
+		xscale_filter_sel = 3;
+
+	if (phase_step_y > HAL_MDP_PHASE_STEP_2P50)
+		yscale_filter_sel = 0;
+	else if (phase_step_y > HAL_MDP_PHASE_STEP_1P66)
+		yscale_filter_sel = 1;
+	else if (phase_step_y > HAL_MDP_PHASE_STEP_1P25)
+		yscale_filter_sel = 2;
+	else
+		yscale_filter_sel = 3;
+
+	/* calculate phase init for the x direction */
+
+	/* if using FIR scalar */
+	if (scale_unit_sel_x == 0) {
+		if (out_ROI_width == 1)
+			phase_init_x =
+				(uint32_t) ((src_ROI_width - 1) <<
+							SCALER_PHASE_BITS);
+		else
+			phase_init_x = 0;
+	} else if (scale_unit_sel_x == 1) /* M over N scalar  */
+		phase_init_x = 0;
+
+	/* calculate phase init for the y direction
+	if using FIR scalar */
+	if (scale_unit_sel_y == 0) {
+		if (out_ROI_height == 1)
+			phase_init_y =
+			(uint32_t) ((src_ROI_height -
+						1) << SCALER_PHASE_BITS);
+		else
+			phase_init_y = 0;
+	} else if (scale_unit_sel_y == 1) /* M over N scalar   */
+		phase_init_y = 0;
+
+	CDBG("phase step x = %d, step y = %d.\n",
+		 phase_step_x, phase_step_y);
+	CDBG("phase init x = %d, init y = %d.\n",
+		 phase_init_x, phase_init_y);
+
+	msm_camera_io_w(phase_step_x, vpe_ctrl->vpebase +
+			VPE_SCALE_PHASEX_STEP_OFFSET);
+	msm_camera_io_w(phase_step_y, vpe_ctrl->vpebase +
+			VPE_SCALE_PHASEY_STEP_OFFSET);
+
+	msm_camera_io_w(phase_init_x, vpe_ctrl->vpebase +
+			VPE_SCALE_PHASEX_INIT_OFFSET);
+
+	msm_camera_io_w(phase_init_y, vpe_ctrl->vpebase +
+			VPE_SCALE_PHASEY_INIT_OFFSET);
+
+	return 1;
+}
+
+int msm_vpe_is_busy(void)
+{
+	int busy = 0;
+	unsigned long flags;
+	spin_lock_irqsave(&vpe_ctrl->lock, flags);
+	if (vpe_ctrl->state == VPE_STATE_ACTIVE)
+		busy = 1;
+	spin_unlock_irqrestore(&vpe_ctrl->lock, flags);
+	return busy;
+}
+static int msm_send_frame_to_vpe(void)
+{
+	int rc = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&vpe_ctrl->lock, flags);
+	msm_camera_io_w((vpe_ctrl->pp_frame_info->src_frame.sp.phy_addr +
+			  vpe_ctrl->pp_frame_info->src_frame.sp.y_off),
+			vpe_ctrl->vpebase + VPE_SRCP0_ADDR_OFFSET);
+	msm_camera_io_w((vpe_ctrl->pp_frame_info->src_frame.sp.phy_addr +
+			  vpe_ctrl->pp_frame_info->src_frame.sp.cbcr_off),
+			vpe_ctrl->vpebase + VPE_SRCP1_ADDR_OFFSET);
+	msm_camera_io_w((vpe_ctrl->pp_frame_info->dest_frame.sp.phy_addr +
+			  vpe_ctrl->pp_frame_info->dest_frame.sp.y_off),
+			vpe_ctrl->vpebase + VPE_OUTP0_ADDR_OFFSET);
+	msm_camera_io_w((vpe_ctrl->pp_frame_info->dest_frame.sp.phy_addr +
+			  vpe_ctrl->pp_frame_info->dest_frame.sp.cbcr_off),
+			vpe_ctrl->vpebase + VPE_OUTP1_ADDR_OFFSET);
+	vpe_ctrl->state = VPE_STATE_ACTIVE;
+	spin_unlock_irqrestore(&vpe_ctrl->lock, flags);
+	vpe_start();
+	return rc;
+}
+
+static void vpe_send_outmsg(void)
+{
+	unsigned long flags;
+	struct msm_vpe_resp rp;
+	memset(&rp, 0, sizeof(rp));
+	spin_lock_irqsave(&vpe_ctrl->lock, flags);
+	if (vpe_ctrl->state == VPE_STATE_IDLE) {
+		pr_err("%s VPE is in IDLE state. Ignore the ack msg", __func__);
+		spin_unlock_irqrestore(&vpe_ctrl->lock, flags);
+		return;
+	}
+	rp.type = vpe_ctrl->pp_frame_info->pp_frame_cmd.path;
+	rp.extdata = (void *)vpe_ctrl->pp_frame_info;
+	rp.extlen = sizeof(*vpe_ctrl->pp_frame_info);
+	vpe_ctrl->state = VPE_STATE_INIT;   /* put it back to idle. */
+	vpe_ctrl->pp_frame_info = NULL;
+	spin_unlock_irqrestore(&vpe_ctrl->lock, flags);
+	v4l2_subdev_notify(&vpe_ctrl->subdev,
+		NOTIFY_VPE_MSG_EVT, (void *)&rp);
+}
+
+static void vpe_do_tasklet(unsigned long data)
+{
+	CDBG("%s: irq_status = 0x%x",
+		   __func__, vpe_ctrl->irq_status);
+	if (vpe_ctrl->irq_status & 0x1)
+		vpe_send_outmsg();
+
+}
+DECLARE_TASKLET(vpe_tasklet, vpe_do_tasklet, 0);
+
+static irqreturn_t vpe_parse_irq(int irq_num, void *data)
+{
+	vpe_ctrl->irq_status = msm_camera_io_r_mb(vpe_ctrl->vpebase +
+							VPE_INTR_STATUS_OFFSET);
+	msm_camera_io_w_mb(vpe_ctrl->irq_status, vpe_ctrl->vpebase +
+				VPE_INTR_CLEAR_OFFSET);
+	msm_camera_io_w(0, vpe_ctrl->vpebase + VPE_INTR_ENABLE_OFFSET);
+	CDBG("%s: vpe_parse_irq =0x%x.\n", __func__, vpe_ctrl->irq_status);
+	tasklet_schedule(&vpe_tasklet);
+	return IRQ_HANDLED;
+}
+
+static struct msm_cam_clk_info vpe_clk_info[] = {
+	{"vpe_clk", 160000000},
+	{"vpe_pclk", -1},
+};
+
+int vpe_enable(uint32_t clk_rate)
+{
+	int rc = 0;
+	unsigned long flags = 0;
+	CDBG("%s", __func__);
+	/* don't change the order of clock and irq.*/
+	spin_lock_irqsave(&vpe_ctrl->lock, flags);
+	if (vpe_ctrl->state != VPE_STATE_IDLE) {
+		pr_err("%s: VPE already enabled", __func__);
+		spin_unlock_irqrestore(&vpe_ctrl->lock, flags);
+		return 0;
+	}
+	vpe_ctrl->state = VPE_STATE_INIT;
+	spin_unlock_irqrestore(&vpe_ctrl->lock, flags);
+	enable_irq(vpe_ctrl->vpeirq->start);
+	vpe_ctrl->fs_vpe = regulator_get(NULL, "fs_vpe");
+	if (IS_ERR(vpe_ctrl->fs_vpe)) {
+		pr_err("%s: Regulator FS_VPE get failed %ld\n", __func__,
+			PTR_ERR(vpe_ctrl->fs_vpe));
+		vpe_ctrl->fs_vpe = NULL;
+		goto vpe_fs_failed;
+	} else if (regulator_enable(vpe_ctrl->fs_vpe)) {
+		pr_err("%s: Regulator FS_VPE enable failed\n", __func__);
+		regulator_put(vpe_ctrl->fs_vpe);
+		goto vpe_fs_failed;
+	}
+
+	rc = msm_cam_clk_enable(&vpe_ctrl->pdev->dev, vpe_clk_info,
+			vpe_ctrl->vpe_clk, ARRAY_SIZE(vpe_clk_info), 1);
+	if (rc < 0)
+		goto vpe_clk_failed;
+
+	return rc;
+
+vpe_clk_failed:
+	regulator_disable(vpe_ctrl->fs_vpe);
+	regulator_put(vpe_ctrl->fs_vpe);
+	vpe_ctrl->fs_vpe = NULL;
+vpe_fs_failed:
+	disable_irq(vpe_ctrl->vpeirq->start);
+	vpe_ctrl->state = VPE_STATE_IDLE;
+	return rc;
+}
+
+int vpe_disable(void)
+{
+	int rc = 0;
+	unsigned long flags = 0;
+	CDBG("%s", __func__);
+	spin_lock_irqsave(&vpe_ctrl->lock, flags);
+	if (vpe_ctrl->state == VPE_STATE_IDLE) {
+		CDBG("%s: VPE already disabled", __func__);
+		spin_unlock_irqrestore(&vpe_ctrl->lock, flags);
+		return rc;
+	}
+	spin_unlock_irqrestore(&vpe_ctrl->lock, flags);
+
+	disable_irq(vpe_ctrl->vpeirq->start);
+	tasklet_kill(&vpe_tasklet);
+	msm_cam_clk_enable(&vpe_ctrl->pdev->dev, vpe_clk_info,
+			vpe_ctrl->vpe_clk, ARRAY_SIZE(vpe_clk_info), 0);
+
+	regulator_disable(vpe_ctrl->fs_vpe);
+	regulator_put(vpe_ctrl->fs_vpe);
+	vpe_ctrl->fs_vpe = NULL;
+	spin_lock_irqsave(&vpe_ctrl->lock, flags);
+	vpe_ctrl->state = VPE_STATE_IDLE;
+	spin_unlock_irqrestore(&vpe_ctrl->lock, flags);
+	return rc;
+}
+
+static int msm_vpe_do_pp(struct msm_mctl_pp_cmd *cmd,
+			struct msm_mctl_pp_frame_info *pp_frame_info)
+{
+	int rc = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&vpe_ctrl->lock, flags);
+	if (vpe_ctrl->state == VPE_STATE_ACTIVE ||
+		 vpe_ctrl->state == VPE_STATE_IDLE) {
+		spin_unlock_irqrestore(&vpe_ctrl->lock, flags);
+		pr_err(" =====VPE in wrong state:%d!!!  Wrong!========\n",
+		vpe_ctrl->state);
+		return -EBUSY;
+	}
+	spin_unlock_irqrestore(&vpe_ctrl->lock, flags);
+	vpe_ctrl->pp_frame_info = pp_frame_info;
+	msm_vpe_cfg_update(
+		&vpe_ctrl->pp_frame_info->pp_frame_cmd.crop);
+	CDBG("%s Sending frame idx %d id %d to VPE ", __func__,
+		pp_frame_info->src_frame.buf_idx,
+		pp_frame_info->src_frame.frame_id);
+	rc = msm_send_frame_to_vpe();
+	return rc;
+}
+
+static int msm_vpe_resource_init(void);
+
+int msm_vpe_subdev_init(struct v4l2_subdev *sd,
+		struct msm_cam_media_controller *mctl)
+{
+	int rc = 0;
+	CDBG("%s:begin", __func__);
+	if (atomic_read(&vpe_init_done)) {
+		pr_err("%s: VPE has been initialized", __func__);
+		return -EBUSY;
+	}
+	atomic_set(&vpe_init_done, 1);
+
+	rc = msm_vpe_resource_init();
+	if (rc < 0) {
+		atomic_set(&vpe_init_done, 0);
+		return rc;
+	}
+	v4l2_set_subdev_hostdata(sd, mctl);
+	spin_lock_init(&vpe_ctrl->lock);
+	CDBG("%s:end", __func__);
+	return rc;
+}
+EXPORT_SYMBOL(msm_vpe_subdev_init);
+
+static int msm_vpe_resource_init(void)
+{
+	int rc = 0;
+
+	vpe_ctrl->vpebase = ioremap(vpe_ctrl->vpemem->start,
+		resource_size(vpe_ctrl->vpemem));
+
+	if (!vpe_ctrl->vpebase) {
+		rc = -ENOMEM;
+		pr_err("%s: vpe ioremap failed\n", __func__);
+		goto vpe_unmap_mem_region;
+	}
+
+	return rc;
+/* from this part it is error handling. */
+vpe_unmap_mem_region:
+	iounmap(vpe_ctrl->vpebase);
+	vpe_ctrl->vpebase = NULL;
+	return rc;  /* this rc should have error code. */
+}
+
+void msm_vpe_subdev_release(void)
+{
+	if (!atomic_read(&vpe_init_done)) {
+		/* no VPE object created */
+		pr_err("%s: no VPE object to release", __func__);
+		return;
+	}
+
+	vpe_reset();
+	vpe_disable();
+	iounmap(vpe_ctrl->vpebase);
+	vpe_ctrl->vpebase = NULL;
+	atomic_set(&vpe_init_done, 0);
+}
+EXPORT_SYMBOL(msm_vpe_subdev_release);
+
+static long msm_vpe_subdev_ioctl(struct v4l2_subdev *sd,
+			unsigned int subdev_cmd, void *arg)
+{
+	struct msm_mctl_pp_params *vpe_params;
+	struct msm_mctl_pp_cmd *cmd;
+	int rc = 0;
+
+	if (subdev_cmd == VIDIOC_MSM_VPE_INIT) {
+		struct msm_cam_media_controller *mctl =
+			(struct msm_cam_media_controller *)arg;
+		msm_vpe_subdev_init(sd, mctl);
+	} else if (subdev_cmd == VIDIOC_MSM_VPE_RELEASE) {
+		msm_vpe_subdev_release();
+	} else if (subdev_cmd == VIDIOC_MSM_VPE_CFG) {
+		vpe_params = (struct msm_mctl_pp_params *)arg;
+		cmd = vpe_params->cmd;
+		switch (cmd->id) {
+		case VPE_CMD_INIT:
+		case VPE_CMD_DEINIT:
+			break;
+		case VPE_CMD_RESET:
+			rc = vpe_reset();
+			break;
+		case VPE_CMD_OPERATION_MODE_CFG:
+			rc = vpe_operation_config(cmd->value);
+			break;
+		case VPE_CMD_INPUT_PLANE_CFG:
+			vpe_input_plane_config(cmd->value);
+			break;
+		case VPE_CMD_OUTPUT_PLANE_CFG:
+			vpe_output_plane_config(cmd->value);
+			break;
+		case VPE_CMD_SCALE_CFG_TYPE:
+			vpe_update_scale_coef(cmd->value);
+			break;
+		case VPE_CMD_ZOOM: {
+			rc = msm_vpe_do_pp(cmd,
+			(struct msm_mctl_pp_frame_info *)vpe_params->data);
+			break;
+		}
+		case VPE_CMD_ENABLE: {
+			struct msm_vpe_clock_rate *clk_rate = cmd->value;
+			int turbo_mode = (int)clk_rate->rate;
+			rc = turbo_mode ?
+				vpe_enable(VPE_TURBO_MODE_CLOCK_RATE) :
+				vpe_enable(VPE_NORMAL_MODE_CLOCK_RATE);
+			break;
+		}
+		case VPE_CMD_DISABLE:
+			rc = vpe_disable();
+			break;
+		case VPE_CMD_INPUT_PLANE_UPDATE:
+		case VPE_CMD_FLUSH:
+		default:
+			break;
+		}
+		CDBG("%s: end, id = %d, rc = %d", __func__, cmd->id, rc);
+	}
+	return rc;
+}
+
+static const struct v4l2_subdev_core_ops msm_vpe_subdev_core_ops = {
+	.ioctl = msm_vpe_subdev_ioctl,
+};
+
+static const struct v4l2_subdev_ops msm_vpe_subdev_ops = {
+	.core = &msm_vpe_subdev_core_ops,
+};
+
+static const struct v4l2_subdev_internal_ops msm_vpe_internal_ops;
+
+static int __devinit vpe_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	CDBG("%s: device id = %d\n", __func__, pdev->id);
+	vpe_ctrl = kzalloc(sizeof(struct vpe_ctrl_type), GFP_KERNEL);
+	if (!vpe_ctrl) {
+		pr_err("%s: no enough memory\n", __func__);
+		return -ENOMEM;
+	}
+
+	v4l2_subdev_init(&vpe_ctrl->subdev, &msm_vpe_subdev_ops);
+	v4l2_set_subdevdata(&vpe_ctrl->subdev, vpe_ctrl);
+	vpe_ctrl->subdev.internal_ops = &msm_vpe_internal_ops;
+	vpe_ctrl->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	snprintf(vpe_ctrl->subdev.name, sizeof(vpe_ctrl->subdev.name), "vpe");
+	platform_set_drvdata(pdev, &vpe_ctrl->subdev);
+
+	vpe_ctrl->vpemem = platform_get_resource_byname(pdev,
+					IORESOURCE_MEM, "vpe");
+	if (!vpe_ctrl->vpemem) {
+		pr_err("%s: no mem resource?\n", __func__);
+		rc = -ENODEV;
+		goto vpe_no_resource;
+	}
+	vpe_ctrl->vpeirq = platform_get_resource_byname(pdev,
+					IORESOURCE_IRQ, "vpe");
+	if (!vpe_ctrl->vpeirq) {
+		pr_err("%s: no irq resource?\n", __func__);
+		rc = -ENODEV;
+		goto vpe_no_resource;
+	}
+
+	vpe_ctrl->vpeio = request_mem_region(vpe_ctrl->vpemem->start,
+		resource_size(vpe_ctrl->vpemem), pdev->name);
+	if (!vpe_ctrl->vpeio) {
+		pr_err("%s: no valid mem region\n", __func__);
+		rc = -EBUSY;
+		goto vpe_no_resource;
+	}
+
+	rc = request_irq(vpe_ctrl->vpeirq->start, vpe_parse_irq,
+		IRQF_TRIGGER_RISING, "vpe", 0);
+	if (rc < 0) {
+		release_mem_region(vpe_ctrl->vpemem->start,
+			resource_size(vpe_ctrl->vpemem));
+		pr_err("%s: irq request fail\n", __func__);
+		rc = -EBUSY;
+		goto vpe_no_resource;
+	}
+
+	disable_irq(vpe_ctrl->vpeirq->start);
+
+	vpe_ctrl->pdev = pdev;
+	msm_cam_register_subdev_node(&vpe_ctrl->subdev, VPE_DEV, pdev->id);
+	return 0;
+
+vpe_no_resource:
+	kfree(vpe_ctrl);
+	return 0;
+}
+
+struct platform_driver vpe_driver = {
+	.probe = vpe_probe,
+	.driver = {
+		.name = MSM_VPE_DRV_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init msm_vpe_init_module(void)
+{
+	return platform_driver_register(&vpe_driver);
+}
+
+module_init(msm_vpe_init_module);
+MODULE_DESCRIPTION("VPE driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/msm_vpe.h b/drivers/media/video/msm/msm_vpe.h
new file mode 100644
index 0000000..0d14626
--- /dev/null
+++ b/drivers/media/video/msm/msm_vpe.h
@@ -0,0 +1,191 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _MSM_VPE_H_
+#define _MSM_VPE_H_
+
+#include <mach/camera.h>
+
+/***********  start of register offset *********************/
+#define VPE_INTR_ENABLE_OFFSET                0x0020
+#define VPE_INTR_STATUS_OFFSET                0x0024
+#define VPE_INTR_CLEAR_OFFSET                 0x0028
+#define VPE_DL0_START_OFFSET                  0x0030
+#define VPE_HW_VERSION_OFFSET                 0x0070
+#define VPE_SW_RESET_OFFSET                   0x0074
+#define VPE_AXI_RD_ARB_CONFIG_OFFSET          0x0078
+#define VPE_SEL_CLK_OR_HCLK_TEST_BUS_OFFSET   0x007C
+#define VPE_CGC_EN_OFFSET                     0x0100
+#define VPE_CMD_STATUS_OFFSET                 0x10008
+#define VPE_PROFILE_EN_OFFSET                 0x10010
+#define VPE_PROFILE_COUNT_OFFSET              0x10014
+#define VPE_CMD_MODE_OFFSET                   0x10060
+#define VPE_SRC_SIZE_OFFSET                   0x10108
+#define VPE_SRCP0_ADDR_OFFSET                 0x1010C
+#define VPE_SRCP1_ADDR_OFFSET                 0x10110
+#define VPE_SRC_YSTRIDE1_OFFSET               0x1011C
+#define VPE_SRC_FORMAT_OFFSET                 0x10124
+#define VPE_SRC_UNPACK_PATTERN1_OFFSET        0x10128
+#define VPE_OP_MODE_OFFSET                    0x10138
+#define VPE_SCALE_PHASEX_INIT_OFFSET          0x1013C
+#define VPE_SCALE_PHASEY_INIT_OFFSET          0x10140
+#define VPE_SCALE_PHASEX_STEP_OFFSET          0x10144
+#define VPE_SCALE_PHASEY_STEP_OFFSET          0x10148
+#define VPE_OUT_FORMAT_OFFSET                 0x10150
+#define VPE_OUT_PACK_PATTERN1_OFFSET          0x10154
+#define VPE_OUT_SIZE_OFFSET                   0x10164
+#define VPE_OUTP0_ADDR_OFFSET                 0x10168
+#define VPE_OUTP1_ADDR_OFFSET                 0x1016C
+#define VPE_OUT_YSTRIDE1_OFFSET               0x10178
+#define VPE_OUT_XY_OFFSET                     0x1019C
+#define VPE_SRC_XY_OFFSET                     0x10200
+#define VPE_SRC_IMAGE_SIZE_OFFSET             0x10208
+#define VPE_SCALE_CONFIG_OFFSET               0x10230
+#define VPE_DEINT_STATUS_OFFSET               0x30000
+#define VPE_DEINT_DECISION_OFFSET             0x30004
+#define VPE_DEINT_COEFF0_OFFSET               0x30010
+#define VPE_SCALE_STATUS_OFFSET               0x50000
+#define VPE_SCALE_SVI_PARAM_OFFSET            0x50010
+#define VPE_SCALE_SHARPEN_CFG_OFFSET          0x50020
+#define VPE_SCALE_COEFF_LSP_0_OFFSET          0x50400
+#define VPE_SCALE_COEFF_MSP_0_OFFSET          0x50404
+
+#define VPE_AXI_ARB_1_OFFSET                  0x00408
+#define VPE_AXI_ARB_2_OFFSET                  0x0040C
+
+#define VPE_SCALE_COEFF_LSBn(n)	(0x50400 + 8 * (n))
+#define VPE_SCALE_COEFF_MSBn(n)	(0x50404 + 8 * (n))
+#define VPE_SCALE_COEFF_NUM			32
+
+/*********** end of register offset ********************/
+
+
+#define VPE_HARDWARE_VERSION          0x00080308
+#define VPE_SW_RESET_VALUE            0x00000010  /* bit 4 for PPP*/
+#define VPE_AXI_RD_ARB_CONFIG_VALUE   0x124924
+#define VPE_CMD_MODE_VALUE            0x1
+#define VPE_DEFAULT_OP_MODE_VALUE     0x40FC0004
+#define VPE_CGC_ENABLE_VALUE          0xffff
+#define VPE_DEFAULT_SCALE_CONFIG      0x3c
+
+#define VPE_NORMAL_MODE_CLOCK_RATE   150000000
+#define VPE_TURBO_MODE_CLOCK_RATE   200000000
+
+
+/**************************************************/
+/*********** End of command id ********************/
+/**************************************************/
+
+enum vpe_state {
+	VPE_STATE_IDLE,
+	VPE_STATE_INIT,
+	VPE_STATE_ACTIVE,
+};
+
+struct vpe_ctrl_type {
+	spinlock_t        lock;
+	uint32_t          irq_status;
+	void              *syncdata;
+	uint16_t          op_mode;
+	void              *extdata;
+	uint32_t          extlen;
+	struct msm_vpe_callback *resp;
+	uint32_t          out_h;  /* this is BEFORE rotation. */
+	uint32_t          out_w;  /* this is BEFORE rotation. */
+	struct timespec   ts;
+	int               output_type;
+	int               frame_pack;
+	uint8_t           pad_2k_bool;
+	enum vpe_state    state;
+	unsigned long     out_y_addr;
+	unsigned long     out_cbcr_addr;
+	struct v4l2_subdev subdev;
+	struct platform_device *pdev;
+	struct resource   *vpeirq;
+	void __iomem      *vpebase;
+	struct resource	  *vpemem;
+	struct resource   *vpeio;
+	void        *device_extdata;
+	struct regulator *fs_vpe;
+	struct clk	*vpe_clk[2];
+	struct msm_mctl_pp_frame_info *pp_frame_info;
+};
+
+/*
+* vpe_input_update
+*
+* Define the parameters for output plane
+*/
+/* this is the dimension of ROI.  width / height. */
+struct vpe_src_size_packed {
+	uint32_t        src_w;
+	uint32_t        src_h;
+};
+
+struct vpe_src_xy_packed {
+	uint32_t        src_x;
+	uint32_t        src_y;
+};
+
+struct vpe_input_plane_update_type {
+	struct vpe_src_size_packed             src_roi_size;
+	/* crop updates this set. */
+	struct vpe_src_xy_packed               src_roi_offset;
+	/* input address*/
+	uint8_t                         *src_p0_addr;
+	uint8_t                         *src_p1_addr;
+};
+
+struct vpe_msg_stats {
+	uint32_t    buffer;
+	uint32_t    frameCounter;
+};
+
+struct vpe_msg_output {
+	uint8_t   output_id;
+	uint32_t  yBuffer;
+	uint32_t  cbcrBuffer;
+	uint32_t  frameCounter;
+};
+
+struct vpe_message {
+	uint8_t  _d;
+	union {
+		struct vpe_msg_output              msgOut;
+		struct vpe_msg_stats               msgStats;
+	} _u;
+};
+
+#define SCALER_PHASE_BITS 29
+#define HAL_MDP_PHASE_STEP_2P50    0x50000000
+#define HAL_MDP_PHASE_STEP_1P66    0x35555555
+#define HAL_MDP_PHASE_STEP_1P25    0x28000000
+
+struct phase_val_t {
+	int32_t phase_init_x;
+	int32_t phase_init_y;
+	int32_t phase_step_x;
+	int32_t phase_step_y;
+};
+
+#define VIDIOC_MSM_VPE_INIT \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 15, struct msm_cam_media_controller *)
+
+#define VIDIOC_MSM_VPE_RELEASE \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 16, struct msm_cam_media_controller *)
+
+#define VIDIOC_MSM_VPE_CFG \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 17, struct msm_mctl_pp_params *)
+
+#endif /*_MSM_VPE_H_*/
+
diff --git a/drivers/media/video/msm/msm_vpe1.c b/drivers/media/video/msm/msm_vpe1.c
new file mode 100644
index 0000000..4f97c43
--- /dev/null
+++ b/drivers/media/video/msm/msm_vpe1.c
@@ -0,0 +1,1468 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/uaccess.h>
+#include <linux/interrupt.h>
+#include <mach/irqs.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include "msm_vpe1.h"
+#include <linux/pm_qos.h>
+#include <linux/clk.h>
+#include <mach/clk.h>
+#include <asm/div64.h>
+
+static int vpe_enable(uint32_t);
+static int vpe_disable(void);
+static int vpe_update_scaler(struct video_crop_t *pcrop);
+static struct vpe_device_type  vpe_device_data;
+static struct vpe_device_type  *vpe_device;
+struct vpe_ctrl_type    *vpe_ctrl;
+char *vpe_general_cmd[] = {
+	"VPE_DUMMY_0",  /* 0 */
+	"VPE_SET_CLK",
+	"VPE_RESET",
+	"VPE_START",
+	"VPE_ABORT",
+	"VPE_OPERATION_MODE_CFG",  /* 5 */
+	"VPE_INPUT_PLANE_CFG",
+	"VPE_OUTPUT_PLANE_CFG",
+	"VPE_INPUT_PLANE_UPDATE",
+	"VPE_SCALE_CFG_TYPE",
+	"VPE_ROTATION_CFG_TYPE",  /* 10 */
+	"VPE_AXI_OUT_CFG",
+	"VPE_CMD_DIS_OFFSET_CFG",
+	"VPE_ENABLE",
+	"VPE_DISABLE",
+};
+static uint32_t orig_src_y, orig_src_cbcr;
+
+#define CHECKED_COPY_FROM_USER(in) {					\
+	if (copy_from_user((in), (void __user *)cmd->value,		\
+			cmd->length)) {					\
+		rc = -EFAULT;						\
+		break;							\
+	}								\
+}
+
+#define msm_dequeue_vpe(queue, member) ({			\
+	unsigned long flags;					\
+	struct msm_device_queue *__q = (queue);			\
+	struct msm_queue_cmd *qcmd = 0;				\
+	spin_lock_irqsave(&__q->lock, flags);			\
+	if (!list_empty(&__q->list)) {				\
+		__q->len--;					\
+		qcmd = list_first_entry(&__q->list,		\
+				struct msm_queue_cmd, member);	\
+		list_del_init(&qcmd->member);			\
+	}							\
+	spin_unlock_irqrestore(&__q->lock, flags);		\
+	qcmd;							\
+})
+
+/*
+static   struct vpe_cmd_type vpe_cmd[] = {
+		{VPE_DUMMY_0, 0},
+		{VPE_SET_CLK, 0},
+		{VPE_RESET, 0},
+		{VPE_START, 0},
+		{VPE_ABORT, 0},
+		{VPE_OPERATION_MODE_CFG, VPE_OPERATION_MODE_CFG_LEN},
+		{VPE_INPUT_PLANE_CFG, VPE_INPUT_PLANE_CFG_LEN},
+		{VPE_OUTPUT_PLANE_CFG, VPE_OUTPUT_PLANE_CFG_LEN},
+		{VPE_INPUT_PLANE_UPDATE, VPE_INPUT_PLANE_UPDATE_LEN},
+		{VPE_SCALE_CFG_TYPE, VPE_SCALER_CONFIG_LEN},
+		{VPE_ROTATION_CFG_TYPE, 0},
+		{VPE_AXI_OUT_CFG, 0},
+		{VPE_CMD_DIS_OFFSET_CFG, VPE_DIS_OFFSET_CFG_LEN},
+};
+*/
+
+static long long vpe_do_div(long long num, long long den)
+{
+	do_div(num, den);
+	return num;
+}
+
+static int vpe_start(void)
+{
+	/*  enable the frame irq, bit 0 = Display list 0 ROI done */
+	msm_camera_io_w(1, vpe_device->vpebase + VPE_INTR_ENABLE_OFFSET);
+	msm_camera_io_dump(vpe_device->vpebase + 0x10000, 0x250);
+	/* this triggers the operation. */
+	msm_camera_io_w(1, vpe_device->vpebase + VPE_DL0_START_OFFSET);
+
+	return 0;
+}
+
+void vpe_reset_state_variables(void)
+{
+	/* initialize local variables for state control, etc.*/
+	vpe_ctrl->op_mode = 0;
+	vpe_ctrl->state = VPE_STATE_INIT;
+	spin_lock_init(&vpe_ctrl->tasklet_lock);
+	spin_lock_init(&vpe_ctrl->state_lock);
+	INIT_LIST_HEAD(&vpe_ctrl->tasklet_q);
+}
+
+static void vpe_config_axi_default(void)
+{
+	msm_camera_io_w(0x25, vpe_device->vpebase + VPE_AXI_ARB_2_OFFSET);
+
+	CDBG("%s: yaddr %ld cbcraddr %ld", __func__,
+		 vpe_ctrl->out_y_addr, vpe_ctrl->out_cbcr_addr);
+
+	if (!vpe_ctrl->out_y_addr || !vpe_ctrl->out_cbcr_addr)
+		return;
+
+	msm_camera_io_w(vpe_ctrl->out_y_addr,
+		vpe_device->vpebase + VPE_OUTP0_ADDR_OFFSET);
+	/* for video  CbCr address */
+	msm_camera_io_w(vpe_ctrl->out_cbcr_addr,
+		vpe_device->vpebase + VPE_OUTP1_ADDR_OFFSET);
+
+}
+
+static int vpe_reset(void)
+{
+	uint32_t vpe_version;
+	uint32_t rc;
+
+	vpe_reset_state_variables();
+	vpe_version = msm_camera_io_r(
+			vpe_device->vpebase + VPE_HW_VERSION_OFFSET);
+	CDBG("vpe_version = 0x%x\n", vpe_version);
+
+	/* disable all interrupts.*/
+	msm_camera_io_w(0, vpe_device->vpebase + VPE_INTR_ENABLE_OFFSET);
+	/* clear all pending interrupts*/
+	msm_camera_io_w(0x1fffff, vpe_device->vpebase + VPE_INTR_CLEAR_OFFSET);
+
+	/* write sw_reset to reset the core. */
+	msm_camera_io_w(0x10, vpe_device->vpebase + VPE_SW_RESET_OFFSET);
+
+	/* then poll the reset bit, it should be self-cleared. */
+	while (1) {
+		rc = msm_camera_io_r(vpe_device->vpebase + VPE_SW_RESET_OFFSET)
+				& 0x10;
+		if (rc == 0)
+			break;
+	}
+
+	/*  at this point, hardware is reset. Then pogram to default
+		values. */
+	msm_camera_io_w(VPE_AXI_RD_ARB_CONFIG_VALUE,
+			vpe_device->vpebase + VPE_AXI_RD_ARB_CONFIG_OFFSET);
+
+	msm_camera_io_w(VPE_CGC_ENABLE_VALUE,
+			vpe_device->vpebase + VPE_CGC_EN_OFFSET);
+
+	msm_camera_io_w(1, vpe_device->vpebase + VPE_CMD_MODE_OFFSET);
+
+	msm_camera_io_w(VPE_DEFAULT_OP_MODE_VALUE,
+			vpe_device->vpebase + VPE_OP_MODE_OFFSET);
+
+	msm_camera_io_w(VPE_DEFAULT_SCALE_CONFIG,
+			vpe_device->vpebase + VPE_SCALE_CONFIG_OFFSET);
+
+	vpe_config_axi_default();
+	return 0;
+}
+
+int msm_vpe_cfg_update(void *pinfo)
+{
+	uint32_t  rot_flag, rc = 0;
+	struct video_crop_t *pcrop = (struct video_crop_t *)pinfo;
+
+	rot_flag = msm_camera_io_r(vpe_device->vpebase +
+						VPE_OP_MODE_OFFSET) & 0xE00;
+	if (pinfo != NULL) {
+		CDBG("Crop info in2_w = %d, in2_h = %d "
+			"out2_h = %d out2_w = %d \n", pcrop->in2_w,
+			pcrop->in2_h,
+			pcrop->out2_h, pcrop->out2_w);
+		rc = vpe_update_scaler(pcrop);
+	}
+	CDBG("return rc = %d rot_flag = %d\n", rc, rot_flag);
+	rc |= rot_flag;
+
+	return rc;
+}
+
+void vpe_update_scale_coef(uint32_t *p)
+{
+	uint32_t i, offset;
+	offset = *p;
+	for (i = offset; i < (VPE_SCALE_COEFF_NUM + offset); i++) {
+		msm_camera_io_w(*(++p),
+			vpe_device->vpebase + VPE_SCALE_COEFF_LSBn(i));
+		msm_camera_io_w(*(++p),
+			vpe_device->vpebase + VPE_SCALE_COEFF_MSBn(i));
+	}
+}
+
+void vpe_input_plane_config(uint32_t *p)
+{
+	msm_camera_io_w(*p,
+		vpe_device->vpebase + VPE_SRC_FORMAT_OFFSET);
+	msm_camera_io_w(*(++p),
+		vpe_device->vpebase + VPE_SRC_UNPACK_PATTERN1_OFFSET);
+	msm_camera_io_w(*(++p),
+		vpe_device->vpebase + VPE_SRC_IMAGE_SIZE_OFFSET);
+	msm_camera_io_w(*(++p),
+		vpe_device->vpebase + VPE_SRC_YSTRIDE1_OFFSET);
+	msm_camera_io_w(*(++p),
+		vpe_device->vpebase + VPE_SRC_SIZE_OFFSET);
+	vpe_ctrl->in_h_w = *p;
+	msm_camera_io_w(*(++p),
+		vpe_device->vpebase + VPE_SRC_XY_OFFSET);
+}
+
+void vpe_output_plane_config(uint32_t *p)
+{
+	msm_camera_io_w(*p,
+		vpe_device->vpebase + VPE_OUT_FORMAT_OFFSET);
+	msm_camera_io_w(*(++p),
+		vpe_device->vpebase + VPE_OUT_PACK_PATTERN1_OFFSET);
+	msm_camera_io_w(*(++p),
+		vpe_device->vpebase + VPE_OUT_YSTRIDE1_OFFSET);
+	msm_camera_io_w(*(++p),
+		vpe_device->vpebase + VPE_OUT_SIZE_OFFSET);
+	msm_camera_io_w(*(++p),
+		vpe_device->vpebase + VPE_OUT_XY_OFFSET);
+	vpe_ctrl->pcbcr_dis_offset = *(++p);
+}
+
+static int vpe_operation_config(uint32_t *p)
+{
+	uint32_t  outw, outh, temp;
+	msm_camera_io_w(*p, vpe_device->vpebase + VPE_OP_MODE_OFFSET);
+
+	temp = msm_camera_io_r(vpe_device->vpebase + VPE_OUT_SIZE_OFFSET);
+	outw = temp & 0xFFF;
+	outh = (temp & 0xFFF0000) >> 16;
+
+	if (*p++ & 0xE00) {
+		/* rotation enabled. */
+		vpe_ctrl->out_w = outh;
+		vpe_ctrl->out_h = outw;
+	} else {
+		vpe_ctrl->out_w = outw;
+		vpe_ctrl->out_h = outh;
+	}
+	vpe_ctrl->dis_en = *p;
+	return 0;
+}
+
+/* Later we can separate the rotation and scaler calc. If
+*  rotation is enabled, simply swap the destination dimension.
+*  And then pass the already swapped output size to this
+*  function. */
+static int vpe_update_scaler(struct video_crop_t *pcrop)
+{
+	uint32_t out_ROI_width, out_ROI_height;
+	uint32_t src_ROI_width, src_ROI_height;
+
+	uint32_t rc = 0;  /* default to no zoom. */
+	/*
+	* phase_step_x, phase_step_y, phase_init_x and phase_init_y
+	* are represented in fixed-point, unsigned 3.29 format
+	*/
+	uint32_t phase_step_x = 0;
+	uint32_t phase_step_y = 0;
+	uint32_t phase_init_x = 0;
+	uint32_t phase_init_y = 0;
+
+	uint32_t src_roi, src_x, src_y, src_xy, temp;
+	uint32_t yscale_filter_sel, xscale_filter_sel;
+	uint32_t scale_unit_sel_x, scale_unit_sel_y;
+	uint64_t numerator, denominator;
+
+	if ((pcrop->in2_w >= pcrop->out2_w) &&
+		(pcrop->in2_h >= pcrop->out2_h)) {
+		CDBG(" =======VPE no zoom needed.\n");
+
+		temp = msm_camera_io_r(vpe_device->vpebase + VPE_OP_MODE_OFFSET)
+		& 0xfffffffc;
+		msm_camera_io_w(temp, vpe_device->vpebase + VPE_OP_MODE_OFFSET);
+
+
+		msm_camera_io_w(0, vpe_device->vpebase + VPE_SRC_XY_OFFSET);
+
+		CDBG("vpe_ctrl->in_h_w = %d\n", vpe_ctrl->in_h_w);
+		msm_camera_io_w(vpe_ctrl->in_h_w , vpe_device->vpebase +
+				VPE_SRC_SIZE_OFFSET);
+
+		return rc;
+	}
+	/* If fall through then scaler is needed.*/
+
+	CDBG("========VPE zoom needed.\n");
+	/* assumption is both direction need zoom. this can be
+	improved. */
+	temp =
+		msm_camera_io_r(vpe_device->vpebase + VPE_OP_MODE_OFFSET) | 0x3;
+	msm_camera_io_w(temp, vpe_device->vpebase + VPE_OP_MODE_OFFSET);
+
+	src_ROI_width = pcrop->in2_w;
+	src_ROI_height = pcrop->in2_h;
+	out_ROI_width = pcrop->out2_w;
+	out_ROI_height = pcrop->out2_h;
+
+	CDBG("src w = 0x%x, h=0x%x, dst w = 0x%x, h =0x%x.\n",
+		src_ROI_width, src_ROI_height, out_ROI_width,
+		out_ROI_height);
+	src_roi = (src_ROI_height << 16) + src_ROI_width;
+
+	msm_camera_io_w(src_roi, vpe_device->vpebase + VPE_SRC_SIZE_OFFSET);
+
+	src_x = (out_ROI_width - src_ROI_width)/2;
+	src_y = (out_ROI_height - src_ROI_height)/2;
+
+	CDBG("src_x = %d, src_y=%d.\n", src_x, src_y);
+
+	src_xy = src_y*(1<<16) + src_x;
+	msm_camera_io_w(src_xy, vpe_device->vpebase +
+			VPE_SRC_XY_OFFSET);
+	CDBG("src_xy = %d, src_roi=%d.\n", src_xy, src_roi);
+
+	/* decide whether to use FIR or M/N for scaling */
+	if ((out_ROI_width == 1 && src_ROI_width < 4) ||
+		(src_ROI_width < 4 * out_ROI_width - 3))
+		scale_unit_sel_x = 0;/* use FIR scalar */
+	else
+		scale_unit_sel_x = 1;/* use M/N scalar */
+
+	if ((out_ROI_height == 1 && src_ROI_height < 4) ||
+		(src_ROI_height < 4 * out_ROI_height - 3))
+		scale_unit_sel_y = 0;/* use FIR scalar */
+	else
+		scale_unit_sel_y = 1;/* use M/N scalar */
+
+	/* calculate phase step for the x direction */
+
+	/* if destination is only 1 pixel wide,
+	the value of phase_step_x
+	is unimportant. Assigning phase_step_x to
+	src ROI width as an arbitrary value. */
+	if (out_ROI_width == 1)
+		phase_step_x = (uint32_t) ((src_ROI_width) <<
+						SCALER_PHASE_BITS);
+
+		/* if using FIR scalar */
+	else if (scale_unit_sel_x == 0) {
+
+		/* Calculate the quotient ( src_ROI_width - 1 )
+		/ ( out_ROI_width - 1)
+		with u3.29 precision. Quotient is rounded up to
+		the larger 29th decimal point. */
+		numerator = (uint64_t)(src_ROI_width - 1) <<
+			SCALER_PHASE_BITS;
+		/* never equals to 0 because of the
+		"(out_ROI_width == 1 )"*/
+		denominator = (uint64_t)(out_ROI_width - 1);
+		/* divide and round up to the larger 29th
+		decimal point. */
+		phase_step_x = (uint32_t) vpe_do_div((numerator +
+					denominator - 1), denominator);
+	} else if (scale_unit_sel_x == 1) { /* if M/N scalar */
+		/* Calculate the quotient ( src_ROI_width ) /
+		( out_ROI_width)
+		with u3.29 precision. Quotient is rounded down to the
+		smaller 29th decimal point. */
+		numerator = (uint64_t)(src_ROI_width) <<
+			SCALER_PHASE_BITS;
+		denominator = (uint64_t)(out_ROI_width);
+		phase_step_x =
+			(uint32_t) vpe_do_div(numerator, denominator);
+	}
+	/* calculate phase step for the y direction */
+
+	/* if destination is only 1 pixel wide, the value of
+		phase_step_x is unimportant. Assigning phase_step_x
+		to src ROI width as an arbitrary value. */
+	if (out_ROI_height == 1)
+		phase_step_y =
+		(uint32_t) ((src_ROI_height) << SCALER_PHASE_BITS);
+
+	/* if FIR scalar */
+	else if (scale_unit_sel_y == 0) {
+		/* Calculate the quotient ( src_ROI_height - 1 ) /
+		( out_ROI_height - 1)
+		with u3.29 precision. Quotient is rounded up to the
+		larger 29th decimal point. */
+		numerator = (uint64_t)(src_ROI_height - 1) <<
+			SCALER_PHASE_BITS;
+		/* never equals to 0 because of the "
+		( out_ROI_height == 1 )" case */
+		denominator = (uint64_t)(out_ROI_height - 1);
+		/* Quotient is rounded up to the larger
+		29th decimal point. */
+		phase_step_y =
+		(uint32_t) vpe_do_div(
+			(numerator + denominator - 1), denominator);
+	} else if (scale_unit_sel_y == 1) { /* if M/N scalar */
+		/* Calculate the quotient ( src_ROI_height )
+		/ ( out_ROI_height)
+		with u3.29 precision. Quotient is rounded down
+		to the smaller 29th decimal point. */
+		numerator = (uint64_t)(src_ROI_height) <<
+			SCALER_PHASE_BITS;
+		denominator = (uint64_t)(out_ROI_height);
+		phase_step_y = (uint32_t) vpe_do_div(
+			numerator, denominator);
+	}
+
+	/* decide which set of FIR coefficients to use */
+	if (phase_step_x > HAL_MDP_PHASE_STEP_2P50)
+		xscale_filter_sel = 0;
+	else if (phase_step_x > HAL_MDP_PHASE_STEP_1P66)
+		xscale_filter_sel = 1;
+	else if (phase_step_x > HAL_MDP_PHASE_STEP_1P25)
+		xscale_filter_sel = 2;
+	else
+		xscale_filter_sel = 3;
+
+	if (phase_step_y > HAL_MDP_PHASE_STEP_2P50)
+		yscale_filter_sel = 0;
+	else if (phase_step_y > HAL_MDP_PHASE_STEP_1P66)
+		yscale_filter_sel = 1;
+	else if (phase_step_y > HAL_MDP_PHASE_STEP_1P25)
+		yscale_filter_sel = 2;
+	else
+		yscale_filter_sel = 3;
+
+	/* calculate phase init for the x direction */
+
+	/* if using FIR scalar */
+	if (scale_unit_sel_x == 0) {
+		if (out_ROI_width == 1)
+			phase_init_x =
+				(uint32_t) ((src_ROI_width - 1) <<
+							SCALER_PHASE_BITS);
+		else
+			phase_init_x = 0;
+	} else if (scale_unit_sel_x == 1) /* M over N scalar  */
+		phase_init_x = 0;
+
+	/* calculate phase init for the y direction
+	if using FIR scalar */
+	if (scale_unit_sel_y == 0) {
+		if (out_ROI_height == 1)
+			phase_init_y =
+			(uint32_t) ((src_ROI_height -
+						1) << SCALER_PHASE_BITS);
+		else
+			phase_init_y = 0;
+	} else if (scale_unit_sel_y == 1) /* M over N scalar   */
+		phase_init_y = 0;
+
+	CDBG("phase step x = %d, step y = %d.\n",
+		 phase_step_x, phase_step_y);
+	CDBG("phase init x = %d, init y = %d.\n",
+		 phase_init_x, phase_init_y);
+
+	msm_camera_io_w(phase_step_x, vpe_device->vpebase +
+			VPE_SCALE_PHASEX_STEP_OFFSET);
+	msm_camera_io_w(phase_step_y, vpe_device->vpebase +
+			VPE_SCALE_PHASEY_STEP_OFFSET);
+
+	msm_camera_io_w(phase_init_x, vpe_device->vpebase +
+			VPE_SCALE_PHASEX_INIT_OFFSET);
+
+	msm_camera_io_w(phase_init_y, vpe_device->vpebase +
+			VPE_SCALE_PHASEY_INIT_OFFSET);
+
+	return 1;
+}
+
+static int vpe_update_scaler_with_dis(struct video_crop_t *pcrop,
+				struct dis_offset_type *dis_offset)
+{
+	uint32_t out_ROI_width, out_ROI_height;
+	uint32_t src_ROI_width, src_ROI_height;
+
+	uint32_t rc = 0;  /* default to no zoom. */
+	/*
+	* phase_step_x, phase_step_y, phase_init_x and phase_init_y
+	* are represented in fixed-point, unsigned 3.29 format
+	*/
+	uint32_t phase_step_x = 0;
+	uint32_t phase_step_y = 0;
+	uint32_t phase_init_x = 0;
+	uint32_t phase_init_y = 0;
+
+	uint32_t src_roi, temp;
+	int32_t  src_x, src_y, src_xy;
+	uint32_t yscale_filter_sel, xscale_filter_sel;
+	uint32_t scale_unit_sel_x, scale_unit_sel_y;
+	uint64_t numerator, denominator;
+	int32_t  zoom_dis_x, zoom_dis_y;
+
+	CDBG("%s: pcrop->in2_w = %d, pcrop->in2_h = %d\n", __func__,
+		 pcrop->in2_w, pcrop->in2_h);
+	CDBG("%s: pcrop->out2_w = %d, pcrop->out2_h = %d\n", __func__,
+		 pcrop->out2_w, pcrop->out2_h);
+
+	if ((pcrop->in2_w >= pcrop->out2_w) &&
+		(pcrop->in2_h >= pcrop->out2_h)) {
+		CDBG(" =======VPE no zoom needed, DIS is still enabled.\n");
+
+		temp = msm_camera_io_r(vpe_device->vpebase + VPE_OP_MODE_OFFSET)
+		& 0xfffffffc;
+		msm_camera_io_w(temp, vpe_device->vpebase + VPE_OP_MODE_OFFSET);
+
+		/* no zoom, use dis offset directly. */
+		src_xy = dis_offset->dis_offset_y * (1<<16) +
+			dis_offset->dis_offset_x;
+
+		msm_camera_io_w(src_xy,
+			vpe_device->vpebase + VPE_SRC_XY_OFFSET);
+
+		CDBG("vpe_ctrl->in_h_w = 0x%x\n", vpe_ctrl->in_h_w);
+		msm_camera_io_w(vpe_ctrl->in_h_w,
+			vpe_device->vpebase + VPE_SRC_SIZE_OFFSET);
+		return rc;
+	}
+	/* If fall through then scaler is needed.*/
+
+	CDBG("========VPE zoom needed + DIS enabled.\n");
+	/* assumption is both direction need zoom. this can be
+	 improved. */
+	temp = msm_camera_io_r(vpe_device->vpebase +
+					VPE_OP_MODE_OFFSET) | 0x3;
+	msm_camera_io_w(temp, vpe_device->vpebase +
+			VPE_OP_MODE_OFFSET);
+	zoom_dis_x = dis_offset->dis_offset_x *
+		pcrop->in2_w / pcrop->out2_w;
+	zoom_dis_y = dis_offset->dis_offset_y *
+		pcrop->in2_h / pcrop->out2_h;
+
+	src_x = zoom_dis_x + (pcrop->out2_w-pcrop->in2_w)/2;
+	src_y = zoom_dis_y + (pcrop->out2_h-pcrop->in2_h)/2;
+
+	out_ROI_width = vpe_ctrl->out_w;
+	out_ROI_height = vpe_ctrl->out_h;
+
+	src_ROI_width = out_ROI_width * pcrop->in2_w / pcrop->out2_w;
+	src_ROI_height = out_ROI_height * pcrop->in2_h / pcrop->out2_h;
+
+	/* clamp to output size.  This is because along
+	processing, we mostly do truncation, therefore
+	dis_offset tends to be
+	smaller values.  The intention was to make sure that the
+	offset does not exceed margin.   But in the case it could
+	result src_roi bigger, due to subtract a smaller value. */
+	CDBG("src w = 0x%x, h=0x%x, dst w = 0x%x, h =0x%x.\n",
+		src_ROI_width, src_ROI_height, out_ROI_width,
+		out_ROI_height);
+
+	src_roi = (src_ROI_height << 16) + src_ROI_width;
+
+	msm_camera_io_w(src_roi, vpe_device->vpebase + VPE_SRC_SIZE_OFFSET);
+
+	CDBG("src_x = %d, src_y=%d.\n", src_x, src_y);
+
+	src_xy = src_y*(1<<16) + src_x;
+	msm_camera_io_w(src_xy, vpe_device->vpebase +
+			VPE_SRC_XY_OFFSET);
+	CDBG("src_xy = 0x%x, src_roi=0x%x.\n", src_xy, src_roi);
+
+	/* decide whether to use FIR or M/N for scaling */
+	if ((out_ROI_width == 1 && src_ROI_width < 4) ||
+		(src_ROI_width < 4 * out_ROI_width - 3))
+		scale_unit_sel_x = 0;/* use FIR scalar */
+	else
+		scale_unit_sel_x = 1;/* use M/N scalar */
+
+	if ((out_ROI_height == 1 && src_ROI_height < 4) ||
+		(src_ROI_height < 4 * out_ROI_height - 3))
+		scale_unit_sel_y = 0;/* use FIR scalar */
+	else
+		scale_unit_sel_y = 1;/* use M/N scalar */
+	/* calculate phase step for the x direction */
+
+	/* if destination is only 1 pixel wide, the value of
+	phase_step_x is unimportant. Assigning phase_step_x
+	to src ROI width as an arbitrary value. */
+	if (out_ROI_width == 1)
+		phase_step_x = (uint32_t) ((src_ROI_width) <<
+							SCALER_PHASE_BITS);
+	else if (scale_unit_sel_x == 0) { /* if using FIR scalar */
+		/* Calculate the quotient ( src_ROI_width - 1 )
+		/ ( out_ROI_width - 1)with u3.29 precision.
+		Quotient is rounded up to the larger
+		29th decimal point. */
+		numerator =
+			(uint64_t)(src_ROI_width - 1) <<
+			SCALER_PHASE_BITS;
+		/* never equals to 0 because of the "
+		(out_ROI_width == 1 )"*/
+		denominator = (uint64_t)(out_ROI_width - 1);
+		/* divide and round up to the larger 29th
+		decimal point. */
+		phase_step_x = (uint32_t) vpe_do_div(
+			(numerator + denominator - 1), denominator);
+	} else if (scale_unit_sel_x == 1) { /* if M/N scalar */
+		/* Calculate the quotient
+		( src_ROI_width ) / ( out_ROI_width)
+		with u3.29 precision. Quotient is rounded
+		down to the smaller 29th decimal point. */
+		numerator = (uint64_t)(src_ROI_width) <<
+			SCALER_PHASE_BITS;
+		denominator = (uint64_t)(out_ROI_width);
+		phase_step_x =
+			(uint32_t) vpe_do_div(numerator, denominator);
+	}
+	/* calculate phase step for the y direction */
+
+	/* if destination is only 1 pixel wide, the value of
+		phase_step_x is unimportant. Assigning phase_step_x
+		to src ROI width as an arbitrary value. */
+	if (out_ROI_height == 1)
+		phase_step_y =
+		(uint32_t) ((src_ROI_height) << SCALER_PHASE_BITS);
+	else if (scale_unit_sel_y == 0) { /* if FIR scalar */
+		/* Calculate the quotient
+		( src_ROI_height - 1 ) / ( out_ROI_height - 1)
+		with u3.29 precision. Quotient is rounded up to the
+		larger 29th decimal point. */
+		numerator = (uint64_t)(src_ROI_height - 1) <<
+			SCALER_PHASE_BITS;
+		/* never equals to 0 because of the
+		"( out_ROI_height == 1 )" case */
+		denominator = (uint64_t)(out_ROI_height - 1);
+		/* Quotient is rounded up to the larger 29th
+		decimal point. */
+		phase_step_y =
+		(uint32_t) vpe_do_div(
+		(numerator + denominator - 1), denominator);
+	} else if (scale_unit_sel_y == 1) { /* if M/N scalar */
+		/* Calculate the quotient ( src_ROI_height ) / ( out_ROI_height)
+		with u3.29 precision. Quotient is rounded down to the smaller
+		29th decimal point. */
+		numerator = (uint64_t)(src_ROI_height) <<
+			SCALER_PHASE_BITS;
+		denominator = (uint64_t)(out_ROI_height);
+		phase_step_y = (uint32_t) vpe_do_div(
+			numerator, denominator);
+	}
+
+	/* decide which set of FIR coefficients to use */
+	if (phase_step_x > HAL_MDP_PHASE_STEP_2P50)
+		xscale_filter_sel = 0;
+	else if (phase_step_x > HAL_MDP_PHASE_STEP_1P66)
+		xscale_filter_sel = 1;
+	else if (phase_step_x > HAL_MDP_PHASE_STEP_1P25)
+		xscale_filter_sel = 2;
+	else
+		xscale_filter_sel = 3;
+
+	if (phase_step_y > HAL_MDP_PHASE_STEP_2P50)
+		yscale_filter_sel = 0;
+	else if (phase_step_y > HAL_MDP_PHASE_STEP_1P66)
+		yscale_filter_sel = 1;
+	else if (phase_step_y > HAL_MDP_PHASE_STEP_1P25)
+		yscale_filter_sel = 2;
+	else
+		yscale_filter_sel = 3;
+
+	/* calculate phase init for the x direction */
+
+	/* if using FIR scalar */
+	if (scale_unit_sel_x == 0) {
+		if (out_ROI_width == 1)
+			phase_init_x =
+			(uint32_t) ((src_ROI_width - 1) <<
+						SCALER_PHASE_BITS);
+		else
+			phase_init_x = 0;
+
+	} else if (scale_unit_sel_x == 1) /* M over N scalar  */
+		phase_init_x = 0;
+
+	/* calculate phase init for the y direction
+	if using FIR scalar */
+	if (scale_unit_sel_y == 0) {
+		if (out_ROI_height == 1)
+			phase_init_y =
+			(uint32_t) ((src_ROI_height -
+						1) << SCALER_PHASE_BITS);
+		else
+			phase_init_y = 0;
+
+	} else if (scale_unit_sel_y == 1) /* M over N scalar   */
+		phase_init_y = 0;
+
+	CDBG("phase step x = %d, step y = %d.\n",
+		phase_step_x, phase_step_y);
+	CDBG("phase init x = %d, init y = %d.\n",
+		phase_init_x, phase_init_y);
+
+	msm_camera_io_w(phase_step_x, vpe_device->vpebase +
+			VPE_SCALE_PHASEX_STEP_OFFSET);
+
+	msm_camera_io_w(phase_step_y, vpe_device->vpebase +
+			VPE_SCALE_PHASEY_STEP_OFFSET);
+
+	msm_camera_io_w(phase_init_x, vpe_device->vpebase +
+			VPE_SCALE_PHASEX_INIT_OFFSET);
+
+	msm_camera_io_w(phase_init_y, vpe_device->vpebase +
+			VPE_SCALE_PHASEY_INIT_OFFSET);
+
+	return 1;
+}
+
+void msm_send_frame_to_vpe(uint32_t p0_phy_add, uint32_t p1_phy_add,
+		struct timespec *ts, int output_type)
+{
+	uint32_t temp_pyaddr = 0, temp_pcbcraddr = 0;
+
+	CDBG("vpe input, p0_phy_add = 0x%x, p1_phy_add = 0x%x\n",
+		p0_phy_add, p1_phy_add);
+	msm_camera_io_w(p0_phy_add,
+		vpe_device->vpebase + VPE_SRCP0_ADDR_OFFSET);
+	msm_camera_io_w(p1_phy_add,
+		vpe_device->vpebase + VPE_SRCP1_ADDR_OFFSET);
+
+	if (vpe_ctrl->state == VPE_STATE_ACTIVE)
+		CDBG(" =====VPE is busy!!!  Wrong!========\n");
+
+	if (output_type != OUTPUT_TYPE_ST_R)
+		vpe_ctrl->ts = *ts;
+
+	if (output_type == OUTPUT_TYPE_ST_L) {
+		vpe_ctrl->pcbcr_before_dis =
+			msm_camera_io_r(vpe_device->vpebase +
+			VPE_OUTP1_ADDR_OFFSET);
+		temp_pyaddr = msm_camera_io_r(vpe_device->vpebase +
+			VPE_OUTP0_ADDR_OFFSET);
+		temp_pcbcraddr = temp_pyaddr + PAD_TO_2K(vpe_ctrl->out_w *
+			vpe_ctrl->out_h * 2, vpe_ctrl->pad_2k_bool);
+		msm_camera_io_w(temp_pcbcraddr, vpe_device->vpebase +
+			VPE_OUTP1_ADDR_OFFSET);
+	}
+
+	if (vpe_ctrl->dis_en) {
+		/* Changing the VPE output CBCR address,
+		to make Y/CBCR continuous */
+		vpe_ctrl->pcbcr_before_dis =
+			msm_camera_io_r(vpe_device->vpebase +
+			VPE_OUTP1_ADDR_OFFSET);
+		temp_pyaddr = msm_camera_io_r(vpe_device->vpebase +
+			VPE_OUTP0_ADDR_OFFSET);
+		temp_pcbcraddr = temp_pyaddr + vpe_ctrl->pcbcr_dis_offset;
+		msm_camera_io_w(temp_pcbcraddr, vpe_device->vpebase +
+			VPE_OUTP1_ADDR_OFFSET);
+	}
+
+	vpe_ctrl->output_type = output_type;
+	vpe_ctrl->state = VPE_STATE_ACTIVE;
+	vpe_start();
+}
+
+static int vpe_proc_general(struct msm_vpe_cmd *cmd)
+{
+	int rc = 0;
+	uint32_t *cmdp = NULL;
+	struct msm_queue_cmd *qcmd = NULL;
+	struct msm_vpe_buf_info *vpe_buf;
+	int turbo_mode = 0;
+	struct msm_sync *sync = (struct msm_sync *)vpe_ctrl->syncdata;
+	CDBG("vpe_proc_general: cmdID = %s, length = %d\n",
+		vpe_general_cmd[cmd->id], cmd->length);
+	switch (cmd->id) {
+	case VPE_ENABLE:
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto vpe_proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto vpe_proc_general_done;
+		}
+		turbo_mode = *((int *)(cmd->value));
+		rc = turbo_mode ? vpe_enable(VPE_TURBO_MODE_CLOCK_RATE)
+			: vpe_enable(VPE_NORMAL_MODE_CLOCK_RATE);
+		break;
+	case VPE_DISABLE:
+		rc = vpe_disable();
+		break;
+	case VPE_RESET:
+	case VPE_ABORT:
+		rc = vpe_reset();
+		break;
+	case VPE_START:
+		rc = vpe_start();
+		break;
+
+	case VPE_INPUT_PLANE_CFG:
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto vpe_proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto vpe_proc_general_done;
+		}
+		vpe_input_plane_config(cmdp);
+		break;
+
+	case VPE_OPERATION_MODE_CFG:
+		CDBG("cmd->length = %d \n", cmd->length);
+		if (cmd->length != VPE_OPERATION_MODE_CFG_LEN) {
+			rc = -EINVAL;
+			goto vpe_proc_general_done;
+		}
+		cmdp = kmalloc(VPE_OPERATION_MODE_CFG_LEN,
+					GFP_ATOMIC);
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			VPE_OPERATION_MODE_CFG_LEN)) {
+			rc = -EFAULT;
+			goto vpe_proc_general_done;
+		}
+		rc = vpe_operation_config(cmdp);
+		CDBG("rc = %d \n", rc);
+		break;
+
+	case VPE_OUTPUT_PLANE_CFG:
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto vpe_proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto vpe_proc_general_done;
+		}
+		vpe_output_plane_config(cmdp);
+		break;
+
+	case VPE_SCALE_CFG_TYPE:
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto vpe_proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto vpe_proc_general_done;
+		}
+		vpe_update_scale_coef(cmdp);
+		break;
+
+	case VPE_CMD_DIS_OFFSET_CFG: {
+		struct msm_vfe_resp *vdata;
+		/* first get the dis offset and frame id. */
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto vpe_proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+			(void __user *)(cmd->value),
+			cmd->length)) {
+			rc = -EFAULT;
+			goto vpe_proc_general_done;
+		}
+		/* get the offset. */
+		vpe_ctrl->dis_offset = *(struct dis_offset_type *)cmdp;
+		qcmd = msm_dequeue_vpe(&sync->vpe_q, list_vpe_frame);
+		if (!qcmd) {
+			pr_err("%s: no video frame.\n", __func__);
+			kfree(cmdp);
+			return -EAGAIN;
+		}
+		vdata = (struct msm_vfe_resp *)(qcmd->command);
+		vpe_buf = &vdata->vpe_bf;
+		vpe_update_scaler_with_dis(&(vpe_buf->vpe_crop),
+					&(vpe_ctrl->dis_offset));
+
+		msm_send_frame_to_vpe(vpe_buf->p0_phy, vpe_buf->p1_phy,
+						&(vpe_buf->ts), OUTPUT_TYPE_V);
+
+		if (!qcmd || !atomic_read(&qcmd->on_heap)) {
+			kfree(cmdp);
+			return -EAGAIN;
+		}
+		if (!atomic_sub_return(1, &qcmd->on_heap))
+			kfree(qcmd);
+		break;
+	}
+
+	default:
+		break;
+	}
+vpe_proc_general_done:
+	kfree(cmdp);
+	return rc;
+}
+
+static void vpe_addr_convert(struct msm_vpe_phy_info *pinfo,
+	enum vpe_resp_msg type, void *data, void **ext, int32_t *elen)
+{
+	CDBG("In vpe_addr_convert type = %d\n", type);
+	switch (type) {
+	case VPE_MSG_OUTPUT_V:
+		pinfo->output_id = OUTPUT_TYPE_V;
+		break;
+	case VPE_MSG_OUTPUT_ST_R:
+		/* output_id will be used by user space only. */
+		pinfo->output_id = OUTPUT_TYPE_V;
+		break;
+	default:
+		break;
+	} /* switch */
+
+	CDBG("In vpe_addr_convert output_id = %d\n", pinfo->output_id);
+
+	pinfo->p0_phy =
+		((struct vpe_message *)data)->_u.msgOut.p0_Buffer;
+	pinfo->p1_phy =
+		((struct vpe_message *)data)->_u.msgOut.p1_Buffer;
+	*ext  = vpe_ctrl->extdata;
+	*elen = vpe_ctrl->extlen;
+}
+
+void vpe_proc_ops(uint8_t id, void *msg, size_t len)
+{
+	struct msm_vpe_resp *rp;
+
+	rp = vpe_ctrl->resp->vpe_alloc(sizeof(struct msm_vpe_resp),
+		vpe_ctrl->syncdata, GFP_ATOMIC);
+	if (!rp) {
+		CDBG("rp: cannot allocate buffer\n");
+		return;
+	}
+
+	CDBG("vpe_proc_ops, msgId = %d rp->evt_msg.msg_id = %d\n",
+		id, rp->evt_msg.msg_id);
+	rp->evt_msg.type   = MSM_CAMERA_MSG;
+	rp->evt_msg.msg_id = id;
+	rp->evt_msg.len    = len;
+	rp->evt_msg.data   = msg;
+
+	switch (rp->evt_msg.msg_id) {
+	case MSG_ID_VPE_OUTPUT_V:
+		rp->type = VPE_MSG_OUTPUT_V;
+		vpe_addr_convert(&(rp->phy), VPE_MSG_OUTPUT_V,
+			rp->evt_msg.data, &(rp->extdata),
+			&(rp->extlen));
+		break;
+
+	case MSG_ID_VPE_OUTPUT_ST_R:
+		rp->type = VPE_MSG_OUTPUT_ST_R;
+		vpe_addr_convert(&(rp->phy), VPE_MSG_OUTPUT_ST_R,
+			rp->evt_msg.data, &(rp->extdata),
+			&(rp->extlen));
+		break;
+
+	case MSG_ID_VPE_OUTPUT_ST_L:
+		rp->type = VPE_MSG_OUTPUT_ST_L;
+		break;
+
+	default:
+		rp->type = VPE_MSG_GENERAL;
+		break;
+	}
+	CDBG("%s: time = %ld\n",
+			__func__, vpe_ctrl->ts.tv_nsec);
+
+	vpe_ctrl->resp->vpe_resp(rp, MSM_CAM_Q_VPE_MSG,
+					vpe_ctrl->syncdata,
+					&(vpe_ctrl->ts), GFP_ATOMIC);
+}
+
+int vpe_config_axi(struct axidata *ad)
+{
+	uint32_t p1;
+	struct msm_pmem_region *regp1 = NULL;
+	CDBG("vpe_config_axi:bufnum1 = %d.\n", ad->bufnum1);
+
+	if (ad->bufnum1 != 1)
+		return -EINVAL;
+
+	regp1 = &(ad->region[0]);
+	/* for video  Y address */
+	p1 = (regp1->paddr + regp1->info.planar0_off);
+	msm_camera_io_w(p1, vpe_device->vpebase + VPE_OUTP0_ADDR_OFFSET);
+	/* for video  CbCr address */
+	p1 = (regp1->paddr + regp1->info.planar1_off);
+	msm_camera_io_w(p1, vpe_device->vpebase + VPE_OUTP1_ADDR_OFFSET);
+
+	return 0;
+}
+
+int msm_vpe_config(struct msm_vpe_cfg_cmd *cmd, void *data)
+{
+	struct msm_vpe_cmd vpecmd;
+	int rc = 0;
+	if (copy_from_user(&vpecmd,
+			(void __user *)(cmd->value),
+			sizeof(vpecmd))) {
+		pr_err("%s %d: copy_from_user failed\n", __func__,
+				__LINE__);
+		return -EFAULT;
+	}
+	CDBG("%s: cmd_type %d\n", __func__, cmd->cmd_type);
+	switch (cmd->cmd_type) {
+	case CMD_VPE:
+		rc = vpe_proc_general(&vpecmd);
+		CDBG(" rc = %d\n", rc);
+		break;
+
+	case CMD_AXI_CFG_VPE:
+	case CMD_AXI_CFG_SNAP_VPE:
+	case CMD_AXI_CFG_SNAP_THUMB_VPE: {
+		struct axidata *axid;
+		axid = data;
+		if (!axid)
+			return -EFAULT;
+		vpe_config_axi(axid);
+		break;
+	}
+	default:
+		break;
+	}
+	CDBG("%s: rc = %d\n", __func__, rc);
+	return rc;
+}
+
+void msm_vpe_offset_update(int frame_pack, uint32_t pyaddr, uint32_t pcbcraddr,
+	struct timespec *ts, int output_id, struct msm_st_half st_half,
+	int frameid)
+{
+	struct msm_vpe_buf_info vpe_buf;
+	uint32_t input_stride;
+
+	vpe_buf.vpe_crop.in2_w = st_half.stCropInfo.in_w;
+	vpe_buf.vpe_crop.in2_h = st_half.stCropInfo.in_h;
+	vpe_buf.vpe_crop.out2_w = st_half.stCropInfo.out_w;
+	vpe_buf.vpe_crop.out2_h = st_half.stCropInfo.out_h;
+	vpe_ctrl->dis_offset.dis_offset_x = st_half.pix_x_off;
+	vpe_ctrl->dis_offset.dis_offset_y = st_half.pix_y_off;
+	vpe_ctrl->dis_offset.frame_id = frameid;
+	vpe_ctrl->frame_pack = frame_pack;
+	vpe_ctrl->output_type = output_id;
+
+	input_stride = (st_half.buf_p1_stride * (1<<16)) +
+		st_half.buf_p0_stride;
+
+	msm_camera_io_w(input_stride,
+		vpe_device->vpebase + VPE_SRC_YSTRIDE1_OFFSET);
+
+	vpe_update_scaler_with_dis(&(vpe_buf.vpe_crop),
+		&(vpe_ctrl->dis_offset));
+
+	msm_send_frame_to_vpe(pyaddr, pcbcraddr, ts, output_id);
+}
+
+static void vpe_send_outmsg(uint8_t msgid, uint32_t p0_addr,
+	uint32_t p1_addr, uint32_t p2_addr)
+{
+	struct vpe_message msg;
+	uint8_t outid;
+	msg._d = outid = msgid;
+	msg._u.msgOut.output_id   = msgid;
+	msg._u.msgOut.p0_Buffer = p0_addr;
+	msg._u.msgOut.p1_Buffer = p1_addr;
+	msg._u.msgOut.p2_Buffer = p2_addr;
+	vpe_proc_ops(outid, &msg, sizeof(struct vpe_message));
+	return;
+}
+
+int msm_vpe_reg(struct msm_vpe_callback *presp)
+{
+	if (presp && presp->vpe_resp)
+		vpe_ctrl->resp = presp;
+
+	return 0;
+}
+
+static void vpe_send_msg_no_payload(enum VPE_MESSAGE_ID id)
+{
+	struct vpe_message msg;
+
+	CDBG("vfe31_send_msg_no_payload\n");
+	msg._d = id;
+	vpe_proc_ops(id, &msg, 0);
+}
+
+static void vpe_do_tasklet(unsigned long data)
+{
+	unsigned long flags;
+	uint32_t pyaddr = 0, pcbcraddr = 0;
+	uint32_t src_y, src_cbcr, temp;
+
+	struct vpe_isr_queue_cmd_type *qcmd = NULL;
+
+	CDBG("=== vpe_do_tasklet start === \n");
+
+	spin_lock_irqsave(&vpe_ctrl->tasklet_lock, flags);
+	qcmd = list_first_entry(&vpe_ctrl->tasklet_q,
+		struct vpe_isr_queue_cmd_type, list);
+
+	if (!qcmd) {
+		spin_unlock_irqrestore(&vpe_ctrl->tasklet_lock, flags);
+		return;
+	}
+
+	list_del(&qcmd->list);
+	spin_unlock_irqrestore(&vpe_ctrl->tasklet_lock, flags);
+
+	/* interrupt to be processed,  *qcmd has the payload.  */
+	if (qcmd->irq_status & 0x1) {
+		if (vpe_ctrl->output_type == OUTPUT_TYPE_ST_L) {
+			CDBG("vpe left frame done.\n");
+			vpe_ctrl->output_type = 0;
+			CDBG("vpe send out msg.\n");
+			orig_src_y = msm_camera_io_r(vpe_device->vpebase +
+				VPE_SRCP0_ADDR_OFFSET);
+			orig_src_cbcr = msm_camera_io_r(vpe_device->vpebase +
+				VPE_SRCP1_ADDR_OFFSET);
+
+			pyaddr = msm_camera_io_r(vpe_device->vpebase +
+				VPE_OUTP0_ADDR_OFFSET);
+			pcbcraddr = msm_camera_io_r(vpe_device->vpebase +
+				VPE_OUTP1_ADDR_OFFSET);
+			CDBG("%s: out_w = %d, out_h = %d\n", __func__,
+				vpe_ctrl->out_w, vpe_ctrl->out_h);
+
+			if ((vpe_ctrl->frame_pack == TOP_DOWN_FULL) ||
+				(vpe_ctrl->frame_pack == TOP_DOWN_HALF)) {
+				msm_camera_io_w(pyaddr + (vpe_ctrl->out_w *
+					vpe_ctrl->out_h), vpe_device->vpebase +
+					VPE_OUTP0_ADDR_OFFSET);
+				msm_camera_io_w(pcbcraddr + (vpe_ctrl->out_w *
+					vpe_ctrl->out_h/2),
+					vpe_device->vpebase +
+					VPE_OUTP1_ADDR_OFFSET);
+			} else if ((vpe_ctrl->frame_pack ==
+				SIDE_BY_SIDE_HALF) || (vpe_ctrl->frame_pack ==
+				SIDE_BY_SIDE_FULL)) {
+				msm_camera_io_w(pyaddr + vpe_ctrl->out_w,
+					vpe_device->vpebase +
+					VPE_OUTP0_ADDR_OFFSET);
+				msm_camera_io_w(pcbcraddr + vpe_ctrl->out_w,
+					vpe_device->vpebase +
+					VPE_OUTP1_ADDR_OFFSET);
+			} else
+				CDBG("%s: Invalid packing = %d\n", __func__,
+					vpe_ctrl->frame_pack);
+
+			vpe_send_msg_no_payload(MSG_ID_VPE_OUTPUT_ST_L);
+			vpe_ctrl->state = VPE_STATE_INIT;
+			kfree(qcmd);
+			return;
+		} else if (vpe_ctrl->output_type == OUTPUT_TYPE_ST_R) {
+			src_y = orig_src_y;
+			src_cbcr = orig_src_cbcr;
+			CDBG("%s: out_w = %d, out_h = %d\n", __func__,
+				vpe_ctrl->out_w, vpe_ctrl->out_h);
+
+			if ((vpe_ctrl->frame_pack == TOP_DOWN_FULL) ||
+				(vpe_ctrl->frame_pack == TOP_DOWN_HALF)) {
+				pyaddr = msm_camera_io_r(vpe_device->vpebase +
+					VPE_OUTP0_ADDR_OFFSET) -
+					(vpe_ctrl->out_w * vpe_ctrl->out_h);
+			} else if ((vpe_ctrl->frame_pack ==
+				SIDE_BY_SIDE_HALF) || (vpe_ctrl->frame_pack ==
+				SIDE_BY_SIDE_FULL)) {
+				pyaddr = msm_camera_io_r(vpe_device->vpebase +
+				VPE_OUTP0_ADDR_OFFSET) - vpe_ctrl->out_w;
+			} else
+				CDBG("%s: Invalid packing = %d\n", __func__,
+					vpe_ctrl->frame_pack);
+
+			pcbcraddr = vpe_ctrl->pcbcr_before_dis;
+		} else {
+			src_y =	msm_camera_io_r(vpe_device->vpebase +
+				VPE_SRCP0_ADDR_OFFSET);
+			src_cbcr = msm_camera_io_r(vpe_device->vpebase +
+				VPE_SRCP1_ADDR_OFFSET);
+			pyaddr = msm_camera_io_r(vpe_device->vpebase +
+				VPE_OUTP0_ADDR_OFFSET);
+			pcbcraddr = msm_camera_io_r(vpe_device->vpebase +
+				VPE_OUTP1_ADDR_OFFSET);
+		}
+
+		if (vpe_ctrl->dis_en)
+			pcbcraddr = vpe_ctrl->pcbcr_before_dis;
+
+		msm_camera_io_w(src_y,
+				vpe_device->vpebase + VPE_OUTP0_ADDR_OFFSET);
+		msm_camera_io_w(src_cbcr,
+				vpe_device->vpebase + VPE_OUTP1_ADDR_OFFSET);
+
+		temp = msm_camera_io_r(vpe_device->vpebase + VPE_OP_MODE_OFFSET)
+				& 0xFFFFFFFC;
+		msm_camera_io_w(temp, vpe_device->vpebase + VPE_OP_MODE_OFFSET);
+
+		/*  now pass this frame to msm_camera.c. */
+		if (vpe_ctrl->output_type == OUTPUT_TYPE_ST_R) {
+			CDBG("vpe send out R msg.\n");
+			vpe_send_outmsg(MSG_ID_VPE_OUTPUT_ST_R, pyaddr,
+				pcbcraddr, pyaddr);
+		} else if (vpe_ctrl->output_type == OUTPUT_TYPE_V) {
+			CDBG("vpe send out V msg.\n");
+			vpe_send_outmsg(MSG_ID_VPE_OUTPUT_V, pyaddr,
+				pcbcraddr, pyaddr);
+		}
+
+		vpe_ctrl->output_type = 0;
+		vpe_ctrl->state = VPE_STATE_INIT;   /* put it back to idle. */
+
+	}
+	kfree(qcmd);
+}
+DECLARE_TASKLET(vpe_tasklet, vpe_do_tasklet, 0);
+
+static irqreturn_t vpe_parse_irq(int irq_num, void *data)
+{
+	unsigned long flags;
+	uint32_t irq_status = 0;
+	struct vpe_isr_queue_cmd_type *qcmd;
+
+	CDBG("vpe_parse_irq.\n");
+	/* read and clear back-to-back. */
+	irq_status = msm_camera_io_r_mb(vpe_device->vpebase +
+							VPE_INTR_STATUS_OFFSET);
+	msm_camera_io_w_mb(irq_status, vpe_device->vpebase +
+				VPE_INTR_CLEAR_OFFSET);
+
+	msm_camera_io_w(0, vpe_device->vpebase + VPE_INTR_ENABLE_OFFSET);
+
+	if (irq_status == 0) {
+		pr_err("%s: irq_status = 0,Something is wrong!\n", __func__);
+		return IRQ_HANDLED;
+	}
+	irq_status &= 0x1;
+	/* apply mask. only interested in bit 0.  */
+	if (irq_status) {
+		qcmd = kzalloc(sizeof(struct vpe_isr_queue_cmd_type),
+			GFP_ATOMIC);
+		if (!qcmd) {
+			pr_err("%s: qcmd malloc failed!\n", __func__);
+			return IRQ_HANDLED;
+		}
+		/* must be 0x1 now. so in bottom half we don't really
+		need to check. */
+		qcmd->irq_status = irq_status & 0x1;
+		spin_lock_irqsave(&vpe_ctrl->tasklet_lock, flags);
+		list_add_tail(&qcmd->list, &vpe_ctrl->tasklet_q);
+		spin_unlock_irqrestore(&vpe_ctrl->tasklet_lock, flags);
+		tasklet_schedule(&vpe_tasklet);
+	}
+	return IRQ_HANDLED;
+}
+
+static int vpe_enable_irq(void)
+{
+	uint32_t   rc = 0;
+	rc = request_irq(vpe_device->vpeirq,
+				vpe_parse_irq,
+				IRQF_TRIGGER_HIGH, "vpe", 0);
+	return rc;
+}
+
+int msm_vpe_open(void)
+{
+	int rc = 0;
+
+	CDBG("%s: In \n", __func__);
+
+	vpe_ctrl = kzalloc(sizeof(struct vpe_ctrl_type), GFP_KERNEL);
+	if (!vpe_ctrl) {
+		pr_err("%s: no memory!\n", __func__);
+		return -ENOMEM;
+	}
+
+	spin_lock_init(&vpe_ctrl->ops_lock);
+	CDBG("%s: Out\n", __func__);
+
+	return rc;
+}
+
+int msm_vpe_release(void)
+{
+	/* clean up....*/
+	int rc = 0;
+	CDBG("%s: state %d\n", __func__, vpe_ctrl->state);
+	if (vpe_ctrl->state != VPE_STATE_IDLE)
+		rc = vpe_disable();
+
+	kfree(vpe_ctrl);
+	return rc;
+}
+
+
+int vpe_enable(uint32_t clk_rate)
+{
+	int rc = 0;
+	unsigned long flags = 0;
+	/* don't change the order of clock and irq.*/
+	CDBG("%s: enable_clock rate %u\n", __func__, clk_rate);
+	spin_lock_irqsave(&vpe_ctrl->ops_lock, flags);
+	if (vpe_ctrl->state != VPE_STATE_IDLE) {
+		CDBG("%s: VPE already enabled", __func__);
+		spin_unlock_irqrestore(&vpe_ctrl->ops_lock, flags);
+		return 0;
+	}
+	vpe_ctrl->state = VPE_STATE_INIT;
+	spin_unlock_irqrestore(&vpe_ctrl->ops_lock, flags);
+
+	rc = msm_camio_vpe_clk_enable(clk_rate);
+	if (rc < 0) {
+		pr_err("%s: msm_camio_vpe_clk_enable failed", __func__);
+		vpe_ctrl->state = VPE_STATE_IDLE;
+		return rc;
+	}
+
+	CDBG("%s: enable_irq\n", __func__);
+	vpe_enable_irq();
+
+	/* initialize the data structure - lock, queue etc. */
+	spin_lock_init(&vpe_ctrl->tasklet_lock);
+	INIT_LIST_HEAD(&vpe_ctrl->tasklet_q);
+
+	return rc;
+}
+
+int vpe_disable(void)
+{
+	int rc = 0;
+	unsigned long flags = 0;
+	CDBG("%s: called", __func__);
+	spin_lock_irqsave(&vpe_ctrl->ops_lock, flags);
+	if (vpe_ctrl->state == VPE_STATE_IDLE) {
+		CDBG("%s: VPE already disabled", __func__);
+		spin_unlock_irqrestore(&vpe_ctrl->ops_lock, flags);
+		return 0;
+	}
+	vpe_ctrl->state = VPE_STATE_IDLE;
+	spin_unlock_irqrestore(&vpe_ctrl->ops_lock, flags);
+	vpe_ctrl->out_y_addr = msm_camera_io_r(vpe_device->vpebase +
+		VPE_OUTP0_ADDR_OFFSET);
+	vpe_ctrl->out_cbcr_addr = msm_camera_io_r(vpe_device->vpebase +
+		VPE_OUTP1_ADDR_OFFSET);
+	free_irq(vpe_device->vpeirq, 0);
+	tasklet_kill(&vpe_tasklet);
+	rc = msm_camio_vpe_clk_disable();
+	return rc;
+}
+
+static int __msm_vpe_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct resource   *vpemem, *vpeirq, *vpeio;
+	void __iomem      *vpebase;
+
+	/* first allocate */
+
+	vpe_device = &vpe_device_data;
+	memset(vpe_device, 0, sizeof(struct vpe_device_type));
+
+	/* does the device exist? */
+	vpeirq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!vpeirq) {
+		pr_err("%s: no vpe irq resource.\n", __func__);
+		rc = -ENODEV;
+		goto vpe_free_device;
+	}
+	vpemem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!vpemem) {
+		pr_err("%s: no vpe mem resource!\n", __func__);
+		rc = -ENODEV;
+		goto vpe_free_device;
+	}
+	vpeio = request_mem_region(vpemem->start,
+			resource_size(vpemem), pdev->name);
+	if (!vpeio) {
+		pr_err("%s: VPE region already claimed.\n", __func__);
+		rc = -EBUSY;
+		goto vpe_free_device;
+	}
+
+	vpebase =
+		ioremap(vpemem->start,
+				(vpemem->end - vpemem->start) + 1);
+	if (!vpebase) {
+		pr_err("%s: vpe ioremap failed.\n", __func__);
+		rc = -ENOMEM;
+		goto vpe_release_mem_region;
+	}
+
+	/* Fall through, _probe is successful. */
+	vpe_device->vpeirq = vpeirq->start;
+	vpe_device->vpemem = vpemem;
+	vpe_device->vpeio = vpeio;
+	vpe_device->vpebase = vpebase;
+	return rc;  /* this rc should be zero.*/
+
+	iounmap(vpe_device->vpebase);  /* this path should never occur */
+	vpe_device->vpebase = NULL;
+/* from this part it is error handling. */
+vpe_release_mem_region:
+	release_mem_region(vpemem->start, (vpemem->end - vpemem->start) + 1);
+vpe_free_device:
+	return rc;  /* this rc should have error code. */
+}
+
+static int __msm_vpe_remove(struct platform_device *pdev)
+{
+	struct resource	*vpemem;
+	vpemem = vpe_device->vpemem;
+
+	iounmap(vpe_device->vpebase);
+	vpe_device->vpebase = NULL;
+	release_mem_region(vpemem->start,
+					(vpemem->end - vpemem->start) + 1);
+	return 0;
+}
+
+static struct platform_driver msm_vpe_driver = {
+	.probe = __msm_vpe_probe,
+	.remove = __msm_vpe_remove,
+	.driver = {
+		.name = "msm_vpe",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init msm_vpe_init(void)
+{
+	return platform_driver_register(&msm_vpe_driver);
+}
+module_init(msm_vpe_init);
+
+static void __exit msm_vpe_exit(void)
+{
+	platform_driver_unregister(&msm_vpe_driver);
+}
+module_exit(msm_vpe_exit);
+
+MODULE_DESCRIPTION("msm vpe 1.0 driver");
+MODULE_VERSION("msm vpe driver 1.0");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/msm_vpe1.h b/drivers/media/video/msm/msm_vpe1.h
new file mode 100644
index 0000000..f4d328d
--- /dev/null
+++ b/drivers/media/video/msm/msm_vpe1.h
@@ -0,0 +1,254 @@
+/* Copyright (c) 2010, 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _msm_vpe1_h_
+#define _msm_vpe1_h_
+
+#include <mach/camera.h>
+
+/***********  start of register offset *********************/
+#define VPE_INTR_ENABLE_OFFSET                0x0020
+#define VPE_INTR_STATUS_OFFSET                0x0024
+#define VPE_INTR_CLEAR_OFFSET                 0x0028
+#define VPE_DL0_START_OFFSET                  0x0030
+#define VPE_HW_VERSION_OFFSET                 0x0070
+#define VPE_SW_RESET_OFFSET                   0x0074
+#define VPE_AXI_RD_ARB_CONFIG_OFFSET          0x0078
+#define VPE_SEL_CLK_OR_HCLK_TEST_BUS_OFFSET   0x007C
+#define VPE_CGC_EN_OFFSET                     0x0100
+#define VPE_CMD_STATUS_OFFSET                 0x10008
+#define VPE_PROFILE_EN_OFFSET                 0x10010
+#define VPE_PROFILE_COUNT_OFFSET              0x10014
+#define VPE_CMD_MODE_OFFSET                   0x10060
+#define VPE_SRC_SIZE_OFFSET                   0x10108
+#define VPE_SRCP0_ADDR_OFFSET                 0x1010C
+#define VPE_SRCP1_ADDR_OFFSET                 0x10110
+#define VPE_SRC_YSTRIDE1_OFFSET               0x1011C
+#define VPE_SRC_FORMAT_OFFSET                 0x10124
+#define VPE_SRC_UNPACK_PATTERN1_OFFSET        0x10128
+#define VPE_OP_MODE_OFFSET                    0x10138
+#define VPE_SCALE_PHASEX_INIT_OFFSET          0x1013C
+#define VPE_SCALE_PHASEY_INIT_OFFSET          0x10140
+#define VPE_SCALE_PHASEX_STEP_OFFSET          0x10144
+#define VPE_SCALE_PHASEY_STEP_OFFSET          0x10148
+#define VPE_OUT_FORMAT_OFFSET                 0x10150
+#define VPE_OUT_PACK_PATTERN1_OFFSET          0x10154
+#define VPE_OUT_SIZE_OFFSET                   0x10164
+#define VPE_OUTP0_ADDR_OFFSET                 0x10168
+#define VPE_OUTP1_ADDR_OFFSET                 0x1016C
+#define VPE_OUT_YSTRIDE1_OFFSET               0x10178
+#define VPE_OUT_XY_OFFSET                     0x1019C
+#define VPE_SRC_XY_OFFSET                     0x10200
+#define VPE_SRC_IMAGE_SIZE_OFFSET             0x10208
+#define VPE_SCALE_CONFIG_OFFSET               0x10230
+#define VPE_DEINT_STATUS_OFFSET               0x30000
+#define VPE_DEINT_DECISION_OFFSET             0x30004
+#define VPE_DEINT_COEFF0_OFFSET               0x30010
+#define VPE_SCALE_STATUS_OFFSET               0x50000
+#define VPE_SCALE_SVI_PARAM_OFFSET            0x50010
+#define VPE_SCALE_SHARPEN_CFG_OFFSET          0x50020
+#define VPE_SCALE_COEFF_LSP_0_OFFSET          0x50400
+#define VPE_SCALE_COEFF_MSP_0_OFFSET          0x50404
+
+#define VPE_AXI_ARB_2_OFFSET                  0x004C
+
+#define VPE_SCALE_COEFF_LSBn(n)	(0x50400 + 8 * (n))
+#define VPE_SCALE_COEFF_MSBn(n)	(0x50404 + 8 * (n))
+#define VPE_SCALE_COEFF_NUM			32
+
+/*********** end of register offset ********************/
+
+
+#define VPE_HARDWARE_VERSION          0x00080308
+#define VPE_SW_RESET_VALUE            0x00000010  /* bit 4 for PPP*/
+#define VPE_AXI_RD_ARB_CONFIG_VALUE   0x124924
+#define VPE_CMD_MODE_VALUE        0x1
+#define VPE_DEFAULT_OP_MODE_VALUE     0x40FC0004
+#define VPE_CGC_ENABLE_VALUE          0xffff
+#define VPE_DEFAULT_SCALE_CONFIG      0x3c
+
+#define VPE_NORMAL_MODE_CLOCK_RATE   150000000
+#define VPE_TURBO_MODE_CLOCK_RATE   200000000
+/**************************************************/
+/*********** Start of command id ******************/
+/**************************************************/
+enum VPE_CMD_ID_ENUM {
+	VPE_DUMMY_0 = 0,
+	VPE_SET_CLK,
+	VPE_RESET,
+	VPE_START,
+	VPE_ABORT,
+	VPE_OPERATION_MODE_CFG, /* 5 */
+	VPE_INPUT_PLANE_CFG,
+	VPE_OUTPUT_PLANE_CFG,
+	VPE_INPUT_PLANE_UPDATE,
+	VPE_SCALE_CFG_TYPE,
+	VPE_ROTATION_CFG_TYPE, /* 10 */
+	VPE_AXI_OUT_CFG,
+	VPE_CMD_DIS_OFFSET_CFG,
+	VPE_ENABLE,
+	VPE_DISABLE,
+};
+
+/* Length of each command.  In bytes.  (payload only) */
+#define VPE_OPERATION_MODE_CFG_LEN 8
+#define VPE_INPUT_PLANE_CFG_LEN    24
+#define VPE_OUTPUT_PLANE_CFG_LEN   20
+#define VPE_INPUT_PLANE_UPDATE_LEN 12
+#define VPE_SCALER_CONFIG_LEN      260
+#define VPE_DIS_OFFSET_CFG_LEN     12
+/**************************************************/
+/*********** End of command id ********************/
+/**************************************************/
+
+struct msm_vpe_cmd {
+	int32_t  id;
+	uint16_t length;
+	void     *value;
+};
+
+struct vpe_cmd_type {
+	uint16_t id;
+	uint32_t length;
+};
+
+struct vpe_isr_queue_cmd_type {
+	struct list_head            list;
+	uint32_t                    irq_status;
+};
+
+enum VPE_MESSAGE_ID {
+	MSG_ID_VPE_OUTPUT_V = 7, /* To match with that of VFE */
+	MSG_ID_VPE_OUTPUT_ST_L,
+	MSG_ID_VPE_OUTPUT_ST_R,
+};
+
+enum vpe_state {
+	VPE_STATE_IDLE,
+	VPE_STATE_INIT,
+	VPE_STATE_ACTIVE,
+};
+
+struct vpe_device_type {
+	/* device related. */
+	int   vpeirq;
+	void __iomem      *vpebase;
+	struct resource	  *vpemem;
+	struct resource   *vpeio;
+	void        *device_extdata;
+};
+
+struct dis_offset_type {
+	int32_t dis_offset_x;
+	int32_t dis_offset_y;
+	uint32_t frame_id;
+};
+
+struct vpe_ctrl_type {
+	spinlock_t        tasklet_lock;
+	spinlock_t        state_lock;
+	spinlock_t        ops_lock;
+
+	struct list_head  tasklet_q;
+	void              *syncdata;
+	uint16_t          op_mode;
+	void              *extdata;
+	uint32_t          extlen;
+	struct msm_vpe_callback *resp;
+	uint32_t          in_h_w;
+	uint32_t          out_h;  /* this is BEFORE rotation. */
+	uint32_t          out_w;  /* this is BEFORE rotation. */
+	uint32_t          dis_en;
+	struct timespec   ts;
+	struct dis_offset_type   dis_offset;
+	uint32_t          pcbcr_before_dis;
+	uint32_t          pcbcr_dis_offset;
+	int               output_type;
+	int               frame_pack;
+	uint8_t           pad_2k_bool;
+	enum vpe_state    state;
+	unsigned long     out_y_addr;
+	unsigned long     out_cbcr_addr;
+};
+
+/*
+* vpe_input_update
+*
+* Define the parameters for output plane
+*/
+/* this is the dimension of ROI.  width / height. */
+struct vpe_src_size_packed {
+	uint32_t        src_w;
+	uint32_t        src_h;
+};
+
+struct vpe_src_xy_packed {
+	uint32_t        src_x;
+	uint32_t        src_y;
+};
+
+struct vpe_input_plane_update_type {
+	struct vpe_src_size_packed             src_roi_size;
+	/* DIS updates this set. */
+	struct vpe_src_xy_packed               src_roi_offset;
+	/* input address*/
+	uint8_t                         *src_p0_addr;
+	uint8_t                         *src_p1_addr;
+};
+
+struct vpe_msg_stats{
+	uint32_t    buffer;
+	uint32_t    frameCounter;
+};
+
+struct vpe_msg_output {
+	uint8_t   output_id;
+	uint32_t  p0_Buffer;
+	uint32_t  p1_Buffer;
+	uint32_t  p2_Buffer;
+	uint32_t  frameCounter;
+};
+
+struct vpe_message {
+	uint8_t  _d;
+	union {
+		struct vpe_msg_output              msgOut;
+		struct vpe_msg_stats               msgStats;
+	} _u;
+};
+
+#define SCALER_PHASE_BITS 29
+#define HAL_MDP_PHASE_STEP_2P50    0x50000000
+#define HAL_MDP_PHASE_STEP_1P66    0x35555555
+#define HAL_MDP_PHASE_STEP_1P25    0x28000000
+
+struct phase_val_t {
+	int32_t phase_init_x;
+	int32_t phase_init_y;
+	int32_t phase_step_x;
+	int32_t phase_step_y;
+};
+
+extern struct vpe_ctrl_type *vpe_ctrl;
+
+int msm_vpe_open(void);
+int msm_vpe_release(void);
+int msm_vpe_reg(struct msm_vpe_callback *presp);
+void msm_send_frame_to_vpe(uint32_t pyaddr, uint32_t pcbcraddr,
+	struct timespec *ts, int output_id);
+int msm_vpe_config(struct msm_vpe_cfg_cmd *cmd, void *data);
+int msm_vpe_cfg_update(void *pinfo);
+void msm_vpe_offset_update(int frame_pack, uint32_t pyaddr, uint32_t pcbcraddr,
+	struct timespec *ts, int output_id, struct msm_st_half st_half,
+	int frameid);
+#endif /*_msm_vpe1_h_*/
+
diff --git a/drivers/media/video/msm/mt9d112.c b/drivers/media/video/msm/mt9d112.c
new file mode 100644
index 0000000..a7b5156
--- /dev/null
+++ b/drivers/media/video/msm/mt9d112.c
@@ -0,0 +1,845 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <media/msm_camera.h>
+#include <mach/gpio.h>
+#include "mt9d112.h"
+
+/* Micron MT9D112 Registers and their values */
+/* Sensor Core Registers */
+#define  REG_MT9D112_MODEL_ID 0x3000
+#define  MT9D112_MODEL_ID     0x1580
+
+/*  SOC Registers Page 1  */
+#define  REG_MT9D112_SENSOR_RESET     0x301A
+#define  REG_MT9D112_STANDBY_CONTROL  0x3202
+#define  REG_MT9D112_MCU_BOOT         0x3386
+
+#define SENSOR_DEBUG 0
+
+struct mt9d112_work {
+	struct work_struct work;
+};
+
+static struct  mt9d112_work *mt9d112_sensorw;
+static struct  i2c_client *mt9d112_client;
+
+struct mt9d112_ctrl {
+	const struct msm_camera_sensor_info *sensordata;
+};
+
+
+static struct mt9d112_ctrl *mt9d112_ctrl;
+
+static DECLARE_WAIT_QUEUE_HEAD(mt9d112_wait_queue);
+DEFINE_SEMAPHORE(mt9d112_sem);
+static int16_t mt9d112_effect = CAMERA_EFFECT_OFF;
+
+/*=============================================================
+	EXTERNAL DECLARATIONS
+==============================================================*/
+extern struct mt9d112_reg mt9d112_regs;
+
+
+/*=============================================================*/
+
+static int mt9d112_reset(const struct msm_camera_sensor_info *dev)
+{
+	int rc = 0;
+
+	rc = gpio_request(dev->sensor_reset, "mt9d112");
+
+	if (!rc) {
+		rc = gpio_direction_output(dev->sensor_reset, 0);
+		msleep(20);
+		gpio_set_value_cansleep(dev->sensor_reset, 1);
+		msleep(20);
+	}
+
+	return rc;
+}
+
+static int32_t mt9d112_i2c_txdata(unsigned short saddr,
+	unsigned char *txdata, int length)
+{
+	struct i2c_msg msg[] = {
+		{
+			.addr = saddr,
+			.flags = 0,
+			.len = length,
+			.buf = txdata,
+		},
+	};
+
+#if SENSOR_DEBUG
+	if (length == 2)
+		CDBG("msm_io_i2c_w: 0x%04x 0x%04x\n",
+			*(u16 *) txdata, *(u16 *) (txdata + 2));
+	else if (length == 4)
+		CDBG("msm_io_i2c_w: 0x%04x\n", *(u16 *) txdata);
+	else
+		CDBG("msm_io_i2c_w: length = %d\n", length);
+#endif
+	if (i2c_transfer(mt9d112_client->adapter, msg, 1) < 0) {
+		CDBG("mt9d112_i2c_txdata failed\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int32_t mt9d112_i2c_write(unsigned short saddr,
+	unsigned short waddr, unsigned short wdata, enum mt9d112_width width)
+{
+	int32_t rc = -EIO;
+	unsigned char buf[4];
+
+	memset(buf, 0, sizeof(buf));
+	switch (width) {
+	case WORD_LEN: {
+		buf[0] = (waddr & 0xFF00)>>8;
+		buf[1] = (waddr & 0x00FF);
+		buf[2] = (wdata & 0xFF00)>>8;
+		buf[3] = (wdata & 0x00FF);
+
+		rc = mt9d112_i2c_txdata(saddr, buf, 4);
+	}
+		break;
+
+	case BYTE_LEN: {
+		buf[0] = waddr;
+		buf[1] = wdata;
+		rc = mt9d112_i2c_txdata(saddr, buf, 2);
+	}
+		break;
+
+	default:
+		break;
+	}
+
+	if (rc < 0)
+		CDBG(
+		"i2c_write failed, addr = 0x%x, val = 0x%x!\n",
+		waddr, wdata);
+
+	return rc;
+}
+
+static int32_t mt9d112_i2c_write_table(
+	struct mt9d112_i2c_reg_conf const *reg_conf_tbl,
+	int num_of_items_in_table)
+{
+	int i;
+	int32_t rc = -EIO;
+
+	for (i = 0; i < num_of_items_in_table; i++) {
+		rc = mt9d112_i2c_write(mt9d112_client->addr,
+			reg_conf_tbl->waddr, reg_conf_tbl->wdata,
+			reg_conf_tbl->width);
+		if (rc < 0)
+			break;
+		if (reg_conf_tbl->mdelay_time != 0)
+			mdelay(reg_conf_tbl->mdelay_time);
+		reg_conf_tbl++;
+	}
+
+	return rc;
+}
+
+static int mt9d112_i2c_rxdata(unsigned short saddr,
+	unsigned char *rxdata, int length)
+{
+	struct i2c_msg msgs[] = {
+	{
+		.addr   = saddr,
+		.flags = 0,
+		.len   = 2,
+		.buf   = rxdata,
+	},
+	{
+		.addr   = saddr,
+		.flags = I2C_M_RD,
+		.len   = length,
+		.buf   = rxdata,
+	},
+	};
+
+#if SENSOR_DEBUG
+	if (length == 2)
+		CDBG("msm_io_i2c_r: 0x%04x 0x%04x\n",
+			*(u16 *) rxdata, *(u16 *) (rxdata + 2));
+	else if (length == 4)
+		CDBG("msm_io_i2c_r: 0x%04x\n", *(u16 *) rxdata);
+	else
+		CDBG("msm_io_i2c_r: length = %d\n", length);
+#endif
+
+	if (i2c_transfer(mt9d112_client->adapter, msgs, 2) < 0) {
+		CDBG("mt9d112_i2c_rxdata failed!\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int32_t mt9d112_i2c_read(unsigned short   saddr,
+	unsigned short raddr, unsigned short *rdata, enum mt9d112_width width)
+{
+	int32_t rc = 0;
+	unsigned char buf[4];
+
+	if (!rdata)
+		return -EIO;
+
+	memset(buf, 0, sizeof(buf));
+
+	switch (width) {
+	case WORD_LEN: {
+		buf[0] = (raddr & 0xFF00)>>8;
+		buf[1] = (raddr & 0x00FF);
+
+		rc = mt9d112_i2c_rxdata(saddr, buf, 2);
+		if (rc < 0)
+			return rc;
+
+		*rdata = buf[0] << 8 | buf[1];
+	}
+		break;
+
+	default:
+		break;
+	}
+
+	if (rc < 0)
+		CDBG("mt9d112_i2c_read failed!\n");
+
+	return rc;
+}
+
+static int32_t mt9d112_set_lens_roll_off(void)
+{
+	int32_t rc = 0;
+	rc = mt9d112_i2c_write_table(&mt9d112_regs.rftbl[0],
+								 mt9d112_regs.rftbl_size);
+	return rc;
+}
+
+static long mt9d112_reg_init(void)
+{
+	int32_t array_length;
+	int32_t i;
+	long rc;
+
+	/* PLL Setup Start */
+	rc = mt9d112_i2c_write_table(&mt9d112_regs.plltbl[0],
+					mt9d112_regs.plltbl_size);
+
+	if (rc < 0)
+		return rc;
+	/* PLL Setup End   */
+
+	array_length = mt9d112_regs.prev_snap_reg_settings_size;
+
+	/* Configure sensor for Preview mode and Snapshot mode */
+	for (i = 0; i < array_length; i++) {
+		rc = mt9d112_i2c_write(mt9d112_client->addr,
+		  mt9d112_regs.prev_snap_reg_settings[i].register_address,
+		  mt9d112_regs.prev_snap_reg_settings[i].register_value,
+		  WORD_LEN);
+
+		if (rc < 0)
+			return rc;
+	}
+
+	/* Configure for Noise Reduction, Saturation and Aperture Correction */
+	array_length = mt9d112_regs.noise_reduction_reg_settings_size;
+
+	for (i = 0; i < array_length; i++) {
+		rc = mt9d112_i2c_write(mt9d112_client->addr,
+			mt9d112_regs.noise_reduction_reg_settings[i].register_address,
+			mt9d112_regs.noise_reduction_reg_settings[i].register_value,
+			WORD_LEN);
+
+		if (rc < 0)
+			return rc;
+	}
+
+	/* Set Color Kill Saturation point to optimum value */
+	rc =
+	mt9d112_i2c_write(mt9d112_client->addr,
+	0x35A4,
+	0x0593,
+	WORD_LEN);
+	if (rc < 0)
+		return rc;
+
+	rc = mt9d112_i2c_write_table(&mt9d112_regs.stbl[0],
+					mt9d112_regs.stbl_size);
+	if (rc < 0)
+		return rc;
+
+	rc = mt9d112_set_lens_roll_off();
+	if (rc < 0)
+		return rc;
+
+	return 0;
+}
+
+static long mt9d112_set_effect(int mode, int effect)
+{
+	uint16_t reg_addr;
+	uint16_t reg_val;
+	long rc = 0;
+
+	switch (mode) {
+	case SENSOR_PREVIEW_MODE:
+		/* Context A Special Effects */
+		reg_addr = 0x2799;
+		break;
+
+	case SENSOR_RAW_SNAPSHOT_MODE:
+	case SENSOR_SNAPSHOT_MODE:
+		/* Context B Special Effects */
+		reg_addr = 0x279B;
+		break;
+
+	default:
+		reg_addr = 0x2799;
+		break;
+	}
+
+	switch (effect) {
+	case CAMERA_EFFECT_OFF: {
+		reg_val = 0x6440;
+
+		rc = mt9d112_i2c_write(mt9d112_client->addr,
+			0x338C, reg_addr, WORD_LEN);
+		if (rc < 0)
+			return rc;
+
+		rc = mt9d112_i2c_write(mt9d112_client->addr,
+			0x3390, reg_val, WORD_LEN);
+		if (rc < 0)
+			return rc;
+	}
+			break;
+
+	case CAMERA_EFFECT_MONO: {
+		reg_val = 0x6441;
+		rc = mt9d112_i2c_write(mt9d112_client->addr,
+			0x338C, reg_addr, WORD_LEN);
+		if (rc < 0)
+			return rc;
+
+		rc = mt9d112_i2c_write(mt9d112_client->addr,
+			0x3390, reg_val, WORD_LEN);
+		if (rc < 0)
+			return rc;
+	}
+		break;
+
+	case CAMERA_EFFECT_NEGATIVE: {
+		reg_val = 0x6443;
+		rc = mt9d112_i2c_write(mt9d112_client->addr,
+			0x338C, reg_addr, WORD_LEN);
+		if (rc < 0)
+			return rc;
+
+		rc = mt9d112_i2c_write(mt9d112_client->addr,
+			0x3390, reg_val, WORD_LEN);
+		if (rc < 0)
+			return rc;
+	}
+		break;
+
+	case CAMERA_EFFECT_SOLARIZE: {
+		reg_val = 0x6445;
+		rc = mt9d112_i2c_write(mt9d112_client->addr,
+			0x338C, reg_addr, WORD_LEN);
+		if (rc < 0)
+			return rc;
+
+		rc = mt9d112_i2c_write(mt9d112_client->addr,
+			0x3390, reg_val, WORD_LEN);
+		if (rc < 0)
+			return rc;
+	}
+		break;
+
+	case CAMERA_EFFECT_SEPIA: {
+		reg_val = 0x6442;
+		rc = mt9d112_i2c_write(mt9d112_client->addr,
+			0x338C, reg_addr, WORD_LEN);
+		if (rc < 0)
+			return rc;
+
+		rc = mt9d112_i2c_write(mt9d112_client->addr,
+			0x3390, reg_val, WORD_LEN);
+		if (rc < 0)
+			return rc;
+	}
+		break;
+
+	default: {
+		reg_val = 0x6440;
+		rc = mt9d112_i2c_write(mt9d112_client->addr,
+			0x338C, reg_addr, WORD_LEN);
+		if (rc < 0)
+			return rc;
+
+		rc = mt9d112_i2c_write(mt9d112_client->addr,
+			0x3390, reg_val, WORD_LEN);
+		if (rc < 0)
+			return rc;
+
+		return -EINVAL;
+	}
+	}
+	mt9d112_effect = effect;
+	/* Refresh Sequencer */
+	rc = mt9d112_i2c_write(mt9d112_client->addr,
+		0x338C, 0xA103, WORD_LEN);
+	if (rc < 0)
+		return rc;
+
+	rc = mt9d112_i2c_write(mt9d112_client->addr,
+		0x3390, 0x0005, WORD_LEN);
+
+	return rc;
+}
+
+static long mt9d112_set_sensor_mode(int mode)
+{
+	uint16_t clock;
+	long rc = 0;
+
+	switch (mode) {
+	case SENSOR_PREVIEW_MODE:
+		rc =
+			mt9d112_i2c_write(mt9d112_client->addr,
+				0x338C, 0xA20C, WORD_LEN);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9d112_i2c_write(mt9d112_client->addr,
+				0x3390, 0x0004, WORD_LEN);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9d112_i2c_write(mt9d112_client->addr,
+				0x338C, 0xA215, WORD_LEN);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9d112_i2c_write(mt9d112_client->addr,
+				0x3390, 0x0004, WORD_LEN);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9d112_i2c_write(mt9d112_client->addr,
+				0x338C, 0xA20B, WORD_LEN);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9d112_i2c_write(mt9d112_client->addr,
+				0x3390, 0x0000, WORD_LEN);
+		if (rc < 0)
+			return rc;
+
+		clock = 0x23C;
+
+		rc =
+			mt9d112_i2c_write(mt9d112_client->addr,
+				0x341C, clock, WORD_LEN);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9d112_i2c_write(mt9d112_client->addr,
+				0x338C, 0xA103, WORD_LEN);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9d112_i2c_write(mt9d112_client->addr,
+				0x3390, 0x0001, WORD_LEN);
+		if (rc < 0)
+			return rc;
+		mdelay(5);
+
+		break;
+
+	case SENSOR_SNAPSHOT_MODE:
+		/* Switch to lower fps for Snapshot */
+		rc =
+			mt9d112_i2c_write(mt9d112_client->addr,
+				0x341C, 0x0120, WORD_LEN);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9d112_i2c_write(mt9d112_client->addr,
+				0x338C, 0xA120, WORD_LEN);
+		if (rc < 0)
+			return rc;
+
+		msleep(40);/*waiting for the delay of one frame*/
+		rc =
+			mt9d112_i2c_write(mt9d112_client->addr,
+				0x3390, 0x0002, WORD_LEN);
+		if (rc < 0)
+			return rc;
+
+		msleep(80);/*waiting for the delay of two frames*/
+
+		rc =
+			mt9d112_i2c_write(mt9d112_client->addr,
+				0x338C, 0xA103, WORD_LEN);
+		if (rc < 0)
+			return rc;
+
+		msleep(40);/*waiting for the delay of one frame*/
+		rc =
+			mt9d112_i2c_write(mt9d112_client->addr,
+				0x3390, 0x0002, WORD_LEN);
+		if (rc < 0)
+			return rc;
+		break;
+
+	case SENSOR_RAW_SNAPSHOT_MODE:
+		/* Setting the effect to CAMERA_EFFECT_OFF */
+		rc =
+			mt9d112_i2c_write(mt9d112_client->addr,
+				0x338C, 0x279B, WORD_LEN);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9d112_i2c_write(mt9d112_client->addr,
+			0x3390, 0x6440, WORD_LEN);
+		if (rc < 0)
+			return rc;
+		msleep(40);/*waiting for the delay of one frame*/
+		/* Switch to lower fps for Snapshot */
+		rc =
+			mt9d112_i2c_write(mt9d112_client->addr,
+				0x341C, 0x0120, WORD_LEN);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9d112_i2c_write(mt9d112_client->addr,
+				0x338C, 0xA120, WORD_LEN);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9d112_i2c_write(mt9d112_client->addr,
+				0x3390, 0x0002, WORD_LEN);
+		if (rc < 0)
+			return rc;
+		msleep(80);/*waiting for the delay of two frames frame*/
+		rc =
+			mt9d112_i2c_write(mt9d112_client->addr,
+				0x338C, 0xA103, WORD_LEN);
+		if (rc < 0)
+			return rc;
+		msleep(40);/*waiting for the delay of one frame*/
+		rc =
+			mt9d112_i2c_write(mt9d112_client->addr,
+				0x3390, 0x0002, WORD_LEN);
+		if (rc < 0)
+			return rc;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int mt9d112_sensor_init_probe(const struct msm_camera_sensor_info *data)
+{
+	uint16_t model_id = 0;
+	int rc = 0;
+
+	CDBG("init entry \n");
+	rc = mt9d112_reset(data);
+	if (rc < 0) {
+		CDBG("reset failed!\n");
+		goto init_probe_fail;
+	}
+
+	msm_camio_clk_rate_set(24000000);
+	msleep(20);
+
+	/* Micron suggested Power up block Start:
+	* Put MCU into Reset - Stop MCU */
+	rc = mt9d112_i2c_write(mt9d112_client->addr,
+		REG_MT9D112_MCU_BOOT, 0x0501, WORD_LEN);
+	if (rc < 0)
+		goto init_probe_fail;
+
+	/* Pull MCU from Reset - Start MCU */
+	rc = mt9d112_i2c_write(mt9d112_client->addr,
+		REG_MT9D112_MCU_BOOT, 0x0500, WORD_LEN);
+	if (rc < 0)
+		goto init_probe_fail;
+
+	mdelay(5);
+
+	/* Micron Suggested - Power up block */
+	rc = mt9d112_i2c_write(mt9d112_client->addr,
+		REG_MT9D112_SENSOR_RESET, 0x0ACC, WORD_LEN);
+	if (rc < 0)
+		goto init_probe_fail;
+
+	rc = mt9d112_i2c_write(mt9d112_client->addr,
+		REG_MT9D112_STANDBY_CONTROL, 0x0008, WORD_LEN);
+	if (rc < 0)
+		goto init_probe_fail;
+
+	/* FUSED_DEFECT_CORRECTION */
+	rc = mt9d112_i2c_write(mt9d112_client->addr,
+		0x33F4, 0x031D, WORD_LEN);
+	if (rc < 0)
+		goto init_probe_fail;
+
+	mdelay(5);
+
+	/* Micron suggested Power up block End */
+	/* Read the Model ID of the sensor */
+	rc = mt9d112_i2c_read(mt9d112_client->addr,
+		REG_MT9D112_MODEL_ID, &model_id, WORD_LEN);
+	if (rc < 0)
+		goto init_probe_fail;
+
+	CDBG("mt9d112 model_id = 0x%x\n", model_id);
+
+	/* Check if it matches it with the value in Datasheet */
+	if (model_id != MT9D112_MODEL_ID) {
+		rc = -EINVAL;
+		goto init_probe_fail;
+	}
+
+	rc = mt9d112_reg_init();
+	if (rc < 0)
+		goto init_probe_fail;
+
+	return rc;
+
+init_probe_fail:
+	return rc;
+}
+
+int mt9d112_sensor_init(const struct msm_camera_sensor_info *data)
+{
+	int rc = 0;
+
+	mt9d112_ctrl = kzalloc(sizeof(struct mt9d112_ctrl), GFP_KERNEL);
+	if (!mt9d112_ctrl) {
+		CDBG("mt9d112_init failed!\n");
+		rc = -ENOMEM;
+		goto init_done;
+	}
+
+	if (data)
+		mt9d112_ctrl->sensordata = data;
+
+	/* Input MCLK = 24MHz */
+	msm_camio_clk_rate_set(24000000);
+	mdelay(5);
+
+	msm_camio_camif_pad_reg_reset();
+
+	rc = mt9d112_sensor_init_probe(data);
+	if (rc < 0) {
+		CDBG("mt9d112_sensor_init failed!\n");
+		goto init_fail;
+	}
+
+init_done:
+	return rc;
+
+init_fail:
+	kfree(mt9d112_ctrl);
+	return rc;
+}
+
+static int mt9d112_init_client(struct i2c_client *client)
+{
+	/* Initialize the MSM_CAMI2C Chip */
+	init_waitqueue_head(&mt9d112_wait_queue);
+	return 0;
+}
+
+int mt9d112_sensor_config(void __user *argp)
+{
+	struct sensor_cfg_data cfg_data;
+	long   rc = 0;
+
+	if (copy_from_user(&cfg_data,
+			(void *)argp,
+			sizeof(struct sensor_cfg_data)))
+		return -EFAULT;
+
+	/* down(&mt9d112_sem); */
+
+	CDBG("mt9d112_ioctl, cfgtype = %d, mode = %d\n",
+		cfg_data.cfgtype, cfg_data.mode);
+
+		switch (cfg_data.cfgtype) {
+		case CFG_SET_MODE:
+			rc = mt9d112_set_sensor_mode(
+						cfg_data.mode);
+			break;
+
+		case CFG_SET_EFFECT:
+			rc = mt9d112_set_effect(cfg_data.mode,
+						cfg_data.cfg.effect);
+			break;
+
+		case CFG_GET_AF_MAX_STEPS:
+		default:
+			rc = -EINVAL;
+			break;
+		}
+
+	/* up(&mt9d112_sem); */
+
+	return rc;
+}
+
+int mt9d112_sensor_release(void)
+{
+	int rc = 0;
+
+	/* down(&mt9d112_sem); */
+	gpio_set_value_cansleep(mt9d112_ctrl->sensordata->sensor_reset, 0);
+	msleep(20);
+	gpio_free(mt9d112_ctrl->sensordata->sensor_reset);
+	kfree(mt9d112_ctrl);
+	/* up(&mt9d112_sem); */
+
+	return rc;
+}
+
+static int mt9d112_i2c_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	int rc = 0;
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		rc = -ENOTSUPP;
+		goto probe_failure;
+	}
+
+	mt9d112_sensorw =
+		kzalloc(sizeof(struct mt9d112_work), GFP_KERNEL);
+
+	if (!mt9d112_sensorw) {
+		rc = -ENOMEM;
+		goto probe_failure;
+	}
+
+	i2c_set_clientdata(client, mt9d112_sensorw);
+	mt9d112_init_client(client);
+	mt9d112_client = client;
+
+	CDBG("mt9d112_probe succeeded!\n");
+
+	return 0;
+
+probe_failure:
+	kfree(mt9d112_sensorw);
+	mt9d112_sensorw = NULL;
+	CDBG("mt9d112_probe failed!\n");
+	return rc;
+}
+
+static const struct i2c_device_id mt9d112_i2c_id[] = {
+	{ "mt9d112", 0},
+	{ },
+};
+
+static struct i2c_driver mt9d112_i2c_driver = {
+	.id_table = mt9d112_i2c_id,
+	.probe  = mt9d112_i2c_probe,
+	.remove = __exit_p(mt9d112_i2c_remove),
+	.driver = {
+		.name = "mt9d112",
+	},
+};
+
+static int mt9d112_sensor_probe(const struct msm_camera_sensor_info *info,
+				struct msm_sensor_ctrl *s)
+{
+	int rc = i2c_add_driver(&mt9d112_i2c_driver);
+	if (rc < 0 || mt9d112_client == NULL) {
+		rc = -ENOTSUPP;
+		goto probe_done;
+	}
+
+	/* Input MCLK = 24MHz */
+	msm_camio_clk_rate_set(24000000);
+	mdelay(5);
+
+	rc = mt9d112_sensor_init_probe(info);
+	if (rc < 0) {
+		gpio_free(info->sensor_reset);
+		goto probe_done;
+	}
+	s->s_init = mt9d112_sensor_init;
+	s->s_release = mt9d112_sensor_release;
+	s->s_config  = mt9d112_sensor_config;
+	s->s_camera_type = FRONT_CAMERA_2D;
+	s->s_mount_angle  = 0;
+	gpio_set_value_cansleep(info->sensor_reset, 0);
+	msleep(20);
+	gpio_free(info->sensor_reset);
+
+probe_done:
+	CDBG("%s %s:%d\n", __FILE__, __func__, __LINE__);
+	return rc;
+}
+
+static int __mt9d112_probe(struct platform_device *pdev)
+{
+	return msm_camera_drv_start(pdev, mt9d112_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+	.probe = __mt9d112_probe,
+	.driver = {
+		.name = "msm_camera_mt9d112",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init mt9d112_init(void)
+{
+	return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(mt9d112_init);
diff --git a/drivers/media/video/msm/mt9d112.h b/drivers/media/video/msm/mt9d112.h
new file mode 100644
index 0000000..309fcec
--- /dev/null
+++ b/drivers/media/video/msm/mt9d112.h
@@ -0,0 +1,47 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef MT9D112_H
+#define MT9D112_H
+
+#include <linux/types.h>
+#include <mach/camera.h>
+
+extern struct mt9d112_reg mt9d112_regs;
+
+enum mt9d112_width {
+	WORD_LEN,
+	BYTE_LEN
+};
+
+struct mt9d112_i2c_reg_conf {
+	unsigned short waddr;
+	unsigned short wdata;
+	enum mt9d112_width width;
+	unsigned short mdelay_time;
+};
+
+struct mt9d112_reg {
+	const struct register_address_value_pair *prev_snap_reg_settings;
+	uint16_t prev_snap_reg_settings_size;
+	const struct register_address_value_pair *noise_reduction_reg_settings;
+	uint16_t noise_reduction_reg_settings_size;
+	const struct mt9d112_i2c_reg_conf *plltbl;
+	uint16_t plltbl_size;
+	const struct mt9d112_i2c_reg_conf *stbl;
+	uint16_t stbl_size;
+	const struct mt9d112_i2c_reg_conf *rftbl;
+	uint16_t rftbl_size;
+};
+
+#endif /* MT9D112_H */
diff --git a/drivers/media/video/msm/mt9d112_reg.c b/drivers/media/video/msm/mt9d112_reg.c
new file mode 100644
index 0000000..24edaf2
--- /dev/null
+++ b/drivers/media/video/msm/mt9d112_reg.c
@@ -0,0 +1,319 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "mt9d112.h"
+
+
+struct register_address_value_pair const
+preview_snapshot_mode_reg_settings_array[] = {
+	{0x338C, 0x2703},
+	{0x3390, 800},    /* Output Width (P) = 640 */
+	{0x338C, 0x2705},
+	{0x3390, 600},    /* Output Height (P) = 480 */
+	{0x338C, 0x2707},
+	{0x3390, 0x0640}, /* Output Width (S) = 1600 */
+	{0x338C, 0x2709},
+	{0x3390, 0x04B0}, /* Output Height (S) = 1200 */
+	{0x338C, 0x270D},
+	{0x3390, 0x0000}, /* Row Start (P) = 0 */
+	{0x338C, 0x270F},
+	{0x3390, 0x0000}, /* Column Start (P) = 0 */
+	{0x338C, 0x2711},
+	{0x3390, 0x04BD}, /* Row End (P) = 1213 */
+	{0x338C, 0x2713},
+	{0x3390, 0x064D}, /* Column End (P) = 1613 */
+	{0x338C, 0x2715},
+	{0x3390, 0x0000}, /* Extra Delay (P) = 0 */
+	{0x338C, 0x2717},
+	{0x3390, 0x2111}, /* Row Speed (P) = 8465 */
+	{0x338C, 0x2719},
+	{0x3390, 0x046C}, /* Read Mode (P) = 1132 */
+	{0x338C, 0x271B},
+	{0x3390, 0x024F}, /* Sensor_Sample_Time_pck(P) = 591 */
+	{0x338C, 0x271D},
+	{0x3390, 0x0102}, /* Sensor_Fine_Correction(P) = 258 */
+	{0x338C, 0x271F},
+	{0x3390, 0x0279}, /* Sensor_Fine_IT_min(P) = 633 */
+	{0x338C, 0x2721},
+	{0x3390, 0x0155}, /* Sensor_Fine_IT_max_margin(P) = 341 */
+	{0x338C, 0x2723},
+	{0x3390, 659},    /* Frame Lines (P) = 679 */
+	{0x338C, 0x2725},
+	{0x3390, 0x061B}, /* Line Length (P) = 1563 */
+	{0x338C, 0x2727},
+	{0x3390, 0x2020},
+	{0x338C, 0x2729},
+	{0x3390, 0x2020},
+	{0x338C, 0x272B},
+	{0x3390, 0x1020},
+	{0x338C, 0x272D},
+	{0x3390, 0x2007},
+	{0x338C, 0x272F},
+	{0x3390, 0x0004}, /* Row Start(S) = 4 */
+	{0x338C, 0x2731},
+	{0x3390, 0x0004}, /* Column Start(S) = 4 */
+	{0x338C, 0x2733},
+	{0x3390, 0x04BB}, /* Row End(S) = 1211 */
+	{0x338C, 0x2735},
+	{0x3390, 0x064B}, /* Column End(S) = 1611 */
+	{0x338C, 0x2737},
+	{0x3390, 0x04CE}, /* Extra Delay(S) = 1230 */
+	{0x338C, 0x2739},
+	{0x3390, 0x2111}, /* Row Speed(S) = 8465 */
+	{0x338C, 0x273B},
+	{0x3390, 0x0024}, /* Read Mode(S) = 36 */
+	{0x338C, 0x273D},
+	{0x3390, 0x0120}, /* Sensor sample time pck(S) = 288 */
+	{0x338C, 0x2741},
+	{0x3390, 0x0169}, /* Sensor_Fine_IT_min(P) = 361 */
+	{0x338C, 0x2745},
+	{0x3390, 0x04FF}, /* Frame Lines(S) = 1279 */
+	{0x338C, 0x2747},
+	{0x3390, 0x0824}, /* Line Length(S) = 2084 */
+	{0x338C, 0x2751},
+	{0x3390, 0x0000}, /* Crop_X0(P) = 0 */
+	{0x338C, 0x2753},
+	{0x3390, 0x0320}, /* Crop_X1(P) = 800 */
+	{0x338C, 0x2755},
+	{0x3390, 0x0000}, /* Crop_Y0(P) = 0 */
+	{0x338C, 0x2757},
+	{0x3390, 0x0258}, /* Crop_Y1(P) = 600 */
+	{0x338C, 0x275F},
+	{0x3390, 0x0000}, /* Crop_X0(S) = 0 */
+	{0x338C, 0x2761},
+	{0x3390, 0x0640}, /* Crop_X1(S) = 1600 */
+	{0x338C, 0x2763},
+	{0x3390, 0x0000}, /* Crop_Y0(S) = 0 */
+	{0x338C, 0x2765},
+	{0x3390, 0x04B0}, /* Crop_Y1(S) = 1200 */
+	{0x338C, 0x222E},
+	{0x3390, 0x00A0}, /* R9 Step = 160 */
+	{0x338C, 0xA408},
+	{0x3390, 0x001F},
+	{0x338C, 0xA409},
+	{0x3390, 0x0021},
+	{0x338C, 0xA40A},
+	{0x3390, 0x0025},
+	{0x338C, 0xA40B},
+	{0x3390, 0x0027},
+	{0x338C, 0x2411},
+	{0x3390, 0x00A0},
+	{0x338C, 0x2413},
+	{0x3390, 0x00C0},
+	{0x338C, 0x2415},
+	{0x3390, 0x00A0},
+	{0x338C, 0x2417},
+	{0x3390, 0x00C0},
+	{0x338C, 0x2799},
+	{0x3390, 0x6408}, /* MODE_SPEC_EFFECTS(P) */
+	{0x338C, 0x279B},
+	{0x3390, 0x6408}, /* MODE_SPEC_EFFECTS(S) */
+};
+
+static struct register_address_value_pair const
+noise_reduction_reg_settings_array[] = {
+	{0x338C, 0xA76D},
+	{0x3390, 0x0003},
+	{0x338C, 0xA76E},
+	{0x3390, 0x0003},
+	{0x338C, 0xA76F},
+	{0x3390, 0},
+	{0x338C, 0xA770},
+	{0x3390, 21},
+	{0x338C, 0xA771},
+	{0x3390, 37},
+	{0x338C, 0xA772},
+	{0x3390, 63},
+	{0x338C, 0xA773},
+	{0x3390, 100},
+	{0x338C, 0xA774},
+	{0x3390, 128},
+	{0x338C, 0xA775},
+	{0x3390, 151},
+	{0x338C, 0xA776},
+	{0x3390, 169},
+	{0x338C, 0xA777},
+	{0x3390, 186},
+	{0x338C, 0xA778},
+	{0x3390, 199},
+	{0x338C, 0xA779},
+	{0x3390, 210},
+	{0x338C, 0xA77A},
+	{0x3390, 220},
+	{0x338C, 0xA77B},
+	{0x3390, 228},
+	{0x338C, 0xA77C},
+	{0x3390, 234},
+	{0x338C, 0xA77D},
+	{0x3390, 240},
+	{0x338C, 0xA77E},
+	{0x3390, 244},
+	{0x338C, 0xA77F},
+	{0x3390, 248},
+	{0x338C, 0xA780},
+	{0x3390, 252},
+	{0x338C, 0xA781},
+	{0x3390, 255},
+	{0x338C, 0xA782},
+	{0x3390, 0},
+	{0x338C, 0xA783},
+	{0x3390, 21},
+	{0x338C, 0xA784},
+	{0x3390, 37},
+	{0x338C, 0xA785},
+	{0x3390, 63},
+	{0x338C, 0xA786},
+	{0x3390, 100},
+	{0x338C, 0xA787},
+	{0x3390, 128},
+	{0x338C, 0xA788},
+	{0x3390, 151},
+	{0x338C, 0xA789},
+	{0x3390, 169},
+	{0x338C, 0xA78A},
+	{0x3390, 186},
+	{0x338C, 0xA78B},
+	{0x3390, 199},
+	{0x338C, 0xA78C},
+	{0x3390, 210},
+	{0x338C, 0xA78D},
+	{0x3390, 220},
+	{0x338C, 0xA78E},
+	{0x3390, 228},
+	{0x338C, 0xA78F},
+	{0x3390, 234},
+	{0x338C, 0xA790},
+	{0x3390, 240},
+	{0x338C, 0xA791},
+	{0x3390, 244},
+	{0x338C, 0xA793},
+	{0x3390, 252},
+	{0x338C, 0xA794},
+	{0x3390, 255},
+	{0x338C, 0xA103},
+	{0x3390, 6},
+};
+
+static const struct mt9d112_i2c_reg_conf const lens_roll_off_tbl[] = {
+	{ 0x34CE, 0x81A0, WORD_LEN, 0 },
+	{ 0x34D0, 0x6331, WORD_LEN, 0 },
+	{ 0x34D2, 0x3394, WORD_LEN, 0 },
+	{ 0x34D4, 0x9966, WORD_LEN, 0 },
+	{ 0x34D6, 0x4B25, WORD_LEN, 0 },
+	{ 0x34D8, 0x2670, WORD_LEN, 0 },
+	{ 0x34DA, 0x724C, WORD_LEN, 0 },
+	{ 0x34DC, 0xFFFD, WORD_LEN, 0 },
+	{ 0x34DE, 0x00CA, WORD_LEN, 0 },
+	{ 0x34E6, 0x00AC, WORD_LEN, 0 },
+	{ 0x34EE, 0x0EE1, WORD_LEN, 0 },
+	{ 0x34F6, 0x0D87, WORD_LEN, 0 },
+	{ 0x3500, 0xE1F7, WORD_LEN, 0 },
+	{ 0x3508, 0x1CF4, WORD_LEN, 0 },
+	{ 0x3510, 0x1D28, WORD_LEN, 0 },
+	{ 0x3518, 0x1F26, WORD_LEN, 0 },
+	{ 0x3520, 0x2220, WORD_LEN, 0 },
+	{ 0x3528, 0x333D, WORD_LEN, 0 },
+	{ 0x3530, 0x15D9, WORD_LEN, 0 },
+	{ 0x3538, 0xCFB8, WORD_LEN, 0 },
+	{ 0x354C, 0x05FE, WORD_LEN, 0 },
+	{ 0x3544, 0x05F8, WORD_LEN, 0 },
+	{ 0x355C, 0x0596, WORD_LEN, 0 },
+	{ 0x3554, 0x0611, WORD_LEN, 0 },
+	{ 0x34E0, 0x00F2, WORD_LEN, 0 },
+	{ 0x34E8, 0x00A8, WORD_LEN, 0 },
+	{ 0x34F0, 0x0F7B, WORD_LEN, 0 },
+	{ 0x34F8, 0x0CD7, WORD_LEN, 0 },
+	{ 0x3502, 0xFEDB, WORD_LEN, 0 },
+	{ 0x350A, 0x13E4, WORD_LEN, 0 },
+	{ 0x3512, 0x1F2C, WORD_LEN, 0 },
+	{ 0x351A, 0x1D20, WORD_LEN, 0 },
+	{ 0x3522, 0x2422, WORD_LEN, 0 },
+	{ 0x352A, 0x2925, WORD_LEN, 0 },
+	{ 0x3532, 0x1D04, WORD_LEN, 0 },
+	{ 0x353A, 0xFBF2, WORD_LEN, 0 },
+	{ 0x354E, 0x0616, WORD_LEN, 0 },
+	{ 0x3546, 0x0597, WORD_LEN, 0 },
+	{ 0x355E, 0x05CD, WORD_LEN, 0 },
+	{ 0x3556, 0x0529, WORD_LEN, 0 },
+	{ 0x34E4, 0x00B2, WORD_LEN, 0 },
+	{ 0x34EC, 0x005E, WORD_LEN, 0 },
+	{ 0x34F4, 0x0F43, WORD_LEN, 0 },
+	{ 0x34FC, 0x0E2F, WORD_LEN, 0 },
+	{ 0x3506, 0xF9FC, WORD_LEN, 0 },
+	{ 0x350E, 0x0CE4, WORD_LEN, 0 },
+	{ 0x3516, 0x1E1E, WORD_LEN, 0 },
+	{ 0x351E, 0x1B19, WORD_LEN, 0 },
+	{ 0x3526, 0x151B, WORD_LEN, 0 },
+	{ 0x352E, 0x1416, WORD_LEN, 0 },
+	{ 0x3536, 0x10FC, WORD_LEN, 0 },
+	{ 0x353E, 0xC018, WORD_LEN, 0 },
+	{ 0x3552, 0x06B4, WORD_LEN, 0 },
+	{ 0x354A, 0x0506, WORD_LEN, 0 },
+	{ 0x3562, 0x06AB, WORD_LEN, 0 },
+	{ 0x355A, 0x063A, WORD_LEN, 0 },
+	{ 0x34E2, 0x00E5, WORD_LEN, 0 },
+	{ 0x34EA, 0x008B, WORD_LEN, 0 },
+	{ 0x34F2, 0x0E4C, WORD_LEN, 0 },
+	{ 0x34FA, 0x0CA3, WORD_LEN, 0 },
+	{ 0x3504, 0x0907, WORD_LEN, 0 },
+	{ 0x350C, 0x1DFD, WORD_LEN, 0 },
+	{ 0x3514, 0x1E24, WORD_LEN, 0 },
+	{ 0x351C, 0x2529, WORD_LEN, 0 },
+	{ 0x3524, 0x1D20, WORD_LEN, 0 },
+	{ 0x352C, 0x2332, WORD_LEN, 0 },
+	{ 0x3534, 0x10E9, WORD_LEN, 0 },
+	{ 0x353C, 0x0BCB, WORD_LEN, 0 },
+	{ 0x3550, 0x04EF, WORD_LEN, 0 },
+	{ 0x3548, 0x0609, WORD_LEN, 0 },
+	{ 0x3560, 0x0580, WORD_LEN, 0 },
+	{ 0x3558, 0x05DD, WORD_LEN, 0 },
+	{ 0x3540, 0x0000, WORD_LEN, 0 },
+	{ 0x3542, 0x0000, WORD_LEN, 0 }
+};
+
+static const struct mt9d112_i2c_reg_conf const pll_setup_tbl[] = {
+	{ 0x341E, 0x8F09, WORD_LEN, 0 },
+	{ 0x341C, 0x0250, WORD_LEN, 0 },
+	{ 0x341E, 0x8F09, WORD_LEN, 5 },
+	{ 0x341E, 0x8F08, WORD_LEN, 0 }
+};
+
+/* Refresh Sequencer */
+static const struct mt9d112_i2c_reg_conf const sequencer_tbl[] = {
+	{ 0x338C, 0x2799, WORD_LEN, 0},
+	{ 0x3390, 0x6440, WORD_LEN, 5},
+	{ 0x338C, 0x279B, WORD_LEN, 0},
+	{ 0x3390, 0x6440, WORD_LEN, 5},
+	{ 0x338C, 0xA103, WORD_LEN, 0},
+	{ 0x3390, 0x0005, WORD_LEN, 5},
+	{ 0x338C, 0xA103, WORD_LEN, 0},
+	{ 0x3390, 0x0006, WORD_LEN, 5}
+};
+
+struct mt9d112_reg mt9d112_regs = {
+	.prev_snap_reg_settings = &preview_snapshot_mode_reg_settings_array[0],
+	.prev_snap_reg_settings_size = ARRAY_SIZE(
+		preview_snapshot_mode_reg_settings_array),
+	.noise_reduction_reg_settings = &noise_reduction_reg_settings_array[0],
+	.noise_reduction_reg_settings_size = ARRAY_SIZE(
+		noise_reduction_reg_settings_array),
+	.plltbl = pll_setup_tbl,
+	.plltbl_size = ARRAY_SIZE(pll_setup_tbl),
+	.stbl = sequencer_tbl,
+	.stbl_size = ARRAY_SIZE(sequencer_tbl),
+	.rftbl = lens_roll_off_tbl,
+	.rftbl_size = ARRAY_SIZE(lens_roll_off_tbl)
+};
+
+
+
diff --git a/drivers/media/video/msm/mt9d113.c b/drivers/media/video/msm/mt9d113.c
new file mode 100644
index 0000000..8e81bda
--- /dev/null
+++ b/drivers/media/video/msm/mt9d113.c
@@ -0,0 +1,656 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <media/msm_camera.h>
+#include <mach/gpio.h>
+#include "mt9d113.h"
+
+/* Micron MT9D113 Registers and their values */
+#define  REG_MT9D113_MODEL_ID	0x0000
+#define  MT9D113_MODEL_ID		0x2580
+#define Q8						0x00000100
+
+struct mt9d113_work {
+	struct work_struct work;
+};
+
+static struct  mt9d113_work *mt9d113_sensorw;
+static struct  i2c_client *mt9d113_client;
+
+struct mt9d113_ctrl {
+	const struct msm_camera_sensor_info *sensordata;
+	uint32_t sensormode;
+	uint32_t fps_divider;/* init to 1 * 0x00000400 */
+	uint32_t pict_fps_divider;/* init to 1 * 0x00000400 */
+	uint16_t fps;
+	uint16_t curr_step_pos;
+	uint16_t my_reg_gain;
+	uint32_t my_reg_line_count;
+	uint16_t total_lines_per_frame;
+	uint16_t config_csi;
+	enum mt9d113_resolution_t prev_res;
+	enum mt9d113_resolution_t pict_res;
+	enum mt9d113_resolution_t curr_res;
+	enum mt9d113_test_mode_t  set_test;
+};
+
+static struct mt9d113_ctrl *mt9d113_ctrl;
+
+static DECLARE_WAIT_QUEUE_HEAD(mt9d113_wait_queue);
+DEFINE_MUTEX(mt9d113_mut);
+
+static int mt9d113_i2c_rxdata(unsigned short saddr,
+				unsigned char *rxdata, int length)
+{
+	struct i2c_msg msgs[] = {
+		{
+			.addr   = saddr,
+			.flags = 0,
+			.len   = 2,
+			.buf   = rxdata,
+		},
+		{
+			.addr   = saddr,
+			.flags = I2C_M_RD,
+			.len   = length,
+			.buf   = rxdata,
+		},
+	};
+	if (i2c_transfer(mt9d113_client->adapter, msgs, 2) < 0) {
+		CDBG("mt9d113_i2c_rxdata failed!\n");
+		return -EIO;
+	}
+	return 0;
+}
+
+static int32_t mt9d113_i2c_read(unsigned short   saddr,
+				unsigned short raddr,
+				unsigned short *rdata,
+				enum mt9d113_width width)
+{
+	int32_t rc = 0;
+	unsigned char buf[4];
+	if (!rdata)
+		return -EIO;
+	memset(buf, 0, sizeof(buf));
+	switch (width) {
+	case WORD_LEN: {
+			buf[0] = (raddr & 0xFF00)>>8;
+			buf[1] = (raddr & 0x00FF);
+			rc = mt9d113_i2c_rxdata(saddr, buf, 2);
+			if (rc < 0)
+				return rc;
+			*rdata = buf[0] << 8 | buf[1];
+		}
+		break;
+	default:
+		break;
+	}
+	if (rc < 0)
+		CDBG("mt9d113_i2c_read failed !\n");
+	return rc;
+}
+
+static int32_t mt9d113_i2c_txdata(unsigned short saddr,
+				unsigned char *txdata, int length)
+{
+	struct i2c_msg msg[] = {
+		{
+			.addr = saddr,
+			.flags = 0,
+			.len = length,
+			.buf = txdata,
+		},
+	};
+	if (i2c_transfer(mt9d113_client->adapter, msg, 1) < 0) {
+		CDBG("mt9d113_i2c_txdata failed\n");
+		return -EIO;
+	}
+	return 0;
+}
+
+static int32_t mt9d113_i2c_write(unsigned short saddr,
+				unsigned short waddr,
+				unsigned short wdata,
+				enum mt9d113_width width)
+{
+	int32_t rc = -EIO;
+	unsigned char buf[4];
+	memset(buf, 0, sizeof(buf));
+	switch (width) {
+	case WORD_LEN: {
+			buf[0] = (waddr & 0xFF00)>>8;
+			buf[1] = (waddr & 0x00FF);
+			buf[2] = (wdata & 0xFF00)>>8;
+			buf[3] = (wdata & 0x00FF);
+			rc = mt9d113_i2c_txdata(saddr, buf, 4);
+		}
+		break;
+	case BYTE_LEN: {
+			buf[0] = waddr;
+			buf[1] = wdata;
+			rc = mt9d113_i2c_txdata(saddr, buf, 2);
+		}
+		break;
+	default:
+		break;
+	}
+	if (rc < 0)
+		printk(KERN_ERR
+			"i2c_write failed, addr = 0x%x, val = 0x%x!\n",
+			waddr, wdata);
+	return rc;
+}
+
+static int32_t mt9d113_i2c_write_table(
+				struct mt9d113_i2c_reg_conf
+				const *reg_conf_tbl,
+				int num_of_items_in_table)
+{
+	int i;
+	int32_t rc = -EIO;
+	for (i = 0; i < num_of_items_in_table; i++) {
+		rc = mt9d113_i2c_write(mt9d113_client->addr,
+				reg_conf_tbl->waddr, reg_conf_tbl->wdata,
+				WORD_LEN);
+		if (rc < 0)
+			break;
+		reg_conf_tbl++;
+	}
+	return rc;
+}
+
+static long mt9d113_reg_init(void)
+{
+	uint16_t data = 0;
+	int32_t rc = 0;
+	int count = 0;
+	struct msm_camera_csi_params mt9d113_csi_params;
+	if (!mt9d113_ctrl->config_csi) {
+		mt9d113_csi_params.lane_cnt = 1;
+		mt9d113_csi_params.data_format = CSI_8BIT;
+		mt9d113_csi_params.lane_assign = 0xe4;
+		mt9d113_csi_params.dpcm_scheme = 0;
+		mt9d113_csi_params.settle_cnt = 0x14;
+		rc = msm_camio_csi_config(&mt9d113_csi_params);
+		mt9d113_ctrl->config_csi = 1;
+		msleep(50);
+	}
+	/* Disable parallel and enable mipi*/
+	rc = mt9d113_i2c_write(mt9d113_client->addr,
+				0x001A,
+				0x0051, WORD_LEN);
+	rc = mt9d113_i2c_write(mt9d113_client->addr,
+				0x001A,
+				0x0050,
+				WORD_LEN);
+	msleep(20);
+	rc = mt9d113_i2c_write(mt9d113_client->addr,
+				0x001A,
+				0x0058,
+				WORD_LEN);
+
+	/* Preset pll settings begin*/
+	rc = mt9d113_i2c_write_table(&mt9d113_regs.pll_tbl[0],
+				mt9d113_regs.pll_tbl_size);
+	if (rc < 0)
+		return rc;
+	rc = mt9d113_i2c_read(mt9d113_client->addr,
+				0x0014, &data, WORD_LEN);
+	data = data&0x8000;
+	/* Poll*/
+	while (data == 0x0000) {
+		data = 0;
+		rc = mt9d113_i2c_read(mt9d113_client->addr,
+				0x0014, &data, WORD_LEN);
+		data = data & 0x8000;
+		usleep_range(11000, 12000);
+		count++;
+		if (count == 100) {
+			CDBG(" Timeout:1\n");
+			break;
+		}
+	}
+	rc = mt9d113_i2c_write(mt9d113_client->addr,
+				0x0014,
+				0x20FA,
+				WORD_LEN);
+
+	/*Preset pll Ends*/
+	mt9d113_i2c_write(mt9d113_client->addr,
+				0x0018,
+				0x402D,
+				WORD_LEN);
+
+	mt9d113_i2c_write(mt9d113_client->addr,
+				0x0018,
+				0x402C,
+				WORD_LEN);
+	/*POLL_REG=0x0018,0x4000,!=0x0000,DELAY=10,TIMEOUT=100*/
+	data = 0;
+	rc = mt9d113_i2c_read(mt9d113_client->addr,
+		0x0018, &data, WORD_LEN);
+	data = data & 0x4000;
+	count = 0;
+	while (data != 0x0000) {
+		rc = mt9d113_i2c_read(mt9d113_client->addr,
+			0x0018, &data, WORD_LEN);
+		data = data & 0x4000;
+		CDBG(" data is %d\n" , data);
+		usleep_range(11000, 12000);
+		count++;
+		if (count == 100) {
+			CDBG(" Loop2 timeout: MT9D113\n");
+			break;
+		}
+		CDBG(" Not streaming\n");
+	}
+	CDBG("MT9D113: Start stream\n");
+	/*Preset Register Wizard Conf*/
+	rc = mt9d113_i2c_write_table(&mt9d113_regs.register_tbl[0],
+				mt9d113_regs.register_tbl_size);
+	if (rc < 0)
+		return rc;
+	rc = mt9d113_i2c_write_table(&mt9d113_regs.err_tbl[0],
+				mt9d113_regs.err_tbl_size);
+	if (rc < 0)
+		return rc;
+	rc = mt9d113_i2c_write_table(&mt9d113_regs.eeprom_tbl[0],
+				mt9d113_regs.eeprom_tbl_size);
+	if (rc < 0)
+		return rc;
+
+	rc = mt9d113_i2c_write_table(&mt9d113_regs.low_light_tbl[0],
+				mt9d113_regs.low_light_tbl_size);
+	if (rc < 0)
+		return rc;
+
+	rc = mt9d113_i2c_write_table(&mt9d113_regs.awb_tbl[0],
+				mt9d113_regs.awb_tbl_size);
+	if (rc < 0)
+		return rc;
+
+	rc = mt9d113_i2c_write_table(&mt9d113_regs.patch_tbl[0],
+				mt9d113_regs.patch_tbl_size);
+	if (rc < 0)
+		return rc;
+
+	/*check patch load*/
+	mt9d113_i2c_write(mt9d113_client->addr,
+				0x098C,
+				0xA024,
+				WORD_LEN);
+	count = 0;
+	/*To check if patch is loaded properly
+	poll the register 0x990 till the condition is
+	met or till the timeout*/
+	data = 0;
+	rc = mt9d113_i2c_read(mt9d113_client->addr,
+				0x0990, &data, WORD_LEN);
+	while (data == 0) {
+		data = 0;
+		rc = mt9d113_i2c_read(mt9d113_client->addr,
+				0x0990, &data, WORD_LEN);
+		usleep_range(11000, 12000);
+		count++;
+		if (count == 100) {
+			CDBG("Timeout in patch loading\n");
+			break;
+		}
+	}
+		/*BITFIELD=0x0018, 0x0004, 0*/
+	/*Preset continue begin */
+	rc = mt9d113_i2c_write(mt9d113_client->addr, 0x0018, 0x0028,
+				WORD_LEN);
+	CDBG(" mt9d113 wait for seq done\n");
+	/* syncronize the FW with the sensor
+	MCU_ADDRESS [SEQ_CMD]*/
+	rc = mt9d113_i2c_write(mt9d113_client->addr,
+				0x098C, 0xA103, WORD_LEN);
+	rc = mt9d113_i2c_write(mt9d113_client->addr,
+				0x0990, 0x0006, WORD_LEN);
+		/*mt9d113 wait for seq done
+	 syncronize the FW with the sensor */
+	msleep(20);
+	/*Preset continue end */
+	CDBG(" MT9D113: Preset continue end\n");
+	rc = mt9d113_i2c_write(mt9d113_client->addr,
+				0x0012,
+				0x00F5,
+				WORD_LEN);
+	/*continue begin */
+	CDBG(" MT9D113: Preset continue begin\n");
+	rc = mt9d113_i2c_write(mt9d113_client->addr, 0x0018, 0x0028 ,
+				WORD_LEN);
+	/*mt9d113 wait for seq done
+	 syncronize the FW with the sensor
+	MCU_ADDRESS [SEQ_CMD]*/
+	msleep(20);
+	rc = mt9d113_i2c_write(mt9d113_client->addr,
+				0x098C, 0xA103, WORD_LEN);
+	/* MCU DATA */
+	rc = mt9d113_i2c_write(mt9d113_client->addr, 0x0990,
+				0x0006, WORD_LEN);
+	/*mt9d113 wait for seq done
+	syncronize the FW with the sensor */
+	/* MCU_ADDRESS [SEQ_CMD]*/
+	msleep(20);
+	/*Preset continue end*/
+	return rc;
+
+}
+
+static long mt9d113_set_sensor_mode(int mode)
+{
+	long rc = 0;
+	switch (mode) {
+	case SENSOR_PREVIEW_MODE:
+		rc = mt9d113_reg_init();
+		CDBG("MT9D113: configure to preview begin\n");
+		rc =
+		mt9d113_i2c_write(mt9d113_client->addr,
+						0x098C, 0xA115, WORD_LEN);
+		if (rc < 0)
+			return rc;
+		rc =
+		mt9d113_i2c_write(mt9d113_client->addr,
+						0x0990, 0x0000, WORD_LEN);
+		if (rc < 0)
+			return rc;
+		rc =
+		mt9d113_i2c_write(mt9d113_client->addr,
+						0x098C, 0xA103, WORD_LEN);
+		if (rc < 0)
+			return rc;
+		rc =
+		mt9d113_i2c_write(mt9d113_client->addr,
+						0x0990, 0x0001, WORD_LEN);
+		if (rc < 0)
+			return rc;
+		break;
+	case SENSOR_SNAPSHOT_MODE:
+	case SENSOR_RAW_SNAPSHOT_MODE:
+		rc =
+		mt9d113_i2c_write(mt9d113_client->addr,
+						0x098C, 0xA115, WORD_LEN);
+		rc =
+		mt9d113_i2c_write(mt9d113_client->addr,
+						0x0990, 0x0002, WORD_LEN);
+		rc =
+		mt9d113_i2c_write(mt9d113_client->addr,
+						0x098C, 0xA103, WORD_LEN);
+		rc =
+		mt9d113_i2c_write(mt9d113_client->addr,
+						0x0990, 0x0002, WORD_LEN);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int mt9d113_sensor_init_probe(const struct
+				msm_camera_sensor_info * data)
+{
+	uint16_t model_id = 0;
+	int rc = 0;
+	/* Read the Model ID of the sensor */
+	rc = mt9d113_i2c_read(mt9d113_client->addr,
+						REG_MT9D113_MODEL_ID,
+						&model_id, WORD_LEN);
+	if (rc < 0)
+		goto init_probe_fail;
+	/* Check if it matches it with the value in Datasheet */
+	if (model_id != MT9D113_MODEL_ID)
+		printk(KERN_INFO "mt9d113 model_id = 0x%x\n", model_id);
+	if (rc < 0)
+		goto init_probe_fail;
+	return rc;
+init_probe_fail:
+	printk(KERN_INFO "probe fail\n");
+	return rc;
+}
+
+static int mt9d113_init_client(struct i2c_client *client)
+{
+	/* Initialize the MSM_CAMI2C Chip */
+	init_waitqueue_head(&mt9d113_wait_queue);
+	return 0;
+}
+
+int mt9d113_sensor_config(void __user *argp)
+{
+	struct sensor_cfg_data cfg_data;
+	long rc = 0;
+
+	if (copy_from_user(&cfg_data,
+					(void *)argp,
+					(sizeof(struct sensor_cfg_data))))
+		return -EFAULT;
+	mutex_lock(&mt9d113_mut);
+	CDBG("mt9d113_ioctl, cfgtype = %d, mode = %d\n",
+		 cfg_data.cfgtype, cfg_data.mode);
+	switch (cfg_data.cfgtype) {
+	case CFG_SET_MODE:
+		rc = mt9d113_set_sensor_mode(
+						cfg_data.mode);
+		break;
+	case CFG_SET_EFFECT:
+		return rc;
+	case CFG_GET_AF_MAX_STEPS:
+	default:
+		rc = -EINVAL;
+		break;
+	}
+	mutex_unlock(&mt9d113_mut);
+	return rc;
+}
+
+int mt9d113_sensor_release(void)
+{
+	int rc = 0;
+
+	mutex_lock(&mt9d113_mut);
+	gpio_set_value_cansleep(mt9d113_ctrl->sensordata->sensor_reset, 0);
+	msleep(20);
+	gpio_free(mt9d113_ctrl->sensordata->sensor_reset);
+	kfree(mt9d113_ctrl);
+	mutex_unlock(&mt9d113_mut);
+
+	return rc;
+}
+
+static int mt9d113_probe_init_done(const struct msm_camera_sensor_info
+				*data)
+{
+	gpio_free(data->sensor_reset);
+	return 0;
+}
+
+static int mt9d113_probe_init_sensor(const struct msm_camera_sensor_info
+				*data)
+{
+	int32_t rc = 0;
+	uint16_t chipid = 0;
+
+	rc = gpio_request(data->sensor_reset, "mt9d113");
+	printk(KERN_INFO " mt9d113_probe_init_sensor\n");
+	if (!rc) {
+		printk(KERN_INFO "sensor_reset = %d\n", rc);
+		gpio_direction_output(data->sensor_reset, 0);
+		usleep_range(11000, 12000);
+		gpio_set_value_cansleep(data->sensor_reset, 1);
+		usleep_range(11000, 12000);
+	} else
+		goto init_probe_done;
+	printk(KERN_INFO " mt9d113_probe_init_sensor called\n");
+	rc = mt9d113_i2c_read(mt9d113_client->addr, REG_MT9D113_MODEL_ID,
+						&chipid, WORD_LEN);
+	if (rc < 0)
+		goto init_probe_fail;
+	/*Compare sensor ID to MT9D113 ID: */
+	if (chipid != MT9D113_MODEL_ID) {
+		printk(KERN_INFO "mt9d113_probe_init_sensor chip id is%d\n",
+			chipid);
+	}
+	CDBG("mt9d113_probe_init_sensor Success\n");
+	goto init_probe_done;
+init_probe_fail:
+	CDBG(" ov2720_probe_init_sensor fails\n");
+	gpio_set_value_cansleep(data->sensor_reset, 0);
+	mt9d113_probe_init_done(data);
+init_probe_done:
+	printk(KERN_INFO " mt9d113_probe_init_sensor finishes\n");
+	return rc;
+}
+
+static int mt9d113_i2c_probe(struct i2c_client *client,
+				const struct i2c_device_id *id)
+{
+	int rc = 0;
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		rc = -ENOTSUPP;
+		goto probe_failure;
+	}
+	mt9d113_sensorw =
+	kzalloc(sizeof(struct mt9d113_work), GFP_KERNEL);
+	if (!mt9d113_sensorw) {
+		rc = -ENOMEM;
+		goto probe_failure;
+	}
+	i2c_set_clientdata(client, mt9d113_sensorw);
+	mt9d113_init_client(client);
+	mt9d113_client = client;
+	CDBG("mt9d113_probe succeeded!\n");
+	return 0;
+probe_failure:
+	kfree(mt9d113_sensorw);
+	mt9d113_sensorw = NULL;
+	CDBG("mt9d113_probe failed!\n");
+	return rc;
+}
+
+static const struct i2c_device_id mt9d113_i2c_id[] = {
+	{ "mt9d113", 0},
+	{},
+};
+
+static struct i2c_driver mt9d113_i2c_driver = {
+	.id_table = mt9d113_i2c_id,
+	.probe  = mt9d113_i2c_probe,
+	.remove = __exit_p(mt9d113_i2c_remove),
+			  .driver = {
+		.name = "mt9d113",
+	},
+};
+
+int mt9d113_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+	int32_t rc = 0;
+	mt9d113_ctrl = kzalloc(sizeof(struct mt9d113_ctrl), GFP_KERNEL);
+	if (!mt9d113_ctrl) {
+		printk(KERN_INFO "mt9d113_init failed!\n");
+		rc = -ENOMEM;
+		goto init_done;
+	}
+	mt9d113_ctrl->fps_divider = 1 * 0x00000400;
+	mt9d113_ctrl->pict_fps_divider = 1 * 0x00000400;
+	mt9d113_ctrl->set_test = TEST_OFF;
+	mt9d113_ctrl->config_csi = 0;
+	mt9d113_ctrl->prev_res = QTR_SIZE;
+	mt9d113_ctrl->pict_res = FULL_SIZE;
+	mt9d113_ctrl->curr_res = INVALID_SIZE;
+	if (data)
+		mt9d113_ctrl->sensordata = data;
+	if (rc < 0) {
+		printk(KERN_INFO "mt9d113_sensor_open_init fail\n");
+		return rc;
+	}
+		/* enable mclk first */
+		msm_camio_clk_rate_set(24000000);
+		msleep(20);
+		rc = mt9d113_probe_init_sensor(data);
+		if (rc < 0)
+			goto init_fail;
+		mt9d113_ctrl->fps = 30*Q8;
+		rc = mt9d113_sensor_init_probe(data);
+		if (rc < 0) {
+			gpio_set_value_cansleep(data->sensor_reset, 0);
+			goto init_fail;
+		} else
+			printk(KERN_ERR "%s: %d\n", __func__, __LINE__);
+		goto init_done;
+init_fail:
+		printk(KERN_INFO "init_fail\n");
+		mt9d113_probe_init_done(data);
+init_done:
+		CDBG("init_done\n");
+		return rc;
+}
+
+static int mt9d113_sensor_probe(const struct msm_camera_sensor_info
+				*info,
+				struct msm_sensor_ctrl *s)
+{
+	int rc = 0;
+	rc = i2c_add_driver(&mt9d113_i2c_driver);
+	if (rc < 0 || mt9d113_client == NULL) {
+		rc = -ENOTSUPP;
+		goto probe_fail;
+	}
+	msm_camio_clk_rate_set(24000000);
+	usleep_range(5000, 6000);
+	rc = mt9d113_probe_init_sensor(info);
+	if (rc < 0)
+		goto probe_fail;
+	s->s_init = mt9d113_sensor_open_init;
+	s->s_release = mt9d113_sensor_release;
+	s->s_config  = mt9d113_sensor_config;
+	s->s_camera_type = FRONT_CAMERA_2D;
+	s->s_mount_angle  = 0;
+	gpio_set_value_cansleep(info->sensor_reset, 0);
+	mt9d113_probe_init_done(info);
+	return rc;
+probe_fail:
+	printk(KERN_INFO "mt9d113_sensor_probe: SENSOR PROBE FAILS!\n");
+	return rc;
+}
+
+static int __mt9d113_probe(struct platform_device *pdev)
+{
+	return msm_camera_drv_start(pdev, mt9d113_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+	.probe = __mt9d113_probe,
+	.driver = {
+		.name = "msm_cam_mt9d113",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init mt9d113_init(void)
+{
+	return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(mt9d113_init);
+
+MODULE_DESCRIPTION("Micron 2MP YUV sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/mt9d113.h b/drivers/media/video/msm/mt9d113.h
new file mode 100644
index 0000000..f22f16c
--- /dev/null
+++ b/drivers/media/video/msm/mt9d113.h
@@ -0,0 +1,66 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef MT9D113_H
+#define MT9D113_H
+
+#include <linux/types.h>
+#include <mach/camera.h>
+
+extern struct mt9d113_reg mt9d113_regs;
+
+enum mt9d113_width {
+	WORD_LEN,
+	BYTE_LEN
+};
+
+struct mt9d113_i2c_reg_conf {
+	unsigned short waddr;
+	unsigned short wdata;
+};
+
+struct mt9d113_reg {
+	const struct mt9d113_i2c_reg_conf *pll_tbl;
+	uint16_t pll_tbl_size;
+	const struct mt9d113_i2c_reg_conf *register_tbl;
+	uint16_t register_tbl_size;
+	const struct mt9d113_i2c_reg_conf *err_tbl;
+	uint16_t err_tbl_size;
+	const struct mt9d113_i2c_reg_conf *low_light_tbl;
+	uint16_t low_light_tbl_size;
+	const struct mt9d113_i2c_reg_conf *awb_tbl;
+	uint16_t awb_tbl_size;
+	const struct mt9d113_i2c_reg_conf *patch_tbl;
+	uint16_t patch_tbl_size;
+	const struct mt9d113_i2c_reg_conf *eeprom_tbl ;
+	uint16_t eeprom_tbl_size ;
+};
+
+enum mt9d113_test_mode_t {
+	TEST_OFF,
+	TEST_1,
+	TEST_2,
+	TEST_3
+};
+
+enum mt9d113_resolution_t {
+	QTR_SIZE,
+	FULL_SIZE,
+	INVALID_SIZE
+};
+
+enum mt9d113_setting {
+	RES_PREVIEW,
+	RES_CAPTURE
+};
+#endif /* MT9D113_H */
diff --git a/drivers/media/video/msm/mt9d113_reg.c b/drivers/media/video/msm/mt9d113_reg.c
new file mode 100644
index 0000000..cd5be0f
--- /dev/null
+++ b/drivers/media/video/msm/mt9d113_reg.c
@@ -0,0 +1,455 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "mt9d113.h"
+
+struct mt9d113_i2c_reg_conf const
+	pll_tbl_settings[] = {
+		{0x0014, 0x21F9 }, /*PLL control: BYPASS PLL = 8697*/
+		{0x0010, 0x0115 }, /*PLL Dividers = 277*/
+		{0x0012, 0x0F5  }, /*PLL P Dividers = 245*/
+		{0x0014, 0x21FB }, /*PLL control: PLL_ENABLE on = 8699*/
+		{0x0014, 0x20FB }, /*PLL control: SEL_LOCK_DET on = 8443*/
+};
+
+struct mt9d113_i2c_reg_conf const
+	register_wizard_settings[] = {
+		{0x098C, 0x2719},
+		{0x0990, 0x005A},
+		{0x098C, 0x271B},
+		{0x0990, 0x01BE},
+		{0x098C, 0x271D},
+		{0x0990, 0x0131},
+		{0x098C, 0x271F},
+		{0x0990, 0x02BB},
+		{0x098C, 0x2721},
+		{0x0990, 0x0888},
+		{0x098C, 0x272F},
+		{0x0990, 0x003A},
+		{0x098C, 0x2731},
+		{0x0990, 0x00F6},
+		{0x098C, 0x2733},
+		{0x0990, 0x008B},
+		{0x098C, 0x2735},
+		{0x0990, 0x0521},
+		{0x098C, 0x2737},
+		{0x0990, 0x0888},
+		{0x098C, 0x275F},
+		{0x0990, 0x0194},
+		{0x098C, 0x2761},
+		{0x0990, 0x0014},
+		{0x098C, 0xA765},
+		{0x0990, 0x0044},
+		{0x098C, 0xA24F},
+		{0x0990, 0x0028},
+		{0x098C, 0xA20E},
+		{0x0990, 0x00A0},
+		{0x098C, 0xA20C},
+		{0x0990, 0x000E},
+		{0x098C, 0x2222},
+		{0x0990, 0x00A0},
+		{0x098C, 0x2212},
+		{0x0990, 0x01EE},
+		{0x098C, 0xA408},
+		{0x0990, 0x0026},
+		{0x098C, 0xA409},
+		{0x0990, 0x0029},
+		{0x098C, 0xA40A},
+		{0x0990, 0x002E},
+		{0x098C, 0xA40B},
+		{0x0990, 0x0031},
+		{0x098C, 0x2411},
+		{0x0990, 0x00A0},
+		{0x098C, 0x2413},
+		{0x0990, 0x00C0},
+		{0x098C, 0x2415},
+		{0x0990, 0x00A0},
+		{0x098C, 0x2417},
+		{0x0990, 0x00C0},
+};
+
+struct mt9d113_i2c_reg_conf const
+	err_settings[] = {
+		{0x3084, 0x240C},
+		{0x3092, 0x0A4C},
+		{0x3094, 0x4C4C},
+		{0x3096, 0x4C54},
+};
+
+struct mt9d113_i2c_reg_conf const
+	patch_settings[] = {
+		{0x098C, 0x0415},    /* MCU_ADDRESS*/
+		{0x0990, 0xF601},
+		{0x0992, 0x42C1},
+		{0x0994, 0x0326},
+		{0x0996, 0x11F6},
+		{0x0998, 0x0143},
+		{0x099A, 0xC104},
+		{0x099C, 0x260A},
+		{0x099E, 0xCC04},
+		{0x098C, 0x0425},
+		{0x0990, 0x33BD},
+		{0x0992, 0xA362},
+		{0x0994, 0xBD04},
+		{0x0996, 0x3339},
+		{0x0998, 0xC6FF},
+		{0x099A, 0xF701},
+		{0x099C, 0x6439},
+		{0x099E, 0xFE01},
+		{0x098C, 0x0435},
+		{0x0990, 0x6918},
+		{0x0992, 0xCE03},
+		{0x0994, 0x25CC},
+		{0x0996, 0x0013},
+		{0x0998, 0xBDC2},
+		{0x099A, 0xB8CC},
+		{0x099C, 0x0489},
+		{0x099E, 0xFD03},
+		{0x098C, 0x0445},
+		{0x0990, 0x27CC},
+		{0x0992, 0x0325},
+		{0x0994, 0xFD01},
+		{0x0996, 0x69FE},
+		{0x0998, 0x02BD},
+		{0x099A, 0x18CE},
+		{0x099C, 0x0339},
+		{0x099E, 0xCC00},
+		{0x098C, 0x0455},
+		{0x0990, 0x11BD},
+		{0x0992, 0xC2B8},
+		{0x0994, 0xCC04},
+		{0x0996, 0xC8FD},
+		{0x0998, 0x0347},
+		{0x099A, 0xCC03},
+		{0x099C, 0x39FD},
+		{0x099E, 0x02BD},
+		{0x098C, 0x0465},
+		{0x0990, 0xDE00},
+		{0x0992, 0x18CE},
+		{0x0994, 0x00C2},
+		{0x0996, 0xCC00},
+		{0x0998, 0x37BD},
+		{0x099A, 0xC2B8},
+		{0x099C, 0xCC04},
+		{0x099E, 0xEFDD},
+		{0x098C, 0x0475},
+		{0x0990, 0xE6CC},
+		{0x0992, 0x00C2},
+		{0x0994, 0xDD00},
+		{0x0996, 0xC601},
+		{0x0998, 0xF701},
+		{0x099A, 0x64C6},
+		{0x099C, 0x03F7},
+		{0x099E, 0x0165},
+		{0x098C, 0x0485},
+		{0x0990, 0x7F01},
+		{0x0992, 0x6639},
+		{0x0994, 0x3C3C},
+		{0x0996, 0x3C34},
+		{0x0998, 0xCC32},
+		{0x099A, 0x3EBD},
+		{0x099C, 0xA558},
+		{0x099E, 0x30ED},
+		{0x098C, 0x0495},
+		{0x0990, 0x04BD},
+		{0x0992, 0xB2D7},
+		{0x0994, 0x30E7},
+		{0x0996, 0x06CC},
+		{0x0998, 0x323E},
+		{0x099A, 0xED00},
+		{0x099C, 0xEC04},
+		{0x099E, 0xBDA5},
+		{0x098C, 0x04A5},
+		{0x0990, 0x44CC},
+		{0x0992, 0x3244},
+		{0x0994, 0xBDA5},
+		{0x0996, 0x585F},
+		{0x0998, 0x30ED},
+		{0x099A, 0x02CC},
+		{0x099C, 0x3244},
+		{0x099E, 0xED00},
+		{0x098C, 0x04B5},
+		{0x0990, 0xF601},
+		{0x0992, 0xD54F},
+		{0x0994, 0xEA03},
+		{0x0996, 0xAA02},
+		{0x0998, 0xBDA5},
+		{0x099A, 0x4430},
+		{0x099C, 0xE606},
+		{0x099E, 0x3838},
+		{0x098C, 0x04C5},
+		{0x0990, 0x3831},
+		{0x0992, 0x39BD},
+		{0x0994, 0xD661},
+		{0x0996, 0xF602},
+		{0x0998, 0xF4C1},
+		{0x099A, 0x0126},
+		{0x099C, 0x0BFE},
+		{0x099E, 0x02BD},
+		{0x098C, 0x04D5},
+		{0x0990, 0xEE10},
+		{0x0992, 0xFC02},
+		{0x0994, 0xF5AD},
+		{0x0996, 0x0039},
+		{0x0998, 0xF602},
+		{0x099A, 0xF4C1},
+		{0x099C, 0x0226},
+		{0x099E, 0x0AFE},
+		{0x098C, 0x04E5},
+		{0x0990, 0x02BD},
+		{0x0992, 0xEE10},
+		{0x0994, 0xFC02},
+		{0x0996, 0xF7AD},
+		{0x0998, 0x0039},
+		{0x099A, 0x3CBD},
+		{0x099C, 0xB059},
+		{0x099E, 0xCC00},
+		{0x098C, 0x04F5},
+		{0x0990, 0x28BD},
+		{0x0992, 0xA558},
+		{0x0994, 0x8300},
+		{0x0996, 0x0027},
+		{0x0998, 0x0BCC},
+		{0x099A, 0x0026},
+		{0x099C, 0x30ED},
+		{0x099E, 0x00C6},
+		{0x098C, 0x0505},
+		{0x0990, 0x03BD},
+		{0x0992, 0xA544},
+		{0x0994, 0x3839},
+		{0x098C, 0x2006},
+		{0x0990, 0x0415},
+		{0x098C, 0xA005},
+		{0x0990, 0x0001},
+};
+
+struct mt9d113_i2c_reg_conf const
+	eeprom_settings[] = {
+		{0x3658, 0x0110},
+		{0x365A, 0x1B6D},
+		{0x365C, 0x01F2},
+		{0x365E, 0xFBCD},
+		{0x3660, 0x8C91},
+		{0x3680, 0xB9ED},
+		{0x3682, 0x0EE},
+		{0x3684, 0x256F},
+		{0x3686, 0x824F},
+		{0x3688, 0xD293},
+		{0x36A8, 0x5BF2},
+		{0x36AA, 0x1711},
+		{0x36AC, 0xA095},
+		{0x36AE, 0x642C},
+		{0x36B0, 0x0E38},
+		{0x36D0, 0x88B0},
+		{0x36D2, 0x2EB2},
+		{0x36D4, 0x4C74},
+		{0x36D6, 0x9F96},
+		{0x36D8, 0x9557},
+		{0x36F8, 0xCE51},
+		{0x36FA, 0xB354},
+		{0x36FC, 0x2817},
+		{0x36FE, 0x14B8},
+		{0x3700, 0xB019},
+		{0x364E, 0x0710},
+		{0x3650, 0x30ED},
+		{0x3652, 0x03F2},
+		{0x3654, 0xF12E},
+		{0x3656, 0x8492},
+		{0x3676, 0xD9AD},
+		{0x3678, 0x88D0},
+		{0x367A, 0x7DED},
+		{0x367C, 0x3E31},
+		{0x367E, 0x91B3},
+		{0x369E, 0x7032},
+		{0x36A0, 0x2791},
+		{0x36A2, 0xBB55},
+		{0x36A4, 0xAB32},
+		{0x36A6, 0x1A58},
+		{0x36C6, 0xB50F},
+		{0x36C8, 0x0011},
+		{0x36CA, 0x6DB4},
+		{0x36CC, 0x96F5},
+		{0x36CE, 0x9BB7},
+		{0x36EE, 0x9353},
+		{0x36F0, 0xDF74},
+		{0x36F2, 0x04F8},
+		{0x36F4, 0x0FD8},
+		{0x36F6, 0xA87A},
+		{0x3662, 0x0170},
+		{0x3664, 0x6F0C},
+		{0x3666, 0x0112},
+		{0x3668, 0xCBAB},
+		{0x366A, 0x9111},
+		{0x368A, 0xB38D},
+		{0x368C, 0xE96F},
+		{0x368E, 0xCC0F},
+		{0x3690, 0x5851},
+		{0x3692, 0xFDD2},
+		{0x36B2, 0x5F92},
+		{0x36B4, 0x33B2},
+		{0x36B6, 0x9815},
+		{0x36B8, 0x86F5},
+		{0x36BA, 0x0578},
+		{0x36DA, 0xCD90},
+		{0x36DC, 0x1131},
+		{0x36DE, 0x5275},
+		{0x36E0, 0xE855},
+		{0x36E2, 0xD037},
+		{0x3702, 0xAAD1},
+		{0x3704, 0xEB75},
+		{0x3706, 0x0CD7},
+		{0x3708, 0x2C79},
+		{0x370A, 0xE0B9},
+		{0x366C, 0x0190},
+		{0x366E, 0x1C8D},
+		{0x3670, 0x0052},
+		{0x3672, 0xD66E},
+		{0x3674, 0xF511},
+		{0x3694, 0xB54D},
+		{0x3696, 0x6E4E},
+		{0x3698, 0x142E},
+		{0x369A, 0xC190},
+		{0x369C, 0xA753},
+		{0x36BC, 0x70F2},
+		{0x36BE, 0x04F1},
+		{0x36C0, 0xBD95},
+		{0x36C2, 0x0CEE},
+		{0x36C4, 0x1BF8},
+		{0x36E4, 0x806F},
+		{0x36E6, 0x1672},
+		{0x36E8, 0x2DF4},
+		{0x36EA, 0x8F16},
+		{0x36EC, 0xF776},
+		{0x370C, 0xAD73},
+		{0x370E, 0xB534},
+		{0x3710, 0x0D18},
+		{0x3712, 0x6057},
+		{0x3714, 0xBD1A},
+		{0x3644, 0x0354},
+		{0x3642, 0x0234},
+		{0x3210, 0x01B8},
+};
+
+struct mt9d113_i2c_reg_conf const
+	awb_settings[] = {
+		{0x098C, 0x2306},
+		{0x0990, 0x0180},
+		{0x098C, 0x2308},
+		{0x0990, 0xFF00},
+		{0x098C, 0x230A},
+		{0x0990, 0x0080},
+		{0x098C, 0x230C},
+		{0x0990, 0xFF66},
+		{0x098C, 0x230E},
+		{0x0990, 0x0180},
+		{0x098C, 0x2310},
+		{0x0990, 0xFFEE},
+		{0x098C, 0x2312},
+		{0x0990, 0xFFCD},
+		{0x098C, 0x2314},
+		{0x0990, 0xFECD},
+		{0x098C, 0x2316},
+		{0x0990, 0x019A},
+		{0x098C, 0x2318},
+		{0x0990, 0x0020},
+		{0x098C, 0x231A},
+		{0x0990, 0x0033},
+		{0x098C, 0x231C},
+		{0x0990, 0x0100},
+		{0x098C, 0x231E},
+		{0x0990, 0xFF9A},
+		{0x098C, 0x2320},
+		{0x0990, 0x0000},
+		{0x098C, 0x2322},
+		{0x0990, 0x004D},
+		{0x098C, 0x2324},
+		{0x0990, 0xFFCD},
+		{0x098C, 0x2326},
+		{0x0990, 0xFFB8},
+		{0x098C, 0x2328},
+		{0x0990, 0x004D},
+		{0x098C, 0x232A},
+		{0x0990, 0x0080},
+		{0x098C, 0x232C},
+		{0x0990, 0xFF66},
+		{0x098C, 0x232E},
+		{0x0990, 0x0008},
+		{0x098C, 0x2330},
+		{0x0990, 0xFFF7},
+		{0x098C, 0xA363},
+		{0x0990, 0x00D2},
+		{0x098C, 0xA364},
+		{0x0990, 0x00EE},
+		{0x3244, 0x0328},
+		{0x323E, 0xC22C},
+};
+
+struct mt9d113_i2c_reg_conf const
+	low_light_setting[] = {
+		{0x098C, 0x2B28},
+		{0x0990, 0x35E8},
+		{0x098C, 0x2B2A},
+		{0x0990, 0xB3B0},
+		{0x098C, 0xAB20},
+		{0x0990, 0x004B},
+		{0x098C, 0xAB24},
+		{0x0990, 0x0000},
+		{0x098C, 0xAB25},
+		{0x0990, 0x00FF},
+		{0x098C, 0xAB30},
+		{0x0990, 0x00FF},
+		{0x098C, 0xAB31},
+		{0x0990, 0x00FF},
+		{0x098C, 0xAB32},
+		{0x0990, 0x00FF},
+		{0x098C, 0xAB33},
+		{0x0990, 0x0057},
+		{0x098C, 0xAB34},
+		{0x0990, 0x0080},
+		{0x098C, 0xAB35},
+		{0x0990, 0x00FF},
+		{0x098C, 0xAB36},
+		{0x0990, 0x0014},
+		{0x098C, 0xAB37},
+		{0x0990, 0x0003},
+		{0x098C, 0x2B38},
+		{0x0990, 0x32C8},
+		{0x098C, 0x2B3A},
+		{0x0990, 0x7918},
+		{0x098C, 0x2B62},
+		{0x0990, 0xFFFE},
+		{0x098C, 0x2B64},
+		{0x0990, 0xFFFF},
+};
+
+struct mt9d113_reg mt9d113_regs = {
+		.pll_tbl = pll_tbl_settings,
+		.pll_tbl_size = ARRAY_SIZE(
+			pll_tbl_settings),
+		.register_tbl = register_wizard_settings,
+		.register_tbl_size = ARRAY_SIZE(
+			register_wizard_settings),
+		.err_tbl = err_settings,
+		.err_tbl_size = ARRAY_SIZE(err_settings),
+		.low_light_tbl = low_light_setting,
+		.low_light_tbl_size = ARRAY_SIZE(low_light_setting),
+		.awb_tbl = awb_settings,
+		.awb_tbl_size = ARRAY_SIZE(awb_settings),
+		.patch_tbl = patch_settings,
+		.patch_tbl_size = ARRAY_SIZE(patch_settings),
+		.eeprom_tbl = eeprom_settings,
+		.eeprom_tbl_size = ARRAY_SIZE(eeprom_settings),
+};
+
+
+
diff --git a/drivers/media/video/msm/mt9e013.c b/drivers/media/video/msm/mt9e013.c
new file mode 100644
index 0000000..94546f4
--- /dev/null
+++ b/drivers/media/video/msm/mt9e013.c
@@ -0,0 +1,1140 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <media/msm_camera.h>
+#include <mach/gpio.h>
+#include <mach/camera.h>
+#include "mt9e013.h"
+/*=============================================================
+	SENSOR REGISTER DEFINES
+==============================================================*/
+#define REG_GROUPED_PARAMETER_HOLD		0x0104
+#define GROUPED_PARAMETER_HOLD_OFF		0x00
+#define GROUPED_PARAMETER_HOLD			0x01
+/* Integration Time */
+#define REG_COARSE_INTEGRATION_TIME		0x3012
+/* Gain */
+#define REG_GLOBAL_GAIN	0x305E
+/* PLL registers */
+#define REG_FRAME_LENGTH_LINES		0x0340
+/* Test Pattern */
+#define REG_TEST_PATTERN_MODE			0x0601
+#define REG_VCM_NEW_CODE			0x30F2
+
+/*============================================================================
+							 TYPE DECLARATIONS
+============================================================================*/
+
+/* 16bit address - 8 bit context register structure */
+#define Q8	0x00000100
+#define Q10	0x00000400
+#define MT9E013_MASTER_CLK_RATE 24000000
+
+/* AF Total steps parameters */
+#define MT9E013_TOTAL_STEPS_NEAR_TO_FAR    32
+
+uint16_t mt9e013_step_position_table[MT9E013_TOTAL_STEPS_NEAR_TO_FAR+1];
+uint16_t mt9e013_nl_region_boundary1;
+uint16_t mt9e013_nl_region_code_per_step1;
+uint16_t mt9e013_l_region_code_per_step = 4;
+uint16_t mt9e013_damping_threshold = 10;
+uint16_t mt9e013_sw_damping_time_wait = 1;
+
+struct mt9e013_work_t {
+	struct work_struct work;
+};
+
+static struct mt9e013_work_t *mt9e013_sensorw;
+static struct i2c_client *mt9e013_client;
+
+struct mt9e013_ctrl_t {
+	const struct  msm_camera_sensor_info *sensordata;
+
+	uint32_t sensormode;
+	uint32_t fps_divider;/* init to 1 * 0x00000400 */
+	uint32_t pict_fps_divider;/* init to 1 * 0x00000400 */
+	uint16_t fps;
+
+	uint16_t curr_lens_pos;
+	uint16_t curr_step_pos;
+	uint16_t my_reg_gain;
+	uint32_t my_reg_line_count;
+	uint16_t total_lines_per_frame;
+
+	enum mt9e013_resolution_t prev_res;
+	enum mt9e013_resolution_t pict_res;
+	enum mt9e013_resolution_t curr_res;
+	enum mt9e013_test_mode_t  set_test;
+};
+
+
+static bool CSI_CONFIG;
+static struct mt9e013_ctrl_t *mt9e013_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(mt9e013_wait_queue);
+DEFINE_MUTEX(mt9e013_mut);
+
+static int cam_debug_init(void);
+static struct dentry *debugfs_base;
+/*=============================================================*/
+
+static int mt9e013_i2c_rxdata(unsigned short saddr,
+	unsigned char *rxdata, int length)
+{
+	struct i2c_msg msgs[] = {
+		{
+			.addr  = saddr,
+			.flags = 0,
+			.len   = 2,
+			.buf   = rxdata,
+		},
+		{
+			.addr  = saddr,
+			.flags = I2C_M_RD,
+			.len   = 2,
+			.buf   = rxdata,
+		},
+	};
+	if (i2c_transfer(mt9e013_client->adapter, msgs, 2) < 0) {
+		CDBG("mt9e013_i2c_rxdata faild 0x%x\n", saddr);
+		return -EIO;
+	}
+	return 0;
+}
+
+static int32_t mt9e013_i2c_txdata(unsigned short saddr,
+				unsigned char *txdata, int length)
+{
+	struct i2c_msg msg[] = {
+		{
+			.addr = saddr,
+			.flags = 0,
+			.len = length,
+			.buf = txdata,
+		 },
+	};
+	if (i2c_transfer(mt9e013_client->adapter, msg, 1) < 0) {
+		CDBG("mt9e013_i2c_txdata faild 0x%x\n", saddr);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int32_t mt9e013_i2c_read(unsigned short raddr,
+	unsigned short *rdata, int rlen)
+{
+	int32_t rc = 0;
+	unsigned char buf[2];
+	if (!rdata)
+		return -EIO;
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (raddr & 0xFF00) >> 8;
+	buf[1] = (raddr & 0x00FF);
+	rc = mt9e013_i2c_rxdata(mt9e013_client->addr<<1, buf, rlen);
+	if (rc < 0) {
+		CDBG("mt9e013_i2c_read 0x%x failed!\n", raddr);
+		return rc;
+	}
+	*rdata = (rlen == 2 ? buf[0] << 8 | buf[1] : buf[0]);
+	CDBG("mt9e013_i2c_read 0x%x val = 0x%x!\n", raddr, *rdata);
+	return rc;
+}
+
+static int32_t mt9e013_i2c_write_w_sensor(unsigned short waddr, uint16_t wdata)
+{
+	int32_t rc = -EFAULT;
+	unsigned char buf[4];
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (waddr & 0xFF00) >> 8;
+	buf[1] = (waddr & 0x00FF);
+	buf[2] = (wdata & 0xFF00) >> 8;
+	buf[3] = (wdata & 0x00FF);
+	CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, wdata);
+	rc = mt9e013_i2c_txdata(mt9e013_client->addr<<1, buf, 4);
+	if (rc < 0) {
+		CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+			waddr, wdata);
+	}
+	return rc;
+}
+
+static int32_t mt9e013_i2c_write_b_sensor(unsigned short waddr, uint8_t bdata)
+{
+	int32_t rc = -EFAULT;
+	unsigned char buf[3];
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (waddr & 0xFF00) >> 8;
+	buf[1] = (waddr & 0x00FF);
+	buf[2] = bdata;
+	CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, bdata);
+	rc = mt9e013_i2c_txdata(mt9e013_client->addr<<1, buf, 3);
+	if (rc < 0) {
+		CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+			waddr, bdata);
+	}
+	return rc;
+}
+
+static int32_t mt9e013_i2c_write_w_table(struct mt9e013_i2c_reg_conf const
+					 *reg_conf_tbl, int num)
+{
+	int i;
+	int32_t rc = -EIO;
+	for (i = 0; i < num; i++) {
+		rc = mt9e013_i2c_write_w_sensor(reg_conf_tbl->waddr,
+			reg_conf_tbl->wdata);
+		if (rc < 0)
+			break;
+		reg_conf_tbl++;
+	}
+	return rc;
+}
+
+static void mt9e013_group_hold_on(void)
+{
+	mt9e013_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+						GROUPED_PARAMETER_HOLD);
+}
+
+static void mt9e013_group_hold_off(void)
+{
+	mt9e013_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+						GROUPED_PARAMETER_HOLD_OFF);
+}
+
+static void mt9e013_start_stream(void)
+{
+	mt9e013_i2c_write_w_sensor(0x301A, 0x8250);
+	mt9e013_i2c_write_w_sensor(0x301A, 0x8650);
+	mt9e013_i2c_write_w_sensor(0x301A, 0x8658);
+	mt9e013_i2c_write_b_sensor(0x0104, 0x00);
+	mt9e013_i2c_write_w_sensor(0x301A, 0x065C);
+}
+
+static void mt9e013_stop_stream(void)
+{
+	mt9e013_i2c_write_w_sensor(0x301A, 0x0058);
+	mt9e013_i2c_write_w_sensor(0x301A, 0x0050);
+	mt9e013_i2c_write_b_sensor(0x0104, 0x01);
+}
+
+static void mt9e013_get_pict_fps(uint16_t fps, uint16_t *pfps)
+{
+	/* input fps is preview fps in Q8 format */
+	uint32_t divider, d1, d2;
+
+	d1 = mt9e013_regs.reg_prev[E013_FRAME_LENGTH_LINES].wdata
+		* 0x00000400/
+		mt9e013_regs.reg_snap[E013_FRAME_LENGTH_LINES].wdata;
+	d2 = mt9e013_regs.reg_prev[E013_LINE_LENGTH_PCK].wdata
+		* 0x00000400/
+		mt9e013_regs.reg_snap[E013_LINE_LENGTH_PCK].wdata;
+	divider = d1 * d2 / 0x400;
+
+	/*Verify PCLK settings and frame sizes.*/
+	*pfps = (uint16_t) (fps * divider / 0x400);
+	/* 2 is the ratio of no.of snapshot channels
+	to number of preview channels */
+}
+
+static uint16_t mt9e013_get_prev_lines_pf(void)
+{
+	if (mt9e013_ctrl->prev_res == QTR_SIZE)
+		return mt9e013_regs.reg_prev[E013_FRAME_LENGTH_LINES].wdata;
+	else if (mt9e013_ctrl->prev_res == FULL_SIZE)
+		return mt9e013_regs.reg_snap[E013_FRAME_LENGTH_LINES].wdata;
+	else if (mt9e013_ctrl->prev_res == HFR_60FPS)
+		return mt9e013_regs.reg_60fps[E013_FRAME_LENGTH_LINES].wdata;
+	else if (mt9e013_ctrl->prev_res == HFR_90FPS)
+		return mt9e013_regs.reg_120fps[E013_FRAME_LENGTH_LINES].wdata;
+	else
+		return mt9e013_regs.reg_120fps[E013_FRAME_LENGTH_LINES].wdata;
+}
+
+static uint16_t mt9e013_get_prev_pixels_pl(void)
+{
+	if (mt9e013_ctrl->prev_res == QTR_SIZE)
+		return mt9e013_regs.reg_prev[E013_LINE_LENGTH_PCK].wdata;
+	else if (mt9e013_ctrl->prev_res == FULL_SIZE)
+		return mt9e013_regs.reg_snap[E013_LINE_LENGTH_PCK].wdata;
+	else if (mt9e013_ctrl->prev_res == HFR_60FPS)
+		return mt9e013_regs.reg_60fps[E013_LINE_LENGTH_PCK].wdata;
+	else if (mt9e013_ctrl->prev_res == HFR_90FPS)
+		return mt9e013_regs.reg_120fps[E013_LINE_LENGTH_PCK].wdata;
+	else
+		return mt9e013_regs.reg_120fps[E013_LINE_LENGTH_PCK].wdata;
+}
+
+static uint16_t mt9e013_get_pict_lines_pf(void)
+{
+	if (mt9e013_ctrl->pict_res == QTR_SIZE)
+		return mt9e013_regs.reg_prev[E013_FRAME_LENGTH_LINES].wdata;
+	else if (mt9e013_ctrl->pict_res == FULL_SIZE)
+		return mt9e013_regs.reg_snap[E013_FRAME_LENGTH_LINES].wdata;
+	else if (mt9e013_ctrl->pict_res == HFR_60FPS)
+		return mt9e013_regs.reg_60fps[E013_FRAME_LENGTH_LINES].wdata;
+	else if (mt9e013_ctrl->pict_res == HFR_90FPS)
+		return mt9e013_regs.reg_120fps[E013_FRAME_LENGTH_LINES].wdata;
+	else
+		return mt9e013_regs.reg_120fps[E013_FRAME_LENGTH_LINES].wdata;
+}
+
+static uint16_t mt9e013_get_pict_pixels_pl(void)
+{
+	if (mt9e013_ctrl->pict_res == QTR_SIZE)
+		return mt9e013_regs.reg_prev[E013_LINE_LENGTH_PCK].wdata;
+	else if (mt9e013_ctrl->pict_res == FULL_SIZE)
+		return mt9e013_regs.reg_snap[E013_LINE_LENGTH_PCK].wdata;
+	else if (mt9e013_ctrl->pict_res == HFR_60FPS)
+		return mt9e013_regs.reg_60fps[E013_LINE_LENGTH_PCK].wdata;
+	else if (mt9e013_ctrl->pict_res == HFR_90FPS)
+		return mt9e013_regs.reg_120fps[E013_LINE_LENGTH_PCK].wdata;
+	else
+		return mt9e013_regs.reg_120fps[E013_LINE_LENGTH_PCK].wdata;
+}
+
+static uint32_t mt9e013_get_pict_max_exp_lc(void)
+{
+	if (mt9e013_ctrl->pict_res == QTR_SIZE)
+		return mt9e013_regs.reg_prev[E013_FRAME_LENGTH_LINES].wdata
+			* 24;
+	else if (mt9e013_ctrl->pict_res == FULL_SIZE)
+		return mt9e013_regs.reg_snap[E013_FRAME_LENGTH_LINES].wdata
+			* 24;
+	else if (mt9e013_ctrl->pict_res == HFR_60FPS)
+		return mt9e013_regs.reg_60fps[E013_FRAME_LENGTH_LINES].wdata
+			* 24;
+	else if (mt9e013_ctrl->pict_res == HFR_90FPS)
+		return mt9e013_regs.reg_120fps[E013_FRAME_LENGTH_LINES].wdata
+			* 24;
+	else
+		return mt9e013_regs.reg_120fps[E013_FRAME_LENGTH_LINES].wdata
+			* 24;
+}
+
+static int32_t mt9e013_set_fps(struct fps_cfg   *fps)
+{
+	uint16_t total_lines_per_frame;
+	int32_t rc = 0;
+	if (mt9e013_ctrl->curr_res == QTR_SIZE)
+		total_lines_per_frame =
+		mt9e013_regs.reg_prev[E013_FRAME_LENGTH_LINES].wdata;
+	else if (mt9e013_ctrl->curr_res == FULL_SIZE)
+		total_lines_per_frame =
+		mt9e013_regs.reg_snap[E013_FRAME_LENGTH_LINES].wdata;
+	else if (mt9e013_ctrl->curr_res == HFR_60FPS)
+		total_lines_per_frame =
+		mt9e013_regs.reg_60fps[E013_FRAME_LENGTH_LINES].wdata;
+	else if (mt9e013_ctrl->curr_res == HFR_90FPS)
+		total_lines_per_frame =
+		mt9e013_regs.reg_120fps[E013_FRAME_LENGTH_LINES].wdata;
+	else
+		total_lines_per_frame =
+		mt9e013_regs.reg_120fps[E013_FRAME_LENGTH_LINES].wdata;
+
+	mt9e013_ctrl->fps_divider = fps->fps_div;
+	mt9e013_ctrl->pict_fps_divider = fps->pict_fps_div;
+
+	if (mt9e013_ctrl->curr_res == FULL_SIZE) {
+		total_lines_per_frame = (uint16_t)
+		(total_lines_per_frame * mt9e013_ctrl->pict_fps_divider/0x400);
+	} else {
+		total_lines_per_frame = (uint16_t)
+		(total_lines_per_frame * mt9e013_ctrl->fps_divider/0x400);
+	}
+
+	mt9e013_group_hold_on();
+	rc = mt9e013_i2c_write_w_sensor(REG_FRAME_LENGTH_LINES,
+							total_lines_per_frame);
+	mt9e013_group_hold_off();
+	return rc;
+}
+
+static int32_t mt9e013_write_exp_gain(uint16_t gain, uint32_t line)
+{
+	uint16_t max_legal_gain = 0xE7F;
+	int32_t rc = 0;
+	if (gain > max_legal_gain) {
+		CDBG("Max legal gain Line:%d\n", __LINE__);
+		gain = max_legal_gain;
+	}
+
+	if (mt9e013_ctrl->curr_res != FULL_SIZE) {
+		mt9e013_ctrl->my_reg_gain = gain;
+		mt9e013_ctrl->my_reg_line_count = (uint16_t) line;
+		line = (uint32_t) (line * mt9e013_ctrl->fps_divider /
+						   0x00000400);
+	} else {
+		line = (uint32_t) (line * mt9e013_ctrl->pict_fps_divider /
+						   0x00000400);
+	}
+
+	gain |= 0x1000;
+
+	mt9e013_group_hold_on();
+	rc = mt9e013_i2c_write_w_sensor(REG_GLOBAL_GAIN, gain);
+	rc = mt9e013_i2c_write_w_sensor(REG_COARSE_INTEGRATION_TIME, line);
+	mt9e013_group_hold_off();
+	return rc;
+}
+
+static int32_t mt9e013_set_pict_exp_gain(uint16_t gain, uint32_t line)
+{
+	int32_t rc = 0;
+	rc = mt9e013_write_exp_gain(gain, line);
+	mt9e013_i2c_write_w_sensor(0x301A, 0x065C|0x2);
+	return rc;
+}
+
+#define DIV_CEIL(x, y) (x/y + (x%y) ? 1 : 0)
+
+static int32_t mt9e013_move_focus(int direction,
+	int32_t num_steps)
+{
+	int16_t step_direction, dest_lens_position, dest_step_position;
+	int16_t target_dist, small_step, next_lens_position;
+	if (direction == MOVE_NEAR)
+		step_direction = 1;
+	else
+		step_direction = -1;
+
+	dest_step_position = mt9e013_ctrl->curr_step_pos
+						+ (step_direction * num_steps);
+
+	if (dest_step_position < 0)
+		dest_step_position = 0;
+	else if (dest_step_position > MT9E013_TOTAL_STEPS_NEAR_TO_FAR)
+		dest_step_position = MT9E013_TOTAL_STEPS_NEAR_TO_FAR;
+
+	if (dest_step_position == mt9e013_ctrl->curr_step_pos)
+		return 0;
+
+	dest_lens_position = mt9e013_step_position_table[dest_step_position];
+	target_dist = step_direction *
+		(dest_lens_position - mt9e013_ctrl->curr_lens_pos);
+
+	if (step_direction < 0 && (target_dist >=
+		mt9e013_step_position_table[mt9e013_damping_threshold])) {
+		small_step = DIV_CEIL(target_dist, 10);
+		mt9e013_sw_damping_time_wait = 10;
+	} else {
+		small_step = DIV_CEIL(target_dist, 4);
+		mt9e013_sw_damping_time_wait = 4;
+	}
+
+	for (next_lens_position = mt9e013_ctrl->curr_lens_pos
+		+ (step_direction * small_step);
+		(step_direction * next_lens_position) <=
+		(step_direction * dest_lens_position);
+		next_lens_position += (step_direction * small_step)) {
+		mt9e013_i2c_write_w_sensor(REG_VCM_NEW_CODE,
+		next_lens_position);
+		mt9e013_ctrl->curr_lens_pos = next_lens_position;
+		usleep(mt9e013_sw_damping_time_wait*50);
+	}
+
+	if (mt9e013_ctrl->curr_lens_pos != dest_lens_position) {
+		mt9e013_i2c_write_w_sensor(REG_VCM_NEW_CODE,
+		dest_lens_position);
+		usleep(mt9e013_sw_damping_time_wait*50);
+	}
+	mt9e013_ctrl->curr_lens_pos = dest_lens_position;
+	mt9e013_ctrl->curr_step_pos = dest_step_position;
+	return 0;
+}
+
+static int32_t mt9e013_set_default_focus(uint8_t af_step)
+{
+	int32_t rc = 0;
+	if (mt9e013_ctrl->curr_step_pos != 0) {
+		rc = mt9e013_move_focus(MOVE_FAR,
+		mt9e013_ctrl->curr_step_pos);
+	} else {
+		mt9e013_i2c_write_w_sensor(REG_VCM_NEW_CODE, 0x00);
+	}
+
+	mt9e013_ctrl->curr_lens_pos = 0;
+	mt9e013_ctrl->curr_step_pos = 0;
+
+	return rc;
+}
+
+static void mt9e013_init_focus(void)
+{
+	uint8_t i;
+	mt9e013_step_position_table[0] = 0;
+	for (i = 1; i <= MT9E013_TOTAL_STEPS_NEAR_TO_FAR; i++) {
+		if (i <= mt9e013_nl_region_boundary1) {
+			mt9e013_step_position_table[i] =
+				mt9e013_step_position_table[i-1]
+				+ mt9e013_nl_region_code_per_step1;
+		} else {
+			mt9e013_step_position_table[i] =
+				mt9e013_step_position_table[i-1]
+				+ mt9e013_l_region_code_per_step;
+		}
+
+		if (mt9e013_step_position_table[i] > 255)
+			mt9e013_step_position_table[i] = 255;
+	}
+}
+
+static int32_t mt9e013_test(enum mt9e013_test_mode_t mo)
+{
+	int32_t rc = 0;
+	if (mo == TEST_OFF)
+		return rc;
+	else {
+		/* REG_0x30D8[4] is TESBYPEN: 0: Normal Operation,
+		1: Bypass Signal Processing
+		REG_0x30D8[5] is EBDMASK: 0:
+		Output Embedded data, 1: No output embedded data */
+		if (mt9e013_i2c_write_b_sensor(REG_TEST_PATTERN_MODE,
+			(uint8_t) mo) < 0) {
+			return rc;
+		}
+	}
+	return rc;
+}
+
+static int32_t mt9e013_sensor_setting(int update_type, int rt)
+{
+
+	int32_t rc = 0;
+	struct msm_camera_csi_params mt9e013_csi_params;
+	uint8_t stored_af_step = 0;
+	CDBG("sensor_settings\n");
+	stored_af_step = mt9e013_ctrl->curr_step_pos;
+	mt9e013_set_default_focus(0);
+	mt9e013_stop_stream();
+	msleep(15);
+	if (update_type == REG_INIT) {
+		mt9e013_i2c_write_w_table(mt9e013_regs.reg_mipi,
+			mt9e013_regs.reg_mipi_size);
+		mt9e013_i2c_write_w_table(mt9e013_regs.rec_settings,
+			mt9e013_regs.rec_size);
+		cam_debug_init();
+		CSI_CONFIG = 0;
+	} else if (update_type == UPDATE_PERIODIC) {
+		if (rt == QTR_SIZE) {
+			mt9e013_i2c_write_w_table(mt9e013_regs.reg_pll,
+				mt9e013_regs.reg_pll_size);
+			mt9e013_i2c_write_w_table(mt9e013_regs.reg_prev,
+				mt9e013_regs.reg_prev_size);
+		} else if (rt == FULL_SIZE) {
+			mt9e013_i2c_write_w_table(mt9e013_regs.reg_pll,
+				mt9e013_regs.reg_pll_size);
+			mt9e013_i2c_write_w_table(mt9e013_regs.reg_snap,
+				mt9e013_regs.reg_snap_size);
+		} else if (rt == HFR_60FPS) {
+			mt9e013_i2c_write_w_table(mt9e013_regs.reg_pll_120fps,
+				mt9e013_regs.reg_pll_120fps_size);
+			mt9e013_i2c_write_w_sensor(0x0306, 0x0029);
+			mt9e013_i2c_write_w_table(mt9e013_regs.reg_120fps,
+				mt9e013_regs.reg_120fps_size);
+		} else if (rt == HFR_90FPS) {
+			mt9e013_i2c_write_w_table(mt9e013_regs.reg_pll_120fps,
+				mt9e013_regs.reg_pll_120fps_size);
+			mt9e013_i2c_write_w_sensor(0x0306, 0x003D);
+			mt9e013_i2c_write_w_table(mt9e013_regs.reg_120fps,
+				mt9e013_regs.reg_120fps_size);
+		} else if (rt == HFR_120FPS) {
+			msm_camio_vfe_clk_rate_set(266667000);
+			mt9e013_i2c_write_w_table(mt9e013_regs.reg_pll_120fps,
+				mt9e013_regs.reg_pll_120fps_size);
+			mt9e013_i2c_write_w_table(mt9e013_regs.reg_120fps,
+				mt9e013_regs.reg_120fps_size);
+		}
+		if (!CSI_CONFIG) {
+			msm_camio_vfe_clk_rate_set(192000000);
+			mt9e013_csi_params.data_format = CSI_10BIT;
+			mt9e013_csi_params.lane_cnt = 2;
+			mt9e013_csi_params.lane_assign = 0xe4;
+			mt9e013_csi_params.dpcm_scheme = 0;
+			mt9e013_csi_params.settle_cnt = 0x18;
+			rc = msm_camio_csi_config(&mt9e013_csi_params);
+			msleep(10);
+			CSI_CONFIG = 1;
+		}
+		mt9e013_move_focus(MOVE_NEAR, stored_af_step);
+		mt9e013_start_stream();
+	}
+	return rc;
+}
+
+static int32_t mt9e013_video_config(int mode)
+{
+
+	int32_t rc = 0;
+
+	CDBG("video config\n");
+	/* change sensor resolution if needed */
+	if (mt9e013_sensor_setting(UPDATE_PERIODIC,
+			mt9e013_ctrl->prev_res) < 0)
+		return rc;
+	if (mt9e013_ctrl->set_test) {
+		if (mt9e013_test(mt9e013_ctrl->set_test) < 0)
+			return  rc;
+	}
+
+	mt9e013_ctrl->curr_res = mt9e013_ctrl->prev_res;
+	mt9e013_ctrl->sensormode = mode;
+	return rc;
+}
+
+static int32_t mt9e013_snapshot_config(int mode)
+{
+	int32_t rc = 0;
+	/*change sensor resolution if needed */
+	if (mt9e013_ctrl->curr_res != mt9e013_ctrl->pict_res) {
+		if (mt9e013_sensor_setting(UPDATE_PERIODIC,
+				mt9e013_ctrl->pict_res) < 0)
+			return rc;
+	}
+
+	mt9e013_ctrl->curr_res = mt9e013_ctrl->pict_res;
+	mt9e013_ctrl->sensormode = mode;
+	return rc;
+} /*end of mt9e013_snapshot_config*/
+
+static int32_t mt9e013_raw_snapshot_config(int mode)
+{
+	int32_t rc = 0;
+	/* change sensor resolution if needed */
+	if (mt9e013_ctrl->curr_res != mt9e013_ctrl->pict_res) {
+		if (mt9e013_sensor_setting(UPDATE_PERIODIC,
+				mt9e013_ctrl->pict_res) < 0)
+			return rc;
+	}
+
+	mt9e013_ctrl->curr_res = mt9e013_ctrl->pict_res;
+	mt9e013_ctrl->sensormode = mode;
+	return rc;
+} /*end of mt9e013_raw_snapshot_config*/
+
+static int32_t mt9e013_set_sensor_mode(int mode,
+	int res)
+{
+	int32_t rc = 0;
+	switch (mode) {
+	case SENSOR_PREVIEW_MODE:
+	case SENSOR_HFR_60FPS_MODE:
+	case SENSOR_HFR_90FPS_MODE:
+	case SENSOR_HFR_120FPS_MODE:
+		mt9e013_ctrl->prev_res = res;
+		rc = mt9e013_video_config(mode);
+		break;
+	case SENSOR_SNAPSHOT_MODE:
+		mt9e013_ctrl->pict_res = res;
+		rc = mt9e013_snapshot_config(mode);
+		break;
+	case SENSOR_RAW_SNAPSHOT_MODE:
+		mt9e013_ctrl->pict_res = res;
+		rc = mt9e013_raw_snapshot_config(mode);
+		break;
+	default:
+		rc = -EINVAL;
+		break;
+	}
+	return rc;
+}
+
+static int32_t mt9e013_power_down(void)
+{
+	return 0;
+}
+
+static int mt9e013_probe_init_done(const struct msm_camera_sensor_info *data)
+{
+	CDBG("probe done\n");
+	gpio_free(data->sensor_reset);
+	return 0;
+}
+
+static int mt9e013_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+	int32_t rc = 0;
+	uint16_t chipid = 0;
+	CDBG("%s: %d\n", __func__, __LINE__);
+	rc = gpio_request(data->sensor_reset, "mt9e013");
+	CDBG(" mt9e013_probe_init_sensor\n");
+	if (!rc) {
+		CDBG("sensor_reset = %d\n", rc);
+		gpio_direction_output(data->sensor_reset, 0);
+		msleep(10);
+		gpio_set_value_cansleep(data->sensor_reset, 1);
+		msleep(10);
+	} else {
+		goto init_probe_done;
+	}
+
+	CDBG(" mt9e013_probe_init_sensor is called\n");
+	rc = mt9e013_i2c_read(0x0000, &chipid, 2);
+	CDBG("ID: %d\n", chipid);
+	/* 4. Compare sensor ID to MT9E013 ID: */
+	if (chipid != 0x4B00) {
+		rc = -ENODEV;
+		CDBG("mt9e013_probe_init_sensor fail chip id doesnot match\n");
+		goto init_probe_fail;
+	}
+
+	mt9e013_ctrl = kzalloc(sizeof(struct mt9e013_ctrl_t), GFP_KERNEL);
+	if (!mt9e013_ctrl) {
+		CDBG("mt9e013_init failed!\n");
+		rc = -ENOMEM;
+	}
+	mt9e013_ctrl->fps_divider = 1 * 0x00000400;
+	mt9e013_ctrl->pict_fps_divider = 1 * 0x00000400;
+	mt9e013_ctrl->set_test = TEST_OFF;
+	mt9e013_ctrl->prev_res = QTR_SIZE;
+	mt9e013_ctrl->pict_res = FULL_SIZE;
+
+	if (data)
+		mt9e013_ctrl->sensordata = data;
+
+	goto init_probe_done;
+init_probe_fail:
+	CDBG(" mt9e013_probe_init_sensor fails\n");
+	gpio_set_value_cansleep(data->sensor_reset, 0);
+	mt9e013_probe_init_done(data);
+init_probe_done:
+	CDBG(" mt9e013_probe_init_sensor finishes\n");
+	return rc;
+}
+/* camsensor_mt9e013_reset */
+
+int mt9e013_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+	int32_t rc = 0;
+
+	CDBG("%s: %d\n", __func__, __LINE__);
+	CDBG("Calling mt9e013_sensor_open_init\n");
+
+	mt9e013_ctrl = kzalloc(sizeof(struct mt9e013_ctrl_t), GFP_KERNEL);
+	if (!mt9e013_ctrl) {
+		CDBG("mt9e013_init failed!\n");
+		rc = -ENOMEM;
+		goto init_done;
+	}
+	mt9e013_ctrl->fps_divider = 1 * 0x00000400;
+	mt9e013_ctrl->pict_fps_divider = 1 * 0x00000400;
+	mt9e013_ctrl->set_test = TEST_OFF;
+	mt9e013_ctrl->prev_res = QTR_SIZE;
+	mt9e013_ctrl->pict_res = FULL_SIZE;
+
+	if (data)
+		mt9e013_ctrl->sensordata = data;
+	if (rc < 0) {
+		CDBG("Calling mt9e013_sensor_open_init fail1\n");
+		return rc;
+	}
+	CDBG("%s: %d\n", __func__, __LINE__);
+	/* enable mclk first */
+	msm_camio_clk_rate_set(MT9E013_MASTER_CLK_RATE);
+	rc = mt9e013_probe_init_sensor(data);
+	if (rc < 0)
+		goto init_fail;
+
+	CDBG("init settings\n");
+	rc = mt9e013_sensor_setting(REG_INIT, mt9e013_ctrl->prev_res);
+	mt9e013_ctrl->fps = 30*Q8;
+	mt9e013_init_focus();
+	if (rc < 0) {
+		gpio_set_value_cansleep(data->sensor_reset, 0);
+		goto init_fail;
+	} else
+		goto init_done;
+init_fail:
+	CDBG("init_fail\n");
+	mt9e013_probe_init_done(data);
+init_done:
+	CDBG("init_done\n");
+	return rc;
+} /*endof mt9e013_sensor_open_init*/
+
+static int mt9e013_init_client(struct i2c_client *client)
+{
+	/* Initialize the MSM_CAMI2C Chip */
+	init_waitqueue_head(&mt9e013_wait_queue);
+	return 0;
+}
+
+static const struct i2c_device_id mt9e013_i2c_id[] = {
+	{"mt9e013", 0},
+	{ }
+};
+
+static int mt9e013_i2c_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	int rc = 0;
+	CDBG("mt9e013_probe called!\n");
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		CDBG("i2c_check_functionality failed\n");
+		goto probe_failure;
+	}
+
+	mt9e013_sensorw = kzalloc(sizeof(struct mt9e013_work_t), GFP_KERNEL);
+	if (!mt9e013_sensorw) {
+		CDBG("kzalloc failed.\n");
+		rc = -ENOMEM;
+		goto probe_failure;
+	}
+
+	i2c_set_clientdata(client, mt9e013_sensorw);
+	mt9e013_init_client(client);
+	mt9e013_client = client;
+
+
+	CDBG("mt9e013_probe successed! rc = %d\n", rc);
+	return 0;
+
+probe_failure:
+	CDBG("mt9e013_probe failed! rc = %d\n", rc);
+	return rc;
+}
+
+static int mt9e013_send_wb_info(struct wb_info_cfg *wb)
+{
+	return 0;
+
+} /*end of mt9e013_snapshot_config*/
+
+static int __exit mt9e013_remove(struct i2c_client *client)
+{
+	struct mt9e013_work_t_t *sensorw = i2c_get_clientdata(client);
+	free_irq(client->irq, sensorw);
+	mt9e013_client = NULL;
+	kfree(sensorw);
+	return 0;
+}
+
+static struct i2c_driver mt9e013_i2c_driver = {
+	.id_table = mt9e013_i2c_id,
+	.probe  = mt9e013_i2c_probe,
+	.remove = __exit_p(mt9e013_i2c_remove),
+	.driver = {
+		.name = "mt9e013",
+	},
+};
+
+int mt9e013_sensor_config(void __user *argp)
+{
+	struct sensor_cfg_data cdata;
+	long   rc = 0;
+	if (copy_from_user(&cdata,
+		(void *)argp,
+		sizeof(struct sensor_cfg_data)))
+		return -EFAULT;
+	mutex_lock(&mt9e013_mut);
+	CDBG("mt9e013_sensor_config: cfgtype = %d\n",
+	cdata.cfgtype);
+		switch (cdata.cfgtype) {
+		case CFG_GET_PICT_FPS:
+			mt9e013_get_pict_fps(
+				cdata.cfg.gfps.prevfps,
+				&(cdata.cfg.gfps.pictfps));
+
+			if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+				rc = -EFAULT;
+			break;
+
+		case CFG_GET_PREV_L_PF:
+			cdata.cfg.prevl_pf =
+			mt9e013_get_prev_lines_pf();
+
+			if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+				rc = -EFAULT;
+			break;
+
+		case CFG_GET_PREV_P_PL:
+			cdata.cfg.prevp_pl =
+				mt9e013_get_prev_pixels_pl();
+
+			if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+				rc = -EFAULT;
+			break;
+
+		case CFG_GET_PICT_L_PF:
+			cdata.cfg.pictl_pf =
+				mt9e013_get_pict_lines_pf();
+
+			if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+				rc = -EFAULT;
+			break;
+
+		case CFG_GET_PICT_P_PL:
+			cdata.cfg.pictp_pl =
+				mt9e013_get_pict_pixels_pl();
+
+			if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+				rc = -EFAULT;
+			break;
+
+		case CFG_GET_PICT_MAX_EXP_LC:
+			cdata.cfg.pict_max_exp_lc =
+				mt9e013_get_pict_max_exp_lc();
+
+			if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+				rc = -EFAULT;
+			break;
+
+		case CFG_SET_FPS:
+		case CFG_SET_PICT_FPS:
+			rc = mt9e013_set_fps(&(cdata.cfg.fps));
+			break;
+
+		case CFG_SET_EXP_GAIN:
+			rc =
+				mt9e013_write_exp_gain(
+					cdata.cfg.exp_gain.gain,
+					cdata.cfg.exp_gain.line);
+			break;
+
+		case CFG_SET_PICT_EXP_GAIN:
+			rc =
+				mt9e013_set_pict_exp_gain(
+				cdata.cfg.exp_gain.gain,
+				cdata.cfg.exp_gain.line);
+			break;
+
+		case CFG_SET_MODE:
+			rc = mt9e013_set_sensor_mode(cdata.mode,
+					cdata.rs);
+			break;
+
+		case CFG_PWR_DOWN:
+			rc = mt9e013_power_down();
+			break;
+
+		case CFG_MOVE_FOCUS:
+			rc =
+				mt9e013_move_focus(
+				cdata.cfg.focus.dir,
+				cdata.cfg.focus.steps);
+			break;
+
+		case CFG_SET_DEFAULT_FOCUS:
+			rc =
+				mt9e013_set_default_focus(
+				cdata.cfg.focus.steps);
+			break;
+
+		case CFG_GET_AF_MAX_STEPS:
+			cdata.max_steps = MT9E013_TOTAL_STEPS_NEAR_TO_FAR;
+			if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+				rc = -EFAULT;
+			break;
+
+		case CFG_SET_EFFECT:
+			rc = mt9e013_set_default_focus(
+				cdata.cfg.effect);
+			break;
+
+
+		case CFG_SEND_WB_INFO:
+			rc = mt9e013_send_wb_info(
+				&(cdata.cfg.wb_info));
+			break;
+
+		default:
+			rc = -EFAULT;
+			break;
+		}
+
+	mutex_unlock(&mt9e013_mut);
+
+	return rc;
+}
+
+static int mt9e013_sensor_release(void)
+{
+	int rc = -EBADF;
+	mutex_lock(&mt9e013_mut);
+	mt9e013_power_down();
+	gpio_set_value_cansleep(mt9e013_ctrl->sensordata->sensor_reset, 0);
+	msleep(5);
+	gpio_free(mt9e013_ctrl->sensordata->sensor_reset);
+	kfree(mt9e013_ctrl);
+	mt9e013_ctrl = NULL;
+	CDBG("mt9e013_release completed\n");
+	mutex_unlock(&mt9e013_mut);
+
+	return rc;
+}
+
+static int mt9e013_sensor_probe(const struct msm_camera_sensor_info *info,
+		struct msm_sensor_ctrl *s)
+{
+	int rc = 0;
+	rc = i2c_add_driver(&mt9e013_i2c_driver);
+	if (rc < 0 || mt9e013_client == NULL) {
+		rc = -ENOTSUPP;
+		CDBG("I2C add driver failed");
+		goto probe_fail;
+	}
+	msm_camio_clk_rate_set(MT9E013_MASTER_CLK_RATE);
+	rc = mt9e013_probe_init_sensor(info);
+	if (rc < 0)
+		goto probe_fail;
+	s->s_init = mt9e013_sensor_open_init;
+	s->s_release = mt9e013_sensor_release;
+	s->s_config  = mt9e013_sensor_config;
+	s->s_mount_angle = info->sensor_platform_info->mount_angle;
+	gpio_set_value_cansleep(info->sensor_reset, 0);
+	mt9e013_probe_init_done(info);
+	return rc;
+
+probe_fail:
+	CDBG("mt9e013_sensor_probe: SENSOR PROBE FAILS!\n");
+	return rc;
+}
+
+static int __mt9e013_probe(struct platform_device *pdev)
+{
+	return msm_camera_drv_start(pdev, mt9e013_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+	.probe = __mt9e013_probe,
+	.driver = {
+		.name = "msm_camera_mt9e013",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init mt9e013_init(void)
+{
+	return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(mt9e013_init);
+void mt9e013_exit(void)
+{
+	i2c_del_driver(&mt9e013_i2c_driver);
+}
+MODULE_DESCRIPTION("Aptina 8 MP Bayer sensor driver");
+MODULE_LICENSE("GPL v2");
+
+static bool streaming = 1;
+
+static int mt9e013_focus_test(void *data, u64 *val)
+{
+	int i = 0;
+	mt9e013_set_default_focus(0);
+
+	for (i = 90; i < 256; i++) {
+		mt9e013_i2c_write_w_sensor(REG_VCM_NEW_CODE, i);
+		msleep(5000);
+	}
+	msleep(5000);
+	for (i = 255; i > 90; i--) {
+		mt9e013_i2c_write_w_sensor(REG_VCM_NEW_CODE, i);
+		msleep(5000);
+	}
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(cam_focus, mt9e013_focus_test,
+			NULL, "%lld\n");
+
+static int mt9e013_step_test(void *data, u64 *val)
+{
+	int i = 0;
+	mt9e013_set_default_focus(0);
+
+	for (i = 0; i < MT9E013_TOTAL_STEPS_NEAR_TO_FAR; i++) {
+		mt9e013_move_focus(MOVE_NEAR, 1);
+		msleep(5000);
+	}
+
+	mt9e013_move_focus(MOVE_FAR, MT9E013_TOTAL_STEPS_NEAR_TO_FAR);
+	msleep(5000);
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(cam_step, mt9e013_step_test,
+			NULL, "%lld\n");
+
+static int cam_debug_stream_set(void *data, u64 val)
+{
+	int rc = 0;
+
+	if (val) {
+		mt9e013_start_stream();
+		streaming = 1;
+	} else {
+		mt9e013_stop_stream();
+		streaming = 0;
+	}
+
+	return rc;
+}
+
+static int cam_debug_stream_get(void *data, u64 *val)
+{
+	*val = streaming;
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(cam_stream, cam_debug_stream_get,
+			cam_debug_stream_set, "%llu\n");
+
+
+static int cam_debug_init(void)
+{
+	struct dentry *cam_dir;
+	debugfs_base = debugfs_create_dir("sensor", NULL);
+	if (!debugfs_base)
+		return -ENOMEM;
+
+	cam_dir = debugfs_create_dir("mt9e013", debugfs_base);
+	if (!cam_dir)
+		return -ENOMEM;
+
+	if (!debugfs_create_file("focus", S_IRUGO | S_IWUSR, cam_dir,
+							 NULL, &cam_focus))
+		return -ENOMEM;
+	if (!debugfs_create_file("step", S_IRUGO | S_IWUSR, cam_dir,
+							 NULL, &cam_step))
+		return -ENOMEM;
+	if (!debugfs_create_file("stream", S_IRUGO | S_IWUSR, cam_dir,
+							 NULL, &cam_stream))
+		return -ENOMEM;
+
+	return 0;
+}
+
+
+
diff --git a/drivers/media/video/msm/mt9e013.h b/drivers/media/video/msm/mt9e013.h
new file mode 100644
index 0000000..9052a35
--- /dev/null
+++ b/drivers/media/video/msm/mt9e013.h
@@ -0,0 +1,174 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef MT9E013_H
+#define MT9E013_H
+#include <linux/types.h>
+#include <mach/board.h>
+extern struct mt9e013_reg mt9e013_regs;
+struct reg_struct_init {
+	uint8_t reg_0x0112;      /* 0x0112*/
+	uint8_t reg_0x0113;      /* 0x0113*/
+	uint8_t vt_pix_clk_div;  /* 0x0301*/
+	uint8_t pre_pll_clk_div; /* 0x0305*/
+	uint8_t pll_multiplier;  /* 0x0307*/
+	uint8_t op_pix_clk_div;  /* 0x0309*/
+	uint8_t reg_0x3030;      /*0x3030*/
+	uint8_t reg_0x0111;      /*0x0111*/
+	uint8_t reg_0x0b00;      /*0x0b00*/
+	uint8_t reg_0x3001;      /*0x3001*/
+	uint8_t reg_0x3004;      /*0x3004*/
+	uint8_t reg_0x3007;      /*0x3007*/
+	uint8_t reg_0x3016;      /*0x3016*/
+	uint8_t reg_0x301d;      /*0x301d*/
+	uint8_t reg_0x317e;      /*0x317E*/
+	uint8_t reg_0x317f;      /*0x317F*/
+	uint8_t reg_0x3400;      /*0x3400*/
+	uint8_t reg_0x0b06;      /*0x0b06*/
+	uint8_t reg_0x0b07;      /*0x0b07*/
+	uint8_t reg_0x0b08;      /*0x0b08*/
+	uint8_t reg_0x0b09;      /*0x0b09*/
+	uint8_t reg_0x0136;
+	uint8_t reg_0x0137;
+	/* Edof */
+	uint8_t reg_0x0b83;      /*0x0b83*/
+	uint8_t reg_0x0b84;      /*0x0b84*/
+	uint8_t reg_0x0b85;      /*0x0b85*/
+	uint8_t reg_0x0b88;      /*0x0b88*/
+	uint8_t reg_0x0b89;      /*0x0b89*/
+	uint8_t reg_0x0b8a;      /*0x0b8a*/
+	};
+struct reg_struct {
+	uint8_t coarse_integration_time_hi; /*REG_COARSE_INTEGRATION_TIME_HI*/
+	uint8_t coarse_integration_time_lo; /*REG_COARSE_INTEGRATION_TIME_LO*/
+	uint8_t analogue_gain_code_global;
+	uint8_t frame_length_lines_hi; /* 0x0340*/
+	uint8_t frame_length_lines_lo; /* 0x0341*/
+	uint8_t line_length_pck_hi;    /* 0x0342*/
+	uint8_t line_length_pck_lo;    /* 0x0343*/
+	uint8_t reg_0x3005;   /* 0x3005*/
+	uint8_t reg_0x3010;  /* 0x3010*/
+	uint8_t reg_0x3011;  /* 0x3011*/
+	uint8_t reg_0x301a;  /* 0x301a*/
+	uint8_t reg_0x3035;  /* 0x3035*/
+	uint8_t reg_0x3036;   /* 0x3036*/
+	uint8_t reg_0x3041;  /*0x3041*/
+	uint8_t reg_0x3042;  /*0x3042*/
+	uint8_t reg_0x3045;  /*0x3045*/
+	uint8_t reg_0x0b80;   /* 0x0b80*/
+	uint8_t reg_0x0900;   /*0x0900*/
+	uint8_t reg_0x0901;   /* 0x0901*/
+	uint8_t reg_0x0902;   /*0x0902*/
+	uint8_t reg_0x0383;   /*0x0383*/
+	uint8_t reg_0x0387;   /* 0x0387*/
+	uint8_t reg_0x034c;   /* 0x034c*/
+	uint8_t reg_0x034d;   /*0x034d*/
+	uint8_t reg_0x034e;   /* 0x034e*/
+	uint8_t reg_0x034f;   /* 0x034f*/
+	uint8_t reg_0x1716; /*0x1716*/
+	uint8_t reg_0x1717; /*0x1717*/
+	uint8_t reg_0x1718; /*0x1718*/
+	uint8_t reg_0x1719; /*0x1719*/
+	uint8_t reg_0x3210;/*0x3210*/
+	uint8_t reg_0x111; /*0x111*/
+	uint8_t reg_0x3410;  /*0x3410*/
+	uint8_t reg_0x3098;
+	uint8_t reg_0x309D;
+	uint8_t reg_0x0200;
+	uint8_t reg_0x0201;
+	};
+struct mt9e013_i2c_reg_conf {
+	unsigned short waddr;
+	unsigned short wdata;
+};
+
+enum mt9e013_test_mode_t {
+	TEST_OFF,
+	TEST_1,
+	TEST_2,
+	TEST_3
+};
+
+enum mt9e013_resolution_t {
+	QTR_SIZE,
+	FULL_SIZE,
+	HFR_60FPS,
+	HFR_90FPS,
+	HFR_120FPS,
+	INVALID_SIZE
+};
+enum mt9e013_setting {
+	RES_PREVIEW,
+	RES_CAPTURE
+};
+enum mt9e013_reg_update {
+	/* Sensor egisters that need to be updated during initialization */
+	REG_INIT,
+	/* Sensor egisters that needs periodic I2C writes */
+	UPDATE_PERIODIC,
+	/* All the sensor Registers will be updated */
+	UPDATE_ALL,
+	/* Not valid update */
+	UPDATE_INVALID
+};
+
+enum mt9e013_reg_pll {
+	E013_VT_PIX_CLK_DIV,
+	E013_VT_SYS_CLK_DIV,
+	E013_PRE_PLL_CLK_DIV,
+	E013_PLL_MULTIPLIER,
+	E013_OP_PIX_CLK_DIV,
+	E013_OP_SYS_CLK_DIV
+};
+
+enum mt9e013_reg_mode {
+	E013_X_ADDR_START,
+	E013_X_ADDR_END,
+	E013_Y_ADDR_START,
+	E013_Y_ADDR_END,
+	E013_X_OUTPUT_SIZE,
+	E013_Y_OUTPUT_SIZE,
+	E013_DATAPATH_SELECT,
+	E013_READ_MODE,
+	E013_ANALOG_CONTROL5,
+	E013_DAC_LD_4_5,
+	E013_SCALING_MODE,
+	E013_SCALE_M,
+	E013_LINE_LENGTH_PCK,
+	E013_FRAME_LENGTH_LINES,
+	E013_COARSE_INTEGRATION_TIME,
+	E013_FINE_INTEGRATION_TIME,
+	E013_FINE_CORRECTION
+};
+
+struct mt9e013_reg {
+	const struct mt9e013_i2c_reg_conf *reg_mipi;
+	const unsigned short reg_mipi_size;
+	const struct mt9e013_i2c_reg_conf *rec_settings;
+	const unsigned short rec_size;
+	const struct mt9e013_i2c_reg_conf *reg_pll;
+	const unsigned short reg_pll_size;
+	const struct mt9e013_i2c_reg_conf *reg_pll_60fps;
+	const unsigned short reg_pll_60fps_size;
+	const struct mt9e013_i2c_reg_conf *reg_pll_120fps;
+	const unsigned short reg_pll_120fps_size;
+	const struct mt9e013_i2c_reg_conf *reg_prev;
+	const unsigned short reg_prev_size;
+	const struct mt9e013_i2c_reg_conf *reg_snap;
+	const unsigned short reg_snap_size;
+	const struct mt9e013_i2c_reg_conf *reg_60fps;
+	const unsigned short reg_60fps_size;
+	const struct mt9e013_i2c_reg_conf *reg_120fps;
+	const unsigned short reg_120fps_size;
+};
+#endif /* MT9E013_H */
diff --git a/drivers/media/video/msm/mt9e013_reg.c b/drivers/media/video/msm/mt9e013_reg.c
new file mode 100644
index 0000000..9a4bd7e
--- /dev/null
+++ b/drivers/media/video/msm/mt9e013_reg.c
@@ -0,0 +1,234 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+
+#include "mt9e013.h"
+
+static struct mt9e013_i2c_reg_conf mipi_settings[] = {
+	/*Disable embedded data*/
+	{0x3064, 0x7800},/*SMIA_TEST*/
+	/*configure 2-lane MIPI*/
+	{0x31AE, 0x0202},/*SERIAL_FORMAT*/
+	{0x31B8, 0x0E3F},/*MIPI_TIMING_2*/
+	/*set data to RAW10 format*/
+	{0x0112, 0x0A0A},/*CCP_DATA_FORMAT*/
+	{0x30F0, 0x8000},/*VCM CONTROL*/
+};
+
+/*PLL Configuration
+(Ext=24MHz, vt_pix_clk=174MHz, op_pix_clk=69.6MHz)*/
+static struct mt9e013_i2c_reg_conf pll_settings[] = {
+	{0x0300, 0x0004},/*VT_PIX_CLK_DIV*/
+	{0x0302, 0x0001},/*VT_SYS_CLK_DIV*/
+	{0x0304, 0x0002},/*PRE_PLL_CLK_DIV*/
+	{0x0306, 0x003A},/*PLL_MULTIPLIER*/
+	{0x0308, 0x000A},/*OP_PIX_CLK_DIV*/
+	{0x030A, 0x0001},/*OP_SYS_CLK_DIV*/
+};
+
+static struct mt9e013_i2c_reg_conf prev_settings[] = {
+	/*Output Size (1632x1224)*/
+	{0x0344, 0x0008},/*X_ADDR_START*/
+	{0x0348, 0x0CC9},/*X_ADDR_END*/
+	{0x0346, 0x0008},/*Y_ADDR_START*/
+	{0x034A, 0x0999},/*Y_ADDR_END*/
+	{0x034C, 0x0660},/*X_OUTPUT_SIZE*/
+	{0x034E, 0x04C8},/*Y_OUTPUT_SIZE*/
+	{0x306E, 0xFCB0},/*DATAPATH_SELECT*/
+	{0x3040, 0x04C3},/*READ_MODE*/
+	{0x3178, 0x0000},/*ANALOG_CONTROL5*/
+	{0x3ED0, 0x1E24},/*DAC_LD_4_5*/
+	{0x0400, 0x0002},/*SCALING_MODE*/
+	{0x0404, 0x0010},/*SCALE_M*/
+	/*Timing configuration*/
+	{0x0342, 0x1018},/*LINE_LENGTH_PCK*/
+	{0x0340, 0x055B},/*FRAME_LENGTH_LINES*/
+	{0x0202, 0x0557},/*COARSE_INTEGRATION_TIME*/
+	{0x3014, 0x0846},/*FINE_INTEGRATION_TIME_*/
+	{0x3010, 0x0130},/*FINE_CORRECTION*/
+};
+
+static struct mt9e013_i2c_reg_conf snap_settings[] = {
+	/*Output Size (3264x2448)*/
+	{0x0344, 0x0008},/*X_ADDR_START */
+	{0x0348, 0x0CD7},/*X_ADDR_END*/
+	{0x0346, 0x0008},/*Y_ADDR_START */
+	{0x034A, 0x09A7},/*Y_ADDR_END*/
+	{0x034C, 0x0CD0},/*X_OUTPUT_SIZE*/
+	{0x034E, 0x09A0},/*Y_OUTPUT_SIZE*/
+	{0x306E, 0xFC80},/*DATAPATH_SELECT*/
+	{0x3040, 0x0041},/*READ_MODE*/
+	{0x3178, 0x0000},/*ANALOG_CONTROL5*/
+	{0x3ED0, 0x1E24},/*DAC_LD_4_5*/
+	{0x0400, 0x0000},/*SCALING_MODE*/
+	{0x0404, 0x0010},/*SCALE_M*/
+	/*Timing configuration*/
+	{0x0342, 0x13F8},/*LINE_LENGTH_PCK*/
+	{0x0340, 0x0A2F},/*FRAME_LENGTH_LINES*/
+	{0x0202, 0x0A1F},/*COARSE_INTEGRATION_TIME*/
+	{0x3014, 0x03F6},/*FINE_INTEGRATION_TIME_ */
+	{0x3010, 0x0078},/*FINE_CORRECTION*/
+};
+
+static struct mt9e013_i2c_reg_conf pll_settings_60fps[] = {
+	{0x0300, 0x0004},/*VT_PIX_CLK_DIV*/
+	{0x0302, 0x0001},/*VT_SYS_CLK_DIV*/
+	{0x0304, 0x0002},/*PRE_PLL_CLK_DIV*/
+	{0x0306, 0x0042},/*PLL_MULTIPLIER*/
+	{0x0308, 0x000A},/*OP_PIX_CLK_DIV*/
+	{0x030A, 0x0001},/*OP_SYS_CLK_DIV*/
+};
+
+static struct mt9e013_i2c_reg_conf prev_settings_60fps[] = {
+	/*Output Size (1632x1224)*/
+	{0x0344, 0x0008},/*X_ADDR_START*/
+	{0x0348, 0x0CC5},/*X_ADDR_END*/
+	{0x0346, 0x013a},/*Y_ADDR_START*/
+	{0x034A, 0x0863},/*Y_ADDR_END*/
+	{0x034C, 0x0660},/*X_OUTPUT_SIZE*/
+	{0x034E, 0x0396},/*Y_OUTPUT_SIZE*/
+	{0x306E, 0xFC80},/*DATAPATH_SELECT*/
+	{0x3040, 0x00C3},/*READ_MODE*/
+	{0x3178, 0x0000},/*ANALOG_CONTROL5*/
+	{0x3ED0, 0x1E24},/*DAC_LD_4_5*/
+	{0x0400, 0x0000},/*SCALING_MODE*/
+	{0x0404, 0x0010},/*SCALE_M*/
+	/*Timing configuration*/
+	{0x0342, 0x0BE8},/*LINE_LENGTH_PCK*/
+	{0x0340, 0x0425},/*FRAME_LENGTH_LINES*/
+	{0x0202, 0x0425},/*COARSE_INTEGRATION_TIME*/
+	{0x3014, 0x03F6},/*FINE_INTEGRATION_TIME_*/
+	{0x3010, 0x0078},/*FINE_CORRECTION*/
+};
+
+static struct mt9e013_i2c_reg_conf pll_settings_120fps[] = {
+	{0x0300, 0x0005},/*VT_PIX_CLK_DIV*/
+	{0x0302, 0x0001},/*VT_SYS_CLK_DIV*/
+	{0x0304, 0x0002},/*PRE_PLL_CLK_DIV*/
+	{0x0306, 0x0052},/*PLL_MULTIPLIER*/
+	{0x0308, 0x000A},/*OP_PIX_CLK_DIV*/
+	{0x030A, 0x0001},/*OP_SYS_CLK_DIV*/
+};
+
+static struct mt9e013_i2c_reg_conf prev_settings_120fps[] = {
+	{0x0344, 0x0008},/*X_ADDR_START*/
+	{0x0348, 0x0685},/*X_ADDR_END*/
+	{0x0346, 0x013a},/*Y_ADDR_START*/
+	{0x034A, 0x055B},/*Y_ADDR_END*/
+	{0x034C, 0x0340},/*X_OUTPUT_SIZE*/
+	{0x034E, 0x0212},/*Y_OUTPUT_SIZE*/
+	{0x306E, 0xFC80},/*DATAPATH_SELECT*/
+	{0x3040, 0x00C3},/*READ_MODE*/
+	{0x3178, 0x0000},/*ANALOG_CONTROL5*/
+	{0x3ED0, 0x1E24},/*DAC_LD_4_5*/
+	{0x0400, 0x0000},/*SCALING_MODE*/
+	{0x0404, 0x0010},/*SCALE_M*/
+	/*Timing configuration*/
+	{0x0342, 0x0970},/*LINE_LENGTH_PCK*/
+	{0x0340, 0x02A1},/*FRAME_LENGTH_LINES*/
+	{0x0202, 0x02A1},/*COARSE_INTEGRATION_TIME*/
+	{0x3014, 0x03F6},/*FINE_INTEGRATION_TIME_*/
+	{0x3010, 0x0078},/*FINE_CORRECTION*/
+};
+
+static struct mt9e013_i2c_reg_conf recommend_settings[] = {
+	{0x3044, 0x0590},
+	{0x306E, 0xFC80},
+	{0x30B2, 0xC000},
+	{0x30D6, 0x0800},
+	{0x316C, 0xB42F},
+	{0x316E, 0x869C},
+	{0x3170, 0x210E},
+	{0x317A, 0x010E},
+	{0x31E0, 0x1FB9},
+	{0x31E6, 0x07FC},
+	{0x37C0, 0x0000},
+	{0x37C2, 0x0000},
+	{0x37C4, 0x0000},
+	{0x37C6, 0x0000},
+	{0x3E02, 0x8801},
+	{0x3E04, 0x2301},
+	{0x3E06, 0x8449},
+	{0x3E08, 0x6841},
+	{0x3E0A, 0x400C},
+	{0x3E0C, 0x1001},
+	{0x3E0E, 0x2103},
+	{0x3E10, 0x4B41},
+	{0x3E12, 0x4B26},
+	{0x3E16, 0x8802},
+	{0x3E18, 0x84FF},
+	{0x3E1A, 0x8601},
+	{0x3E1C, 0x8401},
+	{0x3E1E, 0x840A},
+	{0x3E20, 0xFF00},
+	{0x3E22, 0x8401},
+	{0x3E24, 0x00FF},
+	{0x3E26, 0x0088},
+	{0x3E28, 0x2E8A},
+	{0x3E32, 0x8801},
+	{0x3E34, 0x4024},
+	{0x3E38, 0x8469},
+	{0x3E3C, 0x2301},
+	{0x3E3E, 0x3E25},
+	{0x3E40, 0x1C01},
+	{0x3E42, 0x8486},
+	{0x3E44, 0x8401},
+	{0x3E46, 0x00FF},
+	{0x3E48, 0x8401},
+	{0x3E4A, 0x8601},
+	{0x3E4C, 0x8402},
+	{0x3E4E, 0x00FF},
+	{0x3E50, 0x6623},
+	{0x3E52, 0x8340},
+	{0x3E54, 0x00FF},
+	{0x3E56, 0x4A42},
+	{0x3E58, 0x2203},
+	{0x3E5A, 0x674D},
+	{0x3E5C, 0x3F25},
+	{0x3E5E, 0x846A},
+	{0x3E60, 0x4C01},
+	{0x3E62, 0x8401},
+	{0x3E66, 0x3901},
+	{0x3ECC, 0x00EB},
+	{0x3ED0, 0x1E24},
+	{0x3ED4, 0xAFC4},
+	{0x3ED6, 0x909B},
+	{0x3ED8, 0x0006},
+	{0x3EDA, 0xCFC6},
+	{0x3EDC, 0x4FE4},
+	{0x3EE0, 0x2424},
+	{0x3EE2, 0x9797},
+	{0x3EE4, 0xC100},
+	{0x3EE6, 0x0540}
+};
+
+struct mt9e013_reg mt9e013_regs = {
+	.reg_mipi = &mipi_settings[0],
+	.reg_mipi_size = ARRAY_SIZE(mipi_settings),
+	.rec_settings = &recommend_settings[0],
+	.rec_size = ARRAY_SIZE(recommend_settings),
+	.reg_pll = &pll_settings[0],
+	.reg_pll_size = ARRAY_SIZE(pll_settings),
+	.reg_prev = &prev_settings[0],
+	.reg_pll_60fps = &pll_settings_60fps[0],
+	.reg_pll_60fps_size = ARRAY_SIZE(pll_settings_60fps),
+	.reg_pll_120fps = &pll_settings_120fps[0],
+	.reg_pll_120fps_size = ARRAY_SIZE(pll_settings_120fps),
+	.reg_prev_size = ARRAY_SIZE(prev_settings),
+	.reg_snap = &snap_settings[0],
+	.reg_snap_size = ARRAY_SIZE(snap_settings),
+	.reg_60fps = &prev_settings_60fps[0],
+	.reg_60fps_size = ARRAY_SIZE(prev_settings_60fps),
+	.reg_120fps = &prev_settings_120fps[0],
+	.reg_120fps_size = ARRAY_SIZE(prev_settings_120fps),
+};
diff --git a/drivers/media/video/msm/mt9p012.h b/drivers/media/video/msm/mt9p012.h
new file mode 100644
index 0000000..0579813
--- /dev/null
+++ b/drivers/media/video/msm/mt9p012.h
@@ -0,0 +1,59 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef MT9T012_H
+#define MT9T012_H
+
+#include <linux/types.h>
+
+extern struct mt9p012_reg mt9p012_regs;	/* from mt9p012_reg.c */
+
+struct reg_struct {
+	uint16_t vt_pix_clk_div;     /* 0x0300 */
+	uint16_t vt_sys_clk_div;     /* 0x0302 */
+	uint16_t pre_pll_clk_div;    /* 0x0304 */
+	uint16_t pll_multiplier;     /* 0x0306 */
+	uint16_t op_pix_clk_div;     /* 0x0308 */
+	uint16_t op_sys_clk_div;     /* 0x030A */
+	uint16_t scale_m;            /* 0x0404 */
+	uint16_t row_speed;          /* 0x3016 */
+	uint16_t x_addr_start;       /* 0x3004 */
+	uint16_t x_addr_end;         /* 0x3008 */
+	uint16_t y_addr_start;       /* 0x3002 */
+	uint16_t y_addr_end;         /* 0x3006 */
+	uint16_t read_mode;          /* 0x3040 */
+	uint16_t x_output_size ;     /* 0x034C */
+	uint16_t y_output_size;      /* 0x034E */
+	uint16_t line_length_pck;    /* 0x300C */
+	uint16_t frame_length_lines; /* 0x300A */
+	uint16_t coarse_int_time;    /* 0x3012 */
+	uint16_t fine_int_time;      /* 0x3014 */
+};
+
+
+struct mt9p012_i2c_reg_conf {
+	unsigned short waddr;
+	unsigned short wdata;
+};
+
+
+struct mt9p012_reg {
+	struct reg_struct const *reg_pat;
+	uint16_t reg_pat_size;
+	struct mt9p012_i2c_reg_conf const *ttbl;
+	uint16_t ttbl_size;
+	struct mt9p012_i2c_reg_conf const *rftbl;
+	uint16_t rftbl_size;
+};
+
+#endif /* MT9T012_H */
diff --git a/drivers/media/video/msm/mt9p012_bam.c b/drivers/media/video/msm/mt9p012_bam.c
new file mode 100644
index 0000000..9197380
--- /dev/null
+++ b/drivers/media/video/msm/mt9p012_bam.c
@@ -0,0 +1,1426 @@
+/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/kernel.h>
+#include <media/msm_camera.h>
+#include <mach/gpio.h>
+#include <mach/camera.h>
+#include "mt9p012.h"
+
+/*=============================================================
+    SENSOR REGISTER DEFINES
+==============================================================*/
+#define MT9P012_REG_MODEL_ID         0x0000
+#define MT9P012_MODEL_ID             0x2801
+#define REG_GROUPED_PARAMETER_HOLD   0x0104
+#define GROUPED_PARAMETER_HOLD       0x0100
+#define GROUPED_PARAMETER_UPDATE     0x0000
+#define REG_COARSE_INT_TIME          0x3012
+#define REG_VT_PIX_CLK_DIV           0x0300
+#define REG_VT_SYS_CLK_DIV           0x0302
+#define REG_PRE_PLL_CLK_DIV          0x0304
+#define REG_PLL_MULTIPLIER           0x0306
+#define REG_OP_PIX_CLK_DIV           0x0308
+#define REG_OP_SYS_CLK_DIV           0x030A
+#define REG_SCALE_M                  0x0404
+#define REG_FRAME_LENGTH_LINES       0x300A
+#define REG_LINE_LENGTH_PCK          0x300C
+#define REG_X_ADDR_START             0x3004
+#define REG_Y_ADDR_START             0x3002
+#define REG_X_ADDR_END               0x3008
+#define REG_Y_ADDR_END               0x3006
+#define REG_X_OUTPUT_SIZE            0x034C
+#define REG_Y_OUTPUT_SIZE            0x034E
+#define REG_FINE_INTEGRATION_TIME    0x3014
+#define REG_ROW_SPEED                0x3016
+#define MT9P012_REG_RESET_REGISTER   0x301A
+#define MT9P012_RESET_REGISTER_PWON  0x10CC
+#define MT9P012_RESET_REGISTER_PWOFF 0x10C8
+#define REG_READ_MODE                0x3040
+#define REG_GLOBAL_GAIN              0x305E
+#define REG_TEST_PATTERN_MODE        0x3070
+
+#define MT9P012_REV_7
+
+enum mt9p012_test_mode {
+	TEST_OFF,
+	TEST_1,
+	TEST_2,
+	TEST_3
+};
+
+enum mt9p012_resolution {
+	QTR_SIZE,
+	FULL_SIZE,
+	INVALID_SIZE
+};
+
+enum mt9p012_reg_update {
+	/* Sensor egisters that need to be updated during initialization */
+	REG_INIT,
+	/* Sensor egisters that needs periodic I2C writes */
+	UPDATE_PERIODIC,
+	/* All the sensor Registers will be updated */
+	UPDATE_ALL,
+	/* Not valid update */
+	UPDATE_INVALID
+};
+
+enum mt9p012_setting {
+	RES_PREVIEW,
+	RES_CAPTURE
+};
+
+/* actuator's Slave Address */
+#define MT9P012_AF_I2C_ADDR   0x0A
+
+/* AF Total steps parameters */
+#define MT9P012_STEPS_NEAR_TO_CLOSEST_INF  20
+#define MT9P012_TOTAL_STEPS_NEAR_TO_FAR    20
+
+#define MT9P012_MU5M0_PREVIEW_DUMMY_PIXELS 0
+#define MT9P012_MU5M0_PREVIEW_DUMMY_LINES  0
+
+/* Time in milisecs for waiting for the sensor to reset.*/
+#define MT9P012_RESET_DELAY_MSECS   66
+
+/* for 20 fps preview */
+#define MT9P012_DEFAULT_CLOCK_RATE  24000000
+#define MT9P012_DEFAULT_MAX_FPS     26	/* ???? */
+
+struct mt9p012_work {
+	struct work_struct work;
+};
+static struct mt9p012_work *mt9p012_sensorw;
+static struct i2c_client *mt9p012_client;
+
+struct mt9p012_ctrl {
+	const struct msm_camera_sensor_info *sensordata;
+
+	int sensormode;
+	uint32_t fps_divider;	/* init to 1 * 0x00000400 */
+	uint32_t pict_fps_divider;	/* init to 1 * 0x00000400 */
+
+	uint16_t curr_lens_pos;
+	uint16_t init_curr_lens_pos;
+	uint16_t my_reg_gain;
+	uint32_t my_reg_line_count;
+
+	enum mt9p012_resolution prev_res;
+	enum mt9p012_resolution pict_res;
+	enum mt9p012_resolution curr_res;
+	enum mt9p012_test_mode set_test;
+};
+
+static uint16_t bam_macro, bam_infinite;
+static uint16_t bam_step_lookup_table[MT9P012_TOTAL_STEPS_NEAR_TO_FAR + 1];
+static uint16_t update_type = UPDATE_PERIODIC;
+static struct mt9p012_ctrl *mt9p012_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(mt9p012_wait_queue);
+DEFINE_MUTEX(mt9p012_mut);
+
+/*=============================================================*/
+
+static int mt9p012_i2c_rxdata(unsigned short saddr, int slength,
+			      unsigned char *rxdata, int rxlength)
+{
+	struct i2c_msg msgs[] = {
+		{
+			.addr = saddr,
+			.flags = 0,
+			.len = slength,
+			.buf = rxdata,
+		},
+		{
+			.addr = saddr,
+			.flags = I2C_M_RD,
+			.len = rxlength,
+			.buf = rxdata,
+		},
+	};
+
+	if (i2c_transfer(mt9p012_client->adapter, msgs, 2) < 0) {
+		CDBG("mt9p012_i2c_rxdata failed!\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+static int32_t mt9p012_i2c_read_b(unsigned short saddr, unsigned char raddr,
+				  unsigned short *rdata)
+{
+	int32_t rc = 0;
+	if (!rdata)
+		return -EIO;
+	rc = mt9p012_i2c_rxdata(saddr, 1, &raddr, 1);
+	if (rc < 0)
+		return rc;
+	*rdata = raddr;
+	if (rc < 0)
+		CDBG("mt9p012_i2c_read_b failed!\n");
+	return rc;
+}
+
+static int32_t mt9p012_i2c_read_w(unsigned short saddr, unsigned short raddr,
+				  unsigned short *rdata)
+{
+	int32_t rc = 0;
+	unsigned char buf[4];
+
+	if (!rdata)
+		return -EIO;
+
+	memset(buf, 0, sizeof(buf));
+
+	buf[0] = (raddr & 0xFF00) >> 8;
+	buf[1] = (raddr & 0x00FF);
+
+	rc = mt9p012_i2c_rxdata(saddr, 2, buf, 2);
+	if (rc < 0)
+		return rc;
+
+	*rdata = buf[0] << 8 | buf[1];
+
+	if (rc < 0)
+		CDBG("mt9p012_i2c_read failed!\n");
+
+	return rc;
+}
+
+static int32_t mt9p012_i2c_txdata(unsigned short saddr, unsigned char *txdata,
+				  int length)
+{
+	struct i2c_msg msg[] = {
+		{
+		 .addr = saddr,
+		 .flags = 0,
+		 .len = length,
+		 .buf = txdata,
+		 },
+	};
+
+	if (i2c_transfer(mt9p012_client->adapter, msg, 1) < 0) {
+		CDBG("mt9p012_i2c_txdata failed\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int32_t mt9p012_i2c_write_b(unsigned short saddr, unsigned short baddr,
+				   unsigned short bdata)
+{
+	int32_t rc = -EIO;
+	unsigned char buf[2];
+
+	memset(buf, 0, sizeof(buf));
+	buf[0] = baddr;
+	buf[1] = bdata;
+	rc = mt9p012_i2c_txdata(saddr, buf, 2);
+
+	if (rc < 0)
+		CDBG("i2c_write failed, saddr = 0x%x addr = 0x%x, val =0x%x!\n",
+		     saddr, baddr, bdata);
+
+	return rc;
+}
+
+static int32_t mt9p012_i2c_write_w(unsigned short saddr, unsigned short waddr,
+				   unsigned short wdata)
+{
+	int32_t rc = -EIO;
+	unsigned char buf[4];
+
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (waddr & 0xFF00) >> 8;
+	buf[1] = (waddr & 0x00FF);
+	buf[2] = (wdata & 0xFF00) >> 8;
+	buf[3] = (wdata & 0x00FF);
+
+	rc = mt9p012_i2c_txdata(saddr, buf, 4);
+
+	if (rc < 0)
+		CDBG("i2c_write_w failed, addr = 0x%x, val = 0x%x!\n",
+		     waddr, wdata);
+
+	return rc;
+}
+
+static int32_t mt9p012_i2c_write_w_table(struct mt9p012_i2c_reg_conf const
+					 *reg_conf_tbl, int num)
+{
+	int i;
+	int32_t rc = -EIO;
+
+	for (i = 0; i < num; i++) {
+		rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+					 reg_conf_tbl->waddr,
+					 reg_conf_tbl->wdata);
+		if (rc < 0)
+			break;
+		reg_conf_tbl++;
+	}
+
+	return rc;
+}
+
+static int32_t mt9p012_test(enum mt9p012_test_mode mo)
+{
+	int32_t rc = 0;
+
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+				 REG_GROUPED_PARAMETER_HOLD,
+				 GROUPED_PARAMETER_HOLD);
+	if (rc < 0)
+		return rc;
+
+	if (mo == TEST_OFF)
+		return 0;
+	else {
+		rc = mt9p012_i2c_write_w_table(mt9p012_regs.ttbl,
+					       mt9p012_regs.ttbl_size);
+		if (rc < 0)
+			return rc;
+
+		rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+					 REG_TEST_PATTERN_MODE, (uint16_t) mo);
+		if (rc < 0)
+			return rc;
+	}
+
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+				 REG_GROUPED_PARAMETER_HOLD,
+				 GROUPED_PARAMETER_UPDATE);
+	if (rc < 0)
+		return rc;
+
+	return rc;
+}
+
+static int32_t mt9p012_lens_shading_enable(uint8_t is_enable)
+{
+	int32_t rc = 0;
+
+	CDBG("%s: entered. enable = %d\n", __func__, is_enable);
+
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+				 REG_GROUPED_PARAMETER_HOLD,
+				 GROUPED_PARAMETER_HOLD);
+	if (rc < 0)
+		return rc;
+
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr, 0x3780,
+				 ((uint16_t) is_enable) << 15);
+	if (rc < 0)
+		return rc;
+
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+				 REG_GROUPED_PARAMETER_HOLD,
+				 GROUPED_PARAMETER_UPDATE);
+
+	CDBG("%s: exiting. rc = %d\n", __func__, rc);
+	return rc;
+}
+
+static int32_t mt9p012_set_lc(void)
+{
+	int32_t rc;
+
+	rc = mt9p012_i2c_write_w_table(mt9p012_regs.rftbl,
+				       mt9p012_regs.rftbl_size);
+
+	return rc;
+}
+
+static void mt9p012_get_pict_fps(uint16_t fps, uint16_t *pfps)
+{
+	/* input fps is preview fps in Q8 format */
+	uint32_t divider;	/*Q10 */
+	uint32_t pclk_mult;	/*Q10 */
+
+	if (mt9p012_ctrl->prev_res == QTR_SIZE) {
+		divider = (uint32_t)
+		    (((mt9p012_regs.reg_pat[RES_PREVIEW].frame_length_lines *
+		       mt9p012_regs.reg_pat[RES_PREVIEW].line_length_pck) *
+		      0x00000400) /
+		     (mt9p012_regs.reg_pat[RES_CAPTURE].frame_length_lines *
+		      mt9p012_regs.reg_pat[RES_CAPTURE].line_length_pck));
+
+		pclk_mult =
+		    (uint32_t) ((mt9p012_regs.reg_pat[RES_CAPTURE].
+				 pll_multiplier * 0x00000400) /
+				(mt9p012_regs.reg_pat[RES_PREVIEW].
+				 pll_multiplier));
+	} else {
+		/* full size resolution used for preview. */
+		divider = 0x00000400;	/*1.0 */
+		pclk_mult = 0x00000400;	/*1.0 */
+	}
+
+	/* Verify PCLK settings and frame sizes. */
+	*pfps = (uint16_t) (fps * divider * pclk_mult / 0x00000400 /
+			    0x00000400);
+}
+
+static uint16_t mt9p012_get_prev_lines_pf(void)
+{
+	if (mt9p012_ctrl->prev_res == QTR_SIZE)
+		return mt9p012_regs.reg_pat[RES_PREVIEW].frame_length_lines;
+	else
+		return mt9p012_regs.reg_pat[RES_CAPTURE].frame_length_lines;
+}
+
+static uint16_t mt9p012_get_prev_pixels_pl(void)
+{
+	if (mt9p012_ctrl->prev_res == QTR_SIZE)
+		return mt9p012_regs.reg_pat[RES_PREVIEW].line_length_pck;
+	else
+		return mt9p012_regs.reg_pat[RES_CAPTURE].line_length_pck;
+}
+
+static uint16_t mt9p012_get_pict_lines_pf(void)
+{
+	return mt9p012_regs.reg_pat[RES_CAPTURE].frame_length_lines;
+}
+
+static uint16_t mt9p012_get_pict_pixels_pl(void)
+{
+	return mt9p012_regs.reg_pat[RES_CAPTURE].line_length_pck;
+}
+
+static uint32_t mt9p012_get_pict_max_exp_lc(void)
+{
+	uint16_t snapshot_lines_per_frame;
+
+	if (mt9p012_ctrl->pict_res == QTR_SIZE)
+		snapshot_lines_per_frame =
+		    mt9p012_regs.reg_pat[RES_PREVIEW].frame_length_lines - 1;
+	else
+		snapshot_lines_per_frame =
+		    mt9p012_regs.reg_pat[RES_CAPTURE].frame_length_lines - 1;
+
+	return snapshot_lines_per_frame * 24;
+}
+
+static int32_t mt9p012_set_fps(struct fps_cfg *fps)
+{
+	/* input is new fps in Q10 format */
+	int32_t rc = 0;
+	enum mt9p012_setting setting;
+
+	mt9p012_ctrl->fps_divider = fps->fps_div;
+	mt9p012_ctrl->pict_fps_divider = fps->pict_fps_div;
+
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+				 REG_GROUPED_PARAMETER_HOLD,
+				 GROUPED_PARAMETER_HOLD);
+	if (rc < 0)
+		return -EBUSY;
+
+	if (mt9p012_ctrl->sensormode == SENSOR_PREVIEW_MODE)
+		setting = RES_PREVIEW;
+	else
+		setting = RES_CAPTURE;
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+			REG_FRAME_LENGTH_LINES,
+			(mt9p012_regs.reg_pat[setting].frame_length_lines *
+			fps->fps_div / 0x00000400));
+	if (rc < 0)
+		return rc;
+
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+				 REG_GROUPED_PARAMETER_HOLD,
+				 GROUPED_PARAMETER_UPDATE);
+
+	return rc;
+}
+
+static int32_t mt9p012_write_exp_gain(uint16_t gain, uint32_t line)
+{
+	uint16_t max_legal_gain = 0x01FF;
+	uint32_t line_length_ratio = 0x00000400;
+	enum mt9p012_setting setting;
+	int32_t rc = 0;
+
+	CDBG("Line:%d mt9p012_write_exp_gain \n", __LINE__);
+
+	if (mt9p012_ctrl->sensormode == SENSOR_PREVIEW_MODE) {
+		mt9p012_ctrl->my_reg_gain = gain;
+		mt9p012_ctrl->my_reg_line_count = (uint16_t) line;
+	}
+
+	if (gain > max_legal_gain) {
+		CDBG("Max legal gain Line:%d \n", __LINE__);
+		gain = max_legal_gain;
+	}
+
+	/* Verify no overflow */
+	if (mt9p012_ctrl->sensormode == SENSOR_PREVIEW_MODE) {
+		line = (uint32_t) (line * mt9p012_ctrl->fps_divider /
+				   0x00000400);
+		setting = RES_PREVIEW;
+	} else {
+		line = (uint32_t) (line * mt9p012_ctrl->pict_fps_divider /
+				   0x00000400);
+		setting = RES_CAPTURE;
+	}
+
+	/* Set digital gain to 1 */
+#ifdef MT9P012_REV_7
+	gain |= 0x1000;
+#else
+	gain |= 0x0200;
+#endif
+
+	if ((mt9p012_regs.reg_pat[setting].frame_length_lines - 1) < line) {
+		line_length_ratio = (uint32_t) (line * 0x00000400) /
+		    (mt9p012_regs.reg_pat[setting].frame_length_lines - 1);
+	} else
+		line_length_ratio = 0x00000400;
+
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr, REG_GLOBAL_GAIN, gain);
+	if (rc < 0) {
+		CDBG("mt9p012_i2c_write_w failed... Line:%d \n", __LINE__);
+		return rc;
+	}
+
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+				 REG_COARSE_INT_TIME, line);
+	if (rc < 0) {
+		CDBG("mt9p012_i2c_write_w failed... Line:%d \n", __LINE__);
+		return rc;
+	}
+
+	CDBG("mt9p012_write_exp_gain: gain = %d, line = %d\n", gain, line);
+
+	return rc;
+}
+
+static int32_t mt9p012_set_pict_exp_gain(uint16_t gain, uint32_t line)
+{
+	int32_t rc = 0;
+
+	CDBG("Line:%d mt9p012_set_pict_exp_gain \n", __LINE__);
+
+	rc = mt9p012_write_exp_gain(gain, line);
+	if (rc < 0) {
+		CDBG("Line:%d mt9p012_set_pict_exp_gain failed... \n",
+		     __LINE__);
+		return rc;
+	}
+
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+				 MT9P012_REG_RESET_REGISTER, 0x10CC | 0x0002);
+	if (rc < 0) {
+		CDBG("mt9p012_i2c_write_w failed... Line:%d \n", __LINE__);
+		return rc;
+	}
+
+	mdelay(5);
+
+	/* camera_timed_wait(snapshot_wait*exposure_ratio); */
+	return rc;
+}
+
+static int32_t mt9p012_setting(enum mt9p012_reg_update rupdate,
+			       enum mt9p012_setting rt)
+{
+	int32_t rc = 0;
+
+	switch (rupdate) {
+	case UPDATE_PERIODIC:
+		if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+			struct mt9p012_i2c_reg_conf ppc_tbl[] = {
+				{REG_GROUPED_PARAMETER_HOLD,
+				 GROUPED_PARAMETER_HOLD},
+				{REG_ROW_SPEED,
+				 mt9p012_regs.reg_pat[rt].row_speed},
+				{REG_X_ADDR_START,
+				 mt9p012_regs.reg_pat[rt].x_addr_start},
+				{REG_X_ADDR_END,
+				 mt9p012_regs.reg_pat[rt].x_addr_end},
+				{REG_Y_ADDR_START,
+				 mt9p012_regs.reg_pat[rt].y_addr_start},
+				{REG_Y_ADDR_END,
+				 mt9p012_regs.reg_pat[rt].y_addr_end},
+				{REG_READ_MODE,
+				 mt9p012_regs.reg_pat[rt].read_mode},
+				{REG_SCALE_M, mt9p012_regs.reg_pat[rt].scale_m},
+				{REG_X_OUTPUT_SIZE,
+				 mt9p012_regs.reg_pat[rt].x_output_size},
+				{REG_Y_OUTPUT_SIZE,
+				 mt9p012_regs.reg_pat[rt].y_output_size},
+
+				{REG_LINE_LENGTH_PCK,
+				 mt9p012_regs.reg_pat[rt].line_length_pck},
+				{REG_FRAME_LENGTH_LINES,
+				 (mt9p012_regs.reg_pat[rt].frame_length_lines *
+				  mt9p012_ctrl->fps_divider / 0x00000400)},
+				{REG_COARSE_INT_TIME,
+				 mt9p012_regs.reg_pat[rt].coarse_int_time},
+				{REG_FINE_INTEGRATION_TIME,
+				 mt9p012_regs.reg_pat[rt].fine_int_time},
+				{REG_GROUPED_PARAMETER_HOLD,
+				 GROUPED_PARAMETER_UPDATE},
+			};
+			if (update_type == REG_INIT) {
+				update_type = rupdate;
+				return rc;
+			}
+			rc = mt9p012_i2c_write_w_table(&ppc_tbl[0],
+						ARRAY_SIZE(ppc_tbl));
+			if (rc < 0)
+				return rc;
+
+			rc = mt9p012_test(mt9p012_ctrl->set_test);
+			if (rc < 0)
+				return rc;
+
+			rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+						 MT9P012_REG_RESET_REGISTER,
+						 MT9P012_RESET_REGISTER_PWON |
+						 0x0002);
+			if (rc < 0)
+				return rc;
+
+			mdelay(5);	/* 15? wait for sensor to transition */
+
+			return rc;
+		}
+		break;		/* UPDATE_PERIODIC */
+
+	case REG_INIT:
+		if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+			struct mt9p012_i2c_reg_conf ipc_tbl1[] = {
+				{MT9P012_REG_RESET_REGISTER,
+				 MT9P012_RESET_REGISTER_PWOFF},
+				{REG_VT_PIX_CLK_DIV,
+				 mt9p012_regs.reg_pat[rt].vt_pix_clk_div},
+				{REG_VT_SYS_CLK_DIV,
+				 mt9p012_regs.reg_pat[rt].vt_sys_clk_div},
+				{REG_PRE_PLL_CLK_DIV,
+				 mt9p012_regs.reg_pat[rt].pre_pll_clk_div},
+				{REG_PLL_MULTIPLIER,
+				 mt9p012_regs.reg_pat[rt].pll_multiplier},
+				{REG_OP_PIX_CLK_DIV,
+				 mt9p012_regs.reg_pat[rt].op_pix_clk_div},
+				{REG_OP_SYS_CLK_DIV,
+				 mt9p012_regs.reg_pat[rt].op_sys_clk_div},
+#ifdef MT9P012_REV_7
+				{0x30B0, 0x0001},
+				{0x308E, 0xE060},
+				{0x3092, 0x0A52},
+				{0x3094, 0x4656},
+				{0x3096, 0x5652},
+				{0x30CA, 0x8006},
+				{0x312A, 0xDD02},
+				{0x312C, 0x00E4},
+				{0x3170, 0x299A},
+#endif
+				/* optimized settings for noise */
+				{0x3088, 0x6FF6},
+				{0x3154, 0x0282},
+				{0x3156, 0x0381},
+				{0x3162, 0x04CE},
+				{0x0204, 0x0010},
+				{0x0206, 0x0010},
+				{0x0208, 0x0010},
+				{0x020A, 0x0010},
+				{0x020C, 0x0010},
+				{MT9P012_REG_RESET_REGISTER,
+				 MT9P012_RESET_REGISTER_PWON},
+			};
+
+			struct mt9p012_i2c_reg_conf ipc_tbl2[] = {
+				{MT9P012_REG_RESET_REGISTER,
+				 MT9P012_RESET_REGISTER_PWOFF},
+				{REG_VT_PIX_CLK_DIV,
+				 mt9p012_regs.reg_pat[rt].vt_pix_clk_div},
+				{REG_VT_SYS_CLK_DIV,
+				 mt9p012_regs.reg_pat[rt].vt_sys_clk_div},
+				{REG_PRE_PLL_CLK_DIV,
+				 mt9p012_regs.reg_pat[rt].pre_pll_clk_div},
+				{REG_PLL_MULTIPLIER,
+				 mt9p012_regs.reg_pat[rt].pll_multiplier},
+				{REG_OP_PIX_CLK_DIV,
+				 mt9p012_regs.reg_pat[rt].op_pix_clk_div},
+				{REG_OP_SYS_CLK_DIV,
+				 mt9p012_regs.reg_pat[rt].op_sys_clk_div},
+#ifdef MT9P012_REV_7
+				{0x30B0, 0x0001},
+				{0x308E, 0xE060},
+				{0x3092, 0x0A52},
+				{0x3094, 0x4656},
+				{0x3096, 0x5652},
+				{0x30CA, 0x8006},
+				{0x312A, 0xDD02},
+				{0x312C, 0x00E4},
+				{0x3170, 0x299A},
+#endif
+				/* optimized settings for noise */
+				{0x3088, 0x6FF6},
+				{0x3154, 0x0282},
+				{0x3156, 0x0381},
+				{0x3162, 0x04CE},
+				{0x0204, 0x0010},
+				{0x0206, 0x0010},
+				{0x0208, 0x0010},
+				{0x020A, 0x0010},
+				{0x020C, 0x0010},
+				{MT9P012_REG_RESET_REGISTER,
+				 MT9P012_RESET_REGISTER_PWON},
+			};
+
+			struct mt9p012_i2c_reg_conf ipc_tbl3[] = {
+				{REG_GROUPED_PARAMETER_HOLD,
+				 GROUPED_PARAMETER_HOLD},
+				/* Set preview or snapshot mode */
+				{REG_ROW_SPEED,
+				 mt9p012_regs.reg_pat[rt].row_speed},
+				{REG_X_ADDR_START,
+				 mt9p012_regs.reg_pat[rt].x_addr_start},
+				{REG_X_ADDR_END,
+				 mt9p012_regs.reg_pat[rt].x_addr_end},
+				{REG_Y_ADDR_START,
+				 mt9p012_regs.reg_pat[rt].y_addr_start},
+				{REG_Y_ADDR_END,
+				 mt9p012_regs.reg_pat[rt].y_addr_end},
+				{REG_READ_MODE,
+				 mt9p012_regs.reg_pat[rt].read_mode},
+				{REG_SCALE_M, mt9p012_regs.reg_pat[rt].scale_m},
+				{REG_X_OUTPUT_SIZE,
+				 mt9p012_regs.reg_pat[rt].x_output_size},
+				{REG_Y_OUTPUT_SIZE,
+				 mt9p012_regs.reg_pat[rt].y_output_size},
+				{REG_LINE_LENGTH_PCK,
+				 mt9p012_regs.reg_pat[rt].line_length_pck},
+				{REG_FRAME_LENGTH_LINES,
+				 mt9p012_regs.reg_pat[rt].frame_length_lines},
+				{REG_COARSE_INT_TIME,
+				 mt9p012_regs.reg_pat[rt].coarse_int_time},
+				{REG_FINE_INTEGRATION_TIME,
+				 mt9p012_regs.reg_pat[rt].fine_int_time},
+				{REG_GROUPED_PARAMETER_HOLD,
+				 GROUPED_PARAMETER_UPDATE},
+			};
+
+			/* reset fps_divider */
+			mt9p012_ctrl->fps_divider = 1 * 0x0400;
+
+			rc = mt9p012_i2c_write_w_table(&ipc_tbl1[0],
+						       ARRAY_SIZE(ipc_tbl1));
+			if (rc < 0)
+				return rc;
+
+			rc = mt9p012_i2c_write_w_table(&ipc_tbl2[0],
+						       ARRAY_SIZE(ipc_tbl2));
+			if (rc < 0)
+				return rc;
+
+			mdelay(5);
+
+			rc = mt9p012_i2c_write_w_table(&ipc_tbl3[0],
+						       ARRAY_SIZE(ipc_tbl3));
+			if (rc < 0)
+				return rc;
+
+			/* load lens shading */
+			rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+						 REG_GROUPED_PARAMETER_HOLD,
+						 GROUPED_PARAMETER_HOLD);
+			if (rc < 0)
+				return rc;
+
+			rc = mt9p012_set_lc();
+			if (rc < 0)
+				return rc;
+
+			rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+						 REG_GROUPED_PARAMETER_HOLD,
+						 GROUPED_PARAMETER_UPDATE);
+
+			if (rc < 0)
+				return rc;
+		}
+		update_type = rupdate;
+		break;		/* case REG_INIT: */
+
+	default:
+		rc = -EINVAL;
+		break;
+	}			/* switch (rupdate) */
+
+	return rc;
+}
+
+static int32_t mt9p012_video_config(int mode, int res)
+{
+	int32_t rc;
+
+	switch (res) {
+	case QTR_SIZE:
+		rc = mt9p012_setting(UPDATE_PERIODIC, RES_PREVIEW);
+		if (rc < 0)
+			return rc;
+
+		CDBG("mt9p012 sensor configuration done!\n");
+		break;
+
+	case FULL_SIZE:
+		rc = mt9p012_setting(UPDATE_PERIODIC, RES_CAPTURE);
+		if (rc < 0)
+			return rc;
+
+		break;
+
+	default:
+		return 0;
+	}			/* switch */
+
+	mt9p012_ctrl->prev_res = res;
+	mt9p012_ctrl->curr_res = res;
+	mt9p012_ctrl->sensormode = mode;
+
+	rc = mt9p012_write_exp_gain(mt9p012_ctrl->my_reg_gain,
+				    mt9p012_ctrl->my_reg_line_count);
+
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+				 MT9P012_REG_RESET_REGISTER, 0x10cc | 0x0002);
+
+	return rc;
+}
+
+static int32_t mt9p012_snapshot_config(int mode)
+{
+	int32_t rc = 0;
+
+	rc = mt9p012_setting(UPDATE_PERIODIC, RES_CAPTURE);
+	if (rc < 0)
+		return rc;
+
+	mt9p012_ctrl->curr_res = mt9p012_ctrl->pict_res;
+
+	mt9p012_ctrl->sensormode = mode;
+
+	return rc;
+}
+
+static int32_t mt9p012_raw_snapshot_config(int mode)
+{
+	int32_t rc = 0;
+
+	rc = mt9p012_setting(UPDATE_PERIODIC, RES_CAPTURE);
+	if (rc < 0)
+		return rc;
+
+	mt9p012_ctrl->curr_res = mt9p012_ctrl->pict_res;
+
+	mt9p012_ctrl->sensormode = mode;
+
+	return rc;
+}
+
+static int32_t mt9p012_power_down(void)
+{
+	int32_t rc = 0;
+
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+				 MT9P012_REG_RESET_REGISTER,
+				 MT9P012_RESET_REGISTER_PWOFF);
+
+	mdelay(5);
+	return rc;
+}
+
+static int32_t mt9p012_move_focus(int direction, int32_t num_steps)
+{
+	int32_t rc;
+	int16_t step_direction;
+	int16_t actual_step;
+	int16_t next_position;
+	uint8_t code_val;
+	uint8_t time_out;
+	uint8_t temp_pos;
+
+	uint16_t actual_position_target;
+	if (num_steps > MT9P012_TOTAL_STEPS_NEAR_TO_FAR)
+		num_steps = MT9P012_TOTAL_STEPS_NEAR_TO_FAR;
+	else if (num_steps == 0) {
+		CDBG("mt9p012_move_focus failed at line %d ...\n", __LINE__);
+		return -EINVAL;
+	}
+
+	if (direction == MOVE_NEAR)
+		step_direction = -1;
+	else if (direction == MOVE_FAR)
+		step_direction = 1;
+	else {
+		CDBG("mt9p012_move_focus failed at line %d ...\n", __LINE__);
+		return -EINVAL;
+	}
+
+	if (mt9p012_ctrl->curr_lens_pos < mt9p012_ctrl->init_curr_lens_pos)
+		mt9p012_ctrl->curr_lens_pos = mt9p012_ctrl->init_curr_lens_pos;
+
+	actual_step = (int16_t) (step_direction * (int16_t) num_steps);
+	next_position = (int16_t) (mt9p012_ctrl->curr_lens_pos + actual_step);
+
+	if (next_position > MT9P012_TOTAL_STEPS_NEAR_TO_FAR)
+		next_position = MT9P012_TOTAL_STEPS_NEAR_TO_FAR;
+	else if (next_position < 0)
+		next_position = 0;
+
+	if (num_steps >= 10)
+		time_out = 100;
+	else
+		time_out = 30;
+	code_val = next_position;
+	actual_position_target = bam_step_lookup_table[code_val];
+	rc = mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x01, 0x29);
+	if (rc < 0)
+		return rc;
+	temp_pos = (uint8_t) (actual_position_target >> 8);
+	rc = mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x05, temp_pos);
+	if (rc < 0)
+		return rc;
+	temp_pos = (uint8_t) (actual_position_target & 0x00FF);
+	/* code_val_lsb |= mode_mask; */
+	rc = mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x06, temp_pos);
+	if (rc < 0)
+		return rc;
+
+	rc = mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x0B, time_out);
+	if (rc < 0)
+		return rc;
+	rc = mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x07, 0x27);
+	if (rc < 0)
+		return rc;
+
+	mdelay(time_out);
+
+	/* Storing the current lens Position */
+	mt9p012_ctrl->curr_lens_pos = next_position;
+
+	return rc;
+}
+
+static int32_t mt9p012_set_default_focus(void)
+{
+	int32_t rc = 0;
+
+	uint8_t temp_pos;
+
+	/* Write the digital code for current to the actuator */
+	rc = mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x01, 0x29);
+	if (rc < 0)
+		return rc;
+	temp_pos = (uint8_t) (bam_infinite >> 8);
+
+	rc = mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x05, temp_pos);
+	if (rc < 0)
+		return rc;
+	temp_pos = (uint8_t) (bam_infinite & 0x00FF);
+	rc = mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x06, temp_pos);
+	if (rc < 0)
+		return rc;
+
+	rc = mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x0B, 0x64);
+	if (rc < 0)
+		return rc;
+
+	rc = mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x07, 0x27);
+	if (rc < 0)
+		return rc;
+
+	mdelay(140);
+
+	mt9p012_ctrl->curr_lens_pos = MT9P012_TOTAL_STEPS_NEAR_TO_FAR;
+
+	return rc;
+}
+
+static int mt9p012_probe_init_done(const struct msm_camera_sensor_info *data)
+{
+	gpio_direction_output(data->sensor_reset, 0);
+	gpio_free(data->sensor_reset);
+	return 0;
+}
+
+static int mt9p012_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+	int32_t rc;
+	uint16_t chipid;
+
+	rc = gpio_request(data->sensor_reset, "mt9p012");
+	if (!rc)
+		gpio_direction_output(data->sensor_reset, 1);
+	else
+		goto init_probe_done;
+
+	msleep(20);
+
+	/* RESET the sensor image part via I2C command */
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+				 MT9P012_REG_RESET_REGISTER, 0x10CC | 0x0001);
+	if (rc < 0) {
+		CDBG("sensor reset failed. rc = %d\n", rc);
+		goto init_probe_fail;
+	}
+
+	msleep(MT9P012_RESET_DELAY_MSECS);
+
+	/* 3. Read sensor Model ID: */
+	rc = mt9p012_i2c_read_w(mt9p012_client->addr,
+				MT9P012_REG_MODEL_ID, &chipid);
+	if (rc < 0)
+		goto init_probe_fail;
+
+	/* 4. Compare sensor ID to MT9T012VC ID: */
+	if (chipid != MT9P012_MODEL_ID) {
+		rc = -ENODEV;
+		goto init_probe_fail;
+	}
+
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr, 0x306E, 0x9000);
+	if (rc < 0) {
+		CDBG("REV_7 write failed. rc = %d\n", rc);
+		goto init_probe_fail;
+	}
+
+	/* RESET_REGISTER, enable parallel interface and disable serialiser */
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr, 0x301A, 0x10CC);
+	if (rc < 0) {
+		CDBG("enable parallel interface failed. rc = %d\n", rc);
+		goto init_probe_fail;
+	}
+
+	/* To disable the 2 extra lines */
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr, 0x3064, 0x0805);
+
+	if (rc < 0) {
+		CDBG("disable the 2 extra lines failed. rc = %d\n", rc);
+		goto init_probe_fail;
+	}
+
+	goto init_probe_done;
+
+init_probe_fail:
+	mt9p012_probe_init_done(data);
+init_probe_done:
+	return rc;
+}
+
+static int mt9p012_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+	int32_t rc;
+	unsigned short temp_pos;
+	uint8_t i;
+	uint16_t temp;
+
+	mt9p012_ctrl = kzalloc(sizeof(struct mt9p012_ctrl), GFP_KERNEL);
+	if (!mt9p012_ctrl) {
+		CDBG("mt9p012_init failed!\n");
+		rc = -ENOMEM;
+		goto init_done;
+	}
+
+	mt9p012_ctrl->fps_divider = 1 * 0x00000400;
+	mt9p012_ctrl->pict_fps_divider = 1 * 0x00000400;
+	mt9p012_ctrl->set_test = TEST_OFF;
+	mt9p012_ctrl->prev_res = QTR_SIZE;
+	mt9p012_ctrl->pict_res = FULL_SIZE;
+
+	if (data)
+		mt9p012_ctrl->sensordata = data;
+
+	msm_camio_camif_pad_reg_reset();
+	mdelay(20);
+
+	rc = mt9p012_probe_init_sensor(data);
+	if (rc < 0)
+		goto init_fail1;
+
+	if (mt9p012_ctrl->prev_res == QTR_SIZE)
+		rc = mt9p012_setting(REG_INIT, RES_PREVIEW);
+	else
+		rc = mt9p012_setting(REG_INIT, RES_CAPTURE);
+
+	if (rc < 0) {
+		CDBG("mt9p012_setting failed. rc = %d\n", rc);
+		goto init_fail1;
+	}
+
+	/* sensor : output enable */
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+				 MT9P012_REG_RESET_REGISTER,
+				 MT9P012_RESET_REGISTER_PWON);
+	if (rc < 0) {
+		CDBG("sensor output enable failed. rc = %d\n", rc);
+		goto init_fail1;
+	}
+
+	/* enable AF actuator */
+	rc = gpio_request(mt9p012_ctrl->sensordata->vcm_pwd, "mt9p012");
+	if (!rc)
+		gpio_direction_output(mt9p012_ctrl->sensordata->vcm_pwd, 1);
+	else {
+		CDBG("mt9p012_ctrl gpio request failed!\n");
+		goto init_fail1;
+	}
+
+	mdelay(20);
+
+	bam_infinite = 0;
+	bam_macro = 0;
+	/*initialize AF actuator */
+	mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x01, 0x09);
+	mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x07, 0x2E);
+	mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x0A, 0x01);
+	mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x17, 0x06);
+	mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x16, 0x0A);
+
+	mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x01, 0x29);
+	mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x05, 0x00);
+	mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x06, 0x00);
+	mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x0B, 0x64);
+	mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x07, 0x27);
+	mdelay(140);
+	mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x01, 0x29);
+	mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x05, 0x03);
+	mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x06, 0xFF);
+	mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x0B, 0x64);
+	mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x07, 0x27);
+	mdelay(140);
+
+	if (mt9p012_i2c_read_b(MT9P012_AF_I2C_ADDR >> 1, 0x12, &temp_pos)
+	    >= 0) {
+		bam_infinite = (uint16_t) temp_pos;
+		if (mt9p012_i2c_read_b
+		    (MT9P012_AF_I2C_ADDR >> 1, 0x13, &temp_pos) >= 0)
+			bam_infinite =
+			    (bam_infinite << 8) | ((uint16_t) temp_pos);
+	} else {
+		bam_infinite = 100;
+	}
+
+	if (mt9p012_i2c_read_b(MT9P012_AF_I2C_ADDR >> 1, 0x14, &temp_pos)
+	    >= 0) {
+		bam_macro = (uint16_t) temp_pos;
+		if (mt9p012_i2c_read_b
+		    (MT9P012_AF_I2C_ADDR >> 1, 0x15, &temp_pos) >= 0)
+			bam_macro = (bam_macro << 8) | ((uint16_t) temp_pos);
+	}
+	temp = (bam_infinite - bam_macro) / MT9P012_TOTAL_STEPS_NEAR_TO_FAR;
+	for (i = 0; i < MT9P012_TOTAL_STEPS_NEAR_TO_FAR; i++)
+		bam_step_lookup_table[i] = bam_macro + temp * i;
+
+	bam_step_lookup_table[MT9P012_TOTAL_STEPS_NEAR_TO_FAR] = bam_infinite;
+
+	rc = mt9p012_set_default_focus();
+	if (rc >= 0)
+		goto init_done;
+
+init_fail1:
+	mt9p012_probe_init_done(data);
+	kfree(mt9p012_ctrl);
+init_done:
+	return rc;
+}
+
+static int mt9p012_init_client(struct i2c_client *client)
+{
+	/* Initialize the MSM_CAMI2C Chip */
+	init_waitqueue_head(&mt9p012_wait_queue);
+	return 0;
+}
+
+static int32_t mt9p012_set_sensor_mode(int mode, int res)
+{
+	int32_t rc = 0;
+
+	switch (mode) {
+	case SENSOR_PREVIEW_MODE:
+		rc = mt9p012_video_config(mode, res);
+		break;
+
+	case SENSOR_SNAPSHOT_MODE:
+		rc = mt9p012_snapshot_config(mode);
+		break;
+
+	case SENSOR_RAW_SNAPSHOT_MODE:
+		rc = mt9p012_raw_snapshot_config(mode);
+		break;
+
+	default:
+		rc = -EINVAL;
+		break;
+	}
+
+	return rc;
+}
+
+int mt9p012_sensor_config(void __user *argp)
+{
+	struct sensor_cfg_data cdata;
+	int rc = 0;
+
+	if (copy_from_user(&cdata,
+			   (void *)argp, sizeof(struct sensor_cfg_data)))
+		return -EFAULT;
+
+	mutex_lock(&mt9p012_mut);
+
+	CDBG("%s: cfgtype = %d\n", __func__, cdata.cfgtype);
+	switch (cdata.cfgtype) {
+	case CFG_GET_PICT_FPS:
+		mt9p012_get_pict_fps(cdata.cfg.gfps.prevfps,
+				     &(cdata.cfg.gfps.pictfps));
+
+		if (copy_to_user((void *)argp, &cdata,
+				 sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PREV_L_PF:
+		cdata.cfg.prevl_pf = mt9p012_get_prev_lines_pf();
+
+		if (copy_to_user((void *)argp,
+				 &cdata, sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PREV_P_PL:
+		cdata.cfg.prevp_pl = mt9p012_get_prev_pixels_pl();
+
+		if (copy_to_user((void *)argp,
+				 &cdata, sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PICT_L_PF:
+		cdata.cfg.pictl_pf = mt9p012_get_pict_lines_pf();
+
+		if (copy_to_user((void *)argp,
+				 &cdata, sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PICT_P_PL:
+		cdata.cfg.pictp_pl = mt9p012_get_pict_pixels_pl();
+
+		if (copy_to_user((void *)argp,
+				 &cdata, sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PICT_MAX_EXP_LC:
+		cdata.cfg.pict_max_exp_lc = mt9p012_get_pict_max_exp_lc();
+
+		if (copy_to_user((void *)argp,
+				 &cdata, sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_SET_FPS:
+	case CFG_SET_PICT_FPS:
+		rc = mt9p012_set_fps(&(cdata.cfg.fps));
+		break;
+
+	case CFG_SET_EXP_GAIN:
+		rc = mt9p012_write_exp_gain(cdata.cfg.exp_gain.gain,
+					    cdata.cfg.exp_gain.line);
+		break;
+
+	case CFG_SET_PICT_EXP_GAIN:
+		CDBG("Line:%d CFG_SET_PICT_EXP_GAIN \n", __LINE__);
+		rc = mt9p012_set_pict_exp_gain(cdata.cfg.exp_gain.gain,
+					       cdata.cfg.exp_gain.line);
+		break;
+
+	case CFG_SET_MODE:
+		rc = mt9p012_set_sensor_mode(cdata.mode, cdata.rs);
+		break;
+
+	case CFG_PWR_DOWN:
+		rc = mt9p012_power_down();
+		break;
+
+	case CFG_MOVE_FOCUS:
+		CDBG("mt9p012_ioctl: CFG_MOVE_FOCUS: dir=%d steps=%d\n",
+		     cdata.cfg.focus.dir, cdata.cfg.focus.steps);
+		rc = mt9p012_move_focus(cdata.cfg.focus.dir,
+					cdata.cfg.focus.steps);
+		break;
+
+	case CFG_SET_DEFAULT_FOCUS:
+		rc = mt9p012_set_default_focus();
+
+		break;
+
+	case CFG_SET_EFFECT:
+		rc = mt9p012_set_default_focus();
+		break;
+
+	case CFG_SET_LENS_SHADING:
+		CDBG("%s: CFG_SET_LENS_SHADING\n", __func__);
+		rc = mt9p012_lens_shading_enable(cdata.cfg.lens_shading);
+		break;
+
+	case CFG_GET_AF_MAX_STEPS:
+		cdata.max_steps = MT9P012_STEPS_NEAR_TO_CLOSEST_INF;
+		if (copy_to_user((void *)argp,
+				 &cdata, sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	default:
+		rc = -EINVAL;
+		break;
+	}
+
+	mutex_unlock(&mt9p012_mut);
+	return rc;
+}
+
+int mt9p012_sensor_release(void)
+{
+	int rc = -EBADF;
+
+	mutex_lock(&mt9p012_mut);
+
+	mt9p012_power_down();
+
+	gpio_direction_output(mt9p012_ctrl->sensordata->sensor_reset, 0);
+	gpio_free(mt9p012_ctrl->sensordata->sensor_reset);
+
+	gpio_direction_output(mt9p012_ctrl->sensordata->vcm_pwd, 0);
+	gpio_free(mt9p012_ctrl->sensordata->vcm_pwd);
+
+	kfree(mt9p012_ctrl);
+	mt9p012_ctrl = NULL;
+
+	CDBG("mt9p012_release completed\n");
+
+	mutex_unlock(&mt9p012_mut);
+	return rc;
+}
+
+static int mt9p012_i2c_probe(struct i2c_client *client,
+			     const struct i2c_device_id *id)
+{
+	int rc = 0;
+	CDBG("mt9p012_probe called!\n");
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		CDBG("i2c_check_functionality failed\n");
+		goto probe_failure;
+	}
+
+	mt9p012_sensorw = kzalloc(sizeof(struct mt9p012_work), GFP_KERNEL);
+	if (!mt9p012_sensorw) {
+		CDBG("kzalloc failed.\n");
+		rc = -ENOMEM;
+		goto probe_failure;
+	}
+
+	i2c_set_clientdata(client, mt9p012_sensorw);
+	mt9p012_init_client(client);
+	mt9p012_client = client;
+
+	mdelay(50);
+
+	CDBG("mt9p012_probe successed! rc = %d\n", rc);
+	return 0;
+
+probe_failure:
+	CDBG("mt9p012_probe failed! rc = %d\n", rc);
+	return rc;
+}
+
+static int __exit mt9p012_remove(struct i2c_client *client)
+{
+	struct mt9p012_work_t *sensorw = i2c_get_clientdata(client);
+	free_irq(client->irq, sensorw);
+	mt9p012_client = NULL;
+	kfree(sensorw);
+	return 0;
+}
+
+static const struct i2c_device_id mt9p012_i2c_id[] = {
+	{"mt9p012", 0}
+};
+
+static struct i2c_driver mt9p012_i2c_driver = {
+	.id_table = mt9p012_i2c_id,
+	.probe = mt9p012_i2c_probe,
+	.remove = __exit_p(mt9p012_i2c_remove),
+	.driver = {
+		.name = "mt9p012",
+	},
+};
+
+static int mt9p012_sensor_probe(const struct msm_camera_sensor_info *info,
+				struct msm_sensor_ctrl *s)
+{
+	int rc = i2c_add_driver(&mt9p012_i2c_driver);
+	if (rc < 0 || mt9p012_client == NULL) {
+		rc = -ENOTSUPP;
+		goto probe_done;
+	}
+
+	msm_camio_clk_rate_set(MT9P012_DEFAULT_CLOCK_RATE);
+	mdelay(20);
+
+	rc = mt9p012_probe_init_sensor(info);
+	if (rc < 0)
+		goto probe_done;
+
+	s->s_init = mt9p012_sensor_open_init;
+	s->s_release = mt9p012_sensor_release;
+	s->s_config = mt9p012_sensor_config;
+	s->s_mount_angle  = 0;
+	mt9p012_probe_init_done(info);
+
+probe_done:
+	CDBG("%s %s:%d\n", __FILE__, __func__, __LINE__);
+	return rc;
+}
+
+static int __mt9p012_probe(struct platform_device *pdev)
+{
+	return msm_camera_drv_start(pdev, mt9p012_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+	.probe = __mt9p012_probe,
+	.driver = {
+		.name = "msm_camera_mt9p012",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init mt9p012_init(void)
+{
+	return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(mt9p012_init);
+void mt9p012_exit(void)
+{
+	i2c_del_driver(&mt9p012_i2c_driver);
+}
diff --git a/drivers/media/video/msm/mt9p012_fox.c b/drivers/media/video/msm/mt9p012_fox.c
new file mode 100644
index 0000000..a652c9f
--- /dev/null
+++ b/drivers/media/video/msm/mt9p012_fox.c
@@ -0,0 +1,1346 @@
+/* Copyright (c) 2009, 2011 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/kernel.h>
+#include <media/msm_camera.h>
+#include <mach/gpio.h>
+#include <mach/camera.h>
+#include "mt9p012.h"
+
+/*=============================================================
+    SENSOR REGISTER DEFINES
+==============================================================*/
+#define MT9P012_REG_MODEL_ID         0x0000
+#define MT9P012_MODEL_ID             0x2801
+#define REG_GROUPED_PARAMETER_HOLD   0x0104
+#define GROUPED_PARAMETER_HOLD       0x0100
+#define GROUPED_PARAMETER_UPDATE     0x0000
+#define REG_COARSE_INT_TIME          0x3012
+#define REG_VT_PIX_CLK_DIV           0x0300
+#define REG_VT_SYS_CLK_DIV           0x0302
+#define REG_PRE_PLL_CLK_DIV          0x0304
+#define REG_PLL_MULTIPLIER           0x0306
+#define REG_OP_PIX_CLK_DIV           0x0308
+#define REG_OP_SYS_CLK_DIV           0x030A
+#define REG_SCALE_M                  0x0404
+#define REG_FRAME_LENGTH_LINES       0x300A
+#define REG_LINE_LENGTH_PCK          0x300C
+#define REG_X_ADDR_START             0x3004
+#define REG_Y_ADDR_START             0x3002
+#define REG_X_ADDR_END               0x3008
+#define REG_Y_ADDR_END               0x3006
+#define REG_X_OUTPUT_SIZE            0x034C
+#define REG_Y_OUTPUT_SIZE            0x034E
+#define REG_FINE_INTEGRATION_TIME    0x3014
+#define REG_ROW_SPEED                0x3016
+#define MT9P012_REG_RESET_REGISTER   0x301A
+#define MT9P012_RESET_REGISTER_PWON  0x10CC
+#define MT9P012_RESET_REGISTER_PWOFF 0x10C8
+#define REG_READ_MODE                0x3040
+#define REG_GLOBAL_GAIN              0x305E
+#define REG_TEST_PATTERN_MODE        0x3070
+
+#define MT9P012_REV_7
+
+enum mt9p012_test_mode {
+	TEST_OFF,
+	TEST_1,
+	TEST_2,
+	TEST_3
+};
+
+enum mt9p012_resolution {
+	QTR_SIZE,
+	FULL_SIZE,
+	INVALID_SIZE
+};
+
+enum mt9p012_reg_update {
+	/* Sensor egisters that need to be updated during initialization */
+	REG_INIT,
+	/* Sensor egisters that needs periodic I2C writes */
+	UPDATE_PERIODIC,
+	/* All the sensor Registers will be updated */
+	UPDATE_ALL,
+	/* Not valid update */
+	UPDATE_INVALID
+};
+
+enum mt9p012_setting {
+	RES_PREVIEW,
+	RES_CAPTURE
+};
+
+/* actuator's Slave Address */
+#define MT9P012_AF_I2C_ADDR   0x18
+
+/* AF Total steps parameters */
+#define MT9P012_STEPS_NEAR_TO_CLOSEST_INF  32
+#define MT9P012_TOTAL_STEPS_NEAR_TO_FAR    32
+
+#define MT9P012_MU5M0_PREVIEW_DUMMY_PIXELS 0
+#define MT9P012_MU5M0_PREVIEW_DUMMY_LINES  0
+
+/* Time in milisecs for waiting for the sensor to reset.*/
+#define MT9P012_RESET_DELAY_MSECS   66
+
+/* for 20 fps preview */
+#define MT9P012_DEFAULT_CLOCK_RATE  24000000
+#define MT9P012_DEFAULT_MAX_FPS     26	/* ???? */
+
+struct mt9p012_work {
+	struct work_struct work;
+};
+static struct mt9p012_work *mt9p012_sensorw;
+static struct i2c_client *mt9p012_client;
+
+struct mt9p012_ctrl {
+	const struct msm_camera_sensor_info *sensordata;
+
+	int sensormode;
+	uint32_t fps_divider;	/* init to 1 * 0x00000400 */
+	uint32_t pict_fps_divider;	/* init to 1 * 0x00000400 */
+
+	uint16_t curr_lens_pos;
+	uint16_t init_curr_lens_pos;
+	uint16_t my_reg_gain;
+	uint32_t my_reg_line_count;
+
+	enum mt9p012_resolution prev_res;
+	enum mt9p012_resolution pict_res;
+	enum mt9p012_resolution curr_res;
+	enum mt9p012_test_mode set_test;
+};
+static uint16_t update_type = UPDATE_PERIODIC;
+static struct mt9p012_ctrl *mt9p012_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(mt9p012_wait_queue);
+DEFINE_MUTEX(mt9p012_mut);
+
+
+/*=============================================================*/
+
+static int mt9p012_i2c_rxdata(unsigned short saddr, unsigned char *rxdata,
+			      int length)
+{
+	int retry_cnt = 0;
+	int rc;
+
+	struct i2c_msg msgs[] = {
+		{
+		 .addr = saddr,
+		 .flags = 0,
+		 .len = 2,
+		 .buf = rxdata,
+		 },
+		{
+		 .addr = saddr,
+		 .flags = I2C_M_RD,
+		 .len = length,
+		 .buf = rxdata,
+		 },
+	};
+
+	do {
+		rc = i2c_transfer(mt9p012_client->adapter, msgs, 2);
+		if (rc > 0)
+			break;
+		retry_cnt++;
+	} while (retry_cnt < 3);
+
+	if (rc < 0) {
+		pr_err("mt9p012_i2c_rxdata failed!:%d %d\n", rc, retry_cnt);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int32_t mt9p012_i2c_read_w(unsigned short saddr, unsigned short raddr,
+				  unsigned short *rdata)
+{
+	int32_t rc = 0;
+	unsigned char buf[4];
+
+	if (!rdata)
+		return -EIO;
+
+	memset(buf, 0, sizeof(buf));
+
+	buf[0] = (raddr & 0xFF00) >> 8;
+	buf[1] = (raddr & 0x00FF);
+
+	rc = mt9p012_i2c_rxdata(saddr, buf, 2);
+	if (rc < 0)
+		return rc;
+
+	*rdata = buf[0] << 8 | buf[1];
+
+	if (rc < 0)
+		CDBG("mt9p012_i2c_read failed!\n");
+
+	return rc;
+}
+
+static int32_t mt9p012_i2c_txdata(unsigned short saddr, unsigned char *txdata,
+				  int length)
+{
+	int retry_cnt = 0;
+	int rc;
+
+	struct i2c_msg msg[] = {
+		{
+		 .addr = saddr,
+		 .flags = 0,
+		 .len = length,
+		 .buf = txdata,
+		 },
+	};
+
+	do {
+		rc = i2c_transfer(mt9p012_client->adapter, msg, 1);
+		if (rc > 0)
+			break;
+		retry_cnt++;
+	} while (retry_cnt < 3);
+
+	if (rc < 0) {
+		pr_err("mt9p012_i2c_txdata failed: %d %d\n", rc, retry_cnt);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int32_t mt9p012_i2c_write_b(unsigned short saddr, unsigned short baddr,
+				   unsigned short bdata)
+{
+	int32_t rc = -EIO;
+	unsigned char buf[2];
+
+	memset(buf, 0, sizeof(buf));
+	buf[0] = baddr;
+	buf[1] = bdata;
+	rc = mt9p012_i2c_txdata(saddr, buf, 2);
+
+	if (rc < 0)
+		CDBG("i2c_write failed, saddr = 0x%x addr = 0x%x, val =0x%x!\n",
+		     saddr, baddr, bdata);
+
+	return rc;
+}
+
+static int32_t mt9p012_i2c_write_w(unsigned short saddr, unsigned short waddr,
+				   unsigned short wdata)
+{
+	int32_t rc = -EIO;
+	unsigned char buf[4];
+
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (waddr & 0xFF00) >> 8;
+	buf[1] = (waddr & 0x00FF);
+	buf[2] = (wdata & 0xFF00) >> 8;
+	buf[3] = (wdata & 0x00FF);
+
+	rc = mt9p012_i2c_txdata(saddr, buf, 4);
+
+	if (rc < 0)
+		CDBG("i2c_write_w failed, addr = 0x%x, val = 0x%x!\n",
+		     waddr, wdata);
+
+	return rc;
+}
+
+static int32_t mt9p012_i2c_write_w_table(struct mt9p012_i2c_reg_conf const
+					 *reg_conf_tbl, int num)
+{
+	int i;
+	int32_t rc = -EIO;
+
+	for (i = 0; i < num; i++) {
+		rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+					 reg_conf_tbl->waddr,
+					 reg_conf_tbl->wdata);
+		if (rc < 0)
+			break;
+		reg_conf_tbl++;
+	}
+
+	return rc;
+}
+
+static int32_t mt9p012_test(enum mt9p012_test_mode mo)
+{
+	int32_t rc = 0;
+
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+				 REG_GROUPED_PARAMETER_HOLD,
+				 GROUPED_PARAMETER_HOLD);
+	if (rc < 0)
+		return rc;
+
+	if (mo == TEST_OFF)
+		return 0;
+	else {
+		rc = mt9p012_i2c_write_w_table(mt9p012_regs.ttbl,
+					       mt9p012_regs.ttbl_size);
+		if (rc < 0)
+			return rc;
+
+		rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+					 REG_TEST_PATTERN_MODE, (uint16_t) mo);
+		if (rc < 0)
+			return rc;
+	}
+
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+				 REG_GROUPED_PARAMETER_HOLD,
+				 GROUPED_PARAMETER_UPDATE);
+	if (rc < 0)
+		return rc;
+
+	return rc;
+}
+
+static int32_t mt9p012_lens_shading_enable(uint8_t is_enable)
+{
+	int32_t rc = 0;
+
+	CDBG("%s: entered. enable = %d\n", __func__, is_enable);
+
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+				 REG_GROUPED_PARAMETER_HOLD,
+				 GROUPED_PARAMETER_HOLD);
+	if (rc < 0)
+		return rc;
+
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr, 0x3780,
+				 ((uint16_t) is_enable) << 15);
+	if (rc < 0)
+		return rc;
+
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+				 REG_GROUPED_PARAMETER_HOLD,
+				 GROUPED_PARAMETER_UPDATE);
+
+	CDBG("%s: exiting. rc = %d\n", __func__, rc);
+	return rc;
+}
+
+static int32_t mt9p012_set_lc(void)
+{
+	int32_t rc;
+
+	rc = mt9p012_i2c_write_w_table(mt9p012_regs.rftbl,
+				       mt9p012_regs.rftbl_size);
+
+	return rc;
+}
+
+static void mt9p012_get_pict_fps(uint16_t fps, uint16_t *pfps)
+{
+	/* input fps is preview fps in Q8 format */
+	uint32_t divider;	/*Q10 */
+	uint32_t pclk_mult;	/*Q10 */
+	uint32_t d1;
+	uint32_t d2;
+
+	d1 =
+		(uint32_t)(
+		(mt9p012_regs.reg_pat[RES_PREVIEW].frame_length_lines *
+		0x00000400) /
+		mt9p012_regs.reg_pat[RES_CAPTURE].frame_length_lines);
+
+	d2 =
+		(uint32_t)(
+		(mt9p012_regs.reg_pat[RES_PREVIEW].line_length_pck *
+		0x00000400) /
+		mt9p012_regs.reg_pat[RES_CAPTURE].line_length_pck);
+
+	divider = (uint32_t) (d1 * d2) / 0x00000400;
+
+	pclk_mult =
+		(uint32_t) ((mt9p012_regs.reg_pat[RES_CAPTURE].pll_multiplier *
+		0x00000400) /
+		(mt9p012_regs.reg_pat[RES_PREVIEW].pll_multiplier));
+
+	/* Verify PCLK settings and frame sizes. */
+	*pfps = (uint16_t) (fps * divider * pclk_mult / 0x00000400 /
+			    0x00000400);
+}
+
+static uint16_t mt9p012_get_prev_lines_pf(void)
+{
+	if (mt9p012_ctrl->prev_res == QTR_SIZE)
+		return mt9p012_regs.reg_pat[RES_PREVIEW].frame_length_lines;
+	else
+		return mt9p012_regs.reg_pat[RES_CAPTURE].frame_length_lines;
+}
+
+static uint16_t mt9p012_get_prev_pixels_pl(void)
+{
+	if (mt9p012_ctrl->prev_res == QTR_SIZE)
+		return mt9p012_regs.reg_pat[RES_PREVIEW].line_length_pck;
+	else
+		return mt9p012_regs.reg_pat[RES_CAPTURE].line_length_pck;
+}
+
+static uint16_t mt9p012_get_pict_lines_pf(void)
+{
+	return mt9p012_regs.reg_pat[RES_CAPTURE].frame_length_lines;
+}
+
+static uint16_t mt9p012_get_pict_pixels_pl(void)
+{
+	return mt9p012_regs.reg_pat[RES_CAPTURE].line_length_pck;
+}
+
+static uint32_t mt9p012_get_pict_max_exp_lc(void)
+{
+	uint16_t snapshot_lines_per_frame;
+
+	if (mt9p012_ctrl->pict_res == QTR_SIZE)
+		snapshot_lines_per_frame =
+		    mt9p012_regs.reg_pat[RES_PREVIEW].frame_length_lines - 1;
+	else
+		snapshot_lines_per_frame =
+		    mt9p012_regs.reg_pat[RES_CAPTURE].frame_length_lines - 1;
+
+	return snapshot_lines_per_frame * 24;
+}
+
+static int32_t mt9p012_set_fps(struct fps_cfg *fps)
+{
+	/* input is new fps in Q10 format */
+	int32_t rc = 0;
+	enum mt9p012_setting setting;
+
+	mt9p012_ctrl->fps_divider = fps->fps_div;
+	mt9p012_ctrl->pict_fps_divider = fps->pict_fps_div;
+
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+				 REG_GROUPED_PARAMETER_HOLD,
+				 GROUPED_PARAMETER_HOLD);
+	if (rc < 0)
+		return -EBUSY;
+
+	if (mt9p012_ctrl->sensormode == SENSOR_PREVIEW_MODE)
+		setting = RES_PREVIEW;
+	else
+		setting = RES_CAPTURE;
+
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+		REG_FRAME_LENGTH_LINES,
+		(mt9p012_regs.reg_pat[setting].frame_length_lines *
+		fps->fps_div / 0x00000400));
+	if (rc < 0)
+		return rc;
+
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+				 REG_GROUPED_PARAMETER_HOLD,
+				 GROUPED_PARAMETER_UPDATE);
+
+	return rc;
+}
+
+static int32_t mt9p012_write_exp_gain(uint16_t gain, uint32_t line)
+{
+	uint16_t max_legal_gain = 0x01FF;
+	uint32_t line_length_ratio = 0x00000400;
+	enum mt9p012_setting setting;
+	int32_t rc = 0;
+
+	CDBG("Line:%d mt9p012_write_exp_gain \n", __LINE__);
+
+	if (mt9p012_ctrl->sensormode == SENSOR_PREVIEW_MODE) {
+		mt9p012_ctrl->my_reg_gain = gain;
+		mt9p012_ctrl->my_reg_line_count = (uint16_t) line;
+	}
+
+	if (gain > max_legal_gain) {
+		CDBG("Max legal gain Line:%d \n", __LINE__);
+		gain = max_legal_gain;
+	}
+
+	/* Verify no overflow */
+	if (mt9p012_ctrl->sensormode == SENSOR_PREVIEW_MODE) {
+		line = (uint32_t) (line * mt9p012_ctrl->fps_divider /
+				   0x00000400);
+		setting = RES_PREVIEW;
+	} else {
+		line = (uint32_t) (line * mt9p012_ctrl->pict_fps_divider /
+				   0x00000400);
+		setting = RES_CAPTURE;
+	}
+
+	/* Set digital gain to 1 */
+#ifdef MT9P012_REV_7
+	gain |= 0x1000;
+#else
+	gain |= 0x0200;
+#endif
+
+	if ((mt9p012_regs.reg_pat[setting].frame_length_lines - 1) < line) {
+		line_length_ratio = (uint32_t) (line * 0x00000400) /
+		    (mt9p012_regs.reg_pat[setting].frame_length_lines - 1);
+	} else
+		line_length_ratio = 0x00000400;
+
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr, REG_GLOBAL_GAIN, gain);
+	if (rc < 0) {
+		CDBG("mt9p012_i2c_write_w failed... Line:%d \n", __LINE__);
+		return rc;
+	}
+
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+				 REG_COARSE_INT_TIME, line);
+	if (rc < 0) {
+		CDBG("mt9p012_i2c_write_w failed... Line:%d \n", __LINE__);
+		return rc;
+	}
+
+	CDBG("mt9p012_write_exp_gain: gain = %d, line = %d\n", gain, line);
+
+	return rc;
+}
+
+static int32_t mt9p012_set_pict_exp_gain(uint16_t gain, uint32_t line)
+{
+	int32_t rc = 0;
+
+	CDBG("Line:%d mt9p012_set_pict_exp_gain \n", __LINE__);
+
+	rc = mt9p012_write_exp_gain(gain, line);
+	if (rc < 0) {
+		CDBG("Line:%d mt9p012_set_pict_exp_gain failed... \n",
+		     __LINE__);
+		return rc;
+	}
+
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+				 MT9P012_REG_RESET_REGISTER, 0x10CC | 0x0002);
+	if (rc < 0) {
+		CDBG("mt9p012_i2c_write_w failed... Line:%d \n", __LINE__);
+		return rc;
+	}
+
+	mdelay(5);
+
+	/* camera_timed_wait(snapshot_wait*exposure_ratio); */
+	return rc;
+}
+
+static int32_t mt9p012_setting(enum mt9p012_reg_update rupdate,
+			       enum mt9p012_setting rt)
+{
+	int32_t rc = 0;
+	switch (rupdate) {
+	case UPDATE_PERIODIC:
+		if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+			struct mt9p012_i2c_reg_conf ppc_tbl[] = {
+				{REG_GROUPED_PARAMETER_HOLD,
+				 GROUPED_PARAMETER_HOLD},
+				{REG_ROW_SPEED,
+				 mt9p012_regs.reg_pat[rt].row_speed},
+				{REG_X_ADDR_START,
+				 mt9p012_regs.reg_pat[rt].x_addr_start},
+				{REG_X_ADDR_END,
+				 mt9p012_regs.reg_pat[rt].x_addr_end},
+				{REG_Y_ADDR_START,
+				 mt9p012_regs.reg_pat[rt].y_addr_start},
+				{REG_Y_ADDR_END,
+				 mt9p012_regs.reg_pat[rt].y_addr_end},
+				{REG_READ_MODE,
+				 mt9p012_regs.reg_pat[rt].read_mode},
+				{REG_SCALE_M, mt9p012_regs.reg_pat[rt].scale_m},
+				{REG_X_OUTPUT_SIZE,
+				 mt9p012_regs.reg_pat[rt].x_output_size},
+				{REG_Y_OUTPUT_SIZE,
+				 mt9p012_regs.reg_pat[rt].y_output_size},
+
+				{REG_LINE_LENGTH_PCK,
+				 mt9p012_regs.reg_pat[rt].line_length_pck},
+				{REG_FRAME_LENGTH_LINES,
+				 (mt9p012_regs.reg_pat[rt].frame_length_lines *
+				  mt9p012_ctrl->fps_divider / 0x00000400)},
+				{REG_COARSE_INT_TIME,
+				 mt9p012_regs.reg_pat[rt].coarse_int_time},
+				{REG_FINE_INTEGRATION_TIME,
+				 mt9p012_regs.reg_pat[rt].fine_int_time},
+				{REG_GROUPED_PARAMETER_HOLD,
+				 GROUPED_PARAMETER_UPDATE},
+			};
+			if (update_type == REG_INIT) {
+				update_type = rupdate;
+				return rc;
+			}
+			rc = mt9p012_i2c_write_w_table(&ppc_tbl[0],
+						ARRAY_SIZE(ppc_tbl));
+			if (rc < 0)
+				return rc;
+
+			rc = mt9p012_test(mt9p012_ctrl->set_test);
+			if (rc < 0)
+				return rc;
+
+			rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+						 MT9P012_REG_RESET_REGISTER,
+						 MT9P012_RESET_REGISTER_PWON |
+						 0x0002);
+			if (rc < 0)
+				return rc;
+
+			mdelay(5);	/* 15? wait for sensor to transition */
+
+			return rc;
+		}
+		break;		/* UPDATE_PERIODIC */
+
+	case REG_INIT:
+		if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+			struct mt9p012_i2c_reg_conf ipc_tbl1[] = {
+				{MT9P012_REG_RESET_REGISTER,
+				 MT9P012_RESET_REGISTER_PWOFF},
+				{REG_VT_PIX_CLK_DIV,
+				 mt9p012_regs.reg_pat[rt].vt_pix_clk_div},
+				{REG_VT_SYS_CLK_DIV,
+				 mt9p012_regs.reg_pat[rt].vt_sys_clk_div},
+				{REG_PRE_PLL_CLK_DIV,
+				 mt9p012_regs.reg_pat[rt].pre_pll_clk_div},
+				{REG_PLL_MULTIPLIER,
+				 mt9p012_regs.reg_pat[rt].pll_multiplier},
+				{REG_OP_PIX_CLK_DIV,
+				 mt9p012_regs.reg_pat[rt].op_pix_clk_div},
+				{REG_OP_SYS_CLK_DIV,
+				 mt9p012_regs.reg_pat[rt].op_sys_clk_div},
+#ifdef MT9P012_REV_7
+				{0x30B0, 0x0001},
+				{0x308E, 0xE060},
+				{0x3092, 0x0A52},
+				{0x3094, 0x4656},
+				{0x3096, 0x5652},
+				{0x30CA, 0x8006},
+				{0x312A, 0xDD02},
+				{0x312C, 0x00E4},
+				{0x3170, 0x299A},
+#endif
+				/* optimized settings for noise */
+				{0x3088, 0x6FF6},
+				{0x3154, 0x0282},
+				{0x3156, 0x0381},
+				{0x3162, 0x04CE},
+				{0x0204, 0x0010},
+				{0x0206, 0x0010},
+				{0x0208, 0x0010},
+				{0x020A, 0x0010},
+				{0x020C, 0x0010},
+				{MT9P012_REG_RESET_REGISTER,
+				 MT9P012_RESET_REGISTER_PWON},
+			};
+
+			struct mt9p012_i2c_reg_conf ipc_tbl2[] = {
+				{MT9P012_REG_RESET_REGISTER,
+				 MT9P012_RESET_REGISTER_PWOFF},
+				{REG_VT_PIX_CLK_DIV,
+				 mt9p012_regs.reg_pat[rt].vt_pix_clk_div},
+				{REG_VT_SYS_CLK_DIV,
+				 mt9p012_regs.reg_pat[rt].vt_sys_clk_div},
+				{REG_PRE_PLL_CLK_DIV,
+				 mt9p012_regs.reg_pat[rt].pre_pll_clk_div},
+				{REG_PLL_MULTIPLIER,
+				 mt9p012_regs.reg_pat[rt].pll_multiplier},
+				{REG_OP_PIX_CLK_DIV,
+				 mt9p012_regs.reg_pat[rt].op_pix_clk_div},
+				{REG_OP_SYS_CLK_DIV,
+				 mt9p012_regs.reg_pat[rt].op_sys_clk_div},
+#ifdef MT9P012_REV_7
+				{0x30B0, 0x0001},
+				{0x308E, 0xE060},
+				{0x3092, 0x0A52},
+				{0x3094, 0x4656},
+				{0x3096, 0x5652},
+				{0x30CA, 0x8006},
+				{0x312A, 0xDD02},
+				{0x312C, 0x00E4},
+				{0x3170, 0x299A},
+#endif
+				/* optimized settings for noise */
+				{0x3088, 0x6FF6},
+				{0x3154, 0x0282},
+				{0x3156, 0x0381},
+				{0x3162, 0x04CE},
+				{0x0204, 0x0010},
+				{0x0206, 0x0010},
+				{0x0208, 0x0010},
+				{0x020A, 0x0010},
+				{0x020C, 0x0010},
+				{MT9P012_REG_RESET_REGISTER,
+				 MT9P012_RESET_REGISTER_PWON},
+			};
+
+			struct mt9p012_i2c_reg_conf ipc_tbl3[] = {
+				{REG_GROUPED_PARAMETER_HOLD,
+				 GROUPED_PARAMETER_HOLD},
+				/* Set preview or snapshot mode */
+				{REG_ROW_SPEED,
+				 mt9p012_regs.reg_pat[rt].row_speed},
+				{REG_X_ADDR_START,
+				 mt9p012_regs.reg_pat[rt].x_addr_start},
+				{REG_X_ADDR_END,
+				 mt9p012_regs.reg_pat[rt].x_addr_end},
+				{REG_Y_ADDR_START,
+				 mt9p012_regs.reg_pat[rt].y_addr_start},
+				{REG_Y_ADDR_END,
+				 mt9p012_regs.reg_pat[rt].y_addr_end},
+				{REG_READ_MODE,
+				 mt9p012_regs.reg_pat[rt].read_mode},
+				{REG_SCALE_M, mt9p012_regs.reg_pat[rt].scale_m},
+				{REG_X_OUTPUT_SIZE,
+				 mt9p012_regs.reg_pat[rt].x_output_size},
+				{REG_Y_OUTPUT_SIZE,
+				 mt9p012_regs.reg_pat[rt].y_output_size},
+				{REG_LINE_LENGTH_PCK,
+				 mt9p012_regs.reg_pat[rt].line_length_pck},
+				{REG_FRAME_LENGTH_LINES,
+				 mt9p012_regs.reg_pat[rt].frame_length_lines},
+				{REG_COARSE_INT_TIME,
+				 mt9p012_regs.reg_pat[rt].coarse_int_time},
+				{REG_FINE_INTEGRATION_TIME,
+				 mt9p012_regs.reg_pat[rt].fine_int_time},
+				{REG_GROUPED_PARAMETER_HOLD,
+				 GROUPED_PARAMETER_UPDATE},
+			};
+
+			/* reset fps_divider */
+			mt9p012_ctrl->fps_divider = 1 * 0x0400;
+
+			rc = mt9p012_i2c_write_w_table(&ipc_tbl1[0],
+						       ARRAY_SIZE(ipc_tbl1));
+			if (rc < 0)
+				return rc;
+
+			rc = mt9p012_i2c_write_w_table(&ipc_tbl2[0],
+						       ARRAY_SIZE(ipc_tbl2));
+			if (rc < 0)
+				return rc;
+
+			mdelay(5);
+
+			rc = mt9p012_i2c_write_w_table(&ipc_tbl3[0],
+						       ARRAY_SIZE(ipc_tbl3));
+			if (rc < 0)
+				return rc;
+
+			/* load lens shading */
+			rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+						 REG_GROUPED_PARAMETER_HOLD,
+						 GROUPED_PARAMETER_HOLD);
+			if (rc < 0)
+				return rc;
+
+			rc = mt9p012_set_lc();
+			if (rc < 0)
+				return rc;
+
+			rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+						 REG_GROUPED_PARAMETER_HOLD,
+						 GROUPED_PARAMETER_UPDATE);
+
+			if (rc < 0)
+				return rc;
+		}
+		update_type = rupdate;
+		break;		/* case REG_INIT: */
+
+	default:
+		rc = -EINVAL;
+		break;
+	}			/* switch (rupdate) */
+
+	return rc;
+}
+
+static int32_t mt9p012_video_config(int mode, int res)
+{
+	int32_t rc;
+
+	switch (res) {
+	case QTR_SIZE:
+		rc = mt9p012_setting(UPDATE_PERIODIC, RES_PREVIEW);
+		if (rc < 0)
+			return rc;
+
+		CDBG("mt9p012 sensor configuration done!\n");
+		break;
+
+	case FULL_SIZE:
+		rc = mt9p012_setting(UPDATE_PERIODIC, RES_CAPTURE);
+		if (rc < 0)
+			return rc;
+
+		break;
+
+	default:
+		return 0;
+	}			/* switch */
+
+	mt9p012_ctrl->prev_res = res;
+	mt9p012_ctrl->curr_res = res;
+	mt9p012_ctrl->sensormode = mode;
+
+	rc = mt9p012_write_exp_gain(mt9p012_ctrl->my_reg_gain,
+				    mt9p012_ctrl->my_reg_line_count);
+
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+				 MT9P012_REG_RESET_REGISTER, 0x10cc | 0x0002);
+
+	return rc;
+}
+
+static int32_t mt9p012_snapshot_config(int mode)
+{
+	int32_t rc = 0;
+
+	rc = mt9p012_setting(UPDATE_PERIODIC, RES_CAPTURE);
+	if (rc < 0)
+		return rc;
+
+	mt9p012_ctrl->curr_res = mt9p012_ctrl->pict_res;
+
+	mt9p012_ctrl->sensormode = mode;
+
+	return rc;
+}
+
+static int32_t mt9p012_raw_snapshot_config(int mode)
+{
+	int32_t rc = 0;
+
+	rc = mt9p012_setting(UPDATE_PERIODIC, RES_CAPTURE);
+	if (rc < 0)
+		return rc;
+
+	mt9p012_ctrl->curr_res = mt9p012_ctrl->pict_res;
+
+	mt9p012_ctrl->sensormode = mode;
+
+	return rc;
+}
+
+static int32_t mt9p012_power_down(void)
+{
+	int32_t rc = 0;
+
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+				 MT9P012_REG_RESET_REGISTER,
+				 MT9P012_RESET_REGISTER_PWOFF);
+
+	mdelay(5);
+	return rc;
+}
+
+static int32_t mt9p012_move_focus(int direction, int32_t num_steps)
+{
+	int16_t step_direction;
+	int16_t actual_step;
+	int16_t next_position;
+	uint8_t code_val_msb, code_val_lsb;
+
+	if (num_steps > MT9P012_TOTAL_STEPS_NEAR_TO_FAR)
+		num_steps = MT9P012_TOTAL_STEPS_NEAR_TO_FAR;
+	else if (num_steps == 0) {
+		CDBG("mt9p012_move_focus failed at line %d ...\n", __LINE__);
+		return -EINVAL;
+	}
+
+	if (direction == MOVE_NEAR)
+		step_direction = 16;	/* 10bit */
+	else if (direction == MOVE_FAR)
+		step_direction = -16;	/* 10 bit */
+	else {
+		CDBG("mt9p012_move_focus failed at line %d ...\n", __LINE__);
+		return -EINVAL;
+	}
+
+	if (mt9p012_ctrl->curr_lens_pos < mt9p012_ctrl->init_curr_lens_pos)
+		mt9p012_ctrl->curr_lens_pos = mt9p012_ctrl->init_curr_lens_pos;
+
+	actual_step = (int16_t) (step_direction * (int16_t) num_steps);
+	next_position = (int16_t) (mt9p012_ctrl->curr_lens_pos + actual_step);
+
+	if (next_position > 1023)
+		next_position = 1023;
+	else if (next_position < 0)
+		next_position = 0;
+
+	code_val_msb = next_position >> 4;
+	code_val_lsb = (next_position & 0x000F) << 4;
+	/* code_val_lsb |= mode_mask; */
+
+	/* Writing the digital code for current to the actuator */
+	if (mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1,
+				code_val_msb, code_val_lsb) < 0) {
+		CDBG("mt9p012_move_focus failed at line %d ...\n", __LINE__);
+		return -EBUSY;
+	}
+
+	/* Storing the current lens Position */
+	mt9p012_ctrl->curr_lens_pos = next_position;
+
+	return 0;
+}
+
+static int32_t mt9p012_set_default_focus(void)
+{
+	int32_t rc = 0;
+	uint8_t code_val_msb, code_val_lsb;
+
+	code_val_msb = 0x00;
+	code_val_lsb = 0x00;
+
+	/* Write the digital code for current to the actuator */
+	rc = mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1,
+				 code_val_msb, code_val_lsb);
+
+	mt9p012_ctrl->curr_lens_pos = 0;
+	mt9p012_ctrl->init_curr_lens_pos = 0;
+
+	return rc;
+}
+
+static int mt9p012_probe_init_done(const struct msm_camera_sensor_info *data)
+{
+	gpio_direction_output(data->sensor_reset, 0);
+	gpio_free(data->sensor_reset);
+	return 0;
+}
+
+static int mt9p012_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+	int32_t rc;
+	uint16_t chipid;
+
+	rc = gpio_request(data->sensor_reset, "mt9p012");
+	if (!rc)
+		gpio_direction_output(data->sensor_reset, 1);
+	else
+		goto init_probe_done;
+
+	msleep(20);
+
+	/* RESET the sensor image part via I2C command */
+	CDBG("mt9p012_sensor_init(): reseting sensor.\n");
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+				 MT9P012_REG_RESET_REGISTER, 0x10CC | 0x0001);
+	if (rc < 0) {
+		CDBG("sensor reset failed. rc = %d\n", rc);
+		goto init_probe_fail;
+	}
+
+	msleep(MT9P012_RESET_DELAY_MSECS);
+
+	/* 3. Read sensor Model ID: */
+	rc = mt9p012_i2c_read_w(mt9p012_client->addr,
+				MT9P012_REG_MODEL_ID, &chipid);
+	if (rc < 0)
+		goto init_probe_fail;
+
+	/* 4. Compare sensor ID to MT9T012VC ID: */
+	if (chipid != MT9P012_MODEL_ID) {
+		CDBG("mt9p012 wrong model_id = 0x%x\n", chipid);
+		rc = -ENODEV;
+		goto init_probe_fail;
+	}
+
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr, 0x306E, 0x9000);
+	if (rc < 0) {
+		CDBG("REV_7 write failed. rc = %d\n", rc);
+		goto init_probe_fail;
+	}
+
+	/* RESET_REGISTER, enable parallel interface and disable serialiser */
+	CDBG("mt9p012_sensor_init(): enabling parallel interface.\n");
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr, 0x301A, 0x10CC);
+	if (rc < 0) {
+		CDBG("enable parallel interface failed. rc = %d\n", rc);
+		goto init_probe_fail;
+	}
+
+	/* To disable the 2 extra lines */
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr, 0x3064, 0x0805);
+
+	if (rc < 0) {
+		CDBG("disable the 2 extra lines failed. rc = %d\n", rc);
+		goto init_probe_fail;
+	}
+	goto init_probe_done;
+
+init_probe_fail:
+	mt9p012_probe_init_done(data);
+init_probe_done:
+	return rc;
+}
+
+static int mt9p012_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+	int32_t rc;
+
+	mt9p012_ctrl = kzalloc(sizeof(struct mt9p012_ctrl), GFP_KERNEL);
+	if (!mt9p012_ctrl) {
+		CDBG("mt9p012_init failed!\n");
+		rc = -ENOMEM;
+		goto init_done;
+	}
+
+	mt9p012_ctrl->fps_divider = 1 * 0x00000400;
+	mt9p012_ctrl->pict_fps_divider = 1 * 0x00000400;
+	mt9p012_ctrl->set_test = TEST_OFF;
+	mt9p012_ctrl->prev_res = QTR_SIZE;
+	mt9p012_ctrl->pict_res = FULL_SIZE;
+
+	if (data)
+		mt9p012_ctrl->sensordata = data;
+
+	msm_camio_camif_pad_reg_reset();
+	mdelay(20);
+
+	rc = mt9p012_probe_init_sensor(data);
+	if (rc < 0)
+		goto init_fail1;
+
+	if (mt9p012_ctrl->prev_res == QTR_SIZE)
+		rc = mt9p012_setting(REG_INIT, RES_PREVIEW);
+	else
+		rc = mt9p012_setting(REG_INIT, RES_CAPTURE);
+
+	if (rc < 0) {
+		CDBG("mt9p012_setting failed. rc = %d\n", rc);
+		goto init_fail1;
+	}
+
+	/* sensor : output enable */
+	CDBG("mt9p012_sensor_open_init(): enabling output.\n");
+	rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+				 MT9P012_REG_RESET_REGISTER,
+				 MT9P012_RESET_REGISTER_PWON);
+	if (rc < 0) {
+		CDBG("sensor output enable failed. rc = %d\n", rc);
+		goto init_fail1;
+	}
+
+	/* enable AF actuator */
+	if (mt9p012_ctrl->sensordata->vcm_enable) {
+		CDBG("enable AF actuator, gpio = %d\n",
+			 mt9p012_ctrl->sensordata->vcm_pwd);
+		rc = gpio_request(mt9p012_ctrl->sensordata->vcm_pwd,
+						"mt9p012");
+		if (!rc)
+			gpio_direction_output(
+				mt9p012_ctrl->sensordata->vcm_pwd,
+				 1);
+		else {
+			CDBG("mt9p012_ctrl gpio request failed!\n");
+			goto init_fail1;
+		}
+		msleep(20);
+		rc = mt9p012_set_default_focus();
+		if (rc < 0) {
+			gpio_direction_output(mt9p012_ctrl->sensordata->vcm_pwd,
+								0);
+			gpio_free(mt9p012_ctrl->sensordata->vcm_pwd);
+		}
+	}
+	if (rc >= 0)
+		goto init_done;
+init_fail1:
+	mt9p012_probe_init_done(data);
+	kfree(mt9p012_ctrl);
+init_done:
+	return rc;
+}
+
+static int mt9p012_init_client(struct i2c_client *client)
+{
+	/* Initialize the MSM_CAMI2C Chip */
+	init_waitqueue_head(&mt9p012_wait_queue);
+	return 0;
+}
+
+static int32_t mt9p012_set_sensor_mode(int mode, int res)
+{
+	int32_t rc = 0;
+
+	switch (mode) {
+	case SENSOR_PREVIEW_MODE:
+		rc = mt9p012_video_config(mode, res);
+		break;
+
+	case SENSOR_SNAPSHOT_MODE:
+		rc = mt9p012_snapshot_config(mode);
+		break;
+
+	case SENSOR_RAW_SNAPSHOT_MODE:
+		rc = mt9p012_raw_snapshot_config(mode);
+		break;
+
+	default:
+		rc = -EINVAL;
+		break;
+	}
+
+	return rc;
+}
+
+int mt9p012_sensor_config(void __user *argp)
+{
+	struct sensor_cfg_data cdata;
+	int rc = 0;
+
+	if (copy_from_user(&cdata,
+			   (void *)argp, sizeof(struct sensor_cfg_data)))
+		return -EFAULT;
+
+	mutex_lock(&mt9p012_mut);
+
+	CDBG("%s: cfgtype = %d\n", __func__, cdata.cfgtype);
+	switch (cdata.cfgtype) {
+	case CFG_GET_PICT_FPS:
+		mt9p012_get_pict_fps(cdata.cfg.gfps.prevfps,
+				     &(cdata.cfg.gfps.pictfps));
+
+		if (copy_to_user((void *)argp, &cdata,
+				 sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PREV_L_PF:
+		cdata.cfg.prevl_pf = mt9p012_get_prev_lines_pf();
+
+		if (copy_to_user((void *)argp,
+				 &cdata, sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PREV_P_PL:
+		cdata.cfg.prevp_pl = mt9p012_get_prev_pixels_pl();
+
+		if (copy_to_user((void *)argp,
+				 &cdata, sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PICT_L_PF:
+		cdata.cfg.pictl_pf = mt9p012_get_pict_lines_pf();
+
+		if (copy_to_user((void *)argp,
+				 &cdata, sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PICT_P_PL:
+		cdata.cfg.pictp_pl = mt9p012_get_pict_pixels_pl();
+
+		if (copy_to_user((void *)argp,
+				 &cdata, sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PICT_MAX_EXP_LC:
+		cdata.cfg.pict_max_exp_lc = mt9p012_get_pict_max_exp_lc();
+
+		if (copy_to_user((void *)argp,
+				 &cdata, sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_SET_FPS:
+	case CFG_SET_PICT_FPS:
+		rc = mt9p012_set_fps(&(cdata.cfg.fps));
+		break;
+
+	case CFG_SET_EXP_GAIN:
+		rc = mt9p012_write_exp_gain(cdata.cfg.exp_gain.gain,
+					    cdata.cfg.exp_gain.line);
+		break;
+
+	case CFG_SET_PICT_EXP_GAIN:
+		CDBG("Line:%d CFG_SET_PICT_EXP_GAIN \n", __LINE__);
+		rc = mt9p012_set_pict_exp_gain(cdata.cfg.exp_gain.gain,
+					       cdata.cfg.exp_gain.line);
+		break;
+
+	case CFG_SET_MODE:
+		rc = mt9p012_set_sensor_mode(cdata.mode, cdata.rs);
+		break;
+
+	case CFG_PWR_DOWN:
+		rc = mt9p012_power_down();
+		break;
+
+	case CFG_MOVE_FOCUS:
+		CDBG("mt9p012_ioctl: CFG_MOVE_FOCUS: cdata.cfg.focus.dir=%d \
+				cdata.cfg.focus.steps=%d\n",
+				cdata.cfg.focus.dir, cdata.cfg.focus.steps);
+		rc = mt9p012_move_focus(cdata.cfg.focus.dir,
+					cdata.cfg.focus.steps);
+		break;
+
+	case CFG_SET_DEFAULT_FOCUS:
+		rc = mt9p012_set_default_focus();
+		break;
+
+	case CFG_SET_LENS_SHADING:
+		CDBG("%s: CFG_SET_LENS_SHADING\n", __func__);
+		rc = mt9p012_lens_shading_enable(cdata.cfg.lens_shading);
+		break;
+
+	case CFG_GET_AF_MAX_STEPS:
+		cdata.max_steps = MT9P012_STEPS_NEAR_TO_CLOSEST_INF;
+		if (copy_to_user((void *)argp,
+				 &cdata, sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_SET_EFFECT:
+	default:
+		rc = -EINVAL;
+		break;
+	}
+
+	mutex_unlock(&mt9p012_mut);
+	return rc;
+}
+
+int mt9p012_sensor_release(void)
+{
+	int rc = -EBADF;
+
+	mutex_lock(&mt9p012_mut);
+
+	mt9p012_power_down();
+
+	gpio_direction_output(mt9p012_ctrl->sensordata->sensor_reset, 0);
+	gpio_free(mt9p012_ctrl->sensordata->sensor_reset);
+
+	if (mt9p012_ctrl->sensordata->vcm_enable) {
+		gpio_direction_output(mt9p012_ctrl->sensordata->vcm_pwd, 0);
+		gpio_free(mt9p012_ctrl->sensordata->vcm_pwd);
+	}
+
+	kfree(mt9p012_ctrl);
+	mt9p012_ctrl = NULL;
+
+	CDBG("mt9p012_release completed\n");
+
+	mutex_unlock(&mt9p012_mut);
+	return rc;
+}
+
+static int mt9p012_i2c_probe(struct i2c_client *client,
+			     const struct i2c_device_id *id)
+{
+	int rc = 0;
+	CDBG("mt9p012_probe called!\n");
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		CDBG("i2c_check_functionality failed\n");
+		goto probe_failure;
+	}
+
+	mt9p012_sensorw = kzalloc(sizeof(struct mt9p012_work), GFP_KERNEL);
+	if (!mt9p012_sensorw) {
+		CDBG("kzalloc failed.\n");
+		rc = -ENOMEM;
+		goto probe_failure;
+	}
+
+	i2c_set_clientdata(client, mt9p012_sensorw);
+	mt9p012_init_client(client);
+	mt9p012_client = client;
+
+	mdelay(50);
+
+	CDBG("mt9p012_probe successed! rc = %d\n", rc);
+	return 0;
+
+probe_failure:
+	CDBG("mt9p012_probe failed! rc = %d\n", rc);
+	return rc;
+}
+
+static const struct i2c_device_id mt9p012_i2c_id[] = {
+	{"mt9p012", 0},
+	{}
+};
+
+static struct i2c_driver mt9p012_i2c_driver = {
+	.id_table = mt9p012_i2c_id,
+	.probe = mt9p012_i2c_probe,
+	.remove = __exit_p(mt9p012_i2c_remove),
+	.driver = {
+		   .name = "mt9p012",
+		   },
+};
+
+static int mt9p012_sensor_probe(const struct msm_camera_sensor_info *info,
+				struct msm_sensor_ctrl *s)
+{
+	int rc = i2c_add_driver(&mt9p012_i2c_driver);
+	if (rc < 0 || mt9p012_client == NULL) {
+		rc = -ENOTSUPP;
+		goto probe_done;
+	}
+
+	msm_camio_clk_rate_set(MT9P012_DEFAULT_CLOCK_RATE);
+	mdelay(20);
+
+	rc = mt9p012_probe_init_sensor(info);
+	if (rc < 0)
+		goto probe_done;
+
+	s->s_init = mt9p012_sensor_open_init;
+	s->s_release = mt9p012_sensor_release;
+	s->s_config = mt9p012_sensor_config;
+	s->s_mount_angle  = 0;
+	mt9p012_probe_init_done(info);
+
+probe_done:
+	CDBG("%s %s:%d\n", __FILE__, __func__, __LINE__);
+	return rc;
+}
+
+static int __mt9p012_probe(struct platform_device *pdev)
+{
+	return msm_camera_drv_start(pdev, mt9p012_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+	.probe = __mt9p012_probe,
+	.driver = {
+		   .name = "msm_camera_mt9p012",
+		   .owner = THIS_MODULE,
+		   },
+};
+
+static int __init mt9p012_init(void)
+{
+	return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(mt9p012_init);
diff --git a/drivers/media/video/msm/mt9p012_km.c b/drivers/media/video/msm/mt9p012_km.c
new file mode 100644
index 0000000..5ba0e62
--- /dev/null
+++ b/drivers/media/video/msm/mt9p012_km.c
@@ -0,0 +1,1296 @@
+/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/kernel.h>
+#include <media/msm_camera.h>
+#include <mach/gpio.h>
+#include <mach/camera.h>
+#include "mt9p012_km.h"
+
+/*=============================================================
+    SENSOR REGISTER DEFINES
+==============================================================*/
+
+#define MT9P012_KM_REG_MODEL_ID      0x0000
+#define MT9P012_KM_MODEL_ID          0x2800
+#define REG_GROUPED_PARAMETER_HOLD   0x0104
+#define GROUPED_PARAMETER_HOLD       0x0100
+#define GROUPED_PARAMETER_UPDATE     0x0000
+#define REG_COARSE_INT_TIME          0x3012
+#define REG_VT_PIX_CLK_DIV           0x0300
+#define REG_VT_SYS_CLK_DIV           0x0302
+#define REG_PRE_PLL_CLK_DIV          0x0304
+#define REG_PLL_MULTIPLIER           0x0306
+#define REG_OP_PIX_CLK_DIV           0x0308
+#define REG_OP_SYS_CLK_DIV           0x030A
+#define REG_SCALE_M                  0x0404
+#define REG_FRAME_LENGTH_LINES       0x300A
+#define REG_LINE_LENGTH_PCK          0x300C
+#define REG_X_ADDR_START             0x3004
+#define REG_Y_ADDR_START             0x3002
+#define REG_X_ADDR_END               0x3008
+#define REG_Y_ADDR_END               0x3006
+#define REG_X_OUTPUT_SIZE            0x034C
+#define REG_Y_OUTPUT_SIZE            0x034E
+#define REG_FINE_INTEGRATION_TIME    0x3014
+#define REG_ROW_SPEED                0x3016
+#define MT9P012_KM_REG_RESET_REGISTER   0x301A
+#define MT9P012_KM_RESET_REGISTER_PWON  0x10CC
+#define MT9P012_KM_RESET_REGISTER_PWOFF 0x10C8
+#define REG_READ_MODE                0x3040
+#define REG_GLOBAL_GAIN              0x305E
+#define REG_TEST_PATTERN_MODE        0x3070
+
+enum mt9p012_km_test_mode {
+	TEST_OFF,
+	TEST_1,
+	TEST_2,
+	TEST_3
+};
+
+enum mt9p012_km_resolution {
+	QTR_SIZE,
+	FULL_SIZE,
+	INVALID_SIZE
+};
+
+enum mt9p012_km_reg_update {
+	/* Sensor egisters that need to be updated during initialization */
+	REG_INIT,
+	/* Sensor egisters that needs periodic I2C writes */
+	UPDATE_PERIODIC,
+	/* All the sensor Registers will be updated */
+	UPDATE_ALL,
+	/* Not valid update */
+	UPDATE_INVALID
+};
+
+enum mt9p012_km_setting {
+	RES_PREVIEW,
+	RES_CAPTURE
+};
+
+uint8_t mode_mask = 0x04;
+
+/* actuator's Slave Address */
+#define MT9P012_KM_AF_I2C_ADDR   (0x18 >> 1)
+
+/* AF Total steps parameters */
+#define MT9P012_KM_STEPS_NEAR_TO_CLOSEST_INF  30
+#define MT9P012_KM_TOTAL_STEPS_NEAR_TO_FAR    30
+
+/* Time in milisecs for waiting for the sensor to reset.*/
+#define MT9P012_KM_RESET_DELAY_MSECS   66
+
+/* for 20 fps preview */
+#define MT9P012_KM_DEFAULT_CLOCK_RATE  24000000
+
+struct mt9p012_km_work {
+	struct work_struct work;
+};
+static struct mt9p012_km_work *mt9p012_km_sensorw;
+static struct i2c_client *mt9p012_km_client;
+
+struct mt9p012_km_ctrl {
+	const struct msm_camera_sensor_info *sensordata;
+
+	int sensormode;
+	uint32_t fps_divider;	/* init to 1 * 0x00000400 */
+	uint32_t pict_fps_divider;	/* init to 1 * 0x00000400 */
+
+	uint16_t curr_lens_pos;
+	uint16_t init_curr_lens_pos;
+	uint16_t my_reg_gain;
+	uint32_t my_reg_line_count;
+
+	enum mt9p012_km_resolution prev_res;
+	enum mt9p012_km_resolution pict_res;
+	enum mt9p012_km_resolution curr_res;
+	enum mt9p012_km_test_mode set_test;
+};
+static uint16_t update_type = UPDATE_PERIODIC;
+static struct mt9p012_km_ctrl *mt9p012_km_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(mt9p012_km_wait_queue);
+DEFINE_MUTEX(mt9p012_km_mut);
+
+/*=============================================================*/
+
+static int mt9p012_km_i2c_rxdata(unsigned short saddr, unsigned char *rxdata,
+			int length)
+{
+	struct i2c_msg msgs[] = {
+		{
+			.addr = saddr << 1,
+			.flags = 0,
+			.len = 2,
+			.buf = rxdata,
+		},
+		{
+			.addr = saddr << 1,
+			.flags = I2C_M_RD,
+			.len = length,
+			.buf = rxdata,
+		},
+	};
+
+	if (i2c_transfer(mt9p012_km_client->adapter, msgs, 2) < 0) {
+		CDBG("mt9p012_km_i2c_rxdata failed!\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int32_t mt9p012_km_i2c_read_w(unsigned short saddr, unsigned short raddr,
+				  unsigned short *rdata)
+{
+	int32_t rc = 0;
+	unsigned char buf[4];
+
+	if (!rdata)
+		return -EIO;
+
+	memset(buf, 0, sizeof(buf));
+
+	buf[0] = (raddr & 0xFF00) >> 8;
+	buf[1] = (raddr & 0x00FF);
+
+	rc = mt9p012_km_i2c_rxdata(saddr, buf, 2);
+	if (rc < 0)
+		return rc;
+
+	*rdata = buf[0] << 8 | buf[1];
+
+	if (rc < 0)
+		CDBG("mt9p012_km_i2c_read failed!\n");
+
+	return rc;
+}
+
+static int32_t mt9p012_km_i2c_txdata(unsigned short saddr,
+				  unsigned char *txdata,
+				  int length)
+{
+	struct i2c_msg msg[] = {
+		{
+		 .addr = saddr << 1,
+		 .flags = 0,
+		 .len = length,
+		 .buf = txdata,
+		 },
+	};
+
+	if (i2c_transfer(mt9p012_km_client->adapter, msg, 1) < 0) {
+		CDBG("mt9p012_km_i2c_txdata failed\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int32_t mt9p012_km_i2c_write_b(unsigned short saddr,
+				   unsigned short baddr,
+				   unsigned short bdata)
+{
+	int32_t rc = -EIO;
+	unsigned char buf[2];
+
+	memset(buf, 0, sizeof(buf));
+	buf[0] = baddr;
+	buf[1] = bdata;
+	rc = mt9p012_km_i2c_txdata(saddr, buf, 2);
+
+	if (rc < 0)
+		CDBG("i2c_write failed, saddr = 0x%x addr = 0x%x, val =0x%x!\n",
+		     saddr, baddr, bdata);
+
+	return rc;
+}
+
+static int32_t mt9p012_km_i2c_write_w(unsigned short saddr,
+				   unsigned short waddr,
+				   unsigned short wdata)
+{
+	int32_t rc = -EIO;
+	unsigned char buf[4];
+
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (waddr & 0xFF00) >> 8;
+	buf[1] = (waddr & 0x00FF);
+	buf[2] = (wdata & 0xFF00) >> 8;
+	buf[3] = (wdata & 0x00FF);
+
+	rc = mt9p012_km_i2c_txdata(saddr, buf, 4);
+
+	if (rc < 0)
+		CDBG("i2c_write_w failed, addr = 0x%x, val = 0x%x!\n",
+		     waddr, wdata);
+
+	return rc;
+}
+
+static int32_t mt9p012_km_i2c_write_w_table(struct mt9p012_km_i2c_reg_conf const
+					 *reg_conf_tbl, int num)
+{
+	int i;
+	int32_t rc = -EIO;
+
+	for (i = 0; i < num; i++) {
+		rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+					 reg_conf_tbl->waddr,
+					 reg_conf_tbl->wdata);
+		if (rc < 0)
+			break;
+		reg_conf_tbl++;
+	}
+
+	return rc;
+}
+
+static int32_t mt9p012_km_test(enum mt9p012_km_test_mode mo)
+{
+	int32_t rc = 0;
+
+	rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+				 REG_GROUPED_PARAMETER_HOLD,
+				 GROUPED_PARAMETER_HOLD);
+	if (rc < 0)
+		return rc;
+
+	if (mo == TEST_OFF)
+		return 0;
+	else {
+		rc = mt9p012_km_i2c_write_w_table(mt9p012_km_regs.ttbl,
+					 mt9p012_km_regs.ttbl_size);
+		if (rc < 0)
+			return rc;
+
+		rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+					 REG_TEST_PATTERN_MODE, (uint16_t) mo);
+		if (rc < 0)
+			return rc;
+	}
+
+	rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+				 REG_GROUPED_PARAMETER_HOLD,
+				 GROUPED_PARAMETER_UPDATE);
+	if (rc < 0)
+		return rc;
+
+	return rc;
+}
+
+static int32_t mt9p012_km_lens_shading_enable(uint8_t is_enable)
+{
+	int32_t rc = 0;
+
+	CDBG("%s: entered. enable = %d\n", __func__, is_enable);
+
+	rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+				 REG_GROUPED_PARAMETER_HOLD,
+				 GROUPED_PARAMETER_HOLD);
+	if (rc < 0)
+		return rc;
+
+	rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr, 0x3780,
+				 ((uint16_t) is_enable) << 15);
+	if (rc < 0)
+		return rc;
+
+	rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+				 REG_GROUPED_PARAMETER_HOLD,
+				 GROUPED_PARAMETER_UPDATE);
+
+	CDBG("%s: exiting. rc = %d\n", __func__, rc);
+	return rc;
+}
+
+static int32_t mt9p012_km_set_lc(void)
+{
+	int32_t rc;
+
+	rc = mt9p012_km_i2c_write_w_table(mt9p012_km_regs.lctbl,
+				       mt9p012_km_regs.lctbl_size);
+
+	return rc;
+}
+
+static void mt9p012_km_get_pict_fps(uint16_t fps, uint16_t *pfps)
+{
+
+	/* input fps is preview fps in Q8 format */
+	uint32_t divider;   /*Q10 */
+	uint32_t pclk_mult; /*Q10 */
+	uint32_t d1;
+	uint32_t d2;
+
+	d1 =
+		(uint32_t)(
+		(mt9p012_km_regs.reg_pat[RES_PREVIEW].frame_length_lines *
+		0x00000400) /
+		mt9p012_km_regs.reg_pat[RES_CAPTURE].frame_length_lines);
+
+	d2 =
+		(uint32_t)(
+		(mt9p012_km_regs.reg_pat[RES_PREVIEW].line_length_pck *
+		0x00000400) /
+		mt9p012_km_regs.reg_pat[RES_CAPTURE].line_length_pck);
+
+	divider = (uint32_t) (d1 * d2) / 0x00000400;
+
+	pclk_mult =
+		(uint32_t) ((mt9p012_km_regs.reg_pat[RES_CAPTURE].
+		pll_multiplier * 0x00000400) /
+		(mt9p012_km_regs.reg_pat[RES_PREVIEW].pll_multiplier));
+
+
+	/* Verify PCLK settings and frame sizes. */
+	*pfps = (uint16_t)((((fps * pclk_mult) / 0x00000400) * divider)/
+				0x00000400);
+}
+
+static uint16_t mt9p012_km_get_prev_lines_pf(void)
+{
+	if (mt9p012_km_ctrl->prev_res == QTR_SIZE)
+		return  mt9p012_km_regs.reg_pat[RES_PREVIEW].frame_length_lines;
+	else
+		return  mt9p012_km_regs.reg_pat[RES_CAPTURE].frame_length_lines;
+}
+
+static uint16_t mt9p012_km_get_prev_pixels_pl(void)
+{
+	if (mt9p012_km_ctrl->prev_res == QTR_SIZE)
+		return  mt9p012_km_regs.reg_pat[RES_PREVIEW].line_length_pck;
+	else
+		return  mt9p012_km_regs.reg_pat[RES_CAPTURE].line_length_pck;
+}
+
+static uint16_t mt9p012_km_get_pict_lines_pf(void)
+{
+	return  mt9p012_km_regs.reg_pat[RES_CAPTURE].frame_length_lines;
+}
+
+static uint16_t mt9p012_km_get_pict_pixels_pl(void)
+{
+	return  mt9p012_km_regs.reg_pat[RES_CAPTURE].line_length_pck;
+}
+
+static uint32_t mt9p012_km_get_pict_max_exp_lc(void)
+{
+	uint16_t snapshot_lines_per_frame;
+
+	if (mt9p012_km_ctrl->pict_res == QTR_SIZE)
+		snapshot_lines_per_frame =
+	    mt9p012_km_regs.reg_pat[RES_PREVIEW].frame_length_lines - 1;
+	else
+		snapshot_lines_per_frame =
+	    mt9p012_km_regs.reg_pat[RES_CAPTURE].frame_length_lines - 1;
+
+	return snapshot_lines_per_frame * 24;
+}
+
+static int32_t mt9p012_km_set_fps(struct fps_cfg *fps)
+{
+	int32_t rc = 0;
+
+	mt9p012_km_ctrl->fps_divider = fps->fps_div;
+	mt9p012_km_ctrl->pict_fps_divider = fps->pict_fps_div;
+
+	rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+				 REG_GROUPED_PARAMETER_HOLD,
+				 GROUPED_PARAMETER_HOLD);
+	if (rc < 0)
+		return -EBUSY;
+
+	rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+			REG_FRAME_LENGTH_LINES,
+			mt9p012_km_regs.reg_pat[mt9p012_km_ctrl->sensormode].
+			frame_length_lines *
+			mt9p012_km_ctrl->fps_divider / 0x00000400);
+	if (rc < 0)
+		return rc;
+
+	rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+				 REG_GROUPED_PARAMETER_HOLD,
+				 GROUPED_PARAMETER_UPDATE);
+
+	return rc;
+}
+
+
+static int32_t mt9p012_km_write_exp_gain(uint16_t gain, uint32_t line)
+{
+	uint16_t max_legal_gain = 0x01FF;
+	uint32_t line_length_ratio = 0x00000400;
+	enum mt9p012_km_setting setting;
+	int32_t rc = 0;
+
+	CDBG("Line:%d mt9p012_km_write_exp_gain \n", __LINE__);
+
+	if (mt9p012_km_ctrl->sensormode == SENSOR_PREVIEW_MODE) {
+		mt9p012_km_ctrl->my_reg_gain = gain;
+		mt9p012_km_ctrl->my_reg_line_count = (uint16_t) line;
+	}
+
+	if (gain > max_legal_gain) {
+		CDBG("Max legal gain Line:%d \n", __LINE__);
+		gain = max_legal_gain;
+	}
+
+	/* Verify no overflow */
+	if (mt9p012_km_ctrl->sensormode == SENSOR_PREVIEW_MODE) {
+		line = (uint32_t) (line * mt9p012_km_ctrl->fps_divider /
+				   0x00000400);
+		setting = RES_PREVIEW;
+	} else {
+		line = (uint32_t) (line * mt9p012_km_ctrl->pict_fps_divider /
+				   0x00000400);
+		setting = RES_CAPTURE;
+	}
+
+	gain |= 0x0200;
+
+	if ((mt9p012_km_regs.reg_pat[setting].frame_length_lines - 1) < line) {
+		line_length_ratio = (uint32_t) (line * 0x00000400) /
+		    (mt9p012_km_regs.reg_pat[setting].frame_length_lines - 1);
+	} else
+		line_length_ratio = 0x00000400;
+
+	rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+				REG_GROUPED_PARAMETER_HOLD,
+				GROUPED_PARAMETER_HOLD);
+	if (rc < 0) {
+		CDBG("mt9p012_km_i2c_write_w failed... Line:%d \n", __LINE__);
+		return rc;
+	}
+
+	rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+				 REG_GLOBAL_GAIN, gain);
+	if (rc < 0) {
+		CDBG("mt9p012_km_i2c_write_w failed... Line:%d \n", __LINE__);
+		return rc;
+	}
+
+	rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+				REG_LINE_LENGTH_PCK,
+			       (uint16_t) (mt9p012_km_regs.reg_pat[setting].
+			    line_length_pck * line_length_ratio / 0x00000400));
+	if (rc < 0)
+		return rc;
+
+	rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+				 REG_COARSE_INT_TIME,
+				 (uint16_t) ((line * 0x00000400)/
+				 line_length_ratio));
+	if (rc < 0) {
+		CDBG("mt9p012_km_i2c_write_w failed... Line:%d \n", __LINE__);
+		return rc;
+	}
+
+	rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+				 REG_GROUPED_PARAMETER_HOLD,
+				 GROUPED_PARAMETER_UPDATE);
+	if (rc < 0) {
+		CDBG("mt9p012_km_i2c_write_w failed... Line:%d \n", __LINE__);
+		return rc;
+	}
+
+	CDBG("mt9p012_km_write_exp_gain: gain = %d, line = %d\n", gain, line);
+
+	return rc;
+}
+
+static int32_t mt9p012_km_set_pict_exp_gain(uint16_t gain, uint32_t line)
+{
+	int32_t rc = 0;
+
+	CDBG("Line:%d mt9p012_km_set_pict_exp_gain \n", __LINE__);
+
+	rc = mt9p012_km_write_exp_gain(gain, line);
+	if (rc < 0) {
+		CDBG("Line:%d mt9p012_km_set_pict_exp_gain failed... \n",
+		     __LINE__);
+		return rc;
+	}
+
+	rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+				 MT9P012_KM_REG_RESET_REGISTER,
+				 0x10CC | 0x0002);
+	if (rc < 0) {
+		CDBG("mt9p012_km_i2c_write_w failed... Line:%d \n", __LINE__);
+		return rc;
+	}
+
+	mdelay(5);
+
+	/* camera_timed_wait(snapshot_wait*exposure_ratio); */
+	return rc;
+}
+
+static int32_t mt9p012_km_setting(enum mt9p012_km_reg_update rupdate,
+				enum mt9p012_km_setting rt)
+{
+	int32_t rc = 0;
+
+	switch (rupdate) {
+	case UPDATE_PERIODIC:
+		if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+
+			struct mt9p012_km_i2c_reg_conf ppc_tbl[] = {
+				{REG_GROUPED_PARAMETER_HOLD,
+				 GROUPED_PARAMETER_HOLD},
+				{REG_ROW_SPEED,
+				 mt9p012_km_regs.reg_pat[rt].row_speed},
+				{REG_X_ADDR_START,
+				 mt9p012_km_regs.reg_pat[rt].x_addr_start},
+				{REG_X_ADDR_END,
+				 mt9p012_km_regs.reg_pat[rt].x_addr_end},
+				{REG_Y_ADDR_START,
+				 mt9p012_km_regs.reg_pat[rt].y_addr_start},
+				{REG_Y_ADDR_END,
+				 mt9p012_km_regs.reg_pat[rt].y_addr_end},
+				{REG_READ_MODE,
+				 mt9p012_km_regs.reg_pat[rt].read_mode},
+				{REG_SCALE_M,
+				 mt9p012_km_regs.reg_pat[rt].scale_m},
+				{REG_X_OUTPUT_SIZE,
+				 mt9p012_km_regs.reg_pat[rt].x_output_size},
+				{REG_Y_OUTPUT_SIZE,
+				 mt9p012_km_regs.reg_pat[rt].y_output_size},
+				{REG_LINE_LENGTH_PCK,
+				 mt9p012_km_regs.reg_pat[rt].line_length_pck},
+				{REG_FRAME_LENGTH_LINES,
+			       (mt9p012_km_regs.reg_pat[rt].frame_length_lines *
+				mt9p012_km_ctrl->fps_divider / 0x00000400)},
+				{REG_COARSE_INT_TIME,
+				 mt9p012_km_regs.reg_pat[rt].coarse_int_time},
+				{REG_FINE_INTEGRATION_TIME,
+				 mt9p012_km_regs.reg_pat[rt].fine_int_time},
+				{REG_GROUPED_PARAMETER_HOLD,
+				 GROUPED_PARAMETER_UPDATE},
+			};
+
+			if (update_type == REG_INIT) {
+				update_type = rupdate;
+				return rc;
+			}
+
+			rc = mt9p012_km_i2c_write_w_table(&ppc_tbl[0],
+						ARRAY_SIZE(ppc_tbl));
+			if (rc < 0)
+				return rc;
+
+			rc = mt9p012_km_test(mt9p012_km_ctrl->set_test);
+			if (rc < 0)
+				return rc;
+
+			rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+						 MT9P012_KM_REG_RESET_REGISTER,
+						 0x10cc |
+						 0x0002);
+			if (rc < 0)
+				return rc;
+
+			mdelay(15);	/* 15? wait for sensor to transition */
+
+			return rc;
+		}
+		break;	/* UPDATE_PERIODIC */
+
+	case REG_INIT:
+		if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+			struct mt9p012_km_i2c_reg_conf ipc_tbl1[] = {
+				{MT9P012_KM_REG_RESET_REGISTER,
+				 MT9P012_KM_RESET_REGISTER_PWOFF},
+				{REG_VT_PIX_CLK_DIV,
+				 mt9p012_km_regs.reg_pat[rt].vt_pix_clk_div},
+				{REG_VT_SYS_CLK_DIV,
+				 mt9p012_km_regs.reg_pat[rt].vt_sys_clk_div},
+				{REG_PRE_PLL_CLK_DIV,
+				 mt9p012_km_regs.reg_pat[rt].pre_pll_clk_div},
+				{REG_PLL_MULTIPLIER,
+				 mt9p012_km_regs.reg_pat[rt].pll_multiplier},
+				{REG_OP_PIX_CLK_DIV,
+				 mt9p012_km_regs.reg_pat[rt].op_pix_clk_div},
+				{REG_OP_SYS_CLK_DIV,
+				 mt9p012_km_regs.reg_pat[rt].op_sys_clk_div},
+				{MT9P012_KM_REG_RESET_REGISTER,
+				 MT9P012_KM_RESET_REGISTER_PWON},
+			};
+
+			struct mt9p012_km_i2c_reg_conf ipc_tbl2[] = {
+				{REG_GROUPED_PARAMETER_HOLD,
+				 GROUPED_PARAMETER_HOLD},
+				/* Optimized register settings for
+				   Rev3 Silicon */
+				{0x308A, 0x6424},
+				{0x3092, 0x0A52},
+				{0x3094, 0x4656},
+				{0x3096, 0x5652},
+				{0x0204, 0x0010},
+				{0x0206, 0x0010},
+				{0x0208, 0x0010},
+				{0x020A, 0x0010},
+				{0x020C, 0x0010},
+				{0x3088, 0x6FF6},
+				{0x3154, 0x0282},
+				{0x3156, 0x0381},
+				{0x3162, 0x04CE},
+			};
+
+			struct mt9p012_km_i2c_reg_conf ipc_tbl3[] = {
+				/* Set preview or snapshot mode */
+				{REG_ROW_SPEED,
+				 mt9p012_km_regs.reg_pat[rt].row_speed},
+				{REG_X_ADDR_START,
+				 mt9p012_km_regs.reg_pat[rt].x_addr_start},
+				{REG_X_ADDR_END,
+				 mt9p012_km_regs.reg_pat[rt].x_addr_end},
+				{REG_Y_ADDR_START,
+				 mt9p012_km_regs.reg_pat[rt].y_addr_start},
+				{REG_Y_ADDR_END,
+				 mt9p012_km_regs.reg_pat[rt].y_addr_end},
+				{REG_READ_MODE,
+				 mt9p012_km_regs.reg_pat[rt].read_mode},
+				{REG_SCALE_M,
+				 mt9p012_km_regs.reg_pat[rt].scale_m},
+				{REG_X_OUTPUT_SIZE,
+				 mt9p012_km_regs.reg_pat[rt].x_output_size},
+				{REG_Y_OUTPUT_SIZE,
+				 mt9p012_km_regs.reg_pat[rt].y_output_size},
+				{REG_LINE_LENGTH_PCK,
+				 mt9p012_km_regs.reg_pat[rt].line_length_pck},
+				{REG_FRAME_LENGTH_LINES,
+				 mt9p012_km_regs.reg_pat[rt].
+				 frame_length_lines},
+				{REG_COARSE_INT_TIME,
+				 mt9p012_km_regs.reg_pat[rt].coarse_int_time},
+				{REG_FINE_INTEGRATION_TIME,
+				 mt9p012_km_regs.reg_pat[rt].fine_int_time},
+				{REG_GROUPED_PARAMETER_HOLD,
+				 GROUPED_PARAMETER_UPDATE},
+			};
+
+			/* reset fps_divider */
+			mt9p012_km_ctrl->fps_divider = 1 * 0x0400;
+
+			rc = mt9p012_km_i2c_write_w_table(&ipc_tbl1[0],
+							ARRAY_SIZE(ipc_tbl1));
+			if (rc < 0)
+				return rc;
+
+			mdelay(15);
+
+			rc = mt9p012_km_i2c_write_w_table(&ipc_tbl2[0],
+							ARRAY_SIZE(ipc_tbl2));
+			if (rc < 0)
+				return rc;
+
+			mdelay(5);
+
+			rc = mt9p012_km_i2c_write_w_table(&ipc_tbl3[0],
+						       ARRAY_SIZE(ipc_tbl3));
+			if (rc < 0)
+				return rc;
+
+			/* load lens shading */
+			rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+						 REG_GROUPED_PARAMETER_HOLD,
+						 GROUPED_PARAMETER_HOLD);
+			if (rc < 0)
+				return rc;
+
+			rc = mt9p012_km_set_lc();
+			if (rc < 0)
+				return rc;
+
+			rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+						 REG_GROUPED_PARAMETER_HOLD,
+						 GROUPED_PARAMETER_UPDATE);
+
+			if (rc < 0)
+				return rc;
+		}
+		update_type = rupdate;
+		break;		/* case REG_INIT: */
+
+	default:
+		rc = -EINVAL;
+		break;
+	}			/* switch (rupdate) */
+
+	return rc;
+}
+
+static int32_t mt9p012_km_video_config(int mode, int res)
+{
+	int32_t rc;
+
+	switch (res) {
+	case QTR_SIZE:
+		rc = mt9p012_km_setting(UPDATE_PERIODIC, RES_PREVIEW);
+		if (rc < 0)
+			return rc;
+
+		CDBG("mt9p012_km sensor configuration done!\n");
+		break;
+
+	case FULL_SIZE:
+		rc = mt9p012_km_setting(UPDATE_PERIODIC, RES_CAPTURE);
+		if (rc < 0)
+			return rc;
+
+		break;
+
+	default:
+		return 0;
+	}			/* switch */
+
+	mt9p012_km_ctrl->prev_res = res;
+	mt9p012_km_ctrl->curr_res = res;
+	mt9p012_km_ctrl->sensormode = mode;
+
+	rc = mt9p012_km_write_exp_gain(mt9p012_km_ctrl->my_reg_gain,
+				    mt9p012_km_ctrl->my_reg_line_count);
+
+	rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+				 MT9P012_KM_REG_RESET_REGISTER,
+				 0x10cc | 0x0002);
+
+	mdelay(15);
+	return rc;
+}
+
+static int32_t mt9p012_km_snapshot_config(int mode)
+{
+	int32_t rc = 0;
+
+	rc = mt9p012_km_setting(UPDATE_PERIODIC, RES_CAPTURE);
+	if (rc < 0)
+		return rc;
+
+	mt9p012_km_ctrl->curr_res = mt9p012_km_ctrl->pict_res;
+
+	mt9p012_km_ctrl->sensormode = mode;
+
+	return rc;
+}
+
+static int32_t mt9p012_km_raw_snapshot_config(int mode)
+{
+	int32_t rc = 0;
+
+	rc = mt9p012_km_setting(UPDATE_PERIODIC, RES_CAPTURE);
+	if (rc < 0)
+		return rc;
+
+	mt9p012_km_ctrl->curr_res = mt9p012_km_ctrl->pict_res;
+
+	mt9p012_km_ctrl->sensormode = mode;
+
+	return rc;
+}
+
+static int32_t mt9p012_km_power_down(void)
+{
+	int32_t rc = 0;
+
+	rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+				 MT9P012_KM_REG_RESET_REGISTER,
+				 MT9P012_KM_RESET_REGISTER_PWOFF);
+
+	mdelay(5);
+	return rc;
+}
+
+static int32_t mt9p012_km_move_focus(int direction, int32_t num_steps)
+{
+	int16_t step_direction;
+	int16_t actual_step;
+	int16_t next_position;
+	uint8_t code_val_msb, code_val_lsb;
+
+	if (num_steps > MT9P012_KM_TOTAL_STEPS_NEAR_TO_FAR)
+		num_steps = MT9P012_KM_TOTAL_STEPS_NEAR_TO_FAR;
+	else if (num_steps == 0) {
+		CDBG("mt9p012_km_move_focus failed at line %d ...\n", __LINE__);
+		return -EINVAL;
+	}
+
+	if (direction == MOVE_NEAR)
+		step_direction = 16;	/* 10bit */
+	else if (direction == MOVE_FAR)
+		step_direction = -16;	/* 10 bit */
+	else {
+		CDBG("mt9p012_km_move_focus failed at line %d ...\n", __LINE__);
+		return -EINVAL;
+	}
+
+	if (mt9p012_km_ctrl->curr_lens_pos <
+				mt9p012_km_ctrl->init_curr_lens_pos)
+		mt9p012_km_ctrl->curr_lens_pos =
+				mt9p012_km_ctrl->init_curr_lens_pos;
+
+	actual_step = (int16_t) (step_direction * (int16_t) num_steps);
+	next_position = (int16_t) (mt9p012_km_ctrl->curr_lens_pos +
+							actual_step);
+
+	if (next_position > 1023)
+		next_position = 1023;
+	else if (next_position < 0)
+		next_position = 0;
+
+	code_val_msb = next_position >> 4;
+	code_val_lsb = (next_position & 0x000F) << 4;
+	code_val_lsb |= mode_mask;
+
+	/* Writing the digital code for current to the actuator */
+	if (mt9p012_km_i2c_write_b(MT9P012_KM_AF_I2C_ADDR >> 1,
+				code_val_msb, code_val_lsb) < 0) {
+		CDBG("mt9p012_km_move_focus failed at line %d ...\n", __LINE__);
+		return -EBUSY;
+	}
+
+	/* Storing the current lens Position */
+	mt9p012_km_ctrl->curr_lens_pos = next_position;
+
+	return 0;
+}
+
+static int32_t mt9p012_km_set_default_focus(void)
+{
+	int32_t rc = 0;
+	uint8_t code_val_msb, code_val_lsb;
+
+	code_val_msb = 0x00;
+	code_val_lsb = 0x04;
+
+	/* Write the digital code for current to the actuator */
+	rc = mt9p012_km_i2c_write_b(MT9P012_KM_AF_I2C_ADDR >> 1,
+				 code_val_msb, code_val_lsb);
+
+	mt9p012_km_ctrl->curr_lens_pos = 0;
+	mt9p012_km_ctrl->init_curr_lens_pos = 0;
+
+	return rc;
+}
+
+static int mt9p012_km_probe_init_done(const struct msm_camera_sensor_info *data)
+{
+	gpio_direction_output(data->sensor_reset, 0);
+	gpio_free(data->sensor_reset);
+	return 0;
+}
+
+static int
+	mt9p012_km_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+	int32_t rc;
+	uint16_t chipid;
+
+	rc = gpio_request(data->sensor_reset, "mt9p012_km");
+	if (!rc)
+		gpio_direction_output(data->sensor_reset, 1);
+	else
+		goto init_probe_done;
+
+	msleep(20);
+
+	/* RESET the sensor image part via I2C command */
+	CDBG("mt9p012_km_sensor_init(): reseting sensor.\n");
+	rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+				 MT9P012_KM_REG_RESET_REGISTER,
+				 0x10CC | 0x0001);
+	if (rc < 0) {
+		CDBG("sensor reset failed. rc = %d\n", rc);
+		goto init_probe_fail;
+	}
+
+		msleep(MT9P012_KM_RESET_DELAY_MSECS);
+
+	/* 3. Read sensor Model ID: */
+	rc = mt9p012_km_i2c_read_w(mt9p012_km_client->addr,
+				MT9P012_KM_REG_MODEL_ID, &chipid);
+	if (rc < 0)
+		goto init_probe_fail;
+
+	/* 4. Compare sensor ID to MT9T012VC ID: */
+	if (chipid != MT9P012_KM_MODEL_ID) {
+		CDBG("mt9p012_km wrong model_id = 0x%x\n", chipid);
+		rc = -ENODEV;
+		goto init_probe_fail;
+	}
+
+	rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr, 0x306E, 0x9080);
+	if (rc < 0) {
+		CDBG("REV_7 write failed. rc = %d\n", rc);
+		goto init_probe_fail;
+	}
+
+	/* RESET_REGISTER, enable parallel interface and disable serialiser */
+	CDBG("mt9p012_km_sensor_init(): enabling parallel interface.\n");
+	rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr, 0x301A, 0x10CC);
+	if (rc < 0) {
+		CDBG("enable parallel interface failed. rc = %d\n", rc);
+		goto init_probe_fail;
+	}
+
+	/* To disable the 2 extra lines */
+	rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr, 0x3064, 0x0805);
+
+	if (rc < 0) {
+		CDBG("disable the 2 extra lines failed. rc = %d\n", rc);
+		goto init_probe_fail;
+	}
+
+	goto init_probe_done;
+
+init_probe_fail:
+	mt9p012_km_probe_init_done(data);
+init_probe_done:
+	return rc;
+}
+
+static int
+	mt9p012_km_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+	int32_t rc;
+
+	mt9p012_km_ctrl = kzalloc(sizeof(struct mt9p012_km_ctrl), GFP_KERNEL);
+	if (!mt9p012_km_ctrl) {
+		CDBG("mt9p012_km_init failed!\n");
+		rc = -ENOMEM;
+		goto init_done;
+	}
+
+	mt9p012_km_ctrl->fps_divider = 1 * 0x00000400;
+	mt9p012_km_ctrl->pict_fps_divider = 1 * 0x00000400;
+	mt9p012_km_ctrl->set_test = TEST_OFF;
+	mt9p012_km_ctrl->prev_res = QTR_SIZE;
+	mt9p012_km_ctrl->pict_res = FULL_SIZE;
+
+	if (data)
+		mt9p012_km_ctrl->sensordata = data;
+
+	msm_camio_camif_pad_reg_reset();
+	mdelay(20);
+
+	rc = mt9p012_km_probe_init_sensor(data);
+	if (rc < 0)
+		goto init_fail1;
+
+	if (mt9p012_km_ctrl->prev_res == QTR_SIZE)
+		rc = mt9p012_km_setting(REG_INIT, RES_PREVIEW);
+	else
+		rc = mt9p012_km_setting(REG_INIT, RES_CAPTURE);
+
+	if (rc < 0) {
+		CDBG("mt9p012_km_setting failed. rc = %d\n", rc);
+		goto init_fail1;
+	}
+
+	/* sensor : output enable */
+	CDBG("mt9p012_km_sensor_open_init(): enabling output.\n");
+	rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+				 MT9P012_KM_REG_RESET_REGISTER,
+				 MT9P012_KM_RESET_REGISTER_PWON);
+	if (rc < 0) {
+		CDBG("sensor output enable failed. rc = %d\n", rc);
+		goto init_fail1;
+	}
+
+	if (rc >= 0)
+		goto init_done;
+
+init_fail1:
+	mt9p012_km_probe_init_done(data);
+	kfree(mt9p012_km_ctrl);
+init_done:
+	return rc;
+}
+
+static int mt9p012_km_init_client(struct i2c_client *client)
+{
+	/* Initialize the MSM_CAMI2C Chip */
+	init_waitqueue_head(&mt9p012_km_wait_queue);
+	return 0;
+}
+
+static int32_t mt9p012_km_set_sensor_mode(int mode, int res)
+{
+	int32_t rc = 0;
+
+	switch (mode) {
+	case SENSOR_PREVIEW_MODE:
+		rc = mt9p012_km_video_config(mode, res);
+		break;
+
+	case SENSOR_SNAPSHOT_MODE:
+		rc = mt9p012_km_snapshot_config(mode);
+		break;
+
+	case SENSOR_RAW_SNAPSHOT_MODE:
+		rc = mt9p012_km_raw_snapshot_config(mode);
+		break;
+
+	default:
+		rc = -EINVAL;
+		break;
+	}
+
+	return rc;
+}
+
+int mt9p012_km_sensor_config(void __user *argp)
+{
+	struct sensor_cfg_data cdata;
+	int rc = 0;
+
+	if (copy_from_user(&cdata,
+			   (void *)argp, sizeof(struct sensor_cfg_data)))
+		return -EFAULT;
+
+	mutex_lock(&mt9p012_km_mut);
+
+	CDBG("%s: cfgtype = %d\n", __func__, cdata.cfgtype);
+	switch (cdata.cfgtype) {
+	case CFG_GET_PICT_FPS:
+		mt9p012_km_get_pict_fps(cdata.cfg.gfps.prevfps,
+				     &(cdata.cfg.gfps.pictfps));
+
+		if (copy_to_user((void *)argp, &cdata,
+				 sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PREV_L_PF:
+		cdata.cfg.prevl_pf = mt9p012_km_get_prev_lines_pf();
+
+		if (copy_to_user((void *)argp,
+				 &cdata, sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PREV_P_PL:
+		cdata.cfg.prevp_pl = mt9p012_km_get_prev_pixels_pl();
+
+		if (copy_to_user((void *)argp,
+				 &cdata, sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PICT_L_PF:
+		cdata.cfg.pictl_pf = mt9p012_km_get_pict_lines_pf();
+
+		if (copy_to_user((void *)argp,
+				 &cdata, sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PICT_P_PL:
+		cdata.cfg.pictp_pl = mt9p012_km_get_pict_pixels_pl();
+
+		if (copy_to_user((void *)argp,
+				 &cdata, sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PICT_MAX_EXP_LC:
+		cdata.cfg.pict_max_exp_lc = mt9p012_km_get_pict_max_exp_lc();
+
+		if (copy_to_user((void *)argp,
+				 &cdata, sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_SET_FPS:
+	case CFG_SET_PICT_FPS:
+		rc = mt9p012_km_set_fps(&(cdata.cfg.fps));
+		break;
+
+	case CFG_SET_EXP_GAIN:
+		rc = mt9p012_km_write_exp_gain(cdata.cfg.exp_gain.gain,
+					    cdata.cfg.exp_gain.line);
+		break;
+
+	case CFG_SET_PICT_EXP_GAIN:
+		CDBG("Line:%d CFG_SET_PICT_EXP_GAIN \n", __LINE__);
+		rc = mt9p012_km_set_pict_exp_gain(cdata.cfg.exp_gain.gain,
+					       cdata.cfg.exp_gain.line);
+		break;
+
+	case CFG_SET_MODE:
+		rc = mt9p012_km_set_sensor_mode(cdata.mode, cdata.rs);
+		break;
+
+	case CFG_PWR_DOWN:
+		rc = mt9p012_km_power_down();
+		break;
+
+	case CFG_MOVE_FOCUS:
+		CDBG("mt9p012_km_ioctl: CFG_MOVE_FOCUS: cdata.cfg.focus.dir=%d \
+				cdata.cfg.focus.steps=%d\n",
+				cdata.cfg.focus.dir, cdata.cfg.focus.steps);
+		rc = mt9p012_km_move_focus(cdata.cfg.focus.dir,
+					cdata.cfg.focus.steps);
+		break;
+
+	case CFG_SET_DEFAULT_FOCUS:
+		rc = mt9p012_km_set_default_focus();
+		break;
+
+	case CFG_SET_LENS_SHADING:
+		CDBG("%s: CFG_SET_LENS_SHADING\n", __func__);
+		rc = mt9p012_km_lens_shading_enable(cdata.cfg.lens_shading);
+		break;
+
+	case CFG_GET_AF_MAX_STEPS:
+		cdata.max_steps = MT9P012_KM_STEPS_NEAR_TO_CLOSEST_INF;
+		if (copy_to_user((void *)argp,
+				 &cdata, sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_SET_EFFECT:
+	default:
+		rc = -EINVAL;
+		break;
+	}
+
+	mutex_unlock(&mt9p012_km_mut);
+	return rc;
+}
+
+int mt9p012_km_sensor_release(void)
+{
+	int rc = -EBADF;
+
+	mutex_lock(&mt9p012_km_mut);
+
+	mt9p012_km_power_down();
+
+	gpio_direction_output(mt9p012_km_ctrl->sensordata->sensor_reset, 0);
+	gpio_free(mt9p012_km_ctrl->sensordata->sensor_reset);
+
+	gpio_direction_output(mt9p012_km_ctrl->sensordata->vcm_pwd, 0);
+	gpio_free(mt9p012_km_ctrl->sensordata->vcm_pwd);
+
+	kfree(mt9p012_km_ctrl);
+	mt9p012_km_ctrl = NULL;
+
+	CDBG("mt9p012_km_release completed\n");
+
+	mutex_unlock(&mt9p012_km_mut);
+	return rc;
+}
+
+static int mt9p012_km_i2c_probe(struct i2c_client *client,
+			     const struct i2c_device_id *id)
+{
+	int rc = 0;
+	CDBG("mt9p012_km_probe called!\n");
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		CDBG("i2c_check_functionality failed\n");
+		goto probe_failure;
+	}
+
+	mt9p012_km_sensorw = kzalloc(sizeof(struct mt9p012_km_work),
+							GFP_KERNEL);
+	if (!mt9p012_km_sensorw) {
+		CDBG("kzalloc failed.\n");
+		rc = -ENOMEM;
+		goto probe_failure;
+	}
+
+	i2c_set_clientdata(client, mt9p012_km_sensorw);
+	mt9p012_km_init_client(client);
+	mt9p012_km_client = client;
+
+	mdelay(50);
+
+	CDBG("mt9p012_km_probe successed! rc = %d\n", rc);
+	return 0;
+
+probe_failure:
+	CDBG("mt9p012_km_probe failed! rc = %d\n", rc);
+	return rc;
+}
+
+static const struct i2c_device_id mt9p012_km_i2c_id[] = {
+	{"mt9p012_km", 0},
+	{}
+};
+
+static struct i2c_driver mt9p012_km_i2c_driver = {
+	.id_table = mt9p012_km_i2c_id,
+	.probe = mt9p012_km_i2c_probe,
+	.remove = __exit_p(mt9p012_km_i2c_remove),
+	.driver = {
+		   .name = "mt9p012_km",
+		   },
+};
+
+static int mt9p012_km_sensor_probe(const struct msm_camera_sensor_info *info,
+				struct msm_sensor_ctrl *s)
+{
+	int rc = i2c_add_driver(&mt9p012_km_i2c_driver);
+	if (rc < 0 || mt9p012_km_client == NULL) {
+		rc = -ENOTSUPP;
+		goto probe_done;
+	}
+
+	msm_camio_clk_rate_set(MT9P012_KM_DEFAULT_CLOCK_RATE);
+	mdelay(20);
+
+	rc = mt9p012_km_probe_init_sensor(info);
+	if (rc < 0)
+		goto probe_done;
+
+	s->s_init = mt9p012_km_sensor_open_init;
+	s->s_release = mt9p012_km_sensor_release;
+	s->s_config = mt9p012_km_sensor_config;
+	s->s_mount_angle  = 0;
+	mt9p012_km_probe_init_done(info);
+
+probe_done:
+	CDBG("%s %s:%d\n", __FILE__, __func__, __LINE__);
+	return rc;
+}
+
+static int __mt9p012_km_probe(struct platform_device *pdev)
+{
+	return msm_camera_drv_start(pdev, mt9p012_km_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+	.probe = __mt9p012_km_probe,
+	.driver = {
+		   .name = "msm_camera_mt9p012_km",
+		   .owner = THIS_MODULE,
+		   },
+};
+
+static int __init mt9p012_km_init(void)
+{
+	return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(mt9p012_km_init);
diff --git a/drivers/media/video/msm/mt9p012_km.h b/drivers/media/video/msm/mt9p012_km.h
new file mode 100644
index 0000000..aefabd4
--- /dev/null
+++ b/drivers/media/video/msm/mt9p012_km.h
@@ -0,0 +1,61 @@
+/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef MT9P012_KM_H
+#define MT9P012_KM_H
+
+#include <linux/types.h>
+
+extern struct mt9p012_km_reg mt9p012_km_regs;	/* from mt9p012_km_reg.c */
+
+struct reg_struct {
+	uint16_t vt_pix_clk_div;     /* 0x0300 */
+	uint16_t vt_sys_clk_div;     /* 0x0302 */
+	uint16_t pre_pll_clk_div;    /* 0x0304 */
+	uint16_t pll_multiplier;     /* 0x0306 */
+	uint16_t op_pix_clk_div;     /* 0x0308 */
+	uint16_t op_sys_clk_div;     /* 0x030A */
+	uint16_t scale_m;            /* 0x0404 */
+	uint16_t row_speed;          /* 0x3016 */
+	uint16_t x_addr_start;       /* 0x3004 */
+	uint16_t x_addr_end;         /* 0x3008 */
+	uint16_t y_addr_start;       /* 0x3002 */
+	uint16_t y_addr_end;         /* 0x3006 */
+	uint16_t read_mode;          /* 0x3040 */
+	uint16_t x_output_size ;     /* 0x034C */
+	uint16_t y_output_size;      /* 0x034E */
+	uint16_t line_length_pck;    /* 0x300C */
+	uint16_t frame_length_lines; /* 0x300A */
+	uint16_t coarse_int_time;    /* 0x3012 */
+	uint16_t fine_int_time;      /* 0x3014 */
+};
+
+
+struct mt9p012_km_i2c_reg_conf {
+	unsigned short waddr;
+	unsigned short wdata;
+};
+
+
+struct mt9p012_km_reg {
+	struct reg_struct const *reg_pat;
+	uint16_t reg_pat_size;
+	struct mt9p012_km_i2c_reg_conf const *ttbl;
+	uint16_t ttbl_size;
+	struct mt9p012_km_i2c_reg_conf const *lctbl;
+	uint16_t lctbl_size;
+	struct mt9p012_km_i2c_reg_conf const *rftbl;
+	uint16_t rftbl_size;
+};
+
+#endif /* MT9P012_KM_H */
diff --git a/drivers/media/video/msm/mt9p012_km_reg.c b/drivers/media/video/msm/mt9p012_km_reg.c
new file mode 100644
index 0000000..109930b
--- /dev/null
+++ b/drivers/media/video/msm/mt9p012_km_reg.c
@@ -0,0 +1,375 @@
+/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "mt9p012_km.h"
+#include <linux/kernel.h>
+
+/*Micron settings from Applications for lower power consumption.*/
+struct reg_struct const mt9p012_km_reg_pat[2] = {
+	{ /* Preview */
+		/* vt_pix_clk_div          REG=0x0300 */
+		6,  /* 5 */
+
+		/* vt_sys_clk_div          REG=0x0302 */
+		1,
+
+		/* pre_pll_clk_div         REG=0x0304 */
+		2,
+
+		/* pll_multiplier          REG=0x0306 */
+		60,
+
+		/* op_pix_clk_div          REG=0x0308 */
+		8,  /* 10 */
+
+		/* op_sys_clk_div          REG=0x030A */
+		1,
+
+		/* scale_m                 REG=0x0404 */
+		16,
+
+		/* row_speed               REG=0x3016 */
+		0x0111,
+
+		/* x_addr_start            REG=0x3004 */
+		8,
+
+		/* x_addr_end              REG=0x3008 */
+		2597,
+
+		/* y_addr_start            REG=0x3002 */
+		8,
+
+		/* y_addr_end              REG=0x3006 */
+		1949,
+
+		/* read_mode               REG=0x3040
+		 * Preview 2x2 skipping */
+		0x006C,
+
+		/* x_output_size           REG=0x034C */
+		1296,
+
+		/* y_output_size           REG=0x034E */
+		972,
+
+		/* line_length_pck         REG=0x300C */
+		3783,
+
+		/* frame_length_lines      REG=0x300A */
+		1074,
+
+		/* coarse_integration_time REG=0x3012 */
+		16,
+
+		/* fine_integration_time   REG=0x3014 */
+		1764
+	},
+	{ /* Snapshot */
+		/* vt_pix_clk_div          REG=0x0300 */
+		6,
+
+		/* vt_sys_clk_div          REG=0x0302 */
+		1,
+
+		/* pre_pll_clk_div         REG=0x0304 */
+		2,
+
+		/* pll_multiplier          REG=0x0306
+		 * 39 for 10fps snapshot */
+		39,
+
+		/* op_pix_clk_div          REG=0x0308 */
+		8,
+
+		/* op_sys_clk_div          REG=0x030A */
+		1,
+
+		/* scale_m                 REG=0x0404 */
+		16,
+
+		/* row_speed               REG=0x3016 */
+		0x0111,
+
+		/* x_addr_start            REG=0x3004 */
+		8,
+
+		/* x_addr_end              REG=0x3008 */
+		2615,
+
+		/* y_addr_start            REG=0x3002 */
+		8,
+
+		/* y_addr_end              REG=0x3006 */
+		1967,
+
+		/* read_mode               REG=0x3040 */
+		0x0024,
+
+		/* x_output_size           REG=0x034C */
+		2608,
+
+		/* y_output_size           REG=0x034E */
+		1960,
+
+		/* line_length_pck         REG=0x300C */
+		3788,
+
+		/* frame_length_lines      REG=0x300A 10 fps snapshot */
+		2045,
+
+		/* coarse_integration_time REG=0x3012 */
+		16,
+
+		/* fine_integration_time   REG=0x3014 */
+		882
+	}
+};
+
+struct mt9p012_km_i2c_reg_conf const mt9p012_km_test_tbl[] = {
+	{0x3044, 0x0544 & 0xFBFF},
+	{0x30CA, 0x0004 | 0x0001},
+	{0x30D4, 0x9020 & 0x7FFF},
+	{0x31E0, 0x0003 & 0xFFFE},
+	{0x3180, 0x91FF & 0x7FFF},
+	{0x301A, (0x10CC | 0x8000) & 0xFFF7},
+	{0x301E, 0x0000},
+	{0x3780, 0x0000},
+};
+
+
+struct mt9p012_km_i2c_reg_conf const mt9p012_km_lc_tbl[] = {
+	{0x360A, 0x00F0},
+	{0x360C, 0x0B29},
+	{0x360E, 0x5ED1},
+	{0x3610, 0x890D},
+	{0x3612, 0x9871},
+	{0x364A, 0xAD2C},
+	{0x364C, 0x0A8C},
+	{0x364E, 0x91EC},
+	{0x3650, 0x94EC},
+	{0x3652, 0xC76B},
+	{0x368A, 0x5931},
+	{0x368C, 0x4FED},
+	{0x368E, 0x8A50},
+	{0x3690, 0x5C0F},
+	{0x3692, 0x8393},
+	{0x36CA, 0xDB8E},
+	{0x36CC, 0xCA4D},
+	{0x36CE, 0x146F},
+	{0x36D0, 0x618F},
+	{0x36D2, 0x014F},
+	{0x370A, 0x1FEE},
+	{0x370C, 0xDD50},
+	{0x370E, 0xDB54},
+	{0x3710, 0xCA92},
+	{0x3712, 0x1896},
+	{0x3600, 0x00F0},
+	{0x3602, 0xA04C},
+	{0x3604, 0x5711},
+	{0x3606, 0x5E6D},
+	{0x3608, 0xA971},
+	{0x3640, 0xDCCC},
+	{0x3642, 0x0529},
+	{0x3644, 0x96ED},
+	{0x3646, 0xF447},
+	{0x3648, 0x4AEE},
+	{0x3680, 0x2171},
+	{0x3682, 0x634F},
+	{0x3684, 0xCC91},
+	{0x3686, 0xA9CE},
+	{0x3688, 0x8751},
+	{0x36C0, 0x8B6D},
+	{0x36C2, 0xE20E},
+	{0x36C4, 0x750F},
+	{0x36C6, 0x0090},
+	{0x36C8, 0x9E91},
+	{0x3700, 0xEAAF},
+	{0x3702, 0xB8AF},
+	{0x3704, 0xE293},
+	{0x3706, 0xAB33},
+	{0x3708, 0x4595},
+	{0x3614, 0x00D0},
+	{0x3616, 0x8AAB},
+	{0x3618, 0x18B1},
+	{0x361A, 0x54AD},
+	{0x361C, 0x9DB0},
+	{0x3654, 0x11EB},
+	{0x3656, 0x332C},
+	{0x3658, 0x316D},
+	{0x365A, 0xF0EB},
+	{0x365C, 0xB4ED},
+	{0x3694, 0x0F31},
+	{0x3696, 0x08D0},
+	{0x3698, 0xA52F},
+	{0x369A, 0xE64F},
+	{0x369C, 0xC9D2},
+	{0x36D4, 0x8C2D},
+	{0x36D6, 0xAD6E},
+	{0x36D8, 0xE1CE},
+	{0x36DA, 0x1750},
+	{0x36DC, 0x8CAD},
+	{0x3714, 0x8CAF},
+	{0x3716, 0x8C11},
+	{0x3718, 0xE453},
+	{0x371A, 0x9693},
+	{0x371C, 0x38B5},
+	{0x361E, 0x00D0},
+	{0x3620, 0xB6CB},
+	{0x3622, 0x4811},
+	{0x3624, 0xB70C},
+	{0x3626, 0xA771},
+	{0x365E, 0xB5A9},
+	{0x3660, 0x05AA},
+	{0x3662, 0x00CF},
+	{0x3664, 0xB86B},
+	{0x3666, 0xA4AF},
+	{0x369E, 0x3E31},
+	{0x36A0, 0x902B},
+	{0x36A2, 0xD251},
+	{0x36A4, 0x5C2F},
+	{0x36A6, 0x8471},
+	{0x36DE, 0x2C6D},
+	{0x36E0, 0xECEE},
+	{0x36E2, 0xB650},
+	{0x36E4, 0x0210},
+	{0x36E6, 0xACAE},
+	{0x371E, 0xAC30},
+	{0x3720, 0x394E},
+	{0x3722, 0xFDD3},
+	{0x3724, 0xBCB2},
+	{0x3726, 0x5AD5},
+	{0x3782, 0x0508},
+	{0x3784, 0x03B4},
+	{0x3780, 0x8000},
+};
+
+struct mt9p012_km_i2c_reg_conf const mt9p012_km_rolloff_tbl[] = {
+	{0x360A, 0x00F0},
+	{0x360C, 0x0B29},
+	{0x360E, 0x5ED1},
+	{0x3610, 0x890D},
+	{0x3612, 0x9871},
+	{0x364A, 0xAD2C},
+	{0x364C, 0x0A8C},
+	{0x364E, 0x91EC},
+	{0x3650, 0x94EC},
+	{0x3652, 0xC76B},
+	{0x368A, 0x5931},
+	{0x368C, 0x4FED},
+	{0x368E, 0x8A50},
+	{0x3690, 0x5C0F},
+	{0x3692, 0x8393},
+	{0x36CA, 0xDB8E},
+	{0x36CC, 0xCA4D},
+	{0x36CE, 0x146F},
+	{0x36D0, 0x618F},
+	{0x36D2, 0x014F},
+	{0x370A, 0x1FEE},
+	{0x370C, 0xDD50},
+	{0x370E, 0xDB54},
+	{0x3710, 0xCA92},
+	{0x3712, 0x1896},
+	{0x3600, 0x00F0},
+	{0x3602, 0xA04C},
+	{0x3604, 0x5711},
+	{0x3606, 0x5E6D},
+	{0x3608, 0xA971},
+	{0x3640, 0xDCCC},
+	{0x3642, 0x0529},
+	{0x3644, 0x96ED},
+	{0x3646, 0xF447},
+	{0x3648, 0x4AEE},
+	{0x3680, 0x2171},
+	{0x3682, 0x634F},
+	{0x3684, 0xCC91},
+	{0x3686, 0xA9CE},
+	{0x3688, 0x8751},
+	{0x36C0, 0x8B6D},
+	{0x36C2, 0xE20E},
+	{0x36C4, 0x750F},
+	{0x36C6, 0x0090},
+	{0x36C8, 0x9E91},
+	{0x3700, 0xEAAF},
+	{0x3702, 0xB8AF},
+	{0x3704, 0xE293},
+	{0x3706, 0xAB33},
+	{0x3708, 0x4595},
+	{0x3614, 0x00D0},
+	{0x3616, 0x8AAB},
+	{0x3618, 0x18B1},
+	{0x361A, 0x54AD},
+	{0x361C, 0x9DB0},
+	{0x3654, 0x11EB},
+	{0x3656, 0x332C},
+	{0x3658, 0x316D},
+	{0x365A, 0xF0EB},
+	{0x365C, 0xB4ED},
+	{0x3694, 0x0F31},
+	{0x3696, 0x08D0},
+	{0x3698, 0xA52F},
+	{0x369A, 0xE64F},
+	{0x369C, 0xC9D2},
+	{0x36D4, 0x8C2D},
+	{0x36D6, 0xAD6E},
+	{0x36D8, 0xE1CE},
+	{0x36DA, 0x1750},
+	{0x36DC, 0x8CAD},
+	{0x3714, 0x8CAF},
+	{0x3716, 0x8C11},
+	{0x3718, 0xE453},
+	{0x371A, 0x9693},
+	{0x371C, 0x38B5},
+	{0x361E, 0x00D0},
+	{0x3620, 0xB6CB},
+	{0x3622, 0x4811},
+	{0x3624, 0xB70C},
+	{0x3626, 0xA771},
+	{0x365E, 0xB5A9},
+	{0x3660, 0x05AA},
+	{0x3662, 0x00CF},
+	{0x3664, 0xB86B},
+	{0x3666, 0xA4AF},
+	{0x369E, 0x3E31},
+	{0x36A0, 0x902B},
+	{0x36A2, 0xD251},
+	{0x36A4, 0x5C2F},
+	{0x36A6, 0x8471},
+	{0x36DE, 0x2C6D},
+	{0x36E0, 0xECEE},
+	{0x36E2, 0xB650},
+	{0x36E4, 0x0210},
+	{0x36E6, 0xACAE},
+	{0x371E, 0xAC30},
+	{0x3720, 0x394E},
+	{0x3722, 0xFDD3},
+	{0x3724, 0xBCB2},
+	{0x3726, 0x5AD5},
+	{0x3782, 0x0508},
+	{0x3784, 0x03B4},
+	{0x3780, 0x8000},
+};
+
+
+struct mt9p012_km_reg mt9p012_km_regs = {
+	.reg_pat = &mt9p012_km_reg_pat[0],
+	.reg_pat_size = ARRAY_SIZE(mt9p012_km_reg_pat),
+	.ttbl = &mt9p012_km_test_tbl[0],
+	.ttbl_size = ARRAY_SIZE(mt9p012_km_test_tbl),
+	.lctbl = &mt9p012_km_lc_tbl[0],
+	.lctbl_size = ARRAY_SIZE(mt9p012_km_lc_tbl),
+	.rftbl = &mt9p012_km_rolloff_tbl[0],
+	.rftbl_size = ARRAY_SIZE(mt9p012_km_rolloff_tbl)
+};
+
+
diff --git a/drivers/media/video/msm/mt9p012_reg.c b/drivers/media/video/msm/mt9p012_reg.c
new file mode 100644
index 0000000..06fe419
--- /dev/null
+++ b/drivers/media/video/msm/mt9p012_reg.c
@@ -0,0 +1,263 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "mt9p012.h"
+#include <linux/kernel.h>
+
+/*Micron settings from Applications for lower power consumption.*/
+struct reg_struct const mt9p012_reg_pat[2] = {
+	{ /* Preview */
+		/* vt_pix_clk_div          REG=0x0300 */
+		6,  /* 5 */
+
+		/* vt_sys_clk_div          REG=0x0302 */
+		1,
+		/* pre_pll_clk_div         REG=0x0304 */
+		2,
+		/* pll_multiplier          REG=0x0306 */
+		60,
+
+		/* op_pix_clk_div          REG=0x0308 */
+		8,  /* 10 */
+
+		/* op_sys_clk_div          REG=0x030A */
+		1,
+
+		/* scale_m                 REG=0x0404 */
+		16,
+
+		/* row_speed               REG=0x3016 */
+		0x0111,
+
+		/* x_addr_start            REG=0x3004 */
+		8,
+
+		/* x_addr_end              REG=0x3008 */
+		2597,
+
+		/* y_addr_start            REG=0x3002 */
+		8,
+
+		/* y_addr_end              REG=0x3006 */
+		1949,
+
+		/* read_mode               REG=0x3040
+		 * Preview 2x2 skipping */
+		0x00C3,
+
+		/* x_output_size           REG=0x034C */
+		1296,
+
+		/* y_output_size           REG=0x034E */
+		972,
+
+		/* line_length_pck         REG=0x300C */
+		3659,
+
+		/* frame_length_lines      REG=0x300A */
+		1074,
+
+		/* coarse_integration_time REG=0x3012 */
+		16,
+
+		/* fine_integration_time   REG=0x3014 */
+		1764
+	},
+	{ /* Snapshot */
+		/* vt_pix_clk_div          REG=0x0300 */
+		6,
+
+		/* vt_sys_clk_div          REG=0x0302 */
+		1,
+
+		/* pre_pll_clk_div         REG=0x0304 */
+		2,
+
+		/* pll_multiplier          REG=0x0306
+		 * 60 for 10fps snapshot */
+		60,
+
+		/* op_pix_clk_div          REG=0x0308 */
+		8,
+
+		/* op_sys_clk_div          REG=0x030A */
+		1,
+
+		/* scale_m                 REG=0x0404 */
+		16,
+
+		/* row_speed               REG=0x3016 */
+		0x0111,
+
+		/* x_addr_start            REG=0x3004 */
+		8,
+
+		/* x_addr_end              REG=0x3008 */
+		2615,
+
+		/* y_addr_start            REG=0x3002 */
+		8,
+
+		/* y_addr_end              REG=0x3006 */
+		1967,
+
+		/* read_mode               REG=0x3040 */
+		0x0041,
+
+		/* x_output_size           REG=0x034C */
+		2608,
+
+		/* y_output_size           REG=0x034E */
+		1960,
+
+		/* line_length_pck         REG=0x300C */
+		3911,
+
+		/* frame_length_lines      REG=0x300A 10 fps snapshot */
+		2045,
+
+		/* coarse_integration_time REG=0x3012 */
+		16,
+
+		/* fine_integration_time   REG=0x3014 */
+		882
+	}
+};
+
+
+struct mt9p012_i2c_reg_conf const mt9p012_test_tbl[] = {
+	{0x3044, 0x0544 & 0xFBFF},
+	{0x30CA, 0x0004 | 0x0001},
+	{0x30D4, 0x9020 & 0x7FFF},
+	{0x31E0, 0x0003 & 0xFFFE},
+	{0x3180, 0x91FF & 0x7FFF},
+	{0x301A, (0x10CC | 0x8000) & 0xFFF7},
+	{0x301E, 0x0000},
+	{0x3780, 0x0000},
+};
+struct mt9p012_i2c_reg_conf const mt9p012_rolloff_tbl[] = {
+	{0x360A, 0x0110},
+	{0x360C, 0x270D},
+	{0x360E, 0x0071},
+	{0x3610, 0xA38D},
+	{0x3612, 0xA610},
+	{0x364A, 0x8F49},
+	{0x364C, 0x696A},
+	{0x364E, 0x0FCD},
+	{0x3650, 0x20ED},
+	{0x3652, 0x81ED},
+	{0x368A, 0x1031},
+	{0x368C, 0xBCAD},
+	{0x368E, 0x77AA},
+	{0x3690, 0xD10E},
+	{0x3692, 0xC133},
+	{0x36CA, 0x4F8D},
+	{0x36CC, 0xAC4D},
+	{0x36CE, 0xC8CE},
+	{0x36D0, 0x73AD},
+	{0x36D2, 0xC150},
+	{0x370A, 0xB590},
+	{0x370C, 0x9010},
+	{0x370E, 0xAC52},
+	{0x3710, 0x4D51},
+	{0x3712, 0x5670},
+	{0x3600, 0x00F0},
+	{0x3602, 0xCE4B},
+	{0x3604, 0x4270},
+	{0x3606, 0x8BC9},
+	{0x3608, 0xFA2F},
+	{0x3640, 0x9A09},
+	{0x3642, 0xB40C},
+	{0x3644, 0x4ECD},
+	{0x3646, 0x1BCC},
+	{0x3648, 0xD68E},
+	{0x3680, 0x1BF0},
+	{0x3682, 0xC94D},
+	{0x3684, 0x714F},
+	{0x3686, 0x1491},
+	{0x3688, 0xB8D3},
+	{0x36C0, 0x3E49},
+	{0x36C2, 0x7A6C},
+	{0x36C4, 0xEF2E},
+	{0x36C6, 0xE0EE},
+	{0x36C8, 0x570F},
+	{0x3700, 0xD6AF},
+	{0x3702, 0x2251},
+	{0x3704, 0x8A33},
+	{0x3706, 0xEFB3},
+	{0x3708, 0x1174},
+	{0x3614, 0x0150},
+	{0x3616, 0xA9AB},
+	{0x3618, 0x1770},
+	{0x361A, 0x8809},
+	{0x361C, 0xE3AE},
+	{0x3654, 0x5ACC},
+	{0x3656, 0x35EA},
+	{0x3658, 0x2DEC},
+	{0x365A, 0xB90B},
+	{0x365C, 0x250C},
+	{0x3694, 0x1630},
+	{0x3696, 0xD88C},
+	{0x3698, 0xBD0E},
+	{0x369A, 0x16D1},
+	{0x369C, 0xE492},
+	{0x36D4, 0x5D6D},
+	{0x36D6, 0x906E},
+	{0x36D8, 0x10AE},
+	{0x36DA, 0x7A8E},
+	{0x36DC, 0x9672},
+	{0x3714, 0x8D90},
+	{0x3716, 0x04F1},
+	{0x3718, 0x23F1},
+	{0x371A, 0xF313},
+	{0x371C, 0xE833},
+	{0x361E, 0x0490},
+	{0x3620, 0x14CD},
+	{0x3622, 0x38F0},
+	{0x3624, 0xBAED},
+	{0x3626, 0xFF6F},
+	{0x365E, 0x358C},
+	{0x3660, 0xA9E9},
+	{0x3662, 0x4A4E},
+	{0x3664, 0x398D},
+	{0x3666, 0x890F},
+	{0x369E, 0x2DF0},
+	{0x36A0, 0xF7CE},
+	{0x36A2, 0xB3CC},
+	{0x36A4, 0x118D},
+	{0x36A6, 0x9CB3},
+	{0x36DE, 0x462D},
+	{0x36E0, 0x74AA},
+	{0x36E2, 0xC8CF},
+	{0x36E4, 0x8DEF},
+	{0x36E6, 0xF130},
+	{0x371E, 0x9250},
+	{0x3720, 0x19CC},
+	{0x3722, 0xDFD1},
+	{0x3724, 0x5B70},
+	{0x3726, 0x34D2},
+	{0x3782, 0x0530},
+	{0x3784, 0x03C8},
+	{0x3780, 0x8000},
+};
+
+struct mt9p012_reg mt9p012_regs = {
+	.reg_pat = &mt9p012_reg_pat[0],
+	.reg_pat_size = ARRAY_SIZE(mt9p012_reg_pat),
+	.ttbl = &mt9p012_test_tbl[0],
+	.ttbl_size = ARRAY_SIZE(mt9p012_test_tbl),
+	.rftbl = &mt9p012_rolloff_tbl[0],
+	.rftbl_size = ARRAY_SIZE(mt9p012_rolloff_tbl)
+};
+
+
diff --git a/drivers/media/video/msm/mt9t013.c b/drivers/media/video/msm/mt9t013.c
new file mode 100644
index 0000000..e1f6167
--- /dev/null
+++ b/drivers/media/video/msm/mt9t013.c
@@ -0,0 +1,1503 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/kernel.h>
+#include <media/msm_camera.h>
+#include <mach/gpio.h>
+#include <mach/camera.h>
+#include <asm/mach-types.h>
+#include "mt9t013.h"
+
+/*=============================================================
+	SENSOR REGISTER DEFINES
+==============================================================*/
+#define MT9T013_REG_MODEL_ID 		 0x0000
+#define MT9T013_MODEL_ID     		 0x2600
+#define REG_GROUPED_PARAMETER_HOLD   0x0104
+#define GROUPED_PARAMETER_HOLD       0x0100
+#define GROUPED_PARAMETER_UPDATE     0x0000
+#define REG_COARSE_INT_TIME          0x3012
+#define REG_VT_PIX_CLK_DIV           0x0300
+#define REG_VT_SYS_CLK_DIV           0x0302
+#define REG_PRE_PLL_CLK_DIV          0x0304
+#define REG_PLL_MULTIPLIER           0x0306
+#define REG_OP_PIX_CLK_DIV           0x0308
+#define REG_OP_SYS_CLK_DIV           0x030A
+#define REG_SCALE_M                  0x0404
+#define REG_FRAME_LENGTH_LINES       0x300A
+#define REG_LINE_LENGTH_PCK          0x300C
+#define REG_X_ADDR_START             0x3004
+#define REG_Y_ADDR_START             0x3002
+#define REG_X_ADDR_END               0x3008
+#define REG_Y_ADDR_END               0x3006
+#define REG_X_OUTPUT_SIZE            0x034C
+#define REG_Y_OUTPUT_SIZE            0x034E
+#define REG_FINE_INT_TIME            0x3014
+#define REG_ROW_SPEED                0x3016
+#define MT9T013_REG_RESET_REGISTER   0x301A
+#define MT9T013_RESET_REGISTER_PWON  0x10CC
+#define MT9T013_RESET_REGISTER_PWOFF 0x1008 /* 0x10C8 stop streaming*/
+#define MT9T013_RESET_FAST_TRANSITION 0x0002
+#define REG_READ_MODE                0x3040
+#define REG_GLOBAL_GAIN              0x305E
+#define REG_TEST_PATTERN_MODE        0x3070
+
+
+enum mt9t013_test_mode {
+	TEST_OFF,
+	TEST_1,
+	TEST_2,
+	TEST_3
+};
+
+enum mt9t013_resolution {
+	QTR_SIZE,
+	FULL_SIZE,
+	INVALID_SIZE
+};
+
+enum mt9t013_reg_update {
+	REG_INIT, /* registers that need to be updated during initialization */
+	UPDATE_PERIODIC, /* registers that needs periodic I2C writes */
+	UPDATE_ALL, /* all registers will be updated */
+	UPDATE_INVALID
+};
+
+enum mt9t013_setting {
+	RES_PREVIEW,
+	RES_CAPTURE
+};
+
+/* actuator's Slave Address */
+#define MT9T013_AF_I2C_ADDR   0x18
+
+/*
+* AF Total steps parameters
+*/
+#define MT9T013_TOTAL_STEPS_NEAR_TO_FAR    30
+
+/*
+ * Time in milisecs for waiting for the sensor to reset.
+ */
+#define MT9T013_RESET_DELAY_MSECS   66
+
+/* for 30 fps preview */
+#define MT9T013_DEFAULT_CLOCK_RATE  24000000
+#define MT9T013_DEFAULT_MAX_FPS     26
+
+
+/* FIXME: Changes from here */
+struct mt9t013_work {
+	struct work_struct work;
+};
+
+static struct  mt9t013_work *mt9t013_sensorw;
+static struct  i2c_client *mt9t013_client;
+
+struct mt9t013_ctrl {
+	const struct msm_camera_sensor_info *sensordata;
+
+	int sensormode;
+	uint32_t fps_divider; 		/* init to 1 * 0x00000400 */
+	uint32_t pict_fps_divider; 	/* init to 1 * 0x00000400 */
+
+	uint16_t curr_lens_pos;
+	uint16_t init_curr_lens_pos;
+	uint16_t my_reg_gain;
+	uint32_t my_reg_line_count;
+
+	enum mt9t013_resolution prev_res;
+	enum mt9t013_resolution pict_res;
+	enum mt9t013_resolution curr_res;
+	enum mt9t013_test_mode  set_test;
+
+	unsigned short imgaddr;
+};
+
+
+static struct mt9t013_ctrl *mt9t013_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(mt9t013_wait_queue);
+DEFINE_SEMAPHORE(mt9t013_sem);
+
+static int mt9t013_i2c_rxdata(unsigned short saddr,
+	unsigned char *rxdata, int length)
+{
+	struct i2c_msg msgs[] = {
+	{
+		.addr   = saddr,
+		.flags = 0,
+		.len   = 2,
+		.buf   = rxdata,
+	},
+	{
+		.addr  = saddr,
+		.flags = I2C_M_RD,
+		.len   = length,
+		.buf   = rxdata,
+	},
+	};
+
+	if (i2c_transfer(mt9t013_client->adapter, msgs, 2) < 0) {
+		pr_err("mt9t013_i2c_rxdata failed!\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int32_t mt9t013_i2c_read_w(unsigned short saddr,
+	unsigned short raddr, unsigned short *rdata)
+{
+	int32_t rc = 0;
+	unsigned char buf[4];
+
+	if (!rdata)
+		return -EIO;
+
+	memset(buf, 0, sizeof(buf));
+
+	buf[0] = (raddr & 0xFF00)>>8;
+	buf[1] = (raddr & 0x00FF);
+
+	rc = mt9t013_i2c_rxdata(saddr, buf, 2);
+	if (rc < 0)
+		return rc;
+
+	*rdata = buf[0] << 8 | buf[1];
+
+	if (rc < 0)
+		pr_err("mt9t013_i2c_read failed!\n");
+
+	return rc;
+}
+
+static int32_t mt9t013_i2c_txdata(unsigned short saddr,
+	unsigned char *txdata, int length)
+{
+	struct i2c_msg msg[] = {
+	{
+		.addr = saddr,
+		.flags = 0,
+		.len = length,
+		.buf = txdata,
+	},
+	};
+
+	if (i2c_transfer(mt9t013_client->adapter, msg, 1) < 0) {
+		pr_err("mt9t013_i2c_txdata failed\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int32_t mt9t013_i2c_write_b(unsigned short saddr,
+	unsigned short waddr, unsigned short wdata)
+{
+	int32_t rc = -EIO;
+	unsigned char buf[2];
+
+	memset(buf, 0, sizeof(buf));
+	buf[0] = waddr;
+	buf[1] = wdata;
+	rc = mt9t013_i2c_txdata(saddr, buf, 2);
+
+	if (rc < 0)
+		pr_err("i2c_write failed, addr = 0x%x, val = 0x%x!\n",
+		waddr, wdata);
+
+	return rc;
+}
+
+static int32_t mt9t013_i2c_write_w(unsigned short saddr,
+	unsigned short waddr, unsigned short wdata)
+{
+	int32_t rc = -EIO;
+	unsigned char buf[4];
+
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (waddr & 0xFF00)>>8;
+	buf[1] = (waddr & 0x00FF);
+	buf[2] = (wdata & 0xFF00)>>8;
+	buf[3] = (wdata & 0x00FF);
+
+	rc = mt9t013_i2c_txdata(saddr, buf, 4);
+
+	if (rc < 0)
+		pr_err("i2c_write_w failed, addr = 0x%x, val = 0x%x!\n",
+		waddr, wdata);
+
+	return rc;
+}
+
+static int32_t mt9t013_i2c_write_w_table(
+	struct mt9t013_i2c_reg_conf const *reg_conf_tbl,
+	int num_of_items_in_table)
+{
+	int i;
+	int32_t rc = -EIO;
+
+	for (i = 0; i < num_of_items_in_table; i++) {
+		rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+			reg_conf_tbl->waddr, reg_conf_tbl->wdata);
+		if (rc < 0)
+			break;
+		reg_conf_tbl++;
+	}
+
+	return rc;
+}
+
+static int32_t mt9t013_test(enum mt9t013_test_mode mo)
+{
+	int32_t rc = 0;
+
+	rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+			REG_GROUPED_PARAMETER_HOLD,
+			GROUPED_PARAMETER_HOLD);
+	if (rc < 0)
+		return rc;
+
+	if (mo == TEST_OFF)
+		return 0;
+	else {
+		rc = mt9t013_i2c_write_w_table(mt9t013_regs.ttbl,
+				mt9t013_regs.ttbl_size);
+		if (rc < 0)
+			return rc;
+		rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_TEST_PATTERN_MODE, (uint16_t)mo);
+		if (rc < 0)
+			return rc;
+	}
+
+	rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+			REG_GROUPED_PARAMETER_HOLD,
+			GROUPED_PARAMETER_UPDATE);
+	if (rc < 0)
+		return rc;
+
+	return rc;
+}
+
+static int32_t mt9t013_set_lc(void)
+{
+	int32_t rc;
+
+	rc = mt9t013_i2c_write_w_table(mt9t013_regs.lctbl,
+		mt9t013_regs.lctbl_size);
+	if (rc < 0)
+		return rc;
+
+	return rc;
+}
+
+static int32_t mt9t013_set_default_focus(uint8_t af_step)
+{
+	int32_t rc = 0;
+	uint8_t code_val_msb, code_val_lsb;
+	code_val_msb = 0x01;
+	code_val_lsb = af_step;
+
+	/* Write the digital code for current to the actuator */
+	rc = mt9t013_i2c_write_b(MT9T013_AF_I2C_ADDR>>1,
+			code_val_msb, code_val_lsb);
+
+	mt9t013_ctrl->curr_lens_pos = 0;
+	mt9t013_ctrl->init_curr_lens_pos = 0;
+	return rc;
+}
+
+static void mt9t013_get_pict_fps(uint16_t fps, uint16_t *pfps)
+{
+	/* input fps is preview fps in Q8 format */
+	uint32_t divider;   /*Q10 */
+	uint32_t pclk_mult; /*Q10 */
+	uint32_t d1;
+	uint32_t d2;
+
+	d1 =
+		(uint32_t)(
+		(mt9t013_regs.reg_pat[RES_PREVIEW].frame_length_lines *
+		0x00000400) /
+		mt9t013_regs.reg_pat[RES_CAPTURE].frame_length_lines);
+
+	d2 =
+		(uint32_t)(
+		(mt9t013_regs.reg_pat[RES_PREVIEW].line_length_pck *
+		0x00000400) /
+		mt9t013_regs.reg_pat[RES_CAPTURE].line_length_pck);
+
+	divider = (uint32_t) (d1 * d2) / 0x00000400;
+
+	pclk_mult =
+		(uint32_t) ((mt9t013_regs.reg_pat[RES_CAPTURE].pll_multiplier *
+		0x00000400) /
+		(mt9t013_regs.reg_pat[RES_PREVIEW].pll_multiplier));
+
+
+	/* Verify PCLK settings and frame sizes. */
+	*pfps =
+		(uint16_t) (fps * divider * pclk_mult /
+		0x00000400 / 0x00000400);
+}
+
+static uint16_t mt9t013_get_prev_lines_pf(void)
+{
+	if (mt9t013_ctrl->prev_res == QTR_SIZE)
+		return mt9t013_regs.reg_pat[RES_PREVIEW].frame_length_lines;
+	else
+		return mt9t013_regs.reg_pat[RES_CAPTURE].frame_length_lines;
+}
+
+static uint16_t mt9t013_get_prev_pixels_pl(void)
+{
+	if (mt9t013_ctrl->prev_res == QTR_SIZE)
+		return mt9t013_regs.reg_pat[RES_PREVIEW].line_length_pck;
+	else
+		return mt9t013_regs.reg_pat[RES_CAPTURE].line_length_pck;
+}
+
+static uint16_t mt9t013_get_pict_lines_pf(void)
+{
+	return mt9t013_regs.reg_pat[RES_CAPTURE].frame_length_lines;
+}
+
+static uint16_t mt9t013_get_pict_pixels_pl(void)
+{
+	return mt9t013_regs.reg_pat[RES_CAPTURE].line_length_pck;
+}
+
+static uint32_t mt9t013_get_pict_max_exp_lc(void)
+{
+	uint16_t snapshot_lines_per_frame;
+
+	if (mt9t013_ctrl->pict_res == QTR_SIZE) {
+		snapshot_lines_per_frame =
+		mt9t013_regs.reg_pat[RES_PREVIEW].frame_length_lines - 1;
+	} else  {
+		snapshot_lines_per_frame =
+		mt9t013_regs.reg_pat[RES_CAPTURE].frame_length_lines - 1;
+	}
+
+	return snapshot_lines_per_frame * 24;
+}
+
+static int32_t mt9t013_set_fps(struct fps_cfg *fps)
+{
+	/* input is new fps in Q8 format */
+	int32_t rc = 0;
+	enum mt9t013_setting setting;
+
+	mt9t013_ctrl->fps_divider = fps->fps_div;
+	mt9t013_ctrl->pict_fps_divider = fps->pict_fps_div;
+
+	rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+			REG_GROUPED_PARAMETER_HOLD,
+			GROUPED_PARAMETER_HOLD);
+	if (rc < 0)
+		return -EBUSY;
+
+	CDBG("mt9t013_set_fps: fps_div is %d, f_mult is %d\n",
+			fps->fps_div, fps->f_mult);
+
+	if (mt9t013_ctrl->sensormode == SENSOR_PREVIEW_MODE)
+		setting = RES_PREVIEW;
+	else
+		setting = RES_CAPTURE;
+	rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+			REG_FRAME_LENGTH_LINES,
+			(uint16_t) (
+			mt9t013_regs.reg_pat[setting].frame_length_lines *
+			fps->fps_div / 0x00000400));
+
+	if (rc < 0)
+		return rc;
+
+	rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+			REG_GROUPED_PARAMETER_HOLD,
+			GROUPED_PARAMETER_UPDATE);
+	if (rc < 0)
+		return rc;
+
+	return rc;
+}
+
+static int32_t mt9t013_write_exp_gain(uint16_t gain, uint32_t line)
+{
+	uint16_t max_legal_gain = 0x01FF;
+	int32_t rc = 0;
+
+	if (mt9t013_ctrl->sensormode == SENSOR_PREVIEW_MODE) {
+		mt9t013_ctrl->my_reg_gain = gain;
+		mt9t013_ctrl->my_reg_line_count = (uint16_t) line;
+	}
+
+	if (gain > max_legal_gain)
+		gain = max_legal_gain;
+
+	if (mt9t013_ctrl->sensormode != SENSOR_SNAPSHOT_MODE)
+		line = (uint32_t) (line * mt9t013_ctrl->fps_divider /
+				   0x00000400);
+	else
+		line = (uint32_t) (line * mt9t013_ctrl->pict_fps_divider /
+				   0x00000400);
+
+	/*Set digital gain to 1 */
+	gain |= 0x0200;
+
+	/* There used to be PARAMETER_HOLD register write before and
+	 * after REG_GLOBAL_GAIN & REG_COARSE_INIT_TIME. This causes
+	 * aec oscillation. Hence removed. */
+
+	rc = mt9t013_i2c_write_w(mt9t013_client->addr, REG_GLOBAL_GAIN, gain);
+	if (rc < 0)
+		return rc;
+
+	rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+			REG_COARSE_INT_TIME, line);
+	if (rc < 0)
+		return rc;
+
+	return rc;
+}
+
+static int32_t mt9t013_set_pict_exp_gain(uint16_t gain, uint32_t line)
+{
+	int32_t rc = 0;
+
+	rc = mt9t013_write_exp_gain(gain, line);
+	if (rc < 0)
+		return rc;
+
+	rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+			MT9T013_REG_RESET_REGISTER,
+			0x10CC | 0x0002);
+
+	mdelay(5);
+
+	return rc;
+}
+
+static int32_t mt9t013_setting(enum mt9t013_reg_update rupdate,
+	enum mt9t013_setting rt)
+{
+	int32_t rc = 0;
+
+	switch (rupdate) {
+	case UPDATE_PERIODIC: {
+
+	if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+#if 0
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				MT9T013_REG_RESET_REGISTER,
+				MT9T013_RESET_REGISTER_PWOFF);
+		if (rc < 0)
+			return rc;
+#endif
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_VT_PIX_CLK_DIV,
+				mt9t013_regs.reg_pat[rt].vt_pix_clk_div);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_VT_SYS_CLK_DIV,
+				mt9t013_regs.reg_pat[rt].vt_sys_clk_div);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_PRE_PLL_CLK_DIV,
+				mt9t013_regs.reg_pat[rt].pre_pll_clk_div);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_PLL_MULTIPLIER,
+				mt9t013_regs.reg_pat[rt].pll_multiplier);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_OP_PIX_CLK_DIV,
+				mt9t013_regs.reg_pat[rt].op_pix_clk_div);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_OP_SYS_CLK_DIV,
+				mt9t013_regs.reg_pat[rt].op_sys_clk_div);
+		if (rc < 0)
+			return rc;
+
+		mdelay(5);
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_GROUPED_PARAMETER_HOLD,
+				GROUPED_PARAMETER_HOLD);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_ROW_SPEED,
+				mt9t013_regs.reg_pat[rt].row_speed);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_X_ADDR_START,
+				mt9t013_regs.reg_pat[rt].x_addr_start);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_X_ADDR_END,
+				mt9t013_regs.reg_pat[rt].x_addr_end);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_Y_ADDR_START,
+				mt9t013_regs.reg_pat[rt].y_addr_start);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_Y_ADDR_END,
+				mt9t013_regs.reg_pat[rt].y_addr_end);
+		if (rc < 0)
+			return rc;
+
+		if (machine_is_sapphire()) {
+			if (rt == 0)
+				rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+					REG_READ_MODE,
+					0x046F);
+			else
+				rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+					REG_READ_MODE,
+					0x0027);
+		} else
+			rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+					REG_READ_MODE,
+					mt9t013_regs.reg_pat[rt].read_mode);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_SCALE_M,
+				mt9t013_regs.reg_pat[rt].scale_m);
+		if (rc < 0)
+			return rc;
+
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_X_OUTPUT_SIZE,
+				mt9t013_regs.reg_pat[rt].x_output_size);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_Y_OUTPUT_SIZE,
+				mt9t013_regs.reg_pat[rt].y_output_size);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_LINE_LENGTH_PCK,
+				mt9t013_regs.reg_pat[rt].line_length_pck);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+			REG_FRAME_LENGTH_LINES,
+			(mt9t013_regs.reg_pat[rt].frame_length_lines *
+			mt9t013_ctrl->fps_divider / 0x00000400));
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+			REG_COARSE_INT_TIME,
+			mt9t013_regs.reg_pat[rt].coarse_int_time);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+			REG_FINE_INT_TIME,
+			mt9t013_regs.reg_pat[rt].fine_int_time);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+			REG_GROUPED_PARAMETER_HOLD,
+			GROUPED_PARAMETER_UPDATE);
+		if (rc < 0)
+			return rc;
+
+		rc = mt9t013_test(mt9t013_ctrl->set_test);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+			MT9T013_REG_RESET_REGISTER,
+			MT9T013_RESET_REGISTER_PWON|
+			MT9T013_RESET_FAST_TRANSITION);
+		if (rc < 0)
+			return rc;
+
+		mdelay(5);
+
+		return rc;
+	}
+	}
+		break;
+
+	/*CAMSENSOR_REG_UPDATE_PERIODIC */
+	case REG_INIT: {
+	if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				MT9T013_REG_RESET_REGISTER,
+				MT9T013_RESET_REGISTER_PWOFF);
+		if (rc < 0)
+			/* MODE_SELECT, stop streaming */
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_VT_PIX_CLK_DIV,
+				mt9t013_regs.reg_pat[rt].vt_pix_clk_div);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_VT_SYS_CLK_DIV,
+				mt9t013_regs.reg_pat[rt].vt_sys_clk_div);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_PRE_PLL_CLK_DIV,
+				mt9t013_regs.reg_pat[rt].pre_pll_clk_div);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_PLL_MULTIPLIER,
+				mt9t013_regs.reg_pat[rt].pll_multiplier);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_OP_PIX_CLK_DIV,
+				mt9t013_regs.reg_pat[rt].op_pix_clk_div);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_OP_SYS_CLK_DIV,
+				mt9t013_regs.reg_pat[rt].op_sys_clk_div);
+		if (rc < 0)
+			return rc;
+
+		mdelay(5);
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_GROUPED_PARAMETER_HOLD,
+				GROUPED_PARAMETER_HOLD);
+		if (rc < 0)
+			return rc;
+
+		/* additional power saving mode ok around 38.2MHz */
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				0x3084, 0x2409);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				0x3092, 0x0A49);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				0x3094, 0x4949);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				0x3096, 0x4949);
+		if (rc < 0)
+			return rc;
+
+		/* Set preview or snapshot mode */
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_ROW_SPEED,
+				mt9t013_regs.reg_pat[rt].row_speed);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_X_ADDR_START,
+				mt9t013_regs.reg_pat[rt].x_addr_start);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_X_ADDR_END,
+				mt9t013_regs.reg_pat[rt].x_addr_end);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_Y_ADDR_START,
+				mt9t013_regs.reg_pat[rt].y_addr_start);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_Y_ADDR_END,
+				mt9t013_regs.reg_pat[rt].y_addr_end);
+		if (rc < 0)
+			return rc;
+
+		if (machine_is_sapphire()) {
+			if (rt == 0)
+				rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+					REG_READ_MODE,
+					0x046F);
+			else
+				rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+					REG_READ_MODE,
+					0x0027);
+		} else
+			rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+					REG_READ_MODE,
+					mt9t013_regs.reg_pat[rt].read_mode);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_SCALE_M,
+				mt9t013_regs.reg_pat[rt].scale_m);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_X_OUTPUT_SIZE,
+				mt9t013_regs.reg_pat[rt].x_output_size);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_Y_OUTPUT_SIZE,
+				mt9t013_regs.reg_pat[rt].y_output_size);
+		if (rc < 0)
+			return 0;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_LINE_LENGTH_PCK,
+				mt9t013_regs.reg_pat[rt].line_length_pck);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_FRAME_LENGTH_LINES,
+				mt9t013_regs.reg_pat[rt].frame_length_lines);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_COARSE_INT_TIME,
+				mt9t013_regs.reg_pat[rt].coarse_int_time);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_FINE_INT_TIME,
+				mt9t013_regs.reg_pat[rt].fine_int_time);
+		if (rc < 0)
+			return rc;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_GROUPED_PARAMETER_HOLD,
+				GROUPED_PARAMETER_UPDATE);
+			if (rc < 0)
+				return rc;
+
+		/* load lens shading */
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_GROUPED_PARAMETER_HOLD,
+				GROUPED_PARAMETER_HOLD);
+		if (rc < 0)
+			return rc;
+
+		/* most likely needs to be written only once. */
+		rc = mt9t013_set_lc();
+		if (rc < 0)
+			return -EBUSY;
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_GROUPED_PARAMETER_HOLD,
+				GROUPED_PARAMETER_UPDATE);
+		if (rc < 0)
+			return rc;
+
+		rc = mt9t013_test(mt9t013_ctrl->set_test);
+		if (rc < 0)
+			return rc;
+
+		mdelay(5);
+
+		rc =
+			mt9t013_i2c_write_w(mt9t013_client->addr,
+				MT9T013_REG_RESET_REGISTER,
+				MT9T013_RESET_REGISTER_PWON);
+		if (rc < 0)
+			/* MODE_SELECT, stop streaming */
+			return rc;
+
+		CDBG("!!! mt9t013 !!! PowerOn is done!\n");
+		mdelay(5);
+		return rc;
+		}
+	} /* case CAMSENSOR_REG_INIT: */
+	break;
+
+	/*CAMSENSOR_REG_INIT */
+	default:
+		rc = -EINVAL;
+		break;
+	} /* switch (rupdate) */
+
+	return rc;
+}
+
+static int32_t mt9t013_video_config(int mode, int res)
+{
+	int32_t rc;
+
+	switch (res) {
+	case QTR_SIZE:
+		rc = mt9t013_setting(UPDATE_PERIODIC, RES_PREVIEW);
+		if (rc < 0)
+			return rc;
+		CDBG("sensor configuration done!\n");
+		break;
+
+	case FULL_SIZE:
+		rc = mt9t013_setting(UPDATE_PERIODIC, RES_CAPTURE);
+		if (rc < 0)
+			return rc;
+		break;
+
+	default:
+		return -EINVAL;
+	} /* switch */
+
+	mt9t013_ctrl->prev_res = res;
+	mt9t013_ctrl->curr_res = res;
+	mt9t013_ctrl->sensormode = mode;
+
+	rc = mt9t013_write_exp_gain(mt9t013_ctrl->my_reg_gain,
+			mt9t013_ctrl->my_reg_line_count);
+	if (rc < 0)
+		return rc;
+
+	rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+		MT9T013_REG_RESET_REGISTER,
+		MT9T013_RESET_REGISTER_PWON|MT9T013_RESET_FAST_TRANSITION);
+	if (rc < 0)
+		return rc;
+
+	msleep(5);
+	return rc;
+}
+
+static int32_t mt9t013_snapshot_config(int mode)
+{
+	int32_t rc = 0;
+
+	rc = mt9t013_setting(UPDATE_PERIODIC, RES_CAPTURE);
+	if (rc < 0)
+		return rc;
+
+	mt9t013_ctrl->curr_res = mt9t013_ctrl->pict_res;
+	mt9t013_ctrl->sensormode = mode;
+	return rc;
+}
+
+static int32_t mt9t013_raw_snapshot_config(int mode)
+{
+	int32_t rc = 0;
+
+	rc = mt9t013_setting(UPDATE_PERIODIC, RES_CAPTURE);
+	if (rc < 0)
+		return rc;
+
+	mt9t013_ctrl->curr_res = mt9t013_ctrl->pict_res;
+	mt9t013_ctrl->sensormode = mode;
+	return rc;
+}
+
+static int32_t mt9t013_power_down(void)
+{
+	int32_t rc = 0;
+
+	rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+			MT9T013_REG_RESET_REGISTER,
+			MT9T013_RESET_REGISTER_PWOFF);
+	if (rc >= 0)
+		mdelay(5);
+	return rc;
+}
+
+static int32_t mt9t013_move_focus(int direction, int32_t num_steps)
+{
+	int16_t step_direction;
+	int16_t actual_step;
+	int16_t next_position;
+	int16_t break_steps[4];
+	uint8_t code_val_msb, code_val_lsb;
+	int16_t i;
+
+	if (num_steps > MT9T013_TOTAL_STEPS_NEAR_TO_FAR)
+		num_steps = MT9T013_TOTAL_STEPS_NEAR_TO_FAR;
+	else if (num_steps == 0)
+		return -EINVAL;
+
+	if (direction == MOVE_NEAR)
+		step_direction = 4;
+	else if (direction == MOVE_FAR)
+		step_direction = -4;
+	else
+		return -EINVAL;
+
+	if (mt9t013_ctrl->curr_lens_pos < mt9t013_ctrl->init_curr_lens_pos)
+		mt9t013_ctrl->curr_lens_pos = mt9t013_ctrl->init_curr_lens_pos;
+
+	actual_step =
+		(int16_t) (step_direction *
+		(int16_t) num_steps);
+
+	for (i = 0; i < 4; i++)
+		break_steps[i] =
+			actual_step / 4 * (i + 1) - actual_step / 4 * i;
+
+	for (i = 0; i < 4; i++) {
+		next_position =
+		(int16_t)
+		(mt9t013_ctrl->curr_lens_pos + break_steps[i]);
+
+		if (next_position > 255)
+			next_position = 255;
+		else if (next_position < 0)
+			next_position = 0;
+
+		code_val_msb =
+		((next_position >> 4) << 2) |
+		((next_position << 4) >> 6);
+
+		code_val_lsb =
+		((next_position & 0x03) << 6);
+
+		/* Writing the digital code for current to the actuator */
+		if (mt9t013_i2c_write_b(MT9T013_AF_I2C_ADDR>>1,
+				code_val_msb, code_val_lsb) < 0)
+			return -EBUSY;
+
+		/* Storing the current lens Position */
+		mt9t013_ctrl->curr_lens_pos = next_position;
+
+		if (i < 3)
+			mdelay(1);
+	} /* for */
+
+	return 0;
+}
+
+static int mt9t013_sensor_init_done(const struct msm_camera_sensor_info *data)
+{
+	gpio_direction_output(data->sensor_reset, 0);
+	gpio_free(data->sensor_reset);
+	return 0;
+}
+
+static int mt9t013_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+	int rc;
+	uint16_t chipid;
+
+	rc = gpio_request(data->sensor_reset, "mt9t013");
+	if (!rc)
+		gpio_direction_output(data->sensor_reset, 1);
+	else
+		goto init_probe_done;
+
+	mdelay(20);
+
+	/* RESET the sensor image part via I2C command */
+	rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+		MT9T013_REG_RESET_REGISTER, 0x1009);
+	if (rc < 0)
+		goto init_probe_fail;
+
+	msleep(10);
+
+	/* 3. Read sensor Model ID: */
+	rc = mt9t013_i2c_read_w(mt9t013_client->addr,
+		MT9T013_REG_MODEL_ID, &chipid);
+
+	if (rc < 0)
+		goto init_probe_fail;
+
+	CDBG("mt9t013 model_id = 0x%x\n", chipid);
+
+	/* 4. Compare sensor ID to MT9T012VC ID: */
+	if (chipid != MT9T013_MODEL_ID) {
+		rc = -ENODEV;
+		goto init_probe_fail;
+	}
+
+	rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+		0x3064, 0x0805);
+	if (rc < 0)
+		goto init_probe_fail;
+
+	mdelay(MT9T013_RESET_DELAY_MSECS);
+
+	goto init_probe_done;
+
+	/* sensor: output enable */
+#if 0
+	rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+		MT9T013_REG_RESET_REGISTER,
+		MT9T013_RESET_REGISTER_PWON);
+
+	/* if this fails, the sensor is not the MT9T013 */
+	rc = mt9t013_set_default_focus(0);
+#endif
+
+init_probe_fail:
+	gpio_direction_output(data->sensor_reset, 0);
+	gpio_free(data->sensor_reset);
+init_probe_done:
+	return rc;
+}
+
+static int32_t mt9t013_poweron_af(void)
+{
+	int32_t rc = 0;
+
+	/* enable AF actuator */
+	CDBG("enable AF actuator, gpio = %d\n",
+			mt9t013_ctrl->sensordata->vcm_pwd);
+	rc = gpio_request(mt9t013_ctrl->sensordata->vcm_pwd, "mt9t013");
+	if (!rc) {
+		gpio_direction_output(mt9t013_ctrl->sensordata->vcm_pwd, 0);
+		mdelay(20);
+		rc = mt9t013_set_default_focus(0);
+	} else
+		pr_err("%s, gpio_request failed (%d)!\n", __func__, rc);
+	return rc;
+}
+
+static void mt9t013_poweroff_af(void)
+{
+	gpio_direction_output(mt9t013_ctrl->sensordata->vcm_pwd, 1);
+	gpio_free(mt9t013_ctrl->sensordata->vcm_pwd);
+}
+
+int mt9t013_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+	int32_t  rc;
+
+	mt9t013_ctrl = kzalloc(sizeof(struct mt9t013_ctrl), GFP_KERNEL);
+	if (!mt9t013_ctrl) {
+		pr_err("mt9t013_init failed!\n");
+		rc = -ENOMEM;
+		goto init_done;
+	}
+
+	mt9t013_ctrl->fps_divider = 1 * 0x00000400;
+	mt9t013_ctrl->pict_fps_divider = 1 * 0x00000400;
+	mt9t013_ctrl->set_test = TEST_OFF;
+	mt9t013_ctrl->prev_res = QTR_SIZE;
+	mt9t013_ctrl->pict_res = FULL_SIZE;
+
+	if (data)
+		mt9t013_ctrl->sensordata = data;
+
+	/* enable mclk first */
+	msm_camio_clk_rate_set(MT9T013_DEFAULT_CLOCK_RATE);
+	mdelay(20);
+
+	msm_camio_camif_pad_reg_reset();
+	mdelay(20);
+
+	rc = mt9t013_probe_init_sensor(data);
+	if (rc < 0)
+		goto init_fail;
+
+	if (mt9t013_ctrl->prev_res == QTR_SIZE)
+		rc = mt9t013_setting(REG_INIT, RES_PREVIEW);
+	else
+		rc = mt9t013_setting(REG_INIT, RES_CAPTURE);
+
+	if (rc >= 0)
+		if (machine_is_sapphire())
+			rc = mt9t013_poweron_af();
+
+	if (rc < 0)
+		goto init_fail;
+	else
+		goto init_done;
+
+init_fail:
+	kfree(mt9t013_ctrl);
+init_done:
+	return rc;
+}
+
+static int mt9t013_init_client(struct i2c_client *client)
+{
+	/* Initialize the MSM_CAMI2C Chip */
+	init_waitqueue_head(&mt9t013_wait_queue);
+	return 0;
+}
+
+
+static int32_t mt9t013_set_sensor_mode(int mode, int res)
+{
+	int32_t rc = 0;
+	rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+			REG_GROUPED_PARAMETER_HOLD,
+			GROUPED_PARAMETER_HOLD);
+	if (rc < 0)
+		return rc;
+
+	switch (mode) {
+	case SENSOR_PREVIEW_MODE:
+		rc = mt9t013_video_config(mode, res);
+		break;
+
+	case SENSOR_SNAPSHOT_MODE:
+		rc = mt9t013_snapshot_config(mode);
+		break;
+
+	case SENSOR_RAW_SNAPSHOT_MODE:
+		rc = mt9t013_raw_snapshot_config(mode);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	/* FIXME: what should we do if rc < 0? */
+	if (rc >= 0)
+		return mt9t013_i2c_write_w(mt9t013_client->addr,
+				REG_GROUPED_PARAMETER_HOLD,
+				GROUPED_PARAMETER_UPDATE);
+	return rc;
+}
+
+int mt9t013_sensor_config(void __user *argp)
+{
+	struct sensor_cfg_data cdata;
+	long   rc = 0;
+
+	if (copy_from_user(&cdata, (void *)argp,
+			sizeof(struct sensor_cfg_data)))
+		return -EFAULT;
+
+	down(&mt9t013_sem);
+
+	CDBG("mt9t013_sensor_config: cfgtype = %d\n", cdata.cfgtype);
+	switch (cdata.cfgtype) {
+	case CFG_GET_PICT_FPS:
+		mt9t013_get_pict_fps(cdata.cfg.gfps.prevfps,
+				&(cdata.cfg.gfps.pictfps));
+		if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PREV_L_PF:
+		cdata.cfg.prevl_pf = mt9t013_get_prev_lines_pf();
+		if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PREV_P_PL:
+		cdata.cfg.prevp_pl = mt9t013_get_prev_pixels_pl();
+		if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PICT_L_PF:
+		cdata.cfg.pictl_pf = mt9t013_get_pict_lines_pf();
+		if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PICT_P_PL:
+		cdata.cfg.pictp_pl =
+			mt9t013_get_pict_pixels_pl();
+
+		if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PICT_MAX_EXP_LC:
+		cdata.cfg.pict_max_exp_lc =
+			mt9t013_get_pict_max_exp_lc();
+
+		if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_SET_FPS:
+	case CFG_SET_PICT_FPS:
+		rc = mt9t013_set_fps(&(cdata.cfg.fps));
+		break;
+
+	case CFG_SET_EXP_GAIN:
+		rc = mt9t013_write_exp_gain(cdata.cfg.exp_gain.gain,
+				cdata.cfg.exp_gain.line);
+		break;
+
+	case CFG_SET_PICT_EXP_GAIN:
+		rc = mt9t013_set_pict_exp_gain(cdata.cfg.exp_gain.gain,
+				cdata.cfg.exp_gain.line);
+		break;
+
+	case CFG_SET_MODE:
+		rc = mt9t013_set_sensor_mode(cdata.mode, cdata.rs);
+		break;
+
+	case CFG_PWR_DOWN:
+		rc = mt9t013_power_down();
+		break;
+
+	case CFG_MOVE_FOCUS:
+		rc = mt9t013_move_focus(cdata.cfg.focus.dir,
+				cdata.cfg.focus.steps);
+		break;
+
+	case CFG_SET_DEFAULT_FOCUS:
+		rc = mt9t013_set_default_focus(cdata.cfg.focus.steps);
+		break;
+
+	case CFG_GET_AF_MAX_STEPS:
+		cdata.max_steps = MT9T013_TOTAL_STEPS_NEAR_TO_FAR;
+		if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_SET_EFFECT:
+	default:
+		rc = -EINVAL;
+		break;
+	}
+
+	up(&mt9t013_sem);
+	return rc;
+}
+
+int mt9t013_sensor_release(void)
+{
+	int rc = -EBADF;
+
+	down(&mt9t013_sem);
+
+	if (machine_is_sapphire())
+		mt9t013_poweroff_af();
+	mt9t013_power_down();
+
+	gpio_direction_output(mt9t013_ctrl->sensordata->sensor_reset,
+			0);
+	gpio_free(mt9t013_ctrl->sensordata->sensor_reset);
+
+	kfree(mt9t013_ctrl);
+
+	up(&mt9t013_sem);
+	CDBG("mt9t013_release completed!\n");
+	return rc;
+}
+
+static int mt9t013_i2c_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	int rc = 0;
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		rc = -ENOTSUPP;
+		goto probe_failure;
+	}
+
+	mt9t013_sensorw =
+		kzalloc(sizeof(struct mt9t013_work), GFP_KERNEL);
+
+	if (!mt9t013_sensorw) {
+		rc = -ENOMEM;
+		goto probe_failure;
+	}
+
+	i2c_set_clientdata(client, mt9t013_sensorw);
+	mt9t013_init_client(client);
+	mt9t013_client = client;
+	mt9t013_client->addr = mt9t013_client->addr >> 1;
+	mdelay(50);
+
+	CDBG("i2c probe ok\n");
+	return 0;
+
+probe_failure:
+	kfree(mt9t013_sensorw);
+	mt9t013_sensorw = NULL;
+	pr_err("i2c probe failure %d\n", rc);
+	return rc;
+}
+
+static const struct i2c_device_id mt9t013_i2c_id[] = {
+	{ "mt9t013", 0},
+	{ }
+};
+
+static struct i2c_driver mt9t013_i2c_driver = {
+	.id_table = mt9t013_i2c_id,
+	.probe  = mt9t013_i2c_probe,
+	.remove = __exit_p(mt9t013_i2c_remove),
+	.driver = {
+		.name = "mt9t013",
+	},
+};
+
+static int mt9t013_sensor_probe(
+		const struct msm_camera_sensor_info *info,
+		struct msm_sensor_ctrl *s)
+{
+	/* We expect this driver to match with the i2c device registered
+	 * in the board file immediately. */
+	int rc = i2c_add_driver(&mt9t013_i2c_driver);
+	if (rc < 0 || mt9t013_client == NULL) {
+		rc = -ENOTSUPP;
+		goto probe_done;
+	}
+
+	/* enable mclk first */
+	msm_camio_clk_rate_set(MT9T013_DEFAULT_CLOCK_RATE);
+	mdelay(20);
+
+	rc = mt9t013_probe_init_sensor(info);
+	if (rc < 0) {
+		i2c_del_driver(&mt9t013_i2c_driver);
+		goto probe_done;
+	}
+
+	s->s_init = mt9t013_sensor_open_init;
+	s->s_release = mt9t013_sensor_release;
+	s->s_config  = mt9t013_sensor_config;
+	s->s_mount_angle = 0;
+	mt9t013_sensor_init_done(info);
+
+probe_done:
+	return rc;
+}
+
+static int __mt9t013_probe(struct platform_device *pdev)
+{
+	return msm_camera_drv_start(pdev, mt9t013_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+	.probe = __mt9t013_probe,
+	.driver = {
+		.name = "msm_camera_mt9t013",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init mt9t013_init(void)
+{
+	return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(mt9t013_init);
diff --git a/drivers/media/video/msm/mt9t013.h b/drivers/media/video/msm/mt9t013.h
new file mode 100644
index 0000000..f6b7c28
--- /dev/null
+++ b/drivers/media/video/msm/mt9t013.h
@@ -0,0 +1,59 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef MT9T013_H
+#define MT9T013_H
+
+#include <linux/types.h>
+
+extern struct mt9t013_reg mt9t013_regs; /* from mt9t013_reg.c */
+
+struct reg_struct {
+	uint16_t vt_pix_clk_div;        /*  0x0300 */
+	uint16_t vt_sys_clk_div;        /*  0x0302 */
+	uint16_t pre_pll_clk_div;       /*  0x0304 */
+	uint16_t pll_multiplier;        /*  0x0306 */
+	uint16_t op_pix_clk_div;        /*  0x0308 */
+	uint16_t op_sys_clk_div;        /*  0x030A */
+	uint16_t scale_m;               /*  0x0404 */
+	uint16_t row_speed;             /*  0x3016 */
+	uint16_t x_addr_start;          /*  0x3004 */
+	uint16_t x_addr_end;            /*  0x3008 */
+	uint16_t y_addr_start;        	/*  0x3002 */
+	uint16_t y_addr_end;            /*  0x3006 */
+	uint16_t read_mode;             /*  0x3040 */
+	uint16_t x_output_size;         /*  0x034C */
+	uint16_t y_output_size;         /*  0x034E */
+	uint16_t line_length_pck;       /*  0x300C */
+	uint16_t frame_length_lines;	/*  0x300A */
+	uint16_t coarse_int_time; 		/*  0x3012 */
+	uint16_t fine_int_time;   		/*  0x3014 */
+};
+
+struct mt9t013_i2c_reg_conf {
+	unsigned short waddr;
+	unsigned short wdata;
+};
+
+struct mt9t013_reg {
+	struct reg_struct const *reg_pat;
+	uint16_t reg_pat_size;
+	struct mt9t013_i2c_reg_conf const *ttbl;
+	uint16_t ttbl_size;
+	struct mt9t013_i2c_reg_conf const *lctbl;
+	uint16_t lctbl_size;
+	struct mt9t013_i2c_reg_conf const *rftbl;
+	uint16_t rftbl_size;
+};
+
+#endif /* #define MT9T013_H */
diff --git a/drivers/media/video/msm/mt9t013_reg.c b/drivers/media/video/msm/mt9t013_reg.c
new file mode 100644
index 0000000..9a9867e
--- /dev/null
+++ b/drivers/media/video/msm/mt9t013_reg.c
@@ -0,0 +1,275 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "mt9t013.h"
+#include <linux/kernel.h>
+
+struct reg_struct const mt9t013_reg_pat[2] = {
+	{ /* Preview 2x2 binning 20fps, pclk MHz, MCLK 24MHz */
+	/* vt_pix_clk_div:REG=0x0300 update get_snapshot_fps
+	* if this change */
+	8,
+
+	/* vt_sys_clk_div: REG=0x0302  update get_snapshot_fps
+	* if this change */
+	1,
+
+	/* pre_pll_clk_div REG=0x0304  update get_snapshot_fps
+	* if this change */
+	2,
+
+	/* pll_multiplier  REG=0x0306 60 for 30fps preview, 40
+	 * for 20fps preview
+	 * 46 for 30fps preview, try 47/48 to increase further */
+	46,
+
+	/* op_pix_clk_div        REG=0x0308 */
+	8,
+
+	/* op_sys_clk_div        REG=0x030A */
+	1,
+
+	/* scale_m       REG=0x0404 */
+	16,
+
+	/* row_speed     REG=0x3016 */
+	0x0111,
+
+	/* x_addr_start  REG=0x3004 */
+	8,
+
+	/* x_addr_end    REG=0x3008 */
+	2053,
+
+	/* y_addr_start  REG=0x3002 */
+	8,
+
+	/* y_addr_end    REG=0x3006 */
+	1541,
+
+	/* read_mode     REG=0x3040 */
+	0x046C,
+
+	/* x_output_size REG=0x034C */
+	1024,
+
+	/* y_output_size REG=0x034E */
+	768,
+
+	/* line_length_pck    REG=0x300C */
+	2616,
+
+	/* frame_length_lines REG=0x300A */
+	916,
+
+	/* coarse_int_time REG=0x3012 */
+	16,
+
+	/* fine_int_time   REG=0x3014 */
+	1461
+	},
+	{ /*Snapshot */
+	/* vt_pix_clk_div  REG=0x0300 update get_snapshot_fps
+	* if this change */
+	8,
+
+	/* vt_sys_clk_div  REG=0x0302 update get_snapshot_fps
+	* if this change */
+	1,
+
+	/* pre_pll_clk_div REG=0x0304 update get_snapshot_fps
+	 * if this change */
+	2,
+
+	/* pll_multiplier REG=0x0306 50 for 15fps snapshot,
+	 * 40 for 10fps snapshot
+	 * 46 for 30fps snapshot, try 47/48 to increase further */
+	46,
+
+	/* op_pix_clk_div        REG=0x0308 */
+	8,
+
+	/* op_sys_clk_div        REG=0x030A */
+	1,
+
+	/* scale_m       REG=0x0404 */
+	16,
+
+	/* row_speed     REG=0x3016 */
+	0x0111,
+
+	/* x_addr_start  REG=0x3004 */
+	8,
+
+	/* x_addr_end    REG=0x3008 */
+	2071,
+
+	/* y_addr_start  REG=0x3002 */
+	8,
+
+	/* y_addr_end    REG=0x3006 */
+	1551,
+
+	/* read_mode     REG=0x3040 */
+	0x0024,
+
+	/* x_output_size REG=0x034C */
+	2064,
+
+	/* y_output_size REG=0x034E */
+	1544,
+
+	/* line_length_pck REG=0x300C */
+	2952,
+
+	/* frame_length_lines    REG=0x300A */
+	1629,
+
+	/* coarse_int_time REG=0x3012 */
+	16,
+
+	/* fine_int_time REG=0x3014   */
+	733
+	}
+};
+
+struct mt9t013_i2c_reg_conf const mt9t013_test_tbl[] = {
+	{ 0x3044, 0x0544 & 0xFBFF },
+	{ 0x30CA, 0x0004 | 0x0001 },
+	{ 0x30D4, 0x9020 & 0x7FFF },
+	{ 0x31E0, 0x0003 & 0xFFFE },
+	{ 0x3180, 0x91FF & 0x7FFF },
+	{ 0x301A, (0x10CC | 0x8000) & 0xFFF7 },
+	{ 0x301E, 0x0000 },
+	{ 0x3780, 0x0000 },
+};
+
+/* [Lens shading 85 Percent TL84] */
+struct mt9t013_i2c_reg_conf const mt9t013_lc_tbl[] = {
+	{ 0x360A, 0x0290 }, /* P_RD_P0Q0 */
+	{ 0x360C, 0xC92D }, /* P_RD_P0Q1 */
+	{ 0x360E, 0x0771 }, /* P_RD_P0Q2 */
+	{ 0x3610, 0xE38C }, /* P_RD_P0Q3 */
+	{ 0x3612, 0xD74F }, /* P_RD_P0Q4 */
+	{ 0x364A, 0x168C }, /* P_RD_P1Q0 */
+	{ 0x364C, 0xCACB }, /* P_RD_P1Q1 */
+	{ 0x364E, 0x8C4C }, /* P_RD_P1Q2 */
+	{ 0x3650, 0x0BEA }, /* P_RD_P1Q3 */
+	{ 0x3652, 0xDC0F }, /* P_RD_P1Q4 */
+	{ 0x368A, 0x70B0 }, /* P_RD_P2Q0 */
+	{ 0x368C, 0x200B }, /* P_RD_P2Q1 */
+	{ 0x368E, 0x30B2 }, /* P_RD_P2Q2 */
+	{ 0x3690, 0xD04F }, /* P_RD_P2Q3 */
+	{ 0x3692, 0xACF5 }, /* P_RD_P2Q4 */
+	{ 0x36CA, 0xF7C9 }, /* P_RD_P3Q0 */
+	{ 0x36CC, 0x2AED }, /* P_RD_P3Q1 */
+	{ 0x36CE, 0xA652 }, /* P_RD_P3Q2 */
+	{ 0x36D0, 0x8192 }, /* P_RD_P3Q3 */
+	{ 0x36D2, 0x3A15 }, /* P_RD_P3Q4 */
+	{ 0x370A, 0xDA30 }, /* P_RD_P4Q0 */
+	{ 0x370C, 0x2E2F }, /* P_RD_P4Q1 */
+	{ 0x370E, 0xBB56 }, /* P_RD_P4Q2 */
+	{ 0x3710, 0x8195 }, /* P_RD_P4Q3 */
+	{ 0x3712, 0x02F9 }, /* P_RD_P4Q4 */
+	{ 0x3600, 0x0230 }, /* P_GR_P0Q0 */
+	{ 0x3602, 0x58AD }, /* P_GR_P0Q1 */
+	{ 0x3604, 0x18D1 }, /* P_GR_P0Q2 */
+	{ 0x3606, 0x260D }, /* P_GR_P0Q3 */
+	{ 0x3608, 0xF530 }, /* P_GR_P0Q4 */
+	{ 0x3640, 0x17EB }, /* P_GR_P1Q0 */
+	{ 0x3642, 0x3CAB }, /* P_GR_P1Q1 */
+	{ 0x3644, 0x87CE }, /* P_GR_P1Q2 */
+	{ 0x3646, 0xC02E }, /* P_GR_P1Q3 */
+	{ 0x3648, 0xF48F }, /* P_GR_P1Q4 */
+	{ 0x3680, 0x5350 }, /* P_GR_P2Q0 */
+	{ 0x3682, 0x7EAF }, /* P_GR_P2Q1 */
+	{ 0x3684, 0x4312 }, /* P_GR_P2Q2 */
+	{ 0x3686, 0xC652 }, /* P_GR_P2Q3 */
+	{ 0x3688, 0xBC15 }, /* P_GR_P2Q4 */
+	{ 0x36C0, 0xB8AD }, /* P_GR_P3Q0 */
+	{ 0x36C2, 0xBDCD }, /* P_GR_P3Q1 */
+	{ 0x36C4, 0xE4B2 }, /* P_GR_P3Q2 */
+	{ 0x36C6, 0xB50F }, /* P_GR_P3Q3 */
+	{ 0x36C8, 0x5B95 }, /* P_GR_P3Q4 */
+	{ 0x3700, 0xFC90 }, /* P_GR_P4Q0 */
+	{ 0x3702, 0x8C51 }, /* P_GR_P4Q1 */
+	{ 0x3704, 0xCED6 }, /* P_GR_P4Q2 */
+	{ 0x3706, 0xB594 }, /* P_GR_P4Q3 */
+	{ 0x3708, 0x0A39 }, /* P_GR_P4Q4 */
+	{ 0x3614, 0x0230 }, /* P_BL_P0Q0 */
+	{ 0x3616, 0x160D }, /* P_BL_P0Q1 */
+	{ 0x3618, 0x08D1 }, /* P_BL_P0Q2 */
+	{ 0x361A, 0x98AB }, /* P_BL_P0Q3 */
+	{ 0x361C, 0xEA50 }, /* P_BL_P0Q4 */
+	{ 0x3654, 0xB4EA }, /* P_BL_P1Q0 */
+	{ 0x3656, 0xEA6C }, /* P_BL_P1Q1 */
+	{ 0x3658, 0xFE08 }, /* P_BL_P1Q2 */
+	{ 0x365A, 0x2C6E }, /* P_BL_P1Q3 */
+	{ 0x365C, 0xEB0E }, /* P_BL_P1Q4 */
+	{ 0x3694, 0x6DF0 }, /* P_BL_P2Q0 */
+	{ 0x3696, 0x3ACF }, /* P_BL_P2Q1 */
+	{ 0x3698, 0x3E0F }, /* P_BL_P2Q2 */
+	{ 0x369A, 0xB2B1 }, /* P_BL_P2Q3 */
+	{ 0x369C, 0xC374 }, /* P_BL_P2Q4 */
+	{ 0x36D4, 0xF2AA }, /* P_BL_P3Q0 */
+	{ 0x36D6, 0x8CCC }, /* P_BL_P3Q1 */
+	{ 0x36D8, 0xDEF2 }, /* P_BL_P3Q2 */
+	{ 0x36DA, 0xFA11 }, /* P_BL_P3Q3 */
+	{ 0x36DC, 0x42F5 }, /* P_BL_P3Q4 */
+	{ 0x3714, 0xF4F1 }, /* P_BL_P4Q0 */
+	{ 0x3716, 0xF6F0 }, /* P_BL_P4Q1 */
+	{ 0x3718, 0x8FD6 }, /* P_BL_P4Q2 */
+	{ 0x371A, 0xEA14 }, /* P_BL_P4Q3 */
+	{ 0x371C, 0x6338 }, /* P_BL_P4Q4 */
+	{ 0x361E, 0x0350 }, /* P_GB_P0Q0 */
+	{ 0x3620, 0x91AE }, /* P_GB_P0Q1 */
+	{ 0x3622, 0x0571 }, /* P_GB_P0Q2 */
+	{ 0x3624, 0x100D }, /* P_GB_P0Q3 */
+	{ 0x3626, 0xCA70 }, /* P_GB_P0Q4 */
+	{ 0x365E, 0xE6CB }, /* P_GB_P1Q0 */
+	{ 0x3660, 0x50ED }, /* P_GB_P1Q1 */
+	{ 0x3662, 0x3DAE }, /* P_GB_P1Q2 */
+	{ 0x3664, 0xAA4F }, /* P_GB_P1Q3 */
+	{ 0x3666, 0xDC50 }, /* P_GB_P1Q4 */
+	{ 0x369E, 0x5470 }, /* P_GB_P2Q0 */
+	{ 0x36A0, 0x1F6E }, /* P_GB_P2Q1 */
+	{ 0x36A2, 0x6671 }, /* P_GB_P2Q2 */
+	{ 0x36A4, 0xC010 }, /* P_GB_P2Q3 */
+	{ 0x36A6, 0x8DF5 }, /* P_GB_P2Q4 */
+	{ 0x36DE, 0x0B0C }, /* P_GB_P3Q0 */
+	{ 0x36E0, 0x84CE }, /* P_GB_P3Q1 */
+	{ 0x36E2, 0x8493 }, /* P_GB_P3Q2 */
+	{ 0x36E4, 0xA610 }, /* P_GB_P3Q3 */
+	{ 0x36E6, 0x50B5 }, /* P_GB_P3Q4 */
+	{ 0x371E, 0x9651 }, /* P_GB_P4Q0 */
+	{ 0x3720, 0x1EAB }, /* P_GB_P4Q1 */
+	{ 0x3722, 0xAF76 }, /* P_GB_P4Q2 */
+	{ 0x3724, 0xE4F4 }, /* P_GB_P4Q3 */
+	{ 0x3726, 0x79F8 }, /* P_GB_P4Q4 */
+	{ 0x3782, 0x0410 }, /* POLY_ORIGIN_C */
+	{ 0x3784, 0x0320 }, /* POLY_ORIGIN_R  */
+	{ 0x3780, 0x8000 } /* POLY_SC_ENABLE */
+};
+
+struct mt9t013_reg mt9t013_regs = {
+	.reg_pat = &mt9t013_reg_pat[0],
+	.reg_pat_size = ARRAY_SIZE(mt9t013_reg_pat),
+	.ttbl = &mt9t013_test_tbl[0],
+	.ttbl_size = ARRAY_SIZE(mt9t013_test_tbl),
+	.lctbl = &mt9t013_lc_tbl[0],
+	.lctbl_size = ARRAY_SIZE(mt9t013_lc_tbl),
+	.rftbl = &mt9t013_lc_tbl[0],	/* &mt9t013_rolloff_tbl[0], */
+	.rftbl_size = ARRAY_SIZE(mt9t013_lc_tbl)
+};
+
+
diff --git a/drivers/media/video/msm/ov5640.c b/drivers/media/video/msm/ov5640.c
new file mode 100644
index 0000000..1380bcf
--- /dev/null
+++ b/drivers/media/video/msm/ov5640.c
@@ -0,0 +1,1477 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/* #define DEBUG */
+
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/leds.h>
+#include <linux/slab.h>
+#include <media/msm_camera.h>
+#include <mach/gpio.h>
+#include <mach/camera.h>
+#include "ov5640.h"
+
+#define FALSE 0
+#define TRUE 1
+
+struct ov5640_work {
+	struct work_struct work;
+};
+
+struct __ov5640_ctrl {
+	const struct msm_camera_sensor_info *sensordata;
+	int sensormode;
+	uint fps_divider; /* init to 1 * 0x00000400 */
+	uint pict_fps_divider; /* init to 1 * 0x00000400 */
+	u16 curr_step_pos;
+	u16 curr_lens_pos;
+	u16 init_curr_lens_pos;
+	u16 my_reg_gain;
+	u16 my_reg_line_count;
+	enum msm_s_resolution prev_res;
+	enum msm_s_resolution pict_res;
+	enum msm_s_resolution curr_res;
+	enum msm_s_test_mode  set_test;
+};
+
+static DECLARE_WAIT_QUEUE_HEAD(ov5640_wait_queue);
+DEFINE_MUTEX(ov5640_mutex);
+
+static int ov5640_pwdn_gpio;
+static int ov5640_reset_gpio;
+static int ov5640_driver_pwdn_gpio;
+static int OV5640_CSI_CONFIG;
+static struct ov5640_work *ov5640_sensorw;
+static struct i2c_client    *ov5640_client;
+static u8 ov5640_i2c_buf[4];
+static u8 ov5640_counter;
+static int16_t ov5640_effect;
+static int is_autoflash;
+static int effect_value;
+unsigned int ov5640_SAT_U = 0x40;
+unsigned int ov5640_SAT_V = 0x40;
+
+static struct __ov5640_ctrl *ov5640_ctrl;
+static int ov5640_afinit = 1;
+
+struct rw_semaphore ov_leds_list_lock;
+struct list_head ov_leds_list;
+
+static int ov5640_i2c_remove(struct i2c_client *client);
+static int ov5640_i2c_probe(struct i2c_client *client,
+		const struct i2c_device_id *id);
+
+static int ov5640_i2c_txdata(u16 saddr, u8 *txdata, int length)
+{
+	struct i2c_msg msg[] = {
+		{
+			.addr	= saddr,
+			.flags	= 0,
+			.len	= length,
+			.buf	= txdata,
+		},
+	};
+
+	if (i2c_transfer(ov5640_client->adapter, msg, 1) < 0)
+		return -EIO;
+	else
+		return 0;
+}
+
+static int ov5640_i2c_write(unsigned short saddr, unsigned int waddr,
+		unsigned short bdata, u8 trytimes)
+{
+	int rc = -EIO;
+
+	ov5640_counter = 0;
+	ov5640_i2c_buf[0] = (waddr & 0xFF00) >> 8;
+	ov5640_i2c_buf[1] = (waddr & 0x00FF);
+	ov5640_i2c_buf[2] = (bdata & 0x00FF);
+
+	while ((ov5640_counter < trytimes) && (rc != 0)) {
+		rc = ov5640_i2c_txdata(saddr, ov5640_i2c_buf, 3);
+
+		if (rc < 0) {
+			ov5640_counter++;
+			CDBG("***--CAMERA i2c_write_w failed,i2c addr=0x%x,"
+				"command addr = 0x%x, val = 0x%x,s=%d,"
+					"rc=%d!\n", saddr, waddr, bdata,
+					ov5640_counter, rc);
+			msleep(20);
+		}
+	}
+	return rc;
+}
+
+static int ov5640_i2c_rxdata(unsigned short saddr, unsigned char *rxdata,
+		int length)
+{
+	struct i2c_msg msgs[] = {
+		{
+			.addr	= saddr,
+			.flags	= 0,
+			.len	= 2,
+			.buf	= rxdata,
+		},
+		{
+			.addr	= saddr,
+			.flags	= I2C_M_RD,
+			.len	= length,
+			.buf	= rxdata,
+		},
+	};
+
+	if (i2c_transfer(ov5640_client->adapter, msgs, 2) < 0) {
+		CDBG("ov5640_i2c_rxdata failed!\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int32_t ov5640_i2c_read_byte(unsigned short  saddr,
+		unsigned int raddr, unsigned int *rdata)
+{
+	int rc = 0;
+	unsigned char buf[2];
+
+	memset(buf, 0, sizeof(buf));
+
+	buf[0] = (raddr & 0xFF00)>>8;
+	buf[1] = (raddr & 0x00FF);
+
+	rc = ov5640_i2c_rxdata(saddr, buf, 1);
+	if (rc < 0) {
+		CDBG("ov5640_i2c_read_byte failed!\n");
+		return rc;
+	}
+
+	*rdata = buf[0];
+
+	return rc;
+}
+
+static int32_t ov5640_writepregs(struct ov5640_sensor *ptb, int32_t len)
+{
+	int32_t i, ret = 0;
+	uint32_t regv;
+
+	for (i = 0; i < len; i++) {
+		if (0 == ptb[i].mask) {
+			ov5640_i2c_write(ov5640_client->addr, ptb[i].addr,
+					ptb[i].data, 10);
+		} else {
+			ov5640_i2c_read_byte(ov5640_client->addr, ptb[i].addr,
+					&regv);
+			regv &= ptb[i].mask;
+			regv |= (ptb[i].data & (~ptb[i].mask));
+			ov5640_i2c_write(ov5640_client->addr, ptb[i].addr,
+					regv, 10);
+		}
+	}
+	return ret;
+}
+
+static void camera_sw_power_onoff(int v)
+{
+	if (v == 0) {
+		CDBG("camera_sw_power_onoff: down\n");
+		ov5640_i2c_write(ov5640_client->addr, 0x3008, 0x42, 10);
+	} else {
+		CDBG("camera_sw_power_onoff: on\n");
+		ov5640_i2c_write(ov5640_client->addr, 0x3008, 0x02, 10);
+	}
+}
+
+static void ov5640_power_off(void)
+{
+	CDBG("--CAMERA-- %s ... (Start...)\n", __func__);
+	gpio_set_value(ov5640_pwdn_gpio, 1);
+	CDBG("--CAMERA-- %s ... (End...)\n", __func__);
+}
+
+static void ov5640_power_on(void)
+{
+	CDBG("--CAMERA-- %s ... (Start...)\n", __func__);
+	gpio_set_value(ov5640_pwdn_gpio, 0);
+	CDBG("--CAMERA-- %s ... (End...)\n", __func__);
+}
+
+static void ov5640_power_reset(void)
+{
+	CDBG("--CAMERA-- %s ... (Start...)\n", __func__);
+	gpio_set_value(ov5640_reset_gpio, 1);   /* reset camera reset pin */
+	msleep(20);
+	gpio_set_value(ov5640_reset_gpio, 0);
+	msleep(20);
+	gpio_set_value(ov5640_reset_gpio, 1);
+	msleep(20);
+
+	CDBG("--CAMERA-- %s ... (End...)\n", __func__);
+}
+
+static int ov5640_probe_readID(const struct msm_camera_sensor_info *data)
+{
+	int rc = 0;
+	u32 device_id_high = 0;
+	u32 device_id_low = 0;
+
+	CDBG("--CAMERA-- %s (Start...)\n", __func__);
+	CDBG("--CAMERA-- %s sensor poweron,begin to read ID!\n", __func__);
+
+	/* 0x300A ,sensor ID register */
+	rc = ov5640_i2c_read_byte(ov5640_client->addr, 0x300A,
+			&device_id_high);
+
+	if (rc < 0) {
+		CDBG("--CAMERA-- %s ok , readI2C failed, rc = 0x%x\r\n",
+				__func__, rc);
+		return rc;
+	}
+	CDBG("--CAMERA-- %s  readID high byte, data = 0x%x\r\n",
+			__func__, device_id_high);
+
+	/* 0x300B ,sensor ID register */
+	rc = ov5640_i2c_read_byte(ov5640_client->addr, 0x300B,
+			&device_id_low);
+	if (rc < 0) {
+		CDBG("--CAMERA-- %s ok , readI2C failed,rc = 0x%x\r\n",
+				__func__, rc);
+		return rc;
+	}
+
+	CDBG("--CAMERA-- %s  readID low byte, data = 0x%x\r\n",
+			__func__, device_id_low);
+	CDBG("--CAMERA-- %s return ID :0x%x\n", __func__,
+			(device_id_high << 8) + device_id_low);
+
+	/* 0x5640, ov5640 chip id */
+	if ((device_id_high << 8) + device_id_low != OV5640_SENSOR_ID) {
+		CDBG("--CAMERA-- %s ok , device id error, should be 0x%x\r\n",
+				__func__, OV5640_SENSOR_ID);
+		return -EINVAL;
+	} else {
+		CDBG("--CAMERA-- %s ok , device id=0x%x\n", __func__,
+				OV5640_SENSOR_ID);
+		return 0;
+	}
+}
+
+static int ov5640_af_setting(void)
+{
+	int rc = 0;
+	int lens = sizeof(ov5640_afinit_tbl) / sizeof(ov5640_afinit_tbl[0]);
+
+	CDBG("--CAMERA-- ov5640_af_setting\n");
+
+	ov5640_i2c_write(ov5640_client->addr, 0x3000, 0x20, 10);
+
+	rc = ov5640_i2c_txdata(ov5640_client->addr, ov5640_afinit_tbl, lens);
+	if (rc < 0) {
+		CDBG("--CAMERA-- AF_init failed\n");
+		return rc;
+	}
+
+	ov5640_i2c_write(ov5640_client->addr, OV5640_CMD_MAIN, 0x00, 10);
+	ov5640_i2c_write(ov5640_client->addr, OV5640_CMD_ACK, 0x00, 10);
+	ov5640_i2c_write(ov5640_client->addr, OV5640_CMD_PARA0, 0x00, 10);
+	ov5640_i2c_write(ov5640_client->addr, OV5640_CMD_PARA1, 0x00, 10);
+	ov5640_i2c_write(ov5640_client->addr, OV5640_CMD_PARA2, 0x00, 10);
+	ov5640_i2c_write(ov5640_client->addr, OV5640_CMD_PARA3, 0x00, 10);
+	ov5640_i2c_write(ov5640_client->addr, OV5640_CMD_PARA4, 0x00, 10);
+	ov5640_i2c_write(ov5640_client->addr, OV5640_CMD_FW_STATUS, 0x7f, 10);
+	ov5640_i2c_write(ov5640_client->addr, 0x3000, 0x00, 10);
+
+	return rc;
+}
+
+static int ov5640_set_flash_light(enum led_brightness brightness)
+{
+	struct led_classdev *led_cdev;
+
+	CDBG("ov5640_set_flash_light brightness = %d\n", brightness);
+
+	down_read(&ov_leds_list_lock);
+	list_for_each_entry(led_cdev, &ov_leds_list, node) {
+		if (!strncmp(led_cdev->name, "flashlight", 10))
+			break;
+	}
+	up_read(&ov_leds_list_lock);
+
+	if (led_cdev) {
+		led_brightness_set(led_cdev, brightness);
+	} else {
+		CDBG("get flashlight device failed\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ov5640_video_config(void)
+{
+	int rc = 0;
+
+	CDBG("--CAMERA-- ov5640_video_config\n");
+	CDBG("--CAMERA-- preview in, is_autoflash - 0x%x\n", is_autoflash);
+
+	/* autoflash setting */
+	if (is_autoflash == 1)
+		ov5640_set_flash_light(LED_OFF);
+
+	/* preview setting */
+	rc = OV5640CORE_WRITEPREG(ov5640_preview_tbl);
+	return rc;
+}
+
+static int ov5640_snapshot_config(void)
+{
+	int rc = 0;
+	unsigned int tmp;
+
+	CDBG("--CAMERA-- SENSOR_SNAPSHOT_MODE\n");
+	CDBG("--CAMERA-- %s, snapshot in, is_autoflash - 0x%x\n", __func__,
+			is_autoflash);
+
+	if (is_autoflash == 1) {
+		ov5640_i2c_read_byte(ov5640_client->addr, 0x350b, &tmp);
+		CDBG("--CAMERA-- GAIN VALUE : %x\n", tmp);
+		if ((tmp & 0x80) == 0)
+			ov5640_set_flash_light(LED_OFF);
+		else
+			ov5640_set_flash_light(LED_FULL);
+	}
+
+	rc = OV5640CORE_WRITEPREG(ov5640_capture_tbl);
+
+	return rc;
+}
+
+static int ov5640_setting(enum msm_s_reg_update rupdate,
+		enum msm_s_setting rt)
+{
+	int rc = -EINVAL, tmp;
+	struct msm_camera_csi_params ov5640_csi_params;
+
+	CDBG("--CAMERA-- %s (Start...), rupdate=%d\n", __func__, rupdate);
+
+	switch (rupdate) {
+	case S_UPDATE_PERIODIC:
+		if (!OV5640_CSI_CONFIG) {
+			camera_sw_power_onoff(0); /* standby */
+			msleep(20);
+
+			ov5640_csi_params.lane_cnt = 2;
+			ov5640_csi_params.data_format = CSI_8BIT;
+			ov5640_csi_params.lane_assign = 0xe4;
+			ov5640_csi_params.dpcm_scheme = 0;
+			ov5640_csi_params.settle_cnt = 0x6;
+
+			CDBG("%s: msm_camio_csi_config\n", __func__);
+
+			rc = msm_camio_csi_config(&ov5640_csi_params);
+			msleep(20);
+			camera_sw_power_onoff(1); /* on */
+			msleep(20);
+
+			OV5640_CSI_CONFIG = 1;
+
+		} else {
+			rc = 0;
+		}
+
+		if (S_RES_PREVIEW == rt)
+			rc = ov5640_video_config();
+		else if (S_RES_CAPTURE == rt)
+			rc = ov5640_snapshot_config();
+
+		break; /* UPDATE_PERIODIC */
+
+	case S_REG_INIT:
+		CDBG("--CAMERA-- S_REG_INIT (Start)\n");
+
+		rc = ov5640_i2c_write(ov5640_client->addr, 0x3103, 0x11, 10);
+		rc = ov5640_i2c_write(ov5640_client->addr, 0x3008, 0x82, 10);
+		msleep(20);
+
+		/* set sensor init setting */
+		CDBG("set sensor init setting\n");
+		rc = OV5640CORE_WRITEPREG(ov5640_init_tbl);
+		if (rc < 0) {
+			CDBG("sensor init setting failed\n");
+			break;
+		}
+
+		/* set image quality setting */
+		rc = OV5640CORE_WRITEPREG(ov5640_init_iq_tbl);
+		rc = ov5640_i2c_read_byte(ov5640_client->addr, 0x4740, &tmp);
+		CDBG("--CAMERA-- init 0x4740 value=0x%x\n", tmp);
+
+		if (tmp != 0x21) {
+			rc = ov5640_i2c_write(ov5640_client->addr, 0x4740,
+					0x21, 10);
+			msleep(20);
+			rc = ov5640_i2c_read_byte(ov5640_client->addr,
+					0x4740, &tmp);
+			CDBG("--CAMERA-- WG 0x4740 value=0x%x\n", tmp);
+		}
+
+		CDBG("--CAMERA-- AF_init: ov5640_afinit = %d\n",
+				ov5640_afinit);
+		if (ov5640_afinit == 1) {
+			rc = ov5640_af_setting();
+			if (rc < 0) {
+				CDBG("--CAMERA-- ov5640_af_setting failed\n");
+				break;
+			}
+			ov5640_afinit = 0;
+		}
+
+		/* reset fps_divider */
+		ov5640_ctrl->fps_divider = 1 * 0x0400;
+		CDBG("--CAMERA-- S_REG_INIT (End)\n");
+		break; /* case REG_INIT: */
+
+	default:
+		break;
+	} /* switch (rupdate) */
+
+	CDBG("--CAMERA-- %s (End), rupdate=%d\n", __func__, rupdate);
+
+	return rc;
+}
+
+static int ov5640_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+	int rc = -ENOMEM;
+
+	CDBG("--CAMERA-- %s\n", __func__);
+	ov5640_ctrl = kzalloc(sizeof(struct __ov5640_ctrl), GFP_KERNEL);
+	if (!ov5640_ctrl) {
+		CDBG("--CAMERA-- kzalloc ov5640_ctrl error !!\n");
+		kfree(ov5640_ctrl);
+		return rc;
+	}
+
+	ov5640_ctrl->fps_divider = 1 * 0x00000400;
+	ov5640_ctrl->pict_fps_divider = 1 * 0x00000400;
+	ov5640_ctrl->set_test = S_TEST_OFF;
+	ov5640_ctrl->prev_res = S_QTR_SIZE;
+	ov5640_ctrl->pict_res = S_FULL_SIZE;
+
+	if (data)
+		ov5640_ctrl->sensordata = data;
+
+	ov5640_power_off();
+
+	CDBG("%s: msm_camio_clk_rate_set\n", __func__);
+
+	msm_camio_clk_rate_set(24000000);
+	msleep(20);
+
+	ov5640_power_on();
+	ov5640_power_reset();
+
+	CDBG("%s: init sequence\n", __func__);
+
+	if (ov5640_ctrl->prev_res == S_QTR_SIZE)
+		rc = ov5640_setting(S_REG_INIT, S_RES_PREVIEW);
+	else
+		rc = ov5640_setting(S_REG_INIT, S_RES_CAPTURE);
+
+	if (rc < 0) {
+		CDBG("--CAMERA-- %s : ov5640_setting failed. rc = %d\n",
+				__func__, rc);
+		kfree(ov5640_ctrl);
+		return rc;
+	}
+
+	OV5640_CSI_CONFIG = 0;
+
+	CDBG("--CAMERA--re_init_sensor ok!!\n");
+	return rc;
+}
+
+static int ov5640_sensor_release(void)
+{
+	CDBG("--CAMERA--ov5640_sensor_release!!\n");
+
+	mutex_lock(&ov5640_mutex);
+
+	ov5640_power_off();
+
+	kfree(ov5640_ctrl);
+	ov5640_ctrl = NULL;
+
+	OV5640_CSI_CONFIG = 0;
+
+	mutex_unlock(&ov5640_mutex);
+	return 0;
+}
+
+static const struct i2c_device_id ov5640_i2c_id[] = {
+	{"ov5640",  0}, {}
+};
+
+static int ov5640_i2c_remove(struct i2c_client *client)
+{
+	return 0;
+}
+
+static int ov5640_init_client(struct i2c_client *client)
+{
+	/* Initialize the MSM_CAMI2C Chip */
+	init_waitqueue_head(&ov5640_wait_queue);
+	return 0;
+}
+
+static long ov5640_set_effect(int mode, int effect)
+{
+	int rc = 0;
+
+	CDBG("--CAMERA-- %s ...(Start)\n", __func__);
+
+	switch (mode) {
+	case SENSOR_PREVIEW_MODE:
+		/* Context A Special Effects */
+		CDBG("--CAMERA-- %s ...SENSOR_PREVIEW_MODE\n", __func__);
+		break;
+
+	case SENSOR_SNAPSHOT_MODE:
+		/* Context B Special Effects */
+		CDBG("--CAMERA-- %s ...SENSOR_SNAPSHOT_MODE\n", __func__);
+		break;
+
+	default:
+		break;
+	}
+
+	effect_value = effect;
+
+	switch (effect)	{
+	case CAMERA_EFFECT_OFF:
+		CDBG("--CAMERA-- %s ...CAMERA_EFFECT_OFF\n", __func__);
+		rc = OV5640CORE_WRITEPREG(ov5640_effect_normal_tbl);
+		/* for recover saturation level when change special effect */
+		ov5640_i2c_write(ov5640_client->addr, 0x5583, ov5640_SAT_U,
+				10);
+		/* for recover saturation level when change special effect */
+		ov5640_i2c_write(ov5640_client->addr, 0x5584, ov5640_SAT_V,
+				10);
+		break;
+
+	case CAMERA_EFFECT_MONO:
+		CDBG("--CAMERA-- %s ...CAMERA_EFFECT_MONO\n", __func__);
+		rc = OV5640CORE_WRITEPREG(ov5640_effect_mono_tbl);
+		break;
+
+	case CAMERA_EFFECT_BW:
+		CDBG("--CAMERA-- %s ...CAMERA_EFFECT_BW\n", __func__);
+		rc = OV5640CORE_WRITEPREG(ov5640_effect_bw_tbl);
+		break;
+
+	case CAMERA_EFFECT_BLUISH:
+		CDBG("--CAMERA-- %s ...CAMERA_EFFECT_BLUISH\n", __func__);
+		rc = OV5640CORE_WRITEPREG(ov5640_effect_bluish_tbl);
+		break;
+
+	case CAMERA_EFFECT_SOLARIZE:
+		CDBG("--CAMERA-- %s ...CAMERA_EFFECT_NEGATIVE\n", __func__);
+		rc = OV5640CORE_WRITEPREG(ov5640_effect_solarize_tbl);
+		break;
+
+	case CAMERA_EFFECT_SEPIA:
+		CDBG("--CAMERA-- %s ...CAMERA_EFFECT_SEPIA\n", __func__);
+		rc = OV5640CORE_WRITEPREG(ov5640_effect_sepia_tbl);
+		break;
+
+	case CAMERA_EFFECT_REDDISH:
+		CDBG("--CAMERA-- %s ...CAMERA_EFFECT_REDDISH\n", __func__);
+		rc = OV5640CORE_WRITEPREG(ov5640_effect_reddish_tbl);
+		break;
+
+	case CAMERA_EFFECT_GREENISH:
+		CDBG("--CAMERA-- %s ...CAMERA_EFFECT_GREENISH\n", __func__);
+		rc = OV5640CORE_WRITEPREG(ov5640_effect_greenish_tbl);
+		break;
+
+	case CAMERA_EFFECT_NEGATIVE:
+		CDBG("--CAMERA-- %s ...CAMERA_EFFECT_NEGATIVE\n", __func__);
+		rc = OV5640CORE_WRITEPREG(ov5640_effect_negative_tbl);
+		break;
+
+	default:
+		CDBG("--CAMERA-- %s ...Default(Not Support)\n", __func__);
+	}
+
+	ov5640_effect = effect;
+	/* Refresh Sequencer */
+	CDBG("--CAMERA-- %s ...(End)\n", __func__);
+	return rc;
+}
+
+static int ov5640_set_brightness(int8_t brightness)
+{
+	int rc = 0;
+
+	CDBG("--CAMERA-- %s ...(Start)\n", __func__);
+	CDBG("--CAMERA-- %s ...brightness = %d\n", __func__ , brightness);
+
+	switch (brightness) {
+	case CAMERA_BRIGHTNESS_LV0:
+		CDBG("--CAMERA--CAMERA_BRIGHTNESS_LV0\n");
+		rc = OV5640CORE_WRITEPREG(ov5640_brightness_lv0_tbl);
+		break;
+
+	case CAMERA_BRIGHTNESS_LV1:
+		CDBG("--CAMERA--CAMERA_BRIGHTNESS_LV1\n");
+		rc = OV5640CORE_WRITEPREG(ov5640_brightness_lv1_tbl);
+		break;
+
+	case CAMERA_BRIGHTNESS_LV2:
+		CDBG("--CAMERA--CAMERA_BRIGHTNESS_LV2\n");
+		rc = OV5640CORE_WRITEPREG(ov5640_brightness_lv2_tbl);
+		break;
+
+	case CAMERA_BRIGHTNESS_LV3:
+		CDBG("--CAMERA--CAMERA_BRIGHTNESS_LV3\n");
+		rc = OV5640CORE_WRITEPREG(ov5640_brightness_lv3_tbl);
+		break;
+
+	case CAMERA_BRIGHTNESS_LV4:
+		CDBG("--CAMERA--CAMERA_BRIGHTNESS_LV4\n");
+		rc = OV5640CORE_WRITEPREG(ov5640_brightness_default_lv4_tbl);
+		break;
+
+	case CAMERA_BRIGHTNESS_LV5:
+		CDBG("--CAMERA--CAMERA_BRIGHTNESS_LV5\n");
+		rc = OV5640CORE_WRITEPREG(ov5640_brightness_lv5_tbl);
+		break;
+
+	case CAMERA_BRIGHTNESS_LV6:
+		CDBG("--CAMERA--CAMERA_BRIGHTNESS_LV6\n");
+		rc = OV5640CORE_WRITEPREG(ov5640_brightness_lv6_tbl);
+		break;
+
+	case CAMERA_BRIGHTNESS_LV7:
+		CDBG("--CAMERA--CAMERA_BRIGHTNESS_LV7\n");
+		rc = OV5640CORE_WRITEPREG(ov5640_brightness_lv7_tbl);
+		break;
+
+	case CAMERA_BRIGHTNESS_LV8:
+		CDBG("--CAMERA--CAMERA_BRIGHTNESS_LV8\n");
+		rc = OV5640CORE_WRITEPREG(ov5640_brightness_lv8_tbl);
+		break;
+
+	default:
+		CDBG("--CAMERA--CAMERA_BRIGHTNESS_ERROR COMMAND\n");
+		break;
+	}
+
+	CDBG("--CAMERA-- %s ...(End)\n", __func__);
+	return rc;
+}
+
+static int ov5640_set_contrast(int contrast)
+{
+	int rc = 0;
+
+	CDBG("--CAMERA-- %s ...(Start)\n", __func__);
+	CDBG("--CAMERA-- %s ...contrast = %d\n", __func__ , contrast);
+
+	if (effect_value == CAMERA_EFFECT_OFF) {
+		switch (contrast) {
+		case CAMERA_CONTRAST_LV0:
+			CDBG("--CAMERA--CAMERA_CONTRAST_LV0\n");
+			rc = OV5640CORE_WRITEPREG(ov5640_contrast_lv0_tbl);
+			break;
+
+		case CAMERA_CONTRAST_LV1:
+			CDBG("--CAMERA--CAMERA_CONTRAST_LV1\n");
+			rc = OV5640CORE_WRITEPREG(ov5640_contrast_lv1_tbl);
+			break;
+
+		case CAMERA_CONTRAST_LV2:
+			CDBG("--CAMERA--CAMERA_CONTRAST_LV2\n");
+			rc = OV5640CORE_WRITEPREG(ov5640_contrast_lv2_tbl);
+			break;
+
+		case CAMERA_CONTRAST_LV3:
+			CDBG("--CAMERA--CAMERA_CONTRAST_LV3\n");
+			rc = OV5640CORE_WRITEPREG(ov5640_contrast_lv3_tbl);
+			break;
+
+		case CAMERA_CONTRAST_LV4:
+			CDBG("--CAMERA--CAMERA_CONTRAST_LV4\n");
+			rc = OV5640CORE_WRITEPREG(
+					ov5640_contrast_default_lv4_tbl);
+			break;
+
+		case CAMERA_CONTRAST_LV5:
+			CDBG("--CAMERA--CAMERA_CONTRAST_LV5\n");
+			rc = OV5640CORE_WRITEPREG(ov5640_contrast_lv5_tbl);
+			break;
+
+		case CAMERA_CONTRAST_LV6:
+			CDBG("--CAMERA--CAMERA_CONTRAST_LV6\n");
+			rc = OV5640CORE_WRITEPREG(ov5640_contrast_lv6_tbl);
+			break;
+
+		case CAMERA_CONTRAST_LV7:
+			CDBG("--CAMERA--CAMERA_CONTRAST_LV7\n");
+			rc = OV5640CORE_WRITEPREG(ov5640_contrast_lv7_tbl);
+			break;
+
+		case CAMERA_CONTRAST_LV8:
+			CDBG("--CAMERA--CAMERA_CONTRAST_LV8\n");
+			rc = OV5640CORE_WRITEPREG(ov5640_contrast_lv8_tbl);
+			break;
+
+		default:
+			CDBG("--CAMERA--CAMERA_CONTRAST_ERROR COMMAND\n");
+			break;
+		}
+	}
+
+	CDBG("--CAMERA-- %s ...(End)\n", __func__);
+	return rc;
+}
+
+static int ov5640_set_sharpness(int sharpness)
+{
+	int rc = 0;
+
+	CDBG("--CAMERA-- %s ...(Start)\n", __func__);
+	CDBG("--CAMERA-- %s ...sharpness = %d\n", __func__ , sharpness);
+
+	if (effect_value == CAMERA_EFFECT_OFF) {
+		switch (sharpness) {
+		case CAMERA_SHARPNESS_LV0:
+			CDBG("--CAMERA--CAMERA_SHARPNESS_LV0\n");
+			rc = OV5640CORE_WRITEPREG(ov5640_sharpness_lv0_tbl);
+			break;
+
+		case CAMERA_SHARPNESS_LV1:
+			CDBG("--CAMERA--CAMERA_SHARPNESS_LV1\n");
+			rc = OV5640CORE_WRITEPREG(ov5640_sharpness_lv1_tbl);
+			break;
+
+		case CAMERA_SHARPNESS_LV2:
+			CDBG("--CAMERA--CAMERA_SHARPNESS_LV2\n");
+			rc = OV5640CORE_WRITEPREG(
+					ov5640_sharpness_default_lv2_tbl);
+			break;
+
+		case CAMERA_SHARPNESS_LV3:
+			CDBG("--CAMERA--CAMERA_SHARPNESS_LV3\n");
+			rc = OV5640CORE_WRITEPREG(ov5640_sharpness_lv3_tbl);
+			break;
+
+		case CAMERA_SHARPNESS_LV4:
+			CDBG("--CAMERA--CAMERA_SHARPNESS_LV4\n");
+			rc = OV5640CORE_WRITEPREG(ov5640_sharpness_lv4_tbl);
+			break;
+
+		case CAMERA_SHARPNESS_LV5:
+			CDBG("--CAMERA--CAMERA_SHARPNESS_LV5\n");
+			rc = OV5640CORE_WRITEPREG(ov5640_sharpness_lv5_tbl);
+			break;
+
+		case CAMERA_SHARPNESS_LV6:
+			CDBG("--CAMERA--CAMERA_SHARPNESS_LV6\n");
+			rc = OV5640CORE_WRITEPREG(ov5640_sharpness_lv6_tbl);
+			break;
+
+		case CAMERA_SHARPNESS_LV7:
+			CDBG("--CAMERA--CAMERA_SHARPNESS_LV7\n");
+			rc = OV5640CORE_WRITEPREG(ov5640_sharpness_lv7_tbl);
+			break;
+
+		case CAMERA_SHARPNESS_LV8:
+			CDBG("--CAMERA--CAMERA_SHARPNESS_LV8\n");
+			rc = OV5640CORE_WRITEPREG(ov5640_sharpness_lv8_tbl);
+			break;
+
+		default:
+			CDBG("--CAMERA--CAMERA_SHARPNESS_ERROR COMMAND\n");
+			break;
+		}
+	}
+
+	CDBG("--CAMERA-- %s ...(End)\n", __func__);
+	return rc;
+}
+
+static int ov5640_set_saturation(int saturation)
+{
+	long rc = 0;
+
+	CDBG("--CAMERA-- %s ...(Start)\n", __func__);
+	CDBG("--CAMERA-- %s ...saturation = %d\n", __func__ , saturation);
+
+	if (effect_value == CAMERA_EFFECT_OFF) {
+		switch (saturation) {
+		case CAMERA_SATURATION_LV0:
+			CDBG("--CAMERA--CAMERA_SATURATION_LV0\n");
+			rc = OV5640CORE_WRITEPREG(ov5640_saturation_lv0_tbl);
+			break;
+
+		case CAMERA_SATURATION_LV1:
+			CDBG("--CAMERA--CAMERA_SATURATION_LV1\n");
+			rc = OV5640CORE_WRITEPREG(ov5640_saturation_lv1_tbl);
+			break;
+
+		case CAMERA_SATURATION_LV2:
+			CDBG("--CAMERA--CAMERA_SATURATION_LV2\n");
+			rc = OV5640CORE_WRITEPREG(ov5640_saturation_lv2_tbl);
+			break;
+
+		case CAMERA_SATURATION_LV3:
+			CDBG("--CAMERA--CAMERA_SATURATION_LV3\n");
+			rc = OV5640CORE_WRITEPREG(ov5640_saturation_lv3_tbl);
+			break;
+
+		case CAMERA_SATURATION_LV4:
+			CDBG("--CAMERA--CAMERA_SATURATION_LV4\n");
+			rc = OV5640CORE_WRITEPREG(
+					ov5640_saturation_default_lv4_tbl);
+			break;
+
+		case CAMERA_SATURATION_LV5:
+			CDBG("--CAMERA--CAMERA_SATURATION_LV5\n");
+			rc = OV5640CORE_WRITEPREG(ov5640_saturation_lv5_tbl);
+			break;
+
+		case CAMERA_SATURATION_LV6:
+			CDBG("--CAMERA--CAMERA_SATURATION_LV6\n");
+			rc = OV5640CORE_WRITEPREG(ov5640_saturation_lv6_tbl);
+			break;
+
+		case CAMERA_SATURATION_LV7:
+			CDBG("--CAMERA--CAMERA_SATURATION_LV7\n");
+			rc = OV5640CORE_WRITEPREG(ov5640_saturation_lv7_tbl);
+			break;
+
+		case CAMERA_SATURATION_LV8:
+			CDBG("--CAMERA--CAMERA_SATURATION_LV8\n");
+			rc = OV5640CORE_WRITEPREG(ov5640_saturation_lv8_tbl);
+			break;
+
+		default:
+			CDBG("--CAMERA--CAMERA_SATURATION_ERROR COMMAND\n");
+			break;
+		}
+	}
+
+	/* for recover saturation level when change special effect */
+	switch (saturation) {
+	case CAMERA_SATURATION_LV0:
+		CDBG("--CAMERA--CAMERA_SATURATION_LV0\n");
+		ov5640_SAT_U = 0x00;
+		ov5640_SAT_V = 0x00;
+		break;
+	case CAMERA_SATURATION_LV1:
+		CDBG("--CAMERA--CAMERA_SATURATION_LV1\n");
+		ov5640_SAT_U = 0x10;
+		ov5640_SAT_V = 0x10;
+		break;
+	case CAMERA_SATURATION_LV2:
+		CDBG("--CAMERA--CAMERA_SATURATION_LV2\n");
+		ov5640_SAT_U = 0x20;
+		ov5640_SAT_V = 0x20;
+		break;
+	case CAMERA_SATURATION_LV3:
+		CDBG("--CAMERA--CAMERA_SATURATION_LV3\n");
+		ov5640_SAT_U = 0x30;
+		ov5640_SAT_V = 0x30;
+		break;
+	case CAMERA_SATURATION_LV4:
+		CDBG("--CAMERA--CAMERA_SATURATION_LV4\n");
+		ov5640_SAT_U = 0x40;
+		ov5640_SAT_V = 0x40;            break;
+	case CAMERA_SATURATION_LV5:
+		CDBG("--CAMERA--CAMERA_SATURATION_LV5\n");
+		ov5640_SAT_U = 0x50;
+		ov5640_SAT_V = 0x50;            break;
+	case CAMERA_SATURATION_LV6:
+		CDBG("--CAMERA--CAMERA_SATURATION_LV6\n");
+		ov5640_SAT_U = 0x60;
+		ov5640_SAT_V = 0x60;
+		break;
+	case CAMERA_SATURATION_LV7:
+		CDBG("--CAMERA--CAMERA_SATURATION_LV7\n");
+		ov5640_SAT_U = 0x70;
+		ov5640_SAT_V = 0x70;            break;
+	case CAMERA_SATURATION_LV8:
+		CDBG("--CAMERA--CAMERA_SATURATION_LV8\n");
+		ov5640_SAT_U = 0x80;
+		ov5640_SAT_V = 0x80;
+		break;
+	default:
+		CDBG("--CAMERA--CAMERA_SATURATION_ERROR COMMAND\n");
+		break;
+	}
+
+	CDBG("--CAMERA-- %s ...(End)\n", __func__);
+	return rc;
+}
+
+static long ov5640_set_antibanding(int antibanding)
+{
+	long rc = 0;
+
+	CDBG("--CAMERA-- %s ...(Start)\n",  __func__);
+	CDBG("--CAMERA-- %s ...antibanding = %d\n",  __func__, antibanding);
+
+	switch (antibanding) {
+	case CAMERA_ANTIBANDING_OFF:
+		CDBG("--CAMERA--CAMERA_ANTIBANDING_OFF\n");
+		break;
+
+	case CAMERA_ANTIBANDING_60HZ:
+		CDBG("--CAMERA--CAMERA_ANTIBANDING_60HZ\n");
+		rc = OV5640CORE_WRITEPREG(ov5640_antibanding_60z_tbl);
+		break;
+
+	case CAMERA_ANTIBANDING_50HZ:
+		CDBG("--CAMERA--CAMERA_ANTIBANDING_50HZ\n");
+		rc = OV5640CORE_WRITEPREG(ov5640_antibanding_50z_tbl);
+		break;
+
+	case CAMERA_ANTIBANDING_AUTO:
+		CDBG("--CAMERA--CAMERA_ANTIBANDING_AUTO\n");
+		rc = OV5640CORE_WRITEPREG(ov5640_antibanding_auto_tbl);
+		break;
+
+	default:
+		CDBG("--CAMERA--CAMERA_ANTIBANDING_ERROR COMMAND\n");
+		break;
+	}
+
+	CDBG("--CAMERA-- %s ...(End)\n", __func__);
+	return rc;
+}
+
+static long ov5640_set_exposure_mode(int mode)
+{
+	long rc = 0;
+	CDBG("--CAMERA-- %s ...(Start)\n", __func__);
+	CDBG("--CAMERA-- %s ...mode = %d\n", __func__ , mode);
+	CDBG("--CAMERA-- %s ...(End)\n", __func__);
+	return rc;
+}
+
+static int32_t ov5640_lens_shading_enable(uint8_t is_enable)
+{
+	int32_t rc = 0;
+	CDBG("--CAMERA--%s: ...(Start). enable = %d\n",  __func__, is_enable);
+
+	if (is_enable) {
+		CDBG("%s: enable~!!\n", __func__);
+		rc = OV5640CORE_WRITEPREG(ov5640_lens_shading_on_tbl);
+	} else {
+		CDBG("%s: disable~!!\n", __func__);
+		rc = OV5640CORE_WRITEPREG(ov5640_lens_shading_off_tbl);
+	}
+	CDBG("--CAMERA--%s: ...(End). rc = %d\n", __func__, rc);
+	return rc;
+}
+
+static int ov5640_set_sensor_mode(int mode, int res)
+{
+	int rc = 0;
+
+	CDBG("--CAMERA-- ov5640_set_sensor_mode mode = %d, res = %d\n",
+			mode, res);
+
+	switch (mode) {
+	case SENSOR_PREVIEW_MODE:
+		CDBG("--CAMERA-- SENSOR_PREVIEW_MODE\n");
+		rc = ov5640_setting(S_UPDATE_PERIODIC, S_RES_PREVIEW);
+		break;
+
+	case SENSOR_SNAPSHOT_MODE:
+		CDBG("--CAMERA-- SENSOR_SNAPSHOT_MODE\n");
+		rc = ov5640_setting(S_UPDATE_PERIODIC, S_RES_CAPTURE);
+		break;
+
+	case SENSOR_RAW_SNAPSHOT_MODE:
+		CDBG("--CAMERA-- SENSOR_RAW_SNAPSHOT_MODE\n");
+		rc = ov5640_setting(S_UPDATE_PERIODIC, S_RES_CAPTURE);
+		break;
+
+	default:
+		CDBG("--CAMERA--ov5640_set_sensor_mode no support\n");
+		rc = -EINVAL;
+		break;
+	}
+
+	return rc;
+}
+
+static int ov5640_set_wb_oem(uint8_t param)
+{
+	int rc = 0;
+	unsigned int tmp2;
+
+	CDBG("[kylin] %s \r\n", __func__);
+
+	ov5640_i2c_read_byte(ov5640_client->addr, 0x350b, &tmp2);
+	CDBG("--CAMERA-- GAIN VALUE : %x\n", tmp2);
+
+	switch (param) {
+	case CAMERA_WB_AUTO:
+
+		CDBG("--CAMERA--CAMERA_WB_AUTO\n");
+		rc = OV5640CORE_WRITEPREG(ov5640_wb_def);
+		break;
+
+	case CAMERA_WB_CUSTOM:
+		CDBG("--CAMERA--CAMERA_WB_CUSTOM\n");
+		rc = OV5640CORE_WRITEPREG(ov5640_wb_custom);
+		break;
+	case CAMERA_WB_INCANDESCENT:
+		CDBG("--CAMERA--CAMERA_WB_INCANDESCENT\n");
+		rc = OV5640CORE_WRITEPREG(ov5640_wb_inc);
+		break;
+	case CAMERA_WB_DAYLIGHT:
+		CDBG("--CAMERA--CAMERA_WB_DAYLIGHT\n");
+		rc = OV5640CORE_WRITEPREG(ov5640_wb_daylight);
+		break;
+	case CAMERA_WB_CLOUDY_DAYLIGHT:
+		CDBG("--CAMERA--CAMERA_WB_CLOUDY_DAYLIGHT\n");
+		rc = OV5640CORE_WRITEPREG(ov5640_wb_cloudy);
+		break;
+	default:
+		break;
+	}
+	return rc;
+}
+
+static int ov5640_set_touchaec(uint32_t x, uint32_t y)
+{
+	uint8_t aec_arr[8] = {0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11};
+	int idx = 0;
+	int i;
+
+	CDBG("[kylin] %s x: %d ,y: %d\r\n", __func__ , x, y);
+	idx = x / 2 + y * 2;
+	CDBG("[kylin] idx: %d\r\n", idx);
+
+	if (x % 2 == 0)
+		aec_arr[idx] = 0x10 | 0x0a;
+	else
+		aec_arr[idx] = 0x01 | 0xa0;
+
+	for (i = 0; i < 8; i++) {
+		CDBG("write : %x val : %x ", 0x5688 + i, aec_arr[i]);
+		ov5640_i2c_write(ov5640_client->addr, 0x5688 + i,
+				aec_arr[i], 10);
+	}
+
+	return 1;
+}
+
+static int ov5640_set_exposure_compensation(int compensation)
+{
+	long rc = 0;
+
+	CDBG("--CAMERA-- %s ...(Start)\n", __func__);
+
+	CDBG("--CAMERA-- %s ...exposure_compensation = %d\n", __func__ ,
+			    compensation);
+
+	switch (compensation) {
+	case CAMERA_EXPOSURE_COMPENSATION_LV0:
+		CDBG("--CAMERA--CAMERA_EXPOSURE_COMPENSATION_LV0\n");
+		rc = OV5640CORE_WRITEPREG(
+				ov5640_exposure_compensation_lv0_tbl);
+		break;
+
+	case CAMERA_EXPOSURE_COMPENSATION_LV1:
+		CDBG("--CAMERA--CAMERA_EXPOSURE_COMPENSATION_LV1\n");
+		rc = OV5640CORE_WRITEPREG(
+				ov5640_exposure_compensation_lv1_tbl);
+		break;
+
+	case CAMERA_EXPOSURE_COMPENSATION_LV2:
+		CDBG("--CAMERA--CAMERA_EXPOSURE_COMPENSATION_LV2\n");
+		rc = OV5640CORE_WRITEPREG(
+			    ov5640_exposure_compensation_lv2_default_tbl);
+		break;
+
+	case CAMERA_EXPOSURE_COMPENSATION_LV3:
+		CDBG("--CAMERA--CAMERA_EXPOSURE_COMPENSATION_LV3\n");
+		rc = OV5640CORE_WRITEPREG(
+				ov5640_exposure_compensation_lv3_tbl);
+		break;
+
+	case CAMERA_EXPOSURE_COMPENSATION_LV4:
+		CDBG("--CAMERA--CAMERA_EXPOSURE_COMPENSATION_LV3\n");
+		rc = OV5640CORE_WRITEPREG(
+				ov5640_exposure_compensation_lv4_tbl);
+		break;
+
+	default:
+		CDBG("--CAMERA--ERROR CAMERA_EXPOSURE_COMPENSATION\n");
+		break;
+	}
+
+	CDBG("--CAMERA-- %s ...(End)\n", __func__);
+
+	return rc;
+}
+
+static int ov5640_sensor_start_af(void)
+{
+	int i;
+	unsigned int af_st = 0;
+	unsigned int af_ack = 0;
+	unsigned int tmp = 0;
+	int rc = 0;
+
+	CDBG("--CAMERA-- %s (Start...)\n", __func__);
+
+	ov5640_i2c_read_byte(ov5640_client->addr,
+			OV5640_CMD_FW_STATUS, &af_st);
+	CDBG("--CAMERA-- %s af_st = %d\n", __func__, af_st);
+
+	ov5640_i2c_write(ov5640_client->addr, OV5640_CMD_ACK, 0x01, 10);
+	ov5640_i2c_write(ov5640_client->addr, OV5640_CMD_MAIN, 0x03, 10);
+
+	for (i = 0; i < 50; i++) {
+		ov5640_i2c_read_byte(ov5640_client->addr,
+				OV5640_CMD_ACK, &af_ack);
+		if (af_ack == 0)
+			break;
+		msleep(50);
+	}
+	CDBG("--CAMERA-- %s af_ack = 0x%x\n", __func__, af_ack);
+
+	ov5640_i2c_read_byte(ov5640_client->addr, OV5640_CMD_FW_STATUS,
+			&af_st);
+	CDBG("--CAMERA-- %s af_st = %d\n", __func__, af_st);
+
+	if (af_st == 0x10) {
+		CDBG("--CAMERA-- %s AF ok and release AF setting~!!\n",
+				__func__);
+	} else {
+		CDBG("--CAMERA-- %s AF not ready!!\n", __func__);
+	}
+
+	ov5640_i2c_write(ov5640_client->addr, OV5640_CMD_ACK, 0x01, 10);
+	ov5640_i2c_write(ov5640_client->addr, OV5640_CMD_MAIN, 0x07, 10);
+
+	for (i = 0; i < 70; i++) {
+		ov5640_i2c_read_byte(ov5640_client->addr, OV5640_CMD_ACK,
+				&af_ack);
+		if (af_ack == 0)
+			break;
+		msleep(25);
+	}
+
+	ov5640_i2c_read_byte(ov5640_client->addr, OV5640_CMD_PARA0, &tmp);
+	CDBG("0x3024 = %x\n", tmp);
+	rc = ((tmp == 0) ? 1 : 0);
+
+	ov5640_i2c_read_byte(ov5640_client->addr, OV5640_CMD_PARA1, &tmp);
+	CDBG("0x3025 = %x\n", tmp);
+	rc = ((tmp == 0) ? 1 : 0);
+
+	ov5640_i2c_read_byte(ov5640_client->addr, OV5640_CMD_PARA2, &tmp);
+	CDBG("0x3026 = %x\n", tmp);
+	rc = ((tmp == 0) ? 1 : 0);
+
+	ov5640_i2c_read_byte(ov5640_client->addr, OV5640_CMD_PARA3, &tmp);
+	CDBG("0x3027 = %x\n", tmp);
+	rc = ((tmp == 0) ? 1 : 0) ;
+
+	ov5640_i2c_read_byte(ov5640_client->addr, OV5640_CMD_PARA4, &tmp);
+	CDBG("0x3028 = %x\n", tmp);
+	rc = ((tmp == 0) ? 1 : 0) ;
+
+	CDBG("--CAMERA-- %s rc = %d(End...)\n", __func__, rc);
+	return rc;
+}
+
+static int ov5640_sensor_config(void __user *argp)
+{
+	struct sensor_cfg_data cdata;
+	long rc = 0;
+
+	if (copy_from_user(&cdata, (void *)argp,
+				sizeof(struct sensor_cfg_data)))
+		return -EFAULT;
+
+	CDBG("--CAMERA-- %s %d\n", __func__, cdata.cfgtype);
+
+	mutex_lock(&ov5640_mutex);
+
+	switch (cdata.cfgtype) {
+	case CFG_SET_MODE:
+		rc = ov5640_set_sensor_mode(cdata.mode, cdata.rs);
+		break;
+
+	case CFG_SET_EFFECT:
+		CDBG("--CAMERA-- CFG_SET_EFFECT mode=%d,"
+				"effect = %d !!\n", cdata.mode,
+				cdata.cfg.effect);
+		rc = ov5640_set_effect(cdata.mode, cdata.cfg.effect);
+		break;
+
+	case CFG_START:
+		CDBG("--CAMERA-- CFG_START (Not Support) !!\n");
+		/* Not Support */
+		break;
+
+	case CFG_PWR_UP:
+		CDBG("--CAMERA-- CFG_PWR_UP (Not Support) !!\n");
+		/* Not Support */
+		break;
+
+	case CFG_PWR_DOWN:
+		CDBG("--CAMERA-- CFG_PWR_DOWN (Not Support)\n");
+		ov5640_power_off();
+		break;
+
+	case CFG_SET_DEFAULT_FOCUS:
+		CDBG("--CAMERA-- CFG_SET_DEFAULT_FOCUS (Not Implement) !!\n");
+		break;
+
+	case CFG_MOVE_FOCUS:
+		CDBG("--CAMERA-- CFG_MOVE_FOCUS (Not Implement) !!\n");
+		break;
+
+	case CFG_SET_BRIGHTNESS:
+		CDBG("--CAMERA-- CFG_SET_BRIGHTNESS  !!\n");
+		rc = ov5640_set_brightness(cdata.cfg.brightness);
+		break;
+
+	case CFG_SET_CONTRAST:
+		CDBG("--CAMERA-- CFG_SET_CONTRAST  !!\n");
+		rc = ov5640_set_contrast(cdata.cfg.contrast);
+		break;
+
+	case CFG_SET_EXPOSURE_MODE:
+		CDBG("--CAMERA-- CFG_SET_EXPOSURE_MODE !!\n");
+		rc = ov5640_set_exposure_mode(cdata.cfg.ae_mode);
+		break;
+
+	case CFG_SET_ANTIBANDING:
+		CDBG("--CAMERA-- CFG_SET_ANTIBANDING antibanding = %d!!\n",
+				cdata.cfg.antibanding);
+		rc = ov5640_set_antibanding(cdata.cfg.antibanding);
+		break;
+
+	case CFG_SET_LENS_SHADING:
+		CDBG("--CAMERA-- CFG_SET_LENS_SHADING !!\n");
+		rc = ov5640_lens_shading_enable(
+				cdata.cfg.lens_shading);
+		break;
+
+	case CFG_SET_SATURATION:
+		CDBG("--CAMERA-- CFG_SET_SATURATION !!\n");
+		rc = ov5640_set_saturation(cdata.cfg.saturation);
+		break;
+
+	case CFG_SET_SHARPNESS:
+		CDBG("--CAMERA-- CFG_SET_SHARPNESS !!\n");
+		rc = ov5640_set_sharpness(cdata.cfg.sharpness);
+		break;
+
+	case CFG_SET_WB:
+		CDBG("--CAMERA-- CFG_SET_WB!!\n");
+		ov5640_set_wb_oem(cdata.cfg.wb_val);
+		rc = 0 ;
+		break;
+
+	case CFG_SET_TOUCHAEC:
+		CDBG("--CAMERA-- CFG_SET_TOUCHAEC!!\n");
+		ov5640_set_touchaec(cdata.cfg.aec_cord.x,
+				cdata.cfg.aec_cord.y);
+		rc = 0 ;
+		break;
+
+	case CFG_SET_AUTO_FOCUS:
+		CDBG("--CAMERA-- CFG_SET_AUTO_FOCUS !\n");
+		rc = ov5640_sensor_start_af();
+		break;
+
+	case CFG_SET_AUTOFLASH:
+		CDBG("--CAMERA-- CFG_SET_AUTOFLASH !\n");
+		is_autoflash = cdata.cfg.is_autoflash;
+		CDBG("[kylin] is autoflash %d\r\n", is_autoflash);
+		rc = 0;
+		break;
+
+	case CFG_SET_EXPOSURE_COMPENSATION:
+		CDBG("--CAMERA-- CFG_SET_EXPOSURE_COMPENSATION !\n");
+		rc = ov5640_set_exposure_compensation(
+				cdata.cfg.exp_compensation);
+		break;
+
+	default:
+		CDBG("%s: Command=%d (Not Implement)!!\n", __func__,
+				cdata.cfgtype);
+		rc = -EINVAL;
+		break;
+	}
+
+	mutex_unlock(&ov5640_mutex);
+	return rc;
+}
+
+static struct i2c_driver ov5640_i2c_driver = {
+	.id_table = ov5640_i2c_id,
+	.probe  = ov5640_i2c_probe,
+	.remove = ov5640_i2c_remove,
+	.driver = {
+		.name = "ov5640",
+	},
+};
+
+static int ov5640_probe_init_gpio(const struct msm_camera_sensor_info *data)
+{
+	int rc = 0;
+
+	CDBG("--CAMERA-- %s\n", __func__);
+
+	ov5640_pwdn_gpio = data->sensor_pwd;
+	ov5640_reset_gpio = data->sensor_reset;
+	ov5640_driver_pwdn_gpio = data->vcm_pwd ;
+
+	if (data->vcm_enable)
+		gpio_direction_output(data->vcm_pwd, 1);
+
+	gpio_direction_output(data->sensor_reset, 1);
+	gpio_direction_output(data->sensor_pwd, 1);
+
+	return rc;
+
+}
+
+static void ov5640_probe_free_gpio(const struct msm_camera_sensor_info *data)
+{
+	gpio_free(ov5640_pwdn_gpio);
+	gpio_free(ov5640_reset_gpio);
+
+	if (data->vcm_enable) {
+		gpio_free(ov5640_driver_pwdn_gpio);
+		ov5640_driver_pwdn_gpio = 0xFF ;
+	}
+
+	ov5640_pwdn_gpio	= 0xFF;
+	ov5640_reset_gpio	= 0xFF;
+}
+
+static int ov5640_sensor_probe(const struct msm_camera_sensor_info *info,
+		struct msm_sensor_ctrl *s)
+{
+	int rc = -ENOTSUPP;
+
+	CDBG("--CAMERA-- %s (Start...)\n", __func__);
+	rc = i2c_add_driver(&ov5640_i2c_driver);
+	CDBG("--CAMERA-- i2c_add_driver ret:0x%x,ov5640_client=0x%x\n",
+			rc, (unsigned int)ov5640_client);
+	if ((rc < 0) || (ov5640_client == NULL)) {
+		CDBG("--CAMERA-- i2c_add_driver FAILS!!\n");
+		return rc;
+	}
+
+	rc = ov5640_probe_init_gpio(info);
+	if (rc < 0)
+		return rc;
+
+	ov5640_power_off();
+
+	/* SENSOR NEED MCLK TO DO I2C COMMUNICTION, OPEN CLK FIRST*/
+	msm_camio_clk_rate_set(24000000);
+
+	msleep(20);
+
+	ov5640_power_on();
+	ov5640_power_reset();
+
+	rc = ov5640_probe_readID(info);
+
+	if (rc < 0) {
+		CDBG("--CAMERA--ov5640_probe_readID Fail !!~~~~!!\n");
+		CDBG("--CAMERA-- %s, unregister\n", __func__);
+		i2c_del_driver(&ov5640_i2c_driver);
+		ov5640_power_off();
+		ov5640_probe_free_gpio(info);
+		return rc;
+	}
+
+	s->s_init		= ov5640_sensor_open_init;
+	s->s_release		= ov5640_sensor_release;
+	s->s_config		= ov5640_sensor_config;
+	s->s_camera_type	= BACK_CAMERA_2D;
+	s->s_mount_angle	= info->sensor_platform_info->mount_angle;
+
+	ov5640_power_off();
+
+	CDBG("--CAMERA-- %s (End...)\n", __func__);
+	return rc;
+}
+
+static int ov5640_i2c_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	CDBG("--CAMERA-- %s ... (Start...)\n", __func__);
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		CDBG("--CAMERA--i2c_check_functionality failed\n");
+		return -ENOMEM;
+	}
+
+	ov5640_sensorw = kzalloc(sizeof(struct ov5640_work), GFP_KERNEL);
+	if (!ov5640_sensorw) {
+		CDBG("--CAMERA--kzalloc failed\n");
+		return -ENOMEM;
+	}
+
+	i2c_set_clientdata(client, ov5640_sensorw);
+	ov5640_init_client(client);
+	ov5640_client = client;
+
+	CDBG("--CAMERA-- %s ... (End...)\n", __func__);
+	return 0;
+}
+
+static int __ov5640_probe(struct platform_device *pdev)
+{
+	return msm_camera_drv_start(pdev, ov5640_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+	.probe	= __ov5640_probe,
+	.driver	= {
+		.name	= "msm_camera_ov5640",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init ov5640_init(void)
+{
+	ov5640_i2c_buf[0] = 0x5A;
+	return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(ov5640_init);
+
+MODULE_DESCRIPTION("OV5640 YUV MIPI sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/ov5640.h b/drivers/media/video/msm/ov5640.h
new file mode 100644
index 0000000..0e65329
--- /dev/null
+++ b/drivers/media/video/msm/ov5640.h
@@ -0,0 +1,2993 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/*
+[SENSOR]
+Sensor Model:   OV5640
+Camera Module:
+Lens Model:
+Driver IC:
+PV Size         = 640 x 480
+Cap Size        = 2592 x 1944
+Output Format   = YUYV
+MCLK Speed      = 24M
+PV DVP_PCLK     = 28M
+Cap DVP_PCLK    = 56M
+PV Frame Rate   = 30fps
+Cap Frame Rate  = 7.5fps
+I2C Slave ID    = 0x78
+I2C Mode        = 16Addr, 8Data
+*/
+
+#ifndef CAMSENSOR_OV5640
+#define CAMSENSOR_OV5640
+
+#define INVMASK(v)  (0xff-v)
+#define OV5640CORE_WRITEPREG(PTBL)	ov5640_writepregs(PTBL,\
+					sizeof(PTBL)/sizeof(PTBL[0]))
+
+/* OV SENSOR SCCB */
+struct ov5640_sensor {
+	uint16_t addr;
+	uint8_t data;
+	uint8_t mask;
+};
+
+/* Auto Focus Command */
+#define OV5640_CMD_MAIN 0x3022
+#define OV5640_CMD_ACK 0x3023
+#define OV5640_CMD_PARA0 0x3024
+#define OV5640_CMD_PARA1 0x3025
+#define OV5640_CMD_PARA2 0x3026
+#define OV5640_CMD_PARA3 0x3027
+#define OV5640_CMD_PARA4 0x3028
+#define OV5640_CMD_FW_STATUS 0x3029
+
+/* Sensor ID */
+#define OV5640_SENSOR_ID 0x5640
+
+#define capture_framerate 750     /* 7.5fps capture frame rate */
+#define g_preview_frameRate 3000  /* 30fps preview frame rate */
+
+struct ov5640_sensor ov5640_init_tbl[] = {
+	{0x3008, 0x42},
+	{0x3103, 0x03},
+	{0x3017, 0x00},
+	{0x3018, 0x00},
+	{0x3034, 0x18},
+	{0x3035, 0x14},
+	{0x3036, 0x38},
+	{0x3037, 0x13},
+	{0x3108, 0x01},
+	{0x3630, 0x36},
+	{0x3631, 0x0e},
+	{0x3632, 0xe2},
+	{0x3633, 0x12},
+	{0x3621, 0xe0},
+	{0x3704, 0xa0},
+	{0x3703, 0x5a},
+	{0x3715, 0x78},
+	{0x3717, 0x01},
+	{0x370b, 0x60},
+	{0x3705, 0x1a},
+	{0x3905, 0x02},
+	{0x3906, 0x10},
+	{0x3901, 0x0a},
+	{0x3731, 0x12},
+	{0x3600, 0x08},
+	{0x3601, 0x33},
+	{0x302d, 0x60},
+	{0x3620, 0x52},
+	{0x371b, 0x20},
+	{0x471c, 0x50},
+	{0x3a13, 0x43},
+	{0x3a18, 0x00},
+	{0x3a19, 0xf8},
+	{0x3635, 0x13},
+	{0x3636, 0x03},
+	{0x3634, 0x40},
+	{0x3622, 0x01},
+	{0x3c01, 0x34},
+	{0x3c04, 0x28},
+	{0x3c05, 0x98},
+	{0x3c06, 0x00},
+	{0x3c07, 0x08},
+	{0x3c08, 0x00},
+	{0x3c09, 0x1c},
+	{0x3c0a, 0x9c},
+	{0x3c0b, 0x40},
+	{0x3820, 0x41},
+	{0x3821, 0x07},
+	{0x3814, 0x31},
+	{0x3815, 0x31},
+	{0x3800, 0x00},
+	{0x3801, 0x00},
+	{0x3802, 0x00},
+	{0x3803, 0x04},
+	{0x3804, 0x0a},
+	{0x3805, 0x3f},
+	{0x3806, 0x07},
+	{0x3807, 0x9b},
+	{0x3808, 0x02},
+	{0x3809, 0x80},
+	{0x380a, 0x01},
+	{0x380b, 0xe0},
+	{0x380c, 0x07},
+	{0x380d, 0x68},
+	{0x380e, 0x03},
+	{0x380f, 0xd8},
+	{0x3810, 0x00},
+	{0x3811, 0x10},
+	{0x3812, 0x00},
+	{0x3813, 0x06},
+	{0x3618, 0x00},
+	{0x3612, 0x29},
+	{0x3708, 0x64},
+	{0x3709, 0x52},
+	{0x370c, 0x03},
+	{0x3a02, 0x03},
+	{0x3a03, 0xd8},
+	{0x3a08, 0x01},
+	{0x3a09, 0x27},
+	{0x3a0a, 0x00},
+	{0x3a0b, 0xf6},
+	{0x3a0e, 0x03},
+	{0x3a0d, 0x04},
+	{0x3a14, 0x03},
+	{0x3a15, 0xd8},
+	{0x4001, 0x02},
+	{0x4004, 0x02},
+	{0x3000, 0x00},
+	{0x3002, 0x1c},
+	{0x3004, 0xff},
+	{0x3006, 0xc3},
+	{0x300e, 0x45},
+	{0x302e, 0x08},
+	{0x4300, 0x30},
+	{0x501f, 0x00},
+	{0x4713, 0x03},
+	{0x4407, 0x04},
+	{0x440e, 0x00},
+	{0x460b, 0x35},
+	{0x460c, 0x22},
+	{0x4837, 0x44},
+	{0x3824, 0x02},
+	{0x5000, 0xa7},
+	{0x5001, 0xa3},
+	{0x5180, 0xff},
+	{0x5181, 0xf2},
+	{0x5182, 0x00},
+	{0x5183, 0x14},
+	{0x5184, 0x25},
+	{0x5185, 0x24},
+	{0x5186, 0x09},
+	{0x5187, 0x09},
+	{0x5188, 0x09},
+	{0x5189, 0x75},
+	{0x518a, 0x54},
+	{0x518b, 0xe0},
+	{0x518c, 0xb2},
+	{0x518d, 0x42},
+	{0x518e, 0x3d},
+	{0x518f, 0x56},
+	{0x5190, 0x46},
+	{0x5191, 0xf8},
+	{0x5192, 0x04},
+	{0x5193, 0x70},
+	{0x5194, 0xf0},
+	{0x5195, 0xf0},
+	{0x5196, 0x03},
+	{0x5197, 0x01},
+	{0x5198, 0x04},
+	{0x5199, 0x12},
+	{0x519a, 0x04},
+	{0x519b, 0x00},
+	{0x519c, 0x06},
+	{0x519d, 0x82},
+	{0x519e, 0x38},
+	{0x5381, 0x1e},
+	{0x5382, 0x5b},
+	{0x5383, 0x08},
+	{0x5384, 0x0a},
+	{0x5385, 0x7e},
+	{0x5386, 0x88},
+	{0x5387, 0x7c},
+	{0x5388, 0x6c},
+	{0x5389, 0x10},
+	{0x538a, 0x01},
+	{0x538b, 0x98},
+	{0x5300, 0x08},
+	{0x5301, 0x30},
+	{0x5302, 0x10},
+	{0x5303, 0x00},
+	{0x5304, 0x08},
+	{0x5305, 0x30},
+	{0x5306, 0x08},
+	{0x5307, 0x16},
+	{0x5309, 0x08},
+	{0x530a, 0x30},
+	{0x530b, 0x04},
+	{0x530c, 0x06},
+	{0x5480, 0x01},
+	{0x5481, 0x08},
+	{0x5482, 0x14},
+	{0x5483, 0x28},
+	{0x5484, 0x51},
+	{0x5485, 0x65},
+	{0x5486, 0x71},
+	{0x5487, 0x7d},
+	{0x5488, 0x87},
+	{0x5489, 0x91},
+	{0x548a, 0x9a},
+	{0x548b, 0xaa},
+	{0x548c, 0xb8},
+	{0x548d, 0xcd},
+	{0x548e, 0xdd},
+	{0x548f, 0xea},
+	{0x5490, 0x1d},
+	{0x5580, 0x02},
+	{0x5583, 0x40},
+	{0x5584, 0x10},
+	{0x5589, 0x10},
+	{0x558a, 0x00},
+	{0x558b, 0xf8},
+	{0x5800, 0x23},
+	{0x5801, 0x14},
+	{0x5802, 0x0f},
+	{0x5803, 0x0f},
+	{0x5804, 0x12},
+	{0x5805, 0x26},
+	{0x5806, 0x0c},
+	{0x5807, 0x08},
+	{0x5808, 0x05},
+	{0x5809, 0x05},
+	{0x580a, 0x08},
+	{0x580b, 0x0d},
+	{0x580c, 0x08},
+	{0x580d, 0x03},
+	{0x580e, 0x00},
+	{0x580f, 0x00},
+	{0x5810, 0x03},
+	{0x5811, 0x09},
+	{0x5812, 0x07},
+	{0x5813, 0x03},
+	{0x5814, 0x00},
+	{0x5815, 0x01},
+	{0x5816, 0x03},
+	{0x5817, 0x08},
+	{0x5818, 0x0d},
+	{0x5819, 0x08},
+	{0x581a, 0x05},
+	{0x581b, 0x06},
+	{0x581c, 0x08},
+	{0x581d, 0x0e},
+	{0x581e, 0x29},
+	{0x581f, 0x17},
+	{0x5820, 0x11},
+	{0x5821, 0x11},
+	{0x5822, 0x15},
+	{0x5823, 0x28},
+	{0x5824, 0x46},
+	{0x5825, 0x26},
+	{0x5826, 0x08},
+	{0x5827, 0x26},
+	{0x5828, 0x64},
+	{0x5829, 0x26},
+	{0x582a, 0x24},
+	{0x582b, 0x22},
+	{0x582c, 0x24},
+	{0x582d, 0x24},
+	{0x582e, 0x06},
+	{0x582f, 0x22},
+	{0x5830, 0x40},
+	{0x5831, 0x42},
+	{0x5832, 0x24},
+	{0x5833, 0x26},
+	{0x5834, 0x24},
+	{0x5835, 0x22},
+	{0x5836, 0x22},
+	{0x5837, 0x26},
+	{0x5838, 0x44},
+	{0x5839, 0x24},
+	{0x583a, 0x26},
+	{0x583b, 0x28},
+	{0x583c, 0x42},
+	{0x583d, 0xce},
+	{0x5025, 0x00},
+	{0x3a0f, 0x30},
+	{0x3a10, 0x28},
+	{0x3a1b, 0x30},
+	{0x3a1e, 0x26},
+	{0x3a11, 0x60},
+	{0x3a1f, 0x14},
+	{0x3008, 0x02},
+};
+
+struct ov5640_sensor ov5640_init_iq_tbl[] = {
+/* Lens correction */
+/* OV5640 LENC setting */
+	{0x5800, 0x3f},
+	{0x5801, 0x20},
+	{0x5802, 0x1a},
+	{0x5803, 0x1a},
+	{0x5804, 0x23},
+	{0x5805, 0x3f},
+	{0x5806, 0x11},
+	{0x5807, 0x0c},
+	{0x5808, 0x09},
+	{0x5809, 0x08},
+	{0x580a, 0x0d},
+	{0x580b, 0x12},
+	{0x580c, 0x0d},
+	{0x580d, 0x04},
+	{0x580e, 0x00},
+	{0x580f, 0x00},
+	{0x5810, 0x05},
+	{0x5811, 0x0d},
+	{0x5812, 0x0d},
+	{0x5813, 0x04},
+	{0x5814, 0x00},
+	{0x5815, 0x00},
+	{0x5816, 0x04},
+	{0x5817, 0x0d},
+	{0x5818, 0x13},
+	{0x5819, 0x0d},
+	{0x581a, 0x08},
+	{0x581b, 0x08},
+	{0x581c, 0x0c},
+	{0x581d, 0x13},
+	{0x581e, 0x3f},
+	{0x581f, 0x1f},
+	{0x5820, 0x1b},
+	{0x5821, 0x1c},
+	{0x5822, 0x23},
+	{0x5823, 0x3f},
+	{0x5824, 0x6a},
+	{0x5825, 0x06},
+	{0x5826, 0x08},
+	{0x5827, 0x06},
+	{0x5828, 0x2a},
+	{0x5829, 0x08},
+	{0x582a, 0x24},
+	{0x582b, 0x24},
+	{0x582c, 0x24},
+	{0x582d, 0x08},
+	{0x582e, 0x08},
+	{0x582f, 0x22},
+	{0x5830, 0x40},
+	{0x5831, 0x22},
+	{0x5832, 0x06},
+	{0x5833, 0x08},
+	{0x5834, 0x24},
+	{0x5835, 0x24},
+	{0x5836, 0x04},
+	{0x5837, 0x0a},
+	{0x5838, 0x86},
+	{0x5839, 0x08},
+	{0x583a, 0x28},
+	{0x583b, 0x28},
+	{0x583c, 0x66},
+	{0x583d, 0xce},
+/* AEC */
+	{0x3a0f, 0x38},
+	{0x3a10, 0x30},
+	{0x3a11, 0x61},
+	{0x3a1b, 0x38},
+	{0x3a1e, 0x30},
+	{0x3a1f, 0x10},
+	/* AWB */
+	{0x5180, 0xff},
+	{0x5181, 0xf2},
+	{0x5182, 0x00},
+	{0x5183, 0x14},
+	{0x5184, 0x25},
+	{0x5185, 0x24},
+	{0x5186, 0x09},
+	{0x5187, 0x09},
+	{0x5188, 0x09},
+	{0x5189, 0x88},
+	{0x518a, 0x54},
+	{0x518b, 0xee},
+	{0x518c, 0xb2},
+	{0x518d, 0x50},
+	{0x518e, 0x34},
+	{0x518f, 0x6b},
+	{0x5190, 0x46},
+	{0x5191, 0xf8},
+	{0x5192, 0x04},
+	{0x5193, 0x70},
+	{0x5194, 0xf0},
+	{0x5195, 0xf0},
+	{0x5196, 0x03},
+	{0x5197, 0x01},
+	{0x5198, 0x04},
+	{0x5199, 0x6c},
+	{0x519a, 0x04},
+	{0x519b, 0x00},
+	{0x519c, 0x09},
+	{0x519d, 0x2b},
+	{0x519e, 0x38},
+
+/* UV Adjust Auto Mode */
+	{0x5580, 0x02},	/* 02 ;Sat enable */
+	{0x5588, 0x01},	/*40 ;enable UV adj */
+	{0x5583, 0x40},	/*	;offset high */
+	{0x5584, 0x18},	/*	;offset low */
+	{0x5589, 0x18},	/*	;gth1	*/
+	{0x558a, 0x00},
+	{0x358b, 0xf8},	/*	;gth2 */
+};
+
+struct ov5640_sensor ov5640_preview_tbl[] = {
+/* @@ MIPI_2lane_5M to vga(YUV) 30fps 99 640 480 98 0 0 */
+	{0x3503, 0x00}, /* enable AE back from capture to preview */
+	{0x3035, 0x14},
+	{0x3036, 0x38},
+	{0x3820, 0x41},
+	{0x3821, 0x07},
+	{0x3814, 0x31},
+	{0x3815, 0x31},
+	{0x3803, 0x04},
+	{0x3807, 0x9b},
+	{0x3808, 0x02},
+	{0x3809, 0x80},
+	{0x380a, 0x01},
+	{0x380b, 0xe0},
+	{0x380c, 0x07},
+	{0x380d, 0x68},
+	{0x380e, 0x03},
+	{0x380f, 0xd8},
+	{0x3813, 0x06},
+	{0x3618, 0x00},
+	{0x3612, 0x29},
+	{0x3708, 0x64},
+	{0x3709, 0x52},
+	{0x370c, 0x03},
+	{0x5001, 0xa3},
+	{0x4004, 0x02},
+	{0x4005, 0x18},
+	{0x4837, 0x44},
+	{0x4713, 0x03},
+	{0x4407, 0x04},
+	{0x460b, 0x35},
+	{0x460c, 0x22},
+	{0x3824, 0x02},
+};
+
+struct ov5640_sensor ov5640_capture_tbl[] = {
+/* @@ MIPI_2lane_5M(YUV) 7.5/15fps 99 2592 1944 98 0 0 */
+	{0x3035, 0x21}, /* 11 */
+	{0x3036, 0x54},
+	{0x3820, 0x40},
+	{0x3821, 0x06},
+	{0x3814, 0x11},
+	{0x3815, 0x11},
+	{0x3803, 0x00},
+	{0x3807, 0x9f},
+	{0x3808, 0x0a},
+	{0x3809, 0x20},
+	{0x380a, 0x07},
+	{0x380b, 0x98},
+	{0x380c, 0x0b},
+	{0x380d, 0x1c},
+	{0x380e, 0x07},
+	{0x380f, 0xb0},
+	{0x3813, 0x04},
+	{0x3618, 0x04},
+	{0x3612, 0x2b},
+	{0x3708, 0x21},
+	{0x3709, 0x12},
+	{0x370c, 0x00},
+	{0x5001, 0x83},
+	{0x4004, 0x06},
+	{0x4005, 0x1a},
+	{0x4837, 0x15}, /* 0a */
+	{0x4713, 0x02},
+	{0x4407, 0x0c},
+	{0x460b, 0x37},
+	{0x460c, 0x20},
+	{0x3824, 0x01},
+};
+
+/* Contrast */
+
+struct ov5640_sensor ov5640_contrast_lv0_tbl[] = {
+/* Contrast -4 */
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5580, 0x04, INVMASK(0x04)}, /* Enable BIT2 for contrast/brightness
+					  control*/
+	{0x5586, 0x10},                /* Gain */
+	{0x5585, 0x10},                /* Offset */
+	{0x5588, 0x00, INVMASK(0x04)}, /* Offset sign */
+};
+
+struct ov5640_sensor ov5640_contrast_lv1_tbl[] = {
+/* Contrast -3 */
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5580, 0x04, INVMASK(0x04)}, /* Enable BIT2 for contrast/brightness
+					  control */
+	{0x5586, 0x14},                /* Gain */
+	{0x5585, 0x14},                /* Offset */
+	{0x5588, 0x00, INVMASK(0x04)}, /* Offset sign */
+};
+
+struct ov5640_sensor ov5640_contrast_lv2_tbl[] = {
+/* Contrast -2 */
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5580, 0x04, INVMASK(0x04)}, /* Enable BIT2 for contrast/brightness
+					  control */
+	{0x5586, 0x18},                /* Gain */
+	{0x5585, 0x18},                /* Offset */
+	{0x5588, 0x00, INVMASK(0x04)}, /* Offset sign */
+};
+
+struct ov5640_sensor ov5640_contrast_lv3_tbl[] = {
+/* Contrast -1 */
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5580, 0x04, INVMASK(0x04)},
+	{0x5586, 0x1c},
+	{0x5585, 0x1c},
+	{0x5588, 0x00, INVMASK(0x04)},
+};
+
+struct ov5640_sensor ov5640_contrast_default_lv4_tbl[] = {
+/* Contrast (Default) */
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5580, 0x04, INVMASK(0x04)},
+	{0x5586, 0x20},
+	{0x5585, 0x00},
+	{0x5588, 0x00, INVMASK(0x04)},
+};
+
+struct ov5640_sensor ov5640_contrast_lv5_tbl[] = {
+/* Contrast +1 */
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5580, 0x04, INVMASK(0x04)},
+	{0x5586, 0x24},
+	{0x5585, 0x10},
+	{0x5588, 0x00, INVMASK(0x04)},
+};
+
+struct ov5640_sensor ov5640_contrast_lv6_tbl[] = {
+/* Contrast +2 */
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5580, 0x04, INVMASK(0x04)},
+	{0x5586, 0x28},
+	{0x5585, 0x18},
+	{0x5588, 0x00, INVMASK(0x04)},
+};
+
+struct ov5640_sensor ov5640_contrast_lv7_tbl[] = {
+/* Contrast +3 */
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5580, 0x04, INVMASK(0x04)},
+	{0x5586, 0x2c},
+	{0x5585, 0x1c},
+	{0x5588, 0x00, INVMASK(0x04)},
+};
+
+struct ov5640_sensor ov5640_contrast_lv8_tbl[] = {
+/* Contrast +4 */
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5580, 0x04, INVMASK(0x04)},
+	{0x5586, 0x30},
+	{0x5585, 0x20},
+	{0x5588, 0x00, INVMASK(0x04)},
+};
+
+/* Sharpness */
+
+struct ov5640_sensor ov5640_sharpness_lv0_tbl[] = {
+/* Sharpness 0 */
+	{0x5308, 0x40, INVMASK(0x40)},
+	{0x5302, 0x00},
+};
+
+struct ov5640_sensor ov5640_sharpness_lv1_tbl[] = {
+/* Sharpness 1 */
+	{0x5308, 0x40, INVMASK(0x40)},
+	{0x5302, 0x02},
+};
+
+struct ov5640_sensor ov5640_sharpness_default_lv2_tbl[] = {
+/* Sharpness_Auto (Default) */
+	{0x5308, 0x00, INVMASK(0x40)},
+	{0x5300, 0x08},
+	{0x5301, 0x30},
+	{0x5302, 0x10},
+	{0x5303, 0x00},
+	{0x5309, 0x08},
+	{0x530a, 0x30},
+	{0x530b, 0x04},
+	{0x530c, 0x06},
+};
+
+struct ov5640_sensor ov5640_sharpness_lv3_tbl[] = {
+/* Sharpness 3 */
+	{0x5308, 0x40, INVMASK(0x40)},
+	{0x5302, 0x08},
+};
+
+struct ov5640_sensor ov5640_sharpness_lv4_tbl[] = {
+/* Sharpness 4 */
+	{0x5308, 0x40, INVMASK(0x40)},
+	{0x5302, 0x0c},
+};
+
+struct ov5640_sensor ov5640_sharpness_lv5_tbl[] = {
+/* Sharpness 5 */
+	{0x5308, 0x40, INVMASK(0x40)},
+	{0x5302, 0x10},
+};
+
+struct ov5640_sensor ov5640_sharpness_lv6_tbl[] = {
+/* Sharpness 6 */
+	{0x5308, 0x40, INVMASK(0x40)},
+	{0x5302, 0x14},
+};
+
+struct ov5640_sensor ov5640_sharpness_lv7_tbl[] = {
+/* Sharpness 7 */
+	{0x5308, 0x40, INVMASK(0x40)},
+	{0x5302, 0x18},
+};
+
+struct ov5640_sensor ov5640_sharpness_lv8_tbl[] = {
+/* Sharpness 8 */
+	{0x5308, 0x40, INVMASK(0x40)},
+	{0x5302, 0x20},
+};
+
+/* Saturation */
+
+struct ov5640_sensor ov5640_saturation_lv0_tbl[] = {
+/* Saturation x0.25 */
+	{0x5001, 0x83, INVMASK(0x80)},  /* SDE_En */
+	{0x5583, 0x00},                 /* Saturaion gain in U */
+	{0x5584, 0x00},                 /* Saturation gain in V */
+	{0x5580, 0x02, INVMASK(0x02)},  /* Saturation enable */
+	{0x5588, 0x40, INVMASK(0x40)},
+};
+
+struct ov5640_sensor ov5640_saturation_lv1_tbl[] = {
+/* Saturation x0.5 */
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5583, 0x10},
+	{0x5584, 0x10},
+	{0x5580, 0x02, INVMASK(0x02)},
+	{0x5588, 0x40, INVMASK(0x40)},
+};
+
+struct ov5640_sensor ov5640_saturation_lv2_tbl[] = {
+/* Saturation x0.75 */
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5583, 0x20},
+	{0x5584, 0x20},
+	{0x5580, 0x02, INVMASK(0x02)},
+	{0x5588, 0x40, INVMASK(0x40)},
+};
+
+struct ov5640_sensor ov5640_saturation_lv3_tbl[] = {
+/* Saturation x0.75 */
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5583, 0x30},
+	{0x5584, 0x30},
+	{0x5580, 0x02, INVMASK(0x02)},
+	{0x5588, 0x40, INVMASK(0x40)},
+};
+
+struct ov5640_sensor ov5640_saturation_default_lv4_tbl[] = {
+/* Saturation x1 (Default) */
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5583, 0x40},
+	{0x5584, 0x40},
+	{0x5580, 0x02, INVMASK(0x02)},
+	{0x5588, 0x40, INVMASK(0x40)},
+};
+
+struct ov5640_sensor ov5640_saturation_lv5_tbl[] = {
+/* Saturation x1.25 */
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5583, 0x50},
+	{0x5584, 0x50},
+	{0x5580, 0x02, INVMASK(0x02)},
+	{0x5588, 0x40, INVMASK(0x40)},
+};
+
+struct ov5640_sensor ov5640_saturation_lv6_tbl[] = {
+/* Saturation x1.5 */
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5583, 0x60},
+	{0x5584, 0x60},
+	{0x5580, 0x02, INVMASK(0x02)},
+	{0x5588, 0x40, INVMASK(0x40)},
+};
+
+struct ov5640_sensor ov5640_saturation_lv7_tbl[] = {
+/* Saturation x1.25 */
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5583, 0x70},
+	{0x5584, 0x70},
+	{0x5580, 0x02, INVMASK(0x02)},
+	{0x5588, 0x40, INVMASK(0x40)},
+};
+
+struct ov5640_sensor ov5640_saturation_lv8_tbl[] = {
+/* Saturation x1.5 */
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5583, 0x80},
+	{0x5584, 0x80},
+	{0x5580, 0x02, INVMASK(0x02)},
+	{0x5588, 0x40, INVMASK(0x40)},
+};
+
+/* Brightness */
+
+struct ov5640_sensor ov5640_brightness_lv0_tbl[] = {
+/* Brightness -4 */
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5587, 0x40},
+	{0x5580, 0x04, INVMASK(0x04)},
+	{0x5588, 0x08, INVMASK(0x08)},
+};
+
+struct ov5640_sensor ov5640_brightness_lv1_tbl[] = {
+/* Brightness -3 */
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5587, 0x30},
+	{0x5580, 0x04, INVMASK(0x04)},
+	{0x5588, 0x08, INVMASK(0x08)},
+};
+
+struct ov5640_sensor ov5640_brightness_lv2_tbl[] = {
+/* Brightness -2 */
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5587, 0x20},
+	{0x5580, 0x04, INVMASK(0x04)},
+	{0x5588, 0x08, INVMASK(0x08)},
+};
+
+struct ov5640_sensor ov5640_brightness_lv3_tbl[] = {
+/* Brightness -1 */
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5587, 0x10},
+	{0x5580, 0x04, INVMASK(0x04)},
+	{0x5588, 0x08, INVMASK(0x08)},
+};
+
+struct ov5640_sensor ov5640_brightness_default_lv4_tbl[] = {
+/* Brightness 0 (Default) */
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5587, 0x00},
+	{0x5580, 0x04, INVMASK(0x04)},
+	{0x5588, 0x00, INVMASK(0x08)},
+};
+
+struct ov5640_sensor ov5640_brightness_lv5_tbl[] = {
+/* Brightness +1 */
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5587, 0x10},
+	{0x5580, 0x04, INVMASK(0x04)},
+	{0x5588, 0x00, INVMASK(0x08)},
+};
+
+struct ov5640_sensor ov5640_brightness_lv6_tbl[] = {
+/* Brightness +2 */
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5587, 0x20},
+	{0x5580, 0x04, INVMASK(0x04)},
+	{0x5588, 0x00, INVMASK(0x08)},
+};
+
+struct ov5640_sensor ov5640_brightness_lv7_tbl[] = {
+/* Brightness +3 */
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5587, 0x30},
+	{0x5580, 0x04, INVMASK(0x04)},
+	{0x5588, 0x00, INVMASK(0x08)},
+};
+
+struct ov5640_sensor ov5640_brightness_lv8_tbl[] = {
+/* Brightness +4 */
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5587, 0x40},
+	{0x5580, 0x04, INVMASK(0x04)},
+	{0x5588, 0x00, INVMASK(0x08)},
+};
+
+/* Exposure Compensation */
+struct ov5640_sensor ov5640_exposure_compensation_lv0_tbl[] = {
+	/* @@ +1.7EV */
+	{0x3a0f, 0x60},
+	{0x3a10, 0x58},
+	{0x3a11, 0xa0},
+	{0x3a1b, 0x60},
+	{0x3a1e, 0x58},
+	{0x3a1f, 0x20},
+};
+
+struct ov5640_sensor ov5640_exposure_compensation_lv1_tbl[] = {
+	/* @@ +1.0EV */
+	{0x3a0f, 0x50},
+	{0x3a10, 0x48},
+	{0x3a11, 0x90},
+	{0x3a1b, 0x50},
+	{0x3a1e, 0x48},
+	{0x3a1f, 0x20},
+};
+
+struct ov5640_sensor ov5640_exposure_compensation_lv2_default_tbl[] = {
+	/* @@ default */
+	{0x3a0f, 0x38},
+	{0x3a10, 0x30},
+	{0x3a11, 0x61},
+	{0x3a1b, 0x38},
+	{0x3a1e, 0x30},
+	{0x3a1f, 0x10},
+};
+
+struct ov5640_sensor ov5640_exposure_compensation_lv3_tbl[] = {
+	/* @@ -1.0EV */
+	{0x3a0f, 0x20},
+	{0x3a10, 0x18},
+	{0x3a11, 0x41},
+	{0x3a1b, 0x20},
+	{0x3a1e, 0x18},
+	{0x3a1f, 0x10},
+};
+
+struct ov5640_sensor ov5640_exposure_compensation_lv4_tbl[] = {
+	/* @@ -1.7EV */
+	{0x3a0f, 0x10},
+	{0x3a10, 0x08},
+	{0x3a11, 0x10},
+	{0x3a1b, 0x08},
+	{0x3a1e, 0x20},
+	{0x3a1f, 0x10},
+};
+
+/* Auto Expourse Weight */
+
+struct ov5640_sensor ov5640_ae_average_tbl[] = {
+  /* Whole Image Average */
+	{0x5688, 0x11}, /* Zone 1/Zone 0 weight */
+	{0x5689, 0x11}, /* Zone 3/Zone 2 weight */
+	{0x569a, 0x11}, /* Zone 5/Zone 4 weight */
+	{0x569b, 0x11}, /* Zone 7/Zone 6 weight */
+	{0x569c, 0x11}, /* Zone 9/Zone 8 weight */
+	{0x569d, 0x11}, /* Zone b/Zone a weight */
+	{0x569e, 0x11}, /* Zone d/Zone c weight */
+	{0x569f, 0x11}, /* Zone f/Zone e weight */
+};
+
+struct ov5640_sensor ov5640_ae_centerweight_tbl[] = {
+  /* Whole Image Center More weight */
+	{0x5688, 0x62},
+	{0x5689, 0x26},
+	{0x568a, 0xe6},
+	{0x568b, 0x6e},
+	{0x568c, 0xea},
+	{0x568d, 0xae},
+	{0x568e, 0xa6},
+	{0x568f, 0x6a},
+};
+
+/* Light Mode */
+struct ov5640_sensor ov5640_wb_def[] = {
+	{0x3406, 0x00, INVMASK(0x01)},
+};
+
+struct ov5640_sensor ov5640_wb_custom[] = {
+	{0x3406, 0x01, INVMASK(0x01)},
+	{0x3400, 0x04},
+	{0x3401, 0x58},
+	{0x3402, 0x04},
+	{0x3403, 0x00},
+	{0x3404, 0x08},
+	{0x3405, 0x40},
+};
+
+struct ov5640_sensor ov5640_wb_inc[] = {
+	{0x3406, 0x01, INVMASK(0x01)},
+	{0x3400, 0x04},
+	{0x3401, 0x88},
+	{0x3402, 0x04},
+	{0x3403, 0x00},
+	{0x3404, 0x08},
+	{0x3405, 0xb6},
+};
+
+struct ov5640_sensor ov5640_wb_daylight[] = {
+	{0x3406, 0x01, INVMASK(0x01)},
+	{0x3400, 0x07},
+	{0x3401, 0x02},
+	{0x3402, 0x04},
+	{0x3403, 0x00},
+	{0x3404, 0x05},
+	{0x3405, 0x15},
+};
+
+struct ov5640_sensor ov5640_wb_cloudy[] = {
+	{0x3406, 0x01, INVMASK(0x01)},
+	{0x3400, 0x07},
+	{0x3401, 0x88},
+	{0x3402, 0x04},
+	{0x3403, 0x00},
+	{0x3404, 0x05},
+	{0x3405, 0x00},
+};
+
+/* EFFECT */
+struct ov5640_sensor ov5640_effect_normal_tbl[] = {
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5580, 0x00, INVMASK(0x78)},
+	{0x5003, 0x08},
+	{0x5583, 0x40},
+	{0x5584, 0x40},
+};
+
+struct ov5640_sensor ov5640_effect_mono_tbl[] = {
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5580, 0x20, INVMASK(0x78)},
+	{0x5003, 0x08},
+	{0x5583, 0x40},
+	{0x5584, 0x40},
+};
+
+struct ov5640_sensor ov5640_effect_bw_tbl[] = {
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5580, 0x18, INVMASK(0x78)},
+	{0x5003, 0x08},
+	{0x5583, 0x80},
+	{0x5584, 0x80},
+};
+
+struct ov5640_sensor ov5640_effect_bluish_tbl[] = {
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5580, 0x18, INVMASK(0x78)},
+	{0x5003, 0x08},
+	{0x5583, 0xa0},
+	{0x5584, 0x40},
+};
+
+struct ov5640_sensor ov5640_effect_solarize_tbl[] = {
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5580, 0x00, INVMASK(0x78)},
+	{0x5003, 0x09},
+};
+
+
+struct ov5640_sensor ov5640_effect_sepia_tbl[] = {
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5580, 0x18, INVMASK(0x78)},
+	{0x5003, 0x08},
+	{0x5583, 0x40},
+	{0x5584, 0xa0},
+};
+
+struct ov5640_sensor ov5640_effect_reddish_tbl[] = {
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5580, 0x18, INVMASK(0x78)},
+	{0x5003, 0x08},
+	{0x5583, 0x80},
+	{0x5584, 0xc0},
+};
+
+struct ov5640_sensor ov5640_effect_greenish_tbl[] = {
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5580, 0x18, INVMASK(0x78)},
+	{0x5003, 0x08},
+	{0x5583, 0x60},
+	{0x5584, 0x60},
+};
+
+struct ov5640_sensor ov5640_effect_negative_tbl[] = {
+	{0x5001, 0x83, INVMASK(0x80)},
+	{0x5580, 0x40, INVMASK(0x78)},
+	{0x5003, 0x08},
+};
+
+/* AntiBanding */
+struct ov5640_sensor ov5640_antibanding_auto_tbl[] = {
+  /* Auto-XCLK24MHz */
+	{0x3622, 0x01}, /* PD-sel */
+	{0x3635, 0x1c}, /* VMREF 3635[2:0] */
+	{0x3634, 0x40}, /* I_5060 3643[2:0] */
+	{0x3c01, 0x34},
+	{0x3c00, 0x00},
+	{0x3c04, 0x28},
+	{0x3c05, 0x98},
+	{0x3c06, 0x00},
+	{0x3c07, 0x08},
+	{0x3c08, 0x00},
+	{0x3c09, 0x1c},
+	{0x300c, 0x22}, /* 50/60div 300c[2:0] */
+	{0x3c0a, 0x9c},
+	{0x3c0b, 0x40},
+};
+
+struct ov5640_sensor ov5640_antibanding_50z_tbl[] = {
+  /* Band 50Hz */
+	{0x3c01, 0x80, INVMASK(0x80)},
+	{0x3c00, 0x04},
+};
+
+struct ov5640_sensor ov5640_antibanding_60z_tbl[] = {
+  /* Band 60Hz */
+	{0x3c01, 0x80, INVMASK(0x80)},
+	{0x3c00, 0x00},
+};
+
+
+/* Lens_shading */
+
+struct ov5640_sensor ov5640_lens_shading_on_tbl[] = {
+	/* @@ Lenc On(C) */
+	{0x5000, 0x80, INVMASK(0x80)},
+};
+
+struct ov5640_sensor ov5640_lens_shading_off_tbl[] = {
+	/*  Lenc Off */
+	{0x5000, 0x00, INVMASK(0x80)},
+};
+
+/* Auto Focus Firmware-use 2011-08-24 firmware settings */
+u8 ov5640_afinit_tbl[] = {
+	0x80,	0x00,	0x02,	0x0b,	0x7b,	0x02,	0x07,	0xbd,	0xc2,
+	0x01,	0x22,	0x22,	0x00,	0x02,	0x0b,	0x57,	0xe5,	0x1f,
+	0x70,	0x72,	0xf5,	0x1e,	0xd2,	0x35,	0xff,	0xef,	0x25,
+	0xe0,	0x24,	0x4b,	0xf8,	0xe4,	0xf6,	0x08,	0xf6,	0x0f,
+	0xbf,	0x34,	0xf2,	0x90,	0x0e,	0x88,	0xe4,	0x93,	0xff,
+	0xe5,	0x49,	0xc3,	0x9f,	0x50,	0x04,	0x7f,	0x05,	0x80,
+	0x02,	0x7f,	0xfb,	0x78,	0xba,	0xa6,	0x07,	0x12,	0x0a,
+	0xb4,	0x40,	0x04,	0x7f,	0x03,	0x80,	0x02,	0x7f,	0x30,
+	0x78,	0xb9,	0xa6,	0x07,	0xe6,	0x18,	0xf6,	0x08,	0xe6,
+	0x78,	0xb6,	0xf6,	0x78,	0xb9,	0xe6,	0x78,	0xb7,	0xf6,
+	0x78,	0xbc,	0x76,	0x33,	0xe4,	0x08,	0xf6,	0x78,	0xb5,
+	0x76,	0x01,	0x75,	0x48,	0x02,	0x78,	0xb3,	0xf6,	0x08,
+	0xf6,	0x74,	0xff,	0x78,	0xbe,	0xf6,	0x08,	0xf6,	0x75,
+	0x1f,	0x01,	0x78,	0xb9,	0xe6,	0x75,	0xf0,	0x05,	0xa4,
+	0xf5,	0x49,	0x12,	0x08,	0x5b,	0xc2,	0x37,	0x22,	0x78,
+	0xb5,	0xe6,	0xd3,	0x94,	0x00,	0x40,	0x02,	0x16,	0x22,
+	0xe5,	0x1f,	0x64,	0x05,	0x70,	0x28,	0xf5,	0x1f,	0xc2,
+	0x01,	0x78,	0xb6,	0xe6,	0x25,	0xe0,	0x24,	0x4b,	0xf8,
+	0xe6,	0xfe,	0x08,	0xe6,	0xff,	0x78,	0x4b,	0xa6,	0x06,
+	0x08,	0xa6,	0x07,	0xa2,	0x37,	0xe4,	0x33,	0xf5,	0x3c,
+	0x90,	0x30,	0x28,	0xf0,	0x75,	0x1e,	0x10,	0xd2,	0x35,
+	0x22,	0xe5,	0x49,	0x75,	0xf0,	0x05,	0x84,	0x78,	0xb9,
+	0xf6,	0x90,	0x0e,	0x85,	0xe4,	0x93,	0xff,	0x25,	0xe0,
+	0x24,	0x0a,	0xf8,	0xe6,	0xfc,	0x08,	0xe6,	0xfd,	0x78,
+	0xb9,	0xe6,	0x25,	0xe0,	0x24,	0x4b,	0xf8,	0xa6,	0x04,
+	0x08,	0xa6,	0x05,	0xef,	0x12,	0x0a,	0xbb,	0xd3,	0x78,
+	0xb4,	0x96,	0xee,	0x18,	0x96,	0x40,	0x0d,	0x78,	0xb9,
+	0xe6,	0x78,	0xb6,	0xf6,	0x78,	0xb3,	0xa6,	0x06,	0x08,
+	0xa6,	0x07,	0x90,	0x0e,	0x85,	0xe4,	0x93,	0x12,	0x0a,
+	0xbb,	0xc3,	0x78,	0xbf,	0x96,	0xee,	0x18,	0x96,	0x50,
+	0x0d,	0x78,	0xb9,	0xe6,	0x78,	0xb7,	0xf6,	0x78,	0xbe,
+	0xa6,	0x06,	0x08,	0xa6,	0x07,	0x78,	0xb3,	0xe6,	0xfe,
+	0x08,	0xe6,	0xc3,	0x78,	0xbf,	0x96,	0xff,	0xee,	0x18,
+	0x96,	0x78,	0xc0,	0xf6,	0x08,	0xa6,	0x07,	0x90,	0x0e,
+	0x8a,	0xe4,	0x18,	0x12,	0x0a,	0x99,	0xc3,	0x33,	0xce,
+	0x33,	0xce,	0xd8,	0xf9,	0xff,	0xd3,	0xed,	0x9f,	0xec,
+	0x9e,	0x40,	0x02,	0xd2,	0x37,	0x78,	0xb9,	0xe6,	0x08,
+	0x26,	0x08,	0xf6,	0xe5,	0x1f,	0x64,	0x01,	0x70,	0x55,
+	0xe6,	0xc3,	0x78,	0xbd,	0x12,	0x0a,	0x8f,	0x40,	0x10,
+	0x12,	0x0a,	0x8a,	0x50,	0x0b,	0x30,	0x37,	0x41,	0x78,
+	0xb9,	0xe6,	0x78,	0xb6,	0x66,	0x60,	0x39,	0x12,	0x0a,
+	0xb2,	0x40,	0x04,	0x7f,	0xfe,	0x80,	0x02,	0x7f,	0x02,
+	0x78,	0xba,	0xa6,	0x07,	0x78,	0xb6,	0xe6,	0x24,	0x03,
+	0x78,	0xbc,	0xf6,	0x78,	0xb6,	0xe6,	0x24,	0xfd,	0x78,
+	0xbd,	0xf6,	0x12,	0x0a,	0xb2,	0x40,	0x06,	0x78,	0xbd,
+	0xe6,	0xff,	0x80,	0x04,	0x78,	0xbc,	0xe6,	0xff,	0x78,
+	0xbb,	0xa6,	0x07,	0x75,	0x1f,	0x02,	0x78,	0xb5,	0x76,
+	0x01,	0x02,	0x02,	0x68,	0xe5,	0x1f,	0x64,	0x02,	0x60,
+	0x03,	0x02,	0x02,	0x48,	0x78,	0xbb,	0xe6,	0xff,	0xc3,
+	0x78,	0xbd,	0x12,	0x0a,	0x90,	0x40,	0x08,	0x12,	0x0a,
+	0x8a,	0x50,	0x03,	0x02,	0x02,	0x46,	0x12,	0x0a,	0xb2,
+	0x40,	0x04,	0x7f,	0xff,	0x80,	0x02,	0x7f,	0x01,	0x78,
+	0xba,	0xa6,	0x07,	0x78,	0xb6,	0xe6,	0x04,	0x78,	0xbc,
+	0xf6,	0x78,	0xb6,	0xe6,	0x14,	0x78,	0xbd,	0xf6,	0x18,
+	0x12,	0x0a,	0xb4,	0x40,	0x04,	0xe6,	0xff,	0x80,	0x02,
+	0x7f,	0x00,	0x78,	0xbc,	0xa6,	0x07,	0xd3,	0x08,	0xe6,
+	0x64,	0x80,	0x94,	0x80,	0x40,	0x04,	0xe6,	0xff,	0x80,
+	0x02,	0x7f,	0x00,	0x78,	0xbd,	0xa6,	0x07,	0xc3,	0x18,
+	0xe6,	0x64,	0x80,	0x94,	0xb3,	0x50,	0x04,	0xe6,	0xff,
+	0x80,	0x02,	0x7f,	0x33,	0x78,	0xbc,	0xa6,	0x07,	0xc3,
+	0x08,	0xe6,	0x64,	0x80,	0x94,	0xb3,	0x50,	0x04,	0xe6,
+	0xff,	0x80,	0x02,	0x7f,	0x33,	0x78,	0xbd,	0xa6,	0x07,
+	0x12,	0x0a,	0xb2,	0x40,	0x06,	0x78,	0xbd,	0xe6,	0xff,
+	0x80,	0x04,	0x78,	0xbc,	0xe6,	0xff,	0x78,	0xbb,	0xa6,
+	0x07,	0x75,	0x1f,	0x03,	0x78,	0xb5,	0x76,	0x01,	0x80,
+	0x20,	0xe5,	0x1f,	0x64,	0x03,	0x70,	0x26,	0x78,	0xbb,
+	0xe6,	0xff,	0xc3,	0x78,	0xbd,	0x12,	0x0a,	0x90,	0x40,
+	0x05,	0x12,	0x0a,	0x8a,	0x40,	0x09,	0x78,	0xb6,	0xe6,
+	0x78,	0xbb,	0xf6,	0x75,	0x1f,	0x04,	0x78,	0xbb,	0xe6,
+	0x75,	0xf0,	0x05,	0xa4,	0xf5,	0x49,	0x02,	0x08,	0x5b,
+	0xe5,	0x1f,	0xb4,	0x04,	0x1d,	0x90,	0x0e,	0x89,	0xe4,
+	0x78,	0xc0,	0x12,	0x0a,	0x99,	0xc3,	0x33,	0xce,	0x33,
+	0xce,	0xd8,	0xf9,	0xff,	0xd3,	0xed,	0x9f,	0xec,	0x9e,
+	0x40,	0x02,	0xd2,	0x37,	0x75,	0x1f,	0x05,	0x22,	0xef,
+	0x8d,	0xf0,	0xa4,	0xa8,	0xf0,	0xcf,	0x8c,	0xf0,	0xa4,
+	0x28,	0xce,	0x8d,	0xf0,	0xa4,	0x2e,	0xfe,	0x22,	0xbc,
+	0x00,	0x0b,	0xbe,	0x00,	0x29,	0xef,	0x8d,	0xf0,	0x84,
+	0xff,	0xad,	0xf0,	0x22,	0xe4,	0xcc,	0xf8,	0x75,	0xf0,
+	0x08,	0xef,	0x2f,	0xff,	0xee,	0x33,	0xfe,	0xec,	0x33,
+	0xfc,	0xee,	0x9d,	0xec,	0x98,	0x40,	0x05,	0xfc,	0xee,
+	0x9d,	0xfe,	0x0f,	0xd5,	0xf0,	0xe9,	0xe4,	0xce,	0xfd,
+	0x22,	0xed,	0xf8,	0xf5,	0xf0,	0xee,	0x84,	0x20,	0xd2,
+	0x1c,	0xfe,	0xad,	0xf0,	0x75,	0xf0,	0x08,	0xef,	0x2f,
+	0xff,	0xed,	0x33,	0xfd,	0x40,	0x07,	0x98,	0x50,	0x06,
+	0xd5,	0xf0,	0xf2,	0x22,	0xc3,	0x98,	0xfd,	0x0f,	0xd5,
+	0xf0,	0xea,	0x22,	0xe8,	0x8f,	0xf0,	0xa4,	0xcc,	0x8b,
+	0xf0,	0xa4,	0x2c,	0xfc,	0xe9,	0x8e,	0xf0,	0xa4,	0x2c,
+	0xfc,	0x8a,	0xf0,	0xed,	0xa4,	0x2c,	0xfc,	0xea,	0x8e,
+	0xf0,	0xa4,	0xcd,	0xa8,	0xf0,	0x8b,	0xf0,	0xa4,	0x2d,
+	0xcc,	0x38,	0x25,	0xf0,	0xfd,	0xe9,	0x8f,	0xf0,	0xa4,
+	0x2c,	0xcd,	0x35,	0xf0,	0xfc,	0xeb,	0x8e,	0xf0,	0xa4,
+	0xfe,	0xa9,	0xf0,	0xeb,	0x8f,	0xf0,	0xa4,	0xcf,	0xc5,
+	0xf0,	0x2e,	0xcd,	0x39,	0xfe,	0xe4,	0x3c,	0xfc,	0xea,
+	0xa4,	0x2d,	0xce,	0x35,	0xf0,	0xfd,	0xe4,	0x3c,	0xfc,
+	0x22,	0x75,	0xf0,	0x08,	0x75,	0x82,	0x00,	0xef,	0x2f,
+	0xff,	0xee,	0x33,	0xfe,	0xcd,	0x33,	0xcd,	0xcc,	0x33,
+	0xcc,	0xc5,	0x82,	0x33,	0xc5,	0x82,	0x9b,	0xed,	0x9a,
+	0xec,	0x99,	0xe5,	0x82,	0x98,	0x40,	0x0c,	0xf5,	0x82,
+	0xee,	0x9b,	0xfe,	0xed,	0x9a,	0xfd,	0xec,	0x99,	0xfc,
+	0x0f,	0xd5,	0xf0,	0xd6,	0xe4,	0xce,	0xfb,	0xe4,	0xcd,
+	0xfa,	0xe4,	0xcc,	0xf9,	0xa8,	0x82,	0x22,	0xb8,	0x00,
+	0xc1,	0xb9,	0x00,	0x59,	0xba,	0x00,	0x2d,	0xec,	0x8b,
+	0xf0,	0x84,	0xcf,	0xce,	0xcd,	0xfc,	0xe5,	0xf0,	0xcb,
+	0xf9,	0x78,	0x18,	0xef,	0x2f,	0xff,	0xee,	0x33,	0xfe,
+	0xed,	0x33,	0xfd,	0xec,	0x33,	0xfc,	0xeb,	0x33,	0xfb,
+	0x10,	0xd7,	0x03,	0x99,	0x40,	0x04,	0xeb,	0x99,	0xfb,
+	0x0f,	0xd8,	0xe5,	0xe4,	0xf9,	0xfa,	0x22,	0x78,	0x18,
+	0xef,	0x2f,	0xff,	0xee,	0x33,	0xfe,	0xed,	0x33,	0xfd,
+	0xec,	0x33,	0xfc,	0xc9,	0x33,	0xc9,	0x10,	0xd7,	0x05,
+	0x9b,	0xe9,	0x9a,	0x40,	0x07,	0xec,	0x9b,	0xfc,	0xe9,
+	0x9a,	0xf9,	0x0f,	0xd8,	0xe0,	0xe4,	0xc9,	0xfa,	0xe4,
+	0xcc,	0xfb,	0x22,	0x75,	0xf0,	0x10,	0xef,	0x2f,	0xff,
+	0xee,	0x33,	0xfe,	0xed,	0x33,	0xfd,	0xcc,	0x33,	0xcc,
+	0xc8,	0x33,	0xc8,	0x10,	0xd7,	0x07,	0x9b,	0xec,	0x9a,
+	0xe8,	0x99,	0x40,	0x0a,	0xed,	0x9b,	0xfd,	0xec,	0x9a,
+	0xfc,	0xe8,	0x99,	0xf8,	0x0f,	0xd5,	0xf0,	0xda,	0xe4,
+	0xcd,	0xfb,	0xe4,	0xcc,	0xfa,	0xe4,	0xc8,	0xf9,	0x22,
+	0xeb,	0x9f,	0xf5,	0xf0,	0xea,	0x9e,	0x42,	0xf0,	0xe9,
+	0x9d,	0x42,	0xf0,	0xe8,	0x9c,	0x45,	0xf0,	0x22,	0xe8,
+	0x60,	0x0f,	0xef,	0xc3,	0x33,	0xff,	0xee,	0x33,	0xfe,
+	0xed,	0x33,	0xfd,	0xec,	0x33,	0xfc,	0xd8,	0xf1,	0x22,
+	0xe4,	0x93,	0xfc,	0x74,	0x01,	0x93,	0xfd,	0x74,	0x02,
+	0x93,	0xfe,	0x74,	0x03,	0x93,	0xff,	0x22,	0xe6,	0xfb,
+	0x08,	0xe6,	0xf9,	0x08,	0xe6,	0xfa,	0x08,	0xe6,	0xcb,
+	0xf8,	0x22,	0xec,	0xf6,	0x08,	0xed,	0xf6,	0x08,	0xee,
+	0xf6,	0x08,	0xef,	0xf6,	0x22,	0xa4,	0x25,	0x82,	0xf5,
+	0x82,	0xe5,	0xf0,	0x35,	0x83,	0xf5,	0x83,	0x22,	0xd0,
+	0x83,	0xd0,	0x82,	0xf8,	0xe4,	0x93,	0x70,	0x12,	0x74,
+	0x01,	0x93,	0x70,	0x0d,	0xa3,	0xa3,	0x93,	0xf8,	0x74,
+	0x01,	0x93,	0xf5,	0x82,	0x88,	0x83,	0xe4,	0x73,	0x74,
+	0x02,	0x93,	0x68,	0x60,	0xef,	0xa3,	0xa3,	0xa3,	0x80,
+	0xdf,	0x90,	0x38,	0x04,	0x78,	0x4f,	0x12,	0x09,	0x50,
+	0x90,	0x38,	0x00,	0xe0,	0xfe,	0xa3,	0xe0,	0xfd,	0xed,
+	0xff,	0xc3,	0x12,	0x09,	0x09,	0x90,	0x38,	0x10,	0x12,
+	0x08,	0xfd,	0x90,	0x38,	0x06,	0x78,	0x51,	0x12,	0x09,
+	0x50,	0x90,	0x38,	0x02,	0xe0,	0xfe,	0xa3,	0xe0,	0xfd,
+	0xed,	0xff,	0xc3,	0x12,	0x09,	0x09,	0x90,	0x38,	0x12,
+	0x12,	0x08,	0xfd,	0xa3,	0xe0,	0xb4,	0x31,	0x07,	0x78,
+	0x4f,	0x79,	0x4f,	0x12,	0x09,	0x66,	0x90,	0x38,	0x14,
+	0xe0,	0xb4,	0x71,	0x15,	0x78,	0x4f,	0xe6,	0xfe,	0x08,
+	0xe6,	0x78,	0x02,	0xce,	0xc3,	0x13,	0xce,	0x13,	0xd8,
+	0xf9,	0x79,	0x50,	0xf7,	0xee,	0x19,	0xf7,	0x90,	0x38,
+	0x15,	0xe0,	0xb4,	0x31,	0x07,	0x78,	0x51,	0x79,	0x51,
+	0x12,	0x09,	0x66,	0x90,	0x38,	0x15,	0xe0,	0xb4,	0x71,
+	0x15,	0x78,	0x51,	0xe6,	0xfe,	0x08,	0xe6,	0x78,	0x02,
+	0xce,	0xc3,	0x13,	0xce,	0x13,	0xd8,	0xf9,	0x79,	0x52,
+	0xf7,	0xee,	0x19,	0xf7,	0x79,	0x4f,	0x12,	0x09,	0x38,
+	0x09,	0x12,	0x09,	0x38,	0xaf,	0x45,	0x12,	0x08,	0xee,
+	0x7d,	0x50,	0x12,	0x02,	0xa9,	0x78,	0x57,	0xa6,	0x06,
+	0x08,	0xa6,	0x07,	0xaf,	0x43,	0x12,	0x08,	0xee,	0x7d,
+	0x50,	0x12,	0x02,	0xa9,	0x78,	0x53,	0xa6,	0x06,	0x08,
+	0xa6,	0x07,	0xaf,	0x46,	0x78,	0x51,	0x12,	0x08,	0xf0,
+	0x7d,	0x3c,	0x12,	0x02,	0xa9,	0x78,	0x59,	0xa6,	0x06,
+	0x08,	0xa6,	0x07,	0xaf,	0x44,	0x7e,	0x00,	0x78,	0x51,
+	0x12,	0x08,	0xf2,	0x7d,	0x3c,	0x12,	0x02,	0xa9,	0x78,
+	0x55,	0xa6,	0x06,	0x08,	0xa6,	0x07,	0xc3,	0x78,	0x58,
+	0xe6,	0x94,	0x08,	0x18,	0xe6,	0x94,	0x00,	0x50,	0x05,
+	0x76,	0x00,	0x08,	0x76,	0x08,	0xc3,	0x78,	0x5a,	0xe6,
+	0x94,	0x08,	0x18,	0xe6,	0x94,	0x00,	0x50,	0x05,	0x76,
+	0x00,	0x08,	0x76,	0x08,	0x78,	0x57,	0x12,	0x09,	0x25,
+	0xff,	0xd3,	0x78,	0x54,	0xe6,	0x9f,	0x18,	0xe6,	0x9e,
+	0x40,	0x0e,	0x78,	0x57,	0xe6,	0x13,	0xfe,	0x08,	0xe6,
+	0x78,	0x54,	0x12,	0x09,	0x5b,	0x80,	0x04,	0x7e,	0x00,
+	0x7f,	0x00,	0x78,	0x5b,	0x12,	0x09,	0x1d,	0xff,	0xd3,
+	0x78,	0x56,	0xe6,	0x9f,	0x18,	0xe6,	0x9e,	0x40,	0x0e,
+	0x78,	0x59,	0xe6,	0x13,	0xfe,	0x08,	0xe6,	0x78,	0x56,
+	0x12,	0x09,	0x5b,	0x80,	0x04,	0x7e,	0x00,	0x7f,	0x00,
+	0xe4,	0xfc,	0xfd,	0x78,	0x5f,	0x12,	0x04,	0x5c,	0x78,
+	0x57,	0x12,	0x09,	0x25,	0x78,	0x54,	0x26,	0xff,	0xee,
+	0x18,	0x36,	0xfe,	0x78,	0x63,	0x12,	0x09,	0x1d,	0x78,
+	0x56,	0x26,	0xff,	0xee,	0x18,	0x36,	0xfe,	0xe4,	0xfc,
+	0xfd,	0x78,	0x67,	0x12,	0x04,	0x5c,	0x12,	0x09,	0x2d,
+	0x78,	0x63,	0x12,	0x04,	0x4f,	0xd3,	0x12,	0x04,	0x1b,
+	0x40,	0x08,	0x12,	0x09,	0x2d,	0x78,	0x63,	0x12,	0x04,
+	0x5c,	0x78,	0x51,	0x12,	0x09,	0x2f,	0x78,	0x67,	0x12,
+	0x04,	0x4f,	0xd3,	0x12,	0x04,	0x1b,	0x40,	0x0a,	0x78,
+	0x51,	0x12,	0x09,	0x2f,	0x78,	0x67,	0x12,	0x04,	0x5c,
+	0xe4,	0xfd,	0x78,	0x5e,	0x12,	0x09,	0x48,	0x24,	0x01,
+	0x12,	0x09,	0x11,	0x78,	0x62,	0x12,	0x09,	0x48,	0x24,
+	0x02,	0x12,	0x09,	0x11,	0x78,	0x66,	0x12,	0x09,	0x48,
+	0x24,	0x03,	0x12,	0x09,	0x11,	0x78,	0x6a,	0x12,	0x09,
+	0x48,	0x24,	0x04,	0x12,	0x09,	0x11,	0x0d,	0xbd,	0x05,
+	0xd4,	0xc2,	0x0e,	0xc2,	0x06,	0x22,	0x85,	0x08,	0x41,
+	0x90,	0x30,	0x24,	0xe0,	0xf5,	0x3d,	0xa3,	0xe0,	0xf5,
+	0x3e,	0xa3,	0xe0,	0xf5,	0x3f,	0xa3,	0xe0,	0xf5,	0x40,
+	0xa3,	0xe0,	0xf5,	0x3c,	0xd2,	0x34,	0xe5,	0x41,	0x12,
+	0x04,	0x74,	0x06,	0xc7,	0x03,	0x06,	0xcb,	0x04,	0x06,
+	0xd1,	0x07,	0x06,	0xda,	0x08,	0x06,	0xeb,	0x12,	0x07,
+	0x03,	0x18,	0x07,	0x19,	0x19,	0x06,	0xee,	0x1a,	0x06,
+	0xfa,	0x1b,	0x07,	0x3e,	0x80,	0x07,	0x43,	0x81,	0x07,
+	0xa1,	0x8f,	0x07,	0x90,	0x90,	0x07,	0xa1,	0x91,	0x07,
+	0xa1,	0x92,	0x07,	0xa1,	0x93,	0x07,	0xa1,	0x94,	0x07,
+	0xa1,	0x98,	0x07,	0x9e,	0x9f,	0x00,	0x00,	0x07,	0xbc,
+	0x12,	0x0a,	0xf4,	0x22,	0x12,	0x0a,	0xf4,	0xd2,	0x03,
+	0x22,	0xa2,	0x37,	0xe4,	0x33,	0xf5,	0x3c,	0x02,	0x07,
+	0xa1,	0xc2,	0x01,	0xc2,	0x02,	0xc2,	0x03,	0x12,	0x09,
+	0x70,	0x75,	0x1e,	0x70,	0xd2,	0x35,	0x02,	0x07,	0xa1,
+	0x02,	0x07,	0x8b,	0x85,	0x40,	0x48,	0x85,	0x3c,	0x49,
+	0x12,	0x08,	0x5b,	0x02,	0x07,	0xa1,	0x85,	0x48,	0x40,
+	0x85,	0x49,	0x3c,	0x02,	0x07,	0xa1,	0xe4,	0xf5,	0x22,
+	0xf5,	0x23,	0x85,	0x40,	0x31,	0x85,	0x3f,	0x30,	0x85,
+	0x3e,	0x2f,	0x85,	0x3d,	0x2e,	0x12,	0x0a,	0xc6,	0x80,
+	0x1f,	0x75,	0x22,	0x00,	0x75,	0x23,	0x01,	0x74,	0xff,
+	0xf5,	0x2d,	0xf5,	0x2c,	0xf5,	0x2b,	0xf5,	0x2a,	0x12,
+	0x0a,	0xc6,	0x85,	0x2d,	0x40,	0x85,	0x2c,	0x3f,	0x85,
+	0x2b,	0x3e,	0x85,	0x2a,	0x3d,	0xe4,	0xf5,	0x3c,	0x02,
+	0x07,	0xa1,	0x12,	0x0b,	0x3d,	0x80,	0x5e,	0x85,	0x3d,
+	0x43,	0x85,	0x3e,	0x44,	0xe5,	0x45,	0xc3,	0x13,	0xff,
+	0xe5,	0x43,	0xc3,	0x9f,	0x50,	0x02,	0x8f,	0x43,	0xe5,
+	0x46,	0xc3,	0x13,	0xff,	0xe5,	0x44,	0xc3,	0x9f,	0x50,
+	0x02,	0x8f,	0x44,	0xe5,	0x45,	0xc3,	0x13,	0xff,	0xfd,
+	0xe5,	0x43,	0x90,	0x0e,	0x7f,	0x12,	0x0b,	0x10,	0x40,
+	0x04,	0xee,	0x9f,	0xf5,	0x43,	0xe5,	0x46,	0xc3,	0x13,
+	0xff,	0xfd,	0xe5,	0x44,	0x90,	0x0e,	0x80,	0x12,	0x0b,
+	0x10,	0x40,	0x04,	0xee,	0x9f,	0xf5,	0x44,	0x12,	0x04,
+	0x9a,	0x80,	0x11,	0x85,	0x40,	0x46,	0x85,	0x3f,	0x45,
+	0x85,	0x3e,	0x44,	0x85,	0x3d,	0x43,	0x80,	0x03,	0x02,
+	0x04,	0x9a,	0x90,	0x30,	0x24,	0xe5,	0x3d,	0xf0,	0xa3,
+	0xe5,	0x3e,	0xf0,	0xa3,	0xe5,	0x3f,	0xf0,	0xa3,	0xe5,
+	0x40,	0xf0,	0xa3,	0xe5,	0x3c,	0xf0,	0x90,	0x30,	0x23,
+	0xe4,	0xf0,	0x22,	0xc0,	0xe0,	0xc0,	0x83,	0xc0,	0x82,
+	0xc0,	0xd0,	0x90,	0x3f,	0x0c,	0xe0,	0xf5,	0x32,	0xe5,
+	0x32,	0x30,	0xe3,	0x4c,	0x30,	0x36,	0x3e,	0x90,	0x60,
+	0x19,
+	0xe0,
+	0xf5,
+	0x0a,
+	0xa3,
+	0xe0,
+	0xf5,
+	0x0b,
+	0x90,
+	0x60,
+	0x1d,
+	0xe0,
+	0xf5,
+	0x14,
+	0xa3,
+	0xe0,
+	0xf5,
+	0x15,
+	0x30,
+	0x01,
+	0x06,
+	0x30,
+	0x33,
+	0x03,
+	0xd3,
+	0x80,
+	0x01,
+	0xc3,
+	0x92,
+	0x09,
+	0x30,
+	0x02,
+	0x06,
+	0x30,
+	0x33,
+	0x03,
+	0xd3,
+	0x80,
+	0x01,
+	0xc3,
+	0x92,
+	0x0a,
+	0x30,
+	0x33,
+	0x0c,
+	0x30,
+	0x03,
+	0x09,
+	0x20,
+	0x02,
+	0x06,
+	0x20,
+	0x01,
+	0x03,
+	0xd3,
+	0x80,
+	0x01,
+	0xc3,
+	0x92,
+	0x0b,
+	0x90,
+	0x30,
+	0x01,
+	0xe0,
+	0x44,
+	0x40,
+	0xf0,
+	0xe0,
+	0x54,
+	0xbf,
+	0xf0,
+	0xe5,
+	0x32,
+	0x30,
+	0xe1,
+	0x14,
+	0x30,
+	0x34,
+	0x11,
+	0x90,
+	0x30,
+	0x22,
+	0xe0,
+	0xf5,
+	0x08,
+	0xe4,
+	0xf0,
+	0x30,
+	0x00,
+	0x03,
+	0xd3,
+	0x80,
+	0x01,
+	0xc3,
+	0x92,
+	0x08,
+	0xe5,
+	0x32,
+	0x30,
+	0xe5,
+	0x12,
+	0x90,
+	0x56,
+	0xa1,
+	0xe0,
+	0xf5,
+	0x09,
+	0x30,
+	0x31,
+	0x09,
+	0x30,
+	0x05,
+	0x03,
+	0xd3,
+	0x80,
+	0x01,
+	0xc3,
+	0x92,
+	0x0d,
+	0x90,
+	0x3f,
+	0x0c,
+	0xe5,
+	0x32,
+	0xf0,
+	0xd0,
+	0xd0,
+	0xd0,
+	0x82,
+	0xd0,
+	0x83,
+	0xd0,
+	0xe0,
+	0x32,
+	0x90,
+	0x0e,
+	0x7d,
+	0xe4,
+	0x93,
+	0xfe,
+	0x74,
+	0x01,
+	0x93,
+	0xff,
+	0xc3,
+	0x90,
+	0x0e,
+	0x7b,
+	0x74,
+	0x01,
+	0x93,
+	0x9f,
+	0xff,
+	0xe4,
+	0x93,
+	0x9e,
+	0xfe,
+	0xe4,
+	0x8f,
+	0x3b,
+	0x8e,
+	0x3a,
+	0xf5,
+	0x39,
+	0xf5,
+	0x38,
+	0xab,
+	0x3b,
+	0xaa,
+	0x3a,
+	0xa9,
+	0x39,
+	0xa8,
+	0x38,
+	0xaf,
+	0x49,
+	0xfc,
+	0xfd,
+	0xfe,
+	0x12,
+	0x02,
+	0xfe,
+	0x12,
+	0x0b,
+	0x22,
+	0xe4,
+	0x7b,
+	0xff,
+	0xfa,
+	0xf9,
+	0xf8,
+	0x12,
+	0x03,
+	0x89,
+	0x12,
+	0x0b,
+	0x22,
+	0x90,
+	0x0e,
+	0x69,
+	0xe4,
+	0x12,
+	0x0b,
+	0x37,
+	0x12,
+	0x0b,
+	0x22,
+	0xe4,
+	0x85,
+	0x48,
+	0x37,
+	0xf5,
+	0x36,
+	0xf5,
+	0x35,
+	0xf5,
+	0x34,
+	0xaf,
+	0x37,
+	0xae,
+	0x36,
+	0xad,
+	0x35,
+	0xac,
+	0x34,
+	0xa3,
+	0x12,
+	0x0b,
+	0x37,
+	0x8f,
+	0x37,
+	0x8e,
+	0x36,
+	0x8d,
+	0x35,
+	0x8c,
+	0x34,
+	0xe5,
+	0x3b,
+	0x45,
+	0x37,
+	0xf5,
+	0x3b,
+	0xe5,
+	0x3a,
+	0x45,
+	0x36,
+	0xf5,
+	0x3a,
+	0xe5,
+	0x39,
+	0x45,
+	0x35,
+	0xf5,
+	0x39,
+	0xe5,
+	0x38,
+	0x45,
+	0x34,
+	0xf5,
+	0x38,
+	0xe4,
+	0xf5,
+	0x22,
+	0xf5,
+	0x23,
+	0x85,
+	0x3b,
+	0x31,
+	0x85,
+	0x3a,
+	0x30,
+	0x85,
+	0x39,
+	0x2f,
+	0x85,
+	0x38,
+	0x2e,
+	0x02,
+	0x0a,
+	0xc6,
+	0x78,
+	0x4f,
+	0x7e,
+	0x00,
+	0xe6,
+	0xfc,
+	0x08,
+	0xe6,
+	0xfd,
+	0x12,
+	0x02,
+	0x97,
+	0x7c,
+	0x00,
+	0x22,
+	0xe0,
+	0xa3,
+	0xe0,
+	0x75,
+	0xf0,
+	0x02,
+	0xa4,
+	0xff,
+	0xae,
+	0xf0,
+	0xc3,
+	0x08,
+	0xe6,
+	0x9f,
+	0xf6,
+	0x18,
+	0xe6,
+	0x9e,
+	0xf6,
+	0x22,
+	0xff,
+	0xe5,
+	0xf0,
+	0x34,
+	0x60,
+	0x8f,
+	0x82,
+	0xf5,
+	0x83,
+	0xec,
+	0xf0,
+	0x22,
+	0xe4,
+	0xfc,
+	0xfd,
+	0x12,
+	0x04,
+	0x5c,
+	0x78,
+	0x59,
+	0xe6,
+	0xc3,
+	0x13,
+	0xfe,
+	0x08,
+	0xe6,
+	0x13,
+	0x22,
+	0x78,
+	0x4f,
+	0xe6,
+	0xfe,
+	0x08,
+	0xe6,
+	0xff,
+	0xe4,
+	0xfc,
+	0xfd,
+	0x22,
+	0xe7,
+	0xc4,
+	0xf8,
+	0x54,
+	0xf0,
+	0xc8,
+	0x68,
+	0xf7,
+	0x09,
+	0xe7,
+	0xc4,
+	0x54,
+	0x0f,
+	0x48,
+	0xf7,
+	0x22,
+	0xe6,
+	0xfc,
+	0xed,
+	0x75,
+	0xf0,
+	0x04,
+	0xa4,
+	0x22,
+	0xe0,
+	0xfe,
+	0xa3,
+	0xe0,
+	0xfd,
+	0xee,
+	0xf6,
+	0xed,
+	0x08,
+	0xf6,
+	0x22,
+	0x13,
+	0xff,
+	0xc3,
+	0xe6,
+	0x9f,
+	0xff,
+	0x18,
+	0xe6,
+	0x9e,
+	0xfe,
+	0x22,
+	0xe6,
+	0xc3,
+	0x13,
+	0xf7,
+	0x08,
+	0xe6,
+	0x13,
+	0x09,
+	0xf7,
+	0x22,
+	0xe4,
+	0xf5,
+	0x49,
+	0x90,
+	0x0e,
+	0x77,
+	0x93,
+	0xff,
+	0xe4,
+	0x8f,
+	0x37,
+	0xf5,
+	0x36,
+	0xf5,
+	0x35,
+	0xf5,
+	0x34,
+	0xaf,
+	0x37,
+	0xae,
+	0x36,
+	0xad,
+	0x35,
+	0xac,
+	0x34,
+	0x90,
+	0x0e,
+	0x6a,
+	0x12,
+	0x0b,
+	0x37,
+	0x8f,
+	0x37,
+	0x8e,
+	0x36,
+	0x8d,
+	0x35,
+	0x8c,
+	0x34,
+	0x90,
+	0x0e,
+	0x72,
+	0x12,
+	0x04,
+	0x3f,
+	0xef,
+	0x45,
+	0x37,
+	0xf5,
+	0x37,
+	0xee,
+	0x45,
+	0x36,
+	0xf5,
+	0x36,
+	0xed,
+	0x45,
+	0x35,
+	0xf5,
+	0x35,
+	0xec,
+	0x45,
+	0x34,
+	0xf5,
+	0x34,
+	0xe4,
+	0xf5,
+	0x22,
+	0xf5,
+	0x23,
+	0x85,
+	0x37,
+	0x31,
+	0x85,
+	0x36,
+	0x30,
+	0x85,
+	0x35,
+	0x2f,
+	0x85,
+	0x34,
+	0x2e,
+	0x12,
+	0x0a,
+	0xc6,
+	0xe4,
+	0xf5,
+	0x22,
+	0xf5,
+	0x23,
+	0x90,
+	0x0e,
+	0x72,
+	0x12,
+	0x0b,
+	0x2b,
+	0x12,
+	0x0a,
+	0xc6,
+	0xe4,
+	0xf5,
+	0x22,
+	0xf5,
+	0x23,
+	0x90,
+	0x0e,
+	0x6e,
+	0x12,
+	0x0b,
+	0x2b,
+	0x02,
+	0x0a,
+	0xc6,
+	0x75,
+	0x89,
+	0x03,
+	0x75,
+	0xa8,
+	0x01,
+	0x75,
+	0xb8,
+	0x04,
+	0x75,
+	0x34,
+	0xff,
+	0x75,
+	0x35,
+	0x0e,
+	0x75,
+	0x36,
+	0x15,
+	0x75,
+	0x37,
+	0x0d,
+	0x12,
+	0x0a,
+	0x4a,
+	0x12,
+	0x00,
+	0x09,
+	0x12,
+	0x0b,
+	0x3d,
+	0x12,
+	0x00,
+	0x06,
+	0xd2,
+	0x00,
+	0xd2,
+	0x34,
+	0xd2,
+	0xaf,
+	0x75,
+	0x34,
+	0xff,
+	0x75,
+	0x35,
+	0x0e,
+	0x75,
+	0x36,
+	0x49,
+	0x75,
+	0x37,
+	0x03,
+	0x12,
+	0x0a,
+	0x4a,
+	0x30,
+	0x08,
+	0x09,
+	0xc2,
+	0x34,
+	0x12,
+	0x06,
+	0x6a,
+	0xc2,
+	0x08,
+	0xd2,
+	0x34,
+	0x30,
+	0x09,
+	0x09,
+	0xc2,
+	0x36,
+	0x12,
+	0x00,
+	0x0e,
+	0xc2,
+	0x09,
+	0xd2,
+	0x36,
+	0x30,
+	0x0e,
+	0x03,
+	0x12,
+	0x04,
+	0x9a,
+	0x30,
+	0x35,
+	0xdf,
+	0x90,
+	0x30,
+	0x29,
+	0xe5,
+	0x1e,
+	0xf0,
+	0xb4,
+	0x10,
+	0x05,
+	0x90,
+	0x30,
+	0x23,
+	0xe4,
+	0xf0,
+	0xc2,
+	0x35,
+	0x80,
+	0xcd,
+	0xae,
+	0x35,
+	0xaf,
+	0x36,
+	0xe4,
+	0xfd,
+	0xed,
+	0xc3,
+	0x95,
+	0x37,
+	0x50,
+	0x33,
+	0x12,
+	0x0b,
+	0x87,
+	0xe4,
+	0x93,
+	0xf5,
+	0x38,
+	0x74,
+	0x01,
+	0x93,
+	0xf5,
+	0x39,
+	0x45,
+	0x38,
+	0x60,
+	0x23,
+	0x85,
+	0x39,
+	0x82,
+	0x85,
+	0x38,
+	0x83,
+	0xe0,
+	0xfc,
+	0x12,
+	0x0b,
+	0x87,
+	0x74,
+	0x03,
+	0x93,
+	0x52,
+	0x04,
+	0x12,
+	0x0b,
+	0x87,
+	0x74,
+	0x02,
+	0x93,
+	0x42,
+	0x04,
+	0x85,
+	0x39,
+	0x82,
+	0x85,
+	0x38,
+	0x83,
+	0xec,
+	0xf0,
+	0x0d,
+	0x80,
+	0xc7,
+	0x22,
+	0x78,
+	0xbb,
+	0xe6,
+	0xd3,
+	0x08,
+	0xff,
+	0xe6,
+	0x64,
+	0x80,
+	0xf8,
+	0xef,
+	0x64,
+	0x80,
+	0x98,
+	0x22,
+	0x93,
+	0xff,
+	0x7e,
+	0x00,
+	0xe6,
+	0xfc,
+	0x08,
+	0xe6,
+	0xfd,
+	0x12,
+	0x02,
+	0x97,
+	0xac,
+	0x06,
+	0xad,
+	0x07,
+	0x78,
+	0xb3,
+	0xe6,
+	0xfe,
+	0x08,
+	0xe6,
+	0x78,
+	0x03,
+	0x22,
+	0x78,
+	0xba,
+	0xd3,
+	0xe6,
+	0x64,
+	0x80,
+	0x94,
+	0x80,
+	0x22,
+	0x25,
+	0xe0,
+	0x24,
+	0x0a,
+	0xf8,
+	0xe6,
+	0xfe,
+	0x08,
+	0xe6,
+	0xff,
+	0x22,
+	0xa2,
+	0xaf,
+	0x92,
+	0x32,
+	0xc2,
+	0xaf,
+	0xe5,
+	0x23,
+	0x45,
+	0x22,
+	0x90,
+	0x0e,
+	0x5d,
+	0x60,
+	0x0e,
+	0x12,
+	0x0b,
+	0x70,
+	0xe0,
+	0xf5,
+	0x2c,
+	0x12,
+	0x0b,
+	0x6d,
+	0xe0,
+	0xf5,
+	0x2d,
+	0x80,
+	0x0c,
+	0x12,
+	0x0b,
+	0x70,
+	0xe5,
+	0x30,
+	0xf0,
+	0x12,
+	0x0b,
+	0x6d,
+	0xe5,
+	0x31,
+	0xf0,
+	0xa2,
+	0x32,
+	0x92,
+	0xaf,
+	0x22,
+	0xd2,
+	0x01,
+	0xc2,
+	0x02,
+	0xe4,
+	0xf5,
+	0x1f,
+	0xf5,
+	0x1e,
+	0xd2,
+	0x35,
+	0xd2,
+	0x33,
+	0xd2,
+	0x36,
+	0xd2,
+	0x01,
+	0xc2,
+	0x02,
+	0xf5,
+	0x1f,
+	0xf5,
+	0x1e,
+	0xd2,
+	0x35,
+	0xd2,
+	0x33,
+	0x22,
+	0x2d,
+	0xfd,
+	0xe4,
+	0x33,
+	0xfc,
+	0xe4,
+	0x93,
+	0xfe,
+	0xfb,
+	0xd3,
+	0xed,
+	0x9b,
+	0x74,
+	0x80,
+	0xf8,
+	0x6c,
+	0x98,
+	0x22,
+	0x8f,
+	0x3b,
+	0x8e,
+	0x3a,
+	0x8d,
+	0x39,
+	0x8c,
+	0x38,
+	0x22,
+	0x12,
+	0x04,
+	0x3f,
+	0x8f,
+	0x31,
+	0x8e,
+	0x30,
+	0x8d,
+	0x2f,
+	0x8c,
+	0x2e,
+	0x22,
+	0x93,
+	0xf9,
+	0xf8,
+	0x02,
+	0x04,
+	0x2c,
+	0x90,
+	0x0e,
+	0x81,
+	0x12,
+	0x04,
+	0x3f,
+	0x8f,
+	0x46,
+	0x8e,
+	0x45,
+	0x8d,
+	0x44,
+	0x8c,
+	0x43,
+	0xd2,
+	0x06,
+	0x30,
+	0x06,
+	0x03,
+	0xd3,
+	0x80,
+	0x01,
+	0xc3,
+	0x92,
+	0x0e,
+	0x22,
+	0xc0,
+	0xe0,
+	0xc0,
+	0x83,
+	0xc0,
+	0x82,
+	0x90,
+	0x3f,
+	0x0d,
+	0xe0,
+	0xf5,
+	0x33,
+	0xe5,
+	0x33,
+	0xf0,
+	0xd0,
+	0x82,
+	0xd0,
+	0x83,
+	0xd0,
+	0xe0,
+	0x32,
+	0x90,
+	0x0e,
+	0x5f,
+	0xe4,
+	0x93,
+	0xfe,
+	0x74,
+	0x01,
+	0x93,
+	0xf5,
+	0x82,
+	0x8e,
+	0x83,
+	0x22,
+	0x78,
+	0x7f,
+	0xe4,
+	0xf6,
+	0xd8,
+	0xfd,
+	0x75,
+	0x81,
+	0xca,
+	0x02,
+	0x09,
+	0xe1,
+	0x8f,
+	0x82,
+	0x8e,
+	0x83,
+	0x75,
+	0xf0,
+	0x04,
+	0xed,
+	0x02,
+	0x04,
+	0x68,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x11,
+	0x07,
+	0x21,
+	0x15,
+	0x29,
+	0x13,
+	0x4f,
+	0x56,
+	0x54,
+	0x20,
+	0x20,
+	0x20,
+	0x20,
+	0x20,
+	0x20,
+	0x01,
+	0x10,
+	0x00,
+	0x56,
+	0x40,
+	0x1a,
+	0x30,
+	0x29,
+	0x7e,
+	0x00,
+	0x30,
+	0x04,
+	0x20,
+	0xdf,
+	0x30,
+	0x05,
+	0x40,
+	0xbf,
+	0x50,
+	0x03,
+	0x00,
+	0xfd,
+	0x50,
+	0x27,
+	0x01,
+	0xfe,
+	0x60,
+	0x00,
+	0x11,
+	0x00,
+	0x3f,
+	0x05,
+	0x30,
+	0x00,
+	0x3f,
+	0x06,
+	0x22,
+	0x00,
+	0x3f,
+	0x01,
+	0x2a,
+	0x00,
+	0x3f,
+	0x02,
+	0x00,
+	0x00,
+	0x36,
+	0x06,
+	0x07,
+	0x00,
+	0x3f,
+	0x0b,
+	0x0f,
+	0xf0,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x30,
+	0x01,
+	0x40,
+	0xbf,
+	0x30,
+	0x01,
+	0x00,
+	0xbf,
+	0x30,
+	0x29,
+	0x70,
+	0x00,
+	0x3a,
+	0x00,
+	0x00,
+	0xff,
+	0x3a,
+	0x00,
+	0x00,
+	0xff,
+	0x36,
+	0x03,
+	0x36,
+	0x02,
+	0x41,
+	0x44,
+	0x58,
+	0x20,
+	0x18,
+	0x10,
+	0x0a,
+	0x04,
+	0x04,
+	0x00,
+	0x03,
+	0xff,
+	0x64,
+	0x00,
+	0x00,
+	0x80,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x00,
+	0x02,
+	0x04,
+	0x06,
+	0x00,
+	0x03,
+	0x98,
+	0x00,
+	0xcc,
+	0x50,
+	0x3c,
+	0x28,
+	0x1e,
+	0x10,
+	0x10,
+	0x00,
+	0x00,
+	0x00,
+	0x6e,
+	0x30,
+	0x28,
+	0x00,
+	0xa5,
+	0x5a,
+	0x00,
+};
+
+#endif /* CAMSENSOR_OV5640 */
diff --git a/drivers/media/video/msm/ov5647.c b/drivers/media/video/msm/ov5647.c
new file mode 100644
index 0000000..2a6e7be
--- /dev/null
+++ b/drivers/media/video/msm/ov5647.c
@@ -0,0 +1,1201 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/bitops.h>
+#include <linux/leds.h>
+#include <mach/camera.h>
+#include <media/msm_camera.h>
+#include "ov5647.h"
+
+/* 16bit address - 8 bit context register structure */
+#define Q8	0x00000100
+#define Q10	0x00000400
+
+#define REG_OV5647_GAIN_MSB           0x350A
+#define REG_OV5647_GAIN_LSB           0x350B
+#define REG_OV5647_LINE_HSB           0x3500
+#define REG_OV5647_LINE_MSB           0x3501
+#define REG_OV5647_LINE_LSB           0x3502
+
+/* MCLK */
+#define OV5647_MASTER_CLK_RATE 24000000
+
+/* AF Total steps parameters */
+#define OV5647_TOTAL_STEPS_NEAR_TO_FAR	32
+
+#define OV5647_REG_PREV_FRAME_LEN_1	31
+#define OV5647_REG_PREV_FRAME_LEN_2	32
+#define OV5647_REG_PREV_LINE_LEN_1	33
+#define OV5647_REG_PREV_LINE_LEN_2	34
+
+#define OV5647_REG_SNAP_FRAME_LEN_1	15
+#define OV5647_REG_SNAP_FRAME_LEN_2	16
+#define OV5647_REG_SNAP_LINE_LEN_1	17
+#define OV5647_REG_SNAP_LINE_LEN_2	18
+#define MSB                             1
+#define LSB                             0
+
+/* Debug switch */
+#ifdef CDBG
+#undef CDBG
+#endif
+#ifdef CDBG_HIGH
+#undef CDBG_HIGH
+#endif
+
+/*#define OV5647_VERBOSE_DGB*/
+
+#ifdef OV5647_VERBOSE_DGB
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+#define CDBG_HIGH(fmt, args...) pr_debug(fmt, ##args)
+#else
+#define CDBG(fmt, args...) do { } while (0)
+#define CDBG_HIGH(fmt, args...) pr_debug(fmt, ##args)
+#endif
+
+/*for debug*/
+#ifdef CDBG
+#undef CDBG
+#endif
+#define CDBG(fmt, args...) printk(fmt, ##args)
+
+static uint8_t  mode_mask = 0x09;
+struct ov5647_work_t {
+	struct work_struct work;
+};
+
+static struct ov5647_work_t *ov5647_sensorw;
+static struct ov5647_work_t *ov5647_af_sensorw;
+static struct i2c_client *ov5647_af_client;
+static struct i2c_client *ov5647_client;
+
+struct ov5647_ctrl_t {
+	const struct  msm_camera_sensor_info *sensordata;
+
+	uint32_t sensormode;
+	uint32_t fps_divider;/* init to 1 * 0x00000400 */
+	uint32_t pict_fps_divider;/* init to 1 * 0x00000400 */
+	uint16_t fps;
+
+	uint16_t curr_lens_pos;
+	uint16_t curr_step_pos;
+	uint16_t my_reg_gain;
+	uint32_t my_reg_line_count;
+	uint16_t total_lines_per_frame;
+
+	enum ov5647_resolution_t prev_res;
+	enum ov5647_resolution_t pict_res;
+	enum ov5647_resolution_t curr_res;
+	enum ov5647_test_mode_t  set_test;
+};
+
+static bool CSI_CONFIG;
+static struct ov5647_ctrl_t *ov5647_ctrl;
+
+static DECLARE_WAIT_QUEUE_HEAD(ov5647_wait_queue);
+static DECLARE_WAIT_QUEUE_HEAD(ov5647_af_wait_queue);
+DEFINE_MUTEX(ov5647_mut);
+
+static uint16_t prev_line_length_pck;
+static uint16_t prev_frame_length_lines;
+static uint16_t snap_line_length_pck;
+static uint16_t snap_frame_length_lines;
+
+static int ov5647_i2c_rxdata(unsigned short saddr,
+		unsigned char *rxdata, int length)
+{
+	struct i2c_msg msgs[] = {
+		{
+			.addr  = saddr,
+			.flags = 0,
+			.len   = 2,
+			.buf   = rxdata,
+		},
+		{
+			.addr  = saddr,
+			.flags = I2C_M_RD,
+			.len   = 1,
+			.buf   = rxdata,
+		},
+	};
+	if (i2c_transfer(ov5647_client->adapter, msgs, 2) < 0) {
+		CDBG("ov5647_i2c_rxdata faild 0x%x\n", saddr);
+		return -EIO;
+	}
+	return 0;
+}
+
+static int32_t ov5647_i2c_txdata(unsigned short saddr,
+		unsigned char *txdata, int length)
+{
+	struct i2c_msg msg[] = {
+		{
+			.addr = saddr,
+			.flags = 0,
+			.len = length,
+			.buf = txdata,
+		},
+	};
+	if (i2c_transfer(ov5647_client->adapter, msg, 1) < 0) {
+		CDBG("ov5647_i2c_txdata faild 0x%x\n", saddr);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int32_t ov5647_i2c_read(unsigned short raddr,
+		unsigned short *rdata)
+{
+	int32_t rc = 0;
+	unsigned char buf[2];
+
+	if (!rdata)
+		return -EIO;
+	CDBG("%s:saddr:0x%x raddr:0x%x data:0x%x",
+		__func__, ov5647_client->addr, raddr, *rdata);
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (raddr & 0xFF00) >> 8;
+	buf[1] = (raddr & 0x00FF);
+	rc = ov5647_i2c_rxdata(ov5647_client->addr >> 1, buf, 1);
+	if (rc < 0) {
+		CDBG("ov5647_i2c_read 0x%x failed!\n", raddr);
+		return rc;
+	}
+	*rdata = buf[0];
+	CDBG("ov5647_i2c_read 0x%x val = 0x%x!\n", raddr, *rdata);
+
+	return rc;
+}
+
+static int32_t ov5647_i2c_write_b_sensor(unsigned short waddr, uint8_t bdata)
+{
+	int32_t rc = -EFAULT;
+	unsigned char buf[3];
+
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (waddr & 0xFF00) >> 8;
+	buf[1] = (waddr & 0x00FF);
+	buf[2] = bdata;
+	CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, bdata);
+	rc = ov5647_i2c_txdata(ov5647_client->addr >> 1, buf, 3);
+	if (rc < 0) {
+		pr_err("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+				waddr, bdata);
+	}
+	return rc;
+}
+
+static int32_t ov5647_i2c_write_b_table(struct ov5647_i2c_reg_conf const
+		*reg_conf_tbl, int num)
+{
+	int i;
+	int32_t rc = -EIO;
+
+	for (i = 0; i < num; i++) {
+		rc = ov5647_i2c_write_b_sensor(reg_conf_tbl->waddr,
+				reg_conf_tbl->wdata);
+		if (rc < 0)
+			break;
+		reg_conf_tbl++;
+	}
+	return rc;
+}
+
+static int32_t ov5647_af_i2c_txdata(unsigned short saddr,
+		unsigned char *txdata, int length)
+{
+	struct i2c_msg msg[] = {
+		{
+			.addr = saddr,
+			.flags = 0,
+			.len = length,
+			.buf = txdata,
+		},
+	};
+	if (i2c_transfer(ov5647_af_client->adapter, msg, 1) < 0) {
+		pr_err("ov5647_af_i2c_txdata faild 0x%x\n", saddr);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int32_t ov5647_af_i2c_write_b_sensor(uint8_t waddr, uint8_t bdata)
+{
+	int32_t rc = -EFAULT;
+	unsigned char buf[2];
+
+	memset(buf, 0, sizeof(buf));
+	buf[0] = waddr;
+	buf[1] = bdata;
+	CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, bdata);
+	rc = ov5647_af_i2c_txdata(ov5647_af_client->addr, buf, 2);
+	if (rc < 0) {
+		pr_err("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+				waddr, bdata);
+	}
+	return rc;
+}
+
+static void ov5647_start_stream(void)
+{
+	CDBG("CAMERA_DBG: 0x4202 0x0, stream on...\r\n");
+	ov5647_i2c_write_b_sensor(0x4202, 0x00);/* streaming on */
+}
+
+static void ov5647_stop_stream(void)
+{
+	CDBG("CAMERA_DBG: 0x4202 0xf, stream off...\r\n");
+	ov5647_i2c_write_b_sensor(0x4202, 0x0f);/* streaming off */
+}
+
+static void ov5647_group_hold_on(void)
+{
+	ov5647_i2c_write_b_sensor(0x0104, 0x01);
+}
+
+static void ov5647_group_hold_off(void)
+{
+	ov5647_i2c_write_b_sensor(0x0104, 0x0);
+}
+
+static void ov5647_get_pict_fps(uint16_t fps, uint16_t *pfps)
+{
+	/* input fps is preview fps in Q8 format */
+	uint32_t divider, d1, d2;
+	uint32_t preview_pclk = 0x37, snapshot_pclk = 0x4f;
+
+	d1 = (prev_frame_length_lines * 0x00000400) / snap_frame_length_lines;
+	d2 = (prev_line_length_pck * 0x00000400) / snap_line_length_pck;
+	divider = (d1 * d2*preview_pclk/snapshot_pclk) / 0x400;
+	CDBG(KERN_ERR "ov5647_get_pict_fps divider = %d", divider);
+	/*Verify PCLK settings and frame sizes.*/
+	*pfps = (uint16_t) (fps * divider / 0x400);
+}
+
+static uint16_t ov5647_get_prev_lines_pf(void)
+{
+	if (ov5647_ctrl->prev_res == QTR_SIZE)
+		return prev_frame_length_lines;
+	else
+		return snap_frame_length_lines;
+}
+
+static uint16_t ov5647_get_prev_pixels_pl(void)
+{
+	if (ov5647_ctrl->prev_res == QTR_SIZE)
+		return prev_line_length_pck;
+	else
+		return snap_line_length_pck;
+}
+
+static uint16_t ov5647_get_pict_lines_pf(void)
+{
+	if (ov5647_ctrl->pict_res == QTR_SIZE)
+		return prev_frame_length_lines;
+	else
+		return snap_frame_length_lines;
+}
+
+static uint16_t ov5647_get_pict_pixels_pl(void)
+{
+	if (ov5647_ctrl->pict_res == QTR_SIZE)
+		return prev_line_length_pck;
+	else
+		return snap_line_length_pck;
+}
+
+static uint32_t ov5647_get_pict_max_exp_lc(void)
+{
+	return snap_frame_length_lines * 24;
+}
+
+static int32_t ov5647_set_fps(struct fps_cfg   *fps)
+{
+	uint16_t total_lines_per_frame;
+	int32_t rc = 0;
+
+	ov5647_ctrl->fps_divider = fps->fps_div;
+	ov5647_ctrl->pict_fps_divider = fps->pict_fps_div;
+
+	if (ov5647_ctrl->sensormode == SENSOR_PREVIEW_MODE) {
+		total_lines_per_frame = (uint16_t)
+		((prev_frame_length_lines * ov5647_ctrl->fps_divider) / 0x400);
+	} else {
+		total_lines_per_frame = (uint16_t)
+		((snap_frame_length_lines * ov5647_ctrl->fps_divider) / 0x400);
+	}
+
+	ov5647_group_hold_on();
+	rc = ov5647_i2c_write_b_sensor(0x0340,
+			((total_lines_per_frame & 0xFF00) >> 8));
+	rc = ov5647_i2c_write_b_sensor(0x0341,
+			(total_lines_per_frame & 0x00FF));
+	ov5647_group_hold_off();
+
+	return rc;
+}
+
+static inline uint8_t ov5647_byte(uint16_t word, uint8_t offset)
+{
+	return word >> (offset * BITS_PER_BYTE);
+}
+
+static int32_t ov5647_write_exp_gain(uint16_t gain, uint32_t line)
+{
+	int rc = 0;
+	uint16_t max_line;
+	u8 intg_time_hsb, intg_time_msb, intg_time_lsb;
+	uint8_t gain_lsb, gain_hsb;
+	ov5647_ctrl->my_reg_gain = gain;
+	ov5647_ctrl->my_reg_line_count = (uint16_t)line;
+
+	CDBG(KERN_ERR "preview exposure setting 0x%x, 0x%x, %d",
+		 gain, line, line);
+
+	gain_lsb = (uint8_t) (ov5647_ctrl->my_reg_gain);
+	gain_hsb = (uint8_t)((ov5647_ctrl->my_reg_gain & 0x300)>>8);
+	/* adjust frame rate */
+	if (line > 980) {
+		rc = ov5647_i2c_write_b_sensor(0x380E,
+			 (uint8_t)((line+4) >> 8)) ;
+		rc = ov5647_i2c_write_b_sensor(0x380F,
+			 (uint8_t)((line+4) & 0x00FF)) ;
+		max_line = line + 4;
+	} else if (max_line > 984) {
+		rc = ov5647_i2c_write_b_sensor(0x380E,
+			 (uint8_t)(984 >> 8)) ;
+		rc = ov5647_i2c_write_b_sensor(0x380F,
+			 (uint8_t)(984 & 0x00FF)) ;
+		max_line = 984;
+	}
+
+	line = line<<4;
+	/* ov5647 need this operation */
+	intg_time_hsb = (u8)(line>>16);
+	intg_time_msb = (u8) ((line & 0xFF00) >> 8);
+	intg_time_lsb = (u8) (line & 0x00FF);
+
+	ov5647_group_hold_on();
+	rc = ov5647_i2c_write_b_sensor(REG_OV5647_LINE_HSB, intg_time_hsb) ;
+	rc = ov5647_i2c_write_b_sensor(REG_OV5647_LINE_MSB, intg_time_msb) ;
+	rc = ov5647_i2c_write_b_sensor(REG_OV5647_LINE_LSB, intg_time_lsb) ;
+
+	rc = ov5647_i2c_write_b_sensor(REG_OV5647_GAIN_MSB, gain_hsb) ;
+	rc = ov5647_i2c_write_b_sensor(REG_OV5647_GAIN_LSB, gain_lsb) ;
+	ov5647_group_hold_off();
+
+	return rc;
+}
+
+
+static int32_t ov5647_set_pict_exp_gain(uint16_t gain, uint32_t line)
+{
+	uint16_t max_line;
+	int rc = 0;
+	uint8_t gain_lsb, gain_hsb;
+	u8 intg_time_hsb, intg_time_msb, intg_time_lsb;
+
+	ov5647_ctrl->my_reg_gain = gain;
+	ov5647_ctrl->my_reg_line_count = (uint16_t)line;
+
+	gain_lsb = (uint8_t) (ov5647_ctrl->my_reg_gain);
+	gain_hsb = (uint8_t)((ov5647_ctrl->my_reg_gain & 0x300)>>8);
+
+	CDBG(KERN_ERR "snapshot exposure seting 0x%x, 0x%x, %d"
+		, gain, line, line);
+
+	if (line > 1964) {
+		rc = ov5647_i2c_write_b_sensor(0x380E,
+			 (uint8_t)((line+4) >> 8)) ;
+		rc = ov5647_i2c_write_b_sensor(0x380F,
+			 (uint8_t)((line+4) & 0x00FF)) ;
+		max_line = line + 4;
+	} else if (max_line > 1968) {
+		rc = ov5647_i2c_write_b_sensor(0x380E,
+			 (uint8_t)(1968 >> 8)) ;
+		rc = ov5647_i2c_write_b_sensor(0x380F,
+			 (uint8_t)(1968 & 0x00FF)) ;
+		max_line = 1968;
+	}
+	line = line<<4;
+	/* ov5647 need this operation */
+	intg_time_hsb = (u8)(line>>16);
+	intg_time_msb = (u8) ((line & 0xFF00) >> 8);
+	intg_time_lsb = (u8) (line & 0x00FF);
+
+	/* FIXME for BLC trigger */
+	ov5647_group_hold_on();
+	rc = ov5647_i2c_write_b_sensor(REG_OV5647_LINE_HSB, intg_time_hsb) ;
+	rc = ov5647_i2c_write_b_sensor(REG_OV5647_LINE_MSB, intg_time_msb) ;
+	rc = ov5647_i2c_write_b_sensor(REG_OV5647_LINE_LSB, intg_time_lsb) ;
+
+	rc = ov5647_i2c_write_b_sensor(REG_OV5647_GAIN_MSB, gain_hsb) ;
+	rc = ov5647_i2c_write_b_sensor(REG_OV5647_GAIN_LSB, gain_lsb - 1) ;
+
+	rc = ov5647_i2c_write_b_sensor(REG_OV5647_LINE_HSB, intg_time_hsb) ;
+	rc = ov5647_i2c_write_b_sensor(REG_OV5647_LINE_MSB, intg_time_msb) ;
+	rc = ov5647_i2c_write_b_sensor(REG_OV5647_LINE_LSB, intg_time_lsb) ;
+
+	rc = ov5647_i2c_write_b_sensor(REG_OV5647_GAIN_MSB, gain_hsb) ;
+	rc = ov5647_i2c_write_b_sensor(REG_OV5647_GAIN_LSB, gain_lsb) ;
+	ov5647_group_hold_off();
+
+	msleep(500);
+	return rc;
+
+}
+
+static int32_t ov5647_move_focus(int direction, int32_t num_steps)
+{
+	uint8_t   code_val_msb = 0;
+	uint8_t   code_val_lsb = 0;
+	int16_t   step_direction, actual_step, next_position;
+	int rc;
+
+	if (num_steps == 0)
+		return 0;
+
+	if (direction == MOVE_NEAR)
+		step_direction = 20;
+	else if (direction == MOVE_FAR)
+		step_direction = -20;
+	else
+		return -EINVAL;
+
+	actual_step = (int16_t)(step_direction * num_steps);
+	next_position = (int16_t)ov5647_ctrl->curr_lens_pos + actual_step;
+	if (next_position < 0) {
+		CDBG(KERN_ERR "%s: OV5647 position(=%d) out of range",
+			__func__, next_position);
+		next_position = 0;
+	}
+	if (next_position > 0x3FF) {
+		CDBG(KERN_ERR "%s: OV5647 position(=%d) out of range",
+			__func__, next_position);
+		next_position = 0x3FF;
+	}
+	ov5647_ctrl->curr_lens_pos = next_position;
+
+	code_val_msb = (uint8_t)((ov5647_ctrl->curr_lens_pos & 0x03FF) >> 4);
+	code_val_lsb = (uint8_t)((ov5647_ctrl->curr_lens_pos & 0x000F) << 4);
+	code_val_lsb |= mode_mask;
+
+	rc = ov5647_af_i2c_write_b_sensor(code_val_msb, code_val_lsb);
+	/* DAC Setting */
+	if (rc != 0) {
+		CDBG(KERN_ERR "%s: WRITE ERROR lsb = 0x%x, msb = 0x%x",
+			__func__, code_val_lsb, code_val_msb);
+	} else {
+		CDBG(KERN_ERR "%s: Successful lsb = 0x%x, msb = 0x%x",
+			__func__, code_val_lsb, code_val_msb);
+		/* delay may set based on the steps moved
+		when I2C write successful */
+		msleep(100);
+	}
+	return 0;
+}
+
+static int32_t ov5647_set_default_focus(uint8_t af_step)
+{
+	uint8_t  code_val_msb = 0;
+	uint8_t  code_val_lsb = 0;
+	int rc = 0;
+
+	ov5647_ctrl->curr_lens_pos = 200;
+
+
+	code_val_msb = (ov5647_ctrl->curr_lens_pos & 0x03FF) >> 4;
+	code_val_lsb = (ov5647_ctrl->curr_lens_pos & 0x000F) << 4;
+	code_val_lsb |= mode_mask;
+
+	CDBG(KERN_ERR "ov5647_set_default_focus:lens pos = %d",
+		 ov5647_ctrl->curr_lens_pos);
+	rc = ov5647_af_i2c_write_b_sensor(code_val_msb, code_val_lsb);
+	/* DAC Setting */
+	if (rc != 0)
+		CDBG(KERN_ERR "%s: WRITE ERROR lsb = 0x%x, msb = 0x%x",
+			__func__, code_val_lsb, code_val_msb);
+	else
+		CDBG(KERN_ERR "%s: WRITE successful lsb = 0x%x, msb = 0x%x",
+			__func__, code_val_lsb, code_val_msb);
+
+	usleep_range(10000, 11000);
+	return 0;
+}
+
+static int32_t ov5647_test(enum ov5647_test_mode_t mo)
+{
+	int32_t rc = 0;
+
+	if (mo != TEST_OFF)
+		rc = ov5647_i2c_write_b_sensor(0x0601, (uint8_t) mo);
+
+	return rc;
+}
+
+static void ov5647_reset_sensor(void)
+{
+	ov5647_i2c_write_b_sensor(0x103, 0x1);
+}
+
+
+static int32_t ov5647_sensor_setting(int update_type, int rt)
+{
+
+	int32_t rc = 0;
+	struct msm_camera_csi_params ov5647_csi_params;
+
+	ov5647_stop_stream();
+
+	/* wait for clk/data really stop */
+	if ((rt == RES_CAPTURE) || (CSI_CONFIG == 0))
+		msleep(66);
+	else
+		msleep(266);
+
+	CDBG("CAMERA_DBG1: 0x4800 regVal:0x25\r\n");
+	ov5647_i2c_write_b_sensor(0x4800, 0x25);/* streaming off */
+
+	usleep_range(10000, 11000);
+
+	if (update_type == REG_INIT) {
+		ov5647_reset_sensor();
+		ov5647_i2c_write_b_table(ov5647_regs.rec_settings,
+			ov5647_regs.rec_size);
+		CSI_CONFIG = 0;
+	} else if (update_type == UPDATE_PERIODIC) {
+			/* turn off flash when preview */
+
+			if (rt == RES_PREVIEW) {
+				ov5647_i2c_write_b_table(ov5647_regs.reg_prev,
+					 ov5647_regs.reg_prev_size);
+				CDBG("CAMERA_DBG:preview settings...\r\n");
+			} else {
+				ov5647_i2c_write_b_table(ov5647_regs.reg_snap,
+					 ov5647_regs.reg_snap_size);
+				CDBG("CAMERA_DBG:snapshot settings...\r\n");
+			}
+
+			msleep(20);
+			if (!CSI_CONFIG) {
+				msm_camio_vfe_clk_rate_set(192000000);
+				ov5647_csi_params.data_format = CSI_8BIT;
+				ov5647_csi_params.lane_cnt = 2;
+				ov5647_csi_params.lane_assign = 0xe4;
+				ov5647_csi_params.dpcm_scheme = 0;
+				ov5647_csi_params.settle_cnt = 10;
+				rc = msm_camio_csi_config(&ov5647_csi_params);
+				msleep(20);
+				CSI_CONFIG = 1;
+			/* exit powerdown state */
+				ov5647_i2c_write_b_sensor(0x0100, 0x01);
+			}
+			CDBG("CAMERA_DBG: 0x4800 regVal:0x04\r\n");
+			/* streaming on */
+			ov5647_i2c_write_b_sensor(0x4800, 0x04);
+			msleep(266);
+			ov5647_start_stream();
+			msleep(30);
+	}
+	return rc;
+}
+
+static int32_t ov5647_video_config(int mode)
+{
+	int32_t rc = 0;
+	int rt;
+	CDBG("video config\n");
+	/* change sensor resolution if needed */
+	if (ov5647_ctrl->prev_res == QTR_SIZE)
+		rt = RES_PREVIEW;
+	else
+		rt = RES_CAPTURE;
+	if (ov5647_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+		return rc;
+	if (ov5647_ctrl->set_test) {
+		if (ov5647_test(ov5647_ctrl->set_test) < 0)
+			return  rc;
+	}
+
+	ov5647_ctrl->curr_res = ov5647_ctrl->prev_res;
+	ov5647_ctrl->sensormode = mode;
+	return rc;
+}
+
+static int32_t ov5647_snapshot_config(int mode)
+{
+	int32_t rc = 0;
+	int rt;
+
+	/*change sensor resolution if needed */
+	if (ov5647_ctrl->curr_res != ov5647_ctrl->pict_res) {
+		if (ov5647_ctrl->pict_res == QTR_SIZE)
+			rt = RES_PREVIEW;
+		else
+			rt = RES_CAPTURE;
+		if (ov5647_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+			return rc;
+	}
+
+	ov5647_ctrl->curr_res = ov5647_ctrl->pict_res;
+	ov5647_ctrl->sensormode = mode;
+	return rc;
+}
+
+static int32_t ov5647_raw_snapshot_config(int mode)
+{
+	int32_t rc = 0;
+	int rt;
+
+	/* change sensor resolution if needed */
+	if (ov5647_ctrl->curr_res != ov5647_ctrl->pict_res) {
+		if (ov5647_ctrl->pict_res == QTR_SIZE)
+			rt = RES_PREVIEW;
+		else
+			rt = RES_CAPTURE;
+		if (ov5647_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+			return rc;
+	}
+
+	ov5647_ctrl->curr_res = ov5647_ctrl->pict_res;
+	ov5647_ctrl->sensormode = mode;
+	return rc;
+}
+
+static int32_t ov5647_set_sensor_mode(int mode,
+		int res)
+{
+	int32_t rc = 0;
+
+	switch (mode) {
+	case SENSOR_PREVIEW_MODE:
+		rc = ov5647_video_config(mode);
+		break;
+	case SENSOR_SNAPSHOT_MODE:
+		rc = ov5647_snapshot_config(mode);
+		break;
+	case SENSOR_RAW_SNAPSHOT_MODE:
+		rc = ov5647_raw_snapshot_config(mode);
+		break;
+	default:
+		rc = -EINVAL;
+		break;
+	}
+	return rc;
+}
+
+static int32_t ov5647_power_down(void)
+{
+	ov5647_stop_stream();
+	return 0;
+}
+
+static int ov5647_probe_init_done(const struct msm_camera_sensor_info *data)
+{
+	CDBG("probe done\n");
+	gpio_direction_output(data->sensor_pwd, 1);
+	return 0;
+}
+
+static int ov5647_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+	int32_t rc = 0;
+	uint16_t regaddress1 = 0x300a;
+	uint16_t regaddress2 = 0x300b;
+	uint16_t chipid1 = 0;
+	uint16_t chipid2 = 0;
+
+	CDBG("%s: %d\n", __func__, __LINE__);
+
+	gpio_direction_output(data->sensor_pwd, 0);
+	usleep_range(4000, 4100);
+	gpio_direction_output(data->sensor_reset, 1);
+	usleep_range(2000, 2100);
+
+	ov5647_i2c_read(regaddress1, &chipid1);
+	if (chipid1 != 0x56) {
+		rc = -ENODEV;
+		pr_err("ov5647_probe_init_sensor fail chip id doesnot match\n");
+		goto init_probe_fail;
+	}
+
+	ov5647_i2c_read(regaddress2, &chipid2);
+	if (chipid2 != 0x47) {
+		rc = -ENODEV;
+		pr_err("ov5647_probe_init_sensor fail chip id doesnot match\n");
+		goto init_probe_fail;
+	}
+
+	pr_err("ID1: 0x%x\n", chipid1);
+	pr_err("ID2: 0x%x\n", chipid2);
+	goto init_probe_done;
+
+init_probe_fail:
+	pr_err(" ov5647_probe_init_sensor fails\n");
+	ov5647_probe_init_done(data);
+	return rc;
+init_probe_done:
+	pr_debug(" ov5647_probe_init_sensor finishes\n");
+	gpio_direction_output(data->sensor_pwd, 1);
+	return rc;
+}
+
+
+static int ov5647_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+	int32_t rc = 0;
+
+	CDBG("%s: %d\n", __func__, __LINE__);
+	CDBG("Calling ov5647_sensor_open_init\n");
+
+	ov5647_ctrl = kzalloc(sizeof(struct ov5647_ctrl_t), GFP_KERNEL);
+	if (!ov5647_ctrl) {
+		CDBG("ov5647_init failed!\n");
+		rc = -ENOMEM;
+		goto init_done;
+	}
+	ov5647_ctrl->fps_divider = 1 * 0x00000400;
+	ov5647_ctrl->pict_fps_divider = 1 * 0x00000400;
+	ov5647_ctrl->set_test = TEST_OFF;
+	ov5647_ctrl->prev_res = QTR_SIZE;
+	ov5647_ctrl->pict_res = FULL_SIZE;
+
+	if (data)
+		ov5647_ctrl->sensordata = data;
+
+	prev_frame_length_lines = 0x3d8;
+
+	prev_line_length_pck = 0x768*2;
+
+	snap_frame_length_lines = 0x7b0;
+
+	snap_line_length_pck = 0xa8c;
+
+	/* enable mclk first */
+	msm_camio_clk_rate_set(OV5647_MASTER_CLK_RATE);
+
+	gpio_direction_output(data->sensor_pwd, 1);
+	gpio_direction_output(data->sensor_reset, 0);
+	usleep_range(10000, 11000);
+	/* power on camera ldo and vreg */
+	if (ov5647_ctrl->sensordata->pmic_gpio_enable)
+		lcd_camera_power_onoff(1);
+	usleep_range(10000, 11000); /*waiting for ldo stable*/
+	gpio_direction_output(data->sensor_pwd, 0);
+	msleep(20);
+	gpio_direction_output(data->sensor_reset, 1);
+	msleep(25);
+
+	CDBG("init settings\n");
+	if (ov5647_ctrl->prev_res == QTR_SIZE)
+		rc = ov5647_sensor_setting(REG_INIT, RES_PREVIEW);
+	else
+		rc = ov5647_sensor_setting(REG_INIT, RES_CAPTURE);
+	ov5647_ctrl->fps = 30 * Q8;
+
+	/* enable AF actuator */
+	if (ov5647_ctrl->sensordata->vcm_enable) {
+		CDBG("enable AF actuator, gpio = %d\n",
+			 ov5647_ctrl->sensordata->vcm_pwd);
+		rc = gpio_request(ov5647_ctrl->sensordata->vcm_pwd,
+						"ov5647_af");
+		if (!rc)
+			gpio_direction_output(
+				ov5647_ctrl->sensordata->vcm_pwd,
+				 1);
+		else {
+			pr_err("ov5647_ctrl gpio request failed!\n");
+			goto init_fail;
+		}
+		msleep(20);
+		rc = ov5647_set_default_focus(0);
+		if (rc < 0) {
+			gpio_direction_output(ov5647_ctrl->sensordata->vcm_pwd,
+								0);
+			gpio_free(ov5647_ctrl->sensordata->vcm_pwd);
+		}
+	}
+	if (rc < 0)
+		goto init_fail;
+	else
+		goto init_done;
+init_fail:
+	CDBG("init_fail\n");
+	ov5647_probe_init_done(data);
+	/* No need to power OFF camera ldo and vreg
+	affects Display while resume */
+init_done:
+	CDBG("init_done\n");
+	return rc;
+}
+
+static int ov5647_i2c_remove(struct i2c_client *client)
+{
+	return 0;
+}
+
+static int ov5647_init_client(struct i2c_client *client)
+{
+	/* Initialize the MSM_CAMI2C Chip */
+	init_waitqueue_head(&ov5647_wait_queue);
+	return 0;
+}
+
+static int ov5647_af_init_client(struct i2c_client *client)
+{
+	/* Initialize the MSM_CAMI2C Chip */
+	init_waitqueue_head(&ov5647_af_wait_queue);
+	return 0;
+}
+
+static const struct i2c_device_id ov5647_af_i2c_id[] = {
+	{"ov5647_af", 0},
+	{ }
+};
+
+static int ov5647_af_i2c_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	int rc = 0;
+	CDBG("ov5647_af_probe called!\n");
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		CDBG("i2c_check_functionality failed\n");
+		goto probe_failure;
+	}
+
+	ov5647_af_sensorw = kzalloc(sizeof(struct ov5647_work_t), GFP_KERNEL);
+	if (!ov5647_af_sensorw) {
+		CDBG("kzalloc failed.\n");
+		rc = -ENOMEM;
+		goto probe_failure;
+	}
+
+	i2c_set_clientdata(client, ov5647_af_sensorw);
+	ov5647_af_init_client(client);
+	ov5647_af_client = client;
+
+	msleep(50);
+
+	CDBG("ov5647_af_probe successed! rc = %d\n", rc);
+	return 0;
+
+probe_failure:
+	CDBG("ov5647_af_probe failed! rc = %d\n", rc);
+	return rc;
+}
+
+static const struct i2c_device_id ov5647_i2c_id[] = {
+	{"ov5647", 0}, {}
+};
+
+static int ov5647_i2c_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	int rc = 0;
+	CDBG("ov5647_probe called!\n");
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		CDBG("i2c_check_functionality failed\n");
+		goto probe_failure;
+	}
+
+	ov5647_sensorw = kzalloc(sizeof(struct ov5647_work_t), GFP_KERNEL);
+	if (!ov5647_sensorw) {
+		CDBG("kzalloc failed.\n");
+		rc = -ENOMEM;
+		goto probe_failure;
+	}
+
+	i2c_set_clientdata(client, ov5647_sensorw);
+	ov5647_init_client(client);
+	ov5647_client = client;
+
+	msleep(50);
+
+	CDBG("ov5647_probe successed! rc = %d\n", rc);
+	return 0;
+
+probe_failure:
+	CDBG("ov5647_probe failed! rc = %d\n", rc);
+	return rc;
+}
+
+static int __devexit ov5647_remove(struct i2c_client *client)
+{
+	struct ov5647_work_t *sensorw = i2c_get_clientdata(client);
+	free_irq(client->irq, sensorw);
+	ov5647_client = NULL;
+	kfree(sensorw);
+	return 0;
+}
+
+static int __devexit ov5647_af_remove(struct i2c_client *client)
+{
+	struct ov5647_work_t *ov5647_af = i2c_get_clientdata(client);
+	free_irq(client->irq, ov5647_af);
+	ov5647_af_client = NULL;
+	kfree(ov5647_af);
+	return 0;
+}
+
+static struct i2c_driver ov5647_i2c_driver = {
+	.id_table = ov5647_i2c_id,
+	.probe  = ov5647_i2c_probe,
+	.remove = ov5647_i2c_remove,
+	.driver = {
+		.name = "ov5647",
+	},
+};
+
+static struct i2c_driver ov5647_af_i2c_driver = {
+	.id_table = ov5647_af_i2c_id,
+	.probe  = ov5647_af_i2c_probe,
+	.remove = __exit_p(ov5647_af_i2c_remove),
+	.driver = {
+		.name = "ov5647_af",
+	},
+};
+
+int ov5647_sensor_config(void __user *argp)
+{
+	struct sensor_cfg_data cdata;
+	long   rc = 0;
+	if (copy_from_user(&cdata,
+				(void *)argp,
+				sizeof(struct sensor_cfg_data)))
+		return -EFAULT;
+	mutex_lock(&ov5647_mut);
+	CDBG("ov5647_sensor_config: cfgtype = %d\n",
+			cdata.cfgtype);
+	switch (cdata.cfgtype) {
+	case CFG_GET_PICT_FPS:
+		ov5647_get_pict_fps(
+			cdata.cfg.gfps.prevfps,
+			&(cdata.cfg.gfps.pictfps));
+
+		if (copy_to_user((void *)argp,
+			&cdata,
+			sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+	case CFG_GET_PREV_L_PF:
+		cdata.cfg.prevl_pf =
+			ov5647_get_prev_lines_pf();
+
+		if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+	case CFG_GET_PREV_P_PL:
+		cdata.cfg.prevp_pl =
+			ov5647_get_prev_pixels_pl();
+
+		if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+	case CFG_GET_PICT_L_PF:
+		cdata.cfg.pictl_pf =
+			ov5647_get_pict_lines_pf();
+
+		if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+	case CFG_GET_PICT_P_PL:
+		cdata.cfg.pictp_pl =
+			ov5647_get_pict_pixels_pl();
+		if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+	case CFG_GET_PICT_MAX_EXP_LC:
+		cdata.cfg.pict_max_exp_lc =
+			ov5647_get_pict_max_exp_lc();
+
+		if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+	case CFG_SET_FPS:
+	case CFG_SET_PICT_FPS:
+		rc = ov5647_set_fps(&(cdata.cfg.fps));
+		break;
+	case CFG_SET_EXP_GAIN:
+		rc = ov5647_write_exp_gain(cdata.cfg.exp_gain.gain,
+				cdata.cfg.exp_gain.line);
+		break;
+	case CFG_SET_PICT_EXP_GAIN:
+		rc = ov5647_set_pict_exp_gain(cdata.cfg.exp_gain.gain,
+				cdata.cfg.exp_gain.line);
+		break;
+	case CFG_SET_MODE:
+		rc = ov5647_set_sensor_mode(cdata.mode, cdata.rs);
+		break;
+	case CFG_PWR_DOWN:
+		rc = ov5647_power_down();
+		break;
+	case CFG_MOVE_FOCUS:
+		rc = ov5647_move_focus(cdata.cfg.focus.dir,
+				cdata.cfg.focus.steps);
+		break;
+	case CFG_SET_DEFAULT_FOCUS:
+		rc = ov5647_set_default_focus(cdata.cfg.focus.steps);
+		break;
+
+	case CFG_GET_AF_MAX_STEPS:
+		cdata.max_steps = OV5647_TOTAL_STEPS_NEAR_TO_FAR;
+		if (copy_to_user((void *)argp,
+					&cdata,
+				sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+	case CFG_SET_EFFECT:
+		rc = ov5647_set_default_focus(cdata.cfg.effect);
+		break;
+	default:
+		rc = -EFAULT;
+		break;
+	}
+	mutex_unlock(&ov5647_mut);
+
+	return rc;
+}
+
+static int ov5647_sensor_release(void)
+{
+	int rc = -EBADF;
+	unsigned short rdata;
+
+	mutex_lock(&ov5647_mut);
+	ov5647_power_down();
+	msleep(20);
+	ov5647_i2c_read(0x3018, &rdata);
+	rdata |= 0x18; /*set bit 3 bit 4 to 1*/
+	ov5647_i2c_write_b_sensor(0x3018, rdata);/*write back*/
+	msleep(20);
+
+	gpio_set_value(ov5647_ctrl->sensordata->sensor_pwd, 1);
+	usleep_range(5000, 5100);
+	if (ov5647_ctrl->sensordata->vcm_enable) {
+		gpio_direction_output(ov5647_ctrl->sensordata->vcm_pwd, 0);
+		gpio_free(ov5647_ctrl->sensordata->vcm_pwd);
+	}
+
+	/* No need to power OFF camera ldo and vreg
+	affects Display while resume */
+
+	kfree(ov5647_ctrl);
+	ov5647_ctrl = NULL;
+	CDBG("ov5647_release completed\n");
+	mutex_unlock(&ov5647_mut);
+
+	return rc;
+}
+
+static int ov5647_sensor_probe(const struct msm_camera_sensor_info *info,
+		struct msm_sensor_ctrl *s)
+{
+	int rc = 0;
+
+	CDBG("%s E\n", __func__);
+
+	gpio_direction_output(info->sensor_pwd, 1);
+	gpio_direction_output(info->sensor_reset, 0);
+	usleep_range(1000, 1100);
+	/* turn on ldo and vreg */
+	if (info->pmic_gpio_enable)
+		lcd_camera_power_onoff(1);
+
+	rc = i2c_add_driver(&ov5647_i2c_driver);
+	if (rc < 0 || ov5647_client == NULL) {
+		rc = -ENOTSUPP;
+		CDBG("I2C add driver ov5647 failed");
+		goto probe_fail_2;
+	}
+	if (info->vcm_enable) {
+		rc = i2c_add_driver(&ov5647_af_i2c_driver);
+		if (rc < 0 || ov5647_af_client == NULL) {
+			rc = -ENOTSUPP;
+			CDBG("I2C add driver ov5647 af failed");
+			goto probe_fail_3;
+		}
+	}
+	msm_camio_clk_rate_set(OV5647_MASTER_CLK_RATE);
+
+	rc = ov5647_probe_init_sensor(info);
+	if (rc < 0)
+		goto probe_fail_1;
+
+	s->s_init = ov5647_sensor_open_init;
+	s->s_release = ov5647_sensor_release;
+	s->s_config  = ov5647_sensor_config;
+	s->s_mount_angle = info->sensor_platform_info->mount_angle;
+	gpio_set_value(info->sensor_pwd, 1);
+	ov5647_probe_init_done(info);
+	/* turn off ldo and vreg */
+	if (info->pmic_gpio_enable)
+		lcd_camera_power_onoff(0);
+
+	CDBG("%s X", __func__);
+	return rc;
+
+probe_fail_3:
+	i2c_del_driver(&ov5647_af_i2c_driver);
+probe_fail_2:
+	i2c_del_driver(&ov5647_i2c_driver);
+probe_fail_1:
+	/* turn off ldo and vreg */
+	if (info->pmic_gpio_enable)
+		lcd_camera_power_onoff(0);
+	CDBG("ov5647_sensor_probe: SENSOR PROBE FAILS!\n");
+	CDBG("%s X", __func__);
+	return rc;
+}
+
+static int __devinit ov5647_probe(struct platform_device *pdev)
+{
+	return msm_camera_drv_start(pdev, ov5647_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+	.probe = ov5647_probe,
+	.driver = {
+		.name = "msm_camera_ov5647",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init ov5647_init(void)
+{
+	return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(ov5647_init);
+MODULE_DESCRIPTION("Omnivision 5 MP Bayer sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/ov5647.h b/drivers/media/video/msm/ov5647.h
new file mode 100644
index 0000000..b43f15c
--- /dev/null
+++ b/drivers/media/video/msm/ov5647.h
@@ -0,0 +1,92 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef OV5647_H
+#define OV5647_H
+#include <linux/types.h>
+#include <mach/board.h>
+
+extern struct ov5647_reg ov5647_regs;
+extern int lcd_camera_power_onoff(int on);
+extern struct rw_semaphore leds_list_lock;
+extern struct list_head leds_list;
+
+struct ov5647_i2c_reg_conf {
+	unsigned short waddr;
+	unsigned short wdata;
+};
+
+enum ov5647_test_mode_t {
+	TEST_OFF,
+	TEST_1,
+	TEST_2,
+	TEST_3
+};
+
+enum ov5647_resolution_t {
+	QTR_SIZE,
+	FULL_SIZE,
+	INVALID_SIZE
+};
+enum ov5647_setting {
+	RES_PREVIEW,
+	RES_CAPTURE
+};
+enum ov5647_reg_update {
+	/* Sensor egisters that need to be updated during initialization */
+	REG_INIT,
+	/* Sensor egisters that needs periodic I2C writes */
+	UPDATE_PERIODIC,
+	/* All the sensor Registers will be updated */
+	UPDATE_ALL,
+	/* Not valid update */
+	UPDATE_INVALID
+};
+
+enum ov5647_reg_pll {
+	E013_VT_PIX_CLK_DIV,
+	E013_VT_SYS_CLK_DIV,
+	E013_PRE_PLL_CLK_DIV,
+	E013_PLL_MULTIPLIER,
+	E013_OP_PIX_CLK_DIV,
+	E013_OP_SYS_CLK_DIV
+};
+
+enum ov5647_reg_mode {
+	E013_X_ADDR_START,
+	E013_X_ADDR_END,
+	E013_Y_ADDR_START,
+	E013_Y_ADDR_END,
+	E013_X_OUTPUT_SIZE,
+	E013_Y_OUTPUT_SIZE,
+	E013_DATAPATH_SELECT,
+	E013_READ_MODE,
+	E013_ANALOG_CONTROL5,
+	E013_DAC_LD_4_5,
+	E013_SCALING_MODE,
+	E013_SCALE_M,
+	E013_LINE_LENGTH_PCK,
+	E013_FRAME_LENGTH_LINES,
+	E013_COARSE_INTEGRATION_TIME,
+	E013_FINE_INTEGRATION_TIME,
+	E013_FINE_CORRECTION
+};
+
+struct ov5647_reg {
+	const struct ov5647_i2c_reg_conf *rec_settings;
+	const unsigned short rec_size;
+	const struct ov5647_i2c_reg_conf *reg_prev;
+	const unsigned short reg_prev_size;
+	const struct ov5647_i2c_reg_conf *reg_snap;
+	const unsigned short reg_snap_size;
+};
+#endif /* OV5647_H */
diff --git a/drivers/media/video/msm/ov5647_reg.c b/drivers/media/video/msm/ov5647_reg.c
new file mode 100644
index 0000000..4a0fed4
--- /dev/null
+++ b/drivers/media/video/msm/ov5647_reg.c
@@ -0,0 +1,219 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+#include "ov5647.h"
+struct ov5647_i2c_reg_conf ov5647_prev_settings[] = {
+	/*1280*960 Reference Setting 24M MCLK 2lane 280Mbps/lane 30fps
+	for back to preview*/
+	{0x3035, 0x21},
+	{0x3036, 0x37},
+	{0x3821, 0x07},
+	{0x3820, 0x41},
+	{0x3612, 0x09},
+	{0x3618, 0x00},
+	{0x380c, 0x07},
+	{0x380d, 0x68},
+	{0x380e, 0x03},
+	{0x380f, 0xd8},
+	{0x3814, 0x31},
+	{0x3815, 0x31},
+	{0x3709, 0x52},
+	{0x3808, 0x05},
+	{0x3809, 0x00},
+	{0x380a, 0x03},
+	{0x380b, 0xc0},
+	{0x3800, 0x00},
+	{0x3801, 0x18},
+	{0x3802, 0x00},
+	{0x3803, 0x0e},
+	{0x3804, 0x0a},
+	{0x3805, 0x27},
+	{0x3806, 0x07},
+	{0x3807, 0x95},
+	{0x4004, 0x02},
+};
+
+struct ov5647_i2c_reg_conf ov5647_snap_settings[] = {
+	/*2608*1952 Reference Setting 24M MCLK 2lane 280Mbps/lane 30fps*/
+	{0x3035, 0x21},
+	{0x3036, 0x4f},
+	{0x3821, 0x06},
+	{0x3820, 0x00},
+	{0x3612, 0x0b},
+	{0x3618, 0x04},
+	{0x380c, 0x0a},
+	{0x380d, 0x8c},
+	{0x380e, 0x07},
+	{0x380f, 0xb0},
+	{0x3814, 0x11},
+	{0x3815, 0x11},
+	{0x3709, 0x12},
+	{0x3808, 0x0a},
+	{0x3809, 0x30},
+	{0x380a, 0x07},
+	{0x380b, 0xa0},
+	{0x3800, 0x00},
+	{0x3801, 0x04},
+	{0x3802, 0x00},
+	{0x3803, 0x00},
+	{0x3804, 0x0a},
+	{0x3805, 0x3b},
+	{0x3806, 0x07},
+	{0x3807, 0xa3},
+	{0x4004, 0x04},
+};
+
+struct ov5647_i2c_reg_conf ov5647_recommend_settings[] = {
+	{0x3035, 0x11},
+	{0x303c, 0x11},
+	{0x370c, 0x03},
+	{0x5000, 0x06},
+	{0x5003, 0x08},
+	{0x5a00, 0x08},
+	{0x3000, 0xff},
+	{0x3001, 0xff},
+	{0x3002, 0xff},
+	{0x301d, 0xf0},
+	{0x3a18, 0x00},
+	{0x3a19, 0xf8},
+	{0x3c01, 0x80},
+	{0x3b07, 0x0c},
+	{0x3708, 0x64},
+	{0x3630, 0x2e},
+	{0x3632, 0xe2},
+	{0x3633, 0x23},
+	{0x3634, 0x44},
+	{0x3620, 0x64},
+	{0x3621, 0xe0},
+	{0x3600, 0x37},
+	{0x3704, 0xa0},
+	{0x3703, 0x5a},
+	{0x3715, 0x78},
+	{0x3717, 0x01},
+	{0x3731, 0x02},
+	{0x370b, 0x60},
+	{0x3705, 0x1a},
+	{0x3f05, 0x02},
+	{0x3f06, 0x10},
+	{0x3f01, 0x0a},
+	{0x3a08, 0x01},
+	{0x3a0f, 0x58},
+	{0x3a10, 0x50},
+	{0x3a1b, 0x58},
+	{0x3a1e, 0x50},
+	{0x3a11, 0x60},
+	{0x3a1f, 0x28},
+	{0x4001, 0x02},
+	{0x4000, 0x09},
+	{0x3000, 0x00},
+	{0x3001, 0x00},
+	{0x3002, 0x00},
+	{0x3017, 0xe0},
+	{0x301c, 0xfc},
+	{0x3636, 0x06},
+	{0x3016, 0x08},
+	{0x3827, 0xec},
+	{0x3018, 0x44},
+	{0x3035, 0x21},
+	{0x3106, 0xf5},
+	{0x3034, 0x18},
+	{0x301c, 0xf8},
+	/*lens setting*/
+	{0x5000, 0x86},
+	{0x5800, 0x11},
+	{0x5801, 0x0c},
+	{0x5802, 0x0a},
+	{0x5803, 0x0b},
+	{0x5804, 0x0d},
+	{0x5805, 0x13},
+	{0x5806, 0x09},
+	{0x5807, 0x05},
+	{0x5808, 0x03},
+	{0x5809, 0x03},
+	{0x580a, 0x06},
+	{0x580b, 0x08},
+	{0x580c, 0x05},
+	{0x580d, 0x01},
+	{0x580e, 0x00},
+	{0x580f, 0x00},
+	{0x5810, 0x02},
+	{0x5811, 0x06},
+	{0x5812, 0x05},
+	{0x5813, 0x01},
+	{0x5814, 0x00},
+	{0x5815, 0x00},
+	{0x5816, 0x02},
+	{0x5817, 0x06},
+	{0x5818, 0x09},
+	{0x5819, 0x05},
+	{0x581a, 0x04},
+	{0x581b, 0x04},
+	{0x581c, 0x06},
+	{0x581d, 0x09},
+	{0x581e, 0x11},
+	{0x581f, 0x0c},
+	{0x5820, 0x0b},
+	{0x5821, 0x0b},
+	{0x5822, 0x0d},
+	{0x5823, 0x13},
+	{0x5824, 0x22},
+	{0x5825, 0x26},
+	{0x5826, 0x26},
+	{0x5827, 0x24},
+	{0x5828, 0x24},
+	{0x5829, 0x24},
+	{0x582a, 0x22},
+	{0x582b, 0x20},
+	{0x582c, 0x22},
+	{0x582d, 0x26},
+	{0x582e, 0x22},
+	{0x582f, 0x22},
+	{0x5830, 0x42},
+	{0x5831, 0x22},
+	{0x5832, 0x02},
+	{0x5833, 0x24},
+	{0x5834, 0x22},
+	{0x5835, 0x22},
+	{0x5836, 0x22},
+	{0x5837, 0x26},
+	{0x5838, 0x42},
+	{0x5839, 0x26},
+	{0x583a, 0x06},
+	{0x583b, 0x26},
+	{0x583c, 0x24},
+	{0x583d, 0xce},
+	/* manual AWB,manual AE,close Lenc,open WBC*/
+	{0x3503, 0x03}, /*manual AE*/
+	{0x3501, 0x10},
+	{0x3502, 0x80},
+	{0x350a, 0x00},
+	{0x350b, 0x7f},
+	{0x5001, 0x01}, /*manual AWB*/
+	{0x5180, 0x08},
+	{0x5186, 0x04},
+	{0x5187, 0x00},
+	{0x5188, 0x04},
+	{0x5189, 0x00},
+	{0x518a, 0x04},
+	{0x518b, 0x00},
+	{0x5000, 0x06}, /*No lenc,WBC on*/
+};
+
+struct ov5647_reg ov5647_regs = {
+	.rec_settings = &ov5647_recommend_settings[0],
+	.rec_size = ARRAY_SIZE(ov5647_recommend_settings),
+	.reg_prev = &ov5647_prev_settings[0],
+	.reg_prev_size = ARRAY_SIZE(ov5647_prev_settings),
+	.reg_snap = &ov5647_snap_settings[0],
+	.reg_snap_size = ARRAY_SIZE(ov5647_snap_settings),
+};
diff --git a/drivers/media/video/msm/ov7692.c b/drivers/media/video/msm/ov7692.c
new file mode 100644
index 0000000..7696b44
--- /dev/null
+++ b/drivers/media/video/msm/ov7692.c
@@ -0,0 +1,597 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <media/msm_camera.h>
+#include <mach/camera.h>
+#include "ov7692.h"
+
+/*=============================================================
+	SENSOR REGISTER DEFINES
+==============================================================*/
+#define Q8    0x00000100
+
+/* Omnivision8810 product ID register address */
+#define REG_OV7692_MODEL_ID_MSB                       0x0A
+#define REG_OV7692_MODEL_ID_LSB                       0x0B
+
+#define OV7692_MODEL_ID                       0x7692
+/* Omnivision8810 product ID */
+
+/* Time in milisecs for waiting for the sensor to reset */
+#define OV7692_RESET_DELAY_MSECS    66
+#define OV7692_DEFAULT_CLOCK_RATE   24000000
+/* Registers*/
+
+/* Color bar pattern selection */
+#define OV7692_COLOR_BAR_PATTERN_SEL_REG     0x82
+/* Color bar enabling control */
+#define OV7692_COLOR_BAR_ENABLE_REG           0x601
+/* Time in milisecs for waiting for the sensor to reset*/
+#define OV7692_RESET_DELAY_MSECS    66
+
+/*============================================================================
+							DATA DECLARATIONS
+============================================================================*/
+/*  96MHz PCLK @ 24MHz MCLK */
+struct reg_addr_val_pair_struct ov7692_init_settings_array[] = {
+    {0x12, 0x80},
+    {0x0e, 0x08},
+    {0x69, 0x52},
+    {0x1e, 0xb3},
+    {0x48, 0x42},
+    {0xff, 0x01},
+    {0xae, 0xa0},
+    {0xa8, 0x26},
+    {0xb4, 0xc0},
+    {0xb5, 0x40},
+    {0xff, 0x00},
+    {0x0c, 0x00},
+    {0x62, 0x10},
+    {0x12, 0x00},
+    {0x17, 0x65},
+    {0x18, 0xa4},
+    {0x19, 0x0a},
+    {0x1a, 0xf6},
+    {0x3e, 0x30},
+    {0x64, 0x0a},
+    {0xff, 0x01},
+    {0xb4, 0xc0},
+    {0xff, 0x00},
+    {0x67, 0x20},
+    {0x81, 0x3f},
+    {0xcc, 0x02},
+    {0xcd, 0x80},
+    {0xce, 0x01},
+    {0xcf, 0xe0},
+    {0xc8, 0x02},
+    {0xc9, 0x80},
+    {0xca, 0x01},
+    {0xcb, 0xe0},
+    {0xd0, 0x48},
+    {0x82, 0x03},
+    {0x0e, 0x00},
+    {0x70, 0x00},
+    {0x71, 0x34},
+    {0x74, 0x28},
+    {0x75, 0x98},
+    {0x76, 0x00},
+    {0x77, 0x64},
+    {0x78, 0x01},
+    {0x79, 0xc2},
+    {0x7a, 0x4e},
+    {0x7b, 0x1f},
+    {0x7c, 0x00},
+    {0x11, 0x00},
+    {0x20, 0x00},
+    {0x21, 0x23},
+    {0x50, 0x9a},
+    {0x51, 0x80},
+    {0x4c, 0x7d},
+    {0x0e, 0x00},
+    {0x80, 0x7f},
+    {0x85, 0x10},
+    {0x86, 0x00},
+    {0x87, 0x00},
+    {0x88, 0x00},
+    {0x89, 0x2a},
+    {0x8a, 0x26},
+    {0x8b, 0x22},
+    {0xbb, 0x7a},
+    {0xbc, 0x69},
+    {0xbd, 0x11},
+    {0xbe, 0x13},
+    {0xbf, 0x81},
+    {0xc0, 0x96},
+    {0xc1, 0x1e},
+    {0xb7, 0x05},
+    {0xb8, 0x09},
+    {0xb9, 0x00},
+    {0xba, 0x18},
+    {0x5a, 0x1f},
+    {0x5b, 0x9f},
+    {0x5c, 0x6a},
+    {0x5d, 0x42},
+    {0x24, 0x78},
+    {0x25, 0x68},
+    {0x26, 0xb3},
+    {0xa3, 0x0b},
+    {0xa4, 0x15},
+    {0xa5, 0x2a},
+    {0xa6, 0x51},
+    {0xa7, 0x63},
+    {0xa8, 0x74},
+    {0xa9, 0x83},
+    {0xaa, 0x91},
+    {0xab, 0x9e},
+    {0xac, 0xaa},
+    {0xad, 0xbe},
+    {0xae, 0xce},
+    {0xaf, 0xe5},
+    {0xb0, 0xf3},
+    {0xb1, 0xfb},
+    {0xb2, 0x06},
+    {0x8c, 0x5c},
+    {0x8d, 0x11},
+    {0x8e, 0x12},
+    {0x8f, 0x19},
+    {0x90, 0x50},
+    {0x91, 0x20},
+    {0x92, 0x96},
+    {0x93, 0x80},
+    {0x94, 0x13},
+    {0x95, 0x1b},
+    {0x96, 0xff},
+    {0x97, 0x00},
+    {0x98, 0x3d},
+    {0x99, 0x36},
+    {0x9a, 0x51},
+    {0x9b, 0x43},
+    {0x9c, 0xf0},
+    {0x9d, 0xf0},
+    {0x9e, 0xf0},
+    {0x9f, 0xff},
+    {0xa0, 0x68},
+    {0xa1, 0x62},
+    {0xa2, 0x0e},
+};
+
+static bool OV7692_CSI_CONFIG;
+/* 816x612, 24MHz MCLK 96MHz PCLK */
+uint32_t OV7692_FULL_SIZE_WIDTH        = 640;
+uint32_t OV7692_FULL_SIZE_HEIGHT       = 480;
+
+uint32_t OV7692_QTR_SIZE_WIDTH         = 640;
+uint32_t OV7692_QTR_SIZE_HEIGHT        = 480;
+
+uint32_t OV7692_HRZ_FULL_BLK_PIXELS    = 16;
+uint32_t OV7692_VER_FULL_BLK_LINES     = 12;
+uint32_t OV7692_HRZ_QTR_BLK_PIXELS     = 16;
+uint32_t OV7692_VER_QTR_BLK_LINES      = 12;
+
+struct ov7692_work_t {
+	struct work_struct work;
+};
+static struct  ov7692_work_t *ov7692_sensorw;
+static struct  i2c_client *ov7692_client;
+struct ov7692_ctrl_t {
+	const struct  msm_camera_sensor_info *sensordata;
+	uint32_t sensormode;
+	uint32_t fps_divider;		/* init to 1 * 0x00000400 */
+	uint32_t pict_fps_divider;	/* init to 1 * 0x00000400 */
+	uint32_t fps;
+	int32_t  curr_lens_pos;
+	uint32_t curr_step_pos;
+	uint32_t my_reg_gain;
+	uint32_t my_reg_line_count;
+	uint32_t total_lines_per_frame;
+	enum ov7692_resolution_t prev_res;
+	enum ov7692_resolution_t pict_res;
+	enum ov7692_resolution_t curr_res;
+	enum ov7692_test_mode_t  set_test;
+	unsigned short imgaddr;
+};
+static struct ov7692_ctrl_t *ov7692_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(ov7692_wait_queue);
+DEFINE_MUTEX(ov7692_mut);
+
+/*=============================================================*/
+
+static int ov7692_i2c_rxdata(unsigned short saddr,
+	unsigned char *rxdata, int length)
+{
+	struct i2c_msg msgs[] = {
+		{
+			.addr  = saddr,
+			.flags = 0,
+			.len   = 1,
+			.buf   = rxdata,
+		},
+		{
+			.addr  = saddr,
+			.flags = I2C_M_RD,
+			.len   = 1,
+			.buf   = rxdata,
+		},
+	};
+	if (i2c_transfer(ov7692_client->adapter, msgs, 2) < 0) {
+		CDBG("ov7692_i2c_rxdata failed!\n");
+		return -EIO;
+	}
+	return 0;
+}
+static int32_t ov7692_i2c_txdata(unsigned short saddr,
+				unsigned char *txdata, int length)
+{
+	struct i2c_msg msg[] = {
+		{
+			.addr = saddr,
+			.flags = 0,
+			.len = 2,
+			.buf = txdata,
+		 },
+	};
+	if (i2c_transfer(ov7692_client->adapter, msg, 1) < 0) {
+		CDBG("ov7692_i2c_txdata faild 0x%x\n", ov7692_client->addr);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int32_t ov7692_i2c_read(uint8_t raddr,
+	uint8_t *rdata, int rlen)
+{
+	int32_t rc = 0;
+	unsigned char buf[1];
+	if (!rdata)
+		return -EIO;
+	memset(buf, 0, sizeof(buf));
+	buf[0] = raddr;
+	rc = ov7692_i2c_rxdata(ov7692_client->addr >> 1, buf, rlen);
+	if (rc < 0) {
+		CDBG("ov7692_i2c_read 0x%x failed!\n", raddr);
+		return rc;
+	}
+	*rdata = buf[0];
+	return rc;
+}
+static int32_t ov7692_i2c_write_b_sensor(uint8_t waddr, uint8_t bdata)
+{
+	int32_t rc = -EFAULT;
+	unsigned char buf[2];
+	memset(buf, 0, sizeof(buf));
+	buf[0] = waddr;
+	buf[1] = bdata;
+	CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, bdata);
+	rc = ov7692_i2c_txdata(ov7692_client->addr >> 1, buf, 2);
+	if (rc < 0)
+		CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+			waddr, bdata);
+	return rc;
+}
+
+static int32_t ov7692_sensor_setting(int update_type, int rt)
+{
+	int32_t i, array_length;
+	int32_t rc = 0;
+	struct msm_camera_csi_params ov7692_csi_params;
+	switch (update_type) {
+	case REG_INIT:
+		OV7692_CSI_CONFIG = 0;
+		ov7692_i2c_write_b_sensor(0x0e, 0x08);
+		return rc;
+		break;
+	case UPDATE_PERIODIC:
+		if (!OV7692_CSI_CONFIG) {
+			ov7692_csi_params.lane_cnt = 1;
+			ov7692_csi_params.data_format = CSI_8BIT;
+			ov7692_csi_params.lane_assign = 0xe4;
+			ov7692_csi_params.dpcm_scheme = 0;
+			ov7692_csi_params.settle_cnt = 0x14;
+
+			rc = msm_camio_csi_config(&ov7692_csi_params);
+			msleep(10);
+			array_length = sizeof(ov7692_init_settings_array) /
+				sizeof(ov7692_init_settings_array[0]);
+			for (i = 0; i < array_length; i++) {
+				rc = ov7692_i2c_write_b_sensor(
+					ov7692_init_settings_array[i].reg_addr,
+					ov7692_init_settings_array[i].reg_val);
+				if (rc < 0)
+					return rc;
+			}
+			OV7692_CSI_CONFIG = 1;
+			msleep(20);
+			return rc;
+		}
+		break;
+	default:
+		rc = -EINVAL;
+		break;
+	}
+	return rc;
+}
+
+static int32_t ov7692_video_config(int mode)
+{
+	int32_t rc = 0;
+	int rt;
+	/* change sensor resolution if needed */
+	rt = RES_PREVIEW;
+
+	if (ov7692_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+		return rc;
+	ov7692_ctrl->curr_res = ov7692_ctrl->prev_res;
+	ov7692_ctrl->sensormode = mode;
+	return rc;
+}
+
+static int32_t ov7692_set_sensor_mode(int mode,
+	int res)
+{
+	int32_t rc = 0;
+	switch (mode) {
+	case SENSOR_PREVIEW_MODE:
+		rc = ov7692_video_config(mode);
+		break;
+	case SENSOR_SNAPSHOT_MODE:
+	case SENSOR_RAW_SNAPSHOT_MODE:
+		break;
+	default:
+		rc = -EINVAL;
+		break;
+	}
+	return rc;
+}
+static int32_t ov7692_power_down(void)
+{
+	return 0;
+}
+
+static int ov7692_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+	uint8_t model_id_msb, model_id_lsb = 0;
+	uint16_t model_id;
+	int32_t rc = 0;
+	/*The reset pin is not physically connected to the sensor.
+	The standby pin will do the reset hence there is no need
+	to request the gpio reset*/
+
+	/* Read sensor Model ID: */
+	rc = ov7692_i2c_read(REG_OV7692_MODEL_ID_MSB, &model_id_msb, 1);
+	if (rc < 0)
+		goto init_probe_fail;
+	rc = ov7692_i2c_read(REG_OV7692_MODEL_ID_LSB, &model_id_lsb, 1);
+	if (rc < 0)
+		goto init_probe_fail;
+	model_id = (model_id_msb << 8) | ((model_id_lsb & 0x00FF)) ;
+	CDBG("ov7692 model_id = 0x%x, 0x%x, 0x%x\n",
+		 model_id, model_id_msb, model_id_lsb);
+	/* 4. Compare sensor ID to OV7692 ID: */
+	if (model_id != OV7692_MODEL_ID) {
+		rc = -ENODEV;
+		goto init_probe_fail;
+	}
+	goto init_probe_done;
+init_probe_fail:
+	pr_warning(" ov7692_probe_init_sensor fails\n");
+init_probe_done:
+	CDBG(" ov7692_probe_init_sensor finishes\n");
+	return rc;
+}
+
+int ov7692_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+	int32_t rc = 0;
+
+	CDBG("%s: %d\n", __func__, __LINE__);
+	CDBG("Calling ov7692_sensor_open_init\n");
+	ov7692_ctrl = kzalloc(sizeof(struct ov7692_ctrl_t), GFP_KERNEL);
+	if (!ov7692_ctrl) {
+		CDBG("ov7692_init failed!\n");
+		rc = -ENOMEM;
+		goto init_done;
+	}
+	ov7692_ctrl->fps_divider = 1 * 0x00000400;
+	ov7692_ctrl->pict_fps_divider = 1 * 0x00000400;
+	ov7692_ctrl->fps = 30 * Q8;
+	ov7692_ctrl->set_test = TEST_OFF;
+	ov7692_ctrl->prev_res = QTR_SIZE;
+	ov7692_ctrl->pict_res = FULL_SIZE;
+	ov7692_ctrl->curr_res = INVALID_SIZE;
+
+	if (data)
+		ov7692_ctrl->sensordata = data;
+
+	/* enable mclk first */
+
+	msm_camio_clk_rate_set(24000000);
+	msleep(20);
+
+	rc = ov7692_probe_init_sensor(data);
+	if (rc < 0) {
+		CDBG("Calling ov7692_sensor_open_init fail\n");
+		goto init_fail;
+	}
+
+	rc = ov7692_sensor_setting(REG_INIT, RES_PREVIEW);
+	if (rc < 0)
+		goto init_fail;
+	else
+		goto init_done;
+
+init_fail:
+	CDBG(" ov7692_sensor_open_init fail\n");
+	kfree(ov7692_ctrl);
+init_done:
+	CDBG("ov7692_sensor_open_init done\n");
+	return rc;
+}
+
+static int ov7692_init_client(struct i2c_client *client)
+{
+	/* Initialize the MSM_CAMI2C Chip */
+	init_waitqueue_head(&ov7692_wait_queue);
+	return 0;
+}
+
+static const struct i2c_device_id ov7692_i2c_id[] = {
+	{"ov7692", 0},
+	{ }
+};
+
+static int ov7692_i2c_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	int rc = 0;
+	CDBG("ov7692_i2c_probe called!\n");
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		CDBG("i2c_check_functionality failed\n");
+		goto probe_failure;
+	}
+
+	ov7692_sensorw = kzalloc(sizeof(struct ov7692_work_t), GFP_KERNEL);
+	if (!ov7692_sensorw) {
+		CDBG("kzalloc failed.\n");
+		rc = -ENOMEM;
+		goto probe_failure;
+	}
+
+	i2c_set_clientdata(client, ov7692_sensorw);
+	ov7692_init_client(client);
+	ov7692_client = client;
+
+	CDBG("ov7692_i2c_probe success! rc = %d\n", rc);
+	return 0;
+
+probe_failure:
+	CDBG("ov7692_i2c_probe failed! rc = %d\n", rc);
+	return rc;
+}
+
+static int __exit ov7692_remove(struct i2c_client *client)
+{
+	struct ov7692_work_t_t *sensorw = i2c_get_clientdata(client);
+	free_irq(client->irq, sensorw);
+	ov7692_client = NULL;
+	kfree(sensorw);
+	return 0;
+}
+
+static struct i2c_driver ov7692_i2c_driver = {
+	.id_table = ov7692_i2c_id,
+	.probe  = ov7692_i2c_probe,
+	.remove = __exit_p(ov7692_i2c_remove),
+	.driver = {
+		.name = "ov7692",
+	},
+};
+
+int ov7692_sensor_config(void __user *argp)
+{
+	struct sensor_cfg_data cdata;
+	long   rc = 0;
+	if (copy_from_user(&cdata,
+		(void *)argp,
+		sizeof(struct sensor_cfg_data)))
+		return -EFAULT;
+	mutex_lock(&ov7692_mut);
+	CDBG("ov7692_sensor_config: cfgtype = %d\n",
+	cdata.cfgtype);
+	switch (cdata.cfgtype) {
+	case CFG_SET_MODE:
+		rc = ov7692_set_sensor_mode(cdata.mode,
+			cdata.rs);
+		break;
+	case CFG_PWR_DOWN:
+		rc = ov7692_power_down();
+		break;
+	case CFG_SET_EFFECT:
+		break;
+	default:
+		rc = -EFAULT;
+		break;
+	}
+
+	mutex_unlock(&ov7692_mut);
+
+	return rc;
+}
+static int ov7692_sensor_release(void)
+{
+	int rc = -EBADF;
+	mutex_lock(&ov7692_mut);
+	ov7692_power_down();
+	kfree(ov7692_ctrl);
+	ov7692_ctrl = NULL;
+	CDBG("ov7692_release completed\n");
+	mutex_unlock(&ov7692_mut);
+
+	return rc;
+}
+
+static int ov7692_sensor_probe(const struct msm_camera_sensor_info *info,
+		struct msm_sensor_ctrl *s)
+{
+	int rc = 0;
+	rc = i2c_add_driver(&ov7692_i2c_driver);
+	if (rc < 0 || ov7692_client == NULL) {
+		rc = -ENOTSUPP;
+		goto probe_fail;
+	}
+	msm_camio_clk_rate_set(24000000);
+	rc = ov7692_probe_init_sensor(info);
+	if (rc < 0)
+		goto probe_fail;
+	s->s_init = ov7692_sensor_open_init;
+	s->s_release = ov7692_sensor_release;
+	s->s_config  = ov7692_sensor_config;
+	s->s_camera_type = FRONT_CAMERA_2D;
+	s->s_mount_angle = 0;
+	return rc;
+
+probe_fail:
+	CDBG("ov7692_sensor_probe: SENSOR PROBE FAILS!\n");
+	i2c_del_driver(&ov7692_i2c_driver);
+	return rc;
+}
+
+static int __ov7692_probe(struct platform_device *pdev)
+{
+
+	return msm_camera_drv_start(pdev, ov7692_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+	.probe = __ov7692_probe,
+	.driver = {
+		.name = "msm_camera_ov7692",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init ov7692_init(void)
+{
+	return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(ov7692_init);
+
+MODULE_DESCRIPTION("OMNI VGA YUV sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/ov7692.h b/drivers/media/video/msm/ov7692.h
new file mode 100644
index 0000000..fc9cf1c
--- /dev/null
+++ b/drivers/media/video/msm/ov7692.h
@@ -0,0 +1,666 @@
+/* Copyright (c) 2010, 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef OV7692_H
+#define OV7692_H
+#include <linux/types.h>
+#include <mach/board.h>
+
+#define INVMASK(v)  (0xff-v)
+#define OV7692Core_WritePREG(pTbl)  OV7692_WritePRegs \
+			(pTbl, sizeof(pTbl)/sizeof(pTbl[0]))
+
+extern int lcd_camera_power_onoff(int on);
+struct reg_addr_val_pair_struct {
+	uint8_t	reg_addr;
+	uint8_t	reg_val;
+};
+
+enum ov7692_test_mode_t {
+	TEST_OFF,
+	TEST_1,
+	TEST_2,
+	TEST_3
+};
+
+enum ov7692_resolution_t {
+	QTR_SIZE,
+	FULL_SIZE,
+	INVALID_SIZE
+};
+
+enum ov7692_setting {
+	RES_PREVIEW,
+	RES_CAPTURE
+};
+enum ov7692_reg_update {
+	/* Sensor egisters that need to be updated during initialization */
+	REG_INIT,
+	/* Sensor egisters that needs periodic I2C writes */
+	UPDATE_PERIODIC,
+	/* All the sensor Registers will be updated */
+	UPDATE_ALL,
+	/* Not valid update */
+	UPDATE_INVALID
+};
+
+/*OV SENSOR SCCB*/
+struct OV7692_WREG {
+	uint8_t addr;
+	uint8_t data;
+	uint8_t mask;
+} OV7692_WREG;
+
+#ifdef CONFIG_WEBCAM_OV7692_QRD
+/*  96MHz PCLK @ 24MHz MCLK */
+struct reg_addr_val_pair_struct ov7692_init_settings_array[] = {
+	{0x12, 0x80},
+	{0x0e, 0x08},
+	{0x69, 0x52},
+	{0x1e, 0xb3},
+	{0x48, 0x42},
+	{0xff, 0x01},
+	{0xae, 0xa0},
+	{0xa8, 0x26},
+	{0xb4, 0xc0},
+	{0xb5, 0x40},
+	{0xff, 0x00},
+	{0x0c, 0x00},
+	{0x62, 0x10},
+	{0x12, 0x00},
+	{0x17, 0x65},
+	{0x18, 0xa4},
+	{0x19, 0x0a},
+	{0x1a, 0xf6},
+	{0x3e, 0x30},
+	{0x64, 0x0a},
+	{0xff, 0x01},
+	{0xb4, 0xc0},
+	{0xff, 0x00},
+	{0x67, 0x20},
+	{0x81, 0x3f},
+	{0xcc, 0x02},
+	{0xcd, 0x80},
+	{0xce, 0x01},
+	{0xcf, 0xe0},
+	{0xc8, 0x02},
+	{0xc9, 0x80},
+	{0xca, 0x01},
+	{0xcb, 0xe0},
+	{0xd0, 0x48},
+	{0x82, 0x03},
+	/*{0x0e, 0x00},*/
+	{0x70, 0x00},
+	{0x71, 0x34},
+	{0x74, 0x28},
+	{0x75, 0x98},
+	{0x76, 0x00},
+	{0x77, 0x64},
+	{0x78, 0x01},
+	{0x79, 0xc2},
+	{0x7a, 0x4e},
+	{0x7b, 0x1f},
+	{0x7c, 0x00},
+	{0x11, 0x00},
+	{0x20, 0x00},
+	{0x21, 0x23},
+	{0x50, 0x9a},
+	{0x51, 0x80},
+	{0x4c, 0x7d},
+	/*{0x0e, 0x00},*/
+	{0x85, 0x10},
+	{0x86, 0x00},
+	{0x87, 0x00},
+	{0x88, 0x00},
+	{0x89, 0x2a},
+	{0x8a, 0x26},
+	{0x8b, 0x22},
+	{0xbb, 0x7a},
+	{0xbc, 0x69},
+	{0xbd, 0x11},
+	{0xbe, 0x13},
+	{0xbf, 0x81},
+	{0xc0, 0x96},
+	{0xc1, 0x1e},
+	{0xb7, 0x05},
+	{0xb8, 0x09},
+	{0xb9, 0x00},
+	{0xba, 0x18},
+	{0x5a, 0x1f},
+	{0x5b, 0x9f},
+	{0x5c, 0x6a},
+	{0x5d, 0x42},
+	{0x24, 0x78},
+	{0x25, 0x68},
+	{0x26, 0xb3},
+	{0xa3, 0x0b},
+	{0xa4, 0x15},
+	{0xa5, 0x2a},
+	{0xa6, 0x51},
+	{0xa7, 0x63},
+	{0xa8, 0x74},
+	{0xa9, 0x83},
+	{0xaa, 0x91},
+	{0xab, 0x9e},
+	{0xac, 0xaa},
+	{0xad, 0xbe},
+	{0xae, 0xce},
+	{0xaf, 0xe5},
+	{0xb0, 0xf3},
+	{0xb1, 0xfb},
+	{0xb2, 0x06},
+	{0x8c, 0x5c},
+	{0x8d, 0x11},
+	{0x8e, 0x12},
+	{0x8f, 0x19},
+	{0x90, 0x50},
+	{0x91, 0x20},
+	{0x92, 0x96},
+	{0x93, 0x80},
+	{0x94, 0x13},
+	{0x95, 0x1b},
+	{0x96, 0xff},
+	{0x97, 0x00},
+	{0x98, 0x3d},
+	{0x99, 0x36},
+	{0x9a, 0x51},
+	{0x9b, 0x43},
+	{0x9c, 0xf0},
+	{0x9d, 0xf0},
+	{0x9e, 0xf0},
+	{0x9f, 0xff},
+	{0xa0, 0x68},
+	{0xa1, 0x62},
+	{0xa2, 0x0e},
+};
+#endif
+/* Exposure Compensation */
+struct OV7692_WREG ov7692_exposure_compensation_lv0_tbl[] = {
+	/*@@ +1.7EV*/
+	{0x24, 0xc0},
+	{0x25, 0xb8},
+	{0x26, 0xe6},
+};
+
+struct OV7692_WREG ov7692_exposure_compensation_lv1_tbl[] = {
+	/*@@ +1.0EV*/
+	{0x24, 0xa8},
+	{0x25, 0xa0},
+	{0x26, 0xc4},
+};
+
+struct OV7692_WREG ov7692_exposure_compensation_lv2_default_tbl[] = {
+	/*@@ default*/
+	{0x24, 0x86},
+	{0x25, 0x76},
+	{0x26, 0xb3},
+};
+
+struct OV7692_WREG ov7692_exposure_compensation_lv3_tbl[] = {
+	/*@@ -1.0EV*/
+	{0x24, 0x70},
+	{0x25, 0x60},
+	{0x26, 0xa2},
+};
+
+struct OV7692_WREG ov7692_exposure_compensation_lv4_tbl[] = {
+	/*@@ -1.7EV*/
+	{0x24, 0x50},
+	{0x25, 0x40},
+	{0x26, 0xa2},
+};
+
+struct OV7692_WREG ov7692_antibanding_off_tbl[] = {
+	{0x13, 0xE5, INVMASK(0x20)},
+};
+
+struct OV7692_WREG ov7692_antibanding_auto_tbl[] = {
+	{0x13, 0x20, INVMASK(0x20)},
+	{0x14, 0x14, INVMASK(0x17)},
+};
+
+struct OV7692_WREG ov7692_antibanding_50z_tbl[] = {
+	/*Band 50Hz*/
+	{0x13, 0x20, INVMASK(0x20)},
+	{0x14, 0x17, INVMASK(0x17)},
+};
+
+struct OV7692_WREG ov7692_antibanding_60z_tbl[] = {
+	/*Band 60Hz*/
+	{0x13, 0x20, INVMASK(0x20)},
+	{0x14, 0x16, INVMASK(0x17)},
+};
+
+/*Saturation*/
+struct OV7692_WREG ov7692_saturation_lv0_tbl[] = {
+	/*Saturation level 0*/
+	{0x81, 0x33, INVMASK(0x33)},
+	{0xd8, 0x00, INVMASK(0xff)},
+	{0xd9, 0x00, INVMASK(0xff)},
+	{0xd2, 0x02, INVMASK(0xff)},
+};
+
+struct OV7692_WREG ov7692_saturation_lv1_tbl[] = {
+	/*Saturation level 1*/
+	{0x81, 0x33, INVMASK(0x33)},
+	{0xd8, 0x10, INVMASK(0xff)},
+	{0xd9, 0x10, INVMASK(0xff)},
+	{0xd2, 0x02, INVMASK(0xff)},
+};
+
+struct OV7692_WREG ov7692_saturation_lv2_tbl[] = {
+	/*Saturation level 2*/
+	{0x81, 0x33, INVMASK(0x33)},
+	{0xd8, 0x20, INVMASK(0xff)},
+	{0xd9, 0x20, INVMASK(0xff)},
+	{0xd2, 0x02, INVMASK(0xff)},
+
+};
+
+struct OV7692_WREG ov7692_saturation_lv3_tbl[] = {
+	/*Saturation level 3*/
+	{0x81, 0x33, INVMASK(0x33)},
+	{0xd8, 0x30, INVMASK(0xff)},
+	{0xd9, 0x30, INVMASK(0xff)},
+	{0xd2, 0x02, INVMASK(0xff)},
+
+};
+
+struct OV7692_WREG ov7692_saturation_default_lv4_tbl[] = {
+	/*Saturation level 4 (default)*/
+	{0x81, 0x33, INVMASK(0x33)},
+	{0xd8, 0x40, INVMASK(0xff)},
+	{0xd9, 0x40, INVMASK(0xff)},
+	{0xd2, 0x02, INVMASK(0xff)},
+};
+
+struct OV7692_WREG ov7692_saturation_lv5_tbl[] = {
+	/*Saturation level 5*/
+	{0x81, 0x33, INVMASK(0x33)},
+	{0xd8, 0x50, INVMASK(0xff)},
+	{0xd9, 0x50, INVMASK(0xff)},
+	{0xd2, 0x02, INVMASK(0xff)},
+};
+
+struct OV7692_WREG ov7692_saturation_lv6_tbl[] = {
+	/*Saturation level 6*/
+	{0x81, 0x33, INVMASK(0x33)},
+	{0xd8, 0x60, INVMASK(0xff)},
+	{0xd9, 0x60, INVMASK(0xff)},
+	{0xd2, 0x02, INVMASK(0xff)},
+};
+
+struct OV7692_WREG ov7692_saturation_lv7_tbl[] = {
+	/*Saturation level 7*/
+	{0x81, 0x33, INVMASK(0x33)},
+	{0xd8, 0x70, INVMASK(0xff)},
+	{0xd9, 0x70, INVMASK(0xff)},
+	{0xd2, 0x02, INVMASK(0xff)},
+};
+
+struct OV7692_WREG ov7692_saturation_lv8_tbl[] = {
+	/*Saturation level 8*/
+	{0x81, 0x33, INVMASK(0x33)},
+	{0xd8, 0x80, INVMASK(0xff)},
+	{0xd9, 0x80, INVMASK(0xff)},
+	{0xd2, 0x02, INVMASK(0xff)},
+};
+
+/*EFFECT*/
+struct OV7692_WREG ov7692_effect_normal_tbl[] = {
+	{0x81, 0x00, INVMASK(0x20)},
+	{0x28, 0x00, },
+	{0xd2, 0x00, },
+	{0xda, 0x80, },
+	{0xdb, 0x80, },
+};
+
+struct OV7692_WREG ov7692_effect_mono_tbl[] = {
+	{0x81, 0x20, INVMASK(0x20)},
+	{0x28, 0x00, },
+	{0xd2, 0x18, },
+	{0xda, 0x80, },
+	{0xdb, 0x80, },
+};
+
+struct OV7692_WREG ov7692_effect_bw_tbl[] = {
+	{0x81, 0x20, INVMASK(0x20)},
+	{0x28, 0x00, },
+	{0xd2, 0x18, },
+	{0xda, 0x80, },
+	{0xdb, 0x80, },
+};
+
+struct OV7692_WREG ov7692_effect_sepia_tbl[] = {
+	{0x81, 0x20, INVMASK(0x20)},
+	{0x28, 0x00, },
+	{0xd2, 0x18, },
+	{0xda, 0x40, },
+	{0xdb, 0xa0, },
+};
+
+struct OV7692_WREG ov7692_effect_bluish_tbl[] = {
+	{0x81, 0x20, INVMASK(0x20)},
+	{0x28, 0x00, },
+	{0xd2, 0x18, },
+	{0xda, 0xc0, },
+	{0xdb, 0x80, },
+};
+
+struct OV7692_WREG ov7692_effect_reddish_tbl[] = {
+	{0x81, 0x20, INVMASK(0x20)},
+	{0x28, 0x00, },
+	{0xd2, 0x18, },
+	{0xda, 0x80, },
+	{0xdb, 0xc0, },
+};
+
+struct OV7692_WREG ov7692_effect_greenish_tbl[] = {
+	{0x81, 0x20, INVMASK(0x20)},
+	{0x28, 0x00, },
+	{0xd2, 0x18, },
+	{0xda, 0x60, },
+	{0xdb, 0x60, },
+};
+
+struct OV7692_WREG ov7692_effect_negative_tbl[] = {
+	{0x81, 0x20, INVMASK(0x20)},
+	{0x28, 0x80, },
+	{0xd2, 0x40, },
+	{0xda, 0x80, },
+	{0xdb, 0x80, },
+};
+
+/*Contrast*/
+struct OV7692_WREG ov7692_contrast_lv0_tbl[] = {
+	/*Contrast -4*/
+	{0xb2, 0x29},
+	{0xa3, 0x55},
+	{0xa4, 0x5b},
+	{0xa5, 0x67},
+	{0xa6, 0x7e},
+	{0xa7, 0x89},
+	{0xa8, 0x93},
+	{0xa9, 0x9c},
+	{0xaa, 0xa4},
+	{0xab, 0xac},
+	{0xac, 0xb3},
+	{0xad, 0xbe},
+	{0xae, 0xc7},
+	{0xaf, 0xd5},
+	{0xb0, 0xdd},
+	{0xb1, 0xe1},
+};
+
+struct OV7692_WREG ov7692_contrast_lv1_tbl[] = {
+	/*Contrast -3*/
+	{0xb2, 0x20},
+	{0xa3, 0x43},
+	{0xa4, 0x4a},
+	{0xa5, 0x58},
+	{0xa6, 0x73},
+	{0xa7, 0x80},
+	{0xa8, 0x8b},
+	{0xa9, 0x96},
+	{0xaa, 0x9f},
+	{0xab, 0xa8},
+	{0xac, 0xb1},
+	{0xad, 0xbe},
+	{0xae, 0xc9},
+	{0xaf, 0xd8},
+	{0xb0, 0xe2},
+	{0xb1, 0xe8},
+};
+
+struct OV7692_WREG ov7692_contrast_lv2_tbl[] = {
+	/*Contrast -2*/
+	{0xb2, 0x18},
+	{0xa3, 0x31},
+	{0xa4, 0x39},
+	{0xa5, 0x4a},
+	{0xa6, 0x68},
+	{0xa7, 0x77},
+	{0xa8, 0x84},
+	{0xa9, 0x90},
+	{0xaa, 0x9b},
+	{0xab, 0xa5},
+	{0xac, 0xaf},
+	{0xad, 0xbe},
+	{0xae, 0xca},
+	{0xaf, 0xdc},
+	{0xb0, 0xe7},
+	{0xb1, 0xee},
+};
+
+struct OV7692_WREG ov7692_contrast_lv3_tbl[] = {
+	/*Contrast -1*/
+	{0xb2, 0x10},
+	{0xa3, 0x1f},
+	{0xa4, 0x28},
+	{0xa5, 0x3b},
+	{0xa6, 0x5d},
+	{0xa7, 0x6e},
+	{0xa8, 0x7d},
+	{0xa9, 0x8a},
+	{0xaa, 0x96},
+	{0xab, 0xa2},
+	{0xac, 0xad},
+	{0xad, 0xbe},
+	{0xae, 0xcc},
+	{0xaf, 0xe0},
+	{0xb0, 0xed},
+	{0xb1, 0xf4},
+};
+
+struct OV7692_WREG ov7692_contrast_default_lv4_tbl[] = {
+	/*Contrast 0*/
+	{0xb2, 0x6},
+	{0xa3, 0xb},
+	{0xa4, 0x15},
+	{0xa5, 0x2a},
+	{0xa6, 0x51},
+	{0xa7, 0x63},
+	{0xa8, 0x74},
+	{0xa9, 0x83},
+	{0xaa, 0x91},
+	{0xab, 0x9e},
+	{0xac, 0xaa},
+	{0xad, 0xbe},
+	{0xae, 0xce},
+	{0xaf, 0xe5},
+	{0xb0, 0xf3},
+	{0xb1, 0xfb},
+};
+
+struct OV7692_WREG ov7692_contrast_lv5_tbl[] = {
+	/*Contrast 1*/
+	{0xb2, 0xc},
+	{0xa3, 0x4},
+	{0xa4, 0xc},
+	{0xa5, 0x1f},
+	{0xa6, 0x45},
+	{0xa7, 0x58},
+	{0xa8, 0x6b},
+	{0xa9, 0x7c},
+	{0xaa, 0x8d},
+	{0xab, 0x9d},
+	{0xac, 0xac},
+	{0xad, 0xc3},
+	{0xae, 0xd2},
+	{0xaf, 0xe8},
+	{0xb0, 0xf2},
+	{0xb1, 0xf7},
+};
+
+struct OV7692_WREG ov7692_contrast_lv6_tbl[] = {
+	/*Contrast 2*/
+	{0xb2, 0x1},
+	{0xa3, 0x2},
+	{0xa4, 0x9},
+	{0xa5, 0x1a},
+	{0xa6, 0x3e},
+	{0xa7, 0x4a},
+	{0xa8, 0x59},
+	{0xa9, 0x6a},
+	{0xaa, 0x79},
+	{0xab, 0x8e},
+	{0xac, 0xa4},
+	{0xad, 0xc1},
+	{0xae, 0xdb},
+	{0xaf, 0xf4},
+	{0xb0, 0xff},
+	{0xb1, 0xff},
+};
+
+struct OV7692_WREG ov7692_contrast_lv7_tbl[] = {
+	/*Contrast 3*/
+	{0xb2, 0xc},
+	{0xa3, 0x4},
+	{0xa4, 0x8},
+	{0xa5, 0x17},
+	{0xa6, 0x27},
+	{0xa7, 0x3d},
+	{0xa8, 0x54},
+	{0xa9, 0x60},
+	{0xaa, 0x77},
+	{0xab, 0x85},
+	{0xac, 0xa4},
+	{0xad, 0xc6},
+	{0xae, 0xd2},
+	{0xaf, 0xe9},
+	{0xb0, 0xf0},
+	{0xb1, 0xf7},
+};
+
+struct OV7692_WREG ov7692_contrast_lv8_tbl[] = {
+	/*Contrast 4*/
+	{0xb2, 0x1},
+	{0xa3, 0x4},
+	{0xa4, 0x4},
+	{0xa5, 0x7},
+	{0xa6, 0xb},
+	{0xa7, 0x17},
+	{0xa8, 0x2a},
+	{0xa9, 0x41},
+	{0xaa, 0x59},
+	{0xab, 0x6b},
+	{0xac, 0x8b},
+	{0xad, 0xb1},
+	{0xae, 0xd2},
+	{0xaf, 0xea},
+	{0xb0, 0xf4},
+	{0xb1, 0xff},
+};
+
+	/*Sharpness*/
+struct OV7692_WREG ov7692_sharpness_lv0_tbl[] = {
+	/*Sharpness 0*/
+	{0xb4, 0x20, INVMASK(0x20)},
+	{0xb6, 0x00, INVMASK(0x1f)},
+};
+struct OV7692_WREG ov7692_sharpness_lv1_tbl[] = {
+	/*Sharpness 1*/
+	{0xb4, 0x20, INVMASK(0x20)},
+	{0xb6, 0x01, INVMASK(0x1f)},
+};
+struct OV7692_WREG ov7692_sharpness_default_lv2_tbl[] = {
+	/*Sharpness Auto (Default)*/
+	{0xb4, 0x00, INVMASK(0x20)},
+	{0xb6, 0x00, INVMASK(0x1f)},
+};
+struct OV7692_WREG ov7692_sharpness_lv3_tbl[] = {
+	/*Sharpness 3*/
+	{0xb4, 0x20, INVMASK(0x20)},
+	{0xb6, 0x66, INVMASK(0x04)},
+};
+struct OV7692_WREG ov7692_sharpness_lv4_tbl[] = {
+	/*Sharpness 4*/
+	{0xb4, 0x20, INVMASK(0x20)},
+	{0xb6, 0x99, INVMASK(0x1f)},
+};
+struct OV7692_WREG ov7692_sharpness_lv5_tbl[] = {
+	/*Sharpness 5*/
+	{0xb4, 0x20, INVMASK(0x20)},
+	{0xb6, 0xcc, INVMASK(0x1f)},
+};
+struct OV7692_WREG ov7692_sharpness_lv6_tbl[] = {
+	/*Sharpness 6*/
+	{0xb4, 0x20, INVMASK(0x20)},
+	{0xb6, 0xff, INVMASK(0x1f)},
+};
+
+	/* ISO TYPE*/
+struct OV7692_WREG ov7692_iso_type_auto[] = {
+	/*@@ISO Auto*/
+	{0x14, 0x20, INVMASK(0x70)},
+};
+
+struct OV7692_WREG ov7692_iso_type_100[] = {
+	/*@@ISO 100*/
+	{0x14, 0x00, INVMASK(0x70)},
+};
+
+struct OV7692_WREG ov7692_iso_type_200[] = {
+	/*@@ISO 200*/
+	{0x14, 0x10, INVMASK(0x70)},
+};
+
+struct OV7692_WREG ov7692_iso_type_400[] = {
+	/*@@ISO 400*/
+	{0x14, 0x20, INVMASK(0x70)},
+};
+
+struct OV7692_WREG ov7692_iso_type_800[] = {
+	/*@@ISO 800*/
+	{0x14, 0x30, INVMASK(0x70)},
+};
+
+struct OV7692_WREG ov7692_iso_type_1600[] = {
+	/*@@ISO 1600*/
+	{0x14, 0x40, INVMASK(0x70)},
+};
+
+	/*Light Mode*/
+struct OV7692_WREG ov7692_wb_def[] = {
+	{0x13, 0xf7},
+	{0x15, 0x00},
+};
+
+struct OV7692_WREG ov7692_wb_custom[] = {
+	{0x13, 0xf5},
+	{0x01, 0x56},
+	{0x02, 0x50},
+	{0x15, 0x00},
+};
+
+struct OV7692_WREG ov7692_wb_inc[] = {
+	{0x13, 0xf5},
+	{0x01, 0x66},
+	{0x02, 0x40},
+	{0x15, 0x00},
+};
+
+struct OV7692_WREG ov7692_wb_daylight[] = {
+	{0x13, 0xf5},
+	{0x01, 0x43},
+	{0x02, 0x5d},
+	{0x15, 0x00},
+};
+
+struct OV7692_WREG ov7692_wb_cloudy[] = {
+	{0x13, 0xf5},
+	{0x01, 0x48},
+	{0x02, 0x63},
+	{0x15, 0x00},
+};
+
+#endif
+
diff --git a/drivers/media/video/msm/ov7692_qrd.c b/drivers/media/video/msm/ov7692_qrd.c
new file mode 100644
index 0000000..d83f28e
--- /dev/null
+++ b/drivers/media/video/msm/ov7692_qrd.c
@@ -0,0 +1,1178 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <media/msm_camera.h>
+#include <mach/camera.h>
+#include <mach/gpio.h>
+#include "ov7692.h"
+
+/*=============================================================
+    SENSOR REGISTER DEFINES
+==============================================================*/
+#define Q8    0x00000100
+
+/* Omnivision8810 product ID register address */
+#define REG_OV7692_MODEL_ID_MSB                       0x0A
+#define REG_OV7692_MODEL_ID_LSB                       0x0B
+
+#define OV7692_MODEL_ID                       0x7692
+/* Omnivision8810 product ID */
+
+/* Time in milisecs for waiting for the sensor to reset */
+#define OV7692_RESET_DELAY_MSECS    66
+#define OV7692_DEFAULT_CLOCK_RATE   24000000
+/* Registers*/
+
+/* Color bar pattern selection */
+#define OV7692_COLOR_BAR_PATTERN_SEL_REG     0x82
+/* Color bar enabling control */
+#define OV7692_COLOR_BAR_ENABLE_REG           0x601
+/* Time in milisecs for waiting for the sensor to reset*/
+#define OV7692_RESET_DELAY_MSECS    66
+
+static int ov7692_pwdn_gpio;
+static int ov7692_reset_gpio;
+
+
+/*============================================================================
+			DATA DECLARATIONS
+============================================================================*/
+
+
+static bool OV7692_CSI_CONFIG;
+/* 816x612, 24MHz MCLK 96MHz PCLK */
+uint32_t OV7692_FULL_SIZE_WIDTH        = 640;
+uint32_t OV7692_FULL_SIZE_HEIGHT       = 480;
+
+uint32_t OV7692_QTR_SIZE_WIDTH         = 640;
+uint32_t OV7692_QTR_SIZE_HEIGHT        = 480;
+
+uint32_t OV7692_HRZ_FULL_BLK_PIXELS    = 16;
+uint32_t OV7692_VER_FULL_BLK_LINES     = 12;
+uint32_t OV7692_HRZ_QTR_BLK_PIXELS     = 16;
+uint32_t OV7692_VER_QTR_BLK_LINES      = 12;
+
+struct ov7692_work_t {
+	struct work_struct work;
+};
+static struct  ov7692_work_t *ov7692_sensorw;
+static struct  i2c_client *ov7692_client;
+struct ov7692_ctrl_t {
+	const struct  msm_camera_sensor_info *sensordata;
+	uint32_t sensormode;
+	uint32_t fps_divider;        /* init to 1 * 0x00000400 */
+	uint32_t pict_fps_divider;    /* init to 1 * 0x00000400 */
+	uint32_t fps;
+	int32_t  curr_lens_pos;
+	uint32_t curr_step_pos;
+	uint32_t my_reg_gain;
+	uint32_t my_reg_line_count;
+	uint32_t total_lines_per_frame;
+	enum ov7692_resolution_t prev_res;
+	enum ov7692_resolution_t pict_res;
+	enum ov7692_resolution_t curr_res;
+	enum ov7692_test_mode_t  set_test;
+	unsigned short imgaddr;
+};
+static struct ov7692_ctrl_t *ov7692_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(ov7692_wait_queue);
+DEFINE_MUTEX(ov7692_mut);
+static int effect_value;
+static int16_t ov7692_effect = CAMERA_EFFECT_OFF;
+static unsigned int SAT_U = 0x80;
+static unsigned int SAT_V = 0x80;
+
+/*=============================================================*/
+
+static int ov7692_i2c_rxdata(unsigned short saddr,
+		unsigned char *rxdata, int length)
+{
+	struct i2c_msg msgs[] = {
+		{
+			.addr  = saddr,
+			.flags = 0,
+			.len   = 1,
+			.buf   = rxdata,
+		},
+		{
+			.addr  = saddr,
+			.flags = I2C_M_RD,
+			.len   = 1,
+			.buf   = rxdata,
+		},
+	};
+	if (i2c_transfer(ov7692_client->adapter, msgs, 2) < 0) {
+		CDBG("ov7692_i2c_rxdata failed!\n");
+		return -EIO;
+	}
+	return 0;
+}
+static int32_t ov7692_i2c_txdata(unsigned short saddr,
+		unsigned char *txdata, int length)
+{
+	struct i2c_msg msg[] = {
+		{
+			.addr = saddr,
+			.flags = 0,
+			.len = 2,
+			.buf = txdata,
+		},
+	};
+	if (i2c_transfer(ov7692_client->adapter, msg, 1) < 0) {
+		CDBG("ov7692_i2c_txdata faild 0x%x\n", ov7692_client->addr);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int32_t ov7692_i2c_read(uint8_t raddr,
+		uint8_t *rdata, int rlen)
+{
+	int32_t rc = 0;
+	unsigned char buf[1];
+	if (!rdata)
+		return -EIO;
+	memset(buf, 0, sizeof(buf));
+	buf[0] = raddr;
+	rc = ov7692_i2c_rxdata(ov7692_client->addr >> 1, buf, rlen);
+	if (rc < 0) {
+		CDBG("ov7692_i2c_read 0x%x failed!\n", raddr);
+		return rc;
+	}
+	*rdata = buf[0];
+	return rc;
+}
+static int32_t ov7692_i2c_write_b_sensor(uint8_t waddr, uint8_t bdata)
+{
+	int32_t rc = -EFAULT;
+	unsigned char buf[2];
+
+	memset(buf, 0, sizeof(buf));
+	buf[0] = waddr;
+	buf[1] = bdata;
+	CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, bdata);
+	rc = ov7692_i2c_txdata(ov7692_client->addr >> 1, buf, 2);
+	if (rc < 0)
+		CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+		waddr, bdata);
+
+	return rc;
+}
+
+static int32_t OV7692_WritePRegs(struct OV7692_WREG *pTb, int32_t len)
+{
+	int32_t i, ret = 0;
+	uint8_t regv;
+
+	for (i = 0; i < len; i++) {
+		if (pTb[i].mask == 0) {
+			ov7692_i2c_write_b_sensor(pTb[i].addr, pTb[i].data);
+		} else {
+			ov7692_i2c_read(pTb[i].addr, &regv, 1);
+			regv &= pTb[i].mask;
+			regv |= (pTb[i].data & (~pTb[i].mask));
+			ov7692_i2c_write_b_sensor(pTb[i].addr, regv);
+		}
+	}
+	return ret;
+}
+
+static int32_t ov7692_sensor_setting(int update_type, int rt)
+{
+	int32_t i, array_length;
+	int32_t rc = 0;
+	struct msm_camera_csi_params ov7692_csi_params;
+
+	CDBG("%s: rt = %d\n", __func__, rt);
+
+	switch (update_type) {
+	case REG_INIT:
+		OV7692_CSI_CONFIG = 0;
+		ov7692_i2c_write_b_sensor(0x0e, 0x08);
+		return rc;
+		break;
+	case UPDATE_PERIODIC:
+		if (!OV7692_CSI_CONFIG) {
+			ov7692_csi_params.lane_cnt = 1;
+			ov7692_csi_params.data_format = CSI_8BIT;
+			ov7692_csi_params.lane_assign = 0xe4;
+			ov7692_csi_params.dpcm_scheme = 0;
+			ov7692_csi_params.settle_cnt = 0x14;
+
+			array_length = sizeof(ov7692_init_settings_array) /
+				sizeof(ov7692_init_settings_array[0]);
+			for (i = 0; i < array_length; i++) {
+				rc = ov7692_i2c_write_b_sensor(
+				ov7692_init_settings_array[i].reg_addr,
+				ov7692_init_settings_array[i].reg_val);
+				if (rc < 0)
+					return rc;
+			}
+			usleep_range(10000, 11000);
+			rc = msm_camio_csi_config(&ov7692_csi_params);
+			usleep_range(10000, 11000);
+			ov7692_i2c_write_b_sensor(0x0e, 0x00);
+			OV7692_CSI_CONFIG = 1;
+			msleep(20);
+			return rc;
+		}
+		break;
+	default:
+		rc = -EINVAL;
+		break;
+	}
+	return rc;
+}
+
+static int32_t ov7692_video_config(int mode)
+{
+	int32_t rc = 0;
+	int rt;
+	/* change sensor resolution if needed */
+	rt = RES_PREVIEW;
+
+	CDBG("%s\n", __func__);
+
+	if (ov7692_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+		return rc;
+	ov7692_ctrl->curr_res = ov7692_ctrl->prev_res;
+	ov7692_ctrl->sensormode = mode;
+	return rc;
+}
+
+static int32_t ov7692_set_sensor_mode(int mode,
+		int res)
+{
+	int32_t rc = 0;
+	switch (mode) {
+	case SENSOR_PREVIEW_MODE:
+		rc = ov7692_video_config(mode);
+		break;
+	case SENSOR_SNAPSHOT_MODE:
+	case SENSOR_RAW_SNAPSHOT_MODE:
+		break;
+	default:
+		rc = -EINVAL;
+		break;
+	}
+	return rc;
+}
+
+static int ov7692_set_exposure_compensation(int compensation)
+{
+	long rc = 0;
+
+	CDBG("--CAMERA-- %s ...(Start)\n", __func__);
+	CDBG("--CAMERA-- %s ...exposure_compensation = %d\n",
+		 __func__ , compensation);
+	switch (compensation) {
+	case CAMERA_EXPOSURE_COMPENSATION_LV0:
+		CDBG("--CAMERA--CAMERA_EXPOSURE_COMPENSATION_LV0\n");
+		rc = OV7692Core_WritePREG(
+			ov7692_exposure_compensation_lv0_tbl);
+		break;
+	case CAMERA_EXPOSURE_COMPENSATION_LV1:
+		CDBG("--CAMERA--CAMERA_EXPOSURE_COMPENSATION_LV1\n");
+		rc = OV7692Core_WritePREG(
+			ov7692_exposure_compensation_lv1_tbl);
+		break;
+	case CAMERA_EXPOSURE_COMPENSATION_LV2:
+		CDBG("--CAMERA--CAMERA_EXPOSURE_COMPENSATION_LV2\n");
+		rc = OV7692Core_WritePREG(
+			ov7692_exposure_compensation_lv2_default_tbl);
+		break;
+	case CAMERA_EXPOSURE_COMPENSATION_LV3:
+		CDBG("--CAMERA--CAMERA_EXPOSURE_COMPENSATION_LV3\n");
+		rc = OV7692Core_WritePREG(
+			ov7692_exposure_compensation_lv3_tbl);
+		break;
+	case CAMERA_EXPOSURE_COMPENSATION_LV4:
+		CDBG("--CAMERA--CAMERA_EXPOSURE_COMPENSATION_LV3\n");
+		rc = OV7692Core_WritePREG(
+			ov7692_exposure_compensation_lv4_tbl);
+		break;
+	default:
+		CDBG("--CAMERA--ERROR CAMERA_EXPOSURE_COMPENSATION\n");
+		break;
+	}
+	CDBG("--CAMERA-- %s ...(End)\n", __func__);
+	return rc;
+}
+
+static long ov7692_set_antibanding(int antibanding)
+{
+	long rc = 0;
+
+	CDBG("--CAMERA-- %s ...(Start)\n", __func__);
+	CDBG("--CAMERA-- %s ...antibanding = %d\n", __func__, antibanding);
+	switch (antibanding) {
+	case CAMERA_ANTIBANDING_OFF:
+		CDBG("--CAMERA--CAMERA_ANTIBANDING_OFF\n");
+		break;
+	case CAMERA_ANTIBANDING_60HZ:
+		CDBG("--CAMERA--CAMERA_ANTIBANDING_60HZ\n");
+		rc = OV7692Core_WritePREG(ov7692_antibanding_60z_tbl);
+		break;
+	case CAMERA_ANTIBANDING_50HZ:
+		CDBG("--CAMERA--CAMERA_ANTIBANDING_50HZ\n");
+		rc = OV7692Core_WritePREG(ov7692_antibanding_50z_tbl);
+		break;
+	case CAMERA_ANTIBANDING_AUTO:
+		CDBG("--CAMERA--CAMERA_ANTIBANDING_AUTO\n");
+		rc = OV7692Core_WritePREG(ov7692_antibanding_auto_tbl);
+		break;
+	default:
+		CDBG("--CAMERA--CAMERA_ANTIBANDING_ERROR COMMAND\n");
+		break;
+	}
+	CDBG("--CAMERA-- %s ...(End)\n", __func__);
+	return rc;
+}
+
+static int ov7692_set_saturation(int saturation)
+{
+	long rc = 0;
+
+	CDBG("--CAMERA-- %s ...(Start)\n", __func__);
+	CDBG("--CAMERA-- %s ...saturation = %d\n", __func__ , saturation);
+
+	if (effect_value == CAMERA_EFFECT_OFF) {
+		switch (saturation) {
+		case CAMERA_SATURATION_LV0:
+			CDBG("--CAMERA--CAMERA_SATURATION_LV0\n");
+			rc = OV7692Core_WritePREG(ov7692_saturation_lv0_tbl);
+			break;
+		case CAMERA_SATURATION_LV1:
+			CDBG("--CAMERA--CAMERA_SATURATION_LV1\n");
+			rc = OV7692Core_WritePREG(ov7692_saturation_lv1_tbl);
+			break;
+		case CAMERA_SATURATION_LV2:
+			CDBG("--CAMERA--CAMERA_SATURATION_LV2\n");
+			rc = OV7692Core_WritePREG(ov7692_saturation_lv2_tbl);
+			break;
+		case CAMERA_SATURATION_LV3:
+			CDBG("--CAMERA--CAMERA_SATURATION_LV3\n");
+			rc = OV7692Core_WritePREG(ov7692_saturation_lv3_tbl);
+			break;
+		case CAMERA_SATURATION_LV4:
+			CDBG("--CAMERA--CAMERA_SATURATION_LV4\n");
+			rc = OV7692Core_WritePREG(
+				ov7692_saturation_default_lv4_tbl);
+			break;
+		case CAMERA_SATURATION_LV5:
+			CDBG("--CAMERA--CAMERA_SATURATION_LV5\n");
+			rc = OV7692Core_WritePREG(ov7692_saturation_lv5_tbl);
+			break;
+		case CAMERA_SATURATION_LV6:
+			CDBG("--CAMERA--CAMERA_SATURATION_LV6\n");
+			rc = OV7692Core_WritePREG(ov7692_saturation_lv6_tbl);
+			break;
+		case CAMERA_SATURATION_LV7:
+			CDBG("--CAMERA--CAMERA_SATURATION_LV7\n");
+			rc = OV7692Core_WritePREG(ov7692_saturation_lv7_tbl);
+			break;
+		case CAMERA_SATURATION_LV8:
+			CDBG("--CAMERA--CAMERA_SATURATION_LV8\n");
+			rc = OV7692Core_WritePREG(ov7692_saturation_lv8_tbl);
+			break;
+		default:
+			CDBG("--CAMERA--CAMERA_SATURATION_ERROR COMMAND\n");
+			break;
+		}
+	}
+
+	/*for recover saturation level when change special effect*/
+	switch (saturation) {
+	case CAMERA_SATURATION_LV0:
+		CDBG("--CAMERA--CAMERA_SATURATION_LV0\n");
+		SAT_U = 0x00;
+		SAT_V = 0x00;
+		break;
+	case CAMERA_SATURATION_LV1:
+		CDBG("--CAMERA--CAMERA_SATURATION_LV1\n");
+		SAT_U = 0x10;
+		SAT_V = 0x10;
+		break;
+	case CAMERA_SATURATION_LV2:
+		CDBG("--CAMERA--CAMERA_SATURATION_LV2\n");
+		SAT_U = 0x20;
+		SAT_V = 0x20;
+		break;
+	case CAMERA_SATURATION_LV3:
+		CDBG("--CAMERA--CAMERA_SATURATION_LV3\n");
+		SAT_U = 0x30;
+		SAT_V = 0x30;
+		break;
+	case CAMERA_SATURATION_LV4:
+		CDBG("--CAMERA--CAMERA_SATURATION_LV4\n");
+		SAT_U = 0x40;
+		SAT_V = 0x40;
+		break;
+	case CAMERA_SATURATION_LV5:
+		CDBG("--CAMERA--CAMERA_SATURATION_LV5\n");
+		SAT_U = 0x50;
+		SAT_V = 0x50;
+		break;
+	case CAMERA_SATURATION_LV6:
+		CDBG("--CAMERA--CAMERA_SATURATION_LV6\n");
+		SAT_U = 0x60;
+		SAT_V = 0x60;
+		break;
+	case CAMERA_SATURATION_LV7:
+		CDBG("--CAMERA--CAMERA_SATURATION_LV7\n");
+		SAT_U = 0x70;
+		SAT_V = 0x70;
+		break;
+	case CAMERA_SATURATION_LV8:
+		CDBG("--CAMERA--CAMERA_SATURATION_LV8\n");
+		SAT_U = 0x80;
+		SAT_V = 0x80;
+		break;
+	default:
+		CDBG("--CAMERA--CAMERA_SATURATION_ERROR COMMAND\n");
+		break;
+	}
+	CDBG("--CAMERA-- %s ...(End)\n", __func__);
+	return rc;
+}
+
+static long ov7692_set_effect(int mode, int effect)
+{
+	int rc = 0;
+	CDBG("--CAMERA-- %s ...(Start)\n", __func__);
+
+	switch (mode) {
+	case SENSOR_PREVIEW_MODE:
+		break;
+	case SENSOR_HFR_60FPS_MODE:
+		break;
+	case SENSOR_HFR_90FPS_MODE:
+		/* Context A Special Effects */
+		CDBG("-CAMERA- %s ...SENSOR_PREVIEW_MODE\n", __func__);
+		break;
+	case SENSOR_SNAPSHOT_MODE:
+		/* Context B Special Effects */
+		CDBG("-CAMERA- %s ...SENSOR_SNAPSHOT_MODE\n", __func__);
+		break;
+	default:
+		break;
+	}
+	effect_value = effect;
+	switch (effect) {
+	case CAMERA_EFFECT_OFF: {
+		CDBG("--CAMERA-- %s ...CAMERA_EFFECT_OFF\n", __func__);
+		rc = OV7692Core_WritePREG(ov7692_effect_normal_tbl);
+		/* for recover saturation level
+		 when change special effect*/
+		ov7692_i2c_write_b_sensor(0xda, SAT_U);
+		/* for recover saturation level
+		when change special effect*/
+		ov7692_i2c_write_b_sensor(0xdb, SAT_V);
+		break;
+	}
+	case CAMERA_EFFECT_MONO: {
+		CDBG("--CAMERA-- %s ...CAMERA_EFFECT_MONO\n", __func__);
+		rc = OV7692Core_WritePREG(ov7692_effect_mono_tbl);
+		break;
+	}
+	case CAMERA_EFFECT_BW: {
+		CDBG("--CAMERA-- %s ...CAMERA_EFFECT_BW\n", __func__);
+		rc = OV7692Core_WritePREG(ov7692_effect_bw_tbl);
+		break;
+	}
+	case CAMERA_EFFECT_BLUISH: {
+		CDBG("--CAMERA-- %s ...CAMERA_EFFECT_BLUISH\n", __func__);
+		rc = OV7692Core_WritePREG(ov7692_effect_bluish_tbl);
+		break;
+	}
+	case CAMERA_EFFECT_SOLARIZE: {
+		CDBG("%s ...CAMERA_EFFECT_NEGATIVE(No Support)!\n", __func__);
+		break;
+	}
+	case CAMERA_EFFECT_SEPIA: {
+		CDBG("--CAMERA-- %s ...CAMERA_EFFECT_SEPIA\n", __func__);
+		rc = OV7692Core_WritePREG(ov7692_effect_sepia_tbl);
+		break;
+	}
+	case CAMERA_EFFECT_REDDISH: {
+		CDBG("--CAMERA-- %s ...CAMERA_EFFECT_REDDISH\n", __func__);
+		rc = OV7692Core_WritePREG(ov7692_effect_reddish_tbl);
+		break;
+	}
+	case CAMERA_EFFECT_GREENISH: {
+		CDBG("--CAMERA-- %s ...CAMERA_EFFECT_GREENISH\n", __func__);
+		rc = OV7692Core_WritePREG(ov7692_effect_greenish_tbl);
+		break;
+	}
+	case CAMERA_EFFECT_NEGATIVE: {
+		CDBG("--CAMERA-- %s ...CAMERA_EFFECT_NEGATIVE\n", __func__);
+		rc = OV7692Core_WritePREG(ov7692_effect_negative_tbl);
+		break;
+	}
+	default: {
+		CDBG("--CAMERA-- %s ...Default(Not Support)\n", __func__);
+	}
+	}
+	ov7692_effect = effect;
+	/*Refresh Sequencer */
+	CDBG("--CAMERA-- %s ...(End)\n", __func__);
+	return rc;
+}
+
+static int ov7692_set_contrast(int contrast)
+{
+	int rc = 0;
+	CDBG("--CAMERA-- %s ...(Start)\n", __func__);
+	CDBG("--CAMERA-- %s ...contrast = %d\n", __func__ , contrast);
+
+	if (effect_value == CAMERA_EFFECT_OFF) {
+		switch (contrast) {
+		case CAMERA_CONTRAST_LV0:
+			CDBG("--CAMERA--CAMERA_CONTRAST_LV0\n");
+			rc = OV7692Core_WritePREG(ov7692_contrast_lv0_tbl);
+			break;
+		case CAMERA_CONTRAST_LV1:
+			CDBG("--CAMERA--CAMERA_CONTRAST_LV1\n");
+			rc = OV7692Core_WritePREG(ov7692_contrast_lv1_tbl);
+			break;
+		case CAMERA_CONTRAST_LV2:
+			CDBG("--CAMERA--CAMERA_CONTRAST_LV2\n");
+			rc = OV7692Core_WritePREG(ov7692_contrast_lv2_tbl);
+			break;
+		case CAMERA_CONTRAST_LV3:
+			CDBG("--CAMERA--CAMERA_CONTRAST_LV3\n");
+			rc = OV7692Core_WritePREG(ov7692_contrast_lv3_tbl);
+			break;
+		case CAMERA_CONTRAST_LV4:
+			CDBG("--CAMERA--CAMERA_CONTRAST_LV4\n");
+			rc = OV7692Core_WritePREG(
+				ov7692_contrast_default_lv4_tbl);
+			break;
+		case CAMERA_CONTRAST_LV5:
+			CDBG("--CAMERA--CAMERA_CONTRAST_LV5\n");
+			rc = OV7692Core_WritePREG(ov7692_contrast_lv5_tbl);
+			break;
+		case CAMERA_CONTRAST_LV6:
+			CDBG("--CAMERA--CAMERA_CONTRAST_LV6\n");
+			rc = OV7692Core_WritePREG(ov7692_contrast_lv6_tbl);
+			break;
+		case CAMERA_CONTRAST_LV7:
+			CDBG("--CAMERA--CAMERA_CONTRAST_LV7\n");
+			rc = OV7692Core_WritePREG(ov7692_contrast_lv7_tbl);
+			break;
+		case CAMERA_CONTRAST_LV8:
+			CDBG("--CAMERA--CAMERA_CONTRAST_LV8\n");
+			rc = OV7692Core_WritePREG(ov7692_contrast_lv8_tbl);
+			break;
+		default:
+			CDBG("--CAMERA--CAMERA_CONTRAST_ERROR COMMAND\n");
+			break;
+		}
+	}
+	CDBG("--CAMERA-- %s ...(End)\n", __func__);
+	return rc;
+}
+
+static int ov7692_set_sharpness(int sharpness)
+{
+	int rc = 0;
+	CDBG("--CAMERA-- %s ...(Start)\n", __func__);
+	CDBG("--CAMERA-- %s ...sharpness = %d\n", __func__ , sharpness);
+
+	if (effect_value == CAMERA_EFFECT_OFF) {
+		switch (sharpness) {
+		case CAMERA_SHARPNESS_LV0:
+			CDBG("--CAMERA--CAMERA_SHARPNESS_LV0\n");
+			rc = OV7692Core_WritePREG(ov7692_sharpness_lv0_tbl);
+			break;
+		case CAMERA_SHARPNESS_LV1:
+			CDBG("--CAMERA--CAMERA_SHARPNESS_LV1\n");
+			rc = OV7692Core_WritePREG(ov7692_sharpness_lv1_tbl);
+			break;
+		case CAMERA_SHARPNESS_LV2:
+			CDBG("--CAMERA--CAMERA_SHARPNESS_LV2\n");
+			rc = OV7692Core_WritePREG(
+				ov7692_sharpness_default_lv2_tbl);
+			break;
+		case CAMERA_SHARPNESS_LV3:
+			CDBG("--CAMERA--CAMERA_SHARPNESS_LV3\n");
+			rc = OV7692Core_WritePREG(ov7692_sharpness_lv3_tbl);
+			break;
+		case CAMERA_SHARPNESS_LV4:
+			CDBG("--CAMERA--CAMERA_SHARPNESS_LV4\n");
+			rc = OV7692Core_WritePREG(ov7692_sharpness_lv4_tbl);
+			break;
+		case CAMERA_SHARPNESS_LV5:
+			CDBG("--CAMERA--CAMERA_SHARPNESS_LV5\n");
+			rc = OV7692Core_WritePREG(ov7692_sharpness_lv5_tbl);
+			break;
+		case CAMERA_SHARPNESS_LV6:
+			CDBG("--CAMERA--CAMERA_SHARPNESS_LV6\n");
+			rc = OV7692Core_WritePREG(ov7692_sharpness_lv6_tbl);
+			break;
+		default:
+			CDBG("--CAMERA--CAMERA_SHARPNESS_ERROR COMMAND\n");
+			break;
+		}
+	}
+	CDBG("--CAMERA-- %s ...(End)\n", __func__);
+	return rc;
+}
+
+static int ov7692_set_iso(int8_t iso_type)
+{
+	long rc = 0;
+
+	CDBG("--CAMERA-- %s ...(Start)\n", __func__);
+	CDBG("--CAMERA-- %s ...iso_type = %d\n", __func__ , iso_type);
+	switch (iso_type) {
+	case CAMERA_ISO_TYPE_AUTO:
+		CDBG("--CAMERA--CAMERA_ISO_TYPE_AUTO\n");
+		rc = OV7692Core_WritePREG(ov7692_iso_type_auto);
+		break;
+	case CAMEAR_ISO_TYPE_HJR:
+		CDBG("--CAMERA--CAMEAR_ISO_TYPE_HJR\n");
+		rc = OV7692Core_WritePREG(ov7692_iso_type_auto);
+		break;
+	case CAMEAR_ISO_TYPE_100:
+		CDBG("--CAMERA--CAMEAR_ISO_TYPE_100\n");
+		rc = OV7692Core_WritePREG(ov7692_iso_type_100);
+		break;
+	case CAMERA_ISO_TYPE_200:
+		CDBG("--CAMERA--CAMERA_ISO_TYPE_200\n");
+		rc = OV7692Core_WritePREG(ov7692_iso_type_200);
+		break;
+	case CAMERA_ISO_TYPE_400:
+		CDBG("--CAMERA--CAMERA_ISO_TYPE_400\n");
+		rc = OV7692Core_WritePREG(ov7692_iso_type_400);
+		break;
+	case CAMEAR_ISO_TYPE_800:
+		CDBG("--CAMERA--CAMEAR_ISO_TYPE_800\n");
+		rc = OV7692Core_WritePREG(ov7692_iso_type_800);
+		break;
+	case CAMERA_ISO_TYPE_1600:
+		CDBG("--CAMERA--CAMERA_ISO_TYPE_1600\n");
+		rc = OV7692Core_WritePREG(ov7692_iso_type_1600);
+		break;
+	default:
+		CDBG("--CAMERA--ERROR ISO TYPE\n");
+		break;
+	}
+	CDBG("--CAMERA-- %s ...(End)\n", __func__);
+	return rc;
+}
+
+static int ov7692_set_wb_oem(uint8_t param)
+{
+	int rc = 0;
+	CDBG("--CAMERA--%s runs\r\n", __func__);
+
+	switch (param) {
+	case CAMERA_WB_AUTO:
+		CDBG("--CAMERA--CAMERA_WB_AUTO\n");
+		rc = OV7692Core_WritePREG(ov7692_wb_def);
+		break;
+	case CAMERA_WB_CUSTOM:
+		CDBG("--CAMERA--CAMERA_WB_CUSTOM\n");
+		rc = OV7692Core_WritePREG(ov7692_wb_custom);
+		break;
+	case CAMERA_WB_INCANDESCENT:
+		CDBG("--CAMERA--CAMERA_WB_INCANDESCENT\n");
+		rc = OV7692Core_WritePREG(ov7692_wb_inc);
+		break;
+	case CAMERA_WB_DAYLIGHT:
+		CDBG("--CAMERA--CAMERA_WB_DAYLIGHT\n");
+		rc = OV7692Core_WritePREG(ov7692_wb_daylight);
+		break;
+	case CAMERA_WB_CLOUDY_DAYLIGHT:
+		CDBG("--CAMERA--CAMERA_WB_CLOUDY_DAYLIGHT\n");
+		rc = OV7692Core_WritePREG(ov7692_wb_cloudy);
+		break;
+	default:
+		break;
+	}
+	return rc;
+}
+
+static void ov7692_power_on(void)
+{
+	CDBG("%s\n", __func__);
+	gpio_set_value(ov7692_pwdn_gpio, 0);
+}
+
+static void ov7692_power_down(void)
+{
+	CDBG("%s\n", __func__);
+	gpio_set_value(ov7692_pwdn_gpio, 1);
+}
+
+static void ov7692_sw_reset(void)
+{
+	CDBG("%s\n", __func__);
+	ov7692_i2c_write_b_sensor(0x12, 0x80);
+}
+
+static void ov7692_hw_reset(void)
+{
+	CDBG("--CAMERA-- %s ... (Start...)\n", __func__);
+	gpio_set_value(ov7692_reset_gpio, 1);   /*reset camera reset pin*/
+	usleep_range(5000, 5100);
+	gpio_set_value(ov7692_reset_gpio, 0);
+	usleep_range(5000, 5100);
+	gpio_set_value(ov7692_reset_gpio, 1);
+	usleep_range(1000, 1100);
+	CDBG("--CAMERA-- %s ... (End...)\n", __func__);
+}
+
+
+
+static int ov7692_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+	uint8_t model_id_msb, model_id_lsb = 0;
+	uint16_t model_id = 0;
+	int32_t rc = 0;
+	/*The reset pin is not physically connected to the sensor.
+	  The standby pin will do the reset hence there is no need
+	  to request the gpio reset*/
+
+	/* Read sensor Model ID: */
+	rc = ov7692_i2c_read(REG_OV7692_MODEL_ID_MSB, &model_id_msb, 1);
+	if (rc < 0)
+		goto init_probe_fail;
+	rc = ov7692_i2c_read(REG_OV7692_MODEL_ID_LSB, &model_id_lsb, 1);
+	if (rc < 0)
+		goto init_probe_fail;
+	model_id = (model_id_msb << 8) | ((model_id_lsb & 0x00FF)) ;
+	CDBG("ov7692 model_id = 0x%x, 0x%x, 0x%x\n",
+			model_id, model_id_msb, model_id_lsb);
+	/* 4. Compare sensor ID to OV7692 ID: */
+	if (model_id != OV7692_MODEL_ID) {
+		rc = -ENODEV;
+		goto init_probe_fail;
+	}
+	goto init_probe_done;
+init_probe_fail:
+	pr_warning(" ov7692_probe_init_sensor fails\n");
+init_probe_done:
+	CDBG(" ov7692_probe_init_sensor finishes\n");
+	return rc;
+}
+
+int ov7692_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+	int32_t rc = 0;
+
+	CDBG("%s: %d\n", __func__, __LINE__);
+	CDBG("Calling ov7692_sensor_open_init\n");
+	ov7692_ctrl = kzalloc(sizeof(struct ov7692_ctrl_t), GFP_KERNEL);
+	if (!ov7692_ctrl) {
+		CDBG("ov7692_init failed!\n");
+		rc = -ENOMEM;
+		goto init_done;
+	}
+	ov7692_ctrl->fps_divider = 1 * 0x00000400;
+	ov7692_ctrl->pict_fps_divider = 1 * 0x00000400;
+	ov7692_ctrl->fps = 30 * Q8;
+	ov7692_ctrl->set_test = TEST_OFF;
+	ov7692_ctrl->prev_res = QTR_SIZE;
+	ov7692_ctrl->pict_res = FULL_SIZE;
+	ov7692_ctrl->curr_res = INVALID_SIZE;
+
+	if (data)
+		ov7692_ctrl->sensordata = data;
+	/* turn on LDO for PVT */
+	if (data->pmic_gpio_enable)
+		lcd_camera_power_onoff(1);
+
+	/* enable mclk first */
+
+	msm_camio_clk_rate_set(24000000);
+	msleep(20);
+
+	ov7692_power_on();
+	usleep_range(5000, 5100);
+
+	rc = ov7692_probe_init_sensor(data);
+	if (rc < 0) {
+		CDBG("Calling ov7692_sensor_open_init fail\n");
+		goto init_fail;
+	}
+
+	rc = ov7692_sensor_setting(REG_INIT, RES_PREVIEW);
+	if (rc < 0)
+		goto init_fail;
+	else
+		goto init_done;
+
+init_fail:
+	CDBG(" ov7692_sensor_open_init fail\n");
+	if (data->pmic_gpio_enable)
+		lcd_camera_power_onoff(0);
+	kfree(ov7692_ctrl);
+init_done:
+	CDBG("ov7692_sensor_open_init done\n");
+	return rc;
+}
+
+static int ov7692_init_client(struct i2c_client *client)
+{
+	/* Initialize the MSM_CAMI2C Chip */
+	init_waitqueue_head(&ov7692_wait_queue);
+	return 0;
+}
+
+static const struct i2c_device_id ov7692_i2c_id[] = {
+	{"ov7692", 0},
+	{ }
+};
+
+static int ov7692_i2c_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	int rc = 0;
+	CDBG("ov7692_i2c_probe called!\n");
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		CDBG("i2c_check_functionality failed\n");
+		goto probe_failure;
+	}
+
+	ov7692_sensorw = kzalloc(sizeof(struct ov7692_work_t), GFP_KERNEL);
+	if (!ov7692_sensorw) {
+		CDBG("kzalloc failed.\n");
+		rc = -ENOMEM;
+		goto probe_failure;
+	}
+
+	i2c_set_clientdata(client, ov7692_sensorw);
+	ov7692_init_client(client);
+	ov7692_client = client;
+
+	CDBG("ov7692_i2c_probe success! rc = %d\n", rc);
+	return 0;
+
+probe_failure:
+	CDBG("ov7692_i2c_probe failed! rc = %d\n", rc);
+	return rc;
+}
+
+static int __exit ov7692_remove(struct i2c_client *client)
+{
+	struct ov7692_work_t_t *sensorw = i2c_get_clientdata(client);
+	free_irq(client->irq, sensorw);
+	ov7692_client = NULL;
+	kfree(sensorw);
+	return 0;
+}
+
+static struct i2c_driver ov7692_i2c_driver = {
+	.id_table = ov7692_i2c_id,
+	.probe  = ov7692_i2c_probe,
+	.remove = __exit_p(ov7692_i2c_remove),
+	.driver = {
+		.name = "ov7692",
+	},
+};
+
+int ov7692_sensor_config(void __user *argp)
+{
+	struct sensor_cfg_data cdata;
+	long   rc = 0;
+	if (copy_from_user(&cdata,
+				(void *)argp,
+				sizeof(struct sensor_cfg_data)))
+		return -EFAULT;
+	mutex_lock(&ov7692_mut);
+	CDBG("ov7692_sensor_config: cfgtype = %d\n", cdata.cfgtype);
+	switch (cdata.cfgtype) {
+	case CFG_SET_MODE:
+		rc = ov7692_set_sensor_mode(cdata.mode, cdata.rs);
+		break;
+	case CFG_SET_EFFECT:
+		CDBG("--CAMERA-- CFG_SET_EFFECT mode=%d, effect = %d !!\n",
+			 cdata.mode, cdata.cfg.effect);
+		rc = ov7692_set_effect(cdata.mode, cdata.cfg.effect);
+		break;
+	case CFG_START:
+		CDBG("--CAMERA-- CFG_START (Not Support) !!\n");
+		/* Not Support */
+		break;
+	case CFG_PWR_UP:
+		CDBG("--CAMERA-- CFG_PWR_UP (Not Support) !!\n");
+		/* Not Support */
+		break;
+	case CFG_PWR_DOWN:
+		CDBG("--CAMERA-- CFG_PWR_DOWN !!\n");
+		ov7692_power_down();
+		break;
+	case CFG_WRITE_EXPOSURE_GAIN:
+		CDBG("--CAMERA-- CFG_WRITE_EXPOSURE_GAIN (Not Support) !!\n");
+		/* Not Support */
+		break;
+	case CFG_SET_DEFAULT_FOCUS:
+		CDBG("--CAMERA-- CFG_SET_DEFAULT_FOCUS (Not Implement) !!\n");
+		break;
+	case CFG_MOVE_FOCUS:
+		CDBG("--CAMERA-- CFG_MOVE_FOCUS (Not Implement) !!\n");
+		break;
+	case CFG_REGISTER_TO_REAL_GAIN:
+		CDBG("--CAMERA-- CFG_REGISTER_TO_REAL_GAIN (Not Support) !!\n");
+		/* Not Support */
+		break;
+	case CFG_REAL_TO_REGISTER_GAIN:
+		CDBG("--CAMERA-- CFG_REAL_TO_REGISTER_GAIN (Not Support) !!\n");
+		/* Not Support */
+		break;
+	case CFG_SET_FPS:
+		CDBG("--CAMERA-- CFG_SET_FPS (Not Support) !!\n");
+		/* Not Support */
+		break;
+	case CFG_SET_PICT_FPS:
+		CDBG("--CAMERA-- CFG_SET_PICT_FPS (Not Support) !!\n");
+		/* Not Support */
+		break;
+	case CFG_SET_BRIGHTNESS:
+		CDBG("--CAMERA-- CFG_SET_BRIGHTNESS  !!\n");
+		/* rc = ov7692_set_brightness(cdata.cfg.brightness); */
+		break;
+	case CFG_SET_CONTRAST:
+		CDBG("--CAMERA-- CFG_SET_CONTRAST  !!\n");
+		rc = ov7692_set_contrast(cdata.cfg.contrast);
+		break;
+	case CFG_SET_ZOOM:
+		CDBG("--CAMERA-- CFG_SET_ZOOM (Not Support) !!\n");
+		/* Not Support */
+		break;
+	case CFG_SET_EXPOSURE_MODE:
+		CDBG("--CAMERA-- CFG_SET_EXPOSURE_MODE !!\n");
+		/* rc = ov7692_set_exposure_mode(cdata.cfg.ae_mode); */
+		break;
+	case CFG_SET_WB:
+		CDBG("--CAMERA-- CFG_SET_WB!!\n");
+		ov7692_set_wb_oem(cdata.cfg.wb_val);
+		rc = 0 ;
+		break;
+	case CFG_SET_ANTIBANDING:
+		CDBG("--CAMERA-- CFG_SET_ANTIBANDING antibanding = %d !!\n",
+			 cdata.cfg.antibanding);
+		rc = ov7692_set_antibanding(cdata.cfg.antibanding);
+		break;
+	case CFG_SET_EXP_GAIN:
+		CDBG("--CAMERA-- CFG_SET_EXP_GAIN (Not Support) !!\n");
+		/* Not Support */
+		break;
+	case CFG_SET_PICT_EXP_GAIN:
+		CDBG("--CAMERA-- CFG_SET_PICT_EXP_GAIN (Not Support) !!\n");
+		/* Not Support */
+		break;
+	case CFG_SET_LENS_SHADING:
+		CDBG("--CAMERA-- CFG_SET_LENS_SHADING !!\n");
+		/* rc = ov7692_lens_shading_enable(cdata.cfg.lens_shading); */
+		break;
+	case CFG_GET_PICT_FPS:
+		CDBG("--CAMERA-- CFG_GET_PICT_FPS (Not Support) !!\n");
+		/* Not Support */
+		break;
+	case CFG_GET_PREV_L_PF:
+		CDBG("--CAMERA-- CFG_GET_PREV_L_PF (Not Support) !!\n");
+		/* Not Support */
+		break;
+	case CFG_GET_PREV_P_PL:
+		CDBG("--CAMERA-- CFG_GET_PREV_P_PL (Not Support) !!\n");
+		/* Not Support */
+		break;
+	case CFG_GET_PICT_L_PF:
+		CDBG("--CAMERA-- CFG_GET_PICT_L_PF (Not Support) !!\n");
+		/* Not Support */
+		break;
+	case CFG_GET_PICT_P_PL:
+		CDBG("--CAMERA-- CFG_GET_PICT_P_PL (Not Support) !!\n");
+		/* Not Support */
+		break;
+	case CFG_GET_AF_MAX_STEPS:
+		CDBG("--CAMERA-- CFG_GET_AF_MAX_STEPS (Not Support) !!\n");
+		/* Not Support */
+		break;
+	case CFG_GET_PICT_MAX_EXP_LC:
+		CDBG("--CAMERA-- CFG_GET_PICT_MAX_EXP_LC (Not Support) !!\n");
+		/* Not Support */
+		break;
+	case CFG_SEND_WB_INFO:
+		CDBG("--CAMERA-- CFG_SEND_WB_INFO (Not Support) !!\n");
+		/* Not Support */
+		break;
+	case CFG_SENSOR_INIT:
+		CDBG("--CAMERA-- CFG_SENSOR_INIT (Not Support) !!\n");
+		/* Not Support */
+		break;
+	case CFG_SET_SATURATION:
+		CDBG("--CAMERA-- CFG_SET_SATURATION !!\n");
+		rc = ov7692_set_saturation(cdata.cfg.saturation);
+		break;
+	case CFG_SET_SHARPNESS:
+		CDBG("--CAMERA-- CFG_SET_SHARPNESS !!\n");
+		rc = ov7692_set_sharpness(cdata.cfg.sharpness);
+		break;
+	case CFG_SET_TOUCHAEC:
+		CDBG("--CAMERA-- CFG_SET_TOUCHAEC!!\n");
+		/* ov7692_set_touchaec(cdata.cfg.aec_cord.x,
+			 cdata.cfg.aec_cord.y); */
+		rc = 0 ;
+		break;
+	case CFG_SET_AUTO_FOCUS:
+		CDBG("--CAMERA-- CFG_SET_AUTO_FOCUS (Not Support) !!\n");
+		/* Not Support */
+		break;
+	case CFG_SET_AUTOFLASH:
+		CDBG("--CAMERA-- CFG_SET_AUTOFLASH (Not Support) !!\n");
+		/* Not Support */
+		break;
+	case CFG_SET_EXPOSURE_COMPENSATION:
+		CDBG("--CAMERA-- CFG_SET_EXPOSURE_COMPENSATION !\n");
+		rc = ov7692_set_exposure_compensation(
+			cdata.cfg.exp_compensation);
+		break;
+	case CFG_SET_ISO:
+		CDBG("--CAMERA-- CFG_SET_ISO !\n");
+		rc = ov7692_set_iso(cdata.cfg.iso_type);
+		break;
+	default:
+		CDBG("--CAMERA-- %s: Command=%d (Not Implement) !!\n",
+			 __func__, cdata.cfgtype);
+		rc = -EINVAL;
+		break;
+	}
+
+	mutex_unlock(&ov7692_mut);
+
+	return rc;
+}
+static int ov7692_sensor_release(void)
+{
+	int rc = -EBADF;
+
+	mutex_lock(&ov7692_mut);
+	ov7692_sw_reset();
+	ov7692_power_down();
+	kfree(ov7692_ctrl);
+	ov7692_ctrl = NULL;
+	CDBG("ov7692_release completed\n");
+	mutex_unlock(&ov7692_mut);
+
+	return rc;
+}
+
+static int ov7692_probe_init_gpio(const struct msm_camera_sensor_info *data)
+{
+	int rc = 0;
+
+	ov7692_pwdn_gpio = data->sensor_pwd;
+	ov7692_reset_gpio = data->sensor_reset ;
+
+	if (data->sensor_reset_enable)
+		gpio_direction_output(data->sensor_reset, 1);
+
+	gpio_direction_output(data->sensor_pwd, 1);
+
+	return rc;
+
+}
+
+
+static int ov7692_sensor_probe(const struct msm_camera_sensor_info *info,
+		struct msm_sensor_ctrl *s)
+{
+	int rc = 0;
+	rc = i2c_add_driver(&ov7692_i2c_driver);
+	if (rc < 0 || ov7692_client == NULL) {
+		rc = -ENOTSUPP;
+		goto probe_fail;
+	}
+	pr_debug("%s: %d Entered\n", __func__, __LINE__);
+	rc = ov7692_probe_init_gpio(info);
+	if (rc < 0) {
+		CDBG("%s: gpio init failed\n", __func__);
+		goto probe_fail;
+	}
+	/* turn on LDO for PVT */
+	if (info->pmic_gpio_enable)
+		lcd_camera_power_onoff(1);
+
+	ov7692_power_down();
+
+	msm_camio_clk_rate_set(24000000);
+	usleep_range(5000, 5100);
+
+	ov7692_power_on();
+	usleep_range(5000, 5100);
+
+	if (info->sensor_reset_enable)
+		ov7692_hw_reset();
+	else
+		ov7692_sw_reset();
+
+	rc = ov7692_probe_init_sensor(info);
+	if (rc < 0)
+		goto probe_fail;
+
+
+	s->s_init = ov7692_sensor_open_init;
+	s->s_release = ov7692_sensor_release;
+	s->s_config  = ov7692_sensor_config;
+	s->s_camera_type = FRONT_CAMERA_2D;
+	s->s_mount_angle = info->sensor_platform_info->mount_angle;
+
+	/* ov7692_sw_reset(); */
+	ov7692_power_down();
+
+	if (info->pmic_gpio_enable)
+		lcd_camera_power_onoff(0);
+
+	return rc;
+
+probe_fail:
+	CDBG("ov7692_sensor_probe: SENSOR PROBE FAILS!\n");
+	if (info->pmic_gpio_enable)
+		lcd_camera_power_onoff(0);
+	i2c_del_driver(&ov7692_i2c_driver);
+	return rc;
+}
+
+static int __ov7692_probe(struct platform_device *pdev)
+{
+	return msm_camera_drv_start(pdev, ov7692_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+	.probe = __ov7692_probe,
+	.driver = {
+		.name = "msm_camera_ov7692",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init ov7692_init(void)
+{
+	return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(ov7692_init);
+
+MODULE_DESCRIPTION("OMNI VGA YUV sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/ov9726.c b/drivers/media/video/msm/ov9726.c
new file mode 100644
index 0000000..9619baa
--- /dev/null
+++ b/drivers/media/video/msm/ov9726.c
@@ -0,0 +1,794 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <media/msm_camera.h>
+#include <mach/gpio.h>
+#include <mach/camera.h>
+#include "ov9726.h"
+
+/*=============================================================
+	SENSOR REGISTER DEFINES
+==============================================================*/
+#define OV9726_Q8				0x00000100
+#define OV9726_Q8Shift				8
+#define OV9726_Q10				0x00000400
+#define OV9726_Q10Shift				10
+
+/* Omnivision8810 product ID register address */
+#define	OV9726_PIDH_REG				0x0000
+#define	OV9726_PIDL_REG				0x0001
+/* Omnivision8810 product ID */
+#define	OV9726_PID				0x97
+/* Omnivision8810 version */
+#define	OV9726_VER				0x26
+/* Time in milisecs for waiting for the sensor to reset */
+#define	OV9726_RESET_DELAY_MSECS		66
+#define	OV9726_DEFAULT_CLOCK_RATE		24000000
+/* Registers*/
+#define	OV9726_GAIN				0x3000
+#define	OV9726_AEC_MSB				0x3002
+#define	OV9726_AEC_LSB				0x3003
+
+/* Color bar pattern selection */
+#define OV9726_COLOR_BAR_PATTERN_SEL_REG	0x600
+/* Color bar enabling control */
+#define OV9726_COLOR_BAR_ENABLE_REG		0x601
+/* Time in milisecs for waiting for the sensor to reset*/
+#define OV9726_RESET_DELAY_MSECS		66
+/* I2C Address of the Sensor */
+/*============================================================================
+		DATA DECLARATIONS
+============================================================================*/
+#define OV9726_FULL_SIZE_DUMMY_PIXELS		0
+#define OV9726_FULL_SIZE_DUMMY_LINES		0
+#define OV9726_QTR_SIZE_DUMMY_PIXELS		0
+#define OV9726_QTR_SIZE_DUMMY_LINES		0
+
+#define OV9726_FULL_SIZE_WIDTH			1296
+#define OV9726_FULL_SIZE_HEIGHT			808
+
+#define OV9726_QTR_SIZE_WIDTH			1296
+#define OV9726_QTR_SIZE_HEIGHT			808
+
+#define OV9726_HRZ_FULL_BLK_PIXELS		368
+#define OV9726_VER_FULL_BLK_LINES		32
+#define OV9726_HRZ_QTR_BLK_PIXELS		368
+#define OV9726_VER_QTR_BLK_LINES		32
+
+#define OV9726_MSB_MASK			0xFF00
+#define OV9726_LSB_MASK			0x00FF
+
+struct ov9726_work_t {
+	struct work_struct work;
+};
+static struct ov9726_work_t *ov9726_sensorw;
+static struct i2c_client *ov9726_client;
+struct ov9726_ctrl_t {
+	const struct  msm_camera_sensor_info *sensordata;
+	uint32_t sensormode;
+	uint32_t fps_divider;		/* init to 1 * 0x00000400 */
+	uint32_t pict_fps_divider;	/* init to 1 * 0x00000400 */
+	uint16_t fps;
+	int16_t curr_lens_pos;
+	uint16_t curr_step_pos;
+	uint16_t my_reg_gain;
+	uint32_t my_reg_line_count;
+	uint16_t total_lines_per_frame;
+	enum ov9726_resolution_t prev_res;
+	enum ov9726_resolution_t pict_res;
+	enum ov9726_resolution_t curr_res;
+	enum ov9726_test_mode_t  set_test;
+	unsigned short imgaddr;
+};
+static struct ov9726_ctrl_t *ov9726_ctrl;
+static int8_t config_not_set = 1;
+static DECLARE_WAIT_QUEUE_HEAD(ov9726_wait_queue);
+DEFINE_MUTEX(ov9726_mut);
+
+/*=============================================================*/
+static int ov9726_i2c_rxdata(unsigned short saddr,
+	unsigned char *rxdata, int length)
+{
+	struct i2c_msg msgs[] = {
+	{
+		.addr  = saddr,
+		.flags = 0,
+		.len   = 2,
+		.buf   = rxdata,
+	},
+	{
+		.addr  = saddr,
+		.flags = I2C_M_RD,
+		.len   = length,
+		.buf   = rxdata,
+	},
+	};
+
+	if (i2c_transfer(ov9726_client->adapter, msgs, 2) < 0) {
+		CDBG("ov9726_i2c_rxdata failed!\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int32_t ov9726_i2c_txdata(unsigned short saddr,
+				unsigned char *txdata, int length)
+{
+	struct i2c_msg msg[] = {
+		{
+		 .addr = saddr ,
+		 .flags = 0,
+		 .len = length,
+		 .buf = txdata,
+		 },
+	};
+
+	if (i2c_transfer(ov9726_client->adapter, msg, 1) < 0) {
+		CDBG("ov9726_i2c_txdata faild 0x%x\n", ov9726_client->addr);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int32_t ov9726_i2c_read(unsigned short raddr,
+				unsigned short *rdata, int rlen)
+{
+	int32_t rc = 0;
+	unsigned char buf[2];
+
+	if (!rdata)
+		return -EIO;
+
+	buf[0] = (raddr & OV9726_MSB_MASK) >> 8;
+	buf[1] = (raddr & OV9726_LSB_MASK);
+
+	rc = ov9726_i2c_rxdata(ov9726_client->addr, buf, rlen);
+
+	if (rc < 0) {
+		CDBG("ov9726_i2c_read 0x%x failed!\n", raddr);
+		return rc;
+	}
+
+	*rdata = (rlen == 2 ? buf[0] << 8 | buf[1] : buf[0]);
+	return rc;
+}
+
+static int32_t ov9726_i2c_write_b(unsigned short saddr,
+	unsigned short waddr, uint8_t bdata)
+{
+	int32_t rc = -EFAULT;
+	unsigned char buf[3];
+
+	buf[0] = (waddr & OV9726_MSB_MASK) >> 8;
+	buf[1] = (waddr & OV9726_LSB_MASK);
+	buf[2] = bdata;
+
+	CDBG("i2c_write_b addr = 0x%x, val = 0x%xd\n", waddr, bdata);
+	rc = ov9726_i2c_txdata(saddr, buf, 3);
+
+	if (rc < 0) {
+		CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+			 waddr, bdata);
+	}
+
+	return rc;
+}
+
+static void ov9726_get_pict_fps(uint16_t fps, uint16_t *pfps)
+{
+	uint32_t divider;	/*Q10 */
+	uint32_t d1;
+	uint32_t d2;
+	uint16_t snapshot_height, preview_height, preview_width, snapshot_width;
+	if (ov9726_ctrl->prev_res == QTR_SIZE) {
+		preview_width = OV9726_QTR_SIZE_WIDTH +
+			OV9726_HRZ_QTR_BLK_PIXELS ;
+		preview_height = OV9726_QTR_SIZE_HEIGHT +
+			OV9726_VER_QTR_BLK_LINES ;
+	} else {
+		/* full size resolution used for preview. */
+		preview_width = OV9726_FULL_SIZE_WIDTH +
+			OV9726_HRZ_FULL_BLK_PIXELS ;
+		preview_height = OV9726_FULL_SIZE_HEIGHT +
+			OV9726_VER_FULL_BLK_LINES ;
+	}
+	if (ov9726_ctrl->pict_res == QTR_SIZE) {
+		snapshot_width  = OV9726_QTR_SIZE_WIDTH +
+			OV9726_HRZ_QTR_BLK_PIXELS ;
+		snapshot_height = OV9726_QTR_SIZE_HEIGHT +
+			OV9726_VER_QTR_BLK_LINES ;
+	} else {
+		snapshot_width  = OV9726_FULL_SIZE_WIDTH +
+			OV9726_HRZ_FULL_BLK_PIXELS;
+		snapshot_height = OV9726_FULL_SIZE_HEIGHT +
+			OV9726_VER_FULL_BLK_LINES;
+	}
+
+	d1 = (uint32_t)(((uint32_t)preview_height <<
+		OV9726_Q10Shift) /
+		snapshot_height);
+
+	d2 = (uint32_t)(((uint32_t)preview_width <<
+		OV9726_Q10Shift) /
+		 snapshot_width);
+
+	divider = (uint32_t) (d1 * d2) >> OV9726_Q10Shift;
+	*pfps = (uint16_t)((uint32_t)(fps * divider) >> OV9726_Q10Shift);
+}
+
+static uint16_t ov9726_get_prev_lines_pf(void)
+{
+	if (ov9726_ctrl->prev_res == QTR_SIZE)
+		return OV9726_QTR_SIZE_HEIGHT + OV9726_VER_QTR_BLK_LINES;
+	else
+		return OV9726_FULL_SIZE_HEIGHT + OV9726_VER_FULL_BLK_LINES;
+}
+
+static uint16_t ov9726_get_prev_pixels_pl(void)
+{
+	if (ov9726_ctrl->prev_res == QTR_SIZE)
+		return OV9726_QTR_SIZE_WIDTH + OV9726_HRZ_QTR_BLK_PIXELS;
+	else
+		return OV9726_FULL_SIZE_WIDTH + OV9726_HRZ_FULL_BLK_PIXELS;
+}
+
+static uint16_t ov9726_get_pict_lines_pf(void)
+{
+	if (ov9726_ctrl->pict_res == QTR_SIZE)
+		return OV9726_QTR_SIZE_HEIGHT + OV9726_VER_QTR_BLK_LINES;
+	else
+		return OV9726_FULL_SIZE_HEIGHT + OV9726_VER_FULL_BLK_LINES;
+}
+
+static uint16_t ov9726_get_pict_pixels_pl(void)
+{
+	if (ov9726_ctrl->pict_res == QTR_SIZE)
+		return OV9726_QTR_SIZE_WIDTH + OV9726_HRZ_QTR_BLK_PIXELS;
+	else
+		return OV9726_FULL_SIZE_WIDTH + OV9726_HRZ_FULL_BLK_PIXELS;
+}
+
+static uint32_t ov9726_get_pict_max_exp_lc(void)
+{
+	if (ov9726_ctrl->pict_res == QTR_SIZE)
+		return (OV9726_QTR_SIZE_HEIGHT + OV9726_VER_QTR_BLK_LINES)*24;
+	else
+		return (OV9726_FULL_SIZE_HEIGHT + OV9726_VER_FULL_BLK_LINES)*24;
+}
+
+static int32_t ov9726_set_fps(struct fps_cfg	*fps)
+{
+	int32_t rc = 0;
+	CDBG("%s: fps->fps_div = %d\n", __func__, fps->fps_div);
+	/* TODO: Passing of fps_divider from user space has issues. */
+	/* ov9726_ctrl->fps_divider = fps->fps_div; */
+	ov9726_ctrl->fps_divider = 1 * 0x400;
+	CDBG("%s: ov9726_ctrl->fps_divider = %d\n", __func__,
+		ov9726_ctrl->fps_divider);
+	ov9726_ctrl->pict_fps_divider = fps->pict_fps_div;
+	ov9726_ctrl->fps = fps->f_mult;
+	return rc;
+}
+
+static int32_t ov9726_write_exp_gain(uint16_t gain, uint32_t line)
+{
+	static uint16_t max_legal_gain = 0x00FF;
+	uint8_t gain_msb, gain_lsb;
+	uint8_t intg_time_msb, intg_time_lsb;
+	uint8_t ov9726_offset = 6;
+	uint8_t line_length_pck_msb, line_length_pck_lsb;
+	uint16_t line_length_pck, frame_length_lines;
+	uint32_t line_length_ratio = 1 << OV9726_Q8Shift;
+	int32_t rc = -1;
+	CDBG("%s: gain = %d	line = %d", __func__, gain, line);
+
+	if (ov9726_ctrl->sensormode != SENSOR_SNAPSHOT_MODE) {
+		if (ov9726_ctrl->curr_res == QTR_SIZE) {
+			frame_length_lines = OV9726_QTR_SIZE_HEIGHT +
+			 OV9726_VER_QTR_BLK_LINES;
+			line_length_pck = OV9726_QTR_SIZE_WIDTH	+
+			 OV9726_HRZ_QTR_BLK_PIXELS;
+		} else {
+			frame_length_lines = OV9726_FULL_SIZE_HEIGHT +
+				OV9726_VER_FULL_BLK_LINES;
+			line_length_pck = OV9726_FULL_SIZE_WIDTH +
+				OV9726_HRZ_FULL_BLK_PIXELS;
+		}
+		if (line > (frame_length_lines - ov9726_offset))
+			ov9726_ctrl->fps = (uint16_t) (((uint32_t)30 <<
+				OV9726_Q8Shift) *
+				(frame_length_lines - ov9726_offset) / line);
+		else
+			ov9726_ctrl->fps = (uint16_t) ((uint32_t)30 <<
+				OV9726_Q8Shift);
+	} else {
+		frame_length_lines = OV9726_FULL_SIZE_HEIGHT +
+			OV9726_VER_FULL_BLK_LINES;
+		line_length_pck = OV9726_FULL_SIZE_WIDTH +
+			OV9726_HRZ_FULL_BLK_PIXELS;
+	}
+
+	if (ov9726_ctrl->sensormode != SENSOR_SNAPSHOT_MODE) {
+		line = (uint32_t) (line * ov9726_ctrl->fps_divider) >>
+			OV9726_Q10Shift;
+	} else {
+		line = (uint32_t) (line * ov9726_ctrl->pict_fps_divider) >>
+			OV9726_Q10Shift;
+	}
+
+	/* calculate line_length_ratio */
+	if (line > (frame_length_lines - ov9726_offset)) {
+		line_length_ratio = (line << OV9726_Q8Shift) /
+			(frame_length_lines - ov9726_offset);
+		line = frame_length_lines - ov9726_offset;
+	} else
+		line_length_ratio = (uint32_t)1 << OV9726_Q8Shift;
+
+	if (gain > max_legal_gain) {
+		/* range:	0	to 224 */
+		gain = max_legal_gain;
+	}
+	/* update	gain registers */
+	gain_msb = (uint8_t) ((gain & 0xFF00) >> 8);
+	gain_lsb = (uint8_t) (gain & 0x00FF);
+	/* linear	AFR	horizontal stretch */
+	line_length_pck = (uint16_t) ((line_length_pck *
+		line_length_ratio) >> OV9726_Q8Shift);
+	line_length_pck_msb = (uint8_t) ((line_length_pck & 0xFF00) >> 8);
+	line_length_pck_lsb = (uint8_t) (line_length_pck & 0x00FF);
+	/* update	line count registers */
+	intg_time_msb = (uint8_t) ((line & 0xFF00) >> 8);
+	intg_time_lsb = (uint8_t) (line	& 0x00FF);
+
+	rc = ov9726_i2c_write_b(ov9726_client->addr, 0x104, 0x1);
+	if (rc < 0)
+		return rc;
+
+	rc = ov9726_i2c_write_b(ov9726_client->addr, 0x204, gain_msb);
+	if (rc < 0)
+		return rc;
+
+	rc = ov9726_i2c_write_b(ov9726_client->addr, 0x205, gain_lsb);
+	if (rc < 0)
+		return rc;
+
+	rc = ov9726_i2c_write_b(ov9726_client->addr, 0x342,
+		line_length_pck_msb);
+	if (rc < 0)
+		return rc;
+
+	rc = ov9726_i2c_write_b(ov9726_client->addr, 0x343,
+		line_length_pck_lsb);
+	if (rc < 0)
+		return rc;
+
+	rc = ov9726_i2c_write_b(ov9726_client->addr, 0x0202, intg_time_msb);
+	if (rc < 0)
+		return rc;
+
+	rc = ov9726_i2c_write_b(ov9726_client->addr, 0x0203, intg_time_lsb);
+	if (rc < 0)
+		return rc;
+
+	rc = ov9726_i2c_write_b(ov9726_client->addr, 0x104, 0x0);
+	if (rc < 0)
+		return rc;
+
+	return rc;
+}
+
+static int32_t ov9726_set_pict_exp_gain(uint16_t gain, uint32_t line)
+{
+	int32_t rc = 0;
+	rc = ov9726_write_exp_gain(gain, line);
+	return rc;
+}
+
+static int32_t initialize_ov9726_registers(void)
+{
+	int32_t i;
+	int32_t rc = 0;
+	ov9726_ctrl->sensormode = SENSOR_PREVIEW_MODE ;
+	/* Configure sensor for Preview mode and Snapshot mode */
+	CDBG("Initialize_ov9726_registers\n");
+	for (i = 0; i < ov9726_array_length; i++) {
+		rc = ov9726_i2c_write_b(ov9726_client->addr,
+			ov9726_init_settings_array[i].reg_addr,
+			ov9726_init_settings_array[i].reg_val);
+	if (rc < 0)
+		return rc;
+	}
+	return rc;
+}
+
+static int32_t ov9726_video_config(int mode)
+{
+	int32_t rc = 0;
+
+	ov9726_ctrl->sensormode = mode;
+
+	if (config_not_set) {
+		struct msm_camera_csi_params ov9726_csi_params;
+
+		/* sensor in standby */
+		ov9726_i2c_write_b(ov9726_client->addr, 0x100, 0);
+		msleep(5);
+		/* Initialize Sensor registers */
+		ov9726_csi_params.data_format = CSI_10BIT;
+		ov9726_csi_params.lane_cnt = 1;
+		ov9726_csi_params.lane_assign = 0xe4;
+		ov9726_csi_params.dpcm_scheme = 0;
+		ov9726_csi_params.settle_cnt = 7;
+
+		rc = msm_camio_csi_config(&ov9726_csi_params);
+		rc = initialize_ov9726_registers();
+		config_not_set = 0;
+	}
+	return rc;
+}
+
+static int32_t ov9726_snapshot_config(int mode)
+{
+	int32_t rc = 0;
+	ov9726_ctrl->sensormode = mode;
+	return rc;
+}
+
+static int32_t ov9726_raw_snapshot_config(int mode)
+{
+	int32_t rc = 0;
+	ov9726_ctrl->sensormode = mode;
+	return rc;
+}
+
+static int32_t ov9726_set_sensor_mode(int  mode,
+			int  res)
+{
+	int32_t rc = 0;
+	switch (mode) {
+	case SENSOR_PREVIEW_MODE:
+		rc = ov9726_video_config(mode);
+		break;
+	case SENSOR_SNAPSHOT_MODE:
+		rc = ov9726_snapshot_config(mode);
+		break;
+	case SENSOR_RAW_SNAPSHOT_MODE:
+		rc = ov9726_raw_snapshot_config(mode);
+		break;
+	default:
+		rc = -EINVAL;
+		break;
+	}
+	return rc;
+}
+
+static int ov9726_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+	int32_t rc = 0;
+	uint16_t  chipidl, chipidh;
+
+	if (data->sensor_reset_enable) {
+		rc = gpio_request(data->sensor_reset, "ov9726");
+		if (!rc) {
+			gpio_direction_output(data->sensor_reset, 0);
+			gpio_set_value_cansleep(data->sensor_reset, 1);
+			msleep(20);
+		} else
+			goto init_probe_done;
+	}
+	/* 3. Read sensor Model ID: */
+	rc = ov9726_i2c_read(OV9726_PIDH_REG, &chipidh, 1);
+	if (rc < 0)
+		goto init_probe_fail;
+	rc = ov9726_i2c_read(OV9726_PIDL_REG, &chipidl, 1);
+	if (rc < 0)
+		goto init_probe_fail;
+	CDBG("kov9726 model_id = 0x%x  0x%x\n", chipidh, chipidl);
+	/* 4. Compare sensor ID to OV9726 ID: */
+	if (chipidh != OV9726_PID) {
+		rc = -ENODEV;
+		printk(KERN_INFO "Probeinit fail\n");
+		goto init_probe_fail;
+	}
+	CDBG("chipidh == OV9726_PID\n");
+	msleep(OV9726_RESET_DELAY_MSECS);
+	CDBG("after delay\n");
+	goto init_probe_done;
+
+init_probe_fail:
+	if (data->sensor_reset_enable) {
+		gpio_direction_output(data->sensor_reset, 0);
+		gpio_free(data->sensor_reset);
+	}
+init_probe_done:
+	printk(KERN_INFO " ov9726_probe_init_sensor finishes\n");
+	return rc;
+}
+
+int ov9726_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+	int32_t  rc;
+
+	CDBG("Calling ov9726_sensor_open_init\n");
+	ov9726_ctrl = kzalloc(sizeof(struct ov9726_ctrl_t), GFP_KERNEL);
+	if (!ov9726_ctrl) {
+		CDBG("ov9726_init failed!\n");
+		rc = -ENOMEM;
+		goto init_done;
+	}
+	ov9726_ctrl->curr_lens_pos = -1;
+	ov9726_ctrl->fps_divider = 1 << OV9726_Q10Shift;
+	ov9726_ctrl->pict_fps_divider = 1 << OV9726_Q10Shift;
+	ov9726_ctrl->set_test = TEST_OFF;
+	ov9726_ctrl->prev_res = FULL_SIZE;
+	ov9726_ctrl->pict_res = FULL_SIZE;
+	ov9726_ctrl->curr_res = INVALID_SIZE;
+	config_not_set = 1;
+	if (data)
+		ov9726_ctrl->sensordata = data;
+	/* enable mclk first */
+	msm_camio_clk_rate_set(OV9726_DEFAULT_CLOCK_RATE);
+	msleep(20);
+	rc = ov9726_probe_init_sensor(data);
+	if (rc < 0)
+		goto init_fail;
+
+	ov9726_ctrl->fps = (uint16_t)(30 << OV9726_Q8Shift);
+	/* generate test pattern */
+	if (rc < 0)
+		goto init_fail;
+	else
+		goto init_done;
+	/* reset the driver state */
+init_fail:
+	CDBG(" init_fail\n");
+	kfree(ov9726_ctrl);
+init_done:
+	CDBG("init_done\n");
+	return rc;
+}
+
+static int ov9726_init_client(struct i2c_client *client)
+{
+	/* Initialize the MSM_CAMI2C Chip */
+	init_waitqueue_head(&ov9726_wait_queue);
+	return 0;
+}
+
+static const struct i2c_device_id ov9726_i2c_id[] = {
+	{ "ov9726", 0},
+	{ }
+};
+
+static int ov9726_i2c_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	int rc = 0;
+	CDBG("ov9726_probe called!\n");
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		CDBG("i2c_check_functionality failed\n");
+		goto probe_failure;
+	}
+	ov9726_sensorw = kzalloc(sizeof(struct ov9726_work_t), GFP_KERNEL);
+	if (!ov9726_sensorw) {
+		CDBG("kzalloc failed.\n");
+		rc = -ENOMEM;
+		goto probe_failure;
+	}
+	i2c_set_clientdata(client, ov9726_sensorw);
+	ov9726_init_client(client);
+	ov9726_client = client;
+	msleep(50);
+	CDBG("ov9726_probe successed! rc = %d\n", rc);
+	return 0;
+probe_failure:
+	CDBG("ov9726_probe failed! rc = %d\n", rc);
+	return rc;
+}
+
+static int __exit ov9726_remove(struct i2c_client *client)
+{
+	struct ov9726_work_t_t *sensorw = i2c_get_clientdata(client);
+	free_irq(client->irq, sensorw);
+	ov9726_client = NULL;
+	kfree(sensorw);
+	return 0;
+}
+
+static struct i2c_driver ov9726_i2c_driver = {
+	.id_table = ov9726_i2c_id,
+	.probe	= ov9726_i2c_probe,
+	.remove = __exit_p(ov9726_i2c_remove),
+	.driver = {
+		.name = "ov9726",
+	},
+};
+
+int ov9726_sensor_config(void __user *argp)
+{
+	struct sensor_cfg_data cdata;
+	long   rc = 0;
+
+	if (copy_from_user(&cdata,
+				(void *)argp,
+				sizeof(struct sensor_cfg_data)))
+		return -EFAULT;
+	mutex_lock(&ov9726_mut);
+	CDBG("ov9726_sensor_config: cfgtype = %d\n",
+		cdata.cfgtype);
+	switch (cdata.cfgtype) {
+	case CFG_GET_PICT_FPS:
+		ov9726_get_pict_fps(cdata.cfg.gfps.prevfps,
+				&(cdata.cfg.gfps.pictfps));
+		if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+			break;
+	case CFG_GET_PREV_L_PF:
+		cdata.cfg.prevl_pf = ov9726_get_prev_lines_pf();
+		if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+	case CFG_GET_PREV_P_PL:
+		cdata.cfg.prevp_pl = ov9726_get_prev_pixels_pl();
+		if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+	case CFG_GET_PICT_L_PF:
+		cdata.cfg.pictl_pf = ov9726_get_pict_lines_pf();
+		if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+	case CFG_GET_PICT_P_PL:
+		cdata.cfg.pictp_pl =
+				ov9726_get_pict_pixels_pl();
+		if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+	case CFG_GET_PICT_MAX_EXP_LC:
+		cdata.cfg.pict_max_exp_lc = ov9726_get_pict_max_exp_lc();
+		if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+	case CFG_SET_FPS:
+	case CFG_SET_PICT_FPS:
+		rc = ov9726_set_fps(&(cdata.cfg.fps));
+		break;
+	case CFG_SET_EXP_GAIN:
+		rc = ov9726_write_exp_gain(
+					cdata.cfg.exp_gain.gain,
+					cdata.cfg.exp_gain.line);
+		break;
+	case CFG_SET_PICT_EXP_GAIN:
+		rc = ov9726_set_pict_exp_gain(
+					cdata.cfg.exp_gain.gain,
+					cdata.cfg.exp_gain.line);
+		break;
+	case CFG_SET_MODE:
+		rc = ov9726_set_sensor_mode(cdata.mode,
+						cdata.rs);
+		break;
+	case CFG_PWR_DOWN:
+	case CFG_MOVE_FOCUS:
+	case CFG_SET_DEFAULT_FOCUS:
+		rc = 0;
+		break;
+	case CFG_SET_EFFECT:
+	default:
+		rc = -EFAULT;
+		break;
+	}
+	mutex_unlock(&ov9726_mut);
+	return rc;
+}
+
+static int ov9726_probe_init_done(const struct msm_camera_sensor_info *data)
+{
+	if (data->sensor_reset_enable) {
+		gpio_direction_output(data->sensor_reset, 0);
+		gpio_free(data->sensor_reset);
+	}
+	return 0;
+}
+
+static int ov9726_sensor_release(void)
+{
+	int rc = -EBADF;
+	mutex_lock(&ov9726_mut);
+	if (ov9726_ctrl->sensordata->sensor_reset_enable) {
+		gpio_direction_output(
+			ov9726_ctrl->sensordata->sensor_reset, 0);
+		gpio_free(ov9726_ctrl->sensordata->sensor_reset);
+	}
+	kfree(ov9726_ctrl);
+	ov9726_ctrl = NULL;
+	CDBG("ov9726_release completed\n");
+	mutex_unlock(&ov9726_mut);
+	return rc;
+}
+
+static int ov9726_sensor_probe(const struct msm_camera_sensor_info *info,
+		struct msm_sensor_ctrl *s)
+{
+	int rc = 0;
+
+	rc = i2c_add_driver(&ov9726_i2c_driver);
+	if (rc < 0 || ov9726_client == NULL) {
+		rc = -ENOTSUPP;
+		goto probe_fail;
+	}
+	msm_camio_clk_rate_set(24000000);
+	msleep(20);
+	rc = ov9726_probe_init_sensor(info);
+	if (rc < 0)
+		goto probe_fail;
+
+	s->s_init = ov9726_sensor_open_init;
+	s->s_release = ov9726_sensor_release;
+	s->s_config  = ov9726_sensor_config;
+	s->s_camera_type = FRONT_CAMERA_2D;
+	s->s_mount_angle = info->sensor_platform_info->mount_angle;
+	ov9726_probe_init_done(info);
+
+	return rc;
+
+probe_fail:
+	CDBG("SENSOR PROBE FAILS!\n");
+	return rc;
+}
+
+static int __ov9726_probe(struct platform_device *pdev)
+{
+	return msm_camera_drv_start(pdev, ov9726_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+	.probe = __ov9726_probe,
+	.driver = {
+		.name = "msm_camera_ov9726",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init ov9726_init(void)
+{
+	return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(ov9726_init);
+void ov9726_exit(void)
+{
+	i2c_del_driver(&ov9726_i2c_driver);
+}
+
+MODULE_DESCRIPTION("OMNI VGA Bayer sensor driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/media/video/msm/ov9726.h b/drivers/media/video/msm/ov9726.h
new file mode 100644
index 0000000..56d3da6
--- /dev/null
+++ b/drivers/media/video/msm/ov9726.h
@@ -0,0 +1,39 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef OV9726_H
+#define OV9726_H
+#include <linux/types.h>
+#include <mach/board.h>
+
+/* 16bit address - 8 bit context register structure */
+struct reg_struct_type {
+	uint16_t	reg_addr;
+	unsigned char	reg_val;
+};
+
+enum ov9726_test_mode_t {
+	TEST_OFF,
+	TEST_1,
+	TEST_2,
+	TEST_3
+};
+
+enum ov9726_resolution_t {
+	QTR_SIZE,
+	FULL_SIZE,
+	INVALID_SIZE
+};
+extern struct reg_struct_type ov9726_init_settings_array[];
+extern int32_t ov9726_array_length;
+#endif
+
diff --git a/drivers/media/video/msm/ov9726_reg.c b/drivers/media/video/msm/ov9726_reg.c
new file mode 100644
index 0000000..54afbe8
--- /dev/null
+++ b/drivers/media/video/msm/ov9726_reg.c
@@ -0,0 +1,101 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "ov9726.h"
+struct reg_struct_type ov9726_init_settings_array[] = {
+	{0x0103, 0x01}, /* SOFTWARE_RESET */
+	{0x3026, 0x00}, /* OUTPUT_SELECT01 */
+	{0x3027, 0x00}, /* OUTPUT_SELECT02 */
+	{0x3002, 0xe8}, /* IO_CTRL00 */
+	{0x3004, 0x03}, /* IO_CTRL01 */
+	{0x3005, 0xff}, /* IO_CTRL02 */
+	{0x3703, 0x42},
+	{0x3704, 0x10},
+	{0x3705, 0x45},
+	{0x3603, 0xaa},
+	{0x3632, 0x2f},
+	{0x3620, 0x66},
+	{0x3621, 0xc0},
+	{0x0340, 0x03}, /* FRAME_LENGTH_LINES_HI */
+	{0x0341, 0xC1}, /* FRAME_LENGTH_LINES_LO */
+	{0x0342, 0x06}, /* LINE_LENGTH_PCK_HI */
+	{0x0343, 0x80}, /* LINE_LENGTH_PCK_LO */
+	{0x0202, 0x03}, /* COARSE_INTEGRATION_TIME_HI */
+	{0x0203, 0x43}, /* COARSE_INTEGRATION_TIME_LO */
+	{0x3833, 0x04},
+	{0x3835, 0x02},
+	{0x4702, 0x04},
+	{0x4704, 0x00}, /* DVP_CTRL01 */
+	{0x4706, 0x08},
+	{0x5052, 0x01},
+	{0x3819, 0x6e},
+	{0x3817, 0x94},
+	{0x3a18, 0x00}, /* AEC_GAIN_CEILING_HI */
+	{0x3a19, 0x7f}, /* AEC_GAIN_CEILING_LO */
+	{0x404e, 0x7e},
+	{0x3631, 0x52},
+	{0x3633, 0x50},
+	{0x3630, 0xd2},
+	{0x3604, 0x08},
+	{0x3601, 0x40},
+	{0x3602, 0x14},
+	{0x3610, 0xa0},
+	{0x3612, 0x20},
+	{0x034c, 0x05}, /* X_OUTPUT_SIZE_HI */
+	{0x034d, 0x10}, /* X_OUTPUT_SIZE_LO */
+	{0x034e, 0x03}, /* Y_OUTPUT_SIZE_HI */
+	{0x034f, 0x28}, /* Y_OUTPUT_SIZE_LO */
+	{0x0340, 0x03}, /* FRAME_LENGTH_LINES_HI */
+	{0x0341, 0xC1}, /* FRAME_LENGTH_LINES_LO */
+	{0x0342, 0x06}, /* LINE_LENGTH_PCK_HI */
+	{0x0343, 0x80}, /* LINE_LENGTH_PCK_LO */
+	{0x0202, 0x03}, /* COARSE_INTEGRATION_TIME_HI */
+	{0x0203, 0x43}, /* COARSE_INTEGRATION_TIME_LO */
+	{0x0303, 0x01}, /* VT_SYS_CLK_DIV_LO */
+	{0x3002, 0x00}, /* IO_CTRL00 */
+	{0x3004, 0x00}, /* IO_CTRL01 */
+	{0x3005, 0x00}, /* IO_CTRL02 */
+	{0x4801, 0x0f}, /* MIPI_CTRL01 */
+	{0x4803, 0x05}, /* MIPI_CTRL03 */
+	{0x4601, 0x16}, /* VFIFO_READ_CONTROL */
+	{0x3014, 0x05}, /* SC_CMMN_MIPI / SC_CTRL00 */
+	{0x3104, 0x80},
+	{0x0305, 0x04}, /* PRE_PLL_CLK_DIV_LO */
+	{0x0307, 0x64}, /* PLL_MULTIPLIER_LO */
+	{0x300c, 0x02},
+	{0x300d, 0x20},
+	{0x300e, 0x01},
+	{0x3010, 0x01},
+	{0x460e, 0x81}, /* VFIFO_CONTROL00 */
+	{0x0101, 0x01}, /* IMAGE_ORIENTATION */
+	{0x3707, 0x14},
+	{0x3622, 0x9f},
+	{0x5047, 0x3D}, /* ISP_CTRL47 */
+	{0x4002, 0x45}, /* BLC_CTRL02 */
+	{0x5000, 0x06}, /* ISP_CTRL0 */
+	{0x5001, 0x00}, /* ISP_CTRL1 */
+	{0x3406, 0x00}, /* AWB_MANUAL_CTRL */
+	{0x3503, 0x13}, /* AEC_ENABLE */
+	{0x4005, 0x18}, /* BLC_CTRL05 */
+	{0x4837, 0x21},
+	{0x0100, 0x01}, /* MODE_SELECT */
+	{0x3a0f, 0x64}, /* AEC_CTRL0F */
+	{0x3a10, 0x54}, /* AEC_CTRL10 */
+	{0x3a11, 0xc2}, /* AEC_CTRL11 */
+	{0x3a1b, 0x64}, /* AEC_CTRL1B */
+	{0x3a1e, 0x54}, /* AEC_CTRL1E */
+	{0x3a1a, 0x05}, /* AEC_DIFF_MAX */
+};
+int32_t ov9726_array_length = sizeof(ov9726_init_settings_array) /
+	sizeof(ov9726_init_settings_array[0]);
+
diff --git a/drivers/media/video/msm/qs_s5k4e1.c b/drivers/media/video/msm/qs_s5k4e1.c
new file mode 100644
index 0000000..64db015
--- /dev/null
+++ b/drivers/media/video/msm/qs_s5k4e1.c
@@ -0,0 +1,1822 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <media/msm_camera.h>
+#include <mach/gpio.h>
+#include <mach/camera.h>
+#include "qs_s5k4e1.h"
+/*=============================================================
+	SENSOR REGISTER DEFINES
+==============================================================*/
+#define REG_GROUPED_PARAMETER_HOLD		0x0104
+#define GROUPED_PARAMETER_HOLD_OFF		0x00
+#define GROUPED_PARAMETER_HOLD			0x01
+/* Integration Time */
+#define REG_COARSE_INTEGRATION_TIME		0x0202
+/* Gain */
+#define REG_GLOBAL_GAIN					0x0204
+#define REG_GR_GAIN					0x020E
+#define REG_R_GAIN					0x0210
+#define REG_B_GAIN					0x0212
+#define REG_GB_GAIN					0x0214
+/* PLL registers */
+#define REG_FRAME_LENGTH_LINES			0x0340
+#define REG_LINE_LENGTH_PCK				0x0342
+/* Test Pattern */
+#define REG_TEST_PATTERN_MODE			0x0601
+#define REG_VCM_NEW_CODE				0x30F2
+#define AF_ADDR							0x18
+#define BRIDGE_ADDR						0x80
+/*============================================================================
+			 TYPE DECLARATIONS
+============================================================================*/
+
+/* 16bit address - 8 bit context register structure */
+#define Q8  0x00000100
+#define Q10 0x00000400
+#define QS_S5K4E1_MASTER_CLK_RATE 24000000
+#define QS_S5K4E1_OFFSET			8
+
+/* AF Total steps parameters */
+#define QS_S5K4E1_TOTAL_STEPS_NEAR_TO_FAR    32
+#define QS_S5K4E1_TOTAL_STEPS_3D    32
+
+uint16_t qs_s5k4e1_step_position_table[QS_S5K4E1_TOTAL_STEPS_NEAR_TO_FAR+1];
+uint16_t qs_s5k4e1_step_position_table_left[QS_S5K4E1_TOTAL_STEPS_3D+1];
+uint16_t qs_s5k4e1_step_position_table_right[QS_S5K4E1_TOTAL_STEPS_3D+1];
+uint16_t qs_s5k4e1_nl_region_boundary1;
+uint16_t qs_s5k4e1_nl_region_code_per_step1 = 190;
+uint16_t qs_s5k4e1_l_region_code_per_step = 8;
+uint16_t qs_s5k4e1_damping_threshold = 10;
+uint16_t qs_s5k4e1_sw_damping_time_wait = 8;
+uint16_t qs_s5k4e1_af_mode = 4;
+int16_t qs_s5k4e1_af_initial_code = 190;
+int16_t qs_s5k4e1_af_right_adjust;
+
+struct qs_s5k4e1_work_t {
+	struct work_struct work;
+};
+
+static struct qs_s5k4e1_work_t *qs_s5k4e1_sensorw;
+static struct i2c_client *qs_s5k4e1_client;
+static char lens_eeprom_data[864];
+static bool cali_data_status;
+struct qs_s5k4e1_ctrl_t {
+	const struct  msm_camera_sensor_info *sensordata;
+
+	uint32_t sensormode;
+	uint32_t fps_divider;/* init to 1 * 0x00000400 */
+	uint32_t pict_fps_divider;/* init to 1 * 0x00000400 */
+	uint16_t fps;
+
+	uint16_t curr_lens_pos;
+	uint16_t curr_step_pos;
+	uint16_t my_reg_gain;
+	uint32_t my_reg_line_count;
+	uint16_t total_lines_per_frame;
+
+	enum qs_s5k4e1_resolution_t prev_res;
+	enum qs_s5k4e1_resolution_t pict_res;
+	enum qs_s5k4e1_resolution_t curr_res;
+	enum qs_s5k4e1_test_mode_t  set_test;
+	enum qs_s5k4e1_cam_mode_t cam_mode;
+};
+
+static uint16_t prev_line_length_pck;
+static uint16_t prev_frame_length_lines;
+static uint16_t snap_line_length_pck;
+static uint16_t snap_frame_length_lines;
+
+static bool CSI_CONFIG, LENS_SHADE_CONFIG, default_lens_shade;
+static struct qs_s5k4e1_ctrl_t *qs_s5k4e1_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(qs_s5k4e1_wait_queue);
+DEFINE_MUTEX(qs_s5k4e1_mut);
+
+static int cam_debug_init(void);
+static struct dentry *debugfs_base;
+/*=============================================================*/
+
+static int qs_s5k4e1_i2c_rxdata(unsigned short saddr,
+	unsigned char *rxdata, int length)
+{
+	struct i2c_msg msgs[] = {
+		{
+			.addr  = saddr,
+			.flags = 0,
+			.len   = 2,
+			.buf   = rxdata,
+		},
+		{
+			.addr  = saddr,
+			.flags = I2C_M_RD,
+			.len   = length,
+			.buf   = rxdata,
+		},
+	};
+	if (i2c_transfer(qs_s5k4e1_client->adapter, msgs, 2) < 0) {
+		CDBG("qs_s5k4e1_i2c_rxdata faild 0x%x\n", saddr);
+		return -EIO;
+	}
+	return 0;
+}
+
+static int32_t qs_s5k4e1_i2c_txdata(unsigned short saddr,
+				unsigned char *txdata, int length)
+{
+	struct i2c_msg msg[] = {
+		{
+			.addr = saddr,
+			.flags = 0,
+			.len = length,
+			.buf = txdata,
+		 },
+	};
+	if (i2c_transfer(qs_s5k4e1_client->adapter, msg, 1) < 0) {
+		CDBG("qs_s5k4e1_i2c_txdata faild 0x%x\n", saddr);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int32_t qs_s5k4e1_i2c_read(unsigned short raddr,
+	unsigned short *rdata, int rlen)
+{
+	int32_t rc = 0;
+	unsigned char buf[2];
+	if (!rdata)
+		return -EIO;
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (raddr & 0xFF00) >> 8;
+	buf[1] = (raddr & 0x00FF);
+	rc = qs_s5k4e1_i2c_rxdata(qs_s5k4e1_client->addr>>1, buf, rlen);
+	if (rc < 0) {
+		CDBG("qs_s5k4e1_i2c_read 0x%x failed!\n", raddr);
+		return rc;
+	}
+	*rdata = (rlen == 2 ? buf[0] << 8 | buf[1] : buf[0]);
+	CDBG("qs_s5k4e1_i2c_read 0x%x val = 0x%x!\n", raddr, *rdata);
+	return rc;
+}
+
+static int32_t qs_s5k4e1_i2c_write_w_sensor(unsigned short waddr,
+	 uint16_t wdata)
+{
+	int32_t rc = -EFAULT;
+	unsigned char buf[4];
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (waddr & 0xFF00) >> 8;
+	buf[1] = (waddr & 0x00FF);
+	buf[2] = (wdata & 0xFF00) >> 8;
+	buf[3] = (wdata & 0x00FF);
+	CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, wdata);
+	rc = qs_s5k4e1_i2c_txdata(qs_s5k4e1_client->addr>>1, buf, 4);
+	if (rc < 0) {
+		CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+			waddr, wdata);
+	}
+	return rc;
+}
+
+static int32_t qs_s5k4e1_i2c_write_b_sensor(unsigned short waddr, uint8_t bdata)
+{
+	int32_t rc = -EFAULT;
+	unsigned char buf[3];
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (waddr & 0xFF00) >> 8;
+	buf[1] = (waddr & 0x00FF);
+	buf[2] = bdata;
+	CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, bdata);
+	rc = qs_s5k4e1_i2c_txdata(qs_s5k4e1_client->addr>>1, buf, 3);
+	if (rc < 0) {
+		CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+			waddr, bdata);
+	}
+	return rc;
+}
+
+static int32_t qs_s5k4e1_i2c_write_b_table(struct qs_s5k4e1_i2c_reg_conf const
+					 *reg_conf_tbl, int num)
+{
+	int i;
+	int32_t rc = -EIO;
+	for (i = 0; i < num; i++) {
+		rc = qs_s5k4e1_i2c_write_b_sensor(reg_conf_tbl->waddr,
+			reg_conf_tbl->wdata);
+		if (rc < 0)
+			break;
+		reg_conf_tbl++;
+	}
+	return rc;
+}
+
+static int32_t qs_s5k4e1_i2c_write_seq_sensor(unsigned short waddr,
+		unsigned char *seq_data, int len)
+{
+	int32_t rc = -EFAULT;
+	unsigned char buf[len+2];
+	int i = 0;
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (waddr & 0xFF00) >> 8;
+	buf[1] = (waddr & 0x00FF);
+	for (i = 0; i < len; i++)
+		buf[i+2] = seq_data[i];
+	rc = qs_s5k4e1_i2c_txdata(qs_s5k4e1_client->addr>>1, buf, len+2);
+	return rc;
+}
+
+static int32_t af_i2c_write_b_sensor(unsigned short baddr, uint8_t bdata)
+{
+	int32_t rc = -EFAULT;
+	unsigned char buf[2];
+	memset(buf, 0, sizeof(buf));
+	buf[0] = baddr;
+	buf[1] = bdata;
+	CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", baddr, bdata);
+	rc = qs_s5k4e1_i2c_txdata(AF_ADDR>>1, buf, 2);
+	if (rc < 0) {
+		CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+			baddr, bdata);
+	}
+	return rc;
+}
+
+static int32_t bridge_i2c_write_w(unsigned short waddr, uint16_t wdata)
+{
+	int32_t rc = -EFAULT;
+	unsigned char buf[4];
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (waddr & 0xFF00) >> 8;
+	buf[1] = (waddr & 0x00FF);
+	buf[2] = (wdata & 0xFF00) >> 8;
+	buf[3] = (wdata & 0x00FF);
+	CDBG("bridge_i2c_write_w addr = 0x%x, val = 0x%x\n", waddr, wdata);
+	rc = qs_s5k4e1_i2c_txdata(BRIDGE_ADDR>>1, buf, 4);
+	if (rc < 0) {
+		CDBG("bridge_i2c_write_w failed, addr = 0x%x, val = 0x%x!\n",
+			waddr, wdata);
+	}
+	return rc;
+}
+
+static int32_t bridge_i2c_read(unsigned short raddr,
+	unsigned short *rdata, int rlen)
+{
+	int32_t rc = 0;
+	unsigned char buf[2];
+	if (!rdata)
+		return -EIO;
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (raddr & 0xFF00) >> 8;
+	buf[1] = (raddr & 0x00FF);
+	rc = qs_s5k4e1_i2c_rxdata(BRIDGE_ADDR>>1, buf, rlen);
+	if (rc < 0) {
+		CDBG("bridge_i2c_read 0x%x failed!\n", raddr);
+		return rc;
+	}
+	*rdata = (rlen == 2 ? buf[0] << 8 | buf[1] : buf[0]);
+	CDBG("bridge_i2c_read 0x%x val = 0x%x!\n", raddr, *rdata);
+	return rc;
+}
+
+static int32_t qs_s5k4e1_eeprom_i2c_read(unsigned short raddr,
+	unsigned char *rdata, int rlen)
+{
+	int32_t rc = 0;
+	unsigned short i2caddr = 0xA0 >> 1;
+	unsigned char buf[rlen];
+	int i = 0;
+	if (!rdata)
+		return -EIO;
+
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (raddr & 0xFF00) >> 8;
+	buf[1] = (raddr & 0x00FF);
+	rc = qs_s5k4e1_i2c_rxdata(i2caddr, buf, rlen);
+	if (rc < 0) {
+		CDBG("qs_s5k4e1_eeprom_i2c_read 0x%x failed!\n", raddr);
+		return rc;
+	}
+	for (i = 0; i < rlen; i++) {
+		rdata[i] = buf[i];
+		CDBG("qs_s5k4e1_eeprom_i2c_read 0x%x index: %d val = 0x%x!\n",
+			raddr, i, buf[i]);
+	}
+	return rc;
+}
+
+static int32_t qs_s5k4e1_eeprom_i2c_read_b(unsigned short raddr,
+	unsigned short *rdata, int rlen)
+{
+	int32_t rc = 0;
+	unsigned char buf[2];
+	rc = qs_s5k4e1_eeprom_i2c_read(raddr, &buf[0], rlen);
+	*rdata = (rlen == 2 ? buf[0] << 8 | buf[1] : buf[0]);
+	CDBG("qs_s5k4e1_eeprom_i2c_read 0x%x val = 0x%x!\n", raddr, *rdata);
+	return rc;
+}
+
+static int32_t qs_s5k4e1_get_calibration_data(
+	struct sensor_3d_cali_data_t *cdata)
+{
+	int32_t rc = 0;
+	cali_data_status = 1;
+	rc = qs_s5k4e1_eeprom_i2c_read(0x0,
+		&(cdata->left_p_matrix[0][0][0]), 96);
+	if (rc < 0)
+		goto fail;
+	rc = qs_s5k4e1_eeprom_i2c_read(0x60,
+		&(cdata->right_p_matrix[0][0][0]), 96);
+	if (rc < 0)
+		goto fail;
+	rc = qs_s5k4e1_eeprom_i2c_read(0xC0, &(cdata->square_len[0]), 8);
+	if (rc < 0)
+		goto fail;
+	rc = qs_s5k4e1_eeprom_i2c_read(0xC8, &(cdata->focal_len[0]), 8);
+	if (rc < 0)
+		goto fail;
+	rc = qs_s5k4e1_eeprom_i2c_read(0xD0, &(cdata->pixel_pitch[0]), 8);
+	if (rc < 0)
+		goto fail;
+	rc = qs_s5k4e1_eeprom_i2c_read_b(0x100, &(cdata->left_r), 1);
+	if (rc < 0)
+		goto fail;
+	rc = qs_s5k4e1_eeprom_i2c_read_b(0x101, &(cdata->right_r), 1);
+	if (rc < 0)
+		goto fail;
+	rc = qs_s5k4e1_eeprom_i2c_read_b(0x102, &(cdata->left_b), 1);
+	if (rc < 0)
+		goto fail;
+	rc = qs_s5k4e1_eeprom_i2c_read_b(0x103, &(cdata->right_b), 1);
+	if (rc < 0)
+		goto fail;
+	rc = qs_s5k4e1_eeprom_i2c_read_b(0x104, &(cdata->left_gb), 1);
+	if (rc < 0)
+		goto fail;
+	rc = qs_s5k4e1_eeprom_i2c_read_b(0x105, &(cdata->right_gb), 1);
+	if (rc < 0)
+		goto fail;
+	rc = qs_s5k4e1_eeprom_i2c_read_b(0x110, &(cdata->left_af_far), 2);
+	if (rc < 0)
+		goto fail;
+	rc = qs_s5k4e1_eeprom_i2c_read_b(0x112, &(cdata->right_af_far), 2);
+	if (rc < 0)
+		goto fail;
+	rc = qs_s5k4e1_eeprom_i2c_read_b(0x114, &(cdata->left_af_mid), 2);
+	if (rc < 0)
+		goto fail;
+	rc = qs_s5k4e1_eeprom_i2c_read_b(0x116, &(cdata->right_af_mid), 2);
+	if (rc < 0)
+		goto fail;
+	rc = qs_s5k4e1_eeprom_i2c_read_b(0x118, &(cdata->left_af_short), 2);
+	if (rc < 0)
+		goto fail;
+	rc = qs_s5k4e1_eeprom_i2c_read_b(0x11A, &(cdata->right_af_short), 2);
+	if (rc < 0)
+		goto fail;
+	rc = qs_s5k4e1_eeprom_i2c_read_b(0x11C, &(cdata->left_af_5um), 2);
+	if (rc < 0)
+		goto fail;
+	rc = qs_s5k4e1_eeprom_i2c_read_b(0x11E, &(cdata->right_af_5um), 2);
+	if (rc < 0)
+		goto fail;
+	rc = qs_s5k4e1_eeprom_i2c_read_b(0x120, &(cdata->left_af_50up), 2);
+	if (rc < 0)
+		goto fail;
+	rc = qs_s5k4e1_eeprom_i2c_read_b(0x122, &(cdata->right_af_50up), 2);
+	if (rc < 0)
+		goto fail;
+	rc = qs_s5k4e1_eeprom_i2c_read_b(0x124, &(cdata->left_af_50down), 2);
+	if (rc < 0)
+		goto fail;
+	rc = qs_s5k4e1_eeprom_i2c_read_b(0x126, &(cdata->right_af_50down), 2);
+	if (rc < 0)
+		goto fail;
+
+	return 0;
+
+fail:
+	cali_data_status = 0;
+	return -EIO;
+
+}
+static int32_t qs_s5k4e1_write_left_lsc(char *left_lsc, int rt)
+{
+	struct qs_s5k4e1_i2c_reg_conf *ptr = (struct qs_s5k4e1_i2c_reg_conf *)
+		(qs_s5k4e1_regs.reg_lens + rt);
+	bridge_i2c_write_w(0x06, 0x02);
+	if (!LENS_SHADE_CONFIG) {
+		qs_s5k4e1_i2c_write_b_sensor(0x3096, 0x40);
+		qs_s5k4e1_i2c_write_b_table(ptr, qs_s5k4e1_regs.reg_lens_size);
+		if (default_lens_shade)
+			qs_s5k4e1_i2c_write_b_table(qs_s5k4e1_regs.
+			reg_default_lens, qs_s5k4e1_regs.reg_default_lens_size);
+		else {
+			qs_s5k4e1_i2c_write_seq_sensor(0x3200,
+				&left_lsc[0], 216);
+			qs_s5k4e1_i2c_write_seq_sensor(0x32D8,
+				&left_lsc[216], 216);
+		}
+		qs_s5k4e1_i2c_write_b_sensor(0x3096, 0x60);
+		qs_s5k4e1_i2c_write_b_sensor(0x3096, 0x40);
+	} else
+		qs_s5k4e1_i2c_write_b_table(ptr, qs_s5k4e1_regs.reg_lens_size);
+	return 0;
+}
+
+static int32_t qs_s5k4e1_write_right_lsc(char *right_lsc, int rt)
+{
+	struct qs_s5k4e1_i2c_reg_conf *ptr = (struct qs_s5k4e1_i2c_reg_conf *)
+		(qs_s5k4e1_regs.reg_lens + rt);
+	bridge_i2c_write_w(0x06, 0x01);
+	if (!LENS_SHADE_CONFIG) {
+		qs_s5k4e1_i2c_write_b_sensor(0x3096, 0x40);
+		qs_s5k4e1_i2c_write_b_table(ptr, qs_s5k4e1_regs.reg_lens_size);
+		if (default_lens_shade)
+			qs_s5k4e1_i2c_write_b_table(qs_s5k4e1_regs.
+			reg_default_lens, qs_s5k4e1_regs.reg_default_lens_size);
+		else {
+			qs_s5k4e1_i2c_write_seq_sensor(0x3200,
+				&right_lsc[0], 216);
+			qs_s5k4e1_i2c_write_seq_sensor(0x32D8,
+				&right_lsc[216], 216);
+		}
+		qs_s5k4e1_i2c_write_b_sensor(0x3096, 0x60);
+		qs_s5k4e1_i2c_write_b_sensor(0x3096, 0x40);
+	} else
+		qs_s5k4e1_i2c_write_b_table(ptr, qs_s5k4e1_regs.reg_lens_size);
+	return 0;
+}
+
+static int32_t qs_s5k4e1_write_lsc(char *lsc, int rt)
+{
+	if (qs_s5k4e1_ctrl->cam_mode == MODE_3D) {
+		qs_s5k4e1_write_left_lsc(&lsc[0], rt);
+		qs_s5k4e1_write_right_lsc(&lsc[432], rt);
+		bridge_i2c_write_w(0x06, 0x03);
+	} else if (qs_s5k4e1_ctrl->cam_mode == MODE_2D_LEFT)
+		qs_s5k4e1_write_left_lsc(&lsc[0], rt);
+	else if (qs_s5k4e1_ctrl->cam_mode == MODE_2D_RIGHT)
+		qs_s5k4e1_write_right_lsc(&lsc[432], rt);
+	return 0;
+}
+
+static int32_t qs_s5k4e1_read_left_lsc(char *left_lsc)
+{
+	qs_s5k4e1_eeprom_i2c_read(0x200, &left_lsc[0], 216);
+	qs_s5k4e1_eeprom_i2c_read(0x2D8, &left_lsc[216], 216);
+	return 0;
+}
+
+static int32_t qs_s5k4e1_read_right_lsc(char *right_lsc)
+{
+	qs_s5k4e1_eeprom_i2c_read(0x3B0, &right_lsc[0], 216);
+	qs_s5k4e1_eeprom_i2c_read(0x488, &right_lsc[216], 216);
+	return 0;
+}
+
+static int32_t qs_s5k4e1_read_lsc(char *lsc)
+{
+	qs_s5k4e1_read_left_lsc(&lsc[0]);
+	qs_s5k4e1_read_right_lsc(&lsc[432]);
+	return 0;
+}
+
+static int32_t qs_s5k4e1_bridge_reset(void){
+	unsigned short RegData = 0, GPIOInState = 0;
+	int32_t rc = 0;
+	rc = bridge_i2c_write_w(0x50, 0x00);
+	if (rc < 0)
+		goto bridge_fail;
+	rc = bridge_i2c_write_w(0x53, 0x00);
+	if (rc < 0)
+		goto bridge_fail;
+	msleep(30);
+	rc = bridge_i2c_write_w(0x53, 0x01);
+	if (rc < 0)
+		goto bridge_fail;
+	msleep(30);
+	rc = bridge_i2c_write_w(0x0E, 0xFFFF);
+	if (rc < 0)
+		goto err;
+	rc = bridge_i2c_read(0x54, &RegData, 2);
+	if (rc < 0)
+		goto err;
+	rc = bridge_i2c_write_w(0x54, (RegData | 0x1));
+	if (rc < 0)
+		goto err;
+	msleep(30);
+	rc = bridge_i2c_read(0x54, &RegData, 2);
+	if (rc < 0)
+		goto err;
+	rc = bridge_i2c_write_w(0x54, (RegData | 0x4));
+	if (rc < 0)
+		goto err;
+	rc = bridge_i2c_read(0x55, &GPIOInState, 2);
+	if (rc < 0)
+		goto err;
+	rc = bridge_i2c_write_w(0x55, (GPIOInState | 0x1));
+	if (rc < 0)
+		goto err;
+	msleep(30);
+	rc = bridge_i2c_read(0x55, &GPIOInState, 2);
+	if (rc < 0)
+		goto err;
+	rc = bridge_i2c_write_w(0x55, (GPIOInState | 0x4));
+	if (rc < 0)
+		goto err;
+	msleep(30);
+	rc = bridge_i2c_read(0x55, &GPIOInState, 2);
+	if (rc < 0)
+		goto err;
+	GPIOInState = ((GPIOInState >> 4) & 0x1);
+
+	rc = bridge_i2c_read(0x08, &GPIOInState, 2);
+	if (rc < 0)
+		goto err;
+	rc = bridge_i2c_write_w(0x08, GPIOInState | 0x4000);
+	if (rc < 0)
+		goto err;
+	return rc;
+
+err:
+	bridge_i2c_write_w(0x53, 0x00);
+	msleep(30);
+
+bridge_fail:
+	return rc;
+
+}
+
+static void qs_s5k4e1_bridge_config(int mode, int rt)
+{
+	unsigned short RegData = 0;
+	if (mode == MODE_3D) {
+		bridge_i2c_read(0x54, &RegData, 2);
+		bridge_i2c_write_w(0x54, (RegData | 0x2));
+		bridge_i2c_write_w(0x54, (RegData | 0xa));
+		bridge_i2c_read(0x55, &RegData, 2);
+		bridge_i2c_write_w(0x55, (RegData | 0x2));
+		bridge_i2c_write_w(0x55, (RegData | 0xa));
+		bridge_i2c_write_w(0x14, 0x0C);
+		msleep(20);
+		bridge_i2c_write_w(0x16, 0x00);
+		bridge_i2c_write_w(0x51, 0x3);
+		bridge_i2c_write_w(0x52, 0x1);
+		bridge_i2c_write_w(0x06, 0x03);
+		bridge_i2c_write_w(0x04, 0x2018);
+		bridge_i2c_write_w(0x50, 0x00);
+	} else if (mode == MODE_2D_RIGHT) {
+		bridge_i2c_read(0x54, &RegData, 2);
+		RegData |= 0x2;
+		bridge_i2c_write_w(0x54, RegData);
+		bridge_i2c_write_w(0x54, (RegData & ~(0x8)));
+		bridge_i2c_read(0x55, &RegData, 2);
+		RegData |= 0x2;
+		bridge_i2c_write_w(0x55, RegData);
+		bridge_i2c_write_w(0x55, (RegData & ~(0x8)));
+		bridge_i2c_write_w(0x14, 0x04);
+		msleep(20);
+		bridge_i2c_write_w(0x51, 0x3);
+		bridge_i2c_write_w(0x06, 0x01);
+		bridge_i2c_write_w(0x04, 0x2018);
+		bridge_i2c_write_w(0x50, 0x01);
+	} else if (mode == MODE_2D_LEFT) {
+		bridge_i2c_read(0x54, &RegData, 2);
+		RegData |= 0x8;
+		bridge_i2c_write_w(0x54, RegData);
+		bridge_i2c_write_w(0x54, (RegData & ~(0x2)));
+		bridge_i2c_read(0x55, &RegData, 2);
+		RegData |= 0x8;
+		bridge_i2c_write_w(0x55, RegData);
+		bridge_i2c_write_w(0x55, (RegData & ~(0x2)));
+		bridge_i2c_write_w(0x14, 0x08);
+		msleep(20);
+		bridge_i2c_write_w(0x51, 0x3);
+		bridge_i2c_write_w(0x06, 0x02);
+		bridge_i2c_write_w(0x04, 0x2018);
+		bridge_i2c_write_w(0x50, 0x02);
+	}
+}
+
+static void qs_s5k4e1_group_hold_on(void)
+{
+	qs_s5k4e1_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+						GROUPED_PARAMETER_HOLD);
+}
+
+static void qs_s5k4e1_group_hold_off(void)
+{
+	qs_s5k4e1_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+						GROUPED_PARAMETER_HOLD_OFF);
+}
+
+static void qs_s5k4e1_start_stream(void)
+{
+	qs_s5k4e1_i2c_write_b_sensor(0x0100, 0x01);
+}
+
+static void qs_s5k4e1_stop_stream(void)
+{
+	qs_s5k4e1_i2c_write_b_sensor(0x0100, 0x00);
+}
+
+static void qs_s5k4e1_get_pict_fps(uint16_t fps, uint16_t *pfps)
+{
+	/* input fps is preview fps in Q8 format */
+	uint32_t divider, d1, d2;
+
+	d1 = prev_frame_length_lines * 0x00000400 / snap_frame_length_lines;
+	d2 = prev_line_length_pck * 0x00000400 / snap_line_length_pck;
+	divider = d1 * d2 / 0x400;
+
+	/*Verify PCLK settings and frame sizes.*/
+	*pfps = (uint16_t) (fps * divider / 0x400);
+	/* 2 is the ratio of no.of snapshot channels
+	to number of preview channels */
+}
+
+static uint16_t qs_s5k4e1_get_prev_lines_pf(void)
+{
+
+	return prev_frame_length_lines;
+
+}
+
+static uint16_t qs_s5k4e1_get_prev_pixels_pl(void)
+{
+	return prev_line_length_pck;
+
+}
+
+static uint16_t qs_s5k4e1_get_pict_lines_pf(void)
+{
+	return snap_frame_length_lines;
+}
+
+static uint16_t qs_s5k4e1_get_pict_pixels_pl(void)
+{
+	return snap_line_length_pck;
+}
+
+
+static uint32_t qs_s5k4e1_get_pict_max_exp_lc(void)
+{
+	return snap_frame_length_lines  * 24;
+}
+
+static int32_t qs_s5k4e1_set_fps(struct fps_cfg   *fps)
+{
+	uint16_t total_lines_per_frame;
+	int32_t rc = 0;
+	qs_s5k4e1_ctrl->fps_divider = fps->fps_div;
+	qs_s5k4e1_ctrl->pict_fps_divider = fps->pict_fps_div;
+	if (qs_s5k4e1_ctrl->sensormode == SENSOR_PREVIEW_MODE) {
+		total_lines_per_frame = (uint16_t)
+		((prev_frame_length_lines) * qs_s5k4e1_ctrl->fps_divider/0x400);
+	} else {
+		total_lines_per_frame = (uint16_t)
+		((snap_frame_length_lines) *
+			qs_s5k4e1_ctrl->pict_fps_divider/0x400);
+	}
+	qs_s5k4e1_group_hold_on();
+	rc = qs_s5k4e1_i2c_write_w_sensor(REG_FRAME_LENGTH_LINES,
+							total_lines_per_frame);
+	qs_s5k4e1_group_hold_off();
+	return rc;
+}
+
+static int32_t qs_s5k4e1_write_exp_gain(struct sensor_3d_exp_cfg exp_cfg)
+{
+	uint16_t max_legal_gain = 0x0200;
+	uint32_t ll_pck, fl_lines;
+	uint16_t gain = exp_cfg.gain;
+	uint32_t line = exp_cfg.line;
+	int32_t rc = 0;
+	if (gain > max_legal_gain) {
+		CDBG("Max legal gain Line:%d\n", __LINE__);
+		gain = max_legal_gain;
+	}
+	CDBG("qs_s5k4e1_write_exp_gain : gain = %d line = %d\n", gain, line);
+
+	if (qs_s5k4e1_ctrl->sensormode == SENSOR_PREVIEW_MODE) {
+		qs_s5k4e1_ctrl->my_reg_gain = gain;
+		qs_s5k4e1_ctrl->my_reg_line_count = (uint16_t) line;
+		fl_lines = prev_frame_length_lines *
+			qs_s5k4e1_ctrl->fps_divider / 0x400;
+		ll_pck = prev_line_length_pck;
+	} else {
+		fl_lines = snap_frame_length_lines *
+			qs_s5k4e1_ctrl->pict_fps_divider / 0x400;
+		ll_pck = snap_line_length_pck;
+	}
+	if (line > (fl_lines - QS_S5K4E1_OFFSET))
+		fl_lines = line + QS_S5K4E1_OFFSET;
+	qs_s5k4e1_group_hold_on();
+	rc = qs_s5k4e1_i2c_write_w_sensor(REG_GLOBAL_GAIN, gain);
+	rc = qs_s5k4e1_i2c_write_w_sensor(REG_FRAME_LENGTH_LINES, fl_lines);
+	rc = qs_s5k4e1_i2c_write_w_sensor(REG_COARSE_INTEGRATION_TIME, line);
+	if ((qs_s5k4e1_ctrl->cam_mode == MODE_3D) && (cali_data_status == 1)) {
+		bridge_i2c_write_w(0x06, 0x01);
+		rc = qs_s5k4e1_i2c_write_w_sensor(REG_GLOBAL_GAIN,
+			 exp_cfg.gain_adjust);
+		rc = qs_s5k4e1_i2c_write_w_sensor(REG_GR_GAIN, exp_cfg.gr_gain);
+		rc = qs_s5k4e1_i2c_write_w_sensor(REG_R_GAIN,
+				exp_cfg.r_gain);
+		rc = qs_s5k4e1_i2c_write_w_sensor(REG_B_GAIN,
+				exp_cfg.b_gain);
+		rc = qs_s5k4e1_i2c_write_w_sensor(REG_GB_GAIN,
+				exp_cfg.gb_gain);
+		bridge_i2c_write_w(0x06, 0x03);
+	}
+	qs_s5k4e1_group_hold_off();
+	return rc;
+}
+
+static int32_t qs_s5k4e1_set_pict_exp_gain(struct sensor_3d_exp_cfg exp_cfg)
+{
+	int32_t rc = 0;
+	rc = qs_s5k4e1_write_exp_gain(exp_cfg);
+	return rc;
+}
+
+static int32_t qs_s5k4e1_write_focus_value(uint16_t code_value)
+{
+	uint8_t code_val_msb, code_val_lsb;
+	if ((qs_s5k4e1_ctrl->cam_mode == MODE_2D_LEFT) ||
+		(qs_s5k4e1_ctrl->cam_mode == MODE_3D)) {
+		/* Left */
+		bridge_i2c_write_w(0x06, 0x02);
+		CDBG("%s: Left Lens Position: %d\n", __func__,
+			code_value);
+		code_val_msb = code_value >> 4;
+		code_val_lsb = (code_value & 0x000F) << 4;
+		code_val_lsb |= qs_s5k4e1_af_mode;
+		if (af_i2c_write_b_sensor(code_val_msb, code_val_lsb) < 0) {
+			CDBG("move_focus failed at line %d ...\n", __LINE__);
+			return -EBUSY;
+		}
+	}
+
+	if ((qs_s5k4e1_ctrl->cam_mode == MODE_2D_RIGHT) ||
+		(qs_s5k4e1_ctrl->cam_mode == MODE_3D)) {
+		/* Right */
+		bridge_i2c_write_w(0x06, 0x01);
+		code_value += qs_s5k4e1_af_right_adjust;
+		CDBG("%s: Right Lens Position: %d\n", __func__,
+			code_value);
+		code_val_msb = code_value >> 4;
+		code_val_lsb = (code_value & 0x000F) << 4;
+		code_val_lsb |= qs_s5k4e1_af_mode;
+		if (af_i2c_write_b_sensor(code_val_msb, code_val_lsb) < 0) {
+			CDBG("move_focus failed at line %d ...\n", __LINE__);
+			return -EBUSY;
+		}
+	}
+
+	if (qs_s5k4e1_ctrl->cam_mode == MODE_3D) {
+		/* 3D Mode */
+		bridge_i2c_write_w(0x06, 0x03);
+	}
+	usleep(qs_s5k4e1_sw_damping_time_wait*50);
+	return 0;
+}
+
+static int32_t qs_s5k4e1_write_1D_focus_value(uint16_t code_value)
+{
+	uint8_t code_val_msb, code_val_lsb;
+	CDBG("%s: Lens Position: %d\n", __func__, code_value);
+	code_val_msb = code_value >> 4;
+	code_val_lsb = (code_value & 0x000F) << 4;
+	code_val_lsb |= qs_s5k4e1_af_mode;
+	if (af_i2c_write_b_sensor(code_val_msb, code_val_lsb) < 0) {
+		CDBG("move_focus failed at line %d ...\n", __LINE__);
+		return -EBUSY;
+	}
+
+	usleep(qs_s5k4e1_sw_damping_time_wait*50);
+	return 0;
+}
+
+static int32_t qs_s5k4e1_move_focus(int direction,
+	int32_t num_steps)
+{
+	int16_t step_direction, actual_step, dest_lens_position,
+		dest_step_position;
+	int16_t max_step_postion = QS_S5K4E1_TOTAL_STEPS_NEAR_TO_FAR;
+	CDBG("Inside %s\n", __func__);
+	if (direction == MOVE_NEAR)
+		step_direction = 1;
+	else
+		step_direction = -1;
+
+	actual_step = (int16_t) (step_direction * (int16_t) num_steps);
+	dest_step_position = (int16_t) (qs_s5k4e1_ctrl->curr_step_pos +
+		actual_step);
+
+	if (qs_s5k4e1_ctrl->cam_mode == MODE_3D)
+		max_step_postion = QS_S5K4E1_TOTAL_STEPS_3D;
+
+	if (dest_step_position > max_step_postion)
+		dest_step_position = max_step_postion;
+	else if (dest_step_position < 0)
+		dest_step_position = 0;
+
+	if (dest_step_position == qs_s5k4e1_ctrl->curr_step_pos) {
+		CDBG("%s cur and dest pos are same\n", __func__);
+		CDBG("%s cur_step_pos:%d\n", __func__,
+			qs_s5k4e1_ctrl->curr_step_pos);
+		return 0;
+	}
+
+	if (step_direction < 0) {
+		if (num_steps >= 20) {
+			/* sweeping towards all the way in infinity direction */
+			qs_s5k4e1_af_mode = 2;
+			qs_s5k4e1_sw_damping_time_wait = 8;
+		} else if (num_steps <= 4) {
+			/* reverse search during macro mode */
+			qs_s5k4e1_af_mode = 4;
+			qs_s5k4e1_sw_damping_time_wait = 16;
+		} else {
+			qs_s5k4e1_af_mode = 3;
+			qs_s5k4e1_sw_damping_time_wait = 12;
+		}
+	} else {
+		/* coarse search towards macro direction */
+		qs_s5k4e1_af_mode = 4;
+		qs_s5k4e1_sw_damping_time_wait = 16;
+	}
+
+	if (qs_s5k4e1_ctrl->cam_mode == MODE_3D) {
+		/* Left */
+		bridge_i2c_write_w(0x06, 0x02);
+		dest_lens_position =
+			qs_s5k4e1_step_position_table_left[dest_step_position];
+		if (qs_s5k4e1_write_1D_focus_value(dest_lens_position) < 0) {
+			CDBG("move_focus failed at line %d ...\n", __LINE__);
+			bridge_i2c_write_w(0x06, 0x03);
+			return -EBUSY;
+		}
+		/* Keep left sensor as reference as AF stats is from left */
+		qs_s5k4e1_ctrl->curr_step_pos = dest_step_position;
+		qs_s5k4e1_ctrl->curr_lens_pos = dest_lens_position;
+
+		/* Right */
+		bridge_i2c_write_w(0x06, 0x01);
+		dest_lens_position =
+			qs_s5k4e1_step_position_table_right[dest_step_position];
+		if (qs_s5k4e1_write_1D_focus_value(dest_lens_position) < 0) {
+			CDBG("move_focus failed at line %d ...\n", __LINE__);
+			bridge_i2c_write_w(0x06, 0x03);
+			return -EBUSY;
+		}
+
+		/* 3D Mode */
+		bridge_i2c_write_w(0x06, 0x03);
+		return 0;
+	}
+
+	dest_lens_position = qs_s5k4e1_step_position_table[dest_step_position];
+	CDBG("%s: Step Position: %d\n", __func__, dest_step_position);
+	if (qs_s5k4e1_ctrl->curr_lens_pos != dest_lens_position) {
+		if (qs_s5k4e1_write_focus_value(dest_lens_position) < 0) {
+			CDBG("move_focus failed at line %d ...\n", __LINE__);
+			return -EBUSY;
+		}
+	}
+
+	qs_s5k4e1_ctrl->curr_step_pos = dest_step_position;
+	qs_s5k4e1_ctrl->curr_lens_pos = dest_lens_position;
+	return 0;
+}
+
+static int32_t qs_s5k4e1_set_default_focus(uint8_t af_step)
+{
+	int32_t rc = 0;
+	if (qs_s5k4e1_ctrl->curr_step_pos) {
+		rc = qs_s5k4e1_move_focus(MOVE_FAR,
+			qs_s5k4e1_ctrl->curr_step_pos);
+		if (rc < 0)
+			return rc;
+	} else {
+		if (qs_s5k4e1_ctrl->cam_mode == MODE_3D) {
+			/* Left */
+			bridge_i2c_write_w(0x06, 0x02);
+			rc = qs_s5k4e1_write_1D_focus_value(
+				qs_s5k4e1_step_position_table_left[0]);
+			if (rc < 0) {
+				bridge_i2c_write_w(0x06, 0x03);
+				return rc;
+			}
+
+			/* Right */
+			bridge_i2c_write_w(0x06, 0x01);
+			rc = qs_s5k4e1_write_1D_focus_value(
+				qs_s5k4e1_step_position_table_right[0]);
+			if (rc < 0) {
+				bridge_i2c_write_w(0x06, 0x03);
+				return rc;
+			}
+
+			/* Left sensor is the reference sensor for AF stats */
+			qs_s5k4e1_ctrl->curr_lens_pos =
+				qs_s5k4e1_step_position_table_left[0];
+
+			/* 3D Mode */
+			bridge_i2c_write_w(0x06, 0x03);
+		} else {
+			rc = qs_s5k4e1_write_focus_value(
+				qs_s5k4e1_step_position_table[0]);
+			if (rc < 0)
+				return rc;
+			qs_s5k4e1_ctrl->curr_lens_pos =
+				qs_s5k4e1_step_position_table[0];
+		}
+	}
+	CDBG("%s\n", __func__);
+	return 0;
+}
+
+static void qs_s5k4e1_3d_table_init(void)
+{
+	int16_t af_data = 0;
+	uint16_t step = 8, step_q2 = 8, anchor_point_q2;
+	int32_t rc = 0, i, j;
+	uint16_t eeprom_read_addr[2][3] = {{0x110, 0x114, 0x118},
+		{0x112, 0x116, 0x11A} };
+	uint16_t *step_position_table;
+
+	step_position_table = qs_s5k4e1_step_position_table_left;
+	for (j = 0; j < 2; j++) {
+		rc = qs_s5k4e1_eeprom_i2c_read_b(eeprom_read_addr[j][0],
+			&af_data, 2);
+		if (rc == 0) {
+			CDBG("%s: Far data - %d\n", __func__, af_data);
+			step_position_table[0] = af_data;
+		} else {
+			CDBG("%s: EEPROM data read error\n", __func__);
+			return;
+		}
+
+		rc = qs_s5k4e1_eeprom_i2c_read_b(eeprom_read_addr[j][1],
+			&af_data, 2);
+		if (rc == 0) {
+			CDBG("%s: Medium data - %d\n", __func__, af_data);
+			step_position_table[2] = af_data;
+		} else {
+			CDBG("%s: EEPROM data read error\n", __func__);
+			return;
+		}
+
+		/*
+		 * Using the 150cm and 100cm calibration values
+		 * as per the Lens characteristics derive intermediate step
+		 */
+		step_position_table[1] = step_position_table[0] +
+			(step_position_table[2] - step_position_table[0])/2;
+		CDBG("%s: Step between 150cm:100cm is %d\n", __func__,
+			step_position_table[1]);
+
+		rc = qs_s5k4e1_eeprom_i2c_read_b(eeprom_read_addr[j][2],
+			&af_data, 2);
+		if (rc == 0) {
+			CDBG("%s: Short data - %d\n", __func__, af_data);
+			step_position_table[6] = af_data;
+		} else {
+			CDBG("%s: EEPROM data read error\n", __func__);
+			return;
+		}
+
+		/*
+		 * Using the 100cm and 50cm calibration values
+		 * as per the Lens characteristics derive
+		 * intermediate steps
+		 */
+		step = (step_position_table[6] - step_position_table[2])/4;
+
+		/*
+		 * Interpolate the intermediate steps between 100cm
+		 * to 50cm based on COC1.5
+		 */
+		step_position_table[3] = step_position_table[2] + step;
+		step_position_table[4] = step_position_table[3] + step;
+		step_position_table[5] = step_position_table[4] + step;
+
+		/*
+		 * Extrapolate the steps within 50cm based on
+		 * OC2 to converge faster. This range is beyond the 3D
+		 * specification of 50cm
+		 */
+		anchor_point_q2 = step_position_table[6] << 1;
+		step_q2 = (step_position_table[6] - step_position_table[2]);
+
+		for (i = 7; i < QS_S5K4E1_TOTAL_STEPS_3D; i++) {
+			anchor_point_q2 += step_q2;
+			step_position_table[i] = anchor_point_q2 >> 1;
+		}
+		step_position_table = qs_s5k4e1_step_position_table_right;
+	}
+}
+
+static void qs_s5k4e1_init_focus(void)
+{
+	uint8_t i;
+	int32_t rc = 0;
+	int16_t af_far_data = 0;
+	qs_s5k4e1_af_initial_code = 190;
+	/* Read the calibration data from left and right sensors if available */
+	rc = qs_s5k4e1_eeprom_i2c_read_b(0x110, &af_far_data, 2);
+	if (rc == 0) {
+		CDBG("%s: Left Far data - %d\n", __func__, af_far_data);
+		qs_s5k4e1_af_initial_code = af_far_data;
+	}
+
+	rc = qs_s5k4e1_eeprom_i2c_read_b(0x112, &af_far_data, 2);
+	if (rc == 0) {
+		CDBG("%s: Right Far data - %d\n", __func__, af_far_data);
+		qs_s5k4e1_af_right_adjust = af_far_data -
+			qs_s5k4e1_af_initial_code;
+	}
+
+	qs_s5k4e1_3d_table_init();
+
+	qs_s5k4e1_step_position_table[0] = qs_s5k4e1_af_initial_code;
+	for (i = 1; i <= QS_S5K4E1_TOTAL_STEPS_NEAR_TO_FAR; i++) {
+		if (i <= qs_s5k4e1_nl_region_boundary1) {
+			qs_s5k4e1_step_position_table[i] =
+				qs_s5k4e1_step_position_table[i-1]
+				+ qs_s5k4e1_nl_region_code_per_step1;
+		} else {
+			qs_s5k4e1_step_position_table[i] =
+				qs_s5k4e1_step_position_table[i-1]
+				+ qs_s5k4e1_l_region_code_per_step;
+		}
+
+		if (qs_s5k4e1_step_position_table[i] > 1023)
+			qs_s5k4e1_step_position_table[i] = 1023;
+	}
+	qs_s5k4e1_ctrl->curr_step_pos = 0;
+}
+
+static int32_t qs_s5k4e1_test(enum qs_s5k4e1_test_mode_t mo)
+{
+	int32_t rc = 0;
+	if (mo == TEST_OFF)
+		return rc;
+	else {
+		/* REG_0x30D8[4] is TESBYPEN: 0: Normal Operation,
+		1: Bypass Signal Processing
+		REG_0x30D8[5] is EBDMASK: 0:
+		Output Embedded data, 1: No output embedded data */
+		if (qs_s5k4e1_i2c_write_b_sensor(REG_TEST_PATTERN_MODE,
+			(uint8_t) mo) < 0) {
+			return rc;
+		}
+	}
+	return rc;
+}
+
+static int32_t qs_s5k4e1_sensor_setting(int update_type, int rt)
+{
+
+	int32_t rc = 0;
+	struct msm_camera_csi_params qs_s5k4e1_csi_params;
+
+	qs_s5k4e1_stop_stream();
+	msleep(80);
+	bridge_i2c_write_w(0x53, 0x00);
+	msleep(80);
+	if (update_type == REG_INIT) {
+		CSI_CONFIG = 0;
+		LENS_SHADE_CONFIG = 0;
+		default_lens_shade = 0;
+		bridge_i2c_write_w(0x53, 0x01);
+		msleep(30);
+		qs_s5k4e1_bridge_config(qs_s5k4e1_ctrl->cam_mode, rt);
+		msleep(30);
+		qs_s5k4e1_i2c_write_b_table(qs_s5k4e1_regs.rec_settings,
+				qs_s5k4e1_regs.rec_size);
+		msleep(30);
+	} else if (update_type == UPDATE_PERIODIC) {
+		qs_s5k4e1_write_lsc(lens_eeprom_data, rt);
+		msleep(100);
+		if (!CSI_CONFIG) {
+			if (qs_s5k4e1_ctrl->cam_mode == MODE_3D) {
+				qs_s5k4e1_csi_params.lane_cnt = 4;
+				qs_s5k4e1_csi_params.data_format = CSI_8BIT;
+			} else {
+				qs_s5k4e1_csi_params.lane_cnt = 1;
+				qs_s5k4e1_csi_params.data_format = CSI_10BIT;
+			}
+			qs_s5k4e1_csi_params.lane_assign = 0xe4;
+			qs_s5k4e1_csi_params.dpcm_scheme = 0;
+			qs_s5k4e1_csi_params.settle_cnt = 28;
+			rc = msm_camio_csi_config(&qs_s5k4e1_csi_params);
+			msleep(10);
+			cam_debug_init();
+			CSI_CONFIG = 1;
+		}
+		bridge_i2c_write_w(0x53, 0x01);
+		msleep(50);
+		qs_s5k4e1_i2c_write_b_table(qs_s5k4e1_regs.conf_array[rt].conf,
+			qs_s5k4e1_regs.conf_array[rt].size);
+		msleep(50);
+		qs_s5k4e1_start_stream();
+		msleep(80);
+	}
+	return rc;
+}
+
+static int32_t qs_s5k4e1_video_config(int mode)
+{
+
+	int32_t rc = 0;
+	/* change sensor resolution if needed */
+	if (qs_s5k4e1_sensor_setting(UPDATE_PERIODIC,
+			qs_s5k4e1_ctrl->prev_res) < 0)
+		return rc;
+	if (qs_s5k4e1_ctrl->set_test) {
+		if (qs_s5k4e1_test(qs_s5k4e1_ctrl->set_test) < 0)
+			return  rc;
+	}
+
+	qs_s5k4e1_ctrl->curr_res = qs_s5k4e1_ctrl->prev_res;
+	qs_s5k4e1_ctrl->sensormode = mode;
+	return rc;
+}
+
+static int32_t qs_s5k4e1_snapshot_config(int mode)
+{
+	int32_t rc = 0;
+	/*change sensor resolution if needed */
+	if (qs_s5k4e1_ctrl->curr_res != qs_s5k4e1_ctrl->pict_res) {
+		if (qs_s5k4e1_sensor_setting(UPDATE_PERIODIC,
+				qs_s5k4e1_ctrl->pict_res) < 0)
+			return rc;
+	}
+
+	qs_s5k4e1_ctrl->curr_res = qs_s5k4e1_ctrl->pict_res;
+	qs_s5k4e1_ctrl->sensormode = mode;
+	return rc;
+} /*end of qs_s5k4e1_snapshot_config*/
+
+static int32_t qs_s5k4e1_raw_snapshot_config(int mode)
+{
+	int32_t rc = 0;
+	/* change sensor resolution if needed */
+	if (qs_s5k4e1_ctrl->curr_res != qs_s5k4e1_ctrl->pict_res) {
+		if (qs_s5k4e1_sensor_setting(UPDATE_PERIODIC,
+				qs_s5k4e1_ctrl->pict_res) < 0)
+			return rc;
+	}
+
+	qs_s5k4e1_ctrl->curr_res = qs_s5k4e1_ctrl->pict_res;
+	qs_s5k4e1_ctrl->sensormode = mode;
+	return rc;
+} /*end of qs_s5k4e1_raw_snapshot_config*/
+
+static int32_t qs_s5k4e1_mode_init(int mode, struct sensor_init_cfg init_info)
+{
+	int32_t rc = 0;
+	if (mode != qs_s5k4e1_ctrl->cam_mode) {
+		qs_s5k4e1_ctrl->prev_res = init_info.prev_res;
+		qs_s5k4e1_ctrl->pict_res = init_info.pict_res;
+		qs_s5k4e1_ctrl->cam_mode = mode;
+
+		prev_frame_length_lines =
+		((qs_s5k4e1_regs.conf_array[qs_s5k4e1_ctrl->prev_res]\
+			.conf[QS_S5K4E1_FRAME_LENGTH_LINES_H].wdata << 8)
+			| qs_s5k4e1_regs.conf_array[qs_s5k4e1_ctrl->prev_res]\
+			.conf[QS_S5K4E1_FRAME_LENGTH_LINES_L].wdata);
+		prev_line_length_pck =
+		(qs_s5k4e1_regs.conf_array[qs_s5k4e1_ctrl->prev_res]\
+			.conf[QS_S5K4E1_LINE_LENGTH_PCK_H].wdata << 8)
+			| qs_s5k4e1_regs.conf_array[qs_s5k4e1_ctrl->prev_res]\
+			.conf[QS_S5K4E1_LINE_LENGTH_PCK_L].wdata;
+		snap_frame_length_lines =
+		(qs_s5k4e1_regs.conf_array[qs_s5k4e1_ctrl->pict_res]\
+			.conf[QS_S5K4E1_FRAME_LENGTH_LINES_H].wdata << 8)
+			| qs_s5k4e1_regs.conf_array[qs_s5k4e1_ctrl->pict_res]\
+			.conf[QS_S5K4E1_FRAME_LENGTH_LINES_L].wdata;
+		snap_line_length_pck =
+		(qs_s5k4e1_regs.conf_array[qs_s5k4e1_ctrl->pict_res]\
+			.conf[QS_S5K4E1_LINE_LENGTH_PCK_H].wdata << 8)
+			| qs_s5k4e1_regs.conf_array[qs_s5k4e1_ctrl->pict_res]\
+			.conf[QS_S5K4E1_LINE_LENGTH_PCK_L].wdata;
+
+	rc = qs_s5k4e1_sensor_setting(REG_INIT,
+		qs_s5k4e1_ctrl->prev_res);
+	}
+	return rc;
+}
+static int32_t qs_s5k4e1_set_sensor_mode(int mode,
+	int res)
+{
+	int32_t rc = 0;
+	switch (mode) {
+	case SENSOR_PREVIEW_MODE:
+		qs_s5k4e1_ctrl->prev_res = res;
+		rc = qs_s5k4e1_video_config(mode);
+		break;
+	case SENSOR_SNAPSHOT_MODE:
+		qs_s5k4e1_ctrl->pict_res = res;
+		rc = qs_s5k4e1_snapshot_config(mode);
+		break;
+	case SENSOR_RAW_SNAPSHOT_MODE:
+		qs_s5k4e1_ctrl->pict_res = res;
+		rc = qs_s5k4e1_raw_snapshot_config(mode);
+		break;
+	default:
+		rc = -EINVAL;
+		break;
+	}
+	return rc;
+}
+
+static int32_t qs_s5k4e1_power_down(void)
+{
+	qs_s5k4e1_stop_stream();
+	msleep(30);
+	qs_s5k4e1_af_mode = 2;
+	qs_s5k4e1_af_right_adjust = 0;
+	qs_s5k4e1_write_focus_value(0);
+	msleep(100);
+	/* Set AF actutator to PowerDown */
+	af_i2c_write_b_sensor(0x80, 00);
+	return 0;
+}
+
+static int qs_s5k4e1_probe_init_done(const struct msm_camera_sensor_info *data)
+{
+	CDBG("probe done\n");
+	gpio_free(data->sensor_reset);
+	return 0;
+}
+
+static int
+	qs_s5k4e1_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+	int32_t rc = 0;
+	uint16_t chipid = 0;
+	CDBG("%s: %d\n", __func__, __LINE__);
+	rc = gpio_request(data->sensor_reset, "qs_s5k4e1");
+	CDBG(" qs_s5k4e1_probe_init_sensor\n");
+	if (!rc) {
+		CDBG("sensor_reset = %d\n", rc);
+		gpio_direction_output(data->sensor_reset, 0);
+		msleep(50);
+		gpio_set_value_cansleep(data->sensor_reset, 1);
+		msleep(13);
+	} else {
+		goto init_probe_done;
+	}
+	msleep(70);
+	rc = qs_s5k4e1_bridge_reset();
+	if (rc < 0)
+		goto init_probe_fail;
+	qs_s5k4e1_bridge_config(MODE_3D, RES_PREVIEW);
+	msleep(30);
+
+	CDBG(" qs_s5k4e1_probe_init_sensor is called\n");
+	rc = qs_s5k4e1_i2c_read(0x0000, &chipid, 2);
+	CDBG("ID: %d\n", chipid);
+	/* 4. Compare sensor ID to QS_S5K4E1 ID: */
+	if (chipid != 0x4e10) {
+		rc = -ENODEV;
+		CDBG("qs_s5k4e1_probe_init_sensor fail chip id mismatch\n");
+		goto init_probe_fail;
+	}
+	goto init_probe_done;
+init_probe_fail:
+	CDBG(" qs_s5k4e1_probe_init_sensor fails\n");
+	gpio_set_value_cansleep(data->sensor_reset, 0);
+	qs_s5k4e1_probe_init_done(data);
+init_probe_done:
+	CDBG(" qs_s5k4e1_probe_init_sensor finishes\n");
+	return rc;
+}
+/* camsensor_qs_s5k4e1_reset */
+
+int qs_s5k4e1_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+	int32_t rc = 0;
+	CDBG("%s: %d\n", __func__, __LINE__);
+	CDBG("Calling qs_s5k4e1_sensor_open_init\n");
+
+	qs_s5k4e1_ctrl = kzalloc(sizeof(struct qs_s5k4e1_ctrl_t), GFP_KERNEL);
+	if (!qs_s5k4e1_ctrl) {
+		CDBG("qs_s5k4e1_init failed!\n");
+		rc = -ENOMEM;
+		goto init_done;
+	}
+	qs_s5k4e1_ctrl->fps_divider = 1 * 0x00000400;
+	qs_s5k4e1_ctrl->pict_fps_divider = 1 * 0x00000400;
+	qs_s5k4e1_ctrl->set_test = TEST_OFF;
+	qs_s5k4e1_ctrl->cam_mode = MODE_INVALID;
+
+	if (data)
+		qs_s5k4e1_ctrl->sensordata = data;
+	if (rc < 0) {
+		CDBG("Calling qs_s5k4e1_sensor_open_init fail1\n");
+		return rc;
+	}
+	CDBG("%s: %d\n", __func__, __LINE__);
+	/* enable mclk first */
+	msm_camio_clk_rate_set(QS_S5K4E1_MASTER_CLK_RATE);
+	rc = qs_s5k4e1_probe_init_sensor(data);
+	if (rc < 0)
+		goto init_fail;
+/*Default mode is 3D*/
+	memcpy(lens_eeprom_data, data->eeprom_data, 864);
+	qs_s5k4e1_ctrl->fps = 30*Q8;
+	qs_s5k4e1_init_focus();
+	if (rc < 0) {
+		gpio_set_value_cansleep(data->sensor_reset, 0);
+		goto init_fail;
+	} else
+		goto init_done;
+init_fail:
+	CDBG("init_fail\n");
+	qs_s5k4e1_probe_init_done(data);
+init_done:
+	CDBG("init_done\n");
+	return rc;
+} /*endof qs_s5k4e1_sensor_open_init*/
+
+static int qs_s5k4e1_init_client(struct i2c_client *client)
+{
+	/* Initialize the MSM_CAMI2C Chip */
+	init_waitqueue_head(&qs_s5k4e1_wait_queue);
+	return 0;
+}
+
+static const struct i2c_device_id qs_s5k4e1_i2c_id[] = {
+	{"qs_s5k4e1", 0},
+	{ }
+};
+
+static int qs_s5k4e1_i2c_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	int rc = 0;
+	CDBG("qs_s5k4e1_probe called!\n");
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		CDBG("i2c_check_functionality failed\n");
+		goto probe_failure;
+	}
+
+	qs_s5k4e1_sensorw = kzalloc(sizeof(struct qs_s5k4e1_work_t),
+		 GFP_KERNEL);
+	if (!qs_s5k4e1_sensorw) {
+		CDBG("kzalloc failed.\n");
+		rc = -ENOMEM;
+		goto probe_failure;
+	}
+
+	i2c_set_clientdata(client, qs_s5k4e1_sensorw);
+	qs_s5k4e1_init_client(client);
+	qs_s5k4e1_client = client;
+
+	msleep(50);
+
+	CDBG("qs_s5k4e1_probe successed! rc = %d\n", rc);
+	return 0;
+
+probe_failure:
+	CDBG("qs_s5k4e1_probe failed! rc = %d\n", rc);
+	return rc;
+}
+
+static int qs_s5k4e1_send_wb_info(struct wb_info_cfg *wb)
+{
+	return 0;
+
+} /*end of qs_s5k4e1_snapshot_config*/
+
+static int __exit qs_s5k4e1_remove(struct i2c_client *client)
+{
+	struct qs_s5k4e1_work_t_t *sensorw = i2c_get_clientdata(client);
+	free_irq(client->irq, sensorw);
+	qs_s5k4e1_client = NULL;
+	kfree(sensorw);
+	return 0;
+}
+
+static struct i2c_driver qs_s5k4e1_i2c_driver = {
+	.id_table = qs_s5k4e1_i2c_id,
+	.probe  = qs_s5k4e1_i2c_probe,
+	.remove = __exit_p(qs_s5k4e1_i2c_remove),
+	.driver = {
+		.name = "qs_s5k4e1",
+	},
+};
+
+int qs_s5k4e1_3D_sensor_config(void __user *argp)
+{
+	struct sensor_large_data cdata;
+	long rc;
+	if (copy_from_user(&cdata,
+		(void *)argp,
+		sizeof(struct sensor_large_data)))
+		return -EFAULT;
+	mutex_lock(&qs_s5k4e1_mut);
+	rc = qs_s5k4e1_get_calibration_data
+		(&cdata.data.sensor_3d_cali_data);
+	if (rc < 0)
+		goto fail;
+	if (copy_to_user((void *)argp,
+		&cdata,
+		sizeof(struct sensor_large_data)))
+		rc = -EFAULT;
+fail:
+	mutex_unlock(&qs_s5k4e1_mut);
+	return rc;
+}
+
+int qs_s5k4e1_2D_sensor_config(void __user *argp)
+{
+	struct sensor_cfg_data cdata;
+	long   rc = 0;
+	if (copy_from_user(&cdata,
+		(void *)argp,
+		sizeof(struct sensor_cfg_data)))
+		return -EFAULT;
+	mutex_lock(&qs_s5k4e1_mut);
+	CDBG("qs_s5k4e1_sensor_config: cfgtype = %d\n",
+	cdata.cfgtype);
+		switch (cdata.cfgtype) {
+		case CFG_GET_PICT_FPS:
+			qs_s5k4e1_get_pict_fps(
+				cdata.cfg.gfps.prevfps,
+				&(cdata.cfg.gfps.pictfps));
+
+			if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+				rc = -EFAULT;
+			break;
+
+		case CFG_GET_PREV_L_PF:
+			cdata.cfg.prevl_pf =
+			qs_s5k4e1_get_prev_lines_pf();
+
+			if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+				rc = -EFAULT;
+			break;
+
+		case CFG_GET_PREV_P_PL:
+			cdata.cfg.prevp_pl =
+				qs_s5k4e1_get_prev_pixels_pl();
+
+			if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+				rc = -EFAULT;
+			break;
+
+		case CFG_GET_PICT_L_PF:
+			cdata.cfg.pictl_pf =
+				qs_s5k4e1_get_pict_lines_pf();
+
+			if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+				rc = -EFAULT;
+			break;
+
+		case CFG_GET_PICT_P_PL:
+			cdata.cfg.pictp_pl =
+				qs_s5k4e1_get_pict_pixels_pl();
+
+			if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+				rc = -EFAULT;
+			break;
+
+		case CFG_GET_PICT_MAX_EXP_LC:
+			cdata.cfg.pict_max_exp_lc =
+				qs_s5k4e1_get_pict_max_exp_lc();
+
+			if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+				rc = -EFAULT;
+			break;
+
+		case CFG_SET_FPS:
+		case CFG_SET_PICT_FPS:
+			rc = qs_s5k4e1_set_fps(&(cdata.cfg.fps));
+			break;
+
+		case CFG_SET_EXP_GAIN:
+			rc =
+				qs_s5k4e1_write_exp_gain(
+					cdata.cfg.sensor_3d_exp);
+			break;
+
+		case CFG_SET_PICT_EXP_GAIN:
+			rc =
+				qs_s5k4e1_set_pict_exp_gain(
+				cdata.cfg.sensor_3d_exp);
+			break;
+
+		case CFG_SET_MODE:
+			rc = qs_s5k4e1_set_sensor_mode(cdata.mode,
+					cdata.rs);
+			break;
+
+		case CFG_PWR_DOWN:
+			rc = qs_s5k4e1_power_down();
+			break;
+
+		case CFG_MOVE_FOCUS:
+			rc =
+				qs_s5k4e1_move_focus(
+				cdata.cfg.focus.dir,
+				cdata.cfg.focus.steps);
+			break;
+
+		case CFG_SET_DEFAULT_FOCUS:
+			rc =
+				qs_s5k4e1_set_default_focus(
+				cdata.cfg.focus.steps);
+			break;
+
+		case CFG_GET_AF_MAX_STEPS:
+			if (qs_s5k4e1_ctrl->cam_mode == MODE_3D)
+				cdata.max_steps = QS_S5K4E1_TOTAL_STEPS_3D;
+			else
+				cdata.max_steps =
+					QS_S5K4E1_TOTAL_STEPS_NEAR_TO_FAR;
+
+			if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+				rc = -EFAULT;
+			break;
+
+		case CFG_SET_EFFECT:
+			rc = qs_s5k4e1_set_default_focus(
+				cdata.cfg.effect);
+			break;
+
+
+		case CFG_SEND_WB_INFO:
+			rc = qs_s5k4e1_send_wb_info(
+				&(cdata.cfg.wb_info));
+			break;
+
+		case CFG_SENSOR_INIT:
+			rc = qs_s5k4e1_mode_init(cdata.mode,
+					cdata.cfg.init_info);
+			break;
+
+		default:
+			rc = -EFAULT;
+			break;
+		}
+
+	mutex_unlock(&qs_s5k4e1_mut);
+
+	return rc;
+}
+
+int qs_s5k4e1_sensor_config(void __user *argp)
+{
+	int cfgtype;
+	long rc;
+	if (copy_from_user(&cfgtype,
+		(void *)argp,
+		sizeof(int)))
+		return -EFAULT;
+	if (cfgtype != CFG_GET_3D_CALI_DATA)
+		rc = qs_s5k4e1_2D_sensor_config(argp);
+	else
+		rc = qs_s5k4e1_3D_sensor_config(argp);
+	return rc;
+}
+
+static int qs_s5k4e1_sensor_release(void)
+{
+	int rc = -EBADF;
+	mutex_lock(&qs_s5k4e1_mut);
+	qs_s5k4e1_power_down();
+	bridge_i2c_write_w(0x53, 0x00);
+	msleep(20);
+	gpio_set_value_cansleep(qs_s5k4e1_ctrl->sensordata->sensor_reset, 0);
+	msleep(5);
+	gpio_free(qs_s5k4e1_ctrl->sensordata->sensor_reset);
+	kfree(qs_s5k4e1_ctrl);
+	qs_s5k4e1_ctrl = NULL;
+	CDBG("qs_s5k4e1_release completed\n");
+	mutex_unlock(&qs_s5k4e1_mut);
+
+	return rc;
+}
+
+static int qs_s5k4e1_sensor_probe(const struct msm_camera_sensor_info *info,
+		struct msm_sensor_ctrl *s)
+{
+	int rc = 0;
+	rc = i2c_add_driver(&qs_s5k4e1_i2c_driver);
+	if (rc < 0 || qs_s5k4e1_client == NULL) {
+		rc = -ENOTSUPP;
+		CDBG("I2C add driver failed");
+		goto probe_fail;
+	}
+	msm_camio_clk_rate_set(QS_S5K4E1_MASTER_CLK_RATE);
+	rc = qs_s5k4e1_probe_init_sensor(info);
+	if (rc < 0)
+		goto probe_fail;
+	qs_s5k4e1_read_lsc(info->eeprom_data); /*Default mode is 3D*/
+	s->s_init = qs_s5k4e1_sensor_open_init;
+	s->s_release = qs_s5k4e1_sensor_release;
+	s->s_config  = qs_s5k4e1_sensor_config;
+	s->s_mount_angle = info->sensor_platform_info->mount_angle;
+	s->s_camera_type = BACK_CAMERA_3D;
+	s->s_video_packing = SIDE_BY_SIDE_HALF;
+	s->s_snap_packing = SIDE_BY_SIDE_FULL;
+	bridge_i2c_write_w(0x53, 0x00);
+	msleep(20);
+	gpio_set_value_cansleep(info->sensor_reset, 0);
+	qs_s5k4e1_probe_init_done(info);
+	return rc;
+
+probe_fail:
+	CDBG("qs_s5k4e1_sensor_probe: SENSOR PROBE FAILS!\n");
+	return rc;
+}
+
+static bool streaming = 1;
+
+static int qs_s5k4e1_focus_test(void *data, u64 *val)
+{
+	int i = 0;
+	qs_s5k4e1_set_default_focus(0);
+
+	for (i = 0; i < QS_S5K4E1_TOTAL_STEPS_NEAR_TO_FAR; i++) {
+		qs_s5k4e1_move_focus(MOVE_NEAR, 1);
+		msleep(2000);
+	}
+	msleep(5000);
+	for ( ; i > 0; i--) {
+		qs_s5k4e1_move_focus(MOVE_FAR, 1);
+		msleep(2000);
+	}
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(cam_focus, qs_s5k4e1_focus_test,
+			NULL, "%lld\n");
+
+static int qs_s5k4e1_step_test(void *data, u64 *val)
+{
+	int rc = 0;
+	struct sensor_large_data cdata;
+	rc = qs_s5k4e1_get_calibration_data
+		(&cdata.data.sensor_3d_cali_data);
+	if (rc < 0)
+		CDBG("%s: Calibration data read fail.\n", __func__);
+
+	return 0;
+}
+
+static int qs_s5k4e1_set_step(void *data, u64 val)
+{
+	qs_s5k4e1_l_region_code_per_step = val & 0xFF;
+	qs_s5k4e1_af_mode = (val >> 8) & 0xFF;
+	qs_s5k4e1_nl_region_code_per_step1 = (val >> 16) & 0xFFFF;
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(cam_step, qs_s5k4e1_step_test,
+			qs_s5k4e1_set_step, "%lld\n");
+
+static int cam_debug_stream_set(void *data, u64 val)
+{
+	int rc = 0;
+
+	if (val) {
+		qs_s5k4e1_start_stream();
+		streaming = 1;
+	} else {
+		qs_s5k4e1_stop_stream();
+		streaming = 0;
+	}
+
+	return rc;
+}
+
+static int cam_debug_stream_get(void *data, u64 *val)
+{
+	*val = streaming;
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(cam_stream, cam_debug_stream_get,
+			cam_debug_stream_set, "%llu\n");
+
+static uint16_t qs_s5k4e1_step_val = QS_S5K4E1_TOTAL_STEPS_NEAR_TO_FAR;
+static uint8_t qs_s5k4e1_step_dir = MOVE_NEAR;
+static int qs_s5k4e1_af_step_config(void *data, u64 val)
+{
+	qs_s5k4e1_step_val = val & 0xFFFF;
+	qs_s5k4e1_step_dir = (val >> 16) & 0x1;
+	CDBG("%s\n", __func__);
+	return 0;
+}
+
+static int qs_s5k4e1_af_step(void *data, u64 *val)
+{
+	int i = 0;
+	int dir = MOVE_NEAR;
+	CDBG("%s\n", __func__);
+	qs_s5k4e1_set_default_focus(0);
+	msleep(5000);
+	if (qs_s5k4e1_step_dir == 1)
+		dir = MOVE_FAR;
+
+	for (i = 0; i < qs_s5k4e1_step_val; i += 4) {
+		qs_s5k4e1_move_focus(dir, 4);
+		msleep(1000);
+	}
+	qs_s5k4e1_set_default_focus(0);
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(af_step, qs_s5k4e1_af_step,
+			qs_s5k4e1_af_step_config, "%llu\n");
+
+static int cam_debug_init(void)
+{
+	struct dentry *cam_dir;
+	debugfs_base = debugfs_create_dir("sensor", NULL);
+	if (!debugfs_base)
+		return -ENOMEM;
+
+	cam_dir = debugfs_create_dir("qs_s5k4e1", debugfs_base);
+	if (!cam_dir)
+		return -ENOMEM;
+
+	if (!debugfs_create_file("focus", S_IRUGO | S_IWUSR, cam_dir,
+							 NULL, &cam_focus))
+		return -ENOMEM;
+	if (!debugfs_create_file("step", S_IRUGO | S_IWUSR, cam_dir,
+							 NULL, &cam_step))
+		return -ENOMEM;
+	if (!debugfs_create_file("stream", S_IRUGO | S_IWUSR, cam_dir,
+							 NULL, &cam_stream))
+		return -ENOMEM;
+	if (!debugfs_create_file("af_step", S_IRUGO | S_IWUSR, cam_dir,
+							 NULL, &af_step))
+		return -ENOMEM;
+	return 0;
+}
+
+static int __qs_s5k4e1_probe(struct platform_device *pdev)
+{
+	return msm_camera_drv_start(pdev, qs_s5k4e1_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+	.probe = __qs_s5k4e1_probe,
+	.driver = {
+		.name = "msm_camera_qs_s5k4e1",
+	.owner = THIS_MODULE,
+	},
+};
+
+static int __init qs_s5k4e1_init(void)
+{
+	return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(qs_s5k4e1_init);
+void qs_s5k4e1_exit(void)
+{
+	i2c_del_driver(&qs_s5k4e1_i2c_driver);
+}
+MODULE_DESCRIPTION("Samsung 5MP Bayer sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/qs_s5k4e1.h b/drivers/media/video/msm/qs_s5k4e1.h
new file mode 100644
index 0000000..f9c4c3f
--- /dev/null
+++ b/drivers/media/video/msm/qs_s5k4e1.h
@@ -0,0 +1,89 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef QS_S5K4E1_H
+#define QS_S5K4E1_H
+#include <linux/types.h>
+#include <mach/board.h>
+extern struct qs_s5k4e1_reg qs_s5k4e1_regs;
+
+#define LENS_SHADE_TABLE 16
+
+struct qs_s5k4e1_i2c_reg_conf {
+	unsigned short waddr;
+	unsigned short wdata;
+};
+
+struct qs_s5k4e1_i2c_conf_array {
+       struct qs_s5k4e1_i2c_reg_conf *conf;
+       unsigned short size;
+};
+
+enum qs_s5k4e1_test_mode_t {
+	TEST_OFF,
+	TEST_1,
+	TEST_2,
+	TEST_3
+};
+
+enum qs_s5k4e1_resolution_t {
+	QTR_2D_SIZE,
+	FULL_2D_SIZE,
+	QTR_3D_SIZE,
+	FULL_3D_SIZE,
+	INVALID_SIZE
+};
+enum qs_s5k4e1_setting {
+	RES_PREVIEW,
+	RES_CAPTURE,
+	RES_3D_PREVIEW,
+	RES_3D_CAPTURE
+};
+enum qs_s5k4e1_cam_mode_t {
+    MODE_2D_RIGHT,
+	MODE_2D_LEFT,
+	MODE_3D,
+	MODE_INVALID
+};
+enum qs_s5k4e1_reg_update {
+	/* Sensor egisters that need to be updated during initialization */
+	REG_INIT,
+	/* Sensor egisters that needs periodic I2C writes */
+	UPDATE_PERIODIC,
+	/* All the sensor Registers will be updated */
+	UPDATE_ALL,
+	/* Not valid update */
+	UPDATE_INVALID
+};
+
+enum qs_s5k4e1_reg_mode {
+	QS_S5K4E1_FRAME_LENGTH_LINES_H = 1,
+	QS_S5K4E1_FRAME_LENGTH_LINES_L,
+	QS_S5K4E1_LINE_LENGTH_PCK_H,
+	QS_S5K4E1_LINE_LENGTH_PCK_L,
+};
+
+struct qs_s5k4e1_reg {
+	const struct qs_s5k4e1_i2c_reg_conf *rec_settings;
+	const unsigned short rec_size;
+	const struct qs_s5k4e1_i2c_reg_conf *reg_prev;
+	const unsigned short reg_prev_size;
+	const struct qs_s5k4e1_i2c_reg_conf *reg_snap;
+	const unsigned short reg_snap_size;
+	const struct qs_s5k4e1_i2c_reg_conf (*reg_lens)[LENS_SHADE_TABLE];
+	const unsigned short reg_lens_size;
+	const struct qs_s5k4e1_i2c_reg_conf *reg_default_lens;
+	const unsigned short reg_default_lens_size;
+	const struct qs_s5k4e1_i2c_conf_array *conf_array;
+};
+#endif /* QS_S5K4E1_H */
diff --git a/drivers/media/video/msm/qs_s5k4e1_reg.c b/drivers/media/video/msm/qs_s5k4e1_reg.c
new file mode 100644
index 0000000..39c3f29
--- /dev/null
+++ b/drivers/media/video/msm/qs_s5k4e1_reg.c
@@ -0,0 +1,804 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+
+#include "qs_s5k4e1.h"
+
+struct qs_s5k4e1_i2c_reg_conf qs_s5k4e1_prev_settings_3d[] = {
+	{0x0100, 0x00},
+	/*Frame Length*/
+	{0x0340, 0x04},
+	{0x0341, 0x90},
+	/*Line Length*/
+	{0x0342, 0x0A},
+	{0x0343, 0xB2},
+	{0x3030, 0x06},
+	{0x3017, 0xA4},
+	{0x301B, 0x88},
+	{0x30BC, 0x90},
+	{0x301C, 0x04},
+	{0x0202, 0x04},
+	{0x0203, 0x12},
+	{0x0204, 0x00},
+	{0x0205, 0x80},
+	{0x0306, 0x00},
+	{0x0307, 0x60},
+	{0x30B5, 0x01},
+	{0x30E2, 0x02},/*num lanes[1:0] = 1*/
+	{0x30F1, 0x60},
+/*MIPI Size Setting*/
+	{0x30A9, 0x02},
+	{0x300E, 0xE8},
+	{0x0387, 0x01},
+	{0x0344, 0x01},
+	{0x0345, 0x18},
+	{0x0348, 0x09},
+	{0x0349, 0x17},
+	{0x0346, 0x01},
+	{0x0347, 0x94},
+	{0x034A, 0x06},
+	{0x034B, 0x13},
+	{0x0380, 0x00},
+	{0x0381, 0x01},
+	{0x0382, 0x00},
+	{0x0383, 0x01},
+	{0x0384, 0x00},
+	{0x0385, 0x01},
+	{0x0386, 0x00},
+	{0x0387, 0x01},
+	{0x034C, 0x04},
+	{0x034D, 0x00},
+	{0x034E, 0x04},
+	{0x034F, 0x80},
+	{0x30BF, 0xAA},
+	{0x30C0, 0x40},
+	{0x30C8, 0x04},
+	{0x30C9, 0x00},
+};
+
+struct qs_s5k4e1_i2c_reg_conf qs_s5k4e1_prev_settings_2d[] = {
+	{0x0100, 0x00},
+	{0x0340, 0x03},
+	{0x0341, 0xe0},
+	{0x0342, 0x0A},
+	{0x0343, 0xB2},
+	{0x3030, 0x06},
+	{0x301B, 0x83},
+	{0x30BC, 0x98},
+	{0x301C, 0x04},
+	{0x0202, 0x01},
+	{0x0203, 0xFD},
+	{0x0204, 0x00},
+	{0x0205, 0x80},
+	{0x0306, 0x00},
+	{0x0307, 0x64},
+	{0x30B5, 0x00},
+	{0x30E2, 0x01},/*num lanes[1:0] = 1*/
+	{0x30F1, 0xd0},
+	{0x30A9, 0x02},
+	{0x300E, 0xEB},
+	{0x0387, 0x03},
+	{0x0344, 0x00},
+	{0x0345, 0x00},
+	{0x0348, 0x0A},
+	{0x0349, 0x2F},
+	{0x0346, 0x00},
+	{0x0347, 0x00},
+	{0x034A, 0x07},
+	{0x034B, 0xA7},
+	{0x0380, 0x00},
+	{0x0381, 0x01},
+	{0x0382, 0x00},
+	{0x0383, 0x01},
+	{0x0384, 0x00},
+	{0x0385, 0x01},
+	{0x0386, 0x00},
+	{0x0387, 0x03},
+	{0x034C, 0x05},
+	{0x034D, 0x10},
+	{0x034E, 0x03},
+	{0x034F, 0xd4},
+	{0x30BF, 0xAB},
+	{0x30C0, 0xc0},
+	{0x30C8, 0x06},
+	{0x30C9, 0x54},
+};
+
+struct qs_s5k4e1_i2c_reg_conf qs_s5k4e1_snap_settings_2d[] = {
+	{0x0100, 0x00},
+	{0x0340, 0x07},
+	{0x0341, 0xb4},
+	{0x0342, 0x0A},
+	{0x0343, 0xB2},
+	{0x3030, 0x06}, /*shut streaming off*/
+	{0x300E, 0xE8},
+	{0x301B, 0x75},
+	{0x301C, 0x04},
+	{0x30BC, 0x98},
+	{0x0202, 0x04},
+	{0x0203, 0x12},
+	{0x0204, 0x00},
+	{0x0205, 0x80},
+	{0x0306, 0x00},
+	{0x0307, 0x64},
+	{0x30B5, 0x00},
+	{0x30E2, 0x01},/*num lanes[1:0] = 1*/
+	{0x30F1, 0xd0},
+	{0x30A9, 0x03},/*Horizontal Binning Off*/
+	{0x300E, 0xE8},/*Vertical Binning Off*/
+	{0x0387, 0x01},/*y_odd_inc*/
+	{0x034C, 0x0A},/*x_output size*/
+	{0x034D, 0x30},
+	{0x034E, 0x07},/*y_output size*/
+	{0x034F, 0xA8},
+	{0x30BF, 0xAB},/*outif_enable[7], data_type[5:0](2Bh = bayer 10bit)*/
+	{0x30C0, 0x86},/*video_offset[7:4] 3260%12*/
+	{0x30C8, 0x0C},/*video_data_length 3260 = 2608 * 1.25*/
+	{0x30C9, 0xBC},
+
+};
+
+struct qs_s5k4e1_i2c_reg_conf qs_s5k4e1_snap_settings_3d[] = {
+	{0x0100, 0x00},
+
+/* Frame Length*/
+	{0x0340, 0x09},
+	{0x0341, 0x20},
+/* Line Length*/
+	{0x0342, 0x0A},
+	{0x0343, 0xB2},
+	{0x3030, 0x06},/*shut streaming off*/
+/*Analog Setting*/
+	{0x3017, 0xA4},
+	{0x301B, 0x88},
+	{0x30BC, 0x90},
+	{0x301C, 0x04},
+/*Integration setting ... */
+	{0x0202, 0x04},
+	{0x0203, 0x12},
+	{0x0204, 0x00},
+	{0x0205, 0x80},
+/*PLL setting ...*/
+	{0x0306, 0x00},
+	{0x0307, 0x60},
+	{0x30B5, 0x01},
+	{0x30E2, 0x02},/*num lanes[1:0] = 1*/
+	{0x30F1, 0x60},
+/*MIPI Size Setting*/
+	{0x30A9, 0x01},
+	{0x300E, 0xE8},
+	{0x0387, 0x01},
+	{0x0344, 0x01},/*x_addr_start*/
+	{0x0345, 0x14},
+	{0x0348, 0x09},/*x_addr_end*/
+	{0x0349, 0x17},
+	{0x0346, 0x01},/*y_addr_start*/
+	{0x0347, 0x94},
+	{0x034A, 0x06},/*y_addr_end*/
+	{0x034B, 0x13},
+	{0x0380, 0x00},/*x_even_inc 1*/
+	{0x0381, 0x01},
+	{0x0382, 0x00},/*x_odd_inc 1*/
+	{0x0383, 0x01},
+	{0x0384, 0x00},/*y_even_inc 1*/
+	{0x0385, 0x01},
+	{0x0386, 0x00},/*y_odd_inc 1*/
+	{0x0387, 0x01},
+	{0x034C, 0x08},/*x_output size*/
+	{0x034D, 0x00},
+	{0x034E, 0x04},/*y_output size*/
+	{0x034F, 0x80},
+	{0x30BF, 0xAA},/*outif_enable[7], data_type[5:0](2Bh = bayer 8bit)*/
+	{0x30C0, 0x80},/*video_offset[7:4]*/
+	{0x30C8, 0x08},/*video_data_length*/
+	{0x30C9, 0x00},
+
+};
+
+struct qs_s5k4e1_i2c_reg_conf qs_s5k4e1_recommend_settings[] = {
+	{0x0100, 0x00},
+
+	{0x3030, 0x06},/*shut streaming*/
+/*Analog Setting*/
+	{0x3000, 0x05},
+	{0x3001, 0x03},
+	{0x3002, 0x08},
+	{0x3003, 0x09},
+	{0x3004, 0x2E},
+	{0x3005, 0x06},
+	{0x3006, 0x34},
+	{0x3007, 0x00},
+	{0x3008, 0x3C},
+	{0x3009, 0x3C},
+	{0x300A, 0x28},
+	{0x300B, 0x04},
+	{0x300C, 0x0A},
+	{0x300D, 0x02},
+	{0x300F, 0x82},
+	{0x3010, 0x00},
+	{0x3011, 0x4C},
+	{0x3012, 0x30},
+	{0x3013, 0xC0},
+	{0x3014, 0x00},
+	{0x3015, 0x00},
+	{0x3016, 0x2C},
+	{0x3017, 0x94},
+	{0x3018, 0x78},
+	{0x301D, 0xD4},
+	{0x3021, 0x02},
+	{0x3022, 0x24},
+	{0x3024, 0x40},
+	{0x3027, 0x08},
+	{0x3029, 0xC6},
+	{0x302B, 0x01},
+	{0x30D8, 0x3F},
+/* ADLC setting ...*/
+	{0x3070, 0x5F},
+	{0x3071, 0x00},
+	{0x3080, 0x04},
+	{0x3081, 0x38},
+
+/*MIPI setting*/
+	{0x30BD, 0x00},/*SEL_CCP[0]*/
+	{0x3084, 0x15},/*SYNC Mode*/
+	{0x30BE, 0x1A},/*M_PCLKDIV_AUTO[4], M_DIV_PCLK[3:0]*/
+	{0x30C1, 0x01},/*pack video enable [0]*/
+	{0x30EE, 0x02},/*DPHY enable [1]*/
+	{0x3111, 0x86},/*Embedded data off [5]*/
+/*For MIPI T8 T9*/
+	{0x30E3, 0x38},
+	{0x30E4, 0x40},
+	{0x3113, 0x70},
+	{0x3114, 0x80},
+	{0x3115, 0x7B},
+	{0x3116, 0xC0},
+	{0x30EE, 0x12},
+
+/*PLL setting ...*/
+	{0x0305, 0x06},
+};
+static struct qs_s5k4e1_i2c_reg_conf qs_s5k4e1_default_lenshading_settings[] = {
+
+	{0x3200, 0x00},
+	{0x3201, 0x9a},
+	{0x3202, 0x56},
+	{0x3203, 0xf },
+	{0x3204, 0xd8},
+	{0x3205, 0x94},
+	{0x3206, 0x0 },
+	{0x3207, 0x10},
+	{0x3208, 0x71},
+	{0x3209, 0x0 },
+	{0x320a, 0x9 },
+	{0x320b, 0xc1},
+	{0x320c, 0xf },
+	{0x320d, 0xf1},
+	{0x320e, 0x3d},
+	{0x320f, 0x0 },
+	{0x3210, 0xa },
+	{0x3211, 0x93},
+	{0x3212, 0xf },
+	{0x3213, 0xc9},
+	{0x3214, 0xa1},
+	{0x3215, 0x0 },
+	{0x3216, 0x10},
+	{0x3217, 0x89},
+	{0x3218, 0xf },
+	{0x3219, 0xfb},
+	{0x321a, 0xf3},
+	{0x321b, 0xf },
+	{0x321c, 0xf8},
+	{0x321d, 0xfc},
+	{0x321e, 0x0 },
+	{0x321f, 0x4 },
+	{0x3220, 0xe3},
+	{0x3221, 0xf },
+	{0x3222, 0xfe},
+	{0x3223, 0x94},
+	{0x3224, 0x0 },
+	{0x3225, 0x24},
+	{0x3226, 0x59},
+	{0x3227, 0xf },
+	{0x3228, 0xe9},
+	{0x3229, 0x68},
+	{0x322a, 0xf },
+	{0x322b, 0xfa},
+	{0x322c, 0x7f},
+	{0x322d, 0x0 },
+	{0x322e, 0x13},
+	{0x322f, 0xe1},
+	{0x3230, 0x0 },
+	{0x3231, 0x3 },
+	{0x3232, 0xbc},
+	{0x3233, 0xf },
+	{0x3234, 0xf0},
+	{0x3235, 0xa1},
+	{0x3236, 0xf },
+	{0x3237, 0xf4},
+	{0x3238, 0xc9},
+	{0x3239, 0x0 },
+	{0x323a, 0x11},
+	{0x323b, 0x4b},
+	{0x323c, 0x0 },
+	{0x323d, 0x12},
+	{0x323e, 0xc5},
+	{0x323f, 0xf },
+	{0x3240, 0xe3},
+	{0x3241, 0xb },
+	{0x3242, 0xf },
+	{0x3243, 0xf8},
+	{0x3244, 0x4f},
+	{0x3245, 0x0 },
+	{0x3246, 0x13},
+	{0x3247, 0xac},
+	{0x3248, 0x0 },
+	{0x3249, 0x0 },
+	{0x324a, 0x7c},
+	{0x324b, 0xf },
+	{0x324c, 0xfe},
+	{0x324d, 0xdd},
+	{0x324e, 0xf },
+	{0x324f, 0xf2},
+	{0x3250, 0x96},
+	{0x3251, 0x0 },
+	{0x3252, 0x8 },
+	{0x3253, 0xef},
+	{0x3254, 0x0 },
+	{0x3255, 0x6 },
+	{0x3256, 0xa4},
+	{0x3257, 0x0 },
+	{0x3258, 0x2 },
+	{0x3259, 0x4b},
+	{0x325a, 0x0 },
+	{0x325b, 0x6 },
+	{0x325c, 0x85},
+	{0x325d, 0xf },
+	{0x325e, 0xf8},
+	{0x325f, 0x6a},
+	{0x3260, 0xf },
+	{0x3261, 0xfd},
+	{0x3262, 0x70},
+	{0x3263, 0x0 },
+	{0x3264, 0xd },
+	{0x3265, 0xa9},
+	{0x3266, 0xf },
+	{0x3267, 0xfd},
+	{0x3268, 0xf8},
+	{0x3269, 0xf },
+	{0x326a, 0xec},
+	{0x326b, 0xfc},
+	{0x326c, 0x0 },
+	{0x326d, 0xa7},
+	{0x326e, 0x5 },
+	{0x326f, 0xf },
+	{0x3270, 0xd6},
+	{0x3271, 0x19},
+	{0x3272, 0x0 },
+	{0x3273, 0xa },
+	{0x3274, 0xe8},
+	{0x3275, 0x0 },
+	{0x3276, 0x17},
+	{0x3277, 0x1 },
+	{0x3278, 0xf },
+	{0x3279, 0xe7},
+	{0x327a, 0xa0},
+	{0x327b, 0x0 },
+	{0x327c, 0xb },
+	{0x327d, 0xc3},
+	{0x327e, 0xf },
+	{0x327f, 0xc0},
+	{0x3280, 0xe3},
+	{0x3281, 0x0 },
+	{0x3282, 0x15},
+	{0x3283, 0x5a},
+	{0x3284, 0xf },
+	{0x3285, 0xf9},
+	{0x3286, 0xa0},
+	{0x3287, 0xf },
+	{0x3288, 0xf4},
+	{0x3289, 0xce},
+	{0x328a, 0x0 },
+	{0x328b, 0xb },
+	{0x328c, 0x72},
+	{0x328d, 0xf },
+	{0x328e, 0xfb},
+	{0x328f, 0xb5},
+	{0x3290, 0x0 },
+	{0x3291, 0x2f},
+	{0x3292, 0xb },
+	{0x3293, 0xf },
+	{0x3294, 0xde},
+	{0x3295, 0xc0},
+	{0x3296, 0x0 },
+	{0x3297, 0x0 },
+	{0x3298, 0x58},
+	{0x3299, 0x0 },
+	{0x329a, 0x1b},
+	{0x329b, 0x5 },
+	{0x329c, 0xf },
+	{0x329d, 0xf9},
+	{0x329e, 0x23},
+	{0x329f, 0xf },
+	{0x32a0, 0xf3},
+	{0x32a1, 0x94},
+	{0x32a2, 0xf },
+	{0x32a3, 0xe7},
+	{0x32a4, 0xc2},
+	{0x32a5, 0x0 },
+	{0x32a6, 0x1d},
+	{0x32a7, 0xe5},
+	{0x32a8, 0x0 },
+	{0x32a9, 0x5 },
+	{0x32aa, 0xaf},
+	{0x32ab, 0xf },
+	{0x32ac, 0xe3},
+	{0x32ad, 0xb7},
+	{0x32ae, 0xf },
+	{0x32af, 0xf8},
+	{0x32b0, 0x34},
+	{0x32b1, 0x0 },
+	{0x32b2, 0x1c},
+	{0x32b3, 0x3d},
+	{0x32b4, 0x0 },
+	{0x32b5, 0x10},
+	{0x32b6, 0x4a},
+	{0x32b7, 0xf },
+	{0x32b8, 0xfa},
+	{0x32b9, 0x7 },
+	{0x32ba, 0xf },
+	{0x32bb, 0xff},
+	{0x32bc, 0x16},
+	{0x32bd, 0x0 },
+	{0x32be, 0x5 },
+	{0x32bf, 0x4e},
+	{0x32c0, 0x0 },
+	{0x32c1, 0xc },
+	{0x32c2, 0x1b},
+	{0x32c3, 0xf },
+	{0x32c4, 0xf1},
+	{0x32c5, 0xdb},
+	{0x32c6, 0xf },
+	{0x32c7, 0xfc},
+	{0x32c8, 0xf8},
+	{0x32c9, 0xf },
+	{0x32ca, 0xf4},
+	{0x32cb, 0xad},
+	{0x32cc, 0xf },
+	{0x32cd, 0xfb},
+	{0x32ce, 0x59},
+	{0x32cf, 0x0 },
+	{0x32d0, 0x9 },
+	{0x32d1, 0xf7},
+	{0x32d2, 0x0 },
+	{0x32d3, 0x0 },
+	{0x32d4, 0xc1},
+	{0x32d5, 0xf },
+	{0x32d6, 0xf5},
+	{0x32d7, 0x30},
+	{0x32d8, 0x0 },
+	{0x32d9, 0x83},
+	{0x32da, 0x1d},
+	{0x32db, 0xf },
+	{0x32dc, 0xe3},
+	{0x32dd, 0x3c},
+	{0x32de, 0x0 },
+	{0x32df, 0xa },
+	{0x32e0, 0x10},
+	{0x32e1, 0x0 },
+	{0x32e2, 0x7 },
+	{0x32e3, 0x65},
+	{0x32e4, 0xf },
+	{0x32e5, 0xfe},
+	{0x32e6, 0x79},
+	{0x32e7, 0xf },
+	{0x32e8, 0xfd},
+	{0x32e9, 0x57},
+	{0x32ea, 0xf },
+	{0x32eb, 0xd6},
+	{0x32ec, 0x8f},
+	{0x32ed, 0x0 },
+	{0x32ee, 0x3 },
+	{0x32ef, 0x93},
+	{0x32f0, 0x0 },
+	{0x32f1, 0x6 },
+	{0x32f2, 0xa },
+	{0x32f3, 0xf },
+	{0x32f4, 0xfa},
+	{0x32f5, 0x6c},
+	{0x32f6, 0xf },
+	{0x32f7, 0xf1},
+	{0x32f8, 0x1e},
+	{0x32f9, 0x0 },
+	{0x32fa, 0x14},
+	{0x32fb, 0xe7},
+	{0x32fc, 0x0 },
+	{0x32fd, 0x1f},
+	{0x32fe, 0x2d},
+	{0x32ff, 0x0 },
+	{0x3300, 0x7 },
+	{0x3301, 0x5e},
+	{0x3302, 0xf },
+	{0x3303, 0xe0},
+	{0x3304, 0x55},
+	{0x3305, 0x0 },
+	{0x3306, 0x20},
+	{0x3307, 0x93},
+	{0x3308, 0x0 },
+	{0x3309, 0xf },
+	{0x330a, 0x20},
+	{0x330b, 0xf },
+	{0x330c, 0xd7},
+	{0x330d, 0xf5},
+	{0x330e, 0xf },
+	{0x330f, 0xef},
+	{0x3310, 0xb8},
+	{0x3311, 0xf },
+	{0x3312, 0xf0},
+	{0x3313, 0x29},
+	{0x3314, 0x0 },
+	{0x3315, 0x27},
+	{0x3316, 0x5e},
+	{0x3317, 0xf },
+	{0x3318, 0xda},
+	{0x3319, 0x14},
+	{0x331a, 0xf },
+	{0x331b, 0xef},
+	{0x331c, 0x93},
+	{0x331d, 0x0 },
+	{0x331e, 0x2c},
+	{0x331f, 0xdc},
+	{0x3320, 0x0 },
+	{0x3321, 0xe },
+	{0x3322, 0x2d},
+	{0x3323, 0x0 },
+	{0x3324, 0x6 },
+	{0x3325, 0xcf},
+	{0x3326, 0xf },
+	{0x3327, 0xfb},
+	{0x3328, 0x26},
+	{0x3329, 0x0 },
+	{0x332a, 0x3 },
+	{0x332b, 0x5 },
+	{0x332c, 0x0 },
+	{0x332d, 0x6 },
+	{0x332e, 0xa6},
+	{0x332f, 0xf },
+	{0x3330, 0xf7},
+	{0x3331, 0x7b},
+	{0x3332, 0xf },
+	{0x3333, 0xf9},
+	{0x3334, 0xb },
+	{0x3335, 0x0 },
+	{0x3336, 0x7 },
+	{0x3337, 0x5a},
+	{0x3338, 0xf },
+	{0x3339, 0xe4},
+	{0x333a, 0x7a},
+	{0x333b, 0x0 },
+	{0x333c, 0x1b},
+	{0x333d, 0xb0},
+	{0x333e, 0x0 },
+	{0x333f, 0x2 },
+	{0x3340, 0xa7},
+	{0x3341, 0xf },
+	{0x3342, 0xe9},
+	{0x3343, 0x3a},
+	{0x3344, 0x0 },
+	{0x3345, 0x95},
+	{0x3346, 0x42},
+	{0x3347, 0xf },
+	{0x3348, 0xda},
+	{0x3349, 0x45},
+	{0x334a, 0x0 },
+	{0x334b, 0x16},
+	{0x334c, 0x7a},
+	{0x334d, 0xf },
+	{0x334e, 0xfb},
+	{0x334f, 0x32},
+	{0x3350, 0x0 },
+	{0x3351, 0x6 },
+	{0x3352, 0x35},
+	{0x3353, 0xf },
+	{0x3354, 0xfc},
+	{0x3355, 0x8f},
+	{0x3356, 0xf },
+	{0x3357, 0xca},
+	{0x3358, 0xd5},
+	{0x3359, 0x0 },
+	{0x335a, 0x11},
+	{0x335b, 0x59},
+	{0x335c, 0xf },
+	{0x335d, 0xfa},
+	{0x335e, 0xaa},
+	{0x335f, 0xf },
+	{0x3360, 0xfe},
+	{0x3361, 0x84},
+	{0x3362, 0xf },
+	{0x3363, 0xf6},
+	{0x3364, 0x8f},
+	{0x3365, 0x0 },
+	{0x3366, 0xb },
+	{0x3367, 0x70},
+	{0x3368, 0x0 },
+	{0x3369, 0x25},
+	{0x336a, 0x83},
+	{0x336b, 0xf },
+	{0x336c, 0xe7},
+	{0x336d, 0x27},
+	{0x336e, 0xf },
+	{0x336f, 0xf1},
+	{0x3370, 0x72},
+	{0x3371, 0x0 },
+	{0x3372, 0x21},
+	{0x3373, 0x6d},
+	{0x3374, 0x0 },
+	{0x3375, 0x2 },
+	{0x3376, 0xc3},
+	{0x3377, 0xf },
+	{0x3378, 0xe8},
+	{0x3379, 0x5a},
+	{0x337a, 0xf },
+	{0x337b, 0xf2},
+	{0x337c, 0x73},
+	{0x337d, 0x0 },
+	{0x337e, 0x19},
+	{0x337f, 0xa5},
+	{0x3380, 0x0 },
+	{0x3381, 0x1a},
+	{0x3382, 0x81},
+	{0x3383, 0xf },
+	{0x3384, 0xd0},
+	{0x3385, 0x31},
+	{0x3386, 0xf },
+	{0x3387, 0xfb},
+	{0x3388, 0xff},
+	{0x3389, 0x0 },
+	{0x338a, 0x1e},
+	{0x338b, 0xe1},
+	{0x338c, 0x0 },
+	{0x338d, 0x5 },
+	{0x338e, 0xe1},
+	{0x338f, 0xf },
+	{0x3390, 0xee},
+	{0x3391, 0xe2},
+	{0x3392, 0xf },
+	{0x3393, 0xf6},
+	{0x3394, 0xcf},
+	{0x3395, 0x0 },
+	{0x3396, 0x13},
+	{0x3397, 0x8f},
+	{0x3398, 0x0 },
+	{0x3399, 0x3 },
+	{0x339a, 0x61},
+	{0x339b, 0xf },
+	{0x339c, 0xf8},
+	{0x339d, 0xf7},
+	{0x339e, 0x0 },
+	{0x339f, 0x0 },
+	{0x33a0, 0xb5},
+	{0x33a1, 0x0 },
+	{0x33a2, 0x5 },
+	{0x33a3, 0x78},
+	{0x33a4, 0xf },
+	{0x33a5, 0xf4},
+	{0x33a6, 0x5 },
+	{0x33a7, 0x0 },
+	{0x33a8, 0xc },
+	{0x33a9, 0xe },
+	{0x33aa, 0x0 },
+	{0x33ab, 0x3 },
+	{0x33ac, 0x53},
+	{0x33ad, 0xf },
+	{0x33ae, 0xec},
+	{0x33af, 0xbd},
+};
+
+const struct
+qs_s5k4e1_i2c_reg_conf qs_s5k4e1_lenshading_settings[4][LENS_SHADE_TABLE] = {
+	{/*2D Preview*/
+		{0x3097, 0x52},/*sh4ch_blk_width = 82*/
+		{0x3098, 0x3e},/*sh4ch_blk_height = 62*/
+		{0x3099, 0x03},/*sh4ch_step_x msb (sh4ch_step_x = 799)*/
+		{0x309a, 0x1f},/*sh4ch_step_x lsb*/
+		{0x309b, 0x04},/*sh4ch_step_y msb (sh4ch_step_y = 1057)*/
+		{0x309c, 0x21},/*sh4ch_step_y lsb*/
+		{0x309d, 0x00},/*sh4ch_start_blk_cnt_x = 0*/
+		{0x309e, 0x00},/*sh4ch_start_int_cnt_x = 0*/
+		{0x309f, 0x00},/*sh4ch_start_frac_cnt_x msb (0)*/
+		{0x30a0, 0x00},/*sh4ch_start_frac_cnt_x lsb*/
+		{0x30a1, 0x00},/*sh4ch_start_blk_cnt_y = 0*/
+		{0x30a2, 0x00},/*sh4ch_start_int_cnt_y = 0*/
+		{0x30a3, 0x00},/*sh4ch_start_frac_cnt_y msb (0)*/
+		{0x30a4, 0x00},/*sh4ch_start_frac_cnt_y lsb*/
+		{0x30a5, 0x01},
+		{0x30a6, 0x00},/*gs_pedestal	= 64*/
+	},
+	{/*2D Snapshot*/
+		{0x3097, 0x52},/*sh4ch_blk_width = 82*/
+		{0x3098, 0x7b},/*sh4ch_blk_height = 123*/
+		{0x3099, 0x03},/*sh4ch_step_x msb (sh4ch_step_x = 799)*/
+		{0x309a, 0x1f},/*sh4ch_step_x lsb*/
+		{0x309b, 0x02},/*sh4ch_step_y msb (sh4ch_step_y = 533)*/
+		{0x309c, 0x15},/*sh4ch_step_y lsb*/
+		{0x309d, 0x00},/*sh4ch_start_blk_cnt_x = 0*/
+		{0x309e, 0x00},/*sh4ch_start_int_cnt_x = 0*/
+		{0x309f, 0x00},/*sh4ch_start_frac_cnt_x msb (0)*/
+		{0x30a0, 0x00},/*sh4ch_start_frac_cnt_x lsb*/
+		{0x30a1, 0x00},/*sh4ch_start_blk_cnt_y = 0*/
+		{0x30a2, 0x00},/*sh4ch_start_int_cnt_y = 0*/
+		{0x30a3, 0x00},/*sh4ch_start_frac_cnt_y msb (0)*/
+		{0x30a4, 0x00},/*sh4ch_start_frac_cnt_y lsb*/
+		{0x30a5, 0x01},
+		{0x30a6, 0x00},/*gs_pedestal	= 64*/
+	},
+
+	{/*3D Preview*/
+		{0x3097, 0x52},/*sh4ch_blk_width = 82*/
+		{0x3098, 0x7b},/*sh4ch_blk_height = 123*/
+		{0x3099, 0x03},/*sh4ch_step_x msb (sh4ch_step_x = 799)*/
+		{0x309a, 0x1f},/*sh4ch_step_x lsb*/
+		{0x309b, 0x02},/*sh4ch_step_y msb (sh4ch_step_y = 533)*/
+		{0x309c, 0x15},/*sh4ch_step_y lsb*/
+		{0x309d, 0x3a},/*sh4ch_start_blk_cnt_x = 58*/
+		{0x309e, 0x01},/*sh4ch_start_int_cnt_x = 1*/
+		{0x309f, 0xb5},/*sh4ch_start_frac_cnt_x msb (46342)*/
+		{0x30a0, 0x06},/*sh4ch_start_frac_cnt_x lsb*/
+		{0x30a1, 0x23},/*sh4ch_start_blk_cnt_y = 35*/
+		{0x30a2, 0x03},/*sh4ch_start_int_cnt_y = 3*/
+		{0x30a3, 0x48},/*sh4ch_start_frac_cnt_y msb (46342)*/
+		{0x30a4, 0xdf},/*sh4ch_start_frac_cnt_y lsb*/
+		{0x30a5, 0x01},
+		{0x30a6, 0x00},/*gs_pedestal	= 64*/
+	},
+
+	{/*3D Snapshot*/
+		{0x3097, 0x52},/*sh4ch_blk_width = 82*/
+		{0x3098, 0x7b},/*sh4ch_blk_height = 123*/
+		{0x3099, 0x03},/*sh4ch_step_x msb (sh4ch_step_x = 799)*/
+		{0x309a, 0x1f},/*sh4ch_step_x lsb*/
+		{0x309b, 0x02},/*sh4ch_step_y msb (sh4ch_step_y = 533)*/
+		{0x309c, 0x15},/*sh4ch_step_y lsb*/
+		{0x309d, 0x38},/*sh4ch_start_blk_cnt_x = 56*/
+		{0x309e, 0x01},/*sh4ch_start_int_cnt_x = 1*/
+		{0x309f, 0xae},/*sh4ch_start_frac_cnt_x msb (44744)*/
+		{0x30a0, 0xc8},/*sh4ch_start_frac_cnt_x lsb*/
+		{0x30a1, 0x23},/*sh4ch_start_blk_cnt_y = 35*/
+		{0x30a2, 0x03},/*sh4ch_start_int_cnt_y = 3*/
+		{0x30a3, 0x48},/*sh4ch_start_frac_cnt_y msb (44744)*/
+		{0x30a4, 0xdf},/*sh4ch_start_frac_cnt_y lsb*/
+		{0x30a5, 0x01},
+		{0x30a6, 0x00},/*gs_pedestal	= 64*/
+	},
+
+};
+
+struct qs_s5k4e1_i2c_conf_array qs_s5k4e1_confs[] = {
+	{&qs_s5k4e1_prev_settings_2d[0], \
+		ARRAY_SIZE(qs_s5k4e1_prev_settings_2d)},
+	{&qs_s5k4e1_snap_settings_2d[0], \
+		ARRAY_SIZE(qs_s5k4e1_snap_settings_2d)},
+	{&qs_s5k4e1_prev_settings_3d[0], \
+		ARRAY_SIZE(qs_s5k4e1_prev_settings_3d)},
+	{&qs_s5k4e1_snap_settings_3d[0], \
+		ARRAY_SIZE(qs_s5k4e1_snap_settings_3d)},
+};
+struct qs_s5k4e1_reg qs_s5k4e1_regs = {
+	.rec_settings = &qs_s5k4e1_recommend_settings[0],
+	.rec_size = ARRAY_SIZE(qs_s5k4e1_recommend_settings),
+	.reg_lens = &qs_s5k4e1_lenshading_settings[0],
+	.reg_lens_size = ARRAY_SIZE(qs_s5k4e1_lenshading_settings[0]),
+	.reg_default_lens = &qs_s5k4e1_default_lenshading_settings[0],
+	.reg_default_lens_size =
+		ARRAY_SIZE(qs_s5k4e1_default_lenshading_settings),
+	.conf_array = &qs_s5k4e1_confs[0],
+};
diff --git a/drivers/media/video/msm/s5k3e2fx.c b/drivers/media/video/msm/s5k3e2fx.c
new file mode 100644
index 0000000..07a058e
--- /dev/null
+++ b/drivers/media/video/msm/s5k3e2fx.c
@@ -0,0 +1,1387 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <media/msm_camera.h>
+#include <mach/gpio.h>
+#include <mach/camera.h>
+#include "s5k3e2fx.h"
+
+#define S5K3E2FX_REG_MODEL_ID   0x0000
+#define S5K3E2FX_MODEL_ID       0x3E2F
+
+/* PLL Registers */
+#define REG_PRE_PLL_CLK_DIV           0x0305
+#define REG_PLL_MULTIPLIER_MSB        0x0306
+#define REG_PLL_MULTIPLIER_LSB        0x0307
+#define REG_VT_PIX_CLK_DIV            0x0301
+#define REG_VT_SYS_CLK_DIV            0x0303
+#define REG_OP_PIX_CLK_DIV            0x0309
+#define REG_OP_SYS_CLK_DIV            0x030B
+
+/* Data Format Registers */
+#define REG_CCP_DATA_FORMAT_MSB       0x0112
+#define REG_CCP_DATA_FORMAT_LSB       0x0113
+
+/* Output Size */
+#define REG_X_OUTPUT_SIZE_MSB         0x034C
+#define REG_X_OUTPUT_SIZE_LSB         0x034D
+#define REG_Y_OUTPUT_SIZE_MSB         0x034E
+#define REG_Y_OUTPUT_SIZE_LSB         0x034F
+
+/* Binning */
+#define REG_X_EVEN_INC                0x0381
+#define REG_X_ODD_INC                 0x0383
+#define REG_Y_EVEN_INC                0x0385
+#define REG_Y_ODD_INC                 0x0387
+/*Reserved register */
+#define REG_BINNING_ENABLE            0x3014
+
+/* Frame Fotmat */
+#define REG_FRAME_LENGTH_LINES_MSB    0x0340
+#define REG_FRAME_LENGTH_LINES_LSB    0x0341
+#define REG_LINE_LENGTH_PCK_MSB       0x0342
+#define REG_LINE_LENGTH_PCK_LSB       0x0343
+
+/* MSR setting */
+/* Reserved registers */
+#define REG_SHADE_CLK_ENABLE          0x30AC
+#define REG_SEL_CCP                   0x30C4
+#define REG_VPIX                      0x3024
+#define REG_CLAMP_ON                  0x3015
+#define REG_OFFSET                    0x307E
+
+/* CDS timing settings */
+/* Reserved registers */
+#define REG_LD_START                  0x3000
+#define REG_LD_END                    0x3001
+#define REG_SL_START                  0x3002
+#define REG_SL_END                    0x3003
+#define REG_RX_START                  0x3004
+#define REG_S1_START                  0x3005
+#define REG_S1_END                    0x3006
+#define REG_S1S_START                 0x3007
+#define REG_S1S_END                   0x3008
+#define REG_S3_START                  0x3009
+#define REG_S3_END                    0x300A
+#define REG_CMP_EN_START              0x300B
+#define REG_CLP_SL_START              0x300C
+#define REG_CLP_SL_END                0x300D
+#define REG_OFF_START                 0x300E
+#define REG_RMP_EN_START              0x300F
+#define REG_TX_START                  0x3010
+#define REG_TX_END                    0x3011
+#define REG_STX_WIDTH                 0x3012
+#define REG_TYPE1_AF_ENABLE           0x3130
+#define DRIVER_ENABLED                0x0001
+#define AUTO_START_ENABLED            0x0010
+#define REG_NEW_POSITION              0x3131
+#define REG_3152_RESERVED             0x3152
+#define REG_315A_RESERVED             0x315A
+#define REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB 0x0204
+#define REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB 0x0205
+#define REG_FINE_INTEGRATION_TIME         0x0200
+#define REG_COARSE_INTEGRATION_TIME       0x0202
+#define REG_COARSE_INTEGRATION_TIME_LSB   0x0203
+
+/* Mode select register */
+#define S5K3E2FX_REG_MODE_SELECT      0x0100
+#define S5K3E2FX_MODE_SELECT_STREAM     0x01   /* start streaming */
+#define S5K3E2FX_MODE_SELECT_SW_STANDBY 0x00   /* software standby */
+#define S5K3E2FX_REG_SOFTWARE_RESET   0x0103
+#define S5K3E2FX_SOFTWARE_RESET         0x01
+#define REG_TEST_PATTERN_MODE         0x0601
+
+struct reg_struct {
+  uint8_t pre_pll_clk_div;               /* 0x0305 */
+  uint8_t pll_multiplier_msb;            /* 0x0306 */
+  uint8_t pll_multiplier_lsb;            /* 0x0307 */
+  uint8_t vt_pix_clk_div;                /* 0x0301 */
+  uint8_t vt_sys_clk_div;                /* 0x0303 */
+  uint8_t op_pix_clk_div;                /* 0x0309 */
+  uint8_t op_sys_clk_div;                /* 0x030B */
+  uint8_t ccp_data_format_msb;           /* 0x0112 */
+  uint8_t ccp_data_format_lsb;           /* 0x0113 */
+  uint8_t x_output_size_msb;             /* 0x034C */
+  uint8_t x_output_size_lsb;             /* 0x034D */
+  uint8_t y_output_size_msb;             /* 0x034E */
+  uint8_t y_output_size_lsb;             /* 0x034F */
+  uint8_t x_even_inc;                    /* 0x0381 */
+  uint8_t x_odd_inc;                     /* 0x0383 */
+  uint8_t y_even_inc;                    /* 0x0385 */
+  uint8_t y_odd_inc;                     /* 0x0387 */
+  uint8_t binning_enable;                /* 0x3014 */
+  uint8_t frame_length_lines_msb;        /* 0x0340 */
+  uint8_t frame_length_lines_lsb;        /* 0x0341 */
+  uint8_t line_length_pck_msb;           /* 0x0342 */
+  uint8_t line_length_pck_lsb;           /* 0x0343 */
+  uint8_t shade_clk_enable ;             /* 0x30AC */
+  uint8_t sel_ccp;                       /* 0x30C4 */
+  uint8_t vpix;                          /* 0x3024 */
+  uint8_t clamp_on;                      /* 0x3015 */
+  uint8_t offset;                        /* 0x307E */
+  uint8_t ld_start;                      /* 0x3000 */
+  uint8_t ld_end;                        /* 0x3001 */
+  uint8_t sl_start;                      /* 0x3002 */
+  uint8_t sl_end;                        /* 0x3003 */
+  uint8_t rx_start;                      /* 0x3004 */
+  uint8_t s1_start;                      /* 0x3005 */
+  uint8_t s1_end;                        /* 0x3006 */
+  uint8_t s1s_start;                     /* 0x3007 */
+  uint8_t s1s_end;                       /* 0x3008 */
+  uint8_t s3_start;                      /* 0x3009 */
+  uint8_t s3_end;                        /* 0x300A */
+  uint8_t cmp_en_start;                  /* 0x300B */
+  uint8_t clp_sl_start;                  /* 0x300C */
+  uint8_t clp_sl_end;                    /* 0x300D */
+  uint8_t off_start;                     /* 0x300E */
+  uint8_t rmp_en_start;                  /* 0x300F */
+  uint8_t tx_start;                      /* 0x3010 */
+  uint8_t tx_end;                        /* 0x3011 */
+  uint8_t stx_width;                     /* 0x3012 */
+  uint8_t reg_3152_reserved;             /* 0x3152 */
+  uint8_t reg_315A_reserved;             /* 0x315A */
+  uint8_t analogue_gain_code_global_msb; /* 0x0204 */
+  uint8_t analogue_gain_code_global_lsb; /* 0x0205 */
+  uint8_t fine_integration_time;         /* 0x0200 */
+  uint8_t coarse_integration_time;       /* 0x0202 */
+  uint32_t  size_h;
+  uint32_t  blk_l;
+  uint32_t  size_w;
+  uint32_t  blk_p;
+};
+
+struct reg_struct s5k3e2fx_reg_pat[2] =  {
+  {	/* Preview */
+    0x06,  /* pre_pll_clk_div       REG=0x0305 */
+    0x00,  /* pll_multiplier_msb    REG=0x0306 */
+    0x88,  /* pll_multiplier_lsb    REG=0x0307 */
+    0x0a,  /* vt_pix_clk_div        REG=0x0301 */
+    0x01,  /* vt_sys_clk_div        REG=0x0303 */
+    0x0a,  /* op_pix_clk_div        REG=0x0309 */
+    0x01,  /* op_sys_clk_div        REG=0x030B */
+    0x0a,  /* ccp_data_format_msb   REG=0x0112 */
+    0x0a,  /* ccp_data_format_lsb   REG=0x0113 */
+    0x05,  /* x_output_size_msb     REG=0x034C */
+    0x10,  /* x_output_size_lsb     REG=0x034D */
+    0x03,  /* y_output_size_msb     REG=0x034E */
+    0xcc,  /* y_output_size_lsb     REG=0x034F */
+
+    /* enable binning for preview */
+    0x01,  /* x_even_inc             REG=0x0381 */
+    0x01,  /* x_odd_inc              REG=0x0383 */
+    0x01,  /* y_even_inc             REG=0x0385 */
+    0x03,  /* y_odd_inc              REG=0x0387 */
+    0x06,  /* binning_enable         REG=0x3014 */
+
+    0x03,  /* frame_length_lines_msb        REG=0x0340 */
+    0xde,  /* frame_length_lines_lsb        REG=0x0341 */
+    0x0a,  /* line_length_pck_msb           REG=0x0342 */
+    0xac,  /* line_length_pck_lsb           REG=0x0343 */
+    0x81,  /* shade_clk_enable              REG=0x30AC */
+    0x01,  /* sel_ccp                       REG=0x30C4 */
+    0x04,  /* vpix                          REG=0x3024 */
+    0x00,  /* clamp_on                      REG=0x3015 */
+    0x02,  /* offset                        REG=0x307E */
+    0x03,  /* ld_start                      REG=0x3000 */
+    0x9c,  /* ld_end                        REG=0x3001 */
+    0x02,  /* sl_start                      REG=0x3002 */
+    0x9e,  /* sl_end                        REG=0x3003 */
+    0x05,  /* rx_start                      REG=0x3004 */
+    0x0f,  /* s1_start                      REG=0x3005 */
+    0x24,  /* s1_end                        REG=0x3006 */
+    0x7c,  /* s1s_start                     REG=0x3007 */
+    0x9a,  /* s1s_end                       REG=0x3008 */
+    0x10,  /* s3_start                      REG=0x3009 */
+    0x14,  /* s3_end                        REG=0x300A */
+    0x10,  /* cmp_en_start                  REG=0x300B */
+    0x04,  /* clp_sl_start                  REG=0x300C */
+    0x26,  /* clp_sl_end                    REG=0x300D */
+    0x02,  /* off_start                     REG=0x300E */
+    0x0e,  /* rmp_en_start                  REG=0x300F */
+    0x30,  /* tx_start                      REG=0x3010 */
+    0x4e,  /* tx_end                        REG=0x3011 */
+    0x1E,  /* stx_width                     REG=0x3012 */
+    0x08,  /* reg_3152_reserved             REG=0x3152 */
+    0x10,  /* reg_315A_reserved             REG=0x315A */
+    0x00,  /* analogue_gain_code_global_msb REG=0x0204 */
+    0x80,  /* analogue_gain_code_global_lsb REG=0x0205 */
+    0x02,  /* fine_integration_time         REG=0x0200 */
+    0x03,  /* coarse_integration_time       REG=0x0202 */
+		972,
+		18,
+		1296,
+		1436
+  },
+  { /* Snapshot */
+    0x06,  /* pre_pll_clk_div               REG=0x0305 */
+    0x00,  /* pll_multiplier_msb            REG=0x0306 */
+    0x88,  /* pll_multiplier_lsb            REG=0x0307 */
+    0x0a,  /* vt_pix_clk_div                REG=0x0301 */
+    0x01,  /* vt_sys_clk_div                REG=0x0303 */
+    0x0a,  /* op_pix_clk_div                REG=0x0309 */
+    0x01,  /* op_sys_clk_div                REG=0x030B */
+    0x0a,  /* ccp_data_format_msb           REG=0x0112 */
+    0x0a,  /* ccp_data_format_lsb           REG=0x0113 */
+    0x0a,  /* x_output_size_msb             REG=0x034C */
+    0x30,  /* x_output_size_lsb             REG=0x034D */
+    0x07,  /* y_output_size_msb             REG=0x034E */
+    0xa8,  /* y_output_size_lsb             REG=0x034F */
+
+    /* disable binning for snapshot */
+    0x01,  /* x_even_inc                    REG=0x0381 */
+    0x01,  /* x_odd_inc                     REG=0x0383 */
+    0x01,  /* y_even_inc                    REG=0x0385 */
+    0x01,  /* y_odd_inc                     REG=0x0387 */
+    0x00,  /* binning_enable                REG=0x3014 */
+
+    0x07,  /* frame_length_lines_msb        REG=0x0340 */
+    0xb6,  /* frame_length_lines_lsb        REG=0x0341 */
+    0x0a,  /* line_length_pck_msb           REG=0x0342 */
+    0xac,  /* line_length_pck_lsb           REG=0x0343 */
+    0x81,  /* shade_clk_enable              REG=0x30AC */
+    0x01,  /* sel_ccp                       REG=0x30C4 */
+    0x04,  /* vpix                          REG=0x3024 */
+    0x00,  /* clamp_on                      REG=0x3015 */
+    0x02,  /* offset                        REG=0x307E */
+    0x03,  /* ld_start                      REG=0x3000 */
+    0x9c,  /* ld_end                        REG=0x3001 */
+    0x02,  /* sl_start                      REG=0x3002 */
+    0x9e,  /* sl_end                        REG=0x3003 */
+    0x05,  /* rx_start                      REG=0x3004 */
+    0x0f,  /* s1_start                      REG=0x3005 */
+    0x24,  /* s1_end                        REG=0x3006 */
+    0x7c,  /* s1s_start                     REG=0x3007 */
+    0x9a,  /* s1s_end                       REG=0x3008 */
+    0x10,  /* s3_start                      REG=0x3009 */
+    0x14,  /* s3_end                        REG=0x300A */
+    0x10,  /* cmp_en_start                  REG=0x300B */
+    0x04,  /* clp_sl_start                  REG=0x300C */
+    0x26,  /* clp_sl_end                    REG=0x300D */
+    0x02,  /* off_start                     REG=0x300E */
+    0x0e,  /* rmp_en_start                  REG=0x300F */
+    0x30,  /* tx_start                      REG=0x3010 */
+    0x4e,  /* tx_end                        REG=0x3011 */
+    0x1E,  /* stx_width                     REG=0x3012 */
+    0x08,  /* reg_3152_reserved             REG=0x3152 */
+    0x10,  /* reg_315A_reserved             REG=0x315A */
+    0x00,  /* analogue_gain_code_global_msb REG=0x0204 */
+    0x80,  /* analogue_gain_code_global_lsb REG=0x0205 */
+    0x02,  /* fine_integration_time         REG=0x0200 */
+    0x03,  /* coarse_integration_time       REG=0x0202 */
+		1960,
+		14,
+		2608,
+		124
+	}
+};
+
+struct s5k3e2fx_work {
+	struct work_struct work;
+};
+static struct s5k3e2fx_work *s5k3e2fx_sensorw;
+static struct i2c_client *s5k3e2fx_client;
+
+struct s5k3e2fx_ctrl {
+	const struct msm_camera_sensor_info *sensordata;
+
+	int sensormode;
+	uint32_t fps_divider; /* init to 1 * 0x00000400 */
+	uint32_t pict_fps_divider; /* init to 1 * 0x00000400 */
+
+	uint16_t curr_lens_pos;
+	uint16_t init_curr_lens_pos;
+	uint16_t my_reg_gain;
+	uint32_t my_reg_line_count;
+
+	enum msm_s_resolution prev_res;
+	enum msm_s_resolution pict_res;
+	enum msm_s_resolution curr_res;
+	enum msm_s_test_mode  set_test;
+};
+
+struct s5k3e2fx_i2c_reg_conf {
+	unsigned short waddr;
+	unsigned char  bdata;
+};
+
+static struct s5k3e2fx_ctrl *s5k3e2fx_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(s5k3e2fx_wait_queue);
+DEFINE_MUTEX(s5k3e2fx_mutex);
+
+static int s5k3e2fx_i2c_rxdata(unsigned short saddr, unsigned char *rxdata,
+	int length)
+{
+	struct i2c_msg msgs[] = {
+		{
+			.addr   = saddr,
+			.flags = 0,
+			.len   = 2,
+			.buf   = rxdata,
+		},
+		{
+			.addr   = saddr,
+			.flags = I2C_M_RD,
+			.len   = length,
+			.buf   = rxdata,
+		},
+	};
+
+	if (i2c_transfer(s5k3e2fx_client->adapter, msgs, 2) < 0) {
+		CDBG("s5k3e2fx_i2c_rxdata failed!\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int32_t s5k3e2fx_i2c_txdata(unsigned short saddr,
+	unsigned char *txdata, int length)
+{
+	struct i2c_msg msg[] = {
+		{
+		.addr  = saddr,
+		.flags = 0,
+		.len = length,
+		.buf = txdata,
+		},
+	};
+
+	if (i2c_transfer(s5k3e2fx_client->adapter, msg, 1) < 0) {
+		CDBG("s5k3e2fx_i2c_txdata failed\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int32_t s5k3e2fx_i2c_write_b(unsigned short saddr, unsigned short waddr,
+	unsigned char bdata)
+{
+	int32_t rc = -EIO;
+	unsigned char buf[4];
+
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (waddr & 0xFF00)>>8;
+	buf[1] = (waddr & 0x00FF);
+	buf[2] = bdata;
+
+	rc = s5k3e2fx_i2c_txdata(saddr, buf, 3);
+
+	if (rc < 0)
+		CDBG("i2c_write_w failed, addr = 0x%x, val = 0x%x!\n",
+			waddr, bdata);
+
+	return rc;
+}
+
+static int32_t s5k3e2fx_i2c_write_table(
+	struct s5k3e2fx_i2c_reg_conf *reg_cfg_tbl, int num)
+{
+	int i;
+	int32_t rc = -EIO;
+	for (i = 0; i < num; i++) {
+		rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+			reg_cfg_tbl->waddr, reg_cfg_tbl->bdata);
+		if (rc < 0)
+			break;
+		reg_cfg_tbl++;
+	}
+
+	return rc;
+}
+
+static int32_t s5k3e2fx_i2c_read_w(unsigned short saddr, unsigned short raddr,
+	unsigned short *rdata)
+{
+	int32_t rc = 0;
+	unsigned char buf[4];
+
+	if (!rdata)
+		return -EIO;
+
+	memset(buf, 0, sizeof(buf));
+
+	buf[0] = (raddr & 0xFF00)>>8;
+	buf[1] = (raddr & 0x00FF);
+
+	rc = s5k3e2fx_i2c_rxdata(saddr, buf, 2);
+	if (rc < 0)
+		return rc;
+
+	*rdata = buf[0] << 8 | buf[1];
+
+	if (rc < 0)
+		CDBG("s5k3e2fx_i2c_read failed!\n");
+
+	return rc;
+}
+
+static int s5k3e2fx_probe_init_done(const struct msm_camera_sensor_info *data)
+{
+	gpio_direction_output(data->sensor_reset, 0);
+	gpio_free(data->sensor_reset);
+	return 0;
+}
+
+static int s5k3e2fx_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+	int32_t  rc;
+	uint16_t chipid = 0;
+
+	rc = gpio_request(data->sensor_reset, "s5k3e2fx");
+	if (!rc)
+		gpio_direction_output(data->sensor_reset, 1);
+	else
+		goto init_probe_done;
+
+	mdelay(20);
+
+	CDBG("s5k3e2fx_sensor_init(): reseting sensor.\n");
+
+	rc = s5k3e2fx_i2c_read_w(s5k3e2fx_client->addr,
+		S5K3E2FX_REG_MODEL_ID, &chipid);
+	if (rc < 0)
+		goto init_probe_fail;
+
+	if (chipid != S5K3E2FX_MODEL_ID) {
+		CDBG("S5K3E2FX wrong model_id = 0x%x\n", chipid);
+		rc = -ENODEV;
+		goto init_probe_fail;
+	}
+
+	goto init_probe_done;
+
+init_probe_fail:
+	s5k3e2fx_probe_init_done(data);
+init_probe_done:
+	return rc;
+}
+
+static int s5k3e2fx_init_client(struct i2c_client *client)
+{
+	/* Initialize the MSM_CAMI2C Chip */
+	init_waitqueue_head(&s5k3e2fx_wait_queue);
+	return 0;
+}
+
+static const struct i2c_device_id s5k3e2fx_i2c_id[] = {
+	{ "s5k3e2fx", 0},
+	{ }
+};
+
+static int s5k3e2fx_i2c_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	int rc = 0;
+	CDBG("s5k3e2fx_probe called!\n");
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		CDBG("i2c_check_functionality failed\n");
+		goto probe_failure;
+	}
+
+	s5k3e2fx_sensorw = kzalloc(sizeof(struct s5k3e2fx_work), GFP_KERNEL);
+	if (!s5k3e2fx_sensorw) {
+		CDBG("kzalloc failed.\n");
+		rc = -ENOMEM;
+		goto probe_failure;
+	}
+
+	i2c_set_clientdata(client, s5k3e2fx_sensorw);
+	s5k3e2fx_init_client(client);
+	s5k3e2fx_client = client;
+
+	mdelay(50);
+
+	CDBG("s5k3e2fx_probe successed! rc = %d\n", rc);
+	return 0;
+
+probe_failure:
+	CDBG("s5k3e2fx_probe failed! rc = %d\n", rc);
+	return rc;
+}
+
+static struct i2c_driver s5k3e2fx_i2c_driver = {
+	.id_table = s5k3e2fx_i2c_id,
+	.probe  = s5k3e2fx_i2c_probe,
+	.remove = __exit_p(s5k3e2fx_i2c_remove),
+	.driver = {
+		.name = "s5k3e2fx",
+	},
+};
+
+static int32_t s5k3e2fx_test(enum msm_s_test_mode mo)
+{
+	int32_t rc = 0;
+
+	if (mo == S_TEST_OFF)
+		rc = 0;
+	else
+		rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+			REG_TEST_PATTERN_MODE, (uint16_t)mo);
+
+	return rc;
+}
+
+static int32_t s5k3e2fx_setting(enum msm_s_reg_update rupdate,
+	enum msm_s_setting rt)
+{
+	int32_t rc = 0;
+  uint16_t num_lperf;
+
+	switch (rupdate) {
+	case S_UPDATE_PERIODIC:
+	if (rt == S_RES_PREVIEW || rt == S_RES_CAPTURE) {
+
+		struct s5k3e2fx_i2c_reg_conf tbl_1[] = {
+			{REG_CCP_DATA_FORMAT_MSB,
+				s5k3e2fx_reg_pat[rt].ccp_data_format_msb},
+			{REG_CCP_DATA_FORMAT_LSB,
+				s5k3e2fx_reg_pat[rt].ccp_data_format_lsb},
+			{REG_X_OUTPUT_SIZE_MSB,
+				s5k3e2fx_reg_pat[rt].x_output_size_msb},
+			{REG_X_OUTPUT_SIZE_LSB,
+				s5k3e2fx_reg_pat[rt].x_output_size_lsb},
+			{REG_Y_OUTPUT_SIZE_MSB,
+				s5k3e2fx_reg_pat[rt].y_output_size_msb},
+			{REG_Y_OUTPUT_SIZE_LSB,
+				s5k3e2fx_reg_pat[rt].y_output_size_lsb},
+			{REG_X_EVEN_INC,
+				s5k3e2fx_reg_pat[rt].x_even_inc},
+			{REG_X_ODD_INC,
+				s5k3e2fx_reg_pat[rt].x_odd_inc},
+			{REG_Y_EVEN_INC,
+				s5k3e2fx_reg_pat[rt].y_even_inc},
+			{REG_Y_ODD_INC,
+				s5k3e2fx_reg_pat[rt].y_odd_inc},
+			{REG_BINNING_ENABLE,
+				s5k3e2fx_reg_pat[rt].binning_enable},
+		};
+
+		struct s5k3e2fx_i2c_reg_conf tbl_2[] = {
+			{REG_FRAME_LENGTH_LINES_MSB, 0},
+			{REG_FRAME_LENGTH_LINES_LSB, 0},
+			{REG_LINE_LENGTH_PCK_MSB,
+				s5k3e2fx_reg_pat[rt].line_length_pck_msb},
+			{REG_LINE_LENGTH_PCK_LSB,
+				s5k3e2fx_reg_pat[rt].line_length_pck_lsb},
+			{REG_SHADE_CLK_ENABLE,
+				s5k3e2fx_reg_pat[rt].shade_clk_enable},
+			{REG_SEL_CCP, s5k3e2fx_reg_pat[rt].sel_ccp},
+			{REG_VPIX, s5k3e2fx_reg_pat[rt].vpix},
+			{REG_CLAMP_ON, s5k3e2fx_reg_pat[rt].clamp_on},
+			{REG_OFFSET, s5k3e2fx_reg_pat[rt].offset},
+			{REG_LD_START, s5k3e2fx_reg_pat[rt].ld_start},
+			{REG_LD_END, s5k3e2fx_reg_pat[rt].ld_end},
+			{REG_SL_START, s5k3e2fx_reg_pat[rt].sl_start},
+			{REG_SL_END, s5k3e2fx_reg_pat[rt].sl_end},
+			{REG_RX_START, s5k3e2fx_reg_pat[rt].rx_start},
+			{REG_S1_START, s5k3e2fx_reg_pat[rt].s1_start},
+			{REG_S1_END, s5k3e2fx_reg_pat[rt].s1_end},
+			{REG_S1S_START, s5k3e2fx_reg_pat[rt].s1s_start},
+			{REG_S1S_END, s5k3e2fx_reg_pat[rt].s1s_end},
+			{REG_S3_START, s5k3e2fx_reg_pat[rt].s3_start},
+			{REG_S3_END, s5k3e2fx_reg_pat[rt].s3_end},
+			{REG_CMP_EN_START, s5k3e2fx_reg_pat[rt].cmp_en_start},
+			{REG_CLP_SL_START, s5k3e2fx_reg_pat[rt].clp_sl_start},
+			{REG_CLP_SL_END, s5k3e2fx_reg_pat[rt].clp_sl_end},
+			{REG_OFF_START, s5k3e2fx_reg_pat[rt].off_start},
+			{REG_RMP_EN_START, s5k3e2fx_reg_pat[rt].rmp_en_start},
+			{REG_TX_START, s5k3e2fx_reg_pat[rt].tx_start},
+			{REG_TX_END, s5k3e2fx_reg_pat[rt].tx_end},
+			{REG_STX_WIDTH, s5k3e2fx_reg_pat[rt].stx_width},
+			{REG_3152_RESERVED,
+				s5k3e2fx_reg_pat[rt].reg_3152_reserved},
+			{REG_315A_RESERVED,
+				s5k3e2fx_reg_pat[rt].reg_315A_reserved},
+			{REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB,
+				s5k3e2fx_reg_pat[rt].
+				analogue_gain_code_global_msb},
+			{REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB,
+				s5k3e2fx_reg_pat[rt].
+				analogue_gain_code_global_lsb},
+			{REG_FINE_INTEGRATION_TIME,
+				s5k3e2fx_reg_pat[rt].fine_integration_time},
+			{REG_COARSE_INTEGRATION_TIME,
+				s5k3e2fx_reg_pat[rt].coarse_integration_time},
+			{S5K3E2FX_REG_MODE_SELECT, S5K3E2FX_MODE_SELECT_STREAM},
+		};
+
+		rc = s5k3e2fx_i2c_write_table(&tbl_1[0],
+			ARRAY_SIZE(tbl_1));
+		if (rc < 0)
+			return rc;
+
+		num_lperf = (uint16_t)
+			((s5k3e2fx_reg_pat[rt].frame_length_lines_msb << 8)
+			& 0xFF00)
+			+ s5k3e2fx_reg_pat[rt].frame_length_lines_lsb;
+
+		num_lperf = num_lperf * s5k3e2fx_ctrl->fps_divider / 0x0400;
+
+		tbl_2[0] = (struct s5k3e2fx_i2c_reg_conf)
+			{REG_FRAME_LENGTH_LINES_MSB, (num_lperf & 0xFF00) >> 8};
+		tbl_2[1] = (struct s5k3e2fx_i2c_reg_conf)
+			{REG_FRAME_LENGTH_LINES_LSB, (num_lperf & 0x00FF)};
+
+		rc = s5k3e2fx_i2c_write_table(&tbl_2[0],
+			ARRAY_SIZE(tbl_2));
+		if (rc < 0)
+			return rc;
+
+		mdelay(5);
+
+		rc = s5k3e2fx_test(s5k3e2fx_ctrl->set_test);
+		if (rc < 0)
+			return rc;
+	  }
+    break; /* UPDATE_PERIODIC */
+
+	case S_REG_INIT:
+	if (rt == S_RES_PREVIEW || rt == S_RES_CAPTURE) {
+
+		struct s5k3e2fx_i2c_reg_conf tbl_3[] = {
+			{S5K3E2FX_REG_SOFTWARE_RESET, S5K3E2FX_SOFTWARE_RESET},
+			{S5K3E2FX_REG_MODE_SELECT,
+				S5K3E2FX_MODE_SELECT_SW_STANDBY},
+			/* PLL setting */
+			{REG_PRE_PLL_CLK_DIV,
+				s5k3e2fx_reg_pat[rt].pre_pll_clk_div},
+			{REG_PLL_MULTIPLIER_MSB,
+				s5k3e2fx_reg_pat[rt].pll_multiplier_msb},
+			{REG_PLL_MULTIPLIER_LSB,
+				s5k3e2fx_reg_pat[rt].pll_multiplier_lsb},
+			{REG_VT_PIX_CLK_DIV,
+				s5k3e2fx_reg_pat[rt].vt_pix_clk_div},
+			{REG_VT_SYS_CLK_DIV,
+				s5k3e2fx_reg_pat[rt].vt_sys_clk_div},
+			{REG_OP_PIX_CLK_DIV,
+				s5k3e2fx_reg_pat[rt].op_pix_clk_div},
+			{REG_OP_SYS_CLK_DIV,
+				s5k3e2fx_reg_pat[rt].op_sys_clk_div},
+			/*Data Format */
+			{REG_CCP_DATA_FORMAT_MSB,
+				s5k3e2fx_reg_pat[rt].ccp_data_format_msb},
+			{REG_CCP_DATA_FORMAT_LSB,
+				s5k3e2fx_reg_pat[rt].ccp_data_format_lsb},
+			/*Output Size */
+			{REG_X_OUTPUT_SIZE_MSB,
+				s5k3e2fx_reg_pat[rt].x_output_size_msb},
+			{REG_X_OUTPUT_SIZE_LSB,
+				s5k3e2fx_reg_pat[rt].x_output_size_lsb},
+			{REG_Y_OUTPUT_SIZE_MSB,
+				s5k3e2fx_reg_pat[rt].y_output_size_msb},
+			{REG_Y_OUTPUT_SIZE_LSB,
+				s5k3e2fx_reg_pat[rt].y_output_size_lsb},
+			/* Binning */
+			{REG_X_EVEN_INC, s5k3e2fx_reg_pat[rt].x_even_inc},
+			{REG_X_ODD_INC, s5k3e2fx_reg_pat[rt].x_odd_inc },
+			{REG_Y_EVEN_INC, s5k3e2fx_reg_pat[rt].y_even_inc},
+			{REG_Y_ODD_INC, s5k3e2fx_reg_pat[rt].y_odd_inc},
+			{REG_BINNING_ENABLE,
+				s5k3e2fx_reg_pat[rt].binning_enable},
+			/* Frame format */
+			{REG_FRAME_LENGTH_LINES_MSB,
+				s5k3e2fx_reg_pat[rt].frame_length_lines_msb},
+			{REG_FRAME_LENGTH_LINES_LSB,
+				s5k3e2fx_reg_pat[rt].frame_length_lines_lsb},
+			{REG_LINE_LENGTH_PCK_MSB,
+				s5k3e2fx_reg_pat[rt].line_length_pck_msb},
+			{REG_LINE_LENGTH_PCK_LSB,
+				s5k3e2fx_reg_pat[rt].line_length_pck_lsb},
+			/* MSR setting */
+			{REG_SHADE_CLK_ENABLE,
+				s5k3e2fx_reg_pat[rt].shade_clk_enable},
+			{REG_SEL_CCP, s5k3e2fx_reg_pat[rt].sel_ccp},
+			{REG_VPIX, s5k3e2fx_reg_pat[rt].vpix},
+			{REG_CLAMP_ON, s5k3e2fx_reg_pat[rt].clamp_on},
+			{REG_OFFSET, s5k3e2fx_reg_pat[rt].offset},
+			/* CDS timing setting */
+			{REG_LD_START, s5k3e2fx_reg_pat[rt].ld_start},
+			{REG_LD_END, s5k3e2fx_reg_pat[rt].ld_end},
+			{REG_SL_START, s5k3e2fx_reg_pat[rt].sl_start},
+			{REG_SL_END, s5k3e2fx_reg_pat[rt].sl_end},
+			{REG_RX_START, s5k3e2fx_reg_pat[rt].rx_start},
+			{REG_S1_START, s5k3e2fx_reg_pat[rt].s1_start},
+			{REG_S1_END, s5k3e2fx_reg_pat[rt].s1_end},
+			{REG_S1S_START, s5k3e2fx_reg_pat[rt].s1s_start},
+			{REG_S1S_END, s5k3e2fx_reg_pat[rt].s1s_end},
+			{REG_S3_START, s5k3e2fx_reg_pat[rt].s3_start},
+			{REG_S3_END, s5k3e2fx_reg_pat[rt].s3_end},
+			{REG_CMP_EN_START, s5k3e2fx_reg_pat[rt].cmp_en_start},
+			{REG_CLP_SL_START, s5k3e2fx_reg_pat[rt].clp_sl_start},
+			{REG_CLP_SL_END, s5k3e2fx_reg_pat[rt].clp_sl_end},
+			{REG_OFF_START, s5k3e2fx_reg_pat[rt].off_start},
+			{REG_RMP_EN_START, s5k3e2fx_reg_pat[rt].rmp_en_start},
+			{REG_TX_START, s5k3e2fx_reg_pat[rt].tx_start},
+			{REG_TX_END, s5k3e2fx_reg_pat[rt].tx_end},
+			{REG_STX_WIDTH, s5k3e2fx_reg_pat[rt].stx_width},
+			{REG_3152_RESERVED,
+				s5k3e2fx_reg_pat[rt].reg_3152_reserved},
+			{REG_315A_RESERVED,
+				s5k3e2fx_reg_pat[rt].reg_315A_reserved},
+			{REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB,
+				s5k3e2fx_reg_pat[rt].
+				analogue_gain_code_global_msb},
+			{REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB,
+				s5k3e2fx_reg_pat[rt].
+				analogue_gain_code_global_lsb},
+			{REG_FINE_INTEGRATION_TIME,
+				s5k3e2fx_reg_pat[rt].fine_integration_time},
+			{REG_COARSE_INTEGRATION_TIME,
+				s5k3e2fx_reg_pat[rt].coarse_integration_time},
+			{S5K3E2FX_REG_MODE_SELECT, S5K3E2FX_MODE_SELECT_STREAM},
+		};
+
+		/* reset fps_divider */
+		s5k3e2fx_ctrl->fps_divider = 1 * 0x0400;
+		rc = s5k3e2fx_i2c_write_table(&tbl_3[0],
+			ARRAY_SIZE(tbl_3));
+		if (rc < 0)
+			return rc;
+		}
+		break; /* case REG_INIT: */
+
+	default:
+		rc = -EINVAL;
+		break;
+	} /* switch (rupdate) */
+
+	return rc;
+}
+
+static int s5k3e2fx_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+	int32_t  rc;
+
+	s5k3e2fx_ctrl = kzalloc(sizeof(struct s5k3e2fx_ctrl), GFP_KERNEL);
+	if (!s5k3e2fx_ctrl) {
+		CDBG("s5k3e2fx_init failed!\n");
+		rc = -ENOMEM;
+		goto init_done;
+	}
+
+	s5k3e2fx_ctrl->fps_divider = 1 * 0x00000400;
+	s5k3e2fx_ctrl->pict_fps_divider = 1 * 0x00000400;
+	s5k3e2fx_ctrl->set_test = S_TEST_OFF;
+	s5k3e2fx_ctrl->prev_res = S_QTR_SIZE;
+	s5k3e2fx_ctrl->pict_res = S_FULL_SIZE;
+
+	if (data)
+		s5k3e2fx_ctrl->sensordata = data;
+
+	/* enable mclk first */
+	msm_camio_clk_rate_set(24000000);
+	mdelay(20);
+
+	msm_camio_camif_pad_reg_reset();
+	mdelay(20);
+
+	rc = s5k3e2fx_probe_init_sensor(data);
+	if (rc < 0)
+		goto init_fail1;
+
+	if (s5k3e2fx_ctrl->prev_res == S_QTR_SIZE)
+		rc = s5k3e2fx_setting(S_REG_INIT, S_RES_PREVIEW);
+	else
+		rc = s5k3e2fx_setting(S_REG_INIT, S_RES_CAPTURE);
+
+	if (rc < 0) {
+		CDBG("s5k3e2fx_setting failed. rc = %d\n", rc);
+		goto init_fail1;
+	}
+
+	/* initialize AF */
+	rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+			0x3146, 0x3A);
+	if (rc < 0)
+		goto init_fail1;
+
+	rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+			0x3130, 0x03);
+	if (rc < 0)
+		goto init_fail1;
+
+	goto init_done;
+
+init_fail1:
+	s5k3e2fx_probe_init_done(data);
+	kfree(s5k3e2fx_ctrl);
+init_done:
+	return rc;
+}
+
+static int32_t s5k3e2fx_power_down(void)
+{
+	int32_t rc = 0;
+	return rc;
+}
+
+static int s5k3e2fx_sensor_release(void)
+{
+	int rc = -EBADF;
+
+	mutex_lock(&s5k3e2fx_mutex);
+
+	s5k3e2fx_power_down();
+
+	gpio_direction_output(s5k3e2fx_ctrl->sensordata->sensor_reset,
+		0);
+	gpio_free(s5k3e2fx_ctrl->sensordata->sensor_reset);
+
+	kfree(s5k3e2fx_ctrl);
+	s5k3e2fx_ctrl = NULL;
+
+	CDBG("s5k3e2fx_release completed\n");
+
+	mutex_unlock(&s5k3e2fx_mutex);
+	return rc;
+}
+
+static void s5k3e2fx_get_pict_fps(uint16_t fps, uint16_t *pfps)
+{
+	/* input fps is preview fps in Q8 format */
+	uint32_t divider;   /* Q10 */
+
+	divider = (uint32_t)
+		((s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h +
+			s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l) *
+		 (s5k3e2fx_reg_pat[S_RES_PREVIEW].size_w +
+			s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_p)) * 0x00000400 /
+		((s5k3e2fx_reg_pat[S_RES_CAPTURE].size_h +
+			s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_l) *
+		 (s5k3e2fx_reg_pat[S_RES_CAPTURE].size_w +
+			s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_p));
+
+	/* Verify PCLK settings and frame sizes. */
+	*pfps = (uint16_t)(fps * divider / 0x00000400);
+}
+
+static uint16_t s5k3e2fx_get_prev_lines_pf(void)
+{
+	return s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h +
+		s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l;
+}
+
+static uint16_t s5k3e2fx_get_prev_pixels_pl(void)
+{
+	return s5k3e2fx_reg_pat[S_RES_PREVIEW].size_w +
+		s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_p;
+}
+
+static uint16_t s5k3e2fx_get_pict_lines_pf(void)
+{
+	return s5k3e2fx_reg_pat[S_RES_CAPTURE].size_h +
+		s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_l;
+}
+
+static uint16_t s5k3e2fx_get_pict_pixels_pl(void)
+{
+	return s5k3e2fx_reg_pat[S_RES_CAPTURE].size_w +
+		s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_p;
+}
+
+static uint32_t s5k3e2fx_get_pict_max_exp_lc(void)
+{
+	uint32_t snapshot_lines_per_frame;
+
+	if (s5k3e2fx_ctrl->pict_res == S_QTR_SIZE)
+		snapshot_lines_per_frame =
+		s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h +
+		s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l;
+	else
+		snapshot_lines_per_frame = 3961 * 3;
+
+	return snapshot_lines_per_frame;
+}
+
+static int32_t s5k3e2fx_set_fps(struct fps_cfg *fps)
+{
+	/* input is new fps in Q10 format */
+	int32_t rc = 0;
+	enum msm_s_setting setting;
+
+	s5k3e2fx_ctrl->fps_divider = fps->fps_div;
+
+	if (s5k3e2fx_ctrl->sensormode == SENSOR_PREVIEW_MODE)
+		setting = S_RES_PREVIEW;
+	else
+		setting = S_RES_CAPTURE;
+
+  rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+		REG_FRAME_LENGTH_LINES_MSB,
+		(((s5k3e2fx_reg_pat[setting].size_h +
+			s5k3e2fx_reg_pat[setting].blk_l) *
+			s5k3e2fx_ctrl->fps_divider / 0x400) & 0xFF00) >> 8);
+	if (rc < 0)
+		goto set_fps_done;
+
+  rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+		REG_FRAME_LENGTH_LINES_LSB,
+		(((s5k3e2fx_reg_pat[setting].size_h +
+			s5k3e2fx_reg_pat[setting].blk_l) *
+			s5k3e2fx_ctrl->fps_divider / 0x400) & 0x00FF));
+
+set_fps_done:
+	return rc;
+}
+
+static int32_t s5k3e2fx_write_exp_gain(uint16_t gain, uint32_t line)
+{
+	int32_t rc = 0;
+
+	uint16_t max_legal_gain = 0x0200;
+	uint32_t ll_ratio; /* Q10 */
+	uint32_t ll_pck, fl_lines;
+	uint16_t offset = 4;
+	uint32_t  gain_msb, gain_lsb;
+	uint32_t  intg_t_msb, intg_t_lsb;
+	uint32_t  ll_pck_msb, ll_pck_lsb;
+
+	struct s5k3e2fx_i2c_reg_conf tbl[2];
+
+	CDBG("Line:%d s5k3e2fx_write_exp_gain \n", __LINE__);
+
+	if (s5k3e2fx_ctrl->sensormode == SENSOR_PREVIEW_MODE) {
+
+		s5k3e2fx_ctrl->my_reg_gain = gain;
+		s5k3e2fx_ctrl->my_reg_line_count = (uint16_t)line;
+
+		fl_lines = s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h +
+			s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l;
+
+		ll_pck = s5k3e2fx_reg_pat[S_RES_PREVIEW].size_w +
+			s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_p;
+
+	} else {
+
+		fl_lines = s5k3e2fx_reg_pat[S_RES_CAPTURE].size_h +
+			s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_l;
+
+		ll_pck = s5k3e2fx_reg_pat[S_RES_CAPTURE].size_w +
+			s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_p;
+	}
+
+	if (gain > max_legal_gain)
+		gain = max_legal_gain;
+
+	/* in Q10 */
+	line = (line * s5k3e2fx_ctrl->fps_divider);
+
+	if (fl_lines < (line / 0x400))
+		ll_ratio = (line / (fl_lines - offset));
+	else
+		ll_ratio = 0x400;
+
+	/* update gain registers */
+	gain_msb = (gain & 0xFF00) >> 8;
+	gain_lsb = gain & 0x00FF;
+	tbl[0].waddr = REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB;
+	tbl[0].bdata = gain_msb;
+	tbl[1].waddr = REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB;
+	tbl[1].bdata = gain_lsb;
+	rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl));
+	if (rc < 0)
+		goto write_gain_done;
+
+	ll_pck = ll_pck * ll_ratio;
+	ll_pck_msb = ((ll_pck / 0x400) & 0xFF00) >> 8;
+	ll_pck_lsb = (ll_pck / 0x400) & 0x00FF;
+	tbl[0].waddr = REG_LINE_LENGTH_PCK_MSB;
+	tbl[0].bdata = ll_pck_msb;
+	tbl[1].waddr = REG_LINE_LENGTH_PCK_LSB;
+	tbl[1].bdata = ll_pck_lsb;
+	rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl));
+	if (rc < 0)
+		goto write_gain_done;
+
+	line = line / ll_ratio;
+	intg_t_msb = (line & 0xFF00) >> 8;
+	intg_t_lsb = (line & 0x00FF);
+	tbl[0].waddr = REG_COARSE_INTEGRATION_TIME;
+	tbl[0].bdata = intg_t_msb;
+	tbl[1].waddr = REG_COARSE_INTEGRATION_TIME_LSB;
+	tbl[1].bdata = intg_t_lsb;
+	rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl));
+
+write_gain_done:
+	return rc;
+}
+
+static int32_t s5k3e2fx_set_pict_exp_gain(uint16_t gain, uint32_t line)
+{
+	int32_t rc = 0;
+
+	CDBG("Line:%d s5k3e2fx_set_pict_exp_gain \n", __LINE__);
+
+	rc =
+		s5k3e2fx_write_exp_gain(gain, line);
+
+	return rc;
+}
+
+static int32_t s5k3e2fx_video_config(int mode, int res)
+{
+	int32_t rc;
+
+	switch (res) {
+	case S_QTR_SIZE:
+		rc = s5k3e2fx_setting(S_UPDATE_PERIODIC, S_RES_PREVIEW);
+		if (rc < 0)
+			return rc;
+
+		CDBG("s5k3e2fx sensor configuration done!\n");
+		break;
+
+	case S_FULL_SIZE:
+		rc = s5k3e2fx_setting(S_UPDATE_PERIODIC, S_RES_CAPTURE);
+		if (rc < 0)
+			return rc;
+
+		break;
+
+	default:
+		return 0;
+	} /* switch */
+
+	s5k3e2fx_ctrl->prev_res = res;
+	s5k3e2fx_ctrl->curr_res = res;
+	s5k3e2fx_ctrl->sensormode = mode;
+
+	rc =
+		s5k3e2fx_write_exp_gain(s5k3e2fx_ctrl->my_reg_gain,
+			s5k3e2fx_ctrl->my_reg_line_count);
+
+	return rc;
+}
+
+static int32_t s5k3e2fx_snapshot_config(int mode)
+{
+	int32_t rc = 0;
+
+	rc = s5k3e2fx_setting(S_UPDATE_PERIODIC, S_RES_CAPTURE);
+	if (rc < 0)
+		return rc;
+
+	s5k3e2fx_ctrl->curr_res = s5k3e2fx_ctrl->pict_res;
+	s5k3e2fx_ctrl->sensormode = mode;
+
+	return rc;
+}
+
+static int32_t s5k3e2fx_raw_snapshot_config(int mode)
+{
+	int32_t rc = 0;
+
+	rc = s5k3e2fx_setting(S_UPDATE_PERIODIC, S_RES_CAPTURE);
+	if (rc < 0)
+		return rc;
+
+	s5k3e2fx_ctrl->curr_res = s5k3e2fx_ctrl->pict_res;
+	s5k3e2fx_ctrl->sensormode = mode;
+
+	return rc;
+}
+
+static int32_t s5k3e2fx_set_sensor_mode(int mode, int res)
+{
+	int32_t rc = 0;
+
+	switch (mode) {
+	case SENSOR_PREVIEW_MODE:
+		rc = s5k3e2fx_video_config(mode, res);
+		break;
+
+	case SENSOR_SNAPSHOT_MODE:
+		rc = s5k3e2fx_snapshot_config(mode);
+		break;
+
+	case SENSOR_RAW_SNAPSHOT_MODE:
+		rc = s5k3e2fx_raw_snapshot_config(mode);
+		break;
+
+	default:
+		rc = -EINVAL;
+		break;
+	}
+
+	return rc;
+}
+
+static int32_t s5k3e2fx_set_default_focus(void)
+{
+	int32_t rc = 0;
+
+  rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+		0x3131, 0);
+	if (rc < 0)
+		return rc;
+
+  rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+		0x3132, 0);
+	if (rc < 0)
+		return rc;
+
+	s5k3e2fx_ctrl->curr_lens_pos = 0;
+
+	return rc;
+}
+
+static int32_t s5k3e2fx_move_focus(int direction, int32_t num_steps)
+{
+	int32_t rc = 0;
+	int32_t i;
+	int16_t step_direction;
+	int16_t actual_step;
+	int16_t next_pos, pos_offset;
+	int16_t init_code = 50;
+	uint8_t next_pos_msb, next_pos_lsb;
+	int16_t s_move[5];
+	uint32_t gain; /* Q10 format */
+
+	if (direction == MOVE_NEAR)
+		step_direction = 20;
+	else if (direction == MOVE_FAR)
+		step_direction = -20;
+	else {
+		CDBG("s5k3e2fx_move_focus failed at line %d ...\n", __LINE__);
+		return -EINVAL;
+	}
+
+	actual_step = step_direction * (int16_t)num_steps;
+	pos_offset = init_code + s5k3e2fx_ctrl->curr_lens_pos;
+	gain = actual_step * 0x400 / 5;
+
+	for (i = 0; i <= 4; i++) {
+		if (actual_step >= 0)
+			s_move[i] = (((i+1)*gain+0x200)-(i*gain+0x200))/0x400;
+		else
+			s_move[i] = (((i+1)*gain-0x200)-(i*gain-0x200))/0x400;
+	}
+
+	/* Ring Damping Code */
+	for (i = 0; i <= 4; i++) {
+		next_pos = (int16_t)(pos_offset + s_move[i]);
+
+		if (next_pos > (738 + init_code))
+			next_pos = 738 + init_code;
+		else if (next_pos < 0)
+			next_pos = 0;
+
+		CDBG("next_position in damping mode = %d\n", next_pos);
+		/* Writing the Values to the actuator */
+		if (next_pos == init_code)
+			next_pos = 0x00;
+
+		next_pos_msb = next_pos >> 8;
+		next_pos_lsb = next_pos & 0x00FF;
+
+		rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+			0x3131, next_pos_msb);
+		if (rc < 0)
+			break;
+
+		rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+			0x3132, next_pos_lsb);
+		if (rc < 0)
+			break;
+
+		pos_offset = next_pos;
+		s5k3e2fx_ctrl->curr_lens_pos = pos_offset - init_code;
+		if (i < 4)
+			mdelay(3);
+	}
+
+	return rc;
+}
+
+static int s5k3e2fx_sensor_config(void __user *argp)
+{
+	struct sensor_cfg_data cdata;
+	long   rc = 0;
+
+	if (copy_from_user(&cdata,
+			(void *)argp,
+			sizeof(struct sensor_cfg_data)))
+		return -EFAULT;
+
+	mutex_lock(&s5k3e2fx_mutex);
+
+	CDBG("%s: cfgtype = %d\n", __func__, cdata.cfgtype);
+	switch (cdata.cfgtype) {
+	case CFG_GET_PICT_FPS:
+		s5k3e2fx_get_pict_fps(cdata.cfg.gfps.prevfps,
+			&(cdata.cfg.gfps.pictfps));
+
+		if (copy_to_user((void *)argp, &cdata,
+				sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PREV_L_PF:
+		cdata.cfg.prevl_pf = s5k3e2fx_get_prev_lines_pf();
+
+		if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PREV_P_PL:
+		cdata.cfg.prevp_pl = s5k3e2fx_get_prev_pixels_pl();
+
+		if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PICT_L_PF:
+		cdata.cfg.pictl_pf = s5k3e2fx_get_pict_lines_pf();
+
+		if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PICT_P_PL:
+		cdata.cfg.pictp_pl = s5k3e2fx_get_pict_pixels_pl();
+
+		if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PICT_MAX_EXP_LC:
+		cdata.cfg.pict_max_exp_lc =
+			s5k3e2fx_get_pict_max_exp_lc();
+
+		if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_SET_FPS:
+	case CFG_SET_PICT_FPS:
+		rc = s5k3e2fx_set_fps(&(cdata.cfg.fps));
+		break;
+
+	case CFG_SET_EXP_GAIN:
+		rc =
+			s5k3e2fx_write_exp_gain(cdata.cfg.exp_gain.gain,
+				cdata.cfg.exp_gain.line);
+		break;
+
+	case CFG_SET_PICT_EXP_GAIN:
+		CDBG("Line:%d CFG_SET_PICT_EXP_GAIN \n", __LINE__);
+		rc =
+			s5k3e2fx_set_pict_exp_gain(
+				cdata.cfg.exp_gain.gain,
+				cdata.cfg.exp_gain.line);
+		break;
+
+	case CFG_SET_MODE:
+		rc =
+			s5k3e2fx_set_sensor_mode(
+			cdata.mode, cdata.rs);
+		break;
+
+	case CFG_PWR_DOWN:
+		rc = s5k3e2fx_power_down();
+		break;
+
+	case CFG_MOVE_FOCUS:
+		rc =
+			s5k3e2fx_move_focus(
+			cdata.cfg.focus.dir,
+			cdata.cfg.focus.steps);
+		break;
+
+	case CFG_SET_DEFAULT_FOCUS:
+		rc =
+			s5k3e2fx_set_default_focus();
+		break;
+
+	case CFG_GET_AF_MAX_STEPS:
+	case CFG_SET_EFFECT:
+	case CFG_SET_LENS_SHADING:
+	default:
+		rc = -EINVAL;
+		break;
+	}
+
+	mutex_unlock(&s5k3e2fx_mutex);
+	return rc;
+}
+
+static int s5k3e2fx_sensor_probe(const struct msm_camera_sensor_info *info,
+		struct msm_sensor_ctrl *s)
+{
+	int rc = 0;
+
+	rc = i2c_add_driver(&s5k3e2fx_i2c_driver);
+	if (rc < 0 || s5k3e2fx_client == NULL) {
+		rc = -ENOTSUPP;
+		goto probe_fail;
+	}
+
+	msm_camio_clk_rate_set(24000000);
+	mdelay(20);
+
+	rc = s5k3e2fx_probe_init_sensor(info);
+	if (rc < 0)
+		goto probe_fail;
+
+	s->s_init = s5k3e2fx_sensor_open_init;
+	s->s_release = s5k3e2fx_sensor_release;
+	s->s_config  = s5k3e2fx_sensor_config;
+	s->s_mount_angle  = 0;
+	s5k3e2fx_probe_init_done(info);
+
+	return rc;
+
+probe_fail:
+	CDBG("SENSOR PROBE FAILS!\n");
+	return rc;
+}
+
+static int __s5k3e2fx_probe(struct platform_device *pdev)
+{
+	return msm_camera_drv_start(pdev, s5k3e2fx_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+	.probe = __s5k3e2fx_probe,
+	.driver = {
+		.name = "msm_camera_s5k3e2fx",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init s5k3e2fx_init(void)
+{
+	return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(s5k3e2fx_init);
+
diff --git a/drivers/media/video/msm/s5k3e2fx.h b/drivers/media/video/msm/s5k3e2fx.h
new file mode 100644
index 0000000..cf3f881
--- /dev/null
+++ b/drivers/media/video/msm/s5k3e2fx.h
@@ -0,0 +1,18 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef CAMSENSOR_S5K3E2FX
+#define CAMSENSOR_S5K3E2FX
+
+#include <mach/board.h>
+#endif /* CAMSENSOR_S5K3E2FX */
diff --git a/drivers/media/video/msm/s5k4e1.c b/drivers/media/video/msm/s5k4e1.c
new file mode 100644
index 0000000..30572d8
--- /dev/null
+++ b/drivers/media/video/msm/s5k4e1.c
@@ -0,0 +1,1103 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/bitops.h>
+#include <mach/camera.h>
+#include <media/msm_camera.h>
+#include "s5k4e1.h"
+
+/* 16bit address - 8 bit context register structure */
+#define Q8	0x00000100
+#define Q10	0x00000400
+
+/* MCLK */
+#define S5K4E1_MASTER_CLK_RATE 24000000
+
+/* AF Total steps parameters */
+#define S5K4E1_TOTAL_STEPS_NEAR_TO_FAR	32
+
+#define S5K4E1_REG_PREV_FRAME_LEN_1	31
+#define S5K4E1_REG_PREV_FRAME_LEN_2	32
+#define S5K4E1_REG_PREV_LINE_LEN_1	33
+#define S5K4E1_REG_PREV_LINE_LEN_2	34
+
+#define S5K4E1_REG_SNAP_FRAME_LEN_1	15
+#define S5K4E1_REG_SNAP_FRAME_LEN_2	16
+#define  S5K4E1_REG_SNAP_LINE_LEN_1	17
+#define S5K4E1_REG_SNAP_LINE_LEN_2	18
+#define MSB                             1
+#define LSB                             0
+
+struct s5k4e1_work_t {
+	struct work_struct work;
+};
+
+static struct s5k4e1_work_t *s5k4e1_sensorw;
+static struct s5k4e1_work_t *s5k4e1_af_sensorw;
+static struct i2c_client *s5k4e1_af_client;
+static struct i2c_client *s5k4e1_client;
+
+struct s5k4e1_ctrl_t {
+	const struct  msm_camera_sensor_info *sensordata;
+
+	uint32_t sensormode;
+	uint32_t fps_divider;/* init to 1 * 0x00000400 */
+	uint32_t pict_fps_divider;/* init to 1 * 0x00000400 */
+	uint16_t fps;
+
+	uint16_t curr_lens_pos;
+	uint16_t curr_step_pos;
+	uint16_t my_reg_gain;
+	uint32_t my_reg_line_count;
+	uint16_t total_lines_per_frame;
+
+	enum s5k4e1_resolution_t prev_res;
+	enum s5k4e1_resolution_t pict_res;
+	enum s5k4e1_resolution_t curr_res;
+	enum s5k4e1_test_mode_t  set_test;
+};
+
+static bool CSI_CONFIG;
+static struct s5k4e1_ctrl_t *s5k4e1_ctrl;
+
+static DECLARE_WAIT_QUEUE_HEAD(s5k4e1_wait_queue);
+static DECLARE_WAIT_QUEUE_HEAD(s5k4e1_af_wait_queue);
+DEFINE_MUTEX(s5k4e1_mut);
+
+static uint16_t prev_line_length_pck;
+static uint16_t prev_frame_length_lines;
+static uint16_t snap_line_length_pck;
+static uint16_t snap_frame_length_lines;
+
+static int s5k4e1_i2c_rxdata(unsigned short saddr,
+		unsigned char *rxdata, int length)
+{
+	struct i2c_msg msgs[] = {
+		{
+			.addr  = saddr,
+			.flags = 0,
+			.len   = 1,
+			.buf   = rxdata,
+		},
+		{
+			.addr  = saddr,
+			.flags = I2C_M_RD,
+			.len   = 1,
+			.buf   = rxdata,
+		},
+	};
+	if (i2c_transfer(s5k4e1_client->adapter, msgs, 2) < 0) {
+		CDBG("s5k4e1_i2c_rxdata faild 0x%x\n", saddr);
+		return -EIO;
+	}
+	return 0;
+}
+
+static int32_t s5k4e1_i2c_txdata(unsigned short saddr,
+		unsigned char *txdata, int length)
+{
+	struct i2c_msg msg[] = {
+		{
+			.addr = saddr,
+			.flags = 0,
+			.len = length,
+			.buf = txdata,
+		},
+	};
+	if (i2c_transfer(s5k4e1_client->adapter, msg, 1) < 0) {
+		CDBG("s5k4e1_i2c_txdata faild 0x%x\n", saddr);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int32_t s5k4e1_i2c_read(unsigned short raddr,
+		unsigned short *rdata, int rlen)
+{
+	int32_t rc = 0;
+	unsigned char buf[2];
+
+	if (!rdata)
+		return -EIO;
+
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (raddr & 0xFF00) >> 8;
+	buf[1] = (raddr & 0x00FF);
+	rc = s5k4e1_i2c_rxdata(s5k4e1_client->addr, buf, rlen);
+	if (rc < 0) {
+		CDBG("s5k4e1_i2c_read 0x%x failed!\n", raddr);
+		return rc;
+	}
+	*rdata = (rlen == 2 ? buf[0] << 8 | buf[1] : buf[0]);
+	CDBG("s5k4e1_i2c_read 0x%x val = 0x%x!\n", raddr, *rdata);
+
+	return rc;
+}
+
+static int32_t s5k4e1_i2c_write_b_sensor(unsigned short waddr, uint8_t bdata)
+{
+	int32_t rc = -EFAULT;
+	unsigned char buf[3];
+
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (waddr & 0xFF00) >> 8;
+	buf[1] = (waddr & 0x00FF);
+	buf[2] = bdata;
+	CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, bdata);
+	rc = s5k4e1_i2c_txdata(s5k4e1_client->addr, buf, 3);
+	if (rc < 0) {
+		CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+				waddr, bdata);
+	}
+	return rc;
+}
+
+static int32_t s5k4e1_i2c_write_b_table(struct s5k4e1_i2c_reg_conf const
+		*reg_conf_tbl, int num)
+{
+	int i;
+	int32_t rc = -EIO;
+
+	for (i = 0; i < num; i++) {
+		rc = s5k4e1_i2c_write_b_sensor(reg_conf_tbl->waddr,
+				reg_conf_tbl->wdata);
+		if (rc < 0)
+			break;
+		reg_conf_tbl++;
+	}
+	return rc;
+}
+
+static int32_t s5k4e1_af_i2c_txdata(unsigned short saddr,
+		unsigned char *txdata, int length)
+{
+	struct i2c_msg msg[] = {
+		{
+			.addr = saddr,
+			.flags = 0,
+			.len = length,
+			.buf = txdata,
+		},
+	};
+	if (i2c_transfer(s5k4e1_af_client->adapter, msg, 1) < 0) {
+		pr_err("s5k4e1_af_i2c_txdata faild 0x%x\n", saddr);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int32_t s5k4e1_af_i2c_write_b_sensor(uint8_t waddr, uint8_t bdata)
+{
+	int32_t rc = -EFAULT;
+	unsigned char buf[2];
+
+	memset(buf, 0, sizeof(buf));
+	buf[0] = waddr;
+	buf[1] = bdata;
+	CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, bdata);
+	rc = s5k4e1_af_i2c_txdata(s5k4e1_af_client->addr << 1, buf, 2);
+	if (rc < 0) {
+		pr_err("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+				waddr, bdata);
+	}
+	return rc;
+}
+
+static void s5k4e1_start_stream(void)
+{
+	s5k4e1_i2c_write_b_sensor(0x0100, 0x01);/* streaming on */
+}
+
+static void s5k4e1_stop_stream(void)
+{
+	s5k4e1_i2c_write_b_sensor(0x0100, 0x00);/* streaming off */
+}
+
+static void s5k4e1_group_hold_on(void)
+{
+	s5k4e1_i2c_write_b_sensor(0x0104, 0x01);
+}
+
+static void s5k4e1_group_hold_off(void)
+{
+	s5k4e1_i2c_write_b_sensor(0x0104, 0x0);
+}
+
+static void s5k4e1_get_pict_fps(uint16_t fps, uint16_t *pfps)
+{
+	/* input fps is preview fps in Q8 format */
+	uint32_t divider, d1, d2;
+
+	d1 = (prev_frame_length_lines * 0x00000400) / snap_frame_length_lines;
+	d2 = (prev_line_length_pck * 0x00000400) / snap_line_length_pck;
+	divider = (d1 * d2) / 0x400;
+
+	/*Verify PCLK settings and frame sizes.*/
+	*pfps = (uint16_t) (fps * divider / 0x400);
+}
+
+static uint16_t s5k4e1_get_prev_lines_pf(void)
+{
+	if (s5k4e1_ctrl->prev_res == QTR_SIZE)
+		return prev_frame_length_lines;
+	else
+		return snap_frame_length_lines;
+}
+
+static uint16_t s5k4e1_get_prev_pixels_pl(void)
+{
+	if (s5k4e1_ctrl->prev_res == QTR_SIZE)
+		return prev_line_length_pck;
+	else
+		return snap_line_length_pck;
+}
+
+static uint16_t s5k4e1_get_pict_lines_pf(void)
+{
+	if (s5k4e1_ctrl->pict_res == QTR_SIZE)
+		return prev_frame_length_lines;
+	else
+		return snap_frame_length_lines;
+}
+
+static uint16_t s5k4e1_get_pict_pixels_pl(void)
+{
+	if (s5k4e1_ctrl->pict_res == QTR_SIZE)
+		return prev_line_length_pck;
+	else
+		return snap_line_length_pck;
+}
+
+static uint32_t s5k4e1_get_pict_max_exp_lc(void)
+{
+	return snap_frame_length_lines * 24;
+}
+
+static int32_t s5k4e1_set_fps(struct fps_cfg   *fps)
+{
+	uint16_t total_lines_per_frame;
+	int32_t rc = 0;
+
+	s5k4e1_ctrl->fps_divider = fps->fps_div;
+	s5k4e1_ctrl->pict_fps_divider = fps->pict_fps_div;
+
+	if (s5k4e1_ctrl->sensormode == SENSOR_PREVIEW_MODE) {
+		total_lines_per_frame = (uint16_t)
+		((prev_frame_length_lines * s5k4e1_ctrl->fps_divider) / 0x400);
+	} else {
+		total_lines_per_frame = (uint16_t)
+		((snap_frame_length_lines * s5k4e1_ctrl->fps_divider) / 0x400);
+	}
+
+	s5k4e1_group_hold_on();
+	rc = s5k4e1_i2c_write_b_sensor(0x0340,
+			((total_lines_per_frame & 0xFF00) >> 8));
+	rc = s5k4e1_i2c_write_b_sensor(0x0341,
+			(total_lines_per_frame & 0x00FF));
+	s5k4e1_group_hold_off();
+
+	return rc;
+}
+
+static inline uint8_t s5k4e1_byte(uint16_t word, uint8_t offset)
+{
+	return word >> (offset * BITS_PER_BYTE);
+}
+
+static int32_t s5k4e1_write_exp_gain(uint16_t gain, uint32_t line)
+{
+	uint16_t max_legal_gain = 0x0200;
+	int32_t rc = 0;
+	static uint32_t fl_lines;
+
+	if (gain > max_legal_gain) {
+		pr_debug("Max legal gain Line:%d\n", __LINE__);
+		gain = max_legal_gain;
+	}
+	/* Analogue Gain */
+	s5k4e1_i2c_write_b_sensor(0x0204, s5k4e1_byte(gain, MSB));
+	s5k4e1_i2c_write_b_sensor(0x0205, s5k4e1_byte(gain, LSB));
+
+	if (line > (prev_frame_length_lines - 4)) {
+		fl_lines = line+4;
+		s5k4e1_group_hold_on();
+		s5k4e1_i2c_write_b_sensor(0x0340, s5k4e1_byte(fl_lines, MSB));
+		s5k4e1_i2c_write_b_sensor(0x0341, s5k4e1_byte(fl_lines, LSB));
+		/* Coarse Integration Time */
+		s5k4e1_i2c_write_b_sensor(0x0202, s5k4e1_byte(line, MSB));
+		s5k4e1_i2c_write_b_sensor(0x0203, s5k4e1_byte(line, LSB));
+		s5k4e1_group_hold_off();
+	} else if (line < (fl_lines - 4)) {
+		fl_lines = line+4;
+		if (fl_lines < prev_frame_length_lines)
+			fl_lines = prev_frame_length_lines;
+
+		s5k4e1_group_hold_on();
+		/* Coarse Integration Time */
+		s5k4e1_i2c_write_b_sensor(0x0202, s5k4e1_byte(line, MSB));
+		s5k4e1_i2c_write_b_sensor(0x0203, s5k4e1_byte(line, LSB));
+		s5k4e1_i2c_write_b_sensor(0x0340, s5k4e1_byte(fl_lines, MSB));
+		s5k4e1_i2c_write_b_sensor(0x0341, s5k4e1_byte(fl_lines, LSB));
+		s5k4e1_group_hold_off();
+	} else {
+		fl_lines = line+4;
+		s5k4e1_group_hold_on();
+		/* Coarse Integration Time */
+		s5k4e1_i2c_write_b_sensor(0x0202, s5k4e1_byte(line, MSB));
+		s5k4e1_i2c_write_b_sensor(0x0203, s5k4e1_byte(line, LSB));
+		s5k4e1_group_hold_off();
+	}
+	return rc;
+}
+
+static int32_t s5k4e1_set_pict_exp_gain(uint16_t gain, uint32_t line)
+{
+	uint16_t max_legal_gain = 0x0200;
+	uint16_t min_ll_pck = 0x0AB2;
+	uint32_t ll_pck, fl_lines;
+	uint32_t ll_ratio;
+	int32_t rc = 0;
+	uint8_t gain_msb, gain_lsb;
+	uint8_t intg_time_msb, intg_time_lsb;
+	uint8_t ll_pck_msb, ll_pck_lsb;
+
+	if (gain > max_legal_gain) {
+		pr_debug("Max legal gain Line:%d\n", __LINE__);
+		gain = max_legal_gain;
+	}
+
+	pr_debug("s5k4e1_write_exp_gain : gain = %d line = %d\n", gain, line);
+	line = (uint32_t) (line * s5k4e1_ctrl->pict_fps_divider);
+	fl_lines = snap_frame_length_lines;
+	ll_pck = snap_line_length_pck;
+
+	if (fl_lines < (line / 0x400))
+		ll_ratio = (line / (fl_lines - 4));
+	else
+		ll_ratio = 0x400;
+
+	ll_pck = ll_pck * ll_ratio / 0x400;
+	line = line / ll_ratio;
+	if (ll_pck < min_ll_pck)
+		ll_pck = min_ll_pck;
+
+	gain_msb = (uint8_t) ((gain & 0xFF00) >> 8);
+	gain_lsb = (uint8_t) (gain & 0x00FF);
+
+	intg_time_msb = (uint8_t) ((line & 0xFF00) >> 8);
+	intg_time_lsb = (uint8_t) (line & 0x00FF);
+
+	ll_pck_msb = (uint8_t) ((ll_pck & 0xFF00) >> 8);
+	ll_pck_lsb = (uint8_t) (ll_pck & 0x00FF);
+
+	s5k4e1_group_hold_on();
+	s5k4e1_i2c_write_b_sensor(0x0204, gain_msb); /* Analogue Gain */
+	s5k4e1_i2c_write_b_sensor(0x0205, gain_lsb);
+
+	s5k4e1_i2c_write_b_sensor(0x0342, ll_pck_msb);
+	s5k4e1_i2c_write_b_sensor(0x0343, ll_pck_lsb);
+
+	/* Coarse Integration Time */
+	s5k4e1_i2c_write_b_sensor(0x0202, intg_time_msb);
+	s5k4e1_i2c_write_b_sensor(0x0203, intg_time_lsb);
+	s5k4e1_group_hold_off();
+
+	return rc;
+}
+
+static int32_t s5k4e1_move_focus(int direction,
+		int32_t num_steps)
+{
+	int16_t step_direction, actual_step, next_position;
+	uint8_t code_val_msb, code_val_lsb;
+
+	if (direction == MOVE_NEAR)
+		step_direction = 16;
+	else
+		step_direction = -16;
+
+	actual_step = (int16_t) (step_direction * num_steps);
+	next_position = (int16_t) (s5k4e1_ctrl->curr_lens_pos + actual_step);
+
+	if (next_position > 1023)
+		next_position = 1023;
+	else if (next_position < 0)
+		next_position = 0;
+
+	code_val_msb = next_position >> 4;
+	code_val_lsb = (next_position & 0x000F) << 4;
+
+	if (s5k4e1_af_i2c_write_b_sensor(code_val_msb, code_val_lsb) < 0) {
+		pr_err("move_focus failed at line %d ...\n", __LINE__);
+		return -EBUSY;
+	}
+
+	s5k4e1_ctrl->curr_lens_pos = next_position;
+	return 0;
+}
+
+static int32_t s5k4e1_set_default_focus(uint8_t af_step)
+{
+	int32_t rc = 0;
+
+	if (s5k4e1_ctrl->curr_step_pos != 0) {
+		rc = s5k4e1_move_focus(MOVE_FAR,
+				s5k4e1_ctrl->curr_step_pos);
+	} else {
+		s5k4e1_af_i2c_write_b_sensor(0x00, 0x00);
+	}
+
+	s5k4e1_ctrl->curr_lens_pos = 0;
+	s5k4e1_ctrl->curr_step_pos = 0;
+
+	return rc;
+}
+
+static int32_t s5k4e1_test(enum s5k4e1_test_mode_t mo)
+{
+	int32_t rc = 0;
+
+	if (mo != TEST_OFF)
+		rc = s5k4e1_i2c_write_b_sensor(0x0601, (uint8_t) mo);
+
+	return rc;
+}
+
+static void s5k4e1_reset_sensor(void)
+{
+	s5k4e1_i2c_write_b_sensor(0x103, 0x1);
+}
+
+static int32_t s5k4e1_sensor_setting(int update_type, int rt)
+{
+
+	int32_t rc = 0;
+	struct msm_camera_csi_params s5k4e1_csi_params;
+
+	s5k4e1_stop_stream();
+	msleep(30);
+
+	if (update_type == REG_INIT) {
+		s5k4e1_reset_sensor();
+		s5k4e1_i2c_write_b_table(s5k4e1_regs.reg_mipi,
+				s5k4e1_regs.reg_mipi_size);
+		s5k4e1_i2c_write_b_table(s5k4e1_regs.rec_settings,
+				s5k4e1_regs.rec_size);
+		s5k4e1_i2c_write_b_table(s5k4e1_regs.reg_pll_p,
+				s5k4e1_regs.reg_pll_p_size);
+		CSI_CONFIG = 0;
+	} else if (update_type == UPDATE_PERIODIC) {
+		if (rt == RES_PREVIEW)
+			s5k4e1_i2c_write_b_table(s5k4e1_regs.reg_prev,
+					s5k4e1_regs.reg_prev_size);
+		else
+			s5k4e1_i2c_write_b_table(s5k4e1_regs.reg_snap,
+					s5k4e1_regs.reg_snap_size);
+		msleep(20);
+		if (!CSI_CONFIG) {
+			msm_camio_vfe_clk_rate_set(192000000);
+			s5k4e1_csi_params.data_format = CSI_10BIT;
+			s5k4e1_csi_params.lane_cnt = 1;
+			s5k4e1_csi_params.lane_assign = 0xe4;
+			s5k4e1_csi_params.dpcm_scheme = 0;
+			s5k4e1_csi_params.settle_cnt = 24;
+			rc = msm_camio_csi_config(&s5k4e1_csi_params);
+			msleep(20);
+			CSI_CONFIG = 1;
+		}
+		s5k4e1_start_stream();
+		msleep(30);
+	}
+	return rc;
+}
+
+static int32_t s5k4e1_video_config(int mode)
+{
+
+	int32_t rc = 0;
+	int rt;
+	CDBG("video config\n");
+	/* change sensor resolution if needed */
+	if (s5k4e1_ctrl->prev_res == QTR_SIZE)
+		rt = RES_PREVIEW;
+	else
+		rt = RES_CAPTURE;
+	if (s5k4e1_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+		return rc;
+	if (s5k4e1_ctrl->set_test) {
+		if (s5k4e1_test(s5k4e1_ctrl->set_test) < 0)
+			return  rc;
+	}
+
+	s5k4e1_ctrl->curr_res = s5k4e1_ctrl->prev_res;
+	s5k4e1_ctrl->sensormode = mode;
+	return rc;
+}
+
+static int32_t s5k4e1_snapshot_config(int mode)
+{
+	int32_t rc = 0;
+	int rt;
+
+	/*change sensor resolution if needed */
+	if (s5k4e1_ctrl->curr_res != s5k4e1_ctrl->pict_res) {
+		if (s5k4e1_ctrl->pict_res == QTR_SIZE)
+			rt = RES_PREVIEW;
+		else
+			rt = RES_CAPTURE;
+		if (s5k4e1_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+			return rc;
+	}
+
+	s5k4e1_ctrl->curr_res = s5k4e1_ctrl->pict_res;
+	s5k4e1_ctrl->sensormode = mode;
+	return rc;
+}
+
+static int32_t s5k4e1_raw_snapshot_config(int mode)
+{
+	int32_t rc = 0;
+	int rt;
+
+	/* change sensor resolution if needed */
+	if (s5k4e1_ctrl->curr_res != s5k4e1_ctrl->pict_res) {
+		if (s5k4e1_ctrl->pict_res == QTR_SIZE)
+			rt = RES_PREVIEW;
+		else
+			rt = RES_CAPTURE;
+		if (s5k4e1_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+			return rc;
+	}
+
+	s5k4e1_ctrl->curr_res = s5k4e1_ctrl->pict_res;
+	s5k4e1_ctrl->sensormode = mode;
+	return rc;
+}
+
+static int32_t s5k4e1_set_sensor_mode(int mode,
+		int res)
+{
+	int32_t rc = 0;
+
+	switch (mode) {
+	case SENSOR_PREVIEW_MODE:
+		rc = s5k4e1_video_config(mode);
+		break;
+	case SENSOR_SNAPSHOT_MODE:
+		rc = s5k4e1_snapshot_config(mode);
+		break;
+	case SENSOR_RAW_SNAPSHOT_MODE:
+		rc = s5k4e1_raw_snapshot_config(mode);
+		break;
+	default:
+		rc = -EINVAL;
+		break;
+	}
+	return rc;
+}
+
+static int32_t s5k4e1_power_down(void)
+{
+	s5k4e1_stop_stream();
+	return 0;
+}
+
+static int s5k4e1_probe_init_done(const struct msm_camera_sensor_info *data)
+{
+	CDBG("probe done\n");
+	gpio_free(data->sensor_reset);
+	return 0;
+}
+
+static int s5k4e1_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+	int32_t rc = 0;
+	uint16_t regaddress1 = 0x0000;
+	uint16_t regaddress2 = 0x0001;
+	uint16_t chipid1 = 0;
+	uint16_t chipid2 = 0;
+
+	CDBG("%s: %d\n", __func__, __LINE__);
+	CDBG(" s5k4e1_probe_init_sensor is called\n");
+
+	rc = gpio_request(data->sensor_reset, "s5k4e1");
+	CDBG(" s5k4e1_probe_init_sensor\n");
+	if (!rc) {
+		CDBG("sensor_reset = %d\n", rc);
+		gpio_direction_output(data->sensor_reset, 0);
+		msleep(50);
+		gpio_set_value_cansleep(data->sensor_reset, 1);
+		msleep(20);
+	} else
+		goto gpio_req_fail;
+
+	msleep(20);
+
+	s5k4e1_i2c_read(regaddress1, &chipid1, 1);
+	if (chipid1 != 0x4E) {
+		rc = -ENODEV;
+		CDBG("s5k4e1_probe_init_sensor fail chip id doesnot match\n");
+		goto init_probe_fail;
+	}
+
+	s5k4e1_i2c_read(regaddress2, &chipid2 , 1);
+	if (chipid2 != 0x10) {
+		rc = -ENODEV;
+		CDBG("s5k4e1_probe_init_sensor fail chip id doesnot match\n");
+		goto init_probe_fail;
+	}
+
+	CDBG("ID: %d\n", chipid1);
+	CDBG("ID: %d\n", chipid1);
+
+	return rc;
+
+init_probe_fail:
+	CDBG(" s5k4e1_probe_init_sensor fails\n");
+	gpio_set_value_cansleep(data->sensor_reset, 0);
+	s5k4e1_probe_init_done(data);
+	if (data->vcm_enable) {
+		int ret = gpio_request(data->vcm_pwd, "s5k4e1_af");
+		if (!ret) {
+			gpio_direction_output(data->vcm_pwd, 0);
+			msleep(20);
+			gpio_free(data->vcm_pwd);
+		}
+	}
+gpio_req_fail:
+	return rc;
+}
+
+int s5k4e1_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+	int32_t rc = 0;
+
+	CDBG("%s: %d\n", __func__, __LINE__);
+	CDBG("Calling s5k4e1_sensor_open_init\n");
+
+	s5k4e1_ctrl = kzalloc(sizeof(struct s5k4e1_ctrl_t), GFP_KERNEL);
+	if (!s5k4e1_ctrl) {
+		CDBG("s5k4e1_init failed!\n");
+		rc = -ENOMEM;
+		goto init_done;
+	}
+	s5k4e1_ctrl->fps_divider = 1 * 0x00000400;
+	s5k4e1_ctrl->pict_fps_divider = 1 * 0x00000400;
+	s5k4e1_ctrl->set_test = TEST_OFF;
+	s5k4e1_ctrl->prev_res = QTR_SIZE;
+	s5k4e1_ctrl->pict_res = FULL_SIZE;
+
+	if (data)
+		s5k4e1_ctrl->sensordata = data;
+
+	prev_frame_length_lines =
+	((s5k4e1_regs.reg_prev[S5K4E1_REG_PREV_FRAME_LEN_1].wdata << 8) |
+		s5k4e1_regs.reg_prev[S5K4E1_REG_PREV_FRAME_LEN_2].wdata);
+
+	prev_line_length_pck =
+	(s5k4e1_regs.reg_prev[S5K4E1_REG_PREV_LINE_LEN_1].wdata << 8) |
+		s5k4e1_regs.reg_prev[S5K4E1_REG_PREV_LINE_LEN_2].wdata;
+
+	snap_frame_length_lines =
+	(s5k4e1_regs.reg_snap[S5K4E1_REG_SNAP_FRAME_LEN_1].wdata << 8) |
+		s5k4e1_regs.reg_snap[S5K4E1_REG_SNAP_FRAME_LEN_2].wdata;
+
+	snap_line_length_pck =
+	(s5k4e1_regs.reg_snap[S5K4E1_REG_SNAP_LINE_LEN_1].wdata << 8) |
+		s5k4e1_regs.reg_snap[S5K4E1_REG_SNAP_LINE_LEN_1].wdata;
+
+	/* enable mclk first */
+	msm_camio_clk_rate_set(S5K4E1_MASTER_CLK_RATE);
+	rc = s5k4e1_probe_init_sensor(data);
+	if (rc < 0)
+		goto init_fail;
+
+	CDBG("init settings\n");
+	if (s5k4e1_ctrl->prev_res == QTR_SIZE)
+		rc = s5k4e1_sensor_setting(REG_INIT, RES_PREVIEW);
+	else
+		rc = s5k4e1_sensor_setting(REG_INIT, RES_CAPTURE);
+	s5k4e1_ctrl->fps = 30 * Q8;
+
+	/* enable AF actuator */
+	if (s5k4e1_ctrl->sensordata->vcm_enable) {
+		CDBG("enable AF actuator, gpio = %d\n",
+			 s5k4e1_ctrl->sensordata->vcm_pwd);
+		rc = gpio_request(s5k4e1_ctrl->sensordata->vcm_pwd,
+						"s5k4e1_af");
+		if (!rc)
+			gpio_direction_output(
+				s5k4e1_ctrl->sensordata->vcm_pwd,
+				 1);
+		else {
+			pr_err("s5k4e1_ctrl gpio request failed!\n");
+			goto init_fail;
+		}
+		msleep(20);
+		rc = s5k4e1_set_default_focus(0);
+		if (rc < 0) {
+			gpio_direction_output(s5k4e1_ctrl->sensordata->vcm_pwd,
+								0);
+			gpio_free(s5k4e1_ctrl->sensordata->vcm_pwd);
+		}
+	}
+	if (rc < 0)
+		goto init_fail;
+	else
+		goto init_done;
+init_fail:
+	CDBG("init_fail\n");
+	s5k4e1_probe_init_done(data);
+init_done:
+	CDBG("init_done\n");
+	return rc;
+}
+
+static int s5k4e1_init_client(struct i2c_client *client)
+{
+	/* Initialize the MSM_CAMI2C Chip */
+	init_waitqueue_head(&s5k4e1_wait_queue);
+	return 0;
+}
+
+static int s5k4e1_af_init_client(struct i2c_client *client)
+{
+	/* Initialize the MSM_CAMI2C Chip */
+	init_waitqueue_head(&s5k4e1_af_wait_queue);
+	return 0;
+}
+
+static const struct i2c_device_id s5k4e1_af_i2c_id[] = {
+	{"s5k4e1_af", 0},
+	{ }
+};
+
+static int s5k4e1_af_i2c_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	int rc = 0;
+	CDBG("s5k4e1_af_probe called!\n");
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		CDBG("i2c_check_functionality failed\n");
+		goto probe_failure;
+	}
+
+	s5k4e1_af_sensorw = kzalloc(sizeof(struct s5k4e1_work_t), GFP_KERNEL);
+	if (!s5k4e1_af_sensorw) {
+		CDBG("kzalloc failed.\n");
+		rc = -ENOMEM;
+		goto probe_failure;
+	}
+
+	i2c_set_clientdata(client, s5k4e1_af_sensorw);
+	s5k4e1_af_init_client(client);
+	s5k4e1_af_client = client;
+
+	msleep(50);
+
+	CDBG("s5k4e1_af_probe successed! rc = %d\n", rc);
+	return 0;
+
+probe_failure:
+	CDBG("s5k4e1_af_probe failed! rc = %d\n", rc);
+	return rc;
+}
+
+static const struct i2c_device_id s5k4e1_i2c_id[] = {
+	{"s5k4e1", 0},
+	{ }
+};
+
+static int s5k4e1_i2c_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	int rc = 0;
+	CDBG("s5k4e1_probe called!\n");
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		CDBG("i2c_check_functionality failed\n");
+		goto probe_failure;
+	}
+
+	s5k4e1_sensorw = kzalloc(sizeof(struct s5k4e1_work_t), GFP_KERNEL);
+	if (!s5k4e1_sensorw) {
+		CDBG("kzalloc failed.\n");
+		rc = -ENOMEM;
+		goto probe_failure;
+	}
+
+	i2c_set_clientdata(client, s5k4e1_sensorw);
+	s5k4e1_init_client(client);
+	s5k4e1_client = client;
+
+	msleep(50);
+
+	CDBG("s5k4e1_probe successed! rc = %d\n", rc);
+	return 0;
+
+probe_failure:
+	CDBG("s5k4e1_probe failed! rc = %d\n", rc);
+	return rc;
+}
+
+static int __devexit s5k4e1_remove(struct i2c_client *client)
+{
+	struct s5k4e1_work_t *sensorw = i2c_get_clientdata(client);
+	free_irq(client->irq, sensorw);
+	s5k4e1_client = NULL;
+	kfree(sensorw);
+	return 0;
+}
+
+static int __devexit s5k4e1_af_remove(struct i2c_client *client)
+{
+	struct s5k4e1_work_t *s5k4e1_af = i2c_get_clientdata(client);
+	free_irq(client->irq, s5k4e1_af);
+	s5k4e1_af_client = NULL;
+	kfree(s5k4e1_af);
+	return 0;
+}
+
+static struct i2c_driver s5k4e1_i2c_driver = {
+	.id_table = s5k4e1_i2c_id,
+	.probe  = s5k4e1_i2c_probe,
+	.remove = __exit_p(s5k4e1_i2c_remove),
+	.driver = {
+		.name = "s5k4e1",
+	},
+};
+
+static struct i2c_driver s5k4e1_af_i2c_driver = {
+	.id_table = s5k4e1_af_i2c_id,
+	.probe  = s5k4e1_af_i2c_probe,
+	.remove = __exit_p(s5k4e1_af_i2c_remove),
+	.driver = {
+		.name = "s5k4e1_af",
+	},
+};
+
+int s5k4e1_sensor_config(void __user *argp)
+{
+	struct sensor_cfg_data cdata;
+	long   rc = 0;
+	if (copy_from_user(&cdata,
+				(void *)argp,
+				sizeof(struct sensor_cfg_data)))
+		return -EFAULT;
+	mutex_lock(&s5k4e1_mut);
+	CDBG("s5k4e1_sensor_config: cfgtype = %d\n",
+			cdata.cfgtype);
+	switch (cdata.cfgtype) {
+	case CFG_GET_PICT_FPS:
+		s5k4e1_get_pict_fps(
+			cdata.cfg.gfps.prevfps,
+			&(cdata.cfg.gfps.pictfps));
+
+		if (copy_to_user((void *)argp,
+			&cdata,
+			sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+	case CFG_GET_PREV_L_PF:
+		cdata.cfg.prevl_pf =
+			s5k4e1_get_prev_lines_pf();
+
+		if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+	case CFG_GET_PREV_P_PL:
+		cdata.cfg.prevp_pl =
+			s5k4e1_get_prev_pixels_pl();
+
+		if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+	case CFG_GET_PICT_L_PF:
+		cdata.cfg.pictl_pf =
+			s5k4e1_get_pict_lines_pf();
+
+		if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+	case CFG_GET_PICT_P_PL:
+		cdata.cfg.pictp_pl =
+			s5k4e1_get_pict_pixels_pl();
+		if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+	case CFG_GET_PICT_MAX_EXP_LC:
+		cdata.cfg.pict_max_exp_lc =
+			s5k4e1_get_pict_max_exp_lc();
+
+		if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+	case CFG_SET_FPS:
+	case CFG_SET_PICT_FPS:
+		rc = s5k4e1_set_fps(&(cdata.cfg.fps));
+		break;
+	case CFG_SET_EXP_GAIN:
+		rc = s5k4e1_write_exp_gain(cdata.cfg.exp_gain.gain,
+				cdata.cfg.exp_gain.line);
+		break;
+	case CFG_SET_PICT_EXP_GAIN:
+		rc = s5k4e1_set_pict_exp_gain(cdata.cfg.exp_gain.gain,
+				cdata.cfg.exp_gain.line);
+		break;
+	case CFG_SET_MODE:
+		rc = s5k4e1_set_sensor_mode(cdata.mode, cdata.rs);
+		break;
+	case CFG_PWR_DOWN:
+		rc = s5k4e1_power_down();
+		break;
+	case CFG_MOVE_FOCUS:
+		rc = s5k4e1_move_focus(cdata.cfg.focus.dir,
+				cdata.cfg.focus.steps);
+		break;
+	case CFG_SET_DEFAULT_FOCUS:
+		rc = s5k4e1_set_default_focus(cdata.cfg.focus.steps);
+		break;
+	case CFG_GET_AF_MAX_STEPS:
+		cdata.max_steps = S5K4E1_TOTAL_STEPS_NEAR_TO_FAR;
+		if (copy_to_user((void *)argp,
+					&cdata,
+				sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+	case CFG_SET_EFFECT:
+		rc = s5k4e1_set_default_focus(cdata.cfg.effect);
+		break;
+	default:
+		rc = -EFAULT;
+		break;
+	}
+	mutex_unlock(&s5k4e1_mut);
+
+	return rc;
+}
+
+static int s5k4e1_sensor_release(void)
+{
+	int rc = -EBADF;
+
+	mutex_lock(&s5k4e1_mut);
+	s5k4e1_power_down();
+	msleep(20);
+	gpio_set_value_cansleep(s5k4e1_ctrl->sensordata->sensor_reset, 0);
+	usleep_range(5000, 5100);
+	gpio_free(s5k4e1_ctrl->sensordata->sensor_reset);
+	if (s5k4e1_ctrl->sensordata->vcm_enable) {
+		gpio_set_value_cansleep(s5k4e1_ctrl->sensordata->vcm_pwd, 0);
+		gpio_free(s5k4e1_ctrl->sensordata->vcm_pwd);
+	}
+	kfree(s5k4e1_ctrl);
+	s5k4e1_ctrl = NULL;
+	CDBG("s5k4e1_release completed\n");
+	mutex_unlock(&s5k4e1_mut);
+
+	return rc;
+}
+
+static int s5k4e1_sensor_probe(const struct msm_camera_sensor_info *info,
+		struct msm_sensor_ctrl *s)
+{
+	int rc = 0;
+
+	rc = i2c_add_driver(&s5k4e1_i2c_driver);
+	if (rc < 0 || s5k4e1_client == NULL) {
+		rc = -ENOTSUPP;
+		CDBG("I2C add driver failed");
+		goto probe_fail_1;
+	}
+
+	rc = i2c_add_driver(&s5k4e1_af_i2c_driver);
+	if (rc < 0 || s5k4e1_af_client == NULL) {
+		rc = -ENOTSUPP;
+		CDBG("I2C add driver failed");
+		goto probe_fail_2;
+	}
+
+	msm_camio_clk_rate_set(S5K4E1_MASTER_CLK_RATE);
+
+	rc = s5k4e1_probe_init_sensor(info);
+	if (rc < 0)
+		goto probe_fail_3;
+
+	s->s_init = s5k4e1_sensor_open_init;
+	s->s_release = s5k4e1_sensor_release;
+	s->s_config  = s5k4e1_sensor_config;
+	s->s_mount_angle = info->sensor_platform_info->mount_angle;
+	gpio_set_value_cansleep(info->sensor_reset, 0);
+	s5k4e1_probe_init_done(info);
+	/* Keep vcm_pwd to OUT Low */
+	if (info->vcm_enable) {
+		rc = gpio_request(info->vcm_pwd, "s5k4e1_af");
+		if (!rc) {
+			gpio_direction_output(info->vcm_pwd, 0);
+			msleep(20);
+			gpio_free(info->vcm_pwd);
+		} else
+			return rc;
+	}
+	return rc;
+
+probe_fail_3:
+	i2c_del_driver(&s5k4e1_af_i2c_driver);
+probe_fail_2:
+	i2c_del_driver(&s5k4e1_i2c_driver);
+probe_fail_1:
+	CDBG("s5k4e1_sensor_probe: SENSOR PROBE FAILS!\n");
+	return rc;
+}
+
+static int __devinit s5k4e1_probe(struct platform_device *pdev)
+{
+	return msm_camera_drv_start(pdev, s5k4e1_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+	.probe = s5k4e1_probe,
+	.driver = {
+		.name = "msm_camera_s5k4e1",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init s5k4e1_init(void)
+{
+	return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(s5k4e1_init);
+MODULE_DESCRIPTION("Samsung 5 MP Bayer sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/s5k4e1.h b/drivers/media/video/msm/s5k4e1.h
new file mode 100644
index 0000000..7f60332
--- /dev/null
+++ b/drivers/media/video/msm/s5k4e1.h
@@ -0,0 +1,94 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef S5K4E1_H
+#define S5K4E1_H
+#include <linux/types.h>
+#include <mach/board.h>
+extern struct s5k4e1_reg s5k4e1_regs;
+
+struct s5k4e1_i2c_reg_conf {
+	unsigned short waddr;
+	unsigned short wdata;
+};
+
+enum s5k4e1_test_mode_t {
+	TEST_OFF,
+	TEST_1,
+	TEST_2,
+	TEST_3
+};
+
+enum s5k4e1_resolution_t {
+	QTR_SIZE,
+	FULL_SIZE,
+	INVALID_SIZE
+};
+enum s5k4e1_setting {
+	RES_PREVIEW,
+	RES_CAPTURE
+};
+enum s5k4e1_reg_update {
+	/* Sensor egisters that need to be updated during initialization */
+	REG_INIT,
+	/* Sensor egisters that needs periodic I2C writes */
+	UPDATE_PERIODIC,
+	/* All the sensor Registers will be updated */
+	UPDATE_ALL,
+	/* Not valid update */
+	UPDATE_INVALID
+};
+
+enum s5k4e1_reg_pll {
+	E013_VT_PIX_CLK_DIV,
+	E013_VT_SYS_CLK_DIV,
+	E013_PRE_PLL_CLK_DIV,
+	E013_PLL_MULTIPLIER,
+	E013_OP_PIX_CLK_DIV,
+	E013_OP_SYS_CLK_DIV
+};
+
+enum s5k4e1_reg_mode {
+	E013_X_ADDR_START,
+	E013_X_ADDR_END,
+	E013_Y_ADDR_START,
+	E013_Y_ADDR_END,
+	E013_X_OUTPUT_SIZE,
+	E013_Y_OUTPUT_SIZE,
+	E013_DATAPATH_SELECT,
+	E013_READ_MODE,
+	E013_ANALOG_CONTROL5,
+	E013_DAC_LD_4_5,
+	E013_SCALING_MODE,
+	E013_SCALE_M,
+	E013_LINE_LENGTH_PCK,
+	E013_FRAME_LENGTH_LINES,
+	E013_COARSE_INTEGRATION_TIME,
+	E013_FINE_INTEGRATION_TIME,
+	E013_FINE_CORRECTION
+};
+
+struct s5k4e1_reg {
+	const struct s5k4e1_i2c_reg_conf *reg_mipi;
+	const unsigned short reg_mipi_size;
+	const struct s5k4e1_i2c_reg_conf *rec_settings;
+	const unsigned short rec_size;
+	const struct s5k4e1_i2c_reg_conf *reg_pll_p;
+	const unsigned short reg_pll_p_size;
+	const struct s5k4e1_i2c_reg_conf *reg_pll_s;
+	const unsigned short reg_pll_s_size;
+	const struct s5k4e1_i2c_reg_conf *reg_prev;
+	const unsigned short reg_prev_size;
+	const struct s5k4e1_i2c_reg_conf *reg_snap;
+	const unsigned short reg_snap_size;
+};
+#endif /* S5K4E1_H */
diff --git a/drivers/media/video/msm/s5k4e1_reg.c b/drivers/media/video/msm/s5k4e1_reg.c
new file mode 100644
index 0000000..59bb1c8
--- /dev/null
+++ b/drivers/media/video/msm/s5k4e1_reg.c
@@ -0,0 +1,169 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+#include "s5k4e1.h"
+
+struct s5k4e1_i2c_reg_conf s5k4e1_mipi_settings[] = {
+	{0x30BD, 0x00},/* SEL_CCP[0] */
+	{0x3084, 0x15},/* SYNC Mode */
+	{0x30BE, 0x1A},/* M_PCLKDIV_AUTO[4], M_DIV_PCLK[3:0] */
+	{0x30C1, 0x01},/* pack video enable [0] */
+	{0x30EE, 0x02},/* DPHY enable [ 1] */
+	{0x3111, 0x86},/* Embedded data off [5] */
+};
+
+/* PLL Configuration */
+struct s5k4e1_i2c_reg_conf s5k4e1_pll_preview_settings[] = {
+	{0x0305, 0x04},
+	{0x0306, 0x00},
+	{0x0307, 0x44},
+	{0x30B5, 0x00},
+	{0x30E2, 0x01},/* num lanes[1:0] = 2 */
+	{0x30F1, 0xB0},
+};
+
+struct s5k4e1_i2c_reg_conf s5k4e1_pll_snap_settings[] = {
+	{0x0305, 0x04},
+	{0x0306, 0x00},
+	{0x0307, 0x44},
+	{0x30B5, 0x00},
+	{0x30E2, 0x01},/* num lanes[1:0] = 2 */
+	{0x30F1, 0xB0},
+};
+
+struct s5k4e1_i2c_reg_conf s5k4e1_prev_settings[] = {
+	/* output size (1304 x 980) */
+	{0x30A9, 0x02},/* Horizontal Binning On */
+	{0x300E, 0xEB},/* Vertical Binning On */
+	{0x0387, 0x03},/* y_odd_inc 03(10b AVG) */
+	{0x0344, 0x00},/* x_addr_start 0 */
+	{0x0345, 0x00},
+	{0x0348, 0x0A},/* x_addr_end 2607 */
+	{0x0349, 0x2F},
+	{0x0346, 0x00},/* y_addr_start 0 */
+	{0x0347, 0x00},
+	{0x034A, 0x07},/* y_addr_end 1959 */
+	{0x034B, 0xA7},
+	{0x0380, 0x00},/* x_even_inc 1 */
+	{0x0381, 0x01},
+	{0x0382, 0x00},/* x_odd_inc 1 */
+	{0x0383, 0x01},
+	{0x0384, 0x00},/* y_even_inc 1 */
+	{0x0385, 0x01},
+	{0x0386, 0x00},/* y_odd_inc 3 */
+	{0x0387, 0x03},
+	{0x034C, 0x05},/* x_output_size 1304 */
+	{0x034D, 0x18},
+	{0x034E, 0x03},/* y_output_size 980 */
+	{0x034F, 0xd4},
+	{0x30BF, 0xAB},/* outif_enable[7], data_type[5:0](2Bh = bayer 10bit} */
+	{0x30C0, 0xA0},/* video_offset[7:4] 3260%12 */
+	{0x30C8, 0x06},/* video_data_length 1600 = 1304 * 1.25 */
+	{0x30C9, 0x5E},
+	/* Timing Configuration */
+	{0x0202, 0x03},
+	{0x0203, 0x14},
+	{0x0204, 0x00},
+	{0x0205, 0x80},
+	{0x0340, 0x03},/* Frame Length */
+	{0x0341, 0xE0},
+	{0x0342, 0x0A},/* 2738  Line Length */
+	{0x0343, 0xB2},
+};
+
+struct s5k4e1_i2c_reg_conf s5k4e1_snap_settings[] = {
+	/*Output Size (2608x1960)*/
+	{0x30A9, 0x03},/* Horizontal Binning Off */
+	{0x300E, 0xE8},/* Vertical Binning Off */
+	{0x0387, 0x01},/* y_odd_inc */
+	{0x034C, 0x0A},/* x_output size */
+	{0x034D, 0x30},
+	{0x034E, 0x07},/* y_output size */
+	{0x034F, 0xA8},
+	{0x30BF, 0xAB},/* outif_enable[7], data_type[5:0](2Bh = bayer 10bit} */
+	{0x30C0, 0x80},/* video_offset[7:4] 3260%12 */
+	{0x30C8, 0x0C},/* video_data_length 3260 = 2608 * 1.25 */
+	{0x30C9, 0xBC},
+	/*Timing configuration*/
+	{0x0202, 0x06},
+	{0x0203, 0x28},
+	{0x0204, 0x00},
+	{0x0205, 0x80},
+	{0x0340, 0x07},/* Frame Length */
+	{0x0341, 0xB4},
+	{0x0342, 0x0A},/* 2738 Line Length */
+	{0x0343, 0xB2},
+};
+
+struct s5k4e1_i2c_reg_conf s5k4e1_recommend_settings[] = {
+	/*CDS timing setting ... */
+	{0x3000, 0x05},
+	{0x3001, 0x03},
+	{0x3002, 0x08},
+	{0x3003, 0x0A},
+	{0x3004, 0x50},
+	{0x3005, 0x0E},
+	{0x3006, 0x5E},
+	{0x3007, 0x00},
+	{0x3008, 0x78},
+	{0x3009, 0x78},
+	{0x300A, 0x50},
+	{0x300B, 0x08},
+	{0x300C, 0x14},
+	{0x300D, 0x00},
+	{0x300E, 0xE8},
+	{0x300F, 0x82},
+	{0x301B, 0x77},
+
+	/* CDS option setting ... */
+	{0x3010, 0x00},
+	{0x3011, 0x3A},
+	{0x3029, 0x04},
+	{0x3012, 0x30},
+	{0x3013, 0xA0},
+	{0x3014, 0x00},
+	{0x3015, 0x00},
+	{0x3016, 0x30},
+	{0x3017, 0x94},
+	{0x3018, 0x70},
+	{0x301D, 0xD4},
+	{0x3021, 0x02},
+	{0x3022, 0x24},
+	{0x3024, 0x40},
+	{0x3027, 0x08},
+
+	/* Pixel option setting ...   */
+	{0x301C, 0x04},
+	{0x30D8, 0x3F},
+	{0x302B, 0x01},
+
+	{0x3070, 0x5F},
+	{0x3071, 0x00},
+	{0x3080, 0x04},
+	{0x3081, 0x38},
+};
+
+struct s5k4e1_reg s5k4e1_regs = {
+	.reg_mipi = &s5k4e1_mipi_settings[0],
+	.reg_mipi_size = ARRAY_SIZE(s5k4e1_mipi_settings),
+	.rec_settings = &s5k4e1_recommend_settings[0],
+	.rec_size = ARRAY_SIZE(s5k4e1_recommend_settings),
+	.reg_pll_p = &s5k4e1_pll_preview_settings[0],
+	.reg_pll_p_size = ARRAY_SIZE(s5k4e1_pll_preview_settings),
+	.reg_pll_s = &s5k4e1_pll_snap_settings[0],
+	.reg_pll_s_size = ARRAY_SIZE(s5k4e1_pll_snap_settings),
+	.reg_prev = &s5k4e1_prev_settings[0],
+	.reg_prev_size = ARRAY_SIZE(s5k4e1_prev_settings),
+	.reg_snap = &s5k4e1_snap_settings[0],
+	.reg_snap_size = ARRAY_SIZE(s5k4e1_snap_settings),
+};
diff --git a/drivers/media/video/msm/sensors/Makefile b/drivers/media/video/msm/sensors/Makefile
new file mode 100644
index 0000000..ea36bf6
--- /dev/null
+++ b/drivers/media/video/msm/sensors/Makefile
@@ -0,0 +1,17 @@
+GCC_VERSION      := $(shell $(CONFIG_SHELL) $(PWD)/scripts/gcc-version.sh $(CROSS_COMPILE)gcc)
+EXTRA_CFLAGS += -Idrivers/media/video/msm
+EXTRA_CFLAGS += -Idrivers/media/video/msm/io
+EXTRA_CFLAGS += -Idrivers/media/video/msm/eeprom
+EXTRA_CFLAGS += -Idrivers/media/video/msm/csi
+obj-$(CONFIG_MSM_CAMERA_SENSOR) += msm_sensor.o
+obj-$(CONFIG_OV5647) += ov5647_v4l2.o
+obj-$(CONFIG_IMX074) += imx074_v4l2.o
+obj-$(CONFIG_S5K3L1YX) += s5k3l1yx.o
+obj-$(CONFIG_IMX091) += imx091.o
+obj-$(CONFIG_OV2720) += ov2720.o
+obj-$(CONFIG_MT9M114) += mt9m114_v4l2.o
+obj-$(CONFIG_S5K4E1) += s5k4e1_v4l2.o
+obj-$(CONFIG_MT9E013) += mt9e013_v4l2.o
+obj-$(CONFIG_WEBCAM_OV9726) += ov9726_v4l2.o
+obj-$(CONFIG_OV7692) += ov7692_v4l2.o
+obj-$(CONFIG_VX6953) += vx6953.o
diff --git a/drivers/media/video/msm/sensors/imx074_v4l2.c b/drivers/media/video/msm/sensors/imx074_v4l2.c
new file mode 100644
index 0000000..7e41418
--- /dev/null
+++ b/drivers/media/video/msm/sensors/imx074_v4l2.c
@@ -0,0 +1,321 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_sensor.h"
+#define SENSOR_NAME "imx074"
+#define PLATFORM_DRIVER_NAME "msm_camera_imx074"
+#define imx074_obj imx074_##obj
+
+DEFINE_MUTEX(imx074_mut);
+static struct msm_sensor_ctrl_t imx074_s_ctrl;
+
+static struct msm_camera_i2c_reg_conf imx074_start_settings[] = {
+	{0x0100, 0x01},
+};
+
+static struct msm_camera_i2c_reg_conf imx074_stop_settings[] = {
+	{0x0100, 0x00},
+};
+
+static struct msm_camera_i2c_reg_conf imx074_groupon_settings[] = {
+	{0x104, 0x01},
+};
+
+static struct msm_camera_i2c_reg_conf imx074_groupoff_settings[] = {
+	{0x104, 0x00},
+};
+
+static struct msm_camera_i2c_reg_conf imx074_prev_settings[] = {
+	{0x0307, 0x2D}, /*pll_multiplier*/
+	{0x0340, 0x06}, /*frame_length_lines_hi*/
+	{0x0341, 0x34}, /*frame_length_lines_lo*/
+	{0x0342, 0x11}, /*line_length_pclk_hi*/
+	{0x0343, 0x78}, /*line_length_pclk_lo*/
+	{0x0347, 0x00}, /*y_addr_start*/
+	{0x034b, 0x2F}, /*y_add_end*/
+	{0x034c, 0x08}, /*x_output_size_msb*/
+	{0x034d, 0x38}, /*x_output_size_lsb*/
+	{0x034e, 0x06}, /*y_output_size_msb*/
+	{0x034f, 0x18}, /*y_output_size_lsb*/
+	{0x0381, 0x01}, /*x_even_inc*/
+	{0x0383, 0x03}, /*x_odd_inc*/
+	{0x0385, 0x01}, /*y_even_inc*/
+	{0x0387, 0x03}, /*y_odd_inc*/
+	{0x3001, 0x80}, /*hmodeadd*/
+	{0x3016, 0x16}, /*vmodeadd*/
+	{0x3069, 0x24}, /*vapplinepos_start*/
+	{0x306b, 0x53}, /*vapplinepos_end*/
+	{0x3086, 0x00}, /*shutter*/
+	{0x30e8, 0x80}, /*haddave*/
+	{0x3301, 0x83}, /*lanesel*/
+};
+
+static struct msm_camera_i2c_reg_conf imx074_snap_settings[] = {
+	{0x0307, 0x26}, /*pll_multiplier*/
+	{0x0340, 0x0C}, /*frame_length_lines_hi*/
+	{0x0341, 0x90}, /*frame_length_lines_lo*/
+	{0x0342, 0x11}, /*line_length_pclk_hi*/
+	{0x0343, 0x78}, /*line_length_pclk_lo*/
+	{0x0347, 0x00}, /*y_addr_start*/
+	{0x034b, 0x2F}, /*y_add_end*/
+	{0x034c, 0x10}, /*x_output_size_msb*/
+	{0x034d, 0x70}, /*x_output_size_lsb*/
+	{0x034e, 0x0c}, /*y_output_size_msb*/
+	{0x034f, 0x30}, /*y_output_size_lsb*/
+	{0x0381, 0x01}, /*x_even_inc*/
+	{0x0383, 0x01}, /*x_odd_inc*/
+	{0x0385, 0x01}, /*y_even_inc*/
+	{0x0387, 0x01}, /*y_odd_inc*/
+	{0x3001, 0x00}, /*hmodeadd*/
+	{0x3016, 0x06}, /*vmodeadd*/
+	{0x3069, 0x24}, /*vapplinepos_start*/
+	{0x306b, 0x53}, /*vapplinepos_end*/
+	{0x3086, 0x00}, /*shutter*/
+	{0x30e8, 0x00}, /*haddave*/
+	{0x3301, 0x03}, /*lanesel*/
+};
+
+static struct msm_camera_i2c_reg_conf imx074_recommend_settings[] = {
+	{0x0305, 0x02},
+	{0x302b, 0x4B},
+	{0x3024, 0x03},
+	{0x0101, 0x00},
+	{0x300a, 0x80},
+	{0x3014, 0x08},
+	{0x3015, 0x37},
+	{0x301c, 0x01},
+	{0x302c, 0x05},
+	{0x3031, 0x26},
+	{0x3041, 0x60},
+	{0x3051, 0x24},
+	{0x3053, 0x34},
+	{0x3057, 0xc0},
+	{0x305c, 0x09},
+	{0x305d, 0x07},
+	{0x3060, 0x30},
+	{0x3065, 0x00},
+	{0x30aa, 0x08},
+	{0x30ab, 0x1c},
+	{0x30b0, 0x32},
+	{0x30b2, 0x83},
+	{0x30d3, 0x04},
+	{0x3106, 0x78},
+	{0x310c, 0x82},
+	{0x3304, 0x05},
+	{0x3305, 0x04},
+	{0x3306, 0x11},
+	{0x3307, 0x02},
+	{0x3308, 0x0c},
+	{0x3309, 0x06},
+	{0x330a, 0x08},
+	{0x330b, 0x04},
+	{0x330c, 0x08},
+	{0x330d, 0x06},
+	{0x330f, 0x01},
+	{0x3381, 0x00},
+};
+
+static struct v4l2_subdev_info imx074_subdev_info[] = {
+	{
+	.code   = V4L2_MBUS_FMT_SBGGR10_1X10,
+	.colorspace = V4L2_COLORSPACE_JPEG,
+	.fmt    = 1,
+	.order    = 0,
+	},
+	/* more can be supported, to be added later */
+};
+
+static struct msm_camera_i2c_conf_array imx074_init_conf[] = {
+	{&imx074_recommend_settings[0],
+	ARRAY_SIZE(imx074_recommend_settings), 0, MSM_CAMERA_I2C_BYTE_DATA}
+};
+
+static struct msm_camera_i2c_conf_array imx074_confs[] = {
+	{&imx074_snap_settings[0],
+	ARRAY_SIZE(imx074_snap_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+	{&imx074_prev_settings[0],
+	ARRAY_SIZE(imx074_prev_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+};
+
+static struct msm_sensor_output_info_t imx074_dimensions[] = {
+	{
+		.x_output = 0x1070,
+		.y_output = 0xC30,
+		.line_length_pclk = 0x1178,
+		.frame_length_lines = 0xC90,
+		.vt_pixel_clk = 182400000,
+		.op_pixel_clk = 182400000,
+		.binning_factor = 1,
+	},
+	{
+		.x_output = 0x838,
+		.y_output = 0x618,
+		.line_length_pclk = 0x1178,
+		.frame_length_lines = 0x634,
+		.vt_pixel_clk = 216000000,
+		.op_pixel_clk = 108000000,
+		.binning_factor = 2,
+	},
+};
+
+static struct msm_camera_csi_params imx074_csic_params = {
+	.data_format = CSI_10BIT,
+	.lane_cnt    = 4,
+	.lane_assign = 0xe4,
+	.dpcm_scheme = 0,
+	.settle_cnt  = 0x14,
+};
+
+static struct msm_camera_csi_params *imx074_csic_params_array[] = {
+	&imx074_csic_params,
+	&imx074_csic_params,
+};
+
+static struct msm_camera_csid_vc_cfg imx074_cid_cfg[] = {
+	{0, CSI_RAW10, CSI_DECODE_10BIT},
+	{1, CSI_EMBED_DATA, CSI_DECODE_8BIT},
+	{2, CSI_RESERVED_DATA_0, CSI_DECODE_8BIT},
+};
+
+static struct msm_camera_csi2_params imx074_csi_params = {
+	.csid_params = {
+		.lane_cnt = 4,
+		.lut_params = {
+			.num_cid = ARRAY_SIZE(imx074_cid_cfg),
+			.vc_cfg = imx074_cid_cfg,
+		},
+	},
+	.csiphy_params = {
+		.lane_cnt = 4,
+		.settle_cnt = 0x1B,
+	},
+};
+
+static struct msm_camera_csi2_params *imx074_csi_params_array[] = {
+	&imx074_csi_params,
+	&imx074_csi_params,
+};
+
+static struct msm_sensor_output_reg_addr_t imx074_reg_addr = {
+	.x_output = 0x34C,
+	.y_output = 0x34E,
+	.line_length_pclk = 0x342,
+	.frame_length_lines = 0x340,
+};
+
+static struct msm_sensor_id_info_t imx074_id_info = {
+	.sensor_id_reg_addr = 0x0,
+	.sensor_id = 0x0074,
+};
+
+static struct msm_sensor_exp_gain_info_t imx074_exp_gain_info = {
+	.coarse_int_time_addr = 0x202,
+	.global_gain_addr = 0x204,
+	.vert_offset = 3,
+};
+
+static const struct i2c_device_id imx074_i2c_id[] = {
+	{SENSOR_NAME, (kernel_ulong_t)&imx074_s_ctrl},
+	{ }
+};
+
+static struct i2c_driver imx074_i2c_driver = {
+	.id_table = imx074_i2c_id,
+	.probe  = msm_sensor_i2c_probe,
+	.driver = {
+		.name = SENSOR_NAME,
+	},
+};
+
+static struct msm_camera_i2c_client imx074_sensor_i2c_client = {
+	.addr_type = MSM_CAMERA_I2C_WORD_ADDR,
+};
+
+static int __init msm_sensor_init_module(void)
+{
+	return i2c_add_driver(&imx074_i2c_driver);
+}
+
+static struct v4l2_subdev_core_ops imx074_subdev_core_ops = {
+	.ioctl = msm_sensor_subdev_ioctl,
+	.s_power = msm_sensor_power,
+};
+
+static struct v4l2_subdev_video_ops imx074_subdev_video_ops = {
+	.enum_mbus_fmt = msm_sensor_v4l2_enum_fmt,
+};
+
+static struct v4l2_subdev_ops imx074_subdev_ops = {
+	.core = &imx074_subdev_core_ops,
+	.video  = &imx074_subdev_video_ops,
+};
+
+static struct msm_sensor_fn_t imx074_func_tbl = {
+	.sensor_start_stream = msm_sensor_start_stream,
+	.sensor_stop_stream = msm_sensor_stop_stream,
+	.sensor_group_hold_on = msm_sensor_group_hold_on,
+	.sensor_group_hold_off = msm_sensor_group_hold_off,
+	.sensor_set_fps = msm_sensor_set_fps,
+	.sensor_write_exp_gain = msm_sensor_write_exp_gain1,
+	.sensor_write_snapshot_exp_gain = msm_sensor_write_exp_gain1,
+	.sensor_setting = msm_sensor_setting,
+	.sensor_csi_setting = msm_sensor_setting1,
+	.sensor_set_sensor_mode = msm_sensor_set_sensor_mode,
+	.sensor_mode_init = msm_sensor_mode_init,
+	.sensor_get_output_info = msm_sensor_get_output_info,
+	.sensor_config = msm_sensor_config,
+	.sensor_power_up = msm_sensor_power_up,
+	.sensor_power_down = msm_sensor_power_down,
+	.sensor_adjust_frame_lines = msm_sensor_adjust_frame_lines,
+};
+
+static struct msm_sensor_reg_t imx074_regs = {
+	.default_data_type = MSM_CAMERA_I2C_BYTE_DATA,
+	.start_stream_conf = imx074_start_settings,
+	.start_stream_conf_size = ARRAY_SIZE(imx074_start_settings),
+	.stop_stream_conf = imx074_stop_settings,
+	.stop_stream_conf_size = ARRAY_SIZE(imx074_stop_settings),
+	.group_hold_on_conf = imx074_groupon_settings,
+	.group_hold_on_conf_size = ARRAY_SIZE(imx074_groupon_settings),
+	.group_hold_off_conf = imx074_groupoff_settings,
+	.group_hold_off_conf_size =
+		ARRAY_SIZE(imx074_groupoff_settings),
+	.init_settings = &imx074_init_conf[0],
+	.init_size = ARRAY_SIZE(imx074_init_conf),
+	.mode_settings = &imx074_confs[0],
+	.output_settings = &imx074_dimensions[0],
+	.num_conf = ARRAY_SIZE(imx074_confs),
+};
+
+static struct msm_sensor_ctrl_t imx074_s_ctrl = {
+	.msm_sensor_reg = &imx074_regs,
+	.sensor_i2c_client = &imx074_sensor_i2c_client,
+	.sensor_i2c_addr = 0x34,
+	.sensor_output_reg_addr = &imx074_reg_addr,
+	.sensor_id_info = &imx074_id_info,
+	.sensor_exp_gain_info = &imx074_exp_gain_info,
+	.cam_mode = MSM_SENSOR_MODE_INVALID,
+	.csic_params = &imx074_csic_params_array[0],
+	.csi_params = &imx074_csi_params_array[0],
+	.msm_sensor_mutex = &imx074_mut,
+	.sensor_i2c_driver = &imx074_i2c_driver,
+	.sensor_v4l2_subdev_info = imx074_subdev_info,
+	.sensor_v4l2_subdev_info_size = ARRAY_SIZE(imx074_subdev_info),
+	.sensor_v4l2_subdev_ops = &imx074_subdev_ops,
+	.func_tbl = &imx074_func_tbl,
+	.clk_rate = MSM_SENSOR_MCLK_24HZ,
+};
+
+module_init(msm_sensor_init_module);
+MODULE_DESCRIPTION("Sony 13MP Bayer sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/sensors/imx091.c b/drivers/media/video/msm/sensors/imx091.c
new file mode 100644
index 0000000..70c3f6e
--- /dev/null
+++ b/drivers/media/video/msm/sensors/imx091.c
@@ -0,0 +1,346 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include "msm_sensor.h"
+#define SENSOR_NAME "imx091"
+#define PLATFORM_DRIVER_NAME "msm_camera_imx091"
+#define imx091_obj imx091_##obj
+
+DEFINE_MUTEX(imx091_mut);
+static struct msm_sensor_ctrl_t imx091_s_ctrl;
+
+static struct msm_camera_i2c_reg_conf imx091_start_settings[] = {
+	{0x0100, 0x01},
+};
+
+static struct msm_camera_i2c_reg_conf imx091_stop_settings[] = {
+	{0x0100, 0x00},
+};
+
+static struct msm_camera_i2c_reg_conf imx091_groupon_settings[] = {
+	{0x0104, 0x01},
+};
+
+static struct msm_camera_i2c_reg_conf imx091_groupoff_settings[] = {
+	{0x0104, 0x00},
+};
+
+static struct msm_camera_i2c_reg_conf imx091_prev_settings[] = {
+	/* 30fps 1/2 * 1/2 */
+	/* PLL setting */
+	{0x0305, 0x02}, /* pre_pll_clk_div[7:0] */
+	{0x0307, 0x2F}, /* pll_multiplier[7:0] */
+	{0x30A4, 0x02},
+	{0x303C, 0x4B},
+	/* mode setting */
+	{0x0340, 0x06}, /* frame_length_lines[15:8] */
+	{0x0341, 0x5A}, /* frame_length_lines[7:0] */
+	{0x0342, 0x12}, /* line_length_pck[15:8] */
+	{0x0343, 0x0C}, /* line_length_pck[7:0] */
+	{0x0344, 0x00}, /* x_addr_start[15:8] */
+	{0x0345, 0x08}, /* x_addr_start[7:0] */
+	{0x0346, 0x00}, /* y_addr_start[15:8] */
+	{0x0347, 0x30}, /* y_addr_start[7:0] */
+	{0x0348, 0x10}, /* x_addr_end[15:8] */
+	{0x0349, 0x77}, /* x_addr_end[7:0] */
+	{0x034A, 0x0C}, /* y_addr_end[15:8] */
+	{0x034B, 0x5F}, /* y_addr_end[7:0] */
+	{0x034C, 0x08}, /* x_output_size[15:8] */
+	{0x034D, 0x38}, /* x_output_size[7:0] */
+	{0x034E, 0x06}, /* y_output_size[15:8] */
+	{0x034F, 0x18}, /* y_output_size[7:0] */
+	{0x0381, 0x01}, /* x_even_inc[3:0] */
+	{0x0383, 0x03}, /* x_odd_inc[3:0] */
+	{0x0385, 0x01}, /* y_even_inc[7:0] */
+	{0x0387, 0x03}, /* y_odd_inc[7:0] */
+	{0x3040, 0x08},
+	{0x3041, 0x97},
+	{0x3048, 0x01},
+	{0x3064, 0x12},
+	{0x309B, 0x28},
+	{0x309E, 0x00},
+	{0x30D5, 0x09},
+	{0x30D6, 0x01},
+	{0x30D7, 0x01},
+	{0x30D8, 0x64},
+	{0x30D9, 0x89},
+	{0x30DE, 0x02},
+	{0x3102, 0x10},
+	{0x3103, 0x44},
+	{0x3104, 0x40},
+	{0x3105, 0x00},
+	{0x3106, 0x0D},
+	{0x3107, 0x01},
+	{0x310A, 0x0A},
+	{0x315C, 0x99},
+	{0x315D, 0x98},
+	{0x316E, 0x9A},
+	{0x316F, 0x99},
+	{0x3318, 0x73},
+};
+
+static struct msm_camera_i2c_reg_conf imx091_snap_settings[] = {
+	/* full size */
+	/* PLL setting */
+	{0x0305, 0x02}, /* pre_pll_clk_div[7:0] */
+	{0x0307, 0x2B}, /* pll_multiplier[7:0] */
+	{0x30A4, 0x02},
+	{0x303C, 0x4B},
+	/* mode setting */
+	{0x0340, 0x0C}, /* frame_length_lines[15:8] */
+	{0x0341, 0x8C}, /* frame_length_lines[7:0] */
+	{0x0342, 0x12}, /* line_length_pck[15:8] */
+	{0x0343, 0x0C}, /* line_length_pck[7:0] */
+	{0x0344, 0x00}, /* x_addr_start[15:8] */
+	{0x0345, 0x08}, /* x_addr_start[7:0] */
+	{0x0346, 0x00}, /* y_addr_start[15:8] */
+	{0x0347, 0x30}, /* y_addr_start[7:0] */
+	{0x0348, 0x10}, /* x_addr_end[15:8] */
+	{0x0349, 0x77}, /* x_addr_end[7:0] */
+	{0x034A, 0x0C}, /* y_addr_end[15:8] */
+	{0x034B, 0x5F}, /* y_addr_end[7:0] */
+	{0x034C, 0x10}, /* x_output_size[15:8] */
+	{0x034D, 0x70}, /* x_output_size[7:0] */
+	{0x034E, 0x0C}, /* y_output_size[15:8] */
+	{0x034F, 0x30}, /* y_output_size[7:0] */
+	{0x0381, 0x01}, /* x_even_inc[3:0] */
+	{0x0383, 0x01}, /* x_odd_inc[3:0] */
+	{0x0385, 0x01}, /* y_even_inc[7:0] */
+	{0x0387, 0x01}, /* y_odd_inc[7:0] */
+	{0x3040, 0x08},
+	{0x3041, 0x97},
+	{0x3048, 0x00},
+	{0x3064, 0x12},
+	{0x309B, 0x20},
+	{0x309E, 0x00},
+	{0x30D5, 0x00},
+	{0x30D6, 0x85},
+	{0x30D7, 0x2A},
+	{0x30D8, 0x64},
+	{0x30D9, 0x89},
+	{0x30DE, 0x00},
+	{0x3102, 0x10},
+	{0x3103, 0x44},
+	{0x3104, 0x40},
+	{0x3105, 0x00},
+	{0x3106, 0x0D},
+	{0x3107, 0x01},
+	{0x310A, 0x0A},
+	{0x315C, 0x99},
+	{0x315D, 0x98},
+	{0x316E, 0x9A},
+	{0x316F, 0x99},
+	{0x3318, 0x64},
+};
+
+static struct msm_camera_i2c_reg_conf imx091_recommend_settings[] = {
+	/* global setting */
+	{0x3087, 0x53},
+	{0x309D, 0x94},
+	{0x30A1, 0x08},
+	{0x30C7, 0x00},
+	{0x3115, 0x0E},
+	{0x3118, 0x42},
+	{0x311D, 0x34},
+	{0x3121, 0x0D},
+	{0x3212, 0xF2},
+	{0x3213, 0x0F},
+	{0x3215, 0x0F},
+	{0x3217, 0x0B},
+	{0x3219, 0x0B},
+	{0x321B, 0x0D},
+	{0x321D, 0x0D},
+	/* black level setting */
+	{0x3032, 0x40},
+};
+
+static struct v4l2_subdev_info imx091_subdev_info[] = {
+	{
+	.code   = V4L2_MBUS_FMT_SBGGR10_1X10,
+	.colorspace = V4L2_COLORSPACE_JPEG,
+	.fmt    = 1,
+	.order    = 0,
+	},
+	/* more can be supported, to be added later */
+};
+
+static struct msm_camera_i2c_conf_array imx091_init_conf[] = {
+	{&imx091_recommend_settings[0],
+	ARRAY_SIZE(imx091_recommend_settings), 0, MSM_CAMERA_I2C_BYTE_DATA}
+};
+
+static struct msm_camera_i2c_conf_array imx091_confs[] = {
+	{&imx091_snap_settings[0],
+	ARRAY_SIZE(imx091_snap_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+	{&imx091_prev_settings[0],
+	ARRAY_SIZE(imx091_prev_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+};
+
+static struct msm_sensor_output_info_t imx091_dimensions[] = {
+	{
+	/* full size */
+		.x_output = 0x1070, /* 4208 */
+		.y_output = 0x0C30, /* 3120 */
+		.line_length_pclk = 0x120C, /* 4620 */
+		.frame_length_lines = 0x0C8C, /* 3212 */
+		.vt_pixel_clk = 206400000,
+		.op_pixel_clk = 206400000,
+		.binning_factor = 1,
+	},
+	{
+	/* 30 fps 1/2 * 1/2 */
+		.x_output = 0x0838, /* 2104 */
+		.y_output = 0x0618, /* 1560 */
+		.line_length_pclk = 0x120C, /* 4620 */
+		.frame_length_lines = 0x065A, /* 1626 */
+		.vt_pixel_clk = 225600000,
+		.op_pixel_clk = 225600000,
+		.binning_factor = 1,
+	},
+};
+
+static struct msm_camera_csid_vc_cfg imx091_cid_cfg[] = {
+	{0, CSI_RAW10, CSI_DECODE_10BIT},
+	{1, CSI_EMBED_DATA, CSI_DECODE_8BIT},
+	{2, CSI_RESERVED_DATA_0, CSI_DECODE_8BIT},
+};
+
+static struct msm_camera_csi2_params imx091_csi_params = {
+	.csid_params = {
+		.lane_cnt = 4,
+		.lut_params = {
+			.num_cid = ARRAY_SIZE(imx091_cid_cfg),
+			.vc_cfg = imx091_cid_cfg,
+		},
+	},
+	.csiphy_params = {
+		.lane_cnt = 4,
+		.settle_cnt = 0x12,
+	},
+};
+
+static struct msm_camera_csi2_params *imx091_csi_params_array[] = {
+	&imx091_csi_params,
+	&imx091_csi_params,
+};
+
+static struct msm_sensor_output_reg_addr_t imx091_reg_addr = {
+	.x_output = 0x034C,
+	.y_output = 0x034E,
+	.line_length_pclk = 0x0342,
+	.frame_length_lines = 0x0340,
+};
+
+static struct msm_sensor_id_info_t imx091_id_info = {
+	.sensor_id_reg_addr = 0x0000,
+	.sensor_id = 0x0091,
+};
+
+static struct msm_sensor_exp_gain_info_t imx091_exp_gain_info = {
+	.coarse_int_time_addr = 0x0202,
+	.global_gain_addr = 0x0204,
+	.vert_offset = 5,
+};
+
+static const struct i2c_device_id imx091_i2c_id[] = {
+	{SENSOR_NAME, (kernel_ulong_t)&imx091_s_ctrl},
+	{ }
+};
+
+static struct i2c_driver imx091_i2c_driver = {
+	.id_table = imx091_i2c_id,
+	.probe  = msm_sensor_i2c_probe,
+	.driver = {
+		.name = SENSOR_NAME,
+	},
+};
+
+static struct msm_camera_i2c_client imx091_sensor_i2c_client = {
+	.addr_type = MSM_CAMERA_I2C_WORD_ADDR,
+};
+
+
+static int __init imx091_sensor_init_module(void)
+{
+	return i2c_add_driver(&imx091_i2c_driver);
+}
+
+static struct v4l2_subdev_core_ops imx091_subdev_core_ops = {
+	.ioctl = msm_sensor_subdev_ioctl,
+	.s_power = msm_sensor_power,
+};
+
+static struct v4l2_subdev_video_ops imx091_subdev_video_ops = {
+	.enum_mbus_fmt = msm_sensor_v4l2_enum_fmt,
+};
+
+static struct v4l2_subdev_ops imx091_subdev_ops = {
+	.core = &imx091_subdev_core_ops,
+	.video  = &imx091_subdev_video_ops,
+};
+
+static struct msm_sensor_fn_t imx091_func_tbl = {
+	.sensor_start_stream = msm_sensor_start_stream,
+	.sensor_stop_stream = msm_sensor_stop_stream,
+	.sensor_group_hold_on = msm_sensor_group_hold_on,
+	.sensor_group_hold_off = msm_sensor_group_hold_off,
+	.sensor_set_fps = msm_sensor_set_fps,
+	.sensor_write_exp_gain = msm_sensor_write_exp_gain1,
+	.sensor_write_snapshot_exp_gain = msm_sensor_write_exp_gain1,
+	.sensor_setting = msm_sensor_setting,
+	.sensor_set_sensor_mode = msm_sensor_set_sensor_mode,
+	.sensor_mode_init = msm_sensor_mode_init,
+	.sensor_get_output_info = msm_sensor_get_output_info,
+	.sensor_config = msm_sensor_config,
+	.sensor_power_up = msm_sensor_power_up,
+	.sensor_power_down = msm_sensor_power_down,
+	.sensor_adjust_frame_lines = msm_sensor_adjust_frame_lines,
+};
+
+static struct msm_sensor_reg_t imx091_regs = {
+	.default_data_type = MSM_CAMERA_I2C_BYTE_DATA,
+	.start_stream_conf = imx091_start_settings,
+	.start_stream_conf_size = ARRAY_SIZE(imx091_start_settings),
+	.stop_stream_conf = imx091_stop_settings,
+	.stop_stream_conf_size = ARRAY_SIZE(imx091_stop_settings),
+	.group_hold_on_conf = imx091_groupon_settings,
+	.group_hold_on_conf_size = ARRAY_SIZE(imx091_groupon_settings),
+	.group_hold_off_conf = imx091_groupoff_settings,
+	.group_hold_off_conf_size = ARRAY_SIZE(imx091_groupoff_settings),
+	.init_settings = &imx091_init_conf[0],
+	.init_size = ARRAY_SIZE(imx091_init_conf),
+	.mode_settings = &imx091_confs[0],
+	.output_settings = &imx091_dimensions[0],
+	.num_conf = ARRAY_SIZE(imx091_confs),
+};
+
+static struct msm_sensor_ctrl_t imx091_s_ctrl = {
+	.msm_sensor_reg = &imx091_regs,
+	.sensor_i2c_client = &imx091_sensor_i2c_client,
+	.sensor_i2c_addr = 0x34,
+	.sensor_output_reg_addr = &imx091_reg_addr,
+	.sensor_id_info = &imx091_id_info,
+	.sensor_exp_gain_info = &imx091_exp_gain_info,
+	.cam_mode = MSM_SENSOR_MODE_INVALID,
+	.csi_params = &imx091_csi_params_array[0],
+	.msm_sensor_mutex = &imx091_mut,
+	.sensor_i2c_driver = &imx091_i2c_driver,
+	.sensor_v4l2_subdev_info = imx091_subdev_info,
+	.sensor_v4l2_subdev_info_size = ARRAY_SIZE(imx091_subdev_info),
+	.sensor_v4l2_subdev_ops = &imx091_subdev_ops,
+	.func_tbl = &imx091_func_tbl,
+	.clk_rate = MSM_SENSOR_MCLK_24HZ,
+};
+
+module_init(imx091_sensor_init_module);
+MODULE_DESCRIPTION("SONY 12MP Bayer sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/sensors/msm_sensor.c b/drivers/media/video/msm/sensors/msm_sensor.c
new file mode 100644
index 0000000..5b9eb31
--- /dev/null
+++ b/drivers/media/video/msm/sensors/msm_sensor.c
@@ -0,0 +1,880 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "msm_sensor.h"
+#include "msm.h"
+#include "msm_ispif.h"
+#include "msm_camera_i2c_mux.h"
+
+/*=============================================================*/
+int32_t msm_sensor_adjust_frame_lines(struct msm_sensor_ctrl_t *s_ctrl,
+	uint16_t res)
+{
+	uint16_t cur_line = 0;
+	uint16_t exp_fl_lines = 0;
+	if (s_ctrl->sensor_exp_gain_info) {
+		if (s_ctrl->prev_gain && s_ctrl->prev_line &&
+			s_ctrl->func_tbl->sensor_write_exp_gain)
+			s_ctrl->func_tbl->sensor_write_exp_gain(
+				s_ctrl,
+				s_ctrl->prev_gain,
+				s_ctrl->prev_line);
+
+		msm_camera_i2c_read(s_ctrl->sensor_i2c_client,
+			s_ctrl->sensor_exp_gain_info->coarse_int_time_addr,
+			&cur_line,
+			MSM_CAMERA_I2C_WORD_DATA);
+		exp_fl_lines = cur_line +
+			s_ctrl->sensor_exp_gain_info->vert_offset;
+		if (exp_fl_lines > s_ctrl->msm_sensor_reg->
+			output_settings[res].frame_length_lines)
+			msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+				s_ctrl->sensor_output_reg_addr->
+				frame_length_lines,
+				exp_fl_lines,
+				MSM_CAMERA_I2C_WORD_DATA);
+		CDBG("%s cur_fl_lines %d, exp_fl_lines %d\n", __func__,
+			s_ctrl->msm_sensor_reg->
+			output_settings[res].frame_length_lines,
+			exp_fl_lines);
+	}
+	return 0;
+}
+
+int32_t msm_sensor_write_init_settings(struct msm_sensor_ctrl_t *s_ctrl)
+{
+	int32_t rc;
+	rc = msm_sensor_write_all_conf_array(
+		s_ctrl->sensor_i2c_client,
+		s_ctrl->msm_sensor_reg->init_settings,
+		s_ctrl->msm_sensor_reg->init_size);
+	return rc;
+}
+
+int32_t msm_sensor_write_res_settings(struct msm_sensor_ctrl_t *s_ctrl,
+	uint16_t res)
+{
+	int32_t rc;
+	rc = msm_sensor_write_conf_array(
+		s_ctrl->sensor_i2c_client,
+		s_ctrl->msm_sensor_reg->mode_settings, res);
+	if (rc < 0)
+		return rc;
+
+	rc = msm_sensor_write_output_settings(s_ctrl, res);
+	if (rc < 0)
+		return rc;
+
+	if (s_ctrl->func_tbl->sensor_adjust_frame_lines)
+		rc = s_ctrl->func_tbl->sensor_adjust_frame_lines(s_ctrl, res);
+
+	return rc;
+}
+
+int32_t msm_sensor_write_output_settings(struct msm_sensor_ctrl_t *s_ctrl,
+	uint16_t res)
+{
+	int32_t rc = -EFAULT;
+	struct msm_camera_i2c_reg_conf dim_settings[] = {
+		{s_ctrl->sensor_output_reg_addr->x_output,
+			s_ctrl->msm_sensor_reg->
+			output_settings[res].x_output},
+		{s_ctrl->sensor_output_reg_addr->y_output,
+			s_ctrl->msm_sensor_reg->
+			output_settings[res].y_output},
+		{s_ctrl->sensor_output_reg_addr->line_length_pclk,
+			s_ctrl->msm_sensor_reg->
+			output_settings[res].line_length_pclk},
+		{s_ctrl->sensor_output_reg_addr->frame_length_lines,
+			s_ctrl->msm_sensor_reg->
+			output_settings[res].frame_length_lines},
+	};
+
+	rc = msm_camera_i2c_write_tbl(s_ctrl->sensor_i2c_client, dim_settings,
+		ARRAY_SIZE(dim_settings), MSM_CAMERA_I2C_WORD_DATA);
+	return rc;
+}
+
+void msm_sensor_start_stream(struct msm_sensor_ctrl_t *s_ctrl)
+{
+	msm_camera_i2c_write_tbl(
+		s_ctrl->sensor_i2c_client,
+		s_ctrl->msm_sensor_reg->start_stream_conf,
+		s_ctrl->msm_sensor_reg->start_stream_conf_size,
+		s_ctrl->msm_sensor_reg->default_data_type);
+}
+
+void msm_sensor_stop_stream(struct msm_sensor_ctrl_t *s_ctrl)
+{
+	msm_camera_i2c_write_tbl(
+		s_ctrl->sensor_i2c_client,
+		s_ctrl->msm_sensor_reg->stop_stream_conf,
+		s_ctrl->msm_sensor_reg->stop_stream_conf_size,
+		s_ctrl->msm_sensor_reg->default_data_type);
+}
+
+void msm_sensor_group_hold_on(struct msm_sensor_ctrl_t *s_ctrl)
+{
+	msm_camera_i2c_write_tbl(
+		s_ctrl->sensor_i2c_client,
+		s_ctrl->msm_sensor_reg->group_hold_on_conf,
+		s_ctrl->msm_sensor_reg->group_hold_on_conf_size,
+		s_ctrl->msm_sensor_reg->default_data_type);
+}
+
+void msm_sensor_group_hold_off(struct msm_sensor_ctrl_t *s_ctrl)
+{
+	msm_camera_i2c_write_tbl(
+		s_ctrl->sensor_i2c_client,
+		s_ctrl->msm_sensor_reg->group_hold_off_conf,
+		s_ctrl->msm_sensor_reg->group_hold_off_conf_size,
+		s_ctrl->msm_sensor_reg->default_data_type);
+}
+
+int32_t msm_sensor_set_fps(struct msm_sensor_ctrl_t *s_ctrl,
+						struct fps_cfg *fps)
+{
+	s_ctrl->fps_divider = fps->fps_div;
+
+	return 0;
+}
+
+int32_t msm_sensor_write_exp_gain1(struct msm_sensor_ctrl_t *s_ctrl,
+		uint16_t gain, uint32_t line)
+{
+	uint32_t fl_lines;
+	uint8_t offset;
+	fl_lines = s_ctrl->curr_frame_length_lines;
+	fl_lines = (fl_lines * s_ctrl->fps_divider) / Q10;
+	offset = s_ctrl->sensor_exp_gain_info->vert_offset;
+	if (line > (fl_lines - offset))
+		fl_lines = line + offset;
+
+	s_ctrl->func_tbl->sensor_group_hold_on(s_ctrl);
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_output_reg_addr->frame_length_lines, fl_lines,
+		MSM_CAMERA_I2C_WORD_DATA);
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->coarse_int_time_addr, line,
+		MSM_CAMERA_I2C_WORD_DATA);
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->global_gain_addr, gain,
+		MSM_CAMERA_I2C_WORD_DATA);
+	s_ctrl->func_tbl->sensor_group_hold_off(s_ctrl);
+	return 0;
+}
+
+int32_t msm_sensor_write_exp_gain2(struct msm_sensor_ctrl_t *s_ctrl,
+		uint16_t gain, uint32_t line)
+{
+	uint32_t fl_lines, ll_pclk, ll_ratio;
+	uint8_t offset;
+	fl_lines = s_ctrl->curr_frame_length_lines * s_ctrl->fps_divider / Q10;
+	ll_pclk = s_ctrl->curr_line_length_pclk;
+	offset = s_ctrl->sensor_exp_gain_info->vert_offset;
+	if (line > (fl_lines - offset)) {
+		ll_ratio = (line * Q10) / (fl_lines - offset);
+		ll_pclk = ll_pclk * ll_ratio / Q10;
+		line = fl_lines - offset;
+	}
+
+	s_ctrl->func_tbl->sensor_group_hold_on(s_ctrl);
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_output_reg_addr->line_length_pclk, ll_pclk,
+		MSM_CAMERA_I2C_WORD_DATA);
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->coarse_int_time_addr, line,
+		MSM_CAMERA_I2C_WORD_DATA);
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->global_gain_addr, gain,
+		MSM_CAMERA_I2C_WORD_DATA);
+	s_ctrl->func_tbl->sensor_group_hold_off(s_ctrl);
+	return 0;
+}
+
+int32_t msm_sensor_setting1(struct msm_sensor_ctrl_t *s_ctrl,
+			int update_type, int res)
+{
+	int32_t rc = 0;
+	static int csi_config;
+
+	s_ctrl->func_tbl->sensor_stop_stream(s_ctrl);
+	msleep(30);
+	if (update_type == MSM_SENSOR_REG_INIT) {
+		CDBG("Register INIT\n");
+		s_ctrl->curr_csi_params = NULL;
+		msm_sensor_enable_debugfs(s_ctrl);
+		msm_sensor_write_init_settings(s_ctrl);
+		csi_config = 0;
+	} else if (update_type == MSM_SENSOR_UPDATE_PERIODIC) {
+		CDBG("PERIODIC : %d\n", res);
+		msm_sensor_write_conf_array(
+			s_ctrl->sensor_i2c_client,
+			s_ctrl->msm_sensor_reg->mode_settings, res);
+		msleep(30);
+		if (!csi_config) {
+			s_ctrl->curr_csic_params = s_ctrl->csic_params[res];
+			CDBG("CSI config in progress\n");
+			v4l2_subdev_notify(&s_ctrl->sensor_v4l2_subdev,
+				NOTIFY_CSIC_CFG,
+				s_ctrl->curr_csic_params);
+			CDBG("CSI config is done\n");
+			mb();
+			msleep(30);
+			csi_config = 1;
+		}
+		v4l2_subdev_notify(&s_ctrl->sensor_v4l2_subdev,
+			NOTIFY_PCLK_CHANGE,
+			&s_ctrl->sensordata->pdata->ioclk.vfe_clk_rate);
+
+		s_ctrl->func_tbl->sensor_start_stream(s_ctrl);
+		msleep(50);
+	}
+	return rc;
+}
+int32_t msm_sensor_setting(struct msm_sensor_ctrl_t *s_ctrl,
+			int update_type, int res)
+{
+	int32_t rc = 0;
+
+	v4l2_subdev_notify(&s_ctrl->sensor_v4l2_subdev,
+		NOTIFY_ISPIF_STREAM, (void *)ISPIF_STREAM(
+		PIX_0, ISPIF_OFF_IMMEDIATELY));
+	s_ctrl->func_tbl->sensor_stop_stream(s_ctrl);
+	msleep(30);
+	if (update_type == MSM_SENSOR_REG_INIT) {
+		s_ctrl->curr_csi_params = NULL;
+		msm_sensor_enable_debugfs(s_ctrl);
+		msm_sensor_write_init_settings(s_ctrl);
+	} else if (update_type == MSM_SENSOR_UPDATE_PERIODIC) {
+		msm_sensor_write_res_settings(s_ctrl, res);
+		if (s_ctrl->curr_csi_params != s_ctrl->csi_params[res]) {
+			s_ctrl->curr_csi_params = s_ctrl->csi_params[res];
+			s_ctrl->curr_csi_params->csid_params.lane_assign =
+				s_ctrl->sensordata->sensor_platform_info->
+				csi_lane_params->csi_lane_assign;
+			s_ctrl->curr_csi_params->csiphy_params.lane_mask =
+				s_ctrl->sensordata->sensor_platform_info->
+				csi_lane_params->csi_lane_mask;
+			v4l2_subdev_notify(&s_ctrl->sensor_v4l2_subdev,
+				NOTIFY_CSID_CFG,
+				&s_ctrl->curr_csi_params->csid_params);
+			v4l2_subdev_notify(&s_ctrl->sensor_v4l2_subdev,
+						NOTIFY_CID_CHANGE, NULL);
+			mb();
+			v4l2_subdev_notify(&s_ctrl->sensor_v4l2_subdev,
+				NOTIFY_CSIPHY_CFG,
+				&s_ctrl->curr_csi_params->csiphy_params);
+			mb();
+			msleep(20);
+		}
+
+		v4l2_subdev_notify(&s_ctrl->sensor_v4l2_subdev,
+			NOTIFY_PCLK_CHANGE, &s_ctrl->msm_sensor_reg->
+			output_settings[res].op_pixel_clk);
+		v4l2_subdev_notify(&s_ctrl->sensor_v4l2_subdev,
+			NOTIFY_ISPIF_STREAM, (void *)ISPIF_STREAM(
+			PIX_0, ISPIF_ON_FRAME_BOUNDARY));
+		s_ctrl->func_tbl->sensor_start_stream(s_ctrl);
+		msleep(30);
+	}
+	return rc;
+}
+
+int32_t msm_sensor_set_sensor_mode(struct msm_sensor_ctrl_t *s_ctrl,
+	int mode, int res)
+{
+	int32_t rc = 0;
+	if (s_ctrl->curr_res != res) {
+		s_ctrl->curr_frame_length_lines =
+			s_ctrl->msm_sensor_reg->
+			output_settings[res].frame_length_lines;
+
+		s_ctrl->curr_line_length_pclk =
+			s_ctrl->msm_sensor_reg->
+			output_settings[res].line_length_pclk;
+
+		if (s_ctrl->sensordata->pdata->is_csic ||
+			!s_ctrl->sensordata->csi_if)
+			rc = s_ctrl->func_tbl->sensor_csi_setting(s_ctrl,
+				MSM_SENSOR_UPDATE_PERIODIC, res);
+		else
+			rc = s_ctrl->func_tbl->sensor_setting(s_ctrl,
+				MSM_SENSOR_UPDATE_PERIODIC, res);
+		if (rc < 0)
+			return rc;
+		s_ctrl->curr_res = res;
+	}
+
+	return rc;
+}
+
+int32_t msm_sensor_mode_init(struct msm_sensor_ctrl_t *s_ctrl,
+			int mode, struct sensor_init_cfg *init_info)
+{
+	int32_t rc = 0;
+	s_ctrl->fps_divider = Q10;
+	s_ctrl->cam_mode = MSM_SENSOR_MODE_INVALID;
+
+	CDBG("%s: %d\n", __func__, __LINE__);
+	if (mode != s_ctrl->cam_mode) {
+		s_ctrl->curr_res = MSM_SENSOR_INVALID_RES;
+		s_ctrl->cam_mode = mode;
+
+		if (s_ctrl->sensordata->pdata->is_csic ||
+			!s_ctrl->sensordata->csi_if)
+			rc = s_ctrl->func_tbl->sensor_csi_setting(s_ctrl,
+				MSM_SENSOR_REG_INIT, 0);
+		else
+			rc = s_ctrl->func_tbl->sensor_setting(s_ctrl,
+				MSM_SENSOR_REG_INIT, 0);
+	}
+	return rc;
+}
+
+int32_t msm_sensor_get_output_info(struct msm_sensor_ctrl_t *s_ctrl,
+		struct sensor_output_info_t *sensor_output_info)
+{
+	int rc = 0;
+	sensor_output_info->num_info = s_ctrl->msm_sensor_reg->num_conf;
+	if (copy_to_user((void *)sensor_output_info->output_info,
+		s_ctrl->msm_sensor_reg->output_settings,
+		sizeof(struct msm_sensor_output_info_t) *
+		s_ctrl->msm_sensor_reg->num_conf))
+		rc = -EFAULT;
+
+	return rc;
+}
+
+int32_t msm_sensor_release(struct msm_sensor_ctrl_t *s_ctrl)
+{
+	long fps = 0;
+	uint32_t delay = 0;
+	CDBG("%s called\n", __func__);
+	s_ctrl->func_tbl->sensor_stop_stream(s_ctrl);
+	if (s_ctrl->curr_res != MSM_SENSOR_INVALID_RES) {
+		fps = s_ctrl->msm_sensor_reg->
+			output_settings[s_ctrl->curr_res].vt_pixel_clk /
+			s_ctrl->curr_frame_length_lines /
+			s_ctrl->curr_line_length_pclk;
+		delay = 1000 / fps;
+		CDBG("%s fps = %ld, delay = %d\n", __func__, fps, delay);
+		msleep(delay);
+	}
+	return 0;
+}
+
+long msm_sensor_subdev_ioctl(struct v4l2_subdev *sd,
+			unsigned int cmd, void *arg)
+{
+	struct msm_sensor_ctrl_t *s_ctrl = get_sctrl(sd);
+	void __user *argp = (void __user *)arg;
+	switch (cmd) {
+	case VIDIOC_MSM_SENSOR_CFG:
+		return s_ctrl->func_tbl->sensor_config(s_ctrl, argp);
+	case VIDIOC_MSM_SENSOR_RELEASE:
+		return msm_sensor_release(s_ctrl);
+	default:
+		return -ENOIOCTLCMD;
+	}
+}
+
+int32_t msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp)
+{
+	struct sensor_cfg_data cdata;
+	long   rc = 0;
+	if (copy_from_user(&cdata,
+		(void *)argp,
+		sizeof(struct sensor_cfg_data)))
+		return -EFAULT;
+	mutex_lock(s_ctrl->msm_sensor_mutex);
+	CDBG("msm_sensor_config: cfgtype = %d\n",
+	cdata.cfgtype);
+		switch (cdata.cfgtype) {
+		case CFG_SET_FPS:
+		case CFG_SET_PICT_FPS:
+			if (s_ctrl->func_tbl->
+			sensor_set_fps == NULL) {
+				rc = -EFAULT;
+				break;
+			}
+			rc = s_ctrl->func_tbl->
+				sensor_set_fps(
+				s_ctrl,
+				&(cdata.cfg.fps));
+			break;
+
+		case CFG_SET_EXP_GAIN:
+			if (s_ctrl->func_tbl->
+			sensor_write_exp_gain == NULL) {
+				rc = -EFAULT;
+				break;
+			}
+			rc =
+				s_ctrl->func_tbl->
+				sensor_write_exp_gain(
+					s_ctrl,
+					cdata.cfg.exp_gain.gain,
+					cdata.cfg.exp_gain.line);
+			s_ctrl->prev_gain = cdata.cfg.exp_gain.gain;
+			s_ctrl->prev_line = cdata.cfg.exp_gain.line;
+			break;
+
+		case CFG_SET_PICT_EXP_GAIN:
+			if (s_ctrl->func_tbl->
+			sensor_write_snapshot_exp_gain == NULL) {
+				rc = -EFAULT;
+				break;
+			}
+			rc =
+				s_ctrl->func_tbl->
+				sensor_write_snapshot_exp_gain(
+					s_ctrl,
+					cdata.cfg.exp_gain.gain,
+					cdata.cfg.exp_gain.line);
+			break;
+
+		case CFG_SET_MODE:
+			if (s_ctrl->func_tbl->
+			sensor_set_sensor_mode == NULL) {
+				rc = -EFAULT;
+				break;
+			}
+			rc = s_ctrl->func_tbl->
+				sensor_set_sensor_mode(
+					s_ctrl,
+					cdata.mode,
+					cdata.rs);
+			break;
+
+		case CFG_SET_EFFECT:
+			break;
+
+		case CFG_SENSOR_INIT:
+			if (s_ctrl->func_tbl->
+			sensor_mode_init == NULL) {
+				rc = -EFAULT;
+				break;
+			}
+			rc = s_ctrl->func_tbl->
+				sensor_mode_init(
+				s_ctrl,
+				cdata.mode,
+				&(cdata.cfg.init_info));
+			break;
+
+		case CFG_GET_OUTPUT_INFO:
+			if (s_ctrl->func_tbl->
+			sensor_get_output_info == NULL) {
+				rc = -EFAULT;
+				break;
+			}
+			rc = s_ctrl->func_tbl->
+				sensor_get_output_info(
+				s_ctrl,
+				&cdata.cfg.output_info);
+
+			if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+				rc = -EFAULT;
+			break;
+
+		default:
+			rc = -EFAULT;
+			break;
+		}
+
+	mutex_unlock(s_ctrl->msm_sensor_mutex);
+
+	return rc;
+}
+
+static struct msm_cam_clk_info cam_clk_info[] = {
+	{"cam_clk", MSM_SENSOR_MCLK_24HZ},
+};
+
+int32_t msm_sensor_enable_i2c_mux(struct msm_camera_i2c_conf *i2c_conf)
+{
+	struct v4l2_subdev *i2c_mux_sd =
+		dev_get_drvdata(&i2c_conf->mux_dev->dev);
+	v4l2_subdev_call(i2c_mux_sd, core, ioctl,
+		VIDIOC_MSM_I2C_MUX_INIT, NULL);
+	v4l2_subdev_call(i2c_mux_sd, core, ioctl,
+		VIDIOC_MSM_I2C_MUX_CFG, (void *)&i2c_conf->i2c_mux_mode);
+	return 0;
+}
+
+int32_t msm_sensor_disable_i2c_mux(struct msm_camera_i2c_conf *i2c_conf)
+{
+	struct v4l2_subdev *i2c_mux_sd =
+		dev_get_drvdata(&i2c_conf->mux_dev->dev);
+	v4l2_subdev_call(i2c_mux_sd, core, ioctl,
+				VIDIOC_MSM_I2C_MUX_RELEASE, NULL);
+	return 0;
+}
+
+int32_t msm_sensor_power_up(struct msm_sensor_ctrl_t *s_ctrl)
+{
+	int32_t rc = 0;
+	struct msm_camera_sensor_info *data = s_ctrl->sensordata;
+	CDBG("%s: %d\n", __func__, __LINE__);
+	s_ctrl->reg_ptr = kzalloc(sizeof(struct regulator *)
+			* data->sensor_platform_info->num_vreg, GFP_KERNEL);
+	if (!s_ctrl->reg_ptr) {
+		pr_err("%s: could not allocate mem for regulators\n",
+			__func__);
+		return -ENOMEM;
+	}
+
+	rc = msm_camera_request_gpio_table(data, 1);
+	if (rc < 0) {
+		pr_err("%s: request gpio failed\n", __func__);
+		goto request_gpio_failed;
+	}
+
+	rc = msm_camera_config_vreg(&s_ctrl->sensor_i2c_client->client->dev,
+			s_ctrl->sensordata->sensor_platform_info->cam_vreg,
+			s_ctrl->sensordata->sensor_platform_info->num_vreg,
+			s_ctrl->reg_ptr, 1);
+	if (rc < 0) {
+		pr_err("%s: regulator on failed\n", __func__);
+		goto config_vreg_failed;
+	}
+
+	rc = msm_camera_enable_vreg(&s_ctrl->sensor_i2c_client->client->dev,
+			s_ctrl->sensordata->sensor_platform_info->cam_vreg,
+			s_ctrl->sensordata->sensor_platform_info->num_vreg,
+			s_ctrl->reg_ptr, 1);
+	if (rc < 0) {
+		pr_err("%s: enable regulator failed\n", __func__);
+		goto enable_vreg_failed;
+	}
+
+	rc = msm_camera_config_gpio_table(data, 1);
+	if (rc < 0) {
+		pr_err("%s: config gpio failed\n", __func__);
+		goto config_gpio_failed;
+	}
+
+	if (s_ctrl->clk_rate != 0)
+		cam_clk_info->clk_rate = s_ctrl->clk_rate;
+
+	rc = msm_cam_clk_enable(&s_ctrl->sensor_i2c_client->client->dev,
+		cam_clk_info, &s_ctrl->cam_clk, ARRAY_SIZE(cam_clk_info), 1);
+	if (rc < 0) {
+		pr_err("%s: clk enable failed\n", __func__);
+		goto enable_clk_failed;
+	}
+
+	usleep_range(1000, 2000);
+	if (data->sensor_platform_info->ext_power_ctrl != NULL)
+		data->sensor_platform_info->ext_power_ctrl(1);
+
+	if (data->sensor_platform_info->i2c_conf &&
+		data->sensor_platform_info->i2c_conf->use_i2c_mux)
+		msm_sensor_enable_i2c_mux(data->sensor_platform_info->i2c_conf);
+
+	return rc;
+
+enable_clk_failed:
+		msm_camera_config_gpio_table(data, 0);
+config_gpio_failed:
+	msm_camera_enable_vreg(&s_ctrl->sensor_i2c_client->client->dev,
+			s_ctrl->sensordata->sensor_platform_info->cam_vreg,
+			s_ctrl->sensordata->sensor_platform_info->num_vreg,
+			s_ctrl->reg_ptr, 0);
+
+enable_vreg_failed:
+	msm_camera_config_vreg(&s_ctrl->sensor_i2c_client->client->dev,
+		s_ctrl->sensordata->sensor_platform_info->cam_vreg,
+		s_ctrl->sensordata->sensor_platform_info->num_vreg,
+		s_ctrl->reg_ptr, 0);
+config_vreg_failed:
+	msm_camera_request_gpio_table(data, 0);
+request_gpio_failed:
+	kfree(s_ctrl->reg_ptr);
+	return rc;
+}
+
+int32_t msm_sensor_power_down(struct msm_sensor_ctrl_t *s_ctrl)
+{
+	struct msm_camera_sensor_info *data = s_ctrl->sensordata;
+	CDBG("%s\n", __func__);
+	if (data->sensor_platform_info->i2c_conf &&
+		data->sensor_platform_info->i2c_conf->use_i2c_mux)
+		msm_sensor_disable_i2c_mux(
+			data->sensor_platform_info->i2c_conf);
+
+	if (data->sensor_platform_info->ext_power_ctrl != NULL)
+		data->sensor_platform_info->ext_power_ctrl(0);
+	msm_cam_clk_enable(&s_ctrl->sensor_i2c_client->client->dev,
+		cam_clk_info, &s_ctrl->cam_clk, ARRAY_SIZE(cam_clk_info), 0);
+	msm_camera_config_gpio_table(data, 0);
+	msm_camera_enable_vreg(&s_ctrl->sensor_i2c_client->client->dev,
+		s_ctrl->sensordata->sensor_platform_info->cam_vreg,
+		s_ctrl->sensordata->sensor_platform_info->num_vreg,
+		s_ctrl->reg_ptr, 0);
+	msm_camera_config_vreg(&s_ctrl->sensor_i2c_client->client->dev,
+		s_ctrl->sensordata->sensor_platform_info->cam_vreg,
+		s_ctrl->sensordata->sensor_platform_info->num_vreg,
+		s_ctrl->reg_ptr, 0);
+	msm_camera_request_gpio_table(data, 0);
+	kfree(s_ctrl->reg_ptr);
+	return 0;
+}
+
+int32_t msm_sensor_match_id(struct msm_sensor_ctrl_t *s_ctrl)
+{
+	int32_t rc = 0;
+	uint16_t chipid = 0;
+	rc = msm_camera_i2c_read(
+			s_ctrl->sensor_i2c_client,
+			s_ctrl->sensor_id_info->sensor_id_reg_addr, &chipid,
+			MSM_CAMERA_I2C_WORD_DATA);
+	if (rc < 0) {
+		pr_err("%s: %s: read id failed\n", __func__,
+			s_ctrl->sensordata->sensor_name);
+		return rc;
+	}
+
+	CDBG("msm_sensor id: %d\n", chipid);
+	if (chipid != s_ctrl->sensor_id_info->sensor_id) {
+		pr_err("msm_sensor_match_id chip id doesnot match\n");
+		return -ENODEV;
+	}
+	return rc;
+}
+
+struct msm_sensor_ctrl_t *get_sctrl(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct msm_sensor_ctrl_t, sensor_v4l2_subdev);
+}
+
+int32_t msm_sensor_i2c_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	int rc = 0;
+	struct msm_sensor_ctrl_t *s_ctrl;
+	CDBG("%s %s_i2c_probe called\n", __func__, client->name);
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		pr_err("%s %s i2c_check_functionality failed\n",
+			__func__, client->name);
+		rc = -EFAULT;
+		return rc;
+	}
+
+	s_ctrl = (struct msm_sensor_ctrl_t *)(id->driver_data);
+	if (s_ctrl->sensor_i2c_client != NULL) {
+		s_ctrl->sensor_i2c_client->client = client;
+		if (s_ctrl->sensor_i2c_addr != 0)
+			s_ctrl->sensor_i2c_client->client->addr =
+				s_ctrl->sensor_i2c_addr;
+	} else {
+		pr_err("%s %s sensor_i2c_client NULL\n",
+			__func__, client->name);
+		rc = -EFAULT;
+		return rc;
+	}
+
+	s_ctrl->sensordata = client->dev.platform_data;
+	if (s_ctrl->sensordata == NULL) {
+		pr_err("%s %s NULL sensor data\n", __func__, client->name);
+		return -EFAULT;
+	}
+
+	rc = s_ctrl->func_tbl->sensor_power_up(s_ctrl);
+	if (rc < 0) {
+		pr_err("%s %s power up failed\n", __func__, client->name);
+		return rc;
+	}
+
+	if (s_ctrl->func_tbl->sensor_match_id)
+		rc = s_ctrl->func_tbl->sensor_match_id(s_ctrl);
+	else
+		rc = msm_sensor_match_id(s_ctrl);
+	if (rc < 0)
+		goto probe_fail;
+
+	snprintf(s_ctrl->sensor_v4l2_subdev.name,
+		sizeof(s_ctrl->sensor_v4l2_subdev.name), "%s", id->name);
+	v4l2_i2c_subdev_init(&s_ctrl->sensor_v4l2_subdev, client,
+		s_ctrl->sensor_v4l2_subdev_ops);
+
+	msm_sensor_register(&s_ctrl->sensor_v4l2_subdev);
+	goto power_down;
+probe_fail:
+	pr_err("%s %s_i2c_probe failed\n", __func__, client->name);
+power_down:
+	if (rc > 0)
+		rc = 0;
+	s_ctrl->func_tbl->sensor_power_down(s_ctrl);
+	return rc;
+}
+
+int32_t msm_sensor_power(struct v4l2_subdev *sd, int on)
+{
+	int rc = 0;
+	struct msm_sensor_ctrl_t *s_ctrl = get_sctrl(sd);
+	mutex_lock(s_ctrl->msm_sensor_mutex);
+	if (on) {
+		rc = s_ctrl->func_tbl->sensor_power_up(s_ctrl);
+		if (rc < 0) {
+			pr_err("%s: %s power_up failed rc = %d\n", __func__,
+				s_ctrl->sensordata->sensor_name, rc);
+		} else {
+			if (s_ctrl->func_tbl->sensor_match_id)
+				rc = s_ctrl->func_tbl->sensor_match_id(s_ctrl);
+			else
+				rc = msm_sensor_match_id(s_ctrl);
+			if (rc < 0) {
+				pr_err("%s: %s match_id failed  rc=%d\n",
+					__func__,
+					s_ctrl->sensordata->sensor_name, rc);
+				if (s_ctrl->func_tbl->sensor_power_down(s_ctrl)
+					< 0)
+					pr_err("%s: %s power_down failed\n",
+					__func__,
+					s_ctrl->sensordata->sensor_name);
+			}
+		}
+	} else {
+		rc = s_ctrl->func_tbl->sensor_power_down(s_ctrl);
+	}
+	mutex_unlock(s_ctrl->msm_sensor_mutex);
+	return rc;
+}
+
+int32_t msm_sensor_v4l2_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+			   enum v4l2_mbus_pixelcode *code)
+{
+	struct msm_sensor_ctrl_t *s_ctrl = get_sctrl(sd);
+
+	if ((unsigned int)index >= s_ctrl->sensor_v4l2_subdev_info_size)
+		return -EINVAL;
+
+	*code = s_ctrl->sensor_v4l2_subdev_info[index].code;
+	return 0;
+}
+
+int32_t msm_sensor_v4l2_s_ctrl(struct v4l2_subdev *sd,
+	struct v4l2_control *ctrl)
+{
+	int rc = -1, i = 0;
+	struct msm_sensor_ctrl_t *s_ctrl =
+		(struct msm_sensor_ctrl_t *) sd->dev_priv;
+	struct msm_sensor_v4l2_ctrl_info_t *v4l2_ctrl =
+		s_ctrl->msm_sensor_v4l2_ctrl_info;
+
+	CDBG("%s\n", __func__);
+	CDBG("%d\n", ctrl->id);
+	if (v4l2_ctrl == NULL)
+		return rc;
+
+	for (i = 0; i < s_ctrl->num_v4l2_ctrl; i++) {
+		if (v4l2_ctrl[i].ctrl_id == ctrl->id) {
+			if (v4l2_ctrl[i].s_v4l2_ctrl != NULL) {
+				rc = v4l2_ctrl[i].s_v4l2_ctrl(
+					s_ctrl,
+					&s_ctrl->msm_sensor_v4l2_ctrl_info[i],
+					ctrl->value);
+			}
+			break;
+		}
+	}
+
+	return rc;
+}
+
+int32_t msm_sensor_v4l2_query_ctrl(
+	struct v4l2_subdev *sd, struct v4l2_queryctrl *qctrl)
+{
+	int rc = -1, i = 0;
+	struct msm_sensor_ctrl_t *s_ctrl =
+		(struct msm_sensor_ctrl_t *) sd->dev_priv;
+
+	CDBG("%s\n", __func__);
+	CDBG("%s id: %d\n", __func__, qctrl->id);
+
+	if (s_ctrl->msm_sensor_v4l2_ctrl_info == NULL)
+		return rc;
+
+	for (i = 0; i < s_ctrl->num_v4l2_ctrl; i++) {
+		if (s_ctrl->msm_sensor_v4l2_ctrl_info[i].ctrl_id == qctrl->id) {
+			qctrl->minimum =
+				s_ctrl->msm_sensor_v4l2_ctrl_info[i].min;
+			qctrl->maximum =
+				s_ctrl->msm_sensor_v4l2_ctrl_info[i].max;
+			qctrl->flags = 1;
+			rc = 0;
+			break;
+		}
+	}
+
+	return rc;
+}
+
+int msm_sensor_s_ctrl_by_enum(struct msm_sensor_ctrl_t *s_ctrl,
+		struct msm_sensor_v4l2_ctrl_info_t *ctrl_info, int value)
+{
+	int rc = 0;
+	CDBG("%s enter\n", __func__);
+	rc = msm_sensor_write_enum_conf_array(
+		s_ctrl->sensor_i2c_client,
+		ctrl_info->enum_cfg_settings, value);
+	return rc;
+}
+
+static int msm_sensor_debugfs_stream_s(void *data, u64 val)
+{
+	struct msm_sensor_ctrl_t *s_ctrl = (struct msm_sensor_ctrl_t *) data;
+	if (val)
+		s_ctrl->func_tbl->sensor_start_stream(s_ctrl);
+	else
+		s_ctrl->func_tbl->sensor_stop_stream(s_ctrl);
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(sensor_debugfs_stream, NULL,
+			msm_sensor_debugfs_stream_s, "%llu\n");
+
+static int msm_sensor_debugfs_test_s(void *data, u64 val)
+{
+	CDBG("val: %llu\n", val);
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(sensor_debugfs_test, NULL,
+			msm_sensor_debugfs_test_s, "%llu\n");
+
+int msm_sensor_enable_debugfs(struct msm_sensor_ctrl_t *s_ctrl)
+{
+	struct dentry *debugfs_base, *sensor_dir;
+	debugfs_base = debugfs_create_dir("msm_sensor", NULL);
+	if (!debugfs_base)
+		return -ENOMEM;
+
+	sensor_dir = debugfs_create_dir
+		(s_ctrl->sensordata->sensor_name, debugfs_base);
+	if (!sensor_dir)
+		return -ENOMEM;
+
+	if (!debugfs_create_file("stream", S_IRUGO | S_IWUSR, sensor_dir,
+			(void *) s_ctrl, &sensor_debugfs_stream))
+		return -ENOMEM;
+
+	if (!debugfs_create_file("test", S_IRUGO | S_IWUSR, sensor_dir,
+			(void *) s_ctrl, &sensor_debugfs_test))
+		return -ENOMEM;
+
+	return 0;
+}
diff --git a/drivers/media/video/msm/sensors/msm_sensor.h b/drivers/media/video/msm/sensors/msm_sensor.h
new file mode 100644
index 0000000..c5fbea2
--- /dev/null
+++ b/drivers/media/video/msm/sensors/msm_sensor.h
@@ -0,0 +1,255 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MSM_SENSOR_H
+#define MSM_SENSOR_H
+
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <mach/camera.h>
+#include <mach/gpio.h>
+#include <media/msm_camera.h>
+#include <media/v4l2-subdev.h>
+#include "msm_camera_i2c.h"
+#include "msm_camera_eeprom.h"
+#define Q8  0x00000100
+#define Q10 0x00000400
+
+#define MSM_SENSOR_MCLK_8HZ 8000000
+#define MSM_SENSOR_MCLK_16HZ 16000000
+#define MSM_SENSOR_MCLK_24HZ 24000000
+
+enum msm_sensor_reg_update {
+	/* Sensor egisters that need to be updated during initialization */
+	MSM_SENSOR_REG_INIT,
+	/* Sensor egisters that needs periodic I2C writes */
+	MSM_SENSOR_UPDATE_PERIODIC,
+	/* All the sensor Registers will be updated */
+	MSM_SENSOR_UPDATE_ALL,
+	/* Not valid update */
+	MSM_SENSOR_UPDATE_INVALID
+};
+
+enum msm_sensor_cam_mode_t {
+	MSM_SENSOR_MODE_2D_RIGHT,
+	MSM_SENSOR_MODE_2D_LEFT,
+	MSM_SENSOR_MODE_3D,
+	MSM_SENSOR_MODE_INVALID
+};
+
+struct msm_sensor_output_reg_addr_t {
+	uint16_t x_output;
+	uint16_t y_output;
+	uint16_t line_length_pclk;
+	uint16_t frame_length_lines;
+};
+
+struct msm_sensor_id_info_t {
+	uint16_t sensor_id_reg_addr;
+	uint16_t sensor_id;
+};
+
+struct msm_sensor_exp_gain_info_t {
+	uint16_t coarse_int_time_addr;
+	uint16_t global_gain_addr;
+	uint16_t vert_offset;
+};
+
+struct msm_sensor_reg_t {
+	enum msm_camera_i2c_data_type default_data_type;
+	struct msm_camera_i2c_reg_conf *start_stream_conf;
+	uint8_t start_stream_conf_size;
+	struct msm_camera_i2c_reg_conf *stop_stream_conf;
+	uint8_t stop_stream_conf_size;
+	struct msm_camera_i2c_reg_conf *group_hold_on_conf;
+	uint8_t group_hold_on_conf_size;
+	struct msm_camera_i2c_reg_conf *group_hold_off_conf;
+	uint8_t group_hold_off_conf_size;
+	struct msm_camera_i2c_conf_array *init_settings;
+	uint8_t init_size;
+	struct msm_camera_i2c_conf_array *mode_settings;
+	struct msm_sensor_output_info_t *output_settings;
+	uint8_t num_conf;
+};
+
+struct v4l2_subdev_info {
+	enum v4l2_mbus_pixelcode code;
+	enum v4l2_colorspace colorspace;
+	uint16_t fmt;
+	uint16_t order;
+};
+
+struct msm_sensor_ctrl_t;
+
+struct msm_sensor_v4l2_ctrl_info_t {
+	uint32_t ctrl_id;
+	int16_t min;
+	int16_t max;
+	int16_t step;
+	struct msm_camera_i2c_enum_conf_array *enum_cfg_settings;
+	int (*s_v4l2_ctrl) (struct msm_sensor_ctrl_t *,
+		struct msm_sensor_v4l2_ctrl_info_t *, int);
+};
+
+struct msm_sensor_fn_t {
+	void (*sensor_start_stream) (struct msm_sensor_ctrl_t *);
+	void (*sensor_stop_stream) (struct msm_sensor_ctrl_t *);
+	void (*sensor_group_hold_on) (struct msm_sensor_ctrl_t *);
+	void (*sensor_group_hold_off) (struct msm_sensor_ctrl_t *);
+
+	int32_t (*sensor_set_fps) (struct msm_sensor_ctrl_t *,
+			struct fps_cfg *);
+	int32_t (*sensor_write_exp_gain) (struct msm_sensor_ctrl_t *,
+			uint16_t, uint32_t);
+	int32_t (*sensor_write_snapshot_exp_gain) (struct msm_sensor_ctrl_t *,
+			uint16_t, uint32_t);
+	int32_t (*sensor_setting) (struct msm_sensor_ctrl_t *,
+			int update_type, int rt);
+	int32_t (*sensor_csi_setting) (struct msm_sensor_ctrl_t *,
+			int update_type, int rt);
+	int32_t (*sensor_set_sensor_mode)
+			(struct msm_sensor_ctrl_t *, int, int);
+	int32_t (*sensor_mode_init) (struct msm_sensor_ctrl_t *,
+		int, struct sensor_init_cfg *);
+	int32_t (*sensor_get_output_info) (struct msm_sensor_ctrl_t *,
+		struct sensor_output_info_t *);
+	int (*sensor_config) (struct msm_sensor_ctrl_t *, void __user *);
+	int (*sensor_power_down)
+		(struct msm_sensor_ctrl_t *);
+	int (*sensor_power_up) (struct msm_sensor_ctrl_t *);
+	int32_t (*sensor_match_id)(struct msm_sensor_ctrl_t *s_ctrl);
+	int (*sensor_adjust_frame_lines)
+		(struct msm_sensor_ctrl_t *s_ctrl, uint16_t res);
+};
+
+struct msm_sensor_ctrl_t {
+	struct  msm_camera_sensor_info *sensordata;
+	struct i2c_client *msm_sensor_client;
+	struct i2c_driver *sensor_i2c_driver;
+	struct msm_camera_i2c_client *sensor_i2c_client;
+	uint16_t sensor_i2c_addr;
+
+	struct msm_sensor_output_reg_addr_t *sensor_output_reg_addr;
+	struct msm_sensor_id_info_t *sensor_id_info;
+	struct msm_sensor_exp_gain_info_t *sensor_exp_gain_info;
+	struct msm_sensor_reg_t *msm_sensor_reg;
+	struct msm_sensor_v4l2_ctrl_info_t *msm_sensor_v4l2_ctrl_info;
+	uint16_t num_v4l2_ctrl;
+
+	uint16_t curr_line_length_pclk;
+	uint16_t curr_frame_length_lines;
+	uint16_t prev_gain;
+	uint16_t prev_line;
+
+	uint32_t fps_divider;
+	enum msm_sensor_resolution_t curr_res;
+	enum msm_sensor_cam_mode_t cam_mode;
+
+	struct mutex *msm_sensor_mutex;
+	struct msm_camera_csi2_params *curr_csi_params;
+	struct msm_camera_csi2_params **csi_params;
+	struct msm_camera_csi_params **csic_params;
+	struct msm_camera_csi_params *curr_csic_params;
+
+	struct v4l2_subdev sensor_v4l2_subdev;
+	struct v4l2_subdev_info *sensor_v4l2_subdev_info;
+	uint8_t sensor_v4l2_subdev_info_size;
+	struct v4l2_subdev_ops *sensor_v4l2_subdev_ops;
+	struct msm_sensor_fn_t *func_tbl;
+	struct regulator **reg_ptr;
+	struct clk *cam_clk;
+	long clk_rate;
+};
+
+void msm_sensor_start_stream(struct msm_sensor_ctrl_t *s_ctrl);
+void msm_sensor_stop_stream(struct msm_sensor_ctrl_t *s_ctrl);
+void msm_sensor_group_hold_on(struct msm_sensor_ctrl_t *s_ctrl);
+void msm_sensor_group_hold_off(struct msm_sensor_ctrl_t *s_ctrl);
+
+int32_t msm_sensor_set_fps(struct msm_sensor_ctrl_t *s_ctrl,
+			struct fps_cfg   *fps);
+int32_t msm_sensor_write_exp_gain1(struct msm_sensor_ctrl_t *s_ctrl,
+		uint16_t gain, uint32_t line);
+int32_t msm_sensor_write_exp_gain2(struct msm_sensor_ctrl_t *s_ctrl,
+		uint16_t gain, uint32_t line);
+int32_t msm_sensor_set_sensor_mode(struct msm_sensor_ctrl_t *s_ctrl,
+	int mode, int res);
+int32_t msm_sensor_mode_init(struct msm_sensor_ctrl_t *s_ctrl,
+			int mode, struct sensor_init_cfg *init_info);
+int32_t msm_sensor_get_output_info(struct msm_sensor_ctrl_t *,
+		struct sensor_output_info_t *);
+int32_t msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl,
+			void __user *argp);
+int32_t msm_sensor_power_up(struct msm_sensor_ctrl_t *s_ctrl);
+int32_t msm_sensor_power_down(struct msm_sensor_ctrl_t *s_ctrl);
+
+int32_t msm_sensor_match_id(struct msm_sensor_ctrl_t *s_ctrl);
+int msm_sensor_i2c_probe(struct i2c_client *client,
+	const struct i2c_device_id *id);
+int32_t msm_sensor_power(struct v4l2_subdev *sd, int on);
+
+int32_t msm_sensor_v4l2_s_ctrl(struct v4l2_subdev *sd,
+	struct v4l2_control *ctrl);
+
+int32_t msm_sensor_v4l2_query_ctrl(
+	struct v4l2_subdev *sd, struct v4l2_queryctrl *qctrl);
+
+int msm_sensor_s_ctrl_by_index(struct msm_sensor_ctrl_t *s_ctrl,
+	struct msm_sensor_v4l2_ctrl_info_t *ctrl_info, int value);
+
+int msm_sensor_s_ctrl_by_enum(struct msm_sensor_ctrl_t *s_ctrl,
+		struct msm_sensor_v4l2_ctrl_info_t *ctrl_info, int value);
+
+int msm_sensor_v4l2_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+			enum v4l2_mbus_pixelcode *code);
+
+int msm_sensor_write_init_settings(struct msm_sensor_ctrl_t *s_ctrl);
+int msm_sensor_write_res_settings
+	(struct msm_sensor_ctrl_t *s_ctrl, uint16_t res);
+
+int32_t msm_sensor_write_output_settings(struct msm_sensor_ctrl_t *s_ctrl,
+	uint16_t res);
+
+int32_t msm_sensor_adjust_frame_lines(struct msm_sensor_ctrl_t *s_ctrl,
+	uint16_t res);
+
+int32_t msm_sensor_setting(struct msm_sensor_ctrl_t *s_ctrl,
+			int update_type, int res);
+
+int32_t msm_sensor_setting1(struct msm_sensor_ctrl_t *s_ctrl,
+			int update_type, int res);
+
+int msm_sensor_enable_debugfs(struct msm_sensor_ctrl_t *s_ctrl);
+
+long msm_sensor_subdev_ioctl(struct v4l2_subdev *sd,
+			unsigned int cmd, void *arg);
+
+struct msm_sensor_ctrl_t *get_sctrl(struct v4l2_subdev *sd);
+
+#if defined(CONFIG_OV5647)
+	extern int lcd_camera_power_onoff(int on);
+#endif
+
+#define VIDIOC_MSM_SENSOR_CFG \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 10, void __user *)
+
+#define VIDIOC_MSM_SENSOR_RELEASE \
+	_IO('V', BASE_VIDIOC_PRIVATE + 11)
+
+#endif
diff --git a/drivers/media/video/msm/sensors/mt9e013_v4l2.c b/drivers/media/video/msm/sensors/mt9e013_v4l2.c
new file mode 100644
index 0000000..e6e2d52
--- /dev/null
+++ b/drivers/media/video/msm/sensors/mt9e013_v4l2.c
@@ -0,0 +1,513 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_sensor.h"
+#define SENSOR_NAME "mt9e013"
+#define PLATFORM_DRIVER_NAME "msm_camera_mt9e013"
+#define mt9e013_obj mt9e013_##obj
+
+DEFINE_MUTEX(mt9e013_mut);
+static struct msm_sensor_ctrl_t mt9e013_s_ctrl;
+
+static struct msm_camera_i2c_reg_conf mt9e013_groupon_settings[] = {
+	{0x0104, 0x01},
+};
+
+static struct msm_camera_i2c_reg_conf mt9e013_groupoff_settings[] = {
+	{0x0104, 0x00},
+};
+
+static struct msm_camera_i2c_reg_conf mt9e013_prev_settings[] = {
+	/*Output Size (1632x1224)*/
+	{0x0344, 0x0008},/*X_ADDR_START*/
+	{0x0348, 0x0CC9},/*X_ADDR_END*/
+	{0x0346, 0x0008},/*Y_ADDR_START*/
+	{0x034A, 0x0999},/*Y_ADDR_END*/
+	{0x034C, 0x0660},/*X_OUTPUT_SIZE*/
+	{0x034E, 0x04C8},/*Y_OUTPUT_SIZE*/
+	{0x306E, 0xFCB0},/*DATAPATH_SELECT*/
+	{0x3040, 0x04C3},/*READ_MODE*/
+	{0x3178, 0x0000},/*ANALOG_CONTROL5*/
+	{0x3ED0, 0x1E24},/*DAC_LD_4_5*/
+	{0x0400, 0x0002},/*SCALING_MODE*/
+	{0x0404, 0x0010},/*SCALE_M*/
+	/*Timing configuration*/
+	{0x0342, 0x1018},/*LINE_LENGTH_PCK*/
+	{0x0340, 0x055B},/*FRAME_LENGTH_LINES*/
+	{0x0202, 0x0557},/*COARSE_INTEGRATION_TIME*/
+	{0x3014, 0x0846},/*FINE_INTEGRATION_TIME_*/
+	{0x3010, 0x0130},/*FINE_CORRECTION*/
+};
+
+static struct msm_camera_i2c_reg_conf mt9e013_snap_settings[] = {
+	/*Output Size (3264x2448)*/
+	{0x0344, 0x0000},/*X_ADDR_START */
+	{0x0348, 0x0CCF},/*X_ADDR_END*/
+	{0x0346, 0x0000},/*Y_ADDR_START */
+	{0x034A, 0x099F},/*Y_ADDR_END*/
+	{0x034C, 0x0CD0},/*X_OUTPUT_SIZE*/
+	{0x034E, 0x09A0},/*Y_OUTPUT_SIZE*/
+	{0x306E, 0xFC80},/*DATAPATH_SELECT*/
+	{0x3040, 0x0041},/*READ_MODE*/
+	{0x3178, 0x0000},/*ANALOG_CONTROL5*/
+	{0x3ED0, 0x1E24},/*DAC_LD_4_5*/
+	{0x0400, 0x0000},/*SCALING_MODE*/
+	{0x0404, 0x0010},/*SCALE_M*/
+	/*Timing configuration*/
+	{0x0342, 0x13F8},/*LINE_LENGTH_PCK*/
+	{0x0340, 0x0A2F},/*FRAME_LENGTH_LINES*/
+	{0x0202, 0x0A1F},/*COARSE_INTEGRATION_TIME*/
+	{0x3014, 0x03F6},/*FINE_INTEGRATION_TIME_ */
+	{0x3010, 0x0078},/*FINE_CORRECTION*/
+};
+
+static struct msm_camera_i2c_reg_conf mt9e013_hfr60_settings[] = {
+	{0x0300, 0x0005},/*VT_PIX_CLK_DIV*/
+	{0x0302, 0x0001},/*VT_SYS_CLK_DIV*/
+	{0x0304, 0x0002},/*PRE_PLL_CLK_DIV*/
+	{0x0306, 0x0029},/*PLL_MULTIPLIER*/
+	{0x0308, 0x000A},/*OP_PIX_CLK_DIV*/
+	{0x030A, 0x0001},/*OP_SYS_CLK_DIV*/
+	{0x0344, 0x0008},/*X_ADDR_START*/
+	{0x0348, 0x0685},/*X_ADDR_END*/
+	{0x0346, 0x013a},/*Y_ADDR_START*/
+	{0x034A, 0x055B},/*Y_ADDR_END*/
+	{0x034C, 0x0340},/*X_OUTPUT_SIZE*/
+	{0x034E, 0x0212},/*Y_OUTPUT_SIZE*/
+	{0x306E, 0xFC80},/*DATAPATH_SELECT*/
+	{0x3040, 0x00C3},/*READ_MODE*/
+	{0x3178, 0x0000},/*ANALOG_CONTROL5*/
+	{0x3ED0, 0x1E24},/*DAC_LD_4_5*/
+	{0x0400, 0x0000},/*SCALING_MODE*/
+	{0x0404, 0x0010},/*SCALE_M*/
+	/*Timing configuration*/
+	{0x0342, 0x0970},/*LINE_LENGTH_PCK*/
+	{0x0340, 0x02A1},/*FRAME_LENGTH_LINES*/
+	{0x0202, 0x02A1},/*COARSE_INTEGRATION_TIME*/
+	{0x3014, 0x03F6},/*FINE_INTEGRATION_TIME_*/
+	{0x3010, 0x0078},/*FINE_CORRECTION*/
+};
+
+static struct msm_camera_i2c_reg_conf mt9e013_hfr90_settings[] = {
+	{0x0300, 0x0005},/*VT_PIX_CLK_DIV*/
+	{0x0302, 0x0001},/*VT_SYS_CLK_DIV*/
+	{0x0304, 0x0002},/*PRE_PLL_CLK_DIV*/
+	{0x0306, 0x003D},/*PLL_MULTIPLIER*/
+	{0x0308, 0x000A},/*OP_PIX_CLK_DIV*/
+	{0x030A, 0x0001},/*OP_SYS_CLK_DIV*/
+	{0x0344, 0x0008},/*X_ADDR_START*/
+	{0x0348, 0x0685},/*X_ADDR_END*/
+	{0x0346, 0x013a},/*Y_ADDR_START*/
+	{0x034A, 0x055B},/*Y_ADDR_END*/
+	{0x034C, 0x0340},/*X_OUTPUT_SIZE*/
+	{0x034E, 0x0212},/*Y_OUTPUT_SIZE*/
+	{0x306E, 0xFC80},/*DATAPATH_SELECT*/
+	{0x3040, 0x00C3},/*READ_MODE*/
+	{0x3178, 0x0000},/*ANALOG_CONTROL5*/
+	{0x3ED0, 0x1E24},/*DAC_LD_4_5*/
+	{0x0400, 0x0000},/*SCALING_MODE*/
+	{0x0404, 0x0010},/*SCALE_M*/
+	/*Timing configuration*/
+	{0x0342, 0x0970},/*LINE_LENGTH_PCK*/
+	{0x0340, 0x02A1},/*FRAME_LENGTH_LINES*/
+	{0x0202, 0x02A1},/*COARSE_INTEGRATION_TIME*/
+	{0x3014, 0x03F6},/*FINE_INTEGRATION_TIME_*/
+	{0x3010, 0x0078},/*FINE_CORRECTION*/
+};
+
+static struct msm_camera_i2c_reg_conf mt9e013_hfr120_settings[] = {
+	{0x0300, 0x0005},/*VT_PIX_CLK_DIV*/
+	{0x0302, 0x0001},/*VT_SYS_CLK_DIV*/
+	{0x0304, 0x0002},/*PRE_PLL_CLK_DIV*/
+	{0x0306, 0x0052},/*PLL_MULTIPLIER*/
+	{0x0308, 0x000A},/*OP_PIX_CLK_DIV*/
+	{0x030A, 0x0001},/*OP_SYS_CLK_DIV*/
+	{0x0344, 0x0008},/*X_ADDR_START*/
+	{0x0348, 0x0685},/*X_ADDR_END*/
+	{0x0346, 0x013a},/*Y_ADDR_START*/
+	{0x034A, 0x055B},/*Y_ADDR_END*/
+	{0x034C, 0x0340},/*X_OUTPUT_SIZE*/
+	{0x034E, 0x0212},/*Y_OUTPUT_SIZE*/
+	{0x306E, 0xFC80},/*DATAPATH_SELECT*/
+	{0x3040, 0x00C3},/*READ_MODE*/
+	{0x3178, 0x0000},/*ANALOG_CONTROL5*/
+	{0x3ED0, 0x1E24},/*DAC_LD_4_5*/
+	{0x0400, 0x0000},/*SCALING_MODE*/
+	{0x0404, 0x0010},/*SCALE_M*/
+	/*Timing configuration*/
+	{0x0342, 0x0970},/*LINE_LENGTH_PCK*/
+	{0x0340, 0x02A1},/*FRAME_LENGTH_LINES*/
+	{0x0202, 0x02A1},/*COARSE_INTEGRATION_TIME*/
+	{0x3014, 0x03F6},/*FINE_INTEGRATION_TIME_*/
+	{0x3010, 0x0078},/*FINE_CORRECTION*/
+};
+
+static struct msm_camera_i2c_reg_conf mt9e013_recommend_settings[] = {
+	/*Disable embedded data*/
+	{0x3064, 0x7800},/*SMIA_TEST*/
+	/*configure 2-lane MIPI*/
+	{0x31AE, 0x0202},/*SERIAL_FORMAT*/
+	{0x31B8, 0x0E3F},/*MIPI_TIMING_2*/
+	/*set data to RAW10 format*/
+	{0x0112, 0x0A0A},/*CCP_DATA_FORMAT*/
+	{0x30F0, 0x800D},/*VCM CONTROL*/
+
+	{0x3044, 0x0590},
+	{0x306E, 0xFC80},
+	{0x30B2, 0xC000},
+	{0x30D6, 0x0800},
+	{0x316C, 0xB42F},
+	{0x316E, 0x869A},
+	{0x3170, 0x210E},
+	{0x317A, 0x010E},
+	{0x31E0, 0x1FB9},
+	{0x31E6, 0x07FC},
+	{0x37C0, 0x0000},
+	{0x37C2, 0x0000},
+	{0x37C4, 0x0000},
+	{0x37C6, 0x0000},
+	{0x3E00, 0x0011},
+	{0x3E02, 0x8801},
+	{0x3E04, 0x2801},
+	{0x3E06, 0x8449},
+	{0x3E08, 0x6841},
+	{0x3E0A, 0x400C},
+	{0x3E0C, 0x1001},
+	{0x3E0E, 0x2603},
+	{0x3E10, 0x4B41},
+	{0x3E12, 0x4B24},
+	{0x3E14, 0xA3CF},
+	{0x3E16, 0x8802},
+	{0x3E18, 0x8401},
+	{0x3E1A, 0x8601},
+	{0x3E1C, 0x8401},
+	{0x3E1E, 0x840A},
+	{0x3E20, 0xFF00},
+	{0x3E22, 0x8401},
+	{0x3E24, 0x00FF},
+	{0x3E26, 0x0088},
+	{0x3E28, 0x2E8A},
+	{0x3E30, 0x0000},
+	{0x3E32, 0x8801},
+	{0x3E34, 0x4029},
+	{0x3E36, 0x00FF},
+	{0x3E38, 0x8469},
+	{0x3E3A, 0x00FF},
+	{0x3E3C, 0x2801},
+	{0x3E3E, 0x3E2A},
+	{0x3E40, 0x1C01},
+	{0x3E42, 0xFF84},
+	{0x3E44, 0x8401},
+	{0x3E46, 0x0C01},
+	{0x3E48, 0x8401},
+	{0x3E4A, 0x00FF},
+	{0x3E4C, 0x8402},
+	{0x3E4E, 0x8984},
+	{0x3E50, 0x6628},
+	{0x3E52, 0x8340},
+	{0x3E54, 0x00FF},
+	{0x3E56, 0x4A42},
+	{0x3E58, 0x2703},
+	{0x3E5A, 0x6752},
+	{0x3E5C, 0x3F2A},
+	{0x3E5E, 0x846A},
+	{0x3E60, 0x4C01},
+	{0x3E62, 0x8401},
+	{0x3E66, 0x3901},
+	{0x3E90, 0x2C01},
+	{0x3E98, 0x2B02},
+	{0x3E92, 0x2A04},
+	{0x3E94, 0x2509},
+	{0x3E96, 0x0000},
+	{0x3E9A, 0x2905},
+	{0x3E9C, 0x00FF},
+	{0x3ECC, 0x00EB},
+	{0x3ED0, 0x1E24},
+	{0x3ED4, 0xAFC4},
+	{0x3ED6, 0x909B},
+	{0x3EE0, 0x2424},
+	{0x3EE2, 0x9797},
+	{0x3EE4, 0xC100},
+	{0x3EE6, 0x0540},
+	{0x3174, 0x8000},
+	/* PLL settings */
+	{0x0300, 0x0004},/*VT_PIX_CLK_DIV*/
+	{0x0302, 0x0001},/*VT_SYS_CLK_DIV*/
+	{0x0304, 0x0002},/*PRE_PLL_CLK_DIV*/
+	{0x0306, 0x003A},/*PLL_MULTIPLIER*/
+	{0x0308, 0x000A},/*OP_PIX_CLK_DIV*/
+	{0x030A, 0x0001},/*OP_SYS_CLK_DIV*/
+};
+
+static struct v4l2_subdev_info mt9e013_subdev_info[] = {
+	{
+	.code   = V4L2_MBUS_FMT_SBGGR10_1X10,
+	.colorspace = V4L2_COLORSPACE_JPEG,
+	.fmt    = 1,
+	.order    = 0,
+	},
+	/* more can be supported, to be added later */
+};
+
+static struct msm_camera_i2c_conf_array mt9e013_init_conf[] = {
+	{&mt9e013_recommend_settings[0],
+	ARRAY_SIZE(mt9e013_recommend_settings), 0, MSM_CAMERA_I2C_WORD_DATA}
+};
+
+static struct msm_camera_i2c_conf_array mt9e013_confs[] = {
+	{&mt9e013_snap_settings[0],
+	ARRAY_SIZE(mt9e013_snap_settings), 0, MSM_CAMERA_I2C_WORD_DATA},
+	{&mt9e013_prev_settings[0],
+	ARRAY_SIZE(mt9e013_prev_settings), 0, MSM_CAMERA_I2C_WORD_DATA},
+	{&mt9e013_hfr60_settings[0],
+	ARRAY_SIZE(mt9e013_hfr60_settings), 0, MSM_CAMERA_I2C_WORD_DATA},
+	{&mt9e013_hfr90_settings[0],
+	ARRAY_SIZE(mt9e013_hfr90_settings), 0, MSM_CAMERA_I2C_WORD_DATA},
+	{&mt9e013_hfr120_settings[0],
+	ARRAY_SIZE(mt9e013_hfr120_settings), 0, MSM_CAMERA_I2C_WORD_DATA},
+};
+
+static struct msm_sensor_output_info_t mt9e013_dimensions[] = {
+	{
+		.x_output = 0xCD0,
+		.y_output = 0x9A0,
+		.line_length_pclk = 0x13F8,
+		.frame_length_lines = 0xA2F,
+		.vt_pixel_clk = 174000000,
+		.op_pixel_clk = 174000000,
+		.binning_factor = 1,
+	},
+	{
+		.x_output = 0x660,
+		.y_output = 0x4C8,
+		.line_length_pclk = 0x1018,
+		.frame_length_lines = 0x55B,
+		.vt_pixel_clk = 174000000,
+		.op_pixel_clk = 174000000,
+		.binning_factor = 1,
+	},
+	{
+		.x_output = 0x340,
+		.y_output = 0x212,
+		.line_length_pclk = 0x970,
+		.frame_length_lines = 0x2A1,
+		.vt_pixel_clk = 98400000,
+		.op_pixel_clk = 98400000,
+		.binning_factor = 1,
+	},
+	{
+		.x_output = 0x340,
+		.y_output = 0x212,
+		.line_length_pclk = 0x970,
+		.frame_length_lines = 0x2A1,
+		.vt_pixel_clk = 146400000,
+		.op_pixel_clk = 146400000,
+		.binning_factor = 1,
+	},
+	{
+		.x_output = 0x340,
+		.y_output = 0x212,
+		.line_length_pclk = 0x970,
+		.frame_length_lines = 0x2A1,
+		.vt_pixel_clk = 196800000,
+		.op_pixel_clk = 196800000,
+		.binning_factor = 1,
+	},
+};
+
+static struct msm_camera_csi_params mt9e013_csi_params = {
+	.data_format = CSI_10BIT,
+	.lane_cnt    = 2,
+	.lane_assign = 0xe4,
+	.dpcm_scheme = 0,
+	.settle_cnt  = 0x18,
+};
+
+static struct msm_camera_csi_params *mt9e013_csi_params_array[] = {
+	&mt9e013_csi_params,
+	&mt9e013_csi_params,
+	&mt9e013_csi_params,
+	&mt9e013_csi_params,
+	&mt9e013_csi_params,
+};
+
+static struct msm_sensor_output_reg_addr_t mt9e013_reg_addr = {
+	.x_output = 0x34C,
+	.y_output = 0x34E,
+	.line_length_pclk = 0x342,
+	.frame_length_lines = 0x340,
+};
+
+static struct msm_sensor_id_info_t mt9e013_id_info = {
+	.sensor_id_reg_addr = 0x0,
+	.sensor_id = 0x4B00,
+};
+
+static struct msm_sensor_exp_gain_info_t mt9e013_exp_gain_info = {
+	.coarse_int_time_addr = 0x202,
+	.global_gain_addr = 0x305E,
+	.vert_offset = 0,
+};
+
+static int32_t mt9e013_write_exp_gain(struct msm_sensor_ctrl_t *s_ctrl,
+		uint16_t gain, uint32_t line)
+{
+	uint32_t fl_lines;
+	fl_lines =
+		(s_ctrl->curr_frame_length_lines * s_ctrl->fps_divider) / Q10;
+
+	s_ctrl->func_tbl->sensor_group_hold_on(s_ctrl);
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->global_gain_addr, gain | 0x1000,
+		MSM_CAMERA_I2C_WORD_DATA);
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->coarse_int_time_addr, line,
+		MSM_CAMERA_I2C_WORD_DATA);
+	s_ctrl->func_tbl->sensor_group_hold_off(s_ctrl);
+	return 0;
+}
+
+static int32_t mt9e013_write_exp_snapshot_gain(struct msm_sensor_ctrl_t *s_ctrl,
+		uint16_t gain, uint32_t line)
+{
+	uint32_t fl_lines;
+	fl_lines =
+		(s_ctrl->curr_frame_length_lines * s_ctrl->fps_divider) / Q10;
+
+	s_ctrl->func_tbl->sensor_group_hold_on(s_ctrl);
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->global_gain_addr, gain | 0x1000,
+		MSM_CAMERA_I2C_WORD_DATA);
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->coarse_int_time_addr, line,
+		MSM_CAMERA_I2C_WORD_DATA);
+	s_ctrl->func_tbl->sensor_group_hold_off(s_ctrl);
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		0x301A, (0x065C|0x2), MSM_CAMERA_I2C_WORD_DATA);
+
+	return 0;
+}
+static void mt9e013_start_stream(struct msm_sensor_ctrl_t *s_ctrl)
+{
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		0x301A, 0x8250, MSM_CAMERA_I2C_WORD_DATA);
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		0x301A, 0x8650, MSM_CAMERA_I2C_WORD_DATA);
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		0x301A, 0x8658, MSM_CAMERA_I2C_WORD_DATA);
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		0x0104, 0x00, MSM_CAMERA_I2C_BYTE_DATA);
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		0x301A, 0x065C, MSM_CAMERA_I2C_WORD_DATA);
+}
+
+static void mt9e013_stop_stream(struct msm_sensor_ctrl_t *s_ctrl)
+{
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		0x301A, 0x0058, MSM_CAMERA_I2C_WORD_DATA);
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		0x301A, 0x0050, MSM_CAMERA_I2C_WORD_DATA);
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		0x0104, 0x01, MSM_CAMERA_I2C_BYTE_DATA);
+}
+
+static const struct i2c_device_id mt9e013_i2c_id[] = {
+	{SENSOR_NAME, (kernel_ulong_t)&mt9e013_s_ctrl},
+	{ }
+};
+
+static struct i2c_driver mt9e013_i2c_driver = {
+	.id_table = mt9e013_i2c_id,
+	.probe  = msm_sensor_i2c_probe,
+	.driver = {
+		.name = SENSOR_NAME,
+	},
+};
+
+static struct msm_camera_i2c_client mt9e013_sensor_i2c_client = {
+	.addr_type = MSM_CAMERA_I2C_WORD_ADDR,
+};
+
+static int __init msm_sensor_init_module(void)
+{
+	return i2c_add_driver(&mt9e013_i2c_driver);
+}
+
+static struct v4l2_subdev_core_ops mt9e013_subdev_core_ops = {
+	.ioctl = msm_sensor_subdev_ioctl,
+	.s_power = msm_sensor_power,
+};
+
+static struct v4l2_subdev_video_ops mt9e013_subdev_video_ops = {
+	.enum_mbus_fmt = msm_sensor_v4l2_enum_fmt,
+};
+
+static struct v4l2_subdev_ops mt9e013_subdev_ops = {
+	.core = &mt9e013_subdev_core_ops,
+	.video  = &mt9e013_subdev_video_ops,
+};
+
+static struct msm_sensor_fn_t mt9e013_func_tbl = {
+	.sensor_start_stream = mt9e013_start_stream,
+	.sensor_stop_stream = mt9e013_stop_stream,
+	.sensor_group_hold_on = msm_sensor_group_hold_on,
+	.sensor_group_hold_off = msm_sensor_group_hold_off,
+	.sensor_set_fps = msm_sensor_set_fps,
+	.sensor_write_exp_gain = mt9e013_write_exp_gain,
+	.sensor_write_snapshot_exp_gain = mt9e013_write_exp_snapshot_gain,
+	.sensor_csi_setting = msm_sensor_setting1,
+	.sensor_set_sensor_mode = msm_sensor_set_sensor_mode,
+	.sensor_mode_init = msm_sensor_mode_init,
+	.sensor_get_output_info = msm_sensor_get_output_info,
+	.sensor_config = msm_sensor_config,
+	.sensor_power_up = msm_sensor_power_up,
+	.sensor_power_down = msm_sensor_power_down,
+};
+
+static struct msm_sensor_reg_t mt9e013_regs = {
+	.default_data_type = MSM_CAMERA_I2C_BYTE_DATA,
+	.group_hold_on_conf = mt9e013_groupon_settings,
+	.group_hold_on_conf_size = ARRAY_SIZE(mt9e013_groupon_settings),
+	.group_hold_off_conf = mt9e013_groupoff_settings,
+	.group_hold_off_conf_size =
+		ARRAY_SIZE(mt9e013_groupoff_settings),
+	.init_settings = &mt9e013_init_conf[0],
+	.init_size = ARRAY_SIZE(mt9e013_init_conf),
+	.mode_settings = &mt9e013_confs[0],
+	.output_settings = &mt9e013_dimensions[0],
+	.num_conf = ARRAY_SIZE(mt9e013_confs),
+};
+
+static struct msm_sensor_ctrl_t mt9e013_s_ctrl = {
+	.msm_sensor_reg = &mt9e013_regs,
+	.sensor_i2c_client = &mt9e013_sensor_i2c_client,
+	.sensor_i2c_addr = 0x6C,
+	.sensor_output_reg_addr = &mt9e013_reg_addr,
+	.sensor_id_info = &mt9e013_id_info,
+	.sensor_exp_gain_info = &mt9e013_exp_gain_info,
+	.cam_mode = MSM_SENSOR_MODE_INVALID,
+	.csic_params = &mt9e013_csi_params_array[0],
+	.msm_sensor_mutex = &mt9e013_mut,
+	.sensor_i2c_driver = &mt9e013_i2c_driver,
+	.sensor_v4l2_subdev_info = mt9e013_subdev_info,
+	.sensor_v4l2_subdev_info_size = ARRAY_SIZE(mt9e013_subdev_info),
+	.sensor_v4l2_subdev_ops = &mt9e013_subdev_ops,
+	.func_tbl = &mt9e013_func_tbl,
+	.clk_rate = MSM_SENSOR_MCLK_24HZ,
+};
+
+module_init(msm_sensor_init_module);
+MODULE_DESCRIPTION("Aptina 8MP Bayer sensor driver");
+MODULE_LICENSE("GPL v2");
+
+
diff --git a/drivers/media/video/msm/sensors/mt9m114_v4l2.c b/drivers/media/video/msm/sensors/mt9m114_v4l2.c
new file mode 100644
index 0000000..2184806
--- /dev/null
+++ b/drivers/media/video/msm/sensors/mt9m114_v4l2.c
@@ -0,0 +1,1304 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_sensor.h"
+#define SENSOR_NAME "mt9m114"
+#define PLATFORM_DRIVER_NAME "msm_camera_mt9m114"
+#define mt9m114_obj mt9m114_##obj
+
+/* Sysctl registers */
+#define MT9M114_COMMAND_REGISTER                0x0080
+#define MT9M114_COMMAND_REGISTER_APPLY_PATCH    (1 << 0)
+#define MT9M114_COMMAND_REGISTER_SET_STATE      (1 << 1)
+#define MT9M114_COMMAND_REGISTER_REFRESH        (1 << 2)
+#define MT9M114_COMMAND_REGISTER_WAIT_FOR_EVENT (1 << 3)
+#define MT9M114_COMMAND_REGISTER_OK             (1 << 15)
+
+DEFINE_MUTEX(mt9m114_mut);
+static struct msm_sensor_ctrl_t mt9m114_s_ctrl;
+
+static struct msm_camera_i2c_reg_conf mt9m114_720p_settings[] = {
+	{0xdc00, 0x50, MSM_CAMERA_I2C_BYTE_DATA, MSM_CAMERA_I2C_CMD_WRITE},
+	{MT9M114_COMMAND_REGISTER, MT9M114_COMMAND_REGISTER_SET_STATE,
+		MSM_CAMERA_I2C_UNSET_WORD_MASK, MSM_CAMERA_I2C_CMD_POLL},
+	{MT9M114_COMMAND_REGISTER, (MT9M114_COMMAND_REGISTER_OK |
+		MT9M114_COMMAND_REGISTER_SET_STATE), MSM_CAMERA_I2C_WORD_DATA,
+		MSM_CAMERA_I2C_CMD_WRITE},
+	{MT9M114_COMMAND_REGISTER, MT9M114_COMMAND_REGISTER_SET_STATE,
+		MSM_CAMERA_I2C_UNSET_WORD_MASK, MSM_CAMERA_I2C_CMD_POLL},
+	{0xDC01, 0x52, MSM_CAMERA_I2C_BYTE_DATA, MSM_CAMERA_I2C_CMD_POLL},
+
+	{0x098E, 0, MSM_CAMERA_I2C_BYTE_DATA},
+	{0xC800, 0x007C,},/*y_addr_start = 124*/
+	{0xC802, 0x0004,},/*x_addr_start = 4*/
+	{0xC804, 0x0353,},/*y_addr_end = 851*/
+	{0xC806, 0x050B,},/*x_addr_end = 1291*/
+	{0xC808, 0x02DC,},/*pixclk = 48000000*/
+	{0xC80A, 0x6C00,},/*pixclk = 48000000*/
+	{0xC80C, 0x0001,},/*row_speed = 1*/
+	{0xC80E, 0x00DB,},/*fine_integ_time_min = 219*/
+	{0xC810, 0x05BD,},/*fine_integ_time_max = 1469*/
+	{0xC812, 0x03E8,},/*frame_length_lines = 1000*/
+	{0xC814, 0x0640,},/*line_length_pck = 1600*/
+	{0xC816, 0x0060,},/*fine_correction = 96*/
+	{0xC818, 0x02D3,},/*cpipe_last_row = 723*/
+	{0xC826, 0x0020,},/*reg_0_data = 32*/
+	{0xC834, 0x0000,},/*sensor_control_read_mode = 0*/
+	{0xC854, 0x0000,},/*crop_window_xoffset = 0*/
+	{0xC856, 0x0000,},/*crop_window_yoffset = 0*/
+	{0xC858, 0x0500,},/*crop_window_width = 1280*/
+	{0xC85A, 0x02D0,},/*crop_window_height = 720*/
+	{0xC85C, 0x03, MSM_CAMERA_I2C_BYTE_DATA},  /*crop_cropmode = 3*/
+	{0xC868, 0x0500,},/*output_width = 1280*/
+	{0xC86A, 0x02D0,},/*output_height = 720*/
+	{0xC878, 0x00, MSM_CAMERA_I2C_BYTE_DATA},  /*aet_aemode = 0*/
+	{0xC88C, 0x1E00,},/*aet_max_frame_rate = 7680*/
+	{0xC88E, 0x1E00,},/*aet_min_frame_rate = 7680*/
+	{0xC914, 0x0000,},/*stat_awb_window_xstart = 0*/
+	{0xC916, 0x0000,},/*stat_awb_window_ystart = 0*/
+	{0xC918, 0x04FF,},/*stat_awb_window_xend = 1279*/
+	{0xC91A, 0x02CF,},/*stat_awb_window_yend = 719*/
+	{0xC91C, 0x0000,},/*stat_ae_window_xstart = 0*/
+	{0xC91E, 0x0000,},/*stat_ae_window_ystart = 0*/
+	{0xC920, 0x00FF,},/*stat_ae_window_xend = 255*/
+	{0xC922, 0x008F,},/*stat_ae_window_yend = 143*/
+};
+
+static struct msm_camera_i2c_reg_conf mt9m114_recommend_settings[] = {
+	{0x301A, 0x0200, MSM_CAMERA_I2C_SET_WORD_MASK},
+	{0x098E, 0, MSM_CAMERA_I2C_BYTE_DATA},
+	/*cam_sysctl_pll_enable = 1*/
+	{0xC97E, 0x01, MSM_CAMERA_I2C_BYTE_DATA},
+	/*cam_sysctl_pll_divider_m_n = 288*/
+	{0xC980, 0x0120,},
+	/*cam_sysctl_pll_divider_p = 1792*/
+	{0xC982, 0x0700,},
+	/*output_control = 32769*/
+	{0xC984, 0x8001,},
+	/*mipi_timing_t_hs_zero = 3840*/
+	{0xC988, 0x0F00,},
+	/*mipi_timing_t_hs_exit_hs_trail = 2823*/
+	{0xC98A, 0x0B07,},
+	/*mipi_timing_t_clk_post_clk_pre = 3329*/
+	{0xC98C, 0x0D01,},
+	/*mipi_timing_t_clk_trail_clk_zero = 1821*/
+	{0xC98E, 0x071D,},
+	/*mipi_timing_t_lpx = 6*/
+	{0xC990, 0x0006,},
+	/*mipi_timing_init_timing = 2572*/
+	{0xC992, 0x0A0C,},
+	{0xC800, 0x007C,},/*y_addr_start = 124*/
+	{0xC802, 0x0004,},/*x_addr_start = 4*/
+	{0xC804, 0x0353,},/*y_addr_end = 851*/
+	{0xC806, 0x050B,},/*x_addr_end = 1291*/
+	{0xC808, 0x02DC,},/*pixclk = 48000000*/
+	{0xC80A, 0x6C00,},/*pixclk = 48000000*/
+	{0xC80C, 0x0001,},/*row_speed = 1*/
+	{0xC80E, 0x00DB,},/*fine_integ_time_min = 219*/
+	{0xC810, 0x05BD,},/*fine_integ_time_max = 1469*/
+	{0xC812, 0x03E8,},/*frame_length_lines = 1000*/
+	{0xC814, 0x0640,},/*line_length_pck = 1600*/
+	{0xC816, 0x0060,},/*fine_correction = 96*/
+	{0xC818, 0x02D3,},/*cpipe_last_row = 723*/
+	{0xC826, 0x0020,},/*reg_0_data = 32*/
+	{0xC834, 0x0000,},/*sensor_control_read_mode = 0*/
+	{0xC854, 0x0000,},/*crop_window_xoffset = 0*/
+	{0xC856, 0x0000,},/*crop_window_yoffset = 0*/
+	{0xC858, 0x0500,},/*crop_window_width = 1280*/
+	{0xC85A, 0x02D0,},/*crop_window_height = 720*/
+	{0xC85C, 0x03, MSM_CAMERA_I2C_BYTE_DATA},  /*crop_cropmode = 3*/
+	{0xC868, 0x0500,},/*output_width = 1280*/
+	{0xC86A, 0x02D0,},/*output_height = 720*/
+	{0xC878, 0x00, MSM_CAMERA_I2C_BYTE_DATA},  /*aet_aemode = 0*/
+	{0xC88C, 0x1E00,},/*aet_max_frame_rate = 7680*/
+	{0xC88E, 0x1E00,},/*aet_min_frame_rate = 7680*/
+	{0xC914, 0x0000,},/*stat_awb_window_xstart = 0*/
+	{0xC916, 0x0000,},/*stat_awb_window_ystart = 0*/
+	{0xC918, 0x04FF,},/*stat_awb_window_xend = 1279*/
+	{0xC91A, 0x02CF,},/*stat_awb_window_yend = 719*/
+	{0xC91C, 0x0000,},/*stat_ae_window_xstart = 0*/
+	{0xC91E, 0x0000,},/*stat_ae_window_ystart = 0*/
+	{0xC920, 0x00FF,},/*stat_ae_window_xend = 255*/
+	{0xC922, 0x008F,},/*stat_ae_window_yend = 143*/
+
+	/*Sensor optimization*/
+	{0x316A, 0x8270,},
+	{0x316C, 0x8270,},
+	{0x3ED0, 0x2305,},
+	{0x3ED2, 0x77CF,},
+	{0x316E, 0x8202,},
+	{0x3180, 0x87FF,},
+	{0x30D4, 0x6080,},
+	{0xA802, 0x0008,},/*AE_TRACK_MODE*/
+	{0x3E14, 0xFF39,},
+	{0x0982, 0x0001,},/*ACCESS_CTL_STAT*/
+	{0x098A, 0x5000,},/*PHYSICAL_ADDRESS_ACCESS*/
+	{0xD000, 0x70CF,},
+	{0xD002, 0xFFFF,},
+	{0xD004, 0xC5D4,},
+	{0xD006, 0x903A,},
+	{0xD008, 0x2144,},
+	{0xD00A, 0x0C00,},
+	{0xD00C, 0x2186,},
+	{0xD00E, 0x0FF3,},
+	{0xD010, 0xB844,},
+	{0xD012, 0xB948,},
+	{0xD014, 0xE082,},
+	{0xD016, 0x20CC,},
+	{0xD018, 0x80E2,},
+	{0xD01A, 0x21CC,},
+	{0xD01C, 0x80A2,},
+	{0xD01E, 0x21CC,},
+	{0xD020, 0x80E2,},
+	{0xD022, 0xF404,},
+	{0xD024, 0xD801,},
+	{0xD026, 0xF003,},
+	{0xD028, 0xD800,},
+	{0xD02A, 0x7EE0,},
+	{0xD02C, 0xC0F1,},
+	{0xD02E, 0x08BA,},
+	{0xD030, 0x0600,},
+	{0xD032, 0xC1A1,},
+	{0xD034, 0x76CF,},
+	{0xD036, 0xFFFF,},
+	{0xD038, 0xC130,},
+	{0xD03A, 0x6E04,},
+	{0xD03C, 0xC040,},
+	{0xD03E, 0x71CF,},
+	{0xD040, 0xFFFF,},
+	{0xD042, 0xC790,},
+	{0xD044, 0x8103,},
+	{0xD046, 0x77CF,},
+	{0xD048, 0xFFFF,},
+	{0xD04A, 0xC7C0,},
+	{0xD04C, 0xE001,},
+	{0xD04E, 0xA103,},
+	{0xD050, 0xD800,},
+	{0xD052, 0x0C6A,},
+	{0xD054, 0x04E0,},
+	{0xD056, 0xB89E,},
+	{0xD058, 0x7508,},
+	{0xD05A, 0x8E1C,},
+	{0xD05C, 0x0809,},
+	{0xD05E, 0x0191,},
+	{0xD060, 0xD801,},
+	{0xD062, 0xAE1D,},
+	{0xD064, 0xE580,},
+	{0xD066, 0x20CA,},
+	{0xD068, 0x0022,},
+	{0xD06A, 0x20CF,},
+	{0xD06C, 0x0522,},
+	{0xD06E, 0x0C5C,},
+	{0xD070, 0x04E2,},
+	{0xD072, 0x21CA,},
+	{0xD074, 0x0062,},
+	{0xD076, 0xE580,},
+	{0xD078, 0xD901,},
+	{0xD07A, 0x79C0,},
+	{0xD07C, 0xD800,},
+	{0xD07E, 0x0BE6,},
+	{0xD080, 0x04E0,},
+	{0xD082, 0xB89E,},
+	{0xD084, 0x70CF,},
+	{0xD086, 0xFFFF,},
+	{0xD088, 0xC8D4,},
+	{0xD08A, 0x9002,},
+	{0xD08C, 0x0857,},
+	{0xD08E, 0x025E,},
+	{0xD090, 0xFFDC,},
+	{0xD092, 0xE080,},
+	{0xD094, 0x25CC,},
+	{0xD096, 0x9022,},
+	{0xD098, 0xF225,},
+	{0xD09A, 0x1700,},
+	{0xD09C, 0x108A,},
+	{0xD09E, 0x73CF,},
+	{0xD0A0, 0xFF00,},
+	{0xD0A2, 0x3174,},
+	{0xD0A4, 0x9307,},
+	{0xD0A6, 0x2A04,},
+	{0xD0A8, 0x103E,},
+	{0xD0AA, 0x9328,},
+	{0xD0AC, 0x2942,},
+	{0xD0AE, 0x7140,},
+	{0xD0B0, 0x2A04,},
+	{0xD0B2, 0x107E,},
+	{0xD0B4, 0x9349,},
+	{0xD0B6, 0x2942,},
+	{0xD0B8, 0x7141,},
+	{0xD0BA, 0x2A04,},
+	{0xD0BC, 0x10BE,},
+	{0xD0BE, 0x934A,},
+	{0xD0C0, 0x2942,},
+	{0xD0C2, 0x714B,},
+	{0xD0C4, 0x2A04,},
+	{0xD0C6, 0x10BE,},
+	{0xD0C8, 0x130C,},
+	{0xD0CA, 0x010A,},
+	{0xD0CC, 0x2942,},
+	{0xD0CE, 0x7142,},
+	{0xD0D0, 0x2250,},
+	{0xD0D2, 0x13CA,},
+	{0xD0D4, 0x1B0C,},
+	{0xD0D6, 0x0284,},
+	{0xD0D8, 0xB307,},
+	{0xD0DA, 0xB328,},
+	{0xD0DC, 0x1B12,},
+	{0xD0DE, 0x02C4,},
+	{0xD0E0, 0xB34A,},
+	{0xD0E2, 0xED88,},
+	{0xD0E4, 0x71CF,},
+	{0xD0E6, 0xFF00,},
+	{0xD0E8, 0x3174,},
+	{0xD0EA, 0x9106,},
+	{0xD0EC, 0xB88F,},
+	{0xD0EE, 0xB106,},
+	{0xD0F0, 0x210A,},
+	{0xD0F2, 0x8340,},
+	{0xD0F4, 0xC000,},
+	{0xD0F6, 0x21CA,},
+	{0xD0F8, 0x0062,},
+	{0xD0FA, 0x20F0,},
+	{0xD0FC, 0x0040,},
+	{0xD0FE, 0x0B02,},
+	{0xD100, 0x0320,},
+	{0xD102, 0xD901,},
+	{0xD104, 0x07F1,},
+	{0xD106, 0x05E0,},
+	{0xD108, 0xC0A1,},
+	{0xD10A, 0x78E0,},
+	{0xD10C, 0xC0F1,},
+	{0xD10E, 0x71CF,},
+	{0xD110, 0xFFFF,},
+	{0xD112, 0xC7C0,},
+	{0xD114, 0xD840,},
+	{0xD116, 0xA900,},
+	{0xD118, 0x71CF,},
+	{0xD11A, 0xFFFF,},
+	{0xD11C, 0xD02C,},
+	{0xD11E, 0xD81E,},
+	{0xD120, 0x0A5A,},
+	{0xD122, 0x04E0,},
+	{0xD124, 0xDA00,},
+	{0xD126, 0xD800,},
+	{0xD128, 0xC0D1,},
+	{0xD12A, 0x7EE0,},
+	{0x098E, 0x0000,},
+
+	{0x0982, 0x0001,},
+	{0x098A, 0x5C10,},
+	{0xDC10, 0xC0F1,},
+	{0xDC12, 0x0CDA,},
+	{0xDC14, 0x0580,},
+	{0xDC16, 0x76CF,},
+	{0xDC18, 0xFF00,},
+	{0xDC1A, 0x2184,},
+	{0xDC1C, 0x9624,},
+	{0xDC1E, 0x218C,},
+	{0xDC20, 0x8FC3,},
+	{0xDC22, 0x75CF,},
+	{0xDC24, 0xFFFF,},
+	{0xDC26, 0xE058,},
+	{0xDC28, 0xF686,},
+	{0xDC2A, 0x1550,},
+	{0xDC2C, 0x1080,},
+	{0xDC2E, 0xE001,},
+	{0xDC30, 0x1D50,},
+	{0xDC32, 0x1002,},
+	{0xDC34, 0x1552,},
+	{0xDC36, 0x1100,},
+	{0xDC38, 0x6038,},
+	{0xDC3A, 0x1D52,},
+	{0xDC3C, 0x1004,},
+	{0xDC3E, 0x1540,},
+	{0xDC40, 0x1080,},
+	{0xDC42, 0x081B,},
+	{0xDC44, 0x00D1,},
+	{0xDC46, 0x8512,},
+	{0xDC48, 0x1000,},
+	{0xDC4A, 0x00C0,},
+	{0xDC4C, 0x7822,},
+	{0xDC4E, 0x2089,},
+	{0xDC50, 0x0FC1,},
+	{0xDC52, 0x2008,},
+	{0xDC54, 0x0F81,},
+	{0xDC56, 0xFFFF,},
+	{0xDC58, 0xFF80,},
+	{0xDC5A, 0x8512,},
+	{0xDC5C, 0x1801,},
+	{0xDC5E, 0x0052,},
+	{0xDC60, 0xA512,},
+	{0xDC62, 0x1544,},
+	{0xDC64, 0x1080,},
+	{0xDC66, 0xB861,},
+	{0xDC68, 0x262F,},
+	{0xDC6A, 0xF007,},
+	{0xDC6C, 0x1D44,},
+	{0xDC6E, 0x1002,},
+	{0xDC70, 0x20CA,},
+	{0xDC72, 0x0021,},
+	{0xDC74, 0x20CF,},
+	{0xDC76, 0x04E1,},
+	{0xDC78, 0x0850,},
+	{0xDC7A, 0x04A1,},
+	{0xDC7C, 0x21CA,},
+	{0xDC7E, 0x0021,},
+	{0xDC80, 0x1542,},
+	{0xDC82, 0x1140,},
+	{0xDC84, 0x8D2C,},
+	{0xDC86, 0x6038,},
+	{0xDC88, 0x1D42,},
+	{0xDC8A, 0x1004,},
+	{0xDC8C, 0x1542,},
+	{0xDC8E, 0x1140,},
+	{0xDC90, 0xB601,},
+	{0xDC92, 0x046D,},
+	{0xDC94, 0x0580,},
+	{0xDC96, 0x78E0,},
+	{0xDC98, 0xD800,},
+	{0xDC9A, 0xB893,},
+	{0xDC9C, 0x002D,},
+	{0xDC9E, 0x04A0,},
+	{0xDCA0, 0xD900,},
+	{0xDCA2, 0x78E0,},
+	{0xDCA4, 0x72CF,},
+	{0xDCA6, 0xFFFF,},
+	{0xDCA8, 0xE058,},
+	{0xDCAA, 0x2240,},
+	{0xDCAC, 0x0340,},
+	{0xDCAE, 0xA212,},
+	{0xDCB0, 0x208A,},
+	{0xDCB2, 0x0FFF,},
+	{0xDCB4, 0x1A42,},
+	{0xDCB6, 0x0004,},
+	{0xDCB8, 0xD830,},
+	{0xDCBA, 0x1A44,},
+	{0xDCBC, 0x0002,},
+	{0xDCBE, 0xD800,},
+	{0xDCC0, 0x1A50,},
+	{0xDCC2, 0x0002,},
+	{0xDCC4, 0x1A52,},
+	{0xDCC6, 0x0004,},
+	{0xDCC8, 0x1242,},
+	{0xDCCA, 0x0140,},
+	{0xDCCC, 0x8A2C,},
+	{0xDCCE, 0x6038,},
+	{0xDCD0, 0x1A42,},
+	{0xDCD2, 0x0004,},
+	{0xDCD4, 0x1242,},
+	{0xDCD6, 0x0141,},
+	{0xDCD8, 0x70CF,},
+	{0xDCDA, 0xFF00,},
+	{0xDCDC, 0x2184,},
+	{0xDCDE, 0xB021,},
+	{0xDCE0, 0xD800,},
+	{0xDCE2, 0xB893,},
+	{0xDCE4, 0x07E5,},
+	{0xDCE6, 0x0460,},
+	{0xDCE8, 0xD901,},
+	{0xDCEA, 0x78E0,},
+	{0xDCEC, 0xC0F1,},
+	{0xDCEE, 0x0BFA,},
+	{0xDCF0, 0x05A0,},
+	{0xDCF2, 0x216F,},
+	{0xDCF4, 0x0043,},
+	{0xDCF6, 0xC1A4,},
+	{0xDCF8, 0x220A,},
+	{0xDCFA, 0x1F80,},
+	{0xDCFC, 0xFFFF,},
+	{0xDCFE, 0xE058,},
+	{0xDD00, 0x2240,},
+	{0xDD02, 0x134F,},
+	{0xDD04, 0x1A48,},
+	{0xDD06, 0x13C0,},
+	{0xDD08, 0x1248,},
+	{0xDD0A, 0x1002,},
+	{0xDD0C, 0x70CF,},
+	{0xDD0E, 0x7FFF,},
+	{0xDD10, 0xFFFF,},
+	{0xDD12, 0xE230,},
+	{0xDD14, 0xC240,},
+	{0xDD16, 0xDA00,},
+	{0xDD18, 0xF00C,},
+	{0xDD1A, 0x1248,},
+	{0xDD1C, 0x1003,},
+	{0xDD1E, 0x1301,},
+	{0xDD20, 0x04CB,},
+	{0xDD22, 0x7261,},
+	{0xDD24, 0x2108,},
+	{0xDD26, 0x0081,},
+	{0xDD28, 0x2009,},
+	{0xDD2A, 0x0080,},
+	{0xDD2C, 0x1A48,},
+	{0xDD2E, 0x10C0,},
+	{0xDD30, 0x1248,},
+	{0xDD32, 0x100B,},
+	{0xDD34, 0xC300,},
+	{0xDD36, 0x0BE7,},
+	{0xDD38, 0x90C4,},
+	{0xDD3A, 0x2102,},
+	{0xDD3C, 0x0003,},
+	{0xDD3E, 0x238C,},
+	{0xDD40, 0x8FC3,},
+	{0xDD42, 0xF6C7,},
+	{0xDD44, 0xDAFF,},
+	{0xDD46, 0x1A05,},
+	{0xDD48, 0x1082,},
+	{0xDD4A, 0xC241,},
+	{0xDD4C, 0xF005,},
+	{0xDD4E, 0x7A6F,},
+	{0xDD50, 0xC241,},
+	{0xDD52, 0x1A05,},
+	{0xDD54, 0x10C2,},
+	{0xDD56, 0x2000,},
+	{0xDD58, 0x8040,},
+	{0xDD5A, 0xDA00,},
+	{0xDD5C, 0x20C0,},
+	{0xDD5E, 0x0064,},
+	{0xDD60, 0x781C,},
+	{0xDD62, 0xC042,},
+	{0xDD64, 0x1C0E,},
+	{0xDD66, 0x3082,},
+	{0xDD68, 0x1A48,},
+	{0xDD6A, 0x13C0,},
+	{0xDD6C, 0x7548,},
+	{0xDD6E, 0x7348,},
+	{0xDD70, 0x7148,},
+	{0xDD72, 0x7648,},
+	{0xDD74, 0xF002,},
+	{0xDD76, 0x7608,},
+	{0xDD78, 0x1248,},
+	{0xDD7A, 0x1000,},
+	{0xDD7C, 0x1400,},
+	{0xDD7E, 0x300B,},
+	{0xDD80, 0x084D,},
+	{0xDD82, 0x02C5,},
+	{0xDD84, 0x1248,},
+	{0xDD86, 0x1000,},
+	{0xDD88, 0xE101,},
+	{0xDD8A, 0x1001,},
+	{0xDD8C, 0x04CB,},
+	{0xDD8E, 0x1A48,},
+	{0xDD90, 0x1000,},
+	{0xDD92, 0x7361,},
+	{0xDD94, 0x1408,},
+	{0xDD96, 0x300B,},
+	{0xDD98, 0x2302,},
+	{0xDD9A, 0x02C0,},
+	{0xDD9C, 0x780D,},
+	{0xDD9E, 0x2607,},
+	{0xDDA0, 0x903E,},
+	{0xDDA2, 0x07D6,},
+	{0xDDA4, 0xFFE3,},
+	{0xDDA6, 0x792F,},
+	{0xDDA8, 0x09CF,},
+	{0xDDAA, 0x8152,},
+	{0xDDAC, 0x1248,},
+	{0xDDAE, 0x100E,},
+	{0xDDB0, 0x2400,},
+	{0xDDB2, 0x334B,},
+	{0xDDB4, 0xE501,},
+	{0xDDB6, 0x7EE2,},
+	{0xDDB8, 0x0DBF,},
+	{0xDDBA, 0x90F2,},
+	{0xDDBC, 0x1B0C,},
+	{0xDDBE, 0x1382,},
+	{0xDDC0, 0xC123,},
+	{0xDDC2, 0x140E,},
+	{0xDDC4, 0x3080,},
+	{0xDDC6, 0x7822,},
+	{0xDDC8, 0x1A07,},
+	{0xDDCA, 0x1002,},
+	{0xDDCC, 0x124C,},
+	{0xDDCE, 0x1000,},
+	{0xDDD0, 0x120B,},
+	{0xDDD2, 0x1081,},
+	{0xDDD4, 0x1207,},
+	{0xDDD6, 0x1083,},
+	{0xDDD8, 0x2142,},
+	{0xDDDA, 0x004B,},
+	{0xDDDC, 0x781B,},
+	{0xDDDE, 0x0B21,},
+	{0xDDE0, 0x02E2,},
+	{0xDDE2, 0x1A4C,},
+	{0xDDE4, 0x1000,},
+	{0xDDE6, 0xE101,},
+	{0xDDE8, 0x0915,},
+	{0xDDEA, 0x00C2,},
+	{0xDDEC, 0xC101,},
+	{0xDDEE, 0x1204,},
+	{0xDDF0, 0x1083,},
+	{0xDDF2, 0x090D,},
+	{0xDDF4, 0x00C2,},
+	{0xDDF6, 0xE001,},
+	{0xDDF8, 0x1A4C,},
+	{0xDDFA, 0x1000,},
+	{0xDDFC, 0x1A06,},
+	{0xDDFE, 0x1002,},
+	{0xDE00, 0x234A,},
+	{0xDE02, 0x1000,},
+	{0xDE04, 0x7169,},
+	{0xDE06, 0xF008,},
+	{0xDE08, 0x2053,},
+	{0xDE0A, 0x0003,},
+	{0xDE0C, 0x6179,},
+	{0xDE0E, 0x781C,},
+	{0xDE10, 0x2340,},
+	{0xDE12, 0x104B,},
+	{0xDE14, 0x1203,},
+	{0xDE16, 0x1083,},
+	{0xDE18, 0x0BF1,},
+	{0xDE1A, 0x90C2,},
+	{0xDE1C, 0x1202,},
+	{0xDE1E, 0x1080,},
+	{0xDE20, 0x091D,},
+	{0xDE22, 0x0004,},
+	{0xDE24, 0x70CF,},
+	{0xDE26, 0xFFFF,},
+	{0xDE28, 0xC644,},
+	{0xDE2A, 0x881B,},
+	{0xDE2C, 0xE0B2,},
+	{0xDE2E, 0xD83C,},
+	{0xDE30, 0x20CA,},
+	{0xDE32, 0x0CA2,},
+	{0xDE34, 0x1A01,},
+	{0xDE36, 0x1002,},
+	{0xDE38, 0x1A4C,},
+	{0xDE3A, 0x1080,},
+	{0xDE3C, 0x02B9,},
+	{0xDE3E, 0x05A0,},
+	{0xDE40, 0xC0A4,},
+	{0xDE42, 0x78E0,},
+	{0xDE44, 0xC0F1,},
+	{0xDE46, 0xFF95,},
+	{0xDE48, 0xD800,},
+	{0xDE4A, 0x71CF,},
+	{0xDE4C, 0xFF00,},
+	{0xDE4E, 0x1FE0,},
+	{0xDE50, 0x19D0,},
+	{0xDE52, 0x001C,},
+	{0xDE54, 0x19D1,},
+	{0xDE56, 0x001C,},
+	{0xDE58, 0x70CF,},
+	{0xDE5A, 0xFFFF,},
+	{0xDE5C, 0xE058,},
+	{0xDE5E, 0x901F,},
+	{0xDE60, 0xB861,},
+	{0xDE62, 0x19D2,},
+	{0xDE64, 0x001C,},
+	{0xDE66, 0xC0D1,},
+	{0xDE68, 0x7EE0,},
+	{0xDE6A, 0x78E0,},
+	{0xDE6C, 0xC0F1,},
+	{0xDE6E, 0x0A7A,},
+	{0xDE70, 0x0580,},
+	{0xDE72, 0x70CF,},
+	{0xDE74, 0xFFFF,},
+	{0xDE76, 0xC5D4,},
+	{0xDE78, 0x9041,},
+	{0xDE7A, 0x9023,},
+	{0xDE7C, 0x75CF,},
+	{0xDE7E, 0xFFFF,},
+	{0xDE80, 0xE058,},
+	{0xDE82, 0x7942,},
+	{0xDE84, 0xB967,},
+	{0xDE86, 0x7F30,},
+	{0xDE88, 0xB53F,},
+	{0xDE8A, 0x71CF,},
+	{0xDE8C, 0xFFFF,},
+	{0xDE8E, 0xC84C,},
+	{0xDE90, 0x91D3,},
+	{0xDE92, 0x108B,},
+	{0xDE94, 0x0081,},
+	{0xDE96, 0x2615,},
+	{0xDE98, 0x1380,},
+	{0xDE9A, 0x090F,},
+	{0xDE9C, 0x0C91,},
+	{0xDE9E, 0x0A8E,},
+	{0xDEA0, 0x05A0,},
+	{0xDEA2, 0xD906,},
+	{0xDEA4, 0x7E10,},
+	{0xDEA6, 0x2615,},
+	{0xDEA8, 0x1380,},
+	{0xDEAA, 0x0A82,},
+	{0xDEAC, 0x05A0,},
+	{0xDEAE, 0xD960,},
+	{0xDEB0, 0x790F,},
+	{0xDEB2, 0x090D,},
+	{0xDEB4, 0x0133,},
+	{0xDEB6, 0xAD0C,},
+	{0xDEB8, 0xD904,},
+	{0xDEBA, 0xAD2C,},
+	{0xDEBC, 0x79EC,},
+	{0xDEBE, 0x2941,},
+	{0xDEC0, 0x7402,},
+	{0xDEC2, 0x71CF,},
+	{0xDEC4, 0xFF00,},
+	{0xDEC6, 0x2184,},
+	{0xDEC8, 0xB142,},
+	{0xDECA, 0x1906,},
+	{0xDECC, 0x0E44,},
+	{0xDECE, 0xFFDE,},
+	{0xDED0, 0x70C9,},
+	{0xDED2, 0x0A5A,},
+	{0xDED4, 0x05A0,},
+	{0xDED6, 0x8D2C,},
+	{0xDED8, 0xAD0B,},
+	{0xDEDA, 0xD800,},
+	{0xDEDC, 0xAD01,},
+	{0xDEDE, 0x0219,},
+	{0xDEE0, 0x05A0,},
+	{0xDEE2, 0xA513,},
+	{0xDEE4, 0xC0F1,},
+	{0xDEE6, 0x71CF,},
+	{0xDEE8, 0xFFFF,},
+	{0xDEEA, 0xC644,},
+	{0xDEEC, 0xA91B,},
+	{0xDEEE, 0xD902,},
+	{0xDEF0, 0x70CF,},
+	{0xDEF2, 0xFFFF,},
+	{0xDEF4, 0xC84C,},
+	{0xDEF6, 0x093E,},
+	{0xDEF8, 0x03A0,},
+	{0xDEFA, 0xA826,},
+	{0xDEFC, 0xFFDC,},
+	{0xDEFE, 0xF1B5,},
+	{0xDF00, 0xC0F1,},
+	{0xDF02, 0x09EA,},
+	{0xDF04, 0x0580,},
+	{0xDF06, 0x75CF,},
+	{0xDF08, 0xFFFF,},
+	{0xDF0A, 0xE058,},
+	{0xDF0C, 0x1540,},
+	{0xDF0E, 0x1080,},
+	{0xDF10, 0x08A7,},
+	{0xDF12, 0x0010,},
+	{0xDF14, 0x8D00,},
+	{0xDF16, 0x0813,},
+	{0xDF18, 0x009E,},
+	{0xDF1A, 0x1540,},
+	{0xDF1C, 0x1081,},
+	{0xDF1E, 0xE181,},
+	{0xDF20, 0x20CA,},
+	{0xDF22, 0x00A1,},
+	{0xDF24, 0xF24B,},
+	{0xDF26, 0x1540,},
+	{0xDF28, 0x1081,},
+	{0xDF2A, 0x090F,},
+	{0xDF2C, 0x0050,},
+	{0xDF2E, 0x1540,},
+	{0xDF30, 0x1081,},
+	{0xDF32, 0x0927,},
+	{0xDF34, 0x0091,},
+	{0xDF36, 0x1550,},
+	{0xDF38, 0x1081,},
+	{0xDF3A, 0xDE00,},
+	{0xDF3C, 0xAD2A,},
+	{0xDF3E, 0x1D50,},
+	{0xDF40, 0x1382,},
+	{0xDF42, 0x1552,},
+	{0xDF44, 0x1101,},
+	{0xDF46, 0x1D52,},
+	{0xDF48, 0x1384,},
+	{0xDF4A, 0xB524,},
+	{0xDF4C, 0x082D,},
+	{0xDF4E, 0x015F,},
+	{0xDF50, 0xFF55,},
+	{0xDF52, 0xD803,},
+	{0xDF54, 0xF033,},
+	{0xDF56, 0x1540,},
+	{0xDF58, 0x1081,},
+	{0xDF5A, 0x0967,},
+	{0xDF5C, 0x00D1,},
+	{0xDF5E, 0x1550,},
+	{0xDF60, 0x1081,},
+	{0xDF62, 0xDE00,},
+	{0xDF64, 0xAD2A,},
+	{0xDF66, 0x1D50,},
+	{0xDF68, 0x1382,},
+	{0xDF6A, 0x1552,},
+	{0xDF6C, 0x1101,},
+	{0xDF6E, 0x1D52,},
+	{0xDF70, 0x1384,},
+	{0xDF72, 0xB524,},
+	{0xDF74, 0x0811,},
+	{0xDF76, 0x019E,},
+	{0xDF78, 0xB8A0,},
+	{0xDF7A, 0xAD00,},
+	{0xDF7C, 0xFF47,},
+	{0xDF7E, 0x1D40,},
+	{0xDF80, 0x1382,},
+	{0xDF82, 0xF01F,},
+	{0xDF84, 0xFF5A,},
+	{0xDF86, 0x8D01,},
+	{0xDF88, 0x8D40,},
+	{0xDF8A, 0xE812,},
+	{0xDF8C, 0x71CF,},
+	{0xDF8E, 0xFFFF,},
+	{0xDF90, 0xC644,},
+	{0xDF92, 0x893B,},
+	{0xDF94, 0x7030,},
+	{0xDF96, 0x22D1,},
+	{0xDF98, 0x8062,},
+	{0xDF9A, 0xF20A,},
+	{0xDF9C, 0x0A0F,},
+	{0xDF9E, 0x009E,},
+	{0xDFA0, 0x71CF,},
+	{0xDFA2, 0xFFFF,},
+	{0xDFA4, 0xC84C,},
+	{0xDFA6, 0x893B,},
+	{0xDFA8, 0xE902,},
+	{0xDFAA, 0xFFCF,},
+	{0xDFAC, 0x8D00,},
+	{0xDFAE, 0xB8E7,},
+	{0xDFB0, 0x26CA,},
+	{0xDFB2, 0x1022,},
+	{0xDFB4, 0xF5E2,},
+	{0xDFB6, 0xFF3C,},
+	{0xDFB8, 0xD801,},
+	{0xDFBA, 0x1D40,},
+	{0xDFBC, 0x1002,},
+	{0xDFBE, 0x0141,},
+	{0xDFC0, 0x0580,},
+	{0xDFC2, 0x78E0,},
+	{0xDFC4, 0xC0F1,},
+	{0xDFC6, 0xC5E1,},
+	{0xDFC8, 0xFF34,},
+	{0xDFCA, 0xDD00,},
+	{0xDFCC, 0x70CF,},
+	{0xDFCE, 0xFFFF,},
+	{0xDFD0, 0xE090,},
+	{0xDFD2, 0xA8A8,},
+	{0xDFD4, 0xD800,},
+	{0xDFD6, 0xB893,},
+	{0xDFD8, 0x0C8A,},
+	{0xDFDA, 0x0460,},
+	{0xDFDC, 0xD901,},
+	{0xDFDE, 0x71CF,},
+	{0xDFE0, 0xFFFF,},
+	{0xDFE2, 0xDC10,},
+	{0xDFE4, 0xD813,},
+	{0xDFE6, 0x0B96,},
+	{0xDFE8, 0x0460,},
+	{0xDFEA, 0x72A9,},
+	{0xDFEC, 0x0119,},
+	{0xDFEE, 0x0580,},
+	{0xDFF0, 0xC0F1,},
+	{0xDFF2, 0x71CF,},
+	{0xDFF4, 0x0000,},
+	{0xDFF6, 0x5BAE,},
+	{0xDFF8, 0x7940,},
+	{0xDFFA, 0xFF9D,},
+	{0xDFFC, 0xF135,},
+	{0xDFFE, 0x78E0,},
+	{0xE000, 0xC0F1,},
+	{0xE002, 0x70CF,},
+	{0xE004, 0x0000,},
+	{0xE006, 0x5CBA,},
+	{0xE008, 0x7840,},
+	{0xE00A, 0x70CF,},
+	{0xE00C, 0xFFFF,},
+	{0xE00E, 0xE058,},
+	{0xE010, 0x8800,},
+	{0xE012, 0x0815,},
+	{0xE014, 0x001E,},
+	{0xE016, 0x70CF,},
+	{0xE018, 0xFFFF,},
+	{0xE01A, 0xC84C,},
+	{0xE01C, 0x881A,},
+	{0xE01E, 0xE080,},
+	{0xE020, 0x0EE0,},
+	{0xE022, 0xFFC1,},
+	{0xE024, 0xF121,},
+	{0xE026, 0x78E0,},
+	{0xE028, 0xC0F1,},
+	{0xE02A, 0xD900,},
+	{0xE02C, 0xF009,},
+	{0xE02E, 0x70CF,},
+	{0xE030, 0xFFFF,},
+	{0xE032, 0xE0AC,},
+	{0xE034, 0x7835,},
+	{0xE036, 0x8041,},
+	{0xE038, 0x8000,},
+	{0xE03A, 0xE102,},
+	{0xE03C, 0xA040,},
+	{0xE03E, 0x09F3,},
+	{0xE040, 0x8114,},
+	{0xE042, 0x71CF,},
+	{0xE044, 0xFFFF,},
+	{0xE046, 0xE058,},
+	{0xE048, 0x70CF,},
+	{0xE04A, 0xFFFF,},
+	{0xE04C, 0xC594,},
+	{0xE04E, 0xB030,},
+	{0xE050, 0xFFDD,},
+	{0xE052, 0xD800,},
+	{0xE054, 0xF109,},
+	{0xE056, 0x0000,},
+	{0xE058, 0x0300,},
+	{0xE05A, 0x0204,},
+	{0xE05C, 0x0700,},
+	{0xE05E, 0x0000,},
+	{0xE060, 0x0000,},
+	{0xE062, 0x0000,},
+	{0xE064, 0x0000,},
+	{0xE066, 0x0000,},
+	{0xE068, 0x0000,},
+	{0xE06A, 0x0000,},
+	{0xE06C, 0x0000,},
+	{0xE06E, 0x0000,},
+	{0xE070, 0x0000,},
+	{0xE072, 0x0000,},
+	{0xE074, 0x0000,},
+	{0xE076, 0x0000,},
+	{0xE078, 0x0000,},
+	{0xE07A, 0x0000,},
+	{0xE07C, 0x0000,},
+	{0xE07E, 0x0000,},
+	{0xE080, 0x0000,},
+	{0xE082, 0x0000,},
+	{0xE084, 0x0000,},
+	{0xE086, 0x0000,},
+	{0xE088, 0x0000,},
+	{0xE08A, 0x0000,},
+	{0xE08C, 0x0000,},
+	{0xE08E, 0x0000,},
+	{0xE090, 0x0000,},
+	{0xE092, 0x0000,},
+	{0xE094, 0x0000,},
+	{0xE096, 0x0000,},
+	{0xE098, 0x0000,},
+	{0xE09A, 0x0000,},
+	{0xE09C, 0x0000,},
+	{0xE09E, 0x0000,},
+	{0xE0A0, 0x0000,},
+	{0xE0A2, 0x0000,},
+	{0xE0A4, 0x0000,},
+	{0xE0A6, 0x0000,},
+	{0xE0A8, 0x0000,},
+	{0xE0AA, 0x0000,},
+	{0xE0AC, 0xFFFF,},
+	{0xE0AE, 0xCB68,},
+	{0xE0B0, 0xFFFF,},
+	{0xE0B2, 0xDFF0,},
+	{0xE0B4, 0xFFFF,},
+	{0xE0B6, 0xCB6C,},
+	{0xE0B8, 0xFFFF,},
+	{0xE0BA, 0xE000,},
+	{0x098E, 0x0000,},
+
+	/*MIPI setting for SOC1040*/
+	{0x3C5A, 0x0009,},
+	{0x3C44, 0x0080,},/*MIPI_CUSTOM_SHORT_PKT*/
+
+	/*[Tuning_settings]*/
+
+	/*[CCM]*/
+	{0xC892, 0x0267,},/*CAM_AWB_CCM_L_0*/
+	{0xC894, 0xFF1A,},/*CAM_AWB_CCM_L_1*/
+	{0xC896, 0xFFB3,},/*CAM_AWB_CCM_L_2*/
+	{0xC898, 0xFF80,},/*CAM_AWB_CCM_L_3*/
+	{0xC89A, 0x0166,},/*CAM_AWB_CCM_L_4*/
+	{0xC89C, 0x0003,},/*CAM_AWB_CCM_L_5*/
+	{0xC89E, 0xFF9A,},/*CAM_AWB_CCM_L_6*/
+	{0xC8A0, 0xFEB4,},/*CAM_AWB_CCM_L_7*/
+	{0xC8A2, 0x024D,},/*CAM_AWB_CCM_L_8*/
+	{0xC8A4, 0x01BF,},/*CAM_AWB_CCM_M_0*/
+	{0xC8A6, 0xFF01,},/*CAM_AWB_CCM_M_1*/
+	{0xC8A8, 0xFFF3,},/*CAM_AWB_CCM_M_2*/
+	{0xC8AA, 0xFF75,},/*CAM_AWB_CCM_M_3*/
+	{0xC8AC, 0x0198,},/*CAM_AWB_CCM_M_4*/
+	{0xC8AE, 0xFFFD,},/*CAM_AWB_CCM_M_5*/
+	{0xC8B0, 0xFF9A,},/*CAM_AWB_CCM_M_6*/
+	{0xC8B2, 0xFEE7,},/*CAM_AWB_CCM_M_7*/
+	{0xC8B4, 0x02A8,},/*CAM_AWB_CCM_M_8*/
+	{0xC8B6, 0x01D9,},/*CAM_AWB_CCM_R_0*/
+	{0xC8B8, 0xFF26,},/*CAM_AWB_CCM_R_1*/
+	{0xC8BA, 0xFFF3,},/*CAM_AWB_CCM_R_2*/
+	{0xC8BC, 0xFFB3,},/*CAM_AWB_CCM_R_3*/
+	{0xC8BE, 0x0132,},/*CAM_AWB_CCM_R_4*/
+	{0xC8C0, 0xFFE8,},/*CAM_AWB_CCM_R_5*/
+	{0xC8C2, 0xFFDA,},/*CAM_AWB_CCM_R_6*/
+	{0xC8C4, 0xFECD,},/*CAM_AWB_CCM_R_7*/
+	{0xC8C6, 0x02C2,},/*CAM_AWB_CCM_R_8*/
+	{0xC8C8, 0x0075,},/*CAM_AWB_CCM_L_RG_GAIN*/
+	{0xC8CA, 0x011C,},/*CAM_AWB_CCM_L_BG_GAIN*/
+	{0xC8CC, 0x009A,},/*CAM_AWB_CCM_M_RG_GAIN*/
+	{0xC8CE, 0x0105,},/*CAM_AWB_CCM_M_BG_GAIN*/
+	{0xC8D0, 0x00A4,},/*CAM_AWB_CCM_R_RG_GAIN*/
+	{0xC8D2, 0x00AC,},/*CAM_AWB_CCM_R_BG_GAIN*/
+	{0xC8D4, 0x0A8C,},/*CAM_AWB_CCM_L_CTEMP*/
+	{0xC8D6, 0x0F0A,},/*CAM_AWB_CCM_M_CTEMP*/
+	{0xC8D8, 0x1964,},/*CAM_AWB_CCM_R_CTEMP*/
+
+	/*[AWB]*/
+	{0xC914, 0x0000,},/*CAM_STAT_AWB_CLIP_WINDOW_XSTART*/
+	{0xC916, 0x0000,},/*CAM_STAT_AWB_CLIP_WINDOW_YSTART*/
+	{0xC918, 0x04FF,},/*CAM_STAT_AWB_CLIP_WINDOW_XEND*/
+	{0xC91A, 0x02CF,},/*CAM_STAT_AWB_CLIP_WINDOW_YEND*/
+	{0xC904, 0x0033,},/*CAM_AWB_AWB_XSHIFT_PRE_ADJ*/
+	{0xC906, 0x0040,},/*CAM_AWB_AWB_YSHIFT_PRE_ADJ*/
+	{0xC8F2, 0x03, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_AWB_AWB_XSCALE*/
+	{0xC8F3, 0x02, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_AWB_AWB_YSCALE*/
+	{0xC906, 0x003C,},/*CAM_AWB_AWB_YSHIFT_PRE_ADJ*/
+	{0xC8F4, 0x0000,},/*CAM_AWB_AWB_WEIGHTS_0*/
+	{0xC8F6, 0x0000,},/*CAM_AWB_AWB_WEIGHTS_1*/
+	{0xC8F8, 0x0000,},/*CAM_AWB_AWB_WEIGHTS_2*/
+	{0xC8FA, 0xE724,},/*CAM_AWB_AWB_WEIGHTS_3*/
+	{0xC8FC, 0x1583,},/*CAM_AWB_AWB_WEIGHTS_4*/
+	{0xC8FE, 0x2045,},/*CAM_AWB_AWB_WEIGHTS_5*/
+	{0xC900, 0x03FF,},/*CAM_AWB_AWB_WEIGHTS_6*/
+	{0xC902, 0x007C,},/*CAM_AWB_AWB_WEIGHTS_7*/
+	{0xC90C, 0x80, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_AWB_K_R_L*/
+	{0xC90D, 0x80, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_AWB_K_G_L*/
+	{0xC90E, 0x80, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_AWB_K_B_L*/
+	{0xC90F, 0x88, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_AWB_K_R_R*/
+	{0xC910, 0x80, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_AWB_K_G_R*/
+	{0xC911, 0x80, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_AWB_K_B_R*/
+
+	/*[Step7-CPIPE_Preference]*/
+	{0xC926, 0x0020,},/*CAM_LL_START_BRIGHTNESS*/
+	{0xC928, 0x009A,},/*CAM_LL_STOP_BRIGHTNESS*/
+	{0xC946, 0x0070,},/*CAM_LL_START_GAIN_METRIC*/
+	{0xC948, 0x00F3,},/*CAM_LL_STOP_GAIN_METRIC*/
+	{0xC952, 0x0020,},/*CAM_LL_START_TARGET_LUMA_BM*/
+	{0xC954, 0x009A,},/*CAM_LL_STOP_TARGET_LUMA_BM*/
+	{0xC92A, 0x80, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_START_SATURATION*/
+	{0xC92B, 0x4B, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_END_SATURATION*/
+	{0xC92C, 0x00, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_START_DESATURATION*/
+	{0xC92D, 0xFF, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_END_DESATURATION*/
+	{0xC92E, 0x3C, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_START_DEMOSAIC*/
+	{0xC92F, 0x02, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_START_AP_GAIN*/
+	{0xC930, 0x06, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_START_AP_THRESH*/
+	{0xC931, 0x64, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_STOP_DEMOSAIC*/
+	{0xC932, 0x01, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_STOP_AP_GAIN*/
+	{0xC933, 0x0C, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_STOP_AP_THRESH*/
+	{0xC934, 0x3C, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_START_NR_RED*/
+	{0xC935, 0x3C, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_START_NR_GREEN*/
+	{0xC936, 0x3C, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_START_NR_BLUE*/
+	{0xC937, 0x0F, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_START_NR_THRESH*/
+	{0xC938, 0x64, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_STOP_NR_RED*/
+	{0xC939, 0x64, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_STOP_NR_GREEN*/
+	{0xC93A, 0x64, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_STOP_NR_BLUE*/
+	{0xC93B, 0x32, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_STOP_NR_THRESH*/
+	{0xC93C, 0x0020,},/*CAM_LL_START_CONTRAST_BM*/
+	{0xC93E, 0x009A,},/*CAM_LL_STOP_CONTRAST_BM*/
+	{0xC940, 0x00DC,},/*CAM_LL_GAMMA*/
+	/*CAM_LL_START_CONTRAST_GRADIENT*/
+	{0xC942, 0x38, MSM_CAMERA_I2C_BYTE_DATA},
+	/*CAM_LL_STOP_CONTRAST_GRADIENT*/
+	{0xC943, 0x30, MSM_CAMERA_I2C_BYTE_DATA},
+	{0xC944, 0x50, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_START_CONTRAST_LUMA*/
+	{0xC945, 0x19, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_STOP_CONTRAST_LUMA*/
+	{0xC94A, 0x0230,},/*CAM_LL_START_FADE_TO_BLACK_LUMA*/
+	{0xC94C, 0x0010,},/*CAM_LL_STOP_FADE_TO_BLACK_LUMA*/
+	{0xC94E, 0x01CD,},/*CAM_LL_CLUSTER_DC_TH_BM*/
+	{0xC950, 0x05, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_CLUSTER_DC_GATE*/
+	{0xC951, 0x40, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_SUMMING_SENSITIVITY*/
+	/*CAM_AET_TARGET_AVERAGE_LUMA_DARK*/
+	{0xC87B, 0x1B, MSM_CAMERA_I2C_BYTE_DATA},
+	{0xC878, 0x0E, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_AET_AEMODE*/
+	{0xC890, 0x0080,},/*CAM_AET_TARGET_GAIN*/
+	{0xC886, 0x0100,},/*CAM_AET_AE_MAX_VIRT_AGAIN*/
+	{0xC87C, 0x005A,},/*CAM_AET_BLACK_CLIPPING_TARGET*/
+	{0xB42A, 0x05, MSM_CAMERA_I2C_BYTE_DATA},/*CCM_DELTA_GAIN*/
+	/*AE_TRACK_AE_TRACKING_DAMPENING*/
+	{0xA80A, 0x20, MSM_CAMERA_I2C_BYTE_DATA},
+	{0x3C44, 0x0080,},
+	{0x3C40, 0x0004, MSM_CAMERA_I2C_UNSET_WORD_MASK},
+	{0xA802, 0x08, MSM_CAMERA_I2C_SET_BYTE_MASK},
+	{0xC908, 0x01, MSM_CAMERA_I2C_BYTE_DATA},
+	{0xC879, 0x01, MSM_CAMERA_I2C_BYTE_DATA},
+	{0xC909, 0x01, MSM_CAMERA_I2C_UNSET_BYTE_MASK},
+	{0xA80A, 0x18, MSM_CAMERA_I2C_BYTE_DATA},
+	{0xA80B, 0x18, MSM_CAMERA_I2C_BYTE_DATA},
+	{0xAC16, 0x18, MSM_CAMERA_I2C_BYTE_DATA},
+	{0xC878, 0x08, MSM_CAMERA_I2C_SET_BYTE_MASK},
+	{0xBC02, 0x08, MSM_CAMERA_I2C_UNSET_BYTE_MASK},
+};
+
+static struct v4l2_subdev_info mt9m114_subdev_info[] = {
+	{
+	.code   = V4L2_MBUS_FMT_YUYV8_2X8,
+	.colorspace = V4L2_COLORSPACE_JPEG,
+	.fmt    = 1,
+	.order    = 0,
+	},
+	/* more can be supported, to be added later */
+};
+
+static struct msm_camera_i2c_reg_conf mt9m114_config_change_settings[] = {
+	{0xdc00, 0x28, MSM_CAMERA_I2C_BYTE_DATA, MSM_CAMERA_I2C_CMD_WRITE},
+	{MT9M114_COMMAND_REGISTER, MT9M114_COMMAND_REGISTER_SET_STATE,
+		MSM_CAMERA_I2C_UNSET_WORD_MASK, MSM_CAMERA_I2C_CMD_POLL},
+	{MT9M114_COMMAND_REGISTER, (MT9M114_COMMAND_REGISTER_OK |
+		MT9M114_COMMAND_REGISTER_SET_STATE), MSM_CAMERA_I2C_WORD_DATA,
+		MSM_CAMERA_I2C_CMD_WRITE},
+	{MT9M114_COMMAND_REGISTER, MT9M114_COMMAND_REGISTER_SET_STATE,
+		MSM_CAMERA_I2C_UNSET_WORD_MASK, MSM_CAMERA_I2C_CMD_POLL},
+	{0xDC01, 0x31, MSM_CAMERA_I2C_BYTE_DATA},
+};
+
+static void mt9m114_stop_stream(struct msm_sensor_ctrl_t *s_ctrl) {}
+
+static struct msm_camera_i2c_conf_array mt9m114_init_conf[] = {
+	{mt9m114_recommend_settings,
+	ARRAY_SIZE(mt9m114_recommend_settings), 0, MSM_CAMERA_I2C_WORD_DATA},
+	{mt9m114_config_change_settings,
+	ARRAY_SIZE(mt9m114_config_change_settings),
+	0, MSM_CAMERA_I2C_WORD_DATA},
+};
+
+static struct msm_camera_i2c_conf_array mt9m114_confs[] = {
+	{mt9m114_720p_settings,
+	ARRAY_SIZE(mt9m114_720p_settings), 0, MSM_CAMERA_I2C_WORD_DATA},
+};
+
+static struct msm_camera_i2c_reg_conf mt9m114_saturation[][1] = {
+	{{0xCC12, 0x00},},
+	{{0xCC12, 0x1A},},
+	{{0xCC12, 0x34},},
+	{{0xCC12, 0x4E},},
+	{{0xCC12, 0x68},},
+	{{0xCC12, 0x80},},
+	{{0xCC12, 0x9A},},
+	{{0xCC12, 0xB4},},
+	{{0xCC12, 0xCE},},
+	{{0xCC12, 0xE8},},
+	{{0xCC12, 0xFF},},
+};
+
+static struct msm_camera_i2c_reg_conf mt9m114_refresh[] = {
+	{MT9M114_COMMAND_REGISTER, MT9M114_COMMAND_REGISTER_REFRESH,
+		MSM_CAMERA_I2C_UNSET_WORD_MASK, MSM_CAMERA_I2C_CMD_POLL},
+	{MT9M114_COMMAND_REGISTER, (MT9M114_COMMAND_REGISTER_OK |
+		MT9M114_COMMAND_REGISTER_REFRESH), MSM_CAMERA_I2C_WORD_DATA,
+		MSM_CAMERA_I2C_CMD_WRITE},
+	{MT9M114_COMMAND_REGISTER, MT9M114_COMMAND_REGISTER_REFRESH,
+		MSM_CAMERA_I2C_UNSET_WORD_MASK, MSM_CAMERA_I2C_CMD_POLL},
+	{MT9M114_COMMAND_REGISTER, MT9M114_COMMAND_REGISTER_OK,
+		MSM_CAMERA_I2C_SET_WORD_MASK, MSM_CAMERA_I2C_CMD_POLL},
+};
+
+static struct msm_camera_i2c_conf_array mt9m114_saturation_confs[][2] = {
+	{{mt9m114_saturation[0],
+		ARRAY_SIZE(mt9m114_saturation[0]), 0, MSM_CAMERA_I2C_WORD_DATA},
+	{mt9m114_refresh,
+		ARRAY_SIZE(mt9m114_refresh), 0, MSM_CAMERA_I2C_WORD_DATA},},
+	{{mt9m114_saturation[1],
+		ARRAY_SIZE(mt9m114_saturation[1]), 0, MSM_CAMERA_I2C_WORD_DATA},
+	{mt9m114_refresh,
+		ARRAY_SIZE(mt9m114_refresh), 0, MSM_CAMERA_I2C_WORD_DATA},},
+	{{mt9m114_saturation[2],
+		ARRAY_SIZE(mt9m114_saturation[2]), 0, MSM_CAMERA_I2C_WORD_DATA},
+	{mt9m114_refresh,
+		ARRAY_SIZE(mt9m114_refresh), 0, MSM_CAMERA_I2C_WORD_DATA},},
+	{{mt9m114_saturation[3],
+		ARRAY_SIZE(mt9m114_saturation[3]), 0, MSM_CAMERA_I2C_WORD_DATA},
+	{mt9m114_refresh,
+		ARRAY_SIZE(mt9m114_refresh), 0, MSM_CAMERA_I2C_WORD_DATA},},
+	{{mt9m114_saturation[4],
+		ARRAY_SIZE(mt9m114_saturation[4]), 0, MSM_CAMERA_I2C_WORD_DATA},
+	{mt9m114_refresh,
+		ARRAY_SIZE(mt9m114_refresh), 0, MSM_CAMERA_I2C_WORD_DATA},},
+	{{mt9m114_saturation[5],
+		ARRAY_SIZE(mt9m114_saturation[5]), 0, MSM_CAMERA_I2C_WORD_DATA},
+	{mt9m114_refresh,
+		ARRAY_SIZE(mt9m114_refresh), 0, MSM_CAMERA_I2C_WORD_DATA},},
+	{{mt9m114_saturation[6],
+		ARRAY_SIZE(mt9m114_saturation[6]), 0, MSM_CAMERA_I2C_WORD_DATA},
+	{mt9m114_refresh,
+		ARRAY_SIZE(mt9m114_refresh), 0, MSM_CAMERA_I2C_WORD_DATA},},
+	{{mt9m114_saturation[7],
+		ARRAY_SIZE(mt9m114_saturation[7]), 0, MSM_CAMERA_I2C_WORD_DATA},
+	{mt9m114_refresh,
+		ARRAY_SIZE(mt9m114_refresh), 0, MSM_CAMERA_I2C_WORD_DATA},},
+	{{mt9m114_saturation[8],
+		ARRAY_SIZE(mt9m114_saturation[8]), 0, MSM_CAMERA_I2C_WORD_DATA},
+	{mt9m114_refresh,
+		ARRAY_SIZE(mt9m114_refresh), 0, MSM_CAMERA_I2C_WORD_DATA},},
+	{{mt9m114_saturation[9],
+		ARRAY_SIZE(mt9m114_saturation[9]), 0, MSM_CAMERA_I2C_WORD_DATA},
+	{mt9m114_refresh,
+		ARRAY_SIZE(mt9m114_refresh), 0, MSM_CAMERA_I2C_WORD_DATA},},
+	{{mt9m114_saturation[10],
+		ARRAY_SIZE(mt9m114_saturation[10]),
+		0, MSM_CAMERA_I2C_WORD_DATA},
+	{mt9m114_refresh,
+		ARRAY_SIZE(mt9m114_refresh), 0, MSM_CAMERA_I2C_WORD_DATA},},
+};
+
+static int mt9m114_saturation_enum_map[] = {
+	MSM_V4L2_SATURATION_L0,
+	MSM_V4L2_SATURATION_L1,
+	MSM_V4L2_SATURATION_L2,
+	MSM_V4L2_SATURATION_L3,
+	MSM_V4L2_SATURATION_L4,
+	MSM_V4L2_SATURATION_L5,
+	MSM_V4L2_SATURATION_L6,
+	MSM_V4L2_SATURATION_L7,
+	MSM_V4L2_SATURATION_L8,
+	MSM_V4L2_SATURATION_L9,
+	MSM_V4L2_SATURATION_L10,
+};
+
+static struct msm_camera_i2c_enum_conf_array mt9m114_saturation_enum_confs = {
+	.conf = &mt9m114_saturation_confs[0][0],
+	.conf_enum = mt9m114_saturation_enum_map,
+	.num_enum = ARRAY_SIZE(mt9m114_saturation_enum_map),
+	.num_index = ARRAY_SIZE(mt9m114_saturation_confs),
+	.num_conf = ARRAY_SIZE(mt9m114_saturation_confs[0]),
+	.data_type = MSM_CAMERA_I2C_WORD_DATA,
+};
+
+struct msm_sensor_v4l2_ctrl_info_t mt9m114_v4l2_ctrl_info[] = {
+	{
+		.ctrl_id = V4L2_CID_SATURATION,
+		.min = MSM_V4L2_SATURATION_L0,
+		.max = MSM_V4L2_SATURATION_L10,
+		.step = 1,
+		.enum_cfg_settings = &mt9m114_saturation_enum_confs,
+		.s_v4l2_ctrl = msm_sensor_s_ctrl_by_enum,
+	},
+};
+
+static struct msm_sensor_output_info_t mt9m114_dimensions[] = {
+	{
+		.x_output = 0x500,
+		.y_output = 0x2D0,
+		.line_length_pclk = 0x500,
+		.frame_length_lines = 0x2D0,
+		.vt_pixel_clk = 48000000,
+		.op_pixel_clk = 128000000,
+		.binning_factor = 1,
+	},
+};
+
+static struct msm_camera_csid_vc_cfg mt9m114_cid_cfg[] = {
+	{0, CSI_YUV422_8, CSI_DECODE_8BIT},
+	{1, CSI_EMBED_DATA, CSI_DECODE_8BIT},
+};
+
+static struct msm_camera_csi2_params mt9m114_csi_params = {
+	.csid_params = {
+		.lane_cnt = 1,
+		.lut_params = {
+			.num_cid = 2,
+			.vc_cfg = mt9m114_cid_cfg,
+		},
+	},
+	.csiphy_params = {
+		.lane_cnt = 1,
+		.settle_cnt = 0x14,
+	},
+};
+
+static struct msm_camera_csi2_params *mt9m114_csi_params_array[] = {
+	&mt9m114_csi_params,
+	&mt9m114_csi_params,
+};
+
+static struct msm_sensor_output_reg_addr_t mt9m114_reg_addr = {
+	.x_output = 0xC868,
+	.y_output = 0xC86A,
+	.line_length_pclk = 0xC868,
+	.frame_length_lines = 0xC86A,
+};
+
+static struct msm_sensor_id_info_t mt9m114_id_info = {
+	.sensor_id_reg_addr = 0x0,
+	.sensor_id = 0x2481,
+};
+
+static const struct i2c_device_id mt9m114_i2c_id[] = {
+	{SENSOR_NAME, (kernel_ulong_t)&mt9m114_s_ctrl},
+	{ }
+};
+
+static struct i2c_driver mt9m114_i2c_driver = {
+	.id_table = mt9m114_i2c_id,
+	.probe  = msm_sensor_i2c_probe,
+	.driver = {
+		.name = SENSOR_NAME,
+	},
+};
+
+static struct msm_camera_i2c_client mt9m114_sensor_i2c_client = {
+	.addr_type = MSM_CAMERA_I2C_WORD_ADDR,
+};
+
+static int __init msm_sensor_init_module(void)
+{
+	return i2c_add_driver(&mt9m114_i2c_driver);
+}
+
+static struct v4l2_subdev_core_ops mt9m114_subdev_core_ops = {
+	.s_ctrl = msm_sensor_v4l2_s_ctrl,
+	.queryctrl = msm_sensor_v4l2_query_ctrl,
+	.ioctl = msm_sensor_subdev_ioctl,
+	.s_power = msm_sensor_power,
+};
+
+static struct v4l2_subdev_video_ops mt9m114_subdev_video_ops = {
+	.enum_mbus_fmt = msm_sensor_v4l2_enum_fmt,
+};
+
+static struct v4l2_subdev_ops mt9m114_subdev_ops = {
+	.core = &mt9m114_subdev_core_ops,
+	.video  = &mt9m114_subdev_video_ops,
+};
+
+static struct msm_sensor_fn_t mt9m114_func_tbl = {
+	.sensor_start_stream = msm_sensor_start_stream,
+	.sensor_stop_stream = mt9m114_stop_stream,
+	.sensor_setting = msm_sensor_setting,
+	.sensor_set_sensor_mode = msm_sensor_set_sensor_mode,
+	.sensor_mode_init = msm_sensor_mode_init,
+	.sensor_get_output_info = msm_sensor_get_output_info,
+	.sensor_config = msm_sensor_config,
+	.sensor_power_up = msm_sensor_power_up,
+	.sensor_power_down = msm_sensor_power_down,
+};
+
+static struct msm_sensor_reg_t mt9m114_regs = {
+	.default_data_type = MSM_CAMERA_I2C_BYTE_DATA,
+	.start_stream_conf = mt9m114_config_change_settings,
+	.start_stream_conf_size = ARRAY_SIZE(mt9m114_config_change_settings),
+	.init_settings = &mt9m114_init_conf[0],
+	.init_size = ARRAY_SIZE(mt9m114_init_conf),
+	.mode_settings = &mt9m114_confs[0],
+	.output_settings = &mt9m114_dimensions[0],
+	.num_conf = ARRAY_SIZE(mt9m114_confs),
+};
+
+static struct msm_sensor_ctrl_t mt9m114_s_ctrl = {
+	.msm_sensor_reg = &mt9m114_regs,
+	.msm_sensor_v4l2_ctrl_info = mt9m114_v4l2_ctrl_info,
+	.num_v4l2_ctrl = ARRAY_SIZE(mt9m114_v4l2_ctrl_info),
+	.sensor_i2c_client = &mt9m114_sensor_i2c_client,
+	.sensor_i2c_addr = 0x90,
+	.sensor_output_reg_addr = &mt9m114_reg_addr,
+	.sensor_id_info = &mt9m114_id_info,
+	.cam_mode = MSM_SENSOR_MODE_INVALID,
+	.csi_params = &mt9m114_csi_params_array[0],
+	.msm_sensor_mutex = &mt9m114_mut,
+	.sensor_i2c_driver = &mt9m114_i2c_driver,
+	.sensor_v4l2_subdev_info = mt9m114_subdev_info,
+	.sensor_v4l2_subdev_info_size = ARRAY_SIZE(mt9m114_subdev_info),
+	.sensor_v4l2_subdev_ops = &mt9m114_subdev_ops,
+	.func_tbl = &mt9m114_func_tbl,
+};
+
+module_init(msm_sensor_init_module);
+MODULE_DESCRIPTION("Aptina 1.26MP YUV sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/sensors/ov2720.c b/drivers/media/video/msm/sensors/ov2720.c
new file mode 100644
index 0000000..40867fb
--- /dev/null
+++ b/drivers/media/video/msm/sensors/ov2720.c
@@ -0,0 +1,829 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_sensor.h"
+#include "ov2720.h"
+#define SENSOR_NAME "ov2720"
+#define PLATFORM_DRIVER_NAME "msm_camera_ov2720"
+#define ov2720_obj ov2720_##obj
+
+DEFINE_MUTEX(ov2720_mut);
+static struct msm_sensor_ctrl_t ov2720_s_ctrl;
+
+static struct msm_camera_i2c_reg_conf ov2720_start_settings[] = {
+	{0x0100, 0x01},
+};
+
+static struct msm_camera_i2c_reg_conf ov2720_stop_settings[] = {
+	{0x0100, 0x00},
+};
+
+static struct msm_camera_i2c_reg_conf ov2720_groupon_settings[] = {
+	{0x3208, 0x00},
+};
+
+static struct msm_camera_i2c_reg_conf ov2720_groupoff_settings[] = {
+	{0x3208, 0x10},
+	{0x3208, 0xA0},
+};
+
+static struct msm_camera_i2c_reg_conf ov2720_prev_settings[] = {
+	{0x3800, 0x00},
+	{0x3801, 0x02},
+	{0x3802, 0x00},
+	{0x3803, 0x00},
+	{0x3804, 0x07},
+	{0x3805, 0xA1},
+	{0x3806, 0x04},
+	{0x3807, 0x47},
+	{0x3810, 0x00},
+	{0x3811, 0x09},
+	{0x3812, 0x00},
+	{0x3813, 0x02},
+	{0x3820, 0x80},
+	{0x3821, 0x06},
+	{0x3814, 0x11},
+	{0x3815, 0x11},
+	{0x3612, 0x0b},
+	{0x3618, 0x04},
+	{0x3a08, 0x01},
+	{0x3a09, 0x50},
+	{0x3a0a, 0x01},
+	{0x3a0b, 0x18},
+	{0x3a0d, 0x03},
+	{0x3a0e, 0x03},
+	{0x4520, 0x00},
+	{0x4837, 0x1b},
+	{0x3000, 0xff},
+	{0x3001, 0xff},
+	{0x3002, 0xf0},
+	{0x3600, 0x08},
+	{0x3621, 0xc0},
+	{0x3632, 0xd2},
+	{0x3633, 0x23},
+	{0x3634, 0x54},
+	{0x3f01, 0x0c},
+	{0x5001, 0xc1},
+	{0x3614, 0xf0},
+	{0x3630, 0x2d},
+	{0x370b, 0x62},
+	{0x3706, 0x61},
+	{0x4000, 0x02},
+	{0x4002, 0xc5},
+	{0x4005, 0x08},
+	{0x404f, 0x84},
+	{0x4051, 0x00},
+	{0x5000, 0xcf},
+	{0x3a18, 0x00},
+	{0x3a19, 0x80},
+	{0x3503, 0x03},
+	{0x4521, 0x00},
+	{0x5183, 0xb0},
+	{0x5184, 0xb0},
+	{0x5185, 0xb0},
+	{0x370c, 0x0c},
+	{0x3035, 0x10},
+	{0x3036, 0x1e},
+	{0x3037, 0x21},
+	{0x303e, 0x19},
+	{0x3038, 0x06},
+	{0x3018, 0x04},
+	{0x3000, 0x00},
+	{0x3001, 0x00},
+	{0x3002, 0x00},
+	{0x3a0f, 0x40},
+	{0x3a10, 0x38},
+	{0x3a1b, 0x48},
+	{0x3a1e, 0x30},
+	{0x3a11, 0x90},
+	{0x3a1f, 0x10},
+	{0x4800, 0x24},
+};
+
+static struct msm_camera_i2c_reg_conf ov2720_720_settings[] = {
+	{0x3800, 0x01},
+	{0x3801, 0x4a},
+	{0x3802, 0x00},
+	{0x3803, 0xba},
+	{0x3804, 0x06},
+	{0x3805, 0x51+32},
+	{0x3806, 0x03},
+	{0x3807, 0x8d+24},
+	{0x3810, 0x00},
+	{0x3811, 0x05},
+	{0x3812, 0x00},
+	{0x3813, 0x02},
+	{0x3820, 0x80},
+	{0x3821, 0x06},
+	{0x3814, 0x11},
+	{0x3815, 0x11},
+	{0x3612, 0x0b},
+	{0x3618, 0x04},
+	{0x3a08, 0x01},
+	{0x3a09, 0x50},
+	{0x3a0a, 0x01},
+	{0x3a0b, 0x18},
+	{0x3a0d, 0x03},
+	{0x3a0e, 0x03},
+	{0x4520, 0x00},
+	{0x4837, 0x1b},
+	{0x3000, 0xff},
+	{0x3001, 0xff},
+	{0x3002, 0xf0},
+	{0x3600, 0x08},
+	{0x3621, 0xc0},
+	{0x3632, 0xd2},
+	{0x3633, 0x23},
+	{0x3634, 0x54},
+	{0x3f01, 0x0c},
+	{0x5001, 0xc1},
+	{0x3614, 0xf0},
+	{0x3630, 0x2d},
+	{0x370b, 0x62},
+	{0x3706, 0x61},
+	{0x4000, 0x02},
+	{0x4002, 0xc5},
+	{0x4005, 0x08},
+	{0x404f, 0x84},
+	{0x4051, 0x00},
+	{0x5000, 0xff},
+	{0x3a18, 0x00},
+	{0x3a19, 0x80},
+	{0x3503, 0x13},
+	{0x4521, 0x00},
+	{0x5183, 0xb0},
+	{0x5184, 0xb0},
+	{0x5185, 0xb0},
+	{0x370c, 0x0c},
+	{0x3035, 0x10},
+	{0x3036, 0x04},
+	{0x3037, 0x61},
+	{0x303e, 0x19},
+	{0x3038, 0x06},
+	{0x3018, 0x04},
+	{0x3000, 0x00},
+	{0x3001, 0x00},
+	{0x3002, 0x00},
+	{0x3a0f, 0x40},
+	{0x3a10, 0x38},
+	{0x3a1b, 0x48},
+	{0x3a1e, 0x30},
+	{0x3a11, 0x90},
+	{0x3a1f, 0x10},
+	{0x4800, 0x24},
+};
+
+static struct msm_camera_i2c_reg_conf ov2720_vga_settings[] = {
+	{0x3800, 0x00},
+	{0x3801, 0x0c},
+	{0x3802, 0x00},
+	{0x3803, 0x02},
+	{0x3804, 0x07},
+	{0x3805, 0x97+32},
+	{0x3806, 0x04},
+	{0x3807, 0x45+24},
+	{0x3810, 0x00},
+	{0x3811, 0x03},
+	{0x3812, 0x00},
+	{0x3813, 0x03},
+	{0x3820, 0x80},
+	{0x3821, 0x06},
+	{0x3814, 0x11},
+	{0x3815, 0x11},
+	{0x3612, 0x0b},
+	{0x3618, 0x04},
+	{0x3a08, 0x01},
+	{0x3a09, 0x50},
+	{0x3a0a, 0x01},
+	{0x3a0b, 0x18},
+	{0x3a0d, 0x03},
+	{0x3a0e, 0x03},
+	{0x4520, 0x00},
+	{0x4837, 0x1b},
+	{0x3000, 0xff},
+	{0x3001, 0xff},
+	{0x3002, 0xf0},
+	{0x3600, 0x08},
+	{0x3621, 0xc0},
+	{0x3632, 0xd2},
+	{0x3633, 0x23},
+	{0x3634, 0x54},
+	{0x3f01, 0x0c},
+	{0x5001, 0xc1},
+	{0x3614, 0xf0},
+	{0x3630, 0x2d},
+	{0x370b, 0x62},
+	{0x3706, 0x61},
+	{0x4000, 0x02},
+	{0x4002, 0xc5},
+	{0x4005, 0x08},
+	{0x404f, 0x84},
+	{0x4051, 0x00},
+	{0x5000, 0xff},
+	{0x3a18, 0x00},
+	{0x3a19, 0x80},
+	{0x3503, 0x13},
+	{0x4521, 0x00},
+	{0x5183, 0xb0},
+	{0x5184, 0xb0},
+	{0x5185, 0xb0},
+	{0x370c, 0x0c},
+	{0x3035, 0x10},
+	{0x3036, 0x04},
+	{0x3037, 0x61},
+	{0x303e, 0x19},
+	{0x3038, 0x06},
+	{0x3018, 0x04},
+	{0x3000, 0x00},
+	{0x3001, 0x00},
+	{0x3002, 0x00},
+	{0x3a0f, 0x40},
+	{0x3a10, 0x38},
+	{0x3a1b, 0x48},
+	{0x3a1e, 0x30},
+	{0x3a11, 0x90},
+	{0x3a1f, 0x10},
+	{0x4800, 0x24},
+	{0x3500, 0x00},
+	{0x3501, 0x17},
+	{0x3502, 0xf0},
+	{0x3508, 0x00},
+	{0x3509, 0x20},
+};
+
+static struct msm_camera_i2c_reg_conf ov2720_60fps_settings[] = {
+	{0x3718, 0x10},
+	{0x3702, 0x18},
+	{0x373a, 0x3c},
+	{0x3715, 0x01},
+	{0x3703, 0x1d},
+	{0x3705, 0x0b},
+	{0x3730, 0x1f},
+	{0x3704, 0x3f},
+	{0x3f06, 0x1d},
+	{0x371c, 0x00},
+	{0x371d, 0x83},
+	{0x371e, 0x00},
+	{0x371f, 0xb6},
+	{0x3708, 0x63},
+	{0x3709, 0x52},
+	{0x3800, 0x01},
+	{0x3801, 0x42},
+	{0x3802, 0x00},
+	{0x3803, 0x40},
+	{0x3804, 0x06},
+	{0x3805, 0x61},
+	{0x3806, 0x04},
+	{0x3807, 0x08},
+	{0x3808, 0x02},
+	{0x3809, 0x80},
+	{0x380a, 0x01},
+	{0x380b, 0xe0},
+	{0x380c, 0x03},
+	{0x380d, 0x0c},
+	{0x380e, 0x02},
+	{0x380f, 0x00},
+	{0x3810, 0x00},
+	{0x3811, 0x0f},
+	{0x3812, 0x00},
+	{0x3813, 0x02},
+	{0x3820, 0x80},
+	{0x3821, 0x06},
+	{0x3814, 0x31},
+	{0x3815, 0x31},
+	{0x3612, 0x0b},
+	{0x3618, 0x04},
+	{0x3a08, 0x02},
+	{0x3a09, 0x67},
+	{0x3a0a, 0x02},
+	{0x3a0b, 0x00},
+	{0x3a0d, 0x00},
+	{0x3a0e, 0x00},
+	{0x4520, 0x0a},
+	{0x4837, 0x29},
+	{0x3000, 0xff},
+	{0x3001, 0xff},
+	{0x3002, 0xf0},
+	{0x3600, 0x08},
+	{0x3621, 0xc0},
+	{0x3632, 0xd2},
+	{0x3633, 0x23},
+	{0x3634, 0x54},
+	{0x3f01, 0x0c},
+	{0x5001, 0xc1},
+	{0x3614, 0xf0},
+	{0x3630, 0x2d},
+	{0x370b, 0x62},
+	{0x3706, 0x61},
+	{0x4000, 0x02},
+	{0x4002, 0xc5},
+	{0x4005, 0x08},
+	{0x404f, 0x84},
+	{0x4051, 0x00},
+	{0x5000, 0xcf},
+	{0x3a18, 0x00},
+	{0x3a19, 0x80},
+	{0x3503, 0x07},
+	{0x4521, 0x00},
+	{0x5183, 0xb0},
+	{0x5184, 0xb0},
+	{0x5185, 0xb0},
+	{0x370c, 0x0c},
+	{0x3035, 0x30},
+	{0x3036, 0x14},
+	{0x3037, 0x21},
+	{0x303e, 0x19},
+	{0x3038, 0x06},
+	{0x3018, 0x04},
+	{0x3000, 0x00},
+	{0x3001, 0x00},
+	{0x3002, 0x00},
+	{0x3a0f, 0x40},
+	{0x3a10, 0x38},
+	{0x3a1b, 0x48},
+	{0x3a1e, 0x30},
+	{0x3a11, 0x90},
+	{0x3a1f, 0x10},
+	{0x3011, 0x22},
+	{0x3a00, 0x58},
+};
+
+static struct msm_camera_i2c_reg_conf ov2720_90fps_settings[] = {
+	{0x3718, 0x10},
+	{0x3702, 0x18},
+	{0x373a, 0x3c},
+	{0x3715, 0x01},
+	{0x3703, 0x1d},
+	{0x3705, 0x0b},
+	{0x3730, 0x1f},
+	{0x3704, 0x3f},
+	{0x3f06, 0x1d},
+	{0x371c, 0x00},
+	{0x371d, 0x83},
+	{0x371e, 0x00},
+	{0x371f, 0xb6},
+	{0x3708, 0x63},
+	{0x3709, 0x52},
+	{0x3800, 0x01},
+	{0x3801, 0x42},
+	{0x3802, 0x00},
+	{0x3803, 0x40},
+	{0x3804, 0x06},
+	{0x3805, 0x61},
+	{0x3806, 0x04},
+	{0x3807, 0x08},
+	{0x3808, 0x02},
+	{0x3809, 0x80},
+	{0x380a, 0x01},
+	{0x380b, 0xe0},
+	{0x380c, 0x03},
+	{0x380d, 0x0c},
+	{0x380e, 0x02},
+	{0x380f, 0x00},
+	{0x3810, 0x00},
+	{0x3811, 0x0f},
+	{0x3812, 0x00},
+	{0x3813, 0x02},
+	{0x3820, 0x80},
+	{0x3821, 0x06},
+	{0x3814, 0x31},
+	{0x3815, 0x31},
+	{0x3612, 0x0b},
+	{0x3618, 0x04},
+	{0x3a08, 0x02},
+	{0x3a09, 0x67},
+	{0x3a0a, 0x02},
+	{0x3a0b, 0x00},
+	{0x3a0d, 0x00},
+	{0x3a0e, 0x00},
+	{0x4520, 0x0a},
+	{0x4837, 0x29},
+	{0x3000, 0xff},
+	{0x3001, 0xff},
+	{0x3002, 0xf0},
+	{0x3600, 0x08},
+	{0x3621, 0xc0},
+	{0x3632, 0xd2},
+	{0x3633, 0x23},
+	{0x3634, 0x54},
+	{0x3f01, 0x0c},
+	{0x5001, 0xc1},
+	{0x3614, 0xf0},
+	{0x3630, 0x2d},
+	{0x370b, 0x62},
+	{0x3706, 0x61},
+	{0x4000, 0x02},
+	{0x4002, 0xc5},
+	{0x4005, 0x08},
+	{0x404f, 0x84},
+	{0x4051, 0x00},
+	{0x5000, 0xcf},
+	{0x3a18, 0x00},
+	{0x3a19, 0x80},
+	{0x3503, 0x07},
+	{0x4521, 0x00},
+	{0x5183, 0xb0},
+	{0x5184, 0xb0},
+	{0x5185, 0xb0},
+	{0x370c, 0x0c},
+	{0x3035, 0x30},
+	{0x3036, 0x1e},
+	{0x3037, 0x21},
+	{0x303e, 0x19},
+	{0x3038, 0x06},
+	{0x3018, 0x04},
+	{0x3000, 0x00},
+	{0x3001, 0x00},
+	{0x3002, 0x00},
+	{0x3a0f, 0x40},
+	{0x3a10, 0x38},
+	{0x3a1b, 0x48},
+	{0x3a1e, 0x30},
+	{0x3a11, 0x90},
+	{0x3a1f, 0x10},
+	{0x3011, 0x22},
+	{0x3a00, 0x58},
+};
+
+static struct msm_camera_i2c_reg_conf ov2720_120fps_settings[] = {
+	{0x3718, 0x10},
+	{0x3702, 0x18},
+	{0x373a, 0x3c},
+	{0x3715, 0x01},
+	{0x3703, 0x1d},
+	{0x3705, 0x0b},
+	{0x3730, 0x1f},
+	{0x3704, 0x3f},
+	{0x3f06, 0x1d},
+	{0x371c, 0x00},
+	{0x371d, 0x83},
+	{0x371e, 0x00},
+	{0x371f, 0xb6},
+	{0x3708, 0x63},
+	{0x3709, 0x52},
+	{0x3800, 0x01},
+	{0x3801, 0x42},
+	{0x3802, 0x00},
+	{0x3803, 0x40},
+	{0x3804, 0x06},
+	{0x3805, 0x61},
+	{0x3806, 0x04},
+	{0x3807, 0x08},
+	{0x3808, 0x02},
+	{0x3809, 0x80},
+	{0x380a, 0x01},
+	{0x380b, 0xe0},
+	{0x380c, 0x03},
+	{0x380d, 0x0c},
+	{0x380e, 0x02},
+	{0x380f, 0x00},
+	{0x3810, 0x00},
+	{0x3811, 0x0f},
+	{0x3812, 0x00},
+	{0x3813, 0x02},
+	{0x3820, 0x80},
+	{0x3821, 0x06},
+	{0x3814, 0x31},
+	{0x3815, 0x31},
+	{0x3612, 0x0b},
+	{0x3618, 0x04},
+	{0x3a08, 0x02},
+	{0x3a09, 0x67},
+	{0x3a0a, 0x02},
+	{0x3a0b, 0x00},
+	{0x3a0d, 0x00},
+	{0x3a0e, 0x00},
+	{0x4520, 0x0a},
+	{0x4837, 0x29},
+	{0x3000, 0xff},
+	{0x3001, 0xff},
+	{0x3002, 0xf0},
+	{0x3600, 0x08},
+	{0x3621, 0xc0},
+	{0x3632, 0xd2},
+	{0x3633, 0x23},
+	{0x3634, 0x54},
+	{0x3f01, 0x0c},
+	{0x5001, 0xc1},
+	{0x3614, 0xf0},
+	{0x3630, 0x2d},
+	{0x370b, 0x62},
+	{0x3706, 0x61},
+	{0x4000, 0x02},
+	{0x4002, 0xc5},
+	{0x4005, 0x08},
+	{0x404f, 0x84},
+	{0x4051, 0x00},
+	{0x5000, 0xcf},
+	{0x3a18, 0x00},
+	{0x3a19, 0x80},
+	{0x3503, 0x07},
+	{0x4521, 0x00},
+	{0x5183, 0xb0},
+	{0x5184, 0xb0},
+	{0x5185, 0xb0},
+	{0x370c, 0x0c},
+	{0x3035, 0x10},
+	{0x3036, 0x14},
+	{0x3037, 0x21},
+	{0x303e, 0x19},
+	{0x3038, 0x06},
+	{0x3018, 0x04},
+	{0x3000, 0x00},
+	{0x3001, 0x00},
+	{0x3002, 0x00},
+	{0x3a0f, 0x40},
+	{0x3a10, 0x38},
+	{0x3a1b, 0x48},
+	{0x3a1e, 0x30},
+	{0x3a11, 0x90},
+	{0x3a1f, 0x10},
+	{0x3011, 0x22},
+	{0x3a00, 0x58},
+};
+
+static struct msm_camera_i2c_reg_conf ov2720_recommend_settings[] = {
+	{0x0103, 0x01},
+	{0x3718, 0x10},
+	{0x3702, 0x24},
+	{0x373a, 0x60},
+	{0x3715, 0x01},
+	{0x3703, 0x2e},
+	{0x3705, 0x10},
+	{0x3730, 0x30},
+	{0x3704, 0x62},
+	{0x3f06, 0x3a},
+	{0x371c, 0x00},
+	{0x371d, 0xc4},
+	{0x371e, 0x01},
+	{0x371f, 0x0d},
+	{0x3708, 0x61},
+	{0x3709, 0x12},
+};
+
+static struct v4l2_subdev_info ov2720_subdev_info[] = {
+	{
+	.code   = V4L2_MBUS_FMT_SBGGR10_1X10,
+	.colorspace = V4L2_COLORSPACE_JPEG,
+	.fmt    = 1,
+	.order    = 0,
+	},
+	/* more can be supported, to be added later */
+};
+
+static struct msm_camera_i2c_conf_array ov2720_init_conf[] = {
+	{&ov2720_recommend_settings[0],
+	ARRAY_SIZE(ov2720_recommend_settings), 0, MSM_CAMERA_I2C_BYTE_DATA}
+};
+
+static struct msm_camera_i2c_conf_array ov2720_confs[] = {
+	{&ov2720_prev_settings[0],
+	ARRAY_SIZE(ov2720_prev_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+	{&ov2720_vga_settings[0],
+	ARRAY_SIZE(ov2720_vga_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+	{&ov2720_720_settings[0],
+	ARRAY_SIZE(ov2720_720_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+	{&ov2720_60fps_settings[0],
+	ARRAY_SIZE(ov2720_60fps_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+	{&ov2720_90fps_settings[0],
+	ARRAY_SIZE(ov2720_90fps_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+	{&ov2720_120fps_settings[0],
+	ARRAY_SIZE(ov2720_120fps_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+};
+
+static struct msm_sensor_output_info_t ov2720_dimensions[] = {
+	{
+		.x_output = 0x78C,
+		.y_output = 0x444,
+		.line_length_pclk = 0x85c,
+		.frame_length_lines = 0x460,
+		.vt_pixel_clk = 72000000,
+		.op_pixel_clk = 72000000,
+		.binning_factor = 1,
+	},
+	{
+		.x_output = 0x510,
+		.y_output = 0x278,
+		.line_length_pclk = 0x85c,
+		.frame_length_lines = 0x460,
+		.vt_pixel_clk = 72000000,
+		.op_pixel_clk = 72000000,
+		.binning_factor = 1,
+	},
+	{
+		.x_output = 0x298,
+		.y_output = 0x1F2,
+		.line_length_pclk = 0x85c,
+		.frame_length_lines = 0x460,
+		.vt_pixel_clk = 72000000,
+		.op_pixel_clk = 72000000,
+		.binning_factor = 1,
+	},
+	{
+		.x_output = 0x280, /* 640 */
+		.y_output = 0x1E0, /* 480 */
+		.line_length_pclk = 0x30C, /* 780 */
+		.frame_length_lines = 0x200, /* 512 */
+		.vt_pixel_clk = 24000000,
+		.op_pixel_clk = 24000000,
+		.binning_factor = 1,
+	},
+	{
+		.x_output = 0x280, /* 640 */
+		.y_output = 0x1E0, /* 480 */
+		.line_length_pclk = 0x30C, /* 780 */
+		.frame_length_lines = 0x200, /* 512 */
+		.vt_pixel_clk = 36000000,
+		.op_pixel_clk = 36000000,
+		.binning_factor = 1,
+	},
+	{
+		.x_output = 0x280, /* 640 */
+		.y_output = 0x1E0, /* 480 */
+		.line_length_pclk = 0x30C, /* 780 */
+		.frame_length_lines = 0x200, /* 512 */
+		.vt_pixel_clk = 48000000,
+		.op_pixel_clk = 48000000,
+		.binning_factor = 1,
+	},
+};
+
+static struct msm_camera_csid_vc_cfg ov2720_cid_cfg[] = {
+	{0, CSI_RAW10, CSI_DECODE_10BIT},
+	{1, CSI_EMBED_DATA, CSI_DECODE_8BIT},
+};
+
+static struct msm_camera_csi2_params ov2720_csi_params = {
+	.csid_params = {
+		.lane_cnt = 2,
+		.lut_params = {
+			.num_cid = 2,
+			.vc_cfg = ov2720_cid_cfg,
+		},
+	},
+	.csiphy_params = {
+		.lane_cnt = 2,
+		.settle_cnt = 0x1B,
+	},
+};
+
+static struct msm_camera_csi2_params *ov2720_csi_params_array[] = {
+	&ov2720_csi_params,
+	&ov2720_csi_params,
+	&ov2720_csi_params,
+	&ov2720_csi_params,
+	&ov2720_csi_params,
+	&ov2720_csi_params,
+};
+
+static struct msm_sensor_output_reg_addr_t ov2720_reg_addr = {
+	.x_output = 0x3808,
+	.y_output = 0x380a,
+	.line_length_pclk = 0x380c,
+	.frame_length_lines = 0x380e,
+};
+
+static struct msm_sensor_id_info_t ov2720_id_info = {
+	.sensor_id_reg_addr = 0x300A,
+	.sensor_id = 0x2720,
+};
+
+static struct msm_sensor_exp_gain_info_t ov2720_exp_gain_info = {
+	.coarse_int_time_addr = 0x3501,
+	.global_gain_addr = 0x3508,
+	.vert_offset = 6,
+};
+
+static int32_t ov2720_write_exp_gain(struct msm_sensor_ctrl_t *s_ctrl,
+		uint16_t gain, uint32_t line)
+{
+	uint32_t fl_lines, offset;
+	uint8_t int_time[3];
+	fl_lines =
+		(s_ctrl->curr_frame_length_lines * s_ctrl->fps_divider) / Q10;
+	offset = s_ctrl->sensor_exp_gain_info->vert_offset;
+	if (line > (fl_lines - offset))
+		fl_lines = line + offset;
+
+	s_ctrl->func_tbl->sensor_group_hold_on(s_ctrl);
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_output_reg_addr->frame_length_lines, fl_lines,
+		MSM_CAMERA_I2C_WORD_DATA);
+	int_time[0] = line >> 12;
+	int_time[1] = line >> 4;
+	int_time[2] = line << 4;
+	msm_camera_i2c_write_seq(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->coarse_int_time_addr-1,
+		&int_time[0], 3);
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->global_gain_addr, gain,
+		MSM_CAMERA_I2C_WORD_DATA);
+	s_ctrl->func_tbl->sensor_group_hold_off(s_ctrl);
+	return 0;
+}
+
+static const struct i2c_device_id ov2720_i2c_id[] = {
+	{SENSOR_NAME, (kernel_ulong_t)&ov2720_s_ctrl},
+	{ }
+};
+
+static struct i2c_driver ov2720_i2c_driver = {
+	.id_table = ov2720_i2c_id,
+	.probe  = msm_sensor_i2c_probe,
+	.driver = {
+		.name = SENSOR_NAME,
+	},
+};
+
+static struct msm_camera_i2c_client ov2720_sensor_i2c_client = {
+	.addr_type = MSM_CAMERA_I2C_WORD_ADDR,
+};
+
+static int __init msm_sensor_init_module(void)
+{
+	return i2c_add_driver(&ov2720_i2c_driver);
+}
+
+static struct v4l2_subdev_core_ops ov2720_subdev_core_ops = {
+	.ioctl = msm_sensor_subdev_ioctl,
+	.s_power = msm_sensor_power,
+};
+
+static struct v4l2_subdev_video_ops ov2720_subdev_video_ops = {
+	.enum_mbus_fmt = msm_sensor_v4l2_enum_fmt,
+};
+
+static struct v4l2_subdev_ops ov2720_subdev_ops = {
+	.core = &ov2720_subdev_core_ops,
+	.video  = &ov2720_subdev_video_ops,
+};
+
+static struct msm_sensor_fn_t ov2720_func_tbl = {
+	.sensor_start_stream = msm_sensor_start_stream,
+	.sensor_stop_stream = msm_sensor_stop_stream,
+	.sensor_group_hold_on = msm_sensor_group_hold_on,
+	.sensor_group_hold_off = msm_sensor_group_hold_off,
+	.sensor_set_fps = msm_sensor_set_fps,
+	.sensor_write_exp_gain = ov2720_write_exp_gain,
+	.sensor_write_snapshot_exp_gain = ov2720_write_exp_gain,
+	.sensor_setting = msm_sensor_setting,
+	.sensor_set_sensor_mode = msm_sensor_set_sensor_mode,
+	.sensor_mode_init = msm_sensor_mode_init,
+	.sensor_get_output_info = msm_sensor_get_output_info,
+	.sensor_config = msm_sensor_config,
+	.sensor_power_up = msm_sensor_power_up,
+	.sensor_power_down = msm_sensor_power_down,
+	.sensor_adjust_frame_lines = msm_sensor_adjust_frame_lines,
+};
+
+static struct msm_sensor_reg_t ov2720_regs = {
+	.default_data_type = MSM_CAMERA_I2C_BYTE_DATA,
+	.start_stream_conf = ov2720_start_settings,
+	.start_stream_conf_size = ARRAY_SIZE(ov2720_start_settings),
+	.stop_stream_conf = ov2720_stop_settings,
+	.stop_stream_conf_size = ARRAY_SIZE(ov2720_stop_settings),
+	.group_hold_on_conf = ov2720_groupon_settings,
+	.group_hold_on_conf_size = ARRAY_SIZE(ov2720_groupon_settings),
+	.group_hold_off_conf = ov2720_groupoff_settings,
+	.group_hold_off_conf_size =
+		ARRAY_SIZE(ov2720_groupoff_settings),
+	.init_settings = &ov2720_init_conf[0],
+	.init_size = ARRAY_SIZE(ov2720_init_conf),
+	.mode_settings = &ov2720_confs[0],
+	.output_settings = &ov2720_dimensions[0],
+	.num_conf = ARRAY_SIZE(ov2720_confs),
+};
+
+static struct msm_sensor_ctrl_t ov2720_s_ctrl = {
+	.msm_sensor_reg = &ov2720_regs,
+	.sensor_i2c_client = &ov2720_sensor_i2c_client,
+	.sensor_i2c_addr = 0x6C,
+	.sensor_output_reg_addr = &ov2720_reg_addr,
+	.sensor_id_info = &ov2720_id_info,
+	.sensor_exp_gain_info = &ov2720_exp_gain_info,
+	.cam_mode = MSM_SENSOR_MODE_INVALID,
+	.csi_params = &ov2720_csi_params_array[0],
+	.msm_sensor_mutex = &ov2720_mut,
+	.sensor_i2c_driver = &ov2720_i2c_driver,
+	.sensor_v4l2_subdev_info = ov2720_subdev_info,
+	.sensor_v4l2_subdev_info_size = ARRAY_SIZE(ov2720_subdev_info),
+	.sensor_v4l2_subdev_ops = &ov2720_subdev_ops,
+	.func_tbl = &ov2720_func_tbl,
+	.clk_rate = MSM_SENSOR_MCLK_24HZ,
+};
+
+module_init(msm_sensor_init_module);
+MODULE_DESCRIPTION("Omnivision 2MP Bayer sensor driver");
+MODULE_LICENSE("GPL v2");
+
+
diff --git a/drivers/media/video/msm/sensors/ov2720.h b/drivers/media/video/msm/sensors/ov2720.h
new file mode 100644
index 0000000..7077a7d
--- /dev/null
+++ b/drivers/media/video/msm/sensors/ov2720.h
@@ -0,0 +1,16 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <mach/board.h>
+extern struct platform_driver ov2720_driver;
+
diff --git a/drivers/media/video/msm/sensors/ov5647_v4l2.c b/drivers/media/video/msm/sensors/ov5647_v4l2.c
new file mode 100644
index 0000000..aac2f2b
--- /dev/null
+++ b/drivers/media/video/msm/sensors/ov5647_v4l2.c
@@ -0,0 +1,868 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_sensor.h"
+#include "msm.h"
+#define SENSOR_NAME "ov5647"
+#define PLATFORM_DRIVER_NAME "msm_camera_ov5647"
+#define ov5647_obj ov5647_##obj
+
+static struct msm_sensor_ctrl_t ov5647_s_ctrl;
+
+DEFINE_MUTEX(ov5647_mut);
+
+static struct msm_camera_i2c_reg_conf ov5647_start_settings[] = {
+	{0x4202, 0x00},  /* streaming on */
+};
+
+static struct msm_camera_i2c_reg_conf ov5647_stop_settings[] = {
+	{0x4202, 0x0f},  /* streaming off*/
+};
+
+static struct msm_camera_i2c_reg_conf ov5647_groupon_settings[] = {
+	{0x3208, 0x0},
+};
+
+static struct msm_camera_i2c_reg_conf ov5647_groupoff_settings[] = {
+	{0x3208, 0x10},
+	{0x3208, 0xa0},
+};
+
+static struct msm_camera_i2c_reg_conf ov5647_prev_settings[] = {
+	/*1280*960 Reference Setting 24M MCLK 2lane 280Mbps/lane 30fps
+	for back to preview*/
+	{0x3035, 0x21},
+	{0x3036, 0x37},
+	{0x3821, 0x07},
+	{0x3820, 0x41},
+	{0x3612, 0x09},
+	{0x3618, 0x00},
+	{0x380c, 0x07},
+	{0x380d, 0x68},
+	{0x380e, 0x03},
+	{0x380f, 0xd8},
+	{0x3814, 0x31},
+	{0x3815, 0x31},
+	{0x3709, 0x52},
+	{0x3808, 0x05},
+	{0x3809, 0x00},
+	{0x380a, 0x03},
+	{0x380b, 0xc0},
+	{0x3800, 0x00},
+	{0x3801, 0x18},
+	{0x3802, 0x00},
+	{0x3803, 0x0e},
+	{0x3804, 0x0a},
+	{0x3805, 0x27},
+	{0x3806, 0x07},
+	{0x3807, 0x95},
+	{0x4004, 0x02},
+};
+
+static struct msm_camera_i2c_reg_conf ov5647_snap_settings[] = {
+	/*2608*1952 Reference Setting 24M MCLK 2lane 280Mbps/lane 30fps*/
+	{0x3035, 0x21},
+	{0x3036, 0x4f},
+	{0x3821, 0x06},
+	{0x3820, 0x00},
+	{0x3612, 0x0b},
+	{0x3618, 0x04},
+	{0x380c, 0x0a},
+	{0x380d, 0x8c},
+	{0x380e, 0x07},
+	{0x380f, 0xb0},
+	{0x3814, 0x11},
+	{0x3815, 0x11},
+	{0x3709, 0x12},
+	{0x3808, 0x0a},
+	{0x3809, 0x30},
+	{0x380a, 0x07},
+	{0x380b, 0xa0},
+	{0x3800, 0x00},
+	{0x3801, 0x04},
+	{0x3802, 0x00},
+	{0x3803, 0x00},
+	{0x3804, 0x0a},
+	{0x3805, 0x3b},
+	{0x3806, 0x07},
+	{0x3807, 0xa3},
+	{0x4004, 0x04},
+};
+
+static struct msm_camera_i2c_reg_conf ov5647_video_60fps_settings[] = {
+	{0x3035, 0x21},
+	{0x3036, 0x38},
+	{0x3821, 0x07},
+	{0x3820, 0x41},
+	{0x3612, 0x49},
+	{0x3618, 0x00},
+	{0x380c, 0x07},
+	{0x380d, 0x30},
+	{0x380e, 0x01},
+	{0x380f, 0xf8},
+	{0x3814, 0x71},
+	{0x3815, 0x71},
+	{0x3709, 0x52},
+	{0x3808, 0x02},
+	{0x3809, 0x80},
+	{0x380a, 0x01},
+	{0x380b, 0xe0},
+	{0x3800, 0x00},
+	{0x3801, 0x10},
+	{0x3802, 0x00},
+	{0x3803, 0x00},
+	{0x3804, 0x0a},
+	{0x3805, 0x2f},
+	{0x3806, 0x07},
+	{0x3807, 0x9f},
+	{0x4004, 0x02},
+};
+
+static struct msm_camera_i2c_reg_conf ov5647_video_90fps_settings[] = {
+	{0x3035, 0x11},
+	{0x3036, 0x2a},
+	{0x3821, 0x07},
+	{0x3820, 0x41},
+	{0x3612, 0x49},
+	{0x3618, 0x00},
+	{0x380c, 0x07},
+	{0x380d, 0x30},
+	{0x380e, 0x01},
+	{0x380f, 0xf8},
+	{0x3814, 0x71},
+	{0x3815, 0x71},
+	{0x3709, 0x52},
+	{0x3808, 0x02},
+	{0x3809, 0x80},
+	{0x380a, 0x01},
+	{0x380b, 0xe0},
+	{0x3800, 0x00},
+	{0x3801, 0x10},
+	{0x3802, 0x00},
+	{0x3803, 0x00},
+	{0x3804, 0x0a},
+	{0x3805, 0x2f},
+	{0x3806, 0x07},
+	{0x3807, 0x9f},
+	{0x4004, 0x02},
+};
+
+static struct msm_camera_i2c_reg_conf ov5647_zsl_settings[] = {
+	{0x3035, 0x21},
+	{0x3036, 0x2f},
+	{0x3821, 0x06},
+	{0x3820, 0x00},
+	{0x3612, 0x0b},
+	{0x3618, 0x04},
+	{0x380c, 0x0a},
+	{0x380d, 0x8c},
+	{0x380e, 0x07},
+	{0x380f, 0xb0},
+	{0x3814, 0x11},
+	{0x3815, 0x11},
+	{0x3709, 0x12},
+	{0x3808, 0x0a},
+	{0x3809, 0x30},
+	{0x380a, 0x07},
+	{0x380b, 0xa0},
+	{0x3800, 0x00},
+	{0x3801, 0x04},
+	{0x3802, 0x00},
+	{0x3803, 0x00},
+	{0x3804, 0x0a},
+	{0x3805, 0x3b},
+	{0x3806, 0x07},
+	{0x3807, 0xa3},
+	{0x4004, 0x04},
+};
+
+static struct msm_camera_i2c_reg_conf ov5647_recommend_settings[] = {
+	{0x3035, 0x11},
+	{0x303c, 0x11},
+	{0x370c, 0x03},
+	{0x5000, 0x06},
+	{0x5003, 0x08},
+	{0x5a00, 0x08},
+	{0x3000, 0xff},
+	{0x3001, 0xff},
+	{0x3002, 0xff},
+	{0x301d, 0xf0},
+	{0x3a18, 0x00},
+	{0x3a19, 0xf8},
+	{0x3c01, 0x80},
+	{0x3b07, 0x0c},
+	{0x3708, 0x64},
+	{0x3630, 0x2e},
+	{0x3632, 0xe2},
+	{0x3633, 0x23},
+	{0x3634, 0x44},
+	{0x3620, 0x64},
+	{0x3621, 0xe0},
+	{0x3600, 0x37},
+	{0x3704, 0xa0},
+	{0x3703, 0x5a},
+	{0x3715, 0x78},
+	{0x3717, 0x01},
+	{0x3731, 0x02},
+	{0x370b, 0x60},
+	{0x3705, 0x1a},
+	{0x3f05, 0x02},
+	{0x3f06, 0x10},
+	{0x3f01, 0x0a},
+	{0x3a08, 0x01},
+	{0x3a0f, 0x58},
+	{0x3a10, 0x50},
+	{0x3a1b, 0x58},
+	{0x3a1e, 0x50},
+	{0x3a11, 0x60},
+	{0x3a1f, 0x28},
+	{0x4001, 0x02},
+	{0x4000, 0x09},
+	{0x3000, 0x00},
+	{0x3001, 0x00},
+	{0x3002, 0x00},
+	{0x3017, 0xe0},
+	{0x301c, 0xfc},
+	{0x3636, 0x06},
+	{0x3016, 0x08},
+	{0x3827, 0xec},
+	{0x3018, 0x44},
+	{0x3035, 0x21},
+	{0x3106, 0xf5},
+	{0x3034, 0x18},
+	{0x301c, 0xf8},
+	/*lens setting*/
+	{0x5000, 0x86},
+	{0x5800, 0x11},
+	{0x5801, 0x0c},
+	{0x5802, 0x0a},
+	{0x5803, 0x0b},
+	{0x5804, 0x0d},
+	{0x5805, 0x13},
+	{0x5806, 0x09},
+	{0x5807, 0x05},
+	{0x5808, 0x03},
+	{0x5809, 0x03},
+	{0x580a, 0x06},
+	{0x580b, 0x08},
+	{0x580c, 0x05},
+	{0x580d, 0x01},
+	{0x580e, 0x00},
+	{0x580f, 0x00},
+	{0x5810, 0x02},
+	{0x5811, 0x06},
+	{0x5812, 0x05},
+	{0x5813, 0x01},
+	{0x5814, 0x00},
+	{0x5815, 0x00},
+	{0x5816, 0x02},
+	{0x5817, 0x06},
+	{0x5818, 0x09},
+	{0x5819, 0x05},
+	{0x581a, 0x04},
+	{0x581b, 0x04},
+	{0x581c, 0x06},
+	{0x581d, 0x09},
+	{0x581e, 0x11},
+	{0x581f, 0x0c},
+	{0x5820, 0x0b},
+	{0x5821, 0x0b},
+	{0x5822, 0x0d},
+	{0x5823, 0x13},
+	{0x5824, 0x22},
+	{0x5825, 0x26},
+	{0x5826, 0x26},
+	{0x5827, 0x24},
+	{0x5828, 0x24},
+	{0x5829, 0x24},
+	{0x582a, 0x22},
+	{0x582b, 0x20},
+	{0x582c, 0x22},
+	{0x582d, 0x26},
+	{0x582e, 0x22},
+	{0x582f, 0x22},
+	{0x5830, 0x42},
+	{0x5831, 0x22},
+	{0x5832, 0x02},
+	{0x5833, 0x24},
+	{0x5834, 0x22},
+	{0x5835, 0x22},
+	{0x5836, 0x22},
+	{0x5837, 0x26},
+	{0x5838, 0x42},
+	{0x5839, 0x26},
+	{0x583a, 0x06},
+	{0x583b, 0x26},
+	{0x583c, 0x24},
+	{0x583d, 0xce},
+	/* manual AWB,manual AE,close Lenc,open WBC*/
+	{0x3503, 0x03}, /*manual AE*/
+	{0x3501, 0x10},
+	{0x3502, 0x80},
+	{0x350a, 0x00},
+	{0x350b, 0x7f},
+	{0x5001, 0x01}, /*manual AWB*/
+	{0x5180, 0x08},
+	{0x5186, 0x04},
+	{0x5187, 0x00},
+	{0x5188, 0x04},
+	{0x5189, 0x00},
+	{0x518a, 0x04},
+	{0x518b, 0x00},
+	{0x5000, 0x06}, /*No lenc,WBC on*/
+	{0x4005, 0x18},
+	{0x4051, 0x8f},
+};
+
+
+static struct msm_camera_i2c_conf_array ov5647_init_conf[] = {
+	{&ov5647_recommend_settings[0],
+	ARRAY_SIZE(ov5647_recommend_settings), 0, MSM_CAMERA_I2C_BYTE_DATA}
+};
+
+static struct msm_camera_i2c_conf_array ov5647_confs[] = {
+	{&ov5647_snap_settings[0],
+	ARRAY_SIZE(ov5647_snap_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+	{&ov5647_prev_settings[0],
+	ARRAY_SIZE(ov5647_prev_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+	{&ov5647_video_60fps_settings[0],
+	ARRAY_SIZE(ov5647_video_60fps_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+	{&ov5647_video_90fps_settings[0],
+	ARRAY_SIZE(ov5647_video_90fps_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+	{&ov5647_zsl_settings[0],
+	ARRAY_SIZE(ov5647_zsl_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+};
+
+static struct msm_camera_csi_params ov5647_csi_params = {
+	.data_format = CSI_8BIT,
+	.lane_cnt    = 2,
+	.lane_assign = 0xe4,
+	.dpcm_scheme = 0,
+	.settle_cnt  = 10,
+};
+
+static struct v4l2_subdev_info ov5647_subdev_info[] = {
+	{
+		.code   = V4L2_MBUS_FMT_SBGGR10_1X10,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.fmt    = 1,
+		.order    = 0,
+	},
+	/* more can be supported, to be added later */
+};
+
+static struct msm_sensor_output_info_t ov5647_dimensions[] = {
+	{ /* For SNAPSHOT */
+		.x_output = 0xA30,  /*2608*/  /*for 5Mp*/
+		.y_output = 0x7A0,   /*1952*/
+		.line_length_pclk = 0xA8C,
+		.frame_length_lines = 0x7B0,
+		.vt_pixel_clk = 79704000,
+		.op_pixel_clk = 159408000,
+		.binning_factor = 0x0,
+	},
+	{ /* For PREVIEW */
+		.x_output = 0x500, /*1280*/
+		.y_output = 0x3C0, /*960*/
+		.line_length_pclk = 0x768,
+		.frame_length_lines = 0x3D8,
+		.vt_pixel_clk = 55969920,
+		.op_pixel_clk = 159408000,
+		.binning_factor = 0x0,
+	},
+	{ /* For 60fps */
+		.x_output = 0x280,  /*640*/
+		.y_output = 0x1E0,   /*480*/
+		.line_length_pclk = 0x73C,
+		.frame_length_lines = 0x1F8,
+		.vt_pixel_clk = 56004480,
+		.op_pixel_clk = 159408000,
+		.binning_factor = 0x0,
+	},
+	{ /* For 90fps */
+		.x_output = 0x280,  /*640*/
+		.y_output = 0x1E0,   /*480*/
+		.line_length_pclk = 0x73C,
+		.frame_length_lines = 0x1F8,
+		.vt_pixel_clk = 56004480,
+		.op_pixel_clk = 159408000,
+		.binning_factor = 0x0,
+	},
+	{ /* For ZSL */
+		.x_output = 0xA30,  /*2608*/  /*for 5Mp*/
+		.y_output = 0x7A0,   /*1952*/
+		.line_length_pclk = 0xA8C,
+		.frame_length_lines = 0x7B0,
+		.vt_pixel_clk = 79704000,
+		.op_pixel_clk = 159408000,
+		.binning_factor = 0x0,
+	},
+
+};
+
+static struct msm_sensor_output_reg_addr_t ov5647_reg_addr = {
+	.x_output = 0x3808,
+	.y_output = 0x380A,
+	.line_length_pclk = 0x380C,
+	.frame_length_lines = 0x380E,
+};
+
+static struct msm_camera_csi_params *ov5647_csi_params_array[] = {
+	&ov5647_csi_params, /* Snapshot */
+	&ov5647_csi_params, /* Preview */
+	&ov5647_csi_params, /* 60fps */
+	&ov5647_csi_params, /* 90fps */
+	&ov5647_csi_params, /* ZSL */
+};
+
+static struct msm_sensor_id_info_t ov5647_id_info = {
+	.sensor_id_reg_addr = 0x300a,
+	.sensor_id = 0x5647,
+};
+
+static struct msm_sensor_exp_gain_info_t ov5647_exp_gain_info = {
+	.coarse_int_time_addr = 0x3500,
+	.global_gain_addr = 0x350A,
+	.vert_offset = 4,
+};
+
+void ov5647_sensor_reset_stream(struct msm_sensor_ctrl_t *s_ctrl)
+{
+	msm_camera_i2c_write(
+		s_ctrl->sensor_i2c_client,
+		0x103, 0x1,
+		MSM_CAMERA_I2C_BYTE_DATA);
+}
+
+static int32_t ov5647_write_pict_exp_gain(struct msm_sensor_ctrl_t *s_ctrl,
+		uint16_t gain, uint32_t line)
+{
+
+	static uint16_t max_line = 1964;
+	uint8_t gain_lsb, gain_hsb;
+	u8 intg_time_hsb, intg_time_msb, intg_time_lsb;
+
+	gain_lsb = (uint8_t) (gain);
+	gain_hsb = (uint8_t)((gain & 0x300)>>8);
+
+	CDBG(KERN_ERR "snapshot exposure seting 0x%x, 0x%x, %d"
+		, gain, line, line);
+	s_ctrl->func_tbl->sensor_group_hold_on(s_ctrl);
+	if (line > 1964 && line <= 1968) {
+		msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+			s_ctrl->sensor_output_reg_addr->frame_length_lines,
+			(uint8_t)((line+4) >> 8),
+			MSM_CAMERA_I2C_BYTE_DATA);
+
+		msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+			s_ctrl->sensor_output_reg_addr->frame_length_lines + 1,
+			(uint8_t)((line+4) & 0x00FF),
+			MSM_CAMERA_I2C_BYTE_DATA);
+		max_line = line + 4;
+	} else if (max_line > 1968) {
+		msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+			s_ctrl->sensor_output_reg_addr->frame_length_lines,
+			(uint8_t)(1968 >> 8),
+			MSM_CAMERA_I2C_BYTE_DATA);
+
+		 msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+			s_ctrl->sensor_output_reg_addr->frame_length_lines + 1,
+			(uint8_t)(1968 & 0x00FF),
+			MSM_CAMERA_I2C_BYTE_DATA);
+			max_line = 1968;
+	}
+
+
+	line = line<<4;
+	/* ov5647 need this operation */
+	intg_time_hsb = (u8)(line>>16);
+	intg_time_msb = (u8) ((line & 0xFF00) >> 8);
+	intg_time_lsb = (u8) (line & 0x00FF);
+
+	/* FIXME for BLC trigger */
+	/* Coarse Integration Time */
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->coarse_int_time_addr,
+		intg_time_hsb,
+		MSM_CAMERA_I2C_BYTE_DATA);
+
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->coarse_int_time_addr + 1,
+		intg_time_msb,
+		MSM_CAMERA_I2C_BYTE_DATA);
+
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->coarse_int_time_addr + 2,
+		intg_time_lsb,
+		MSM_CAMERA_I2C_BYTE_DATA);
+
+	/* gain */
+
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->global_gain_addr,
+		gain_hsb,
+		MSM_CAMERA_I2C_BYTE_DATA);
+
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->global_gain_addr + 1,
+		gain_lsb^0x1,
+		MSM_CAMERA_I2C_BYTE_DATA);
+
+	/* Coarse Integration Time */
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->coarse_int_time_addr,
+		intg_time_hsb,
+		MSM_CAMERA_I2C_BYTE_DATA);
+
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->coarse_int_time_addr + 1,
+		intg_time_msb,
+		MSM_CAMERA_I2C_BYTE_DATA);
+
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->coarse_int_time_addr + 2,
+		intg_time_lsb,
+		MSM_CAMERA_I2C_BYTE_DATA);
+
+	/* gain */
+
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->global_gain_addr,
+		gain_hsb,
+		MSM_CAMERA_I2C_BYTE_DATA);
+
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->global_gain_addr + 1,
+		gain_lsb,
+		MSM_CAMERA_I2C_BYTE_DATA);
+
+
+	s_ctrl->func_tbl->sensor_group_hold_off(s_ctrl);
+	return 0;
+
+}
+
+
+static int32_t ov5647_write_prev_exp_gain(struct msm_sensor_ctrl_t *s_ctrl,
+						uint16_t gain, uint32_t line)
+{
+
+	static uint16_t max_line = 984;
+	u8 intg_time_hsb, intg_time_msb, intg_time_lsb;
+	uint8_t gain_lsb, gain_hsb;
+
+	CDBG(KERN_ERR "preview exposure setting 0x%x, 0x%x, %d",
+		 gain, line, line);
+
+	gain_lsb = (uint8_t) (gain);
+	gain_hsb = (uint8_t)((gain & 0x300)>>8);
+
+	s_ctrl->func_tbl->sensor_group_hold_on(s_ctrl);
+
+	/* adjust frame rate */
+	if (line > 980 && line <= 984) {
+		msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_output_reg_addr->frame_length_lines,
+		(uint8_t)((line+4) >> 8),
+		MSM_CAMERA_I2C_BYTE_DATA);
+
+		msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_output_reg_addr->frame_length_lines + 1,
+		(uint8_t)((line+4) & 0x00FF),
+		MSM_CAMERA_I2C_BYTE_DATA);
+		max_line = line + 4;
+	} else if (max_line > 984) {
+
+		msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_output_reg_addr->frame_length_lines,
+		(uint8_t)(984 >> 8),
+		MSM_CAMERA_I2C_BYTE_DATA);
+
+		msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_output_reg_addr->frame_length_lines + 1 ,
+		(uint8_t)(984 & 0x00FF),
+		MSM_CAMERA_I2C_BYTE_DATA);
+		max_line = 984;
+	}
+
+	line = line<<4;
+	/* ov5647 need this operation */
+	intg_time_hsb = (u8)(line>>16);
+	intg_time_msb = (u8) ((line & 0xFF00) >> 8);
+	intg_time_lsb = (u8) (line & 0x00FF);
+
+
+	/* Coarse Integration Time */
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->coarse_int_time_addr,
+		intg_time_hsb,
+		MSM_CAMERA_I2C_BYTE_DATA);
+
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->coarse_int_time_addr + 1,
+		intg_time_msb,
+		MSM_CAMERA_I2C_BYTE_DATA);
+
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->coarse_int_time_addr + 2,
+		intg_time_lsb,
+		MSM_CAMERA_I2C_BYTE_DATA);
+
+	/* gain */
+
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->global_gain_addr,
+		gain_hsb,
+		MSM_CAMERA_I2C_BYTE_DATA);
+
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->global_gain_addr + 1,
+		gain_lsb,
+		MSM_CAMERA_I2C_BYTE_DATA);
+
+	s_ctrl->func_tbl->sensor_group_hold_off(s_ctrl);
+
+	return 0;
+}
+
+static const struct i2c_device_id ov5647_i2c_id[] = {
+	{SENSOR_NAME, (kernel_ulong_t)&ov5647_s_ctrl},
+	{ }
+};
+int32_t ov5647_sensor_i2c_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	int32_t rc = 0;
+	struct msm_sensor_ctrl_t *s_ctrl;
+
+	rc = msm_sensor_i2c_probe(client, id);
+
+	if (client->dev.platform_data == NULL) {
+		pr_err("%s: NULL sensor data\n", __func__);
+		return -EFAULT;
+	}
+
+	s_ctrl = client->dev.platform_data;
+	if (s_ctrl->sensordata->pmic_gpio_enable)
+		lcd_camera_power_onoff(0);
+
+	return rc;
+}
+
+static struct i2c_driver ov5647_i2c_driver = {
+	.id_table = ov5647_i2c_id,
+	.probe  = ov5647_sensor_i2c_probe,
+	.driver = {
+		.name = SENSOR_NAME,
+	},
+};
+
+
+
+static struct msm_camera_i2c_client ov5647_sensor_i2c_client = {
+	.addr_type = MSM_CAMERA_I2C_WORD_ADDR,
+};
+
+static int __init msm_sensor_init_module(void)
+{
+	return i2c_add_driver(&ov5647_i2c_driver);
+}
+
+static struct v4l2_subdev_core_ops ov5647_subdev_core_ops = {
+	.ioctl = msm_sensor_subdev_ioctl,
+	.s_power = msm_sensor_power,
+};
+
+static struct v4l2_subdev_video_ops ov5647_subdev_video_ops = {
+	.enum_mbus_fmt = msm_sensor_v4l2_enum_fmt,
+};
+
+static struct v4l2_subdev_ops ov5647_subdev_ops = {
+	.core = &ov5647_subdev_core_ops,
+	.video  = &ov5647_subdev_video_ops,
+};
+
+int32_t ov5647_sensor_power_down(struct msm_sensor_ctrl_t *s_ctrl)
+{
+	struct msm_camera_sensor_info *info = NULL;
+	unsigned short rdata;
+	int rc;
+
+	info = s_ctrl->sensordata;
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		0x4202, 0xf,
+		MSM_CAMERA_I2C_BYTE_DATA);
+	msleep(20);
+	rc = msm_camera_i2c_read(s_ctrl->sensor_i2c_client, 0x3018,
+			&rdata, MSM_CAMERA_I2C_WORD_DATA);
+	CDBG("ov5647_sensor_power_down: %d\n", rc);
+	rdata |= 0x18;
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		0x3018, rdata,
+		MSM_CAMERA_I2C_WORD_DATA);
+	msleep(20);
+	gpio_direction_output(info->sensor_pwd, 1);
+	usleep_range(5000, 5100);
+	msm_sensor_power_down(s_ctrl);
+	return 0;
+}
+
+int32_t ov5647_sensor_power_up(struct msm_sensor_ctrl_t *s_ctrl)
+{
+	int32_t rc = 0;
+	struct msm_camera_sensor_info *info = NULL;
+
+	info = s_ctrl->sensordata;
+	gpio_direction_output(info->sensor_pwd, 1);
+	gpio_direction_output(info->sensor_reset, 0);
+	usleep_range(10000, 11000);
+	if (info->pmic_gpio_enable) {
+		info->pmic_gpio_enable = 0;
+		lcd_camera_power_onoff(1);
+	}
+	usleep_range(10000, 11000);
+	rc = msm_sensor_power_up(s_ctrl);
+	if (rc < 0) {
+		CDBG("%s: msm_sensor_power_up failed\n", __func__);
+		return rc;
+	}
+
+	/* turn on ldo and vreg */
+
+	gpio_direction_output(info->sensor_pwd, 0);
+	msleep(20);
+	gpio_direction_output(info->sensor_reset, 1);
+	msleep(25);
+
+	return rc;
+
+}
+
+static int32_t vfe_clk = 266667000;
+
+int32_t ov5647_sensor_setting(struct msm_sensor_ctrl_t *s_ctrl,
+			int update_type, int res)
+{
+	int32_t rc = 0;
+	static int csi_config;
+	s_ctrl->func_tbl->sensor_stop_stream(s_ctrl);
+	if (csi_config == 0 || res == 0)
+		msleep(66);
+	else
+		msleep(266);
+
+	msm_camera_i2c_write(
+			s_ctrl->sensor_i2c_client,
+			0x4800, 0x25,
+			MSM_CAMERA_I2C_BYTE_DATA);
+	if (update_type == MSM_SENSOR_REG_INIT) {
+		CDBG("Register INIT\n");
+		s_ctrl->curr_csi_params = NULL;
+		msm_camera_i2c_write(
+				s_ctrl->sensor_i2c_client,
+				0x103, 0x1,
+				MSM_CAMERA_I2C_BYTE_DATA);
+		msm_sensor_enable_debugfs(s_ctrl);
+		msm_sensor_write_init_settings(s_ctrl);
+		csi_config = 0;
+	} else if (update_type == MSM_SENSOR_UPDATE_PERIODIC) {
+		CDBG("PERIODIC : %d\n", res);
+		msm_sensor_write_conf_array(
+			s_ctrl->sensor_i2c_client,
+			s_ctrl->msm_sensor_reg->mode_settings, res);
+		msleep(30);
+		if (!csi_config) {
+			s_ctrl->curr_csic_params = s_ctrl->csic_params[res];
+			CDBG("CSI config in progress\n");
+			v4l2_subdev_notify(&s_ctrl->sensor_v4l2_subdev,
+				NOTIFY_CSIC_CFG,
+				s_ctrl->curr_csic_params);
+			CDBG("CSI config is done\n");
+			mb();
+			msleep(30);
+			csi_config = 1;
+		msm_camera_i2c_write(
+			s_ctrl->sensor_i2c_client,
+			0x100, 0x1,
+			MSM_CAMERA_I2C_BYTE_DATA);
+		}
+		msm_camera_i2c_write(
+			s_ctrl->sensor_i2c_client,
+			0x4800, 0x4,
+			MSM_CAMERA_I2C_BYTE_DATA);
+		msleep(266);
+		if (res == MSM_SENSOR_RES_4)
+			v4l2_subdev_notify(&s_ctrl->sensor_v4l2_subdev,
+					NOTIFY_PCLK_CHANGE,
+					&vfe_clk);
+		s_ctrl->func_tbl->sensor_start_stream(s_ctrl);
+		msleep(50);
+	}
+	return rc;
+}
+static struct msm_sensor_fn_t ov5647_func_tbl = {
+	.sensor_start_stream = msm_sensor_start_stream,
+	.sensor_stop_stream = msm_sensor_stop_stream,
+	.sensor_group_hold_on = msm_sensor_group_hold_on,
+	.sensor_group_hold_off = msm_sensor_group_hold_off,
+	.sensor_set_fps = msm_sensor_set_fps,
+	.sensor_write_exp_gain = ov5647_write_prev_exp_gain,
+	.sensor_write_snapshot_exp_gain = ov5647_write_pict_exp_gain,
+	.sensor_csi_setting = ov5647_sensor_setting,
+	.sensor_set_sensor_mode = msm_sensor_set_sensor_mode,
+	.sensor_mode_init = msm_sensor_mode_init,
+	.sensor_get_output_info = msm_sensor_get_output_info,
+	.sensor_config = msm_sensor_config,
+	.sensor_power_up = ov5647_sensor_power_up,
+	.sensor_power_down = ov5647_sensor_power_down,
+};
+
+static struct msm_sensor_reg_t ov5647_regs = {
+	.default_data_type = MSM_CAMERA_I2C_BYTE_DATA,
+	.start_stream_conf = ov5647_start_settings,
+	.start_stream_conf_size = ARRAY_SIZE(ov5647_start_settings),
+	.stop_stream_conf = ov5647_stop_settings,
+	.stop_stream_conf_size = ARRAY_SIZE(ov5647_stop_settings),
+	.group_hold_on_conf = ov5647_groupon_settings,
+	.group_hold_on_conf_size = ARRAY_SIZE(ov5647_groupon_settings),
+	.group_hold_off_conf = ov5647_groupoff_settings,
+	.group_hold_off_conf_size =
+		ARRAY_SIZE(ov5647_groupoff_settings),
+	.init_settings = &ov5647_init_conf[0],
+	.init_size = ARRAY_SIZE(ov5647_init_conf),
+	.mode_settings = &ov5647_confs[0],
+	.output_settings = &ov5647_dimensions[0],
+	.num_conf = ARRAY_SIZE(ov5647_confs),
+};
+
+static struct msm_sensor_ctrl_t ov5647_s_ctrl = {
+	.msm_sensor_reg = &ov5647_regs,
+	.sensor_i2c_client = &ov5647_sensor_i2c_client,
+	.sensor_i2c_addr =  0x36 << 1 ,
+	.sensor_output_reg_addr = &ov5647_reg_addr,
+	.sensor_id_info = &ov5647_id_info,
+	.sensor_exp_gain_info = &ov5647_exp_gain_info,
+	.cam_mode = MSM_SENSOR_MODE_INVALID,
+	.csic_params = &ov5647_csi_params_array[0],
+	.msm_sensor_mutex = &ov5647_mut,
+	.sensor_i2c_driver = &ov5647_i2c_driver,
+	.sensor_v4l2_subdev_info = ov5647_subdev_info,
+	.sensor_v4l2_subdev_info_size = ARRAY_SIZE(ov5647_subdev_info),
+	.sensor_v4l2_subdev_ops = &ov5647_subdev_ops,
+	.func_tbl = &ov5647_func_tbl,
+	.clk_rate = MSM_SENSOR_MCLK_24HZ,
+};
+
+module_init(msm_sensor_init_module);
+MODULE_DESCRIPTION("Omnivision WXGA Bayer sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/sensors/ov7692_v4l2.c b/drivers/media/video/msm/sensors/ov7692_v4l2.c
new file mode 100644
index 0000000..e7970d5
--- /dev/null
+++ b/drivers/media/video/msm/sensors/ov7692_v4l2.c
@@ -0,0 +1,332 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_sensor.h"
+#define SENSOR_NAME "ov7692"
+
+DEFINE_MUTEX(ov7692_mut);
+static struct msm_sensor_ctrl_t ov7692_s_ctrl;
+
+static struct msm_camera_i2c_reg_conf ov7692_start_settings[] = {
+	{0x0e, 0x00},
+};
+
+static struct msm_camera_i2c_reg_conf ov7692_stop_settings[] = {
+	{0x0e, 0x08},
+};
+
+static struct msm_camera_i2c_reg_conf ov7692_recommend_settings[] = {
+	{0x12, 0x80},
+	{0x0e, 0x08},
+	{0x69, 0x52},
+	{0x1e, 0xb3},
+	{0x48, 0x42},
+	{0xff, 0x01},
+	{0xae, 0xa0},
+	{0xa8, 0x26},
+	{0xb4, 0xc0},
+	{0xb5, 0x40},
+	{0xff, 0x00},
+	{0x0c, 0x00},
+	{0x62, 0x10},
+	{0x12, 0x00},
+	{0x17, 0x65},
+	{0x18, 0xa4},
+	{0x19, 0x0a},
+	{0x1a, 0xf6},
+	{0x3e, 0x30},
+	{0x64, 0x0a},
+	{0xff, 0x01},
+	{0xb4, 0xc0},
+	{0xff, 0x00},
+	{0x67, 0x20},
+	{0x81, 0x3f},
+	{0xd0, 0x48},
+	{0x82, 0x03},
+	{0x70, 0x00},
+	{0x71, 0x34},
+	{0x74, 0x28},
+	{0x75, 0x98},
+	{0x76, 0x00},
+	{0x77, 0x64},
+	{0x78, 0x01},
+	{0x79, 0xc2},
+	{0x7a, 0x4e},
+	{0x7b, 0x1f},
+	{0x7c, 0x00},
+	{0x11, 0x00},
+	{0x20, 0x00},
+	{0x21, 0x23},
+	{0x50, 0x9a},
+	{0x51, 0x80},
+	{0x4c, 0x7d},
+	{0x85, 0x10},
+	{0x86, 0x00},
+	{0x87, 0x00},
+	{0x88, 0x00},
+	{0x89, 0x2a},
+	{0x8a, 0x26},
+	{0x8b, 0x22},
+	{0xbb, 0x7a},
+	{0xbc, 0x69},
+	{0xbd, 0x11},
+	{0xbe, 0x13},
+	{0xbf, 0x81},
+	{0xc0, 0x96},
+	{0xc1, 0x1e},
+	{0xb7, 0x05},
+	{0xb8, 0x09},
+	{0xb9, 0x00},
+	{0xba, 0x18},
+	{0x5a, 0x1f},
+	{0x5b, 0x9f},
+	{0x5c, 0x6a},
+	{0x5d, 0x42},
+	{0x24, 0x78},
+	{0x25, 0x68},
+	{0x26, 0xb3},
+	{0xa3, 0x0b},
+	{0xa4, 0x15},
+	{0xa5, 0x2a},
+	{0xa6, 0x51},
+	{0xa7, 0x63},
+	{0xa8, 0x74},
+	{0xa9, 0x83},
+	{0xaa, 0x91},
+	{0xab, 0x9e},
+	{0xac, 0xaa},
+	{0xad, 0xbe},
+	{0xae, 0xce},
+	{0xaf, 0xe5},
+	{0xb0, 0xf3},
+	{0xb1, 0xfb},
+	{0xb2, 0x06},
+	{0x8c, 0x5c},
+	{0x8d, 0x11},
+	{0x8e, 0x12},
+	{0x8f, 0x19},
+	{0x90, 0x50},
+	{0x91, 0x20},
+	{0x92, 0x96},
+	{0x93, 0x80},
+	{0x94, 0x13},
+	{0x95, 0x1b},
+	{0x96, 0xff},
+	{0x97, 0x00},
+	{0x98, 0x3d},
+	{0x99, 0x36},
+	{0x9a, 0x51},
+	{0x9b, 0x43},
+	{0x9c, 0xf0},
+	{0x9d, 0xf0},
+	{0x9e, 0xf0},
+	{0x9f, 0xff},
+	{0xa0, 0x68},
+	{0xa1, 0x62},
+	{0xa2, 0x0e},
+};
+
+static struct msm_camera_i2c_reg_conf ov7692_full_settings[] = {
+	{0xcc, 0x02},
+	{0xcd, 0x80},
+	{0xce, 0x01},
+	{0xcf, 0xe0},
+	{0xc8, 0x02},
+	{0xc9, 0x80},
+	{0xca, 0x01},
+	{0xcb, 0xe0},
+};
+
+static struct v4l2_subdev_info ov7692_subdev_info[] = {
+	{
+		.code   = V4L2_MBUS_FMT_YUYV8_2X8,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.fmt    = 1,
+		.order    = 0,
+	},
+	/* more can be supported, to be added later */
+};
+
+
+static struct msm_camera_i2c_conf_array ov7692_init_conf[] = {
+	{&ov7692_recommend_settings[0],
+	ARRAY_SIZE(ov7692_recommend_settings), 0, MSM_CAMERA_I2C_BYTE_DATA}
+};
+
+static struct msm_camera_i2c_conf_array ov7692_confs[] = {
+	{&ov7692_full_settings[0],
+	ARRAY_SIZE(ov7692_full_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+};
+
+static struct msm_sensor_output_info_t ov7692_dimensions[] = {
+	{
+		.x_output = 0x280,
+		.y_output = 0x1E0,
+		.line_length_pclk = 0x290,
+		.frame_length_lines = 0x1EC,
+		.vt_pixel_clk = 9216000,
+		.op_pixel_clk = 9216000,
+		.binning_factor = 1,
+	},
+};
+
+
+static struct msm_camera_csi_params ov7692_csi_params = {
+	.data_format = CSI_8BIT,
+	.lane_cnt    = 1,
+	.lane_assign = 0xe4,
+	.dpcm_scheme = 0,
+	.settle_cnt  = 0x14,
+};
+
+static struct msm_camera_csi_params *ov7692_csi_params_array[] = {
+	&ov7692_csi_params,
+};
+
+static struct msm_sensor_output_reg_addr_t ov7692_reg_addr = {
+	.x_output = 0xCC,
+	.y_output = 0xCE,
+	.line_length_pclk = 0xC8,
+	.frame_length_lines = 0xCA,
+};
+
+static struct msm_sensor_id_info_t ov7692_id_info = {
+	.sensor_id_reg_addr = 0x0A,
+	.sensor_id = 0x7692,
+};
+
+static const struct i2c_device_id ov7692_i2c_id[] = {
+	{SENSOR_NAME, (kernel_ulong_t)&ov7692_s_ctrl},
+	{ }
+};
+
+
+static struct i2c_driver ov7692_i2c_driver = {
+	.id_table = ov7692_i2c_id,
+	.probe  = msm_sensor_i2c_probe,
+	.driver = {
+		.name = SENSOR_NAME,
+	},
+};
+
+static struct msm_camera_i2c_client ov7692_sensor_i2c_client = {
+	.addr_type = MSM_CAMERA_I2C_BYTE_ADDR,
+};
+
+static int __init msm_sensor_init_module(void)
+{
+	int rc = 0;
+	CDBG("OV7692\n");
+
+	rc = i2c_add_driver(&ov7692_i2c_driver);
+
+	return rc;
+}
+
+static struct v4l2_subdev_core_ops ov7692_subdev_core_ops = {
+	.ioctl = msm_sensor_subdev_ioctl,
+	.s_power = msm_sensor_power,
+};
+
+static struct v4l2_subdev_video_ops ov7692_subdev_video_ops = {
+	.enum_mbus_fmt = msm_sensor_v4l2_enum_fmt,
+};
+
+static struct v4l2_subdev_ops ov7692_subdev_ops = {
+	.core = &ov7692_subdev_core_ops,
+	.video  = &ov7692_subdev_video_ops,
+};
+
+int32_t ov7692_sensor_power_up(struct msm_sensor_ctrl_t *s_ctrl)
+{
+	int32_t rc = 0;
+	struct msm_camera_sensor_info *info = NULL;
+
+	info = s_ctrl->sensordata;
+	if (info->pmic_gpio_enable) {
+		info->sensor_lcd_gpio_onoff(1);
+		usleep_range(5000, 5100);
+	}
+
+	rc = msm_sensor_power_up(s_ctrl);
+	if (rc < 0) {
+		CDBG("%s: msm_sensor_power_up failed\n", __func__);
+		return rc;
+	}
+
+	return rc;
+}
+
+int32_t ov7692_sensor_power_down(struct msm_sensor_ctrl_t *s_ctrl)
+{
+	int32_t rc = 0;
+	struct msm_camera_sensor_info *info = NULL;
+
+	rc = msm_sensor_power_down(s_ctrl);
+	if (rc < 0)
+		CDBG("%s: msm_sensor_power_down failed\n", __func__);
+
+	info = s_ctrl->sensordata;
+	if (info->pmic_gpio_enable) {
+		info->pmic_gpio_enable = 0;
+		info->sensor_lcd_gpio_onoff(0);
+		usleep_range(5000, 5100);
+	}
+	return rc;
+}
+
+static struct msm_sensor_fn_t ov7692_func_tbl = {
+	.sensor_start_stream = msm_sensor_start_stream,
+	.sensor_stop_stream = msm_sensor_stop_stream,
+	.sensor_csi_setting = msm_sensor_setting1,
+	.sensor_set_sensor_mode = msm_sensor_set_sensor_mode,
+	.sensor_mode_init = msm_sensor_mode_init,
+	.sensor_get_output_info = msm_sensor_get_output_info,
+	.sensor_config = msm_sensor_config,
+	.sensor_power_up = ov7692_sensor_power_up,
+	.sensor_power_down = ov7692_sensor_power_down,
+};
+
+static struct msm_sensor_reg_t ov7692_regs = {
+	.default_data_type = MSM_CAMERA_I2C_BYTE_DATA,
+	.start_stream_conf = ov7692_start_settings,
+	.start_stream_conf_size = ARRAY_SIZE(ov7692_start_settings),
+	.stop_stream_conf = ov7692_stop_settings,
+	.stop_stream_conf_size = ARRAY_SIZE(ov7692_stop_settings),
+	.init_settings = &ov7692_init_conf[0],
+	.init_size = ARRAY_SIZE(ov7692_init_conf),
+	.mode_settings = &ov7692_confs[0],
+	.output_settings = &ov7692_dimensions[0],
+	.num_conf = ARRAY_SIZE(ov7692_confs),
+};
+
+static struct msm_sensor_ctrl_t ov7692_s_ctrl = {
+	.msm_sensor_reg = &ov7692_regs,
+	.sensor_i2c_client = &ov7692_sensor_i2c_client,
+	.sensor_i2c_addr = 0x78,
+	.sensor_output_reg_addr = &ov7692_reg_addr,
+	.sensor_id_info = &ov7692_id_info,
+	.cam_mode = MSM_SENSOR_MODE_INVALID,
+	.csic_params = &ov7692_csi_params_array[0],
+	.msm_sensor_mutex = &ov7692_mut,
+	.sensor_i2c_driver = &ov7692_i2c_driver,
+	.sensor_v4l2_subdev_info = ov7692_subdev_info,
+	.sensor_v4l2_subdev_info_size = ARRAY_SIZE(ov7692_subdev_info),
+	.sensor_v4l2_subdev_ops = &ov7692_subdev_ops,
+	.func_tbl = &ov7692_func_tbl,
+	.clk_rate = MSM_SENSOR_MCLK_24HZ,
+};
+
+module_init(msm_sensor_init_module);
+MODULE_DESCRIPTION("Omnivision VGA YUV sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/sensors/ov9726_v4l2.c b/drivers/media/video/msm/sensors/ov9726_v4l2.c
new file mode 100644
index 0000000..61c693e
--- /dev/null
+++ b/drivers/media/video/msm/sensors/ov9726_v4l2.c
@@ -0,0 +1,281 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_sensor.h"
+#define SENSOR_NAME "ov9726"
+#define PLATFORM_DRIVER_NAME "msm_camera_ov9726"
+#define ov9726_obj ov9726_##obj
+
+DEFINE_MUTEX(ov9726_mut);
+static struct msm_sensor_ctrl_t ov9726_s_ctrl;
+
+static struct msm_camera_i2c_reg_conf ov9726_start_settings[] = {
+	{0x0100, 0x01},
+};
+
+static struct msm_camera_i2c_reg_conf ov9726_stop_settings[] = {
+	{0x0100, 0x00},
+};
+
+static struct msm_camera_i2c_reg_conf ov9726_groupon_settings[] = {
+	{0x0104, 0x01},
+};
+
+static struct msm_camera_i2c_reg_conf ov9726_groupoff_settings[] = {
+	{0x0104, 0x00},
+};
+
+static struct msm_camera_i2c_reg_conf ov9726_prev_settings[] = {
+};
+
+static struct msm_camera_i2c_reg_conf ov9726_recommend_settings[] = {
+	{0x0103, 0x01}, /* SOFTWARE_RESET */
+	{0x3026, 0x00}, /* OUTPUT_SELECT01 */
+	{0x3027, 0x00}, /* OUTPUT_SELECT02 */
+	{0x3002, 0xe8}, /* IO_CTRL00 */
+	{0x3004, 0x03}, /* IO_CTRL01 */
+	{0x3005, 0xff}, /* IO_CTRL02 */
+	{0x3703, 0x42},
+	{0x3704, 0x10},
+	{0x3705, 0x45},
+	{0x3603, 0xaa},
+	{0x3632, 0x2f},
+	{0x3620, 0x66},
+	{0x3621, 0xc0},
+	{0x0340, 0x03}, /* FRAME_LENGTH_LINES_HI */
+	{0x0341, 0xC1}, /* FRAME_LENGTH_LINES_LO */
+	{0x0342, 0x06}, /* LINE_LENGTH_PCK_HI */
+	{0x0343, 0x80}, /* LINE_LENGTH_PCK_LO */
+	{0x0202, 0x03}, /* COARSE_INTEGRATION_TIME_HI */
+	{0x0203, 0x43}, /* COARSE_INTEGRATION_TIME_LO */
+	{0x3833, 0x04},
+	{0x3835, 0x02},
+	{0x4702, 0x04},
+	{0x4704, 0x00}, /* DVP_CTRL01 */
+	{0x4706, 0x08},
+	{0x5052, 0x01},
+	{0x3819, 0x6e},
+	{0x3817, 0x94},
+	{0x3a18, 0x00}, /* AEC_GAIN_CEILING_HI */
+	{0x3a19, 0x7f}, /* AEC_GAIN_CEILING_LO */
+	{0x404e, 0x7e},
+	{0x3631, 0x52},
+	{0x3633, 0x50},
+	{0x3630, 0xd2},
+	{0x3604, 0x08},
+	{0x3601, 0x40},
+	{0x3602, 0x14},
+	{0x3610, 0xa0},
+	{0x3612, 0x20},
+	{0x034c, 0x05}, /* X_OUTPUT_SIZE_HI */
+	{0x034d, 0x10}, /* X_OUTPUT_SIZE_LO */
+	{0x034e, 0x03}, /* Y_OUTPUT_SIZE_HI */
+	{0x034f, 0x28}, /* Y_OUTPUT_SIZE_LO */
+	{0x0340, 0x03}, /* FRAME_LENGTH_LINES_HI */
+	{0x0341, 0xC1}, /* FRAME_LENGTH_LINES_LO */
+	{0x0342, 0x06}, /* LINE_LENGTH_PCK_HI */
+	{0x0343, 0x80}, /* LINE_LENGTH_PCK_LO */
+	{0x0202, 0x03}, /* COARSE_INTEGRATION_TIME_HI */
+	{0x0203, 0x43}, /* COARSE_INTEGRATION_TIME_LO */
+	{0x0303, 0x01}, /* VT_SYS_CLK_DIV_LO */
+	{0x3002, 0x00}, /* IO_CTRL00 */
+	{0x3004, 0x00}, /* IO_CTRL01 */
+	{0x3005, 0x00}, /* IO_CTRL02 */
+	{0x4801, 0x0f}, /* MIPI_CTRL01 */
+	{0x4803, 0x05}, /* MIPI_CTRL03 */
+	{0x4601, 0x16}, /* VFIFO_READ_CONTROL */
+	{0x3014, 0x05}, /* SC_CMMN_MIPI / SC_CTRL00 */
+	{0x3104, 0x80},
+	{0x0305, 0x04}, /* PRE_PLL_CLK_DIV_LO */
+	{0x0307, 0x64}, /* PLL_MULTIPLIER_LO */
+	{0x300c, 0x02},
+	{0x300d, 0x20},
+	{0x300e, 0x01},
+	{0x3010, 0x01},
+	{0x460e, 0x81}, /* VFIFO_CONTROL00 */
+	{0x0101, 0x01}, /* IMAGE_ORIENTATION */
+	{0x3707, 0x14},
+	{0x3622, 0x9f},
+	{0x5047, 0x3D}, /* ISP_CTRL47 */
+	{0x4002, 0x45}, /* BLC_CTRL02 */
+	{0x5000, 0x06}, /* ISP_CTRL0 */
+	{0x5001, 0x00}, /* ISP_CTRL1 */
+	{0x3406, 0x00}, /* AWB_MANUAL_CTRL */
+	{0x3503, 0x13}, /* AEC_ENABLE */
+	{0x4005, 0x18}, /* BLC_CTRL05 */
+	{0x4837, 0x21},
+	{0x0100, 0x01}, /* MODE_SELECT */
+	{0x3a0f, 0x64}, /* AEC_CTRL0F */
+	{0x3a10, 0x54}, /* AEC_CTRL10 */
+	{0x3a11, 0xc2}, /* AEC_CTRL11 */
+	{0x3a1b, 0x64}, /* AEC_CTRL1B */
+	{0x3a1e, 0x54}, /* AEC_CTRL1E */
+	{0x3a1a, 0x05}, /* AEC_DIFF_MAX */
+};
+
+static struct v4l2_subdev_info ov9726_subdev_info[] = {
+	{
+	.code   = V4L2_MBUS_FMT_SBGGR10_1X10,
+	.colorspace = V4L2_COLORSPACE_JPEG,
+	.fmt    = 1,
+	.order    = 0,
+	},
+	/* more can be supported, to be added later */
+};
+
+static struct msm_camera_i2c_conf_array ov9726_init_conf[] = {
+	{&ov9726_recommend_settings[0],
+	ARRAY_SIZE(ov9726_recommend_settings), 0, MSM_CAMERA_I2C_BYTE_DATA}
+};
+
+static struct msm_camera_i2c_conf_array ov9726_confs[] = {
+	{&ov9726_prev_settings[0],
+	ARRAY_SIZE(ov9726_prev_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+};
+
+static struct msm_sensor_output_info_t ov9726_dimensions[] = {
+	{
+		.x_output = 0x510, /* 1296 */
+		.y_output = 0x328, /* 808 */
+		.line_length_pclk = 0x680, /* 1664 */
+		.frame_length_lines = 0x3C1, /* 961 */
+		.vt_pixel_clk = 320000000,
+		.op_pixel_clk = 320000000,
+		.binning_factor = 1,
+	},
+};
+
+static struct msm_camera_csi_params ov9726_csi_params = {
+	       .data_format = CSI_10BIT,
+	       .lane_cnt    = 1,
+	       .lane_assign = 0xe4,
+	       .dpcm_scheme = 0,
+	       .settle_cnt  = 7,
+};
+
+static struct msm_camera_csi_params *ov9726_csi_params_array[] = {
+	&ov9726_csi_params,
+};
+
+static struct msm_sensor_output_reg_addr_t ov9726_reg_addr = {
+	.x_output = 0x034c,
+	.y_output = 0x034e,
+	.line_length_pclk = 0x0342,
+	.frame_length_lines = 0x0340,
+};
+
+static struct msm_sensor_id_info_t ov9726_id_info = {
+	.sensor_id_reg_addr = 0x0000,
+	.sensor_id = 0x9726,
+};
+
+static struct msm_sensor_exp_gain_info_t ov9726_exp_gain_info = {
+	.coarse_int_time_addr = 0x0202,
+	.global_gain_addr = 0x0204,
+	.vert_offset = 6,
+};
+
+static const struct i2c_device_id ov9726_i2c_id[] = {
+	{SENSOR_NAME, (kernel_ulong_t)&ov9726_s_ctrl},
+	{ }
+};
+
+static struct i2c_driver ov9726_i2c_driver = {
+	.id_table = ov9726_i2c_id,
+	.probe  = msm_sensor_i2c_probe,
+	.driver = {
+		.name = SENSOR_NAME,
+	},
+};
+
+static struct msm_camera_i2c_client ov9726_sensor_i2c_client = {
+	.addr_type = MSM_CAMERA_I2C_WORD_ADDR,
+};
+
+static int __init msm_sensor_init_module(void)
+{
+	return i2c_add_driver(&ov9726_i2c_driver);
+}
+
+static struct v4l2_subdev_core_ops ov9726_subdev_core_ops = {
+	.ioctl = msm_sensor_subdev_ioctl,
+	.s_power = msm_sensor_power,
+};
+
+static struct v4l2_subdev_video_ops ov9726_subdev_video_ops = {
+	.enum_mbus_fmt = msm_sensor_v4l2_enum_fmt,
+};
+
+static struct v4l2_subdev_ops ov9726_subdev_ops = {
+	.core = &ov9726_subdev_core_ops,
+	.video  = &ov9726_subdev_video_ops,
+};
+
+static struct msm_sensor_fn_t ov9726_func_tbl = {
+	.sensor_start_stream = msm_sensor_start_stream,
+	.sensor_stop_stream = msm_sensor_stop_stream,
+	.sensor_group_hold_on = msm_sensor_group_hold_on,
+	.sensor_group_hold_off = msm_sensor_group_hold_off,
+	.sensor_set_fps = msm_sensor_set_fps,
+	.sensor_write_exp_gain = msm_sensor_write_exp_gain1,
+	.sensor_write_snapshot_exp_gain = msm_sensor_write_exp_gain1,
+	.sensor_csi_setting = msm_sensor_setting1,
+	.sensor_set_sensor_mode = msm_sensor_set_sensor_mode,
+	.sensor_mode_init = msm_sensor_mode_init,
+	.sensor_get_output_info = msm_sensor_get_output_info,
+	.sensor_config = msm_sensor_config,
+	.sensor_power_up = msm_sensor_power_up,
+	.sensor_power_down = msm_sensor_power_down,
+};
+
+static struct msm_sensor_reg_t ov9726_regs = {
+	.default_data_type = MSM_CAMERA_I2C_BYTE_DATA,
+	.start_stream_conf = ov9726_start_settings,
+	.start_stream_conf_size = ARRAY_SIZE(ov9726_start_settings),
+	.stop_stream_conf = ov9726_stop_settings,
+	.stop_stream_conf_size = ARRAY_SIZE(ov9726_stop_settings),
+	.group_hold_on_conf = ov9726_groupon_settings,
+	.group_hold_on_conf_size = ARRAY_SIZE(ov9726_groupon_settings),
+	.group_hold_off_conf = ov9726_groupoff_settings,
+	.group_hold_off_conf_size =
+		ARRAY_SIZE(ov9726_groupoff_settings),
+	.init_settings = &ov9726_init_conf[0],
+	.init_size = ARRAY_SIZE(ov9726_init_conf),
+	.mode_settings = &ov9726_confs[0],
+	.output_settings = &ov9726_dimensions[0],
+	.num_conf = ARRAY_SIZE(ov9726_confs),
+};
+
+static struct msm_sensor_ctrl_t ov9726_s_ctrl = {
+	.msm_sensor_reg = &ov9726_regs,
+	.sensor_i2c_client = &ov9726_sensor_i2c_client,
+	.sensor_i2c_addr = 0x20,
+	.sensor_output_reg_addr = &ov9726_reg_addr,
+	.sensor_id_info = &ov9726_id_info,
+	.sensor_exp_gain_info = &ov9726_exp_gain_info,
+	.cam_mode = MSM_SENSOR_MODE_INVALID,
+	.csic_params = &ov9726_csi_params_array[0],
+	.msm_sensor_mutex = &ov9726_mut,
+	.sensor_i2c_driver = &ov9726_i2c_driver,
+	.sensor_v4l2_subdev_info = ov9726_subdev_info,
+	.sensor_v4l2_subdev_info_size = ARRAY_SIZE(ov9726_subdev_info),
+	.sensor_v4l2_subdev_ops = &ov9726_subdev_ops,
+	.func_tbl = &ov9726_func_tbl,
+	.clk_rate = MSM_SENSOR_MCLK_24HZ,
+};
+
+module_init(msm_sensor_init_module);
+MODULE_DESCRIPTION("Omnivision WXGA Bayer sensor driver");
+MODULE_LICENSE("GPL v2");
+
+
diff --git a/drivers/media/video/msm/sensors/s5k3l1yx.c b/drivers/media/video/msm/sensors/s5k3l1yx.c
new file mode 100644
index 0000000..debda88
--- /dev/null
+++ b/drivers/media/video/msm/sensors/s5k3l1yx.c
@@ -0,0 +1,696 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_sensor.h"
+#define SENSOR_NAME "s5k3l1yx"
+#define PLATFORM_DRIVER_NAME "msm_camera_s5k3l1yx"
+
+DEFINE_MUTEX(s5k3l1yx_mut);
+static struct msm_sensor_ctrl_t s5k3l1yx_s_ctrl;
+
+static struct msm_camera_i2c_reg_conf s5k3l1yx_start_settings[] = {
+	{0x0100, 0x01},
+};
+
+static struct msm_camera_i2c_reg_conf s5k3l1yx_stop_settings[] = {
+	{0x0100, 0x00},
+};
+
+static struct msm_camera_i2c_reg_conf s5k3l1yx_groupon_settings[] = {
+	{0x104, 0x01},
+};
+
+static struct msm_camera_i2c_reg_conf s5k3l1yx_groupoff_settings[] = {
+	{0x104, 0x00},
+};
+
+static struct msm_camera_i2c_reg_conf s5k3l1yx_snap_settings[] = {
+	{0x0501, 0x00}, /* compression_algorithim_L(1d) */
+	{0x0112, 0x0A}, /* CCP_data_format_H */
+	{0x0113, 0x0A}, /* CCP_data_format_L raw8=0808 ,DCPM10 -->8= 0A08 */
+	{0x0306, 0x00}, /* pll_multiplier */
+	{0x0307, 0xA5}, /* pll_multiplier */
+	{0x0202, 0x09}, /* coarse_integration_time */
+	{0x0203, 0x32}, /* coarse_integration_time */
+	{0x0340, 0x0B}, /* frame_length_lines */
+	{0x0341, 0xEC}, /* frame_length_lines */
+	{0x0342, 0x14}, /* line_length_pck */
+	{0x0343, 0xD8}, /* line_length_pck */
+	{0x0344, 0x00}, /* x_addr_start */
+	{0x0345, 0x08}, /* x_addr_start */
+	{0x0346, 0x00}, /* y_addr_start */
+	{0x0347, 0x00}, /* y_addr_start */
+	{0x0348, 0x0F}, /* x_addr_end */
+	{0x0349, 0xA7}, /* x_addr_end */
+	{0x034A, 0x0B}, /* y_addr_end */
+	{0x034B, 0xC7}, /* y_addr_end */
+	{0x034C, 0x0F}, /* x_output_size */
+	{0x034D, 0xA0}, /* x_output_size */
+	{0x034E, 0x0B}, /* y_output_size */
+	{0x034F, 0xC8}, /* y_output_size */
+	{0x0380, 0x00}, /* x_even_inc */
+	{0x0381, 0x01}, /* x_even_inc */
+	{0x0382, 0x00}, /* x_odd_inc */
+	{0x0383, 0x01}, /* x_odd_inc */
+	{0x0384, 0x00}, /* y_even_inc */
+	{0x0385, 0x01}, /* y_even_inc */
+	{0x0386, 0x00}, /* y_odd_inc */
+	{0x0387, 0x01}, /* y_odd_inc */
+	{0x0900, 0x00}, /* binning_mode */
+	{0x0901, 0x22}, /* binning_type */
+	{0x0902, 0x01}, /* binning_weighting */
+};
+
+static struct msm_camera_i2c_reg_conf s5k3l1yx_prev_settings[] = {
+	{0x0501, 0x00}, /* compression_algorithim_L(1d) */
+	{0x0112, 0x0A}, /* CCP_data_format_H */
+	{0x0113, 0x0A}, /* CCP_data_format_L raw8=0808 ,DCPM10 -->8= 0A08 */
+	{0x0306, 0x00}, /* pll_multiplier */
+	{0x0307, 0xA5}, /* pll_multiplier */
+	{0x0202, 0x06}, /* coarse_integration_time */
+	{0x0203, 0x00}, /* coarse_integration_time */
+	{0x0340, 0x09}, /* frame_length_lines */
+	{0x0341, 0x98}, /* frame_length_lines */
+	{0x0342, 0x11}, /* line_length_pck */
+	{0x0343, 0x80}, /* line_length_pck */
+	{0x0344, 0x00}, /* x_addr_start */
+	{0x0345, 0x18}, /* x_addr_start */
+	{0x0346, 0x00}, /* y_addr_start */
+	{0x0347, 0x00}, /* y_addr_start */
+	{0x0348, 0x0F}, /* x_addr_end */
+	{0x0349, 0x97}, /* x_addr_end */
+	{0x034A, 0x0B}, /* y_addr_end */
+	{0x034B, 0xC7}, /* y_addr_end */
+	{0x034C, 0x07}, /* x_output_size */
+	{0x034D, 0xC0}, /* x_output_size */
+	{0x034E, 0x05}, /* y_output_size */
+	{0x034F, 0xE4}, /* y_output_size */
+	{0x0380, 0x00}, /* x_even_inc */
+	{0x0381, 0x01}, /* x_even_inc */
+	{0x0382, 0x00}, /* x_odd_inc */
+	{0x0383, 0x03}, /* x_odd_inc */
+	{0x0384, 0x00}, /* y_even_inc */
+	{0x0385, 0x01}, /* y_even_inc */
+	{0x0386, 0x00}, /* y_odd_inc */
+	{0x0387, 0x03}, /* y_odd_inc */
+	{0x0900, 0x01}, /* binning_mode */
+	{0x0901, 0x22}, /* binning_type */
+	{0x0902, 0x01}, /* binning_weighting */
+};
+
+static struct msm_camera_i2c_reg_conf s5k3l1yx_video_60fps_settings[] = {
+	{0x0501, 0x00}, /* compression_algorithim_L(1d) */
+	{0x0112, 0x0A}, /* CCP_data_format_H */
+	{0x0113, 0x0A}, /* CCP_data_format_L raw8=0808 ,DCPM10 -->8= 0A08 */
+	{0x0306, 0x00}, /* pll_multiplier */
+	{0x0307, 0xA5}, /* pll_multiplier */
+	{0x0202, 0x03}, /* coarse_integration_time */
+	{0x0203, 0xD8}, /* coarse_integration_time */
+	{0x0340, 0x03}, /* frame_length_lines */
+	{0x0341, 0xE0}, /* frame_length_lines */
+	{0x0342, 0x14}, /* line_length_pck */
+	{0x0343, 0xD8}, /* line_length_pck */
+	{0x0344, 0x01}, /* x_addr_start */
+	{0x0345, 0x20}, /* x_addr_start */
+	{0x0346, 0x02}, /* y_addr_start */
+	{0x0347, 0x24}, /* y_addr_start */
+	{0x0348, 0x0E}, /* x_addr_end */
+	{0x0349, 0xA0}, /* x_addr_end */
+	{0x034A, 0x09}, /* y_addr_end */
+	{0x034B, 0xA4}, /* y_addr_end */
+	{0x034C, 0x03}, /* x_output_size */
+	{0x034D, 0x60}, /* x_output_size */
+	{0x034E, 0x01}, /* y_output_size */
+	{0x034F, 0xE0}, /* y_output_size */
+	{0x0380, 0x00}, /* x_even_inc */
+	{0x0381, 0x01}, /* x_even_inc */
+	{0x0382, 0x00}, /* x_odd_inc */
+	{0x0383, 0x07}, /* x_odd_inc */
+	{0x0384, 0x00}, /* y_even_inc */
+	{0x0385, 0x01}, /* y_even_inc */
+	{0x0386, 0x00}, /* y_odd_inc */
+	{0x0387, 0x07}, /* y_odd_inc */
+	{0x0900, 0x01}, /* binning_mode */
+	{0x0901, 0x44}, /* binning_type */
+	{0x0902, 0x01}, /* binning_weighting */
+};
+
+static struct msm_camera_i2c_reg_conf s5k3l1yx_video_90fps_settings[] = {
+	{0x0501, 0x00}, /* compression_algorithim_L(1d) */
+	{0x0112, 0x0A}, /* CCP_data_format_H */
+	{0x0113, 0x0A}, /* CCP_data_format_L raw8=0808 ,DCPM10 -->8= 0A08 */
+	{0x0306, 0x00}, /* pll_multiplier */
+	{0x0307, 0xA5}, /* pll_multiplier */
+	{0x0202, 0x02}, /* coarse_integration_time */
+	{0x0203, 0x90}, /* coarse_integration_time */
+	{0x0340, 0x02}, /* frame_length_lines */
+	{0x0341, 0x98}, /* frame_length_lines */
+	{0x0342, 0x14}, /* line_length_pck */
+	{0x0343, 0xD8}, /* line_length_pck */
+	{0x0344, 0x01}, /* x_addr_start */
+	{0x0345, 0x20}, /* x_addr_start */
+	{0x0346, 0x02}, /* y_addr_start */
+	{0x0347, 0x24}, /* y_addr_start */
+	{0x0348, 0x0E}, /* x_addr_end */
+	{0x0349, 0xA0}, /* x_addr_end */
+	{0x034A, 0x09}, /* y_addr_end */
+	{0x034B, 0xA4}, /* y_addr_end */
+	{0x034C, 0x03}, /* x_output_size */
+	{0x034D, 0x60}, /* x_output_size */
+	{0x034E, 0x01}, /* y_output_size */
+	{0x034F, 0xE0}, /* y_output_size */
+	{0x0380, 0x00}, /* x_even_inc */
+	{0x0381, 0x01}, /* x_even_inc */
+	{0x0382, 0x00}, /* x_odd_inc */
+	{0x0383, 0x07}, /* x_odd_inc */
+	{0x0384, 0x00}, /* y_even_inc */
+	{0x0385, 0x01}, /* y_even_inc */
+	{0x0386, 0x00}, /* y_odd_inc */
+	{0x0387, 0x07}, /* y_odd_inc */
+	{0x0900, 0x01}, /* binning_mode */
+	{0x0901, 0x44}, /* binning_type */
+	{0x0902, 0x01}, /* binning_weighting */
+};
+
+static struct msm_camera_i2c_reg_conf s5k3l1yx_video_120fps_settings[] = {
+	{0x0501, 0x00}, /* compression_algorithim_L(1d) */
+	{0x0112, 0x0A}, /* CCP_data_format_H */
+	{0x0113, 0x0A}, /* CCP_data_format_L raw8=0808 ,DCPM10 -->8= 0A08 */
+	{0x0306, 0x00}, /* pll_multiplier */
+	{0x0307, 0xA5}, /* pll_multiplier */
+	{0x0202, 0x01}, /* coarse_integration_time */
+	{0x0203, 0xFA}, /* coarse_integration_time */
+	{0x0340, 0x02}, /* frame_length_lines */
+	{0x0341, 0x02}, /* frame_length_lines */
+	{0x0342, 0x14}, /* line_length_pck */
+	{0x0343, 0xD8}, /* line_length_pck */
+	{0x0344, 0x01}, /* x_addr_start */
+	{0x0345, 0x20}, /* x_addr_start */
+	{0x0346, 0x02}, /* y_addr_start */
+	{0x0347, 0x24}, /* y_addr_start */
+	{0x0348, 0x0E}, /* x_addr_end */
+	{0x0349, 0xA0}, /* x_addr_end */
+	{0x034A, 0x09}, /* y_addr_end */
+	{0x034B, 0xA4}, /* y_addr_end */
+	{0x034C, 0x03}, /* x_output_size */
+	{0x034D, 0x60}, /* x_output_size */
+	{0x034E, 0x01}, /* y_output_size */
+	{0x034F, 0xE0}, /* y_output_size */
+	{0x0380, 0x00}, /* x_even_inc */
+	{0x0381, 0x01}, /* x_even_inc */
+	{0x0382, 0x00}, /* x_odd_inc */
+	{0x0383, 0x07}, /* x_odd_inc */
+	{0x0384, 0x00}, /* y_even_inc */
+	{0x0385, 0x01}, /* y_even_inc */
+	{0x0386, 0x00}, /* y_odd_inc */
+	{0x0387, 0x07}, /* y_odd_inc */
+	{0x0900, 0x01}, /* binning_mode */
+	{0x0901, 0x44}, /* binning_type */
+	{0x0902, 0x01}, /* binning_weighting */
+};
+
+static struct msm_camera_i2c_reg_conf s5k3l1yx_dpcm_settings[] = {
+	{0x0501, 0x01}, /* compression_algorithim_L(1d) */
+	{0x0112, 0x0A}, /* CCP_data_format_H */
+	{0x0113, 0x08}, /* CCP_data_format_L raw8=0808 ,DCPM10 -->8= 0A08 */
+	{0x0306, 0x00}, /* pll_multiplier */
+	{0x0307, 0xA0}, /* pll_multiplier */
+	{0x0202, 0x09}, /* coarse_integration_time */
+	{0x0203, 0x32}, /* coarse_integration_time */
+	{0x0340, 0x0B}, /* frame_length_lines */
+	{0x0341, 0xEC}, /* frame_length_lines */
+	{0x0342, 0x11}, /* line_length_pck */
+	{0x0343, 0x80}, /* line_length_pck */
+	{0x0344, 0x00}, /* x_addr_start */
+	{0x0345, 0x08}, /* x_addr_start */
+	{0x0346, 0x00}, /* y_addr_start */
+	{0x0347, 0x00}, /* y_addr_start */
+	{0x0348, 0x0F}, /* x_addr_end */
+	{0x0349, 0xA7}, /* x_addr_end */
+	{0x034A, 0x0B}, /* y_addr_end */
+	{0x034B, 0xC7}, /* y_addr_end */
+	{0x034C, 0x0F}, /* x_output_size */
+	{0x034D, 0xA0}, /* x_output_size */
+	{0x034E, 0x0B}, /* y_output_size */
+	{0x034F, 0xC8}, /* y_output_size */
+	{0x0380, 0x00}, /* x_even_inc */
+	{0x0381, 0x01}, /* x_even_inc */
+	{0x0382, 0x00}, /* x_odd_inc */
+	{0x0383, 0x01}, /* x_odd_inc */
+	{0x0384, 0x00}, /* y_even_inc */
+	{0x0385, 0x01}, /* y_even_inc */
+	{0x0386, 0x00}, /* y_odd_inc */
+	{0x0387, 0x01}, /* y_odd_inc */
+	{0x0900, 0x00}, /* binning_mode */
+	{0x0901, 0x22}, /* binning_type */
+	{0x0902, 0x01}, /* binning_weighting */
+};
+
+static struct msm_camera_i2c_reg_conf s5k3l1yx_recommend_settings[] = {
+	{0x0100, 0x00},
+	{0x0103, 0x01}, /* software_reset */
+	{0x0104, 0x00}, /* grouped_parameter_hold */
+	{0x0114, 0x03}, /* CSI_lane_mode, 4 lane setting */
+	{0x0120, 0x00}, /* gain_mode, global analogue gain*/
+	{0x0121, 0x00}, /* exposure_mode, global exposure */
+	{0x0136, 0x18}, /* Extclk_frequency_mhz */
+	{0x0137, 0x00}, /* Extclk_frequency_mhz */
+	{0x0200, 0x08}, /* fine_integration_time */
+	{0x0201, 0x88}, /* fine_integration_time */
+	{0x0204, 0x00}, /* analogue_gain_code_global */
+	{0x0205, 0x20}, /* analogue_gain_code_global */
+	{0x020E, 0x01}, /* digital_gain_greenR */
+	{0x020F, 0x00}, /* digital_gain_greenR */
+	{0x0210, 0x01}, /* digital_gain_red */
+	{0x0211, 0x00}, /* digital_gain_red */
+	{0x0212, 0x01}, /* digital_gain_blue */
+	{0x0213, 0x00}, /* digital_gain_blue */
+	{0x0214, 0x01}, /* digital_gain_greenB */
+	{0x0215, 0x00}, /* digital_gain_greenB */
+	{0x0300, 0x00}, /* vt_pix_clk_div */
+	{0x0301, 0x02}, /* vt_pix_clk_div */
+	{0x0302, 0x00}, /* vt_sys_clk_div */
+	{0x0303, 0x01}, /* vt_sys_clk_div */
+	{0x0304, 0x00}, /* pre_pll_clk_div */
+	{0x0305, 0x06}, /* pre_pll_clk_div */
+	{0x0308, 0x00}, /* op_pix_clk_div */
+	{0x0309, 0x02}, /* op_pix_clk_div */
+	{0x030A, 0x00}, /* op_sys_clk_div */
+	{0x030B, 0x01}, /* op_sys_clk_div */
+	{0x0800, 0x00}, /* tclk_post for D-PHY control */
+	{0x0801, 0x00}, /* ths_prepare for D-PHY control */
+	{0x0802, 0x00}, /* ths_zero_min for D-PHY control */
+	{0x0803, 0x00}, /* ths_trail for D-PHY control */
+	{0x0804, 0x00}, /* tclk_trail_min for D-PHY control */
+	{0x0805, 0x00}, /* tclk_prepare for D-PHY control */
+	{0x0806, 0x00}, /* tclk_zero_zero for D-PHY control */
+	{0x0807, 0x00}, /* tlpx for D-PHY control */
+	{0x0820, 0x02}, /* requested_link_bit_rate_mbps */
+	{0x0821, 0x94}, /* requested_link_bit_rate_mbps */
+	{0x0822, 0x00}, /* requested_link_bit_rate_mbps */
+	{0x0823, 0x00}, /* requested_link_bit_rate_mbps */
+	{0x3000, 0x0A},
+	{0x3001, 0xF7},
+	{0x3002, 0x0A},
+	{0x3003, 0xF7},
+	{0x3004, 0x08},
+	{0x3005, 0xF8},
+	{0x3006, 0x5B},
+	{0x3007, 0x73},
+	{0x3008, 0x49},
+	{0x3009, 0x0C},
+	{0x300A, 0xF8},
+	{0x300B, 0x4E},
+	{0x300C, 0x64},
+	{0x300D, 0x5C},
+	{0x300E, 0x71},
+	{0x300F, 0x0C},
+	{0x3010, 0x6A},
+	{0x3011, 0x14},
+	{0x3012, 0x14},
+	{0x3013, 0x0C},
+	{0x3014, 0x24},
+	{0x3015, 0x4F},
+	{0x3016, 0x86},
+	{0x3017, 0x0E},
+	{0x3018, 0x2C},
+	{0x3019, 0x30},
+	{0x301A, 0x31},
+	{0x301B, 0x32},
+	{0x301C, 0xFF},
+	{0x301D, 0x33},
+	{0x301E, 0x5C},
+	{0x301F, 0xFA},
+	{0x3020, 0x36},
+	{0x3021, 0x46},
+	{0x3022, 0x92},
+	{0x3023, 0xF5},
+	{0x3024, 0x6E},
+	{0x3025, 0x19},
+	{0x3026, 0x32},
+	{0x3027, 0x4B},
+	{0x3028, 0x04},
+	{0x3029, 0x50},
+	{0x302A, 0x0C},
+	{0x302B, 0x04},
+	{0x302C, 0xEF},
+	{0x302D, 0xC1},
+	{0x302E, 0x74},
+	{0x302F, 0x40},
+	{0x3030, 0x00},
+	{0x3031, 0x00},
+	{0x3032, 0x00},
+	{0x3033, 0x00},
+	{0x3034, 0x0F},
+	{0x3035, 0x01},
+	{0x3036, 0x00},
+	{0x3037, 0x00},
+	{0x3038, 0x88},
+	{0x3039, 0x98},
+	{0x303A, 0x1F},
+	{0x303B, 0x01},
+	{0x303C, 0x00},
+	{0x303D, 0x03},
+	{0x303E, 0x2F},
+	{0x303F, 0x09},
+	{0x3040, 0xFF},
+	{0x3041, 0x22},
+	{0x3042, 0x03},
+	{0x3043, 0x03},
+	{0x3044, 0x20},
+	{0x3045, 0x10},
+	{0x3046, 0x10},
+	{0x3047, 0x08},
+	{0x3048, 0x10},
+	{0x3049, 0x01},
+	{0x304A, 0x00},
+	{0x304B, 0x80},
+	{0x304C, 0x80},
+	{0x304D, 0x00},
+	{0x304E, 0x00},
+	{0x304F, 0x00},
+	{0x3051, 0x09},
+	{0x3052, 0xC4},
+	{0x305A, 0xE0},
+	{0x323D, 0x04},
+	{0x323E, 0x38},
+	{0x3305, 0xDD},
+	{0x3050, 0x01},
+	{0x3202, 0x01},
+	{0x3203, 0x01},
+	{0x3204, 0x01},
+	{0x3205, 0x01},
+	{0x3206, 0x01},
+	{0x3207, 0x01},
+	{0x320A, 0x05},
+	{0x320B, 0x20},
+	{0x3235, 0xB7},
+	{0x324C, 0x04},
+	{0x324A, 0x07},
+	{0x3902, 0x01},
+	{0x3915, 0x70},
+	{0x3916, 0x80},
+	{0x3A00, 0x01},
+	{0x3A06, 0x03},
+	{0x3B29, 0x01},
+	{0x3C11, 0x08},
+	{0x3C12, 0x7B},
+	{0x3C13, 0xC0},
+	{0x3C14, 0x70},
+	{0x3C15, 0x80},
+	{0x3C20, 0x00},
+	{0x3C23, 0x03},
+	{0x3C24, 0x00},
+	{0x3C50, 0x72},
+	{0x3C51, 0x85},
+	{0x3C53, 0x40},
+	{0x3C55, 0xA0},
+	{0x3D00, 0x00},
+	{0x3D01, 0x00},
+	{0x3D11, 0x01},
+	{0x3486, 0x05},
+	{0x3B35, 0x06},
+	{0x3A05, 0x01},
+	{0x3A07, 0x2B},
+	{0x3A09, 0x01},
+	{0x3940, 0xFF},
+	{0x3300, 0x00},
+	{0x3900, 0xFF},
+	{0x3914, 0x08},
+	{0x3A01, 0x0F},
+	{0x3A02, 0xA0},
+	{0x3A03, 0x0B},
+	{0x3A04, 0xC8},
+	{0x3701, 0x00},
+	{0x3702, 0x00},
+	{0x3703, 0x00},
+	{0x3704, 0x00},
+	{0x0101, 0x00}, /* image_orientation, mirror & flip off*/
+	{0x0105, 0x01}, /* mask_corrupted_frames */
+	{0x0110, 0x00}, /* CSI-2_channel_identifier */
+	{0x3942, 0x01}, /* [0] 1:mipi, 0:pvi */
+	{0x0B00, 0x00},
+};
+
+static struct v4l2_subdev_info s5k3l1yx_subdev_info[] = {
+	{
+	.code   = V4L2_MBUS_FMT_SBGGR10_1X10,
+	.colorspace = V4L2_COLORSPACE_JPEG,
+	.fmt    = 1,
+	.order    = 0,
+	},
+	/* more can be supported, to be added later */
+};
+
+static struct msm_camera_i2c_conf_array s5k3l1yx_init_conf[] = {
+	{&s5k3l1yx_recommend_settings[0],
+	ARRAY_SIZE(s5k3l1yx_recommend_settings), 0, MSM_CAMERA_I2C_BYTE_DATA}
+};
+
+static struct msm_camera_i2c_conf_array s5k3l1yx_confs[] = {
+	{&s5k3l1yx_snap_settings[0],
+	ARRAY_SIZE(s5k3l1yx_snap_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+	{&s5k3l1yx_prev_settings[0],
+	ARRAY_SIZE(s5k3l1yx_prev_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+	{&s5k3l1yx_video_60fps_settings[0],
+	ARRAY_SIZE(s5k3l1yx_video_60fps_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+	{&s5k3l1yx_video_90fps_settings[0],
+	ARRAY_SIZE(s5k3l1yx_video_90fps_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+	{&s5k3l1yx_video_120fps_settings[0],
+	ARRAY_SIZE(s5k3l1yx_video_120fps_settings), 0,
+					MSM_CAMERA_I2C_BYTE_DATA},
+	{&s5k3l1yx_dpcm_settings[0],
+	ARRAY_SIZE(s5k3l1yx_dpcm_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+};
+
+static struct msm_sensor_output_info_t s5k3l1yx_dimensions[] = {
+	/* 20 fps snapshot */
+	{
+		.x_output = 4000,
+		.y_output = 3016,
+		.line_length_pclk = 5336,
+		.frame_length_lines = 3052,
+		.vt_pixel_clk = 330000000,
+		.op_pixel_clk = 320000000,
+		.binning_factor = 1,
+	},
+	/* 30 fps preview */
+	{
+		.x_output = 1984,
+		.y_output = 1508,
+		.line_length_pclk = 4480,
+		.frame_length_lines = 2456,
+		.vt_pixel_clk = 330000000,
+		.op_pixel_clk = 320000000,
+		.binning_factor = 1,
+	},
+	/* 60 fps video */
+	{
+		.x_output = 864,
+		.y_output = 480,
+		.line_length_pclk = 5336,
+		.frame_length_lines = 992,
+		.vt_pixel_clk = 330000000,
+		.op_pixel_clk = 320000000,
+		.binning_factor = 1,
+	},
+	/* 90 fps video */
+	{
+		.x_output = 864,
+		.y_output = 480,
+		.line_length_pclk = 5336,
+		.frame_length_lines = 664,
+		.vt_pixel_clk = 330000000,
+		.op_pixel_clk = 320000000,
+		.binning_factor = 1,
+	},
+	/* 120 fps video */
+	{
+		.x_output = 864,
+		.y_output = 480,
+		.line_length_pclk = 5336,
+		.frame_length_lines = 514,
+		.vt_pixel_clk = 330000000,
+		.op_pixel_clk = 320000000,
+		.binning_factor = 1,
+	},
+	/* 24 fps snapshot */
+	{
+		.x_output = 4000,
+		.y_output = 3016,
+		.line_length_pclk = 4480,
+		.frame_length_lines = 3052,
+		.vt_pixel_clk = 330000000,
+		.op_pixel_clk = 320000000,
+		.binning_factor = 1,
+	},
+};
+
+static struct msm_camera_csid_vc_cfg s5k3l1yx_cid_cfg[] = {
+	{0, CSI_RAW10, CSI_DECODE_10BIT},
+	{1, CSI_EMBED_DATA, CSI_DECODE_8BIT},
+};
+
+static struct msm_camera_csi2_params s5k3l1yx_csi_params = {
+	.csid_params = {
+		.lane_cnt = 4,
+		.lut_params = {
+			.num_cid = ARRAY_SIZE(s5k3l1yx_cid_cfg),
+			.vc_cfg = s5k3l1yx_cid_cfg,
+		},
+	},
+	.csiphy_params = {
+		.lane_cnt = 4,
+		.settle_cnt = 0x1B,
+	},
+};
+
+static struct msm_camera_csid_vc_cfg s5k3l1yx_cid_dpcm_cfg[] = {
+	{0, CSI_RAW8, CSI_DECODE_DPCM_10_8_10},
+};
+
+static struct msm_camera_csi2_params s5k3l1yx_csi_dpcm_params = {
+	.csid_params = {
+		.lane_assign = 0xe4,
+		.lane_cnt = 4,
+		.lut_params = {
+			.num_cid = ARRAY_SIZE(s5k3l1yx_cid_dpcm_cfg),
+			.vc_cfg = s5k3l1yx_cid_dpcm_cfg,
+		},
+	},
+	.csiphy_params = {
+		.lane_cnt = 4,
+		.settle_cnt = 0x1B,
+	},
+};
+
+static struct msm_camera_csi2_params *s5k3l1yx_csi_params_array[] = {
+	&s5k3l1yx_csi_params,
+	&s5k3l1yx_csi_params,
+	&s5k3l1yx_csi_params,
+	&s5k3l1yx_csi_params,
+	&s5k3l1yx_csi_params,
+	&s5k3l1yx_csi_dpcm_params,
+};
+
+static struct msm_sensor_output_reg_addr_t s5k3l1yx_reg_addr = {
+	.x_output = 0x34C,
+	.y_output = 0x34E,
+	.line_length_pclk = 0x342,
+	.frame_length_lines = 0x340,
+};
+
+static struct msm_sensor_id_info_t s5k3l1yx_id_info = {
+	.sensor_id_reg_addr = 0x0,
+	.sensor_id = 0x3121,
+};
+
+static struct msm_sensor_exp_gain_info_t s5k3l1yx_exp_gain_info = {
+	.coarse_int_time_addr = 0x202,
+	.global_gain_addr = 0x204,
+	.vert_offset = 8,
+};
+
+static const struct i2c_device_id s5k3l1yx_i2c_id[] = {
+	{SENSOR_NAME, (kernel_ulong_t)&s5k3l1yx_s_ctrl},
+	{ }
+};
+
+static struct i2c_driver s5k3l1yx_i2c_driver = {
+	.id_table = s5k3l1yx_i2c_id,
+	.probe  = msm_sensor_i2c_probe,
+	.driver = {
+		.name = SENSOR_NAME,
+	},
+};
+
+static struct msm_camera_i2c_client s5k3l1yx_sensor_i2c_client = {
+	.addr_type = MSM_CAMERA_I2C_WORD_ADDR,
+};
+
+static int __init msm_sensor_init_module(void)
+{
+	return i2c_add_driver(&s5k3l1yx_i2c_driver);
+}
+
+static struct v4l2_subdev_core_ops s5k3l1yx_subdev_core_ops = {
+	.ioctl = msm_sensor_subdev_ioctl,
+	.s_power = msm_sensor_power,
+};
+
+static struct v4l2_subdev_video_ops s5k3l1yx_subdev_video_ops = {
+	.enum_mbus_fmt = msm_sensor_v4l2_enum_fmt,
+};
+
+static struct v4l2_subdev_ops s5k3l1yx_subdev_ops = {
+	.core = &s5k3l1yx_subdev_core_ops,
+	.video  = &s5k3l1yx_subdev_video_ops,
+};
+
+static struct msm_sensor_fn_t s5k3l1yx_func_tbl = {
+	.sensor_start_stream = msm_sensor_start_stream,
+	.sensor_stop_stream = msm_sensor_stop_stream,
+	.sensor_group_hold_on = msm_sensor_group_hold_on,
+	.sensor_group_hold_off = msm_sensor_group_hold_off,
+	.sensor_set_fps = msm_sensor_set_fps,
+	.sensor_write_exp_gain = msm_sensor_write_exp_gain1,
+	.sensor_write_snapshot_exp_gain = msm_sensor_write_exp_gain1,
+	.sensor_setting = msm_sensor_setting,
+	.sensor_set_sensor_mode = msm_sensor_set_sensor_mode,
+	.sensor_mode_init = msm_sensor_mode_init,
+	.sensor_get_output_info = msm_sensor_get_output_info,
+	.sensor_config = msm_sensor_config,
+	.sensor_power_up = msm_sensor_power_up,
+	.sensor_power_down = msm_sensor_power_down,
+	.sensor_adjust_frame_lines = msm_sensor_adjust_frame_lines,
+};
+
+static struct msm_sensor_reg_t s5k3l1yx_regs = {
+	.default_data_type = MSM_CAMERA_I2C_BYTE_DATA,
+	.start_stream_conf = s5k3l1yx_start_settings,
+	.start_stream_conf_size = ARRAY_SIZE(s5k3l1yx_start_settings),
+	.stop_stream_conf = s5k3l1yx_stop_settings,
+	.stop_stream_conf_size = ARRAY_SIZE(s5k3l1yx_stop_settings),
+	.group_hold_on_conf = s5k3l1yx_groupon_settings,
+	.group_hold_on_conf_size = ARRAY_SIZE(s5k3l1yx_groupon_settings),
+	.group_hold_off_conf = s5k3l1yx_groupoff_settings,
+	.group_hold_off_conf_size =
+		ARRAY_SIZE(s5k3l1yx_groupoff_settings),
+	.init_settings = &s5k3l1yx_init_conf[0],
+	.init_size = ARRAY_SIZE(s5k3l1yx_init_conf),
+	.mode_settings = &s5k3l1yx_confs[0],
+	.output_settings = &s5k3l1yx_dimensions[0],
+	.num_conf = ARRAY_SIZE(s5k3l1yx_confs),
+};
+
+static struct msm_sensor_ctrl_t s5k3l1yx_s_ctrl = {
+	.msm_sensor_reg = &s5k3l1yx_regs,
+	.sensor_i2c_client = &s5k3l1yx_sensor_i2c_client,
+	.sensor_i2c_addr = 0x6E,
+	.sensor_output_reg_addr = &s5k3l1yx_reg_addr,
+	.sensor_id_info = &s5k3l1yx_id_info,
+	.sensor_exp_gain_info = &s5k3l1yx_exp_gain_info,
+	.cam_mode = MSM_SENSOR_MODE_INVALID,
+	.csi_params = &s5k3l1yx_csi_params_array[0],
+	.msm_sensor_mutex = &s5k3l1yx_mut,
+	.sensor_i2c_driver = &s5k3l1yx_i2c_driver,
+	.sensor_v4l2_subdev_info = s5k3l1yx_subdev_info,
+	.sensor_v4l2_subdev_info_size = ARRAY_SIZE(s5k3l1yx_subdev_info),
+	.sensor_v4l2_subdev_ops = &s5k3l1yx_subdev_ops,
+	.func_tbl = &s5k3l1yx_func_tbl,
+	.clk_rate = MSM_SENSOR_MCLK_24HZ,
+};
+
+module_init(msm_sensor_init_module);
+MODULE_DESCRIPTION("Samsung 12MP Bayer sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/sensors/s5k4e1_v4l2.c b/drivers/media/video/msm/sensors/s5k4e1_v4l2.c
new file mode 100644
index 0000000..2d25824
--- /dev/null
+++ b/drivers/media/video/msm/sensors/s5k4e1_v4l2.c
@@ -0,0 +1,532 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_sensor.h"
+#define SENSOR_NAME "s5k4e1"
+#define PLATFORM_DRIVER_NAME "msm_camera_s5k4e1"
+#define s5k4e1_obj s5k4e1_##obj
+#define MSB                             1
+#define LSB                             0
+
+DEFINE_MUTEX(s5k4e1_mut);
+static struct msm_sensor_ctrl_t s5k4e1_s_ctrl;
+
+static struct msm_camera_i2c_reg_conf s5k4e1_start_settings[] = {
+	{0x0100, 0x01},
+};
+
+static struct msm_camera_i2c_reg_conf s5k4e1_stop_settings[] = {
+	{0x0100, 0x00},
+};
+
+static struct msm_camera_i2c_reg_conf s5k4e1_groupon_settings[] = {
+	{0x0104, 0x01},
+};
+
+static struct msm_camera_i2c_reg_conf s5k4e1_groupoff_settings[] = {
+	{0x0104, 0x00},
+};
+
+static struct msm_camera_i2c_reg_conf s5k4e1_prev_settings[] = {
+	/* output size (1304 x 980) */
+	{0x30A9, 0x02},/* Horizontal Binning On */
+	{0x300E, 0xEB},/* Vertical Binning On */
+	{0x0387, 0x03},/* y_odd_inc 03(10b AVG) */
+	{0x0344, 0x00},/* x_addr_start 0 */
+	{0x0345, 0x00},
+	{0x0348, 0x0A},/* x_addr_end 2607 */
+	{0x0349, 0x2F},
+	{0x0346, 0x00},/* y_addr_start 0 */
+	{0x0347, 0x00},
+	{0x034A, 0x07},/* y_addr_end 1959 */
+	{0x034B, 0xA7},
+	{0x0380, 0x00},/* x_even_inc 1 */
+	{0x0381, 0x01},
+	{0x0382, 0x00},/* x_odd_inc 1 */
+	{0x0383, 0x01},
+	{0x0384, 0x00},/* y_even_inc 1 */
+	{0x0385, 0x01},
+	{0x0386, 0x00},/* y_odd_inc 3 */
+	{0x0387, 0x03},
+	{0x034C, 0x05},/* x_output_size 1304 */
+	{0x034D, 0x18},
+	{0x034E, 0x03},/* y_output_size 980 */
+	{0x034F, 0xd4},
+	{0x30BF, 0xAB},/* outif_enable[7], data_type[5:0](2Bh = bayer 10bit} */
+	{0x30C0, 0xA0},/* video_offset[7:4] 3260%12 */
+	{0x30C8, 0x06},/* video_data_length 1600 = 1304 * 1.25 */
+	{0x30C9, 0x5E},
+	/* Timing Configuration */
+	{0x0202, 0x03},
+	{0x0203, 0x14},
+	{0x0204, 0x00},
+	{0x0205, 0x80},
+	{0x0340, 0x03},/* Frame Length */
+	{0x0341, 0xE0},
+	{0x0342, 0x0A},/* 2738  Line Length */
+	{0x0343, 0xB2},
+};
+
+static struct msm_camera_i2c_reg_conf s5k4e1_snap_settings[] = {
+	/*Output Size (2608x1960)*/
+	{0x30A9, 0x03},/* Horizontal Binning Off */
+	{0x300E, 0xE8},/* Vertical Binning Off */
+	{0x0387, 0x01},/* y_odd_inc */
+	{0x034C, 0x0A},/* x_output size */
+	{0x034D, 0x30},
+	{0x034E, 0x07},/* y_output size */
+	{0x034F, 0xA8},
+	{0x30BF, 0xAB},/* outif_enable[7], data_type[5:0](2Bh = bayer 10bit} */
+	{0x30C0, 0x80},/* video_offset[7:4] 3260%12 */
+	{0x30C8, 0x0C},/* video_data_length 3260 = 2608 * 1.25 */
+	{0x30C9, 0xBC},
+	/*Timing configuration*/
+	{0x0202, 0x06},
+	{0x0203, 0x28},
+	{0x0204, 0x00},
+	{0x0205, 0x80},
+	{0x0340, 0x07},/* Frame Length */
+	{0x0341, 0xB4},
+	{0x0342, 0x0A},/* 2738 Line Length */
+	{0x0343, 0xB2},
+};
+
+static struct msm_camera_i2c_reg_conf s5k4e1_recommend_settings[] = {
+	/* Reset setting */
+	{0x0103, 0x01},
+	/* MIPI settings */
+	{0x30BD, 0x00},/* SEL_CCP[0] */
+	{0x3084, 0x15},/* SYNC Mode */
+	{0x30BE, 0x1A},/* M_PCLKDIV_AUTO[4], M_DIV_PCLK[3:0] */
+	{0x30C1, 0x01},/* pack video enable [0] */
+	{0x30EE, 0x02},/* DPHY enable [ 1] */
+	{0x3111, 0x86},/* Embedded data off [5] */
+
+	/* REC Settings */
+	/*CDS timing setting ... */
+	{0x3000, 0x05},
+	{0x3001, 0x03},
+	{0x3002, 0x08},
+	{0x3003, 0x0A},
+	{0x3004, 0x50},
+	{0x3005, 0x0E},
+	{0x3006, 0x5E},
+	{0x3007, 0x00},
+	{0x3008, 0x78},
+	{0x3009, 0x78},
+	{0x300A, 0x50},
+	{0x300B, 0x08},
+	{0x300C, 0x14},
+	{0x300D, 0x00},
+	{0x300E, 0xE8},
+	{0x300F, 0x82},
+	{0x301B, 0x77},
+
+	/* CDS option setting ... */
+	{0x3010, 0x00},
+	{0x3011, 0x3A},
+	{0x3029, 0x04},
+	{0x3012, 0x30},
+	{0x3013, 0xA0},
+	{0x3014, 0x00},
+	{0x3015, 0x00},
+	{0x3016, 0x30},
+	{0x3017, 0x94},
+	{0x3018, 0x70},
+	{0x301D, 0xD4},
+	{0x3021, 0x02},
+	{0x3022, 0x24},
+	{0x3024, 0x40},
+	{0x3027, 0x08},
+
+	/* Pixel option setting ...   */
+	{0x301C, 0x04},
+	{0x30D8, 0x3F},
+	{0x302B, 0x01},
+
+	{0x3070, 0x5F},
+	{0x3071, 0x00},
+	{0x3080, 0x04},
+	{0x3081, 0x38},
+
+	/* PLL settings */
+	{0x0305, 0x04},
+	{0x0306, 0x00},
+	{0x0307, 0x44},
+	{0x30B5, 0x00},
+	{0x30E2, 0x01},/* num lanes[1:0] = 2 */
+	{0x30F1, 0xB0},
+};
+
+static struct v4l2_subdev_info s5k4e1_subdev_info[] = {
+	{
+	.code   = V4L2_MBUS_FMT_SGRBG10_1X10,
+	.colorspace = V4L2_COLORSPACE_JPEG,
+	.fmt    = 1,
+	.order    = 0,
+	},
+	/* more can be supported, to be added later */
+};
+
+static struct msm_camera_i2c_conf_array s5k4e1_init_conf[] = {
+	{&s5k4e1_recommend_settings[0],
+	ARRAY_SIZE(s5k4e1_recommend_settings), 0, MSM_CAMERA_I2C_BYTE_DATA}
+};
+
+static struct msm_camera_i2c_conf_array s5k4e1_confs[] = {
+	{&s5k4e1_snap_settings[0],
+	ARRAY_SIZE(s5k4e1_snap_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+	{&s5k4e1_prev_settings[0],
+	ARRAY_SIZE(s5k4e1_prev_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+};
+
+static struct msm_sensor_output_info_t s5k4e1_dimensions[] = {
+	{
+		.x_output = 0xA30,
+		.y_output = 0x7A8,
+		.line_length_pclk = 0xAB2,
+		.frame_length_lines = 0x7B4,
+		.vt_pixel_clk = 81600000,
+		.op_pixel_clk = 81600000,
+		.binning_factor = 0,
+	},
+	{
+		.x_output = 0x518,
+		.y_output = 0x3D4,
+		.line_length_pclk = 0xAB2,
+		.frame_length_lines = 0x3E0,
+		.vt_pixel_clk = 81600000,
+		.op_pixel_clk = 81600000,
+		.binning_factor = 1,
+	},
+};
+
+static struct msm_camera_csi_params s5k4e1_csi_params = {
+	.data_format = CSI_10BIT,
+	.lane_cnt    = 1,
+	.lane_assign = 0xe4,
+	.dpcm_scheme = 0,
+	.settle_cnt  = 24,
+};
+
+static struct msm_camera_csi_params *s5k4e1_csi_params_array[] = {
+	&s5k4e1_csi_params,
+	&s5k4e1_csi_params,
+};
+
+static struct msm_sensor_output_reg_addr_t s5k4e1_reg_addr = {
+	.x_output = 0x034C,
+	.y_output = 0x034E,
+	.line_length_pclk = 0x0342,
+	.frame_length_lines = 0x0340,
+};
+
+static struct msm_sensor_id_info_t s5k4e1_id_info = {
+	.sensor_id_reg_addr = 0x0000,
+	.sensor_id = 0x4E10,
+};
+
+static struct msm_sensor_exp_gain_info_t s5k4e1_exp_gain_info = {
+	.coarse_int_time_addr = 0x0202,
+	.global_gain_addr = 0x0204,
+	.vert_offset = 4,
+};
+
+static inline uint8_t s5k4e1_byte(uint16_t word, uint8_t offset)
+{
+	return word >> (offset * BITS_PER_BYTE);
+}
+
+static int32_t s5k4e1_write_prev_exp_gain(struct msm_sensor_ctrl_t *s_ctrl,
+						uint16_t gain, uint32_t line)
+{
+	uint16_t max_legal_gain = 0x0200;
+	int32_t rc = 0;
+	static uint32_t fl_lines, offset;
+
+	pr_info("s5k4e1_write_prev_exp_gain :%d %d\n", gain, line);
+	offset = s_ctrl->sensor_exp_gain_info->vert_offset;
+	if (gain > max_legal_gain) {
+		CDBG("Max legal gain Line:%d\n", __LINE__);
+		gain = max_legal_gain;
+	}
+
+	/* Analogue Gain */
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->global_gain_addr,
+		s5k4e1_byte(gain, MSB),
+		MSM_CAMERA_I2C_BYTE_DATA);
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->global_gain_addr + 1,
+		s5k4e1_byte(gain, LSB),
+		MSM_CAMERA_I2C_BYTE_DATA);
+
+	if (line > (s_ctrl->curr_frame_length_lines - offset)) {
+		fl_lines = line + offset;
+		s_ctrl->func_tbl->sensor_group_hold_on(s_ctrl);
+		msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+			s_ctrl->sensor_output_reg_addr->frame_length_lines,
+			s5k4e1_byte(fl_lines, MSB),
+			MSM_CAMERA_I2C_BYTE_DATA);
+		msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+			s_ctrl->sensor_output_reg_addr->frame_length_lines + 1,
+			s5k4e1_byte(fl_lines, LSB),
+			MSM_CAMERA_I2C_BYTE_DATA);
+		/* Coarse Integration Time */
+		msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+			s_ctrl->sensor_exp_gain_info->coarse_int_time_addr,
+			s5k4e1_byte(line, MSB),
+			MSM_CAMERA_I2C_BYTE_DATA);
+		msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+			s_ctrl->sensor_exp_gain_info->coarse_int_time_addr + 1,
+			s5k4e1_byte(line, LSB),
+			MSM_CAMERA_I2C_BYTE_DATA);
+		s_ctrl->func_tbl->sensor_group_hold_off(s_ctrl);
+	} else if (line < (fl_lines - offset)) {
+		fl_lines = line + offset;
+		if (fl_lines < s_ctrl->curr_frame_length_lines)
+			fl_lines = s_ctrl->curr_frame_length_lines;
+
+		s_ctrl->func_tbl->sensor_group_hold_on(s_ctrl);
+		/* Coarse Integration Time */
+		msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+			s_ctrl->sensor_exp_gain_info->coarse_int_time_addr,
+			s5k4e1_byte(line, MSB),
+			MSM_CAMERA_I2C_BYTE_DATA);
+		msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+			s_ctrl->sensor_exp_gain_info->coarse_int_time_addr + 1,
+			s5k4e1_byte(line, LSB),
+			MSM_CAMERA_I2C_BYTE_DATA);
+		msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+			s_ctrl->sensor_output_reg_addr->frame_length_lines,
+			s5k4e1_byte(fl_lines, MSB),
+			MSM_CAMERA_I2C_BYTE_DATA);
+		msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+			s_ctrl->sensor_output_reg_addr->frame_length_lines + 1,
+			s5k4e1_byte(fl_lines, LSB),
+			MSM_CAMERA_I2C_BYTE_DATA);
+		s_ctrl->func_tbl->sensor_group_hold_off(s_ctrl);
+	} else {
+		fl_lines = line+4;
+		s_ctrl->func_tbl->sensor_group_hold_on(s_ctrl);
+		/* Coarse Integration Time */
+		msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+			s_ctrl->sensor_exp_gain_info->coarse_int_time_addr,
+			s5k4e1_byte(line, MSB),
+			MSM_CAMERA_I2C_BYTE_DATA);
+		msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+			s_ctrl->sensor_exp_gain_info->coarse_int_time_addr + 1,
+			s5k4e1_byte(line, LSB),
+			MSM_CAMERA_I2C_BYTE_DATA);
+		s_ctrl->func_tbl->sensor_group_hold_off(s_ctrl);
+	}
+	return rc;
+}
+
+static int32_t s5k4e1_write_pict_exp_gain(struct msm_sensor_ctrl_t *s_ctrl,
+		uint16_t gain, uint32_t line)
+{
+	uint16_t max_legal_gain = 0x0200;
+	uint16_t min_ll_pck = 0x0AB2;
+	uint32_t ll_pck, fl_lines;
+	uint32_t ll_ratio;
+	uint8_t gain_msb, gain_lsb;
+	uint8_t intg_time_msb, intg_time_lsb;
+	uint8_t ll_pck_msb, ll_pck_lsb;
+
+	if (gain > max_legal_gain) {
+		CDBG("Max legal gain Line:%d\n", __LINE__);
+		gain = max_legal_gain;
+	}
+
+	pr_info("s5k4e1_write_exp_gain : gain = %d line = %d\n", gain, line);
+	line = (uint32_t) (line * s_ctrl->fps_divider);
+	fl_lines = s_ctrl->curr_frame_length_lines * s_ctrl->fps_divider / Q10;
+	ll_pck = s_ctrl->curr_line_length_pclk;
+
+	if (fl_lines < (line / Q10))
+		ll_ratio = (line / (fl_lines - 4));
+	else
+		ll_ratio = Q10;
+
+	ll_pck = ll_pck * ll_ratio / Q10;
+	line = line / ll_ratio;
+	if (ll_pck < min_ll_pck)
+		ll_pck = min_ll_pck;
+
+	gain_msb = (uint8_t) ((gain & 0xFF00) >> 8);
+	gain_lsb = (uint8_t) (gain & 0x00FF);
+
+	intg_time_msb = (uint8_t) ((line & 0xFF00) >> 8);
+	intg_time_lsb = (uint8_t) (line & 0x00FF);
+
+	ll_pck_msb = (uint8_t) ((ll_pck & 0xFF00) >> 8);
+	ll_pck_lsb = (uint8_t) (ll_pck & 0x00FF);
+
+	s_ctrl->func_tbl->sensor_group_hold_on(s_ctrl);
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->global_gain_addr,
+		gain_msb,
+		MSM_CAMERA_I2C_BYTE_DATA);
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->global_gain_addr + 1,
+		gain_lsb,
+		MSM_CAMERA_I2C_BYTE_DATA);
+
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_output_reg_addr->line_length_pclk,
+		ll_pck_msb,
+		MSM_CAMERA_I2C_BYTE_DATA);
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_output_reg_addr->line_length_pclk + 1,
+		ll_pck_lsb,
+		MSM_CAMERA_I2C_BYTE_DATA);
+
+	/* Coarse Integration Time */
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->coarse_int_time_addr,
+		intg_time_msb,
+		MSM_CAMERA_I2C_BYTE_DATA);
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->coarse_int_time_addr + 1,
+		intg_time_lsb,
+		MSM_CAMERA_I2C_BYTE_DATA);
+	s_ctrl->func_tbl->sensor_group_hold_off(s_ctrl);
+
+	return 0;
+}
+
+int32_t s5k4e1_sensor_i2c_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	int rc = 0;
+	struct msm_camera_sensor_info *s_info;
+
+	rc = msm_sensor_i2c_probe(client, id);
+
+	s_info = client->dev.platform_data;
+	if (s_info == NULL) {
+		pr_err("%s %s NULL sensor data\n", __func__, client->name);
+		return -EFAULT;
+	}
+
+	if (s_info->actuator_info->vcm_enable) {
+		rc = gpio_request(s_info->actuator_info->vcm_pwd,
+				"msm_actuator");
+		if (rc < 0)
+			pr_err("%s: gpio_request:msm_actuator %d failed\n",
+				__func__, s_info->actuator_info->vcm_pwd);
+		rc = gpio_direction_output(s_info->actuator_info->vcm_pwd, 0);
+		if (rc < 0)
+			pr_err("%s: gpio:msm_actuator %d direction can't be set\n",
+				__func__, s_info->actuator_info->vcm_pwd);
+		gpio_free(s_info->actuator_info->vcm_pwd);
+	}
+
+	return rc;
+}
+
+static const struct i2c_device_id s5k4e1_i2c_id[] = {
+	{SENSOR_NAME, (kernel_ulong_t)&s5k4e1_s_ctrl},
+	{ }
+};
+
+static struct i2c_driver s5k4e1_i2c_driver = {
+	.id_table = s5k4e1_i2c_id,
+	.probe  = s5k4e1_sensor_i2c_probe,
+	.driver = {
+		.name = SENSOR_NAME,
+	},
+};
+
+static struct msm_camera_i2c_client s5k4e1_sensor_i2c_client = {
+	.addr_type = MSM_CAMERA_I2C_WORD_ADDR,
+};
+
+static int __init msm_sensor_init_module(void)
+{
+	return i2c_add_driver(&s5k4e1_i2c_driver);
+}
+
+static struct v4l2_subdev_core_ops s5k4e1_subdev_core_ops = {
+	.ioctl = msm_sensor_subdev_ioctl,
+	.s_power = msm_sensor_power,
+};
+
+static struct v4l2_subdev_video_ops s5k4e1_subdev_video_ops = {
+	.enum_mbus_fmt = msm_sensor_v4l2_enum_fmt,
+};
+
+static struct v4l2_subdev_ops s5k4e1_subdev_ops = {
+	.core = &s5k4e1_subdev_core_ops,
+	.video  = &s5k4e1_subdev_video_ops,
+};
+
+static struct msm_sensor_fn_t s5k4e1_func_tbl = {
+	.sensor_start_stream = msm_sensor_start_stream,
+	.sensor_stop_stream = msm_sensor_stop_stream,
+	.sensor_group_hold_on = msm_sensor_group_hold_on,
+	.sensor_group_hold_off = msm_sensor_group_hold_off,
+	.sensor_set_fps = msm_sensor_set_fps,
+	.sensor_write_exp_gain = s5k4e1_write_prev_exp_gain,
+	.sensor_write_snapshot_exp_gain = s5k4e1_write_pict_exp_gain,
+	.sensor_csi_setting = msm_sensor_setting1,
+	.sensor_set_sensor_mode = msm_sensor_set_sensor_mode,
+	.sensor_mode_init = msm_sensor_mode_init,
+	.sensor_get_output_info = msm_sensor_get_output_info,
+	.sensor_config = msm_sensor_config,
+	.sensor_power_up = msm_sensor_power_up,
+	.sensor_power_down = msm_sensor_power_down,
+};
+
+static struct msm_sensor_reg_t s5k4e1_regs = {
+	.default_data_type = MSM_CAMERA_I2C_BYTE_DATA,
+	.start_stream_conf = s5k4e1_start_settings,
+	.start_stream_conf_size = ARRAY_SIZE(s5k4e1_start_settings),
+	.stop_stream_conf = s5k4e1_stop_settings,
+	.stop_stream_conf_size = ARRAY_SIZE(s5k4e1_stop_settings),
+	.group_hold_on_conf = s5k4e1_groupon_settings,
+	.group_hold_on_conf_size = ARRAY_SIZE(s5k4e1_groupon_settings),
+	.group_hold_off_conf = s5k4e1_groupoff_settings,
+	.group_hold_off_conf_size =
+		ARRAY_SIZE(s5k4e1_groupoff_settings),
+	.init_settings = &s5k4e1_init_conf[0],
+	.init_size = ARRAY_SIZE(s5k4e1_init_conf),
+	.mode_settings = &s5k4e1_confs[0],
+	.output_settings = &s5k4e1_dimensions[0],
+	.num_conf = ARRAY_SIZE(s5k4e1_confs),
+};
+
+static struct msm_sensor_ctrl_t s5k4e1_s_ctrl = {
+	.msm_sensor_reg = &s5k4e1_regs,
+	.sensor_i2c_client = &s5k4e1_sensor_i2c_client,
+	.sensor_i2c_addr = 0x6C,
+	.sensor_output_reg_addr = &s5k4e1_reg_addr,
+	.sensor_id_info = &s5k4e1_id_info,
+	.sensor_exp_gain_info = &s5k4e1_exp_gain_info,
+	.cam_mode = MSM_SENSOR_MODE_INVALID,
+	.csic_params = &s5k4e1_csi_params_array[0],
+	.msm_sensor_mutex = &s5k4e1_mut,
+	.sensor_i2c_driver = &s5k4e1_i2c_driver,
+	.sensor_v4l2_subdev_info = s5k4e1_subdev_info,
+	.sensor_v4l2_subdev_info_size = ARRAY_SIZE(s5k4e1_subdev_info),
+	.sensor_v4l2_subdev_ops = &s5k4e1_subdev_ops,
+	.func_tbl = &s5k4e1_func_tbl,
+	.clk_rate = MSM_SENSOR_MCLK_24HZ,
+};
+
+module_init(msm_sensor_init_module);
+MODULE_DESCRIPTION("Samsung 5MP Bayer sensor driver");
+MODULE_LICENSE("GPL v2");
+
+
diff --git a/drivers/media/video/msm/sensors/vx6953.c b/drivers/media/video/msm/sensors/vx6953.c
new file mode 100644
index 0000000..b43782c
--- /dev/null
+++ b/drivers/media/video/msm/sensors/vx6953.c
@@ -0,0 +1,2058 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_sensor.h"
+#include "msm.h"
+#include "vx6953.h"
+#include "vx6953_reg.h"
+#define SENSOR_NAME "vx6953"
+#define PLATFORM_DRIVER_NAME "msm_camera_vx6953"
+#define vx6953_obj vx6953_##obj
+
+DEFINE_MUTEX(vx6953_mut);
+
+#undef CDBG
+#define CDBG printk
+#define REG_GROUPED_PARAMETER_HOLD			0x0104
+#define GROUPED_PARAMETER_HOLD_OFF			0x00
+#define GROUPED_PARAMETER_HOLD				0x01
+#define REG_MODE_SELECT					0x0100
+#define MODE_SELECT_STANDBY_MODE			0x00
+#define MODE_SELECT_STREAM				0x01
+/* Integration Time */
+#define REG_COARSE_INTEGRATION_TIME_HI			0x0202
+#define REG_COARSE_INTEGRATION_TIME_LO			0x0203
+/* Gain */
+#define REG_ANALOGUE_GAIN_CODE_GLOBAL_HI		0x0204
+#define REG_ANALOGUE_GAIN_CODE_GLOBAL_LO		0x0205
+/* Digital Gain */
+#define REG_DIGITAL_GAIN_GREEN_R_HI			0x020E
+#define REG_DIGITAL_GAIN_GREEN_R_LO			0x020F
+#define REG_DIGITAL_GAIN_RED_HI				0x0210
+#define REG_DIGITAL_GAIN_RED_LO				0x0211
+#define REG_DIGITAL_GAIN_BLUE_HI			0x0212
+#define REG_DIGITAL_GAIN_BLUE_LO			0x0213
+#define REG_DIGITAL_GAIN_GREEN_B_HI			0x0214
+#define REG_DIGITAL_GAIN_GREEN_B_LO			0x0215
+/* output bits setting */
+#define REG_0x0112					0x0112
+#define REG_0x0113					0x0113
+/* PLL registers */
+#define REG_VT_PIX_CLK_DIV				0x0301
+#define REG_PRE_PLL_CLK_DIV				0x0305
+#define REG_PLL_MULTIPLIER				0x0307
+#define REG_OP_PIX_CLK_DIV				0x0309
+#define REG_0x034c					0x034c
+#define REG_0x034d					0x034d
+#define REG_0x034e					0x034e
+#define REG_0x034f					0x034f
+#define REG_0x0387					0x0387
+#define REG_0x0383					0x0383
+#define REG_FRAME_LENGTH_LINES_HI			0x0340
+#define REG_FRAME_LENGTH_LINES_LO			0x0341
+#define REG_LINE_LENGTH_PCK_HI				0x0342
+#define REG_LINE_LENGTH_PCK_LO				0x0343
+#define REG_0x3030					0x3030
+#define REG_0x0111					0x0111
+#define REG_0x0136					0x0136
+#define REG_0x0137					0x0137
+#define REG_0x0b00					0x0b00
+#define REG_0x3001					0x3001
+#define REG_0x3004					0x3004
+#define REG_0x3007					0x3007
+#define REG_0x301a					0x301a
+#define REG_0x3101					0x3101
+#define REG_0x3364					0x3364
+#define REG_0x3365					0x3365
+#define REG_0x0b83					0x0b83
+#define REG_0x0b84					0x0b84
+#define REG_0x0b85					0x0b85
+#define REG_0x0b88					0x0b88
+#define REG_0x0b89					0x0b89
+#define REG_0x0b8a					0x0b8a
+#define REG_0x3005					0x3005
+#define REG_0x3010					0x3010
+#define REG_0x3036					0x3036
+#define REG_0x3041					0x3041
+#define REG_0x0b80					0x0b80
+#define REG_0x0900					0x0900
+#define REG_0x0901					0x0901
+#define REG_0x0902					0x0902
+#define REG_0x3016					0x3016
+#define REG_0x301d					0x301d
+#define REG_0x317e					0x317e
+#define REG_0x317f					0x317f
+#define REG_0x3400					0x3400
+#define REG_0x303a					0x303a
+#define REG_0x1716					0x1716
+#define REG_0x1717					0x1717
+#define REG_0x1718					0x1718
+#define REG_0x1719					0x1719
+#define REG_0x3006					0x3006
+#define REG_0x301b					0x301b
+#define REG_0x3098					0x3098
+#define REG_0x309d					0x309d
+#define REG_0x3011					0x3011
+#define REG_0x3035					0x3035
+#define REG_0x3045					0x3045
+#define REG_0x3210					0x3210
+#define	REG_0x0111					0x0111
+#define REG_0x3410					0x3410
+#define REG_0x0b06					0x0b06
+#define REG_0x0b07					0x0b07
+#define REG_0x0b08					0x0b08
+#define REG_0x0b09					0x0b09
+#define REG_0x3640					0x3640
+/* Test Pattern */
+#define REG_TEST_PATTERN_MODE				0x0601
+/* 16bit address - 8 bit context register structure */
+#define	VX6953_STM5M0EDOF_OFFSET	9
+#define	Q8		0x00000100
+#define	Q10		0x00000400
+#define	VX6953_STM5M0EDOF_MAX_SNAPSHOT_EXPOSURE_LINE_COUNT	2922
+#define	VX6953_STM5M0EDOF_DEFAULT_MASTER_CLK_RATE	24000000
+#define	VX6953_STM5M0EDOF_OP_PIXEL_CLOCK_RATE	79800000
+#define	VX6953_STM5M0EDOF_VT_PIXEL_CLOCK_RATE	88670000
+/* Full	Size */
+#define	VX6953_FULL_SIZE_WIDTH	2608
+#define	VX6953_FULL_SIZE_HEIGHT		1960
+#define	VX6953_FULL_SIZE_DUMMY_PIXELS	1
+#define	VX6953_FULL_SIZE_DUMMY_LINES	0
+/* Quarter Size	*/
+#define	VX6953_QTR_SIZE_WIDTH	1304
+#define	VX6953_QTR_SIZE_HEIGHT		980
+#define	VX6953_QTR_SIZE_DUMMY_PIXELS	1
+#define	VX6953_QTR_SIZE_DUMMY_LINES		0
+/* Blanking	as measured	on the scope */
+/* Full	Size */
+#define	VX6953_HRZ_FULL_BLK_PIXELS	348
+#define	VX6953_VER_FULL_BLK_LINES	40
+/* Quarter Size	*/
+#define	VX6953_HRZ_QTR_BLK_PIXELS	1628
+#define	VX6953_VER_QTR_BLK_LINES	28
+#define	MAX_LINE_LENGTH_PCK		8190
+#define	MAX_FRAME_LENGTH_LINES	16383
+#define	VX6953_REVISION_NUMBER_CUT2	0x10/*revision number	for	Cut2.0*/
+#define	VX6953_REVISION_NUMBER_CUT3	0x20/*revision number	for	Cut3.0*/
+static struct msm_sensor_ctrl_t vx6953_s_ctrl;
+static uint32_t fps_divider;/* init to 1 * 0x00000400 */
+static uint16_t fps;
+static uint8_t vx6953_stm5m0edof_delay_msecs_stdby;
+static struct msm_camera_i2c_reg_conf vx6953_start_settings[] = {
+	{0x0100, 0x01},
+};
+
+static struct msm_camera_i2c_reg_conf vx6953_stop_settings[] = {
+	{0x0100, 0x00},
+};
+
+static struct msm_camera_i2c_reg_conf vx6953_groupon_settings[] = {
+	{0x0104, 0x01},
+};
+
+static struct msm_camera_i2c_reg_conf vx6953_groupoff_settings[] = {
+	{0x0104, 0x00},
+};
+
+static struct msm_camera_i2c_reg_conf vx6953_prev_settings[] = {
+	{0x0202, 0x03},/*REG = 0x0202 coarse integration_time_hi*/
+	{0x0203, 0xD0},/*REG = 0x0203 coarse_integration_time_lo*/
+	{0x0205, 0xC0},/*REG = 0x0205 analogue_gain_code_global*/
+	{0x0340, 0x03},/*REG = 0x0340 frame_length_lines_hi*/
+	{0x0341, 0xf0},/*REG = 0x0341 frame_length_lines_lo*/
+	{0x0342, 0x0b},/*REG = 0x0342  line_length_pck_hi*/
+	{0x0343, 0x74},/*REG = 0x0343  line_length_pck_lo*/
+	{0x3005, 0x03},/*REG = 0x3005*/
+	{0x3010, 0x00},/*REG = 0x3010*/
+	{0x3011, 0x01},/*REG = 0x3011*/
+	{0x301a, 0x6a},/*REG = 0x301a*/
+	{0x3035, 0x03},/*REG = 0x3035*/
+	{0x3036, 0x2c},/*REG = 0x3036*/
+	{0x3041, 0x00},/*REG = 0x3041*/
+	{0x3042, 0x24},/*REG = 0x3042*/
+	{0x3045, 0x81},/*REG = 0x3045*/
+	{0x0b80, 0x02},/*REG = 0x0b80 edof estimate*/
+	{0x0900, 0x01},/*REG = 0x0900*/
+	{0x0901, 0x22},/*REG = 0x0901*/
+	{0x0902, 0x04},/*REG = 0x0902*/
+	{0x0383, 0x03},/*REG = 0x0383*/
+	{0x0387, 0x03},/*REG = 0x0387*/
+	{0x034c, 0x05},/*REG = 0x034c*/
+	{0x034d, 0x18},/*REG = 0x034d*/
+	{0x034e, 0x03},/*REG = 0x034e*/
+	{0x034f, 0xd4},/*REG = 0x034f*/
+	{0x1716, 0x02},/*0x1716*/
+	{0x1717, 0x04},/*0x1717*/
+	{0x1718, 0x08},/*0x1718*/
+	{0x1719, 0x2c},/*0x1719*/
+};
+
+static struct msm_camera_i2c_reg_conf vx6953_snap_settings[] = {
+	{0x0202, 0x07},/*REG = 0x0202 coarse_integration_time_hi*/
+	{0x0203, 0x00},/*REG = 0x0203 coarse_integration_time_lo*/
+	{0x0205, 0xc0},/*REG = 0x0205 analogue_gain_code_global*/
+	{0x0340, 0x07},/*REG = 0x0340 frame_length_lines_hi*/
+	{0x0341, 0xd0},/*REG = 0x0341 frame_length_lines_lo*/
+	{0x0342, 0x0b},/*REG = 0x0342 line_length_pck_hi*/
+	{0x0343, 0x8c},/*REG = 0x0343 line_length_pck_lo*/
+	{0x3005, 0x01},/*REG = 0x3005*/
+	{0x3010, 0x00},/*REG = 0x3010*/
+	{0x3011, 0x00},/*REG = 0x3011*/
+	{0x301a, 0x55},/*REG = 0x301a*/
+	{0x3035, 0x01},/*REG = 0x3035*/
+	{0x3036, 0x23},/*REG = 0x3036*/
+	{0x3041, 0x00},/*REG = 0x3041*/
+	{0x3042, 0x24},/*REG = 0x3042*/
+	{0x3045, 0xb7},/*REG = 0x3045*/
+	{0x0b80, 0x01},/*REG = 0x0b80 edof application*/
+	{0x0900, 0x00},/*REG = 0x0900*/
+	{0x0901, 0x00},/*REG = 0x0901*/
+	{0x0902, 0x00},/*REG = 0x0902*/
+	{0x0383, 0x01},/*REG = 0x0383*/
+	{0x0387, 0x01},/*REG = 0x0387*/
+	{0x034c, 0x0A},/*REG = 0x034c*/
+	{0x034d, 0x30},/*REG = 0x034d*/
+	{0x034e, 0x07},/*REG = 0x034e*/
+	{0x034f, 0xA8},/*REG = 0x034f*/
+	{0x1716, 0x02},/*0x1716*/
+	{0x1717, 0x0d},/*0x1717*/
+	{0x1718, 0x07},/*0x1718*/
+	{0x1719, 0x7d},/*0x1719*/
+};
+
+static struct msm_camera_i2c_reg_conf vx6953_recommend_settings[] = {
+	{0x0103, 0x01}, /* standby */
+	{0x0100, 0x00}, /* stop streaming */
+	/* patch cut 2*/
+	{0xFB94, 0},	/*intialise Data Xfer Status reg*/
+	{0xFB95, 0},	/*gain 1	  (0x00)*/
+	{0xFB96, 0},	/*gain 1.07   (0x10)*/
+	{0xFB97, 0},	/*gain 1.14   (0x20)*/
+	{0xFB98, 0},	/*gain 1.23   (0x30)*/
+	{0xFB99, 0},	/*gain 1.33   (0x40)*/
+	{0xFB9A, 0},	/*gain 1.45   (0x50)*/
+	{0xFB9B, 0},	/*gain 1.6    (0x60)*/
+	{0xFB9C, 0},	/*gain 1.78   (0x70)*/
+	{0xFB9D, 2},	/*gain 2	  (0x80)*/
+	{0xFB9E, 2},	/*gain 2.29   (0x90)*/
+	{0xFB9F, 3},	/*gain 2.67   (0xA0)*/
+	{0xFBA0, 3},	/*gain 3.2    (0xB0)*/
+	{0xFBA1, 4},	/*gain 4	  (0xC0)*/
+	{0xFBA2, 7},	/*gain 5.33   (0xD0)*/
+	{0xFBA3, 10},	/*gain 8	  (0xE0)*/
+	{0xFBA4, 11},	/*gain 9.14   (0xE4)*/
+	{0xFBA5, 13},	/*gain 10.67  (0xE8)*/
+	{0xFBA6, 15},	/*gain 12.8   (0xEC)*/
+	{0xFBA7, 19},	/*gain 16     (0xF0)*/
+	{0xF800, 0x12},
+	{0xF801, 0x06},
+	{0xF802, 0xf7},
+	{0xF803, 0x90},
+	{0xF804, 0x02},
+	{0xF805, 0x05},
+	{0xF806, 0xe0},
+	{0xF807, 0xff},
+	{0xF808, 0x65},
+	{0xF809, 0x7d},
+	{0xF80A, 0x70},
+	{0xF80B, 0x03},
+	{0xF80C, 0x02},
+	{0xF80D, 0xf9},
+	{0xF80E, 0x1c},
+	{0xF80F, 0x8f},
+	{0xF810, 0x7d},
+	{0xF811, 0xe4},
+	{0xF812, 0xf5},
+	{0xF813, 0x7a},
+	{0xF814, 0x75},
+	{0xF815, 0x78},
+	{0xF816, 0x30},
+	{0xF817, 0x75},
+	{0xF818, 0x79},
+	{0xF819, 0x53},
+	{0xF81A, 0x85},
+	{0xF81B, 0x79},
+	{0xF81C, 0x82},
+	{0xF81D, 0x85},
+	{0xF81E, 0x78},
+	{0xF81F, 0x83},
+	{0xF820, 0xe0},
+	{0xF821, 0xc3},
+	{0xF822, 0x95},
+	{0xF823, 0x7b},
+	{0xF824, 0xf0},
+	{0xF825, 0x74},
+	{0xF826, 0x02},
+	{0xF827, 0x25},
+	{0xF828, 0x79},
+	{0xF829, 0xf5},
+	{0xF82A, 0x79},
+	{0xF82B, 0xe4},
+	{0xF82C, 0x35},
+	{0xF82D, 0x78},
+	{0xF82E, 0xf5},
+	{0xF82F, 0x78},
+	{0xF830, 0x05},
+	{0xF831, 0x7a},
+	{0xF832, 0xe5},
+	{0xF833, 0x7a},
+	{0xF834, 0xb4},
+	{0xF835, 0x08},
+	{0xF836, 0xe3},
+	{0xF837, 0xe5},
+	{0xF838, 0x7d},
+	{0xF839, 0x70},
+	{0xF83A, 0x04},
+	{0xF83B, 0xff},
+	{0xF83C, 0x02},
+	{0xF83D, 0xf8},
+	{0xF83E, 0xe4},
+	{0xF83F, 0xe5},
+	{0xF840, 0x7d},
+	{0xF841, 0xb4},
+	{0xF842, 0x10},
+	{0xF843, 0x05},
+	{0xF844, 0x7f},
+	{0xF845, 0x01},
+	{0xF846, 0x02},
+	{0xF847, 0xf8},
+	{0xF848, 0xe4},
+	{0xF849, 0xe5},
+	{0xF84A, 0x7d},
+	{0xF84B, 0xb4},
+	{0xF84C, 0x20},
+	{0xF84D, 0x05},
+	{0xF84E, 0x7f},
+	{0xF84F, 0x02},
+	{0xF850, 0x02},
+	{0xF851, 0xf8},
+	{0xF852, 0xe4},
+	{0xF853, 0xe5},
+	{0xF854, 0x7d},
+	{0xF855, 0xb4},
+	{0xF856, 0x30},
+	{0xF857, 0x05},
+	{0xF858, 0x7f},
+	{0xF859, 0x03},
+	{0xF85A, 0x02},
+	{0xF85B, 0xf8},
+	{0xF85C, 0xe4},
+	{0xF85D, 0xe5},
+	{0xF85E, 0x7d},
+	{0xF85F, 0xb4},
+	{0xF860, 0x40},
+	{0xF861, 0x04},
+	{0xF862, 0x7f},
+	{0xF863, 0x04},
+	{0xF864, 0x80},
+	{0xF865, 0x7e},
+	{0xF866, 0xe5},
+	{0xF867, 0x7d},
+	{0xF868, 0xb4},
+	{0xF869, 0x50},
+	{0xF86A, 0x04},
+	{0xF86B, 0x7f},
+	{0xF86C, 0x05},
+	{0xF86D, 0x80},
+	{0xF86E, 0x75},
+	{0xF86F, 0xe5},
+	{0xF870, 0x7d},
+	{0xF871, 0xb4},
+	{0xF872, 0x60},
+	{0xF873, 0x04},
+	{0xF874, 0x7f},
+	{0xF875, 0x06},
+	{0xF876, 0x80},
+	{0xF877, 0x6c},
+	{0xF878, 0xe5},
+	{0xF879, 0x7d},
+	{0xF87A, 0xb4},
+	{0xF87B, 0x70},
+	{0xF87C, 0x04},
+	{0xF87D, 0x7f},
+	{0xF87E, 0x07},
+	{0xF87F, 0x80},
+	{0xF880, 0x63},
+	{0xF881, 0xe5},
+	{0xF882, 0x7d},
+	{0xF883, 0xb4},
+	{0xF884, 0x80},
+	{0xF885, 0x04},
+	{0xF886, 0x7f},
+	{0xF887, 0x08},
+	{0xF888, 0x80},
+	{0xF889, 0x5a},
+	{0xF88A, 0xe5},
+	{0xF88B, 0x7d},
+	{0xF88C, 0xb4},
+	{0xF88D, 0x90},
+	{0xF88E, 0x04},
+	{0xF88F, 0x7f},
+	{0xF890, 0x09},
+	{0xF891, 0x80},
+	{0xF892, 0x51},
+	{0xF893, 0xe5},
+	{0xF894, 0x7d},
+	{0xF895, 0xb4},
+	{0xF896, 0xa0},
+	{0xF897, 0x04},
+	{0xF898, 0x7f},
+	{0xF899, 0x0a},
+	{0xF89A, 0x80},
+	{0xF89B, 0x48},
+	{0xF89C, 0xe5},
+	{0xF89D, 0x7d},
+	{0xF89E, 0xb4},
+	{0xF89F, 0xb0},
+	{0xF8A0, 0x04},
+	{0xF8A1, 0x7f},
+	{0xF8A2, 0x0b},
+	{0xF8A3, 0x80},
+	{0xF8A4, 0x3f},
+	{0xF8A5, 0xe5},
+	{0xF8A6, 0x7d},
+	{0xF8A7, 0xb4},
+	{0xF8A8, 0xc0},
+	{0xF8A9, 0x04},
+	{0xF8AA, 0x7f},
+	{0xF8AB, 0x0c},
+	{0xF8AC, 0x80},
+	{0xF8AD, 0x36},
+	{0xF8AE, 0xe5},
+	{0xF8AF, 0x7d},
+	{0xF8B0, 0xb4},
+	{0xF8B1, 0xd0},
+	{0xF8B2, 0x04},
+	{0xF8B3, 0x7f},
+	{0xF8B4, 0x0d},
+	{0xF8B5, 0x80},
+	{0xF8B6, 0x2d},
+	{0xF8B7, 0xe5},
+	{0xF8B8, 0x7d},
+	{0xF8B9, 0xb4},
+	{0xF8BA, 0xe0},
+	{0xF8BB, 0x04},
+	{0xF8BC, 0x7f},
+	{0xF8BD, 0x0e},
+	{0xF8BE, 0x80},
+	{0xF8BF, 0x24},
+	{0xF8C0, 0xe5},
+	{0xF8C1, 0x7d},
+	{0xF8C2, 0xb4},
+	{0xF8C3, 0xe4},
+	{0xF8C4, 0x04},
+	{0xF8C5, 0x7f},
+	{0xF8C6, 0x0f},
+	{0xF8C7, 0x80},
+	{0xF8C8, 0x1b},
+	{0xF8C9, 0xe5},
+	{0xF8CA, 0x7d},
+	{0xF8CB, 0xb4},
+	{0xF8CC, 0xe8},
+	{0xF8CD, 0x04},
+	{0xF8CE, 0x7f},
+	{0xF8CF, 0x10},
+	{0xF8D0, 0x80},
+	{0xF8D1, 0x12},
+	{0xF8D2, 0xe5},
+	{0xF8D3, 0x7d},
+	{0xF8D4, 0xb4},
+	{0xF8D5, 0xec},
+	{0xF8D6, 0x04},
+	{0xF8D7, 0x7f},
+	{0xF8D8, 0x11},
+	{0xF8D9, 0x80},
+	{0xF8DA, 0x09},
+	{0xF8DB, 0xe5},
+	{0xF8DC, 0x7d},
+	{0xF8DD, 0x7f},
+	{0xF8DE, 0x00},
+	{0xF8DF, 0xb4},
+	{0xF8E0, 0xf0},
+	{0xF8E1, 0x02},
+	{0xF8E2, 0x7f},
+	{0xF8E3, 0x12},
+	{0xF8E4, 0x8f},
+	{0xF8E5, 0x7c},
+	{0xF8E6, 0xef},
+	{0xF8E7, 0x24},
+	{0xF8E8, 0x95},
+	{0xF8E9, 0xff},
+	{0xF8EA, 0xe4},
+	{0xF8EB, 0x34},
+	{0xF8EC, 0xfb},
+	{0xF8ED, 0x8f},
+	{0xF8EE, 0x82},
+	{0xF8EF, 0xf5},
+	{0xF8F0, 0x83},
+	{0xF8F1, 0xe4},
+	{0xF8F2, 0x93},
+	{0xF8F3, 0xf5},
+	{0xF8F4, 0x7c},
+	{0xF8F5, 0xf5},
+	{0xF8F6, 0x7b},
+	{0xF8F7, 0xe4},
+	{0xF8F8, 0xf5},
+	{0xF8F9, 0x7a},
+	{0xF8FA, 0x75},
+	{0xF8FB, 0x78},
+	{0xF8FC, 0x30},
+	{0xF8FD, 0x75},
+	{0xF8FE, 0x79},
+	{0xF8FF, 0x53},
+	{0xF900, 0x85},
+	{0xF901, 0x79},
+	{0xF902, 0x82},
+	{0xF903, 0x85},
+	{0xF904, 0x78},
+	{0xF905, 0x83},
+	{0xF906, 0xe0},
+	{0xF907, 0x25},
+	{0xF908, 0x7c},
+	{0xF909, 0xf0},
+	{0xF90A, 0x74},
+	{0xF90B, 0x02},
+	{0xF90C, 0x25},
+	{0xF90D, 0x79},
+	{0xF90E, 0xf5},
+	{0xF90F, 0x79},
+	{0xF910, 0xe4},
+	{0xF911, 0x35},
+	{0xF912, 0x78},
+	{0xF913, 0xf5},
+	{0xF914, 0x78},
+	{0xF915, 0x05},
+	{0xF916, 0x7a},
+	{0xF917, 0xe5},
+	{0xF918, 0x7a},
+	{0xF919, 0xb4},
+	{0xF91A, 0x08},
+	{0xF91B, 0xe4},
+	{0xF91C, 0x02},
+	{0xF91D, 0x18},
+	{0xF91E, 0x32},
+	{0xF91F, 0x22},
+	{0xF920, 0xf0},
+	{0xF921, 0x90},
+	{0xF922, 0xa0},
+	{0xF923, 0xf8},
+	{0xF924, 0xe0},
+	{0xF925, 0x70},
+	{0xF926, 0x02},
+	{0xF927, 0xa3},
+	{0xF928, 0xe0},
+	{0xF929, 0x70},
+	{0xF92A, 0x0a},
+	{0xF92B, 0x90},
+	{0xF92C, 0xa1},
+	{0xF92D, 0x10},
+	{0xF92E, 0xe0},
+	{0xF92F, 0xfe},
+	{0xF930, 0xa3},
+	{0xF931, 0xe0},
+	{0xF932, 0xff},
+	{0xF933, 0x80},
+	{0xF934, 0x04},
+	{0xF935, 0x7e},
+	{0xF936, 0x00},
+	{0xF937, 0x7f},
+	{0xF938, 0x00},
+	{0xF939, 0x8e},
+	{0xF93A, 0x7e},
+	{0xF93B, 0x8f},
+	{0xF93C, 0x7f},
+	{0xF93D, 0x90},
+	{0xF93E, 0x36},
+	{0xF93F, 0x0d},
+	{0xF940, 0xe0},
+	{0xF941, 0x44},
+	{0xF942, 0x02},
+	{0xF943, 0xf0},
+	{0xF944, 0x90},
+	{0xF945, 0x36},
+	{0xF946, 0x0e},
+	{0xF947, 0xe5},
+	{0xF948, 0x7e},
+	{0xF949, 0xf0},
+	{0xF94A, 0xa3},
+	{0xF94B, 0xe5},
+	{0xF94C, 0x7f},
+	{0xF94D, 0xf0},
+	{0xF94E, 0xe5},
+	{0xF94F, 0x3a},
+	{0xF950, 0x60},
+	{0xF951, 0x0c},
+	{0xF952, 0x90},
+	{0xF953, 0x36},
+	{0xF954, 0x09},
+	{0xF955, 0xe0},
+	{0xF956, 0x70},
+	{0xF957, 0x06},
+	{0xF958, 0x90},
+	{0xF959, 0x36},
+	{0xF95A, 0x08},
+	{0xF95B, 0xf0},
+	{0xF95C, 0xf5},
+	{0xF95D, 0x3a},
+	{0xF95E, 0x02},
+	{0xF95F, 0x03},
+	{0xF960, 0x94},
+	{0xF961, 0x22},
+	{0xF962, 0x78},
+	{0xF963, 0x07},
+	{0xF964, 0xe6},
+	{0xF965, 0xd3},
+	{0xF966, 0x94},
+	{0xF967, 0x00},
+	{0xF968, 0x40},
+	{0xF969, 0x16},
+	{0xF96A, 0x16},
+	{0xF96B, 0xe6},
+	{0xF96C, 0x90},
+	{0xF96D, 0x30},
+	{0xF96E, 0xa1},
+	{0xF96F, 0xf0},
+	{0xF970, 0x90},
+	{0xF971, 0x43},
+	{0xF972, 0x83},
+	{0xF973, 0xe0},
+	{0xF974, 0xb4},
+	{0xF975, 0x01},
+	{0xF976, 0x0f},
+	{0xF977, 0x90},
+	{0xF978, 0x43},
+	{0xF979, 0x87},
+	{0xF97A, 0xe0},
+	{0xF97B, 0xb4},
+	{0xF97C, 0x01},
+	{0xF97D, 0x08},
+	{0xF97E, 0x80},
+	{0xF97F, 0x00},
+	{0xF980, 0x90},
+	{0xF981, 0x30},
+	{0xF982, 0xa0},
+	{0xF983, 0x74},
+	{0xF984, 0x01},
+	{0xF985, 0xf0},
+	{0xF986, 0x22},
+	{0xF987, 0xf0},
+	{0xF988, 0x90},
+	{0xF989, 0x35},
+	{0xF98A, 0xba},
+	{0xF98B, 0xe0},
+	{0xF98C, 0xb4},
+	{0xF98D, 0x0a},
+	{0xF98E, 0x0d},
+	{0xF98F, 0xa3},
+	{0xF990, 0xe0},
+	{0xF991, 0xb4},
+	{0xF992, 0x01},
+	{0xF993, 0x08},
+	{0xF994, 0x90},
+	{0xF995, 0xfb},
+	{0xF996, 0x94},
+	{0xF997, 0xe0},
+	{0xF998, 0x90},
+	{0xF999, 0x35},
+	{0xF99A, 0xb8},
+	{0xF99B, 0xf0},
+	{0xF99C, 0xd0},
+	{0xF99D, 0xd0},
+	{0xF99E, 0xd0},
+	{0xF99F, 0x82},
+	{0xF9A0, 0xd0},
+	{0xF9A1, 0x83},
+	{0xF9A2, 0xd0},
+	{0xF9A3, 0xe0},
+	{0xF9A4, 0x32},
+	{0xF9A5, 0x22},
+	{0xF9A6, 0xe5},
+	{0xF9A7, 0x7f},
+	{0xF9A8, 0x45},
+	{0xF9A9, 0x7e},
+	{0xF9AA, 0x60},
+	{0xF9AB, 0x15},
+	{0xF9AC, 0x90},
+	{0xF9AD, 0x01},
+	{0xF9AE, 0x00},
+	{0xF9AF, 0xe0},
+	{0xF9B0, 0x70},
+	{0xF9B1, 0x0f},
+	{0xF9B2, 0x90},
+	{0xF9B3, 0xa0},
+	{0xF9B4, 0xf8},
+	{0xF9B5, 0xe5},
+	{0xF9B6, 0x7e},
+	{0xF9B7, 0xf0},
+	{0xF9B8, 0xa3},
+	{0xF9B9, 0xe5},
+	{0xF9BA, 0x7f},
+	{0xF9BB, 0xf0},
+	{0xF9BC, 0xe4},
+	{0xF9BD, 0xf5},
+	{0xF9BE, 0x7e},
+	{0xF9BF, 0xf5},
+	{0xF9C0, 0x7f},
+	{0xF9C1, 0x22},
+	{0xF9C2, 0x02},
+	{0xF9C3, 0x0e},
+	{0xF9C4, 0x79},
+	{0xF9C5, 0x22},
+	/* Offsets:*/
+	{0x35C6, 0x00},/* FIDDLEDARKCAL*/
+	{0x35C7, 0x00},
+	{0x35C8, 0x01},/*STOREDISTANCEATSTOPSTREAMING*/
+	{0x35C9, 0x20},
+	{0x35CA, 0x01},/*BRUCEFIX*/
+	{0x35CB, 0x62},
+	{0x35CC, 0x01},/*FIXDATAXFERSTATUSREG*/
+	{0x35CD, 0x87},
+	{0x35CE, 0x01},/*FOCUSDISTANCEUPDATE*/
+	{0x35CF, 0xA6},
+	{0x35D0, 0x01},/*SKIPEDOFRESET*/
+	{0x35D1, 0xC2},
+	{0x35D2, 0x00},
+	{0x35D3, 0xFB},
+	{0x35D4, 0x00},
+	{0x35D5, 0x94},
+	{0x35D6, 0x00},
+	{0x35D7, 0xFB},
+	{0x35D8, 0x00},
+	{0x35D9, 0x94},
+	{0x35DA, 0x00},
+	{0x35DB, 0xFB},
+	{0x35DC, 0x00},
+	{0x35DD, 0x94},
+	{0x35DE, 0x00},
+	{0x35DF, 0xFB},
+	{0x35E0, 0x00},
+	{0x35E1, 0x94},
+	{0x35E6, 0x18},/* FIDDLEDARKCAL*/
+	{0x35E7, 0x2F},
+	{0x35E8, 0x03},/* STOREDISTANCEATSTOPSTREAMING*/
+	{0x35E9, 0x93},
+	{0x35EA, 0x18},/* BRUCEFIX*/
+	{0x35EB, 0x99},
+	{0x35EC, 0x00},/* FIXDATAXFERSTATUSREG*/
+	{0x35ED, 0xA3},
+	{0x35EE, 0x21},/* FOCUSDISTANCEUPDATE*/
+	{0x35EF, 0x5B},
+	{0x35F0, 0x0E},/* SKIPEDOFRESET*/
+	{0x35F1, 0x74},
+	{0x35F2, 0x04},
+	{0x35F3, 0x64},
+	{0x35F4, 0x04},
+	{0x35F5, 0x65},
+	{0x35F6, 0x04},
+	{0x35F7, 0x7B},
+	{0x35F8, 0x04},
+	{0x35F9, 0x7C},
+	{0x35FA, 0x04},
+	{0x35FB, 0xDD},
+	{0x35FC, 0x04},
+	{0x35FD, 0xDE},
+	{0x35FE, 0x04},
+	{0x35FF, 0xEF},
+	{0x3600, 0x04},
+	{0x3601, 0xF0},
+	/*Jump/Data:*/
+	{0x35C2, 0x3F},/* Jump Reg*/
+	{0x35C3, 0xFF},/* Jump Reg*/
+	{0x35C4, 0x3F},/* Data Reg*/
+	{0x35C5, 0xC0},/* Data Reg*/
+	{0x35C0, 0x01},/* Enable*/
+	/* end of patch cut 2 */
+	/* common settings */
+	{0x0112, 10},/*REG = 0x0112 , 10 bit */
+	{0x0113, 10},/*REG = 0x0113*/
+	{0x0301, 9},/*REG = 0x0301 vt_pix_clk_div*/
+	{0x0305, 4},/*REG = 0x0305 pre_pll_clk_div*/
+	{0x0307, 133},/*REG = 0x0307 pll_multiplier*/
+	{0x0309, 10},/*REG = 0x0309 op_pix_clk_div*/
+	{0x3030, 0x08},/*REG = 0x3030*/
+	{0x0111, 0x02},/*REG = 0x0111*/
+	{0x0b00, 0x01},/*REG = 0x0b00 ,lens shading off */
+	{0x3001, 0x30},/*REG = 0x3001*/
+	{0x3004, 0x33},/*REG = 0x3004*/
+	{0x3007, 0x09},/*REG = 0x3007*/
+	{0x3016, 0x1F},/*REG = 0x3016*/
+	{0x301d, 0x03},/*REG = 0x301d*/
+	{0x317E, 0x11},/*REG = 0x317E*/
+	{0x317F, 0x09},/*REG = 0x317F*/
+	{0x3400, 0x38},/*REG = 0x3400*/
+	{0x0b06, 0x00},/*REG_0x0b06*/
+	{0x0b07, 0x80},/*REG_0x0b07*/
+	{0x0b08, 0x01},/*REG_0x0b08*/
+	{0x0b09, 0x4F},/*REG_0x0b09*/
+	{0x0136, 0x18},/*REG_0x0136*/
+	{0x0137, 0x00},/*/REG_0x0137*/
+	{0x0b83, 0x20},/*REG = 0x0b83*/
+	{0x0b84, 0x90},/*REG = 0x0b84*/
+	{0x0b85, 0x20},/*REG = 0x0b85*/
+	{0x0b88, 0x80},/*REG = 0x0b88*/
+	{0x0b89, 0x00},/*REG = 0x0b89*/
+	{0x0b8a, 0x00},/*REG = 0x0b8a*/
+	/* end of common settings */
+};
+
+static struct v4l2_subdev_info vx6953_subdev_info[] = {
+	{
+	.code   = V4L2_MBUS_FMT_SGRBG10_1X10,
+	.colorspace = V4L2_COLORSPACE_JPEG,
+	.fmt    = 1,
+	.order    = 0,
+	},
+	/* more can be supported, to be added later */
+};
+
+static struct msm_camera_i2c_conf_array vx6953_init_conf[] = {
+	{&vx6953_recommend_settings[0],
+	ARRAY_SIZE(vx6953_recommend_settings), 0, MSM_CAMERA_I2C_BYTE_DATA}
+};
+
+static struct msm_camera_i2c_conf_array vx6953_confs[] = {
+	{&vx6953_snap_settings[0],
+	ARRAY_SIZE(vx6953_snap_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+	{&vx6953_prev_settings[0],
+	ARRAY_SIZE(vx6953_prev_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+};
+
+static struct msm_sensor_output_info_t vx6953_dimensions[] = {
+	{
+		.x_output = 0xA30,
+		.y_output = 0x7A8,
+		.line_length_pclk = 0xB8C,
+		.frame_length_lines = 0x7D0,
+		.vt_pixel_clk = 88666666,
+		.op_pixel_clk = 192000000,
+		.binning_factor = 1,
+	},
+	{
+		.x_output = 0x518,
+		.y_output = 0x3D4,
+		.line_length_pclk = 0xB74,
+		.frame_length_lines = 0x3F0,
+		.vt_pixel_clk = 88666666,
+		.op_pixel_clk = 192000000,
+		.binning_factor = 1,
+	},
+};
+
+static struct msm_camera_csi_params vx6953_csi_params = {
+	.data_format = CSI_8BIT,
+	.lane_cnt    = 1,
+	.lane_assign = 0xe4,
+	.dpcm_scheme = 0,
+	.settle_cnt  = 7,
+};
+
+static struct msm_camera_csi_params *vx6953_csi_params_array[] = {
+	&vx6953_csi_params,
+	&vx6953_csi_params,
+};
+
+static struct msm_sensor_output_reg_addr_t vx6953_reg_addr = {
+	.x_output = 0x034C,
+	.y_output = 0x034E,
+	.line_length_pclk = 0x0342,
+	.frame_length_lines = 0x0340,
+};
+
+static struct msm_sensor_id_info_t vx6953_id_info = {
+	.sensor_id_reg_addr = 0x0000,
+	.sensor_id = 0x03B9,
+};
+
+static struct msm_sensor_exp_gain_info_t vx6953_exp_gain_info = {
+	.coarse_int_time_addr = 0x0202,
+	.global_gain_addr = 0x0204,
+	.vert_offset = 9,
+};
+
+static const struct i2c_device_id vx6953_i2c_id[] = {
+	{SENSOR_NAME, (kernel_ulong_t)&vx6953_s_ctrl},
+	{ }
+};
+
+static struct i2c_driver vx6953_i2c_driver = {
+	.id_table = vx6953_i2c_id,
+	.probe  = msm_sensor_i2c_probe,
+	.driver = {
+		.name = SENSOR_NAME,
+	},
+};
+
+static struct msm_camera_i2c_client vx6953_sensor_i2c_client = {
+	.addr_type = MSM_CAMERA_I2C_WORD_ADDR,
+};
+
+static int __init msm_sensor_init_module(void)
+{
+	return i2c_add_driver(&vx6953_i2c_driver);
+}
+
+static int32_t vx6953_set_fps(struct msm_sensor_ctrl_t *s_ctrl,
+	struct fps_cfg *fps) {
+	return 0;
+}
+
+int32_t vx6953_write_exp_gain(struct msm_sensor_ctrl_t *s_ctrl,
+	uint16_t gain, uint32_t line) {
+	return 0;
+}
+
+static struct v4l2_subdev_core_ops vx6953_subdev_core_ops = {
+	.ioctl = msm_sensor_subdev_ioctl,
+	.s_power = msm_sensor_power,
+};
+
+static struct v4l2_subdev_video_ops vx6953_subdev_video_ops = {
+	.enum_mbus_fmt = msm_sensor_v4l2_enum_fmt,
+};
+
+static struct v4l2_subdev_ops vx6953_subdev_ops = {
+	.core = &vx6953_subdev_core_ops,
+	.video  = &vx6953_subdev_video_ops,
+};
+
+static struct msm_camera_i2c_reg_conf vx6953_edof_estimation[] = {
+	{REG_0x0b80, 0x02},
+};
+
+static struct msm_camera_i2c_reg_conf vx6953_edof_application[] = {
+	{REG_0x0b80, 0x01},
+};
+
+static struct msm_camera_i2c_reg_conf vx6953_edof_default[] = {
+	{REG_0x0b80, 0x00},
+};
+
+static int vx6953_enable_edof(enum edof_mode_t edof_mode)
+{
+	int rc = 0;
+	if (edof_mode == VX6953_EDOF_ESTIMATION) {
+		/* EDof Estimation mode for preview */
+		msm_camera_i2c_write_tbl(
+			vx6953_s_ctrl.sensor_i2c_client,
+			vx6953_edof_estimation,
+			ARRAY_SIZE(vx6953_edof_estimation),
+			vx6953_s_ctrl.msm_sensor_reg->default_data_type);
+		CDBG("VX6953_EDOF_ESTIMATION");
+	} else if (edof_mode == VX6953_EDOF_APPLICATION) {
+		/* EDof Application mode for Capture */
+		msm_camera_i2c_write_tbl(
+			vx6953_s_ctrl.sensor_i2c_client,
+			vx6953_edof_application,
+			ARRAY_SIZE(vx6953_edof_application),
+			vx6953_s_ctrl.msm_sensor_reg->default_data_type);
+		CDBG("VX6953_EDOF_APPLICATION");
+	} else {
+		/* EDOF disabled */
+		msm_camera_i2c_write_tbl(
+			vx6953_s_ctrl.sensor_i2c_client,
+			vx6953_edof_default,
+			ARRAY_SIZE(vx6953_edof_default),
+			vx6953_s_ctrl.msm_sensor_reg->default_data_type);
+		CDBG("VX6953_EDOF_DISABLE");
+	}
+	return rc;
+}
+
+static struct msm_camera_i2c_reg_conf vx6953_standby[] = {
+	{0x103, 0x01},
+};
+
+static struct msm_camera_i2c_reg_conf patch_tbl_cut2[] = {
+	{0xFB94, 0},	/*intialise Data Xfer Status reg*/
+	{0xFB95, 0},	/*gain 1	  (0x00)*/
+	{0xFB96, 0},	/*gain 1.07   (0x10)*/
+	{0xFB97, 0},	/*gain 1.14   (0x20)*/
+	{0xFB98, 0},	/*gain 1.23   (0x30)*/
+	{0xFB99, 0},	/*gain 1.33   (0x40)*/
+	{0xFB9A, 0},	/*gain 1.45   (0x50)*/
+	{0xFB9B, 0},	/*gain 1.6    (0x60)*/
+	{0xFB9C, 0},	/*gain 1.78   (0x70)*/
+	{0xFB9D, 2},	/*gain 2	  (0x80)*/
+	{0xFB9E, 2},	/*gain 2.29   (0x90)*/
+	{0xFB9F, 3},	/*gain 2.67   (0xA0)*/
+	{0xFBA0, 3},	/*gain 3.2    (0xB0)*/
+	{0xFBA1, 4},	/*gain 4	  (0xC0)*/
+	{0xFBA2, 7},	/*gain 5.33   (0xD0)*/
+	{0xFBA3, 10},	/*gain 8	  (0xE0)*/
+	{0xFBA4, 11},	/*gain 9.14   (0xE4)*/
+	{0xFBA5, 13},	/*gain 10.67  (0xE8)*/
+	{0xFBA6, 15},	/*gain 12.8   (0xEC)*/
+	{0xFBA7, 19},	/*gain 16     (0xF0)*/
+	{0xF800, 0x12},
+	{0xF801, 0x06},
+	{0xF802, 0xf7},
+	{0xF803, 0x90},
+	{0xF804, 0x02},
+	{0xF805, 0x05},
+	{0xF806, 0xe0},
+	{0xF807, 0xff},
+	{0xF808, 0x65},
+	{0xF809, 0x7d},
+	{0xF80A, 0x70},
+	{0xF80B, 0x03},
+	{0xF80C, 0x02},
+	{0xF80D, 0xf9},
+	{0xF80E, 0x1c},
+	{0xF80F, 0x8f},
+	{0xF810, 0x7d},
+	{0xF811, 0xe4},
+	{0xF812, 0xf5},
+	{0xF813, 0x7a},
+	{0xF814, 0x75},
+	{0xF815, 0x78},
+	{0xF816, 0x30},
+	{0xF817, 0x75},
+	{0xF818, 0x79},
+	{0xF819, 0x53},
+	{0xF81A, 0x85},
+	{0xF81B, 0x79},
+	{0xF81C, 0x82},
+	{0xF81D, 0x85},
+	{0xF81E, 0x78},
+	{0xF81F, 0x83},
+	{0xF820, 0xe0},
+	{0xF821, 0xc3},
+	{0xF822, 0x95},
+	{0xF823, 0x7b},
+	{0xF824, 0xf0},
+	{0xF825, 0x74},
+	{0xF826, 0x02},
+	{0xF827, 0x25},
+	{0xF828, 0x79},
+	{0xF829, 0xf5},
+	{0xF82A, 0x79},
+	{0xF82B, 0xe4},
+	{0xF82C, 0x35},
+	{0xF82D, 0x78},
+	{0xF82E, 0xf5},
+	{0xF82F, 0x78},
+	{0xF830, 0x05},
+	{0xF831, 0x7a},
+	{0xF832, 0xe5},
+	{0xF833, 0x7a},
+	{0xF834, 0xb4},
+	{0xF835, 0x08},
+	{0xF836, 0xe3},
+	{0xF837, 0xe5},
+	{0xF838, 0x7d},
+	{0xF839, 0x70},
+	{0xF83A, 0x04},
+	{0xF83B, 0xff},
+	{0xF83C, 0x02},
+	{0xF83D, 0xf8},
+	{0xF83E, 0xe4},
+	{0xF83F, 0xe5},
+	{0xF840, 0x7d},
+	{0xF841, 0xb4},
+	{0xF842, 0x10},
+	{0xF843, 0x05},
+	{0xF844, 0x7f},
+	{0xF845, 0x01},
+	{0xF846, 0x02},
+	{0xF847, 0xf8},
+	{0xF848, 0xe4},
+	{0xF849, 0xe5},
+	{0xF84A, 0x7d},
+	{0xF84B, 0xb4},
+	{0xF84C, 0x20},
+	{0xF84D, 0x05},
+	{0xF84E, 0x7f},
+	{0xF84F, 0x02},
+	{0xF850, 0x02},
+	{0xF851, 0xf8},
+	{0xF852, 0xe4},
+	{0xF853, 0xe5},
+	{0xF854, 0x7d},
+	{0xF855, 0xb4},
+	{0xF856, 0x30},
+	{0xF857, 0x05},
+	{0xF858, 0x7f},
+	{0xF859, 0x03},
+	{0xF85A, 0x02},
+	{0xF85B, 0xf8},
+	{0xF85C, 0xe4},
+	{0xF85D, 0xe5},
+	{0xF85E, 0x7d},
+	{0xF85F, 0xb4},
+	{0xF860, 0x40},
+	{0xF861, 0x04},
+	{0xF862, 0x7f},
+	{0xF863, 0x04},
+	{0xF864, 0x80},
+	{0xF865, 0x7e},
+	{0xF866, 0xe5},
+	{0xF867, 0x7d},
+	{0xF868, 0xb4},
+	{0xF869, 0x50},
+	{0xF86A, 0x04},
+	{0xF86B, 0x7f},
+	{0xF86C, 0x05},
+	{0xF86D, 0x80},
+	{0xF86E, 0x75},
+	{0xF86F, 0xe5},
+	{0xF870, 0x7d},
+	{0xF871, 0xb4},
+	{0xF872, 0x60},
+	{0xF873, 0x04},
+	{0xF874, 0x7f},
+	{0xF875, 0x06},
+	{0xF876, 0x80},
+	{0xF877, 0x6c},
+	{0xF878, 0xe5},
+	{0xF879, 0x7d},
+	{0xF87A, 0xb4},
+	{0xF87B, 0x70},
+	{0xF87C, 0x04},
+	{0xF87D, 0x7f},
+	{0xF87E, 0x07},
+	{0xF87F, 0x80},
+	{0xF880, 0x63},
+	{0xF881, 0xe5},
+	{0xF882, 0x7d},
+	{0xF883, 0xb4},
+	{0xF884, 0x80},
+	{0xF885, 0x04},
+	{0xF886, 0x7f},
+	{0xF887, 0x08},
+	{0xF888, 0x80},
+	{0xF889, 0x5a},
+	{0xF88A, 0xe5},
+	{0xF88B, 0x7d},
+	{0xF88C, 0xb4},
+	{0xF88D, 0x90},
+	{0xF88E, 0x04},
+	{0xF88F, 0x7f},
+	{0xF890, 0x09},
+	{0xF891, 0x80},
+	{0xF892, 0x51},
+	{0xF893, 0xe5},
+	{0xF894, 0x7d},
+	{0xF895, 0xb4},
+	{0xF896, 0xa0},
+	{0xF897, 0x04},
+	{0xF898, 0x7f},
+	{0xF899, 0x0a},
+	{0xF89A, 0x80},
+	{0xF89B, 0x48},
+	{0xF89C, 0xe5},
+	{0xF89D, 0x7d},
+	{0xF89E, 0xb4},
+	{0xF89F, 0xb0},
+	{0xF8A0, 0x04},
+	{0xF8A1, 0x7f},
+	{0xF8A2, 0x0b},
+	{0xF8A3, 0x80},
+	{0xF8A4, 0x3f},
+	{0xF8A5, 0xe5},
+	{0xF8A6, 0x7d},
+	{0xF8A7, 0xb4},
+	{0xF8A8, 0xc0},
+	{0xF8A9, 0x04},
+	{0xF8AA, 0x7f},
+	{0xF8AB, 0x0c},
+	{0xF8AC, 0x80},
+	{0xF8AD, 0x36},
+	{0xF8AE, 0xe5},
+	{0xF8AF, 0x7d},
+	{0xF8B0, 0xb4},
+	{0xF8B1, 0xd0},
+	{0xF8B2, 0x04},
+	{0xF8B3, 0x7f},
+	{0xF8B4, 0x0d},
+	{0xF8B5, 0x80},
+	{0xF8B6, 0x2d},
+	{0xF8B7, 0xe5},
+	{0xF8B8, 0x7d},
+	{0xF8B9, 0xb4},
+	{0xF8BA, 0xe0},
+	{0xF8BB, 0x04},
+	{0xF8BC, 0x7f},
+	{0xF8BD, 0x0e},
+	{0xF8BE, 0x80},
+	{0xF8BF, 0x24},
+	{0xF8C0, 0xe5},
+	{0xF8C1, 0x7d},
+	{0xF8C2, 0xb4},
+	{0xF8C3, 0xe4},
+	{0xF8C4, 0x04},
+	{0xF8C5, 0x7f},
+	{0xF8C6, 0x0f},
+	{0xF8C7, 0x80},
+	{0xF8C8, 0x1b},
+	{0xF8C9, 0xe5},
+	{0xF8CA, 0x7d},
+	{0xF8CB, 0xb4},
+	{0xF8CC, 0xe8},
+	{0xF8CD, 0x04},
+	{0xF8CE, 0x7f},
+	{0xF8CF, 0x10},
+	{0xF8D0, 0x80},
+	{0xF8D1, 0x12},
+	{0xF8D2, 0xe5},
+	{0xF8D3, 0x7d},
+	{0xF8D4, 0xb4},
+	{0xF8D5, 0xec},
+	{0xF8D6, 0x04},
+	{0xF8D7, 0x7f},
+	{0xF8D8, 0x11},
+	{0xF8D9, 0x80},
+	{0xF8DA, 0x09},
+	{0xF8DB, 0xe5},
+	{0xF8DC, 0x7d},
+	{0xF8DD, 0x7f},
+	{0xF8DE, 0x00},
+	{0xF8DF, 0xb4},
+	{0xF8E0, 0xf0},
+	{0xF8E1, 0x02},
+	{0xF8E2, 0x7f},
+	{0xF8E3, 0x12},
+	{0xF8E4, 0x8f},
+	{0xF8E5, 0x7c},
+	{0xF8E6, 0xef},
+	{0xF8E7, 0x24},
+	{0xF8E8, 0x95},
+	{0xF8E9, 0xff},
+	{0xF8EA, 0xe4},
+	{0xF8EB, 0x34},
+	{0xF8EC, 0xfb},
+	{0xF8ED, 0x8f},
+	{0xF8EE, 0x82},
+	{0xF8EF, 0xf5},
+	{0xF8F0, 0x83},
+	{0xF8F1, 0xe4},
+	{0xF8F2, 0x93},
+	{0xF8F3, 0xf5},
+	{0xF8F4, 0x7c},
+	{0xF8F5, 0xf5},
+	{0xF8F6, 0x7b},
+	{0xF8F7, 0xe4},
+	{0xF8F8, 0xf5},
+	{0xF8F9, 0x7a},
+	{0xF8FA, 0x75},
+	{0xF8FB, 0x78},
+	{0xF8FC, 0x30},
+	{0xF8FD, 0x75},
+	{0xF8FE, 0x79},
+	{0xF8FF, 0x53},
+	{0xF900, 0x85},
+	{0xF901, 0x79},
+	{0xF902, 0x82},
+	{0xF903, 0x85},
+	{0xF904, 0x78},
+	{0xF905, 0x83},
+	{0xF906, 0xe0},
+	{0xF907, 0x25},
+	{0xF908, 0x7c},
+	{0xF909, 0xf0},
+	{0xF90A, 0x74},
+	{0xF90B, 0x02},
+	{0xF90C, 0x25},
+	{0xF90D, 0x79},
+	{0xF90E, 0xf5},
+	{0xF90F, 0x79},
+	{0xF910, 0xe4},
+	{0xF911, 0x35},
+	{0xF912, 0x78},
+	{0xF913, 0xf5},
+	{0xF914, 0x78},
+	{0xF915, 0x05},
+	{0xF916, 0x7a},
+	{0xF917, 0xe5},
+	{0xF918, 0x7a},
+	{0xF919, 0xb4},
+	{0xF91A, 0x08},
+	{0xF91B, 0xe4},
+	{0xF91C, 0x02},
+	{0xF91D, 0x18},
+	{0xF91E, 0x32},
+	{0xF91F, 0x22},
+	{0xF920, 0xf0},
+	{0xF921, 0x90},
+	{0xF922, 0xa0},
+	{0xF923, 0xf8},
+	{0xF924, 0xe0},
+	{0xF925, 0x70},
+	{0xF926, 0x02},
+	{0xF927, 0xa3},
+	{0xF928, 0xe0},
+	{0xF929, 0x70},
+	{0xF92A, 0x0a},
+	{0xF92B, 0x90},
+	{0xF92C, 0xa1},
+	{0xF92D, 0x10},
+	{0xF92E, 0xe0},
+	{0xF92F, 0xfe},
+	{0xF930, 0xa3},
+	{0xF931, 0xe0},
+	{0xF932, 0xff},
+	{0xF933, 0x80},
+	{0xF934, 0x04},
+	{0xF935, 0x7e},
+	{0xF936, 0x00},
+	{0xF937, 0x7f},
+	{0xF938, 0x00},
+	{0xF939, 0x8e},
+	{0xF93A, 0x7e},
+	{0xF93B, 0x8f},
+	{0xF93C, 0x7f},
+	{0xF93D, 0x90},
+	{0xF93E, 0x36},
+	{0xF93F, 0x0d},
+	{0xF940, 0xe0},
+	{0xF941, 0x44},
+	{0xF942, 0x02},
+	{0xF943, 0xf0},
+	{0xF944, 0x90},
+	{0xF945, 0x36},
+	{0xF946, 0x0e},
+	{0xF947, 0xe5},
+	{0xF948, 0x7e},
+	{0xF949, 0xf0},
+	{0xF94A, 0xa3},
+	{0xF94B, 0xe5},
+	{0xF94C, 0x7f},
+	{0xF94D, 0xf0},
+	{0xF94E, 0xe5},
+	{0xF94F, 0x3a},
+	{0xF950, 0x60},
+	{0xF951, 0x0c},
+	{0xF952, 0x90},
+	{0xF953, 0x36},
+	{0xF954, 0x09},
+	{0xF955, 0xe0},
+	{0xF956, 0x70},
+	{0xF957, 0x06},
+	{0xF958, 0x90},
+	{0xF959, 0x36},
+	{0xF95A, 0x08},
+	{0xF95B, 0xf0},
+	{0xF95C, 0xf5},
+	{0xF95D, 0x3a},
+	{0xF95E, 0x02},
+	{0xF95F, 0x03},
+	{0xF960, 0x94},
+	{0xF961, 0x22},
+	{0xF962, 0x78},
+	{0xF963, 0x07},
+	{0xF964, 0xe6},
+	{0xF965, 0xd3},
+	{0xF966, 0x94},
+	{0xF967, 0x00},
+	{0xF968, 0x40},
+	{0xF969, 0x16},
+	{0xF96A, 0x16},
+	{0xF96B, 0xe6},
+	{0xF96C, 0x90},
+	{0xF96D, 0x30},
+	{0xF96E, 0xa1},
+	{0xF96F, 0xf0},
+	{0xF970, 0x90},
+	{0xF971, 0x43},
+	{0xF972, 0x83},
+	{0xF973, 0xe0},
+	{0xF974, 0xb4},
+	{0xF975, 0x01},
+	{0xF976, 0x0f},
+	{0xF977, 0x90},
+	{0xF978, 0x43},
+	{0xF979, 0x87},
+	{0xF97A, 0xe0},
+	{0xF97B, 0xb4},
+	{0xF97C, 0x01},
+	{0xF97D, 0x08},
+	{0xF97E, 0x80},
+	{0xF97F, 0x00},
+	{0xF980, 0x90},
+	{0xF981, 0x30},
+	{0xF982, 0xa0},
+	{0xF983, 0x74},
+	{0xF984, 0x01},
+	{0xF985, 0xf0},
+	{0xF986, 0x22},
+	{0xF987, 0xf0},
+	{0xF988, 0x90},
+	{0xF989, 0x35},
+	{0xF98A, 0xba},
+	{0xF98B, 0xe0},
+	{0xF98C, 0xb4},
+	{0xF98D, 0x0a},
+	{0xF98E, 0x0d},
+	{0xF98F, 0xa3},
+	{0xF990, 0xe0},
+	{0xF991, 0xb4},
+	{0xF992, 0x01},
+	{0xF993, 0x08},
+	{0xF994, 0x90},
+	{0xF995, 0xfb},
+	{0xF996, 0x94},
+	{0xF997, 0xe0},
+	{0xF998, 0x90},
+	{0xF999, 0x35},
+	{0xF99A, 0xb8},
+	{0xF99B, 0xf0},
+	{0xF99C, 0xd0},
+	{0xF99D, 0xd0},
+	{0xF99E, 0xd0},
+	{0xF99F, 0x82},
+	{0xF9A0, 0xd0},
+	{0xF9A1, 0x83},
+	{0xF9A2, 0xd0},
+	{0xF9A3, 0xe0},
+	{0xF9A4, 0x32},
+	{0xF9A5, 0x22},
+	{0xF9A6, 0xe5},
+	{0xF9A7, 0x7f},
+	{0xF9A8, 0x45},
+	{0xF9A9, 0x7e},
+	{0xF9AA, 0x60},
+	{0xF9AB, 0x15},
+	{0xF9AC, 0x90},
+	{0xF9AD, 0x01},
+	{0xF9AE, 0x00},
+	{0xF9AF, 0xe0},
+	{0xF9B0, 0x70},
+	{0xF9B1, 0x0f},
+	{0xF9B2, 0x90},
+	{0xF9B3, 0xa0},
+	{0xF9B4, 0xf8},
+	{0xF9B5, 0xe5},
+	{0xF9B6, 0x7e},
+	{0xF9B7, 0xf0},
+	{0xF9B8, 0xa3},
+	{0xF9B9, 0xe5},
+	{0xF9BA, 0x7f},
+	{0xF9BB, 0xf0},
+	{0xF9BC, 0xe4},
+	{0xF9BD, 0xf5},
+	{0xF9BE, 0x7e},
+	{0xF9BF, 0xf5},
+	{0xF9C0, 0x7f},
+	{0xF9C1, 0x22},
+	{0xF9C2, 0x02},
+	{0xF9C3, 0x0e},
+	{0xF9C4, 0x79},
+	{0xF9C5, 0x22},
+	/* Offsets:*/
+	{0x35C6, 0x00},/* FIDDLEDARKCAL*/
+	{0x35C7, 0x00},
+	{0x35C8, 0x01},/*STOREDISTANCEATSTOPSTREAMING*/
+	{0x35C9, 0x20},
+	{0x35CA, 0x01},/*BRUCEFIX*/
+	{0x35CB, 0x62},
+	{0x35CC, 0x01},/*FIXDATAXFERSTATUSREG*/
+	{0x35CD, 0x87},
+	{0x35CE, 0x01},/*FOCUSDISTANCEUPDATE*/
+	{0x35CF, 0xA6},
+	{0x35D0, 0x01},/*SKIPEDOFRESET*/
+	{0x35D1, 0xC2},
+	{0x35D2, 0x00},
+	{0x35D3, 0xFB},
+	{0x35D4, 0x00},
+	{0x35D5, 0x94},
+	{0x35D6, 0x00},
+	{0x35D7, 0xFB},
+	{0x35D8, 0x00},
+	{0x35D9, 0x94},
+	{0x35DA, 0x00},
+	{0x35DB, 0xFB},
+	{0x35DC, 0x00},
+	{0x35DD, 0x94},
+	{0x35DE, 0x00},
+	{0x35DF, 0xFB},
+	{0x35E0, 0x00},
+	{0x35E1, 0x94},
+	{0x35E6, 0x18},/* FIDDLEDARKCAL*/
+	{0x35E7, 0x2F},
+	{0x35E8, 0x03},/* STOREDISTANCEATSTOPSTREAMING*/
+	{0x35E9, 0x93},
+	{0x35EA, 0x18},/* BRUCEFIX*/
+	{0x35EB, 0x99},
+	{0x35EC, 0x00},/* FIXDATAXFERSTATUSREG*/
+	{0x35ED, 0xA3},
+	{0x35EE, 0x21},/* FOCUSDISTANCEUPDATE*/
+	{0x35EF, 0x5B},
+	{0x35F0, 0x0E},/* SKIPEDOFRESET*/
+	{0x35F1, 0x74},
+	{0x35F2, 0x04},
+	{0x35F3, 0x64},
+	{0x35F4, 0x04},
+	{0x35F5, 0x65},
+	{0x35F6, 0x04},
+	{0x35F7, 0x7B},
+	{0x35F8, 0x04},
+	{0x35F9, 0x7C},
+	{0x35FA, 0x04},
+	{0x35FB, 0xDD},
+	{0x35FC, 0x04},
+	{0x35FD, 0xDE},
+	{0x35FE, 0x04},
+	{0x35FF, 0xEF},
+	{0x3600, 0x04},
+	{0x3601, 0xF0},
+	/*Jump/Data:*/
+	{0x35C2, 0x3F},/* Jump Reg*/
+	{0x35C3, 0xFF},/* Jump Reg*/
+	{0x35C4, 0x3F},/* Data Reg*/
+	{0x35C5, 0xC0},/* Data Reg*/
+	{0x35C0, 0x01},/* Enable*/
+};
+struct msm_camera_i2c_reg_conf init_tbl[] = {
+	{0x0112, 10},
+	{0x0113, 10},
+	{0x0301, 9},
+	{0x0305, 4},
+	{0x0307, 133},
+	{0x0309, 10},
+	{0x0202, 0x03},
+	{0x0203, 0xd0},
+	{0x0205, 0xc0},
+	{0x3030, 0x08},
+	{0x0111, 0x02},
+	{0x0b00, 0x01},
+	{0x3001, 0x30},
+	{0x3004, 0x33},
+	{0x3007, 0x09},
+	{0x3016, 0x1F},
+	{0x301d, 0x03},
+	{0x317e, 0x11},
+	{0x317f, 0x09},
+	{0x3400, 0x38},
+	{0x0b06, 0x00},
+	{0x0b07, 0x80},
+	{0x0b08, 0x01},
+	{0x0b09, 0x4F},
+	{0x0136, 0x18},
+	{0x0137, 0x00},
+	{0x0b83, 0x20},
+	{0x0b84, 0x90},
+	{0x0b85, 0x20},
+	{0x0b88, 0x80},
+	{0x0b89, 0x00},
+	{0x0b8a, 0x00},
+	{0x0340, 0x03},   /*REG = 0x0340 frame_length_lines_hi*/
+	{0x0341, 0xf0},   /*REG = 0x0341 frame_length_lines_lo*/
+	{0x0342, 0x0b},   /*REG = 0x0342  line_length_pck_hi*/
+	{0x0343, 0x74},   /*REG = 0x0343  line_length_pck_lo*/
+	{0x3005, 0x03},   /*REG = 0x3005*/
+	{0x3010, 0x00},   /*REG = 0x3010*/
+	{0x3011, 0x01},   /*REG = 0x3011*/
+	{0x301a, 0x6a},   /*REG = 0x301a*/
+	{0x3035, 0x03},   /*REG = 0x3035*/
+	{0x3036, 0x2c},   /*REG = 0x3036*/
+	{0x3041, 0x00},   /*REG = 0x3041*/
+	{0x3042, 0x24},   /*REG = 0x3042*/
+	{0x3045, 0x81},   /*REG = 0x3045*/
+	{0x0b80, 0x02},   /*REG = 0x0b80 edof estimate*/
+	{0x0900, 0x01},   /*REG = 0x0900*/
+	{0x0901, 0x22},   /*REG = 0x0901*/
+	{0x0902, 0x04},   /*REG = 0x0902*/
+	{0x0383, 0x03},   /*REG = 0x0383*/
+	{0x0387, 0x03},   /*REG = 0x0387*/
+	{0x034c, 0x05},   /*REG = 0x034c*/
+	{0x034d, 0x18},   /*REG = 0x034d*/
+	{0x034e, 0x03},   /*REG = 0x034e*/
+	{0x034f, 0xd4},   /*REG = 0x034f*/
+	{0x1716, 0x02},   /*0x1716*/
+	{0x1717, 0x04},   /*0x1717*/
+	{0x1718, 0x08},   /*0x1718*/
+	{0x1719, 0x2c},   /*0x1719*/
+};
+
+struct msm_camera_i2c_reg_conf mode_tbl1[] = {
+	{REG_0x0112, 10},/*REG = 0x0112 , 10 bit */
+	{REG_0x0113, 10},/*REG = 0x0113*/
+	{REG_VT_PIX_CLK_DIV, 9},/*REG = 0x0301 vt_pix_clk_div*/
+	{REG_PRE_PLL_CLK_DIV, 4},/*REG = 0x0305 pre_pll_clk_div*/
+	{REG_PLL_MULTIPLIER, 133},/*REG = 0x0307 pll_multiplier*/
+	{REG_OP_PIX_CLK_DIV, 10},/*REG = 0x0309 op_pix_clk_div*/
+	{REG_FRAME_LENGTH_LINES_HI, 0x03},/*REG = 0x0340 frame_length_lines_hi*/
+	{REG_FRAME_LENGTH_LINES_LO, 0xf0},/*REG = 0x0341 frame_length_lines_lo*/
+	{REG_LINE_LENGTH_PCK_HI, 0x0b},   /*REG = 0x0342  line_length_pck_hi*/
+	{REG_LINE_LENGTH_PCK_LO, 0x74},   /*REG = 0x0343  line_length_pck_lo*/
+	{REG_0x3005, 0x03},   /*REG = 0x3005*/
+	{0x3010, 0x00},   /*REG = 0x3010*/
+	{REG_0x3011, 0x01},   /*REG = 0x3011*/
+	{REG_0x301a, 0x6a},   /*REG = 0x301a*/
+	{REG_0x3035, 0x03},   /*REG = 0x3035*/
+	{REG_0x3036, 0x2c},   /*REG = 0x3036*/
+	{REG_0x3041, 0x00},   /*REG = 0x3041*/
+	{0x3042, 0x24},   /*REG = 0x3042*/
+	{REG_0x3045, 0x81},   /*REG = 0x3045*/
+	{REG_0x0b80, 0x02},   /*REG = 0x0b80 edof estimate*/
+	{REG_0x0900, 0x01},   /*REG = 0x0900*/
+	{REG_0x0901, 0x22},   /*REG = 0x0901*/
+	{REG_0x0902, 0x04},   /*REG = 0x0902*/
+	{REG_0x0383, 0x03},   /*REG = 0x0383*/
+	{REG_0x0387, 0x03},   /*REG = 0x0387*/
+	{REG_0x034c, 0x05},   /*REG = 0x034c*/
+	{REG_0x034d, 0x18},   /*REG = 0x034d*/
+	{REG_0x034e, 0x03},   /*REG = 0x034e*/
+	{REG_0x034f, 0xd4},   /*REG = 0x034f*/
+	{REG_0x1716, 0x02},   /*0x1716*/
+	{REG_0x1717, 0x04},   /*0x1717*/
+	{REG_0x1718, 0x08},   /*0x1718*/
+	{REG_0x1719, 0x2c},   /*0x1719*/
+};
+
+struct msm_camera_i2c_reg_conf mode_tbl2[] = {
+	{REG_0x0112, 10},/*REG = 0x0112 , 10 bit */
+	{REG_0x0113, 10},/*REG = 0x0113*/
+	{REG_VT_PIX_CLK_DIV, 9},/*REG = 0x0301 vt_pix_clk_div*/
+	{REG_PRE_PLL_CLK_DIV, 4},/*REG = 0x0305 pre_pll_clk_div*/
+	{REG_PLL_MULTIPLIER, 133},/*REG = 0x0307 pll_multiplier*/
+	{REG_OP_PIX_CLK_DIV, 10},/*REG = 0x0309 op_pix_clk_div*/
+	{REG_FRAME_LENGTH_LINES_HI, 0x07},/*REG = 0x0340 frame_length_lines_hi*/
+	{REG_FRAME_LENGTH_LINES_LO, 0xd0},/*REG = 0x0341 frame_length_lines_lo*/
+	{REG_LINE_LENGTH_PCK_HI, 0x0b},/*REG = 0x0342 line_length_pck_hi*/
+	{REG_LINE_LENGTH_PCK_LO, 0x8c},/*REG = 0x0343 line_length_pck_lo*/
+	{REG_0x3005, 0x01},/*REG = 0x3005*/
+	{0x3010, 0x00},/*REG = 0x3010*/
+	{REG_0x3011, 0x00},/*REG = 0x3011*/
+	{REG_0x301a, 0x55},/*REG = 0x301a*/
+	{REG_0x3035, 0x01},/*REG = 0x3035*/
+	{REG_0x3036, 0x23},/*REG = 0x3036*/
+	{REG_0x3041, 0x00},/*REG = 0x3041*/
+	{0x3042, 0x24},/*REG = 0x3042*/
+	{REG_0x3045, 0xb7},/*REG = 0x3045*/
+	{REG_0x0b80, 0x01},/*REG = 0x0b80 edof application*/
+	{REG_0x0900, 0x00},/*REG = 0x0900*/
+	{REG_0x0901, 0x00},/*REG = 0x0901*/
+	{REG_0x0902, 0x00},/*REG = 0x0902*/
+	{REG_0x0383, 0x01},/*REG = 0x0383*/
+	{REG_0x0387, 0x01},/*REG = 0x0387*/
+	{REG_0x034c, 0x0A},/*REG = 0x034c*/
+	{REG_0x034d, 0x30},/*REG = 0x034d*/
+	{REG_0x034e, 0x07},/*REG = 0x034e*/
+	{REG_0x034f, 0xA8},/*REG = 0x034f*/
+	{REG_0x1716, 0x02},/*0x1716*/
+	{REG_0x1717, 0x0d},/*0x1717*/
+	{REG_0x1718, 0x07},/*0x1718*/
+	{REG_0x1719, 0x7d},/*0x1719*/
+};
+
+static int32_t vx6953_sensor_setting(int update_type, int rt)
+{
+
+	int32_t rc = 0;
+	uint16_t frame_cnt = 0;
+		CDBG("%s update type = %d, rt = %d\n",
+			__func__, update_type, rt);
+
+		switch (update_type) {
+		case REG_INIT:
+		if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+						/* reset fps_divider */
+			fps = 30 * Q8;
+			/* stop streaming */
+
+			/* Reset everything first */
+			msm_camera_i2c_write_tbl(
+				vx6953_s_ctrl.sensor_i2c_client,
+				vx6953_standby,
+				ARRAY_SIZE(vx6953_standby),
+				vx6953_s_ctrl.msm_sensor_reg->
+				default_data_type);
+
+			msleep(20);
+
+			CDBG("Init vx6953_sensor_setting standby\n");
+			msm_camera_i2c_write_tbl(
+				vx6953_s_ctrl.sensor_i2c_client,
+				vx6953_stop_settings,
+				ARRAY_SIZE(vx6953_stop_settings),
+				vx6953_s_ctrl.msm_sensor_reg->
+				default_data_type);
+				/*vx6953_stm5m0edof_delay_msecs_stdby*/
+			msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+			msm_camera_i2c_write_tbl(
+				vx6953_s_ctrl.sensor_i2c_client,
+				patch_tbl_cut2,
+				ARRAY_SIZE(patch_tbl_cut2),
+				vx6953_s_ctrl.msm_sensor_reg->
+				default_data_type);
+			msm_camera_i2c_write_tbl(
+				vx6953_s_ctrl.sensor_i2c_client,
+				init_tbl,
+				ARRAY_SIZE(init_tbl),
+				vx6953_s_ctrl.msm_sensor_reg->
+				default_data_type);
+			msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+		}
+		return rc;
+		case UPDATE_PERIODIC:
+		if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+			struct msm_camera_i2c_reg_conf init_mode_tbl[] =  {
+			{REG_0x0112,
+				vx6953_regs.reg_pat_init[0].reg_0x0112},
+			{REG_0x0113,
+				vx6953_regs.reg_pat_init[0].reg_0x0113},
+			{REG_VT_PIX_CLK_DIV,
+				vx6953_regs.reg_pat_init[0].
+				vt_pix_clk_div},
+			{REG_PRE_PLL_CLK_DIV,
+				vx6953_regs.reg_pat_init[0].
+				pre_pll_clk_div},
+			{REG_PLL_MULTIPLIER,
+				vx6953_regs.reg_pat_init[0].
+				pll_multiplier},
+			{REG_OP_PIX_CLK_DIV,
+				vx6953_regs.reg_pat_init[0].
+				op_pix_clk_div},
+			{REG_COARSE_INTEGRATION_TIME_HI,
+				vx6953_regs.reg_pat[rt].
+				coarse_integration_time_hi},
+			{REG_COARSE_INTEGRATION_TIME_LO,
+				vx6953_regs.reg_pat[rt].
+				coarse_integration_time_lo},
+			{REG_ANALOGUE_GAIN_CODE_GLOBAL_LO,
+				vx6953_regs.reg_pat[rt].
+				analogue_gain_code_global},
+			{REG_0x3030,
+				vx6953_regs.reg_pat_init[0].reg_0x3030},
+			/* 953 specific registers */
+			{REG_0x0111,
+				vx6953_regs.reg_pat_init[0].reg_0x0111},
+			{REG_0x0b00,
+				vx6953_regs.reg_pat_init[0].reg_0x0b00},
+			{REG_0x3001,
+				vx6953_regs.reg_pat_init[0].reg_0x3001},
+			{REG_0x3004,
+				vx6953_regs.reg_pat_init[0].reg_0x3004},
+			{REG_0x3007,
+				vx6953_regs.reg_pat_init[0].reg_0x3007},
+			{REG_0x3016,
+				vx6953_regs.reg_pat_init[0].reg_0x3016},
+			{REG_0x301d,
+				vx6953_regs.reg_pat_init[0].reg_0x301d},
+			{REG_0x317e,
+				vx6953_regs.reg_pat_init[0].reg_0x317e},
+			{REG_0x317f,
+				vx6953_regs.reg_pat_init[0].reg_0x317f},
+			{REG_0x3400,
+				vx6953_regs.reg_pat_init[0].reg_0x3400},
+			{0x0b06,
+				vx6953_regs.reg_pat_init[0].reg_0x0b06},
+			/*Single_defect_correct_weight = auto*/
+			{0x0b07,
+				vx6953_regs.reg_pat_init[0].reg_0x0b07},
+			/*Dynamic couplet correction ENABLED*/
+			{0x0b08,
+				vx6953_regs.reg_pat_init[0].reg_0x0b08},
+			/*Dynamic couplet correction weight*/
+			{0x0b09,
+				vx6953_regs.reg_pat_init[0].reg_0x0b09},
+			/* Clock Setup */
+			/* Tell sensor ext clk is 24MHz*/
+			{0x0136,
+				vx6953_regs.reg_pat_init[0].reg_0x0136},
+			{0x0137,
+				vx6953_regs.reg_pat_init[0].reg_0x0137},
+			/* The white balance gains must be written
+			to the sensor every frame. */
+			/* Edof */
+			{REG_0x0b83,
+				vx6953_regs.reg_pat_init[0].reg_0x0b83},
+			{REG_0x0b84,
+				vx6953_regs.reg_pat_init[0].reg_0x0b84},
+			{0x0b85,
+				vx6953_regs.reg_pat_init[0].reg_0x0b85},
+			{0x0b88,
+				vx6953_regs.reg_pat_init[0].reg_0x0b88},
+			{0x0b89,
+				vx6953_regs.reg_pat_init[0].reg_0x0b89},
+			{REG_0x0b8a,
+				vx6953_regs.reg_pat_init[0].reg_0x0b8a},
+			/* Mode specific regieters */
+			{REG_FRAME_LENGTH_LINES_HI,
+				vx6953_regs.reg_pat[rt].
+				frame_length_lines_hi},
+			{REG_FRAME_LENGTH_LINES_LO,
+				vx6953_regs.reg_pat[rt].
+				frame_length_lines_lo},
+			{REG_LINE_LENGTH_PCK_HI,
+				vx6953_regs.reg_pat[rt].
+				line_length_pck_hi},
+			{REG_LINE_LENGTH_PCK_LO,
+				vx6953_regs.reg_pat[rt].
+				line_length_pck_lo},
+			{REG_0x3005,
+				vx6953_regs.reg_pat[rt].reg_0x3005},
+			{0x3010,
+				vx6953_regs.reg_pat[rt].reg_0x3010},
+			{REG_0x3011,
+				vx6953_regs.reg_pat[rt].reg_0x3011},
+			{REG_0x301a,
+				vx6953_regs.reg_pat[rt].reg_0x301a},
+			{REG_0x3035,
+				vx6953_regs.reg_pat[rt].reg_0x3035},
+			{REG_0x3036,
+				vx6953_regs.reg_pat[rt].reg_0x3036},
+			{REG_0x3041,
+				vx6953_regs.reg_pat[rt].reg_0x3041},
+			{0x3042,
+				vx6953_regs.reg_pat[rt].reg_0x3042},
+			{REG_0x3045,
+				vx6953_regs.reg_pat[rt].reg_0x3045},
+			/*EDOF: Estimation settings for Preview mode
+			Application settings for capture mode
+			(standard settings - Not tuned) */
+			{REG_0x0b80,
+				vx6953_regs.reg_pat[rt].reg_0x0b80},
+			{REG_0x0900,
+				vx6953_regs.reg_pat[rt].reg_0x0900},
+			{REG_0x0901,
+				vx6953_regs.reg_pat[rt].reg_0x0901},
+			{REG_0x0902,
+				vx6953_regs.reg_pat[rt].reg_0x0902},
+			{REG_0x0383,
+				vx6953_regs.reg_pat[rt].reg_0x0383},
+			{REG_0x0387,
+				vx6953_regs.reg_pat[rt].reg_0x0387},
+			/* Change output size / frame rate */
+			{REG_0x034c,
+				vx6953_regs.reg_pat[rt].reg_0x034c},
+			{REG_0x034d,
+				vx6953_regs.reg_pat[rt].reg_0x034d},
+			{REG_0x034e,
+				vx6953_regs.reg_pat[rt].reg_0x034e},
+			{REG_0x034f,
+				vx6953_regs.reg_pat[rt].reg_0x034f},
+			{REG_0x1716,
+				vx6953_regs.reg_pat[rt].reg_0x1716},
+			{REG_0x1717,
+				vx6953_regs.reg_pat[rt].reg_0x1717},
+			{REG_0x1718,
+				vx6953_regs.reg_pat[rt].reg_0x1718},
+			{REG_0x1719,
+				vx6953_regs.reg_pat[rt].reg_0x1719},
+			};
+			/* stop streaming */
+			msleep(20);
+
+			/* Reset everything first */
+			msm_camera_i2c_write_tbl(
+				vx6953_s_ctrl.sensor_i2c_client,
+				vx6953_standby,
+				ARRAY_SIZE(vx6953_standby),
+				vx6953_s_ctrl.msm_sensor_reg->
+				default_data_type);
+
+
+			msleep(20);
+
+			msm_camera_i2c_write_tbl(
+				vx6953_s_ctrl.sensor_i2c_client,
+				vx6953_stop_settings,
+				ARRAY_SIZE(vx6953_stop_settings),
+				vx6953_s_ctrl.msm_sensor_reg->
+				default_data_type);
+			/*vx6953_stm5m0edof_delay_msecs_stdby*/
+			msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+			msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+			msm_camera_i2c_write_tbl(
+				vx6953_s_ctrl.sensor_i2c_client,
+				patch_tbl_cut2,
+				ARRAY_SIZE(patch_tbl_cut2),
+				vx6953_s_ctrl.msm_sensor_reg->
+				default_data_type);
+
+			msm_camera_i2c_write_tbl(
+				vx6953_s_ctrl.sensor_i2c_client,
+				init_mode_tbl,
+				ARRAY_SIZE(init_mode_tbl),
+				vx6953_s_ctrl.msm_sensor_reg->
+				default_data_type);
+
+
+			vx6953_s_ctrl.curr_csic_params =
+				vx6953_s_ctrl.csic_params[0];
+			v4l2_subdev_notify(&vx6953_s_ctrl.sensor_v4l2_subdev,
+				NOTIFY_CSIC_CFG,
+				vx6953_s_ctrl.curr_csic_params);
+
+			msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+			if (rt == RES_PREVIEW) {
+				CDBG("%s write mode_tbl for preview\n",
+					__func__);
+				msm_camera_i2c_write_tbl(
+					vx6953_s_ctrl.sensor_i2c_client,
+					mode_tbl1,
+					ARRAY_SIZE(mode_tbl1),
+					vx6953_s_ctrl.msm_sensor_reg->
+					default_data_type);
+			} else if (rt == RES_CAPTURE) {
+				CDBG("%s write mode_tbl for capture\n",
+					__func__);
+				msm_camera_i2c_write_tbl(
+					vx6953_s_ctrl.sensor_i2c_client,
+					mode_tbl2,
+					ARRAY_SIZE(mode_tbl2),
+					vx6953_s_ctrl.msm_sensor_reg->
+					default_data_type);
+			}
+			msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+			/* Start sensor streaming */
+			msm_camera_i2c_write_tbl(
+				vx6953_s_ctrl.sensor_i2c_client,
+				vx6953_start_settings,
+				ARRAY_SIZE(vx6953_start_settings),
+				vx6953_s_ctrl.msm_sensor_reg->
+				default_data_type);
+			msleep(20);
+
+			msm_camera_i2c_read(
+				vx6953_s_ctrl.sensor_i2c_client,
+				0x0005,
+				&frame_cnt,
+				MSM_CAMERA_I2C_BYTE_ADDR);
+			while (frame_cnt == 0xFF) {
+				msm_camera_i2c_read(
+					vx6953_s_ctrl.sensor_i2c_client,
+					0x0005,
+					&frame_cnt,
+					MSM_CAMERA_I2C_BYTE_ADDR);
+				CDBG("%s frame_cnt = %d\n",
+					__func__, frame_cnt);
+				usleep_range(5000, 10000);
+			}
+		}
+		return rc;
+		default:
+		return rc;
+	}
+	return rc;
+}
+
+static int32_t vx6953_init_config(void)
+{
+	int32_t rc = 0;
+	int rt;
+	/* change sensor resolution	if needed */
+	CDBG("%s called\n", __func__);
+	rt = RES_PREVIEW;
+	vx6953_stm5m0edof_delay_msecs_stdby =
+		((((2 * 1000 * fps_divider) /
+		   fps) * Q8) / Q10) + 1;
+
+	vx6953_sensor_setting(REG_INIT, rt);
+
+	vx6953_enable_edof(VX6953_EDOF_ESTIMATION);
+	return rc;
+}
+
+static int32_t vx6953_update_config(int rt)
+{
+	int32_t rc = 0;
+	CDBG("%s rt = %d\n", __func__, rt);
+	if (rt == MSM_SENSOR_RES_FULL)
+		rt = RES_CAPTURE;
+	else if (rt == MSM_SENSOR_RES_QTR)
+		rt = RES_PREVIEW;
+
+	vx6953_stm5m0edof_delay_msecs_stdby = 67;
+	vx6953_sensor_setting(UPDATE_PERIODIC, rt);
+
+	if (rt == RES_PREVIEW)
+		vx6953_enable_edof(VX6953_EDOF_ESTIMATION);
+	else if (rt == RES_CAPTURE)
+		vx6953_enable_edof(VX6953_EDOF_APPLICATION);
+
+	return rc;
+} /*end of vx6953_snapshot_config*/
+
+static int32_t vx6953_set_sensor_mode(struct msm_sensor_ctrl_t *s_ctrl,
+	int update_type, int rt)
+{
+	int32_t rc = 0;
+
+	fps_divider = 1 * 0x00000400;
+	fps = 30*Q8;
+
+	switch (update_type) {
+	case MSM_SENSOR_REG_INIT:
+		rc = vx6953_init_config();
+		break;
+	case MSM_SENSOR_UPDATE_PERIODIC:
+		rc = vx6953_update_config(rt);
+		break;
+	default:
+		rc = -EINVAL;
+		break;
+	}
+	return rc;
+}
+static struct msm_sensor_fn_t vx6953_func_tbl = {
+	.sensor_start_stream = msm_sensor_start_stream,
+	.sensor_stop_stream = msm_sensor_stop_stream,
+	.sensor_group_hold_on = msm_sensor_group_hold_on,
+	.sensor_group_hold_off = msm_sensor_group_hold_off,
+	.sensor_set_fps = vx6953_set_fps,
+	.sensor_write_exp_gain = vx6953_write_exp_gain,
+	.sensor_write_snapshot_exp_gain = vx6953_write_exp_gain,
+	.sensor_csi_setting = vx6953_set_sensor_mode,
+	.sensor_set_sensor_mode = msm_sensor_set_sensor_mode,
+	.sensor_mode_init = msm_sensor_mode_init,
+	.sensor_get_output_info = msm_sensor_get_output_info,
+	.sensor_config = msm_sensor_config,
+	.sensor_power_up = msm_sensor_power_up,
+	.sensor_power_down = msm_sensor_power_down,
+};
+
+static struct msm_sensor_reg_t vx6953_data_regs = {
+	.default_data_type = MSM_CAMERA_I2C_BYTE_DATA,
+	.start_stream_conf = vx6953_start_settings,
+	.start_stream_conf_size = ARRAY_SIZE(vx6953_start_settings),
+	.stop_stream_conf = vx6953_stop_settings,
+	.stop_stream_conf_size = ARRAY_SIZE(vx6953_stop_settings),
+	.group_hold_on_conf = vx6953_groupon_settings,
+	.group_hold_on_conf_size = ARRAY_SIZE(vx6953_groupon_settings),
+	.group_hold_off_conf = vx6953_groupoff_settings,
+	.group_hold_off_conf_size =
+		ARRAY_SIZE(vx6953_groupoff_settings),
+	.init_settings = &vx6953_init_conf[0],
+	.init_size = ARRAY_SIZE(vx6953_init_conf),
+	.mode_settings = &vx6953_confs[0],
+	.output_settings = &vx6953_dimensions[0],
+	.num_conf = ARRAY_SIZE(vx6953_confs),
+};
+
+static struct msm_sensor_ctrl_t vx6953_s_ctrl = {
+	.msm_sensor_reg = &vx6953_data_regs,
+	.sensor_i2c_client = &vx6953_sensor_i2c_client,
+	.sensor_i2c_addr = 0x20,
+	.sensor_output_reg_addr = &vx6953_reg_addr,
+	.sensor_id_info = &vx6953_id_info,
+	.sensor_exp_gain_info = &vx6953_exp_gain_info,
+	.cam_mode = MSM_SENSOR_MODE_INVALID,
+	.csic_params = &vx6953_csi_params_array[0],
+	.msm_sensor_mutex = &vx6953_mut,
+	.sensor_i2c_driver = &vx6953_i2c_driver,
+	.sensor_v4l2_subdev_info = vx6953_subdev_info,
+	.sensor_v4l2_subdev_info_size = ARRAY_SIZE(vx6953_subdev_info),
+	.sensor_v4l2_subdev_ops = &vx6953_subdev_ops,
+	.func_tbl = &vx6953_func_tbl,
+	.clk_rate = MSM_SENSOR_MCLK_24HZ,
+};
+
+module_init(msm_sensor_init_module);
+MODULE_DESCRIPTION("Sensor VX6953 (BAYER 5M)");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/media/video/msm/sensors/vx6953.h b/drivers/media/video/msm/sensors/vx6953.h
new file mode 100644
index 0000000..0fcdb53
--- /dev/null
+++ b/drivers/media/video/msm/sensors/vx6953.h
@@ -0,0 +1,135 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef VX6953_H
+#define VX6953_H
+#include <linux/types.h>
+#include <mach/board.h>
+struct reg_struct_init {
+	uint8_t reg_0x0112;      /* 0x0112*/
+	uint8_t reg_0x0113;      /* 0x0113*/
+	uint8_t vt_pix_clk_div;  /* 0x0301*/
+	uint8_t pre_pll_clk_div; /* 0x0305*/
+	uint8_t pll_multiplier;  /* 0x0307*/
+	uint8_t op_pix_clk_div;  /* 0x0309*/
+	uint8_t reg_0x3030;      /*0x3030*/
+	uint8_t reg_0x0111;      /*0x0111*/
+	uint8_t reg_0x0b00;      /*0x0b00*/
+	uint8_t reg_0x3001;      /*0x3001*/
+	uint8_t reg_0x3004;      /*0x3004*/
+	uint8_t reg_0x3007;      /*0x3007*/
+	uint8_t reg_0x3016;      /*0x3016*/
+	uint8_t reg_0x301d;      /*0x301d*/
+	uint8_t reg_0x317e;      /*0x317E*/
+	uint8_t reg_0x317f;      /*0x317F*/
+	uint8_t reg_0x3400;      /*0x3400*/
+	uint8_t reg_0x0b06;      /*0x0b06*/
+	uint8_t reg_0x0b07;      /*0x0b07*/
+	uint8_t reg_0x0b08;      /*0x0b08*/
+	uint8_t reg_0x0b09;      /*0x0b09*/
+	uint8_t reg_0x0136;
+	uint8_t reg_0x0137;
+	/* Edof */
+	uint8_t reg_0x0b83;      /*0x0b83*/
+	uint8_t reg_0x0b84;      /*0x0b84*/
+	uint8_t reg_0x0b85;      /*0x0b85*/
+	uint8_t reg_0x0b88;      /*0x0b88*/
+	uint8_t reg_0x0b89;      /*0x0b89*/
+	uint8_t reg_0x0b8a;      /*0x0b8a*/
+	};
+struct reg_struct {
+	uint8_t coarse_integration_time_hi; /*REG_COARSE_INTEGRATION_TIME_HI*/
+	uint8_t coarse_integration_time_lo; /*REG_COARSE_INTEGRATION_TIME_LO*/
+	uint8_t analogue_gain_code_global;
+	uint8_t frame_length_lines_hi; /* 0x0340*/
+	uint8_t frame_length_lines_lo; /* 0x0341*/
+	uint8_t line_length_pck_hi;    /* 0x0342*/
+	uint8_t line_length_pck_lo;    /* 0x0343*/
+	uint8_t reg_0x3005;   /* 0x3005*/
+	uint8_t reg_0x3010;  /* 0x3010*/
+	uint8_t reg_0x3011;  /* 0x3011*/
+	uint8_t reg_0x301a;  /* 0x301a*/
+	uint8_t reg_0x3035;  /* 0x3035*/
+	uint8_t reg_0x3036;   /* 0x3036*/
+	uint8_t reg_0x3041;  /*0x3041*/
+	uint8_t reg_0x3042;  /*0x3042*/
+	uint8_t reg_0x3045;  /*0x3045*/
+	uint8_t reg_0x0b80;   /* 0x0b80*/
+	uint8_t reg_0x0900;   /*0x0900*/
+	uint8_t reg_0x0901;   /* 0x0901*/
+	uint8_t reg_0x0902;   /*0x0902*/
+	uint8_t reg_0x0383;   /*0x0383*/
+	uint8_t reg_0x0387;   /* 0x0387*/
+	uint8_t reg_0x034c;   /* 0x034c*/
+	uint8_t reg_0x034d;   /*0x034d*/
+	uint8_t reg_0x034e;   /* 0x034e*/
+	uint8_t reg_0x034f;   /* 0x034f*/
+	uint8_t reg_0x1716; /*0x1716*/
+	uint8_t reg_0x1717; /*0x1717*/
+	uint8_t reg_0x1718; /*0x1718*/
+	uint8_t reg_0x1719; /*0x1719*/
+	uint8_t reg_0x3210;/*0x3210*/
+	uint8_t reg_0x111; /*0x111*/
+	uint8_t reg_0x3410;  /*0x3410*/
+	uint8_t reg_0x3098;
+	uint8_t reg_0x309D;
+	uint8_t reg_0x0200;
+	uint8_t reg_0x0201;
+	};
+struct vx6953_i2c_reg_conf {
+	unsigned short waddr;
+	unsigned short wdata;
+};
+
+enum vx6953_test_mode_t {
+	TEST_OFF,
+	TEST_1,
+	TEST_2,
+	TEST_3
+};
+
+enum vx6953_resolution_t {
+	QTR_SIZE,
+	FULL_SIZE,
+	INVALID_SIZE
+};
+enum vx6953_setting {
+	RES_PREVIEW,
+	RES_CAPTURE
+};
+enum mt9p012_reg_update {
+	/* Sensor egisters that need to be updated during initialization */
+	REG_INIT,
+	/* Sensor egisters that needs periodic I2C writes */
+	UPDATE_PERIODIC,
+	/* All the sensor Registers will be updated */
+	UPDATE_ALL,
+	/* Not valid update */
+	UPDATE_INVALID
+};
+
+enum sensor_revision_t {
+	VX6953_STM5M0EDOF_CUT_1,
+	VX6953_STM5M0EDOF_CUT_2,
+	VX6953_STM5M0EDOF_CUT_3
+};
+enum edof_mode_t {
+	VX6953_EDOF_DISABLE,       /* 0x00 */
+	VX6953_EDOF_APPLICATION,   /* 0x01 */
+	VX6953_EDOF_ESTIMATION     /* 0x02 */
+};
+struct vx6953_reg {
+	const struct reg_struct_init  *reg_pat_init;
+	const struct reg_struct  *reg_pat;
+};
+#endif /* VX6953_H */
diff --git a/drivers/media/video/msm/sensors/vx6953_reg.h b/drivers/media/video/msm/sensors/vx6953_reg.h
new file mode 100644
index 0000000..b76aa93
--- /dev/null
+++ b/drivers/media/video/msm/sensors/vx6953_reg.h
@@ -0,0 +1,134 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+
+struct reg_struct_init vx6953_reg_init[1] = {
+	{
+		10,			/*REG = 0x0112 , 10 bit */
+		10,			/*REG = 0x0113*/
+		9,			/*REG = 0x0301 vt_pix_clk_div*/
+		4,		/*REG = 0x0305 pre_pll_clk_div*/
+		133,		/*REG = 0x0307 pll_multiplier*/
+		10,		/*REG = 0x0309 op_pix_clk_div*/
+		0x08,		/*REG = 0x3030*/
+		0x02,		/*REG = 0x0111*/
+		0x01,		/*REG = 0x0b00 ,lens shading off */
+		0x30,		/*REG = 0x3001*/
+		0x33,		/*REG = 0x3004*/
+		0x09,		/*REG = 0x3007*/
+		0x1F,		/*REG = 0x3016*/
+		0x03,		/*REG = 0x301d*/
+		0x11,		/*REG = 0x317E*/
+		0x09,		/*REG = 0x317F*/
+		0x38,		/*REG = 0x3400*/
+		0x00,		/*REG_0x0b06*/
+		0x80,		/*REG_0x0b07*/
+		0x01,		/*REG_0x0b08*/
+		0x4F,		/*REG_0x0b09*/
+		0x18,		/*REG_0x0136*/
+		0x00,		/*/REG_0x0137*/
+		0x20,		/*REG = 0x0b83*/
+		0x90,		/*REG = 0x0b84*/
+		0x20,		/*REG = 0x0b85*/
+		0x80,		/*REG = 0x0b88*/
+		0x00,		/*REG = 0x0b89*/
+		0x00,		/*REG = 0x0b8a*/
+	}
+};
+struct reg_struct vx6953_reg_pat[2] = {
+	{/* Preview */
+		0x03,	/*REG = 0x0202 coarse integration_time_hi*/
+		0xd0,	/*REG = 0x0203 coarse_integration_time_lo*/
+		0xc0,	/*REG = 0x0205 analogue_gain_code_global*/
+		0x03,	/*REG = 0x0340 frame_length_lines_hi*/
+		0xf0,	/*REG = 0x0341 frame_length_lines_lo*/
+		0x0b,	/*REG = 0x0342  line_length_pck_hi*/
+		0x74,	/*REG = 0x0343  line_length_pck_lo*/
+		0x03,	/*REG = 0x3005*/
+		0x00,	/*REG = 0x3010*/
+		0x01,	/*REG = 0x3011*/
+		0x6a,	/*REG = 0x301a*/
+		0x03,	/*REG = 0x3035*/
+		0x2c,	/*REG = 0x3036*/
+		0x00,	/*REG = 0x3041*/
+		0x24,	/*REG = 0x3042*/
+		0x81,	/*REG = 0x3045*/
+		0x02,	/*REG = 0x0b80 edof estimate*/
+		0x01,	/*REG = 0x0900*/
+		0x22,	/*REG = 0x0901*/
+		0x04,	/*REG = 0x0902*/
+		0x03,	/*REG = 0x0383*/
+		0x03,	/*REG = 0x0387*/
+		0x05,	/*REG = 0x034c*/
+		0x18,	/*REG = 0x034d*/
+		0x03,	/*REG = 0x034e*/
+		0xd4,	/*REG = 0x034f*/
+		0x02,	/*0x1716*/
+		0x04,	/*0x1717*/
+		0x08,	/*0x1718*/
+		0x2c,	/*0x1719*/
+		0x01,   /*0x3210*/
+		0x02,   /*0x111*/
+		0x01,   /*0x3410*/
+		0x01,   /*0x3098*/
+		0x05,   /*0x309D*/
+		0x02,
+		0x04,
+	},
+	{ /* Snapshot */
+		0x07,/*REG = 0x0202 coarse_integration_time_hi*/
+		0x00,/*REG = 0x0203 coarse_integration_time_lo*/
+		0xc0,/*REG = 0x0205 analogue_gain_code_global*/
+		0x07,/*REG = 0x0340 frame_length_lines_hi*/
+		0xd0,/*REG = 0x0341 frame_length_lines_lo*/
+		0x0b,/*REG = 0x0342 line_length_pck_hi*/
+		0x8c,/*REG = 0x0343 line_length_pck_lo*/
+		0x01,/*REG = 0x3005*/
+		0x00,/*REG = 0x3010*/
+		0x00,/*REG = 0x3011*/
+		0x55,/*REG = 0x301a*/
+		0x01,/*REG = 0x3035*/
+		0x23,/*REG = 0x3036*/
+		0x00,/*REG = 0x3041*/
+		0x24,/*REG = 0x3042*/
+		0xb7,/*REG = 0x3045*/
+		0x01,/*REG = 0x0b80 edof application*/
+		0x00,/*REG = 0x0900*/
+		0x00,/*REG = 0x0901*/
+		0x00,/*REG = 0x0902*/
+		0x01,/*REG = 0x0383*/
+		0x01,/*REG = 0x0387*/
+		0x0A,/*REG = 0x034c*/
+		0x30,/*REG = 0x034d*/
+		0x07,/*REG = 0x034e*/
+		0xA8,/*REG = 0x034f*/
+		0x02,/*0x1716*/
+		0x0d,/*0x1717*/
+		0x07,/*0x1718*/
+		0x7d,/*0x1719*/
+		0x01,/*0x3210*/
+		0x02,/*0x111*/
+		0x01,/*0x3410*/
+		0x01,/*0x3098*/
+		0x05, /*0x309D*/
+		0x02,
+		0x00,
+	}
+};
+
+
+
+struct vx6953_reg vx6953_regs = {
+	.reg_pat_init = &vx6953_reg_init[0],
+	.reg_pat = &vx6953_reg_pat[0],
+};
diff --git a/drivers/media/video/msm/sn12m0pz.c b/drivers/media/video/msm/sn12m0pz.c
new file mode 100644
index 0000000..2eabb3c
--- /dev/null
+++ b/drivers/media/video/msm/sn12m0pz.c
@@ -0,0 +1,1850 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/kernel.h>
+#include <media/msm_camera.h>
+#include <mach/gpio.h>
+#include <mach/camera.h>
+#include <linux/slab.h>
+#include "sn12m0pz.h"
+
+
+#define	Q8					0x00000100
+#define	REG_GROUPED_PARAMETER_HOLD		0x0104
+#define	GROUPED_PARAMETER_HOLD_OFF		0x00
+#define	GROUPED_PARAMETER_HOLD			0x01
+#define	REG_MODE_SELECT				0x0100
+#define	MODE_SELECT_STANDBY_MODE		0x00
+#define	MODE_SELECT_STREAM			0x01
+
+/* Integration Time */
+#define	REG_COARSE_INTEGRATION_TIME_MSB		0x0202
+#define	REG_COARSE_INTEGRATION_TIME_LSB		0x0203
+
+/* Gain */
+#define	REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB	0x0204
+#define	REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB	0x0205
+
+/* PLL Register Defines */
+#define	REG_PLL_MULTIPLIER			0x0307
+#define	REG_0x302B				0x302B
+
+/* MIPI Enable Settings */
+#define	REG_0x30E5				0x30E5
+#define	REG_0x3300				0x3300
+
+/* Global Setting */
+#define	REG_IMAGE_ORIENTATION			0x0101
+
+#define	REG_0x300A				0x300A
+#define	REG_0x3014				0x3014
+#define	REG_0x3015				0x3015
+#define	REG_0x3017				0x3017
+#define	REG_0x301C				0x301C
+#define	REG_0x3031				0x3031
+#define	REG_0x3040				0x3040
+#define	REG_0x3041				0x3041
+#define	REG_0x3051				0x3051
+#define	REG_0x3053				0x3053
+#define	REG_0x3055				0x3055
+#define	REG_0x3057				0x3057
+#define	REG_0x3060				0x3060
+#define	REG_0x3065				0x3065
+#define	REG_0x30AA				0x30AA
+#define	REG_0x30AB				0x30AB
+#define	REG_0x30B0				0x30B0
+#define	REG_0x30B2				0x30B2
+#define	REG_0x30D3				0x30D3
+
+#define	REG_0x3106				0x3106
+#define	REG_0x3108				0x3108
+#define	REG_0x310A				0x310A
+#define	REG_0x310C				0x310C
+#define	REG_0x310E				0x310E
+#define	REG_0x3126				0x3126
+#define	REG_0x312E				0x312E
+#define	REG_0x313C				0x313C
+#define	REG_0x313E				0x313E
+#define	REG_0x3140				0x3140
+#define	REG_0x3142				0x3142
+#define	REG_0x3144				0x3144
+#define	REG_0x3148				0x3148
+#define	REG_0x314A				0x314A
+#define	REG_0x3166				0x3166
+#define	REG_0x3168				0x3168
+#define	REG_0x316F				0x316F
+#define	REG_0x3171				0x3171
+#define	REG_0x3173				0x3173
+#define	REG_0x3175				0x3175
+#define	REG_0x3177				0x3177
+#define	REG_0x3179				0x3179
+#define	REG_0x317B				0x317B
+#define	REG_0x317D				0x317D
+#define	REG_0x317F			0x317F
+#define	REG_0x3181			0x3181
+#define	REG_0x3184			0x3184
+#define	REG_0x3185			0x3185
+#define	REG_0x3187			0x3187
+
+#define	REG_0x31A4			0x31A4
+#define	REG_0x31A6			0x31A6
+#define	REG_0x31AC			0x31AC
+#define	REG_0x31AE			0x31AE
+#define	REG_0x31B4			0x31B4
+#define	REG_0x31B6			0x31B6
+
+#define	REG_0x3254			0x3254
+#define	REG_0x3256			0x3256
+#define	REG_0x3258			0x3258
+#define	REG_0x325A			0x325A
+#define	REG_0x3260			0x3260
+#define	REG_0x3262			0x3262
+
+
+#define	REG_0x3304			0x3304
+#define	REG_0x3305			0x3305
+#define	REG_0x3306			0x3306
+#define	REG_0x3307			0x3307
+#define	REG_0x3308			0x3308
+#define	REG_0x3309			0x3309
+#define	REG_0x330A			0x330A
+#define	REG_0x330B			0x330B
+#define	REG_0x330C			0x330C
+#define	REG_0x330D			0x330D
+
+/* Mode Setting */
+#define	REG_FRAME_LENGTH_LINES_MSB	0x0340
+#define	REG_FRAME_LENGTH_LINES_LSB	0x0341
+#define	REG_LINE_LENGTH_PCK_MSB		0x0342
+#define	REG_LINE_LENGTH_PCK_LSB		0x0343
+#define	REG_X_OUTPUT_SIZE_MSB		0x034C
+#define	REG_X_OUTPUT_SIZE_LSB		0x034D
+#define	REG_Y_OUTPUT_SIZE_MSB		0x034E
+#define	REG_Y_OUTPUT_SIZE_LSB		0x034F
+#define	REG_X_EVEN_INC_LSB		0x0381
+#define	REG_X_ODD_INC_LSB		0x0383
+#define	REG_Y_EVEN_INC_LSB		0x0385
+#define	REG_Y_ODD_INC_LSB		0x0387
+#define	REG_0x3016			0x3016
+#define	REG_0x30E8			0x30E8
+#define	REG_0x3301			0x3301
+/* for 120fps support */
+#define	REG_0x0344			0x0344
+#define	REG_0x0345			0x0345
+#define	REG_0x0346			0x0346
+#define	REG_0x0347			0x0347
+#define	REG_0x0348			0x0348
+#define	REG_0x0349			0x0349
+#define	REG_0x034A			0x034A
+#define	REG_0x034B			0x034B
+
+/* Test Pattern */
+#define	REG_0x30D8			0x30D8
+#define	REG_TEST_PATTERN_MODE		0x0601
+
+/* Solid Color Test Pattern */
+#define	REG_TEST_DATA_RED_MSB		0x0603
+#define	REG_TEST_DATA_RED_LSB		0x0603
+#define	REG_TEST_DATA_GREENR_MSB	0x0604
+#define	REG_TEST_DATA_GREENR_LSB	0x0605
+#define	REG_TEST_DATA_BLUE_MSB		0x0606
+#define	REG_TEST_DATA_BLUE_LSB		0x0607
+#define	REG_TEST_DATA_GREENB_MSB	0x0608
+#define	REG_TEST_DATA_GREENB_LSB	0x0609
+#define	SN12M0PZ_AF_I2C_SLAVE_ID	0xE4
+#define	SN12M0PZ_STEPS_NEAR_TO_CLOSEST_INF	42
+#define	SN12M0PZ_TOTAL_STEPS_NEAR_TO_FAR	42
+
+
+/* TYPE DECLARATIONS */
+
+
+enum mipi_config_type {
+	IU060F_SN12M0PZ_STMIPID01,
+	IU060F_SN12M0PZ_STMIPID02
+};
+
+enum sn12m0pz_test_mode_t {
+	TEST_OFF,
+	TEST_1,
+	TEST_2,
+	TEST_3
+};
+
+enum sn12m0pz_resolution_t {
+	QTR_SIZE,
+	FULL_SIZE,
+	INVALID_SIZE,
+	QVGA_SIZE,
+};
+
+enum sn12m0pz_setting {
+	RES_PREVIEW,
+	RES_CAPTURE,
+	RES_VIDEO_120FPS,
+};
+
+enum mt9p012_reg_update {
+	/* Sensor egisters that need to be updated during initialization */
+	REG_INIT,
+	/* Sensor egisters that needs periodic I2C writes */
+	UPDATE_PERIODIC,
+	/* All the sensor Registers will be updated */
+	UPDATE_ALL,
+	/* Not valid update */
+	UPDATE_INVALID
+};
+
+/* 816x612, 24MHz MCLK 96MHz PCLK */
+#define	IU060F_SN12M0PZ_OFFSET			3
+/* Time in milisecs for waiting for the sensor to reset.*/
+#define	SN12M0PZ_RESET_DELAY_MSECS		66
+#define	SN12M0PZ_WIDTH				4032
+#define	SN12M0PZ_HEIGHT				3024
+#define	SN12M0PZ_FULL_SIZE_WIDTH		4032
+#define	SN12M0PZ_FULL_SIZE_HEIGHT		3024
+#define	SN12M0PZ_HRZ_FULL_BLK_PIXELS		176
+#define	SN12M0PZ_VER_FULL_BLK_LINES		50
+#define	SN12M0PZ_QTR_SIZE_WIDTH			2016
+#define	SN12M0PZ_QTR_SIZE_HEIGHT		1512
+#define	SN12M0PZ_HRZ_QTR_BLK_PIXELS		2192
+#define	SN12M0PZ_VER_QTR_BLK_LINES		26
+
+/* 120fps mode */
+#define	SN12M0PZ_QVGA_SIZE_WIDTH		4032
+#define	SN12M0PZ_QVGA_SIZE_HEIGHT		249
+#define	SN12M0PZ_HRZ_QVGA_BLK_PIXELS		176
+#define	SN12M0PZ_VER_QVGA_BLK_LINES		9
+#define	SN12M0PZ_DEFAULT_CLOCK_RATE		24000000
+
+static uint32_t IU060F_SN12M0PZ_DELAY_MSECS = 30;
+static enum mipi_config_type mipi_config = IU060F_SN12M0PZ_STMIPID02;
+/* AF Tuning Parameters */
+static int16_t enable_single_D02_lane;
+static int16_t fullsize_cropped_at_8mp;
+
+struct sn12m0pz_work_t {
+	struct work_struct work;
+};
+
+static struct sn12m0pz_work_t *sn12m0pz_sensorw;
+static struct i2c_client *sn12m0pz_client;
+
+struct sn12m0pz_ctrl_t {
+	const struct msm_camera_sensor_info *sensordata;
+	uint32_t sensormode;
+	uint32_t fps_divider;/* init to 1 * 0x00000400 */
+	uint32_t pict_fps_divider;/* init to 1 * 0x00000400 */
+	uint16_t fps;
+	int16_t curr_lens_pos;
+	uint16_t curr_step_pos;
+	uint16_t my_reg_gain;
+	uint32_t my_reg_line_count;
+	uint16_t total_lines_per_frame;
+	enum sn12m0pz_resolution_t prev_res;
+	enum sn12m0pz_resolution_t pict_res;
+	enum sn12m0pz_resolution_t curr_res;
+	enum sn12m0pz_test_mode_t  set_test;
+	unsigned short imgaddr;
+};
+
+static struct sn12m0pz_ctrl_t *sn12m0pz_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(sn12m0pz_wait_queue);
+DEFINE_MUTEX(sn12m0pz_mut);
+
+
+static int sn12m0pz_i2c_rxdata(unsigned short saddr,
+	unsigned char *rxdata, int length)
+{
+	struct i2c_msg msgs[] = {
+		{
+			.addr  = saddr,
+			.flags = 0,
+			.len   = 2,
+			.buf   = rxdata,
+		},
+		{
+			.addr  = saddr,
+			.flags = I2C_M_RD,
+			.len   = 2,
+			.buf   = rxdata,
+		},
+	};
+
+	if (i2c_transfer(sn12m0pz_client->adapter, msgs, 2) < 0) {
+		CDBG("sn12m0pz_i2c_rxdata failed!");
+		return -EIO;
+	}
+
+	return 0;
+}
+static int32_t sn12m0pz_i2c_txdata(unsigned short saddr,
+				unsigned char *txdata, int length)
+{
+
+	struct i2c_msg msg[] = {
+		{
+			.addr  = saddr,
+			.flags = 0,
+			.len	 = length,
+			.buf	 = txdata,
+		},
+	};
+
+	if (i2c_transfer(sn12m0pz_client->adapter, msg, 1) < 0) {
+		CDBG("sn12m0pz_i2c_txdata faild 0x%x", sn12m0pz_client->addr);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int32_t sn12m0pz_i2c_read(unsigned short raddr,
+				unsigned short *rdata, int rlen)
+{
+	int32_t rc;
+	unsigned char buf[2];
+	if (!rdata)
+		return -EIO;
+
+	memset(buf, 0, sizeof(buf));
+
+	buf[0] = (raddr & 0xFF00) >> 8;
+	buf[1] = (raddr & 0x00FF);
+
+	rc = sn12m0pz_i2c_rxdata(sn12m0pz_client->addr, buf, rlen);
+
+	if (rc < 0) {
+		CDBG("sn12m0pz_i2c_read 0x%x failed!", raddr);
+		return rc;
+	}
+
+	*rdata = (rlen == 2 ? buf[0] << 8 | buf[1] : buf[0]);
+
+	return rc;
+}
+
+static int32_t sn12m0pz_i2c_write_b_sensor(unsigned short waddr, uint8_t bdata)
+{
+	int32_t rc;
+	unsigned char buf[3];
+
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (waddr & 0xFF00) >> 8;
+	buf[1] = (waddr & 0x00FF);
+	buf[2] = bdata;
+	udelay(90);
+	CDBG("i2c_write_b addr = %x, val = %x\n", waddr, bdata);
+	rc = sn12m0pz_i2c_txdata(sn12m0pz_client->addr, buf, 3);
+
+	if (rc < 0) {
+		CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!",
+			waddr, bdata);
+	}
+
+	return rc;
+}
+
+static int16_t sn12m0pz_i2c_write_b_af(unsigned short saddr,
+				unsigned short baddr, unsigned short bdata)
+{
+	int16_t rc;
+	unsigned char buf[2];
+
+	memset(buf, 0, sizeof(buf));
+	buf[0] = baddr;
+	buf[1] = bdata;
+	rc = sn12m0pz_i2c_txdata(saddr, buf, 2);
+
+	if (rc < 0)
+		CDBG("i2c_write failed, saddr = 0x%x addr = 0x%x, val =0x%x!",
+			saddr, baddr, bdata);
+
+	return rc;
+}
+
+static int32_t sn12m0pz_i2c_write_byte_bridge(unsigned short saddr,
+				unsigned short waddr, uint8_t bdata)
+{
+	int32_t rc;
+	unsigned char buf[3];
+
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (waddr & 0xFF00) >> 8;
+	buf[1] = (waddr & 0x00FF);
+	buf[2] = bdata;
+
+	CDBG("i2c_write_b addr = %x, val = %x", waddr, bdata);
+	rc = sn12m0pz_i2c_txdata(saddr, buf, 3);
+
+	if (rc < 0)
+		CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!",
+			waddr, bdata);
+
+	return rc;
+}
+
+static int32_t sn12m0pz_stmipid01_config(void)
+{
+	int32_t rc = 0;
+	/* Initiate I2C for D01: */
+	/* MIPI Bridge configuration */
+	if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0002, 0x19) < 0)
+		return rc; /* enable clock lane*/
+	if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0003, 0x00) < 0)
+		return rc;
+	if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0004, 0x3E) < 0)
+		return rc; /* mipi mode clock*/
+	if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0005, 0x01) < 0)
+		return rc; /* enable data line*/
+	if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0006, 0x0F) < 0)
+		return rc; /* mipi mode data 0x01*/
+	if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0009, 0x00) < 0)
+		return rc; /* Data_Lane1_Reg1*/
+	if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x000D, 0x92) < 0)
+		return rc; /* CCPRxRegisters*/
+	if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x000E, 0x28) < 0)
+		return rc; /* 10 bits for pixel width input for CCP rx.*/
+	if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0014, 0xC0) < 0)
+		return rc; /* no bypass, no decomp, 1Lane System,CSIstreaming*/
+	if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0015, 0x48) < 0)
+		return rc; /* ModeControlRegisters-- Don't reset error flag*/
+	if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0017, 0x2B) < 0)
+		return rc; /* Data_ID_Rreg*/
+	if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0018, 0x2B) < 0)
+		return rc; /* Data_ID_Rreg_emb*/
+	if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0019, 0x0C) < 0)
+		return rc;
+	if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x001E, 0x0A) < 0)
+		return rc;
+	if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x001F, 0x0A) < 0)
+		return rc;
+
+	return rc;
+}
+static int32_t sn12m0pz_stmipid02_config(void)
+{
+	int32_t rc = 0;
+
+	/* Main Camera Clock Lane 1 (CLHP1, CLKN1)*/
+	/* Enable Clock Lane 1 (CLHP1, CLKN1), 0x15 for 400MHz */
+	if (enable_single_D02_lane) {
+		if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0002, 0x19) < 0)
+			return rc;
+		/* Main Camera Data Lane 1.1 (DATA2P1, DATA2N1) */
+		if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0009, 0x00) < 0)
+			return rc;/* Enable Data Lane 1.2 (DATA2P1, DATA2N1) */
+		if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x000A, 0x00) < 0)
+			return rc; /*CSIMode on Data Lane1.2(DATA2P1,DATA2N1)*/
+		/* Mode Control */
+		/* Enable single lane for qtr preview */
+		if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0014, 0xC0) < 0)
+			return rc; /*set 0xC0 - left justified on upper bits)*/
+		/* bit 1 set to 0 i.e. 1 lane system for qtr size preview */
+	} else {
+		if (sn12m0pz_ctrl->prev_res == QVGA_SIZE) {
+			if (sn12m0pz_i2c_write_byte_bridge(0x28>>1,
+				0x0002, 0x19) < 0)
+				return rc;
+		} else {
+			if (sn12m0pz_i2c_write_byte_bridge(0x28>>1,
+				0x0002, 0x21) < 0)
+				return rc;
+		}
+		/* Main Camera Data Lane 1.1 (DATA2P1, DATA2N1) */
+		if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0009, 0x01) < 0)
+			return rc; /* Enable Data Lane 1.2 (DATA2P1, DATA2N1) */
+		if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x000A, 0x01) < 0)
+			return rc; /* CSI Mode Data Lane1.2(DATA2P1, DATA2N1)*/
+
+		/* Mode Control */
+		/* Enable two lanes for full size preview/ snapshot */
+		if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0014, 0xC2) < 0)
+			return rc; /* No decompression, CSI dual lane */
+	}
+
+	if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0004, 0x1E) < 0)
+		return rc;
+
+	/* Main Camera Data Lane 1.1 (DATA1P1, DATA1N1) */
+	if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0005, 0x03) < 0)
+		return rc; /* Enable Data Lane 1.1 (DATA1P1, DATA1N1) */
+	if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0006, 0x0f) < 0)
+		return rc; /* CSI Mode on Data Lane 1.1 (DATA1P1, DATA1N1) */
+
+	/* Tristated Output, continuous clock, */
+	/*polarity of clock is inverted and sync signals not inverted*/
+	if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0015, 0x08) < 0)
+		return rc;
+	if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0036, 0x20) < 0)
+		return rc; /* Enable compensation macro, main camera */
+
+	/* Data type: 0x2B Raw 10 */
+	if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0017, 0x2B) < 0)
+		return rc;
+	if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0018, 0x2B) < 0)
+		return rc; /* Data type of embedded data: 0x2B Raw 10 */
+	if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0019, 0x0C) < 0)
+		return rc; /* Data type and pixel width programmed 0x0C*/
+
+	/* Decompression Mode */
+
+	/* Pixel Width and Decompression ON/OFF */
+	if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x001E, 0x0A) < 0)
+		return rc; /* Image data not compressed: 0x0A for 10 bits */
+	if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x001F, 0x0A) < 0)
+		return rc; /* Embedded data not compressed: 0x0A for 10 bits */
+	return rc;
+}
+
+static int16_t sn12m0pz_af_init(void)
+{
+	int16_t rc;
+	/* Initialize waveform */
+	rc = sn12m0pz_i2c_write_b_af(SN12M0PZ_AF_I2C_SLAVE_ID >> 1, 0x01, 0xA9);
+
+	rc = sn12m0pz_i2c_write_b_af(SN12M0PZ_AF_I2C_SLAVE_ID >> 1, 0x02, 0xD2);
+
+	rc = sn12m0pz_i2c_write_b_af(SN12M0PZ_AF_I2C_SLAVE_ID >> 1, 0x03, 0x0C);
+
+	rc = sn12m0pz_i2c_write_b_af(SN12M0PZ_AF_I2C_SLAVE_ID >> 1, 0x04, 0x14);
+
+	rc = sn12m0pz_i2c_write_b_af(SN12M0PZ_AF_I2C_SLAVE_ID >> 1, 0x05, 0xB6);
+
+	rc = sn12m0pz_i2c_write_b_af(SN12M0PZ_AF_I2C_SLAVE_ID >> 1, 0x06, 0x4F);
+
+	rc = sn12m0pz_i2c_write_b_af(SN12M0PZ_AF_I2C_SLAVE_ID >> 1, 0x07, 0x00);
+
+	return rc;
+}
+
+static int32_t sn12m0pz_move_focus(int direction,
+	int32_t num_steps)
+{
+	int8_t step_direction, dest_step_position, bit_mask;
+	int32_t rc = 0;
+	uint16_t sn12m0pz_l_region_code_per_step = 3;
+
+	if (num_steps == 0)
+		return rc;
+
+	if (direction == MOVE_NEAR) {
+		step_direction = 1;
+		bit_mask = 0x80;
+	} else if (direction == MOVE_FAR) {
+		step_direction = -1;
+		bit_mask = 0x00;
+	} else {
+		CDBG("sn12m0pz_move_focus: Illegal focus direction");
+		return -EINVAL;
+	}
+
+	dest_step_position = sn12m0pz_ctrl->curr_step_pos +
+		(step_direction * num_steps);
+
+	if (dest_step_position < 0)
+		dest_step_position = 0;
+	else if (dest_step_position > SN12M0PZ_TOTAL_STEPS_NEAR_TO_FAR)
+		dest_step_position = SN12M0PZ_TOTAL_STEPS_NEAR_TO_FAR;
+
+	rc = sn12m0pz_i2c_write_b_af(SN12M0PZ_AF_I2C_SLAVE_ID >> 1, 0x00,
+		((num_steps * sn12m0pz_l_region_code_per_step) | bit_mask));
+
+	sn12m0pz_ctrl->curr_step_pos = dest_step_position;
+
+	return rc;
+}
+static int32_t sn12m0pz_set_default_focus(uint8_t af_step)
+{
+	int32_t rc;
+
+	/* Initialize to infinity */
+
+	rc = sn12m0pz_i2c_write_b_af(SN12M0PZ_AF_I2C_SLAVE_ID >> 1, 0x00, 0x7F);
+
+	rc = sn12m0pz_i2c_write_b_af(SN12M0PZ_AF_I2C_SLAVE_ID >> 1, 0x00, 0x7F);
+
+	sn12m0pz_ctrl->curr_step_pos = 0;
+
+	return rc;
+}
+static void sn12m0pz_get_pict_fps(uint16_t fps, uint16_t *pfps)
+{
+	/* input fps is preview fps in Q8 format */
+	uint16_t preview_frame_length_lines, snapshot_frame_length_lines;
+	uint16_t preview_line_length_pck, snapshot_line_length_pck;
+	uint32_t divider, pclk_mult, d1, d2;
+
+	/* Total frame_length_lines and line_length_pck for preview */
+	CDBG("sn12m0pz_get_pict_fps prev_res %d", sn12m0pz_ctrl->prev_res);
+	if (sn12m0pz_ctrl->prev_res == QVGA_SIZE) {
+		preview_frame_length_lines = SN12M0PZ_QVGA_SIZE_HEIGHT +
+			SN12M0PZ_VER_QVGA_BLK_LINES;
+		preview_line_length_pck = SN12M0PZ_QVGA_SIZE_WIDTH +
+			SN12M0PZ_HRZ_QVGA_BLK_PIXELS;
+	} else {
+		preview_frame_length_lines = SN12M0PZ_QTR_SIZE_HEIGHT +
+			SN12M0PZ_VER_QTR_BLK_LINES;
+		preview_line_length_pck = SN12M0PZ_QTR_SIZE_WIDTH +
+			SN12M0PZ_HRZ_QTR_BLK_PIXELS;
+	}
+	/* Total frame_length_lines and line_length_pck for snapshot */
+	snapshot_frame_length_lines = SN12M0PZ_FULL_SIZE_HEIGHT
+				+ SN12M0PZ_HRZ_FULL_BLK_PIXELS;
+	snapshot_line_length_pck = SN12M0PZ_FULL_SIZE_WIDTH
+				+ SN12M0PZ_HRZ_FULL_BLK_PIXELS;
+	d1 = preview_frame_length_lines *
+				0x00000400 / snapshot_frame_length_lines;
+	d2 = preview_line_length_pck *
+				0x00000400/snapshot_line_length_pck;
+	divider = d1 * d2 / 0x400;
+	pclk_mult =
+		(uint32_t)
+		(sn12m0pz_regs.reg_pat[RES_CAPTURE].pll_multiplier_lsb *
+		0x400) / (uint32_t)
+		sn12m0pz_regs.reg_pat[RES_PREVIEW].pll_multiplier_lsb;
+	*pfps = (uint16_t) (((fps * divider) / 0x400 * pclk_mult) / 0x400);
+}
+
+static uint16_t sn12m0pz_get_prev_lines_pf(void)
+{
+	if (sn12m0pz_ctrl->prev_res == QTR_SIZE)
+		return SN12M0PZ_QTR_SIZE_HEIGHT +
+			SN12M0PZ_VER_QTR_BLK_LINES;
+	else if (sn12m0pz_ctrl->prev_res == QVGA_SIZE)
+		return SN12M0PZ_QVGA_SIZE_HEIGHT +
+			SN12M0PZ_VER_QVGA_BLK_LINES;
+
+	else
+		return SN12M0PZ_FULL_SIZE_HEIGHT +
+			SN12M0PZ_VER_FULL_BLK_LINES;
+}
+
+static uint16_t sn12m0pz_get_prev_pixels_pl(void)
+{
+	if (sn12m0pz_ctrl->prev_res == QTR_SIZE)
+		return SN12M0PZ_QTR_SIZE_WIDTH +
+			SN12M0PZ_HRZ_QTR_BLK_PIXELS;
+	else
+		return SN12M0PZ_FULL_SIZE_WIDTH +
+			SN12M0PZ_HRZ_FULL_BLK_PIXELS;
+}
+
+static uint16_t sn12m0pz_get_pict_lines_pf(void)
+{
+	if (sn12m0pz_ctrl->pict_res == QTR_SIZE)
+		return SN12M0PZ_QTR_SIZE_HEIGHT +
+			SN12M0PZ_VER_QTR_BLK_LINES;
+	else
+		return SN12M0PZ_FULL_SIZE_HEIGHT +
+			SN12M0PZ_VER_FULL_BLK_LINES;
+}
+
+static uint16_t sn12m0pz_get_pict_pixels_pl(void)
+{
+	if (sn12m0pz_ctrl->pict_res == QTR_SIZE)
+		return SN12M0PZ_QTR_SIZE_WIDTH +
+			SN12M0PZ_HRZ_QTR_BLK_PIXELS;
+	else
+		return SN12M0PZ_FULL_SIZE_WIDTH +
+			SN12M0PZ_HRZ_FULL_BLK_PIXELS;
+}
+
+static uint32_t sn12m0pz_get_pict_max_exp_lc(void)
+{
+	if (sn12m0pz_ctrl->pict_res == QTR_SIZE)
+		return (SN12M0PZ_QTR_SIZE_HEIGHT +
+			SN12M0PZ_VER_QTR_BLK_LINES) * 24;
+	else
+		return (SN12M0PZ_FULL_SIZE_HEIGHT +
+			SN12M0PZ_VER_FULL_BLK_LINES) * 24;
+}
+
+static int32_t sn12m0pz_set_fps(struct fps_cfg	*fps)
+{
+	uint16_t total_lines_per_frame;
+	int32_t rc = 0;
+
+	total_lines_per_frame = (uint16_t)((SN12M0PZ_QTR_SIZE_HEIGHT +
+				SN12M0PZ_VER_QTR_BLK_LINES) *
+				sn12m0pz_ctrl->fps_divider / 0x400);
+
+	if (sn12m0pz_i2c_write_b_sensor(REG_FRAME_LENGTH_LINES_MSB,
+				((total_lines_per_frame & 0xFF00) >> 8)) < 0)
+		return rc;
+
+	if (sn12m0pz_i2c_write_b_sensor(REG_FRAME_LENGTH_LINES_LSB,
+				(total_lines_per_frame & 0x00FF)) < 0)
+		return rc;
+
+	return rc;
+}
+
+static int32_t sn12m0pz_write_exp_gain(uint16_t gain, uint32_t line)
+{
+	static uint16_t max_legal_gain = 0x00E0;
+	uint8_t gain_msb, gain_lsb;
+	uint8_t intg_time_msb, intg_time_lsb;
+	uint8_t line_length_pck_msb, line_length_pck_lsb;
+	uint16_t line_length_pck, frame_length_lines, temp_lines;
+	uint32_t line_length_ratio = 1 * Q8;
+	int32_t rc = 0;
+	CDBG("sn12m0pz_write_exp_gain : gain = %d line = %d", gain, line);
+
+	if (sn12m0pz_ctrl->sensormode != SENSOR_SNAPSHOT_MODE) {
+		if (sn12m0pz_ctrl->prev_res == QVGA_SIZE) {
+			frame_length_lines = SN12M0PZ_QVGA_SIZE_HEIGHT +
+						SN12M0PZ_VER_QVGA_BLK_LINES;
+			line_length_pck = SN12M0PZ_QVGA_SIZE_WIDTH +
+						SN12M0PZ_HRZ_QVGA_BLK_PIXELS;
+			if (line > (frame_length_lines -
+					IU060F_SN12M0PZ_OFFSET))
+				line = frame_length_lines -
+						IU060F_SN12M0PZ_OFFSET;
+			sn12m0pz_ctrl->fps = (uint16_t) (120 * Q8);
+		} else {
+			if (sn12m0pz_ctrl->curr_res  == QTR_SIZE) {
+				frame_length_lines = SN12M0PZ_QTR_SIZE_HEIGHT +
+						SN12M0PZ_VER_QTR_BLK_LINES;
+				line_length_pck = SN12M0PZ_QTR_SIZE_WIDTH +
+						SN12M0PZ_HRZ_QTR_BLK_PIXELS;
+			} else {
+				frame_length_lines = SN12M0PZ_HEIGHT +
+						SN12M0PZ_VER_FULL_BLK_LINES;
+				line_length_pck = SN12M0PZ_WIDTH +
+						SN12M0PZ_HRZ_FULL_BLK_PIXELS;
+			}
+			if (line > (frame_length_lines -
+						IU060F_SN12M0PZ_OFFSET))
+				sn12m0pz_ctrl->fps = (uint16_t) (30 * Q8 *
+			(frame_length_lines - IU060F_SN12M0PZ_OFFSET) / line);
+			else
+				sn12m0pz_ctrl->fps = (uint16_t) (30 * Q8);
+		}
+	} else {
+		if (sn12m0pz_ctrl->curr_res  == QTR_SIZE) {
+			frame_length_lines = SN12M0PZ_QTR_SIZE_HEIGHT +
+						SN12M0PZ_VER_QTR_BLK_LINES;
+			line_length_pck = SN12M0PZ_QTR_SIZE_WIDTH +
+						SN12M0PZ_HRZ_QTR_BLK_PIXELS;
+		} else {
+			frame_length_lines = SN12M0PZ_HEIGHT +
+						SN12M0PZ_VER_FULL_BLK_LINES;
+			line_length_pck = SN12M0PZ_WIDTH +
+						SN12M0PZ_HRZ_FULL_BLK_PIXELS;
+		}
+	}
+	if (gain > max_legal_gain)
+		/* range: 0 to 224 */
+		gain = max_legal_gain;
+	temp_lines = line;
+	/* calculate line_length_ratio */
+	if (line > (frame_length_lines - IU060F_SN12M0PZ_OFFSET)) {
+		line_length_ratio = (line * Q8) / (frame_length_lines -
+					IU060F_SN12M0PZ_OFFSET);
+		temp_lines = frame_length_lines - IU060F_SN12M0PZ_OFFSET;
+		if (line_length_ratio == 0)
+			line_length_ratio = 1 * Q8;
+	} else
+		line_length_ratio = 1 * Q8;
+
+	line = (uint32_t) (line * sn12m0pz_ctrl->fps_divider/0x400);
+
+	/* update gain registers */
+	gain_msb = (uint8_t) ((gain & 0xFF00) >> 8);
+	gain_lsb = (uint8_t) (gain & 0x00FF);
+
+	/* linear AFR horizontal stretch */
+	line_length_pck = (uint16_t) (line_length_pck * line_length_ratio / Q8);
+	line_length_pck_msb = (uint8_t) ((line_length_pck & 0xFF00) >> 8);
+	line_length_pck_lsb = (uint8_t) (line_length_pck & 0x00FF);
+
+	/* update line count registers */
+	intg_time_msb = (uint8_t) ((temp_lines & 0xFF00) >> 8);
+	intg_time_lsb = (uint8_t) (temp_lines & 0x00FF);
+
+
+	if (sn12m0pz_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+			GROUPED_PARAMETER_HOLD) < 0)
+		return rc;
+
+	if (sn12m0pz_i2c_write_b_sensor(REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB,
+			gain_msb) < 0)
+		return rc;
+
+	if (sn12m0pz_i2c_write_b_sensor(REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB,
+			gain_lsb) < 0)
+		return rc;
+
+	if (sn12m0pz_i2c_write_b_sensor(REG_LINE_LENGTH_PCK_MSB,
+			line_length_pck_msb) < 0)
+		return rc;
+
+	if (sn12m0pz_i2c_write_b_sensor(REG_LINE_LENGTH_PCK_LSB,
+			line_length_pck_lsb) < 0)
+		return rc;
+
+	if (sn12m0pz_i2c_write_b_sensor(REG_COARSE_INTEGRATION_TIME_MSB,
+			intg_time_msb) < 0)
+		return rc;
+
+	if (sn12m0pz_i2c_write_b_sensor(REG_COARSE_INTEGRATION_TIME_LSB,
+			intg_time_lsb) < 0)
+		return rc;
+
+	if (sn12m0pz_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+			GROUPED_PARAMETER_HOLD_OFF) < 0)
+		return rc;
+
+	return rc;
+}
+
+
+static int32_t sn12m0pz_set_pict_exp_gain(uint16_t gain, uint32_t line)
+{
+	int32_t rc;
+	rc = sn12m0pz_write_exp_gain(gain, line);
+	return rc;
+}
+
+static int32_t sn12m0pz_test(enum sn12m0pz_test_mode_t mo)
+{
+	uint8_t test_data_val_msb = 0x07;
+	uint8_t test_data_val_lsb = 0xFF;
+	int32_t rc = 0;
+	if (mo == TEST_OFF)
+		return rc;
+	else {
+		/* REG_0x30D8[4] is TESBYPEN: 0: Normal Operation,
+		 1: Bypass Signal Processing. REG_0x30D8[5] is EBDMASK:
+		 0: Output Embedded data, 1: No output embedded data */
+
+		if (sn12m0pz_i2c_write_b_sensor(REG_0x30D8, 0x10) < 0)
+			return rc;
+
+		if (sn12m0pz_i2c_write_b_sensor(REG_TEST_PATTERN_MODE,
+			(uint8_t) mo) < 0)
+			return rc;
+
+		/* Solid Color Test Pattern */
+
+		if (mo == TEST_1) {
+			if (sn12m0pz_i2c_write_b_sensor(REG_TEST_DATA_RED_MSB,
+				test_data_val_msb) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_TEST_DATA_RED_LSB,
+				test_data_val_lsb) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(
+						REG_TEST_DATA_GREENR_MSB,
+						test_data_val_msb) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(
+						REG_TEST_DATA_GREENR_LSB,
+						test_data_val_lsb) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_TEST_DATA_BLUE_MSB,
+				test_data_val_msb) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_TEST_DATA_BLUE_LSB,
+				test_data_val_lsb) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(
+						REG_TEST_DATA_GREENB_MSB,
+						test_data_val_msb) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(
+						REG_TEST_DATA_GREENB_LSB,
+						test_data_val_lsb) < 0)
+				return rc;
+		}
+
+	}
+
+	return rc;
+}
+
+static int32_t sn12m0pz_reset(void)
+{
+	int32_t rc = 0;
+	/* register 0x0002 is Port 2, CAM_XCLRO */
+	gpio_direction_output(sn12m0pz_ctrl->
+		sensordata->sensor_reset,
+		0);
+	msleep(50);
+	gpio_direction_output(sn12m0pz_ctrl->
+		sensordata->sensor_reset,
+		1);
+	msleep(13);
+	return rc;
+}
+
+static int32_t sn12m0pz_sensor_setting(int update_type, int rt)
+{
+	uint16_t total_lines_per_frame;
+	int32_t rc = 0;
+
+	switch (update_type) {
+	case UPDATE_PERIODIC:
+		/* Put Sensor into sofware standby mode	*/
+		if (sn12m0pz_i2c_write_b_sensor(REG_MODE_SELECT,
+				MODE_SELECT_STANDBY_MODE) <  0)
+			return rc;
+		msleep(5);
+		/* Hardware reset D02, lane config between full size/qtr size*/
+		rc = sn12m0pz_reset();
+		if (rc < 0)
+			return rc;
+
+		if (sn12m0pz_stmipid02_config() < 0)
+			return rc;
+	case REG_INIT:
+		if (rt == RES_PREVIEW || rt == RES_CAPTURE
+				|| rt == RES_VIDEO_120FPS) {
+			/* reset fps_divider */
+			sn12m0pz_ctrl->fps_divider = 1 * 0x400;
+
+			/* PLL settings */
+			if (sn12m0pz_i2c_write_b_sensor(REG_PLL_MULTIPLIER,
+			sn12m0pz_regs.reg_pat[rt].pll_multiplier_lsb) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x302B,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x302B) < 0)
+				return rc;
+
+			/* MIPI Enable Settings */
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x30E5,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x30E5) < 0)
+				return rc;
+
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3300,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3300) < 0)
+				return rc;
+
+			/* Global Setting */
+			if (
+				sn12m0pz_i2c_write_b_sensor(
+				REG_IMAGE_ORIENTATION,
+				sn12m0pz_regs.reg_pat_init[0].image_orient) < 0)
+				return rc;
+			if (
+				sn12m0pz_i2c_write_b_sensor(
+				REG_COARSE_INTEGRATION_TIME_MSB,
+				sn12m0pz_regs.reg_pat[rt].coarse_integ_time_msb)
+				< 0)
+				return rc;
+			if (
+				sn12m0pz_i2c_write_b_sensor(
+				REG_COARSE_INTEGRATION_TIME_LSB,
+				sn12m0pz_regs.reg_pat[rt].coarse_integ_time_lsb)
+				 < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x300A,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x300A) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3014,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3014) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3015,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3015) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3017,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3017) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x301C,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x301C) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3031,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3031) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3040,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3040) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3041,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3041) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3051,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3051) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3053,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3053) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3055,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3055) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3057,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3057) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3060,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3060) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3065,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3065) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x30AA,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x30AA) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x30AB,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x30AB) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x30B0,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x30B0) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x30B2,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x30B2) < 0)
+				return rc;
+
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x30D3,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x30D3) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x30D8,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x30D8) < 0)
+				return rc;
+
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3106,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3106) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3108,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3108) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x310A,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x310A) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x310C,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x310C) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x310E,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x310E) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3126,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3126) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x312E,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x312E) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x313C,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x313C) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x313E,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x313E) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3140,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3140) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3142,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3142) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3144,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3144) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3148,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3148) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x314A,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x314A) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3166,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3166) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3168,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3168) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x316F,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x316F) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3171,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3171) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3173,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3173) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3175,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3175) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3177,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3177) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3179,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3179) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x317B,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x317B) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x317D,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x317D) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x317F,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x317F) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3181,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3181) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3184,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3184) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3185,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3185) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3187,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3187) < 0)
+				return rc;
+
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x31A4,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x31A4) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x31A6,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x31A6) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x31AC,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x31AC) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x31AE,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x31AE) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x31B4,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x31B4) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x31B6,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x31B6) < 0)
+				return rc;
+
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3254,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3254) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3256,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3256) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3258,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3258) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x325A,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x325A) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3260,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3260) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3262,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3262) < 0)
+				return rc;
+
+
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3304,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3304) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3305,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3305) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3306,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3306) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3307,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3307) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3308,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3308) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3309,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x3309) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x330A,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x330A) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x330B,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x330B) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x330C,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x330C) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x330D,
+				sn12m0pz_regs.reg_pat_init[0].reg_0x330D) < 0)
+				return rc;
+
+			/* Mode setting */
+			/* Update registers with correct
+				 frame_length_line value for AFR */
+			total_lines_per_frame = (uint16_t)(
+			(sn12m0pz_regs.reg_pat[rt].frame_length_lines_msb << 8)
+			& 0xFF00) +
+			sn12m0pz_regs.reg_pat[rt].frame_length_lines_lsb;
+			total_lines_per_frame = total_lines_per_frame *
+					sn12m0pz_ctrl->fps_divider / 0x400;
+
+			if (sn12m0pz_i2c_write_b_sensor(
+					REG_FRAME_LENGTH_LINES_MSB,
+					(total_lines_per_frame & 0xFF00) >> 8)
+					< 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(
+					REG_FRAME_LENGTH_LINES_LSB,
+					(total_lines_per_frame & 0x00FF)) < 0)
+				return rc;
+
+			if (sn12m0pz_i2c_write_b_sensor(REG_LINE_LENGTH_PCK_MSB,
+				sn12m0pz_regs.reg_pat[rt].line_length_pck_msb) <
+				0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_LINE_LENGTH_PCK_LSB,
+				sn12m0pz_regs.reg_pat[rt].line_length_pck_lsb) <
+				0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_X_OUTPUT_SIZE_MSB,
+				sn12m0pz_regs.reg_pat[rt].x_output_size_msb) <
+				0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_X_OUTPUT_SIZE_LSB,
+				sn12m0pz_regs.reg_pat[rt].x_output_size_lsb) <
+				0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_Y_OUTPUT_SIZE_MSB,
+				sn12m0pz_regs.reg_pat[rt].y_output_size_msb) <
+				0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_Y_OUTPUT_SIZE_LSB,
+				sn12m0pz_regs.reg_pat[rt].y_output_size_lsb) <
+				0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_X_EVEN_INC_LSB,
+				sn12m0pz_regs.reg_pat[rt].x_even_inc_lsb) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_X_ODD_INC_LSB,
+				sn12m0pz_regs.reg_pat[rt].x_odd_inc_lsb) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_Y_EVEN_INC_LSB,
+				sn12m0pz_regs.reg_pat[rt].y_even_inc_lsb) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_Y_ODD_INC_LSB,
+				sn12m0pz_regs.reg_pat[rt].y_odd_inc_lsb) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3016,
+				sn12m0pz_regs.reg_pat[rt].reg_0x3016) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x30E8,
+				sn12m0pz_regs.reg_pat[rt].reg_0x30E8) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x3301,
+				sn12m0pz_regs.reg_pat[rt].reg_0x3301) < 0)
+				return rc;
+
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x0344,
+				sn12m0pz_regs.reg_pat[rt].reg_0x0344) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x0345,
+				sn12m0pz_regs.reg_pat[rt].reg_0x0345) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x0346,
+				sn12m0pz_regs.reg_pat[rt].reg_0x0346) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x0347,
+				sn12m0pz_regs.reg_pat[rt].reg_0x0347) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x0348,
+				sn12m0pz_regs.reg_pat[rt].reg_0x0348) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x0349,
+				sn12m0pz_regs.reg_pat[rt].reg_0x0349) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x034A,
+				sn12m0pz_regs.reg_pat[rt].reg_0x034A) < 0)
+				return rc;
+			if (sn12m0pz_i2c_write_b_sensor(REG_0x034B,
+				sn12m0pz_regs.reg_pat[rt].reg_0x034B) < 0)
+				return rc;
+
+			if ((rt == RES_CAPTURE) && fullsize_cropped_at_8mp) {
+				/* x address end */
+				if (sn12m0pz_i2c_write_b_sensor(0x0348,
+								0x0C) < 0)
+					return rc;
+				if (sn12m0pz_i2c_write_b_sensor(0x0349,
+								0x0CF) < 0)
+					return rc;
+				/* y address end */
+				if (sn12m0pz_i2c_write_b_sensor(0x034A,
+								0x09) < 0)
+					return rc;
+				if (sn12m0pz_i2c_write_b_sensor(0x034B,
+								0x9F) < 0)
+					return rc;
+			}
+
+			if (mipi_config == IU060F_SN12M0PZ_STMIPID01) {
+				if (sn12m0pz_i2c_write_b_sensor(
+						REG_PLL_MULTIPLIER, 0x43) < 0)
+					return rc;
+				if (rt == RES_CAPTURE) {
+					if (sn12m0pz_i2c_write_b_sensor(
+						REG_0x3301, 0x01) < 0)
+						return rc;
+				if (sn12m0pz_i2c_write_b_sensor(
+						REG_0x3017, 0xE0) < 0)
+					return rc;
+				}
+			}
+
+			if (sn12m0pz_i2c_write_b_sensor(REG_MODE_SELECT,
+						MODE_SELECT_STREAM) < 0)
+				return rc;
+
+			msleep(IU060F_SN12M0PZ_DELAY_MSECS);
+
+			if (sn12m0pz_test(sn12m0pz_ctrl->set_test) < 0)
+				return rc;
+
+			if (mipi_config == IU060F_SN12M0PZ_STMIPID02)
+				CDBG("%s,%d", __func__, __LINE__);
+			return rc;
+		}
+	default:
+		return rc;
+		}
+}
+
+
+static int32_t sn12m0pz_video_config(int mode)
+{
+
+	int32_t rc = 0;
+	int rt;
+
+
+	if (mode == SENSOR_HFR_120FPS_MODE)
+		sn12m0pz_ctrl->prev_res = QVGA_SIZE;
+
+	/* change sensor resolution if needed */
+	if (sn12m0pz_ctrl->curr_res != sn12m0pz_ctrl->prev_res) {
+		if (sn12m0pz_ctrl->prev_res == QTR_SIZE) {
+			rt = RES_PREVIEW;
+			IU060F_SN12M0PZ_DELAY_MSECS = 35; /*measured on scope*/
+			enable_single_D02_lane = 1;
+		} else if (sn12m0pz_ctrl->prev_res == QVGA_SIZE) {
+			rt = RES_VIDEO_120FPS;
+			IU060F_SN12M0PZ_DELAY_MSECS = 35; /*measured on scope*/
+			enable_single_D02_lane = 0;
+		} else {
+			rt = RES_CAPTURE;
+			enable_single_D02_lane = 0;
+		}
+
+		if (sn12m0pz_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+			return rc;
+	}
+
+	sn12m0pz_ctrl->curr_res = sn12m0pz_ctrl->prev_res;
+	sn12m0pz_ctrl->sensormode = mode;
+
+	return rc;
+}
+static int32_t sn12m0pz_snapshot_config(int mode)
+{
+	int32_t rc = 0;
+	int rt;
+	/* change sensor resolution if needed */
+	if (sn12m0pz_ctrl->curr_res != sn12m0pz_ctrl->pict_res) {
+		if (sn12m0pz_ctrl->pict_res == QTR_SIZE) {
+			rt = RES_PREVIEW;
+			enable_single_D02_lane = 1;
+		} else {
+			rt = RES_CAPTURE;
+			IU060F_SN12M0PZ_DELAY_MSECS = 100;/*measured on scope*/
+			enable_single_D02_lane = 0;
+		}
+
+		if (sn12m0pz_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+			return rc;
+	}
+
+	sn12m0pz_ctrl->curr_res = sn12m0pz_ctrl->pict_res;
+	sn12m0pz_ctrl->sensormode = mode;
+	return rc;
+}
+
+static int32_t sn12m0pz_raw_snapshot_config(int mode)
+{
+	int32_t rc = 0;
+	int rt;
+	/* change sensor resolution if needed */
+	if (sn12m0pz_ctrl->curr_res != sn12m0pz_ctrl->pict_res) {
+		if (sn12m0pz_ctrl->pict_res == QTR_SIZE) {
+			rt = RES_PREVIEW;
+			enable_single_D02_lane = 1;
+		} else {
+			rt = RES_CAPTURE;
+			IU060F_SN12M0PZ_DELAY_MSECS = 100;/*measured on scope*/
+			enable_single_D02_lane = 0;
+		}
+		if (sn12m0pz_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+			return rc;
+		}
+	sn12m0pz_ctrl->curr_res = sn12m0pz_ctrl->pict_res;
+	sn12m0pz_ctrl->sensormode = mode;
+	return rc;
+}
+static int32_t sn12m0pz_set_sensor_mode(int  mode,
+	int  res)
+{
+	int32_t rc;
+
+	switch (mode) {
+	case SENSOR_PREVIEW_MODE:
+	case SENSOR_HFR_120FPS_MODE:
+		rc = sn12m0pz_video_config(mode);
+		break;
+
+	case SENSOR_SNAPSHOT_MODE:
+		rc = sn12m0pz_snapshot_config(mode);
+		break;
+
+	case SENSOR_RAW_SNAPSHOT_MODE:
+		rc = sn12m0pz_raw_snapshot_config(mode);
+		break;
+
+	default:
+		rc = -EINVAL;
+		break;
+	}
+	return rc;
+}
+
+static int32_t sn12m0pz_power_down(void)
+{
+	return 0;
+}
+
+
+static int sn12m0pz_probe_init_done(const struct msm_camera_sensor_info *data)
+{
+
+	gpio_direction_output(data->sensor_reset, 0);
+	gpio_free(data->sensor_reset);
+	gpio_direction_output(data->vcm_pwd, 0);
+	gpio_free(data->vcm_pwd);
+	return 0;
+}
+
+static int sn12m0pz_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+	int32_t rc;
+	unsigned short chipidl, chipidh;
+	CDBG("Requesting gpio");
+	rc = gpio_request(data->sensor_reset, "sn12m0pz");
+	CDBG(" sn12m0pz_probe_init_sensor");
+	if (!rc) {
+		gpio_direction_output(data->sensor_reset, 0);
+		msleep(20);
+		gpio_direction_output(data->sensor_reset, 1);
+		msleep(13);
+	} else {
+		goto init_probe_done;
+	}
+	CDBG("Requestion gpio");
+	rc = gpio_request(data->vcm_pwd, "sn12m0pz");
+	CDBG(" sn12m0pz_probe_init_sensor");
+
+	if (!rc) {
+		gpio_direction_output(data->vcm_pwd, 0);
+		msleep(20);
+		gpio_direction_output(data->vcm_pwd, 1);
+		msleep(13);
+	} else {
+		gpio_direction_output(data->sensor_reset, 0);
+		gpio_free(data->sensor_reset);
+		goto init_probe_done;
+	}
+
+	msleep(20);
+
+	/* 3. Read sensor Model ID: */
+	rc = sn12m0pz_i2c_read(0x0000, &chipidh, 1);
+	if (rc < 0) {
+		CDBG(" sn12m0pz_probe_init_sensor3");
+		goto init_probe_fail;
+	}
+	rc = sn12m0pz_i2c_read(0x0001, &chipidl, 1);
+	if (rc < 0) {
+		CDBG(" sn12m0pz_probe_init_sensor4");
+		goto init_probe_fail;
+	}
+
+	/* 4. Compare sensor ID to SN12M0PZ ID: */
+	if (chipidh != 0x00 || chipidl != 0x60) {
+		rc = -ENODEV;
+		CDBG("sn12m0pz_probe_init_sensor fail chip id doesnot match");
+		goto init_probe_fail;
+	}
+
+	msleep(SN12M0PZ_RESET_DELAY_MSECS);
+
+	goto init_probe_done;
+
+init_probe_fail:
+	CDBG(" sn12m0pz_probe_init_sensor fails");
+	sn12m0pz_probe_init_done(data);
+
+init_probe_done:
+	CDBG(" sn12m0pz_probe_init_sensor finishes");
+	return rc;
+}
+
+int sn12m0pz_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+	int32_t rc = 0;
+	CDBG("Calling sn12m0pz_sensor_open_init");
+
+	sn12m0pz_ctrl = kzalloc(sizeof(struct sn12m0pz_ctrl_t), GFP_KERNEL);
+	if (!sn12m0pz_ctrl) {
+		CDBG("sn12m0pz_init failed!");
+		rc = -ENOMEM;
+		goto init_done;
+	}
+
+	sn12m0pz_ctrl->fps_divider      = 1 * 0x00000400;
+	sn12m0pz_ctrl->pict_fps_divider = 1 * 0x00000400;
+	sn12m0pz_ctrl->set_test = TEST_OFF;
+	sn12m0pz_ctrl->prev_res = QTR_SIZE;
+	sn12m0pz_ctrl->pict_res = FULL_SIZE;
+	sn12m0pz_ctrl->curr_res = INVALID_SIZE;
+	if (data)
+		sn12m0pz_ctrl->sensordata = data;
+
+	if (rc < 0)
+		return rc;
+
+	/* enable mclk first */
+	msm_camio_clk_rate_set(SN12M0PZ_DEFAULT_CLOCK_RATE);
+	msleep(20);
+	msm_camio_camif_pad_reg_reset();
+	msleep(20);
+	CDBG("Calling sn12m0pz_sensor_open_init");
+	rc = sn12m0pz_probe_init_sensor(data);
+
+	if (rc < 0)
+		goto init_fail;
+	/* send reset signal */
+	if (mipi_config == IU060F_SN12M0PZ_STMIPID01) {
+		if (sn12m0pz_stmipid01_config() < 0) {
+			CDBG("Calling sn12m0pz_sensor_open_init fail");
+			return rc;
+		}
+	} else {
+		if (sn12m0pz_ctrl->prev_res  == QTR_SIZE)
+			enable_single_D02_lane = 1;
+		else /* FULL_SIZE */
+			enable_single_D02_lane = 0;
+
+		if (sn12m0pz_stmipid02_config() < 0) {
+			CDBG("Calling sn12m0pz_sensor_open_init fail");
+			return rc;
+		}
+	}
+
+
+	if (sn12m0pz_ctrl->prev_res == QTR_SIZE) {
+		if (sn12m0pz_sensor_setting(REG_INIT, RES_PREVIEW) < 0)
+			return rc;
+	} else if (sn12m0pz_ctrl->prev_res == QVGA_SIZE) {
+		if (sn12m0pz_sensor_setting(REG_INIT, RES_VIDEO_120FPS) < 0)
+			return rc;
+	} else {
+		if (sn12m0pz_sensor_setting(REG_INIT, RES_CAPTURE) < 0)
+			return rc;
+	}
+
+	if (sn12m0pz_af_init() < 0)
+		return rc;
+	sn12m0pz_ctrl->fps = 30*Q8;
+	if (rc < 0)
+		goto init_fail;
+	else
+		goto init_done;
+init_fail:
+	CDBG(" init_fail");
+	sn12m0pz_probe_init_done(data);
+	kfree(sn12m0pz_ctrl);
+init_done:
+	CDBG("init_done");
+	return rc;
+}
+static int __devinit sn12m0pz_init_client(struct i2c_client *client)
+{
+	/* Initialize the MSM_CAMI2C Chip */
+	init_waitqueue_head(&sn12m0pz_wait_queue);
+	return 0;
+}
+
+static const struct i2c_device_id sn12m0pz_i2c_id[] = {
+	{ "sn12m0pz", 0},
+	{ }
+};
+
+static int __devinit sn12m0pz_i2c_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	int rc = 0;
+	CDBG("sn12m0pz_probe called!");
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		CDBG("i2c_check_functionality failed");
+		goto probe_failure;
+	}
+
+	sn12m0pz_sensorw = kzalloc(sizeof(struct sn12m0pz_work_t), GFP_KERNEL);
+	if (!sn12m0pz_sensorw) {
+		CDBG("kzalloc failed");
+		rc = -ENOMEM;
+		goto probe_failure;
+	}
+
+	i2c_set_clientdata(client, sn12m0pz_sensorw);
+	sn12m0pz_init_client(client);
+	sn12m0pz_client = client;
+
+	msleep(50);
+
+	CDBG("sn12m0pz_probe successed! rc = %d", rc);
+	return 0;
+
+probe_failure:
+	CDBG("sn12m0pz_probe failed! rc = %d", rc);
+	return rc;
+}
+
+static int __exit sn12m0pz_remove(struct i2c_client *client)
+{
+	struct sn12m0pz_work_t_t *sensorw = i2c_get_clientdata(client);
+	free_irq(client->irq, sensorw);
+	sn12m0pz_client = NULL;
+	kfree(sensorw);
+	return 0;
+}
+
+static struct i2c_driver sn12m0pz_i2c_driver = {
+	.id_table = sn12m0pz_i2c_id,
+	.probe	= sn12m0pz_i2c_probe,
+	.remove = __exit_p(sn12m0pz_i2c_remove),
+	.driver = {
+		.name = "sn12m0pz",
+	},
+};
+
+int sn12m0pz_sensor_config(void __user *argp)
+{
+	struct sensor_cfg_data cdata;
+	int32_t rc = 0;
+	if (copy_from_user(&cdata,
+				(void *)argp,
+				sizeof(struct sensor_cfg_data)))
+		return -EFAULT;
+
+	mutex_lock(&sn12m0pz_mut);
+
+	CDBG("sn12m0pz_sensor_config: cfgtype = %d",
+		cdata.cfgtype);
+	switch (cdata.cfgtype) {
+	case CFG_GET_PICT_FPS:
+		sn12m0pz_get_pict_fps(cdata.cfg.gfps.prevfps,
+					&(cdata.cfg.gfps.pictfps));
+
+		if (copy_to_user((void *)argp,
+			&cdata,
+			sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PREV_L_PF:
+		cdata.cfg.prevl_pf =
+			sn12m0pz_get_prev_lines_pf();
+
+		if (copy_to_user((void *)argp,
+			&cdata,
+			sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PREV_P_PL:
+		cdata.cfg.prevp_pl =
+			sn12m0pz_get_prev_pixels_pl();
+
+		if (copy_to_user((void *)argp,
+			&cdata,
+			sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PICT_L_PF:
+		cdata.cfg.pictl_pf =
+			sn12m0pz_get_pict_lines_pf();
+
+		if (copy_to_user((void *)argp,
+			&cdata,
+			sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PICT_P_PL:
+		cdata.cfg.pictp_pl =
+			sn12m0pz_get_pict_pixels_pl();
+
+		if (copy_to_user((void *)argp,
+			&cdata,
+			sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PICT_MAX_EXP_LC:
+		cdata.cfg.pict_max_exp_lc =
+			sn12m0pz_get_pict_max_exp_lc();
+
+		if (copy_to_user((void *)argp,
+			&cdata,
+			sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_SET_FPS:
+	case CFG_SET_PICT_FPS:
+		rc = sn12m0pz_set_fps(&(cdata.cfg.fps));
+		break;
+
+	case CFG_SET_EXP_GAIN:
+		rc =
+			sn12m0pz_write_exp_gain(
+				cdata.cfg.exp_gain.gain,
+				cdata.cfg.exp_gain.line);
+		break;
+	case CFG_SET_PICT_EXP_GAIN:
+		rc =
+			sn12m0pz_set_pict_exp_gain(
+				cdata.cfg.exp_gain.gain,
+				cdata.cfg.exp_gain.line);
+		break;
+
+	case CFG_SET_MODE:
+		rc = sn12m0pz_set_sensor_mode(cdata.mode,
+					cdata.rs);
+		break;
+
+	case CFG_PWR_DOWN:
+		rc = sn12m0pz_power_down();
+		break;
+
+	case CFG_MOVE_FOCUS:
+		rc = sn12m0pz_move_focus(cdata.cfg.focus.dir,
+					cdata.cfg.focus.steps);
+		break;
+
+	case CFG_SET_DEFAULT_FOCUS:
+		rc = sn12m0pz_set_default_focus(cdata.cfg.focus.steps);
+		break;
+
+	case CFG_SET_EFFECT:
+		rc = 0;
+		break;
+	case CFG_SET_LENS_SHADING:
+		rc = 0;
+		break;
+	default:
+		rc = -EFAULT;
+		break;
+	}
+
+	mutex_unlock(&sn12m0pz_mut);
+
+	return rc;
+}
+
+static int sn12m0pz_sensor_release(void)
+{
+	int rc = -EBADF;
+
+	mutex_lock(&sn12m0pz_mut);
+
+	sn12m0pz_power_down();
+
+	gpio_direction_output(sn12m0pz_ctrl->sensordata->sensor_reset,
+		0);
+	gpio_free(sn12m0pz_ctrl->sensordata->sensor_reset);
+
+	gpio_direction_output(sn12m0pz_ctrl->sensordata->vcm_pwd,
+		0);
+	gpio_free(sn12m0pz_ctrl->sensordata->vcm_pwd);
+
+	kfree(sn12m0pz_ctrl);
+	sn12m0pz_ctrl = NULL;
+
+	CDBG("sn12m0pz_release completed");
+
+
+	mutex_unlock(&sn12m0pz_mut);
+
+	return rc;
+}
+
+static int sn12m0pz_sensor_probe(const struct msm_camera_sensor_info *info,
+		struct msm_sensor_ctrl *s)
+{
+	int rc;
+
+	rc = i2c_add_driver(&sn12m0pz_i2c_driver);
+	if (rc < 0 || sn12m0pz_client == NULL) {
+		rc = -ENOTSUPP;
+		goto probe_fail;
+	}
+
+	msm_camio_clk_rate_set(SN12M0PZ_DEFAULT_CLOCK_RATE);
+	msleep(20);
+
+	rc = sn12m0pz_probe_init_sensor(info);
+	if (rc < 0)
+		goto probe_fail;
+
+	s->s_init = sn12m0pz_sensor_open_init;
+	s->s_release = sn12m0pz_sensor_release;
+	s->s_config  = sn12m0pz_sensor_config;
+	s->s_mount_angle  = 0;
+	sn12m0pz_probe_init_done(info);
+
+	return rc;
+
+probe_fail:
+	CDBG("SENSOR PROBE FAILS!");
+	i2c_del_driver(&sn12m0pz_i2c_driver);
+	return rc;
+}
+
+static int __sn12m0pz_probe(struct platform_device *pdev)
+{
+	return msm_camera_drv_start(pdev, sn12m0pz_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+	.probe = __sn12m0pz_probe,
+	.driver = {
+		.name = "msm_camera_sn12m0pz",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init sn12m0pz_init(void)
+{
+	return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(sn12m0pz_init);
+
+MODULE_DESCRIPTION("Sony 12M MP Bayer sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/sn12m0pz.h b/drivers/media/video/msm/sn12m0pz.h
new file mode 100644
index 0000000..f2abc47
--- /dev/null
+++ b/drivers/media/video/msm/sn12m0pz.h
@@ -0,0 +1,138 @@
+
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef SN12M0PZ_H
+#define SN12M0PZ_H
+
+#include <linux/types.h>
+extern struct sn12m0pz_reg sn12m0pz_regs; /* from mt9t013_reg.c */
+struct reg_struct{
+	uint8_t pll_multiplier_lsb;            /* 0x0307*/
+	uint8_t coarse_integ_time_msb;   /* 0x0202*/
+	uint8_t coarse_integ_time_lsb;   /* 0x0203*/
+	uint8_t frame_length_lines_msb;        /* 0x0340*/
+	uint8_t frame_length_lines_lsb;        /* 0x0341*/
+	uint8_t line_length_pck_msb;           /* 0x0342*/
+	uint8_t line_length_pck_lsb;           /* 0x0343*/
+	uint8_t x_output_size_msb;             /* 0x034C*/
+	uint8_t x_output_size_lsb;             /* 0x034D*/
+	uint8_t y_output_size_msb;             /* 0x034E*/
+	uint8_t y_output_size_lsb;             /* 0x034F*/
+	uint8_t x_even_inc_lsb;                /* 0x0381*/
+	uint8_t x_odd_inc_lsb;                 /* 0x0383*/
+	uint8_t y_even_inc_lsb;                /* 0x0385*/
+	uint8_t y_odd_inc_lsb;                 /* 0x0387*/
+	uint8_t reg_0x3016;                    /* 0x3016 VMODEADD*/
+	uint8_t reg_0x30E8;                    /* 0x30E8 HADDAVE*/
+	uint8_t reg_0x3301;                    /* 0x3301 RGLANESEL*/
+	/*added for 120fps support */
+	uint8_t reg_0x0344;
+	uint8_t reg_0x0345;
+	uint8_t reg_0x0346;
+	uint8_t reg_0x0347;
+	uint8_t reg_0x0348;
+	uint8_t reg_0x0349;
+	uint8_t reg_0x034A;
+	uint8_t reg_0x034B;
+};
+struct reg_struct_init{
+	uint8_t reg_0x302B;/* 0x302B*/
+
+	uint8_t reg_0x30E5;/* 0x30E5*/
+	uint8_t reg_0x3300;   /* 0x3300*/
+
+	uint8_t image_orient;   /* 0x0101*/
+
+	uint8_t reg_0x300A;   /* 0x300A*/
+	uint8_t reg_0x3014;   /* 0x3014*/
+	uint8_t reg_0x3015;   /* 0x3015*/
+	uint8_t reg_0x3017;   /* 0x3017*/
+	uint8_t reg_0x301C;   /* 0x301C*/
+	uint8_t reg_0x3031;   /* 0x3031*/
+	uint8_t reg_0x3040;   /* 0x3040*/
+	uint8_t reg_0x3041;   /* 0x3041*/
+	uint8_t reg_0x3051;   /* 0x3051*/
+	uint8_t reg_0x3053;   /* 0x3053*/
+	uint8_t reg_0x3055;   /* 0x3055*/
+	uint8_t reg_0x3057;   /* 0x3057*/
+	uint8_t reg_0x3060;   /* 0x3060*/
+	uint8_t reg_0x3065;   /* 0x3065*/
+	uint8_t reg_0x30AA;   /* 0x30AA*/
+	uint8_t reg_0x30AB;   /* 0x30AB*/
+	uint8_t reg_0x30B0;   /* 0x30B0*/
+	uint8_t reg_0x30B2;   /* 0x30B2*/
+
+	uint8_t reg_0x30D3;   /* 0X30D3*/
+	uint8_t reg_0x30D8;   /* 0X30D8*/
+
+	uint8_t reg_0x3106;   /* 0x3106*/
+	uint8_t reg_0x3108;   /* 0x3108*/
+	uint8_t reg_0x310A;   /* 0x310A*/
+	uint8_t reg_0x310C;   /* 0x310C*/
+	uint8_t reg_0x310E;   /* 0x310E*/
+	uint8_t reg_0x3126;   /* 0x3126*/
+	uint8_t reg_0x312E;   /* 0x312E*/
+	uint8_t reg_0x313C;   /* 0x313C*/
+	uint8_t reg_0x313E;   /* 0x313E*/
+	uint8_t reg_0x3140;   /* 0x3140*/
+	uint8_t reg_0x3142;   /* 0x3142*/
+	uint8_t reg_0x3144;   /* 0x3144*/
+	uint8_t reg_0x3148;   /* 0x3148*/
+	uint8_t reg_0x314A;   /* 0x314A*/
+	uint8_t reg_0x3166;   /* 0x3166*/
+	uint8_t reg_0x3168;   /* 0x3168*/
+	uint8_t reg_0x316F;   /* 0x316F*/
+	uint8_t reg_0x3171;   /* 0x3171*/
+	uint8_t reg_0x3173;   /* 0x3173*/
+	uint8_t reg_0x3175;   /* 0x3175*/
+	uint8_t reg_0x3177;   /* 0x3177*/
+	uint8_t reg_0x3179;   /* 0x3179*/
+	uint8_t reg_0x317B;   /* 0x317B*/
+	uint8_t reg_0x317D;   /* 0x317D*/
+	uint8_t reg_0x317F;   /* 0x317F*/
+	uint8_t reg_0x3181;   /* 0x3181*/
+	uint8_t reg_0x3184;   /* 0x3184*/
+	uint8_t reg_0x3185;   /* 0x3185*/
+	uint8_t reg_0x3187;   /* 0x3187*/
+
+	uint8_t reg_0x31A4;   /* 0x31A4*/
+	uint8_t reg_0x31A6;   /* 0x31A6*/
+	uint8_t reg_0x31AC;   /* 0x31AC*/
+	uint8_t reg_0x31AE;   /* 0x31AE*/
+	uint8_t reg_0x31B4;   /* 0x31B4*/
+	uint8_t reg_0x31B6;   /* 0x31B6*/
+
+	uint8_t reg_0x3254;   /* 0x3254*/
+	uint8_t reg_0x3256;   /* 0x3256*/
+	uint8_t reg_0x3258;   /* 0x3258*/
+	uint8_t reg_0x325A;   /* 0x325A*/
+	uint8_t reg_0x3260;   /* 0x3260*/
+	uint8_t reg_0x3262;   /* 0x3262*/
+
+	uint8_t reg_0x3304;   /* 0x3304*/
+	uint8_t reg_0x3305;   /* 0x3305*/
+	uint8_t reg_0x3306;   /* 0x3306*/
+	uint8_t reg_0x3307;   /* 0x3307*/
+	uint8_t reg_0x3308;   /* 0x3308*/
+	uint8_t reg_0x3309;   /* 0x3309*/
+	uint8_t reg_0x330A;   /* 0x330A*/
+	uint8_t reg_0x330B;   /* 0x330B*/
+	uint8_t reg_0x330C;   /* 0x330C*/
+	uint8_t reg_0x330D;   /* 0x330D*/
+
+};
+struct sn12m0pz_reg{
+	const struct reg_struct  *reg_pat;
+	const struct reg_struct_init  *reg_pat_init;
+};
+#endif
diff --git a/drivers/media/video/msm/sn12m0pz_reg.c b/drivers/media/video/msm/sn12m0pz_reg.c
new file mode 100644
index 0000000..d21eac1
--- /dev/null
+++ b/drivers/media/video/msm/sn12m0pz_reg.c
@@ -0,0 +1,213 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "sn12m0pz.h"
+/* Initialisation settings */
+
+const struct reg_struct_init iu060f_reg_pat_init[1] = {{
+	/* PLL setting */
+	0x4B, /* reg 0x302B*/
+	/* MIPI Enable Setting */
+	0x04, /* reg 0x30E5*/
+	0x00, /* reg 0x3300*/
+	/* Global Setting */
+	0x00, /* image_orientation*/
+	0x80, /* reg 0x300A*/
+	0x08, /* reg 0x3014*/
+	0x37, /* reg 0x3015*/
+	0x60, /* reg 0x3017*/
+	0x01, /* reg 0x301C*/
+	0x28, /* reg 0x3031*/
+	0x00, /* reg 0x3040*/
+	0x60, /* reg 0x3041*/
+	0x24, /* reg 0x3051*/
+	0x34, /* reg 0x3053*/
+	0x3B, /* reg 0x3055*/
+	0xC0, /* reg 0x3057*/
+	0x30, /* reg 0x3060*/
+	0x00, /* reg 0x3065*/
+	0x88, /* reg 0x30AA*/
+	0x1C, /* reg 0x30AB*/
+	0x32, /* reg 0x30B0*/
+	0x83, /* reg 0x30B2*/
+	0x04, /* reg 0x30D3*/
+	0xC0, /* reg 0x30D8*/
+	0x50, /* reg 0x3106*/
+	0xA5, /* reg 0x3108*/
+	0xA9, /* reg 0x310A*/
+	0x0C, /* reg 0x310C*/
+	0x55, /* reg 0x310E*/
+	0xCC, /* reg 0x3126*/
+	0x83, /* reg 0x312E*/
+	0xC7, /* reg 0x313C*/
+	0x07, /* reg 0x313E*/
+	0x32, /* reg 0x3140*/
+	0x35, /* reg 0x3142*/
+	0x35, /* reg 0x3144*/
+	0x73, /* reg 0x3148*/
+	0x80, /* reg 0x314A*/
+	0xBE, /* reg 0x3166*/
+	0xBD, /* reg 0x3168*/
+	0x82, /* reg 0x316F*/
+	0xBC, /* reg 0x3171*/
+	0x82, /* reg 0x3173*/
+	0xBC, /* reg 0x3175*/
+	0x0C, /* reg 0x3177*/
+	0x2C, /* reg 0x3179*/
+	0x83, /* reg 0x317B*/
+	0xAF, /* reg 0x317D*/
+	0x83, /* reg 0x317F*/
+	0xAF, /* reg 0x3181*/
+	0x06, /* reg 0x3184*/
+	0xBA, /* reg 0x3185*/
+	0xBE, /* reg 0x3187*/
+	0xD8, /* reg 0x31A4*/
+	0x17, /* reg 0x31A6*/
+	0xCF, /* reg 0x31AC*/
+	0xF1, /* reg 0x31AE*/
+	0xD8, /* reg 0x31B4*/
+	0x17, /* reg 0x31B6*/
+	0x09, /* reg 0x3254 */
+	0xC5, /* reg 0x3256 */
+	0x84, /* reg 0x3258 */
+	0x6C, /* reg 0x325A */
+	0x0B, /* reg 0x3260 */
+	0x09, /* reg 0x3262 */
+	0x05, /* reg 0x3304*/
+	0x04, /* reg 0x3305*/
+	0x15, /* reg 0x3306*/
+	0x03, /* reg 0x3307*/
+	0x13, /* reg 0x3308*/
+	0x05, /* reg 0x3309*/
+	0x0B, /* reg 0x330A*/
+	0x04, /* reg 0x330B*/
+	0x0B, /* reg 0x330C*/
+	0x06  /* reg 0x330D*/
+}
+};
+
+/* Preview / Snapshot register settings */
+const struct reg_struct iu060f_reg_pat[3] = {
+	{ /* Preview */
+		0x22, /*0x1b*/ /* fps*/
+
+		/* Global Setting */
+		0x01, /* coarse_integration_time_msb*/
+		0xFF, /* coarse_integration_time_lsb*/
+
+		/* Mode Setting */
+		/* V: 1/2 V-addition (1,3),
+		H: 1/2 H-averaging (1,3) */
+
+		0x06, /* frame_length_lines_msb     0x0340*/
+		0x02, /* frame_length_lines_lsb     0x0341*/
+		0x10, /* line_length_pck_msb        0x0342*/
+		0x70, /* line_length_pck_lsb        0x0343*/
+		0x07, /* x_output_size_msb          0x034C*/
+		0xe0, /* x_output_size_lsb          0x034D*/
+		0x05, /* y_output_size_msb          0x034E*/
+		0xe8, /* y_output_size_lsb          0x034F*/
+		0x01, /* x_even_inc_lsb             0x0381*/
+		0x03, /* x_odd_inc_lsb              0x0383*/
+		0x01, /* y_even_inc_lsb             0x0385*/
+		0x03, /* y_odd_inc_lsb              0x0387*/
+		0x46, /* reg 0x3016 VMODEADD        0x3016*/
+		0x86, /* reg 0x30E8 HADDAVE         0x30E8*/
+		0x01, /* reg 0x3301 RGLANESEL       0x3301*/
+
+		0x00,  /* 0x0344 */
+		0x00,  /* 0x0345 */
+		0x00,  /* 0x0346 */
+		0x00,  /* 0x0347 */
+		0x0F,  /* 0x0348 */
+		0xBF,  /* 0x0349 */
+		0x0B,  /* 0x034A */
+		0xCF,  /* 0x034B */
+	},
+	{ /* Snapshot */
+		0x14, /* pll_multiplier_lsb    // 20/10 fps*/
+		/* 0x14 for pclk 96MHz at 7.5 fps */
+
+		/* Global Setting */
+		0x0B, /* coarse_integration_time_msb*/
+		0xFF, /* coarse_integration_time_lsb*/
+
+		/* Mode Setting */
+		/* Full */
+		0x0C,/*frame_length_lines_msb 0x0340*/
+		0x02,/*frame_length_lines_lsb 0x0341*/
+		0x10,/*line_length_pck_msb 0x0342*/
+		0x70,/* line_length_pck_lsb 0x0343*/
+		0x0F,/* x_output_size_msb   0x034C*/
+		0xC0, /* x_output_size_lsb  0x034D*/
+		0x0B, /* y_output_size_msb  0x034E*/
+		0xD0, /* y_output_size_lsb  0x034F*/
+		0x01, /* x_even_inc_lsb     0x0381*/
+		0x01, /* x_odd_inc_lsb      0x0383*/
+		0x01, /* y_even_inc_lsb                     0x0385*/
+		0x01, /* y_odd_inc_lsb                      0x0387*/
+		0x06, /* reg 0x3016 VMODEADD                0x3016*/
+		0x06, /* reg 0x30E8 HADDAVE                 0x30E8*/
+		0x00, /* reg 0x3301 RGLANESEL               0x3301*/
+
+		0x00,  /* 0x0344 */
+		0x00,  /* 0x0345 */
+		0x00,  /* 0x0346 */
+		0x00,  /* 0x0347 */
+		0x0F,  /* 0x0348 */
+		0xBF,  /* 0x0349 */
+		0x0B,  /* 0x034A */
+		0xCF,  /* 0x034B */
+	},
+	/* 120 fps settings */
+	{
+		0x1B, /*0x1B fps*/
+		/* Global Setting */
+		0x00, /* coarse_integration_time_msb*/
+		0xFE, /* coarse_integration_time_lsb*/
+
+		/* Mode Setting */
+		/* V: 1/8 V-addition (9,7),
+		H: Full */
+
+		0x01, /* frame_length_lines_msb     0x0340*/
+		0x01, /* frame_length_lines_lsb     0x0341*/
+		0x10, /* line_length_pck_msb        0x0342*/
+		0x70, /* line_length_pck_lsb        0x0343*/
+		0x0f, /* x_output_size_msb          0x034C*/
+		0xc0, /* x_output_size_lsb          0x034D*/
+		0x00, /* y_output_size_msb          0x034E*/
+		0xF8, /* y_output_size_lsb          0x034F*/
+		0x01, /* x_even_inc_lsb             0x0381*/
+		0x01, /* x_odd_inc_lsb              0x0383*/
+		0x09, /* y_even_inc_lsb             0x0385*/
+		0x07, /* y_odd_inc_lsb              0x0387*/
+		0x46, /* reg 0x3016 VMODEADD        0x3016*/
+		0x86, /* reg 0x30E8 HADDAVE         0x30E8*/
+		0x00, /* reg 0x3301 RGLANESEL       0x3301*/
+		/* add for 120fps support */
+		0x00, /* 0x0344*/
+		0x00, /* 0x0345*/
+		0x02, /* 0x0346*/
+		0x10, /* 0x0347*/
+		0x0F, /* 0x0348*/
+		0xBF, /* 0x0349*/
+		0x09, /* 0x034A*/
+		0xCF, /* 0x034B*/
+	}
+};
+struct sn12m0pz_reg sn12m0pz_regs = {
+	.reg_pat = &iu060f_reg_pat[0],
+	.reg_pat_init = &iu060f_reg_pat_init[0],
+};
+
diff --git a/drivers/media/video/msm/vb6801.c b/drivers/media/video/msm/vb6801.c
new file mode 100644
index 0000000..fa82570
--- /dev/null
+++ b/drivers/media/video/msm/vb6801.c
@@ -0,0 +1,1616 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <media/msm_camera.h>
+#include <mach/gpio.h>
+#include <mach/camera.h>
+#include "vb6801.h"
+
+/*=============================================================
+	SENSOR REGISTER DEFINES
+==============================================================*/
+enum {
+	REG_HOLD = 0x0104,
+	RELEASE_HOLD = 0x0000,
+	HOLD = 0x0001,
+	STANDBY_MODE = 0x0000,
+	REG_COARSE_INTEGRATION_TIME = 0x0202,
+	REG_ANALOGUE_GAIN_CODE_GLOBAL = 0x0204,
+	REG_RAMP_SCALE = 0x3116,
+	REG_POWER_MAN_ENABLE_3 = 0x3142,
+	REG_POWER_MAN_ENABLE_4 = 0x3143,
+	REG_POWER_MAN_ENABLE_5 = 0x3144,
+	REG_CCP2_DATA_FORMAT = 0x0112,
+	REG_PRE_PLL_CLK_DIV = 0x0304,
+	REG_PLL_MULTIPLIER = 0x0306,
+	REG_VT_SYS_CLK_DIV = 0x0302,
+	REG_VT_PIX_CLK_DIV = 0x0300,
+	REG_OP_SYS_CLK_DIV = 0x030A,
+	REG_OP_PIX_CLK_DIV = 0x0308,
+	REG_VT_LINE_LENGTH_PCK = 0x0342,
+	REG_X_OUTPUT_SIZE = 0x034C,
+	REG_Y_OUTPUT_SIZE = 0x034E,
+	REG_X_ODD_INC = 0x0382,
+	REG_Y_ODD_INC = 0x0386,
+	REG_VT_FRAME_LENGTH_LINES = 0x0340,
+	REG_ANALOG_TIMING_MODES_2 = 0x3113,
+	REG_BRUCE_ENABLE = 0x37B0,
+	REG_OP_CODER_SYNC_CLK_SETUP = 0x3400,
+	REG_OP_CODER_ENABLE = 0x3401,
+	REG_OP_CODER_SLOW_PAD_EN = 0x3402,
+	REG_OP_CODER_AUTO_STARTUP = 0x3414,
+	REG_SCYTHE_ENABLE = 0x3204,
+	REG_SCYTHE_WEIGHT = 0x3206,
+	REG_FRAME_COUNT = 0x0005,
+	REG_MODE_SELECT = 0x0100,
+	REG_CCP2_CHANNEL_IDENTIFIER = 0x0110,
+	REG_CCP2_SIGNALLING_MODE = 0x0111,
+	REG_BTL_LEVEL_SETUP = 0x311B,
+	REG_OP_CODER_AUTOMATIC_MODE_ENABLE = 0x3403,
+	REG_PLL_CTRL = 0x3801,
+	REG_VCM_DAC_CODE = 0x3860,
+	REG_VCM_DAC_STROBE = 0x3868,
+	REG_VCM_DAC_ENABLE = 0x386C,
+	REG_NVM_T1_ADDR_00 = 0x3600,
+	REG_NVM_T1_ADDR_01 = 0x3601,
+	REG_NVM_T1_ADDR_02 = 0x3602,
+	REG_NVM_T1_ADDR_03 = 0x3603,
+	REG_NVM_T1_ADDR_04 = 0x3604,
+	REG_NVM_T1_ADDR_05 = 0x3605,
+	REG_NVM_T1_ADDR_06 = 0x3606,
+	REG_NVM_T1_ADDR_07 = 0x3607,
+	REG_NVM_T1_ADDR_08 = 0x3608,
+	REG_NVM_T1_ADDR_09 = 0x3609,
+	REG_NVM_T1_ADDR_0A = 0x360A,
+	REG_NVM_T1_ADDR_0B = 0x360B,
+	REG_NVM_T1_ADDR_0C = 0x360C,
+	REG_NVM_T1_ADDR_0D = 0x360D,
+	REG_NVM_T1_ADDR_0E = 0x360E,
+	REG_NVM_T1_ADDR_0F = 0x360F,
+	REG_NVM_T1_ADDR_10 = 0x3610,
+	REG_NVM_T1_ADDR_11 = 0x3611,
+	REG_NVM_T1_ADDR_12 = 0x3612,
+	REG_NVM_T1_ADDR_13 = 0x3613,
+	REG_NVM_CTRL = 0x3680,
+	REG_NVM_PDN = 0x3681,
+	REG_NVM_PULSE_WIDTH = 0x368B,
+};
+
+#define VB6801_LINES_PER_FRAME_PREVIEW   800
+#define VB6801_LINES_PER_FRAME_SNAPSHOT 1600
+#define VB6801_PIXELS_PER_LINE_PREVIEW  2500
+#define VB6801_PIXELS_PER_LINE_SNAPSHOT 2500
+
+/* AF constant */
+#define VB6801_TOTAL_STEPS_NEAR_TO_FAR    25
+#define VB6801_STEPS_NEAR_TO_CLOSEST_INF  25
+
+/* for 30 fps preview */
+#define VB6801_DEFAULT_CLOCK_RATE    12000000
+
+enum vb6801_test_mode_t {
+	TEST_OFF,
+	TEST_1,
+	TEST_2,
+	TEST_3
+};
+
+enum vb6801_resolution_t {
+	QTR_SIZE,
+	FULL_SIZE,
+	INVALID_SIZE
+};
+
+enum vb6801_setting_t {
+	RES_PREVIEW,
+	RES_CAPTURE
+};
+
+struct vb6801_work_t {
+	struct work_struct work;
+};
+
+struct sensor_dynamic_params_t {
+	uint16_t preview_pixelsPerLine;
+	uint16_t preview_linesPerFrame;
+	uint16_t snapshot_pixelsPerLine;
+	uint16_t snapshot_linesPerFrame;
+	uint8_t snapshot_changed_fps;
+	uint32_t pclk;
+};
+
+struct vb6801_sensor_info {
+	/* Sensor Configuration Input Parameters */
+	uint32_t ext_clk_freq_mhz;
+	uint32_t target_frame_rate_fps;
+	uint32_t target_vt_pix_clk_freq_mhz;
+	uint32_t sub_sampling_factor;
+	uint32_t analog_binning_allowed;
+	uint32_t raw_mode;
+	uint32_t capture_mode;
+
+	/* Image Readout Registers */
+	uint32_t x_odd_inc;	/* x pixel array addressing odd increment */
+	uint32_t y_odd_inc;	/* y pixel array addressing odd increment */
+	uint32_t x_output_size;	/* width of output image  */
+	uint32_t y_output_size;	/* height of output image */
+
+	/* Declare data format */
+	uint32_t ccp2_data_format;
+
+	/* Clock Tree Registers */
+	uint32_t pre_pll_clk_div;
+	uint32_t pll_multiplier;
+	uint32_t vt_sys_clk_div;
+	uint32_t vt_pix_clk_div;
+	uint32_t op_sys_clk_div;
+	uint32_t op_pix_clk_div;
+
+	/* Video Timing Registers */
+	uint32_t vt_line_length_pck;
+	uint32_t vt_frame_length_lines;
+
+	/* Analogue Binning Registers */
+	uint8_t vtiming_major;
+	uint8_t analog_timing_modes_4;
+
+	/* Fine (pixel) Integration Time Registers */
+	uint32_t fine_integration_time;
+
+	/* Coarse (lines) Integration Time Limit Registers */
+	uint32_t coarse_integration_time_max;
+
+	/* Coarse (lines) Integration Timit Register (16-bit) */
+	uint32_t coarse_integration_time;
+
+	/* Analogue Gain Code Global Registers */
+	uint32_t analogue_gain_code_global;
+
+	/* Digital Gain Code Registers */
+	uint32_t digital_gain_code;
+
+	/* Overall gain (analogue & digital) code
+	 * Note that this is not a real register but just
+	 * an abstraction for the combination of analogue
+	 * and digital gain */
+	uint32_t gain_code;
+
+	/* FMT Test Information */
+	uint32_t pass_fail;
+	uint32_t day;
+	uint32_t month;
+	uint32_t year;
+	uint32_t tester;
+	uint32_t part_number;
+
+	/* Autofocus controls */
+	uint32_t vcm_dac_code;
+	int vcm_max_dac_code_step;
+	int vcm_proportional_factor;
+	int vcm_dac_code_spacing_ms;
+
+	/* VCM NVM Characterisation Information */
+	uint32_t vcm_dac_code_infinity_dn;
+	uint32_t vcm_dac_code_macro_up;
+	uint32_t vcm_dac_code_up_dn_delta;
+
+	/* Internal Variables */
+	uint32_t min_vt_frame_length_lines;
+};
+
+struct vb6801_work_t *vb6801_sensorw;
+struct i2c_client *vb6801_client;
+
+struct vb6801_ctrl_t {
+	const struct msm_camera_sensor_info *sensordata;
+
+	int sensormode;
+	uint32_t factor_fps;	/* init to 1 * 0x00000400 */
+	uint16_t curr_fps;
+	uint16_t max_fps;
+	int8_t pict_exp_update;
+	int8_t reducel;
+	uint16_t curr_lens_pos;
+	uint16_t init_curr_lens_pos;
+	enum vb6801_resolution_t prev_res;
+	enum vb6801_resolution_t pict_res;
+	enum vb6801_resolution_t curr_res;
+	enum vb6801_test_mode_t set_test;
+
+	struct vb6801_sensor_info s_info;
+	struct sensor_dynamic_params_t s_dynamic_params;
+};
+
+static struct vb6801_ctrl_t *vb6801_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(vb6801_wait_queue);
+DEFINE_MUTEX(vb6801_mut);
+
+static int vb6801_i2c_rxdata(unsigned short saddr,
+			     unsigned char *rxdata, int length)
+{
+	struct i2c_msg msgs[] = {
+		{
+			.addr = saddr,
+			.flags = 0,
+			.len = 2,
+			.buf = rxdata,
+		},
+		{
+			.addr = saddr,
+			.flags = I2C_M_RD,
+			.len = 2,
+			.buf = rxdata,
+		},
+	};
+
+	if (i2c_transfer(vb6801_client->adapter, msgs, 2) < 0) {
+		CDBG("vb6801_i2c_rxdata failed!\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int32_t vb6801_i2c_read(unsigned short raddr,
+			       unsigned short *rdata, int rlen)
+{
+	int32_t rc = 0;
+	unsigned char buf[2];
+
+	if (!rdata)
+		return -EIO;
+
+	memset(buf, 0, sizeof(buf));
+
+	buf[0] = (raddr & 0xFF00) >> 8;
+	buf[1] = (raddr & 0x00FF);
+
+	rc = vb6801_i2c_rxdata(vb6801_client->addr, buf, rlen);
+
+	if (rc < 0) {
+		CDBG("vb6801_i2c_read 0x%x failed!\n", raddr);
+		return rc;
+	}
+
+	*rdata = (rlen == 2 ? buf[0] << 8 | buf[1] : buf[0]);
+
+	return rc;
+}
+
+static int32_t vb6801_i2c_read_table(struct vb6801_i2c_reg_conf_t *regs,
+				     int items)
+{
+	int i;
+	int32_t rc = -EFAULT;
+
+	for (i = 0; i < items; i++) {
+		unsigned short *buf =
+		    regs->dlen == D_LEN_BYTE ?
+		    (unsigned short *)&regs->bdata :
+		    (unsigned short *)&regs->wdata;
+		rc = vb6801_i2c_read(regs->waddr, buf, regs->dlen + 1);
+
+		if (rc < 0) {
+			CDBG("vb6801_i2c_read_table Failed!!!\n");
+			break;
+		}
+
+		regs++;
+	}
+
+	return rc;
+}
+
+static int32_t vb6801_i2c_txdata(unsigned short saddr,
+				 unsigned char *txdata, int length)
+{
+	struct i2c_msg msg[] = {
+		{
+			.addr = saddr,
+			.flags = 0,
+			.len = length,
+			.buf = txdata,
+		},
+	};
+
+	if (i2c_transfer(vb6801_client->adapter, msg, 1) < 0) {
+		CDBG("vb6801_i2c_txdata faild 0x%x\n", vb6801_client->addr);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int32_t vb6801_i2c_write_b(unsigned short waddr, uint8_t bdata)
+{
+	int32_t rc = -EFAULT;
+	unsigned char buf[3];
+
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (waddr & 0xFF00) >> 8;
+	buf[1] = (waddr & 0x00FF);
+	buf[2] = bdata;
+
+	CDBG("i2c_write_b addr = %d, val = %d\n", waddr, bdata);
+	rc = vb6801_i2c_txdata(vb6801_client->addr, buf, 3);
+
+	if (rc < 0) {
+		CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+		     waddr, bdata);
+	}
+
+	return rc;
+}
+
+static int32_t vb6801_i2c_write_w(unsigned short waddr, unsigned short wdata)
+{
+	int32_t rc = -EFAULT;
+	unsigned char buf[4];
+
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (waddr & 0xFF00) >> 8;
+	buf[1] = (waddr & 0x00FF);
+	buf[2] = (wdata & 0xFF00) >> 8;
+	buf[3] = (wdata & 0x00FF);
+
+	CDBG("i2c_write_w addr = %d, val = %d, buf[2] = 0x%x, buf[3] = 0x%x\n",
+	     waddr, wdata, buf[2], buf[3]);
+
+	rc = vb6801_i2c_txdata(vb6801_client->addr, buf, 4);
+	if (rc < 0) {
+		CDBG("i2c_write_w failed, addr = 0x%x, val = 0x%x!\n",
+		     waddr, wdata);
+	}
+
+	return rc;
+}
+
+static int32_t vb6801_i2c_write_table(struct vb6801_i2c_reg_conf_t *regs,
+				      int items)
+{
+	int i;
+	int32_t rc = -EFAULT;
+
+	for (i = 0; i < items; i++) {
+		rc = ((regs->dlen == D_LEN_BYTE) ?
+		      vb6801_i2c_write_b(regs->waddr, regs->bdata) :
+		      vb6801_i2c_write_w(regs->waddr, regs->wdata));
+
+		if (rc < 0) {
+			CDBG("vb6801_i2c_write_table Failed!!!\n");
+			break;
+		}
+
+		regs++;
+	}
+
+	return rc;
+}
+
+static int32_t vb6801_reset(const struct msm_camera_sensor_info *data)
+{
+	int rc;
+
+	rc = gpio_request(data->sensor_reset, "vb6801");
+	if (!rc) {
+		CDBG("sensor_reset SUcceeded\n");
+		gpio_direction_output(data->sensor_reset, 0);
+		mdelay(50);
+		gpio_direction_output(data->sensor_reset, 1);
+		mdelay(13);
+	} else
+		CDBG("sensor_reset FAiled\n");
+
+	return rc;
+}
+
+static int32_t vb6801_set_default_focus(void)
+{
+	int32_t rc = 0;
+
+	/* FIXME: Default focus not supported */
+
+	return rc;
+}
+
+static void vb6801_get_pict_fps(uint16_t fps, uint16_t *pfps)
+{
+	/* input fps is preview fps in Q8 format */
+	uint32_t divider; /*Q10 */
+	uint32_t pclk_mult; /*Q10 */
+	uint32_t d1;
+	uint32_t d2;
+
+	d1 =
+		(uint32_t)(
+		(vb6801_ctrl->s_dynamic_params.preview_linesPerFrame *
+		0x00000400) /
+		vb6801_ctrl->s_dynamic_params.snapshot_linesPerFrame);
+
+	d2 =
+		(uint32_t)(
+		(vb6801_ctrl->s_dynamic_params.preview_pixelsPerLine *
+		0x00000400) /
+		vb6801_ctrl->s_dynamic_params.snapshot_pixelsPerLine);
+
+
+	divider = (uint32_t) (d1 * d2) / 0x00000400;
+
+	pclk_mult = (48 * 0x400) / 60;
+
+	/* Verify PCLK settings and frame sizes. */
+	*pfps = (uint16_t)((((fps * pclk_mult) / 0x00000400) * divider)/
+				0x00000400);
+}
+
+static uint16_t vb6801_get_prev_lines_pf(void)
+{
+	if (vb6801_ctrl->prev_res == QTR_SIZE)
+		return vb6801_ctrl->s_dynamic_params.preview_linesPerFrame;
+	else
+		return vb6801_ctrl->s_dynamic_params.snapshot_linesPerFrame;
+}
+
+static uint16_t vb6801_get_prev_pixels_pl(void)
+{
+	if (vb6801_ctrl->prev_res == QTR_SIZE)
+		return vb6801_ctrl->s_dynamic_params.preview_pixelsPerLine;
+	else
+		return vb6801_ctrl->s_dynamic_params.snapshot_pixelsPerLine;
+}
+
+static uint16_t vb6801_get_pict_lines_pf(void)
+{
+	return vb6801_ctrl->s_dynamic_params.snapshot_linesPerFrame;
+}
+
+static uint16_t vb6801_get_pict_pixels_pl(void)
+{
+	return vb6801_ctrl->s_dynamic_params.snapshot_pixelsPerLine;
+}
+
+static uint32_t vb6801_get_pict_max_exp_lc(void)
+{
+	uint16_t snapshot_lines_per_frame;
+
+	if (vb6801_ctrl->pict_res == QTR_SIZE) {
+		snapshot_lines_per_frame =
+		    vb6801_ctrl->s_dynamic_params.preview_linesPerFrame - 3;
+	} else {
+		snapshot_lines_per_frame =
+		    vb6801_ctrl->s_dynamic_params.snapshot_linesPerFrame - 3;
+	}
+
+	return snapshot_lines_per_frame;
+}
+
+static int32_t vb6801_set_fps(struct fps_cfg *fps)
+{
+	int32_t rc = 0;
+
+	/* input is new fps in Q8 format */
+	switch (fps->fps_div) {
+	case 7680:		/* 30 * Q8 */
+		vb6801_ctrl->factor_fps = 1;
+		break;
+
+	case 3840:		/* 15 * Q8 */
+		vb6801_ctrl->factor_fps = 2;
+		break;
+
+	case 2560:		/* 10 * Q8 */
+		vb6801_ctrl->factor_fps = 3;
+		break;
+
+	case 1920:		/* 7.5 * Q8 */
+		vb6801_ctrl->factor_fps = 4;
+		break;
+
+	default:
+		rc = -ENODEV;
+		break;
+	}
+
+	return rc;
+}
+
+static int32_t vb6801_write_exp_gain(uint16_t gain, uint32_t line)
+{
+	int32_t rc = 0;
+	uint16_t lpf;
+
+	if (vb6801_ctrl->curr_res == SENSOR_FULL_SIZE)
+		lpf = VB6801_LINES_PER_FRAME_SNAPSHOT;
+	else
+		lpf = VB6801_LINES_PER_FRAME_PREVIEW;
+
+	/* hold */
+	rc = vb6801_i2c_write_w(REG_HOLD, HOLD);
+	if (rc < 0)
+		goto exp_gain_done;
+
+	if ((vb6801_ctrl->curr_fps <
+	     vb6801_ctrl->max_fps / vb6801_ctrl->factor_fps) &&
+	    (!vb6801_ctrl->pict_exp_update)) {
+
+		if (vb6801_ctrl->reducel) {
+
+			rc = vb6801_i2c_write_w(REG_VT_FRAME_LENGTH_LINES,
+						lpf * vb6801_ctrl->factor_fps);
+
+			vb6801_ctrl->curr_fps =
+			    vb6801_ctrl->max_fps / vb6801_ctrl->factor_fps;
+
+		} else if (!vb6801_ctrl->reducel) {
+
+			rc = vb6801_i2c_write_w(REG_COARSE_INTEGRATION_TIME,
+						line * vb6801_ctrl->factor_fps);
+
+			vb6801_ctrl->reducel = 1;
+		}
+	} else if ((vb6801_ctrl->curr_fps >
+		    vb6801_ctrl->max_fps / vb6801_ctrl->factor_fps) &&
+		   (!vb6801_ctrl->pict_exp_update)) {
+
+		rc = vb6801_i2c_write_w(REG_VT_FRAME_LENGTH_LINES,
+					lpf * vb6801_ctrl->factor_fps);
+
+		vb6801_ctrl->curr_fps =
+		    vb6801_ctrl->max_fps / vb6801_ctrl->factor_fps;
+
+	} else {
+		/* analogue_gain_code_global */
+		rc = vb6801_i2c_write_w(REG_ANALOGUE_GAIN_CODE_GLOBAL, gain);
+		if (rc < 0)
+			goto exp_gain_done;
+
+		/* coarse_integration_time */
+		rc = vb6801_i2c_write_w(REG_COARSE_INTEGRATION_TIME,
+					line * vb6801_ctrl->factor_fps);
+		if (rc < 0)
+			goto exp_gain_done;
+
+		vb6801_ctrl->pict_exp_update = 1;
+	}
+
+	rc = vb6801_i2c_write_w(REG_HOLD, RELEASE_HOLD);
+
+exp_gain_done:
+	return rc;
+}
+
+static int32_t vb6801_set_pict_exp_gain(uint16_t gain, uint32_t line)
+{
+	vb6801_ctrl->pict_exp_update = 1;
+	return vb6801_write_exp_gain(gain, line);
+}
+
+static int32_t vb6801_power_down(void)
+{
+	int32_t rc = 0;
+	rc = vb6801_i2c_write_b(REG_NVM_PDN, 0);
+
+	mdelay(5);
+	return rc;
+}
+
+static int32_t vb6801_go_to_position(uint32_t target_vcm_dac_code,
+				     struct vb6801_sensor_info *ps)
+{
+	/* Prior to running this function the following values must
+	 * be initialised in the sensor data structure, PS
+	 * ps->vcm_dac_code
+	 * ps->vcm_max_dac_code_step
+	 * ps->vcm_dac_code_spacing_ms */
+
+	int32_t rc = 0;
+
+	ps->vcm_dac_code = target_vcm_dac_code;
+
+	/* Restore Strobe to zero state */
+	rc = vb6801_i2c_write_b(REG_VCM_DAC_STROBE, 0x00);
+	if (rc < 0)
+		return rc;
+
+	/* Write 9-bit VCM DAC Code */
+	rc = vb6801_i2c_write_w(REG_VCM_DAC_CODE, ps->vcm_dac_code);
+	if (rc < 0)
+		return rc;
+
+	/* Generate a rising edge on the dac_strobe to latch
+	 * new DAC value */
+
+	rc = vb6801_i2c_write_w(REG_VCM_DAC_STROBE, 0x01);
+
+	return rc;
+}
+
+static int32_t vb6801_move_focus(int direction, int32_t num_steps)
+{
+	int16_t step_direction;
+	int16_t actual_step;
+	int16_t next_position;
+	uint32_t step_size;
+	int16_t small_move[4];
+	uint16_t i;
+	int32_t rc = 0;
+
+	step_size = (vb6801_ctrl->s_info.vcm_dac_code_macro_up -
+		     vb6801_ctrl->s_info.vcm_dac_code_infinity_dn) /
+	    VB6801_TOTAL_STEPS_NEAR_TO_FAR;
+
+	if (num_steps > VB6801_TOTAL_STEPS_NEAR_TO_FAR)
+		num_steps = VB6801_TOTAL_STEPS_NEAR_TO_FAR;
+	else if (num_steps == 0)
+		return -EINVAL;
+
+	if (direction == MOVE_NEAR)
+		step_direction = 4;
+	else if (direction == MOVE_FAR)
+		step_direction = -4;
+	else
+		return -EINVAL;
+
+	/* need to decide about default position and power supplied
+	 * at start up and reset */
+	if (vb6801_ctrl->curr_lens_pos < vb6801_ctrl->init_curr_lens_pos)
+		vb6801_ctrl->curr_lens_pos = vb6801_ctrl->init_curr_lens_pos;
+
+	actual_step = (step_direction * num_steps);
+
+	next_position = vb6801_ctrl->curr_lens_pos;
+
+	for (i = 0; i < 4; i++) {
+		if (actual_step >= 0)
+			small_move[i] =
+			    (i + 1) * actual_step / 4 - i * actual_step / 4;
+
+		if (actual_step < 0)
+			small_move[i] =
+			    (i + 1) * actual_step / 4 - i * actual_step / 4;
+	}
+
+	if (next_position > 511)
+		next_position = 511;
+	else if (next_position < 0)
+		next_position = 0;
+
+	/* for damping */
+	for (i = 0; i < 4; i++) {
+		next_position =
+		    (int16_t) (vb6801_ctrl->curr_lens_pos + small_move[i]);
+
+		/* Writing the digital code for current to the actuator */
+		CDBG("next_position in damping mode = %d\n", next_position);
+
+		rc = vb6801_go_to_position(next_position, &vb6801_ctrl->s_info);
+		if (rc < 0) {
+			CDBG("go_to_position Failed!!!\n");
+			return rc;
+		}
+
+		vb6801_ctrl->curr_lens_pos = next_position;
+		if (i < 3)
+			mdelay(5);
+	}
+
+	return rc;
+}
+
+static int vb6801_read_nvm_data(struct vb6801_sensor_info *ps)
+{
+	/* +--------+------+------+----------------+---------------+
+	 * | Index | NVM | NVM | Name | Description |
+	 * | | Addr | Byte | | |
+	 * +--------+------+------+----------------+---------------+
+	 * | 0x3600 | 0 | 3 | nvm_t1_addr_00 | {PF[2:0]:Day[4:0]} |
+	 * | 0x3601 | 0 | 2 | nvm_t1_addr_01 | {Month[3:0]:Year[3:0]} |
+	 * | 0x3602 | 0 | 1 | nvm_t1_addr_02 | Tester[7:0] |
+	 * | 0x3603 | 0 | 0 | nvm_t1_addr_03 | Part[15:8] |
+	 * +--------+------+------+----------------+---------------+
+	 * | 0x3604 | 1 | 3 | nvm_t1_addr_04 | Part[7:0] |
+	 * | 0x3605 | 1 | 2 | nvm_t1_addr_05 | StartWPM[7:0] |
+	 * | 0x3606 | 1 | 1 | nvm_t1_addr_06 | Infinity[7:0] |
+	 * | 0x3607 | 1 | 0 | nvm_t1_addr_07 | Macro[7:0] |
+	 * +--------+------+------+----------------+---------------+
+	 * | 0x3608 | 2 | 3 | nvm_t1_addr_08 | Reserved |
+	 * | 0x3609 | 2 | 2 | nvm_t1_addr_09 | Reserved |
+	 * | 0x360A | 2 | 1 | nvm_t1_addr_0A | UpDown[7:0] |
+	 * | 0x360B | 2 | 0 | nvm_t1_addr_0B | Reserved |
+	 * +--------+------+------+----------------+---------------+
+	 * | 0x360C | 3 | 3 | nvm_t1_addr_0C | Reserved |
+	 * | 0x360D | 3 | 2 | nvm_t1_addr_0D | Reserved |
+	 * | 0x360E | 3 | 1 | nvm_t1_addr_0E | Reserved |
+	 * | 0x360F | 3 | 0 | nvm_t1_addr_0F | Reserved |
+	 * +--------+------+------+----------------+---------------+
+	 * | 0x3610 | 4 | 3 | nvm_t1_addr_10 | Reserved |
+	 * | 0x3611 | 4 | 2 | nvm_t1_addr_11 | Reserved |
+	 * | 0x3612 | 4 | 1 | nvm_t1_addr_12 | Reserved |
+	 * | 0x3613 | 4 | 0 | nvm_t1_addr_13 | Reserved |
+	 * +--------+------+------+----------------+---------------+*/
+
+	int32_t rc;
+	struct vb6801_i2c_reg_conf_t rreg[] = {
+		{REG_NVM_T1_ADDR_00, 0, 0, D_LEN_BYTE},
+		{REG_NVM_T1_ADDR_01, 0, 0, D_LEN_BYTE},
+		{REG_NVM_T1_ADDR_02, 0, 0, D_LEN_BYTE},
+		{REG_NVM_T1_ADDR_03, 0, 0, D_LEN_BYTE},
+		{REG_NVM_T1_ADDR_04, 0, 0, D_LEN_BYTE},
+		{REG_NVM_T1_ADDR_05, 0, 0, D_LEN_BYTE},
+		{REG_NVM_T1_ADDR_06, 0, 0, D_LEN_BYTE},
+		{REG_NVM_T1_ADDR_07, 0, 0, D_LEN_BYTE},
+		{REG_NVM_T1_ADDR_08, 0, 0, D_LEN_BYTE},
+		{REG_NVM_T1_ADDR_09, 0, 0, D_LEN_BYTE},
+		{REG_NVM_T1_ADDR_0A, 0, 0, D_LEN_BYTE},
+		{REG_NVM_T1_ADDR_0B, 0, 0, D_LEN_BYTE},
+		{REG_NVM_T1_ADDR_0C, 0, 0, D_LEN_BYTE},
+		{REG_NVM_T1_ADDR_0D, 0, 0, D_LEN_BYTE},
+		{REG_NVM_T1_ADDR_0E, 0, 0, D_LEN_BYTE},
+		{REG_NVM_T1_ADDR_0F, 0, 0, D_LEN_BYTE},
+		{REG_NVM_T1_ADDR_10, 0, 0, D_LEN_BYTE},
+		{REG_NVM_T1_ADDR_11, 0, 0, D_LEN_BYTE},
+		{REG_NVM_T1_ADDR_12, 0, 0, D_LEN_BYTE},
+		{REG_NVM_T1_ADDR_13, 0, 0, D_LEN_BYTE},
+	};
+
+	struct vb6801_i2c_reg_conf_t wreg[] = {
+		/* Enable NVM for Direct Reading */
+		{REG_NVM_CTRL, 0, 2, D_LEN_BYTE},
+
+		/* Power up NVM */
+		{REG_NVM_PDN, 0, 1, D_LEN_BYTE},
+	};
+
+	rc = vb6801_i2c_write_table(wreg, ARRAY_SIZE(wreg));
+	if (rc < 0) {
+		CDBG("I2C Write Table FAILED!!!\n");
+		return rc;
+	}
+
+	/* NVM Read Pulse Width
+	 * ====================
+	 * nvm_pulse_width_us = nvm_pulse_width_ext_clk / ext_clk_freq_mhz
+	 * Valid Range for Read Pulse Width = 400ns -> 3.0us
+	 * Min ext_clk_freq_mhz = 6MHz  => 3.0 *  6  = 18
+	 * Max ext_clk_freq_mhz = 27MHz => 0.4 * 27 = 10.8
+	 * Choose 15 as a common value
+	 *  - 15 /  6.0 = 2.5000us
+	 *  - 15 / 12.0 = 1.2500us
+	 *  - 15 / 27.0 = 0.5555us */
+	rc = vb6801_i2c_write_w(REG_NVM_PULSE_WIDTH, 15);
+	if (rc < 0) {
+		rc = -EBUSY;
+		goto nv_shutdown;
+	}
+
+	rc = vb6801_i2c_read_table(rreg, ARRAY_SIZE(rreg));
+	if (rc < 0) {
+		CDBG("I2C Read Table FAILED!!!\n");
+		rc = -EBUSY;
+		goto nv_shutdown;
+	}
+
+	/* Decode and Save FMT Info */
+	ps->pass_fail = (rreg[0].bdata & 0x00E0) >> 5;
+	ps->day = (rreg[0].bdata & 0x001F);
+	ps->month = (rreg[1].bdata & 0x00F0) >> 4;
+	ps->year = (rreg[1].bdata & 0x000F) + 2000;
+	ps->tester = rreg[2].bdata;
+	ps->part_number = (rreg[3].bdata << 8) + rreg[4].bdata;
+
+	/* Decode and Save VCM Dac Values in data structure */
+	ps->vcm_dac_code_infinity_dn = rreg[6].bdata;
+	ps->vcm_dac_code_macro_up = rreg[7].bdata << 1;
+	ps->vcm_dac_code_up_dn_delta = rreg[10].bdata;
+
+nv_shutdown:
+	/* Power Down NVM to extend life time */
+	rc = vb6801_i2c_write_b(REG_NVM_PDN, 0);
+
+	return rc;
+}
+
+static int vb6801_config_sensor(int32_t ext_clk_freq_mhz,
+				int32_t target_frame_rate_fps,
+				int32_t target_vt_pix_clk_freq_mhz,
+				uint32_t sub_sampling_factor,
+				uint32_t analog_binning_allowed,
+				uint32_t raw_mode, int capture_mode,
+				enum vb6801_resolution_t res)
+{
+	uint32_t rc;
+	/* ext_clk_freq_mhz      = 6.0 -> 27.0 MHz
+	 * target_frame_rate_fps  = 15 fps
+	 * target_vt_pix_clk_freq_mhz = 24.0 -> 64.0MHz
+	 * sub_sampling factor   = 1, 2, 3, or 4
+	 * raw_mode factor       = 10
+	 *
+	 * capture_mode, 0 = CCP1
+	 * capture_mode, 1 = CCP2
+	 * capture_mode, 2 = 10-bit parallel + hsync + vsync */
+
+	/* Declare data format */
+	uint32_t ccp2_data_format = 0x0A0A;
+
+	/*  Declare clock tree variables */
+	int32_t min_pll_ip_freq_mhz = 6;
+	int32_t max_pll_op_freq_mhz = 640;
+	uint32_t pre_pll_clk_div = 1;
+	int32_t pll_ip_freq_mhz = 6;
+	uint32_t pll_multiplier = 100;
+	int32_t pll_op_freq_mhz = 600;
+	uint32_t vt_sys_clk_div = 1;
+	int32_t vt_sys_clk_freq_mhz = 600;
+	uint32_t vt_pix_clk_div = 10;
+	int32_t vt_pix_clk_freq_mhz = 60;
+	uint32_t op_sys_clk_div = 1;
+	int32_t op_sys_clk_freq_mhz = 60;
+	uint32_t op_pix_clk_div = 10;
+	int32_t op_pix_clk_freq_mhz = 60;
+
+	/* Declare pixel array and frame timing variables */
+	uint32_t x_pixel_array = 2064;
+	uint32_t y_pixel_array = 1544;
+	uint32_t x_even_inc = 1;
+	uint32_t x_odd_inc = 1;
+	uint32_t y_even_inc = 1;
+	uint32_t y_odd_inc = 1;
+	uint32_t x_output_size = 2064;
+	uint32_t y_output_size = 1544;
+	uint32_t additional_rows = 2;
+	uint32_t min_vt_frame_blanking_lines = 16;
+	uint32_t vt_line_length_pck = 2500;
+	uint32_t vt_line_length_us = 0;
+	uint32_t min_vt_frame_length_lines = 1562;
+	uint32_t vt_frame_length_lines = 1600;
+	uint32_t target_vt_frame_length_ms;	/* 200 * 0x0001000 / 3; */
+	uint32_t vt_frame_length_ms;	/* 200 * 0x0001000 / 3; */
+	uint32_t frame_rate_fps = 15;
+
+	/* Coarse intergration time */
+	uint32_t coarse_integration_time = 1597;
+	uint32_t coarse_integration_time_max_margin = 3;
+	uint16_t frame_count;
+	int timeout;
+
+	struct vb6801_sensor_info *pinfo = &vb6801_ctrl->s_info;
+
+	struct vb6801_i2c_reg_conf_t rreg[] = {
+		{REG_PRE_PLL_CLK_DIV, 0, 0, D_LEN_WORD},
+		{REG_PLL_MULTIPLIER, 0, 0, D_LEN_WORD},
+		{REG_VT_SYS_CLK_DIV, 0, 0, D_LEN_WORD},
+		{REG_VT_PIX_CLK_DIV, 0, 0, D_LEN_WORD},
+		{REG_OP_SYS_CLK_DIV, 0, 0, D_LEN_WORD},
+		{REG_OP_PIX_CLK_DIV, 0, 0, D_LEN_WORD},
+		{REG_FRAME_COUNT, 0, 0, D_LEN_BYTE},
+	};
+
+	struct vb6801_i2c_reg_conf_t wreg2[] = {
+		{REG_POWER_MAN_ENABLE_3, 0, 95, D_LEN_BYTE},
+		{REG_POWER_MAN_ENABLE_4, 0, 142, D_LEN_BYTE},
+		{REG_POWER_MAN_ENABLE_5, 0, 7, D_LEN_BYTE},
+	};
+
+	/* VIDEO TIMING CALCULATIONS
+	 * ========================= */
+
+	/* Pixel Array Size */
+	x_pixel_array = 2064;
+	y_pixel_array = 1544;
+
+	/* set current resolution */
+	vb6801_ctrl->curr_res = res;
+
+	/* Analogue binning setup */
+	if (pinfo->analog_binning_allowed > 0 &&
+	    pinfo->sub_sampling_factor == 4) {
+
+		pinfo->vtiming_major = 1;
+		pinfo->analog_timing_modes_4 = 32;
+	} else if (pinfo->analog_binning_allowed > 0 &&
+		   pinfo->sub_sampling_factor == 2) {
+
+		pinfo->vtiming_major = 1;
+		pinfo->analog_timing_modes_4 = 0;
+	} else {
+
+		pinfo->vtiming_major = 0;
+		pinfo->analog_timing_modes_4 = 0;
+	}
+
+	/* Sub-Sampling X & Y Odd Increments: valid values 1, 3, 5, 7 */
+	x_even_inc = 1;
+	y_even_inc = 1;
+	x_odd_inc = (sub_sampling_factor << 1) - x_even_inc;
+	y_odd_inc = (sub_sampling_factor << 1) - y_even_inc;
+
+	/* Output image size
+	 * Must always be a multiple of 2 - round down */
+	x_output_size = ((x_pixel_array / sub_sampling_factor) >> 1) << 1;
+	y_output_size = ((y_pixel_array / sub_sampling_factor) >> 1) << 1;
+
+	/* Output data format */
+	ccp2_data_format = (raw_mode << 8) + raw_mode;
+
+	/* Pre PLL clock divider : valid values 1, 2 or 4
+	 * The 1st step is to ensure that PLL input frequency is as close
+	 * as possible to the min allowed PLL input frequency.
+	 * This yields the smallest step size in the PLL output frequency. */
+	pre_pll_clk_div =
+	    ((int)(ext_clk_freq_mhz / min_pll_ip_freq_mhz) >> 1) << 1;
+	if (pre_pll_clk_div < 2)
+		pre_pll_clk_div = 1;
+
+	pll_ip_freq_mhz = ext_clk_freq_mhz / pre_pll_clk_div;
+
+	/* Video Timing System Clock divider: valid values 1, 2, 4
+	 * Now need to work backwards through the clock tree to determine the
+	 * 1st pass estimates for vt_sys_clk_freq_mhz and then the PLL output
+	 * frequency.*/
+	vt_sys_clk_freq_mhz = vt_pix_clk_div * target_vt_pix_clk_freq_mhz;
+	vt_sys_clk_div = max_pll_op_freq_mhz / vt_sys_clk_freq_mhz;
+	if (vt_sys_clk_div < 2)
+		vt_sys_clk_div = 1;
+
+	/* PLL Mulitplier: min , max 106 */
+	pll_op_freq_mhz = vt_sys_clk_div * vt_sys_clk_freq_mhz;
+	pll_multiplier = (pll_op_freq_mhz * 0x0001000) / pll_ip_freq_mhz;
+
+	/* Calculate the acutal pll output frequency
+	 * - the pll_multiplier calculation introduces a quantisation error
+	 *   due the integer nature of the pll multiplier */
+	pll_op_freq_mhz = (pll_ip_freq_mhz * pll_multiplier) / 0x0001000;
+
+	/* Re-calculate video timing clock frequencies based
+	 * on actual PLL freq */
+	vt_sys_clk_freq_mhz = pll_op_freq_mhz / vt_sys_clk_div;
+	vt_pix_clk_freq_mhz = ((vt_sys_clk_freq_mhz * 0x0001000) /
+				vt_pix_clk_div)/0x0001000;
+
+	/* Output System Clock Divider: valid value 1, 2, 4, 6, 8
+	 * op_sys_clk_div = vt_sys_clk_div;*/
+	op_sys_clk_div = (vt_sys_clk_div * sub_sampling_factor);
+	if (op_sys_clk_div < 2)
+		op_sys_clk_div = 1;
+
+	/* Calculate output timing clock frequencies */
+	op_sys_clk_freq_mhz = pll_op_freq_mhz / op_sys_clk_div;
+	op_pix_clk_freq_mhz =
+	    (op_sys_clk_freq_mhz * 0x0001000) / (op_pix_clk_div * 0x0001000);
+
+	/* Line length in pixels and us */
+	vt_line_length_pck = 2500;
+	vt_line_length_us =
+	    vt_line_length_pck * 0x0001000 / vt_pix_clk_freq_mhz;
+
+	/* Target vt_frame_length_ms */
+	target_vt_frame_length_ms = (1000 * 0x0001000 / target_frame_rate_fps);
+
+	/* Frame length in lines */
+	min_vt_frame_length_lines =
+	    additional_rows + y_output_size + min_vt_frame_blanking_lines;
+
+	vt_frame_length_lines =
+	    ((1000 * target_vt_frame_length_ms) / vt_line_length_us);
+
+	if (vt_frame_length_lines <= min_vt_frame_length_lines)
+		vt_frame_length_lines = min_vt_frame_length_lines;
+
+	/* Calcuate the actual frame length in ms */
+	vt_frame_length_ms = (vt_frame_length_lines * vt_line_length_us / 1000);
+
+	/* Frame Rate in fps */
+	frame_rate_fps = (1000 * 0x0001000 / vt_frame_length_ms);
+
+	/* Set coarse integration to max */
+	coarse_integration_time =
+	    vt_frame_length_lines - coarse_integration_time_max_margin;
+
+	CDBG("SENSOR VIDEO TIMING SUMMARY:\n");
+	CDBG(" ============================\n");
+	CDBG("ext_clk_freq_mhz      = %d\n", ext_clk_freq_mhz);
+	CDBG("pre_pll_clk_div       = %d\n", pre_pll_clk_div);
+	CDBG("pll_ip_freq_mhz       = %d\n", pll_ip_freq_mhz);
+	CDBG("pll_multiplier        = %d\n", pll_multiplier);
+	CDBG("pll_op_freq_mhz       = %d\n", pll_op_freq_mhz);
+	CDBG("vt_sys_clk_div        = %d\n", vt_sys_clk_div);
+	CDBG("vt_sys_clk_freq_mhz   = %d\n", vt_sys_clk_freq_mhz);
+	CDBG("vt_pix_clk_div        = %d\n", vt_pix_clk_div);
+	CDBG("vt_pix_clk_freq_mhz   = %d\n", vt_pix_clk_freq_mhz);
+	CDBG("op_sys_clk_div        = %d\n", op_sys_clk_div);
+	CDBG("op_sys_clk_freq_mhz   = %d\n", op_sys_clk_freq_mhz);
+	CDBG("op_pix_clk_div        = %d\n", op_pix_clk_div);
+	CDBG("op_pix_clk_freq_mhz   = %d\n", op_pix_clk_freq_mhz);
+	CDBG("vt_line_length_pck    = %d\n", vt_line_length_pck);
+	CDBG("vt_line_length_us     = %d\n", vt_line_length_us/0x0001000);
+	CDBG("vt_frame_length_lines = %d\n", vt_frame_length_lines);
+	CDBG("vt_frame_length_ms    = %d\n", vt_frame_length_ms/0x0001000);
+	CDBG("frame_rate_fps        = %d\n", frame_rate_fps);
+	CDBG("ccp2_data_format = %d\n", ccp2_data_format);
+	CDBG("x_output_size = %d\n", x_output_size);
+	CDBG("y_output_size = %d\n", y_output_size);
+	CDBG("x_odd_inc = %d\n", x_odd_inc);
+	CDBG("y_odd_inc = %d\n", y_odd_inc);
+	CDBG("(vt_frame_length_lines * frame_rate_factor ) = %d\n",
+	    (vt_frame_length_lines * vb6801_ctrl->factor_fps));
+	CDBG("coarse_integration_time = %d\n", coarse_integration_time);
+	CDBG("pinfo->vcm_dac_code = %d\n", pinfo->vcm_dac_code);
+	CDBG("capture_mode = %d\n", capture_mode);
+
+	/* RE-CONFIGURE SENSOR WITH NEW TIMINGS
+	 * ====================================
+	 * Enter Software Standby Mode */
+	rc = vb6801_i2c_write_b(REG_MODE_SELECT, 0);
+	if (rc < 0) {
+		CDBG("I2C vb6801_i2c_write_b FAILED!!!\n");
+		return rc;
+	}
+
+	/* Wait 100ms */
+	mdelay(100);
+
+	if (capture_mode == 0) {
+
+		rc = vb6801_i2c_write_b(REG_CCP2_CHANNEL_IDENTIFIER, 0);
+		rc = vb6801_i2c_write_b(REG_CCP2_SIGNALLING_MODE, 0);
+	} else if (capture_mode == 1) {
+
+		rc = vb6801_i2c_write_b(REG_CCP2_CHANNEL_IDENTIFIER, 0);
+		rc = vb6801_i2c_write_b(REG_CCP2_SIGNALLING_MODE, 1);
+	}
+
+	{
+		struct vb6801_i2c_reg_conf_t wreg[] = {
+			/* Re-configure Sensor */
+			{REG_CCP2_DATA_FORMAT, ccp2_data_format, 0,
+			 D_LEN_WORD},
+			{REG_ANALOGUE_GAIN_CODE_GLOBAL, 128, 0, D_LEN_WORD},
+			{REG_PRE_PLL_CLK_DIV, pre_pll_clk_div, 0, D_LEN_WORD},
+			{REG_VT_SYS_CLK_DIV, vt_sys_clk_div, 0, D_LEN_WORD},
+			{REG_VT_PIX_CLK_DIV, vt_pix_clk_div, 0, D_LEN_WORD},
+			{REG_OP_SYS_CLK_DIV, vt_sys_clk_div, 0, D_LEN_WORD},
+			{REG_OP_PIX_CLK_DIV, vt_pix_clk_div, 0, D_LEN_WORD},
+			{REG_VT_LINE_LENGTH_PCK, vt_line_length_pck, 0,
+			 D_LEN_WORD},
+			{REG_X_OUTPUT_SIZE, x_output_size, 0, D_LEN_WORD},
+			{REG_Y_OUTPUT_SIZE, y_output_size, 0, D_LEN_WORD},
+			{REG_X_ODD_INC, x_odd_inc, 0, D_LEN_WORD},
+			{REG_Y_ODD_INC, y_odd_inc, 0, D_LEN_WORD},
+			{REG_VT_FRAME_LENGTH_LINES,
+			 vt_frame_length_lines * vb6801_ctrl->factor_fps, 0,
+			 D_LEN_WORD},
+			{REG_COARSE_INTEGRATION_TIME,
+			 coarse_integration_time, 0, D_LEN_WORD},
+			/* Analogue Settings */
+			{REG_ANALOG_TIMING_MODES_2, 0, 132, D_LEN_BYTE},
+			{REG_RAMP_SCALE, 0, 5, D_LEN_BYTE},
+			{REG_BTL_LEVEL_SETUP, 0, 11, D_LEN_BYTE},
+			/* Enable Defect Correction */
+			{REG_SCYTHE_ENABLE, 0, 1, D_LEN_BYTE},
+			{REG_SCYTHE_WEIGHT, 0, 16, D_LEN_BYTE},
+			{REG_BRUCE_ENABLE, 0, 1, D_LEN_BYTE},
+			/* Auto Focus Configuration
+			 * Please note that the DAC Code is a written as a
+			 * 16-bit value 0 = infinity (no DAC current) */
+			{REG_VCM_DAC_CODE, pinfo->vcm_dac_code, 0, D_LEN_WORD},
+			{REG_VCM_DAC_STROBE, 0, 0, D_LEN_BYTE},
+			{REG_VCM_DAC_ENABLE, 0, 1, D_LEN_BYTE},
+		};
+
+		rc = vb6801_i2c_write_table(wreg, ARRAY_SIZE(wreg));
+		if (rc < 0) {
+			CDBG("I2C Write Table FAILED!!!\n");
+			return rc;
+		}
+	}
+	/* Parallel Interface Configuration */
+	if (capture_mode >= 2) {
+		struct vb6801_i2c_reg_conf_t wreg1[] = {
+			{REG_OP_CODER_SYNC_CLK_SETUP, 0, 15, D_LEN_BYTE},
+			{REG_OP_CODER_ENABLE, 0, 3, D_LEN_BYTE},
+			{REG_OP_CODER_SLOW_PAD_EN, 0, 1, D_LEN_BYTE},
+			{REG_OP_CODER_AUTOMATIC_MODE_ENABLE, 0, 3, D_LEN_BYTE},
+			{REG_OP_CODER_AUTO_STARTUP, 0, 2, D_LEN_BYTE},
+		};
+
+		rc = vb6801_i2c_write_table(wreg1, ARRAY_SIZE(wreg1));
+		if (rc < 0) {
+			CDBG("I2C Write Table FAILED!!!\n");
+			return rc;
+		}
+	}
+
+	/* Enter Streaming Mode */
+	rc = vb6801_i2c_write_b(REG_MODE_SELECT, 1);
+	if (rc < 0) {
+		CDBG("I2C Write Table FAILED!!!\n");
+		return rc;
+	}
+
+	/* Wait until the sensor starts streaming
+	 * Poll until the reported frame_count value is != 0xFF */
+	frame_count = 0xFF;
+	timeout = 2000;
+	while (frame_count == 0xFF && timeout > 0) {
+		rc = vb6801_i2c_read(REG_FRAME_COUNT, &frame_count, 1);
+		if (rc < 0)
+			return rc;
+
+		CDBG("REG_FRAME_COUNT  = 0x%x\n", frame_count);
+		timeout--;
+	}
+
+	/* Post Streaming Configuration */
+
+	rc = vb6801_i2c_write_table(wreg2, ARRAY_SIZE(wreg2));
+	if (rc < 0) {
+		CDBG("I2C Write Table FAILED!!!\n");
+		return rc;
+	}
+
+	rc = vb6801_i2c_read_table(rreg, ARRAY_SIZE(rreg));
+	if (rc < 0) {
+		CDBG("I2C Read Table FAILED!!!\n");
+		return rc;
+	}
+
+	CDBG("REG_PRE_PLL_CLK_DIV = 0x%x\n", rreg[0].wdata);
+	CDBG("REG_PLL_MULTIPLIER  = 0x%x\n", rreg[1].wdata);
+	CDBG("REG_VT_SYS_CLK_DIV  = 0x%x\n", rreg[2].wdata);
+	CDBG("REG_VT_PIX_CLK_DIV  = 0x%x\n", rreg[3].wdata);
+	CDBG("REG_OP_SYS_CLK_DIV  = 0x%x\n", rreg[4].wdata);
+	CDBG("REG_OP_PIX_CLK_DIV  = 0x%x\n", rreg[5].wdata);
+	CDBG("REG_FRAME_COUNT  = 0x%x\n", rreg[6].bdata);
+
+	mdelay(50);
+	frame_count = 0;
+	rc = vb6801_i2c_read(REG_FRAME_COUNT, &frame_count, 1);
+	CDBG("REG_FRAME_COUNT1  = 0x%x\n", frame_count);
+
+	mdelay(150);
+	frame_count = 0;
+	rc = vb6801_i2c_read(REG_FRAME_COUNT, &frame_count, 1);
+	CDBG("REG_FRAME_COUNT2  = 0x%x\n", frame_count);
+
+	mdelay(100);
+	frame_count = 0;
+	rc = vb6801_i2c_read(REG_FRAME_COUNT, &frame_count, 1);
+	CDBG("REG_FRAME_COUNT3  = 0x%x\n", frame_count);
+
+	mdelay(250);
+	frame_count = 0;
+	rc = vb6801_i2c_read(REG_FRAME_COUNT, &frame_count, 1);
+	CDBG("REG_FRAME_COUNT4  = 0x%x\n", frame_count);
+
+	return rc;
+}
+
+static int vb6801_sensor_init_done(const struct msm_camera_sensor_info *data)
+{
+	gpio_direction_output(data->sensor_reset, 0);
+	gpio_free(data->sensor_reset);
+	return 0;
+}
+
+static int vb6801_init_client(struct i2c_client *client)
+{
+	/* Initialize the MSM_CAMI2C Chip */
+	init_waitqueue_head(&vb6801_wait_queue);
+	return 0;
+}
+
+static int32_t vb6801_video_config(int mode, int res)
+{
+	int32_t rc = 0;
+
+	vb6801_ctrl->prev_res = res;
+	vb6801_ctrl->curr_res = res;
+	vb6801_ctrl->sensormode = mode;
+
+	rc = vb6801_config_sensor(12, 30, 60, 2, 1, 10, 2, RES_PREVIEW);
+	if (rc < 0)
+		return rc;
+
+	rc = vb6801_i2c_read(REG_VT_LINE_LENGTH_PCK,
+			     &vb6801_ctrl->s_dynamic_params.
+			     preview_pixelsPerLine, 2);
+	if (rc < 0)
+		return rc;
+
+	rc = vb6801_i2c_read(REG_VT_LINE_LENGTH_PCK,
+			     &vb6801_ctrl->s_dynamic_params.
+			     preview_linesPerFrame, 2);
+
+	return rc;
+}
+
+static int32_t vb6801_snapshot_config(int mode, int res)
+{
+	int32_t rc = 0;
+
+	vb6801_ctrl->curr_res = vb6801_ctrl->pict_res;
+	vb6801_ctrl->sensormode = mode;
+
+	rc = vb6801_config_sensor(12, 12, 48, 1, 1, 10, 2, RES_CAPTURE);
+	if (rc < 0)
+		return rc;
+
+	rc = vb6801_i2c_read(REG_VT_LINE_LENGTH_PCK,
+			     &vb6801_ctrl->s_dynamic_params.
+			     snapshot_pixelsPerLine, 2);
+	if (rc < 0)
+		return rc;
+
+	rc = vb6801_i2c_read(REG_VT_LINE_LENGTH_PCK,
+			     &vb6801_ctrl->s_dynamic_params.
+			     snapshot_linesPerFrame, 2);
+
+	return rc;
+}
+
+static int32_t vb6801_set_sensor_mode(int mode, int res)
+{
+	int32_t rc = 0;
+
+	switch (mode) {
+	case SENSOR_PREVIEW_MODE:
+		rc = vb6801_video_config(mode, res);
+		break;
+
+	case SENSOR_SNAPSHOT_MODE:
+	case SENSOR_RAW_SNAPSHOT_MODE:
+		rc = vb6801_snapshot_config(mode, res);
+		break;
+
+	default:
+		rc = -EINVAL;
+		break;
+	}
+
+	return rc;
+}
+
+int vb6801_sensor_config(void __user *argp)
+{
+	struct sensor_cfg_data cdata;
+	long rc = 0;
+
+	if (copy_from_user(&cdata,
+			   (void *)argp, sizeof(struct sensor_cfg_data)))
+		return -EFAULT;
+
+	mutex_lock(&vb6801_mut);
+
+	CDBG("vb6801_sensor_config, cfgtype = %d\n", cdata.cfgtype);
+
+	switch (cdata.cfgtype) {
+	case CFG_GET_PICT_FPS:
+		vb6801_get_pict_fps(cdata.cfg.gfps.prevfps,
+				    &(cdata.cfg.gfps.pictfps));
+
+		if (copy_to_user((void *)argp,
+				 &cdata, sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PREV_L_PF:
+		cdata.cfg.prevl_pf = vb6801_get_prev_lines_pf();
+
+		if (copy_to_user((void *)argp,
+				 &cdata, sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PREV_P_PL:
+		cdata.cfg.prevp_pl = vb6801_get_prev_pixels_pl();
+
+		if (copy_to_user((void *)argp,
+				 &cdata, sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PICT_L_PF:
+		cdata.cfg.pictl_pf = vb6801_get_pict_lines_pf();
+
+		if (copy_to_user((void *)argp,
+				 &cdata, sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PICT_P_PL:
+		cdata.cfg.pictp_pl = vb6801_get_pict_pixels_pl();
+
+		if (copy_to_user((void *)argp,
+				 &cdata, sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_GET_PICT_MAX_EXP_LC:
+		cdata.cfg.pict_max_exp_lc = vb6801_get_pict_max_exp_lc();
+
+		if (copy_to_user((void *)argp,
+				 &cdata, sizeof(struct sensor_cfg_data)))
+			rc = -EFAULT;
+		break;
+
+	case CFG_SET_FPS:
+	case CFG_SET_PICT_FPS:
+		rc = vb6801_set_fps(&(cdata.cfg.fps));
+		break;
+
+	case CFG_SET_EXP_GAIN:
+		rc = vb6801_write_exp_gain(cdata.cfg.exp_gain.gain,
+					   cdata.cfg.exp_gain.line);
+		break;
+
+	case CFG_SET_PICT_EXP_GAIN:
+		rc = vb6801_set_pict_exp_gain(cdata.cfg.exp_gain.gain,
+					      cdata.cfg.exp_gain.line);
+		break;
+
+	case CFG_SET_MODE:
+		rc = vb6801_set_sensor_mode(cdata.mode, cdata.rs);
+		break;
+
+	case CFG_PWR_DOWN:
+		rc = vb6801_power_down();
+		break;
+
+	case CFG_MOVE_FOCUS:
+		rc = vb6801_move_focus(cdata.cfg.focus.dir,
+				       cdata.cfg.focus.steps);
+		break;
+
+	case CFG_SET_DEFAULT_FOCUS:
+		rc = vb6801_set_default_focus();
+		break;
+
+	default:
+		rc = -EFAULT;
+		break;
+	}
+
+	mutex_unlock(&vb6801_mut);
+
+	return rc;
+}
+
+static int vb6801_sensor_release(void)
+{
+	int rc = -EBADF;
+
+	mutex_lock(&vb6801_mut);
+
+	vb6801_power_down();
+	vb6801_sensor_init_done(vb6801_ctrl->sensordata);
+	kfree(vb6801_ctrl);
+	mutex_unlock(&vb6801_mut);
+
+	return rc;
+}
+
+static int vb6801_i2c_probe(struct i2c_client *client,
+			    const struct i2c_device_id *id)
+{
+	int rc = 0;
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		rc = -ENOTSUPP;
+		goto probe_failure;
+	}
+
+	vb6801_sensorw = kzalloc(sizeof(struct vb6801_work_t), GFP_KERNEL);
+
+	if (!vb6801_sensorw) {
+		rc = -ENOMEM;
+		goto probe_failure;
+	}
+
+	i2c_set_clientdata(client, vb6801_sensorw);
+	vb6801_init_client(client);
+	vb6801_client = client;
+	vb6801_client->addr = vb6801_client->addr >> 1;
+
+	return 0;
+
+probe_failure:
+	if (vb6801_sensorw != NULL) {
+		kfree(vb6801_sensorw);
+		vb6801_sensorw = NULL;
+	}
+	return rc;
+}
+
+static int __exit vb6801_i2c_remove(struct i2c_client *client)
+{
+	struct vb6801_work_t *sensorw = i2c_get_clientdata(client);
+	free_irq(client->irq, sensorw);
+	vb6801_client = NULL;
+	kfree(sensorw);
+	return 0;
+}
+
+static const struct i2c_device_id vb6801_i2c_id[] = {
+	{"vb6801", 0},
+	{}
+};
+
+static struct i2c_driver vb6801_i2c_driver = {
+	.id_table = vb6801_i2c_id,
+	.probe = vb6801_i2c_probe,
+	.remove = __exit_p(vb6801_i2c_remove),
+	.driver = {
+		   .name = "vb6801",
+		   },
+};
+
+static int vb6801_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+	int rc;
+
+	struct vb6801_i2c_reg_conf_t rreg[] = {
+		{0x0000, 0, 0, D_LEN_BYTE},
+		{0x0001, 0, 0, D_LEN_BYTE},
+	};
+
+	rc = vb6801_reset(data);
+	if (rc < 0)
+		goto init_probe_done;
+
+	mdelay(20);
+
+	rc = vb6801_i2c_read_table(rreg, ARRAY_SIZE(rreg));
+	if (rc < 0) {
+		CDBG("I2C Read Table FAILED!!!\n");
+		goto init_probe_fail;
+	}
+
+	/* 4. Compare sensor ID to VB6801 ID: */
+	if (rreg[0].bdata != 0x03 || rreg[1].bdata != 0x53) {
+		CDBG("vb6801_sensor_init: sensor ID don't match!\n");
+		goto init_probe_fail;
+	}
+
+	goto init_probe_done;
+
+init_probe_fail:
+	vb6801_sensor_init_done(data);
+init_probe_done:
+	return rc;
+}
+
+int vb6801_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+	int32_t rc;
+	struct vb6801_i2c_reg_conf_t wreg[] = {
+		{REG_MODE_SELECT, 0, STANDBY_MODE, D_LEN_BYTE},
+		{0x0113, 0, 0x0A, D_LEN_BYTE},
+	};
+
+	vb6801_ctrl = kzalloc(sizeof(struct vb6801_ctrl_t), GFP_KERNEL);
+	if (!vb6801_ctrl) {
+		rc = -ENOMEM;
+		goto open_init_fail1;
+	}
+
+	vb6801_ctrl->factor_fps = 1 /** 0x00000400*/ ;
+	vb6801_ctrl->curr_fps = 7680; /* 30 * Q8 */ ;
+	vb6801_ctrl->max_fps = 7680; /* 30 * Q8 */ ;
+	vb6801_ctrl->pict_exp_update = 0; /* 30 * Q8 */ ;
+	vb6801_ctrl->reducel = 0; /* 30 * Q8 */ ;
+
+	vb6801_ctrl->set_test = TEST_OFF;
+	vb6801_ctrl->prev_res = QTR_SIZE;
+	vb6801_ctrl->pict_res = FULL_SIZE;
+
+	vb6801_ctrl->s_dynamic_params.preview_linesPerFrame =
+	    VB6801_LINES_PER_FRAME_PREVIEW;
+	vb6801_ctrl->s_dynamic_params.preview_pixelsPerLine =
+	    VB6801_PIXELS_PER_LINE_PREVIEW;
+	vb6801_ctrl->s_dynamic_params.snapshot_linesPerFrame =
+	    VB6801_LINES_PER_FRAME_SNAPSHOT;
+	vb6801_ctrl->s_dynamic_params.snapshot_pixelsPerLine =
+	    VB6801_PIXELS_PER_LINE_SNAPSHOT;
+
+	if (data)
+		vb6801_ctrl->sensordata = data;
+
+	/* enable mclk first */
+	msm_camio_clk_rate_set(VB6801_DEFAULT_CLOCK_RATE);
+	mdelay(20);
+
+	rc = vb6801_reset(data);
+	if (rc < 0)
+		goto open_init_fail1;
+
+	rc = vb6801_i2c_write_table(wreg, ARRAY_SIZE(wreg));
+	if (rc < 0) {
+		CDBG("I2C Write Table FAILED!!!\n");
+		goto open_init_fail2;
+	}
+
+	rc = vb6801_read_nvm_data(&vb6801_ctrl->s_info);
+	if (rc < 0) {
+		CDBG("vb6801_read_nvm_data FAILED!!!\n");
+		goto open_init_fail2;
+	}
+	mdelay(66);
+
+	rc = vb6801_config_sensor(12, 30, 60, 2, 1, 10, 2, RES_PREVIEW);
+	if (rc < 0)
+		goto open_init_fail2;
+
+	goto open_init_done;
+
+open_init_fail2:
+	vb6801_sensor_init_done(data);
+open_init_fail1:
+	kfree(vb6801_ctrl);
+open_init_done:
+	return rc;
+}
+
+static int vb6801_sensor_probe(const struct msm_camera_sensor_info *info,
+			       struct msm_sensor_ctrl *s)
+{
+	int rc = i2c_add_driver(&vb6801_i2c_driver);
+	if (rc < 0 || vb6801_client == NULL) {
+		rc = -ENOTSUPP;
+		goto probe_done;
+	}
+
+	/* enable mclk first */
+	msm_camio_clk_rate_set(VB6801_DEFAULT_CLOCK_RATE);
+	mdelay(20);
+
+	rc = vb6801_probe_init_sensor(info);
+	if (rc < 0)
+		goto probe_done;
+
+	s->s_init = vb6801_sensor_open_init;
+	s->s_release = vb6801_sensor_release;
+	s->s_config = vb6801_sensor_config;
+	s->s_mount_angle  = 0;
+	vb6801_sensor_init_done(info);
+
+probe_done:
+	return rc;
+}
+
+static int __vb6801_probe(struct platform_device *pdev)
+{
+	return msm_camera_drv_start(pdev, vb6801_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+	.probe = __vb6801_probe,
+	.driver = {
+		   .name = "msm_camera_vb6801",
+		   .owner = THIS_MODULE,
+		   },
+};
+
+static int __init vb6801_init(void)
+{
+	return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(vb6801_init);
+void vb6801_exit(void)
+{
+	i2c_del_driver(&vb6801_i2c_driver);
+}
diff --git a/drivers/media/video/msm/vb6801.h b/drivers/media/video/msm/vb6801.h
new file mode 100644
index 0000000..8248f8d
--- /dev/null
+++ b/drivers/media/video/msm/vb6801.h
@@ -0,0 +1,66 @@
+/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef VB6801_H
+#define VB6801_H
+
+#include <mach/board.h>
+
+extern struct vb6801_reg_t vb6801_regs;	/* from vb6801_reg.c */
+
+struct reg_struct {
+	uint16_t vt_pix_clk_div;	/*  0x0300 */
+	uint16_t vt_sys_clk_div;	/*  0x0302 */
+	uint16_t pre_pll_clk_div;	/*  0x0304 */
+	uint16_t pll_multiplier;	/*  0x0306 */
+	uint16_t op_pix_clk_div;	/*  0x0308 */
+	uint16_t op_sys_clk_div;	/*  0x030A */
+	uint16_t scale_m;	/*  0x0404 */
+	uint16_t row_speed;	/*  0x3016 */
+	uint16_t x_addr_start;	/*  0x3004 */
+	uint16_t x_addr_end;	/*  0x3008 */
+	uint16_t y_addr_start;	/*  0x3002 */
+	uint16_t y_addr_end;	/*  0x3006 */
+	uint16_t read_mode;	/*  0x3040 */
+	uint16_t x_output_size;	/*  0x034C */
+	uint16_t y_output_size;	/*  0x034E */
+	uint16_t line_length_pck;	/*  0x300C */
+	uint16_t frame_length_lines;	/*  0x300A */
+	uint16_t coarse_int_time;	/*  0x3012 */
+	uint16_t fine_int_time;	/*  0x3014 */
+};
+
+enum i2c_data_len {
+	D_LEN_BYTE,
+	D_LEN_WORD
+};
+
+struct vb6801_i2c_reg_conf_t {
+	unsigned short waddr;
+	unsigned short wdata;
+	uint8_t bdata;
+	enum i2c_data_len dlen;
+};
+
+struct vb6801_reg_t {
+	struct reg_struct const *reg_pat;
+	uint16_t reg_pat_size;
+	struct vb6801_i2c_reg_conf_t const *ttbl;
+	uint16_t ttbl_size;
+	struct vb6801_i2c_reg_conf_t const *lctbl;
+	uint16_t lctbl_size;
+	struct vb6801_i2c_reg_conf_t const *rftbl;
+	uint16_t rftbl_size;
+};
+
+#endif /* VB6801_H */
diff --git a/drivers/media/video/msm/vx6953.c b/drivers/media/video/msm/vx6953.c
new file mode 100644
index 0000000..3b8f14c
--- /dev/null
+++ b/drivers/media/video/msm/vx6953.c
@@ -0,0 +1,3666 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <media/msm_camera.h>
+#include <mach/gpio.h>
+#include <mach/camera.h>
+#include <linux/slab.h>
+#include "vx6953.h"
+
+/*=============================================================
+	SENSOR REGISTER DEFINES
+==============================================================*/
+
+#define REG_GROUPED_PARAMETER_HOLD			0x0104
+#define GROUPED_PARAMETER_HOLD_OFF			0x00
+#define GROUPED_PARAMETER_HOLD				0x01
+#define REG_MODE_SELECT					0x0100
+#define MODE_SELECT_STANDBY_MODE			0x00
+#define MODE_SELECT_STREAM				0x01
+/* Integration Time */
+#define REG_COARSE_INTEGRATION_TIME_HI			0x0202
+#define REG_COARSE_INTEGRATION_TIME_LO			0x0203
+/* Gain */
+#define REG_ANALOGUE_GAIN_CODE_GLOBAL_HI		0x0204
+#define REG_ANALOGUE_GAIN_CODE_GLOBAL_LO		0x0205
+/* Digital Gain */
+#define REG_DIGITAL_GAIN_GREEN_R_HI			0x020E
+#define REG_DIGITAL_GAIN_GREEN_R_LO			0x020F
+#define REG_DIGITAL_GAIN_RED_HI				0x0210
+#define REG_DIGITAL_GAIN_RED_LO				0x0211
+#define REG_DIGITAL_GAIN_BLUE_HI			0x0212
+#define REG_DIGITAL_GAIN_BLUE_LO			0x0213
+#define REG_DIGITAL_GAIN_GREEN_B_HI			0x0214
+#define REG_DIGITAL_GAIN_GREEN_B_LO			0x0215
+/* output bits setting */
+#define REG_0x0112					0x0112
+#define REG_0x0113					0x0113
+/* PLL registers */
+#define REG_VT_PIX_CLK_DIV				0x0301
+#define REG_PRE_PLL_CLK_DIV				0x0305
+#define REG_PLL_MULTIPLIER				0x0307
+#define REG_OP_PIX_CLK_DIV				0x0309
+#define REG_0x034c					0x034c
+#define REG_0x034d					0x034d
+#define REG_0x034e					0x034e
+#define REG_0x034f					0x034f
+#define REG_0x0387					0x0387
+#define REG_0x0383					0x0383
+#define REG_FRAME_LENGTH_LINES_HI			0x0340
+#define REG_FRAME_LENGTH_LINES_LO			0x0341
+#define REG_LINE_LENGTH_PCK_HI				0x0342
+#define REG_LINE_LENGTH_PCK_LO				0x0343
+#define REG_0x3030					0x3030
+#define REG_0x0111					0x0111
+#define REG_0x0136					0x0136
+#define REG_0x0137					0x0137
+#define REG_0x0b00					0x0b00
+#define REG_0x3001					0x3001
+#define REG_0x3004					0x3004
+#define REG_0x3007					0x3007
+#define REG_0x301a					0x301a
+#define REG_0x3101					0x3101
+#define REG_0x3364					0x3364
+#define REG_0x3365					0x3365
+#define REG_0x0b83					0x0b83
+#define REG_0x0b84					0x0b84
+#define REG_0x0b85					0x0b85
+#define REG_0x0b88					0x0b88
+#define REG_0x0b89					0x0b89
+#define REG_0x0b8a					0x0b8a
+#define REG_0x3005					0x3005
+#define REG_0x3010					0x3010
+#define REG_0x3036					0x3036
+#define REG_0x3041					0x3041
+#define REG_0x0b80					0x0b80
+#define REG_0x0900					0x0900
+#define REG_0x0901					0x0901
+#define REG_0x0902					0x0902
+#define REG_0x3016					0x3016
+#define REG_0x301d					0x301d
+#define REG_0x317e					0x317e
+#define REG_0x317f					0x317f
+#define REG_0x3400					0x3400
+#define REG_0x303a					0x303a
+#define REG_0x1716					0x1716
+#define REG_0x1717					0x1717
+#define REG_0x1718					0x1718
+#define REG_0x1719					0x1719
+#define REG_0x3006					0x3006
+#define REG_0x301b					0x301b
+#define REG_0x3098					0x3098
+#define REG_0x309d					0x309d
+#define REG_0x3011					0x3011
+#define REG_0x3035					0x3035
+#define REG_0x3045					0x3045
+#define REG_0x3210					0x3210
+#define	REG_0x0111					0x0111
+#define REG_0x3410					0x3410
+#define REG_0x0b06					0x0b06
+#define REG_0x0b07					0x0b07
+#define REG_0x0b08					0x0b08
+#define REG_0x0b09					0x0b09
+#define REG_0x3640					0x3640
+/* Test Pattern */
+#define REG_TEST_PATTERN_MODE				0x0601
+
+/*============================================================================
+							 TYPE DECLARATIONS
+============================================================================*/
+
+/* 16bit address - 8 bit context register structure */
+#define	VX6953_STM5M0EDOF_OFFSET	9
+#define	Q8		0x00000100
+#define	Q10		0x00000400
+#define	VX6953_STM5M0EDOF_MAX_SNAPSHOT_EXPOSURE_LINE_COUNT	2922
+#define	VX6953_STM5M0EDOF_DEFAULT_MASTER_CLK_RATE	24000000
+#define	VX6953_STM5M0EDOF_OP_PIXEL_CLOCK_RATE	79800000
+#define	VX6953_STM5M0EDOF_VT_PIXEL_CLOCK_RATE	88670000
+/* Full	Size */
+#define	VX6953_FULL_SIZE_WIDTH	2608
+#define	VX6953_FULL_SIZE_HEIGHT		1960
+#define	VX6953_FULL_SIZE_DUMMY_PIXELS	1
+#define	VX6953_FULL_SIZE_DUMMY_LINES	0
+/* Quarter Size	*/
+#define	VX6953_QTR_SIZE_WIDTH	1304
+#define	VX6953_QTR_SIZE_HEIGHT		980
+#define	VX6953_QTR_SIZE_DUMMY_PIXELS	1
+#define	VX6953_QTR_SIZE_DUMMY_LINES		0
+/* Blanking	as measured	on the scope */
+/* Full	Size */
+#define	VX6953_HRZ_FULL_BLK_PIXELS	348
+#define	VX6953_VER_FULL_BLK_LINES	40
+/* Quarter Size	*/
+#define	VX6953_HRZ_QTR_BLK_PIXELS	1628
+#define	VX6953_VER_QTR_BLK_LINES	28
+#define	MAX_LINE_LENGTH_PCK		8190
+#define	MAX_FRAME_LENGTH_LINES	16383
+#define	VX6953_REVISION_NUMBER_CUT2	0x10/*revision number	for	Cut2.0*/
+#define	VX6953_REVISION_NUMBER_CUT3	0x20/*revision number	for	Cut3.0*/
+/* FIXME: Changes from here */
+struct vx6953_work_t {
+	struct work_struct work;
+};
+
+static struct vx6953_work_t *vx6953_sensorw;
+static struct i2c_client *vx6953_client;
+
+struct vx6953_ctrl_t {
+	const struct  msm_camera_sensor_info *sensordata;
+
+	uint32_t sensormode;
+	uint32_t fps_divider;   	/* init to 1 * 0x00000400 */
+	uint32_t pict_fps_divider;  /* init to 1 * 0x00000400 */
+	uint16_t fps;
+
+	int16_t curr_lens_pos;
+	uint16_t curr_step_pos;
+	uint16_t my_reg_gain;
+	uint32_t my_reg_line_count;
+	uint16_t total_lines_per_frame;
+
+	enum vx6953_resolution_t prev_res;
+	enum vx6953_resolution_t pict_res;
+	enum vx6953_resolution_t curr_res;
+	enum vx6953_test_mode_t  set_test;
+	enum sensor_revision_t sensor_type;
+
+	enum edof_mode_t edof_mode;
+
+	unsigned short imgaddr;
+};
+
+
+static uint8_t vx6953_stm5m0edof_delay_msecs_stdby;
+static uint16_t vx6953_stm5m0edof_delay_msecs_stream = 20;
+static uint8_t count;
+static struct vx6953_ctrl_t *vx6953_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(vx6953_wait_queue);
+DEFINE_MUTEX(vx6953_mut);
+static struct vx6953_i2c_reg_conf patch_tbl_cut2[] = {
+	{0xFB94, 0},	/*intialise Data Xfer Status reg*/
+	{0xFB95, 0},	/*gain 1	  (0x00)*/
+	{0xFB96, 0},	/*gain 1.07   (0x10)*/
+	{0xFB97, 0},	/*gain 1.14   (0x20)*/
+	{0xFB98, 0},	/*gain 1.23   (0x30)*/
+	{0xFB99, 0},	/*gain 1.33   (0x40)*/
+	{0xFB9A, 0},	/*gain 1.45   (0x50)*/
+	{0xFB9B, 0},	/*gain 1.6    (0x60)*/
+	{0xFB9C, 0},	/*gain 1.78   (0x70)*/
+	{0xFB9D, 2},	/*gain 2	  (0x80)*/
+	{0xFB9E, 2},	/*gain 2.29   (0x90)*/
+	{0xFB9F, 3},	/*gain 2.67   (0xA0)*/
+	{0xFBA0, 3},	/*gain 3.2    (0xB0)*/
+	{0xFBA1, 4},	/*gain 4	  (0xC0)*/
+	{0xFBA2, 7},	/*gain 5.33   (0xD0)*/
+	{0xFBA3, 10},	/*gain 8	  (0xE0)*/
+	{0xFBA4, 11},	/*gain 9.14   (0xE4)*/
+	{0xFBA5, 13},	/*gain 10.67  (0xE8)*/
+	{0xFBA6, 15},	/*gain 12.8   (0xEC)*/
+	{0xFBA7, 19},	/*gain 16     (0xF0)*/
+	{0xF800, 0x12},
+	{0xF801, 0x06},
+	{0xF802, 0xf7},
+	{0xF803, 0x90},
+	{0xF804, 0x02},
+	{0xF805, 0x05},
+	{0xF806, 0xe0},
+	{0xF807, 0xff},
+	{0xF808, 0x65},
+	{0xF809, 0x7d},
+	{0xF80A, 0x70},
+	{0xF80B, 0x03},
+	{0xF80C, 0x02},
+	{0xF80D, 0xf9},
+	{0xF80E, 0x1c},
+	{0xF80F, 0x8f},
+	{0xF810, 0x7d},
+	{0xF811, 0xe4},
+	{0xF812, 0xf5},
+	{0xF813, 0x7a},
+	{0xF814, 0x75},
+	{0xF815, 0x78},
+	{0xF816, 0x30},
+	{0xF817, 0x75},
+	{0xF818, 0x79},
+	{0xF819, 0x53},
+	{0xF81A, 0x85},
+	{0xF81B, 0x79},
+	{0xF81C, 0x82},
+	{0xF81D, 0x85},
+	{0xF81E, 0x78},
+	{0xF81F, 0x83},
+	{0xF820, 0xe0},
+	{0xF821, 0xc3},
+	{0xF822, 0x95},
+	{0xF823, 0x7b},
+	{0xF824, 0xf0},
+	{0xF825, 0x74},
+	{0xF826, 0x02},
+	{0xF827, 0x25},
+	{0xF828, 0x79},
+	{0xF829, 0xf5},
+	{0xF82A, 0x79},
+	{0xF82B, 0xe4},
+	{0xF82C, 0x35},
+	{0xF82D, 0x78},
+	{0xF82E, 0xf5},
+	{0xF82F, 0x78},
+	{0xF830, 0x05},
+	{0xF831, 0x7a},
+	{0xF832, 0xe5},
+	{0xF833, 0x7a},
+	{0xF834, 0xb4},
+	{0xF835, 0x08},
+	{0xF836, 0xe3},
+	{0xF837, 0xe5},
+	{0xF838, 0x7d},
+	{0xF839, 0x70},
+	{0xF83A, 0x04},
+	{0xF83B, 0xff},
+	{0xF83C, 0x02},
+	{0xF83D, 0xf8},
+	{0xF83E, 0xe4},
+	{0xF83F, 0xe5},
+	{0xF840, 0x7d},
+	{0xF841, 0xb4},
+	{0xF842, 0x10},
+	{0xF843, 0x05},
+	{0xF844, 0x7f},
+	{0xF845, 0x01},
+	{0xF846, 0x02},
+	{0xF847, 0xf8},
+	{0xF848, 0xe4},
+	{0xF849, 0xe5},
+	{0xF84A, 0x7d},
+	{0xF84B, 0xb4},
+	{0xF84C, 0x20},
+	{0xF84D, 0x05},
+	{0xF84E, 0x7f},
+	{0xF84F, 0x02},
+	{0xF850, 0x02},
+	{0xF851, 0xf8},
+	{0xF852, 0xe4},
+	{0xF853, 0xe5},
+	{0xF854, 0x7d},
+	{0xF855, 0xb4},
+	{0xF856, 0x30},
+	{0xF857, 0x05},
+	{0xF858, 0x7f},
+	{0xF859, 0x03},
+	{0xF85A, 0x02},
+	{0xF85B, 0xf8},
+	{0xF85C, 0xe4},
+	{0xF85D, 0xe5},
+	{0xF85E, 0x7d},
+	{0xF85F, 0xb4},
+	{0xF860, 0x40},
+	{0xF861, 0x04},
+	{0xF862, 0x7f},
+	{0xF863, 0x04},
+	{0xF864, 0x80},
+	{0xF865, 0x7e},
+	{0xF866, 0xe5},
+	{0xF867, 0x7d},
+	{0xF868, 0xb4},
+	{0xF869, 0x50},
+	{0xF86A, 0x04},
+	{0xF86B, 0x7f},
+	{0xF86C, 0x05},
+	{0xF86D, 0x80},
+	{0xF86E, 0x75},
+	{0xF86F, 0xe5},
+	{0xF870, 0x7d},
+	{0xF871, 0xb4},
+	{0xF872, 0x60},
+	{0xF873, 0x04},
+	{0xF874, 0x7f},
+	{0xF875, 0x06},
+	{0xF876, 0x80},
+	{0xF877, 0x6c},
+	{0xF878, 0xe5},
+	{0xF879, 0x7d},
+	{0xF87A, 0xb4},
+	{0xF87B, 0x70},
+	{0xF87C, 0x04},
+	{0xF87D, 0x7f},
+	{0xF87E, 0x07},
+	{0xF87F, 0x80},
+	{0xF880, 0x63},
+	{0xF881, 0xe5},
+	{0xF882, 0x7d},
+	{0xF883, 0xb4},
+	{0xF884, 0x80},
+	{0xF885, 0x04},
+	{0xF886, 0x7f},
+	{0xF887, 0x08},
+	{0xF888, 0x80},
+	{0xF889, 0x5a},
+	{0xF88A, 0xe5},
+	{0xF88B, 0x7d},
+	{0xF88C, 0xb4},
+	{0xF88D, 0x90},
+	{0xF88E, 0x04},
+	{0xF88F, 0x7f},
+	{0xF890, 0x09},
+	{0xF891, 0x80},
+	{0xF892, 0x51},
+	{0xF893, 0xe5},
+	{0xF894, 0x7d},
+	{0xF895, 0xb4},
+	{0xF896, 0xa0},
+	{0xF897, 0x04},
+	{0xF898, 0x7f},
+	{0xF899, 0x0a},
+	{0xF89A, 0x80},
+	{0xF89B, 0x48},
+	{0xF89C, 0xe5},
+	{0xF89D, 0x7d},
+	{0xF89E, 0xb4},
+	{0xF89F, 0xb0},
+	{0xF8A0, 0x04},
+	{0xF8A1, 0x7f},
+	{0xF8A2, 0x0b},
+	{0xF8A3, 0x80},
+	{0xF8A4, 0x3f},
+	{0xF8A5, 0xe5},
+	{0xF8A6, 0x7d},
+	{0xF8A7, 0xb4},
+	{0xF8A8, 0xc0},
+	{0xF8A9, 0x04},
+	{0xF8AA, 0x7f},
+	{0xF8AB, 0x0c},
+	{0xF8AC, 0x80},
+	{0xF8AD, 0x36},
+	{0xF8AE, 0xe5},
+	{0xF8AF, 0x7d},
+	{0xF8B0, 0xb4},
+	{0xF8B1, 0xd0},
+	{0xF8B2, 0x04},
+	{0xF8B3, 0x7f},
+	{0xF8B4, 0x0d},
+	{0xF8B5, 0x80},
+	{0xF8B6, 0x2d},
+	{0xF8B7, 0xe5},
+	{0xF8B8, 0x7d},
+	{0xF8B9, 0xb4},
+	{0xF8BA, 0xe0},
+	{0xF8BB, 0x04},
+	{0xF8BC, 0x7f},
+	{0xF8BD, 0x0e},
+	{0xF8BE, 0x80},
+	{0xF8BF, 0x24},
+	{0xF8C0, 0xe5},
+	{0xF8C1, 0x7d},
+	{0xF8C2, 0xb4},
+	{0xF8C3, 0xe4},
+	{0xF8C4, 0x04},
+	{0xF8C5, 0x7f},
+	{0xF8C6, 0x0f},
+	{0xF8C7, 0x80},
+	{0xF8C8, 0x1b},
+	{0xF8C9, 0xe5},
+	{0xF8CA, 0x7d},
+	{0xF8CB, 0xb4},
+	{0xF8CC, 0xe8},
+	{0xF8CD, 0x04},
+	{0xF8CE, 0x7f},
+	{0xF8CF, 0x10},
+	{0xF8D0, 0x80},
+	{0xF8D1, 0x12},
+	{0xF8D2, 0xe5},
+	{0xF8D3, 0x7d},
+	{0xF8D4, 0xb4},
+	{0xF8D5, 0xec},
+	{0xF8D6, 0x04},
+	{0xF8D7, 0x7f},
+	{0xF8D8, 0x11},
+	{0xF8D9, 0x80},
+	{0xF8DA, 0x09},
+	{0xF8DB, 0xe5},
+	{0xF8DC, 0x7d},
+	{0xF8DD, 0x7f},
+	{0xF8DE, 0x00},
+	{0xF8DF, 0xb4},
+	{0xF8E0, 0xf0},
+	{0xF8E1, 0x02},
+	{0xF8E2, 0x7f},
+	{0xF8E3, 0x12},
+	{0xF8E4, 0x8f},
+	{0xF8E5, 0x7c},
+	{0xF8E6, 0xef},
+	{0xF8E7, 0x24},
+	{0xF8E8, 0x95},
+	{0xF8E9, 0xff},
+	{0xF8EA, 0xe4},
+	{0xF8EB, 0x34},
+	{0xF8EC, 0xfb},
+	{0xF8ED, 0x8f},
+	{0xF8EE, 0x82},
+	{0xF8EF, 0xf5},
+	{0xF8F0, 0x83},
+	{0xF8F1, 0xe4},
+	{0xF8F2, 0x93},
+	{0xF8F3, 0xf5},
+	{0xF8F4, 0x7c},
+	{0xF8F5, 0xf5},
+	{0xF8F6, 0x7b},
+	{0xF8F7, 0xe4},
+	{0xF8F8, 0xf5},
+	{0xF8F9, 0x7a},
+	{0xF8FA, 0x75},
+	{0xF8FB, 0x78},
+	{0xF8FC, 0x30},
+	{0xF8FD, 0x75},
+	{0xF8FE, 0x79},
+	{0xF8FF, 0x53},
+	{0xF900, 0x85},
+	{0xF901, 0x79},
+	{0xF902, 0x82},
+	{0xF903, 0x85},
+	{0xF904, 0x78},
+	{0xF905, 0x83},
+	{0xF906, 0xe0},
+	{0xF907, 0x25},
+	{0xF908, 0x7c},
+	{0xF909, 0xf0},
+	{0xF90A, 0x74},
+	{0xF90B, 0x02},
+	{0xF90C, 0x25},
+	{0xF90D, 0x79},
+	{0xF90E, 0xf5},
+	{0xF90F, 0x79},
+	{0xF910, 0xe4},
+	{0xF911, 0x35},
+	{0xF912, 0x78},
+	{0xF913, 0xf5},
+	{0xF914, 0x78},
+	{0xF915, 0x05},
+	{0xF916, 0x7a},
+	{0xF917, 0xe5},
+	{0xF918, 0x7a},
+	{0xF919, 0xb4},
+	{0xF91A, 0x08},
+	{0xF91B, 0xe4},
+	{0xF91C, 0x02},
+	{0xF91D, 0x18},
+	{0xF91E, 0x32},
+	{0xF91F, 0x22},
+	{0xF920, 0xf0},
+	{0xF921, 0x90},
+	{0xF922, 0xa0},
+	{0xF923, 0xf8},
+	{0xF924, 0xe0},
+	{0xF925, 0x70},
+	{0xF926, 0x02},
+	{0xF927, 0xa3},
+	{0xF928, 0xe0},
+	{0xF929, 0x70},
+	{0xF92A, 0x0a},
+	{0xF92B, 0x90},
+	{0xF92C, 0xa1},
+	{0xF92D, 0x10},
+	{0xF92E, 0xe0},
+	{0xF92F, 0xfe},
+	{0xF930, 0xa3},
+	{0xF931, 0xe0},
+	{0xF932, 0xff},
+	{0xF933, 0x80},
+	{0xF934, 0x04},
+	{0xF935, 0x7e},
+	{0xF936, 0x00},
+	{0xF937, 0x7f},
+	{0xF938, 0x00},
+	{0xF939, 0x8e},
+	{0xF93A, 0x7e},
+	{0xF93B, 0x8f},
+	{0xF93C, 0x7f},
+	{0xF93D, 0x90},
+	{0xF93E, 0x36},
+	{0xF93F, 0x0d},
+	{0xF940, 0xe0},
+	{0xF941, 0x44},
+	{0xF942, 0x02},
+	{0xF943, 0xf0},
+	{0xF944, 0x90},
+	{0xF945, 0x36},
+	{0xF946, 0x0e},
+	{0xF947, 0xe5},
+	{0xF948, 0x7e},
+	{0xF949, 0xf0},
+	{0xF94A, 0xa3},
+	{0xF94B, 0xe5},
+	{0xF94C, 0x7f},
+	{0xF94D, 0xf0},
+	{0xF94E, 0xe5},
+	{0xF94F, 0x3a},
+	{0xF950, 0x60},
+	{0xF951, 0x0c},
+	{0xF952, 0x90},
+	{0xF953, 0x36},
+	{0xF954, 0x09},
+	{0xF955, 0xe0},
+	{0xF956, 0x70},
+	{0xF957, 0x06},
+	{0xF958, 0x90},
+	{0xF959, 0x36},
+	{0xF95A, 0x08},
+	{0xF95B, 0xf0},
+	{0xF95C, 0xf5},
+	{0xF95D, 0x3a},
+	{0xF95E, 0x02},
+	{0xF95F, 0x03},
+	{0xF960, 0x94},
+	{0xF961, 0x22},
+	{0xF962, 0x78},
+	{0xF963, 0x07},
+	{0xF964, 0xe6},
+	{0xF965, 0xd3},
+	{0xF966, 0x94},
+	{0xF967, 0x00},
+	{0xF968, 0x40},
+	{0xF969, 0x16},
+	{0xF96A, 0x16},
+	{0xF96B, 0xe6},
+	{0xF96C, 0x90},
+	{0xF96D, 0x30},
+	{0xF96E, 0xa1},
+	{0xF96F, 0xf0},
+	{0xF970, 0x90},
+	{0xF971, 0x43},
+	{0xF972, 0x83},
+	{0xF973, 0xe0},
+	{0xF974, 0xb4},
+	{0xF975, 0x01},
+	{0xF976, 0x0f},
+	{0xF977, 0x90},
+	{0xF978, 0x43},
+	{0xF979, 0x87},
+	{0xF97A, 0xe0},
+	{0xF97B, 0xb4},
+	{0xF97C, 0x01},
+	{0xF97D, 0x08},
+	{0xF97E, 0x80},
+	{0xF97F, 0x00},
+	{0xF980, 0x90},
+	{0xF981, 0x30},
+	{0xF982, 0xa0},
+	{0xF983, 0x74},
+	{0xF984, 0x01},
+	{0xF985, 0xf0},
+	{0xF986, 0x22},
+	{0xF987, 0xf0},
+	{0xF988, 0x90},
+	{0xF989, 0x35},
+	{0xF98A, 0xba},
+	{0xF98B, 0xe0},
+	{0xF98C, 0xb4},
+	{0xF98D, 0x0a},
+	{0xF98E, 0x0d},
+	{0xF98F, 0xa3},
+	{0xF990, 0xe0},
+	{0xF991, 0xb4},
+	{0xF992, 0x01},
+	{0xF993, 0x08},
+	{0xF994, 0x90},
+	{0xF995, 0xfb},
+	{0xF996, 0x94},
+	{0xF997, 0xe0},
+	{0xF998, 0x90},
+	{0xF999, 0x35},
+	{0xF99A, 0xb8},
+	{0xF99B, 0xf0},
+	{0xF99C, 0xd0},
+	{0xF99D, 0xd0},
+	{0xF99E, 0xd0},
+	{0xF99F, 0x82},
+	{0xF9A0, 0xd0},
+	{0xF9A1, 0x83},
+	{0xF9A2, 0xd0},
+	{0xF9A3, 0xe0},
+	{0xF9A4, 0x32},
+	{0xF9A5, 0x22},
+	{0xF9A6, 0xe5},
+	{0xF9A7, 0x7f},
+	{0xF9A8, 0x45},
+	{0xF9A9, 0x7e},
+	{0xF9AA, 0x60},
+	{0xF9AB, 0x15},
+	{0xF9AC, 0x90},
+	{0xF9AD, 0x01},
+	{0xF9AE, 0x00},
+	{0xF9AF, 0xe0},
+	{0xF9B0, 0x70},
+	{0xF9B1, 0x0f},
+	{0xF9B2, 0x90},
+	{0xF9B3, 0xa0},
+	{0xF9B4, 0xf8},
+	{0xF9B5, 0xe5},
+	{0xF9B6, 0x7e},
+	{0xF9B7, 0xf0},
+	{0xF9B8, 0xa3},
+	{0xF9B9, 0xe5},
+	{0xF9BA, 0x7f},
+	{0xF9BB, 0xf0},
+	{0xF9BC, 0xe4},
+	{0xF9BD, 0xf5},
+	{0xF9BE, 0x7e},
+	{0xF9BF, 0xf5},
+	{0xF9C0, 0x7f},
+	{0xF9C1, 0x22},
+	{0xF9C2, 0x02},
+	{0xF9C3, 0x0e},
+	{0xF9C4, 0x79},
+	{0xF9C5, 0x22},
+	/* Offsets:*/
+	{0x35C6, 0x00},/* FIDDLEDARKCAL*/
+	{0x35C7, 0x00},
+	{0x35C8, 0x01},/*STOREDISTANCEATSTOPSTREAMING*/
+	{0x35C9, 0x20},
+	{0x35CA, 0x01},/*BRUCEFIX*/
+	{0x35CB, 0x62},
+	{0x35CC, 0x01},/*FIXDATAXFERSTATUSREG*/
+	{0x35CD, 0x87},
+	{0x35CE, 0x01},/*FOCUSDISTANCEUPDATE*/
+	{0x35CF, 0xA6},
+	{0x35D0, 0x01},/*SKIPEDOFRESET*/
+	{0x35D1, 0xC2},
+	{0x35D2, 0x00},
+	{0x35D3, 0xFB},
+	{0x35D4, 0x00},
+	{0x35D5, 0x94},
+	{0x35D6, 0x00},
+	{0x35D7, 0xFB},
+	{0x35D8, 0x00},
+	{0x35D9, 0x94},
+	{0x35DA, 0x00},
+	{0x35DB, 0xFB},
+	{0x35DC, 0x00},
+	{0x35DD, 0x94},
+	{0x35DE, 0x00},
+	{0x35DF, 0xFB},
+	{0x35E0, 0x00},
+	{0x35E1, 0x94},
+	{0x35E6, 0x18},/* FIDDLEDARKCAL*/
+	{0x35E7, 0x2F},
+	{0x35E8, 0x03},/* STOREDISTANCEATSTOPSTREAMING*/
+	{0x35E9, 0x93},
+	{0x35EA, 0x18},/* BRUCEFIX*/
+	{0x35EB, 0x99},
+	{0x35EC, 0x00},/* FIXDATAXFERSTATUSREG*/
+	{0x35ED, 0xA3},
+	{0x35EE, 0x21},/* FOCUSDISTANCEUPDATE*/
+	{0x35EF, 0x5B},
+	{0x35F0, 0x0E},/* SKIPEDOFRESET*/
+	{0x35F1, 0x74},
+	{0x35F2, 0x04},
+	{0x35F3, 0x64},
+	{0x35F4, 0x04},
+	{0x35F5, 0x65},
+	{0x35F6, 0x04},
+	{0x35F7, 0x7B},
+	{0x35F8, 0x04},
+	{0x35F9, 0x7C},
+	{0x35FA, 0x04},
+	{0x35FB, 0xDD},
+	{0x35FC, 0x04},
+	{0x35FD, 0xDE},
+	{0x35FE, 0x04},
+	{0x35FF, 0xEF},
+	{0x3600, 0x04},
+	{0x3601, 0xF0},
+	/*Jump/Data:*/
+	{0x35C2, 0x3F},/* Jump Reg*/
+	{0x35C3, 0xFF},/* Jump Reg*/
+	{0x35C4, 0x3F},/* Data Reg*/
+	{0x35C5, 0xC0},/* Data Reg*/
+	{0x35C0, 0x01},/* Enable*/
+
+};
+
+static struct vx6953_i2c_reg_conf cut3_cali_data[] = {
+		{0x360A, 0x07 },
+		{0x3530, 0x07 },
+		{0x35B5, 0x00 },
+		{0x35BC, 0x00 },
+		{0xAFF8, 0x00 },
+		{0xAFF9, 0x01 },
+		{0xF800, 0x90 },
+		{0xF801, 0x30 },
+		{0xF802, 0x31 },
+		{0xF803, 0xe0 },
+		{0xF804, 0xf5 },
+		{0xF805, 0x7d },
+		{0xF806, 0xb4 },
+		{0xF807, 0x01 },
+		{0xF808, 0x06 },
+		{0xF809, 0x75 },
+		{0xF80A, 0x7d },
+		{0xF80B, 0x03 },
+		{0xF80C, 0x74 },
+		{0xF80D, 0x03 },
+		{0xF80E, 0xf0 },
+		{0xF80F, 0x90 },
+		{0xF810, 0x30 },
+		{0xF811, 0x04 },
+		{0xF812, 0x74 },
+		{0xF813, 0x33 },
+		{0xF814, 0xf0 },
+		{0xF815, 0x90 },
+		{0xF816, 0x30 },
+		{0xF817, 0x06 },
+		{0xF818, 0xe4 },
+		{0xF819, 0xf0 },
+		{0xF81A, 0xa3 },
+		{0xF81B, 0x74 },
+		{0xF81C, 0x08 },
+		{0xF81D, 0xf0 },
+		{0xF81E, 0x90 },
+		{0xF81F, 0x30 },
+		{0xF820, 0x10 },
+		{0xF821, 0xe4 },
+		{0xF822, 0xf0 },
+		{0xF823, 0xa3 },
+		{0xF824, 0xf0 },
+		{0xF825, 0x90 },
+		{0xF826, 0x30 },
+		{0xF827, 0x16 },
+		{0xF828, 0x74 },
+		{0xF829, 0x1e },
+		{0xF82A, 0xf0 },
+		{0xF82B, 0x90 },
+		{0xF82C, 0x30 },
+		{0xF82D, 0x1a },
+		{0xF82E, 0x74 },
+		{0xF82F, 0x6a },
+		{0xF830, 0xf0 },
+		{0xF831, 0x90 },
+		{0xF832, 0x30 },
+		{0xF833, 0x30 },
+		{0xF834, 0x74 },
+		{0xF835, 0x08 },
+		{0xF836, 0xf0 },
+		{0xF837, 0x90 },
+		{0xF838, 0x30 },
+		{0xF839, 0x36 },
+		{0xF83A, 0x74 },
+		{0xF83B, 0x2c },
+		{0xF83C, 0xf0 },
+		{0xF83D, 0x90 },
+		{0xF83E, 0x30 },
+		{0xF83F, 0x41 },
+		{0xF840, 0xe4 },
+		{0xF841, 0xf0 },
+		{0xF842, 0xa3 },
+		{0xF843, 0x74 },
+		{0xF844, 0x24 },
+		{0xF845, 0xf0 },
+		{0xF846, 0x90 },
+		{0xF847, 0x30 },
+		{0xF848, 0x45 },
+		{0xF849, 0x74 },
+		{0xF84A, 0x81 },
+		{0xF84B, 0xf0 },
+		{0xF84C, 0x90 },
+		{0xF84D, 0x30 },
+		{0xF84E, 0x98 },
+		{0xF84F, 0x74 },
+		{0xF850, 0x01 },
+		{0xF851, 0xf0 },
+		{0xF852, 0x90 },
+		{0xF853, 0x30 },
+		{0xF854, 0x9d },
+		{0xF855, 0x74 },
+		{0xF856, 0x05 },
+		{0xF857, 0xf0 },
+		{0xF858, 0xe5 },
+		{0xF859, 0x7d },
+		{0xF85A, 0x70 },
+		{0xF85B, 0x22 },
+		{0xF85C, 0x90 },
+		{0xF85D, 0x02 },
+		{0xF85E, 0x00 },
+		{0xF85F, 0x74 },
+		{0xF860, 0x02 },
+		{0xF861, 0xf0 },
+		{0xF862, 0xa3 },
+		{0xF863, 0x74 },
+		{0xF864, 0x54 },
+		{0xF865, 0xf0 },
+		{0xF866, 0x90 },
+		{0xF867, 0x30 },
+		{0xF868, 0x05 },
+		{0xF869, 0x74 },
+		{0xF86A, 0x01 },
+		{0xF86B, 0xf0 },
+		{0xF86C, 0x90 },
+		{0xF86D, 0x30 },
+		{0xF86E, 0x1b },
+		{0xF86F, 0x74 },
+		{0xF870, 0x29 },
+		{0xF871, 0xf0 },
+		{0xF872, 0x90 },
+		{0xF873, 0x30 },
+		{0xF874, 0x30 },
+		{0xF875, 0xe4 },
+		{0xF876, 0xf0 },
+		{0xF877, 0x90 },
+		{0xF878, 0x30 },
+		{0xF879, 0x35 },
+		{0xF87A, 0x04 },
+		{0xF87B, 0xf0 },
+		{0xF87C, 0x80 },
+		{0xF87D, 0x69 },
+		{0xF87E, 0xe5 },
+		{0xF87F, 0x7d },
+		{0xF880, 0x64 },
+		{0xF881, 0x02 },
+		{0xF882, 0x70 },
+		{0xF883, 0x3c },
+		{0xF884, 0x90 },
+		{0xF885, 0x02 },
+		{0xF886, 0x00 },
+		{0xF887, 0x74 },
+		{0xF888, 0x04 },
+		{0xF889, 0xf0 },
+		{0xF88A, 0xa3 },
+		{0xF88B, 0x74 },
+		{0xF88C, 0x10 },
+		{0xF88D, 0xf0 },
+		{0xF88E, 0x90 },
+		{0xF88F, 0x30 },
+		{0xF890, 0x04 },
+		{0xF891, 0x74 },
+		{0xF892, 0x34 },
+		{0xF893, 0xf0 },
+		{0xF894, 0xa3 },
+		{0xF895, 0x74 },
+		{0xF896, 0x07 },
+		{0xF897, 0xf0 },
+		{0xF898, 0x90 },
+		{0xF899, 0x30 },
+		{0xF89A, 0x10 },
+		{0xF89B, 0x74 },
+		{0xF89C, 0x10 },
+		{0xF89D, 0xf0 },
+		{0xF89E, 0x90 },
+		{0xF89F, 0x30 },
+		{0xF8A0, 0x16 },
+		{0xF8A1, 0x74 },
+		{0xF8A2, 0x1f },
+		{0xF8A3, 0xf0 },
+		{0xF8A4, 0x90 },
+		{0xF8A5, 0x30 },
+		{0xF8A6, 0x1a },
+		{0xF8A7, 0x74 },
+		{0xF8A8, 0x62 },
+		{0xF8A9, 0xf0 },
+		{0xF8AA, 0xa3 },
+		{0xF8AB, 0x74 },
+		{0xF8AC, 0x2a },
+		{0xF8AD, 0xf0 },
+		{0xF8AE, 0x90 },
+		{0xF8AF, 0x30 },
+		{0xF8B0, 0x35 },
+		{0xF8B1, 0x74 },
+		{0xF8B2, 0x04 },
+		{0xF8B3, 0xf0 },
+		{0xF8B4, 0x90 },
+		{0xF8B5, 0x30 },
+		{0xF8B6, 0x41 },
+		{0xF8B7, 0x74 },
+		{0xF8B8, 0x60 },
+		{0xF8B9, 0xf0 },
+		{0xF8BA, 0xa3 },
+		{0xF8BB, 0x74 },
+		{0xF8BC, 0x64 },
+		{0xF8BD, 0xf0 },
+		{0xF8BE, 0x80 },
+		{0xF8BF, 0x27 },
+		{0xF8C0, 0xe5 },
+		{0xF8C1, 0x7d },
+		{0xF8C2, 0xb4 },
+		{0xF8C3, 0x03 },
+		{0xF8C4, 0x22 },
+		{0xF8C5, 0x90 },
+		{0xF8C6, 0x02 },
+		{0xF8C7, 0x00 },
+		{0xF8C8, 0x74 },
+		{0xF8C9, 0x02 },
+		{0xF8CA, 0xf0 },
+		{0xF8CB, 0xa3 },
+		{0xF8CC, 0x74 },
+		{0xF8CD, 0x26 },
+		{0xF8CE, 0xf0 },
+		{0xF8CF, 0x90 },
+		{0xF8D0, 0x30 },
+		{0xF8D1, 0x05 },
+		{0xF8D2, 0x74 },
+		{0xF8D3, 0x03 },
+		{0xF8D4, 0xf0 },
+		{0xF8D5, 0x90 },
+		{0xF8D6, 0x30 },
+		{0xF8D7, 0x11 },
+		{0xF8D8, 0x74 },
+		{0xF8D9, 0x01 },
+		{0xF8DA, 0xf0 },
+		{0xF8DB, 0x90 },
+		{0xF8DC, 0x30 },
+		{0xF8DD, 0x1b },
+		{0xF8DE, 0x74 },
+		{0xF8DF, 0x2a },
+		{0xF8E0, 0xf0 },
+		{0xF8E1, 0x90 },
+		{0xF8E2, 0x30 },
+		{0xF8E3, 0x35 },
+		{0xF8E4, 0x74 },
+		{0xF8E5, 0x03 },
+		{0xF8E6, 0xf0 },
+		{0xF8E7, 0x90 },
+		{0xF8E8, 0x41 },
+		{0xF8E9, 0x01 },
+		{0xF8EA, 0xe0 },
+		{0xF8EB, 0xf5 },
+		{0xF8EC, 0x79 },
+		{0xF8ED, 0x90 },
+		{0xF8EE, 0x43 },
+		{0xF8EF, 0x87 },
+		{0xF8F0, 0xe0 },
+		{0xF8F1, 0xf5 },
+		{0xF8F2, 0x7a },
+		{0xF8F3, 0x90 },
+		{0xF8F4, 0x42 },
+		{0xF8F5, 0x05 },
+		{0xF8F6, 0xe0 },
+		{0xF8F7, 0xf5 },
+		{0xF8F8, 0x7b },
+		{0xF8F9, 0x22 },
+		{0xF8FA, 0x78 },
+		{0xF8FB, 0x07 },
+		{0xF8FC, 0xe6 },
+		{0xF8FD, 0xf5 },
+		{0xF8FE, 0x7c },
+		{0xF8FF, 0xe5 },
+		{0xF900, 0x7c },
+		{0xF901, 0x60 },
+		{0xF902, 0x1e },
+		{0xF903, 0x90 },
+		{0xF904, 0x43 },
+		{0xF905, 0x83 },
+		{0xF906, 0xe0 },
+		{0xF907, 0xb4 },
+		{0xF908, 0x01 },
+		{0xF909, 0x17 },
+		{0xF90A, 0x90 },
+		{0xF90B, 0x43 },
+		{0xF90C, 0x87 },
+		{0xF90D, 0xe0 },
+		{0xF90E, 0xb4 },
+		{0xF90F, 0x01 },
+		{0xF910, 0x10 },
+		{0xF911, 0x15 },
+		{0xF912, 0x7c },
+		{0xF913, 0x90 },
+		{0xF914, 0x30 },
+		{0xF915, 0xa1 },
+		{0xF916, 0xe5 },
+		{0xF917, 0x7c },
+		{0xF918, 0xf0 },
+		{0xF919, 0x90 },
+		{0xF91A, 0x30 },
+		{0xF91B, 0xa0 },
+		{0xF91C, 0x74 },
+		{0xF91D, 0x01 },
+		{0xF91E, 0xf0 },
+		{0xF91F, 0x80 },
+		{0xF920, 0x05 },
+		{0xF921, 0xe4 },
+		{0xF922, 0x90 },
+		{0xF923, 0x30 },
+		{0xF924, 0xa0 },
+		{0xF925, 0xf0 },
+		{0xF926, 0x90 },
+		{0xF927, 0x41 },
+		{0xF928, 0x01 },
+		{0xF929, 0xe0 },
+		{0xF92A, 0xfc },
+		{0xF92B, 0x54 },
+		{0xF92C, 0x02 },
+		{0xF92D, 0xfe },
+		{0xF92E, 0xe5 },
+		{0xF92F, 0x79 },
+		{0xF930, 0x54 },
+		{0xF931, 0x02 },
+		{0xF932, 0xb5 },
+		{0xF933, 0x06 },
+		{0xF934, 0x0f },
+		{0xF935, 0x90 },
+		{0xF936, 0x43 },
+		{0xF937, 0x87 },
+		{0xF938, 0xe0 },
+		{0xF939, 0xb5 },
+		{0xF93A, 0x7a },
+		{0xF93B, 0x08 },
+		{0xF93C, 0x90 },
+		{0xF93D, 0x42 },
+		{0xF93E, 0x05 },
+		{0xF93F, 0xe0 },
+		{0xF940, 0x65 },
+		{0xF941, 0x7b },
+		{0xF942, 0x60 },
+		{0xF943, 0x0b },
+		{0xF944, 0x90 },
+		{0xF945, 0x30 },
+		{0xF946, 0x50 },
+		{0xF947, 0xe0 },
+		{0xF948, 0x54 },
+		{0xF949, 0xf9 },
+		{0xF94A, 0x44 },
+		{0xF94B, 0x02 },
+		{0xF94C, 0xf0 },
+		{0xF94D, 0x80 },
+		{0xF94E, 0x09 },
+		{0xF94F, 0x90 },
+		{0xF950, 0x30 },
+		{0xF951, 0x50 },
+		{0xF952, 0xe0 },
+		{0xF953, 0x54 },
+		{0xF954, 0xf9 },
+		{0xF955, 0x44 },
+		{0xF956, 0x04 },
+		{0xF957, 0xf0 },
+		{0xF958, 0x8c },
+		{0xF959, 0x79 },
+		{0xF95A, 0x90 },
+		{0xF95B, 0x43 },
+		{0xF95C, 0x87 },
+		{0xF95D, 0xe0 },
+		{0xF95E, 0xf5 },
+		{0xF95F, 0x7a },
+		{0xF960, 0x90 },
+		{0xF961, 0x42 },
+		{0xF962, 0x05 },
+		{0xF963, 0xe0 },
+		{0xF964, 0xf5 },
+		{0xF965, 0x7b },
+		{0xF966, 0x22 },
+		{0xF967, 0xc3 },
+		{0xF968, 0x90 },
+		{0xF969, 0x0b },
+		{0xF96A, 0x89 },
+		{0xF96B, 0xe0 },
+		{0xF96C, 0x94 },
+		{0xF96D, 0x1e },
+		{0xF96E, 0x90 },
+		{0xF96F, 0x0b },
+		{0xF970, 0x88 },
+		{0xF971, 0xe0 },
+		{0xF972, 0x94 },
+		{0xF973, 0x00 },
+		{0xF974, 0x50 },
+		{0xF975, 0x06 },
+		{0xF976, 0x7e },
+		{0xF977, 0x00 },
+		{0xF978, 0x7f },
+		{0xF979, 0x01 },
+		{0xF97A, 0x80 },
+		{0xF97B, 0x3d },
+		{0xF97C, 0xc3 },
+		{0xF97D, 0x90 },
+		{0xF97E, 0x0b },
+		{0xF97F, 0x89 },
+		{0xF980, 0xe0 },
+		{0xF981, 0x94 },
+		{0xF982, 0x3c },
+		{0xF983, 0x90 },
+		{0xF984, 0x0b },
+		{0xF985, 0x88 },
+		{0xF986, 0xe0 },
+		{0xF987, 0x94 },
+		{0xF988, 0x00 },
+		{0xF989, 0x50 },
+		{0xF98A, 0x06 },
+		{0xF98B, 0x7e },
+		{0xF98C, 0x00 },
+		{0xF98D, 0x7f },
+		{0xF98E, 0x02 },
+		{0xF98F, 0x80 },
+		{0xF990, 0x28 },
+		{0xF991, 0xc3 },
+		{0xF992, 0x90 },
+		{0xF993, 0x0b },
+		{0xF994, 0x89 },
+		{0xF995, 0xe0 },
+		{0xF996, 0x94 },
+		{0xF997, 0xfa },
+		{0xF998, 0x90 },
+		{0xF999, 0x0b },
+		{0xF99A, 0x88 },
+		{0xF99B, 0xe0 },
+		{0xF99C, 0x94 },
+		{0xF99D, 0x00 },
+		{0xF99E, 0x50 },
+		{0xF99F, 0x06 },
+		{0xF9A0, 0x7e },
+		{0xF9A1, 0x00 },
+		{0xF9A2, 0x7f },
+		{0xF9A3, 0x03 },
+		{0xF9A4, 0x80 },
+		{0xF9A5, 0x13 },
+		{0xF9A6, 0xc3 },
+		{0xF9A7, 0x90 },
+		{0xF9A8, 0x0b },
+		{0xF9A9, 0x88 },
+		{0xF9AA, 0xe0 },
+		{0xF9AB, 0x94 },
+		{0xF9AC, 0x80 },
+		{0xF9AD, 0x50 },
+		{0xF9AE, 0x06 },
+		{0xF9AF, 0x7e },
+		{0xF9B0, 0x00 },
+		{0xF9B1, 0x7f },
+		{0xF9B2, 0x04 },
+		{0xF9B3, 0x80 },
+		{0xF9B4, 0x04 },
+		{0xF9B5, 0xae },
+		{0xF9B6, 0x7e },
+		{0xF9B7, 0xaf },
+		{0xF9B8, 0x7f },
+		{0xF9B9, 0x90 },
+		{0xF9BA, 0xa0 },
+		{0xF9BB, 0xf8 },
+		{0xF9BC, 0xee },
+		{0xF9BD, 0xf0 },
+		{0xF9BE, 0xa3 },
+		{0xF9BF, 0xef },
+		{0xF9C0, 0xf0 },
+		{0xF9C1, 0x22 },
+		{0xF9C2, 0x90 },
+		{0xF9C3, 0x33 },
+		{0xF9C4, 0x82 },
+		{0xF9C5, 0xe0 },
+		{0xF9C6, 0xff },
+		{0xF9C7, 0x64 },
+		{0xF9C8, 0x01 },
+		{0xF9C9, 0x70 },
+		{0xF9CA, 0x30 },
+		{0xF9CB, 0xe5 },
+		{0xF9CC, 0x7f },
+		{0xF9CD, 0x64 },
+		{0xF9CE, 0x02 },
+		{0xF9CF, 0x45 },
+		{0xF9D0, 0x7e },
+		{0xF9D1, 0x70 },
+		{0xF9D2, 0x04 },
+		{0xF9D3, 0x7d },
+		{0xF9D4, 0x1e },
+		{0xF9D5, 0x80 },
+		{0xF9D6, 0x1d },
+		{0xF9D7, 0xe5 },
+		{0xF9D8, 0x7f },
+		{0xF9D9, 0x64 },
+		{0xF9DA, 0x03 },
+		{0xF9DB, 0x45 },
+		{0xF9DC, 0x7e },
+		{0xF9DD, 0x70 },
+		{0xF9DE, 0x04 },
+		{0xF9DF, 0x7d },
+		{0xF9E0, 0x3c },
+		{0xF9E1, 0x80 },
+		{0xF9E2, 0x11 },
+		{0xF9E3, 0xe5 },
+		{0xF9E4, 0x7f },
+		{0xF9E5, 0x64 },
+		{0xF9E6, 0x04 },
+		{0xF9E7, 0x45 },
+		{0xF9E8, 0x7e },
+		{0xF9E9, 0x70 },
+		{0xF9EA, 0x04 },
+		{0xF9EB, 0x7d },
+		{0xF9EC, 0xfa },
+		{0xF9ED, 0x80 },
+		{0xF9EE, 0x05 },
+		{0xF9EF, 0x90 },
+		{0xF9F0, 0x33 },
+		{0xF9F1, 0x81 },
+		{0xF9F2, 0xe0 },
+		{0xF9F3, 0xfd },
+		{0xF9F4, 0xae },
+		{0xF9F5, 0x05 },
+		{0xF9F6, 0x90 },
+		{0xF9F7, 0x33 },
+		{0xF9F8, 0x81 },
+		{0xF9F9, 0xed },
+		{0xF9FA, 0xf0 },
+		{0xF9FB, 0xef },
+		{0xF9FC, 0xb4 },
+		{0xF9FD, 0x01 },
+		{0xF9FE, 0x10 },
+		{0xF9FF, 0x90 },
+		{0xFA00, 0x01 },
+		{0xFA01, 0x00 },
+		{0xFA02, 0xe0 },
+		{0xFA03, 0x60 },
+		{0xFA04, 0x0a },
+		{0xFA05, 0x90 },
+		{0xFA06, 0xa1 },
+		{0xFA07, 0x10 },
+		{0xFA08, 0xe0 },
+		{0xFA09, 0xf5 },
+		{0xFA0A, 0x7e },
+		{0xFA0B, 0xa3 },
+		{0xFA0C, 0xe0 },
+		{0xFA0D, 0xf5 },
+		{0xFA0E, 0x7f },
+		{0xFA0F, 0x22 },
+		{0xFA10, 0x12 },
+		{0xFA11, 0x2f },
+		{0xFA12, 0x4d },
+		{0xFA13, 0x90 },
+		{0xFA14, 0x35 },
+		{0xFA15, 0x38 },
+		{0xFA16, 0xe0 },
+		{0xFA17, 0x70 },
+		{0xFA18, 0x05 },
+		{0xFA19, 0x12 },
+		{0xFA1A, 0x00 },
+		{0xFA1B, 0x0e },
+		{0xFA1C, 0x80 },
+		{0xFA1D, 0x03 },
+		{0xFA1E, 0x12 },
+		{0xFA1F, 0x07 },
+		{0xFA20, 0xc9 },
+		{0xFA21, 0x90 },
+		{0xFA22, 0x40 },
+		{0xFA23, 0x06 },
+		{0xFA24, 0xe0 },
+		{0xFA25, 0xf4 },
+		{0xFA26, 0x54 },
+		{0xFA27, 0x02 },
+		{0xFA28, 0xff },
+		{0xFA29, 0xe0 },
+		{0xFA2A, 0x54 },
+		{0xFA2B, 0x01 },
+		{0xFA2C, 0x4f },
+		{0xFA2D, 0x90 },
+		{0xFA2E, 0x31 },
+		{0xFA2F, 0x32 },
+		{0xFA30, 0xf0 },
+		{0xFA31, 0x90 },
+		{0xFA32, 0xfa },
+		{0xFA33, 0x9d },
+		{0xFA34, 0xe0 },
+		{0xFA35, 0x70 },
+		{0xFA36, 0x03 },
+		{0xFA37, 0x12 },
+		{0xFA38, 0x27 },
+		{0xFA39, 0x27 },
+		{0xFA3A, 0x02 },
+		{0xFA3B, 0x05 },
+		{0xFA3C, 0xac },
+		{0xFA3D, 0x22 },
+		{0xFA3E, 0xf0 },
+		{0xFA3F, 0xe5 },
+		{0xFA40, 0x3a },
+		{0xFA41, 0xb4 },
+		{0xFA42, 0x06 },
+		{0xFA43, 0x06 },
+		{0xFA44, 0x63 },
+		{0xFA45, 0x3e },
+		{0xFA46, 0x02 },
+		{0xFA47, 0x12 },
+		{0xFA48, 0x03 },
+		{0xFA49, 0xea },
+		{0xFA4A, 0x02 },
+		{0xFA4B, 0x17 },
+		{0xFA4C, 0x4a },
+		{0xFA4D, 0x22 },
+		{0x35C9, 0xFA },
+		{0x35CA, 0x01 },
+		{0x35CB, 0x67 },
+		{0x35CC, 0x01 },
+		{0x35CD, 0xC2 },
+		{0x35CE, 0x02 },
+		{0x35CF, 0x10 },
+		{0x35D0, 0x02 },
+		{0x35D1, 0x3E },
+		{0x35D3, 0xF6 },
+		{0x35D5, 0x07 },
+		{0x35D7, 0xA3 },
+		{0x35DB, 0x02 },
+		{0x35DD, 0x06 },
+		{0x35DF, 0x27 },
+		{0x35E6, 0x28 },
+		{0x35E7, 0x76 },
+		{0x35E8, 0x2A },
+		{0x35E9, 0x15 },
+		{0x35EA, 0x2D },
+		{0x35EB, 0x07 },
+		{0x35EC, 0x04 },
+		{0x35ED, 0x43 },
+		{0x35EE, 0x05 },
+		{0x35EF, 0xA9 },
+		{0x35F0, 0x17 },
+		{0x35F1, 0x41 },
+		{0x35F2, 0x24 },
+		{0x35F3, 0x88 },
+		{0x35F4, 0x01 },
+		{0x35F5, 0x54 },
+		{0x35F6, 0x01 },
+		{0x35F7, 0x55 },
+		{0x35F8, 0x2E },
+		{0x35F9, 0xF2 },
+		{0x35FA, 0x06 },
+		{0x35FB, 0x02 },
+		{0x35FC, 0x06 },
+		{0x35FD, 0x03 },
+		{0x35FE, 0x06 },
+		{0x35FF, 0x04 },
+		{0x3600, 0x0F },
+		{0x3601, 0x48 },
+		{0x3602, 0x0F },
+		{0x3603, 0x49 },
+		{0x3604, 0x0F },
+		{0x3605, 0x4A },
+		{0x35C2, 0xFF },
+		{0x35C3, 0xFF },
+		{0x35C4, 0xFF },
+		{0x35C5, 0xC0 },
+		{0x35C0, 0x01 },
+
+
+		{0xa098, 0x02 },
+		{0xa099, 0x87 },
+		{0xa09c, 0x00 },
+		{0xa09d, 0xc5 },
+		{0xa4ec, 0x05 },
+		{0xa4ed, 0x05 },
+		{0xa4f0, 0x04 },
+		{0xa4f1, 0x04 },
+		{0xa4f4, 0x04 },
+		{0xa4f5, 0x05 },
+		{0xa4f8, 0x05 },
+		{0xa4f9, 0x07 },
+		{0xa4fc, 0x07 },
+		{0xa4fd, 0x07 },
+		{0xa500, 0x07 },
+		{0xa501, 0x07 },
+		{0xa504, 0x08 },
+		{0xa505, 0x08 },
+		{0xa518, 0x01 },
+		{0xa519, 0x02 },
+		{0xa51c, 0x01 },
+		{0xa51d, 0x00 },
+		{0xa534, 0x00 },
+		{0xa535, 0x04 },
+		{0xa538, 0x04 },
+		{0xa539, 0x03 },
+		{0xa53c, 0x05 },
+		{0xa53d, 0x07 },
+		{0xa540, 0x07 },
+		{0xa541, 0x06 },
+		{0xa544, 0x07 },
+		{0xa545, 0x06 },
+		{0xa548, 0x05 },
+		{0xa549, 0x06 },
+		{0xa54c, 0x06 },
+		{0xa54d, 0x07 },
+		{0xa550, 0x07 },
+		{0xa551, 0x04 },
+		{0xa554, 0x04 },
+		{0xa555, 0x04 },
+		{0xa558, 0x05 },
+		{0xa559, 0x06 },
+		{0xa55c, 0x07 },
+		{0xa55d, 0x07 },
+		{0xa56c, 0x00 },
+		{0xa56d, 0x0a },
+		{0xa570, 0x08 },
+		{0xa571, 0x05 },
+		{0xa574, 0x04 },
+		{0xa575, 0x03 },
+		{0xa578, 0x04 },
+		{0xa579, 0x04 },
+		{0xa58c, 0x1f },
+		{0xa58d, 0x1b },
+		{0xa590, 0x17 },
+		{0xa591, 0x13 },
+		{0xa594, 0x10 },
+		{0xa595, 0x0d },
+		{0xa598, 0x0f },
+		{0xa599, 0x11 },
+		{0xa59c, 0x03 },
+		{0xa59d, 0x03 },
+		{0xa5a0, 0x03 },
+		{0xa5a1, 0x03 },
+		{0xa5a4, 0x03 },
+		{0xa5a5, 0x04 },
+		{0xa5a8, 0x05 },
+		{0xa5a9, 0x00 },
+		{0xa5ac, 0x00 },
+		{0xa5ad, 0x00 },
+		{0xa5b0, 0x00 },
+		{0xa5b1, 0x00 },
+		{0xa5b4, 0x00 },
+		{0xa5b5, 0x00 },
+		{0xa5c4, 0x1f },
+		{0xa5c5, 0x13 },
+		{0xa5c8, 0x14 },
+		{0xa5c9, 0x14 },
+		{0xa5cc, 0x14 },
+		{0xa5cd, 0x13 },
+		{0xa5d0, 0x17 },
+		{0xa5d1, 0x1a },
+		{0xa5f4, 0x05 },
+		{0xa5f5, 0x05 },
+		{0xa5f8, 0x05 },
+		{0xa5f9, 0x06 },
+		{0xa5fc, 0x06 },
+		{0xa5fd, 0x06 },
+		{0xa600, 0x06 },
+		{0xa601, 0x06 },
+		{0xa608, 0x07 },
+		{0xa609, 0x08 },
+		{0xa60c, 0x08 },
+		{0xa60d, 0x07 },
+		{0xa63c, 0x00 },
+		{0xa63d, 0x02 },
+		{0xa640, 0x02 },
+		{0xa641, 0x02 },
+		{0xa644, 0x02 },
+		{0xa645, 0x02 },
+		{0xa648, 0x03 },
+		{0xa649, 0x04 },
+		{0xa64c, 0x0a },
+		{0xa64d, 0x09 },
+		{0xa650, 0x08 },
+		{0xa651, 0x09 },
+		{0xa654, 0x09 },
+		{0xa655, 0x0a },
+		{0xa658, 0x0a },
+		{0xa659, 0x0a },
+		{0xa65c, 0x0a },
+		{0xa65d, 0x09 },
+		{0xa660, 0x09 },
+		{0xa661, 0x09 },
+		{0xa664, 0x09 },
+		{0xa665, 0x08 },
+		{0xa680, 0x01 },
+		{0xa681, 0x02 },
+		{0xa694, 0x1f },
+		{0xa695, 0x10 },
+		{0xa698, 0x0e },
+		{0xa699, 0x0c },
+		{0xa69c, 0x0d },
+		{0xa69d, 0x0d },
+		{0xa6a0, 0x0f },
+		{0xa6a1, 0x11 },
+		{0xa6a4, 0x00 },
+		{0xa6a5, 0x00 },
+		{0xa6a8, 0x00 },
+		{0xa6a9, 0x00 },
+		{0xa6ac, 0x00 },
+		{0xa6ad, 0x00 },
+		{0xa6b0, 0x00 },
+		{0xa6b1, 0x04 },
+		{0xa6b4, 0x04 },
+		{0xa6b5, 0x04 },
+		{0xa6b8, 0x04 },
+		{0xa6b9, 0x04 },
+		{0xa6bc, 0x05 },
+		{0xa6bd, 0x05 },
+		{0xa6c0, 0x1f },
+		{0xa6c1, 0x1f },
+		{0xa6c4, 0x1f },
+		{0xa6c5, 0x1f },
+		{0xa6c8, 0x1f },
+		{0xa6c9, 0x1f },
+		{0xa6cc, 0x1f },
+		{0xa6cd, 0x0b },
+		{0xa6d0, 0x0c },
+		{0xa6d1, 0x0d },
+		{0xa6d4, 0x0d },
+		{0xa6d5, 0x0d },
+		{0xa6d8, 0x11 },
+		{0xa6d9, 0x14 },
+		{0xa6fc, 0x02 },
+		{0xa6fd, 0x03 },
+		{0xa700, 0x03 },
+		{0xa701, 0x03 },
+		{0xa704, 0x03 },
+		{0xa705, 0x04 },
+		{0xa708, 0x05 },
+		{0xa709, 0x02 },
+		{0xa70c, 0x02 },
+		{0xa70d, 0x02 },
+		{0xa710, 0x03 },
+		{0xa711, 0x04 },
+		{0xa714, 0x04 },
+		{0xa715, 0x04 },
+		{0xa744, 0x00 },
+		{0xa745, 0x03 },
+		{0xa748, 0x04 },
+		{0xa749, 0x04 },
+		{0xa74c, 0x05 },
+		{0xa74d, 0x06 },
+		{0xa750, 0x07 },
+		{0xa751, 0x07 },
+		{0xa754, 0x05 },
+		{0xa755, 0x05 },
+		{0xa758, 0x05 },
+		{0xa759, 0x05 },
+		{0xa75c, 0x05 },
+		{0xa75d, 0x06 },
+		{0xa760, 0x07 },
+		{0xa761, 0x07 },
+		{0xa764, 0x06 },
+		{0xa765, 0x05 },
+		{0xa768, 0x05 },
+		{0xa769, 0x05 },
+		{0xa76c, 0x06 },
+		{0xa76d, 0x07 },
+		{0xa77c, 0x00 },
+		{0xa77d, 0x05 },
+		{0xa780, 0x05 },
+		{0xa781, 0x05 },
+		{0xa784, 0x05 },
+		{0xa785, 0x04 },
+		{0xa788, 0x05 },
+		{0xa789, 0x06 },
+		{0xa79c, 0x1f },
+		{0xa79d, 0x15 },
+		{0xa7a0, 0x13 },
+		{0xa7a1, 0x10 },
+		{0xa7a4, 0x0f },
+		{0xa7a5, 0x0d },
+		{0xa7a8, 0x11 },
+		{0xa7a9, 0x14 },
+		{0xa7ac, 0x02 },
+		{0xa7ad, 0x02 },
+		{0xa7b0, 0x02 },
+		{0xa7b1, 0x02 },
+		{0xa7b4, 0x02 },
+		{0xa7b5, 0x03 },
+		{0xa7b8, 0x03 },
+		{0xa7b9, 0x00 },
+		{0xa7bc, 0x00 },
+		{0xa7bd, 0x00 },
+		{0xa7c0, 0x00 },
+		{0xa7c1, 0x00 },
+		{0xa7c4, 0x00 },
+		{0xa7c5, 0x00 },
+		{0xa7d4, 0x1f },
+		{0xa7d5, 0x0d },
+		{0xa7d8, 0x0f },
+		{0xa7d9, 0x10 },
+		{0xa7dc, 0x10 },
+		{0xa7dd, 0x10 },
+		{0xa7e0, 0x13 },
+		{0xa7e1, 0x16 },
+		{0xa7f4, 0x00 },
+		{0xa7f5, 0x03 },
+		{0xa7f8, 0x04 },
+		{0xa7f9, 0x04 },
+		{0xa7fc, 0x04 },
+		{0xa7fd, 0x03 },
+		{0xa800, 0x03 },
+		{0xa801, 0x03 },
+		{0xa804, 0x03 },
+		{0xa805, 0x03 },
+		{0xa808, 0x03 },
+		{0xa809, 0x03 },
+		{0xa80c, 0x03 },
+		{0xa80d, 0x04 },
+		{0xa810, 0x04 },
+		{0xa811, 0x0a },
+		{0xa814, 0x0a },
+		{0xa815, 0x0a },
+		{0xa818, 0x0f },
+		{0xa819, 0x14 },
+		{0xa81c, 0x14 },
+		{0xa81d, 0x14 },
+		{0xa82c, 0x00 },
+		{0xa82d, 0x04 },
+		{0xa830, 0x02 },
+		{0xa831, 0x00 },
+		{0xa834, 0x00 },
+		{0xa835, 0x00 },
+		{0xa838, 0x00 },
+		{0xa839, 0x00 },
+		{0xa840, 0x1f },
+		{0xa841, 0x1f },
+		{0xa848, 0x1f },
+		{0xa849, 0x1f },
+		{0xa84c, 0x1f },
+		{0xa84d, 0x0c },
+		{0xa850, 0x0c },
+		{0xa851, 0x0c },
+		{0xa854, 0x0c },
+		{0xa855, 0x0c },
+		{0xa858, 0x0c },
+		{0xa859, 0x0c },
+		{0xa85c, 0x0c },
+		{0xa85d, 0x0c },
+		{0xa860, 0x0c },
+		{0xa861, 0x0c },
+		{0xa864, 0x0c },
+		{0xa865, 0x0c },
+		{0xa868, 0x0c },
+		{0xa869, 0x0c },
+		{0xa86c, 0x0c },
+		{0xa86d, 0x0c },
+		{0xa870, 0x0c },
+		{0xa871, 0x0c },
+		{0xa874, 0x0c },
+		{0xa875, 0x0c },
+		{0xa878, 0x1f },
+		{0xa879, 0x1f },
+		{0xa87c, 0x1f },
+		{0xa87d, 0x1f },
+		{0xa880, 0x1f },
+		{0xa881, 0x1f },
+		{0xa884, 0x1f },
+		{0xa885, 0x0c },
+		{0xa888, 0x0c },
+		{0xa889, 0x0c },
+		{0xa88c, 0x0c },
+		{0xa88d, 0x0c },
+		{0xa890, 0x0c },
+		{0xa891, 0x0c },
+		{0xa898, 0x1f },
+		{0xa899, 0x1f },
+		{0xa8a0, 0x1f },
+		{0xa8a1, 0x1f },
+		{0xa8a4, 0x1f },
+		{0xa8a5, 0x0c },
+		{0xa8a8, 0x0c },
+		{0xa8a9, 0x0c },
+		{0xa8ac, 0x0c },
+		{0xa8ad, 0x0c },
+		{0xa8b0, 0x0c },
+		{0xa8b1, 0x0c },
+		{0xa8b4, 0x0c },
+		{0xa8b5, 0x0c },
+		{0xa8b8, 0x0c },
+		{0xa8b9, 0x0c },
+		{0xa8bc, 0x0c },
+		{0xa8bd, 0x0c },
+		{0xa8c0, 0x0c },
+		{0xa8c1, 0x0c },
+		{0xa8c4, 0x0c },
+		{0xa8c5, 0x0c },
+		{0xa8c8, 0x0c },
+		{0xa8c9, 0x0c },
+		{0xa8cc, 0x0c },
+		{0xa8cd, 0x0c },
+		{0xa8d0, 0x1f },
+		{0xa8d1, 0x1f },
+		{0xa8d4, 0x1f },
+		{0xa8d5, 0x1f },
+		{0xa8d8, 0x1f },
+		{0xa8d9, 0x1f },
+		{0xa8dc, 0x1f },
+		{0xa8dd, 0x0c },
+		{0xa8e0, 0x0c },
+		{0xa8e1, 0x0c },
+		{0xa8e4, 0x0c },
+		{0xa8e5, 0x0c },
+		{0xa8e8, 0x0c },
+		{0xa8e9, 0x0c },
+		{0xa8f0, 0x1f },
+		{0xa8f1, 0x1f },
+		{0xa8f8, 0x1f },
+		{0xa8f9, 0x1f },
+		{0xa8fc, 0x1f },
+		{0xa8fd, 0x0c },
+		{0xa900, 0x0c },
+		{0xa901, 0x0c },
+		{0xa904, 0x0c },
+		{0xa905, 0x0c },
+		{0xa908, 0x0c },
+		{0xa909, 0x0c },
+		{0xa90c, 0x0c },
+		{0xa90d, 0x0c },
+		{0xa910, 0x0c },
+		{0xa911, 0x0c },
+		{0xa914, 0x0c },
+		{0xa915, 0x0c },
+		{0xa918, 0x0c },
+		{0xa919, 0x0c },
+		{0xa91c, 0x0c },
+		{0xa91d, 0x0c },
+		{0xa920, 0x0c },
+		{0xa921, 0x0c },
+		{0xa924, 0x0c },
+		{0xa925, 0x0c },
+		{0xa928, 0x1f },
+		{0xa929, 0x1f },
+		{0xa92c, 0x1f },
+		{0xa92d, 0x1f },
+		{0xa930, 0x1f },
+		{0xa931, 0x1f },
+		{0xa934, 0x1f },
+		{0xa935, 0x0c },
+		{0xa938, 0x0c },
+		{0xa939, 0x0c },
+		{0xa93c, 0x0c },
+		{0xa93d, 0x0c },
+		{0xa940, 0x0c },
+		{0xa941, 0x0c },
+		{0xa96c, 0x0d },
+		{0xa96d, 0x16 },
+		{0xa970, 0x19 },
+		{0xa971, 0x0e },
+		{0xa974, 0x16 },
+		{0xa975, 0x1a },
+		{0xa978, 0x0d },
+		{0xa979, 0x15 },
+		{0xa97c, 0x19 },
+		{0xa97d, 0x0d },
+		{0xa980, 0x15 },
+		{0xa981, 0x1a },
+		{0xa984, 0x0d },
+		{0xa985, 0x15 },
+		{0xa988, 0x1a },
+		{0xa989, 0x0d },
+		{0xa98c, 0x15 },
+		{0xa98d, 0x1a },
+		{0xa990, 0x0b },
+		{0xa991, 0x11 },
+		{0xa994, 0x02 },
+		{0xa995, 0x0e },
+		{0xa998, 0x16 },
+		{0xa999, 0x02 },
+		{0xa99c, 0x0c },
+		{0xa99d, 0x13 },
+		{0xa9a0, 0x02 },
+		{0xa9a1, 0x0c },
+		{0xa9a4, 0x12 },
+		{0xa9a5, 0x02 },
+		{0xa9a8, 0x0c },
+		{0xa9a9, 0x12 },
+		{0xa9ac, 0x02 },
+		{0xa9ad, 0x0c },
+		{0xa9b0, 0x12 },
+		{0xa9b1, 0x02 },
+		{0xa9b4, 0x10 },
+		{0xa9b5, 0x1e },
+		{0xa9b8, 0x0f },
+		{0xa9b9, 0x13 },
+		{0xa9bc, 0x20 },
+		{0xa9bd, 0x10 },
+		{0xa9c0, 0x11 },
+		{0xa9c1, 0x1e },
+		{0xa9c4, 0x10 },
+		{0xa9c5, 0x11 },
+		{0xa9c8, 0x1e },
+		{0xa9c9, 0x10 },
+		{0xa9cc, 0x11 },
+		{0xa9cd, 0x20 },
+		{0xa9d0, 0x10 },
+		{0xa9d1, 0x13 },
+		{0xa9d4, 0x24 },
+		{0xa9d5, 0x10 },
+		{0xa9f0, 0x02 },
+		{0xa9f1, 0x01 },
+		{0xa9f8, 0x19 },
+		{0xa9f9, 0x0b },
+		{0xa9fc, 0x0a },
+		{0xa9fd, 0x07 },
+		{0xaa00, 0x0c },
+		{0xaa01, 0x0e },
+		{0xaa08, 0x0c },
+		{0xaa09, 0x06 },
+		{0xaa0c, 0x0c },
+		{0xaa0d, 0x0a },
+		{0xaa24, 0x10 },
+		{0xaa25, 0x12 },
+		{0xaa28, 0x0b },
+		{0xaa29, 0x07 },
+		{0xaa2c, 0x10 },
+		{0xaa2d, 0x14 },
+		{0xaa34, 0x0e },
+		{0xaa35, 0x0e },
+		{0xaa38, 0x07 },
+		{0xaa39, 0x07 },
+		{0xaa3c, 0x0e },
+		{0xaa3d, 0x0c },
+		{0xaa48, 0x09 },
+		{0xaa49, 0x0c },
+		{0xaa4c, 0x0c },
+		{0xaa4d, 0x07 },
+		{0xaa54, 0x08 },
+		{0xaa55, 0x06 },
+		{0xaa58, 0x04 },
+		{0xaa59, 0x05 },
+		{0xaa5c, 0x06 },
+		{0xaa5d, 0x06 },
+		{0xaa68, 0x05 },
+		{0xaa69, 0x05 },
+		{0xaa6c, 0x04 },
+		{0xaa6d, 0x05 },
+		{0xaa74, 0x06 },
+		{0xaa75, 0x04 },
+		{0xaa78, 0x05 },
+		{0xaa79, 0x05 },
+		{0xaa7c, 0x04 },
+		{0xaa7d, 0x06 },
+		{0xac18, 0x14 },
+		{0xac19, 0x00 },
+		{0xac1c, 0x14 },
+		{0xac1d, 0x00 },
+		{0xac20, 0x14 },
+		{0xac21, 0x00 },
+		{0xac24, 0x14 },
+		{0xac25, 0x00 },
+		{0xac28, 0x14 },
+		{0xac29, 0x00 },
+		{0xac2c, 0x14 },
+		{0xac2d, 0x00 },
+		{0xac34, 0x16 },
+		{0xac35, 0x00 },
+		{0xac38, 0x16 },
+		{0xac39, 0x00 },
+		{0xac3c, 0x16 },
+		{0xac3d, 0x00 },
+		{0xac40, 0x16 },
+		{0xac41, 0x00 },
+		{0xac44, 0x16 },
+		{0xac45, 0x00 },
+		{0xac48, 0x16 },
+		{0xac49, 0x00 },
+		{0xac50, 0x1b },
+		{0xac51, 0x00 },
+		{0xac54, 0x1b },
+		{0xac55, 0x00 },
+		{0xac58, 0x1b },
+		{0xac59, 0x00 },
+		{0xac5c, 0x1b },
+		{0xac5d, 0x00 },
+		{0xac60, 0x1b },
+		{0xac61, 0x00 },
+		{0xac64, 0x1b },
+		{0xac65, 0x00 },
+		{0xac74, 0x09 },
+		{0xac75, 0x0c },
+		{0xac78, 0x0f },
+		{0xac79, 0x11 },
+		{0xac7c, 0x12 },
+		{0xac7d, 0x14 },
+		{0xac80, 0x09 },
+		{0xac81, 0x0c },
+		{0xac84, 0x0f },
+		{0xac85, 0x11 },
+		{0xac88, 0x12 },
+		{0xac89, 0x14 },
+		{0xac8c, 0x09 },
+		{0xac8d, 0x0c },
+		{0xac90, 0x0f },
+		{0xac91, 0x11 },
+		{0xac94, 0x12 },
+		{0xac95, 0x14 },
+		{0xac98, 0x09 },
+		{0xac99, 0x0c },
+		{0xac9c, 0x0f },
+		{0xac9d, 0x11 },
+		{0xaca0, 0x12 },
+		{0xaca1, 0x14 },
+		{0xaca4, 0x09 },
+		{0xaca5, 0x0c },
+		{0xaca8, 0x0f },
+		{0xaca9, 0x11 },
+		{0xacac, 0x12 },
+		{0xacad, 0x14 },
+		{0xacb0, 0x07 },
+		{0xacb1, 0x09 },
+		{0xacb4, 0x0c },
+		{0xacb5, 0x0d },
+		{0xacb8, 0x0d },
+		{0xacb9, 0x0e },
+		{0xacbc, 0x05 },
+		{0xacbd, 0x07 },
+		{0xacc0, 0x0a },
+		{0xacc1, 0x0b },
+		{0xacc4, 0x0b },
+		{0xacc5, 0x0c },
+		{0xacc8, 0x03 },
+		{0xacc9, 0x04 },
+		{0xaccc, 0x07 },
+		{0xaccd, 0x08 },
+		{0xacd0, 0x09 },
+		{0xacd1, 0x09 },
+		{0x35B5, 0x01 },
+		{0x35BC, 0x01 },
+		{0x360A, 0x02 },
+		{0xFA9B, 0x01 },
+};
+
+#define NUM_LSC_CAST_REGS      33
+
+enum LSC_Cast_t{
+	cast_H = 0,
+	cast_U30,
+	cast_CW,
+	cast_D,
+	cast_MAX
+};
+
+static short int LSC_CorrectionForCast[cast_MAX][NUM_LSC_CAST_REGS] = {
+	{-30, -20,  8, 11, -16, -26, -35, -53, -9, -10, 44, 57, -39,
+		-14, 50, -173, -38, -32, -1, 9, 39, 51, -33, -49, -28,
+		-22, 7, 11, -21, 17, -62, -56, 0},
+	{-29, -18,  6,  1,  17, -35, -77, 0, 5, -17, -6, -22, -41, -1,
+		-37, 83, -38, -32, 1, -2, 15, 25, -67, 19, -28, -22, 5,
+		2, -18, 21, -86, 0, 0},
+	{-10, -15, -4, -6,  -8,  -3, -63, 8, 25, -9, -39, -51, -9,
+		0, -21, 112, -10, -23, -7, -9, 10, 18, -11, 23, -10,
+		-15, -4, -6, -10, -3, -52, 7, 0},
+	{  5,   3, -4, -5,  -1,   3,   4, 8, 12, 3, -22, -21, 7, 17,
+		2, 35, 8, 2, -3, -2, -9, -5, 10, 4, 9, 2, -4, -5,
+		-2, 0, -6, 9, 0}
+};
+
+static unsigned short LSC_CastRegs[] = {
+	0xFB7E,			/* H   */
+	0xFB3C,			/* U30 */
+	0xFAFA,			/* CW  */
+	0xFAB8			/* D65 */
+};
+
+/*=============================================================*/
+
+static int vx6953_i2c_rxdata(unsigned short saddr,
+	unsigned char *rxdata, int length)
+{
+	struct i2c_msg msgs[] = {
+		{
+			.addr  = saddr,
+			.flags = 0,
+			.len   = 2,
+			.buf   = rxdata,
+		},
+		{
+			.addr  = saddr,
+			.flags = I2C_M_RD,
+			.len   = 2,
+			.buf   = rxdata,
+		},
+	};
+	if (i2c_transfer(vx6953_client->adapter, msgs, 2) < 0) {
+		CDBG("vx6953_i2c_rxdata failed!\n");
+		return -EIO;
+	}
+	return 0;
+}
+static int32_t vx6953_i2c_txdata(unsigned short saddr,
+				unsigned char *txdata, int length)
+{
+	struct i2c_msg msg[] = {
+		{
+			.addr = saddr,
+			.flags = 0,
+			.len = length,
+			.buf = txdata,
+		 },
+	};
+	if (i2c_transfer(vx6953_client->adapter, msg, 1) < 0) {
+		CDBG("vx6953_i2c_txdata faild 0x%x\n", vx6953_client->addr);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+
+static int32_t vx6953_i2c_read(unsigned short raddr,
+	unsigned short *rdata, int rlen)
+{
+	int32_t rc = 0;
+	unsigned char buf[2];
+	if (!rdata)
+		return -EIO;
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (raddr & 0xFF00) >> 8;
+	buf[1] = (raddr & 0x00FF);
+	rc = vx6953_i2c_rxdata(vx6953_client->addr>>1, buf, rlen);
+	if (rc < 0) {
+		CDBG("vx6953_i2c_read 0x%x failed!\n", raddr);
+		return rc;
+	}
+	*rdata = (rlen == 2 ? buf[0] << 8 | buf[1] : buf[0]);
+	return rc;
+}
+static int32_t vx6953_i2c_write_b_sensor(unsigned short waddr, uint8_t bdata)
+{
+	int32_t rc = -EFAULT;
+	unsigned char buf[3];
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (waddr & 0xFF00) >> 8;
+	buf[1] = (waddr & 0x00FF);
+	buf[2] = bdata;
+	CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, bdata);
+	rc = vx6953_i2c_txdata(vx6953_client->addr>>1, buf, 3);
+	if (rc < 0) {
+		CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+			waddr, bdata);
+	}
+	return rc;
+}
+static int32_t vx6953_i2c_write_w_sensor(unsigned short waddr, uint16_t wdata)
+{
+	int32_t rc = -EFAULT;
+	unsigned char buf[4];
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (waddr & 0xFF00) >> 8;
+	buf[1] = (waddr & 0x00FF);
+	buf[2] = (wdata & 0xFF00) >> 8;
+	buf[3] = (wdata & 0x00FF);
+	CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, wdata);
+	rc = vx6953_i2c_txdata(vx6953_client->addr>>1, buf, 4);
+	if (rc < 0) {
+		CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+			waddr, wdata);
+	}
+	return rc;
+}
+static int32_t vx6953_i2c_write_seq_sensor(unsigned short waddr,
+	uint8_t *bdata, uint16_t len)
+{
+	int32_t rc = -EFAULT;
+	unsigned char buf[len+2];
+	int i;
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (waddr & 0xFF00) >> 8;
+	buf[1] = (waddr & 0x00FF);
+	for (i = 2; i < len+2; i++)
+		buf[i] = *bdata++;
+	rc = vx6953_i2c_txdata(vx6953_client->addr>>1, buf, len+2);
+	if (rc < 0) {
+		CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+			 waddr, bdata[0]);
+	}
+	return rc;
+}
+
+static int32_t vx6953_i2c_write_w_table(struct vx6953_i2c_reg_conf const
+					 *reg_conf_tbl, int num)
+{
+	int i;
+	int32_t rc = -EIO;
+	for (i = 0; i < num; i++) {
+		rc = vx6953_i2c_write_b_sensor(reg_conf_tbl->waddr,
+			reg_conf_tbl->wdata);
+		if (rc < 0)
+			break;
+		reg_conf_tbl++;
+	}
+	return rc;
+}
+
+static void vx6953_get_pict_fps(uint16_t fps, uint16_t *pfps)
+{
+	/* input fps is preview fps in Q8 format */
+	uint16_t preview_frame_length_lines, snapshot_frame_length_lines;
+	uint16_t preview_line_length_pck, snapshot_line_length_pck;
+	uint32_t divider, d1, d2;
+	/* Total frame_length_lines and line_length_pck for preview */
+	preview_frame_length_lines = VX6953_QTR_SIZE_HEIGHT +
+		VX6953_VER_QTR_BLK_LINES;
+	preview_line_length_pck = VX6953_QTR_SIZE_WIDTH +
+		VX6953_HRZ_QTR_BLK_PIXELS;
+	/* Total frame_length_lines and line_length_pck for snapshot */
+	snapshot_frame_length_lines = VX6953_FULL_SIZE_HEIGHT +
+		VX6953_VER_FULL_BLK_LINES;
+	snapshot_line_length_pck = VX6953_FULL_SIZE_WIDTH +
+		VX6953_HRZ_FULL_BLK_PIXELS;
+	d1 = preview_frame_length_lines * 0x00000400/
+		snapshot_frame_length_lines;
+	d2 = preview_line_length_pck * 0x00000400/
+		snapshot_line_length_pck;
+	divider = d1 * d2 / 0x400;
+	/*Verify PCLK settings and frame sizes.*/
+	*pfps = (uint16_t) (fps * divider / 0x400);
+	/* 2 is the ratio of no.of snapshot channels
+	to number of preview channels */
+
+}
+
+static uint16_t vx6953_get_prev_lines_pf(void)
+{
+	if (vx6953_ctrl->prev_res == QTR_SIZE)
+		return VX6953_QTR_SIZE_HEIGHT + VX6953_VER_QTR_BLK_LINES;
+	else
+		return VX6953_FULL_SIZE_HEIGHT + VX6953_VER_FULL_BLK_LINES;
+
+}
+
+static uint16_t vx6953_get_prev_pixels_pl(void)
+{
+	if (vx6953_ctrl->prev_res == QTR_SIZE)
+		return VX6953_QTR_SIZE_WIDTH + VX6953_HRZ_QTR_BLK_PIXELS;
+	else
+		return VX6953_FULL_SIZE_WIDTH + VX6953_HRZ_FULL_BLK_PIXELS;
+}
+
+static uint16_t vx6953_get_pict_lines_pf(void)
+{
+		if (vx6953_ctrl->pict_res == QTR_SIZE)
+			return VX6953_QTR_SIZE_HEIGHT +
+				VX6953_VER_QTR_BLK_LINES;
+		else
+			return VX6953_FULL_SIZE_HEIGHT +
+				VX6953_VER_FULL_BLK_LINES;
+}
+
+static uint16_t vx6953_get_pict_pixels_pl(void)
+{
+	if (vx6953_ctrl->pict_res == QTR_SIZE)
+		return VX6953_QTR_SIZE_WIDTH +
+			VX6953_HRZ_QTR_BLK_PIXELS;
+	else
+		return VX6953_FULL_SIZE_WIDTH +
+			VX6953_HRZ_FULL_BLK_PIXELS;
+}
+
+static uint32_t vx6953_get_pict_max_exp_lc(void)
+{
+	if (vx6953_ctrl->pict_res == QTR_SIZE)
+		return (VX6953_QTR_SIZE_HEIGHT +
+			VX6953_VER_QTR_BLK_LINES)*24;
+	else
+		return (VX6953_FULL_SIZE_HEIGHT +
+			VX6953_VER_FULL_BLK_LINES)*24;
+}
+
+static int32_t vx6953_set_fps(struct fps_cfg	*fps)
+{
+	uint16_t total_lines_per_frame;
+	int32_t rc = 0;
+	total_lines_per_frame = (uint16_t)((VX6953_QTR_SIZE_HEIGHT +
+		VX6953_VER_QTR_BLK_LINES) * vx6953_ctrl->fps_divider/0x400);
+
+	vx6953_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+		GROUPED_PARAMETER_HOLD);
+	if (vx6953_i2c_write_b_sensor(REG_FRAME_LENGTH_LINES_HI,
+		((total_lines_per_frame & 0xFF00) >> 8)) < 0)
+		return rc;
+	if (vx6953_i2c_write_b_sensor(REG_FRAME_LENGTH_LINES_LO,
+		(total_lines_per_frame & 0x00FF)) < 0)
+		return rc;
+	vx6953_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+		GROUPED_PARAMETER_HOLD_OFF);
+	return rc;
+}
+
+static int32_t vx6953_write_exp_gain(uint16_t gain, uint32_t line)
+{
+	uint16_t line_length_pck, frame_length_lines;
+	uint8_t gain_hi, gain_lo;
+	uint8_t intg_time_hi, intg_time_lo;
+	uint8_t frame_length_lines_hi = 0, frame_length_lines_lo = 0;
+	int32_t rc = 0;
+	if (vx6953_ctrl->sensormode != SENSOR_SNAPSHOT_MODE) {
+		frame_length_lines = VX6953_QTR_SIZE_HEIGHT +
+		VX6953_VER_QTR_BLK_LINES;
+		line_length_pck = VX6953_QTR_SIZE_WIDTH +
+			VX6953_HRZ_QTR_BLK_PIXELS;
+		if (line > (frame_length_lines -
+			VX6953_STM5M0EDOF_OFFSET)) {
+			vx6953_ctrl->fps = (uint16_t) (30 * Q8 *
+			(frame_length_lines - VX6953_STM5M0EDOF_OFFSET)/
+			line);
+		} else {
+			vx6953_ctrl->fps = (uint16_t) (30 * Q8);
+		}
+	} else {
+		frame_length_lines = VX6953_FULL_SIZE_HEIGHT +
+				VX6953_VER_FULL_BLK_LINES;
+		line_length_pck = VX6953_FULL_SIZE_WIDTH +
+				VX6953_HRZ_FULL_BLK_PIXELS;
+	}
+
+	vx6953_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+		GROUPED_PARAMETER_HOLD);
+	if ((line + VX6953_STM5M0EDOF_OFFSET) > MAX_FRAME_LENGTH_LINES) {
+		frame_length_lines = MAX_FRAME_LENGTH_LINES;
+		line = MAX_FRAME_LENGTH_LINES - VX6953_STM5M0EDOF_OFFSET;
+	} else if ((line + VX6953_STM5M0EDOF_OFFSET) > frame_length_lines) {
+		frame_length_lines = line + VX6953_STM5M0EDOF_OFFSET;
+		line = frame_length_lines;
+	}
+
+	frame_length_lines_hi = (uint8_t) ((frame_length_lines &
+		0xFF00) >> 8);
+	frame_length_lines_lo = (uint8_t) (frame_length_lines &
+		0x00FF);
+	vx6953_i2c_write_b_sensor(REG_FRAME_LENGTH_LINES_HI,
+		frame_length_lines_hi);
+	vx6953_i2c_write_b_sensor(REG_FRAME_LENGTH_LINES_LO,
+		frame_length_lines_lo);
+
+	/* update analogue gain registers */
+	gain_hi = (uint8_t) ((gain & 0xFF00) >> 8);
+	gain_lo = (uint8_t) (gain & 0x00FF);
+	vx6953_i2c_write_b_sensor(REG_ANALOGUE_GAIN_CODE_GLOBAL_LO,
+		gain_lo);
+	vx6953_i2c_write_b_sensor(REG_DIGITAL_GAIN_GREEN_R_LO, gain_hi);
+	vx6953_i2c_write_b_sensor(REG_DIGITAL_GAIN_RED_LO, gain_hi);
+	vx6953_i2c_write_b_sensor(REG_DIGITAL_GAIN_BLUE_LO, gain_hi);
+	vx6953_i2c_write_b_sensor(REG_DIGITAL_GAIN_GREEN_B_LO, gain_hi);
+	CDBG("%s, gain_hi 0x%x, gain_lo 0x%x\n", __func__,
+		gain_hi, gain_lo);
+	/* update line count registers */
+	intg_time_hi = (uint8_t) (((uint16_t)line & 0xFF00) >> 8);
+	intg_time_lo = (uint8_t) ((uint16_t)line & 0x00FF);
+	vx6953_i2c_write_b_sensor(REG_COARSE_INTEGRATION_TIME_HI,
+		intg_time_hi);
+	vx6953_i2c_write_b_sensor(REG_COARSE_INTEGRATION_TIME_LO,
+		intg_time_lo);
+	vx6953_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+		GROUPED_PARAMETER_HOLD_OFF);
+
+	return rc;
+}
+
+static int32_t vx6953_set_pict_exp_gain(uint16_t gain, uint32_t line)
+{
+	int32_t rc = 0;
+	rc = vx6953_write_exp_gain(gain, line);
+	return rc;
+} /* endof vx6953_set_pict_exp_gain*/
+
+static int32_t vx6953_move_focus(int direction,
+	int32_t num_steps)
+{
+	return 0;
+}
+
+
+static int32_t vx6953_set_default_focus(uint8_t af_step)
+{
+	return 0;
+}
+
+static int32_t vx6953_test(enum vx6953_test_mode_t mo)
+{
+	int32_t rc = 0;
+	if (mo == TEST_OFF)
+		return rc;
+	else {
+		/* REG_0x30D8[4] is TESBYPEN: 0: Normal Operation,
+		1: Bypass Signal Processing
+		REG_0x30D8[5] is EBDMASK: 0:
+		Output Embedded data, 1: No output embedded data */
+		if (vx6953_i2c_write_b_sensor(REG_TEST_PATTERN_MODE,
+			(uint8_t) mo) < 0) {
+			return rc;
+		}
+	}
+	return rc;
+}
+
+static int vx6953_enable_edof(enum edof_mode_t edof_mode)
+{
+	int rc = 0;
+	if (edof_mode == VX6953_EDOF_ESTIMATION) {
+		/* EDof Estimation mode for preview */
+		if (vx6953_i2c_write_b_sensor(REG_0x0b80, 0x02) < 0)
+			return rc;
+		CDBG("VX6953_EDOF_ESTIMATION");
+	} else if (edof_mode == VX6953_EDOF_APPLICATION) {
+		/* EDof Application mode for Capture */
+		if (vx6953_i2c_write_b_sensor(REG_0x0b80, 0x01) < 0)
+			return rc;
+		CDBG("VX6953_EDOF_APPLICATION");
+	} else {
+		/* EDOF disabled */
+		if (vx6953_i2c_write_b_sensor(REG_0x0b80, 0x00) < 0)
+			return rc;
+		CDBG("VX6953_EDOF_DISABLE");
+	}
+	return rc;
+}
+
+static int32_t vx6953_patch_for_cut2(void)
+{
+	int32_t rc = 0;
+	rc = vx6953_i2c_write_w_table(patch_tbl_cut2,
+		ARRAY_SIZE(patch_tbl_cut2));
+	if (rc < 0)
+		return rc;
+
+	return rc;
+}
+
+static int32_t vx6953_lsc_patch(void)
+{
+	int32_t rc = 0;
+	int i, j;
+	short int  v;
+	unsigned short version = 0;
+	unsigned short LSC_Raw[NUM_LSC_CAST_REGS];
+	unsigned short LSC_Fixed[NUM_LSC_CAST_REGS];
+
+	vx6953_i2c_read(0x10, &version, 1);
+	CDBG("Cut 3 Version %d\n", version);
+	if (version != 1)
+		return 0;
+
+	vx6953_i2c_write_b_sensor(0x3640, 0x00);
+	for (j = cast_H; j < cast_MAX; j++) {
+		for (i = 0; i < NUM_LSC_CAST_REGS; i++) {
+			rc = vx6953_i2c_read(LSC_CastRegs[cast_D]+(2*i),
+								&LSC_Raw[i], 2);
+			if (rc < 0)
+				return rc;
+			v = LSC_Raw[i];
+			v +=  LSC_CorrectionForCast[j][i];
+			LSC_Fixed[i] = (unsigned short) v;
+		}
+		for (i = 0; i < NUM_LSC_CAST_REGS; i++) {
+			rc = vx6953_i2c_write_w_sensor(LSC_CastRegs[j]+(2*i),
+								LSC_Fixed[i]);
+			if (rc < 0)
+				return rc;
+		}
+	}
+	CDBG("vx6953_lsc_patch done\n");
+	return rc;
+}
+
+static int32_t vx6953_sensor_setting(int update_type, int rt)
+{
+
+	int32_t rc = 0;
+	unsigned short frame_cnt;
+	struct msm_camera_csi_params vx6953_csi_params;
+	if (vx6953_ctrl->sensor_type != VX6953_STM5M0EDOF_CUT_2) {
+		switch (update_type) {
+		case REG_INIT:
+		if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+			struct vx6953_i2c_reg_conf init_tbl[] = {
+				{REG_0x0112,
+					vx6953_regs.reg_pat_init[0].
+					reg_0x0112},
+				{REG_0x0113,
+					vx6953_regs.reg_pat_init[0].
+					reg_0x0113},
+				{REG_VT_PIX_CLK_DIV,
+					vx6953_regs.reg_pat_init[0].
+					vt_pix_clk_div},
+				{0x303, 0x01},
+				{0x30b, 0x01},
+				{REG_PRE_PLL_CLK_DIV,
+					vx6953_regs.reg_pat_init[0].
+					pre_pll_clk_div},
+				{REG_PLL_MULTIPLIER,
+					vx6953_regs.reg_pat_init[0].
+					pll_multiplier},
+				{REG_OP_PIX_CLK_DIV,
+					vx6953_regs.reg_pat_init[0].
+					op_pix_clk_div},
+				{REG_0x3210, 0x01},
+				{REG_0x0111,
+					vx6953_regs.reg_pat_init[0].
+					reg_0x0111},
+				{REG_0x0b00,
+					vx6953_regs.reg_pat_init[0].
+					reg_0x0b00},
+				{REG_0x0136,
+					vx6953_regs.reg_pat_init[0].
+					reg_0x0136},
+				{REG_0x0137,
+					vx6953_regs.reg_pat_init[0].
+					reg_0x0137},
+				{REG_0x0b06,
+					vx6953_regs.reg_pat_init[0].
+					reg_0x0b06},
+				{REG_0x0b07,
+					vx6953_regs.reg_pat_init[0].
+					reg_0x0b07},
+				{REG_0x0b08,
+					vx6953_regs.reg_pat_init[0].
+					reg_0x0b08},
+				{REG_0x0b09,
+					vx6953_regs.reg_pat_init[0].
+					reg_0x0b09},
+				{REG_0x0b83,
+					vx6953_regs.reg_pat_init[0].
+					reg_0x0b83},
+				{REG_0x0b84,
+					vx6953_regs.reg_pat_init[0].
+					reg_0x0b84},
+				{REG_0x0b85,
+					vx6953_regs.reg_pat_init[0].
+					reg_0x0b85},
+				{REG_0x0b88,
+					vx6953_regs.reg_pat_init[0].
+					reg_0x0b88},
+				{REG_0x0b89,
+					vx6953_regs.reg_pat_init[0].
+					reg_0x0b89},
+				{REG_0x0b8a,
+					vx6953_regs.reg_pat_init[0].
+					reg_0x0b8a},
+				{0x3393, 0x06},
+				{0x3394, 0x07},
+				{0x338d, 0x08},
+				{0x338e, 0x08},
+				{0x338f, 0x00},
+			};
+			/* reset fps_divider */
+			vx6953_ctrl->fps = 30 * Q8;
+			/* stop streaming */
+
+			count = 0;
+			CDBG("Init vx6953_sensor_setting standby\n");
+			if (vx6953_i2c_write_b_sensor(REG_MODE_SELECT,
+				MODE_SELECT_STANDBY_MODE) < 0)
+				return rc;
+			msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+			vx6953_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+			GROUPED_PARAMETER_HOLD);
+
+			rc = vx6953_i2c_write_w_table(cut3_cali_data,
+				ARRAY_SIZE(cut3_cali_data));
+
+			vx6953_lsc_patch();
+
+			vx6953_i2c_write_w_sensor(0x100A, 0x07A3);
+			vx6953_i2c_write_w_sensor(0x114A, 0x002A);
+			vx6953_i2c_write_w_sensor(0x1716, 0x0204);
+			vx6953_i2c_write_w_sensor(0x1718, 0x0880);
+
+			rc = vx6953_i2c_write_w_table(&init_tbl[0],
+				ARRAY_SIZE(init_tbl));
+			if (rc < 0)
+				return rc;
+
+			msleep(10);
+
+		}
+		return rc;
+		case UPDATE_PERIODIC:
+		if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+			struct vx6953_i2c_reg_conf preview_mode_tbl[] = {
+				{0x200, 0x02},
+				{0x201, 0x26},
+				{REG_COARSE_INTEGRATION_TIME_HI,
+					vx6953_regs.reg_pat[rt].
+					coarse_integration_time_hi},
+				{REG_COARSE_INTEGRATION_TIME_LO,
+					vx6953_regs.reg_pat[rt].
+					coarse_integration_time_lo},
+				{REG_ANALOGUE_GAIN_CODE_GLOBAL_LO,
+					vx6953_regs.reg_pat[rt].
+					analogue_gain_code_global},
+				{REG_FRAME_LENGTH_LINES_HI,
+					vx6953_regs.reg_pat[rt].
+					frame_length_lines_hi},
+				{REG_FRAME_LENGTH_LINES_LO,
+					vx6953_regs.reg_pat[rt].
+					frame_length_lines_lo},
+				{REG_LINE_LENGTH_PCK_HI,
+					vx6953_regs.reg_pat[rt].
+					line_length_pck_hi},
+				{REG_LINE_LENGTH_PCK_LO,
+					vx6953_regs.reg_pat[rt].
+					line_length_pck_lo},
+				{REG_0x0b80,
+					vx6953_regs.reg_pat[rt].
+					reg_0x0b80},
+				{REG_0x0900,
+					vx6953_regs.reg_pat[rt].
+					reg_0x0900},
+				{REG_0x0901,
+					vx6953_regs.reg_pat[rt].
+					reg_0x0901},
+				{REG_0x0902,
+					vx6953_regs.reg_pat[rt].
+					reg_0x0902},
+				{REG_0x0383,
+					vx6953_regs.reg_pat[rt].
+					reg_0x0383},
+				{REG_0x0387,
+					vx6953_regs.reg_pat[rt].
+					reg_0x0387},
+				{REG_0x034c,
+					vx6953_regs.reg_pat[rt].
+					reg_0x034c},
+				{REG_0x034d,
+					vx6953_regs.reg_pat[rt].
+					reg_0x034d},
+				{REG_0x034e,
+					vx6953_regs.reg_pat[rt].
+					reg_0x034e},
+				{REG_0x034f,
+					vx6953_regs.reg_pat[rt].
+					reg_0x034f},
+				{REG_0x3640, 0x00},
+			};
+
+			struct vx6953_i2c_reg_conf snapshot_mode_tbl[] = {
+				{0x0200, 0x02},
+				{0x0201, 0x54},
+				{REG_COARSE_INTEGRATION_TIME_HI,
+					vx6953_regs.reg_pat[rt].
+					coarse_integration_time_hi},
+				{REG_COARSE_INTEGRATION_TIME_LO,
+					vx6953_regs.reg_pat[rt].
+					coarse_integration_time_lo},
+				{REG_ANALOGUE_GAIN_CODE_GLOBAL_LO,
+					vx6953_regs.reg_pat[rt].
+					analogue_gain_code_global},
+				{REG_FRAME_LENGTH_LINES_HI,
+					vx6953_regs.reg_pat[rt].
+					frame_length_lines_hi},
+				{REG_FRAME_LENGTH_LINES_LO,
+					vx6953_regs.reg_pat[rt].
+					frame_length_lines_lo},
+				{REG_LINE_LENGTH_PCK_HI,
+					vx6953_regs.reg_pat[rt].
+					line_length_pck_hi},
+				{REG_LINE_LENGTH_PCK_LO,
+					vx6953_regs.reg_pat[rt].
+					line_length_pck_lo},
+				{REG_0x0b80,
+					vx6953_regs.reg_pat[rt].
+					reg_0x0b80},
+				{REG_0x0900,
+					vx6953_regs.reg_pat[rt].
+					reg_0x0900},
+				{REG_0x0901,
+					vx6953_regs.reg_pat[rt].
+					reg_0x0901},
+				{REG_0x0902,
+					vx6953_regs.reg_pat[rt].
+					reg_0x0902},
+				{REG_0x0383,
+					vx6953_regs.reg_pat[rt].
+					reg_0x0383},
+				{REG_0x0387,
+					vx6953_regs.reg_pat[rt].
+					reg_0x0387},
+				{REG_0x034c,
+					vx6953_regs.reg_pat[rt].
+					reg_0x034c},
+				{REG_0x034d,
+					vx6953_regs.reg_pat[rt].
+					reg_0x034d},
+				{REG_0x034e,
+					vx6953_regs.reg_pat[rt].
+					reg_0x034e},
+				{REG_0x034f,
+					vx6953_regs.reg_pat[rt].
+					reg_0x034f},
+				{0x3140, 0x01},
+				{REG_0x3640, 0x00},
+			};
+			/* stop streaming */
+
+			if (vx6953_i2c_write_b_sensor(REG_MODE_SELECT,
+				MODE_SELECT_STANDBY_MODE) < 0)
+				return rc;
+			msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+			if (count == 0) {
+				vx6953_csi_params.data_format = CSI_8BIT;
+				vx6953_csi_params.lane_cnt = 1;
+				vx6953_csi_params.lane_assign = 0xe4;
+				vx6953_csi_params.dpcm_scheme = 0;
+				vx6953_csi_params.settle_cnt = 7;
+				rc = msm_camio_csi_config(&vx6953_csi_params);
+				if (rc < 0)
+					CDBG("config csi controller failed\n");
+
+				msleep(20);
+				count = 1;
+			}
+
+			vx6953_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+			GROUPED_PARAMETER_HOLD);
+
+			if (rt == RES_PREVIEW) {
+				rc = vx6953_i2c_write_w_table(
+					&preview_mode_tbl[0],
+					ARRAY_SIZE(preview_mode_tbl));
+				if (rc < 0)
+					return rc;
+			}
+			if (rt == RES_CAPTURE) {
+				rc = vx6953_i2c_write_w_table(
+					&snapshot_mode_tbl[0],
+					ARRAY_SIZE(snapshot_mode_tbl));
+				if (rc < 0)
+					return rc;
+			}
+
+			vx6953_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+			GROUPED_PARAMETER_HOLD_OFF);
+
+			/* Start sensor streaming */
+			if (vx6953_i2c_write_b_sensor(REG_MODE_SELECT,
+				MODE_SELECT_STREAM) < 0)
+				return rc;
+			msleep(10);
+
+			if (vx6953_i2c_read(0x0005, &frame_cnt, 1) < 0)
+				return rc;
+
+			while (frame_cnt == 0xFF) {
+				if (vx6953_i2c_read(0x0005, &frame_cnt, 1) < 0)
+					return rc;
+				CDBG("frame_cnt=%d\n", frame_cnt);
+				msleep(2);
+			}
+		}
+		return rc;
+		default:
+			return rc;
+		}
+	} else {
+		switch (update_type) {
+		case REG_INIT:
+		if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+			struct vx6953_i2c_reg_conf init_tbl[] = {
+			{REG_0x0112,
+				vx6953_regs.reg_pat_init[0].reg_0x0112},
+			{REG_0x0113,
+				vx6953_regs.reg_pat_init[0].reg_0x0113},
+			{REG_VT_PIX_CLK_DIV,
+				vx6953_regs.reg_pat_init[0].
+				vt_pix_clk_div},
+			{REG_PRE_PLL_CLK_DIV,
+				vx6953_regs.reg_pat_init[0].
+				pre_pll_clk_div},
+			{REG_PLL_MULTIPLIER,
+				vx6953_regs.reg_pat_init[0].
+				pll_multiplier},
+			{REG_OP_PIX_CLK_DIV,
+				vx6953_regs.reg_pat_init[0].
+				op_pix_clk_div},
+			{REG_COARSE_INTEGRATION_TIME_HI,
+				vx6953_regs.reg_pat[rt].
+				coarse_integration_time_hi},
+			{REG_COARSE_INTEGRATION_TIME_LO,
+				vx6953_regs.reg_pat[rt].
+				coarse_integration_time_lo},
+			{REG_ANALOGUE_GAIN_CODE_GLOBAL_LO,
+				vx6953_regs.reg_pat[rt].
+				analogue_gain_code_global},
+			{REG_0x3030,
+				vx6953_regs.reg_pat_init[0].reg_0x3030},
+			/* 953 specific registers */
+			{REG_0x0111,
+				vx6953_regs.reg_pat_init[0].reg_0x0111},
+			{REG_0x0b00,
+				vx6953_regs.reg_pat_init[0].reg_0x0b00},
+			{REG_0x3001,
+				vx6953_regs.reg_pat_init[0].reg_0x3001},
+			{REG_0x3004,
+				vx6953_regs.reg_pat_init[0].reg_0x3004},
+			{REG_0x3007,
+				vx6953_regs.reg_pat_init[0].reg_0x3007},
+			{REG_0x3016,
+				vx6953_regs.reg_pat_init[0].reg_0x3016},
+			{REG_0x301d,
+				vx6953_regs.reg_pat_init[0].reg_0x301d},
+			{REG_0x317e,
+				vx6953_regs.reg_pat_init[0].reg_0x317e},
+			{REG_0x317f,
+				vx6953_regs.reg_pat_init[0].reg_0x317f},
+			{REG_0x3400,
+				vx6953_regs.reg_pat_init[0].reg_0x3400},
+			/* DEFCOR settings */
+			/*Single Defect Correction Weight DISABLE*/
+			{0x0b06,
+				vx6953_regs.reg_pat_init[0].reg_0x0b06},
+			/*Single_defect_correct_weight = auto*/
+			{0x0b07,
+				vx6953_regs.reg_pat_init[0].reg_0x0b07},
+			/*Dynamic couplet correction ENABLED*/
+			{0x0b08,
+				vx6953_regs.reg_pat_init[0].reg_0x0b08},
+			/*Dynamic couplet correction weight*/
+			{0x0b09,
+				vx6953_regs.reg_pat_init[0].reg_0x0b09},
+			/* Clock Setup */
+			/* Tell sensor ext clk is 24MHz*/
+			{0x0136,
+				vx6953_regs.reg_pat_init[0].reg_0x0136},
+			{0x0137,
+				vx6953_regs.reg_pat_init[0].reg_0x0137},
+			/* The white balance gains must be written
+			to the sensor every frame. */
+			/* Edof */
+			{REG_0x0b83,
+				vx6953_regs.reg_pat_init[0].reg_0x0b83},
+			{REG_0x0b84,
+				vx6953_regs.reg_pat_init[0].reg_0x0b84},
+			{0x0b85,
+				vx6953_regs.reg_pat_init[0].reg_0x0b85},
+			{0x0b88,
+				vx6953_regs.reg_pat_init[0].reg_0x0b88},
+			{0x0b89,
+				vx6953_regs.reg_pat_init[0].reg_0x0b89},
+			{REG_0x0b8a,
+				vx6953_regs.reg_pat_init[0].reg_0x0b8a},
+			/* Mode specific regieters */
+			{REG_FRAME_LENGTH_LINES_HI,
+				vx6953_regs.reg_pat[rt].
+				frame_length_lines_hi},
+			{REG_FRAME_LENGTH_LINES_LO,
+				vx6953_regs.reg_pat[rt].
+				frame_length_lines_lo},
+			{REG_LINE_LENGTH_PCK_HI,
+				vx6953_regs.reg_pat[rt].
+				line_length_pck_hi},
+			{REG_LINE_LENGTH_PCK_LO,
+				vx6953_regs.reg_pat[rt].
+				line_length_pck_lo},
+			{REG_0x3005,
+				vx6953_regs.reg_pat[rt].reg_0x3005},
+			{0x3010,
+				vx6953_regs.reg_pat[rt].reg_0x3010},
+			{REG_0x3011,
+				vx6953_regs.reg_pat[rt].reg_0x3011},
+			{REG_0x301a,
+				vx6953_regs.reg_pat[rt].reg_0x301a},
+			{REG_0x3035,
+				vx6953_regs.reg_pat[rt].reg_0x3035},
+			{REG_0x3036,
+				vx6953_regs.reg_pat[rt].reg_0x3036},
+			{REG_0x3041,
+				vx6953_regs.reg_pat[rt].reg_0x3041},
+			{0x3042,
+				vx6953_regs.reg_pat[rt].reg_0x3042},
+			{REG_0x3045,
+				vx6953_regs.reg_pat[rt].reg_0x3045},
+			/*EDOF: Estimation settings for Preview mode
+			Application settings for capture mode
+			(standard settings - Not tuned) */
+			{REG_0x0b80,
+				vx6953_regs.reg_pat[rt].reg_0x0b80},
+			{REG_0x0900,
+				vx6953_regs.reg_pat[rt].reg_0x0900},
+			{REG_0x0901,
+				vx6953_regs.reg_pat[rt].reg_0x0901},
+			{REG_0x0902,
+				vx6953_regs.reg_pat[rt].reg_0x0902},
+			{REG_0x0383,
+				vx6953_regs.reg_pat[rt].reg_0x0383},
+			{REG_0x0387,
+				vx6953_regs.reg_pat[rt].reg_0x0387},
+			/* Change output size / frame rate */
+			{REG_0x034c,
+				vx6953_regs.reg_pat[rt].reg_0x034c},
+			{REG_0x034d,
+				vx6953_regs.reg_pat[rt].reg_0x034d},
+			{REG_0x034e,
+				vx6953_regs.reg_pat[rt].reg_0x034e},
+			{REG_0x034f,
+				vx6953_regs.reg_pat[rt].reg_0x034f},
+			{REG_0x1716,
+				vx6953_regs.reg_pat[rt].reg_0x1716},
+			{REG_0x1717,
+				vx6953_regs.reg_pat[rt].reg_0x1717},
+			{REG_0x1718,
+				vx6953_regs.reg_pat[rt].reg_0x1718},
+			{REG_0x1719,
+				vx6953_regs.reg_pat[rt].reg_0x1719},
+			};
+						/* reset fps_divider */
+			vx6953_ctrl->fps = 30 * Q8;
+			/* stop streaming */
+
+			/* Reset everything first */
+			if (vx6953_i2c_write_b_sensor(0x103, 0x01) < 0) {
+				CDBG("S/W reset failed\n");
+				return rc;
+			} else
+				CDBG("S/W reset successful\n");
+
+			msleep(10);
+
+			CDBG("Init vx6953_sensor_setting standby\n");
+			if (vx6953_i2c_write_b_sensor(REG_MODE_SELECT,
+				MODE_SELECT_STANDBY_MODE) < 0)
+				return rc;
+				/*vx6953_stm5m0edof_delay_msecs_stdby*/
+			msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+			vx6953_patch_for_cut2();
+			rc = vx6953_i2c_write_w_table(&init_tbl[0],
+				ARRAY_SIZE(init_tbl));
+			if (rc < 0)
+				return rc;
+				msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+		}
+	return rc;
+	case UPDATE_PERIODIC:
+		if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+			struct vx6953_i2c_reg_conf init_mode_tbl[] =  {
+			{REG_0x0112,
+				vx6953_regs.reg_pat_init[0].reg_0x0112},
+			{REG_0x0113,
+				vx6953_regs.reg_pat_init[0].reg_0x0113},
+			{REG_VT_PIX_CLK_DIV,
+				vx6953_regs.reg_pat_init[0].
+				vt_pix_clk_div},
+			{REG_PRE_PLL_CLK_DIV,
+				vx6953_regs.reg_pat_init[0].
+				pre_pll_clk_div},
+			{REG_PLL_MULTIPLIER,
+				vx6953_regs.reg_pat_init[0].
+				pll_multiplier},
+			{REG_OP_PIX_CLK_DIV,
+				vx6953_regs.reg_pat_init[0].
+				op_pix_clk_div},
+			{REG_COARSE_INTEGRATION_TIME_HI,
+				vx6953_regs.reg_pat[rt].
+				coarse_integration_time_hi},
+			{REG_COARSE_INTEGRATION_TIME_LO,
+				vx6953_regs.reg_pat[rt].
+				coarse_integration_time_lo},
+			{REG_ANALOGUE_GAIN_CODE_GLOBAL_LO,
+				vx6953_regs.reg_pat[rt].
+				analogue_gain_code_global},
+			{REG_0x3030,
+				vx6953_regs.reg_pat_init[0].reg_0x3030},
+			/* 953 specific registers */
+			{REG_0x0111,
+				vx6953_regs.reg_pat_init[0].reg_0x0111},
+			{REG_0x0b00,
+				vx6953_regs.reg_pat_init[0].reg_0x0b00},
+			{REG_0x3001,
+				vx6953_regs.reg_pat_init[0].reg_0x3001},
+			{REG_0x3004,
+				vx6953_regs.reg_pat_init[0].reg_0x3004},
+			{REG_0x3007,
+				vx6953_regs.reg_pat_init[0].reg_0x3007},
+			{REG_0x3016,
+				vx6953_regs.reg_pat_init[0].reg_0x3016},
+			{REG_0x301d,
+				vx6953_regs.reg_pat_init[0].reg_0x301d},
+			{REG_0x317e,
+				vx6953_regs.reg_pat_init[0].reg_0x317e},
+			{REG_0x317f,
+				vx6953_regs.reg_pat_init[0].reg_0x317f},
+			{REG_0x3400,
+				vx6953_regs.reg_pat_init[0].reg_0x3400},
+			{0x0b06,
+				vx6953_regs.reg_pat_init[0].reg_0x0b06},
+			/*Single_defect_correct_weight = auto*/
+			{0x0b07,
+				vx6953_regs.reg_pat_init[0].reg_0x0b07},
+			/*Dynamic couplet correction ENABLED*/
+			{0x0b08,
+				vx6953_regs.reg_pat_init[0].reg_0x0b08},
+			/*Dynamic couplet correction weight*/
+			{0x0b09,
+				vx6953_regs.reg_pat_init[0].reg_0x0b09},
+			/* Clock Setup */
+			/* Tell sensor ext clk is 24MHz*/
+			{0x0136,
+				vx6953_regs.reg_pat_init[0].reg_0x0136},
+			{0x0137,
+				vx6953_regs.reg_pat_init[0].reg_0x0137},
+			/* The white balance gains must be written
+			to the sensor every frame. */
+			/* Edof */
+			{REG_0x0b83,
+				vx6953_regs.reg_pat_init[0].reg_0x0b83},
+			{REG_0x0b84,
+				vx6953_regs.reg_pat_init[0].reg_0x0b84},
+			{0x0b85,
+				vx6953_regs.reg_pat_init[0].reg_0x0b85},
+			{0x0b88,
+				vx6953_regs.reg_pat_init[0].reg_0x0b88},
+			{0x0b89,
+				vx6953_regs.reg_pat_init[0].reg_0x0b89},
+			{REG_0x0b8a,
+				vx6953_regs.reg_pat_init[0].reg_0x0b8a},
+			/* Mode specific regieters */
+			{REG_FRAME_LENGTH_LINES_HI,
+				vx6953_regs.reg_pat[rt].
+				frame_length_lines_hi},
+			{REG_FRAME_LENGTH_LINES_LO,
+				vx6953_regs.reg_pat[rt].
+				frame_length_lines_lo},
+			{REG_LINE_LENGTH_PCK_HI,
+				vx6953_regs.reg_pat[rt].
+				line_length_pck_hi},
+			{REG_LINE_LENGTH_PCK_LO,
+				vx6953_regs.reg_pat[rt].
+				line_length_pck_lo},
+			{REG_0x3005,
+				vx6953_regs.reg_pat[rt].reg_0x3005},
+			{0x3010,
+				vx6953_regs.reg_pat[rt].reg_0x3010},
+			{REG_0x3011,
+				vx6953_regs.reg_pat[rt].reg_0x3011},
+			{REG_0x301a,
+				vx6953_regs.reg_pat[rt].reg_0x301a},
+			{REG_0x3035,
+				vx6953_regs.reg_pat[rt].reg_0x3035},
+			{REG_0x3036,
+				vx6953_regs.reg_pat[rt].reg_0x3036},
+			{REG_0x3041,
+				vx6953_regs.reg_pat[rt].reg_0x3041},
+			{0x3042,
+				vx6953_regs.reg_pat[rt].reg_0x3042},
+			{REG_0x3045,
+				vx6953_regs.reg_pat[rt].reg_0x3045},
+			/*EDOF: Estimation settings for Preview mode
+			Application settings for capture mode
+			(standard settings - Not tuned) */
+			{REG_0x0b80,
+				vx6953_regs.reg_pat[rt].reg_0x0b80},
+			{REG_0x0900,
+				vx6953_regs.reg_pat[rt].reg_0x0900},
+			{REG_0x0901,
+				vx6953_regs.reg_pat[rt].reg_0x0901},
+			{REG_0x0902,
+				vx6953_regs.reg_pat[rt].reg_0x0902},
+			{REG_0x0383,
+				vx6953_regs.reg_pat[rt].reg_0x0383},
+			{REG_0x0387,
+				vx6953_regs.reg_pat[rt].reg_0x0387},
+			/* Change output size / frame rate */
+			{REG_0x034c,
+				vx6953_regs.reg_pat[rt].reg_0x034c},
+			{REG_0x034d,
+				vx6953_regs.reg_pat[rt].reg_0x034d},
+			{REG_0x034e,
+				vx6953_regs.reg_pat[rt].reg_0x034e},
+			{REG_0x034f,
+				vx6953_regs.reg_pat[rt].reg_0x034f},
+			{REG_0x1716,
+				vx6953_regs.reg_pat[rt].reg_0x1716},
+			{REG_0x1717,
+				vx6953_regs.reg_pat[rt].reg_0x1717},
+			{REG_0x1718,
+				vx6953_regs.reg_pat[rt].reg_0x1718},
+			{REG_0x1719,
+				vx6953_regs.reg_pat[rt].reg_0x1719},
+			};
+			struct vx6953_i2c_reg_conf mode_tbl[] = {
+			{REG_0x0112,
+				vx6953_regs.reg_pat_init[0].reg_0x0112},
+			{REG_0x0113,
+				vx6953_regs.reg_pat_init[0].reg_0x0113},
+			{REG_VT_PIX_CLK_DIV,
+				vx6953_regs.reg_pat_init[0].
+				vt_pix_clk_div},
+			{REG_PRE_PLL_CLK_DIV,
+				vx6953_regs.reg_pat_init[0].
+				pre_pll_clk_div},
+			{REG_PLL_MULTIPLIER,
+				vx6953_regs.reg_pat_init[0].
+				pll_multiplier},
+			{REG_OP_PIX_CLK_DIV,
+				vx6953_regs.reg_pat_init[0].
+				op_pix_clk_div},
+		/* Mode specific regieters */
+			{REG_FRAME_LENGTH_LINES_HI,
+				vx6953_regs.reg_pat[rt].frame_length_lines_hi},
+			{REG_FRAME_LENGTH_LINES_LO,
+				vx6953_regs.reg_pat[rt].frame_length_lines_lo},
+			{REG_LINE_LENGTH_PCK_HI,
+				vx6953_regs.reg_pat[rt].line_length_pck_hi},
+			{REG_LINE_LENGTH_PCK_LO,
+				vx6953_regs.reg_pat[rt].line_length_pck_lo},
+			{REG_0x3005, vx6953_regs.reg_pat[rt].reg_0x3005},
+			{0x3010, vx6953_regs.reg_pat[rt].reg_0x3010},
+			{REG_0x3011, vx6953_regs.reg_pat[rt].reg_0x3011},
+			{REG_0x301a, vx6953_regs.reg_pat[rt].reg_0x301a},
+			{REG_0x3035, vx6953_regs.reg_pat[rt].reg_0x3035},
+			{REG_0x3036, vx6953_regs.reg_pat[rt].reg_0x3036},
+			{REG_0x3041, vx6953_regs.reg_pat[rt].reg_0x3041},
+			{0x3042, vx6953_regs.reg_pat[rt].reg_0x3042},
+			{REG_0x3045, vx6953_regs.reg_pat[rt].reg_0x3045},
+			/*EDOF: Estimation settings for Preview mode
+			Application settings for capture
+			mode(standard settings - Not tuned) */
+			{REG_0x0b80, vx6953_regs.reg_pat[rt].reg_0x0b80},
+			{REG_0x0900, vx6953_regs.reg_pat[rt].reg_0x0900},
+			{REG_0x0901, vx6953_regs.reg_pat[rt].reg_0x0901},
+			{REG_0x0902, vx6953_regs.reg_pat[rt].reg_0x0902},
+			{REG_0x0383, vx6953_regs.reg_pat[rt].reg_0x0383},
+			{REG_0x0387, vx6953_regs.reg_pat[rt].reg_0x0387},
+			/* Change output size / frame rate */
+			{REG_0x034c, vx6953_regs.reg_pat[rt].reg_0x034c},
+			{REG_0x034d, vx6953_regs.reg_pat[rt].reg_0x034d},
+			{REG_0x034e, vx6953_regs.reg_pat[rt].reg_0x034e},
+			{REG_0x034f, vx6953_regs.reg_pat[rt].reg_0x034f},
+			/*{0x200, vx6953_regs.reg_pat[rt].reg_0x0200},
+			{0x201, vx6953_regs.reg_pat[rt].reg_0x0201},*/
+			{REG_0x1716, vx6953_regs.reg_pat[rt].reg_0x1716},
+			{REG_0x1717, vx6953_regs.reg_pat[rt].reg_0x1717},
+			{REG_0x1718, vx6953_regs.reg_pat[rt].reg_0x1718},
+			{REG_0x1719, vx6953_regs.reg_pat[rt].reg_0x1719},
+			};
+			/* stop streaming */
+			msleep(5);
+
+			/* Reset everything first */
+			if (vx6953_i2c_write_b_sensor(0x103, 0x01) < 0) {
+				CDBG("S/W reset failed\n");
+				return rc;
+			} else
+				CDBG("S/W reset successful\n");
+
+			msleep(10);
+
+			if (vx6953_i2c_write_b_sensor(REG_MODE_SELECT,
+				MODE_SELECT_STANDBY_MODE) < 0)
+				return rc;
+			/*vx6953_stm5m0edof_delay_msecs_stdby*/
+			msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+
+			vx6953_csi_params.data_format = CSI_8BIT;
+			vx6953_csi_params.lane_cnt = 1;
+			vx6953_csi_params.lane_assign = 0xe4;
+			vx6953_csi_params.dpcm_scheme = 0;
+			vx6953_csi_params.settle_cnt = 7;
+			rc = msm_camio_csi_config(&vx6953_csi_params);
+			if (rc < 0)
+				return rc;
+
+			msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+			vx6953_patch_for_cut2();
+			rc = vx6953_i2c_write_w_table(&init_mode_tbl[0],
+				ARRAY_SIZE(init_mode_tbl));
+			if (rc < 0)
+				return rc;
+
+			msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+			rc = vx6953_i2c_write_w_table(&mode_tbl[0],
+				ARRAY_SIZE(mode_tbl));
+			if (rc < 0)
+				return rc;
+
+			msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+			/* Start sensor streaming */
+			if (vx6953_i2c_write_b_sensor(REG_MODE_SELECT,
+				MODE_SELECT_STREAM) < 0)
+				return rc;
+			msleep(vx6953_stm5m0edof_delay_msecs_stream);
+
+			if (vx6953_i2c_read(0x0005, &frame_cnt, 1) < 0)
+				return rc;
+
+			while (frame_cnt == 0xFF) {
+				if (vx6953_i2c_read(0x0005, &frame_cnt, 1) < 0)
+					return rc;
+				CDBG("frame_cnt=%d", frame_cnt);
+				msleep(10);
+			}
+		}
+		return rc;
+	default:
+		return rc;
+	}
+	}
+	return rc;
+}
+
+
+static int32_t vx6953_video_config(int mode)
+{
+
+	int32_t	rc = 0;
+	int	rt;
+	/* change sensor resolution	if needed */
+	if (vx6953_ctrl->curr_res != vx6953_ctrl->prev_res) {
+		if (vx6953_ctrl->prev_res == QTR_SIZE) {
+			rt = RES_PREVIEW;
+			vx6953_stm5m0edof_delay_msecs_stdby	=
+				((((2 * 1000 * vx6953_ctrl->fps_divider) /
+				   vx6953_ctrl->fps) * Q8) / Q10) + 1;
+		} else {
+			rt = RES_CAPTURE;
+			vx6953_stm5m0edof_delay_msecs_stdby	=
+				((((1000 * vx6953_ctrl->fps_divider) /
+				   vx6953_ctrl->fps) * Q8) / Q10) + 1;
+		}
+		if (vx6953_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+			return rc;
+	}
+	if (vx6953_ctrl->set_test) {
+		if (vx6953_test(vx6953_ctrl->set_test) < 0)
+			return	rc;
+	}
+	vx6953_ctrl->edof_mode = VX6953_EDOF_ESTIMATION;
+	rc = vx6953_enable_edof(vx6953_ctrl->edof_mode);
+	if (rc < 0)
+		return rc;
+	vx6953_ctrl->curr_res = vx6953_ctrl->prev_res;
+	vx6953_ctrl->sensormode = mode;
+	return rc;
+}
+
+static int32_t vx6953_snapshot_config(int mode)
+{
+	int32_t rc = 0;
+	int rt;
+	/*change sensor resolution if needed */
+	if (vx6953_ctrl->curr_res != vx6953_ctrl->pict_res) {
+		if (vx6953_ctrl->pict_res == QTR_SIZE) {
+			rt = RES_PREVIEW;
+			vx6953_stm5m0edof_delay_msecs_stdby =
+				((((2 * 1000 * vx6953_ctrl->fps_divider) /
+				vx6953_ctrl->fps) * Q8) / Q10) + 1;
+		} else {
+			rt = RES_CAPTURE;
+			vx6953_stm5m0edof_delay_msecs_stdby =
+				((((1000 * vx6953_ctrl->fps_divider) /
+				vx6953_ctrl->fps) * Q8) / Q10) + 1;
+		}
+	if (vx6953_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+		return rc;
+	}
+
+	vx6953_ctrl->edof_mode = VX6953_EDOF_APPLICATION;
+	if (vx6953_enable_edof(vx6953_ctrl->edof_mode) < 0)
+		return rc;
+	vx6953_ctrl->curr_res = vx6953_ctrl->pict_res;
+	vx6953_ctrl->sensormode = mode;
+	return rc;
+} /*end of vx6953_snapshot_config*/
+
+static int32_t vx6953_raw_snapshot_config(int mode)
+{
+	int32_t rc = 0;
+	int rt;
+	/* change sensor resolution if needed */
+	if (vx6953_ctrl->curr_res != vx6953_ctrl->pict_res) {
+		if (vx6953_ctrl->pict_res == QTR_SIZE) {
+			rt = RES_PREVIEW;
+			vx6953_stm5m0edof_delay_msecs_stdby =
+				((((2 * 1000 * vx6953_ctrl->fps_divider)/
+				vx6953_ctrl->fps) * Q8) / Q10) + 1;
+		} else {
+			rt = RES_CAPTURE;
+			vx6953_stm5m0edof_delay_msecs_stdby =
+				((((1000 * vx6953_ctrl->fps_divider)/
+				vx6953_ctrl->fps) * Q8) / Q10) + 1;
+		}
+		if (vx6953_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+			return rc;
+	}
+	vx6953_ctrl->edof_mode = VX6953_EDOF_APPLICATION;
+	if (vx6953_enable_edof(vx6953_ctrl->edof_mode) < 0)
+		return rc;
+	vx6953_ctrl->curr_res = vx6953_ctrl->pict_res;
+	vx6953_ctrl->sensormode = mode;
+	return rc;
+} /*end of vx6953_raw_snapshot_config*/
+static int32_t vx6953_set_sensor_mode(int mode,
+	int res)
+{
+	int32_t rc = 0;
+	switch (mode) {
+	case SENSOR_PREVIEW_MODE:
+		rc = vx6953_video_config(mode);
+		break;
+	case SENSOR_SNAPSHOT_MODE:
+		rc = vx6953_snapshot_config(mode);
+		break;
+	case SENSOR_RAW_SNAPSHOT_MODE:
+		rc = vx6953_raw_snapshot_config(mode);
+		break;
+	default:
+		rc = -EINVAL;
+		break;
+	}
+	return rc;
+}
+static int32_t vx6953_power_down(void)
+{
+	return 0;
+}
+
+
+static int vx6953_probe_init_done(const struct msm_camera_sensor_info *data)
+{
+	gpio_set_value_cansleep(data->sensor_reset, 0);
+	gpio_free(data->sensor_reset);
+	return 0;
+}
+static int vx6953_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+	int32_t rc = 0;
+	unsigned short chipidl, chipidh;
+	CDBG("%s: %d\n", __func__, __LINE__);
+	rc = gpio_request(data->sensor_reset, "vx6953");
+	CDBG(" vx6953_probe_init_sensor \n");
+	if (!rc) {
+		CDBG("sensor_reset = %d\n", rc);
+		CDBG(" vx6953_probe_init_sensor 1\n");
+		gpio_direction_output(data->sensor_reset, 0);
+		msleep(10);
+		CDBG(" vx6953_probe_init_sensor 1\n");
+		gpio_set_value_cansleep(data->sensor_reset, 1);
+	} else {
+		CDBG(" vx6953_probe_init_sensor 2\n");
+		goto init_probe_done;
+	}
+	msleep(20);
+	CDBG(" vx6953_probe_init_sensor is called\n");
+	/* 3. Read sensor Model ID: */
+	rc = vx6953_i2c_read(0x0000, &chipidh, 1);
+	if (rc < 0) {
+		CDBG(" vx6953_probe_init_sensor 3\n");
+		goto init_probe_fail;
+	}
+	rc = vx6953_i2c_read(0x0001, &chipidl, 1);
+	if (rc < 0) {
+		CDBG(" vx6953_probe_init_sensor4\n");
+		goto init_probe_fail;
+	}
+	CDBG("vx6953 model_id = 0x%x  0x%x\n", chipidh, chipidl);
+	/* 4. Compare sensor ID to VX6953 ID: */
+	if (chipidh != 0x03 || chipidl != 0xB9) {
+		rc = -ENODEV;
+		CDBG("vx6953_probe_init_sensor fail chip id doesnot match\n");
+		goto init_probe_fail;
+	}
+	goto init_probe_done;
+init_probe_fail:
+	CDBG(" vx6953_probe_init_sensor fails\n");
+	vx6953_probe_init_done(data);
+init_probe_done:
+	CDBG(" vx6953_probe_init_sensor finishes\n");
+	return rc;
+	}
+/* camsensor_iu060f_vx6953_reset */
+int vx6953_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+	unsigned short revision_number;
+	int32_t rc = 0;
+
+	CDBG("%s: %d\n", __func__, __LINE__);
+	CDBG("Calling vx6953_sensor_open_init\n");
+	vx6953_ctrl = kzalloc(sizeof(struct vx6953_ctrl_t), GFP_KERNEL);
+	if (!vx6953_ctrl) {
+		CDBG("vx6953_init failed!\n");
+		rc = -ENOMEM;
+		goto init_done;
+	}
+	vx6953_ctrl->fps_divider = 1 * 0x00000400;
+	vx6953_ctrl->pict_fps_divider = 1 * 0x00000400;
+	vx6953_ctrl->set_test = TEST_OFF;
+	vx6953_ctrl->prev_res = QTR_SIZE;
+	vx6953_ctrl->pict_res = FULL_SIZE;
+	vx6953_ctrl->curr_res = INVALID_SIZE;
+	vx6953_ctrl->sensor_type = VX6953_STM5M0EDOF_CUT_2;
+	vx6953_ctrl->edof_mode = VX6953_EDOF_ESTIMATION;
+	if (data)
+		vx6953_ctrl->sensordata = data;
+	if (rc < 0) {
+		CDBG("Calling vx6953_sensor_open_init fail1\n");
+		return rc;
+	}
+	CDBG("%s: %d\n", __func__, __LINE__);
+	/* enable mclk first */
+	msm_camio_clk_rate_set(VX6953_STM5M0EDOF_DEFAULT_MASTER_CLK_RATE);
+	CDBG("%s: %d\n", __func__, __LINE__);
+	rc = vx6953_probe_init_sensor(data);
+	if (rc < 0) {
+		CDBG("Calling vx6953_sensor_open_init fail3\n");
+		goto init_fail;
+	}
+	if (vx6953_i2c_read(0x0002, &revision_number, 1) < 0)
+		return rc;
+		CDBG("sensor revision number major = 0x%x\n", revision_number);
+	if (vx6953_i2c_read(0x0018, &revision_number, 1) < 0)
+		return rc;
+		CDBG("sensor revision number = 0x%x\n", revision_number);
+	if (revision_number == VX6953_REVISION_NUMBER_CUT3) {
+		vx6953_ctrl->sensor_type = VX6953_STM5M0EDOF_CUT_3;
+		CDBG("VX6953 EDof Cut 3.0 sensor\n ");
+	} else if (revision_number == VX6953_REVISION_NUMBER_CUT2) {
+		vx6953_ctrl->sensor_type = VX6953_STM5M0EDOF_CUT_2;
+		CDBG("VX6953 EDof Cut 2.0 sensor\n ");
+	} else {/* Cut1.0 reads 0x00 for register 0x0018*/
+		vx6953_ctrl->sensor_type = VX6953_STM5M0EDOF_CUT_1;
+		CDBG("VX6953 EDof Cut 1.0 sensor\n ");
+	}
+	if (vx6953_ctrl->prev_res == QTR_SIZE) {
+		if (vx6953_sensor_setting(REG_INIT, RES_PREVIEW) < 0)
+			return rc;
+	} else {
+		if (vx6953_sensor_setting(REG_INIT, RES_CAPTURE) < 0)
+			return rc;
+	}
+	vx6953_ctrl->fps = 30*Q8;
+	if (rc < 0)
+		goto init_fail;
+	else
+		goto init_done;
+init_fail:
+	CDBG("init_fail\n");
+	vx6953_probe_init_done(data);
+	kfree(vx6953_ctrl);
+init_done:
+	CDBG("init_done\n");
+	return rc;
+} /*endof vx6953_sensor_open_init*/
+
+static int vx6953_init_client(struct i2c_client *client)
+{
+	/* Initialize the MSM_CAMI2C Chip */
+	init_waitqueue_head(&vx6953_wait_queue);
+	return 0;
+}
+
+static const struct i2c_device_id vx6953_i2c_id[] = {
+	{"vx6953", 0},
+	{ }
+};
+
+static int vx6953_i2c_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	int rc = 0;
+	CDBG("vx6953_probe called!\n");
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		CDBG("i2c_check_functionality failed\n");
+		goto probe_failure;
+	}
+
+	vx6953_sensorw = kzalloc(sizeof(struct vx6953_work_t), GFP_KERNEL);
+	if (!vx6953_sensorw) {
+		CDBG("kzalloc failed.\n");
+		rc = -ENOMEM;
+		goto probe_failure;
+	}
+
+	i2c_set_clientdata(client, vx6953_sensorw);
+	vx6953_init_client(client);
+	vx6953_client = client;
+
+	msleep(50);
+
+	CDBG("vx6953_probe successed! rc = %d\n", rc);
+	return 0;
+
+probe_failure:
+	CDBG("vx6953_probe failed! rc = %d\n", rc);
+	return rc;
+}
+
+static int vx6953_send_wb_info(struct wb_info_cfg *wb)
+{
+	unsigned short read_data;
+	uint8_t temp[8];
+	int rc = 0;
+	int i = 0;
+
+	/* red_gain */
+	temp[2] = wb->red_gain >> 8;
+	temp[3] = wb->red_gain & 0xFF;
+
+	/* green_gain */
+	temp[0] = wb->green_gain >> 8;
+	temp[1] = wb->green_gain & 0xFF;
+	temp[6] = temp[0];
+	temp[7] = temp[1];
+
+	/* blue_gain */
+	temp[4] = wb->blue_gain >> 8;
+	temp[5] = wb->blue_gain & 0xFF;
+	rc = vx6953_i2c_write_seq_sensor(0x0B8E, &temp[0], 8);
+
+	for (i = 0; i < 6; i++) {
+		rc = vx6953_i2c_read(0x0B8E + i, &read_data, 1);
+		CDBG("%s addr 0x%x val %d \n", __func__, 0x0B8E + i, read_data);
+	}
+	rc = vx6953_i2c_read(0x0B82, &read_data, 1);
+	CDBG("%s addr 0x%x val %d \n", __func__, 0x0B82, read_data);
+	if (rc < 0)
+		return rc;
+	return rc;
+} /*end of vx6953_snapshot_config*/
+
+static int __exit vx6953_remove(struct i2c_client *client)
+{
+	struct vx6953_work_t_t *sensorw = i2c_get_clientdata(client);
+	free_irq(client->irq, sensorw);
+	vx6953_client = NULL;
+	kfree(sensorw);
+	return 0;
+}
+
+static struct i2c_driver vx6953_i2c_driver = {
+	.id_table = vx6953_i2c_id,
+	.probe  = vx6953_i2c_probe,
+	.remove = __exit_p(vx6953_i2c_remove),
+	.driver = {
+		.name = "vx6953",
+	},
+};
+
+int vx6953_sensor_config(void __user *argp)
+{
+	struct sensor_cfg_data cdata;
+	long   rc = 0;
+	if (copy_from_user(&cdata,
+		(void *)argp,
+		sizeof(struct sensor_cfg_data)))
+		return -EFAULT;
+	mutex_lock(&vx6953_mut);
+	CDBG("vx6953_sensor_config: cfgtype = %d\n",
+	cdata.cfgtype);
+		switch (cdata.cfgtype) {
+		case CFG_GET_PICT_FPS:
+			vx6953_get_pict_fps(
+				cdata.cfg.gfps.prevfps,
+				&(cdata.cfg.gfps.pictfps));
+
+			if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+				rc = -EFAULT;
+			break;
+
+		case CFG_GET_PREV_L_PF:
+			cdata.cfg.prevl_pf =
+			vx6953_get_prev_lines_pf();
+
+			if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+				rc = -EFAULT;
+			break;
+
+		case CFG_GET_PREV_P_PL:
+			cdata.cfg.prevp_pl =
+				vx6953_get_prev_pixels_pl();
+
+			if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+				rc = -EFAULT;
+			break;
+
+		case CFG_GET_PICT_L_PF:
+			cdata.cfg.pictl_pf =
+				vx6953_get_pict_lines_pf();
+
+			if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+				rc = -EFAULT;
+			break;
+
+		case CFG_GET_PICT_P_PL:
+			cdata.cfg.pictp_pl =
+				vx6953_get_pict_pixels_pl();
+
+			if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+				rc = -EFAULT;
+			break;
+
+		case CFG_GET_PICT_MAX_EXP_LC:
+			cdata.cfg.pict_max_exp_lc =
+				vx6953_get_pict_max_exp_lc();
+
+			if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+				rc = -EFAULT;
+			break;
+
+		case CFG_SET_FPS:
+		case CFG_SET_PICT_FPS:
+			rc = vx6953_set_fps(&(cdata.cfg.fps));
+			break;
+
+		case CFG_SET_EXP_GAIN:
+			rc =
+				vx6953_write_exp_gain(
+					cdata.cfg.exp_gain.gain,
+					cdata.cfg.exp_gain.line);
+			break;
+
+		case CFG_SET_PICT_EXP_GAIN:
+			rc =
+				vx6953_set_pict_exp_gain(
+				cdata.cfg.exp_gain.gain,
+				cdata.cfg.exp_gain.line);
+			break;
+
+		case CFG_SET_MODE:
+			rc = vx6953_set_sensor_mode(cdata.mode,
+					cdata.rs);
+			break;
+
+		case CFG_PWR_DOWN:
+			rc = vx6953_power_down();
+			break;
+
+		case CFG_MOVE_FOCUS:
+			rc =
+				vx6953_move_focus(
+				cdata.cfg.focus.dir,
+				cdata.cfg.focus.steps);
+			break;
+
+		case CFG_SET_DEFAULT_FOCUS:
+			rc =
+				vx6953_set_default_focus(
+				cdata.cfg.focus.steps);
+			break;
+
+		case CFG_SET_EFFECT:
+			rc = vx6953_set_default_focus(
+				cdata.cfg.effect);
+			break;
+
+
+		case CFG_SEND_WB_INFO:
+			rc = vx6953_send_wb_info(
+				&(cdata.cfg.wb_info));
+			break;
+
+		default:
+			rc = -EFAULT;
+			break;
+		}
+
+	mutex_unlock(&vx6953_mut);
+
+	return rc;
+}
+
+
+
+
+static int vx6953_sensor_release(void)
+{
+	int rc = -EBADF;
+	mutex_lock(&vx6953_mut);
+	vx6953_power_down();
+	gpio_direction_output(vx6953_ctrl->sensordata->sensor_reset, 0);
+	gpio_free(vx6953_ctrl->sensordata->sensor_reset);
+	kfree(vx6953_ctrl);
+	vx6953_ctrl = NULL;
+	CDBG("vx6953_release completed\n");
+	mutex_unlock(&vx6953_mut);
+
+	return rc;
+}
+
+static int vx6953_sensor_probe(const struct msm_camera_sensor_info *info,
+		struct msm_sensor_ctrl *s)
+{
+	int rc = 0;
+	rc = i2c_add_driver(&vx6953_i2c_driver);
+	if (rc < 0 || vx6953_client == NULL) {
+		rc = -ENOTSUPP;
+		goto probe_fail;
+	}
+	msm_camio_clk_rate_set(24000000);
+	rc = vx6953_probe_init_sensor(info);
+	if (rc < 0)
+		goto probe_fail;
+	s->s_init = vx6953_sensor_open_init;
+	s->s_release = vx6953_sensor_release;
+	s->s_config  = vx6953_sensor_config;
+	s->s_mount_angle  = info->sensor_platform_info->mount_angle;
+	vx6953_probe_init_done(info);
+	return rc;
+
+probe_fail:
+	CDBG("vx6953_sensor_probe: SENSOR PROBE FAILS!\n");
+	return rc;
+}
+
+static int __vx6953_probe(struct platform_device *pdev)
+{
+	return msm_camera_drv_start(pdev, vx6953_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+	.probe = __vx6953_probe,
+	.driver = {
+		.name = "msm_camera_vx6953",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init vx6953_init(void)
+{
+	return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(vx6953_init);
+void vx6953_exit(void)
+{
+	i2c_del_driver(&vx6953_i2c_driver);
+}
+
+
diff --git a/drivers/media/video/msm/vx6953.h b/drivers/media/video/msm/vx6953.h
new file mode 100644
index 0000000..0e12063
--- /dev/null
+++ b/drivers/media/video/msm/vx6953.h
@@ -0,0 +1,136 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef VX6953_H
+#define VX6953_H
+#include <linux/types.h>
+#include <mach/board.h>
+extern struct vx6953_reg vx6953_regs;
+struct reg_struct_init {
+	uint8_t reg_0x0112;      /* 0x0112*/
+	uint8_t reg_0x0113;      /* 0x0113*/
+	uint8_t vt_pix_clk_div;  /* 0x0301*/
+	uint8_t pre_pll_clk_div; /* 0x0305*/
+	uint8_t pll_multiplier;  /* 0x0307*/
+	uint8_t op_pix_clk_div;  /* 0x0309*/
+	uint8_t reg_0x3030;      /*0x3030*/
+	uint8_t reg_0x0111;      /*0x0111*/
+	uint8_t reg_0x0b00;      /*0x0b00*/
+	uint8_t reg_0x3001;      /*0x3001*/
+	uint8_t reg_0x3004;      /*0x3004*/
+	uint8_t reg_0x3007;      /*0x3007*/
+	uint8_t reg_0x3016;      /*0x3016*/
+	uint8_t reg_0x301d;      /*0x301d*/
+	uint8_t reg_0x317e;      /*0x317E*/
+	uint8_t reg_0x317f;      /*0x317F*/
+	uint8_t reg_0x3400;      /*0x3400*/
+	uint8_t reg_0x0b06;      /*0x0b06*/
+	uint8_t reg_0x0b07;      /*0x0b07*/
+	uint8_t reg_0x0b08;      /*0x0b08*/
+	uint8_t reg_0x0b09;      /*0x0b09*/
+	uint8_t reg_0x0136;
+	uint8_t reg_0x0137;
+	/* Edof */
+	uint8_t reg_0x0b83;      /*0x0b83*/
+	uint8_t reg_0x0b84;      /*0x0b84*/
+	uint8_t reg_0x0b85;      /*0x0b85*/
+	uint8_t reg_0x0b88;      /*0x0b88*/
+	uint8_t reg_0x0b89;      /*0x0b89*/
+	uint8_t reg_0x0b8a;      /*0x0b8a*/
+	};
+struct reg_struct {
+	uint8_t coarse_integration_time_hi; /*REG_COARSE_INTEGRATION_TIME_HI*/
+	uint8_t coarse_integration_time_lo; /*REG_COARSE_INTEGRATION_TIME_LO*/
+	uint8_t analogue_gain_code_global;
+	uint8_t frame_length_lines_hi; /* 0x0340*/
+	uint8_t frame_length_lines_lo; /* 0x0341*/
+	uint8_t line_length_pck_hi;    /* 0x0342*/
+	uint8_t line_length_pck_lo;    /* 0x0343*/
+	uint8_t reg_0x3005;   /* 0x3005*/
+	uint8_t reg_0x3010;  /* 0x3010*/
+	uint8_t reg_0x3011;  /* 0x3011*/
+	uint8_t reg_0x301a;  /* 0x301a*/
+	uint8_t reg_0x3035;  /* 0x3035*/
+	uint8_t reg_0x3036;   /* 0x3036*/
+	uint8_t reg_0x3041;  /*0x3041*/
+	uint8_t reg_0x3042;  /*0x3042*/
+	uint8_t reg_0x3045;  /*0x3045*/
+	uint8_t reg_0x0b80;   /* 0x0b80*/
+	uint8_t reg_0x0900;   /*0x0900*/
+	uint8_t reg_0x0901;   /* 0x0901*/
+	uint8_t reg_0x0902;   /*0x0902*/
+	uint8_t reg_0x0383;   /*0x0383*/
+	uint8_t reg_0x0387;   /* 0x0387*/
+	uint8_t reg_0x034c;   /* 0x034c*/
+	uint8_t reg_0x034d;   /*0x034d*/
+	uint8_t reg_0x034e;   /* 0x034e*/
+	uint8_t reg_0x034f;   /* 0x034f*/
+	uint8_t reg_0x1716; /*0x1716*/
+	uint8_t reg_0x1717; /*0x1717*/
+	uint8_t reg_0x1718; /*0x1718*/
+	uint8_t reg_0x1719; /*0x1719*/
+	uint8_t reg_0x3210;/*0x3210*/
+	uint8_t reg_0x111; /*0x111*/
+	uint8_t reg_0x3410;  /*0x3410*/
+	uint8_t reg_0x3098;
+	uint8_t reg_0x309D;
+	uint8_t reg_0x0200;
+	uint8_t reg_0x0201;
+	};
+struct vx6953_i2c_reg_conf {
+	unsigned short waddr;
+	unsigned short wdata;
+};
+
+enum vx6953_test_mode_t {
+	TEST_OFF,
+	TEST_1,
+	TEST_2,
+	TEST_3
+};
+
+enum vx6953_resolution_t {
+	QTR_SIZE,
+	FULL_SIZE,
+	INVALID_SIZE
+};
+enum vx6953_setting {
+	RES_PREVIEW,
+	RES_CAPTURE
+};
+enum mt9p012_reg_update {
+	/* Sensor egisters that need to be updated during initialization */
+	REG_INIT,
+	/* Sensor egisters that needs periodic I2C writes */
+	UPDATE_PERIODIC,
+	/* All the sensor Registers will be updated */
+	UPDATE_ALL,
+	/* Not valid update */
+	UPDATE_INVALID
+};
+
+enum sensor_revision_t {
+	VX6953_STM5M0EDOF_CUT_1,
+	VX6953_STM5M0EDOF_CUT_2,
+	VX6953_STM5M0EDOF_CUT_3
+};
+enum edof_mode_t {
+	VX6953_EDOF_DISABLE,       /* 0x00 */
+	VX6953_EDOF_APPLICATION,   /* 0x01 */
+	VX6953_EDOF_ESTIMATION     /* 0x02 */
+};
+struct vx6953_reg {
+	const struct reg_struct_init  *reg_pat_init;
+	const struct reg_struct  *reg_pat;
+};
+#endif /* VX6953_H */
diff --git a/drivers/media/video/msm/vx6953_reg.c b/drivers/media/video/msm/vx6953_reg.c
new file mode 100644
index 0000000..48fc71f
--- /dev/null
+++ b/drivers/media/video/msm/vx6953_reg.c
@@ -0,0 +1,135 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+
+#include "vx6953.h"
+const struct reg_struct_init vx6953_reg_init[1] = {
+	{
+		10,			/*REG = 0x0112 , 10 bit */
+		10,			/*REG = 0x0113*/
+		9,			/*REG = 0x0301 vt_pix_clk_div*/
+		4,		/*REG = 0x0305 pre_pll_clk_div*/
+		133,		/*REG = 0x0307 pll_multiplier*/
+		10,		/*REG = 0x0309 op_pix_clk_div*/
+		0x08,		/*REG = 0x3030*/
+		0x02,		/*REG = 0x0111*/
+		0x01,		/*REG = 0x0b00 ,lens shading off */
+		0x30,		/*REG = 0x3001*/
+		0x33,		/*REG = 0x3004*/
+		0x09,		/*REG = 0x3007*/
+		0x1F,		/*REG = 0x3016*/
+		0x03,		/*REG = 0x301d*/
+		0x11,		/*REG = 0x317E*/
+		0x09,		/*REG = 0x317F*/
+		0x38,		/*REG = 0x3400*/
+		0x00,		/*REG_0x0b06*/
+		0x80,		/*REG_0x0b07*/
+		0x01,		/*REG_0x0b08*/
+		0x4F,		/*REG_0x0b09*/
+		0x18,		/*REG_0x0136*/
+		0x00,		/*/REG_0x0137*/
+		0x20,		/*REG = 0x0b83*/
+		0x90,		/*REG = 0x0b84*/
+		0x20,		/*REG = 0x0b85*/
+		0x80,		/*REG = 0x0b88*/
+		0x00,		/*REG = 0x0b89*/
+		0x00,		/*REG = 0x0b8a*/
+	}
+};
+const struct reg_struct vx6953_reg_pat[2] = {
+	{/* Preview */
+		0x03,	/*REG = 0x0202 coarse integration_time_hi*/
+		0xd0,	/*REG = 0x0203 coarse_integration_time_lo*/
+		0xc0,	/*REG = 0x0205 analogue_gain_code_global*/
+		0x03,	/*REG = 0x0340 frame_length_lines_hi*/
+		0xf0,	/*REG = 0x0341 frame_length_lines_lo*/
+		0x0b,	/*REG = 0x0342  line_length_pck_hi*/
+		0x74,	/*REG = 0x0343  line_length_pck_lo*/
+		0x03,	/*REG = 0x3005*/
+		0x00,	/*REG = 0x3010*/
+		0x01,	/*REG = 0x3011*/
+		0x6a,	/*REG = 0x301a*/
+		0x03,	/*REG = 0x3035*/
+		0x2c,	/*REG = 0x3036*/
+		0x00,	/*REG = 0x3041*/
+		0x24,	/*REG = 0x3042*/
+		0x81,	/*REG = 0x3045*/
+		0x02,	/*REG = 0x0b80 edof estimate*/
+		0x01,	/*REG = 0x0900*/
+		0x22,	/*REG = 0x0901*/
+		0x04,	/*REG = 0x0902*/
+		0x03,	/*REG = 0x0383*/
+		0x03,	/*REG = 0x0387*/
+		0x05,	/*REG = 0x034c*/
+		0x18,	/*REG = 0x034d*/
+		0x03,	/*REG = 0x034e*/
+		0xd4,	/*REG = 0x034f*/
+		0x02,	/*0x1716*/
+		0x04,	/*0x1717*/
+		0x08,	/*0x1718*/
+		0x2c,	/*0x1719*/
+		0x01,   /*0x3210*/
+		0x02,   /*0x111*/
+		0x01,   /*0x3410*/
+		0x01,   /*0x3098*/
+		0x05,   /*0x309D*/
+		0x02,
+		0x04,
+	},
+	{ /* Snapshot */
+		0x07,/*REG = 0x0202 coarse_integration_time_hi*/
+		0x00,/*REG = 0x0203 coarse_integration_time_lo*/
+		0xc0,/*REG = 0x0205 analogue_gain_code_global*/
+		0x07,/*REG = 0x0340 frame_length_lines_hi*/
+		0xd0,/*REG = 0x0341 frame_length_lines_lo*/
+		0x0b,/*REG = 0x0342 line_length_pck_hi*/
+		0x8c,/*REG = 0x0343 line_length_pck_lo*/
+		0x01,/*REG = 0x3005*/
+		0x00,/*REG = 0x3010*/
+		0x00,/*REG = 0x3011*/
+		0x55,/*REG = 0x301a*/
+		0x01,/*REG = 0x3035*/
+		0x23,/*REG = 0x3036*/
+		0x00,/*REG = 0x3041*/
+		0x24,/*REG = 0x3042*/
+		0xb7,/*REG = 0x3045*/
+		0x01,/*REG = 0x0b80 edof application*/
+		0x00,/*REG = 0x0900*/
+		0x00,/*REG = 0x0901*/
+		0x00,/*REG = 0x0902*/
+		0x01,/*REG = 0x0383*/
+		0x01,/*REG = 0x0387*/
+		0x0A,/*REG = 0x034c*/
+		0x30,/*REG = 0x034d*/
+		0x07,/*REG = 0x034e*/
+		0xA8,/*REG = 0x034f*/
+		0x02,/*0x1716*/
+		0x0d,/*0x1717*/
+		0x07,/*0x1718*/
+		0x7d,/*0x1719*/
+		0x01,/*0x3210*/
+		0x02,/*0x111*/
+		0x01,/*0x3410*/
+		0x01,/*0x3098*/
+		0x05, /*0x309D*/
+		0x02,
+		0x00,
+	}
+};
+
+
+
+struct vx6953_reg vx6953_regs = {
+	.reg_pat_init = &vx6953_reg_init[0],
+	.reg_pat = &vx6953_reg_pat[0],
+};
diff --git a/drivers/media/video/msm/vx6953_reg_v4l2.c b/drivers/media/video/msm/vx6953_reg_v4l2.c
new file mode 100644
index 0000000..f16054b
--- /dev/null
+++ b/drivers/media/video/msm/vx6953_reg_v4l2.c
@@ -0,0 +1,135 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+
+#include "vx6953_v4l2.h"
+const struct reg_struct_init vx6953_reg_init[1] = {
+	{
+		10,			/*REG = 0x0112 , 10 bit */
+		10,			/*REG = 0x0113*/
+		9,			/*REG = 0x0301 vt_pix_clk_div*/
+		4,		/*REG = 0x0305 pre_pll_clk_div*/
+		133,		/*REG = 0x0307 pll_multiplier*/
+		10,		/*REG = 0x0309 op_pix_clk_div*/
+		0x08,		/*REG = 0x3030*/
+		0x02,		/*REG = 0x0111*/
+		0x01,		/*REG = 0x0b00 ,lens shading off */
+		0x30,		/*REG = 0x3001*/
+		0x33,		/*REG = 0x3004*/
+		0x09,		/*REG = 0x3007*/
+		0x1F,		/*REG = 0x3016*/
+		0x03,		/*REG = 0x301d*/
+		0x11,		/*REG = 0x317E*/
+		0x09,		/*REG = 0x317F*/
+		0x38,		/*REG = 0x3400*/
+		0x00,		/*REG_0x0b06*/
+		0x80,		/*REG_0x0b07*/
+		0x01,		/*REG_0x0b08*/
+		0x4F,		/*REG_0x0b09*/
+		0x18,		/*REG_0x0136*/
+		0x00,		/*/REG_0x0137*/
+		0x20,		/*REG = 0x0b83*/
+		0x90,		/*REG = 0x0b84*/
+		0x20,		/*REG = 0x0b85*/
+		0x80,		/*REG = 0x0b88*/
+		0x00,		/*REG = 0x0b89*/
+		0x00,		/*REG = 0x0b8a*/
+	}
+};
+const struct reg_struct vx6953_reg_pat[2] = {
+	{/* Preview */
+		0x03,	/*REG = 0x0202 coarse integration_time_hi*/
+		0xd0,	/*REG = 0x0203 coarse_integration_time_lo*/
+		0xc0,	/*REG = 0x0205 analogue_gain_code_global*/
+		0x03,	/*REG = 0x0340 frame_length_lines_hi*/
+		0xf0,	/*REG = 0x0341 frame_length_lines_lo*/
+		0x0b,	/*REG = 0x0342  line_length_pck_hi*/
+		0xa5,	/*REG = 0x0343  line_length_pck_lo*/
+		0x03,	/*REG = 0x3005*/
+		0x00,	/*REG = 0x3010*/
+		0x01,	/*REG = 0x3011*/
+		0x6a,	/*REG = 0x301a*/
+		0x03,	/*REG = 0x3035*/
+		0x2c,	/*REG = 0x3036*/
+		0x00,	/*REG = 0x3041*/
+		0x24,	/*REG = 0x3042*/
+		0x81,	/*REG = 0x3045*/
+		0x02,	/*REG = 0x0b80 edof estimate*/
+		0x01,	/*REG = 0x0900*/
+		0x22,	/*REG = 0x0901*/
+		0x04,	/*REG = 0x0902*/
+		0x03,	/*REG = 0x0383*/
+		0x03,	/*REG = 0x0387*/
+		0x05,	/*REG = 0x034c*/
+		0x18,	/*REG = 0x034d*/
+		0x03,	/*REG = 0x034e*/
+		0xd4,	/*REG = 0x034f*/
+		0x02,	/*0x1716*/
+		0x04,	/*0x1717*/
+		0x08,	/*0x1718*/
+		0x80,	/*0x1719*/
+		0x01,   /*0x3210*/
+		0x02,   /*0x111*/
+		0x01,   /*0x3410*/
+		0x01,   /*0x3098*/
+		0x05,   /*0x309D*/
+		0x02,
+		0x04,
+	},
+	{ /* Snapshot */
+		0x07,/*REG = 0x0202 coarse_integration_time_hi*/
+		0x00,/*REG = 0x0203 coarse_integration_time_lo*/
+		0xc0,/*REG = 0x0205 analogue_gain_code_global*/
+		0x07,/*REG = 0x0340 frame_length_lines_hi*/
+		0xd0,/*REG = 0x0341 frame_length_lines_lo*/
+		0x0b,/*REG = 0x0342 line_length_pck_hi*/
+		0x8c,/*REG = 0x0343 line_length_pck_lo*/
+		0x01,/*REG = 0x3005*/
+		0x00,/*REG = 0x3010*/
+		0x00,/*REG = 0x3011*/
+		0x55,/*REG = 0x301a*/
+		0x01,/*REG = 0x3035*/
+		0x23,/*REG = 0x3036*/
+		0x00,/*REG = 0x3041*/
+		0x24,/*REG = 0x3042*/
+		0xb7,/*REG = 0x3045*/
+		0x01,/*REG = 0x0b80 edof application*/
+		0x00,/*REG = 0x0900*/
+		0x00,/*REG = 0x0901*/
+		0x00,/*REG = 0x0902*/
+		0x01,/*REG = 0x0383*/
+		0x01,/*REG = 0x0387*/
+		0x0A,/*REG = 0x034c*/
+		0x30,/*REG = 0x034d*/
+		0x07,/*REG = 0x034e*/
+		0xA8,/*REG = 0x034f*/
+		0x02,/*0x1716*/
+		0x0d,/*0x1717*/
+		0x07,/*0x1718*/
+		0x7d,/*0x1719*/
+		0x01,/*0x3210*/
+		0x02,/*0x111*/
+		0x01,/*0x3410*/
+		0x01,/*0x3098*/
+		0x05, /*0x309D*/
+		0x02,
+		0x00,
+	}
+};
+
+
+
+struct vx6953_reg vx6953_regs = {
+	.reg_pat_init = &vx6953_reg_init[0],
+	.reg_pat = &vx6953_reg_pat[0],
+};
diff --git a/drivers/media/video/msm/vx6953_v4l2.c b/drivers/media/video/msm/vx6953_v4l2.c
new file mode 100644
index 0000000..2e5e39b
--- /dev/null
+++ b/drivers/media/video/msm/vx6953_v4l2.c
@@ -0,0 +1,4149 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <media/v4l2-subdev.h>
+#include <media/msm_camera.h>
+#include <mach/gpio.h>
+#include <mach/camera.h>
+#include <linux/slab.h>
+#include "vx6953_v4l2.h"
+#include "msm.h"
+
+#define V4L2_IDENT_VX6953  50000
+
+/*=============================================================
+	SENSOR REGISTER DEFINES
+==============================================================*/
+
+#define REG_GROUPED_PARAMETER_HOLD			0x0104
+#define GROUPED_PARAMETER_HOLD_OFF			0x00
+#define GROUPED_PARAMETER_HOLD				0x01
+#define REG_MODE_SELECT					0x0100
+#define MODE_SELECT_STANDBY_MODE			0x00
+#define MODE_SELECT_STREAM				0x01
+/* Integration Time */
+#define REG_COARSE_INTEGRATION_TIME_HI			0x0202
+#define REG_COARSE_INTEGRATION_TIME_LO			0x0203
+/* Gain */
+#define REG_ANALOGUE_GAIN_CODE_GLOBAL_HI		0x0204
+#define REG_ANALOGUE_GAIN_CODE_GLOBAL_LO		0x0205
+/* Digital Gain */
+#define REG_DIGITAL_GAIN_GREEN_R_HI			0x020E
+#define REG_DIGITAL_GAIN_GREEN_R_LO			0x020F
+#define REG_DIGITAL_GAIN_RED_HI				0x0210
+#define REG_DIGITAL_GAIN_RED_LO				0x0211
+#define REG_DIGITAL_GAIN_BLUE_HI			0x0212
+#define REG_DIGITAL_GAIN_BLUE_LO			0x0213
+#define REG_DIGITAL_GAIN_GREEN_B_HI			0x0214
+#define REG_DIGITAL_GAIN_GREEN_B_LO			0x0215
+/* output bits setting */
+#define REG_0x0112					0x0112
+#define REG_0x0113					0x0113
+/* PLL registers */
+#define REG_VT_PIX_CLK_DIV				0x0301
+#define REG_PRE_PLL_CLK_DIV				0x0305
+#define REG_PLL_MULTIPLIER				0x0307
+#define REG_OP_PIX_CLK_DIV				0x0309
+#define REG_0x034c					0x034c
+#define REG_0x034d					0x034d
+#define REG_0x034e					0x034e
+#define REG_0x034f					0x034f
+#define REG_0x0387					0x0387
+#define REG_0x0383					0x0383
+#define REG_FRAME_LENGTH_LINES_HI			0x0340
+#define REG_FRAME_LENGTH_LINES_LO			0x0341
+#define REG_LINE_LENGTH_PCK_HI				0x0342
+#define REG_LINE_LENGTH_PCK_LO				0x0343
+#define REG_0x3030					0x3030
+#define REG_0x0111					0x0111
+#define REG_0x0136					0x0136
+#define REG_0x0137					0x0137
+#define REG_0x0b00					0x0b00
+#define REG_0x3001					0x3001
+#define REG_0x3004					0x3004
+#define REG_0x3007					0x3007
+#define REG_0x301a					0x301a
+#define REG_0x3101					0x3101
+#define REG_0x3364					0x3364
+#define REG_0x3365					0x3365
+#define REG_0x0b83					0x0b83
+#define REG_0x0b84					0x0b84
+#define REG_0x0b85					0x0b85
+#define REG_0x0b88					0x0b88
+#define REG_0x0b89					0x0b89
+#define REG_0x0b8a					0x0b8a
+#define REG_0x3005					0x3005
+#define REG_0x3010					0x3010
+#define REG_0x3036					0x3036
+#define REG_0x3041					0x3041
+#define REG_0x0b80					0x0b80
+#define REG_0x0900					0x0900
+#define REG_0x0901					0x0901
+#define REG_0x0902					0x0902
+#define REG_0x3016					0x3016
+#define REG_0x301d					0x301d
+#define REG_0x317e					0x317e
+#define REG_0x317f					0x317f
+#define REG_0x3400					0x3400
+#define REG_0x303a					0x303a
+#define REG_0x1716					0x1716
+#define REG_0x1717					0x1717
+#define REG_0x1718					0x1718
+#define REG_0x1719					0x1719
+#define REG_0x3006					0x3006
+#define REG_0x301b					0x301b
+#define REG_0x3098					0x3098
+#define REG_0x309d					0x309d
+#define REG_0x3011					0x3011
+#define REG_0x3035					0x3035
+#define REG_0x3045					0x3045
+#define REG_0x3210					0x3210
+#define	REG_0x0111					0x0111
+#define REG_0x3410					0x3410
+/* Test Pattern */
+#define REG_TEST_PATTERN_MODE				0x0601
+
+/*============================================================================
+							 TYPE DECLARATIONS
+============================================================================*/
+
+/* 16bit address - 8 bit context register structure */
+#define	VX6953_STM5M0EDOF_OFFSET	9
+#define	Q8		0x00000100
+#define	Q10		0x00000400
+#define	VX6953_STM5M0EDOF_MAX_SNAPSHOT_EXPOSURE_LINE_COUNT	2922
+#define	VX6953_STM5M0EDOF_DEFAULT_MASTER_CLK_RATE	24000000
+#define	VX6953_STM5M0EDOF_OP_PIXEL_CLOCK_RATE	79800000
+#define	VX6953_STM5M0EDOF_VT_PIXEL_CLOCK_RATE	88670000
+/* Full	Size */
+#define	VX6953_FULL_SIZE_WIDTH	2608
+#define	VX6953_FULL_SIZE_HEIGHT		1960
+#define	VX6953_FULL_SIZE_DUMMY_PIXELS	1
+#define	VX6953_FULL_SIZE_DUMMY_LINES	0
+/* Quarter Size	*/
+#define	VX6953_QTR_SIZE_WIDTH	1304
+#define	VX6953_QTR_SIZE_HEIGHT		980
+#define	VX6953_QTR_SIZE_DUMMY_PIXELS	1
+#define	VX6953_QTR_SIZE_DUMMY_LINES		0
+/* Blanking	as measured	on the scope */
+/* Full	Size */
+#define	VX6953_HRZ_FULL_BLK_PIXELS	348
+#define	VX6953_VER_FULL_BLK_LINES	40
+/* Quarter Size	*/
+#define	VX6953_HRZ_QTR_BLK_PIXELS	1628
+#define	VX6953_VER_QTR_BLK_LINES	28
+#define	MAX_LINE_LENGTH_PCK		8190
+#define	VX6953_REVISION_NUMBER_CUT2	0x10/*revision number	for	Cut2.0*/
+#define	VX6953_REVISION_NUMBER_CUT3	0x20/*revision number	for	Cut3.0*/
+/* FIXME: Changes from here */
+struct vx6953_work_t {
+	struct work_struct work;
+};
+
+static struct vx6953_work_t *vx6953_sensorw;
+static struct i2c_client *vx6953_client;
+
+struct vx6953_ctrl_t {
+	const struct  msm_camera_sensor_info *sensordata;
+
+	uint32_t sensormode;
+	uint32_t fps_divider;  /* init to 1 * 0x00000400 */
+	uint32_t pict_fps_divider;  /* init to 1 * 0x00000400 */
+	uint16_t fps;
+
+	int16_t curr_lens_pos;
+	uint16_t curr_step_pos;
+	uint16_t my_reg_gain;
+	uint32_t my_reg_line_count;
+	uint16_t total_lines_per_frame;
+
+	enum vx6953_resolution_t prev_res;
+	enum vx6953_resolution_t pict_res;
+	enum vx6953_resolution_t curr_res;
+	enum vx6953_test_mode_t  set_test;
+	enum sensor_revision_t sensor_type;
+
+	enum edof_mode_t edof_mode;
+
+	unsigned short imgaddr;
+
+	struct v4l2_subdev *sensor_dev;
+	struct vx6953_format *fmt;
+};
+
+
+static uint8_t vx6953_stm5m0edof_delay_msecs_stdby;
+static uint16_t vx6953_stm5m0edof_delay_msecs_stream = 20;
+
+static struct vx6953_ctrl_t *vx6953_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(vx6953_wait_queue);
+DEFINE_MUTEX(vx6953_mut);
+static struct vx6953_i2c_reg_conf patch_tbl_cut2[] = {
+	{0xFB94, 0},	/*intialise Data Xfer Status reg*/
+	{0xFB95, 0},	/*gain 1	  (0x00)*/
+	{0xFB96, 0},	/*gain 1.07   (0x10)*/
+	{0xFB97, 0},	/*gain 1.14   (0x20)*/
+	{0xFB98, 0},	/*gain 1.23   (0x30)*/
+	{0xFB99, 0},	/*gain 1.33   (0x40)*/
+	{0xFB9A, 0},	/*gain 1.45   (0x50)*/
+	{0xFB9B, 0},	/*gain 1.6    (0x60)*/
+	{0xFB9C, 0},	/*gain 1.78   (0x70)*/
+	{0xFB9D, 2},	/*gain 2	  (0x80)*/
+	{0xFB9E, 2},	/*gain 2.29   (0x90)*/
+	{0xFB9F, 3},	/*gain 2.67   (0xA0)*/
+	{0xFBA0, 3},	/*gain 3.2    (0xB0)*/
+	{0xFBA1, 4},	/*gain 4	  (0xC0)*/
+	{0xFBA2, 7},	/*gain 5.33   (0xD0)*/
+	{0xFBA3, 10},	/*gain 8	  (0xE0)*/
+	{0xFBA4, 11},	/*gain 9.14   (0xE4)*/
+	{0xFBA5, 13},	/*gain 10.67  (0xE8)*/
+	{0xFBA6, 15},	/*gain 12.8   (0xEC)*/
+	{0xFBA7, 19},	/*gain 16     (0xF0)*/
+	{0xF800, 0x12},
+	{0xF801, 0x06},
+	{0xF802, 0xf7},
+	{0xF803, 0x90},
+	{0xF804, 0x02},
+	{0xF805, 0x05},
+	{0xF806, 0xe0},
+	{0xF807, 0xff},
+	{0xF808, 0x65},
+	{0xF809, 0x7d},
+	{0xF80A, 0x70},
+	{0xF80B, 0x03},
+	{0xF80C, 0x02},
+	{0xF80D, 0xf9},
+	{0xF80E, 0x1c},
+	{0xF80F, 0x8f},
+	{0xF810, 0x7d},
+	{0xF811, 0xe4},
+	{0xF812, 0xf5},
+	{0xF813, 0x7a},
+	{0xF814, 0x75},
+	{0xF815, 0x78},
+	{0xF816, 0x30},
+	{0xF817, 0x75},
+	{0xF818, 0x79},
+	{0xF819, 0x53},
+	{0xF81A, 0x85},
+	{0xF81B, 0x79},
+	{0xF81C, 0x82},
+	{0xF81D, 0x85},
+	{0xF81E, 0x78},
+	{0xF81F, 0x83},
+	{0xF820, 0xe0},
+	{0xF821, 0xc3},
+	{0xF822, 0x95},
+	{0xF823, 0x7b},
+	{0xF824, 0xf0},
+	{0xF825, 0x74},
+	{0xF826, 0x02},
+	{0xF827, 0x25},
+	{0xF828, 0x79},
+	{0xF829, 0xf5},
+	{0xF82A, 0x79},
+	{0xF82B, 0xe4},
+	{0xF82C, 0x35},
+	{0xF82D, 0x78},
+	{0xF82E, 0xf5},
+	{0xF82F, 0x78},
+	{0xF830, 0x05},
+	{0xF831, 0x7a},
+	{0xF832, 0xe5},
+	{0xF833, 0x7a},
+	{0xF834, 0xb4},
+	{0xF835, 0x08},
+	{0xF836, 0xe3},
+	{0xF837, 0xe5},
+	{0xF838, 0x7d},
+	{0xF839, 0x70},
+	{0xF83A, 0x04},
+	{0xF83B, 0xff},
+	{0xF83C, 0x02},
+	{0xF83D, 0xf8},
+	{0xF83E, 0xe4},
+	{0xF83F, 0xe5},
+	{0xF840, 0x7d},
+	{0xF841, 0xb4},
+	{0xF842, 0x10},
+	{0xF843, 0x05},
+	{0xF844, 0x7f},
+	{0xF845, 0x01},
+	{0xF846, 0x02},
+	{0xF847, 0xf8},
+	{0xF848, 0xe4},
+	{0xF849, 0xe5},
+	{0xF84A, 0x7d},
+	{0xF84B, 0xb4},
+	{0xF84C, 0x20},
+	{0xF84D, 0x05},
+	{0xF84E, 0x7f},
+	{0xF84F, 0x02},
+	{0xF850, 0x02},
+	{0xF851, 0xf8},
+	{0xF852, 0xe4},
+	{0xF853, 0xe5},
+	{0xF854, 0x7d},
+	{0xF855, 0xb4},
+	{0xF856, 0x30},
+	{0xF857, 0x05},
+	{0xF858, 0x7f},
+	{0xF859, 0x03},
+	{0xF85A, 0x02},
+	{0xF85B, 0xf8},
+	{0xF85C, 0xe4},
+	{0xF85D, 0xe5},
+	{0xF85E, 0x7d},
+	{0xF85F, 0xb4},
+	{0xF860, 0x40},
+	{0xF861, 0x04},
+	{0xF862, 0x7f},
+	{0xF863, 0x04},
+	{0xF864, 0x80},
+	{0xF865, 0x7e},
+	{0xF866, 0xe5},
+	{0xF867, 0x7d},
+	{0xF868, 0xb4},
+	{0xF869, 0x50},
+	{0xF86A, 0x04},
+	{0xF86B, 0x7f},
+	{0xF86C, 0x05},
+	{0xF86D, 0x80},
+	{0xF86E, 0x75},
+	{0xF86F, 0xe5},
+	{0xF870, 0x7d},
+	{0xF871, 0xb4},
+	{0xF872, 0x60},
+	{0xF873, 0x04},
+	{0xF874, 0x7f},
+	{0xF875, 0x06},
+	{0xF876, 0x80},
+	{0xF877, 0x6c},
+	{0xF878, 0xe5},
+	{0xF879, 0x7d},
+	{0xF87A, 0xb4},
+	{0xF87B, 0x70},
+	{0xF87C, 0x04},
+	{0xF87D, 0x7f},
+	{0xF87E, 0x07},
+	{0xF87F, 0x80},
+	{0xF880, 0x63},
+	{0xF881, 0xe5},
+	{0xF882, 0x7d},
+	{0xF883, 0xb4},
+	{0xF884, 0x80},
+	{0xF885, 0x04},
+	{0xF886, 0x7f},
+	{0xF887, 0x08},
+	{0xF888, 0x80},
+	{0xF889, 0x5a},
+	{0xF88A, 0xe5},
+	{0xF88B, 0x7d},
+	{0xF88C, 0xb4},
+	{0xF88D, 0x90},
+	{0xF88E, 0x04},
+	{0xF88F, 0x7f},
+	{0xF890, 0x09},
+	{0xF891, 0x80},
+	{0xF892, 0x51},
+	{0xF893, 0xe5},
+	{0xF894, 0x7d},
+	{0xF895, 0xb4},
+	{0xF896, 0xa0},
+	{0xF897, 0x04},
+	{0xF898, 0x7f},
+	{0xF899, 0x0a},
+	{0xF89A, 0x80},
+	{0xF89B, 0x48},
+	{0xF89C, 0xe5},
+	{0xF89D, 0x7d},
+	{0xF89E, 0xb4},
+	{0xF89F, 0xb0},
+	{0xF8A0, 0x04},
+	{0xF8A1, 0x7f},
+	{0xF8A2, 0x0b},
+	{0xF8A3, 0x80},
+	{0xF8A4, 0x3f},
+	{0xF8A5, 0xe5},
+	{0xF8A6, 0x7d},
+	{0xF8A7, 0xb4},
+	{0xF8A8, 0xc0},
+	{0xF8A9, 0x04},
+	{0xF8AA, 0x7f},
+	{0xF8AB, 0x0c},
+	{0xF8AC, 0x80},
+	{0xF8AD, 0x36},
+	{0xF8AE, 0xe5},
+	{0xF8AF, 0x7d},
+	{0xF8B0, 0xb4},
+	{0xF8B1, 0xd0},
+	{0xF8B2, 0x04},
+	{0xF8B3, 0x7f},
+	{0xF8B4, 0x0d},
+	{0xF8B5, 0x80},
+	{0xF8B6, 0x2d},
+	{0xF8B7, 0xe5},
+	{0xF8B8, 0x7d},
+	{0xF8B9, 0xb4},
+	{0xF8BA, 0xe0},
+	{0xF8BB, 0x04},
+	{0xF8BC, 0x7f},
+	{0xF8BD, 0x0e},
+	{0xF8BE, 0x80},
+	{0xF8BF, 0x24},
+	{0xF8C0, 0xe5},
+	{0xF8C1, 0x7d},
+	{0xF8C2, 0xb4},
+	{0xF8C3, 0xe4},
+	{0xF8C4, 0x04},
+	{0xF8C5, 0x7f},
+	{0xF8C6, 0x0f},
+	{0xF8C7, 0x80},
+	{0xF8C8, 0x1b},
+	{0xF8C9, 0xe5},
+	{0xF8CA, 0x7d},
+	{0xF8CB, 0xb4},
+	{0xF8CC, 0xe8},
+	{0xF8CD, 0x04},
+	{0xF8CE, 0x7f},
+	{0xF8CF, 0x10},
+	{0xF8D0, 0x80},
+	{0xF8D1, 0x12},
+	{0xF8D2, 0xe5},
+	{0xF8D3, 0x7d},
+	{0xF8D4, 0xb4},
+	{0xF8D5, 0xec},
+	{0xF8D6, 0x04},
+	{0xF8D7, 0x7f},
+	{0xF8D8, 0x11},
+	{0xF8D9, 0x80},
+	{0xF8DA, 0x09},
+	{0xF8DB, 0xe5},
+	{0xF8DC, 0x7d},
+	{0xF8DD, 0x7f},
+	{0xF8DE, 0x00},
+	{0xF8DF, 0xb4},
+	{0xF8E0, 0xf0},
+	{0xF8E1, 0x02},
+	{0xF8E2, 0x7f},
+	{0xF8E3, 0x12},
+	{0xF8E4, 0x8f},
+	{0xF8E5, 0x7c},
+	{0xF8E6, 0xef},
+	{0xF8E7, 0x24},
+	{0xF8E8, 0x95},
+	{0xF8E9, 0xff},
+	{0xF8EA, 0xe4},
+	{0xF8EB, 0x34},
+	{0xF8EC, 0xfb},
+	{0xF8ED, 0x8f},
+	{0xF8EE, 0x82},
+	{0xF8EF, 0xf5},
+	{0xF8F0, 0x83},
+	{0xF8F1, 0xe4},
+	{0xF8F2, 0x93},
+	{0xF8F3, 0xf5},
+	{0xF8F4, 0x7c},
+	{0xF8F5, 0xf5},
+	{0xF8F6, 0x7b},
+	{0xF8F7, 0xe4},
+	{0xF8F8, 0xf5},
+	{0xF8F9, 0x7a},
+	{0xF8FA, 0x75},
+	{0xF8FB, 0x78},
+	{0xF8FC, 0x30},
+	{0xF8FD, 0x75},
+	{0xF8FE, 0x79},
+	{0xF8FF, 0x53},
+	{0xF900, 0x85},
+	{0xF901, 0x79},
+	{0xF902, 0x82},
+	{0xF903, 0x85},
+	{0xF904, 0x78},
+	{0xF905, 0x83},
+	{0xF906, 0xe0},
+	{0xF907, 0x25},
+	{0xF908, 0x7c},
+	{0xF909, 0xf0},
+	{0xF90A, 0x74},
+	{0xF90B, 0x02},
+	{0xF90C, 0x25},
+	{0xF90D, 0x79},
+	{0xF90E, 0xf5},
+	{0xF90F, 0x79},
+	{0xF910, 0xe4},
+	{0xF911, 0x35},
+	{0xF912, 0x78},
+	{0xF913, 0xf5},
+	{0xF914, 0x78},
+	{0xF915, 0x05},
+	{0xF916, 0x7a},
+	{0xF917, 0xe5},
+	{0xF918, 0x7a},
+	{0xF919, 0xb4},
+	{0xF91A, 0x08},
+	{0xF91B, 0xe4},
+	{0xF91C, 0x02},
+	{0xF91D, 0x18},
+	{0xF91E, 0x32},
+	{0xF91F, 0x22},
+	{0xF920, 0xf0},
+	{0xF921, 0x90},
+	{0xF922, 0xa0},
+	{0xF923, 0xf8},
+	{0xF924, 0xe0},
+	{0xF925, 0x70},
+	{0xF926, 0x02},
+	{0xF927, 0xa3},
+	{0xF928, 0xe0},
+	{0xF929, 0x70},
+	{0xF92A, 0x0a},
+	{0xF92B, 0x90},
+	{0xF92C, 0xa1},
+	{0xF92D, 0x10},
+	{0xF92E, 0xe0},
+	{0xF92F, 0xfe},
+	{0xF930, 0xa3},
+	{0xF931, 0xe0},
+	{0xF932, 0xff},
+	{0xF933, 0x80},
+	{0xF934, 0x04},
+	{0xF935, 0x7e},
+	{0xF936, 0x00},
+	{0xF937, 0x7f},
+	{0xF938, 0x00},
+	{0xF939, 0x8e},
+	{0xF93A, 0x7e},
+	{0xF93B, 0x8f},
+	{0xF93C, 0x7f},
+	{0xF93D, 0x90},
+	{0xF93E, 0x36},
+	{0xF93F, 0x0d},
+	{0xF940, 0xe0},
+	{0xF941, 0x44},
+	{0xF942, 0x02},
+	{0xF943, 0xf0},
+	{0xF944, 0x90},
+	{0xF945, 0x36},
+	{0xF946, 0x0e},
+	{0xF947, 0xe5},
+	{0xF948, 0x7e},
+	{0xF949, 0xf0},
+	{0xF94A, 0xa3},
+	{0xF94B, 0xe5},
+	{0xF94C, 0x7f},
+	{0xF94D, 0xf0},
+	{0xF94E, 0xe5},
+	{0xF94F, 0x3a},
+	{0xF950, 0x60},
+	{0xF951, 0x0c},
+	{0xF952, 0x90},
+	{0xF953, 0x36},
+	{0xF954, 0x09},
+	{0xF955, 0xe0},
+	{0xF956, 0x70},
+	{0xF957, 0x06},
+	{0xF958, 0x90},
+	{0xF959, 0x36},
+	{0xF95A, 0x08},
+	{0xF95B, 0xf0},
+	{0xF95C, 0xf5},
+	{0xF95D, 0x3a},
+	{0xF95E, 0x02},
+	{0xF95F, 0x03},
+	{0xF960, 0x94},
+	{0xF961, 0x22},
+	{0xF962, 0x78},
+	{0xF963, 0x07},
+	{0xF964, 0xe6},
+	{0xF965, 0xd3},
+	{0xF966, 0x94},
+	{0xF967, 0x00},
+	{0xF968, 0x40},
+	{0xF969, 0x16},
+	{0xF96A, 0x16},
+	{0xF96B, 0xe6},
+	{0xF96C, 0x90},
+	{0xF96D, 0x30},
+	{0xF96E, 0xa1},
+	{0xF96F, 0xf0},
+	{0xF970, 0x90},
+	{0xF971, 0x43},
+	{0xF972, 0x83},
+	{0xF973, 0xe0},
+	{0xF974, 0xb4},
+	{0xF975, 0x01},
+	{0xF976, 0x0f},
+	{0xF977, 0x90},
+	{0xF978, 0x43},
+	{0xF979, 0x87},
+	{0xF97A, 0xe0},
+	{0xF97B, 0xb4},
+	{0xF97C, 0x01},
+	{0xF97D, 0x08},
+	{0xF97E, 0x80},
+	{0xF97F, 0x00},
+	{0xF980, 0x90},
+	{0xF981, 0x30},
+	{0xF982, 0xa0},
+	{0xF983, 0x74},
+	{0xF984, 0x01},
+	{0xF985, 0xf0},
+	{0xF986, 0x22},
+	{0xF987, 0xf0},
+	{0xF988, 0x90},
+	{0xF989, 0x35},
+	{0xF98A, 0xba},
+	{0xF98B, 0xe0},
+	{0xF98C, 0xb4},
+	{0xF98D, 0x0a},
+	{0xF98E, 0x0d},
+	{0xF98F, 0xa3},
+	{0xF990, 0xe0},
+	{0xF991, 0xb4},
+	{0xF992, 0x01},
+	{0xF993, 0x08},
+	{0xF994, 0x90},
+	{0xF995, 0xfb},
+	{0xF996, 0x94},
+	{0xF997, 0xe0},
+	{0xF998, 0x90},
+	{0xF999, 0x35},
+	{0xF99A, 0xb8},
+	{0xF99B, 0xf0},
+	{0xF99C, 0xd0},
+	{0xF99D, 0xd0},
+	{0xF99E, 0xd0},
+	{0xF99F, 0x82},
+	{0xF9A0, 0xd0},
+	{0xF9A1, 0x83},
+	{0xF9A2, 0xd0},
+	{0xF9A3, 0xe0},
+	{0xF9A4, 0x32},
+	{0xF9A5, 0x22},
+	{0xF9A6, 0xe5},
+	{0xF9A7, 0x7f},
+	{0xF9A8, 0x45},
+	{0xF9A9, 0x7e},
+	{0xF9AA, 0x60},
+	{0xF9AB, 0x15},
+	{0xF9AC, 0x90},
+	{0xF9AD, 0x01},
+	{0xF9AE, 0x00},
+	{0xF9AF, 0xe0},
+	{0xF9B0, 0x70},
+	{0xF9B1, 0x0f},
+	{0xF9B2, 0x90},
+	{0xF9B3, 0xa0},
+	{0xF9B4, 0xf8},
+	{0xF9B5, 0xe5},
+	{0xF9B6, 0x7e},
+	{0xF9B7, 0xf0},
+	{0xF9B8, 0xa3},
+	{0xF9B9, 0xe5},
+	{0xF9BA, 0x7f},
+	{0xF9BB, 0xf0},
+	{0xF9BC, 0xe4},
+	{0xF9BD, 0xf5},
+	{0xF9BE, 0x7e},
+	{0xF9BF, 0xf5},
+	{0xF9C0, 0x7f},
+	{0xF9C1, 0x22},
+	{0xF9C2, 0x02},
+	{0xF9C3, 0x0e},
+	{0xF9C4, 0x79},
+	{0xF9C5, 0x22},
+	/* Offsets:*/
+	{0x35C6, 0x00},/* FIDDLEDARKCAL*/
+	{0x35C7, 0x00},
+	{0x35C8, 0x01},/*STOREDISTANCEATSTOPSTREAMING*/
+	{0x35C9, 0x20},
+	{0x35CA, 0x01},/*BRUCEFIX*/
+	{0x35CB, 0x62},
+	{0x35CC, 0x01},/*FIXDATAXFERSTATUSREG*/
+	{0x35CD, 0x87},
+	{0x35CE, 0x01},/*FOCUSDISTANCEUPDATE*/
+	{0x35CF, 0xA6},
+	{0x35D0, 0x01},/*SKIPEDOFRESET*/
+	{0x35D1, 0xC2},
+	{0x35D2, 0x00},
+	{0x35D3, 0xFB},
+	{0x35D4, 0x00},
+	{0x35D5, 0x94},
+	{0x35D6, 0x00},
+	{0x35D7, 0xFB},
+	{0x35D8, 0x00},
+	{0x35D9, 0x94},
+	{0x35DA, 0x00},
+	{0x35DB, 0xFB},
+	{0x35DC, 0x00},
+	{0x35DD, 0x94},
+	{0x35DE, 0x00},
+	{0x35DF, 0xFB},
+	{0x35E0, 0x00},
+	{0x35E1, 0x94},
+	{0x35E6, 0x18},/* FIDDLEDARKCAL*/
+	{0x35E7, 0x2F},
+	{0x35E8, 0x03},/* STOREDISTANCEATSTOPSTREAMING*/
+	{0x35E9, 0x93},
+	{0x35EA, 0x18},/* BRUCEFIX*/
+	{0x35EB, 0x99},
+	{0x35EC, 0x00},/* FIXDATAXFERSTATUSREG*/
+	{0x35ED, 0xA3},
+	{0x35EE, 0x21},/* FOCUSDISTANCEUPDATE*/
+	{0x35EF, 0x5B},
+	{0x35F0, 0x0E},/* SKIPEDOFRESET*/
+	{0x35F1, 0x74},
+	{0x35F2, 0x04},
+	{0x35F3, 0x64},
+	{0x35F4, 0x04},
+	{0x35F5, 0x65},
+	{0x35F6, 0x04},
+	{0x35F7, 0x7B},
+	{0x35F8, 0x04},
+	{0x35F9, 0x7C},
+	{0x35FA, 0x04},
+	{0x35FB, 0xDD},
+	{0x35FC, 0x04},
+	{0x35FD, 0xDE},
+	{0x35FE, 0x04},
+	{0x35FF, 0xEF},
+	{0x3600, 0x04},
+	{0x3601, 0xF0},
+	/*Jump/Data:*/
+	{0x35C2, 0x3F},/* Jump Reg*/
+	{0x35C3, 0xFF},/* Jump Reg*/
+	{0x35C4, 0x3F},/* Data Reg*/
+	{0x35C5, 0xC0},/* Data Reg*/
+	{0x35C0, 0x01},/* Enable*/
+
+};
+
+static struct vx6953_i2c_reg_conf edof_tbl[] = {
+	{0xa098, 0x02},
+	{0xa099, 0x87},
+	{0xa09c, 0x00},
+	{0xa09d, 0xc5},
+	{0xa4ec, 0x05},
+	{0xa4ed, 0x05},
+	{0xa4f0, 0x04},
+	{0xa4f1, 0x04},
+	{0xa4f4, 0x04},
+	{0xa4f5, 0x05},
+	{0xa4f8, 0x05},
+	{0xa4f9, 0x07},
+	{0xa4fc, 0x07},
+	{0xa4fd, 0x07},
+	{0xa500, 0x07},
+	{0xa501, 0x07},
+	{0xa504, 0x08},
+	{0xa505, 0x08},
+	{0xa518, 0x01},
+	{0xa519, 0x02},
+	{0xa51c, 0x01},
+	{0xa51d, 0x00},
+	{0xa534, 0x00},
+	{0xa535, 0x04},
+	{0xa538, 0x04},
+	{0xa539, 0x03},
+	{0xa53c, 0x05},
+	{0xa53d, 0x07},
+	{0xa540, 0x07},
+	{0xa541, 0x06},
+	{0xa544, 0x07},
+	{0xa545, 0x06},
+	{0xa548, 0x05},
+	{0xa549, 0x06},
+	{0xa54c, 0x06},
+	{0xa54d, 0x07},
+	{0xa550, 0x07},
+	{0xa551, 0x04},
+	{0xa554, 0x04},
+	{0xa555, 0x04},
+	{0xa558, 0x05},
+	{0xa559, 0x06},
+	{0xa55c, 0x07},
+	{0xa55d, 0x07},
+	{0xa56c, 0x00},
+	{0xa56d, 0x0a},
+	{0xa570, 0x08},
+	{0xa571, 0x05},
+	{0xa574, 0x04},
+	{0xa575, 0x03},
+	{0xa578, 0x04},
+	{0xa579, 0x04},
+	{0xa58c, 0x1f},
+	{0xa58d, 0x1b},
+	{0xa590, 0x17},
+	{0xa591, 0x13},
+	{0xa594, 0x10},
+	{0xa595, 0x0d},
+	{0xa598, 0x0f},
+	{0xa599, 0x11},
+	{0xa59c, 0x03},
+	{0xa59d, 0x03},
+	{0xa5a0, 0x03},
+	{0xa5a1, 0x03},
+	{0xa5a4, 0x03},
+	{0xa5a5, 0x04},
+	{0xa5a8, 0x05},
+	{0xa5a9, 0x00},
+	{0xa5ac, 0x00},
+	{0xa5ad, 0x00},
+	{0xa5b0, 0x00},
+	{0xa5b1, 0x00},
+	{0xa5b4, 0x00},
+	{0xa5b5, 0x00},
+	{0xa5c4, 0x1f},
+	{0xa5c5, 0x13},
+	{0xa5c8, 0x14},
+	{0xa5c9, 0x14},
+	{0xa5cc, 0x14},
+	{0xa5cd, 0x13},
+	{0xa5d0, 0x17},
+	{0xa5d1, 0x1a},
+	{0xa5f4, 0x05},
+	{0xa5f5, 0x05},
+	{0xa5f8, 0x05},
+	{0xa5f9, 0x06},
+	{0xa5fc, 0x06},
+	{0xa5fd, 0x06},
+	{0xa600, 0x06},
+	{0xa601, 0x06},
+	{0xa608, 0x07},
+	{0xa609, 0x08},
+	{0xa60c, 0x08},
+	{0xa60d, 0x07},
+	{0xa63c, 0x00},
+	{0xa63d, 0x02},
+	{0xa640, 0x02},
+	{0xa641, 0x02},
+	{0xa644, 0x02},
+	{0xa645, 0x02},
+	{0xa648, 0x03},
+	{0xa649, 0x04},
+	{0xa64c, 0x0a},
+	{0xa64d, 0x09},
+	{0xa650, 0x08},
+	{0xa651, 0x09},
+	{0xa654, 0x09},
+	{0xa655, 0x0a},
+	{0xa658, 0x0a},
+	{0xa659, 0x0a},
+	{0xa65c, 0x0a},
+	{0xa65d, 0x09},
+	{0xa660, 0x09},
+	{0xa661, 0x09},
+	{0xa664, 0x09},
+	{0xa665, 0x08},
+	{0xa680, 0x01},
+	{0xa681, 0x02},
+	{0xa694, 0x1f},
+	{0xa695, 0x10},
+	{0xa698, 0x0e},
+	{0xa699, 0x0c},
+	{0xa69c, 0x0d},
+	{0xa69d, 0x0d},
+	{0xa6a0, 0x0f},
+	{0xa6a1, 0x11},
+	{0xa6a4, 0x00},
+	{0xa6a5, 0x00},
+	{0xa6a8, 0x00},
+	{0xa6a9, 0x00},
+	{0xa6ac, 0x00},
+	{0xa6ad, 0x00},
+	{0xa6b0, 0x00},
+	{0xa6b1, 0x04},
+	{0xa6b4, 0x04},
+	{0xa6b5, 0x04},
+	{0xa6b8, 0x04},
+	{0xa6b9, 0x04},
+	{0xa6bc, 0x05},
+	{0xa6bd, 0x05},
+	{0xa6c0, 0x1f},
+	{0xa6c1, 0x1f},
+	{0xa6c4, 0x1f},
+	{0xa6c5, 0x1f},
+	{0xa6c8, 0x1f},
+	{0xa6c9, 0x1f},
+	{0xa6cc, 0x1f},
+	{0xa6cd, 0x0b},
+	{0xa6d0, 0x0c},
+	{0xa6d1, 0x0d},
+	{0xa6d4, 0x0d},
+	{0xa6d5, 0x0d},
+	{0xa6d8, 0x11},
+	{0xa6d9, 0x14},
+	{0xa6fc, 0x02},
+	{0xa6fd, 0x03},
+	{0xa700, 0x03},
+	{0xa701, 0x03},
+	{0xa704, 0x03},
+	{0xa705, 0x04},
+	{0xa708, 0x05},
+	{0xa709, 0x02},
+	{0xa70c, 0x02},
+	{0xa70d, 0x02},
+	{0xa710, 0x03},
+	{0xa711, 0x04},
+	{0xa714, 0x04},
+	{0xa715, 0x04},
+	{0xa744, 0x00},
+	{0xa745, 0x03},
+	{0xa748, 0x04},
+	{0xa749, 0x04},
+	{0xa74c, 0x05},
+	{0xa74d, 0x06},
+	{0xa750, 0x07},
+	{0xa751, 0x07},
+	{0xa754, 0x05},
+	{0xa755, 0x05},
+	{0xa758, 0x05},
+	{0xa759, 0x05},
+	{0xa75c, 0x05},
+	{0xa75d, 0x06},
+	{0xa760, 0x07},
+	{0xa761, 0x07},
+	{0xa764, 0x06},
+	{0xa765, 0x05},
+	{0xa768, 0x05},
+	{0xa769, 0x05},
+	{0xa76c, 0x06},
+	{0xa76d, 0x07},
+	{0xa77c, 0x00},
+	{0xa77d, 0x05},
+	{0xa780, 0x05},
+	{0xa781, 0x05},
+	{0xa784, 0x05},
+	{0xa785, 0x04},
+	{0xa788, 0x05},
+	{0xa789, 0x06},
+	{0xa79c, 0x1f},
+	{0xa79d, 0x15},
+	{0xa7a0, 0x13},
+	{0xa7a1, 0x10},
+	{0xa7a4, 0x0f},
+	{0xa7a5, 0x0d},
+	{0xa7a8, 0x11},
+	{0xa7a9, 0x14},
+	{0xa7ac, 0x02},
+	{0xa7ad, 0x02},
+	{0xa7b0, 0x02},
+	{0xa7b1, 0x02},
+	{0xa7b4, 0x02},
+	{0xa7b5, 0x03},
+	{0xa7b8, 0x03},
+	{0xa7b9, 0x00},
+	{0xa7bc, 0x00},
+	{0xa7bd, 0x00},
+	{0xa7c0, 0x00},
+	{0xa7c1, 0x00},
+	{0xa7c4, 0x00},
+	{0xa7c5, 0x00},
+	{0xa7d4, 0x1f},
+	{0xa7d5, 0x0d},
+	{0xa7d8, 0x0f},
+	{0xa7d9, 0x10},
+	{0xa7dc, 0x10},
+	{0xa7dd, 0x10},
+	{0xa7e0, 0x13},
+	{0xa7e1, 0x16},
+	{0xa7f4, 0x00},
+	{0xa7f5, 0x03},
+	{0xa7f8, 0x04},
+	{0xa7f9, 0x04},
+	{0xa7fc, 0x04},
+	{0xa7fd, 0x03},
+	{0xa800, 0x03},
+	{0xa801, 0x03},
+	{0xa804, 0x03},
+	{0xa805, 0x03},
+	{0xa808, 0x03},
+	{0xa809, 0x03},
+	{0xa80c, 0x03},
+	{0xa80d, 0x04},
+	{0xa810, 0x04},
+	{0xa811, 0x0a},
+	{0xa814, 0x0a},
+	{0xa815, 0x0a},
+	{0xa818, 0x0f},
+	{0xa819, 0x14},
+	{0xa81c, 0x14},
+	{0xa81d, 0x14},
+	{0xa82c, 0x00},
+	{0xa82d, 0x04},
+	{0xa830, 0x02},
+	{0xa831, 0x00},
+	{0xa834, 0x00},
+	{0xa835, 0x00},
+	{0xa838, 0x00},
+	{0xa839, 0x00},
+	{0xa840, 0x1f},
+	{0xa841, 0x1f},
+	{0xa848, 0x1f},
+	{0xa849, 0x1f},
+	{0xa84c, 0x1f},
+	{0xa84d, 0x0c},
+	{0xa850, 0x0c},
+	{0xa851, 0x0c},
+	{0xa854, 0x0c},
+	{0xa855, 0x0c},
+	{0xa858, 0x0c},
+	{0xa859, 0x0c},
+	{0xa85c, 0x0c},
+	{0xa85d, 0x0c},
+	{0xa860, 0x0c},
+	{0xa861, 0x0c},
+	{0xa864, 0x0c},
+	{0xa865, 0x0c},
+	{0xa868, 0x0c},
+	{0xa869, 0x0c},
+	{0xa86c, 0x0c},
+	{0xa86d, 0x0c},
+	{0xa870, 0x0c},
+	{0xa871, 0x0c},
+	{0xa874, 0x0c},
+	{0xa875, 0x0c},
+	{0xa878, 0x1f},
+	{0xa879, 0x1f},
+	{0xa87c, 0x1f},
+	{0xa87d, 0x1f},
+	{0xa880, 0x1f},
+	{0xa881, 0x1f},
+	{0xa884, 0x1f},
+	{0xa885, 0x0c},
+	{0xa888, 0x0c},
+	{0xa889, 0x0c},
+	{0xa88c, 0x0c},
+	{0xa88d, 0x0c},
+	{0xa890, 0x0c},
+	{0xa891, 0x0c},
+	{0xa898, 0x1f},
+	{0xa899, 0x1f},
+	{0xa8a0, 0x1f},
+	{0xa8a1, 0x1f},
+	{0xa8a4, 0x1f},
+	{0xa8a5, 0x0c},
+	{0xa8a8, 0x0c},
+	{0xa8a9, 0x0c},
+	{0xa8ac, 0x0c},
+	{0xa8ad, 0x0c},
+	{0xa8b0, 0x0c},
+	{0xa8b1, 0x0c},
+	{0xa8b4, 0x0c},
+	{0xa8b5, 0x0c},
+	{0xa8b8, 0x0c},
+	{0xa8b9, 0x0c},
+	{0xa8bc, 0x0c},
+	{0xa8bd, 0x0c},
+	{0xa8c0, 0x0c},
+	{0xa8c1, 0x0c},
+	{0xa8c4, 0x0c},
+	{0xa8c5, 0x0c},
+	{0xa8c8, 0x0c},
+	{0xa8c9, 0x0c},
+	{0xa8cc, 0x0c},
+	{0xa8cd, 0x0c},
+	{0xa8d0, 0x1f},
+	{0xa8d1, 0x1f},
+	{0xa8d4, 0x1f},
+	{0xa8d5, 0x1f},
+	{0xa8d8, 0x1f},
+	{0xa8d9, 0x1f},
+	{0xa8dc, 0x1f},
+	{0xa8dd, 0x0c},
+	{0xa8e0, 0x0c},
+	{0xa8e1, 0x0c},
+	{0xa8e4, 0x0c},
+	{0xa8e5, 0x0c},
+	{0xa8e8, 0x0c},
+	{0xa8e9, 0x0c},
+	{0xa8f0, 0x1f},
+	{0xa8f1, 0x1f},
+	{0xa8f8, 0x1f},
+	{0xa8f9, 0x1f},
+	{0xa8fc, 0x1f},
+	{0xa8fd, 0x0c},
+	{0xa900, 0x0c},
+	{0xa901, 0x0c},
+	{0xa904, 0x0c},
+	{0xa905, 0x0c},
+	{0xa908, 0x0c},
+	{0xa909, 0x0c},
+	{0xa90c, 0x0c},
+	{0xa90d, 0x0c},
+	{0xa910, 0x0c},
+	{0xa911, 0x0c},
+	{0xa914, 0x0c},
+	{0xa915, 0x0c},
+	{0xa918, 0x0c},
+	{0xa919, 0x0c},
+	{0xa91c, 0x0c},
+	{0xa91d, 0x0c},
+	{0xa920, 0x0c},
+	{0xa921, 0x0c},
+	{0xa924, 0x0c},
+	{0xa925, 0x0c},
+	{0xa928, 0x1f},
+	{0xa929, 0x1f},
+	{0xa92c, 0x1f},
+	{0xa92d, 0x1f},
+	{0xa930, 0x1f},
+	{0xa931, 0x1f},
+	{0xa934, 0x1f},
+	{0xa935, 0x0c},
+	{0xa938, 0x0c},
+	{0xa939, 0x0c},
+	{0xa93c, 0x0c},
+	{0xa93d, 0x0c},
+	{0xa940, 0x0c},
+	{0xa941, 0x0c},
+	{0xa96c, 0x0d},
+	{0xa96d, 0x16},
+	{0xa970, 0x19},
+	{0xa971, 0x0e},
+	{0xa974, 0x16},
+	{0xa975, 0x1a},
+	{0xa978, 0x0d},
+	{0xa979, 0x15},
+	{0xa97c, 0x19},
+	{0xa97d, 0x0d},
+	{0xa980, 0x15},
+	{0xa981, 0x1a},
+	{0xa984, 0x0d},
+	{0xa985, 0x15},
+	{0xa988, 0x1a},
+	{0xa989, 0x0d},
+	{0xa98c, 0x15},
+	{0xa98d, 0x1a},
+	{0xa990, 0x0b},
+	{0xa991, 0x11},
+	{0xa994, 0x02},
+	{0xa995, 0x0e},
+	{0xa998, 0x16},
+	{0xa999, 0x02},
+	{0xa99c, 0x0c},
+	{0xa99d, 0x13},
+	{0xa9a0, 0x02},
+	{0xa9a1, 0x0c},
+	{0xa9a4, 0x12},
+	{0xa9a5, 0x02},
+	{0xa9a8, 0x0c},
+	{0xa9a9, 0x12},
+	{0xa9ac, 0x02},
+	{0xa9ad, 0x0c},
+	{0xa9b0, 0x12},
+	{0xa9b1, 0x02},
+	{0xa9b4, 0x10},
+	{0xa9b5, 0x1e},
+	{0xa9b8, 0x0f},
+	{0xa9b9, 0x13},
+	{0xa9bc, 0x20},
+	{0xa9bd, 0x10},
+	{0xa9c0, 0x11},
+	{0xa9c1, 0x1e},
+	{0xa9c4, 0x10},
+	{0xa9c5, 0x11},
+	{0xa9c8, 0x1e},
+	{0xa9c9, 0x10},
+	{0xa9cc, 0x11},
+	{0xa9cd, 0x20},
+	{0xa9d0, 0x10},
+	{0xa9d1, 0x13},
+	{0xa9d4, 0x24},
+	{0xa9d5, 0x10},
+	{0xa9f0, 0x02},
+	{0xa9f1, 0x01},
+	{0xa9f8, 0x19},
+	{0xa9f9, 0x0b},
+	{0xa9fc, 0x0a},
+	{0xa9fd, 0x07},
+	{0xaa00, 0x0c},
+	{0xaa01, 0x0e},
+	{0xaa08, 0x0c},
+	{0xaa09, 0x06},
+	{0xaa0c, 0x0c},
+	{0xaa0d, 0x0a},
+	{0xaa24, 0x10},
+	{0xaa25, 0x12},
+	{0xaa28, 0x0b},
+	{0xaa29, 0x07},
+	{0xaa2c, 0x10},
+	{0xaa2d, 0x14},
+	{0xaa34, 0x0e},
+	{0xaa35, 0x0e},
+	{0xaa38, 0x07},
+	{0xaa39, 0x07},
+	{0xaa3c, 0x0e},
+	{0xaa3d, 0x0c},
+	{0xaa48, 0x09},
+	{0xaa49, 0x0c},
+	{0xaa4c, 0x0c},
+	{0xaa4d, 0x07},
+	{0xaa54, 0x08},
+	{0xaa55, 0x06},
+	{0xaa58, 0x04},
+	{0xaa59, 0x05},
+	{0xaa5c, 0x06},
+	{0xaa5d, 0x06},
+	{0xaa68, 0x05},
+	{0xaa69, 0x05},
+	{0xaa6c, 0x04},
+	{0xaa6d, 0x05},
+	{0xaa74, 0x06},
+	{0xaa75, 0x04},
+	{0xaa78, 0x05},
+	{0xaa79, 0x05},
+	{0xaa7c, 0x04},
+	{0xaa7d, 0x06},
+	{0xac18, 0x14},
+	{0xac19, 0x00},
+	{0xac1c, 0x14},
+	{0xac1d, 0x00},
+	{0xac20, 0x14},
+	{0xac21, 0x00},
+	{0xac24, 0x14},
+	{0xac25, 0x00},
+	{0xac28, 0x14},
+	{0xac29, 0x00},
+	{0xac2c, 0x14},
+	{0xac2d, 0x00},
+	{0xac34, 0x16},
+	{0xac35, 0x00},
+	{0xac38, 0x16},
+	{0xac39, 0x00},
+	{0xac3c, 0x16},
+	{0xac3d, 0x00},
+	{0xac40, 0x16},
+	{0xac41, 0x00},
+	{0xac44, 0x16},
+	{0xac45, 0x00},
+	{0xac48, 0x16},
+	{0xac49, 0x00},
+	{0xac50, 0x1b},
+	{0xac51, 0x00},
+	{0xac54, 0x1b},
+	{0xac55, 0x00},
+	{0xac58, 0x1b},
+	{0xac59, 0x00},
+	{0xac5c, 0x1b},
+	{0xac5d, 0x00},
+	{0xac60, 0x1b},
+	{0xac61, 0x00},
+	{0xac64, 0x1b},
+	{0xac65, 0x00},
+	{0xac74, 0x09},
+	{0xac75, 0x0c},
+	{0xac78, 0x0f},
+	{0xac79, 0x11},
+	{0xac7c, 0x12},
+	{0xac7d, 0x14},
+	{0xac80, 0x09},
+	{0xac81, 0x0c},
+	{0xac84, 0x0f},
+	{0xac85, 0x11},
+	{0xac88, 0x12},
+	{0xac89, 0x14},
+	{0xac8c, 0x09},
+	{0xac8d, 0x0c},
+	{0xac90, 0x0f},
+	{0xac91, 0x11},
+	{0xac94, 0x12},
+	{0xac95, 0x14},
+	{0xac98, 0x09},
+	{0xac99, 0x0c},
+	{0xac9c, 0x0f},
+	{0xac9d, 0x11},
+	{0xaca0, 0x12},
+	{0xaca1, 0x14},
+	{0xaca4, 0x09},
+	{0xaca5, 0x0c},
+	{0xaca8, 0x0f},
+	{0xaca9, 0x11},
+	{0xacac, 0x12},
+	{0xacad, 0x14},
+	{0xacb0, 0x07},
+	{0xacb1, 0x09},
+	{0xacb4, 0x0c},
+	{0xacb5, 0x0d},
+	{0xacb8, 0x0d},
+	{0xacb9, 0x0e},
+	{0xacbc, 0x05},
+	{0xacbd, 0x07},
+	{0xacc0, 0x0a},
+	{0xacc1, 0x0b},
+	{0xacc4, 0x0b},
+	{0xacc5, 0x0c},
+	{0xacc8, 0x03},
+	{0xacc9, 0x04},
+	{0xaccc, 0x07},
+	{0xaccd, 0x08},
+	{0xacd0, 0x09},
+	{0xacd1, 0x09}
+};
+
+static struct vx6953_i2c_reg_conf patch_tbl_cut3[] = {
+	{0xF800, 0x90},
+	{0xF801, 0x30},
+	{0xF802, 0x31},
+	{0xF803, 0xe0},
+	{0xF804, 0xf5},
+	{0xF805, 0x7d},
+	{0xF806, 0xb4},
+	{0xF807, 0x01},
+	{0xF808, 0x06},
+	{0xF809, 0x75},
+	{0xF80A, 0x7d},
+	{0xF80B, 0x03},
+	{0xF80C, 0x74},
+	{0xF80D, 0x03},
+	{0xF80E, 0xf0},
+	{0xF80F, 0x90},
+	{0xF810, 0x30},
+	{0xF811, 0x04},
+	{0xF812, 0x74},
+	{0xF813, 0x33},
+	{0xF814, 0xf0},
+	{0xF815, 0x90},
+	{0xF816, 0x30},
+	{0xF817, 0x06},
+	{0xF818, 0xe4},
+	{0xF819, 0xf0},
+	{0xF81A, 0xa3},
+	{0xF81B, 0x74},
+	{0xF81C, 0x09},
+	{0xF81D, 0xf0},
+	{0xF81E, 0x90},
+	{0xF81F, 0x30},
+	{0xF820, 0x10},
+	{0xF821, 0xe4},
+	{0xF822, 0xf0},
+	{0xF823, 0xa3},
+	{0xF824, 0xf0},
+	{0xF825, 0x90},
+	{0xF826, 0x30},
+	{0xF827, 0x16},
+	{0xF828, 0x74},
+	{0xF829, 0x1e},
+	{0xF82A, 0xf0},
+	{0xF82B, 0x90},
+	{0xF82C, 0x30},
+	{0xF82D, 0x1a},
+	{0xF82E, 0x74},
+	{0xF82F, 0x6a},
+	{0xF830, 0xf0},
+	{0xF831, 0xa3},
+	{0xF832, 0x74},
+	{0xF833, 0x29},
+	{0xF834, 0xf0},
+	{0xF835, 0x90},
+	{0xF836, 0x30},
+	{0xF837, 0x30},
+	{0xF838, 0x74},
+	{0xF839, 0x08},
+	{0xF83A, 0xf0},
+	{0xF83B, 0x90},
+	{0xF83C, 0x30},
+	{0xF83D, 0x36},
+	{0xF83E, 0x74},
+	{0xF83F, 0x2c},
+	{0xF840, 0xf0},
+	{0xF841, 0x90},
+	{0xF842, 0x30},
+	{0xF843, 0x41},
+	{0xF844, 0xe4},
+	{0xF845, 0xf0},
+	{0xF846, 0xa3},
+	{0xF847, 0x74},
+	{0xF848, 0x24},
+	{0xF849, 0xf0},
+	{0xF84A, 0x90},
+	{0xF84B, 0x30},
+	{0xF84C, 0x45},
+	{0xF84D, 0x74},
+	{0xF84E, 0x81},
+	{0xF84F, 0xf0},
+	{0xF850, 0x90},
+	{0xF851, 0x30},
+	{0xF852, 0x98},
+	{0xF853, 0x74},
+	{0xF854, 0x01},
+	{0xF855, 0xf0},
+	{0xF856, 0x90},
+	{0xF857, 0x30},
+	{0xF858, 0x9d},
+	{0xF859, 0x74},
+	{0xF85A, 0x05},
+	{0xF85B, 0xf0},
+	{0xF85C, 0xe5},
+	{0xF85D, 0x7d},
+	{0xF85E, 0x70},
+	{0xF85F, 0x10},
+	{0xF860, 0x90},
+	{0xF861, 0x30},
+	{0xF862, 0x05},
+	{0xF863, 0x04},
+	{0xF864, 0xf0},
+	{0xF865, 0x90},
+	{0xF866, 0x30},
+	{0xF867, 0x30},
+	{0xF868, 0xe4},
+	{0xF869, 0xf0},
+	{0xF86A, 0x90},
+	{0xF86B, 0x30},
+	{0xF86C, 0x35},
+	{0xF86D, 0x04},
+	{0xF86E, 0xf0},
+	{0xF86F, 0x22},
+	{0xF870, 0xe5},
+	{0xF871, 0x7d},
+	{0xF872, 0x64},
+	{0xF873, 0x02},
+	{0xF874, 0x70},
+	{0xF875, 0x2d},
+	{0xF876, 0x90},
+	{0xF877, 0x30},
+	{0xF878, 0x04},
+	{0xF879, 0x74},
+	{0xF87A, 0x34},
+	{0xF87B, 0xf0},
+	{0xF87C, 0xa3},
+	{0xF87D, 0x74},
+	{0xF87E, 0x07},
+	{0xF87F, 0xf0},
+	{0xF880, 0x90},
+	{0xF881, 0x30},
+	{0xF882, 0x10},
+	{0xF883, 0x74},
+	{0xF884, 0x10},
+	{0xF885, 0xf0},
+	{0xF886, 0x90},
+	{0xF887, 0x30},
+	{0xF888, 0x16},
+	{0xF889, 0x74},
+	{0xF88A, 0x1f},
+	{0xF88B, 0xf0},
+	{0xF88C, 0x90},
+	{0xF88D, 0x30},
+	{0xF88E, 0x1a},
+	{0xF88F, 0x74},
+	{0xF890, 0x62},
+	{0xF891, 0xf0},
+	{0xF892, 0x90},
+	{0xF893, 0x30},
+	{0xF894, 0x35},
+	{0xF895, 0x74},
+	{0xF896, 0x04},
+	{0xF897, 0xf0},
+	{0xF898, 0x90},
+	{0xF899, 0x30},
+	{0xF89A, 0x41},
+	{0xF89B, 0x74},
+	{0xF89C, 0x60},
+	{0xF89D, 0xf0},
+	{0xF89E, 0xa3},
+	{0xF89F, 0x74},
+	{0xF8A0, 0x64},
+	{0xF8A1, 0xf0},
+	{0xF8A2, 0x22},
+	{0xF8A3, 0xe5},
+	{0xF8A4, 0x7d},
+	{0xF8A5, 0xb4},
+	{0xF8A6, 0x03},
+	{0xF8A7, 0x12},
+	{0xF8A8, 0x90},
+	{0xF8A9, 0x30},
+	{0xF8AA, 0x05},
+	{0xF8AB, 0x74},
+	{0xF8AC, 0x03},
+	{0xF8AD, 0xf0},
+	{0xF8AE, 0x90},
+	{0xF8AF, 0x30},
+	{0xF8B0, 0x11},
+	{0xF8B1, 0x74},
+	{0xF8B2, 0x01},
+	{0xF8B3, 0xf0},
+	{0xF8B4, 0x90},
+	{0xF8B5, 0x30},
+	{0xF8B6, 0x35},
+	{0xF8B7, 0x74},
+	{0xF8B8, 0x03},
+	{0xF8B9, 0xf0},
+	{0xF8BA, 0x22},
+	{0xF8BB, 0xc3},
+	{0xF8BC, 0x90},
+	{0xF8BD, 0x0b},
+	{0xF8BE, 0x89},
+	{0xF8BF, 0xe0},
+	{0xF8C0, 0x94},
+	{0xF8C1, 0x1e},
+	{0xF8C2, 0x90},
+	{0xF8C3, 0x0b},
+	{0xF8C4, 0x88},
+	{0xF8C5, 0xe0},
+	{0xF8C6, 0x94},
+	{0xF8C7, 0x00},
+	{0xF8C8, 0x50},
+	{0xF8C9, 0x06},
+	{0xF8CA, 0x7e},
+	{0xF8CB, 0x00},
+	{0xF8CC, 0x7f},
+	{0xF8CD, 0x01},
+	{0xF8CE, 0x80},
+	{0xF8CF, 0x3d},
+	{0xF8D0, 0xc3},
+	{0xF8D1, 0x90},
+	{0xF8D2, 0x0b},
+	{0xF8D3, 0x89},
+	{0xF8D4, 0xe0},
+	{0xF8D5, 0x94},
+	{0xF8D6, 0x3c},
+	{0xF8D7, 0x90},
+	{0xF8D8, 0x0b},
+	{0xF8D9, 0x88},
+	{0xF8DA, 0xe0},
+	{0xF8DB, 0x94},
+	{0xF8DC, 0x00},
+	{0xF8DD, 0x50},
+	{0xF8DE, 0x06},
+	{0xF8DF, 0x7e},
+	{0xF8E0, 0x00},
+	{0xF8E1, 0x7f},
+	{0xF8E2, 0x02},
+	{0xF8E3, 0x80},
+	{0xF8E4, 0x28},
+	{0xF8E5, 0xc3},
+	{0xF8E6, 0x90},
+	{0xF8E7, 0x0b},
+	{0xF8E8, 0x89},
+	{0xF8E9, 0xe0},
+	{0xF8EA, 0x94},
+	{0xF8EB, 0xfa},
+	{0xF8EC, 0x90},
+	{0xF8ED, 0x0b},
+	{0xF8EE, 0x88},
+	{0xF8EF, 0xe0},
+	{0xF8F0, 0x94},
+	{0xF8F1, 0x00},
+	{0xF8F2, 0x50},
+	{0xF8F3, 0x06},
+	{0xF8F4, 0x7e},
+	{0xF8F5, 0x00},
+	{0xF8F6, 0x7f},
+	{0xF8F7, 0x03},
+	{0xF8F8, 0x80},
+	{0xF8F9, 0x13},
+	{0xF8FA, 0xc3},
+	{0xF8FB, 0x90},
+	{0xF8FC, 0x0b},
+	{0xF8FD, 0x88},
+	{0xF8FE, 0xe0},
+	{0xF8FF, 0x94},
+	{0xF900, 0x80},
+	{0xF901, 0x50},
+	{0xF902, 0x06},
+	{0xF903, 0x7e},
+	{0xF904, 0x00},
+	{0xF905, 0x7f},
+	{0xF906, 0x04},
+	{0xF907, 0x80},
+	{0xF908, 0x04},
+	{0xF909, 0xae},
+	{0xF90A, 0x7e},
+	{0xF90B, 0xaf},
+	{0xF90C, 0x7f},
+	{0xF90D, 0x90},
+	{0xF90E, 0xa0},
+	{0xF90F, 0xf8},
+	{0xF910, 0xee},
+	{0xF911, 0xf0},
+	{0xF912, 0xa3},
+	{0xF913, 0xef},
+	{0xF914, 0xf0},
+	{0xF915, 0x22},
+	{0xF916, 0x90},
+	{0xF917, 0x33},
+	{0xF918, 0x82},
+	{0xF919, 0xe0},
+	{0xF91A, 0xff},
+	{0xF91B, 0x64},
+	{0xF91C, 0x01},
+	{0xF91D, 0x70},
+	{0xF91E, 0x30},
+	{0xF91F, 0xe5},
+	{0xF920, 0x7f},
+	{0xF921, 0x64},
+	{0xF922, 0x02},
+	{0xF923, 0x45},
+	{0xF924, 0x7e},
+	{0xF925, 0x70},
+	{0xF926, 0x04},
+	{0xF927, 0x7d},
+	{0xF928, 0x1e},
+	{0xF929, 0x80},
+	{0xF92A, 0x1d},
+	{0xF92B, 0xe5},
+	{0xF92C, 0x7f},
+	{0xF92D, 0x64},
+	{0xF92E, 0x03},
+	{0xF92F, 0x45},
+	{0xF930, 0x7e},
+	{0xF931, 0x70},
+	{0xF932, 0x04},
+	{0xF933, 0x7d},
+	{0xF934, 0x3c},
+	{0xF935, 0x80},
+	{0xF936, 0x11},
+	{0xF937, 0xe5},
+	{0xF938, 0x7f},
+	{0xF939, 0x64},
+	{0xF93A, 0x04},
+	{0xF93B, 0x45},
+	{0xF93C, 0x7e},
+	{0xF93D, 0x70},
+	{0xF93E, 0x04},
+	{0xF93F, 0x7d},
+	{0xF940, 0xfa},
+	{0xF941, 0x80},
+	{0xF942, 0x05},
+	{0xF943, 0x90},
+	{0xF944, 0x33},
+	{0xF945, 0x81},
+	{0xF946, 0xe0},
+	{0xF947, 0xfd},
+	{0xF948, 0xae},
+	{0xF949, 0x05},
+	{0xF94A, 0x90},
+	{0xF94B, 0x33},
+	{0xF94C, 0x81},
+	{0xF94D, 0xed},
+	{0xF94E, 0xf0},
+	{0xF94F, 0xef},
+	{0xF950, 0xb4},
+	{0xF951, 0x01},
+	{0xF952, 0x10},
+	{0xF953, 0x90},
+	{0xF954, 0x01},
+	{0xF955, 0x00},
+	{0xF956, 0xe0},
+	{0xF957, 0x60},
+	{0xF958, 0x0a},
+	{0xF959, 0x90},
+	{0xF95A, 0xa1},
+	{0xF95B, 0x10},
+	{0xF95C, 0xe0},
+	{0xF95D, 0xf5},
+	{0xF95E, 0x7e},
+	{0xF95F, 0xa3},
+	{0xF960, 0xe0},
+	{0xF961, 0xf5},
+	{0xF962, 0x7f},
+	{0xF963, 0x22},
+	{0xF964, 0x12},
+	{0xF965, 0x2f},
+	{0xF966, 0x4d},
+	{0xF967, 0x90},
+	{0xF968, 0x35},
+	{0xF969, 0x38},
+	{0xF96A, 0xe0},
+	{0xF96B, 0x70},
+	{0xF96C, 0x05},
+	{0xF96D, 0x12},
+	{0xF96E, 0x00},
+	{0xF96F, 0x0e},
+	{0xF970, 0x80},
+	{0xF971, 0x03},
+	{0xF972, 0x12},
+	{0xF973, 0x07},
+	{0xF974, 0xc9},
+	{0xF975, 0x90},
+	{0xF976, 0x40},
+	{0xF977, 0x06},
+	{0xF978, 0xe0},
+	{0xF979, 0xf4},
+	{0xF97A, 0x54},
+	{0xF97B, 0x02},
+	{0xF97C, 0xff},
+	{0xF97D, 0xe0},
+	{0xF97E, 0x54},
+	{0xF97F, 0x01},
+	{0xF980, 0x4f},
+	{0xF981, 0x90},
+	{0xF982, 0x31},
+	{0xF983, 0x32},
+	{0xF984, 0xf0},
+	{0xF985, 0x90},
+	{0xF986, 0xfa},
+	{0xF987, 0x9d},
+	{0xF988, 0xe0},
+	{0xF989, 0x70},
+	{0xF98A, 0x03},
+	{0xF98B, 0x12},
+	{0xF98C, 0x27},
+	{0xF98D, 0x27},
+	{0xF98E, 0x02},
+	{0xF98F, 0x05},
+	{0xF990, 0xac},
+	{0xF991, 0x22},
+	{0xF992, 0x78},
+	{0xF993, 0x07},
+	{0xF994, 0xe6},
+	{0xF995, 0xf5},
+	{0xF996, 0x7c},
+	{0xF997, 0xe5},
+	{0xF998, 0x7c},
+	{0xF999, 0x60},
+	{0xF99A, 0x1d},
+	{0xF99B, 0x90},
+	{0xF99C, 0x43},
+	{0xF99D, 0x83},
+	{0xF99E, 0xe0},
+	{0xF99F, 0xb4},
+	{0xF9A0, 0x01},
+	{0xF9A1, 0x16},
+	{0xF9A2, 0x90},
+	{0xF9A3, 0x43},
+	{0xF9A4, 0x87},
+	{0xF9A5, 0xe0},
+	{0xF9A6, 0xb4},
+	{0xF9A7, 0x01},
+	{0xF9A8, 0x0f},
+	{0xF9A9, 0x15},
+	{0xF9AA, 0x7c},
+	{0xF9AB, 0x90},
+	{0xF9AC, 0x30},
+	{0xF9AD, 0xa1},
+	{0xF9AE, 0xe5},
+	{0xF9AF, 0x7c},
+	{0xF9B0, 0xf0},
+	{0xF9B1, 0x90},
+	{0xF9B2, 0x30},
+	{0xF9B3, 0xa0},
+	{0xF9B4, 0x74},
+	{0xF9B5, 0x01},
+	{0xF9B6, 0xf0},
+	{0xF9B7, 0x22},
+	{0xF9B8, 0xe4},
+	{0xF9B9, 0x90},
+	{0xF9BA, 0x30},
+	{0xF9BB, 0xa0},
+	{0xF9BC, 0xf0},
+	{0xF9BD, 0x22},
+	{0xF9BE, 0xf0},
+	{0xF9BF, 0xe5},
+	{0xF9C0, 0x3a},
+	{0xF9C1, 0xb4},
+	{0xF9C2, 0x06},
+	{0xF9C3, 0x06},
+	{0xF9C4, 0x63},
+	{0xF9C5, 0x3e},
+	{0xF9C6, 0x02},
+	{0xF9C7, 0x12},
+	{0xF9C8, 0x03},
+	{0xF9C9, 0xea},
+	{0xF9CA, 0x02},
+	{0xF9CB, 0x17},
+	{0xF9CC, 0x4a},
+	{0xF9CD, 0x22},
+	{0x35C9, 0xBB},
+	{0x35CA, 0x01},
+	{0x35CB, 0x16},
+	{0x35CC, 0x01},
+	{0x35CD, 0x64},
+	{0x35CE, 0x01},
+	{0x35CF, 0x92},
+	{0x35D0, 0x01},
+	{0x35D1, 0xBE},
+	{0x35D3, 0xF6},
+	{0x35D5, 0x07},
+	{0x35D7, 0xA3},
+	{0x35DB, 0x02},
+	{0x35DD, 0x06},
+	{0x35DF, 0x1B},
+	{0x35E6, 0x28},
+	{0x35E7, 0x76},
+	{0x35E8, 0x2D},
+	{0x35E9, 0x07},
+	{0x35EA, 0x04},
+	{0x35EB, 0x43},
+	{0x35EC, 0x05},
+	{0x35ED, 0xA9},
+	{0x35EE, 0x2A},
+	{0x35EF, 0x15},
+	{0x35F0, 0x17},
+	{0x35F1, 0x41},
+	{0x35F2, 0x24},
+	{0x35F3, 0x88},
+	{0x35F4, 0x01},
+	{0x35F5, 0x54},
+	{0x35F6, 0x01},
+	{0x35F7, 0x55},
+	{0x35F8, 0x2E},
+	{0x35F9, 0xF2},
+	{0x35FA, 0x06},
+	{0x35FB, 0x02},
+	{0x35FC, 0x06},
+	{0x35FD, 0x03},
+	{0x35FE, 0x06},
+	{0x35FF, 0x04},
+	{0x35C2, 0x1F},
+	{0x35C3, 0xFF},
+	{0x35C4, 0x1F},
+	{0x35C5, 0xC0},
+	{0x35C0, 0x01},
+};
+
+struct vx6953_format {
+	enum v4l2_mbus_pixelcode code;
+	enum v4l2_colorspace colorspace;
+	u16 fmt;
+	u16 order;
+};
+
+static const struct vx6953_format vx6953_cfmts[] = {
+	{
+	.code   = V4L2_MBUS_FMT_YUYV8_2X8,
+	.colorspace = V4L2_COLORSPACE_JPEG,
+	.fmt    = 1,
+	.order    = 0,
+	}
+	/* more can be supported, to be added later */
+};
+
+
+/*=============================================================*/
+
+static int vx6953_i2c_rxdata(unsigned short saddr,
+	unsigned char *rxdata, int length)
+{
+	struct i2c_msg msgs[] = {
+		{
+			.addr  = saddr,
+			.flags = 0,
+			.len   = 2,
+			.buf   = rxdata,
+		},
+		{
+			.addr  = saddr,
+			.flags = I2C_M_RD,
+			.len   = 2,
+			.buf   = rxdata,
+		},
+	};
+	if (i2c_transfer(vx6953_client->adapter, msgs, 2) < 0) {
+		CDBG("vx6953_i2c_rxdata failed!\n");
+		return -EIO;
+	}
+	return 0;
+}
+static int32_t vx6953_i2c_txdata(unsigned short saddr,
+				unsigned char *txdata, int length)
+{
+	struct i2c_msg msg[] = {
+		{
+			.addr = saddr,
+			.flags = 0,
+			.len = length,
+			.buf = txdata,
+		 },
+	};
+	if (i2c_transfer(vx6953_client->adapter, msg, 1) < 0) {
+		CDBG("vx6953_i2c_txdata faild 0x%x\n", vx6953_client->addr);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+
+static int32_t vx6953_i2c_read(unsigned short raddr,
+	unsigned short *rdata, int rlen)
+{
+	int32_t rc = 0;
+	unsigned char buf[2];
+	if (!rdata)
+		return -EIO;
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (raddr & 0xFF00) >> 8;
+	buf[1] = (raddr & 0x00FF);
+	rc = vx6953_i2c_rxdata(vx6953_client->addr>>1, buf, rlen);
+	if (rc < 0) {
+		CDBG("vx6953_i2c_read 0x%x failed!\n", raddr);
+		return rc;
+	}
+	*rdata = (rlen == 2 ? buf[0] << 8 | buf[1] : buf[0]);
+	return rc;
+}
+static int32_t vx6953_i2c_write_b_sensor(unsigned short waddr, uint8_t bdata)
+{
+	int32_t rc = -EFAULT;
+	unsigned char buf[3];
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (waddr & 0xFF00) >> 8;
+	buf[1] = (waddr & 0x00FF);
+	buf[2] = bdata;
+	CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, bdata);
+	rc = vx6953_i2c_txdata(vx6953_client->addr>>1, buf, 3);
+	if (rc < 0) {
+		CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+			waddr, bdata);
+	}
+	return rc;
+}
+static int32_t vx6953_i2c_write_seq_sensor(unsigned short waddr,
+	uint8_t *bdata, uint16_t len)
+{
+	int32_t rc = -EFAULT;
+	unsigned char buf[len+2];
+	int i;
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (waddr & 0xFF00) >> 8;
+	buf[1] = (waddr & 0x00FF);
+	for (i = 2; i < len+2; i++)
+		buf[i] = *bdata++;
+	rc = vx6953_i2c_txdata(vx6953_client->addr>>1, buf, len+2);
+	if (rc < 0) {
+		CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+			 waddr, bdata[0]);
+	}
+	return rc;
+}
+
+static int32_t vx6953_i2c_write_w_table(struct vx6953_i2c_reg_conf const
+					 *reg_conf_tbl, int num)
+{
+	int i;
+	int32_t rc = -EIO;
+	for (i = 0; i < num; i++) {
+		rc = vx6953_i2c_write_b_sensor(reg_conf_tbl->waddr,
+			reg_conf_tbl->wdata);
+		if (rc < 0)
+			break;
+		reg_conf_tbl++;
+	}
+	return rc;
+}
+
+static void vx6953_get_pict_fps(uint16_t fps, uint16_t *pfps)
+{
+	/* input fps is preview fps in Q8 format */
+	uint16_t preview_frame_length_lines, snapshot_frame_length_lines;
+	uint16_t preview_line_length_pck, snapshot_line_length_pck;
+	uint32_t divider, d1, d2;
+	/* Total frame_length_lines and line_length_pck for preview */
+	preview_frame_length_lines = VX6953_QTR_SIZE_HEIGHT +
+		VX6953_VER_QTR_BLK_LINES;
+	preview_line_length_pck = VX6953_QTR_SIZE_WIDTH +
+		VX6953_HRZ_QTR_BLK_PIXELS;
+	/* Total frame_length_lines and line_length_pck for snapshot */
+	snapshot_frame_length_lines = VX6953_FULL_SIZE_HEIGHT +
+		VX6953_VER_FULL_BLK_LINES;
+	snapshot_line_length_pck = VX6953_FULL_SIZE_WIDTH +
+		VX6953_HRZ_FULL_BLK_PIXELS;
+	d1 = preview_frame_length_lines * 0x00000400/
+		snapshot_frame_length_lines;
+	d2 = preview_line_length_pck * 0x00000400/
+		snapshot_line_length_pck;
+	divider = d1 * d2 / 0x400;
+	/*Verify PCLK settings and frame sizes.*/
+	*pfps = (uint16_t) (fps * divider / 0x400);
+	/* 2 is the ratio of no.of snapshot channels
+	to number of preview channels */
+
+}
+
+static uint16_t vx6953_get_prev_lines_pf(void)
+{
+	if (vx6953_ctrl->prev_res == QTR_SIZE)
+		return VX6953_QTR_SIZE_HEIGHT + VX6953_VER_QTR_BLK_LINES;
+	else
+		return VX6953_FULL_SIZE_HEIGHT + VX6953_VER_FULL_BLK_LINES;
+
+}
+
+static uint16_t vx6953_get_prev_pixels_pl(void)
+{
+	if (vx6953_ctrl->prev_res == QTR_SIZE)
+		return VX6953_QTR_SIZE_WIDTH + VX6953_HRZ_QTR_BLK_PIXELS;
+	else
+		return VX6953_FULL_SIZE_WIDTH + VX6953_HRZ_FULL_BLK_PIXELS;
+}
+
+static uint16_t vx6953_get_pict_lines_pf(void)
+{
+		if (vx6953_ctrl->pict_res == QTR_SIZE)
+			return VX6953_QTR_SIZE_HEIGHT +
+				VX6953_VER_QTR_BLK_LINES;
+		else
+			return VX6953_FULL_SIZE_HEIGHT +
+				VX6953_VER_FULL_BLK_LINES;
+}
+
+static uint16_t vx6953_get_pict_pixels_pl(void)
+{
+	if (vx6953_ctrl->pict_res == QTR_SIZE)
+		return VX6953_QTR_SIZE_WIDTH +
+			VX6953_HRZ_QTR_BLK_PIXELS;
+	else
+		return VX6953_FULL_SIZE_WIDTH +
+			VX6953_HRZ_FULL_BLK_PIXELS;
+}
+
+static uint32_t vx6953_get_pict_max_exp_lc(void)
+{
+	if (vx6953_ctrl->pict_res == QTR_SIZE)
+		return (VX6953_QTR_SIZE_HEIGHT +
+			VX6953_VER_QTR_BLK_LINES)*24;
+	else
+		return (VX6953_FULL_SIZE_HEIGHT +
+			VX6953_VER_FULL_BLK_LINES)*24;
+}
+
+static int32_t vx6953_set_fps(struct fps_cfg	*fps)
+{
+	uint16_t total_lines_per_frame;
+	int32_t rc = 0;
+	total_lines_per_frame = (uint16_t)((VX6953_QTR_SIZE_HEIGHT +
+		VX6953_VER_QTR_BLK_LINES) * vx6953_ctrl->fps_divider/0x400);
+	if (vx6953_i2c_write_b_sensor(REG_FRAME_LENGTH_LINES_HI,
+		((total_lines_per_frame & 0xFF00) >> 8)) < 0)
+		return rc;
+	if (vx6953_i2c_write_b_sensor(REG_FRAME_LENGTH_LINES_LO,
+		(total_lines_per_frame & 0x00FF)) < 0)
+		return rc;
+	return rc;
+}
+
+static int32_t vx6953_write_exp_gain(uint16_t gain, uint32_t line)
+{
+	uint16_t line_length_pck, frame_length_lines;
+	uint8_t gain_hi, gain_lo;
+	uint8_t intg_time_hi, intg_time_lo;
+	uint8_t line_length_pck_hi = 0, line_length_pck_lo = 0;
+	uint16_t line_length_ratio = 1 * Q8;
+	int32_t rc = 0;
+	if (vx6953_ctrl->sensormode != SENSOR_SNAPSHOT_MODE) {
+		frame_length_lines = VX6953_QTR_SIZE_HEIGHT +
+		VX6953_VER_QTR_BLK_LINES;
+		line_length_pck = VX6953_QTR_SIZE_WIDTH +
+			VX6953_HRZ_QTR_BLK_PIXELS;
+		if (line > (frame_length_lines -
+			VX6953_STM5M0EDOF_OFFSET)) {
+			vx6953_ctrl->fps = (uint16_t) (30 * Q8 *
+			(frame_length_lines - VX6953_STM5M0EDOF_OFFSET)/
+			line);
+		} else {
+			vx6953_ctrl->fps = (uint16_t) (30 * Q8);
+		}
+	} else {
+		frame_length_lines = VX6953_FULL_SIZE_HEIGHT +
+				VX6953_VER_FULL_BLK_LINES;
+		line_length_pck = VX6953_FULL_SIZE_WIDTH +
+				VX6953_HRZ_FULL_BLK_PIXELS;
+	}
+	/* calculate line_length_ratio */
+	if ((frame_length_lines - VX6953_STM5M0EDOF_OFFSET) < line) {
+		line_length_ratio = (line*Q8) /
+			(frame_length_lines - VX6953_STM5M0EDOF_OFFSET);
+		line = frame_length_lines - VX6953_STM5M0EDOF_OFFSET;
+	} else {
+		line_length_ratio = 1*Q8;
+	}
+	vx6953_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+		GROUPED_PARAMETER_HOLD);
+	line_length_pck = (line_length_pck >
+		MAX_LINE_LENGTH_PCK) ?
+		MAX_LINE_LENGTH_PCK : line_length_pck;
+	line_length_pck = (uint16_t) (line_length_pck *
+		line_length_ratio/Q8);
+	line_length_pck_hi = (uint8_t) ((line_length_pck &
+		0xFF00) >> 8);
+	line_length_pck_lo = (uint8_t) (line_length_pck &
+		0x00FF);
+	vx6953_i2c_write_b_sensor(REG_LINE_LENGTH_PCK_HI,
+		line_length_pck_hi);
+	vx6953_i2c_write_b_sensor(REG_LINE_LENGTH_PCK_LO,
+		line_length_pck_lo);
+	/* update analogue gain registers */
+	gain_hi = (uint8_t) ((gain & 0xFF00) >> 8);
+	gain_lo = (uint8_t) (gain & 0x00FF);
+	vx6953_i2c_write_b_sensor(REG_ANALOGUE_GAIN_CODE_GLOBAL_LO,
+		gain_lo);
+	vx6953_i2c_write_b_sensor(REG_DIGITAL_GAIN_GREEN_R_LO, gain_hi);
+	vx6953_i2c_write_b_sensor(REG_DIGITAL_GAIN_RED_LO, gain_hi);
+	vx6953_i2c_write_b_sensor(REG_DIGITAL_GAIN_BLUE_LO, gain_hi);
+	vx6953_i2c_write_b_sensor(REG_DIGITAL_GAIN_GREEN_B_LO, gain_hi);
+	CDBG("%s, gain_hi 0x%x, gain_lo 0x%x\n", __func__,
+		gain_hi, gain_lo);
+	/* update line count registers */
+	intg_time_hi = (uint8_t) (((uint16_t)line & 0xFF00) >> 8);
+	intg_time_lo = (uint8_t) ((uint16_t)line & 0x00FF);
+	vx6953_i2c_write_b_sensor(REG_COARSE_INTEGRATION_TIME_HI,
+		intg_time_hi);
+	vx6953_i2c_write_b_sensor(REG_COARSE_INTEGRATION_TIME_LO,
+		intg_time_lo);
+	vx6953_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+		GROUPED_PARAMETER_HOLD_OFF);
+
+	return rc;
+}
+
+static int32_t vx6953_set_pict_exp_gain(uint16_t gain, uint32_t line)
+{
+	int32_t rc = 0;
+	rc = vx6953_write_exp_gain(gain, line);
+	return rc;
+} /* endof vx6953_set_pict_exp_gain*/
+
+static int32_t vx6953_move_focus(int direction,
+	int32_t num_steps)
+{
+	return 0;
+}
+
+
+static int32_t vx6953_set_default_focus(uint8_t af_step)
+{
+	return 0;
+}
+
+static int32_t vx6953_test(enum vx6953_test_mode_t mo)
+{
+	int32_t rc = 0;
+	if (mo == TEST_OFF)
+		return rc;
+	else {
+		/* REG_0x30D8[4] is TESBYPEN: 0: Normal Operation,
+		1: Bypass Signal Processing
+		REG_0x30D8[5] is EBDMASK: 0:
+		Output Embedded data, 1: No output embedded data */
+		if (vx6953_i2c_write_b_sensor(REG_TEST_PATTERN_MODE,
+			(uint8_t) mo) < 0) {
+			return rc;
+		}
+	}
+	return rc;
+}
+
+static int vx6953_enable_edof(enum edof_mode_t edof_mode)
+{
+	int rc = 0;
+	if (edof_mode == VX6953_EDOF_ESTIMATION) {
+		/* EDof Estimation mode for preview */
+		if (vx6953_i2c_write_b_sensor(REG_0x0b80, 0x02) < 0)
+			return rc;
+		CDBG("VX6953_EDOF_ESTIMATION");
+	} else if (edof_mode == VX6953_EDOF_APPLICATION) {
+		/* EDof Application mode for Capture */
+		if (vx6953_i2c_write_b_sensor(REG_0x0b80, 0x01) < 0)
+			return rc;
+		CDBG("VX6953_EDOF_APPLICATION");
+	} else {
+		/* EDOF disabled */
+		if (vx6953_i2c_write_b_sensor(REG_0x0b80, 0x00) < 0)
+			return rc;
+		CDBG("VX6953_EDOF_DISABLE");
+	}
+	return rc;
+}
+
+static int32_t vx6953_patch_for_cut2(void)
+{
+	int32_t rc = 0;
+	rc = vx6953_i2c_write_w_table(patch_tbl_cut2,
+		ARRAY_SIZE(patch_tbl_cut2));
+	if (rc < 0)
+		return rc;
+
+	return rc;
+}
+static int32_t vx6953_patch_for_cut3(void)
+{
+	int32_t rc = 0;
+	rc = vx6953_i2c_write_w_table(patch_tbl_cut3,
+		ARRAY_SIZE(patch_tbl_cut3));
+	if (rc < 0)
+		return rc;
+
+	return rc;
+}
+static int32_t vx6953_sensor_setting(int update_type, int rt)
+{
+
+	int32_t rc = 0;
+	unsigned short frame_cnt;
+	struct msm_camera_csi_params vx6953_csi_params;
+	if (vx6953_ctrl->sensor_type != VX6953_STM5M0EDOF_CUT_2) {
+		switch (update_type) {
+		case REG_INIT:
+		if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+			struct vx6953_i2c_reg_conf init_tbl[] = {
+			{REG_0x0112,
+				vx6953_regs.reg_pat_init[0].reg_0x0112},
+			{0x6003, 0x01},
+			{REG_0x0113,
+				vx6953_regs.reg_pat_init[0].reg_0x0113},
+			{REG_VT_PIX_CLK_DIV,
+				vx6953_regs.reg_pat_init[0].
+				vt_pix_clk_div},
+			{REG_PRE_PLL_CLK_DIV,
+				vx6953_regs.reg_pat_init[0].
+				pre_pll_clk_div},
+			{REG_PLL_MULTIPLIER,
+				vx6953_regs.reg_pat_init[0].
+				pll_multiplier},
+			{REG_OP_PIX_CLK_DIV,
+				vx6953_regs.reg_pat_init[0].
+				op_pix_clk_div},
+			{REG_COARSE_INTEGRATION_TIME_HI,
+				vx6953_regs.reg_pat[rt].
+				coarse_integration_time_hi},
+			{REG_COARSE_INTEGRATION_TIME_LO,
+				vx6953_regs.reg_pat[rt].
+				coarse_integration_time_lo},
+			{REG_ANALOGUE_GAIN_CODE_GLOBAL_LO,
+				vx6953_regs.reg_pat[rt].
+				analogue_gain_code_global},
+			{REG_0x3030,
+				vx6953_regs.reg_pat_init[0].reg_0x3030},
+			/* 953 specific registers */
+			{REG_0x0111,
+				vx6953_regs.reg_pat_init[0].reg_0x0111},
+			{REG_0x0b00,
+				vx6953_regs.reg_pat_init[0].reg_0x0b00},
+			{REG_0x3001,
+				vx6953_regs.reg_pat_init[0].reg_0x3001},
+			{REG_0x3004,
+				vx6953_regs.reg_pat_init[0].reg_0x3004},
+			{0x3006, 0x00},
+			{REG_0x3007,
+				vx6953_regs.reg_pat_init[0].reg_0x3007},
+			{0x301b, 0x29},
+			/* DEFCOR settings */
+			/*Single Defect Correction Weight DISABLE*/
+			{0x0b06,
+				vx6953_regs.reg_pat_init[0].reg_0x0b06},
+			/*Single_defect_correct_weight = auto*/
+			{0x0b07,
+				vx6953_regs.reg_pat_init[0].reg_0x0b07},
+			/*Dynamic couplet correction ENABLED*/
+			{0x0b08,
+				vx6953_regs.reg_pat_init[0].reg_0x0b08},
+			/*Dynamic couplet correction weight*/
+			{0x0b09,
+				vx6953_regs.reg_pat_init[0].reg_0x0b09},
+			/* Clock Setup */
+			/* Tell sensor ext clk is 24MHz*/
+			{REG_0x0136,
+				vx6953_regs.reg_pat_init[0].reg_0x0136},
+			{REG_0x0137,
+				vx6953_regs.reg_pat_init[0].reg_0x0137},
+			/* The white balance gains must be written
+			to the sensor every frame. */
+			/* Edof */
+			{REG_0x0b83,
+				vx6953_regs.reg_pat_init[0].reg_0x0b83},
+			{REG_0x0b84,
+				vx6953_regs.reg_pat_init[0].reg_0x0b84},
+			{REG_0x0b85,
+				vx6953_regs.reg_pat_init[0].reg_0x0b85},
+			{REG_0x0b88,
+				vx6953_regs.reg_pat_init[0].reg_0x0b88},
+			{REG_0x0b89,
+				vx6953_regs.reg_pat_init[0].reg_0x0b89},
+			{REG_0x0b8a,
+				vx6953_regs.reg_pat_init[0].reg_0x0b8a},
+			/* Mode specific regieters */
+			{REG_FRAME_LENGTH_LINES_HI,
+				vx6953_regs.reg_pat[rt].
+				frame_length_lines_hi},
+			{REG_FRAME_LENGTH_LINES_LO,
+				vx6953_regs.reg_pat[rt].
+				frame_length_lines_lo},
+			{REG_LINE_LENGTH_PCK_HI,
+				vx6953_regs.reg_pat[rt].
+				line_length_pck_hi},
+			{REG_LINE_LENGTH_PCK_LO,
+				vx6953_regs.reg_pat[rt].
+				line_length_pck_lo},
+			{REG_0x3005,
+				vx6953_regs.reg_pat[rt].reg_0x3005},
+			{0x3010,
+				vx6953_regs.reg_pat[rt].reg_0x3010},
+			{REG_0x3011,
+				vx6953_regs.reg_pat[rt].reg_0x3011},
+			{REG_0x301a,
+				vx6953_regs.reg_pat[rt].reg_0x301a},
+			{REG_0x3035,
+				vx6953_regs.reg_pat[rt].reg_0x3035},
+			{REG_0x3036,
+				vx6953_regs.reg_pat[rt].reg_0x3036},
+			{REG_0x3041,
+				vx6953_regs.reg_pat[rt].reg_0x3041},
+			{0x3042,
+				vx6953_regs.reg_pat[rt].reg_0x3042},
+			{REG_0x3045,
+				vx6953_regs.reg_pat[rt].reg_0x3045},
+			/*EDOF: Estimation settings for Preview mode
+			Application settings for capture mode
+			(standard settings - Not tuned) */
+			{REG_0x0b80,
+				vx6953_regs.reg_pat[rt].reg_0x0b80},
+			{REG_0x0900,
+				vx6953_regs.reg_pat[rt].reg_0x0900},
+			{REG_0x0901,
+				vx6953_regs.reg_pat[rt].reg_0x0901},
+			{REG_0x0902,
+				vx6953_regs.reg_pat[rt].reg_0x0902},
+			{REG_0x0383,
+				vx6953_regs.reg_pat[rt].reg_0x0383},
+			{REG_0x0387,
+				vx6953_regs.reg_pat[rt].reg_0x0387},
+			/* Change output size / frame rate */
+			{REG_0x034c,
+				vx6953_regs.reg_pat[rt].reg_0x034c},
+			{REG_0x034d,
+				vx6953_regs.reg_pat[rt].reg_0x034d},
+			{REG_0x034e,
+				vx6953_regs.reg_pat[rt].reg_0x034e},
+			{REG_0x034f,
+				vx6953_regs.reg_pat[rt].reg_0x034f},
+			};
+			/* reset fps_divider */
+			vx6953_ctrl->fps = 30 * Q8;
+			/* stop streaming */
+
+			/* Reset everything first */
+			if (vx6953_i2c_write_b_sensor(0x103, 0x01) < 0) {
+				CDBG("S/W reset failed\n");
+				return rc;
+			} else
+				CDBG("S/W reset successful\n");
+
+			msleep(10);
+
+			CDBG("Init vx6953_sensor_setting standby\n");
+			if (vx6953_i2c_write_b_sensor(REG_MODE_SELECT,
+				MODE_SELECT_STANDBY_MODE) < 0)
+				return rc;
+			/*vx6953_stm5m0edof_delay_msecs_stdby*/
+			msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+
+
+			vx6953_patch_for_cut3();
+			rc = vx6953_i2c_write_w_table(&init_tbl[0],
+				ARRAY_SIZE(init_tbl));
+			if (rc < 0)
+				return rc;
+
+			msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+			vx6953_i2c_write_b_sensor(0x0b80, 0x00);
+			vx6953_i2c_write_b_sensor(0x3388, 0x03);
+			vx6953_i2c_write_b_sensor(0x3640, 0x00);
+
+			rc = vx6953_i2c_write_w_table(&edof_tbl[0],
+				ARRAY_SIZE(edof_tbl));
+			vx6953_i2c_write_b_sensor(0x3388, 0x00);
+
+		}
+		return rc;
+		case UPDATE_PERIODIC:
+		if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+			struct vx6953_i2c_reg_conf preview_mode_tbl[] = {
+			{REG_0x0112,
+				vx6953_regs.reg_pat_init[0].reg_0x0112},
+			{0x6003, 0x01},
+			{REG_0x0113,
+				vx6953_regs.reg_pat_init[0].reg_0x0113},
+			{REG_VT_PIX_CLK_DIV,
+				vx6953_regs.reg_pat_init[0].
+				vt_pix_clk_div},
+			{REG_PRE_PLL_CLK_DIV,
+				vx6953_regs.reg_pat_init[0].
+				pre_pll_clk_div},
+			{REG_PLL_MULTIPLIER,
+				vx6953_regs.reg_pat_init[0].
+				pll_multiplier},
+			{REG_OP_PIX_CLK_DIV,
+				vx6953_regs.reg_pat_init[0].
+				op_pix_clk_div},
+
+			{REG_COARSE_INTEGRATION_TIME_HI,
+				vx6953_regs.reg_pat[rt].
+				coarse_integration_time_hi},
+			{REG_COARSE_INTEGRATION_TIME_LO,
+				vx6953_regs.reg_pat[rt].
+				coarse_integration_time_lo},
+			{REG_ANALOGUE_GAIN_CODE_GLOBAL_LO,
+				vx6953_regs.reg_pat[rt].
+				analogue_gain_code_global},
+
+			{REG_0x3210, vx6953_regs.reg_pat[rt].reg_0x3210},
+			{REG_0x0111, vx6953_regs.reg_pat[rt].reg_0x111},
+			{REG_0x3410, vx6953_regs.reg_pat[rt].reg_0x3410},
+
+			{REG_0x3004,
+				vx6953_regs.reg_pat_init[0].reg_0x3004},
+			{REG_0x3006, 0x00},
+			{REG_0x3007,
+				vx6953_regs.reg_pat_init[0].reg_0x3007},
+			{REG_0x301b, 0x29},
+			{REG_0x3036,
+				vx6953_regs.reg_pat[rt].reg_0x3036},
+			{REG_0x3045, vx6953_regs.reg_pat[rt].reg_0x3045},
+			{REG_0x3098, vx6953_regs.reg_pat[rt].reg_0x3098},
+			{REG_0x309d, vx6953_regs.reg_pat[rt].reg_0x309D},
+
+			{REG_0x0900, vx6953_regs.reg_pat[rt].reg_0x0900},
+			{REG_0x0901, vx6953_regs.reg_pat[rt].reg_0x0901},
+			{REG_0x0902, vx6953_regs.reg_pat[rt].reg_0x0902},
+			{REG_0x0383, vx6953_regs.reg_pat[rt].reg_0x0383},
+			{REG_0x0387, vx6953_regs.reg_pat[rt].reg_0x0387},
+
+			{REG_FRAME_LENGTH_LINES_HI,
+				vx6953_regs.reg_pat[rt].
+				frame_length_lines_hi},
+			{REG_FRAME_LENGTH_LINES_LO,
+				vx6953_regs.reg_pat[rt].
+				frame_length_lines_lo},
+			{REG_LINE_LENGTH_PCK_HI,
+				vx6953_regs.reg_pat[rt].
+				line_length_pck_hi},
+			{REG_LINE_LENGTH_PCK_LO,
+				vx6953_regs.reg_pat[rt].
+				line_length_pck_lo},
+			{REG_0x034c,
+				vx6953_regs.reg_pat[rt].reg_0x034c},
+			{REG_0x034d,
+				vx6953_regs.reg_pat[rt].reg_0x034d},
+			{REG_0x034e,
+				vx6953_regs.reg_pat[rt].reg_0x034e},
+			{REG_0x034f,
+				vx6953_regs.reg_pat[rt].reg_0x034f},
+
+			{REG_0x3005, vx6953_regs.reg_pat[rt].reg_0x3005},
+			{REG_0x3010, vx6953_regs.reg_pat[rt].reg_0x3010},
+			{REG_0x3011, vx6953_regs.reg_pat[rt].reg_0x3011},
+			{REG_0x301a, vx6953_regs.reg_pat[rt].reg_0x301a},
+			{REG_0x3030, 0x08},
+			{REG_0x3035, vx6953_regs.reg_pat[rt].reg_0x3035},
+			{REG_0x3041, vx6953_regs.reg_pat[rt].reg_0x3041},
+			{0x3042, vx6953_regs.reg_pat[rt].reg_0x3042},
+
+			{0x200, vx6953_regs.reg_pat[rt].reg_0x0200},
+			{0x201, vx6953_regs.reg_pat[rt].reg_0x0201},
+
+			{0x0b06,
+				vx6953_regs.reg_pat_init[0].reg_0x0b06},
+			/*Single_defect_correct_weight = auto*/
+			{0x0b07,
+				vx6953_regs.reg_pat_init[0].reg_0x0b07},
+			/*Dynamic couplet correction ENABLED*/
+			{0x0b08,
+				vx6953_regs.reg_pat_init[0].reg_0x0b08},
+			/*Dynamic couplet correction weight*/
+			{0x0b09,
+				vx6953_regs.reg_pat_init[0].reg_0x0b09},
+
+			{REG_0x0136,
+				vx6953_regs.reg_pat_init[0].reg_0x0136},
+			{REG_0x0137,
+				vx6953_regs.reg_pat_init[0].reg_0x0137},
+
+			/*EDOF: Estimation settings for Preview mode
+			Application settings for capture
+			mode(standard settings - Not tuned) */
+			{REG_0x0b80, vx6953_regs.reg_pat[rt].reg_0x0b80},
+			{REG_0x0b83,
+				vx6953_regs.reg_pat_init[0].reg_0x0b83},
+			{REG_0x0b84,
+				vx6953_regs.reg_pat_init[0].reg_0x0b84},
+			{REG_0x0b85,
+				vx6953_regs.reg_pat_init[0].reg_0x0b85},
+			{REG_0x0b88,
+				vx6953_regs.reg_pat_init[0].reg_0x0b88},
+			{REG_0x0b89,
+				vx6953_regs.reg_pat_init[0].reg_0x0b89},
+			{REG_0x0b8a,
+				vx6953_regs.reg_pat_init[0].reg_0x0b8a},
+			{0x3393, 0x06}, /* man_spec_edof_ctrl_edof*/
+			{0x3394, 0x07}, /* man_spec_edof_ctrl_edof*/
+			};
+
+			struct vx6953_i2c_reg_conf snapshot_mode_tbl[] = {
+			{REG_MODE_SELECT,	MODE_SELECT_STANDBY_MODE},
+			{REG_0x0112,
+				vx6953_regs.reg_pat_init[0].reg_0x0112},
+			{0x6003, 0x01},
+			{REG_0x0113,
+				vx6953_regs.reg_pat_init[0].reg_0x0113},
+			{REG_VT_PIX_CLK_DIV,
+				vx6953_regs.reg_pat_init[0].
+				vt_pix_clk_div},
+			{0x303,	1}, /* VT_SYS_CLK_DIV */
+			{REG_PRE_PLL_CLK_DIV,
+				vx6953_regs.reg_pat_init[0].
+				pre_pll_clk_div},
+			{REG_PLL_MULTIPLIER,
+				vx6953_regs.reg_pat_init[0].
+				pll_multiplier},
+			{REG_OP_PIX_CLK_DIV,
+				vx6953_regs.reg_pat_init[0].
+				op_pix_clk_div},
+			{0x30b,	1},
+			{REG_COARSE_INTEGRATION_TIME_HI,
+				vx6953_regs.reg_pat[rt].
+				coarse_integration_time_hi},
+			{REG_COARSE_INTEGRATION_TIME_LO,
+				vx6953_regs.reg_pat[rt].
+				coarse_integration_time_lo},
+			{REG_ANALOGUE_GAIN_CODE_GLOBAL_LO,
+				vx6953_regs.reg_pat[rt].
+				analogue_gain_code_global},
+			{REG_LINE_LENGTH_PCK_HI,
+				vx6953_regs.reg_pat[rt].
+				line_length_pck_hi},
+			{REG_LINE_LENGTH_PCK_LO,
+				vx6953_regs.reg_pat[rt].
+				line_length_pck_lo},
+			{REG_FRAME_LENGTH_LINES_HI,
+				vx6953_regs.reg_pat[rt].
+				frame_length_lines_hi},
+			{REG_FRAME_LENGTH_LINES_LO,
+				vx6953_regs.reg_pat[rt].
+				frame_length_lines_lo},
+			{REG_0x3210, vx6953_regs.reg_pat[rt].reg_0x3210},
+			{REG_0x0111, vx6953_regs.reg_pat[rt].reg_0x111},
+
+			{REG_0x0b00,
+				vx6953_regs.reg_pat_init[0].reg_0x0b00},
+			{0x3140, 0x01},  /* AV2X2 block enabled */
+			{REG_0x3410, vx6953_regs.reg_pat[rt].reg_0x3410},
+			{0x0b06,
+				vx6953_regs.reg_pat_init[0].reg_0x0b06},
+			/*Single_defect_correct_weight = auto*/
+			{0x0b07,
+				vx6953_regs.reg_pat_init[0].reg_0x0b07},
+			/*Dynamic couplet correction ENABLED*/
+			{0x0b08,
+				vx6953_regs.reg_pat_init[0].reg_0x0b08},
+			/*Dynamic couplet correction weight*/
+			{0x0b09,
+				vx6953_regs.reg_pat_init[0].reg_0x0b09},
+
+
+			{REG_0x3004,
+				vx6953_regs.reg_pat_init[0].reg_0x3004},
+			{REG_0x3006, 0x00},
+			{REG_0x3007,
+				vx6953_regs.reg_pat_init[0].reg_0x3007},
+			{0x301A, 0x6A},
+			{REG_0x301b, 0x29},
+			{REG_0x3036,
+				vx6953_regs.reg_pat[rt].reg_0x3036},
+			{REG_0x3045, vx6953_regs.reg_pat[rt].reg_0x3045},
+			{REG_0x3098, vx6953_regs.reg_pat[rt].reg_0x3098},
+			{REG_0x309d, vx6953_regs.reg_pat[rt].reg_0x309D},
+
+			{REG_0x0136,
+				vx6953_regs.reg_pat_init[0].reg_0x0136},
+			{REG_0x0137,
+				vx6953_regs.reg_pat_init[0].reg_0x0137},
+
+			{REG_0x0b80, vx6953_regs.reg_pat[rt].reg_0x0b80},
+			{REG_0x0b83,
+				vx6953_regs.reg_pat_init[0].reg_0x0b83},
+			{REG_0x0b84,
+				vx6953_regs.reg_pat_init[0].reg_0x0b84},
+			{REG_0x0b85,
+				vx6953_regs.reg_pat_init[0].reg_0x0b85},
+			{REG_0x0b88,
+				vx6953_regs.reg_pat_init[0].reg_0x0b88},
+			{REG_0x0b89,
+				vx6953_regs.reg_pat_init[0].reg_0x0b89},
+			{REG_0x0b8a,
+				vx6953_regs.reg_pat_init[0].reg_0x0b8a},
+			{0x3393, 0x06}, /* man_spec_edof_ctrl*/
+			{0x3394, 0x07}, /* man_spec_edof_ctrl*/
+			};
+			/* stop streaming */
+			msleep(5);
+
+			/* Reset everything first */
+
+			if (vx6953_i2c_write_b_sensor(0x103, 0x01) < 0) {
+				CDBG("S/W reset failed\n");
+				return rc;
+			} else
+				CDBG("S/W reset successful\n");
+
+			msleep(10);
+
+			if (vx6953_i2c_write_b_sensor(REG_MODE_SELECT,
+				MODE_SELECT_STANDBY_MODE) < 0)
+				return rc;
+			/*vx6953_stm5m0edof_delay_msecs_stdby*/
+			msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+			vx6953_csi_params.data_format = CSI_8BIT;
+			vx6953_csi_params.lane_cnt = 1;
+			vx6953_csi_params.lane_assign = 0xe4;
+			vx6953_csi_params.dpcm_scheme = 0;
+			vx6953_csi_params.settle_cnt = 7;
+			rc = msm_camio_csi_config(&vx6953_csi_params);
+			if (rc < 0)
+				CDBG(" config csi controller failed\n");
+
+			msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+			vx6953_patch_for_cut3();
+
+			msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+			if (rt == RES_PREVIEW) {
+				rc = vx6953_i2c_write_w_table(
+					&preview_mode_tbl[0],
+					ARRAY_SIZE(preview_mode_tbl));
+				if (rc < 0)
+					return rc;
+			}
+			if (rt == RES_CAPTURE) {
+				rc = vx6953_i2c_write_w_table(
+					&snapshot_mode_tbl[0],
+					ARRAY_SIZE(snapshot_mode_tbl));
+				if (rc < 0)
+					return rc;
+			}
+			msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+			/* Start sensor streaming */
+			if (vx6953_i2c_write_b_sensor(REG_MODE_SELECT,
+				MODE_SELECT_STREAM) < 0)
+				return rc;
+			msleep(vx6953_stm5m0edof_delay_msecs_stream);
+			/* man_spec_edof_ctrl_tune_smooth_lowlight*/
+			vx6953_i2c_write_b_sensor(0x338d, 0x08);
+			/* man_spec_edof_ctrl_tune_smooth_indoor*/
+			vx6953_i2c_write_b_sensor(0x338e, 0x08);
+			/* man_spec_edof_ctrl_tune_smooth_outdoor*/
+			vx6953_i2c_write_b_sensor(0x338f, 0x00);
+			/*Apply Capture FPGA state machine reset*/
+			vx6953_i2c_write_b_sensor(0x16, 0x00);
+			msleep(100);
+			vx6953_i2c_write_b_sensor(0x16, 0x01);
+
+			if (vx6953_i2c_read(0x0005, &frame_cnt, 1) < 0)
+				return rc;
+
+			while (frame_cnt == 0xFF) {
+				if (vx6953_i2c_read(0x0005, &frame_cnt, 1) < 0)
+					return rc;
+				CDBG("frame_cnt=%d", frame_cnt);
+				msleep(10);
+			}
+		}
+		return rc;
+		default:
+			return rc;
+		}
+	} else {
+		switch (update_type) {
+		case REG_INIT:
+		if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+			struct vx6953_i2c_reg_conf init_tbl[] = {
+			{REG_0x0112,
+				vx6953_regs.reg_pat_init[0].reg_0x0112},
+			{REG_0x0113,
+				vx6953_regs.reg_pat_init[0].reg_0x0113},
+			{REG_VT_PIX_CLK_DIV,
+				vx6953_regs.reg_pat_init[0].
+				vt_pix_clk_div},
+			{REG_PRE_PLL_CLK_DIV,
+				vx6953_regs.reg_pat_init[0].
+				pre_pll_clk_div},
+			{REG_PLL_MULTIPLIER,
+				vx6953_regs.reg_pat_init[0].
+				pll_multiplier},
+			{REG_OP_PIX_CLK_DIV,
+				vx6953_regs.reg_pat_init[0].
+				op_pix_clk_div},
+			{REG_COARSE_INTEGRATION_TIME_HI,
+				vx6953_regs.reg_pat[rt].
+				coarse_integration_time_hi},
+			{REG_COARSE_INTEGRATION_TIME_LO,
+				vx6953_regs.reg_pat[rt].
+				coarse_integration_time_lo},
+			{REG_ANALOGUE_GAIN_CODE_GLOBAL_LO,
+				vx6953_regs.reg_pat[rt].
+				analogue_gain_code_global},
+			{REG_0x3030,
+				vx6953_regs.reg_pat_init[0].reg_0x3030},
+			/* 953 specific registers */
+			{REG_0x0111,
+				vx6953_regs.reg_pat_init[0].reg_0x0111},
+			{REG_0x0b00,
+				vx6953_regs.reg_pat_init[0].reg_0x0b00},
+			{REG_0x3001,
+				vx6953_regs.reg_pat_init[0].reg_0x3001},
+			{REG_0x3004,
+				vx6953_regs.reg_pat_init[0].reg_0x3004},
+			{REG_0x3007,
+				vx6953_regs.reg_pat_init[0].reg_0x3007},
+			{REG_0x3016,
+				vx6953_regs.reg_pat_init[0].reg_0x3016},
+			{REG_0x301d,
+				vx6953_regs.reg_pat_init[0].reg_0x301d},
+			{REG_0x317e,
+				vx6953_regs.reg_pat_init[0].reg_0x317e},
+			{REG_0x317f,
+				vx6953_regs.reg_pat_init[0].reg_0x317f},
+			{REG_0x3400,
+				vx6953_regs.reg_pat_init[0].reg_0x3400},
+			/* DEFCOR settings */
+			/*Single Defect Correction Weight DISABLE*/
+			{0x0b06,
+				vx6953_regs.reg_pat_init[0].reg_0x0b06},
+			/*Single_defect_correct_weight = auto*/
+			{0x0b07,
+				vx6953_regs.reg_pat_init[0].reg_0x0b07},
+			/*Dynamic couplet correction ENABLED*/
+			{0x0b08,
+				vx6953_regs.reg_pat_init[0].reg_0x0b08},
+			/*Dynamic couplet correction weight*/
+			{0x0b09,
+				vx6953_regs.reg_pat_init[0].reg_0x0b09},
+			/* Clock Setup */
+			/* Tell sensor ext clk is 24MHz*/
+			{0x0136,
+				vx6953_regs.reg_pat_init[0].reg_0x0136},
+			{0x0137,
+				vx6953_regs.reg_pat_init[0].reg_0x0137},
+			/* The white balance gains must be written
+			to the sensor every frame. */
+			/* Edof */
+			{REG_0x0b83,
+				vx6953_regs.reg_pat_init[0].reg_0x0b83},
+			{REG_0x0b84,
+				vx6953_regs.reg_pat_init[0].reg_0x0b84},
+			{0x0b85,
+				vx6953_regs.reg_pat_init[0].reg_0x0b85},
+			{0x0b88,
+				vx6953_regs.reg_pat_init[0].reg_0x0b88},
+			{0x0b89,
+				vx6953_regs.reg_pat_init[0].reg_0x0b89},
+			{REG_0x0b8a,
+				vx6953_regs.reg_pat_init[0].reg_0x0b8a},
+			/* Mode specific regieters */
+			{REG_FRAME_LENGTH_LINES_HI,
+				vx6953_regs.reg_pat[rt].
+				frame_length_lines_hi},
+			{REG_FRAME_LENGTH_LINES_LO,
+				vx6953_regs.reg_pat[rt].
+				frame_length_lines_lo},
+			{REG_LINE_LENGTH_PCK_HI,
+				vx6953_regs.reg_pat[rt].
+				line_length_pck_hi},
+			{REG_LINE_LENGTH_PCK_LO,
+				vx6953_regs.reg_pat[rt].
+				line_length_pck_lo},
+			{REG_0x3005,
+				vx6953_regs.reg_pat[rt].reg_0x3005},
+			{0x3010,
+				vx6953_regs.reg_pat[rt].reg_0x3010},
+			{REG_0x3011,
+				vx6953_regs.reg_pat[rt].reg_0x3011},
+			{REG_0x301a,
+				vx6953_regs.reg_pat[rt].reg_0x301a},
+			{REG_0x3035,
+				vx6953_regs.reg_pat[rt].reg_0x3035},
+			{REG_0x3036,
+				vx6953_regs.reg_pat[rt].reg_0x3036},
+			{REG_0x3041,
+				vx6953_regs.reg_pat[rt].reg_0x3041},
+			{0x3042,
+				vx6953_regs.reg_pat[rt].reg_0x3042},
+			{REG_0x3045,
+				vx6953_regs.reg_pat[rt].reg_0x3045},
+			/*EDOF: Estimation settings for Preview mode
+			Application settings for capture mode
+			(standard settings - Not tuned) */
+			{REG_0x0b80,
+				vx6953_regs.reg_pat[rt].reg_0x0b80},
+			{REG_0x0900,
+				vx6953_regs.reg_pat[rt].reg_0x0900},
+			{REG_0x0901,
+				vx6953_regs.reg_pat[rt].reg_0x0901},
+			{REG_0x0902,
+				vx6953_regs.reg_pat[rt].reg_0x0902},
+			{REG_0x0383,
+				vx6953_regs.reg_pat[rt].reg_0x0383},
+			{REG_0x0387,
+				vx6953_regs.reg_pat[rt].reg_0x0387},
+			/* Change output size / frame rate */
+			{REG_0x034c,
+				vx6953_regs.reg_pat[rt].reg_0x034c},
+			{REG_0x034d,
+				vx6953_regs.reg_pat[rt].reg_0x034d},
+			{REG_0x034e,
+				vx6953_regs.reg_pat[rt].reg_0x034e},
+			{REG_0x034f,
+				vx6953_regs.reg_pat[rt].reg_0x034f},
+			{REG_0x1716,
+				vx6953_regs.reg_pat[rt].reg_0x1716},
+			{REG_0x1717,
+				vx6953_regs.reg_pat[rt].reg_0x1717},
+			{REG_0x1718,
+				vx6953_regs.reg_pat[rt].reg_0x1718},
+			{REG_0x1719,
+				vx6953_regs.reg_pat[rt].reg_0x1719},
+			};
+			/* reset fps_divider */
+			vx6953_ctrl->fps = 30 * Q8;
+			/* stop streaming */
+
+			/* Reset everything first */
+			if (vx6953_i2c_write_b_sensor(0x103, 0x01) < 0) {
+				CDBG("S/W reset failed\n");
+				return rc;
+			} else
+				CDBG("S/W reset successful\n");
+
+			msleep(10);
+
+			CDBG("Init vx6953_sensor_setting standby\n");
+			if (vx6953_i2c_write_b_sensor(REG_MODE_SELECT,
+				MODE_SELECT_STANDBY_MODE) < 0)
+				return rc;
+				/*vx6953_stm5m0edof_delay_msecs_stdby*/
+			msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+			vx6953_patch_for_cut2();
+			rc = vx6953_i2c_write_w_table(&init_tbl[0],
+				ARRAY_SIZE(init_tbl));
+			if (rc < 0)
+				return rc;
+				msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+		}
+		return rc;
+		case UPDATE_PERIODIC:
+		if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+			struct vx6953_i2c_reg_conf init_mode_tbl[] =  {
+			{REG_0x0112,
+				vx6953_regs.reg_pat_init[0].reg_0x0112},
+			{REG_0x0113,
+				vx6953_regs.reg_pat_init[0].reg_0x0113},
+			{REG_VT_PIX_CLK_DIV,
+				vx6953_regs.reg_pat_init[0].
+				vt_pix_clk_div},
+			{REG_PRE_PLL_CLK_DIV,
+				vx6953_regs.reg_pat_init[0].
+				pre_pll_clk_div},
+			{REG_PLL_MULTIPLIER,
+				vx6953_regs.reg_pat_init[0].
+				pll_multiplier},
+			{REG_OP_PIX_CLK_DIV,
+				vx6953_regs.reg_pat_init[0].
+				op_pix_clk_div},
+			{REG_COARSE_INTEGRATION_TIME_HI,
+				vx6953_regs.reg_pat[rt].
+				coarse_integration_time_hi},
+			{REG_COARSE_INTEGRATION_TIME_LO,
+				vx6953_regs.reg_pat[rt].
+				coarse_integration_time_lo},
+			{REG_ANALOGUE_GAIN_CODE_GLOBAL_LO,
+				vx6953_regs.reg_pat[rt].
+				analogue_gain_code_global},
+			{REG_0x3030,
+				vx6953_regs.reg_pat_init[0].reg_0x3030},
+			/* 953 specific registers */
+			{REG_0x0111,
+				vx6953_regs.reg_pat_init[0].reg_0x0111},
+			{REG_0x0b00,
+				vx6953_regs.reg_pat_init[0].reg_0x0b00},
+			{REG_0x3001,
+				vx6953_regs.reg_pat_init[0].reg_0x3001},
+			{REG_0x3004,
+				vx6953_regs.reg_pat_init[0].reg_0x3004},
+			{REG_0x3007,
+				vx6953_regs.reg_pat_init[0].reg_0x3007},
+			{REG_0x3016,
+				vx6953_regs.reg_pat_init[0].reg_0x3016},
+			{REG_0x301d,
+				vx6953_regs.reg_pat_init[0].reg_0x301d},
+			{REG_0x317e,
+				vx6953_regs.reg_pat_init[0].reg_0x317e},
+			{REG_0x317f,
+				vx6953_regs.reg_pat_init[0].reg_0x317f},
+			{REG_0x3400,
+				vx6953_regs.reg_pat_init[0].reg_0x3400},
+			{0x0b06,
+				vx6953_regs.reg_pat_init[0].reg_0x0b06},
+			/*Single_defect_correct_weight = auto*/
+			{0x0b07,
+				vx6953_regs.reg_pat_init[0].reg_0x0b07},
+			/*Dynamic couplet correction ENABLED*/
+			{0x0b08,
+				vx6953_regs.reg_pat_init[0].reg_0x0b08},
+			/*Dynamic couplet correction weight*/
+			{0x0b09,
+				vx6953_regs.reg_pat_init[0].reg_0x0b09},
+			/* Clock Setup */
+			/* Tell sensor ext clk is 24MHz*/
+			{0x0136,
+				vx6953_regs.reg_pat_init[0].reg_0x0136},
+			{0x0137,
+				vx6953_regs.reg_pat_init[0].reg_0x0137},
+			/* The white balance gains must be written
+			to the sensor every frame. */
+			/* Edof */
+			{REG_0x0b83,
+				vx6953_regs.reg_pat_init[0].reg_0x0b83},
+			{REG_0x0b84,
+				vx6953_regs.reg_pat_init[0].reg_0x0b84},
+			{0x0b85,
+				vx6953_regs.reg_pat_init[0].reg_0x0b85},
+			{0x0b88,
+				vx6953_regs.reg_pat_init[0].reg_0x0b88},
+			{0x0b89,
+				vx6953_regs.reg_pat_init[0].reg_0x0b89},
+			{REG_0x0b8a,
+				vx6953_regs.reg_pat_init[0].reg_0x0b8a},
+			/* Mode specific regieters */
+			{REG_FRAME_LENGTH_LINES_HI,
+				vx6953_regs.reg_pat[rt].
+				frame_length_lines_hi},
+			{REG_FRAME_LENGTH_LINES_LO,
+				vx6953_regs.reg_pat[rt].
+				frame_length_lines_lo},
+			{REG_LINE_LENGTH_PCK_HI,
+				vx6953_regs.reg_pat[rt].
+				line_length_pck_hi},
+			{REG_LINE_LENGTH_PCK_LO,
+				vx6953_regs.reg_pat[rt].
+				line_length_pck_lo},
+			{REG_0x3005,
+				vx6953_regs.reg_pat[rt].reg_0x3005},
+			{0x3010,
+				vx6953_regs.reg_pat[rt].reg_0x3010},
+			{REG_0x3011,
+				vx6953_regs.reg_pat[rt].reg_0x3011},
+			{REG_0x301a,
+				vx6953_regs.reg_pat[rt].reg_0x301a},
+			{REG_0x3035,
+				vx6953_regs.reg_pat[rt].reg_0x3035},
+			{REG_0x3036,
+				vx6953_regs.reg_pat[rt].reg_0x3036},
+			{REG_0x3041,
+				vx6953_regs.reg_pat[rt].reg_0x3041},
+			{0x3042,
+				vx6953_regs.reg_pat[rt].reg_0x3042},
+			{REG_0x3045,
+				vx6953_regs.reg_pat[rt].reg_0x3045},
+			/*EDOF: Estimation settings for Preview mode
+			Application settings for capture mode
+			(standard settings - Not tuned) */
+			{REG_0x0b80,
+				vx6953_regs.reg_pat[rt].reg_0x0b80},
+			{REG_0x0900,
+				vx6953_regs.reg_pat[rt].reg_0x0900},
+			{REG_0x0901,
+				vx6953_regs.reg_pat[rt].reg_0x0901},
+			{REG_0x0902,
+				vx6953_regs.reg_pat[rt].reg_0x0902},
+			{REG_0x0383,
+				vx6953_regs.reg_pat[rt].reg_0x0383},
+			{REG_0x0387,
+				vx6953_regs.reg_pat[rt].reg_0x0387},
+			/* Change output size / frame rate */
+			{REG_0x034c,
+				vx6953_regs.reg_pat[rt].reg_0x034c},
+			{REG_0x034d,
+				vx6953_regs.reg_pat[rt].reg_0x034d},
+			{REG_0x034e,
+				vx6953_regs.reg_pat[rt].reg_0x034e},
+			{REG_0x034f,
+				vx6953_regs.reg_pat[rt].reg_0x034f},
+			{REG_0x1716,
+				vx6953_regs.reg_pat[rt].reg_0x1716},
+			{REG_0x1717,
+				vx6953_regs.reg_pat[rt].reg_0x1717},
+			{REG_0x1718,
+				vx6953_regs.reg_pat[rt].reg_0x1718},
+			{REG_0x1719,
+				vx6953_regs.reg_pat[rt].reg_0x1719},
+			};
+			struct vx6953_i2c_reg_conf mode_tbl[] = {
+			{REG_0x0112,
+				vx6953_regs.reg_pat_init[0].reg_0x0112},
+			{REG_0x0113,
+				vx6953_regs.reg_pat_init[0].reg_0x0113},
+			{REG_VT_PIX_CLK_DIV,
+				vx6953_regs.reg_pat_init[0].
+				vt_pix_clk_div},
+			{REG_PRE_PLL_CLK_DIV,
+				vx6953_regs.reg_pat_init[0].
+				pre_pll_clk_div},
+			{REG_PLL_MULTIPLIER,
+				vx6953_regs.reg_pat_init[0].
+				pll_multiplier},
+			{REG_OP_PIX_CLK_DIV,
+				vx6953_regs.reg_pat_init[0].
+				op_pix_clk_div},
+		/* Mode specific regieters */
+			{REG_FRAME_LENGTH_LINES_HI,
+				vx6953_regs.reg_pat[rt].frame_length_lines_hi},
+			{REG_FRAME_LENGTH_LINES_LO,
+				vx6953_regs.reg_pat[rt].frame_length_lines_lo},
+			{REG_LINE_LENGTH_PCK_HI,
+				vx6953_regs.reg_pat[rt].line_length_pck_hi},
+			{REG_LINE_LENGTH_PCK_LO,
+				vx6953_regs.reg_pat[rt].line_length_pck_lo},
+			{REG_0x3005, vx6953_regs.reg_pat[rt].reg_0x3005},
+			{0x3010, vx6953_regs.reg_pat[rt].reg_0x3010},
+			{REG_0x3011, vx6953_regs.reg_pat[rt].reg_0x3011},
+			{REG_0x301a, vx6953_regs.reg_pat[rt].reg_0x301a},
+			{REG_0x3035, vx6953_regs.reg_pat[rt].reg_0x3035},
+			{REG_0x3036, vx6953_regs.reg_pat[rt].reg_0x3036},
+			{REG_0x3041, vx6953_regs.reg_pat[rt].reg_0x3041},
+			{0x3042, vx6953_regs.reg_pat[rt].reg_0x3042},
+			{REG_0x3045, vx6953_regs.reg_pat[rt].reg_0x3045},
+			/*EDOF: Estimation settings for Preview mode
+			Application settings for capture
+			mode(standard settings - Not tuned) */
+			{REG_0x0b80, vx6953_regs.reg_pat[rt].reg_0x0b80},
+			{REG_0x0900, vx6953_regs.reg_pat[rt].reg_0x0900},
+			{REG_0x0901, vx6953_regs.reg_pat[rt].reg_0x0901},
+			{REG_0x0902, vx6953_regs.reg_pat[rt].reg_0x0902},
+			{REG_0x0383, vx6953_regs.reg_pat[rt].reg_0x0383},
+			{REG_0x0387, vx6953_regs.reg_pat[rt].reg_0x0387},
+			/* Change output size / frame rate */
+			{REG_0x034c, vx6953_regs.reg_pat[rt].reg_0x034c},
+			{REG_0x034d, vx6953_regs.reg_pat[rt].reg_0x034d},
+			{REG_0x034e, vx6953_regs.reg_pat[rt].reg_0x034e},
+			{REG_0x034f, vx6953_regs.reg_pat[rt].reg_0x034f},
+			/*{0x200, vx6953_regs.reg_pat[rt].reg_0x0200},
+			{0x201, vx6953_regs.reg_pat[rt].reg_0x0201},*/
+			{REG_0x1716, vx6953_regs.reg_pat[rt].reg_0x1716},
+			{REG_0x1717, vx6953_regs.reg_pat[rt].reg_0x1717},
+			{REG_0x1718, vx6953_regs.reg_pat[rt].reg_0x1718},
+			{REG_0x1719, vx6953_regs.reg_pat[rt].reg_0x1719},
+			};
+			/* stop streaming */
+			msleep(5);
+
+			/* Reset everything first */
+			if (vx6953_i2c_write_b_sensor(0x103, 0x01) < 0) {
+				CDBG("S/W reset failed\n");
+				return rc;
+			} else
+				CDBG("S/W reset successful\n");
+
+			msleep(10);
+
+			if (vx6953_i2c_write_b_sensor(REG_MODE_SELECT,
+				MODE_SELECT_STANDBY_MODE) < 0)
+				return rc;
+			/*vx6953_stm5m0edof_delay_msecs_stdby*/
+			msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+			vx6953_csi_params.data_format = CSI_8BIT;
+			vx6953_csi_params.lane_cnt = 1;
+			vx6953_csi_params.lane_assign = 0xe4;
+			vx6953_csi_params.dpcm_scheme = 0;
+			vx6953_csi_params.settle_cnt = 7;
+			rc = msm_camio_csi_config(&vx6953_csi_params);
+			if (rc < 0)
+				CDBG(" config csi controller failed\n");
+
+			msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+			vx6953_patch_for_cut2();
+			rc = vx6953_i2c_write_w_table(&init_mode_tbl[0],
+				ARRAY_SIZE(init_mode_tbl));
+			if (rc < 0)
+				return rc;
+
+			msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+			rc = vx6953_i2c_write_w_table(&mode_tbl[0],
+				ARRAY_SIZE(mode_tbl));
+			if (rc < 0)
+				return rc;
+
+			msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+			/* Start sensor streaming */
+			if (vx6953_i2c_write_b_sensor(REG_MODE_SELECT,
+				MODE_SELECT_STREAM) < 0)
+				return rc;
+			msleep(vx6953_stm5m0edof_delay_msecs_stream);
+
+			if (vx6953_i2c_read(0x0005, &frame_cnt, 1) < 0)
+				return rc;
+
+			while (frame_cnt == 0xFF) {
+				if (vx6953_i2c_read(0x0005, &frame_cnt, 1) < 0)
+					return rc;
+				CDBG("frame_cnt=%d", frame_cnt);
+				msleep(10);
+			}
+		}
+		return rc;
+		default:
+		return rc;
+	}
+	}
+	return rc;
+}
+
+
+static int32_t vx6953_video_config(int mode)
+{
+
+	int32_t	rc = 0;
+	int	rt;
+	/* change sensor resolution	if needed */
+	if (vx6953_ctrl->prev_res == QTR_SIZE) {
+		rt = RES_PREVIEW;
+		vx6953_stm5m0edof_delay_msecs_stdby	=
+			((((2 * 1000 * vx6953_ctrl->fps_divider) /
+			vx6953_ctrl->fps) * Q8) / Q10) + 1;
+	} else {
+		rt = RES_CAPTURE;
+		vx6953_stm5m0edof_delay_msecs_stdby	=
+			((((1000 * vx6953_ctrl->fps_divider) /
+			vx6953_ctrl->fps) * Q8) / Q10) + 1;
+	}
+	if (vx6953_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+		return rc;
+	if (vx6953_ctrl->set_test) {
+		if (vx6953_test(vx6953_ctrl->set_test) < 0)
+			return	rc;
+	}
+	vx6953_ctrl->edof_mode = VX6953_EDOF_ESTIMATION;
+	rc = vx6953_enable_edof(vx6953_ctrl->edof_mode);
+	if (rc < 0)
+		return rc;
+	vx6953_ctrl->curr_res = vx6953_ctrl->prev_res;
+	vx6953_ctrl->sensormode = mode;
+	return rc;
+}
+
+static int32_t vx6953_snapshot_config(int mode)
+{
+	int32_t rc = 0;
+	int rt;
+	/*change sensor resolution if needed */
+	if (vx6953_ctrl->curr_res != vx6953_ctrl->pict_res) {
+		if (vx6953_ctrl->pict_res == QTR_SIZE) {
+			rt = RES_PREVIEW;
+			vx6953_stm5m0edof_delay_msecs_stdby =
+				((((2 * 1000 * vx6953_ctrl->fps_divider) /
+				vx6953_ctrl->fps) * Q8) / Q10) + 1;
+		} else {
+			rt = RES_CAPTURE;
+			vx6953_stm5m0edof_delay_msecs_stdby =
+				((((1000 * vx6953_ctrl->fps_divider) /
+				vx6953_ctrl->fps) * Q8) / Q10) + 1;
+		}
+	if (vx6953_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+		return rc;
+	}
+
+	vx6953_ctrl->edof_mode = VX6953_EDOF_APPLICATION;
+	if (vx6953_enable_edof(vx6953_ctrl->edof_mode) < 0)
+		return rc;
+	vx6953_ctrl->curr_res = vx6953_ctrl->pict_res;
+	vx6953_ctrl->sensormode = mode;
+	return rc;
+} /*end of vx6953_snapshot_config*/
+
+static int32_t vx6953_raw_snapshot_config(int mode)
+{
+	int32_t rc = 0;
+	int rt;
+	/* change sensor resolution if needed */
+	if (vx6953_ctrl->curr_res != vx6953_ctrl->pict_res) {
+		if (vx6953_ctrl->pict_res == QTR_SIZE) {
+			rt = RES_PREVIEW;
+			vx6953_stm5m0edof_delay_msecs_stdby =
+				((((2 * 1000 * vx6953_ctrl->fps_divider)/
+				vx6953_ctrl->fps) * Q8) / Q10) + 1;
+		} else {
+			rt = RES_CAPTURE;
+			vx6953_stm5m0edof_delay_msecs_stdby =
+				((((1000 * vx6953_ctrl->fps_divider)/
+				vx6953_ctrl->fps) * Q8) / Q10) + 1;
+		}
+		if (vx6953_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+			return rc;
+	}
+	vx6953_ctrl->edof_mode = VX6953_EDOF_APPLICATION;
+	if (vx6953_enable_edof(vx6953_ctrl->edof_mode) < 0)
+		return rc;
+	vx6953_ctrl->curr_res = vx6953_ctrl->pict_res;
+	vx6953_ctrl->sensormode = mode;
+	return rc;
+} /*end of vx6953_raw_snapshot_config*/
+static int32_t vx6953_set_sensor_mode(int mode,
+	int res)
+{
+	int32_t rc = 0;
+	switch (mode) {
+	case SENSOR_PREVIEW_MODE:
+		rc = vx6953_video_config(mode);
+		break;
+	case SENSOR_SNAPSHOT_MODE:
+		rc = vx6953_snapshot_config(mode);
+		break;
+	case SENSOR_RAW_SNAPSHOT_MODE:
+		rc = vx6953_raw_snapshot_config(mode);
+		break;
+	default:
+		rc = -EINVAL;
+		break;
+	}
+	return rc;
+}
+static int32_t vx6953_power_down(void)
+{
+	vx6953_i2c_write_b_sensor(REG_MODE_SELECT,
+	MODE_SELECT_STANDBY_MODE);
+	return 0;
+}
+
+
+static int vx6953_probe_init_done(const struct msm_camera_sensor_info *data)
+{
+	gpio_free(data->sensor_reset);
+	kfree(vx6953_ctrl);
+	vx6953_ctrl = NULL;
+	return 0;
+}
+static int vx6953_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+	unsigned short revision_number;
+	int32_t rc = 0;
+	unsigned short chipidl, chipidh;
+	CDBG("%s: %d\n", __func__, __LINE__);
+	rc = gpio_request(data->sensor_reset, "vx6953");
+	CDBG(" vx6953_probe_init_sensor\n");
+	if (!rc) {
+		CDBG("sensor_reset = %d\n", rc);
+		CDBG(" vx6953_probe_init_sensor 1\n");
+		gpio_direction_output(data->sensor_reset, 0);
+		msleep(50);
+		CDBG(" vx6953_probe_init_sensor 1\n");
+		gpio_direction_output(data->sensor_reset, 1);
+		msleep(13);
+	} else {
+		CDBG(" vx6953_probe_init_sensor 2\n");
+		goto init_probe_done;
+	}
+	msleep(20);
+	CDBG(" vx6953_probe_init_sensor is called\n");
+	/* 3. Read sensor Model ID: */
+	rc = vx6953_i2c_read(0x0000, &chipidh, 1);
+	if (rc < 0) {
+		CDBG(" vx6953_probe_init_sensor 3\n");
+		goto init_probe_fail;
+	}
+	rc = vx6953_i2c_read(0x0001, &chipidl, 1);
+	if (rc < 0) {
+		CDBG(" vx6953_probe_init_sensor4\n");
+		goto init_probe_fail;
+	}
+	CDBG("vx6953 model_id = 0x%x  0x%x\n", chipidh, chipidl);
+	/* 4. Compare sensor ID to VX6953 ID: */
+	if (chipidh != 0x03 || chipidl != 0xB9) {
+		rc = -ENODEV;
+		CDBG("vx6953_probe_init_sensor fail chip id doesnot match\n");
+		goto init_probe_fail;
+	}
+
+	vx6953_ctrl = kzalloc(sizeof(struct vx6953_ctrl_t), GFP_KERNEL);
+	if (!vx6953_ctrl) {
+		CDBG("vx6953_init failed!\n");
+		rc = -ENOMEM;
+	}
+	vx6953_ctrl->fps_divider = 1 * 0x00000400;
+	vx6953_ctrl->pict_fps_divider = 1 * 0x00000400;
+	vx6953_ctrl->set_test = TEST_OFF;
+	vx6953_ctrl->prev_res = QTR_SIZE;
+	vx6953_ctrl->pict_res = FULL_SIZE;
+	vx6953_ctrl->curr_res = INVALID_SIZE;
+	vx6953_ctrl->sensor_type = VX6953_STM5M0EDOF_CUT_2;
+	vx6953_ctrl->edof_mode = VX6953_EDOF_ESTIMATION;
+
+	if (data)
+		vx6953_ctrl->sensordata = data;
+
+	if (vx6953_i2c_read(0x0002, &revision_number, 1) < 0)
+		return rc;
+		CDBG("sensor revision number major = 0x%x\n", revision_number);
+	if (vx6953_i2c_read(0x0018, &revision_number, 1) < 0)
+		return rc;
+		CDBG("sensor revision number = 0x%x\n", revision_number);
+	if (revision_number == VX6953_REVISION_NUMBER_CUT3) {
+		vx6953_ctrl->sensor_type = VX6953_STM5M0EDOF_CUT_3;
+		CDBG("VX6953 EDof Cut 3.0 sensor\n ");
+	} else if (revision_number == VX6953_REVISION_NUMBER_CUT2) {
+		vx6953_ctrl->sensor_type = VX6953_STM5M0EDOF_CUT_2;
+		CDBG("VX6953 EDof Cut 2.0 sensor\n ");
+	} else {/* Cut1.0 reads 0x00 for register 0x0018*/
+		vx6953_ctrl->sensor_type = VX6953_STM5M0EDOF_CUT_1;
+		CDBG("VX6953 EDof Cut 1.0 sensor\n ");
+	}
+
+	if (vx6953_ctrl->prev_res == QTR_SIZE) {
+		if (vx6953_sensor_setting(REG_INIT, RES_PREVIEW) < 0)
+			goto init_probe_fail;
+	} else {
+		if (vx6953_sensor_setting(REG_INIT, RES_CAPTURE) < 0)
+			goto init_probe_fail;
+	}
+
+	goto init_probe_done;
+init_probe_fail:
+	CDBG(" vx6953_probe_init_sensor fails\n");
+	gpio_direction_output(data->sensor_reset, 0);
+	vx6953_probe_init_done(data);
+init_probe_done:
+	CDBG(" vx6953_probe_init_sensor finishes\n");
+	return rc;
+	}
+/* camsensor_iu060f_vx6953_reset */
+int vx6953_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+	unsigned short revision_number;
+	int32_t rc = 0;
+
+	CDBG("%s: %d\n", __func__, __LINE__);
+	CDBG("Calling vx6953_sensor_open_init\n");
+	rc = gpio_request(data->sensor_reset, "vx6953");
+	if (!rc)
+		CDBG("vx6953 gpio_request fail\n");
+
+	vx6953_ctrl = kzalloc(sizeof(struct vx6953_ctrl_t), GFP_KERNEL);
+	if (!vx6953_ctrl) {
+		CDBG("vx6953_init failed!\n");
+		rc = -ENOMEM;
+		goto init_done;
+	}
+	vx6953_ctrl->fps_divider = 1 * 0x00000400;
+	vx6953_ctrl->pict_fps_divider = 1 * 0x00000400;
+	vx6953_ctrl->set_test = TEST_OFF;
+	vx6953_ctrl->prev_res = QTR_SIZE;
+	vx6953_ctrl->pict_res = FULL_SIZE;
+	vx6953_ctrl->curr_res = INVALID_SIZE;
+	vx6953_ctrl->sensor_type = VX6953_STM5M0EDOF_CUT_2;
+	vx6953_ctrl->edof_mode = VX6953_EDOF_ESTIMATION;
+	if (data)
+		vx6953_ctrl->sensordata = data;
+	if (rc < 0) {
+		CDBG("Calling vx6953_sensor_open_init fail1\n");
+		return rc;
+	}
+	CDBG("%s: %d\n", __func__, __LINE__);
+	/* enable mclk first */
+	msm_camio_clk_rate_set(VX6953_STM5M0EDOF_DEFAULT_MASTER_CLK_RATE);
+	CDBG("%s: %d\n", __func__, __LINE__);
+	if (vx6953_i2c_read(0x0002, &revision_number, 1) < 0)
+		return rc;
+		CDBG("sensor revision number major = 0x%x\n", revision_number);
+	if (vx6953_i2c_read(0x0018, &revision_number, 1) < 0)
+		return rc;
+		CDBG("sensor revision number = 0x%x\n", revision_number);
+	if (revision_number == VX6953_REVISION_NUMBER_CUT3) {
+		vx6953_ctrl->sensor_type = VX6953_STM5M0EDOF_CUT_3;
+		CDBG("VX6953 EDof Cut 3.0 sensor\n ");
+	} else if (revision_number == VX6953_REVISION_NUMBER_CUT2) {
+		vx6953_ctrl->sensor_type = VX6953_STM5M0EDOF_CUT_2;
+		CDBG("VX6953 EDof Cut 2.0 sensor\n ");
+	} else {/* Cut1.0 reads 0x00 for register 0x0018*/
+		vx6953_ctrl->sensor_type = VX6953_STM5M0EDOF_CUT_1;
+		CDBG("VX6953 EDof Cut 1.0 sensor\n ");
+	}
+
+	vx6953_ctrl->fps = 30*Q8;
+	if (rc < 0)
+		goto init_fail;
+	else
+		goto init_done;
+init_fail:
+	CDBG("init_fail\n");
+	gpio_direction_output(data->sensor_reset, 0);
+	vx6953_probe_init_done(data);
+init_done:
+	CDBG("init_done\n");
+	return rc;
+} /*endof vx6953_sensor_open_init*/
+
+static int vx6953_init_client(struct i2c_client *client)
+{
+	/* Initialize the MSM_CAMI2C Chip */
+	init_waitqueue_head(&vx6953_wait_queue);
+	return 0;
+}
+
+static const struct i2c_device_id vx6953_i2c_id[] = {
+	{"vx6953", 0},
+	{ }
+};
+
+static int vx6953_i2c_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	int rc = 0;
+	CDBG("vx6953_probe called!\n");
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		CDBG("i2c_check_functionality failed\n");
+		goto probe_failure;
+	}
+
+	vx6953_sensorw = kzalloc(sizeof(struct vx6953_work_t), GFP_KERNEL);
+	if (!vx6953_sensorw) {
+		CDBG("kzalloc failed.\n");
+		rc = -ENOMEM;
+		goto probe_failure;
+	}
+
+	i2c_set_clientdata(client, vx6953_sensorw);
+	vx6953_init_client(client);
+	vx6953_client = client;
+
+	msleep(50);
+
+	CDBG("vx6953_probe successed! rc = %d\n", rc);
+	return 0;
+
+probe_failure:
+	CDBG("vx6953_probe failed! rc = %d\n", rc);
+	return rc;
+}
+
+static int vx6953_send_wb_info(struct wb_info_cfg *wb)
+{
+	unsigned short read_data;
+	uint8_t temp[8];
+	int rc = 0;
+	int i = 0;
+
+	/* red_gain */
+	temp[2] = wb->red_gain >> 8;
+	temp[3] = wb->red_gain & 0xFF;
+
+	/* green_gain */
+	temp[0] = wb->green_gain >> 8;
+	temp[1] = wb->green_gain & 0xFF;
+	temp[6] = temp[0];
+	temp[7] = temp[1];
+
+	/* blue_gain */
+	temp[4] = wb->blue_gain >> 8;
+	temp[5] = wb->blue_gain & 0xFF;
+	rc = vx6953_i2c_write_seq_sensor(0x0B8E, &temp[0], 8);
+
+	for (i = 0; i < 6; i++) {
+		rc = vx6953_i2c_read(0x0B8E + i, &read_data, 1);
+		CDBG("%s addr 0x%x val %d\n", __func__, 0x0B8E + i, read_data);
+	}
+	rc = vx6953_i2c_read(0x0B82, &read_data, 1);
+	CDBG("%s addr 0x%x val %d\n", __func__, 0x0B82, read_data);
+	if (rc < 0)
+		return rc;
+	return rc;
+} /*end of vx6953_snapshot_config*/
+
+static int __exit vx6953_remove(struct i2c_client *client)
+{
+	struct vx6953_work_t_t *sensorw = i2c_get_clientdata(client);
+	free_irq(client->irq, sensorw);
+	vx6953_client = NULL;
+	kfree(sensorw);
+	return 0;
+}
+
+static struct i2c_driver vx6953_i2c_driver = {
+	.id_table = vx6953_i2c_id,
+	.probe  = vx6953_i2c_probe,
+	.remove = __exit_p(vx6953_i2c_remove),
+	.driver = {
+		.name = "vx6953",
+	},
+};
+
+static int vx6953_sensor_config(void __user *argp)
+{
+	struct sensor_cfg_data cdata;
+	long   rc = 0;
+	if (copy_from_user(&cdata,
+		(void *)argp,
+		sizeof(struct sensor_cfg_data)))
+		return -EFAULT;
+	mutex_lock(&vx6953_mut);
+	CDBG("vx6953_sensor_config: cfgtype = %d\n",
+	cdata.cfgtype);
+		switch (cdata.cfgtype) {
+		case CFG_GET_PICT_FPS:
+			vx6953_get_pict_fps(
+				cdata.cfg.gfps.prevfps,
+				&(cdata.cfg.gfps.pictfps));
+
+			if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+				rc = -EFAULT;
+			break;
+
+		case CFG_GET_PREV_L_PF:
+			cdata.cfg.prevl_pf =
+			vx6953_get_prev_lines_pf();
+
+			if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+				rc = -EFAULT;
+			break;
+
+		case CFG_GET_PREV_P_PL:
+			cdata.cfg.prevp_pl =
+				vx6953_get_prev_pixels_pl();
+
+			if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+				rc = -EFAULT;
+			break;
+
+		case CFG_GET_PICT_L_PF:
+			cdata.cfg.pictl_pf =
+				vx6953_get_pict_lines_pf();
+
+			if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+				rc = -EFAULT;
+			break;
+
+		case CFG_GET_PICT_P_PL:
+			cdata.cfg.pictp_pl =
+				vx6953_get_pict_pixels_pl();
+
+			if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+				rc = -EFAULT;
+			break;
+
+		case CFG_GET_PICT_MAX_EXP_LC:
+			cdata.cfg.pict_max_exp_lc =
+				vx6953_get_pict_max_exp_lc();
+
+			if (copy_to_user((void *)argp,
+				&cdata,
+				sizeof(struct sensor_cfg_data)))
+				rc = -EFAULT;
+			break;
+
+		case CFG_SET_FPS:
+		case CFG_SET_PICT_FPS:
+			rc = vx6953_set_fps(&(cdata.cfg.fps));
+			break;
+
+		case CFG_SET_EXP_GAIN:
+			rc =
+				vx6953_write_exp_gain(
+					cdata.cfg.exp_gain.gain,
+					cdata.cfg.exp_gain.line);
+			break;
+
+		case CFG_SET_PICT_EXP_GAIN:
+			rc =
+				vx6953_set_pict_exp_gain(
+				cdata.cfg.exp_gain.gain,
+				cdata.cfg.exp_gain.line);
+			break;
+
+		case CFG_SET_MODE:
+			rc = vx6953_set_sensor_mode(cdata.mode,
+					cdata.rs);
+			break;
+
+		case CFG_PWR_DOWN:
+			rc = vx6953_power_down();
+			break;
+
+		case CFG_MOVE_FOCUS:
+			rc =
+				vx6953_move_focus(
+				cdata.cfg.focus.dir,
+				cdata.cfg.focus.steps);
+			break;
+
+		case CFG_SET_DEFAULT_FOCUS:
+			rc =
+				vx6953_set_default_focus(
+				cdata.cfg.focus.steps);
+			break;
+
+		case CFG_SET_EFFECT:
+			rc = vx6953_set_default_focus(
+				cdata.cfg.effect);
+			break;
+
+
+		case CFG_SEND_WB_INFO:
+			rc = vx6953_send_wb_info(
+				&(cdata.cfg.wb_info));
+			break;
+
+		default:
+			rc = -EFAULT;
+			break;
+		}
+
+	mutex_unlock(&vx6953_mut);
+
+	return rc;
+}
+
+
+
+
+static int vx6953_sensor_release(void)
+{
+	int rc = -EBADF;
+	mutex_lock(&vx6953_mut);
+	vx6953_power_down();
+	gpio_free(vx6953_ctrl->sensordata->sensor_reset);
+	kfree(vx6953_ctrl);
+	vx6953_ctrl = NULL;
+	CDBG("vx6953_release completed\n");
+	mutex_unlock(&vx6953_mut);
+
+	return rc;
+}
+
+static int vx6953_g_chip_ident(struct v4l2_subdev *sd,
+			struct v4l2_dbg_chip_ident *id)
+{
+	/* TODO: Need to add this ID in v4l2-chip-ident.h */
+	id->ident    = V4L2_IDENT_VX6953;
+	id->revision = 0;
+
+	return 0;
+}
+
+static int vx6953_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *param)
+{
+	int ret = 0;
+	/* return current mode value */
+	param->parm.capture.capturemode = vx6953_ctrl->sensormode;
+	return ret;
+}
+
+static int vx6953_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *param)
+{
+	/* set the desired mode */
+	/* right now, the only purpose is to set the desired mode -
+	 preview or snapshot */
+	vx6953_ctrl->sensormode = param->parm.capture.capturemode;
+	return 0;
+}
+
+static int vx6953_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	long rc = 0;
+	int mode = vx6953_ctrl->sensormode;
+	int rt = RES_PREVIEW;
+	unsigned short frame_cnt;
+	struct msm_camera_csi_params vx6953_csi_params;
+
+	CDBG("mode = %d, enable = %d\n", mode, enable);
+
+	if (!enable) {
+		/* turn off streaming */
+		/* TODO: Make call to I2C write to turn streaming off */
+		/* rc = vx6953_i2c_write_b_sensor(); */
+
+		struct vx6953_i2c_reg_conf init_tbl[] = {
+			{REG_0x0112,
+			vx6953_regs.reg_pat_init[0].reg_0x0112},
+			{0x6003, 0x01},
+			{REG_0x0113,
+			vx6953_regs.reg_pat_init[0].reg_0x0113},
+			{REG_VT_PIX_CLK_DIV,
+			vx6953_regs.reg_pat_init[0].
+			vt_pix_clk_div},
+			{REG_PRE_PLL_CLK_DIV,
+			vx6953_regs.reg_pat_init[0].
+			pre_pll_clk_div},
+			{REG_PLL_MULTIPLIER,
+			vx6953_regs.reg_pat_init[0].
+			pll_multiplier},
+			{REG_OP_PIX_CLK_DIV,
+			vx6953_regs.reg_pat_init[0].
+			op_pix_clk_div},
+			{REG_COARSE_INTEGRATION_TIME_HI,
+			vx6953_regs.reg_pat[rt].
+			coarse_integration_time_hi},
+			{REG_COARSE_INTEGRATION_TIME_LO,
+			vx6953_regs.reg_pat[rt].
+			coarse_integration_time_lo},
+			{REG_ANALOGUE_GAIN_CODE_GLOBAL_LO,
+			vx6953_regs.reg_pat[rt].
+			analogue_gain_code_global},
+			{REG_0x3030,
+			vx6953_regs.reg_pat_init[0].reg_0x3030},
+			/* 953 specific registers */
+			{REG_0x0111,
+			vx6953_regs.reg_pat_init[0].reg_0x0111},
+			{REG_0x0b00,
+			vx6953_regs.reg_pat_init[0].reg_0x0b00},
+			{REG_0x3001,
+			vx6953_regs.reg_pat_init[0].reg_0x3001},
+			{REG_0x3004,
+			vx6953_regs.reg_pat_init[0].reg_0x3004},
+			{0x3006, 0x00},
+			{REG_0x3007,
+			vx6953_regs.reg_pat_init[0].reg_0x3007},
+			{0x301b, 0x29},
+			/* DEFCOR settings */
+			/*Single Defect Correction Weight DISABLE*/
+			{0x0b06,
+			vx6953_regs.reg_pat_init[0].reg_0x0b06},
+			/*Single_defect_correct_weight = auto*/
+			{0x0b07,
+			vx6953_regs.reg_pat_init[0].reg_0x0b07},
+			/*Dynamic couplet correction ENABLED*/
+			{0x0b08,
+			vx6953_regs.reg_pat_init[0].reg_0x0b08},
+			/*Dynamic couplet correction weight*/
+			{0x0b09,
+			vx6953_regs.reg_pat_init[0].reg_0x0b09},
+			/* Clock Setup */
+			/* Tell sensor ext clk is 24MHz*/
+			{REG_0x0136,
+			vx6953_regs.reg_pat_init[0].reg_0x0136},
+			{REG_0x0137,
+			vx6953_regs.reg_pat_init[0].reg_0x0137},
+			/* The white balance gains must be written
+			 to the sensor every frame. */
+			/* Edof */
+			{REG_0x0b83,
+			vx6953_regs.reg_pat_init[0].reg_0x0b83},
+			{REG_0x0b84,
+			vx6953_regs.reg_pat_init[0].reg_0x0b84},
+			{REG_0x0b85,
+			vx6953_regs.reg_pat_init[0].reg_0x0b85},
+			{REG_0x0b88,
+			vx6953_regs.reg_pat_init[0].reg_0x0b88},
+			{REG_0x0b89,
+			vx6953_regs.reg_pat_init[0].reg_0x0b89},
+			{REG_0x0b8a,
+			vx6953_regs.reg_pat_init[0].reg_0x0b8a},
+			/* Mode specific regieters */
+			{REG_FRAME_LENGTH_LINES_HI,
+			vx6953_regs.reg_pat[rt].
+			frame_length_lines_hi},
+			{REG_FRAME_LENGTH_LINES_LO,
+			vx6953_regs.reg_pat[rt].
+			frame_length_lines_lo},
+			{REG_LINE_LENGTH_PCK_HI,
+			vx6953_regs.reg_pat[rt].
+			line_length_pck_hi},
+			{REG_LINE_LENGTH_PCK_LO,
+			vx6953_regs.reg_pat[rt].
+			line_length_pck_lo},
+			{REG_0x3005,
+			vx6953_regs.reg_pat[rt].reg_0x3005},
+			{0x3010,
+			vx6953_regs.reg_pat[rt].reg_0x3010},
+			{REG_0x3011,
+			vx6953_regs.reg_pat[rt].reg_0x3011},
+			{REG_0x301a,
+			vx6953_regs.reg_pat[rt].reg_0x301a},
+			{REG_0x3035,
+			vx6953_regs.reg_pat[rt].reg_0x3035},
+			{REG_0x3036,
+			vx6953_regs.reg_pat[rt].reg_0x3036},
+			{REG_0x3041,
+			vx6953_regs.reg_pat[rt].reg_0x3041},
+			{0x3042,
+			vx6953_regs.reg_pat[rt].reg_0x3042},
+			{REG_0x3045,
+			vx6953_regs.reg_pat[rt].reg_0x3045},
+			/*EDOF: Estimation settings for Preview mode
+			  Application settings for capture mode
+			  (standard settings - Not tuned) */
+			{REG_0x0b80,
+			vx6953_regs.reg_pat[rt].reg_0x0b80},
+			{REG_0x0900,
+			vx6953_regs.reg_pat[rt].reg_0x0900},
+			{REG_0x0901,
+			vx6953_regs.reg_pat[rt].reg_0x0901},
+			{REG_0x0902,
+			vx6953_regs.reg_pat[rt].reg_0x0902},
+			{REG_0x0383,
+			vx6953_regs.reg_pat[rt].reg_0x0383},
+			{REG_0x0387,
+			vx6953_regs.reg_pat[rt].reg_0x0387},
+			/* Change output size / frame rate */
+			{REG_0x034c,
+			vx6953_regs.reg_pat[rt].reg_0x034c},
+			{REG_0x034d,
+			vx6953_regs.reg_pat[rt].reg_0x034d},
+			{REG_0x034e,
+			vx6953_regs.reg_pat[rt].reg_0x034e},
+			{REG_0x034f,
+			vx6953_regs.reg_pat[rt].reg_0x034f},
+		};
+		/* reset fps_divider */
+		vx6953_ctrl->fps = 30 * Q8;
+		/* stop streaming */
+
+		/* Reset everything first */
+		if (vx6953_i2c_write_b_sensor(0x103, 0x01) < 0) {
+			CDBG("S/W reset failed\n");
+			return rc;
+		} else
+			CDBG("S/W reset successful\n");
+
+		msleep(10);
+
+		CDBG("Init vx6953_sensor_setting standby\n");
+		if (vx6953_i2c_write_b_sensor(REG_MODE_SELECT,
+				    MODE_SELECT_STANDBY_MODE) < 0)
+			return rc;
+
+		/*vx6953_stm5m0edof_delay_msecs_stdby*/
+		msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+		vx6953_csi_params.data_format = CSI_8BIT;
+		vx6953_csi_params.lane_cnt = 1;
+		vx6953_csi_params.lane_assign = 0xe4;
+		vx6953_csi_params.dpcm_scheme = 0;
+		vx6953_csi_params.settle_cnt = 7;
+		rc = msm_camio_csi_config(&vx6953_csi_params);
+		if (rc < 0)
+			CDBG(" config csi controller failed\n");
+		msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+		vx6953_patch_for_cut3();
+		rc = vx6953_i2c_write_w_table(&init_tbl[0],
+					    ARRAY_SIZE(init_tbl));
+		if (rc < 0)
+			return rc;
+
+		msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+		vx6953_i2c_write_b_sensor(0x0b80, 0x00);
+		vx6953_i2c_write_b_sensor(0x3388, 0x03);
+		vx6953_i2c_write_b_sensor(0x3640, 0x00);
+		return rc;
+	} else {
+		/* Start sensor streaming */
+		if (vx6953_i2c_write_b_sensor(REG_MODE_SELECT,
+					    MODE_SELECT_STREAM) < 0)
+			return rc;
+		CDBG("Init vx6953_sensor_setting stream\n");
+		msleep(vx6953_stm5m0edof_delay_msecs_stream);
+		if (vx6953_i2c_read(0x0005, &frame_cnt, 1) < 0)
+			return rc;
+
+		rc = vx6953_i2c_write_w_table(&edof_tbl[0],
+					    ARRAY_SIZE(edof_tbl));
+		vx6953_i2c_write_b_sensor(0x3388, 0x00);
+
+		while (frame_cnt == 0xFF) {
+			if (vx6953_i2c_read(0x0005, &frame_cnt, 1) < 0)
+				return rc;
+			CDBG("frame_cnt=%d", frame_cnt);
+			msleep(10);
+		}
+
+		/* set desired mode */
+		switch (mode) {
+		case SENSOR_PREVIEW_MODE:
+			CDBG("SENSOR_PREVIEW_MODE\n");
+			rc = vx6953_video_config(mode);
+			break;
+		case SENSOR_SNAPSHOT_MODE:
+			CDBG("SENSOR_SNAPSHOT_MODE\n");
+			rc = vx6953_snapshot_config(mode);
+			break;
+		case SENSOR_RAW_SNAPSHOT_MODE:
+			CDBG("SENSOR_RAW_SNAPSHOT_MODE\n");
+			rc = vx6953_raw_snapshot_config(mode);
+			break;
+		default:
+			CDBG("default\n");
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static void vx6953_frame_check(u32 *width, u32 *height)
+{
+	/* get mode first */
+	int mode = vx6953_ctrl->sensormode;
+
+	switch (mode) {
+	case SENSOR_PREVIEW_MODE:
+		if (*width > VX6953_QTR_SIZE_WIDTH)
+			*width = VX6953_QTR_SIZE_WIDTH;
+
+		if (*height > VX6953_QTR_SIZE_HEIGHT)
+			*height = VX6953_QTR_SIZE_HEIGHT;
+		break;
+	case SENSOR_SNAPSHOT_MODE:
+	case SENSOR_RAW_SNAPSHOT_MODE:
+		if (*width > VX6953_HRZ_FULL_BLK_PIXELS)
+			*width = VX6953_HRZ_FULL_BLK_PIXELS;
+
+		if (*height > VX6953_VER_FULL_BLK_LINES)
+			*height = VX6953_VER_FULL_BLK_LINES;
+		break;
+	default:
+		break;
+	}
+}
+
+
+static int vx6953_set_params(struct i2c_client *client, u32 width, u32 height,
+			     enum v4l2_mbus_pixelcode code)
+{
+	int i;
+	vx6953_ctrl->fmt = NULL;
+
+	/*
+	 * frame size check
+	 */
+	vx6953_frame_check(&width, &height);
+
+	/*
+	 * get color format
+	 */
+	for (i = 0; i < ARRAY_SIZE(vx6953_cfmts); i++)
+		if (vx6953_cfmts[i].code == code)
+			break;
+	if (i == ARRAY_SIZE(vx6953_cfmts))
+		return -EINVAL;
+
+	/* sensor supports one fixed size depending upon the mode */
+	switch (vx6953_ctrl->sensormode) {
+	case SENSOR_PREVIEW_MODE:
+		vx6953_video_config(vx6953_ctrl->sensormode);
+		break;
+	case SENSOR_SNAPSHOT_MODE:
+		vx6953_snapshot_config(vx6953_ctrl->sensormode);
+		break;
+	case SENSOR_RAW_SNAPSHOT_MODE:
+		vx6953_raw_snapshot_config(vx6953_ctrl->sensormode);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* why need this ? vx6953_ctrl->fmt = &(vx6953_cfmts[i]); */
+
+	return 0;
+}
+
+static int vx6953_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+	/* right now we are not supporting, probably vfe can take care */
+	return -EINVAL;
+}
+
+static int vx6953_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+	return -EINVAL;
+}
+
+static int vx6953_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+	return -EINVAL;
+}
+
+static int vx6953_g_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
+{
+	/* by this time vx6953_client should already be set */
+	struct i2c_client *client = vx6953_client;
+
+	/* currently sensor supports fixed dimensions only
+	 * depending upon the mode*/
+	if (!vx6953_ctrl->fmt) {
+		int ret = vx6953_set_params(client, VX6953_QTR_SIZE_WIDTH,
+						VX6953_QTR_SIZE_HEIGHT,
+						V4L2_MBUS_FMT_YUYV8_2X8);
+		if (ret < 0)
+			return ret;
+	}
+
+	mf->width = vx6953_get_pict_pixels_pl();
+	mf->height  = vx6953_get_pict_lines_pf();
+	/* TODO: set colorspace */
+	mf->code  = vx6953_ctrl->fmt->code;
+	mf->field = V4L2_FIELD_NONE;
+
+	return 0;
+}
+
+static int vx6953_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
+{
+	/* by this time vx6953_client should already be set */
+	struct i2c_client *client = vx6953_client;
+
+	/* TODO: We need to define this function */
+	/* TODO: set colorspace */
+	return vx6953_set_params(client, mf->width, mf->height, mf->code);
+}
+
+static int vx6953_try_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(vx6953_cfmts); i++)
+		if (mf->code == vx6953_cfmts[i].code)
+			break;
+
+	if (i == ARRAY_SIZE(vx6953_cfmts))
+		return -EINVAL;
+
+	/* check that frame is within max sensor supported frame size */
+	vx6953_frame_check(&mf->width, &mf->height);
+
+	/* TODO: set colorspace */
+	mf->field = V4L2_FIELD_NONE;
+
+	return 0;
+}
+
+static int vx6953_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+			   enum v4l2_mbus_pixelcode *code)
+{
+	printk(KERN_DEBUG "Index is %d\n", index);
+	if ((unsigned int)index >= ARRAY_SIZE(vx6953_cfmts))
+		return -EINVAL;
+
+	*code = vx6953_cfmts[index].code;
+	return 0;
+}
+
+static struct v4l2_subdev_core_ops vx6953_subdev_core_ops = {
+	.g_chip_ident = vx6953_g_chip_ident,
+};
+
+static struct v4l2_subdev_video_ops vx6953_subdev_video_ops = {
+	.g_parm			   = vx6953_g_parm,
+	.s_parm			   = vx6953_s_parm,
+	.s_stream = vx6953_s_stream,
+	.g_mbus_fmt = vx6953_g_fmt,
+	.s_mbus_fmt = vx6953_s_fmt,
+	.try_mbus_fmt = vx6953_try_fmt,
+	.cropcap  = vx6953_cropcap,
+	.g_crop   = vx6953_g_crop,
+	.s_crop   = vx6953_s_crop,
+	.enum_mbus_fmt  = vx6953_enum_fmt,
+};
+
+static struct v4l2_subdev_ops vx6953_subdev_ops = {
+	.core = &vx6953_subdev_core_ops,
+	.video  = &vx6953_subdev_video_ops,
+};
+
+static int vx6953_sensor_probe(const struct msm_camera_sensor_info *info,
+		struct msm_sensor_ctrl *s)
+{
+	int rc = 0;
+	rc = i2c_add_driver(&vx6953_i2c_driver);
+	if (rc < 0 || vx6953_client == NULL) {
+		rc = -ENOTSUPP;
+		goto probe_fail;
+	}
+	msm_camio_clk_rate_set(24000000);
+	rc = vx6953_probe_init_sensor(info);
+	if (rc < 0)
+		goto probe_fail;
+	s->s_init = vx6953_sensor_open_init;
+	s->s_release = vx6953_sensor_release;
+	s->s_config  = vx6953_sensor_config;
+	vx6953_probe_init_done(info);
+	return rc;
+
+probe_fail:
+	CDBG("vx6953_sensor_probe: SENSOR PROBE FAILS!\n");
+	return rc;
+}
+
+
+static int vx6953_sensor_probe_cb(const struct msm_camera_sensor_info *info,
+	struct v4l2_subdev *sdev, struct msm_sensor_ctrl *s)
+{
+	int rc = 0;
+	rc = vx6953_sensor_probe(info, s);
+	if (rc < 0)
+		return rc;
+
+	vx6953_ctrl = kzalloc(sizeof(struct vx6953_ctrl_t), GFP_KERNEL);
+	if (!vx6953_ctrl) {
+		CDBG("vx6953_sensor_probe failed!\n");
+		return -ENOMEM;
+	}
+
+	/* probe is successful, init a v4l2 subdevice */
+	printk(KERN_DEBUG "going into v4l2_i2c_subdev_init\n");
+	if (sdev) {
+		v4l2_i2c_subdev_init(sdev, vx6953_client,
+						&vx6953_subdev_ops);
+		vx6953_ctrl->sensor_dev = sdev;
+	}
+	return rc;
+}
+
+static int __vx6953_probe(struct platform_device *pdev)
+{
+	return msm_sensor_register(pdev, vx6953_sensor_probe_cb);
+}
+
+static struct platform_driver msm_camera_driver = {
+	.probe = __vx6953_probe,
+	.driver = {
+		.name = "msm_camera_vx6953",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init vx6953_init(void)
+{
+	return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(vx6953_init);
+void vx6953_exit(void)
+{
+	i2c_del_driver(&vx6953_i2c_driver);
+}
+
+
diff --git a/drivers/media/video/msm/vx6953_v4l2.h b/drivers/media/video/msm/vx6953_v4l2.h
new file mode 100644
index 0000000..e5428e9
--- /dev/null
+++ b/drivers/media/video/msm/vx6953_v4l2.h
@@ -0,0 +1,136 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef VX6953_V4L2_H
+#define VX6953_V4L2_H
+#include <linux/types.h>
+#include <mach/board.h>
+extern struct vx6953_reg vx6953_regs;
+struct reg_struct_init {
+	uint8_t reg_0x0112;      /* 0x0112*/
+	uint8_t reg_0x0113;      /* 0x0113*/
+	uint8_t vt_pix_clk_div;  /* 0x0301*/
+	uint8_t pre_pll_clk_div; /* 0x0305*/
+	uint8_t pll_multiplier;  /* 0x0307*/
+	uint8_t op_pix_clk_div;  /* 0x0309*/
+	uint8_t reg_0x3030;      /*0x3030*/
+	uint8_t reg_0x0111;      /*0x0111*/
+	uint8_t reg_0x0b00;      /*0x0b00*/
+	uint8_t reg_0x3001;      /*0x3001*/
+	uint8_t reg_0x3004;      /*0x3004*/
+	uint8_t reg_0x3007;      /*0x3007*/
+	uint8_t reg_0x3016;      /*0x3016*/
+	uint8_t reg_0x301d;      /*0x301d*/
+	uint8_t reg_0x317e;      /*0x317E*/
+	uint8_t reg_0x317f;      /*0x317F*/
+	uint8_t reg_0x3400;      /*0x3400*/
+	uint8_t reg_0x0b06;      /*0x0b06*/
+	uint8_t reg_0x0b07;      /*0x0b07*/
+	uint8_t reg_0x0b08;      /*0x0b08*/
+	uint8_t reg_0x0b09;      /*0x0b09*/
+	uint8_t reg_0x0136;
+	uint8_t reg_0x0137;
+	/* Edof */
+	uint8_t reg_0x0b83;      /*0x0b83*/
+	uint8_t reg_0x0b84;      /*0x0b84*/
+	uint8_t reg_0x0b85;      /*0x0b85*/
+	uint8_t reg_0x0b88;      /*0x0b88*/
+	uint8_t reg_0x0b89;      /*0x0b89*/
+	uint8_t reg_0x0b8a;      /*0x0b8a*/
+	};
+struct reg_struct {
+	uint8_t coarse_integration_time_hi; /*REG_COARSE_INTEGRATION_TIME_HI*/
+	uint8_t coarse_integration_time_lo; /*REG_COARSE_INTEGRATION_TIME_LO*/
+	uint8_t analogue_gain_code_global;
+	uint8_t frame_length_lines_hi; /* 0x0340*/
+	uint8_t frame_length_lines_lo; /* 0x0341*/
+	uint8_t line_length_pck_hi;    /* 0x0342*/
+	uint8_t line_length_pck_lo;    /* 0x0343*/
+	uint8_t reg_0x3005;   /* 0x3005*/
+	uint8_t reg_0x3010;  /* 0x3010*/
+	uint8_t reg_0x3011;  /* 0x3011*/
+	uint8_t reg_0x301a;  /* 0x301a*/
+	uint8_t reg_0x3035;  /* 0x3035*/
+	uint8_t reg_0x3036;   /* 0x3036*/
+	uint8_t reg_0x3041;  /*0x3041*/
+	uint8_t reg_0x3042;  /*0x3042*/
+	uint8_t reg_0x3045;  /*0x3045*/
+	uint8_t reg_0x0b80;   /* 0x0b80*/
+	uint8_t reg_0x0900;   /*0x0900*/
+	uint8_t reg_0x0901;   /* 0x0901*/
+	uint8_t reg_0x0902;   /*0x0902*/
+	uint8_t reg_0x0383;   /*0x0383*/
+	uint8_t reg_0x0387;   /* 0x0387*/
+	uint8_t reg_0x034c;   /* 0x034c*/
+	uint8_t reg_0x034d;   /*0x034d*/
+	uint8_t reg_0x034e;   /* 0x034e*/
+	uint8_t reg_0x034f;   /* 0x034f*/
+	uint8_t reg_0x1716; /*0x1716*/
+	uint8_t reg_0x1717; /*0x1717*/
+	uint8_t reg_0x1718; /*0x1718*/
+	uint8_t reg_0x1719; /*0x1719*/
+	uint8_t reg_0x3210;/*0x3210*/
+	uint8_t reg_0x111; /*0x111*/
+	uint8_t reg_0x3410;  /*0x3410*/
+	uint8_t reg_0x3098;
+	uint8_t reg_0x309D;
+	uint8_t reg_0x0200;
+	uint8_t reg_0x0201;
+	};
+struct vx6953_i2c_reg_conf {
+	unsigned short waddr;
+	unsigned short wdata;
+};
+
+enum vx6953_test_mode_t {
+	TEST_OFF,
+	TEST_1,
+	TEST_2,
+	TEST_3
+};
+
+enum vx6953_resolution_t {
+	QTR_SIZE,
+	FULL_SIZE,
+	INVALID_SIZE
+};
+enum vx6953_setting {
+	RES_PREVIEW,
+	RES_CAPTURE
+};
+enum mt9p012_reg_update {
+	/* Sensor egisters that need to be updated during initialization */
+	REG_INIT,
+	/* Sensor egisters that needs periodic I2C writes */
+	UPDATE_PERIODIC,
+	/* All the sensor Registers will be updated */
+	UPDATE_ALL,
+	/* Not valid update */
+	UPDATE_INVALID
+};
+
+enum sensor_revision_t {
+	VX6953_STM5M0EDOF_CUT_1,
+	VX6953_STM5M0EDOF_CUT_2,
+	VX6953_STM5M0EDOF_CUT_3
+};
+enum edof_mode_t {
+	VX6953_EDOF_DISABLE,       /* 0x00 */
+	VX6953_EDOF_APPLICATION,   /* 0x01 */
+	VX6953_EDOF_ESTIMATION     /* 0x02 */
+};
+struct vx6953_reg {
+	const struct reg_struct_init  *reg_pat_init;
+	const struct reg_struct  *reg_pat;
+};
+#endif /* VX6953_H */
diff --git a/drivers/media/video/msm/wfd/Makefile b/drivers/media/video/msm/wfd/Makefile
new file mode 100644
index 0000000..5decaca
--- /dev/null
+++ b/drivers/media/video/msm/wfd/Makefile
@@ -0,0 +1,5 @@
+obj-y += mdp-subdev.o
+obj-y += enc-subdev.o
+obj-y += vsg-subdev.o
+obj-y += wfd-ioctl.o
+obj-y += wfd-util.o
diff --git a/drivers/media/video/msm/wfd/enc-subdev.c b/drivers/media/video/msm/wfd/enc-subdev.c
new file mode 100644
index 0000000..c94fa13
--- /dev/null
+++ b/drivers/media/video/msm/wfd/enc-subdev.c
@@ -0,0 +1,2286 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2 and
+* only version 2 as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+*/
+
+#include <media/v4l2-subdev.h>
+#include <mach/iommu_domains.h>
+#include "enc-subdev.h"
+#include "wfd-util.h"
+#include <media/msm/vcd_api.h>
+#include <media/msm/vidc_init.h>
+#include <media/msm/vcd_property.h>
+#include <linux/time.h>
+#include <linux/ktime.h>
+
+#define VID_ENC_MAX_ENCODER_CLIENTS 1
+#define MAX_NUM_CTRLS 20
+
+struct venc_inst {
+	struct video_client_ctx venc_client;
+	void *cbdata;
+	void (*op_buffer_done)(void *cookie, u32 status,
+			struct vb2_buffer *buf);
+	void (*ip_buffer_done)(void *cookie, u32 status,
+			struct mem_region *mregion);
+	u32 width;
+	u32 height;
+	int secure;
+};
+
+struct venc {
+	s32 device_handle;
+	void *virt_base;
+	struct venc_inst venc_clients[VID_ENC_MAX_ENCODER_CLIENTS];
+	struct mutex lock;
+	struct ion_client *iclient;
+};
+
+static struct venc venc_p;
+
+static void *venc_map_dev_base_addr(void *device_name)
+{
+		return venc_p.virt_base;
+}
+
+static void venc_interrupt_deregister(void)
+{
+}
+
+static void venc_interrupt_register(void *device_name)
+{
+}
+
+static void venc_interrupt_clear(void)
+{
+}
+
+int venc_load_fw(struct v4l2_subdev *sd)
+{
+	return !vidc_load_firmware();
+}
+
+static u32 venc_get_empty_client_index(void)
+{
+	u32 i;
+	u32 found = false;
+
+	for (i = 0; i < VID_ENC_MAX_ENCODER_CLIENTS; i++) {
+		if (!venc_p.venc_clients[i].venc_client.vcd_handle) {
+			found = true;
+			break;
+		}
+	}
+	if (!found) {
+		WFD_MSG_ERR("%s():ERROR No space for new client\n",
+				__func__);
+		return -ENOMEM;
+	}
+	WFD_MSG_INFO("%s(): available client index = %u\n",
+				__func__, i);
+	return i;
+}
+
+int venc_init(struct v4l2_subdev *sd, u32 val)
+{
+	struct vcd_init_config vcd_init_config;
+	mutex_init(&venc_p.lock);
+	venc_p.virt_base = vidc_get_ioaddr();
+	vcd_init_config.device_name = "VIDC";
+	vcd_init_config.map_dev_base_addr = venc_map_dev_base_addr;
+	vcd_init_config.interrupt_clr = venc_interrupt_clear;
+	vcd_init_config.register_isr = venc_interrupt_register;
+	vcd_init_config.deregister_isr = venc_interrupt_deregister;
+	vcd_init(&vcd_init_config, &venc_p.device_handle);
+	return 0;
+}
+
+static void venc_notify_client(struct video_client_ctx *client_ctx)
+{
+	if (client_ctx)
+		complete(&client_ctx->event);
+}
+
+static void venc_open_done(struct video_client_ctx *client_ctx,
+	struct vcd_handle_container *handle_container)
+{
+	if (client_ctx) {
+		if (handle_container)
+			client_ctx->vcd_handle = handle_container->handle;
+		else
+			WFD_MSG_ERR("handle_container is NULL\n");
+		venc_notify_client(client_ctx);
+	} else
+		WFD_MSG_ERR("ERROR. client_ctx is NULL");
+}
+
+static void venc_start_done(struct video_client_ctx *client_ctx, u32 status)
+{
+	if (client_ctx)
+		venc_notify_client(client_ctx);
+	else
+		WFD_MSG_ERR("ERROR. client_ctx is NULL");
+}
+
+static void venc_stop_done(struct video_client_ctx *client_ctx, u32 status)
+{
+	WFD_MSG_DBG("Inside venc_stop_done: E\n");
+	if (client_ctx)
+		venc_notify_client(client_ctx);
+	else
+		WFD_MSG_ERR("ERROR. client_ctx is NULL");
+	WFD_MSG_DBG("Inside venc_stop_done: X\n");
+}
+
+static void venc_cb(u32 event, u32 status, void *info, u32 size, void *handle,
+		void *const client_data)
+{
+	struct venc_inst *inst = client_data;
+	struct video_client_ctx *client_ctx = &inst->venc_client;
+	struct vb2_buffer *vbuf;
+	struct mem_region *mregion;
+	struct vcd_frame_data *frame_data = (struct vcd_frame_data *)info;
+
+	if (!client_ctx) {
+		WFD_MSG_ERR("Client context is NULL\n");
+		return;
+	}
+	client_ctx->event_status = status;
+	switch (event) {
+	case VCD_EVT_RESP_OPEN:
+		WFD_MSG_DBG("EVENT: open done = %d\n", event);
+		venc_open_done(client_ctx,
+				(struct vcd_handle_container *)info);
+		break;
+	case VCD_EVT_RESP_INPUT_DONE:
+	case VCD_EVT_RESP_INPUT_FLUSHED:
+		WFD_MSG_DBG("EVENT: input done = %d\n", event);
+		mregion = (struct mem_region *)
+			frame_data->frm_clnt_data;
+		inst->ip_buffer_done(inst->cbdata, status, mregion);
+		break;
+	case VCD_EVT_RESP_OUTPUT_DONE:
+	case VCD_EVT_RESP_OUTPUT_FLUSHED:
+		WFD_MSG_DBG("EVENT: output done = %d\n", event);
+		vbuf = (struct vb2_buffer *)
+			frame_data->frm_clnt_data;
+		vbuf->v4l2_planes[0].bytesused =
+			frame_data->data_len;
+
+		switch (frame_data->frame) {
+		case VCD_FRAME_I:
+		case VCD_FRAME_IDR:
+			vbuf->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;
+			break;
+		case VCD_FRAME_P:
+			vbuf->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME;
+			break;
+		case VCD_FRAME_B:
+			vbuf->v4l2_buf.flags |= V4L2_BUF_FLAG_BFRAME;
+			break;
+		default:
+			break;
+		}
+
+		vbuf->v4l2_buf.timestamp =
+			ns_to_timeval(frame_data->time_stamp * NSEC_PER_USEC);
+
+		WFD_MSG_DBG("bytes used %d, ts: %d.%d, frame type is %d\n",
+				frame_data->data_len,
+				(int)vbuf->v4l2_buf.timestamp.tv_sec,
+				(int)vbuf->v4l2_buf.timestamp.tv_usec,
+				frame_data->frame);
+
+		/*
+		 * Output buffers are enc-subdev and vcd's problem, so
+		 * if buffer is cached, need to flush before giving to
+		 * client. So doing the dirty stuff in this little context
+		 */
+		{
+			unsigned long kvaddr, phys_addr;
+			s32 buffer_index = -1, ion_flags = 0;
+			struct ion_handle *ion_handle;
+			int pmem_fd;
+			struct file *filp;
+			bool rc;
+
+			rc = vidc_lookup_addr_table(client_ctx,
+					BUFFER_TYPE_OUTPUT, true,
+					(unsigned long *)&frame_data->
+					frm_clnt_data, &kvaddr, &phys_addr,
+					&pmem_fd, &filp, &buffer_index);
+
+			if (rc)
+				ion_flags = vidc_get_fd_info(client_ctx,
+					BUFFER_TYPE_OUTPUT, pmem_fd,
+					kvaddr, buffer_index, &ion_handle);
+			else
+				WFD_MSG_ERR("Got an output buffer that we "
+						"couldn't recognize!\n");
+
+			if (msm_ion_do_cache_op(client_ctx->user_ion_client,
+				ion_handle, &kvaddr, frame_data->data_len,
+				ION_IOC_CLEAN_INV_CACHES))
+				WFD_MSG_ERR("OP buffer flush failed\n");
+
+		}
+
+		inst->op_buffer_done(inst->cbdata, status, vbuf);
+		break;
+	case VCD_EVT_RESP_START:
+		WFD_MSG_DBG("EVENT: start done = %d\n", event);
+		venc_start_done(client_ctx, status);
+		/*TODO: should wait for this event*/
+		break;
+	case VCD_EVT_RESP_STOP:
+		WFD_MSG_DBG("EVENT: not expected = %d\n", event);
+		venc_stop_done(client_ctx, status);
+		break;
+	case VCD_EVT_RESP_FLUSH_INPUT_DONE:
+	case VCD_EVT_RESP_FLUSH_OUTPUT_DONE:
+		venc_notify_client(client_ctx);
+		break;
+	case VCD_EVT_RESP_PAUSE:
+	case VCD_EVT_IND_OUTPUT_RECONFIG:
+		WFD_MSG_DBG("EVENT: not expected = %d\n", event);
+		break;
+	case VCD_EVT_IND_HWERRFATAL:
+	case VCD_EVT_IND_RESOURCES_LOST:
+		WFD_MSG_DBG("EVENT: error = %d\n", event);
+		break;
+	default:
+		WFD_MSG_ERR("Invalid event type = %u\n", event);
+		break;
+	}
+}
+
+static long venc_open(struct v4l2_subdev *sd, void *arg)
+{
+	u32 client_index;
+	int rc = 0;
+	struct venc_inst *inst;
+	struct video_client_ctx *client_ctx;
+	struct venc_msg_ops *vmops  =  arg;
+	int flags = 0;
+	mutex_lock(&venc_p.lock);
+	client_index = venc_get_empty_client_index();
+	if (client_index < 0) {
+		WFD_MSG_ERR("No free clients, client_index = %d\n",
+				client_index);
+		rc = -ENODEV;
+		goto no_free_client;
+	}
+	inst = &venc_p.venc_clients[client_index];
+	client_ctx = &inst->venc_client;
+	init_completion(&client_ctx->event);
+	mutex_init(&client_ctx->msg_queue_lock);
+	mutex_init(&client_ctx->enrty_queue_lock);
+	INIT_LIST_HEAD(&client_ctx->msg_queue);
+	init_waitqueue_head(&client_ctx->msg_wait);
+	inst->op_buffer_done = vmops->op_buffer_done;
+	inst->ip_buffer_done = vmops->ip_buffer_done;
+	inst->cbdata = vmops->cbdata;
+	inst->secure = vmops->secure;
+	if (vmops->secure) {
+		WFD_MSG_ERR("OPENING SECURE SESSION\n");
+		flags |= VCD_CP_SESSION;
+	}
+	if (vcd_get_ion_status()) {
+		client_ctx->user_ion_client = vcd_get_ion_client();
+		if (!client_ctx->user_ion_client) {
+			WFD_MSG_ERR("vcd_open ion get client failed");
+			return -EFAULT;
+		}
+	}
+
+	rc = vcd_open(venc_p.device_handle, false, venc_cb,
+				inst, flags);
+	if (rc) {
+		WFD_MSG_ERR("vcd_open failed, rc = %d\n", rc);
+		rc = -ENODEV;
+		goto no_free_client;
+	}
+	wait_for_completion(&client_ctx->event);
+	if (client_ctx->event_status) {
+		WFD_MSG_ERR("callback for vcd_open returned error: %u",
+				client_ctx->event_status);
+		goto no_free_client;
+	}
+	WFD_MSG_ERR("NOTE: client_ctx = %p\n", client_ctx);
+	vmops->cookie = inst;
+	sd->dev_priv = inst;
+no_free_client:
+	mutex_unlock(&venc_p.lock);
+	return rc;
+}
+
+static long venc_close(struct v4l2_subdev *sd, void *arg)
+{
+	long rc = 0;
+	struct venc_inst *inst;
+	struct video_client_ctx *client_ctx = NULL;
+	mutex_lock(&venc_p.lock);
+	inst = sd->dev_priv;
+	client_ctx = &inst->venc_client;
+	if (!client_ctx || !client_ctx->vcd_handle) {
+		WFD_MSG_ERR("Invalid client context in close\n");
+		rc = -ENODEV;
+		goto end;
+	}
+	rc = vcd_close(client_ctx->vcd_handle);
+	if (rc) {
+		WFD_MSG_ERR("Failed to close encoder subdevice\n");
+		goto end;
+	}
+	memset((void *)client_ctx, 0,
+			sizeof(struct video_client_ctx));
+end:
+	mutex_unlock(&venc_p.lock);
+	return rc;
+}
+
+static long venc_get_buffer_req(struct v4l2_subdev *sd, void *arg)
+{
+	int rc = 0;
+	struct v4l2_requestbuffers *b = arg;
+	struct vcd_buffer_requirement buf_req;
+	struct venc_inst *inst = sd->dev_priv;
+	struct video_client_ctx *client_ctx = &inst->venc_client;
+	if (!client_ctx) {
+		WFD_MSG_ERR("Invalid client context");
+		rc = -EINVAL;
+		goto err;
+	}
+	rc = vcd_get_buffer_requirements(client_ctx->vcd_handle,
+			VCD_BUFFER_OUTPUT, &buf_req);
+	if (rc) {
+		WFD_MSG_ERR("Failed to get out buf reqs rc = %d", rc);
+		goto err;
+	}
+
+	buf_req.actual_count = b->count = max(buf_req.min_count, b->count);
+	rc = vcd_set_buffer_requirements(client_ctx->vcd_handle,
+			VCD_BUFFER_OUTPUT, &buf_req);
+	if (rc) {
+		WFD_MSG_ERR("Failed to set out buf reqs rc = %d", rc);
+		goto err;
+	}
+
+err:
+	return rc;
+}
+
+static long venc_set_buffer_req(struct v4l2_subdev *sd, void *arg)
+{
+	int rc = 0;
+	struct bufreq *b = arg;
+	struct vcd_buffer_requirement buf_req;
+	struct venc_inst *inst = sd->dev_priv;
+	struct video_client_ctx *client_ctx = &inst->venc_client;
+	int aligned_width, aligned_height;
+	if (!client_ctx) {
+		WFD_MSG_ERR("Invalid client context");
+		rc = -EINVAL;
+		goto err;
+	}
+	aligned_width = ALIGN(b->width, 16);
+	aligned_height = ALIGN(b->height, 16);
+
+	if (aligned_width != b->width) {
+		WFD_MSG_ERR("Width not 16 byte aligned\n");
+		rc = -EINVAL;
+		goto err;
+	}
+
+	buf_req.actual_count = b->count;
+	buf_req.min_count = b->count;
+	buf_req.max_count = b->count;
+	buf_req.sz = ALIGN(aligned_height * aligned_width, SZ_2K)
+		+ ALIGN(aligned_height * aligned_width * 1/2, SZ_2K);
+	buf_req.align = SZ_4K;
+	inst->width = b->width;
+	inst->height = b->height;
+	rc = vcd_set_buffer_requirements(client_ctx->vcd_handle,
+			VCD_BUFFER_INPUT, &buf_req);
+	if (rc) {
+		WFD_MSG_ERR("Failed to get out buf reqs rc = %d", rc);
+		goto err;
+	}
+	b->size = buf_req.sz;
+err:
+	return rc;
+}
+
+static long venc_start(struct v4l2_subdev *sd)
+{
+	struct venc_inst *inst = sd->dev_priv;
+	struct video_client_ctx *client_ctx = &inst->venc_client;
+	int rc;
+	if (!client_ctx) {
+		WFD_MSG_ERR("Client context is NULL");
+		return -EINVAL;
+	}
+	rc = vcd_encode_start(client_ctx->vcd_handle);
+	if (rc) {
+		WFD_MSG_ERR("vcd_encode_start failed, rc = %d\n", rc);
+		goto err;
+	}
+	wait_for_completion(&client_ctx->event);
+	if (client_ctx->event_status)
+		WFD_MSG_ERR("callback for vcd_encode_start returned error: %u",
+				client_ctx->event_status);
+err:
+	return rc;
+}
+
+static long venc_stop(struct v4l2_subdev *sd)
+{
+	struct venc_inst *inst = sd->dev_priv;
+	struct video_client_ctx *client_ctx = &inst->venc_client;
+	int rc;
+	if (!client_ctx) {
+		WFD_MSG_ERR("Client context is NULL");
+		return -EINVAL;
+	}
+	rc = vcd_stop(client_ctx->vcd_handle);
+	wait_for_completion(&client_ctx->event);
+	return rc;
+}
+
+static long venc_set_codec(struct video_client_ctx *client_ctx, __s32 codec)
+{
+	struct vcd_property_codec vcd_property_codec;
+	struct vcd_property_hdr vcd_property_hdr;
+	vcd_property_hdr.prop_id = VCD_I_CODEC;
+	vcd_property_hdr.sz = sizeof(struct vcd_property_codec);
+	vcd_property_codec.codec = VCD_CODEC_H264;
+
+	switch (codec) {
+	case V4L2_PIX_FMT_H264:
+		vcd_property_codec.codec = VCD_CODEC_H264;
+		break;
+	case V4L2_PIX_FMT_MPEG4:
+		vcd_property_codec.codec = VCD_CODEC_MPEG4;
+		break;
+	default:
+		WFD_MSG_ERR("Codec not supported, defaulting to h264\n");
+		break;
+	}
+	return vcd_set_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &vcd_property_codec);
+}
+
+static long venc_set_codec_level(struct video_client_ctx *client_ctx,
+					__s32 codec, __s32 level)
+{
+	struct vcd_property_level vcd_property_level;
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_codec vcd_property_codec;
+
+	int rc = 0;
+	int mpeg4_base = VCD_LEVEL_MPEG4_0;
+	int h264_base = VCD_LEVEL_H264_1;
+
+	/* Validate params */
+	vcd_property_hdr.prop_id = VCD_I_CODEC;
+	vcd_property_hdr.sz = sizeof(struct vcd_property_codec);
+	rc = vcd_get_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &vcd_property_codec);
+
+	if (rc < 0) {
+		WFD_MSG_ERR("Error getting codec property");
+		rc = -EINVAL;
+		goto err;
+	}
+
+	if (!((vcd_property_codec.codec == VCD_CODEC_H264
+		&& codec == V4L2_CID_MPEG_VIDEO_H264_LEVEL) ||
+		(vcd_property_codec.codec == VCD_CODEC_MPEG4
+		&& codec == V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL))) {
+		WFD_MSG_ERR("Attempting to set %d for codec type %d",
+			codec, vcd_property_codec.codec);
+		rc = -EINVAL;
+		goto err;
+	}
+
+	/* Set property */
+	vcd_property_hdr.prop_id = VCD_I_LEVEL;
+	vcd_property_hdr.sz = sizeof(struct vcd_property_level);
+
+	if (codec == V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL) {
+		vcd_property_level.level = mpeg4_base + level;
+
+		if (vcd_property_level.level < VCD_LEVEL_MPEG4_0
+			|| vcd_property_level.level > VCD_LEVEL_MPEG4_X) {
+			WFD_MSG_ERR("Level (%d) out of range"
+					"for codec (%d)\n", level, codec);
+
+			rc = -EINVAL;
+			goto err;
+		}
+	} else if (codec == V4L2_CID_MPEG_VIDEO_H264_LEVEL) {
+		vcd_property_level.level = h264_base + level;
+
+		if (vcd_property_level.level < VCD_LEVEL_H264_1
+			|| vcd_property_level.level > VCD_LEVEL_H264_5p1) {
+			WFD_MSG_ERR("Level (%d) out of range"
+					"for codec (%d)\n", level, codec);
+
+			rc = -EINVAL;
+			goto err;
+		}
+	} else {
+		WFD_MSG_ERR("Codec (%d) not supported, not setting level (%d)",
+				codec, level);
+		rc = -ENOTSUPP;
+		goto err;
+	}
+
+	rc = vcd_set_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &vcd_property_level);
+err:
+	return rc;
+}
+
+static long venc_get_codec_level(struct video_client_ctx *client_ctx,
+					__s32 codec, __s32 *level)
+{
+	struct vcd_property_level vcd_property_level;
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_codec vcd_property_codec;
+
+	int rc = 0;
+	int mpeg4_base = VCD_LEVEL_MPEG4_0;
+	int h264_base = VCD_LEVEL_H264_1;
+
+	/* Validate params */
+	vcd_property_hdr.prop_id = VCD_I_CODEC;
+	vcd_property_hdr.sz = sizeof(struct vcd_property_codec);
+	rc = vcd_get_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &vcd_property_codec);
+
+	if (rc < 0) {
+		WFD_MSG_ERR("Error getting codec property");
+		rc = -EINVAL;
+		goto err;
+	}
+
+	if (!((vcd_property_codec.codec == VCD_CODEC_H264
+		&& codec == V4L2_CID_MPEG_VIDEO_H264_LEVEL) ||
+		(vcd_property_codec.codec == VCD_CODEC_MPEG4
+		&& codec == V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL))) {
+		WFD_MSG_ERR("Attempting to get %d for codec type %d",
+			codec, vcd_property_codec.codec);
+		rc = -EINVAL;
+		goto err;
+	}
+
+	vcd_property_hdr.prop_id = VCD_I_LEVEL;
+	vcd_property_hdr.sz = sizeof(struct vcd_property_level);
+
+	rc = vcd_get_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &vcd_property_level);
+	if (rc < 0) {
+		rc = -EINVAL;
+		goto err;
+	}
+
+	if (codec == V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL) {
+		*level = vcd_property_level.level - mpeg4_base;
+	} else if (codec == V4L2_CID_MPEG_VIDEO_H264_LEVEL) {
+		*level = vcd_property_level.level - h264_base;
+	} else {
+		WFD_MSG_ERR("Codec (%d) not supported", codec);
+		rc = -ENOTSUPP;
+		goto err;
+	}
+
+err:
+	return rc;
+}
+
+static long venc_set_codec_profile(struct video_client_ctx *client_ctx,
+					__s32 codec, __s32 profile)
+{
+	struct vcd_property_profile vcd_property_profile;
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_codec vcd_property_codec;
+	struct vcd_property_i_period vcd_property_i_period;
+	int rc = 0;
+
+	/* Validate params */
+	vcd_property_hdr.prop_id = VCD_I_CODEC;
+	vcd_property_hdr.sz = sizeof(struct vcd_property_codec);
+	rc = vcd_get_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &vcd_property_codec);
+
+	if (rc < 0) {
+		WFD_MSG_ERR("Error getting codec property");
+		rc = -EINVAL;
+		goto err_set_profile;
+	}
+
+	if (!((vcd_property_codec.codec == VCD_CODEC_H264
+		&& codec == V4L2_CID_MPEG_VIDEO_H264_PROFILE) ||
+		(vcd_property_codec.codec == VCD_CODEC_MPEG4
+		&& codec == V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE))) {
+		WFD_MSG_ERR("Attempting to set %d for codec type %d",
+			codec, vcd_property_codec.codec);
+		rc = -EINVAL;
+		goto err_set_profile;
+	}
+
+	/* Set property */
+	vcd_property_hdr.prop_id = VCD_I_PROFILE;
+	vcd_property_hdr.sz = sizeof(struct vcd_property_profile);
+
+	if (codec == V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE) {
+		switch (profile) {
+		case V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE:
+			vcd_property_profile.profile = VCD_PROFILE_MPEG4_SP;
+			break;
+		case V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE:
+			vcd_property_profile.profile = VCD_PROFILE_MPEG4_ASP;
+			break;
+		default:
+			WFD_MSG_ERR("Profile %d not supported,"
+					"defaulting to simple (%d)",
+					profile, VCD_PROFILE_MPEG4_SP);
+			vcd_property_profile.profile = VCD_PROFILE_MPEG4_SP;
+			break;
+		}
+	} else if (codec == V4L2_CID_MPEG_VIDEO_H264_PROFILE) {
+		switch (profile) {
+		case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE:
+			vcd_property_profile.profile =
+				VCD_PROFILE_H264_BASELINE;
+			break;
+		case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN:
+			vcd_property_profile.profile = VCD_PROFILE_H264_MAIN;
+			break;
+		case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH:
+			vcd_property_profile.profile = VCD_PROFILE_H264_HIGH;
+			break;
+		default:
+			WFD_MSG_ERR("Profile %d not supported,"
+					"defaulting to baseline (%d)",
+					profile, VCD_PROFILE_H264_BASELINE);
+			vcd_property_profile.profile =
+				VCD_PROFILE_H264_BASELINE;
+			break;
+		}
+	} else {
+		WFD_MSG_ERR("Codec (%d) not supported,"
+				"not setting profile (%d)",
+				codec, profile);
+		rc = -ENOTSUPP;
+		goto err_set_profile;
+	}
+
+	rc = vcd_set_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &vcd_property_profile);
+
+	/* Disable B-frames, since VSG doesn't support out of order i/p bufs */
+	vcd_property_hdr.prop_id = VCD_I_INTRA_PERIOD;
+	vcd_property_hdr.sz = sizeof(struct vcd_property_i_period);
+
+	rc = vcd_get_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &vcd_property_i_period);
+	if (rc) {
+		WFD_MSG_ERR("Error getting I-period property");
+		goto err_set_profile;
+	}
+	vcd_property_i_period.b_frames = 0;
+	rc = vcd_set_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &vcd_property_i_period);
+	if (rc) {
+		WFD_MSG_ERR("Error setting I-period property");
+		goto err_set_profile;
+	}
+
+err_set_profile:
+	return rc;
+}
+
+static long venc_get_codec_profile(struct video_client_ctx *client_ctx,
+		__s32 codec, __s32 *profile)
+{
+	struct vcd_property_profile vcd_property_profile;
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_codec vcd_property_codec;
+	int rc = 0;
+
+	/* Validate params */
+	vcd_property_hdr.prop_id = VCD_I_CODEC;
+	vcd_property_hdr.sz = sizeof(struct vcd_property_codec);
+	rc = vcd_get_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &vcd_property_codec);
+
+	if (rc < 0) {
+		WFD_MSG_ERR("Error getting codec property");
+		rc = -EINVAL;
+		goto err;
+	}
+
+	if (!((vcd_property_codec.codec == VCD_CODEC_H264
+		&& codec == V4L2_CID_MPEG_VIDEO_H264_PROFILE) ||
+		(vcd_property_codec.codec == VCD_CODEC_MPEG4
+		&& codec == V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE))) {
+		WFD_MSG_ERR("Attempting to set %d for codec type %d",
+			codec, vcd_property_codec.codec);
+		rc = -EINVAL;
+		goto err;
+	}
+
+	/* Set property */
+	vcd_property_hdr.prop_id = VCD_I_PROFILE;
+	vcd_property_hdr.sz = sizeof(struct vcd_property_profile);
+
+	rc = vcd_get_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &vcd_property_profile);
+
+	if (rc < 0) {
+		WFD_MSG_ERR("Unable to get property");
+		rc = -EINVAL;
+		goto err;
+	}
+
+	switch (vcd_property_profile.profile) {
+	case VCD_PROFILE_MPEG4_SP:
+		*profile = V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE;
+		break;
+	case VCD_PROFILE_MPEG4_ASP:
+		*profile = V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE;
+		break;
+	case VCD_PROFILE_H264_BASELINE:
+		*profile = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE;
+		break;
+	case VCD_PROFILE_H264_MAIN:
+		*profile = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN;
+		break;
+	case VCD_PROFILE_H264_HIGH:
+		*profile = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH;
+		break;
+	default:
+		WFD_MSG_ERR("Unexpected profile");
+		rc = -EINVAL;
+		goto err;
+		break;
+	}
+err:
+	return rc;
+}
+
+static long venc_set_h264_intra_period(struct video_client_ctx *client_ctx,
+		__s32 period)
+{
+	struct vcd_property_i_period vcd_property_i_period;
+	struct vcd_property_codec vcd_property_codec;
+	struct vcd_property_hdr vcd_property_hdr;
+	int rc = 0;
+
+	vcd_property_hdr.prop_id = VCD_I_CODEC;
+	vcd_property_hdr.sz = sizeof(struct vcd_property_codec);
+
+	rc = vcd_get_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &vcd_property_codec);
+
+	if (rc < 0) {
+		WFD_MSG_ERR("Error getting codec property\n");
+		goto err;
+	}
+
+	if (vcd_property_codec.codec != VCD_CODEC_H264) {
+		rc = -ENOTSUPP;
+		WFD_MSG_ERR("Control not supported for non H264 codec\n");
+		goto err;
+	}
+
+	vcd_property_hdr.prop_id = VCD_I_INTRA_PERIOD;
+	vcd_property_hdr.sz = sizeof(struct vcd_property_i_period);
+
+	vcd_property_i_period.p_frames = period - 1;
+	vcd_property_i_period.b_frames = 0;
+
+	rc = vcd_set_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &vcd_property_i_period);
+
+	if (rc < 0) {
+		WFD_MSG_ERR("Error setting intra period\n");
+		goto err;
+	}
+
+err:
+	return rc;
+}
+
+static long venc_get_h264_intra_period(struct video_client_ctx *client_ctx,
+		__s32 *period)
+{
+	struct vcd_property_i_period vcd_property_i_period;
+	struct vcd_property_codec vcd_property_codec;
+	struct vcd_property_hdr vcd_property_hdr;
+	int rc = 0;
+
+	vcd_property_hdr.prop_id = VCD_I_CODEC;
+	vcd_property_hdr.sz = sizeof(struct vcd_property_codec);
+
+	rc = vcd_get_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &vcd_property_codec);
+
+	if (rc < 0) {
+		WFD_MSG_ERR("Error getting codec property\n");
+		goto err;
+	}
+
+	if (vcd_property_codec.codec != VCD_CODEC_H264) {
+		rc = -ENOTSUPP;
+		WFD_MSG_ERR("Control not supported for non H264 codec\n");
+		goto err;
+	}
+
+	vcd_property_hdr.prop_id = VCD_I_INTRA_PERIOD;
+	vcd_property_hdr.sz = sizeof(struct vcd_property_i_period);
+
+	rc = vcd_get_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &vcd_property_i_period);
+
+	if (rc < 0) {
+		WFD_MSG_ERR("Error getting intra period\n");
+		goto err;
+	}
+
+	*period = vcd_property_i_period.p_frames + 1;
+err:
+	return rc;
+}
+
+static long venc_request_frame(struct video_client_ctx *client_ctx, __s32 type)
+{
+	struct vcd_property_req_i_frame vcd_property_req_i_frame;
+	struct vcd_property_hdr vcd_property_hdr;
+
+	int rc = 0;
+	switch (type) {
+	case V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_DISABLED:
+		/*So...nothing to do?*/
+		break;
+	case V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_I_FRAME:
+		vcd_property_hdr.prop_id = VCD_I_REQ_IFRAME;
+		vcd_property_hdr.sz = sizeof(struct vcd_property_req_i_frame);
+		vcd_property_req_i_frame.req_i_frame = 1;
+
+		rc = vcd_set_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &vcd_property_req_i_frame);
+		break;
+	case V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_NOT_CODED:
+	default:
+		rc = -ENOTSUPP;
+	}
+
+	return rc;
+}
+
+static long venc_set_bitrate(struct video_client_ctx *client_ctx,
+			__s32 bitrate)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_target_bitrate bit_rate;
+	if (!client_ctx || !bitrate)
+		return -EINVAL;
+
+	vcd_property_hdr.prop_id = VCD_I_TARGET_BITRATE;
+	vcd_property_hdr.sz =
+			sizeof(struct vcd_property_target_bitrate);
+	bit_rate.target_bitrate = bitrate;
+	return vcd_set_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &bit_rate);
+}
+
+static long venc_get_bitrate(struct video_client_ctx *client_ctx,
+			__s32 *bitrate)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_target_bitrate bit_rate;
+	int rc = 0;
+
+	if (!client_ctx || !bitrate)
+		return -EINVAL;
+
+	vcd_property_hdr.prop_id = VCD_I_TARGET_BITRATE;
+	vcd_property_hdr.sz =
+			sizeof(struct vcd_property_target_bitrate);
+	rc = vcd_get_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &bit_rate);
+
+	if (rc < 0) {
+		WFD_MSG_ERR("Failed getting property for bitrate");
+		return rc;
+	}
+
+	*bitrate = bit_rate.target_bitrate;
+	return rc;
+}
+
+static long venc_set_bitrate_mode(struct video_client_ctx *client_ctx,
+			__s32 mode)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_rate_control rate_control;
+	int rc = 0;
+
+	if (!client_ctx) {
+		rc = -EINVAL;
+		goto err;
+	}
+
+	vcd_property_hdr.prop_id = VCD_I_RATE_CONTROL;
+	vcd_property_hdr.sz = sizeof(struct vcd_property_rate_control);
+	/*
+	 * XXX: V4L doesn't seem have a control to toggle between CFR
+	 * and VFR, so assuming worse case VFR.
+	 */
+	switch (mode) {
+	case V4L2_MPEG_VIDEO_BITRATE_MODE_VBR:
+		rate_control.rate_control = VCD_RATE_CONTROL_VBR_VFR;
+		break;
+	case V4L2_MPEG_VIDEO_BITRATE_MODE_CBR:
+		rate_control.rate_control = VCD_RATE_CONTROL_CBR_VFR;
+		break;
+	default:
+		WFD_MSG_ERR("unknown bitrate mode %d", mode);
+		rc = -EINVAL;
+		goto err;
+	}
+
+	rc = vcd_set_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &rate_control);
+err:
+	return rc;
+}
+
+static long venc_get_bitrate_mode(struct video_client_ctx *client_ctx,
+			__s32 *mode)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_rate_control rate_control;
+	int rc = 0;
+
+	if (!client_ctx)
+		return -EINVAL;
+
+	vcd_property_hdr.prop_id = VCD_I_RATE_CONTROL;
+	vcd_property_hdr.sz = sizeof(struct vcd_property_rate_control);
+	rc = vcd_get_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &rate_control);
+
+	switch (rate_control.rate_control) {
+	case VCD_RATE_CONTROL_CBR_VFR:
+	case VCD_RATE_CONTROL_CBR_CFR:
+		*mode = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR;
+		break;
+	case VCD_RATE_CONTROL_VBR_VFR:
+	case VCD_RATE_CONTROL_VBR_CFR:
+		*mode = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR;
+		break;
+	default:
+		WFD_MSG_ERR("unknown bitrate mode %d",
+				rate_control.rate_control);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static long venc_set_frame_size(struct video_client_ctx *client_ctx,
+				u32 height, u32 width)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_frame_size frame_size;
+	vcd_property_hdr.prop_id = VCD_I_FRAME_SIZE;
+	vcd_property_hdr.sz =
+		sizeof(struct vcd_property_frame_size);
+	frame_size.height = height;
+	frame_size.width = width;
+	return vcd_set_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &frame_size);
+}
+
+static long venc_set_format(struct v4l2_subdev *sd, void *arg)
+{
+	struct venc_inst *inst;
+	struct video_client_ctx *client_ctx;
+	struct v4l2_format *fmt = arg;
+	struct vcd_buffer_requirement buf_req;
+	int rc = 0;
+
+	inst = sd->dev_priv;
+	client_ctx = &inst->venc_client;
+	if (!inst || !client_ctx || !fmt) {
+		WFD_MSG_ERR("Invalid parameters\n");
+		return -EINVAL;
+	}
+	rc = venc_set_codec(client_ctx, fmt->fmt.pix.pixelformat);
+	if (rc) {
+		WFD_MSG_ERR("Failed to set codec, rc = %d\n", rc);
+		goto err;
+	}
+
+	rc = venc_set_frame_size(client_ctx, fmt->fmt.pix.height,
+				fmt->fmt.pix.width);
+	if (rc) {
+		WFD_MSG_ERR("Failed to set frame size, rc = %d\n", rc);
+		goto err;
+	}
+	rc = vcd_get_buffer_requirements(client_ctx->vcd_handle,
+			VCD_BUFFER_OUTPUT, &buf_req);
+	if (rc) {
+		WFD_MSG_ERR("Failed to get buf requrements, rc = %d\n", rc);
+		goto err;
+	}
+	fmt->fmt.pix.sizeimage = buf_req.sz;
+err:
+	return rc;
+}
+
+static long venc_set_framerate(struct v4l2_subdev *sd,
+				void *arg)
+{
+	struct venc_inst *inst = sd->dev_priv;
+	struct video_client_ctx *client_ctx = &inst->venc_client;
+	struct v4l2_fract *frate = arg;
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_frame_rate vcd_frame_rate;
+	struct vcd_property_vop_timing_constant_delta vcd_delta;
+	int rc;
+	vcd_property_hdr.prop_id = VCD_I_FRAME_RATE;
+	vcd_property_hdr.sz =
+				sizeof(struct vcd_property_frame_rate);
+	/* v4l2 passes in "fps" as "spf", so take reciprocal*/
+	vcd_frame_rate.fps_denominator = frate->numerator;
+	vcd_frame_rate.fps_numerator = frate->denominator;
+	rc = vcd_set_property(client_ctx->vcd_handle,
+					&vcd_property_hdr, &vcd_frame_rate);
+	if (rc) {
+		WFD_MSG_ERR("Failed to set frame rate, rc = %d\n", rc);
+		goto set_framerate_fail;
+	}
+
+	vcd_property_hdr.prop_id = VCD_I_VOP_TIMING_CONSTANT_DELTA;
+	vcd_property_hdr.sz = sizeof(vcd_delta);
+
+	vcd_delta.constant_delta = (frate->numerator * USEC_PER_SEC) /
+					frate->denominator;
+	rc = vcd_set_property(client_ctx->vcd_handle,
+					&vcd_property_hdr, &vcd_delta);
+
+	if (rc) {
+		WFD_MSG_ERR("Failed to set frame delta, rc = %d", rc);
+		goto set_framerate_fail;
+	}
+
+set_framerate_fail:
+	return rc;
+}
+
+static long venc_set_qp_value(struct video_client_ctx *client_ctx,
+		__s32 frametype, __s32 qp)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_session_qp vcd_property_session_qp;
+	int rc = 0;
+
+	if (!client_ctx) {
+		WFD_MSG_ERR("Invalid parameters\n");
+		return -EINVAL;
+	}
+
+	vcd_property_hdr.prop_id = VCD_I_SESSION_QP;
+	vcd_property_hdr.sz = sizeof(vcd_property_session_qp);
+
+	rc = vcd_get_property(client_ctx->vcd_handle, &vcd_property_hdr,
+			&vcd_property_session_qp);
+
+	if (rc) {
+		WFD_MSG_ERR("Failed to get session qp\n");
+		goto err;
+	}
+
+	switch (frametype) {
+	case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP:
+		vcd_property_session_qp.i_frame_qp = qp;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP:
+		vcd_property_session_qp.p_frame_qp = qp;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP:
+		vcd_property_session_qp.b_frame_qp = qp;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP:
+		rc = -ENOTSUPP;
+		goto err;
+	default:
+		rc = -EINVAL;
+		goto err;
+	}
+
+
+	rc = vcd_set_property(client_ctx->vcd_handle, &vcd_property_hdr,
+			&vcd_property_session_qp);
+
+	if (rc) {
+		WFD_MSG_ERR("Failed to set session qp\n");
+		goto err;
+	}
+err:
+	return rc;
+}
+
+static long venc_get_qp_value(struct video_client_ctx *client_ctx,
+		__s32 frametype, __s32 *qp)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_session_qp vcd_property_session_qp;
+	int rc = 0;
+
+	if (!client_ctx) {
+		WFD_MSG_ERR("Invalid parameters\n");
+		return -EINVAL;
+	}
+
+	vcd_property_hdr.prop_id = VCD_I_SESSION_QP;
+	vcd_property_hdr.sz = sizeof(vcd_property_session_qp);
+
+	rc = vcd_get_property(client_ctx->vcd_handle, &vcd_property_hdr,
+			&vcd_property_session_qp);
+
+	if (rc) {
+		WFD_MSG_ERR("Failed to get session qp\n");
+		goto err;
+	}
+
+	switch (frametype) {
+	case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP:
+		*qp = vcd_property_session_qp.i_frame_qp;
+		break;
+	case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP:
+		*qp = vcd_property_session_qp.p_frame_qp;
+		break;
+	case V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP:
+		*qp = vcd_property_session_qp.b_frame_qp;
+		break;
+	default:
+		rc = -EINVAL;
+		goto err;
+	}
+
+err:
+	return rc;
+}
+
+static long venc_set_qp_range(struct video_client_ctx *client_ctx,
+		__s32 type, __s32 qp)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_qp_range vcd_property_qp_range;
+	int rc = 0;
+
+	if (!client_ctx) {
+		WFD_MSG_ERR("Invalid parameters\n");
+		return -EINVAL;
+	}
+
+	vcd_property_hdr.prop_id = VCD_I_QP_RANGE;
+	vcd_property_hdr.sz = sizeof(vcd_property_qp_range);
+
+	rc = vcd_get_property(client_ctx->vcd_handle, &vcd_property_hdr,
+			&vcd_property_qp_range);
+
+	if (rc) {
+		WFD_MSG_ERR("Failed to get qp range\n");
+		goto err;
+	}
+
+	switch (type) {
+	case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP:
+	case V4L2_CID_MPEG_VIDEO_H263_MIN_QP:
+	case V4L2_CID_MPEG_VIDEO_H264_MIN_QP:
+		vcd_property_qp_range.min_qp = qp;
+		break;
+	case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP:
+	case V4L2_CID_MPEG_VIDEO_H263_MAX_QP:
+	case V4L2_CID_MPEG_VIDEO_H264_MAX_QP:
+		vcd_property_qp_range.max_qp = qp;
+		break;
+	default:
+		rc = -EINVAL;
+		goto err;
+	}
+
+	rc = vcd_set_property(client_ctx->vcd_handle, &vcd_property_hdr,
+			&vcd_property_qp_range);
+
+	if (rc) {
+		WFD_MSG_ERR("Failed to set qp range\n");
+		goto err;
+	}
+err:
+	return rc;
+}
+
+static long venc_get_qp_range(struct video_client_ctx *client_ctx,
+		__s32 type, __s32 *qp)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_qp_range vcd_property_qp_range;
+	int rc = 0;
+
+	if (!client_ctx) {
+		WFD_MSG_ERR("Invalid parameters\n");
+		return -EINVAL;
+	}
+
+	vcd_property_hdr.prop_id = VCD_I_QP_RANGE;
+	vcd_property_hdr.sz = sizeof(vcd_property_qp_range);
+
+	rc = vcd_get_property(client_ctx->vcd_handle, &vcd_property_hdr,
+			&vcd_property_qp_range);
+
+	if (rc) {
+		WFD_MSG_ERR("Failed to get qp range\n");
+		goto err;
+	}
+
+	switch (type) {
+	case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP:
+	case V4L2_CID_MPEG_VIDEO_H263_MIN_QP:
+	case V4L2_CID_MPEG_VIDEO_H264_MIN_QP:
+		*qp = vcd_property_qp_range.min_qp;
+		break;
+	case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP:
+	case V4L2_CID_MPEG_VIDEO_H263_MAX_QP:
+	case V4L2_CID_MPEG_VIDEO_H264_MAX_QP:
+		*qp = vcd_property_qp_range.max_qp;
+		break;
+	default:
+		rc = -EINVAL;
+		goto err;
+	}
+
+	rc = vcd_set_property(client_ctx->vcd_handle, &vcd_property_hdr,
+			&vcd_property_qp_range);
+
+	if (rc) {
+		WFD_MSG_ERR("Failed to set qp range\n");
+		goto err;
+	}
+err:
+	return rc;
+}
+static long venc_set_max_perf_level(struct video_client_ctx *client_ctx,
+		int val)
+{
+	int rc = 0;
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_perf_level perf;
+	vcd_property_hdr.prop_id = VCD_REQ_PERF_LEVEL;
+	vcd_property_hdr.sz =
+		sizeof(struct vcd_property_perf_level);
+	perf.level = VCD_PERF_LEVEL2;
+	rc = vcd_set_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &perf);
+	return rc;
+}
+static long venc_set_header_mode(struct video_client_ctx *client_ctx,
+		__s32 mode)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_sps_pps_for_idr_enable sps_pps_for_idr_enable;
+	int rc = 0;
+
+	if (!client_ctx) {
+		WFD_MSG_ERR("Invalid parameters\n");
+		rc = -EINVAL;
+		goto err;
+	}
+
+	vcd_property_hdr.prop_id = VCD_I_ENABLE_SPS_PPS_FOR_IDR;
+	vcd_property_hdr.sz = sizeof(sps_pps_for_idr_enable);
+	switch (mode) {
+	case V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE:
+		sps_pps_for_idr_enable.sps_pps_for_idr_enable_flag = 0;
+		break;
+	case V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_I_FRAME:
+		sps_pps_for_idr_enable.sps_pps_for_idr_enable_flag = 1;
+		break;
+	case V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME:
+	default:
+		WFD_MSG_ERR("Video header mode %d not supported\n",
+				mode);
+		rc = -ENOTSUPP;
+		goto err;
+	}
+
+	rc =  vcd_set_property(client_ctx->vcd_handle, &vcd_property_hdr,
+			&sps_pps_for_idr_enable);
+	if (rc) {
+		WFD_MSG_ERR("Failed to set enable_sps_pps_for_idr\n");
+		goto err;
+	}
+err:
+	return rc;
+}
+
+static long venc_get_header_mode(struct video_client_ctx *client_ctx,
+		__s32 *mode)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_sps_pps_for_idr_enable sps_pps_for_idr_enable;
+	int rc = 0;
+
+	if (!client_ctx) {
+		WFD_MSG_ERR("Invalid parameters\n");
+		rc = -EINVAL;
+		goto err;
+	}
+
+	vcd_property_hdr.prop_id = VCD_I_ENABLE_SPS_PPS_FOR_IDR;
+	vcd_property_hdr.sz = sizeof(sps_pps_for_idr_enable);
+	rc =  vcd_get_property(client_ctx->vcd_handle, &vcd_property_hdr,
+			&sps_pps_for_idr_enable);
+	if (rc) {
+		WFD_MSG_ERR("Failed to get sps/pps for idr enable\n");
+		goto err;
+	}
+
+	*mode = sps_pps_for_idr_enable.sps_pps_for_idr_enable_flag ?
+		V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_I_FRAME :
+		V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE;
+err:
+	return rc;
+}
+
+static long venc_set_multislicing_mode(struct video_client_ctx *client_ctx,
+			__u32 control, __s32 value)
+{
+	int rc = 0;
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_frame_size vcd_frame_size;
+	struct vcd_buffer_requirement vcd_buf_req;
+	struct vcd_property_multi_slice vcd_multi_slice;
+
+	if (!client_ctx) {
+		WFD_MSG_ERR("Invalid parameters\n");
+		rc = -EINVAL;
+		goto set_multislicing_mode_fail;
+	}
+
+	vcd_property_hdr.prop_id = VCD_I_FRAME_SIZE;
+	vcd_property_hdr.sz =
+		sizeof(vcd_frame_size);
+	rc = vcd_get_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &vcd_frame_size);
+
+	if (rc) {
+		WFD_MSG_ERR("Failed to get frame size\n");
+		goto set_multislicing_mode_fail;
+	}
+
+	rc = vcd_get_buffer_requirements(client_ctx->vcd_handle,
+			VCD_BUFFER_OUTPUT, &vcd_buf_req);
+
+	if (rc) {
+		WFD_MSG_ERR("Failed to get buf reqs\n");
+		goto set_multislicing_mode_fail;
+	}
+
+	vcd_property_hdr.prop_id = VCD_I_MULTI_SLICE;
+	vcd_property_hdr.sz = sizeof(vcd_multi_slice);
+	rc = vcd_get_property(client_ctx->vcd_handle, &vcd_property_hdr,
+			&vcd_multi_slice);
+	if (rc) {
+		WFD_MSG_ERR("Failed to get multi slice\n");
+		goto set_multislicing_mode_fail;
+	}
+
+	switch (control) {
+	case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES:
+		if (vcd_multi_slice.m_slice_sel !=
+				VCD_MSLICE_BY_BYTE_COUNT) {
+			WFD_MSG_ERR("Not in proper mode\n");
+			goto set_multislicing_mode_fail;
+		}
+		vcd_multi_slice.m_slice_size = value;
+		break;
+
+	case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB:
+		if (vcd_multi_slice.m_slice_sel !=
+				VCD_MSLICE_BY_MB_COUNT) {
+			WFD_MSG_ERR("Not in proper mode\n");
+			goto set_multislicing_mode_fail;
+		}
+		vcd_multi_slice.m_slice_size = value;
+		break;
+
+	case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
+		switch (value) {
+		case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE:
+			vcd_multi_slice.m_slice_sel = VCD_MSLICE_OFF;
+			break;
+		case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB:
+			vcd_multi_slice.m_slice_sel = VCD_MSLICE_BY_MB_COUNT;
+			/* Just a temporary size until client calls
+			 * V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB */
+			vcd_multi_slice.m_slice_size =
+				(vcd_frame_size.stride / 16) *
+				(vcd_frame_size.scan_lines / 16);
+			break;
+		case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES:
+			vcd_multi_slice.m_slice_sel = VCD_MSLICE_BY_BYTE_COUNT;
+			/* Just a temporary size until client calls
+			 * V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES */
+			vcd_multi_slice.m_slice_size = vcd_buf_req.sz;
+			break;
+		default:
+			WFD_MSG_ERR("Unrecognized mode %d\n", value);
+			rc = -ENOTSUPP;
+			goto set_multislicing_mode_fail;
+		}
+
+		break;
+	default:
+		rc = -EINVAL;
+		goto set_multislicing_mode_fail;
+	}
+
+	rc = vcd_set_property(client_ctx->vcd_handle, &vcd_property_hdr,
+			&vcd_multi_slice);
+	if (rc) {
+		WFD_MSG_ERR("Failed to set multi slice\n");
+		goto set_multislicing_mode_fail;
+	}
+
+set_multislicing_mode_fail:
+	return rc;
+}
+
+static long venc_get_multislicing_mode(struct video_client_ctx *client_ctx,
+			__u32 control, __s32 *value)
+{
+	int rc = 0;
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_frame_size vcd_frame_size;
+	struct vcd_buffer_requirement vcd_buf_req;
+	struct vcd_property_multi_slice vcd_multi_slice;
+
+	if (!client_ctx) {
+		WFD_MSG_ERR("Invalid parameters\n");
+		rc = -EINVAL;
+		goto get_multislicing_mode_fail;
+	}
+
+	vcd_property_hdr.prop_id = VCD_I_FRAME_SIZE;
+	vcd_property_hdr.sz =
+		sizeof(vcd_frame_size);
+	rc = vcd_get_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &vcd_frame_size);
+
+	if (rc) {
+		WFD_MSG_ERR("Failed to get frame size\n");
+		goto get_multislicing_mode_fail;
+	}
+
+	vcd_property_hdr.prop_id = VCD_I_MULTI_SLICE;
+	vcd_property_hdr.sz = sizeof(vcd_multi_slice);
+	rc = vcd_get_property(client_ctx->vcd_handle, &vcd_property_hdr,
+			&vcd_multi_slice);
+	if (rc) {
+		WFD_MSG_ERR("Failed to get multi slice\n");
+		goto get_multislicing_mode_fail;
+	}
+
+	rc = vcd_get_buffer_requirements(client_ctx->vcd_handle,
+			VCD_BUFFER_OUTPUT, &vcd_buf_req);
+
+	if (rc) {
+		WFD_MSG_ERR("Failed to get buf reqs\n");
+		goto get_multislicing_mode_fail;
+	}
+
+	switch (control) {
+	case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES:
+		if (vcd_multi_slice.m_slice_sel == VCD_MSLICE_BY_BYTE_COUNT)
+			*value = vcd_multi_slice.m_slice_size;
+		else {
+			WFD_MSG_ERR("Invalid query when in slice mode %d\n",
+					vcd_multi_slice.m_slice_sel);
+			rc = -EINVAL;
+			goto get_multislicing_mode_fail;
+		}
+		break;
+
+	case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB:
+		if (vcd_multi_slice.m_slice_sel == VCD_MSLICE_BY_MB_COUNT)
+			*value = vcd_multi_slice.m_slice_size;
+		else {
+			WFD_MSG_ERR("Invalid query when in slice mode %d\n",
+					vcd_multi_slice.m_slice_sel);
+			rc = -EINVAL;
+			goto get_multislicing_mode_fail;
+		}
+		break;
+
+	case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
+		switch (vcd_multi_slice.m_slice_sel) {
+		case VCD_MSLICE_OFF:
+			*value = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE;
+			break;
+		case VCD_MSLICE_BY_MB_COUNT:
+			*value = V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB;
+			break;
+		case VCD_MSLICE_BY_BYTE_COUNT:
+			*value = V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES;
+			break;
+		default:
+			WFD_MSG_ERR("Encoder in an unknown mode %d\n",
+					vcd_multi_slice.m_slice_sel);
+			rc = -ENOENT;
+			goto get_multislicing_mode_fail;
+
+		}
+		break;
+	default:
+		rc = -EINVAL;
+		goto get_multislicing_mode_fail;
+	}
+
+get_multislicing_mode_fail:
+	return rc;
+}
+
+static long venc_set_entropy_mode(struct video_client_ctx *client_ctx,
+		__s32 value)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_entropy_control entropy_control;
+	int rc = 0;
+
+	if (!client_ctx) {
+		WFD_MSG_ERR("Invalid parameters\n");
+		rc = -EINVAL;
+		goto set_entropy_mode_fail;
+	}
+
+	vcd_property_hdr.prop_id = VCD_I_ENTROPY_CTRL;
+	vcd_property_hdr.sz = sizeof(entropy_control);
+
+	switch (value) {
+	case V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC:
+		entropy_control.entropy_sel = VCD_ENTROPY_SEL_CAVLC;
+		break;
+	case V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC:
+		entropy_control.entropy_sel = VCD_ENTROPY_SEL_CABAC;
+		entropy_control.cabac_model = VCD_CABAC_MODEL_NUMBER_0;
+		break;
+	default:
+		WFD_MSG_ERR("Entropy type %d not supported\n", value);
+		rc = -ENOTSUPP;
+		goto set_entropy_mode_fail;
+	}
+	rc = vcd_set_property(client_ctx->vcd_handle, &vcd_property_hdr,
+			&entropy_control);
+	if (rc) {
+		WFD_MSG_ERR("Failed to set entropy mode\n");
+		goto set_entropy_mode_fail;
+	}
+
+set_entropy_mode_fail:
+	return rc;
+}
+
+static long venc_get_entropy_mode(struct video_client_ctx *client_ctx,
+		__s32 *value)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_entropy_control entropy_control;
+	int rc = 0;
+
+	if (!client_ctx || !value) {
+		WFD_MSG_ERR("Invalid parameters\n");
+		rc = -EINVAL;
+		goto get_entropy_mode_fail;
+	}
+
+	vcd_property_hdr.prop_id = VCD_I_ENTROPY_CTRL;
+	vcd_property_hdr.sz = sizeof(entropy_control);
+
+	rc = vcd_get_property(client_ctx->vcd_handle, &vcd_property_hdr,
+			&entropy_control);
+
+	if (rc) {
+		WFD_MSG_ERR("Failed to get entropy mode\n");
+		goto get_entropy_mode_fail;
+	}
+
+	switch (entropy_control.entropy_sel) {
+	case VCD_ENTROPY_SEL_CAVLC:
+		*value = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC;
+		break;
+	case VCD_ENTROPY_SEL_CABAC:
+		*value = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC;
+		break;
+	default:
+		WFD_MSG_ERR("Entropy type %d not known\n",
+				entropy_control.entropy_sel);
+		rc = -EINVAL;
+		goto get_entropy_mode_fail;
+	}
+get_entropy_mode_fail:
+	return rc;
+}
+
+static long venc_set_cyclic_intra_refresh_mb(
+		struct video_client_ctx *client_ctx,
+		__s32 value)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_intra_refresh_mb_number cir_mb_num;
+	int rc = 0;
+
+	if (!client_ctx) {
+		WFD_MSG_ERR("Invalid parameters\n");
+		rc = -EINVAL;
+		goto set_cir_mbs_fail;
+	}
+
+	vcd_property_hdr.prop_id = VCD_I_INTRA_REFRESH;
+	vcd_property_hdr.sz = sizeof(cir_mb_num);
+
+	cir_mb_num.cir_mb_number = value;
+
+	rc = vcd_set_property(client_ctx->vcd_handle, &vcd_property_hdr,
+			&cir_mb_num);
+	if (rc) {
+		WFD_MSG_ERR("Failed to set CIR MBs\n");
+		goto set_cir_mbs_fail;
+	}
+
+set_cir_mbs_fail:
+	return rc;
+}
+
+static long venc_get_cyclic_intra_refresh_mb(
+		struct video_client_ctx *client_ctx,
+		__s32 *value)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_intra_refresh_mb_number cir_mb_num;
+	int rc = 0;
+
+	if (!client_ctx || !value) {
+		WFD_MSG_ERR("Invalid parameters\n");
+		rc = -EINVAL;
+		goto get_cir_mbs_fail;
+	}
+
+	vcd_property_hdr.prop_id = VCD_I_INTRA_REFRESH;
+	vcd_property_hdr.sz = sizeof(cir_mb_num);
+
+	rc = vcd_get_property(client_ctx->vcd_handle, &vcd_property_hdr,
+			&cir_mb_num);
+	if (rc) {
+		WFD_MSG_ERR("Failed to set CIR MBs\n");
+		goto get_cir_mbs_fail;
+	}
+
+	*value = cir_mb_num.cir_mb_number;
+
+get_cir_mbs_fail:
+	return rc;
+}
+static long venc_set_input_buffer(struct v4l2_subdev *sd, void *arg)
+{
+	struct mem_region *mregion = arg;
+	struct venc_inst *inst = sd->dev_priv;
+	unsigned long paddr, kvaddr, temp;
+	struct video_client_ctx *client_ctx = &inst->venc_client;
+	int rc = 0;
+
+	if (!client_ctx || !mregion) {
+		WFD_MSG_ERR("Invalid input\n");
+		rc = -EINVAL;
+		goto ins_table_fail;
+	}
+
+	kvaddr = (unsigned long)mregion->kvaddr;
+	paddr = (unsigned long)mregion->paddr;
+
+	if (!kvaddr || !paddr) {
+		WFD_MSG_ERR("Invalid addresses\n");
+		rc = -EINVAL;
+		goto ins_table_fail;
+	}
+
+	/*
+	 * Just a note: the third arg of vidc_insert_\
+	 * addr_table_kernel is supposed to be a userspace
+	 * address that is used as a key in the table. As
+	 * these bufs never leave the kernel, we need to have
+	 * an unique value to use as a key.  So re-using kernel
+	 * virtual addr for this purpose
+	 */
+	rc = vidc_insert_addr_table_kernel(client_ctx,
+		BUFFER_TYPE_INPUT, kvaddr, kvaddr,
+		paddr, 32, mregion->size);
+
+	if (rc == (u32)false) {
+		WFD_MSG_ERR("Failed to insert input buffer into table\n");
+		rc = -EFAULT;
+		goto ins_table_fail;
+	}
+
+	rc = vcd_set_buffer(client_ctx->vcd_handle,
+			VCD_BUFFER_INPUT, (u8 *)kvaddr,
+			mregion->size);
+
+	if (rc) {
+		WFD_MSG_ERR("Failed to set input buffer\n");
+		rc = -EFAULT;
+		goto set_input_buf_fail;
+	}
+
+
+	return rc;
+
+set_input_buf_fail:
+	vidc_delete_addr_table(client_ctx, BUFFER_TYPE_INPUT,
+			kvaddr, &temp);
+ins_table_fail:
+	return rc;
+}
+
+static long venc_set_output_buffer(struct v4l2_subdev *sd, void *arg)
+{
+	int rc = 0;
+	struct venc_inst *inst = sd->dev_priv;
+	struct video_client_ctx *client_ctx = &inst->venc_client;
+	struct mem_region *mregion = arg;
+	if (!client_ctx || !mregion) {
+		WFD_MSG_ERR("Invalid input\n");
+		return -EINVAL;
+	}
+	WFD_MSG_DBG("size = %u, offset = %u fd = %d\n", mregion->size,
+				mregion->offset, mregion->fd);
+	rc = vidc_insert_addr_table(client_ctx, BUFFER_TYPE_OUTPUT,
+					mregion->cookie,
+					(unsigned long *)&mregion->kvaddr,
+					mregion->fd,
+					mregion->offset,
+					32,
+					mregion->size);
+	if (rc == (u32)false) {
+		WFD_MSG_ERR("Failed to insert outbuf in table\n");
+		rc = -EINVAL;
+		goto err;
+	}
+	WFD_MSG_DBG("size = %u, %p\n", mregion->size, mregion->kvaddr);
+
+	rc = vcd_set_buffer(client_ctx->vcd_handle,
+				    VCD_BUFFER_OUTPUT, (u8 *) mregion->kvaddr,
+				    mregion->size);
+	if (rc)
+		WFD_MSG_ERR("Failed to set outbuf on encoder\n");
+err:
+	return rc;
+}
+
+static long venc_fill_outbuf(struct v4l2_subdev *sd, void *arg)
+{
+	int rc = 0;
+	struct venc_inst *inst = sd->dev_priv;
+	struct video_client_ctx *client_ctx = &inst->venc_client;
+	struct mem_region *mregion = arg;
+	struct vcd_frame_data vcd_frame = {0};
+	unsigned long kernel_vaddr, phy_addr, user_vaddr;
+	int pmem_fd;
+	struct file *file;
+	s32 buffer_index = -1;
+
+	user_vaddr = mregion->cookie;
+	rc = vidc_lookup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT,
+			true, &user_vaddr,
+			&kernel_vaddr, &phy_addr, &pmem_fd, &file,
+			&buffer_index);
+	if (!rc) {
+		WFD_MSG_ERR("Address lookup failed\n");
+		goto err;
+	}
+	vcd_frame.virtual = (u8 *) kernel_vaddr;
+	vcd_frame.frm_clnt_data = mregion->cookie;
+	vcd_frame.alloc_len = mregion->size;
+
+	rc = vcd_fill_output_buffer(client_ctx->vcd_handle,	&vcd_frame);
+	if (rc)
+		WFD_MSG_ERR("Failed to fill output buffer on encoder");
+err:
+	return rc;
+}
+
+static long venc_encode_frame(struct v4l2_subdev *sd, void *arg)
+{
+	int rc = 0;
+	struct venc_inst *inst = sd->dev_priv;
+	struct video_client_ctx *client_ctx = &inst->venc_client;
+	struct venc_buf_info *venc_buf = arg;
+	struct mem_region *mregion = venc_buf->mregion;
+	struct vcd_frame_data vcd_input_buffer = {0};
+	int64_t ts = 0;
+
+	ts = venc_buf->timestamp;
+	do_div(ts, NSEC_PER_USEC);
+
+	vcd_input_buffer.virtual = mregion->kvaddr;
+	vcd_input_buffer.frm_clnt_data = (u32)mregion;
+	vcd_input_buffer.ip_frm_tag = (u32)mregion;
+	vcd_input_buffer.data_len = mregion->size;
+	vcd_input_buffer.time_stamp = ts;
+	vcd_input_buffer.offset = 0;
+
+	rc = vcd_encode_frame(client_ctx->vcd_handle,
+			&vcd_input_buffer);
+
+	if (rc)
+		WFD_MSG_ERR("encode frame failed\n");
+	return rc;
+}
+
+static long venc_alloc_recon_buffers(struct v4l2_subdev *sd, void *arg)
+{
+	int rc = 0;
+	struct venc_inst *inst = sd->dev_priv;
+	struct video_client_ctx *client_ctx = &inst->venc_client;
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_buffer_size control;
+	struct vcd_property_enc_recon_buffer *ctrl = NULL;
+	unsigned long phy_addr;
+	int i = 0;
+	int flags = 0;
+	u32 len;
+	control.width = inst->width;
+	control.height = inst->height;
+	vcd_property_hdr.prop_id = VCD_I_GET_RECON_BUFFER_SIZE;
+	vcd_property_hdr.sz = sizeof(struct vcd_property_buffer_size);
+
+	rc = vcd_get_property(client_ctx->vcd_handle,
+					&vcd_property_hdr, &control);
+	if (rc) {
+		WFD_MSG_ERR("Failed to get recon buf size\n");
+		goto err;
+	}
+	flags = ION_HEAP(ION_CP_MM_HEAP_ID);
+	flags |= inst->secure ? ION_SECURE : ION_HEAP(ION_IOMMU_HEAP_ID);
+
+	if (vcd_get_ion_status()) {
+		for (i = 0; i < 4; ++i) {
+			ctrl = &client_ctx->recon_buffer[i];
+			ctrl->buffer_size = control.size;
+			ctrl->pmem_fd = 0;
+			ctrl->offset = 0;
+			ctrl->user_virtual_addr = (void *)i;
+			client_ctx->recon_buffer_ion_handle[i]
+				= ion_alloc(client_ctx->user_ion_client,
+			control.size, SZ_8K, flags);
+
+			ctrl->kernel_virtual_addr = ion_map_kernel(
+				client_ctx->user_ion_client,
+				client_ctx->recon_buffer_ion_handle[i],	0);
+
+			rc = ion_map_iommu(client_ctx->user_ion_client,
+				client_ctx->recon_buffer_ion_handle[i],
+				VIDEO_DOMAIN, VIDEO_MAIN_POOL, SZ_4K,
+				0, &phy_addr, (unsigned long *)&len, 0, 0);
+			if (rc) {
+				WFD_MSG_ERR("Failed to allo recon buffers\n");
+				break;
+			}
+			ctrl->physical_addr =  (u8 *) phy_addr;
+			ctrl->dev_addr = ctrl->physical_addr;
+			vcd_property_hdr.prop_id = VCD_I_RECON_BUFFERS;
+			vcd_property_hdr.sz =
+				sizeof(struct vcd_property_enc_recon_buffer);
+			rc = vcd_set_property(client_ctx->vcd_handle,
+					&vcd_property_hdr, ctrl);
+			if (rc) {
+				WFD_MSG_ERR("Failed to set recon buffers\n");
+				break;
+			}
+		}
+	} else {
+		WFD_MSG_ERR("PMEM not suported\n");
+		return -ENOMEM;
+	}
+err:
+	return rc;
+}
+
+static long venc_free_output_buffer(struct v4l2_subdev *sd, void *arg)
+{
+	int rc = 0;
+	struct venc_inst *inst = sd->dev_priv;
+	struct video_client_ctx *client_ctx = &inst->venc_client;
+	struct mem_region *mregion = arg;
+	unsigned long kernel_vaddr, user_vaddr;
+
+	if (!client_ctx || !mregion) {
+		WFD_MSG_ERR("Invalid input\n");
+		return -EINVAL;
+	}
+
+	user_vaddr = mregion->cookie;
+	rc = vidc_delete_addr_table(client_ctx, BUFFER_TYPE_OUTPUT,
+				user_vaddr,
+				&kernel_vaddr);
+	if (!rc) {
+		WFD_MSG_ERR("Failed to delete buf from address table\n");
+		return -EINVAL;
+	}
+	return vcd_free_buffer(client_ctx->vcd_handle, VCD_BUFFER_OUTPUT,
+					 (u8 *)kernel_vaddr);
+}
+
+static long venc_flush_buffers(struct v4l2_subdev *sd, void *arg)
+{
+	int rc = 0;
+	struct venc_inst *inst = sd->dev_priv;
+	struct video_client_ctx *client_ctx = &inst->venc_client;
+	if (!client_ctx) {
+		WFD_MSG_ERR("Invalid input\n");
+		return -EINVAL;
+	}
+	rc = vcd_flush(client_ctx->vcd_handle, VCD_FLUSH_INPUT);
+	if (rc) {
+		WFD_MSG_ERR("Failed to flush input buffers\n");
+		rc = -EIO;
+		goto flush_failed;
+	}
+	wait_for_completion(&client_ctx->event);
+	if (client_ctx->event_status) {
+		WFD_MSG_ERR("callback for vcd_flush input returned error: %u",
+				client_ctx->event_status);
+		rc = -EIO;
+		goto flush_failed;
+	}
+	rc = vcd_flush(client_ctx->vcd_handle, VCD_FLUSH_OUTPUT);
+	if (rc) {
+		WFD_MSG_ERR("Failed to flush output buffers\n");
+		rc = -EIO;
+		goto flush_failed;
+	}
+	wait_for_completion(&client_ctx->event);
+	if (client_ctx->event_status) {
+		WFD_MSG_ERR("callback for vcd_flush output returned error: %u",
+				client_ctx->event_status);
+		rc = -EIO;
+		goto flush_failed;
+	}
+
+flush_failed:
+	return rc;
+}
+
+static long venc_free_input_buffer(struct v4l2_subdev *sd, void *arg)
+{
+	int del_rc = 0, free_rc = 0;
+	struct venc_inst *inst = sd->dev_priv;
+	struct video_client_ctx *client_ctx = &inst->venc_client;
+	struct mem_region *mregion = arg;
+	unsigned long vidc_kvaddr;
+
+	if (!client_ctx || !mregion) {
+		WFD_MSG_ERR("Invalid input\n");
+		return -EINVAL;
+	}
+
+	del_rc = vidc_delete_addr_table(client_ctx, BUFFER_TYPE_INPUT,
+				(unsigned long)mregion->kvaddr,
+				&vidc_kvaddr);
+	/*
+	 * Even if something went wrong in when
+	 * deleting from table, call vcd_free_buf
+	 */
+	if (del_rc == (u32)false) {
+		WFD_MSG_ERR("Failed to delete buf from address table\n");
+		del_rc = -ENOKEY;
+	} else if ((u8 *)vidc_kvaddr != mregion->kvaddr) {
+		WFD_MSG_ERR("Failed to find expected buffer\n");
+		del_rc = -EINVAL;
+	} else
+		del_rc = 0;
+
+	free_rc = vcd_free_buffer(client_ctx->vcd_handle, VCD_BUFFER_INPUT,
+					 (u8 *)vidc_kvaddr);
+
+	if (free_rc) {
+		WFD_MSG_ERR("Failed to free buffer from encoder\n");
+		free_rc = -EINVAL;
+	}
+
+	return del_rc ? del_rc : free_rc;
+}
+
+static long venc_free_recon_buffers(struct v4l2_subdev *sd, void *arg)
+{
+	int rc = 0;
+	struct venc_inst *inst = sd->dev_priv;
+	struct video_client_ctx *client_ctx = &inst->venc_client;
+	struct vcd_property_hdr vcd_property_hdr;
+	int i;
+
+	if (vcd_get_ion_status()) {
+		for (i = 0; i < 4; i++) {
+			vcd_property_hdr.prop_id = VCD_I_FREE_RECON_BUFFERS;
+			vcd_property_hdr.sz =
+				sizeof(struct vcd_property_buffer_size);
+			rc = vcd_set_property(client_ctx->vcd_handle,
+			&vcd_property_hdr, &client_ctx->recon_buffer[i]);
+			if (rc)
+				WFD_MSG_ERR("Failed to free recon buffer\n");
+
+			if (client_ctx->recon_buffer_ion_handle[i]) {
+				ion_unmap_iommu(client_ctx->user_ion_client,
+					 client_ctx->recon_buffer_ion_handle[i],
+					 VIDEO_DOMAIN, VIDEO_MAIN_POOL);
+				ion_unmap_kernel(client_ctx->user_ion_client,
+					client_ctx->recon_buffer_ion_handle[i]);
+				ion_free(client_ctx->user_ion_client,
+					client_ctx->recon_buffer_ion_handle[i]);
+				client_ctx->recon_buffer_ion_handle[i] = NULL;
+			}
+		}
+	}
+	return rc;
+}
+
+static long venc_set_property(struct v4l2_subdev *sd, void *arg)
+{
+	int rc = 0;
+	struct venc_inst *inst = sd->dev_priv;
+	struct v4l2_control *ctrl = arg;
+	struct video_client_ctx *client_ctx = &inst->venc_client;
+	switch (ctrl->id) {
+	case V4L2_CID_MPEG_VIDEO_BITRATE:
+		rc = venc_set_bitrate(client_ctx, ctrl->value);
+		break;
+	case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+		rc = venc_set_bitrate_mode(client_ctx, ctrl->value);
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD:
+		rc = venc_set_h264_intra_period(client_ctx, ctrl->value);
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+		rc = venc_set_codec_level(client_ctx, ctrl->id, ctrl->value);
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+		rc = venc_set_codec_profile(client_ctx, ctrl->id, ctrl->value);
+		break;
+	case V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE:
+		rc = venc_request_frame(client_ctx, ctrl->value);
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP:
+		rc = venc_set_qp_value(client_ctx, ctrl->id, ctrl->value);
+		break;
+	case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP:
+	case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP:
+	case V4L2_CID_MPEG_VIDEO_H263_MIN_QP:
+	case V4L2_CID_MPEG_VIDEO_H263_MAX_QP:
+	case V4L2_CID_MPEG_VIDEO_H264_MIN_QP:
+	case V4L2_CID_MPEG_VIDEO_H264_MAX_QP:
+		rc = venc_set_qp_range(client_ctx, ctrl->id, ctrl->value);
+		break;
+	case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
+		rc = venc_set_header_mode(client_ctx, ctrl->value);
+		break;
+	case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES:
+	case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB:
+	case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
+		rc = venc_set_multislicing_mode(client_ctx, ctrl->id,
+				ctrl->value);
+		break;
+	case V4L2_CID_MPEG_QCOM_SET_PERF_LEVEL:
+		rc = venc_set_max_perf_level(client_ctx, ctrl->value);
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
+		rc = venc_set_entropy_mode(client_ctx, ctrl->value);
+		break;
+	case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB:
+		rc = venc_set_cyclic_intra_refresh_mb(client_ctx, ctrl->value);
+		break;
+	default:
+		WFD_MSG_ERR("Set property not suported: %d\n", ctrl->id);
+		rc = -ENOTSUPP;
+		break;
+	}
+	return rc;
+}
+
+static long venc_get_property(struct v4l2_subdev *sd, void *arg)
+{
+	int rc = 0;
+	struct venc_inst *inst = sd->dev_priv;
+	struct v4l2_control *ctrl = arg;
+	struct video_client_ctx *client_ctx = &inst->venc_client;
+
+	switch (ctrl->id) {
+	case V4L2_CID_MPEG_VIDEO_BITRATE:
+		rc = venc_get_bitrate(client_ctx, &ctrl->value);
+		break;
+	case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+		rc = venc_get_bitrate_mode(client_ctx, &ctrl->value);
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+		rc = venc_get_codec_level(client_ctx, ctrl->id, &ctrl->value);
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+		rc = venc_get_codec_profile(client_ctx, ctrl->id, &ctrl->value);
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD:
+		rc = venc_get_h264_intra_period(client_ctx, &ctrl->value);
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP:
+		rc = venc_get_qp_value(client_ctx, ctrl->id, &ctrl->value);
+		break;
+	case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP:
+	case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP:
+	case V4L2_CID_MPEG_VIDEO_H263_MIN_QP:
+	case V4L2_CID_MPEG_VIDEO_H263_MAX_QP:
+	case V4L2_CID_MPEG_VIDEO_H264_MIN_QP:
+	case V4L2_CID_MPEG_VIDEO_H264_MAX_QP:
+		rc = venc_get_qp_range(client_ctx, ctrl->id, &ctrl->value);
+		break;
+	case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
+		rc = venc_get_header_mode(client_ctx, &ctrl->value);
+		break;
+	case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES:
+	case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB:
+	case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
+		rc = venc_get_multislicing_mode(client_ctx, ctrl->id,
+				&ctrl->value);
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
+		rc = venc_get_entropy_mode(client_ctx, &ctrl->value);
+		break;
+	case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB:
+		rc = venc_get_cyclic_intra_refresh_mb(client_ctx, &ctrl->value);
+		break;
+	default:
+		WFD_MSG_ERR("Get property not suported: %d\n", ctrl->id);
+		rc = -ENOTSUPP;
+		break;
+	}
+	return rc;
+}
+
+long venc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+	long rc = 0;
+	switch (cmd) {
+	case OPEN:
+		rc = venc_open(sd, arg);
+		break;
+	case CLOSE:
+		rc = venc_close(sd, arg);
+		break;
+	case ENCODE_START:
+		rc = venc_start(sd);
+		break;
+	case ENCODE_FRAME:
+		venc_encode_frame(sd, arg);
+		break;
+	case ENCODE_STOP:
+		rc = venc_stop(sd);
+		break;
+	case SET_PROP:
+		rc = venc_set_property(sd, arg);
+		break;
+	case GET_PROP:
+		rc = venc_get_property(sd, arg);
+		break;
+	case GET_BUFFER_REQ:
+		rc = venc_get_buffer_req(sd, arg);
+		break;
+	case SET_BUFFER_REQ:
+		rc = venc_set_buffer_req(sd, arg);
+		break;
+	case FREE_BUFFER:
+		break;
+	case FILL_OUTPUT_BUFFER:
+		rc = venc_fill_outbuf(sd, arg);
+		break;
+	case SET_FORMAT:
+		rc = venc_set_format(sd, arg);
+		break;
+	case SET_FRAMERATE:
+		rc = venc_set_framerate(sd, arg);
+		break;
+	case SET_INPUT_BUFFER:
+		rc = venc_set_input_buffer(sd, arg);
+		break;
+	case SET_OUTPUT_BUFFER:
+		rc = venc_set_output_buffer(sd, arg);
+		break;
+	case ALLOC_RECON_BUFFERS:
+		rc = venc_alloc_recon_buffers(sd, arg);
+		break;
+	case FREE_OUTPUT_BUFFER:
+		rc = venc_free_output_buffer(sd, arg);
+		break;
+	case FREE_INPUT_BUFFER:
+		rc = venc_free_input_buffer(sd, arg);
+		break;
+	case FREE_RECON_BUFFERS:
+		rc = venc_free_recon_buffers(sd, arg);
+		break;
+	case ENCODE_FLUSH:
+		rc = venc_flush_buffers(sd, arg);
+		break;
+	default:
+		rc = -1;
+		break;
+	}
+	return rc;
+}
diff --git a/drivers/media/video/msm/wfd/enc-subdev.h b/drivers/media/video/msm/wfd/enc-subdev.h
new file mode 100644
index 0000000..5873e62
--- /dev/null
+++ b/drivers/media/video/msm/wfd/enc-subdev.h
@@ -0,0 +1,84 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2 and
+* only version 2 as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+*/
+
+#ifndef _WFD_ENC_SUBDEV_
+#define _WFD_ENC_SUBDEV_
+
+#include <linux/ion.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-core.h>
+#define VENC_MAGIC_IOCTL 'V'
+
+struct mem_region {
+	struct list_head list;
+	u8 *kvaddr;
+	u8 *paddr;
+	u32 size;
+	u32 offset;
+	u32 fd;
+	u32 cookie;
+	struct ion_handle *ion_handle;
+};
+struct bufreq {
+	u32 count;
+	u32 height;
+	u32 width;
+	u32 size;
+};
+
+struct venc_buf_info {
+	u64 timestamp;
+	struct mem_region *mregion;
+};
+
+struct venc_msg_ops {
+	void *cookie;
+	void *cbdata;
+	int secure;
+	void (*op_buffer_done)(void *cookie, u32 status,
+			struct vb2_buffer *buf);
+	void (*ip_buffer_done)(void *cookie, u32 status,
+			struct mem_region *mregion);
+};
+
+#define OPEN  _IOR('V', 1, void *)
+#define CLOSE  _IO('V', 2)
+#define ENCODE_START  _IO('V', 3)
+#define ENCODE_FRAME  _IOW('V', 4, struct venc_buf_info *)
+#define PAUSE  _IO('V', 5)
+#define RESUME  _IO('V', 6)
+#define FLUSH  _IO('V', 7)
+#define ENCODE_STOP  _IO('V', 8)
+#define SET_PROP  _IO('V', 9)
+#define GET_PROP  _IO('V', 10)
+#define SET_BUFFER_REQ  _IOWR('V', 11, struct v4l2_requestbuffers *)
+#define GET_BUFFER_REQ  _IOWR('V', 12, struct v4l2_requestbuffers *)
+#define ALLOCATE_BUFFER  _IO('V', 13)
+#define FREE_BUFFER  _IO('V', 14)
+#define FILL_OUTPUT_BUFFER  _IO('V', 15)
+#define SET_FORMAT _IOW('V', 16, struct v4l2_format *)
+#define SET_FRAMERATE _IOW('V', 17, struct v4l2_fract *)
+#define SET_INPUT_BUFFER _IOWR('V', 18, struct mem_region *)
+#define SET_OUTPUT_BUFFER _IOWR('V', 19, struct mem_region *)
+#define ALLOC_RECON_BUFFERS _IO('V', 20)
+#define FREE_OUTPUT_BUFFER _IOWR('V', 21, struct mem_region *)
+#define FREE_INPUT_BUFFER _IOWR('V', 22, struct mem_region *)
+#define FREE_RECON_BUFFERS _IO('V', 23)
+#define ENCODE_FLUSH _IO('V', 24)
+
+extern int venc_init(struct v4l2_subdev *sd, u32 val);
+extern int venc_load_fw(struct v4l2_subdev *sd);
+extern long venc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg);
+
+
+#endif /* _WFD_ENC_SUBDEV_ */
diff --git a/drivers/media/video/msm/wfd/mdp-subdev.c b/drivers/media/video/msm/wfd/mdp-subdev.c
new file mode 100644
index 0000000..a6d244f
--- /dev/null
+++ b/drivers/media/video/msm/wfd/mdp-subdev.c
@@ -0,0 +1,200 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2 and
+* only version 2 as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+*/
+#include "mdp-subdev.h"
+#include "wfd-util.h"
+#include <media/videobuf2-core.h>
+#include <linux/msm_mdp.h>
+
+struct mdp_instance {
+	struct fb_info *mdp;
+	u32 height;
+	u32 width;
+};
+
+int mdp_init(struct v4l2_subdev *sd, u32 val)
+{
+	return 0;
+}
+int mdp_open(struct v4l2_subdev *sd, void *arg)
+{
+	struct mdp_instance *inst = kzalloc(sizeof(struct mdp_instance),
+					GFP_KERNEL);
+	void **cookie = (void **)arg;
+	int rc = 0;
+	struct fb_info *fbi = NULL;
+
+	if (!inst) {
+		WFD_MSG_ERR("Out of memory\n");
+		return -ENOMEM;
+	}
+
+	fbi = msm_fb_get_writeback_fb();
+	if (!fbi) {
+		WFD_MSG_ERR("Failed to acquire mdp instance\n");
+		rc = -ENODEV;
+		goto exit;
+	}
+
+	msm_fb_writeback_init(fbi);
+	inst->mdp = fbi;
+	*cookie = inst;
+	return rc;
+exit:
+	kfree(inst);
+	return rc;
+}
+
+int mdp_start(struct v4l2_subdev *sd, void *arg)
+{
+	struct mdp_instance *inst = arg;
+	int rc = 0;
+	struct fb_info *fbi = NULL;
+	if (inst) {
+		rc = msm_fb_writeback_start(inst->mdp);
+		if (rc) {
+			WFD_MSG_ERR("Failed to start MDP mode\n");
+			goto exit;
+		}
+		fbi = msm_fb_get_writeback_fb();
+		if (!fbi) {
+			WFD_MSG_ERR("Failed to acquire mdp instance\n");
+			rc = -ENODEV;
+			goto exit;
+		}
+	}
+exit:
+	return rc;
+}
+int mdp_stop(struct v4l2_subdev *sd, void *arg)
+{
+	struct mdp_instance *inst = arg;
+	int rc = 0;
+	if (inst) {
+		rc = msm_fb_writeback_stop(inst->mdp);
+		if (rc) {
+			WFD_MSG_ERR("Failed to stop writeback mode\n");
+			return rc;
+		}
+	}
+	return 0;
+}
+int mdp_close(struct v4l2_subdev *sd, void *arg)
+{
+	struct mdp_instance *inst = arg;
+	struct fb_info *fbi = NULL;
+	if (inst) {
+		fbi = (struct fb_info *)inst->mdp;
+		msm_fb_writeback_terminate(fbi);
+		kfree(inst);
+	}
+	return 0;
+}
+int mdp_q_buffer(struct v4l2_subdev *sd, void *arg)
+{
+	int rc = 0;
+	struct mdp_buf_info *binfo = arg;
+	struct msmfb_data fbdata;
+	struct mdp_instance *inst;
+	if (!binfo || !binfo->inst || !binfo->cookie) {
+		WFD_MSG_ERR("Invalid argument\n");
+		return -EINVAL;
+	}
+	inst = binfo->inst;
+	fbdata.offset = binfo->offset;
+	fbdata.memory_id = binfo->fd;
+	fbdata.iova = binfo->paddr;
+	fbdata.id = 0;
+	fbdata.flags = 0;
+	fbdata.priv = (uint32_t)binfo->cookie;
+
+	WFD_MSG_INFO("queue buffer to mdp with offset = %u,"
+			"fd = %u, priv = %p, iova = %p\n",
+			fbdata.offset, fbdata.memory_id,
+			(void *)fbdata.priv, (void *)fbdata.iova);
+	rc = msm_fb_writeback_queue_buffer(inst->mdp, &fbdata);
+
+	if (rc)
+		WFD_MSG_ERR("Failed to queue buffer\n");
+	return rc;
+}
+int mdp_dq_buffer(struct v4l2_subdev *sd, void *arg)
+{
+	int rc = 0;
+	struct mdp_buf_info *obuf = arg;
+	struct msmfb_data fbdata;
+	struct mdp_instance *inst;
+	if (!arg) {
+		WFD_MSG_ERR("Invalid argument\n");
+		return -EINVAL;
+	}
+
+	inst = obuf->inst;
+	fbdata.flags = MSMFB_WRITEBACK_DEQUEUE_BLOCKING;
+	rc = msm_fb_writeback_dequeue_buffer(inst->mdp, &fbdata);
+	if (rc) {
+		WFD_MSG_ERR("Failed to dequeue buffer\n");
+		return rc;
+	}
+	WFD_MSG_DBG("dequeue buf from mdp with priv = %u\n",
+			fbdata.priv);
+	obuf->cookie = (void *)fbdata.priv;
+	return rc;
+}
+int mdp_set_prop(struct v4l2_subdev *sd, void *arg)
+{
+	struct mdp_prop *prop = (struct mdp_prop *)arg;
+	struct mdp_instance *inst = prop->inst;
+	if (!prop || !inst) {
+		WFD_MSG_ERR("Invalid arguments\n");
+		return -EINVAL;
+	}
+	inst->height = prop->height;
+	inst->width = prop->width;
+	return 0;
+}
+long mdp_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+	int rc = 0;
+	if (!sd) {
+		WFD_MSG_ERR("Invalid arguments\n");
+		return -EINVAL;
+	}
+	switch (cmd) {
+	case MDP_Q_BUFFER:
+		rc = mdp_q_buffer(sd, arg);
+		break;
+	case MDP_DQ_BUFFER:
+		rc = mdp_dq_buffer(sd, arg);
+		break;
+	case MDP_OPEN:
+		rc = mdp_open(sd, arg);
+		break;
+	case MDP_START:
+		rc = mdp_start(sd, arg);
+		break;
+	case MDP_STOP:
+		rc = mdp_stop(sd, arg);
+		break;
+	case MDP_SET_PROP:
+		rc = mdp_set_prop(sd, arg);
+		break;
+	case MDP_CLOSE:
+		rc = mdp_close(sd, arg);
+		break;
+	default:
+		WFD_MSG_ERR("IOCTL: %u not supported\n", cmd);
+		rc = -EINVAL;
+		break;
+	}
+	return rc;
+}
diff --git a/drivers/media/video/msm/wfd/mdp-subdev.h b/drivers/media/video/msm/wfd/mdp-subdev.h
new file mode 100644
index 0000000..081fead
--- /dev/null
+++ b/drivers/media/video/msm/wfd/mdp-subdev.h
@@ -0,0 +1,58 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2 and
+* only version 2 as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+*/
+
+#ifndef _WFD_MDP_SUBDEV_
+#define _WFD_MDP_SUBDEV_
+
+#include <linux/videodev2.h>
+#include <media/v4l2-subdev.h>
+
+#define MDP_MAGIC_IOCTL 'M'
+
+struct mdp_buf_info {
+	void *inst;
+	void *cookie;
+	u32 fd;
+	u32 offset;
+	u32 kvaddr;
+	u32 paddr;
+};
+
+struct mdp_prop {
+	void *inst;
+	u32 height;
+	u32 width;
+};
+
+static inline bool mdp_buf_info_equals(struct mdp_buf_info *a,
+		struct mdp_buf_info *b)
+{
+	return a->inst == b->inst
+		&& a->fd == b->fd
+		&& a->offset == b->offset
+		&& a->kvaddr == b->kvaddr
+		&& a->paddr == b->paddr;
+}
+
+#define MDP_Q_BUFFER  _IOW(MDP_MAGIC_IOCTL, 1, struct mdp_buf_info *)
+#define MDP_DQ_BUFFER  _IOR(MDP_MAGIC_IOCTL, 2, struct mdp_out_buf *)
+#define MDP_OPEN  _IOR(MDP_MAGIC_IOCTL, 3, void **)
+#define MDP_SET_PROP  _IOW(MDP_MAGIC_IOCTL, 4, struct mdp_prop *)
+#define MDP_CLOSE  _IOR(MDP_MAGIC_IOCTL, 5, void *)
+#define MDP_START  _IOR(MDP_MAGIC_IOCTL, 6, void *)
+#define MDP_STOP  _IOR(MDP_MAGIC_IOCTL, 7, void *)
+extern int mdp_init(struct v4l2_subdev *sd, u32 val);
+extern long mdp_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg);
+
+
+#endif /* _WFD_MDP_SUBDEV_ */
diff --git a/drivers/media/video/msm/wfd/vsg-subdev.c b/drivers/media/video/msm/wfd/vsg-subdev.c
new file mode 100644
index 0000000..73b840b
--- /dev/null
+++ b/drivers/media/video/msm/wfd/vsg-subdev.c
@@ -0,0 +1,700 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2 and
+* only version 2 as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+*/
+#include <linux/hrtimer.h>
+#include <linux/time.h>
+#include <linux/list.h>
+#include <media/videobuf2-core.h>
+#include "enc-subdev.h"
+#include "vsg-subdev.h"
+#include "wfd-util.h"
+
+#define DEFAULT_FRAME_INTERVAL (66*NSEC_PER_MSEC)
+#define DEFAULT_MAX_FRAME_INTERVAL (1*NSEC_PER_SEC)
+#define DEFAULT_MODE ((enum vsg_modes)VSG_MODE_CFR)
+#define MAX_BUFS_BUSY_WITH_ENC 5
+
+static int vsg_release_input_buffer(struct vsg_context *context,
+		struct vsg_buf_info *buf)
+{
+	WFD_MSG_DBG("Releasing frame with ts %lld ms, paddr %p\n",
+			timespec_to_ns(&buf->time),
+			(void *)buf->mdp_buf_info.paddr);
+
+	if (buf->flags & VSG_NEVER_RELEASE)
+		WFD_MSG_WARN("Warning releasing buffer that's"
+				"not supposed to be released\n");
+
+	return context->vmops.release_input_frame(context->vmops.cbdata,
+			buf);
+
+}
+
+static int vsg_encode_frame(struct vsg_context *context,
+		struct vsg_buf_info *buf)
+{
+	WFD_MSG_DBG("Encoding frame with ts %lld ms, paddr %p\n",
+			timespec_to_ns(&buf->time),
+			(void *)buf->mdp_buf_info.paddr);
+
+	return context->vmops.encode_frame(context->vmops.cbdata,
+			buf);
+}
+
+static void vsg_set_last_buffer(struct vsg_context *context,
+		struct vsg_buf_info *buf)
+{
+	if (buf->flags & VSG_NEVER_SET_LAST_BUFFER)
+		WFD_MSG_WARN("Shouldn't be setting this to last buffer\n");
+
+	context->last_buffer = buf;
+
+	WFD_MSG_DBG("Setting last buffer to paddr %p\n",
+			(void *)buf->mdp_buf_info.paddr);
+}
+
+static void vsg_encode_helper_func(struct work_struct *task)
+{
+	struct vsg_encode_work *work =
+		container_of(task, struct vsg_encode_work, work);
+
+	/*
+	 * Note: don't need to lock for context below as we only
+	 * access fields that are "static".
+	 */
+	int rc = vsg_encode_frame(work->context, work->buf);
+	if (rc < 0) {
+		mutex_lock(&work->context->mutex);
+		work->context->state = VSG_STATE_ERROR;
+		mutex_unlock(&work->context->mutex);
+	}
+	kfree(work);
+}
+
+static void vsg_work_func(struct work_struct *task)
+{
+	struct vsg_work *work =
+		container_of(task, struct vsg_work, work);
+	struct vsg_encode_work *encode_work;
+	struct vsg_context *context = work->context;
+	struct vsg_buf_info *buf_info = NULL, *temp = NULL;
+	int rc = 0, count = 0;
+	mutex_lock(&context->mutex);
+
+	if (list_empty(&context->free_queue.node)) {
+		WFD_MSG_DBG("%s: queue empty doing nothing\n", __func__);
+		goto err_skip_encode;
+	} else if (context->state != VSG_STATE_STARTED) {
+		WFD_MSG_DBG("%s: vsg is stopped or in error state "
+				"doing nothing\n", __func__);
+		goto err_skip_encode;
+	}
+
+	list_for_each_entry(temp, &context->busy_queue.node, node) {
+		if (++count > MAX_BUFS_BUSY_WITH_ENC) {
+			WFD_MSG_WARN("Skipping encode, too many "
+				"buffers with encoder");
+			goto err_skip_encode;
+		}
+	}
+
+	buf_info = list_first_entry(&context->free_queue.node,
+			struct vsg_buf_info, node);
+	list_del(&buf_info->node);
+	INIT_LIST_HEAD(&buf_info->node);
+
+	ktime_get_ts(&buf_info->time);
+	hrtimer_forward_now(&context->threshold_timer, ns_to_ktime(
+				context->max_frame_interval));
+
+	temp = NULL;
+	list_for_each_entry(temp, &context->busy_queue.node, node) {
+		if (mdp_buf_info_equals(&temp->mdp_buf_info,
+					&buf_info->mdp_buf_info)) {
+			temp->flags |= VSG_NEVER_RELEASE;
+		}
+	}
+
+	if (context->last_buffer &&
+		mdp_buf_info_equals(&context->last_buffer->mdp_buf_info,
+			&buf_info->mdp_buf_info)) {
+		context->last_buffer->flags |= VSG_NEVER_RELEASE;
+	}
+
+	encode_work = kmalloc(sizeof(*encode_work), GFP_KERNEL);
+	encode_work->buf = buf_info;
+	encode_work->context = context;
+	INIT_WORK(&encode_work->work, vsg_encode_helper_func);
+	rc = queue_work(context->work_queue, &encode_work->work);
+	if (!rc) {
+		WFD_MSG_ERR("Queueing buffer for encode failed\n");
+		kfree(encode_work);
+		encode_work = NULL;
+		goto err_skip_encode;
+	}
+
+	buf_info->flags |= VSG_BUF_BEING_ENCODED;
+	if (!(buf_info->flags & VSG_NEVER_SET_LAST_BUFFER)) {
+		if (context->last_buffer) {
+			struct vsg_buf_info *old_last_buffer =
+				context->last_buffer;
+			bool last_buf_with_us = old_last_buffer &&
+				!(old_last_buffer->flags &
+					VSG_BUF_BEING_ENCODED);
+			bool can_release = old_last_buffer &&
+				!(old_last_buffer->flags &
+					VSG_NEVER_RELEASE);
+
+			if (old_last_buffer && last_buf_with_us
+				&& can_release) {
+				vsg_release_input_buffer(context,
+					old_last_buffer);
+				kfree(old_last_buffer);
+			}
+		}
+		vsg_set_last_buffer(context, buf_info);
+	}
+
+	list_add_tail(&buf_info->node, &context->busy_queue.node);
+err_skip_encode:
+	mutex_unlock(&context->mutex);
+	kfree(work);
+}
+
+static void vsg_timer_helper_func(struct work_struct *task)
+{
+	struct vsg_work *work =
+		container_of(task, struct vsg_work, work);
+	struct vsg_work *new_work = NULL;
+	struct vsg_context *context = work->context;
+	int num_bufs_to_queue = 1, c = 0;
+
+	mutex_lock(&context->mutex);
+
+	if (context->state != VSG_STATE_STARTED)
+		goto err_locked;
+
+	if (list_empty(&context->free_queue.node)
+		&& context->last_buffer) {
+		struct vsg_buf_info *info = NULL, *buf_to_encode = NULL;
+
+		if (context->mode == VSG_MODE_CFR)
+			num_bufs_to_queue = 1;
+		else if (context->mode == VSG_MODE_VFR)
+			num_bufs_to_queue = 2;
+
+		for (c = 0; c < num_bufs_to_queue; ++c) {
+			info = kzalloc(sizeof(*info), GFP_KERNEL);
+
+			if (!info) {
+				WFD_MSG_ERR("Couldn't allocate memory in %s\n",
+					__func__);
+				goto err_locked;
+			}
+
+			buf_to_encode = context->last_buffer;
+
+			info->mdp_buf_info = buf_to_encode->mdp_buf_info;
+			info->flags = 0;
+			INIT_LIST_HEAD(&info->node);
+
+			list_add_tail(&info->node, &context->free_queue.node);
+			WFD_MSG_DBG("Regenerated frame with paddr %p\n",
+				(void *)info->mdp_buf_info.paddr);
+		}
+	}
+
+	for (c = 0; c < num_bufs_to_queue; ++c) {
+		new_work = kzalloc(sizeof(*new_work), GFP_KERNEL);
+		if (!new_work) {
+			WFD_MSG_ERR("Unable to allocate memory"
+					"to queue buffer\n");
+			goto err_locked;
+		}
+
+		INIT_WORK(&new_work->work, vsg_work_func);
+		new_work->context = context;
+		queue_work(context->work_queue, &new_work->work);
+	}
+
+err_locked:
+	mutex_unlock(&context->mutex);
+	kfree(work);
+}
+
+static enum hrtimer_restart vsg_threshold_timeout_func(struct hrtimer *timer)
+{
+	struct vsg_context *context = NULL;
+	struct vsg_work *task = NULL;
+
+	task = kzalloc(sizeof(*task), GFP_ATOMIC);
+	context = container_of(timer, struct vsg_context,
+			threshold_timer);
+	if (!task) {
+		WFD_MSG_ERR("Out of memory in %s", __func__);
+		goto threshold_err_bad_param;
+	} else if (!context) {
+		WFD_MSG_ERR("Context not proper in %s", __func__);
+		goto threshold_err_no_context;
+	}
+
+	INIT_WORK(&task->work, vsg_timer_helper_func);
+	task->context = context;
+
+	queue_work(context->work_queue, &task->work);
+threshold_err_bad_param:
+	hrtimer_forward_now(&context->threshold_timer, ns_to_ktime(
+				context->max_frame_interval));
+	return HRTIMER_RESTART;
+threshold_err_no_context:
+	return HRTIMER_NORESTART;
+}
+
+int vsg_init(struct v4l2_subdev *sd, u32 val)
+{
+	return 0;
+}
+
+static int vsg_open(struct v4l2_subdev *sd, void *arg)
+{
+	struct vsg_context *context = NULL;
+
+	if (!arg || !sd)
+		return -EINVAL;
+
+	context = kzalloc(sizeof(*context), GFP_KERNEL);
+	INIT_LIST_HEAD(&context->free_queue.node);
+	INIT_LIST_HEAD(&context->busy_queue.node);
+
+	context->vmops = *(struct vsg_msg_ops *)arg;
+	context->work_queue = create_singlethread_workqueue("v4l-vsg");
+
+	context->frame_interval = DEFAULT_FRAME_INTERVAL;
+	context->max_frame_interval = DEFAULT_MAX_FRAME_INTERVAL;
+
+	hrtimer_init(&context->threshold_timer, CLOCK_MONOTONIC,
+			HRTIMER_MODE_REL);
+	context->threshold_timer.function = vsg_threshold_timeout_func;
+
+	context->last_buffer = NULL;
+	context->mode = DEFAULT_MODE;
+	context->state = VSG_STATE_NONE;
+	mutex_init(&context->mutex);
+
+	sd->dev_priv = context;
+	return 0;
+}
+
+static int vsg_close(struct v4l2_subdev *sd)
+{
+	struct vsg_context *context = NULL;
+
+	if (!sd)
+		return -EINVAL;
+
+	context = (struct vsg_context *)sd->dev_priv;
+	destroy_workqueue(context->work_queue);
+	kfree(context);
+	return 0;
+}
+
+static int vsg_start(struct v4l2_subdev *sd)
+{
+	struct vsg_context *context = NULL;
+
+	if (!sd) {
+		WFD_MSG_ERR("ERROR, invalid arguments into %s\n", __func__);
+		return -EINVAL;
+	}
+
+	context = (struct vsg_context *)sd->dev_priv;
+
+	if (context->state == VSG_STATE_STARTED) {
+		WFD_MSG_ERR("VSG not stopped, start not allowed\n");
+		return -EINPROGRESS;
+	} else if (context->state == VSG_STATE_ERROR) {
+		WFD_MSG_ERR("VSG in error state, not allowed to restart\n");
+		return -ENOTRECOVERABLE;
+	}
+
+	context->state = VSG_STATE_STARTED;
+	hrtimer_start(&context->threshold_timer, ns_to_ktime(context->
+			max_frame_interval), HRTIMER_MODE_REL);
+	return 0;
+}
+
+static int vsg_stop(struct v4l2_subdev *sd)
+{
+	struct vsg_context *context = NULL;
+
+	if (!sd) {
+		WFD_MSG_ERR("ERROR, invalid arguments into %s\n", __func__);
+		return -EINVAL;
+	}
+
+	context = (struct vsg_context *)sd->dev_priv;
+
+	mutex_lock(&context->mutex);
+	context->state = VSG_STATE_STOPPED;
+	{ /*delete pending buffers as we're not going to encode them*/
+		struct list_head *pos, *next;
+		list_for_each_safe(pos, next, &context->free_queue.node) {
+			struct vsg_buf_info *temp =
+				list_entry(pos, struct vsg_buf_info, node);
+			list_del(&temp->node);
+			kfree(temp);
+		}
+	}
+
+	hrtimer_cancel(&context->threshold_timer);
+
+	mutex_unlock(&context->mutex);
+
+	flush_workqueue(context->work_queue);
+	return 0;
+}
+
+static long vsg_queue_buffer(struct v4l2_subdev *sd, void *arg)
+{
+	struct vsg_context *context = NULL;
+	struct vsg_buf_info *buf_info = kzalloc(sizeof(*buf_info), GFP_KERNEL);
+	int rc = 0;
+	bool push = false;
+
+	if (!arg || !sd) {
+		WFD_MSG_ERR("ERROR, invalid arguments into %s\n", __func__);
+		rc = -EINVAL;
+		goto queue_err_bad_param;
+	} else if (!buf_info) {
+		WFD_MSG_ERR("ERROR, out of memory in %s\n", __func__);
+		rc = -ENOMEM;
+		goto queue_err_bad_param;
+	}
+
+	context = (struct vsg_context *)sd->dev_priv;
+	mutex_lock(&context->mutex);
+
+	*buf_info = *(struct vsg_buf_info *)arg;
+	INIT_LIST_HEAD(&buf_info->node);
+	buf_info->flags = 0;
+	ktime_get_ts(&buf_info->time);
+
+	WFD_MSG_DBG("Queue frame with paddr %p\n",
+			(void *)buf_info->mdp_buf_info.paddr);
+
+	{ /*return pending buffers as we're not going to encode them*/
+		struct list_head *pos, *next;
+		list_for_each_safe(pos, next, &context->free_queue.node) {
+			struct vsg_buf_info *temp =
+				list_entry(pos, struct vsg_buf_info, node);
+			bool is_last_buffer = context->last_buffer &&
+				mdp_buf_info_equals(
+					&context->last_buffer->mdp_buf_info,
+					&temp->mdp_buf_info);
+
+			list_del(&temp->node);
+
+			if (!is_last_buffer &&
+				!(temp->flags & VSG_NEVER_RELEASE)) {
+				vsg_release_input_buffer(context, temp);
+				kfree(temp);
+			}
+		}
+	}
+
+	list_add_tail(&buf_info->node, &context->free_queue.node);
+
+	if (context->mode == VSG_MODE_VFR) {
+		if (!context->last_buffer)
+			push = true;
+		else {
+			struct timespec diff = timespec_sub(buf_info->time,
+					context->last_buffer->time);
+			struct timespec temp = ns_to_timespec(
+						context->frame_interval);
+
+			if (timespec_compare(&diff, &temp) >= 0)
+				push = true;
+		}
+	} else if (context->mode == VSG_MODE_CFR) {
+		if (!context->last_buffer) {
+			push = true;
+			/*
+			 * We need to reset the timer after pushing the buffer
+			 * otherwise, diff between two consecutive frames might
+			 * be less than max_frame_interval (for just one sample)
+			 */
+			hrtimer_forward_now(&context->threshold_timer,
+				ns_to_ktime(context->max_frame_interval));
+		}
+	}
+
+	if (push) {
+		struct vsg_work *new_work =
+			kzalloc(sizeof(*new_work), GFP_KERNEL);
+
+		INIT_WORK(&new_work->work, vsg_work_func);
+		new_work->context = context;
+		queue_work(context->work_queue, &new_work->work);
+	}
+
+	mutex_unlock(&context->mutex);
+queue_err_bad_param:
+	if (rc < 0)
+		kfree(buf_info);
+
+	return rc;
+}
+
+static long vsg_return_ip_buffer(struct v4l2_subdev *sd, void *arg)
+{
+	struct vsg_context *context = NULL;
+	struct vsg_buf_info *buf_info, *last_buffer,
+			*expected_buffer;
+	int rc = 0;
+
+	if (!arg || !sd) {
+		WFD_MSG_ERR("ERROR, invalid arguments into %s\n", __func__);
+		rc = -EINVAL;
+		goto return_ip_buf_err_bad_param;
+	}
+
+	context = (struct vsg_context *)sd->dev_priv;
+	mutex_lock(&context->mutex);
+	buf_info = (struct vsg_buf_info *)arg;
+	last_buffer = context->last_buffer;
+
+	expected_buffer = list_first_entry(&context->busy_queue.node,
+			struct vsg_buf_info, node);
+
+	WFD_MSG_DBG("Return frame with paddr %p\n",
+			(void *)buf_info->mdp_buf_info.paddr);
+
+	if (!expected_buffer) {
+		WFD_MSG_ERR("Unexpectedly received buffer from enc with "
+			"paddr %p\n", (void *)buf_info->mdp_buf_info.paddr);
+		goto return_ip_buf_bad_buf;
+	}
+
+	expected_buffer->flags &= ~VSG_BUF_BEING_ENCODED;
+	if (mdp_buf_info_equals(&expected_buffer->mdp_buf_info,
+				&buf_info->mdp_buf_info)) {
+		bool is_same_buffer = context->last_buffer &&
+			mdp_buf_info_equals(
+					&context->last_buffer->mdp_buf_info,
+					&expected_buffer->mdp_buf_info);
+
+		list_del(&expected_buffer->node);
+		if (!is_same_buffer &&
+			!(expected_buffer->flags & VSG_NEVER_RELEASE)) {
+			vsg_release_input_buffer(context, expected_buffer);
+			kfree(expected_buffer);
+		}
+	} else {
+		WFD_MSG_ERR("Returned buffer %p is not latest buffer, "
+				"expected %p\n",
+				(void *)buf_info->mdp_buf_info.paddr,
+				(void *)expected_buffer->mdp_buf_info.paddr);
+		rc = -EINVAL;
+		goto return_ip_buf_bad_buf;
+	}
+
+return_ip_buf_bad_buf:
+	mutex_unlock(&context->mutex);
+return_ip_buf_err_bad_param:
+	return rc;
+}
+
+static long vsg_set_frame_interval(struct v4l2_subdev *sd, void *arg)
+{
+	struct vsg_context *context = NULL;
+	int64_t interval;
+
+	if (!arg || !sd) {
+		WFD_MSG_ERR("ERROR, invalid arguments into %s\n", __func__);
+		return -EINVAL;
+	}
+
+	context = (struct vsg_context *)sd->dev_priv;
+	interval = *(int64_t *)arg;
+
+	if (interval <= 0) {
+		WFD_MSG_ERR("ERROR, invalid interval %lld into %s\n",
+				interval, __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&context->mutex);
+
+	context->frame_interval = interval;
+	if (interval > context->max_frame_interval) {
+		WFD_MSG_WARN("Changing max frame interval from %lld to %lld\n",
+				context->max_frame_interval, interval);
+		context->max_frame_interval = interval;
+	}
+
+	mutex_unlock(&context->mutex);
+	return 0;
+}
+
+static long vsg_get_frame_interval(struct v4l2_subdev *sd, void *arg)
+{
+	struct vsg_context *context = NULL;
+
+	if (!arg || !sd) {
+		WFD_MSG_ERR("ERROR, invalid arguments into %s\n", __func__);
+		return -EINVAL;
+	}
+
+	context = (struct vsg_context *)sd->dev_priv;
+	mutex_lock(&context->mutex);
+	*(int64_t *)arg = context->frame_interval;
+	mutex_unlock(&context->mutex);
+
+	return 0;
+}
+
+static long vsg_set_max_frame_interval(struct v4l2_subdev *sd, void *arg)
+{
+	struct vsg_context *context = NULL;
+	int64_t interval;
+
+	if (!arg || !sd) {
+		WFD_MSG_ERR("ERROR, invalid arguments into %s\n", __func__);
+		return -EINVAL;
+	}
+
+	context = (struct vsg_context *)sd->dev_priv;
+	interval = *(int64_t *)arg;
+
+	if (interval <= 0) {
+		WFD_MSG_ERR("ERROR, invalid interval %lld into %s\n",
+				interval, __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&context->mutex);
+
+	context->max_frame_interval = interval;
+	if (interval < context->frame_interval) {
+		WFD_MSG_WARN("Changing frame interval from %lld to %lld\n",
+				context->frame_interval, interval);
+		context->frame_interval = interval;
+	}
+
+	mutex_unlock(&context->mutex);
+
+	return 0;
+}
+
+static long vsg_get_max_frame_interval(struct v4l2_subdev *sd, void *arg)
+{
+	struct vsg_context *context = NULL;
+
+	if (!arg || !sd) {
+		WFD_MSG_ERR("ERROR, invalid arguments into %s\n", __func__);
+		return -EINVAL;
+	}
+
+	context = (struct vsg_context *)sd->dev_priv;
+	mutex_lock(&context->mutex);
+	*(int64_t *)arg = context->max_frame_interval;
+	mutex_unlock(&context->mutex);
+
+	return 0;
+}
+
+static long vsg_set_mode(struct v4l2_subdev *sd, void *arg)
+{
+	struct vsg_context *context = NULL;
+	enum vsg_modes *mode = NULL;
+	int rc = 0;
+
+	if (!arg || !sd) {
+		WFD_MSG_ERR("ERROR, invalid arguments into %s\n", __func__);
+		rc = -EINVAL;
+		goto set_mode_err_bad_parm;
+	}
+
+	context = (struct vsg_context *)sd->dev_priv;
+	mutex_lock(&context->mutex);
+	mode = arg;
+
+	switch (*mode) {
+	case VSG_MODE_CFR:
+		context->max_frame_interval = context->frame_interval;
+		/*fall through*/
+	case VSG_MODE_VFR:
+		context->mode = *mode;
+		break;
+	default:
+		context->mode = DEFAULT_MODE;
+		rc = -EINVAL;
+		goto set_mode_err_bad_mode;
+		break;
+	}
+
+set_mode_err_bad_mode:
+	mutex_unlock(&context->mutex);
+set_mode_err_bad_parm:
+	return rc;
+}
+
+long vsg_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+	int rc = 0;
+
+	WFD_MSG_DBG("VSG ioctl: %d\n", cmd);
+	if (sd == NULL)
+		return -EINVAL;
+
+	switch (cmd) {
+	case VSG_OPEN:
+		rc = vsg_open(sd, arg);
+		break;
+	case VSG_CLOSE:
+		rc = vsg_close(sd);
+		break;
+	case VSG_START:
+		rc = vsg_start(sd);
+		break;
+	case VSG_STOP:
+		rc = vsg_stop(sd);
+		break;
+	case VSG_Q_BUFFER:
+		rc = vsg_queue_buffer(sd, arg);
+		break;
+	case VSG_RETURN_IP_BUFFER:
+		rc = vsg_return_ip_buffer(sd, arg);
+		break;
+	case VSG_GET_FRAME_INTERVAL:
+		rc = vsg_get_frame_interval(sd, arg);
+		break;
+	case VSG_SET_FRAME_INTERVAL:
+		rc = vsg_set_frame_interval(sd, arg);
+		break;
+	case VSG_GET_MAX_FRAME_INTERVAL:
+		rc = vsg_get_max_frame_interval(sd, arg);
+		break;
+	case VSG_SET_MAX_FRAME_INTERVAL:
+		rc = vsg_set_max_frame_interval(sd, arg);
+		break;
+	case VSG_SET_MODE:
+		rc = vsg_set_mode(sd, arg);
+		break;
+	default:
+		rc = -ENOTSUPP;
+		break;
+	}
+
+	return rc;
+}
diff --git a/drivers/media/video/msm/wfd/vsg-subdev.h b/drivers/media/video/msm/wfd/vsg-subdev.h
new file mode 100644
index 0000000..dfc2e2e
--- /dev/null
+++ b/drivers/media/video/msm/wfd/vsg-subdev.h
@@ -0,0 +1,100 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2 and
+* only version 2 as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+*/
+
+#ifndef _WFD_VSG_SUBDEV_
+#define _WFD_VSG_SUBDEV_
+
+#include <linux/videodev2.h>
+#include <linux/list.h>
+#include <linux/ktime.h>
+#include <linux/workqueue.h>
+#include <media/v4l2-subdev.h>
+#include "mdp-subdev.h"
+
+#define VSG_MAGIC_IOCTL 'V'
+
+enum vsg_flags {
+	VSG_NEVER_RELEASE = 1<<0,
+	VSG_NEVER_SET_LAST_BUFFER = 1<<1,
+	VSG_BUF_BEING_ENCODED = 1<<2,
+};
+
+enum vsg_modes {
+	VSG_MODE_CFR,
+	VSG_MODE_VFR,
+};
+
+enum vsg_states {
+	VSG_STATE_NONE,
+	VSG_STATE_STARTED,
+	VSG_STATE_STOPPED,
+	VSG_STATE_ERROR
+};
+
+struct vsg_buf_info {
+	struct mdp_buf_info mdp_buf_info;
+	struct timespec time;
+	/* Internal */
+	struct list_head node;
+	uint32_t flags;
+};
+
+struct vsg_msg_ops {
+	void *cbdata;
+	int (*encode_frame)(void *cbdata, struct vsg_buf_info *buffer);
+	int (*release_input_frame)(void *cbdata, struct vsg_buf_info *buffer);
+};
+
+struct vsg_context {
+	struct vsg_buf_info	free_queue, busy_queue;
+	struct vsg_msg_ops vmops;
+	/* All time related values below in nanosecs */
+	int64_t frame_interval, max_frame_interval;
+	struct workqueue_struct *work_queue;
+	struct hrtimer threshold_timer;
+	struct mutex mutex;
+	struct vsg_buf_info *last_buffer;
+	int mode;
+	int state;
+};
+
+struct vsg_work {
+	struct vsg_context *context;
+	struct work_struct work;
+};
+
+struct vsg_encode_work {
+	struct vsg_buf_info *buf;
+	struct vsg_context *context;
+	struct work_struct work;
+};
+
+#define VSG_OPEN  _IO(VSG_MAGIC_IOCTL, 1)
+#define VSG_CLOSE  _IO(VSG_MAGIC_IOCTL, 2)
+#define VSG_START  _IO(VSG_MAGIC_IOCTL, 3)
+#define VSG_STOP  _IO(VSG_MAGIC_IOCTL, 4)
+#define VSG_Q_BUFFER  _IOW(VSG_MAGIC_IOCTL, 5, struct vsg_buf_info *)
+#define VSG_DQ_BUFFER  _IOR(VSG_MAGIC_IOCTL, 6, struct vsg_out_buf *)
+#define VSG_RETURN_IP_BUFFER _IOW(VSG_MAGIC_IOCTL, 7, struct vsg_buf_info *)
+#define VSG_ENCODE_DONE _IO(VSG_MAGIC_IOCTL, 8)
+/* Time related arguments for frame interval ioctls are always in nanosecs*/
+#define VSG_SET_FRAME_INTERVAL _IOW(VSG_MAGIC_IOCTL, 9, int64_t *)
+#define VSG_GET_FRAME_INTERVAL _IOR(VSG_MAGIC_IOCTL, 10, int64_t *)
+#define VSG_SET_MAX_FRAME_INTERVAL _IOW(VSG_MAGIC_IOCTL, 11, int64_t *)
+#define VSG_GET_MAX_FRAME_INTERVAL _IOR(VSG_MAGIC_IOCTL, 12, int64_t *)
+#define VSG_SET_MODE _IOW(VSG_MAGIC_IOCTL, 13, enum vsg_modes *)
+
+extern int vsg_init(struct v4l2_subdev *sd, u32 val);
+extern long vsg_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg);
+
+#endif /* _WFD_VSG_SUBDEV_ */
diff --git a/drivers/media/video/msm/wfd/wfd-ioctl.c b/drivers/media/video/msm/wfd/wfd-ioctl.c
new file mode 100644
index 0000000..c198815
--- /dev/null
+++ b/drivers/media/video/msm/wfd/wfd-ioctl.c
@@ -0,0 +1,1592 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/ioctl.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/platform_device.h>
+#include <linux/android_pmem.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/time.h>
+
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-msm-mem.h>
+#include "wfd-util.h"
+#include "mdp-subdev.h"
+#include "enc-subdev.h"
+#include "vsg-subdev.h"
+
+#define WFD_VERSION KERNEL_VERSION(0, 0, 1)
+#define WFD_NUM_DEVICES 2
+#define WFD_DEVICE_NUMBER_BASE 38
+#define WFD_DEVICE_SECURE (WFD_DEVICE_NUMBER_BASE + 1)
+#define DEFAULT_WFD_WIDTH 640
+#define DEFAULT_WFD_HEIGHT 480
+#define VENC_INPUT_BUFFERS 4
+
+struct wfd_device {
+	struct mutex dev_lock;
+	struct platform_device *pdev;
+	struct v4l2_device v4l2_dev;
+	struct video_device *pvdev;
+	struct v4l2_subdev mdp_sdev;
+	struct v4l2_subdev enc_sdev;
+	struct v4l2_subdev vsg_sdev;
+	struct ion_client *ion_client;
+	bool secure_device;
+	bool in_use;
+};
+
+struct mem_info {
+	u32 fd;
+	u32 offset;
+};
+
+struct mem_info_entry {
+	struct list_head list;
+	unsigned long userptr;
+	struct mem_info minfo;
+};
+
+struct mem_region_pair {
+	struct mem_region *enc;
+	struct mem_region *mdp;
+	struct list_head list;
+};
+
+struct wfd_inst {
+	struct vb2_queue vid_bufq;
+	spinlock_t inst_lock;
+	u32 buf_count;
+	struct task_struct *mdp_task;
+	void *mdp_inst;
+	void *venc_inst;
+	u32 height;
+	u32 width;
+	u32 pixelformat;
+	struct list_head minfo_list;
+	bool streamoff;
+	u32 input_bufs_allocated;
+	u32 input_buf_size;
+	u32 out_buf_size;
+	struct list_head input_mem_list;
+	struct wfd_stats stats;
+};
+
+struct wfd_vid_buffer {
+	struct vb2_buffer    vidbuf;
+};
+
+static int wfd_vidbuf_queue_setup(struct vb2_queue *q,
+				   const struct v4l2_format *fmt,
+				   unsigned int *num_buffers,
+				   unsigned int *num_planes,
+				   unsigned int sizes[], void *alloc_ctxs[])
+{
+	struct file *priv_data = (struct file *)(q->drv_priv);
+	struct wfd_inst *inst = (struct wfd_inst *)priv_data->private_data;
+	unsigned long flags;
+	int i;
+
+	WFD_MSG_DBG("In %s\n", __func__);
+	if (num_buffers == NULL || num_planes == NULL)
+		return -EINVAL;
+
+	*num_planes = 1;
+	spin_lock_irqsave(&inst->inst_lock, flags);
+	for (i = 0; i < *num_planes; ++i) {
+		sizes[i] = inst->out_buf_size;
+		alloc_ctxs[i] = inst;
+	}
+	spin_unlock_irqrestore(&inst->inst_lock, flags);
+
+	return 0;
+}
+
+void wfd_vidbuf_wait_prepare(struct vb2_queue *q)
+{
+}
+void wfd_vidbuf_wait_finish(struct vb2_queue *q)
+{
+}
+
+static unsigned long wfd_enc_addr_to_mdp_addr(struct wfd_inst *inst,
+		unsigned long addr)
+{
+	struct list_head *ptr, *next;
+	struct mem_region_pair *mpair;
+	if (!list_empty(&inst->input_mem_list)) {
+		list_for_each_safe(ptr, next,
+				&inst->input_mem_list) {
+			mpair = list_entry(ptr, struct mem_region_pair,
+					list);
+			if (mpair->enc->paddr == (u8 *)addr)
+				return (unsigned long)mpair->mdp->paddr;
+		}
+	}
+
+	return (unsigned long)NULL;
+}
+
+static int wfd_allocate_ion_buffer(struct ion_client *client,
+		bool secure, struct mem_region *mregion)
+{
+	struct ion_handle *handle;
+	void *kvaddr, *phys_addr;
+	unsigned long size;
+	unsigned int alloc_regions = 0;
+	int rc;
+
+	alloc_regions = ION_HEAP(ION_CP_MM_HEAP_ID);
+	alloc_regions |= secure ? ION_SECURE :
+				ION_HEAP(ION_IOMMU_HEAP_ID);
+	handle = ion_alloc(client,
+			mregion->size, SZ_4K, alloc_regions);
+
+	if (IS_ERR_OR_NULL(handle)) {
+		WFD_MSG_ERR("Failed to allocate input buffer\n");
+		rc = PTR_ERR(handle);
+		goto alloc_fail;
+	}
+
+	kvaddr = ion_map_kernel(client,	handle,	CACHED);
+
+	if (IS_ERR_OR_NULL(kvaddr)) {
+		WFD_MSG_ERR("Failed to get virtual addr\n");
+		rc = PTR_ERR(kvaddr);
+		goto alloc_fail;
+	}
+
+	rc = ion_map_iommu(client, handle,
+			VIDEO_DOMAIN, VIDEO_MAIN_POOL, SZ_4K,
+			0, (unsigned long *)&phys_addr,
+			&size, 0, 0);
+
+	if (rc) {
+		WFD_MSG_ERR("Failed to get physical addr\n");
+		goto alloc_fail;
+	} else if (size < mregion->size) {
+		WFD_MSG_ERR("Failed to map enough memory\n");
+		rc = -ENOMEM;
+		goto alloc_fail;
+	}
+
+	mregion->kvaddr = kvaddr;
+	mregion->paddr = phys_addr;
+	mregion->ion_handle = handle;
+
+	return rc;
+alloc_fail:
+	if (!IS_ERR_OR_NULL(handle)) {
+		ion_unmap_kernel(client, handle);
+		ion_free(client, handle);
+
+		mregion->kvaddr = NULL;
+		mregion->paddr = NULL;
+		mregion->ion_handle = NULL;
+	}
+	return rc;
+}
+
+/* Doesn't do iommu unmap */
+static int wfd_free_ion_buffer(struct ion_client *client,
+		struct mem_region *mregion)
+{
+	if (!client || !mregion) {
+		WFD_MSG_ERR("Failed to free ion buffer: "
+				"Invalid client or region");
+		return -EINVAL;
+	}
+	ion_unmap_kernel(client, mregion->ion_handle);
+	ion_free(client, mregion->ion_handle);
+	return 0;
+}
+
+static int wfd_flush_ion_buffer(struct ion_client *client,
+		struct mem_region *mregion)
+{
+	if (!client || !mregion) {
+		WFD_MSG_ERR("Failed to flush ion buffer: "
+				"Invalid client or region");
+		return -EINVAL;
+	} else if (!mregion->ion_handle) {
+		WFD_MSG_ERR("Failed to flush ion buffer: "
+				"not an ion buffer");
+		return -EINVAL;
+	}
+
+	return msm_ion_do_cache_op(client,
+			mregion->ion_handle,
+			mregion->kvaddr,
+			mregion->size,
+			ION_IOC_INV_CACHES);
+
+}
+int wfd_allocate_input_buffers(struct wfd_device *wfd_dev,
+			struct wfd_inst *inst)
+{
+	int i;
+	struct mem_region *enc_mregion, *mdp_mregion;
+	struct mem_region_pair *mpair;
+	int rc;
+	unsigned long flags;
+	struct mdp_buf_info mdp_buf = {0};
+	spin_lock_irqsave(&inst->inst_lock, flags);
+	if (inst->input_bufs_allocated) {
+		spin_unlock_irqrestore(&inst->inst_lock, flags);
+		return 0;
+	}
+	inst->input_bufs_allocated = true;
+	spin_unlock_irqrestore(&inst->inst_lock, flags);
+
+	for (i = 0; i < VENC_INPUT_BUFFERS; ++i) {
+		mpair = kzalloc(sizeof(*mpair), GFP_KERNEL);
+		enc_mregion = kzalloc(sizeof(*enc_mregion), GFP_KERNEL);
+		mdp_mregion = kzalloc(sizeof(*enc_mregion), GFP_KERNEL);
+		enc_mregion->size = ALIGN(inst->input_buf_size, SZ_4K);
+
+		rc = wfd_allocate_ion_buffer(wfd_dev->ion_client,
+				wfd_dev->secure_device, enc_mregion);
+		if (rc) {
+			WFD_MSG_ERR("Failed to allocate input memory."
+				" This error causes memory leak!!!\n");
+			goto alloc_fail;
+		}
+
+		WFD_MSG_DBG("NOTE: enc paddr = %p, kvaddr = %p\n",
+				enc_mregion->paddr,
+				enc_mregion->kvaddr);
+
+		rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl,
+				SET_INPUT_BUFFER, (void *)enc_mregion);
+
+		/* map the buffer from encoder to mdp */
+		mdp_mregion->kvaddr = enc_mregion->kvaddr;
+		mdp_mregion->size = enc_mregion->size;
+		mdp_mregion->offset = enc_mregion->offset;
+		mdp_mregion->fd = enc_mregion->fd;
+		mdp_mregion->cookie = 0;
+		mdp_mregion->ion_handle = enc_mregion->ion_handle;
+
+		rc = ion_map_iommu(wfd_dev->ion_client, mdp_mregion->ion_handle,
+				DISPLAY_DOMAIN, GEN_POOL, SZ_4K,
+				0, (unsigned long *)&mdp_mregion->paddr,
+				(unsigned long *)&mdp_mregion->size, 0, 0);
+		if (rc) {
+			WFD_MSG_ERR("Failed to map to mdp\n");
+			mdp_mregion->kvaddr = NULL;
+			mdp_mregion->paddr = NULL;
+			mdp_mregion->ion_handle = NULL;
+			goto alloc_fail;
+		}
+
+		mdp_buf.inst = inst->mdp_inst;
+		mdp_buf.cookie = enc_mregion;
+		mdp_buf.kvaddr = (u32) mdp_mregion->kvaddr;
+		mdp_buf.paddr = (u32) mdp_mregion->paddr;
+
+		WFD_MSG_DBG("NOTE: mdp paddr = %p, kvaddr = %p\n",
+				mdp_mregion->paddr,
+				mdp_mregion->kvaddr);
+
+		INIT_LIST_HEAD(&mpair->list);
+		mpair->enc = enc_mregion;
+		mpair->mdp = mdp_mregion;
+		list_add_tail(&mpair->list, &inst->input_mem_list);
+
+		rc = v4l2_subdev_call(&wfd_dev->mdp_sdev, core, ioctl,
+				MDP_Q_BUFFER, (void *)&mdp_buf);
+		if (rc) {
+			WFD_MSG_ERR("Unable to queue the"
+					" buffer to mdp\n");
+			break;
+		} else {
+			wfd_stats_update(&inst->stats,
+					WFD_STAT_EVENT_MDP_QUEUE);
+		}
+	}
+	rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl,
+			ALLOC_RECON_BUFFERS, NULL);
+	if (rc) {
+		WFD_MSG_ERR("Failed to allocate recon buffers\n");
+		goto alloc_fail;
+	}
+alloc_fail:
+	return rc;
+}
+void wfd_free_input_buffers(struct wfd_device *wfd_dev,
+			struct wfd_inst *inst)
+{
+	struct list_head *ptr, *next;
+	struct mem_region_pair *mpair;
+	unsigned long flags;
+	int rc = 0;
+	spin_lock_irqsave(&inst->inst_lock, flags);
+	if (!inst->input_bufs_allocated) {
+		spin_unlock_irqrestore(&inst->inst_lock, flags);
+		return;
+	}
+	inst->input_bufs_allocated = false;
+	spin_unlock_irqrestore(&inst->inst_lock, flags);
+	if (!list_empty(&inst->input_mem_list)) {
+		list_for_each_safe(ptr, next,
+				&inst->input_mem_list) {
+			mpair = list_entry(ptr, struct mem_region_pair,
+						list);
+			rc = v4l2_subdev_call(&wfd_dev->enc_sdev,
+					core, ioctl, FREE_INPUT_BUFFER,
+					(void *)mpair->enc);
+
+			if (rc)
+				WFD_MSG_ERR("Failed to free buffers "
+						"from encoder\n");
+
+			if (mpair->mdp->paddr)
+				ion_unmap_iommu(wfd_dev->ion_client,
+						mpair->mdp->ion_handle,
+						DISPLAY_DOMAIN, GEN_POOL);
+
+			if (mpair->enc->paddr)
+				ion_unmap_iommu(wfd_dev->ion_client,
+						mpair->enc->ion_handle,
+						VIDEO_DOMAIN, VIDEO_MAIN_POOL);
+
+			wfd_free_ion_buffer(wfd_dev->ion_client, mpair->enc);
+			list_del(&mpair->list);
+			kfree(mpair->enc);
+			kfree(mpair->mdp);
+			kfree(mpair);
+		}
+	}
+	rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl,
+			FREE_RECON_BUFFERS, NULL);
+	if (rc)
+		WFD_MSG_ERR("Failed to free recon buffers\n");
+}
+
+struct mem_info *wfd_get_mem_info(struct wfd_inst *inst,
+			unsigned long userptr)
+{
+	struct mem_info_entry *temp;
+	struct mem_info *ret = NULL;
+	unsigned long flags;
+	spin_lock_irqsave(&inst->inst_lock, flags);
+	if (!list_empty(&inst->minfo_list)) {
+		list_for_each_entry(temp, &inst->minfo_list, list) {
+			if (temp && temp->userptr == userptr) {
+				ret = &temp->minfo;
+				break;
+			}
+		}
+	}
+	spin_unlock_irqrestore(&inst->inst_lock, flags);
+	return ret;
+}
+void wfd_put_mem_info(struct wfd_inst *inst,
+			struct mem_info *minfo)
+{
+	struct list_head *ptr, *next;
+	struct mem_info_entry *temp;
+	unsigned long flags;
+	spin_lock_irqsave(&inst->inst_lock, flags);
+	if (!list_empty(&inst->minfo_list)) {
+		list_for_each_safe(ptr, next,
+				&inst->minfo_list) {
+			temp = list_entry(ptr, struct mem_info_entry,
+						list);
+			if (temp && (&temp->minfo == minfo)) {
+				list_del(&temp->list);
+				kfree(temp);
+			}
+		}
+	}
+	spin_unlock_irqrestore(&inst->inst_lock, flags);
+}
+static void wfd_unregister_out_buf(struct wfd_inst *inst,
+		struct mem_info *minfo)
+{
+	if (!minfo || !inst) {
+		WFD_MSG_ERR("Invalid arguments\n");
+		return;
+	}
+	wfd_put_mem_info(inst, minfo);
+}
+int wfd_vidbuf_buf_init(struct vb2_buffer *vb)
+{
+	int rc = 0;
+	struct vb2_queue *q = vb->vb2_queue;
+	struct file *priv_data = (struct file *)(q->drv_priv);
+	struct wfd_inst *inst = (struct wfd_inst *)priv_data->private_data;
+	struct wfd_device *wfd_dev =
+		(struct wfd_device *)video_drvdata(priv_data);
+	struct mem_info *minfo = vb2_plane_cookie(vb, 0);
+	struct mem_region mregion;
+	mregion.fd = minfo->fd;
+	mregion.offset = minfo->offset;
+	mregion.cookie = (u32)vb;
+	/*TODO: should be fixed in kernel 3.2*/
+	mregion.size =  inst->out_buf_size;
+
+	if (inst && !inst->vid_bufq.streaming) {
+		rc = wfd_allocate_input_buffers(wfd_dev, inst);
+		if (rc) {
+			WFD_MSG_ERR("Failed to allocate input buffers\n");
+			goto free_input_bufs;
+		}
+		rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl,
+				SET_OUTPUT_BUFFER, (void *)&mregion);
+		if (rc) {
+			WFD_MSG_ERR("Failed to set output buffer\n");
+			goto free_input_bufs;
+		}
+	}
+	return rc;
+free_input_bufs:
+	wfd_free_input_buffers(wfd_dev, inst);
+	return rc;
+}
+
+int wfd_vidbuf_buf_prepare(struct vb2_buffer *vb)
+{
+	return 0;
+}
+
+int wfd_vidbuf_buf_finish(struct vb2_buffer *vb)
+{
+	return 0;
+}
+
+void wfd_vidbuf_buf_cleanup(struct vb2_buffer *vb)
+{
+	int rc = 0;
+	struct vb2_queue *q = vb->vb2_queue;
+	struct file *priv_data = (struct file *)(q->drv_priv);
+	struct wfd_device *wfd_dev =
+		(struct wfd_device *)video_drvdata(priv_data);
+	struct wfd_inst *inst = (struct wfd_inst *)priv_data->private_data;
+	struct mem_info *minfo = vb2_plane_cookie(vb, 0);
+	struct mem_region mregion;
+
+	if (minfo == NULL) {
+		WFD_MSG_ERR("not freeing buffers since allocation failed");
+		return;
+	}
+
+	mregion.fd = minfo->fd;
+	mregion.offset = minfo->offset;
+	mregion.cookie = (u32)vb;
+	mregion.size =  inst->out_buf_size;
+
+	rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl,
+			FREE_OUTPUT_BUFFER, (void *)&mregion);
+	if (rc)
+		WFD_MSG_ERR("Failed to free output buffer\n");
+	wfd_unregister_out_buf(inst, minfo);
+}
+
+static int mdp_output_thread(void *data)
+{
+	int rc = 0;
+	struct file *filp = (struct file *)data;
+	struct wfd_inst *inst = filp->private_data;
+	struct wfd_device *wfd_dev =
+		(struct wfd_device *)video_drvdata(filp);
+	struct mdp_buf_info obuf_mdp = {inst->mdp_inst, 0, 0, 0};
+	struct mem_region *mregion;
+	struct vsg_buf_info ibuf_vsg;
+	while (!kthread_should_stop()) {
+		WFD_MSG_DBG("waiting for mdp output\n");
+		rc = v4l2_subdev_call(&wfd_dev->mdp_sdev,
+			core, ioctl, MDP_DQ_BUFFER, (void *)&obuf_mdp);
+
+		if (rc) {
+			if (rc != -ENOBUFS)
+				WFD_MSG_ERR("MDP reported err %d\n", rc);
+
+			WFD_MSG_ERR("Streamoff called\n");
+			break;
+		} else {
+			wfd_stats_update(&inst->stats,
+				WFD_STAT_EVENT_MDP_DEQUEUE);
+		}
+
+		mregion = obuf_mdp.cookie;
+		if (!mregion) {
+			WFD_MSG_ERR("mdp cookie is null\n");
+			rc = -EINVAL;
+			break;
+		}
+
+		ibuf_vsg.mdp_buf_info = obuf_mdp;
+		ibuf_vsg.mdp_buf_info.inst = inst->mdp_inst;
+		ibuf_vsg.mdp_buf_info.cookie = mregion;
+		ibuf_vsg.mdp_buf_info.kvaddr = (u32) mregion->kvaddr;
+		ibuf_vsg.mdp_buf_info.paddr =
+			(u32)wfd_enc_addr_to_mdp_addr(inst,
+					(unsigned long)mregion->paddr);
+		rc = v4l2_subdev_call(&wfd_dev->vsg_sdev,
+			core, ioctl, VSG_Q_BUFFER, (void *)&ibuf_vsg);
+
+		if (rc) {
+			WFD_MSG_ERR("Failed to queue frame to vsg\n");
+			break;
+		} else {
+			wfd_stats_update(&inst->stats,
+				WFD_STAT_EVENT_VSG_QUEUE);
+		}
+	}
+	WFD_MSG_DBG("Exiting the thread\n");
+	return rc;
+}
+
+int wfd_vidbuf_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+	struct file *priv_data = (struct file *)(q->drv_priv);
+	struct wfd_device *wfd_dev =
+		(struct wfd_device *)video_drvdata(priv_data);
+	struct wfd_inst *inst = (struct wfd_inst *)priv_data->private_data;
+	int rc = 0;
+
+	WFD_MSG_ERR("Stream on called\n");
+	WFD_MSG_DBG("enc start\n");
+	rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl,
+			ENCODE_START, (void *)inst->venc_inst);
+	if (rc) {
+		WFD_MSG_ERR("Failed to start encoder\n");
+		goto subdev_start_fail;
+	}
+
+	WFD_MSG_DBG("vsg start\n");
+	rc = v4l2_subdev_call(&wfd_dev->vsg_sdev, core, ioctl,
+			VSG_START, NULL);
+	if (rc) {
+		WFD_MSG_ERR("Failed to start vsg\n");
+		goto subdev_start_fail;
+	}
+
+	inst->mdp_task = kthread_run(mdp_output_thread, priv_data,
+				"mdp_output_thread");
+	if (IS_ERR(inst->mdp_task)) {
+		rc = PTR_ERR(inst->mdp_task);
+		goto subdev_start_fail;
+	}
+	WFD_MSG_DBG("mdp start\n");
+	rc = v4l2_subdev_call(&wfd_dev->mdp_sdev, core, ioctl,
+			 MDP_START, (void *)inst->mdp_inst);
+	if (rc)
+		WFD_MSG_ERR("Failed to start MDP\n");
+subdev_start_fail:
+	return rc;
+}
+
+int wfd_vidbuf_stop_streaming(struct vb2_queue *q)
+{
+	struct file *priv_data = (struct file *)(q->drv_priv);
+	struct wfd_device *wfd_dev =
+		(struct wfd_device *)video_drvdata(priv_data);
+	struct wfd_inst *inst = (struct wfd_inst *)priv_data->private_data;
+	int rc = 0;
+	WFD_MSG_DBG("mdp stop\n");
+	rc = v4l2_subdev_call(&wfd_dev->mdp_sdev, core, ioctl,
+			 MDP_STOP, (void *)inst->mdp_inst);
+	if (rc)
+		WFD_MSG_ERR("Failed to stop MDP\n");
+
+	WFD_MSG_DBG("vsg stop\n");
+	rc = v4l2_subdev_call(&wfd_dev->vsg_sdev, core, ioctl,
+			 VSG_STOP, NULL);
+	if (rc)
+		WFD_MSG_ERR("Failed to stop VSG\n");
+
+	kthread_stop(inst->mdp_task);
+	rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl,
+			ENCODE_FLUSH, (void *)inst->venc_inst);
+	if (rc)
+		WFD_MSG_ERR("Failed to flush encoder\n");
+	WFD_MSG_DBG("enc stop\n");
+	rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl,
+			ENCODE_STOP, (void *)inst->venc_inst);
+	if (rc)
+		WFD_MSG_ERR("Failed to stop encoder\n");
+
+	return rc;
+}
+
+void wfd_vidbuf_buf_queue(struct vb2_buffer *vb)
+{
+	int rc = 0;
+	struct vb2_queue *q = vb->vb2_queue;
+	struct file *priv_data = (struct file *)(q->drv_priv);
+	struct wfd_device *wfd_dev =
+		(struct wfd_device *)video_drvdata(priv_data);
+	struct wfd_inst *inst = (struct wfd_inst *)priv_data->private_data;
+	struct mem_region mregion;
+	struct mem_info *minfo = vb2_plane_cookie(vb, 0);
+	mregion.fd = minfo->fd;
+	mregion.offset = minfo->offset;
+	mregion.cookie = (u32)vb;
+	mregion.size =  inst->out_buf_size;
+	rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl,
+			FILL_OUTPUT_BUFFER, (void *)&mregion);
+	if (rc) {
+		WFD_MSG_ERR("Failed to fill output buffer\n");
+	}
+}
+
+static struct vb2_ops wfd_vidbuf_ops = {
+	.queue_setup = wfd_vidbuf_queue_setup,
+
+	.wait_prepare = wfd_vidbuf_wait_prepare,
+	.wait_finish = wfd_vidbuf_wait_finish,
+
+	.buf_init = wfd_vidbuf_buf_init,
+	.buf_prepare = wfd_vidbuf_buf_prepare,
+	.buf_finish = wfd_vidbuf_buf_finish,
+	.buf_cleanup = wfd_vidbuf_buf_cleanup,
+
+	.start_streaming = wfd_vidbuf_start_streaming,
+	.stop_streaming = wfd_vidbuf_stop_streaming,
+
+	.buf_queue = wfd_vidbuf_buf_queue,
+};
+
+static const struct v4l2_subdev_core_ops mdp_subdev_core_ops = {
+	.init = mdp_init,
+	.ioctl = mdp_ioctl,
+};
+
+static const struct v4l2_subdev_ops mdp_subdev_ops = {
+	.core = &mdp_subdev_core_ops,
+};
+
+static const struct v4l2_subdev_core_ops enc_subdev_core_ops = {
+	.init = venc_init,
+	.load_fw = venc_load_fw,
+	.ioctl = venc_ioctl,
+};
+
+static const struct v4l2_subdev_ops enc_subdev_ops = {
+	.core = &enc_subdev_core_ops,
+};
+
+static const struct v4l2_subdev_core_ops vsg_subdev_core_ops = {
+	.init = vsg_init,
+	.ioctl = vsg_ioctl,
+};
+
+static const struct v4l2_subdev_ops vsg_subdev_ops = {
+	.core = &vsg_subdev_core_ops,
+};
+
+static int wfdioc_querycap(struct file *filp, void *fh,
+		struct v4l2_capability *cap) {
+	WFD_MSG_DBG("wfdioc_querycap: E\n");
+	memset(cap, 0, sizeof(struct v4l2_capability));
+	strlcpy(cap->driver, "wifi-display", sizeof(cap->driver));
+	strlcpy(cap->card, "msm", sizeof(cap->card));
+	cap->version = WFD_VERSION;
+	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+	WFD_MSG_DBG("wfdioc_querycap: X\n");
+	return 0;
+}
+static int wfdioc_g_fmt(struct file *filp, void *fh,
+			struct v4l2_format *fmt)
+{
+	struct wfd_inst *inst = filp->private_data;
+	unsigned long flags;
+	if (!fmt) {
+		WFD_MSG_ERR("Invalid argument\n");
+		return -EINVAL;
+	}
+	if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+		WFD_MSG_ERR("Only V4L2_BUF_TYPE_VIDEO_CAPTURE is supported\n");
+		return -EINVAL;
+	}
+	spin_lock_irqsave(&inst->inst_lock, flags);
+	fmt->fmt.pix.width = inst->width;
+	fmt->fmt.pix.height = inst->height;
+	fmt->fmt.pix.pixelformat = inst->pixelformat;
+	fmt->fmt.pix.sizeimage = inst->out_buf_size;
+	fmt->fmt.pix.priv = 0;
+	spin_unlock_irqrestore(&inst->inst_lock, flags);
+	return 0;
+}
+
+static int wfdioc_s_fmt(struct file *filp, void *fh,
+			struct v4l2_format *fmt)
+{
+	int rc = 0;
+	struct wfd_inst *inst = filp->private_data;
+	struct wfd_device *wfd_dev = video_drvdata(filp);
+	struct mdp_prop prop;
+	unsigned long flags;
+	struct bufreq breq;
+	if (!fmt) {
+		WFD_MSG_ERR("Invalid argument\n");
+		return -EINVAL;
+	}
+	if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+		fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_H264) {
+		WFD_MSG_ERR("Only V4L2_BUF_TYPE_VIDEO_CAPTURE and "
+				"V4L2_PIX_FMT_H264 are supported\n");
+		return -EINVAL;
+	}
+
+	if (fmt->fmt.pix.width % 16) {
+		WFD_MSG_ERR("Only 16 byte aligned widths are supported\n");
+		return -ENOTSUPP;
+	}
+	rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl, SET_FORMAT,
+				(void *)fmt);
+	if (rc) {
+		WFD_MSG_ERR("Failed to set format on encoder, rc = %d\n", rc);
+		return rc;
+	}
+	breq.count = VENC_INPUT_BUFFERS;
+	breq.height = fmt->fmt.pix.height;
+	breq.width = fmt->fmt.pix.width;
+	rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl,
+			SET_BUFFER_REQ, (void *)&breq);
+	if (rc) {
+		WFD_MSG_ERR("Failed to set buffer reqs on encoder\n");
+		return rc;
+	}
+	spin_lock_irqsave(&inst->inst_lock, flags);
+	inst->input_buf_size = breq.size;
+	inst->out_buf_size = fmt->fmt.pix.sizeimage;
+	prop.height = inst->height = fmt->fmt.pix.height;
+	prop.width = inst->width = fmt->fmt.pix.width;
+	prop.inst = inst->mdp_inst;
+	spin_unlock_irqrestore(&inst->inst_lock, flags);
+	rc = v4l2_subdev_call(&wfd_dev->mdp_sdev, core, ioctl, MDP_SET_PROP,
+				(void *)&prop);
+	if (rc)
+		WFD_MSG_ERR("Failed to set height/width property on mdp\n");
+	return rc;
+}
+static int wfdioc_reqbufs(struct file *filp, void *fh,
+		struct v4l2_requestbuffers *b)
+{
+	struct wfd_inst *inst = filp->private_data;
+	struct wfd_device *wfd_dev = video_drvdata(filp);
+	unsigned long flags;
+	int rc = 0;
+
+	if (b->type != V4L2_CAP_VIDEO_CAPTURE ||
+		b->memory != V4L2_MEMORY_USERPTR) {
+		WFD_MSG_ERR("Only V4L2_CAP_VIDEO_CAPTURE and "
+		"V4L2_MEMORY_USERPTR are supported\n");
+		return -EINVAL;
+	}
+	rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl,
+			GET_BUFFER_REQ, (void *)b);
+	if (rc) {
+		WFD_MSG_ERR("Failed to get buf reqs from encoder\n");
+		return rc;
+	}
+	spin_lock_irqsave(&inst->inst_lock, flags);
+	inst->buf_count = b->count;
+	spin_unlock_irqrestore(&inst->inst_lock, flags);
+	rc = vb2_reqbufs(&inst->vid_bufq, b);
+	return rc;
+}
+static int wfd_register_out_buf(struct wfd_inst *inst,
+		struct v4l2_buffer *b)
+{
+	struct mem_info_entry *minfo_entry;
+	struct mem_info *minfo;
+	unsigned long flags;
+	if (!b || !inst || !b->reserved) {
+		WFD_MSG_ERR("Invalid arguments\n");
+		return -EINVAL;
+	}
+	minfo = wfd_get_mem_info(inst, b->m.userptr);
+	if (!minfo) {
+		minfo_entry = kzalloc(sizeof(struct mem_info_entry),
+				GFP_KERNEL);
+		if (copy_from_user(&minfo_entry->minfo, (void *)b->reserved,
+					sizeof(struct mem_info))) {
+			WFD_MSG_ERR(" copy_from_user failed. Populate"
+					" v4l2_buffer->reserved with meminfo\n");
+			return -EINVAL;
+		}
+		minfo_entry->userptr = b->m.userptr;
+		spin_lock_irqsave(&inst->inst_lock, flags);
+		list_add_tail(&minfo_entry->list, &inst->minfo_list);
+		spin_unlock_irqrestore(&inst->inst_lock, flags);
+	} else
+		WFD_MSG_INFO("Buffer already registered\n");
+
+	return 0;
+}
+static int wfdioc_qbuf(struct file *filp, void *fh,
+		struct v4l2_buffer *b)
+{
+	int rc = 0;
+	struct wfd_inst *inst = filp->private_data;
+	if (!inst || !b ||
+			(b->index < 0 || b->index >= inst->buf_count)) {
+		WFD_MSG_ERR("Invalid input parameters to QBUF IOCTL\n");
+		return -EINVAL;
+	}
+	rc = wfd_register_out_buf(inst, b);
+	if (rc) {
+		WFD_MSG_ERR("Failed to register buffer\n");
+		return rc;
+	}
+
+	rc = vb2_qbuf(&inst->vid_bufq, b);
+	if (rc)
+		WFD_MSG_ERR("Failed to queue buffer\n");
+	else
+		wfd_stats_update(&inst->stats, WFD_STAT_EVENT_CLIENT_QUEUE);
+
+	return rc;
+}
+
+static int wfdioc_streamon(struct file *filp, void *fh,
+		enum v4l2_buf_type i)
+{
+	int rc = 0;
+	struct wfd_inst *inst = filp->private_data;
+	unsigned long flags;
+	if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+		WFD_MSG_ERR("stream on for buffer type = %d is not "
+			"supported.\n", i);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&inst->inst_lock, flags);
+	inst->streamoff = false;
+	spin_unlock_irqrestore(&inst->inst_lock, flags);
+
+	rc = vb2_streamon(&inst->vid_bufq, i);
+	if (rc) {
+		WFD_MSG_ERR("videobuf_streamon failed with err = %d\n", rc);
+		goto vidbuf_streamon_failed;
+	}
+	return rc;
+
+vidbuf_streamon_failed:
+	vb2_streamoff(&inst->vid_bufq, i);
+	return rc;
+}
+static int wfdioc_streamoff(struct file *filp, void *fh,
+		enum v4l2_buf_type i)
+{
+	struct wfd_inst *inst = filp->private_data;
+	unsigned long flags;
+
+	if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+		WFD_MSG_ERR("stream off for buffer type = %d is not "
+			"supported.\n", i);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&inst->inst_lock, flags);
+	if (inst->streamoff) {
+		WFD_MSG_ERR("Module is already in streamoff state\n");
+		spin_unlock_irqrestore(&inst->inst_lock, flags);
+		return -EINVAL;
+	}
+	inst->streamoff = true;
+	spin_unlock_irqrestore(&inst->inst_lock, flags);
+	WFD_MSG_DBG("Calling videobuf_streamoff\n");
+	vb2_streamoff(&inst->vid_bufq, i);
+	return 0;
+}
+static int wfdioc_dqbuf(struct file *filp, void *fh,
+		struct v4l2_buffer *b)
+{
+	struct wfd_inst *inst = filp->private_data;
+	int rc;
+
+	WFD_MSG_INFO("Waiting to dequeue buffer\n");
+	rc = vb2_dqbuf(&inst->vid_bufq, b, 0);
+
+	if (rc)
+		WFD_MSG_ERR("Failed to dequeue buffer\n");
+	else
+		wfd_stats_update(&inst->stats, WFD_STAT_EVENT_CLIENT_DEQUEUE);
+
+	return rc;
+}
+static int wfdioc_g_ctrl(struct file *filp, void *fh,
+					struct v4l2_control *a)
+{
+	int rc = 0;
+	struct wfd_device *wfd_dev = video_drvdata(filp);
+	rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core,
+			ioctl, GET_PROP, a);
+	if (rc)
+		WFD_MSG_ERR("Failed to get encoder property\n");
+	return rc;
+}
+static int wfdioc_s_ctrl(struct file *filp, void *fh,
+					struct v4l2_control *a)
+{
+	int rc = 0;
+	struct wfd_device *wfd_dev = video_drvdata(filp);
+	rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core,
+			ioctl, SET_PROP, a);
+	if (rc)
+		WFD_MSG_ERR("Failed to set encoder property\n");
+	return rc;
+}
+
+static int wfdioc_g_parm(struct file *filp, void *fh,
+		struct v4l2_streamparm *a)
+{
+	int rc = 0;
+	struct wfd_device *wfd_dev = video_drvdata(filp);
+	struct wfd_inst *inst = filp->private_data;
+	int64_t frame_interval = 0,
+		max_frame_interval = 0; /* both in nsecs*/
+	struct v4l2_qcom_frameskip frameskip, *usr_frameskip;
+
+	usr_frameskip = (struct v4l2_qcom_frameskip *)
+			a->parm.capture.extendedmode;
+
+	if (!usr_frameskip) {
+		rc = -EINVAL;
+		goto get_parm_fail;
+	}
+	rc = v4l2_subdev_call(&wfd_dev->vsg_sdev, core,
+			ioctl, VSG_GET_FRAME_INTERVAL, &frame_interval);
+
+	if (rc < 0)
+		goto get_parm_fail;
+
+	rc = v4l2_subdev_call(&wfd_dev->vsg_sdev, core,
+			ioctl, VSG_GET_MAX_FRAME_INTERVAL, &max_frame_interval);
+
+	if (rc < 0)
+		goto get_parm_fail;
+
+	frameskip = (struct v4l2_qcom_frameskip) {
+		.maxframeinterval = max_frame_interval,
+	};
+
+	a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	a->parm.capture = (struct v4l2_captureparm) {
+		.capability = V4L2_CAP_TIMEPERFRAME,
+		.capturemode = 0,
+		.timeperframe = (struct v4l2_fract) {
+			.numerator = frame_interval,
+			.denominator = NSEC_PER_SEC,
+		},
+		.readbuffers = inst->buf_count,
+		.extendedmode = (__u32)usr_frameskip,
+		.reserved = {0}
+	};
+
+	rc = copy_to_user((void *)a->parm.capture.extendedmode,
+			&frameskip, sizeof(frameskip));
+	if (rc < 0)
+		goto get_parm_fail;
+
+get_parm_fail:
+	return rc;
+}
+
+static int wfdioc_s_parm(struct file *filp, void *fh,
+		struct v4l2_streamparm *a)
+{
+	int rc = 0;
+	struct wfd_device *wfd_dev = video_drvdata(filp);
+	struct wfd_inst *inst = filp->private_data;
+	struct v4l2_qcom_frameskip frameskip;
+	int64_t frame_interval, max_frame_interval;
+	void *extendedmode = NULL;
+	enum vsg_modes mode = VSG_MODE_VFR;
+
+
+	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+		rc = -ENOTSUPP;
+		goto set_parm_fail;
+	}
+
+	if (a->parm.capture.readbuffers == 0 ||
+		a->parm.capture.readbuffers == inst->buf_count) {
+		a->parm.capture.readbuffers = inst->buf_count;
+	} else {
+		rc = -EINVAL;
+		goto set_parm_fail;
+	}
+
+	extendedmode = (void *)a->parm.capture.extendedmode;
+	if (a->parm.capture.capability & V4L2_CAP_TIMEPERFRAME) {
+		if (a->parm.capture.timeperframe.denominator == 0) {
+			rc = -EINVAL;
+			goto set_parm_fail;
+		}
+		frame_interval =
+			a->parm.capture.timeperframe.numerator * NSEC_PER_SEC /
+			a->parm.capture.timeperframe.denominator;
+
+		rc = v4l2_subdev_call(&wfd_dev->vsg_sdev, core,
+				ioctl, VSG_SET_FRAME_INTERVAL,
+				&frame_interval);
+
+		if (rc)
+			goto set_parm_fail;
+
+		rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core,
+				ioctl, SET_FRAMERATE,
+				&a->parm.capture.timeperframe);
+
+		if (rc)
+			goto set_parm_fail;
+	}
+
+	if (a->parm.capture.capability & V4L2_CAP_QCOM_FRAMESKIP &&
+		extendedmode) {
+		rc = copy_from_user(&frameskip,
+				extendedmode, sizeof(frameskip));
+
+		if (rc)
+			goto set_parm_fail;
+
+		max_frame_interval = (int64_t)frameskip.maxframeinterval;
+		mode = VSG_MODE_VFR;
+
+		rc = v4l2_subdev_call(&wfd_dev->vsg_sdev, core,
+				ioctl, VSG_SET_MAX_FRAME_INTERVAL,
+				&max_frame_interval);
+
+		if (rc)
+			goto set_parm_fail;
+
+		rc = v4l2_subdev_call(&wfd_dev->vsg_sdev, core,
+				ioctl, VSG_SET_MODE, &mode);
+
+		if (rc)
+			goto set_parm_fail;
+	} else {
+		mode = VSG_MODE_CFR;
+		rc = v4l2_subdev_call(&wfd_dev->vsg_sdev, core,
+				ioctl, VSG_SET_MODE, &mode);
+
+		if (rc)
+			goto set_parm_fail;
+	}
+
+set_parm_fail:
+	return rc;
+}
+
+static const struct v4l2_ioctl_ops g_wfd_ioctl_ops = {
+	.vidioc_querycap = wfdioc_querycap,
+	.vidioc_s_fmt_vid_cap = wfdioc_s_fmt,
+	.vidioc_g_fmt_vid_cap = wfdioc_g_fmt,
+	.vidioc_reqbufs = wfdioc_reqbufs,
+	.vidioc_qbuf = wfdioc_qbuf,
+	.vidioc_streamon = wfdioc_streamon,
+	.vidioc_streamoff = wfdioc_streamoff,
+	.vidioc_dqbuf = wfdioc_dqbuf,
+	.vidioc_g_ctrl = wfdioc_g_ctrl,
+	.vidioc_s_ctrl = wfdioc_s_ctrl,
+	.vidioc_g_parm = wfdioc_g_parm,
+	.vidioc_s_parm = wfdioc_s_parm,
+};
+static int wfd_set_default_properties(struct file *filp)
+{
+	unsigned long flags;
+	struct v4l2_format fmt;
+	struct v4l2_control ctrl;
+	struct wfd_inst *inst = filp->private_data;
+	if (!inst) {
+		WFD_MSG_ERR("Invalid argument\n");
+		return -EINVAL;
+	}
+	spin_lock_irqsave(&inst->inst_lock, flags);
+	fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	fmt.fmt.pix.height = inst->height = DEFAULT_WFD_HEIGHT;
+	fmt.fmt.pix.width = inst->width = DEFAULT_WFD_WIDTH;
+	fmt.fmt.pix.pixelformat = inst->pixelformat
+			= V4L2_PIX_FMT_H264;
+	spin_unlock_irqrestore(&inst->inst_lock, flags);
+	wfdioc_s_fmt(filp, filp->private_data, &fmt);
+
+	ctrl.id = V4L2_CID_MPEG_VIDEO_HEADER_MODE;
+	ctrl.value = V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_I_FRAME;
+	wfdioc_s_ctrl(filp, filp->private_data, &ctrl);
+	return 0;
+}
+static void venc_op_buffer_done(void *cookie, u32 status,
+			struct vb2_buffer *buf)
+{
+	WFD_MSG_DBG("yay!! got callback\n");
+	vb2_buffer_done(buf, VB2_BUF_STATE_DONE);
+}
+
+static void venc_ip_buffer_done(void *cookie, u32 status,
+			struct mem_region *mregion)
+{
+	struct file *filp = cookie;
+	struct wfd_inst *inst = filp->private_data;
+	struct vsg_buf_info buf;
+	struct mdp_buf_info mdp_buf = {0};
+	struct wfd_device *wfd_dev =
+		(struct wfd_device *)video_drvdata(filp);
+	int rc = 0;
+	WFD_MSG_DBG("yay!! got ip callback\n");
+	mdp_buf.inst = inst->mdp_inst;
+	mdp_buf.cookie = mregion;
+	mdp_buf.kvaddr = (u32) mregion->kvaddr;
+	mdp_buf.paddr =
+		(u32)wfd_enc_addr_to_mdp_addr(inst,
+			(unsigned long)mregion->paddr);
+	buf.mdp_buf_info = mdp_buf;
+
+	rc = v4l2_subdev_call(&wfd_dev->vsg_sdev, core,
+			ioctl, VSG_RETURN_IP_BUFFER, (void *)&buf);
+	if (rc)
+		WFD_MSG_ERR("Failed to return buffer to vsg\n");
+	else
+		wfd_stats_update(&inst->stats, WFD_STAT_EVENT_ENC_DEQUEUE);
+
+}
+
+static int vsg_release_input_frame(void *cookie, struct vsg_buf_info *buf)
+{
+	struct file *filp = cookie;
+	struct wfd_inst *inst = filp->private_data;
+	struct wfd_device *wfd_dev =
+		(struct wfd_device *)video_drvdata(filp);
+	int rc = 0;
+
+	rc = v4l2_subdev_call(&wfd_dev->mdp_sdev, core,
+			ioctl, MDP_Q_BUFFER, buf);
+	if (rc)
+		WFD_MSG_ERR("Failed to Q buffer to mdp\n");
+	else {
+		wfd_stats_update(&inst->stats, WFD_STAT_EVENT_MDP_QUEUE);
+		wfd_stats_update(&inst->stats, WFD_STAT_EVENT_VSG_DEQUEUE);
+	}
+
+	return rc;
+}
+
+static int vsg_encode_frame(void *cookie, struct vsg_buf_info *buf)
+{
+	struct file *filp = cookie;
+	struct wfd_inst *inst = filp->private_data;
+	struct wfd_device *wfd_dev =
+		(struct wfd_device *)video_drvdata(filp);
+	struct venc_buf_info venc_buf;
+	int rc = 0;
+
+	if (!buf)
+		return -EINVAL;
+
+	venc_buf = (struct venc_buf_info){
+		.timestamp = timespec_to_ns(&buf->time),
+		.mregion = buf->mdp_buf_info.cookie
+	};
+
+	wfd_flush_ion_buffer(wfd_dev->ion_client, venc_buf.mregion);
+
+	rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl,
+			ENCODE_FRAME, &venc_buf);
+
+	if (rc)
+		WFD_MSG_ERR("Encode failed\n");
+	else
+		wfd_stats_update(&inst->stats, WFD_STAT_EVENT_ENC_QUEUE);
+
+	return rc;
+}
+
+void *wfd_vb2_mem_ops_get_userptr(void *alloc_ctx, unsigned long vaddr,
+					unsigned long size, int write)
+{
+	return wfd_get_mem_info(alloc_ctx, vaddr);
+}
+
+void wfd_vb2_mem_ops_put_userptr(void *buf_priv)
+{
+	/*TODO: Free the list*/
+}
+
+void *wfd_vb2_mem_ops_cookie(void *buf_priv)
+{
+	return buf_priv;
+}
+
+
+static struct vb2_mem_ops wfd_vb2_mem_ops = {
+	.get_userptr = wfd_vb2_mem_ops_get_userptr,
+	.put_userptr = wfd_vb2_mem_ops_put_userptr,
+	.cookie = wfd_vb2_mem_ops_cookie,
+};
+
+int wfd_initialize_vb2_queue(struct vb2_queue *q, void *priv)
+{
+	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	q->io_modes = VB2_USERPTR;
+	q->ops = &wfd_vidbuf_ops;
+	q->mem_ops = &wfd_vb2_mem_ops;
+	q->drv_priv = priv;
+	return vb2_queue_init(q);
+}
+
+static int wfd_open(struct file *filp)
+{
+	int rc = 0;
+	struct wfd_inst *inst = NULL;
+	struct wfd_device *wfd_dev = NULL;
+	struct venc_msg_ops enc_mops;
+	struct vsg_msg_ops vsg_mops;
+
+	WFD_MSG_DBG("wfd_open: E\n");
+	wfd_dev = video_drvdata(filp);
+
+	mutex_lock(&wfd_dev->dev_lock);
+	if (wfd_dev->in_use) {
+		WFD_MSG_ERR("Device already in use.\n");
+		rc = -EBUSY;
+		mutex_unlock(&wfd_dev->dev_lock);
+		goto err_dev_busy;
+	}
+
+	wfd_dev->in_use = true;
+	mutex_unlock(&wfd_dev->dev_lock);
+
+	inst = kzalloc(sizeof(struct wfd_inst), GFP_KERNEL);
+	if (!inst || !wfd_dev) {
+		WFD_MSG_ERR("Could not allocate memory for "
+			"wfd instance\n");
+		rc = -ENOMEM;
+		goto err_mdp_open;
+	}
+	filp->private_data = inst;
+	spin_lock_init(&inst->inst_lock);
+	INIT_LIST_HEAD(&inst->input_mem_list);
+	INIT_LIST_HEAD(&inst->minfo_list);
+
+	wfd_stats_init(&inst->stats, MINOR(filp->f_dentry->d_inode->i_rdev));
+
+	rc = v4l2_subdev_call(&wfd_dev->mdp_sdev, core, ioctl, MDP_OPEN,
+				(void *)&inst->mdp_inst);
+	if (rc) {
+		WFD_MSG_ERR("Failed to open mdp subdevice: %d\n", rc);
+		goto err_mdp_open;
+	}
+
+	rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, load_fw);
+	if (rc) {
+		WFD_MSG_ERR("Failed to load video encoder firmware: %d\n", rc);
+		goto err_venc;
+	}
+	enc_mops.op_buffer_done = venc_op_buffer_done;
+	enc_mops.ip_buffer_done = venc_ip_buffer_done;
+	enc_mops.cbdata = filp;
+	enc_mops.secure = wfd_dev->secure_device;
+	rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl, OPEN,
+				(void *)&enc_mops);
+	if (rc || !enc_mops.cookie) {
+		WFD_MSG_ERR("Failed to open encoder subdevice: %d\n", rc);
+		goto err_venc;
+	}
+	inst->venc_inst = enc_mops.cookie;
+
+	vsg_mops.encode_frame = vsg_encode_frame;
+	vsg_mops.release_input_frame = vsg_release_input_frame;
+	vsg_mops.cbdata = filp;
+	rc = v4l2_subdev_call(&wfd_dev->vsg_sdev, core, ioctl, VSG_OPEN,
+				&vsg_mops);
+	if (rc) {
+		WFD_MSG_ERR("Failed to open vsg subdevice: %d\n", rc);
+		goto err_vsg_open;
+	}
+
+	wfd_initialize_vb2_queue(&inst->vid_bufq, filp);
+	wfd_set_default_properties(filp);
+	WFD_MSG_DBG("wfd_open: X\n");
+	return rc;
+
+err_vsg_open:
+	v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl, CLOSE, NULL);
+err_venc:
+	v4l2_subdev_call(&wfd_dev->mdp_sdev, core, ioctl,
+				MDP_CLOSE, (void *)inst->mdp_inst);
+err_mdp_open:
+	kfree(inst);
+err_dev_busy:
+	return rc;
+}
+
+static int wfd_close(struct file *filp)
+{
+	struct wfd_inst *inst;
+	struct wfd_device *wfd_dev;
+	int rc = 0;
+	wfd_dev = video_drvdata(filp);
+	WFD_MSG_DBG("wfd_close: E\n");
+	inst = filp->private_data;
+	if (inst) {
+		wfdioc_streamoff(filp, NULL, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+		rc = v4l2_subdev_call(&wfd_dev->mdp_sdev, core, ioctl,
+				MDP_CLOSE, (void *)inst->mdp_inst);
+		if (rc)
+			WFD_MSG_ERR("Failed to CLOSE mdp subdevice: %d\n", rc);
+
+		vb2_queue_release(&inst->vid_bufq);
+		wfd_free_input_buffers(wfd_dev, inst);
+		rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl,
+				CLOSE, (void *)inst->venc_inst);
+
+		if (rc)
+			WFD_MSG_ERR("Failed to CLOSE enc subdev: %d\n", rc);
+
+		rc = v4l2_subdev_call(&wfd_dev->vsg_sdev, core, ioctl,
+				VSG_CLOSE, NULL);
+
+		if (rc)
+			WFD_MSG_ERR("Failed to CLOSE vsg subdev: %d\n", rc);
+
+		wfd_stats_deinit(&inst->stats);
+		kfree(inst);
+	}
+
+	mutex_lock(&wfd_dev->dev_lock);
+	wfd_dev->in_use = false;
+	mutex_unlock(&wfd_dev->dev_lock);
+
+	WFD_MSG_DBG("wfd_close: X\n");
+	return 0;
+}
+static const struct v4l2_file_operations g_wfd_fops = {
+	.owner = THIS_MODULE,
+	.open = wfd_open,
+	.release = wfd_close,
+	.ioctl = video_ioctl2
+};
+void release_video_device(struct video_device *pvdev)
+{
+
+}
+
+static int wfd_dev_setup(struct wfd_device *wfd_dev, int dev_num,
+		struct platform_device *pdev)
+{
+	int rc = 0;
+	rc = v4l2_device_register(&pdev->dev, &wfd_dev->v4l2_dev);
+	if (rc) {
+		WFD_MSG_ERR("Failed to register the video device\n");
+		goto err_v4l2_registration;
+	}
+	wfd_dev->pvdev = video_device_alloc();
+	if (!wfd_dev->pvdev) {
+		WFD_MSG_ERR("Failed to allocate video device\n");
+		goto err_video_device_alloc;
+	}
+
+	wfd_dev->pvdev->release = release_video_device;
+	wfd_dev->pvdev->fops = &g_wfd_fops;
+	wfd_dev->pvdev->ioctl_ops = &g_wfd_ioctl_ops;
+
+	rc = video_register_device(wfd_dev->pvdev, VFL_TYPE_GRABBER,
+			dev_num);
+	if (rc) {
+		WFD_MSG_ERR("Failed to register the device\n");
+		goto err_video_register_device;
+	}
+	video_set_drvdata(wfd_dev->pvdev, wfd_dev);
+
+	v4l2_subdev_init(&wfd_dev->mdp_sdev, &mdp_subdev_ops);
+	strncpy(wfd_dev->mdp_sdev.name, "wfd-mdp", V4L2_SUBDEV_NAME_SIZE);
+	rc = v4l2_device_register_subdev(&wfd_dev->v4l2_dev,
+			&wfd_dev->mdp_sdev);
+	if (rc) {
+		WFD_MSG_ERR("Failed to register mdp subdevice: %d\n", rc);
+		goto err_mdp_register_subdev;
+	}
+
+	v4l2_subdev_init(&wfd_dev->enc_sdev, &enc_subdev_ops);
+	strncpy(wfd_dev->enc_sdev.name, "wfd-venc", V4L2_SUBDEV_NAME_SIZE);
+	rc = v4l2_device_register_subdev(&wfd_dev->v4l2_dev,
+			&wfd_dev->enc_sdev);
+	if (rc) {
+		WFD_MSG_ERR("Failed to register encoder subdevice: %d\n", rc);
+		goto err_venc_register_subdev;
+	}
+	rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, init, 0);
+	if (rc) {
+		WFD_MSG_ERR("Failed to initiate encoder device %d\n", rc);
+		goto err_venc_init;
+	}
+
+	v4l2_subdev_init(&wfd_dev->vsg_sdev, &vsg_subdev_ops);
+	strncpy(wfd_dev->vsg_sdev.name, "wfd-vsg", V4L2_SUBDEV_NAME_SIZE);
+	rc = v4l2_device_register_subdev(&wfd_dev->v4l2_dev,
+			&wfd_dev->vsg_sdev);
+	if (rc) {
+		WFD_MSG_ERR("Failed to register vsg subdevice: %d\n", rc);
+		goto err_venc_init;
+	}
+
+	WFD_MSG_DBG("__wfd_probe: X\n");
+	return rc;
+
+err_venc_init:
+	v4l2_device_unregister_subdev(&wfd_dev->enc_sdev);
+err_venc_register_subdev:
+	v4l2_device_unregister_subdev(&wfd_dev->mdp_sdev);
+err_mdp_register_subdev:
+	video_unregister_device(wfd_dev->pvdev);
+err_video_register_device:
+	video_device_release(wfd_dev->pvdev);
+err_video_device_alloc:
+	v4l2_device_unregister(&wfd_dev->v4l2_dev);
+err_v4l2_registration:
+	return rc;
+}
+static int __devinit __wfd_probe(struct platform_device *pdev)
+{
+	int rc = 0, c = 0;
+	struct wfd_device *wfd_dev; /* Should be taken as an array*/
+	struct ion_client *ion_client = NULL;
+
+	WFD_MSG_DBG("__wfd_probe: E\n");
+	wfd_dev = kzalloc(sizeof(*wfd_dev)*WFD_NUM_DEVICES, GFP_KERNEL);
+	if (!wfd_dev) {
+		WFD_MSG_ERR("Could not allocate memory for "
+				"wfd device\n");
+		rc = -ENOMEM;
+		goto err_v4l2_probe;
+	}
+	pdev->dev.platform_data = (void *) wfd_dev;
+
+	ion_client = msm_ion_client_create(-1, "wfd");
+
+	rc = wfd_stats_setup();
+	if (rc) {
+		WFD_MSG_ERR("No debugfs support: %d\n", rc);
+		/* Don't treat this as a fatal err */
+		rc = 0;
+	}
+
+	if (!ion_client) {
+		WFD_MSG_ERR("Failed to create ion client\n");
+		rc = -ENODEV;
+		goto err_v4l2_probe;
+	}
+
+	for (c = 0; c < WFD_NUM_DEVICES; ++c) {
+		rc = wfd_dev_setup(&wfd_dev[c],
+			WFD_DEVICE_NUMBER_BASE + c, pdev);
+
+		if (rc) {
+			/* Clear out old devices */
+			for (--c; c >= 0; --c) {
+				v4l2_device_unregister_subdev(
+						&wfd_dev[c].vsg_sdev);
+				v4l2_device_unregister_subdev(
+						&wfd_dev[c].enc_sdev);
+				v4l2_device_unregister_subdev(
+						&wfd_dev[c].mdp_sdev);
+				video_unregister_device(wfd_dev[c].pvdev);
+				video_device_release(wfd_dev[c].pvdev);
+				v4l2_device_unregister(&wfd_dev[c].v4l2_dev);
+			}
+
+			goto err_v4l2_probe;
+		}
+
+		/* Other device specific stuff */
+		mutex_init(&wfd_dev[c].dev_lock);
+		wfd_dev[c].ion_client = ion_client;
+		wfd_dev[c].in_use = false;
+		switch (WFD_DEVICE_NUMBER_BASE + c) {
+		case WFD_DEVICE_SECURE:
+			wfd_dev[c].secure_device = true;
+			break;
+		default:
+			break;
+		}
+
+	}
+	WFD_MSG_DBG("__wfd_probe: X\n");
+	return rc;
+err_v4l2_probe:
+	kfree(wfd_dev);
+	return rc;
+}
+
+static int __devexit __wfd_remove(struct platform_device *pdev)
+{
+	struct wfd_device *wfd_dev;
+	int c = 0;
+
+	wfd_dev = (struct wfd_device *)pdev->dev.platform_data;
+
+	WFD_MSG_DBG("Inside wfd_remove\n");
+	if (!wfd_dev) {
+		WFD_MSG_ERR("Error removing WFD device");
+		return -ENODEV;
+	}
+
+	wfd_stats_teardown();
+	for (c = 0; c < WFD_NUM_DEVICES; ++c) {
+		v4l2_device_unregister_subdev(&wfd_dev[c].vsg_sdev);
+		v4l2_device_unregister_subdev(&wfd_dev[c].enc_sdev);
+		v4l2_device_unregister_subdev(&wfd_dev[c].mdp_sdev);
+		video_unregister_device(wfd_dev[c].pvdev);
+		video_device_release(wfd_dev[c].pvdev);
+		v4l2_device_unregister(&wfd_dev[c].v4l2_dev);
+	}
+
+	kfree(wfd_dev);
+	return 0;
+}
+static struct platform_driver wfd_driver = {
+	.probe =  __wfd_probe,
+	.remove = __wfd_remove,
+	.driver = {
+		.name = "msm_wfd",
+		.owner = THIS_MODULE,
+	}
+};
+
+static int __init wfd_init(void)
+{
+	int rc = 0;
+	WFD_MSG_DBG("Calling init function of wfd driver\n");
+	rc = platform_driver_register(&wfd_driver);
+	if (rc) {
+		WFD_MSG_ERR("failed to load the driver\n");
+		goto err_platform_registration;
+	}
+err_platform_registration:
+	return rc;
+}
+
+static void __exit wfd_exit(void)
+{
+	WFD_MSG_DBG("wfd_exit: X\n");
+	platform_driver_unregister(&wfd_driver);
+}
+
+module_init(wfd_init);
+module_exit(wfd_exit);
diff --git a/drivers/media/video/msm/wfd/wfd-util.c b/drivers/media/video/msm/wfd/wfd-util.c
new file mode 100644
index 0000000..233668b0
--- /dev/null
+++ b/drivers/media/video/msm/wfd/wfd-util.c
@@ -0,0 +1,217 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/debugfs.h>
+#include <linux/hrtimer.h>
+#include <linux/limits.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+
+#include "wfd-util.h"
+
+static struct dentry *wfd_debugfs_root;
+
+int wfd_stats_setup()
+{
+	wfd_debugfs_root = debugfs_create_dir("wfd", NULL);
+
+	if (wfd_debugfs_root == ERR_PTR(-ENODEV))
+		return -ENODEV;
+	else if (!wfd_debugfs_root)
+		return -ENOMEM;
+	else
+		return 0;
+}
+
+void wfd_stats_teardown()
+{
+	if (wfd_debugfs_root)
+		debugfs_remove_recursive(wfd_debugfs_root);
+}
+
+int wfd_stats_init(struct wfd_stats *stats, int device)
+{
+	char device_str[NAME_MAX] = "";
+	int rc = 0;
+
+	if (!stats) {
+		rc = -EINVAL;
+		goto wfd_stats_init_fail;
+	} else if (!wfd_debugfs_root) {
+		WFD_MSG_ERR("wfd debugfs root does not exist\n");
+		rc = -ENOENT;
+		goto wfd_stats_init_fail;
+	}
+
+	memset(stats, 0, sizeof(*stats));
+	INIT_LIST_HEAD(&stats->enc_queue);
+
+	snprintf(device_str, sizeof(device_str), "%d", device);
+	stats->d_parent = debugfs_create_dir(device_str, wfd_debugfs_root);
+	if (IS_ERR(stats->d_parent)) {
+		rc = PTR_ERR(stats->d_parent);
+		stats->d_parent = NULL;
+		goto wfd_stats_init_fail;
+	}
+
+	stats->d_v4l2_buf_count = debugfs_create_u32("v4l2_buf_count", S_IRUGO,
+			stats->d_parent, &stats->v4l2_buf_count);
+	if (IS_ERR(stats->d_v4l2_buf_count)) {
+		rc = PTR_ERR(stats->d_v4l2_buf_count);
+		stats->d_v4l2_buf_count = NULL;
+		goto wfd_stats_init_fail;
+	}
+
+	stats->d_mdp_buf_count = debugfs_create_u32("mdp_buf_count", S_IRUGO,
+			stats->d_parent, &stats->mdp_buf_count);
+	if (IS_ERR(stats->d_mdp_buf_count)) {
+		rc = PTR_ERR(stats->d_mdp_buf_count);
+		stats->d_mdp_buf_count = NULL;
+		goto wfd_stats_init_fail;
+	}
+
+	stats->d_vsg_buf_count = debugfs_create_u32("vsg_buf_count", S_IRUGO,
+			stats->d_parent, &stats->vsg_buf_count);
+	if (IS_ERR(stats->d_vsg_buf_count)) {
+		rc = PTR_ERR(stats->d_vsg_buf_count);
+		stats->d_vsg_buf_count = NULL;
+		goto wfd_stats_init_fail;
+	}
+
+	stats->d_enc_buf_count = debugfs_create_u32("enc_buf_count", S_IRUGO,
+			stats->d_parent, &stats->enc_buf_count);
+	if (IS_ERR(stats->d_enc_buf_count)) {
+		rc = PTR_ERR(stats->d_enc_buf_count);
+		stats->d_enc_buf_count = NULL;
+		goto wfd_stats_init_fail;
+	}
+
+	stats->d_frames_encoded = debugfs_create_u32("frames_encoded", S_IRUGO,
+			stats->d_parent, &stats->frames_encoded);
+	if (IS_ERR(stats->d_frames_encoded)) {
+		rc = PTR_ERR(stats->d_frames_encoded);
+		stats->d_frames_encoded = NULL;
+		goto wfd_stats_init_fail;
+	}
+
+	stats->d_mdp_updates = debugfs_create_u32("mdp_updates", S_IRUGO,
+			stats->d_parent, &stats->mdp_updates);
+	if (IS_ERR(stats->d_mdp_updates)) {
+		rc = PTR_ERR(stats->d_mdp_updates);
+		stats->d_mdp_updates = NULL;
+		goto wfd_stats_init_fail;
+	}
+
+	stats->d_enc_avg_latency = debugfs_create_u32("enc_avg_latency",
+			S_IRUGO, stats->d_parent, &stats->enc_avg_latency);
+	if (IS_ERR(stats->d_enc_avg_latency)) {
+		rc = PTR_ERR(stats->d_enc_avg_latency);
+		stats->d_enc_avg_latency = NULL;
+		goto wfd_stats_init_fail;
+	}
+
+	return rc;
+wfd_stats_init_fail:
+	return rc;
+}
+
+int wfd_stats_update(struct wfd_stats *stats, enum wfd_stats_event event)
+{
+	int rc = 0;
+	switch (event) {
+	case WFD_STAT_EVENT_CLIENT_QUEUE:
+		stats->v4l2_buf_count++;
+		break;
+	case WFD_STAT_EVENT_CLIENT_DEQUEUE: {
+		struct wfd_stats_encode_sample *sample = NULL;
+
+		stats->v4l2_buf_count--;
+
+		if (!list_empty(&stats->enc_queue))
+			sample = list_first_entry(&stats->enc_queue,
+					struct wfd_stats_encode_sample,
+					list);
+		if (sample) {
+			ktime_t kdiff = ktime_sub(ktime_get(),
+						sample->encode_start_ts);
+			uint32_t diff = ktime_to_ms(kdiff);
+
+			stats->enc_cumulative_latency += diff;
+			stats->enc_latency_samples++;
+			stats->enc_avg_latency = stats->enc_cumulative_latency /
+				stats->enc_latency_samples;
+
+			list_del(&sample->list);
+			kfree(sample);
+			sample = NULL;
+		}
+		break;
+	}
+	case WFD_STAT_EVENT_MDP_QUEUE:
+		stats->mdp_buf_count++;
+		stats->mdp_updates++;
+		break;
+	case WFD_STAT_EVENT_MDP_DEQUEUE:
+		stats->mdp_buf_count--;
+		break;
+	case WFD_STAT_EVENT_ENC_QUEUE: {
+		struct wfd_stats_encode_sample *sample = NULL;
+
+		stats->enc_buf_count++;
+		stats->frames_encoded++;
+
+		sample = kzalloc(sizeof(*sample), GFP_KERNEL);
+		if (sample) {
+			INIT_LIST_HEAD(&sample->list);
+			sample->encode_start_ts = ktime_get();
+			list_add_tail(&sample->list, &stats->enc_queue);
+		} else {
+			WFD_MSG_WARN("Unable to measure latency\n");
+		}
+		break;
+	}
+	case WFD_STAT_EVENT_ENC_DEQUEUE:
+		stats->enc_buf_count--;
+		break;
+	case WFD_STAT_EVENT_VSG_QUEUE:
+		stats->vsg_buf_count++;
+		break;
+	case WFD_STAT_EVENT_VSG_DEQUEUE:
+		stats->vsg_buf_count--;
+		break;
+	default:
+		rc = -ENOTSUPP;
+	}
+
+	return rc;
+}
+
+int wfd_stats_deinit(struct wfd_stats *stats)
+{
+	WFD_MSG_ERR("Latencies: avg enc. latency %d",
+			stats->enc_avg_latency);
+	/* Delete all debugfs files in one shot :) */
+	if (stats->d_parent)
+		debugfs_remove_recursive(stats->d_parent);
+
+	stats->d_parent =
+	stats->d_v4l2_buf_count =
+	stats->d_mdp_buf_count =
+	stats->d_vsg_buf_count =
+	stats->d_enc_buf_count =
+	stats->d_frames_encoded =
+	stats->d_mdp_updates =
+	stats->d_enc_avg_latency = NULL;
+
+	return 0;
+}
diff --git a/drivers/media/video/msm/wfd/wfd-util.h b/drivers/media/video/msm/wfd/wfd-util.h
new file mode 100644
index 0000000..b6bb245
--- /dev/null
+++ b/drivers/media/video/msm/wfd/wfd-util.h
@@ -0,0 +1,89 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/debugfs.h>
+#include <linux/list.h>
+#include <linux/ktime.h>
+
+#ifndef _WFD_UTIL_H_
+#define _WFD_UTIL_H_
+
+/*#define DEBUG_WFD*/
+
+#define WFD_TAG "wfd: "
+#ifdef DEBUG_WFD
+	#define WFD_MSG_INFO(fmt...) pr_info(WFD_TAG fmt)
+	#define WFD_MSG_WARN(fmt...) pr_warning(WFD_TAG fmt)
+#else
+	#define WFD_MSG_INFO(fmt...)
+	#define WFD_MSG_WARN(fmt...)
+#endif
+	#define WFD_MSG_ERR(fmt...) pr_err(KERN_ERR WFD_TAG fmt)
+	#define WFD_MSG_CRIT(fmt...) pr_crit(KERN_CRIT WFD_TAG fmt)
+	#define WFD_MSG_DBG(fmt...) pr_debug(WFD_TAG fmt)
+
+
+struct wfd_stats_encode_sample {
+	ktime_t encode_start_ts;
+	struct list_head list;
+};
+
+struct wfd_stats {
+	/* Output Buffers */
+	uint32_t v4l2_buf_count;
+
+	/* Input Buffers */
+	uint32_t mdp_buf_count;
+	uint32_t vsg_buf_count;
+	uint32_t enc_buf_count;
+
+	/* Other */
+	uint32_t frames_encoded;
+	uint32_t mdp_updates;
+
+	uint32_t enc_avg_latency;
+	uint32_t enc_cumulative_latency;
+	uint32_t enc_latency_samples;
+	struct list_head enc_queue;
+
+	/* Debugfs entries */
+	struct dentry *d_parent;
+	struct dentry *d_v4l2_buf_count;
+	struct dentry *d_mdp_buf_count;
+	struct dentry *d_vsg_buf_count;
+	struct dentry *d_enc_buf_count;
+	struct dentry *d_frames_encoded;
+	struct dentry *d_mdp_updates;
+	struct dentry *d_enc_avg_latency;
+};
+
+enum wfd_stats_event {
+	WFD_STAT_EVENT_CLIENT_QUEUE,
+	WFD_STAT_EVENT_CLIENT_DEQUEUE,
+
+	WFD_STAT_EVENT_MDP_QUEUE,
+	WFD_STAT_EVENT_MDP_DEQUEUE,
+
+	WFD_STAT_EVENT_VSG_QUEUE,
+	WFD_STAT_EVENT_VSG_DEQUEUE,
+
+	WFD_STAT_EVENT_ENC_QUEUE,
+	WFD_STAT_EVENT_ENC_DEQUEUE,
+};
+
+int wfd_stats_setup(void);
+int wfd_stats_init(struct wfd_stats *, int device);
+int wfd_stats_update(struct wfd_stats *, enum wfd_stats_event);
+int wfd_stats_deinit(struct wfd_stats *);
+void wfd_stats_teardown(void);
+#endif
diff --git a/drivers/media/video/msm_vidc/Kconfig b/drivers/media/video/msm_vidc/Kconfig
new file mode 100644
index 0000000..0b5a5fe
--- /dev/null
+++ b/drivers/media/video/msm_vidc/Kconfig
@@ -0,0 +1,8 @@
+#
+# VIDEO CORE
+#
+
+menuconfig MSM_VIDC
+	bool "Qualcomm MSM Video Core Driver"
+		depends on ARCH_MSMCOPPER && VIDEO_V4L2
+		default y
diff --git a/drivers/media/video/msm_vidc/Makefile b/drivers/media/video/msm_vidc/Makefile
new file mode 100644
index 0000000..12c61c9
--- /dev/null
+++ b/drivers/media/video/msm_vidc/Makefile
@@ -0,0 +1,8 @@
+obj-$(CONFIG_MSM_VIDC) := 	msm_v4l2_vidc.o \
+				msm_vidc_common.o \
+				msm_vidc.o \
+				msm_vdec.o \
+				msm_venc.o \
+				msm_smem.o \
+				vidc_hal.o \
+				vidc_hal_interrupt_handler.o \
diff --git a/drivers/media/video/msm_vidc/msm_smem.c b/drivers/media/video/msm_vidc/msm_smem.c
new file mode 100644
index 0000000..25b5c5c
--- /dev/null
+++ b/drivers/media/video/msm_vidc/msm_smem.c
@@ -0,0 +1,244 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/slab.h>
+#include "msm_smem.h"
+
+struct smem_client {
+	int mem_type;
+	void *clnt;
+};
+
+static int ion_user_to_kernel(struct smem_client *client,
+			int fd, u32 offset, struct msm_smem *mem)
+{
+	struct ion_handle *hndl;
+	unsigned long ionflag;
+	size_t len;
+	int rc = 0;
+	hndl = ion_import_fd(client->clnt, fd);
+	if (IS_ERR_OR_NULL(hndl)) {
+		pr_err("Failed to get handle: %p, %d, %d, %p\n",
+				client, fd, offset, hndl);
+		rc = -ENOMEM;
+		goto fail_import_fd;
+	}
+	rc = ion_handle_get_flags(client->clnt, hndl, &ionflag);
+	if (rc) {
+		pr_err("Failed to get ion flags: %d", rc);
+		goto fail_map;
+	}
+	rc = ion_phys(client->clnt, hndl, &mem->paddr, &len);
+	if (rc) {
+		pr_err("Failed to get physical address\n");
+		goto fail_map;
+	}
+	mem->kvaddr = ion_map_kernel(client->clnt, hndl, ionflag);
+	if (!mem->kvaddr) {
+		pr_err("Failed to map shared mem in kernel\n");
+		rc = -EIO;
+		goto fail_map;
+	}
+
+	mem->kvaddr += offset;
+	mem->paddr += offset;
+	mem->mem_type = client->mem_type;
+	mem->smem_priv = hndl;
+	mem->device_addr = mem->paddr;
+	mem->size = len;
+	return rc;
+fail_map:
+	ion_free(client->clnt, hndl);
+fail_import_fd:
+	return rc;
+}
+
+static int alloc_ion_mem(struct smem_client *client, size_t size,
+		u32 align, u32 flags, struct msm_smem *mem)
+{
+	struct ion_handle *hndl;
+	size_t len;
+	int rc = 0;
+	flags = flags | ION_HEAP(ION_CP_MM_HEAP_ID);
+	hndl = ion_alloc(client->clnt, size, align, flags);
+	if (IS_ERR_OR_NULL(hndl)) {
+		pr_err("Failed to allocate shared memory = %p, %d, %d, 0x%x\n",
+				client, size, align, flags);
+		rc = -ENOMEM;
+		goto fail_shared_mem_alloc;
+	}
+	mem->mem_type = client->mem_type;
+	mem->smem_priv = hndl;
+	if (ion_phys(client->clnt, hndl, &mem->paddr, &len)) {
+		pr_err("Failed to get physical address\n");
+		rc = -EIO;
+		goto fail_map;
+	}
+	mem->device_addr = mem->paddr;
+	mem->size = size;
+	mem->kvaddr = ion_map_kernel(client->clnt, hndl, 0);
+	if (!mem->kvaddr) {
+		pr_err("Failed to map shared mem in kernel\n");
+		rc = -EIO;
+		goto fail_map;
+	}
+	return rc;
+fail_map:
+	ion_free(client->clnt, hndl);
+fail_shared_mem_alloc:
+	return rc;
+}
+
+static void free_ion_mem(struct smem_client *client, struct msm_smem *mem)
+{
+	ion_unmap_kernel(client->clnt, mem->smem_priv);
+	ion_free(client->clnt, mem->smem_priv);
+}
+
+static void *ion_new_client(void)
+{
+	struct ion_client *client = NULL;
+	client = msm_ion_client_create(-1, "video_client");
+	if (!client)
+		pr_err("Failed to create smem client\n");
+	return client;
+};
+
+static void ion_delete_client(struct smem_client *client)
+{
+	ion_client_destroy(client->clnt);
+}
+
+struct msm_smem *msm_smem_user_to_kernel(void *clt, int fd, u32 offset)
+{
+	struct smem_client *client = clt;
+	int rc = 0;
+	struct msm_smem *mem;
+	if (fd < 0) {
+		pr_err("Invalid fd: %d\n", fd);
+		return NULL;
+	}
+	mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+	if (!mem) {
+		pr_err("Failed to allocte shared mem\n");
+		return NULL;
+	}
+	switch (client->mem_type) {
+	case SMEM_ION:
+		rc = ion_user_to_kernel(clt, fd, offset, mem);
+		break;
+	default:
+		pr_err("Mem type not supported\n");
+		rc = -EINVAL;
+		break;
+	}
+	if (rc) {
+		pr_err("Failed to allocate shared memory\n");
+		kfree(mem);
+		mem = NULL;
+	}
+	return mem;
+}
+
+void *msm_smem_new_client(enum smem_type mtype)
+{
+	struct smem_client *client = NULL;
+	void *clnt = NULL;
+	switch (mtype) {
+	case SMEM_ION:
+		clnt = ion_new_client();
+		break;
+	default:
+		pr_err("Mem type not supported\n");
+		break;
+	}
+	if (clnt) {
+		client = kzalloc(sizeof(*client), GFP_KERNEL);
+		if (client) {
+			client->mem_type = mtype;
+			client->clnt = clnt;
+		}
+	} else {
+		pr_err("Failed to create new client: mtype = %d\n", mtype);
+	}
+	return client;
+};
+
+struct msm_smem *msm_smem_alloc(void *clt, size_t size, u32 align, u32 flags)
+{
+	struct smem_client *client;
+	int rc = 0;
+	struct msm_smem *mem;
+
+	client = clt;
+	if (!client) {
+		pr_err("Invalid  client passed\n");
+		return NULL;
+	}
+	mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+	if (!mem) {
+		pr_err("Failed to allocate shared mem\n");
+		return NULL;
+	}
+	switch (client->mem_type) {
+	case SMEM_ION:
+		rc = alloc_ion_mem(client, size, align, flags, mem);
+		break;
+	default:
+		pr_err("Mem type not supported\n");
+		rc = -EINVAL;
+		break;
+	}
+	if (rc) {
+		pr_err("Failed to allocate shared memory\n");
+		kfree(mem);
+		mem = NULL;
+	}
+	return mem;
+}
+
+void msm_smem_free(void *clt, struct msm_smem *mem)
+{
+	struct smem_client *client = clt;
+	if (!client || !mem) {
+		pr_err("Invalid  client/handle passed\n");
+		return;
+	}
+	switch (client->mem_type) {
+	case SMEM_ION:
+		free_ion_mem(client, mem);
+		break;
+	default:
+		pr_err("Mem type not supported\n");
+		break;
+	}
+	kfree(mem);
+};
+
+void msm_smem_delete_client(void *clt)
+{
+	struct smem_client *client = clt;
+	if (!client) {
+		pr_err("Invalid  client passed\n");
+		return;
+	}
+	switch (client->mem_type) {
+	case SMEM_ION:
+		ion_delete_client(client);
+		break;
+	default:
+		pr_err("Mem type not supported\n");
+		break;
+	}
+	kfree(client);
+}
diff --git a/drivers/media/video/msm_vidc/msm_smem.h b/drivers/media/video/msm_vidc/msm_smem.h
new file mode 100644
index 0000000..84d12cc
--- /dev/null
+++ b/drivers/media/video/msm_vidc/msm_smem.h
@@ -0,0 +1,38 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef _MSM_SMEM_H_
+#define _MSM_SMEM_H_
+
+#include <linux/types.h>
+#include <linux/ion.h>
+
+enum smem_type {
+	SMEM_ION,
+};
+
+struct msm_smem {
+	int mem_type;
+	size_t size;
+	void *kvaddr;
+	unsigned long paddr;
+	unsigned long device_addr;
+	/*Device address and others to follow*/
+	void *smem_priv;
+};
+
+void *msm_smem_new_client(enum smem_type mtype);
+struct msm_smem *msm_smem_alloc(void *clt, size_t size, u32 align, u32 flags);
+void msm_smem_free(void *clt, struct msm_smem *mem);
+void msm_smem_delete_client(void *clt);
+struct msm_smem *msm_smem_user_to_kernel(void *clt, int fd, u32 offset);
+#endif
diff --git a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
new file mode 100644
index 0000000..1c646dd
--- /dev/null
+++ b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
@@ -0,0 +1,604 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/ioctl.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/version.h>
+#include <linux/slab.h>
+
+#include <media/msm_vidc.h>
+#include "msm_vidc_internal.h"
+#include "vidc_hal_api.h"
+#include "msm_smem.h"
+
+#define BASE_DEVICE_NUMBER 32
+
+struct msm_vidc_drv *vidc_driver;
+
+struct buffer_info {
+	struct list_head list;
+	int type;
+	int fd;
+	int buff_off;
+	int size;
+	u32 uvaddr;
+	struct msm_smem *handle;
+};
+
+struct msm_v4l2_vid_inst {
+	struct msm_vidc_inst vidc_inst;
+	void *mem_client;
+	struct list_head registered_bufs;
+};
+
+static inline struct msm_vidc_inst *get_vidc_inst(struct file *filp, void *fh)
+{
+	return container_of(filp->private_data,
+					struct msm_vidc_inst, event_handler);
+}
+
+static inline struct msm_v4l2_vid_inst *get_v4l2_inst(struct file *filp,
+			void *fh)
+{
+	struct msm_vidc_inst *vidc_inst;
+	vidc_inst = container_of(filp->private_data,
+			struct msm_vidc_inst, event_handler);
+	return container_of((void *)vidc_inst,
+			struct msm_v4l2_vid_inst, vidc_inst);
+}
+
+static int msm_vidc_v4l2_setup_event_queue(void *inst,
+				struct video_device *pvdev)
+{
+	int rc = 0;
+	struct msm_vidc_inst *vidc_inst = (struct msm_vidc_inst *)inst;
+	spin_lock_init(&pvdev->fh_lock);
+	INIT_LIST_HEAD(&pvdev->fh_list);
+
+	v4l2_fh_init(&vidc_inst->event_handler, pvdev);
+	v4l2_fh_add(&vidc_inst->event_handler);
+
+	return rc;
+}
+
+struct buffer_info *get_registered_buf(struct list_head *list,
+				int fd, u32 buff_off, u32 size)
+{
+	struct buffer_info *temp;
+	struct buffer_info *ret = NULL;
+	if (!list || fd < 0) {
+		pr_err("%s Invalid input\n", __func__);
+		goto err_invalid_input;
+	}
+	if (!list_empty(list)) {
+		list_for_each_entry(temp, list, list) {
+			if (temp && temp->fd == fd &&
+			(CONTAINS(temp->buff_off, temp->size, buff_off)
+			|| CONTAINS(buff_off, size, temp->buff_off)
+			|| OVERLAPS(buff_off, size,
+				temp->buff_off, temp->size))) {
+				pr_err("This memory region is already mapped\n");
+				ret = temp;
+				break;
+			}
+		}
+	}
+err_invalid_input:
+	return ret;
+}
+
+static int msm_v4l2_open(struct file *filp)
+{
+	int rc = 0;
+	struct video_device *vdev = video_devdata(filp);
+	struct msm_video_device *vid_dev =
+		container_of(vdev, struct msm_video_device, vdev);
+	struct msm_vidc_core *core = video_drvdata(filp);
+	struct msm_v4l2_vid_inst *v4l2_inst = kzalloc(sizeof(*v4l2_inst),
+						GFP_KERNEL);
+	if (!v4l2_inst) {
+		pr_err("Failed to allocate memory for this instance\n");
+		rc = -ENOMEM;
+		goto fail_nomem;
+	}
+	v4l2_inst->mem_client = msm_smem_new_client(SMEM_ION);
+	if (!v4l2_inst->mem_client) {
+		pr_err("Failed to create memory client\n");
+		rc = -ENOMEM;
+		goto fail_mem_client;
+	}
+	rc = msm_vidc_open(&v4l2_inst->vidc_inst, core->id, vid_dev->type);
+	if (rc) {
+		pr_err("Failed to create video instance, core: %d, type = %d\n",
+			core->id, vid_dev->type);
+		rc = -ENOMEM;
+		goto fail_open;
+	}
+	INIT_LIST_HEAD(&v4l2_inst->registered_bufs);
+	rc = msm_vidc_v4l2_setup_event_queue(&v4l2_inst->vidc_inst, vdev);
+	clear_bit(V4L2_FL_USES_V4L2_FH, &vdev->flags);
+	filp->private_data = &(v4l2_inst->vidc_inst.event_handler);
+	return rc;
+fail_open:
+	msm_smem_delete_client(v4l2_inst->mem_client);
+fail_mem_client:
+	kfree(v4l2_inst);
+fail_nomem:
+	return rc;
+}
+
+static int msm_v4l2_close(struct file *filp)
+{
+	int rc;
+	struct list_head *ptr, *next;
+	struct buffer_info *binfo;
+	struct msm_vidc_inst *vidc_inst;
+	struct msm_v4l2_vid_inst *v4l2_inst;
+	vidc_inst = get_vidc_inst(filp, NULL);
+	v4l2_inst = get_v4l2_inst(filp, NULL);
+	rc = msm_vidc_close(vidc_inst);
+	list_for_each_safe(ptr, next, &v4l2_inst->registered_bufs) {
+		binfo = list_entry(ptr, struct buffer_info, list);
+		list_del(&binfo->list);
+		msm_smem_free(v4l2_inst->mem_client, binfo->handle);
+		kfree(binfo);
+	}
+	msm_smem_delete_client(v4l2_inst->mem_client);
+	kfree(v4l2_inst);
+	return rc;
+}
+
+static int msm_v4l2_querycap(struct file *filp, void *fh,
+			struct v4l2_capability *cap)
+{
+	struct msm_vidc_inst *vidc_inst = get_vidc_inst(filp, fh);
+	return msm_vidc_querycap((void *)vidc_inst, cap);
+}
+
+int msm_v4l2_enum_fmt(struct file *file, void *fh,
+					struct v4l2_fmtdesc *f)
+{
+	struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
+	return msm_vidc_enum_fmt((void *)vidc_inst, f);
+}
+
+int msm_v4l2_s_fmt(struct file *file, void *fh,
+					struct v4l2_format *f)
+{
+	struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
+	return msm_vidc_s_fmt((void *)vidc_inst, f);
+}
+
+int msm_v4l2_g_fmt(struct file *file, void *fh,
+					struct v4l2_format *f)
+{
+	struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
+	return msm_vidc_g_fmt((void *)vidc_inst, f);
+}
+
+int msm_v4l2_s_ctrl(struct file *file, void *fh,
+					struct v4l2_control *a)
+{
+	struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
+	return msm_vidc_s_ctrl((void *)vidc_inst, a);
+}
+
+int msm_v4l2_g_ctrl(struct file *file, void *fh,
+					struct v4l2_control *a)
+{
+	struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
+	return msm_vidc_g_ctrl((void *)vidc_inst, a);
+}
+
+int msm_v4l2_reqbufs(struct file *file, void *fh,
+				struct v4l2_requestbuffers *b)
+{
+	struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
+	struct msm_v4l2_vid_inst *v4l2_inst;
+	struct list_head *ptr, *next;
+	int rc;
+	struct buffer_info *bi;
+	struct v4l2_buffer buffer_info;
+	v4l2_inst = get_v4l2_inst(file, NULL);
+	if (b->count == 0) {
+		list_for_each_safe(ptr, next, &v4l2_inst->registered_bufs) {
+			bi = list_entry(ptr, struct buffer_info, list);
+			if (bi->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+				buffer_info.type = bi->type;
+				buffer_info.m.planes[0].reserved[0] =
+					bi->fd;
+				buffer_info.m.planes[0].reserved[1] =
+					bi->buff_off;
+				buffer_info.m.planes[0].length = bi->size;
+				buffer_info.m.planes[0].m.userptr =
+					bi->uvaddr;
+				buffer_info.length = 1;
+				pr_err("Releasing buffer: %d, %d, %d\n",
+				buffer_info.m.planes[0].reserved[0],
+				buffer_info.m.planes[0].reserved[1],
+				buffer_info.m.planes[0].length);
+				rc = msm_vidc_release_buf(&v4l2_inst->vidc_inst,
+					&buffer_info);
+				list_del(&bi->list);
+				msm_smem_free(v4l2_inst->mem_client,
+					bi->handle);
+				kfree(bi);
+			}
+		}
+	}
+	return msm_vidc_reqbufs((void *)vidc_inst, b);
+}
+
+int msm_v4l2_prepare_buf(struct file *file, void *fh,
+				struct v4l2_buffer *b)
+{
+	struct msm_smem *handle;
+	struct buffer_info *binfo;
+	struct msm_vidc_inst *vidc_inst;
+	struct msm_v4l2_vid_inst *v4l2_inst;
+	int i, rc = 0;
+	vidc_inst = get_vidc_inst(file, fh);
+	v4l2_inst = get_v4l2_inst(file, fh);
+	if (!v4l2_inst->mem_client) {
+		pr_err("Failed to get memory client\n");
+		rc = -ENOMEM;
+		goto exit;
+	}
+	for (i = 0; i < b->length; ++i) {
+		binfo = get_registered_buf(&v4l2_inst->registered_bufs,
+				b->m.planes[i].reserved[0],
+				b->m.planes[i].reserved[1],
+				b->m.planes[i].length);
+		if (binfo) {
+			pr_err("This memory region has already been prepared\n");
+			rc = -EINVAL;
+			goto exit;
+		}
+		binfo = kzalloc(sizeof(*binfo), GFP_KERNEL);
+		if (!binfo) {
+			pr_err("Out of memory\n");
+			rc = -ENOMEM;
+			goto exit;
+		}
+		handle = msm_smem_user_to_kernel(v4l2_inst->mem_client,
+				b->m.planes[i].reserved[0],
+				b->m.planes[i].reserved[1]);
+		if (!handle) {
+			pr_err("Failed to get device buffer address\n");
+			kfree(binfo);
+			goto exit;
+		}
+		binfo->type = b->type;
+		binfo->fd = b->m.planes[i].reserved[0];
+		binfo->buff_off = b->m.planes[i].reserved[1];
+		binfo->size = b->m.planes[i].length;
+		binfo->uvaddr = b->m.planes[i].m.userptr;
+		binfo->handle = handle;
+		pr_debug("Registering buffer: %d, %d, %d\n",
+				b->m.planes[i].reserved[0],
+				b->m.planes[i].reserved[1],
+				b->m.planes[i].length);
+		list_add_tail(&binfo->list, &v4l2_inst->registered_bufs);
+		b->m.planes[i].m.userptr = handle->device_addr;
+	}
+	rc = msm_vidc_prepare_buf(&v4l2_inst->vidc_inst, b);
+exit:
+	return rc;
+}
+
+int msm_v4l2_qbuf(struct file *file, void *fh,
+				struct v4l2_buffer *b)
+{
+	struct msm_vidc_inst *vidc_inst;
+	struct msm_v4l2_vid_inst *v4l2_inst;
+	struct buffer_info *binfo;
+	int rc = 0;
+	int i;
+	vidc_inst = get_vidc_inst(file, fh);
+	v4l2_inst = get_v4l2_inst(file, fh);
+	for (i = 0; i < b->length; ++i) {
+		binfo = get_registered_buf(&v4l2_inst->registered_bufs,
+				b->m.planes[i].reserved[0],
+				b->m.planes[i].reserved[1],
+				b->m.planes[i].length);
+		if (!binfo) {
+			pr_err("This buffer is not registered: %d, %d, %d\n",
+				b->m.planes[i].reserved[0],
+				b->m.planes[i].reserved[1],
+				b->m.planes[i].length);
+			rc = -EINVAL;
+			goto err_invalid_buff;
+		}
+		b->m.planes[i].m.userptr = binfo->handle->device_addr;
+		pr_debug("Queueing device address = %ld\n",
+				binfo->handle->device_addr);
+	}
+	rc = msm_vidc_qbuf(&v4l2_inst->vidc_inst, b);
+err_invalid_buff:
+	return rc;
+}
+
+int msm_v4l2_dqbuf(struct file *file, void *fh,
+				struct v4l2_buffer *b)
+{
+	struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
+	return msm_vidc_dqbuf((void *)vidc_inst, b);
+}
+
+int msm_v4l2_streamon(struct file *file, void *fh,
+				enum v4l2_buf_type i)
+{
+	struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
+	return msm_vidc_streamon((void *)vidc_inst, i);
+}
+
+int msm_v4l2_streamoff(struct file *file, void *fh,
+				enum v4l2_buf_type i)
+{
+	struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
+	return msm_vidc_streamoff((void *)vidc_inst, i);
+}
+
+static int msm_v4l2_subscribe_event(struct v4l2_fh *fh,
+				struct v4l2_event_subscription *sub)
+{
+	int rc = 0;
+	if (sub->type == V4L2_EVENT_ALL)
+		sub->type = V4L2_EVENT_PRIVATE_START + V4L2_EVENT_VIDC_BASE;
+	rc = v4l2_event_subscribe(fh, sub, 0);
+	return rc;
+}
+
+static int msm_v4l2_unsubscribe_event(struct v4l2_fh *fh,
+				struct v4l2_event_subscription *sub)
+{
+	int rc = 0;
+	rc = v4l2_event_unsubscribe(fh, sub);
+	return rc;
+}
+
+static int msm_v4l2_decoder_cmd(struct file *file, void *fh,
+				struct v4l2_decoder_cmd *dec)
+{
+	struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
+	return msm_vidc_decoder_cmd((void *)vidc_inst, dec);
+}
+static const struct v4l2_ioctl_ops msm_v4l2_ioctl_ops = {
+	.vidioc_querycap = msm_v4l2_querycap,
+	.vidioc_enum_fmt_vid_cap_mplane = msm_v4l2_enum_fmt,
+	.vidioc_enum_fmt_vid_out_mplane = msm_v4l2_enum_fmt,
+	.vidioc_s_fmt_vid_cap_mplane = msm_v4l2_s_fmt,
+	.vidioc_s_fmt_vid_out_mplane = msm_v4l2_s_fmt,
+	.vidioc_g_fmt_vid_cap_mplane = msm_v4l2_g_fmt,
+	.vidioc_g_fmt_vid_out_mplane = msm_v4l2_g_fmt,
+	.vidioc_reqbufs = msm_v4l2_reqbufs,
+	.vidioc_prepare_buf = msm_v4l2_prepare_buf,
+	.vidioc_qbuf = msm_v4l2_qbuf,
+	.vidioc_dqbuf = msm_v4l2_dqbuf,
+	.vidioc_streamon = msm_v4l2_streamon,
+	.vidioc_streamoff = msm_v4l2_streamoff,
+	.vidioc_s_ctrl = msm_v4l2_s_ctrl,
+	.vidioc_g_ctrl = msm_v4l2_g_ctrl,
+	.vidioc_subscribe_event = msm_v4l2_subscribe_event,
+	.vidioc_unsubscribe_event = msm_v4l2_unsubscribe_event,
+	.vidioc_decoder_cmd = msm_v4l2_decoder_cmd,
+};
+
+static const struct v4l2_ioctl_ops msm_v4l2_enc_ioctl_ops = {
+};
+
+static unsigned int msm_v4l2_poll(struct file *filp,
+	struct poll_table_struct *pt)
+{
+	struct msm_vidc_inst *vidc_inst = get_vidc_inst(filp, NULL);
+	return msm_vidc_poll((void *)vidc_inst, filp, pt);
+}
+
+static const struct v4l2_file_operations msm_v4l2_vidc_fops = {
+	.owner = THIS_MODULE,
+	.open = msm_v4l2_open,
+	.release = msm_v4l2_close,
+	.ioctl = video_ioctl2,
+	.poll = msm_v4l2_poll,
+};
+
+void msm_vidc_release_video_device(struct video_device *pvdev)
+{
+}
+
+static int msm_vidc_initialize_core(struct platform_device *pdev,
+				struct msm_vidc_core *core)
+{
+	struct resource *res;
+	int i = 0;
+	if (!core)
+		return -EINVAL;
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		pr_err("Failed to get IORESOURCE_MEM\n");
+		return -ENODEV;
+	}
+	core->register_base = res->start;
+	core->register_size = resource_size(res);
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!res) {
+		pr_err("Failed to get IORESOURCE_IRQ\n");
+		return -ENODEV;
+	}
+	core->irq = res->start;
+	INIT_LIST_HEAD(&core->instances);
+	mutex_init(&core->sync_lock);
+	spin_lock_init(&core->lock);
+	core->base_addr = 0x34f00000;
+	core->state = VIDC_CORE_UNINIT;
+	for (i = SYS_MSG_INDEX(SYS_MSG_START);
+		i <= SYS_MSG_INDEX(SYS_MSG_END); i++) {
+		init_completion(&core->completions[i]);
+	}
+	return 0;
+}
+
+static int __devinit msm_vidc_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct msm_vidc_core *core;
+	unsigned long flags;
+	char debugfs_name[MAX_DEBUGFS_NAME];
+
+	core = kzalloc(sizeof(*core), GFP_KERNEL);
+	if (!core || !vidc_driver) {
+		pr_err("Failed to allocate memory for device core\n");
+		rc = -ENOMEM;
+		goto err_no_mem;
+	}
+	rc = msm_vidc_initialize_core(pdev, core);
+	if (rc) {
+		pr_err("Failed to init core\n");
+		goto err_v4l2_register;
+	}
+	rc = v4l2_device_register(&pdev->dev, &core->v4l2_dev);
+	if (rc) {
+		pr_err("Failed to register v4l2 device\n");
+		goto err_v4l2_register;
+	}
+	core->vdev[MSM_VIDC_DECODER].vdev.release =
+		msm_vidc_release_video_device;
+	core->vdev[MSM_VIDC_DECODER].vdev.fops = &msm_v4l2_vidc_fops;
+	core->vdev[MSM_VIDC_DECODER].vdev.ioctl_ops = &msm_v4l2_ioctl_ops;
+	core->vdev[MSM_VIDC_DECODER].type = MSM_VIDC_DECODER;
+	rc = video_register_device(&core->vdev[MSM_VIDC_DECODER].vdev,
+					VFL_TYPE_GRABBER, BASE_DEVICE_NUMBER);
+	if (rc) {
+		pr_err("Failed to register video decoder device");
+		goto err_dec_register;
+	}
+	video_set_drvdata(&core->vdev[MSM_VIDC_DECODER].vdev, core);
+
+	core->vdev[MSM_VIDC_ENCODER].vdev.release =
+		msm_vidc_release_video_device;
+	core->vdev[MSM_VIDC_ENCODER].vdev.fops = &msm_v4l2_vidc_fops;
+	core->vdev[MSM_VIDC_ENCODER].vdev.ioctl_ops = &msm_v4l2_ioctl_ops;
+	core->vdev[MSM_VIDC_ENCODER].type = MSM_VIDC_ENCODER;
+	rc = video_register_device(&core->vdev[MSM_VIDC_ENCODER].vdev,
+				VFL_TYPE_GRABBER, BASE_DEVICE_NUMBER + 1);
+	if (rc) {
+		pr_err("Failed to register video encoder device");
+		goto err_enc_register;
+	}
+	video_set_drvdata(&core->vdev[MSM_VIDC_ENCODER].vdev, core);
+	core->device = vidc_hal_add_device(core->id, core->base_addr,
+			core->register_base, core->register_size, core->irq,
+			&handle_cmd_response);
+	if (!core->device) {
+		pr_err("Failed to create interrupt handler");
+		goto err_cores_exceeded;
+	}
+
+	spin_lock_irqsave(&vidc_driver->lock, flags);
+	if (vidc_driver->num_cores  + 1 > MSM_VIDC_CORES_MAX) {
+		spin_unlock_irqrestore(&vidc_driver->lock, flags);
+		pr_err("Maximum cores already exist, core_no = %d\n",
+				vidc_driver->num_cores);
+		goto err_cores_exceeded;
+	}
+
+	core->id = vidc_driver->num_cores++;
+	list_add_tail(&core->list, &vidc_driver->cores);
+	spin_unlock_irqrestore(&vidc_driver->lock, flags);
+	snprintf(debugfs_name, MAX_DEBUGFS_NAME, "core%d", core->id);
+	core->debugfs_root = debugfs_create_dir(debugfs_name,
+						vidc_driver->debugfs_root);
+	pdev->dev.platform_data = core;
+	return rc;
+
+err_cores_exceeded:
+	video_unregister_device(&core->vdev[MSM_VIDC_ENCODER].vdev);
+err_enc_register:
+	video_unregister_device(&core->vdev[MSM_VIDC_DECODER].vdev);
+err_dec_register:
+	v4l2_device_unregister(&core->v4l2_dev);
+err_v4l2_register:
+	kfree(core);
+err_no_mem:
+	return rc;
+}
+
+static int __devexit msm_vidc_remove(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct msm_vidc_core *core = pdev->dev.platform_data;
+	vidc_hal_delete_device(core->device);
+	video_unregister_device(&core->vdev[MSM_VIDC_ENCODER].vdev);
+	video_unregister_device(&core->vdev[MSM_VIDC_DECODER].vdev);
+	v4l2_device_unregister(&core->v4l2_dev);
+	kfree(core);
+	return rc;
+}
+static const struct of_device_id msm_vidc_dt_match[] = {
+	{.compatible = "qcom,msm-vidc"},
+};
+
+MODULE_DEVICE_TABLE(of, msm_vidc_dt_match);
+
+static struct platform_driver msm_vidc_driver = {
+	.probe = msm_vidc_probe,
+	.remove = msm_vidc_remove,
+	.driver = {
+		.name = "msm_vidc",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_vidc_dt_match,
+	},
+};
+
+static int __init msm_vidc_init(void)
+{
+	int rc = 0;
+	vidc_driver = kzalloc(sizeof(*vidc_driver),
+						GFP_KERNEL);
+	if (!vidc_driver) {
+		pr_err("Failed to allocate memroy for msm_vidc_drv\n");
+		return -ENOMEM;
+	}
+
+	INIT_LIST_HEAD(&vidc_driver->cores);
+	spin_lock_init(&vidc_driver->lock);
+	vidc_driver->debugfs_root = debugfs_create_dir("msm_vidc", NULL);
+	if (!vidc_driver->debugfs_root)
+		pr_err("Failed to create debugfs for msm_vidc\n");
+
+	rc = platform_driver_register(&msm_vidc_driver);
+	if (rc) {
+		pr_err("Failed to register platform driver\n");
+		kfree(vidc_driver);
+		vidc_driver = NULL;
+	}
+
+	return rc;
+}
+
+static void __exit msm_vidc_exit(void)
+{
+	platform_driver_unregister(&msm_vidc_driver);
+	debugfs_remove_recursive(vidc_driver->debugfs_root);
+	kfree(vidc_driver);
+	vidc_driver = NULL;
+}
+
+module_init(msm_vidc_init);
+module_exit(msm_vidc_exit);
diff --git a/drivers/media/video/msm_vidc/msm_vdec.c b/drivers/media/video/msm_vidc/msm_vdec.c
new file mode 100644
index 0000000..20a0cd0
--- /dev/null
+++ b/drivers/media/video/msm_vidc/msm_vdec.c
@@ -0,0 +1,885 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/slab.h>
+
+#include "msm_vidc_internal.h"
+#include "msm_vidc_common.h"
+#include "vidc_hal_api.h"
+#include "msm_smem.h"
+
+#define MSM_VDEC_DVC_NAME "msm_vdec_8974"
+#define MAX_PLANES 1
+#define DEFAULT_HEIGHT 720
+#define DEFAULT_WIDTH 1280
+#define MIN_NUM_OUTPUT_BUFFERS 2
+#define MAX_NUM_OUTPUT_BUFFERS 6
+
+static const char *const mpeg_video_vidc_divx_format[] = {
+	"DIVX Format 3",
+	"DIVX Format 4",
+	"DIVX Format 5",
+	"DIVX Format 6",
+	NULL
+};
+static const char *mpeg_video_stream_format[] = {
+	"NAL Format Start Codes",
+	"NAL Format One NAL Per Buffer",
+	"NAL Format One Byte Length",
+	"NAL Format Two Byte Length",
+	"NAL Format Four Byte Length",
+	NULL
+};
+static const char *const mpeg_video_output_order[] = {
+	"Display Order",
+	"Decode Order",
+	NULL
+};
+static const struct msm_vidc_ctrl msm_vdec_ctrls[] = {
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_STREAM_FORMAT,
+		.name = "NAL Format",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_STARTCODES,
+		.maximum = V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_FOUR_BYTE_LENGTH,
+		.default_value = V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_STARTCODES,
+		.menu_skip_mask = ~(
+		(1 << V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_STARTCODES) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_ONE_NAL_PER_BUFFER) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_ONE_BYTE_LENGTH) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_TWO_BYTE_LENGTH) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_FOUR_BYTE_LENGTH)
+		),
+		.qmenu = mpeg_video_stream_format,
+		.step = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_OUTPUT_ORDER,
+		.name = "Output Order",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDC_VIDEO_OUTPUT_ORDER_DISPLAY,
+		.maximum = V4L2_MPEG_VIDC_VIDEO_OUTPUT_ORDER_DECODE,
+		.default_value = V4L2_MPEG_VIDC_VIDEO_OUTPUT_ORDER_DISPLAY,
+		.menu_skip_mask = ~(
+			(1 << V4L2_MPEG_VIDC_VIDEO_OUTPUT_ORDER_DISPLAY) |
+			(1 << V4L2_MPEG_VIDC_VIDEO_OUTPUT_ORDER_DECODE)
+			),
+		.qmenu = mpeg_video_output_order,
+		.step = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_ENABLE_PICTURE_TYPE,
+		.name = "Picture Type Decoding",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 1,
+		.maximum = 15,
+		.default_value = 15,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_KEEP_ASPECT_RATIO,
+		.name = "Keep Aspect Ratio",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.minimum = 0,
+		.maximum = 1,
+		.default_value = 0,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_POST_LOOP_DEBLOCKER_MODE,
+		.name = "Deblocker Mode",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.minimum = 0,
+		.maximum = 1,
+		.default_value = 0,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_DIVX_FORMAT,
+		.name = "Divx Format",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDC_VIDEO_DIVX_FORMAT_4,
+		.maximum = V4L2_MPEG_VIDC_VIDEO_DIVX_FORMAT_6,
+		.default_value = V4L2_MPEG_VIDC_VIDEO_DIVX_FORMAT_4,
+		.menu_skip_mask = ~(
+			(1 << V4L2_MPEG_VIDC_VIDEO_DIVX_FORMAT_4) |
+			(1 << V4L2_MPEG_VIDC_VIDEO_DIVX_FORMAT_5) |
+			(1 << V4L2_MPEG_VIDC_VIDEO_DIVX_FORMAT_6)
+			),
+		.qmenu = mpeg_video_vidc_divx_format,
+		.step = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_MB_ERROR_MAP_REPORTING,
+		.name = "MB Error Map Reporting",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.minimum = 0,
+		.maximum = 1,
+		.default_value = 0,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_CONTINUE_DATA_TRANSFER,
+		.name = "control",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.minimum = 0,
+		.maximum = 1,
+		.default_value = 0,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+};
+
+#define NUM_CTRLS ARRAY_SIZE(msm_vdec_ctrls)
+
+static u32 get_frame_size_nv12(int plane,
+					u32 height, u32 width)
+{
+	return (ALIGN(height, 32) * ALIGN(width, 32) * 3) / 2;
+}
+static u32 get_frame_size_nv21(int plane,
+					u32 height, u32 width)
+{
+	return height * width * 2;
+}
+
+static u32 get_frame_size_compressed(int plane,
+					u32 height, u32 width)
+{
+	return 0x500000;
+}
+
+static const struct msm_vidc_format vdec_formats[] = {
+	{
+		.name = "YCbCr Semiplanar 4:2:0",
+		.description = "Y/CbCr 4:2:0",
+		.fourcc = V4L2_PIX_FMT_NV12,
+		.num_planes = 1,
+		.get_frame_size = get_frame_size_nv12,
+		.type = CAPTURE_PORT,
+	},
+	{
+		.name = "Mpeg4",
+		.description = "Mpeg4 compressed format",
+		.fourcc = V4L2_PIX_FMT_MPEG4,
+		.num_planes = 1,
+		.get_frame_size = get_frame_size_compressed,
+		.type = OUTPUT_PORT,
+	},
+	{
+		.name = "Mpeg2",
+		.description = "Mpeg2 compressed format",
+		.fourcc = V4L2_PIX_FMT_MPEG2,
+		.num_planes = 1,
+		.get_frame_size = get_frame_size_compressed,
+		.type = OUTPUT_PORT,
+	},
+	{
+		.name = "H263",
+		.description = "H263 compressed format",
+		.fourcc = V4L2_PIX_FMT_H263,
+		.num_planes = 1,
+		.get_frame_size = get_frame_size_compressed,
+		.type = OUTPUT_PORT,
+	},
+	{
+		.name = "H264",
+		.description = "H264 compressed format",
+		.fourcc = V4L2_PIX_FMT_H264,
+		.num_planes = 1,
+		.get_frame_size = get_frame_size_compressed,
+		.type = OUTPUT_PORT,
+	},
+	{
+		.name = "YCrCb Semiplanar 4:2:0",
+		.description = "Y/CrCb 4:2:0",
+		.fourcc = V4L2_PIX_FMT_NV21,
+		.num_planes = 1,
+		.get_frame_size = get_frame_size_nv21,
+		.type = CAPTURE_PORT,
+	},
+	{
+		.name = "DIVX 311",
+		.description = "DIVX 311 compressed format",
+		.fourcc = V4L2_PIX_FMT_DIVX_311,
+		.num_planes = 1,
+		.get_frame_size = get_frame_size_compressed,
+		.type = OUTPUT_PORT,
+	}
+};
+
+int msm_vdec_streamon(struct msm_vidc_inst *inst, enum v4l2_buf_type i)
+{
+	int rc = 0;
+	struct vb2_queue *q;
+	q = msm_comm_get_vb2q(inst, i);
+	if (!q) {
+		pr_err("Failed to find buffer queue for type = %d\n", i);
+		return -EINVAL;
+	}
+	pr_debug("Calling streamon\n");
+	rc = vb2_streamon(q, i);
+	if (rc)
+		pr_err("streamon failed on port: %d\n", i);
+	return rc;
+}
+
+int msm_vdec_streamoff(struct msm_vidc_inst *inst, enum v4l2_buf_type i)
+{
+	int rc = 0;
+	struct vb2_queue *q;
+
+	q = msm_comm_get_vb2q(inst, i);
+	if (!q) {
+		pr_err("Failed to find buffer queue for type = %d\n", i);
+		return -EINVAL;
+	}
+	pr_debug("Calling streamoff\n");
+	rc = vb2_streamoff(q, i);
+	if (rc)
+		pr_err("streamoff failed on port: %d\n", i);
+	return rc;
+}
+
+int msm_vdec_prepare_buf(struct msm_vidc_inst *inst,
+					struct v4l2_buffer *b)
+{
+	int rc = 0;
+	int i;
+	struct vidc_buffer_addr_info buffer_info;
+	switch (b->type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		break;
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		for (i = 0; i < b->length; i++) {
+			pr_err("device_addr = %ld, size = %d\n",
+				b->m.planes[i].m.userptr,
+				b->m.planes[i].length);
+			buffer_info.buffer_size = b->m.planes[i].length;
+			buffer_info.buffer_type = HAL_BUFFER_OUTPUT;
+			buffer_info.num_buffers = 1;
+			buffer_info.align_device_addr =
+				b->m.planes[i].m.userptr;
+			buffer_info.extradata_size = 0;
+			buffer_info.extradata_addr = 0;
+			rc = vidc_hal_session_set_buffers((void *)inst->session,
+					&buffer_info);
+			if (rc) {
+				pr_err("vidc_hal_session_set_buffers failed");
+				break;
+			}
+		}
+		break;
+	default:
+		pr_err("Buffer type not recognized: %d\n", b->type);
+		break;
+	}
+	return rc;
+}
+
+int msm_vdec_release_buf(struct msm_vidc_inst *inst,
+					struct v4l2_buffer *b)
+{
+	int rc = 0;
+	int i;
+	struct vidc_buffer_addr_info buffer_info;
+
+	switch (b->type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		break;
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		for (i = 0; i < b->length; i++) {
+			pr_debug("Release device_addr = %ld, size = %d\n",
+				b->m.planes[i].m.userptr,
+				b->m.planes[i].length);
+			buffer_info.buffer_size = b->m.planes[i].length;
+			buffer_info.buffer_type = HAL_BUFFER_OUTPUT;
+			buffer_info.num_buffers = 1;
+			buffer_info.align_device_addr =
+				 b->m.planes[i].m.userptr;
+			buffer_info.extradata_addr = 0;
+			rc = vidc_hal_session_release_buffers(
+				(void *)inst->session, &buffer_info);
+			if (rc)
+				pr_err("vidc_hal_session_release_buffers failed");
+		}
+		break;
+	default:
+		pr_err("Buffer type not recognized: %d\n", b->type);
+		break;
+	}
+	return rc;
+}
+
+int msm_vdec_qbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b)
+{
+	struct vb2_queue *q = NULL;
+	int rc = 0;
+	q = msm_comm_get_vb2q(inst, b->type);
+	if (!q) {
+		pr_err("Failed to find buffer queue for type = %d\n", b->type);
+		return -EINVAL;
+	}
+	rc = vb2_qbuf(q, b);
+	if (rc)
+		pr_err("Failed to qbuf, %d\n", rc);
+	return rc;
+}
+int msm_vdec_dqbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b)
+{
+	struct vb2_queue *q = NULL;
+	int rc = 0;
+	q = msm_comm_get_vb2q(inst, b->type);
+	if (!q) {
+		pr_err("Failed to find buffer queue for type = %d\n", b->type);
+		return -EINVAL;
+	}
+	rc = vb2_dqbuf(q, b, true);
+	if (rc)
+		pr_err("Failed to dqbuf, %d\n", rc);
+	return rc;
+}
+
+int msm_vdec_reqbufs(struct msm_vidc_inst *inst, struct v4l2_requestbuffers *b)
+{
+	struct vb2_queue *q = NULL;
+	int rc = 0;
+	if (!inst || !b) {
+		pr_err("Invalid input, inst = %p, buffer = %p\n", inst, b);
+		return -EINVAL;
+	}
+	q = msm_comm_get_vb2q(inst, b->type);
+	if (!q) {
+		pr_err("Failed to find buffer queue for type = %d\n", b->type);
+		return -EINVAL;
+	}
+
+	rc = vb2_reqbufs(q, b);
+	if (rc)
+		pr_err("Failed to get reqbufs, %d\n", rc);
+	return rc;
+}
+
+int msm_vdec_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
+{
+	const struct msm_vidc_format *fmt = NULL;
+	int rc = 0;
+	int i;
+	if (!inst || !f) {
+		pr_err("Invalid input, inst = %p, format = %p\n", inst, f);
+		return -EINVAL;
+	}
+	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		fmt = inst->fmts[CAPTURE_PORT];
+	else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+		fmt = inst->fmts[OUTPUT_PORT];
+
+	if (fmt) {
+		f->fmt.pix_mp.pixelformat = fmt->fourcc;
+		if (inst->in_reconfig == true) {
+			inst->height = inst->reconfig_height;
+			inst->width = inst->reconfig_width;
+		}
+		f->fmt.pix_mp.height = inst->height;
+		f->fmt.pix_mp.width = inst->width;
+		for (i = 0; i < fmt->num_planes; ++i) {
+			f->fmt.pix_mp.plane_fmt[i].sizeimage =
+			fmt->get_frame_size(i, inst->height, inst->width);
+		}
+	} else {
+		pr_err("Buf type not recognized, type = %d\n",
+					f->type);
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+int msm_vdec_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
+{
+	const struct msm_vidc_format *fmt = NULL;
+	int rc = 0;
+	int i;
+	if (!inst || !f) {
+		pr_err("Invalid input, inst = %p, format = %p\n", inst, f);
+		return -EINVAL;
+	}
+	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		inst->width = f->fmt.pix_mp.width;
+		inst->height = f->fmt.pix_mp.height;
+		fmt = msm_comm_get_pixel_fmt_fourcc(vdec_formats,
+			ARRAY_SIZE(vdec_formats), f->fmt.pix_mp.pixelformat,
+			CAPTURE_PORT);
+		if (fmt && fmt->type != CAPTURE_PORT) {
+			pr_err("Format: %d not supported on CAPTURE port\n",
+					f->fmt.pix_mp.pixelformat);
+			rc = -EINVAL;
+			goto err_invalid_fmt;
+		}
+	} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		fmt = msm_comm_get_pixel_fmt_fourcc(vdec_formats,
+			ARRAY_SIZE(vdec_formats), f->fmt.pix_mp.pixelformat,
+			OUTPUT_PORT);
+		if (fmt && fmt->type != OUTPUT_PORT) {
+			pr_err("Format: %d not supported on OUTPUT port\n",
+					f->fmt.pix_mp.pixelformat);
+			rc = -EINVAL;
+			goto err_invalid_fmt;
+		}
+	}
+
+	if (fmt) {
+		for (i = 0; i < fmt->num_planes; ++i) {
+			f->fmt.pix_mp.plane_fmt[i].sizeimage =
+				fmt->get_frame_size(i, f->fmt.pix_mp.height,
+						f->fmt.pix_mp.width);
+		}
+		inst->fmts[fmt->type] = fmt;
+		if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+			rc = msm_comm_try_state(inst, MSM_VIDC_OPEN);
+			if (rc) {
+				pr_err("Failed to open instance\n");
+				goto err_invalid_fmt;
+			}
+		}
+	} else {
+		pr_err("Buf type not recognized, type = %d\n",
+					f->type);
+		rc = -EINVAL;
+	}
+err_invalid_fmt:
+	return rc;
+}
+
+int msm_vdec_querycap(struct msm_vidc_inst *inst, struct v4l2_capability *cap)
+{
+	if (!inst || !cap) {
+		pr_err("Invalid input, inst = %p, cap = %p\n", inst, cap);
+		return -EINVAL;
+	}
+	strlcpy(cap->driver, MSM_VIDC_DRV_NAME, sizeof(cap->driver));
+	strlcpy(cap->card, MSM_VDEC_DVC_NAME, sizeof(cap->card));
+	cap->bus_info[0] = 0;
+	cap->version = MSM_VIDC_VERSION;
+	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
+						V4L2_CAP_VIDEO_OUTPUT_MPLANE |
+						V4L2_CAP_STREAMING;
+	memset(cap->reserved, 0, sizeof(cap->reserved));
+	return 0;
+}
+
+int msm_vdec_enum_fmt(struct msm_vidc_inst *inst, struct v4l2_fmtdesc *f)
+{
+	const struct msm_vidc_format *fmt = NULL;
+	int rc = 0;
+	if (!inst || !f) {
+		pr_err("Invalid input, inst = %p, f = %p\n", inst, f);
+		return -EINVAL;
+	}
+	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		fmt = msm_comm_get_pixel_fmt_index(vdec_formats,
+			ARRAY_SIZE(vdec_formats), f->index, CAPTURE_PORT);
+	} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		fmt = msm_comm_get_pixel_fmt_index(vdec_formats,
+			ARRAY_SIZE(vdec_formats), f->index, OUTPUT_PORT);
+		f->flags = V4L2_FMT_FLAG_COMPRESSED;
+	}
+
+	memset(f->reserved, 0 , sizeof(f->reserved));
+	if (fmt) {
+		strlcpy(f->description, fmt->description,
+				sizeof(f->description));
+		f->pixelformat = fmt->fourcc;
+	} else {
+		pr_err("No more formats found\n");
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+static int msm_vdec_queue_setup(struct vb2_queue *q,
+				const struct v4l2_format *fmt,
+				unsigned int *num_buffers,
+				unsigned int *num_planes, unsigned int sizes[],
+				void *alloc_ctxs[])
+{
+	int i, rc = 0;
+	struct msm_vidc_inst *inst;
+	struct hal_frame_size frame_sz;
+	unsigned long flags;
+	if (!q || !q->drv_priv) {
+		pr_err("Invalid input, q = %p\n", q);
+		return -EINVAL;
+	}
+	inst = q->drv_priv;
+	switch (q->type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		*num_planes = 1;
+		if (*num_buffers < MIN_NUM_OUTPUT_BUFFERS ||
+				*num_buffers > MAX_NUM_OUTPUT_BUFFERS)
+			*num_buffers = MIN_NUM_OUTPUT_BUFFERS;
+		for (i = 0; i < *num_planes; i++) {
+			sizes[i] = inst->fmts[OUTPUT_PORT]->get_frame_size(
+					i, inst->height, inst->width);
+		}
+		break;
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		pr_debug("Getting bufreqs on capture plane\n");
+		rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE);
+		if (rc) {
+			pr_err("Failed to open instance\n");
+			break;
+		}
+		frame_sz.buffer_type = HAL_BUFFER_OUTPUT;
+		frame_sz.width = inst->width;
+		frame_sz.height = inst->height;
+		pr_debug("width = %d, height = %d\n",
+				frame_sz.width, frame_sz.height);
+		rc = vidc_hal_session_set_property((void *)inst->session,
+				HAL_PARAM_FRAME_SIZE, &frame_sz);
+		if (rc) {
+			pr_err("Failed to set hal property for framesize\n");
+			break;
+		}
+		rc = msm_comm_try_get_bufreqs(inst);
+		if (rc) {
+			pr_err("Failed to get buffer requirements: %d\n", rc);
+			break;
+		}
+		*num_planes = 1;
+		spin_lock_irqsave(&inst->lock, flags);
+		*num_buffers = inst->buff_req.buffer[1].buffer_count_actual;
+		spin_unlock_irqrestore(&inst->lock, flags);
+		pr_debug("size = %d, alignment = %d\n",
+				inst->buff_req.buffer[1].buffer_size,
+				inst->buff_req.buffer[1].buffer_alignment);
+		for (i = 0; i < *num_planes; i++) {
+			sizes[i] = inst->fmts[CAPTURE_PORT]->get_frame_size(
+					i, inst->height, inst->width);
+		}
+
+		break;
+	default:
+		pr_err("Invalid q type = %d\n", q->type);
+		rc = -EINVAL;
+		break;
+	}
+	return rc;
+}
+
+static inline int start_streaming(struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+	unsigned long flags;
+	struct vb2_buf_entry *temp;
+	struct list_head *ptr, *next;
+	struct v4l2_control control;
+	struct hal_nal_stream_format_supported stream_format;
+	struct hal_enable_picture enable_picture;
+	struct hal_enable hal_property;
+	u32 control_idx = 0;
+	enum hal_property property_id = 0;
+	u32 property_val = 0;
+	void *pdata;
+	rc = msm_comm_set_scratch_buffers(inst);
+	if (rc) {
+		pr_err("Failed to set scratch buffers: %d\n", rc);
+		goto fail_start;
+	}
+	for (; control_idx < NUM_CTRLS; control_idx++) {
+		control.id = msm_vdec_ctrls[control_idx].id;
+		rc = v4l2_g_ctrl(&inst->ctrl_handler, &control);
+		if (rc) {
+			pr_err("Failed to get control value for ID=%d\n",
+				   msm_vdec_ctrls[control_idx].id);
+		} else {
+			property_id = 0;
+			switch (control.id) {
+			case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_FORMAT:
+				property_id =
+					HAL_PARAM_NAL_STREAM_FORMAT_SELECT;
+				stream_format.nal_stream_format_supported =
+					(0x00000001 << control.value);
+				pdata = &stream_format;
+				break;
+			case V4L2_CID_MPEG_VIDC_VIDEO_OUTPUT_ORDER:
+				property_id = HAL_PARAM_VDEC_OUTPUT_ORDER;
+				property_val = control.value;
+				pdata = &property_val;
+				break;
+			case V4L2_CID_MPEG_VIDC_VIDEO_ENABLE_PICTURE_TYPE:
+				property_id =
+					HAL_PARAM_VDEC_PICTURE_TYPE_DECODE;
+				enable_picture.picture_type = control.value;
+				pdata = &enable_picture;
+				break;
+			case V4L2_CID_MPEG_VIDC_VIDEO_KEEP_ASPECT_RATIO:
+				property_id =
+				HAL_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO;
+				hal_property.enable = control.value;
+				pdata = &hal_property;
+				break;
+			case V4L2_CID_MPEG_VIDC_VIDEO_POST_LOOP_DEBLOCKER_MODE:
+				property_id =
+					HAL_CONFIG_VDEC_POST_LOOP_DEBLOCKER;
+				hal_property.enable = control.value;
+				pdata = &hal_property;
+				break;
+			case V4L2_CID_MPEG_VIDC_VIDEO_DIVX_FORMAT:
+				property_id = HAL_PARAM_DIVX_FORMAT;
+				property_val = control.value;
+				pdata = &property_val;
+				break;
+			case V4L2_CID_MPEG_VIDC_VIDEO_MB_ERROR_MAP_REPORTING:
+				property_id =
+					HAL_CONFIG_VDEC_MB_ERROR_MAP_REPORTING;
+				hal_property.enable = control.value;
+				pdata = &hal_property;
+				break;
+			case V4L2_CID_MPEG_VIDC_VIDEO_CONTINUE_DATA_TRANSFER:
+				property_id =
+					HAL_PARAM_VDEC_CONTINUE_DATA_TRANSFER;
+				hal_property.enable = control.value;
+				pdata = &hal_property;
+				break;
+			default:
+				break;
+			}
+			if (property_id) {
+				pr_err("Control: HAL property=%x,ctrl_id=%x,ctrl_value=%d\n",
+					   property_id,
+					   msm_vdec_ctrls[control_idx].id,
+					   control.value);
+				rc = vidc_hal_session_set_property((void *)
+						inst->session, property_id,
+						pdata);
+			}
+			if (rc)
+				pr_err("Failed to set hal property for framesize\n");
+		}
+	}
+
+	rc = msm_comm_try_state(inst, MSM_VIDC_START_DONE);
+	if (rc) {
+		pr_err("Failed to move inst: %p to start done state\n",
+				inst);
+		goto fail_start;
+	}
+	spin_lock_irqsave(&inst->lock, flags);
+	if (!list_empty(&inst->pendingq)) {
+		list_for_each_safe(ptr, next, &inst->pendingq) {
+			temp = list_entry(ptr, struct vb2_buf_entry, list);
+			rc = msm_comm_qbuf(temp->vb);
+			if (rc) {
+				pr_err("Failed to qbuf to hardware\n");
+				break;
+			}
+			list_del(&temp->list);
+			kfree(temp);
+		}
+	}
+	spin_unlock_irqrestore(&inst->lock, flags);
+	return rc;
+fail_start:
+	return rc;
+}
+
+static int msm_vdec_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+	struct msm_vidc_inst *inst;
+	int rc = 0;
+	if (!q || !q->drv_priv) {
+		pr_err("Invalid input, q = %p\n", q);
+		return -EINVAL;
+	}
+	inst = q->drv_priv;
+	pr_debug("Streamon called on: %d capability\n", q->type);
+	switch (q->type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		if (inst->vb2_bufq[CAPTURE_PORT].streaming)
+			rc = start_streaming(inst);
+		break;
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		inst->in_reconfig = false;
+		if (inst->vb2_bufq[OUTPUT_PORT].streaming)
+			rc = start_streaming(inst);
+		break;
+	default:
+		pr_err("Q-type is not supported: %d\n", q->type);
+		rc = -EINVAL;
+		break;
+	}
+	return rc;
+}
+
+static int msm_vdec_stop_streaming(struct vb2_queue *q)
+{
+	struct msm_vidc_inst *inst;
+	int rc = 0;
+	if (!q || !q->drv_priv) {
+		pr_err("Invalid input, q = %p\n", q);
+		return -EINVAL;
+	}
+	inst = q->drv_priv;
+	pr_debug("Streamoff called on: %d capability\n", q->type);
+	switch (q->type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		if (!inst->vb2_bufq[CAPTURE_PORT].streaming)
+			rc = msm_comm_try_state(inst, MSM_VIDC_CLOSE_DONE);
+		break;
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		if (!inst->vb2_bufq[OUTPUT_PORT].streaming)
+			rc = msm_comm_try_state(inst, MSM_VIDC_CLOSE_DONE);
+		break;
+	default:
+		pr_err("Q-type is not supported: %d\n", q->type);
+		rc = -EINVAL;
+		break;
+	}
+	if (rc)
+		pr_err("Failed to move inst: %p, cap = %d to state: %d\n",
+				inst, q->type, MSM_VIDC_CLOSE_DONE);
+	return rc;
+}
+
+static void msm_vdec_buf_queue(struct vb2_buffer *vb)
+{
+	int rc;
+	rc = msm_comm_qbuf(vb);
+	if (rc)
+		pr_err("Failed to queue buffer: %d\n", rc);
+}
+
+static const struct vb2_ops msm_vdec_vb2q_ops = {
+	.queue_setup = msm_vdec_queue_setup,
+	.start_streaming = msm_vdec_start_streaming,
+	.buf_queue = msm_vdec_buf_queue,
+	.stop_streaming = msm_vdec_stop_streaming,
+};
+
+const struct vb2_ops *msm_vdec_get_vb2q_ops(void)
+{
+	return &msm_vdec_vb2q_ops;
+}
+
+int msm_vdec_inst_init(struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+	if (!inst) {
+		pr_err("Invalid input = %p\n", inst);
+		return -EINVAL;
+	}
+	inst->fmts[OUTPUT_PORT] = &vdec_formats[1];
+	inst->fmts[CAPTURE_PORT] = &vdec_formats[0];
+	inst->height = DEFAULT_HEIGHT;
+	inst->width = DEFAULT_WIDTH;
+	return rc;
+}
+
+static int msm_vdec_op_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	return 0;
+}
+static int msm_vdec_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+	return 0;
+}
+
+static const struct v4l2_ctrl_ops msm_vdec_ctrl_ops = {
+
+	.s_ctrl = msm_vdec_op_s_ctrl,
+	.g_volatile_ctrl = msm_vdec_op_g_volatile_ctrl,
+};
+
+const struct v4l2_ctrl_ops *msm_vdec_get_ctrl_ops(void)
+{
+	return &msm_vdec_ctrl_ops;
+}
+
+int msm_vdec_s_ctrl(struct msm_vidc_inst *inst, struct v4l2_control *ctrl)
+{
+	return v4l2_s_ctrl(NULL, &inst->ctrl_handler, ctrl);
+}
+int msm_vdec_g_ctrl(struct msm_vidc_inst *inst, struct v4l2_control *ctrl)
+{
+	return v4l2_g_ctrl(&inst->ctrl_handler, ctrl);
+}
+int msm_vdec_ctrl_init(struct msm_vidc_inst *inst)
+{
+	int idx = 0;
+	struct v4l2_ctrl_config ctrl_cfg;
+	int ret_val = 0;
+
+	ret_val = v4l2_ctrl_handler_init(&inst->ctrl_handler, NUM_CTRLS);
+
+	if (ret_val) {
+		pr_err("CTRL ERR: Control handler init failed, %d\n",
+				inst->ctrl_handler.error);
+		return ret_val;
+	}
+
+	for (; idx < NUM_CTRLS; idx++) {
+		if (IS_PRIV_CTRL(msm_vdec_ctrls[idx].id)) {
+			/*add private control*/
+			ctrl_cfg.def = msm_vdec_ctrls[idx].default_value;
+			ctrl_cfg.flags = 0;
+			ctrl_cfg.id = msm_vdec_ctrls[idx].id;
+			/*ctrl_cfg.is_private =
+			 * msm_vdec_ctrls[idx].is_private;
+			 * ctrl_cfg.is_volatile =
+			 * msm_vdec_ctrls[idx].is_volatile;*/
+			ctrl_cfg.max = msm_vdec_ctrls[idx].maximum;
+			ctrl_cfg.min = msm_vdec_ctrls[idx].minimum;
+			ctrl_cfg.menu_skip_mask =
+				msm_vdec_ctrls[idx].menu_skip_mask;
+			ctrl_cfg.name = msm_vdec_ctrls[idx].name;
+			ctrl_cfg.ops = &msm_vdec_ctrl_ops;
+			ctrl_cfg.step = msm_vdec_ctrls[idx].step;
+			ctrl_cfg.type = msm_vdec_ctrls[idx].type;
+			ctrl_cfg.qmenu = msm_vdec_ctrls[idx].qmenu;
+
+			v4l2_ctrl_new_custom(&inst->ctrl_handler,
+					&ctrl_cfg, NULL);
+		} else {
+			if (msm_vdec_ctrls[idx].type == V4L2_CTRL_TYPE_MENU) {
+				v4l2_ctrl_new_std_menu(&inst->ctrl_handler,
+					&msm_vdec_ctrl_ops,
+					msm_vdec_ctrls[idx].id,
+					msm_vdec_ctrls[idx].maximum,
+					msm_vdec_ctrls[idx].menu_skip_mask,
+					msm_vdec_ctrls[idx].default_value);
+			} else {
+				v4l2_ctrl_new_std(&inst->ctrl_handler,
+					&msm_vdec_ctrl_ops,
+					msm_vdec_ctrls[idx].id,
+					msm_vdec_ctrls[idx].minimum,
+					msm_vdec_ctrls[idx].maximum,
+					msm_vdec_ctrls[idx].step,
+					msm_vdec_ctrls[idx].default_value);
+			}
+		}
+	}
+	ret_val = inst->ctrl_handler.error;
+	if (ret_val)
+		pr_err("CTRL ERR: Error adding ctrls to ctrl handle, %d\n",
+				inst->ctrl_handler.error);
+	return ret_val;
+}
diff --git a/drivers/media/video/msm_vidc/msm_vdec.h b/drivers/media/video/msm_vidc/msm_vdec.h
new file mode 100644
index 0000000..1242fb4
--- /dev/null
+++ b/drivers/media/video/msm_vidc/msm_vdec.h
@@ -0,0 +1,36 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef _MSM_VDEC_H_
+#define _MSM_VDEC_H_
+
+#include <media/msm_vidc.h>
+#include "msm_vidc_internal.h"
+
+int msm_vdec_inst_init(struct msm_vidc_inst *inst);
+int msm_vdec_ctrl_init(struct msm_vidc_inst *inst);
+int msm_vdec_querycap(void *instance, struct v4l2_capability *cap);
+int msm_vdec_enum_fmt(void *instance, struct v4l2_fmtdesc *f);
+int msm_vdec_s_fmt(void *instance, struct v4l2_format *f);
+int msm_vdec_g_fmt(void *instance, struct v4l2_format *f);
+int msm_vdec_s_ctrl(void *instance, struct v4l2_control *a);
+int msm_vdec_g_ctrl(void *instance, struct v4l2_control *a);
+int msm_vdec_reqbufs(void *instance, struct v4l2_requestbuffers *b);
+int msm_vdec_prepare_buf(struct msm_vidc_inst *inst, struct v4l2_buffer *b);
+int msm_vdec_release_buf(struct msm_vidc_inst *inst, struct v4l2_buffer *b);
+int msm_vdec_qbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b);
+int msm_vdec_dqbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b);
+int msm_vdec_streamon(struct msm_vidc_inst *inst, enum v4l2_buf_type i);
+int msm_vdec_streamoff(struct msm_vidc_inst *inst, enum v4l2_buf_type i);
+struct vb2_ops *msm_vdec_get_vb2q_ops(void);
+
+#endif
diff --git a/drivers/media/video/msm_vidc/msm_venc.c b/drivers/media/video/msm_vidc/msm_venc.c
new file mode 100644
index 0000000..ed99d35
--- /dev/null
+++ b/drivers/media/video/msm_vidc/msm_venc.c
@@ -0,0 +1,1254 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/slab.h>
+
+#include "msm_vidc_internal.h"
+#include "msm_vidc_common.h"
+#include "vidc_hal_api.h"
+#include "msm_smem.h"
+
+#define MSM_VENC_DVC_NAME "msm_venc_8974"
+#define DEFAULT_HEIGHT 720
+#define DEFAULT_WIDTH 1280
+#define MIN_NUM_OUTPUT_BUFFERS 2
+#define MAX_NUM_OUTPUT_BUFFERS 8
+#define MIN_BIT_RATE 64
+#define MAX_BIT_RATE 8000
+#define DEFAULT_BIT_RATE 64
+#define BIT_RATE_STEP 1
+#define MIN_FRAME_RATE 1
+#define MAX_FRAME_RATE 120
+#define DEFAULT_FRAME_RATE 30
+#define MAX_SLICE_BYTE_SIZE 1024
+#define MIN_SLICE_BYTE_SIZE 1024
+#define MAX_SLICE_MB_SIZE 300
+#define I_FRAME_QP 26
+#define P_FRAME_QP 28
+#define B_FRAME_QP 30
+#define MAX_INTRA_REFRESH_MBS 300
+#define L_MODE V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY
+
+static const char *const mpeg_video_rate_control[] = {
+	"No Rate Control",
+	"VBR VFR",
+	"VBR CFR",
+	"CBR VFR",
+	"CBR CFR",
+	NULL
+};
+
+static const char *const mpeg_video_rotation[] = {
+	"No Rotation",
+	"90 Degree Rotation",
+	"180 Degree Rotation",
+	"270 Degree Rotation",
+	NULL
+};
+
+static const char *const h264_video_entropy_cabac_model[] = {
+	"Model 0",
+	"Model 1",
+	"Model 2",
+	NULL
+};
+static const struct msm_vidc_ctrl msm_venc_ctrls[] = {
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_FRAME_RATE,
+		.name = "Frame Rate",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = MIN_FRAME_RATE,
+		.maximum = MAX_FRAME_RATE,
+		.default_value = DEFAULT_FRAME_RATE,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_IDR_PERIOD,
+		.name = "IDR Period",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = 10*MAX_FRAME_RATE,
+		.default_value = 0,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_NUM_P_FRAMES,
+		.name = "Intra Period for P frames",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = 10*DEFAULT_FRAME_RATE,
+		.default_value = 2*DEFAULT_FRAME_RATE-1,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES,
+		.name = "Intra Period for B frames",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = 10*DEFAULT_FRAME_RATE,
+		.default_value = 0,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_REQUEST_IFRAME,
+		.name = "Request I Frame",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.minimum = 0,
+		.maximum = 1,
+		.default_value = 0,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL,
+		.name = "Rate Control",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_OFF,
+		.maximum = V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_CFR,
+		.default_value = V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_OFF,
+		.step = 0,
+		.menu_skip_mask = ~(
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_OFF) |
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_VBR_VFR) |
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_VBR_CFR) |
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_VFR) |
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_CFR)
+		),
+		.qmenu = mpeg_video_rate_control,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_BITRATE,
+		.name = "Bit Rate",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = MIN_BIT_RATE,
+		.maximum = MAX_BIT_RATE,
+		.default_value = DEFAULT_BIT_RATE,
+		.step = BIT_RATE_STEP,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE,
+		.name = "Entropy Mode",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC,
+		.maximum = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC,
+		.default_value = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC,
+		.step = 0,
+		.menu_skip_mask = ~(
+		(1 << V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC) |
+		(1 << V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC)
+		),
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL,
+		.name = "CABAC Model",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_0,
+		.maximum = V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_1,
+		.default_value = V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_0,
+		.step = 0,
+		.menu_skip_mask = ~(
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_0) |
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_1) |
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_2)
+		),
+		.qmenu = h264_video_entropy_cabac_model,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+		.name = "H264 Profile",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
+		.maximum = V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH,
+		.default_value = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
+		.step = 1,
+		.menu_skip_mask = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+		.name = "H264 Level",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
+		.maximum = V4L2_MPEG_VIDEO_H264_LEVEL_5_1,
+		.default_value = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
+		.step = 1,
+		.menu_skip_mask = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_ROTATION,
+		.name = "Rotation",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_NONE,
+		.maximum = V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_270,
+		.default_value = V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_NONE,
+		.step = 0,
+		.menu_skip_mask = ~(
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_NONE) |
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_90) |
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_180) |
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_270)
+		),
+		.qmenu = mpeg_video_rotation,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP,
+		.name = "I Frame Quantization",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 1,
+		.maximum = 51,
+		.default_value = I_FRAME_QP,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP,
+		.name = "P Frame Quantization",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 1,
+		.maximum = 51,
+		.default_value = P_FRAME_QP,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP,
+		.name = "B Frame Quantization",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 1,
+		.maximum = 51,
+		.default_value = B_FRAME_QP,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE,
+		.name = "Slice Mode",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE,
+		.maximum = V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES,
+		.default_value = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE,
+		.step = 1,
+		.menu_skip_mask = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES,
+		.name = "Slice Byte Size",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = MIN_SLICE_BYTE_SIZE,
+		.maximum = MAX_SLICE_BYTE_SIZE,
+		.default_value = 0,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB,
+		.name = "Slice MB Size",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 1,
+		.maximum = MAX_SLICE_MB_SIZE,
+		.default_value = 0,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE,
+		.name = "Intra Refresh Mode",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_NONE,
+		.maximum = V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_RANDOM,
+		.default_value = V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_NONE,
+		.step = 0,
+		.menu_skip_mask = ~(
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_NONE) |
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_CYCLIC) |
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_ADAPTIVE) |
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_CYCLIC_ADAPTIVE) |
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_RANDOM)
+		),
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_AIR_MBS,
+		.name = "Intra Refresh AIR MBS",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = MAX_INTRA_REFRESH_MBS,
+		.default_value = 0,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_AIR_REF,
+		.name = "Intra Refresh AIR REF",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = MAX_INTRA_REFRESH_MBS,
+		.default_value = 0,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_CIR_MBS,
+		.name = "Intra Refresh CIR MBS",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = MAX_INTRA_REFRESH_MBS,
+		.default_value = 0,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA,
+		.name = "H.264 Loop Filter Alpha Offset",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = -6,
+		.maximum = 6,
+		.default_value = 0,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA,
+		.name = "H.264 Loop Filter Beta Offset",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = -6,
+		.maximum = 6,
+		.default_value = 0,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE,
+		.name = "H.264 Loop Filter Mode",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED,
+		.maximum = L_MODE,
+		.default_value = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED,
+		.step = 1,
+		.menu_skip_mask = ~(
+		(1 << V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED) |
+		(1 << V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED) |
+		(1 << L_MODE)
+		),
+	},
+};
+
+#define NUM_CTRLS ARRAY_SIZE(msm_venc_ctrls)
+
+static u32 get_frame_size_nv12(int plane, u32 height, u32 width)
+{
+	return ((height + 31) & (~31)) * ((width + 31) & (~31)) * 3/2;
+}
+
+static u32 get_frame_size_nv21(int plane, u32 height, u32 width)
+{
+	return height * width * 2;
+}
+
+static u32 get_frame_size_compressed(int plane, u32 height, u32 width)
+{
+	return ((height + 31) & (~31)) * ((width + 31) & (~31)) * 3/2;
+}
+
+static struct hal_quantization
+	venc_quantization = {I_FRAME_QP, P_FRAME_QP, B_FRAME_QP};
+static struct hal_intra_period
+	venc_intra_period = {2*DEFAULT_FRAME_RATE-1 , 0};
+static struct hal_profile_level
+	venc_profile_level = {V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
+				V4L2_MPEG_VIDEO_H264_LEVEL_1_0};
+static struct hal_h264_entropy_control
+	venc_h264_entropy_control = {V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC,
+				V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_0};
+static struct hal_multi_slice_control
+	venc_multi_slice_control = {V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE ,
+				0};
+
+static const struct msm_vidc_format venc_formats[] = {
+	{
+		.name = "YCbCr Semiplanar 4:2:0",
+		.description = "Y/CbCr 4:2:0",
+		.fourcc = V4L2_PIX_FMT_NV12,
+		.num_planes = 1,
+		.get_frame_size = get_frame_size_nv12,
+		.type = OUTPUT_PORT,
+	},
+	{
+		.name = "Mpeg4",
+		.description = "Mpeg4 compressed format",
+		.fourcc = V4L2_PIX_FMT_MPEG4,
+		.num_planes = 1,
+		.get_frame_size = get_frame_size_compressed,
+		.type = CAPTURE_PORT,
+	},
+	{
+		.name = "H263",
+		.description = "H263 compressed format",
+		.fourcc = V4L2_PIX_FMT_H263,
+		.num_planes = 1,
+		.get_frame_size = get_frame_size_compressed,
+		.type = CAPTURE_PORT,
+	},
+	{
+		.name = "H264",
+		.description = "H264 compressed format",
+		.fourcc = V4L2_PIX_FMT_H264,
+		.num_planes = 1,
+		.get_frame_size = get_frame_size_compressed,
+		.type = CAPTURE_PORT,
+	},
+	{
+		.name = "YCrCb Semiplanar 4:2:0",
+		.description = "Y/CrCb 4:2:0",
+		.fourcc = V4L2_PIX_FMT_NV21,
+		.num_planes = 1,
+		.get_frame_size = get_frame_size_nv21,
+		.type = OUTPUT_PORT,
+	},
+};
+
+static int msm_venc_queue_setup(struct vb2_queue *q,
+				const struct v4l2_format *fmt,
+				unsigned int *num_buffers,
+				unsigned int *num_planes, unsigned int sizes[],
+				void *alloc_ctxs[])
+{
+	int i, rc = 0;
+	struct msm_vidc_inst *inst;
+	struct hal_frame_size frame_sz;
+	unsigned long flags;
+	if (!q || !q->drv_priv) {
+		pr_err("Invalid input, q = %p\n", q);
+		return -EINVAL;
+	}
+	inst = q->drv_priv;
+	switch (q->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		*num_planes = 1;
+		if (*num_buffers < MIN_NUM_OUTPUT_BUFFERS ||
+				*num_buffers > MAX_NUM_OUTPUT_BUFFERS)
+			*num_buffers = MIN_NUM_OUTPUT_BUFFERS;
+		for (i = 0; i < *num_planes; i++) {
+			sizes[i] = inst->fmts[OUTPUT_PORT]->get_frame_size(
+					i, inst->height, inst->width);
+		}
+		break;
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE);
+		if (rc) {
+			pr_err("Failed to open instance\n");
+			break;
+		}
+		frame_sz.buffer_type = HAL_BUFFER_INPUT;
+		frame_sz.width = inst->width;
+		frame_sz.height = inst->height;
+		pr_debug("width = %d, height = %d\n",
+				frame_sz.width, frame_sz.height);
+		rc = vidc_hal_session_set_property((void *)inst->session,
+				HAL_PARAM_FRAME_SIZE, &frame_sz);
+		if (rc) {
+			pr_err("Failed to set hal property for framesize\n");
+			break;
+		}
+		rc = msm_comm_try_get_bufreqs(inst);
+		if (rc) {
+			pr_err("Failed to get buffer requirements: %d\n", rc);
+			break;
+		}
+		*num_planes = 1;
+		spin_lock_irqsave(&inst->lock, flags);
+		*num_buffers = inst->buff_req.buffer[0].buffer_count_actual;
+		spin_unlock_irqrestore(&inst->lock, flags);
+		pr_debug("size = %d, alignment = %d, count = %d\n",
+				inst->buff_req.buffer[0].buffer_size,
+				inst->buff_req.buffer[0].buffer_alignment,
+				inst->buff_req.buffer[0].buffer_count_actual);
+		for (i = 0; i < *num_planes; i++) {
+			sizes[i] = inst->fmts[CAPTURE_PORT]->get_frame_size(
+					i, inst->height, inst->width);
+		}
+
+		break;
+	default:
+		pr_err("Invalid q type = %d\n", q->type);
+		rc = -EINVAL;
+		break;
+	}
+	return rc;
+}
+
+static inline int start_streaming(struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+	unsigned long flags;
+	struct vb2_buf_entry *temp;
+	struct list_head *ptr, *next;
+	rc = msm_comm_set_scratch_buffers(inst);
+	if (rc) {
+		pr_err("Failed to set scratch buffers: %d\n", rc);
+		goto fail_start;
+	}
+	rc = msm_comm_try_state(inst, MSM_VIDC_START_DONE);
+	if (rc) {
+		pr_err("Failed to move inst: %p to start done state\n",
+				inst);
+		goto fail_start;
+	}
+	spin_lock_irqsave(&inst->lock, flags);
+	if (!list_empty(&inst->pendingq)) {
+		list_for_each_safe(ptr, next, &inst->pendingq) {
+			temp = list_entry(ptr, struct vb2_buf_entry, list);
+			rc = msm_comm_qbuf(temp->vb);
+			if (rc) {
+				pr_err("Failed to qbuf to hardware\n");
+				break;
+			}
+			list_del(&temp->list);
+			kfree(temp);
+		}
+	}
+	spin_unlock_irqrestore(&inst->lock, flags);
+	return rc;
+fail_start:
+	return rc;
+}
+
+static int msm_venc_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+	struct msm_vidc_inst *inst;
+	int rc = 0;
+	if (!q || !q->drv_priv) {
+		pr_err("Invalid input, q = %p\n", q);
+		return -EINVAL;
+	}
+	inst = q->drv_priv;
+	pr_debug("Streamon called on: %d capability\n", q->type);
+	switch (q->type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		if (inst->vb2_bufq[CAPTURE_PORT].streaming)
+			rc = start_streaming(inst);
+		break;
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		if (inst->vb2_bufq[OUTPUT_PORT].streaming)
+			rc = start_streaming(inst);
+		break;
+	default:
+		pr_err("Q-type is not supported: %d\n", q->type);
+		rc = -EINVAL;
+		break;
+	}
+	return rc;
+}
+
+static int msm_venc_stop_streaming(struct vb2_queue *q)
+{
+	struct msm_vidc_inst *inst;
+	int rc = 0;
+	if (!q || !q->drv_priv) {
+		pr_err("Invalid input, q = %p\n", q);
+		return -EINVAL;
+	}
+	inst = q->drv_priv;
+	pr_debug("Streamoff called on: %d capability\n", q->type);
+	switch (q->type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		break;
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		rc = msm_comm_try_state(inst, MSM_VIDC_CLOSE_DONE);
+		break;
+	default:
+		pr_err("Q-type is not supported: %d\n", q->type);
+		rc = -EINVAL;
+		break;
+	}
+	if (rc)
+		pr_err("Failed to move inst: %p, cap = %d to state: %d\n",
+				inst, q->type, MSM_VIDC_CLOSE_DONE);
+	return rc;
+}
+
+static void msm_venc_buf_queue(struct vb2_buffer *vb)
+{
+	int rc;
+	rc = msm_comm_qbuf(vb);
+	if (rc)
+		pr_err("Failed to queue buffer: %d\n", rc);
+}
+
+static const struct vb2_ops msm_venc_vb2q_ops = {
+	.queue_setup = msm_venc_queue_setup,
+	.start_streaming = msm_venc_start_streaming,
+	.buf_queue = msm_venc_buf_queue,
+	.stop_streaming = msm_venc_stop_streaming,
+};
+
+const struct vb2_ops *msm_venc_get_vb2q_ops(void)
+{
+	return &msm_venc_vb2q_ops;
+}
+
+static int msm_venc_op_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+
+	int rc = 0;
+	struct v4l2_control control;
+	struct hal_frame_rate frame_rate;
+	struct hal_request_iframe request_iframe;
+	struct hal_bitrate bitrate;
+	struct hal_profile_level profile_level;
+	struct hal_h264_entropy_control h264_entropy_control;
+	struct hal_quantization quantization;
+	struct hal_intra_period intra_period;
+	struct hal_idr_period idr_period;
+	struct hal_operations operations;
+	struct hal_intra_refresh intra_refresh;
+	struct hal_multi_slice_control multi_slice_control;
+	struct hal_h264_db_control h264_db_control;
+	u32 control_idx = 0;
+	u32 property_id = 0;
+	u32 property_val = 0;
+	void *pdata;
+	struct msm_vidc_inst *inst = container_of(ctrl->handler,
+					struct msm_vidc_inst, ctrl_handler);
+
+	control.id = ctrl->id;
+	control.value = ctrl->val;
+
+	switch (control.id) {
+	case V4L2_CID_MPEG_VIDC_VIDEO_FRAME_RATE:
+			property_id =
+				HAL_CONFIG_FRAME_RATE;
+			frame_rate.frame_rate = control.value;
+			frame_rate.buffer_type = HAL_BUFFER_OUTPUT;
+			pdata = &frame_rate;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_IDR_PERIOD:
+		property_id =
+			HAL_CONFIG_VENC_IDR_PERIOD;
+		idr_period.idr_period = control.value;
+			pdata = &idr_period;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_NUM_P_FRAMES:
+		property_id =
+			HAL_CONFIG_VENC_INTRA_PERIOD;
+		intra_period.pframes = control.value;
+		venc_intra_period.pframes = control.value;
+		intra_period.bframes = venc_intra_period.bframes;
+		pdata = &intra_period;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES:
+		property_id =
+			HAL_CONFIG_VENC_INTRA_PERIOD;
+		intra_period.bframes = control.value;
+		venc_intra_period.bframes = control.value;
+		intra_period.pframes = venc_intra_period.pframes;
+		pdata = &intra_period;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_REQUEST_IFRAME:
+		property_id =
+			HAL_CONFIG_VENC_REQUEST_IFRAME;
+		request_iframe.enable = control.value;
+		pdata = &request_iframe;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL:
+		property_id =
+			HAL_PARAM_VENC_RATE_CONTROL;
+		property_val = control.value;
+		pdata = &property_val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_BITRATE:
+		property_id =
+			HAL_CONFIG_VENC_TARGET_BITRATE;
+		bitrate.bit_rate = control.value;
+		pdata = &bitrate;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
+		property_id =
+			HAL_PARAM_VENC_H264_ENTROPY_CONTROL;
+		h264_entropy_control.entropy_mode = control.value;
+		venc_h264_entropy_control.entropy_mode = control.value;
+		h264_entropy_control.cabac_model =
+			venc_h264_entropy_control.cabac_model;
+		pdata = &h264_entropy_control;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL:
+		property_id =
+			HAL_PARAM_VENC_H264_ENTROPY_CONTROL;
+		h264_entropy_control.cabac_model = control.value;
+		venc_h264_entropy_control.cabac_model = control.value;
+		h264_entropy_control.entropy_mode =
+			venc_h264_entropy_control.entropy_mode;
+		pdata = &h264_entropy_control;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+		property_id =
+			HAL_PARAM_PROFILE_LEVEL_CURRENT;
+
+		switch (control.value) {
+		case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE:
+			control.value = HAL_H264_PROFILE_BASELINE;
+			break;
+		case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN:
+			control.value = HAL_H264_PROFILE_MAIN;
+			break;
+		case V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED:
+			control.value = HAL_H264_PROFILE_EXTENDED;
+			break;
+		case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH:
+			control.value = HAL_H264_PROFILE_HIGH;
+			break;
+		case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10:
+			control.value = HAL_H264_PROFILE_HIGH10;
+			break;
+		case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422:
+			control.value = HAL_H264_PROFILE_HIGH422;
+			break;
+		case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE:
+			control.value = HAL_H264_PROFILE_HIGH444;
+			break;
+		default:
+			break;
+			}
+		profile_level.profile = control.value;
+		venc_profile_level.profile = control.value;
+		profile_level.level = venc_profile_level.level;
+		pdata = &profile_level;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+		property_id =
+			HAL_PARAM_PROFILE_LEVEL_CURRENT;
+
+		switch (control.value) {
+		case V4L2_MPEG_VIDEO_H264_LEVEL_1_0:
+			control.value = HAL_H264_LEVEL_1;
+			break;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_1B:
+			control.value = HAL_H264_LEVEL_1b;
+			break;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_1_1:
+			control.value = HAL_H264_LEVEL_11;
+			break;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_1_2:
+			control.value = HAL_H264_LEVEL_12;
+			break;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_1_3:
+			control.value = HAL_H264_LEVEL_13;
+			break;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_2_0:
+			control.value = HAL_H264_LEVEL_2;
+			break;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_2_1:
+			control.value = HAL_H264_LEVEL_21;
+			break;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_2_2:
+			control.value = HAL_H264_LEVEL_22;
+			break;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_3_0:
+			control.value = HAL_H264_LEVEL_3;
+			break;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_3_1:
+			control.value = HAL_H264_LEVEL_31;
+			break;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_3_2:
+			control.value = HAL_H264_LEVEL_32;
+			break;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_4_0:
+			control.value = HAL_H264_LEVEL_4;
+			break;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_4_1:
+			control.value = HAL_H264_LEVEL_41;
+			break;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_4_2:
+			control.value = HAL_H264_LEVEL_42;
+			break;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_5_0:
+			control.value = HAL_H264_LEVEL_3;
+			break;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_5_1:
+			control.value = HAL_H264_LEVEL_51;
+			break;
+		default:
+			break;
+		}
+		profile_level.level = control.value;
+		venc_profile_level.level = control.value;
+		profile_level.profile = venc_profile_level.profile;
+		pdata = &profile_level;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_ROTATION:
+		property_id =
+			HAL_CONFIG_VPE_OPERATIONS;
+		operations.rotate = control.value;
+		pdata = &operations;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP:
+		property_id =
+			HAL_PARAM_VENC_SESSION_QP;
+		quantization.qpi = control.value;
+		venc_quantization.qpi = control.value;
+		quantization.qpp = venc_quantization.qpp;
+		quantization.qpb = venc_quantization.qpb;
+		pdata = &quantization;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP:
+		property_id =
+			HAL_PARAM_VENC_SESSION_QP;
+		quantization.qpp = control.value;
+		venc_quantization.qpp = control.value;
+		quantization.qpi = venc_quantization.qpi;
+		quantization.qpb = venc_quantization.qpb;
+		pdata = &quantization;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP:
+		property_id =
+			HAL_PARAM_VENC_SESSION_QP;
+		quantization.qpb = control.value;
+		venc_quantization.qpb = control.value;
+		quantization.qpi = venc_quantization.qpi;
+		quantization.qpp = venc_quantization.qpp;
+		pdata = &quantization;
+		break;
+	case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
+		property_id =
+			HAL_PARAM_VENC_MULTI_SLICE_CONTROL;
+		multi_slice_control.multi_slice = control.value;
+		venc_multi_slice_control.multi_slice = control.value;
+		multi_slice_control.slice_size =
+			venc_multi_slice_control.slice_size;
+		pdata = &multi_slice_control;
+		break;
+	case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES:
+	case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB:
+		property_id =
+			HAL_PARAM_VENC_MULTI_SLICE_CONTROL;
+		multi_slice_control.multi_slice =
+			venc_multi_slice_control.multi_slice;
+		multi_slice_control.slice_size = control.value;
+		venc_multi_slice_control.slice_size = control.value;
+		pdata = &multi_slice_control;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE:
+		property_id =
+			HAL_PARAM_VENC_INTRA_REFRESH;
+		intra_refresh.mode = control.value;
+		pdata = &intra_refresh;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_AIR_MBS:
+		property_id =
+			HAL_PARAM_VENC_INTRA_REFRESH;
+		intra_refresh.air_mbs = control.value;
+		pdata = &intra_refresh;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_AIR_REF:
+		property_id =
+			HAL_PARAM_VENC_INTRA_REFRESH;
+		intra_refresh.air_ref = control.value;
+		pdata = &intra_refresh;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_CIR_MBS:
+		property_id =
+			HAL_PARAM_VENC_INTRA_REFRESH;
+		intra_refresh.cir_mbs = control.value;
+		pdata = &intra_refresh;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
+		property_id =
+			HAL_PARAM_VENC_H264_DEBLOCK_CONTROL;
+		h264_db_control.mode = control.value;
+		pdata = &h264_db_control;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA:
+		property_id =
+			HAL_PARAM_VENC_H264_DEBLOCK_CONTROL;
+		h264_db_control.slice_alpha_offset = control.value;
+		pdata = &h264_db_control;
+	case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA:
+		property_id =
+			HAL_PARAM_VENC_H264_DEBLOCK_CONTROL;
+		h264_db_control.slicebeta_offset = control.value;
+		pdata = &h264_db_control;
+	default:
+		break;
+	}
+	if (property_id) {
+		pr_debug("Control: HAL property=%d,ctrl_id=%d,ctrl_value=%d\n",
+				property_id,
+				msm_venc_ctrls[control_idx].id,
+				control.value);
+		rc = vidc_hal_session_set_property((void *)inst->session,
+				property_id, pdata);
+	}
+	if (rc)
+		pr_err("Failed to set hal property for framesize\n");
+	return rc;
+}
+static int msm_venc_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+	return 0;
+}
+
+static const struct v4l2_ctrl_ops msm_venc_ctrl_ops = {
+
+	.s_ctrl = msm_venc_op_s_ctrl,
+	.g_volatile_ctrl = msm_venc_op_g_volatile_ctrl,
+};
+
+const struct v4l2_ctrl_ops *msm_venc_get_ctrl_ops(void)
+{
+	return &msm_venc_ctrl_ops;
+}
+
+int msm_venc_inst_init(struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+	if (!inst) {
+		pr_err("Invalid input = %p\n", inst);
+		return -EINVAL;
+	}
+	inst->fmts[CAPTURE_PORT] = &venc_formats[1];
+	inst->fmts[OUTPUT_PORT] = &venc_formats[0];
+	inst->height = DEFAULT_HEIGHT;
+	inst->width = DEFAULT_WIDTH;
+	return rc;
+}
+
+int msm_venc_s_ctrl(struct msm_vidc_inst *inst, struct v4l2_control *ctrl)
+{
+	return v4l2_s_ctrl(NULL, &inst->ctrl_handler, ctrl);
+}
+int msm_venc_g_ctrl(struct msm_vidc_inst *inst, struct v4l2_control *ctrl)
+{
+	return v4l2_g_ctrl(&inst->ctrl_handler, ctrl);
+}
+
+int msm_venc_querycap(struct msm_vidc_inst *inst, struct v4l2_capability *cap)
+{
+	if (!inst || !cap) {
+		pr_err("Invalid input, inst = %p, cap = %p\n", inst, cap);
+		return -EINVAL;
+	}
+	strlcpy(cap->driver, MSM_VIDC_DRV_NAME, sizeof(cap->driver));
+	strlcpy(cap->card, MSM_VENC_DVC_NAME, sizeof(cap->card));
+	cap->bus_info[0] = 0;
+	cap->version = MSM_VIDC_VERSION;
+	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
+						V4L2_CAP_VIDEO_OUTPUT_MPLANE |
+						V4L2_CAP_STREAMING;
+	memset(cap->reserved, 0, sizeof(cap->reserved));
+	return 0;
+}
+
+int msm_venc_enum_fmt(struct msm_vidc_inst *inst, struct v4l2_fmtdesc *f)
+{
+	const struct msm_vidc_format *fmt = NULL;
+	int rc = 0;
+	if (!inst || !f) {
+		pr_err("Invalid input, inst = %p, f = %p\n", inst, f);
+		return -EINVAL;
+	}
+	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		fmt = msm_comm_get_pixel_fmt_index(venc_formats,
+			ARRAY_SIZE(venc_formats), f->index, CAPTURE_PORT);
+	} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		fmt = msm_comm_get_pixel_fmt_index(venc_formats,
+			ARRAY_SIZE(venc_formats), f->index, OUTPUT_PORT);
+		f->flags = V4L2_FMT_FLAG_COMPRESSED;
+	}
+
+	memset(f->reserved, 0 , sizeof(f->reserved));
+	if (fmt) {
+		strlcpy(f->description, fmt->description,
+				sizeof(f->description));
+		f->pixelformat = fmt->fourcc;
+	} else {
+		pr_err("No more formats found\n");
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
+{
+	const struct msm_vidc_format *fmt = NULL;
+	int rc = 0;
+	int i;
+	if (!inst || !f) {
+		pr_err("Invalid input, inst = %p, format = %p\n", inst, f);
+		return -EINVAL;
+	}
+	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		fmt = msm_comm_get_pixel_fmt_fourcc(venc_formats,
+			ARRAY_SIZE(venc_formats), f->fmt.pix_mp.pixelformat,
+			CAPTURE_PORT);
+		if (fmt && fmt->type != CAPTURE_PORT) {
+			pr_err("Format: %d not supported on CAPTURE port\n",
+					f->fmt.pix_mp.pixelformat);
+			rc = -EINVAL;
+			goto exit;
+		}
+	} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		inst->width = f->fmt.pix_mp.width;
+		inst->height = f->fmt.pix_mp.height;
+		fmt = msm_comm_get_pixel_fmt_fourcc(venc_formats,
+			ARRAY_SIZE(venc_formats), f->fmt.pix_mp.pixelformat,
+			OUTPUT_PORT);
+		if (fmt && fmt->type != OUTPUT_PORT) {
+			pr_err("Format: %d not supported on OUTPUT port\n",
+					f->fmt.pix_mp.pixelformat);
+			rc = -EINVAL;
+			goto exit;
+		}
+	}
+
+	if (fmt) {
+		for (i = 0; i < fmt->num_planes; ++i) {
+			f->fmt.pix_mp.plane_fmt[i].sizeimage =
+				fmt->get_frame_size(i, f->fmt.pix_mp.height,
+						f->fmt.pix_mp.width);
+		}
+		inst->fmts[fmt->type] = fmt;
+		if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+			rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE);
+			if (rc) {
+				pr_err("Failed to open instance\n");
+				goto exit;
+			}
+		}
+	} else {
+		pr_err("Buf type not recognized, type = %d\n",
+					f->type);
+		rc = -EINVAL;
+	}
+exit:
+	return rc;
+}
+
+int msm_venc_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
+{
+	const struct msm_vidc_format *fmt = NULL;
+	int rc = 0;
+	int i;
+	if (!inst || !f) {
+		pr_err("Invalid input, inst = %p, format = %p\n", inst, f);
+		return -EINVAL;
+	}
+	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		fmt = inst->fmts[CAPTURE_PORT];
+	else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+		fmt = inst->fmts[OUTPUT_PORT];
+
+	if (fmt) {
+		f->fmt.pix_mp.pixelformat = fmt->fourcc;
+		f->fmt.pix_mp.height = inst->height;
+		f->fmt.pix_mp.width = inst->width;
+		for (i = 0; i < fmt->num_planes; ++i) {
+			f->fmt.pix_mp.plane_fmt[i].sizeimage =
+			fmt->get_frame_size(i, inst->height, inst->width);
+		}
+	} else {
+		pr_err("Buf type not recognized, type = %d\n",
+					f->type);
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+int msm_venc_reqbufs(struct msm_vidc_inst *inst, struct v4l2_requestbuffers *b)
+{
+	struct vb2_queue *q = NULL;
+	int rc = 0;
+	if (!inst || !b) {
+		pr_err("Invalid input, inst = %p, buffer = %p\n", inst, b);
+		return -EINVAL;
+	}
+	q = msm_comm_get_vb2q(inst, b->type);
+	if (!q) {
+		pr_err("Failed to find buffer queue for type = %d\n", b->type);
+		return -EINVAL;
+	}
+
+	rc = vb2_reqbufs(q, b);
+	if (rc)
+		pr_err("Failed to get reqbufs, %d\n", rc);
+	return rc;
+}
+
+int msm_venc_prepare_buf(struct msm_vidc_inst *inst,
+					struct v4l2_buffer *b)
+{
+	int rc = 0;
+	int i;
+	struct vidc_buffer_addr_info buffer_info;
+
+	switch (b->type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		break;
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		for (i = 0; i < b->length; i++) {
+			pr_debug("device_addr = %ld, size = %d\n",
+				b->m.planes[i].m.userptr,
+				b->m.planes[i].length);
+			buffer_info.buffer_size = b->m.planes[i].length;
+			buffer_info.buffer_type = HAL_BUFFER_OUTPUT;
+			buffer_info.num_buffers = 1;
+			buffer_info.align_device_addr =
+				b->m.planes[i].m.userptr;
+			buffer_info.extradata_size = 0;
+			buffer_info.extradata_addr = 0;
+			rc = vidc_hal_session_set_buffers((void *)inst->session,
+					&buffer_info);
+			if (rc)
+				pr_err("vidc_hal_session_set_buffers failed");
+		}
+		break;
+	default:
+		pr_err("Buffer type not recognized: %d\n", b->type);
+		break;
+	}
+	return rc;
+}
+
+int msm_venc_qbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b)
+{
+	struct vb2_queue *q = NULL;
+	int rc = 0;
+	q = msm_comm_get_vb2q(inst, b->type);
+	if (!q) {
+		pr_err("Failed to find buffer queue for type = %d\n", b->type);
+		return -EINVAL;
+	}
+	rc = vb2_qbuf(q, b);
+	if (rc)
+		pr_err("Failed to qbuf, %d\n", rc);
+	return rc;
+}
+
+int msm_venc_dqbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b)
+{
+	struct vb2_queue *q = NULL;
+	int rc = 0;
+	q = msm_comm_get_vb2q(inst, b->type);
+	if (!q) {
+		pr_err("Failed to find buffer queue for type = %d\n", b->type);
+		return -EINVAL;
+	}
+	rc = vb2_dqbuf(q, b, true);
+	if (rc)
+		pr_err("Failed to qbuf, %d\n", rc);
+	return rc;
+}
+
+int msm_venc_streamon(struct msm_vidc_inst *inst, enum v4l2_buf_type i)
+{
+	int rc = 0;
+	struct vb2_queue *q;
+	q = msm_comm_get_vb2q(inst, i);
+	if (!q) {
+		pr_err("Failed to find buffer queue for type = %d\n", i);
+		return -EINVAL;
+	}
+	pr_debug("Calling streamon\n");
+	rc = vb2_streamon(q, i);
+	if (rc)
+		pr_err("streamon failed on port: %d\n", i);
+	return rc;
+}
+
+int msm_venc_streamoff(struct msm_vidc_inst *inst, enum v4l2_buf_type i)
+{
+	int rc = 0;
+	struct vb2_queue *q;
+	q = msm_comm_get_vb2q(inst, i);
+	if (!q) {
+		pr_err("Failed to find buffer queue for type = %d\n", i);
+		return -EINVAL;
+	}
+	pr_debug("Calling streamoff\n");
+	rc = vb2_streamoff(q, i);
+	if (rc)
+		pr_err("streamoff failed on port: %d\n", i);
+	return rc;
+}
+
+int msm_venc_ctrl_init(struct msm_vidc_inst *inst)
+{
+
+	int idx = 0;
+	struct v4l2_ctrl_config ctrl_cfg;
+	int ret_val = 0;
+	ret_val = v4l2_ctrl_handler_init(&inst->ctrl_handler, NUM_CTRLS);
+	if (ret_val) {
+		pr_err("CTRL ERR: Control handler init failed, %d\n",
+			inst->ctrl_handler.error);
+		return ret_val;
+	}
+
+	for (; idx < NUM_CTRLS; idx++) {
+		if (IS_PRIV_CTRL(msm_venc_ctrls[idx].id)) {
+			ctrl_cfg.def = msm_venc_ctrls[idx].default_value;
+			ctrl_cfg.flags = 0;
+			ctrl_cfg.id = msm_venc_ctrls[idx].id;
+			ctrl_cfg.max = msm_venc_ctrls[idx].maximum;
+			ctrl_cfg.min = msm_venc_ctrls[idx].minimum;
+			ctrl_cfg.menu_skip_mask =
+				msm_venc_ctrls[idx].menu_skip_mask;
+			ctrl_cfg.name = msm_venc_ctrls[idx].name;
+			ctrl_cfg.ops = &msm_venc_ctrl_ops;
+			ctrl_cfg.step = msm_venc_ctrls[idx].step;
+			ctrl_cfg.type = msm_venc_ctrls[idx].type;
+			ctrl_cfg.qmenu = msm_venc_ctrls[idx].qmenu;
+			v4l2_ctrl_new_custom(&inst->ctrl_handler,
+				&ctrl_cfg, NULL);
+		} else {
+			if (msm_venc_ctrls[idx].type == V4L2_CTRL_TYPE_MENU) {
+				v4l2_ctrl_new_std_menu(&inst->ctrl_handler,
+					&msm_venc_ctrl_ops,
+					msm_venc_ctrls[idx].id,
+					msm_venc_ctrls[idx].maximum,
+					msm_venc_ctrls[idx].menu_skip_mask,
+					msm_venc_ctrls[idx].default_value);
+			} else {
+				v4l2_ctrl_new_std(&inst->ctrl_handler,
+					&msm_venc_ctrl_ops,
+					msm_venc_ctrls[idx].id,
+					msm_venc_ctrls[idx].minimum,
+					msm_venc_ctrls[idx].maximum,
+					msm_venc_ctrls[idx].step,
+					msm_venc_ctrls[idx].default_value);
+			}
+		}
+	}
+	ret_val = inst->ctrl_handler.error;
+	if (ret_val)
+		pr_err("CTRL ERR: Error adding ctrls to ctrl handle, %d\n",
+				inst->ctrl_handler.error);
+	return ret_val;
+}
diff --git a/drivers/media/video/msm_vidc/msm_venc.h b/drivers/media/video/msm_vidc/msm_venc.h
new file mode 100644
index 0000000..4a156dd
--- /dev/null
+++ b/drivers/media/video/msm_vidc/msm_venc.h
@@ -0,0 +1,35 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef _MSM_VENC_H_
+#define _MSM_VENC_H_
+
+#include <media/msm_vidc.h>
+#include "msm_vidc_internal.h"
+
+int msm_venc_inst_init(struct msm_vidc_inst *inst);
+int msm_venc_ctrl_init(struct msm_vidc_inst *inst);
+int msm_venc_querycap(void *instance, struct v4l2_capability *cap);
+int msm_venc_enum_fmt(void *instance, struct v4l2_fmtdesc *f);
+int msm_venc_s_fmt(void *instance, struct v4l2_format *f);
+int msm_venc_g_fmt(void *instance, struct v4l2_format *f);
+int msm_venc_s_ctrl(void *instance, struct v4l2_control *a);
+int msm_venc_g_ctrl(void *instance, struct v4l2_control *a);
+int msm_venc_reqbufs(void *instance, struct v4l2_requestbuffers *b);
+int msm_venc_prepare_buf(struct msm_vidc_inst *inst, struct v4l2_buffer *b);
+int msm_venc_qbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b);
+int msm_venc_dqbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b);
+int msm_venc_streamon(struct msm_vidc_inst *inst, enum v4l2_buf_type i);
+int msm_venc_streamoff(struct msm_vidc_inst *inst, enum v4l2_buf_type i);
+struct vb2_ops *msm_venc_get_vb2q_ops(void);
+
+#endif
diff --git a/drivers/media/video/msm_vidc/msm_vidc.c b/drivers/media/video/msm_vidc/msm_vidc.c
new file mode 100644
index 0000000..a11f817
--- /dev/null
+++ b/drivers/media/video/msm_vidc/msm_vidc.c
@@ -0,0 +1,348 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/slab.h>
+#include <media/msm_vidc.h>
+#include "msm_vidc_internal.h"
+#include "msm_vdec.h"
+#include "msm_venc.h"
+#include "msm_vidc_common.h"
+#include "msm_smem.h"
+
+int msm_vidc_poll(void *instance, struct file *filp,
+		struct poll_table_struct *wait)
+{
+	int rc = 0;
+	struct msm_vidc_inst *inst = instance;
+	struct vb2_queue *outq = &inst->vb2_bufq[OUTPUT_PORT];
+	struct vb2_queue *capq = &inst->vb2_bufq[CAPTURE_PORT];
+	struct vb2_buffer *out_vb = NULL;
+	struct vb2_buffer *cap_vb = NULL;
+	unsigned long flags;
+	poll_wait(filp, &inst->event_handler.wait, wait);
+	if (v4l2_event_pending(&inst->event_handler))
+		return POLLPRI;
+	if (!outq->streaming && !capq->streaming) {
+		pr_err("Returning POLLERR from here: %d, %d\n",
+			outq->streaming, capq->streaming);
+		return POLLERR;
+	}
+	poll_wait(filp, &inst->event_handler.wait, wait);
+	if (v4l2_event_pending(&inst->event_handler))
+		return POLLPRI;
+	poll_wait(filp, &capq->done_wq, wait);
+	poll_wait(filp, &outq->done_wq, wait);
+	spin_lock_irqsave(&capq->done_lock, flags);
+	if (!list_empty(&capq->done_list))
+		cap_vb = list_first_entry(&capq->done_list, struct vb2_buffer,
+								done_entry);
+	if (cap_vb && (cap_vb->state == VB2_BUF_STATE_DONE
+				|| cap_vb->state == VB2_BUF_STATE_ERROR))
+		rc |= POLLIN | POLLRDNORM;
+	spin_unlock_irqrestore(&capq->done_lock, flags);
+	spin_lock_irqsave(&outq->done_lock, flags);
+	if (!list_empty(&outq->done_list))
+		out_vb = list_first_entry(&outq->done_list, struct vb2_buffer,
+								done_entry);
+	if (out_vb && (out_vb->state == VB2_BUF_STATE_DONE
+				|| out_vb->state == VB2_BUF_STATE_ERROR))
+		rc |= POLLOUT | POLLWRNORM;
+	spin_unlock_irqrestore(&outq->done_lock, flags);
+	return rc;
+}
+
+int msm_vidc_querycap(void *instance, struct v4l2_capability *cap)
+{
+	struct msm_vidc_inst *inst = instance;
+	if (inst->session_type == MSM_VIDC_DECODER)
+		return msm_vdec_querycap(instance, cap);
+	else if (inst->session_type == MSM_VIDC_ENCODER)
+		return msm_venc_querycap(instance, cap);
+	return -EINVAL;
+}
+int msm_vidc_enum_fmt(void *instance, struct v4l2_fmtdesc *f)
+{
+	struct msm_vidc_inst *inst = instance;
+	if (inst->session_type == MSM_VIDC_DECODER)
+		return msm_vdec_enum_fmt(instance, f);
+	else if (inst->session_type == MSM_VIDC_ENCODER)
+		return msm_venc_enum_fmt(instance, f);
+	return -EINVAL;
+}
+int msm_vidc_s_fmt(void *instance, struct v4l2_format *f)
+{
+	struct msm_vidc_inst *inst = instance;
+	if (inst->session_type == MSM_VIDC_DECODER)
+		return msm_vdec_s_fmt(instance, f);
+	if (inst->session_type == MSM_VIDC_ENCODER)
+		return msm_venc_s_fmt(instance, f);
+	return -EINVAL;
+}
+int msm_vidc_g_fmt(void *instance, struct v4l2_format *f)
+{
+	struct msm_vidc_inst *inst = instance;
+	if (inst->session_type == MSM_VIDC_DECODER)
+		return msm_vdec_g_fmt(instance, f);
+	else if (inst->session_type == MSM_VIDC_ENCODER)
+		return msm_venc_g_fmt(instance, f);
+	return -EINVAL;
+}
+int msm_vidc_s_ctrl(void *instance, struct v4l2_control *control)
+{
+	struct msm_vidc_inst *inst = instance;
+	if (inst->session_type == MSM_VIDC_DECODER)
+		return msm_vdec_s_ctrl(instance, control);
+	if (inst->session_type == MSM_VIDC_ENCODER)
+		return msm_venc_s_ctrl(instance, control);
+	return -EINVAL;
+}
+int msm_vidc_g_ctrl(void *instance, struct v4l2_control *control)
+{
+	struct msm_vidc_inst *inst = instance;
+	if (inst->session_type == MSM_VIDC_DECODER)
+		return msm_vdec_g_ctrl(instance, control);
+	if (inst->session_type == MSM_VIDC_ENCODER)
+		return msm_venc_g_ctrl(instance, control);
+	return -EINVAL;
+}
+int msm_vidc_reqbufs(void *instance, struct v4l2_requestbuffers *b)
+{
+	struct msm_vidc_inst *inst = instance;
+	if (inst->session_type == MSM_VIDC_DECODER)
+		return msm_vdec_reqbufs(instance, b);
+	if (inst->session_type == MSM_VIDC_ENCODER)
+		return msm_venc_reqbufs(instance, b);
+	return -EINVAL;
+}
+
+int msm_vidc_prepare_buf(void *instance, struct v4l2_buffer *b)
+{
+	struct msm_vidc_inst *inst = instance;
+	if (inst->session_type == MSM_VIDC_DECODER)
+		return msm_vdec_prepare_buf(instance, b);
+	if (inst->session_type == MSM_VIDC_ENCODER)
+		return msm_venc_prepare_buf(instance, b);
+	return -EINVAL;
+}
+
+int msm_vidc_release_buf(void *instance, struct v4l2_buffer *b)
+{
+	struct msm_vidc_inst *inst = instance;
+	if (inst->session_type == MSM_VIDC_DECODER)
+		return msm_vdec_release_buf(instance, b);
+	return -EINVAL;
+}
+
+int msm_vidc_qbuf(void *instance, struct v4l2_buffer *b)
+{
+	struct msm_vidc_inst *inst = instance;
+	if (inst->session_type == MSM_VIDC_DECODER)
+		return msm_vdec_qbuf(instance, b);
+	if (inst->session_type == MSM_VIDC_ENCODER)
+		return msm_venc_qbuf(instance, b);
+	return -EINVAL;
+}
+
+int msm_vidc_dqbuf(void *instance, struct v4l2_buffer *b)
+{
+	struct msm_vidc_inst *inst = instance;
+	if (inst->session_type == MSM_VIDC_DECODER)
+		return msm_vdec_dqbuf(instance, b);
+	if (inst->session_type == MSM_VIDC_ENCODER)
+		return msm_venc_dqbuf(instance, b);
+	return -EINVAL;
+}
+
+int msm_vidc_streamon(void *instance, enum v4l2_buf_type i)
+{
+	struct msm_vidc_inst *inst = instance;
+	if (inst->session_type == MSM_VIDC_DECODER)
+		return msm_vdec_streamon(instance, i);
+	if (inst->session_type == MSM_VIDC_ENCODER)
+		return msm_venc_streamon(instance, i);
+	return -EINVAL;
+}
+
+int msm_vidc_streamoff(void *instance, enum v4l2_buf_type i)
+{
+	struct msm_vidc_inst *inst = instance;
+	if (inst->session_type == MSM_VIDC_DECODER)
+		return msm_vdec_streamoff(instance, i);
+	if (inst->session_type == MSM_VIDC_ENCODER)
+		return msm_venc_streamoff(instance, i);
+	return -EINVAL;
+}
+
+void *vidc_get_userptr(void *alloc_ctx, unsigned long vaddr,
+				unsigned long size, int write)
+{
+	return NULL;
+}
+
+void vidc_put_userptr(void *buf_priv)
+{
+}
+
+static const struct vb2_mem_ops msm_vidc_vb2_mem_ops = {
+	.get_userptr = vidc_get_userptr,
+	.put_userptr = vidc_put_userptr,
+};
+
+static inline int vb2_bufq_init(struct msm_vidc_inst *inst,
+		enum v4l2_buf_type type, enum session_type sess)
+{
+	struct vb2_queue *q = NULL;
+	if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		q = &inst->vb2_bufq[CAPTURE_PORT];
+	} else if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		q = &inst->vb2_bufq[OUTPUT_PORT];
+	} else {
+		pr_err("buf_type = %d not recognised\n", type);
+		return -EINVAL;
+	}
+	q->type = type;
+	q->io_modes = VB2_MMAP | VB2_USERPTR;
+	q->io_flags = 0;
+	if (sess == MSM_VIDC_DECODER)
+		q->ops = msm_vdec_get_vb2q_ops();
+	else if (sess == MSM_VIDC_ENCODER)
+		q->ops = msm_venc_get_vb2q_ops();
+	q->mem_ops = &msm_vidc_vb2_mem_ops;
+	q->drv_priv = inst;
+	return vb2_queue_init(q);
+}
+
+int msm_vidc_open(void *vidc_inst, int core_id, int session_type)
+{
+	struct msm_vidc_inst *inst = (struct msm_vidc_inst *)vidc_inst;
+	struct msm_vidc_core *core = NULL;
+	unsigned long flags;
+	int rc = 0;
+	int i = 0;
+	if (core_id >= MSM_VIDC_CORES_MAX ||
+			session_type >= MSM_VIDC_MAX_DEVICES) {
+		pr_err("Invalid input, core_id = %d, session = %d\n",
+			core_id, session_type);
+		goto err_invalid_core;
+	}
+	core = get_vidc_core(core_id);
+	if (!core) {
+		pr_err("Failed to find core for core_id = %d\n", core_id);
+		goto err_invalid_core;
+	}
+
+	mutex_init(&inst->sync_lock);
+	spin_lock_init(&inst->lock);
+	inst->session_type = session_type;
+	INIT_LIST_HEAD(&inst->pendingq);
+	INIT_LIST_HEAD(&inst->internalbufs);
+	inst->state = MSM_VIDC_CORE_UNINIT_DONE;
+	inst->core = core;
+	for (i = SESSION_MSG_INDEX(SESSION_MSG_START);
+		i <= SESSION_MSG_INDEX(SESSION_MSG_END); i++) {
+		init_completion(&inst->completions[i]);
+	}
+	inst->mem_client = msm_smem_new_client(SMEM_ION);
+	if (!inst->mem_client) {
+		pr_err("Failed to create memory client\n");
+		goto fail_mem_client;
+	}
+	if (session_type == MSM_VIDC_DECODER) {
+		msm_vdec_inst_init(inst);
+		msm_vdec_ctrl_init(inst);
+	} else if (session_type == MSM_VIDC_ENCODER) {
+		msm_venc_inst_init(inst);
+		msm_venc_ctrl_init(inst);
+	}
+	rc = vb2_bufq_init(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+			session_type);
+	if (rc) {
+		pr_err("Failed to initialize vb2 queue on capture port\n");
+		goto fail_init;
+	}
+	rc = vb2_bufq_init(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+			session_type);
+	if (rc) {
+		pr_err("Failed to initialize vb2 queue on capture port\n");
+		goto fail_init;
+	}
+	rc = msm_comm_try_state(inst, MSM_VIDC_CORE_INIT);
+	if (rc) {
+		pr_err("Failed to move video instance to init state\n");
+		goto fail_init;
+	}
+	spin_lock_irqsave(&core->lock, flags);
+	list_add_tail(&inst->list, &core->instances);
+	spin_unlock_irqrestore(&core->lock, flags);
+	return rc;
+fail_init:
+	msm_smem_delete_client(inst->mem_client);
+fail_mem_client:
+	kfree(inst);
+	inst = NULL;
+err_invalid_core:
+	return rc;
+}
+
+static void cleanup_instance(struct msm_vidc_inst *inst)
+{
+	unsigned long flags;
+	struct list_head *ptr, *next;
+	struct vb2_buf_entry *entry;
+	struct internal_buf *buf;
+	if (inst) {
+		spin_lock_irqsave(&inst->lock, flags);
+		if (!list_empty(&inst->pendingq)) {
+			list_for_each_safe(ptr, next, &inst->pendingq) {
+				entry = list_entry(ptr, struct vb2_buf_entry,
+						list);
+				list_del(&entry->list);
+				kfree(entry);
+			}
+		}
+		if (!list_empty(&inst->internalbufs)) {
+			list_for_each_safe(ptr, next, &inst->internalbufs) {
+				buf = list_entry(ptr, struct internal_buf,
+						list);
+				list_del(&buf->list);
+				msm_smem_free(inst->mem_client, buf->handle);
+				kfree(buf);
+			}
+		}
+		spin_unlock_irqrestore(&inst->lock, flags);
+		msm_smem_delete_client(inst->mem_client);
+	}
+}
+
+int msm_vidc_close(void *instance)
+{
+	struct msm_vidc_inst *inst = instance;
+	struct msm_vidc_inst *temp;
+	struct msm_vidc_core *core;
+	struct list_head *ptr, *next;
+	int rc = 0;
+	core = inst->core;
+	mutex_lock(&core->sync_lock);
+	list_for_each_safe(ptr, next, &core->instances) {
+		temp = list_entry(ptr, struct msm_vidc_inst, list);
+		if (temp == inst)
+			list_del(&inst->list);
+	}
+	mutex_unlock(&core->sync_lock);
+	rc = msm_comm_try_state(inst, MSM_VIDC_CORE_UNINIT);
+	if (rc)
+		pr_err("Failed to move video instance to uninit state\n");
+	cleanup_instance(inst);
+	pr_debug("Closed the instance\n");
+	return 0;
+}
diff --git a/drivers/media/video/msm_vidc/msm_vidc_common.c b/drivers/media/video/msm_vidc/msm_vidc_common.c
new file mode 100644
index 0000000..6b06943
--- /dev/null
+++ b/drivers/media/video/msm_vidc/msm_vidc_common.c
@@ -0,0 +1,1045 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include "msm_vidc_common.h"
+#include "vidc_hal_api.h"
+#include "msm_smem.h"
+
+#define HW_RESPONSE_TIMEOUT 5000
+
+#define IS_ALREADY_IN_STATE(__p, __d) ({\
+	int __rc = (__p >= __d);\
+	__rc; \
+})
+
+struct msm_vidc_core *get_vidc_core(int core_id)
+{
+	struct msm_vidc_core *core;
+	int found = 0;
+	unsigned long flags;
+	if (core_id > MSM_VIDC_CORES_MAX) {
+		pr_err("Core id = %d is greater than max = %d\n",
+			core_id, MSM_VIDC_CORES_MAX);
+		return NULL;
+	}
+	spin_lock_irqsave(&vidc_driver->lock, flags);
+	list_for_each_entry(core, &vidc_driver->cores, list) {
+		if (core && core->id == core_id)
+			found = 1;
+			break;
+	}
+	spin_unlock_irqrestore(&vidc_driver->lock, flags);
+	if (found)
+		return core;
+	return NULL;
+}
+
+const struct msm_vidc_format *msm_comm_get_pixel_fmt_index(
+	const struct msm_vidc_format fmt[], int size, int index, int fmt_type)
+{
+	int i, k = 0;
+	if (!fmt || index < 0) {
+		pr_err("Invalid inputs, fmt = %p, index = %d\n",
+						fmt, index);
+		return NULL;
+	}
+	for (i = 0; i < size; i++) {
+		if (fmt[i].type != fmt_type)
+			continue;
+		if (k == index)
+			break;
+		k++;
+	}
+	if (i == size) {
+		pr_err("Format not found\n");
+		return NULL;
+	}
+	return &fmt[i];
+}
+const struct msm_vidc_format *msm_comm_get_pixel_fmt_fourcc(
+	const struct msm_vidc_format fmt[], int size, int fourcc, int fmt_type)
+{
+	int i;
+	if (!fmt) {
+		pr_err("Invalid inputs, fmt = %p\n", fmt);
+		return NULL;
+	}
+	for (i = 0; i < size; i++) {
+		if (fmt[i].fourcc == fourcc)
+				break;
+	}
+	if (i == size) {
+		pr_err("Format not found\n");
+		return NULL;
+	}
+	return &fmt[i];
+}
+
+struct vb2_queue *msm_comm_get_vb2q(
+		struct msm_vidc_inst *inst,	enum v4l2_buf_type type)
+{
+	if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		return &inst->vb2_bufq[CAPTURE_PORT];
+	if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+		return &inst->vb2_bufq[OUTPUT_PORT];
+	return NULL;
+}
+
+static void handle_sys_init_done(enum command_response cmd, void *data)
+{
+	struct msm_vidc_cb_cmd_done *response = data;
+	struct msm_vidc_core *core;
+	struct vidc_hal_sys_init_done *sys_init_msg;
+	int index = SYS_MSG_INDEX(cmd);
+	if (!response) {
+		pr_err("Failed to get valid response for sys init\n");
+		return;
+	}
+	core = get_vidc_core(response->device_id);
+	if (!core) {
+		pr_err("Wrong device_id received\n");
+		return;
+	}
+	pr_debug("index = %d\n", index);
+	pr_debug("ptr = %p\n", &(core->completions[index]));
+	complete(&(core->completions[index]));
+	sys_init_msg = response->data;
+	if (!sys_init_msg) {
+		pr_err("sys_init_done message not proper\n");
+		return;
+	}
+}
+
+static inline void change_inst_state(struct msm_vidc_inst *inst,
+	enum instance_state state)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&inst->lock, flags);
+	pr_debug("Moved inst: %p from state: %d to state: %d\n",
+		   inst, inst->state, state);
+	inst->state = state;
+	spin_unlock_irqrestore(&inst->lock, flags);
+}
+
+static int signal_session_msg_receipt(enum command_response cmd,
+		struct msm_vidc_inst *inst)
+{
+	if (!inst) {
+		pr_err("Invalid(%p) instance id\n", inst);
+		return -EINVAL;
+	}
+	complete(&inst->completions[SESSION_MSG_INDEX(cmd)]);
+	return 0;
+}
+
+static int wait_for_sess_signal_receipt(struct msm_vidc_inst *inst,
+	enum command_response cmd)
+{
+	int rc = 0;
+	rc = wait_for_completion_interruptible_timeout(
+		&inst->completions[SESSION_MSG_INDEX(cmd)],
+		msecs_to_jiffies(HW_RESPONSE_TIMEOUT));
+	if (!rc) {
+		pr_err("Wait interrupted or timeout: %d\n", rc);
+		rc = -EIO;
+	} else {
+		rc = 0;
+	}
+	return rc;
+}
+
+static int wait_for_state(struct msm_vidc_inst *inst,
+	enum instance_state flipped_state,
+	enum instance_state desired_state,
+	enum command_response hal_cmd)
+{
+	int rc = 0;
+	if (IS_ALREADY_IN_STATE(flipped_state, desired_state)) {
+		pr_err("inst: %p is already in state: %d\n", inst, inst->state);
+		goto err_same_state;
+	}
+	pr_debug("Waiting for hal_cmd: %d\n", hal_cmd);
+	rc = wait_for_sess_signal_receipt(inst, hal_cmd);
+	if (!rc)
+		change_inst_state(inst, desired_state);
+err_same_state:
+	return rc;
+}
+
+static void handle_session_init_done(enum command_response cmd, void *data)
+{
+	struct msm_vidc_cb_cmd_done *response = data;
+	struct msm_vidc_inst *inst;
+	struct video_device *vdev;
+	struct v4l2_event dqevent;
+	struct msm_vidc_core *core;
+	if (response) {
+		inst = (struct msm_vidc_inst *)response->session_id;
+		signal_session_msg_receipt(cmd, inst);
+		core = inst->core;
+		if (inst->session_type == MSM_VIDC_ENCODER)
+			vdev = &core->vdev[MSM_VIDC_ENCODER].vdev;
+		else
+			vdev = &core->vdev[MSM_VIDC_DECODER].vdev;
+		dqevent.type = V4L2_EVENT_PRIVATE_START + V4L2_EVENT_VIDC_BASE;
+		dqevent.u.data[0] = (uint8_t)MSM_VIDC_OPEN_DONE;
+		v4l2_event_queue(vdev, &dqevent);
+		return;
+	} else {
+		pr_err("Failed to get valid response for session init\n");
+	}
+}
+
+static void handle_event_change(enum command_response cmd, void *data)
+{
+	struct msm_vidc_cb_cmd_done *response = data;
+	struct msm_vidc_inst *inst;
+	struct video_device *vdev;
+	struct v4l2_event dqevent;
+	struct msm_vidc_cb_event *event_notify;
+	struct msm_vidc_core *core;
+	if (response) {
+		inst = (struct msm_vidc_inst *)response->session_id;
+		core = inst->core;
+		if (inst->session_type == MSM_VIDC_ENCODER)
+			vdev = &core->vdev[MSM_VIDC_ENCODER].vdev;
+		else
+			vdev = &core->vdev[MSM_VIDC_DECODER].vdev;
+		dqevent.type = V4L2_EVENT_PRIVATE_START + V4L2_EVENT_VIDC_BASE;
+		dqevent.u.data[0] = (uint8_t)MSM_VIDC_DECODER_EVENT_CHANGE;
+		event_notify = (struct msm_vidc_cb_event *) response->data;
+		inst->reconfig_height = event_notify->height;
+		inst->reconfig_width = event_notify->width;
+		inst->in_reconfig = true;
+		v4l2_event_queue(vdev, &dqevent);
+		return;
+	} else {
+		pr_err("Failed to get valid response for event_change\n");
+	}
+}
+
+static void handle_session_prop_info(enum command_response cmd, void *data)
+{
+	struct msm_vidc_cb_cmd_done *response = data;
+	struct msm_vidc_inst *inst;
+	unsigned long flags;
+	if (!response || !response->data) {
+		pr_err("Failed to get valid response for prop info\n");
+		return;
+	}
+	inst = (struct msm_vidc_inst *)response->session_id;
+	spin_lock_irqsave(&inst->lock, flags);
+	memcpy(&inst->buff_req, response->data,
+			sizeof(struct buffer_requirements));
+	spin_unlock_irqrestore(&inst->lock, flags);
+	signal_session_msg_receipt(cmd, inst);
+}
+
+static void handle_load_resource_done(enum command_response cmd, void *data)
+{
+	struct msm_vidc_cb_cmd_done *response = data;
+	struct msm_vidc_inst *inst;
+	if (response)
+		inst = (struct msm_vidc_inst *)response->session_id;
+	else
+		pr_err("Failed to get valid response for load resource\n");
+}
+
+static void handle_start_done(enum command_response cmd, void *data)
+{
+	struct msm_vidc_cb_cmd_done *response = data;
+	struct msm_vidc_inst *inst;
+	struct video_device *vdev;
+	struct v4l2_event dqevent;
+	struct msm_vidc_core *core;
+	if (response) {
+		inst = (struct msm_vidc_inst *)response->session_id;
+		signal_session_msg_receipt(cmd, inst);
+		core = inst->core;
+		if (inst->session_type == MSM_VIDC_ENCODER)
+			vdev = &core->vdev[MSM_VIDC_ENCODER].vdev;
+		else
+			vdev = &core->vdev[MSM_VIDC_DECODER].vdev;
+		dqevent.type = V4L2_EVENT_PRIVATE_START + V4L2_EVENT_VIDC_BASE;
+		dqevent.u.data[0] = (uint8_t)MSM_VIDC_START_DONE;
+		v4l2_event_queue(vdev, &dqevent);
+	} else {
+		pr_err("Failed to get valid response for start\n");
+	}
+}
+
+static void handle_stop_done(enum command_response cmd, void *data)
+{
+	struct msm_vidc_cb_cmd_done *response = data;
+	struct msm_vidc_inst *inst;
+	struct video_device *vdev;
+	struct v4l2_event dqevent;
+	struct msm_vidc_core *core;
+	if (response) {
+		inst = (struct msm_vidc_inst *)response->session_id;
+		signal_session_msg_receipt(cmd, inst);
+		core = inst->core;
+		if (inst->session_type == MSM_VIDC_ENCODER)
+			vdev = &core->vdev[MSM_VIDC_ENCODER].vdev;
+		else
+			vdev = &core->vdev[MSM_VIDC_DECODER].vdev;
+		dqevent.type = V4L2_EVENT_PRIVATE_START + V4L2_EVENT_VIDC_BASE;
+		dqevent.u.data[0] = (uint8_t)MSM_VIDC_STOP_DONE;
+		v4l2_event_queue(vdev, &dqevent);
+	} else {
+		pr_err("Failed to get valid response for stop\n");
+	}
+}
+
+static void handle_release_res_done(enum command_response cmd, void *data)
+{
+	struct msm_vidc_cb_cmd_done *response = data;
+	struct msm_vidc_inst *inst;
+	if (response) {
+		inst = (struct msm_vidc_inst *)response->session_id;
+		signal_session_msg_receipt(cmd, inst);
+	} else {
+		pr_err("Failed to get valid response for release resource\n");
+	}
+}
+
+static void handle_session_flush(enum command_response cmd, void *data)
+{
+	struct msm_vidc_cb_cmd_done *response = data;
+	struct msm_vidc_inst *inst;
+	struct video_device *vdev;
+	struct v4l2_event dqevent;
+	struct msm_vidc_core *core;
+	if (response) {
+		inst = (struct msm_vidc_inst *)response->session_id;
+		core = inst->core;
+		if (inst->session_type == MSM_VIDC_ENCODER)
+			vdev = &core->vdev[MSM_VIDC_ENCODER].vdev;
+		else
+			vdev = &core->vdev[MSM_VIDC_DECODER].vdev;
+		dqevent.type = V4L2_EVENT_PRIVATE_START + V4L2_EVENT_VIDC_BASE;
+		dqevent.u.data[0] = (uint8_t)MSM_VIDC_DECODER_FLUSH_DONE;
+		v4l2_event_queue(vdev, &dqevent);
+	} else {
+		pr_err("Failed to get valid response for flush\n");
+	}
+}
+
+
+static void handle_session_close(enum command_response cmd, void *data)
+{
+	struct msm_vidc_cb_cmd_done *response = data;
+	struct msm_vidc_inst *inst;
+	struct video_device *vdev;
+	struct v4l2_event dqevent;
+	struct msm_vidc_core *core;
+	if (response) {
+		inst = (struct msm_vidc_inst *)response->session_id;
+		signal_session_msg_receipt(cmd, inst);
+		core = inst->core;
+		if (inst->session_type == MSM_VIDC_ENCODER)
+			vdev = &core->vdev[MSM_VIDC_ENCODER].vdev;
+		else
+			vdev = &core->vdev[MSM_VIDC_DECODER].vdev;
+		dqevent.type = V4L2_EVENT_PRIVATE_START + V4L2_EVENT_VIDC_BASE;
+		dqevent.u.data[0] = (uint8_t)MSM_VIDC_CLOSE_DONE;
+		v4l2_event_queue(vdev, &dqevent);
+	} else {
+		pr_err("Failed to get valid response for session close\n");
+	}
+}
+
+static struct vb2_buffer *get_vb_from_device_addr(struct vb2_queue *q,
+		u32 dev_addr)
+{
+	struct vb2_buffer *vb = NULL;
+	int found = 0;
+	if (!q) {
+		pr_err("Invalid parameter\n");
+		return NULL;
+	}
+	list_for_each_entry(vb, &q->queued_list, queued_entry) {
+		if (vb->v4l2_planes[0].m.userptr == dev_addr) {
+			found = 1;
+			break;
+		}
+	}
+	if (!found) {
+		pr_err("Failed to find the buffer in queued list: %d, %d\n",
+			dev_addr, q->type);
+		vb = NULL;
+	}
+	return vb;
+}
+
+static void handle_ebd(enum command_response cmd, void *data)
+{
+	struct msm_vidc_cb_data_done *response = data;
+	struct vb2_buffer *vb;
+	if (!response) {
+		pr_err("Invalid response from vidc_hal\n");
+		return;
+	}
+	vb = response->clnt_data;
+	if (vb)
+		vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+}
+
+static void handle_fbd(enum command_response cmd, void *data)
+{
+	struct msm_vidc_cb_data_done *response = data;
+	struct msm_vidc_inst *inst;
+	struct vb2_buffer *vb;
+	struct vidc_hal_fbd *fill_buf_done;
+	if (!response) {
+		pr_err("Invalid response from vidc_hal\n");
+		return;
+	}
+	inst = (struct msm_vidc_inst *)response->session_id;
+	fill_buf_done = (struct vidc_hal_fbd *)&response->output_done;
+	vb = get_vb_from_device_addr(&inst->vb2_bufq[CAPTURE_PORT],
+		(u32)fill_buf_done->packet_buffer1);
+	if (vb) {
+		vb->v4l2_planes[0].bytesused = fill_buf_done->filled_len1;
+		pr_debug("Filled length = %d\n", vb->v4l2_planes[0].bytesused);
+		if (fill_buf_done->flags1 & HAL_BUFFERFLAG_EOS)
+			vb->v4l2_buf.flags |= V4L2_BUF_FLAG_EOS;
+		vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+	} else {
+		/*
+		 * FIXME:
+		 * Special handling for EOS case: if we sent a 0 length input
+		 * buf with EOS set, Venus doesn't return a valid output buffer.
+		 * So pick up a random buffer that's with us, and send it to
+		 * v4l2 client with EOS flag set.
+		 *
+		 * This would normally be OK unless client decides to send
+		 * frames even after EOS.
+		 *
+		 * This should be fixed in upcoming versions of firmware
+		 */
+		if (fill_buf_done->flags1 & HAL_BUFFERFLAG_EOS
+			&& fill_buf_done->filled_len1 == 0) {
+			struct vb2_queue *q = &inst->vb2_bufq[CAPTURE_PORT];
+
+			if (!list_empty(&q->queued_list)) {
+				vb = list_first_entry(&q->queued_list,
+					struct vb2_buffer, queued_entry);
+				vb->v4l2_planes[0].bytesused = 0;
+				vb->v4l2_buf.flags |= V4L2_BUF_FLAG_EOS;
+				vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+			}
+
+		}
+
+	}
+}
+
+void handle_cmd_response(enum command_response cmd, void *data)
+{
+	pr_debug("Command response = %d\n", cmd);
+	switch (cmd) {
+	case SYS_INIT_DONE:
+		handle_sys_init_done(cmd, data);
+		break;
+	case SESSION_INIT_DONE:
+		handle_session_init_done(cmd, data);
+		break;
+	case SESSION_PROPERTY_INFO:
+		handle_session_prop_info(cmd, data);
+		break;
+	case SESSION_LOAD_RESOURCE_DONE:
+		handle_load_resource_done(cmd, data);
+		break;
+	case SESSION_START_DONE:
+		handle_start_done(cmd, data);
+		break;
+	case SESSION_ETB_DONE:
+		handle_ebd(cmd, data);
+		break;
+	case SESSION_FTB_DONE:
+		handle_fbd(cmd, data);
+		break;
+	case SESSION_STOP_DONE:
+		handle_stop_done(cmd, data);
+		break;
+	case SESSION_RELEASE_RESOURCE_DONE:
+		handle_release_res_done(cmd, data);
+		break;
+	case SESSION_END_DONE:
+		handle_session_close(cmd, data);
+		break;
+	case VIDC_EVENT_CHANGE:
+		handle_event_change(cmd, data);
+		break;
+	case SESSION_FLUSH_DONE:
+		handle_session_flush(cmd, data);
+		break;
+	default:
+		pr_err("response unhandled\n");
+		break;
+	}
+}
+
+static int msm_comm_init_core_done(struct msm_vidc_inst *inst)
+{
+	struct msm_vidc_core *core = inst->core;
+	unsigned long flags;
+	int rc = 0;
+	mutex_lock(&core->sync_lock);
+	if (core->state >= VIDC_CORE_INIT_DONE) {
+		pr_err("Video core: %d is already in state: %d\n",
+				core->id, core->state);
+		goto core_already_inited;
+	}
+	pr_debug("Waiting for SYS_INIT_DONE\n");
+	rc = wait_for_completion_timeout(
+		&core->completions[SYS_MSG_INDEX(SYS_INIT_DONE)],
+		msecs_to_jiffies(HW_RESPONSE_TIMEOUT));
+	if (!rc) {
+		pr_err("Wait interrupted or timeout: %d\n", rc);
+		rc = -EIO;
+		goto exit;
+	} else {
+		spin_lock_irqsave(&core->lock, flags);
+		core->state = VIDC_CORE_INIT_DONE;
+		spin_unlock_irqrestore(&core->lock, flags);
+	}
+	pr_debug("SYS_INIT_DONE!!!\n");
+core_already_inited:
+	change_inst_state(inst, MSM_VIDC_CORE_INIT_DONE);
+	rc = 0;
+exit:
+	mutex_unlock(&core->sync_lock);
+	return rc;
+}
+
+static int msm_comm_init_core(struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+	struct msm_vidc_core *core = inst->core;
+	unsigned long flags;
+	mutex_lock(&core->sync_lock);
+	if (core->state >= VIDC_CORE_INIT) {
+		pr_err("Video core: %d is already in state: %d\n",
+				core->id, core->state);
+		goto core_already_inited;
+	}
+	init_completion(&core->completions[SYS_MSG_INDEX(SYS_INIT_DONE)]);
+	rc = vidc_hal_core_init(core->device);
+	if (rc) {
+		pr_err("Failed to init core, id = %d\n", core->id);
+		goto exit;
+	}
+	spin_lock_irqsave(&core->lock, flags);
+	core->state = VIDC_CORE_INIT;
+	spin_unlock_irqrestore(&core->lock, flags);
+core_already_inited:
+	change_inst_state(inst, MSM_VIDC_CORE_INIT);
+exit:
+	mutex_unlock(&core->sync_lock);
+	return rc;
+}
+
+static int msm_vidc_deinit_core(struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+	struct msm_vidc_core *core = inst->core;
+	unsigned long flags;
+	mutex_lock(&core->sync_lock);
+	if (core->state == VIDC_CORE_UNINIT) {
+		pr_err("Video core: %d is already in state: %d\n",
+				core->id, core->state);
+		goto core_already_uninited;
+	}
+	if (list_empty(&core->instances)) {
+		pr_debug("Calling vidc_hal_core_release\n");
+		rc = vidc_hal_core_release(core->device);
+		if (rc) {
+			pr_err("Failed to release core, id = %d\n", core->id);
+			goto exit;
+		}
+		spin_lock_irqsave(&core->lock, flags);
+		core->state = VIDC_CORE_UNINIT;
+		spin_unlock_irqrestore(&core->lock, flags);
+	}
+core_already_uninited:
+	change_inst_state(inst, MSM_VIDC_CORE_UNINIT);
+exit:
+	mutex_unlock(&core->sync_lock);
+	return rc;
+}
+
+static enum hal_domain get_hal_domain(int session_type)
+{
+	enum hal_domain domain;
+	switch (session_type) {
+	case MSM_VIDC_ENCODER:
+		domain = HAL_VIDEO_DOMAIN_ENCODER;
+		break;
+	case MSM_VIDC_DECODER:
+		domain = HAL_VIDEO_DOMAIN_DECODER;
+		break;
+	default:
+		pr_err("Wrong domain\n");
+		domain = HAL_UNUSED_DOMAIN;
+		break;
+	}
+	return domain;
+}
+
+static enum hal_video_codec get_hal_codec_type(int fourcc)
+{
+	enum hal_video_codec codec;
+	pr_debug("codec in %s is 0x%x", __func__, fourcc);
+	switch (fourcc) {
+	case V4L2_PIX_FMT_H264:
+	case V4L2_PIX_FMT_H264_NO_SC:
+		codec = HAL_VIDEO_CODEC_H264;
+		break;
+	case V4L2_PIX_FMT_H263:
+		codec = HAL_VIDEO_CODEC_H263;
+		break;
+	case V4L2_PIX_FMT_MPEG1:
+		codec = HAL_VIDEO_CODEC_MPEG1;
+		break;
+	case V4L2_PIX_FMT_MPEG2:
+		codec = HAL_VIDEO_CODEC_MPEG2;
+		break;
+	case V4L2_PIX_FMT_MPEG4:
+		codec = HAL_VIDEO_CODEC_MPEG4;
+		break;
+	case V4L2_PIX_FMT_VC1_ANNEX_G:
+	case V4L2_PIX_FMT_VC1_ANNEX_L:
+		codec = HAL_VIDEO_CODEC_VC1;
+		break;
+	case V4L2_PIX_FMT_DIVX_311:
+		codec = HAL_VIDEO_CODEC_DIVX_311;
+		break;
+		/*HAL_VIDEO_CODEC_MVC
+		  HAL_VIDEO_CODEC_DIVX
+		  HAL_VIDEO_CODEC_SPARK
+		  HAL_VIDEO_CODEC_VP6
+		  HAL_VIDEO_CODEC_VP7
+		  HAL_VIDEO_CODEC_VP8*/
+	default:
+		pr_err("Wrong codec: %d\n", fourcc);
+		codec = HAL_UNUSED_CODEC;
+	}
+	return codec;
+}
+
+static int msm_comm_session_init(int flipped_state,
+	struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+	int fourcc = 0;
+	if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_OPEN)) {
+		pr_err("inst: %p is already in state: %d\n", inst, inst->state);
+		goto exit;
+	}
+	if (inst->session_type == MSM_VIDC_DECODER) {
+		fourcc = inst->fmts[OUTPUT_PORT]->fourcc;
+	} else if (inst->session_type == MSM_VIDC_ENCODER) {
+		fourcc = inst->fmts[CAPTURE_PORT]->fourcc;
+	} else {
+		pr_err("Invalid session\n");
+		return -EINVAL;
+	}
+	init_completion(
+		&inst->completions[SESSION_MSG_INDEX(SESSION_INIT_DONE)]);
+	inst->session = vidc_hal_session_init(inst->core->device, (u32) inst,
+					get_hal_domain(inst->session_type),
+					get_hal_codec_type(fourcc));
+	if (!inst->session) {
+		pr_err("Failed to call session init for: %d, %d, %d, %d\n",
+				(int)inst->core->device, (int)inst,
+				inst->session_type, fourcc);
+		goto exit;
+	}
+	change_inst_state(inst, MSM_VIDC_OPEN);
+exit:
+	return rc;
+}
+
+static int msm_vidc_load_resources(int flipped_state,
+	struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+	if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_LOAD_RESOURCES)) {
+		pr_err("inst: %p is already in state: %d\n", inst, inst->state);
+		goto exit;
+	}
+	rc = vidc_hal_session_load_res((void *) inst->session);
+	if (rc) {
+		pr_err("Failed to send load resources\n");
+		goto exit;
+	}
+	change_inst_state(inst, MSM_VIDC_LOAD_RESOURCES);
+exit:
+	return rc;
+}
+
+static int msm_vidc_start(int flipped_state, struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+	if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_START)) {
+		pr_err("inst: %p is already in state: %d\n", inst, inst->state);
+		goto exit;
+	}
+	init_completion(
+		&inst->completions[SESSION_MSG_INDEX(SESSION_START_DONE)]);
+	rc = vidc_hal_session_start((void *) inst->session);
+	if (rc) {
+		pr_err("Failed to send load resources\n");
+		goto exit;
+	}
+	change_inst_state(inst, MSM_VIDC_START);
+exit:
+	return rc;
+}
+
+static int msm_vidc_stop(int flipped_state, struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+	if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_STOP)) {
+		pr_err("inst: %p is already in state: %d\n", inst, inst->state);
+		goto exit;
+	}
+	pr_debug("Send Stop to hal\n");
+	init_completion(
+		&inst->completions[SESSION_MSG_INDEX(SESSION_STOP_DONE)]);
+	rc = vidc_hal_session_stop((void *) inst->session);
+	if (rc) {
+		pr_err("Failed to send stop\n");
+		goto exit;
+	}
+	change_inst_state(inst, MSM_VIDC_STOP);
+exit:
+	return rc;
+}
+
+static int msm_vidc_release_res(int flipped_state, struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+	if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_RELEASE_RESOURCES)) {
+		pr_err("inst: %p is already in state: %d\n", inst, inst->state);
+		goto exit;
+	}
+	pr_debug("Send release res to hal\n");
+	init_completion(
+	&inst->completions[SESSION_MSG_INDEX(SESSION_RELEASE_RESOURCE_DONE)]);
+	rc = vidc_hal_session_release_res((void *) inst->session);
+	if (rc) {
+		pr_err("Failed to send load resources\n");
+		goto exit;
+	}
+	change_inst_state(inst, MSM_VIDC_RELEASE_RESOURCES);
+exit:
+	return rc;
+}
+
+static int msm_comm_session_close(int flipped_state, struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+	if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_CLOSE)) {
+		pr_err("inst: %p is already in state: %d\n", inst, inst->state);
+		goto exit;
+	}
+	pr_debug("Send session close to hal\n");
+	init_completion(
+		&inst->completions[SESSION_MSG_INDEX(SESSION_END_DONE)]);
+	rc = vidc_hal_session_end((void *) inst->session);
+	if (rc) {
+		pr_err("Failed to send load resources\n");
+		goto exit;
+	}
+	change_inst_state(inst, MSM_VIDC_OPEN);
+exit:
+	return rc;
+}
+
+int msm_comm_try_state(struct msm_vidc_inst *inst, int state)
+{
+	int rc = 0;
+	int flipped_state;
+	if (!inst) {
+		pr_err("Invalid instance pointer = %p\n", inst);
+		return -EINVAL;
+	}
+	pr_debug("Trying to move inst: %p from: 0x%x to 0x%x\n",
+				inst, inst->state, state);
+	mutex_lock(&inst->sync_lock);
+	flipped_state = inst->state;
+	if (flipped_state < MSM_VIDC_STOP
+		&& state > MSM_VIDC_STOP) {
+		flipped_state = MSM_VIDC_STOP + (MSM_VIDC_STOP - flipped_state);
+		flipped_state &= 0xFFFE;
+		flipped_state = flipped_state - 1;
+	} else if (flipped_state > MSM_VIDC_STOP
+		&& state < MSM_VIDC_STOP) {
+		flipped_state = MSM_VIDC_STOP -
+				(flipped_state - MSM_VIDC_STOP + 1);
+		flipped_state &= 0xFFFE;
+		flipped_state = flipped_state - 1;
+	}
+	pr_debug("flipped_state = 0x%x\n", flipped_state);
+	switch (flipped_state) {
+	case MSM_VIDC_CORE_UNINIT_DONE:
+	case MSM_VIDC_CORE_INIT:
+		rc = msm_comm_init_core(inst);
+		if (rc || state <= inst->state)
+			break;
+	case MSM_VIDC_CORE_INIT_DONE:
+		rc = msm_comm_init_core_done(inst);
+		if (rc || state <= inst->state)
+			break;
+	case MSM_VIDC_OPEN:
+		rc = msm_comm_session_init(flipped_state, inst);
+		if (rc || state <= inst->state)
+			break;
+	case MSM_VIDC_OPEN_DONE:
+		rc = wait_for_state(inst, flipped_state, MSM_VIDC_OPEN_DONE,
+			SESSION_INIT_DONE);
+		if (rc || state <= inst->state)
+			break;
+	case MSM_VIDC_LOAD_RESOURCES:
+		rc = msm_vidc_load_resources(flipped_state, inst);
+		if (rc || state <= inst->state)
+			break;
+	case MSM_VIDC_LOAD_RESOURCES_DONE:
+	case MSM_VIDC_START:
+		rc = msm_vidc_start(flipped_state, inst);
+		if (rc || state <= inst->state)
+			break;
+	case MSM_VIDC_START_DONE:
+		rc = wait_for_state(inst, flipped_state, MSM_VIDC_START_DONE,
+				SESSION_START_DONE);
+		if (rc || state <= inst->state)
+			break;
+	case MSM_VIDC_STOP:
+		rc = msm_vidc_stop(flipped_state, inst);
+		if (rc || state <= inst->state)
+			break;
+	case MSM_VIDC_STOP_DONE:
+		rc = wait_for_state(inst, flipped_state, MSM_VIDC_STOP_DONE,
+				SESSION_STOP_DONE);
+		if (rc || state <= inst->state)
+			break;
+		pr_debug("Moving to Stop Done state\n");
+	case MSM_VIDC_RELEASE_RESOURCES:
+		rc = msm_vidc_release_res(flipped_state, inst);
+		if (rc || state <= inst->state)
+			break;
+	case MSM_VIDC_RELEASE_RESOURCES_DONE:
+		rc = wait_for_state(inst, flipped_state,
+			MSM_VIDC_RELEASE_RESOURCES_DONE,
+			SESSION_RELEASE_RESOURCE_DONE);
+		if (rc || state <= inst->state)
+			break;
+		pr_debug("Moving to release resources done state\n");
+	case MSM_VIDC_CLOSE:
+		rc = msm_comm_session_close(flipped_state, inst);
+		if (rc || state <= inst->state)
+			break;
+	case MSM_VIDC_CLOSE_DONE:
+		rc = wait_for_state(inst, flipped_state, MSM_VIDC_CLOSE_DONE,
+			SESSION_END_DONE);
+		if (rc || state <= inst->state)
+			break;
+	case MSM_VIDC_CORE_UNINIT:
+		pr_debug("***************Sending core uninit\n");
+		rc = msm_vidc_deinit_core(inst);
+		if (rc || state == inst->state)
+			break;
+	default:
+		pr_err("State not recognized\n");
+		rc = -EINVAL;
+		break;
+	}
+	mutex_unlock(&inst->sync_lock);
+	if (rc)
+		pr_err("Failed to move from state: %d to %d\n",
+			inst->state, state);
+	return rc;
+}
+
+int msm_comm_qbuf(struct vb2_buffer *vb)
+{
+	int rc = 0;
+	struct vb2_queue *q;
+	struct msm_vidc_inst *inst;
+	unsigned long flags;
+	struct vb2_buf_entry *entry;
+	struct vidc_frame_data frame_data;
+	q = vb->vb2_queue;
+	inst = q->drv_priv;
+
+	if (!inst || !vb) {
+		pr_err("Invalid input: %p, %p\n", inst, vb);
+		return -EINVAL;
+	}
+	if (inst->state != MSM_VIDC_START_DONE) {
+			entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+			if (!entry) {
+				pr_err("Out of memory\n");
+				goto err_no_mem;
+			}
+			entry->vb = vb;
+			pr_debug("Queueing buffer in pendingq\n");
+			spin_lock_irqsave(&inst->lock, flags);
+			list_add_tail(&entry->list, &inst->pendingq);
+			spin_unlock_irqrestore(&inst->lock, flags);
+	} else {
+		memset(&frame_data, 0 , sizeof(struct vidc_frame_data));
+		frame_data.alloc_len = vb->v4l2_planes[0].length;
+		frame_data.filled_len = vb->v4l2_planes[0].bytesused;
+		frame_data.device_addr = vb->v4l2_planes[0].m.userptr;
+		frame_data.clnt_data = (u32)vb;
+		if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+			frame_data.buffer_type = HAL_BUFFER_INPUT;
+			if (vb->v4l2_buf.flags & V4L2_BUF_FLAG_EOS) {
+				frame_data.flags = HAL_BUFFERFLAG_EOS;
+				pr_debug("Received EOS on output capability\n");
+			}
+			pr_debug("Sending etb to hal: Alloc: %d :filled: %d\n",
+				frame_data.alloc_len, frame_data.filled_len);
+			rc = vidc_hal_session_etb((void *) inst->session,
+					&frame_data);
+			pr_debug("Sent etb to HAL\n");
+		} else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+			frame_data.filled_len = 0;
+			frame_data.buffer_type = HAL_BUFFER_OUTPUT;
+			frame_data.extradata_addr = 0;
+			pr_debug("Sending ftb to hal...: Alloc: %d :filled: %d"
+				" extradata_addr: %d\n", frame_data.alloc_len,
+				   frame_data.filled_len,
+				   frame_data.extradata_addr);
+			rc = vidc_hal_session_ftb((void *) inst->session,
+					&frame_data);
+		} else {
+			pr_err("This capability is not supported: %d\n",
+				q->type);
+			rc = -EINVAL;
+		}
+	}
+	if (rc)
+		pr_err("Failed to queue buffer\n");
+err_no_mem:
+	return rc;
+}
+
+int msm_comm_try_get_bufreqs(struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+	mutex_lock(&inst->sync_lock);
+	init_completion(
+		&inst->completions[SESSION_MSG_INDEX(SESSION_PROPERTY_INFO)]);
+	rc = vidc_hal_session_get_buf_req((void *) inst->session);
+	if (rc) {
+		pr_err("Failed to get property\n");
+		goto exit;
+	}
+	rc = wait_for_completion_timeout(
+		&inst->completions[SESSION_MSG_INDEX(SESSION_PROPERTY_INFO)],
+		msecs_to_jiffies(HW_RESPONSE_TIMEOUT));
+	if (!rc) {
+		pr_err("Wait interrupted or timeout: %d\n", rc);
+		rc = -EIO;
+		goto exit;
+	}
+	rc = 0;
+exit:
+	mutex_unlock(&inst->sync_lock);
+	return rc;
+}
+
+int msm_vidc_decoder_cmd(void *instance, struct v4l2_decoder_cmd *dec)
+{
+	int rc = 0;
+	struct msm_vidc_inst *inst = (struct msm_vidc_inst *)instance;
+	mutex_lock(&inst->sync_lock);
+	if (dec->cmd != V4L2_DEC_CMD_STOP)
+		return -EINVAL;
+	rc = vidc_hal_session_flush((void *)inst->session, HAL_FLUSH_OUTPUT);
+	if (rc) {
+		pr_err("Failed to get property\n");
+		goto exit;
+	}
+exit:
+	mutex_unlock(&inst->sync_lock);
+	return rc;
+}
+
+int msm_comm_set_scratch_buffers(struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+	struct msm_smem *handle;
+	struct internal_buf *binfo;
+	struct list_head *ptr, *next;
+	struct vidc_buffer_addr_info buffer_info;
+	unsigned long flags;
+	int i;
+	pr_debug("scratch: num = %d, size = %d\n",
+			inst->buff_req.buffer[6].buffer_count_actual,
+			inst->buff_req.buffer[6].buffer_size);
+	spin_lock_irqsave(&inst->lock, flags);
+	if (!list_empty(&inst->internalbufs)) {
+		list_for_each_safe(ptr, next, &inst->internalbufs) {
+			binfo = list_entry(ptr, struct internal_buf,
+					list);
+			list_del(&binfo->list);
+			msm_smem_free(inst->mem_client, binfo->handle);
+			kfree(binfo);
+		}
+	}
+	spin_unlock_irqrestore(&inst->lock, flags);
+
+
+	for (i = 0; i < inst->buff_req.buffer[6].buffer_count_actual;
+				i++) {
+		handle = msm_smem_alloc(inst->mem_client,
+				inst->buff_req.buffer[6].buffer_size, 1, 0);
+		if (!handle) {
+			pr_err("Failed to allocate scratch memory\n");
+			rc = -ENOMEM;
+			goto err_no_mem;
+		}
+		binfo = kzalloc(sizeof(*binfo), GFP_KERNEL);
+		if (!binfo) {
+			pr_err("Out of memory\n");
+			rc = -ENOMEM;
+			goto err_no_mem;
+		}
+		binfo->handle = handle;
+		spin_lock_irqsave(&inst->lock, flags);
+		list_add_tail(&binfo->list, &inst->internalbufs);
+		spin_unlock_irqrestore(&inst->lock, flags);
+		buffer_info.buffer_size =
+				inst->buff_req.buffer[6].buffer_size;
+		buffer_info.buffer_type = HAL_BUFFER_INTERNAL_SCRATCH;
+		buffer_info.num_buffers = 1;
+		buffer_info.align_device_addr = handle->device_addr;
+		rc = vidc_hal_session_set_buffers((void *) inst->session,
+				&buffer_info);
+		if (rc) {
+			pr_err("vidc_hal_session_set_buffers failed");
+			break;
+		}
+	}
+err_no_mem:
+	return rc;
+}
diff --git a/drivers/media/video/msm_vidc/msm_vidc_common.h b/drivers/media/video/msm_vidc/msm_vidc_common.h
new file mode 100644
index 0000000..2fafa79
--- /dev/null
+++ b/drivers/media/video/msm_vidc/msm_vidc_common.h
@@ -0,0 +1,36 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _MSM_VIDC_COMMON_H_
+#define _MSM_VIDC_COMMON_H_
+#include "msm_vidc_internal.h"
+struct vb2_buf_entry {
+	struct list_head list;
+	struct vb2_buffer *vb;
+};
+struct msm_vidc_core *get_vidc_core(int core_id);
+const struct msm_vidc_format *msm_comm_get_pixel_fmt_index(
+	const struct msm_vidc_format fmt[], int size, int index, int fmt_type);
+const struct msm_vidc_format *msm_comm_get_pixel_fmt_fourcc(
+	const struct msm_vidc_format fmt[], int size, int fourcc, int fmt_type);
+struct vb2_queue *msm_comm_get_vb2q(
+		struct msm_vidc_inst *inst, enum v4l2_buf_type type);
+int msm_comm_try_state(struct msm_vidc_inst *inst, int state);
+int msm_comm_try_get_bufreqs(struct msm_vidc_inst *inst);
+int msm_comm_set_scratch_buffers(struct msm_vidc_inst *inst);
+int msm_comm_qbuf(struct vb2_buffer *vb);
+#define IS_PRIV_CTRL(idx) (\
+		(V4L2_CTRL_ID2CLASS(idx) == V4L2_CTRL_CLASS_MPEG) && \
+		V4L2_CTRL_DRIVER_PRIV(idx))
+
+#endif
diff --git a/drivers/media/video/msm_vidc/msm_vidc_internal.h b/drivers/media/video/msm_vidc/msm_vidc_internal.h
new file mode 100644
index 0000000..fb1ab58
--- /dev/null
+++ b/drivers/media/video/msm_vidc/msm_vidc_internal.h
@@ -0,0 +1,171 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _MSM_VIDC_INTERNAL_H_
+#define _MSM_VIDC_INTERNAL_H_
+
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/completion.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ctrls.h>
+#include <media/videobuf2-core.h>
+#include <media/msm_vidc.h>
+
+#include "vidc_hal_api.h"
+
+#define MSM_VIDC_DRV_NAME "msm_vidc_driver"
+#define MSM_VIDC_VERSION KERNEL_VERSION(0, 0, 1);
+#define MAX_DEBUGFS_NAME 50
+#define DEFAULT_TIMEOUT 3
+
+#define V4L2_EVENT_VIDC_BASE  10
+
+#define SYS_MSG_START VIDC_EVENT_CHANGE
+#define SYS_MSG_END SYS_DEBUG
+#define SESSION_MSG_START SESSION_LOAD_RESOURCE_DONE
+#define SESSION_MSG_END SESSION_PROPERTY_INFO
+#define SYS_MSG_INDEX(__msg) (__msg - SYS_MSG_START)
+#define SESSION_MSG_INDEX(__msg) (__msg - SESSION_MSG_START)
+
+enum vidc_ports {
+	OUTPUT_PORT,
+	CAPTURE_PORT,
+	MAX_PORT_NUM
+};
+
+enum vidc_core_state {
+	VIDC_CORE_UNINIT = 0,
+	VIDC_CORE_INIT,
+	VIDC_CORE_INIT_DONE,
+};
+
+/*Donot change the enum values unless
+ * you know what you are doing*/
+enum instance_state {
+	MSM_VIDC_CORE_UNINIT_DONE = 0x0001,
+	MSM_VIDC_CORE_INIT,
+	MSM_VIDC_CORE_INIT_DONE,
+	MSM_VIDC_OPEN,
+	MSM_VIDC_OPEN_DONE,
+	MSM_VIDC_LOAD_RESOURCES,
+	MSM_VIDC_LOAD_RESOURCES_DONE,
+	MSM_VIDC_START,
+	MSM_VIDC_START_DONE,
+	MSM_VIDC_STOP,
+	MSM_VIDC_STOP_DONE,
+	MSM_VIDC_RELEASE_RESOURCES,
+	MSM_VIDC_RELEASE_RESOURCES_DONE,
+	MSM_VIDC_CLOSE,
+	MSM_VIDC_CLOSE_DONE,
+	MSM_VIDC_CORE_UNINIT,
+};
+
+enum vidc_resposes_id {
+	MSM_VIDC_DECODER_FLUSH_DONE = 0x11,
+	MSM_VIDC_DECODER_EVENT_CHANGE,
+};
+
+struct buf_info {
+	struct list_head list;
+	struct vb2_buffer *buf;
+};
+
+struct internal_buf {
+	struct list_head list;
+	struct msm_smem *handle;
+};
+
+struct msm_vidc_format {
+	char name[64];
+	u8 description[32];
+	u32 fourcc;
+	int num_planes;
+	int type;
+	u32 (*get_frame_size)(int plane, u32 height, u32 width);
+};
+
+struct msm_vidc_drv {
+	spinlock_t lock;
+	struct list_head cores;
+	int num_cores;
+	struct dentry *debugfs_root;
+};
+
+struct msm_video_device {
+	int type;
+	struct video_device vdev;
+};
+
+struct msm_vidc_core {
+	struct list_head list;
+	struct mutex sync_lock;
+	int id;
+	void *device;
+	struct msm_video_device vdev[MSM_VIDC_MAX_DEVICES];
+	struct v4l2_device v4l2_dev;
+	spinlock_t lock;
+	struct list_head instances;
+	struct dentry *debugfs_root;
+	u32 base_addr;
+	u32 register_base;
+	u32 register_size;
+	u32 irq;
+	enum vidc_core_state state;
+	struct completion completions[SYS_MSG_END - SYS_MSG_START + 1];
+};
+
+struct msm_vidc_inst {
+	struct list_head list;
+	struct mutex sync_lock;
+	struct msm_vidc_core *core;
+	int session_type;
+	void *session;
+	u32 width;
+	u32 height;
+	int state;
+	const struct msm_vidc_format *fmts[MAX_PORT_NUM];
+	struct vb2_queue vb2_bufq[MAX_PORT_NUM];
+	spinlock_t lock;
+	struct list_head pendingq;
+	struct list_head internalbufs;
+	struct buffer_requirements buff_req;
+	void *mem_client;
+	struct v4l2_ctrl_handler ctrl_handler;
+	struct completion completions[SESSION_MSG_END - SESSION_MSG_START + 1];
+	struct v4l2_fh event_handler;
+	bool in_reconfig;
+	u32 reconfig_width;
+	u32 reconfig_height;
+};
+
+extern struct msm_vidc_drv *vidc_driver;
+
+struct msm_vidc_ctrl {
+	u32 id;
+	char name[64];
+	enum v4l2_ctrl_type type;
+	s32 minimum;
+	s32 maximum;
+	s32 default_value;
+	u32 step;
+	u32 menu_skip_mask;
+	const char * const *qmenu;
+};
+
+void handle_cmd_response(enum command_response cmd, void *data);
+#endif
diff --git a/drivers/media/video/msm_vidc/vidc_hal.c b/drivers/media/video/msm_vidc/vidc_hal.c
new file mode 100644
index 0000000..1f33c2c
--- /dev/null
+++ b/drivers/media/video/msm_vidc/vidc_hal.c
@@ -0,0 +1,1925 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <asm/memory.h>
+#include "vidc_hal.h"
+#include "vidc_hal_io.h"
+
+#define FIRMWARE_SIZE			0X00A00000
+#define REG_ADDR_OFFSET_BITMASK	0x000FFFFF
+
+/*Workaround for virtio */
+#define HFI_VIRTIO_FW_BIAS		0x34f00000
+
+struct hal_device_data hal_ctxt;
+
+static void hal_virtio_modify_cmd_packet(u8 *packet)
+{
+	struct hfi_cmd_sys_session_init_packet *sys_init;
+	struct hal_session *sess;
+	u8 i;
+
+	if (!packet) {
+		HAL_MSG_ERROR("Invalid Param: %s", __func__);
+		return;
+	}
+
+	sys_init = (struct hfi_cmd_sys_session_init_packet *)packet;
+	sess = (struct hal_session *) sys_init->session_id;
+	switch (sys_init->packet) {
+	case HFI_CMD_SESSION_EMPTY_BUFFER:
+		if (sess->is_decoder) {
+			struct hfi_cmd_session_empty_buffer_compressed_packet
+			*pkt = (struct
+			hfi_cmd_session_empty_buffer_compressed_packet
+			*) packet;
+			pkt->packet_buffer -= HFI_VIRTIO_FW_BIAS;
+		} else {
+			struct
+			hfi_cmd_session_empty_buffer_uncompressed_plane0_packet
+			*pkt = (struct
+			hfi_cmd_session_empty_buffer_uncompressed_plane0_packet
+			*) packet;
+			pkt->packet_buffer -= HFI_VIRTIO_FW_BIAS;
+		}
+		break;
+	case HFI_CMD_SESSION_FILL_BUFFER:
+	{
+		struct hfi_cmd_session_fill_buffer_packet *pkt =
+			(struct hfi_cmd_session_fill_buffer_packet *)packet;
+		pkt->packet_buffer -= HFI_VIRTIO_FW_BIAS;
+		break;
+	}
+	case HFI_CMD_SESSION_SET_BUFFERS:
+	{
+		struct hfi_cmd_session_set_buffers_packet *pkt =
+			(struct hfi_cmd_session_set_buffers_packet *)packet;
+		if ((pkt->buffer_type == HFI_BUFFER_OUTPUT) ||
+			(pkt->buffer_type == HFI_BUFFER_OUTPUT2)) {
+			struct hfi_buffer_info *buff;
+			buff = (struct hfi_buffer_info *) pkt->rg_buffer_info;
+			buff->buffer_addr -= HFI_VIRTIO_FW_BIAS;
+			buff->extradata_addr -= HFI_VIRTIO_FW_BIAS;
+		} else {
+			for (i = 0; i < pkt->num_buffers; i++)
+				pkt->rg_buffer_info[i] -= HFI_VIRTIO_FW_BIAS;
+		}
+		break;
+	}
+	case HFI_CMD_SESSION_RELEASE_BUFFERS:
+	{
+		struct hfi_cmd_session_release_buffer_packet *pkt =
+			(struct hfi_cmd_session_release_buffer_packet *)packet;
+		if ((pkt->buffer_type == HAL_BUFFER_OUTPUT) ||
+			(pkt->buffer_type == HAL_BUFFER_OUTPUT2)) {
+			struct hfi_buffer_info *buff;
+			buff = (struct hfi_buffer_info *) pkt->rg_buffer_info;
+			buff->buffer_addr -= HFI_VIRTIO_FW_BIAS;
+			buff->extradata_addr -= HFI_VIRTIO_FW_BIAS;
+		} else {
+			for (i = 0; i < pkt->num_buffers; i++)
+				pkt->rg_buffer_info[i] -= HFI_VIRTIO_FW_BIAS;
+		}
+		break;
+	}
+	case HFI_CMD_SESSION_PARSE_SEQUENCE_HEADER:
+	{
+		struct hfi_cmd_session_parse_sequence_header_packet *pkt =
+			(struct hfi_cmd_session_parse_sequence_header_packet *)
+		packet;
+		pkt->packet_buffer -= HFI_VIRTIO_FW_BIAS;
+		break;
+	}
+	case HFI_CMD_SESSION_GET_SEQUENCE_HEADER:
+	{
+		struct hfi_cmd_session_get_sequence_header_packet *pkt =
+			(struct hfi_cmd_session_get_sequence_header_packet *)
+		packet;
+		pkt->packet_buffer -= HFI_VIRTIO_FW_BIAS;
+		break;
+	}
+	default:
+		break;
+	}
+}
+
+static int write_queue(void *info, u8 *packet, u32 *rx_req_is_set)
+{
+	struct hfi_queue_header *queue;
+	u32 packet_size_in_words, new_write_idx;
+	struct vidc_iface_q_info *qinfo;
+	u32 empty_space, read_idx;
+	u32 *write_ptr;
+
+	if (!info || !packet || !rx_req_is_set) {
+		HAL_MSG_ERROR("Invalid Params in %s", __func__);
+		return -EINVAL;
+	}
+
+	qinfo =	(struct vidc_iface_q_info *) info;
+	HAL_MSG_LOW("In %s: ", __func__);
+	hal_virtio_modify_cmd_packet(packet);
+
+	queue = (struct hfi_queue_header *) qinfo->q_hdr;
+
+	if (!queue) {
+		HAL_MSG_ERROR("queue not present");
+		return -ENOENT;
+	}
+
+	packet_size_in_words = (*(u32 *)packet) >> 2;
+	HAL_MSG_LOW("Packet_size in words: %d", packet_size_in_words);
+
+	if (packet_size_in_words == 0) {
+		HAL_MSG_ERROR("Zero packet size");
+		return -ENODATA;
+	}
+
+	read_idx = queue->qhdr_read_idx;
+
+	empty_space = (queue->qhdr_write_idx >=  read_idx) ?
+		(queue->qhdr_q_size - (queue->qhdr_write_idx -  read_idx)) :
+		(read_idx - queue->qhdr_write_idx);
+	HAL_MSG_LOW("Empty_space: %d", empty_space);
+	if (empty_space <= packet_size_in_words) {
+		queue->qhdr_tx_req =  1;
+		HAL_MSG_ERROR("Insufficient size (%d) to write (%d)",
+					  empty_space, packet_size_in_words);
+		return -ENOTEMPTY;
+	}
+
+	queue->qhdr_tx_req =  0;
+
+	new_write_idx = (queue->qhdr_write_idx + packet_size_in_words);
+	write_ptr = (u32 *)((qinfo->q_array.align_virtual_addr) +
+		(queue->qhdr_write_idx << 2));
+	HAL_MSG_LOW("Write Ptr: %d", (u32) write_ptr);
+	if (new_write_idx < queue->qhdr_q_size) {
+		memcpy(write_ptr, packet, packet_size_in_words << 2);
+	} else {
+		new_write_idx -= queue->qhdr_q_size;
+		memcpy(write_ptr, packet, (packet_size_in_words -
+			new_write_idx) << 2);
+		memcpy((void *)queue->qhdr_start_addr,
+			packet + ((packet_size_in_words - new_write_idx) << 2),
+			new_write_idx  << 2);
+	}
+	queue->qhdr_write_idx = new_write_idx;
+	*rx_req_is_set = (1 == queue->qhdr_rx_req) ? 1 : 0;
+	HAL_MSG_LOW("Out %s: ", __func__);
+	return 0;
+}
+
+static void hal_virtio_modify_msg_packet(u8 *packet)
+{
+	struct hfi_msg_sys_session_init_done_packet *sys_idle;
+	struct hal_session *sess;
+
+	if (!packet) {
+		HAL_MSG_ERROR("Invalid Param: %s", __func__);
+		return;
+	}
+
+	sys_idle = (struct hfi_msg_sys_session_init_done_packet *)packet;
+	sess = (struct hal_session *) sys_idle->session_id;
+
+	switch (sys_idle->packet_type) {
+	case HFI_MSG_SESSION_FILL_BUFFER_DONE:
+		if (sess->is_decoder) {
+			struct
+			hfi_msg_session_fbd_uncompressed_plane0_packet
+			*pkt_uc = (struct
+			hfi_msg_session_fbd_uncompressed_plane0_packet
+			*) packet;
+			pkt_uc->packet_buffer += HFI_VIRTIO_FW_BIAS;
+		} else {
+			struct
+			hfi_msg_session_fill_buffer_done_compressed_packet
+			*pkt = (struct
+			hfi_msg_session_fill_buffer_done_compressed_packet
+			*) packet;
+			pkt->packet_buffer += HFI_VIRTIO_FW_BIAS;
+		}
+		break;
+	case HFI_MSG_SESSION_EMPTY_BUFFER_DONE:
+	{
+		struct hfi_msg_session_empty_buffer_done_packet *pkt =
+		(struct hfi_msg_session_empty_buffer_done_packet *)packet;
+		pkt->packet_buffer += HFI_VIRTIO_FW_BIAS;
+		break;
+	}
+	case HFI_MSG_SESSION_GET_SEQUENCE_HEADER_DONE:
+	{
+		struct
+		hfi_msg_session_get_sequence_header_done_packet
+		*pkt =
+		(struct hfi_msg_session_get_sequence_header_done_packet *)
+		packet;
+		pkt->sequence_header += HFI_VIRTIO_FW_BIAS;
+		break;
+	}
+	default:
+		break;
+	}
+}
+
+static int read_queue(void *info, u8 *packet, u32 *pb_tx_req_is_set)
+{
+	struct hfi_queue_header *queue;
+	u32 packet_size_in_words, new_read_idx;
+	u32 *read_ptr;
+	struct vidc_iface_q_info *qinfo;
+
+	if (!info || !packet || !pb_tx_req_is_set) {
+		HAL_MSG_ERROR("Invalid Params in %s", __func__);
+		return -EINVAL;
+	}
+
+	qinfo =	(struct vidc_iface_q_info *) info;
+	HAL_MSG_LOW("In %s: ", __func__);
+	queue = (struct hfi_queue_header *) qinfo->q_hdr;
+
+	if (!queue) {
+		HAL_MSG_ERROR("Queue memory is not allocated\n");
+		return -ENOMEM;
+	}
+
+	if (queue->qhdr_read_idx == queue->qhdr_write_idx) {
+		queue->qhdr_rx_req = 1;
+		*pb_tx_req_is_set = 0;
+		return -EPERM;
+	}
+
+	read_ptr = (u32 *)((qinfo->q_array.align_virtual_addr) +
+				(queue->qhdr_read_idx << 2));
+	packet_size_in_words = (*read_ptr) >> 2;
+	HAL_MSG_LOW("packet_size_in_words: %d", packet_size_in_words);
+	if (packet_size_in_words == 0) {
+		HAL_MSG_ERROR("Zero packet size");
+		return -ENODATA;
+	}
+
+	new_read_idx = queue->qhdr_read_idx + packet_size_in_words;
+	HAL_MSG_LOW("Read Ptr: %d", (u32) new_read_idx);
+	if (new_read_idx < queue->qhdr_q_size) {
+		memcpy(packet, read_ptr,
+			packet_size_in_words << 2);
+	} else {
+		new_read_idx -= queue->qhdr_q_size;
+		memcpy(packet, read_ptr,
+			(packet_size_in_words - new_read_idx) << 2);
+		memcpy(packet + ((packet_size_in_words -
+				new_read_idx) << 2),
+			(u8 *)queue->qhdr_start_addr, new_read_idx << 2);
+	}
+
+	queue->qhdr_read_idx = new_read_idx;
+
+	if (queue->qhdr_read_idx != queue->qhdr_write_idx)
+		queue->qhdr_rx_req = 0;
+	else
+		queue->qhdr_rx_req = 1;
+
+	*pb_tx_req_is_set = (1 == queue->qhdr_tx_req) ? 1 : 0;
+	hal_virtio_modify_msg_packet(packet);
+	HAL_MSG_LOW("Out %s: ", __func__);
+	return 0;
+}
+
+static int vidc_hal_alloc(void *mem, void *clnt, u32 size, u32 align, u32 flags)
+{
+	struct vidc_mem_addr *vmem;
+	struct msm_smem *alloc;
+
+	if (!mem || !clnt || !size) {
+		HAL_MSG_ERROR("Invalid Params in %s", __func__);
+		return -EINVAL;
+	}
+	vmem = (struct vidc_mem_addr *)mem;
+	HAL_MSG_HIGH("start to alloc: size:%d, Flags: %d", size, flags);
+
+	alloc  = msm_smem_alloc(clnt, size, align, flags);
+	HAL_MSG_LOW("Alloc done");
+	if (!alloc) {
+		HAL_MSG_HIGH("Alloc fail in %s", __func__);
+		return -ENOMEM;
+	} else {
+		HAL_MSG_MEDIUM("vidc_hal_alloc:ptr=%p,size=%d",
+					   alloc->kvaddr, size);
+		vmem->mem_size = alloc->size;
+		vmem->mem_data = alloc;
+		vmem->align_virtual_addr = (u8 *) alloc->kvaddr;
+		vmem->align_device_addr = (u8 *)alloc->device_addr;
+	}
+	return 0;
+}
+
+static void vidc_hal_free(struct smem_client *clnt, struct msm_smem *mem)
+{
+	msm_smem_free(clnt, mem);
+}
+
+static void write_register(u8 *base_addr, u32 reg, u32 value, u8 *vaddr)
+{
+	u32 hwiosymaddr = reg;
+
+	reg &= REG_ADDR_OFFSET_BITMASK;
+	if (reg == (u32)VIDC_CPU_CS_SCIACMDARG2) {
+		/* workaround to offset of FW bias */
+		struct hfi_queue_header *qhdr;
+		struct hfi_queue_table_header *qtbl_hdr =
+			(struct hfi_queue_table_header *)vaddr;
+
+		qhdr = VIDC_IFACEQ_GET_QHDR_START_ADDR(qtbl_hdr, 0);
+		qhdr->qhdr_start_addr -= HFI_VIRTIO_FW_BIAS;
+
+		qhdr = VIDC_IFACEQ_GET_QHDR_START_ADDR(qtbl_hdr, 1);
+		qhdr->qhdr_start_addr -= HFI_VIRTIO_FW_BIAS;
+
+		qhdr = VIDC_IFACEQ_GET_QHDR_START_ADDR(qtbl_hdr, 2);
+		qhdr->qhdr_start_addr -= HFI_VIRTIO_FW_BIAS;
+		value -= HFI_VIRTIO_FW_BIAS;
+	}
+
+	hwiosymaddr = ((u32)base_addr + (hwiosymaddr));
+	HAL_MSG_LOW("Base addr: 0x%x, written to: 0x%x, Value: 0x%x...",
+			(u32)base_addr, hwiosymaddr, value);
+	writel_relaxed(value, hwiosymaddr);
+	wmb();
+}
+
+static int read_register(u8 *base_addr, u32 reg)
+{
+	int rc = readl_relaxed((u32)base_addr + reg);
+	rmb();
+	return rc;
+}
+
+static int vidc_hal_iface_cmdq_write(struct hal_device *device, void *pkt)
+{
+	u32 rx_req_is_set = 0;
+	struct vidc_iface_q_info *q_info;
+	int result = -EPERM;
+
+	if (!device || !pkt) {
+		HAL_MSG_ERROR("Invalid Params in %s", __func__);
+		return -EINVAL;
+	}
+
+	spin_lock(&device->write_lock);
+	q_info = &device->iface_queues[VIDC_IFACEQ_CMDQ_IDX];
+	if (!q_info) {
+		HAL_MSG_ERROR("cannot write to shared Q's");
+		goto err_q_write;
+	}
+
+	if (!write_queue(q_info, (u8 *)pkt, &rx_req_is_set)) {
+		if (rx_req_is_set)
+			write_register(device->hal_data->register_base_addr,
+				VIDC_CPU_IC_SOFTINT,
+				1 << VIDC_CPU_IC_SOFTINT_H2A_SHFT, 0);
+		result = 0;
+	} else {
+		HAL_MSG_ERROR("vidc_hal_iface_cmdq_write:queue_full");
+	}
+err_q_write:
+	spin_unlock(&device->write_lock);
+	return result;
+}
+
+int vidc_hal_iface_msgq_read(struct hal_device *device, void *pkt)
+{
+	u32 tx_req_is_set = 0;
+	int rc = 0;
+	struct vidc_iface_q_info *q_info;
+
+	if (!pkt) {
+		HAL_MSG_ERROR("Invalid Params in %s", __func__);
+		return -EINVAL;
+	}
+	spin_lock(&device->read_lock);
+	if (device->iface_queues[VIDC_IFACEQ_MSGQ_IDX].
+		q_array.align_virtual_addr == 0) {
+		HAL_MSG_ERROR("cannot read from shared MSG Q's");
+		rc = -ENODATA;
+		goto read_error;
+	}
+	q_info = &device->iface_queues[VIDC_IFACEQ_MSGQ_IDX];
+
+	if (!read_queue(q_info, (u8 *)pkt, &tx_req_is_set)) {
+		if (tx_req_is_set)
+			write_register(device->hal_data->register_base_addr,
+				VIDC_CPU_IC_SOFTINT,
+				1 << VIDC_CPU_IC_SOFTINT_H2A_SHFT, 0);
+		rc = 0;
+	} else {
+		HAL_MSG_ERROR("vidc_hal_iface_msgq_read:queue_empty");
+		rc = -ENODATA;
+	}
+read_error:
+	spin_unlock(&device->read_lock);
+	return rc;
+}
+
+int vidc_hal_iface_dbgq_read(struct hal_device *device, void *pkt)
+{
+	u32 tx_req_is_set = 0;
+	int rc = 0;
+	struct vidc_iface_q_info *q_info;
+
+	if (!pkt) {
+		HAL_MSG_ERROR("Invalid Params in %s", __func__);
+		return -EINVAL;
+	}
+	spin_lock(&device->read_lock);
+	if (device->iface_queues[VIDC_IFACEQ_DBGQ_IDX].
+		q_array.align_virtual_addr == 0) {
+		HAL_MSG_ERROR("cannot read from shared DBG Q's");
+		rc = -ENODATA;
+		goto dbg_error;
+	}
+	q_info = &device->iface_queues[VIDC_IFACEQ_DBGQ_IDX];
+	if (!read_queue(q_info, (u8 *)pkt, &tx_req_is_set)) {
+		if (tx_req_is_set)
+			write_register(device->hal_data->register_base_addr,
+			VIDC_CPU_IC_SOFTINT,
+			1 << VIDC_CPU_IC_SOFTINT_H2A_SHFT, 0);
+		rc = 0;
+	} else {
+		HAL_MSG_MEDIUM("vidc_hal_iface_dbgq_read:queue_empty");
+		rc = -ENODATA;
+	}
+dbg_error:
+	spin_unlock(&device->read_lock);
+	return rc;
+}
+
+static void vidc_hal_set_queue_hdr_defaults(struct hfi_queue_header *q_hdr)
+{
+	q_hdr->qhdr_status = 0x1;
+	q_hdr->qhdr_type = VIDC_IFACEQ_DFLT_QHDR;
+	q_hdr->qhdr_q_size = VIDC_IFACEQ_QUEUE_SIZE;
+	q_hdr->qhdr_pkt_size = 0;
+	q_hdr->qhdr_rx_wm = 0x1;
+	q_hdr->qhdr_tx_wm = 0x1;
+	q_hdr->qhdr_rx_req = 0x1;
+	q_hdr->qhdr_tx_req = 0x0;
+	q_hdr->qhdr_rx_irq_status = 0x0;
+	q_hdr->qhdr_tx_irq_status = 0x0;
+	q_hdr->qhdr_read_idx = 0x0;
+	q_hdr->qhdr_write_idx = 0x0;
+}
+
+static void vidc_hal_interface_queues_release(struct hal_device *device)
+{
+	int i;
+	for (i = 0; i < VIDC_IFACEQ_NUMQ; i++) {
+		vidc_hal_free(device->hal_client,
+			device->iface_queues[i].q_array.mem_data);
+		device->iface_queues[i].q_hdr = NULL;
+		device->iface_queues[i].q_array.mem_data = NULL;
+		device->iface_queues[i].q_array.align_virtual_addr = NULL;
+		device->iface_queues[i].q_array.align_device_addr = NULL;
+	}
+	vidc_hal_free(device->hal_client,
+				device->iface_q_table.mem_data);
+	device->iface_q_table.align_virtual_addr = NULL;
+	device->iface_q_table.align_device_addr = NULL;
+	msm_smem_delete_client(device->hal_client);
+	device->hal_client = NULL;
+}
+
+static int vidc_hal_interface_queues_init(struct hal_device *dev)
+{
+	struct hfi_queue_table_header *q_tbl_hdr;
+	struct hfi_queue_header *q_hdr;
+	u8 i;
+	int rc = 0;
+	struct vidc_iface_q_info *iface_q;
+
+	rc = vidc_hal_alloc((void *) &dev->iface_q_table,
+					dev->hal_client,
+			VIDC_IFACEQ_TABLE_SIZE, 1, 0);
+	if (rc) {
+		HAL_MSG_ERROR("%s:iface_q_table_alloc_fail", __func__);
+		return -ENOMEM;
+	}
+	q_tbl_hdr = (struct hfi_queue_table_header *)
+			dev->iface_q_table.align_virtual_addr;
+	q_tbl_hdr->qtbl_version = 0;
+	q_tbl_hdr->qtbl_size = VIDC_IFACEQ_TABLE_SIZE;
+	q_tbl_hdr->qtbl_qhdr0_offset = sizeof(
+		struct hfi_queue_table_header);
+	q_tbl_hdr->qtbl_qhdr_size = sizeof(
+		struct hfi_queue_header);
+	q_tbl_hdr->qtbl_num_q = VIDC_IFACEQ_NUMQ;
+	q_tbl_hdr->qtbl_num_active_q = VIDC_IFACEQ_NUMQ;
+
+	for (i = 0; i < VIDC_IFACEQ_NUMQ; i++) {
+		iface_q = &dev->iface_queues[i];
+		rc = vidc_hal_alloc((void *) &iface_q->q_array,
+				dev->hal_client, VIDC_IFACEQ_QUEUE_SIZE,
+				1, 0);
+		if (rc) {
+			HAL_MSG_ERROR("%s:iface_q_table_alloc[%d]_fail",
+						__func__, i);
+			vidc_hal_interface_queues_release(dev);
+			return -ENOMEM;
+		} else {
+			iface_q->q_hdr =
+				VIDC_IFACEQ_GET_QHDR_START_ADDR(
+			dev->iface_q_table.align_virtual_addr, i);
+			vidc_hal_set_queue_hdr_defaults(iface_q->q_hdr);
+		}
+	}
+
+	iface_q = &dev->iface_queues[VIDC_IFACEQ_CMDQ_IDX];
+	q_hdr = iface_q->q_hdr;
+	q_hdr->qhdr_start_addr = (u32)
+		iface_q->q_array.align_device_addr;
+	q_hdr->qhdr_type |= HFI_Q_ID_HOST_TO_CTRL_CMD_Q;
+
+	iface_q = &dev->iface_queues[VIDC_IFACEQ_MSGQ_IDX];
+	q_hdr = iface_q->q_hdr;
+	q_hdr->qhdr_start_addr = (u32)
+		iface_q->q_array.align_device_addr;
+	q_hdr->qhdr_type |= HFI_Q_ID_CTRL_TO_HOST_MSG_Q;
+
+	iface_q = &dev->iface_queues[VIDC_IFACEQ_DBGQ_IDX];
+	q_hdr = iface_q->q_hdr;
+	q_hdr->qhdr_start_addr = (u32)
+		iface_q->q_array.align_device_addr;
+	q_hdr->qhdr_type |= HFI_Q_ID_CTRL_TO_HOST_DEBUG_Q;
+	write_register(dev->hal_data->register_base_addr,
+		VIDC_CPU_CS_SCIACMDARG2,
+		(u32) dev->iface_q_table.align_device_addr,
+		dev->iface_q_table.align_virtual_addr);
+	write_register(dev->hal_data->register_base_addr,
+		VIDC_CPU_CS_SCIACMDARG1, 0x01,
+		dev->iface_q_table.align_virtual_addr);
+	return 0;
+}
+
+static int vidc_hal_core_start_cpu(struct hal_device *device)
+{
+	u32 ctrl_status = 0, count = 0, rc = 0;
+	write_register(device->hal_data->register_base_addr,
+			VIDC_WRAPPER_INTR_MASK, 0, 0);
+	write_register(device->hal_data->register_base_addr,
+			VIDC_CPU_CS_SCIACMDARG3, 1, 0);
+	while (!ctrl_status && count < 25) {
+		ctrl_status = read_register(
+		device->hal_data->register_base_addr,
+		VIDC_CPU_CS_SCIACMDARG0);
+		count++;
+	}
+	if (count >= 25)
+		rc = -ETIME;
+	return rc;
+}
+
+int vidc_hal_core_init(void *device)
+{
+	struct hfi_cmd_sys_init_packet pkt;
+	int rc = 0;
+	struct hal_device *dev;
+
+	if (device) {
+		dev = device;
+	} else {
+		HAL_MSG_ERROR("%s:invalid device", __func__);
+		return -ENODEV;
+	}
+	enable_irq(dev->hal_data->irq);
+	INIT_LIST_HEAD(&dev->sess_head);
+	spin_lock_init(&dev->read_lock);
+	spin_lock_init(&dev->write_lock);
+
+	if (!dev->hal_client) {
+		dev->hal_client = msm_smem_new_client(SMEM_ION);
+		if (dev->hal_client == NULL) {
+			HAL_MSG_ERROR("Failed to alloc ION_Client");
+			rc = -ENODEV;
+			goto err_no_mem;
+		}
+
+		HAL_MSG_ERROR("Device_Virt_Address : 0x%x,"
+		"Register_Virt_Addr: 0x%x",
+		(u32) dev->hal_data->device_base_addr,
+		(u32) dev->hal_data->register_base_addr);
+
+		rc = vidc_hal_interface_queues_init(dev);
+		if (rc) {
+			HAL_MSG_ERROR("failed to init queues");
+			rc = -ENOMEM;
+			goto err_no_mem;
+		}
+	} else {
+		HAL_MSG_ERROR("hal_client exists");
+		rc = -EEXIST;
+		goto err_no_mem;
+	}
+	rc = vidc_hal_core_start_cpu(dev);
+	if (rc) {
+		HAL_MSG_ERROR("Failed to start core");
+		rc = -ENODEV;
+		goto err_no_dev;
+	}
+	pkt.size = sizeof(struct hfi_cmd_sys_init_packet);
+	pkt.packet = HFI_CMD_SYS_INIT;
+	if (vidc_hal_iface_cmdq_write(dev, &pkt)) {
+		rc = -ENOTEMPTY;
+		goto err_write_fail;
+	}
+	return rc;
+err_no_dev:
+err_write_fail:
+err_no_mem:
+	disable_irq_nosync(dev->hal_data->irq);
+	return rc;
+}
+
+int vidc_hal_core_release(void *device)
+{
+	struct hal_device *dev;
+	if (device) {
+		dev = device;
+	} else {
+		HAL_MSG_ERROR("%s:invalid device", __func__);
+		return -ENODEV;
+	}
+	write_register(dev->hal_data->register_base_addr,
+		VIDC_CPU_CS_SCIACMDARG3, 0, 0);
+	disable_irq_nosync(dev->hal_data->irq);
+	vidc_hal_interface_queues_release(dev);
+	HAL_MSG_INFO("\nHAL exited\n");
+	return 0;
+}
+
+int vidc_hal_core_pc_prep(void *device)
+{
+	struct hfi_cmd_sys_pc_prep_packet pkt;
+	int rc = 0;
+	struct hal_device *dev;
+
+	if (device) {
+		dev = device;
+	} else {
+		HAL_MSG_ERROR("%s:invalid device", __func__);
+		return -ENODEV;
+	}
+	pkt.size = sizeof(struct hfi_cmd_sys_pc_prep_packet);
+	pkt.packet_type = HFI_CMD_SYS_PC_PREP;
+	if (vidc_hal_iface_cmdq_write(dev, &pkt))
+		rc = -ENOTEMPTY;
+	return rc;
+}
+
+static void vidc_hal_core_clear_interrupt(struct hal_device *device)
+{
+	u32 intr_status = 0;
+
+	if (!device->callback)
+		return;
+
+	intr_status = read_register(
+		device->hal_data->register_base_addr,
+		VIDC_WRAPPER_INTR_STATUS);
+
+	if ((intr_status & VIDC_WRAPPER_INTR_STATUS_A2H_BMSK) ||
+		(intr_status & VIDC_WRAPPER_INTR_STATUS_A2HWD_BMSK)) {
+		device->intr_status |= intr_status;
+		HAL_MSG_ERROR("INTERRUPT for device: 0x%x: "
+			"times: %d interrupt_status: %d",
+			(u32) device, ++device->reg_count, intr_status);
+	} else {
+		HAL_MSG_ERROR("SPURIOUS_INTR for device: 0x%x: "
+			"times: %d interrupt_status: %d",
+			(u32) device, ++device->spur_count, intr_status);
+	}
+	write_register(device->hal_data->register_base_addr,
+			VIDC_CPU_CS_A2HSOFTINTCLR, 1, 0);
+	write_register(device->hal_data->register_base_addr,
+			VIDC_WRAPPER_INTR_CLEAR, intr_status, 0);
+	HAL_MSG_ERROR("Cleared WRAPPER/A2H interrupt");
+}
+
+int vidc_hal_core_set_resource(void *device,
+		struct vidc_resource_hdr *resource_hdr, void *resource_value)
+{
+	struct hfi_cmd_sys_set_resource_packet *pkt;
+	u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE];
+	int rc = 0;
+	struct hal_device *dev;
+
+	if (!device || !resource_hdr || !resource_value) {
+		HAL_MSG_ERROR("Invalid Params in %s", __func__);
+		return -EINVAL;
+	} else {
+		dev = device;
+	}
+
+	pkt = (struct hfi_cmd_sys_set_resource_packet *) packet;
+
+	pkt->size = sizeof(struct hfi_cmd_sys_set_resource_packet);
+	pkt->packet_type = HFI_CMD_SYS_SET_RESOURCE;
+	pkt->resource_handle = resource_hdr->resource_handle;
+
+	switch (resource_hdr->resource_id) {
+	case VIDC_RESOURCE_OCMEM:
+	{
+		struct hfi_resource_ocmem_type *hfioc_mem =
+			(struct hfi_resource_ocmem_type *)
+			&pkt->rg_resource_data[0];
+		struct vidc_mem_addr *vidc_oc_mem =
+			(struct vidc_mem_addr *) resource_value;
+
+		pkt->resource_type = HFI_RESOURCE_OCMEM;
+		hfioc_mem->size = (u32) vidc_oc_mem->mem_size;
+		hfioc_mem->mem = (u8 *) vidc_oc_mem->align_device_addr;
+		pkt->size += sizeof(struct hfi_resource_ocmem_type);
+		if (vidc_hal_iface_cmdq_write(dev, pkt))
+			rc = -ENOTEMPTY;
+		break;
+	}
+	default:
+		HAL_MSG_INFO("In %s called for resource %d",
+					 __func__, resource_hdr->resource_id);
+		break;
+	}
+	return rc;
+}
+
+int vidc_hal_core_release_resource(void *device,
+			struct vidc_resource_hdr *resource_hdr)
+{
+	struct hfi_cmd_sys_release_resource_packet pkt;
+	int rc = 0;
+	struct hal_device *dev;
+
+	if (!device || !resource_hdr) {
+		HAL_MSG_ERROR("Invalid Params in %s", __func__);
+		return -EINVAL;
+	} else {
+		dev = device;
+	}
+
+	pkt.size = sizeof(struct hfi_cmd_sys_release_resource_packet);
+	pkt.packet_type = HFI_CMD_SYS_RELEASE_RESOURCE;
+	pkt.resource_type = resource_hdr->resource_id;
+	pkt.resource_handle = resource_hdr->resource_handle;
+
+	if (vidc_hal_iface_cmdq_write(dev, &pkt))
+		rc = -ENOTEMPTY;
+	return rc;
+}
+
+int vidc_hal_core_ping(void *device)
+{
+	struct hfi_cmd_sys_ping_packet pkt;
+	int rc = 0;
+	struct hal_device *dev;
+
+	if (device) {
+		dev = device;
+	} else {
+		HAL_MSG_ERROR("%s:invalid device", __func__);
+		return -ENODEV;
+	}
+	pkt.size = sizeof(struct hfi_cmd_sys_ping_packet);
+	pkt.packet_type = HFI_CMD_SYS_PING;
+
+	if (vidc_hal_iface_cmdq_write(dev, &pkt))
+		rc = -ENOTEMPTY;
+	return rc;
+}
+
+int vidc_hal_session_set_property(void *sess,
+	enum hal_property ptype, void *pdata)
+{
+	u8 packet[VIDC_IFACEQ_VAR_LARGE_PKT_SIZE];
+	struct hfi_cmd_session_set_property_packet *pkt =
+		(struct hfi_cmd_session_set_property_packet *) &packet;
+	struct hal_session *session;
+
+	if (!sess || !pdata) {
+		HAL_MSG_ERROR("Invalid Params in %s", __func__);
+		return -EINVAL;
+	} else {
+		session = sess;
+	}
+
+	HAL_MSG_INFO("IN func: %s, with property id: %d", __func__, ptype);
+	pkt->size = sizeof(struct hfi_cmd_session_set_property_packet)
+		- sizeof(u32);
+	pkt->packet_type = HFI_CMD_SESSION_SET_PROPERTY;
+	pkt->session_id = (u32) session;
+	pkt->num_properties = 1;
+
+	switch (ptype) {
+	case HAL_CONFIG_FRAME_RATE:
+	{
+		struct hfi_frame_rate *hfi_fps;
+		pkt->rg_property_data[0] = HFI_PROPERTY_CONFIG_FRAME_RATE;
+		hfi_fps = (struct hfi_frame_rate *) &pkt->rg_property_data[1];
+		memcpy(hfi_fps, (struct hfi_frame_rate *)
+			pdata, sizeof(struct hfi_frame_rate));
+		pkt->size += sizeof(u32) + sizeof(struct hfi_frame_rate);
+		break;
+	}
+	case HAL_PARAM_UNCOMPRESSED_FORMAT_SELECT:
+	{
+		struct hfi_uncompressed_format_select *hfi_buf_fmt;
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT;
+		hfi_buf_fmt =
+		(struct hfi_uncompressed_format_select *)
+		&pkt->rg_property_data[1];
+		memcpy(hfi_buf_fmt, (struct hfi_uncompressed_format_select *)
+			pdata, sizeof(struct hfi_uncompressed_format_select));
+		pkt->size += sizeof(u32) + sizeof(struct
+			hfi_uncompressed_format_select);
+		break;
+	}
+	case HAL_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO:
+		break;
+	case HAL_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO:
+		break;
+	case HAL_PARAM_EXTRA_DATA_HEADER_CONFIG:
+		break;
+	case HAL_PARAM_FRAME_SIZE:
+	{
+		struct hfi_frame_size *hfi_rect;
+		pkt->rg_property_data[0] = HFI_PROPERTY_PARAM_FRAME_SIZE;
+		hfi_rect = (struct hfi_frame_size *) &pkt->rg_property_data[1];
+		memcpy(hfi_rect, (struct hfi_frame_size *) pdata,
+			sizeof(struct hfi_frame_size));
+		pkt->size += sizeof(u32) + sizeof(struct hfi_frame_size);
+		break;
+	}
+	case HAL_CONFIG_REALTIME:
+	{
+		struct hfi_enable *hfi;
+		pkt->rg_property_data[0] = HFI_PROPERTY_CONFIG_REALTIME;
+		hfi = (struct hfi_enable *) &pkt->rg_property_data[1];
+		memcpy(hfi, (struct hfi_enable *) pdata,
+				sizeof(struct hfi_enable));
+		pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
+		break;
+	}
+	case HAL_PARAM_BUFFER_COUNT_ACTUAL:
+	{
+		struct hfi_buffer_count_actual *hfi;
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL;
+		hfi = (struct hfi_buffer_count_actual *)
+				&pkt->rg_property_data[1];
+		memcpy(hfi, (struct hfi_buffer_count_actual *) pdata,
+			sizeof(struct hfi_buffer_count_actual));
+		pkt->size += sizeof(u32) + sizeof(struct
+					hfi_buffer_count_actual);
+		break;
+	}
+	case HAL_PARAM_NAL_STREAM_FORMAT_SELECT:
+	{
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SELECT;
+		pkt->rg_property_data[1] = (enum HFI_NAL_STREAM_FORMAT)pdata;
+		pkt->size += sizeof(u32) + sizeof(enum HFI_NAL_STREAM_FORMAT);
+		break;
+	}
+	case HAL_PARAM_VDEC_OUTPUT_ORDER:
+	{
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VDEC_OUTPUT_ORDER;
+		pkt->rg_property_data[1] = (enum HFI_OUTPUT_ORDER)pdata;
+		pkt->size += sizeof(u32) + sizeof(enum HFI_OUTPUT_ORDER);
+		break;
+	}
+	case HAL_PARAM_VDEC_PICTURE_TYPE_DECODE:
+	{
+		struct hfi_enable_picture *hfi;
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VDEC_PICTURE_TYPE_DECODE;
+		hfi = (struct hfi_enable_picture *) &pkt->rg_property_data[1];
+		hfi->picture_type = (u32) pdata;
+		pkt->size += sizeof(u32) + sizeof(struct hfi_enable_picture);
+		break;
+	}
+	case HAL_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO:
+	{
+		struct hfi_enable *hfi;
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO;
+		hfi = (struct hfi_enable *) &pkt->rg_property_data[1];
+		memcpy(hfi, (struct hfi_enable *) pdata,
+				sizeof(struct hfi_enable));
+		pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
+		break;
+	}
+	case HAL_CONFIG_VDEC_POST_LOOP_DEBLOCKER:
+	{
+		struct hfi_enable *hfi;
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER;
+		hfi = (struct hfi_enable *) &pkt->rg_property_data[1];
+		memcpy(hfi, (struct hfi_enable *) pdata,
+				sizeof(struct hfi_enable));
+		pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
+		break;
+	}
+	case HAL_PARAM_VDEC_MULTI_STREAM:
+	{
+		struct hfi_multi_stream *hfi;
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM;
+		hfi = (struct hfi_multi_stream *) &pkt->rg_property_data[1];
+		memcpy(hfi, (struct hfi_multi_stream *)pdata,
+				sizeof(struct hfi_multi_stream));
+		pkt->size += sizeof(u32) + sizeof(struct hfi_multi_stream);
+		break;
+	}
+	case HAL_PARAM_VDEC_DISPLAY_PICTURE_BUFFER_COUNT:
+	{
+		struct hfi_display_picture_buffer_count *hfi_disp_buf;
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VDEC_DISPLAY_PICTURE_BUFFER_COUNT;
+		hfi_disp_buf = (struct hfi_display_picture_buffer_count *)
+			&pkt->rg_property_data[1];
+		memcpy(hfi_disp_buf,
+			(struct hfi_display_picture_buffer_count *)pdata,
+			sizeof(struct hfi_display_picture_buffer_count));
+		pkt->size += sizeof(u32) +
+			sizeof(struct hfi_display_picture_buffer_count);
+		break;
+	}
+	case HAL_PARAM_DIVX_FORMAT:
+	{
+		pkt->rg_property_data[0] = HFI_PROPERTY_PARAM_DIVX_FORMAT;
+		pkt->rg_property_data[1] = (enum HFI_DIVX_FORMAT)pdata;
+		pkt->size += sizeof(u32) + sizeof(enum HFI_DIVX_FORMAT);
+		break;
+	}
+	case HAL_CONFIG_VDEC_MB_ERROR_MAP_REPORTING:
+	{
+		struct hfi_enable *hfi;
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_CONFIG_VDEC_MB_ERROR_MAP_REPORTING;
+		hfi = (struct hfi_enable *) &pkt->rg_property_data[1];
+		memcpy(hfi, (struct hfi_enable *) pdata,
+				sizeof(struct hfi_enable));
+		pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
+		break;
+	}
+	case HAL_PARAM_VDEC_CONTINUE_DATA_TRANSFER:
+	{
+		struct hfi_enable *enable;
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VDEC_CONTINUE_DATA_TRANSFER;
+		enable = (struct hfi_enable *) &pkt->rg_property_data[1];
+		memcpy(enable, (struct hfi_enable *) pdata,
+				sizeof(struct hfi_enable));
+		pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
+		break;
+	}
+	case HAL_CONFIG_VENC_REQUEST_IFRAME:
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_CONFIG_VENC_REQUEST_IFRAME;
+		break;
+	case HAL_PARAM_VENC_MPEG4_SHORT_HEADER:
+		break;
+	case HAL_PARAM_VENC_MPEG4_AC_PREDICTION:
+		break;
+	case HAL_CONFIG_VENC_TARGET_BITRATE:
+	{
+		struct hfi_bitrate *hfi;
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE;
+		hfi = (struct hfi_bitrate *) &pkt->rg_property_data[1];
+		hfi->bit_rate = ((struct hfi_bitrate *)pdata)->bit_rate;
+		pkt->size += sizeof(u32) + sizeof(struct hfi_bitrate);
+		break;
+	}
+	case HAL_PARAM_PROFILE_LEVEL_CURRENT:
+	{
+		struct hfi_profile_level *hfi_profile_level;
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT;
+		hfi_profile_level = (struct hfi_profile_level *)
+		&pkt->rg_property_data[1];
+		memcpy(hfi_profile_level, (struct hfi_profile_level *) pdata,
+			sizeof(struct hfi_profile_level));
+		pkt->size += sizeof(u32) + sizeof(struct hfi_profile_level);
+		break;
+	}
+	case HAL_PARAM_VENC_H264_ENTROPY_CONTROL:
+	{
+		struct hfi_h264_entropy_control *hfi;
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_H264_ENTROPY_CONTROL;
+		hfi = (struct hfi_h264_entropy_control *)
+			&pkt->rg_property_data[1];
+		memcpy(hfi, (struct hfi_h264_entropy_control *) pdata,
+				sizeof(struct hfi_h264_entropy_control));
+		pkt->size += sizeof(u32) + sizeof(
+			struct hfi_h264_entropy_control);
+		break;
+	}
+	case HAL_PARAM_VENC_RATE_CONTROL:
+	{
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_RATE_CONTROL;
+		pkt->rg_property_data[1] = (enum HFI_RATE_CONTROL)pdata;
+		pkt->size += sizeof(u32) + sizeof(enum HFI_RATE_CONTROL);
+		break;
+	}
+	case HAL_PARAM_VENC_MPEG4_TIME_RESOLUTION:
+	{
+		struct hfi_mpeg4_time_resolution *hfi;
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_MPEG4_TIME_RESOLUTION;
+		hfi = (struct hfi_mpeg4_time_resolution *)
+			&pkt->rg_property_data[1];
+		hfi->time_increment_resolution =
+			((struct hal_mpeg4_time_resolution *)pdata)->
+					time_increment_resolution;
+		pkt->size += sizeof(u32) + sizeof(
+			struct hfi_mpeg4_time_resolution);
+		break;
+	}
+	case HAL_PARAM_VENC_MPEG4_HEADER_EXTENSION:
+	{
+		struct hfi_mpeg4_header_extension *hfi;
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_MPEG4_HEADER_EXTENSION;
+		hfi = (struct hfi_mpeg4_header_extension *)
+		&pkt->rg_property_data[1];
+		hfi->header_extension = (u32) pdata;
+		pkt->size += sizeof(u32) +
+			sizeof(struct hfi_mpeg4_header_extension);
+		break;
+	}
+	case HAL_PARAM_VENC_H264_DEBLOCK_CONTROL:
+	{
+		struct hfi_h264_db_control *hfi;
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_H264_DEBLOCK_CONTROL;
+		hfi = (struct hfi_h264_db_control *) &pkt->rg_property_data[1];
+		memcpy(hfi, (struct hfi_h264_db_control *) pdata,
+				sizeof(struct hfi_h264_db_control));
+		pkt->size += sizeof(u32) +
+			sizeof(struct hfi_h264_db_control);
+		break;
+	}
+	case HAL_PARAM_VENC_TEMPORAL_SPATIAL_TRADEOFF:
+	{
+		struct hfi_temporal_spatial_tradeoff *hfi;
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_TEMPORAL_SPATIAL_TRADEOFF;
+		hfi = (struct hfi_temporal_spatial_tradeoff *)
+		&pkt->rg_property_data[1];
+		hfi->ts_factor = ((struct hfi_temporal_spatial_tradeoff *)
+					pdata)->ts_factor;
+		pkt->size += sizeof(u32) +
+			sizeof(struct hfi_temporal_spatial_tradeoff);
+		break;
+	}
+	case HAL_PARAM_VENC_SESSION_QP:
+	{
+		struct hfi_quantization *hfi;
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_SESSION_QP;
+		hfi = (struct hfi_quantization *) &pkt->rg_property_data[1];
+		memcpy(hfi, (struct hfi_quantization *) pdata,
+				sizeof(struct hfi_quantization));
+		pkt->size += sizeof(u32) + sizeof(struct hfi_quantization);
+		break;
+	}
+	case HAL_CONFIG_VENC_INTRA_PERIOD:
+	{
+		struct hfi_intra_period *hfi;
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD;
+		hfi = (struct hfi_intra_period *) &pkt->rg_property_data[1];
+		memcpy(hfi, (struct hfi_intra_period *) pdata,
+				sizeof(struct hfi_intra_period));
+		pkt->size += sizeof(u32) + sizeof(struct hfi_intra_period);
+		break;
+	}
+	case HAL_CONFIG_VENC_IDR_PERIOD:
+	{
+		struct hfi_idr_period *hfi;
+		pkt->rg_property_data[0] = HFI_PROPERTY_CONFIG_VENC_IDR_PERIOD;
+		hfi = (struct hfi_idr_period *) &pkt->rg_property_data[1];
+		hfi->idr_period = ((struct hfi_idr_period *) pdata)->idr_period;
+		pkt->size += sizeof(u32) + sizeof(struct hfi_idr_period);
+		break;
+	}
+	case HAL_CONFIG_VPE_OPERATIONS:
+		break;
+	case HAL_PARAM_VENC_INTRA_REFRESH:
+	{
+		struct hfi_intra_refresh *hfi;
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH;
+		hfi = (struct hfi_intra_refresh *) &pkt->rg_property_data[1];
+		memcpy(hfi, (struct hfi_intra_refresh *) pdata,
+				sizeof(struct hfi_intra_refresh));
+		pkt->size += sizeof(u32) + sizeof(struct hfi_intra_refresh);
+		break;
+	}
+	case HAL_PARAM_VENC_MULTI_SLICE_CONTROL:
+	{
+		struct hfi_multi_slice_control *hfi;
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_MULTI_SLICE_CONTROL;
+		hfi = (struct hfi_multi_slice_control *)
+				&pkt->rg_property_data[1];
+		memcpy(hfi, (struct hfi_multi_slice_control *) pdata,
+				sizeof(struct hfi_multi_slice_control));
+		pkt->size += sizeof(u32) + sizeof(struct
+						hfi_multi_slice_control);
+		break;
+	}
+	case HAL_CONFIG_VPE_DEINTERLACE:
+		break;
+	case HAL_SYS_DEBUG_CONFIG:
+	{
+		struct hfi_debug_config *hfi;
+		pkt->rg_property_data[0] = HFI_PROPERTY_SYS_DEBUG_CONFIG;
+		hfi = (struct hfi_debug_config *) &pkt->rg_property_data[1];
+		memcpy(hfi, (struct hfi_debug_config *) pdata,
+				sizeof(struct hfi_debug_config));
+		pkt->size = sizeof(struct hfi_cmd_sys_set_property_packet) +
+			sizeof(struct hfi_debug_config);
+		break;
+	}
+	/* FOLLOWING PROPERTIES ARE NOT IMPLEMENTED IN CORE YET */
+	case HAL_CONFIG_BUFFER_REQUIREMENTS:
+	case HAL_CONFIG_PRIORITY:
+	case HAL_CONFIG_BATCH_INFO:
+	case HAL_PARAM_METADATA_PASS_THROUGH:
+	case HAL_SYS_IDLE_INDICATOR:
+	case HAL_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED:
+	case HAL_PARAM_INTERLACE_FORMAT_SUPPORTED:
+	case HAL_PARAM_CHROMA_SITE:
+	case HAL_PARAM_PROPERTIES_SUPPORTED:
+	case HAL_PARAM_PROFILE_LEVEL_SUPPORTED:
+	case HAL_PARAM_CAPABILITY_SUPPORTED:
+	case HAL_PARAM_NAL_STREAM_FORMAT_SUPPORTED:
+	case HAL_PARAM_MULTI_VIEW_FORMAT:
+	case HAL_PARAM_MAX_SEQUENCE_HEADER_SIZE:
+	case HAL_PARAM_CODEC_SUPPORTED:
+	case HAL_PARAM_VDEC_MULTI_VIEW_SELECT:
+	case HAL_PARAM_VDEC_MB_QUANTIZATION:
+	case HAL_PARAM_VDEC_NUM_CONCEALED_MB:
+	case HAL_PARAM_VDEC_H264_ENTROPY_SWITCHING:
+	case HAL_PARAM_VENC_SLICE_DELIVERY_MODE:
+	case HAL_PARAM_VENC_MPEG4_DATA_PARTITIONING:
+
+	case HAL_CONFIG_BUFFER_COUNT_ACTUAL:
+	case HAL_CONFIG_VDEC_MULTI_STREAM:
+	case HAL_PARAM_VENC_MULTI_SLICE_INFO:
+	case HAL_CONFIG_VENC_TIMESTAMP_SCALE:
+	case HAL_PARAM_VENC_LOW_LATENCY:
+	default:
+		HAL_MSG_INFO("DEFAULT: Calling 0x%x", ptype);
+		break;
+	}
+	if (vidc_hal_iface_cmdq_write(session->device, pkt))
+		return -ENOTEMPTY;
+	return 0;
+}
+
+int vidc_hal_session_get_property(void *sess,
+	enum hal_property ptype, void *pdata)
+{
+	struct hal_session *session;
+
+	if (!sess || !pdata) {
+		HAL_MSG_ERROR("Invalid Params in %s", __func__);
+		return -EINVAL;
+	} else {
+		session = sess;
+	}
+	HAL_MSG_INFO("IN func: %s, with property id: %d", __func__, ptype);
+
+	switch (ptype) {
+	case HAL_CONFIG_FRAME_RATE:
+		break;
+	case HAL_PARAM_UNCOMPRESSED_FORMAT_SELECT:
+		break;
+	case HAL_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO:
+		break;
+	case HAL_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO:
+		break;
+	case HAL_PARAM_EXTRA_DATA_HEADER_CONFIG:
+		break;
+	case HAL_PARAM_FRAME_SIZE:
+		break;
+	case HAL_CONFIG_REALTIME:
+		break;
+	case HAL_PARAM_BUFFER_COUNT_ACTUAL:
+		break;
+	case HAL_PARAM_NAL_STREAM_FORMAT_SELECT:
+		break;
+	case HAL_PARAM_VDEC_OUTPUT_ORDER:
+		break;
+	case HAL_PARAM_VDEC_PICTURE_TYPE_DECODE:
+		break;
+	case HAL_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO:
+		break;
+	case HAL_CONFIG_VDEC_POST_LOOP_DEBLOCKER:
+		break;
+	case HAL_PARAM_VDEC_MULTI_STREAM:
+		break;
+	case HAL_PARAM_VDEC_DISPLAY_PICTURE_BUFFER_COUNT:
+		break;
+	case HAL_PARAM_DIVX_FORMAT:
+		break;
+	case HAL_CONFIG_VDEC_MB_ERROR_MAP_REPORTING:
+		break;
+	case HAL_PARAM_VDEC_CONTINUE_DATA_TRANSFER:
+		break;
+	case HAL_CONFIG_VDEC_MB_ERROR_MAP:
+		break;
+	case HAL_CONFIG_VENC_REQUEST_IFRAME:
+		break;
+	case HAL_PARAM_VENC_MPEG4_SHORT_HEADER:
+		break;
+	case HAL_PARAM_VENC_MPEG4_AC_PREDICTION:
+		break;
+	case HAL_CONFIG_VENC_TARGET_BITRATE:
+		break;
+	case HAL_PARAM_PROFILE_LEVEL_CURRENT:
+		break;
+	case HAL_PARAM_VENC_H264_ENTROPY_CONTROL:
+		break;
+	case HAL_PARAM_VENC_RATE_CONTROL:
+		break;
+	case HAL_PARAM_VENC_MPEG4_TIME_RESOLUTION:
+		break;
+	case HAL_PARAM_VENC_MPEG4_HEADER_EXTENSION:
+		break;
+	case HAL_PARAM_VENC_H264_DEBLOCK_CONTROL:
+		break;
+	case HAL_PARAM_VENC_TEMPORAL_SPATIAL_TRADEOFF:
+		break;
+	case HAL_PARAM_VENC_SESSION_QP:
+		break;
+	case HAL_CONFIG_VENC_INTRA_PERIOD:
+		break;
+	case HAL_CONFIG_VENC_IDR_PERIOD:
+		break;
+	case HAL_CONFIG_VPE_OPERATIONS:
+		break;
+	case HAL_PARAM_VENC_INTRA_REFRESH:
+		break;
+	case HAL_PARAM_VENC_MULTI_SLICE_CONTROL:
+		break;
+	case HAL_CONFIG_VPE_DEINTERLACE:
+		break;
+	case HAL_SYS_DEBUG_CONFIG:
+		break;
+	/*FOLLOWING PROPERTIES ARE NOT IMPLEMENTED IN CORE YET*/
+	case HAL_CONFIG_BUFFER_REQUIREMENTS:
+	case HAL_CONFIG_PRIORITY:
+	case HAL_CONFIG_BATCH_INFO:
+	case HAL_PARAM_METADATA_PASS_THROUGH:
+	case HAL_SYS_IDLE_INDICATOR:
+	case HAL_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED:
+	case HAL_PARAM_INTERLACE_FORMAT_SUPPORTED:
+	case HAL_PARAM_CHROMA_SITE:
+	case HAL_PARAM_PROPERTIES_SUPPORTED:
+	case HAL_PARAM_PROFILE_LEVEL_SUPPORTED:
+	case HAL_PARAM_CAPABILITY_SUPPORTED:
+	case HAL_PARAM_NAL_STREAM_FORMAT_SUPPORTED:
+	case HAL_PARAM_MULTI_VIEW_FORMAT:
+	case HAL_PARAM_MAX_SEQUENCE_HEADER_SIZE:
+	case HAL_PARAM_CODEC_SUPPORTED:
+	case HAL_PARAM_VDEC_MULTI_VIEW_SELECT:
+	case HAL_PARAM_VDEC_MB_QUANTIZATION:
+	case HAL_PARAM_VDEC_NUM_CONCEALED_MB:
+	case HAL_PARAM_VDEC_H264_ENTROPY_SWITCHING:
+	case HAL_PARAM_VENC_SLICE_DELIVERY_MODE:
+	case HAL_PARAM_VENC_MPEG4_DATA_PARTITIONING:
+
+	case HAL_CONFIG_BUFFER_COUNT_ACTUAL:
+	case HAL_CONFIG_VDEC_MULTI_STREAM:
+	case HAL_PARAM_VENC_MULTI_SLICE_INFO:
+	case HAL_CONFIG_VENC_TIMESTAMP_SCALE:
+	case HAL_PARAM_VENC_LOW_LATENCY:
+	default:
+		HAL_MSG_INFO("DEFAULT: Calling 0x%x", ptype);
+		break;
+	}
+	return 0;
+}
+
+void *vidc_hal_session_init(void *device, u32 session_id,
+	enum hal_domain session_type, enum hal_video_codec codec_type)
+{
+	struct hfi_cmd_sys_session_init_packet pkt;
+	struct hal_session *new_session;
+	struct hal_device *dev;
+
+	if (device) {
+		dev = device;
+	} else {
+		HAL_MSG_ERROR("%s:invalid device", __func__);
+		return NULL;
+	}
+
+	new_session = (struct hal_session *)
+		kzalloc(sizeof(struct hal_session), GFP_KERNEL);
+	new_session->session_id = (u32) session_id;
+	if (session_type == 1)
+		new_session->is_decoder = 0;
+	else if (session_type == 2)
+		new_session->is_decoder = 1;
+	new_session->device = dev;
+	list_add_tail(&new_session->list, &dev->sess_head);
+	pkt.size = sizeof(struct hfi_cmd_sys_session_init_packet);
+	pkt.packet = HFI_CMD_SYS_SESSION_INIT;
+	pkt.session_id = (u32) new_session;
+	pkt.session_domain = session_type;
+	pkt.session_codec = codec_type;
+	if (vidc_hal_iface_cmdq_write(dev, &pkt))
+		return NULL;
+	return (void *) new_session;
+}
+
+static int vidc_hal_send_session_cmd(void *session_id,
+	 enum HFI_COMMAND pkt_type)
+{
+	struct vidc_hal_session_cmd_pkt pkt;
+	int rc = 0;
+	struct hal_session *session;
+
+	if (session_id) {
+		session = session_id;
+	} else {
+		HAL_MSG_ERROR("%s:invalid session", __func__);
+		return -ENODEV;
+	}
+
+	pkt.size = sizeof(struct vidc_hal_session_cmd_pkt);
+	pkt.packet_type = pkt_type;
+	pkt.session_id = (u32) session;
+
+	if (vidc_hal_iface_cmdq_write(session->device, &pkt))
+		rc = -ENOTEMPTY;
+	return rc;
+}
+
+int vidc_hal_session_end(void *session)
+{
+	return vidc_hal_send_session_cmd(session,
+		HFI_CMD_SYS_SESSION_END);
+}
+
+int vidc_hal_session_abort(void *session)
+{
+	return vidc_hal_send_session_cmd(session,
+		HFI_CMD_SYS_SESSION_ABORT);
+}
+
+int vidc_hal_session_set_buffers(void *sess,
+	struct vidc_buffer_addr_info *buffer_info)
+{
+	struct hfi_cmd_session_set_buffers_packet *pkt;
+	u8 packet[VIDC_IFACEQ_VAR_LARGE_PKT_SIZE];
+	int rc = 0;
+	u16 i;
+	struct hal_session *session;
+
+	if (!sess || !buffer_info) {
+		HAL_MSG_ERROR("Invalid Params in %s", __func__);
+		return -EINVAL;
+	} else {
+		session = sess;
+	}
+
+	if (buffer_info->buffer_type == HAL_BUFFER_INPUT)
+		return 0;
+
+	pkt = (struct hfi_cmd_session_set_buffers_packet *)packet;
+
+	pkt->size = sizeof(struct hfi_cmd_session_set_buffers_packet) +
+		((buffer_info->num_buffers - 1) * sizeof(u32));
+	pkt->packet_type = HFI_CMD_SESSION_SET_BUFFERS;
+	pkt->session_id = (u32) session;
+	pkt->buffer_mode = HFI_BUFFER_MODE_STATIC;
+	pkt->buffer_size = buffer_info->buffer_size;
+	pkt->min_buffer_size = buffer_info->buffer_size;
+	pkt->num_buffers = buffer_info->num_buffers;
+
+	if ((buffer_info->buffer_type == HAL_BUFFER_OUTPUT) ||
+		(buffer_info->buffer_type == HAL_BUFFER_OUTPUT2)) {
+		struct hfi_buffer_info *buff;
+		pkt->extradata_size = buffer_info->extradata_size;
+		pkt->size = sizeof(struct hfi_cmd_session_set_buffers_packet) -
+			sizeof(u32) + ((buffer_info->num_buffers) *
+			sizeof(struct hfi_buffer_info));
+		buff = (struct hfi_buffer_info *) pkt->rg_buffer_info;
+		for (i = 0; i < pkt->num_buffers; i++) {
+			buff->buffer_addr =
+				buffer_info->align_device_addr;
+			buff->extradata_addr =
+				buffer_info->extradata_addr;
+		}
+	} else {
+		pkt->extradata_size = 0;
+		pkt->size = sizeof(struct hfi_cmd_session_set_buffers_packet) +
+			((buffer_info->num_buffers - 1) * sizeof(u32));
+		for (i = 0; i < pkt->num_buffers; i++)
+			pkt->rg_buffer_info[i] =
+			buffer_info->align_device_addr;
+	}
+
+	if (buffer_info->buffer_type == HAL_BUFFER_INTERNAL_SCRATCH)
+		pkt->buffer_type = HFI_BUFFER_INTERNAL_SCRATCH;
+	else if (buffer_info->buffer_type == HAL_BUFFER_INTERNAL_PERSIST)
+		pkt->buffer_type = HFI_BUFFER_INTERNAL_PERSIST;
+	else
+		pkt->buffer_type = (enum HFI_BUFFER) buffer_info->buffer_type;
+
+	if (vidc_hal_iface_cmdq_write(session->device, pkt))
+		rc = -ENOTEMPTY;
+	return rc;
+}
+
+int vidc_hal_session_release_buffers(void *sess,
+	struct vidc_buffer_addr_info *buffer_info)
+{
+	struct hfi_cmd_session_release_buffer_packet *pkt;
+	u8 packet[VIDC_IFACEQ_VAR_LARGE_PKT_SIZE];
+	int rc = 0;
+	u32 i;
+	struct hal_session *session;
+
+	if (!sess || !buffer_info) {
+		HAL_MSG_ERROR("Invalid Params in %s", __func__);
+		return -EINVAL;
+	} else {
+		session = sess;
+	}
+
+	if (buffer_info->buffer_type == HAL_BUFFER_INPUT)
+		return 0;
+
+	pkt = (struct hfi_cmd_session_release_buffer_packet *) packet;
+	pkt->size = sizeof(struct hfi_cmd_session_release_buffer_packet) +
+		((buffer_info->num_buffers - 1) * sizeof(u32));
+	pkt->packet_type = HFI_CMD_SESSION_RELEASE_BUFFERS;
+	pkt->session_id = (u32) session;
+	pkt->buffer_type = (enum HFI_BUFFER) buffer_info->buffer_type;
+	pkt->buffer_size = buffer_info->buffer_size;
+	pkt->num_buffers = buffer_info->num_buffers;
+
+	if ((buffer_info->buffer_type == HAL_BUFFER_OUTPUT) ||
+		(buffer_info->buffer_type == HAL_BUFFER_OUTPUT2)) {
+		struct hfi_buffer_info *buff;
+		buff = (struct hfi_buffer_info *) pkt->rg_buffer_info;
+		for (i = 0; i < pkt->num_buffers; i++) {
+			buff->buffer_addr =
+				buffer_info->align_device_addr;
+			buff->extradata_addr =
+				buffer_info->extradata_addr;
+		}
+		pkt->extradata_size = buffer_info->extradata_size;
+		pkt->size = sizeof(struct hfi_cmd_session_set_buffers_packet) -
+			sizeof(u32) + ((buffer_info->num_buffers) *
+			sizeof(struct hfi_buffer_info));
+	} else {
+		for (i = 0; i < pkt->num_buffers; i++)
+			pkt->rg_buffer_info[i] =
+			buffer_info->align_device_addr;
+		pkt->extradata_size = 0;
+		pkt->size = sizeof(struct hfi_cmd_session_set_buffers_packet) +
+			((buffer_info->num_buffers - 1) * sizeof(u32));
+	}
+
+	if (vidc_hal_iface_cmdq_write(session->device, pkt))
+		rc = -ENOTEMPTY;
+	return rc;
+}
+
+int vidc_hal_session_load_res(void *sess)
+{
+	return vidc_hal_send_session_cmd(sess,
+		HFI_CMD_SESSION_LOAD_RESOURCES);
+}
+
+int vidc_hal_session_release_res(void *sess)
+{
+	return vidc_hal_send_session_cmd(sess,
+		HFI_CMD_SESSION_RELEASE_RESOURCES);
+}
+
+int vidc_hal_session_start(void *sess)
+{
+	return vidc_hal_send_session_cmd(sess,
+		HFI_CMD_SESSION_START);
+}
+
+int vidc_hal_session_stop(void *sess)
+{
+	return vidc_hal_send_session_cmd(sess,
+		HFI_CMD_SESSION_STOP);
+}
+
+int vidc_hal_session_suspend(void *sess)
+{
+	return vidc_hal_send_session_cmd(sess,
+		HFI_CMD_SESSION_SUSPEND);
+}
+
+int vidc_hal_session_resume(void *sess)
+{
+	return vidc_hal_send_session_cmd(sess,
+		HFI_CMD_SESSION_RESUME);
+}
+
+int vidc_hal_session_etb(void *sess, struct vidc_frame_data *input_frame)
+{
+	int rc = 0;
+	struct hal_session *session;
+
+	if (!sess || !input_frame) {
+		HAL_MSG_ERROR("Invalid Params in %s", __func__);
+		return -EINVAL;
+	} else {
+		session = sess;
+	}
+
+	if (session->is_decoder) {
+		struct hfi_cmd_session_empty_buffer_compressed_packet pkt;
+		pkt.size = sizeof(
+			struct hfi_cmd_session_empty_buffer_compressed_packet);
+		pkt.packet_type = HFI_CMD_SESSION_EMPTY_BUFFER;
+		pkt.session_id = (u32) session;
+		pkt.timestamp_hi = (int) (((u64)input_frame->timestamp) >> 32);
+		pkt.timestamp_lo = (int) input_frame->timestamp;
+		pkt.flags = input_frame->flags;
+		pkt.mark_target = input_frame->mark_target;
+		pkt.mark_data = input_frame->mark_data;
+		pkt.offset = input_frame->offset;
+		pkt.alloc_len = input_frame->alloc_len;
+		pkt.filled_len = input_frame->filled_len;
+		pkt.input_tag = input_frame->clnt_data;
+		pkt.packet_buffer = (u8 *) input_frame->device_addr;
+		HAL_MSG_ERROR("### Q DECODER INPUT BUFFER ###");
+		if (vidc_hal_iface_cmdq_write(session->device, &pkt))
+			rc = -ENOTEMPTY;
+	} else {
+		struct hfi_cmd_session_empty_buffer_uncompressed_plane0_packet
+			pkt;
+		pkt.size = sizeof(struct
+		hfi_cmd_session_empty_buffer_uncompressed_plane0_packet);
+		pkt.packet = HFI_CMD_SESSION_EMPTY_BUFFER;
+		pkt.session_id = (u32) session;
+		pkt.view_id = 0;
+		pkt.timestamp_hi = (u32) (((u64)input_frame->timestamp) >> 32);
+		pkt.timestamp_lo = (u32) input_frame->timestamp;
+		pkt.flags = input_frame->flags;
+		pkt.mark_target = input_frame->mark_target;
+		pkt.mark_data = input_frame->mark_data;
+		pkt.offset = input_frame->offset;
+		pkt.alloc_len = input_frame->alloc_len;
+		pkt.filled_len = input_frame->filled_len;
+		pkt.input_tag = input_frame->clnt_data;
+		pkt.packet_buffer = (u8 *) input_frame->device_addr;
+		HAL_MSG_ERROR("### Q ENCODER INPUT BUFFER ###");
+		if (vidc_hal_iface_cmdq_write(session->device, &pkt))
+			rc = -ENOTEMPTY;
+	}
+	return rc;
+}
+
+int vidc_hal_session_ftb(void *sess,
+	struct vidc_frame_data *output_frame)
+{
+	struct hfi_cmd_session_fill_buffer_packet pkt;
+	int rc = 0;
+	struct hal_session *session;
+
+	if (!sess || !output_frame) {
+		HAL_MSG_ERROR("Invalid Params in %s", __func__);
+		return -EINVAL;
+	} else {
+		session = sess;
+	}
+
+	pkt.size = sizeof(struct hfi_cmd_session_fill_buffer_packet);
+	pkt.packet_type = HFI_CMD_SESSION_FILL_BUFFER;
+	pkt.session_id = (u32) session;
+	if (output_frame->buffer_type == HAL_BUFFER_OUTPUT)
+		pkt.stream_id = 0;
+	else if (output_frame->buffer_type == HAL_BUFFER_OUTPUT2)
+		pkt.stream_id = 1;
+	pkt.packet_buffer = (u8 *) output_frame->device_addr;
+	pkt.extra_data_buffer =
+		(u8 *) output_frame->extradata_addr;
+
+	HAL_MSG_INFO("### Q OUTPUT BUFFER ###");
+	if (vidc_hal_iface_cmdq_write(session->device, &pkt))
+		rc = -ENOTEMPTY;
+	return rc;
+}
+
+int vidc_hal_session_parse_seq_hdr(void *sess,
+	struct vidc_seq_hdr *seq_hdr)
+{
+	struct hfi_cmd_session_parse_sequence_header_packet *pkt;
+	int rc = 0;
+	u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE];
+	struct hal_session *session;
+
+	if (!sess || !seq_hdr) {
+		HAL_MSG_ERROR("Invalid Params in %s", __func__);
+		return -EINVAL;
+	} else {
+		session = sess;
+	}
+
+	pkt = (struct hfi_cmd_session_parse_sequence_header_packet *) packet;
+	pkt->size = sizeof(struct hfi_cmd_session_parse_sequence_header_packet);
+	pkt->packet_type = HFI_CMD_SESSION_PARSE_SEQUENCE_HEADER;
+	pkt->session_id = (u32) session;
+	pkt->header_len = seq_hdr->seq_hdr_len;
+	pkt->packet_buffer = seq_hdr->seq_hdr;
+
+	if (vidc_hal_iface_cmdq_write(session->device, pkt))
+		rc = -ENOTEMPTY;
+	return rc;
+}
+
+int vidc_hal_session_get_seq_hdr(void *sess,
+	struct vidc_seq_hdr *seq_hdr)
+{
+	struct hfi_cmd_session_get_sequence_header_packet *pkt;
+	int rc = 0;
+	u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE];
+	struct hal_session *session;
+
+	if (!sess || !seq_hdr) {
+		HAL_MSG_ERROR("Invalid Params in %s", __func__);
+		return -EINVAL;
+	} else {
+		session = sess;
+	}
+
+	pkt = (struct hfi_cmd_session_get_sequence_header_packet *) packet;
+	pkt->size = sizeof(struct hfi_cmd_session_get_sequence_header_packet);
+	pkt->packet_type = HFI_CMD_SESSION_GET_SEQUENCE_HEADER;
+	pkt->session_id = (u32) session;
+	pkt->buffer_len = seq_hdr->seq_hdr_len;
+	pkt->packet_buffer = seq_hdr->seq_hdr;
+
+	if (vidc_hal_iface_cmdq_write(session->device, pkt))
+		rc = -ENOTEMPTY;
+	return rc;
+}
+
+int vidc_hal_session_get_buf_req(void *sess)
+{
+	struct hfi_cmd_session_get_property_packet pkt;
+	int rc = 0;
+	struct hal_session *session;
+
+	if (sess) {
+		session = sess;
+	} else {
+		HAL_MSG_ERROR("%s:invalid session", __func__);
+		return -ENODEV;
+	}
+
+	pkt.size = sizeof(struct hfi_cmd_session_get_property_packet);
+	pkt.packet_type = HFI_CMD_SESSION_GET_PROPERTY;
+	pkt.session_id = (u32) session;
+	pkt.num_properties = 1;
+	pkt.rg_property_data[0] = HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS;
+	if (vidc_hal_iface_cmdq_write(session->device, &pkt))
+		rc = -ENOTEMPTY;
+	return rc;
+}
+
+int vidc_hal_session_flush(void *sess, enum hal_flush flush_mode)
+{
+	struct hfi_cmd_session_flush_packet pkt;
+	int rc = 0;
+	struct hal_session *session;
+
+	if (sess) {
+		session = sess;
+	} else {
+		HAL_MSG_ERROR("%s:invalid session", __func__);
+		return -ENODEV;
+	}
+
+	pkt.size = sizeof(struct hfi_cmd_session_flush_packet);
+	pkt.packet_type = HFI_CMD_SESSION_FLUSH;
+	pkt.session_id = (u32) session;
+	pkt.flush_type = flush_mode;
+
+	if (vidc_hal_iface_cmdq_write(session->device, &pkt))
+		rc = -ENOTEMPTY;
+	return rc;
+}
+
+static int vidc_hal_check_core_registered(
+	struct hal_device_data core, u32 fw_addr,
+	u32 reg_addr, u32 reg_size, u32 irq)
+{
+	struct hal_device *device;
+	struct list_head *curr, *next;
+
+	if (core.dev_count) {
+		list_for_each_safe(curr, next, &core.dev_head) {
+			device = list_entry(curr, struct hal_device, list);
+			if (device && device->hal_data->irq == irq &&
+				(CONTAINS((u32)device->hal_data->
+						device_base_addr,
+						FIRMWARE_SIZE, fw_addr) ||
+				CONTAINS(fw_addr, FIRMWARE_SIZE,
+						(u32)device->hal_data->
+						device_base_addr) ||
+				CONTAINS((u32)device->hal_data->
+						register_base_addr,
+						reg_size, reg_addr) ||
+				CONTAINS(reg_addr, reg_size,
+						(u32)device->hal_data->
+						register_base_addr) ||
+				OVERLAPS((u32)device->hal_data->
+						register_base_addr,
+						reg_size, reg_addr, reg_size) ||
+				OVERLAPS(reg_addr, reg_size,
+						(u32)device->hal_data->
+						register_base_addr, reg_size) ||
+				OVERLAPS((u32)device->hal_data->
+						device_base_addr,
+						FIRMWARE_SIZE, fw_addr,
+						FIRMWARE_SIZE) ||
+				OVERLAPS(fw_addr, FIRMWARE_SIZE,
+						(u32)device->hal_data->
+						device_base_addr,
+						FIRMWARE_SIZE))) {
+				return 0;
+			} else {
+				HAL_MSG_INFO("Device not registered");
+				return -EINVAL;
+			}
+		}
+	} else {
+		HAL_MSG_INFO("no device Registered");
+	}
+	return -EINVAL;
+}
+
+static void vidc_hal_core_work_handler(struct work_struct *work)
+{
+	struct hal_device *device = list_first_entry(
+		&hal_ctxt.dev_head, struct hal_device, list);
+
+	HAL_MSG_INFO(" GOT INTERRUPT %s() ", __func__);
+	if (!device->callback) {
+		HAL_MSG_ERROR("No callback function	"
+					  "to process interrupt: %p\n", device);
+		return;
+	}
+	vidc_hal_core_clear_interrupt(device);
+	vidc_hal_response_handler(device);
+	enable_irq(device->hal_data->irq);
+}
+static DECLARE_WORK(vidc_hal_work, vidc_hal_core_work_handler);
+
+static irqreturn_t vidc_hal_isr(int irq, void *dev)
+{
+	struct hal_device *device = dev;
+	HAL_MSG_MEDIUM("\n vidc_hal_isr() %d ", irq);
+	disable_irq_nosync(irq);
+	queue_work(device->vidc_workq, &vidc_hal_work);
+	HAL_MSG_MEDIUM("\n vidc_hal_isr() %d ", irq);
+	return IRQ_HANDLED;
+}
+
+void *vidc_hal_add_device(u32 device_id, u32 fw_base_addr, u32 reg_base,
+		u32 reg_size, u32 irq,
+		void (*callback) (enum command_response cmd, void *data))
+{
+	struct hal_device *hdevice = NULL;
+	struct hal_data *hal = NULL;
+	int rc = 0;
+
+	if (device_id || !fw_base_addr || !reg_base || !reg_size ||
+			!irq || !callback) {
+		HAL_MSG_ERROR("Invalid Paramters");
+		return NULL;
+	} else {
+		HAL_MSG_INFO("entered %s, device_id: %d", __func__, device_id);
+	}
+
+	if (vidc_hal_check_core_registered(hal_ctxt, fw_base_addr,
+						reg_base, reg_size, irq)) {
+		HAL_MSG_LOW("HAL_DATA will be assigned now");
+		hal = (struct hal_data *)
+			kzalloc(sizeof(struct hal_data), GFP_KERNEL);
+		if (!hal) {
+			HAL_MSG_ERROR("Failed to alloc");
+			return NULL;
+		}
+		hal->irq = irq;
+		hal->device_base_addr =
+			ioremap_nocache(fw_base_addr, FIRMWARE_SIZE);
+		if (!hal->device_base_addr) {
+			HAL_MSG_ERROR("could not map fw addr %d of size %d",
+						  fw_base_addr, FIRMWARE_SIZE);
+			goto err_map;
+		}
+		hal->register_base_addr =
+			ioremap_nocache(reg_base, reg_size);
+		if (!hal->register_base_addr) {
+			HAL_MSG_ERROR("could not map reg addr %d of size %d",
+						  reg_base, reg_size);
+			goto err_map;
+		}
+		INIT_LIST_HEAD(&hal_ctxt.dev_head);
+	} else {
+		HAL_MSG_ERROR("Core present/Already added");
+		return NULL;
+	}
+
+	hdevice = (struct hal_device *)
+			kzalloc(sizeof(struct hal_device), GFP_KERNEL);
+	if (!hdevice) {
+		HAL_MSG_ERROR("failed to allocate new device");
+		goto err_map;
+	}
+
+	INIT_LIST_HEAD(&hdevice->list);
+	list_add_tail(&hdevice->list, &hal_ctxt.dev_head);
+	hal_ctxt.dev_count++;
+	hdevice->device_id = device_id;
+	hdevice->hal_data = hal;
+	hdevice->callback = callback;
+
+	hdevice->vidc_workq = create_singlethread_workqueue(
+		"msm_vidc_workerq");
+	if (!hdevice->vidc_workq) {
+		HAL_MSG_ERROR("%s: create workq failed\n", __func__);
+		goto error_createq;
+	}
+
+	rc = request_irq(irq, vidc_hal_isr, IRQF_TRIGGER_HIGH,
+			"msm_vidc", hdevice);
+	if (unlikely(rc)) {
+		HAL_MSG_ERROR("%s() :request_irq failed\n", __func__);
+		goto error_irq_fail;
+	}
+	disable_irq_nosync(irq);
+	return (void *) hdevice;
+error_irq_fail:
+	destroy_workqueue(hdevice->vidc_workq);
+error_createq:
+	hal_ctxt.dev_count--;
+	list_del(&hal_ctxt.dev_head);
+err_map:
+	kfree(hal);
+	return NULL;
+}
+
+void vidc_hal_delete_device(void *device)
+{
+	struct hal_device *close, *dev;
+
+	if (device) {
+		dev = (struct hal_device *) device;
+		list_for_each_entry(close, &hal_ctxt.dev_head, list) {
+			if (close->hal_data->irq == dev->hal_data->irq) {
+				hal_ctxt.dev_count--;
+				free_irq(dev->hal_data->irq, NULL);
+				list_del(&close->list);
+				destroy_workqueue(close->vidc_workq);
+				kfree(close->hal_data);
+				kfree(close);
+				break;
+			}
+		}
+
+	}
+}
diff --git a/drivers/media/video/msm_vidc/vidc_hal.h b/drivers/media/video/msm_vidc/vidc_hal.h
new file mode 100644
index 0000000..166ed0d
--- /dev/null
+++ b/drivers/media/video/msm_vidc/vidc_hal.h
@@ -0,0 +1,1618 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __VIDC_HAL_H__
+#define __VIDC_HAL_H__
+
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include "vidc_hal_api.h"
+#include "msm_smem.h"
+
+#ifdef HAL_MSG_LOG
+#define HAL_MSG_LOW(x...) pr_debug(KERN_INFO x)
+#define HAL_MSG_MEDIUM(x...) pr_debug(KERN_INFO x)
+#define HAL_MSG_HIGH(x...) pr_debug(KERN_INFO x)
+#else
+#define HAL_MSG_LOW(x...)
+#define HAL_MSG_MEDIUM(x...)
+#define HAL_MSG_HIGH(x...)
+#endif
+
+#define HAL_MSG_ERROR(x...) pr_err(KERN_INFO x)
+#define HAL_MSG_FATAL(x...) pr_err(KERN_INFO x)
+#define HAL_MSG_INFO(x...) pr_info(KERN_INFO x)
+
+#define HFI_MASK_QHDR_TX_TYPE			0xFF000000
+#define HFI_MASK_QHDR_RX_TYPE			0x00FF0000
+#define HFI_MASK_QHDR_PRI_TYPE			0x0000FF00
+#define HFI_MASK_QHDR_Q_ID_TYPE			0x000000FF
+#define HFI_Q_ID_HOST_TO_CTRL_CMD_Q		0x00
+#define HFI_Q_ID_CTRL_TO_HOST_MSG_Q		0x01
+#define HFI_Q_ID_CTRL_TO_HOST_DEBUG_Q	0x02
+#define HFI_MASK_QHDR_STATUS			0x000000FF
+
+#define VIDC_MAX_UNCOMPRESSED_FMT_PLANES	3
+
+#define VIDC_IFACEQ_NUMQ					3
+#define VIDC_IFACEQ_CMDQ_IDX				0
+#define VIDC_IFACEQ_MSGQ_IDX				1
+#define VIDC_IFACEQ_DBGQ_IDX				2
+
+#define VIDC_IFACEQ_MAX_PKT_SIZE			1024
+#define VIDC_IFACEQ_MED_PKT_SIZE			768
+#define VIDC_IFACEQ_MIN_PKT_SIZE			8
+#define VIDC_IFACEQ_VAR_SMALL_PKT_SIZE		100
+#define VIDC_IFACEQ_VAR_LARGE_PKT_SIZE		512
+#define VIDC_IFACEQ_MAX_BUF_COUNT			50
+#define VIDC_IFACE_MAX_PARALLEL_CLNTS		16
+#define VIDC_IFACEQ_DFLT_QHDR				0x01010000
+
+struct hfi_queue_table_header {
+	u32 qtbl_version;
+	u32 qtbl_size;
+	u32 qtbl_qhdr0_offset;
+	u32 qtbl_qhdr_size;
+	u32 qtbl_num_q;
+	u32 qtbl_num_active_q;
+};
+
+struct hfi_queue_header {
+	u32 qhdr_status;
+	u32 qhdr_start_addr;
+	u32 qhdr_type;
+	u32 qhdr_q_size;
+	u32 qhdr_pkt_size;
+	u32 qhdr_pkt_drop_cnt;
+	u32 qhdr_rx_wm;
+	u32 qhdr_tx_wm;
+	u32 qhdr_rx_req;
+	u32 qhdr_tx_req;
+	u32 qhdr_rx_irq_status;
+	u32 qhdr_tx_irq_status;
+	u32 qhdr_read_idx;
+	u32 qhdr_write_idx;
+};
+
+#define VIDC_IFACEQ_TABLE_SIZE (sizeof(struct hfi_queue_table_header) \
+	+ sizeof(struct hfi_queue_header) * VIDC_IFACEQ_NUMQ)
+
+#define VIDC_IFACEQ_QUEUE_SIZE		(VIDC_IFACEQ_MAX_PKT_SIZE *  \
+	VIDC_IFACEQ_MAX_BUF_COUNT * VIDC_IFACE_MAX_PARALLEL_CLNTS)
+
+#define VIDC_IFACEQ_GET_QHDR_START_ADDR(ptr, i)     \
+	(void *)((((u32)ptr) + sizeof(struct hfi_queue_table_header)) + \
+		(i * sizeof(struct hfi_queue_header)))
+
+enum vidc_hw_reg {
+	VIDC_HWREG_CTRL_STATUS =  0x1,
+	VIDC_HWREG_QTBL_INFO =  0x2,
+	VIDC_HWREG_QTBL_ADDR =  0x3,
+	VIDC_HWREG_CTRLR_RESET =  0x4,
+	VIDC_HWREG_IFACEQ_FWRXREQ =  0x5,
+	VIDC_HWREG_IFACEQ_FWTXREQ =  0x6,
+	VIDC_HWREG_VHI_SOFTINTEN =  0x7,
+	VIDC_HWREG_VHI_SOFTINTSTATUS =  0x8,
+	VIDC_HWREG_VHI_SOFTINTCLR =  0x9,
+	VIDC_HWREG_HVI_SOFTINTEN =  0xA,
+};
+
+enum HFI_EVENT {
+	HFI_EVENT_SYS_ERROR,
+	HFI_EVENT_SESSION_ERROR,
+	HFI_EVENT_SESSION_SEQUENCE_CHANGED,
+	HFI_EVENT_SESSION_PROPERTY_CHANGED,
+	HFI_UNUSED_EVENT = 0x10000000,
+};
+
+enum HFI_EVENT_DATA_SEQUENCE_CHANGED {
+	HFI_EVENT_DATA_SEQUENCE_CHANGED_SUFFICIENT_BUFFER_RESOURCES,
+	HFI_EVENT_DATA_SEQUENCE_CHANGED_INSUFFICIENT_BUFFER_RESOURCES,
+	HFI_UNUSED_SEQCHG = 0x10000000,
+};
+
+#define HFI_BUFFERFLAG_EOS              0x00000001
+#define HFI_BUFFERFLAG_STARTTIME        0x00000002
+#define HFI_BUFFERFLAG_DECODEONLY       0x00000004
+#define HFI_BUFFERFLAG_DATACORRUPT      0x00000008
+#define HFI_BUFFERFLAG_ENDOFFRAME       0x00000010
+#define HFI_BUFFERFLAG_SYNCFRAME        0x00000020
+#define HFI_BUFFERFLAG_EXTRADATA        0x00000040
+#define HFI_BUFFERFLAG_CODECCONFIG      0x00000080
+#define HFI_BUFFERFLAG_TIMESTAMPINVALID 0x00000100
+#define HFI_BUFFERFLAG_READONLY         0x00000200
+#define HFI_BUFFERFLAG_ENDOFSUBFRAME    0x00000400
+
+enum HFI_ERROR {
+	HFI_ERR_NONE                              = 0,
+	HFI_ERR_SYS_UNKNOWN                       = 0x80000001,
+	HFI_ERR_SYS_FATAL                         = 0x80000002,
+	HFI_ERR_SYS_INVALID_PARAMETER             = 0x80000003,
+	HFI_ERR_SYS_VERSION_MISMATCH              = 0x80000004,
+	HFI_ERR_SYS_INSUFFICIENT_RESOURCES        = 0x80000005,
+	HFI_ERR_SYS_MAX_SESSIONS_REACHED          = 0x80000006,
+	HFI_ERR_SYS_UNSUPPORTED_CODEC             = 0x80000007,
+	HFI_ERR_SYS_SESSION_IN_USE                = 0x80000008,
+	HFI_ERR_SYS_SESSION_ID_OUT_OF_RANGE       = 0x80000009,
+	HFI_ERR_SYS_UNSUPPORTED_DOMAIN            = 0x8000000A,
+	HFI_ERR_SESSION_START_UNUSED              = 0x80001000,
+	HFI_ERR_SESSION_UNKNOWN                   = 0x80001001,
+	HFI_ERR_SESSION_FATAL                     = 0x80001002,
+	HFI_ERR_SESSION_INVALID_PARAMETER         = 0x80001003,
+	HFI_ERR_SESSION_BAD_POINTER               = 0x80001004,
+	HFI_ERR_SESSION_INVALID_SESSION_ID        = 0x80001005,
+	HFI_ERR_SESSION_INVALID_STREAM_ID         = 0x80001006,
+	HFI_ERR_SESSION_INCORRECT_STATE_OPERATION = 0x80001007,
+	HFI_ERR_SESSION_UNSUPPORTED_PROPERTY      = 0x80001008,
+	HFI_ERR_SESSION_UNSUPPORTED_SETTING       = 0x80001009,
+	HFI_ERR_SESSION_INSUFFICIENT_RESOURCES    = 0x8000100A,
+	HFI_ERR_SESSION_STREAM_CORRUPT            = 0x8000100B,
+	HFI_ERR_SESSION_STREAM_CORRUPT_OUTPUT_STALLED    =  0x8000100C,
+	HFI_ERR_SESSION_SYNC_FRAME_NOT_DETECTED          =  0x8000100D,
+	HFI_ERR_SESSION_EMPTY_BUFFER_DONE_OUTPUT_PENDING =  0x8000100E,
+	HFI_ERR_SESSION_SAME_STATE_OPERATION		= 0x8000100F,
+	HFI_UNUSED_ERR = 0x10000000,
+};
+
+enum HFI_DOMAIN {
+	HFI_VIDEO_DOMAIN_VPE,
+	HFI_VIDEO_DOMAIN_ENCODER,
+	HFI_VIDEO_DOMAIN_DECODER,
+	HFI_UNUSED_DOMAIN = 0x10000000,
+};
+
+enum HFI_VIDEO_CODEC {
+	HFI_VIDEO_CODEC_UNKNOWN  = 0x00000000,
+	HFI_VIDEO_CODEC_H264     = 0x00000002,
+	HFI_VIDEO_CODEC_H263     = 0x00000004,
+	HFI_VIDEO_CODEC_MPEG1    = 0x00000008,
+	HFI_VIDEO_CODEC_MPEG2    = 0x00000010,
+	HFI_VIDEO_CODEC_MPEG4    = 0x00000020,
+	HFI_VIDEO_CODEC_DIVX_311 = 0x00000040,
+	HFI_VIDEO_CODEC_DIVX     = 0x00000080,
+	HFI_VIDEO_CODEC_VC1      = 0x00000100,
+	HFI_VIDEO_CODEC_SPARK    = 0x00000200,
+	HFI_VIDEO_CODEC_VP6      = 0x00000400,
+	HFI_VIDEO_CODEC_VP7		 = 0x00000800,
+	HFI_VIDEO_CODEC_VP8		 = 0x00001000,
+	HFI_UNUSED_CODEC		 = 0x10000000,
+};
+
+enum HFI_H263_PROFILE {
+	HFI_H263_PROFILE_BASELINE           = 0x00000001,
+	HFI_H263_PROFILE_H320CODING         = 0x00000002,
+	HFI_H263_PROFILE_BACKWARDCOMPATIBLE = 0x00000004,
+	HFI_H263_PROFILE_ISWV2              = 0x00000008,
+	HFI_H263_PROFILE_ISWV3              = 0x00000010,
+	HFI_H263_PROFILE_HIGHCOMPRESSION    = 0x00000020,
+	HFI_H263_PROFILE_INTERNET           = 0x00000040,
+	HFI_H263_PROFILE_INTERLACE          = 0x00000080,
+	HFI_H263_PROFILE_HIGHLATENCY        = 0x00000100,
+	HFI_UNUSED_H263_PROFILE = 0x10000000,
+};
+
+enum HFI_H263_LEVEL {
+	HFI_H263_LEVEL_10 = 0x00000001,
+	HFI_H263_LEVEL_20 = 0x00000002,
+	HFI_H263_LEVEL_30 = 0x00000004,
+	HFI_H263_LEVEL_40 = 0x00000008,
+	HFI_H263_LEVEL_45 = 0x00000010,
+	HFI_H263_LEVEL_50 = 0x00000020,
+	HFI_H263_LEVEL_60 = 0x00000040,
+	HFI_H263_LEVEL_70 = 0x00000080,
+	HFI_UNUSED_H263_LEVEL = 0x10000000,
+};
+
+enum HFI_MPEG2_PROFILE {
+	HFI_MPEG2_PROFILE_SIMPLE  = 0x00000001,
+	HFI_MPEG2_PROFILE_MAIN    = 0x00000002,
+	HFI_MPEG2_PROFILE_422     = 0x00000004,
+	HFI_MPEG2_PROFILE_SNR     = 0x00000008,
+	HFI_MPEG2_PROFILE_SPATIAL = 0x00000010,
+	HFI_MPEG2_PROFILE_HIGH    = 0x00000020,
+	HFI_UNUSED_MPEG2_PROFILE = 0x10000000,
+};
+
+enum HFI_MPEG2_LEVEL {
+	HFI_MPEG2_LEVEL_LL  = 0x00000001,
+	HFI_MPEG2_LEVEL_ML  = 0x00000002,
+	HFI_MPEG2_LEVEL_H14 = 0x00000004,
+	HFI_MPEG2_LEVEL_HL  = 0x00000008,
+	HFI_UNUSED_MEPG2_LEVEL = 0x10000000,
+};
+
+enum HFI_MPEG4_PROFILE {
+	HFI_MPEG4_PROFILE_SIMPLE           = 0x00000001,
+	HFI_MPEG4_PROFILE_SIMPLESCALABLE   = 0x00000002,
+	HFI_MPEG4_PROFILE_CORE             = 0x00000004,
+	HFI_MPEG4_PROFILE_MAIN             = 0x00000008,
+	HFI_MPEG4_PROFILE_NBIT             = 0x00000010,
+	HFI_MPEG4_PROFILE_SCALABLETEXTURE  = 0x00000020,
+	HFI_MPEG4_PROFILE_SIMPLEFACE       = 0x00000040,
+	HFI_MPEG4_PROFILE_SIMPLEFBA        = 0x00000080,
+	HFI_MPEG4_PROFILE_BASICANIMATED    = 0x00000100,
+	HFI_MPEG4_PROFILE_HYBRID           = 0x00000200,
+	HFI_MPEG4_PROFILE_ADVANCEDREALTIME = 0x00000400,
+	HFI_MPEG4_PROFILE_CORESCALABLE     = 0x00000800,
+	HFI_MPEG4_PROFILE_ADVANCEDCODING   = 0x00001000,
+	HFI_MPEG4_PROFILE_ADVANCEDCORE     = 0x00002000,
+	HFI_MPEG4_PROFILE_ADVANCEDSCALABLE = 0x00004000,
+	HFI_MPEG4_PROFILE_ADVANCEDSIMPLE   = 0x00008000,
+	HFI_UNUSED_MPEG4_PROFILE = 0x10000000,
+};
+
+enum HFI_MPEG4_LEVEL {
+	HFI_MPEG4_LEVEL_0  = 0x00000001,
+	HFI_MPEG4_LEVEL_0b = 0x00000002,
+	HFI_MPEG4_LEVEL_1  = 0x00000004,
+	HFI_MPEG4_LEVEL_2  = 0x00000008,
+	HFI_MPEG4_LEVEL_3  = 0x00000010,
+	HFI_MPEG4_LEVEL_4  = 0x00000020,
+	HFI_MPEG4_LEVEL_4a = 0x00000040,
+	HFI_MPEG4_LEVEL_5  = 0x00000080,
+	HFI_MPEG4_LEVEL_VENDOR_START_UNUSED = 0x7F000000,
+	HFI_MPEG4_LEVEL_6  = 0x7F000001,
+	HFI_MPEG4_LEVEL_7  = 0x7F000002,
+	HFI_MPEG4_LEVEL_8  = 0x7F000003,
+	HFI_MPEG4_LEVEL_9  = 0x7F000004,
+	HFI_MPEG4_LEVEL_3b = 0x7F000005,
+	HFI_UNUSED_MPEG4_LEVEL = 0x10000000,
+};
+
+enum HFI_H264_PROFILE {
+	HFI_H264_PROFILE_BASELINE = 0x00000001,
+	HFI_H264_PROFILE_MAIN     = 0x00000002,
+	HFI_H264_PROFILE_EXTENDED = 0x00000004,
+	HFI_H264_PROFILE_HIGH     = 0x00000008,
+	HFI_H264_PROFILE_HIGH10   = 0x00000010,
+	HFI_H264_PROFILE_HIGH422  = 0x00000020,
+	HFI_H264_PROFILE_HIGH444  = 0x00000040,
+	HFI_H264_PROFILE_STEREO_HIGH = 0x00000080,
+	HFI_H264_PROFILE_MV_HIGH  = 0x00000100,
+	HFI_UNUSED_H264_PROFILE   = 0x10000000,
+};
+
+enum HFI_H264_LEVEL {
+	HFI_H264_LEVEL_1  = 0x00000001,
+	HFI_H264_LEVEL_1b = 0x00000002,
+	HFI_H264_LEVEL_11 = 0x00000004,
+	HFI_H264_LEVEL_12 = 0x00000008,
+	HFI_H264_LEVEL_13 = 0x00000010,
+	HFI_H264_LEVEL_2  = 0x00000020,
+	HFI_H264_LEVEL_21 = 0x00000040,
+	HFI_H264_LEVEL_22 = 0x00000080,
+	HFI_H264_LEVEL_3  = 0x00000100,
+	HFI_H264_LEVEL_31 = 0x00000200,
+	HFI_H264_LEVEL_32 = 0x00000400,
+	HFI_H264_LEVEL_4  = 0x00000800,
+	HFI_H264_LEVEL_41 = 0x00001000,
+	HFI_H264_LEVEL_42 = 0x00002000,
+	HFI_H264_LEVEL_5  = 0x00004000,
+	HFI_H264_LEVEL_51 = 0x00008000,
+	HFI_UNUSED_H264_LEVEL = 0x10000000,
+};
+
+enum HFI_VPX_PROFILE {
+	HFI_VPX_PROFILE_SIMPLE    = 0x00000001,
+	HFI_VPX_PROFILE_ADVANCED  = 0x00000002,
+	HFI_VPX_PROFILE_VERSION_0 = 0x00000004,
+	HFI_VPX_PROFILE_VERSION_1 = 0x00000008,
+	HFI_VPX_PROFILE_VERSION_2 = 0x00000010,
+	HFI_VPX_PROFILE_VERSION_3 = 0x00000020,
+	HFI_VPX_PROFILE_UNUSED = 0x10000000,
+};
+
+enum HFI_VC1_PROFILE {
+	HFI_VC1_PROFILE_SIMPLE   = 0x00000001,
+	HFI_VC1_PROFILE_MAIN     = 0x00000002,
+	HFI_VC1_PROFILE_ADVANCED = 0x00000004,
+	HFI_UNUSED_VC1_PROFILE = 0x10000000,
+};
+
+enum HFI_VC1_LEVEL {
+	HFI_VC1_LEVEL_LOW    = 0x00000001,
+	HFI_VC1_LEVEL_MEDIUM = 0x00000002,
+	HFI_VC1_LEVEL_HIGH   = 0x00000004,
+	HFI_VC1_LEVEL_0      = 0x00000008,
+	HFI_VC1_LEVEL_1      = 0x00000010,
+	HFI_VC1_LEVEL_2      = 0x00000020,
+	HFI_VC1_LEVEL_3      = 0x00000040,
+	HFI_VC1_LEVEL_4      = 0x00000080,
+	HFI_UNUSED_VC1_LEVEL = 0x10000000,
+};
+
+enum HFI_DIVX_FORMAT {
+	HFI_DIVX_FORMAT_4,
+	HFI_DIVX_FORMAT_5,
+	HFI_DIVX_FORMAT_6,
+	HFI_UNUSED_DIVX_FORMAT = 0x10000000,
+};
+
+enum HFI_DIVX_PROFILE {
+	HFI_DIVX_PROFILE_QMOBILE  = 0x00000001,
+	HFI_DIVX_PROFILE_MOBILE   = 0x00000002,
+	HFI_DIVX_PROFILE_MT       = 0x00000004,
+	HFI_DIVX_PROFILE_HT       = 0x00000008,
+	HFI_DIVX_PROFILE_HD       = 0x00000010,
+	HFI_UNUSED_DIVX_PROFILE = 0x10000000,
+};
+
+enum HFI_BUFFER {
+	HFI_BUFFER_INPUT,
+	HFI_BUFFER_OUTPUT,
+	HFI_BUFFER_OUTPUT2,
+	HFI_BUFFER_EXTRADATA_INPUT,
+	HFI_BUFFER_EXTRADATA_OUTPUT,
+	HFI_BUFFER_EXTRADATA_OUTPUT2,
+	HFI_BUFFER_INTERNAL_SCRATCH = 0x7F000001,
+	HFI_BUFFER_INTERNAL_PERSIST = 0x7F000002,
+	HFI_UNUSED_BUFFER = 0x10000000,
+};
+
+enum HFI_BUFFER_MODE {
+	HFI_BUFFER_MODE_STATIC,
+	HFI_BUFFER_MODE_RING,
+	HFI_UNUSED_BUFFER_MODE = 0x10000000,
+};
+
+enum HFI_FLUSH {
+	HFI_FLUSH_INPUT,
+	HFI_FLUSH_OUTPUT,
+	HFI_FLUSH_OUTPUT2,
+	HFI_FLUSH_ALL,
+	HFI_UNUSED_FLUSH = 0x10000000,
+};
+
+enum HFI_EXTRADATA {
+	HFI_EXTRADATA_NONE                 = 0x00000000,
+	HFI_EXTRADATA_MB_QUANTIZATION      = 0x00000001,
+	HFI_EXTRADATA_INTERLACE_VIDEO      = 0x00000002,
+	HFI_EXTRADATA_VC1_FRAMEDISP        = 0x00000003,
+	HFI_EXTRADATA_VC1_SEQDISP          = 0x00000004,
+	HFI_EXTRADATA_TIMESTAMP            = 0x00000005,
+	HFI_EXTRADATA_MULTISLICE_INFO      = 0x7F100000,
+	HFI_EXTRADATA_NUM_CONCEALED_MB     = 0x7F100001,
+	HFI_EXTRADATA_INDEX                = 0x7F100002,
+	HFI_EXTRADATA_METADATA_FILLER      = 0x7FE00002,
+	HFI_UNUSED_EXTRADATA = 0x10000000,
+};
+
+enum HFI_EXTRADATA_INDEX_TYPE {
+	HFI_INDEX_EXTRADATA_INPUT_CROP    = 0x0700000E,
+	HFI_INDEX_EXTRADATA_DIGITAL_ZOOM  = 0x07000010,
+	HFI_INDEX_EXTRADATA_ASPECT_RATIO  = 0x7F100003,
+};
+
+struct hfi_extradata_header {
+	u32 size;
+	u32 version;
+	u32 port_tndex;
+	enum HFI_EXTRADATA type;
+	u32 data_size;
+	u8 rg_data[1];
+};
+
+enum HFI_INTERLACE_FORMAT {
+	HFI_INTERLACE_FRAME_PROGRESSIVE                 = 0x01,
+	HFI_INTERLACE_INTERLEAVE_FRAME_TOPFIELDFIRST    = 0x02,
+	HFI_INTERLACE_INTERLEAVE_FRAME_BOTTOMFIELDFIRST = 0x04,
+	HFI_INTERLACE_FRAME_TOPFIELDFIRST               = 0x08,
+	HFI_INTERLACE_FRAME_BOTTOMFIELDFIRST            = 0x10,
+	HFI_UNUSED_INTERLACE = 0x10000000,
+};
+
+enum HFI_PROPERTY {
+	HFI_PROPERTY_SYS_UNUSED = 0x08000000,
+	HFI_PROPERTY_SYS_IDLE_INDICATOR,
+	HFI_PROPERTY_SYS_DEBUG_CONFIG,
+	HFI_PROPERTY_SYS_RESOURCE_OCMEM_REQUIREMENT_INFO,
+	HFI_PROPERTY_PARAM_UNUSED = 0x04000000,
+	HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL,
+	HFI_PROPERTY_PARAM_FRAME_SIZE,
+	HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT,
+	HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED,
+	HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO,
+	HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO,
+	HFI_PROPERTY_PARAM_INTERLACE_FORMAT_SUPPORTED,
+	HFI_PROPERTY_PARAM_CHROMA_SITE,
+	HFI_PROPERTY_PARAM_EXTRA_DATA_HEADER_CONFIG,
+	HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT,
+	HFI_PROPERTY_PARAM_PROFILE_LEVEL_SUPPORTED,
+	HFI_PROPERTY_PARAM_CAPABILITY_SUPPORTED,
+	HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SUPPORTED,
+	HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SELECT,
+	HFI_PROPERTY_PARAM_MULTI_VIEW_FORMAT,
+	HFI_PROPERTY_PARAM_PROPERTIES_SUPPORTED,
+	HFI_PROPERTY_PARAM_MAX_SEQUENCE_HEADER_SIZE,
+	HFI_PROPERTY_PARAM_CODEC_SUPPORTED,
+	HFI_PROPERTY_PARAM_DIVX_FORMAT,
+
+	HFI_PROPERTY_CONFIG_UNUSED = 0x02000000,
+	HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS,
+	HFI_PROPERTY_CONFIG_REALTIME,
+	HFI_PROPERTY_CONFIG_PRIORITY,
+	HFI_PROPERTY_CONFIG_BATCH_INFO,
+	HFI_PROPERTY_CONFIG_FRAME_RATE,
+
+	HFI_PROPERTY_PARAM_VDEC_UNUSED = 0x01000000,
+	HFI_PROPERTY_PARAM_VDEC_CONTINUE_DATA_TRANSFER,
+	HFI_PROPERTY_PARAM_VDEC_DISPLAY_PICTURE_BUFFER_COUNT,
+	HFI_PROPERTY_PARAM_VDEC_MULTI_VIEW_SELECT,
+	HFI_PROPERTY_PARAM_VDEC_PICTURE_TYPE_DECODE,
+	HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM,
+	HFI_PROPERTY_PARAM_VDEC_OUTPUT_ORDER,
+	HFI_PROPERTY_PARAM_VDEC_MB_QUANTIZATION,
+	HFI_PROPERTY_PARAM_VDEC_NUM_CONCEALED_MB,
+	HFI_PROPERTY_PARAM_VDEC_H264_ENTROPY_SWITCHING,
+	HFI_PROPERTY_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO,
+
+	HFI_PROPERTY_CONFIG_VDEC_UNUSED = 0x00800000,
+	HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER,
+	HFI_PROPERTY_CONFIG_VDEC_MB_ERROR_MAP_REPORTING,
+	HFI_PROPERTY_CONFIG_VDEC_MB_ERROR_MAP,
+
+	HFI_PROPERTY_PARAM_VENC_UNUSED = 0x00400000,
+	HFI_PROPERTY_PARAM_VENC_SLICE_DELIVERY_MODE,
+	HFI_PROPERTY_PARAM_VENC_H264_ENTROPY_CONTROL,
+	HFI_PROPERTY_PARAM_VENC_H264_DEBLOCK_CONTROL,
+	HFI_PROPERTY_PARAM_VENC_RATE_CONTROL,
+	HFI_PROPERTY_PARAM_VENC_TEMPORAL_SPATIAL_TRADEOFF,
+	HFI_PROPERTY_PARAM_VENC_SESSION_QP,
+	HFI_PROPERTY_PARAM_VENC_MPEG4_AC_PREDICTION,
+	HFI_PROPERTY_PARAM_VENC_MPEG4_DATA_PARTITIONING,
+	HFI_PROPERTY_PARAM_VENC_MPEG4_TIME_RESOLUTION,
+	HFI_PROPERTY_PARAM_VENC_MPEG4_SHORT_HEADER,
+	HFI_PROPERTY_PARAM_VENC_MPEG4_HEADER_EXTENSION,
+	HFI_PROPERTY_PARAM_VENC_MULTI_SLICE_INFO,
+	HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH,
+	HFI_PROPERTY_PARAM_VENC_MULTI_SLICE_CONTROL,
+
+	HFI_PROPERTY_CONFIG_VENC_UNUSED = 0x00200000,
+	HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE,
+	HFI_PROPERTY_CONFIG_VENC_IDR_PERIOD,
+	HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD,
+	HFI_PROPERTY_CONFIG_VENC_REQUEST_IFRAME,
+	HFI_PROPERTY_CONFIG_VENC_TIMESTAMP_SCALE,
+	HFI_PROPERTY_PARAM_VENC_MPEG4_QPEL,
+	HFI_PROPERTY_PARAM_VENC_ADVANCED,
+
+	HFI_PROPERTY_PARAM_VPE_UNUSED = 0x00100000,
+
+	HFI_PROPERTY_CONFIG_VPE_UNUSED = 0x00080000,
+	HFI_PROPERTY_CONFIG_VPE_DEINTERLACE,
+	HFI_PROPERTY_CONFIG_VPE_OPERATIONS,
+	HFI_PROPERTY_UNUSED = 0x10000000,
+};
+
+struct hfi_batch_info {
+	u32 input_batch_count;
+	u32 output_batch_count;
+};
+
+struct hfi_bitrate {
+	u32 bit_rate;
+};
+
+struct hfi_buffer_count_actual {
+	enum HFI_BUFFER buffer;
+	u32 buffer_count_actual;
+};
+
+struct hfi_buffer_requirements {
+	enum HFI_BUFFER buffer;
+	u32 buffer_size;
+	u32 buffer_region_size;
+	u32 buffer_hold_count;
+	u32 buffer_count_min;
+	u32 buffer_count_actual;
+	u32 contiguous;
+	u32 buffer_alignment;
+};
+
+enum HFI_CAPABILITY {
+	HFI_CAPABILITY_FRAME_WIDTH,
+	HFI_CAPABILITY_FRAME_HEIGHT,
+	HFI_CAPABILITY_MBS_PER_FRAME,
+	HFI_CAPABILITY_MBS_PER_SECOND,
+	HFI_CAPABILITY_FRAMERATE,
+	HFI_CAPABILITY_SCALE_X,
+	HFI_CAPABILITY_SCALE_Y,
+	HFI_CAPABILITY_BITRATE,
+	HFI_UNUSED_CAPABILITY = 0x10000000,
+};
+
+struct hfi_capability_supported {
+	enum HFI_CAPABILITY eCapabilityType;
+	u32 min;
+	u32 max;
+	u32 step_size;
+};
+
+struct hfi_capability_supported_INFO {
+	u32 num_capabilities;
+	struct hfi_capability_supported rg_data[1];
+};
+
+enum HFI_CHROMA_SITE {
+	HFI_CHROMA_SITE_0,
+	HFI_CHROMA_SITE_1,
+	HFI_UNUSED_CHROMA = 0x10000000,
+};
+
+struct hfi_data_payload {
+	u32 size;
+	u8 rg_data[1];
+};
+
+struct hfi_seq_header_info {
+	u32 max_header_len;
+};
+
+struct hfi_enable_picture {
+	u32 picture_type;
+};
+
+struct hfi_display_picture_buffer_count {
+	int enable;
+	u32 count;
+};
+
+struct hfi_enable {
+	int enable;
+};
+
+enum HFI_H264_DB_MODE {
+	HFI_H264_DB_MODE_DISABLE,
+	HFI_H264_DB_MODE_SKIP_SLICE_BOUNDARY,
+	HFI_H264_DB_MODE_ALL_BOUNDARY,
+	HFI_UNUSED_H264_DB = 0x10000000,
+};
+
+struct hfi_h264_db_control {
+	enum HFI_H264_DB_MODE mode;
+	int slice_alpha_offset;
+	int slice_beta_offset;
+};
+
+enum HFI_H264_ENTROPY {
+	HFI_H264_ENTROPY_CAVLC,
+	HFI_H264_ENTROPY_CABAC,
+	HFI_UNUSED_ENTROPY = 0x10000000,
+};
+
+enum HFI_H264_CABAC_MODEL {
+	HFI_H264_CABAC_MODEL_0,
+	HFI_H264_CABAC_MODEL_1,
+	HFI_H264_CABAC_MODEL_2,
+	HFI_UNUSED_CABAC = 0x10000000,
+};
+
+struct hfi_h264_entropy_control {
+	enum HFI_H264_ENTROPY entropy_mode;
+	enum HFI_H264_CABAC_MODEL cabac_model;
+};
+
+struct hfi_extra_data_header_config {
+	u32 type;
+	enum HFI_BUFFER buffer_type;
+	u32 version;
+	u32 port_index;
+	u32 client_extradata_id;
+};
+
+struct hfi_frame_rate {
+	enum HFI_BUFFER buffer_type;
+	u32 frame_rate;
+};
+
+struct hfi_interlace_format_supported {
+	enum HFI_BUFFER buffer;
+	enum HFI_INTERLACE_FORMAT format;
+};
+
+enum hfi_intra_refresh_mode {
+	HFI_INTRA_REFRESH_NONE,
+	HFI_INTRA_REFRESH_CYCLIC,
+	HFI_INTRA_REFRESH_ADAPTIVE,
+	HFI_INTRA_REFRESH_CYCLIC_ADAPTIVE,
+	HFI_INTRA_REFRESH_RANDOM,
+	HFI_UNUSED_INTRA = 0x10000000,
+};
+
+struct hfi_intra_refresh {
+	enum hfi_intra_refresh_mode mode;
+	u32 air_mbs;
+	u32 air_ref;
+	u32 cir_mbs;
+};
+
+struct hfi_idr_period {
+	u32 idr_period;
+};
+
+struct hfi_intra_period {
+	u32 pframes;
+	u32 bframes;
+};
+
+struct hfi_timestamp_scale {
+	u32 time_stamp_scale;
+};
+
+struct hfi_mb_error_map {
+	u32 error_map_size;
+	u8 rg_error_map[1];
+};
+
+struct hfi_metadata_pass_through {
+	int enable;
+	u32 size;
+};
+
+struct hfi_mpeg4_header_extension {
+	u32 header_extension;
+};
+
+struct hfi_mpeg4_time_resolution {
+	u32 time_increment_resolution;
+};
+
+enum HFI_MULTI_SLICE {
+	HFI_MULTI_SLICE_OFF,
+	HFI_MULTI_SLICE_BY_MB_COUNT,
+	HFI_MULTI_SLICE_BY_BYTE_COUNT,
+	HFI_MULTI_SLICE_GOB,
+	HFI_UNUSED_SLICE = 0x10000000,
+};
+
+struct hfi_multi_slice_control {
+	enum HFI_MULTI_SLICE multi_slice;
+	u32 slice_size;
+};
+
+struct hfi_multi_stream {
+	enum HFI_BUFFER buffer;
+	u32 enable;
+	u32 width;
+	u32 height;
+};
+
+struct hfi_multi_view_format {
+	u32 views;
+	u32 rg_view_order[1];
+};
+
+struct hfi_multi_view_select {
+	u32 view_index;
+};
+
+enum HFI_NAL_STREAM_FORMAT {
+	HFI_NAL_FORMAT_STARTCODES         = 0x00000001,
+	HFI_NAL_FORMAT_ONE_NAL_PER_BUFFER = 0x00000002,
+	HFI_NAL_FORMAT_ONE_BYTE_LENGTH    = 0x00000004,
+	HFI_NAL_FORMAT_TWO_BYTE_LENGTH    = 0x00000008,
+	HFI_NAL_FORMAT_FOUR_BYTE_LENGTH   = 0x00000010,
+	HFI_UNUSED_NAL = 0x10000000,
+};
+
+struct hfi_nal_stream_format_supported {
+	u32 nal_stream_format_supported;
+};
+
+enum HFI_PICTURE {
+	HFI_PICTURE_I   = 0x01,
+	HFI_PICTURE_P   = 0x02,
+	HFI_PICTURE_B   = 0x04,
+	HFI_PICTURE_IDR = 0x7F001000,
+	HFI_UNUSED_PICT = 0x10000000,
+};
+
+enum HFI_PRIORITY {
+	HFI_PRIORITY_LOW = 10,
+	HFI_PRIOIRTY_MEDIUM = 20,
+	HFI_PRIORITY_HIGH = 30,
+	HFI_UNUSED_PRIORITY = 0x10000000,
+};
+
+struct hfi_profile_level {
+	u32 profile;
+	u32 level;
+};
+
+struct hfi_profile_level_supported {
+	u32 profile_count;
+	struct hfi_profile_level rg_profile_level[1];
+};
+
+enum HFI_ROTATE {
+	HFI_ROTATE_NONE,
+	HFI_ROTATE_90,
+	HFI_ROTATE_180,
+	HFI_ROTATE_270,
+	HFI_UNUSED_ROTATE = 0x10000000,
+};
+
+enum HFI_FLIP {
+	HFI_FLIP_NONE,
+	HFI_FLIP_HORIZONTAL,
+	HFI_FLIP_VERTICAL,
+	HFI_UNUSED_FLIP = 0x10000000,
+};
+
+struct hfi_operations {
+	enum HFI_ROTATE rotate;
+	enum HFI_FLIP flip;
+};
+
+enum HFI_OUTPUT_ORDER {
+	HFI_OUTPUT_ORDER_DISPLAY,
+	HFI_OUTPUT_ORDER_DECODE,
+	HFI_UNUSED_OUTPUT = 0x10000000,
+};
+
+struct hfi_quantization {
+	u32 qp_i;
+	u32 qp_p;
+	u32 qp_b;
+};
+
+enum HFI_RATE_CONTROL {
+	HFI_RATE_CONTROL_OFF,
+	HFI_RATE_CONTROL_VBR_VFR,
+	HFI_RATE_CONTROL_VBR_CFR,
+	HFI_RATE_CONTROL_CBR_VFR,
+	HFI_RATE_CONTROL_CBR_CFR,
+	HFI_UNUSED_RC = 0x10000000,
+};
+
+struct hfi_slice_delivery_mode {
+	int enable;
+};
+
+struct hfi_temporal_spatial_tradeoff {
+	u32 ts_factor;
+};
+
+struct hfi_frame_size {
+	enum HFI_BUFFER buffer;
+	u32 width;
+	u32 height;
+};
+
+enum HFI_UNCOMPRESSED_FORMAT {
+	HFI_COLOR_FORMAT_MONOCHROME,
+	HFI_COLOR_FORMAT_NV12,
+	HFI_COLOR_FORMAT_NV21,
+	HFI_COLOR_FORMAT_NV12_4x4TILE,
+	HFI_COLOR_FORMAT_NV21_4x4TILE,
+	HFI_COLOR_FORMAT_YUYV,
+	HFI_COLOR_FORMAT_YVYU,
+	HFI_COLOR_FORMAT_UYVY,
+	HFI_COLOR_FORMAT_VYUY,
+	HFI_COLOR_FORMAT_RGB565,
+	HFI_COLOR_FORMAT_BGR565,
+	HFI_COLOR_FORMAT_RGB888,
+	HFI_COLOR_FORMAT_BGR888,
+	HFI_UNUSED_COLOR = 0x10000000,
+};
+
+struct hfi_uncompressed_format_select {
+	enum HFI_BUFFER buffer;
+	enum HFI_UNCOMPRESSED_FORMAT format;
+};
+
+struct hfi_uncompressed_format_supported {
+	enum HFI_BUFFER buffer;
+	u32 format_entries;
+	u32 rg_format_info[1];
+};
+
+struct hfi_uncompressed_plane_actual {
+	int actual_stride;
+	u32 actual_plane_buffer_height;
+};
+
+struct hfi_uncompressed_plane_actual_info {
+	enum HFI_BUFFER buffer;
+	u32 num_planes;
+	struct hfi_uncompressed_plane_actual rg_plane_format[1];
+};
+
+struct hfi_uncompressed_plane_constraints {
+	u32 stride_multiples;
+	u32 max_stride;
+	u32 min_plane_buffer_height_multiple;
+	u32 buffer_alignment;
+};
+
+struct hfi_uncompressed_plane_info {
+	enum HFI_UNCOMPRESSED_FORMAT format;
+	u32 num_planes;
+	struct hfi_uncompressed_plane_constraints rg_plane_format[1];
+};
+
+struct hfi_uncompressed_plane_actual_constraints_info {
+	enum HFI_BUFFER buffer;
+	u32 num_planes;
+	struct hfi_uncompressed_plane_constraints rg_plane_format[1];
+};
+
+struct hfi_codec_supported {
+	u32 decoder_codec_supported;
+	u32 encoder_codec_supported;
+};
+
+enum HFI_DEBUG_MSG {
+	HFI_DEBUG_MSG_LOW     = 0x00000001,
+	HFI_DEBUG_MSG_MEDIUM  = 0x00000002,
+	HFI_DEBUG_MSG_HIGH    = 0x00000004,
+	HFI_DEBUG_MSG_ERROR   = 0x00000008,
+	HFI_DEBUG_MSG_FATAL   = 0x00000010,
+	HFI_UNUSED_DEBUG_MSG = 0x10000000,
+};
+
+struct hfi_debug_config {
+	u32 debug_config;
+};
+
+struct hfi_properties_supported {
+	u32 num_properties;
+	u32 rg_properties[1];
+};
+
+enum HFI_RESOURCE {
+	HFI_RESOURCE_OCMEM    = 0x00000001,
+	HFI_UNUSED_RESOURCE = 0x10000000,
+};
+
+struct hfi_resource_ocmem_type {
+	u32 size;
+	u8 *mem;
+};
+
+struct hfi_resource_ocmem_requirement {
+	enum HFI_DOMAIN session_domain;
+	u32 width;
+	u32 height;
+	u32 size;
+};
+
+struct hfi_resource_ocmem_requirement_info {
+	u32 num_entries;
+	struct hfi_resource_ocmem_requirement rg_requirements[1];
+};
+
+struct hfi_venc_config_advanced {
+	u8 pipe2d;
+	u8 hw_mode;
+	u8 low_delay_enforce;
+	int h264_constrain_intra_pred;
+	int h264_transform_8x8_flag;
+	int mpeg4_qpel_enable;
+	int multi_refP_en;
+	int qmatrix_en;
+	u8 vpp_info_packet_mode;
+	u8 ref_tile_mode;
+	u8 bitstream_flush_mode;
+	u32 ds_display_frame_width;
+	u32 ds_display_frame_height;
+	u32 perf_tune_param_ptr;
+};
+
+enum HFI_COMMAND {
+	HFI_CMD_SYS_UNUSED = 0x01000000,
+	HFI_CMD_SYS_INIT,
+	HFI_CMD_SYS_SESSION_INIT,
+	HFI_CMD_SYS_SESSION_END,
+	HFI_CMD_SYS_SESSION_ABORT,
+	HFI_CMD_SYS_SET_RESOURCE,
+	HFI_CMD_SYS_RELEASE_RESOURCE,
+	HFI_CMD_SYS_PING,
+	HFI_CMD_SYS_PC_PREP,
+	HFI_CMD_SYS_SET_PROPERTY,
+	HFI_CMD_SYS_GET_PROPERTY,
+
+	HFI_CMD_SESSION_UNUSED = 0x02000000,
+	HFI_CMD_SESSION_LOAD_RESOURCES,
+	HFI_CMD_SESSION_START,
+	HFI_CMD_SESSION_STOP,
+	HFI_CMD_SESSION_EMPTY_BUFFER,
+	HFI_CMD_SESSION_FILL_BUFFER,
+	HFI_CMD_SESSION_FLUSH,
+	HFI_CMD_SESSION_SUSPEND,
+	HFI_CMD_SESSION_RESUME,
+	HFI_CMD_SESSION_SET_PROPERTY,
+	HFI_CMD_SESSION_GET_PROPERTY,
+	HFI_CMD_SESSION_PARSE_SEQUENCE_HEADER,
+	HFI_CMD_SESSION_GET_SEQUENCE_HEADER,
+	HFI_CMD_SESSION_SET_BUFFERS,
+	HFI_CMD_SESSION_RELEASE_BUFFERS,
+	HFI_CMD_SESSION_RELEASE_RESOURCES,
+
+	HFI_CMD_UNUSED = 0x10000000,
+};
+
+enum HFI_MESSAGE {
+	HFI_MSG_SYS_UNUSED = 0x01000000,
+	HFI_MSG_SYS_IDLE,
+	HFI_MSG_SYS_PC_PREP_DONE,
+	HFI_MSG_SYS_RELEASE_RESOURCE,
+	HFI_MSG_SYS_PING_ACK,
+	HFI_MSG_SYS_DEBUG,
+	HFI_MSG_SYS_INIT_DONE,
+	HFI_MSG_SYS_PROPERTY_INFO,
+	HFI_MSG_SESSION_UNUSED = 0x02000000,
+	HFI_MSG_EVENT_NOTIFY,
+	HFI_MSG_SYS_SESSION_INIT_DONE,
+	HFI_MSG_SYS_SESSION_END_DONE,
+	HFI_MSG_SYS_SESSION_ABORT_DONE,
+	HFI_MSG_SESSION_LOAD_RESOURCES_DONE,
+	HFI_MSG_SESSION_START_DONE,
+	HFI_MSG_SESSION_STOP_DONE,
+	HFI_MSG_SESSION_SUSPEND_DONE,
+	HFI_MSG_SESSION_RESUME_DONE,
+	HFI_MSG_SESSION_EMPTY_BUFFER_DONE,
+	HFI_MSG_SESSION_FILL_BUFFER_DONE,
+	HFI_MSG_SESSION_FLUSH_DONE,
+	HFI_MSG_SESSION_PROPERTY_INFO,
+	HFI_MSG_SESSION_RELEASE_RESOURCES_DONE,
+	HFI_MSG_SESSION_PARSE_SEQUENCE_HEADER_DONE,
+	HFI_MSG_SESSION_GET_SEQUENCE_HEADER_DONE,
+	HFI_MSG_UNUSED = 0x10000000,
+};
+
+struct vidc_hal_msg_pkt_hdr {
+	u32 size;
+	enum HFI_MESSAGE packet;
+};
+
+struct vidc_hal_session_cmd_pkt {
+	u32 size;
+	enum HFI_COMMAND packet_type;
+	u32 session_id;
+};
+
+enum HFI_STATUS {
+	HFI_FAIL = 0,
+	HFI_SUCCESS,
+	HFI_UNUSED_STATUS = 0x10000000,
+};
+
+struct hfi_cmd_sys_init_packet {
+	u32 size;
+	enum HFI_COMMAND packet;
+};
+
+struct hfi_cmd_sys_session_init_packet {
+	u32 size;
+	enum HFI_COMMAND packet;
+	u32 session_id;
+	enum HFI_DOMAIN session_domain;
+	enum HFI_VIDEO_CODEC session_codec;
+};
+
+struct hfi_cmd_sys_session_end_packet {
+	u32 size;
+	enum HFI_COMMAND packet_type;
+	u32 session_id;
+};
+
+struct hfi_cmd_sys_session_abort_packet {
+	u32 size;
+	enum HFI_COMMAND packet_type;
+	u32 session_id;
+};
+
+struct hfi_cmd_sys_pc_prep_packet {
+	u32 size;
+	enum HFI_COMMAND packet_type;
+};
+
+struct hfi_cmd_sys_set_resource_packet {
+	u32 size;
+	enum HFI_COMMAND packet_type;
+	u32 resource_handle;
+	enum HFI_RESOURCE resource_type;
+	u32 rg_resource_data[1];
+};
+
+struct hfi_cmd_sys_release_resource_packet {
+	u32 size;
+	enum HFI_COMMAND packet_type;
+	enum HFI_RESOURCE resource_type;
+	u32 resource_handle;
+};
+
+struct hfi_cmd_sys_ping_packet {
+	u32 size;
+	enum HFI_COMMAND packet_type;
+	u32 client_data;
+};
+
+struct hfi_cmd_sys_set_property_packet {
+	u32 size;
+	enum HFI_COMMAND packet_type;
+	u32 num_properties;
+	u32 rg_property_data[1];
+};
+
+struct hfi_cmd_sys_get_property_packet {
+	u32 size;
+	enum HFI_COMMAND packet_type;
+	u32 num_properties;
+	enum HFI_PROPERTY rg_property_data[1];
+};
+
+struct hfi_cmd_session_load_resources_packet {
+	u32 size;
+	enum HFI_COMMAND packet_type;
+	u32 session_id;
+};
+
+struct hfi_cmd_session_start_packet {
+	u32 size;
+	enum HFI_COMMAND packet_type;
+	u32 session_id;
+};
+
+struct hfi_cmd_session_stop_packet {
+	u32 size;
+	enum HFI_COMMAND packet_type;
+	u32 session_id;
+};
+
+struct hfi_cmd_session_empty_buffer_compressed_packet {
+	u32 size;
+	enum HFI_COMMAND packet_type;
+	u32 session_id;
+	u32 timestamp_hi;
+	u32 timestamp_lo;
+	u32 flags;
+	u32 mark_target;
+	u32 mark_data;
+	u32 offset;
+	u32 alloc_len;
+	u32 filled_len;
+	u32 input_tag;
+	u8 *packet_buffer;
+};
+
+struct hfi_cmd_session_empty_buffer_uncompressed_plane0_packet {
+	u32 size;
+	enum HFI_COMMAND packet;
+	u32 session_id;
+	u32 view_id;
+	u32 timestamp_hi;
+	u32 timestamp_lo;
+	u32 flags;
+	u32 mark_target;
+	u32 mark_data;
+	u32 alloc_len;
+	u32 filled_len;
+	u32 offset;
+	u32 input_tag;
+	u8 *packet_buffer;
+};
+
+struct hfi_cmd_session_empty_buffer_uncompressed_plane1_packet {
+	u32 flags;
+	u32 alloc_len;
+	u32 filled_len;
+	u32 offset;
+	u8 *packet_buffer2;
+};
+
+struct hfi_cmd_session_empty_buffer_uncompressed_plane2_packet {
+	u32 flags;
+	u32 alloc_len;
+	u32 filled_len;
+	u32 offset;
+	u8 *packet_buffer3;
+};
+
+struct hfi_cmd_session_fill_buffer_packet {
+	u32 size;
+	enum HFI_COMMAND packet_type;
+	u32 session_id;
+	u32 stream_id;
+	u8 *packet_buffer;
+	u8 *extra_data_buffer;
+};
+
+struct hfi_cmd_session_flush_packet {
+	u32 size;
+	enum HFI_COMMAND packet_type;
+	u32 session_id;
+	enum HFI_FLUSH flush_type;
+};
+
+struct hfi_cmd_session_suspend_packet {
+	u32 size;
+	enum HFI_COMMAND packet;
+	u32 session_id;
+};
+
+struct hfi_cmd_session_resume_packet {
+	u32 size;
+	enum HFI_COMMAND packet_type;
+	u32 session_id;
+};
+
+struct hfi_cmd_session_set_property_packet {
+	u32 size;
+	enum HFI_COMMAND packet_type;
+	u32 session_id;
+	u32 num_properties;
+	u32 rg_property_data[1];
+};
+
+struct hfi_cmd_session_get_property_packet {
+	u32 size;
+	enum HFI_COMMAND packet_type;
+	u32 session_id;
+	u32 num_properties;
+	enum HFI_PROPERTY rg_property_data[1];
+};
+
+struct hfi_buffer_info {
+	u32 buffer_addr;
+	u32 extradata_addr;
+};
+
+struct hfi_cmd_session_set_buffers_packet {
+	u32 size;
+	enum HFI_COMMAND packet_type;
+	u32 session_id;
+	enum HFI_BUFFER buffer_type;
+	enum HFI_BUFFER_MODE buffer_mode;
+	u32 buffer_size;
+	u32 extradata_size;
+	u32 min_buffer_size;
+	u32 num_buffers;
+	u32 rg_buffer_info[1];
+};
+
+struct hfi_cmd_session_release_buffer_packet {
+	u32 size;
+	enum HFI_COMMAND packet_type;
+	u32 session_id;
+	enum HFI_BUFFER buffer_type;
+	u32 buffer_size;
+	u32 extradata_size;
+	u32 num_buffers;
+	u32 rg_buffer_info[1];
+};
+
+struct hfi_cmd_session_release_resources_packet {
+	u32 size;
+	enum HFI_COMMAND packet_type;
+	u32 session_id;
+};
+
+struct hfi_cmd_session_parse_sequence_header_packet {
+	u32 size;
+	enum HFI_COMMAND packet_type;
+	u32 session_id;
+	u32 header_len;
+	u8 *packet_buffer;
+};
+
+struct hfi_cmd_session_get_sequence_header_packet {
+	u32 size;
+	enum HFI_COMMAND packet_type;
+	u32 session_id;
+	u32 buffer_len;
+	u8 *packet_buffer;
+};
+
+struct hfi_msg_event_notify_packet {
+	u32 size;
+	enum HFI_MESSAGE packet_type;
+	u32 session_id;
+	enum HFI_EVENT event_id;
+	u32 event_data1;
+	u32 event_data2;
+	u32 rg_ext_event_data[1];
+};
+
+struct hfi_msg_sys_init_done_packet {
+	u32 size;
+	enum HFI_MESSAGE packet_type;
+	enum HFI_ERROR error_type;
+	u32 num_properties;
+	u32 rg_property_data[1];
+};
+
+struct hfi_msg_sys_session_init_done_packet {
+	u32 size;
+	enum HFI_MESSAGE packet_type;
+	u32 session_id;
+	enum HFI_ERROR error_type;
+	u32 num_properties;
+	u32 rg_property_data[1];
+};
+
+struct hfi_msg_sys_session_end_done_packet {
+	u32 size;
+	enum HFI_MESSAGE packet_type;
+	u32 session_id;
+	enum HFI_ERROR error_type;
+};
+
+struct hfi_msg_sys_session_abort_done_packet {
+	u32 size;
+	enum HFI_MESSAGE packet_type;
+	u32 session_id;
+	enum HFI_ERROR error_type;
+};
+
+struct hfi_msg_sys_idle_packet {
+	u32 size;
+	enum HFI_MESSAGE packet_type;
+};
+
+struct hfi_msg_sys_pc_prep_done_packet {
+	u32 size;
+	enum HFI_MESSAGE packet_type;
+	enum HFI_ERROR error_type;
+};
+
+struct hfi_msg_sys_release_resource_done_packet {
+	u32 size;
+	enum HFI_MESSAGE packet_type;
+	u32 resource_handle;
+	enum HFI_ERROR error_type;
+};
+
+struct hfi_msg_sys_ping_ack_packet {
+	u32 size;
+	enum HFI_MESSAGE packet_type;
+	u32 client_data;
+};
+
+struct hfi_msg_sys_debug_packet {
+	u32 size;
+	enum HFI_MESSAGE packet_type;
+	enum HFI_DEBUG_MSG msg_type;
+	u32 msg_size;
+	u32 timestamp_hi;
+	u32 timestamp_lo;
+	u8 rg_msg_data[1];
+};
+
+struct hfi_msg_sys_property_info_packet {
+	u32 nsize;
+	enum HFI_MESSAGE packet_type;
+	u32 num_properties;
+	u32 rg_property_data[1];
+};
+
+struct hfi_msg_session_load_resources_done_packet {
+	u32 size;
+	enum HFI_MESSAGE packet_type;
+	u32 session_id;
+	enum HFI_ERROR error_type;
+};
+
+struct hfi_msg_session_start_done_packet {
+	u32 size;
+	enum HFI_MESSAGE packet_type;
+	u32 session_id;
+	enum HFI_ERROR error_type;
+};
+
+struct hfi_msg_session_stop_done_packet {
+	u32 size;
+	enum HFI_MESSAGE packet_type;
+	u32 session_id;
+	enum HFI_ERROR error_type;
+};
+
+struct hfi_msg_session_suspend_done_packet {
+	u32 size;
+	enum HFI_MESSAGE packet_type;
+	u32 session_id;
+	enum HFI_ERROR error_type;
+};
+
+struct hfi_msg_session_resume_done_packet {
+	u32 size;
+	enum HFI_MESSAGE packet_type;
+	u32 session_id;
+	enum HFI_ERROR error_type;
+};
+
+struct hfi_msg_session_empty_buffer_done_packet {
+	u32 size;
+	enum HFI_MESSAGE packet_type;
+	u32 session_id;
+	enum HFI_ERROR error_type;
+	u32 offset;
+	u32 filled_len;
+	u32 input_tag;
+	u8 *packet_buffer;
+};
+
+struct hfi_msg_session_fill_buffer_done_compressed_packet {
+	u32 size;
+	enum HFI_MESSAGE packet_type;
+	u32 session_id;
+	u32 timestamp_hi;
+	u32 timestamp_lo;
+	enum HFI_ERROR error_type;
+	u32 flags;
+	u32 mark_target;
+	u32 mark_data;
+	u32 stats;
+	u32 offset;
+	u32 alloc_len;
+	u32 filled_len;
+	u32 input_tag;
+	enum HFI_PICTURE picture_type;
+	u8 *packet_buffer;
+	u8 *extra_data_buffer;
+};
+
+struct hfi_msg_session_fbd_uncompressed_plane0_packet {
+	u32 size;
+	enum HFI_MESSAGE packet_type;
+	u32 session_id;
+	u32 stream_id;
+	u32 view_id;
+	enum HFI_ERROR error_type;
+	u32 timestamp_hi;
+	u32 timestamp_lo;
+	u32 flags;
+	u32 mark_target;
+	u32 mark_data;
+	u32 stats;
+	u32 alloc_len;
+	u32 filled_len;
+	u32 oofset;
+	u32 frame_width;
+	u32 frame_height;
+	u32 start_xCoord;
+	u32 start_yCoord;
+	u32 input_tag;
+	u32 input_tag1;
+	enum HFI_PICTURE picture_type;
+	u8 *packet_buffer;
+	u8 *extra_data_buffer;
+};
+
+struct hfi_msg_session_fill_buffer_done_uncompressed_plane1_packet {
+	u32 flags;
+	u32 alloc_len;
+	u32 filled_len;
+	u32 offset;
+	u8 *packet_buffer;
+};
+
+struct hfi_msg_session_fill_buffer_done_uncompressed_plane2_packet {
+	u32 flags;
+	u32 alloc_len;
+	u32 filled_len;
+	u32 offset;
+	u8 *packet_buffer;
+};
+
+struct hfi_msg_session_flush_done_packet {
+	u32 size;
+	enum HFI_MESSAGE packet_type;
+	u32 session_id;
+	enum HFI_ERROR error_type;
+	enum HFI_FLUSH flush_type;
+};
+
+struct hfi_msg_session_parse_sequence_header_done_packet {
+	u32 size;
+	enum HFI_MESSAGE packet_type;
+	u32 session_id;
+	enum HFI_ERROR error_type;
+	u32 num_properties;
+	u32 rg_property_data[1];
+};
+
+struct hfi_msg_session_get_sequence_header_done_packet {
+	u32 size;
+	enum HFI_MESSAGE packet_type;
+	u32 session_id;
+	enum HFI_ERROR error_type;
+	u32 header_len;
+	u8 *sequence_header;
+};
+
+struct hfi_msg_session_property_info_packet {
+	u32 size;
+	enum HFI_MESSAGE packet_type;
+	u32 session_id;
+	u32 num_properties;
+	u32 rg_property_data[1];
+};
+
+struct hfi_msg_session_release_resources_done_packet {
+	u32 size;
+	enum HFI_MESSAGE packet_type;
+	u32 session_id;
+	enum HFI_ERROR error_type;
+};
+
+struct hfi_extradata_mb_quantization_payload {
+	u8 rg_mb_qp[1];
+};
+
+struct hfi_extradata_vc1_pswnd {
+	u32 ps_wnd_h_offset;
+	u32 ps_wndv_offset;
+	u32 ps_wnd_width;
+	u32 ps_wnd_height;
+};
+
+struct hfi_extradata_vc1_framedisp_payload {
+	u32 res_pic;
+	u32 ref;
+	u32 range_map_present;
+	u32 range_map_y;
+	u32 range_map_uv;
+	u32 num_pan_scan_wnds;
+	struct hfi_extradata_vc1_pswnd rg_ps_wnd[1];
+};
+
+struct hfi_extradata_vc1_seqdisp_payload {
+	u32 prog_seg_frm;
+	u32 uv_sampling_fmt;
+	u32 color_fmt_flag;
+	u32 color_primaries;
+	u32 transfer_char;
+	u32 mat_coeff;
+	u32 aspect_ratio;
+	u32 aspect_horiz;
+	u32 aspect_vert;
+};
+
+struct hfi_extradata_timestamp_payload {
+	u32 timestamp_low;
+	u32 timestamp_high;
+};
+
+struct hfi_extradata_interlace_video_payload {
+	enum HFI_INTERLACE_FORMAT format;
+};
+
+enum HFI_S3D_FP_LAYOUT {
+	HFI_S3D_FP_LAYOUT_NONE,
+	HFI_S3D_FP_LAYOUT_INTRLV_CHECKERBOARD,
+	HFI_S3D_FP_LAYOUT_INTRLV_COLUMN,
+	HFI_S3D_FP_LAYOUT_INTRLV_ROW,
+	HFI_S3D_FP_LAYOUT_SIDEBYSIDE,
+	HFI_S3D_FP_LAYOUT_TOPBOTTOM,
+	HFI_S3D_FP_LAYOUT_UNUSED = 0x10000000,
+};
+
+enum HFI_S3D_FP_VIEW_ORDER {
+	HFI_S3D_FP_LEFTVIEW_FIRST,
+	HFI_S3D_FP_RIGHTVIEW_FIRST,
+	HFI_S3D_FP_UNKNOWN,
+	HFI_S3D_FP_VIEWORDER_UNUSED = 0x10000000,
+};
+
+enum HFI_S3D_FP_FLIP {
+	HFI_S3D_FP_FLIP_NONE,
+	HFI_S3D_FP_FLIP_LEFT_HORIZ,
+	HFI_S3D_FP_FLIP_LEFT_VERT,
+	HFI_S3D_FP_FLIP_RIGHT_HORIZ,
+	HFI_S3D_FP_FLIP_RIGHT_VERT,
+	HFI_S3D_FP_FLIP_UNUSED = 0x10000000,
+};
+
+struct hfi_extradata_s3d_frame_packing_payload {
+	enum HFI_S3D_FP_LAYOUT eLayout;
+	enum HFI_S3D_FP_VIEW_ORDER eOrder;
+	enum HFI_S3D_FP_FLIP eFlip;
+	int bQuinCunx;
+	u32 nLeftViewLumaSiteX;
+	u32 nLeftViewLumaSiteY;
+	u32 nRightViewLumaSiteX;
+	u32 nRightViewLumaSiteY;
+};
+
+struct hfi_extradata_num_concealed_mb_payload {
+	u32 num_mb_concealed;
+};
+
+struct hfi_extradata_sliceinfo {
+	u32 offset_in_stream;
+	u32 slice_length;
+};
+
+struct hfi_extradata_multislice_info_payload {
+	u32 num_slices;
+	struct hfi_extradata_sliceinfo rg_slice_info[1];
+};
+
+struct hfi_index_extradata_input_crop_payload {
+	u32 size;
+	u32 version;
+	u32 port_index;
+	u32 left;
+	u32 top;
+	u32 width;
+	u32 height;
+};
+
+struct hfi_index_extradata_digital_zoom_payload {
+	u32 size;
+	u32 version;
+	u32 port_index;
+	int width;
+	int height;
+};
+
+struct vidc_mem_addr {
+	u8 *align_device_addr;
+	u8 *align_virtual_addr;
+	u32 mem_size;
+	struct msm_smem *mem_data;
+};
+
+struct vidc_iface_q_info {
+	void *q_hdr;
+	struct vidc_mem_addr q_array;
+};
+
+/* Internal data used in vidc_hal not exposed to msm_vidc*/
+
+struct hal_data {
+	u32 irq;
+	u8 *device_base_addr;
+	u8 *register_base_addr;
+};
+
+struct hal_device {
+	struct list_head list;
+	struct list_head sess_head;
+	u32 intr_status;
+	u32 device_id;
+	spinlock_t read_lock;
+	spinlock_t write_lock;
+	void (*callback) (u32 response, void *callback);
+	struct vidc_mem_addr iface_q_table;
+	struct vidc_iface_q_info iface_queues[VIDC_IFACEQ_NUMQ];
+	struct smem_client *hal_client;
+	struct hal_data *hal_data;
+	struct workqueue_struct *vidc_workq;
+	int spur_count;
+	int reg_count;
+};
+
+struct hal_session {
+	struct list_head list;
+	u32 session_id;
+	u32 is_decoder;
+	struct hal_device *device;
+};
+
+struct hal_device_data {
+	struct list_head dev_head;
+	int dev_count;
+};
+
+extern struct hal_device_data hal_ctxt;
+
+int vidc_hal_iface_msgq_read(struct hal_device *device, void *pkt);
+int vidc_hal_iface_dbgq_read(struct hal_device *device, void *pkt);
+
+/* Interrupt Processing:*/
+void vidc_hal_response_handler(struct hal_device *device);
+
+#endif /*__VIDC_HAL_H__ */
diff --git a/drivers/media/video/msm_vidc/vidc_hal_api.h b/drivers/media/video/msm_vidc/vidc_hal_api.h
new file mode 100644
index 0000000..036091b
--- /dev/null
+++ b/drivers/media/video/msm_vidc/vidc_hal_api.h
@@ -0,0 +1,975 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __VIDC_HAL_API_H__
+#define __VIDC_HAL_API_H__
+
+#include <linux/types.h>
+
+#define CONTAINS(__a, __sz, __t) ({\
+	int __rc = __t >= __a && \
+			__t < __a + __sz; \
+	__rc; \
+})
+
+#define OVERLAPS(__t, __tsz, __a, __asz) ({\
+	int __rc = __t <= __a && \
+			__t + __tsz >= __a + __asz; \
+	__rc; \
+})
+
+#define HAL_BUFFERFLAG_EOS              0x00000001
+#define HAL_BUFFERFLAG_STARTTIME        0x00000002
+#define HAL_BUFFERFLAG_DECODEONLY       0x00000004
+#define HAL_BUFFERFLAG_DATACORRUPT      0x00000008
+#define HAL_BUFFERFLAG_ENDOFFRAME       0x00000010
+#define HAL_BUFFERFLAG_SYNCFRAME        0x00000020
+#define HAL_BUFFERFLAG_EXTRADATA        0x00000040
+#define HAL_BUFFERFLAG_CODECCONFIG      0x00000080
+#define HAL_BUFFERFLAG_TIMESTAMPINVALID 0x00000100
+#define HAL_BUFFERFLAG_READONLY         0x00000200
+#define HAL_BUFFERFLAG_ENDOFSUBFRAME    0x00000400
+
+enum vidc_status {
+	VIDC_ERR_NONE = 0x0,
+	VIDC_ERR_FAIL = 0x80000000,
+	VIDC_ERR_ALLOC_FAIL,
+	VIDC_ERR_ILLEGAL_OP,
+	VIDC_ERR_BAD_PARAM,
+	VIDC_ERR_BAD_HANDLE,
+	VIDC_ERR_NOT_SUPPORTED,
+	VIDC_ERR_BAD_STATE,
+	VIDC_ERR_MAX_CLIENT,
+	VIDC_ERR_IFRAME_EXPECTED,
+	VIDC_ERR_HW_FATAL,
+	VIDC_ERR_BITSTREAM_ERR,
+	VIDC_ERR_INDEX_NOMORE,
+	VIDC_ERR_SEQHDR_PARSE_FAIL,
+	VIDC_ERR_INSUFFICIENT_BUFFER,
+	VIDC_ERR_BAD_POWER_STATE,
+	VIDC_ERR_NO_VALID_SESSION,
+	VIDC_ERR_TIMEOUT,
+	VIDC_ERR_CMDQFULL,
+	VIDC_ERR_CLIENT_PRESENT = 0x90000001,
+	VIDC_ERR_CLIENT_FATAL,
+	VIDC_ERR_CMD_QUEUE_FULL,
+	VIDC_ERR_UNUSED = 0x10000000
+};
+
+enum hal_property {
+	HAL_CONFIG_FRAME_RATE = 0x04000001,
+	HAL_PARAM_UNCOMPRESSED_FORMAT_SELECT,
+	HAL_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO,
+	HAL_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO,
+	HAL_PARAM_EXTRA_DATA_HEADER_CONFIG,
+	HAL_PARAM_FRAME_SIZE,
+	HAL_CONFIG_REALTIME,
+	HAL_PARAM_BUFFER_COUNT_ACTUAL,
+	HAL_PARAM_NAL_STREAM_FORMAT_SELECT,
+	HAL_PARAM_VDEC_OUTPUT_ORDER,
+	HAL_PARAM_VDEC_PICTURE_TYPE_DECODE,
+	HAL_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO,
+	HAL_CONFIG_VDEC_POST_LOOP_DEBLOCKER,
+	HAL_PARAM_VDEC_MULTI_STREAM,
+	HAL_PARAM_VDEC_DISPLAY_PICTURE_BUFFER_COUNT,
+	HAL_PARAM_DIVX_FORMAT,
+	HAL_CONFIG_VDEC_MB_ERROR_MAP_REPORTING,
+	HAL_PARAM_VDEC_CONTINUE_DATA_TRANSFER,
+	HAL_CONFIG_VDEC_MB_ERROR_MAP,
+	HAL_CONFIG_VENC_REQUEST_IFRAME,
+	HAL_PARAM_VENC_MPEG4_SHORT_HEADER,
+	HAL_PARAM_VENC_MPEG4_AC_PREDICTION,
+	HAL_CONFIG_VENC_TARGET_BITRATE,
+	HAL_PARAM_PROFILE_LEVEL_CURRENT,
+	HAL_PARAM_VENC_H264_ENTROPY_CONTROL,
+	HAL_PARAM_VENC_RATE_CONTROL,
+	HAL_PARAM_VENC_MPEG4_TIME_RESOLUTION,
+	HAL_PARAM_VENC_MPEG4_HEADER_EXTENSION,
+	HAL_PARAM_VENC_H264_DEBLOCK_CONTROL,
+	HAL_PARAM_VENC_TEMPORAL_SPATIAL_TRADEOFF,
+	HAL_PARAM_VENC_SESSION_QP,
+	HAL_CONFIG_VENC_INTRA_PERIOD,
+	HAL_CONFIG_VENC_IDR_PERIOD,
+	HAL_CONFIG_VPE_OPERATIONS,
+	HAL_PARAM_VENC_INTRA_REFRESH,
+	HAL_PARAM_VENC_MULTI_SLICE_CONTROL,
+	HAL_CONFIG_VPE_DEINTERLACE,
+	HAL_SYS_DEBUG_CONFIG,
+	HAL_CONFIG_BUFFER_REQUIREMENTS,
+	HAL_CONFIG_PRIORITY,
+	HAL_CONFIG_BATCH_INFO,
+	HAL_PARAM_METADATA_PASS_THROUGH,
+	HAL_SYS_IDLE_INDICATOR,
+	HAL_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED,
+	HAL_PARAM_INTERLACE_FORMAT_SUPPORTED,
+	HAL_PARAM_CHROMA_SITE,
+	HAL_PARAM_PROPERTIES_SUPPORTED,
+	HAL_PARAM_PROFILE_LEVEL_SUPPORTED,
+	HAL_PARAM_CAPABILITY_SUPPORTED,
+	HAL_PARAM_NAL_STREAM_FORMAT_SUPPORTED,
+	HAL_PARAM_MULTI_VIEW_FORMAT,
+	HAL_PARAM_MAX_SEQUENCE_HEADER_SIZE,
+	HAL_PARAM_CODEC_SUPPORTED,
+	HAL_PARAM_VDEC_MULTI_VIEW_SELECT,
+	HAL_PARAM_VDEC_MB_QUANTIZATION,
+	HAL_PARAM_VDEC_NUM_CONCEALED_MB,
+	HAL_PARAM_VDEC_H264_ENTROPY_SWITCHING,
+	HAL_PARAM_VENC_SLICE_DELIVERY_MODE,
+	HAL_PARAM_VENC_MPEG4_DATA_PARTITIONING,
+	HAL_CONFIG_BUFFER_COUNT_ACTUAL,
+	HAL_CONFIG_VDEC_MULTI_STREAM,
+	HAL_PARAM_VENC_MULTI_SLICE_INFO,
+	HAL_CONFIG_VENC_TIMESTAMP_SCALE,
+	HAL_PARAM_VENC_LOW_LATENCY,
+};
+
+enum hal_domain {
+	HAL_VIDEO_DOMAIN_VPE,
+	HAL_VIDEO_DOMAIN_ENCODER,
+	HAL_VIDEO_DOMAIN_DECODER,
+	HAL_UNUSED_DOMAIN = 0x10000000,
+};
+
+enum hal_video_codec {
+	HAL_VIDEO_CODEC_UNKNOWN  = 0x00000000,
+	HAL_VIDEO_CODEC_MVC      = 0x00000001,
+	HAL_VIDEO_CODEC_H264     = 0x00000002,
+	HAL_VIDEO_CODEC_H263     = 0x00000004,
+	HAL_VIDEO_CODEC_MPEG1    = 0x00000008,
+	HAL_VIDEO_CODEC_MPEG2    = 0x00000010,
+	HAL_VIDEO_CODEC_MPEG4    = 0x00000020,
+	HAL_VIDEO_CODEC_DIVX_311 = 0x00000040,
+	HAL_VIDEO_CODEC_DIVX     = 0x00000080,
+	HAL_VIDEO_CODEC_VC1      = 0x00000100,
+	HAL_VIDEO_CODEC_SPARK    = 0x00000200,
+	HAL_VIDEO_CODEC_VP6      = 0x00000400,
+	HAL_VIDEO_CODEC_VP7      = 0x00000800,
+	HAL_VIDEO_CODEC_VP8      = 0x00001000,
+	HAL_UNUSED_CODEC = 0x10000000,
+};
+
+enum hal_h263_profile {
+	HAL_H263_PROFILE_BASELINE           = 0x00000001,
+	HAL_H263_PROFILE_H320CODING         = 0x00000002,
+	HAL_H263_PROFILE_BACKWARDCOMPATIBLE = 0x00000004,
+	HAL_H263_PROFILE_ISWV2              = 0x00000008,
+	HAL_H263_PROFILE_ISWV3              = 0x00000010,
+	HAL_H263_PROFILE_HIGHCOMPRESSION    = 0x00000020,
+	HAL_H263_PROFILE_INTERNET           = 0x00000040,
+	HAL_H263_PROFILE_INTERLACE          = 0x00000080,
+	HAL_H263_PROFILE_HIGHLATENCY        = 0x00000100,
+	HAL_UNUSED_H263_PROFILE = 0x10000000,
+};
+
+enum hal_h263_level {
+	HAL_H263_LEVEL_10 = 0x00000001,
+	HAL_H263_LEVEL_20 = 0x00000002,
+	HAL_H263_LEVEL_30 = 0x00000004,
+	HAL_H263_LEVEL_40 = 0x00000008,
+	HAL_H263_LEVEL_45 = 0x00000010,
+	HAL_H263_LEVEL_50 = 0x00000020,
+	HAL_H263_LEVEL_60 = 0x00000040,
+	HAL_H263_LEVEL_70 = 0x00000080,
+	HAL_UNUSED_H263_LEVEL = 0x10000000,
+};
+
+enum hal_mpeg2_profile {
+	HAL_MPEG2_PROFILE_SIMPLE  = 0x00000001,
+	HAL_MPEG2_PROFILE_MAIN    = 0x00000002,
+	HAL_MPEG2_PROFILE_422     = 0x00000004,
+	HAL_MPEG2_PROFILE_SNR     = 0x00000008,
+	HAL_MPEG2_PROFILE_SPATIAL = 0x00000010,
+	HAL_MPEG2_PROFILE_HIGH    = 0x00000020,
+	HAL_UNUSED_MPEG2_PROFILE = 0x10000000,
+};
+
+enum hal_mpeg2_level {
+	HAL_MPEG2_LEVEL_LL  = 0x00000001,
+	HAL_MPEG2_LEVEL_ML  = 0x00000002,
+	HAL_MPEG2_LEVEL_H14 = 0x00000004,
+	HAL_MPEG2_LEVEL_HL  = 0x00000008,
+	HAL_UNUSED_MEPG2_LEVEL = 0x10000000,
+};
+
+enum hal_mpeg4_profile {
+	HAL_MPEG4_PROFILE_SIMPLE           = 0x00000001,
+	HAL_MPEG4_PROFILE_SIMPLESCALABLE   = 0x00000002,
+	HAL_MPEG4_PROFILE_CORE             = 0x00000004,
+	HAL_MPEG4_PROFILE_MAIN             = 0x00000008,
+	HAL_MPEG4_PROFILE_NBIT             = 0x00000010,
+	HAL_MPEG4_PROFILE_SCALABLETEXTURE  = 0x00000020,
+	HAL_MPEG4_PROFILE_SIMPLEFACE       = 0x00000040,
+	HAL_MPEG4_PROFILE_SIMPLEFBA        = 0x00000080,
+	HAL_MPEG4_PROFILE_BASICANIMATED    = 0x00000100,
+	HAL_MPEG4_PROFILE_HYBRID           = 0x00000200,
+	HAL_MPEG4_PROFILE_ADVANCEDREALTIME = 0x00000400,
+	HAL_MPEG4_PROFILE_CORESCALABLE     = 0x00000800,
+	HAL_MPEG4_PROFILE_ADVANCEDCODING   = 0x00001000,
+	HAL_MPEG4_PROFILE_ADVANCEDCORE     = 0x00002000,
+	HAL_MPEG4_PROFILE_ADVANCEDSCALABLE = 0x00004000,
+	HAL_MPEG4_PROFILE_ADVANCEDSIMPLE   = 0x00008000,
+	HAL_UNUSED_MPEG4_PROFILE = 0x10000000,
+};
+
+enum hal_mpeg4_level {
+	HAL_MPEG4_LEVEL_0  = 0x00000001,
+	HAL_MPEG4_LEVEL_0b = 0x00000002,
+	HAL_MPEG4_LEVEL_1  = 0x00000004,
+	HAL_MPEG4_LEVEL_2  = 0x00000008,
+	HAL_MPEG4_LEVEL_3  = 0x00000010,
+	HAL_MPEG4_LEVEL_4  = 0x00000020,
+	HAL_MPEG4_LEVEL_4a = 0x00000040,
+	HAL_MPEG4_LEVEL_5  = 0x00000080,
+	HAL_MPEG4_LEVEL_VENDOR_START_UNUSED = 0x7F000000,
+	HAL_MPEG4_LEVEL_6  = 0x7F000001,
+	HAL_MPEG4_LEVEL_7  = 0x7F000002,
+	HAL_MPEG4_LEVEL_8  = 0x7F000003,
+	HAL_MPEG4_LEVEL_9  = 0x7F000004,
+	HAL_MPEG4_LEVEL_3b = 0x7F000005,
+	HAL_UNUSED_MPEG4_LEVEL = 0x10000000,
+};
+
+enum hal_h264_profile {
+	HAL_H264_PROFILE_BASELINE = 0x00000001,
+	HAL_H264_PROFILE_MAIN     = 0x00000002,
+	HAL_H264_PROFILE_EXTENDED = 0x00000004,
+	HAL_H264_PROFILE_HIGH     = 0x00000008,
+	HAL_H264_PROFILE_HIGH10   = 0x00000010,
+	HAL_H264_PROFILE_HIGH422  = 0x00000020,
+	HAL_H264_PROFILE_HIGH444  = 0x00000040,
+	HAL_UNUSED_H264_PROFILE = 0x10000000,
+};
+
+enum hal_h264_level {
+	HAL_H264_LEVEL_1  = 0x00000001,
+	HAL_H264_LEVEL_1b = 0x00000002,
+	HAL_H264_LEVEL_11 = 0x00000004,
+	HAL_H264_LEVEL_12 = 0x00000008,
+	HAL_H264_LEVEL_13 = 0x00000010,
+	HAL_H264_LEVEL_2  = 0x00000020,
+	HAL_H264_LEVEL_21 = 0x00000040,
+	HAL_H264_LEVEL_22 = 0x00000080,
+	HAL_H264_LEVEL_3  = 0x00000100,
+	HAL_H264_LEVEL_31 = 0x00000200,
+	HAL_H264_LEVEL_32 = 0x00000400,
+	HAL_H264_LEVEL_4  = 0x00000800,
+	HAL_H264_LEVEL_41 = 0x00001000,
+	HAL_H264_LEVEL_42 = 0x00002000,
+	HAL_H264_LEVEL_5  = 0x00004000,
+	HAL_H264_LEVEL_51 = 0x00008000,
+	HAL_UNUSED_H264_LEVEL = 0x10000000,
+};
+
+enum hal_vpx_profile {
+	HAL_VPX_PROFILE_SIMPLE    = 0x00000001,
+	HAL_VPX_PROFILE_ADVANCED  = 0x00000002,
+	HAL_VPX_PROFILE_VERSION_0 = 0x00000004,
+	HAL_VPX_PROFILE_VERSION_1 = 0x00000008,
+	HAL_VPX_PROFILE_VERSION_2 = 0x00000010,
+	HAL_VPX_PROFILE_VERSION_3 = 0x00000020,
+	HAL_VPX_PROFILE_UNUSED = 0x10000000,
+};
+
+enum hal_vc1_profile {
+	HAL_VC1_PROFILE_SIMPLE   = 0x00000001,
+	HAL_VC1_PROFILE_MAIN     = 0x00000002,
+	HAL_VC1_PROFILE_ADVANCED = 0x00000004,
+	HAL_UNUSED_VC1_PROFILE = 0x10000000,
+};
+
+enum hal_vc1_level {
+	HAL_VC1_LEVEL_LOW    = 0x00000001,
+	HAL_VC1_LEVEL_MEDIUM = 0x00000002,
+	HAL_VC1_LEVEL_HIGH   = 0x00000004,
+	HAL_VC1_LEVEL_0      = 0x00000008,
+	HAL_VC1_LEVEL_1      = 0x00000010,
+	HAL_VC1_LEVEL_2      = 0x00000020,
+	HAL_VC1_LEVEL_3      = 0x00000040,
+	HAL_VC1_LEVEL_4      = 0x00000080,
+	HAL_UNUSED_VC1_LEVEL = 0x10000000,
+};
+
+enum hal_divx_format {
+	HAL_DIVX_FORMAT_4,
+	HAL_DIVX_FORMAT_5,
+	HAL_DIVX_FORMAT_6,
+	HAL_UNUSED_DIVX_FORMAT = 0x10000000,
+};
+
+enum hal_divx_profile {
+	HAL_DIVX_PROFILE_QMOBILE  = 0x00000001,
+	HAL_DIVX_PROFILE_MOBILE   = 0x00000002,
+	HAL_DIVX_PROFILE_MT       = 0x00000004,
+	HAL_DIVX_PROFILE_HT       = 0x00000008,
+	HAL_DIVX_PROFILE_HD       = 0x00000010,
+	HAL_UNUSED_DIVX_PROFILE = 0x10000000,
+};
+
+enum hal_mvc_profile {
+	HAL_MVC_PROFILE_STEREO_HIGH  = 0x00000001,
+	HAL_MVC_PROFILE_MV_HIGH      = 0x00000002,
+	HAL_UNUSED_MVC_PROFILE = 0x10000000,
+};
+
+enum hal_mvc_level {
+	HAL_MVC_LEVEL_1  = 0x00000001,
+	HAL_MVC_LEVEL_1b = 0x00000002,
+	HAL_MVC_LEVEL_11 = 0x00000004,
+	HAL_MVC_LEVEL_12 = 0x00000008,
+	HAL_MVC_LEVEL_13 = 0x00000010,
+	HAL_MVC_LEVEL_2  = 0x00000020,
+	HAL_MVC_LEVEL_21 = 0x00000040,
+	HAL_MVC_LEVEL_22 = 0x00000080,
+	HAL_MVC_LEVEL_3  = 0x00000100,
+	HAL_MVC_LEVEL_31 = 0x00000200,
+	HAL_MVC_LEVEL_32 = 0x00000400,
+	HAL_MVC_LEVEL_4  = 0x00000800,
+	HAL_MVC_LEVEL_41 = 0x00001000,
+	HAL_MVC_LEVEL_42 = 0x00002000,
+	HAL_MVC_LEVEL_5  = 0x00004000,
+	HAL_MVC_LEVEL_51 = 0x00008000,
+	HAL_UNUSED_MVC_LEVEL = 0x10000000,
+};
+
+enum hal_buffer {
+	HAL_BUFFER_INPUT,
+	HAL_BUFFER_OUTPUT,
+	HAL_BUFFER_OUTPUT2,
+	HAL_BUFFER_EXTRADATA_INPUT,
+	HAL_BUFFER_EXTRADATA_OUTPUT,
+	HAL_BUFFER_EXTRADATA_OUTPUT2,
+	HAL_BUFFER_INTERNAL_SCRATCH,
+	HAL_BUFFER_INTERNAL_PERSIST,
+	HAL_UNUSED_BUFFER = 0x10000000,
+};
+
+struct hal_frame_rate {
+	enum hal_buffer buffer_type;
+	u32 frame_rate;
+};
+
+enum hal_uncompressed_format {
+	HAL_COLOR_FORMAT_MONOCHROME,
+	HAL_COLOR_FORMAT_NV12,
+	HAL_COLOR_FORMAT_NV21,
+	HAL_COLOR_FORMAT_NV12_4x4TILE,
+	HAL_COLOR_FORMAT_NV21_4x4TILE,
+	HAL_COLOR_FORMAT_YUYV,
+	HAL_COLOR_FORMAT_YVYU,
+	HAL_COLOR_FORMAT_UYVY,
+	HAL_COLOR_FORMAT_VYUY,
+	HAL_COLOR_FORMAT_RGB565,
+	HAL_COLOR_FORMAT_BGR565,
+	HAL_COLOR_FORMAT_RGB888,
+	HAL_COLOR_FORMAT_BGR888,
+	HAL_UNUSED_COLOR = 0x10000000,
+};
+
+struct hal_uncompressed_format_select {
+	enum hal_buffer buffer_type;
+	enum hal_uncompressed_format format;
+};
+
+struct hal_uncompressed_plane_actual {
+	int actual_stride;
+	u32 actual_plane_buffer_height;
+};
+
+struct hal_uncompressed_plane_actual_info {
+	enum hal_buffer buffer_type;
+	u32 num_planes;
+	struct hal_uncompressed_plane_actual rg_plane_format[1];
+};
+
+struct hal_uncompressed_plane_constraints {
+	u32 stride_multiples;
+	u32 max_stride;
+	u32 min_plane_buffer_height_multiple;
+	u32 buffer_alignment;
+};
+
+struct hal_uncompressed_plane_actual_constraints_info {
+	enum hal_buffer buffer_type;
+	u32 num_planes;
+	struct hal_uncompressed_plane_constraints rg_plane_format[1];
+};
+
+struct hal_extra_data_header_config {
+	u32 type;
+	enum hal_buffer buffer_type;
+	u32 version;
+	u32 port_index;
+	u32 client_extradata_id;
+};
+
+struct hal_frame_size {
+	enum hal_buffer buffer_type;
+	u32 width;
+	u32 height;
+};
+
+struct hal_enable {
+	u32 enable;
+};
+
+struct hal_buffer_count_actual {
+	enum hal_buffer buffer_type;
+	u32 buffer_count_actual;
+};
+
+enum hal_nal_stream_format {
+	HAL_NAL_FORMAT_STARTCODES         = 0x00000001,
+	HAL_NAL_FORMAT_ONE_NAL_PER_BUFFER = 0x00000002,
+	HAL_NAL_FORMAT_ONE_BYTE_LENGTH    = 0x00000004,
+	HAL_NAL_FORMAT_TWO_BYTE_LENGTH    = 0x00000008,
+	HAL_NAL_FORMAT_FOUR_BYTE_LENGTH   = 0x00000010,
+};
+
+enum hal_output_order {
+	HAL_OUTPUT_ORDER_DISPLAY,
+	HAL_OUTPUT_ORDER_DECODE,
+	HAL_UNUSED_OUTPUT = 0x10000000,
+};
+
+enum hal_picture {
+	HAL_PICTURE_I = 0x01,
+	HAL_PICTURE_P = 0x02,
+	HAL_PICTURE_B = 0x04,
+	HAL_PICTURE_IDR = 0x7F001000,
+	HAL_FRAME_NOTCODED = 0x7F002000,
+	HAL_FRAME_YUV = 0x7F004000,
+	HAL_UNUSED_PICT = 0x10000000,
+};
+
+struct hal_enable_picture {
+	u32 picture_type;
+};
+
+struct hal_multi_stream {
+	enum hal_buffer buffer_type;
+	u32 enable;
+	u32 width;
+	u32 height;
+};
+
+struct hal_display_picture_buffer_count {
+	u32 enable;
+	u32 count;
+};
+
+struct hal_mb_error_map {
+	u32 error_map_size;
+	u8 rg_error_map[1];
+};
+
+struct hal_request_iframe {
+	u32 enable;
+};
+
+struct hal_bitrate {
+	u32 bit_rate;
+};
+
+struct hal_profile_level {
+	u32 profile;
+	u32 level;
+};
+/*
+struct hal_profile_level_range {
+	u32 profile;
+	u32 min_level;
+	u32 max_level;
+}
+
+struct hal_profile_level_supported {
+	u32 profile_count;
+	struct hal_profile_level_range profile_level[1];
+};
+*/
+enum hal_h264_entropy {
+	HAL_H264_ENTROPY_CAVLC,
+	HAL_H264_ENTROPY_CABAC,
+	HAL_UNUSED_ENTROPY = 0x10000000,
+};
+
+enum hal_h264_cabac_model {
+	HAL_H264_CABAC_MODEL_0,
+	HAL_H264_CABAC_MODEL_1,
+	HAL_H264_CABAC_MODEL_2,
+	HAL_UNUSED_CABAC = 0x10000000,
+};
+
+struct hal_h264_entropy_control {
+	enum hal_h264_entropy entropy_mode;
+	enum hal_h264_cabac_model cabac_model;
+};
+
+enum hal_rate_control {
+	HAL_RATE_CONTROL_OFF,
+	HAL_RATE_CONTROL_VBR_VFR,
+	HAL_RATE_CONTROL_VBR_CFR,
+	HAL_RATE_CONTROL_CBR_VFR,
+	HAL_RATE_CONTROL_CBR_CFR,
+	HAL_UNUSED_RC = 0x10000000,
+};
+
+struct hal_mpeg4_time_resolution {
+	u32 time_increment_resolution;
+};
+
+struct hal_mpeg4_header_extension {
+	u32 header_extension;
+};
+
+enum hal_h264_db_mode {
+	HAL_H264_DB_MODE_DISABLE,
+	HAL_H264_DB_MODE_SKIP_SLICE_BOUNDARY,
+	HAL_H264_DB_MODE_ALL_BOUNDARY,
+	HAL_UNUSED_H264_DB = 0x10000000,
+};
+
+struct hal_h264_db_control {
+	enum hal_h264_db_mode mode;
+	int slice_alpha_offset;
+	int slicebeta_offset;
+};
+
+struct hal_temporal_spatial_tradeoff {
+	u32 ts_factor;
+};
+
+struct hal_quantization {
+	u32 qpi;
+	u32 qpp;
+	u32 qpb;
+};
+
+struct hal_intra_period {
+	u32 pframes;
+	u32 bframes;
+};
+
+struct hal_idr_period {
+	u32 idr_period;
+};
+
+enum hal_rotate {
+	HAL_ROTATE_NONE,
+	HAL_ROTATE_90,
+	HAL_ROTATE_180,
+	HAL_ROTATE_270,
+	HAL_UNUSED_ROTATE = 0x10000000,
+};
+
+enum hal_flip {
+	HAL_FLIP_NONE,
+	HAL_FLIP_HORIZONTAL,
+	HAL_FLIP_VERTICAL,
+	HAL_UNUSED_FLIP = 0x10000000,
+};
+
+struct hal_operations {
+	enum hal_rotate rotate;
+	enum hal_flip flip;
+};
+
+enum hal_intra_refresh_mode {
+	HAL_INTRA_REFRESH_NONE,
+	HAL_INTRA_REFRESH_CYCLIC,
+	HAL_INTRA_REFRESH_ADAPTIVE,
+	HAL_INTRA_REFRESH_CYCLIC_ADAPTIVE,
+	HAL_INTRA_REFRESH_RANDOM,
+	HAL_UNUSED_INTRA = 0x10000000,
+};
+
+struct hal_intra_refresh {
+	enum hal_intra_refresh_mode mode;
+	u32 air_mbs;
+	u32 air_ref;
+	u32 cir_mbs;
+};
+
+enum hal_multi_slice {
+	HAL_MULTI_SLICE_OFF,
+	HAL_MULTI_SLICE_BY_MB_COUNT,
+	HAL_MULTI_SLICE_BY_BYTE_COUNT,
+	HAL_MULTI_SLICE_GOB,
+	HAL_UNUSED_SLICE = 0x10000000,
+};
+
+struct hal_multi_slice_control {
+	enum hal_multi_slice multi_slice;
+	u32 slice_size;
+};
+
+struct hal_debug_config {
+	u32 debug_config;
+};
+
+struct hal_buffer_requirements {
+	enum hal_buffer buffer_type;
+	u32 buffer_size;
+	u32 buffer_region_size;
+	u32 buffer_hold_count;
+	u32 buffer_count_min;
+	u32 buffer_count_actual;
+	u32 contiguous;
+	u32 buffer_alignment;
+};
+
+enum hal_priority {/* Priority increases with number */
+	HAL_PRIORITY_LOW = 10,
+	HAL_PRIOIRTY_MEDIUM = 20,
+	HAL_PRIORITY_HIGH = 30,
+	HAL_UNUSED_PRIORITY = 0x10000000,
+};
+
+struct hal_batch_info {
+	u32 input_batch_count;
+	u32 output_batch_count;
+};
+
+struct hal_metadata_pass_through {
+	u32 enable;
+	u32 size;
+};
+
+struct hal_uncompressed_format_supported {
+	enum hal_buffer buffer_type;
+	u32 format_entries;
+	u32 rg_format_info[1];
+};
+
+enum hal_interlace_format {
+	HAL_INTERLACE_FRAME_PROGRESSIVE                 = 0x01,
+	HAL_INTERLACE_INTERLEAVE_FRAME_TOPFIELDFIRST    = 0x02,
+	HAL_INTERLACE_INTERLEAVE_FRAME_BOTTOMFIELDFIRST = 0x04,
+	HAL_INTERLACE_FRAME_TOPFIELDFIRST               = 0x08,
+	HAL_INTERLACE_FRAME_BOTTOMFIELDFIRST            = 0x10,
+	HAL_UNUSED_INTERLACE = 0x10000000,
+};
+
+struct hal_interlace_format_supported {
+	enum hal_buffer buffer_type;
+	enum hal_interlace_format format;
+};
+
+enum hal_chroma_site {
+	HAL_CHROMA_SITE_0,
+	HAL_CHROMA_SITE_1,
+	HAL_UNUSED_CHROMA = 0x10000000,
+};
+
+struct hal_properties_supported {
+	u32 num_properties;
+	u32 rg_properties[1];
+};
+
+enum hal_capability {
+	HAL_CAPABILITY_FRAME_WIDTH,
+	HAL_CAPABILITY_FRAME_HEIGHT,
+	HAL_CAPABILITY_MBS_PER_FRAME,
+	HAL_CAPABILITY_MBS_PER_SECOND,
+	HAL_CAPABILITY_FRAMERATE,
+	HAL_CAPABILITY_SCALE_X,
+	HAL_CAPABILITY_SCALE_Y,
+	HAL_CAPABILITY_BITRATE,
+	HAL_UNUSED_CAPABILITY = 0x10000000,
+};
+
+struct hal_capability_supported {
+	enum hal_capability capability_type;
+	u32 min;
+	u32 max;
+	u32 step_size;
+};
+
+struct hal_capability_supported_info {
+	u32 num_capabilities;
+	struct hal_capability_supported rg_data[1];
+};
+
+struct hal_nal_stream_format_supported {
+	u32 nal_stream_format_supported;
+};
+
+struct hal_multi_view_format {
+	u32 views;
+	u32 rg_view_order[1];
+};
+
+struct hal_seq_header_info {
+	u32 nax_header_len;
+};
+
+struct hal_codec_supported {
+	u32 decoder_codec_supported;
+	u32 encoder_codec_supported;
+};
+
+struct hal_multi_view_select {
+	u32 view_index;
+};
+
+struct hal_timestamp_scale {
+	u32 time_stamp_scale;
+};
+
+enum vidc_resource_id {
+	VIDC_RESOURCE_OCMEM = 0x00000001,
+	VIDC_UNUSED_RESORUCE = 0x10000000,
+};
+
+struct vidc_resource_hdr {
+	enum vidc_resource_id resource_id;
+	u32 resource_handle;
+	u32 size;
+};
+
+struct vidc_buffer_addr_info {
+	enum hal_buffer buffer_type;
+	u32 buffer_size;
+	u32 num_buffers;
+	u32 align_device_addr;
+	u32 extradata_size;
+	u32 extradata_addr;
+};
+
+struct vidc_frame_plane_config {
+	u32 left;
+	u32 top;
+	u32 width;
+	u32 height;
+	u32 stride;
+	u32 scan_lines;
+};
+
+struct vidc_uncompressed_frame_config {
+	struct vidc_frame_plane_config luma_plane;
+	struct vidc_frame_plane_config chroma_plane;
+};
+
+struct vidc_frame_data {
+	enum hal_buffer buffer_type;
+	u32 device_addr;
+	u32 extradata_addr;
+	int64_t timestamp;
+	u32 flags;
+	u32 offset;
+	u32 alloc_len;
+	u32 filled_len;
+	u32 mark_target;
+	u32 mark_data;
+	u32 clnt_data;
+};
+
+struct vidc_seq_hdr {
+	u8 *seq_hdr;
+	u32 seq_hdr_len;
+};
+
+enum hal_flush {
+	HAL_FLUSH_INPUT,
+	HAL_FLUSH_OUTPUT,
+	HAL_FLUSH_OUTPUT2,
+	HAL_FLUSH_ALL,
+	HAL_UNUSED_FLUSH = 0x10000000,
+};
+
+/* HAL Response */
+
+enum command_response {
+/* SYSTEM COMMANDS_DONE*/
+	VIDC_EVENT_CHANGE,
+	SYS_INIT_DONE,
+	SET_RESOURCE_DONE,
+	RELEASE_RESOURCE_DONE,
+	PING_ACK_DONE,
+	PC_PREP_DONE,
+	SYS_IDLE,
+	SYS_DEBUG,
+/* SESSION COMMANDS_DONE */
+	SESSION_LOAD_RESOURCE_DONE,
+	SESSION_INIT_DONE,
+	SESSION_END_DONE,
+	SESSION_ABORT_DONE,
+	SESSION_START_DONE,
+	SESSION_STOP_DONE,
+	SESSION_ETB_DONE,
+	SESSION_FTB_DONE,
+	SESSION_FLUSH_DONE,
+	SESSION_SUSPEND_DONE,
+	SESSION_RESUME_DONE,
+	SESSION_SET_PROP_DONE,
+	SESSION_GET_PROP_DONE,
+	SESSION_PARSE_SEQ_HDR_DONE,
+	SESSION_GET_SEQ_HDR_DONE,
+	SESSION_RELEASE_BUFFER_DONE,
+	SESSION_RELEASE_RESOURCE_DONE,
+	SESSION_PROPERTY_INFO,
+	RESPONSE_UNUSED = 0x10000000,
+};
+
+/* Command Callback structure */
+
+struct msm_vidc_cb_cmd_done {
+	u32 device_id;
+	u32 session_id;
+	u32 status;
+	u32 size;
+	void *data;
+};
+
+struct msm_vidc_cb_event {
+	u32 device_id;
+	u32 session_id;
+	u32 status;
+	u32 height;
+	u32 width;
+};
+
+/* Data callback structure */
+
+struct vidc_hal_ebd {
+	u32 timestamp_hi;
+	u32 timestamp_lo;
+	u32 flags;
+	u32 mark_target;
+	u32 mark_data;
+	u32 stats;
+	u32 offset;
+	u32 alloc_len;
+	u32 filled_len;
+	enum hal_picture picture_type;
+	u8 *packet_buffer;
+	u8 *extra_data_buffer;
+};
+
+struct vidc_hal_fbd {
+	u32 stream_id;
+	u32 view_id;
+	u32 timestamp_hi;
+	u32 timestamp_lo;
+	u32 flags1;
+	u32 mark_target;
+	u32 mark_data;
+	u32 stats;
+	u32 alloc_len1;
+	u32 filled_len1;
+	u32 offset1;
+	u32 frame_width;
+	u32 frame_height;
+	u32 start_xCoord;
+	u32 start_yCoord;
+	u32 input_tag;
+	u32 input_tag1;
+	enum hal_picture picture_type;
+	u8 *packet_buffer1;
+	u8 *extra_data_buffer;
+	u32 flags2;
+	u32 alloc_len2;
+	u32 filled_len2;
+	u32 offset2;
+	u8 *packet_buffer2;
+	u32 flags3;
+	u32 alloc_len3;
+	u32 filled_len3;
+	u32 offset3;
+	u8 *packet_buffer3;
+	enum hal_buffer buffer_type;
+};
+
+struct msm_vidc_cb_data_done {
+	u32 device_id;
+	u32 session_id;
+	u32 status;
+	u32 size;
+	void *clnt_data;
+	union {
+		struct vidc_hal_ebd input_done;
+		struct vidc_hal_fbd output_done;
+	};
+};
+
+struct vidc_hal_sys_init_done {
+	u32 enc_codec_supported;
+	u32 dec_codec_supported;
+};
+
+struct vidc_hal_session_init_done {
+	struct hal_capability_supported width;
+	struct hal_capability_supported height;
+	struct hal_capability_supported mbs_per_frame;
+	struct hal_capability_supported mbs_per_sec;
+	struct hal_capability_supported frame_rate;
+	struct hal_capability_supported scale_x;
+	struct hal_capability_supported scale_y;
+	struct hal_capability_supported bitrate;
+	struct hal_uncompressed_format_supported uncomp_format;
+	struct hal_interlace_format_supported HAL_format;
+	struct hal_nal_stream_format_supported nal_stream_format;
+/*	struct hal_profile_level_supported profile_level;
+	// allocate and released memory for above. */
+	struct hal_intra_refresh intra_refresh;
+	struct hal_seq_header_info seq_hdr_info;
+};
+
+struct buffer_requirements {
+	struct hal_buffer_requirements buffer[8];
+};
+
+/* VIDC_HAL CORE API's */
+int vidc_hal_core_init(void *device);
+int vidc_hal_core_release(void *device);
+int vidc_hal_core_pc_prep(void *device);
+int vidc_hal_core_set_resource(void *device,
+	struct vidc_resource_hdr *resource_hdr, void *resource_value);
+int vidc_hal_core_release_resource(void *device,
+	struct vidc_resource_hdr *resource_hdr);
+int vidc_hal_core_ping(void *device);
+
+/* VIDC_HAL SESSION API's */
+void *vidc_hal_session_init(void *device, u32 session_id,
+	enum hal_domain session_type, enum hal_video_codec codec_type);
+int vidc_hal_session_end(void *session);
+int vidc_hal_session_abort(void *session);
+int vidc_hal_session_set_buffers(void *sess,
+	struct vidc_buffer_addr_info *buffer_info);
+int vidc_hal_session_release_buffers(void *sess,
+	struct vidc_buffer_addr_info *buffer_info);
+int vidc_hal_session_load_res(void *sess);
+int vidc_hal_session_release_res(void *sess);
+int vidc_hal_session_start(void *sess);
+int vidc_hal_session_stop(void *sess);
+int vidc_hal_session_suspend(void *sess);
+int vidc_hal_session_resume(void *sess);
+int vidc_hal_session_etb(void *sess,
+	struct vidc_frame_data *input_frame);
+int vidc_hal_session_ftb(void *sess,
+	struct vidc_frame_data *output_frame);
+int vidc_hal_session_parse_seq_hdr(void *sess,
+	struct vidc_seq_hdr *seq_hdr);
+int vidc_hal_session_get_seq_hdr(void *sess,
+	struct vidc_seq_hdr *seq_hdr);
+int vidc_hal_session_get_buf_req(void *sess);
+int vidc_hal_session_flush(void *sess, enum hal_flush flush_mode);
+int vidc_hal_session_set_property(void *sess, enum hal_property ptype,
+								  void *pdata);
+int vidc_hal_session_get_property(void *sess, enum hal_property ptype,
+								  void *pdata);
+void *vidc_hal_add_device(u32 device_id, u32 base_addr,
+	u32 reg_base, u32 reg_size, u32 irq,
+	void (*callback) (enum command_response cmd, void *data));
+void vidc_hal_delete_device(void *device);
+
+#endif /*__VIDC_HAL_API_H__ */
diff --git a/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c b/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
new file mode 100644
index 0000000..cb44d3a
--- /dev/null
+++ b/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
@@ -0,0 +1,781 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/list.h>
+#include "vidc_hal.h"
+
+static enum vidc_status vidc_map_hal_err_status(enum HFI_ERROR hfi_err)
+{
+	enum vidc_status vidc_err;
+	switch (hfi_err) {
+	case HFI_ERR_NONE:
+	case HFI_ERR_SESSION_SAME_STATE_OPERATION:
+		vidc_err = VIDC_ERR_NONE;
+		break;
+	case HFI_ERR_SYS_FATAL:
+		vidc_err = VIDC_ERR_HW_FATAL;
+		break;
+	case HFI_ERR_SYS_VERSION_MISMATCH:
+	case HFI_ERR_SYS_INVALID_PARAMETER:
+	case HFI_ERR_SYS_SESSION_ID_OUT_OF_RANGE:
+	case HFI_ERR_SESSION_INVALID_PARAMETER:
+	case HFI_ERR_SESSION_INVALID_SESSION_ID:
+	case HFI_ERR_SESSION_INVALID_STREAM_ID:
+		vidc_err = VIDC_ERR_BAD_PARAM;
+		break;
+	case HFI_ERR_SYS_INSUFFICIENT_RESOURCES:
+	case HFI_ERR_SYS_UNSUPPORTED_DOMAIN:
+	case HFI_ERR_SYS_UNSUPPORTED_CODEC:
+	case HFI_ERR_SESSION_UNSUPPORTED_PROPERTY:
+	case HFI_ERR_SESSION_UNSUPPORTED_SETTING:
+	case HFI_ERR_SESSION_INSUFFICIENT_RESOURCES:
+		vidc_err = VIDC_ERR_NOT_SUPPORTED;
+		break;
+	case HFI_ERR_SYS_MAX_SESSIONS_REACHED:
+		vidc_err = VIDC_ERR_MAX_CLIENT;
+		break;
+	case HFI_ERR_SYS_SESSION_IN_USE:
+		vidc_err = VIDC_ERR_CLIENT_PRESENT;
+		break;
+	case HFI_ERR_SESSION_FATAL:
+		vidc_err = VIDC_ERR_CLIENT_FATAL;
+		break;
+	case HFI_ERR_SESSION_BAD_POINTER:
+		vidc_err = VIDC_ERR_BAD_PARAM;
+		break;
+	case HFI_ERR_SESSION_INCORRECT_STATE_OPERATION:
+		vidc_err = VIDC_ERR_BAD_STATE;
+		break;
+	case HFI_ERR_SESSION_STREAM_CORRUPT:
+	case HFI_ERR_SESSION_STREAM_CORRUPT_OUTPUT_STALLED:
+		vidc_err = VIDC_ERR_BITSTREAM_ERR;
+		break;
+	case HFI_ERR_SESSION_SYNC_FRAME_NOT_DETECTED:
+		vidc_err = VIDC_ERR_IFRAME_EXPECTED;
+		break;
+	case HFI_ERR_SYS_UNKNOWN:
+	case HFI_ERR_SESSION_UNKNOWN:
+	case HFI_ERR_SESSION_EMPTY_BUFFER_DONE_OUTPUT_PENDING:
+	default:
+		vidc_err = VIDC_ERR_FAIL;
+		break;
+	}
+	return vidc_err;
+}
+
+void hal_process_sess_evt_seq_changed(struct hal_device *device,
+	struct hfi_msg_event_notify_packet *pkt)
+{
+	struct msm_vidc_cb_cmd_done cmd_done;
+	struct msm_vidc_cb_event event_notify;
+	int num_properties_changed;
+	struct hfi_frame_size frame_sz;
+	u8 *data_ptr;
+	enum HFI_PROPERTY prop_id;
+	HAL_MSG_LOW("RECEIVED:EVENT_NOTIFY");
+	if (sizeof(struct hfi_msg_event_notify_packet)
+		> pkt->size) {
+		HAL_MSG_ERROR("hal_process_session_init_done:bad_pkt_size");
+		return;
+	}
+
+	memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
+	memset(&event_notify, 0, sizeof(struct
+				msm_vidc_cb_event));
+
+	cmd_done.device_id = device->device_id;
+	cmd_done.session_id = ((struct hal_session *) pkt->session_id)->
+		session_id;
+	cmd_done.status = VIDC_ERR_NONE;
+	cmd_done.size = sizeof(struct msm_vidc_cb_event);
+	num_properties_changed = pkt->event_data2;
+	if (num_properties_changed) {
+		data_ptr = (u8 *) &pkt->rg_ext_event_data[0];
+		do {
+			prop_id = (enum HFI_PROPERTY) *((u32 *)data_ptr);
+			switch (prop_id) {
+			case HFI_PROPERTY_PARAM_FRAME_SIZE:
+				frame_sz.buffer =
+					(enum HFI_BUFFER)
+						*((((u32 *)data_ptr)+1));
+				frame_sz.width =
+					event_notify.width =
+						*((((u32 *)data_ptr)+2));
+				frame_sz.height =
+					event_notify.height =
+						*((((u32 *)data_ptr)+3));
+				data_ptr += 4;
+			break;
+			default:
+			break;
+			}
+			num_properties_changed--;
+		} while (num_properties_changed > 0);
+	}
+	cmd_done.data = &event_notify;
+	device->callback(VIDC_EVENT_CHANGE, &cmd_done);
+}
+
+static void hal_process_event_notify(struct hal_device *device,
+	struct hfi_msg_event_notify_packet *pkt)
+{
+	HAL_MSG_LOW("RECVD:EVENT_NOTIFY");
+
+	if (!device || !pkt ||
+		pkt->size < sizeof(struct hfi_msg_event_notify_packet)) {
+		HAL_MSG_ERROR("Invalid Params in %s", __func__);
+		return;
+	}
+
+	switch (pkt->event_id) {
+	case HFI_EVENT_SYS_ERROR:
+		HAL_MSG_INFO("HFI_EVENT_SYS_ERROR");
+		break;
+	case HFI_EVENT_SESSION_ERROR:
+		HAL_MSG_INFO("HFI_EVENT_SESSION_ERROR");
+		break;
+	case HFI_EVENT_SESSION_SEQUENCE_CHANGED:
+		HAL_MSG_INFO("HFI_EVENT_SESSION_SEQUENCE_CHANGED");
+		hal_process_sess_evt_seq_changed(device, pkt);
+		break;
+	case HFI_EVENT_SESSION_PROPERTY_CHANGED:
+		HAL_MSG_INFO("HFI_EVENT_SESSION_PROPERTY_CHANGED");
+		break;
+	default:
+		HAL_MSG_INFO("hal_process_event_notify:unkown_event_id");
+		break;
+	}
+}
+
+static void hal_process_sys_init_done(struct hal_device *device,
+		struct hfi_msg_sys_init_done_packet *pkt)
+{
+	struct msm_vidc_cb_cmd_done cmd_done;
+	struct vidc_hal_sys_init_done sys_init_done;
+	u32 rem_bytes, bytes_read = 0, num_properties;
+	u8 *data_ptr;
+	enum HFI_PROPERTY prop_id;
+	enum vidc_status status = VIDC_ERR_NONE;
+
+	HAL_MSG_LOW("RECEIVED:SYS_INIT_DONE");
+	if (sizeof(struct hfi_msg_sys_init_done_packet) > pkt->size) {
+		HAL_MSG_ERROR("hal_process_sys_init_done:bad_pkt_size: %d",
+				pkt->size);
+		return;
+	}
+
+	status = vidc_map_hal_err_status((u32)pkt->error_type);
+
+	if (!status) {
+		if (pkt->num_properties == 0) {
+			HAL_MSG_ERROR("hal_process_sys_init_done:"
+						"no_properties");
+			status = VIDC_ERR_FAIL;
+			goto err_no_prop;
+		}
+
+		rem_bytes = pkt->size - sizeof(struct
+			hfi_msg_sys_init_done_packet) + sizeof(u32);
+
+		if (rem_bytes == 0) {
+			HAL_MSG_ERROR("hal_process_sys_init_done:"
+						"missing_prop_info");
+			status = VIDC_ERR_FAIL;
+			goto err_no_prop;
+		}
+		memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
+		memset(&sys_init_done, 0, sizeof(struct
+				vidc_hal_sys_init_done));
+
+		data_ptr = (u8 *) &pkt->rg_property_data[0];
+		num_properties = pkt->num_properties;
+
+		while ((num_properties != 0) && (rem_bytes >= sizeof(u32))) {
+			prop_id = (enum HFI_PROPERTY) *((u32 *)data_ptr);
+			data_ptr = data_ptr + 4;
+
+			switch (prop_id) {
+			case HFI_PROPERTY_PARAM_CODEC_SUPPORTED:
+			{
+				struct hfi_codec_supported *prop =
+					(struct hfi_codec_supported *) data_ptr;
+				if (rem_bytes < sizeof(struct
+						hfi_codec_supported)) {
+					status = VIDC_ERR_BAD_PARAM;
+					break;
+				}
+				sys_init_done.dec_codec_supported =
+					prop->decoder_codec_supported;
+				sys_init_done.enc_codec_supported =
+					prop->encoder_codec_supported;
+				break;
+			}
+			default:
+				HAL_MSG_ERROR("hal_process_sys_init_done:"
+							"bad_prop_id");
+				status = VIDC_ERR_BAD_PARAM;
+				break;
+			}
+			if (!status) {
+				rem_bytes -= bytes_read;
+				data_ptr += bytes_read;
+				num_properties--;
+			}
+		}
+	}
+err_no_prop:
+	cmd_done.device_id = device->device_id;
+	cmd_done.session_id = 0;
+	cmd_done.status = (u32) status;
+	cmd_done.size = sizeof(struct vidc_hal_sys_init_done);
+	cmd_done.data = (void *) &sys_init_done;
+	device->callback(SYS_INIT_DONE, &cmd_done);
+}
+
+enum vidc_status vidc_hal_process_sess_init_done_prop_read(
+	struct hfi_msg_sys_session_init_done_packet *pkt,
+	struct msm_vidc_cb_cmd_done *cmddone)
+{
+	return VIDC_ERR_NONE;
+}
+
+static void hal_process_sess_get_prop_buf_req(
+	struct hfi_msg_session_property_info_packet *prop,
+	struct buffer_requirements *buffreq)
+{
+	struct hfi_buffer_requirements *hfi_buf_req;
+	u32 req_bytes;
+	enum vidc_status rc = VIDC_ERR_NONE;
+
+	HAL_MSG_LOW("Entered %s", __func__);
+	req_bytes = prop->size - sizeof(
+	struct hfi_msg_session_property_info_packet);
+
+	if (req_bytes == 0 || (req_bytes % sizeof(
+		struct hfi_buffer_requirements))) {
+		HAL_MSG_ERROR("hal_process_sess_get_prop_buf_req:bad_pkt_size:"
+					" %d", req_bytes);
+		return;
+	}
+
+	hfi_buf_req = (struct hfi_buffer_requirements *)
+		&prop->rg_property_data[1];
+
+	while (req_bytes != 0) {
+		if ((hfi_buf_req->buffer_count_min > hfi_buf_req->
+			buffer_count_actual)
+			|| (hfi_buf_req->buffer_alignment == 0)
+			|| (hfi_buf_req->buffer_size == 0)) {
+			HAL_MSG_ERROR("hal_process_sess_get_prop_buf_req:"
+						"bad_buf_req");
+			rc = VIDC_ERR_FAIL;
+		}
+		HAL_MSG_LOW("got buffer requirements for: %d",
+					hfi_buf_req->buffer);
+		switch (hfi_buf_req->buffer) {
+		case HFI_BUFFER_INPUT:
+			memcpy(&buffreq->buffer[0], hfi_buf_req,
+				sizeof(struct hfi_buffer_requirements));
+			buffreq->buffer[0].buffer_type = HAL_BUFFER_INPUT;
+			break;
+		case HFI_BUFFER_OUTPUT:
+			memcpy(&buffreq->buffer[1], hfi_buf_req,
+			sizeof(struct hfi_buffer_requirements));
+			buffreq->buffer[1].buffer_type = HAL_BUFFER_OUTPUT;
+			break;
+		case HFI_BUFFER_OUTPUT2:
+			memcpy(&buffreq->buffer[2], hfi_buf_req,
+				sizeof(struct hfi_buffer_requirements));
+			buffreq->buffer[2].buffer_type = HAL_BUFFER_OUTPUT2;
+			break;
+		case HFI_BUFFER_EXTRADATA_INPUT:
+			memcpy(&buffreq->buffer[3], hfi_buf_req,
+				sizeof(struct hfi_buffer_requirements));
+			buffreq->buffer[3].buffer_type =
+				HAL_BUFFER_EXTRADATA_INPUT;
+			break;
+		case HFI_BUFFER_EXTRADATA_OUTPUT:
+			memcpy(&buffreq->buffer[4], hfi_buf_req,
+				sizeof(struct hfi_buffer_requirements));
+			buffreq->buffer[4].buffer_type =
+				HAL_BUFFER_EXTRADATA_OUTPUT;
+			break;
+		case HFI_BUFFER_EXTRADATA_OUTPUT2:
+			memcpy(&buffreq->buffer[5], hfi_buf_req,
+				sizeof(struct hfi_buffer_requirements));
+			buffreq->buffer[5].buffer_type =
+				HAL_BUFFER_EXTRADATA_OUTPUT2;
+			break;
+		case HFI_BUFFER_INTERNAL_SCRATCH:
+			memcpy(&buffreq->buffer[6], hfi_buf_req,
+			sizeof(struct hfi_buffer_requirements));
+			buffreq->buffer[6].buffer_type =
+				HAL_BUFFER_INTERNAL_SCRATCH;
+			break;
+		case HFI_BUFFER_INTERNAL_PERSIST:
+			memcpy(&buffreq->buffer[7], hfi_buf_req,
+			sizeof(struct hfi_buffer_requirements));
+			buffreq->buffer[7].buffer_type =
+				HAL_BUFFER_INTERNAL_PERSIST;
+			break;
+		default:
+			HAL_MSG_ERROR("hal_process_sess_get_prop_buf_req:"
+			"bad_buffer_type: %d", hfi_buf_req->buffer);
+			break;
+		}
+		req_bytes -= sizeof(struct hfi_buffer_requirements);
+		hfi_buf_req++;
+	}
+}
+
+static void hal_process_session_prop_info(struct hal_device *device,
+	struct hfi_msg_session_property_info_packet *pkt)
+{
+	struct msm_vidc_cb_cmd_done cmd_done;
+	struct buffer_requirements buff_req;
+
+	HAL_MSG_INFO("Received SESSION_PROPERTY_INFO");
+
+	if (pkt->size < sizeof(struct hfi_msg_session_property_info_packet)) {
+		HAL_MSG_ERROR("hal_process_session_prop_info:bad_pkt_size");
+		return;
+	}
+
+	if (pkt->num_properties == 0) {
+		HAL_MSG_ERROR("hal_process_session_prop_info:no_properties");
+		return;
+	}
+
+	memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
+	memset(&buff_req, 0, sizeof(struct buffer_requirements));
+
+	switch (pkt->rg_property_data[0]) {
+	case HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS:
+		hal_process_sess_get_prop_buf_req(pkt, &buff_req);
+		cmd_done.device_id = device->device_id;
+		cmd_done.session_id =
+			((struct hal_session *) pkt->session_id)->session_id;
+		cmd_done.status = VIDC_ERR_NONE;
+		cmd_done.data = &buff_req;
+		cmd_done.size = sizeof(struct buffer_requirements);
+		device->callback(SESSION_PROPERTY_INFO, &cmd_done);
+		break;
+	default:
+		HAL_MSG_ERROR("hal_process_session_prop_info:"
+					"unknown_prop_id: %d",
+				pkt->rg_property_data[0]);
+		break;
+	}
+}
+
+static void hal_process_session_init_done(struct hal_device *device,
+	struct hfi_msg_sys_session_init_done_packet *pkt)
+{
+	struct msm_vidc_cb_cmd_done cmd_done;
+	struct vidc_hal_session_init_done session_init_done;
+
+	HAL_MSG_LOW("RECEIVED:SESSION_INIT_DONE");
+	if (sizeof(struct hfi_msg_sys_session_init_done_packet)
+		> pkt->size) {
+		HAL_MSG_ERROR("hal_process_session_init_done:bad_pkt_size");
+		return;
+	}
+
+	memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
+	memset(&session_init_done, 0, sizeof(struct
+				vidc_hal_session_init_done));
+
+	cmd_done.device_id = device->device_id;
+	cmd_done.session_id =
+		((struct hal_session *) pkt->session_id)->session_id;
+	cmd_done.status = vidc_map_hal_err_status((u32)pkt->error_type);
+	cmd_done.data = &session_init_done;
+	if (!cmd_done.status) {
+		cmd_done.status = vidc_hal_process_sess_init_done_prop_read(
+			pkt, &cmd_done);
+	}
+	cmd_done.size = sizeof(struct vidc_hal_session_init_done);
+	device->callback(SESSION_INIT_DONE, &cmd_done);
+}
+
+static void hal_process_session_load_res_done(struct hal_device *device,
+	struct hfi_msg_session_load_resources_done_packet *pkt)
+{
+	struct msm_vidc_cb_cmd_done cmd_done;
+	HAL_MSG_LOW("RECEIVED:SESSION_LOAD_RESOURCES_DONE");
+
+	if (sizeof(struct hfi_msg_session_load_resources_done_packet) !=
+		pkt->size) {
+		HAL_MSG_ERROR("hal_process_session_load_res_done:"
+		" bad packet size: %d", pkt->size);
+		return;
+	}
+
+	memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
+
+	cmd_done.device_id = device->device_id;
+	cmd_done.session_id =
+		((struct hal_session *) pkt->session_id)->session_id;
+	cmd_done.status = vidc_map_hal_err_status((u32)pkt->error_type);
+	cmd_done.data = NULL;
+	cmd_done.size = 0;
+	device->callback(SESSION_LOAD_RESOURCE_DONE, &cmd_done);
+}
+
+static void hal_process_session_flush_done(struct hal_device *device,
+	struct hfi_msg_session_flush_done_packet *pkt)
+{
+	struct msm_vidc_cb_cmd_done cmd_done;
+
+	HAL_MSG_LOW("RECEIVED:SESSION_FLUSH_DONE");
+
+	if (sizeof(struct hfi_msg_session_flush_done_packet) != pkt->size) {
+		HAL_MSG_ERROR("hal_process_session_flush_done: "
+		"bad packet size: %d", pkt->size);
+		return;
+	}
+
+	memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
+	cmd_done.device_id = device->device_id;
+	cmd_done.session_id =
+		((struct hal_session *) pkt->session_id)->session_id;
+	cmd_done.status = vidc_map_hal_err_status((u32)pkt->error_type);
+	cmd_done.data = (void *) pkt->flush_type;
+	cmd_done.size = sizeof(u32);
+	device->callback(SESSION_FLUSH_DONE, &cmd_done);
+}
+
+static void hal_process_session_etb_done(struct hal_device *device,
+	struct hfi_msg_session_empty_buffer_done_packet *pkt)
+{
+	struct msm_vidc_cb_data_done data_done;
+
+	HAL_MSG_LOW("RECEIVED:SESSION_ETB_DONE");
+
+	if (!pkt || pkt->size !=
+		sizeof(struct hfi_msg_session_empty_buffer_done_packet)) {
+		HAL_MSG_ERROR("hal_process_session_etb_done:bad_pkt_size");
+		return;
+	}
+
+	memset(&data_done, 0, sizeof(struct msm_vidc_cb_data_done));
+
+	data_done.device_id = device->device_id;
+	data_done.session_id =
+		((struct hal_session *) pkt->session_id)->session_id;
+	data_done.status = vidc_map_hal_err_status((u32) pkt->error_type);
+	data_done.size = sizeof(struct msm_vidc_cb_data_done);
+	data_done.clnt_data = (void *)pkt->input_tag;
+	data_done.input_done.offset = pkt->offset;
+	data_done.input_done.filled_len = pkt->filled_len;
+	data_done.input_done.packet_buffer = pkt->packet_buffer;
+	device->callback(SESSION_ETB_DONE, &data_done);
+}
+
+static void hal_process_session_ftb_done(struct hal_device *device,
+			void *msg_hdr)
+{
+	struct msm_vidc_cb_data_done data_done;
+	struct hfi_msg_session_fill_buffer_done_compressed_packet *pack =
+	(struct hfi_msg_session_fill_buffer_done_compressed_packet *) msg_hdr;
+	u32 is_decoder = ((struct hal_session *)pack->session_id)->is_decoder;
+	struct hal_session *session;
+
+	if (!msg_hdr) {
+		HAL_MSG_ERROR("Invalid Params in %s", __func__);
+		return;
+	}
+
+	session = (struct hal_session *)
+		((struct hal_session *)	pack->session_id)->session_id;
+	HAL_MSG_ERROR("RECEIVED:SESSION_FTB_DONE");
+
+	memset(&data_done, 0, sizeof(struct msm_vidc_cb_data_done));
+
+	if (is_decoder == 0) {
+		struct hfi_msg_session_fill_buffer_done_compressed_packet *pkt =
+		(struct hfi_msg_session_fill_buffer_done_compressed_packet *)
+		msg_hdr;
+		if (sizeof(struct
+			hfi_msg_session_fill_buffer_done_compressed_packet)
+			!= pkt->size) {
+			HAL_MSG_ERROR("hal_process_session_ftb_done:"
+						"bad_pkt_size");
+			return;
+		}
+
+		data_done.device_id = device->device_id;
+		data_done.session_id = (u32) session;
+		data_done.status = vidc_map_hal_err_status((u32)
+							pkt->error_type);
+		data_done.size = sizeof(struct msm_vidc_cb_data_done);
+		data_done.clnt_data = (void *) pkt->input_tag;
+
+		data_done.output_done.timestamp_hi = pkt->timestamp_hi;
+		data_done.output_done.timestamp_lo = pkt->timestamp_lo;
+		data_done.output_done.flags1 = pkt->flags;
+		data_done.output_done.mark_target = pkt->mark_target;
+		data_done.output_done.mark_data = pkt->mark_data;
+		data_done.output_done.stats = pkt->stats;
+		data_done.output_done.offset1 = pkt->offset;
+		data_done.output_done.alloc_len1 = pkt->alloc_len;
+		data_done.output_done.filled_len1 = pkt->filled_len;
+		data_done.output_done.picture_type = pkt->picture_type;
+		data_done.output_done.packet_buffer1 = pkt->packet_buffer;
+		data_done.output_done.extra_data_buffer =
+			pkt->extra_data_buffer;
+	} else if (is_decoder == 1) {
+		struct hfi_msg_session_fbd_uncompressed_plane0_packet *pkt =
+		(struct	hfi_msg_session_fbd_uncompressed_plane0_packet *)
+		msg_hdr;
+		if (sizeof(struct
+		hfi_msg_session_fbd_uncompressed_plane0_packet)
+		> pkt->size) {
+			HAL_MSG_ERROR("hal_process_session_ftb_done:"
+						"bad_pkt_size");
+			return;
+		}
+
+		data_done.device_id = device->device_id;
+		data_done.session_id = (u32) session;
+		data_done.status = vidc_map_hal_err_status((u32)
+			pkt->error_type);
+		data_done.size = sizeof(struct msm_vidc_cb_data_done);
+		data_done.clnt_data = (void *)pkt->input_tag;
+
+		data_done.output_done.stream_id = pkt->stream_id;
+		data_done.output_done.view_id = pkt->view_id;
+		data_done.output_done.timestamp_hi = pkt->timestamp_hi;
+		data_done.output_done.timestamp_lo = pkt->timestamp_lo;
+		data_done.output_done.flags1 = pkt->flags;
+		data_done.output_done.mark_target = pkt->mark_target;
+		data_done.output_done.mark_data = pkt->mark_data;
+		data_done.output_done.stats = pkt->stats;
+		data_done.output_done.alloc_len1 = pkt->alloc_len;
+		data_done.output_done.filled_len1 = pkt->filled_len;
+		data_done.output_done.offset1 = pkt->oofset;
+		data_done.output_done.frame_width = pkt->frame_width;
+		data_done.output_done.frame_height = pkt->frame_height;
+		data_done.output_done.start_xCoord = pkt->start_xCoord;
+		data_done.output_done.start_yCoord = pkt->start_yCoord;
+		data_done.output_done.input_tag1 = pkt->input_tag1;
+		data_done.output_done.picture_type = pkt->picture_type;
+		data_done.output_done.packet_buffer1 = pkt->packet_buffer;
+		data_done.output_done.extra_data_buffer =
+			pkt->extra_data_buffer;
+
+		if (pkt->stream_id == 0)
+			data_done.output_done.buffer_type = HAL_BUFFER_OUTPUT;
+		else if (pkt->stream_id == 1)
+			data_done.output_done.buffer_type = HAL_BUFFER_OUTPUT2;
+		}
+	device->callback(SESSION_FTB_DONE, &data_done);
+}
+
+static void hal_process_session_start_done(struct hal_device *device,
+	struct hfi_msg_session_start_done_packet *pkt)
+{
+	struct msm_vidc_cb_cmd_done cmd_done;
+
+	HAL_MSG_LOW("RECEIVED:SESSION_START_DONE");
+
+	if (!pkt || pkt->size !=
+		sizeof(struct hfi_msg_session_start_done_packet)) {
+		HAL_MSG_ERROR("hal_process_session_start_done:"
+		"bad packet/packet size: %d", pkt->size);
+		return;
+	}
+
+	memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
+	cmd_done.device_id = device->device_id;
+	cmd_done.session_id =
+		((struct hal_session *) pkt->session_id)->session_id;
+	cmd_done.status = vidc_map_hal_err_status((u32)pkt->error_type);
+	cmd_done.data = NULL;
+	cmd_done.size = 0;
+	device->callback(SESSION_START_DONE, &cmd_done);
+}
+
+static void hal_process_session_stop_done(struct hal_device *device,
+	struct hfi_msg_session_stop_done_packet *pkt)
+{
+	struct msm_vidc_cb_cmd_done cmd_done;
+
+	HAL_MSG_LOW("RECEIVED:SESSION_STOP_DONE");
+
+	if (!pkt || pkt->size !=
+		sizeof(struct hfi_msg_session_stop_done_packet)) {
+		HAL_MSG_ERROR("hal_process_session_stop_done:"
+		"bad packet/packet size: %d", pkt->size);
+		return;
+	}
+
+	memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
+	cmd_done.device_id = device->device_id;
+	cmd_done.session_id =
+		((struct hal_session *) pkt->session_id)->session_id;
+	cmd_done.status = vidc_map_hal_err_status((u32)pkt->error_type);
+	cmd_done.data = NULL;
+	cmd_done.size = 0;
+	device->callback(SESSION_STOP_DONE, &cmd_done);
+}
+
+static void hal_process_session_rel_res_done(struct hal_device *device,
+	struct hfi_msg_session_release_resources_done_packet *pkt)
+{
+	struct msm_vidc_cb_cmd_done cmd_done;
+
+	HAL_MSG_LOW("RECEIVED:SESSION_RELEASE_RESOURCES_DONE");
+
+	if (!pkt || pkt->size !=
+		sizeof(struct hfi_msg_session_release_resources_done_packet)) {
+		HAL_MSG_ERROR("hal_process_session_rel_res_done:"
+		"bad packet/packet size: %d", pkt->size);
+		return;
+	}
+
+	memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
+	cmd_done.device_id = device->device_id;
+	cmd_done.session_id =
+		((struct hal_session *) pkt->session_id)->session_id;
+	cmd_done.status = vidc_map_hal_err_status((u32)pkt->error_type);
+	cmd_done.data = NULL;
+	cmd_done.size = 0;
+	device->callback(SESSION_RELEASE_RESOURCE_DONE, &cmd_done);
+}
+
+static void hal_process_session_end_done(struct hal_device *device,
+	struct hfi_msg_sys_session_end_done_packet *pkt)
+{
+	struct msm_vidc_cb_cmd_done cmd_done;
+	struct list_head *curr, *next;
+	struct hal_session *sess_close;
+
+	HAL_MSG_LOW("RECEIVED:SESSION_END_DONE");
+
+	if (!pkt || pkt->size !=
+		sizeof(struct hfi_msg_sys_session_end_done_packet)) {
+		HAL_MSG_ERROR("hal_process_session_end_done: "
+		"bad packet/packet size: %d", pkt->size);
+		return;
+	}
+
+	list_for_each_safe(curr, next, &device->sess_head) {
+		sess_close = list_entry(curr, struct hal_session, list);
+		HAL_MSG_MEDIUM("deleted the session: 0x%x",
+					   sess_close->session_id);
+		list_del(&sess_close->list);
+		kfree(sess_close);
+	}
+
+	memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
+	cmd_done.device_id = device->device_id;
+	cmd_done.session_id =
+		((struct hal_session *) pkt->session_id)->session_id;
+	cmd_done.status = vidc_map_hal_err_status((u32)pkt->error_type);
+	cmd_done.data = NULL;
+	cmd_done.size = 0;
+	device->callback(SESSION_END_DONE, &cmd_done);
+}
+
+static void hal_process_msg_packet(struct hal_device *device,
+	struct vidc_hal_msg_pkt_hdr *msg_hdr)
+{
+	if (!device || !msg_hdr || msg_hdr->size <
+		VIDC_IFACEQ_MIN_PKT_SIZE) {
+		HAL_MSG_ERROR("hal_process_msg_packet:bad"
+			"packet/packet size: %d", msg_hdr->size);
+		return;
+	}
+
+	HAL_MSG_INFO("Received: 0x%x in %s", msg_hdr->packet, __func__);
+
+	switch (msg_hdr->packet) {
+	case HFI_MSG_EVENT_NOTIFY:
+		hal_process_event_notify(device,
+			(struct hfi_msg_event_notify_packet *) msg_hdr);
+		break;
+	case  HFI_MSG_SYS_INIT_DONE:
+		hal_process_sys_init_done(device,
+			(struct hfi_msg_sys_init_done_packet *)
+					msg_hdr);
+		break;
+	case HFI_MSG_SYS_SESSION_INIT_DONE:
+		hal_process_session_init_done(device,
+			(struct hfi_msg_sys_session_init_done_packet *)
+					msg_hdr);
+		break;
+	case HFI_MSG_SYS_SESSION_END_DONE:
+		hal_process_session_end_done(device,
+			(struct hfi_msg_sys_session_end_done_packet *)
+					msg_hdr);
+		break;
+	case HFI_MSG_SESSION_LOAD_RESOURCES_DONE:
+		hal_process_session_load_res_done(device,
+			(struct hfi_msg_session_load_resources_done_packet *)
+					msg_hdr);
+		break;
+	case HFI_MSG_SESSION_START_DONE:
+		hal_process_session_start_done(device,
+			(struct hfi_msg_session_start_done_packet *)
+					msg_hdr);
+		break;
+	case HFI_MSG_SESSION_STOP_DONE:
+		hal_process_session_stop_done(device,
+			(struct hfi_msg_session_stop_done_packet *)
+					msg_hdr);
+		break;
+	case HFI_MSG_SESSION_EMPTY_BUFFER_DONE:
+		hal_process_session_etb_done(device,
+			(struct hfi_msg_session_empty_buffer_done_packet *)
+					msg_hdr);
+		break;
+	case HFI_MSG_SESSION_FILL_BUFFER_DONE:
+		hal_process_session_ftb_done(device, msg_hdr);
+		break;
+	case HFI_MSG_SESSION_FLUSH_DONE:
+		hal_process_session_flush_done(device,
+			(struct hfi_msg_session_flush_done_packet *)
+					msg_hdr);
+		break;
+	case HFI_MSG_SESSION_PROPERTY_INFO:
+		hal_process_session_prop_info(device,
+			(struct hfi_msg_session_property_info_packet *)
+					msg_hdr);
+		break;
+	case HFI_MSG_SESSION_RELEASE_RESOURCES_DONE:
+		hal_process_session_rel_res_done(device,
+			(struct hfi_msg_session_release_resources_done_packet *)
+					msg_hdr);
+		break;
+	default:
+		HAL_MSG_ERROR("UNKNOWN_MSG_TYPE : %d", msg_hdr->packet);
+		break;
+	}
+}
+
+void vidc_hal_response_handler(struct hal_device *device)
+{
+	u8 packet[VIDC_IFACEQ_MED_PKT_SIZE];
+
+	HAL_MSG_INFO("############vidc_hal_response_handler\n");
+	if (device) {
+		while (!vidc_hal_iface_msgq_read(device, packet)) {
+			hal_process_msg_packet(device,
+				(struct vidc_hal_msg_pkt_hdr *)	packet);
+		}
+	} else {
+		HAL_MSG_ERROR("SPURIOUS_INTERRUPT");
+	}
+}
diff --git a/drivers/media/video/msm_vidc/vidc_hal_io.h b/drivers/media/video/msm_vidc/vidc_hal_io.h
new file mode 100644
index 0000000..05a4c60
--- /dev/null
+++ b/drivers/media/video/msm_vidc/vidc_hal_io.h
@@ -0,0 +1,103 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __VIDCHALIO_H__
+#define __VIDCHALIO_H__
+
+#include <linux/io.h>
+
+#define VIDC_VBIF_BASE_OFFS			0x00080000
+#define VIDC_VBIF_VERSION			(VIDC_VBIF_BASE_OFFS + 0x00)
+#define VIDC_VBIF_ADDR_TRANS_EN		(VIDC_VBIF_BASE_OFFS + 0x10)
+#define VIDC_VBIF_AT_OLD_BASE		(VIDC_VBIF_BASE_OFFS + 0x14)
+#define VIDC_VBIF_AT_OLD_HIGH		(VIDC_VBIF_BASE_OFFS + 0x18)
+#define VIDC_VBIF_AT_NEW_BASE		(VIDC_VBIF_BASE_OFFS + 0x20)
+#define VIDC_VBIF_AT_NEW_HIGH		(VIDC_VBIF_BASE_OFFS + 0x28)
+
+#define VIDC_CPU_BASE_OFFS			0x000C0000
+#define VIDC_CPU_CS_BASE_OFFS		(VIDC_CPU_BASE_OFFS + 0x00012000)
+#define VIDC_CPU_IC_BASE_OFFS		(VIDC_CPU_BASE_OFFS + 0x0001F000)
+
+#define VIDC_CPU_CS_REMAP_OFFS		(VIDC_CPU_CS_BASE_OFFS + 0x00)
+#define VIDC_CPU_CS_TIMER_CONTROL	(VIDC_CPU_CS_BASE_OFFS + 0x04)
+#define VIDC_CPU_CS_A2HSOFTINTEN	(VIDC_CPU_CS_BASE_OFFS + 0x10)
+#define VIDC_CPU_CS_A2HSOFTINTENCLR	(VIDC_CPU_CS_BASE_OFFS + 0x14)
+#define VIDC_CPU_CS_A2HSOFTINT		(VIDC_CPU_CS_BASE_OFFS + 0x18)
+#define VIDC_CPU_CS_A2HSOFTINTCLR	(VIDC_CPU_CS_BASE_OFFS + 0x1C)
+#define VIDC_CPU_CS_SCIACMD			(VIDC_CPU_CS_BASE_OFFS + 0x48)
+
+/* HFI_CTRL_STATUS */
+#define VIDC_CPU_CS_SCIACMDARG0		(VIDC_CPU_CS_BASE_OFFS + 0x4C)
+#define VIDC_CPU_CS_SCIACMDARG0_BMSK	0xff
+#define VIDC_CPU_CS_SCIACMDARG0_SHFT	0x0
+#define VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_ERROR_STATUS_BMSK	0xfe
+#define VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_ERROR_STATUS_SHFT	0x1
+#define VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_INIT_STATUS_BMSK	0x1
+#define VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_INIT_STATUS_SHFT	0x0
+
+/* HFI_QTBL_INFO */
+#define VIDC_CPU_CS_SCIACMDARG1		(VIDC_CPU_CS_BASE_OFFS + 0x50)
+
+/* HFI_QTBL_ADDR */
+#define VIDC_CPU_CS_SCIACMDARG2		(VIDC_CPU_CS_BASE_OFFS + 0x54)
+
+/* HFI_VERSION_INFO */
+#define VIDC_CPU_CS_SCIACMDARG3		(VIDC_CPU_CS_BASE_OFFS + 0x58)
+#define VIDC_CPU_IC_IRQSTATUS		(VIDC_CPU_IC_BASE_OFFS + 0x00)
+#define VIDC_CPU_IC_FIQSTATUS		(VIDC_CPU_IC_BASE_OFFS + 0x04)
+#define VIDC_CPU_IC_RAWINTR			(VIDC_CPU_IC_BASE_OFFS + 0x08)
+#define VIDC_CPU_IC_INTSELECT		(VIDC_CPU_IC_BASE_OFFS + 0x0C)
+#define VIDC_CPU_IC_INTENABLE		(VIDC_CPU_IC_BASE_OFFS + 0x10)
+#define VIDC_CPU_IC_INTENACLEAR		(VIDC_CPU_IC_BASE_OFFS + 0x14)
+#define VIDC_CPU_IC_SOFTINT			(VIDC_CPU_IC_BASE_OFFS + 0x18)
+#define VIDC_CPU_IC_SOFTINT_H2A_BMSK	0x8000
+#define VIDC_CPU_IC_SOFTINT_H2A_SHFT	0xF
+#define VIDC_CPU_IC_SOFTINTCLEAR	(VIDC_CPU_IC_BASE_OFFS + 0x1C)
+
+/*---------------------------------------------------------------------------
+ * MODULE: vidc_wrapper
+ *--------------------------------------------------------------------------*/
+#define VIDC_WRAPPER_BASE_OFFS		0x000E0000
+
+#define VIDC_WRAPPER_HW_VERSION		(VIDC_WRAPPER_BASE_OFFS + 0x00)
+#define VIDC_WRAPPER_CLOCK_CONFIG	(VIDC_WRAPPER_BASE_OFFS + 0x04)
+
+#define VIDC_WRAPPER_INTR_STATUS	(VIDC_WRAPPER_BASE_OFFS + 0x0C)
+#define VIDC_WRAPPER_INTR_STATUS_A2HWD_BMSK	0x10
+#define VIDC_WRAPPER_INTR_STATUS_A2HWD_SHFT	0x4
+#define VIDC_WRAPPER_INTR_STATUS_A2H_BMSK	0x4
+#define VIDC_WRAPPER_INTR_STATUS_A2H_SHFT	0x2
+
+#define VIDC_WRAPPER_INTR_MASK		(VIDC_WRAPPER_BASE_OFFS + 0x10)
+#define VIDC_WRAPPER_INTR_MASK_A2HWD_BMSK	0x10
+#define VIDC_WRAPPER_INTR_MASK_A2HWD_SHFT	0x4
+#define VIDC_WRAPPER_INTR_MASK_A2H_BMSK		0x4
+#define VIDC_WRAPPER_INTR_MASK_A2H_SHFT		0x2
+
+#define VIDC_WRAPPER_INTR_CLEAR		(VIDC_WRAPPER_BASE_OFFS + 0x14)
+#define VIDC_WRAPPER_INTR_CLEAR_A2HWD_BMSK	0x10
+#define VIDC_WRAPPER_INTR_CLEAR_A2HWD_SHFT	0x4
+#define VIDC_WRAPPER_INTR_CLEAR_A2H_BMSK	0x4
+#define VIDC_WRAPPER_INTR_CLEAR_A2H_SHFT	0x2
+
+#define VIDC_WRAPPER_VBIF_XIN_SW_RESET	(VIDC_WRAPPER_BASE_OFFS + 0x18)
+#define VIDC_WRAPPER_VBIF_XIN_STATUS	(VIDC_WRAPPER_BASE_OFFS + 0x1C)
+#define VIDC_WRAPPER_CPU_CLOCK_CONFIG	(VIDC_WRAPPER_BASE_OFFS + 0x2000)
+#define VIDC_WRAPPER_VBIF_XIN_CPU_SW_RESET	\
+				(VIDC_WRAPPER_BASE_OFFS + 0x2004)
+#define VIDC_WRAPPER_AXI_HALT		(VIDC_WRAPPER_BASE_OFFS + 0x2008)
+#define VIDC_WRAPPER_AXI_HALT_STATUS	(VIDC_WRAPPER_BASE_OFFS + 0x200C)
+#define VIDC_WRAPPER_CPU_CGC_DIS	(VIDC_WRAPPER_BASE_OFFS + 0x2010)
+
+#endif
+
diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c
index ff2cddd..b2ef948 100644
--- a/drivers/media/video/uvc/uvc_v4l2.c
+++ b/drivers/media/video/uvc/uvc_v4l2.c
@@ -68,6 +68,15 @@
 			goto done;
 		}
 
+		/* Prevent excessive memory consumption, as well as integer
+		 * overflows.
+		 */
+		if (xmap->menu_count == 0 ||
+		    xmap->menu_count > UVC_MAX_CONTROL_MENU_ENTRIES) {
+			ret = -EINVAL;
+			goto done;
+		}
+
 		size = xmap->menu_count * sizeof(*map->menu_info);
 		map->menu_info = kmalloc(size, GFP_KERNEL);
 		if (map->menu_info == NULL) {
diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c
index 18015c0..7da4657 100644
--- a/drivers/media/video/v4l2-ctrls.c
+++ b/drivers/media/video/v4l2-ctrls.c
@@ -371,6 +371,13 @@
 		"Gray",
 		NULL,
 	};
+	static const char *const mpeg_video_intra_refresh_mode[] = {
+		"No Intra Refresh",
+		"AIR MBS",
+		"AIR REF",
+		"CIR MBS",
+		NULL
+	};
 
 	switch (id) {
 	case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
@@ -438,7 +445,8 @@
 		return mpeg4_profile;
 	case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:
 		return jpeg_chroma_subsampling;
-
+	case V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE:
+		return mpeg_video_intra_refresh_mode;
 	default:
 		return NULL;
 	}
@@ -575,6 +583,14 @@
 	case V4L2_CID_MPEG_VIDEO_VBV_SIZE:			return "VBV Buffer Size";
 	case V4L2_CID_MPEG_VIDEO_DEC_PTS:			return "Video Decoder PTS";
 	case V4L2_CID_MPEG_VIDEO_DEC_FRAME:			return "Video Decoder Frame Count";
+	case V4L2_CID_MPEG_VIDC_VIDEO_ROTATION: return "Rotation";
+	case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL: return "Rate Control";
+	case V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL: return "CABAC Model";
+	case V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE:
+		return "Intra Refresh Mode";
+	case V4L2_CID_MPEG_VIDC_VIDEO_AIR_MBS: return "Intra Refresh AIR MBS";
+	case V4L2_CID_MPEG_VIDC_VIDEO_AIR_REF: return "Intra Refresh AIR REF";
+	case V4L2_CID_MPEG_VIDC_VIDEO_CIR_MBS: return "Intra Refresh CIR MBS";
 
 	/* CAMERA controls */
 	/* Keep the order of the 'case's the same as in videodev2.h! */
@@ -733,6 +749,9 @@
 	case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
 	case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
 	case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:
+	case V4L2_CID_MPEG_VIDC_VIDEO_ROTATION:
+	case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL:
+	case V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL:
 		*type = V4L2_CTRL_TYPE_MENU;
 		break;
 	case V4L2_CID_RDS_TX_PS_NAME:
@@ -751,7 +770,12 @@
 		*min = *max = *step = *def = 0;
 		break;
 	case V4L2_CID_BG_COLOR:
-		*type = V4L2_CTRL_TYPE_INTEGER;
+	case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES:
+	case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB:
+	*type = V4L2_CTRL_TYPE_INTEGER;
 		*step = 1;
 		*min = 0;
 		/* Max is calculated as RGB888 that is 2^24 */
diff --git a/drivers/media/video/vcap_v4l2.c b/drivers/media/video/vcap_v4l2.c
new file mode 100644
index 0000000..9719307
--- /dev/null
+++ b/drivers/media/video/vcap_v4l2.c
@@ -0,0 +1,1569 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <linux/platform_device.h>
+#include <linux/memory_alloc.h>
+
+#include <mach/board.h>
+#include <mach/gpio.h>
+#include <mach/irqs.h>
+
+#include <media/videobuf2-msm-mem.h>
+
+#include <media/videobuf2-vmalloc.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-common.h>
+#include <linux/regulator/consumer.h>
+#include <mach/clk.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
+#include <mach/iommu_domains.h>
+
+#include <media/vcap_v4l2.h>
+#include <media/vcap_fmt.h>
+#include "vcap_vc.h"
+#include "vcap_vp.h"
+
+#define NUM_INPUTS 1
+#define MSM_VCAP_DRV_NAME "msm_vcap"
+
+static struct vcap_dev *vcap_ctrl;
+
+static unsigned debug;
+
+#define dprintk(level, fmt, arg...)					\
+	do {								\
+		if (debug >= level)					\
+			printk(KERN_DEBUG "VCAP: " fmt, ## arg);	\
+	} while (0)
+
+enum vcap_op_mode determine_mode(struct vcap_client_data *cd)
+{
+	if (cd->set_cap == 1 && cd->set_vp_o == 0 &&
+			cd->set_decode == 0)
+		return VC_VCAP_OP;
+	else if (cd->set_cap == 1 && cd->set_vp_o == 1 &&
+			cd->set_decode == 0)
+		return VC_AND_VP_VCAP_OP;
+	else if (cd->set_cap == 0 && cd->set_vp_o == 1 &&
+			cd->set_decode == 1)
+		return VP_VCAP_OP;
+	else
+		return UNKNOWN_VCAP_OP;
+}
+
+void dealloc_resources(struct vcap_client_data *cd)
+{
+	cd->set_cap = false;
+	cd->set_decode = false;
+	cd->set_vp_o = false;
+}
+
+int get_phys_addr(struct vcap_dev *dev, struct vb2_queue *q,
+				  struct v4l2_buffer *b)
+{
+	struct vb2_buffer *vb;
+	struct vcap_buffer *buf;
+	unsigned long len, offset;
+	int rc;
+
+	if (q->fileio) {
+		dprintk(1, "%s: file io in progress\n", __func__);
+		return -EBUSY;
+	}
+
+	if (b->type != q->type) {
+		dprintk(1, "%s: invalid buffer type\n", __func__);
+		return -EINVAL;
+	}
+
+	if (b->index >= q->num_buffers) {
+		dprintk(1, "%s: buffer index out of range\n", __func__);
+		return -EINVAL;
+	}
+
+	vb = q->bufs[b->index];
+	if (NULL == vb) {
+		dprintk(1, "%s: buffer is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	if (vb->state != VB2_BUF_STATE_DEQUEUED) {
+		dprintk(1, "%s: buffer already in use\n", __func__);
+		return -EINVAL;
+	}
+
+	buf = container_of(vb, struct vcap_buffer, vb);
+
+	buf->ion_handle = ion_import_fd(dev->ion_client, b->m.userptr);
+	if (IS_ERR((void *)buf->ion_handle)) {
+		pr_err("%s: Could not alloc memory\n", __func__);
+		buf->ion_handle = NULL;
+		return -ENOMEM;
+	}
+	rc = ion_phys(dev->ion_client, buf->ion_handle,
+			&buf->paddr, (size_t *)&len);
+	if (rc < 0) {
+		pr_err("%s: Could not get phys addr\n", __func__);
+		ion_free(dev->ion_client, buf->ion_handle);
+		buf->ion_handle = NULL;
+		return -EFAULT;
+	}
+
+	offset = b->reserved;
+	buf->paddr += offset;
+	return 0;
+}
+
+void free_ion_handle_work(struct vcap_dev *dev, struct vb2_buffer *vb)
+{
+	struct vcap_buffer *buf;
+
+	buf = container_of(vb, struct vcap_buffer, vb);
+	if (buf->ion_handle == NULL) {
+		dprintk(1, "%s: no ION handle to free\n", __func__);
+		return;
+	}
+	buf->paddr = 0;
+	ion_free(dev->ion_client, buf->ion_handle);
+	buf->ion_handle = NULL;
+	return;
+}
+
+int free_ion_handle(struct vcap_dev *dev, struct vb2_queue *q,
+					 struct v4l2_buffer *b)
+{
+	struct vb2_buffer *vb;
+
+	if (q->fileio)
+		return -EBUSY;
+
+	if (b->type != q->type)
+		return -EINVAL;
+
+	if (b->index >= q->num_buffers)
+		return -EINVAL;
+
+	vb = q->bufs[b->index];
+	if (NULL == vb)
+		return -EINVAL;
+
+	free_ion_handle_work(dev, vb);
+	return 0;
+}
+
+/* VC Videobuf operations */
+
+static int capture_queue_setup(struct vb2_queue *vq,
+			       const struct v4l2_format *fmt,
+			       unsigned int *nbuffers,
+			       unsigned int *nplanes, unsigned int sizes[],
+			       void *alloc_ctxs[])
+{
+	*nbuffers += 2;
+	if (*nbuffers > VIDEO_MAX_FRAME)
+		return -EINVAL;
+	*nplanes = 1;
+	return 0;
+}
+
+static int capture_buffer_init(struct vb2_buffer *vb)
+{
+	return 0;
+}
+
+static int capture_buffer_prepare(struct vb2_buffer *vb)
+{
+	return 0;
+}
+
+static void capture_buffer_queue(struct vb2_buffer *vb)
+{
+	struct vcap_client_data *c_data = vb2_get_drv_priv(vb->vb2_queue);
+	struct vcap_buffer *buf = container_of(vb, struct vcap_buffer, vb);
+	struct vcap_action *vid_vc_action = &c_data->vid_vc_action;
+	struct vb2_queue *q = vb->vb2_queue;
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&c_data->cap_slock, flags);
+	list_add_tail(&buf->list, &vid_vc_action->active);
+	spin_unlock_irqrestore(&c_data->cap_slock, flags);
+
+	if (atomic_read(&c_data->dev->vc_enabled) == 0) {
+
+		if (atomic_read(&q->queued_count) > 1)
+			if (vc_hw_kick_off(c_data) == 0)
+				atomic_set(&c_data->dev->vc_enabled, 1);
+	}
+}
+
+static int capture_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+	struct vcap_client_data *c_data = vb2_get_drv_priv(vq);
+	dprintk(2, "VC start streaming\n");
+	return vc_start_capture(c_data);
+}
+
+static int capture_stop_streaming(struct vb2_queue *vq)
+{
+	struct vcap_client_data *c_data = vb2_get_drv_priv(vq);
+	struct vb2_buffer *vb;
+
+	vc_stop_capture(c_data);
+
+	while (!list_empty(&c_data->vid_vc_action.active)) {
+		struct vcap_buffer *buf;
+		buf = list_entry(c_data->vid_vc_action.active.next,
+			struct vcap_buffer, list);
+		list_del(&buf->list);
+		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+	}
+
+	/* clean ion handles */
+	list_for_each_entry(vb, &vq->queued_list, queued_entry)
+		free_ion_handle_work(c_data->dev, vb);
+	return 0;
+}
+
+static int capture_buffer_finish(struct vb2_buffer *vb)
+{
+	return 0;
+}
+
+static void capture_buffer_cleanup(struct vb2_buffer *vb)
+{
+}
+
+static struct vb2_ops capture_video_qops = {
+	.queue_setup		= capture_queue_setup,
+	.buf_init			= capture_buffer_init,
+	.buf_prepare		= capture_buffer_prepare,
+	.buf_queue			= capture_buffer_queue,
+	.start_streaming	= capture_start_streaming,
+	.stop_streaming		= capture_stop_streaming,
+	.buf_finish			= capture_buffer_finish,
+	.buf_cleanup		= capture_buffer_cleanup,
+};
+
+/* VP I/P Videobuf operations */
+
+static int vp_in_queue_setup(struct vb2_queue *vq,
+			     const struct v4l2_format *fmt,
+			     unsigned int *nbuffers,
+			     unsigned int *nplanes, unsigned int sizes[],
+			     void *alloc_ctxs[])
+{
+	if (*nbuffers >= VIDEO_MAX_FRAME && *nbuffers < 5)
+		*nbuffers = 5;
+
+	*nplanes = 1;
+	return 0;
+}
+
+static int vp_in_buffer_init(struct vb2_buffer *vb)
+{
+	return 0;
+}
+
+static int vp_in_buffer_prepare(struct vb2_buffer *vb)
+{
+	return 0;
+}
+
+static void vp_in_buffer_queue(struct vb2_buffer *vb)
+{
+	struct vcap_client_data *cd = vb2_get_drv_priv(vb->vb2_queue);
+	struct vcap_buffer *buf = container_of(vb, struct vcap_buffer, vb);
+	struct vp_action *vp_act = &cd->vid_vp_action;
+	struct vb2_queue *q = vb->vb2_queue;
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&cd->cap_slock, flags);
+	list_add_tail(&buf->list, &vp_act->in_active);
+	spin_unlock_irqrestore(&cd->cap_slock, flags);
+
+	if (atomic_read(&cd->dev->vp_enabled) == 0) {
+		if (cd->vid_vp_action.vp_state == VP_FRAME1) {
+			if (atomic_read(&q->queued_count) > 1 &&
+				atomic_read(&cd->vp_out_vidq.queued_count) > 0)
+				/* Valid code flow for VC-VP mode */
+				kickoff_vp(cd);
+		} else {
+			/* VP has already kicked off just needs cont */
+			continue_vp(cd);
+		}
+	}
+}
+
+static int vp_in_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+	dprintk(2, "VP IN start streaming\n");
+	return 0;
+}
+
+static int vp_in_stop_streaming(struct vb2_queue *vq)
+{
+	struct vcap_client_data *c_data = vb2_get_drv_priv(vq);
+	struct vb2_buffer *vb;
+
+	dprintk(2, "VP stop streaming\n");
+
+	while (!list_empty(&c_data->vid_vp_action.in_active)) {
+		struct vcap_buffer *buf;
+		buf = list_entry(c_data->vid_vp_action.in_active.next,
+			struct vcap_buffer, list);
+		list_del(&buf->list);
+		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+	}
+
+	/* clean ion handles */
+	list_for_each_entry(vb, &vq->queued_list, queued_entry)
+		free_ion_handle_work(c_data->dev, vb);
+	return 0;
+}
+
+static int vp_in_buffer_finish(struct vb2_buffer *vb)
+{
+	return 0;
+}
+
+static void vp_in_buffer_cleanup(struct vb2_buffer *vb)
+{
+}
+
+static struct vb2_ops vp_in_video_qops = {
+	.queue_setup		= vp_in_queue_setup,
+	.buf_init			= vp_in_buffer_init,
+	.buf_prepare		= vp_in_buffer_prepare,
+	.buf_queue			= vp_in_buffer_queue,
+	.start_streaming	= vp_in_start_streaming,
+	.stop_streaming		= vp_in_stop_streaming,
+	.buf_finish			= vp_in_buffer_finish,
+	.buf_cleanup		= vp_in_buffer_cleanup,
+};
+
+
+/* VP O/P Videobuf operations */
+
+static int vp_out_queue_setup(struct vb2_queue *vq,
+			      const struct v4l2_format *fmt,
+			      unsigned int *nbuffers,
+			      unsigned int *nplanes, unsigned int sizes[],
+			      void *alloc_ctxs[])
+{
+	if (*nbuffers >= VIDEO_MAX_FRAME && *nbuffers < 3)
+		*nbuffers = 3;
+
+	*nplanes = 1;
+	return 0;
+}
+
+static int vp_out_buffer_init(struct vb2_buffer *vb)
+{
+	return 0;
+}
+
+static int vp_out_buffer_prepare(struct vb2_buffer *vb)
+{
+	return 0;
+}
+
+static void vp_out_buffer_queue(struct vb2_buffer *vb)
+{
+	struct vcap_client_data *cd = vb2_get_drv_priv(vb->vb2_queue);
+	struct vcap_buffer *buf = container_of(vb, struct vcap_buffer, vb);
+	struct vp_action *vp_act = &cd->vid_vp_action;
+	struct vb2_queue *q = vb->vb2_queue;
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&cd->cap_slock, flags);
+	list_add_tail(&buf->list, &vp_act->out_active);
+	spin_unlock_irqrestore(&cd->cap_slock, flags);
+
+	if (atomic_read(&cd->dev->vp_enabled) == 0) {
+		if (cd->vid_vp_action.vp_state == VP_FRAME1) {
+			if (atomic_read(&q->queued_count) > 0 &&
+				atomic_read(&
+					cd->vp_in_vidq.queued_count) > 1)
+				kickoff_vp(cd);
+		} else {
+			/* VP has already kicked off just needs cont */
+			continue_vp(cd);
+		}
+	}
+}
+
+static int vp_out_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+	return 0;
+}
+
+static int vp_out_stop_streaming(struct vb2_queue *vq)
+{
+	struct vcap_client_data *c_data = vb2_get_drv_priv(vq);
+	struct vb2_buffer *vb;
+
+	dprintk(2, "VP out q stop streaming\n");
+	vp_stop_capture(c_data);
+
+	while (!list_empty(&c_data->vid_vp_action.out_active)) {
+		struct vcap_buffer *buf;
+		buf = list_entry(c_data->vid_vp_action.out_active.next,
+			struct vcap_buffer, list);
+		list_del(&buf->list);
+		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+	}
+
+	/* clean ion handles */
+	list_for_each_entry(vb, &vq->queued_list, queued_entry)
+		free_ion_handle_work(c_data->dev, vb);
+	return 0;
+}
+
+static int vp_out_buffer_finish(struct vb2_buffer *vb)
+{
+	return 0;
+}
+
+static void vp_out_buffer_cleanup(struct vb2_buffer *vb)
+{
+}
+
+static struct vb2_ops vp_out_video_qops = {
+	.queue_setup		= vp_out_queue_setup,
+	.buf_init			= vp_out_buffer_init,
+	.buf_prepare		= vp_out_buffer_prepare,
+	.buf_queue			= vp_out_buffer_queue,
+	.start_streaming	= vp_out_start_streaming,
+	.stop_streaming		= vp_out_stop_streaming,
+	.buf_finish			= vp_out_buffer_finish,
+	.buf_cleanup		= vp_out_buffer_cleanup,
+};
+
+/* IOCTL vidioc handling */
+
+static int vidioc_querycap(struct file *file, void  *priv,
+					struct v4l2_capability *cap)
+{
+	struct vcap_dev *dev = video_drvdata(file);
+
+	strlcpy(cap->driver, MSM_VCAP_DRV_NAME, sizeof(cap->driver));
+	strlcpy(cap->card, MSM_VCAP_DRV_NAME, sizeof(cap->card));
+	strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info));
+	cap->version = 0x10000000;
+	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+	return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *priv,
+				struct v4l2_input *inp)
+{
+	if (inp->index >= NUM_INPUTS)
+		return -EINVAL;
+	return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+					struct v4l2_format *f)
+{
+	return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+			struct v4l2_format *f)
+{
+	return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+					struct v4l2_format *f)
+{
+	int size;
+	struct vcap_priv_fmt *priv_fmt;
+	struct v4l2_format_vc_ext *vc_format;
+	struct vcap_client_data *c_data = file->private_data;
+
+	priv_fmt = (struct vcap_priv_fmt *) f->fmt.raw_data;
+
+	switch (priv_fmt->type) {
+	case VC_TYPE:
+		vc_format = (struct v4l2_format_vc_ext *) &priv_fmt->u.timing;
+		c_data->vc_format = *vc_format;
+
+		config_vc_format(c_data);
+
+		size = (c_data->vc_format.hactive_end -
+			c_data->vc_format.hactive_start);
+
+		if (c_data->vc_format.color_space)
+			size *= 3;
+		else
+			size *= 2;
+
+		priv_fmt->u.timing.bytesperline = size;
+		size *= (c_data->vc_format.vactive_end -
+			c_data->vc_format.vactive_start);
+		priv_fmt->u.timing.sizeimage = size;
+		vcap_ctrl->vc_client = c_data;
+		c_data->set_cap = true;
+		break;
+	case VP_IN_TYPE:
+		vcap_ctrl->vp_client = c_data;
+		c_data->vp_in_fmt.width = priv_fmt->u.pix.width;
+		c_data->vp_in_fmt.height = priv_fmt->u.pix.height;
+		c_data->vp_in_fmt.pixfmt = priv_fmt->u.pix.pixelformat;
+
+		if (priv_fmt->u.pix.priv)
+			c_data->vid_vp_action.nr_enabled = 1;
+
+		size = c_data->vp_in_fmt.width * c_data->vp_in_fmt.height;
+		if (c_data->vp_in_fmt.pixfmt == V4L2_PIX_FMT_NV16)
+			size = size * 2;
+		else
+			size = size / 2 * 3;
+		priv_fmt->u.pix.sizeimage = size;
+		c_data->set_decode = true;
+		break;
+	case VP_OUT_TYPE:
+		vcap_ctrl->vp_client = c_data;
+		c_data->vp_out_fmt.width = priv_fmt->u.pix.width;
+		c_data->vp_out_fmt.height = priv_fmt->u.pix.height;
+		c_data->vp_out_fmt.pixfmt = priv_fmt->u.pix.pixelformat;
+
+		if (priv_fmt->u.pix.priv)
+			c_data->vid_vp_action.nr_enabled = 1;
+
+		size = c_data->vp_out_fmt.width * c_data->vp_out_fmt.height;
+		if (c_data->vp_out_fmt.pixfmt == V4L2_PIX_FMT_NV16)
+			size = size * 2;
+		else
+			size = size / 2 * 3;
+		priv_fmt->u.pix.sizeimage = size;
+		c_data->set_vp_o = true;
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+			  struct v4l2_requestbuffers *rb)
+{
+	struct vcap_client_data *c_data = file->private_data;
+	int rc;
+
+	dprintk(3, "In Req Buf %08x\n", (unsigned int)rb->type);
+	c_data->op_mode = determine_mode(c_data);
+	if (c_data->op_mode == UNKNOWN_VCAP_OP) {
+		pr_err("VCAP Error: %s: VCAP in unknown mode\n", __func__);
+		return -ENOTRECOVERABLE;
+	}
+
+	switch (rb->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		if (c_data->op_mode == VC_AND_VP_VCAP_OP) {
+			if (c_data->vc_format.color_space) {
+				pr_err("VCAP Err: %s: VP No RGB support\n",
+					__func__);
+				return -ENOTRECOVERABLE;
+			}
+			if (!c_data->vc_format.mode) {
+				pr_err("VCAP Err: VP No prog support\n");
+				return -ENOTRECOVERABLE;
+			}
+			if (rb->count < 6) {
+				pr_err("VCAP Err: Not enough buf for VC_VP\n");
+				return -EINVAL;
+			}
+			rc = vb2_reqbufs(&c_data->vc_vidq, rb);
+			if (rc < 0)
+				return rc;
+
+			c_data->vp_in_fmt.width =
+				(c_data->vc_format.hactive_end -
+				c_data->vc_format.hactive_start);
+			c_data->vp_in_fmt.height =
+				(c_data->vc_format.vactive_end -
+				c_data->vc_format.vactive_start);
+			/* VC outputs YCbCr 4:2:2 */
+			c_data->vp_in_fmt.pixfmt = V4L2_PIX_FMT_NV16;
+			rb->type = V4L2_BUF_TYPE_INTERLACED_IN_DECODER;
+			rc = vb2_reqbufs(&c_data->vp_in_vidq, rb);
+			rb->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+			return rc;
+
+		} else {
+			return vb2_reqbufs(&c_data->vc_vidq, rb);
+		}
+	case V4L2_BUF_TYPE_INTERLACED_IN_DECODER:
+		return vb2_reqbufs(&c_data->vp_in_vidq, rb);
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+		return vb2_reqbufs(&c_data->vp_out_vidq, rb);
+	default:
+		pr_err("VCAP Error: %s: Unknown buffer type\n", __func__);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+	struct vcap_client_data *c_data = file->private_data;
+
+	switch (p->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		return vb2_querybuf(&c_data->vc_vidq, p);
+	default:
+		pr_err("VCAP Error: %s: Unknown buffer type\n", __func__);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+	struct vcap_client_data *c_data = file->private_data;
+	struct vb2_buffer *vb;
+	struct vb2_queue *q;
+	int rc;
+
+	dprintk(3, "In Q Buf %08x\n", (unsigned int)p->type);
+	switch (p->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		if (c_data->op_mode == VC_AND_VP_VCAP_OP) {
+			/* If buffer in vp_in_q it will be coming back */
+			q = &c_data->vp_in_vidq;
+			if (p->index >= q->num_buffers) {
+				dprintk(1, "qbuf: buffer index out of range\n");
+				return -EINVAL;
+			}
+
+			vb = q->bufs[p->index];
+			if (NULL == vb) {
+				dprintk(1, "qbuf: buffer is NULL\n");
+				return -EINVAL;
+			}
+
+			if (vb->state != VB2_BUF_STATE_DEQUEUED) {
+				dprintk(1, "qbuf: buffer already in use\n");
+				return -EINVAL;
+			}
+		}
+		rc = get_phys_addr(c_data->dev, &c_data->vc_vidq, p);
+		if (rc < 0)
+			return rc;
+		rc = vb2_qbuf(&c_data->vc_vidq, p);
+		if (rc < 0)
+			free_ion_handle(c_data->dev, &c_data->vc_vidq, p);
+		return rc;
+	case V4L2_BUF_TYPE_INTERLACED_IN_DECODER:
+		if (c_data->op_mode == VC_AND_VP_VCAP_OP)
+			return -EINVAL;
+		rc = get_phys_addr(c_data->dev, &c_data->vp_in_vidq, p);
+		if (rc < 0)
+			return rc;
+		rc = vb2_qbuf(&c_data->vp_in_vidq, p);
+		if (rc < 0)
+			free_ion_handle(c_data->dev, &c_data->vp_in_vidq, p);
+		return rc;
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+		rc = get_phys_addr(c_data->dev, &c_data->vp_out_vidq, p);
+		if (rc < 0)
+			return rc;
+		rc = vb2_qbuf(&c_data->vp_out_vidq, p);
+		if (rc < 0)
+			free_ion_handle(c_data->dev, &c_data->vp_out_vidq, p);
+		return rc;
+	default:
+		pr_err("VCAP Error: %s: Unknown buffer type\n", __func__);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+	struct vcap_client_data *c_data = file->private_data;
+	int rc;
+
+	dprintk(3, "In DQ Buf %08x\n", (unsigned int)p->type);
+	switch (p->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		if (c_data->op_mode == VC_AND_VP_VCAP_OP)
+			return -EINVAL;
+		rc = vb2_dqbuf(&c_data->vc_vidq, p, file->f_flags & O_NONBLOCK);
+		if (rc < 0)
+			return rc;
+		return free_ion_handle(c_data->dev, &c_data->vc_vidq, p);
+	case V4L2_BUF_TYPE_INTERLACED_IN_DECODER:
+		if (c_data->op_mode == VC_AND_VP_VCAP_OP)
+			return -EINVAL;
+		rc = vb2_dqbuf(&c_data->vp_in_vidq, p, file->f_flags &
+				O_NONBLOCK);
+		if (rc < 0)
+			return rc;
+		return free_ion_handle(c_data->dev, &c_data->vp_in_vidq, p);
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+		rc = vb2_dqbuf(&c_data->vp_out_vidq, p, file->f_flags &
+				O_NONBLOCK);
+		if (rc < 0)
+			return rc;
+		return free_ion_handle(c_data->dev, &c_data->vp_out_vidq, p);
+	default:
+		pr_err("VCAP Error: %s: Unknown buffer type", __func__);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/*
+ * When calling streamon on multiple queues there is a need to first verify
+ * that the steamon will succeed on all queues, similarly for streamoff
+ */
+int streamon_validate_q(struct vb2_queue *q)
+{
+	if (q->fileio) {
+		dprintk(1, "streamon: file io in progress\n");
+		return -EBUSY;
+	}
+
+	if (q->streaming) {
+		dprintk(1, "streamon: already streaming\n");
+		return -EBUSY;
+	}
+
+	if (V4L2_TYPE_IS_OUTPUT(q->type)) {
+		if (list_empty(&q->queued_list)) {
+			dprintk(1, "streamon: no output buffers queued\n");
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+	struct vcap_client_data *c_data = file->private_data;
+	int rc;
+
+	dprintk(3, "In Stream ON\n");
+	if (determine_mode(c_data) != c_data->op_mode) {
+		pr_err("VCAP Error: %s: s_fmt called after req_buf", __func__);
+		return -ENOTRECOVERABLE;
+	}
+
+	switch (c_data->op_mode) {
+	case VC_VCAP_OP:
+		c_data->dev->vc_client = c_data;
+		config_vc_format(c_data);
+		return vb2_streamon(&c_data->vc_vidq, i);
+	case VP_VCAP_OP:
+		rc = streamon_validate_q(&c_data->vp_in_vidq);
+		if (rc < 0)
+			return rc;
+		rc = streamon_validate_q(&c_data->vp_out_vidq);
+		if (rc < 0)
+			return rc;
+
+		c_data->dev->vp_client = c_data;
+
+		rc = config_vp_format(c_data);
+		if (rc < 0)
+			return rc;
+		rc = init_motion_buf(c_data);
+		if (rc < 0)
+			return rc;
+		if (c_data->vid_vp_action.nr_enabled) {
+			rc = init_nr_buf(c_data);
+			if (rc < 0)
+				goto s_on_deinit_m_buf;
+		}
+
+		c_data->vid_vp_action.vp_state = VP_FRAME1;
+
+		rc = vb2_streamon(&c_data->vp_in_vidq,
+				V4L2_BUF_TYPE_INTERLACED_IN_DECODER);
+		if (rc < 0)
+			goto s_on_deinit_nr_buf;
+
+		rc = vb2_streamon(&c_data->vp_out_vidq,
+				V4L2_BUF_TYPE_VIDEO_OUTPUT);
+		if (rc < 0)
+			goto s_on_deinit_nr_buf;
+		return rc;
+	case VC_AND_VP_VCAP_OP:
+		rc = streamon_validate_q(&c_data->vc_vidq);
+		if (rc < 0)
+			return rc;
+		rc = streamon_validate_q(&c_data->vp_in_vidq);
+		if (rc < 0)
+			return rc;
+		rc = streamon_validate_q(&c_data->vp_out_vidq);
+		if (rc < 0)
+			return rc;
+
+		c_data->dev->vc_client = c_data;
+		c_data->dev->vp_client = c_data;
+		c_data->dev->vc_to_vp_work.cd = c_data;
+
+		rc = config_vc_format(c_data);
+		if (rc < 0)
+			return rc;
+		rc = config_vp_format(c_data);
+		if (rc < 0)
+			return rc;
+		rc = init_motion_buf(c_data);
+		if (rc < 0)
+			return rc;
+		if (c_data->vid_vp_action.nr_enabled) {
+			rc = init_nr_buf(c_data);
+			if (rc < 0)
+				goto s_on_deinit_m_buf;
+		}
+		c_data->streaming = 1;
+
+		c_data->vid_vp_action.vp_state = VP_FRAME1;
+
+		/* These stream on calls should not fail */
+		rc = vb2_streamon(&c_data->vc_vidq,
+				V4L2_BUF_TYPE_VIDEO_CAPTURE);
+		if (rc < 0)
+			goto s_on_deinit_nr_buf;
+
+		rc = vb2_streamon(&c_data->vp_in_vidq,
+				V4L2_BUF_TYPE_INTERLACED_IN_DECODER);
+		if (rc < 0)
+			goto s_on_deinit_nr_buf;
+
+		rc = vb2_streamon(&c_data->vp_out_vidq,
+				V4L2_BUF_TYPE_VIDEO_OUTPUT);
+		if (rc < 0)
+			goto s_on_deinit_nr_buf;
+		return rc;
+	default:
+		pr_err("VCAP Error: %s: Operation Mode type", __func__);
+		return -ENOTRECOVERABLE;
+	}
+	return 0;
+
+s_on_deinit_nr_buf:
+	if (c_data->vid_vp_action.nr_enabled)
+		deinit_nr_buf(c_data);
+s_on_deinit_m_buf:
+	deinit_motion_buf(c_data);
+	return rc;
+}
+
+int streamoff_validate_q(struct vb2_queue *q)
+{
+	if (q->fileio) {
+		dprintk(1, "streamoff: file io in progress\n");
+		return -EBUSY;
+	}
+
+	if (!q->streaming) {
+		dprintk(1, "streamoff: not streaming\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+	struct vcap_client_data *c_data = file->private_data;
+	int rc;
+
+	switch (c_data->op_mode) {
+	case VC_VCAP_OP:
+		rc = vb2_streamoff(&c_data->vc_vidq, i);
+		if (rc >= 0)
+			atomic_set(&c_data->dev->vc_enabled, 0);
+		return rc;
+	case VP_VCAP_OP:
+		rc = streamoff_validate_q(&c_data->vp_in_vidq);
+		if (rc < 0)
+			return rc;
+		rc = streamoff_validate_q(&c_data->vp_out_vidq);
+		if (rc < 0)
+			return rc;
+
+		/* These stream on calls should not fail */
+		rc = vb2_streamoff(&c_data->vp_in_vidq,
+				V4L2_BUF_TYPE_INTERLACED_IN_DECODER);
+		if (rc < 0)
+			return rc;
+
+		rc = vb2_streamoff(&c_data->vp_out_vidq,
+				V4L2_BUF_TYPE_VIDEO_OUTPUT);
+		if (rc < 0)
+			return rc;
+
+		deinit_motion_buf(c_data);
+		if (c_data->vid_vp_action.nr_enabled)
+			deinit_nr_buf(c_data);
+		atomic_set(&c_data->dev->vp_enabled, 0);
+		return rc;
+	case VC_AND_VP_VCAP_OP:
+		rc = streamoff_validate_q(&c_data->vc_vidq);
+		if (rc < 0)
+			return rc;
+		rc = streamoff_validate_q(&c_data->vp_in_vidq);
+		if (rc < 0)
+			return rc;
+		rc = streamoff_validate_q(&c_data->vp_out_vidq);
+		if (rc < 0)
+			return rc;
+
+		/* These stream on calls should not fail */
+		c_data->streaming = 0;
+		rc = vb2_streamoff(&c_data->vc_vidq,
+				V4L2_BUF_TYPE_VIDEO_CAPTURE);
+		if (rc < 0)
+			return rc;
+
+		rc = vb2_streamoff(&c_data->vp_in_vidq,
+				V4L2_BUF_TYPE_INTERLACED_IN_DECODER);
+		if (rc < 0)
+			return rc;
+
+		rc = vb2_streamoff(&c_data->vp_out_vidq,
+				V4L2_BUF_TYPE_VIDEO_OUTPUT);
+		if (rc < 0)
+			return rc;
+
+		deinit_motion_buf(c_data);
+		if (c_data->vid_vp_action.nr_enabled)
+			deinit_nr_buf(c_data);
+		atomic_set(&c_data->dev->vc_enabled, 0);
+		atomic_set(&c_data->dev->vp_enabled, 0);
+		return rc;
+	default:
+		pr_err("VCAP Error: %s: Unknown Operation mode", __func__);
+		return -ENOTRECOVERABLE;
+	}
+	return 0;
+}
+
+/* VCAP fops */
+static void *vcap_ops_get_userptr(void *alloc_ctx, unsigned long vaddr,
+					unsigned long size, int write)
+{
+	struct vcap_buf_info *mem;
+	mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+	if (!mem)
+		return ERR_PTR(-ENOMEM);
+	mem->vaddr = vaddr;
+	mem->size = size;
+	return mem;
+}
+
+static void vcap_ops_put_userptr(void *buf_priv)
+{
+	kfree(buf_priv);
+}
+
+const struct vb2_mem_ops vcap_mem_ops = {
+	.get_userptr =		vcap_ops_get_userptr,
+	.put_userptr =		vcap_ops_put_userptr,
+};
+
+static int vcap_open(struct file *file)
+{
+	struct vcap_dev *dev = video_drvdata(file);
+	struct vcap_client_data *c_data;
+	struct vb2_queue *q;
+	int ret;
+	c_data = kzalloc(sizeof(*c_data), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	c_data->dev = dev;
+
+	spin_lock_init(&c_data->cap_slock);
+
+	/* initialize vc queue */
+	q = &c_data->vc_vidq;
+	memset(q, 0, sizeof(c_data->vc_vidq));
+	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	q->io_modes = VB2_USERPTR;
+	q->drv_priv = c_data;
+	q->buf_struct_size = sizeof(struct vcap_buffer);
+	q->ops = &capture_video_qops;
+	q->mem_ops = &vcap_mem_ops;
+	ret = vb2_queue_init(q);
+	if (ret < 0)
+		goto vc_q_failed;
+
+	/* initialize vp in queue */
+	q = &c_data->vp_in_vidq;
+	memset(q, 0, sizeof(c_data->vp_in_vidq));
+	q->type = V4L2_BUF_TYPE_INTERLACED_IN_DECODER;
+	q->io_modes = VB2_USERPTR;
+	q->drv_priv = c_data;
+	q->buf_struct_size = sizeof(struct vcap_buffer);
+	q->ops = &vp_in_video_qops;
+	q->mem_ops = &vcap_mem_ops;
+	ret = vb2_queue_init(q);
+	if (ret < 0)
+		goto vp_in_q_failed;
+
+	/* initialize vp out queue */
+	q = &c_data->vp_out_vidq;
+	memset(q, 0, sizeof(c_data->vp_out_vidq));
+	q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+	q->io_modes = VB2_USERPTR;
+	q->drv_priv = c_data;
+	q->buf_struct_size = sizeof(struct vcap_buffer);
+	q->ops = &vp_out_video_qops;
+	q->mem_ops = &vcap_mem_ops;
+
+	ret = vb2_queue_init(q);
+	if (ret < 0)
+		goto vp_out_q_failed;
+
+	INIT_LIST_HEAD(&c_data->vid_vc_action.active);
+	INIT_LIST_HEAD(&c_data->vid_vp_action.in_active);
+	INIT_LIST_HEAD(&c_data->vid_vp_action.out_active);
+	file->private_data = c_data;
+
+	return 0;
+
+vp_out_q_failed:
+	vb2_queue_release(&c_data->vp_in_vidq);
+vp_in_q_failed:
+	vb2_queue_release(&c_data->vc_vidq);
+vc_q_failed:
+	kfree(c_data);
+	return ret;
+}
+
+static int vcap_close(struct file *file)
+{
+	struct vcap_client_data *c_data = file->private_data;
+	vb2_queue_release(&c_data->vp_out_vidq);
+	vb2_queue_release(&c_data->vp_in_vidq);
+	vb2_queue_release(&c_data->vc_vidq);
+	c_data->dev->vc_client = NULL;
+	c_data->dev->vp_client = NULL;
+	kfree(c_data);
+	return 0;
+}
+
+unsigned int poll_work(struct vb2_queue *q, struct file *file,
+	poll_table *wait, bool write_q)
+{
+	unsigned long flags;
+	struct vb2_buffer *vb = NULL;
+
+	if (q->num_buffers == 0)
+		return POLLERR;
+
+	if (list_empty(&q->queued_list))
+		return POLLERR;
+
+	poll_wait(file, &q->done_wq, wait);
+
+	spin_lock_irqsave(&q->done_lock, flags);
+	if (!list_empty(&q->done_list))
+		vb = list_first_entry(&q->done_list, struct vb2_buffer,
+					done_entry);
+	spin_unlock_irqrestore(&q->done_lock, flags);
+
+	if (vb && (vb->state == VB2_BUF_STATE_DONE
+			|| vb->state == VB2_BUF_STATE_ERROR)) {
+		return (write_q) ? POLLOUT | POLLWRNORM :
+			POLLIN | POLLRDNORM;
+	}
+	return 0;
+}
+
+static unsigned int vcap_poll(struct file *file,
+				  struct poll_table_struct *wait)
+{
+	struct vcap_client_data *c_data = file->private_data;
+	struct vb2_queue *q;
+	unsigned int mask = 0;
+
+	switch (c_data->op_mode) {
+	case VC_VCAP_OP:
+		q = &c_data->vc_vidq;
+		return vb2_poll(q, file, wait);
+	case VP_VCAP_OP:
+		q = &c_data->vp_in_vidq;
+		mask = poll_work(q, file, wait, 0);
+		q = &c_data->vp_out_vidq;
+		mask |= poll_work(q, file, wait, 1);
+		return mask;
+	case VC_AND_VP_VCAP_OP:
+		q = &c_data->vp_out_vidq;
+		mask = poll_work(q, file, wait, 0);
+		return mask;
+	default:
+		pr_err("VCAP Error: %s: Unknown operation mode", __func__);
+		return POLLERR;
+	}
+	return 0;
+}
+/* V4L2 and video device structures */
+
+static const struct v4l2_file_operations vcap_fops = {
+	.owner		= THIS_MODULE,
+	.open		= vcap_open,
+	.release	= vcap_close,
+	.poll		= vcap_poll,
+	.unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
+};
+
+static const struct v4l2_ioctl_ops vcap_ioctl_ops = {
+	.vidioc_querycap      = vidioc_querycap,
+	.vidioc_enum_input    = vidioc_enum_input,
+	.vidioc_g_fmt_vid_cap     = vidioc_g_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap   = vidioc_try_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap     = vidioc_s_fmt_vid_cap,
+	.vidioc_s_fmt_type_private     = vidioc_s_fmt_vid_cap,
+	.vidioc_g_fmt_type_private     = vidioc_g_fmt_vid_cap,
+	.vidioc_s_fmt_vid_out_mplane	= vidioc_s_fmt_vid_cap,
+	.vidioc_g_fmt_vid_out_mplane	= vidioc_g_fmt_vid_cap,
+	.vidioc_reqbufs       = vidioc_reqbufs,
+	.vidioc_querybuf      = vidioc_querybuf,
+	.vidioc_qbuf          = vidioc_qbuf,
+	.vidioc_dqbuf         = vidioc_dqbuf,
+	.vidioc_streamon      = vidioc_streamon,
+	.vidioc_streamoff     = vidioc_streamoff,
+};
+
+static struct video_device vcap_template = {
+	.name		= "vcap",
+	.fops		= &vcap_fops,
+	.ioctl_ops	= &vcap_ioctl_ops,
+	.release	= video_device_release,
+};
+
+int vcap_reg_powerup(struct vcap_dev *dev)
+{
+	dev->fs_vcap = regulator_get(NULL, "fs_vcap");
+	if (IS_ERR(dev->fs_vcap)) {
+		pr_err("%s: Regulator FS_VCAP get failed %ld\n", __func__,
+			PTR_ERR(dev->fs_vcap));
+		dev->fs_vcap = NULL;
+		return -EINVAL;
+	} else if (regulator_enable(dev->fs_vcap)) {
+		pr_err("%s: Regulator FS_VCAP enable failed\n", __func__);
+		regulator_put(dev->fs_vcap);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+void vcap_reg_powerdown(struct vcap_dev *dev)
+{
+	if (dev->fs_vcap == NULL)
+		return;
+	regulator_disable(dev->fs_vcap);
+	regulator_put(dev->fs_vcap);
+	dev->fs_vcap = NULL;
+	return;
+}
+
+int config_gpios(int on, struct vcap_platform_data *pdata)
+{
+	int i, ret;
+	int num_gpios = pdata->num_gpios;
+	unsigned *gpios = pdata->gpios;
+
+	if (on) {
+		for (i = 0; i < num_gpios; i++) {
+			ret = gpio_request(gpios[i], "vcap:vc");
+			if (ret) {
+				pr_err("VCAP: failed at GPIO %d to request\n",
+						gpios[i]);
+				goto gpio_failed;
+			}
+			ret = gpio_direction_input(gpios[i]);
+			if (ret) {
+				pr_err("VCAP: failed at GPIO %d to set to input\n",
+					gpios[i]);
+				i++;
+				goto gpio_failed;
+			}
+		}
+	} else {
+		for (i = 0; i < num_gpios; i++)
+			gpio_free(gpios[i]);
+	}
+	dprintk(2, "GPIO config done\n");
+	return 0;
+gpio_failed:
+	for (i--; i >= 0; i--)
+		gpio_free(gpios[i]);
+	return -EINVAL;
+}
+
+int vcap_clk_powerup(struct vcap_dev *dev, struct device *ddev)
+{
+	int ret = 0;
+
+	dev->vcap_clk = clk_get(ddev, "core_clk");
+	if (IS_ERR(dev->vcap_clk)) {
+		dev->vcap_clk = NULL;
+		pr_err("%s: Could not clk_get core_clk\n", __func__);
+		clk_put(dev->vcap_clk);
+		dev->vcap_clk = NULL;
+		return -EINVAL;
+	}
+
+	clk_prepare(dev->vcap_clk);
+	ret = clk_enable(dev->vcap_clk);
+	if (ret) {
+		pr_err("%s: Failed core clk_enable %d\n", __func__, ret);
+		goto fail_vcap_clk_unprep;
+	}
+
+	clk_set_rate(dev->vcap_clk, 160000000);
+	if (ret) {
+		pr_err("%s: Failed core set_rate %d\n", __func__, ret);
+		goto fail_vcap_clk;
+	}
+
+	dev->vcap_npl_clk = clk_get(ddev, "vcap_npl_clk");
+	if (IS_ERR(dev->vcap_npl_clk)) {
+		dev->vcap_npl_clk = NULL;
+		pr_err("%s: Could not clk_get npl\n", __func__);
+		clk_put(dev->vcap_npl_clk);
+		dev->vcap_npl_clk = NULL;
+		goto fail_vcap_clk;
+	}
+
+	clk_prepare(dev->vcap_npl_clk);
+	ret = clk_enable(dev->vcap_npl_clk);
+	if (ret) {
+		pr_err("%s:Failed npl clk_enable %d\n", __func__, ret);
+		goto fail_vcap_npl_clk_unprep;
+	}
+
+	dev->vcap_p_clk = clk_get(ddev, "iface_clk");
+	if (IS_ERR(dev->vcap_p_clk)) {
+		dev->vcap_p_clk = NULL;
+		pr_err("%s: Could not clk_get pix(AHB)\n", __func__);
+		clk_put(dev->vcap_p_clk);
+		dev->vcap_p_clk = NULL;
+		goto fail_vcap_npl_clk;
+	}
+
+	clk_prepare(dev->vcap_p_clk);
+	ret = clk_enable(dev->vcap_p_clk);
+	if (ret) {
+		pr_err("%s: Failed pix(AHB) clk_enable %d\n", __func__, ret);
+		goto fail_vcap_p_clk_unprep;
+	}
+	return 0;
+
+fail_vcap_p_clk_unprep:
+	clk_unprepare(dev->vcap_p_clk);
+	clk_put(dev->vcap_p_clk);
+	dev->vcap_p_clk = NULL;
+
+fail_vcap_npl_clk:
+	clk_disable(dev->vcap_npl_clk);
+fail_vcap_npl_clk_unprep:
+	clk_unprepare(dev->vcap_npl_clk);
+	clk_put(dev->vcap_npl_clk);
+	dev->vcap_npl_clk = NULL;
+
+fail_vcap_clk:
+	clk_disable(dev->vcap_clk);
+fail_vcap_clk_unprep:
+	clk_unprepare(dev->vcap_clk);
+	clk_put(dev->vcap_clk);
+	dev->vcap_clk = NULL;
+	return -EINVAL;
+}
+
+void vcap_clk_powerdown(struct vcap_dev *dev)
+{
+	if (dev->vcap_p_clk != NULL) {
+		clk_disable(dev->vcap_p_clk);
+		clk_unprepare(dev->vcap_p_clk);
+		clk_put(dev->vcap_p_clk);
+		dev->vcap_p_clk = NULL;
+	}
+
+	if (dev->vcap_npl_clk != NULL) {
+		clk_disable(dev->vcap_npl_clk);
+		clk_unprepare(dev->vcap_npl_clk);
+		clk_put(dev->vcap_npl_clk);
+		dev->vcap_npl_clk = NULL;
+	}
+
+	if (dev->vcap_clk != NULL) {
+		clk_disable(dev->vcap_clk);
+		clk_unprepare(dev->vcap_clk);
+		clk_put(dev->vcap_clk);
+		dev->vcap_clk = NULL;
+	}
+}
+
+int vcap_get_bus_client_handle(struct vcap_dev *dev)
+{
+	struct msm_bus_scale_pdata *vcap_axi_client_pdata =
+			dev->vcap_pdata->bus_client_pdata;
+	dev->bus_client_handle =
+			msm_bus_scale_register_client(vcap_axi_client_pdata);
+
+	return 0;
+}
+
+int vcap_enable(struct vcap_dev *dev, struct device *ddev)
+{
+	int rc;
+
+	rc = vcap_reg_powerup(dev);
+	if (rc < 0)
+		goto reg_failed;
+	rc = vcap_clk_powerup(dev, ddev);
+	if (rc < 0)
+		goto clk_failed;
+	rc = vcap_get_bus_client_handle(dev);
+	if (rc < 0)
+		goto bus_r_failed;
+	config_gpios(1, dev->vcap_pdata);
+	if (rc < 0)
+		goto gpio_failed;
+	return 0;
+
+gpio_failed:
+	msm_bus_scale_unregister_client(dev->bus_client_handle);
+	dev->bus_client_handle = 0;
+bus_r_failed:
+	vcap_clk_powerdown(dev);
+clk_failed:
+	vcap_reg_powerdown(dev);
+reg_failed:
+	return rc;
+}
+
+int vcap_disable(struct vcap_dev *dev)
+{
+	config_gpios(0, dev->vcap_pdata);
+
+	msm_bus_scale_unregister_client(dev->bus_client_handle);
+	dev->bus_client_handle = 0;
+	vcap_clk_powerdown(dev);
+	vcap_reg_powerdown(dev);
+	return 0;
+}
+
+static irqreturn_t vcap_vp_handler(int irq_num, void *data)
+{
+	return vp_handler(vcap_ctrl);
+}
+
+static irqreturn_t vcap_vc_handler(int irq_num, void *data)
+{
+	return vc_handler(vcap_ctrl);
+}
+
+static int __devinit vcap_probe(struct platform_device *pdev)
+{
+	struct vcap_dev *dev;
+	struct video_device *vfd;
+	int ret;
+
+	dprintk(1, "Probe started\n");
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+	vcap_ctrl = dev;
+	dev->vcap_pdata = pdev->dev.platform_data;
+
+	dev->vcapmem = platform_get_resource_byname(pdev,
+			IORESOURCE_MEM, "vcap");
+	if (!dev->vcapmem) {
+		pr_err("VCAP: %s: no mem resource?\n", __func__);
+		ret = -ENODEV;
+		goto free_dev;
+	}
+
+	dev->vcapio = request_mem_region(dev->vcapmem->start,
+		resource_size(dev->vcapmem), pdev->name);
+	if (!dev->vcapio) {
+		pr_err("VCAP: %s: no valid mem region\n", __func__);
+		ret = -EBUSY;
+		goto free_dev;
+	}
+
+	dev->vcapbase = ioremap(dev->vcapmem->start,
+		resource_size(dev->vcapmem));
+	if (!dev->vcapbase) {
+		ret = -ENOMEM;
+		pr_err("VCAP: %s: vcap ioremap failed\n", __func__);
+		goto free_resource;
+	}
+
+	dev->vcirq = platform_get_resource_byname(pdev,
+					IORESOURCE_IRQ, "vc_irq");
+	if (!dev->vcirq) {
+		pr_err("%s: no vc irq resource?\n", __func__);
+		ret = -ENODEV;
+		goto free_resource;
+	}
+	dev->vpirq = platform_get_resource_byname(pdev,
+					IORESOURCE_IRQ, "vp_irq");
+	if (!dev->vpirq) {
+		pr_err("%s: no vp irq resource?\n", __func__);
+		ret = -ENODEV;
+		goto free_resource;
+	}
+
+
+	ret = request_irq(dev->vcirq->start, vcap_vc_handler,
+		IRQF_TRIGGER_RISING, "vc_irq", 0);
+	if (ret < 0) {
+		pr_err("%s: vc irq request fail\n", __func__);
+		ret = -EBUSY;
+		goto free_resource;
+	}
+	disable_irq(dev->vcirq->start);
+
+	ret = request_irq(dev->vpirq->start, vcap_vp_handler,
+		IRQF_TRIGGER_RISING, "vp_irq", 0);
+
+	if (ret < 0) {
+		pr_err("%s: vp irq request fail\n", __func__);
+		ret = -EBUSY;
+		goto free_resource;
+	}
+	disable_irq(dev->vpirq->start);
+
+	snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
+			"%s", MSM_VCAP_DRV_NAME);
+
+	ret = v4l2_device_register(NULL, &dev->v4l2_dev);
+	if (ret)
+		goto free_resource;
+
+	ret = vcap_enable(dev, &pdev->dev);
+	if (ret)
+		goto unreg_dev;
+	msm_bus_scale_client_update_request(dev->bus_client_handle, 3);
+
+	ret = detect_vc(dev);
+
+	if (ret)
+		goto power_down;
+
+	/* init video device*/
+	vfd = video_device_alloc();
+	if (!vfd)
+		goto deinit_vc;
+
+	*vfd = vcap_template;
+	vfd->v4l2_dev = &dev->v4l2_dev;
+
+	ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
+	if (ret < 0)
+		goto rel_vdev;
+
+	dev->vfd = vfd;
+	video_set_drvdata(vfd, dev);
+
+	dev->vcap_wq = create_workqueue("vcap");
+	if (!dev->vcap_wq) {
+		pr_err("Could not create workqueue");
+		goto rel_vdev;
+	}
+
+	dev->ion_client = msm_ion_client_create(-1, "vcap");
+	if (IS_ERR((void *)dev->ion_client)) {
+		pr_err("could not get ion client");
+		goto rel_vcap_wq;
+	}
+
+	atomic_set(&dev->vc_enabled, 0);
+	atomic_set(&dev->vp_enabled, 0);
+
+	dprintk(1, "Exit probe succesfully");
+	return 0;
+rel_vcap_wq:
+	destroy_workqueue(dev->vcap_wq);
+rel_vdev:
+	video_device_release(vfd);
+deinit_vc:
+	deinit_vc();
+power_down:
+	vcap_disable(dev);
+unreg_dev:
+	v4l2_device_unregister(&dev->v4l2_dev);
+free_resource:
+	iounmap(dev->vcapbase);
+	release_mem_region(dev->vcapmem->start, resource_size(dev->vcapmem));
+free_dev:
+	vcap_ctrl = NULL;
+	kfree(dev);
+	return ret;
+}
+
+static int __devexit vcap_remove(struct platform_device *pdev)
+{
+	struct vcap_dev *dev = vcap_ctrl;
+	ion_client_destroy(dev->ion_client);
+	flush_workqueue(dev->vcap_wq);
+	destroy_workqueue(dev->vcap_wq);
+	video_device_release(dev->vfd);
+	deinit_vc();
+	vcap_disable(dev);
+	v4l2_device_unregister(&dev->v4l2_dev);
+	iounmap(dev->vcapbase);
+	release_mem_region(dev->vcapmem->start, resource_size(dev->vcapmem));
+	vcap_ctrl = NULL;
+	kfree(dev);
+
+	return 0;
+}
+
+struct platform_driver vcap_platform_driver = {
+	.driver		= {
+		.name	= MSM_VCAP_DRV_NAME,
+		.owner	= THIS_MODULE,
+	},
+	.probe		= vcap_probe,
+	.remove		= vcap_remove,
+};
+
+static int __init vcap_init_module(void)
+{
+	return platform_driver_register(&vcap_platform_driver);
+}
+
+static void __exit vcap_exit_module(void)
+{
+	platform_driver_unregister(&vcap_platform_driver);
+}
+
+module_init(vcap_init_module);
+module_exit(vcap_exit_module);
+MODULE_DESCRIPTION("VCAP driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/vcap_vc.c b/drivers/media/video/vcap_vc.c
new file mode 100644
index 0000000..2c4a243
--- /dev/null
+++ b/drivers/media/video/vcap_vc.c
@@ -0,0 +1,387 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <mach/camera.h>
+#include <linux/io.h>
+#include <linux/regulator/consumer.h>
+#include <mach/clk.h>
+#include <linux/clk.h>
+
+#include <media/vcap_v4l2.h>
+#include <media/vcap_fmt.h>
+#include "vcap_vc.h"
+
+static unsigned debug;
+
+#define dprintk(level, fmt, arg...)					\
+	do {								\
+		if (debug >= level)					\
+			printk(KERN_DEBUG "VC: " fmt, ## arg);		\
+	} while (0)
+
+void config_buffer(struct vcap_client_data *c_data,
+			struct vcap_buffer *buf,
+			void __iomem *y_addr,
+			void __iomem *c_addr)
+{
+	if (c_data->vc_format.color_space == HAL_VCAP_RGB) {
+		writel_relaxed(buf->paddr, y_addr);
+	} else {
+		int size = ((c_data->vc_format.hactive_end -
+				c_data->vc_format.hactive_start) *
+				(c_data->vc_format.vactive_end -
+				c_data->vc_format.vactive_start));
+		writel_relaxed(buf->paddr, y_addr);
+		writel_relaxed(buf->paddr + size, c_addr);
+	}
+}
+
+static void mov_buf_to_vp(struct work_struct *work)
+{
+	struct vp_work_t *vp_work = container_of(work, struct vp_work_t, work);
+	struct v4l2_buffer p;
+	struct vb2_buffer *vb_vc;
+	struct vcap_buffer *buf_vc;
+	struct vb2_buffer *vb_vp;
+	struct vcap_buffer *buf_vp;
+
+	int rc;
+	p.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	p.memory = V4L2_MEMORY_USERPTR;
+	while (1) {
+		if (!vp_work->cd->streaming)
+			return;
+		rc = vb2_dqbuf(&vp_work->cd->vc_vidq, &p, O_NONBLOCK);
+		if (rc < 0)
+			return;
+
+		vb_vc = vp_work->cd->vc_vidq.bufs[p.index];
+		if (NULL == vb_vc) {
+			dprintk(1, "%s: buffer is NULL\n", __func__);
+			vb2_qbuf(&vp_work->cd->vc_vidq, &p);
+			return;
+		}
+		buf_vc = container_of(vb_vc, struct vcap_buffer, vb);
+
+		vb_vp = vp_work->cd->vp_in_vidq.bufs[p.index];
+		if (NULL == vb_vp) {
+			dprintk(1, "%s: buffer is NULL\n", __func__);
+			vb2_qbuf(&vp_work->cd->vc_vidq, &p);
+			return;
+		}
+		buf_vp = container_of(vb_vp, struct vcap_buffer, vb);
+		buf_vp->ion_handle = buf_vc->ion_handle;
+		buf_vp->paddr = buf_vc->paddr;
+		buf_vc->ion_handle = NULL;
+		buf_vc->paddr = 0;
+
+		p.type = V4L2_BUF_TYPE_INTERLACED_IN_DECODER;
+
+		/* This call should not fail */
+		rc = vb2_qbuf(&vp_work->cd->vp_in_vidq, &p);
+		if (rc < 0) {
+			pr_err("%s: qbuf to vp_in failed\n", __func__);
+			buf_vc->ion_handle = buf_vp->ion_handle;
+			buf_vc->paddr = buf_vp->paddr;
+			buf_vp->ion_handle = NULL;
+			buf_vp->paddr = 0;
+			p.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+			vb2_qbuf(&vp_work->cd->vc_vidq, &p);
+		}
+	}
+}
+
+irqreturn_t vc_handler(struct vcap_dev *dev)
+{
+	uint32_t irq, timestamp;
+	enum rdy_buf vc_buf_status, buf_ind;
+	struct vcap_buffer *buf;
+	struct vb2_buffer *vb = NULL;
+	struct vcap_client_data *c_data;
+
+
+	irq = readl_relaxed(VCAP_VC_INT_STATUS);
+
+	dprintk(1, "%s: irq=0x%08x\n", __func__, irq);
+
+	vc_buf_status = irq & VC_BUFFER_WRITTEN;
+
+	dprintk(1, "Done buf status = %d\n", vc_buf_status);
+
+	if (vc_buf_status == VC_NO_BUF) {
+		writel_relaxed(irq, VCAP_VC_INT_CLEAR);
+		pr_err("VC IRQ shows some error\n");
+		return IRQ_HANDLED;
+	}
+
+	if (dev->vc_client == NULL) {
+		writel_relaxed(irq, VCAP_VC_INT_CLEAR);
+		pr_err("VC: There is no active vc client\n");
+		return IRQ_HANDLED;
+	}
+	c_data = dev->vc_client;
+
+	spin_lock(&dev->vc_client->cap_slock);
+	if (list_empty(&dev->vc_client->vid_vc_action.active)) {
+		/* Just leave we have no new queued buffers */
+		writel_relaxed(irq, VCAP_VC_INT_CLEAR);
+		spin_unlock(&dev->vc_client->cap_slock);
+		dprintk(1, "We have no more avilable buffers\n");
+		return IRQ_HANDLED;
+	}
+	spin_unlock(&dev->vc_client->cap_slock);
+
+	timestamp = readl_relaxed(VCAP_VC_TIMESTAMP);
+
+	buf_ind = dev->vc_client->vid_vc_action.buf_ind;
+
+	if (vc_buf_status == VC_BUF1N2) {
+		/* There are 2 buffer ready */
+		writel_relaxed(irq, VCAP_VC_INT_CLEAR);
+		return IRQ_HANDLED;
+	} else if (buf_ind != vc_buf_status) {
+		/* buffer is out of sync */
+		writel_relaxed(irq, VCAP_VC_INT_CLEAR);
+		return IRQ_HANDLED;
+	}
+
+	if (buf_ind == VC_BUF1) {
+		dprintk(1, "Got BUF1\n");
+		vb = &dev->vc_client->vid_vc_action.buf1->vb;
+		spin_lock(&dev->vc_client->cap_slock);
+		if (list_empty(&dev->vc_client->vid_vc_action.active)) {
+			spin_unlock(&dev->vc_client->cap_slock);
+			writel_relaxed(irq, VCAP_VC_INT_CLEAR);
+			return IRQ_HANDLED;
+		}
+		buf = list_entry(dev->vc_client->vid_vc_action.active.next,
+				struct vcap_buffer, list);
+		list_del(&buf->list);
+		spin_unlock(&dev->vc_client->cap_slock);
+		/* Config vc with this new buffer */
+		config_buffer(c_data, buf, VCAP_VC_Y_ADDR_1,
+				VCAP_VC_C_ADDR_1);
+
+		vb->v4l2_buf.timestamp.tv_usec = timestamp;
+		vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+		dev->vc_client->vid_vc_action.buf1 = buf;
+		dev->vc_client->vid_vc_action.buf_ind = VC_BUF2;
+		irq = VC_BUF1;
+	} else {
+		dprintk(1, "Got BUF2\n");
+		spin_lock(&dev->vc_client->cap_slock);
+		vb = &dev->vc_client->vid_vc_action.buf2->vb;
+		if (list_empty(&dev->vc_client->vid_vc_action.active)) {
+			writel_relaxed(irq, VCAP_VC_INT_CLEAR);
+			spin_unlock(&dev->vc_client->cap_slock);
+			return IRQ_HANDLED;
+		}
+		buf = list_entry(dev->vc_client->vid_vc_action.active.next,
+						 struct vcap_buffer, list);
+		list_del(&buf->list);
+		spin_unlock(&dev->vc_client->cap_slock);
+		/* Config vc with this new buffer */
+		config_buffer(c_data, buf, VCAP_VC_Y_ADDR_2,
+				VCAP_VC_C_ADDR_2);
+
+		vb->v4l2_buf.timestamp.tv_usec = timestamp;
+		vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+
+		dev->vc_client->vid_vc_action.buf2 = buf;
+		dev->vc_client->vid_vc_action.buf_ind = VC_BUF1;
+		irq = VC_BUF2;
+	}
+
+	if (c_data->op_mode == VC_AND_VP_VCAP_OP)
+		queue_work(dev->vcap_wq, &dev->vc_to_vp_work.work);
+
+	writel_relaxed(irq, VCAP_VC_INT_CLEAR);
+
+	return IRQ_HANDLED;
+}
+
+int vc_start_capture(struct vcap_client_data *c_data)
+{
+	return 0;
+}
+
+int vc_hw_kick_off(struct vcap_client_data *c_data)
+{
+	struct vcap_action *vid_vc_action = &c_data->vid_vc_action;
+	struct vcap_dev *dev;
+	unsigned long flags = 0;
+	int rc, counter = 0;
+	struct vcap_buffer *buf;
+
+	dev = c_data->dev;
+	vid_vc_action->buf_ind = VC_BUF1;
+	dprintk(2, "Start Kickoff\n");
+
+	if (dev->vc_client == NULL) {
+		pr_err("No active vc client\n");
+		return -ENODEV;
+	}
+	spin_lock_irqsave(&dev->vc_client->cap_slock, flags);
+	if (list_empty(&dev->vc_client->vid_vc_action.active)) {
+		spin_unlock_irqrestore(&dev->vc_client->cap_slock, flags);
+		pr_err("%s: VC We have no more avilable buffers\n",
+				__func__);
+		return -EINVAL;
+	}
+
+	list_for_each_entry(buf, &vid_vc_action->active, list)
+		counter++;
+
+	if (counter < 2) {
+		/* not enough buffers have been queued */
+		spin_unlock_irqrestore(&dev->vc_client->cap_slock, flags);
+		return -EINVAL;
+	}
+
+	vid_vc_action->buf1 = list_entry(vid_vc_action->active.next,
+			struct vcap_buffer, list);
+	list_del(&vid_vc_action->buf1->list);
+
+	vid_vc_action->buf2 = list_entry(vid_vc_action->active.next,
+			struct vcap_buffer, list);
+	list_del(&vid_vc_action->buf2->list);
+
+	spin_unlock_irqrestore(&dev->vc_client->cap_slock, flags);
+
+	config_buffer(c_data, vid_vc_action->buf1, VCAP_VC_Y_ADDR_1,
+			VCAP_VC_C_ADDR_1);
+	config_buffer(c_data, vid_vc_action->buf2, VCAP_VC_Y_ADDR_2,
+			VCAP_VC_C_ADDR_2);
+
+	rc = readl_relaxed(VCAP_VC_CTRL);
+	writel_iowmb(rc | 0x1, VCAP_VC_CTRL);
+
+	writel_relaxed(0x6, VCAP_VC_INT_MASK);
+
+	enable_irq(dev->vcirq->start);
+	return 0;
+}
+
+void vc_stop_capture(struct vcap_client_data *c_data)
+{
+	struct vcap_dev *dev = c_data->dev;
+	int rc;
+
+	rc = readl_relaxed(VCAP_VC_CTRL);
+	writel_iowmb(rc & ~(0x1), VCAP_VC_CTRL);
+
+	if (atomic_read(&dev->vc_enabled) == 1)
+		disable_irq(dev->vcirq->start);
+
+	flush_workqueue(dev->vcap_wq);
+}
+
+int config_vc_format(struct vcap_client_data *c_data)
+{
+	struct vcap_dev *dev;
+	unsigned int rc;
+	int timeout;
+	struct v4l2_format_vc_ext *vc_format = &c_data->vc_format;
+	dev = c_data->dev;
+
+	/* restart VC */
+	writel_relaxed(0x00000001, VCAP_SW_RESET_REQ);
+	timeout = 10000;
+	while (1) {
+		rc = (readl_relaxed(VCAP_SW_RESET_STATUS) & 0x1);
+		if (!rc)
+			break;
+		timeout--;
+		if (timeout == 0) {
+			pr_err("VC is not resetting properly\n");
+			return -EINVAL;
+		}
+	}
+	writel_relaxed(0x00000000, VCAP_SW_RESET_REQ);
+
+	writel_iowmb(0x00000102, VCAP_VC_NPL_CTRL);
+	rc = readl_relaxed(VCAP_VC_NPL_CTRL);
+	rc = readl_relaxed(VCAP_VC_NPL_CTRL);
+	writel_iowmb(0x00000002, VCAP_VC_NPL_CTRL);
+
+	dprintk(2, "%s: Starting VC configuration\n", __func__);
+	writel_iowmb(0x00000002, VCAP_VC_NPL_CTRL);
+	writel_iowmb(0x00000004 | vc_format->color_space << 1 |
+			vc_format->mode << 3 |
+			vc_format->mode << 10, VCAP_VC_CTRL);
+
+	writel_relaxed(vc_format->h_polar << 4 |
+			vc_format->v_polar << 0, VCAP_VC_POLARITY);
+
+	writel_relaxed(vc_format->h_polar << 4 |
+			vc_format->v_polar << 0, VCAP_VC_POLARITY);
+	writel_relaxed(((vc_format->htotal << 16) | vc_format->vtotal),
+			VCAP_VC_V_H_TOTAL);
+	writel_relaxed(((vc_format->hactive_end << 16) |
+			vc_format->hactive_start), VCAP_VC_H_ACTIVE);
+
+	writel_relaxed(((vc_format->vactive_end << 16) |
+			vc_format->vactive_start), VCAP_VC_V_ACTIVE);
+	writel_relaxed(((vc_format->f2_vactive_end << 16) |
+			vc_format->f2_vactive_start), VCAP_VC_V_ACTIVE_F2);
+	writel_relaxed(((vc_format->vsync_end << 16) | vc_format->vsync_start),
+			VCAP_VC_VSYNC_VPOS);
+	writel_relaxed(((vc_format->f2_vsync_v_end << 16) |
+			vc_format->f2_vsync_v_start), VCAP_VC_VSYNC_F2_VPOS);
+	writel_relaxed(((vc_format->hsync_end << 16) |
+			vc_format->hsync_start), VCAP_VC_HSYNC_HPOS);
+	writel_relaxed(((vc_format->f2_vsync_h_end << 16) |
+			vc_format->f2_vsync_h_start), VCAP_VC_VSYNC_F2_HPOS);
+	writel_iowmb(0x000033FF, VCAP_VC_BUF_CTRL);
+
+	rc = vc_format->hactive_end - vc_format->hactive_start;
+	if (vc_format->color_space)
+		rc *= 3;
+
+	writel_relaxed(rc, VCAP_VC_Y_STRIDE);
+	writel_relaxed(rc, VCAP_VC_C_STRIDE);
+
+	writel_relaxed(0x00010033 , VCAP_OFFSET(0x0898));
+	writel_relaxed(0x00010fff , VCAP_OFFSET(0x089c));
+	writel_relaxed(0x0a418820, VCAP_VC_IN_CTRL1);
+	writel_relaxed(0x16a4a0e6, VCAP_VC_IN_CTRL2);
+	writel_relaxed(0x2307b9ac, VCAP_VC_IN_CTRL3);
+	writel_relaxed(0x2f6ad272, VCAP_VC_IN_CTRL4);
+	writel_relaxed(0x00006b38, VCAP_VC_IN_CTRL5);
+
+	writel_iowmb(0x00000001 , VCAP_OFFSET(0x0d00));
+	dprintk(2, "%s: Done VC configuration\n", __func__);
+
+	return 0;
+}
+
+int detect_vc(struct vcap_dev *dev)
+{
+	int result;
+	result = readl_relaxed(VCAP_HARDWARE_VERSION_REG);
+	dprintk(1, "Hardware version: %08x\n", result);
+	if (result != VCAP_HARDWARE_VERSION)
+		return -ENODEV;
+	INIT_WORK(&dev->vc_to_vp_work.work, mov_buf_to_vp);
+	return 0;
+}
+
+int deinit_vc(void)
+{
+	return 0;
+}
diff --git a/drivers/media/video/vcap_vc.h b/drivers/media/video/vcap_vc.h
new file mode 100644
index 0000000..57d13cd
--- /dev/null
+++ b/drivers/media/video/vcap_vc.h
@@ -0,0 +1,84 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef VCAP_VC_H
+#define VCAP_VC_H
+
+#include <linux/interrupt.h>
+
+#include <media/vcap_v4l2.h>
+
+#define VCAP_HARDWARE_VERSION 0x10000000
+
+#define VCAP_BASE (dev->vcapbase)
+#define VCAP_OFFSET(off) (VCAP_BASE + off)
+
+#define VCAP_HARDWARE_VERSION_REG (VCAP_BASE + 0x0000)
+
+#define VCAP_SW_RESET_REQ (VCAP_BASE + 0x0024)
+#define VCAP_SW_RESET_STATUS (VCAP_BASE + 0x0028)
+
+#define VCAP_VC_CTRL (VCAP_BASE + 0x0800)
+#define VCAP_VC_NPL_CTRL (VCAP_BASE + 0x0804)
+#define VCAP_VC_POLARITY (VCAP_BASE + 0x081c)
+#define VCAP_VC_V_H_TOTAL (VCAP_BASE + 0x0820)
+#define VCAP_VC_H_ACTIVE (VCAP_BASE + 0x0824)
+#define VCAP_VC_V_ACTIVE (VCAP_BASE + 0x0828)
+#define VCAP_VC_V_ACTIVE_F2 (VCAP_BASE + 0x0830)
+#define VCAP_VC_VSYNC_VPOS (VCAP_BASE + 0x0834)
+#define VCAP_VC_VSYNC_F2_VPOS (VCAP_BASE + 0x0838)
+#define VCAP_VC_HSYNC_HPOS (VCAP_BASE + 0x0840)
+#define VCAP_VC_VSYNC_F2_HPOS (VCAP_BASE + 0x083c)
+#define VCAP_VC_BUF_CTRL (VCAP_BASE + 0x0848)
+
+#define VCAP_VC_Y_STRIDE (VCAP_BASE + 0x084c)
+#define VCAP_VC_C_STRIDE (VCAP_BASE + 0x0850)
+
+#define VCAP_VC_Y_ADDR_1 (VCAP_BASE + 0x0854)
+#define VCAP_VC_C_ADDR_1 (VCAP_BASE + 0x0858)
+#define VCAP_VC_Y_ADDR_2 (VCAP_BASE + 0x085c)
+#define VCAP_VC_C_ADDR_2 (VCAP_BASE + 0x0860)
+#define VCAP_VC_Y_ADDR_3 (VCAP_BASE + 0x0864)
+#define VCAP_VC_C_ADDR_3 (VCAP_BASE + 0x0868)
+#define VCAP_VC_Y_ADDR_4 (VCAP_BASE + 0x086c)
+#define VCAP_VC_C_ADDR_4 (VCAP_BASE + 0x0870)
+#define VCAP_VC_Y_ADDR_5 (VCAP_BASE + 0x0874)
+#define VCAP_VC_C_ADDR_5 (VCAP_BASE + 0x0878)
+#define VCAP_VC_Y_ADDR_6 (VCAP_BASE + 0x087c)
+#define VCAP_VC_C_ADDR_6 (VCAP_BASE + 0x0880)
+
+#define VCAP_VC_IN_CTRL1 (VCAP_BASE + 0x0808)
+#define VCAP_VC_IN_CTRL2 (VCAP_BASE + 0x080c)
+#define VCAP_VC_IN_CTRL3 (VCAP_BASE + 0x0810)
+#define VCAP_VC_IN_CTRL4 (VCAP_BASE + 0x0814)
+#define VCAP_VC_IN_CTRL5 (VCAP_BASE + 0x0818)
+
+#define VCAP_VC_INT_MASK (VCAP_BASE + 0x0884)
+#define VCAP_VC_INT_CLEAR (VCAP_BASE + 0x0888)
+#define VCAP_VC_INT_STATUS (VCAP_BASE + 0x088c)
+#define VCAP_VC_TIMESTAMP (VCAP_BASE + 0x0034)
+
+#define VC_BUFFER_WRITTEN (0x3 << 1)
+
+struct vc_reg_data {
+	unsigned data;
+	unsigned addr;
+};
+
+int vc_start_capture(struct vcap_client_data *c_data);
+int vc_hw_kick_off(struct vcap_client_data *c_data);
+void vc_stop_capture(struct vcap_client_data *c_data);
+int config_vc_format(struct vcap_client_data *c_data);
+int detect_vc(struct vcap_dev *dev);
+int deinit_vc(void);
+irqreturn_t vc_handler(struct vcap_dev *dev);
+#endif
diff --git a/drivers/media/video/vcap_vp.c b/drivers/media/video/vcap_vp.c
new file mode 100644
index 0000000..f8dfdc1
--- /dev/null
+++ b/drivers/media/video/vcap_vp.c
@@ -0,0 +1,606 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <mach/camera.h>
+#include <linux/io.h>
+#include <mach/clk.h>
+#include <linux/clk.h>
+
+#include <media/vcap_v4l2.h>
+#include <media/vcap_fmt.h>
+#include "vcap_vp.h"
+
+static unsigned debug;
+
+#define dprintk(level, fmt, arg...)					\
+	do {								\
+		if (debug >= level)					\
+			printk(KERN_DEBUG "VP: " fmt, ## arg);		\
+	} while (0)
+
+void config_nr_buffer(struct vcap_client_data *c_data,
+			struct vcap_buffer *buf)
+{
+	struct vcap_dev *dev = c_data->dev;
+	int size = c_data->vp_in_fmt.height * c_data->vp_in_fmt.width;
+
+	writel_relaxed(buf->paddr, VCAP_VP_NR_T2_Y_BASE_ADDR);
+	writel_relaxed(buf->paddr + size, VCAP_VP_NR_T2_C_BASE_ADDR);
+}
+
+void config_in_buffer(struct vcap_client_data *c_data,
+			struct vcap_buffer *buf)
+{
+	struct vcap_dev *dev = c_data->dev;
+	int size = c_data->vp_in_fmt.height * c_data->vp_in_fmt.width;
+
+	writel_relaxed(buf->paddr, VCAP_VP_T2_Y_BASE_ADDR);
+	writel_relaxed(buf->paddr + size, VCAP_VP_T2_C_BASE_ADDR);
+}
+
+void config_out_buffer(struct vcap_client_data *c_data,
+			struct vcap_buffer *buf)
+{
+	struct vcap_dev *dev = c_data->dev;
+	int size;
+	size = c_data->vp_out_fmt.height * c_data->vp_out_fmt.width;
+	writel_relaxed(buf->paddr, VCAP_VP_OUT_Y_BASE_ADDR);
+	writel_relaxed(buf->paddr + size, VCAP_VP_OUT_C_BASE_ADDR);
+}
+
+int vp_setup_buffers(struct vcap_client_data *c_data)
+{
+	struct vp_action *vp_act;
+	struct vcap_dev *dev;
+	unsigned long flags = 0;
+
+	if (!c_data->streaming)
+		return -ENOEXEC;
+	dev = c_data->dev;
+	dprintk(2, "Start setup buffers\n");
+
+	/* No need to verify vp_client is not NULL caller does so */
+	vp_act = &dev->vp_client->vid_vp_action;
+
+	spin_lock_irqsave(&dev->vp_client->cap_slock, flags);
+	if (list_empty(&vp_act->in_active)) {
+		spin_unlock_irqrestore(&dev->vp_client->cap_slock, flags);
+		dprintk(1, "%s: VP We have no more input buffers\n",
+				__func__);
+		return -EAGAIN;
+	}
+
+	if (list_empty(&vp_act->out_active)) {
+		spin_unlock_irqrestore(&dev->vp_client->cap_slock,
+			flags);
+		dprintk(1, "%s: VP We have no more output buffers\n",
+		   __func__);
+		return -EAGAIN;
+	}
+
+	vp_act->bufT2 = list_entry(vp_act->in_active.next,
+			struct vcap_buffer, list);
+	list_del(&vp_act->bufT2->list);
+
+	vp_act->bufOut = list_entry(vp_act->out_active.next,
+			struct vcap_buffer, list);
+	list_del(&vp_act->bufOut->list);
+	spin_unlock_irqrestore(&dev->vp_client->cap_slock, flags);
+
+	config_in_buffer(c_data, vp_act->bufT2);
+	config_out_buffer(c_data, vp_act->bufOut);
+	return 0;
+}
+
+static void mov_buf_to_vc(struct work_struct *work)
+{
+	struct vp_work_t *vp_work = container_of(work, struct vp_work_t, work);
+	struct v4l2_buffer p;
+	struct vb2_buffer *vb_vc;
+	struct vcap_buffer *buf_vc;
+	struct vb2_buffer *vb_vp;
+	struct vcap_buffer *buf_vp;
+	int rc;
+
+	p.type = V4L2_BUF_TYPE_INTERLACED_IN_DECODER;
+	p.memory = V4L2_MEMORY_USERPTR;
+
+	/* This loop exits when there is no more buffers left */
+	while (1) {
+		if (!vp_work->cd->streaming)
+			return;
+		rc = vb2_dqbuf(&vp_work->cd->vp_in_vidq, &p, O_NONBLOCK);
+		if (rc < 0)
+			return;
+
+		vb_vc = vp_work->cd->vc_vidq.bufs[p.index];
+		if (NULL == vb_vc) {
+			dprintk(1, "%s: buffer is NULL\n", __func__);
+			vb2_qbuf(&vp_work->cd->vp_in_vidq, &p);
+			return;
+		}
+		buf_vc = container_of(vb_vc, struct vcap_buffer, vb);
+
+		vb_vp = vp_work->cd->vp_in_vidq.bufs[p.index];
+		if (NULL == vb_vp) {
+			dprintk(1, "%s: buffer is NULL\n", __func__);
+			vb2_qbuf(&vp_work->cd->vp_in_vidq, &p);
+			return;
+		}
+		buf_vp = container_of(vb_vp, struct vcap_buffer, vb);
+		buf_vc->ion_handle = buf_vp->ion_handle;
+		buf_vc->paddr = buf_vp->paddr;
+		buf_vp->ion_handle = NULL;
+		buf_vp->paddr = 0;
+
+		p.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		/* This call should not fail */
+		rc = vb2_qbuf(&vp_work->cd->vc_vidq, &p);
+		if (rc < 0) {
+			dprintk(1, "%s: qbuf to vc failed\n", __func__);
+			buf_vp->ion_handle = buf_vc->ion_handle;
+			buf_vp->paddr = buf_vc->paddr;
+			buf_vc->ion_handle = NULL;
+			buf_vc->paddr = 0;
+			p.type = V4L2_BUF_TYPE_INTERLACED_IN_DECODER;
+			vb2_qbuf(&vp_work->cd->vp_in_vidq, &p);
+		}
+	}
+}
+
+static void vp_wq_fnc(struct work_struct *work)
+{
+	struct vp_work_t *vp_work = container_of(work, struct vp_work_t, work);
+	struct vcap_dev *dev;
+	struct vp_action *vp_act;
+	uint32_t irq;
+	int rc;
+#ifndef TOP_FIELD_FIX
+	bool top_field;
+#endif
+
+	if (vp_work && vp_work->cd && vp_work->cd->dev)
+		dev = vp_work->cd->dev;
+	else
+		return;
+
+	vp_act = &dev->vp_client->vid_vp_action;
+	irq = vp_work->irq;
+
+	rc = readl_relaxed(VCAP_OFFSET(0x048));
+	while (!(rc & 0x00000100))
+		rc = readl_relaxed(VCAP_OFFSET(0x048));
+
+	writel_relaxed(0x00000000, VCAP_VP_BAL_VMOTION_STATE);
+	writel_relaxed(0x40000000, VCAP_VP_REDUCT_AVG_MOTION2);
+
+	/* Queue the done buffers */
+	if (vp_act->vp_state == VP_NORMAL &&
+			vp_act->bufNR.nr_pos != TM1_BUF) {
+		vb2_buffer_done(&vp_act->bufTm1->vb, VB2_BUF_STATE_DONE);
+		if (vp_work->cd->op_mode == VC_AND_VP_VCAP_OP)
+			queue_work(dev->vcap_wq, &dev->vp_to_vc_work.work);
+	}
+
+	vb2_buffer_done(&vp_act->bufOut->vb, VB2_BUF_STATE_DONE);
+
+	/* Cycle to next state */
+	if (vp_act->vp_state != VP_NORMAL)
+		vp_act->vp_state++;
+#ifdef TOP_FIELD_FIX
+	vp_act->top_field = !vp_act->top_field;
+#endif
+
+	/* Cycle Buffers*/
+	if (vp_work->cd->vid_vp_action.nr_enabled) {
+		if (vp_act->bufNR.nr_pos == TM1_BUF)
+			vp_act->bufNR.nr_pos = BUF_NOT_IN_USE;
+
+		if (vp_act->bufNR.nr_pos != BUF_NOT_IN_USE)
+			vp_act->bufNR.nr_pos++;
+
+		vp_act->bufTm1 = vp_act->bufT0;
+		vp_act->bufT0 = vp_act->bufT1;
+		vp_act->bufT1 = vp_act->bufNRT2;
+		vp_act->bufNRT2 = vp_act->bufT2;
+		config_nr_buffer(vp_work->cd, vp_act->bufNRT2);
+	} else {
+		vp_act->bufTm1 = vp_act->bufT0;
+		vp_act->bufT0 = vp_act->bufT1;
+		vp_act->bufT1 = vp_act->bufT2;
+	}
+
+	rc = vp_setup_buffers(vp_work->cd);
+	if (rc < 0) {
+		/* setup_buf failed because we are waiting for buffers */
+		writel_relaxed(0x00000000, VCAP_VP_INTERRUPT_ENABLE);
+		writel_iowmb(irq, VCAP_VP_INT_CLEAR);
+		atomic_set(&dev->vp_enabled, 0);
+		return;
+	}
+
+	/* Config VP */
+#ifndef TOP_FIELD_FIX
+	if (vp_act->bufT2->vb.v4l2_buf.field == V4L2_FIELD_TOP)
+		top_field = 1;
+#endif
+
+#ifdef TOP_FIELD_FIX
+	writel_iowmb(0x00000000 | vp_act->top_field << 0, VCAP_VP_CTRL);
+	writel_iowmb(0x00030000 | vp_act->top_field << 0, VCAP_VP_CTRL);
+#else
+	writel_iowmb(0x00000000 | top_field, VCAP_VP_CTRL);
+	writel_iowmb(0x00030000 | top_field, VCAP_VP_CTRL);
+#endif
+	enable_irq(dev->vpirq->start);
+	writel_iowmb(irq, VCAP_VP_INT_CLEAR);
+}
+
+irqreturn_t vp_handler(struct vcap_dev *dev)
+{
+	struct vcap_client_data *c_data;
+	struct vp_action *vp_act;
+	uint32_t irq;
+	int rc;
+
+	irq = readl_relaxed(VCAP_VP_INT_STATUS);
+
+	dprintk(1, "%s: irq=0x%08x\n", __func__, irq);
+	if (!irq & VP_PIC_DONE) {
+		writel_relaxed(irq, VCAP_VP_INT_CLEAR);
+		pr_err("VP IRQ shows some error\n");
+		return IRQ_HANDLED;
+	}
+
+	if (dev->vp_client == NULL) {
+		writel_relaxed(irq, VCAP_VP_INT_CLEAR);
+		pr_err("VC: There is no active vp client\n");
+		return IRQ_HANDLED;
+	}
+
+	vp_act = &dev->vp_client->vid_vp_action;
+	c_data = dev->vp_client;
+
+	if (vp_act->vp_state == VP_UNKNOWN) {
+		writel_relaxed(irq, VCAP_VP_INT_CLEAR);
+		pr_err("%s: VP is in an unknown state\n",
+				__func__);
+		return -EAGAIN;
+	}
+
+	INIT_WORK(&dev->vp_work.work, vp_wq_fnc);
+	dev->vp_work.cd = c_data;
+	dev->vp_work.irq = irq;
+	rc = queue_work(dev->vcap_wq, &dev->vp_work.work);
+
+	disable_irq_nosync(dev->vpirq->start);
+	return IRQ_HANDLED;
+}
+
+void vp_stop_capture(struct vcap_client_data *c_data)
+{
+	struct vcap_dev *dev = c_data->dev;
+
+	writel_iowmb(0x00000000, VCAP_VP_CTRL);
+	flush_workqueue(dev->vcap_wq);
+
+	if (atomic_read(&dev->vp_enabled) == 1)
+		disable_irq(dev->vpirq->start);
+
+	writel_iowmb(0x00000001, VCAP_VP_SW_RESET);
+	writel_iowmb(0x00000000, VCAP_VP_SW_RESET);
+}
+
+int config_vp_format(struct vcap_client_data *c_data)
+{
+	struct vcap_dev *dev = c_data->dev;
+
+	INIT_WORK(&dev->vp_to_vc_work.work, mov_buf_to_vc);
+	dev->vp_to_vc_work.cd = c_data;
+
+	/* SW restart VP */
+	writel_iowmb(0x00000001, VCAP_VP_SW_RESET);
+	writel_iowmb(0x00000000, VCAP_VP_SW_RESET);
+
+	/* Film Mode related settings */
+	writel_iowmb(0x00000000, VCAP_VP_FILM_PROJECTION_T0);
+	writel_relaxed(0x00000000, VCAP_VP_FILM_PROJECTION_T2);
+	writel_relaxed(0x00000000, VCAP_VP_FILM_PAST_MAX_PROJ);
+	writel_relaxed(0x00000000, VCAP_VP_FILM_PAST_MIN_PROJ);
+	writel_relaxed(0x00000000, VCAP_VP_FILM_SEQUENCE_HIST);
+	writel_relaxed(0x00000000, VCAP_VP_FILM_MODE_STATE);
+
+	writel_relaxed(0x00000000, VCAP_VP_BAL_VMOTION_STATE);
+	writel_relaxed(0x00000010, VCAP_VP_REDUCT_AVG_MOTION);
+	writel_relaxed(0x40000000, VCAP_VP_REDUCT_AVG_MOTION2);
+	writel_relaxed(0x40000000, VCAP_VP_NR_AVG_LUMA);
+	writel_relaxed(0x40000000, VCAP_VP_NR_AVG_CHROMA);
+	writel_relaxed(0x40000000, VCAP_VP_NR_CTRL_LUMA);
+	writel_relaxed(0x40000000, VCAP_VP_NR_CTRL_CHROMA);
+	writel_relaxed(0x00000000, VCAP_VP_BAL_AVG_BLEND);
+	writel_relaxed(0x00000000, VCAP_VP_VMOTION_HIST);
+	writel_relaxed(0x05047D19, VCAP_VP_FILM_ANALYSIS_CONFIG);
+	writel_relaxed(0x20260200, VCAP_VP_FILM_STATE_CONFIG);
+	writel_relaxed(0x23A60114, VCAP_VP_FVM_CONFIG);
+	writel_relaxed(0x03043210, VCAP_VP_FILM_ANALYSIS_CONFIG2);
+	writel_relaxed(0x04DB7A51, VCAP_VP_MIXED_ANALYSIS_CONFIG);
+	writel_relaxed(0x14224916, VCAP_VP_SPATIAL_CONFIG);
+	writel_relaxed(0x83270400, VCAP_VP_SPATIAL_CONFIG2);
+	writel_relaxed(0x0F000F92, VCAP_VP_SPATIAL_CONFIG3);
+	writel_relaxed(0x00000000, VCAP_VP_TEMPORAL_CONFIG);
+	writel_relaxed(0x00000000, VCAP_VP_PIXEL_DIFF_CONFIG);
+	writel_relaxed(0x0C090511, VCAP_VP_H_FREQ_CONFIG);
+	writel_relaxed(0x0A000000, VCAP_VP_NR_CONFIG);
+	writel_relaxed(0x008F4149, VCAP_VP_NR_LUMA_CONFIG);
+	writel_relaxed(0x008F4149, VCAP_VP_NR_CHROMA_CONFIG);
+	writel_relaxed(0x43C0FD0C, VCAP_VP_BAL_CONFIG);
+	writel_relaxed(0x00000255, VCAP_VP_BAL_MOTION_CONFIG);
+	writel_relaxed(0x24154252, VCAP_VP_BAL_LIGHT_COMB);
+	writel_relaxed(0x10024414, VCAP_VP_BAL_VMOTION_CONFIG);
+	writel_relaxed(0x00000002, VCAP_VP_NR_CONFIG2);
+	writel_relaxed((c_data->vp_out_fmt.height-1)<<16 |
+			(c_data->vp_out_fmt.width - 1), VCAP_VP_FRAME_SIZE);
+	writel_relaxed(0x00000000, VCAP_VP_SPLIT_SCRN_CTRL);
+
+	return 0;
+}
+
+int init_motion_buf(struct vcap_client_data *c_data)
+{
+	struct vcap_dev *dev = c_data->dev;
+	void *buf;
+	unsigned long motion_base_addr;
+	uint32_t size = ((c_data->vp_out_fmt.width + 63) >> 6) *
+		((c_data->vp_out_fmt.height + 7) >> 3) * 16;
+
+	if (c_data->vid_vp_action.bufMotion) {
+		pr_err("Motion buffer has already been created");
+		return -ENOEXEC;
+	}
+
+	buf = kzalloc(size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	c_data->vid_vp_action.bufMotion = buf;
+	motion_base_addr = virt_to_phys(buf);
+	writel_iowmb(motion_base_addr, VCAP_VP_MOTION_EST_ADDR);
+	return 0;
+}
+
+void deinit_motion_buf(struct vcap_client_data *c_data)
+{
+	struct vcap_dev *dev = c_data->dev;
+	void *buf;
+
+	if (!c_data->vid_vp_action.bufMotion) {
+		dprintk(1, "Motion buffer has not been created");
+		return;
+	}
+
+	buf = c_data->vid_vp_action.bufMotion;
+
+	writel_iowmb(0x00000000, VCAP_VP_MOTION_EST_ADDR);
+	c_data->vid_vp_action.bufMotion = NULL;
+	kfree(buf);
+	return;
+}
+
+int init_nr_buf(struct vcap_client_data *c_data)
+{
+	struct vcap_dev *dev = c_data->dev;
+	struct nr_buffer *buf;
+	uint32_t frame_size, tot_size, rc;
+
+	if (c_data->vid_vp_action.bufNR.vaddr) {
+		pr_err("NR buffer has already been created");
+		return -ENOEXEC;
+	}
+	buf = &c_data->vid_vp_action.bufNR;
+
+	frame_size = c_data->vp_in_fmt.width * c_data->vp_in_fmt.height;
+	if (c_data->vp_in_fmt.pixfmt == V4L2_PIX_FMT_NV16)
+		tot_size = frame_size * 2;
+	else
+		tot_size = frame_size / 2 * 3;
+
+	buf->vaddr = kzalloc(tot_size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	buf->paddr = virt_to_phys(buf->vaddr);
+	rc = readl_relaxed(VCAP_VP_NR_CONFIG2);
+	rc |= 0x02D00001;
+	writel_relaxed(rc, VCAP_VP_NR_CONFIG2);
+	writel_relaxed(buf->paddr, VCAP_VP_NR_T2_Y_BASE_ADDR);
+	writel_relaxed(buf->paddr + frame_size, VCAP_VP_NR_T2_C_BASE_ADDR);
+	buf->nr_pos = NRT2_BUF;
+	return 0;
+}
+
+void deinit_nr_buf(struct vcap_client_data *c_data)
+{
+	struct vcap_dev *dev = c_data->dev;
+	struct nr_buffer *buf;
+	uint32_t rc;
+
+	if (!c_data->vid_vp_action.bufNR.vaddr) {
+		pr_err("NR buffer has not been created");
+		return;
+	}
+
+	buf = &c_data->vid_vp_action.bufNR;
+
+	rc = readl_relaxed(VCAP_VP_NR_CONFIG2);
+	rc &= !(0x02D00001);
+	writel_relaxed(rc, VCAP_VP_NR_CONFIG2);
+
+	kfree(buf->vaddr);
+	buf->paddr = 0;
+	buf->vaddr = NULL;
+	return;
+}
+
+int kickoff_vp(struct vcap_client_data *c_data)
+{
+	struct vcap_dev *dev;
+	struct vp_action *vp_act;
+	unsigned long flags = 0;
+	unsigned int chroma_fmt = 0;
+	int size;
+#ifndef TOP_FIELD_FIX
+	bool top_field;
+#endif
+
+	if (!c_data->streaming)
+		return -ENOEXEC;
+
+	dev = c_data->dev;
+	dprintk(2, "Start Kickoff\n");
+
+	if (dev->vp_client == NULL) {
+		pr_err("No active vp client\n");
+		return -ENODEV;
+	}
+	vp_act = &dev->vp_client->vid_vp_action;
+
+	spin_lock_irqsave(&dev->vp_client->cap_slock, flags);
+	if (list_empty(&vp_act->in_active)) {
+		spin_unlock_irqrestore(&dev->vp_client->cap_slock, flags);
+		pr_err("%s: VP We have no more input buffers\n",
+				__func__);
+		return -EAGAIN;
+	}
+
+	vp_act->bufT1 = list_entry(vp_act->in_active.next,
+			struct vcap_buffer, list);
+	list_del(&vp_act->bufT1->list);
+
+	if (list_empty(&vp_act->in_active)) {
+		spin_unlock_irqrestore(&dev->vp_client->cap_slock, flags);
+		list_add(&vp_act->bufT1->list, &vp_act->in_active);
+		pr_err("%s: VP We have no more input buffers\n",
+				__func__);
+		return -EAGAIN;
+	}
+
+	vp_act->bufT2 = list_entry(vp_act->in_active.next,
+			struct vcap_buffer, list);
+	list_del(&vp_act->bufT2->list);
+
+	if (list_empty(&vp_act->out_active)) {
+		spin_unlock_irqrestore(&dev->vp_client->cap_slock, flags);
+		list_add(&vp_act->bufT2->list, &vp_act->in_active);
+		list_add(&vp_act->bufT1->list, &vp_act->in_active);
+		pr_err("%s: VP We have no more output buffers\n",
+				__func__);
+		return -EAGAIN;
+	}
+
+	vp_act->bufOut = list_entry(vp_act->out_active.next,
+			struct vcap_buffer, list);
+	list_del(&vp_act->bufOut->list);
+	spin_unlock_irqrestore(&dev->vp_client->cap_slock, flags);
+
+	size = c_data->vp_in_fmt.height * c_data->vp_in_fmt.width;
+	writel_relaxed(vp_act->bufT1->paddr, VCAP_VP_T1_Y_BASE_ADDR);
+	writel_relaxed(vp_act->bufT1->paddr + size, VCAP_VP_T1_C_BASE_ADDR);
+
+	config_in_buffer(c_data, vp_act->bufT2);
+	config_out_buffer(c_data, vp_act->bufOut);
+
+	/* Config VP */
+	if (c_data->vp_in_fmt.pixfmt == V4L2_PIX_FMT_NV16)
+		chroma_fmt = 1;
+	writel_relaxed((c_data->vp_in_fmt.width / 16) << 20 |
+			chroma_fmt << 11 | 0x2 << 4, VCAP_VP_IN_CONFIG);
+
+	chroma_fmt = 0;
+	if (c_data->vp_in_fmt.pixfmt == V4L2_PIX_FMT_NV16)
+		chroma_fmt = 1;
+
+	writel_relaxed((c_data->vp_in_fmt.width / 16) << 20 |
+			chroma_fmt << 11 | 0x1 << 4, VCAP_VP_OUT_CONFIG);
+
+	/* Enable Interrupt */
+#ifdef TOP_FIELD_FIX
+	vp_act->top_field = 1;
+#else
+	if (vp_act->bufT2->vb.v4l2_buf.field == V4L2_FIELD_TOP)
+		top_field = 1;
+#endif
+	vp_act->vp_state = VP_FRAME2;
+	writel_relaxed(0x01100101, VCAP_VP_INTERRUPT_ENABLE);
+#ifdef TOP_FIELD_FIX
+	writel_iowmb(0x00000000 | vp_act->top_field << 0, VCAP_VP_CTRL);
+	writel_iowmb(0x00030000 | vp_act->top_field << 0, VCAP_VP_CTRL);
+#else
+	writel_iowmb(0x00000000 | top_field, VCAP_VP_CTRL);
+	writel_iowmb(0x00030000 | top_field, VCAP_VP_CTRL);
+#endif
+	atomic_set(&c_data->dev->vp_enabled, 1);
+	enable_irq(dev->vpirq->start);
+	return 0;
+}
+
+int continue_vp(struct vcap_client_data *c_data)
+{
+	struct vcap_dev *dev;
+	struct vp_action *vp_act;
+	int rc;
+#ifndef TOP_FIELD_FIX
+	bool top_field;
+#endif
+
+	dprintk(2, "Start Continue\n");
+	dev = c_data->dev;
+
+	if (dev->vp_client == NULL) {
+		pr_err("No active vp client\n");
+		return -ENODEV;
+	}
+	vp_act = &dev->vp_client->vid_vp_action;
+
+	if (vp_act->vp_state == VP_UNKNOWN) {
+		pr_err("%s: VP is in an unknown state\n",
+				__func__);
+		return -EAGAIN;
+	}
+
+	rc = vp_setup_buffers(c_data);
+	if (rc < 0)
+		return rc;
+
+#ifndef TOP_FIELD_FIX
+	if (vp_act->bufT2->vb.v4l2_buf.field == V4L2_FIELD_TOP)
+		top_field = 1;
+#endif
+
+	/* Config VP & Enable Interrupt */
+	writel_relaxed(0x01100101, VCAP_VP_INTERRUPT_ENABLE);
+#ifdef TOP_FIELD_FIX
+	writel_iowmb(0x00000000 | vp_act->top_field << 0, VCAP_VP_CTRL);
+	writel_iowmb(0x00030000 | vp_act->top_field << 0, VCAP_VP_CTRL);
+#else
+	writel_iowmb(0x00000000 | top_field, VCAP_VP_CTRL);
+	writel_iowmb(0x00030000 | top_field, VCAP_VP_CTRL);
+#endif
+
+	atomic_set(&c_data->dev->vp_enabled, 1);
+	enable_irq(dev->vpirq->start);
+	return 0;
+}
diff --git a/drivers/media/video/vcap_vp.h b/drivers/media/video/vcap_vp.h
new file mode 100644
index 0000000..47ad8d4
--- /dev/null
+++ b/drivers/media/video/vcap_vp.h
@@ -0,0 +1,103 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef VCAP_VP_H
+#define VCAP_VP_H
+
+#include <linux/interrupt.h>
+
+#include <media/vcap_v4l2.h>
+
+#define VCAP_BASE (dev->vcapbase)
+#define VCAP_OFFSET(off) (VCAP_BASE + off)
+
+#define VCAP_VP_INT_STATUS (VCAP_BASE + 0x404)
+#define VCAP_VP_INT_CLEAR (VCAP_BASE + 0x40C)
+
+#define VCAP_VP_SW_RESET (VCAP_BASE + 0x410)
+#define VCAP_VP_INTERRUPT_ENABLE (VCAP_BASE + 0x408)
+
+#define VCAP_VP_FILM_PROJECTION_T0 (VCAP_BASE + 0x50C)
+#define VCAP_VP_FILM_PROJECTION_T2 (VCAP_BASE + 0x508)
+#define VCAP_VP_FILM_PAST_MAX_PROJ (VCAP_BASE + 0x510)
+#define VCAP_VP_FILM_PAST_MIN_PROJ (VCAP_BASE + 0x514)
+#define VCAP_VP_FILM_SEQUENCE_HIST (VCAP_BASE + 0x504)
+#define VCAP_VP_FILM_MODE_STATE (VCAP_BASE + 0x500)
+
+#define VCAP_VP_BAL_VMOTION_STATE (VCAP_BASE + 0x690)
+#define VCAP_VP_REDUCT_AVG_MOTION (VCAP_BASE + 0x610)
+#define VCAP_VP_REDUCT_AVG_MOTION2 (VCAP_BASE + 0x614)
+
+#define VCAP_VP_NR_AVG_LUMA (VCAP_BASE + 0x608)
+#define VCAP_VP_NR_AVG_CHROMA (VCAP_BASE + 0x60C)
+#define VCAP_VP_NR_CTRL_LUMA (VCAP_BASE + 0x600)
+#define VCAP_VP_NR_CTRL_CHROMA (VCAP_BASE + 0x604)
+
+#define VCAP_VP_BAL_AVG_BLEND (VCAP_BASE + 0x694)
+#define VCAP_VP_VMOTION_HIST (VCAP_BASE + 0x6F8)
+
+#define VCAP_VP_MOTION_EST_ADDR (VCAP_BASE + 0x4E0)
+#define VCAP_VP_FILM_ANALYSIS_CONFIG (VCAP_BASE + 0x520)
+#define VCAP_VP_FILM_STATE_CONFIG (VCAP_BASE + 0x524)
+
+#define VCAP_VP_FVM_CONFIG (VCAP_BASE + 0x550)
+#define VCAP_VP_FILM_ANALYSIS_CONFIG2 (VCAP_BASE + 0x52C)
+#define VCAP_VP_MIXED_ANALYSIS_CONFIG (VCAP_BASE + 0x530)
+
+#define VCAP_VP_SPATIAL_CONFIG (VCAP_BASE + 0x580)
+#define VCAP_VP_SPATIAL_CONFIG2 (VCAP_BASE + 0x584)
+#define VCAP_VP_SPATIAL_CONFIG3 (VCAP_BASE + 0x588)
+#define VCAP_VP_TEMPORAL_CONFIG (VCAP_BASE + 0x5C0)
+
+#define VCAP_VP_PIXEL_DIFF_CONFIG (VCAP_BASE + 0x6FC)
+#define VCAP_VP_H_FREQ_CONFIG (VCAP_BASE + 0x528)
+#define VCAP_VP_NR_CONFIG (VCAP_BASE + 0x620)
+#define VCAP_VP_NR_LUMA_CONFIG (VCAP_BASE + 0x624)
+#define VCAP_VP_NR_CHROMA_CONFIG (VCAP_BASE + 0x628)
+#define VCAP_VP_BAL_CONFIG (VCAP_BASE + 0x680)
+#define VCAP_VP_BAL_MOTION_CONFIG (VCAP_BASE + 0x684)
+#define VCAP_VP_BAL_LIGHT_COMB (VCAP_BASE + 0x688)
+#define VCAP_VP_BAL_VMOTION_CONFIG (VCAP_BASE + 0x68C)
+
+#define VCAP_VP_NR_CONFIG2 (VCAP_BASE + 0x484)
+#define VCAP_VP_FRAME_SIZE (VCAP_BASE + 0x48C)
+#define VCAP_VP_SPLIT_SCRN_CTRL (VCAP_BASE + 0x750)
+
+#define VCAP_VP_IN_CONFIG (VCAP_BASE + 0x480)
+#define VCAP_VP_OUT_CONFIG (VCAP_BASE + 0x488)
+
+#define VCAP_VP_T2_Y_BASE_ADDR (VCAP_BASE + 0x4C0)
+#define VCAP_VP_T2_C_BASE_ADDR (VCAP_BASE + 0x4C4)
+#define VCAP_VP_OUT_Y_BASE_ADDR (VCAP_BASE + 0x4CC)
+#define VCAP_VP_OUT_C_BASE_ADDR (VCAP_BASE + 0x4D0)
+#define VCAP_VP_OUT_CR_BASE_ADDR (VCAP_BASE + 0x4D4)
+
+#define VCAP_VP_CTRL (VCAP_BASE + 0x4D8)
+
+#define VCAP_VP_T1_Y_BASE_ADDR (VCAP_BASE + 0x4A8)
+#define VCAP_VP_T1_C_BASE_ADDR (VCAP_BASE + 0x4Ac)
+#define VCAP_VP_NR_T2_Y_BASE_ADDR (VCAP_BASE + 0x4B4)
+#define VCAP_VP_NR_T2_C_BASE_ADDR (VCAP_BASE + 0x4B8)
+
+#define VP_PIC_DONE (0x1 << 0)
+
+irqreturn_t vp_handler(struct vcap_dev *dev);
+int config_vp_format(struct vcap_client_data *c_data);
+void vp_stop_capture(struct vcap_client_data *c_data);
+int init_motion_buf(struct vcap_client_data *c_data);
+void deinit_motion_buf(struct vcap_client_data *c_data);
+int init_nr_buf(struct vcap_client_data *c_data);
+void deinit_nr_buf(struct vcap_client_data *c_data);
+int kickoff_vp(struct vcap_client_data *c_data);
+int continue_vp(struct vcap_client_data *c_data);
+
+#endif
diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c
index de4fa4e..ac48afd 100644
--- a/drivers/media/video/videobuf-core.c
+++ b/drivers/media/video/videobuf-core.c
@@ -330,6 +330,7 @@
 		break;
 	case V4L2_MEMORY_USERPTR:
 		b->m.userptr = vb->baddr;
+		b->reserved = vb->boff;
 		b->length    = vb->bsize;
 		break;
 	case V4L2_MEMORY_OVERLAY:
@@ -600,6 +601,7 @@
 		    buf->baddr != b->m.userptr)
 			q->ops->buf_release(q, buf);
 		buf->baddr = b->m.userptr;
+		buf->boff = b->reserved;
 		break;
 	case V4L2_MEMORY_OVERLAY:
 		buf->boff = b->m.offset;
@@ -1138,8 +1140,6 @@
 			buf = list_entry(q->stream.next,
 					 struct videobuf_buffer, stream);
 	} else {
-		if (!q->reading)
-			__videobuf_read_start(q);
 		if (!q->reading) {
 			rc = POLLERR;
 		} else if (NULL == q->read_buf) {
diff --git a/drivers/media/video/videobuf-msm-mem.c b/drivers/media/video/videobuf-msm-mem.c
new file mode 100644
index 0000000..5646f9f
--- /dev/null
+++ b/drivers/media/video/videobuf-msm-mem.c
@@ -0,0 +1,396 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * Based on videobuf-dma-contig.c,
+ * (c) 2008 Magnus Damm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * helper functions for physically contiguous pmem capture buffers
+ * The functions support contiguous memory allocations using pmem
+ * kernel API.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/sched.h>
+#include <linux/io.h>
+#include <linux/android_pmem.h>
+#include <linux/memory_alloc.h>
+#include <media/videobuf-msm-mem.h>
+#include <media/msm_camera.h>
+#include <mach/memory.h>
+
+#define MAGIC_PMEM 0x0733ac64
+#define MAGIC_CHECK(is, should)               \
+	if (unlikely((is) != (should))) {           \
+		pr_err("magic mismatch: %x expected %x\n", (is), (should)); \
+		BUG();                  \
+	}
+
+#ifdef CONFIG_MSM_CAMERA_DEBUG
+#define D(fmt, args...) printk(KERN_DEBUG "videobuf-msm-mem: " fmt, ##args)
+#else
+#define D(fmt, args...) do {} while (0)
+#endif
+
+static int32_t msm_mem_allocate(const size_t size)
+{
+	int32_t phyaddr;
+	phyaddr = allocate_contiguous_ebi_nomap(size, SZ_4K);
+	return phyaddr;
+}
+
+static int32_t msm_mem_free(const int32_t phyaddr)
+{
+	int32_t rc = 0;
+	free_contiguous_memory_by_paddr(phyaddr);
+	return rc;
+}
+
+static void
+videobuf_vm_open(struct vm_area_struct *vma)
+{
+	struct videobuf_mapping *map = vma->vm_private_data;
+
+	D("vm_open %p [count=%u,vma=%08lx-%08lx]\n",
+		map, map->count, vma->vm_start, vma->vm_end);
+
+	map->count++;
+}
+
+static void videobuf_vm_close(struct vm_area_struct *vma)
+{
+	struct videobuf_mapping *map = vma->vm_private_data;
+	struct videobuf_queue *q = map->q;
+	int i, rc;
+
+	D("vm_close %p [count=%u,vma=%08lx-%08lx]\n",
+		map, map->count, vma->vm_start, vma->vm_end);
+
+	map->count--;
+	if (0 == map->count) {
+		struct videobuf_contig_pmem *mem;
+
+		D("munmap %p q=%p\n", map, q);
+		mutex_lock(&q->vb_lock);
+
+		/* We need first to cancel streams, before unmapping */
+		if (q->streaming)
+			videobuf_queue_cancel(q);
+
+		for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+			if (NULL == q->bufs[i])
+				continue;
+
+			if (q->bufs[i]->map != map)
+				continue;
+
+			mem = q->bufs[i]->priv;
+			if (mem) {
+				/* This callback is called only if kernel has
+				 * allocated memory and this memory is mmapped.
+				 * In this case, memory should be freed,
+				 * in order to do memory unmap.
+				 */
+
+				MAGIC_CHECK(mem->magic, MAGIC_PMEM);
+
+				/* vfree is not atomic - can't be
+				 called with IRQ's disabled
+				 */
+				D("buf[%d] freeing physical %d\n",
+					i, mem->phyaddr);
+
+				rc = msm_mem_free(mem->phyaddr);
+				if (rc < 0)
+					D("%s: Invalid memory location\n",
+								__func__);
+				else {
+					mem->phyaddr = 0;
+				}
+			}
+
+			q->bufs[i]->map   = NULL;
+			q->bufs[i]->baddr = 0;
+		}
+
+		kfree(map);
+
+		mutex_unlock(&q->vb_lock);
+
+		/* deallocate the q->bufs[i] structure not a good solution
+		 as it will result in unnecessary iterations but right now
+		 this looks like the only cleaner way  */
+		videobuf_mmap_free(q);
+	}
+}
+
+static const struct vm_operations_struct videobuf_vm_ops = {
+	.open     = videobuf_vm_open,
+	.close    = videobuf_vm_close,
+};
+
+/**
+ * videobuf_pmem_contig_user_put() - reset pointer to user space buffer
+ * @mem: per-buffer private videobuf-contig-pmem data
+ *
+ * This function resets the user space pointer
+ */
+static void videobuf_pmem_contig_user_put(struct videobuf_contig_pmem *mem)
+{
+	if (mem->phyaddr) {
+		put_pmem_file(mem->file);
+		mem->is_userptr = 0;
+		mem->phyaddr = 0;
+		mem->size = 0;
+	}
+}
+
+/**
+ * videobuf_pmem_contig_user_get() - setup user space memory pointer
+ * @mem: per-buffer private videobuf-contig-pmem data
+ * @vb: video buffer to map
+ *
+ * This function validates and sets up a pointer to user space memory.
+ * Only physically contiguous pfn-mapped memory is accepted.
+ *
+ * Returns 0 if successful.
+ */
+static int videobuf_pmem_contig_user_get(struct videobuf_contig_pmem *mem,
+					struct videobuf_buffer *vb)
+{
+	unsigned long kvstart;
+	unsigned long len;
+	int rc;
+
+	mem->size = PAGE_ALIGN(vb->size);
+	rc = get_pmem_file(vb->baddr, (unsigned long *)&mem->phyaddr,
+					&kvstart, &len, &mem->file);
+	if (rc < 0) {
+		pr_err("%s: get_pmem_file fd %lu error %d\n",
+					__func__, vb->baddr,
+							rc);
+		return rc;
+	}
+	mem->phyaddr += vb->boff;
+	mem->y_off = 0;
+	mem->cbcr_off = (vb->size)*2/3;
+	mem->is_userptr = 1;
+	return rc;
+}
+
+static struct videobuf_buffer *__videobuf_alloc(size_t size)
+{
+	struct videobuf_contig_pmem *mem;
+	struct videobuf_buffer *vb;
+
+	vb = kzalloc(size + sizeof(*mem), GFP_KERNEL);
+	if (vb) {
+		mem = vb->priv = ((char *)vb) + size;
+		mem->magic = MAGIC_PMEM;
+	}
+
+	return vb;
+}
+
+static void *__videobuf_to_vaddr(struct videobuf_buffer *buf)
+{
+	struct videobuf_contig_pmem *mem = buf->priv;
+
+	BUG_ON(!mem);
+	MAGIC_CHECK(mem->magic, MAGIC_PMEM);
+
+	return mem->vaddr;
+}
+
+static int __videobuf_iolock(struct videobuf_queue *q,
+				struct videobuf_buffer *vb,
+				struct v4l2_framebuffer *fbuf)
+{
+	int rc = 0;
+	struct videobuf_contig_pmem *mem = vb->priv;
+
+	BUG_ON(!mem);
+	MAGIC_CHECK(mem->magic, MAGIC_PMEM);
+
+	switch (vb->memory) {
+	case V4L2_MEMORY_MMAP:
+		D("%s memory method MMAP\n", __func__);
+
+		/* All handling should be done by __videobuf_mmap_mapper() */
+		break;
+	case V4L2_MEMORY_USERPTR:
+		D("%s memory method USERPTR\n", __func__);
+
+		/* handle pointer from user space */
+		rc = videobuf_pmem_contig_user_get(mem, vb);
+		break;
+	case V4L2_MEMORY_OVERLAY:
+	default:
+		pr_err("%s memory method OVERLAY/unknown\n", __func__);
+		rc = -EINVAL;
+	}
+
+	return rc;
+}
+
+static int __videobuf_mmap_mapper(struct videobuf_queue *q,
+		struct videobuf_buffer *buf,
+		struct vm_area_struct *vma)
+{
+	struct videobuf_contig_pmem *mem;
+	struct videobuf_mapping *map;
+	int retval;
+	unsigned long size;
+
+	D("%s\n", __func__);
+
+	/* create mapping + update buffer list */
+	map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL);
+	if (!map) {
+		pr_err("%s: kzalloc failed.\n", __func__);
+		return -ENOMEM;
+	}
+
+	buf->map = map;
+	map->q = q;
+
+	buf->baddr = vma->vm_start;
+
+	mem = buf->priv;
+	D("mem = 0x%x\n", (u32)mem);
+	D("buf = 0x%x\n", (u32)buf);
+	BUG_ON(!mem);
+	MAGIC_CHECK(mem->magic, MAGIC_PMEM);
+
+	mem->size = PAGE_ALIGN(buf->bsize);
+	mem->y_off = 0;
+	mem->cbcr_off = (buf->bsize)*2/3;
+	if (buf->i >= 0 && buf->i <= 3)
+		mem->buffer_type = OUTPUT_TYPE_P;
+	else
+		mem->buffer_type = OUTPUT_TYPE_V;
+
+	buf->bsize = mem->size;
+	mem->phyaddr = msm_mem_allocate(mem->size);
+
+	if (!mem->phyaddr) {
+		pr_err("%s : pmem memory allocation failed\n", __func__);
+		goto error;
+	}
+
+	/* Try to remap memory */
+	size = vma->vm_end - vma->vm_start;
+	size = (size < mem->size) ? size : mem->size;
+
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+	retval = remap_pfn_range(vma, vma->vm_start,
+		mem->phyaddr >> PAGE_SHIFT,
+		size, vma->vm_page_prot);
+	if (retval) {
+		pr_err("mmap: remap failed with error %d. ", retval);
+		retval = msm_mem_free(mem->phyaddr);
+		if (retval < 0)
+			printk(KERN_ERR "%s: Invalid memory location\n",
+								__func__);
+		else {
+			mem->phyaddr = 0;
+		}
+		goto error;
+	}
+
+	vma->vm_ops          = &videobuf_vm_ops;
+	vma->vm_flags       |= VM_DONTEXPAND;
+	vma->vm_private_data = map;
+
+	D("mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n",
+		map, q, vma->vm_start, vma->vm_end,
+		(long int)buf->bsize,
+		vma->vm_pgoff, buf->i);
+
+	videobuf_vm_open(vma);
+
+	return 0;
+
+error:
+	kfree(map);
+	return -ENOMEM;
+}
+
+static struct videobuf_qtype_ops qops = {
+	.magic        = MAGIC_QTYPE_OPS,
+
+	.alloc_vb     = __videobuf_alloc,
+	.iolock       = __videobuf_iolock,
+	.mmap_mapper  = __videobuf_mmap_mapper,
+	.vaddr        = __videobuf_to_vaddr,
+};
+
+void videobuf_queue_pmem_contig_init(struct videobuf_queue *q,
+	const struct videobuf_queue_ops *ops,
+	struct device *dev,
+	spinlock_t *irqlock,
+	enum v4l2_buf_type type,
+	enum v4l2_field field,
+	unsigned int msize,
+	void *priv,
+	struct mutex *ext_lock)
+{
+	videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
+							priv, &qops, ext_lock);
+}
+EXPORT_SYMBOL_GPL(videobuf_queue_pmem_contig_init);
+
+int videobuf_to_pmem_contig(struct videobuf_buffer *buf)
+{
+	struct videobuf_contig_pmem *mem = buf->priv;
+
+	BUG_ON(!mem);
+	MAGIC_CHECK(mem->magic, MAGIC_PMEM);
+
+	return mem->phyaddr;
+}
+EXPORT_SYMBOL_GPL(videobuf_to_pmem_contig);
+
+int videobuf_pmem_contig_free(struct videobuf_queue *q,
+				struct videobuf_buffer *buf)
+{
+	struct videobuf_contig_pmem *mem = buf->priv;
+
+	/* mmapped memory can't be freed here, otherwise mmapped region
+	 would be released, while still needed. In this case, the memory
+	 release should happen inside videobuf_vm_close().
+	 So, it should free memory only if the memory were allocated for
+	 read() operation.
+	*/
+	if (buf->memory != V4L2_MEMORY_USERPTR)
+		return -EINVAL;
+
+	if (!mem)
+		return -ENOMEM;
+
+	MAGIC_CHECK(mem->magic, MAGIC_PMEM);
+
+	/* handle user space pointer case */
+	if (buf->baddr) {
+		videobuf_pmem_contig_user_put(mem);
+		return 0;
+	} else {
+		/* don't support read() method */
+		return -EINVAL;
+	}
+}
+EXPORT_SYMBOL_GPL(videobuf_pmem_contig_free);
+
+MODULE_DESCRIPTION("helper module to manage video4linux PMEM contig buffers");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c
index 2e8f1df..2ec872b 100644
--- a/drivers/media/video/videobuf2-core.c
+++ b/drivers/media/video/videobuf2-core.c
@@ -513,6 +513,13 @@
 		return -EINVAL;
 	}
 
+	/*
+	 * If the same number of buffers and memory access method is requested
+	 * then return immediately.
+	 */
+	if (q->memory == req->memory && req->count == q->num_buffers)
+		return 0;
+
 	if (req->count == 0 || q->num_buffers != 0 || q->memory != req->memory) {
 		/*
 		 * We already have buffers allocated, so first check if they
diff --git a/drivers/media/video/videobuf2-msm-mem.c b/drivers/media/video/videobuf2-msm-mem.c
new file mode 100644
index 0000000..186195d
--- /dev/null
+++ b/drivers/media/video/videobuf2-msm-mem.c
@@ -0,0 +1,351 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * Based on videobuf-dma-contig.c,
+ * (c) 2008 Magnus Damm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * helper functions for physically contiguous pmem capture buffers
+ * The functions support contiguous memory allocations using pmem
+ * kernel API.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/sched.h>
+#include <linux/io.h>
+#include <linux/android_pmem.h>
+#include <linux/memory_alloc.h>
+#include <media/videobuf2-msm-mem.h>
+#include <media/msm_camera.h>
+#include <mach/memory.h>
+#include <media/videobuf2-core.h>
+#include <mach/iommu_domains.h>
+
+#define MAGIC_PMEM 0x0733ac64
+#define MAGIC_CHECK(is, should)               \
+	if (unlikely((is) != (should))) {           \
+		pr_err("magic mismatch: %x expected %x\n", (is), (should)); \
+		BUG();                  \
+	}
+
+#ifdef CONFIG_MSM_CAMERA_DEBUG
+#define D(fmt, args...) pr_debug("videobuf-msm-mem: " fmt, ##args)
+#else
+#define D(fmt, args...) do {} while (0)
+#endif
+
+static unsigned long msm_mem_allocate(struct videobuf2_contig_pmem *mem)
+{
+	unsigned long phyaddr;
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+	int rc, len;
+	mem->client = msm_ion_client_create(-1, "camera");
+	if (IS_ERR((void *)mem->client)) {
+		pr_err("%s Could not create client\n", __func__);
+		goto client_failed;
+	}
+	mem->ion_handle = ion_alloc(mem->client, mem->size, SZ_4K,
+		(0x1 << ION_CP_MM_HEAP_ID | 0x1 << ION_IOMMU_HEAP_ID));
+	if (IS_ERR((void *)mem->ion_handle)) {
+		pr_err("%s Could not allocate\n", __func__);
+		goto alloc_failed;
+	}
+	rc = ion_map_iommu(mem->client, mem->ion_handle,
+			CAMERA_DOMAIN, GEN_POOL, SZ_4K, 0,
+			(unsigned long *)&phyaddr,
+			(unsigned long *)&len, UNCACHED, 0);
+	if (rc < 0) {
+		pr_err("%s Could not get physical address\n", __func__);
+		goto phys_failed;
+	}
+#else
+	phyaddr = allocate_contiguous_ebi_nomap(mem->size, SZ_4K);
+#endif
+	return phyaddr;
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+phys_failed:
+	ion_free(mem->client, mem->ion_handle);
+alloc_failed:
+	ion_client_destroy(mem->client);
+client_failed:
+	return 0;
+#endif
+}
+
+static int32_t msm_mem_free(struct videobuf2_contig_pmem *mem)
+{
+	int32_t rc = 0;
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+	ion_unmap_iommu(mem->client, mem->ion_handle, CAMERA_DOMAIN, GEN_POOL);
+	ion_free(mem->client, mem->ion_handle);
+	ion_client_destroy(mem->client);
+#else
+	free_contiguous_memory_by_paddr(mem->phyaddr);
+#endif
+	return rc;
+}
+
+static void videobuf2_vm_close(struct vm_area_struct *vma)
+{
+	struct videobuf2_contig_pmem *mem = vma->vm_private_data;
+	D("vm_close %p [count=%u,vma=%08lx-%08lx]\n",
+		mem, mem->count, vma->vm_start, vma->vm_end);
+	mem->count--;
+}
+static void videobuf2_vm_open(struct vm_area_struct *vma)
+{
+	struct videobuf2_contig_pmem *mem = vma->vm_private_data;
+	D("vm_open %p [count=%u,vma=%08lx-%08lx]\n",
+		mem, mem->count, vma->vm_start, vma->vm_end);
+	mem->count++;
+}
+
+static const struct vm_operations_struct videobuf2_vm_ops = {
+	.open     = videobuf2_vm_open,
+	.close    = videobuf2_vm_close,
+};
+
+static void *msm_vb2_mem_ops_alloc(void *alloc_ctx, unsigned long size)
+{
+	struct videobuf2_contig_pmem *mem;
+	mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+	if (!mem)
+		return ERR_PTR(-ENOMEM);
+
+	mem->magic = MAGIC_PMEM;
+	mem->size =  PAGE_ALIGN(size);
+	mem->alloc_ctx = alloc_ctx;
+	mem->is_userptr = 0;
+	mem->phyaddr = msm_mem_allocate(mem);
+	if (!mem->phyaddr) {
+		pr_err("%s : pmem memory allocation failed\n", __func__);
+		kfree(mem);
+		return ERR_PTR(-ENOMEM);
+	}
+	mem->mapped_phyaddr = mem->phyaddr;
+	return mem;
+}
+static void msm_vb2_mem_ops_put(void *buf_priv)
+{
+	struct videobuf2_contig_pmem *mem = buf_priv;
+	if (!mem->is_userptr) {
+		D("%s Freeing memory ", __func__);
+		msm_mem_free(mem);
+	}
+	kfree(mem);
+}
+int videobuf2_pmem_contig_mmap_get(struct videobuf2_contig_pmem *mem,
+				struct videobuf2_msm_offset *offset,
+				enum videobuf2_buffer_type buffer_type,
+				int path)
+{
+	if (offset)
+		mem->offset = *offset;
+	else
+		memset(&mem->offset, 0, sizeof(struct videobuf2_msm_offset));
+	mem->buffer_type = buffer_type;
+	mem->path = path;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(videobuf2_pmem_contig_mmap_get);
+
+/**
+ * videobuf_pmem_contig_user_get() - setup user space memory pointer
+ * @mem: per-buffer private videobuf-contig-pmem data
+ * @vb: video buffer to map
+ *
+ * This function validates and sets up a pointer to user space memory.
+ * Only physically contiguous pfn-mapped memory is accepted.
+ *
+ * Returns 0 if successful.
+ */
+int videobuf2_pmem_contig_user_get(struct videobuf2_contig_pmem *mem,
+					struct videobuf2_msm_offset *offset,
+					enum videobuf2_buffer_type buffer_type,
+					uint32_t addr_offset, int path,
+					struct ion_client *client)
+{
+	unsigned long len;
+	int rc = 0;
+#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION
+	unsigned long kvstart;
+#endif
+	unsigned long paddr = 0;
+	if (mem->phyaddr != 0)
+		return 0;
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+	mem->ion_handle = ion_import_fd(client, (int)mem->vaddr);
+	if (IS_ERR_OR_NULL(mem->ion_handle)) {
+		pr_err("%s ION import failed\n", __func__);
+		return PTR_ERR(mem->ion_handle);
+	}
+	rc = ion_map_iommu(client, mem->ion_handle, CAMERA_DOMAIN, GEN_POOL,
+		SZ_4K, 0, (unsigned long *)&mem->phyaddr, &len, UNCACHED, 0);
+	if (rc < 0)
+		ion_free(client, mem->ion_handle);
+#elif CONFIG_ANDROID_PMEM
+	rc = get_pmem_file((int)mem->vaddr, (unsigned long *)&mem->phyaddr,
+					&kvstart, &len, &mem->file);
+	if (rc < 0) {
+		pr_err("%s: get_pmem_file fd %d error %d\n",
+					__func__, (int)mem->vaddr, rc);
+		return rc;
+	}
+#else
+	paddr = 0;
+	kvstart = 0;
+#endif
+	if (offset)
+		mem->offset = *offset;
+	else
+		memset(&mem->offset, 0, sizeof(struct videobuf2_msm_offset));
+	mem->path = path;
+	mem->buffer_type = buffer_type;
+	paddr = mem->phyaddr;
+	mem->mapped_phyaddr = paddr + addr_offset;
+	mem->addr_offset = addr_offset;
+	return rc;
+}
+EXPORT_SYMBOL_GPL(videobuf2_pmem_contig_user_get);
+
+void videobuf2_pmem_contig_user_put(struct videobuf2_contig_pmem *mem,
+					struct ion_client *client)
+{
+	if (mem->is_userptr) {
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+		ion_unmap_iommu(client, mem->ion_handle,
+				CAMERA_DOMAIN, GEN_POOL);
+		ion_free(client, mem->ion_handle);
+#elif CONFIG_ANDROID_PMEM
+		put_pmem_file(mem->file);
+#endif
+	}
+	mem->is_userptr = 0;
+	mem->phyaddr = 0;
+	mem->size = 0;
+	mem->mapped_phyaddr = 0;
+}
+EXPORT_SYMBOL_GPL(videobuf2_pmem_contig_user_put);
+
+static void *msm_vb2_mem_ops_get_userptr(void *alloc_ctx, unsigned long vaddr,
+					unsigned long size, int write)
+{
+	struct videobuf2_contig_pmem *mem;
+	mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+	if (!mem)
+		return ERR_PTR(-ENOMEM);
+	mem->magic = MAGIC_PMEM;
+	mem->is_userptr = 1;
+	mem->vaddr = (void *)vaddr;
+	mem->size = size;
+	mem->alloc_ctx = alloc_ctx;
+	return mem;
+}
+static void msm_vb2_mem_ops_put_userptr(void *buf_priv)
+{
+	kfree(buf_priv);
+}
+
+static void *msm_vb2_mem_ops_vaddr(void *buf_priv)
+{
+	struct videobuf2_contig_pmem *mem = buf_priv;
+	return mem->vaddr;
+}
+static void *msm_vb2_mem_ops_cookie(void *buf_priv)
+{
+	return buf_priv;
+}
+static unsigned int msm_vb2_mem_ops_num_users(void *buf_priv)
+{
+	struct videobuf2_contig_pmem *mem = buf_priv;
+	MAGIC_CHECK(mem->magic, MAGIC_PMEM);
+	return mem->count;
+}
+static int msm_vb2_mem_ops_mmap(void *buf_priv, struct vm_area_struct *vma)
+{
+	struct videobuf2_contig_pmem *mem;
+	int retval;
+	unsigned long size;
+	D("%s\n", __func__);
+	mem = buf_priv;
+	D("mem = 0x%x\n", (u32)mem);
+	BUG_ON(!mem);
+	MAGIC_CHECK(mem->magic, MAGIC_PMEM);
+	/* Try to remap memory */
+	size = vma->vm_end - vma->vm_start;
+	size = (size < mem->size) ? size : mem->size;
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+	retval = remap_pfn_range(vma, vma->vm_start,
+			mem->phyaddr >> PAGE_SHIFT,
+			size, vma->vm_page_prot);
+	if (retval) {
+		pr_err("mmap: remap failed with error %d. ", retval);
+		goto error;
+	}
+	mem->vaddr = (void *)vma->vm_start;
+	vma->vm_ops = &videobuf2_vm_ops;
+	vma->vm_flags |= VM_DONTEXPAND;
+	vma->vm_private_data = mem;
+
+	D("mmap %p: %08lx-%08lx (%lx) pgoff %08lx\n",
+		map, vma->vm_start, vma->vm_end,
+		(long int)mem->bsize, vma->vm_pgoff);
+	videobuf2_vm_open(vma);
+	return 0;
+error:
+	return -ENOMEM;
+}
+
+static struct vb2_mem_ops msm_vb2_mem_ops = {
+	.alloc = msm_vb2_mem_ops_alloc,
+	.put = msm_vb2_mem_ops_put,
+	.get_userptr = msm_vb2_mem_ops_get_userptr,
+	.put_userptr = msm_vb2_mem_ops_put_userptr,
+	.vaddr = msm_vb2_mem_ops_vaddr,
+	.cookie = msm_vb2_mem_ops_cookie,
+	.num_users = msm_vb2_mem_ops_num_users,
+	.mmap = msm_vb2_mem_ops_mmap
+};
+
+void videobuf2_queue_pmem_contig_init(struct vb2_queue *q,
+					enum v4l2_buf_type type,
+					const struct vb2_ops *ops,
+					unsigned int size,
+					void *priv)
+{
+	memset(q, 0, sizeof(struct vb2_queue));
+	q->mem_ops = &msm_vb2_mem_ops;
+	q->ops = ops;
+	q->drv_priv = priv;
+	q->type = type;
+	q->io_modes = VB2_MMAP | VB2_USERPTR;
+	q->io_flags = 0;
+	q->buf_struct_size = size;
+	vb2_queue_init(q);
+}
+EXPORT_SYMBOL_GPL(videobuf2_queue_pmem_contig_init);
+
+unsigned long videobuf2_to_pmem_contig(struct vb2_buffer *vb,
+				unsigned int plane_no)
+{
+	struct videobuf2_contig_pmem *mem;
+	mem = vb2_plane_cookie(vb, plane_no);
+	BUG_ON(!mem);
+	MAGIC_CHECK(mem->magic, MAGIC_PMEM);
+	return mem->mapped_phyaddr;
+}
+EXPORT_SYMBOL_GPL(videobuf2_to_pmem_contig);
+
+MODULE_DESCRIPTION("helper module to manage video4linux PMEM contig buffers");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 11e4438..7c61bf3 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -131,6 +131,38 @@
 	  This driver can also be built as a module.  If so, the module
 	  will be called tps65010.
 
+config TPS65023
+        tristate "TPS65023 Power Management chip"
+        depends on I2C && ARCH_MSM_SCORPION && !MSM_SMP
+        default y if I2C && ARCH_MSM_SCORPION && !MSM_SMP
+        help
+          Say yes here for Qualcomm QSD chips. The TI PMIC is used by the
+          QSD8x50 series of chips for power management.
+
+config PMIC8058
+	tristate "PMIC8058 Power Management chip"
+	depends on MSM_SSBI
+	select MFD_CORE
+	select MFD_PM8XXX
+	help
+	  Say yes here for Qualcomm PM8058 chip.
+
+config PMIC8901
+	tristate "PMIC8901 Power Management chip"
+	depends on MSM_SSBI
+	select MFD_CORE
+	select MFD_PM8XXX
+	help
+	  Say yes here for Qualcomm PM8901 chip.
+
+config MARIMBA_TSADC
+	tristate "Support for Marimba Touchscreen ADC"
+	depends on MARIMBA_CORE && ARCH_MSM7X30
+	default y if MARIMBA_CORE
+	help
+	  Say yes here if you want to include support for TSADC in the
+	  Qualcomm Marimba chip.
+
 config TPS6507X
 	tristate "TPS6507x Power Management / Touch Screen chips"
 	select MFD_CORE
@@ -214,6 +246,33 @@
 	  and other features that are often used in portable devices like
 	  cell phones and PDAs.
 
+config MARIMBA_CORE
+	tristate "Marimba Core"
+	depends on I2C && (ARCH_MSM7X30 || ARCH_MSM8X60 || ARCH_MSM7X27A)
+	default n
+	help
+	  Enables the Marimba Core driver. The core driver provides
+	  read/write capability to registers which are part of the
+	  marimba core.
+	  This driver dynamically detects the SoC and works for both
+	  Marimba and Bahama Chip.
+
+config MARIMBA_CODEC
+	tristate "Marimba Codec"
+	depends on MARIMBA_CORE
+	default n
+	help
+	 This driver programs Marimba Wideband Codec for input/output of
+	 audio signal.
+
+config TIMPANI_CODEC
+	tristate "Timpani Codec"
+	depends on MARIMBA_CORE
+	default n
+	help
+	 This driver programs Timpani Wideband Codec for input/output of
+	 audio signal.
+
 config TWL4030_CORE
 	bool "Texas Instruments TWL4030/TWL5030/TWL6030/TPS659x0 Support"
 	depends on I2C=y && GENERIC_HARDIRQS
@@ -822,6 +881,51 @@
 	  Say M here if you want to include support for PM8921 chip as a module.
 	  This will build a module called "pm8921-core".
 
+config MFD_PM8821_CORE
+	tristate "Qualcomm PM8821 PMIC chip"
+	depends on MSM_SSBI
+	select MFD_CORE
+	select MFD_PM8XXX
+	help
+	  If you say yes to this option, support will be included for the
+	  built-in PM8821 PMIC chip.
+
+	  This is required if your board has a PM8821 and uses its features,
+	  such as: MPPs, and interrupts.
+
+	  Say M here if you want to include support for PM8821 chip as a module.
+	  This will build a module called "pm8821-core".
+
+config MFD_PM8018_CORE
+	tristate "Qualcomm PM8018 PMIC chip"
+	depends on MSM_SSBI
+	select MFD_CORE
+	select MFD_PM8XXX
+	help
+	  If you say yes to this option, support will be included for the
+	  built-in PM8018 PMIC chip.
+
+	  This is required if your board has a PM8018 and uses its features,
+	  such as: MPPs, GPIOs, regulators, interrupts, and PWM.
+
+	  Say M here if you want to include support for PM8018 chip as a module.
+	  This will build a module called "pm8018-core".
+
+config MFD_PM8038_CORE
+	tristate "Qualcomm PM8038 PMIC chip"
+	depends on MSM_SSBI
+	select MFD_CORE
+	select MFD_PM8XXX
+	help
+	  If you say yes to this option, support will be included for the
+	  built-in PM8038 PMIC chip.
+
+	  This is required if your board has a PM8038 and uses its features,
+	  such as: MPPs, GPIOs, regulators, interrupts, and PWM.
+
+	  Say M here if you want to include support for PM8038 chip as a module.
+	  This will build a module called "pm8038-core".
+
 config MFD_PM8XXX_IRQ
 	bool "Support for Qualcomm PM8xxx IRQ features"
 	depends on MFD_PM8XXX
@@ -866,6 +970,71 @@
 	  Passage) chip. This chip embeds audio, battery, GPIO, etc.
 	  devices used in Intel Medfield platforms.
 
+config MFD_PM8XXX_DEBUG
+	tristate "Qualcomm PM8xxx debugfs support"
+	depends on MFD_PM8XXX && DEBUG_FS
+	default y if MFD_PM8XXX
+	help
+	  This driver provides a debugfs interface to the SSBI registers on
+	  Qualcomm PM 8xxx PMIC chips.  It allows for reads and writes to
+	  arbitrary addresses.  Writes are blocking so values are guaranteed to
+	  be set into hardware registers upon return.
+
+config MFD_PM8XXX_PWM
+	tristate "Support for Qualcomm PM8xxx PWM feature"
+	depends on MFD_PM8XXX
+	default y if MFD_PM8XXX
+	help
+	  This is the Pulse Width Modulation (PWM) driver for Qualcomm
+	  PM 8xxx PMIC chips. It can drive 8 channels of PWM output, and
+	  has a lookup table with size of 64 to be shared by any of the
+	  8 channels.
+
+config MFD_PM8XXX_MISC
+	tristate "Support for Qualcomm PM8xxx miscellaneous APIs"
+	depends on MFD_PM8XXX
+	default y if MFD_PM8XXX
+	help
+	  This driver implements several miscellaneous APIs that may be needed
+	  in order to control the PM8XXX PMIC chip.
+
+config MFD_PM8XXX_SPK
+	tristate "Support for Qualcomm PM8xxx speaker APIs"
+	depends on MFD_PM8XXX
+	help
+	  This driver implements several external speaker amplifier APIs that
+	  may be needed in order to control the PM8XXX PMIC chip.
+
+config MFD_PM8XXX_BATT_ALARM
+	tristate "Support for Qualcomm PM8xxx battery voltage alarm"
+	depends on MFD_PM8XXX
+	help
+	  This driver provides a means monitor battery under and over-voltage
+	  conditions.  An upper and/or lower threshold can be specified for
+	  normal operation.  A wakeable interrupt is triggered when the battery
+	  voltage leaves the accepatable range which then calls a notifier call
+	  chain.
+
+config WCD9304_CODEC
+        tristate "WCD9304 Codec"
+        select SLIMBUS
+        select MFD_CORE
+        default n
+        help
+          Enables the WCD9304 core driver. The core driver provides
+          read/write capability to registers which are part of the
+          WCD9304 core and gives the ability to use the WCD9304 codec.
+
+config WCD9310_CODEC
+        tristate "WCD9310 Codec"
+        select SLIMBUS
+        select MFD_CORE
+        default n
+        help
+          Enables the WCD9310 core driver. The core driver provides
+          read/write capability to registers which are part of the
+          WCD9310 core and gives the ability to use the WCD9310 codec.
+
 config MFD_RC5T583
 	bool "Ricoh RC5T583 Power Management system device"
 	depends on I2C=y && GENERIC_HARDIRQS
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 05fa538..e81e4b1 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -44,12 +44,26 @@
 obj-$(CONFIG_MFD_TPS65912)	+= tps65912.o
 obj-$(CONFIG_MFD_TPS65912_I2C)	+= tps65912-i2c.o
 obj-$(CONFIG_MFD_TPS65912_SPI)  += tps65912-spi.o
+obj-$(CONFIG_MARIMBA_CODEC)     += marimba-codec.o
 obj-$(CONFIG_MENELAUS)		+= menelaus.o
 
 obj-$(CONFIG_TWL4030_CORE)	+= twl-core.o twl4030-irq.o twl6030-irq.o
 obj-$(CONFIG_TWL4030_MADC)      += twl4030-madc.o
 obj-$(CONFIG_TWL4030_POWER)    += twl4030-power.o
 obj-$(CONFIG_MFD_TWL4030_AUDIO)	+= twl4030-audio.o
+
+obj-$(CONFIG_MARIMBA_CORE)	+= marimba-core.o
+obj-$(CONFIG_MARIMBA_TSADC)	+= marimba-tsadc.o
+obj-$(CONFIG_TPS65023)		+= tps65023.o
+
+obj-$(CONFIG_TIMPANI_CODEC)	+= timpani-codec.o
+
+ifdef CONFIG_TIMPANI_CODEC
+obj-$(CONFIG_TIMPANI_CODEC) += msm-adie-codec.o
+else ifdef CONFIG_MARIMBA_CODEC
+obj-$(CONFIG_MARIMBA_CODEC) += msm-adie-codec.o
+endif
+
 obj-$(CONFIG_TWL6030_PWM)	+= twl6030-pwm.o
 obj-$(CONFIG_TWL6040_CORE)	+= twl6040-core.o twl6040-irq.o
 
@@ -64,6 +78,9 @@
 obj-$(CONFIG_MCP_UCB1200)	+= ucb1x00-core.o
 obj-$(CONFIG_MCP_UCB1200_TS)	+= ucb1x00-ts.o
 
+obj-$(CONFIG_WCD9310_CODEC)       += wcd9xxx-core.o wcd9xxx-irq.o wcd9xxx-slimslave.o
+obj-$(CONFIG_WCD9304_CODEC)       += wcd9xxx-core.o wcd9xxx-irq.o wcd9xxx-slimslave.o
+
 ifeq ($(CONFIG_SA1100_ASSABET),y)
 obj-$(CONFIG_MCP_UCB1200)	+= ucb1x00-assabet.o
 endif
@@ -107,12 +124,22 @@
 obj-$(CONFIG_MFD_WL1273_CORE)	+= wl1273-core.o
 obj-$(CONFIG_MFD_CS5535)	+= cs5535-mfd.o
 obj-$(CONFIG_MFD_OMAP_USB_HOST)	+= omap-usb-host.o
-obj-$(CONFIG_MFD_PM8921_CORE) 	+= pm8921-core.o
-obj-$(CONFIG_MFD_PM8XXX_IRQ) 	+= pm8xxx-irq.o
 obj-$(CONFIG_TPS65911_COMPARATOR)	+= tps65911-comparator.o
 obj-$(CONFIG_MFD_TPS65090)	+= tps65090.o
 obj-$(CONFIG_MFD_AAT2870_CORE)	+= aat2870-core.o
 obj-$(CONFIG_MFD_INTEL_MSIC)	+= intel_msic.o
 obj-$(CONFIG_MFD_RC5T583)	+= rc5t583.o rc5t583-irq.o
 obj-$(CONFIG_MFD_S5M_CORE)	+= s5m-core.o s5m-irq.o
+obj-$(CONFIG_PMIC8058)		+= pmic8058.o
+obj-$(CONFIG_PMIC8901)		+= pmic8901.o
+obj-$(CONFIG_MFD_PM8921_CORE) 	+= pm8921-core.o
+obj-$(CONFIG_MFD_PM8821_CORE) 	+= pm8821-core.o
+obj-$(CONFIG_MFD_PM8018_CORE) 	+= pm8018-core.o
+obj-$(CONFIG_MFD_PM8038_CORE) 	+= pm8038-core.o
+obj-$(CONFIG_MFD_PM8XXX_IRQ) 	+= pm8xxx-irq.o
+obj-$(CONFIG_MFD_PM8XXX_DEBUG) 	+= pm8xxx-debug.o
+obj-$(CONFIG_MFD_PM8XXX_PWM) 	+= pm8xxx-pwm.o
+obj-$(CONFIG_MFD_PM8XXX_MISC) 	+= pm8xxx-misc.o
+obj-$(CONFIG_MFD_PM8XXX_SPK) 	+= pm8xxx-spk.o
+obj-$(CONFIG_MFD_PM8XXX_BATT_ALARM) 	+= pm8xxx-batt-alarm.o
 obj-$(CONFIG_MFD_ANATOP)	+= anatop-mfd.o
diff --git a/drivers/mfd/marimba-codec.c b/drivers/mfd/marimba-codec.c
new file mode 100644
index 0000000..6416e0a
--- /dev/null
+++ b/drivers/mfd/marimba-codec.c
@@ -0,0 +1,963 @@
+/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/msm-adie-codec.h>
+#include <linux/mfd/marimba.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/string.h>
+
+#define MARIMBA_CDC_RX_CTL 0x81
+#define MARIMBA_CDC_RX_CTL_ST_EN_MASK 0x20
+#define MARIMBA_CDC_RX_CTL_ST_EN_SHFT 0x5
+#define MARIMBA_CODEC_CDC_LRXG     0x84
+#define MARIMBA_CODEC_CDC_RRXG     0x85
+#define MARIMBA_CODEC_CDC_LTXG     0x86
+#define MARIMBA_CODEC_CDC_RTXG     0x87
+
+#define MAX_MDELAY_US 2000
+#define MIN_MDELAY_US 1000
+
+struct adie_codec_path {
+	struct adie_codec_dev_profile *profile;
+	struct adie_codec_register_image img;
+	u32 hwsetting_idx;
+	u32 stage_idx;
+	u32 curr_stage;
+};
+
+static struct adie_codec_register adie_codec_tx_regs[] = {
+	{ 0x04, 0xc0, 0x8C },
+	{ 0x0D, 0xFF, 0x00 },
+	{ 0x0E, 0xFF, 0x00 },
+	{ 0x0F, 0xFF, 0x00 },
+	{ 0x10, 0xF8, 0x68 },
+	{ 0x11, 0xFE, 0x00 },
+	{ 0x12, 0xFE, 0x00 },
+	{ 0x13, 0xFF, 0x58 },
+	{ 0x14, 0xFF, 0x00 },
+	{ 0x15, 0xFE, 0x00 },
+	{ 0x16, 0xFF, 0x00 },
+	{ 0x1A, 0xFF, 0x00 },
+	{ 0x80, 0x01, 0x00 },
+	{ 0x82, 0x7F, 0x18 },
+	{ 0x83, 0x1C, 0x00 },
+	{ 0x86, 0xFF, 0xAC },
+	{ 0x87, 0xFF, 0xAC },
+	{ 0x89, 0xFF, 0xFF },
+	{ 0x8A, 0xF0, 0x30 }
+};
+
+static struct adie_codec_register adie_codec_rx_regs[] = {
+	{ 0x23, 0xF8, 0x00 },
+	{ 0x24, 0x6F, 0x00 },
+	{ 0x25, 0x7F, 0x00 },
+	{ 0x26, 0xFC, 0x00 },
+	{ 0x28, 0xFE, 0x00 },
+	{ 0x29, 0xFE, 0x00 },
+	{ 0x33, 0xFF, 0x00 },
+	{ 0x34, 0xFF, 0x00 },
+	{ 0x35, 0xFC, 0x00 },
+	{ 0x36, 0xFE, 0x00 },
+	{ 0x37, 0xFE, 0x00 },
+	{ 0x38, 0xFE, 0x00 },
+	{ 0x39, 0xF0, 0x00 },
+	{ 0x3A, 0xFF, 0x0A },
+	{ 0x3B, 0xFC, 0xAC },
+	{ 0x3C, 0xFC, 0xAC },
+	{ 0x3D, 0xFF, 0x55 },
+	{ 0x3E, 0xFF, 0x55 },
+	{ 0x3F, 0xCF, 0x00 },
+	{ 0x40, 0x3F, 0x00 },
+	{ 0x41, 0x3F, 0x00 },
+	{ 0x42, 0xFF, 0x00 },
+	{ 0x43, 0xF7, 0x00 },
+	{ 0x43, 0xF7, 0x00 },
+	{ 0x43, 0xF7, 0x00 },
+	{ 0x43, 0xF7, 0x00 },
+	{ 0x44, 0xF7, 0x00 },
+	{ 0x45, 0xFF, 0x00 },
+	{ 0x46, 0xFF, 0x00 },
+	{ 0x47, 0xF7, 0x00 },
+	{ 0x48, 0xF7, 0x00 },
+	{ 0x49, 0xFF, 0x00 },
+	{ 0x4A, 0xFF, 0x00 },
+	{ 0x80, 0x02, 0x00 },
+	{ 0x81, 0xFF, 0x4C },
+	{ 0x83, 0x23, 0x00 },
+	{ 0x84, 0xFF, 0xAC },
+	{ 0x85, 0xFF, 0xAC },
+	{ 0x88, 0xFF, 0xFF },
+	{ 0x8A, 0x0F, 0x03 },
+	{ 0x8B, 0xFF, 0xAC },
+	{ 0x8C, 0x03, 0x01 },
+	{ 0x8D, 0xFF, 0x00 },
+	{ 0x8E, 0xFF, 0x00 }
+};
+
+static struct adie_codec_register adie_codec_lb_regs[] = {
+	{ 0x2B, 0x8F, 0x02 },
+	{ 0x2C, 0x8F, 0x02 }
+};
+
+struct adie_codec_state {
+	struct adie_codec_path path[ADIE_CODEC_MAX];
+	u32 ref_cnt;
+	struct marimba *pdrv_ptr;
+	struct marimba_codec_platform_data *codec_pdata;
+	struct mutex lock;
+};
+
+static struct adie_codec_state adie_codec;
+
+/* Array containing write details of Tx and RX Digital Volume
+   Tx and Rx and both the left and right channel use the same data
+*/
+u8 adie_codec_rx_tx_dig_vol_data[] = {
+	0x81, 0x82, 0x83, 0x84,
+	0x85, 0x86, 0x87, 0x88,
+	0x89, 0x8a, 0x8b, 0x8c,
+	0x8d, 0x8e, 0x8f, 0x90,
+	0x91, 0x92, 0x93, 0x94,
+	0x95, 0x96, 0x97, 0x98,
+	0x99, 0x9a, 0x9b, 0x9c,
+	0x9d, 0x9e, 0x9f, 0xa0,
+	0xa1, 0xa2, 0xa3, 0xa4,
+	0xa5, 0xa6, 0xa7, 0xa8,
+	0xa9, 0xaa, 0xab, 0xac,
+	0xad, 0xae, 0xaf, 0xb0,
+	0xb1, 0xb2, 0xb3, 0xb4,
+	0xb5, 0xb6, 0xb7, 0xb8,
+	0xb9, 0xba, 0xbb, 0xbc,
+	0xbd, 0xbe, 0xbf, 0xc0,
+	0xc1, 0xc2, 0xc3, 0xc4,
+	0xc5, 0xc6, 0xc7, 0xc8,
+	0xc9, 0xca, 0xcb, 0xcc,
+	0xcd, 0xce, 0xcf, 0xd0,
+	0xd1, 0xd2, 0xd3, 0xd4,
+	0xd5, 0xd6, 0xd7, 0xd8,
+	0xd9, 0xda, 0xdb, 0xdc,
+	0xdd, 0xde, 0xdf, 0xe0,
+	0xe1, 0xe2, 0xe3, 0xe4,
+	0xe5, 0xe6, 0xe7, 0xe8,
+	0xe9, 0xea, 0xeb, 0xec,
+	0xed, 0xee, 0xf0, 0xf1,
+	0xf2, 0xf3, 0xf4, 0xf5,
+	0xf6, 0xf7, 0xf8, 0xf9,
+	0xfa, 0xfb, 0xfc, 0xfd,
+	0xfe, 0xff, 0x00, 0x01,
+	0x02, 0x03, 0x04, 0x05,
+	0x06, 0x07, 0x08, 0x09,
+	0x0a, 0x0b, 0x0c, 0x0d,
+	0x0e, 0x0f, 0x10, 0x11,
+	0x12, 0x13, 0x14, 0x15,
+	0x16, 0x17, 0x18, 0x19,
+	0x1a, 0x1b, 0x1c, 0x1d,
+	0x1e, 0x1f, 0x20, 0x21,
+	0x22, 0x23, 0x24, 0x25,
+	0x26, 0x27, 0x28, 0x29,
+	0x2a, 0x2b, 0x2c, 0x2d,
+	0x2e, 0x2f, 0x30, 0x31,
+	0x32, 0x33, 0x34, 0x35,
+	0x36, 0x37, 0x38, 0x39,
+	0x3a, 0x3b, 0x3c, 0x3d,
+	0x3e, 0x3f, 0x40, 0x41,
+	0x42, 0x43, 0x44, 0x45,
+	0x46, 0x47, 0x48, 0x49,
+	0x4a, 0x4b, 0x4c, 0x4d,
+	0x4e, 0x4f, 0x50, 0x51,
+	0x52, 0x53, 0x54, 0x55,
+	0x56, 0x57, 0x58, 0x59,
+	0x5a, 0x5b, 0x5c, 0x5d,
+	0x5e, 0x5f, 0x60, 0x61,
+	0x62, 0x63, 0x64, 0x65,
+	0x66, 0x67, 0x68, 0x69,
+	0x6a, 0x6b, 0x6c, 0x6d,
+	0x6e, 0x6f, 0x70, 0x71,
+	0x72, 0x73, 0x74, 0x75,
+	0x76, 0x77, 0x78, 0x79,
+	0x7a, 0x7b, 0x7c, 0x7d,
+	0x7e, 0x7f
+};
+
+enum adie_vol_type {
+	ADIE_CODEC_RX_DIG_VOL,
+	ADIE_CODEC_TX_DIG_VOL,
+	ADIE_CODEC_VOL_TYPE_MAX
+};
+
+struct adie_codec_ch_vol_cntrl {
+	u8 codec_reg;
+	u8 codec_mask;
+	u8 *vol_cntrl_data;
+};
+
+struct adie_codec_vol_cntrl_data {
+
+	enum adie_vol_type vol_type;
+
+	/* Jump length used while doing writes in incremental fashion */
+	u32 jump_length;
+	s32 min_mb;		/* Min Db applicable to the vol control */
+	s32 max_mb;		/* Max Db applicable to the vol control */
+	u32 step_in_mb;
+	u32 steps;		/* No of steps allowed for this vol type */
+
+	struct adie_codec_ch_vol_cntrl *ch_vol_cntrl_info;
+};
+
+static struct adie_codec_ch_vol_cntrl adie_codec_rx_vol_cntrl[] = {
+	{MARIMBA_CODEC_CDC_LRXG, 0xff, adie_codec_rx_tx_dig_vol_data},
+	{MARIMBA_CODEC_CDC_RRXG, 0xff, adie_codec_rx_tx_dig_vol_data}
+};
+
+static struct adie_codec_ch_vol_cntrl adie_codec_tx_vol_cntrl[] = {
+	{MARIMBA_CODEC_CDC_LTXG, 0xff, adie_codec_rx_tx_dig_vol_data},
+	{MARIMBA_CODEC_CDC_RTXG, 0xff, adie_codec_rx_tx_dig_vol_data}
+};
+
+static struct adie_codec_vol_cntrl_data adie_codec_vol_cntrl[] = {
+	{ADIE_CODEC_RX_DIG_VOL, 5100, -12700, 12700, 100, 255,
+	 adie_codec_rx_vol_cntrl},
+
+	{ADIE_CODEC_TX_DIG_VOL, 5100, -12700, 12700, 100, 255,
+	 adie_codec_tx_vol_cntrl}
+};
+
+static int adie_codec_write(u8 reg, u8 mask, u8 val)
+{
+	int rc;
+
+	rc = marimba_write_bit_mask(adie_codec.pdrv_ptr, reg,  &val, 1, mask);
+	if (IS_ERR_VALUE(rc)) {
+		pr_err("%s: fail to write reg %x\n", __func__, reg);
+		return -EIO;
+	}
+
+	pr_debug("%s: write reg %x val %x\n", __func__, reg, val);
+
+	return 0;
+}
+
+static int adie_codec_read(u8 reg, u8 *val)
+{
+	return marimba_read(adie_codec.pdrv_ptr, reg, val, 1);
+}
+
+static int adie_codec_read_dig_vol(enum adie_vol_type vol_type, u32 chan_index,
+				   u32 *cur_index)
+{
+	u32 counter;
+	u32 size;
+	u8 reg, mask, cur_val;
+	int rc;
+
+	reg =
+	    adie_codec_vol_cntrl[vol_type].
+	    ch_vol_cntrl_info[chan_index].codec_reg;
+
+	mask =
+	    adie_codec_vol_cntrl[vol_type].
+	    ch_vol_cntrl_info[chan_index].codec_mask;
+
+	rc = marimba_read(adie_codec.pdrv_ptr, reg, &cur_val, 1);
+
+	if (IS_ERR_VALUE(rc)) {
+		pr_err("%s: fail to read reg %x\n", __func__, reg);
+		return -EIO;
+	}
+
+	cur_val = cur_val & mask;
+
+	pr_debug("%s: reg 0x%x  mask 0x%x  reg_value = 0x%x"
+		"vol_type = %d\n", __func__, reg, mask, cur_val, vol_type);
+
+	size = adie_codec_vol_cntrl[vol_type].steps;
+
+	for (counter = 0; counter <= size; counter++) {
+
+		if (adie_codec_vol_cntrl[vol_type].ch_vol_cntrl_info
+		    [chan_index].vol_cntrl_data[counter] == cur_val) {
+			*cur_index = counter;
+			return 0;
+		}
+	}
+
+	pr_err("%s: could not find 0x%x in reg 0x%x values array\n",
+			__func__, cur_val, reg);
+
+	return -EINVAL;;
+}
+
+static int adie_codec_set_dig_vol(enum adie_vol_type vol_type, u32 chan_index,
+				  u32 cur_index, u32 target_index)
+{
+	u32 count;
+	u8 reg, mask, val;
+	u32 i;
+	u32 index;
+	u32 index_jump;
+
+	int rc;
+
+	index_jump = adie_codec_vol_cntrl[vol_type].jump_length;
+
+	reg =
+	    adie_codec_vol_cntrl[vol_type].
+	    ch_vol_cntrl_info[chan_index].codec_reg;
+
+	mask =
+	    adie_codec_vol_cntrl[vol_type].
+	    ch_vol_cntrl_info[chan_index].codec_mask;
+
+	/* compare the target index with current index */
+	if (cur_index < target_index) {
+
+		/* Volume is being increased loop and increase it in 4-5 steps
+		 */
+		count = ((target_index - cur_index) * 100 / index_jump);
+		index = cur_index;
+
+		for (i = 1; i <= count; i++) {
+			index = index + (int)(index_jump / 100);
+
+			val =
+			    adie_codec_vol_cntrl[vol_type].ch_vol_cntrl_info
+			    [chan_index].vol_cntrl_data[index];
+
+			pr_debug("%s: write reg %x val 0x%x\n",
+					__func__, reg, val);
+
+			rc = adie_codec_write(reg, mask, val);
+			if (rc < 0) {
+				pr_err("%s: write reg %x val 0x%x failed\n",
+					__func__, reg, val);
+				return rc;
+			}
+		}
+
+		/*do one final write to take it to the target index level */
+		val =
+		    adie_codec_vol_cntrl[vol_type].ch_vol_cntrl_info
+		    [chan_index].vol_cntrl_data[target_index];
+
+		pr_debug("%s: write reg %x val 0x%x\n", __func__, reg, val);
+
+		rc = adie_codec_write(reg, mask, val);
+
+		if (rc < 0) {
+			pr_err("%s: write reg %x val 0x%x failed\n",
+					__func__, reg, val);
+			return rc;
+		}
+
+	} else {
+
+		/* Volume is being decreased from the current setting */
+		index = cur_index;
+		/* loop and decrease it in 4-5 steps */
+		count = ((cur_index - target_index) * 100 / index_jump);
+
+		for (i = 1; i <= count; i++) {
+			index = index - (int)(index_jump / 100);
+
+			val =
+			    adie_codec_vol_cntrl[vol_type].ch_vol_cntrl_info
+			    [chan_index].vol_cntrl_data[index];
+
+			pr_debug("%s: write reg %x val 0x%x\n",
+					__func__, reg, val);
+
+			rc = adie_codec_write(reg, mask, val);
+			if (rc < 0) {
+				pr_err("%s: write reg %x val 0x%x failed\n",
+					__func__, reg, val);
+				return rc;
+			}
+		}
+
+		/* do one final write to take it to the target index level */
+		val =
+		    adie_codec_vol_cntrl[vol_type].ch_vol_cntrl_info
+		    [chan_index].vol_cntrl_data[target_index];
+
+		pr_debug("%s: write reg %x val 0x%x\n", __func__, reg, val);
+
+		rc = adie_codec_write(reg, mask, val);
+
+		if (rc < 0) {
+			pr_err("%s: write reg %x val 0x%x failed\n",
+					__func__, reg, val);
+			return rc;
+		}
+	}
+	return 0;
+}
+
+static int marimba_adie_codec_set_device_digital_volume(
+		struct adie_codec_path *path_ptr,
+		u32 num_channels, u32 vol_percentage /* in percentage */)
+{
+	enum adie_vol_type vol_type;
+	s32 milli_bel;
+	u32 chan_index;
+	u32 step_index;
+	u32 cur_step_index = 0;
+
+	if (!path_ptr  || (path_ptr->curr_stage !=
+				ADIE_CODEC_DIGITAL_ANALOG_READY)) {
+		pr_info("%s: Marimba codec not ready for volume control\n",
+		       __func__);
+		return  -EPERM;
+	}
+
+	if (num_channels > 2) {
+		pr_err("%s: Marimba codec only supports max two channels\n",
+		       __func__);
+		return -EINVAL;
+	}
+
+	if (path_ptr->profile->path_type == ADIE_CODEC_RX)
+		vol_type = ADIE_CODEC_RX_DIG_VOL;
+	else if (path_ptr->profile->path_type == ADIE_CODEC_TX)
+		vol_type = ADIE_CODEC_TX_DIG_VOL;
+	else {
+		pr_err("%s: invalid device data neither RX nor TX\n",
+				__func__);
+		return -EINVAL;
+	}
+
+	milli_bel = ((adie_codec_vol_cntrl[vol_type].max_mb -
+			adie_codec_vol_cntrl[vol_type].min_mb) *
+			vol_percentage) / 100;
+
+	milli_bel = adie_codec_vol_cntrl[vol_type].min_mb + milli_bel;
+
+	pr_debug("%s: milli bell = %d vol_type = %d vol_percentage = %d"
+		 " num_cha =  %d \n",
+		 __func__, milli_bel, vol_type, vol_percentage, num_channels);
+
+
+	step_index = ((milli_bel
+		       - adie_codec_vol_cntrl[vol_type].min_mb
+		       + (adie_codec_vol_cntrl[vol_type].step_in_mb / 2))
+		      / adie_codec_vol_cntrl[vol_type].step_in_mb);
+
+
+	for (chan_index = 0; chan_index < num_channels; chan_index++) {
+		adie_codec_read_dig_vol(vol_type, chan_index, &cur_step_index);
+
+		pr_debug("%s: cur_step_index = %u  current vol = 0x%x\n",
+				__func__, cur_step_index,
+			adie_codec_vol_cntrl[vol_type].ch_vol_cntrl_info
+			[chan_index].vol_cntrl_data[cur_step_index]);
+
+		pr_debug("%s: step index = %u  new volume = 0x%x\n",
+		 __func__, step_index,
+		 adie_codec_vol_cntrl[vol_type].ch_vol_cntrl_info
+		 [chan_index].vol_cntrl_data[step_index]);
+
+		adie_codec_set_dig_vol(vol_type, chan_index, cur_step_index,
+				       step_index);
+
+	}
+	return 0;
+}
+
+static int marimba_adie_codec_setpath(struct adie_codec_path *path_ptr,
+					u32 freq_plan, u32 osr)
+{
+	int rc = 0;
+	u32 i, freq_idx = 0, freq = 0;
+
+	if ((path_ptr->curr_stage != ADIE_CODEC_DIGITAL_OFF) &&
+		(path_ptr->curr_stage != ADIE_CODEC_FLASH_IMAGE)) {
+		rc = -EBUSY;
+		goto error;
+	}
+
+	for (i = 0; i < path_ptr->profile->setting_sz; i++) {
+		if (path_ptr->profile->settings[i].osr == osr) {
+			if (path_ptr->profile->settings[i].freq_plan >=
+				freq_plan) {
+				if (freq == 0) {
+					freq = path_ptr->profile->settings[i].
+								freq_plan;
+					freq_idx = i;
+				} else if (path_ptr->profile->settings[i].
+					freq_plan < freq) {
+					freq = path_ptr->profile->settings[i].
+								freq_plan;
+					freq_idx = i;
+				}
+			}
+		}
+	}
+
+	if (freq_idx >= path_ptr->profile->setting_sz)
+		rc = -ENODEV;
+	else {
+		path_ptr->hwsetting_idx = freq_idx;
+		path_ptr->stage_idx = 0;
+	}
+
+error:
+	return rc;
+}
+
+static u32 marimba_adie_codec_freq_supported(
+				struct adie_codec_dev_profile *profile,
+				u32 requested_freq)
+{
+	u32 i, rc = -EINVAL;
+
+	for (i = 0; i < profile->setting_sz; i++) {
+		if (profile->settings[i].freq_plan >= requested_freq) {
+			rc = 0;
+			break;
+		}
+	}
+	return rc;
+}
+
+static int marimba_adie_codec_enable_sidetone(
+				struct adie_codec_path *rx_path_ptr,
+				u32 enable)
+{
+	int rc = 0;
+
+	pr_debug("%s()\n", __func__);
+
+	mutex_lock(&adie_codec.lock);
+
+	if (!rx_path_ptr || &adie_codec.path[ADIE_CODEC_RX] != rx_path_ptr) {
+		pr_err("%s: invalid path pointer\n", __func__);
+		rc = -EINVAL;
+		goto error;
+	} else if (rx_path_ptr->curr_stage !=
+		ADIE_CODEC_DIGITAL_ANALOG_READY) {
+		pr_err("%s: bad state\n", __func__);
+		rc = -EPERM;
+		goto error;
+	}
+
+	if (enable)
+		rc = adie_codec_write(MARIMBA_CDC_RX_CTL,
+		MARIMBA_CDC_RX_CTL_ST_EN_MASK,
+		(0x1 << MARIMBA_CDC_RX_CTL_ST_EN_SHFT));
+	else
+		rc = adie_codec_write(MARIMBA_CDC_RX_CTL,
+		MARIMBA_CDC_RX_CTL_ST_EN_MASK, 0);
+
+error:
+	mutex_unlock(&adie_codec.lock);
+	return rc;
+}
+
+static void adie_codec_reach_stage_action(struct adie_codec_path *path_ptr,
+						u32 stage)
+{
+	u32 iter;
+	struct adie_codec_register *reg_info;
+
+	if (stage == ADIE_CODEC_FLASH_IMAGE) {
+		/* perform reimage */
+		for (iter = 0; iter < path_ptr->img.img_sz; iter++) {
+			reg_info = &path_ptr->img.regs[iter];
+			adie_codec_write(reg_info->reg,
+			reg_info->mask, reg_info->val);
+		}
+	}
+}
+
+static int marimba_adie_codec_proceed_stage(struct adie_codec_path *path_ptr,
+						u32 state)
+{
+	int rc = 0, loop_exit = 0;
+	struct adie_codec_action_unit *curr_action;
+	struct adie_codec_hwsetting_entry *setting;
+	u8 reg, mask, val;
+
+	mutex_lock(&adie_codec.lock);
+	setting = &path_ptr->profile->settings[path_ptr->hwsetting_idx];
+	while (!loop_exit) {
+		curr_action = &setting->actions[path_ptr->stage_idx];
+		switch (curr_action->type) {
+		case ADIE_CODEC_ACTION_ENTRY:
+			ADIE_CODEC_UNPACK_ENTRY(curr_action->action,
+			reg, mask, val);
+			adie_codec_write(reg, mask, val);
+			break;
+		case ADIE_CODEC_ACTION_DELAY_WAIT:
+			if (curr_action->action > MAX_MDELAY_US)
+				msleep(curr_action->action/1000);
+			else if (curr_action->action < MIN_MDELAY_US)
+				udelay(curr_action->action);
+			else
+				mdelay(curr_action->action/1000);
+			break;
+		case ADIE_CODEC_ACTION_STAGE_REACHED:
+			adie_codec_reach_stage_action(path_ptr,
+				curr_action->action);
+			if (curr_action->action == state) {
+				path_ptr->curr_stage = state;
+				loop_exit = 1;
+			}
+			break;
+		default:
+			BUG();
+		}
+
+		path_ptr->stage_idx++;
+		if (path_ptr->stage_idx == setting->action_sz)
+			path_ptr->stage_idx = 0;
+	}
+	mutex_unlock(&adie_codec.lock);
+	return rc;
+}
+
+static void marimba_codec_bring_up(void)
+{
+	/* bring up sequence for Marimba codec core
+	 * ensure RESET_N = 0 and GDFS_CLAMP_EN=1 -
+	 * set GDFS_EN_FEW=1 then GDFS_EN_REST=1 then
+	 * GDFS_CLAMP_EN = 0 and finally RESET_N = 1
+	 * Marimba codec bring up should use the Marimba
+	 * slave address after which the codec slave
+	 * address can be used
+	 */
+
+	/* Bring up codec */
+	adie_codec_write(0xFF, 0xFF, 0x08);
+
+	/* set GDFS_EN_FEW=1 */
+	adie_codec_write(0xFF, 0xFF, 0x0a);
+
+	/* set GDFS_EN_REST=1 */
+	adie_codec_write(0xFF, 0xFF, 0x0e);
+
+	/* set RESET_N=1 */
+	adie_codec_write(0xFF, 0xFF, 0x07);
+
+	adie_codec_write(0xFF, 0xFF, 0x17);
+
+	/* enable band gap */
+	adie_codec_write(0x03, 0xFF, 0x04);
+
+	/* dither delay selected and dmic gain stage bypassed */
+	adie_codec_write(0x8F, 0xFF, 0x44);
+}
+
+static void marimba_codec_bring_down(void)
+{
+	adie_codec_write(0xFF, 0xFF, 0x07);
+	adie_codec_write(0xFF, 0xFF, 0x06);
+	adie_codec_write(0xFF, 0xFF, 0x0e);
+	adie_codec_write(0xFF, 0xFF, 0x08);
+	adie_codec_write(0x03, 0xFF, 0x00);
+}
+
+static int marimba_adie_codec_open(struct adie_codec_dev_profile *profile,
+	struct adie_codec_path **path_pptr)
+{
+	int rc = 0;
+
+	mutex_lock(&adie_codec.lock);
+
+	if (!profile || !path_pptr) {
+		rc = -EINVAL;
+		goto error;
+	}
+
+	if (adie_codec.path[profile->path_type].profile) {
+		rc = -EBUSY;
+		goto error;
+	}
+
+	if (!adie_codec.ref_cnt) {
+
+		if (adie_codec.codec_pdata &&
+				adie_codec.codec_pdata->marimba_codec_power) {
+
+			rc = adie_codec.codec_pdata->marimba_codec_power(1);
+			if (rc) {
+				pr_err("%s: could not power up marimba "
+						"codec\n", __func__);
+				goto error;
+			}
+		}
+		marimba_codec_bring_up();
+	}
+
+	adie_codec.path[profile->path_type].profile = profile;
+	*path_pptr = (void *) &adie_codec.path[profile->path_type];
+	adie_codec.ref_cnt++;
+	adie_codec.path[profile->path_type].hwsetting_idx = 0;
+	adie_codec.path[profile->path_type].curr_stage = ADIE_CODEC_FLASH_IMAGE;
+	adie_codec.path[profile->path_type].stage_idx = 0;
+
+
+error:
+
+	mutex_unlock(&adie_codec.lock);
+	return rc;
+}
+
+static int marimba_adie_codec_close(struct adie_codec_path *path_ptr)
+{
+	int rc = 0;
+
+	mutex_lock(&adie_codec.lock);
+
+	if (!path_ptr) {
+		rc = -EINVAL;
+		goto error;
+	}
+	if (path_ptr->curr_stage != ADIE_CODEC_DIGITAL_OFF)
+		adie_codec_proceed_stage(path_ptr, ADIE_CODEC_DIGITAL_OFF);
+
+	BUG_ON(!adie_codec.ref_cnt);
+
+	path_ptr->profile = NULL;
+	adie_codec.ref_cnt--;
+
+	if (!adie_codec.ref_cnt) {
+
+		marimba_codec_bring_down();
+
+		if (adie_codec.codec_pdata &&
+				adie_codec.codec_pdata->marimba_codec_power) {
+
+			rc = adie_codec.codec_pdata->marimba_codec_power(0);
+			if (rc) {
+				pr_err("%s: could not power down marimba "
+						"codec\n", __func__);
+				goto error;
+			}
+		}
+	}
+
+error:
+	mutex_unlock(&adie_codec.lock);
+	return rc;
+}
+
+static const struct adie_codec_operations marimba_adie_ops = {
+	.codec_id = MARIMBA_ID,
+	.codec_open = marimba_adie_codec_open,
+	.codec_close = marimba_adie_codec_close,
+	.codec_setpath = marimba_adie_codec_setpath,
+	.codec_proceed_stage = marimba_adie_codec_proceed_stage,
+	.codec_freq_supported = marimba_adie_codec_freq_supported,
+	.codec_enable_sidetone = marimba_adie_codec_enable_sidetone,
+	.codec_set_device_digital_volume =
+			marimba_adie_codec_set_device_digital_volume,
+};
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *debugfs_marimba_dent;
+static struct dentry *debugfs_peek;
+static struct dentry *debugfs_poke;
+static struct dentry *debugfs_power;
+
+static unsigned char read_data;
+
+static int codec_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static int get_parameters(char *buf, long int *param1, int num_of_par)
+{
+	char *token;
+	int base, cnt;
+
+	token = strsep(&buf, " ");
+
+	for (cnt = 0; cnt < num_of_par; cnt++) {
+		if (token != NULL) {
+			if ((token[1] == 'x') || (token[1] == 'X'))
+				base = 16;
+			else
+				base = 10;
+
+			if (strict_strtoul(token, base, &param1[cnt]) != 0)
+				return -EINVAL;
+
+			token = strsep(&buf, " ");
+			}
+		else
+			return -EINVAL;
+	}
+	return 0;
+}
+
+static ssize_t codec_debug_read(struct file *file, char __user *ubuf,
+				size_t count, loff_t *ppos)
+{
+	char lbuf[8];
+
+	snprintf(lbuf, sizeof(lbuf), "0x%x\n", read_data);
+	return simple_read_from_buffer(ubuf, count, ppos, lbuf, strlen(lbuf));
+}
+
+static ssize_t codec_debug_write(struct file *filp,
+	const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+	char *access_str = filp->private_data;
+	char lbuf[32];
+	int rc;
+	long int param[5];
+
+	if (cnt > sizeof(lbuf) - 1)
+		return -EINVAL;
+
+	rc = copy_from_user(lbuf, ubuf, cnt);
+	if (rc)
+		return -EFAULT;
+
+	lbuf[cnt] = '\0';
+
+	if (!strcmp(access_str, "power")) {
+		if (get_parameters(lbuf, param, 1) == 0) {
+			switch (param[0]) {
+			case 1:
+				adie_codec.codec_pdata->marimba_codec_power(1);
+				marimba_codec_bring_up();
+				break;
+			case 0:
+				marimba_codec_bring_down();
+				adie_codec.codec_pdata->marimba_codec_power(0);
+				break;
+			default:
+				rc = -EINVAL;
+				break;
+			}
+		} else
+			rc = -EINVAL;
+	} else if (!strcmp(access_str, "poke")) {
+		/* write */
+		rc = get_parameters(lbuf, param, 2);
+		if ((param[0] <= 0xFF) && (param[1] <= 0xFF) &&
+			(rc == 0))
+			adie_codec_write(param[0], 0xFF, param[1]);
+		else
+			rc = -EINVAL;
+	} else if (!strcmp(access_str, "peek")) {
+		/* read */
+		rc = get_parameters(lbuf, param, 1);
+		if ((param[0] <= 0xFF) && (rc == 0))
+			adie_codec_read(param[0], &read_data);
+		else
+			rc = -EINVAL;
+	}
+
+	if (rc == 0)
+		rc = cnt;
+	else
+		pr_err("%s: rc = %d\n", __func__, rc);
+
+	return rc;
+}
+
+static const struct file_operations codec_debug_ops = {
+	.open = codec_debug_open,
+	.write = codec_debug_write,
+	.read = codec_debug_read
+};
+#endif
+
+static int marimba_codec_probe(struct platform_device *pdev)
+{
+	int rc;
+
+	adie_codec.pdrv_ptr = platform_get_drvdata(pdev);
+	adie_codec.codec_pdata = pdev->dev.platform_data;
+
+	if (adie_codec.codec_pdata->snddev_profile_init)
+		adie_codec.codec_pdata->snddev_profile_init();
+
+	/* Register the marimba ADIE operations */
+	rc = adie_codec_register_codec_operations(&marimba_adie_ops);
+
+#ifdef CONFIG_DEBUG_FS
+	debugfs_marimba_dent = debugfs_create_dir("msm_adie_codec", 0);
+	if (!IS_ERR(debugfs_marimba_dent)) {
+		debugfs_peek = debugfs_create_file("peek",
+		S_IFREG | S_IRUGO, debugfs_marimba_dent,
+		(void *) "peek", &codec_debug_ops);
+
+		debugfs_poke = debugfs_create_file("poke",
+		S_IFREG | S_IRUGO, debugfs_marimba_dent,
+		(void *) "poke", &codec_debug_ops);
+
+		debugfs_power = debugfs_create_file("power",
+		S_IFREG | S_IRUGO, debugfs_marimba_dent,
+		(void *) "power", &codec_debug_ops);
+	}
+#endif
+	return rc;
+}
+
+static struct platform_driver marimba_codec_driver = {
+	.probe = marimba_codec_probe,
+	.driver = {
+		.name = "marimba_codec",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init marimba_codec_init(void)
+{
+	s32 rc;
+
+	rc = platform_driver_register(&marimba_codec_driver);
+	if (IS_ERR_VALUE(rc))
+		goto error;
+
+	adie_codec.path[ADIE_CODEC_TX].img.regs = adie_codec_tx_regs;
+	adie_codec.path[ADIE_CODEC_TX].img.img_sz =
+	ARRAY_SIZE(adie_codec_tx_regs);
+	adie_codec.path[ADIE_CODEC_RX].img.regs = adie_codec_rx_regs;
+	adie_codec.path[ADIE_CODEC_RX].img.img_sz =
+	ARRAY_SIZE(adie_codec_rx_regs);
+	adie_codec.path[ADIE_CODEC_LB].img.regs = adie_codec_lb_regs;
+	adie_codec.path[ADIE_CODEC_LB].img.img_sz =
+	ARRAY_SIZE(adie_codec_lb_regs);
+	mutex_init(&adie_codec.lock);
+
+error:
+	return rc;
+}
+
+static void __exit marimba_codec_exit(void)
+{
+#ifdef CONFIG_DEBUG_FS
+	debugfs_remove(debugfs_peek);
+	debugfs_remove(debugfs_poke);
+	debugfs_remove(debugfs_power);
+	debugfs_remove(debugfs_marimba_dent);
+#endif
+	platform_driver_unregister(&marimba_codec_driver);
+}
+
+module_init(marimba_codec_init);
+module_exit(marimba_codec_exit);
+
+MODULE_DESCRIPTION("Marimba codec driver");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/marimba-core.c b/drivers/mfd/marimba-core.c
new file mode 100644
index 0000000..70ec2ec
--- /dev/null
+++ b/drivers/mfd/marimba-core.c
@@ -0,0 +1,937 @@
+/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+/*
+ * Qualcomm Marimba Core Driver
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+
+#include <linux/i2c.h>
+#include <linux/mfd/marimba.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+
+#define MARIMBA_MODE				0x00
+
+#define ADIE_ARRY_SIZE  (CHIP_ID_MAX * MARIMBA_NUM_CHILD)
+
+static int marimba_shadow[ADIE_ARRY_SIZE][0xff];
+static int mutex_initialized;
+struct marimba marimba_modules[ADIE_ARRY_SIZE];
+
+#define MARIMBA_VERSION_REG		0x11
+#define MARIMBA_MODE_REG		0x00
+
+struct marimba_platform_data *marimba_pdata;
+
+static uint32_t marimba_gpio_count;
+static bool fm_status;
+static bool bt_status;
+
+#ifdef CONFIG_I2C_SSBI
+#define NUM_ADD	MARIMBA_NUM_CHILD
+#else
+#define NUM_ADD	(MARIMBA_NUM_CHILD - 1)
+#endif
+
+#if defined(CONFIG_DEBUG_FS)
+struct adie_dbg_device {
+	struct mutex		dbg_mutex;
+	struct dentry		*dent;
+	int			addr;
+	int			mod_id;
+};
+
+static struct adie_dbg_device *marimba_dbg_device;
+static struct adie_dbg_device *timpani_dbg_device;
+static struct adie_dbg_device *bahama_dbg_device;
+#endif
+
+
+/**
+ * marimba_read_bahama_ver - Reads Bahama version.
+ * @param marimba: marimba structure pointer passed by client
+ * @returns result of the operation.
+ */
+int marimba_read_bahama_ver(struct marimba *marimba)
+{
+	int rc;
+	u8 bahama_version;
+
+	rc = marimba_read_bit_mask(marimba, 0x00,  &bahama_version, 1, 0x1F);
+	if (rc < 0)
+		return rc;
+	switch (bahama_version) {
+	case 0x08: /* varient of bahama v1 */
+	case 0x10:
+	case 0x00:
+		return BAHAMA_VER_1_0;
+	case 0x09: /* variant of bahama v2 */
+		return BAHAMA_VER_2_0;
+	default:
+		return BAHAMA_VER_UNSUPPORTED;
+	}
+}
+EXPORT_SYMBOL(marimba_read_bahama_ver);
+/**
+ * marimba_ssbi_write - Writes a n bit TSADC register in Marimba
+ * @param marimba: marimba structure pointer passed by client
+ * @param reg: register address
+ * @param value: buffer to be written
+ * @param len: num of bytes
+ * @returns result of the operation.
+ */
+int marimba_ssbi_write(struct marimba *marimba, u16 reg , u8 *value, int len)
+{
+	struct i2c_msg *msg;
+	int ret;
+
+	marimba = &marimba_modules[marimba->mod_id];
+
+	mutex_lock(&marimba->xfer_lock);
+
+	msg = &marimba->xfer_msg[0];
+	msg->addr = reg;
+	msg->flags = 0x0;
+	msg->buf = value;
+	msg->len = len;
+
+	ret = i2c_transfer(marimba->client->adapter, marimba->xfer_msg, 1);
+
+	mutex_unlock(&marimba->xfer_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(marimba_ssbi_write);
+
+/**
+ * marimba_ssbi_read - Reads a n bit TSADC register in Marimba
+ * @param marimba: marimba structure pointer passed by client
+ * @param reg: register address
+ * @param value: ssbi read of the register to be stored
+ * @param len: num of bytes
+ *
+ * @returns result of the operation.
+*/
+int marimba_ssbi_read(struct marimba *marimba, u16 reg, u8 *value, int len)
+{
+	struct i2c_msg *msg;
+	int ret;
+
+	marimba = &marimba_modules[marimba->mod_id];
+
+	mutex_lock(&marimba->xfer_lock);
+
+	msg = &marimba->xfer_msg[0];
+	msg->addr = reg;
+	msg->flags = I2C_M_RD;
+	msg->buf = value;
+	msg->len = len;
+
+	ret = i2c_transfer(marimba->client->adapter, marimba->xfer_msg, 1);
+
+	mutex_unlock(&marimba->xfer_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(marimba_ssbi_read);
+
+/**
+ * marimba_write_bit_mask - Sets n bit register using bit mask
+ * @param marimba: marimba structure pointer passed by client
+ * @param reg: register address
+ * @param value: buffer to be written to the registers
+ * @param num_bytes: n bytes to write
+ * @param mask: bit mask corresponding to the registers
+ *
+ * @returns result of the operation.
+ */
+int marimba_write_bit_mask(struct marimba *marimba, u8 reg, u8 *value,
+						unsigned num_bytes, u8 mask)
+{
+	int ret, i;
+	struct i2c_msg *msg;
+	u8 data[num_bytes + 1];
+	u8 mask_value[num_bytes];
+
+	marimba = &marimba_modules[marimba->mod_id];
+	if (marimba == NULL) {
+		pr_err("%s: Unable to access Marimba core\n", __func__);
+		return -ENODEV;
+	}
+
+
+	mutex_lock(&marimba->xfer_lock);
+
+	for (i = 0; i < num_bytes; i++)
+		mask_value[i] = (marimba_shadow[marimba->mod_id][reg + i]
+					& ~mask) | (value[i] & mask);
+
+	msg = &marimba->xfer_msg[0];
+	if (marimba->client == NULL) {
+		pr_err("%s: Unable to access the Marimba slave device.\n",
+								__func__);
+		return -ENODEV;
+	}
+
+	msg->addr = marimba->client->addr;
+	msg->flags = 0;
+	msg->len = num_bytes + 1;
+	msg->buf = data;
+	data[0] = reg;
+	memcpy(data+1, mask_value, num_bytes);
+
+	ret = i2c_transfer(marimba->client->adapter, marimba->xfer_msg, 1);
+
+	/* Try again if the write fails */
+	if (ret != 1)
+		ret = i2c_transfer(marimba->client->adapter,
+						marimba->xfer_msg, 1);
+
+	if (ret == 1) {
+		for (i = 0; i < num_bytes; i++)
+			marimba_shadow[marimba->mod_id][reg + i]
+							= mask_value[i];
+	} else {
+		dev_err(&marimba->client->dev, "i2c write failed\n");
+		ret = -ENODEV;
+	}
+
+	mutex_unlock(&marimba->xfer_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(marimba_write_bit_mask);
+
+/**
+ * marimba_write - Sets n bit register in Marimba
+ * @param marimba: marimba structure pointer passed by client
+ * @param reg: register address
+ * @param value: buffer values to be written
+ * @param num_bytes: n bytes to write
+ *
+ * @returns result of the operation.
+ */
+int marimba_write(struct marimba *marimba, u8 reg, u8 *value,
+							unsigned num_bytes)
+{
+	return marimba_write_bit_mask(marimba, reg, value, num_bytes, 0xff);
+}
+EXPORT_SYMBOL(marimba_write);
+
+/**
+ * marimba_read_bit_mask - Reads a n bit register based on bit mask
+ * @param marimba: marimba structure pointer passed by client
+ * @param reg: register address
+ * @param value: i2c read of the register to be stored
+ * @param num_bytes: n bytes to be read.
+ * @param mask: bit mask concerning its register
+ *
+ * @returns result of the operation.
+*/
+int marimba_read_bit_mask(struct marimba *marimba, u8 reg, u8 *value,
+						unsigned num_bytes, u8 mask)
+{
+	int ret, i;
+
+	struct i2c_msg *msg;
+
+	marimba = &marimba_modules[marimba->mod_id];
+
+	mutex_lock(&marimba->xfer_lock);
+
+	msg = &marimba->xfer_msg[0];
+	msg->addr = marimba->client->addr;
+	msg->len = 1;
+	msg->flags = 0;
+	msg->buf = &reg;
+
+	msg = &marimba->xfer_msg[1];
+	msg->addr = marimba->client->addr;
+	msg->len = num_bytes;
+	msg->flags = I2C_M_RD;
+	msg->buf = value;
+
+	ret = i2c_transfer(marimba->client->adapter, marimba->xfer_msg, 2);
+
+	/* Try again if read fails first time */
+	if (ret != 2)
+		ret = i2c_transfer(marimba->client->adapter,
+						marimba->xfer_msg, 2);
+
+	if (ret == 2) {
+		for (i = 0; i < num_bytes; i++) {
+			marimba_shadow[marimba->mod_id][reg + i] = value[i];
+			value[i] &= mask;
+		}
+	} else {
+		dev_err(&marimba->client->dev, "i2c read failed\n");
+		ret = -ENODEV;
+	}
+
+	mutex_unlock(&marimba->xfer_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(marimba_read_bit_mask);
+
+/**
+ * marimba_read - Reads n bit registers in Marimba
+ * @param marimba: marimba structure pointer passed by client
+ * @param reg: register address
+ * @param value: i2c read of the register to be stored
+ * @param num_bytes: n bytes to read.
+ * @param mask: bit mask concerning its register
+ *
+ * @returns result of the operation.
+*/
+int marimba_read(struct marimba *marimba, u8 reg, u8 *value, unsigned num_bytes)
+{
+	return marimba_read_bit_mask(marimba, reg, value, num_bytes, 0xff);
+}
+EXPORT_SYMBOL(marimba_read);
+
+int timpani_read(struct marimba *marimba, u8 reg, u8 *value, unsigned num_bytes)
+{
+	return marimba_read_bit_mask(marimba, reg, value, num_bytes, 0xff);
+}
+EXPORT_SYMBOL(timpani_read);
+
+int timpani_write(struct marimba *marimba, u8 reg,
+					u8 *value, unsigned num_bytes)
+{
+	return marimba_write_bit_mask(marimba, reg, value, num_bytes, 0xff);
+}
+EXPORT_SYMBOL(timpani_write);
+
+static int cur_codec_type = -1, cur_adie_type = -1, cur_connv_type = -1;
+static int adie_arry_idx;
+
+int adie_get_detected_codec_type(void)
+{
+	return cur_codec_type;
+}
+EXPORT_SYMBOL(adie_get_detected_codec_type);
+
+int adie_get_detected_connectivity_type(void)
+{
+	return cur_connv_type;
+}
+EXPORT_SYMBOL(adie_get_detected_connectivity_type);
+
+static struct device *
+add_numbered_child(unsigned chip, const char *name, int num, u8 driver_data,
+					void *pdata, unsigned pdata_len)
+{
+	struct platform_device *pdev;
+	struct marimba  *marimba = &marimba_modules[chip + adie_arry_idx];
+	int status = 0;
+
+	pdev = platform_device_alloc(name, num);
+	if (!pdev) {
+		status = -ENOMEM;
+		return ERR_PTR(status);
+	}
+
+	pdev->dev.parent = &marimba->client->dev;
+
+	marimba->mod_id = chip + adie_arry_idx;
+
+	platform_set_drvdata(pdev, marimba);
+
+	if (pdata) {
+		status = platform_device_add_data(pdev, pdata, pdata_len);
+		if (status < 0)
+			goto err;
+	}
+
+	status = platform_device_add(pdev);
+	if (status < 0)
+		goto err;
+
+err:
+	if (status < 0) {
+		platform_set_drvdata(pdev, NULL);
+		platform_device_put(pdev);
+		dev_err(&marimba->client->dev, "can't add %s dev\n", name);
+		return ERR_PTR(status);
+	}
+	return &pdev->dev;
+}
+
+static inline struct device *add_child(unsigned chip, const char *name,
+		u8 driver_data, void *pdata, unsigned pdata_len)
+{
+	return add_numbered_child(chip, name, -1, driver_data, pdata,
+								pdata_len);
+}
+
+static int marimba_add_child(struct marimba_platform_data *pdata,
+					u8 driver_data)
+{
+	struct device	*child;
+
+	if (cur_adie_type == MARIMBA_ID) {
+		child = add_child(MARIMBA_SLAVE_ID_FM, "marimba_fm",
+			driver_data, pdata->fm, sizeof(*pdata->fm));
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+	} else if ((cur_adie_type == BAHAMA_ID) &&
+			(cur_connv_type == BAHAMA_ID)) {
+		child = add_child(BAHAMA_SLAVE_ID_FM_ID, "marimba_fm",
+			driver_data, pdata->fm, sizeof(*pdata->fm));
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+	}
+
+	/* Add Codec for Marimba and Timpani */
+	if (cur_adie_type == MARIMBA_ID) {
+		child = add_child(MARIMBA_SLAVE_ID_CDC, "marimba_codec",
+			driver_data, pdata->codec, sizeof(*pdata->codec));
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+	} else if (cur_adie_type == TIMPANI_ID) {
+		child = add_child(MARIMBA_SLAVE_ID_CDC, "timpani_codec",
+			driver_data, pdata->codec, sizeof(*pdata->codec));
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+	}
+
+#if defined(CONFIG_I2C_SSBI)
+	if ((pdata->tsadc != NULL) && (cur_adie_type != BAHAMA_ID)) {
+		child = add_child(MARIMBA_ID_TSADC, "marimba_tsadc",
+			driver_data, pdata->tsadc, sizeof(*pdata->tsadc));
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+	}
+#endif
+	return 0;
+}
+
+int marimba_gpio_config(int gpio_value)
+{
+	struct marimba *marimba = &marimba_modules[MARIMBA_SLAVE_ID_MARIMBA];
+	struct marimba_platform_data *pdata = marimba_pdata;
+	int rc = 0;
+
+	/* Clients BT/FM need to manage GPIO 34 on Fusion for its clocks */
+
+	mutex_lock(&marimba->xfer_lock);
+
+	if (gpio_value) {
+		marimba_gpio_count++;
+		if (marimba_gpio_count == 1)
+			rc = pdata->marimba_gpio_config(1);
+	} else {
+		marimba_gpio_count--;
+		if (marimba_gpio_count == 0)
+			rc = pdata->marimba_gpio_config(0);
+	}
+
+	mutex_unlock(&marimba->xfer_lock);
+
+	return rc;
+
+}
+EXPORT_SYMBOL(marimba_gpio_config);
+
+bool marimba_get_fm_status(struct marimba *marimba)
+{
+	bool ret;
+
+	marimba = &marimba_modules[marimba->mod_id];
+
+	mutex_lock(&marimba->xfer_lock);
+
+	ret = fm_status;
+
+	mutex_unlock(&marimba->xfer_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(marimba_get_fm_status);
+
+void marimba_set_fm_status(struct marimba *marimba, bool value)
+{
+	marimba = &marimba_modules[marimba->mod_id];
+
+	mutex_lock(&marimba->xfer_lock);
+
+	fm_status = value;
+
+	mutex_unlock(&marimba->xfer_lock);
+}
+EXPORT_SYMBOL(marimba_set_fm_status);
+
+bool marimba_get_bt_status(struct marimba *marimba)
+{
+	bool ret;
+
+	marimba = &marimba_modules[marimba->mod_id];
+
+	mutex_lock(&marimba->xfer_lock);
+
+	ret = bt_status;
+
+	mutex_unlock(&marimba->xfer_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(marimba_get_bt_status);
+
+void marimba_set_bt_status(struct marimba *marimba, bool value)
+{
+	marimba = &marimba_modules[marimba->mod_id];
+
+	mutex_lock(&marimba->xfer_lock);
+
+	bt_status = value;
+
+	mutex_unlock(&marimba->xfer_lock);
+}
+EXPORT_SYMBOL(marimba_set_bt_status);
+
+#if defined(CONFIG_DEBUG_FS)
+
+static int check_addr(int addr, const char *func_name)
+{
+	if (addr < 0 || addr > 0xFF) {
+		pr_err("%s: Marimba register address is invalid: %d\n",
+			func_name, addr);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int marimba_debugfs_set(void *data, u64 val)
+{
+	struct adie_dbg_device *dbgdev = data;
+	u8 reg = val;
+	int rc;
+	struct marimba marimba_id;
+
+	mutex_lock(&dbgdev->dbg_mutex);
+
+	rc = check_addr(dbgdev->addr, __func__);
+	if (rc)
+		goto done;
+
+	marimba_id.mod_id = dbgdev->mod_id;
+	rc = marimba_write(&marimba_id, dbgdev->addr, &reg, 1);
+	rc = (rc == 1) ? 0 : rc;
+
+	if (rc)
+		pr_err("%s: FAIL marimba_write(0x%03X)=0x%02X: rc=%d\n",
+			__func__, dbgdev->addr, reg, rc);
+done:
+	mutex_unlock(&dbgdev->dbg_mutex);
+	return rc;
+}
+
+static int marimba_debugfs_get(void *data, u64 *val)
+{
+	struct adie_dbg_device *dbgdev = data;
+	int rc;
+	u8 reg;
+	struct marimba marimba_id;
+
+	mutex_lock(&dbgdev->dbg_mutex);
+
+	rc = check_addr(dbgdev->addr, __func__);
+	if (rc)
+		goto done;
+
+	marimba_id.mod_id = dbgdev->mod_id;
+	rc = marimba_read(&marimba_id, dbgdev->addr, &reg, 1);
+	rc = (rc == 2) ? 0 : rc;
+
+	if (rc) {
+		pr_err("%s: FAIL marimba_read(0x%03X)=0x%02X: rc=%d\n",
+			__func__, dbgdev->addr, reg, rc);
+		goto done;
+	}
+
+	*val = reg;
+done:
+	mutex_unlock(&dbgdev->dbg_mutex);
+	return rc;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(dbg_marimba_fops, marimba_debugfs_get,
+		marimba_debugfs_set, "0x%02llX\n");
+
+static int addr_set(void *data, u64 val)
+{
+	struct adie_dbg_device *dbgdev = data;
+	int rc;
+
+	rc = check_addr(val, __func__);
+	if (rc)
+		return rc;
+
+	mutex_lock(&dbgdev->dbg_mutex);
+	dbgdev->addr = val;
+	mutex_unlock(&dbgdev->dbg_mutex);
+
+	return 0;
+}
+
+static int addr_get(void *data, u64 *val)
+{
+	struct adie_dbg_device *dbgdev = data;
+	int rc;
+
+	mutex_lock(&dbgdev->dbg_mutex);
+
+	rc = check_addr(dbgdev->addr, __func__);
+	if (rc) {
+		mutex_unlock(&dbgdev->dbg_mutex);
+		return rc;
+	}
+	*val = dbgdev->addr;
+
+	mutex_unlock(&dbgdev->dbg_mutex);
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(dbg_addr_fops, addr_get, addr_set, "0x%03llX\n");
+
+static int __devinit marimba_dbg_init(int adie_type)
+{
+	struct adie_dbg_device *dbgdev;
+	struct dentry *dent;
+	struct dentry *temp;
+
+	dbgdev = kzalloc(sizeof *dbgdev, GFP_KERNEL);
+	if (dbgdev == NULL) {
+		pr_err("%s: kzalloc() failed.\n", __func__);
+		return -ENOMEM;
+	}
+
+	mutex_init(&dbgdev->dbg_mutex);
+	dbgdev->addr = -1;
+
+	if (adie_type == MARIMBA_ID) {
+		marimba_dbg_device = dbgdev;
+		marimba_dbg_device->mod_id = MARIMBA_SLAVE_ID_MARIMBA;
+		dent = debugfs_create_dir("marimba-dbg", NULL);
+	} else if (adie_type == TIMPANI_ID) {
+		timpani_dbg_device = dbgdev;
+		timpani_dbg_device->mod_id = MARIMBA_SLAVE_ID_MARIMBA;
+		dent = debugfs_create_dir("timpani-dbg", NULL);
+	} else if (adie_type == BAHAMA_ID) {
+		bahama_dbg_device = dbgdev;
+		bahama_dbg_device->mod_id = SLAVE_ID_BAHAMA;
+		dent = debugfs_create_dir("bahama-dbg", NULL);
+	}
+	if (dent == NULL || IS_ERR(dent)) {
+		pr_err("%s: ERR debugfs_create_dir: dent=0x%X\n",
+					__func__, (unsigned)dent);
+		kfree(dbgdev);
+		return -ENOMEM;
+	}
+
+	temp = debugfs_create_file("addr", S_IRUSR | S_IWUSR, dent,
+					dbgdev, &dbg_addr_fops);
+	if (temp == NULL || IS_ERR(temp)) {
+		pr_err("%s: ERR debugfs_create_file: dent=0x%X\n",
+				__func__, (unsigned)temp);
+		goto debug_error;
+	}
+
+	temp = debugfs_create_file("data", S_IRUSR | S_IWUSR, dent,
+					dbgdev,	&dbg_marimba_fops);
+	if (temp == NULL || IS_ERR(temp)) {
+		pr_err("%s: ERR debugfs_create_file: dent=0x%X\n",
+				__func__, (unsigned)temp);
+		goto debug_error;
+	}
+	dbgdev->dent = dent;
+
+	return 0;
+
+debug_error:
+	kfree(dbgdev);
+	debugfs_remove_recursive(dent);
+	return -ENOMEM;
+}
+
+static int __devexit marimba_dbg_remove(void)
+{
+	if (marimba_dbg_device) {
+		debugfs_remove_recursive(marimba_dbg_device->dent);
+		kfree(marimba_dbg_device);
+	}
+	if (timpani_dbg_device) {
+		debugfs_remove_recursive(timpani_dbg_device->dent);
+		kfree(timpani_dbg_device);
+	}
+	if (bahama_dbg_device) {
+		debugfs_remove_recursive(bahama_dbg_device->dent);
+		kfree(bahama_dbg_device);
+	}
+	return 0;
+}
+
+#else
+
+static int __devinit marimba_dbg_init(int adie_type)
+{
+	return 0;
+}
+
+static int __devexit marimba_dbg_remove(void)
+{
+	return 0;
+}
+
+#endif
+
+static int get_adie_type(void)
+{
+	u8 rd_val;
+	int ret;
+
+	struct marimba *marimba = &marimba_modules[ADIE_ARRY_SIZE - 1];
+
+	marimba->mod_id = ADIE_ARRY_SIZE - 1;
+	/* Enable the Mode for Marimba/Timpani */
+	ret = marimba_read(marimba, MARIMBA_MODE_REG, &rd_val, 1);
+
+	if (ret >= 0) {
+		if (rd_val & 0x80) {
+			cur_adie_type = BAHAMA_ID;
+			return cur_adie_type;
+		} else {
+			ret = marimba_read(marimba,
+				MARIMBA_VERSION_REG, &rd_val, 1);
+			if ((ret >= 0) && (rd_val & 0x20)) {
+				cur_adie_type = TIMPANI_ID;
+				return cur_adie_type;
+			} else if (ret >= 0) {
+				cur_adie_type = MARIMBA_ID;
+				return cur_adie_type;
+			}
+		}
+	}
+
+	return ret;
+}
+
+static void marimba_init_reg(struct i2c_client *client, u8 driver_data)
+{
+	struct marimba_platform_data *pdata = client->dev.platform_data;
+	struct marimba *marimba =
+		&marimba_modules[MARIMBA_SLAVE_ID_MARIMBA + adie_arry_idx];
+
+	u8 buf[1];
+
+	buf[0] = 0x10;
+
+	if (cur_adie_type != BAHAMA_ID) {
+		marimba->mod_id = MARIMBA_SLAVE_ID_MARIMBA + adie_arry_idx;
+		/* Enable the Mode for Marimba/Timpani */
+		marimba_write(marimba, MARIMBA_MODE, buf, 1);
+	} else if ((cur_adie_type == BAHAMA_ID) &&
+				(cur_connv_type == BAHAMA_ID)) {
+		marimba->mod_id = MARIMBA_SLAVE_ID_MARIMBA + adie_arry_idx;
+		marimba_write(marimba, BAHAMA_SLAVE_ID_FM_ID,
+				&pdata->slave_id[SLAVE_ID_BAHAMA_FM], 1);
+		/* Configure Bahama core registers (AREG & DREG) */
+		/* with optimal values to eliminate power leakage */
+		if (pdata->bahama_core_config != NULL)
+			pdata->bahama_core_config(cur_adie_type);
+	}
+}
+
+static int __devinit marimba_probe(struct i2c_client *client,
+				const struct i2c_device_id *id)
+{
+	struct marimba_platform_data *pdata = client->dev.platform_data;
+	struct i2c_adapter *ssbi_adap;
+	struct marimba *marimba;
+	int i, status, rc, client_loop, adie_slave_idx_offset;
+	int rc_bahama = 0, rc_marimba = 0;
+
+	if (!pdata) {
+		dev_dbg(&client->dev, "no platform data?\n");
+		status = -EINVAL;
+		goto fail;
+	}
+
+	if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) {
+		dev_dbg(&client->dev, "can't talk I2C?\n");
+		status = -EIO;
+		goto fail;
+	}
+	if (!mutex_initialized) {
+		for (i = 0; i < ADIE_ARRY_SIZE; ++i) {
+			marimba = &marimba_modules[i];
+			mutex_init(&marimba->xfer_lock);
+		}
+		mutex_initialized = 1;
+	}
+	/* First, identify the codec type */
+	if (pdata->marimba_setup != NULL) {
+		rc_marimba = pdata->marimba_setup();
+		if (rc_marimba)
+			pdata->marimba_shutdown();
+	}
+	if (pdata->bahama_setup != NULL &&
+		cur_connv_type != BAHAMA_ID) {
+		rc_bahama = pdata->bahama_setup();
+		if (rc_bahama)
+			pdata->bahama_shutdown(cur_connv_type);
+	}
+	if (rc_marimba & rc_bahama) {
+		status = -EAGAIN;
+		goto fail;
+	}
+	marimba = &marimba_modules[ADIE_ARRY_SIZE - 1];
+	marimba->client = client;
+
+	rc = get_adie_type();
+
+	if (rc < 0) {
+		if (pdata->bahama_setup != NULL)
+			pdata->bahama_shutdown(cur_adie_type);
+		if (pdata->marimba_shutdown != NULL)
+			pdata->marimba_shutdown();
+		status = -ENODEV;
+		goto fail;
+	}
+
+	if (rc < 2) {
+		adie_arry_idx = 0;
+		adie_slave_idx_offset = 0;
+		client_loop = 0;
+		cur_codec_type = rc;
+		if (cur_connv_type < 0)
+			cur_connv_type = rc;
+		if (pdata->bahama_shutdown != NULL)
+			pdata->bahama_shutdown(cur_connv_type);
+	} else {
+		adie_arry_idx = 5;
+		adie_slave_idx_offset = 5;
+		client_loop = 1;
+		cur_connv_type = rc;
+	}
+
+	marimba = &marimba_modules[adie_arry_idx];
+	marimba->client = client;
+
+	for (i = 1; i <= (NUM_ADD - client_loop); i++) {
+		/* Skip adding BT/FM for Timpani */
+		if (i == 1 && rc >= 1)
+			i++;
+		marimba = &marimba_modules[i + adie_arry_idx];
+		if (i != MARIMBA_ID_TSADC)
+			marimba->client = i2c_new_dummy(client->adapter,
+				pdata->slave_id[i + adie_slave_idx_offset]);
+		else if (pdata->tsadc_ssbi_adap) {
+			ssbi_adap = i2c_get_adapter(pdata->tsadc_ssbi_adap);
+			marimba->client = i2c_new_dummy(ssbi_adap,
+						0x55);
+		} else
+			ssbi_adap = NULL;
+
+		if (!marimba->client) {
+			dev_err(&marimba->client->dev,
+				"can't attach client %d\n", i);
+			status = -ENOMEM;
+			goto fail;
+		}
+		strlcpy(marimba->client->name, id->name,
+			sizeof(marimba->client->name));
+
+	}
+
+	if (marimba_dbg_init(rc) != 0)
+		pr_debug("%s: marimba debugfs init failed\n", __func__);
+
+	marimba_init_reg(client, id->driver_data);
+
+	status = marimba_add_child(pdata, id->driver_data);
+
+	marimba_pdata = pdata;
+
+	return 0;
+
+fail:
+	return status;
+}
+
+static int __devexit marimba_remove(struct i2c_client *client)
+{
+	int i;
+	struct marimba_platform_data *pdata;
+
+	pdata = client->dev.platform_data;
+	for (i = 0; i < ADIE_ARRY_SIZE; i++) {
+		struct marimba *marimba = &marimba_modules[i];
+
+		if (marimba->client && marimba->client != client)
+			i2c_unregister_device(marimba->client);
+
+		marimba_modules[i].client = NULL;
+		if (mutex_initialized)
+			mutex_destroy(&marimba->xfer_lock);
+
+	}
+	marimba_dbg_remove();
+	mutex_initialized = 0;
+	if (pdata->marimba_shutdown != NULL)
+		pdata->marimba_shutdown();
+
+	return 0;
+}
+
+static struct i2c_device_id marimba_id_table[] = {
+	{"marimba", MARIMBA_ID},
+	{"timpani", TIMPANI_ID},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, marimba_id_table);
+
+static struct i2c_driver marimba_driver = {
+		.driver			= {
+			.owner		=	THIS_MODULE,
+			.name		=	"marimba-core",
+		},
+		.id_table		=	marimba_id_table,
+		.probe			=	marimba_probe,
+		.remove			=	__devexit_p(marimba_remove),
+};
+
+static int __init marimba_init(void)
+{
+	return i2c_add_driver(&marimba_driver);
+}
+module_init(marimba_init);
+
+static void __exit marimba_exit(void)
+{
+	i2c_del_driver(&marimba_driver);
+}
+module_exit(marimba_exit);
+
+MODULE_DESCRIPTION("Marimba Top level Driver");
+MODULE_ALIAS("platform:marimba-core");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.1");
diff --git a/drivers/mfd/marimba-tsadc.c b/drivers/mfd/marimba-tsadc.c
new file mode 100644
index 0000000..8a7b781
--- /dev/null
+++ b/drivers/mfd/marimba-tsadc.c
@@ -0,0 +1,696 @@
+/*
+ * Marimba TSADC driver.
+ *
+ * Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/mfd/marimba.h>
+#include <linux/mfd/marimba-tsadc.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+#include <linux/earlysuspend.h>
+#endif
+
+/* marimba configuration block: TS_CTL0 */
+#define TS_CTL0			0xFF
+#define TS_CTL0_RESET		BIT(0)
+#define TS_CTL0_CLK_EN		BIT(1)
+#define TS_CTL0_XO_EN		BIT(2)
+#define TS_CTL0_EOC_EN		BIT(3)
+#define TS_CTL0_PENIRQ_EN	BIT(4)
+
+/* TSADC registers */
+#define SSBI_PRESET		0x00
+#define TSHK_DIG_CONFIG		0x4F
+#define TSHK_INTF_CONFIG	0x50
+#define TSHK_SETUP		0x51
+	#define TSHK_SETUP_EN_ADC  BIT(0)
+	#define TSHK_SETUP_EN_PIRQ BIT(7)
+#define TSHK_PARAM		0x52
+#define TSHK_DATA_RD		0x53
+#define TSHK_STATUS		0x54
+#define TSHK_SETUP2		0x55
+#define TSHK_RSV1		0x56
+	#define TSHK_RSV1_PRECHARGE_EN	BIT(0)
+#define TSHK_COMMAND		0x57
+#define TSHK_PARAM2		0x58
+	#define TSHK_INPUT_CLK_MASK	0x3F
+	#define TSHK_SAMPLE_PRD_MASK	0xC7
+	#define TSHK_INPUT_CLK_SHIFT	0x6
+	#define TSHK_SAMPLE_PRD_SHIFT	0x3
+#define TSHK_PARAM3		0x59
+	#define TSHK_PARAM3_MODE_MASK	0xFC
+	#define TSHK_PARAM3_PRE_CHG_SHIFT (5)
+	#define TSHK_PARAM3_STABIZ_SHIFT (2)
+	#define TSHK_STABLE_TIME_MASK	0xE3
+	#define TSHK_PRECHG_TIME_MASK	0x1F
+#define TSHK_PARAM4		0x5A
+#define TSHK_RSV2		0x5B
+#define TSHK_RSV3		0x5C
+#define TSHK_RSV4		0x5D
+#define TSHK_RSV5		0x5E
+
+struct marimba_tsadc_client {
+	unsigned int is_ts;
+	struct platform_device *pdev;
+};
+
+struct marimba_tsadc {
+	struct marimba *marimba;
+	struct device *dev;
+	struct marimba_tsadc_platform_data *pdata;
+	struct clk	*codec_ssbi;
+	struct device *child_tssc;
+	bool clk_enabled;
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+	struct early_suspend		early_suspend;
+#endif
+};
+
+static struct marimba_tsadc *tsadc_dev;
+
+static int marimba_write_u8(struct marimba_tsadc *tsadc, u8 reg, u8 data)
+{
+	int rc;
+
+	tsadc->marimba->mod_id = MARIMBA_SLAVE_ID_MARIMBA;
+	rc = marimba_write(tsadc->marimba, reg, &data, 1);
+
+	if (!rc)
+		dev_warn(tsadc->dev, "Error writing marimba reg %X - ret %X\n",
+				reg, data);
+	return 0;
+}
+
+static int marimba_tsadc_write(struct marimba_tsadc *tsadc, u8 reg, u8 data)
+{
+	int rc;
+
+	tsadc->marimba->mod_id = MARIMBA_ID_TSADC;
+
+	rc = marimba_ssbi_write(tsadc->marimba, reg, &data, 1);
+	if (!rc)
+		dev_warn(tsadc->dev, "Error writing marimba reg %X - ret %X\n",
+				reg, data);
+	return rc;
+}
+
+static int marimba_tsadc_shutdown(struct marimba_tsadc *tsadc)
+{
+	u8 val;
+	int rc;
+
+	/* force reset */
+	val = TS_CTL0_XO_EN | TS_CTL0_EOC_EN | TS_CTL0_PENIRQ_EN |
+				TS_CTL0_CLK_EN;
+	rc = marimba_write_u8(tsadc, TS_CTL0, val);
+	if (rc < 0)
+		return rc;
+
+	/* disable xo, clock */
+	val = TS_CTL0_PENIRQ_EN | TS_CTL0_EOC_EN;
+	rc = marimba_write_u8(tsadc, TS_CTL0, val);
+	if (rc < 0)
+		return rc;
+
+	/* de-vote S2 1.3v */
+	if (tsadc->pdata->level_vote)
+		/* REVISIT: Ignore error for level_vote(0) for now*/
+		tsadc->pdata->level_vote(0);
+
+	return 0;
+}
+
+static int marimba_tsadc_startup(struct marimba_tsadc *tsadc)
+{
+	u8 val;
+	int rc = 0;
+
+	/* vote for S2 1.3v */
+	if (tsadc->pdata->level_vote) {
+		rc = tsadc->pdata->level_vote(1);
+		if (rc < 0)
+			return rc;
+	}
+
+	/* disable XO, clock and output enables */
+	rc = marimba_write_u8(tsadc, TS_CTL0, 0x00);
+	if (rc < 0)
+		goto fail_marimba_write;
+
+	/* Enable output enables */
+	val = TS_CTL0_XO_EN | TS_CTL0_EOC_EN | TS_CTL0_PENIRQ_EN;
+	rc = marimba_write_u8(tsadc, TS_CTL0, val);
+	if (rc < 0)
+		goto fail_marimba_write;
+
+	/* Enable clock */
+	val = val | TS_CTL0_CLK_EN;
+	rc = marimba_write_u8(tsadc, TS_CTL0, val);
+	if (rc < 0)
+		goto fail_marimba_write;
+
+	/* remove reset */
+	val = val | TS_CTL0_RESET;
+	rc = marimba_write_u8(tsadc, TS_CTL0, val);
+	if (rc < 0)
+		goto fail_marimba_write;
+
+	return 0;
+
+fail_marimba_write:
+	if (tsadc->pdata->level_vote)
+		/* REVISIT: Ignore error for level_vote(0) for now*/
+		tsadc->pdata->level_vote(0);
+	return rc;
+}
+
+
+static int marimba_tsadc_configure(struct marimba_tsadc *tsadc)
+{
+	u8 rsv1 = 0,  setup = 0, i, count = 0;
+	u8 param2 = 0,  param3 = 0;
+	unsigned long val;
+	int rc;
+
+	rc = marimba_tsadc_write(tsadc, SSBI_PRESET, 0x00);
+	if (rc < 0)
+		return rc;
+
+	if (!tsadc->pdata)
+		return -EINVAL;
+
+	/* Configure RSV1 register*/
+	if (tsadc->pdata->tsadc_prechg_en == true)
+		rsv1 |= TSHK_RSV1_PRECHARGE_EN;
+	else
+		rsv1 &= ~TSHK_RSV1_PRECHARGE_EN;
+
+	/*  Set RSV1 register*/
+	rc = marimba_tsadc_write(tsadc, TSHK_RSV1, rsv1);
+	if (rc < 0)
+		return rc;
+
+	/* Configure PARAM2 register */
+	/* Input clk */
+	val = tsadc->pdata->params2.input_clk_khz;
+	param2 &= TSHK_INPUT_CLK_MASK;
+	val /= 600;
+	if (val >= 1 && val <= 8 && !(val & (val - 1))) {
+		/* Input clk can be .6, 1.2, 2.4, 4.8Mhz */
+		if (val % 4 != 0)
+			param2 = (4 - (val % 4)) << TSHK_INPUT_CLK_SHIFT;
+		else
+			param2 = ((val / 4) - 1) << TSHK_INPUT_CLK_SHIFT;
+	} else /* Configure the default clk 2.4Mhz */
+		param2 = 0x00 << TSHK_INPUT_CLK_SHIFT;
+
+	/* Sample period */
+	param2 &= TSHK_SAMPLE_PRD_MASK;
+	param2 |=  tsadc->pdata->params2.sample_prd << TSHK_SAMPLE_PRD_SHIFT;
+
+	/* Write PARAM2 register */
+	rc = marimba_tsadc_write(tsadc, TSHK_PARAM2, param2);
+	if (rc < 0)
+		return rc;
+
+	/* REVISIT: If Precharge time, stabilization time  > 409.6us */
+	/* Configure PARAM3 register */
+	val = tsadc->pdata->params3.prechg_time_nsecs;
+	param3 &= TSHK_PRECHG_TIME_MASK;
+	val /= 6400;
+	if (val >= 1 && val <= 64  && !(val & (val - 1))) {
+		count = 0;
+		while ((val = val >> 1) != 0)
+			count++;
+		param3 |= count << TSHK_PARAM3_PRE_CHG_SHIFT;
+	} else	/* Set default value if the input is wrong */
+		param3 |= 0x00 << TSHK_PARAM3_PRE_CHG_SHIFT;
+
+	val = tsadc->pdata->params3.stable_time_nsecs;
+	param3 &= TSHK_STABLE_TIME_MASK;
+	val /= 6400;
+	if (val >= 1 && val <= 64 && !(val & (val - 1))) {
+		count = 0;
+		while ((val = val >> 1) != 0)
+			count++;
+		param3 |= count << TSHK_PARAM3_STABIZ_SHIFT;
+	} else /* Set default value if the input is wrong */
+		param3 |=  0x00 << TSHK_PARAM3_STABIZ_SHIFT;
+
+	/* Get TSADC mode */
+	val = tsadc->pdata->params3.tsadc_test_mode;
+	param3 &= TSHK_PARAM3_MODE_MASK;
+	if (val == 0)
+		param3 |= 0x00;
+	else
+		for (i = 0; i < 3 ; i++) {
+			if (((val + i) % 39322) == 0) {
+				param3 |= (i + 1);
+				break;
+			}
+		}
+	if (i == 3) /* Set to normal mode if input is wrong */
+		param3 |= 0x00;
+
+	rc = marimba_tsadc_write(tsadc, TSHK_PARAM3, param3);
+	if (rc < 0)
+		return rc;
+
+	/* Configure TSHK SETUP Register */
+	if (tsadc->pdata->setup.pen_irq_en == true)
+		setup |= TSHK_SETUP_EN_PIRQ;
+	else
+		setup &= ~TSHK_SETUP_EN_PIRQ;
+
+	if (tsadc->pdata->setup.tsadc_en == true)
+		setup |= TSHK_SETUP_EN_ADC;
+	else
+		setup &= ~TSHK_SETUP_EN_ADC;
+
+	/* Enable signals to ADC, pen irq assertion */
+	rc = marimba_tsadc_write(tsadc, TSHK_SETUP, setup);
+	if (rc < 0)
+		return rc;
+
+	return 0;
+}
+
+int marimba_tsadc_start(struct marimba_tsadc_client *client)
+{
+	int rc = 0;
+
+	if (!client) {
+		pr_err("%s: Not a valid client\n", __func__);
+		return -ENODEV;
+	}
+
+	if (!tsadc_dev) {
+		dev_err(&client->pdev->dev,
+			"%s: No tsadc device available\n", __func__);
+		return -ENODEV;
+	}
+
+	/* REVISIT - add locks */
+	if (client->is_ts) {
+		rc = marimba_tsadc_startup(tsadc_dev);
+		if (rc < 0)
+			goto fail_tsadc_startup;
+		rc = marimba_tsadc_configure(tsadc_dev);
+		if (rc < 0)
+			goto fail_tsadc_conf;
+	}
+
+	return 0;
+fail_tsadc_conf:
+	marimba_tsadc_shutdown(tsadc_dev);
+fail_tsadc_startup:
+	return rc;
+}
+EXPORT_SYMBOL(marimba_tsadc_start);
+
+struct marimba_tsadc_client *
+marimba_tsadc_register(struct platform_device *pdev, unsigned int is_ts)
+{
+	struct marimba_tsadc_client *client;
+
+	if (!pdev) {
+		pr_err("%s: valid platform device pointer please\n", __func__);
+		return ERR_PTR(-EINVAL);
+	}
+
+	if (!is_ts) {
+		dev_err(&pdev->dev, "%s: only TS right now\n", __func__);
+		return ERR_PTR(-EINVAL);
+	}
+
+	if (!tsadc_dev) {
+		dev_err(&pdev->dev,
+			"%s: No tsadc device available\n", __func__);
+		return ERR_PTR(-ENODEV);
+	}
+
+	client = kzalloc(sizeof *client, GFP_KERNEL);
+	if (!client)
+		return ERR_PTR(-ENOMEM);
+
+	client->pdev = pdev;
+	client->is_ts = is_ts;
+
+	return client;
+}
+EXPORT_SYMBOL(marimba_tsadc_register);
+
+void marimba_tsadc_unregister(struct marimba_tsadc_client *client)
+{
+	if (client->is_ts)
+		marimba_tsadc_shutdown(tsadc_dev);
+	kfree(client);
+}
+EXPORT_SYMBOL(marimba_tsadc_unregister);
+
+static struct resource resources_tssc[] = {
+	{
+		.start	= 0xAD300000,
+		.end	= 0xAD300000 + SZ_4K - 1,
+		.name	= "tssc",
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.start	= 55,
+		.end	= 55,
+		.name	= "tssc1",
+		.flags	= IORESOURCE_IRQ | IRQF_TRIGGER_RISING,
+	},
+	{
+		.start	= 56,
+		.end	= 56,
+		.name	= "tssc2",
+		.flags	= IORESOURCE_IRQ | IRQF_TRIGGER_RISING,
+	},
+};
+
+static struct device *
+marimba_add_tssc_subdev(struct device *parent, const char *name, int num,
+			 struct resource *resources, int num_resources,
+			 void *pdata, int pdata_len)
+{
+	struct platform_device	*pdev;
+	int			status;
+
+	pdev = platform_device_alloc(name, num);
+	if (!pdev) {
+		dev_dbg(parent, "can't alloc dev\n");
+		status = -ENOMEM;
+		goto err;
+	}
+
+	pdev->dev.parent = parent;
+
+	if (pdata) {
+		status = platform_device_add_data(pdev, pdata, pdata_len);
+		if (status < 0) {
+			dev_dbg(&pdev->dev, "can't add platform_data\n");
+			goto err;
+		}
+	}
+
+	status = platform_device_add_resources(pdev, resources, num_resources);
+	if (status < 0) {
+		dev_dbg(&pdev->dev, "can't add resources\n");
+		goto err;
+	}
+
+	status = platform_device_add(pdev);
+
+err:
+	if (status < 0) {
+		platform_device_put(pdev);
+		dev_err(parent, "can't add %s dev\n", name);
+		return ERR_PTR(status);
+	}
+	return &pdev->dev;
+}
+
+#ifdef CONFIG_PM
+static int
+marimba_tsadc_suspend(struct device *dev)
+{
+	int rc = 0, ret = 0;
+	struct marimba_tsadc *tsadc = dev_get_drvdata(dev);
+
+	if (tsadc->clk_enabled == true) {
+		clk_disable(tsadc->codec_ssbi);
+		tsadc->clk_enabled = false;
+	}
+
+	if (!(device_may_wakeup(dev) &&
+			device_may_wakeup(tsadc->child_tssc))) {
+		rc = marimba_tsadc_shutdown(tsadc);
+		if (rc < 0) {
+			pr_err("%s: Unable to shutdown TSADC\n", __func__);
+			goto fail_shutdown;
+		}
+
+		if (tsadc->pdata->marimba_tsadc_power) {
+			rc = tsadc->pdata->marimba_tsadc_power(0);
+			if (rc < 0)
+				goto fail_tsadc_power;
+		}
+	}
+	return rc;
+
+fail_tsadc_power:
+	marimba_tsadc_startup(tsadc_dev);
+	marimba_tsadc_configure(tsadc_dev);
+fail_shutdown:
+	if (tsadc->clk_enabled == false) {
+		ret = clk_enable(tsadc->codec_ssbi);
+		if (ret == 0)
+			tsadc->clk_enabled = true;
+	}
+	return rc;
+}
+
+static int marimba_tsadc_resume(struct device *dev)
+{
+	int rc = 0;
+	struct marimba_tsadc *tsadc = dev_get_drvdata(dev);
+
+	if (tsadc->clk_enabled == false) {
+		rc = clk_enable(tsadc->codec_ssbi);
+		if (rc != 0) {
+			pr_err("%s: Clk enable failed\n", __func__);
+			return rc;
+		}
+		tsadc->clk_enabled = true;
+	}
+
+	if (!(device_may_wakeup(dev) &&
+			device_may_wakeup(tsadc->child_tssc))) {
+		if (tsadc->pdata->marimba_tsadc_power) {
+			rc = tsadc->pdata->marimba_tsadc_power(1);
+			if (rc) {
+				pr_err("%s: Unable to power on TSADC \n",
+						__func__);
+				goto fail_tsadc_power;
+			}
+		}
+
+		rc = marimba_tsadc_startup(tsadc_dev);
+		if (rc < 0) {
+			pr_err("%s: Unable to startup TSADC\n", __func__);
+			goto fail_tsadc_startup;
+		}
+
+		rc = marimba_tsadc_configure(tsadc_dev);
+		if (rc < 0) {
+			pr_err("%s: Unable to configure TSADC\n", __func__);
+			goto fail_tsadc_configure;
+		}
+	}
+	return rc;
+
+fail_tsadc_configure:
+	marimba_tsadc_shutdown(tsadc_dev);
+fail_tsadc_startup:
+	if (tsadc->pdata->marimba_tsadc_power)
+		tsadc->pdata->marimba_tsadc_power(0);
+fail_tsadc_power:
+	if (tsadc->clk_enabled == true) {
+		clk_disable(tsadc->codec_ssbi);
+		tsadc->clk_enabled = false;
+	}
+	return rc;
+}
+
+static struct dev_pm_ops tsadc_pm_ops = {
+#ifndef CONFIG_HAS_EARLYSUSPEND
+	.suspend = marimba_tsadc_suspend,
+	.resume = marimba_tsadc_resume,
+#endif
+};
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void marimba_tsadc_early_suspend(struct early_suspend *h)
+{
+	struct marimba_tsadc *tsadc = container_of(h, struct marimba_tsadc,
+						 early_suspend);
+
+	marimba_tsadc_suspend(tsadc->dev);
+}
+
+static void marimba_tsadc_late_resume(struct early_suspend *h)
+{
+	struct marimba_tsadc *tsadc = container_of(h, struct marimba_tsadc,
+						 early_suspend);
+
+	marimba_tsadc_resume(tsadc->dev);
+}
+#endif
+
+static int __devinit marimba_tsadc_probe(struct platform_device *pdev)
+{
+	struct marimba *marimba = platform_get_drvdata(pdev);
+	struct marimba_tsadc *tsadc;
+	struct marimba_tsadc_platform_data *pdata = pdev->dev.platform_data;
+	int rc = 0;
+	struct device *child;
+
+	printk("%s\n", __func__);
+
+	if (!pdata) {
+		dev_dbg(&pdev->dev, "no tsadc platform data?\n");
+		return -EINVAL;
+	}
+
+	tsadc = kzalloc(sizeof *tsadc, GFP_KERNEL);
+	if (!tsadc)
+		return -ENOMEM;
+
+	tsadc->marimba	= marimba;
+	tsadc->dev	= &pdev->dev;
+	tsadc->pdata	= pdata;
+
+	platform_set_drvdata(pdev, tsadc);
+
+	if (tsadc->pdata->init) {
+		rc = tsadc->pdata->init();
+		if (rc < 0)
+			goto fail_tsadc_init;
+	}
+
+	if (tsadc->pdata->marimba_tsadc_power) {
+		rc = tsadc->pdata->marimba_tsadc_power(1);
+		if (rc) {
+			pr_err("%s: Unable to power up TSADC \n", __func__);
+			goto fail_tsadc_power;
+		}
+	}
+
+	tsadc->codec_ssbi = clk_get(NULL, "codec_ssbi_clk");
+	if (IS_ERR(tsadc->codec_ssbi)) {
+		rc = PTR_ERR(tsadc->codec_ssbi);
+		goto fail_clk_get;
+	}
+	rc = clk_enable(tsadc->codec_ssbi);
+	if (rc != 0)
+		goto fail_clk_enable;
+
+	tsadc->clk_enabled = true;
+
+	child = marimba_add_tssc_subdev(&pdev->dev, "msm_touchscreen", -1,
+			 resources_tssc, ARRAY_SIZE(resources_tssc),
+			 pdata->tssc_data, sizeof(*pdata->tssc_data));
+
+	if (IS_ERR(child)) {
+		rc = PTR_ERR(child);
+		goto fail_add_subdev;
+	}
+
+	tsadc->child_tssc = child;
+	platform_set_drvdata(pdev, tsadc);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	tsadc->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN +
+						 TSADC_SUSPEND_LEVEL;
+	tsadc->early_suspend.suspend = marimba_tsadc_early_suspend;
+	tsadc->early_suspend.resume = marimba_tsadc_late_resume;
+	register_early_suspend(&tsadc->early_suspend);
+#endif
+
+	tsadc_dev = tsadc;
+	device_init_wakeup(&pdev->dev, pdata->can_wakeup);
+
+	return rc;
+
+fail_add_subdev:
+	clk_disable(tsadc->codec_ssbi);
+
+fail_clk_enable:
+	clk_put(tsadc->codec_ssbi);
+
+fail_clk_get:
+	if (tsadc->pdata->marimba_tsadc_power)
+		rc = tsadc->pdata->marimba_tsadc_power(0);
+fail_tsadc_power:
+	if (tsadc->pdata->exit)
+		rc = tsadc->pdata->exit();
+fail_tsadc_init:
+	kfree(tsadc);
+	return rc;
+}
+
+static int __devexit marimba_tsadc_remove(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct marimba_tsadc *tsadc = platform_get_drvdata(pdev);
+
+	device_init_wakeup(&pdev->dev, 0);
+
+	if (tsadc->clk_enabled == true)
+		clk_disable(tsadc->codec_ssbi);
+
+	clk_put(tsadc->codec_ssbi);
+
+	if (tsadc->pdata->exit)
+		rc = tsadc->pdata->exit();
+
+	if (tsadc->pdata->marimba_tsadc_power)
+		rc = tsadc->pdata->marimba_tsadc_power(0);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	unregister_early_suspend(&tsadc->early_suspend);
+#endif
+
+	platform_set_drvdata(pdev, NULL);
+	kfree(tsadc);
+	return rc;
+}
+
+static struct platform_driver tsadc_driver = {
+	.probe	= marimba_tsadc_probe,
+	.remove	= __devexit_p(marimba_tsadc_remove),
+	.driver	= {
+		.name = "marimba_tsadc",
+		.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm = &tsadc_pm_ops,
+#endif
+	},
+};
+
+static int __init marimba_tsadc_init(void)
+{
+	return platform_driver_register(&tsadc_driver);
+}
+device_initcall(marimba_tsadc_init);
+
+static void __exit marimba_tsadc_exit(void)
+{
+	return platform_driver_unregister(&tsadc_driver);
+}
+module_exit(marimba_tsadc_exit);
+
+MODULE_DESCRIPTION("Marimba TSADC driver");
+MODULE_VERSION("0.1");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:marimba_tsadc");
diff --git a/drivers/mfd/msm-adie-codec.c b/drivers/mfd/msm-adie-codec.c
new file mode 100644
index 0000000..d9414ed
--- /dev/null
+++ b/drivers/mfd/msm-adie-codec.c
@@ -0,0 +1,196 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/msm-adie-codec.h>
+#include <linux/mfd/marimba.h>
+
+static const struct adie_codec_operations *cur_adie_ops;
+
+int adie_codec_register_codec_operations(
+			const struct adie_codec_operations *adie_ops)
+{
+	if (adie_ops == NULL)
+		return -EINVAL;
+
+	if (adie_ops->codec_id != adie_get_detected_codec_type())
+		return -EINVAL;
+
+	cur_adie_ops = adie_ops;
+	pr_info("%s: codec type %d\n", __func__, adie_ops->codec_id);
+	return 0;
+}
+
+int adie_codec_open(struct adie_codec_dev_profile *profile,
+	struct adie_codec_path **path_pptr)
+{
+	int rc = -EPERM;
+
+	if (cur_adie_ops != NULL) {
+		if (cur_adie_ops->codec_open != NULL)
+			rc = cur_adie_ops->codec_open(profile, path_pptr);
+	} else
+		rc = -ENODEV;
+
+	return rc;
+}
+EXPORT_SYMBOL(adie_codec_open);
+
+int adie_codec_close(struct adie_codec_path *path_ptr)
+{
+	int rc = -EPERM;
+
+	if (cur_adie_ops != NULL) {
+		if (cur_adie_ops->codec_close != NULL)
+			rc = cur_adie_ops->codec_close(path_ptr);
+	} else
+		rc = -ENODEV;
+
+	return rc;
+}
+EXPORT_SYMBOL(adie_codec_close);
+
+int adie_codec_set_device_digital_volume(struct adie_codec_path *path_ptr,
+		u32 num_channels, u32 vol_percentage /* in percentage */)
+{
+	int rc = -EPERM;
+
+	if (cur_adie_ops != NULL) {
+		if (cur_adie_ops->codec_set_device_digital_volume != NULL) {
+			rc = cur_adie_ops->codec_set_device_digital_volume(
+							path_ptr,
+							num_channels,
+							vol_percentage);
+		}
+	} else
+		rc = -ENODEV;
+
+	return rc;
+}
+EXPORT_SYMBOL(adie_codec_set_device_digital_volume);
+
+int adie_codec_set_device_analog_volume(struct adie_codec_path *path_ptr,
+		u32 num_channels, u32 volume /* in percentage */)
+{
+	int rc = -EPERM;
+
+	if (cur_adie_ops != NULL) {
+		if (cur_adie_ops->codec_set_device_analog_volume != NULL) {
+			rc = cur_adie_ops->codec_set_device_analog_volume(
+							path_ptr,
+							num_channels,
+							volume);
+		}
+	} else
+		rc = -ENODEV;
+
+	return rc;
+}
+EXPORT_SYMBOL(adie_codec_set_device_analog_volume);
+
+int adie_codec_setpath(struct adie_codec_path *path_ptr, u32 freq_plan, u32 osr)
+{
+	int rc = -EPERM;
+
+	if (cur_adie_ops != NULL) {
+		if (cur_adie_ops->codec_setpath != NULL) {
+			rc = cur_adie_ops->codec_setpath(path_ptr,
+							freq_plan,
+							osr);
+		}
+	} else
+		rc = -ENODEV;
+
+	return rc;
+}
+EXPORT_SYMBOL(adie_codec_setpath);
+
+u32 adie_codec_freq_supported(struct adie_codec_dev_profile *profile,
+	u32 requested_freq)
+{
+	int rc = -EPERM;
+
+	if (cur_adie_ops != NULL) {
+		if (cur_adie_ops->codec_freq_supported != NULL)
+			rc = cur_adie_ops->codec_freq_supported(profile,
+							requested_freq);
+	} else
+		rc = -ENODEV;
+
+	return rc;
+}
+EXPORT_SYMBOL(adie_codec_freq_supported);
+
+int adie_codec_enable_sidetone(struct adie_codec_path *rx_path_ptr,
+	u32 enable)
+{
+	int rc = -EPERM;
+
+	if (cur_adie_ops != NULL) {
+		if (cur_adie_ops->codec_enable_sidetone != NULL)
+			rc = cur_adie_ops->codec_enable_sidetone(rx_path_ptr,
+								enable);
+	} else
+		rc = -ENODEV;
+
+	return rc;
+}
+EXPORT_SYMBOL(adie_codec_enable_sidetone);
+
+int adie_codec_enable_anc(struct adie_codec_path *rx_path_ptr,
+	u32 enable, struct adie_codec_anc_data *calibration_writes)
+{
+	int rc = -EPERM;
+
+	if (cur_adie_ops != NULL) {
+		if (cur_adie_ops->codec_enable_anc != NULL)
+			rc = cur_adie_ops->codec_enable_anc(rx_path_ptr,
+				enable, calibration_writes);
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(adie_codec_enable_anc);
+
+int adie_codec_proceed_stage(struct adie_codec_path *path_ptr, u32 state)
+{
+	int rc = -EPERM;
+
+	if (cur_adie_ops != NULL) {
+		if (cur_adie_ops->codec_proceed_stage != NULL)
+			rc = cur_adie_ops->codec_proceed_stage(path_ptr,
+								state);
+	} else
+		rc = -ENODEV;
+
+	return rc;
+}
+EXPORT_SYMBOL(adie_codec_proceed_stage);
+
+int adie_codec_set_master_mode(struct adie_codec_path *path_ptr, u8 master)
+{
+	int rc = -EPERM;
+
+	if (cur_adie_ops != NULL) {
+		if (cur_adie_ops->codec_set_master_mode != NULL)
+			rc = cur_adie_ops->codec_set_master_mode(path_ptr,
+					master);
+	} else
+		rc = -ENODEV;
+
+	return rc;
+}
+EXPORT_SYMBOL(adie_codec_set_master_mode);
+
+
diff --git a/drivers/mfd/pm8018-core.c b/drivers/mfd/pm8018-core.c
new file mode 100644
index 0000000..b1b64cb
--- /dev/null
+++ b/drivers/mfd/pm8018-core.c
@@ -0,0 +1,674 @@
+/*
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/err.h>
+#include <linux/msm_ssbi.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/pm8xxx/pm8018.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/regulator.h>
+#include <linux/leds-pm8xxx.h>
+
+
+/* PMIC PM8018 SSBI Addresses */
+#define REG_HWREV		0x002  /* PMIC4 revision */
+#define REG_HWREV_2		0x0E8  /* PMIC4 revision 2 */
+
+#define REG_MPP_BASE		0x050
+#define REG_IRQ_BASE		0x1BB
+
+#define REG_RTC_BASE		0x11D
+
+#define REG_TEMP_ALARM_CTRL	0x01B
+#define REG_TEMP_ALARM_PWM	0x09B
+
+
+#define PM8018_VERSION_MASK	0xFFF0
+#define PM8018_VERSION_VALUE	0x08F0
+#define PM8018_REVISION_MASK	0x000F
+
+#define REG_PM8018_PON_CNTRL_3	0x01D
+#define PM8018_RESTART_REASON_MASK	0x07
+
+#define SINGLE_IRQ_RESOURCE(_name, _irq) \
+{ \
+	.name	= _name, \
+	.start	= _irq, \
+	.end	= _irq, \
+	.flags	= IORESOURCE_IRQ, \
+}
+
+struct pm8018 {
+	struct device					*dev;
+	struct pm_irq_chip				*irq_chip;
+	struct mfd_cell					*mfd_regulators;
+	struct pm8xxx_regulator_core_platform_data	*regulator_cdata;
+	u32						rev_registers;
+};
+
+static int pm8018_readb(const struct device *dev, u16 addr, u8 *val)
+{
+	const struct pm8xxx_drvdata *pm8018_drvdata = dev_get_drvdata(dev);
+	const struct pm8018 *pmic = pm8018_drvdata->pm_chip_data;
+
+	return msm_ssbi_read(pmic->dev->parent, addr, val, 1);
+}
+
+static int pm8018_writeb(const struct device *dev, u16 addr, u8 val)
+{
+	const struct pm8xxx_drvdata *pm8018_drvdata = dev_get_drvdata(dev);
+	const struct pm8018 *pmic = pm8018_drvdata->pm_chip_data;
+
+	return msm_ssbi_write(pmic->dev->parent, addr, &val, 1);
+}
+
+static int pm8018_read_buf(const struct device *dev, u16 addr, u8 *buf,
+									int cnt)
+{
+	const struct pm8xxx_drvdata *pm8018_drvdata = dev_get_drvdata(dev);
+	const struct pm8018 *pmic = pm8018_drvdata->pm_chip_data;
+
+	return msm_ssbi_read(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8018_write_buf(const struct device *dev, u16 addr, u8 *buf,
+									int cnt)
+{
+	const struct pm8xxx_drvdata *pm8018_drvdata = dev_get_drvdata(dev);
+	const struct pm8018 *pmic = pm8018_drvdata->pm_chip_data;
+
+	return msm_ssbi_write(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8018_read_irq_stat(const struct device *dev, int irq)
+{
+	const struct pm8xxx_drvdata *pm8018_drvdata = dev_get_drvdata(dev);
+	const struct pm8018 *pmic = pm8018_drvdata->pm_chip_data;
+
+	return pm8xxx_get_irq_stat(pmic->irq_chip, irq);
+}
+
+static enum pm8xxx_version pm8018_get_version(const struct device *dev)
+{
+	const struct pm8xxx_drvdata *pm8018_drvdata = dev_get_drvdata(dev);
+	const struct pm8018 *pmic = pm8018_drvdata->pm_chip_data;
+	enum pm8xxx_version version = -ENODEV;
+
+	if ((pmic->rev_registers & PM8018_VERSION_MASK) == PM8018_VERSION_VALUE)
+		version = PM8XXX_VERSION_8018;
+
+	return version;
+}
+
+static int pm8018_get_revision(const struct device *dev)
+{
+	const struct pm8xxx_drvdata *pm8018_drvdata = dev_get_drvdata(dev);
+	const struct pm8018 *pmic = pm8018_drvdata->pm_chip_data;
+
+	return pmic->rev_registers & PM8018_REVISION_MASK;
+}
+
+static struct pm8xxx_drvdata pm8018_drvdata = {
+	.pmic_readb		= pm8018_readb,
+	.pmic_writeb		= pm8018_writeb,
+	.pmic_read_buf		= pm8018_read_buf,
+	.pmic_write_buf		= pm8018_write_buf,
+	.pmic_read_irq_stat	= pm8018_read_irq_stat,
+	.pmic_get_version	= pm8018_get_version,
+	.pmic_get_revision	= pm8018_get_revision,
+};
+
+static const struct resource gpio_cell_resources[] __devinitconst = {
+	[0] = {
+		.start = PM8018_IRQ_BLOCK_BIT(PM8018_GPIO_BLOCK_START, 0),
+		.end   = PM8018_IRQ_BLOCK_BIT(PM8018_GPIO_BLOCK_START, 0)
+			+ PM8018_NR_GPIOS - 1,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct mfd_cell gpio_cell __devinitdata = {
+	.name		= PM8XXX_GPIO_DEV_NAME,
+	.id		= -1,
+	.resources	= gpio_cell_resources,
+	.num_resources	= ARRAY_SIZE(gpio_cell_resources),
+};
+
+static const struct resource adc_cell_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE(NULL, PM8018_ADC_EOC_USR_IRQ),
+	SINGLE_IRQ_RESOURCE(NULL, PM8018_ADC_BATT_TEMP_WARM_IRQ),
+	SINGLE_IRQ_RESOURCE(NULL, PM8018_ADC_BATT_TEMP_COLD_IRQ),
+};
+
+static struct mfd_cell adc_cell __devinitdata = {
+	.name		= PM8XXX_ADC_DEV_NAME,
+	.id		= -1,
+	.resources	= adc_cell_resources,
+	.num_resources	= ARRAY_SIZE(adc_cell_resources),
+};
+
+static const struct resource mpp_cell_resources[] __devinitconst = {
+	{
+		.start	= PM8018_IRQ_BLOCK_BIT(PM8018_MPP_BLOCK_START, 0),
+		.end	= PM8018_IRQ_BLOCK_BIT(PM8018_MPP_BLOCK_START, 0)
+			  + PM8018_NR_MPPS - 1,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct mfd_cell mpp_cell __devinitdata = {
+	.name		= PM8XXX_MPP_DEV_NAME,
+	.id		= -1,
+	.resources	= mpp_cell_resources,
+	.num_resources	= ARRAY_SIZE(mpp_cell_resources),
+};
+
+static const struct resource rtc_cell_resources[] __devinitconst = {
+	[0] = SINGLE_IRQ_RESOURCE(NULL, PM8018_RTC_ALARM_IRQ),
+	[1] = {
+		.name   = "pmic_rtc_base",
+		.start  = REG_RTC_BASE,
+		.end    = REG_RTC_BASE,
+		.flags  = IORESOURCE_IO,
+	},
+};
+
+static struct mfd_cell rtc_cell __devinitdata = {
+	.name           = PM8XXX_RTC_DEV_NAME,
+	.id             = -1,
+	.resources      = rtc_cell_resources,
+	.num_resources  = ARRAY_SIZE(rtc_cell_resources),
+};
+
+static const struct resource resources_pwrkey[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE(NULL, PM8018_PWRKEY_REL_IRQ),
+	SINGLE_IRQ_RESOURCE(NULL, PM8018_PWRKEY_PRESS_IRQ),
+};
+
+static struct mfd_cell pwrkey_cell __devinitdata = {
+	.name		= PM8XXX_PWRKEY_DEV_NAME,
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(resources_pwrkey),
+	.resources	= resources_pwrkey,
+};
+
+static struct mfd_cell misc_cell __devinitdata = {
+	.name           = PM8XXX_MISC_DEV_NAME,
+	.id             = -1,
+};
+
+static struct mfd_cell debugfs_cell __devinitdata = {
+	.name		= "pm8xxx-debug",
+	.id		= -1,
+	.platform_data	= "pm8018-dbg",
+	.pdata_size	= sizeof("pm8018-dbg"),
+};
+
+static struct mfd_cell pwm_cell __devinitdata = {
+	.name           = PM8XXX_PWM_DEV_NAME,
+	.id             = -1,
+};
+
+static struct mfd_cell leds_cell __devinitdata = {
+	.name		= PM8XXX_LEDS_DEV_NAME,
+	.id		= -1,
+};
+
+static const struct resource thermal_alarm_cell_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE("pm8018_tempstat_irq", PM8018_TEMPSTAT_IRQ),
+	SINGLE_IRQ_RESOURCE("pm8018_overtemp_irq", PM8018_OVERTEMP_IRQ),
+};
+
+static struct pm8xxx_tm_core_data thermal_alarm_cdata = {
+	.adc_channel =			CHANNEL_DIE_TEMP,
+	.adc_type =			PM8XXX_TM_ADC_PM8XXX_ADC,
+	.reg_addr_temp_alarm_ctrl =	REG_TEMP_ALARM_CTRL,
+	.reg_addr_temp_alarm_pwm =	REG_TEMP_ALARM_PWM,
+	.tm_name =			"pm8018_tz",
+	.irq_name_temp_stat =		"pm8018_tempstat_irq",
+	.irq_name_over_temp =		"pm8018_overtemp_irq",
+};
+
+static struct mfd_cell thermal_alarm_cell __devinitdata = {
+	.name		= PM8XXX_TM_DEV_NAME,
+	.id		= -1,
+	.resources	= thermal_alarm_cell_resources,
+	.num_resources	= ARRAY_SIZE(thermal_alarm_cell_resources),
+	.platform_data	= &thermal_alarm_cdata,
+	.pdata_size	= sizeof(struct pm8xxx_tm_core_data),
+};
+
+static struct pm8xxx_vreg regulator_data[] = {
+	/*   name	     pc_name	    ctrl   test   hpm_min */
+	PLDO("8018_l2",      "8018_l2_pc",  0x0B0, 0x0B1, LDO_50),
+	PLDO("8018_l3",      "8018_l3_pc",  0x0B2, 0x0B3, LDO_50),
+	PLDO("8018_l4",      "8018_l4_pc",  0x0B4, 0x0B5, LDO_300),
+	PLDO("8018_l5",      "8018_l5_pc",  0x0B6, 0x0B7, LDO_150),
+	PLDO("8018_l6",      "8018_l6_pc",  0x0B8, 0x0B9, LDO_150),
+	PLDO("8018_l7",      "8018_l7_pc",  0x0BA, 0x0BB, LDO_300),
+	NLDO("8018_l8",      "8018_l8_pc",  0x0BC, 0x0BD, LDO_150),
+	NLDO1200("8018_l9",		    0x0BE, 0x0BF, LDO_1200),
+	NLDO1200("8018_l10",		    0x0C0, 0x0C1, LDO_1200),
+	NLDO1200("8018_l11",		    0x0C2, 0x0C3, LDO_1200),
+	NLDO1200("8018_l12",		    0x0C4, 0x0C5, LDO_1200),
+	PLDO("8018_l13",     "8018_l13_pc", 0x0C8, 0x0C9, LDO_50),
+	PLDO("8018_l14",     "8018_l14_pc", 0x0CA, 0x0CB, LDO_50),
+
+	/*   name	pc_name       ctrl   test2  clk    sleep  hpm_min */
+	SMPS("8018_s1", "8018_s1_pc", 0x1D0, 0x1D5, 0x009, 0x1D2, SMPS_1500),
+	SMPS("8018_s2", "8018_s2_pc", 0x1D8, 0x1DD, 0x00A, 0x1DA, SMPS_1500),
+	SMPS("8018_s3", "8018_s3_pc", 0x1E0, 0x1E5, 0x00B, 0x1E2, SMPS_1500),
+	SMPS("8018_s4", "8018_s4_pc", 0x1E8, 0x1ED, 0x00C, 0x1EA, SMPS_1500),
+	SMPS("8018_s5", "8018_s5_pc", 0x1F0, 0x1F5, 0x00D, 0x1F2, SMPS_1500),
+
+	/* name		     pc_name	     ctrl   test */
+	VS("8018_lvs1",      "8018_lvs1_pc", 0x060, 0x061),
+};
+
+#define MAX_NAME_COMPARISON_LEN 32
+
+static int __devinit match_regulator(
+	struct pm8xxx_regulator_core_platform_data *core_data, const char *name)
+{
+	int found = 0;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(regulator_data); i++) {
+		if (regulator_data[i].rdesc.name
+		    && strncmp(regulator_data[i].rdesc.name, name,
+				MAX_NAME_COMPARISON_LEN) == 0) {
+			core_data->is_pin_controlled = false;
+			core_data->vreg = &regulator_data[i];
+			found = 1;
+			break;
+		} else if (regulator_data[i].rdesc_pc.name
+			   && strncmp(regulator_data[i].rdesc_pc.name, name,
+				MAX_NAME_COMPARISON_LEN) == 0) {
+			core_data->is_pin_controlled = true;
+			core_data->vreg = &regulator_data[i];
+			found = 1;
+			break;
+		}
+	}
+
+	if (!found)
+		pr_err("could not find a match for regulator: %s\n", name);
+
+	return found;
+}
+
+static int __devinit
+pm8018_add_regulators(const struct pm8018_platform_data *pdata,
+		      struct pm8018 *pmic, int irq_base)
+{
+	int ret = 0;
+	struct mfd_cell *mfd_regulators;
+	struct pm8xxx_regulator_core_platform_data *cdata;
+	int i;
+
+	/* Add one device for each regulator used by the board. */
+	mfd_regulators = kzalloc(sizeof(struct mfd_cell)
+				 * (pdata->num_regulators), GFP_KERNEL);
+	if (!mfd_regulators) {
+		pr_err("Cannot allocate %d bytes for pm8018 regulator "
+			"mfd cells\n", sizeof(struct mfd_cell)
+					* (pdata->num_regulators));
+		return -ENOMEM;
+	}
+	cdata = kzalloc(sizeof(struct pm8xxx_regulator_core_platform_data)
+			* pdata->num_regulators, GFP_KERNEL);
+	if (!cdata) {
+		pr_err("Cannot allocate %d bytes for pm8018 regulator "
+			"core data\n", pdata->num_regulators
+			  * sizeof(struct pm8xxx_regulator_core_platform_data));
+		kfree(mfd_regulators);
+		return -ENOMEM;
+	}
+	for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
+		mutex_init(&regulator_data[i].pc_lock);
+
+	for (i = 0; i < pdata->num_regulators; i++) {
+		if (!pdata->regulator_pdatas[i].init_data.constraints.name) {
+			pr_err("name missing for regulator %d\n", i);
+			ret = -EINVAL;
+			goto bail;
+		}
+		if (!match_regulator(&cdata[i],
+		      pdata->regulator_pdatas[i].init_data.constraints.name)) {
+			ret = -ENODEV;
+			goto bail;
+		}
+		cdata[i].pdata = &(pdata->regulator_pdatas[i]);
+		mfd_regulators[i].name = PM8XXX_REGULATOR_DEV_NAME;
+		mfd_regulators[i].id = cdata[i].pdata->id;
+		mfd_regulators[i].platform_data = &cdata[i];
+		mfd_regulators[i].pdata_size =
+			sizeof(struct pm8xxx_regulator_core_platform_data);
+	}
+	ret = mfd_add_devices(pmic->dev, 0, mfd_regulators,
+			pdata->num_regulators, NULL, irq_base);
+	if (ret)
+		goto bail;
+
+	pmic->mfd_regulators = mfd_regulators;
+	pmic->regulator_cdata = cdata;
+	return ret;
+
+bail:
+	for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
+		mutex_destroy(&regulator_data[i].pc_lock);
+	kfree(mfd_regulators);
+	kfree(cdata);
+	return ret;
+}
+
+static int __devinit
+pm8018_add_subdevices(const struct pm8018_platform_data *pdata,
+		      struct pm8018 *pmic)
+{
+	int ret = 0, irq_base = 0;
+	struct pm_irq_chip *irq_chip;
+
+	if (pdata->irq_pdata) {
+		pdata->irq_pdata->irq_cdata.nirqs = PM8018_NR_IRQS;
+		pdata->irq_pdata->irq_cdata.base_addr = REG_IRQ_BASE;
+		irq_base = pdata->irq_pdata->irq_base;
+		irq_chip = pm8xxx_irq_init(pmic->dev, pdata->irq_pdata);
+
+		if (IS_ERR(irq_chip)) {
+			pr_err("Failed to init interrupts ret=%ld\n",
+					PTR_ERR(irq_chip));
+			return PTR_ERR(irq_chip);
+		}
+		pmic->irq_chip = irq_chip;
+	}
+
+	if (pdata->gpio_pdata) {
+		pdata->gpio_pdata->gpio_cdata.ngpios = PM8018_NR_GPIOS;
+		gpio_cell.platform_data = pdata->gpio_pdata;
+		gpio_cell.pdata_size = sizeof(struct pm8xxx_gpio_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &gpio_cell, 1,
+					NULL, irq_base);
+		if (ret) {
+			pr_err("Failed to add  gpio subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->mpp_pdata) {
+		pdata->mpp_pdata->core_data.nmpps = PM8018_NR_MPPS;
+		pdata->mpp_pdata->core_data.base_addr = REG_MPP_BASE;
+		mpp_cell.platform_data = pdata->mpp_pdata;
+		mpp_cell.pdata_size = sizeof(struct pm8xxx_mpp_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &mpp_cell, 1, NULL,
+					irq_base);
+		if (ret) {
+			pr_err("Failed to add mpp subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->rtc_pdata) {
+		rtc_cell.platform_data = pdata->rtc_pdata;
+		rtc_cell.pdata_size = sizeof(struct pm8xxx_rtc_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &rtc_cell, 1, NULL,
+				irq_base);
+		if (ret) {
+			pr_err("Failed to add rtc subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->pwrkey_pdata) {
+		pwrkey_cell.platform_data = pdata->pwrkey_pdata;
+		pwrkey_cell.pdata_size =
+			sizeof(struct pm8xxx_pwrkey_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &pwrkey_cell, 1, NULL,
+					irq_base);
+		if (ret) {
+			pr_err("Failed to add pwrkey subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->misc_pdata) {
+		misc_cell.platform_data = pdata->misc_pdata;
+		misc_cell.pdata_size = sizeof(struct pm8xxx_misc_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &misc_cell, 1, NULL,
+				      irq_base);
+		if (ret) {
+			pr_err("Failed to add  misc subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->adc_pdata) {
+		adc_cell.platform_data = pdata->adc_pdata;
+		adc_cell.pdata_size = sizeof(struct pm8xxx_adc_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &adc_cell, 1, NULL,
+				      irq_base);
+		if (ret) {
+			pr_err("Failed to add adc subdevice ret=%d\n", ret);
+		}
+	}
+
+	if (pdata->leds_pdata) {
+		leds_cell.platform_data = pdata->leds_pdata;
+		leds_cell.pdata_size = sizeof(struct pm8xxx_led_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &leds_cell, 1, NULL, 0);
+		if (ret) {
+			pr_err("Failed to add leds subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	ret = mfd_add_devices(pmic->dev, 0, &debugfs_cell, 1, NULL, irq_base);
+	if (ret) {
+		pr_err("Failed to add debugfs subdevice ret=%d\n", ret);
+		goto bail;
+	}
+
+	ret = mfd_add_devices(pmic->dev, 0, &pwm_cell, 1, NULL, 0);
+	if (ret) {
+		pr_err("Failed to add pwm subdevice ret=%d\n", ret);
+		goto bail;
+	}
+
+	if (pdata->num_regulators > 0 && pdata->regulator_pdatas) {
+		ret = pm8018_add_regulators(pdata, pmic, irq_base);
+		if (ret) {
+			pr_err("Failed to add regulator subdevices ret=%d\n",
+				ret);
+			goto bail;
+		}
+	}
+
+	ret = mfd_add_devices(pmic->dev, 0, &thermal_alarm_cell, 1, NULL,
+				irq_base);
+	if (ret) {
+		pr_err("Failed to add thermal alarm subdevice, ret=%d\n", ret);
+		goto bail;
+	}
+
+	return 0;
+bail:
+	if (pmic->irq_chip) {
+		pm8xxx_irq_exit(pmic->irq_chip);
+		pmic->irq_chip = NULL;
+	}
+	return ret;
+}
+
+static const char * const pm8018_restart_reason[] = {
+	[0] = "Unknown",
+	[1] = "Triggered from CBL (external charger)",
+	[2] = "Triggered from KPD (power key press)",
+	[3] = "Triggered from CHG (usb charger insertion)",
+	[4] = "Triggered from SMPL (sudden momentary power loss)",
+	[5] = "Triggered from RTC (real time clock)",
+	[6] = "Triggered by Hard Reset",
+	[7] = "Triggered by General Purpose Trigger",
+};
+
+static const char * const pm8018_rev_names[] = {
+	[PM8XXX_REVISION_8018_TEST]	= "test",
+	[PM8XXX_REVISION_8018_1p0]	= "1.0",
+	[PM8XXX_REVISION_8018_2p0]	= "2.0",
+	[PM8XXX_REVISION_8018_2p1]	= "2.1",
+};
+
+static int __devinit pm8018_probe(struct platform_device *pdev)
+{
+	const struct pm8018_platform_data *pdata = pdev->dev.platform_data;
+	const char *revision_name = "unknown";
+	struct pm8018 *pmic;
+	enum pm8xxx_version version;
+	int revision;
+	int rc;
+	u8 val;
+
+	if (!pdata) {
+		pr_err("missing platform data\n");
+		return -EINVAL;
+	}
+
+	pmic = kzalloc(sizeof(struct pm8018), GFP_KERNEL);
+	if (!pmic) {
+		pr_err("Cannot alloc pm8018 struct\n");
+		return -ENOMEM;
+	}
+
+	/* Read PMIC chip revision */
+	rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV, &val, sizeof(val));
+	if (rc) {
+		pr_err("Failed to read hw rev 1 reg %d:rc=%d\n", REG_HWREV, rc);
+		goto err_read_rev;
+	}
+	pr_info("PMIC revision 1: %02X\n", val);
+	pmic->rev_registers = val;
+
+	/* Read PMIC chip revision 2 */
+	rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV_2, &val, sizeof(val));
+	if (rc) {
+		pr_err("Failed to read hw rev 2 reg %d:rc=%d\n", REG_HWREV_2,
+			rc);
+		goto err_read_rev;
+	}
+	pr_info("PMIC revision 2: %02X\n", val);
+	pmic->rev_registers |= val << BITS_PER_BYTE;
+
+	pmic->dev = &pdev->dev;
+	pm8018_drvdata.pm_chip_data = pmic;
+	platform_set_drvdata(pdev, &pm8018_drvdata);
+
+	/* Print out human readable version and revision names. */
+	version = pm8xxx_get_version(pmic->dev);
+	if (version == PM8XXX_VERSION_8018) {
+		revision = pm8xxx_get_revision(pmic->dev);
+		if (revision >= 0 && revision < ARRAY_SIZE(pm8018_rev_names))
+			revision_name = pm8018_rev_names[revision];
+		pr_info("PMIC version: PM8018 rev %s\n", revision_name);
+	} else {
+		WARN_ON(version != PM8XXX_VERSION_8018);
+	}
+	/* Log human readable restart reason */
+	rc = msm_ssbi_read(pdev->dev.parent, REG_PM8018_PON_CNTRL_3, &val, 1);
+	if (rc) {
+		pr_err("Cannot read restart reason rc=%d\n", rc);
+		goto err_read_rev;
+	}
+	val &= PM8018_RESTART_REASON_MASK;
+	pr_info("PMIC Restart Reason: %s\n", pm8018_restart_reason[val]);
+
+	rc = pm8018_add_subdevices(pdata, pmic);
+	if (rc) {
+		pr_err("Cannot add subdevices rc=%d\n", rc);
+		goto err;
+	}
+
+	/* gpio might not work if no irq device is found */
+	WARN_ON(pmic->irq_chip == NULL);
+
+	return 0;
+
+err:
+	mfd_remove_devices(pmic->dev);
+	platform_set_drvdata(pdev, NULL);
+	kfree(pmic->mfd_regulators);
+	kfree(pmic->regulator_cdata);
+err_read_rev:
+	kfree(pmic);
+	return rc;
+}
+
+static int __devexit pm8018_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_drvdata *drvdata;
+	struct pm8018 *pmic = NULL;
+	int i;
+
+	drvdata = platform_get_drvdata(pdev);
+	if (drvdata)
+		pmic = drvdata->pm_chip_data;
+	if (pmic) {
+		if (pmic->dev)
+			mfd_remove_devices(pmic->dev);
+		if (pmic->irq_chip) {
+			pm8xxx_irq_exit(pmic->irq_chip);
+			pmic->irq_chip = NULL;
+		}
+		if (pmic->mfd_regulators) {
+			for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
+				mutex_destroy(&regulator_data[i].pc_lock);
+		}
+		kfree(pmic->mfd_regulators);
+		kfree(pmic->regulator_cdata);
+		kfree(pmic);
+	}
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver pm8018_driver = {
+	.probe		= pm8018_probe,
+	.remove		= __devexit_p(pm8018_remove),
+	.driver		= {
+		.name	= PM8018_CORE_DEV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init pm8018_init(void)
+{
+	return platform_driver_register(&pm8018_driver);
+}
+postcore_initcall(pm8018_init);
+
+static void __exit pm8018_exit(void)
+{
+	platform_driver_unregister(&pm8018_driver);
+}
+module_exit(pm8018_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC 8018 core driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8018_CORE_DEV_NAME);
diff --git a/drivers/mfd/pm8038-core.c b/drivers/mfd/pm8038-core.c
new file mode 100644
index 0000000..4271a2a
--- /dev/null
+++ b/drivers/mfd/pm8038-core.c
@@ -0,0 +1,775 @@
+/*
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/msm_ssbi.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/pm8xxx/pm8038.h>
+#include <linux/mfd/pm8xxx/pm8921.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/regulator.h>
+
+#define REG_HWREV		0x002  /* PMIC4 revision */
+#define REG_HWREV_2		0x0E8  /* PMIC4 revision 2 */
+
+#define REG_MPP_BASE		0x050
+#define REG_RTC_BASE		0x11D
+#define REG_IRQ_BASE            0x1BB
+
+#define REG_SPK_BASE		0x253
+#define REG_SPK_REGISTERS	3
+
+#define PM8038_VERSION_MASK	0xFFF0
+#define PM8038_VERSION_VALUE	0x09F0
+#define PM8038_REVISION_MASK	0x000F
+
+#define REG_PM8038_PON_CNTRL_3	0x01D
+#define PM8038_RESTART_REASON_MASK	0x07
+
+#define SINGLE_IRQ_RESOURCE(_name, _irq) \
+{ \
+	.name	= _name, \
+	.start	= _irq, \
+	.end	= _irq, \
+	.flags	= IORESOURCE_IRQ, \
+}
+
+struct pm8038 {
+	struct device					*dev;
+	struct pm_irq_chip				*irq_chip;
+	struct mfd_cell					*mfd_regulators;
+	struct pm8xxx_regulator_core_platform_data	*regulator_cdata;
+	u32						rev_registers;
+};
+
+static int pm8038_readb(const struct device *dev, u16 addr, u8 *val)
+{
+	const struct pm8xxx_drvdata *pm8038_drvdata = dev_get_drvdata(dev);
+	const struct pm8038 *pmic = pm8038_drvdata->pm_chip_data;
+
+	return msm_ssbi_read(pmic->dev->parent, addr, val, 1);
+}
+
+static int pm8038_writeb(const struct device *dev, u16 addr, u8 val)
+{
+	const struct pm8xxx_drvdata *pm8038_drvdata = dev_get_drvdata(dev);
+	const struct pm8038 *pmic = pm8038_drvdata->pm_chip_data;
+
+	return msm_ssbi_write(pmic->dev->parent, addr, &val, 1);
+}
+
+static int pm8038_read_buf(const struct device *dev, u16 addr, u8 *buf,
+									int cnt)
+{
+	const struct pm8xxx_drvdata *pm8038_drvdata = dev_get_drvdata(dev);
+	const struct pm8038 *pmic = pm8038_drvdata->pm_chip_data;
+
+	return msm_ssbi_read(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8038_write_buf(const struct device *dev, u16 addr, u8 *buf,
+									int cnt)
+{
+	const struct pm8xxx_drvdata *pm8038_drvdata = dev_get_drvdata(dev);
+	const struct pm8038 *pmic = pm8038_drvdata->pm_chip_data;
+
+	return msm_ssbi_write(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8038_read_irq_stat(const struct device *dev, int irq)
+{
+	const struct pm8xxx_drvdata *pm8038_drvdata = dev_get_drvdata(dev);
+	const struct pm8038 *pmic = pm8038_drvdata->pm_chip_data;
+
+	return pm8xxx_get_irq_stat(pmic->irq_chip, irq);
+}
+
+static enum pm8xxx_version pm8038_get_version(const struct device *dev)
+{
+	const struct pm8xxx_drvdata *pm8038_drvdata = dev_get_drvdata(dev);
+	const struct pm8038 *pmic = pm8038_drvdata->pm_chip_data;
+	enum pm8xxx_version version = -ENODEV;
+
+	if ((pmic->rev_registers & PM8038_VERSION_MASK) == PM8038_VERSION_VALUE)
+		version = PM8XXX_VERSION_8038;
+
+	return version;
+}
+
+static int pm8038_get_revision(const struct device *dev)
+{
+	const struct pm8xxx_drvdata *pm8038_drvdata = dev_get_drvdata(dev);
+	const struct pm8038 *pmic = pm8038_drvdata->pm_chip_data;
+
+	return pmic->rev_registers & PM8038_REVISION_MASK;
+}
+
+static struct pm8xxx_drvdata pm8038_drvdata = {
+	.pmic_readb		= pm8038_readb,
+	.pmic_writeb		= pm8038_writeb,
+	.pmic_read_buf		= pm8038_read_buf,
+	.pmic_write_buf		= pm8038_write_buf,
+	.pmic_read_irq_stat	= pm8038_read_irq_stat,
+	.pmic_get_version	= pm8038_get_version,
+	.pmic_get_revision	= pm8038_get_revision,
+};
+
+static const struct resource gpio_cell_resources[] __devinitconst = {
+	[0] = {
+		.start = PM8038_IRQ_BLOCK_BIT(PM8038_GPIO_BLOCK_START, 0),
+		.end   = PM8038_IRQ_BLOCK_BIT(PM8038_GPIO_BLOCK_START, 0)
+			+ PM8038_NR_GPIOS - 1,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct mfd_cell gpio_cell __devinitdata = {
+	.name		= PM8XXX_GPIO_DEV_NAME,
+	.id		= -1,
+	.resources	= gpio_cell_resources,
+	.num_resources	= ARRAY_SIZE(gpio_cell_resources),
+};
+
+static const struct resource adc_cell_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE(NULL, PM8038_ADC_EOC_USR_IRQ),
+	SINGLE_IRQ_RESOURCE(NULL, PM8038_ADC_BATT_TEMP_WARM_IRQ),
+	SINGLE_IRQ_RESOURCE(NULL, PM8038_ADC_BATT_TEMP_COLD_IRQ),
+};
+
+static struct mfd_cell adc_cell __devinitdata = {
+	.name		= PM8XXX_ADC_DEV_NAME,
+	.id		= -1,
+	.resources	= adc_cell_resources,
+	.num_resources	= ARRAY_SIZE(adc_cell_resources),
+};
+
+static const struct resource charger_cell_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE("USBIN_VALID_IRQ", PM8921_USBIN_VALID_IRQ),
+	SINGLE_IRQ_RESOURCE("USBIN_OV_IRQ", PM8921_USBIN_OV_IRQ),
+	SINGLE_IRQ_RESOURCE("BATT_INSERTED_IRQ", PM8921_BATT_INSERTED_IRQ),
+	SINGLE_IRQ_RESOURCE("VBATDET_LOW_IRQ", PM8921_VBATDET_LOW_IRQ),
+	SINGLE_IRQ_RESOURCE("USBIN_UV_IRQ", PM8921_USBIN_UV_IRQ),
+	SINGLE_IRQ_RESOURCE("VBAT_OV_IRQ", PM8921_VBAT_OV_IRQ),
+	SINGLE_IRQ_RESOURCE("CHGWDOG_IRQ", PM8921_CHGWDOG_IRQ),
+	SINGLE_IRQ_RESOURCE("VCP_IRQ", PM8921_VCP_IRQ),
+	SINGLE_IRQ_RESOURCE("ATCDONE_IRQ", PM8921_ATCDONE_IRQ),
+	SINGLE_IRQ_RESOURCE("ATCFAIL_IRQ", PM8921_ATCFAIL_IRQ),
+	SINGLE_IRQ_RESOURCE("CHGDONE_IRQ", PM8921_CHGDONE_IRQ),
+	SINGLE_IRQ_RESOURCE("CHGFAIL_IRQ", PM8921_CHGFAIL_IRQ),
+	SINGLE_IRQ_RESOURCE("CHGSTATE_IRQ", PM8921_CHGSTATE_IRQ),
+	SINGLE_IRQ_RESOURCE("LOOP_CHANGE_IRQ", PM8921_LOOP_CHANGE_IRQ),
+	SINGLE_IRQ_RESOURCE("FASTCHG_IRQ", PM8921_FASTCHG_IRQ),
+	SINGLE_IRQ_RESOURCE("TRKLCHG_IRQ", PM8921_TRKLCHG_IRQ),
+	SINGLE_IRQ_RESOURCE("BATT_REMOVED_IRQ", PM8921_BATT_REMOVED_IRQ),
+	SINGLE_IRQ_RESOURCE("BATTTEMP_HOT_IRQ", PM8921_BATTTEMP_HOT_IRQ),
+	SINGLE_IRQ_RESOURCE("CHGHOT_IRQ", PM8921_CHGHOT_IRQ),
+	SINGLE_IRQ_RESOURCE("BATTTEMP_COLD_IRQ", PM8921_BATTTEMP_COLD_IRQ),
+	SINGLE_IRQ_RESOURCE("CHG_GONE_IRQ", PM8921_CHG_GONE_IRQ),
+	SINGLE_IRQ_RESOURCE("BAT_TEMP_OK_IRQ", PM8921_BAT_TEMP_OK_IRQ),
+	SINGLE_IRQ_RESOURCE("COARSE_DET_LOW_IRQ", PM8921_COARSE_DET_LOW_IRQ),
+	SINGLE_IRQ_RESOURCE("VDD_LOOP_IRQ", PM8921_VDD_LOOP_IRQ),
+	SINGLE_IRQ_RESOURCE("VREG_OV_IRQ", PM8921_VREG_OV_IRQ),
+	SINGLE_IRQ_RESOURCE("VBATDET_IRQ", PM8921_VBATDET_IRQ),
+	SINGLE_IRQ_RESOURCE("BATFET_IRQ", PM8921_BATFET_IRQ),
+	SINGLE_IRQ_RESOURCE("PSI_IRQ", PM8921_PSI_IRQ),
+	SINGLE_IRQ_RESOURCE("DCIN_VALID_IRQ", PM8921_DCIN_VALID_IRQ),
+	SINGLE_IRQ_RESOURCE("DCIN_OV_IRQ", PM8921_DCIN_OV_IRQ),
+	SINGLE_IRQ_RESOURCE("DCIN_UV_IRQ", PM8921_DCIN_UV_IRQ),
+};
+
+static const struct resource bms_cell_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE("PM8921_BMS_SBI_WRITE_OK", PM8921_BMS_SBI_WRITE_OK),
+	SINGLE_IRQ_RESOURCE("PM8921_BMS_CC_THR", PM8921_BMS_CC_THR),
+	SINGLE_IRQ_RESOURCE("PM8921_BMS_VSENSE_THR", PM8921_BMS_VSENSE_THR),
+	SINGLE_IRQ_RESOURCE("PM8921_BMS_VSENSE_FOR_R", PM8921_BMS_VSENSE_FOR_R),
+	SINGLE_IRQ_RESOURCE("PM8921_BMS_OCV_FOR_R", PM8921_BMS_OCV_FOR_R),
+	SINGLE_IRQ_RESOURCE("PM8921_BMS_GOOD_OCV", PM8921_BMS_GOOD_OCV),
+	SINGLE_IRQ_RESOURCE("PM8921_BMS_VSENSE_AVG", PM8921_BMS_VSENSE_AVG),
+};
+
+static struct mfd_cell charger_cell __devinitdata = {
+	.name		= PM8921_CHARGER_DEV_NAME,
+	.id		= -1,
+	.resources	= charger_cell_resources,
+	.num_resources	= ARRAY_SIZE(charger_cell_resources),
+};
+
+static struct mfd_cell bms_cell __devinitdata = {
+	.name		= PM8921_BMS_DEV_NAME,
+	.id		= -1,
+	.resources	= bms_cell_resources,
+	.num_resources	= ARRAY_SIZE(bms_cell_resources),
+};
+static const struct resource mpp_cell_resources[] __devinitconst = {
+	{
+		.start	= PM8038_IRQ_BLOCK_BIT(PM8038_MPP_BLOCK_START, 0),
+		.end	= PM8038_IRQ_BLOCK_BIT(PM8038_MPP_BLOCK_START, 0)
+			  + PM8038_NR_MPPS - 1,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct mfd_cell mpp_cell __devinitdata = {
+	.name		= PM8XXX_MPP_DEV_NAME,
+	.id		= 1,
+	.resources	= mpp_cell_resources,
+	.num_resources	= ARRAY_SIZE(mpp_cell_resources),
+};
+
+static const struct resource rtc_cell_resources[] __devinitconst = {
+	[0] = SINGLE_IRQ_RESOURCE(NULL, PM8038_RTC_ALARM_IRQ),
+	[1] = {
+		.name   = "pmic_rtc_base",
+		.start  = REG_RTC_BASE,
+		.end    = REG_RTC_BASE,
+		.flags  = IORESOURCE_IO,
+	},
+};
+
+static struct mfd_cell rtc_cell __devinitdata = {
+	.name           = PM8XXX_RTC_DEV_NAME,
+	.id             = -1,
+	.resources      = rtc_cell_resources,
+	.num_resources  = ARRAY_SIZE(rtc_cell_resources),
+};
+
+static const struct resource resources_pwrkey[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE(NULL, PM8038_PWRKEY_REL_IRQ),
+	SINGLE_IRQ_RESOURCE(NULL, PM8038_PWRKEY_PRESS_IRQ),
+};
+
+static struct mfd_cell pwrkey_cell __devinitdata = {
+	.name		= PM8XXX_PWRKEY_DEV_NAME,
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(resources_pwrkey),
+	.resources	= resources_pwrkey,
+};
+
+static struct mfd_cell pwm_cell __devinitdata = {
+	.name           = PM8XXX_PWM_DEV_NAME,
+	.id             = -1,
+};
+
+static struct mfd_cell misc_cell __devinitdata = {
+	.name           = PM8XXX_MISC_DEV_NAME,
+	.id             = -1,
+};
+
+static struct mfd_cell leds_cell __devinitdata = {
+	.name		= PM8XXX_LEDS_DEV_NAME,
+	.id		= -1,
+};
+
+static const struct resource resources_spk[] __devinitconst = {
+	[0] = {
+		.name   = PM8XXX_SPK_DEV_NAME,
+		.start  = REG_SPK_BASE,
+		.end    = REG_SPK_BASE + REG_SPK_REGISTERS,
+		.flags  = IORESOURCE_IO,
+	},
+};
+
+static struct mfd_cell spk_cell __devinitdata = {
+	.name           = PM8XXX_SPK_DEV_NAME,
+	.id             = -1,
+	.num_resources	= ARRAY_SIZE(resources_spk),
+	.resources	= resources_spk,
+};
+
+static struct mfd_cell debugfs_cell __devinitdata = {
+	.name		= "pm8xxx-debug",
+	.id		= 0,
+	.platform_data	= "pm8038-dbg",
+	.pdata_size	= sizeof("pm8038-dbg"),
+};
+
+static struct pm8xxx_vreg regulator_data[] = {
+	/*   name	     pc_name	    ctrl   test   hpm_min */
+	NLDO1200("8038_l1",		    0x0AE, 0x0AF, LDO_1200),
+	NLDO("8038_l2",      "8038_l2_pc",  0x0B0, 0x0B1, LDO_150),
+	PLDO("8038_l3",      "8038_l3_pc",  0x0B2, 0x0B3, LDO_50),
+	PLDO("8038_l4",      "8038_l4_pc",  0x0B4, 0x0B5, LDO_50),
+	PLDO("8038_l5",      "8038_l5_pc",  0x0B6, 0x0B7, LDO_600),
+	PLDO("8038_l6",      "8038_l6_pc",  0x0B8, 0x0B9, LDO_600),
+	PLDO("8038_l7",      "8038_l7_pc",  0x0BA, 0x0BB, LDO_600),
+	PLDO("8038_l8",      "8038_l8_pc",  0x0BC, 0x0BD, LDO_300),
+	PLDO("8038_l9",      "8038_l9_pc",  0x0BE, 0x0BF, LDO_300),
+	PLDO("8038_l10",     "8038_l10_pc", 0x0C0, 0x0C1, LDO_600),
+	PLDO("8038_l11",     "8038_l11_pc", 0x0C2, 0x0C3, LDO_600),
+	NLDO("8038_l12",     "8038_l12_pc", 0x0C4, 0x0C5, LDO_300),
+	PLDO("8038_l14",     "8038_l14_pc", 0x0C8, 0x0C9, LDO_50),
+	PLDO("8038_l15",     "8038_l15_pc", 0x0CA, 0x0CB, LDO_150),
+	NLDO1200("8038_l16",		    0x0CC, 0x0CD, LDO_1200),
+	PLDO("8038_l17",     "8038_l17_pc", 0x0CE, 0x0CF, LDO_150),
+	PLDO("8038_l18",     "8038_l18_pc", 0x0D0, 0x0D1, LDO_50),
+	NLDO1200("8038_l19",		    0x0D2, 0x0D3, LDO_1200),
+	NLDO1200("8038_l20",		    0x0D4, 0x0D5, LDO_1200),
+	PLDO("8038_l21",     "8038_l21_pc", 0x0D6, 0x0D7, LDO_150),
+	PLDO("8038_l22",     "8038_l22_pc", 0x0D8, 0x0D9, LDO_50),
+	PLDO("8038_l23",     "8038_l23_pc", 0x0DA, 0x0DB, LDO_50),
+	NLDO1200("8038_l24",		    0x0DC, 0x0DD, LDO_1200),
+	NLDO("8038_l26",     "8038_l26_pc", 0x0E0, 0x0E1, LDO_150),
+	NLDO1200("8038_l27",		    0x0E2, 0x0E3, LDO_1200),
+
+	/*   name	pc_name       ctrl   test2  clk    sleep  hpm_min */
+	SMPS("8038_s1", "8038_s1_pc", 0x1E0, 0x1E5, 0x009, 0x1E2, SMPS_1500),
+	SMPS("8038_s2", "8038_s2_pc", 0x1D8, 0x1DD, 0x00A, 0x1DA, SMPS_1500),
+	SMPS("8038_s3", "8038_s3_pc", 0x1D0, 0x1D5, 0x00B, 0x1D2, SMPS_1500),
+	SMPS("8038_s4", "8038_s4_pc", 0x1E8, 0x1ED, 0x00C, 0x1EA, SMPS_1500),
+
+	/*     name	  ctrl fts_cnfg1 pfm  pwr_cnfg  hpm_min */
+	FTSMPS("8038_s5", 0x025, 0x02E, 0x026, 0x032, SMPS_2000),
+	FTSMPS("8038_s6", 0x036, 0x03F, 0x037, 0x043, SMPS_2000),
+
+	/* name		       pc_name	       ctrl   test */
+	VS("8038_lvs1",        "8038_lvs1_pc", 0x060, 0x061),
+	VS("8038_lvs2",        "8038_lvs2_pc", 0x062, 0x063),
+};
+
+#define MAX_NAME_COMPARISON_LEN 32
+
+static int __devinit match_regulator(
+	struct pm8xxx_regulator_core_platform_data *core_data, const char *name)
+{
+	int found = 0;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(regulator_data); i++) {
+		if (regulator_data[i].rdesc.name
+		    && strncmp(regulator_data[i].rdesc.name, name,
+				MAX_NAME_COMPARISON_LEN) == 0) {
+			core_data->is_pin_controlled = false;
+			core_data->vreg = &regulator_data[i];
+			found = 1;
+			break;
+		} else if (regulator_data[i].rdesc_pc.name
+			   && strncmp(regulator_data[i].rdesc_pc.name, name,
+				MAX_NAME_COMPARISON_LEN) == 0) {
+			core_data->is_pin_controlled = true;
+			core_data->vreg = &regulator_data[i];
+			found = 1;
+			break;
+		}
+	}
+
+	if (!found)
+		pr_err("could not find a match for regulator: %s\n", name);
+
+	return found;
+}
+
+static int __devinit
+pm8038_add_regulators(const struct pm8038_platform_data *pdata,
+		      struct pm8038 *pmic, int irq_base)
+{
+	int ret = 0;
+	struct mfd_cell *mfd_regulators;
+	struct pm8xxx_regulator_core_platform_data *cdata;
+	int i;
+
+	/* Add one device for each regulator used by the board. */
+	mfd_regulators = kzalloc(sizeof(struct mfd_cell)
+				 * (pdata->num_regulators), GFP_KERNEL);
+	if (!mfd_regulators) {
+		pr_err("Cannot allocate %d bytes for pm8038 regulator "
+			"mfd cells\n", sizeof(struct mfd_cell)
+					* (pdata->num_regulators));
+		return -ENOMEM;
+	}
+	cdata = kzalloc(sizeof(struct pm8xxx_regulator_core_platform_data)
+			* pdata->num_regulators, GFP_KERNEL);
+	if (!cdata) {
+		pr_err("Cannot allocate %d bytes for pm8038 regulator "
+			"core data\n", pdata->num_regulators
+			  * sizeof(struct pm8xxx_regulator_core_platform_data));
+		kfree(mfd_regulators);
+		return -ENOMEM;
+	}
+	for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
+		mutex_init(&regulator_data[i].pc_lock);
+
+	for (i = 0; i < pdata->num_regulators; i++) {
+		if (!pdata->regulator_pdatas[i].init_data.constraints.name) {
+			pr_err("name missing for regulator %d\n", i);
+			ret = -EINVAL;
+			goto bail;
+		}
+		if (!match_regulator(&cdata[i],
+		      pdata->regulator_pdatas[i].init_data.constraints.name)) {
+			ret = -ENODEV;
+			goto bail;
+		}
+		cdata[i].pdata = &(pdata->regulator_pdatas[i]);
+		mfd_regulators[i].name = PM8XXX_REGULATOR_DEV_NAME;
+		mfd_regulators[i].id = cdata[i].pdata->id;
+		mfd_regulators[i].platform_data = &cdata[i];
+		mfd_regulators[i].pdata_size =
+			sizeof(struct pm8xxx_regulator_core_platform_data);
+	}
+	ret = mfd_add_devices(pmic->dev, 0, mfd_regulators,
+			pdata->num_regulators, NULL, irq_base);
+	if (ret)
+		goto bail;
+
+	pmic->mfd_regulators = mfd_regulators;
+	pmic->regulator_cdata = cdata;
+	return ret;
+
+bail:
+	for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
+		mutex_destroy(&regulator_data[i].pc_lock);
+	kfree(mfd_regulators);
+	kfree(cdata);
+	return ret;
+}
+
+static int __devinit
+pm8038_add_subdevices(const struct pm8038_platform_data *pdata,
+		      struct pm8038 *pmic)
+{
+	int ret = 0, irq_base = 0;
+	struct pm_irq_chip *irq_chip;
+
+	if (pdata->irq_pdata) {
+		pdata->irq_pdata->irq_cdata.nirqs = PM8038_NR_IRQS;
+		pdata->irq_pdata->irq_cdata.base_addr = REG_IRQ_BASE;
+		irq_base = pdata->irq_pdata->irq_base;
+		irq_chip = pm8xxx_irq_init(pmic->dev, pdata->irq_pdata);
+
+		if (IS_ERR(irq_chip)) {
+			pr_err("Failed to init interrupts ret=%ld\n",
+					PTR_ERR(irq_chip));
+			return PTR_ERR(irq_chip);
+		}
+		pmic->irq_chip = irq_chip;
+	}
+
+	if (pdata->gpio_pdata) {
+		pdata->gpio_pdata->gpio_cdata.ngpios = PM8038_NR_GPIOS;
+		gpio_cell.platform_data = pdata->gpio_pdata;
+		gpio_cell.pdata_size = sizeof(struct pm8xxx_gpio_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &gpio_cell, 1,
+					NULL, irq_base);
+		if (ret) {
+			pr_err("Failed to add  gpio subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->mpp_pdata) {
+		pdata->mpp_pdata->core_data.nmpps = PM8038_NR_MPPS;
+		pdata->mpp_pdata->core_data.base_addr = REG_MPP_BASE;
+		mpp_cell.platform_data = pdata->mpp_pdata;
+		mpp_cell.pdata_size = sizeof(struct pm8xxx_mpp_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &mpp_cell, 1, NULL,
+					irq_base);
+		if (ret) {
+			pr_err("Failed to add mpp subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->rtc_pdata) {
+		rtc_cell.platform_data = pdata->rtc_pdata;
+		rtc_cell.pdata_size = sizeof(struct pm8xxx_rtc_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &rtc_cell, 1, NULL,
+				irq_base);
+		if (ret) {
+			pr_err("Failed to add rtc subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->pwrkey_pdata) {
+		pwrkey_cell.platform_data = pdata->pwrkey_pdata;
+		pwrkey_cell.pdata_size =
+			sizeof(struct pm8xxx_pwrkey_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &pwrkey_cell, 1, NULL,
+					irq_base);
+		if (ret) {
+			pr_err("Failed to add pwrkey subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	ret = mfd_add_devices(pmic->dev, 0, &pwm_cell, 1, NULL, 0);
+	if (ret) {
+		pr_err("Failed to add pwm subdevice ret=%d\n", ret);
+		goto bail;
+	}
+
+	if (pdata->misc_pdata) {
+		misc_cell.platform_data = pdata->misc_pdata;
+		misc_cell.pdata_size = sizeof(struct pm8xxx_misc_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &misc_cell, 1, NULL,
+				      irq_base);
+		if (ret) {
+			pr_err("Failed to add  misc subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->leds_pdata) {
+		leds_cell.platform_data = pdata->leds_pdata;
+		leds_cell.pdata_size = sizeof(struct pm8xxx_led_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &leds_cell, 1, NULL, 0);
+		if (ret) {
+			pr_err("Failed to add leds subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->spk_pdata) {
+		spk_cell.platform_data = pdata->spk_pdata;
+		spk_cell.pdata_size = sizeof(struct pm8xxx_spk_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &spk_cell, 1, NULL, 0);
+		if (ret) {
+			pr_err("Failed to add spk subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->num_regulators > 0 && pdata->regulator_pdatas) {
+		ret = pm8038_add_regulators(pdata, pmic, irq_base);
+		if (ret) {
+			pr_err("Failed to add regulator subdevices ret=%d\n",
+				ret);
+			goto bail;
+		}
+	}
+
+	ret = mfd_add_devices(pmic->dev, 0, &debugfs_cell, 1, NULL, irq_base);
+	if (ret) {
+		pr_err("Failed to add debugfs subdevice ret=%d\n", ret);
+		goto bail;
+	}
+
+	if (pdata->adc_pdata) {
+		adc_cell.platform_data = pdata->adc_pdata;
+		adc_cell.pdata_size =
+			sizeof(struct pm8xxx_adc_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &adc_cell, 1, NULL,
+					irq_base);
+		if (ret) {
+			pr_err("Failed to add adc subdevices ret=%d\n",
+					ret);
+		}
+	}
+
+	if (pdata->charger_pdata) {
+		pdata->charger_pdata->charger_cdata.vbat_channel = CHANNEL_VBAT;
+		pdata->charger_pdata->charger_cdata.batt_temp_channel
+						= CHANNEL_BATT_THERM;
+		pdata->charger_pdata->charger_cdata.batt_id_channel
+						= CHANNEL_BATT_ID;
+		charger_cell.platform_data = pdata->charger_pdata;
+		charger_cell.pdata_size =
+				sizeof(struct pm8921_charger_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &charger_cell, 1, NULL,
+					irq_base);
+		if (ret) {
+			pr_err("Failed to add charger subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->bms_pdata) {
+		pdata->bms_pdata->bms_cdata.batt_temp_channel
+						= CHANNEL_BATT_THERM;
+		pdata->bms_pdata->bms_cdata.vbat_channel = CHANNEL_VBAT;
+		pdata->bms_pdata->bms_cdata.ref625mv_channel = CHANNEL_625MV;
+		pdata->bms_pdata->bms_cdata.ref1p25v_channel = CHANNEL_125V;
+		pdata->bms_pdata->bms_cdata.batt_id_channel = CHANNEL_BATT_ID;
+		bms_cell.platform_data = pdata->bms_pdata;
+		bms_cell.pdata_size = sizeof(struct pm8921_bms_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &bms_cell, 1, NULL,
+					irq_base);
+		if (ret) {
+			pr_err("Failed to add bms subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+	return 0;
+bail:
+	if (pmic->irq_chip) {
+		pm8xxx_irq_exit(pmic->irq_chip);
+		pmic->irq_chip = NULL;
+	}
+	return ret;
+}
+
+static const char * const pm8038_restart_reason[] = {
+	[0] = "Unknown",
+	[1] = "Triggered from CBL (external charger)",
+	[2] = "Triggered from KPD (power key press)",
+	[3] = "Triggered from CHG (usb charger insertion)",
+	[4] = "Triggered from SMPL (sudden momentary power loss)",
+	[5] = "Triggered from RTC (real time clock)",
+	[6] = "Triggered by Hard Reset",
+	[7] = "Triggered by General Purpose Trigger",
+};
+
+static const char * const pm8038_rev_names[] = {
+	[PM8XXX_REVISION_8038_TEST]	= "test",
+	[PM8XXX_REVISION_8038_1p0]	= "1.0",
+	[PM8XXX_REVISION_8038_2p0]	= "2.0",
+	[PM8XXX_REVISION_8038_2p1]	= "2.1",
+};
+
+static int __devinit pm8038_probe(struct platform_device *pdev)
+{
+	const struct pm8038_platform_data *pdata = pdev->dev.platform_data;
+	const char *revision_name = "unknown";
+	struct pm8038 *pmic;
+	enum pm8xxx_version version;
+	int revision;
+	int rc;
+	u8 val;
+
+	if (!pdata) {
+		pr_err("missing platform data\n");
+		return -EINVAL;
+	}
+
+	pmic = kzalloc(sizeof(struct pm8038), GFP_KERNEL);
+	if (!pmic) {
+		pr_err("Cannot alloc pm8038 struct\n");
+		return -ENOMEM;
+	}
+
+	/* Read PMIC chip revision */
+	rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV, &val, sizeof(val));
+	if (rc) {
+		pr_err("Failed to read hw rev reg %d:rc=%d\n", REG_HWREV, rc);
+		goto err_read_rev;
+	}
+	pr_info("PMIC revision 1: PM8038 rev %02X\n", val);
+	pmic->rev_registers = val;
+
+	/* Read PMIC chip revision 2 */
+	rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV_2, &val, sizeof(val));
+	if (rc) {
+		pr_err("Failed to read hw rev 2 reg %d:rc=%d\n",
+			REG_HWREV_2, rc);
+		goto err_read_rev;
+	}
+	pr_info("PMIC revision 2: PM8038 rev %02X\n", val);
+	pmic->rev_registers |= val << BITS_PER_BYTE;
+
+	pmic->dev = &pdev->dev;
+	pm8038_drvdata.pm_chip_data = pmic;
+	platform_set_drvdata(pdev, &pm8038_drvdata);
+
+	/* Print out human readable version and revision names. */
+	version = pm8xxx_get_version(pmic->dev);
+	if (version == PM8XXX_VERSION_8038) {
+		revision = pm8xxx_get_revision(pmic->dev);
+		if (revision >= 0 && revision < ARRAY_SIZE(pm8038_rev_names))
+			revision_name = pm8038_rev_names[revision];
+		pr_info("PMIC version: PM8038 ver %s\n", revision_name);
+	} else {
+		WARN_ON(version != PM8XXX_VERSION_8038);
+	}
+
+	/* Log human readable restart reason */
+	rc = msm_ssbi_read(pdev->dev.parent, REG_PM8038_PON_CNTRL_3, &val, 1);
+	if (rc) {
+		pr_err("Cannot read restart reason rc=%d\n", rc);
+		goto err_read_rev;
+	}
+	val &= PM8038_RESTART_REASON_MASK;
+	pr_info("PMIC Restart Reason: %s\n", pm8038_restart_reason[val]);
+
+	rc = pm8038_add_subdevices(pdata, pmic);
+	if (rc) {
+		pr_err("Cannot add subdevices rc=%d\n", rc);
+		goto err;
+	}
+
+	return 0;
+
+err:
+	mfd_remove_devices(pmic->dev);
+	platform_set_drvdata(pdev, NULL);
+	kfree(pmic->mfd_regulators);
+	kfree(pmic->regulator_cdata);
+err_read_rev:
+	kfree(pmic);
+	return rc;
+}
+
+static int __devexit pm8038_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_drvdata *drvdata;
+	struct pm8038 *pmic = NULL;
+	int i;
+
+	drvdata = platform_get_drvdata(pdev);
+
+	if (drvdata)
+		pmic = drvdata->pm_chip_data;
+
+	if (pmic) {
+		if (pmic->dev)
+			mfd_remove_devices(pmic->dev);
+
+		if (pmic->irq_chip) {
+			pm8xxx_irq_exit(pmic->irq_chip);
+			pmic->irq_chip = NULL;
+		}
+		if (pmic->mfd_regulators) {
+			for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
+				mutex_destroy(&regulator_data[i].pc_lock);
+		}
+		kfree(pmic->mfd_regulators);
+		kfree(pmic->regulator_cdata);
+		kfree(pmic);
+	}
+
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver pm8038_driver = {
+	.probe		= pm8038_probe,
+	.remove		= __devexit_p(pm8038_remove),
+	.driver		= {
+		.name	= PM8038_CORE_DEV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init pm8038_init(void)
+{
+	return platform_driver_register(&pm8038_driver);
+}
+postcore_initcall(pm8038_init);
+
+static void __exit pm8038_exit(void)
+{
+	platform_driver_unregister(&pm8038_driver);
+}
+module_exit(pm8038_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC 8038 core driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pm8038-core");
diff --git a/drivers/mfd/pm8821-core.c b/drivers/mfd/pm8821-core.c
new file mode 100644
index 0000000..df9d2e1
--- /dev/null
+++ b/drivers/mfd/pm8821-core.c
@@ -0,0 +1,317 @@
+/*
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/msm_ssbi.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/pm8xxx/pm8821.h>
+#include <linux/mfd/pm8xxx/core.h>
+
+#define REG_HWREV		0x002  /* PMIC4 revision */
+#define REG_HWREV_2		0x0E8  /* PMIC4 revision 2 */
+
+#define REG_MPP_BASE		0x050
+#define REG_IRQ_BASE		0x1BB
+
+#define PM8821_VERSION_MASK	0xFFF0
+#define PM8821_VERSION_VALUE	0x0BF0
+#define PM8821_REVISION_MASK	0x000F
+
+#define SINGLE_IRQ_RESOURCE(_name, _irq) \
+{ \
+	.name	= _name, \
+	.start	= _irq, \
+	.end	= _irq, \
+	.flags	= IORESOURCE_IRQ, \
+}
+
+struct pm8821 {
+	struct device			*dev;
+	struct pm_irq_chip		*irq_chip;
+	u32				rev_registers;
+};
+
+static int pm8821_readb(const struct device *dev, u16 addr, u8 *val)
+{
+	const struct pm8xxx_drvdata *pm8821_drvdata = dev_get_drvdata(dev);
+	const struct pm8821 *pmic = pm8821_drvdata->pm_chip_data;
+
+	return msm_ssbi_read(pmic->dev->parent, addr, val, 1);
+}
+
+static int pm8821_writeb(const struct device *dev, u16 addr, u8 val)
+{
+	const struct pm8xxx_drvdata *pm8821_drvdata = dev_get_drvdata(dev);
+	const struct pm8821 *pmic = pm8821_drvdata->pm_chip_data;
+
+	return msm_ssbi_write(pmic->dev->parent, addr, &val, 1);
+}
+
+static int pm8821_read_buf(const struct device *dev, u16 addr, u8 *buf,
+									int cnt)
+{
+	const struct pm8xxx_drvdata *pm8821_drvdata = dev_get_drvdata(dev);
+	const struct pm8821 *pmic = pm8821_drvdata->pm_chip_data;
+
+	return msm_ssbi_read(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8821_write_buf(const struct device *dev, u16 addr, u8 *buf,
+									int cnt)
+{
+	const struct pm8xxx_drvdata *pm8821_drvdata = dev_get_drvdata(dev);
+	const struct pm8821 *pmic = pm8821_drvdata->pm_chip_data;
+
+	return msm_ssbi_write(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8821_read_irq_stat(const struct device *dev, int irq)
+{
+	const struct pm8xxx_drvdata *pm8821_drvdata = dev_get_drvdata(dev);
+	const struct pm8821 *pmic = pm8821_drvdata->pm_chip_data;
+
+	return pm8xxx_get_irq_stat(pmic->irq_chip, irq);
+}
+
+static enum pm8xxx_version pm8821_get_version(const struct device *dev)
+{
+	const struct pm8xxx_drvdata *pm8821_drvdata = dev_get_drvdata(dev);
+	const struct pm8821 *pmic = pm8821_drvdata->pm_chip_data;
+	enum pm8xxx_version version = -ENODEV;
+
+	if ((pmic->rev_registers & PM8821_VERSION_MASK) == PM8821_VERSION_VALUE)
+		version = PM8XXX_VERSION_8821;
+
+	return version;
+}
+
+static int pm8821_get_revision(const struct device *dev)
+{
+	const struct pm8xxx_drvdata *pm8821_drvdata = dev_get_drvdata(dev);
+	const struct pm8821 *pmic = pm8821_drvdata->pm_chip_data;
+
+	return pmic->rev_registers & PM8821_REVISION_MASK;
+}
+
+static struct pm8xxx_drvdata pm8821_drvdata = {
+	.pmic_readb		= pm8821_readb,
+	.pmic_writeb		= pm8821_writeb,
+	.pmic_read_buf		= pm8821_read_buf,
+	.pmic_write_buf		= pm8821_write_buf,
+	.pmic_read_irq_stat	= pm8821_read_irq_stat,
+	.pmic_get_version	= pm8821_get_version,
+	.pmic_get_revision	= pm8821_get_revision,
+};
+
+static const struct resource mpp_cell_resources[] __devinitconst = {
+	{
+		.start	= PM8821_IRQ_BLOCK_BIT(PM8821_MPP_BLOCK_START, 0),
+		.end	= PM8821_IRQ_BLOCK_BIT(PM8821_MPP_BLOCK_START, 0)
+			  + PM8821_NR_MPPS - 1,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct mfd_cell mpp_cell __devinitdata = {
+	.name		= PM8XXX_MPP_DEV_NAME,
+	.id		= 1,
+	.resources	= mpp_cell_resources,
+	.num_resources	= ARRAY_SIZE(mpp_cell_resources),
+};
+
+static struct mfd_cell debugfs_cell __devinitdata = {
+	.name		= "pm8xxx-debug",
+	.id		= 1,
+	.platform_data	= "pm8821-dbg",
+	.pdata_size	= sizeof("pm8821-dbg"),
+};
+
+
+static int __devinit
+pm8821_add_subdevices(const struct pm8821_platform_data *pdata,
+		      struct pm8821 *pmic)
+{
+	int ret = 0, irq_base = 0;
+	struct pm_irq_chip *irq_chip;
+
+	if (pdata->irq_pdata) {
+		pdata->irq_pdata->irq_cdata.nirqs = PM8821_NR_IRQS;
+		pdata->irq_pdata->irq_cdata.base_addr = REG_IRQ_BASE;
+		irq_base = pdata->irq_pdata->irq_base;
+		irq_chip = pm8xxx_irq_init(pmic->dev, pdata->irq_pdata);
+
+		if (IS_ERR(irq_chip)) {
+			pr_err("Failed to init interrupts ret=%ld\n",
+					PTR_ERR(irq_chip));
+			return PTR_ERR(irq_chip);
+		}
+		pmic->irq_chip = irq_chip;
+	}
+
+	if (pdata->mpp_pdata) {
+		pdata->mpp_pdata->core_data.nmpps = PM8821_NR_MPPS;
+		pdata->mpp_pdata->core_data.base_addr = REG_MPP_BASE;
+		mpp_cell.platform_data = pdata->mpp_pdata;
+		mpp_cell.pdata_size = sizeof(struct pm8xxx_mpp_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &mpp_cell, 1, NULL,
+					irq_base);
+		if (ret) {
+			pr_err("Failed to add mpp subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	ret = mfd_add_devices(pmic->dev, 0, &debugfs_cell, 1, NULL, irq_base);
+	if (ret) {
+		pr_err("Failed to add debugfs subdevice ret=%d\n", ret);
+		goto bail;
+	}
+
+	return 0;
+bail:
+	if (pmic->irq_chip) {
+		pm8xxx_irq_exit(pmic->irq_chip);
+		pmic->irq_chip = NULL;
+	}
+	return ret;
+}
+
+static const char * const pm8821_rev_names[] = {
+	[PM8XXX_REVISION_8821_TEST]	= "test",
+	[PM8XXX_REVISION_8821_1p0]	= "1.0",
+	[PM8XXX_REVISION_8821_2p0]	= "2.0",
+	[PM8XXX_REVISION_8821_2p1]	= "2.1",
+};
+
+static int __devinit pm8821_probe(struct platform_device *pdev)
+{
+	const struct pm8821_platform_data *pdata = pdev->dev.platform_data;
+	const char *revision_name = "unknown";
+	struct pm8821 *pmic;
+	enum pm8xxx_version version;
+	int revision;
+	int rc;
+	u8 val;
+
+	if (!pdata) {
+		pr_err("missing platform data\n");
+		return -EINVAL;
+	}
+
+	pmic = kzalloc(sizeof(struct pm8821), GFP_KERNEL);
+	if (!pmic) {
+		pr_err("Cannot alloc pm8821 struct\n");
+		return -ENOMEM;
+	}
+
+	/* Read PMIC chip revision */
+	rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV, &val, sizeof(val));
+	if (rc) {
+		pr_err("Failed to read hw rev reg %d:rc=%d\n", REG_HWREV, rc);
+		goto err_read_rev;
+	}
+	pr_info("PMIC revision 1: PM8821 rev %02X\n", val);
+	pmic->rev_registers = val;
+
+	/* Read PMIC chip revision 2 */
+	rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV_2, &val, sizeof(val));
+	if (rc) {
+		pr_err("Failed to read hw rev 2 reg %d:rc=%d\n",
+			REG_HWREV_2, rc);
+		goto err_read_rev;
+	}
+	pr_info("PMIC revision 2: PM8821 rev %02X\n", val);
+	pmic->rev_registers |= val << BITS_PER_BYTE;
+
+	pmic->dev = &pdev->dev;
+	pm8821_drvdata.pm_chip_data = pmic;
+	platform_set_drvdata(pdev, &pm8821_drvdata);
+
+	/* Print out human readable version and revision names. */
+	version = pm8xxx_get_version(pmic->dev);
+	if (version == PM8XXX_VERSION_8821) {
+		revision = pm8xxx_get_revision(pmic->dev);
+		if (revision >= 0 && revision < ARRAY_SIZE(pm8821_rev_names))
+			revision_name = pm8821_rev_names[revision];
+		pr_info("PMIC version: PM8821 ver %s\n", revision_name);
+	} else {
+		WARN_ON(version != PM8XXX_VERSION_8821);
+	}
+
+	rc = pm8821_add_subdevices(pdata, pmic);
+	if (rc) {
+		pr_err("Cannot add subdevices rc=%d\n", rc);
+		goto err;
+	}
+
+	return 0;
+
+err:
+	mfd_remove_devices(pmic->dev);
+	platform_set_drvdata(pdev, NULL);
+err_read_rev:
+	kfree(pmic);
+	return rc;
+}
+
+static int __devexit pm8821_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_drvdata *drvdata;
+	struct pm8821 *pmic = NULL;
+
+	drvdata = platform_get_drvdata(pdev);
+	if (drvdata)
+		pmic = drvdata->pm_chip_data;
+	if (pmic)
+		mfd_remove_devices(pmic->dev);
+	if (pmic->irq_chip) {
+		pm8xxx_irq_exit(pmic->irq_chip);
+		pmic->irq_chip = NULL;
+	}
+	platform_set_drvdata(pdev, NULL);
+	kfree(pmic);
+
+	return 0;
+}
+
+static struct platform_driver pm8821_driver = {
+	.probe		= pm8821_probe,
+	.remove		= __devexit_p(pm8821_remove),
+	.driver		= {
+		.name	= "pm8821-core",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init pm8821_init(void)
+{
+	return platform_driver_register(&pm8821_driver);
+}
+postcore_initcall(pm8821_init);
+
+static void __exit pm8821_exit(void)
+{
+	platform_driver_unregister(&pm8821_driver);
+}
+module_exit(pm8821_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC 8821 core driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pm8821-core");
diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c
index e873b15..f39a19f 100644
--- a/drivers/mfd/pm8921-core.c
+++ b/drivers/mfd/pm8921-core.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -14,20 +14,55 @@
 #define pr_fmt(fmt) "%s: " fmt, __func__
 
 #include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
+#include <linux/string.h>
 #include <linux/err.h>
 #include <linux/msm_ssbi.h>
 #include <linux/mfd/core.h>
 #include <linux/mfd/pm8xxx/pm8921.h>
 #include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/regulator.h>
+#include <linux/leds-pm8xxx.h>
 
 #define REG_HWREV		0x002  /* PMIC4 revision */
 #define REG_HWREV_2		0x0E8  /* PMIC4 revision 2 */
 
+#define REG_MPP_BASE		0x050
+#define REG_IRQ_BASE		0x1BB
+
+#define REG_TEMP_ALARM_CTRL	0x1B
+#define REG_TEMP_ALARM_PWM	0x9B
+
+#define REG_BATT_ALARM_THRESH	0x023
+#define REG_BATT_ALARM_CTRL1	0x024
+#define REG_BATT_ALARM_CTRL2	0x021
+#define REG_BATT_ALARM_PWM_CTRL	0x020
+
+#define PM8921_VERSION_MASK	0xFFF0
+#define PM8921_VERSION_VALUE	0x06F0
+#define PM8922_VERSION_VALUE	0x0AF0
+#define PM8917_VERSION_VALUE	0x0CF0
+#define PM8921_REVISION_MASK	0x000F
+
+#define REG_PM8921_PON_CNTRL_3	0x01D
+#define PM8921_RESTART_REASON_MASK	0x07
+
+#define SINGLE_IRQ_RESOURCE(_name, _irq) \
+{ \
+	.name	= _name, \
+	.start	= _irq, \
+	.end	= _irq, \
+	.flags	= IORESOURCE_IRQ, \
+}
+
 struct pm8921 {
-	struct device			*dev;
-	struct pm_irq_chip		*irq_chip;
+	struct device					*dev;
+	struct pm_irq_chip				*irq_chip;
+	struct mfd_cell					*mfd_regulators;
+	struct pm8xxx_regulator_core_platform_data	*regulator_cdata;
+	u32						rev_registers;
 };
 
 static int pm8921_readb(const struct device *dev, u16 addr, u8 *val)
@@ -72,25 +107,481 @@
 	return pm8xxx_get_irq_stat(pmic->irq_chip, irq);
 }
 
+static enum pm8xxx_version pm8921_get_version(const struct device *dev)
+{
+	const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
+	const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
+	enum pm8xxx_version version = -ENODEV;
+
+	if ((pmic->rev_registers & PM8921_VERSION_MASK) == PM8921_VERSION_VALUE)
+		version = PM8XXX_VERSION_8921;
+	else if ((pmic->rev_registers & PM8921_VERSION_MASK)
+			== PM8922_VERSION_VALUE)
+		version = PM8XXX_VERSION_8922;
+	else if ((pmic->rev_registers & PM8921_VERSION_MASK)
+			== PM8917_VERSION_VALUE)
+		version = PM8XXX_VERSION_8917;
+
+	return version;
+}
+
+static int pm8921_get_revision(const struct device *dev)
+{
+	const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
+	const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
+
+	return pmic->rev_registers & PM8921_REVISION_MASK;
+}
+
 static struct pm8xxx_drvdata pm8921_drvdata = {
 	.pmic_readb		= pm8921_readb,
 	.pmic_writeb		= pm8921_writeb,
 	.pmic_read_buf		= pm8921_read_buf,
 	.pmic_write_buf		= pm8921_write_buf,
 	.pmic_read_irq_stat	= pm8921_read_irq_stat,
+	.pmic_get_version	= pm8921_get_version,
+	.pmic_get_revision	= pm8921_get_revision,
 };
 
-static int __devinit pm8921_add_subdevices(const struct pm8921_platform_data
-					   *pdata,
-					   struct pm8921 *pmic,
-					   u32 rev)
+static struct resource gpio_cell_resources[] = {
+	[0] = {
+		.start = PM8921_IRQ_BLOCK_BIT(PM8921_GPIO_BLOCK_START, 0),
+		.end   = PM8921_IRQ_BLOCK_BIT(PM8921_GPIO_BLOCK_START, 0)
+			+ PM8921_NR_GPIOS - 1,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct mfd_cell gpio_cell __devinitdata = {
+	.name		= PM8XXX_GPIO_DEV_NAME,
+	.id		= -1,
+	.resources	= gpio_cell_resources,
+	.num_resources	= ARRAY_SIZE(gpio_cell_resources),
+};
+
+static const struct resource adc_cell_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE(NULL, PM8921_ADC_EOC_USR_IRQ),
+	SINGLE_IRQ_RESOURCE(NULL, PM8921_ADC_BATT_TEMP_WARM_IRQ),
+	SINGLE_IRQ_RESOURCE(NULL, PM8921_ADC_BATT_TEMP_COLD_IRQ),
+};
+
+static struct mfd_cell adc_cell __devinitdata = {
+	.name		= PM8XXX_ADC_DEV_NAME,
+	.id		= -1,
+	.resources	= adc_cell_resources,
+	.num_resources	= ARRAY_SIZE(adc_cell_resources),
+};
+
+static struct resource mpp_cell_resources[] = {
+	{
+		.start	= PM8921_IRQ_BLOCK_BIT(PM8921_MPP_BLOCK_START, 0),
+		.end	= PM8921_IRQ_BLOCK_BIT(PM8921_MPP_BLOCK_START, 0)
+			  + PM8921_NR_MPPS - 1,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct mfd_cell mpp_cell __devinitdata = {
+	.name		= PM8XXX_MPP_DEV_NAME,
+	.id		= 0,
+	.resources	= mpp_cell_resources,
+	.num_resources	= ARRAY_SIZE(mpp_cell_resources),
+};
+
+static const struct resource rtc_cell_resources[] __devinitconst = {
+	[0] = SINGLE_IRQ_RESOURCE(NULL, PM8921_RTC_ALARM_IRQ),
+	[1] = {
+		.name   = "pmic_rtc_base",
+		.start  = PM8921_RTC_BASE,
+		.end    = PM8921_RTC_BASE,
+		.flags  = IORESOURCE_IO,
+	},
+};
+
+static struct mfd_cell rtc_cell __devinitdata = {
+	.name           = PM8XXX_RTC_DEV_NAME,
+	.id             = -1,
+	.resources      = rtc_cell_resources,
+	.num_resources  = ARRAY_SIZE(rtc_cell_resources),
+};
+
+static const struct resource resources_pwrkey[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE(NULL, PM8921_PWRKEY_REL_IRQ),
+	SINGLE_IRQ_RESOURCE(NULL, PM8921_PWRKEY_PRESS_IRQ),
+};
+
+static struct mfd_cell pwrkey_cell __devinitdata = {
+	.name		= PM8XXX_PWRKEY_DEV_NAME,
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(resources_pwrkey),
+	.resources	= resources_pwrkey,
+};
+
+static const struct resource resources_keypad[] = {
+	SINGLE_IRQ_RESOURCE(NULL, PM8921_KEYPAD_IRQ),
+	SINGLE_IRQ_RESOURCE(NULL, PM8921_KEYSTUCK_IRQ),
+};
+
+static struct mfd_cell keypad_cell __devinitdata = {
+	.name		= PM8XXX_KEYPAD_DEV_NAME,
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(resources_keypad),
+	.resources	= resources_keypad,
+};
+
+static struct mfd_cell debugfs_cell __devinitdata = {
+	.name		= "pm8xxx-debug",
+	.id		= 0,
+	.platform_data	= "pm8921-dbg",
+	.pdata_size	= sizeof("pm8921-dbg"),
+};
+
+static struct mfd_cell pwm_cell __devinitdata = {
+	.name           = PM8XXX_PWM_DEV_NAME,
+	.id             = -1,
+};
+
+static const struct resource charger_cell_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE("USBIN_VALID_IRQ", PM8921_USBIN_VALID_IRQ),
+	SINGLE_IRQ_RESOURCE("USBIN_OV_IRQ", PM8921_USBIN_OV_IRQ),
+	SINGLE_IRQ_RESOURCE("BATT_INSERTED_IRQ", PM8921_BATT_INSERTED_IRQ),
+	SINGLE_IRQ_RESOURCE("VBATDET_LOW_IRQ", PM8921_VBATDET_LOW_IRQ),
+	SINGLE_IRQ_RESOURCE("USBIN_UV_IRQ", PM8921_USBIN_UV_IRQ),
+	SINGLE_IRQ_RESOURCE("VBAT_OV_IRQ", PM8921_VBAT_OV_IRQ),
+	SINGLE_IRQ_RESOURCE("CHGWDOG_IRQ", PM8921_CHGWDOG_IRQ),
+	SINGLE_IRQ_RESOURCE("VCP_IRQ", PM8921_VCP_IRQ),
+	SINGLE_IRQ_RESOURCE("ATCDONE_IRQ", PM8921_ATCDONE_IRQ),
+	SINGLE_IRQ_RESOURCE("ATCFAIL_IRQ", PM8921_ATCFAIL_IRQ),
+	SINGLE_IRQ_RESOURCE("CHGDONE_IRQ", PM8921_CHGDONE_IRQ),
+	SINGLE_IRQ_RESOURCE("CHGFAIL_IRQ", PM8921_CHGFAIL_IRQ),
+	SINGLE_IRQ_RESOURCE("CHGSTATE_IRQ", PM8921_CHGSTATE_IRQ),
+	SINGLE_IRQ_RESOURCE("LOOP_CHANGE_IRQ", PM8921_LOOP_CHANGE_IRQ),
+	SINGLE_IRQ_RESOURCE("FASTCHG_IRQ", PM8921_FASTCHG_IRQ),
+	SINGLE_IRQ_RESOURCE("TRKLCHG_IRQ", PM8921_TRKLCHG_IRQ),
+	SINGLE_IRQ_RESOURCE("BATT_REMOVED_IRQ", PM8921_BATT_REMOVED_IRQ),
+	SINGLE_IRQ_RESOURCE("BATTTEMP_HOT_IRQ", PM8921_BATTTEMP_HOT_IRQ),
+	SINGLE_IRQ_RESOURCE("CHGHOT_IRQ", PM8921_CHGHOT_IRQ),
+	SINGLE_IRQ_RESOURCE("BATTTEMP_COLD_IRQ", PM8921_BATTTEMP_COLD_IRQ),
+	SINGLE_IRQ_RESOURCE("CHG_GONE_IRQ", PM8921_CHG_GONE_IRQ),
+	SINGLE_IRQ_RESOURCE("BAT_TEMP_OK_IRQ", PM8921_BAT_TEMP_OK_IRQ),
+	SINGLE_IRQ_RESOURCE("COARSE_DET_LOW_IRQ", PM8921_COARSE_DET_LOW_IRQ),
+	SINGLE_IRQ_RESOURCE("VDD_LOOP_IRQ", PM8921_VDD_LOOP_IRQ),
+	SINGLE_IRQ_RESOURCE("VREG_OV_IRQ", PM8921_VREG_OV_IRQ),
+	SINGLE_IRQ_RESOURCE("VBATDET_IRQ", PM8921_VBATDET_IRQ),
+	SINGLE_IRQ_RESOURCE("BATFET_IRQ", PM8921_BATFET_IRQ),
+	SINGLE_IRQ_RESOURCE("PSI_IRQ", PM8921_PSI_IRQ),
+	SINGLE_IRQ_RESOURCE("DCIN_VALID_IRQ", PM8921_DCIN_VALID_IRQ),
+	SINGLE_IRQ_RESOURCE("DCIN_OV_IRQ", PM8921_DCIN_OV_IRQ),
+	SINGLE_IRQ_RESOURCE("DCIN_UV_IRQ", PM8921_DCIN_UV_IRQ),
+};
+
+static const struct resource bms_cell_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE("PM8921_BMS_SBI_WRITE_OK", PM8921_BMS_SBI_WRITE_OK),
+	SINGLE_IRQ_RESOURCE("PM8921_BMS_CC_THR", PM8921_BMS_CC_THR),
+	SINGLE_IRQ_RESOURCE("PM8921_BMS_VSENSE_THR", PM8921_BMS_VSENSE_THR),
+	SINGLE_IRQ_RESOURCE("PM8921_BMS_VSENSE_FOR_R", PM8921_BMS_VSENSE_FOR_R),
+	SINGLE_IRQ_RESOURCE("PM8921_BMS_OCV_FOR_R", PM8921_BMS_OCV_FOR_R),
+	SINGLE_IRQ_RESOURCE("PM8921_BMS_GOOD_OCV", PM8921_BMS_GOOD_OCV),
+	SINGLE_IRQ_RESOURCE("PM8921_BMS_VSENSE_AVG", PM8921_BMS_VSENSE_AVG),
+};
+
+static struct mfd_cell charger_cell __devinitdata = {
+	.name		= PM8921_CHARGER_DEV_NAME,
+	.id		= -1,
+	.resources	= charger_cell_resources,
+	.num_resources	= ARRAY_SIZE(charger_cell_resources),
+};
+
+static struct mfd_cell bms_cell __devinitdata = {
+	.name		= PM8921_BMS_DEV_NAME,
+	.id		= -1,
+	.resources	= bms_cell_resources,
+	.num_resources	= ARRAY_SIZE(bms_cell_resources),
+};
+
+static struct mfd_cell misc_cell __devinitdata = {
+	.name           = PM8XXX_MISC_DEV_NAME,
+	.id             = -1,
+};
+
+static struct mfd_cell leds_cell __devinitdata = {
+	.name		= PM8XXX_LEDS_DEV_NAME,
+	.id		= -1,
+};
+
+static const struct resource thermal_alarm_cell_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE("pm8921_tempstat_irq", PM8921_TEMPSTAT_IRQ),
+	SINGLE_IRQ_RESOURCE("pm8921_overtemp_irq", PM8921_OVERTEMP_IRQ),
+};
+
+static struct pm8xxx_tm_core_data thermal_alarm_cdata = {
+	.adc_channel =			CHANNEL_DIE_TEMP,
+	.adc_type =			PM8XXX_TM_ADC_PM8XXX_ADC,
+	.reg_addr_temp_alarm_ctrl =	REG_TEMP_ALARM_CTRL,
+	.reg_addr_temp_alarm_pwm =	REG_TEMP_ALARM_PWM,
+	.tm_name =			"pm8921_tz",
+	.irq_name_temp_stat =		"pm8921_tempstat_irq",
+	.irq_name_over_temp =		"pm8921_overtemp_irq",
+};
+
+static struct mfd_cell thermal_alarm_cell __devinitdata = {
+	.name		= PM8XXX_TM_DEV_NAME,
+	.id		= -1,
+	.resources	= thermal_alarm_cell_resources,
+	.num_resources	= ARRAY_SIZE(thermal_alarm_cell_resources),
+	.platform_data	= &thermal_alarm_cdata,
+	.pdata_size	= sizeof(struct pm8xxx_tm_core_data),
+};
+
+static const struct resource batt_alarm_cell_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE("pm8921_batt_alarm_irq", PM8921_BATT_ALARM_IRQ),
+};
+
+static struct pm8xxx_batt_alarm_core_data batt_alarm_cdata = {
+	.irq_name		= "pm8921_batt_alarm_irq",
+	.reg_addr_threshold	= REG_BATT_ALARM_THRESH,
+	.reg_addr_ctrl1		= REG_BATT_ALARM_CTRL1,
+	.reg_addr_ctrl2		= REG_BATT_ALARM_CTRL2,
+	.reg_addr_pwm_ctrl	= REG_BATT_ALARM_PWM_CTRL,
+};
+
+static struct mfd_cell batt_alarm_cell __devinitdata = {
+	.name		= PM8XXX_BATT_ALARM_DEV_NAME,
+	.id		= -1,
+	.resources	= batt_alarm_cell_resources,
+	.num_resources	= ARRAY_SIZE(batt_alarm_cell_resources),
+	.platform_data	= &batt_alarm_cdata,
+	.pdata_size	= sizeof(struct pm8xxx_batt_alarm_core_data),
+};
+
+static const struct resource ccadc_cell_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE("PM8921_BMS_CCADC_EOC", PM8921_BMS_CCADC_EOC),
+};
+
+static struct mfd_cell ccadc_cell __devinitdata = {
+	.name		= PM8XXX_CCADC_DEV_NAME,
+	.id		= -1,
+	.resources	= ccadc_cell_resources,
+	.num_resources	= ARRAY_SIZE(ccadc_cell_resources),
+};
+
+static struct mfd_cell vibrator_cell __devinitdata = {
+	.name           = PM8XXX_VIBRATOR_DEV_NAME,
+	.id             = -1,
+};
+
+static struct pm8xxx_vreg regulator_data[] = {
+	/*   name	     pc_name	    ctrl   test   hpm_min */
+	NLDO("8921_l1",      "8921_l1_pc",  0x0AE, 0x0AF, LDO_150),
+	NLDO("8921_l2",      "8921_l2_pc",  0x0B0, 0x0B1, LDO_150),
+	PLDO("8921_l3",      "8921_l3_pc",  0x0B2, 0x0B3, LDO_150),
+	PLDO("8921_l4",      "8921_l4_pc",  0x0B4, 0x0B5, LDO_50),
+	PLDO("8921_l5",      "8921_l5_pc",  0x0B6, 0x0B7, LDO_300),
+	PLDO("8921_l6",      "8921_l6_pc",  0x0B8, 0x0B9, LDO_600),
+	PLDO("8921_l7",      "8921_l7_pc",  0x0BA, 0x0BB, LDO_150),
+	PLDO("8921_l8",      "8921_l8_pc",  0x0BC, 0x0BD, LDO_300),
+	PLDO("8921_l9",      "8921_l9_pc",  0x0BE, 0x0BF, LDO_300),
+	PLDO("8921_l10",     "8921_l10_pc", 0x0C0, 0x0C1, LDO_600),
+	PLDO("8921_l11",     "8921_l11_pc", 0x0C2, 0x0C3, LDO_150),
+	NLDO("8921_l12",     "8921_l12_pc", 0x0C4, 0x0C5, LDO_150),
+	PLDO("8921_l14",     "8921_l14_pc", 0x0C8, 0x0C9, LDO_50),
+	PLDO("8921_l15",     "8921_l15_pc", 0x0CA, 0x0CB, LDO_150),
+	PLDO("8921_l16",     "8921_l16_pc", 0x0CC, 0x0CD, LDO_300),
+	PLDO("8921_l17",     "8921_l17_pc", 0x0CE, 0x0CF, LDO_150),
+	NLDO("8921_l18",     "8921_l18_pc", 0x0D0, 0x0D1, LDO_150),
+	PLDO("8921_l21",     "8921_l21_pc", 0x0D6, 0x0D7, LDO_150),
+	PLDO("8921_l22",     "8921_l22_pc", 0x0D8, 0x0D9, LDO_150),
+	PLDO("8921_l23",     "8921_l23_pc", 0x0DA, 0x0DB, LDO_150),
+	NLDO1200("8921_l24",		    0x0DC, 0x0DD, LDO_1200),
+	NLDO1200("8921_l25",		    0x0DE, 0x0DF, LDO_1200),
+	NLDO1200("8921_l26",		    0x0E0, 0x0E1, LDO_1200),
+	NLDO1200("8921_l27",		    0x0E2, 0x0E3, LDO_1200),
+	NLDO1200("8921_l28",		    0x0E4, 0x0E5, LDO_1200),
+	PLDO("8921_l29",     "8921_l29_pc", 0x0E6, 0x0E7, LDO_150),
+
+	/*   name	pc_name       ctrl   test2  clk    sleep  hpm_min */
+	SMPS("8921_s1", "8921_s1_pc", 0x1D0, 0x1D5, 0x009, 0x1D2, SMPS_1500),
+	SMPS("8921_s2", "8921_s2_pc", 0x1D8, 0x1DD, 0x00A, 0x1DA, SMPS_1500),
+	SMPS("8921_s3", "8921_s3_pc", 0x1E0, 0x1E5, 0x00B, 0x1E2, SMPS_1500),
+	SMPS("8921_s4", "8921_s4_pc", 0x1E8, 0x1ED, 0x011, 0x1EA, SMPS_1500),
+
+	/*     name	  ctrl fts_cnfg1 pfm  pwr_cnfg  hpm_min */
+	FTSMPS("8921_s5", 0x025, 0x02E, 0x026, 0x032, SMPS_2000),
+	FTSMPS("8921_s6", 0x036, 0x03F, 0x037, 0x043, SMPS_2000),
+
+	/*   name	pc_name       ctrl   test2  clk    sleep  hpm_min */
+	SMPS("8921_s7", "8921_s7_pc", 0x1F0, 0x1F5, 0x012, 0x1F2, SMPS_1500),
+	SMPS("8921_s8", "8921_s8_pc", 0x1F8, 0x1FD, 0x013, 0x1FA, SMPS_1500),
+
+	/* name		       pc_name	       ctrl   test */
+	VS("8921_lvs1",        "8921_lvs1_pc", 0x060, 0x061),
+	VS300("8921_lvs2",		       0x062, 0x063),
+	VS("8921_lvs3",        "8921_lvs3_pc", 0x064, 0x065),
+	VS("8921_lvs4",        "8921_lvs4_pc", 0x066, 0x067),
+	VS("8921_lvs5",        "8921_lvs5_pc", 0x068, 0x069),
+	VS("8921_lvs6",        "8921_lvs6_pc", 0x06A, 0x06B),
+	VS("8921_lvs7",        "8921_lvs7_pc", 0x06C, 0x06D),
+	VS300("8921_usb_otg",		       0x06E, 0x06F),
+	VS300("8921_hdmi_mvs",		       0x070, 0x071),
+
+	/*  name	ctrl */
+	NCP("8921_ncp", 0x090),
+};
+
+/*
+ * PM8917 adds 6 LDOs and a boost regulator beyond those available on PM8921.
+ * It also replaces SMPS 3 with FTSMPS 3.  PM8917 does not have an NCP.
+ */
+static struct pm8xxx_vreg pm8917_regulator_data[] = {
+	/*   name	     pc_name	    ctrl   test   hpm_min */
+	PLDO("8917_l30",     "8917_l30_pc", 0x0A3, 0x0A4, LDO_150),
+	PLDO("8917_l31",     "8917_l31_pc", 0x0A5, 0x0A6, LDO_150),
+	PLDO("8917_l32",     "8917_l32_pc", 0x0A7, 0x0A8, LDO_150),
+	PLDO("8917_l33",     "8917_l33_pc", 0x0C6, 0x0C7, LDO_150),
+	PLDO("8917_l34",     "8917_l34_pc", 0x0D2, 0x0D3, LDO_150),
+	PLDO("8917_l35",     "8917_l35_pc", 0x0D4, 0x0D5, LDO_300),
+	PLDO("8917_l36",     "8917_l36_pc", 0x0A9, 0x0AA, LDO_50),
+
+	/*    name          ctrl */
+	BOOST("8917_boost", 0x04B),
+};
+
+#define MAX_NAME_COMPARISON_LEN 32
+
+static int __devinit match_regulator(enum pm8xxx_version version,
+	struct pm8xxx_regulator_core_platform_data *core_data, const char *name)
+{
+	int found = 0;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(regulator_data); i++) {
+		if (regulator_data[i].rdesc.name
+		    && strncmp(regulator_data[i].rdesc.name, name,
+				MAX_NAME_COMPARISON_LEN) == 0) {
+			core_data->is_pin_controlled = false;
+			core_data->vreg = &regulator_data[i];
+			found = 1;
+			break;
+		} else if (regulator_data[i].rdesc_pc.name
+			   && strncmp(regulator_data[i].rdesc_pc.name, name,
+				MAX_NAME_COMPARISON_LEN) == 0) {
+			core_data->is_pin_controlled = true;
+			core_data->vreg = &regulator_data[i];
+			found = 1;
+			break;
+		}
+	}
+	if (version == PM8XXX_VERSION_8917) {
+		for (i = 0; i < ARRAY_SIZE(pm8917_regulator_data); i++) {
+			if (pm8917_regulator_data[i].rdesc.name
+			    && strncmp(pm8917_regulator_data[i].rdesc.name,
+					name, MAX_NAME_COMPARISON_LEN) == 0) {
+				core_data->is_pin_controlled = false;
+				core_data->vreg = &pm8917_regulator_data[i];
+				found = 1;
+				break;
+			} else if (pm8917_regulator_data[i].rdesc_pc.name
+			      && strncmp(pm8917_regulator_data[i].rdesc_pc.name,
+					name, MAX_NAME_COMPARISON_LEN) == 0) {
+				core_data->is_pin_controlled = true;
+				core_data->vreg = &pm8917_regulator_data[i];
+				found = 1;
+				break;
+			}
+		}
+	}
+
+	if (!found)
+		pr_err("could not find a match for regulator: %s\n", name);
+
+	return found;
+}
+
+static int __devinit
+pm8921_add_regulators(const struct pm8921_platform_data *pdata,
+		      struct pm8921 *pmic, int irq_base)
+{
+	int ret = 0;
+	struct mfd_cell *mfd_regulators;
+	struct pm8xxx_regulator_core_platform_data *cdata;
+	enum pm8xxx_version version;
+	int i;
+
+	version = pm8xxx_get_version(pmic->dev);
+
+	/* Add one device for each regulator used by the board. */
+	mfd_regulators = kzalloc(sizeof(struct mfd_cell)
+				 * (pdata->num_regulators), GFP_KERNEL);
+	if (!mfd_regulators) {
+		pr_err("Cannot allocate %d bytes for pm8921 regulator "
+			"mfd cells\n", sizeof(struct mfd_cell)
+					* (pdata->num_regulators));
+		return -ENOMEM;
+	}
+	cdata = kzalloc(sizeof(struct pm8xxx_regulator_core_platform_data)
+			* pdata->num_regulators, GFP_KERNEL);
+	if (!cdata) {
+		pr_err("Cannot allocate %d bytes for pm8921 regulator "
+			"core data\n", pdata->num_regulators
+			  * sizeof(struct pm8xxx_regulator_core_platform_data));
+		kfree(mfd_regulators);
+		return -ENOMEM;
+	}
+	for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
+		mutex_init(&regulator_data[i].pc_lock);
+	for (i = 0; i < ARRAY_SIZE(pm8917_regulator_data); i++)
+		mutex_init(&pm8917_regulator_data[i].pc_lock);
+
+	for (i = 0; i < pdata->num_regulators; i++) {
+		if (!pdata->regulator_pdatas[i].init_data.constraints.name) {
+			pr_err("name missing for regulator %d\n", i);
+			ret = -EINVAL;
+			goto bail;
+		}
+		if (!match_regulator(version, &cdata[i],
+		      pdata->regulator_pdatas[i].init_data.constraints.name)) {
+			ret = -ENODEV;
+			goto bail;
+		}
+		cdata[i].pdata = &(pdata->regulator_pdatas[i]);
+		mfd_regulators[i].name = PM8XXX_REGULATOR_DEV_NAME;
+		mfd_regulators[i].id = cdata[i].pdata->id;
+		mfd_regulators[i].platform_data = &cdata[i];
+		mfd_regulators[i].pdata_size =
+			sizeof(struct pm8xxx_regulator_core_platform_data);
+	}
+	ret = mfd_add_devices(pmic->dev, 0, mfd_regulators,
+			pdata->num_regulators, NULL, irq_base);
+	if (ret)
+		goto bail;
+
+	pmic->mfd_regulators = mfd_regulators;
+	pmic->regulator_cdata = cdata;
+	return ret;
+
+bail:
+	for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
+		mutex_destroy(&regulator_data[i].pc_lock);
+	for (i = 0; i < ARRAY_SIZE(pm8917_regulator_data); i++)
+		mutex_destroy(&pm8917_regulator_data[i].pc_lock);
+	kfree(mfd_regulators);
+	kfree(cdata);
+	return ret;
+}
+
+static int __devinit
+pm8921_add_subdevices(const struct pm8921_platform_data *pdata,
+		      struct pm8921 *pmic)
 {
 	int ret = 0, irq_base = 0;
 	struct pm_irq_chip *irq_chip;
+	enum pm8xxx_version version;
+
+	version = pm8xxx_get_version(pmic->dev);
 
 	if (pdata->irq_pdata) {
 		pdata->irq_pdata->irq_cdata.nirqs = PM8921_NR_IRQS;
-		pdata->irq_pdata->irq_cdata.rev = rev;
+		pdata->irq_pdata->irq_cdata.base_addr = REG_IRQ_BASE;
 		irq_base = pdata->irq_pdata->irq_base;
 		irq_chip = pm8xxx_irq_init(pmic->dev, pdata->irq_pdata);
 
@@ -101,16 +592,270 @@
 		}
 		pmic->irq_chip = irq_chip;
 	}
+
+	if (pdata->gpio_pdata) {
+		if (version == PM8XXX_VERSION_8917) {
+			gpio_cell_resources[0].end = gpio_cell_resources[0].end
+							+ PM8917_NR_GPIOS
+							- PM8921_NR_GPIOS;
+			pdata->gpio_pdata->gpio_cdata.ngpios = PM8917_NR_GPIOS;
+		} else {
+			pdata->gpio_pdata->gpio_cdata.ngpios = PM8921_NR_GPIOS;
+		}
+		gpio_cell.platform_data = pdata->gpio_pdata;
+		gpio_cell.pdata_size = sizeof(struct pm8xxx_gpio_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &gpio_cell, 1,
+					NULL, irq_base);
+		if (ret) {
+			pr_err("Failed to add  gpio subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->mpp_pdata) {
+		if (version == PM8XXX_VERSION_8917) {
+			mpp_cell_resources[0].end = mpp_cell_resources[0].end
+							+ PM8917_NR_MPPS
+							- PM8921_NR_MPPS;
+			pdata->mpp_pdata->core_data.nmpps = PM8917_NR_MPPS;
+		} else {
+			pdata->mpp_pdata->core_data.nmpps = PM8921_NR_MPPS;
+		}
+		pdata->mpp_pdata->core_data.base_addr = REG_MPP_BASE;
+		mpp_cell.platform_data = pdata->mpp_pdata;
+		mpp_cell.pdata_size = sizeof(struct pm8xxx_mpp_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &mpp_cell, 1, NULL,
+					irq_base);
+		if (ret) {
+			pr_err("Failed to add mpp subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->rtc_pdata) {
+		rtc_cell.platform_data = pdata->rtc_pdata;
+		rtc_cell.pdata_size = sizeof(struct pm8xxx_rtc_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &rtc_cell, 1, NULL,
+				irq_base);
+		if (ret) {
+			pr_err("Failed to add rtc subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->pwrkey_pdata) {
+		pwrkey_cell.platform_data = pdata->pwrkey_pdata;
+		pwrkey_cell.pdata_size =
+			sizeof(struct pm8xxx_pwrkey_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &pwrkey_cell, 1, NULL,
+					irq_base);
+		if (ret) {
+			pr_err("Failed to add pwrkey subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->keypad_pdata) {
+		keypad_cell.platform_data = pdata->keypad_pdata;
+		keypad_cell.pdata_size =
+			sizeof(struct pm8xxx_keypad_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &keypad_cell, 1, NULL,
+					irq_base);
+		if (ret) {
+			pr_err("Failed to add keypad subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->charger_pdata) {
+		pdata->charger_pdata->charger_cdata.vbat_channel = CHANNEL_VBAT;
+		pdata->charger_pdata->charger_cdata.batt_temp_channel
+						= CHANNEL_BATT_THERM;
+		pdata->charger_pdata->charger_cdata.batt_id_channel
+						= CHANNEL_BATT_ID;
+		charger_cell.platform_data = pdata->charger_pdata;
+		charger_cell.pdata_size =
+				sizeof(struct pm8921_charger_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &charger_cell, 1, NULL,
+					irq_base);
+		if (ret) {
+			pr_err("Failed to add charger subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->adc_pdata) {
+		adc_cell.platform_data = pdata->adc_pdata;
+		adc_cell.pdata_size =
+			sizeof(struct pm8xxx_adc_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &adc_cell, 1, NULL,
+					irq_base);
+		if (ret) {
+			pr_err("Failed to add regulator subdevices ret=%d\n",
+					ret);
+		}
+	}
+
+	if (pdata->bms_pdata) {
+		pdata->bms_pdata->bms_cdata.batt_temp_channel
+						= CHANNEL_BATT_THERM;
+		pdata->bms_pdata->bms_cdata.vbat_channel = CHANNEL_VBAT;
+		pdata->bms_pdata->bms_cdata.ref625mv_channel = CHANNEL_625MV;
+		pdata->bms_pdata->bms_cdata.ref1p25v_channel = CHANNEL_125V;
+		pdata->bms_pdata->bms_cdata.batt_id_channel = CHANNEL_BATT_ID;
+		bms_cell.platform_data = pdata->bms_pdata;
+		bms_cell.pdata_size = sizeof(struct pm8921_bms_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &bms_cell, 1, NULL,
+					irq_base);
+		if (ret) {
+			pr_err("Failed to add bms subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->num_regulators > 0 && pdata->regulator_pdatas) {
+		ret = pm8921_add_regulators(pdata, pmic, irq_base);
+		if (ret) {
+			pr_err("Failed to add regulator subdevices ret=%d\n",
+				ret);
+			goto bail;
+		}
+	}
+
+	ret = mfd_add_devices(pmic->dev, 0, &debugfs_cell, 1, NULL, irq_base);
+	if (ret) {
+		pr_err("Failed to add debugfs subdevice ret=%d\n", ret);
+		goto bail;
+	}
+
+	if (pdata->misc_pdata) {
+		misc_cell.platform_data = pdata->misc_pdata;
+		misc_cell.pdata_size = sizeof(struct pm8xxx_misc_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &misc_cell, 1, NULL,
+				      irq_base);
+		if (ret) {
+			pr_err("Failed to add  misc subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	ret = mfd_add_devices(pmic->dev, 0, &thermal_alarm_cell, 1, NULL,
+				irq_base);
+	if (ret) {
+		pr_err("Failed to add thermal alarm subdevice ret=%d\n",
+			ret);
+		goto bail;
+	}
+
+	ret = mfd_add_devices(pmic->dev, 0, &batt_alarm_cell, 1, NULL,
+				irq_base);
+	if (ret) {
+		pr_err("Failed to add battery alarm subdevice ret=%d\n",
+			ret);
+		goto bail;
+	}
+
+	if (version != PM8XXX_VERSION_8917) {
+		if (pdata->pwm_pdata) {
+			pwm_cell.platform_data = pdata->pwm_pdata;
+			pwm_cell.pdata_size =
+				sizeof(struct pm8xxx_pwm_platform_data);
+		}
+		ret = mfd_add_devices(pmic->dev, 0, &pwm_cell, 1, NULL, 0);
+		if (ret) {
+			pr_err("Failed to add pwm subdevice ret=%d\n", ret);
+			goto bail;
+		}
+
+		if (pdata->leds_pdata) {
+			leds_cell.platform_data = pdata->leds_pdata;
+			leds_cell.pdata_size =
+				sizeof(struct pm8xxx_led_platform_data);
+			ret = mfd_add_devices(pmic->dev, 0, &leds_cell,
+					      1, NULL, 0);
+			if (ret) {
+				pr_err("Failed to add leds subdevice ret=%d\n",
+						ret);
+				goto bail;
+			}
+		}
+
+		if (pdata->vibrator_pdata) {
+			vibrator_cell.platform_data = pdata->vibrator_pdata;
+			vibrator_cell.pdata_size =
+				sizeof(struct pm8xxx_vibrator_platform_data);
+			ret = mfd_add_devices(pmic->dev, 0, &vibrator_cell,
+					      1, NULL, 0);
+			if (ret) {
+				pr_err("Failed to add vibrator ret=%d\n", ret);
+				goto bail;
+			}
+		}
+	}
+
+	if (pdata->ccadc_pdata) {
+		ccadc_cell.platform_data = pdata->ccadc_pdata;
+		ccadc_cell.pdata_size =
+				sizeof(struct pm8xxx_ccadc_platform_data);
+
+		ret = mfd_add_devices(pmic->dev, 0, &ccadc_cell, 1, NULL,
+					irq_base);
+		if (ret) {
+			pr_err("Failed to add ccadc subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	return 0;
+bail:
+	if (pmic->irq_chip) {
+		pm8xxx_irq_exit(pmic->irq_chip);
+		pmic->irq_chip = NULL;
+	}
 	return ret;
 }
 
+static const char * const pm8921_restart_reason[] = {
+	[0] = "Unknown",
+	[1] = "Triggered from CBL (external charger)",
+	[2] = "Triggered from KPD (power key press)",
+	[3] = "Triggered from CHG (usb charger insertion)",
+	[4] = "Triggered from SMPL (sudden momentary power loss)",
+	[5] = "Triggered from RTC (real time clock)",
+	[6] = "Triggered by Hard Reset",
+	[7] = "Triggered by General Purpose Trigger",
+};
+
+static const char * const pm8921_rev_names[] = {
+	[PM8XXX_REVISION_8921_TEST]	= "test",
+	[PM8XXX_REVISION_8921_1p0]	= "1.0",
+	[PM8XXX_REVISION_8921_1p1]	= "1.1",
+	[PM8XXX_REVISION_8921_2p0]	= "2.0",
+	[PM8XXX_REVISION_8921_3p0]	= "3.0",
+	[PM8XXX_REVISION_8921_3p1]	= "3.1",
+};
+
+static const char * const pm8922_rev_names[] = {
+	[PM8XXX_REVISION_8922_TEST]	= "test",
+	[PM8XXX_REVISION_8922_1p0]	= "1.0",
+	[PM8XXX_REVISION_8922_1p1]	= "1.1",
+	[PM8XXX_REVISION_8922_2p0]	= "2.0",
+};
+
+static const char * const pm8917_rev_names[] = {
+	[PM8XXX_REVISION_8917_TEST]	= "test",
+	[PM8XXX_REVISION_8917_1p0]	= "1.0",
+};
+
 static int __devinit pm8921_probe(struct platform_device *pdev)
 {
 	const struct pm8921_platform_data *pdata = pdev->dev.platform_data;
+	const char *revision_name = "unknown";
 	struct pm8921 *pmic;
+	enum pm8xxx_version version;
+	int revision;
 	int rc;
 	u8 val;
-	u32 rev;
 
 	if (!pdata) {
 		pr_err("missing platform data\n");
@@ -130,7 +875,7 @@
 		goto err_read_rev;
 	}
 	pr_info("PMIC revision 1: %02X\n", val);
-	rev = val;
+	pmic->rev_registers = val;
 
 	/* Read PMIC chip revision 2 */
 	rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV_2, &val, sizeof(val));
@@ -140,13 +885,43 @@
 		goto err_read_rev;
 	}
 	pr_info("PMIC revision 2: %02X\n", val);
-	rev |= val << BITS_PER_BYTE;
+	pmic->rev_registers |= val << BITS_PER_BYTE;
 
 	pmic->dev = &pdev->dev;
 	pm8921_drvdata.pm_chip_data = pmic;
 	platform_set_drvdata(pdev, &pm8921_drvdata);
 
-	rc = pm8921_add_subdevices(pdata, pmic, rev);
+	/* Print out human readable version and revision names. */
+	version = pm8xxx_get_version(pmic->dev);
+	revision = pm8xxx_get_revision(pmic->dev);
+	if (version == PM8XXX_VERSION_8921) {
+		if (revision >= 0 && revision < ARRAY_SIZE(pm8921_rev_names))
+			revision_name = pm8921_rev_names[revision];
+		pr_info("PMIC version: PM8921 rev %s\n", revision_name);
+	} else if (version == PM8XXX_VERSION_8922) {
+		if (revision >= 0 && revision < ARRAY_SIZE(pm8922_rev_names))
+			revision_name = pm8922_rev_names[revision];
+		pr_info("PMIC version: PM8922 rev %s\n", revision_name);
+	} else if (version == PM8XXX_VERSION_8917) {
+		if (revision >= 0 && revision < ARRAY_SIZE(pm8917_rev_names))
+			revision_name = pm8917_rev_names[revision];
+		pr_info("PMIC version: PM8917 rev %s\n", revision_name);
+	} else {
+		WARN_ON(version != PM8XXX_VERSION_8921
+			&& version != PM8XXX_VERSION_8922
+			&& version != PM8XXX_VERSION_8917);
+	}
+
+	/* Log human readable restart reason */
+	rc = msm_ssbi_read(pdev->dev.parent, REG_PM8921_PON_CNTRL_3, &val, 1);
+	if (rc) {
+		pr_err("Cannot read restart reason rc=%d\n", rc);
+		goto err_read_rev;
+	}
+	val &= PM8921_RESTART_REASON_MASK;
+	pr_info("PMIC Restart Reason: %s\n", pm8921_restart_reason[val]);
+
+	rc = pm8921_add_subdevices(pdata, pmic);
 	if (rc) {
 		pr_err("Cannot add subdevices rc=%d\n", rc);
 		goto err;
@@ -160,6 +935,8 @@
 err:
 	mfd_remove_devices(pmic->dev);
 	platform_set_drvdata(pdev, NULL);
+	kfree(pmic->mfd_regulators);
+	kfree(pmic->regulator_cdata);
 err_read_rev:
 	kfree(pmic);
 	return rc;
@@ -169,18 +946,28 @@
 {
 	struct pm8xxx_drvdata *drvdata;
 	struct pm8921 *pmic = NULL;
+	int i;
 
 	drvdata = platform_get_drvdata(pdev);
 	if (drvdata)
 		pmic = drvdata->pm_chip_data;
-	if (pmic)
-		mfd_remove_devices(pmic->dev);
-	if (pmic->irq_chip) {
-		pm8xxx_irq_exit(pmic->irq_chip);
-		pmic->irq_chip = NULL;
+	if (pmic) {
+		if (pmic->dev)
+			mfd_remove_devices(pmic->dev);
+		if (pmic->irq_chip)
+			pm8xxx_irq_exit(pmic->irq_chip);
+		if (pmic->mfd_regulators) {
+			for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
+				mutex_destroy(&regulator_data[i].pc_lock);
+			for (i = 0; i < ARRAY_SIZE(pm8917_regulator_data); i++)
+				mutex_destroy(
+					&pm8917_regulator_data[i].pc_lock);
+		}
+		kfree(pmic->mfd_regulators);
+		kfree(pmic->regulator_cdata);
+		kfree(pmic);
 	}
 	platform_set_drvdata(pdev, NULL);
-	kfree(pmic);
 
 	return 0;
 }
@@ -198,7 +985,7 @@
 {
 	return platform_driver_register(&pm8921_driver);
 }
-subsys_initcall(pm8921_init);
+postcore_initcall(pm8921_init);
 
 static void __exit pm8921_exit(void)
 {
diff --git a/drivers/mfd/pm8xxx-batt-alarm.c b/drivers/mfd/pm8xxx-batt-alarm.c
new file mode 100644
index 0000000..1d30db9
--- /dev/null
+++ b/drivers/mfd/pm8xxx-batt-alarm.c
@@ -0,0 +1,806 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+/*
+ * Qualcomm PMIC PM8xxx Battery Alarm driver
+ *
+ */
+
+#define pr_fmt(fmt)	"%s: " fmt, __func__
+
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/batt-alarm.h>
+
+/* Available voltage threshold values */
+#define THRESHOLD_MIN_MV		2500
+#define THRESHOLD_MAX_MV		5675
+#define THRESHOLD_STEP_MV		25
+
+/* Register bit definitions */
+
+/* Threshold register */
+#define THRESHOLD_UPPER_MASK		0xF0
+#define THRESHOLD_LOWER_MASK		0x0F
+#define THRESHOLD_UPPER_SHIFT		4
+#define THRESHOLD_LOWER_SHIFT		0
+
+/* CTRL 1 register */
+#define CTRL1_BATT_ALARM_ENABLE_MASK	0x80
+#define CTRL1_BATT_ALARM_ENABLE		0x80
+#define CTRL1_BATT_ALARM_DISABLE	0x00
+#define CTRL1_HOLD_TIME_MASK		0x70
+#define CTRL1_STATUS_UPPER_MASK		0x02
+#define CTRL1_STATUS_LOWER_MASK		0x01
+#define CTRL1_HOLD_TIME_SHIFT		4
+#define CTRL1_HOLD_TIME_MIN		0
+#define CTRL1_HOLD_TIME_MAX		7
+
+/* CTRL 2 register */
+#define CTRL2_COMP_UPPER_DISABLE_MASK	0x80
+#define CTRL2_COMP_UPPER_ENABLE		0x00
+#define CTRL2_COMP_UPPER_DISABLE	0x80
+#define CTRL2_COMP_LOWER_DISABLE_MASK	0x40
+#define CTRL2_COMP_LOWER_ENABLE		0x00
+#define CTRL2_COMP_LOWER_DISABLE	0x40
+#define CTRL2_FINE_STEP_UPPER_MASK	0x30
+#define CTRL2_RANGE_EXT_UPPER_MASK	0x08
+#define CTRL2_FINE_STEP_LOWER_MASK	0x06
+#define CTRL2_RANGE_EXT_LOWER_MASK	0x01
+#define CTRL2_FINE_STEP_UPPER_SHIFT	4
+#define CTRL2_FINE_STEP_LOWER_SHIFT	1
+
+/* PWM control register */
+#define PWM_CTRL_ALARM_EN_MASK		0xC0
+#define PWM_CTRL_ALARM_EN_NEVER		0x00
+#define PWM_CTRL_ALARM_EN_TCXO		0x40
+#define PWM_CTRL_ALARM_EN_PWM		0x80
+#define PWM_CTRL_ALARM_EN_ALWAYS	0xC0
+#define PWM_CTRL_PRE_MASK		0x38
+#define PWM_CTRL_DIV_MASK		0x07
+#define PWM_CTRL_PRE_SHIFT		3
+#define PWM_CTRL_DIV_SHIFT		0
+#define PWM_CTRL_PRE_MIN		0
+#define PWM_CTRL_PRE_MAX		7
+#define PWM_CTRL_DIV_MIN		1
+#define PWM_CTRL_DIV_MAX		7
+
+/* PWM control input range */
+#define PWM_CTRL_PRE_INPUT_MIN		2
+#define PWM_CTRL_PRE_INPUT_MAX		9
+#define PWM_CTRL_DIV_INPUT_MIN		2
+#define PWM_CTRL_DIV_INPUT_MAX		8
+
+/* Available voltage threshold values */
+#define THRESHOLD_BASIC_MIN_MV		2800
+#define THRESHOLD_EXT_MIN_MV		4400
+
+/*
+ * Default values used during initialization:
+ * Slowest PWM rate to ensure minimal status jittering when crossing thresholds.
+ * Largest hold time also helps reduce status value jittering.  Comparators
+ * are disabled by default and must be turned on by calling
+ * pm8xxx_batt_alarm_state_set.
+ */
+#define DEFAULT_THRESHOLD_LOWER		3200
+#define DEFAULT_THRESHOLD_UPPER		4300
+#define DEFAULT_HOLD_TIME		PM8XXX_BATT_ALARM_HOLD_TIME_16_MS
+#define DEFAULT_USE_PWM			1
+#define DEFAULT_PWM_SCALER		9
+#define DEFAULT_PWM_DIVIDER		8
+#define DEFAULT_LOWER_ENABLE		0
+#define DEFAULT_UPPER_ENABLE		0
+
+struct pm8xxx_batt_alarm_chip {
+	struct pm8xxx_batt_alarm_core_data	cdata;
+	struct srcu_notifier_head		irq_notifier_list;
+	struct work_struct			irq_work;
+	struct device				*dev;
+	struct mutex				lock;
+	unsigned int				irq;
+	int					notifier_count;
+	u8					reg_threshold;
+	u8					reg_ctrl1;
+	u8					reg_ctrl2;
+	u8					reg_pwm_ctrl;
+};
+static struct pm8xxx_batt_alarm_chip *the_battalarm;
+
+static int pm8xxx_reg_write(struct pm8xxx_batt_alarm_chip *chip, u16 addr,
+				u8 val, u8 mask, u8 *reg_save)
+{
+	int rc = 0;
+	u8 reg;
+
+	reg = (*reg_save & ~mask) | (val & mask);
+	if (reg != *reg_save)
+		rc = pm8xxx_writeb(chip->dev->parent, addr, reg);
+	if (rc)
+		pr_err("pm8xxx_writeb failed; addr=%03X, rc=%d\n", addr, rc);
+	else
+		*reg_save = reg;
+	return rc;
+}
+
+/**
+ * pm8xxx_batt_alarm_enable - enable one of the battery voltage threshold
+ *			      comparators
+ * @comparator:	selects which comparator to enable
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_batt_alarm_enable(enum pm8xxx_batt_alarm_comparator comparator)
+{
+	struct pm8xxx_batt_alarm_chip *chip = the_battalarm;
+	int rc;
+	u8 val_ctrl2 = 0, mask_ctrl2 = 0;
+
+	if (!chip) {
+		pr_err("no battery alarm device found.\n");
+		return -ENODEV;
+	}
+
+	if (comparator < 0 || comparator > PM8XXX_BATT_ALARM_UPPER_COMPARATOR) {
+		pr_err("invalid comparator ID number: %d\n", comparator);
+		return -EINVAL;
+	}
+
+	if (comparator == PM8XXX_BATT_ALARM_LOWER_COMPARATOR) {
+		val_ctrl2 = CTRL2_COMP_LOWER_ENABLE;
+		mask_ctrl2 = CTRL2_COMP_LOWER_DISABLE_MASK;
+	} else {
+		val_ctrl2 = CTRL2_COMP_UPPER_ENABLE;
+		mask_ctrl2 = CTRL2_COMP_UPPER_DISABLE_MASK;
+	}
+
+	mutex_lock(&chip->lock);
+
+	/* Enable the battery alarm block. */
+	rc = pm8xxx_reg_write(chip, chip->cdata.reg_addr_ctrl1,
+				CTRL1_BATT_ALARM_ENABLE,
+				CTRL1_BATT_ALARM_ENABLE_MASK, &chip->reg_ctrl1);
+	if (rc)
+		goto bail;
+
+	/* Enable the individual comparators. */
+	rc = pm8xxx_reg_write(chip, chip->cdata.reg_addr_ctrl2, val_ctrl2,
+				mask_ctrl2, &chip->reg_ctrl2);
+
+bail:
+	mutex_unlock(&chip->lock);
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_batt_alarm_enable);
+
+/**
+ * pm8xxx_batt_alarm_disable - disable one of the battery voltage threshold
+ *			       comparators
+ * @comparator:	selects which comparator to disable
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_batt_alarm_disable(enum pm8xxx_batt_alarm_comparator comparator)
+{
+	struct pm8xxx_batt_alarm_chip *chip = the_battalarm;
+	int rc;
+	u8 val_ctrl1 = 0, val_ctrl2 = 0, mask_ctrl2 = 0;
+
+	if (!chip) {
+		pr_err("no battery alarm device found.\n");
+		return -ENODEV;
+	}
+
+	if (comparator < 0 || comparator > PM8XXX_BATT_ALARM_UPPER_COMPARATOR) {
+		pr_err("invalid comparator ID number: %d\n", comparator);
+		return -EINVAL;
+	}
+
+	if (comparator == PM8XXX_BATT_ALARM_LOWER_COMPARATOR) {
+		val_ctrl2 = CTRL2_COMP_LOWER_DISABLE;
+		mask_ctrl2 = CTRL2_COMP_LOWER_DISABLE_MASK;
+	} else {
+		val_ctrl2 = CTRL2_COMP_UPPER_DISABLE;
+		mask_ctrl2 = CTRL2_COMP_UPPER_DISABLE_MASK;
+	}
+
+	mutex_lock(&chip->lock);
+
+	/* Disable the specified comparator. */
+	rc = pm8xxx_reg_write(chip, chip->cdata.reg_addr_ctrl2, val_ctrl2,
+				mask_ctrl2, &chip->reg_ctrl2);
+	if (rc)
+		goto bail;
+
+	/* Disable the battery alarm block if both comparators are disabled. */
+	val_ctrl2 = chip->reg_ctrl2
+	      & (CTRL2_COMP_LOWER_DISABLE_MASK | CTRL2_COMP_UPPER_DISABLE_MASK);
+	if (val_ctrl2 == (CTRL2_COMP_LOWER_DISABLE | CTRL2_COMP_UPPER_DISABLE))
+		val_ctrl1 = CTRL1_BATT_ALARM_DISABLE;
+	else
+		val_ctrl1 = CTRL1_BATT_ALARM_ENABLE;
+
+	rc = pm8xxx_reg_write(chip, chip->cdata.reg_addr_ctrl1, val_ctrl1,
+				CTRL1_BATT_ALARM_ENABLE_MASK, &chip->reg_ctrl1);
+
+bail:
+	mutex_unlock(&chip->lock);
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_batt_alarm_disable);
+
+/**
+ * pm8xxx_batt_alarm_threshold_set - set the lower and upper alarm thresholds
+ * @comparator:		selects which comparator to set the threshold of
+ * @threshold_mV:	battery voltage threshold in millivolts
+ *			set points = 2500-5675 mV in 25 mV steps
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_batt_alarm_threshold_set(
+	enum pm8xxx_batt_alarm_comparator comparator, int threshold_mV)
+{
+	struct pm8xxx_batt_alarm_chip *chip = the_battalarm;
+	int step, fine_step, rc;
+	u8 val_threshold = 0, val_ctrl2 = 0;
+	int threshold_mask, threshold_shift, range_ext_mask, fine_step_mask;
+	int fine_step_shift;
+
+	if (!chip) {
+		pr_err("no battery alarm device found.\n");
+		return -ENXIO;
+	}
+
+	if (comparator < 0 || comparator > PM8XXX_BATT_ALARM_UPPER_COMPARATOR) {
+		pr_err("invalid comparator ID number: %d\n", comparator);
+		return -EINVAL;
+	}
+
+	if (threshold_mV < THRESHOLD_MIN_MV
+	    || threshold_mV > THRESHOLD_MAX_MV) {
+		pr_err("threshold value, %d mV, is outside of allowable "
+			"range: [%d, %d] mV\n", threshold_mV,
+			THRESHOLD_MIN_MV, THRESHOLD_MAX_MV);
+		return -EINVAL;
+	}
+
+	if (comparator == PM8XXX_BATT_ALARM_LOWER_COMPARATOR) {
+		threshold_mask = THRESHOLD_LOWER_MASK;
+		threshold_shift = THRESHOLD_LOWER_SHIFT;
+		range_ext_mask = CTRL2_RANGE_EXT_LOWER_MASK;
+		fine_step_mask = CTRL2_FINE_STEP_LOWER_MASK;
+		fine_step_shift = CTRL2_FINE_STEP_LOWER_SHIFT;
+	} else {
+		threshold_mask = THRESHOLD_UPPER_MASK;
+		threshold_shift = THRESHOLD_UPPER_SHIFT;
+		range_ext_mask = CTRL2_RANGE_EXT_UPPER_MASK;
+		fine_step_mask = CTRL2_FINE_STEP_UPPER_MASK;
+		fine_step_shift = CTRL2_FINE_STEP_UPPER_SHIFT;
+	}
+
+	/* Determine register settings to achieve the threshold. */
+	if (threshold_mV < THRESHOLD_BASIC_MIN_MV) {
+		/* Extended low range */
+		val_ctrl2 |= range_ext_mask;
+
+		step = (threshold_mV - THRESHOLD_MIN_MV) / THRESHOLD_STEP_MV;
+
+		fine_step = step & 0x3;
+		/* Extended low range is for steps 0 to 2 */
+		step >>= 2;
+	} else if (threshold_mV >= THRESHOLD_EXT_MIN_MV) {
+		/* Extended high range */
+		val_ctrl2 |= range_ext_mask;
+
+		step = (threshold_mV - THRESHOLD_EXT_MIN_MV)
+			/ THRESHOLD_STEP_MV;
+
+		fine_step = step & 0x3;
+		/* Extended high range is for steps 3 to 15 */
+		step = (step >> 2) + 3;
+	} else {
+		/* Basic range */
+		step = (threshold_mV - THRESHOLD_BASIC_MIN_MV)
+			/ THRESHOLD_STEP_MV;
+
+		fine_step = step & 0x3;
+		step >>= 2;
+	}
+	val_threshold |= step << threshold_shift;
+	val_ctrl2 |= (fine_step << fine_step_shift) & fine_step_mask;
+
+	mutex_lock(&chip->lock);
+	rc = pm8xxx_reg_write(chip, chip->cdata.reg_addr_threshold,
+			val_threshold, threshold_mask, &chip->reg_threshold);
+	if (rc)
+		goto bail;
+
+	rc = pm8xxx_reg_write(chip, chip->cdata.reg_addr_ctrl2, val_ctrl2,
+			range_ext_mask | fine_step_mask, &chip->reg_ctrl2);
+
+bail:
+	mutex_unlock(&chip->lock);
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_batt_alarm_threshold_set);
+
+/**
+ * pm8xxx_batt_alarm_status_read - get status of both threshold comparators
+ *
+ * RETURNS:	< 0	   = error
+ *		  0	   = battery voltage ok
+ *		BIT(0) set = battery voltage below lower threshold
+ *		BIT(1) set = battery voltage above upper threshold
+ */
+int pm8xxx_batt_alarm_status_read(void)
+{
+	struct pm8xxx_batt_alarm_chip *chip = the_battalarm;
+	int status, rc;
+
+	if (!chip) {
+		pr_err("no battery alarm device found.\n");
+		return -ENXIO;
+	}
+
+	mutex_lock(&chip->lock);
+	rc = pm8xxx_readb(chip->dev->parent, chip->cdata.reg_addr_ctrl1,
+			  &chip->reg_ctrl1);
+
+	status = ((chip->reg_ctrl1 & CTRL1_STATUS_LOWER_MASK)
+			? PM8XXX_BATT_ALARM_STATUS_BELOW_LOWER : 0)
+		| ((chip->reg_ctrl1 & CTRL1_STATUS_UPPER_MASK)
+			? PM8XXX_BATT_ALARM_STATUS_ABOVE_UPPER : 0);
+	mutex_unlock(&chip->lock);
+
+	if (rc) {
+		pr_err("pm8xxx_readb failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	return status;
+}
+EXPORT_SYMBOL(pm8xxx_batt_alarm_status_read);
+
+/**
+ * pm8xxx_batt_alarm_hold_time_set - set hold time of interrupt output *
+ * @hold_time:	amount of time that battery voltage must remain outside of the
+ *		threshold range before the battery alarm interrupt triggers
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_batt_alarm_hold_time_set(enum pm8xxx_batt_alarm_hold_time hold_time)
+{
+	struct pm8xxx_batt_alarm_chip *chip = the_battalarm;
+	int rc;
+	u8 reg_ctrl1 = 0;
+
+	if (!chip) {
+		pr_err("no battery alarm device found.\n");
+		return -ENXIO;
+	}
+
+	if (hold_time < CTRL1_HOLD_TIME_MIN
+	    || hold_time > CTRL1_HOLD_TIME_MAX) {
+
+		pr_err("hold time, %d, is outside of allowable range: "
+			"[%d, %d]\n", hold_time, CTRL1_HOLD_TIME_MIN,
+			CTRL1_HOLD_TIME_MAX);
+		return -EINVAL;
+	}
+
+	reg_ctrl1 = hold_time << CTRL1_HOLD_TIME_SHIFT;
+
+	mutex_lock(&chip->lock);
+	rc = pm8xxx_reg_write(chip, chip->cdata.reg_addr_ctrl1, reg_ctrl1,
+			      CTRL1_HOLD_TIME_MASK, &chip->reg_ctrl1);
+	mutex_unlock(&chip->lock);
+
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_batt_alarm_hold_time_set);
+
+/**
+ * pm8xxx_batt_alarm_pwm_rate_set - set battery alarm update rate *
+ * @use_pwm:		1 = use PWM update rate, 0 = comparators always active
+ * @clock_scaler:	PWM clock scaler = 2 to 9
+ * @clock_divider:	PWM clock divider = 2 to 8
+ *
+ * This function sets the rate at which the battery alarm module enables
+ * the threshold comparators.  The rate is determined by the following equation:
+ *
+ * f_update = (1024 Hz) / (clock_divider * (2 ^ clock_scaler))
+ *
+ * Thus, the update rate can range from 0.25 Hz to 128 Hz.
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_batt_alarm_pwm_rate_set(int use_pwm, int clock_scaler,
+				   int clock_divider)
+{
+	struct pm8xxx_batt_alarm_chip *chip = the_battalarm;
+	int rc;
+	u8 reg_pwm_ctrl = 0, mask = 0;
+
+	if (!chip) {
+		pr_err("no battery alarm device found.\n");
+		return -ENXIO;
+	}
+
+	if (use_pwm && (clock_scaler < PWM_CTRL_PRE_INPUT_MIN
+	    || clock_scaler > PWM_CTRL_PRE_INPUT_MAX)) {
+		pr_err("PWM clock scaler, %d, is outside of allowable range: "
+			"[%d, %d]\n", clock_scaler, PWM_CTRL_PRE_INPUT_MIN,
+			PWM_CTRL_PRE_INPUT_MAX);
+		return -EINVAL;
+	}
+
+	if (use_pwm && (clock_divider < PWM_CTRL_DIV_INPUT_MIN
+	    || clock_divider > PWM_CTRL_DIV_INPUT_MAX)) {
+		pr_err("PWM clock divider, %d, is outside of allowable range: "
+			"[%d, %d]\n", clock_divider, PWM_CTRL_DIV_INPUT_MIN,
+			PWM_CTRL_DIV_INPUT_MAX);
+		return -EINVAL;
+	}
+
+	if (!use_pwm) {
+		/* Turn off PWM control and always enable. */
+		reg_pwm_ctrl = PWM_CTRL_ALARM_EN_ALWAYS;
+		mask = PWM_CTRL_ALARM_EN_MASK;
+	} else {
+		/* Use PWM control. */
+		reg_pwm_ctrl = PWM_CTRL_ALARM_EN_PWM;
+		mask = PWM_CTRL_ALARM_EN_MASK | PWM_CTRL_PRE_MASK
+			| PWM_CTRL_DIV_MASK;
+
+		clock_scaler -= PWM_CTRL_PRE_INPUT_MIN - PWM_CTRL_PRE_MIN;
+		clock_divider -= PWM_CTRL_DIV_INPUT_MIN - PWM_CTRL_DIV_MIN;
+
+		reg_pwm_ctrl |= (clock_scaler << PWM_CTRL_PRE_SHIFT)
+				& PWM_CTRL_PRE_MASK;
+		reg_pwm_ctrl |= (clock_divider << PWM_CTRL_DIV_SHIFT)
+				& PWM_CTRL_DIV_MASK;
+	}
+
+	mutex_lock(&chip->lock);
+	rc = pm8xxx_reg_write(chip, chip->cdata.reg_addr_pwm_ctrl, reg_pwm_ctrl,
+			      mask, &chip->reg_pwm_ctrl);
+	mutex_unlock(&chip->lock);
+
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_batt_alarm_pwm_rate_set);
+
+/*
+ * Handle the BATT_ALARM interrupt:
+ * Battery voltage is above or below threshold range.
+ */
+static irqreturn_t pm8xxx_batt_alarm_isr(int irq, void *data)
+{
+	struct pm8xxx_batt_alarm_chip *chip = data;
+
+	disable_irq_nosync(chip->irq);
+	schedule_work(&chip->irq_work);
+
+	return IRQ_HANDLED;
+}
+
+static void pm8xxx_batt_alarm_isr_work(struct work_struct *work)
+{
+	struct pm8xxx_batt_alarm_chip *chip
+		= container_of(work, struct pm8xxx_batt_alarm_chip, irq_work);
+	int status;
+
+	if (!chip)
+		return;
+
+	status = pm8xxx_batt_alarm_status_read();
+
+	if (status < 0)
+		pr_err("failed to read status, rc=%d\n", status);
+	else
+		srcu_notifier_call_chain(&chip->irq_notifier_list,
+						status, NULL);
+
+	enable_irq(chip->irq);
+}
+
+/**
+ * pm8xxx_batt_alarm_register_notifier - register a notifier to run when a
+ *	battery voltage change interrupt fires
+ * @nb:	notifier block containing callback function to register
+ *
+ * nb->notifier_call must point to a function of this form -
+ * int (*notifier_call)(struct notifier_block *nb, unsigned long status,
+ *			void *unused);
+ * "status" will receive the battery alarm status; "unused" will be NULL.
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_batt_alarm_register_notifier(struct notifier_block *nb)
+{
+	struct pm8xxx_batt_alarm_chip *chip = the_battalarm;
+	int rc;
+
+	if (!chip) {
+		pr_err("no battery alarm device found.\n");
+		return -ENXIO;
+	}
+
+	rc = srcu_notifier_chain_register(&chip->irq_notifier_list, nb);
+	mutex_lock(&chip->lock);
+	if (rc == 0) {
+		if (chip->notifier_count == 0) {
+			enable_irq(chip->irq);
+			rc = irq_set_irq_wake(chip->irq, 1);
+		}
+
+		chip->notifier_count++;
+	}
+
+	mutex_unlock(&chip->lock);
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_batt_alarm_register_notifier);
+
+/**
+ * pm8xxx_batt_alarm_unregister_notifier - unregister a notifier that is run
+ *	when a battery voltage change interrupt fires
+ * @nb:	notifier block containing callback function to unregister
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_batt_alarm_unregister_notifier(struct notifier_block *nb)
+{
+	struct pm8xxx_batt_alarm_chip *chip = the_battalarm;
+	int rc;
+
+	if (!chip) {
+		pr_err("no battery alarm device found.\n");
+		return -ENXIO;
+	}
+
+	rc = srcu_notifier_chain_unregister(&chip->irq_notifier_list, nb);
+	if (rc == 0) {
+		mutex_lock(&chip->lock);
+
+		chip->notifier_count--;
+
+		if (chip->notifier_count == 0) {
+			rc = irq_set_irq_wake(chip->irq, 0);
+			disable_irq(chip->irq);
+		}
+
+		WARN_ON(chip->notifier_count < 0);
+
+		mutex_unlock(&chip->lock);
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_batt_alarm_unregister_notifier);
+
+static int pm8xxx_batt_alarm_reg_init(struct pm8xxx_batt_alarm_chip *chip)
+{
+	int rc = 0;
+
+	/* save the current register states */
+	rc = pm8xxx_readb(chip->dev->parent, chip->cdata.reg_addr_threshold,
+			  &chip->reg_threshold);
+	if (rc)
+		goto bail;
+
+	rc = pm8xxx_readb(chip->dev->parent, chip->cdata.reg_addr_ctrl1,
+			  &chip->reg_ctrl1);
+	if (rc)
+		goto bail;
+
+	rc = pm8xxx_readb(chip->dev->parent, chip->cdata.reg_addr_ctrl2,
+			  &chip->reg_ctrl2);
+	if (rc)
+		goto bail;
+
+	rc = pm8xxx_readb(chip->dev->parent, chip->cdata.reg_addr_pwm_ctrl,
+			  &chip->reg_pwm_ctrl);
+	if (rc)
+		goto bail;
+
+bail:
+	if (rc)
+		pr_err("pm8xxx_readb failed; initial register states "
+			"unknown, rc=%d\n", rc);
+	return rc;
+}
+
+/* TODO: should this default setting function be removed? */
+static int pm8xxx_batt_alarm_config_defaults(void)
+{
+	int rc = 0;
+
+	/* Use default values when no platform data is provided. */
+	rc = pm8xxx_batt_alarm_threshold_set(PM8XXX_BATT_ALARM_LOWER_COMPARATOR,
+		DEFAULT_THRESHOLD_LOWER);
+	if (rc) {
+		pr_err("threshold_set failed, rc=%d\n", rc);
+		goto done;
+	}
+
+	rc = pm8xxx_batt_alarm_threshold_set(PM8XXX_BATT_ALARM_UPPER_COMPARATOR,
+		DEFAULT_THRESHOLD_UPPER);
+	if (rc) {
+		pr_err("threshold_set failed, rc=%d\n", rc);
+		goto done;
+	}
+
+	rc = pm8xxx_batt_alarm_hold_time_set(DEFAULT_HOLD_TIME);
+	if (rc) {
+		pr_err("hold_time_set failed, rc=%d\n", rc);
+		goto done;
+	}
+
+	rc = pm8xxx_batt_alarm_pwm_rate_set(DEFAULT_USE_PWM,
+			DEFAULT_PWM_SCALER, DEFAULT_PWM_DIVIDER);
+	if (rc) {
+		pr_err("pwm_rate_set failed, rc=%d\n", rc);
+		goto done;
+	}
+
+	rc = pm8xxx_batt_alarm_disable(PM8XXX_BATT_ALARM_LOWER_COMPARATOR);
+	if (rc) {
+		pr_err("disable lower failed, rc=%d\n", rc);
+		goto done;
+	}
+
+	rc = pm8xxx_batt_alarm_disable(PM8XXX_BATT_ALARM_UPPER_COMPARATOR);
+	if (rc) {
+		pr_err("disable upper failed, rc=%d\n", rc);
+		goto done;
+	}
+
+done:
+	return rc;
+}
+
+static int __devinit pm8xxx_batt_alarm_probe(struct platform_device *pdev)
+{
+	const struct pm8xxx_batt_alarm_core_data *cdata
+			= pdev->dev.platform_data;
+	struct pm8xxx_batt_alarm_chip *chip;
+	struct resource *res;
+	int rc;
+
+	if (the_battalarm) {
+		pr_err("A PMIC battery alarm device has already probed.\n");
+		return -ENODEV;
+	}
+
+	if (!cdata) {
+		pr_err("missing core data\n");
+		return -EINVAL;
+	}
+
+	if (!cdata->irq_name) {
+		pr_err("missing IRQ name\n");
+		return -EINVAL;
+	}
+
+	chip = kzalloc(sizeof(struct pm8xxx_batt_alarm_chip), GFP_KERNEL);
+	if (chip == NULL) {
+		pr_err("kzalloc() failed.\n");
+		return -ENOMEM;
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+		cdata->irq_name);
+	if (res) {
+		chip->irq = res->start;
+	} else {
+		pr_err("Battery alarm IRQ not specified\n");
+		rc = -EINVAL;
+		goto err_free_chip;
+	}
+
+	chip->dev = &pdev->dev;
+	memcpy(&(chip->cdata), cdata,
+		sizeof(struct pm8xxx_batt_alarm_core_data));
+
+	srcu_init_notifier_head(&chip->irq_notifier_list);
+
+	chip->notifier_count = 0;
+	mutex_init(&chip->lock);
+
+	the_battalarm = chip;
+
+	rc = pm8xxx_batt_alarm_reg_init(chip);
+	if (rc)
+		goto err_free_mutex;
+
+	rc = pm8xxx_batt_alarm_config_defaults();
+	if (rc)
+		goto err_free_mutex;
+
+	INIT_WORK(&chip->irq_work, pm8xxx_batt_alarm_isr_work);
+
+/* TODO: Is it best to trigger on both edges? Should this be configurable? */
+	rc = request_irq(chip->irq, pm8xxx_batt_alarm_isr,
+		IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, cdata->irq_name,
+		chip);
+	if (rc < 0) {
+		pr_err("request_irq(%d) failed, rc=%d\n", chip->irq, rc);
+		goto err_cancel_work;
+	}
+
+	/* Disable the IRQ until a notifier is registered. */
+	disable_irq(chip->irq);
+
+	platform_set_drvdata(pdev, chip);
+
+	return 0;
+
+err_cancel_work:
+	cancel_work_sync(&chip->irq_work);
+err_free_mutex:
+	mutex_destroy(&chip->lock);
+	srcu_cleanup_notifier_head(&chip->irq_notifier_list);
+err_free_chip:
+	kfree(chip);
+	the_battalarm = NULL;
+
+	return rc;
+}
+
+static int __devexit pm8xxx_batt_alarm_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_batt_alarm_chip *chip = platform_get_drvdata(pdev);
+
+	if (chip) {
+		platform_set_drvdata(pdev, NULL);
+		irq_set_irq_wake(chip->irq, 0);
+		free_irq(chip->irq, chip);
+		cancel_work_sync(&chip->irq_work);
+		srcu_cleanup_notifier_head(&chip->irq_notifier_list);
+		mutex_destroy(&chip->lock);
+		kfree(chip);
+		the_battalarm = NULL;
+	}
+
+	return 0;
+}
+
+static struct platform_driver pm8xxx_batt_alarm_driver = {
+	.probe	= pm8xxx_batt_alarm_probe,
+	.remove	= __devexit_p(pm8xxx_batt_alarm_remove),
+	.driver	= {
+		.name = PM8XXX_BATT_ALARM_DEV_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init pm8xxx_batt_alarm_init(void)
+{
+	return platform_driver_register(&pm8xxx_batt_alarm_driver);
+}
+
+static void __exit pm8xxx_batt_alarm_exit(void)
+{
+	platform_driver_unregister(&pm8xxx_batt_alarm_driver);
+}
+
+module_init(pm8xxx_batt_alarm_init);
+module_exit(pm8xxx_batt_alarm_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC PM8xxx Battery Alarm");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8XXX_BATT_ALARM_DEV_NAME);
diff --git a/drivers/mfd/pm8xxx-debug.c b/drivers/mfd/pm8xxx-debug.c
new file mode 100644
index 0000000..d450db3
--- /dev/null
+++ b/drivers/mfd/pm8xxx-debug.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/debugfs.h>
+
+#define PM8XXX_DEBUG_DEV_NAME "pm8xxx-debug"
+
+struct pm8xxx_debug_device {
+	struct mutex		debug_mutex;
+	struct device		*parent;
+	struct dentry		*dir;
+	int			addr;
+};
+
+static bool pm8xxx_debug_addr_is_valid(int addr)
+{
+	if (addr < 0 || addr > 0x3FF) {
+		pr_err("PMIC register address is invalid: %d\n", addr);
+		return false;
+	}
+	return true;
+}
+
+static int pm8xxx_debug_data_set(void *data, u64 val)
+{
+	struct pm8xxx_debug_device *debugdev = data;
+	u8 reg = val;
+	int rc;
+
+	mutex_lock(&debugdev->debug_mutex);
+
+	if (pm8xxx_debug_addr_is_valid(debugdev->addr)) {
+		rc = pm8xxx_writeb(debugdev->parent, debugdev->addr, reg);
+
+		if (rc)
+			pr_err("pm8xxx_writeb(0x%03X)=0x%02X failed: rc=%d\n",
+				debugdev->addr, reg, rc);
+	}
+
+	mutex_unlock(&debugdev->debug_mutex);
+	return 0;
+}
+
+static int pm8xxx_debug_data_get(void *data, u64 *val)
+{
+	struct pm8xxx_debug_device *debugdev = data;
+	int rc;
+	u8 reg;
+
+	mutex_lock(&debugdev->debug_mutex);
+
+	if (pm8xxx_debug_addr_is_valid(debugdev->addr)) {
+		rc = pm8xxx_readb(debugdev->parent, debugdev->addr, &reg);
+
+		if (rc)
+			pr_err("pm8xxx_readb(0x%03X) failed: rc=%d\n",
+				debugdev->addr, rc);
+		else
+			*val = reg;
+	}
+
+	mutex_unlock(&debugdev->debug_mutex);
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(debug_data_fops, pm8xxx_debug_data_get,
+			pm8xxx_debug_data_set, "0x%02llX\n");
+
+static int pm8xxx_debug_addr_set(void *data, u64 val)
+{
+	struct pm8xxx_debug_device *debugdev = data;
+
+	if (pm8xxx_debug_addr_is_valid(val)) {
+		mutex_lock(&debugdev->debug_mutex);
+		debugdev->addr = val;
+		mutex_unlock(&debugdev->debug_mutex);
+	}
+
+	return 0;
+}
+
+static int pm8xxx_debug_addr_get(void *data, u64 *val)
+{
+	struct pm8xxx_debug_device *debugdev = data;
+
+	mutex_lock(&debugdev->debug_mutex);
+
+	if (pm8xxx_debug_addr_is_valid(debugdev->addr))
+		*val = debugdev->addr;
+
+	mutex_unlock(&debugdev->debug_mutex);
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(debug_addr_fops, pm8xxx_debug_addr_get,
+			pm8xxx_debug_addr_set, "0x%03llX\n");
+
+static int __devinit pm8xxx_debug_probe(struct platform_device *pdev)
+{
+	char *name = pdev->dev.platform_data;
+	struct pm8xxx_debug_device *debugdev;
+	struct dentry *dir;
+	struct dentry *temp;
+	int rc;
+
+	if (name == NULL) {
+		pr_err("debugfs directory name must be specified in "
+			"platform_data pointer\n");
+		return -EINVAL;
+	}
+
+	debugdev = kzalloc(sizeof(struct pm8xxx_debug_device), GFP_KERNEL);
+	if (debugdev == NULL) {
+		pr_err("kzalloc failed\n");
+		return -ENOMEM;
+	}
+
+	debugdev->parent = pdev->dev.parent;
+	debugdev->addr = -1;
+
+	dir = debugfs_create_dir(name, NULL);
+	if (dir == NULL || IS_ERR(dir)) {
+		pr_err("debugfs_create_dir failed: rc=%ld\n", PTR_ERR(dir));
+		rc = PTR_ERR(dir);
+		goto dir_error;
+	}
+
+	temp = debugfs_create_file("addr", S_IRUSR | S_IWUSR, dir, debugdev,
+				   &debug_addr_fops);
+	if (temp == NULL || IS_ERR(temp)) {
+		pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp));
+		rc = PTR_ERR(temp);
+		goto file_error;
+	}
+
+	temp = debugfs_create_file("data", S_IRUSR | S_IWUSR, dir, debugdev,
+				   &debug_data_fops);
+	if (temp == NULL || IS_ERR(temp)) {
+		pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp));
+		rc = PTR_ERR(temp);
+		goto file_error;
+	}
+
+	mutex_init(&debugdev->debug_mutex);
+
+	debugdev->dir = dir;
+	platform_set_drvdata(pdev, debugdev);
+
+	return 0;
+
+file_error:
+	debugfs_remove_recursive(dir);
+dir_error:
+	kfree(debugdev);
+
+	return rc;
+}
+
+static int __devexit pm8xxx_debug_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_debug_device *debugdev = platform_get_drvdata(pdev);
+
+	if (debugdev) {
+		debugfs_remove_recursive(debugdev->dir);
+		mutex_destroy(&debugdev->debug_mutex);
+		kfree(debugdev);
+	}
+
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver pm8xxx_debug_driver = {
+	.probe		= pm8xxx_debug_probe,
+	.remove		= __devexit_p(pm8xxx_debug_remove),
+	.driver		= {
+		.name	= PM8XXX_DEBUG_DEV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init pm8xxx_debug_init(void)
+{
+	return platform_driver_register(&pm8xxx_debug_driver);
+}
+subsys_initcall(pm8xxx_debug_init);
+
+static void __exit pm8xxx_debug_exit(void)
+{
+	platform_driver_unregister(&pm8xxx_debug_driver);
+}
+module_exit(pm8xxx_debug_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PM8XXX Debug driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8XXX_DEBUG_DEV_NAME);
diff --git a/drivers/mfd/pm8xxx-irq.c b/drivers/mfd/pm8xxx-irq.c
index d452dd0..cc156b3 100644
--- a/drivers/mfd/pm8xxx-irq.c
+++ b/drivers/mfd/pm8xxx-irq.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -13,6 +13,7 @@
 
 #define pr_fmt(fmt)	"%s: " fmt, __func__
 
+#include <linux/export.h>
 #include <linux/err.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
@@ -24,17 +25,15 @@
 
 /* PMIC8xxx IRQ */
 
-#define	SSBI_REG_ADDR_IRQ_BASE		0x1BB
-
-#define	SSBI_REG_ADDR_IRQ_ROOT		(SSBI_REG_ADDR_IRQ_BASE + 0)
-#define	SSBI_REG_ADDR_IRQ_M_STATUS1	(SSBI_REG_ADDR_IRQ_BASE + 1)
-#define	SSBI_REG_ADDR_IRQ_M_STATUS2	(SSBI_REG_ADDR_IRQ_BASE + 2)
-#define	SSBI_REG_ADDR_IRQ_M_STATUS3	(SSBI_REG_ADDR_IRQ_BASE + 3)
-#define	SSBI_REG_ADDR_IRQ_M_STATUS4	(SSBI_REG_ADDR_IRQ_BASE + 4)
-#define	SSBI_REG_ADDR_IRQ_BLK_SEL	(SSBI_REG_ADDR_IRQ_BASE + 5)
-#define	SSBI_REG_ADDR_IRQ_IT_STATUS	(SSBI_REG_ADDR_IRQ_BASE + 6)
-#define	SSBI_REG_ADDR_IRQ_CONFIG	(SSBI_REG_ADDR_IRQ_BASE + 7)
-#define	SSBI_REG_ADDR_IRQ_RT_STATUS	(SSBI_REG_ADDR_IRQ_BASE + 8)
+#define SSBI_REG_ADDR_IRQ_ROOT(base)		(base + 0)
+#define SSBI_REG_ADDR_IRQ_M_STATUS1(base)	(base + 1)
+#define SSBI_REG_ADDR_IRQ_M_STATUS2(base)	(base + 2)
+#define SSBI_REG_ADDR_IRQ_M_STATUS3(base)	(base + 3)
+#define SSBI_REG_ADDR_IRQ_M_STATUS4(base)	(base + 4)
+#define SSBI_REG_ADDR_IRQ_BLK_SEL(base)		(base + 5)
+#define SSBI_REG_ADDR_IRQ_IT_STATUS(base)	(base + 6)
+#define SSBI_REG_ADDR_IRQ_CONFIG(base)		(base + 7)
+#define SSBI_REG_ADDR_IRQ_RT_STATUS(base)	(base + 8)
 
 #define	PM_IRQF_LVL_SEL			0x01	/* level select */
 #define	PM_IRQF_MASK_FE			0x02	/* mask falling edge */
@@ -50,6 +49,7 @@
 struct pm_irq_chip {
 	struct device		*dev;
 	spinlock_t		pm_irq_lock;
+	unsigned int		base_addr;
 	unsigned int		devirq;
 	unsigned int		irq_base;
 	unsigned int		num_irqs;
@@ -60,13 +60,14 @@
 
 static int pm8xxx_read_root_irq(const struct pm_irq_chip *chip, u8 *rp)
 {
-	return pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_ROOT, rp);
+	return pm8xxx_readb(chip->dev,
+			SSBI_REG_ADDR_IRQ_ROOT(chip->base_addr), rp);
 }
 
 static int pm8xxx_read_master_irq(const struct pm_irq_chip *chip, u8 m, u8 *bp)
 {
 	return pm8xxx_readb(chip->dev,
-			SSBI_REG_ADDR_IRQ_M_STATUS1 + m, bp);
+			SSBI_REG_ADDR_IRQ_M_STATUS1(chip->base_addr) + m, bp);
 }
 
 static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, u8 bp, u8 *ip)
@@ -74,13 +75,15 @@
 	int	rc;
 
 	spin_lock(&chip->pm_irq_lock);
-	rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
+	rc = pm8xxx_writeb(chip->dev,
+			SSBI_REG_ADDR_IRQ_BLK_SEL(chip->base_addr), bp);
 	if (rc) {
 		pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
 		goto bail;
 	}
 
-	rc = pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_IT_STATUS, ip);
+	rc = pm8xxx_readb(chip->dev,
+			SSBI_REG_ADDR_IRQ_IT_STATUS(chip->base_addr), ip);
 	if (rc)
 		pr_err("Failed Reading Status rc=%d\n", rc);
 bail:
@@ -88,19 +91,51 @@
 	return rc;
 }
 
-static int pm8xxx_config_irq(struct pm_irq_chip *chip, u8 bp, u8 cp)
+static int pm8xxx_read_config_irq(struct pm_irq_chip *chip, u8 bp, u8 cp, u8 *r)
 {
 	int	rc;
 
 	spin_lock(&chip->pm_irq_lock);
-	rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
+	rc = pm8xxx_writeb(chip->dev,
+			SSBI_REG_ADDR_IRQ_BLK_SEL(chip->base_addr), bp);
 	if (rc) {
 		pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
 		goto bail;
 	}
 
+	cp &= ~PM_IRQF_WRITE;
+	rc = pm8xxx_writeb(chip->dev,
+			SSBI_REG_ADDR_IRQ_CONFIG(chip->base_addr), cp);
+	if (rc)
+		pr_err("Failed Configuring IRQ rc=%d\n", rc);
+
+	rc = pm8xxx_readb(chip->dev,
+			SSBI_REG_ADDR_IRQ_CONFIG(chip->base_addr), r);
+	if (rc)
+		pr_err("Failed reading IRQ rc=%d\n", rc);
+bail:
+	spin_unlock(&chip->pm_irq_lock);
+	return rc;
+}
+
+static int pm8xxx_write_config_irq(struct pm_irq_chip *chip, u8 bp, u8 cp)
+{
+	int	rc;
+
+	spin_lock(&chip->pm_irq_lock);
+	rc = pm8xxx_writeb(chip->dev,
+			SSBI_REG_ADDR_IRQ_BLK_SEL(chip->base_addr), bp);
+	if (rc) {
+		pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
+		goto bail;
+	}
+	/*
+	 * Set the write bit here as this could be a unrequested irq
+	 * whose PM_IRQF_WRITE bit is not set
+	 */
 	cp |= PM_IRQF_WRITE;
-	rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_CONFIG, cp);
+	rc = pm8xxx_writeb(chip->dev,
+			SSBI_REG_ADDR_IRQ_CONFIG(chip->base_addr), cp);
 	if (rc)
 		pr_err("Failed Configuring IRQ rc=%d\n", rc);
 bail:
@@ -157,17 +192,16 @@
 	return ret;
 }
 
-static void pm8xxx_irq_handler(unsigned int irq, struct irq_desc *desc)
+static irqreturn_t pm8xxx_irq_handler(int irq, void *data)
 {
-	struct pm_irq_chip *chip = irq_desc_get_handler_data(desc);
-	struct irq_chip *irq_chip = irq_desc_get_chip(desc);
+	struct pm_irq_chip *chip = data;
 	u8	root;
 	int	i, ret, masters = 0;
 
 	ret = pm8xxx_read_root_irq(chip, &root);
 	if (ret) {
 		pr_err("Can't read root status ret=%d\n", ret);
-		return;
+		return IRQ_HANDLED;
 	}
 
 	/* on pm8xxx series masters start from bit 1 of the root */
@@ -178,7 +212,27 @@
 		if (masters & (1 << i))
 			pm8xxx_irq_master_handler(chip, i);
 
-	irq_chip->irq_ack(&desc->irq_data);
+	return IRQ_HANDLED;
+}
+
+static void pm8xxx_irq_mask(struct irq_data *d)
+{
+	struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+	unsigned int pmirq = d->irq - chip->irq_base;
+	int	master, irq_bit;
+	u8	block, config;
+
+	block = pmirq / 8;
+	master = block / 8;
+	irq_bit = pmirq % 8;
+
+	if (chip->config[pmirq] == 0) {
+		pr_warn("masking rogue irq=%d pmirq=%d\n", d->irq, pmirq);
+		chip->config[pmirq] = irq_bit << PM_IRQF_BITS_SHIFT;
+	}
+
+	config = chip->config[pmirq] | PM_IRQF_MASK_ALL;
+	pm8xxx_write_config_irq(chip, block, config);
 }
 
 static void pm8xxx_irq_mask_ack(struct irq_data *d)
@@ -192,8 +246,13 @@
 	master = block / 8;
 	irq_bit = pmirq % 8;
 
+	if (chip->config[pmirq] == 0) {
+		pr_warn("mask acking rogue irq=%d pmirq=%d\n", d->irq, pmirq);
+		chip->config[pmirq] = irq_bit << PM_IRQF_BITS_SHIFT;
+	}
+
 	config = chip->config[pmirq] | PM_IRQF_MASK_ALL | PM_IRQF_CLR;
-	pm8xxx_config_irq(chip, block, config);
+	pm8xxx_write_config_irq(chip, block, config);
 }
 
 static void pm8xxx_irq_unmask(struct irq_data *d)
@@ -201,14 +260,17 @@
 	struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
 	unsigned int pmirq = d->irq - chip->irq_base;
 	int	master, irq_bit;
-	u8	block, config;
+	u8	block, config, hw_conf;
 
 	block = pmirq / 8;
 	master = block / 8;
 	irq_bit = pmirq % 8;
 
 	config = chip->config[pmirq];
-	pm8xxx_config_irq(chip, block, config);
+	pm8xxx_read_config_irq(chip, block, config, &hw_conf);
+	/* check if it is masked */
+	if ((hw_conf & PM_IRQF_MASK_ALL) == PM_IRQF_MASK_ALL)
+		pm8xxx_write_config_irq(chip, block, config);
 }
 
 static int pm8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type)
@@ -238,8 +300,14 @@
 			chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
 	}
 
+	/*
+	 * The PM_IRQF_WRITE flag serves as an indication that this interrupt
+	 * been requested
+	 */
+	chip->config[pmirq] |= PM_IRQF_WRITE;
+
 	config = chip->config[pmirq] | PM_IRQF_CLR;
-	return pm8xxx_config_irq(chip, block, config);
+	return pm8xxx_write_config_irq(chip, block, config);
 }
 
 static int pm8xxx_irq_set_wake(struct irq_data *d, unsigned int on)
@@ -247,12 +315,21 @@
 	return 0;
 }
 
+static int pm8xxx_irq_read_line(struct irq_data *d)
+{
+	struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+
+	return pm8xxx_get_irq_stat(chip, d->irq);
+}
+
 static struct irq_chip pm8xxx_irq_chip = {
 	.name		= "pm8xxx",
+	.irq_mask	= pm8xxx_irq_mask,
 	.irq_mask_ack	= pm8xxx_irq_mask_ack,
 	.irq_unmask	= pm8xxx_irq_unmask,
 	.irq_set_type	= pm8xxx_irq_set_type,
 	.irq_set_wake	= pm8xxx_irq_set_wake,
+	.irq_read_line	= pm8xxx_irq_read_line,
 	.flags		= IRQCHIP_MASK_ON_SUSPEND,
 };
 
@@ -286,14 +363,16 @@
 
 	spin_lock_irqsave(&chip->pm_irq_lock, flags);
 
-	rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, block);
+	rc = pm8xxx_writeb(chip->dev,
+			SSBI_REG_ADDR_IRQ_BLK_SEL(chip->base_addr), block);
 	if (rc) {
 		pr_err("Failed Selecting block irq=%d pmirq=%d blk=%d rc=%d\n",
 			irq, pmirq, block, rc);
 		goto bail_out;
 	}
 
-	rc = pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_RT_STATUS, &bits);
+	rc = pm8xxx_readb(chip->dev,
+			SSBI_REG_ADDR_IRQ_RT_STATUS(chip->base_addr), &bits);
 	if (rc) {
 		pr_err("Failed Configuring irq=%d pmirq=%d blk=%d rc=%d\n",
 			irq, pmirq, block, rc);
@@ -339,6 +418,7 @@
 	chip->devirq = devirq;
 	chip->irq_base = pdata->irq_base;
 	chip->num_irqs = pdata->irq_cdata.nirqs;
+	chip->base_addr = pdata->irq_cdata.base_addr;
 	chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8);
 	chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8);
 	spin_lock_init(&chip->pm_irq_lock);
@@ -355,15 +435,22 @@
 #endif
 	}
 
-	irq_set_irq_type(devirq, pdata->irq_trigger_flag);
-	irq_set_handler_data(devirq, chip);
-	irq_set_chained_handler(devirq, pm8xxx_irq_handler);
-	set_irq_wake(devirq, 1);
+	if (devirq != 0) {
+		rc = request_irq(devirq, pm8xxx_irq_handler,
+				pdata->irq_trigger_flag,
+				"pm8xxx_usr_irq", chip);
+		if (rc) {
+			pr_err("failed to request_irq for %d rc=%d\n",
+								devirq, rc);
+		} else {
+			irq_set_irq_wake(devirq, 1);
+		}
+	}
 
 	return chip;
 }
 
-int __devexit pm8xxx_irq_exit(struct pm_irq_chip *chip)
+int pm8xxx_irq_exit(struct pm_irq_chip *chip)
 {
 	irq_set_chained_handler(chip->devirq, NULL);
 	kfree(chip);
diff --git a/drivers/mfd/pm8xxx-misc.c b/drivers/mfd/pm8xxx-misc.c
new file mode 100644
index 0000000..0af013e
--- /dev/null
+++ b/drivers/mfd/pm8xxx-misc.c
@@ -0,0 +1,1256 @@
+/*
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/misc.h>
+
+/* PON CTRL 1 register */
+#define REG_PM8XXX_PON_CTRL_1			0x01C
+
+#define PON_CTRL_1_PULL_UP_MASK			0xE0
+#define PON_CTRL_1_USB_PWR_EN			0x10
+
+#define PON_CTRL_1_WD_EN_MASK			0x08
+#define PON_CTRL_1_WD_EN_RESET			0x08
+#define PON_CTRL_1_WD_EN_PWR_OFF		0x00
+
+/* PON CNTL registers */
+#define REG_PM8058_PON_CNTL_4			0x098
+#define REG_PM8901_PON_CNTL_4			0x099
+#define REG_PM8018_PON_CNTL_4			0x01E
+#define REG_PM8921_PON_CNTL_4			0x01E
+#define REG_PM8058_PON_CNTL_5			0x07B
+#define REG_PM8901_PON_CNTL_5			0x09A
+#define REG_PM8018_PON_CNTL_5			0x01F
+#define REG_PM8921_PON_CNTL_5			0x01F
+
+#define PON_CTRL_4_RESET_EN_MASK		0x01
+#define PON_CTRL_4_SHUTDOWN_ON_RESET		0x0
+#define PON_CTRL_4_RESTART_ON_RESET		0x1
+#define PON_CTRL_5_HARD_RESET_EN_MASK		0x08
+#define PON_CTRL_5_HARD_RESET_EN		0x08
+#define PON_CTRL_5_HARD_RESET_DIS		0x00
+
+/* Regulator master enable addresses */
+#define REG_PM8058_VREG_EN_MSM			0x018
+#define REG_PM8058_VREG_EN_GRP_5_4		0x1C8
+
+/* Regulator control registers for shutdown/reset */
+#define REG_PM8058_S0_CTRL			0x004
+#define REG_PM8058_S1_CTRL			0x005
+#define REG_PM8058_S3_CTRL			0x111
+#define REG_PM8058_L21_CTRL			0x120
+#define REG_PM8058_L22_CTRL			0x121
+
+#define PM8058_REGULATOR_ENABLE_MASK		0x80
+#define PM8058_REGULATOR_ENABLE			0x80
+#define PM8058_REGULATOR_DISABLE		0x00
+#define PM8058_REGULATOR_PULL_DOWN_MASK		0x40
+#define PM8058_REGULATOR_PULL_DOWN_EN		0x40
+
+/* Buck CTRL register */
+#define PM8058_SMPS_LEGACY_VREF_SEL		0x20
+#define PM8058_SMPS_LEGACY_VPROG_MASK		0x1F
+#define PM8058_SMPS_ADVANCED_BAND_MASK		0xC0
+#define PM8058_SMPS_ADVANCED_BAND_SHIFT		6
+#define PM8058_SMPS_ADVANCED_VPROG_MASK		0x3F
+
+/* Buck TEST2 registers for shutdown/reset */
+#define REG_PM8058_S0_TEST2			0x084
+#define REG_PM8058_S1_TEST2			0x085
+#define REG_PM8058_S3_TEST2			0x11A
+
+#define PM8058_REGULATOR_BANK_WRITE		0x80
+#define PM8058_REGULATOR_BANK_MASK		0x70
+#define PM8058_REGULATOR_BANK_SHIFT		4
+#define PM8058_REGULATOR_BANK_SEL(n)	((n) << PM8058_REGULATOR_BANK_SHIFT)
+
+/* Buck TEST2 register bank 1 */
+#define PM8058_SMPS_LEGACY_VLOW_SEL		0x01
+
+/* Buck TEST2 register bank 7 */
+#define PM8058_SMPS_ADVANCED_MODE_MASK		0x02
+#define PM8058_SMPS_ADVANCED_MODE		0x02
+#define PM8058_SMPS_LEGACY_MODE			0x00
+
+/* SLEEP CTRL register */
+#define REG_PM8058_SLEEP_CTRL			0x02B
+#define REG_PM8921_SLEEP_CTRL			0x10A
+#define REG_PM8018_SLEEP_CTRL			0x10A
+
+#define SLEEP_CTRL_SMPL_EN_MASK			0x04
+#define SLEEP_CTRL_SMPL_EN_RESET		0x04
+#define SLEEP_CTRL_SMPL_EN_PWR_OFF		0x00
+
+#define SLEEP_CTRL_SMPL_SEL_MASK		0x03
+#define SLEEP_CTRL_SMPL_SEL_MIN			0
+#define SLEEP_CTRL_SMPL_SEL_MAX			3
+
+/* FTS regulator PMR registers */
+#define REG_PM8901_REGULATOR_S1_PMR		0xA7
+#define REG_PM8901_REGULATOR_S2_PMR		0xA8
+#define REG_PM8901_REGULATOR_S3_PMR		0xA9
+#define REG_PM8901_REGULATOR_S4_PMR		0xAA
+
+#define PM8901_REGULATOR_PMR_STATE_MASK		0x60
+#define PM8901_REGULATOR_PMR_STATE_OFF		0x20
+
+/* COINCELL CHG registers */
+#define REG_PM8058_COIN_CHG			0x02F
+#define REG_PM8921_COIN_CHG			0x09C
+#define REG_PM8018_COIN_CHG			0x09C
+
+#define COINCELL_RESISTOR_SHIFT			0x2
+
+/* GP TEST register */
+#define REG_PM8XXX_GP_TEST_1			0x07A
+
+/* Stay on configuration */
+#define PM8XXX_STAY_ON_CFG			0x92
+
+/* GPIO UART MUX CTRL registers */
+#define REG_PM8XXX_GPIO_MUX_CTRL		0x1CC
+
+#define UART_PATH_SEL_MASK			0x60
+#define UART_PATH_SEL_SHIFT			0x5
+
+#define USB_ID_PU_EN_MASK			0x10	/* PM8921 family only */
+#define USB_ID_PU_EN_SHIFT			4
+
+/* Shutdown/restart delays to allow for LDO 7/dVdd regulator load settling. */
+#define PM8901_DELAY_AFTER_REG_DISABLE_MS	4
+#define PM8901_DELAY_BEFORE_SHUTDOWN_MS		8
+
+#define REG_PM8XXX_XO_CNTRL_2	0x114
+#define MP3_1_MASK	0xE0
+#define MP3_2_MASK	0x1C
+#define MP3_1_SHIFT	5
+#define MP3_2_SHIFT	2
+
+#define REG_HSED_BIAS0_CNTL2		0xA1
+#define REG_HSED_BIAS1_CNTL2		0x135
+#define REG_HSED_BIAS2_CNTL2		0x138
+#define HSED_EN_MASK			0xC0
+
+struct pm8xxx_misc_chip {
+	struct list_head			link;
+	struct pm8xxx_misc_platform_data	pdata;
+	struct device				*dev;
+	enum pm8xxx_version			version;
+	u64					osc_halt_count;
+};
+
+static LIST_HEAD(pm8xxx_misc_chips);
+static DEFINE_SPINLOCK(pm8xxx_misc_chips_lock);
+
+static int pm8xxx_misc_masked_write(struct pm8xxx_misc_chip *chip, u16 addr,
+				    u8 mask, u8 val)
+{
+	int rc;
+	u8 reg;
+
+	rc = pm8xxx_readb(chip->dev->parent, addr, &reg);
+	if (rc) {
+		pr_err("pm8xxx_readb(0x%03X) failed, rc=%d\n", addr, rc);
+		return rc;
+	}
+	reg &= ~mask;
+	reg |= val & mask;
+	rc = pm8xxx_writeb(chip->dev->parent, addr, reg);
+	if (rc)
+		pr_err("pm8xxx_writeb(0x%03X)=0x%02X failed, rc=%d\n", addr,
+			reg, rc);
+	return rc;
+}
+
+/*
+ * Set an SMPS regulator to be disabled in its CTRL register, but enabled
+ * in the master enable register.  Also set it's pull down enable bit.
+ * Take care to make sure that the output voltage doesn't change if switching
+ * from advanced mode to legacy mode.
+ */
+static int
+__pm8058_disable_smps_locally_set_pull_down(struct pm8xxx_misc_chip *chip,
+	u16 ctrl_addr, u16 test2_addr, u16 master_enable_addr,
+	u8 master_enable_bit)
+{
+	int rc = 0;
+	u8 vref_sel, vlow_sel, band, vprog, bank, reg;
+
+	bank = PM8058_REGULATOR_BANK_SEL(7);
+	rc = pm8xxx_writeb(chip->dev->parent, test2_addr, bank);
+	if (rc) {
+		pr_err("%s: pm8xxx_writeb(0x%03X) failed: rc=%d\n", __func__,
+			test2_addr, rc);
+		goto done;
+	}
+
+	rc = pm8xxx_readb(chip->dev->parent, test2_addr, &reg);
+	if (rc) {
+		pr_err("%s: FAIL pm8xxx_readb(0x%03X): rc=%d\n",
+		       __func__, test2_addr, rc);
+		goto done;
+	}
+
+	/* Check if in advanced mode. */
+	if ((reg & PM8058_SMPS_ADVANCED_MODE_MASK) ==
+					PM8058_SMPS_ADVANCED_MODE) {
+		/* Determine current output voltage. */
+		rc = pm8xxx_readb(chip->dev->parent, ctrl_addr, &reg);
+		if (rc) {
+			pr_err("%s: FAIL pm8xxx_readb(0x%03X): rc=%d\n",
+			       __func__, ctrl_addr, rc);
+			goto done;
+		}
+
+		band = (reg & PM8058_SMPS_ADVANCED_BAND_MASK)
+			>> PM8058_SMPS_ADVANCED_BAND_SHIFT;
+		switch (band) {
+		case 3:
+			vref_sel = 0;
+			vlow_sel = 0;
+			break;
+		case 2:
+			vref_sel = PM8058_SMPS_LEGACY_VREF_SEL;
+			vlow_sel = 0;
+			break;
+		case 1:
+			vref_sel = PM8058_SMPS_LEGACY_VREF_SEL;
+			vlow_sel = PM8058_SMPS_LEGACY_VLOW_SEL;
+			break;
+		default:
+			pr_err("%s: regulator already disabled\n", __func__);
+			return -EPERM;
+		}
+		vprog = (reg & PM8058_SMPS_ADVANCED_VPROG_MASK);
+		/* Round up if fine step is in use. */
+		vprog = (vprog + 1) >> 1;
+		if (vprog > PM8058_SMPS_LEGACY_VPROG_MASK)
+			vprog = PM8058_SMPS_LEGACY_VPROG_MASK;
+
+		/* Set VLOW_SEL bit. */
+		bank = PM8058_REGULATOR_BANK_SEL(1);
+		rc = pm8xxx_writeb(chip->dev->parent, test2_addr, bank);
+		if (rc) {
+			pr_err("%s: FAIL pm8xxx_writeb(0x%03X): rc=%d\n",
+			       __func__, test2_addr, rc);
+			goto done;
+		}
+
+		rc = pm8xxx_misc_masked_write(chip, test2_addr,
+			PM8058_REGULATOR_BANK_WRITE | PM8058_REGULATOR_BANK_MASK
+				| PM8058_SMPS_LEGACY_VLOW_SEL,
+			PM8058_REGULATOR_BANK_WRITE |
+			PM8058_REGULATOR_BANK_SEL(1) | vlow_sel);
+		if (rc)
+			goto done;
+
+		/* Switch to legacy mode */
+		bank = PM8058_REGULATOR_BANK_SEL(7);
+		rc = pm8xxx_writeb(chip->dev->parent, test2_addr, bank);
+		if (rc) {
+			pr_err("%s: FAIL pm8xxx_writeb(0x%03X): rc=%d\n",
+					__func__, test2_addr, rc);
+			goto done;
+		}
+		rc = pm8xxx_misc_masked_write(chip, test2_addr,
+				PM8058_REGULATOR_BANK_WRITE |
+				PM8058_REGULATOR_BANK_MASK |
+				PM8058_SMPS_ADVANCED_MODE_MASK,
+				PM8058_REGULATOR_BANK_WRITE |
+				PM8058_REGULATOR_BANK_SEL(7) |
+				PM8058_SMPS_LEGACY_MODE);
+		if (rc)
+			goto done;
+
+		/* Enable locally, enable pull down, keep voltage the same. */
+		rc = pm8xxx_misc_masked_write(chip, ctrl_addr,
+			PM8058_REGULATOR_ENABLE_MASK |
+			PM8058_REGULATOR_PULL_DOWN_MASK |
+			PM8058_SMPS_LEGACY_VREF_SEL |
+			PM8058_SMPS_LEGACY_VPROG_MASK,
+			PM8058_REGULATOR_ENABLE | PM8058_REGULATOR_PULL_DOWN_EN
+				| vref_sel | vprog);
+		if (rc)
+			goto done;
+	}
+
+	/* Enable in master control register. */
+	rc = pm8xxx_misc_masked_write(chip, master_enable_addr,
+			master_enable_bit, master_enable_bit);
+	if (rc)
+		goto done;
+
+	/* Disable locally and enable pull down. */
+	rc = pm8xxx_misc_masked_write(chip, ctrl_addr,
+		PM8058_REGULATOR_ENABLE_MASK | PM8058_REGULATOR_PULL_DOWN_MASK,
+		PM8058_REGULATOR_DISABLE | PM8058_REGULATOR_PULL_DOWN_EN);
+
+done:
+	return rc;
+}
+
+static int
+__pm8058_disable_ldo_locally_set_pull_down(struct pm8xxx_misc_chip *chip,
+		u16 ctrl_addr, u16 master_enable_addr, u8 master_enable_bit)
+{
+	int rc;
+
+	/* Enable LDO in master control register. */
+	rc = pm8xxx_misc_masked_write(chip, master_enable_addr,
+			master_enable_bit, master_enable_bit);
+	if (rc)
+		goto done;
+
+	/* Disable LDO in CTRL register and set pull down */
+	rc = pm8xxx_misc_masked_write(chip, ctrl_addr,
+		PM8058_REGULATOR_ENABLE_MASK | PM8058_REGULATOR_PULL_DOWN_MASK,
+		PM8058_REGULATOR_DISABLE | PM8058_REGULATOR_PULL_DOWN_EN);
+
+done:
+	return rc;
+}
+
+static int __pm8018_reset_pwr_off(struct pm8xxx_misc_chip *chip, int reset)
+{
+	int rc;
+
+	/* Enable SMPL if resetting is desired. */
+	rc = pm8xxx_misc_masked_write(chip, REG_PM8018_SLEEP_CTRL,
+	       SLEEP_CTRL_SMPL_EN_MASK,
+	       (reset ? SLEEP_CTRL_SMPL_EN_RESET : SLEEP_CTRL_SMPL_EN_PWR_OFF));
+	if (rc) {
+		pr_err("pm8xxx_misc_masked_write failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	/*
+	 * Select action to perform (reset or shutdown) when PS_HOLD goes low.
+	 * Also ensure that KPD, CBL0, and CBL1 pull ups are enabled and that
+	 * USB charging is enabled.
+	 */
+	rc = pm8xxx_misc_masked_write(chip, REG_PM8XXX_PON_CTRL_1,
+		PON_CTRL_1_PULL_UP_MASK | PON_CTRL_1_USB_PWR_EN
+		| PON_CTRL_1_WD_EN_MASK,
+		PON_CTRL_1_PULL_UP_MASK | PON_CTRL_1_USB_PWR_EN
+		| (reset ? PON_CTRL_1_WD_EN_RESET : PON_CTRL_1_WD_EN_PWR_OFF));
+	if (rc)
+		pr_err("pm8xxx_misc_masked_write failed, rc=%d\n", rc);
+
+	return rc;
+}
+
+static int __pm8058_reset_pwr_off(struct pm8xxx_misc_chip *chip, int reset)
+{
+	int rc;
+
+	/* When shutting down, enable active pulldowns on important rails. */
+	if (!reset) {
+		/* Disable SMPS's 0,1,3 locally and set pulldown enable bits. */
+		__pm8058_disable_smps_locally_set_pull_down(chip,
+			REG_PM8058_S0_CTRL, REG_PM8058_S0_TEST2,
+			REG_PM8058_VREG_EN_MSM, BIT(7));
+		__pm8058_disable_smps_locally_set_pull_down(chip,
+			REG_PM8058_S1_CTRL, REG_PM8058_S1_TEST2,
+			REG_PM8058_VREG_EN_MSM, BIT(6));
+		__pm8058_disable_smps_locally_set_pull_down(chip,
+			REG_PM8058_S3_CTRL, REG_PM8058_S3_TEST2,
+			REG_PM8058_VREG_EN_GRP_5_4, BIT(7) | BIT(4));
+		/* Disable LDO 21 locally and set pulldown enable bit. */
+		__pm8058_disable_ldo_locally_set_pull_down(chip,
+			REG_PM8058_L21_CTRL, REG_PM8058_VREG_EN_GRP_5_4,
+			BIT(1));
+	}
+
+	/*
+	 * Fix-up: Set regulator LDO22 to 1.225 V in high power mode. Leave its
+	 * pull-down state intact. This ensures a safe shutdown.
+	 */
+	rc = pm8xxx_misc_masked_write(chip, REG_PM8058_L22_CTRL, 0xBF, 0x93);
+	if (rc) {
+		pr_err("pm8xxx_misc_masked_write failed, rc=%d\n", rc);
+		goto read_write_err;
+	}
+
+	/* Enable SMPL if resetting is desired. */
+	rc = pm8xxx_misc_masked_write(chip, REG_PM8058_SLEEP_CTRL,
+	       SLEEP_CTRL_SMPL_EN_MASK,
+	       (reset ? SLEEP_CTRL_SMPL_EN_RESET : SLEEP_CTRL_SMPL_EN_PWR_OFF));
+	if (rc) {
+		pr_err("pm8xxx_misc_masked_write failed, rc=%d\n", rc);
+		goto read_write_err;
+	}
+
+	/*
+	 * Select action to perform (reset or shutdown) when PS_HOLD goes low.
+	 * Also ensure that KPD, CBL0, and CBL1 pull ups are enabled and that
+	 * USB charging is enabled.
+	 */
+	rc = pm8xxx_misc_masked_write(chip, REG_PM8XXX_PON_CTRL_1,
+		PON_CTRL_1_PULL_UP_MASK | PON_CTRL_1_USB_PWR_EN
+		| PON_CTRL_1_WD_EN_MASK,
+		PON_CTRL_1_PULL_UP_MASK | PON_CTRL_1_USB_PWR_EN
+		| (reset ? PON_CTRL_1_WD_EN_RESET : PON_CTRL_1_WD_EN_PWR_OFF));
+	if (rc) {
+		pr_err("pm8xxx_misc_masked_write failed, rc=%d\n", rc);
+		goto read_write_err;
+	}
+
+read_write_err:
+	return rc;
+}
+
+static int __pm8901_reset_pwr_off(struct pm8xxx_misc_chip *chip, int reset)
+{
+	int rc = 0, i;
+	u8 pmr_addr[4] = {
+		REG_PM8901_REGULATOR_S2_PMR,
+		REG_PM8901_REGULATOR_S3_PMR,
+		REG_PM8901_REGULATOR_S4_PMR,
+		REG_PM8901_REGULATOR_S1_PMR,
+	};
+
+	/* Fix-up: Turn off regulators S1, S2, S3, S4 when shutting down. */
+	if (!reset) {
+		for (i = 0; i < 4; i++) {
+			rc = pm8xxx_misc_masked_write(chip, pmr_addr[i],
+				PM8901_REGULATOR_PMR_STATE_MASK,
+				PM8901_REGULATOR_PMR_STATE_OFF);
+			if (rc) {
+				pr_err("pm8xxx_misc_masked_write failed, "
+					"rc=%d\n", rc);
+				goto read_write_err;
+			}
+			mdelay(PM8901_DELAY_AFTER_REG_DISABLE_MS);
+		}
+	}
+
+read_write_err:
+	mdelay(PM8901_DELAY_BEFORE_SHUTDOWN_MS);
+	return rc;
+}
+
+static int __pm8921_reset_pwr_off(struct pm8xxx_misc_chip *chip, int reset)
+{
+	int rc;
+
+	/* Enable SMPL if resetting is desired. */
+	rc = pm8xxx_misc_masked_write(chip, REG_PM8921_SLEEP_CTRL,
+	       SLEEP_CTRL_SMPL_EN_MASK,
+	       (reset ? SLEEP_CTRL_SMPL_EN_RESET : SLEEP_CTRL_SMPL_EN_PWR_OFF));
+	if (rc) {
+		pr_err("pm8xxx_misc_masked_write failed, rc=%d\n", rc);
+		goto read_write_err;
+	}
+
+	/*
+	 * Select action to perform (reset or shutdown) when PS_HOLD goes low.
+	 * Also ensure that KPD, CBL0, and CBL1 pull ups are enabled and that
+	 * USB charging is enabled.
+	 */
+	rc = pm8xxx_misc_masked_write(chip, REG_PM8XXX_PON_CTRL_1,
+		PON_CTRL_1_PULL_UP_MASK | PON_CTRL_1_USB_PWR_EN
+		| PON_CTRL_1_WD_EN_MASK,
+		PON_CTRL_1_PULL_UP_MASK | PON_CTRL_1_USB_PWR_EN
+		| (reset ? PON_CTRL_1_WD_EN_RESET : PON_CTRL_1_WD_EN_PWR_OFF));
+	if (rc) {
+		pr_err("pm8xxx_misc_masked_write failed, rc=%d\n", rc);
+		goto read_write_err;
+	}
+
+read_write_err:
+	return rc;
+}
+
+/**
+ * pm8xxx_reset_pwr_off - switch all PM8XXX PMIC chips attached to the system to
+ *			  either reset or shutdown when they are turned off
+ * @reset: 0 = shudown the PMICs, 1 = shutdown and then restart the PMICs
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_reset_pwr_off(int reset)
+{
+	struct pm8xxx_misc_chip *chip;
+	unsigned long flags;
+	int rc = 0;
+
+	spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
+
+	/* Loop over all attached PMICs and call specific functions for them. */
+	list_for_each_entry(chip, &pm8xxx_misc_chips, link) {
+		switch (chip->version) {
+		case PM8XXX_VERSION_8018:
+			rc = __pm8018_reset_pwr_off(chip, reset);
+			break;
+		case PM8XXX_VERSION_8058:
+			rc = __pm8058_reset_pwr_off(chip, reset);
+			break;
+		case PM8XXX_VERSION_8901:
+			rc = __pm8901_reset_pwr_off(chip, reset);
+			break;
+		case PM8XXX_VERSION_8038:
+		case PM8XXX_VERSION_8917:
+		case PM8XXX_VERSION_8921:
+			rc = __pm8921_reset_pwr_off(chip, reset);
+			break;
+		default:
+			/* PMIC doesn't have reset_pwr_off; do nothing. */
+			break;
+		}
+		if (rc) {
+			pr_err("reset_pwr_off failed, rc=%d\n", rc);
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_reset_pwr_off);
+
+/**
+ * pm8xxx_smpl_control - enables/disables SMPL detection
+ * @enable: 0 = shutdown PMIC on power loss, 1 = reset PMIC on power loss
+ *
+ * This function enables or disables the Sudden Momentary Power Loss detection
+ * module.  If SMPL detection is enabled, then when a sufficiently long power
+ * loss event occurs, the PMIC will automatically reset itself.  If SMPL
+ * detection is disabled, then the PMIC will shutdown when power loss occurs.
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_smpl_control(int enable)
+{
+	struct pm8xxx_misc_chip *chip;
+	unsigned long flags;
+	int rc = 0;
+
+	spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
+
+	/* Loop over all attached PMICs and call specific functions for them. */
+	list_for_each_entry(chip, &pm8xxx_misc_chips, link) {
+		switch (chip->version) {
+		case PM8XXX_VERSION_8018:
+			rc = pm8xxx_misc_masked_write(chip,
+				REG_PM8018_SLEEP_CTRL, SLEEP_CTRL_SMPL_EN_MASK,
+				(enable ? SLEEP_CTRL_SMPL_EN_RESET
+					   : SLEEP_CTRL_SMPL_EN_PWR_OFF));
+			break;
+		case PM8XXX_VERSION_8058:
+			rc = pm8xxx_misc_masked_write(chip,
+				REG_PM8058_SLEEP_CTRL, SLEEP_CTRL_SMPL_EN_MASK,
+				(enable ? SLEEP_CTRL_SMPL_EN_RESET
+					   : SLEEP_CTRL_SMPL_EN_PWR_OFF));
+			break;
+		case PM8XXX_VERSION_8921:
+			rc = pm8xxx_misc_masked_write(chip,
+				REG_PM8921_SLEEP_CTRL, SLEEP_CTRL_SMPL_EN_MASK,
+				(enable ? SLEEP_CTRL_SMPL_EN_RESET
+					   : SLEEP_CTRL_SMPL_EN_PWR_OFF));
+			break;
+		default:
+			/* PMIC doesn't have reset_pwr_off; do nothing. */
+			break;
+		}
+		if (rc) {
+			pr_err("setting smpl control failed, rc=%d\n", rc);
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_smpl_control);
+
+
+/**
+ * pm8xxx_smpl_set_delay - sets the SMPL detection time delay
+ * @delay: enum value corresponding to delay time
+ *
+ * This function sets the time delay of the SMPL detection module.  If power
+ * is reapplied within this interval, then the PMIC reset automatically.  The
+ * SMPL detection module must be enabled for this delay time to take effect.
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_smpl_set_delay(enum pm8xxx_smpl_delay delay)
+{
+	struct pm8xxx_misc_chip *chip;
+	unsigned long flags;
+	int rc = 0;
+
+	if (delay < SLEEP_CTRL_SMPL_SEL_MIN
+	    || delay > SLEEP_CTRL_SMPL_SEL_MAX) {
+		pr_err("%s: invalid delay specified: %d\n", __func__, delay);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
+
+	/* Loop over all attached PMICs and call specific functions for them. */
+	list_for_each_entry(chip, &pm8xxx_misc_chips, link) {
+		switch (chip->version) {
+		case PM8XXX_VERSION_8018:
+			rc = pm8xxx_misc_masked_write(chip,
+				REG_PM8018_SLEEP_CTRL, SLEEP_CTRL_SMPL_SEL_MASK,
+				delay);
+			break;
+		case PM8XXX_VERSION_8058:
+			rc = pm8xxx_misc_masked_write(chip,
+				REG_PM8058_SLEEP_CTRL, SLEEP_CTRL_SMPL_SEL_MASK,
+				delay);
+			break;
+		case PM8XXX_VERSION_8921:
+			rc = pm8xxx_misc_masked_write(chip,
+				REG_PM8921_SLEEP_CTRL, SLEEP_CTRL_SMPL_SEL_MASK,
+				delay);
+			break;
+		default:
+			/* PMIC doesn't have reset_pwr_off; do nothing. */
+			break;
+		}
+		if (rc) {
+			pr_err("setting smpl delay failed, rc=%d\n", rc);
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_smpl_set_delay);
+
+/**
+ * pm8xxx_coincell_chg_config - Disables or enables the coincell charger, and
+ *				configures its voltage and resistor settings.
+ * @chg_config:			Holds both voltage and resistor values, and a
+ *				switch to change the state of charger.
+ *				If state is to disable the charger then
+ *				both voltage and resistor are disregarded.
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_coincell_chg_config(struct pm8xxx_coincell_chg *chg_config)
+{
+	struct pm8xxx_misc_chip *chip;
+	unsigned long flags;
+	u8 reg = 0, voltage, resistor;
+	int rc = 0;
+
+	if (chg_config == NULL) {
+		pr_err("chg_config is NULL\n");
+		return -EINVAL;
+	}
+
+	voltage = chg_config->voltage;
+	resistor = chg_config->resistor;
+
+	if (resistor < PM8XXX_COINCELL_RESISTOR_2100_OHMS ||
+			resistor > PM8XXX_COINCELL_RESISTOR_800_OHMS) {
+		pr_err("Invalid resistor value provided\n");
+		return -EINVAL;
+	}
+
+	if (voltage < PM8XXX_COINCELL_VOLTAGE_3p2V ||
+		(voltage > PM8XXX_COINCELL_VOLTAGE_3p0V &&
+			voltage != PM8XXX_COINCELL_VOLTAGE_2p5V)) {
+		pr_err("Invalid voltage value provided\n");
+		return -EINVAL;
+	}
+
+	if (chg_config->state == PM8XXX_COINCELL_CHG_DISABLE) {
+		reg = 0;
+	} else {
+		reg |= voltage;
+		reg |= (resistor << COINCELL_RESISTOR_SHIFT);
+	}
+
+	spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
+
+	/* Loop over all attached PMICs and call specific functions for them. */
+	list_for_each_entry(chip, &pm8xxx_misc_chips, link) {
+		switch (chip->version) {
+		case PM8XXX_VERSION_8018:
+			rc = pm8xxx_writeb(chip->dev->parent,
+					REG_PM8018_COIN_CHG, reg);
+			break;
+		case PM8XXX_VERSION_8058:
+			rc = pm8xxx_writeb(chip->dev->parent,
+					REG_PM8058_COIN_CHG, reg);
+			break;
+		case PM8XXX_VERSION_8921:
+			rc = pm8xxx_writeb(chip->dev->parent,
+					REG_PM8921_COIN_CHG, reg);
+			break;
+		default:
+			/* PMIC doesn't have reset_pwr_off; do nothing. */
+			break;
+		}
+		if (rc) {
+			pr_err("coincell chg. config failed, rc=%d\n", rc);
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_coincell_chg_config);
+
+/**
+ * pm8xxx_watchdog_reset_control - enables/disables watchdog reset detection
+ * @enable: 0 = shutdown when PS_HOLD goes low, 1 = reset when PS_HOLD goes low
+ *
+ * This function enables or disables the PMIC watchdog reset detection feature.
+ * If watchdog reset detection is enabled, then the PMIC will reset itself
+ * when PS_HOLD goes low.  If it is not enabled, then the PMIC will shutdown
+ * when PS_HOLD goes low.
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_watchdog_reset_control(int enable)
+{
+	struct pm8xxx_misc_chip *chip;
+	unsigned long flags;
+	int rc = 0;
+
+	spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
+
+	/* Loop over all attached PMICs and call specific functions for them. */
+	list_for_each_entry(chip, &pm8xxx_misc_chips, link) {
+		switch (chip->version) {
+		case PM8XXX_VERSION_8018:
+		case PM8XXX_VERSION_8058:
+		case PM8XXX_VERSION_8921:
+			rc = pm8xxx_misc_masked_write(chip,
+				REG_PM8XXX_PON_CTRL_1, PON_CTRL_1_WD_EN_MASK,
+				(enable ? PON_CTRL_1_WD_EN_RESET
+					   : PON_CTRL_1_WD_EN_PWR_OFF));
+			break;
+		default:
+			/* WD reset control not supported */
+			break;
+		}
+		if (rc) {
+			pr_err("setting WD reset control failed, rc=%d\n", rc);
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_watchdog_reset_control);
+
+/**
+ * pm8xxx_stay_on - enables stay_on feature
+ *
+ * PMIC stay-on feature allows PMIC to ignore MSM PS_HOLD=low
+ * signal so that some special functions like debugging could be
+ * performed.
+ *
+ * This feature should not be used in any product release.
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_stay_on(void)
+{
+	struct pm8xxx_misc_chip *chip;
+	unsigned long flags;
+	int rc = 0;
+
+	spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
+
+	/* Loop over all attached PMICs and call specific functions for them. */
+	list_for_each_entry(chip, &pm8xxx_misc_chips, link) {
+		switch (chip->version) {
+		case PM8XXX_VERSION_8018:
+		case PM8XXX_VERSION_8058:
+		case PM8XXX_VERSION_8921:
+			rc = pm8xxx_writeb(chip->dev->parent,
+				REG_PM8XXX_GP_TEST_1, PM8XXX_STAY_ON_CFG);
+			break;
+		default:
+			/* stay on not supported */
+			break;
+		}
+		if (rc) {
+			pr_err("stay_on failed failed, rc=%d\n", rc);
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_stay_on);
+
+static int
+__pm8xxx_hard_reset_config(struct pm8xxx_misc_chip *chip,
+		enum pm8xxx_pon_config config, u16 pon4_addr, u16 pon5_addr)
+{
+	int rc = 0;
+
+	switch (config) {
+	case PM8XXX_DISABLE_HARD_RESET:
+		rc = pm8xxx_misc_masked_write(chip, pon5_addr,
+				PON_CTRL_5_HARD_RESET_EN_MASK,
+				PON_CTRL_5_HARD_RESET_DIS);
+		break;
+	case PM8XXX_SHUTDOWN_ON_HARD_RESET:
+		rc = pm8xxx_misc_masked_write(chip, pon5_addr,
+				PON_CTRL_5_HARD_RESET_EN_MASK,
+				PON_CTRL_5_HARD_RESET_EN);
+		if (!rc) {
+			rc = pm8xxx_misc_masked_write(chip, pon4_addr,
+					PON_CTRL_4_RESET_EN_MASK,
+					PON_CTRL_4_SHUTDOWN_ON_RESET);
+		}
+		break;
+	case PM8XXX_RESTART_ON_HARD_RESET:
+		rc = pm8xxx_misc_masked_write(chip, pon5_addr,
+				PON_CTRL_5_HARD_RESET_EN_MASK,
+				PON_CTRL_5_HARD_RESET_EN);
+		if (!rc) {
+			rc = pm8xxx_misc_masked_write(chip, pon4_addr,
+					PON_CTRL_4_RESET_EN_MASK,
+					PON_CTRL_4_RESTART_ON_RESET);
+		}
+		break;
+	default:
+		rc = -EINVAL;
+		break;
+	}
+	return rc;
+}
+
+/**
+ * pm8xxx_hard_reset_config - Allows different reset configurations
+ *
+ * config = PM8XXX_DISABLE_HARD_RESET to disable hard reset
+ *	  = PM8XXX_SHUTDOWN_ON_HARD_RESET to turn off the system on hard reset
+ *	  = PM8XXX_RESTART_ON_HARD_RESET to restart the system on hard reset
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_hard_reset_config(enum pm8xxx_pon_config config)
+{
+	struct pm8xxx_misc_chip *chip;
+	unsigned long flags;
+	int rc = 0;
+
+	spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
+
+	/* Loop over all attached PMICs and call specific functions for them. */
+	list_for_each_entry(chip, &pm8xxx_misc_chips, link) {
+		switch (chip->version) {
+		case PM8XXX_VERSION_8018:
+			__pm8xxx_hard_reset_config(chip, config,
+				REG_PM8018_PON_CNTL_4, REG_PM8018_PON_CNTL_5);
+			break;
+		case PM8XXX_VERSION_8058:
+			__pm8xxx_hard_reset_config(chip, config,
+				REG_PM8058_PON_CNTL_4, REG_PM8058_PON_CNTL_5);
+			break;
+		case PM8XXX_VERSION_8901:
+			__pm8xxx_hard_reset_config(chip, config,
+				REG_PM8901_PON_CNTL_4, REG_PM8901_PON_CNTL_5);
+			break;
+		case PM8XXX_VERSION_8921:
+			__pm8xxx_hard_reset_config(chip, config,
+				REG_PM8921_PON_CNTL_4, REG_PM8921_PON_CNTL_5);
+			break;
+		default:
+			/* hard reset config. no supported */
+			break;
+		}
+		if (rc) {
+			pr_err("hard reset config. failed, rc=%d\n", rc);
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_hard_reset_config);
+
+/* Handle the OSC_HALT interrupt: 32 kHz XTAL oscillator has stopped. */
+static irqreturn_t pm8xxx_osc_halt_isr(int irq, void *data)
+{
+	struct pm8xxx_misc_chip *chip = data;
+	u64 count = 0;
+
+	if (chip) {
+		chip->osc_halt_count++;
+		count = chip->osc_halt_count;
+	}
+
+	pr_crit("%s: OSC_HALT interrupt has triggered, 32 kHz XTAL oscillator"
+				" has halted (%llu)!\n", __func__, count);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * pm8xxx_uart_gpio_mux_ctrl - Mux configuration to select the UART
+ *
+ * @uart_path_sel: Input argument to select either UART1/2/3
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_uart_gpio_mux_ctrl(enum pm8xxx_uart_path_sel uart_path_sel)
+{
+	struct pm8xxx_misc_chip *chip;
+	unsigned long flags;
+	int rc = 0;
+
+	spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
+
+	/* Loop over all attached PMICs and call specific functions for them. */
+	list_for_each_entry(chip, &pm8xxx_misc_chips, link) {
+		switch (chip->version) {
+		case PM8XXX_VERSION_8018:
+		case PM8XXX_VERSION_8058:
+		case PM8XXX_VERSION_8921:
+			rc = pm8xxx_misc_masked_write(chip,
+				REG_PM8XXX_GPIO_MUX_CTRL, UART_PATH_SEL_MASK,
+				uart_path_sel << UART_PATH_SEL_SHIFT);
+			break;
+		default:
+			/* Functionality not supported */
+			break;
+		}
+		if (rc) {
+			pr_err("uart_gpio_mux_ctrl failed, rc=%d\n", rc);
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_uart_gpio_mux_ctrl);
+
+/**
+ * pm8xxx_usb_id_pullup - Control a pullup for USB ID
+ *
+ * @enable: enable (1) or disable (0) the pullup
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_usb_id_pullup(int enable)
+{
+	struct pm8xxx_misc_chip *chip;
+	unsigned long flags;
+	int rc = -ENXIO;
+
+	spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
+
+	/* Loop over all attached PMICs and call specific functions for them. */
+	list_for_each_entry(chip, &pm8xxx_misc_chips, link) {
+		switch (chip->version) {
+		case PM8XXX_VERSION_8921:
+		case PM8XXX_VERSION_8922:
+		case PM8XXX_VERSION_8917:
+		case PM8XXX_VERSION_8038:
+			rc = pm8xxx_misc_masked_write(chip,
+				REG_PM8XXX_GPIO_MUX_CTRL, USB_ID_PU_EN_MASK,
+				enable << USB_ID_PU_EN_SHIFT);
+
+			if (rc)
+				pr_err("Fail: reg=%x, rc=%d\n",
+				       REG_PM8XXX_GPIO_MUX_CTRL, rc);
+			break;
+		default:
+			/* Functionality not supported */
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_usb_id_pullup);
+
+static int __pm8901_preload_dVdd(struct pm8xxx_misc_chip *chip)
+{
+	int rc;
+
+	/* dVdd preloading is not needed for PMIC PM8901 rev 2.3 and beyond. */
+	if (pm8xxx_get_revision(chip->dev->parent) >= PM8XXX_REVISION_8901_2p3)
+		return 0;
+
+	rc = pm8xxx_writeb(chip->dev->parent, 0x0BD, 0x0F);
+	if (rc)
+		pr_err("pm8xxx_writeb failed for 0x0BD, rc=%d\n", rc);
+
+	rc = pm8xxx_writeb(chip->dev->parent, 0x001, 0xB4);
+	if (rc)
+		pr_err("pm8xxx_writeb failed for 0x001, rc=%d\n", rc);
+
+	pr_info("dVdd preloaded\n");
+
+	return rc;
+}
+
+/**
+ * pm8xxx_preload_dVdd - preload the dVdd regulator during off state.
+ *
+ * This can help to reduce fluctuations in the dVdd voltage during startup
+ * at the cost of additional off state current draw.
+ *
+ * This API should only be called if dVdd startup issues are suspected.
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_preload_dVdd(void)
+{
+	struct pm8xxx_misc_chip *chip;
+	unsigned long flags;
+	int rc = 0;
+
+	spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
+
+	/* Loop over all attached PMICs and call specific functions for them. */
+	list_for_each_entry(chip, &pm8xxx_misc_chips, link) {
+		switch (chip->version) {
+		case PM8XXX_VERSION_8901:
+			rc = __pm8901_preload_dVdd(chip);
+			break;
+		default:
+			/* PMIC doesn't have preload_dVdd; do nothing. */
+			break;
+		}
+		if (rc) {
+			pr_err("preload_dVdd failed, rc=%d\n", rc);
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_preload_dVdd);
+
+int pm8xxx_aux_clk_control(enum pm8xxx_aux_clk_id clk_id,
+				enum pm8xxx_aux_clk_div divider, bool enable)
+{
+	struct pm8xxx_misc_chip *chip;
+	unsigned long flags;
+	u8 clk_mask = 0, value = 0;
+
+	if (clk_id == CLK_MP3_1) {
+		clk_mask = MP3_1_MASK;
+		value = divider << MP3_1_SHIFT;
+	} else if (clk_id == CLK_MP3_2) {
+		clk_mask = MP3_2_MASK;
+		value = divider << MP3_2_SHIFT;
+	} else {
+		pr_err("Invalid clock id of %d\n", clk_id);
+		return -EINVAL;
+	}
+	if (!enable)
+		value = 0;
+
+	spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
+
+	/* Loop over all attached PMICs and call specific functions for them. */
+	list_for_each_entry(chip, &pm8xxx_misc_chips, link) {
+		switch (chip->version) {
+		case PM8XXX_VERSION_8038:
+		case PM8XXX_VERSION_8921:
+			pm8xxx_misc_masked_write(chip,
+					REG_PM8XXX_XO_CNTRL_2, clk_mask, value);
+			break;
+		default:
+			/* Functionality not supported */
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_aux_clk_control);
+
+int pm8xxx_hsed_bias_control(enum pm8xxx_hsed_bias bias, bool enable)
+{
+	struct pm8xxx_misc_chip *chip;
+	unsigned long flags;
+	int rc = 0;
+	u16 addr;
+
+	switch (bias) {
+	case PM8XXX_HSED_BIAS0:
+		addr = REG_HSED_BIAS0_CNTL2;
+		break;
+	case PM8XXX_HSED_BIAS1:
+		addr = REG_HSED_BIAS1_CNTL2;
+		break;
+	case PM8XXX_HSED_BIAS2:
+		addr = REG_HSED_BIAS2_CNTL2;
+		break;
+	default:
+		pr_err("Invalid BIAS line\n");
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
+
+	/* Loop over all attached PMICs and call specific functions for them. */
+	list_for_each_entry(chip, &pm8xxx_misc_chips, link) {
+		switch (chip->version) {
+		case PM8XXX_VERSION_8058:
+		case PM8XXX_VERSION_8921:
+			rc = pm8xxx_misc_masked_write(chip, addr,
+				HSED_EN_MASK, enable ? HSED_EN_MASK : 0);
+			if (rc < 0)
+				pr_err("Enable HSED BIAS failed rc=%d\n", rc);
+			break;
+		default:
+			/* Functionality not supported */
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_hsed_bias_control);
+
+static int __devinit pm8xxx_misc_probe(struct platform_device *pdev)
+{
+	const struct pm8xxx_misc_platform_data *pdata = pdev->dev.platform_data;
+	struct pm8xxx_misc_chip *chip;
+	struct pm8xxx_misc_chip *sibling;
+	struct list_head *prev;
+	unsigned long flags;
+	int rc = 0, irq;
+
+	if (!pdata) {
+		pr_err("missing platform data\n");
+		return -EINVAL;
+	}
+
+	chip = kzalloc(sizeof(struct pm8xxx_misc_chip), GFP_KERNEL);
+	if (!chip) {
+		pr_err("Cannot allocate %d bytes\n",
+			sizeof(struct pm8xxx_misc_chip));
+		return -ENOMEM;
+	}
+
+	chip->dev = &pdev->dev;
+	chip->version = pm8xxx_get_version(chip->dev->parent);
+	memcpy(&(chip->pdata), pdata, sizeof(struct pm8xxx_misc_platform_data));
+
+	irq = platform_get_irq_byname(pdev, "pm8xxx_osc_halt_irq");
+	if (irq > 0) {
+		rc = request_any_context_irq(irq, pm8xxx_osc_halt_isr,
+				 IRQF_TRIGGER_RISING | IRQF_DISABLED,
+				 "pm8xxx_osc_halt_irq", chip);
+		if (rc < 0) {
+			pr_err("%s: request_any_context_irq(%d) FAIL: %d\n",
+							 __func__, irq, rc);
+			goto fail_irq;
+		}
+	}
+
+	/* Insert PMICs in priority order (lowest value first). */
+	spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
+	prev = &pm8xxx_misc_chips;
+	list_for_each_entry(sibling, &pm8xxx_misc_chips, link) {
+		if (chip->pdata.priority < sibling->pdata.priority)
+			break;
+		else
+			prev = &sibling->link;
+	}
+	list_add(&chip->link, prev);
+	spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+	platform_set_drvdata(pdev, chip);
+
+	return rc;
+
+fail_irq:
+	platform_set_drvdata(pdev, NULL);
+	kfree(chip);
+	return rc;
+}
+
+static int __devexit pm8xxx_misc_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_misc_chip *chip = platform_get_drvdata(pdev);
+	unsigned long flags;
+	int irq = platform_get_irq_byname(pdev, "pm8xxx_osc_halt_irq");
+	if (irq > 0)
+		free_irq(irq, chip);
+
+	spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
+	list_del(&chip->link);
+	spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+	platform_set_drvdata(pdev, NULL);
+	kfree(chip);
+
+	return 0;
+}
+
+static struct platform_driver pm8xxx_misc_driver = {
+	.probe	= pm8xxx_misc_probe,
+	.remove	= __devexit_p(pm8xxx_misc_remove),
+	.driver	= {
+		.name	= PM8XXX_MISC_DEV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init pm8xxx_misc_init(void)
+{
+	return platform_driver_register(&pm8xxx_misc_driver);
+}
+postcore_initcall(pm8xxx_misc_init);
+
+static void __exit pm8xxx_misc_exit(void)
+{
+	platform_driver_unregister(&pm8xxx_misc_driver);
+}
+module_exit(pm8xxx_misc_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC 8XXX misc driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8XXX_MISC_DEV_NAME);
diff --git a/drivers/mfd/pm8xxx-pwm.c b/drivers/mfd/pm8xxx-pwm.c
new file mode 100644
index 0000000..022cfb6
--- /dev/null
+++ b/drivers/mfd/pm8xxx-pwm.c
@@ -0,0 +1,1471 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+/*
+ * Qualcomm PM8XXX Pulse Width Modulation (PWM) driver
+ *
+ * The HW module is also called LPG (Light Pulse Generator).
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/debugfs.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/pwm.h>
+
+#define PM8XXX_PWM_CHANNELS		3
+
+/*
+ * For the lack of better term to distinguish functional
+ * differences, hereby, LPG version 0 (V0, v0) denotes
+ * PM8058/8921, and version 1 (V1, v1) denotes
+ * PM8922/8038.
+ */
+#define PM8XXX_LPG_V0_PWM_CHANNELS	8
+#define PM8XXX_LPG_V1_PWM_CHANNELS	6
+#define PM8XXX_LPG_CTL_REGS		7
+
+/* PM8XXX PWM */
+#define SSBI_REG_ADDR_PWM1_CTRL1	0x88
+#define SSBI_REG_ADDR_PWM1_CTRL2	0x89
+#define SSBI_REG_ADDR_PWM_CTL(id, base) (id == 0 ? base : (base + (id << 1)))
+#define SSBI_REG_ADDR_PWM_CTL1(id)	SSBI_REG_ADDR_PWM_CTL(id, \
+						SSBI_REG_ADDR_PWM1_CTRL1)
+#define SSBI_REG_ADDR_PWM_CTL2(id)	SSBI_REG_ADDR_PWM_CTL(id, \
+						SSBI_REG_ADDR_PWM1_CTRL2)
+
+#define PM8XXX_PWM_CLK_SEL_SHIFT	6
+#define PM8XXX_PWM_CLK_SEL_MASK		0xC0
+#define PM8XXX_PWM_PREDIVIDE_SHIFT	5
+#define PM8XXX_PWM_PREDIVIDE_MASK	0x20
+#define PM8XXX_PWM_M_SHIFT		2
+#define PM8XXX_PWM_M_MASK		0x1C
+#define PM8XXX_PWM_SIZE_SHIFT		1
+#define PM8XXX_PWM_SIZE_MASK		0x02
+#define PM8XXX_PWM_VALUE_BIT0		0x01
+#define PM8XXX_PWM_DISABLE		0x3F
+
+/* PM8XXX LPG PWM */
+#define SSBI_REG_ADDR_LPG_CTL_BASE	0x13C
+#define SSBI_REG_ADDR_LPG_CTL(n)	(SSBI_REG_ADDR_LPG_CTL_BASE + (n))
+#define SSBI_REG_ADDR_LPG_BANK_SEL	0x143
+#define SSBI_REG_ADDR_LPG_BANK_EN	0x144
+#define SSBI_REG_ADDR_LPG_LUT_CFG0	0x145
+#define SSBI_REG_ADDR_LPG_LUT_CFG1	0x146
+#define SSBI_REG_ADDR_LPG_TEST		0x147
+
+/* LPG Control 0 */
+#define PM8XXX_PWM_1KHZ_COUNT_MASK	0xF0
+#define PM8XXX_PWM_1KHZ_COUNT_SHIFT	4
+
+#define PM8XXX_PWM_1KHZ_COUNT_MAX	15
+
+#define PM8XXX_PWM_OUTPUT_EN		0x08
+#define PM8XXX_PWM_PWM_EN		0x04
+#define PM8XXX_PWM_RAMP_GEN_EN		0x02
+#define PM8XXX_PWM_RAMP_START		0x01
+
+#define PM8XXX_PWM_PWM_START		(PM8XXX_PWM_OUTPUT_EN \
+					| PM8XXX_PWM_PWM_EN)
+#define PM8XXX_PWM_RAMP_GEN_START	(PM8XXX_PWM_RAMP_GEN_EN \
+					| PM8XXX_PWM_RAMP_START)
+
+/* LPG Control 1 */
+#define PM8XXX_PWM_REVERSE_EN		0x80
+#define PM8XXX_PWM_BYPASS_LUT		0x40
+#define PM8XXX_PWM_HIGH_INDEX_MASK	0x3F
+
+/* LPG Control 2 */
+#define PM8XXX_PWM_LOOP_EN		0x80
+#define PM8XXX_PWM_RAMP_UP		0x40
+#define PM8XXX_PWM_LOW_INDEX_MASK	0x3F
+
+/* LPG Control 3 */
+#define PM8XXX_PWM_VALUE_BIT7_0		0xFF
+#define PM8XXX_PWM_VALUE_BIT5_0		0x3F
+
+/* LPG Control 4 */
+#define PM8XXX_PWM_VALUE_BIT8		0x80
+
+#define PM8XXX_LPG_PWM_CLK_SEL_MASK	0x60
+#define PM8XXX_LPG_PWM_CLK_SEL_SHIFT	5
+
+#define PM8XXX_PWM_CLK_SEL_NO		0
+#define PM8XXX_PWM_CLK_SEL_1KHZ		1
+#define PM8XXX_PWM_CLK_SEL_32KHZ	2
+#define PM8XXX_PWM_CLK_SEL_19P2MHZ	3
+
+#define PM8XXX_LPG_PWM_PREDIVIDE_MASK	0x18
+#define PM8XXX_LPG_PWM_PREDIVIDE_SHIFT	3
+
+#define PM8XXX_PWM_PREDIVIDE_2		0
+#define PM8XXX_PWM_PREDIVIDE_3		1
+#define PM8XXX_PWM_PREDIVIDE_5		2
+#define PM8XXX_PWM_PREDIVIDE_6		3
+
+#define PM8XXX_LPG_PWM_M_MASK		0x07
+#define PM8XXX_PWM_M_MIN		0
+#define PM8XXX_PWM_M_MAX		7
+
+/* LPG Control 5 */
+#define PM8XXX_PWM_PAUSE_COUNT_HI_MASK		0xFC
+#define PM8XXX_PWM_PAUSE_COUNT_HI_SHIFT		2
+
+#define PM8XXX_PWM_PAUSE_ENABLE_HIGH		0x02
+#define PM8XXX_PWM_SIZE_9_BIT			0x01
+
+/* LPG Control 6 */
+#define PM8XXX_PWM_PAUSE_COUNT_LO_MASK		0xFC
+#define PM8XXX_PWM_PAUSE_COUNT_LO_SHIFT		2
+
+#define PM8XXX_PWM_PAUSE_ENABLE_LOW		0x02
+#define PM8XXX_PWM_RESERVED			0x01
+
+#define PM8XXX_PWM_PAUSE_COUNT_MAX		56 /* < 2^6 = 64 */
+
+/* LPG LUT_CFG1 */
+#define PM8XXX_PWM_LUT_READ			0x40
+
+/* TEST */
+#define PM8XXX_PWM_DTEST_MASK		0x38
+#define PM8XXX_PWM_DTEST_SHIFT		3
+#define PM8XXX_PWM_DTEST_BANK_MASK	0x07
+
+/*
+ * PWM Frequency = Clock Frequency / (N * T)
+ *	or
+ * PWM Period = Clock Period * (N * T)
+ *	where
+ * N = 2^9 or 2^6 for 9-bit or 6-bit PWM size
+ * T = Pre-divide * 2^m, where m = 0..7 (exponent)
+ *
+ * This is the formula to figure out m for the best pre-divide and clock:
+ * (PWM Period / N) = (Pre-divide * Clock Period) * 2^m
+ */
+#define NUM_CLOCKS	3
+
+#define NSEC_1024HZ	(NSEC_PER_SEC / 1024)
+#define NSEC_32768HZ	(NSEC_PER_SEC / 32768)
+#define NSEC_19P2MHZ	(NSEC_PER_SEC / 19200000)
+
+#define NUM_LPG_PRE_DIVIDE	4
+#define NUM_PWM_PRE_DIVIDE	2
+
+#define PRE_DIVIDE_1		1	/* v1 */
+#define PRE_DIVIDE_2		2
+#define PRE_DIVIDE_3		3
+#define PRE_DIVIDE_5		5
+#define PRE_DIVIDE_6		6
+
+static unsigned int pt_t[NUM_LPG_PRE_DIVIDE][NUM_CLOCKS] = {
+	{	PRE_DIVIDE_2 * NSEC_1024HZ,
+		PRE_DIVIDE_2 * NSEC_32768HZ,
+		PRE_DIVIDE_2 * NSEC_19P2MHZ,
+	},
+	{	PRE_DIVIDE_3 * NSEC_1024HZ,
+		PRE_DIVIDE_3 * NSEC_32768HZ,
+		PRE_DIVIDE_3 * NSEC_19P2MHZ,
+	},
+	{	PRE_DIVIDE_5 * NSEC_1024HZ,
+		PRE_DIVIDE_5 * NSEC_32768HZ,
+		PRE_DIVIDE_5 * NSEC_19P2MHZ,
+	},
+	{	PRE_DIVIDE_6 * NSEC_1024HZ,
+		PRE_DIVIDE_6 * NSEC_32768HZ,
+		PRE_DIVIDE_6 * NSEC_19P2MHZ,
+	},
+};
+
+/* Private data */
+struct pm8xxx_pwm_chip;
+
+struct pwm_device {
+	int			pwm_id;		/* = bank/channel id */
+	int			in_use;
+	const char		*label;
+	struct pm8xxx_pwm_period	period;
+	int			pwm_value;
+	int			pwm_period;
+	int			pwm_duty;
+	u8			pwm_lpg_ctl[PM8XXX_LPG_CTL_REGS];
+	u8			pwm_ctl1;
+	u8			pwm_ctl2;
+	int			irq;
+	struct pm8xxx_pwm_chip	*chip;
+	int			bypass_lut;
+	int			dtest_mode_supported;
+};
+
+struct pm8xxx_pwm_chip {
+	struct pwm_device		*pwm_dev;
+	u8				pwm_channels;
+	u8				pwm_total_pre_divs;
+	u8				bank_mask;
+	struct mutex			pwm_mutex;
+	struct device			*dev;
+	bool				is_lpg_supported;
+};
+
+static struct pm8xxx_pwm_chip	*pwm_chip;
+
+struct pm8xxx_pwm_lut {
+	/* LUT parameters */
+	int	lut_duty_ms;
+	int	lut_lo_index;
+	int	lut_hi_index;
+	int	lut_pause_hi;
+	int	lut_pause_lo;
+	int	flags;
+};
+
+static const u16 duty_msec[PM8XXX_PWM_1KHZ_COUNT_MAX + 1] = {
+	0, 1, 2, 3, 4, 6, 8, 16, 18, 24, 32, 36, 64, 128, 256, 512
+};
+
+static const u16 pause_count[PM8XXX_PWM_PAUSE_COUNT_MAX + 1] = {
+	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+	23, 28, 31, 42, 47, 56, 63, 83, 94, 111, 125, 167, 188, 222, 250, 333,
+	375, 500, 667, 750, 800, 900, 1000, 1100,
+	1200, 1300, 1400, 1500, 1600, 1800, 2000, 2500,
+	3000, 3500, 4000, 4500, 5000, 5500, 6000, 6500,
+	7000
+};
+
+/* Internal functions */
+static void pm8xxx_pwm_save(u8 *u8p, u8 mask, u8 val)
+{
+	*u8p &= ~mask;
+	*u8p |= val & mask;
+}
+
+static int pm8xxx_pwm_bank_enable(struct pwm_device *pwm, int enable)
+{
+	int	rc;
+	u8	reg;
+	struct pm8xxx_pwm_chip	*chip;
+
+	chip = pwm->chip;
+
+	if (enable)
+		reg = chip->bank_mask | (1 << pwm->pwm_id);
+	else
+		reg = chip->bank_mask & ~(1 << pwm->pwm_id);
+
+	rc = pm8xxx_writeb(chip->dev->parent, SSBI_REG_ADDR_LPG_BANK_EN, reg);
+	if (rc) {
+		pr_err("pm8xxx_writeb(): rc=%d (Enable LPG Bank)\n", rc);
+		return rc;
+	}
+	chip->bank_mask = reg;
+
+	return 0;
+}
+
+static int pm8xxx_pwm_bank_sel(struct pwm_device *pwm)
+{
+	int	rc;
+
+	rc = pm8xxx_writeb(pwm->chip->dev->parent, SSBI_REG_ADDR_LPG_BANK_SEL,
+			   pwm->pwm_id);
+	if (rc)
+		pr_err("pm8xxx_writeb(): rc=%d (Select PWM Bank)\n", rc);
+	return rc;
+}
+
+static int pm8xxx_pwm_start(struct pwm_device *pwm, int start, int ramp_start)
+{
+	int	rc;
+	u8	reg;
+
+	if (start) {
+		reg = pwm->pwm_lpg_ctl[0] | PM8XXX_PWM_PWM_START;
+		if (ramp_start)
+			reg |= PM8XXX_PWM_RAMP_GEN_START;
+		else
+			reg &= ~PM8XXX_PWM_RAMP_GEN_START;
+	} else {
+		reg = pwm->pwm_lpg_ctl[0] & ~PM8XXX_PWM_PWM_START;
+		reg &= ~PM8XXX_PWM_RAMP_GEN_START;
+	}
+
+	rc = pm8xxx_writeb(pwm->chip->dev->parent, SSBI_REG_ADDR_LPG_CTL(0),
+			   reg);
+	if (rc)
+		pr_err("pm8xxx_writeb(): rc=%d (Enable PWM Ctl 0)\n", rc);
+	else
+		pwm->pwm_lpg_ctl[0] = reg;
+	return rc;
+}
+
+static int pm8xxx_pwm_disable(struct pwm_device *pwm)
+{
+	int	rc;
+	u8	reg;
+
+	reg = pwm->pwm_ctl1 & PM8XXX_PWM_DISABLE;
+
+	rc = pm8xxx_writeb(pwm->chip->dev->parent,
+			SSBI_REG_ADDR_PWM_CTL1(pwm->pwm_id), reg);
+
+	if (rc)
+		pr_err("pm8xxx_writeb(): rc=%d (Disable PWM Ctl %d)\n", rc,
+								pwm->pwm_id);
+	return rc;
+}
+
+static int pm8xxx_pwm_enable(struct pwm_device *pwm)
+{
+	/**
+	 * A kind of best Effort: Just write the clock information that
+	 * we have in the register.
+	 */
+	int	rc;
+
+	rc = pm8xxx_writeb(pwm->chip->dev->parent,
+			SSBI_REG_ADDR_PWM_CTL1(pwm->pwm_id), pwm->pwm_ctl1);
+
+	if (rc)
+		pr_err("pm8xxx_writeb(): rc=%d (Enable PWM Ctl %d)\n", rc,
+								pwm->pwm_id);
+	return rc;
+}
+
+static void pm8xxx_pwm_calc_period(unsigned int period_us,
+				   struct pm8xxx_pwm_period *period)
+{
+	int	n, m, clk, div;
+	int	best_m, best_div, best_clk;
+	unsigned int	last_err, cur_err, min_err;
+	unsigned int	tmp_p, period_n;
+
+	/* PWM Period / N */
+	if (period_us < ((unsigned)(-1) / NSEC_PER_USEC)) {
+		period_n = (period_us * NSEC_PER_USEC) >> 6;
+		n = 6;
+	} else {
+		period_n = (period_us >> 9) * NSEC_PER_USEC;
+		n = 9;
+	}
+
+	min_err = last_err = (unsigned)(-1);
+	best_m = 0;
+	best_clk = 0;
+	best_div = 0;
+	for (clk = 0; clk < NUM_CLOCKS; clk++) {
+		for (div = 0; div < pwm_chip->pwm_total_pre_divs; div++) {
+			/* period_n = (PWM Period / N) */
+			/* tmp_p = (Pre-divide * Clock Period) * 2^m */
+			tmp_p = pt_t[div][clk];
+			for (m = 0; m <= PM8XXX_PWM_M_MAX; m++) {
+				if (period_n > tmp_p)
+					cur_err = period_n - tmp_p;
+				else
+					cur_err = tmp_p - period_n;
+
+				if (cur_err < min_err) {
+					min_err = cur_err;
+					best_m = m;
+					best_clk = clk;
+					best_div = div;
+				}
+
+				if (m && cur_err > last_err)
+					/* Break for bigger cur_err */
+					break;
+
+				last_err = cur_err;
+				tmp_p <<= 1;
+			}
+		}
+	}
+
+	/* Use higher resolution */
+	if (best_m >= 3 && n == 6) {
+		n += 3;
+		best_m -= 3;
+	}
+
+	period->pwm_size = n;
+	period->clk = best_clk;
+	period->pre_div = best_div;
+	period->pre_div_exp = best_m;
+}
+
+static void pm8xxx_pwm_calc_pwm_value(struct pwm_device *pwm,
+				      unsigned int period_us,
+				      unsigned int duty_us)
+{
+	unsigned int max_pwm_value, tmp;
+
+	/* Figure out pwm_value with overflow handling */
+	tmp = 1 << (sizeof(tmp) * 8 - pwm->period.pwm_size);
+	if (duty_us < tmp) {
+		tmp = duty_us << pwm->period.pwm_size;
+		pwm->pwm_value = tmp / period_us;
+	} else {
+		tmp = period_us >> pwm->period.pwm_size;
+		pwm->pwm_value = duty_us / tmp;
+	}
+	max_pwm_value = (1 << pwm->period.pwm_size) - 1;
+	if (pwm->pwm_value > max_pwm_value)
+		pwm->pwm_value = max_pwm_value;
+}
+
+static int pm8xxx_pwm_change_table(struct pwm_device *pwm, int duty_pct[],
+				   int start_idx, int len, int raw_value)
+{
+	unsigned int pwm_value, max_pwm_value;
+	u8	cfg0, cfg1;
+	int	i, pwm_size;
+	int	rc = 0;
+
+	pwm_size = (pwm->pwm_lpg_ctl[5] & PM8XXX_PWM_SIZE_9_BIT) ? 9 : 6;
+	max_pwm_value = (1 << pwm_size) - 1;
+	for (i = 0; i < len; i++) {
+		if (raw_value)
+			pwm_value = duty_pct[i];
+		else
+			pwm_value = (duty_pct[i] << pwm_size) / 100;
+
+		if (pwm_value > max_pwm_value)
+			pwm_value = max_pwm_value;
+		cfg0 = pwm_value;
+		cfg1 = (pwm_value >> 1) & 0x80;
+		cfg1 |= start_idx + i;
+
+		rc = pm8xxx_writeb(pwm->chip->dev->parent,
+				   SSBI_REG_ADDR_LPG_LUT_CFG0, cfg0);
+		if (rc)
+			break;
+
+		rc = pm8xxx_writeb(pwm->chip->dev->parent,
+				   SSBI_REG_ADDR_LPG_LUT_CFG1, cfg1);
+		if (rc)
+			break;
+	}
+	return rc;
+}
+
+static void pm8xxx_pwm_save_index(struct pwm_device *pwm,
+				   int low_idx, int high_idx, int flags)
+{
+	pwm->pwm_lpg_ctl[1] = high_idx & PM8XXX_PWM_HIGH_INDEX_MASK;
+	pwm->pwm_lpg_ctl[2] = low_idx & PM8XXX_PWM_LOW_INDEX_MASK;
+
+	if (flags & PM_PWM_LUT_REVERSE)
+		pwm->pwm_lpg_ctl[1] |= PM8XXX_PWM_REVERSE_EN;
+	if (flags & PM_PWM_LUT_RAMP_UP)
+		pwm->pwm_lpg_ctl[2] |= PM8XXX_PWM_RAMP_UP;
+	if (flags & PM_PWM_LUT_LOOP)
+		pwm->pwm_lpg_ctl[2] |= PM8XXX_PWM_LOOP_EN;
+}
+
+static void pm8xxx_pwm_save_period(struct pwm_device *pwm)
+{
+	u8	mask, val;
+
+	if (pwm_chip->is_lpg_supported) {
+		val = ((pwm->period.clk + 1) << PM8XXX_LPG_PWM_CLK_SEL_SHIFT)
+			& PM8XXX_LPG_PWM_CLK_SEL_MASK;
+		val |= (pwm->period.pre_div << PM8XXX_LPG_PWM_PREDIVIDE_SHIFT)
+			& PM8XXX_LPG_PWM_PREDIVIDE_MASK;
+		val |= pwm->period.pre_div_exp & PM8XXX_LPG_PWM_M_MASK;
+		mask = PM8XXX_LPG_PWM_CLK_SEL_MASK |
+			PM8XXX_LPG_PWM_PREDIVIDE_MASK | PM8XXX_LPG_PWM_M_MASK;
+		pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[4], mask, val);
+
+		val = (pwm->period.pwm_size > 6) ? PM8XXX_PWM_SIZE_9_BIT : 0;
+		mask = PM8XXX_PWM_SIZE_9_BIT;
+		pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[5], mask, val);
+	} else {
+		val = ((pwm->period.clk + 1) << PM8XXX_PWM_CLK_SEL_SHIFT)
+			& PM8XXX_PWM_CLK_SEL_MASK;
+		val |= (pwm->period.pre_div << PM8XXX_PWM_PREDIVIDE_SHIFT)
+			& PM8XXX_PWM_PREDIVIDE_MASK;
+		val |= (pwm->period.pre_div_exp << PM8XXX_PWM_M_SHIFT)
+				& PM8XXX_PWM_M_MASK;
+		val |= (((pwm->period.pwm_size > 6) ? PM8XXX_PWM_SIZE_9_BIT : 0)
+			<< PM8XXX_PWM_SIZE_SHIFT) & PM8XXX_PWM_SIZE_MASK;
+
+		mask = PM8XXX_PWM_CLK_SEL_MASK | PM8XXX_PWM_PREDIVIDE_MASK |
+			PM8XXX_PWM_M_MASK | PM8XXX_PWM_SIZE_MASK;
+		pm8xxx_pwm_save(&pwm->pwm_ctl1, mask, val);
+	}
+}
+
+static void pm8xxx_pwm_save_pwm_value(struct pwm_device *pwm)
+{
+	u8	mask, val;
+
+	if (pwm_chip->is_lpg_supported) {
+		val = (pwm->period.pwm_size > 6) ? (pwm->pwm_value >> 1) : 0;
+		pwm->pwm_lpg_ctl[3] = pwm->pwm_value;
+		mask = PM8XXX_PWM_VALUE_BIT8;
+		pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[4], mask, val);
+	} else {
+		val = (pwm->period.pwm_size > 6) ? (pwm->pwm_value >> 8) : 0;
+		pwm->pwm_ctl2 = pwm->pwm_value;
+		mask = PM8XXX_PWM_VALUE_BIT0;
+		pm8xxx_pwm_save(&pwm->pwm_ctl1, mask, val);
+	}
+}
+
+static void pm8xxx_pwm_save_duty_time(struct pwm_device *pwm,
+				      struct pm8xxx_pwm_lut *lut)
+{
+	int	i;
+	u8	mask, val;
+
+	/* Linear search for duty time */
+	for (i = 0; i < PM8XXX_PWM_1KHZ_COUNT_MAX; i++) {
+		if (duty_msec[i] >= lut->lut_duty_ms)
+			break;
+	}
+	val = i << PM8XXX_PWM_1KHZ_COUNT_SHIFT;
+
+	mask = PM8XXX_PWM_1KHZ_COUNT_MASK;
+	pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[0], mask, val);
+}
+
+static void pm8xxx_pwm_save_pause(struct pwm_device *pwm,
+				  struct pm8xxx_pwm_lut *lut)
+{
+	int	i, pause_cnt, time_cnt;
+	u8	mask, val;
+
+	time_cnt = (pwm->pwm_lpg_ctl[0] & PM8XXX_PWM_1KHZ_COUNT_MASK)
+				>> PM8XXX_PWM_1KHZ_COUNT_SHIFT;
+	if (lut->flags & PM_PWM_LUT_PAUSE_HI_EN) {
+		pause_cnt = (lut->lut_pause_hi + duty_msec[time_cnt] / 2)
+				/ duty_msec[time_cnt];
+		/* Linear search for pause time */
+		for (i = 0; i < PM8XXX_PWM_PAUSE_COUNT_MAX; i++) {
+			if (pause_count[i] >= pause_cnt)
+				break;
+		}
+		val = (i << PM8XXX_PWM_PAUSE_COUNT_HI_SHIFT) &
+			PM8XXX_PWM_PAUSE_COUNT_HI_MASK;
+		val |= PM8XXX_PWM_PAUSE_ENABLE_HIGH;
+	} else {
+		val = 0;
+	}
+
+	mask = PM8XXX_PWM_PAUSE_COUNT_HI_MASK | PM8XXX_PWM_PAUSE_ENABLE_HIGH;
+	pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[5], mask, val);
+
+	if (lut->flags & PM_PWM_LUT_PAUSE_LO_EN) {
+		/* Linear search for pause time */
+		pause_cnt = (lut->lut_pause_lo + duty_msec[time_cnt] / 2)
+				/ duty_msec[time_cnt];
+		for (i = 0; i < PM8XXX_PWM_PAUSE_COUNT_MAX; i++) {
+			if (pause_count[i] >= pause_cnt)
+				break;
+		}
+		val = (i << PM8XXX_PWM_PAUSE_COUNT_LO_SHIFT) &
+			PM8XXX_PWM_PAUSE_COUNT_LO_MASK;
+		val |= PM8XXX_PWM_PAUSE_ENABLE_LOW;
+	} else {
+		val = 0;
+	}
+
+	mask = PM8XXX_PWM_PAUSE_COUNT_LO_MASK | PM8XXX_PWM_PAUSE_ENABLE_LOW;
+	pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[6], mask, val);
+}
+
+static int pm8xxx_pwm_write(struct pwm_device *pwm)
+{
+	int rc = 0;
+
+	rc = pm8xxx_writeb(pwm->chip->dev->parent,
+			   SSBI_REG_ADDR_PWM_CTL1(pwm->pwm_id),
+			   pwm->pwm_ctl1);
+	if (rc) {
+		pr_err("pm8xxx_writeb() failed: rc=%d (PWM Ctl1[%d])\n",
+							rc, pwm->pwm_id);
+		return rc;
+	}
+
+	rc = pm8xxx_writeb(pwm->chip->dev->parent,
+			   SSBI_REG_ADDR_PWM_CTL2(pwm->pwm_id),
+			   pwm->pwm_ctl2);
+	if (rc) {
+		pr_err("pm8xxx_writeb() failed: rc=%d (PWM Ctl2[%d])\n",
+							rc, pwm->pwm_id);
+		return rc;
+	}
+
+	return rc;
+}
+
+static int pm8xxx_lpg_pwm_write(struct pwm_device *pwm, int start, int end)
+{
+	int	i, rc;
+
+	/* Write in reverse way so 0 would be the last */
+	for (i = end - 1; i >= start; i--) {
+		rc = pm8xxx_writeb(pwm->chip->dev->parent,
+				   SSBI_REG_ADDR_LPG_CTL(i),
+				   pwm->pwm_lpg_ctl[i]);
+		if (rc) {
+			pr_err("pm8xxx_writeb(): rc=%d (PWM Ctl[%d])\n", rc, i);
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+static int pm8xxx_pwm_change_lut(struct pwm_device *pwm,
+				 struct pm8xxx_pwm_lut *lut)
+{
+	int	rc;
+
+	pm8xxx_pwm_save_index(pwm, lut->lut_lo_index,
+			     lut->lut_hi_index, lut->flags);
+	pm8xxx_pwm_save_duty_time(pwm, lut);
+	pm8xxx_pwm_save_pause(pwm, lut);
+	pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[1], PM8XXX_PWM_BYPASS_LUT, 0);
+
+	pm8xxx_pwm_bank_sel(pwm);
+	rc = pm8xxx_lpg_pwm_write(pwm, 0, 7);
+
+	return rc;
+}
+
+static int pm8xxx_pwm_set_dtest(struct pwm_device *pwm, int enable)
+{
+	int	rc;
+	u8	reg;
+
+	reg = pwm->pwm_id & PM8XXX_PWM_DTEST_BANK_MASK;
+
+	if (enable) {
+		/* Observe LPG_OUT on DTEST1*/
+		reg |= (1 << PM8XXX_PWM_DTEST_SHIFT) &
+				PM8XXX_PWM_DTEST_MASK;
+	}
+
+	rc = pm8xxx_writeb(pwm->chip->dev->parent,
+			SSBI_REG_ADDR_LPG_TEST, reg);
+	if (rc)
+		pr_err("pm8xxx_write(DTEST=0x%x) failed: rc=%d\n",
+							reg, rc);
+
+	return rc;
+}
+
+/* APIs */
+/**
+ * pwm_request - request a PWM device
+ * @pwm_id: PWM id or channel
+ * @label: the label to identify the user
+ */
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+	struct pwm_device	*pwm;
+
+	if (pwm_chip == NULL) {
+		pr_err("No pwm_chip\n");
+		return ERR_PTR(-ENODEV);
+	}
+
+	if (pwm_id >= pwm_chip->pwm_channels || pwm_id < 0) {
+		pr_err("Invalid pwm_id: %d with %s\n",
+		       pwm_id, label ? label : ".");
+		return ERR_PTR(-EINVAL);
+	}
+
+	mutex_lock(&pwm_chip->pwm_mutex);
+	pwm = &pwm_chip->pwm_dev[pwm_id];
+	if (!pwm->in_use) {
+		pwm->in_use = 1;
+		pwm->label = label;
+	} else {
+		pwm = ERR_PTR(-EBUSY);
+	}
+	mutex_unlock(&pwm_chip->pwm_mutex);
+
+	return pwm;
+}
+EXPORT_SYMBOL_GPL(pwm_request);
+
+/**
+ * pwm_free - free a PWM device
+ * @pwm: the PWM device
+ */
+void pwm_free(struct pwm_device *pwm)
+{
+	if (pwm == NULL || IS_ERR(pwm) || pwm->chip == NULL) {
+		pr_err("Invalid pwm handle\n");
+		return;
+	}
+
+	mutex_lock(&pwm->chip->pwm_mutex);
+	if (pwm->in_use) {
+		if (pwm_chip->is_lpg_supported) {
+			pm8xxx_pwm_bank_sel(pwm);
+			pm8xxx_pwm_start(pwm, 0, 0);
+		} else {
+			pm8xxx_pwm_disable(pwm);
+		}
+		pwm->in_use = 0;
+		pwm->label = NULL;
+	}
+	if (pwm_chip->is_lpg_supported)
+		pm8xxx_pwm_bank_enable(pwm, 0);
+	mutex_unlock(&pwm->chip->pwm_mutex);
+}
+EXPORT_SYMBOL_GPL(pwm_free);
+
+/**
+ * pwm_config - change a PWM device configuration
+ * @pwm: the PWM device
+ * @period_us: period in microseconds
+ * @duty_us: duty cycle in microseconds
+ */
+int pwm_config(struct pwm_device *pwm, int duty_us, int period_us)
+{
+	struct pm8xxx_pwm_period *period;
+	int	rc = 0;
+
+	if (pwm == NULL || IS_ERR(pwm) ||
+		duty_us > period_us ||
+		(unsigned)period_us > PM8XXX_PWM_PERIOD_MAX ||
+		(unsigned)period_us < PM8XXX_PWM_PERIOD_MIN) {
+		pr_err("Invalid pwm handle or parameters\n");
+		return -EINVAL;
+	}
+	if (pwm->chip == NULL) {
+		pr_err("No pwm_chip\n");
+		return -ENODEV;
+	}
+
+	period = &pwm->period;
+
+	mutex_lock(&pwm->chip->pwm_mutex);
+
+	if (!pwm->in_use) {
+		rc = -EINVAL;
+		goto out_unlock;
+	}
+
+	if (pwm->pwm_period != period_us) {
+		pm8xxx_pwm_calc_period(period_us, period);
+		pm8xxx_pwm_save_period(pwm);
+		pwm->pwm_period = period_us;
+	}
+
+	pm8xxx_pwm_calc_pwm_value(pwm, period_us, duty_us);
+	pm8xxx_pwm_save_pwm_value(pwm);
+
+	if (pwm_chip->is_lpg_supported) {
+		pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[1],
+				PM8XXX_PWM_BYPASS_LUT, PM8XXX_PWM_BYPASS_LUT);
+
+		pm8xxx_pwm_bank_sel(pwm);
+		rc = pm8xxx_lpg_pwm_write(pwm, 1, 6);
+	} else {
+		rc = pm8xxx_pwm_write(pwm);
+	}
+
+	pr_debug("duty/period=%u/%u usec: pwm_value=%d (of %d)\n",
+		 (unsigned)duty_us, (unsigned)period_us,
+		 pwm->pwm_value, 1 << period->pwm_size);
+
+out_unlock:
+	mutex_unlock(&pwm->chip->pwm_mutex);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(pwm_config);
+
+/**
+ * pwm_enable - start a PWM output toggling
+ * @pwm: the PWM device
+ */
+int pwm_enable(struct pwm_device *pwm)
+{
+	int	rc = 0;
+
+	if (pwm == NULL || IS_ERR(pwm)) {
+		pr_err("Invalid pwm handle\n");
+		return -EINVAL;
+	}
+	if (pwm->chip == NULL) {
+		pr_err("No pwm_chip\n");
+		return -ENODEV;
+	}
+
+	mutex_lock(&pwm->chip->pwm_mutex);
+	if (!pwm->in_use) {
+		pr_err("pwm_id: %d: stale handle?\n", pwm->pwm_id);
+		rc = -EINVAL;
+	} else {
+		if (pwm_chip->is_lpg_supported) {
+			if (pwm->dtest_mode_supported)
+				pm8xxx_pwm_set_dtest(pwm, 1);
+			rc = pm8xxx_pwm_bank_enable(pwm, 1);
+			pm8xxx_pwm_bank_sel(pwm);
+			pm8xxx_pwm_start(pwm, 1, 0);
+		} else {
+			pm8xxx_pwm_enable(pwm);
+		}
+	}
+	mutex_unlock(&pwm->chip->pwm_mutex);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(pwm_enable);
+
+/**
+ * pwm_disable - stop a PWM output toggling
+ * @pwm: the PWM device
+ */
+void pwm_disable(struct pwm_device *pwm)
+{
+	if (pwm == NULL || IS_ERR(pwm) || pwm->chip == NULL) {
+		pr_err("Invalid pwm handle or no pwm_chip\n");
+		return;
+	}
+
+	mutex_lock(&pwm->chip->pwm_mutex);
+	if (pwm->in_use) {
+		if (pwm_chip->is_lpg_supported) {
+			if (pwm->dtest_mode_supported)
+				pm8xxx_pwm_set_dtest(pwm, 0);
+			pm8xxx_pwm_bank_sel(pwm);
+			pm8xxx_pwm_start(pwm, 0, 0);
+			pm8xxx_pwm_bank_enable(pwm, 0);
+		} else {
+			pm8xxx_pwm_disable(pwm);
+		}
+	}
+	mutex_unlock(&pwm->chip->pwm_mutex);
+}
+EXPORT_SYMBOL_GPL(pwm_disable);
+
+/**
+ * pm8xxx_pwm_config_period - change PWM period
+ *
+ * @pwm: the PWM device
+ * @pwm_p: period in struct pm8xxx_pwm_period
+ */
+int pm8xxx_pwm_config_period(struct pwm_device *pwm,
+			     struct pm8xxx_pwm_period *period)
+{
+	int			rc;
+
+	if (pwm == NULL || IS_ERR(pwm) || period == NULL)
+		return -EINVAL;
+	if (pwm->chip == NULL)
+		return -ENODEV;
+
+	mutex_lock(&pwm->chip->pwm_mutex);
+
+	if (!pwm->in_use) {
+		rc = -EINVAL;
+		goto out_unlock;
+	}
+
+	pwm->period.pwm_size = period->pwm_size;
+	pwm->period.clk = period->clk;
+	pwm->period.pre_div = period->pre_div;
+	pwm->period.pre_div_exp = period->pre_div_exp;
+
+	pm8xxx_pwm_save_period(pwm);
+
+	if (pwm_chip->is_lpg_supported) {
+		pm8xxx_pwm_bank_sel(pwm);
+		rc = pm8xxx_lpg_pwm_write(pwm, 4, 6);
+	} else {
+		rc = pm8xxx_pwm_write(pwm);
+	}
+
+
+out_unlock:
+	mutex_unlock(&pwm->chip->pwm_mutex);
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_pwm_config_period);
+
+/**
+ * pm8xxx_pwm_config_pwm_value - change a PWM device configuration
+ * @pwm: the PWM device
+ * @pwm_value: the duty cycle in raw PWM value (< 2^pwm_size)
+ */
+int pm8xxx_pwm_config_pwm_value(struct pwm_device *pwm, int pwm_value)
+{
+	int	rc = 0;
+
+	if (pwm == NULL || IS_ERR(pwm))
+		return -EINVAL;
+	if (pwm->chip == NULL)
+		return -ENODEV;
+
+	mutex_lock(&pwm->chip->pwm_mutex);
+
+	if (!pwm->in_use || !pwm->pwm_period) {
+		rc = -EINVAL;
+		goto out_unlock;
+	}
+
+	if (pwm->pwm_value == pwm_value)
+		goto out_unlock;
+
+	pwm->pwm_value = pwm_value;
+
+	pm8xxx_pwm_save_pwm_value(pwm);
+
+	if (pwm_chip->is_lpg_supported) {
+		pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[1],
+				PM8XXX_PWM_BYPASS_LUT, PM8XXX_PWM_BYPASS_LUT);
+		pm8xxx_pwm_bank_sel(pwm);
+		rc = pm8xxx_lpg_pwm_write(pwm, 1, 6);
+	} else {
+		rc = pm8xxx_pwm_write(pwm);
+	}
+
+	if (rc)
+		pr_err("[%d]: pm8xxx_pwm_write: rc=%d\n", pwm->pwm_id, rc);
+
+out_unlock:
+	mutex_unlock(&pwm->chip->pwm_mutex);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_pwm_config_pwm_value);
+
+/**
+ * pm8xxx_pwm_lut_config - change a PWM device configuration to use LUT
+ * @pwm: the PWM device
+ * @period_us: period in microseconds
+ * @duty_pct: arrary of duty cycles in percent, like 20, 50.
+ * @duty_time_ms: time for each duty cycle in milliseconds
+ * @start_idx: start index in lookup table from 0 to MAX-1
+ * @idx_len: number of index
+ * @pause_lo: pause time in milliseconds at low index
+ * @pause_hi: pause time in milliseconds at high index
+ * @flags: control flags
+ */
+int pm8xxx_pwm_lut_config(struct pwm_device *pwm, int period_us,
+			  int duty_pct[], int duty_time_ms, int start_idx,
+			  int idx_len, int pause_lo, int pause_hi, int flags)
+{
+	struct pm8xxx_pwm_lut	lut;
+	struct pm8xxx_pwm_period *period;
+	int	len;
+	int	rc;
+
+	if (pwm == NULL || IS_ERR(pwm) || !idx_len) {
+		pr_err("Invalid pwm handle or idx_len=0\n");
+		return -EINVAL;
+	}
+	if (duty_pct == NULL && !(flags & PM_PWM_LUT_NO_TABLE)) {
+		pr_err("Invalid duty_pct with flag\n");
+		return -EINVAL;
+	}
+	if (pwm->chip == NULL) {
+		pr_err("No pwm_chip\n");
+		return -ENODEV;
+	}
+
+	if (pwm->chip->is_lpg_supported == 0) {
+		pr_err("LPG module isn't supported\n");
+		return -EINVAL;
+	}
+
+	if (idx_len >= PM_PWM_LUT_SIZE && start_idx) {
+		pr_err("Wrong LUT size or index\n");
+		return -EINVAL;
+	}
+	if ((start_idx + idx_len) > PM_PWM_LUT_SIZE) {
+		pr_err("Exceed LUT limit\n");
+		return -EINVAL;
+	}
+	if ((unsigned)period_us > PM8XXX_PWM_PERIOD_MAX ||
+		(unsigned)period_us < PM8XXX_PWM_PERIOD_MIN) {
+		pr_err("Period out of range\n");
+		return -EINVAL;
+	}
+
+	period = &pwm->period;
+	mutex_lock(&pwm->chip->pwm_mutex);
+
+	if (!pwm->in_use) {
+		pr_err("pwm_id: %d: stale handle?\n", pwm->pwm_id);
+		rc = -EINVAL;
+		goto out_unlock;
+	}
+
+	if (pwm->pwm_period != period_us) {
+		pm8xxx_pwm_calc_period(period_us, period);
+		pm8xxx_pwm_save_period(pwm);
+		pwm->pwm_period = period_us;
+	}
+
+	len = (idx_len > PM_PWM_LUT_SIZE) ? PM_PWM_LUT_SIZE : idx_len;
+
+	if (flags & PM_PWM_LUT_NO_TABLE)
+		goto after_table_write;
+
+	rc = pm8xxx_pwm_change_table(pwm, duty_pct, start_idx, len, 0);
+	if (rc) {
+		pr_err("pm8xxx_pwm_change_table: rc=%d\n", rc);
+		goto out_unlock;
+	}
+
+after_table_write:
+	lut.lut_duty_ms = duty_time_ms;
+	lut.lut_lo_index = start_idx;
+	lut.lut_hi_index = start_idx + len - 1;
+	lut.lut_pause_lo = pause_lo;
+	lut.lut_pause_hi = pause_hi;
+	lut.flags = flags;
+	pwm->bypass_lut = 0;
+
+	rc = pm8xxx_pwm_change_lut(pwm, &lut);
+
+out_unlock:
+	mutex_unlock(&pwm->chip->pwm_mutex);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_pwm_lut_config);
+
+/**
+ * pm8xxx_pwm_lut_enable - control a PWM device to start/stop LUT ramp
+ * @pwm: the PWM device
+ * @start: to start (1), or stop (0)
+ */
+int pm8xxx_pwm_lut_enable(struct pwm_device *pwm, int start)
+{
+	if (pwm == NULL || IS_ERR(pwm)) {
+		pr_err("Invalid pwm handle\n");
+		return -EINVAL;
+	}
+	if (pwm->chip == NULL) {
+		pr_err("No pwm_chip\n");
+		return -ENODEV;
+	}
+	if (pwm->chip->is_lpg_supported == 0) {
+		pr_err("LPG module isn't supported\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&pwm->chip->pwm_mutex);
+	if (start) {
+		if (pwm->dtest_mode_supported)
+			pm8xxx_pwm_set_dtest(pwm, 1);
+
+		pm8xxx_pwm_bank_enable(pwm, 1);
+
+		pm8xxx_pwm_bank_sel(pwm);
+		pm8xxx_pwm_start(pwm, 1, 1);
+	} else {
+		if (pwm->dtest_mode_supported)
+			pm8xxx_pwm_set_dtest(pwm, 0);
+
+		pm8xxx_pwm_bank_sel(pwm);
+		pm8xxx_pwm_start(pwm, 0, 0);
+
+		pm8xxx_pwm_bank_enable(pwm, 0);
+	}
+	mutex_unlock(&pwm->chip->pwm_mutex);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_pwm_lut_enable);
+
+#if defined(CONFIG_DEBUG_FS)
+
+struct pm8xxx_pwm_dbg_device;
+
+struct pm8xxx_pwm_user {
+	int				pwm_id;
+	struct pwm_device		*pwm;
+	int				period;
+	int				duty_cycle;
+	int				enable;
+	struct pm8xxx_pwm_dbg_device	*dbgdev;
+};
+
+struct pm8xxx_pwm_dbg_device {
+	struct mutex		dbg_mutex;
+	struct device		*dev;
+	struct dentry		*dent;
+
+	struct pm8xxx_pwm_user	*user;
+};
+
+static struct pm8xxx_pwm_dbg_device *pmic_dbg_device;
+
+static int dbg_pwm_check_period(int period)
+{
+	if (period < PM8XXX_PWM_PERIOD_MIN || period > PM8XXX_PWM_PERIOD_MAX) {
+		pr_err("period is invalid: %d\n", period);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int dbg_pwm_check_duty_cycle(int duty_cycle, const char *func_name)
+{
+	if (duty_cycle <= 0 || duty_cycle > 100) {
+		pr_err("%s: duty_cycle is invalid: %d\n",
+		      func_name, duty_cycle);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static void dbg_pwm_check_handle(struct pm8xxx_pwm_user *puser)
+{
+	struct pwm_device *tmp;
+
+	if (puser->pwm == NULL) {
+		tmp = pwm_request(puser->pwm_id, "pwm-dbg");
+		if (PTR_ERR(puser->pwm)) {
+			pr_err("pwm_request: err=%ld\n", PTR_ERR(puser->pwm));
+			puser->pwm = NULL;
+		} else {
+			pr_debug("[id=%d] pwm_request ok\n", puser->pwm_id);
+			puser->pwm = tmp;
+		}
+	}
+}
+
+static int dbg_pwm_enable_set(void *data, u64 val)
+{
+	struct pm8xxx_pwm_user	  *puser = data;
+	struct pm8xxx_pwm_dbg_device    *dbgdev = puser->dbgdev;
+	int     rc;
+
+	mutex_lock(&dbgdev->dbg_mutex);
+	rc = dbg_pwm_check_duty_cycle(puser->duty_cycle, __func__);
+	if (!rc) {
+		puser->enable = val;
+		dbg_pwm_check_handle(puser);
+		if (puser->pwm) {
+			if (puser->enable)
+				pwm_enable(puser->pwm);
+			else
+				pwm_disable(puser->pwm);
+		}
+	}
+	mutex_unlock(&dbgdev->dbg_mutex);
+	return 0;
+}
+
+static int dbg_pwm_enable_get(void *data, u64 *val)
+{
+	struct pm8xxx_pwm_user	  *puser = data;
+	struct pm8xxx_pwm_dbg_device    *dbgdev = puser->dbgdev;
+
+	mutex_lock(&dbgdev->dbg_mutex);
+	*val = puser->enable;
+	mutex_unlock(&dbgdev->dbg_mutex);
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(dbg_pwm_enable_fops,
+			dbg_pwm_enable_get, dbg_pwm_enable_set,
+			"%lld\n");
+
+static int dbg_pwm_duty_cycle_set(void *data, u64 val)
+{
+	struct pm8xxx_pwm_user	  *puser = data;
+	struct pm8xxx_pwm_dbg_device    *dbgdev = puser->dbgdev;
+	int     rc;
+
+	mutex_lock(&dbgdev->dbg_mutex);
+	rc = dbg_pwm_check_duty_cycle(val, __func__);
+	if (!rc) {
+		puser->duty_cycle = val;
+		dbg_pwm_check_handle(puser);
+		if (puser->pwm) {
+			int     duty_us;
+
+			duty_us = puser->duty_cycle * puser->period / 100;
+			pwm_config(puser->pwm, duty_us, puser->period);
+		}
+	}
+	mutex_unlock(&dbgdev->dbg_mutex);
+	return 0;
+}
+
+static int dbg_pwm_duty_cycle_get(void *data, u64 *val)
+{
+	struct pm8xxx_pwm_user	  *puser = data;
+	struct pm8xxx_pwm_dbg_device    *dbgdev = puser->dbgdev;
+
+	mutex_lock(&dbgdev->dbg_mutex);
+	*val = puser->duty_cycle;
+	mutex_unlock(&dbgdev->dbg_mutex);
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(dbg_pwm_duty_cycle_fops,
+			dbg_pwm_duty_cycle_get, dbg_pwm_duty_cycle_set,
+			"%lld\n");
+
+static int dbg_pwm_period_set(void *data, u64 val)
+{
+	struct pm8xxx_pwm_user	  *puser = data;
+	struct pm8xxx_pwm_dbg_device    *dbgdev = puser->dbgdev;
+	int     rc;
+
+	mutex_lock(&dbgdev->dbg_mutex);
+	rc = dbg_pwm_check_period(val);
+	if (!rc)
+		puser->period = val;
+	mutex_unlock(&dbgdev->dbg_mutex);
+	return 0;
+}
+
+static int dbg_pwm_period_get(void *data, u64 *val)
+{
+	struct pm8xxx_pwm_user	  *puser = data;
+	struct pm8xxx_pwm_dbg_device    *dbgdev = puser->dbgdev;
+
+	mutex_lock(&dbgdev->dbg_mutex);
+	*val = puser->period;
+	mutex_unlock(&dbgdev->dbg_mutex);
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(dbg_pwm_period_fops,
+			dbg_pwm_period_get, dbg_pwm_period_set, "%lld\n");
+
+static int __devinit pm8xxx_pwm_dbg_probe(struct device *dev)
+{
+	struct pm8xxx_pwm_dbg_device    *dbgdev;
+	struct dentry		   *dent;
+	struct dentry		   *temp;
+	struct pm8xxx_pwm_user	  *puser;
+	int			     i;
+	int rc = 0;
+
+	if (dev == NULL) {
+		pr_err("no parent data passed in.\n");
+		return -EINVAL;
+	}
+
+	dbgdev = kzalloc(sizeof *dbgdev, GFP_KERNEL);
+	if (dbgdev == NULL) {
+		pr_err("kzalloc() failed.\n");
+		return -ENOMEM;
+	}
+
+	dbgdev->user = kcalloc(pwm_chip->pwm_channels,
+				sizeof(struct pm8xxx_pwm_user), GFP_KERNEL);
+	if (dbgdev->user == NULL) {
+		pr_err("kcalloc() failed.\n");
+		rc = -ENOMEM;
+		goto user_error;
+	}
+
+	mutex_init(&dbgdev->dbg_mutex);
+
+	dbgdev->dev = dev;
+
+	dent = debugfs_create_dir("pm8xxx-pwm-dbg", NULL);
+	if (dent == NULL || IS_ERR(dent)) {
+		pr_err("ERR debugfs_create_dir: dent=%p\n", dent);
+		rc = -ENOMEM;
+		goto dir_error;
+	}
+
+	dbgdev->dent = dent;
+
+	for (i = 0; i < pwm_chip->pwm_channels; i++) {
+		char pwm_ch[] = "0";
+
+		pwm_ch[0] = '0' + i;
+		dent = debugfs_create_dir(pwm_ch, dbgdev->dent);
+		if (dent == NULL || IS_ERR(dent)) {
+			pr_err("ERR: pwm=%d: dir: dent=%p\n", i, dent);
+			rc = -ENOMEM;
+			goto debug_error;
+		}
+
+		puser = &dbgdev->user[i];
+		puser->dbgdev = dbgdev;
+		puser->pwm_id = i;
+		temp = debugfs_create_file("period", S_IRUGO | S_IWUSR,
+				dent, puser, &dbg_pwm_period_fops);
+		if (temp == NULL || IS_ERR(temp)) {
+			pr_err("ERR: pwm=%d: period: dent=%p\n", i, dent);
+			rc = -ENOMEM;
+			goto debug_error;
+		}
+
+		temp = debugfs_create_file("duty-cycle", S_IRUGO | S_IWUSR,
+				dent, puser, &dbg_pwm_duty_cycle_fops);
+		if (temp == NULL || IS_ERR(temp)) {
+			pr_err("ERR: pwm=%d: duty-cycle: dent=%p\n", i, dent);
+			rc = -ENOMEM;
+			goto debug_error;
+		}
+
+		temp = debugfs_create_file("enable", S_IRUGO | S_IWUSR,
+				dent, puser, &dbg_pwm_enable_fops);
+		if (temp == NULL || IS_ERR(temp)) {
+			pr_err("ERR: pwm=%d: enable: dent=%p\n", i, dent);
+			rc = -ENOMEM;
+			goto debug_error;
+		}
+	}
+
+	pmic_dbg_device = dbgdev;
+
+	return 0;
+
+debug_error:
+	debugfs_remove_recursive(dbgdev->dent);
+dir_error:
+	kfree(dbgdev->user);
+user_error:
+	kfree(dbgdev);
+	return rc;
+}
+
+static int __devexit pm8xxx_pwm_dbg_remove(void)
+{
+	if (pmic_dbg_device) {
+		kfree(pmic_dbg_device->user);
+		debugfs_remove_recursive(pmic_dbg_device->dent);
+		kfree(pmic_dbg_device);
+	}
+	return 0;
+}
+
+#else
+
+static int __devinit pm8xxx_pwm_dbg_probe(struct device *dev)
+{
+	return 0;
+}
+
+static int __devexit pm8xxx_pwm_dbg_remove(void)
+{
+	return 0;
+}
+
+#endif
+
+static int __devinit pm8xxx_pwm_probe(struct platform_device *pdev)
+{
+	const struct pm8xxx_pwm_platform_data *pdata = pdev->dev.platform_data;
+	struct pm8xxx_pwm_chip	*chip;
+	int	i, dtest_channel;
+	enum pm8xxx_version version;
+
+	chip = kzalloc(sizeof *chip, GFP_KERNEL);
+	if (chip == NULL) {
+		pr_err("kzalloc() failed.\n");
+		return -ENOMEM;
+	}
+
+	if (pdata != NULL)
+		dtest_channel = pdata->dtest_channel;
+	else
+		dtest_channel = -1;
+
+	mutex_init(&chip->pwm_mutex);
+
+	chip->dev = &pdev->dev;
+	pwm_chip = chip;
+
+	version = pm8xxx_get_version(chip->dev->parent);
+
+	if (version == PM8XXX_VERSION_8921 ||
+			version == PM8XXX_VERSION_8058 ||
+			version == PM8XXX_VERSION_8922 ||
+			version == PM8XXX_VERSION_8038) {
+		chip->is_lpg_supported = 1;
+	}
+	if (chip->is_lpg_supported) {
+		if (version == PM8XXX_VERSION_8922 ||
+				version == PM8XXX_VERSION_8038) {
+			for (i = 0; i < NUM_CLOCKS; i++)
+				pt_t[0][i] /= PRE_DIVIDE_2;
+			chip->pwm_channels = PM8XXX_LPG_V1_PWM_CHANNELS;
+		} else {
+			chip->pwm_channels = PM8XXX_LPG_V0_PWM_CHANNELS;
+		}
+		chip->pwm_total_pre_divs = NUM_LPG_PRE_DIVIDE;
+	} else {
+		chip->pwm_channels = PM8XXX_PWM_CHANNELS;
+		chip->pwm_total_pre_divs = NUM_PWM_PRE_DIVIDE;
+	}
+
+	chip->pwm_dev = kcalloc(chip->pwm_channels, sizeof(struct pwm_device),
+								GFP_KERNEL);
+	if (chip->pwm_dev == NULL) {
+		pr_err("kcalloc() failed.\n");
+		mutex_destroy(&chip->pwm_mutex);
+		kfree(chip);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < chip->pwm_channels; i++) {
+		chip->pwm_dev[i].pwm_id = i;
+		chip->pwm_dev[i].chip = chip;
+		if (i == dtest_channel)
+			chip->pwm_dev[i].dtest_mode_supported = 1;
+	}
+
+	platform_set_drvdata(pdev, chip);
+
+	if (pm8xxx_pwm_dbg_probe(&pdev->dev) < 0)
+		pr_err("could not set up debugfs\n");
+
+	pr_notice("OK\n");
+	return 0;
+}
+
+static int __devexit pm8xxx_pwm_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_pwm_chip	*chip = dev_get_drvdata(pdev->dev.parent);
+
+	pm8xxx_pwm_dbg_remove();
+	kfree(chip->pwm_dev);
+	mutex_destroy(&chip->pwm_mutex);
+	platform_set_drvdata(pdev, NULL);
+	kfree(chip);
+	return 0;
+}
+
+static struct platform_driver pm8xxx_pwm_driver = {
+	.probe		= pm8xxx_pwm_probe,
+	.remove		= __devexit_p(pm8xxx_pwm_remove),
+	.driver		= {
+		.name = PM8XXX_PWM_DEV_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init pm8xxx_pwm_init(void)
+{
+	return platform_driver_register(&pm8xxx_pwm_driver);
+}
+
+static void __exit pm8xxx_pwm_exit(void)
+{
+	platform_driver_unregister(&pm8xxx_pwm_driver);
+}
+
+subsys_initcall(pm8xxx_pwm_init);
+module_exit(pm8xxx_pwm_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PM8XXX PWM driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8XXX_PWM_DEV_NAME);
diff --git a/drivers/mfd/pm8xxx-spk.c b/drivers/mfd/pm8xxx-spk.c
new file mode 100644
index 0000000..297ddfa
--- /dev/null
+++ b/drivers/mfd/pm8xxx-spk.c
@@ -0,0 +1,279 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/stddef.h>
+#include <linux/debugfs.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/spk.h>
+
+#define PM8XXX_SPK_CTL1_REG_OFF		0
+#define PM8XXX_SPK_TEST_REG_1_OFF	1
+#define PM8XXX_SPK_TEST_REG_2_OFF	2
+
+#define PM8XXX_SPK_BANK_SEL		4
+#define PM8XXX_SPK_BANK_WRITE		0x80
+#define PM8XXX_SPK_BANK_VAL_MASK	0xF
+
+#define BOOST_6DB_GAIN_EN_MASK		0x8
+#define VSEL_LD0_1P1			0x0
+#define VSEL_LD0_1P2			0x2
+#define VSEL_LD0_1P0			0x4
+
+#define PWM_EN_MASK			0xF
+#define PM8XXX_SPK_TEST_REG_1_BANKS	8
+#define PM8XXX_SPK_TEST_REG_2_BANKS	2
+
+#define PM8XXX_SPK_GAIN			0x5
+#define PM8XXX_ADD_EN			0x1
+
+struct pm8xxx_spk_chip {
+	struct list_head                        link;
+	struct pm8xxx_spk_platform_data		pdata;
+	struct device                           *dev;
+	enum pm8xxx_version                     version;
+	struct mutex				spk_mutex;
+	u16					base;
+	u16					end;
+};
+
+static struct pm8xxx_spk_chip *the_spk_chip;
+
+static inline bool spk_defined(void)
+{
+	if (the_spk_chip == NULL || IS_ERR(the_spk_chip))
+		return false;
+	return true;
+}
+
+static int pm8xxx_spk_bank_write(u16 reg, u16 bank, u8 val)
+{
+	int rc = 0;
+	u8 bank_val = PM8XXX_SPK_BANK_WRITE | (bank << PM8XXX_SPK_BANK_SEL);
+
+	bank_val |= (val & PM8XXX_SPK_BANK_VAL_MASK);
+	mutex_lock(&the_spk_chip->spk_mutex);
+	rc = pm8xxx_writeb(the_spk_chip->dev->parent, reg, bank_val);
+	if (rc)
+		pr_err("pm8xxx_writeb(): rc=%d\n", rc);
+	mutex_unlock(&the_spk_chip->spk_mutex);
+	return rc;
+}
+
+
+static int pm8xxx_spk_read(u16 addr)
+{
+	int rc = 0;
+	u8 val = 0;
+
+	mutex_lock(&the_spk_chip->spk_mutex);
+	rc = pm8xxx_readb(the_spk_chip->dev->parent,
+			the_spk_chip->base + addr, &val);
+	if (rc) {
+		pr_err("pm8xxx_spk_readb() failed: rc=%d\n", rc);
+		val = rc;
+	}
+	mutex_unlock(&the_spk_chip->spk_mutex);
+
+	return val;
+}
+
+static int pm8xxx_spk_write(u16 addr, u8 val)
+{
+	int rc = 0;
+
+	mutex_lock(&the_spk_chip->spk_mutex);
+	rc = pm8xxx_writeb(the_spk_chip->dev->parent,
+			the_spk_chip->base + addr, val);
+	if (rc)
+		pr_err("pm8xxx_writeb() failed: rc=%d\n", rc);
+	mutex_unlock(&the_spk_chip->spk_mutex);
+	return rc;
+}
+
+int pm8xxx_spk_mute(bool mute)
+{
+	u8 val = 0;
+	int ret = 0;
+	if (spk_defined() == false) {
+		pr_err("Invalid spk handle or no spk_chip\n");
+		return -ENODEV;
+	}
+
+	val = pm8xxx_spk_read(PM8XXX_SPK_CTL1_REG_OFF);
+	if (val < 0)
+		return val;
+	val |= mute << 2;
+	ret = pm8xxx_spk_write(PM8XXX_SPK_CTL1_REG_OFF, val);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_spk_mute);
+
+int pm8xxx_spk_gain(u8 gain)
+{
+	u8 val;
+	int ret = 0;
+
+	if (spk_defined() == false) {
+		pr_err("Invalid spk handle or no spk_chip\n");
+		return -ENODEV;
+	}
+
+	val = pm8xxx_spk_read(PM8XXX_SPK_CTL1_REG_OFF);
+	if (val < 0)
+		return val;
+	val |= (gain << 4);
+	ret = pm8xxx_spk_write(PM8XXX_SPK_CTL1_REG_OFF, val);
+	if (!ret) {
+		pm8xxx_spk_bank_write(the_spk_chip->base
+			+ PM8XXX_SPK_TEST_REG_1_OFF,
+			0, BOOST_6DB_GAIN_EN_MASK | VSEL_LD0_1P2);
+	}
+	return ret;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_spk_gain);
+
+int pm8xxx_spk_enable(int enable)
+{
+	int val = 0;
+	u16 addr;
+	int ret = 0;
+
+	if (spk_defined() == false) {
+		pr_err("Invalid spk handle or no spk_chip\n");
+		return -ENODEV;
+	}
+
+	addr = the_spk_chip->base + PM8XXX_SPK_TEST_REG_1_OFF;
+	val = pm8xxx_spk_read(PM8XXX_SPK_CTL1_REG_OFF);
+	if (val < 0)
+		return val;
+	val |= (enable << 3);
+	ret = pm8xxx_spk_write(PM8XXX_SPK_CTL1_REG_OFF, val);
+	if (!ret)
+		ret = pm8xxx_spk_bank_write(addr, 6, PWM_EN_MASK);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_spk_enable);
+
+static int pm8xxx_spk_config(void)
+{
+	u16 addr;
+	int ret = 0;
+
+	if (spk_defined() == false) {
+		pr_err("Invalid spk handle or no spk_chip\n");
+		return -ENODEV;
+	}
+
+	addr = the_spk_chip->base + PM8XXX_SPK_TEST_REG_1_OFF;
+	ret = pm8xxx_spk_bank_write(addr, 6, PWM_EN_MASK & 0);
+	if (!ret)
+		ret = pm8xxx_spk_gain(PM8XXX_SPK_GAIN);
+	return ret;
+}
+
+static int __devinit pm8xxx_spk_probe(struct platform_device *pdev)
+{
+	const struct pm8xxx_spk_platform_data *pdata = pdev->dev.platform_data;
+	int ret = 0;
+
+	if (!pdata) {
+		pr_err("missing platform data\n");
+		return -EINVAL;
+	}
+
+	the_spk_chip = kzalloc(sizeof(struct pm8xxx_spk_chip), GFP_KERNEL);
+	if (the_spk_chip == NULL) {
+		pr_err("kzalloc() failed.\n");
+		return -ENOMEM;
+	}
+
+	mutex_init(&the_spk_chip->spk_mutex);
+
+	the_spk_chip->dev = &pdev->dev;
+	the_spk_chip->version = pm8xxx_get_version(the_spk_chip->dev->parent);
+	switch (pm8xxx_get_version(the_spk_chip->dev->parent)) {
+	case PM8XXX_VERSION_8038:
+		break;
+	default:
+		ret = -ENODEV;
+		goto err_handle;
+	}
+
+	memcpy(&(the_spk_chip->pdata), pdata,
+			sizeof(struct pm8xxx_spk_platform_data));
+
+	the_spk_chip->base = pdev->resource[0].start;
+	the_spk_chip->end = pdev->resource[0].end;
+
+	if (the_spk_chip->pdata.spk_add_enable) {
+		int val;
+		val = pm8xxx_spk_read(PM8XXX_SPK_CTL1_REG_OFF);
+		if (val < 0) {
+			ret = val;
+			goto err_handle;
+		}
+		val |= (the_spk_chip->pdata.spk_add_enable & PM8XXX_ADD_EN);
+		ret = pm8xxx_spk_write(PM8XXX_SPK_CTL1_REG_OFF, val);
+		if (ret < 0)
+			goto err_handle;
+	}
+	return pm8xxx_spk_config();
+err_handle:
+	pr_err("pm8xxx_spk_probe failed."
+			"Audio unavailable on speaker.\n");
+	mutex_destroy(&the_spk_chip->spk_mutex);
+	kfree(the_spk_chip);
+	return ret;
+}
+
+static int __devexit pm8xxx_spk_remove(struct platform_device *pdev)
+{
+	if (spk_defined() == false) {
+		pr_err("Invalid spk handle or no spk_chip\n");
+		return -ENODEV;
+	}
+	mutex_destroy(&the_spk_chip->spk_mutex);
+	kfree(the_spk_chip);
+	return 0;
+}
+
+static struct platform_driver pm8xxx_spk_driver = {
+	.probe		= pm8xxx_spk_probe,
+	.remove		= __devexit_p(pm8xxx_spk_remove),
+	.driver		= {
+		.name = PM8XXX_SPK_DEV_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init pm8xxx_spk_init(void)
+{
+	return platform_driver_register(&pm8xxx_spk_driver);
+}
+subsys_initcall(pm8xxx_spk_init);
+
+static void __exit pm8xxx_spk_exit(void)
+{
+	platform_driver_unregister(&pm8xxx_spk_driver);
+}
+module_exit(pm8xxx_spk_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PM8XXX SPK driver");
+MODULE_ALIAS("platform:" PM8XXX_SPK_DEV_NAME);
diff --git a/drivers/mfd/pmic8058.c b/drivers/mfd/pmic8058.c
new file mode 100644
index 0000000..8f7ab2d
--- /dev/null
+++ b/drivers/mfd/pmic8058.c
@@ -0,0 +1,792 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+/*
+ * Qualcomm PMIC8058 driver
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/msm_ssbi.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/pmic8058.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/msm_adc.h>
+#include <linux/module.h>
+
+#define REG_MPP_BASE			0x50
+#define REG_IRQ_BASE			0x1BB
+
+/* PMIC8058 Revision */
+#define PM8058_REG_REV			0x002  /* PMIC4 revision */
+#define PM8058_VERSION_MASK		0xF0
+#define PM8058_REVISION_MASK		0x0F
+#define PM8058_VERSION_VALUE		0xE0
+
+/* PMIC 8058 Battery Alarm SSBI registers */
+#define REG_BATT_ALARM_THRESH		0x023
+#define REG_BATT_ALARM_CTRL1		0x024
+#define REG_BATT_ALARM_CTRL2		0x0AA
+#define REG_BATT_ALARM_PWM_CTRL		0x0A3
+
+#define REG_TEMP_ALRM_CTRL		0x1B
+#define REG_TEMP_ALRM_PWM		0x9B
+
+/* PON CNTL 4 register */
+#define SSBI_REG_ADDR_PON_CNTL_4 0x98
+#define PM8058_PON_RESET_EN_MASK 0x01
+
+/* PON CNTL 5 register */
+#define SSBI_REG_ADDR_PON_CNTL_5 0x7B
+#define PM8058_HARD_RESET_EN_MASK 0x08
+
+/* GP_TEST1 register */
+#define SSBI_REG_ADDR_GP_TEST_1		0x07A
+
+#define PM8058_RTC_BASE			0x1E8
+#define PM8058_OTHC_CNTR_BASE0		0xA0
+#define PM8058_OTHC_CNTR_BASE1		0x134
+#define PM8058_OTHC_CNTR_BASE2		0x137
+
+#define SINGLE_IRQ_RESOURCE(_name, _irq) \
+{ \
+	.name	= _name, \
+	.start	= _irq, \
+	.end	= _irq, \
+	.flags	= IORESOURCE_IRQ, \
+}
+
+struct pm8058_chip {
+	struct pm8058_platform_data	pdata;
+	struct device		*dev;
+	struct pm_irq_chip	*irq_chip;
+	struct mfd_cell         *mfd_regulators, *mfd_xo_buffers;
+
+	u8		revision;
+};
+
+static int pm8058_readb(const struct device *dev, u16 addr, u8 *val)
+{
+	const struct pm8xxx_drvdata *pm8058_drvdata = dev_get_drvdata(dev);
+	const struct pm8058_chip *pmic = pm8058_drvdata->pm_chip_data;
+
+	return msm_ssbi_read(pmic->dev->parent, addr, val, 1);
+}
+
+static int pm8058_writeb(const struct device *dev, u16 addr, u8 val)
+{
+	const struct pm8xxx_drvdata *pm8058_drvdata = dev_get_drvdata(dev);
+	const struct pm8058_chip *pmic = pm8058_drvdata->pm_chip_data;
+
+	return msm_ssbi_write(pmic->dev->parent, addr, &val, 1);
+}
+
+static int pm8058_read_buf(const struct device *dev, u16 addr, u8 *buf,
+								int cnt)
+{
+	const struct pm8xxx_drvdata *pm8058_drvdata = dev_get_drvdata(dev);
+	const struct pm8058_chip *pmic = pm8058_drvdata->pm_chip_data;
+
+	return msm_ssbi_read(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8058_write_buf(const struct device *dev, u16 addr, u8 *buf,
+								int cnt)
+{
+	const struct pm8xxx_drvdata *pm8058_drvdata = dev_get_drvdata(dev);
+	const struct pm8058_chip *pmic = pm8058_drvdata->pm_chip_data;
+
+	return msm_ssbi_write(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8058_read_irq_stat(const struct device *dev, int irq)
+{
+	const struct pm8xxx_drvdata *pm8058_drvdata = dev_get_drvdata(dev);
+	const struct pm8058_chip *pmic = pm8058_drvdata->pm_chip_data;
+
+	return pm8xxx_get_irq_stat(pmic->irq_chip, irq);
+
+	return 0;
+}
+
+static enum pm8xxx_version pm8058_get_version(const struct device *dev)
+{
+	const struct pm8xxx_drvdata *pm8058_drvdata = dev_get_drvdata(dev);
+	const struct pm8058_chip *pmic = pm8058_drvdata->pm_chip_data;
+	enum pm8xxx_version version = -ENODEV;
+
+	if ((pmic->revision & PM8058_VERSION_MASK) == PM8058_VERSION_VALUE)
+		version = PM8XXX_VERSION_8058;
+
+	return version;
+}
+
+static int pm8058_get_revision(const struct device *dev)
+{
+	const struct pm8xxx_drvdata *pm8058_drvdata = dev_get_drvdata(dev);
+	const struct pm8058_chip *pmic = pm8058_drvdata->pm_chip_data;
+
+	return pmic->revision & PM8058_REVISION_MASK;
+}
+
+static struct pm8xxx_drvdata pm8058_drvdata = {
+	.pmic_readb		= pm8058_readb,
+	.pmic_writeb		= pm8058_writeb,
+	.pmic_read_buf		= pm8058_read_buf,
+	.pmic_write_buf		= pm8058_write_buf,
+	.pmic_read_irq_stat	= pm8058_read_irq_stat,
+	.pmic_get_version	= pm8058_get_version,
+	.pmic_get_revision	= pm8058_get_revision,
+};
+
+static const struct resource pm8058_charger_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE("CHGVAL",		PM8058_CHGVAL_IRQ),
+	SINGLE_IRQ_RESOURCE("CHGINVAL",		PM8058_CHGINVAL_IRQ),
+	SINGLE_IRQ_RESOURCE("CHGILIM",		PM8058_CHGILIM_IRQ),
+	SINGLE_IRQ_RESOURCE("VCP",		PM8058_VCP_IRQ),
+	SINGLE_IRQ_RESOURCE("ATC_DONE",		PM8058_ATC_DONE_IRQ),
+	SINGLE_IRQ_RESOURCE("ATCFAIL",		PM8058_ATCFAIL_IRQ),
+	SINGLE_IRQ_RESOURCE("AUTO_CHGDONE",	PM8058_AUTO_CHGDONE_IRQ),
+	SINGLE_IRQ_RESOURCE("AUTO_CHGFAIL",	PM8058_AUTO_CHGFAIL_IRQ),
+	SINGLE_IRQ_RESOURCE("CHGSTATE",		PM8058_CHGSTATE_IRQ),
+	SINGLE_IRQ_RESOURCE("FASTCHG",		PM8058_FASTCHG_IRQ),
+	SINGLE_IRQ_RESOURCE("CHG_END",		PM8058_CHG_END_IRQ),
+	SINGLE_IRQ_RESOURCE("BATTTEMP",		PM8058_BATTTEMP_IRQ),
+	SINGLE_IRQ_RESOURCE("CHGHOT",		PM8058_CHGHOT_IRQ),
+	SINGLE_IRQ_RESOURCE("CHGTLIMIT",	PM8058_CHGTLIMIT_IRQ),
+	SINGLE_IRQ_RESOURCE("CHG_GONE",		PM8058_CHG_GONE_IRQ),
+	SINGLE_IRQ_RESOURCE("VCPMAJOR",		PM8058_VCPMAJOR_IRQ),
+	SINGLE_IRQ_RESOURCE("VBATDET",		PM8058_VBATDET_IRQ),
+	SINGLE_IRQ_RESOURCE("BATFET",		PM8058_BATFET_IRQ),
+	SINGLE_IRQ_RESOURCE("BATT_REPLACE",	PM8058_BATT_REPLACE_IRQ),
+	SINGLE_IRQ_RESOURCE("BATTCONNECT",	PM8058_BATTCONNECT_IRQ),
+	SINGLE_IRQ_RESOURCE("VBATDET_LOW",	PM8058_VBATDET_LOW_IRQ),
+};
+
+static struct mfd_cell pm8058_charger_cell __devinitdata = {
+	.name		= "pm8058-charger",
+	.id		= -1,
+	.resources	= pm8058_charger_resources,
+	.num_resources	= ARRAY_SIZE(pm8058_charger_resources),
+};
+
+static const struct resource misc_cell_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE("pm8xxx_osc_halt_irq", PM8058_OSCHALT_IRQ),
+};
+
+static struct mfd_cell misc_cell __devinitdata = {
+	.name		= PM8XXX_MISC_DEV_NAME,
+	.id		= -1,
+	.resources	= misc_cell_resources,
+	.num_resources	= ARRAY_SIZE(misc_cell_resources),
+};
+
+static struct mfd_cell pm8058_pwm_cell __devinitdata = {
+	.name		= "pm8058-pwm",
+	.id		= -1,
+};
+
+static struct resource xoadc_resources[] = {
+	SINGLE_IRQ_RESOURCE(NULL, PM8058_ADC_IRQ),
+};
+
+static struct mfd_cell xoadc_cell __devinitdata = {
+	.name		= "pm8058-xoadc",
+	.id		= -1,
+	.resources	= xoadc_resources,
+	.num_resources	= ARRAY_SIZE(xoadc_resources),
+};
+
+static const struct resource thermal_alarm_cell_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE("pm8058_tempstat_irq", PM8058_TEMPSTAT_IRQ),
+	SINGLE_IRQ_RESOURCE("pm8058_overtemp_irq", PM8058_OVERTEMP_IRQ),
+};
+
+static struct pm8xxx_tm_core_data thermal_alarm_cdata = {
+	.adc_channel			= CHANNEL_ADC_DIE_TEMP,
+	.adc_type			= PM8XXX_TM_ADC_PM8058_ADC,
+	.reg_addr_temp_alarm_ctrl	= REG_TEMP_ALRM_CTRL,
+	.reg_addr_temp_alarm_pwm	= REG_TEMP_ALRM_PWM,
+	.tm_name			= "pm8058_tz",
+	.irq_name_temp_stat		= "pm8058_tempstat_irq",
+	.irq_name_over_temp		= "pm8058_overtemp_irq",
+};
+
+static struct mfd_cell thermal_alarm_cell __devinitdata = {
+	.name		= PM8XXX_TM_DEV_NAME,
+	.id		= -1,
+	.resources	= thermal_alarm_cell_resources,
+	.num_resources	= ARRAY_SIZE(thermal_alarm_cell_resources),
+	.platform_data	= &thermal_alarm_cdata,
+	.pdata_size	= sizeof(struct pm8xxx_tm_core_data),
+};
+
+static struct mfd_cell debugfs_cell __devinitdata = {
+	.name		= "pm8xxx-debug",
+	.id		= -1,
+	.platform_data	= "pm8058-dbg",
+	.pdata_size	= sizeof("pm8058-dbg"),
+};
+
+static const struct resource othc0_cell_resources[] __devinitconst = {
+	{
+		.name	= "othc_base",
+		.start	= PM8058_OTHC_CNTR_BASE0,
+		.end	= PM8058_OTHC_CNTR_BASE0,
+		.flags	= IORESOURCE_IO,
+	},
+};
+
+static const struct resource othc1_cell_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE(NULL, PM8058_SW_1_IRQ),
+	SINGLE_IRQ_RESOURCE(NULL, PM8058_IR_1_IRQ),
+	{
+		.name	= "othc_base",
+		.start	= PM8058_OTHC_CNTR_BASE1,
+		.end	= PM8058_OTHC_CNTR_BASE1,
+		.flags	= IORESOURCE_IO,
+	},
+};
+
+static const struct resource othc2_cell_resources[] __devinitconst = {
+	{
+		.name	= "othc_base",
+		.start	= PM8058_OTHC_CNTR_BASE2,
+		.end	= PM8058_OTHC_CNTR_BASE2,
+		.flags	= IORESOURCE_IO,
+	},
+};
+
+static const struct resource batt_alarm_cell_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE("pm8058_batt_alarm_irq", PM8058_BATT_ALARM_IRQ),
+};
+
+static struct mfd_cell leds_cell __devinitdata = {
+	.name		= "pm8058-led",
+	.id		= -1,
+};
+
+static struct mfd_cell othc0_cell __devinitdata = {
+	.name		= "pm8058-othc",
+	.id		= 0,
+	.resources	= othc0_cell_resources,
+	.num_resources  = ARRAY_SIZE(othc0_cell_resources),
+};
+
+static struct mfd_cell othc1_cell __devinitdata = {
+	.name		= "pm8058-othc",
+	.id		= 1,
+	.resources	= othc1_cell_resources,
+	.num_resources  = ARRAY_SIZE(othc1_cell_resources),
+};
+
+static struct mfd_cell othc2_cell __devinitdata = {
+	.name		= "pm8058-othc",
+	.id		= 2,
+	.resources	= othc2_cell_resources,
+	.num_resources  = ARRAY_SIZE(othc2_cell_resources),
+};
+
+static struct pm8xxx_batt_alarm_core_data batt_alarm_cdata = {
+	.irq_name		= "pm8058_batt_alarm_irq",
+	.reg_addr_threshold	= REG_BATT_ALARM_THRESH,
+	.reg_addr_ctrl1		= REG_BATT_ALARM_CTRL1,
+	.reg_addr_ctrl2		= REG_BATT_ALARM_CTRL2,
+	.reg_addr_pwm_ctrl	= REG_BATT_ALARM_PWM_CTRL,
+};
+
+static struct mfd_cell batt_alarm_cell __devinitdata = {
+	.name		= PM8XXX_BATT_ALARM_DEV_NAME,
+	.id		= -1,
+	.resources	= batt_alarm_cell_resources,
+	.num_resources	= ARRAY_SIZE(batt_alarm_cell_resources),
+	.platform_data	= &batt_alarm_cdata,
+	.pdata_size	= sizeof(struct pm8xxx_batt_alarm_core_data),
+};
+
+static struct mfd_cell upl_cell __devinitdata = {
+	.name		= PM8XXX_UPL_DEV_NAME,
+	.id		= -1,
+};
+
+static struct mfd_cell nfc_cell __devinitdata = {
+	.name		= PM8XXX_NFC_DEV_NAME,
+	.id		= -1,
+};
+
+static const struct resource rtc_cell_resources[] __devinitconst = {
+	[0] = SINGLE_IRQ_RESOURCE(NULL, PM8058_RTC_ALARM_IRQ),
+	[1] = {
+		.name   = "pmic_rtc_base",
+		.start  = PM8058_RTC_BASE,
+		.end    = PM8058_RTC_BASE,
+		.flags  = IORESOURCE_IO,
+	},
+};
+
+static struct mfd_cell rtc_cell __devinitdata = {
+	.name		= PM8XXX_RTC_DEV_NAME,
+	.id		= -1,
+	.resources	= rtc_cell_resources,
+	.num_resources  = ARRAY_SIZE(rtc_cell_resources),
+};
+
+static const struct resource resources_pwrkey[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE(NULL, PM8058_PWRKEY_REL_IRQ),
+	SINGLE_IRQ_RESOURCE(NULL, PM8058_PWRKEY_PRESS_IRQ),
+};
+
+static struct mfd_cell vibrator_cell __devinitdata = {
+	.name		= PM8XXX_VIBRATOR_DEV_NAME,
+	.id		= -1,
+};
+
+static struct mfd_cell pwrkey_cell __devinitdata = {
+	.name		= PM8XXX_PWRKEY_DEV_NAME,
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(resources_pwrkey),
+	.resources	= resources_pwrkey,
+};
+
+static const struct resource resources_keypad[] = {
+	SINGLE_IRQ_RESOURCE(NULL, PM8058_KEYPAD_IRQ),
+	SINGLE_IRQ_RESOURCE(NULL, PM8058_KEYSTUCK_IRQ),
+};
+
+static struct mfd_cell keypad_cell __devinitdata = {
+	.name		= PM8XXX_KEYPAD_DEV_NAME,
+	.id		= -1,
+	.num_resources  = ARRAY_SIZE(resources_keypad),
+	.resources	= resources_keypad,
+};
+
+static const struct resource mpp_cell_resources[] __devinitconst = {
+	{
+		.start	= PM8058_IRQ_BLOCK_BIT(PM8058_MPP_BLOCK_START, 0),
+		.end	= PM8058_IRQ_BLOCK_BIT(PM8058_MPP_BLOCK_START, 0)
+			  + PM8058_MPPS - 1,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct mfd_cell mpp_cell __devinitdata = {
+	.name		= PM8XXX_MPP_DEV_NAME,
+	.id		= 0,
+	.resources	= mpp_cell_resources,
+	.num_resources	= ARRAY_SIZE(mpp_cell_resources),
+};
+
+static const struct resource gpio_cell_resources[] __devinitconst = {
+	[0] = {
+		.start = PM8058_IRQ_BLOCK_BIT(PM8058_GPIO_BLOCK_START, 0),
+		.end   = PM8058_IRQ_BLOCK_BIT(PM8058_GPIO_BLOCK_START, 0)
+			+ PM8058_GPIOS - 1,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct mfd_cell gpio_cell __devinitdata = {
+	.name		= PM8XXX_GPIO_DEV_NAME,
+	.id		= -1,
+	.resources	= gpio_cell_resources,
+	.num_resources	= ARRAY_SIZE(gpio_cell_resources),
+};
+
+static int __devinit
+pm8058_add_subdevices(const struct pm8058_platform_data *pdata,
+				struct pm8058_chip *pmic)
+{
+	int rc = 0, irq_base = 0, i;
+	struct pm_irq_chip *irq_chip;
+	static struct mfd_cell *mfd_regulators, *mfd_xo_buffers;
+
+	if (pdata->irq_pdata) {
+		pdata->irq_pdata->irq_cdata.nirqs = PM8058_NR_IRQS;
+		pdata->irq_pdata->irq_cdata.base_addr = REG_IRQ_BASE;
+		irq_base = pdata->irq_pdata->irq_base;
+		irq_chip = pm8xxx_irq_init(pmic->dev, pdata->irq_pdata);
+
+		if (IS_ERR(irq_chip)) {
+			pr_err("Failed to init interrupts ret=%ld\n",
+					PTR_ERR(irq_chip));
+			return PTR_ERR(irq_chip);
+		}
+		pmic->irq_chip = irq_chip;
+	}
+
+	if (pdata->gpio_pdata) {
+		pdata->gpio_pdata->gpio_cdata.ngpios = PM8058_GPIOS;
+		gpio_cell.platform_data = pdata->gpio_pdata;
+		gpio_cell.pdata_size = sizeof(struct pm8xxx_gpio_platform_data);
+		rc = mfd_add_devices(pmic->dev, 0, &gpio_cell, 1,
+					NULL, irq_base);
+		if (rc) {
+			pr_err("Failed to add  gpio subdevice ret=%d\n", rc);
+			goto bail;
+		}
+	}
+
+	if (pdata->mpp_pdata) {
+		pdata->mpp_pdata->core_data.nmpps = PM8058_MPPS;
+		pdata->mpp_pdata->core_data.base_addr = REG_MPP_BASE;
+		mpp_cell.platform_data = pdata->mpp_pdata;
+		mpp_cell.pdata_size = sizeof(struct pm8xxx_mpp_platform_data);
+		rc = mfd_add_devices(pmic->dev, 0, &mpp_cell, 1, NULL,
+					irq_base);
+		if (rc) {
+			pr_err("Failed to add mpp subdevice ret=%d\n", rc);
+			goto bail;
+		}
+	}
+
+	if (pdata->num_regulators > 0 && pdata->regulator_pdatas) {
+		mfd_regulators = kzalloc(sizeof(struct mfd_cell)
+					 * (pdata->num_regulators), GFP_KERNEL);
+		if (!mfd_regulators) {
+			pr_err("Cannot allocate %d bytes for pm8058 regulator "
+				"mfd cells\n", sizeof(struct mfd_cell)
+						* (pdata->num_regulators));
+			rc = -ENOMEM;
+			goto bail;
+		}
+		for (i = 0; i < pdata->num_regulators; i++) {
+			mfd_regulators[i].name = "pm8058-regulator";
+			mfd_regulators[i].id = pdata->regulator_pdatas[i].id;
+			mfd_regulators[i].platform_data =
+				&(pdata->regulator_pdatas[i]);
+			mfd_regulators[i].pdata_size =
+					sizeof(struct pm8058_vreg_pdata);
+		}
+		rc = mfd_add_devices(pmic->dev, 0, mfd_regulators,
+				pdata->num_regulators, NULL, irq_base);
+		if (rc) {
+			pr_err("Failed to add regulator subdevices ret=%d\n",
+				rc);
+			kfree(mfd_regulators);
+			goto bail;
+		}
+		pmic->mfd_regulators = mfd_regulators;
+	}
+
+	if (pdata->num_xo_buffers > 0 && pdata->xo_buffer_pdata) {
+		mfd_xo_buffers = kzalloc(sizeof(struct mfd_cell)
+					 * (pdata->num_xo_buffers), GFP_KERNEL);
+		if (!mfd_xo_buffers) {
+			pr_err("Cannot allocate %d bytes for pm8058 XO buffer "
+				"mfd cells\n", sizeof(struct mfd_cell)
+						* (pdata->num_xo_buffers));
+			rc = -ENOMEM;
+			goto bail;
+		}
+		for (i = 0; i < pdata->num_xo_buffers; i++) {
+			mfd_xo_buffers[i].name = PM8058_XO_BUFFER_DEV_NAME;
+			mfd_xo_buffers[i].id = pdata->xo_buffer_pdata[i].id;
+			mfd_xo_buffers[i].platform_data =
+				&(pdata->xo_buffer_pdata[i]);
+			mfd_xo_buffers[i].pdata_size =
+					sizeof(struct pm8058_xo_pdata);
+		}
+		rc = mfd_add_devices(pmic->dev, 0, mfd_xo_buffers,
+				pdata->num_xo_buffers, NULL, irq_base);
+		if (rc) {
+			pr_err("Failed to add XO buffer subdevices ret=%d\n",
+				rc);
+			kfree(mfd_xo_buffers);
+			goto bail;
+		}
+		pmic->mfd_xo_buffers = mfd_xo_buffers;
+	}
+
+	if (pdata->keypad_pdata) {
+		keypad_cell.platform_data = pdata->keypad_pdata;
+		keypad_cell.pdata_size =
+			sizeof(struct pm8xxx_keypad_platform_data);
+		rc = mfd_add_devices(pmic->dev, 0, &keypad_cell, 1, NULL,
+					irq_base);
+		if (rc) {
+			pr_err("Failed to add keypad subdevice ret=%d\n", rc);
+			goto bail;
+		}
+	}
+
+	if (pdata->rtc_pdata) {
+		rtc_cell.platform_data = pdata->rtc_pdata;
+		rtc_cell.pdata_size = sizeof(struct pm8xxx_rtc_platform_data);
+		rc = mfd_add_devices(pmic->dev, 0, &rtc_cell, 1, NULL,
+						irq_base);
+		if (rc) {
+			pr_err("Failed to add rtc subdevice ret=%d\n", rc);
+			goto bail;
+		}
+	}
+
+	if (pdata->pwrkey_pdata) {
+		pwrkey_cell.platform_data = pdata->pwrkey_pdata;
+		pwrkey_cell.pdata_size =
+			sizeof(struct pm8xxx_pwrkey_platform_data);
+		rc = mfd_add_devices(pmic->dev, 0, &pwrkey_cell, 1, NULL,
+							irq_base);
+		if (rc) {
+			pr_err("Failed to add pwrkey subdevice ret=%d\n", rc);
+			goto bail;
+		}
+	}
+
+	if (pdata->vibrator_pdata) {
+		vibrator_cell.platform_data = pdata->vibrator_pdata;
+		vibrator_cell.pdata_size =
+				sizeof(struct pm8xxx_vibrator_platform_data);
+		rc = mfd_add_devices(pmic->dev, 0, &vibrator_cell, 1, NULL,
+								irq_base);
+		if (rc) {
+			pr_err("Failed to add vibrator subdevice ret=%d\n",
+									rc);
+			goto bail;
+		}
+	}
+
+	if (pdata->leds_pdata) {
+		leds_cell.platform_data = pdata->leds_pdata;
+		leds_cell.pdata_size =
+			sizeof(struct pmic8058_leds_platform_data);
+		rc = mfd_add_devices(pmic->dev, 0, &leds_cell, 1, NULL,
+								irq_base);
+		if (rc) {
+			pr_err("Failed to add leds subdevice ret=%d\n", rc);
+			goto bail;
+		}
+	}
+
+	if (pdata->xoadc_pdata) {
+		xoadc_cell.platform_data = pdata->xoadc_pdata;
+		xoadc_cell.pdata_size =
+			sizeof(struct xoadc_platform_data);
+		rc = mfd_add_devices(pmic->dev, 0, &xoadc_cell, 1, NULL,
+								irq_base);
+		if (rc) {
+			pr_err("Failed to add leds subdevice ret=%d\n", rc);
+			goto bail;
+		}
+	}
+
+	if (pdata->othc0_pdata) {
+		othc0_cell.platform_data = pdata->othc0_pdata;
+		othc0_cell.pdata_size =
+			sizeof(struct pmic8058_othc_config_pdata);
+		rc = mfd_add_devices(pmic->dev, 0, &othc0_cell, 1, NULL, 0);
+		if (rc) {
+			pr_err("Failed to add othc0 subdevice ret=%d\n", rc);
+			goto bail;
+		}
+	}
+
+	if (pdata->othc1_pdata) {
+		othc1_cell.platform_data = pdata->othc1_pdata;
+		othc1_cell.pdata_size =
+			sizeof(struct pmic8058_othc_config_pdata);
+		rc = mfd_add_devices(pmic->dev, 0, &othc1_cell, 1, NULL,
+								irq_base);
+		if (rc) {
+			pr_err("Failed to add othc1 subdevice ret=%d\n", rc);
+			goto bail;
+		}
+	}
+
+	if (pdata->othc2_pdata) {
+		othc2_cell.platform_data = pdata->othc2_pdata;
+		othc2_cell.pdata_size =
+			sizeof(struct pmic8058_othc_config_pdata);
+		rc = mfd_add_devices(pmic->dev, 0, &othc2_cell, 1, NULL, 0);
+		if (rc) {
+			pr_err("Failed to add othc2 subdevice ret=%d\n", rc);
+			goto bail;
+		}
+	}
+
+	if (pdata->pwm_pdata) {
+		pm8058_pwm_cell.platform_data = pdata->pwm_pdata;
+		pm8058_pwm_cell.pdata_size = sizeof(struct pm8058_pwm_pdata);
+		rc = mfd_add_devices(pmic->dev, 0, &pm8058_pwm_cell, 1, NULL,
+								irq_base);
+		if (rc) {
+			pr_err("Failed to add pwm subdevice ret=%d\n", rc);
+			goto bail;
+		}
+	}
+
+	if (pdata->misc_pdata) {
+		misc_cell.platform_data = pdata->misc_pdata;
+		misc_cell.pdata_size = sizeof(struct pm8xxx_misc_platform_data);
+		rc = mfd_add_devices(pmic->dev, 0, &misc_cell, 1, NULL,
+				      irq_base);
+		if (rc) {
+			pr_err("Failed to add  misc subdevice ret=%d\n", rc);
+			goto bail;
+		}
+	}
+
+	rc = mfd_add_devices(pmic->dev, 0, &thermal_alarm_cell, 1, NULL,
+				irq_base);
+	if (rc) {
+		pr_err("Failed to add thermal alarm subdevice ret=%d\n",
+			rc);
+		goto bail;
+	}
+
+	rc = mfd_add_devices(pmic->dev, 0, &batt_alarm_cell, 1, NULL,
+				irq_base);
+	if (rc) {
+		pr_err("Failed to add battery alarm subdevice ret=%d\n",
+			rc);
+		goto bail;
+	}
+
+	rc = mfd_add_devices(pmic->dev, 0, &upl_cell, 1, NULL, 0);
+	if (rc) {
+		pr_err("Failed to add upl subdevice ret=%d\n", rc);
+		goto bail;
+	}
+
+	rc = mfd_add_devices(pmic->dev, 0, &nfc_cell, 1, NULL, 0);
+	if (rc) {
+		pr_err("Failed to add upl subdevice ret=%d\n", rc);
+		goto bail;
+	}
+
+	if (pdata->charger_pdata) {
+		pm8058_charger_cell.platform_data = pdata->charger_pdata;
+		pm8058_charger_cell.pdata_size = sizeof(struct
+						pmic8058_charger_data);
+		rc = mfd_add_devices(pmic->dev, 0, &pm8058_charger_cell,
+						1, NULL, irq_base);
+		if (rc) {
+			pr_err("Failed to add charger subdevice ret=%d\n", rc);
+			goto bail;
+		}
+	}
+
+	rc = mfd_add_devices(pmic->dev, 0, &debugfs_cell, 1, NULL, irq_base);
+	if (rc) {
+		pr_err("Failed to add debugfs subdevice ret=%d\n", rc);
+		goto bail;
+	}
+
+	return rc;
+bail:
+	if (pmic->irq_chip) {
+		pm8xxx_irq_exit(pmic->irq_chip);
+		pmic->irq_chip = NULL;
+	}
+	return rc;
+}
+
+static int __devinit pm8058_probe(struct platform_device *pdev)
+{
+	int rc;
+	struct pm8058_platform_data *pdata = pdev->dev.platform_data;
+	struct pm8058_chip *pmic;
+
+	if (pdata == NULL) {
+		pr_err("%s: No platform_data or IRQ.\n", __func__);
+		return -ENODEV;
+	}
+
+	pmic = kzalloc(sizeof *pmic, GFP_KERNEL);
+	if (pmic == NULL) {
+		pr_err("%s: kzalloc() failed.\n", __func__);
+		return -ENOMEM;
+	}
+
+	pmic->dev = &pdev->dev;
+
+	pm8058_drvdata.pm_chip_data = pmic;
+	platform_set_drvdata(pdev, &pm8058_drvdata);
+
+	/* Read PMIC chip revision */
+	rc = pm8058_readb(pmic->dev, PM8058_REG_REV, &pmic->revision);
+	if (rc)
+		pr_err("%s: Failed on pm8058_readb for revision: rc=%d.\n",
+			__func__, rc);
+
+	pr_info("%s: PMIC revision: %X\n", __func__, pmic->revision);
+
+	(void) memcpy((void *)&pmic->pdata, (const void *)pdata,
+		      sizeof(pmic->pdata));
+
+	rc = pm8058_add_subdevices(pdata, pmic);
+	if (rc) {
+		pr_err("Cannot add subdevices rc=%d\n", rc);
+		goto err;
+	}
+
+	rc = pm8xxx_hard_reset_config(PM8XXX_SHUTDOWN_ON_HARD_RESET);
+	if (rc < 0)
+		pr_err("%s: failed to config shutdown on hard reset: %d\n",
+								__func__, rc);
+
+	return 0;
+
+err:
+	mfd_remove_devices(pmic->dev);
+	platform_set_drvdata(pdev, NULL);
+	kfree(pmic);
+	return rc;
+}
+
+static int __devexit pm8058_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_drvdata *drvdata;
+	struct pm8058_chip *pmic = NULL;
+
+	drvdata = platform_get_drvdata(pdev);
+	if (drvdata)
+		pmic = drvdata->pm_chip_data;
+	if (pmic) {
+		if (pmic->dev)
+			mfd_remove_devices(pmic->dev);
+		if (pmic->irq_chip)
+			pm8xxx_irq_exit(pmic->irq_chip);
+		kfree(pmic->mfd_regulators);
+		kfree(pmic);
+	}
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver pm8058_driver = {
+	.probe		= pm8058_probe,
+	.remove		= __devexit_p(pm8058_remove),
+	.driver		= {
+		.name	= "pm8058-core",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init pm8058_init(void)
+{
+	return platform_driver_register(&pm8058_driver);
+}
+postcore_initcall(pm8058_init);
+
+static void __exit pm8058_exit(void)
+{
+	platform_driver_unregister(&pm8058_driver);
+}
+module_exit(pm8058_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC8058 core driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pmic8058-core");
diff --git a/drivers/mfd/pmic8901.c b/drivers/mfd/pmic8901.c
new file mode 100644
index 0000000..955258c
--- /dev/null
+++ b/drivers/mfd/pmic8901.c
@@ -0,0 +1,382 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/mfd/core.h>
+#include <linux/msm_ssbi.h>
+#include <linux/mfd/pmic8901.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/module.h>
+
+/* PMIC8901 Revision */
+#define PM8901_REG_REV			0x002
+#define PM8901_VERSION_MASK		0xF0
+#define PM8901_REVISION_MASK		0x0F
+#define PM8901_VERSION_VALUE		0xF0
+
+#define REG_IRQ_BASE			0xD5
+#define REG_MPP_BASE			0x27
+
+#define REG_TEMP_ALRM_CTRL		0x23
+#define REG_TEMP_ALRM_PWM		0x24
+
+#define SINGLE_IRQ_RESOURCE(_name, _irq) \
+{ \
+	.name	= _name, \
+	.start	= _irq, \
+	.end	= _irq, \
+	.flags	= IORESOURCE_IRQ, \
+}
+
+struct pm8901_chip {
+	struct pm8901_platform_data	pdata;
+	struct device			*dev;
+	struct pm_irq_chip		*irq_chip;
+	struct mfd_cell			*mfd_regulators;
+	u8				revision;
+};
+
+static int pm8901_readb(const struct device *dev, u16 addr, u8 *val)
+{
+	const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
+	const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
+
+	return msm_ssbi_read(pmic->dev->parent, addr, val, 1);
+}
+
+static int pm8901_writeb(const struct device *dev, u16 addr, u8 val)
+{
+	const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
+	const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
+
+	return msm_ssbi_write(pmic->dev->parent, addr, &val, 1);
+}
+
+static int pm8901_read_buf(const struct device *dev, u16 addr, u8 *buf,
+								int cnt)
+{
+	const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
+	const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
+
+	return msm_ssbi_read(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8901_write_buf(const struct device *dev, u16 addr, u8 *buf,
+								int cnt)
+{
+	const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
+	const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
+
+	return msm_ssbi_write(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8901_read_irq_stat(const struct device *dev, int irq)
+{
+	const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
+	const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
+
+	return pm8xxx_get_irq_stat(pmic->irq_chip, irq);
+
+	return 0;
+}
+
+static enum pm8xxx_version pm8901_get_version(const struct device *dev)
+{
+	const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
+	const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
+	enum pm8xxx_version version = -ENODEV;
+
+	if ((pmic->revision & PM8901_VERSION_MASK) == PM8901_VERSION_VALUE)
+		version = PM8XXX_VERSION_8901;
+
+	return version;
+}
+
+static int pm8901_get_revision(const struct device *dev)
+{
+	const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
+	const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
+
+	return pmic->revision & PM8901_REVISION_MASK;
+}
+
+static struct pm8xxx_drvdata pm8901_drvdata = {
+	.pmic_readb		= pm8901_readb,
+	.pmic_writeb		= pm8901_writeb,
+	.pmic_read_buf		= pm8901_read_buf,
+	.pmic_write_buf		= pm8901_write_buf,
+	.pmic_read_irq_stat	= pm8901_read_irq_stat,
+	.pmic_get_version	= pm8901_get_version,
+	.pmic_get_revision	= pm8901_get_revision,
+};
+
+static struct mfd_cell misc_cell = {
+	.name		= PM8XXX_MISC_DEV_NAME,
+	.id		= 1,
+};
+
+static struct mfd_cell debugfs_cell = {
+	.name		= "pm8xxx-debug",
+	.id		= 1,
+	.platform_data	= "pm8901-dbg",
+	.pdata_size	= sizeof("pm8901-dbg"),
+};
+
+static const struct resource thermal_alarm_cell_resources[] = {
+	SINGLE_IRQ_RESOURCE("pm8901_tempstat_irq", PM8901_TEMPSTAT_IRQ),
+	SINGLE_IRQ_RESOURCE("pm8901_overtemp_irq", PM8901_OVERTEMP_IRQ),
+};
+
+static struct pm8xxx_tm_core_data thermal_alarm_cdata = {
+	.adc_type			= PM8XXX_TM_ADC_NONE,
+	.reg_addr_temp_alarm_ctrl	= REG_TEMP_ALRM_CTRL,
+	.reg_addr_temp_alarm_pwm	= REG_TEMP_ALRM_PWM,
+	.tm_name			= "pm8901_tz",
+	.irq_name_temp_stat		= "pm8901_tempstat_irq",
+	.irq_name_over_temp		= "pm8901_overtemp_irq",
+};
+
+static struct mfd_cell thermal_alarm_cell = {
+	.name		= PM8XXX_TM_DEV_NAME,
+	.id		= 1,
+	.resources	= thermal_alarm_cell_resources,
+	.num_resources	= ARRAY_SIZE(thermal_alarm_cell_resources),
+	.platform_data	= &thermal_alarm_cdata,
+	.pdata_size	= sizeof(struct pm8xxx_tm_core_data),
+};
+
+static const struct resource mpp_cell_resources[] = {
+	{
+		.start	= PM8901_IRQ_BLOCK_BIT(PM8901_MPP_BLOCK_START, 0),
+		.end	= PM8901_IRQ_BLOCK_BIT(PM8901_MPP_BLOCK_START, 0)
+			  + PM8901_MPPS - 1,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct mfd_cell mpp_cell = {
+	.name		= PM8XXX_MPP_DEV_NAME,
+	.id		= 1,
+	.resources	= mpp_cell_resources,
+	.num_resources	= ARRAY_SIZE(mpp_cell_resources),
+};
+
+static int __devinit
+pm8901_add_subdevices(const struct pm8901_platform_data *pdata,
+				struct pm8901_chip *pmic)
+{
+	int rc = 0, irq_base = 0, i;
+	struct pm_irq_chip *irq_chip;
+	static struct mfd_cell *mfd_regulators;
+
+	if (pdata->irq_pdata) {
+		pdata->irq_pdata->irq_cdata.nirqs = PM8901_NR_IRQS;
+		pdata->irq_pdata->irq_cdata.base_addr = REG_IRQ_BASE;
+		irq_base = pdata->irq_pdata->irq_base;
+		irq_chip = pm8xxx_irq_init(pmic->dev, pdata->irq_pdata);
+
+		if (IS_ERR(irq_chip)) {
+			pr_err("Failed to init interrupts ret=%ld\n",
+					PTR_ERR(irq_chip));
+			return PTR_ERR(irq_chip);
+		}
+		pmic->irq_chip = irq_chip;
+	}
+
+	if (pdata->mpp_pdata) {
+		pdata->mpp_pdata->core_data.nmpps = PM8901_MPPS;
+		pdata->mpp_pdata->core_data.base_addr = REG_MPP_BASE;
+		mpp_cell.platform_data = pdata->mpp_pdata;
+		mpp_cell.pdata_size = sizeof(struct pm8xxx_mpp_platform_data);
+		rc = mfd_add_devices(pmic->dev, 0, &mpp_cell, 1, NULL,
+					irq_base);
+		if (rc) {
+			pr_err("Failed to add mpp subdevice ret=%d\n", rc);
+			goto bail;
+		}
+	}
+
+	if (pdata->num_regulators > 0 && pdata->regulator_pdatas) {
+		mfd_regulators = kzalloc(sizeof(struct mfd_cell)
+					 * (pdata->num_regulators), GFP_KERNEL);
+		if (!mfd_regulators) {
+			pr_err("Cannot allocate %d bytes for pm8901 regulator "
+				"mfd cells\n", sizeof(struct mfd_cell)
+						* (pdata->num_regulators));
+			rc = -ENOMEM;
+			goto bail;
+		}
+		for (i = 0; i < pdata->num_regulators; i++) {
+			mfd_regulators[i].name = "pm8901-regulator";
+			mfd_regulators[i].id = pdata->regulator_pdatas[i].id;
+			mfd_regulators[i].platform_data =
+				&(pdata->regulator_pdatas[i]);
+			mfd_regulators[i].pdata_size =
+					sizeof(struct pm8901_vreg_pdata);
+		}
+		rc = mfd_add_devices(pmic->dev, 0, mfd_regulators,
+				pdata->num_regulators, NULL, irq_base);
+		if (rc) {
+			pr_err("Failed to add regulator subdevices ret=%d\n",
+				rc);
+			kfree(mfd_regulators);
+			goto bail;
+		}
+		pmic->mfd_regulators = mfd_regulators;
+	}
+
+	rc = mfd_add_devices(pmic->dev, 0, &thermal_alarm_cell, 1, NULL,
+							irq_base);
+	if (rc) {
+		pr_err("Failed to add thermal alarm subdevice ret=%d\n",
+			rc);
+		goto bail;
+	}
+
+	rc = mfd_add_devices(pmic->dev, 0, &debugfs_cell, 1, NULL, irq_base);
+	if (rc) {
+		pr_err("Failed to add debugfs subdevice ret=%d\n", rc);
+		goto bail;
+	}
+
+	if (pdata->misc_pdata) {
+		misc_cell.platform_data = pdata->misc_pdata;
+		misc_cell.pdata_size = sizeof(struct pm8xxx_misc_platform_data);
+		rc = mfd_add_devices(pmic->dev, 0, &misc_cell, 1, NULL,
+				      irq_base);
+		if (rc) {
+			pr_err("Failed to add  misc subdevice ret=%d\n", rc);
+			goto bail;
+		}
+	}
+
+	return rc;
+
+bail:
+	if (pmic->irq_chip) {
+		pm8xxx_irq_exit(pmic->irq_chip);
+		pmic->irq_chip = NULL;
+	}
+	return rc;
+}
+
+static const char * const pm8901_rev_names[] = {
+	[PM8XXX_REVISION_8901_TEST]	= "test",
+	[PM8XXX_REVISION_8901_1p0]	= "1.0",
+	[PM8XXX_REVISION_8901_1p1]	= "1.1",
+	[PM8XXX_REVISION_8901_2p0]	= "2.0",
+	[PM8XXX_REVISION_8901_2p1]	= "2.1",
+	[PM8XXX_REVISION_8901_2p2]	= "2.2",
+	[PM8XXX_REVISION_8901_2p3]	= "2.3",
+};
+
+static int __devinit pm8901_probe(struct platform_device *pdev)
+{
+	int rc;
+	struct pm8901_platform_data *pdata = pdev->dev.platform_data;
+	const char *revision_name = "unknown";
+	struct pm8901_chip *pmic;
+	int revision;
+
+	if (pdata == NULL) {
+		pr_err("%s: No platform_data or IRQ.\n", __func__);
+		return -ENODEV;
+	}
+
+	pmic = kzalloc(sizeof *pmic, GFP_KERNEL);
+	if (pmic == NULL) {
+		pr_err("%s: kzalloc() failed.\n", __func__);
+		return -ENOMEM;
+	}
+
+	pmic->dev = &pdev->dev;
+
+	pm8901_drvdata.pm_chip_data = pmic;
+	platform_set_drvdata(pdev, &pm8901_drvdata);
+
+	/* Read PMIC chip revision */
+	rc = pm8901_readb(pmic->dev, PM8901_REG_REV, &pmic->revision);
+	if (rc)
+		pr_err("%s: Failed reading version register rc=%d.\n",
+			__func__, rc);
+
+	pr_info("%s: PMIC revision reg: %02X\n", __func__, pmic->revision);
+	revision =  pm8xxx_get_revision(pmic->dev);
+	if (revision >= 0 && revision < ARRAY_SIZE(pm8901_rev_names))
+		revision_name = pm8901_rev_names[revision];
+	pr_info("%s: PMIC version: PM8901 rev %s\n", __func__, revision_name);
+
+	(void) memcpy((void *)&pmic->pdata, (const void *)pdata,
+		      sizeof(pmic->pdata));
+
+	rc = pm8901_add_subdevices(pdata, pmic);
+	if (rc) {
+		pr_err("Cannot add subdevices rc=%d\n", rc);
+		goto err;
+	}
+
+	return 0;
+
+err:
+	platform_set_drvdata(pdev, NULL);
+	kfree(pmic);
+	return rc;
+}
+
+static int __devexit pm8901_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_drvdata *drvdata;
+	struct pm8901_chip *pmic = NULL;
+
+	drvdata = platform_get_drvdata(pdev);
+	if (drvdata)
+		pmic = drvdata->pm_chip_data;
+	if (pmic) {
+		if (pmic->dev)
+			mfd_remove_devices(pmic->dev);
+		if (pmic->irq_chip)
+			pm8xxx_irq_exit(pmic->irq_chip);
+		kfree(pmic->mfd_regulators);
+		kfree(pmic);
+	}
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver pm8901_driver = {
+	.probe		= pm8901_probe,
+	.remove		= __devexit_p(pm8901_remove),
+	.driver		= {
+		.name	= "pm8901-core",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init pm8901_init(void)
+{
+	return  platform_driver_register(&pm8901_driver);
+}
+postcore_initcall(pm8901_init);
+
+static void __exit pm8901_exit(void)
+{
+	platform_driver_unregister(&pm8901_driver);
+}
+module_exit(pm8901_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC8901 core driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pmic8901-core");
diff --git a/drivers/mfd/timpani-codec.c b/drivers/mfd/timpani-codec.c
new file mode 100644
index 0000000..1e0a839
--- /dev/null
+++ b/drivers/mfd/timpani-codec.c
@@ -0,0 +1,3661 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/msm-adie-codec.h>
+#include <linux/mfd/marimba.h>
+#include <linux/mfd/timpani-audio.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/string.h>
+
+/* Timpani codec driver is activated through Marimba core driver */
+
+#define MAX_MDELAY_US 20000
+
+#define TIMPANI_PATH_MASK(x) (1 << (x))
+
+#define TIMPANI_CODEC_AUXPGA_GAIN_RANGE (0x0F)
+
+#define TIMPANI_RX1_ST_MASK (TIMPANI_CDC_RX1_CTL_SIDETONE_EN1_L_M |\
+		TIMPANI_CDC_RX1_CTL_SIDETONE_EN1_R_M)
+#define TIMPANI_RX1_ST_ENABLE ((1 << TIMPANI_CDC_RX1_CTL_SIDETONE_EN1_L_S) |\
+		(1 << TIMPANI_CDC_RX1_CTL_SIDETONE_EN1_R_S))
+#define TIMPANI_CDC_ST_MIXING_TX1_MASK (TIMPANI_CDC_ST_MIXING_TX1_L_M |\
+		TIMPANI_CDC_ST_MIXING_TX1_R_M)
+#define TIMPANI_CDC_ST_MIXING_TX1_ENABLE ((1 << TIMPANI_CDC_ST_MIXING_TX1_L_S)\
+		| (1 << TIMPANI_CDC_ST_MIXING_TX1_R_S))
+#define TIMPANI_CDC_ST_MIXING_TX2_MASK (TIMPANI_CDC_ST_MIXING_TX2_L_M |\
+		TIMPANI_CDC_ST_MIXING_TX2_R_M)
+#define TIMPANI_CDC_ST_MIXING_TX2_ENABLE ((1 << TIMPANI_CDC_ST_MIXING_TX2_L_S)\
+		| (1 << TIMPANI_CDC_ST_MIXING_TX2_R_S))
+
+enum refcnt {
+	DEC = 0,
+	INC = 1,
+	IGNORE = 2,
+};
+#define TIMPANI_ARRAY_SIZE	(TIMPANI_A_CDC_COMP_HALT + 1)
+#define MAX_SHADOW_RIGISTERS	TIMPANI_A_CDC_COMP_HALT
+
+static u8 timpani_shadow[TIMPANI_ARRAY_SIZE];
+
+struct adie_codec_path {
+	struct adie_codec_dev_profile *profile;
+	struct adie_codec_register_image img;
+	u32 hwsetting_idx;
+	u32 stage_idx;
+	u32 curr_stage;
+	u32 reg_owner;
+};
+
+enum /* regaccess blk id */
+{
+	RA_BLOCK_RX1 = 0,
+	RA_BLOCK_RX2,
+	RA_BLOCK_TX1,
+	RA_BLOCK_TX2,
+	RA_BLOCK_LB,
+	RA_BLOCK_SHARED_RX_LB,
+	RA_BLOCK_SHARED_TX,
+	RA_BLOCK_TXFE1,
+	RA_BLOCK_TXFE2,
+	RA_BLOCK_PA_COMMON,
+	RA_BLOCK_PA_EAR,
+	RA_BLOCK_PA_HPH,
+	RA_BLOCK_PA_LINE,
+	RA_BLOCK_PA_AUX,
+	RA_BLOCK_ADC,
+	RA_BLOCK_DMIC,
+	RA_BLOCK_TX_I2S,
+	RA_BLOCK_DRV,
+	RA_BLOCK_TEST,
+	RA_BLOCK_RESERVED,
+	RA_BLOCK_NUM,
+};
+
+enum /* regaccess onwer ID */
+{
+	RA_OWNER_NONE = 0,
+	RA_OWNER_PATH_RX1,
+	RA_OWNER_PATH_RX2,
+	RA_OWNER_PATH_TX1,
+	RA_OWNER_PATH_TX2,
+	RA_OWNER_PATH_LB,
+	RA_OWNER_DRV,
+	RA_OWNER_NUM,
+};
+
+struct reg_acc_blk_cfg {
+	u8 valid_owners[RA_OWNER_NUM];
+};
+
+struct reg_ref_cnt {
+	u8 mask;
+	u8 path_mask;
+};
+
+#define TIMPANI_MAX_FIELDS	5
+
+struct timpani_regaccess {
+	u8 reg_addr;
+	u8 blk_mask[RA_BLOCK_NUM];
+	u8 reg_mask;
+	u8 reg_default;
+	struct reg_ref_cnt fld_ref_cnt[TIMPANI_MAX_FIELDS];
+};
+
+struct timpani_regaccess timpani_regset[] = {
+	{
+		TIMPANI_A_MREF,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFC, 0x0, 0x3},
+		TIMPANI_MREF_M,
+		TIMPANI_MREF_POR,
+		{
+			{ .mask = 0xFC, .path_mask = 0},
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDAC_IDAC_REF_CUR,
+		{0xFC, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDAC_IDAC_REF_CUR_M,
+		TIMPANI_CDAC_IDAC_REF_CUR_POR,
+		{
+			{ .mask = 0xFC, .path_mask = 0},
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXADC12_REF_CURR,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF},
+		TIMPANI_TXADC12_REF_CURR_M,
+		TIMPANI_TXADC12_REF_CURR_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXADC3_EN,
+		{ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFE, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
+		TIMPANI_TXADC3_EN_M,
+		TIMPANI_TXADC3_EN_POR,
+		{
+			{ .mask = 0xFE, .path_mask = 0},
+			{ .mask = 0x01, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXADC4_EN,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFE, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
+		TIMPANI_TXADC4_EN_M,
+		TIMPANI_TXADC4_EN_POR,
+		{
+			{ .mask = 0xFE, .path_mask = 0},
+			{ .mask = 0x01, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CODEC_TXADC_STATUS_REGISTER_1,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC0, 0x30, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF},
+		TIMPANI_CODEC_TXADC_STATUS_REGISTER_1_M,
+		TIMPANI_CODEC_TXADC_STATUS_REGISTER_1_POR,
+		{
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x30, .path_mask = 0},
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXFE1,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_TXFE1_M,
+		TIMPANI_TXFE1_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXFE2,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_TXFE2_M,
+		TIMPANI_TXFE2_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXFE12_ATEST,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_TXFE12_ATEST_M,
+		TIMPANI_TXFE12_ATEST_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXFE_CLT,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF8, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7},
+		TIMPANI_TXFE_CLT_M,
+		TIMPANI_TXFE_CLT_POR,
+		{
+			{ .mask = 0xF8, .path_mask = 0},
+			{ .mask = 0x07, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXADC1_EN,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFE, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
+		TIMPANI_TXADC1_EN_M,
+		TIMPANI_TXADC1_EN_POR,
+		{
+			{ .mask = 0xFE, .path_mask = 0},
+			{ .mask = 0x01, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXADC2_EN,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFE, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
+		TIMPANI_TXADC2_EN_M,
+		TIMPANI_TXADC2_EN_POR,
+		{
+			{ .mask = 0xFE, .path_mask = 0},
+			{ .mask = 0x01, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXADC_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_TXADC_CTL_M,
+		TIMPANI_TXADC_CTL_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXADC_CTL2,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_TXADC_CTL2_M,
+		TIMPANI_TXADC_CTL2_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXADC_CTL3,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0xFE, 0x0, 0x0, 0x0, 0x0, 0x1},
+		TIMPANI_TXADC_CTL3_M,
+		TIMPANI_TXADC_CTL3_POR,
+		{
+			{ .mask = 0xFE, .path_mask = 0},
+			{ .mask = 0x01, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXADC_CHOP_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0xFC, 0x0, 0x0, 0x0, 0x0, 0x3},
+		TIMPANI_TXADC_CHOP_CTL_M,
+		TIMPANI_TXADC_CHOP_CTL_POR,
+		{
+			{ .mask = 0xFC, .path_mask = 0},
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXFE3,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xE2, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1D},
+		TIMPANI_TXFE3_M,
+		TIMPANI_TXFE3_POR,
+		{
+			{ .mask = 0xE2, .path_mask = 0},
+			{ .mask = 0x1D, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXFE4,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xE2, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1D},
+		TIMPANI_TXFE4_M,
+		TIMPANI_TXFE4_POR,
+		{
+			{ .mask = 0xE2, .path_mask = 0},
+			{ .mask = 0x1D, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXFE3_ATEST,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_TXFE3_ATEST_M,
+		TIMPANI_TXFE3_ATEST_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXFE_DIFF_SE,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0xC, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF0},
+		TIMPANI_TXFE_DIFF_SE_M,
+		TIMPANI_TXFE_DIFF_SE_POR,
+		{
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0x0C, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDAC_RX_CLK_CTL,
+		{0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDAC_RX_CLK_CTL_M,
+		TIMPANI_CDAC_RX_CLK_CTL_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDAC_BUFF_CTL,
+		{0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDAC_BUFF_CTL_M,
+		TIMPANI_CDAC_BUFF_CTL_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDAC_REF_CTL1,
+		{0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDAC_REF_CTL1_M,
+		TIMPANI_CDAC_REF_CTL1_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_IDAC_DWA_FIR_CTL,
+		{0xF8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7},
+		TIMPANI_IDAC_DWA_FIR_CTL_M,
+		TIMPANI_IDAC_DWA_FIR_CTL_POR,
+		{
+			{ .mask = 0xF8, .path_mask = 0},
+			{ .mask = 0x07, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDAC_REF_CTL2,
+		{0x6F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x90},
+		TIMPANI_CDAC_REF_CTL2_M,
+		TIMPANI_CDAC_REF_CTL2_POR,
+		{
+			{ .mask = 0x6F, .path_mask = 0},
+			{ .mask = 0x90, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDAC_CTL1,
+		{0x7F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80},
+		TIMPANI_CDAC_CTL1_M,
+		TIMPANI_CDAC_CTL1_POR,
+		{
+			{ .mask = 0x7F, .path_mask = 0},
+			{ .mask = 0x80, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDAC_CTL2,
+		{0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDAC_CTL2_M,
+		TIMPANI_CDAC_CTL2_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_IDAC_L_CTL,
+		{0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_IDAC_L_CTL_M,
+		TIMPANI_IDAC_L_CTL_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_IDAC_R_CTL,
+		{0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_IDAC_R_CTL_M,
+		TIMPANI_IDAC_R_CTL_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_MASTER_BIAS,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1F,
+		0xE0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_PA_MASTER_BIAS_M,
+		TIMPANI_PA_MASTER_BIAS_POR,
+		{
+			{ .mask = 0x1F, .path_mask = 0},
+			{ .mask = 0xE0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_CLASSD_BIAS,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_PA_CLASSD_BIAS_M,
+		TIMPANI_PA_CLASSD_BIAS_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_AUXPGA_CUR,
+		{0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_AUXPGA_CUR_M,
+		TIMPANI_AUXPGA_CUR_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_AUXPGA_CM,
+		{0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_AUXPGA_CM_M,
+		TIMPANI_AUXPGA_CM_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_HPH_EARPA_MSTB_EN,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0xFC,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_PA_HPH_EARPA_MSTB_EN_M,
+		TIMPANI_PA_HPH_EARPA_MSTB_EN_POR,
+		{
+			{ .mask = 0x01, .path_mask = 0},
+			{ .mask = 0x02, .path_mask = 0},
+			{ .mask = 0xFC, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_LINE_AUXO_EN,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0xF8, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_PA_LINE_AUXO_EN_M,
+		TIMPANI_PA_LINE_AUXO_EN_POR,
+		{
+			{ .mask = 0xF8, .path_mask = 0},
+			{ .mask = 0x07, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_CLASSD_AUXPGA_EN,
+		{0x0, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC0},
+		TIMPANI_PA_CLASSD_AUXPGA_EN_M,
+		TIMPANI_PA_CLASSD_AUXPGA_EN_POR,
+		{
+			{ .mask = 0x30, .path_mask = 0},
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_LINE_L_GAIN,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFC, 0x0, 0x3},
+		TIMPANI_PA_LINE_L_GAIN_M,
+		TIMPANI_PA_LINE_L_GAIN_POR,
+		{
+			{ .mask = 0xFC, .path_mask = 0},
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_LINE_R_GAIN,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFC, 0x0, 0x3},
+		TIMPANI_PA_LINE_R_GAIN_M,
+		TIMPANI_PA_LINE_R_GAIN_POR,
+		{
+			{ .mask = 0xFC, .path_mask = 0},
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_HPH_L_GAIN,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFE, 0x0, 0x1},
+		TIMPANI_PA_HPH_L_GAIN_M,
+		TIMPANI_PA_HPH_L_GAIN_POR,
+		{
+			{ .mask = 0xFE, .path_mask = 0},
+			{ .mask = 0x01, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_HPH_R_GAIN,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFE, 0x0, 0x1},
+		TIMPANI_PA_HPH_R_GAIN_M,
+		TIMPANI_PA_HPH_R_GAIN_POR,
+		{
+			{ .mask = 0xFE, .path_mask = 0},
+			{ .mask = 0x01, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_AUXPGA_LR_GAIN,
+		{0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_AUXPGA_LR_GAIN_M,
+		TIMPANI_AUXPGA_LR_GAIN_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_AUXO_EARPA_CONN,
+		{0x21, 0x42, 0x0, 0x0, 0x84, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18},
+		TIMPANI_PA_AUXO_EARPA_CONN_M,
+		TIMPANI_PA_AUXO_EARPA_CONN_POR,
+		{
+			{ .mask = 0x21, .path_mask = 0},
+			{ .mask = 0x42, .path_mask = 0},
+			{ .mask = 0x84, .path_mask = 0},
+			{ .mask = 0x18, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_LINE_ST_CONN,
+		{0x24, 0x48, 0x0, 0x0, 0x93, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_PA_LINE_ST_CONN_M,
+		TIMPANI_PA_LINE_ST_CONN_POR,
+		{
+			{ .mask = 0x24, .path_mask = 0},
+			{ .mask = 0x48, .path_mask = 0},
+			{ .mask = 0x93, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_LINE_MONO_CONN,
+		{0x24, 0x48, 0x0, 0x0, 0x93, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_PA_LINE_MONO_CONN_M,
+		TIMPANI_PA_LINE_MONO_CONN_POR,
+		{
+			{ .mask = 0x24, .path_mask = 0},
+			{ .mask = 0x48, .path_mask = 0},
+			{ .mask = 0x93, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_HPH_ST_CONN,
+		{0x24, 0x48, 0x0, 0x0, 0x90, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_PA_HPH_ST_CONN_M,
+		TIMPANI_PA_HPH_ST_CONN_POR,
+		{
+			{ .mask = 0x24, .path_mask = 0},
+			{ .mask = 0x48, .path_mask = 0},
+			{ .mask = 0x90, .path_mask = 0},
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_HPH_MONO_CONN,
+		{0x24, 0x48, 0x0, 0x0, 0x90, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3},
+		TIMPANI_PA_HPH_MONO_CONN_M,
+		TIMPANI_PA_HPH_MONO_CONN_POR,
+		{
+			{ .mask = 0x24, .path_mask = 0},
+			{ .mask = 0x48, .path_mask = 0},
+			{ .mask = 0x90, .path_mask = 0},
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_CLASSD_CONN,
+		{0x80, 0x40, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF},
+		TIMPANI_PA_CLASSD_CONN_M,
+		TIMPANI_PA_CLASSD_CONN_POR,
+		{
+			{ .mask = 0x80, .path_mask = 0},
+			{ .mask = 0x40, .path_mask = 0},
+			{ .mask = 0x20, .path_mask = 0},
+			{ .mask = 0x10, .path_mask = 0},
+			{ .mask = 0x0F, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_CNP_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xCF,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x30},
+		TIMPANI_PA_CNP_CTL_M,
+		TIMPANI_PA_CNP_CTL_POR,
+		{
+			{ .mask = 0xCF, .path_mask = 0},
+			{ .mask = 0x30, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_CLASSD_L_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3F,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC0},
+		TIMPANI_PA_CLASSD_L_CTL_M,
+		TIMPANI_PA_CLASSD_L_CTL_POR,
+		{
+			{ .mask = 0x3F, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_CLASSD_R_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3F,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC0},
+		TIMPANI_PA_CLASSD_R_CTL_M,
+		TIMPANI_PA_CLASSD_R_CTL_POR,
+		{
+			{ .mask = 0x3F, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_CLASSD_INT2_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_PA_CLASSD_INT2_CTL_M,
+		TIMPANI_PA_CLASSD_INT2_CTL_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_HPH_L_OCP_CLK_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_PA_HPH_L_OCP_CLK_CTL_M,
+		TIMPANI_PA_HPH_L_OCP_CLK_CTL_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_CLASSD_L_SW_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF7,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8},
+		TIMPANI_PA_CLASSD_L_SW_CTL_M,
+		TIMPANI_PA_CLASSD_L_SW_CTL_POR,
+		{
+			{ .mask = 0xF7, .path_mask = 0},
+			{ .mask = 0x08, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_CLASSD_L_OCP1,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_PA_CLASSD_L_OCP1_M,
+		TIMPANI_PA_CLASSD_L_OCP1_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_CLASSD_L_OCP2,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_PA_CLASSD_L_OCP2_M,
+		TIMPANI_PA_CLASSD_L_OCP2_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_HPH_R_OCP_CLK_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_PA_HPH_R_OCP_CLK_CTL_M,
+		TIMPANI_PA_HPH_R_OCP_CLK_CTL_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_CLASSD_R_SW_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF7,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8},
+		TIMPANI_PA_CLASSD_R_SW_CTL_M,
+		TIMPANI_PA_CLASSD_R_SW_CTL_POR,
+		{
+			{ .mask = 0xF7, .path_mask = 0},
+			{ .mask = 0x08, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_CLASSD_R_OCP1,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_PA_CLASSD_R_OCP1_M,
+		TIMPANI_PA_CLASSD_R_OCP1_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_CLASSD_R_OCP2,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_PA_CLASSD_R_OCP2_M,
+		TIMPANI_PA_CLASSD_R_OCP2_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_HPH_CTL1,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF},
+		TIMPANI_PA_HPH_CTL1_M,
+		TIMPANI_PA_HPH_CTL1_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_HPH_CTL2,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFE,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
+		TIMPANI_PA_HPH_CTL2_M,
+		TIMPANI_PA_HPH_CTL2_POR,
+		{
+			{ .mask = 0xFE, .path_mask = 0},
+			{ .mask = 0x01, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_LINE_AUXO_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0xC3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x3C, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_PA_LINE_AUXO_CTL_M,
+		TIMPANI_PA_LINE_AUXO_CTL_POR,
+		{
+			{ .mask = 0xC3, .path_mask = 0},
+			{ .mask = 0x3C, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_AUXO_EARPA_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x0,
+		0x0, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC0},
+		TIMPANI_PA_AUXO_EARPA_CTL_M,
+		TIMPANI_PA_AUXO_EARPA_CTL_POR,
+		{
+			{ .mask = 0x07, .path_mask = 0},
+			{ .mask = 0x38, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_EARO_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_PA_EARO_CTL_M,
+		TIMPANI_PA_EARO_CTL_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_MASTER_BIAS_CUR,
+		{0x0, 0x0, 0x0, 0x0, 0x60, 0x0, 0x0, 0x0, 0x0, 0x80, 0x0, 0x18,
+		0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
+		TIMPANI_PA_MASTER_BIAS_CUR_M,
+		TIMPANI_PA_MASTER_BIAS_CUR_POR,
+		{
+			{ .mask = 0x60, .path_mask = 0},
+			{ .mask = 0x80, .path_mask = 0},
+			{ .mask = 0x18, .path_mask = 0},
+			{ .mask = 0x06, .path_mask = 0},
+			{ .mask = 0x01, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_CLASSD_SC_STATUS,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xCC,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x33},
+		TIMPANI_PA_CLASSD_SC_STATUS_M,
+		TIMPANI_PA_CLASSD_SC_STATUS_POR,
+		{
+			{ .mask = 0xCC, .path_mask = 0},
+			{ .mask = 0x33, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_HPH_SC_STATUS,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x88,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x77},
+		TIMPANI_PA_HPH_SC_STATUS_M,
+		TIMPANI_PA_HPH_SC_STATUS_POR,
+		{
+			{ .mask = 0x88, .path_mask = 0},
+			{ .mask = 0x77, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_ATEST_EN,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x7F},
+		TIMPANI_ATEST_EN_M,
+		TIMPANI_ATEST_EN_POR,
+		{
+			{ .mask = 0x80, .path_mask = 0},
+			{ .mask = 0x7F, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_ATEST_TSHKADC,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0xF0},
+		TIMPANI_ATEST_TSHKADC_M,
+		TIMPANI_ATEST_TSHKADC_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_ATEST_TXADC13,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7F, 0x80},
+		TIMPANI_ATEST_TXADC13_M,
+		TIMPANI_ATEST_TXADC13_POR,
+		{
+			{ .mask = 0x7F, .path_mask = 0},
+			{ .mask = 0x80, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_ATEST_TXADC24,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7F, 0x80},
+		TIMPANI_ATEST_TXADC24_M,
+		TIMPANI_ATEST_TXADC24_POR,
+		{
+			{ .mask = 0x7F, .path_mask = 0},
+			{ .mask = 0x80, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_ATEST_AUXPGA,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF8, 0x7},
+		TIMPANI_ATEST_AUXPGA_M,
+		TIMPANI_ATEST_AUXPGA_POR,
+		{
+			{ .mask = 0xF8, .path_mask = 0},
+			{ .mask = 0x07, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_ATEST_CDAC,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0},
+		TIMPANI_ATEST_CDAC_M,
+		TIMPANI_ATEST_CDAC_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_ATEST_IDAC,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0},
+		TIMPANI_ATEST_IDAC_M,
+		TIMPANI_ATEST_IDAC_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_ATEST_PA1,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0},
+		TIMPANI_ATEST_PA1_M,
+		TIMPANI_ATEST_PA1_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_ATEST_CLASSD,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0},
+		TIMPANI_ATEST_CLASSD_M,
+		TIMPANI_ATEST_CLASSD_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_ATEST_LINEO_AUXO,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0},
+		TIMPANI_ATEST_LINEO_AUXO_M,
+		TIMPANI_ATEST_LINEO_AUXO_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_RESET_CTL,
+		{0x2, 0x8, 0x5, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC0},
+		TIMPANI_CDC_RESET_CTL_M,
+		TIMPANI_CDC_RESET_CTL_POR,
+		{
+			{ .mask = 0x02, .path_mask = 0},
+			{ .mask = 0x08, .path_mask = 0},
+			{ .mask = 0x05, .path_mask = 0},
+			{ .mask = 0x30, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_RX1_CTL,
+		{0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_RX1_CTL_M,
+		TIMPANI_CDC_RX1_CTL_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_TX_I2S_CTL,
+		{0x0, 0x0, 0x10, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0x0, 0xC0},
+		TIMPANI_CDC_TX_I2S_CTL_M,
+		TIMPANI_CDC_TX_I2S_CTL_POR,
+		{
+			{ .mask = 0x10, .path_mask = 0},
+			{ .mask = 0x20, .path_mask = 0},
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_CH_CTL,
+		{0x3, 0x30, 0xC, 0xC0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_CH_CTL_M,
+		TIMPANI_CDC_CH_CTL_POR,
+		{
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0x30, .path_mask = 0},
+			{ .mask = 0x0C, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_RX1LG,
+		{0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_RX1LG_M,
+		TIMPANI_CDC_RX1LG_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_RX1RG,
+		{0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_RX1RG_M,
+		TIMPANI_CDC_RX1RG_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_TX1LG,
+		{0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_TX1LG_M,
+		TIMPANI_CDC_TX1LG_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_TX1RG,
+		{0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_TX1RG_M,
+		TIMPANI_CDC_TX1RG_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_RX_PGA_TIMER,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_RX_PGA_TIMER_M,
+		TIMPANI_CDC_RX_PGA_TIMER_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_TX_PGA_TIMER,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_TX_PGA_TIMER_M,
+		TIMPANI_CDC_TX_PGA_TIMER_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_GCTL1,
+		{0xF, 0x0, 0xF0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_GCTL1_M,
+		TIMPANI_CDC_GCTL1_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_TX1L_STG,
+		{0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_TX1L_STG_M,
+		TIMPANI_CDC_TX1L_STG_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ST_CTL,
+		{0x0, 0xF, 0x0, 0xF0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_ST_CTL_M,
+		TIMPANI_CDC_ST_CTL_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_RX1L_DCOFFSET,
+		{0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_RX1L_DCOFFSET_M,
+		TIMPANI_CDC_RX1L_DCOFFSET_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_RX1R_DCOFFSET,
+		{0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_RX1R_DCOFFSET_M,
+		TIMPANI_CDC_RX1R_DCOFFSET_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_BYPASS_CTL1,
+		{0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF0},
+		TIMPANI_CDC_BYPASS_CTL1_M,
+		TIMPANI_CDC_BYPASS_CTL1_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_PDM_CONFIG,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0xF0},
+		TIMPANI_CDC_PDM_CONFIG_M,
+		TIMPANI_CDC_PDM_CONFIG_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_TESTMODE1,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3F, 0xC0},
+		TIMPANI_CDC_TESTMODE1_M,
+		TIMPANI_CDC_TESTMODE1_POR,
+		{
+			{ .mask = 0x3F, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_DMIC_CLK_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x3F, 0x0, 0x0, 0x0, 0xC0},
+		TIMPANI_CDC_DMIC_CLK_CTL_M,
+		TIMPANI_CDC_DMIC_CLK_CTL_POR,
+		{
+			{ .mask = 0x3F, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ADC12_CLK_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_ADC12_CLK_CTL_M,
+		TIMPANI_CDC_ADC12_CLK_CTL_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_TX1_CTL,
+		{0x0, 0x0, 0x3F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC0},
+		TIMPANI_CDC_TX1_CTL_M,
+		TIMPANI_CDC_TX1_CTL_POR,
+		{
+			{ .mask = 0x3F, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ADC34_CLK_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_ADC34_CLK_CTL_M,
+		TIMPANI_CDC_ADC34_CLK_CTL_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_TX2_CTL,
+		{0x0, 0x0, 0x0, 0x3F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC0},
+		TIMPANI_CDC_TX2_CTL_M,
+		TIMPANI_CDC_TX2_CTL_POR,
+		{
+			{ .mask = 0x3F, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_RX1_CLK_CTL,
+		{0x1F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xE0},
+		TIMPANI_CDC_RX1_CLK_CTL_M,
+		TIMPANI_CDC_RX1_CLK_CTL_POR,
+		{
+			{ .mask = 0x1F, .path_mask = 0},
+			{ .mask = 0xE0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_RX2_CLK_CTL,
+		{0x1F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xE0},
+		TIMPANI_CDC_RX2_CLK_CTL_M,
+		TIMPANI_CDC_RX2_CLK_CTL_POR,
+		{
+			{ .mask = 0x1F, .path_mask = 0},
+			{ .mask = 0xE0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_DEC_ADC_SEL,
+		{0x0, 0x0, 0xF, 0xF0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_DEC_ADC_SEL_M,
+		TIMPANI_CDC_DEC_ADC_SEL_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC_INPUT_MUX,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x3F, 0x0, 0xC0},
+		TIMPANI_CDC_ANC_INPUT_MUX_M,
+		TIMPANI_CDC_ANC_INPUT_MUX_POR,
+		{
+			{ .mask = 0x3F, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC_RX_CLK_NS_SEL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0xFE},
+		TIMPANI_CDC_ANC_RX_CLK_NS_SEL_M,
+		TIMPANI_CDC_ANC_RX_CLK_NS_SEL_POR,
+		{
+			{ .mask = 0x01, .path_mask = 0},
+			{ .mask = 0xFE, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC_FB_TUNE_SEL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF},
+		TIMPANI_CDC_ANC_FB_TUNE_SEL_M,
+		TIMPANI_CDC_ANC_FB_TUNE_SEL_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CLK_DIV_SYNC_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0xFC},
+		TIMPANI_CLK_DIV_SYNC_CTL_M,
+		TIMPANI_CLK_DIV_SYNC_CTL_POR,
+		{
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0xFC, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ADC_CLK_EN,
+		{0x0, 0x0, 0x3, 0xC, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF0},
+		TIMPANI_CDC_ADC_CLK_EN_M,
+		TIMPANI_CDC_ADC_CLK_EN_POR,
+		{
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0x0C, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ST_MIXING,
+		{0x0, 0x0, 0x3, 0xC, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF0},
+		TIMPANI_CDC_ST_MIXING_M,
+		TIMPANI_CDC_ST_MIXING_POR,
+		{
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0x0C, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_RX2_CTL,
+		{0x0, 0x7F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80},
+		TIMPANI_CDC_RX2_CTL_M,
+		TIMPANI_CDC_RX2_CTL_POR,
+		{
+			{ .mask = 0x7F, .path_mask = 0},
+			{ .mask = 0x80, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ARB_CLK_EN,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF},
+		TIMPANI_CDC_ARB_CLK_EN_M,
+		TIMPANI_CDC_ARB_CLK_EN_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_I2S_CTL2,
+		{0x2, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x39, 0x0, 0x0, 0xC0},
+		TIMPANI_CDC_I2S_CTL2_M,
+		TIMPANI_CDC_I2S_CTL2_POR,
+		{
+			{ .mask = 0x02, .path_mask = 0},
+			{ .mask = 0x04, .path_mask = 0},
+			{ .mask = 0x39, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_RX2LG,
+		{0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_RX2LG_M,
+		TIMPANI_CDC_RX2LG_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_RX2RG,
+		{0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_RX2RG_M,
+		TIMPANI_CDC_RX2RG_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_TX2LG,
+		{0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_TX2LG_M,
+		TIMPANI_CDC_TX2LG_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_TX2RG,
+		{0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_TX2RG_M,
+		TIMPANI_CDC_TX2RG_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_DMIC_MUX,
+		{0x0, 0x0, 0xF, 0xF0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_DMIC_MUX_M,
+		TIMPANI_CDC_DMIC_MUX_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ARB_CLK_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0xFC},
+		TIMPANI_CDC_ARB_CLK_CTL_M,
+		TIMPANI_CDC_ARB_CLK_CTL_POR,
+		{
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0xFC, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_GCTL2,
+		{0x0, 0xF, 0x0, 0xF0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_GCTL2_M,
+		TIMPANI_CDC_GCTL2_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_BYPASS_CTL2,
+		{0x0, 0x0, 0x3F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC0},
+		TIMPANI_CDC_BYPASS_CTL2_M,
+		TIMPANI_CDC_BYPASS_CTL2_POR,
+		{
+			{ .mask = 0x3F, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_BYPASS_CTL3,
+		{0x0, 0x0, 0x0, 0x3F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC0},
+		TIMPANI_CDC_BYPASS_CTL3_M,
+		TIMPANI_CDC_BYPASS_CTL3_POR,
+		{
+			{ .mask = 0x3F, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_BYPASS_CTL4,
+		{0x0, 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF0},
+		TIMPANI_CDC_BYPASS_CTL4_M,
+		TIMPANI_CDC_BYPASS_CTL4_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_RX2L_DCOFFSET,
+		{0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_RX2L_DCOFFSET_M,
+		TIMPANI_CDC_RX2L_DCOFFSET_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_RX2R_DCOFFSET,
+		{0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_RX2R_DCOFFSET_M,
+		TIMPANI_CDC_RX2R_DCOFFSET_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_RX_MIX_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0xFC},
+		TIMPANI_CDC_RX_MIX_CTL_M,
+		TIMPANI_CDC_RX_MIX_CTL_POR,
+		{
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0xFC, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_SPARE_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xFE},
+		TIMPANI_CDC_SPARE_CTL_M,
+		TIMPANI_CDC_SPARE_CTL_POR,
+		{
+			{ .mask = 0x01, .path_mask = 0},
+			{ .mask = 0xFE, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_TESTMODE2,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1F, 0xE0},
+		TIMPANI_CDC_TESTMODE2_M,
+		TIMPANI_CDC_TESTMODE2_POR,
+		{
+			{ .mask = 0x1F, .path_mask = 0},
+			{ .mask = 0xE0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_PDM_OE,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0},
+		TIMPANI_CDC_PDM_OE_M,
+		TIMPANI_CDC_PDM_OE_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_TX1R_STG,
+		{0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_TX1R_STG_M,
+		TIMPANI_CDC_TX1R_STG_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_TX2L_STG,
+		{0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_TX2L_STG_M,
+		TIMPANI_CDC_TX2L_STG_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_TX2R_STG,
+		{0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_TX2R_STG_M,
+		TIMPANI_CDC_TX2R_STG_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ARB_BYPASS_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF},
+		TIMPANI_CDC_ARB_BYPASS_CTL_M,
+		TIMPANI_CDC_ARB_BYPASS_CTL_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC1_CTL1,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x1F, 0x0, 0xE0},
+		TIMPANI_CDC_ANC1_CTL1_M,
+		TIMPANI_CDC_ANC1_CTL1_POR,
+		{
+			{ .mask = 0x1F, .path_mask = 0},
+			{ .mask = 0xE0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC1_CTL2,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x3F, 0x0, 0xC0},
+		TIMPANI_CDC_ANC1_CTL2_M,
+		TIMPANI_CDC_ANC1_CTL2_POR,
+		{
+			{ .mask = 0x3F, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC1_FF_FB_SHIFT,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+		TIMPANI_CDC_ANC1_FF_FB_SHIFT_M,
+		TIMPANI_CDC_ANC1_FF_FB_SHIFT_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC1_RX_NS,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x0, 0xF8},
+		TIMPANI_CDC_ANC1_RX_NS_M,
+		TIMPANI_CDC_ANC1_RX_NS_POR,
+		{
+			{ .mask = 0x07, .path_mask = 0},
+			{ .mask = 0xF8, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC1_SPARE,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+		TIMPANI_CDC_ANC1_SPARE_M,
+		TIMPANI_CDC_ANC1_SPARE_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC1_IIR_COEFF_PTR,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x1F, 0x0, 0xE0},
+		TIMPANI_CDC_ANC1_IIR_COEFF_PTR_M,
+		TIMPANI_CDC_ANC1_IIR_COEFF_PTR_POR,
+		{
+			{ .mask = 0x1F, .path_mask = 0},
+			{ .mask = 0xE0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC1_IIR_COEFF_MSB,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0xFE},
+		TIMPANI_CDC_ANC1_IIR_COEFF_MSB_M,
+		TIMPANI_CDC_ANC1_IIR_COEFF_MSB_POR,
+		{
+			{ .mask = 0x01, .path_mask = 0},
+			{ .mask = 0xFE, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC1_IIR_COEFF_LSB,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+		TIMPANI_CDC_ANC1_IIR_COEFF_LSB_M,
+		TIMPANI_CDC_ANC1_IIR_COEFF_LSB_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC1_IIR_COEFF_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0xFC},
+		TIMPANI_CDC_ANC1_IIR_COEFF_CTL_M,
+		TIMPANI_CDC_ANC1_IIR_COEFF_CTL_POR,
+		{
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0xFC, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC1_LPF_COEFF_PTR,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF0},
+		TIMPANI_CDC_ANC1_LPF_COEFF_PTR_M,
+		TIMPANI_CDC_ANC1_LPF_COEFF_PTR_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC1_LPF_COEFF_MSB,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF0},
+		TIMPANI_CDC_ANC1_LPF_COEFF_MSB_M,
+		TIMPANI_CDC_ANC1_LPF_COEFF_MSB_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC1_LPF_COEFF_LSB,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+		TIMPANI_CDC_ANC1_LPF_COEFF_LSB_M,
+		TIMPANI_CDC_ANC1_LPF_COEFF_LSB_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC1_SCALE_PTR,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+		TIMPANI_CDC_ANC1_SCALE_PTR_M,
+		TIMPANI_CDC_ANC1_SCALE_PTR_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC1_SCALE,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+		TIMPANI_CDC_ANC1_SCALE_M,
+		TIMPANI_CDC_ANC1_SCALE_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC1_DEBUG,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF0},
+		TIMPANI_CDC_ANC1_DEBUG_M,
+		TIMPANI_CDC_ANC1_DEBUG_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC2_CTL1,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x1F, 0x0, 0xE0},
+		TIMPANI_CDC_ANC2_CTL1_M,
+		TIMPANI_CDC_ANC2_CTL1_POR,
+		{
+			{ .mask = 0x1F, .path_mask = 0},
+			{ .mask = 0xE0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC2_CTL2,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x3F, 0x0, 0xC0},
+		TIMPANI_CDC_ANC2_CTL2_M,
+		TIMPANI_CDC_ANC2_CTL2_POR,
+		{
+			{ .mask = 0x3F, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC2_FF_FB_SHIFT,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+		TIMPANI_CDC_ANC2_FF_FB_SHIFT_M,
+		TIMPANI_CDC_ANC2_FF_FB_SHIFT_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC2_RX_NS,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x0, 0xF8},
+		TIMPANI_CDC_ANC2_RX_NS_M,
+		TIMPANI_CDC_ANC2_RX_NS_POR,
+		{
+			{ .mask = 0x07, .path_mask = 0},
+			{ .mask = 0xF8, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC2_SPARE,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+		TIMPANI_CDC_ANC2_SPARE_M,
+		TIMPANI_CDC_ANC2_SPARE_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC2_IIR_COEFF_PTR,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF0},
+		TIMPANI_CDC_ANC2_IIR_COEFF_PTR_M,
+		TIMPANI_CDC_ANC2_IIR_COEFF_PTR_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC2_IIR_COEFF_MSB,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0xFE},
+		TIMPANI_CDC_ANC2_IIR_COEFF_MSB_M,
+		TIMPANI_CDC_ANC2_IIR_COEFF_MSB_POR,
+		{
+			{ .mask = 0x01, .path_mask = 0},
+			{ .mask = 0xFE, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC2_IIR_COEFF_LSB,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+		TIMPANI_CDC_ANC2_IIR_COEFF_LSB_M,
+		TIMPANI_CDC_ANC2_IIR_COEFF_LSB_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC2_IIR_COEFF_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0xFC},
+		TIMPANI_CDC_ANC2_IIR_COEFF_CTL_M,
+		TIMPANI_CDC_ANC2_IIR_COEFF_CTL_POR,
+		{
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0xFC, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC2_LPF_COEFF_PTR,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF0},
+		TIMPANI_CDC_ANC2_LPF_COEFF_PTR_M,
+		TIMPANI_CDC_ANC2_LPF_COEFF_PTR_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC2_LPF_COEFF_MSB,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF0},
+		TIMPANI_CDC_ANC2_LPF_COEFF_MSB_M,
+		TIMPANI_CDC_ANC2_LPF_COEFF_MSB_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC2_LPF_COEFF_LSB,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+		TIMPANI_CDC_ANC2_LPF_COEFF_LSB_M,
+		TIMPANI_CDC_ANC2_LPF_COEFF_LSB_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC2_SCALE_PTR,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+		TIMPANI_CDC_ANC2_SCALE_PTR_M,
+		TIMPANI_CDC_ANC2_SCALE_PTR_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC2_SCALE,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+		TIMPANI_CDC_ANC2_SCALE_M,
+		TIMPANI_CDC_ANC2_SCALE_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC2_DEBUG,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF0},
+		TIMPANI_CDC_ANC2_DEBUG_M,
+		TIMPANI_CDC_ANC2_DEBUG_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_LINE_L_AVOL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0xFC, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3},
+		TIMPANI_CDC_LINE_L_AVOL_M,
+		TIMPANI_CDC_LINE_L_AVOL_POR,
+		{
+			{ .mask = 0xFC, .path_mask = 0},
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_LINE_R_AVOL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0xFC, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3},
+		TIMPANI_CDC_LINE_R_AVOL_M,
+		TIMPANI_CDC_LINE_R_AVOL_POR,
+		{
+			{ .mask = 0xFC, .path_mask = 0},
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_HPH_L_AVOL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFE,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
+		TIMPANI_CDC_HPH_L_AVOL_M,
+		TIMPANI_CDC_HPH_L_AVOL_POR,
+		{
+			{ .mask = 0xFE, .path_mask = 0},
+			{ .mask = 0x01, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_HPH_R_AVOL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFE,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
+		TIMPANI_CDC_HPH_R_AVOL_M,
+		TIMPANI_CDC_HPH_R_AVOL_POR,
+		{
+			{ .mask = 0xFE, .path_mask = 0},
+			{ .mask = 0x01, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_COMP_CTL1,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x3F, 0x0, 0xC0},
+		TIMPANI_CDC_COMP_CTL1_M,
+		TIMPANI_CDC_COMP_CTL1_POR,
+		{
+			{ .mask = 0x3F, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_COMP_CTL2,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF0},
+		TIMPANI_CDC_COMP_CTL2_M,
+		TIMPANI_CDC_COMP_CTL2_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_COMP_PEAK_METER,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF0},
+		TIMPANI_CDC_COMP_PEAK_METER_M,
+		TIMPANI_CDC_COMP_PEAK_METER_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_COMP_LEVEL_METER_CTL1,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF0},
+		TIMPANI_CDC_COMP_LEVEL_METER_CTL1_M,
+		TIMPANI_CDC_COMP_LEVEL_METER_CTL1_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_COMP_LEVEL_METER_CTL2,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+		TIMPANI_CDC_COMP_LEVEL_METER_CTL2_M,
+		TIMPANI_CDC_COMP_LEVEL_METER_CTL2_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_COMP_ZONE_SELECT,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x7F, 0x0, 0x80},
+		TIMPANI_CDC_COMP_ZONE_SELECT_M,
+		TIMPANI_CDC_COMP_ZONE_SELECT_POR,
+		{
+			{ .mask = 0x7F, .path_mask = 0},
+			{ .mask = 0x80, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_COMP_ZC_MSB,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+		TIMPANI_CDC_COMP_ZC_MSB_M,
+		TIMPANI_CDC_COMP_ZC_MSB_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_COMP_ZC_LSB,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+		TIMPANI_CDC_COMP_ZC_LSB_M,
+		TIMPANI_CDC_COMP_ZC_LSB_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_COMP_SHUT_DOWN,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF},
+		TIMPANI_CDC_COMP_SHUT_DOWN_M,
+		TIMPANI_CDC_COMP_SHUT_DOWN_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_COMP_SHUT_DOWN_STATUS,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF},
+		TIMPANI_CDC_COMP_SHUT_DOWN_STATUS_M,
+		TIMPANI_CDC_COMP_SHUT_DOWN_STATUS_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_COMP_HALT,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF},
+		TIMPANI_CDC_COMP_HALT_M,
+		TIMPANI_CDC_COMP_HALT_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	}
+};
+
+struct reg_acc_blk_cfg timpani_blkcfg[RA_BLOCK_NUM] = {
+	{
+		.valid_owners = {RA_OWNER_NONE, RA_OWNER_PATH_RX1,
+		0, 0, 0, 0, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_RX1 */
+	{
+		.valid_owners = {RA_OWNER_NONE, 0, RA_OWNER_PATH_RX2,
+		0, 0, 0, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_RX2 */
+	{
+		.valid_owners = {RA_OWNER_NONE, 0, 0, RA_OWNER_PATH_TX1,
+		0, 0, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_TX1 */
+	{
+		.valid_owners = {RA_OWNER_NONE, 0, 0, 0, RA_OWNER_PATH_TX2,
+		0, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_TX2 */
+	{
+		.valid_owners = {RA_OWNER_NONE, 0, 0, 0, 0,
+		RA_OWNER_PATH_LB, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_LB */
+	{
+		.valid_owners = {RA_OWNER_NONE, RA_OWNER_PATH_RX1,
+		RA_OWNER_PATH_RX2, 0, 0, RA_OWNER_PATH_LB, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_SHARED_RX_LB */
+	{
+		.valid_owners = {RA_OWNER_NONE, 0, 0, RA_OWNER_PATH_TX1,
+		RA_OWNER_PATH_TX2, 0, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_SHARED_TX */
+	{
+		.valid_owners = {RA_OWNER_NONE, 0, 0, RA_OWNER_PATH_TX1,
+		RA_OWNER_PATH_TX2, 0, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_TXFE1 */
+	{
+		.valid_owners = {RA_OWNER_NONE, 0, 0, RA_OWNER_PATH_TX1,
+		RA_OWNER_PATH_TX2, 0, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_TXFE2 */
+	{
+		.valid_owners = {RA_OWNER_NONE, RA_OWNER_PATH_RX1,
+		RA_OWNER_PATH_RX2, 0, 0, RA_OWNER_PATH_LB, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_PA_COMMON */
+	{
+		.valid_owners = {RA_OWNER_NONE, RA_OWNER_PATH_RX1,
+		RA_OWNER_PATH_RX2, 0, 0, RA_OWNER_PATH_LB, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_PA_EAR */
+	{
+		.valid_owners = {RA_OWNER_NONE, RA_OWNER_PATH_RX1,
+		RA_OWNER_PATH_RX2, 0, 0, RA_OWNER_PATH_LB, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_PA_HPH */
+	{
+		.valid_owners = {RA_OWNER_NONE, RA_OWNER_PATH_RX1,
+		RA_OWNER_PATH_RX2, 0, 0, RA_OWNER_PATH_LB, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_PA_LINE */
+	{
+		.valid_owners = {RA_OWNER_NONE, RA_OWNER_PATH_RX1,
+		RA_OWNER_PATH_RX2, 0, 0, RA_OWNER_PATH_LB, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_PA_AUX */
+	{
+		.valid_owners = {RA_OWNER_NONE, 0, 0, RA_OWNER_PATH_TX1,
+		RA_OWNER_PATH_TX2, 0, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_ADC */
+	{
+		.valid_owners = {RA_OWNER_NONE, 0, 0, RA_OWNER_PATH_TX1,
+		RA_OWNER_PATH_TX2, 0, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_DMIC */
+	{
+		.valid_owners = {RA_OWNER_NONE, 0, 0, RA_OWNER_PATH_TX1,
+		RA_OWNER_PATH_TX2, 0, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_TX_I2S */
+	{
+		.valid_owners = {RA_OWNER_NONE, 0, 0, 0, 0, 0, RA_OWNER_DRV}
+	},
+	/*RA_BLOCK_DRV */
+	{
+		.valid_owners = {RA_OWNER_NONE, 0, 0, 0, 0, 0, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_TEST */
+	{
+		.valid_owners = {RA_OWNER_NONE, 0, 0, 0, 0, 0, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_RESERVED */
+};
+
+struct adie_codec_state {
+	struct adie_codec_path path[ADIE_CODEC_MAX];
+	u32 ref_cnt;
+	struct marimba *pdrv_ptr;
+	struct marimba_codec_platform_data *codec_pdata;
+	struct mutex lock;
+};
+
+static struct adie_codec_state adie_codec;
+
+/* A cacheable register is one that if the register's current value is being
+ * written to it again, then it is permissable to skip that register write
+ * because it does not actually change the value of the hardware register.
+ *
+ * Some registers are uncacheable, meaning that even they are being written
+ * again with their current value, the write has another purpose and must go
+ * through.
+ *
+ * Knowing the codec's uncacheable registers allows the driver to avoid
+ * unnecessary codec register writes while making sure important register writes
+ * are not skipped.
+ */
+
+static bool timpani_register_is_cacheable(u8 reg)
+{
+	switch (reg) {
+	case TIMPANI_A_PA_LINE_L_GAIN:
+	case TIMPANI_A_PA_LINE_R_GAIN:
+	case TIMPANI_A_PA_HPH_L_GAIN:
+	case TIMPANI_A_PA_HPH_R_GAIN:
+	case TIMPANI_A_CDC_GCTL1:
+	case TIMPANI_A_CDC_ST_CTL:
+	case TIMPANI_A_CDC_GCTL2:
+	case TIMPANI_A_CDC_ARB_BYPASS_CTL:
+	case TIMPANI_A_CDC_CH_CTL:
+	case TIMPANI_A_CDC_ANC1_IIR_COEFF_PTR:
+	case TIMPANI_A_CDC_ANC1_IIR_COEFF_MSB:
+	case TIMPANI_A_CDC_ANC1_IIR_COEFF_LSB:
+	case TIMPANI_A_CDC_ANC1_LPF_COEFF_PTR:
+	case TIMPANI_A_CDC_ANC1_LPF_COEFF_MSB:
+	case TIMPANI_A_CDC_ANC1_LPF_COEFF_LSB:
+	case TIMPANI_A_CDC_ANC1_SCALE_PTR:
+	case TIMPANI_A_CDC_ANC1_SCALE:
+	case TIMPANI_A_CDC_ANC2_IIR_COEFF_PTR:
+	case TIMPANI_A_CDC_ANC2_IIR_COEFF_MSB:
+	case TIMPANI_A_CDC_ANC2_IIR_COEFF_LSB:
+	case TIMPANI_A_CDC_ANC2_LPF_COEFF_PTR:
+	case TIMPANI_A_CDC_ANC2_LPF_COEFF_MSB:
+	case TIMPANI_A_CDC_ANC2_LPF_COEFF_LSB:
+	case TIMPANI_A_CDC_ANC2_SCALE_PTR:
+	case TIMPANI_A_CDC_ANC2_SCALE:
+	case TIMPANI_A_CDC_ANC1_CTL1:
+	case TIMPANI_A_CDC_ANC1_CTL2:
+	case TIMPANI_A_CDC_ANC1_FF_FB_SHIFT:
+	case TIMPANI_A_CDC_ANC2_CTL1:
+	case TIMPANI_A_CDC_ANC2_CTL2:
+	case TIMPANI_A_CDC_ANC2_FF_FB_SHIFT:
+	case TIMPANI_A_AUXPGA_LR_GAIN:
+	case TIMPANI_A_CDC_ANC_INPUT_MUX:
+		return false;
+	default:
+		return true;
+	}
+}
+
+static int adie_codec_write(u8 reg, u8 mask, u8 val)
+{
+	int rc = 0;
+	u8 new_val;
+
+	if (reg > MAX_SHADOW_RIGISTERS) {
+		pr_debug("register number is out of bound for shadow"
+					" registers reg = %d\n", reg);
+		new_val = (val & mask);
+		rc = marimba_write_bit_mask(adie_codec.pdrv_ptr, reg,  &new_val,
+			1, 0xFF);
+		if (IS_ERR_VALUE(rc)) {
+			pr_err("%s: fail to write reg %x\n", __func__, reg);
+			rc = -EIO;
+			goto error;
+		}
+		return rc;
+	}
+	new_val = (val & mask) | (timpani_shadow[reg] & ~mask);
+	if (!(timpani_register_is_cacheable(reg) &&
+		(new_val == timpani_shadow[reg]))) {
+
+		rc = marimba_write_bit_mask(adie_codec.pdrv_ptr, reg,  &new_val,
+			1, 0xFF);
+		if (IS_ERR_VALUE(rc)) {
+			pr_err("%s: fail to write reg %x\n", __func__, reg);
+			rc = -EIO;
+			goto error;
+		}
+		timpani_shadow[reg] = new_val;
+		pr_debug("%s: write reg %x val %x new value %x\n", __func__,
+			reg, val, new_val);
+	}
+
+error:
+	return rc;
+}
+
+
+static int reg_in_use(u8 reg_ref, u8 path_type)
+{
+	if ((reg_ref & ~path_type) == 0)
+		return 0;
+	else
+		return 1;
+}
+
+static int adie_codec_refcnt_write(u8 reg, u8 mask, u8 val, enum refcnt cnt,
+		u8 path_type)
+{
+	u8 i;
+	int j;
+	u8 fld_mask;
+	u8 path_mask;
+	u8 reg_mask = 0;
+	int rc = 0;
+
+	for (i = 0; i < ARRAY_SIZE(timpani_regset); i++) {
+		if (timpani_regset[i].reg_addr == reg) {
+			for (j = 0; j < TIMPANI_MAX_FIELDS; j++) {
+				fld_mask = timpani_regset[i].fld_ref_cnt[j].mask
+					& mask;
+				path_mask = timpani_regset[i].fld_ref_cnt[j]
+							.path_mask;
+				if (fld_mask) {
+					if (!reg_in_use(path_mask, path_type))
+						reg_mask |= fld_mask;
+					if (cnt == INC)
+						timpani_regset[i].fld_ref_cnt[j]
+							.path_mask |= path_type;
+					else if (cnt == DEC)
+						timpani_regset[i].fld_ref_cnt[j]
+							.path_mask &=
+								~path_type;
+				}
+			}
+
+			if (reg_mask)
+				rc = adie_codec_write(reg, reg_mask, val);
+			reg_mask = 0;
+			break;
+		}
+	}
+
+	return rc;
+}
+
+static int adie_codec_read(u8 reg, u8 *val)
+{
+	return marimba_read(adie_codec.pdrv_ptr, reg, val, 1);
+}
+
+static int timpani_adie_codec_setpath(struct adie_codec_path *path_ptr,
+					u32 freq_plan, u32 osr)
+{
+	int rc = 0;
+	u32 i, freq_idx = 0, freq = 0;
+
+	if (path_ptr == NULL)
+		return -EINVAL;
+
+	if (path_ptr->curr_stage != ADIE_CODEC_DIGITAL_OFF) {
+		rc = -EBUSY;
+		goto error;
+	}
+
+	for (i = 0; i < path_ptr->profile->setting_sz; i++) {
+		if (path_ptr->profile->settings[i].osr == osr) {
+			if (path_ptr->profile->settings[i].freq_plan >=
+				freq_plan) {
+				if (freq == 0) {
+					freq = path_ptr->profile->settings[i].
+								freq_plan;
+					freq_idx = i;
+				} else if (path_ptr->profile->settings[i].
+					freq_plan < freq) {
+					freq = path_ptr->profile->settings[i].
+								freq_plan;
+					freq_idx = i;
+				}
+			}
+		}
+	}
+
+	if (freq_idx >= path_ptr->profile->setting_sz)
+		rc = -ENODEV;
+	else {
+		path_ptr->hwsetting_idx = freq_idx;
+		path_ptr->stage_idx = 0;
+	}
+
+error:
+	return rc;
+}
+
+static u32 timpani_adie_codec_freq_supported(
+				struct adie_codec_dev_profile *profile,
+				u32 requested_freq)
+{
+	u32 i, rc = -EINVAL;
+
+	for (i = 0; i < profile->setting_sz; i++) {
+		if (profile->settings[i].freq_plan >= requested_freq) {
+			rc = 0;
+			break;
+		}
+	}
+	return rc;
+}
+int timpani_adie_codec_enable_sidetone(struct adie_codec_path *rx_path_ptr,
+	u32 enable)
+{
+	int rc = 0;
+
+	pr_debug("%s()\n", __func__);
+
+	mutex_lock(&adie_codec.lock);
+
+	if (!rx_path_ptr || &adie_codec.path[ADIE_CODEC_RX] != rx_path_ptr) {
+		pr_err("%s: invalid path pointer\n", __func__);
+		rc = -EINVAL;
+		goto error;
+	} else if (rx_path_ptr->curr_stage !=
+		ADIE_CODEC_DIGITAL_ANALOG_READY) {
+		pr_err("%s: bad state\n", __func__);
+		rc = -EPERM;
+		goto error;
+	}
+
+	if (enable) {
+		rc = adie_codec_write(TIMPANI_A_CDC_RX1_CTL,
+			TIMPANI_RX1_ST_MASK, TIMPANI_RX1_ST_ENABLE);
+
+		if (rx_path_ptr->reg_owner == RA_OWNER_PATH_RX1)
+			adie_codec_write(TIMPANI_A_CDC_ST_MIXING,
+				TIMPANI_CDC_ST_MIXING_TX1_MASK,
+				TIMPANI_CDC_ST_MIXING_TX1_ENABLE);
+		else if (rx_path_ptr->reg_owner == RA_OWNER_PATH_RX2)
+			adie_codec_write(TIMPANI_A_CDC_ST_MIXING,
+				TIMPANI_CDC_ST_MIXING_TX2_MASK,
+				TIMPANI_CDC_ST_MIXING_TX2_ENABLE);
+	 } else {
+		rc = adie_codec_write(TIMPANI_A_CDC_RX1_CTL,
+			TIMPANI_RX1_ST_MASK, 0);
+
+		if (rx_path_ptr->reg_owner == RA_OWNER_PATH_RX1)
+			adie_codec_write(TIMPANI_A_CDC_ST_MIXING,
+				TIMPANI_CDC_ST_MIXING_TX1_MASK, 0);
+		else if (rx_path_ptr->reg_owner == RA_OWNER_PATH_RX2)
+			adie_codec_write(TIMPANI_A_CDC_ST_MIXING,
+				TIMPANI_CDC_ST_MIXING_TX2_MASK, 0);
+	 }
+
+error:
+	mutex_unlock(&adie_codec.lock);
+	return rc;
+}
+static int timpani_adie_codec_enable_anc(struct adie_codec_path *rx_path_ptr,
+	u32 enable, struct adie_codec_anc_data *calibration_writes)
+{
+	int index = 0;
+	int rc = 0;
+	u8 reg, mask, val;
+	pr_debug("%s: enable = %d\n", __func__, enable);
+
+	mutex_lock(&adie_codec.lock);
+
+	if (!rx_path_ptr || &adie_codec.path[ADIE_CODEC_RX] != rx_path_ptr) {
+		pr_err("%s: invalid path pointer\n", __func__);
+		rc = -EINVAL;
+		goto error;
+	} else if (rx_path_ptr->curr_stage !=
+		ADIE_CODEC_DIGITAL_ANALOG_READY) {
+		pr_err("%s: bad state\n", __func__);
+		rc = -EPERM;
+		goto error;
+	}
+	if (enable) {
+		if (!calibration_writes || !calibration_writes->writes) {
+			pr_err("%s: No ANC calibration data\n", __func__);
+			rc = -EPERM;
+			goto error;
+		}
+		while (index < calibration_writes->size) {
+			ADIE_CODEC_UNPACK_ENTRY(calibration_writes->
+				writes[index], reg, mask, val);
+			adie_codec_write(reg, mask, val);
+			index++;
+		}
+	} else {
+		adie_codec_write(TIMPANI_A_CDC_ANC1_CTL1,
+		TIMPANI_CDC_ANC1_CTL1_ANC1_EN_M,
+		TIMPANI_CDC_ANC1_CTL1_ANC1_EN_ANC_DIS <<
+		TIMPANI_CDC_ANC1_CTL1_ANC1_EN_S);
+
+		adie_codec_write(TIMPANI_A_CDC_ANC2_CTL1,
+		TIMPANI_CDC_ANC2_CTL1_ANC2_EN_M,
+		TIMPANI_CDC_ANC2_CTL1_ANC2_EN_ANC_DIS <<
+		TIMPANI_CDC_ANC2_CTL1_ANC2_EN_S);
+	}
+
+error:
+	mutex_unlock(&adie_codec.lock);
+	return rc;
+}
+
+static void adie_codec_restore_regdefault(u8 path_mask, u32 blk)
+{
+	u32 ireg;
+	u32 regset_sz =
+	(sizeof(timpani_regset)/sizeof(struct timpani_regaccess));
+
+	for (ireg = 0; ireg < regset_sz; ireg++) {
+		if (timpani_regset[ireg].blk_mask[blk]) {
+			/* only process register belong to the block */
+			u8 reg = timpani_regset[ireg].reg_addr;
+			u8 mask = timpani_regset[ireg].blk_mask[blk];
+			u8 val = timpani_regset[ireg].reg_default;
+			adie_codec_refcnt_write(reg, mask, val, IGNORE,
+				path_mask);
+		}
+	}
+}
+
+static void adie_codec_reach_stage_action(struct adie_codec_path *path_ptr,
+	u32 stage)
+{
+	u32 iblk, iowner; /* iterators */
+	u8 path_mask;
+
+	if (path_ptr == NULL)
+		return;
+
+	path_mask = TIMPANI_PATH_MASK(path_ptr->reg_owner);
+
+	if (stage != ADIE_CODEC_DIGITAL_OFF)
+		return;
+
+	for (iblk = 0 ; iblk <= RA_BLOCK_RESERVED ; iblk++) {
+		for (iowner = 0; iowner < RA_OWNER_NUM; iowner++) {
+			if (timpani_blkcfg[iblk].valid_owners[iowner] ==
+					path_ptr->reg_owner) {
+				adie_codec_restore_regdefault(path_mask, iblk);
+				break; /* This path owns this block */
+			}
+		}
+	}
+}
+
+static int timpani_adie_codec_proceed_stage(struct adie_codec_path *path_ptr,
+						u32 state)
+{
+	int rc = 0, loop_exit = 0;
+	struct adie_codec_action_unit *curr_action;
+	struct adie_codec_hwsetting_entry *setting;
+	u8 reg, mask, val;
+	u8 path_mask;
+
+	if (path_ptr == NULL)
+		return -EINVAL;
+
+	path_mask = TIMPANI_PATH_MASK(path_ptr->reg_owner);
+
+	mutex_lock(&adie_codec.lock);
+	setting = &path_ptr->profile->settings[path_ptr->hwsetting_idx];
+	while (!loop_exit) {
+
+		curr_action = &setting->actions[path_ptr->stage_idx];
+
+		switch (curr_action->type) {
+		case ADIE_CODEC_ACTION_ENTRY:
+			ADIE_CODEC_UNPACK_ENTRY(curr_action->action,
+			reg, mask, val);
+			if (state == ADIE_CODEC_DIGITAL_OFF)
+				adie_codec_refcnt_write(reg, mask, val, DEC,
+					path_mask);
+			else
+				adie_codec_refcnt_write(reg, mask, val, INC,
+					path_mask);
+			break;
+		case ADIE_CODEC_ACTION_DELAY_WAIT:
+			if (curr_action->action > MAX_MDELAY_US)
+				msleep(curr_action->action/1000);
+			else
+				usleep_range(curr_action->action,
+				curr_action->action);
+			break;
+		case ADIE_CODEC_ACTION_STAGE_REACHED:
+			adie_codec_reach_stage_action(path_ptr,
+				curr_action->action);
+			if (curr_action->action == state) {
+				path_ptr->curr_stage = state;
+				loop_exit = 1;
+			}
+			break;
+		default:
+			BUG();
+		}
+
+		path_ptr->stage_idx++;
+		if (path_ptr->stage_idx == setting->action_sz)
+			path_ptr->stage_idx = 0;
+	}
+	mutex_unlock(&adie_codec.lock);
+
+	return rc;
+}
+
+static void timpani_codec_bring_up(void)
+{
+	/* Codec power up sequence */
+	adie_codec_write(0xFF, 0xFF, 0x08);
+	adie_codec_write(0xFF, 0xFF, 0x0A);
+	adie_codec_write(0xFF, 0xFF, 0x0E);
+	adie_codec_write(0xFF, 0xFF, 0x07);
+	adie_codec_write(0xFF, 0xFF, 0x17);
+	adie_codec_write(TIMPANI_A_MREF, 0xFF, 0xF2);
+	msleep(15);
+	adie_codec_write(TIMPANI_A_MREF, 0xFF, 0x22);
+
+	/* Bypass TX HPFs to prevent pops */
+	adie_codec_write(TIMPANI_A_CDC_BYPASS_CTL2, TIMPANI_CDC_BYPASS_CTL2_M,
+		TIMPANI_CDC_BYPASS_CTL2_POR);
+	adie_codec_write(TIMPANI_A_CDC_BYPASS_CTL3, TIMPANI_CDC_BYPASS_CTL3_M,
+		TIMPANI_CDC_BYPASS_CTL3_POR);
+}
+
+static void timpani_codec_bring_down(void)
+{
+	adie_codec_write(TIMPANI_A_MREF, 0xFF, TIMPANI_MREF_POR);
+	adie_codec_write(0xFF, 0xFF, 0x07);
+	adie_codec_write(0xFF, 0xFF, 0x06);
+	adie_codec_write(0xFF, 0xFF, 0x0E);
+	adie_codec_write(0xFF, 0xFF, 0x08);
+}
+
+static int timpani_adie_codec_open(struct adie_codec_dev_profile *profile,
+	struct adie_codec_path **path_pptr)
+{
+	int rc = 0;
+
+	mutex_lock(&adie_codec.lock);
+
+	if (!profile || !path_pptr) {
+		rc = -EINVAL;
+		goto error;
+	}
+
+	if (adie_codec.path[profile->path_type].profile) {
+		rc = -EBUSY;
+		goto error;
+	}
+
+	if (!adie_codec.ref_cnt) {
+
+		if (adie_codec.codec_pdata &&
+				adie_codec.codec_pdata->marimba_codec_power) {
+
+			rc = adie_codec.codec_pdata->marimba_codec_power(1);
+			if (rc) {
+				pr_err("%s: could not power up timpani "
+						"codec\n", __func__);
+				goto error;
+			}
+			timpani_codec_bring_up();
+		} else {
+			pr_err("%s: couldn't detect timpani codec\n", __func__);
+			rc = -ENODEV;
+			goto error;
+		}
+
+	}
+
+	adie_codec.path[profile->path_type].profile = profile;
+	*path_pptr = (void *) &adie_codec.path[profile->path_type];
+	adie_codec.ref_cnt++;
+	adie_codec.path[profile->path_type].hwsetting_idx = 0;
+	adie_codec.path[profile->path_type].curr_stage = ADIE_CODEC_DIGITAL_OFF;
+	adie_codec.path[profile->path_type].stage_idx = 0;
+
+
+error:
+	mutex_unlock(&adie_codec.lock);
+	return rc;
+}
+
+static int timpani_adie_codec_close(struct adie_codec_path *path_ptr)
+{
+	int rc = 0;
+
+	mutex_lock(&adie_codec.lock);
+
+	if (!path_ptr) {
+		rc = -EINVAL;
+		goto error;
+	}
+	if (path_ptr->curr_stage != ADIE_CODEC_DIGITAL_OFF)
+		adie_codec_proceed_stage(path_ptr, ADIE_CODEC_DIGITAL_OFF);
+
+	BUG_ON(!adie_codec.ref_cnt);
+
+	path_ptr->profile = NULL;
+	adie_codec.ref_cnt--;
+
+	if (!adie_codec.ref_cnt) {
+		/* Timpani CDC power down sequence */
+		timpani_codec_bring_down();
+
+		if (adie_codec.codec_pdata &&
+				adie_codec.codec_pdata->marimba_codec_power) {
+
+			rc = adie_codec.codec_pdata->marimba_codec_power(0);
+			if (rc) {
+				pr_err("%s: could not power down timpani "
+						"codec\n", __func__);
+				goto error;
+			}
+		}
+	}
+
+error:
+	mutex_unlock(&adie_codec.lock);
+	return rc;
+}
+
+static int timpani_adie_codec_set_master_mode(struct adie_codec_path *path_ptr,
+			u8 master)
+{
+	u8 val = master ? 1 : 0;
+
+	if (!path_ptr)
+		return -EINVAL;
+
+	if (path_ptr->reg_owner == RA_OWNER_PATH_RX1)
+		adie_codec_write(TIMPANI_A_CDC_RX1_CTL, 0x01, val);
+	else if (path_ptr->reg_owner == RA_OWNER_PATH_TX1)
+		adie_codec_write(TIMPANI_A_CDC_TX_I2S_CTL, 0x01, val);
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+int timpani_adie_codec_set_device_analog_volume(
+		struct adie_codec_path *path_ptr,
+		u32 num_channels, u32 volume)
+{
+	u8 val;
+	u8 curr_val;
+	u8 i;
+
+	adie_codec_read(TIMPANI_A_AUXPGA_LR_GAIN, &curr_val);
+
+	/* Volume is expressed as a percentage. */
+	/* The upper nibble is the left channel, lower right channel. */
+	val = (u8)((volume * TIMPANI_CODEC_AUXPGA_GAIN_RANGE) / 100);
+	val |= val << 4;
+
+	if ((curr_val & 0x0F) < (val & 0x0F)) {
+		for (i = curr_val; i < val; i += 0x11)
+			adie_codec_write(TIMPANI_A_AUXPGA_LR_GAIN, 0xFF, i);
+	} else if ((curr_val & 0x0F) > (val & 0x0F)) {
+		for (i = curr_val; i > val; i -= 0x11)
+			adie_codec_write(TIMPANI_A_AUXPGA_LR_GAIN, 0xFF, i);
+	}
+
+	return 0;
+}
+
+enum adie_vol_type {
+	ADIE_CODEC_RX_DIG_VOL,
+	ADIE_CODEC_TX_DIG_VOL,
+	ADIE_CODEC_VOL_TYPE_MAX
+};
+
+#define CDC_RX1LG		0x84
+#define CDC_RX1RG		0x85
+#define CDC_TX1LG		0x86
+#define CDC_TX1RG		0x87
+#define	DIG_VOL_MASK		0xFF
+
+#define CDC_GCTL1		0x8A
+#define RX1_PGA_UPDATE_L	0x04
+#define RX1_PGA_UPDATE_R	0x08
+#define TX1_PGA_UPDATE_L	0x40
+#define TX1_PGA_UPDATE_R	0x80
+#define CDC_GCTL1_RX_MASK	0x0F
+#define CDC_GCTL1_TX_MASK	0xF0
+
+enum {
+	TIMPANI_MIN_DIG_VOL	= -84,	/* in DB*/
+	TIMPANI_MAX_DIG_VOL	=  16,	/* in DB*/
+	TIMPANI_DIG_VOL_STEP	=  3	/* in DB*/
+};
+
+static int timpani_adie_codec_set_dig_vol(enum adie_vol_type vol_type,
+	u32 num_chan, u32 vol_per)
+{
+	u8 reg_left, reg_right;
+	u8 gain_reg_val, gain_reg_mask;
+	s8 new_reg_val, cur_reg_val;
+	s8 step_size;
+
+	adie_codec_read(CDC_GCTL1, &gain_reg_val);
+
+	if (vol_type == ADIE_CODEC_RX_DIG_VOL) {
+
+		pr_debug("%s : RX DIG VOL. num_chan = %u\n", __func__,
+				num_chan);
+		reg_left =  CDC_RX1LG;
+		reg_right = CDC_RX1RG;
+
+		if (num_chan == 1)
+			gain_reg_val |=  RX1_PGA_UPDATE_L;
+		else
+			gain_reg_val |= (RX1_PGA_UPDATE_L | RX1_PGA_UPDATE_R);
+
+		gain_reg_mask = CDC_GCTL1_RX_MASK;
+	} else {
+
+		pr_debug("%s : TX DIG VOL. num_chan = %u\n", __func__,
+				num_chan);
+		reg_left = CDC_TX1LG;
+		reg_right = CDC_TX1RG;
+
+		if (num_chan == 1)
+			gain_reg_val |=  TX1_PGA_UPDATE_L;
+		else
+			gain_reg_val |= (TX1_PGA_UPDATE_L | TX1_PGA_UPDATE_R);
+
+		gain_reg_mask = CDC_GCTL1_TX_MASK;
+	}
+
+	adie_codec_read(reg_left, &cur_reg_val);
+
+	pr_debug("%s: vol_per = %d cur_reg_val = %d 0x%x\n", __func__, vol_per,
+			cur_reg_val, cur_reg_val);
+
+	new_reg_val =  TIMPANI_MIN_DIG_VOL +
+		(((TIMPANI_MAX_DIG_VOL - TIMPANI_MIN_DIG_VOL) * vol_per) / 100);
+
+	pr_debug("new_reg_val = %d 0x%x\n", new_reg_val, new_reg_val);
+
+	if (new_reg_val > cur_reg_val) {
+		step_size = TIMPANI_DIG_VOL_STEP;
+	} else if (new_reg_val < cur_reg_val) {
+		step_size = -TIMPANI_DIG_VOL_STEP;
+	} else {
+		pr_debug("new_reg_val and cur_reg_val are same 0x%x\n",
+				new_reg_val);
+		return 0;
+	}
+
+	while (cur_reg_val != new_reg_val) {
+
+		if (((new_reg_val > cur_reg_val) &&
+			((new_reg_val - cur_reg_val) < TIMPANI_DIG_VOL_STEP)) ||
+			((cur_reg_val > new_reg_val) &&
+			((cur_reg_val - new_reg_val)
+			 < TIMPANI_DIG_VOL_STEP))) {
+
+			cur_reg_val = new_reg_val;
+
+			pr_debug("diff less than step. write new_reg_val = %d"
+				" 0x%x\n", new_reg_val, new_reg_val);
+
+		 } else {
+			cur_reg_val = cur_reg_val + step_size;
+
+			pr_debug("cur_reg_val = %d 0x%x\n",
+					cur_reg_val, cur_reg_val);
+		 }
+
+		adie_codec_write(reg_left, DIG_VOL_MASK, cur_reg_val);
+
+		if (num_chan == 2)
+			adie_codec_write(reg_right, DIG_VOL_MASK, cur_reg_val);
+
+		adie_codec_write(CDC_GCTL1, gain_reg_mask, gain_reg_val);
+	}
+	return 0;
+}
+
+static int timpani_adie_codec_set_device_digital_volume(
+		struct adie_codec_path *path_ptr,
+		u32 num_channels, u32 vol_percentage /* in percentage */)
+{
+	enum adie_vol_type vol_type;
+
+	if (!path_ptr  || (path_ptr->curr_stage !=
+				ADIE_CODEC_DIGITAL_ANALOG_READY)) {
+		pr_info("%s: timpani codec not ready for volume control\n",
+		       __func__);
+		return  -EPERM;
+	}
+
+	if (num_channels > 2) {
+		pr_err("%s: timpani odec only supports max two channels\n",
+		       __func__);
+		return -EINVAL;
+	}
+
+	if (path_ptr->profile->path_type == ADIE_CODEC_RX) {
+		vol_type = ADIE_CODEC_RX_DIG_VOL;
+	} else if (path_ptr->profile->path_type == ADIE_CODEC_TX) {
+		vol_type = ADIE_CODEC_TX_DIG_VOL;
+	} else {
+		pr_err("%s: invalid device data neither RX nor TX\n",
+				__func__);
+		return -EINVAL;
+	}
+
+	timpani_adie_codec_set_dig_vol(vol_type, num_channels, vol_percentage);
+
+	return 0;
+}
+
+static const struct adie_codec_operations timpani_adie_ops = {
+	.codec_id = TIMPANI_ID,
+	.codec_open = timpani_adie_codec_open,
+	.codec_close = timpani_adie_codec_close,
+	.codec_setpath = timpani_adie_codec_setpath,
+	.codec_proceed_stage = timpani_adie_codec_proceed_stage,
+	.codec_freq_supported = timpani_adie_codec_freq_supported,
+	.codec_enable_sidetone = timpani_adie_codec_enable_sidetone,
+	.codec_set_master_mode = timpani_adie_codec_set_master_mode,
+	.codec_enable_anc = timpani_adie_codec_enable_anc,
+	.codec_set_device_analog_volume =
+		timpani_adie_codec_set_device_analog_volume,
+	.codec_set_device_digital_volume =
+		timpani_adie_codec_set_device_digital_volume,
+};
+
+static void timpani_codec_populate_shadow_registers(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(timpani_regset); i++) {
+		if (timpani_regset[i].reg_addr < TIMPANI_ARRAY_SIZE) {
+			timpani_shadow[timpani_regset[i].reg_addr] =
+				timpani_regset[i].reg_default;
+		}
+	}
+}
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *debugfs_timpani_dent;
+static struct dentry *debugfs_peek;
+static struct dentry *debugfs_poke;
+static struct dentry *debugfs_power;
+static struct dentry *debugfs_dump;
+
+static unsigned char read_data;
+
+static int codec_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static int get_parameters(char *buf, long int *param1, int num_of_par)
+{
+	char *token;
+	int base, cnt;
+
+	token = strsep(&buf, " ");
+
+	for (cnt = 0; cnt < num_of_par; cnt++) {
+		if (token != NULL) {
+			if ((token[1] == 'x') || (token[1] == 'X'))
+				base = 16;
+			else
+				base = 10;
+
+			if (strict_strtoul(token, base, &param1[cnt]) != 0)
+				return -EINVAL;
+
+			token = strsep(&buf, " ");
+			}
+		else
+			return -EINVAL;
+	}
+	return 0;
+}
+
+static ssize_t codec_debug_read(struct file *file, char __user *ubuf,
+				size_t count, loff_t *ppos)
+{
+	char lbuf[8];
+
+	snprintf(lbuf, sizeof(lbuf), "0x%x\n", read_data);
+	return simple_read_from_buffer(ubuf, count, ppos, lbuf, strlen(lbuf));
+}
+
+static ssize_t codec_debug_write(struct file *filp,
+	const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+	char *access_str = filp->private_data;
+	char lbuf[32];
+	int rc;
+	int i;
+	int read_result;
+	u8 reg_val;
+	long int param[5];
+
+	if (cnt > sizeof(lbuf) - 1)
+		return -EINVAL;
+
+	rc = copy_from_user(lbuf, ubuf, cnt);
+	if (rc)
+		return -EFAULT;
+
+	lbuf[cnt] = '\0';
+
+	if (!strcmp(access_str, "power")) {
+		if (get_parameters(lbuf, param, 1) == 0) {
+			switch (param[0]) {
+			case 1:
+				adie_codec.codec_pdata->marimba_codec_power(1);
+				timpani_codec_bring_up();
+				break;
+			case 0:
+				timpani_codec_bring_down();
+				adie_codec.codec_pdata->marimba_codec_power(0);
+				break;
+			default:
+				rc = -EINVAL;
+				break;
+			}
+		} else
+			rc = -EINVAL;
+	} else if (!strcmp(access_str, "poke")) {
+		/* write */
+		rc = get_parameters(lbuf, param, 2);
+		if ((param[0] <= 0xFF) && (param[1] <= 0xFF) &&
+			(rc == 0))
+			adie_codec_write(param[0], 0xFF, param[1]);
+		else
+			rc = -EINVAL;
+	} else if (!strcmp(access_str, "peek")) {
+		/* read */
+		rc = get_parameters(lbuf, param, 1);
+		if ((param[0] <= 0xFF) && (rc == 0))
+			adie_codec_read(param[0], &read_data);
+		else
+			rc = -EINVAL;
+	} else if (!strcmp(access_str, "dump")) {
+		pr_info("************** timpani regs *************\n");
+		for (i = 0; i < 0xFF; i++) {
+			read_result = adie_codec_read(i, &reg_val);
+			if (read_result < 0) {
+				pr_info("failed to read codec register\n");
+				break;
+			} else
+				pr_info("reg 0x%02X val 0x%02X\n", i, reg_val);
+		}
+		pr_info("*****************************************\n");
+	}
+
+	if (rc == 0)
+		rc = cnt;
+	else
+		pr_err("%s: rc = %d\n", __func__, rc);
+
+	return rc;
+}
+
+static const struct file_operations codec_debug_ops = {
+	.open = codec_debug_open,
+	.write = codec_debug_write,
+	.read = codec_debug_read
+};
+#endif
+
+static int timpani_codec_probe(struct platform_device *pdev)
+{
+	int rc;
+
+	adie_codec.pdrv_ptr = platform_get_drvdata(pdev);
+	adie_codec.codec_pdata = pdev->dev.platform_data;
+
+	if (adie_codec.codec_pdata->snddev_profile_init)
+		adie_codec.codec_pdata->snddev_profile_init();
+
+	timpani_codec_populate_shadow_registers();
+
+	/* Register the timpani ADIE operations */
+	rc = adie_codec_register_codec_operations(&timpani_adie_ops);
+
+#ifdef CONFIG_DEBUG_FS
+	debugfs_timpani_dent = debugfs_create_dir("msm_adie_codec", 0);
+	if (!IS_ERR(debugfs_timpani_dent)) {
+		debugfs_peek = debugfs_create_file("peek",
+		S_IFREG | S_IRUGO, debugfs_timpani_dent,
+		(void *) "peek", &codec_debug_ops);
+
+		debugfs_poke = debugfs_create_file("poke",
+		S_IFREG | S_IRUGO, debugfs_timpani_dent,
+		(void *) "poke", &codec_debug_ops);
+
+		debugfs_power = debugfs_create_file("power",
+		S_IFREG | S_IRUGO, debugfs_timpani_dent,
+		(void *) "power", &codec_debug_ops);
+
+		debugfs_dump = debugfs_create_file("dump",
+		S_IFREG | S_IRUGO, debugfs_timpani_dent,
+		(void *) "dump", &codec_debug_ops);
+
+	}
+#endif
+
+	return rc;
+}
+
+static struct platform_driver timpani_codec_driver = {
+	.probe = timpani_codec_probe,
+	.driver = {
+		.name = "timpani_codec",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init timpani_codec_init(void)
+{
+	s32 rc;
+
+	rc = platform_driver_register(&timpani_codec_driver);
+	if (IS_ERR_VALUE(rc))
+		goto error;
+
+	adie_codec.path[ADIE_CODEC_TX].reg_owner = RA_OWNER_PATH_TX1;
+	adie_codec.path[ADIE_CODEC_RX].reg_owner = RA_OWNER_PATH_RX1;
+	adie_codec.path[ADIE_CODEC_LB].reg_owner = RA_OWNER_PATH_LB;
+	mutex_init(&adie_codec.lock);
+error:
+	return rc;
+}
+
+static void __exit timpani_codec_exit(void)
+{
+#ifdef CONFIG_DEBUG_FS
+	debugfs_remove(debugfs_peek);
+	debugfs_remove(debugfs_poke);
+	debugfs_remove(debugfs_power);
+	debugfs_remove(debugfs_dump);
+	debugfs_remove(debugfs_timpani_dent);
+#endif
+	platform_driver_unregister(&timpani_codec_driver);
+}
+
+module_init(timpani_codec_init);
+module_exit(timpani_codec_exit);
+
+MODULE_DESCRIPTION("Timpani codec driver");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/tps65023.c b/drivers/mfd/tps65023.c
new file mode 100644
index 0000000..318692f
--- /dev/null
+++ b/drivers/mfd/tps65023.c
@@ -0,0 +1,123 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/mfd/tps65023.h>
+#include <linux/module.h>
+
+/* TPS65023_registers */
+#define TPS65023_VERSION	0
+#define TPS65023_PGOODZ		1
+#define TPS65023_MASK		2
+#define TPS65023_REG_CTRL	3
+#define TPS65023_CON_CTRL	4
+#define TPS65023_CON_CTRL2	5
+#define TPS65023_DEFCORE	6
+#define TPS65023_DEFSLEW	7
+#define TPS65023_LDO_CTRL	8
+#define TPS65023_MAX		9
+
+static struct i2c_client *tpsclient;
+
+int tps65023_set_dcdc1_level(int mvolts)
+{
+	int val;
+	int ret;
+
+	if (!tpsclient)
+		return -ENODEV;
+
+	if (mvolts < 800 || mvolts > 1600)
+		return -EINVAL;
+
+	if (mvolts == 1600)
+		val = 0x1F;
+	else
+		val = ((mvolts - 800)/25) & 0x1F;
+
+	ret = i2c_smbus_write_byte_data(tpsclient, TPS65023_DEFCORE, val);
+
+	if (!ret)
+		ret = i2c_smbus_write_byte_data(tpsclient,
+				TPS65023_CON_CTRL2, 0x80);
+
+	return ret;
+}
+EXPORT_SYMBOL(tps65023_set_dcdc1_level);
+
+int tps65023_get_dcdc1_level(int *mvolts)
+{
+	int val;
+
+	if (!tpsclient)
+		return -ENODEV;
+
+	val = i2c_smbus_read_byte_data(tpsclient, TPS65023_DEFCORE) & 0x1F;
+
+	if (val == 0x1F)
+		*mvolts = 1600;
+	else
+		*mvolts = (val * 25) + 800;
+	return 0;
+}
+EXPORT_SYMBOL(tps65023_get_dcdc1_level);
+
+static int tps65023_probe(struct i2c_client *client,
+		const struct i2c_device_id *dev_id)
+{
+	if (!i2c_check_functionality(client->adapter,
+				I2C_FUNC_SMBUS_BYTE_DATA)) {
+		printk(KERN_ERR "TPS65023 does not support SMBUS_BYTE_DATA.\n");
+		return -EINVAL;
+	}
+
+	tpsclient = client;
+	printk(KERN_INFO "TPS65023: PMIC probed.\n");
+	return 0;
+}
+
+static int __devexit tps65023_remove(struct i2c_client *client)
+{
+	tpsclient = NULL;
+	return 0;
+}
+
+static const struct i2c_device_id tps65023_id[] = {
+	{ "tps65023", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, tps65023_id);
+
+static struct i2c_driver tps65023_driver = {
+	.driver = {
+		.name   = "tps65023",
+		.owner  = THIS_MODULE,
+	},
+	.probe  = tps65023_probe,
+	.remove = __devexit_p(tps65023_remove),
+	.id_table = tps65023_id,
+};
+
+static int __init tps65023_init(void)
+{
+	return i2c_add_driver(&tps65023_driver);
+}
+
+
+static void __exit tps65023_exit(void)
+{
+	i2c_del_driver(&tps65023_driver);
+}
+
+module_init(tps65023_init);
+module_exit(tps65023_exit);
diff --git a/drivers/mfd/wcd9xxx-core.c b/drivers/mfd/wcd9xxx-core.c
new file mode 100644
index 0000000..e2dff4b
--- /dev/null
+++ b/drivers/mfd/wcd9xxx-core.c
@@ -0,0 +1,1168 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/pdata.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/debugfs.h>
+#include <linux/regulator/consumer.h>
+#include <linux/i2c.h>
+#include <sound/soc.h>
+
+#define WCD9XXX_SLIM_GLA_MAX_RETRIES 5
+#define WCD9XXX_REGISTER_START_OFFSET 0x800
+#define WCD9XXX_SLIM_RW_MAX_TRIES 3
+
+#define MAX_WCD9XXX_DEVICE	4
+#define WCD9XXX_I2C_MODE	0x03
+
+struct wcd9xxx_i2c {
+	struct i2c_client *client;
+	struct i2c_msg xfer_msg[2];
+	struct mutex xfer_lock;
+	int mod_id;
+};
+
+struct wcd9xxx_i2c wcd9xxx_modules[MAX_WCD9XXX_DEVICE];
+static int wcd9xxx_intf = -1;
+
+static int wcd9xxx_read(struct wcd9xxx *wcd9xxx, unsigned short reg,
+		       int bytes, void *dest, bool interface_reg)
+{
+	int ret;
+	u8 *buf = dest;
+
+	if (bytes <= 0) {
+		dev_err(wcd9xxx->dev, "Invalid byte read length %d\n", bytes);
+		return -EINVAL;
+	}
+
+	ret = wcd9xxx->read_dev(wcd9xxx, reg, bytes, dest, interface_reg);
+	if (ret < 0) {
+		dev_err(wcd9xxx->dev, "Codec read failed\n");
+		return ret;
+	} else
+		dev_dbg(wcd9xxx->dev, "Read 0x%02x from 0x%x\n",
+			 *buf, reg);
+
+	return 0;
+}
+int wcd9xxx_reg_read(struct wcd9xxx *wcd9xxx, unsigned short reg)
+{
+	u8 val;
+	int ret;
+
+	mutex_lock(&wcd9xxx->io_lock);
+	ret = wcd9xxx_read(wcd9xxx, reg, 1, &val, false);
+	mutex_unlock(&wcd9xxx->io_lock);
+
+	if (ret < 0)
+		return ret;
+	else
+		return val;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_reg_read);
+
+static int wcd9xxx_write(struct wcd9xxx *wcd9xxx, unsigned short reg,
+			int bytes, void *src, bool interface_reg)
+{
+	u8 *buf = src;
+
+	if (bytes <= 0) {
+		pr_err("%s: Error, invalid write length\n", __func__);
+		return -EINVAL;
+	}
+
+	dev_dbg(wcd9xxx->dev, "Write %02x to 0x%x\n",
+		 *buf, reg);
+
+	return wcd9xxx->write_dev(wcd9xxx, reg, bytes, src, interface_reg);
+}
+
+int wcd9xxx_reg_write(struct wcd9xxx *wcd9xxx, unsigned short reg,
+		     u8 val)
+{
+	int ret;
+
+	mutex_lock(&wcd9xxx->io_lock);
+	ret = wcd9xxx_write(wcd9xxx, reg, 1, &val, false);
+	mutex_unlock(&wcd9xxx->io_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_reg_write);
+
+static u8 wcd9xxx_pgd_la;
+static u8 wcd9xxx_inf_la;
+
+int wcd9xxx_interface_reg_read(struct wcd9xxx *wcd9xxx, unsigned short reg)
+{
+	u8 val;
+	int ret;
+
+	mutex_lock(&wcd9xxx->io_lock);
+	ret = wcd9xxx_read(wcd9xxx, reg, 1, &val, true);
+	mutex_unlock(&wcd9xxx->io_lock);
+
+	if (ret < 0)
+		return ret;
+	else
+		return val;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_interface_reg_read);
+
+int wcd9xxx_interface_reg_write(struct wcd9xxx *wcd9xxx, unsigned short reg,
+		     u8 val)
+{
+	int ret;
+
+	mutex_lock(&wcd9xxx->io_lock);
+	ret = wcd9xxx_write(wcd9xxx, reg, 1, &val, true);
+	mutex_unlock(&wcd9xxx->io_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_interface_reg_write);
+
+int wcd9xxx_bulk_read(struct wcd9xxx *wcd9xxx, unsigned short reg,
+		     int count, u8 *buf)
+{
+	int ret;
+
+	mutex_lock(&wcd9xxx->io_lock);
+
+	ret = wcd9xxx_read(wcd9xxx, reg, count, buf, false);
+
+	mutex_unlock(&wcd9xxx->io_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_bulk_read);
+
+int wcd9xxx_bulk_write(struct wcd9xxx *wcd9xxx, unsigned short reg,
+		     int count, u8 *buf)
+{
+	int ret;
+
+	mutex_lock(&wcd9xxx->io_lock);
+
+	ret = wcd9xxx_write(wcd9xxx, reg, count, buf, false);
+
+	mutex_unlock(&wcd9xxx->io_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_bulk_write);
+
+static int wcd9xxx_slim_read_device(struct wcd9xxx *wcd9xxx, unsigned short reg,
+				int bytes, void *dest, bool interface)
+{
+	int ret;
+	struct slim_ele_access msg;
+	int slim_read_tries = WCD9XXX_SLIM_RW_MAX_TRIES;
+	msg.start_offset = WCD9XXX_REGISTER_START_OFFSET + reg;
+	msg.num_bytes = bytes;
+	msg.comp = NULL;
+
+	while (1) {
+		mutex_lock(&wcd9xxx->xfer_lock);
+		ret = slim_request_val_element(interface ?
+			       wcd9xxx->slim_slave : wcd9xxx->slim,
+			       &msg, dest, bytes);
+		mutex_unlock(&wcd9xxx->xfer_lock);
+		if (likely(ret == 0) || (--slim_read_tries == 0))
+			break;
+		usleep_range(5000, 5000);
+	}
+
+	if (ret)
+		pr_err("%s: Error, Codec read failed (%d)\n", __func__, ret);
+
+	return ret;
+}
+/* Interface specifies whether the write is to the interface or general
+ * registers.
+ */
+static int wcd9xxx_slim_write_device(struct wcd9xxx *wcd9xxx,
+		unsigned short reg, int bytes, void *src, bool interface)
+{
+	int ret;
+	struct slim_ele_access msg;
+	int slim_write_tries = WCD9XXX_SLIM_RW_MAX_TRIES;
+	msg.start_offset = WCD9XXX_REGISTER_START_OFFSET + reg;
+	msg.num_bytes = bytes;
+	msg.comp = NULL;
+
+	while (1) {
+		mutex_lock(&wcd9xxx->xfer_lock);
+		ret = slim_change_val_element(interface ?
+			      wcd9xxx->slim_slave : wcd9xxx->slim,
+			      &msg, src, bytes);
+		mutex_unlock(&wcd9xxx->xfer_lock);
+		if (likely(ret == 0) || (--slim_write_tries == 0))
+			break;
+		usleep_range(5000, 5000);
+	}
+
+	if (ret)
+		pr_err("%s: Error, Codec write failed (%d)\n", __func__, ret);
+
+	return ret;
+}
+
+static struct mfd_cell tabla1x_devs[] = {
+	{
+		.name = "tabla1x_codec",
+	},
+};
+
+static struct mfd_cell tabla_devs[] = {
+	{
+		.name = "tabla_codec",
+	},
+};
+
+static struct mfd_cell sitar_devs[] = {
+	{
+		.name = "sitar_codec",
+	},
+};
+
+static void wcd9xxx_bring_up(struct wcd9xxx *wcd9xxx)
+{
+	wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_LEAKAGE_CTL, 0x4);
+	wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_CDC_CTL, 0);
+	usleep_range(5000, 5000);
+	wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_CDC_CTL, 3);
+	wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_LEAKAGE_CTL, 3);
+}
+
+static void wcd9xxx_bring_down(struct wcd9xxx *wcd9xxx)
+{
+	wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_LEAKAGE_CTL, 0x7);
+	wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_LEAKAGE_CTL, 0x6);
+	wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_LEAKAGE_CTL, 0xe);
+	wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_LEAKAGE_CTL, 0x8);
+}
+
+static int wcd9xxx_reset(struct wcd9xxx *wcd9xxx)
+{
+	int ret;
+
+	if (wcd9xxx->reset_gpio) {
+		ret = gpio_request(wcd9xxx->reset_gpio, "CDC_RESET");
+		if (ret) {
+			pr_err("%s: Failed to request gpio %d\n", __func__,
+				wcd9xxx->reset_gpio);
+			wcd9xxx->reset_gpio = 0;
+			return ret;
+		}
+
+		gpio_direction_output(wcd9xxx->reset_gpio, 1);
+		msleep(20);
+		gpio_direction_output(wcd9xxx->reset_gpio, 0);
+		msleep(20);
+		gpio_direction_output(wcd9xxx->reset_gpio, 1);
+		msleep(20);
+	}
+	return 0;
+}
+
+static void wcd9xxx_free_reset(struct wcd9xxx *wcd9xxx)
+{
+	if (wcd9xxx->reset_gpio) {
+		gpio_free(wcd9xxx->reset_gpio);
+		wcd9xxx->reset_gpio = 0;
+	}
+}
+
+static int wcd9xxx_device_init(struct wcd9xxx *wcd9xxx, int irq)
+{
+	int ret;
+	u8 idbyte_0, idbyte_1, idbyte_2, idbyte_3;
+	struct mfd_cell *wcd9xxx_dev = NULL;
+	int wcd9xxx_dev_size = 0;
+
+	mutex_init(&wcd9xxx->io_lock);
+	mutex_init(&wcd9xxx->xfer_lock);
+
+	mutex_init(&wcd9xxx->pm_lock);
+	wcd9xxx->wlock_holders = 0;
+	wcd9xxx->pm_state = WCD9XXX_PM_SLEEPABLE;
+	init_waitqueue_head(&wcd9xxx->pm_wq);
+	wake_lock_init(&wcd9xxx->wlock, WAKE_LOCK_IDLE, "wcd9310-irq");
+
+	dev_set_drvdata(wcd9xxx->dev, wcd9xxx);
+
+	wcd9xxx_bring_up(wcd9xxx);
+
+	ret = wcd9xxx_irq_init(wcd9xxx);
+	if (ret) {
+		pr_err("IRQ initialization failed\n");
+		goto err;
+	}
+
+	idbyte_0 = wcd9xxx_reg_read(wcd9xxx, WCD9XXX_A_CHIP_ID_BYTE_0);
+	idbyte_1 = wcd9xxx_reg_read(wcd9xxx, WCD9XXX_A_CHIP_ID_BYTE_1);
+	idbyte_2 = wcd9xxx_reg_read(wcd9xxx, WCD9XXX_A_CHIP_ID_BYTE_2);
+	idbyte_3 = wcd9xxx_reg_read(wcd9xxx, WCD9XXX_A_CHIP_ID_BYTE_3);
+
+	wcd9xxx->version = wcd9xxx_reg_read(wcd9xxx,
+			WCD9XXX_A_CHIP_VERSION) & 0x1F;
+	pr_info("%s : Codec version %u initialized\n",
+		__func__, wcd9xxx->version);
+	pr_info("idbyte_0[%08x] idbyte_1[%08x] idbyte_2[%08x] idbyte_3[%08x]\n",
+			idbyte_0, idbyte_1, idbyte_2, idbyte_3);
+
+	if (wcd9xxx->slim != NULL) {
+		if (!strncmp(wcd9xxx->slim->name, "tabla", 5)) {
+			if (TABLA_IS_1_X(wcd9xxx->version)) {
+				wcd9xxx_dev = tabla1x_devs;
+				wcd9xxx_dev_size = ARRAY_SIZE(tabla1x_devs);
+			} else {
+				wcd9xxx_dev = tabla_devs;
+				wcd9xxx_dev_size = ARRAY_SIZE(tabla_devs);
+			}
+		} else {
+			wcd9xxx_dev = sitar_devs;
+			wcd9xxx_dev_size = ARRAY_SIZE(sitar_devs);
+		}
+	} else {
+		/* Need to add here check for Tabla.
+		 * For now the read of version takes
+		 * care of now only tabla.
+		 */
+		pr_debug("%s : Read codec version using I2C\n",	__func__);
+		if (TABLA_IS_1_X(wcd9xxx->version)) {
+			wcd9xxx_dev = tabla1x_devs;
+			wcd9xxx_dev_size = ARRAY_SIZE(tabla1x_devs);
+		} else if (TABLA_IS_2_0(wcd9xxx->version)) {
+			wcd9xxx_dev = tabla_devs;
+			wcd9xxx_dev_size = ARRAY_SIZE(tabla_devs);
+		} else {
+			wcd9xxx_dev = sitar_devs;
+			wcd9xxx_dev_size = ARRAY_SIZE(sitar_devs);
+		}
+	}
+
+	ret = mfd_add_devices(wcd9xxx->dev, -1,
+		      wcd9xxx_dev, wcd9xxx_dev_size,
+		      NULL, 0);
+	if (ret != 0) {
+		dev_err(wcd9xxx->dev, "Failed to add children: %d\n", ret);
+		goto err_irq;
+	}
+	return ret;
+err_irq:
+	wcd9xxx_irq_exit(wcd9xxx);
+err:
+	wcd9xxx_bring_down(wcd9xxx);
+	wake_lock_destroy(&wcd9xxx->wlock);
+	mutex_destroy(&wcd9xxx->pm_lock);
+	mutex_destroy(&wcd9xxx->io_lock);
+	mutex_destroy(&wcd9xxx->xfer_lock);
+	return ret;
+}
+
+static void wcd9xxx_device_exit(struct wcd9xxx *wcd9xxx)
+{
+	wcd9xxx_irq_exit(wcd9xxx);
+	wcd9xxx_bring_down(wcd9xxx);
+	wcd9xxx_free_reset(wcd9xxx);
+	mutex_destroy(&wcd9xxx->pm_lock);
+	wake_lock_destroy(&wcd9xxx->wlock);
+	mutex_destroy(&wcd9xxx->io_lock);
+	mutex_destroy(&wcd9xxx->xfer_lock);
+	if (wcd9xxx_intf == WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+		slim_remove_device(wcd9xxx->slim_slave);
+	kfree(wcd9xxx);
+}
+
+
+#ifdef CONFIG_DEBUG_FS
+struct wcd9xxx *debugCodec;
+
+static struct dentry *debugfs_wcd9xxx_dent;
+static struct dentry *debugfs_peek;
+static struct dentry *debugfs_poke;
+
+static unsigned char read_data;
+
+static int codec_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static int get_parameters(char *buf, long int *param1, int num_of_par)
+{
+	char *token;
+	int base, cnt;
+
+	token = strsep(&buf, " ");
+
+	for (cnt = 0; cnt < num_of_par; cnt++) {
+		if (token != NULL) {
+			if ((token[1] == 'x') || (token[1] == 'X'))
+				base = 16;
+			else
+				base = 10;
+
+			if (strict_strtoul(token, base, &param1[cnt]) != 0)
+				return -EINVAL;
+
+			token = strsep(&buf, " ");
+		} else
+			return -EINVAL;
+	}
+	return 0;
+}
+
+static ssize_t codec_debug_read(struct file *file, char __user *ubuf,
+				size_t count, loff_t *ppos)
+{
+	char lbuf[8];
+
+	snprintf(lbuf, sizeof(lbuf), "0x%x\n", read_data);
+	return simple_read_from_buffer(ubuf, count, ppos, lbuf,
+		strnlen(lbuf, 7));
+}
+
+
+static ssize_t codec_debug_write(struct file *filp,
+	const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+	char *access_str = filp->private_data;
+	char lbuf[32];
+	int rc;
+	long int param[5];
+
+	if (cnt > sizeof(lbuf) - 1)
+		return -EINVAL;
+
+	rc = copy_from_user(lbuf, ubuf, cnt);
+	if (rc)
+		return -EFAULT;
+
+	lbuf[cnt] = '\0';
+
+	if (!strncmp(access_str, "poke", 6)) {
+		/* write */
+		rc = get_parameters(lbuf, param, 2);
+		if ((param[0] <= 0x3FF) && (param[1] <= 0xFF) &&
+			(rc == 0))
+			wcd9xxx_interface_reg_write(debugCodec, param[0],
+				param[1]);
+		else
+			rc = -EINVAL;
+	} else if (!strncmp(access_str, "peek", 6)) {
+		/* read */
+		rc = get_parameters(lbuf, param, 1);
+		if ((param[0] <= 0x3FF) && (rc == 0))
+			read_data = wcd9xxx_interface_reg_read(debugCodec,
+				param[0]);
+		else
+			rc = -EINVAL;
+	}
+
+	if (rc == 0)
+		rc = cnt;
+	else
+		pr_err("%s: rc = %d\n", __func__, rc);
+
+	return rc;
+}
+
+static const struct file_operations codec_debug_ops = {
+	.open = codec_debug_open,
+	.write = codec_debug_write,
+	.read = codec_debug_read
+};
+#endif
+
+static int wcd9xxx_enable_supplies(struct wcd9xxx *wcd9xxx,
+				struct wcd9xxx_pdata *pdata)
+{
+	int ret;
+	int i;
+	wcd9xxx->supplies = kzalloc(sizeof(struct regulator_bulk_data) *
+				   ARRAY_SIZE(pdata->regulator),
+				   GFP_KERNEL);
+	if (!wcd9xxx->supplies) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(pdata->regulator); i++)
+		wcd9xxx->supplies[i].supply = pdata->regulator[i].name;
+
+	ret = regulator_bulk_get(wcd9xxx->dev, ARRAY_SIZE(pdata->regulator),
+				 wcd9xxx->supplies);
+	if (ret != 0) {
+		dev_err(wcd9xxx->dev, "Failed to get supplies: err = %d\n",
+							ret);
+		goto err_supplies;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(pdata->regulator); i++) {
+		ret = regulator_set_voltage(wcd9xxx->supplies[i].consumer,
+			pdata->regulator[i].min_uV, pdata->regulator[i].max_uV);
+		if (ret) {
+			pr_err("%s: Setting regulator voltage failed for "
+				"regulator %s err = %d\n", __func__,
+				wcd9xxx->supplies[i].supply, ret);
+			goto err_get;
+		}
+
+		ret = regulator_set_optimum_mode(wcd9xxx->supplies[i].consumer,
+			pdata->regulator[i].optimum_uA);
+		if (ret < 0) {
+			pr_err("%s: Setting regulator optimum mode failed for "
+				"regulator %s err = %d\n", __func__,
+				wcd9xxx->supplies[i].supply, ret);
+			goto err_get;
+		}
+	}
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(pdata->regulator),
+				    wcd9xxx->supplies);
+	if (ret != 0) {
+		dev_err(wcd9xxx->dev, "Failed to enable supplies: err = %d\n",
+				ret);
+		goto err_configure;
+	}
+	return ret;
+
+err_configure:
+	for (i = 0; i < ARRAY_SIZE(pdata->regulator); i++) {
+		regulator_set_voltage(wcd9xxx->supplies[i].consumer, 0,
+			pdata->regulator[i].max_uV);
+		regulator_set_optimum_mode(wcd9xxx->supplies[i].consumer, 0);
+	}
+err_get:
+	regulator_bulk_free(ARRAY_SIZE(pdata->regulator), wcd9xxx->supplies);
+err_supplies:
+	kfree(wcd9xxx->supplies);
+err:
+	return ret;
+}
+
+static void wcd9xxx_disable_supplies(struct wcd9xxx *wcd9xxx,
+				     struct wcd9xxx_pdata *pdata)
+{
+	int i;
+
+	regulator_bulk_disable(ARRAY_SIZE(pdata->regulator),
+				    wcd9xxx->supplies);
+	for (i = 0; i < ARRAY_SIZE(pdata->regulator); i++) {
+		regulator_set_voltage(wcd9xxx->supplies[i].consumer, 0,
+			pdata->regulator[i].max_uV);
+		regulator_set_optimum_mode(wcd9xxx->supplies[i].consumer, 0);
+	}
+	regulator_bulk_free(ARRAY_SIZE(pdata->regulator), wcd9xxx->supplies);
+	kfree(wcd9xxx->supplies);
+}
+
+int wcd9xxx_get_intf_type(void)
+{
+	return wcd9xxx_intf;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_get_intf_type);
+
+struct wcd9xxx_i2c *get_i2c_wcd9xxx_device_info(u16 reg)
+{
+	u16 mask = 0x0f00;
+	int value = 0;
+	struct wcd9xxx_i2c *wcd9xxx = NULL;
+	value = ((reg & mask) >> 8) & 0x000f;
+	switch (value) {
+	case 0:
+		wcd9xxx = &wcd9xxx_modules[0];
+		break;
+	case 1:
+		wcd9xxx = &wcd9xxx_modules[1];
+		break;
+	case 2:
+		wcd9xxx = &wcd9xxx_modules[2];
+		break;
+	case 3:
+		wcd9xxx = &wcd9xxx_modules[3];
+		break;
+	default:
+		break;
+	}
+	return wcd9xxx;
+}
+
+int wcd9xxx_i2c_write_device(u16 reg, u8 *value,
+				u32 bytes)
+{
+
+	struct i2c_msg *msg;
+	int ret = 0;
+	u8 reg_addr = 0;
+	u8 data[bytes + 1];
+	struct wcd9xxx_i2c *wcd9xxx;
+
+	wcd9xxx = get_i2c_wcd9xxx_device_info(reg);
+	if (wcd9xxx == NULL || wcd9xxx->client == NULL) {
+		pr_err("failed to get device info\n");
+		return -ENODEV;
+	}
+	reg_addr = (u8)reg;
+	msg = &wcd9xxx->xfer_msg[0];
+	msg->addr = wcd9xxx->client->addr;
+	msg->len = bytes + 1;
+	msg->flags = 0;
+	data[0] = reg;
+	data[1] = *value;
+	msg->buf = data;
+	ret = i2c_transfer(wcd9xxx->client->adapter, wcd9xxx->xfer_msg, 1);
+	/* Try again if the write fails */
+	if (ret != 1) {
+		ret = i2c_transfer(wcd9xxx->client->adapter,
+						wcd9xxx->xfer_msg, 1);
+		if (ret != 1) {
+			pr_err("failed to write the device\n");
+			return ret;
+		}
+	}
+	pr_debug("write sucess register = %x val = %x\n", reg, data[1]);
+	return 0;
+}
+
+
+int wcd9xxx_i2c_read_device(unsigned short reg,
+				  int bytes, unsigned char *dest)
+{
+	struct i2c_msg *msg;
+	int ret = 0;
+	u8 reg_addr = 0;
+	struct wcd9xxx_i2c *wcd9xxx;
+	u8 i = 0;
+
+	wcd9xxx = get_i2c_wcd9xxx_device_info(reg);
+	if (wcd9xxx == NULL || wcd9xxx->client == NULL) {
+		pr_err("failed to get device info\n");
+		return -ENODEV;
+	}
+	for (i = 0; i < bytes; i++) {
+		reg_addr = (u8)reg++;
+		msg = &wcd9xxx->xfer_msg[0];
+		msg->addr = wcd9xxx->client->addr;
+		msg->len = 1;
+		msg->flags = 0;
+		msg->buf = &reg_addr;
+
+		msg = &wcd9xxx->xfer_msg[1];
+		msg->addr = wcd9xxx->client->addr;
+		msg->len = 1;
+		msg->flags = I2C_M_RD;
+		msg->buf = dest++;
+		ret = i2c_transfer(wcd9xxx->client->adapter,
+				wcd9xxx->xfer_msg, 2);
+
+		/* Try again if read fails first time */
+		if (ret != 2) {
+			ret = i2c_transfer(wcd9xxx->client->adapter,
+							wcd9xxx->xfer_msg, 2);
+			if (ret != 2) {
+				pr_err("failed to read wcd9xxx register\n");
+				return ret;
+			}
+		}
+	}
+	return 0;
+}
+
+int wcd9xxx_i2c_read(struct wcd9xxx *wcd9xxx, unsigned short reg,
+			int bytes, void *dest, bool interface_reg)
+{
+	return wcd9xxx_i2c_read_device(reg, bytes, dest);
+}
+
+int wcd9xxx_i2c_write(struct wcd9xxx *wcd9xxx, unsigned short reg,
+			 int bytes, void *src, bool interface_reg)
+{
+	return wcd9xxx_i2c_write_device(reg, src, bytes);
+}
+
+static int __devinit wcd9xxx_i2c_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct wcd9xxx *wcd9xxx;
+	struct wcd9xxx_pdata *pdata = client->dev.platform_data;
+	int val = 0;
+	int ret = 0;
+	static int device_id;
+
+	if (wcd9xxx_intf == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		pr_info("tabla card is already detected in slimbus mode\n");
+		return -ENODEV;
+	}
+	if (device_id > 0) {
+		wcd9xxx_modules[device_id++].client = client;
+		pr_info("probe for other slaves devices of tabla\n");
+		return ret;
+	}
+
+	wcd9xxx = kzalloc(sizeof(struct wcd9xxx), GFP_KERNEL);
+	if (wcd9xxx == NULL) {
+		pr_err("%s: error, allocation failed\n", __func__);
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	if (!pdata) {
+		dev_dbg(&client->dev, "no platform data?\n");
+		ret = -EINVAL;
+		goto fail;
+	}
+	if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) {
+		dev_dbg(&client->dev, "can't talk I2C?\n");
+		ret = -EIO;
+		goto fail;
+	}
+	dev_set_drvdata(&client->dev, wcd9xxx);
+	wcd9xxx->dev = &client->dev;
+	wcd9xxx->reset_gpio = pdata->reset_gpio;
+	ret = wcd9xxx_enable_supplies(wcd9xxx, pdata);
+	if (ret) {
+		pr_err("%s: Fail to enable Codec supplies\n", __func__);
+		goto err_codec;
+	}
+
+	usleep_range(5, 5);
+	ret = wcd9xxx_reset(wcd9xxx);
+	if (ret) {
+		pr_err("%s: Resetting Codec failed\n", __func__);
+		goto err_supplies;
+	}
+	wcd9xxx_modules[device_id++].client = client;
+
+	wcd9xxx->read_dev = wcd9xxx_i2c_read;
+	wcd9xxx->write_dev = wcd9xxx_i2c_write;
+	wcd9xxx->irq = pdata->irq;
+	wcd9xxx->irq_base = pdata->irq_base;
+
+	/*read the tabla status before initializing the device type*/
+	ret = wcd9xxx_read(wcd9xxx, WCD9XXX_A_CHIP_STATUS, 1, &val, 0);
+	if ((ret < 0) || (val != WCD9XXX_I2C_MODE)) {
+		pr_err("failed to read the wcd9xxx status\n");
+		goto err_device_init;
+	}
+
+	ret = wcd9xxx_device_init(wcd9xxx, wcd9xxx->irq);
+	if (ret) {
+		pr_err("%s: error, initializing device failed\n", __func__);
+		goto err_device_init;
+	}
+	wcd9xxx_intf = WCD9XXX_INTERFACE_TYPE_I2C;
+
+	return ret;
+err_device_init:
+	wcd9xxx_free_reset(wcd9xxx);
+err_supplies:
+	wcd9xxx_disable_supplies(wcd9xxx, pdata);
+err_codec:
+	kfree(wcd9xxx);
+fail:
+	return ret;
+}
+
+static int __devexit wcd9xxx_i2c_remove(struct i2c_client *client)
+{
+	struct wcd9xxx *wcd9xxx;
+	struct wcd9xxx_pdata *pdata = client->dev.platform_data;
+	pr_debug("exit\n");
+	wcd9xxx = dev_get_drvdata(&client->dev);
+	wcd9xxx_disable_supplies(wcd9xxx, pdata);
+	wcd9xxx_device_exit(wcd9xxx);
+	return 0;
+}
+
+static int wcd9xxx_slim_probe(struct slim_device *slim)
+{
+	struct wcd9xxx *wcd9xxx;
+	struct wcd9xxx_pdata *pdata;
+	int ret = 0;
+	int sgla_retry_cnt;
+
+	dev_info(&slim->dev, "Initialized slim device %s\n", slim->name);
+	pdata = slim->dev.platform_data;
+
+	if (!pdata) {
+		dev_err(&slim->dev, "Error, no platform data\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	wcd9xxx = kzalloc(sizeof(struct wcd9xxx), GFP_KERNEL);
+	if (wcd9xxx == NULL) {
+		pr_err("%s: error, allocation failed\n", __func__);
+		ret = -ENOMEM;
+		goto err;
+	}
+	if (!slim->ctrl) {
+		pr_err("Error, no SLIMBUS control data\n");
+		ret = -EINVAL;
+		goto err_codec;
+	}
+	wcd9xxx->slim = slim;
+	slim_set_clientdata(slim, wcd9xxx);
+	wcd9xxx->reset_gpio = pdata->reset_gpio;
+	wcd9xxx->dev = &slim->dev;
+
+	ret = wcd9xxx_enable_supplies(wcd9xxx, pdata);
+	if (ret)
+		goto err_codec;
+	usleep_range(5, 5);
+
+	ret = wcd9xxx_reset(wcd9xxx);
+	if (ret) {
+		pr_err("%s: Resetting Codec failed\n", __func__);
+		goto err_supplies;
+	}
+
+	ret = slim_get_logical_addr(wcd9xxx->slim, wcd9xxx->slim->e_addr,
+		ARRAY_SIZE(wcd9xxx->slim->e_addr), &wcd9xxx->slim->laddr);
+	if (ret) {
+		pr_err("fail to get slimbus logical address %d\n", ret);
+		goto err_reset;
+	}
+	wcd9xxx->read_dev = wcd9xxx_slim_read_device;
+	wcd9xxx->write_dev = wcd9xxx_slim_write_device;
+	wcd9xxx->irq = pdata->irq;
+	wcd9xxx->irq_base = pdata->irq_base;
+	wcd9xxx_pgd_la = wcd9xxx->slim->laddr;
+
+	if (pdata->num_irqs < TABLA_NUM_IRQS) {
+		pr_err("%s: Error, not enough interrupt lines allocated\n",
+			__func__);
+		goto err_reset;
+	}
+
+	wcd9xxx->slim_slave = &pdata->slimbus_slave_device;
+
+	ret = slim_add_device(slim->ctrl, wcd9xxx->slim_slave);
+	if (ret) {
+		pr_err("%s: error, adding SLIMBUS device failed\n", __func__);
+		goto err_reset;
+	}
+
+	sgla_retry_cnt = 0;
+
+	while (1) {
+		ret = slim_get_logical_addr(wcd9xxx->slim_slave,
+			wcd9xxx->slim_slave->e_addr,
+			ARRAY_SIZE(wcd9xxx->slim_slave->e_addr),
+			&wcd9xxx->slim_slave->laddr);
+		if (ret) {
+			if (sgla_retry_cnt++ < WCD9XXX_SLIM_GLA_MAX_RETRIES) {
+				/* Give SLIMBUS slave time to report present
+				   and be ready.
+				 */
+				usleep_range(1000, 1000);
+				pr_debug("%s: retry slim_get_logical_addr()\n",
+					__func__);
+				continue;
+			}
+			pr_err("fail to get slimbus slave logical address"
+				" %d\n", ret);
+			goto err_slim_add;
+		}
+		break;
+	}
+	wcd9xxx_inf_la = wcd9xxx->slim_slave->laddr;
+	wcd9xxx_intf = WCD9XXX_INTERFACE_TYPE_SLIMBUS;
+
+	ret = wcd9xxx_device_init(wcd9xxx, wcd9xxx->irq);
+	if (ret) {
+		pr_err("%s: error, initializing device failed\n", __func__);
+		goto err_slim_add;
+	}
+	wcd9xxx_init_slimslave(wcd9xxx, wcd9xxx_pgd_la);
+#ifdef CONFIG_DEBUG_FS
+	debugCodec = wcd9xxx;
+
+	debugfs_wcd9xxx_dent = debugfs_create_dir
+		("wcd9310_slimbus_interface_device", 0);
+	if (!IS_ERR(debugfs_wcd9xxx_dent)) {
+		debugfs_peek = debugfs_create_file("peek",
+		S_IFREG | S_IRUGO, debugfs_wcd9xxx_dent,
+		(void *) "peek", &codec_debug_ops);
+
+		debugfs_poke = debugfs_create_file("poke",
+		S_IFREG | S_IRUGO, debugfs_wcd9xxx_dent,
+		(void *) "poke", &codec_debug_ops);
+	}
+#endif
+
+	return ret;
+
+err_slim_add:
+	slim_remove_device(wcd9xxx->slim_slave);
+err_reset:
+	wcd9xxx_free_reset(wcd9xxx);
+err_supplies:
+	wcd9xxx_disable_supplies(wcd9xxx, pdata);
+err_codec:
+	kfree(wcd9xxx);
+err:
+	return ret;
+}
+static int wcd9xxx_slim_remove(struct slim_device *pdev)
+{
+	struct wcd9xxx *wcd9xxx;
+	struct wcd9xxx_pdata *pdata = pdev->dev.platform_data;
+
+#ifdef CONFIG_DEBUG_FS
+	debugfs_remove(debugfs_peek);
+	debugfs_remove(debugfs_poke);
+	debugfs_remove(debugfs_wcd9xxx_dent);
+#endif
+	wcd9xxx = slim_get_devicedata(pdev);
+	wcd9xxx_deinit_slimslave(wcd9xxx);
+	slim_remove_device(wcd9xxx->slim_slave);
+	wcd9xxx_disable_supplies(wcd9xxx, pdata);
+	wcd9xxx_device_exit(wcd9xxx);
+	return 0;
+}
+
+static int wcd9xxx_resume(struct wcd9xxx *wcd9xxx)
+{
+	int ret = 0;
+
+	pr_debug("%s: enter\n", __func__);
+	mutex_lock(&wcd9xxx->pm_lock);
+	if (wcd9xxx->pm_state == WCD9XXX_PM_ASLEEP) {
+		pr_debug("%s: resuming system, state %d, wlock %d\n", __func__,
+			 wcd9xxx->pm_state, wcd9xxx->wlock_holders);
+		wcd9xxx->pm_state = WCD9XXX_PM_SLEEPABLE;
+	} else {
+		pr_warn("%s: system is already awake, state %d wlock %d\n",
+			__func__, wcd9xxx->pm_state, wcd9xxx->wlock_holders);
+	}
+	mutex_unlock(&wcd9xxx->pm_lock);
+	wake_up_all(&wcd9xxx->pm_wq);
+
+	return ret;
+}
+
+static int wcd9xxx_slim_resume(struct slim_device *sldev)
+{
+	struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev);
+	return wcd9xxx_resume(wcd9xxx);
+}
+
+static int wcd9xxx_i2c_resume(struct i2c_client *i2cdev)
+{
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(&i2cdev->dev);
+	if (wcd9xxx)
+		return wcd9xxx_resume(wcd9xxx);
+	else
+		return 0;
+}
+
+static int wcd9xxx_suspend(struct wcd9xxx *wcd9xxx, pm_message_t pmesg)
+{
+	int ret = 0;
+
+	pr_debug("%s: enter\n", __func__);
+	/* wake_lock() can be called after this suspend chain call started.
+	 * thus suspend can be called while wlock is being held */
+	mutex_lock(&wcd9xxx->pm_lock);
+	if (wcd9xxx->pm_state == WCD9XXX_PM_SLEEPABLE) {
+		pr_debug("%s: suspending system, state %d, wlock %d\n",
+			 __func__, wcd9xxx->pm_state, wcd9xxx->wlock_holders);
+		wcd9xxx->pm_state = WCD9XXX_PM_ASLEEP;
+	} else if (wcd9xxx->pm_state == WCD9XXX_PM_AWAKE) {
+		/* unlock to wait for pm_state == WCD9XXX_PM_SLEEPABLE
+		 * then set to WCD9XXX_PM_ASLEEP */
+		pr_debug("%s: waiting to suspend system, state %d, wlock %d\n",
+			 __func__, wcd9xxx->pm_state, wcd9xxx->wlock_holders);
+		mutex_unlock(&wcd9xxx->pm_lock);
+		if (!(wait_event_timeout(wcd9xxx->pm_wq,
+					 wcd9xxx_pm_cmpxchg(wcd9xxx,
+						  WCD9XXX_PM_SLEEPABLE,
+						  WCD9XXX_PM_ASLEEP) ==
+							WCD9XXX_PM_SLEEPABLE,
+					 HZ))) {
+			pr_debug("%s: suspend failed state %d, wlock %d\n",
+				 __func__, wcd9xxx->pm_state,
+				 wcd9xxx->wlock_holders);
+			ret = -EBUSY;
+		} else {
+			pr_debug("%s: done, state %d, wlock %d\n", __func__,
+				 wcd9xxx->pm_state, wcd9xxx->wlock_holders);
+		}
+		mutex_lock(&wcd9xxx->pm_lock);
+	} else if (wcd9xxx->pm_state == WCD9XXX_PM_ASLEEP) {
+		pr_warn("%s: system is already suspended, state %d, wlock %dn",
+			__func__, wcd9xxx->pm_state, wcd9xxx->wlock_holders);
+	}
+	mutex_unlock(&wcd9xxx->pm_lock);
+
+	return ret;
+}
+
+static int wcd9xxx_slim_suspend(struct slim_device *sldev, pm_message_t pmesg)
+{
+	struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev);
+	return wcd9xxx_suspend(wcd9xxx, pmesg);
+}
+
+static int wcd9xxx_i2c_suspend(struct i2c_client *i2cdev, pm_message_t pmesg)
+{
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(&i2cdev->dev);
+	if (wcd9xxx)
+		return wcd9xxx_suspend(wcd9xxx, pmesg);
+	else
+		return 0;
+}
+
+static const struct slim_device_id sitar_slimtest_id[] = {
+	{"sitar-slim", 0},
+	{}
+};
+static struct slim_driver sitar_slim_driver = {
+	.driver = {
+		.name = "sitar-slim",
+		.owner = THIS_MODULE,
+	},
+	.probe = wcd9xxx_slim_probe,
+	.remove = wcd9xxx_slim_remove,
+	.id_table = sitar_slimtest_id,
+	.resume = wcd9xxx_slim_resume,
+	.suspend = wcd9xxx_slim_suspend,
+};
+
+static const struct slim_device_id sitar1p1_slimtest_id[] = {
+	{"sitar1p1-slim", 0},
+	{}
+};
+static struct slim_driver sitar1p1_slim_driver = {
+	.driver = {
+		.name = "sitar1p1-slim",
+		.owner = THIS_MODULE,
+	},
+	.probe = wcd9xxx_slim_probe,
+	.remove = wcd9xxx_slim_remove,
+	.id_table = sitar1p1_slimtest_id,
+	.resume = wcd9xxx_slim_resume,
+	.suspend = wcd9xxx_slim_suspend,
+};
+
+static const struct slim_device_id slimtest_id[] = {
+	{"tabla-slim", 0},
+	{}
+};
+
+static struct slim_driver tabla_slim_driver = {
+	.driver = {
+		.name = "tabla-slim",
+		.owner = THIS_MODULE,
+	},
+	.probe = wcd9xxx_slim_probe,
+	.remove = wcd9xxx_slim_remove,
+	.id_table = slimtest_id,
+	.resume = wcd9xxx_slim_resume,
+	.suspend = wcd9xxx_slim_suspend,
+};
+
+static const struct slim_device_id slimtest2x_id[] = {
+	{"tabla2x-slim", 0},
+	{}
+};
+
+static struct slim_driver tabla2x_slim_driver = {
+	.driver = {
+		.name = "tabla2x-slim",
+		.owner = THIS_MODULE,
+	},
+	.probe = wcd9xxx_slim_probe,
+	.remove = wcd9xxx_slim_remove,
+	.id_table = slimtest2x_id,
+	.resume = wcd9xxx_slim_resume,
+	.suspend = wcd9xxx_slim_suspend,
+};
+
+#define TABLA_I2C_TOP_LEVEL 0
+#define TABLA_I2C_ANALOG       1
+#define TABLA_I2C_DIGITAL_1    2
+#define TABLA_I2C_DIGITAL_2    3
+
+static struct i2c_device_id tabla_id_table[] = {
+	{"tabla top level", TABLA_I2C_TOP_LEVEL},
+	{"tabla analog", TABLA_I2C_TOP_LEVEL},
+	{"tabla digital1", TABLA_I2C_TOP_LEVEL},
+	{"tabla digital2", TABLA_I2C_TOP_LEVEL},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, tabla_id_table);
+
+static struct i2c_driver tabla_i2c_driver = {
+	.driver                 = {
+		.owner          =       THIS_MODULE,
+		.name           =       "tabla-i2c-core",
+	},
+	.id_table               =       tabla_id_table,
+	.probe                  =       wcd9xxx_i2c_probe,
+	.remove                 =       __devexit_p(wcd9xxx_i2c_remove),
+	.resume	= wcd9xxx_i2c_resume,
+	.suspend = wcd9xxx_i2c_suspend,
+};
+
+static int __init wcd9xxx_init(void)
+{
+	int ret1, ret2, ret3, ret4, ret5;
+
+	ret1 = slim_driver_register(&tabla_slim_driver);
+	if (ret1 != 0)
+		pr_err("Failed to register tabla SB driver: %d\n", ret1);
+
+	ret2 = slim_driver_register(&tabla2x_slim_driver);
+	if (ret2 != 0)
+		pr_err("Failed to register tabla2x SB driver: %d\n", ret2);
+
+	ret3 = i2c_add_driver(&tabla_i2c_driver);
+	if (ret3 != 0)
+		pr_err("failed to add the I2C driver\n");
+
+	ret4 = slim_driver_register(&sitar_slim_driver);
+	if (ret4 != 0)
+		pr_err("Failed to register sitar SB driver: %d\n", ret4);
+
+	ret5 = slim_driver_register(&sitar1p1_slim_driver);
+	if (ret5 != 0)
+		pr_err("Failed to register sitar SB driver: %d\n", ret5);
+
+	return (ret1 && ret2 && ret3 && ret4 && ret5) ? -1 : 0;
+}
+module_init(wcd9xxx_init);
+
+static void __exit wcd9xxx_exit(void)
+{
+}
+module_exit(wcd9xxx_exit);
+
+MODULE_DESCRIPTION("Codec core driver");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/wcd9xxx-irq.c b/drivers/mfd/wcd9xxx-irq.c
new file mode 100644
index 0000000..ba3c1e8
--- /dev/null
+++ b/drivers/mfd/wcd9xxx-irq.c
@@ -0,0 +1,307 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/irq.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+#include <linux/mfd/wcd9xxx/wcd9310_registers.h>
+#include <linux/interrupt.h>
+
+#define BYTE_BIT_MASK(nr)		(1UL << ((nr) % BITS_PER_BYTE))
+#define BIT_BYTE(nr)			((nr) / BITS_PER_BYTE)
+
+struct wcd9xxx_irq {
+	bool level;
+};
+
+static struct wcd9xxx_irq wcd9xxx_irqs[TABLA_NUM_IRQS] = {
+	[0] = { .level = 1},
+/* All other wcd9xxx interrupts are edge triggered */
+};
+
+static inline int irq_to_wcd9xxx_irq(struct wcd9xxx *wcd9xxx, int irq)
+{
+	return irq - wcd9xxx->irq_base;
+}
+
+static void wcd9xxx_irq_lock(struct irq_data *data)
+{
+	struct wcd9xxx *wcd9xxx = irq_data_get_irq_chip_data(data);
+	mutex_lock(&wcd9xxx->irq_lock);
+}
+
+static void wcd9xxx_irq_sync_unlock(struct irq_data *data)
+{
+	struct wcd9xxx *wcd9xxx = irq_data_get_irq_chip_data(data);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(wcd9xxx->irq_masks_cur); i++) {
+		/* If there's been a change in the mask write it back
+		 * to the hardware.
+		 */
+		if (wcd9xxx->irq_masks_cur[i] != wcd9xxx->irq_masks_cache[i]) {
+			wcd9xxx->irq_masks_cache[i] = wcd9xxx->irq_masks_cur[i];
+			wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_MASK0+i,
+				wcd9xxx->irq_masks_cur[i]);
+		}
+	}
+
+	mutex_unlock(&wcd9xxx->irq_lock);
+}
+
+static void wcd9xxx_irq_enable(struct irq_data *data)
+{
+	struct wcd9xxx *wcd9xxx = irq_data_get_irq_chip_data(data);
+	int wcd9xxx_irq = irq_to_wcd9xxx_irq(wcd9xxx, data->irq);
+	wcd9xxx->irq_masks_cur[BIT_BYTE(wcd9xxx_irq)] &=
+		~(BYTE_BIT_MASK(wcd9xxx_irq));
+}
+
+static void wcd9xxx_irq_disable(struct irq_data *data)
+{
+	struct wcd9xxx *wcd9xxx = irq_data_get_irq_chip_data(data);
+	int wcd9xxx_irq = irq_to_wcd9xxx_irq(wcd9xxx, data->irq);
+	wcd9xxx->irq_masks_cur[BIT_BYTE(wcd9xxx_irq)]
+			|= BYTE_BIT_MASK(wcd9xxx_irq);
+}
+
+static struct irq_chip wcd9xxx_irq_chip = {
+	.name = "wcd9xxx",
+	.irq_bus_lock = wcd9xxx_irq_lock,
+	.irq_bus_sync_unlock = wcd9xxx_irq_sync_unlock,
+	.irq_disable = wcd9xxx_irq_disable,
+	.irq_enable = wcd9xxx_irq_enable,
+};
+
+enum wcd9xxx_pm_state wcd9xxx_pm_cmpxchg(struct wcd9xxx *wcd9xxx,
+		enum wcd9xxx_pm_state o,
+		enum wcd9xxx_pm_state n)
+{
+	enum wcd9xxx_pm_state old;
+	mutex_lock(&wcd9xxx->pm_lock);
+	old = wcd9xxx->pm_state;
+	if (old == o)
+		wcd9xxx->pm_state = n;
+	mutex_unlock(&wcd9xxx->pm_lock);
+	return old;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_pm_cmpxchg);
+
+bool wcd9xxx_lock_sleep(struct wcd9xxx *wcd9xxx)
+{
+	enum wcd9xxx_pm_state os;
+
+	/* wcd9xxx_{lock/unlock}_sleep will be called by wcd9xxx_irq_thread
+	 * and its subroutines only motly.
+	 * but btn0_lpress_fn is not wcd9xxx_irq_thread's subroutine and
+	 * it can race with wcd9xxx_irq_thread.
+	 * so need to embrace wlock_holders with mutex.
+	 */
+	mutex_lock(&wcd9xxx->pm_lock);
+	if (wcd9xxx->wlock_holders++ == 0) {
+		pr_debug("%s: holding wake lock\n", __func__);
+		wake_lock(&wcd9xxx->wlock);
+	}
+	mutex_unlock(&wcd9xxx->pm_lock);
+	if (!wait_event_timeout(wcd9xxx->pm_wq,
+			((os = wcd9xxx_pm_cmpxchg(wcd9xxx, WCD9XXX_PM_SLEEPABLE,
+						WCD9XXX_PM_AWAKE)) ==
+						    WCD9XXX_PM_SLEEPABLE ||
+			 (os == WCD9XXX_PM_AWAKE)),
+			5 * HZ)) {
+		pr_err("%s: system didn't resume within 5000ms, state %d, "
+		       "wlock %d\n", __func__, wcd9xxx->pm_state,
+		       wcd9xxx->wlock_holders);
+		WARN_ON(1);
+		wcd9xxx_unlock_sleep(wcd9xxx);
+		return false;
+	}
+	wake_up_all(&wcd9xxx->pm_wq);
+	return true;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_lock_sleep);
+
+void wcd9xxx_unlock_sleep(struct wcd9xxx *wcd9xxx)
+{
+	mutex_lock(&wcd9xxx->pm_lock);
+	if (--wcd9xxx->wlock_holders == 0) {
+		wcd9xxx->pm_state = WCD9XXX_PM_SLEEPABLE;
+		pr_debug("%s: releasing wake lock\n", __func__);
+		wake_unlock(&wcd9xxx->wlock);
+	}
+	mutex_unlock(&wcd9xxx->pm_lock);
+	wake_up_all(&wcd9xxx->pm_wq);
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_unlock_sleep);
+
+static void wcd9xxx_irq_dispatch(struct wcd9xxx *wcd9xxx, int irqbit)
+{
+	if ((irqbit <= TABLA_IRQ_MBHC_INSERTION) &&
+	    (irqbit >= TABLA_IRQ_MBHC_REMOVAL)) {
+		wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_CLEAR0 +
+				  BIT_BYTE(irqbit), BYTE_BIT_MASK(irqbit));
+		if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
+			wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_MODE, 0x02);
+		handle_nested_irq(wcd9xxx->irq_base + irqbit);
+	} else {
+		handle_nested_irq(wcd9xxx->irq_base + irqbit);
+		wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_CLEAR0 +
+				  BIT_BYTE(irqbit), BYTE_BIT_MASK(irqbit));
+		if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
+			wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_MODE, 0x02);
+	}
+}
+
+static irqreturn_t wcd9xxx_irq_thread(int irq, void *data)
+{
+	int ret;
+	struct wcd9xxx *wcd9xxx = data;
+	u8 status[WCD9XXX_NUM_IRQ_REGS];
+	int i;
+
+	if (unlikely(wcd9xxx_lock_sleep(wcd9xxx) == false)) {
+		dev_err(wcd9xxx->dev, "Failed to hold suspend\n");
+		return IRQ_NONE;
+	}
+	ret = wcd9xxx_bulk_read(wcd9xxx, TABLA_A_INTR_STATUS0,
+			       WCD9XXX_NUM_IRQ_REGS, status);
+	if (ret < 0) {
+		dev_err(wcd9xxx->dev, "Failed to read interrupt status: %d\n",
+			ret);
+		wcd9xxx_unlock_sleep(wcd9xxx);
+		return IRQ_NONE;
+	}
+	/* Apply masking */
+	for (i = 0; i < WCD9XXX_NUM_IRQ_REGS; i++)
+		status[i] &= ~wcd9xxx->irq_masks_cur[i];
+
+	/* Find out which interrupt was triggered and call that interrupt's
+	 * handler function
+	 */
+	if (status[BIT_BYTE(TABLA_IRQ_SLIMBUS)] &
+	    BYTE_BIT_MASK(TABLA_IRQ_SLIMBUS))
+		wcd9xxx_irq_dispatch(wcd9xxx, TABLA_IRQ_SLIMBUS);
+
+	/* Since codec has only one hardware irq line which is shared by
+	 * codec's different internal interrupts, so it's possible master irq
+	 * handler dispatches multiple nested irq handlers after breaking
+	 * order.  Dispatch MBHC interrupts order to follow MBHC state
+	 * machine's order */
+	for (i = TABLA_IRQ_MBHC_INSERTION; i >= TABLA_IRQ_MBHC_REMOVAL; i--) {
+		if (status[BIT_BYTE(i)] & BYTE_BIT_MASK(i))
+			wcd9xxx_irq_dispatch(wcd9xxx, i);
+	}
+	for (i = TABLA_IRQ_BG_PRECHARGE; i < TABLA_NUM_IRQS; i++) {
+		if (status[BIT_BYTE(i)] & BYTE_BIT_MASK(i))
+			wcd9xxx_irq_dispatch(wcd9xxx, i);
+	}
+	wcd9xxx_unlock_sleep(wcd9xxx);
+
+	return IRQ_HANDLED;
+}
+
+int wcd9xxx_irq_init(struct wcd9xxx *wcd9xxx)
+{
+	int ret;
+	unsigned int i, cur_irq;
+
+	mutex_init(&wcd9xxx->irq_lock);
+
+	if (!wcd9xxx->irq) {
+		dev_warn(wcd9xxx->dev,
+			 "No interrupt specified, no interrupts\n");
+		wcd9xxx->irq_base = 0;
+		return 0;
+	}
+
+	if (!wcd9xxx->irq_base) {
+		dev_err(wcd9xxx->dev,
+			"No interrupt base specified, no interrupts\n");
+		return 0;
+	}
+	/* Mask the individual interrupt sources */
+	for (i = 0, cur_irq = wcd9xxx->irq_base; i < TABLA_NUM_IRQS; i++,
+		cur_irq++) {
+
+		irq_set_chip_data(cur_irq, wcd9xxx);
+
+		if (wcd9xxx_irqs[i].level)
+			irq_set_chip_and_handler(cur_irq, &wcd9xxx_irq_chip,
+					 handle_level_irq);
+		else
+			irq_set_chip_and_handler(cur_irq, &wcd9xxx_irq_chip,
+					 handle_edge_irq);
+
+		irq_set_nested_thread(cur_irq, 1);
+
+		/* ARM needs us to explicitly flag the IRQ as valid
+		 * and will set them noprobe when we do so. */
+#ifdef CONFIG_ARM
+		set_irq_flags(cur_irq, IRQF_VALID);
+#else
+		set_irq_noprobe(cur_irq);
+#endif
+
+		wcd9xxx->irq_masks_cur[BIT_BYTE(i)] |= BYTE_BIT_MASK(i);
+		wcd9xxx->irq_masks_cache[BIT_BYTE(i)] |= BYTE_BIT_MASK(i);
+		wcd9xxx->irq_level[BIT_BYTE(i)] |= wcd9xxx_irqs[i].level <<
+			(i % BITS_PER_BYTE);
+	}
+	for (i = 0; i < WCD9XXX_NUM_IRQ_REGS; i++) {
+		/* Initialize interrupt mask and level registers */
+		wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_LEVEL0 + i,
+			wcd9xxx->irq_level[i]);
+		wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_MASK0 + i,
+			wcd9xxx->irq_masks_cur[i]);
+	}
+
+	ret = request_threaded_irq(wcd9xxx->irq, NULL, wcd9xxx_irq_thread,
+				   IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+				   "wcd9xxx", wcd9xxx);
+	if (ret != 0)
+		dev_err(wcd9xxx->dev, "Failed to request IRQ %d: %d\n",
+			wcd9xxx->irq, ret);
+	else {
+		ret = enable_irq_wake(wcd9xxx->irq);
+		if (ret == 0) {
+			ret = device_init_wakeup(wcd9xxx->dev, 1);
+			if (ret) {
+				dev_err(wcd9xxx->dev, "Failed to init device"
+					"wakeup : %d\n", ret);
+				disable_irq_wake(wcd9xxx->irq);
+			}
+		} else
+			dev_err(wcd9xxx->dev, "Failed to set wake interrupt on"
+				" IRQ %d: %d\n", wcd9xxx->irq, ret);
+		if (ret)
+			free_irq(wcd9xxx->irq, wcd9xxx);
+	}
+
+	if (ret)
+		mutex_destroy(&wcd9xxx->irq_lock);
+
+	return ret;
+}
+
+void wcd9xxx_irq_exit(struct wcd9xxx *wcd9xxx)
+{
+	if (wcd9xxx->irq) {
+		disable_irq_wake(wcd9xxx->irq);
+		free_irq(wcd9xxx->irq, wcd9xxx);
+		device_init_wakeup(wcd9xxx->dev, 0);
+	}
+	mutex_destroy(&wcd9xxx->irq_lock);
+}
diff --git a/drivers/mfd/wcd9xxx-slimslave.c b/drivers/mfd/wcd9xxx-slimslave.c
new file mode 100644
index 0000000..538ca7c
--- /dev/null
+++ b/drivers/mfd/wcd9xxx-slimslave.c
@@ -0,0 +1,528 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
+
+struct wcd9xxx_slim_sch_rx {
+	u32 sph;
+	u32 ch_num;
+	u16 ch_h;
+	u16 grph;
+};
+
+struct wcd9xxx_slim_sch_tx {
+	u32 sph;
+	u32 ch_num;
+	u16 ch_h;
+	u16 grph;
+};
+
+struct wcd9xxx_slim_sch {
+	struct wcd9xxx_slim_sch_rx rx[SLIM_MAX_RX_PORTS];
+	struct wcd9xxx_slim_sch_tx tx[SLIM_MAX_TX_PORTS];
+};
+
+static struct wcd9xxx_slim_sch sh_ch;
+
+static int wcd9xxx_alloc_slim_sh_ch_rx(struct wcd9xxx *wcd9xxx,
+					u8 wcd9xxx_pgd_la);
+static int wcd9xxx_alloc_slim_sh_ch_tx(struct wcd9xxx *wcd9xxx,
+					u8 wcd9xxx_pgd_la);
+static int wcd9xxx_dealloc_slim_sh_ch_rx(struct wcd9xxx *wcd9xxx);
+static int wcd9xxx_dealloc_slim_sh_ch_tx(struct wcd9xxx *wcd9xxx);
+
+int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx, u8 wcd9xxx_pgd_la)
+{
+	int ret = 0;
+
+	ret = wcd9xxx_alloc_slim_sh_ch_rx(wcd9xxx, wcd9xxx_pgd_la);
+	if (ret) {
+		pr_err("%s: Failed to alloc rx slimbus shared channels\n",
+								__func__);
+		goto rx_err;
+	}
+	ret = wcd9xxx_alloc_slim_sh_ch_tx(wcd9xxx, wcd9xxx_pgd_la);
+	if (ret) {
+		pr_err("%s: Failed to alloc tx slimbus shared channels\n",
+								__func__);
+		goto tx_err;
+	}
+	return 0;
+tx_err:
+	wcd9xxx_dealloc_slim_sh_ch_rx(wcd9xxx);
+rx_err:
+	return ret;
+}
+
+
+int wcd9xxx_deinit_slimslave(struct wcd9xxx *wcd9xxx)
+{
+	int ret = 0;
+	ret = wcd9xxx_dealloc_slim_sh_ch_rx(wcd9xxx);
+	if (ret < 0) {
+		pr_err("%s: fail to dealloc rx slim ports\n", __func__);
+		goto err;
+	}
+	ret = wcd9xxx_dealloc_slim_sh_ch_tx(wcd9xxx);
+	if (ret < 0) {
+		pr_err("%s: fail to dealloc tx slim ports\n", __func__);
+		goto err;
+	}
+err:
+	return ret;
+}
+
+int wcd9xxx_get_channel(struct wcd9xxx *wcd9xxx,
+		unsigned int *rx_ch,
+		unsigned int *tx_ch)
+{
+	int ch_idx = 0;
+	struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+	struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
+
+	for (ch_idx = 0; ch_idx < SLIM_MAX_RX_PORTS; ch_idx++)
+		rx_ch[ch_idx] = rx[ch_idx].ch_num;
+	for (ch_idx = 0; ch_idx < SLIM_MAX_TX_PORTS; ch_idx++)
+		tx_ch[ch_idx] = tx[ch_idx].ch_num;
+	return 0;
+}
+
+static int wcd9xxx_alloc_slim_sh_ch_rx(struct wcd9xxx *wcd9xxx,
+			u8 wcd9xxx_pgd_la)
+{
+	int ret = 0;
+	u8 ch_idx ;
+	u16 slave_port_id = 0;
+	struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+
+	/*
+	 * DSP requires channel number to be between 128 and 255.
+	 */
+	pr_debug("%s: pgd_la[%d]\n", __func__, wcd9xxx_pgd_la);
+	for (ch_idx = 0; ch_idx < SLIM_MAX_RX_PORTS; ch_idx++) {
+		slave_port_id = (ch_idx + 1 +
+				SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS);
+		rx[ch_idx].ch_num = slave_port_id + BASE_CH_NUM;
+		ret = slim_get_slaveport(wcd9xxx_pgd_la, slave_port_id,
+					&rx[ch_idx].sph, SLIM_SINK);
+		if (ret < 0) {
+			pr_err("%s: slave port failure id[%d] ret[%d]\n",
+					__func__, slave_port_id, ret);
+			goto err;
+		}
+
+		ret = slim_query_ch(wcd9xxx->slim, rx[ch_idx].ch_num,
+							&rx[ch_idx].ch_h);
+		if (ret < 0) {
+			pr_err("%s: slim_query_ch failed ch-num[%d] ret[%d]\n",
+					__func__, rx[ch_idx].ch_num, ret);
+			goto err;
+		}
+	}
+err:
+	return ret;
+}
+
+static int wcd9xxx_alloc_slim_sh_ch_tx(struct wcd9xxx *wcd9xxx,
+			u8 wcd9xxx_pgd_la)
+{
+	int ret = 0;
+	u8 ch_idx ;
+	struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
+	u16 slave_port_id = 0;
+
+	pr_debug("%s: pgd_la[%d]\n", __func__, wcd9xxx_pgd_la);
+	/* DSP requires channel number to be between 128 and 255. For RX port
+	 * use channel numbers from 138 to 144, for TX port
+	 * use channel numbers from 128 to 137
+	 */
+	for (ch_idx = 0; ch_idx < SLIM_MAX_TX_PORTS; ch_idx++) {
+		slave_port_id = ch_idx;
+		tx[ch_idx].ch_num = slave_port_id + BASE_CH_NUM;
+		ret = slim_get_slaveport(wcd9xxx_pgd_la, slave_port_id,
+					&tx[ch_idx].sph, SLIM_SRC);
+		if (ret < 0) {
+			pr_err("%s: slave port failure id[%d] ret[%d]\n",
+					__func__, slave_port_id, ret);
+			goto err;
+		}
+		ret = slim_query_ch(wcd9xxx->slim, tx[ch_idx].ch_num,
+							&tx[ch_idx].ch_h);
+		if (ret < 0) {
+			pr_err("%s: slim_query_ch failed ch-num[%d] ret[%d]\n",
+					__func__, tx[ch_idx].ch_num, ret);
+			goto err;
+		}
+	}
+err:
+	return ret;
+}
+
+static int wcd9xxx_dealloc_slim_sh_ch_rx(struct wcd9xxx *wcd9xxx)
+{
+	int idx = 0;
+	int ret = 0;
+	struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+	/* slim_dealloc_ch */
+	for (idx = 0; idx < SLIM_MAX_RX_PORTS; idx++) {
+		ret = slim_dealloc_ch(wcd9xxx->slim, rx[idx].ch_h);
+		if (ret < 0) {
+			pr_err("%s: slim_dealloc_ch fail ret[%d] ch_h[%d]\n",
+				__func__, ret, rx[idx].ch_h);
+		}
+	}
+	memset(sh_ch.rx, 0, sizeof(sh_ch.rx));
+	return ret;
+}
+
+static int wcd9xxx_dealloc_slim_sh_ch_tx(struct wcd9xxx *wcd9xxx)
+{
+	int idx = 0;
+	int ret = 0;
+	struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
+	/* slim_dealloc_ch */
+	for (idx = 0; idx < SLIM_MAX_TX_PORTS; idx++) {
+		ret = slim_dealloc_ch(wcd9xxx->slim, tx[idx].ch_h);
+		if (ret < 0) {
+			pr_err("%s: slim_dealloc_ch fail ret[%d] ch_h[%d]\n",
+				__func__, ret, tx[idx].ch_h);
+		}
+	}
+	memset(sh_ch.tx, 0, sizeof(sh_ch.tx));
+	return ret;
+}
+
+/* Enable slimbus slave device for RX path */
+int wcd9xxx_cfg_slim_sch_rx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
+				unsigned int ch_cnt, unsigned int rate)
+{
+	u8 i = 0;
+	u16 grph;
+	u32 sph[SLIM_MAX_RX_PORTS] = {0};
+	u16 ch_h[SLIM_MAX_RX_PORTS] = {0};
+	u16 slave_port_id;
+	u8  payload_rx = 0, wm_payload = 0;
+	int ret, idx = 0;
+	unsigned short  multi_chan_cfg_reg_addr;
+	struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+	struct slim_ch prop;
+
+	/* Configure slave interface device */
+	pr_debug("%s: ch_cnt[%d] rate=%d\n", __func__, ch_cnt, rate);
+
+	for (i = 0; i < ch_cnt; i++) {
+		idx = (ch_num[i] - BASE_CH_NUM -
+			SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS - 1);
+		ch_h[i] = rx[idx].ch_h;
+		sph[i] = rx[idx].sph;
+		slave_port_id = idx + 1;
+		if ((slave_port_id > SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS) ||
+			(slave_port_id == 0)) {
+			pr_err("Slimbus: invalid slave port id: %d",
+							slave_port_id);
+			ret = -EINVAL;
+			goto err;
+		}
+		slave_port_id += SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS;
+		/* look for the valid port range and chose the
+		 * payload accordingly
+		 */
+		if ((slave_port_id >
+				SB_PGD_TX_PORT_MULTI_CHANNEL_1_END_PORT_ID) &&
+			(slave_port_id <=
+			 SB_PGD_RX_PORT_MULTI_CHANNEL_0_END_PORT_ID)) {
+				payload_rx = payload_rx  |
+				(1 <<
+				(slave_port_id -
+				SB_PGD_RX_PORT_MULTI_CHANNEL_0_START_PORT_ID));
+		} else {
+			ret = -EINVAL;
+			goto err;
+		}
+		multi_chan_cfg_reg_addr =
+				SB_PGD_RX_PORT_MULTI_CHANNEL_0(slave_port_id);
+		/* write to interface device */
+		ret = wcd9xxx_interface_reg_write(wcd9xxx,
+				multi_chan_cfg_reg_addr,
+				payload_rx);
+		if (ret < 0) {
+			pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
+							__func__,
+							multi_chan_cfg_reg_addr,
+							payload_rx, ret);
+			goto err;
+		}
+		/* configure the slave port for water mark and enable*/
+		wm_payload = (SLAVE_PORT_WATER_MARK_VALUE <<
+				SLAVE_PORT_WATER_MARK_SHIFT) +
+				SLAVE_PORT_ENABLE;
+		ret = wcd9xxx_interface_reg_write(wcd9xxx,
+				SB_PGD_PORT_CFG_BYTE_ADDR(slave_port_id),
+				wm_payload);
+		if (ret < 0) {
+			pr_err("%s:watermark set failure for port[%d] ret[%d]",
+						__func__, slave_port_id, ret);
+		}
+	}
+
+	/* slim_define_ch api */
+	prop.prot = SLIM_AUTO_ISO;
+	prop.baser = SLIM_RATE_4000HZ;
+	prop.dataf = SLIM_CH_DATAF_NOT_DEFINED;
+	prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE;
+	prop.ratem = (rate/4000);
+	prop.sampleszbits = 16;
+
+	ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt,
+					true, &grph);
+	if (ret < 0) {
+		pr_err("%s: slim_define_ch failed ret[%d]\n",
+					__func__, ret);
+		goto err;
+	}
+	for (i = 0; i < ch_cnt; i++) {
+		ret = slim_connect_sink(wcd9xxx->slim, &sph[i],
+							1, ch_h[i]);
+		if (ret < 0) {
+			pr_err("%s: slim_connect_sink failed ret[%d]\n",
+						__func__, ret);
+			goto err_close_slim_sch;
+		}
+	}
+	/* slim_control_ch */
+	ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_ACTIVATE,
+					true);
+	if (ret < 0) {
+		pr_err("%s: slim_control_ch failed ret[%d]\n",
+				__func__, ret);
+		goto err_close_slim_sch;
+	}
+	for (i = 0; i < ch_cnt; i++) {
+		idx = (ch_num[i] - BASE_CH_NUM -
+				SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS - 1);
+		rx[idx].grph = grph;
+	}
+	return 0;
+
+err_close_slim_sch:
+	/*  release all acquired handles */
+	wcd9xxx_close_slim_sch_rx(wcd9xxx, ch_num, ch_cnt);
+err:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_cfg_slim_sch_rx);
+
+/* Enable slimbus slave device for RX path */
+int wcd9xxx_cfg_slim_sch_tx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
+				unsigned int ch_cnt, unsigned int rate)
+{
+	u8 i = 0;
+	u8  payload_tx_0 = 0, payload_tx_1 = 0, wm_payload = 0;
+	u16 grph;
+	u32 sph[SLIM_MAX_TX_PORTS] = {0};
+	u16 ch_h[SLIM_MAX_TX_PORTS] = {0};
+	u16 idx = 0, slave_port_id;
+	int ret = 0;
+	unsigned short  multi_chan_cfg_reg_addr;
+
+	struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
+	struct slim_ch prop;
+
+	pr_debug("%s: ch_cnt[%d] rate[%d]\n", __func__, ch_cnt, rate);
+	for (i = 0; i < ch_cnt; i++) {
+		idx = (ch_num[i] - BASE_CH_NUM);
+		ch_h[i] = tx[idx].ch_h;
+		sph[i] = tx[idx].sph;
+		slave_port_id = idx ;
+		if (slave_port_id > SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS) {
+			pr_err("SLIMbus: invalid slave port id: %d",
+							slave_port_id);
+			ret = -EINVAL;
+			goto err;
+		}
+		/* look for the valid port range and chose the
+		 *  payload accordingly
+		 */
+		if (slave_port_id <=
+			SB_PGD_TX_PORT_MULTI_CHANNEL_0_END_PORT_ID) {
+			payload_tx_0 = payload_tx_0 | (1 << slave_port_id);
+		} else if (slave_port_id <=
+				SB_PGD_TX_PORT_MULTI_CHANNEL_1_END_PORT_ID) {
+				payload_tx_1 = payload_tx_1 |
+				(1 <<
+				(slave_port_id -
+				SB_PGD_TX_PORT_MULTI_CHANNEL_1_START_PORT_ID));
+		} else {
+			ret = -EINVAL;
+			goto err;
+		}
+		multi_chan_cfg_reg_addr =
+				SB_PGD_TX_PORT_MULTI_CHANNEL_0(slave_port_id);
+		/* write to interface device */
+		ret = wcd9xxx_interface_reg_write(wcd9xxx,
+				multi_chan_cfg_reg_addr,
+				payload_tx_0);
+		if (ret < 0) {
+			pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
+								__func__,
+						multi_chan_cfg_reg_addr,
+						payload_tx_0, ret);
+			goto err;
+		}
+		multi_chan_cfg_reg_addr =
+				SB_PGD_TX_PORT_MULTI_CHANNEL_1(slave_port_id);
+		/* ports 8,9 */
+		ret = wcd9xxx_interface_reg_write(wcd9xxx,
+				multi_chan_cfg_reg_addr,
+				payload_tx_1);
+		if (ret < 0) {
+			pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
+								__func__,
+						multi_chan_cfg_reg_addr,
+						payload_tx_1, ret);
+			goto err;
+		}
+		/* configure the slave port for water mark and enable*/
+		wm_payload = (SLAVE_PORT_WATER_MARK_VALUE <<
+				SLAVE_PORT_WATER_MARK_SHIFT) +
+				SLAVE_PORT_ENABLE;
+		ret = wcd9xxx_interface_reg_write(wcd9xxx,
+				SB_PGD_PORT_CFG_BYTE_ADDR(slave_port_id),
+				wm_payload);
+		if (ret < 0) {
+			pr_err("%s:watermark set failure for port[%d] ret[%d]",
+						__func__,
+						slave_port_id, ret);
+		}
+	}
+
+	/* slim_define_ch api */
+	prop.prot = SLIM_AUTO_ISO;
+	prop.baser = SLIM_RATE_4000HZ;
+	prop.dataf = SLIM_CH_DATAF_NOT_DEFINED;
+	prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE;
+	prop.ratem = (rate/4000);
+	prop.sampleszbits = 16;
+	ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt,
+					true, &grph);
+	if (ret < 0) {
+		pr_err("%s: slim_define_ch failed ret[%d]\n",
+					__func__, ret);
+		goto err;
+	}
+	for (i = 0; i < ch_cnt; i++) {
+		ret = slim_connect_src(wcd9xxx->slim, sph[i],
+							ch_h[i]);
+		if (ret < 0) {
+			pr_err("%s: slim_connect_src failed ret[%d]\n",
+						__func__, ret);
+			goto err;
+		}
+	}
+	/* slim_control_ch */
+	ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_ACTIVATE,
+					true);
+	if (ret < 0) {
+		pr_err("%s: slim_control_ch failed ret[%d]\n",
+				__func__, ret);
+		goto err;
+	}
+	for (i = 0; i < ch_cnt; i++) {
+		idx = (ch_num[i] - BASE_CH_NUM);
+		tx[idx].grph = grph;
+	}
+	return 0;
+err:
+	/* release all acquired handles */
+	wcd9xxx_close_slim_sch_tx(wcd9xxx, ch_num, ch_cnt);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_cfg_slim_sch_tx);
+
+int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
+				unsigned int ch_cnt)
+{
+	u16 grph = 0;
+	u32 sph[SLIM_MAX_RX_PORTS] = {0};
+	int i = 0 , idx = 0;
+	int ret = 0;
+	struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+
+	pr_debug("%s: ch_cnt[%d]\n", __func__, ch_cnt);
+	for (i = 0; i < ch_cnt; i++) {
+		idx = (ch_num[i] - BASE_CH_NUM -
+			SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS - 1);
+		if (idx < 0) {
+			pr_err("%s: Error:-Invalid index found = %d\n",
+				__func__, idx);
+			ret = -EINVAL;
+			goto err;
+		}
+		sph[i] = rx[idx].sph;
+		grph = rx[idx].grph;
+	}
+
+	/* slim_control_ch (REMOVE) */
+	ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_REMOVE, true);
+	if (ret < 0) {
+		pr_err("%s: slim_control_ch failed ret[%d]\n",
+				__func__, ret);
+		goto err;
+	}
+	for (i = 0; i < ch_cnt; i++) {
+		idx = (ch_num[i] - BASE_CH_NUM -
+				SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS - 1);
+		rx[idx].grph = 0;
+	}
+err:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_close_slim_sch_rx);
+
+int wcd9xxx_close_slim_sch_tx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
+				unsigned int ch_cnt)
+{
+	u16 grph = 0;
+	u32 sph[SLIM_MAX_TX_PORTS] = {0};
+	int ret = 0;
+	int i = 0 , idx = 0;
+	struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
+
+	pr_debug("%s: ch_cnt[%d]\n", __func__, ch_cnt);
+	for (i = 0; i < ch_cnt; i++) {
+		idx = (ch_num[i] - BASE_CH_NUM);
+		if (idx < 0) {
+			pr_err("%s: Error:- Invalid index found = %d\n",
+				__func__, idx);
+			ret = -EINVAL;
+			goto err;
+		}
+		sph[i] = tx[idx].sph;
+		grph = tx[idx].grph;
+	}
+
+	/* slim_control_ch (REMOVE) */
+	ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_REMOVE, true);
+	if (ret < 0) {
+		pr_err("%s: slim_control_ch failed ret[%d]\n",
+				__func__, ret);
+		goto err;
+	}
+	for (i = 0; i < ch_cnt; i++) {
+		idx = (ch_num[i] - BASE_CH_NUM);
+		tx[idx].grph = 0;
+	}
+err:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_close_slim_sch_tx);
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 1a0254a..1c2f018 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -51,6 +51,10 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called ad525x_dpot-spi.
 
+config ANDROID_PMEM
+	bool "Android pmem allocator"
+	default y
+
 config ATMEL_PWM
 	tristate "Atmel AT32/AT91 PWM support"
 	depends on HAVE_CLK
@@ -517,7 +521,146 @@
 	---help---
 	 Creates an rfkill entry in sysfs for power control of Bluetooth
 	 TI wl127x chips.
-	 
+
+config APANIC
+	bool "Android kernel panic diagnostics driver"
+	default n
+	---help---
+	 Driver which handles kernel panics and attempts to write
+	 critical debugging data to flash.
+
+config APANIC_PLABEL
+	string "Android panic dump flash partition label"
+	depends on APANIC
+	default "kpanic"
+	---help---
+	 If your platform uses a different flash partition label for storing
+ 	 crashdumps, enter it here.
+
+config TSIF
+	depends on ARCH_MSM
+	tristate "TSIF (Transport Stream InterFace) support"
+	default n
+	---help---
+	  This driver supports low level TSIF interface. It provides API
+	  for upper layer drivers. If you have a TSIF hardware, say
+	  Y here and read <file:Documentation/arm/msm/tsif.txt>.
+
+	  To compile this driver as module, choose M here: the
+	  module will be called msm_tsif.
+
+config TSIF_CHRDEV
+	tristate "TSIF character device"
+	depends on TSIF
+	default n
+	---help---
+	  This driver uses low level TSIF interface. It provides character
+	  device useable from user space programs: one can read TSIF stream
+	  from this device.
+
+	  This driver may be used as example for TSIF API usage.
+
+	  To compile this driver as module, choose M here: the
+	  module will be called tsif_chrdev.
+
+config TSIF_DEBUG
+	bool "Turn on debugging information for tsif driver"
+	depends on TSIF
+	default n
+	---help---
+	  This turns on debugging information for the tsif driver
+
+config TSPP
+	depends on ARCH_MSM
+	tristate "TSPP (Transport Stream Packet Processor) Support"
+	---help---
+	Transport Stream Packet Processor is used to offload the
+	processing of MPEG transport streams from the main processor.
+	This can also be compiled as a loadable module.
+
+config HAPTIC_ISA1200
+	tristate "ISA1200 haptic support"
+	depends on I2C
+	default n
+	help
+	  The ISA1200 is a high performance enhanced haptic driver.
+
+config PMIC8058_PWM
+	tristate "Qualcomm PM8058 PWM support"
+	depends on PMIC8058
+	default y
+	help
+	  This option enables device driver support for the PWM channels
+	  on Qualcomm PM8058 chip. Pulse Width Modulation is used for
+	  purposes including software controlled brightness of backlight,
+	  motor control, and waveform generation.
+
+config PMIC8XXX_VIBRATOR
+	tristate "Qualcomm Vibrator support for PMIC8XXX"
+	depends on MFD_PM8XXX && ANDROID_TIMED_OUTPUT
+	help
+	  This option enables device driver support for the vibrator
+	  on the PM8XXX chips. The vibrator is controlled using the
+	  timed output class.
+
+config PMIC8XXX_NFC
+	tristate "Qualcomm PM8XXX support for Near Field Communication"
+	depends on MFD_PM8XXX
+	help
+	  Qualcomm PM8XXX chips have a module to support NFC (Near Field
+	  Communication). This option enables the driver to support it.
+
+config PMIC8XXX_UPL
+	tristate "Qualcomm PM8XXX support for User Programmable Logic"
+	depends on MFD_PM8XXX
+	help
+	  This option enables device driver support for User Programmable Logic
+	  on Qualcomm PM8XXX chips. The UPL module provides a means to implement
+	  simple truth table based logic via a set of control registers. I/O may
+	  be routed in and out of the UPL module via GPIO or DTEST pins.
+
+config PMIC8058_XOADC
+	tristate "Qualcomm PM8058 XOADC driver"
+	depends on PMIC8058
+	default n
+	help
+	  Enables User processor ADC reads over the XOADC module of Qualcomm's
+	  PMIC8058. Driver interface to program registers of the ADC over
+	  AMUX channels, devices on programmable MPP's and xotherm.
+
+config TZCOM
+	tristate "Trustzone Communicator driver"
+	default n
+	help
+	  Provides a communication interface between userspace and
+	  TrustZone Operating Environment (TZBSP) using Secure Channel
+	  Manager (SCM) interface.
+
+config QSEECOM
+	tristate "Qualcomm Secure Execution Communicator driver"
+	help
+	  Provides a communication interface between userspace and
+	  Qualcomm Secure Execution Environment (QSEE) using Secure Channel
+	  Manager (SCM) interface.
+
+config QFP_FUSE
+	tristate "QFPROM Fuse Read/Write support"
+	help
+	  This option enables device driver to read/write QFPROM
+	  fuses. The ioctls provides the necessary interface
+	  to the fuse block. Currently this is supported only
+	  on FSM targets.
+
+config USB_HSIC_SMSC_HUB
+	tristate "Support for HSIC based MSM on-chip SMSC3503 HUB"
+	depends on USB_EHCI_MSM_HSIC
+	help
+	  Enables support for the HSIC (High Speed Inter-Chip) based
+	  SMSC3503 hub controller present on the Qualcomm chipsets.
+
+	  This adds support for connecting devices like mouse in HSIC
+	  Host mode.
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 89540d1..b628976 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -19,6 +19,7 @@
 obj-$(CONFIG_SENSORS_BH1780)	+= bh1780gli.o
 obj-$(CONFIG_SENSORS_BH1770)	+= bh1770glc.o
 obj-$(CONFIG_SENSORS_APDS990X)	+= apds990x.o
+obj-$(CONFIG_ANDROID_PMEM)	+= pmem.o
 obj-$(CONFIG_SGI_IOC4)		+= ioc4.o
 obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o
 obj-$(CONFIG_KGDB_TESTS)	+= kgdbts.o
@@ -29,6 +30,7 @@
 obj-$(CONFIG_APDS9802ALS)	+= apds9802als.o
 obj-$(CONFIG_ISL29003)		+= isl29003.o
 obj-$(CONFIG_ISL29020)		+= isl29020.o
+obj-$(CONFIG_USB_HSIC_SMSC_HUB) += smsc_hub.o
 obj-$(CONFIG_SENSORS_TSL2550)	+= tsl2550.o
 obj-$(CONFIG_EP93XX_PWM)	+= ep93xx_pwm.o
 obj-$(CONFIG_DS1682)		+= ds1682.o
@@ -52,3 +54,21 @@
 obj-$(CONFIG_MAX8997_MUIC)	+= max8997-muic.o
 obj-$(CONFIG_WL127X_RFKILL)	+= wl127x-rfkill.o
 obj-$(CONFIG_SENSORS_AK8975)	+= akm8975.o
+obj-$(CONFIG_WL127X_RFKILL)	+= wl127x-rfkill.o
+obj-$(CONFIG_APANIC)		+= apanic.o
+obj-$(CONFIG_SENSORS_AK8975)	+= akm8975.o
+obj-$(CONFIG_TSIF) += msm_tsif.o
+msm_tsif-objs := tsif.o
+obj-$(CONFIG_TSIF_CHRDEV) += tsif_chrdev.o
+obj-$(CONFIG_TSPP) += tspp.o
+obj-$(CONFIG_HAPTIC_ISA1200)		+= isa1200.o
+obj-$(CONFIG_PMIC8058_PWM) += pmic8058-pwm.o
+obj-$(CONFIG_PMIC8XXX_VIBRATOR) += pm8xxx-vibrator.o
+obj-$(CONFIG_PMIC8XXX_NFC) += pm8xxx-nfc.o
+obj-$(CONFIG_PMIC8XXX_UPL) += pm8xxx-upl.o
+obj-$(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_DEEP_POWER_DOWN) \
+	+= msm_migrate_pages.o
+obj-$(CONFIG_PMIC8058_XOADC) += pmic8058-xoadc.o
+obj-$(CONFIG_TZCOM) += tzcom.o
+obj-$(CONFIG_QSEECOM) += qseecom.o
+obj-$(CONFIG_QFP_FUSE) += qfp_fuse.o
diff --git a/drivers/misc/apanic.c b/drivers/misc/apanic.c
new file mode 100644
index 0000000..ca875f8
--- /dev/null
+++ b/drivers/misc/apanic.c
@@ -0,0 +1,606 @@
+/* drivers/misc/apanic.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Author: San Mehat <san@android.com>
+ *
+ * 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/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/wakelock.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/mtd/mtd.h>
+#include <linux/notifier.h>
+#include <linux/mtd/mtd.h>
+#include <linux/debugfs.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/preempt.h>
+
+extern void ram_console_enable_console(int);
+
+struct panic_header {
+	u32 magic;
+#define PANIC_MAGIC 0xdeadf00d
+
+	u32 version;
+#define PHDR_VERSION   0x01
+
+	u32 console_offset;
+	u32 console_length;
+
+	u32 threads_offset;
+	u32 threads_length;
+};
+
+struct apanic_data {
+	struct mtd_info		*mtd;
+	struct panic_header	curr;
+	void			*bounce;
+	struct proc_dir_entry	*apanic_console;
+	struct proc_dir_entry	*apanic_threads;
+};
+
+static struct apanic_data drv_ctx;
+static struct work_struct proc_removal_work;
+static DEFINE_MUTEX(drv_mutex);
+
+static unsigned int *apanic_bbt;
+static unsigned int apanic_erase_blocks;
+static unsigned int apanic_good_blocks;
+
+static void set_bb(unsigned int block, unsigned int *bbt)
+{
+	unsigned int flag = 1;
+
+	BUG_ON(block >= apanic_erase_blocks);
+
+	flag = flag << (block%32);
+	apanic_bbt[block/32] |= flag;
+	apanic_good_blocks--;
+}
+
+static unsigned int get_bb(unsigned int block, unsigned int *bbt)
+{
+	unsigned int flag;
+
+	BUG_ON(block >= apanic_erase_blocks);
+
+	flag = 1 << (block%32);
+	return apanic_bbt[block/32] & flag;
+}
+
+static void alloc_bbt(struct mtd_info *mtd, unsigned int *bbt)
+{
+	int bbt_size;
+	apanic_erase_blocks = (mtd->size)>>(mtd->erasesize_shift);
+	bbt_size = (apanic_erase_blocks+32)/32;
+
+	apanic_bbt = kmalloc(bbt_size*4, GFP_KERNEL);
+	memset(apanic_bbt, 0, bbt_size*4);
+	apanic_good_blocks = apanic_erase_blocks;
+}
+static void scan_bbt(struct mtd_info *mtd, unsigned int *bbt)
+{
+	int i;
+
+	for (i = 0; i < apanic_erase_blocks; i++) {
+		if (mtd->block_isbad(mtd, i*mtd->erasesize))
+			set_bb(i, apanic_bbt);
+	}
+}
+
+#define APANIC_INVALID_OFFSET 0xFFFFFFFF
+
+static unsigned int phy_offset(struct mtd_info *mtd, unsigned int offset)
+{
+	unsigned int logic_block = offset>>(mtd->erasesize_shift);
+	unsigned int phy_block;
+	unsigned good_block = 0;
+
+	for (phy_block = 0; phy_block < apanic_erase_blocks; phy_block++) {
+		if (!get_bb(phy_block, apanic_bbt))
+			good_block++;
+		if (good_block == (logic_block + 1))
+			break;
+	}
+
+	if (good_block != (logic_block + 1))
+		return APANIC_INVALID_OFFSET;
+
+	return offset + ((phy_block-logic_block)<<mtd->erasesize_shift);
+}
+
+static void apanic_erase_callback(struct erase_info *done)
+{
+	wait_queue_head_t *wait_q = (wait_queue_head_t *) done->priv;
+	wake_up(wait_q);
+}
+
+static int apanic_proc_read(char *buffer, char **start, off_t offset,
+			       int count, int *peof, void *dat)
+{
+	struct apanic_data *ctx = &drv_ctx;
+	size_t file_length;
+	off_t file_offset;
+	unsigned int page_no;
+	off_t page_offset;
+	int rc;
+	size_t len;
+
+	if (!count)
+		return 0;
+
+	mutex_lock(&drv_mutex);
+
+	switch ((int) dat) {
+	case 1:	/* apanic_console */
+		file_length = ctx->curr.console_length;
+		file_offset = ctx->curr.console_offset;
+		break;
+	case 2:	/* apanic_threads */
+		file_length = ctx->curr.threads_length;
+		file_offset = ctx->curr.threads_offset;
+		break;
+	default:
+		pr_err("Bad dat (%d)\n", (int) dat);
+		mutex_unlock(&drv_mutex);
+		return -EINVAL;
+	}
+
+	if ((offset + count) > file_length) {
+		mutex_unlock(&drv_mutex);
+		return 0;
+	}
+
+	/* We only support reading a maximum of a flash page */
+	if (count > ctx->mtd->writesize)
+		count = ctx->mtd->writesize;
+
+	page_no = (file_offset + offset) / ctx->mtd->writesize;
+	page_offset = (file_offset + offset) % ctx->mtd->writesize;
+
+
+	if (phy_offset(ctx->mtd, (page_no * ctx->mtd->writesize))
+		== APANIC_INVALID_OFFSET) {
+		pr_err("apanic: reading an invalid address\n");
+		mutex_unlock(&drv_mutex);
+		return -EINVAL;
+	}
+	rc = ctx->mtd->read(ctx->mtd,
+		phy_offset(ctx->mtd, (page_no * ctx->mtd->writesize)),
+		ctx->mtd->writesize,
+		&len, ctx->bounce);
+
+	if (page_offset)
+		count -= page_offset;
+	memcpy(buffer, ctx->bounce + page_offset, count);
+
+	*start = count;
+
+	if ((offset + count) == file_length)
+		*peof = 1;
+
+	mutex_unlock(&drv_mutex);
+	return count;
+}
+
+static void mtd_panic_erase(void)
+{
+	struct apanic_data *ctx = &drv_ctx;
+	struct erase_info erase;
+	DECLARE_WAITQUEUE(wait, current);
+	wait_queue_head_t wait_q;
+	int rc, i;
+
+	init_waitqueue_head(&wait_q);
+	erase.mtd = ctx->mtd;
+	erase.callback = apanic_erase_callback;
+	erase.len = ctx->mtd->erasesize;
+	erase.priv = (u_long)&wait_q;
+	for (i = 0; i < ctx->mtd->size; i += ctx->mtd->erasesize) {
+		erase.addr = i;
+		set_current_state(TASK_INTERRUPTIBLE);
+		add_wait_queue(&wait_q, &wait);
+
+		if (get_bb(erase.addr>>ctx->mtd->erasesize_shift, apanic_bbt)) {
+			printk(KERN_WARNING
+			       "apanic: Skipping erase of bad "
+			       "block @%llx\n", erase.addr);
+			set_current_state(TASK_RUNNING);
+			remove_wait_queue(&wait_q, &wait);
+			continue;
+		}
+
+		rc = ctx->mtd->erase(ctx->mtd, &erase);
+		if (rc) {
+			set_current_state(TASK_RUNNING);
+			remove_wait_queue(&wait_q, &wait);
+			printk(KERN_ERR
+			       "apanic: Erase of 0x%llx, 0x%llx failed\n",
+			       (unsigned long long) erase.addr,
+			       (unsigned long long) erase.len);
+			if (rc == -EIO) {
+				if (ctx->mtd->block_markbad(ctx->mtd,
+							    erase.addr)) {
+					printk(KERN_ERR
+					       "apanic: Err marking blk bad\n");
+					goto out;
+				}
+				printk(KERN_INFO
+				       "apanic: Marked a bad block"
+				       " @%llx\n", erase.addr);
+				set_bb(erase.addr>>ctx->mtd->erasesize_shift,
+					apanic_bbt);
+				continue;
+			}
+			goto out;
+		}
+		schedule();
+		remove_wait_queue(&wait_q, &wait);
+	}
+	printk(KERN_DEBUG "apanic: %s partition erased\n",
+	       CONFIG_APANIC_PLABEL);
+out:
+	return;
+}
+
+static void apanic_remove_proc_work(struct work_struct *work)
+{
+	struct apanic_data *ctx = &drv_ctx;
+
+	mutex_lock(&drv_mutex);
+	mtd_panic_erase();
+	memset(&ctx->curr, 0, sizeof(struct panic_header));
+	if (ctx->apanic_console) {
+		remove_proc_entry("apanic_console", NULL);
+		ctx->apanic_console = NULL;
+	}
+	if (ctx->apanic_threads) {
+		remove_proc_entry("apanic_threads", NULL);
+		ctx->apanic_threads = NULL;
+	}
+	mutex_unlock(&drv_mutex);
+}
+
+static int apanic_proc_write(struct file *file, const char __user *buffer,
+				unsigned long count, void *data)
+{
+	schedule_work(&proc_removal_work);
+	return count;
+}
+
+static void mtd_panic_notify_add(struct mtd_info *mtd)
+{
+	struct apanic_data *ctx = &drv_ctx;
+	struct panic_header *hdr = ctx->bounce;
+	size_t len;
+	int rc;
+	int    proc_entry_created = 0;
+
+	if (strcmp(mtd->name, CONFIG_APANIC_PLABEL))
+		return;
+
+	ctx->mtd = mtd;
+
+	alloc_bbt(mtd, apanic_bbt);
+	scan_bbt(mtd, apanic_bbt);
+
+	if (apanic_good_blocks == 0) {
+		printk(KERN_ERR "apanic: no any good blocks?!\n");
+		goto out_err;
+	}
+
+	rc = mtd->read(mtd, phy_offset(mtd, 0), mtd->writesize,
+			&len, ctx->bounce);
+	if (rc && rc == -EBADMSG) {
+		printk(KERN_WARNING
+		       "apanic: Bad ECC on block 0 (ignored)\n");
+	} else if (rc && rc != -EUCLEAN) {
+		printk(KERN_ERR "apanic: Error reading block 0 (%d)\n", rc);
+		goto out_err;
+	}
+
+	if (len != mtd->writesize) {
+		printk(KERN_ERR "apanic: Bad read size (%d)\n", rc);
+		goto out_err;
+	}
+
+	printk(KERN_INFO "apanic: Bound to mtd partition '%s'\n", mtd->name);
+
+	if (hdr->magic != PANIC_MAGIC) {
+		printk(KERN_INFO "apanic: No panic data available\n");
+		mtd_panic_erase();
+		return;
+	}
+
+	if (hdr->version != PHDR_VERSION) {
+		printk(KERN_INFO "apanic: Version mismatch (%d != %d)\n",
+		       hdr->version, PHDR_VERSION);
+		mtd_panic_erase();
+		return;
+	}
+
+	memcpy(&ctx->curr, hdr, sizeof(struct panic_header));
+
+	printk(KERN_INFO "apanic: c(%u, %u) t(%u, %u)\n",
+	       hdr->console_offset, hdr->console_length,
+	       hdr->threads_offset, hdr->threads_length);
+
+	if (hdr->console_length) {
+		ctx->apanic_console = create_proc_entry("apanic_console",
+						      S_IFREG | S_IRUGO, NULL);
+		if (!ctx->apanic_console)
+			printk(KERN_ERR "%s: failed creating procfile\n",
+			       __func__);
+		else {
+			ctx->apanic_console->read_proc = apanic_proc_read;
+			ctx->apanic_console->write_proc = apanic_proc_write;
+			ctx->apanic_console->size = hdr->console_length;
+			ctx->apanic_console->data = (void *) 1;
+			proc_entry_created = 1;
+		}
+	}
+
+	if (hdr->threads_length) {
+		ctx->apanic_threads = create_proc_entry("apanic_threads",
+						       S_IFREG | S_IRUGO, NULL);
+		if (!ctx->apanic_threads)
+			printk(KERN_ERR "%s: failed creating procfile\n",
+			       __func__);
+		else {
+			ctx->apanic_threads->read_proc = apanic_proc_read;
+			ctx->apanic_threads->write_proc = apanic_proc_write;
+			ctx->apanic_threads->size = hdr->threads_length;
+			ctx->apanic_threads->data = (void *) 2;
+			proc_entry_created = 1;
+		}
+	}
+
+	if (!proc_entry_created)
+		mtd_panic_erase();
+
+	return;
+out_err:
+	ctx->mtd = NULL;
+}
+
+static void mtd_panic_notify_remove(struct mtd_info *mtd)
+{
+	struct apanic_data *ctx = &drv_ctx;
+	if (mtd == ctx->mtd) {
+		ctx->mtd = NULL;
+		printk(KERN_INFO "apanic: Unbound from %s\n", mtd->name);
+	}
+}
+
+static struct mtd_notifier mtd_panic_notifier = {
+	.add	= mtd_panic_notify_add,
+	.remove	= mtd_panic_notify_remove,
+};
+
+static int in_panic = 0;
+
+static int apanic_writeflashpage(struct mtd_info *mtd, loff_t to,
+				 const u_char *buf)
+{
+	int rc;
+	size_t wlen;
+	int panic = in_interrupt() | in_atomic();
+
+	if (panic && !mtd->panic_write) {
+		printk(KERN_EMERG "%s: No panic_write available\n", __func__);
+		return 0;
+	} else if (!panic && !mtd->write) {
+		printk(KERN_EMERG "%s: No write available\n", __func__);
+		return 0;
+	}
+
+	to = phy_offset(mtd, to);
+	if (to == APANIC_INVALID_OFFSET) {
+		printk(KERN_EMERG "apanic: write to invalid address\n");
+		return 0;
+	}
+
+	if (panic)
+		rc = mtd->panic_write(mtd, to, mtd->writesize, &wlen, buf);
+	else
+		rc = mtd->write(mtd, to, mtd->writesize, &wlen, buf);
+
+	if (rc) {
+		printk(KERN_EMERG
+		       "%s: Error writing data to flash (%d)\n",
+		       __func__, rc);
+		return rc;
+	}
+
+	return wlen;
+}
+
+extern int log_buf_copy(char *dest, int idx, int len);
+extern void log_buf_clear(void);
+
+/*
+ * Writes the contents of the console to the specified offset in flash.
+ * Returns number of bytes written
+ */
+static int apanic_write_console(struct mtd_info *mtd, unsigned int off)
+{
+	struct apanic_data *ctx = &drv_ctx;
+	int saved_oip;
+	int idx = 0;
+	int rc, rc2;
+	unsigned int last_chunk = 0;
+
+	while (!last_chunk) {
+		saved_oip = oops_in_progress;
+		oops_in_progress = 1;
+		rc = log_buf_copy(ctx->bounce, idx, mtd->writesize);
+		if (rc < 0)
+			break;
+
+		if (rc != mtd->writesize)
+			last_chunk = rc;
+
+		oops_in_progress = saved_oip;
+		if (rc <= 0)
+			break;
+		if (rc != mtd->writesize)
+			memset(ctx->bounce + rc, 0, mtd->writesize - rc);
+
+		rc2 = apanic_writeflashpage(mtd, off, ctx->bounce);
+		if (rc2 <= 0) {
+			printk(KERN_EMERG
+			       "apanic: Flash write failed (%d)\n", rc2);
+			return idx;
+		}
+		if (!last_chunk)
+			idx += rc2;
+		else
+			idx += last_chunk;
+		off += rc2;
+	}
+	return idx;
+}
+
+static int apanic(struct notifier_block *this, unsigned long event,
+			void *ptr)
+{
+	struct apanic_data *ctx = &drv_ctx;
+	struct panic_header *hdr = (struct panic_header *) ctx->bounce;
+	int console_offset = 0;
+	int console_len = 0;
+	int threads_offset = 0;
+	int threads_len = 0;
+	int rc;
+
+	if (in_panic)
+		return NOTIFY_DONE;
+	in_panic = 1;
+#ifdef CONFIG_PREEMPT
+	/* Ensure that cond_resched() won't try to preempt anybody */
+	add_preempt_count(PREEMPT_ACTIVE);
+#endif
+	touch_softlockup_watchdog();
+
+	if (!ctx->mtd)
+		goto out;
+
+	if (ctx->curr.magic) {
+		printk(KERN_EMERG "Crash partition in use!\n");
+		goto out;
+	}
+	console_offset = ctx->mtd->writesize;
+
+	/*
+	 * Write out the console
+	 */
+	console_len = apanic_write_console(ctx->mtd, console_offset);
+	if (console_len < 0) {
+		printk(KERN_EMERG "Error writing console to panic log! (%d)\n",
+		       console_len);
+		console_len = 0;
+	}
+
+	/*
+	 * Write out all threads
+	 */
+	threads_offset = ALIGN(console_offset + console_len,
+			       ctx->mtd->writesize);
+	if (!threads_offset)
+		threads_offset = ctx->mtd->writesize;
+
+	ram_console_enable_console(0);
+
+	log_buf_clear();
+	show_state_filter(0);
+	threads_len = apanic_write_console(ctx->mtd, threads_offset);
+	if (threads_len < 0) {
+		printk(KERN_EMERG "Error writing threads to panic log! (%d)\n",
+		       threads_len);
+		threads_len = 0;
+	}
+
+	/*
+	 * Finally write the panic header
+	 */
+	memset(ctx->bounce, 0, PAGE_SIZE);
+	hdr->magic = PANIC_MAGIC;
+	hdr->version = PHDR_VERSION;
+
+	hdr->console_offset = console_offset;
+	hdr->console_length = console_len;
+
+	hdr->threads_offset = threads_offset;
+	hdr->threads_length = threads_len;
+
+	rc = apanic_writeflashpage(ctx->mtd, 0, ctx->bounce);
+	if (rc <= 0) {
+		printk(KERN_EMERG "apanic: Header write failed (%d)\n",
+		       rc);
+		goto out;
+	}
+
+	printk(KERN_EMERG "apanic: Panic dump sucessfully written to flash\n");
+
+ out:
+#ifdef CONFIG_PREEMPT
+	sub_preempt_count(PREEMPT_ACTIVE);
+#endif
+	in_panic = 0;
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block panic_blk = {
+	.notifier_call	= apanic,
+};
+
+static int panic_dbg_get(void *data, u64 *val)
+{
+	apanic(NULL, 0, NULL);
+	return 0;
+}
+
+static int panic_dbg_set(void *data, u64 val)
+{
+	BUG();
+	return -1;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(panic_dbg_fops, panic_dbg_get, panic_dbg_set, "%llu\n");
+
+int __init apanic_init(void)
+{
+	register_mtd_user(&mtd_panic_notifier);
+	atomic_notifier_chain_register(&panic_notifier_list, &panic_blk);
+	debugfs_create_file("apanic", 0644, NULL, NULL, &panic_dbg_fops);
+	memset(&drv_ctx, 0, sizeof(drv_ctx));
+	drv_ctx.bounce = (void *) __get_free_page(GFP_KERNEL);
+	INIT_WORK(&proc_removal_work, apanic_remove_proc_work);
+	printk(KERN_INFO "Android kernel panic handler initialized (bind=%s)\n",
+	       CONFIG_APANIC_PLABEL);
+	return 0;
+}
+
+module_init(apanic_init);
diff --git a/drivers/misc/isa1200.c b/drivers/misc/isa1200.c
new file mode 100644
index 0000000..555dfdd
--- /dev/null
+++ b/drivers/misc/isa1200.c
@@ -0,0 +1,716 @@
+/*
+ *  isa1200.c - Haptic Motor
+ *
+ *  Copyright (C) 2009 Samsung Electronics
+ *  Kyungmin Park <kyungmin.park@samsung.com>
+ *  Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/pwm.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/i2c/isa1200.h>
+#include "../staging/android/timed_output.h"
+
+#define ISA1200_HCTRL0		0x30
+#define ISA1200_HCTRL1		0x31
+#define ISA1200_HCTRL5		0x35
+
+#define ISA1200_HCTRL0_RESET	0x01
+#define ISA1200_HCTRL1_RESET	0x4B
+
+#define ISA1200_HCTRL5_VIB_STRT	0xD5
+#define ISA1200_HCTRL5_VIB_STOP	0x6B
+#define ISA1200_POWER_DOWN_MASK 0x7F
+
+struct isa1200_chip {
+	struct i2c_client *client;
+	struct isa1200_platform_data *pdata;
+	struct pwm_device *pwm;
+	struct hrtimer timer;
+	struct timed_output_dev dev;
+	struct work_struct work;
+	spinlock_t lock;
+	unsigned int enable;
+	unsigned int period_ns;
+	bool is_len_gpio_valid;
+	struct regulator **regs;
+	bool clk_on;
+	u8 hctrl0_val;
+};
+
+static int isa1200_read_reg(struct i2c_client *client, int reg)
+{
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(client, reg);
+	if (ret < 0)
+		dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+
+	return ret;
+}
+
+static int isa1200_write_reg(struct i2c_client *client, int reg, u8 value)
+{
+	int ret;
+
+	ret = i2c_smbus_write_byte_data(client, reg, value);
+	if (ret < 0)
+		dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+
+	return ret;
+}
+
+static void isa1200_vib_set(struct isa1200_chip *haptic, int enable)
+{
+	int rc = 0;
+
+	if (enable) {
+		/* if hen and len are seperate then enable hen
+		 * otherwise set normal mode bit */
+		if (haptic->is_len_gpio_valid == true)
+			gpio_set_value_cansleep(haptic->pdata->hap_en_gpio, 1);
+		else {
+			rc = isa1200_write_reg(haptic->client, ISA1200_HCTRL0,
+				haptic->hctrl0_val | ~ISA1200_POWER_DOWN_MASK);
+			if (rc < 0) {
+				pr_err("%s: i2c write failure\n", __func__);
+				return;
+			}
+		}
+
+		if (haptic->pdata->mode_ctrl == PWM_INPUT_MODE) {
+			int period_us = haptic->period_ns / 1000;
+
+			rc = pwm_config(haptic->pwm,
+				(period_us * haptic->pdata->duty) / 100,
+				period_us);
+			if (rc < 0) {
+				pr_err("%s: pwm_config fail\n", __func__);
+				goto chip_dwn;
+			}
+
+			rc = pwm_enable(haptic->pwm);
+			if (rc < 0) {
+				pr_err("%s: pwm_enable fail\n", __func__);
+				goto chip_dwn;
+			}
+		} else if (haptic->pdata->mode_ctrl == PWM_GEN_MODE) {
+			/* vote for clock */
+			if (haptic->pdata->clk_enable && !haptic->clk_on) {
+				rc = haptic->pdata->clk_enable(true);
+				if (rc < 0) {
+					pr_err("%s: clk enable failed\n",
+								__func__);
+					goto chip_dwn;
+				}
+				haptic->clk_on = true;
+			}
+
+			rc = isa1200_write_reg(haptic->client,
+						ISA1200_HCTRL5,
+						ISA1200_HCTRL5_VIB_STRT);
+			if (rc < 0) {
+				pr_err("%s: start vibartion fail\n", __func__);
+				goto dis_clk;
+			}
+		}
+	} else {
+		/* if hen and len are seperate then pull down hen
+		 * otherwise set power down bit */
+		if (haptic->is_len_gpio_valid == true)
+			gpio_set_value_cansleep(haptic->pdata->hap_en_gpio, 0);
+		else {
+			rc = isa1200_write_reg(haptic->client, ISA1200_HCTRL0,
+				haptic->hctrl0_val & ISA1200_POWER_DOWN_MASK);
+			if (rc < 0) {
+				pr_err("%s: i2c write failure\n", __func__);
+				return;
+			}
+		}
+
+		if (haptic->pdata->mode_ctrl == PWM_INPUT_MODE) {
+			pwm_disable(haptic->pwm);
+		} else if (haptic->pdata->mode_ctrl == PWM_GEN_MODE) {
+			rc = isa1200_write_reg(haptic->client,
+						ISA1200_HCTRL5,
+						ISA1200_HCTRL5_VIB_STOP);
+			if (rc < 0)
+				pr_err("%s: stop vibartion fail\n", __func__);
+
+			/* de-vote clock */
+			if (haptic->pdata->clk_enable && haptic->clk_on) {
+				rc = haptic->pdata->clk_enable(false);
+				if (rc < 0) {
+					pr_err("%s: clk disable failed\n",
+								__func__);
+					return;
+				}
+				haptic->clk_on = false;
+			}
+		}
+	}
+
+	return;
+
+dis_clk:
+	if (haptic->pdata->clk_enable && haptic->clk_on) {
+		rc = haptic->pdata->clk_enable(false);
+		if (rc < 0) {
+			pr_err("%s: clk disable failed\n", __func__);
+			return;
+		}
+		haptic->clk_on = false;
+	}
+chip_dwn:
+	if (haptic->is_len_gpio_valid == true)
+		gpio_set_value_cansleep(haptic->pdata->hap_en_gpio, 0);
+	else {
+		rc = isa1200_write_reg(haptic->client, ISA1200_HCTRL0,
+			haptic->hctrl0_val & ISA1200_POWER_DOWN_MASK);
+		if (rc < 0) {
+			pr_err("%s: i2c write failure\n", __func__);
+			return;
+		}
+	}
+}
+
+static void isa1200_chip_work(struct work_struct *work)
+{
+	struct isa1200_chip *haptic;
+
+	haptic = container_of(work, struct isa1200_chip, work);
+	isa1200_vib_set(haptic, haptic->enable);
+}
+
+static void isa1200_chip_enable(struct timed_output_dev *dev, int value)
+{
+	struct isa1200_chip *haptic = container_of(dev, struct isa1200_chip,
+					dev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&haptic->lock, flags);
+	hrtimer_cancel(&haptic->timer);
+	if (value == 0)
+		haptic->enable = 0;
+	else {
+		value = (value > haptic->pdata->max_timeout ?
+				haptic->pdata->max_timeout : value);
+		haptic->enable = 1;
+		hrtimer_start(&haptic->timer,
+			ktime_set(value / 1000, (value % 1000) * 1000000),
+			HRTIMER_MODE_REL);
+	}
+	spin_unlock_irqrestore(&haptic->lock, flags);
+	schedule_work(&haptic->work);
+}
+
+static int isa1200_chip_get_time(struct timed_output_dev *dev)
+{
+	struct isa1200_chip *haptic = container_of(dev, struct isa1200_chip,
+					dev);
+
+	if (hrtimer_active(&haptic->timer)) {
+		ktime_t r = hrtimer_get_remaining(&haptic->timer);
+		struct timeval t = ktime_to_timeval(r);
+		return t.tv_sec * 1000 + t.tv_usec / 1000;
+	} else
+		return 0;
+}
+
+static enum hrtimer_restart isa1200_vib_timer_func(struct hrtimer *timer)
+{
+	struct isa1200_chip *haptic = container_of(timer, struct isa1200_chip,
+					timer);
+	haptic->enable = 0;
+	schedule_work(&haptic->work);
+
+	return HRTIMER_NORESTART;
+}
+
+static void dump_isa1200_reg(char *str, struct i2c_client *client)
+{
+	pr_debug("%s reg0x%x=0x%x, reg0x%x=0x%x, reg0x%x=0x%x\n", str,
+		ISA1200_HCTRL0, isa1200_read_reg(client, ISA1200_HCTRL0),
+		ISA1200_HCTRL1, isa1200_read_reg(client, ISA1200_HCTRL1),
+		ISA1200_HCTRL5, isa1200_read_reg(client, ISA1200_HCTRL5));
+}
+
+static int isa1200_setup(struct i2c_client *client)
+{
+	struct isa1200_chip *haptic = i2c_get_clientdata(client);
+	int temp, rc;
+	u8 value;
+
+	gpio_set_value_cansleep(haptic->pdata->hap_en_gpio, 0);
+	if (haptic->is_len_gpio_valid == true)
+		gpio_set_value_cansleep(haptic->pdata->hap_len_gpio, 0);
+
+	udelay(250);
+
+	gpio_set_value_cansleep(haptic->pdata->hap_en_gpio, 1);
+	if (haptic->is_len_gpio_valid == true)
+		gpio_set_value_cansleep(haptic->pdata->hap_len_gpio, 1);
+
+	value =	(haptic->pdata->smart_en << 3) |
+		(haptic->pdata->is_erm << 5) |
+		(haptic->pdata->ext_clk_en << 7);
+
+	rc = isa1200_write_reg(client, ISA1200_HCTRL1, value);
+	if (rc < 0) {
+		pr_err("%s: i2c write failure\n", __func__);
+		goto reset_gpios;
+	}
+
+	if (haptic->pdata->mode_ctrl == PWM_GEN_MODE) {
+		temp = haptic->pdata->pwm_fd.pwm_div;
+		if (temp < 128 || temp > 1024 || temp % 128) {
+			pr_err("%s: Invalid divider\n", __func__);
+			goto reset_hctrl1;
+		}
+		value = ((temp >> 7) - 1);
+	} else if (haptic->pdata->mode_ctrl == PWM_INPUT_MODE) {
+		temp = haptic->pdata->pwm_fd.pwm_freq;
+		if (temp < 22400 || temp > 172600 || temp % 22400) {
+			pr_err("%s: Invalid frequency\n", __func__);
+			goto reset_hctrl1;
+		}
+		value = ((temp / 22400) - 1);
+		haptic->period_ns = NSEC_PER_SEC / temp;
+	}
+
+	value |= (haptic->pdata->mode_ctrl << 3) |
+		(haptic->pdata->overdrive_high << 5) |
+		(haptic->pdata->overdrive_en << 5) |
+		(haptic->pdata->chip_en << 7);
+
+	rc = isa1200_write_reg(client, ISA1200_HCTRL0, value);
+	if (rc < 0) {
+		pr_err("%s: i2c write failure\n", __func__);
+		goto reset_hctrl1;
+	}
+
+	/* if hen and len are seperate then pull down hen
+	 * otherwise set power down bit */
+	if (haptic->is_len_gpio_valid == true)
+		gpio_set_value_cansleep(haptic->pdata->hap_en_gpio, 0);
+	else {
+		rc = isa1200_write_reg(client, ISA1200_HCTRL0,
+					value & ISA1200_POWER_DOWN_MASK);
+		if (rc < 0) {
+			pr_err("%s: i2c write failure\n", __func__);
+			goto reset_hctrl1;
+		}
+	}
+
+	haptic->hctrl0_val = value;
+	dump_isa1200_reg("new:", client);
+	return 0;
+
+reset_hctrl1:
+	i2c_smbus_write_byte_data(client, ISA1200_HCTRL1,
+				ISA1200_HCTRL1_RESET);
+reset_gpios:
+	gpio_set_value_cansleep(haptic->pdata->hap_en_gpio, 0);
+	if (haptic->is_len_gpio_valid == true)
+		gpio_set_value_cansleep(haptic->pdata->hap_len_gpio, 0);
+	return rc;
+}
+
+static int isa1200_reg_power(struct isa1200_chip *haptic, bool on)
+{
+	const struct isa1200_regulator *reg_info =
+				haptic->pdata->regulator_info;
+	u8 i, num_reg = haptic->pdata->num_regulators;
+	int rc;
+
+	for (i = 0; i < num_reg; i++) {
+		rc = regulator_set_optimum_mode(haptic->regs[i],
+					on ? reg_info[i].load_uA : 0);
+		if (rc < 0) {
+			pr_err("%s: regulator_set_optimum_mode failed(%d)\n",
+							__func__, rc);
+			goto regs_fail;
+		}
+
+		rc = on ? regulator_enable(haptic->regs[i]) :
+			regulator_disable(haptic->regs[i]);
+		if (rc < 0) {
+			pr_err("%s: regulator %sable fail %d\n", __func__,
+					on ? "en" : "dis", rc);
+			regulator_set_optimum_mode(haptic->regs[i],
+					!on ? reg_info[i].load_uA : 0);
+			goto regs_fail;
+		}
+	}
+
+	return 0;
+
+regs_fail:
+	while (i--) {
+		regulator_set_optimum_mode(haptic->regs[i],
+				!on ? reg_info[i].load_uA : 0);
+		!on ? regulator_enable(haptic->regs[i]) :
+			regulator_disable(haptic->regs[i]);
+	}
+	return rc;
+}
+
+static int isa1200_reg_setup(struct isa1200_chip *haptic, bool on)
+{
+	const struct isa1200_regulator *reg_info =
+				haptic->pdata->regulator_info;
+	u8 i, num_reg = haptic->pdata->num_regulators;
+	int rc = 0;
+
+	/* put regulators */
+	if (on == false) {
+		i = num_reg;
+		goto put_regs;
+	}
+
+	haptic->regs = kzalloc(num_reg * sizeof(struct regulator *),
+							GFP_KERNEL);
+	if (!haptic->regs) {
+		pr_err("unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < num_reg; i++) {
+		haptic->regs[i] = regulator_get(&haptic->client->dev,
+							reg_info[i].name);
+		if (IS_ERR(haptic->regs[i])) {
+			rc = PTR_ERR(haptic->regs[i]);
+			pr_err("%s:regulator get failed(%d)\n",	__func__, rc);
+			goto put_regs;
+		}
+
+		if (regulator_count_voltages(haptic->regs[i]) > 0) {
+			rc = regulator_set_voltage(haptic->regs[i],
+				reg_info[i].min_uV, reg_info[i].max_uV);
+			if (rc) {
+				pr_err("%s: regulator_set_voltage failed(%d)\n",
+								__func__, rc);
+				regulator_put(haptic->regs[i]);
+				goto put_regs;
+			}
+		}
+	}
+
+	return rc;
+
+put_regs:
+	while (i--) {
+		if (regulator_count_voltages(haptic->regs[i]) > 0)
+			regulator_set_voltage(haptic->regs[i], 0,
+						reg_info[i].max_uV);
+		regulator_put(haptic->regs[i]);
+	}
+	kfree(haptic->regs);
+	return rc;
+}
+
+static int __devinit isa1200_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct isa1200_chip *haptic;
+	struct isa1200_platform_data *pdata;
+	int ret;
+
+	if (!i2c_check_functionality(client->adapter,
+			I2C_FUNC_SMBUS_BYTE_DATA)) {
+		dev_err(&client->dev, "%s: no support for i2c read/write"
+				"byte data\n", __func__);
+		return -EIO;
+	}
+
+	pdata = client->dev.platform_data;
+	if (!pdata) {
+		dev_err(&client->dev, "%s: no platform data\n", __func__);
+		return -EINVAL;
+	}
+
+	if (pdata->dev_setup) {
+		ret = pdata->dev_setup(true);
+		if (ret < 0) {
+			dev_err(&client->dev, "dev setup failed\n");
+			return -EINVAL;
+		}
+	}
+
+	haptic = kzalloc(sizeof(struct isa1200_chip), GFP_KERNEL);
+	if (!haptic) {
+		ret = -ENOMEM;
+		goto mem_alloc_fail;
+	}
+	haptic->client = client;
+	haptic->enable = 0;
+	haptic->pdata = pdata;
+
+	if (pdata->regulator_info) {
+		ret = isa1200_reg_setup(haptic, true);
+		if (ret) {
+			dev_err(&client->dev, "%s: regulator setup failed\n",
+							__func__);
+			goto reg_setup_fail;
+		}
+
+		ret = isa1200_reg_power(haptic, true);
+		if (ret) {
+			dev_err(&client->dev, "%s: regulator power failed\n",
+							__func__);
+			goto reg_pwr_fail;
+		}
+	}
+
+	if (pdata->power_on) {
+		ret = pdata->power_on(1);
+		if (ret) {
+			dev_err(&client->dev, "%s: power-up failed\n",
+							__func__);
+			goto pwr_up_fail;
+		}
+	}
+
+	spin_lock_init(&haptic->lock);
+	INIT_WORK(&haptic->work, isa1200_chip_work);
+	haptic->clk_on = false;
+
+	hrtimer_init(&haptic->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	haptic->timer.function = isa1200_vib_timer_func;
+
+	/*register with timed output class*/
+	haptic->dev.name = pdata->name;
+	haptic->dev.get_time = isa1200_chip_get_time;
+	haptic->dev.enable = isa1200_chip_enable;
+	ret = timed_output_dev_register(&haptic->dev);
+	if (ret < 0)
+		goto timed_reg_fail;
+
+	i2c_set_clientdata(client, haptic);
+
+	ret = gpio_is_valid(pdata->hap_en_gpio);
+	if (ret) {
+		ret = gpio_request(pdata->hap_en_gpio, "haptic_en_gpio");
+		if (ret) {
+			dev_err(&client->dev, "%s: gpio %d request failed\n",
+					__func__, pdata->hap_en_gpio);
+			goto hen_gpio_fail;
+		}
+	} else {
+		dev_err(&client->dev, "%s: Invalid gpio %d\n", __func__,
+					pdata->hap_en_gpio);
+		goto hen_gpio_fail;
+	}
+
+	haptic->is_len_gpio_valid = true;
+	ret = gpio_is_valid(haptic->pdata->hap_len_gpio);
+	if (ret) {
+		ret = gpio_request(pdata->hap_len_gpio,
+					"haptic_ldo_gpio");
+		if (ret) {
+			dev_err(&client->dev,
+				"%s: gpio %d request failed\n",
+				__func__, pdata->hap_len_gpio);
+			goto len_gpio_fail;
+		}
+	} else {
+		dev_err(&client->dev, "%s: gpio is not used/Invalid %d\n",
+					__func__, pdata->hap_len_gpio);
+		haptic->is_len_gpio_valid = false;
+	}
+
+	ret = isa1200_setup(client);
+	if (ret) {
+		dev_err(&client->dev, "%s: setup fail %d\n", __func__, ret);
+		goto setup_fail;
+	}
+
+	if (haptic->pdata->mode_ctrl == PWM_INPUT_MODE) {
+		haptic->pwm = pwm_request(pdata->pwm_ch_id, id->name);
+		if (IS_ERR(haptic->pwm)) {
+			dev_err(&client->dev, "%s: pwm request failed\n",
+							__func__);
+			ret = PTR_ERR(haptic->pwm);
+			goto reset_hctrl0;
+		}
+	}
+
+	printk(KERN_INFO "%s: %s registered\n", __func__, id->name);
+	return 0;
+
+reset_hctrl0:
+	gpio_set_value_cansleep(haptic->pdata->hap_en_gpio, 0);
+	if (haptic->is_len_gpio_valid == true)
+		gpio_set_value_cansleep(haptic->pdata->hap_len_gpio, 0);
+	i2c_smbus_write_byte_data(client, ISA1200_HCTRL1,
+				ISA1200_HCTRL1_RESET);
+	i2c_smbus_write_byte_data(client, ISA1200_HCTRL0,
+					ISA1200_HCTRL0_RESET);
+setup_fail:
+	if (haptic->is_len_gpio_valid == true)
+		gpio_free(pdata->hap_len_gpio);
+len_gpio_fail:
+	gpio_free(pdata->hap_en_gpio);
+hen_gpio_fail:
+	timed_output_dev_unregister(&haptic->dev);
+timed_reg_fail:
+	if (pdata->power_on)
+		pdata->power_on(0);
+pwr_up_fail:
+	if (pdata->regulator_info)
+		isa1200_reg_power(haptic, false);
+reg_pwr_fail:
+	if (pdata->regulator_info)
+		isa1200_reg_setup(haptic, false);
+reg_setup_fail:
+	kfree(haptic);
+mem_alloc_fail:
+	if (pdata->dev_setup)
+		pdata->dev_setup(false);
+	return ret;
+}
+
+static int __devexit isa1200_remove(struct i2c_client *client)
+{
+	struct isa1200_chip *haptic = i2c_get_clientdata(client);
+
+	hrtimer_cancel(&haptic->timer);
+	cancel_work_sync(&haptic->work);
+
+	/* turn-off current vibration */
+	isa1200_vib_set(haptic, 0);
+
+	if (haptic->pdata->mode_ctrl == PWM_INPUT_MODE)
+		pwm_free(haptic->pwm);
+
+	timed_output_dev_unregister(&haptic->dev);
+
+	gpio_set_value_cansleep(haptic->pdata->hap_en_gpio, 0);
+	if (haptic->is_len_gpio_valid == true)
+		gpio_set_value_cansleep(haptic->pdata->hap_len_gpio, 0);
+
+	gpio_free(haptic->pdata->hap_en_gpio);
+	if (haptic->is_len_gpio_valid == true)
+		gpio_free(haptic->pdata->hap_len_gpio);
+
+	/* reset hardware registers */
+	i2c_smbus_write_byte_data(client, ISA1200_HCTRL0,
+				ISA1200_HCTRL0_RESET);
+	i2c_smbus_write_byte_data(client, ISA1200_HCTRL1,
+				ISA1200_HCTRL1_RESET);
+
+
+	/* power-off the chip */
+	if (haptic->pdata->regulator_info) {
+		isa1200_reg_power(haptic, false);
+		isa1200_reg_setup(haptic, false);
+	}
+
+	if (haptic->pdata->power_on)
+		haptic->pdata->power_on(0);
+
+	if (haptic->pdata->dev_setup)
+		haptic->pdata->dev_setup(false);
+
+	kfree(haptic);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int isa1200_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+	struct isa1200_chip *haptic = i2c_get_clientdata(client);
+	int ret;
+
+	hrtimer_cancel(&haptic->timer);
+	cancel_work_sync(&haptic->work);
+	/* turn-off current vibration */
+	isa1200_vib_set(haptic, 0);
+
+	gpio_set_value_cansleep(haptic->pdata->hap_en_gpio, 0);
+	if (haptic->is_len_gpio_valid == true)
+		gpio_set_value_cansleep(haptic->pdata->hap_len_gpio, 0);
+
+	if (haptic->pdata->regulator_info)
+		isa1200_reg_power(haptic, false);
+
+	if (haptic->pdata->power_on) {
+		ret = haptic->pdata->power_on(0);
+		if (ret) {
+			dev_err(&client->dev, "power-down failed\n");
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int isa1200_resume(struct i2c_client *client)
+{
+	struct isa1200_chip *haptic = i2c_get_clientdata(client);
+	int ret;
+
+	if (haptic->pdata->regulator_info)
+		isa1200_reg_power(haptic, true);
+
+	if (haptic->pdata->power_on) {
+		ret = haptic->pdata->power_on(1);
+		if (ret) {
+			dev_err(&client->dev, "power-up failed\n");
+			return ret;
+		}
+	}
+
+	isa1200_setup(client);
+	return 0;
+}
+#else
+#define isa1200_suspend		NULL
+#define isa1200_resume		NULL
+#endif
+
+static const struct i2c_device_id isa1200_id[] = {
+	{ "isa1200_1", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, isa1200_id);
+
+static struct i2c_driver isa1200_driver = {
+	.driver	= {
+		.name	= "isa1200",
+	},
+	.probe		= isa1200_probe,
+	.remove		= __devexit_p(isa1200_remove),
+	.suspend	= isa1200_suspend,
+	.resume		= isa1200_resume,
+	.id_table	= isa1200_id,
+};
+
+static int __init isa1200_init(void)
+{
+	return i2c_add_driver(&isa1200_driver);
+}
+
+static void __exit isa1200_exit(void)
+{
+	i2c_del_driver(&isa1200_driver);
+}
+
+module_init(isa1200_init);
+module_exit(isa1200_exit);
+
+MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>");
+MODULE_DESCRIPTION("ISA1200 Haptic Motor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/msm_migrate_pages.c b/drivers/misc/msm_migrate_pages.c
new file mode 100644
index 0000000..6dcdd02
--- /dev/null
+++ b/drivers/misc/msm_migrate_pages.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/memory.h>
+#include <linux/memory_hotplug.h>
+#include <linux/module.h>
+#include <mach/msm_migrate_pages.h>
+
+static unsigned long unstable_memory_state;
+
+unsigned long get_msm_migrate_pages_status(void)
+{
+	return unstable_memory_state;
+}
+EXPORT_SYMBOL(get_msm_migrate_pages_status);
+
+#ifdef CONFIG_MEMORY_HOTPLUG
+static int migrate_pages_callback(struct notifier_block *self,
+				unsigned long action, void *arg)
+{
+	int ret = 0;
+
+	switch (action) {
+	case MEM_ONLINE:
+		unstable_memory_state = action;
+		break;
+	case MEM_OFFLINE:
+		unstable_memory_state = action;
+		break;
+	case MEM_GOING_OFFLINE:
+	case MEM_GOING_ONLINE:
+	case MEM_CANCEL_ONLINE:
+	case MEM_CANCEL_OFFLINE:
+		break;
+	}
+	return ret;
+}
+#endif
+
+static int __devinit msm_migrate_pages_probe(struct platform_device *pdev)
+{
+#ifdef CONFIG_MEMORY_HOTPLUG
+	hotplug_memory_notifier(migrate_pages_callback, 0);
+#endif
+	unstable_memory_state = 0;
+	return 0;
+}
+
+static struct platform_driver msm_migrate_pages_driver = {
+	.probe = msm_migrate_pages_probe,
+	.driver = {
+		.name = "msm_migrate_pages",
+	},
+};
+
+static int __init msm_migrate_pages_init(void)
+{
+	return platform_driver_register(&msm_migrate_pages_driver);
+}
+
+static void __exit msm_migrate_pages_exit(void)
+{
+	platform_driver_unregister(&msm_migrate_pages_driver);
+}
+
+module_init(msm_migrate_pages_init);
+module_exit(msm_migrate_pages_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Get Status of Unstable Memory Region");
diff --git a/drivers/misc/pm8xxx-nfc.c b/drivers/misc/pm8xxx-nfc.c
new file mode 100644
index 0000000..1aaa3e3
--- /dev/null
+++ b/drivers/misc/pm8xxx-nfc.c
@@ -0,0 +1,311 @@
+/* Copyright (c) 2010,2011 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+/*
+ * Qualcomm PMIC8XXX NFC driver
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/nfc.h>
+
+/* PM8XXX NFC */
+#define SSBI_REG_NFC_CTRL	0x14D
+#define SSBI_REG_NFC_TEST	0x14E
+
+/* NFC_CTRL */
+#define PM8XXX_NFC_SUPPORT_EN		0x80
+#define PM8XXX_NFC_LDO_EN		0x40
+#define PM8XXX_NFC_EN			0x20
+#define PM8XXX_NFC_EXT_VDDLDO_EN	0x10
+#define PM8XXX_NFC_VPH_PWR_EN		0x08
+#define PM8XXX_NFC_RESERVED		0x04
+#define PM8XXX_NFC_VDDLDO_LEVEL		0x03
+
+/* NFC_TEST */
+#define PM8XXX_NFC_VDDLDO_MON_EN	0x80
+#define PM8XXX_NFC_ATEST_EN		0x40
+#define PM8XXX_NFC_DTEST1_EN		0x20
+#define PM8XXX_NFC_RESERVED2		0x18
+#define PM8XXX_NFC_VDDLDO_OK_S		0x04
+#define PM8XXX_NFC_MBG_EN_S		0x02
+#define PM8XXX_NFC_EXT_EN_S		0x01
+
+struct pm8xxx_nfc_device {
+	struct device *dev;
+	struct mutex		nfc_mutex;
+#if defined(CONFIG_DEBUG_FS)
+	struct dentry		*dent;
+#endif
+};
+static struct pm8xxx_nfc_device	*nfc_dev;
+
+/* APIs */
+/*
+ * pm8xxx_nfc_request - request a handle to access NFC device
+ */
+struct pm8xxx_nfc_device *pm8xxx_nfc_request(void)
+{
+	return nfc_dev;
+}
+EXPORT_SYMBOL(pm8xxx_nfc_request);
+
+/*
+ * pm8xxx_nfc_config - configure NFC signals
+ *
+ * @nfcdev: the NFC device
+ * @mask: signal mask to configure
+ * @flags: control flags
+ */
+int pm8xxx_nfc_config(struct pm8xxx_nfc_device *nfcdev, u32 mask, u32 flags)
+{
+	u8	nfc_ctrl, nfc_test, m, f;
+	int	rc;
+
+	if (nfcdev == NULL || IS_ERR(nfcdev) || !mask)
+		return -EINVAL;
+
+	mutex_lock(&nfcdev->nfc_mutex);
+
+	if (!(mask & PM_NFC_CTRL_REQ))
+		goto config_test;
+
+	rc = pm8xxx_readb(nfcdev->dev->parent, SSBI_REG_NFC_CTRL, &nfc_ctrl);
+	if (rc) {
+		pr_err("%s: FAIL pm8xxx_readb(): rc=%d (nfc_ctrl=0x%x)\n",
+		       __func__, rc, nfc_ctrl);
+		goto config_done;
+	}
+
+	m = mask & 0x00ff;
+	f = flags & 0x00ff;
+	nfc_ctrl &= ~m;
+	nfc_ctrl |= m & f;
+
+	rc = pm8xxx_writeb(nfcdev->dev->parent, SSBI_REG_NFC_CTRL, nfc_ctrl);
+	if (rc) {
+		pr_err("%s: FAIL pm8xxx_writeb(): rc=%d (nfc_ctrl=0x%x)\n",
+		       __func__, rc, nfc_ctrl);
+		goto config_done;
+	}
+
+config_test:
+	if (!(mask & PM_NFC_TEST_REQ))
+		goto config_done;
+
+	rc = pm8xxx_readb(nfcdev->dev->parent, SSBI_REG_NFC_TEST, &nfc_test);
+	if (rc) {
+		pr_err("%s: FAIL pm8xxx_readb(): rc=%d (nfc_test=0x%x)\n",
+		       __func__, rc, nfc_test);
+		goto config_done;
+	}
+
+	m = (mask >> 8) & 0x00ff;
+	f = (flags >> 8) & 0x00ff;
+	nfc_test &= ~m;
+	nfc_test |= m & f;
+
+	rc = pm8xxx_writeb(nfcdev->dev->parent, SSBI_REG_NFC_TEST, nfc_test);
+	if (rc) {
+		pr_err("%s: FAIL pm8xxx_writeb(): rc=%d (nfc_test=0x%x)\n",
+		       __func__, rc, nfc_test);
+		goto config_done;
+	}
+
+config_done:
+	mutex_unlock(&nfcdev->nfc_mutex);
+	return 0;
+}
+EXPORT_SYMBOL(pm8xxx_nfc_config);
+
+/*
+ * pm8xxx_nfc_get_status - get NFC status
+ *
+ * @nfcdev: the NFC device
+ * @mask: of status mask to read
+ * @status: pointer to the status variable
+ */
+int pm8xxx_nfc_get_status(struct pm8xxx_nfc_device *nfcdev,
+			  u32 mask, u32 *status)
+{
+	u8	nfc_ctrl, nfc_test;
+	u32	st;
+	int	rc;
+
+	if (nfcdev == NULL || IS_ERR(nfcdev) || status == NULL)
+		return -EINVAL;
+
+	st = 0;
+	mutex_lock(&nfcdev->nfc_mutex);
+
+	if (!(mask & PM_NFC_CTRL_REQ))
+		goto read_test;
+
+	rc = pm8xxx_readb(nfcdev->dev->parent, SSBI_REG_NFC_CTRL, &nfc_ctrl);
+	if (rc) {
+		pr_err("%s: FAIL pm8xxx_readb(): rc=%d (nfc_ctrl=0x%x)\n",
+		       __func__, rc, nfc_ctrl);
+		goto get_status_done;
+	}
+
+read_test:
+	if (!(mask & (PM_NFC_TEST_REQ | PM_NFC_TEST_STATUS)))
+		goto get_status_done;
+
+	rc = pm8xxx_readb(nfcdev->dev->parent, SSBI_REG_NFC_TEST, &nfc_test);
+	if (rc)
+		pr_err("%s: FAIL pm8xxx_readb(): rc=%d (nfc_test=0x%x)\n",
+		       __func__, rc, nfc_test);
+
+get_status_done:
+	st = nfc_ctrl;
+	st |= nfc_test << 8;
+	*status = st;
+
+	mutex_unlock(&nfcdev->nfc_mutex);
+	return 0;
+}
+EXPORT_SYMBOL(pm8xxx_nfc_get_status);
+
+/*
+ * pm8xxx_nfc_free - free the NFC device
+ */
+void pm8xxx_nfc_free(struct pm8xxx_nfc_device *nfcdev)
+{
+	/* Disable all signals */
+	pm8xxx_nfc_config(nfcdev, PM_NFC_CTRL_REQ, 0);
+}
+EXPORT_SYMBOL(pm8xxx_nfc_free);
+
+#if defined(CONFIG_DEBUG_FS)
+static int pm8xxx_nfc_debug_set(void *data, u64 val)
+{
+	struct pm8xxx_nfc_device *nfcdev;
+	u32	mask, control;
+	int	rc;
+
+	nfcdev = (struct pm8xxx_nfc_device *)data;
+	control = (u32)val & 0xffff;
+	mask = ((u32)val >> 16) & 0xffff;
+	rc = pm8xxx_nfc_config(nfcdev, mask, control);
+	if (rc)
+		pr_err("%s: ERR pm8xxx_nfc_config: rc=%d, "
+		       "[mask, control]=[0x%x, 0x%x]\n",
+		       __func__, rc, mask, control);
+
+	return 0;
+}
+
+static int pm8xxx_nfc_debug_get(void *data, u64 *val)
+{
+	struct pm8xxx_nfc_device *nfcdev;
+	u32	status;
+	int	rc;
+
+	nfcdev = (struct pm8xxx_nfc_device *)data;
+	rc = pm8xxx_nfc_get_status(nfcdev, (u32)-1, &status);
+	if (rc)
+		pr_err("%s: ERR pm8xxx_nfc_get_status: rc=%d, status=0x%x\n",
+		       __func__, rc, status);
+
+	if (val)
+		*val = (u64)status;
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(pm8xxx_nfc_fops, pm8xxx_nfc_debug_get,
+			pm8xxx_nfc_debug_set, "%llu\n");
+
+static int pm8xxx_nfc_debug_init(struct pm8xxx_nfc_device *nfcdev)
+{
+	struct dentry *dent;
+
+	dent = debugfs_create_file("pm8xxx-nfc", 0644, NULL,
+				   (void *)nfcdev, &pm8xxx_nfc_fops);
+
+	if (dent == NULL || IS_ERR(dent))
+		pr_err("%s: ERR debugfs_create_file: dent=0x%x\n",
+		       __func__, (unsigned)dent);
+
+	nfcdev->dent = dent;
+	return 0;
+}
+#endif
+
+static int __devinit pm8xxx_nfc_probe(struct platform_device *pdev)
+{
+	struct pm8xxx_nfc_device	*nfcdev;
+
+	nfcdev = kzalloc(sizeof *nfcdev, GFP_KERNEL);
+	if (nfcdev == NULL) {
+		pr_err("%s: kzalloc() failed.\n", __func__);
+		return -ENOMEM;
+	}
+
+	mutex_init(&nfcdev->nfc_mutex);
+
+	nfcdev->dev = &pdev->dev;
+	nfc_dev = nfcdev;
+	platform_set_drvdata(pdev, nfcdev);
+
+#if defined(CONFIG_DEBUG_FS)
+	pm8xxx_nfc_debug_init(nfc_dev);
+#endif
+
+	pr_notice("%s: OK\n", __func__);
+	return 0;
+}
+
+static int __devexit pm8xxx_nfc_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_nfc_device *nfcdev = platform_get_drvdata(pdev);
+
+#if defined(CONFIG_DEBUG_FS)
+	debugfs_remove(nfcdev->dent);
+#endif
+
+	platform_set_drvdata(pdev, NULL);
+	kfree(nfcdev);
+	return 0;
+}
+
+static struct platform_driver pm8xxx_nfc_driver = {
+	.probe		= pm8xxx_nfc_probe,
+	.remove		= __devexit_p(pm8xxx_nfc_remove),
+	.driver		= {
+		.name = PM8XXX_NFC_DEV_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init pm8xxx_nfc_init(void)
+{
+	return platform_driver_register(&pm8xxx_nfc_driver);
+}
+
+static void __exit pm8xxx_nfc_exit(void)
+{
+	platform_driver_unregister(&pm8xxx_nfc_driver);
+}
+
+module_init(pm8xxx_nfc_init);
+module_exit(pm8xxx_nfc_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PM8XXX NFC driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8XXX_NFC_DEV_NAME);
diff --git a/drivers/misc/pm8xxx-upl.c b/drivers/misc/pm8xxx-upl.c
new file mode 100644
index 0000000..d3d9746f
--- /dev/null
+++ b/drivers/misc/pm8xxx-upl.c
@@ -0,0 +1,350 @@
+/* Copyright (c) 2010,2011 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+/*
+ * Qualcomm PM8XXX UPL driver
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/upl.h>
+
+/* PMIC8XXX UPL registers */
+#define SSBI_REG_UPL_CTRL		0x17B
+#define SSBI_REG_UPL_TRUTHTABLE1	0x17C
+#define SSBI_REG_UPL_TRUTHTABLE2	0x17D
+
+struct pm8xxx_upl_device {
+	struct device		*dev;
+	struct mutex		upl_mutex;
+#if defined(CONFIG_DEBUG_FS)
+	struct dentry		*dent;
+#endif
+};
+static struct pm8xxx_upl_device *upl_dev;
+
+/* APIs */
+
+/*
+ * pm8xxx_upl_request - request a handle to access UPL device
+ */
+struct pm8xxx_upl_device *pm8xxx_upl_request(void)
+{
+	return upl_dev;
+}
+EXPORT_SYMBOL(pm8xxx_upl_request);
+
+/*
+ * pm8xxx_upl_read_truthtable - read value currently stored in UPL truth table
+ *
+ * @upldev: the UPL device
+ * @truthtable: value read from UPL truth table
+ */
+int pm8xxx_upl_read_truthtable(struct pm8xxx_upl_device *upldev,
+				u16 *truthtable)
+{
+	int rc = 0;
+	u8 table[2];
+
+	if (upldev == NULL || IS_ERR(upldev))
+		return -EINVAL;
+
+	mutex_lock(&upldev->upl_mutex);
+
+	rc = pm8xxx_readb(upldev->dev->parent, SSBI_REG_UPL_TRUTHTABLE1,
+							&(table[0]));
+	if (rc) {
+		pr_err("%s: FAIL pm8xxx_readb(0x%X)=0x%02X: rc=%d\n",
+			__func__, SSBI_REG_UPL_TRUTHTABLE1, table[0], rc);
+		goto upl_read_done;
+	}
+
+	rc = pm8xxx_readb(upldev->dev->parent, SSBI_REG_UPL_TRUTHTABLE2,
+							&(table[1]));
+	if (rc)
+		pr_err("%s: FAIL pm8xxx_readb(0x%X)=0x%02X: rc=%d\n",
+			__func__, SSBI_REG_UPL_TRUTHTABLE2, table[1], rc);
+upl_read_done:
+	mutex_unlock(&upldev->upl_mutex);
+	*truthtable = (((u16)table[1]) << 8) | table[0];
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_upl_read_truthtable);
+
+/*
+ * pm8xxx_upl_writes_truthtable - write value into UPL truth table
+ *
+ * @upldev: the UPL device
+ * @truthtable: value written to UPL truth table
+ *
+ * Each bit in parameter "truthtable" corresponds to the UPL output for a given
+ * set of input pin values. For example, if the input pins have the following
+ * values: A=1, B=1, C=1, D=0, then the UPL would output the value of bit 14
+ * (0b1110) in parameter "truthtable".
+ */
+int pm8xxx_upl_write_truthtable(struct pm8xxx_upl_device *upldev,
+				u16 truthtable)
+{
+	int rc = 0;
+	u8 table[2];
+
+	if (upldev == NULL || IS_ERR(upldev))
+		return -EINVAL;
+
+	table[0] = truthtable & 0xFF;
+	table[1] = (truthtable >> 8) & 0xFF;
+
+	mutex_lock(&upldev->upl_mutex);
+
+	rc = pm8xxx_writeb(upldev->dev->parent, SSBI_REG_UPL_TRUTHTABLE1,
+								table[0]);
+	if (rc) {
+		pr_err("%s: FAIL pm8xxx_writeb(0x%X)=0x%04X: rc=%d\n",
+			__func__, SSBI_REG_UPL_TRUTHTABLE1, table[0], rc);
+		goto upl_write_done;
+	}
+
+	rc = pm8xxx_writeb(upldev->dev->parent, SSBI_REG_UPL_TRUTHTABLE2,
+								table[1]);
+	if (rc)
+		pr_err("%s: FAIL pm8xxx_writeb(0x%X)=0x%04X: rc=%d\n",
+			__func__, SSBI_REG_UPL_TRUTHTABLE2, table[1], rc);
+upl_write_done:
+	mutex_unlock(&upldev->upl_mutex);
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_upl_write_truthtable);
+
+/*
+ * pm8xxx_upl_config - configure UPL I/O settings and UPL enable/disable
+ *
+ * @upldev: the UPL device
+ * @mask: setting mask to configure
+ * @flags: setting flags
+ */
+int pm8xxx_upl_config(struct pm8xxx_upl_device *upldev, u32 mask, u32 flags)
+{
+	int rc;
+	u8 upl_ctrl, m, f;
+
+	if (upldev == NULL || IS_ERR(upldev))
+		return -EINVAL;
+
+	mutex_lock(&upldev->upl_mutex);
+
+	rc = pm8xxx_readb(upldev->dev->parent, SSBI_REG_UPL_CTRL, &upl_ctrl);
+	if (rc) {
+		pr_err("%s: FAIL pm8xxx_readb(0x%X)=0x%02X: rc=%d\n",
+			__func__, SSBI_REG_UPL_CTRL, upl_ctrl, rc);
+		goto upl_config_done;
+	}
+
+	m = mask & 0x00ff;
+	f = flags & 0x00ff;
+	upl_ctrl &= ~m;
+	upl_ctrl |= m & f;
+
+	rc = pm8xxx_writeb(upldev->dev->parent, SSBI_REG_UPL_CTRL, upl_ctrl);
+	if (rc)
+		pr_err("%s: FAIL pm8xxx_writeb(0x%X)=0x%02X: rc=%d\n",
+			__func__, SSBI_REG_UPL_CTRL, upl_ctrl, rc);
+upl_config_done:
+	mutex_unlock(&upldev->upl_mutex);
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_upl_config);
+
+#if defined(CONFIG_DEBUG_FS)
+
+static int truthtable_set(void *data, u64 val)
+{
+	int rc;
+
+	rc = pm8xxx_upl_write_truthtable(data, val);
+	if (rc)
+		pr_err("%s: pm8xxx_upl_write_truthtable: rc=%d, "
+			"truthtable=0x%llX\n", __func__, rc, val);
+	return rc;
+}
+
+static int truthtable_get(void *data, u64 *val)
+{
+	int rc;
+	u16 truthtable;
+
+	rc = pm8xxx_upl_read_truthtable(data, &truthtable);
+	if (rc)
+		pr_err("%s: pm8xxx_upl_read_truthtable: rc=%d, "
+			"truthtable=0x%X\n", __func__, rc, truthtable);
+	if (val)
+		*val = truthtable;
+
+	return rc;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(upl_truthtable_fops, truthtable_get,
+			truthtable_set, "0x%04llX\n");
+
+/* enter values as 0xMMMMFFFF where MMMM is the mask and FFFF is the flags */
+static int control_set(void *data, u64 val)
+{
+	u8 mask, flags;
+	int rc;
+
+	flags = val & 0xFFFF;
+	mask = (val >> 16) & 0xFFFF;
+
+	rc = pm8xxx_upl_config(data, mask, flags);
+	if (rc)
+		pr_err("%s: pm8xxx_upl_config: rc=%d, mask = 0x%X, "
+			"flags = 0x%X\n", __func__, rc, mask, flags);
+	return rc;
+}
+
+static int control_get(void *data, u64 *val)
+{
+	struct pm8xxx_upl_device *upldev;
+	int rc = 0;
+	u8 ctrl;
+
+	upldev = data;
+
+	mutex_lock(&upldev->upl_mutex);
+
+	rc = pm8xxx_readb(upldev->dev->parent, SSBI_REG_UPL_CTRL, &ctrl);
+	if (rc)
+		pr_err("%s: FAIL pm8xxx_readb(): rc=%d (ctrl=0x%02X)\n",
+		       __func__, rc, ctrl);
+
+	mutex_unlock(&upldev->upl_mutex);
+
+	*val = ctrl;
+
+	return rc;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(upl_control_fops, control_get,
+			control_set, "0x%02llX\n");
+
+static int pm8xxx_upl_debug_init(struct pm8xxx_upl_device *upldev)
+{
+	struct dentry *dent;
+	struct dentry *temp;
+
+	dent = debugfs_create_dir("pm8xxx-upl", NULL);
+	if (dent == NULL || IS_ERR(dent)) {
+		pr_err("%s: ERR debugfs_create_dir: dent=0x%X\n",
+					__func__, (unsigned)dent);
+		return -ENOMEM;
+	}
+
+	temp = debugfs_create_file("truthtable", S_IRUSR | S_IWUSR, dent,
+					upldev, &upl_truthtable_fops);
+	if (temp == NULL || IS_ERR(temp)) {
+		pr_err("%s: ERR debugfs_create_file: dent=0x%X\n",
+					__func__, (unsigned)dent);
+		goto debug_error;
+	}
+
+	temp = debugfs_create_file("control", S_IRUSR | S_IWUSR, dent,
+					upldev, &upl_control_fops);
+	if (temp == NULL || IS_ERR(temp)) {
+		pr_err("%s: ERR debugfs_create_file: dent=0x%X\n",
+					__func__, (unsigned)dent);
+		goto debug_error;
+	}
+
+	upldev->dent = dent;
+	return 0;
+
+debug_error:
+	debugfs_remove_recursive(dent);
+	return -ENOMEM;
+}
+
+static int __devexit pm8xxx_upl_debug_remove(struct pm8xxx_upl_device *upldev)
+{
+	debugfs_remove_recursive(upldev->dent);
+	return 0;
+}
+
+#endif /* CONFIG_DEBUG_FS */
+
+static int __devinit pm8xxx_upl_probe(struct platform_device *pdev)
+{
+	struct pm8xxx_upl_device	*upldev;
+
+	upldev = kzalloc(sizeof *upldev, GFP_KERNEL);
+	if (upldev == NULL) {
+		pr_err("%s: kzalloc() failed.\n", __func__);
+		return -ENOMEM;
+	}
+
+	mutex_init(&upldev->upl_mutex);
+
+	upl_dev = upldev;
+	upldev->dev = &pdev->dev;
+	platform_set_drvdata(pdev, upldev);
+
+#if defined(CONFIG_DEBUG_FS)
+	pm8xxx_upl_debug_init(upl_dev);
+#endif
+	pr_notice("%s: OK\n", __func__);
+	return 0;
+}
+
+static int __devexit pm8xxx_upl_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_upl_device *upldev = platform_get_drvdata(pdev);
+
+#if defined(CONFIG_DEBUG_FS)
+	pm8xxx_upl_debug_remove(upldev);
+#endif
+
+	platform_set_drvdata(pdev, NULL);
+	kfree(upldev);
+	pr_notice("%s: OK\n", __func__);
+
+	return 0;
+}
+
+static struct platform_driver pm8xxx_upl_driver = {
+	.probe		= pm8xxx_upl_probe,
+	.remove		= __devexit_p(pm8xxx_upl_remove),
+	.driver		= {
+		.name = PM8XXX_UPL_DEV_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init pm8xxx_upl_init(void)
+{
+	return platform_driver_register(&pm8xxx_upl_driver);
+}
+
+static void __exit pm8xxx_upl_exit(void)
+{
+	platform_driver_unregister(&pm8xxx_upl_driver);
+}
+
+module_init(pm8xxx_upl_init);
+module_exit(pm8xxx_upl_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PM8XXX UPL driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8XXX_UPL_DEV_NAME);
diff --git a/drivers/misc/pm8xxx-vibrator.c b/drivers/misc/pm8xxx-vibrator.c
new file mode 100644
index 0000000..62e7b45
--- /dev/null
+++ b/drivers/misc/pm8xxx-vibrator.c
@@ -0,0 +1,325 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/vibrator.h>
+
+#include "../staging/android/timed_output.h"
+
+#define VIB_DRV			0x4A
+
+#define VIB_DRV_SEL_MASK	0xf8
+#define VIB_DRV_SEL_SHIFT	0x03
+#define VIB_DRV_EN_MANUAL_MASK	0xfc
+#define VIB_DRV_LOGIC_SHIFT	0x2
+
+#define VIB_MAX_LEVEL_mV	3100
+#define VIB_MIN_LEVEL_mV	1200
+
+struct pm8xxx_vib {
+	struct hrtimer vib_timer;
+	struct timed_output_dev timed_dev;
+	spinlock_t lock;
+	struct work_struct work;
+	struct device *dev;
+	const struct pm8xxx_vibrator_platform_data *pdata;
+	int state;
+	int level;
+	u8  reg_vib_drv;
+};
+
+static struct pm8xxx_vib *vib_dev;
+
+int pm8xxx_vibrator_config(struct pm8xxx_vib_config *vib_config)
+{
+	u8 reg = 0;
+	int rc;
+
+	if (vib_dev == NULL) {
+		pr_err("%s: vib_dev is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	if (vib_config->drive_mV) {
+		if ((vib_config->drive_mV < VIB_MIN_LEVEL_mV) ||
+			(vib_config->drive_mV > VIB_MAX_LEVEL_mV)) {
+			pr_err("Invalid vibrator drive strength\n");
+			return -EINVAL;
+		}
+	}
+
+	reg = (vib_config->drive_mV / 100) << VIB_DRV_SEL_SHIFT;
+
+	reg |= (!!vib_config->active_low) << VIB_DRV_LOGIC_SHIFT;
+
+	reg |= vib_config->enable_mode;
+
+	rc = pm8xxx_writeb(vib_dev->dev->parent, VIB_DRV, reg);
+	if (rc)
+		pr_err("%s: pm8xxx write failed: rc=%d\n", __func__, rc);
+
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_vibrator_config);
+
+/* REVISIT: just for debugging, will be removed in final working version */
+static void __dump_vib_regs(struct pm8xxx_vib *vib, char *msg)
+{
+	u8 temp;
+
+	dev_dbg(vib->dev, "%s\n", msg);
+
+	pm8xxx_readb(vib->dev->parent, VIB_DRV, &temp);
+	dev_dbg(vib->dev, "VIB_DRV - %X\n", temp);
+}
+
+static int pm8xxx_vib_read_u8(struct pm8xxx_vib *vib,
+				 u8 *data, u16 reg)
+{
+	int rc;
+
+	rc = pm8xxx_readb(vib->dev->parent, reg, data);
+	if (rc < 0)
+		dev_warn(vib->dev, "Error reading pm8xxx: %X - ret %X\n",
+				reg, rc);
+
+	return rc;
+}
+
+static int pm8xxx_vib_write_u8(struct pm8xxx_vib *vib,
+				 u8 data, u16 reg)
+{
+	int rc;
+
+	rc = pm8xxx_writeb(vib->dev->parent, reg, data);
+	if (rc < 0)
+		dev_warn(vib->dev, "Error writing pm8xxx: %X - ret %X\n",
+				reg, rc);
+	return rc;
+}
+
+static int pm8xxx_vib_set(struct pm8xxx_vib *vib, int on)
+{
+	int rc;
+	u8 val;
+
+	if (on) {
+		val = vib->reg_vib_drv;
+		val |= ((vib->level << VIB_DRV_SEL_SHIFT) & VIB_DRV_SEL_MASK);
+		rc = pm8xxx_vib_write_u8(vib, val, VIB_DRV);
+		if (rc < 0)
+			return rc;
+		vib->reg_vib_drv = val;
+	} else {
+		val = vib->reg_vib_drv;
+		val &= ~VIB_DRV_SEL_MASK;
+		rc = pm8xxx_vib_write_u8(vib, val, VIB_DRV);
+		if (rc < 0)
+			return rc;
+		vib->reg_vib_drv = val;
+	}
+	__dump_vib_regs(vib, "vib_set_end");
+
+	return rc;
+}
+
+static void pm8xxx_vib_enable(struct timed_output_dev *dev, int value)
+{
+	struct pm8xxx_vib *vib = container_of(dev, struct pm8xxx_vib,
+					 timed_dev);
+	unsigned long flags;
+
+retry:
+	spin_lock_irqsave(&vib->lock, flags);
+	if (hrtimer_try_to_cancel(&vib->vib_timer) < 0) {
+		spin_unlock_irqrestore(&vib->lock, flags);
+		cpu_relax();
+		goto retry;
+	}
+
+	if (value == 0)
+		vib->state = 0;
+	else {
+		value = (value > vib->pdata->max_timeout_ms ?
+				 vib->pdata->max_timeout_ms : value);
+		vib->state = 1;
+		hrtimer_start(&vib->vib_timer,
+			      ktime_set(value / 1000, (value % 1000) * 1000000),
+			      HRTIMER_MODE_REL);
+	}
+	spin_unlock_irqrestore(&vib->lock, flags);
+	schedule_work(&vib->work);
+}
+
+static void pm8xxx_vib_update(struct work_struct *work)
+{
+	struct pm8xxx_vib *vib = container_of(work, struct pm8xxx_vib,
+					 work);
+
+	pm8xxx_vib_set(vib, vib->state);
+}
+
+static int pm8xxx_vib_get_time(struct timed_output_dev *dev)
+{
+	struct pm8xxx_vib *vib = container_of(dev, struct pm8xxx_vib,
+							 timed_dev);
+
+	if (hrtimer_active(&vib->vib_timer)) {
+		ktime_t r = hrtimer_get_remaining(&vib->vib_timer);
+		return (int)ktime_to_us(r);
+	} else
+		return 0;
+}
+
+static enum hrtimer_restart pm8xxx_vib_timer_func(struct hrtimer *timer)
+{
+	struct pm8xxx_vib *vib = container_of(timer, struct pm8xxx_vib,
+							 vib_timer);
+
+	vib->state = 0;
+	schedule_work(&vib->work);
+
+	return HRTIMER_NORESTART;
+}
+
+#ifdef CONFIG_PM
+static int pm8xxx_vib_suspend(struct device *dev)
+{
+	struct pm8xxx_vib *vib = dev_get_drvdata(dev);
+
+	hrtimer_cancel(&vib->vib_timer);
+	cancel_work_sync(&vib->work);
+	/* turn-off vibrator */
+	pm8xxx_vib_set(vib, 0);
+
+	return 0;
+}
+
+static const struct dev_pm_ops pm8xxx_vib_pm_ops = {
+	.suspend = pm8xxx_vib_suspend,
+};
+#endif
+
+static int __devinit pm8xxx_vib_probe(struct platform_device *pdev)
+
+{
+	const struct pm8xxx_vibrator_platform_data *pdata =
+						pdev->dev.platform_data;
+	struct pm8xxx_vib *vib;
+	u8 val;
+	int rc;
+
+	if (!pdata)
+		return -EINVAL;
+
+	if (pdata->level_mV < VIB_MIN_LEVEL_mV ||
+			 pdata->level_mV > VIB_MAX_LEVEL_mV)
+		return -EINVAL;
+
+	vib = kzalloc(sizeof(*vib), GFP_KERNEL);
+	if (!vib)
+		return -ENOMEM;
+
+	vib->pdata	= pdata;
+	vib->level	= pdata->level_mV / 100;
+	vib->dev	= &pdev->dev;
+
+	spin_lock_init(&vib->lock);
+	INIT_WORK(&vib->work, pm8xxx_vib_update);
+
+	hrtimer_init(&vib->vib_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	vib->vib_timer.function = pm8xxx_vib_timer_func;
+
+	vib->timed_dev.name = "vibrator";
+	vib->timed_dev.get_time = pm8xxx_vib_get_time;
+	vib->timed_dev.enable = pm8xxx_vib_enable;
+
+	__dump_vib_regs(vib, "boot_vib_default");
+
+	/*
+	 * Configure the vibrator, it operates in manual mode
+	 * for timed_output framework.
+	 */
+	rc = pm8xxx_vib_read_u8(vib, &val, VIB_DRV);
+	if (rc < 0)
+		goto err_read_vib;
+	val &= ~VIB_DRV_EN_MANUAL_MASK;
+	rc = pm8xxx_vib_write_u8(vib, val, VIB_DRV);
+	if (rc < 0)
+		goto err_read_vib;
+
+	vib->reg_vib_drv = val;
+
+	rc = timed_output_dev_register(&vib->timed_dev);
+	if (rc < 0)
+		goto err_read_vib;
+
+	pm8xxx_vib_enable(&vib->timed_dev, pdata->initial_vibrate_ms);
+
+	platform_set_drvdata(pdev, vib);
+
+	vib_dev = vib;
+
+	return 0;
+
+err_read_vib:
+	kfree(vib);
+	return rc;
+}
+
+static int __devexit pm8xxx_vib_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_vib *vib = platform_get_drvdata(pdev);
+
+	cancel_work_sync(&vib->work);
+	hrtimer_cancel(&vib->vib_timer);
+	timed_output_dev_unregister(&vib->timed_dev);
+	platform_set_drvdata(pdev, NULL);
+	kfree(vib);
+
+	return 0;
+}
+
+static struct platform_driver pm8xxx_vib_driver = {
+	.probe		= pm8xxx_vib_probe,
+	.remove		= __devexit_p(pm8xxx_vib_remove),
+	.driver		= {
+		.name	= PM8XXX_VIBRATOR_DEV_NAME,
+		.owner	= THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm	= &pm8xxx_vib_pm_ops,
+#endif
+	},
+};
+
+static int __init pm8xxx_vib_init(void)
+{
+	return platform_driver_register(&pm8xxx_vib_driver);
+}
+module_init(pm8xxx_vib_init);
+
+static void __exit pm8xxx_vib_exit(void)
+{
+	platform_driver_unregister(&pm8xxx_vib_driver);
+}
+module_exit(pm8xxx_vib_exit);
+
+MODULE_ALIAS("platform:" PM8XXX_VIBRATOR_DEV_NAME);
+MODULE_DESCRIPTION("pm8xxx vibrator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/pmem.c b/drivers/misc/pmem.c
new file mode 100644
index 0000000..0ab5b69
--- /dev/null
+++ b/drivers/misc/pmem.c
@@ -0,0 +1,2961 @@
+/* drivers/android/pmem.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ *
+ * 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/export.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/fmem.h>
+#include <linux/mm.h>
+#include <linux/list.h>
+#include <linux/debugfs.h>
+#include <linux/android_pmem.h>
+#include <linux/mempolicy.h>
+#include <linux/sched.h>
+#include <linux/kobject.h>
+#include <linux/pm_runtime.h>
+#include <linux/memory_alloc.h>
+#include <linux/vmalloc.h>
+#include <linux/io.h>
+#include <linux/mm_types.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/cacheflush.h>
+#include <asm/sizes.h>
+#include <asm/mach/map.h>
+#include <asm/page.h>
+
+#define PMEM_MAX_DEVICES (10)
+
+#define PMEM_MAX_ORDER (128)
+#define PMEM_MIN_ALLOC PAGE_SIZE
+
+#define PMEM_INITIAL_NUM_BITMAP_ALLOCATIONS (64)
+
+#define PMEM_32BIT_WORD_ORDER (5)
+#define PMEM_BITS_PER_WORD_MASK (BITS_PER_LONG - 1)
+
+#ifdef CONFIG_ANDROID_PMEM_DEBUG
+#define PMEM_DEBUG 1
+#else
+#define PMEM_DEBUG 0
+#endif
+
+#define SYSTEM_ALLOC_RETRY 10
+
+/* indicates that a refernce to this file has been taken via get_pmem_file,
+ * the file should not be released until put_pmem_file is called */
+#define PMEM_FLAGS_BUSY 0x1
+/* indicates that this is a suballocation of a larger master range */
+#define PMEM_FLAGS_CONNECTED 0x1 << 1
+/* indicates this is a master and not a sub allocation and that it is mmaped */
+#define PMEM_FLAGS_MASTERMAP 0x1 << 2
+/* submap and unsubmap flags indicate:
+ * 00: subregion has never been mmaped
+ * 10: subregion has been mmaped, reference to the mm was taken
+ * 11: subretion has ben released, refernece to the mm still held
+ * 01: subretion has been released, reference to the mm has been released
+ */
+#define PMEM_FLAGS_SUBMAP 0x1 << 3
+#define PMEM_FLAGS_UNSUBMAP 0x1 << 4
+
+struct pmem_data {
+	/* in alloc mode: an index into the bitmap
+	 * in no_alloc mode: the size of the allocation */
+	int index;
+	/* see flags above for descriptions */
+	unsigned int flags;
+	/* protects this data field, if the mm_mmap sem will be held at the
+	 * same time as this sem, the mm sem must be taken first (as this is
+	 * the order for vma_open and vma_close ops */
+	struct rw_semaphore sem;
+	/* info about the mmaping process */
+	struct vm_area_struct *vma;
+	/* task struct of the mapping process */
+	struct task_struct *task;
+	/* process id of teh mapping process */
+	pid_t pid;
+	/* file descriptor of the master */
+	int master_fd;
+	/* file struct of the master */
+	struct file *master_file;
+	/* a list of currently available regions if this is a suballocation */
+	struct list_head region_list;
+	/* a linked list of data so we can access them for debugging */
+	struct list_head list;
+#if PMEM_DEBUG
+	int ref;
+#endif
+};
+
+struct pmem_bits {
+	unsigned allocated:1;		/* 1 if allocated, 0 if free */
+	unsigned order:7;		/* size of the region in pmem space */
+};
+
+struct pmem_region_node {
+	struct pmem_region region;
+	struct list_head list;
+};
+
+#define PMEM_DEBUG_MSGS 0
+#if PMEM_DEBUG_MSGS
+#define DLOG(fmt,args...) \
+	do { pr_debug("[%s:%s:%d] "fmt, __FILE__, __func__, __LINE__, \
+		    ##args); } \
+	while (0)
+#else
+#define DLOG(x...) do {} while (0)
+#endif
+
+enum pmem_align {
+	PMEM_ALIGN_4K,
+	PMEM_ALIGN_1M,
+};
+
+#define PMEM_NAME_SIZE 16
+
+struct alloc_list {
+	void *addr;                  /* physical addr of allocation */
+	void *aaddr;                 /* aligned physical addr       */
+	unsigned int size;           /* total size of allocation    */
+	unsigned char __iomem *vaddr; /* Virtual addr                */
+	struct list_head allocs;
+};
+
+struct pmem_info {
+	struct miscdevice dev;
+	/* physical start address of the remaped pmem space */
+	unsigned long base;
+	/* vitual start address of the remaped pmem space */
+	unsigned char __iomem *vbase;
+	/* total size of the pmem space */
+	unsigned long size;
+	/* number of entries in the pmem space */
+	unsigned long num_entries;
+	/* pfn of the garbage page in memory */
+	unsigned long garbage_pfn;
+	/* which memory type (i.e. SMI, EBI1) this PMEM device is backed by */
+	unsigned memory_type;
+
+	char name[PMEM_NAME_SIZE];
+
+	/* index of the garbage page in the pmem space */
+	int garbage_index;
+	/* reserved virtual address range */
+	struct vm_struct *area;
+
+	enum pmem_allocator_type allocator_type;
+
+	int (*allocate)(const int,
+			const unsigned long,
+			const unsigned int);
+	int (*free)(int, int);
+	int (*free_space)(int, struct pmem_freespace *);
+	unsigned long (*len)(int, struct pmem_data *);
+	unsigned long (*start_addr)(int, struct pmem_data *);
+
+	/* actual size of memory element, e.g.: (4 << 10) is 4K */
+	unsigned int quantum;
+
+	/* indicates maps of this region should be cached, if a mix of
+	 * cached and uncached is desired, set this and open the device with
+	 * O_SYNC to get an uncached region */
+	unsigned cached;
+	unsigned buffered;
+	union {
+		struct {
+			/* in all_or_nothing allocator mode the first mapper
+			 * gets the whole space and sets this flag */
+			unsigned allocated;
+		} all_or_nothing;
+
+		struct {
+			/* the buddy allocator bitmap for the region
+			 * indicating which entries are allocated and which
+			 * are free.
+			 */
+
+			struct pmem_bits *buddy_bitmap;
+		} buddy_bestfit;
+
+		struct {
+			unsigned int bitmap_free; /* # of zero bits/quanta */
+			uint32_t *bitmap;
+			int32_t bitmap_allocs;
+			struct {
+				short bit;
+				unsigned short quanta;
+			} *bitm_alloc;
+		} bitmap;
+
+		struct {
+			unsigned long used;      /* Bytes currently allocated */
+			struct list_head alist;  /* List of allocations       */
+		} system_mem;
+	} allocator;
+
+	int id;
+	struct kobject kobj;
+
+	/* for debugging, creates a list of pmem file structs, the
+	 * data_list_mutex should be taken before pmem_data->sem if both are
+	 * needed */
+	struct mutex data_list_mutex;
+	struct list_head data_list;
+	/* arena_mutex protects the global allocation arena
+	 *
+	 * IF YOU TAKE BOTH LOCKS TAKE THEM IN THIS ORDER:
+	 * down(pmem_data->sem) => mutex_lock(arena_mutex)
+	 */
+	struct mutex arena_mutex;
+
+	long (*ioctl)(struct file *, unsigned int, unsigned long);
+	int (*release)(struct inode *, struct file *);
+	/* reference count of allocations */
+	atomic_t allocation_cnt;
+	/*
+	 * request function for a region when the allocation count goes
+	 * from 0 -> 1
+	 */
+	int (*mem_request)(void *);
+	/*
+	 * release function for a region when the allocation count goes
+	 * from 1 -> 0
+	 */
+	int (*mem_release)(void *);
+	/*
+	 * private data for the request/release callback
+	 */
+	void *region_data;
+	/*
+	 * map and unmap as needed
+	 */
+	int map_on_demand;
+	/*
+	 * memory will be reused through fmem
+	 */
+	int reusable;
+};
+#define to_pmem_info_id(a) (container_of(a, struct pmem_info, kobj)->id)
+
+static void ioremap_pmem(int id);
+static void pmem_put_region(int id);
+static int pmem_get_region(int id);
+
+static struct pmem_info pmem[PMEM_MAX_DEVICES];
+static int id_count;
+
+#define PMEM_SYSFS_DIR_NAME "pmem_regions" /* under /sys/kernel/ */
+static struct kset *pmem_kset;
+
+#define PMEM_IS_FREE_BUDDY(id, index) \
+	(!(pmem[id].allocator.buddy_bestfit.buddy_bitmap[index].allocated))
+#define PMEM_BUDDY_ORDER(id, index) \
+	(pmem[id].allocator.buddy_bestfit.buddy_bitmap[index].order)
+#define PMEM_BUDDY_INDEX(id, index) \
+	(index ^ (1 << PMEM_BUDDY_ORDER(id, index)))
+#define PMEM_BUDDY_NEXT_INDEX(id, index) \
+	(index + (1 << PMEM_BUDDY_ORDER(id, index)))
+#define PMEM_OFFSET(index) (index * pmem[id].quantum)
+#define PMEM_START_ADDR(id, index) \
+	(PMEM_OFFSET(index) + pmem[id].base)
+#define PMEM_BUDDY_LEN(id, index) \
+	((1 << PMEM_BUDDY_ORDER(id, index)) * pmem[id].quantum)
+#define PMEM_END_ADDR(id, index) \
+	(PMEM_START_ADDR(id, index) + PMEM_LEN(id, index))
+#define PMEM_START_VADDR(id, index) \
+	(PMEM_OFFSET(id, index) + pmem[id].vbase)
+#define PMEM_END_VADDR(id, index) \
+	(PMEM_START_VADDR(id, index) + PMEM_LEN(id, index))
+#define PMEM_REVOKED(data) (data->flags & PMEM_FLAGS_REVOKED)
+#define PMEM_IS_PAGE_ALIGNED(addr) (!((addr) & (~PAGE_MASK)))
+#define PMEM_IS_SUBMAP(data) \
+	((data->flags & PMEM_FLAGS_SUBMAP) && \
+	(!(data->flags & PMEM_FLAGS_UNSUBMAP)))
+
+static int pmem_release(struct inode *, struct file *);
+static int pmem_mmap(struct file *, struct vm_area_struct *);
+static int pmem_open(struct inode *, struct file *);
+static long pmem_ioctl(struct file *, unsigned int, unsigned long);
+
+struct file_operations pmem_fops = {
+	.release = pmem_release,
+	.mmap = pmem_mmap,
+	.open = pmem_open,
+	.unlocked_ioctl = pmem_ioctl,
+};
+
+#define PMEM_ATTR(_name, _mode, _show, _store) {            \
+	.attr = {.name = __stringify(_name), .mode = _mode }, \
+	.show = _show,                                        \
+	.store = _store,                                      \
+}
+
+struct pmem_attr {
+	struct attribute attr;
+	ssize_t(*show) (const int id, char * const);
+	ssize_t(*store) (const int id, const char * const, const size_t count);
+};
+#define to_pmem_attr(a) container_of(a, struct pmem_attr, attr)
+
+#define RW_PMEM_ATTR(name)  \
+static struct pmem_attr pmem_attr_## name = \
+ PMEM_ATTR(name, S_IRUGO | S_IWUSR, show_pmem_## name, store_pmem_## name)
+
+#define RO_PMEM_ATTR(name)  \
+static struct pmem_attr pmem_attr_## name = \
+ PMEM_ATTR(name, S_IRUGO, show_pmem_## name, NULL)
+
+#define WO_PMEM_ATTR(name)  \
+static struct pmem_attr pmem_attr_## name = \
+ PMEM_ATTR(name, S_IWUSR, NULL, store_pmem_## name)
+
+static ssize_t show_pmem(struct kobject *kobj,
+			struct attribute *attr,
+			char *buf)
+{
+	struct pmem_attr *a = to_pmem_attr(attr);
+	return a->show ? a->show(to_pmem_info_id(kobj), buf) : -EIO;
+}
+
+static ssize_t store_pmem(struct kobject *kobj, struct attribute *attr,
+		     const char *buf, size_t count)
+{
+	struct pmem_attr *a = to_pmem_attr(attr);
+	return a->store ? a->store(to_pmem_info_id(kobj), buf, count) : -EIO;
+}
+
+static struct sysfs_ops pmem_ops = {
+	.show = show_pmem,
+	.store = store_pmem,
+};
+
+static ssize_t show_pmem_base(int id, char *buf)
+{
+	return scnprintf(buf, PAGE_SIZE, "%lu(%#lx)\n",
+		pmem[id].base, pmem[id].base);
+}
+RO_PMEM_ATTR(base);
+
+static ssize_t show_pmem_size(int id, char *buf)
+{
+	return scnprintf(buf, PAGE_SIZE, "%lu(%#lx)\n",
+		pmem[id].size, pmem[id].size);
+}
+RO_PMEM_ATTR(size);
+
+static ssize_t show_pmem_allocator_type(int id, char *buf)
+{
+	switch (pmem[id].allocator_type) {
+	case  PMEM_ALLOCATORTYPE_ALLORNOTHING:
+		return scnprintf(buf, PAGE_SIZE, "%s\n", "All or Nothing");
+	case  PMEM_ALLOCATORTYPE_BUDDYBESTFIT:
+		return scnprintf(buf, PAGE_SIZE, "%s\n", "Buddy Bestfit");
+	case  PMEM_ALLOCATORTYPE_BITMAP:
+		return scnprintf(buf, PAGE_SIZE, "%s\n", "Bitmap");
+	case PMEM_ALLOCATORTYPE_SYSTEM:
+		return scnprintf(buf, PAGE_SIZE, "%s\n", "System heap");
+	default:
+		return scnprintf(buf, PAGE_SIZE,
+			"??? Invalid allocator type (%d) for this region! "
+			"Something isn't right.\n",
+			pmem[id].allocator_type);
+	}
+}
+RO_PMEM_ATTR(allocator_type);
+
+static ssize_t show_pmem_mapped_regions(int id, char *buf)
+{
+	struct list_head *elt;
+	int ret;
+
+	ret = scnprintf(buf, PAGE_SIZE,
+		      "pid #: mapped regions (offset, len) (offset,len)...\n");
+
+	mutex_lock(&pmem[id].data_list_mutex);
+	list_for_each(elt, &pmem[id].data_list) {
+		struct pmem_data *data =
+			list_entry(elt, struct pmem_data, list);
+		struct list_head *elt2;
+
+		down_read(&data->sem);
+		ret += scnprintf(buf + ret, PAGE_SIZE - ret, "pid %u:",
+				data->pid);
+		list_for_each(elt2, &data->region_list) {
+			struct pmem_region_node *region_node = list_entry(elt2,
+					struct pmem_region_node,
+					list);
+			ret += scnprintf(buf + ret, PAGE_SIZE - ret,
+					"(%lx,%lx) ",
+					region_node->region.offset,
+					region_node->region.len);
+		}
+		up_read(&data->sem);
+		ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n");
+	}
+	mutex_unlock(&pmem[id].data_list_mutex);
+	return ret;
+}
+RO_PMEM_ATTR(mapped_regions);
+
+#define PMEM_COMMON_SYSFS_ATTRS \
+	&pmem_attr_base.attr, \
+	&pmem_attr_size.attr, \
+	&pmem_attr_allocator_type.attr, \
+	&pmem_attr_mapped_regions.attr
+
+
+static ssize_t show_pmem_allocated(int id, char *buf)
+{
+	ssize_t ret;
+
+	mutex_lock(&pmem[id].arena_mutex);
+	ret = scnprintf(buf, PAGE_SIZE, "%s\n",
+		pmem[id].allocator.all_or_nothing.allocated ?
+		"is allocated" : "is NOT allocated");
+	mutex_unlock(&pmem[id].arena_mutex);
+	return ret;
+}
+RO_PMEM_ATTR(allocated);
+
+static struct attribute *pmem_allornothing_attrs[] = {
+	PMEM_COMMON_SYSFS_ATTRS,
+
+	&pmem_attr_allocated.attr,
+
+	NULL
+};
+
+static struct kobj_type pmem_allornothing_ktype = {
+	.sysfs_ops = &pmem_ops,
+	.default_attrs = pmem_allornothing_attrs,
+};
+
+static ssize_t show_pmem_total_entries(int id, char *buf)
+{
+	return scnprintf(buf, PAGE_SIZE, "%lu\n", pmem[id].num_entries);
+}
+RO_PMEM_ATTR(total_entries);
+
+static ssize_t show_pmem_quantum_size(int id, char *buf)
+{
+	return scnprintf(buf, PAGE_SIZE, "%u (%#x)\n",
+		pmem[id].quantum, pmem[id].quantum);
+}
+RO_PMEM_ATTR(quantum_size);
+
+static ssize_t show_pmem_buddy_bitmap_dump(int id, char *buf)
+{
+	int ret, i;
+
+	mutex_lock(&pmem[id].data_list_mutex);
+	ret = scnprintf(buf, PAGE_SIZE, "index\torder\tlength\tallocated\n");
+
+	for (i = 0; i < pmem[id].num_entries && (PAGE_SIZE - ret);
+			i = PMEM_BUDDY_NEXT_INDEX(id, i))
+		ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%d\t%d\t%d\t%d\n",
+			i, PMEM_BUDDY_ORDER(id, i),
+			PMEM_BUDDY_LEN(id, i),
+			!PMEM_IS_FREE_BUDDY(id, i));
+
+	mutex_unlock(&pmem[id].data_list_mutex);
+	return ret;
+}
+RO_PMEM_ATTR(buddy_bitmap_dump);
+
+#define PMEM_BITMAP_BUDDY_BESTFIT_COMMON_SYSFS_ATTRS \
+	&pmem_attr_quantum_size.attr, \
+	&pmem_attr_total_entries.attr
+
+static struct attribute *pmem_buddy_bestfit_attrs[] = {
+	PMEM_COMMON_SYSFS_ATTRS,
+
+	PMEM_BITMAP_BUDDY_BESTFIT_COMMON_SYSFS_ATTRS,
+
+	&pmem_attr_buddy_bitmap_dump.attr,
+
+	NULL
+};
+
+static struct kobj_type pmem_buddy_bestfit_ktype = {
+	.sysfs_ops = &pmem_ops,
+	.default_attrs = pmem_buddy_bestfit_attrs,
+};
+
+static ssize_t show_pmem_free_quanta(int id, char *buf)
+{
+	ssize_t ret;
+
+	mutex_lock(&pmem[id].arena_mutex);
+	ret = scnprintf(buf, PAGE_SIZE, "%u\n",
+		pmem[id].allocator.bitmap.bitmap_free);
+	mutex_unlock(&pmem[id].arena_mutex);
+	return ret;
+}
+RO_PMEM_ATTR(free_quanta);
+
+static ssize_t show_pmem_bits_allocated(int id, char *buf)
+{
+	ssize_t ret;
+	unsigned int i;
+
+	mutex_lock(&pmem[id].arena_mutex);
+
+	ret = scnprintf(buf, PAGE_SIZE,
+		"id: %d\nbitnum\tindex\tquanta allocated\n", id);
+
+	for (i = 0; i < pmem[id].allocator.bitmap.bitmap_allocs; i++)
+		if (pmem[id].allocator.bitmap.bitm_alloc[i].bit != -1)
+			ret += scnprintf(buf + ret, PAGE_SIZE - ret,
+				"%u\t%u\t%u\n",
+				i,
+				pmem[id].allocator.bitmap.bitm_alloc[i].bit,
+				pmem[id].allocator.bitmap.bitm_alloc[i].quanta
+				);
+
+	mutex_unlock(&pmem[id].arena_mutex);
+	return ret;
+}
+RO_PMEM_ATTR(bits_allocated);
+
+static struct attribute *pmem_bitmap_attrs[] = {
+	PMEM_COMMON_SYSFS_ATTRS,
+
+	PMEM_BITMAP_BUDDY_BESTFIT_COMMON_SYSFS_ATTRS,
+
+	&pmem_attr_free_quanta.attr,
+	&pmem_attr_bits_allocated.attr,
+
+	NULL
+};
+
+static struct attribute *pmem_system_attrs[] = {
+	PMEM_COMMON_SYSFS_ATTRS,
+
+	NULL
+};
+
+static struct kobj_type pmem_bitmap_ktype = {
+	.sysfs_ops = &pmem_ops,
+	.default_attrs = pmem_bitmap_attrs,
+};
+
+static struct kobj_type pmem_system_ktype = {
+	.sysfs_ops = &pmem_ops,
+	.default_attrs = pmem_system_attrs,
+};
+
+static int pmem_allocate_from_id(const int id, const unsigned long size,
+						const unsigned int align)
+{
+	int ret;
+	ret = pmem_get_region(id);
+
+	if (ret)
+		return -1;
+
+	ret = pmem[id].allocate(id, size, align);
+
+	if (ret < 0)
+		pmem_put_region(id);
+
+	return ret;
+}
+
+static int pmem_free_from_id(const int id, const int index)
+{
+	pmem_put_region(id);
+	return pmem[id].free(id, index);
+}
+
+static int pmem_get_region(int id)
+{
+	/* Must be called with arena mutex locked */
+	atomic_inc(&pmem[id].allocation_cnt);
+	if (!pmem[id].vbase) {
+		DLOG("PMEMDEBUG: mapping for %s", pmem[id].name);
+		if (pmem[id].mem_request) {
+			int ret = pmem[id].mem_request(pmem[id].region_data);
+			if (ret) {
+				atomic_dec(&pmem[id].allocation_cnt);
+				return 1;
+			}
+		}
+		ioremap_pmem(id);
+	}
+
+	if (pmem[id].vbase) {
+		return 0;
+	} else {
+		if (pmem[id].mem_release)
+			pmem[id].mem_release(pmem[id].region_data);
+		atomic_dec(&pmem[id].allocation_cnt);
+		return 1;
+	}
+}
+
+static void pmem_put_region(int id)
+{
+	/* Must be called with arena mutex locked */
+	if (atomic_dec_and_test(&pmem[id].allocation_cnt)) {
+		DLOG("PMEMDEBUG: unmapping for %s", pmem[id].name);
+		BUG_ON(!pmem[id].vbase);
+		if (pmem[id].map_on_demand) {
+			/* unmap_kernel_range() flushes the caches
+			 * and removes the page table entries
+			 */
+			unmap_kernel_range((unsigned long)pmem[id].vbase,
+				 pmem[id].size);
+			pmem[id].vbase = NULL;
+			if (pmem[id].mem_release) {
+				int ret = pmem[id].mem_release(
+						pmem[id].region_data);
+				WARN(ret, "mem_release failed");
+			}
+
+		}
+	}
+}
+
+static int get_id(struct file *file)
+{
+	return MINOR(file->f_dentry->d_inode->i_rdev);
+}
+
+static char *get_name(struct file *file)
+{
+	int id = get_id(file);
+	return pmem[id].name;
+}
+
+static int is_pmem_file(struct file *file)
+{
+	int id;
+
+	if (unlikely(!file || !file->f_dentry || !file->f_dentry->d_inode))
+		return 0;
+
+	id = get_id(file);
+	return (unlikely(id >= PMEM_MAX_DEVICES ||
+		file->f_dentry->d_inode->i_rdev !=
+		     MKDEV(MISC_MAJOR, pmem[id].dev.minor))) ? 0 : 1;
+}
+
+static int has_allocation(struct file *file)
+{
+	/* must be called with at least read lock held on
+	 * ((struct pmem_data *)(file->private_data))->sem which
+	 * means that file is guaranteed not to be NULL upon entry!!
+	 * check is_pmem_file first if not accessed via pmem_file_ops */
+	struct pmem_data *pdata = file->private_data;
+	return pdata && pdata->index != -1;
+}
+
+static int is_master_owner(struct file *file)
+{
+	struct file *master_file;
+	struct pmem_data *data = file->private_data;
+	int put_needed, ret = 0;
+
+	if (!has_allocation(file))
+		return 0;
+	if (PMEM_FLAGS_MASTERMAP & data->flags)
+		return 1;
+	master_file = fget_light(data->master_fd, &put_needed);
+	if (master_file && data->master_file == master_file)
+		ret = 1;
+	if (master_file)
+		fput_light(master_file, put_needed);
+	return ret;
+}
+
+static int pmem_free_all_or_nothing(int id, int index)
+{
+	/* caller should hold the lock on arena_mutex! */
+	DLOG("index %d\n", index);
+
+	pmem[id].allocator.all_or_nothing.allocated =  0;
+	return 0;
+}
+
+static int pmem_free_space_all_or_nothing(int id,
+		struct pmem_freespace *fs)
+{
+	/* caller should hold the lock on arena_mutex! */
+	fs->total = (unsigned long)
+		pmem[id].allocator.all_or_nothing.allocated == 0 ?
+		pmem[id].size : 0;
+
+	fs->largest = fs->total;
+	return 0;
+}
+
+
+static int pmem_free_buddy_bestfit(int id, int index)
+{
+	/* caller should hold the lock on arena_mutex! */
+	int curr = index;
+	DLOG("index %d\n", index);
+
+
+	/* clean up the bitmap, merging any buddies */
+	pmem[id].allocator.buddy_bestfit.buddy_bitmap[curr].allocated = 0;
+	/* find a slots buddy Buddy# = Slot# ^ (1 << order)
+	 * if the buddy is also free merge them
+	 * repeat until the buddy is not free or end of the bitmap is reached
+	 */
+	do {
+		int buddy = PMEM_BUDDY_INDEX(id, curr);
+		if (buddy < pmem[id].num_entries &&
+		    PMEM_IS_FREE_BUDDY(id, buddy) &&
+		    PMEM_BUDDY_ORDER(id, buddy) ==
+				PMEM_BUDDY_ORDER(id, curr)) {
+			PMEM_BUDDY_ORDER(id, buddy)++;
+			PMEM_BUDDY_ORDER(id, curr)++;
+			curr = min(buddy, curr);
+		} else {
+			break;
+		}
+	} while (curr < pmem[id].num_entries);
+
+	return 0;
+}
+
+
+static int pmem_free_space_buddy_bestfit(int id,
+		struct pmem_freespace *fs)
+{
+	/* caller should hold the lock on arena_mutex! */
+	int curr;
+	unsigned long size;
+	fs->total = 0;
+	fs->largest = 0;
+
+	for (curr = 0; curr < pmem[id].num_entries;
+	     curr = PMEM_BUDDY_NEXT_INDEX(id, curr)) {
+		if (PMEM_IS_FREE_BUDDY(id, curr)) {
+			size = PMEM_BUDDY_LEN(id, curr);
+			if (size > fs->largest)
+				fs->largest = size;
+			fs->total += size;
+		}
+	}
+	return 0;
+}
+
+
+static inline uint32_t start_mask(int bit_start)
+{
+	return (uint32_t)(~0) << (bit_start & PMEM_BITS_PER_WORD_MASK);
+}
+
+static inline uint32_t end_mask(int bit_end)
+{
+	return (uint32_t)(~0) >>
+		((BITS_PER_LONG - bit_end) & PMEM_BITS_PER_WORD_MASK);
+}
+
+static inline int compute_total_words(int bit_end, int word_index)
+{
+	return ((bit_end + BITS_PER_LONG - 1) >>
+			PMEM_32BIT_WORD_ORDER) - word_index;
+}
+
+static void bitmap_bits_clear_all(uint32_t *bitp, int bit_start, int bit_end)
+{
+	int word_index = bit_start >> PMEM_32BIT_WORD_ORDER, total_words;
+
+	total_words = compute_total_words(bit_end, word_index);
+	if (total_words > 0) {
+		if (total_words == 1) {
+			bitp[word_index] &=
+				~(start_mask(bit_start) & end_mask(bit_end));
+		} else {
+			bitp[word_index++] &= ~start_mask(bit_start);
+			if (total_words > 2) {
+				int total_bytes;
+
+				total_words -= 2;
+				total_bytes = total_words << 2;
+
+				memset(&bitp[word_index], 0, total_bytes);
+				word_index += total_words;
+			}
+			bitp[word_index] &= ~end_mask(bit_end);
+		}
+	}
+}
+
+static int pmem_free_bitmap(int id, int bitnum)
+{
+	/* caller should hold the lock on arena_mutex! */
+	int i;
+	char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1];
+
+	DLOG("bitnum %d\n", bitnum);
+
+	for (i = 0; i < pmem[id].allocator.bitmap.bitmap_allocs; i++) {
+		const int curr_bit =
+			pmem[id].allocator.bitmap.bitm_alloc[i].bit;
+
+		if (curr_bit == bitnum) {
+			const int curr_quanta =
+				pmem[id].allocator.bitmap.bitm_alloc[i].quanta;
+
+			bitmap_bits_clear_all(pmem[id].allocator.bitmap.bitmap,
+				curr_bit, curr_bit + curr_quanta);
+			pmem[id].allocator.bitmap.bitmap_free += curr_quanta;
+			pmem[id].allocator.bitmap.bitm_alloc[i].bit = -1;
+			pmem[id].allocator.bitmap.bitm_alloc[i].quanta = 0;
+			return 0;
+		}
+	}
+	printk(KERN_ALERT "pmem: %s: Attempt to free unallocated index %d, id"
+		" %d, pid %d(%s)\n", __func__, bitnum, id,  current->pid,
+		get_task_comm(currtask_name, current));
+
+	return -1;
+}
+
+static int pmem_free_system(int id, int index)
+{
+	/* caller should hold the lock on arena_mutex! */
+	struct alloc_list *item;
+
+	DLOG("index %d\n", index);
+	if (index != 0)
+		item = (struct alloc_list *)index;
+	else
+		return 0;
+
+	if (item->vaddr != NULL) {
+		iounmap(item->vaddr);
+		kfree(__va(item->addr));
+		list_del(&item->allocs);
+		kfree(item);
+	}
+
+	return 0;
+}
+
+static int pmem_free_space_bitmap(int id, struct pmem_freespace *fs)
+{
+	int i, j;
+	int max_allocs = pmem[id].allocator.bitmap.bitmap_allocs;
+	int alloc_start = 0;
+	int next_alloc;
+	unsigned long size = 0;
+
+	fs->total = 0;
+	fs->largest = 0;
+
+	for (i = 0; i < max_allocs; i++) {
+
+		int alloc_quanta = 0;
+		int alloc_idx = 0;
+		next_alloc = pmem[id].num_entries;
+
+		/* Look for the lowest bit where next allocation starts */
+		for (j = 0; j < max_allocs; j++) {
+			const int curr_alloc = pmem[id].allocator.
+						bitmap.bitm_alloc[j].bit;
+			if (curr_alloc != -1) {
+				if (alloc_start == curr_alloc)
+					alloc_idx = j;
+				if (alloc_start >= curr_alloc)
+					continue;
+				if (curr_alloc < next_alloc)
+					next_alloc = curr_alloc;
+			}
+		}
+		alloc_quanta = pmem[id].allocator.bitmap.
+				bitm_alloc[alloc_idx].quanta;
+		size = (next_alloc - (alloc_start + alloc_quanta)) *
+				pmem[id].quantum;
+
+		if (size > fs->largest)
+			fs->largest = size;
+		fs->total += size;
+
+		if (next_alloc == pmem[id].num_entries)
+			break;
+		else
+			alloc_start = next_alloc;
+	}
+
+	return 0;
+}
+
+static int pmem_free_space_system(int id, struct pmem_freespace *fs)
+{
+	fs->total = pmem[id].size;
+	fs->largest = pmem[id].size;
+
+	return 0;
+}
+
+static void pmem_revoke(struct file *file, struct pmem_data *data);
+
+static int pmem_release(struct inode *inode, struct file *file)
+{
+	struct pmem_data *data = file->private_data;
+	struct pmem_region_node *region_node;
+	struct list_head *elt, *elt2;
+	int id = get_id(file), ret = 0;
+
+#if PMEM_DEBUG_MSGS
+	char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1];
+#endif
+	DLOG("releasing memory pid %u(%s) file %p(%ld) dev %s(id: %d)\n",
+		current->pid, get_task_comm(currtask_name, current),
+		file, file_count(file), get_name(file), id);
+	mutex_lock(&pmem[id].data_list_mutex);
+	/* if this file is a master, revoke all the memory in the connected
+	 *  files */
+	if (PMEM_FLAGS_MASTERMAP & data->flags) {
+		list_for_each(elt, &pmem[id].data_list) {
+			struct pmem_data *sub_data =
+				list_entry(elt, struct pmem_data, list);
+			int is_master;
+
+			down_read(&sub_data->sem);
+			is_master = (PMEM_IS_SUBMAP(sub_data) &&
+				file == sub_data->master_file);
+			up_read(&sub_data->sem);
+
+			if (is_master)
+				pmem_revoke(file, sub_data);
+		}
+	}
+	list_del(&data->list);
+	mutex_unlock(&pmem[id].data_list_mutex);
+
+	down_write(&data->sem);
+
+	/* if it is not a connected file and it has an allocation, free it */
+	if (!(PMEM_FLAGS_CONNECTED & data->flags) && has_allocation(file)) {
+		mutex_lock(&pmem[id].arena_mutex);
+		ret = pmem_free_from_id(id, data->index);
+		mutex_unlock(&pmem[id].arena_mutex);
+	}
+
+	/* if this file is a submap (mapped, connected file), downref the
+	 * task struct */
+	if (PMEM_FLAGS_SUBMAP & data->flags)
+		if (data->task) {
+			put_task_struct(data->task);
+			data->task = NULL;
+		}
+
+	file->private_data = NULL;
+
+	list_for_each_safe(elt, elt2, &data->region_list) {
+		region_node = list_entry(elt, struct pmem_region_node, list);
+		list_del(elt);
+		kfree(region_node);
+	}
+	BUG_ON(!list_empty(&data->region_list));
+
+	up_write(&data->sem);
+	kfree(data);
+	if (pmem[id].release)
+		ret = pmem[id].release(inode, file);
+
+	return ret;
+}
+
+static int pmem_open(struct inode *inode, struct file *file)
+{
+	struct pmem_data *data;
+	int id = get_id(file);
+	int ret = 0;
+#if PMEM_DEBUG_MSGS
+	char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1];
+#endif
+
+	DLOG("pid %u(%s) file %p(%ld) dev %s(id: %d)\n",
+		current->pid, get_task_comm(currtask_name, current),
+		file, file_count(file), get_name(file), id);
+	data = kmalloc(sizeof(struct pmem_data), GFP_KERNEL);
+	if (!data) {
+		printk(KERN_ALERT "pmem: %s: unable to allocate memory for "
+				"pmem metadata.", __func__);
+		return -1;
+	}
+	data->flags = 0;
+	data->index = -1;
+	data->task = NULL;
+	data->vma = NULL;
+	data->pid = 0;
+	data->master_file = NULL;
+#if PMEM_DEBUG
+	data->ref = 0;
+#endif
+	INIT_LIST_HEAD(&data->region_list);
+	init_rwsem(&data->sem);
+
+	file->private_data = data;
+	INIT_LIST_HEAD(&data->list);
+
+	mutex_lock(&pmem[id].data_list_mutex);
+	list_add(&data->list, &pmem[id].data_list);
+	mutex_unlock(&pmem[id].data_list_mutex);
+	return ret;
+}
+
+static unsigned long pmem_order(unsigned long len, int id)
+{
+	int i;
+
+	len = (len + pmem[id].quantum - 1)/pmem[id].quantum;
+	len--;
+	for (i = 0; i < sizeof(len)*8; i++)
+		if (len >> i == 0)
+			break;
+	return i;
+}
+
+static int pmem_allocator_all_or_nothing(const int id,
+		const unsigned long len,
+		const unsigned int align)
+{
+	/* caller should hold the lock on arena_mutex! */
+	DLOG("all or nothing\n");
+	if ((len > pmem[id].size) ||
+		pmem[id].allocator.all_or_nothing.allocated)
+		return -1;
+	pmem[id].allocator.all_or_nothing.allocated = 1;
+	return len;
+}
+
+static int pmem_allocator_buddy_bestfit(const int id,
+		const unsigned long len,
+		unsigned int align)
+{
+	/* caller should hold the lock on arena_mutex! */
+	int curr;
+	int best_fit = -1;
+	unsigned long order;
+
+	DLOG("buddy bestfit\n");
+	order = pmem_order(len, id);
+	if (order > PMEM_MAX_ORDER)
+		goto out;
+
+	DLOG("order %lx\n", order);
+
+	/* Look through the bitmap.
+	 * 	If a free slot of the correct order is found, use it.
+	 * 	Otherwise, use the best fit (smallest with size > order) slot.
+	 */
+	for (curr = 0;
+	     curr < pmem[id].num_entries;
+	     curr = PMEM_BUDDY_NEXT_INDEX(id, curr))
+		if (PMEM_IS_FREE_BUDDY(id, curr)) {
+			if (PMEM_BUDDY_ORDER(id, curr) ==
+					(unsigned char)order) {
+				/* set the not free bit and clear others */
+				best_fit = curr;
+				break;
+			}
+			if (PMEM_BUDDY_ORDER(id, curr) >
+					(unsigned char)order &&
+			    (best_fit < 0 ||
+			     PMEM_BUDDY_ORDER(id, curr) <
+					PMEM_BUDDY_ORDER(id, best_fit)))
+				best_fit = curr;
+		}
+
+	/* if best_fit < 0, there are no suitable slots; return an error */
+	if (best_fit < 0) {
+#if PMEM_DEBUG
+		printk(KERN_ALERT "pmem: %s: no space left to allocate!\n",
+			__func__);
+#endif
+		goto out;
+	}
+
+	/* now partition the best fit:
+	 * 	split the slot into 2 buddies of order - 1
+	 * 	repeat until the slot is of the correct order
+	 */
+	while (PMEM_BUDDY_ORDER(id, best_fit) > (unsigned char)order) {
+		int buddy;
+		PMEM_BUDDY_ORDER(id, best_fit) -= 1;
+		buddy = PMEM_BUDDY_INDEX(id, best_fit);
+		PMEM_BUDDY_ORDER(id, buddy) = PMEM_BUDDY_ORDER(id, best_fit);
+	}
+	pmem[id].allocator.buddy_bestfit.buddy_bitmap[best_fit].allocated = 1;
+out:
+	return best_fit;
+}
+
+
+static inline unsigned long paddr_from_bit(const int id, const int bitnum)
+{
+	return pmem[id].base + pmem[id].quantum * bitnum;
+}
+
+static inline unsigned long bit_from_paddr(const int id,
+		const unsigned long paddr)
+{
+	return (paddr - pmem[id].base) / pmem[id].quantum;
+}
+
+static void bitmap_bits_set_all(uint32_t *bitp, int bit_start, int bit_end)
+{
+	int word_index = bit_start >> PMEM_32BIT_WORD_ORDER, total_words;
+
+	total_words = compute_total_words(bit_end, word_index);
+	if (total_words > 0) {
+		if (total_words == 1) {
+			bitp[word_index] |=
+				(start_mask(bit_start) & end_mask(bit_end));
+		} else {
+			bitp[word_index++] |= start_mask(bit_start);
+			if (total_words > 2) {
+				int total_bytes;
+
+				total_words -= 2;
+				total_bytes = total_words << 2;
+
+				memset(&bitp[word_index], ~0, total_bytes);
+				word_index += total_words;
+			}
+			bitp[word_index] |= end_mask(bit_end);
+		}
+	}
+}
+
+static int
+bitmap_allocate_contiguous(uint32_t *bitp, int num_bits_to_alloc,
+		int total_bits, int spacing, int start_bit)
+{
+	int bit_start, last_bit, word_index;
+
+	if (num_bits_to_alloc <= 0)
+		return -1;
+
+	for (bit_start = start_bit; ;
+		bit_start = ((last_bit +
+			(word_index << PMEM_32BIT_WORD_ORDER) + spacing - 1)
+			& ~(spacing - 1)) + start_bit) {
+		int bit_end = bit_start + num_bits_to_alloc, total_words;
+
+		if (bit_end > total_bits)
+			return -1; /* out of contiguous memory */
+
+		word_index = bit_start >> PMEM_32BIT_WORD_ORDER;
+		total_words = compute_total_words(bit_end, word_index);
+
+		if (total_words <= 0)
+			return -1;
+
+		if (total_words == 1) {
+			last_bit = fls(bitp[word_index] &
+					(start_mask(bit_start) &
+						end_mask(bit_end)));
+			if (last_bit)
+				continue;
+		} else {
+			int end_word = word_index + (total_words - 1);
+			last_bit =
+				fls(bitp[word_index] & start_mask(bit_start));
+			if (last_bit)
+				continue;
+
+			for (word_index++;
+					word_index < end_word;
+					word_index++) {
+				last_bit = fls(bitp[word_index]);
+				if (last_bit)
+					break;
+			}
+			if (last_bit)
+				continue;
+
+			last_bit = fls(bitp[word_index] & end_mask(bit_end));
+			if (last_bit)
+				continue;
+		}
+		bitmap_bits_set_all(bitp, bit_start, bit_end);
+		return bit_start;
+	}
+	return -1;
+}
+
+static int reserve_quanta(const unsigned int quanta_needed,
+		const int id,
+		unsigned int align)
+{
+	/* alignment should be a valid power of 2 */
+	int ret = -1, start_bit = 0, spacing = 1;
+
+	/* Sanity check */
+	if (quanta_needed > pmem[id].allocator.bitmap.bitmap_free) {
+#if PMEM_DEBUG
+		printk(KERN_ALERT "pmem: %s: request (%d) too big for"
+			" available free (%d)\n", __func__, quanta_needed,
+			pmem[id].allocator.bitmap.bitmap_free);
+#endif
+		return -1;
+	}
+
+	start_bit = bit_from_paddr(id,
+		(pmem[id].base + align - 1) & ~(align - 1));
+	if (start_bit <= -1) {
+#if PMEM_DEBUG
+		printk(KERN_ALERT
+			"pmem: %s: bit_from_paddr fails for"
+			" %u alignment.\n", __func__, align);
+#endif
+		return -1;
+	}
+	spacing = align / pmem[id].quantum;
+	spacing = spacing > 1 ? spacing : 1;
+
+	ret = bitmap_allocate_contiguous(pmem[id].allocator.bitmap.bitmap,
+		quanta_needed,
+		(pmem[id].size + pmem[id].quantum - 1) / pmem[id].quantum,
+		spacing,
+		start_bit);
+
+#if PMEM_DEBUG
+	if (ret < 0)
+		printk(KERN_ALERT "pmem: %s: not enough contiguous bits free "
+			"in bitmap! Region memory is either too fragmented or"
+			" request is too large for available memory.\n",
+			__func__);
+#endif
+
+	return ret;
+}
+
+static int pmem_allocator_bitmap(const int id,
+		const unsigned long len,
+		const unsigned int align)
+{
+	/* caller should hold the lock on arena_mutex! */
+	int bitnum, i;
+	unsigned int quanta_needed;
+
+	DLOG("bitmap id %d, len %ld, align %u\n", id, len, align);
+	if (!pmem[id].allocator.bitmap.bitm_alloc) {
+#if PMEM_DEBUG
+		printk(KERN_ALERT "pmem: bitm_alloc not present! id: %d\n",
+			id);
+#endif
+		return -1;
+	}
+
+	quanta_needed = (len + pmem[id].quantum - 1) / pmem[id].quantum;
+	DLOG("quantum size %u quanta needed %u free %u id %d\n",
+		pmem[id].quantum, quanta_needed,
+		pmem[id].allocator.bitmap.bitmap_free, id);
+
+	if (pmem[id].allocator.bitmap.bitmap_free < quanta_needed) {
+#if PMEM_DEBUG
+		printk(KERN_ALERT "pmem: memory allocation failure. "
+			"PMEM memory region exhausted, id %d."
+			" Unable to comply with allocation request.\n", id);
+#endif
+		return -1;
+	}
+
+	bitnum = reserve_quanta(quanta_needed, id, align);
+	if (bitnum == -1)
+		goto leave;
+
+	for (i = 0;
+		i < pmem[id].allocator.bitmap.bitmap_allocs &&
+			pmem[id].allocator.bitmap.bitm_alloc[i].bit != -1;
+		i++)
+		;
+
+	if (i >= pmem[id].allocator.bitmap.bitmap_allocs) {
+		void *temp;
+		int32_t new_bitmap_allocs =
+			pmem[id].allocator.bitmap.bitmap_allocs << 1;
+		int j;
+
+		if (!new_bitmap_allocs) { /* failed sanity check!! */
+#if PMEM_DEBUG
+			pr_alert("pmem: bitmap_allocs number"
+				" wrapped around to zero! Something "
+				"is VERY wrong.\n");
+#endif
+			return -1;
+		}
+
+		if (new_bitmap_allocs > pmem[id].num_entries) {
+			/* failed sanity check!! */
+#if PMEM_DEBUG
+			pr_alert("pmem: required bitmap_allocs"
+				" number exceeds maximum entries possible"
+				" for current quanta\n");
+#endif
+			return -1;
+		}
+
+		temp = krealloc(pmem[id].allocator.bitmap.bitm_alloc,
+				new_bitmap_allocs *
+				sizeof(*pmem[id].allocator.bitmap.bitm_alloc),
+				GFP_KERNEL);
+		if (!temp) {
+#if PMEM_DEBUG
+			pr_alert("pmem: can't realloc bitmap_allocs,"
+				"id %d, current num bitmap allocs %d\n",
+				id, pmem[id].allocator.bitmap.bitmap_allocs);
+#endif
+			return -1;
+		}
+		pmem[id].allocator.bitmap.bitmap_allocs = new_bitmap_allocs;
+		pmem[id].allocator.bitmap.bitm_alloc = temp;
+
+		for (j = i; j < new_bitmap_allocs; j++) {
+			pmem[id].allocator.bitmap.bitm_alloc[j].bit = -1;
+			pmem[id].allocator.bitmap.bitm_alloc[i].quanta = 0;
+		}
+
+		DLOG("increased # of allocated regions to %d for id %d\n",
+			pmem[id].allocator.bitmap.bitmap_allocs, id);
+	}
+
+	DLOG("bitnum %d, bitm_alloc index %d\n", bitnum, i);
+
+	pmem[id].allocator.bitmap.bitmap_free -= quanta_needed;
+	pmem[id].allocator.bitmap.bitm_alloc[i].bit = bitnum;
+	pmem[id].allocator.bitmap.bitm_alloc[i].quanta = quanta_needed;
+leave:
+	return bitnum;
+}
+
+static int pmem_allocator_system(const int id,
+		const unsigned long len,
+		const unsigned int align)
+{
+	/* caller should hold the lock on arena_mutex! */
+	struct alloc_list *list;
+	unsigned long aligned_len;
+	int count = SYSTEM_ALLOC_RETRY;
+	void *buf;
+
+	DLOG("system id %d, len %ld, align %u\n", id, len, align);
+
+	if ((pmem[id].allocator.system_mem.used + len) > pmem[id].size) {
+		DLOG("requested size would be larger than quota\n");
+		return -1;
+	}
+
+	/* Handle alignment */
+	aligned_len = len + align;
+
+	/* Attempt allocation */
+	list = kmalloc(sizeof(struct alloc_list), GFP_KERNEL);
+	if (list == NULL) {
+		printk(KERN_ERR "pmem: failed to allocate system metadata\n");
+		return -1;
+	}
+	list->vaddr = NULL;
+
+	buf = NULL;
+	while ((buf == NULL) && count--) {
+		buf = kmalloc((aligned_len), GFP_KERNEL);
+		if (buf == NULL) {
+			DLOG("pmem: kmalloc %d temporarily failed len= %ld\n",
+				count, aligned_len);
+		}
+	}
+	if (!buf) {
+		printk(KERN_CRIT "pmem: kmalloc failed for id= %d len= %ld\n",
+			id, aligned_len);
+		kfree(list);
+		return -1;
+	}
+	list->size = aligned_len;
+	list->addr = (void *)__pa(buf);
+	list->aaddr = (void *)(((unsigned int)(list->addr) + (align - 1)) &
+			~(align - 1));
+
+	if (!pmem[id].cached)
+		list->vaddr = ioremap(__pa(buf), aligned_len);
+	else
+		list->vaddr = ioremap_cached(__pa(buf), aligned_len);
+
+	INIT_LIST_HEAD(&list->allocs);
+	list_add(&list->allocs, &pmem[id].allocator.system_mem.alist);
+
+	return (int)list;
+}
+
+static pgprot_t pmem_phys_mem_access_prot(struct file *file, pgprot_t vma_prot)
+{
+	int id = get_id(file);
+#ifdef pgprot_writecombine
+	if (pmem[id].cached == 0 || file->f_flags & O_SYNC)
+		/* on ARMv6 and ARMv7 this expands to Normal Noncached */
+		return pgprot_writecombine(vma_prot);
+#endif
+#ifdef pgprot_ext_buffered
+	else if (pmem[id].buffered)
+		return pgprot_ext_buffered(vma_prot);
+#endif
+	return vma_prot;
+}
+
+static unsigned long pmem_start_addr_all_or_nothing(int id,
+		struct pmem_data *data)
+{
+	return PMEM_START_ADDR(id, 0);
+}
+
+static unsigned long pmem_start_addr_buddy_bestfit(int id,
+		struct pmem_data *data)
+{
+	return PMEM_START_ADDR(id, data->index);
+}
+
+static unsigned long pmem_start_addr_bitmap(int id, struct pmem_data *data)
+{
+	return data->index * pmem[id].quantum + pmem[id].base;
+}
+
+static unsigned long pmem_start_addr_system(int id, struct pmem_data *data)
+{
+	return (unsigned long)(((struct alloc_list *)(data->index))->aaddr);
+}
+
+static void *pmem_start_vaddr(int id, struct pmem_data *data)
+{
+	if (pmem[id].allocator_type == PMEM_ALLOCATORTYPE_SYSTEM)
+		return ((struct alloc_list *)(data->index))->vaddr;
+	else
+	return pmem[id].start_addr(id, data) - pmem[id].base + pmem[id].vbase;
+}
+
+static unsigned long pmem_len_all_or_nothing(int id, struct pmem_data *data)
+{
+	return data->index;
+}
+
+static unsigned long pmem_len_buddy_bestfit(int id, struct pmem_data *data)
+{
+	return PMEM_BUDDY_LEN(id, data->index);
+}
+
+static unsigned long pmem_len_bitmap(int id, struct pmem_data *data)
+{
+	int i;
+	unsigned long ret = 0;
+
+	mutex_lock(&pmem[id].arena_mutex);
+
+	for (i = 0; i < pmem[id].allocator.bitmap.bitmap_allocs; i++)
+		if (pmem[id].allocator.bitmap.bitm_alloc[i].bit ==
+				data->index) {
+			ret = pmem[id].allocator.bitmap.bitm_alloc[i].quanta *
+				pmem[id].quantum;
+			break;
+		}
+
+	mutex_unlock(&pmem[id].arena_mutex);
+#if PMEM_DEBUG
+	if (i >= pmem[id].allocator.bitmap.bitmap_allocs)
+		pr_alert("pmem: %s: can't find bitnum %d in "
+			"alloc'd array!\n", __func__, data->index);
+#endif
+	return ret;
+}
+
+static unsigned long pmem_len_system(int id, struct pmem_data *data)
+{
+	unsigned long ret = 0;
+
+	mutex_lock(&pmem[id].arena_mutex);
+
+	ret = ((struct alloc_list *)data->index)->size;
+	mutex_unlock(&pmem[id].arena_mutex);
+
+	return ret;
+}
+
+static int pmem_map_garbage(int id, struct vm_area_struct *vma,
+			    struct pmem_data *data, unsigned long offset,
+			    unsigned long len)
+{
+	int i, garbage_pages = len >> PAGE_SHIFT;
+
+	vma->vm_flags |= VM_IO | VM_RESERVED | VM_PFNMAP | VM_SHARED | VM_WRITE;
+	for (i = 0; i < garbage_pages; i++) {
+		if (vm_insert_pfn(vma, vma->vm_start + offset + (i * PAGE_SIZE),
+		    pmem[id].garbage_pfn))
+			return -EAGAIN;
+	}
+	return 0;
+}
+
+static int pmem_unmap_pfn_range(int id, struct vm_area_struct *vma,
+				struct pmem_data *data, unsigned long offset,
+				unsigned long len)
+{
+	int garbage_pages;
+	DLOG("unmap offset %lx len %lx\n", offset, len);
+
+	BUG_ON(!PMEM_IS_PAGE_ALIGNED(len));
+
+	garbage_pages = len >> PAGE_SHIFT;
+	zap_page_range(vma, vma->vm_start + offset, len, NULL);
+	pmem_map_garbage(id, vma, data, offset, len);
+	return 0;
+}
+
+static int pmem_map_pfn_range(int id, struct vm_area_struct *vma,
+			      struct pmem_data *data, unsigned long offset,
+			      unsigned long len)
+{
+	int ret;
+	DLOG("map offset %lx len %lx\n", offset, len);
+	BUG_ON(!PMEM_IS_PAGE_ALIGNED(vma->vm_start));
+	BUG_ON(!PMEM_IS_PAGE_ALIGNED(vma->vm_end));
+	BUG_ON(!PMEM_IS_PAGE_ALIGNED(len));
+	BUG_ON(!PMEM_IS_PAGE_ALIGNED(offset));
+
+	ret = io_remap_pfn_range(vma, vma->vm_start + offset,
+		(pmem[id].start_addr(id, data) + offset) >> PAGE_SHIFT,
+		len, vma->vm_page_prot);
+	if (ret) {
+#if PMEM_DEBUG
+		pr_alert("pmem: %s: io_remap_pfn_range fails with "
+			"return value: %d!\n",	__func__, ret);
+#endif
+
+		ret = -EAGAIN;
+	}
+	return ret;
+}
+
+static int pmem_remap_pfn_range(int id, struct vm_area_struct *vma,
+			      struct pmem_data *data, unsigned long offset,
+			      unsigned long len)
+{
+	/* hold the mm semp for the vma you are modifying when you call this */
+	BUG_ON(!vma);
+	zap_page_range(vma, vma->vm_start + offset, len, NULL);
+	return pmem_map_pfn_range(id, vma, data, offset, len);
+}
+
+static void pmem_vma_open(struct vm_area_struct *vma)
+{
+	struct file *file = vma->vm_file;
+	struct pmem_data *data = file->private_data;
+	int id = get_id(file);
+
+#if PMEM_DEBUG_MSGS
+	char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1];
+#endif
+	DLOG("Dev %s(id: %d) pid %u(%s) ppid %u file %p count %ld\n",
+		get_name(file), id, current->pid,
+		get_task_comm(currtask_name, current),
+		current->parent->pid, file, file_count(file));
+	/* this should never be called as we don't support copying pmem
+	 * ranges via fork */
+	down_read(&data->sem);
+	BUG_ON(!has_allocation(file));
+	/* remap the garbage pages, forkers don't get access to the data */
+	pmem_unmap_pfn_range(id, vma, data, 0, vma->vm_start - vma->vm_end);
+	up_read(&data->sem);
+}
+
+static void pmem_vma_close(struct vm_area_struct *vma)
+{
+	struct file *file = vma->vm_file;
+	struct pmem_data *data = file->private_data;
+
+#if PMEM_DEBUG_MSGS
+	char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1];
+#endif
+	DLOG("Dev %s(id: %d) pid %u(%s) ppid %u file %p count %ld\n",
+		get_name(file), get_id(file), current->pid,
+		get_task_comm(currtask_name, current),
+		current->parent->pid, file, file_count(file));
+
+	if (unlikely(!is_pmem_file(file))) {
+		pr_warning("pmem: something is very wrong, you are "
+		       "closing a vm backing an allocation that doesn't "
+		       "exist!\n");
+		return;
+	}
+
+	down_write(&data->sem);
+	if (unlikely(!has_allocation(file))) {
+		up_write(&data->sem);
+		pr_warning("pmem: something is very wrong, you are "
+		       "closing a vm backing an allocation that doesn't "
+		       "exist!\n");
+		return;
+	}
+	if (data->vma == vma) {
+		data->vma = NULL;
+		if ((data->flags & PMEM_FLAGS_CONNECTED) &&
+		    (data->flags & PMEM_FLAGS_SUBMAP))
+			data->flags |= PMEM_FLAGS_UNSUBMAP;
+	}
+	/* the kernel is going to free this vma now anyway */
+	up_write(&data->sem);
+}
+
+static struct vm_operations_struct vm_ops = {
+	.open = pmem_vma_open,
+	.close = pmem_vma_close,
+};
+
+static int pmem_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct pmem_data *data = file->private_data;
+	int index = -1;
+	unsigned long vma_size =  vma->vm_end - vma->vm_start;
+	int ret = 0, id = get_id(file);
+#if PMEM_DEBUG_MSGS
+	char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1];
+#endif
+
+	if (!data) {
+		pr_err("pmem: Invalid file descriptor, no private data\n");
+		return -EINVAL;
+	}
+	DLOG("pid %u(%s) mmap vma_size %lu on dev %s(id: %d)\n", current->pid,
+		get_task_comm(currtask_name, current), vma_size,
+		get_name(file), id);
+	if (vma->vm_pgoff || !PMEM_IS_PAGE_ALIGNED(vma_size)) {
+#if PMEM_DEBUG
+		pr_err("pmem: mmaps must be at offset zero, aligned"
+				" and a multiple of pages_size.\n");
+#endif
+		return -EINVAL;
+	}
+
+	down_write(&data->sem);
+	/* check this file isn't already mmaped, for submaps check this file
+	 * has never been mmaped */
+	if ((data->flags & PMEM_FLAGS_SUBMAP) ||
+	    (data->flags & PMEM_FLAGS_UNSUBMAP)) {
+#if PMEM_DEBUG
+		pr_err("pmem: you can only mmap a pmem file once, "
+		       "this file is already mmaped. %x\n", data->flags);
+#endif
+		ret = -EINVAL;
+		goto error;
+	}
+	/* if file->private_data == unalloced, alloc*/
+	if (data->index == -1) {
+		mutex_lock(&pmem[id].arena_mutex);
+		index = pmem_allocate_from_id(id,
+				vma->vm_end - vma->vm_start,
+				SZ_4K);
+		mutex_unlock(&pmem[id].arena_mutex);
+		/* either no space was available or an error occured */
+		if (index == -1) {
+			pr_err("pmem: mmap unable to allocate memory"
+				"on %s\n", get_name(file));
+			ret = -ENOMEM;
+			goto error;
+		}
+		/* store the index of a successful allocation */
+		data->index = index;
+	}
+
+	if (pmem[id].len(id, data) < vma_size) {
+#if PMEM_DEBUG
+		pr_err("pmem: mmap size [%lu] does not match"
+		       " size of backing region [%lu].\n", vma_size,
+		       pmem[id].len(id, data));
+#endif
+		ret = -EINVAL;
+		goto error;
+	}
+
+	vma->vm_pgoff = pmem[id].start_addr(id, data) >> PAGE_SHIFT;
+
+	vma->vm_page_prot = pmem_phys_mem_access_prot(file, vma->vm_page_prot);
+
+	if (data->flags & PMEM_FLAGS_CONNECTED) {
+		struct pmem_region_node *region_node;
+		struct list_head *elt;
+		if (pmem_map_garbage(id, vma, data, 0, vma_size)) {
+			pr_alert("pmem: mmap failed in kernel!\n");
+			ret = -EAGAIN;
+			goto error;
+		}
+		list_for_each(elt, &data->region_list) {
+			region_node = list_entry(elt, struct pmem_region_node,
+						 list);
+			DLOG("remapping file: %p %lx %lx\n", file,
+				region_node->region.offset,
+				region_node->region.len);
+			if (pmem_remap_pfn_range(id, vma, data,
+						 region_node->region.offset,
+						 region_node->region.len)) {
+				ret = -EAGAIN;
+				goto error;
+			}
+		}
+		data->flags |= PMEM_FLAGS_SUBMAP;
+		get_task_struct(current->group_leader);
+		data->task = current->group_leader;
+		data->vma = vma;
+#if PMEM_DEBUG
+		data->pid = current->pid;
+#endif
+		DLOG("submmapped file %p vma %p pid %u\n", file, vma,
+		     current->pid);
+	} else {
+		if (pmem_map_pfn_range(id, vma, data, 0, vma_size)) {
+			pr_err("pmem: mmap failed in kernel!\n");
+			ret = -EAGAIN;
+			goto error;
+		}
+		data->flags |= PMEM_FLAGS_MASTERMAP;
+		data->pid = current->pid;
+	}
+	vma->vm_ops = &vm_ops;
+error:
+	up_write(&data->sem);
+	return ret;
+}
+
+/* the following are the api for accessing pmem regions by other drivers
+ * from inside the kernel */
+int get_pmem_user_addr(struct file *file, unsigned long *start,
+		   unsigned long *len)
+{
+	int ret = -1;
+
+	if (is_pmem_file(file)) {
+		struct pmem_data *data = file->private_data;
+
+		down_read(&data->sem);
+		if (has_allocation(file)) {
+			if (data->vma) {
+				*start = data->vma->vm_start;
+				*len = data->vma->vm_end - data->vma->vm_start;
+			} else {
+				*start = *len = 0;
+#if PMEM_DEBUG
+				pr_err("pmem: %s: no vma present.\n",
+					__func__);
+#endif
+			}
+			ret = 0;
+		}
+		up_read(&data->sem);
+	}
+
+#if PMEM_DEBUG
+	if (ret)
+		pr_err("pmem: %s: requested pmem data from invalid"
+			"file.\n", __func__);
+#endif
+	return ret;
+}
+
+int get_pmem_addr(struct file *file, unsigned long *start,
+		  unsigned long *vstart, unsigned long *len)
+{
+	int ret = -1;
+
+	if (is_pmem_file(file)) {
+		struct pmem_data *data = file->private_data;
+
+		down_read(&data->sem);
+		if (has_allocation(file)) {
+			int id = get_id(file);
+
+			*start = pmem[id].start_addr(id, data);
+			*len = pmem[id].len(id, data);
+			*vstart = (unsigned long)
+				pmem_start_vaddr(id, data);
+			up_read(&data->sem);
+#if PMEM_DEBUG
+			down_write(&data->sem);
+			data->ref++;
+			up_write(&data->sem);
+#endif
+			DLOG("returning start %#lx len %lu "
+				"vstart %#lx\n",
+				*start, *len, *vstart);
+			ret = 0;
+		} else {
+			up_read(&data->sem);
+		}
+	}
+	return ret;
+}
+
+int get_pmem_file(unsigned int fd, unsigned long *start, unsigned long *vstart,
+		  unsigned long *len, struct file **filp)
+{
+	int ret = -1;
+	struct file *file = fget(fd);
+
+	if (unlikely(file == NULL)) {
+		pr_err("pmem: %s: requested data from file "
+			"descriptor that doesn't exist.\n", __func__);
+	} else {
+#if PMEM_DEBUG_MSGS
+		char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1];
+#endif
+		DLOG("filp %p rdev %d pid %u(%s) file %p(%ld)"
+			" dev %s(id: %d)\n", filp,
+			file->f_dentry->d_inode->i_rdev,
+			current->pid, get_task_comm(currtask_name, current),
+			file, file_count(file), get_name(file), get_id(file));
+
+		if (!get_pmem_addr(file, start, vstart, len)) {
+			if (filp)
+				*filp = file;
+			ret = 0;
+		} else {
+			fput(file);
+		}
+	}
+	return ret;
+}
+EXPORT_SYMBOL(get_pmem_file);
+
+int get_pmem_fd(int fd, unsigned long *start, unsigned long *len)
+{
+	unsigned long vstart;
+	return get_pmem_file(fd, start, &vstart, len, NULL);
+}
+EXPORT_SYMBOL(get_pmem_fd);
+
+void put_pmem_file(struct file *file)
+{
+#if PMEM_DEBUG_MSGS
+	char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1];
+#endif
+	DLOG("rdev %d pid %u(%s) file %p(%ld)" " dev %s(id: %d)\n",
+		file->f_dentry->d_inode->i_rdev, current->pid,
+		get_task_comm(currtask_name, current), file,
+		file_count(file), get_name(file), get_id(file));
+	if (is_pmem_file(file)) {
+#if PMEM_DEBUG
+		struct pmem_data *data = file->private_data;
+
+		down_write(&data->sem);
+		if (!data->ref--) {
+			data->ref++;
+			pr_alert("pmem: pmem_put > pmem_get %s "
+				"(pid %d)\n",
+			       pmem[get_id(file)].dev.name, data->pid);
+			BUG();
+		}
+		up_write(&data->sem);
+#endif
+		fput(file);
+	}
+}
+EXPORT_SYMBOL(put_pmem_file);
+
+void put_pmem_fd(int fd)
+{
+	int put_needed;
+	struct file *file = fget_light(fd, &put_needed);
+
+	if (file) {
+		put_pmem_file(file);
+		fput_light(file, put_needed);
+	}
+}
+
+void flush_pmem_fd(int fd, unsigned long offset, unsigned long len)
+{
+	int fput_needed;
+	struct file *file = fget_light(fd, &fput_needed);
+
+	if (file) {
+		flush_pmem_file(file, offset, len);
+		fput_light(file, fput_needed);
+	}
+}
+
+void flush_pmem_file(struct file *file, unsigned long offset, unsigned long len)
+{
+	struct pmem_data *data;
+	int id;
+	void *vaddr;
+	struct pmem_region_node *region_node;
+	struct list_head *elt;
+	void *flush_start, *flush_end;
+#ifdef CONFIG_OUTER_CACHE
+	unsigned long phy_start, phy_end;
+#endif
+	if (!is_pmem_file(file))
+		return;
+
+	id = get_id(file);
+	if (!pmem[id].cached)
+		return;
+
+	/* is_pmem_file fails if !file */
+	data = file->private_data;
+
+	down_read(&data->sem);
+	if (!has_allocation(file))
+		goto end;
+
+	vaddr = pmem_start_vaddr(id, data);
+
+	if (pmem[id].allocator_type == PMEM_ALLOCATORTYPE_SYSTEM) {
+		dmac_flush_range(vaddr,
+			(void *)((unsigned long)vaddr +
+				 ((struct alloc_list *)(data->index))->size));
+#ifdef CONFIG_OUTER_CACHE
+		phy_start = pmem_start_addr_system(id, data);
+
+		phy_end = phy_start +
+			((struct alloc_list *)(data->index))->size;
+
+		outer_flush_range(phy_start, phy_end);
+#endif
+		goto end;
+	}
+	/* if this isn't a submmapped file, flush the whole thing */
+	if (unlikely(!(data->flags & PMEM_FLAGS_CONNECTED))) {
+		dmac_flush_range(vaddr, vaddr + pmem[id].len(id, data));
+#ifdef CONFIG_OUTER_CACHE
+		phy_start = (unsigned long)vaddr -
+				(unsigned long)pmem[id].vbase + pmem[id].base;
+
+		phy_end  =  phy_start + pmem[id].len(id, data);
+
+		outer_flush_range(phy_start, phy_end);
+#endif
+		goto end;
+	}
+	/* otherwise, flush the region of the file we are drawing */
+	list_for_each(elt, &data->region_list) {
+		region_node = list_entry(elt, struct pmem_region_node, list);
+		if ((offset >= region_node->region.offset) &&
+		    ((offset + len) <= (region_node->region.offset +
+			region_node->region.len))) {
+			flush_start = vaddr + region_node->region.offset;
+			flush_end = flush_start + region_node->region.len;
+			dmac_flush_range(flush_start, flush_end);
+#ifdef CONFIG_OUTER_CACHE
+
+			phy_start = (unsigned long)flush_start -
+				(unsigned long)pmem[id].vbase + pmem[id].base;
+
+			phy_end  =  phy_start + region_node->region.len;
+
+			outer_flush_range(phy_start, phy_end);
+#endif
+			break;
+		}
+	}
+end:
+	up_read(&data->sem);
+}
+
+int pmem_cache_maint(struct file *file, unsigned int cmd,
+		struct pmem_addr *pmem_addr)
+{
+	struct pmem_data *data;
+	int id;
+	unsigned long vaddr, paddr, length, offset,
+		      pmem_len, pmem_start_addr;
+
+	/* Called from kernel-space so file may be NULL */
+	if (!file)
+		return -EBADF;
+
+	/*
+	 * check that the vaddr passed for flushing is valid
+	 * so that you don't crash the kernel
+	 */
+	if (!pmem_addr->vaddr)
+		return -EINVAL;
+
+	data = file->private_data;
+	id = get_id(file);
+
+	if (!pmem[id].cached)
+		return 0;
+
+	offset = pmem_addr->offset;
+	length = pmem_addr->length;
+
+	down_read(&data->sem);
+	if (!has_allocation(file)) {
+		up_read(&data->sem);
+		return -EINVAL;
+	}
+	pmem_len = pmem[id].len(id, data);
+	pmem_start_addr = pmem[id].start_addr(id, data);
+	up_read(&data->sem);
+
+	if (offset + length > pmem_len)
+		return -EINVAL;
+
+	vaddr = pmem_addr->vaddr;
+	paddr = pmem_start_addr + offset;
+
+	DLOG("pmem cache maint on dev %s(id: %d)"
+		"(vaddr %lx paddr %lx len %lu bytes)\n",
+		get_name(file), id, vaddr, paddr, length);
+	if (cmd == PMEM_CLEAN_INV_CACHES)
+		clean_and_invalidate_caches(vaddr,
+				length, paddr);
+	else if (cmd == PMEM_CLEAN_CACHES)
+		clean_caches(vaddr, length, paddr);
+	else if (cmd == PMEM_INV_CACHES)
+		invalidate_caches(vaddr, length, paddr);
+
+	return 0;
+}
+EXPORT_SYMBOL(pmem_cache_maint);
+
+static int pmem_connect(unsigned long connect, struct file *file)
+{
+	int ret = 0, put_needed;
+	struct file *src_file;
+
+	if (!file) {
+		pr_err("pmem: %s: NULL file pointer passed in, "
+			"bailing out!\n", __func__);
+		ret = -EINVAL;
+		goto leave;
+	}
+
+	src_file = fget_light(connect, &put_needed);
+
+	if (!src_file) {
+		pr_err("pmem: %s: src file not found!\n", __func__);
+		ret = -EBADF;
+		goto leave;
+	}
+
+	if (src_file == file) { /* degenerative case, operator error */
+		pr_err("pmem: %s: src_file and passed in file are "
+			"the same; refusing to connect to self!\n", __func__);
+		ret = -EINVAL;
+		goto put_src_file;
+	}
+
+	if (unlikely(!is_pmem_file(src_file))) {
+		pr_err("pmem: %s: src file is not a pmem file!\n",
+			__func__);
+		ret = -EINVAL;
+		goto put_src_file;
+	} else {
+		struct pmem_data *src_data = src_file->private_data;
+
+		if (!src_data) {
+			pr_err("pmem: %s: src file pointer has no"
+				"private data, bailing out!\n", __func__);
+			ret = -EINVAL;
+			goto put_src_file;
+		}
+
+		down_read(&src_data->sem);
+
+		if (unlikely(!has_allocation(src_file))) {
+			up_read(&src_data->sem);
+			pr_err("pmem: %s: src file has no allocation!\n",
+				__func__);
+			ret = -EINVAL;
+		} else {
+			struct pmem_data *data;
+			int src_index = src_data->index;
+
+			up_read(&src_data->sem);
+
+			data = file->private_data;
+			if (!data) {
+				pr_err("pmem: %s: passed in file "
+					"pointer has no private data, bailing"
+					" out!\n", __func__);
+				ret = -EINVAL;
+				goto put_src_file;
+			}
+
+			down_write(&data->sem);
+			if (has_allocation(file) &&
+					(data->index != src_index)) {
+				up_write(&data->sem);
+
+				pr_err("pmem: %s: file is already "
+					"mapped but doesn't match this "
+					"src_file!\n", __func__);
+				ret = -EINVAL;
+			} else {
+				data->index = src_index;
+				data->flags |= PMEM_FLAGS_CONNECTED;
+				data->master_fd = connect;
+				data->master_file = src_file;
+
+				up_write(&data->sem);
+
+				DLOG("connect %p to %p\n", file, src_file);
+			}
+		}
+	}
+put_src_file:
+	fput_light(src_file, put_needed);
+leave:
+	return ret;
+}
+
+static void pmem_unlock_data_and_mm(struct pmem_data *data,
+				    struct mm_struct *mm)
+{
+	up_write(&data->sem);
+	if (mm != NULL) {
+		up_write(&mm->mmap_sem);
+		mmput(mm);
+	}
+}
+
+static int pmem_lock_data_and_mm(struct file *file, struct pmem_data *data,
+				 struct mm_struct **locked_mm)
+{
+	int ret = 0;
+	struct mm_struct *mm = NULL;
+#if PMEM_DEBUG_MSGS
+	char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1];
+#endif
+	DLOG("pid %u(%s) file %p(%ld)\n",
+		current->pid, get_task_comm(currtask_name, current),
+		file, file_count(file));
+
+	*locked_mm = NULL;
+lock_mm:
+	down_read(&data->sem);
+	if (PMEM_IS_SUBMAP(data)) {
+		mm = get_task_mm(data->task);
+		if (!mm) {
+			up_read(&data->sem);
+#if PMEM_DEBUG
+			pr_alert("pmem: can't remap - task is gone!\n");
+#endif
+			return -1;
+		}
+	}
+	up_read(&data->sem);
+
+	if (mm)
+		down_write(&mm->mmap_sem);
+
+	down_write(&data->sem);
+	/* check that the file didn't get mmaped before we could take the
+	 * data sem, this should be safe b/c you can only submap each file
+	 * once */
+	if (PMEM_IS_SUBMAP(data) && !mm) {
+		pmem_unlock_data_and_mm(data, mm);
+		DLOG("mapping contention, repeating mmap op\n");
+		goto lock_mm;
+	}
+	/* now check that vma.mm is still there, it could have been
+	 * deleted by vma_close before we could get the data->sem */
+	if ((data->flags & PMEM_FLAGS_UNSUBMAP) && (mm != NULL)) {
+		/* might as well release this */
+		if (data->flags & PMEM_FLAGS_SUBMAP) {
+			put_task_struct(data->task);
+			data->task = NULL;
+			/* lower the submap flag to show the mm is gone */
+			data->flags &= ~(PMEM_FLAGS_SUBMAP);
+		}
+		pmem_unlock_data_and_mm(data, mm);
+#if PMEM_DEBUG
+		pr_alert("pmem: vma.mm went away!\n");
+#endif
+		return -1;
+	}
+	*locked_mm = mm;
+	return ret;
+}
+
+int pmem_remap(struct pmem_region *region, struct file *file,
+		      unsigned operation)
+{
+	int ret;
+	struct pmem_region_node *region_node;
+	struct mm_struct *mm = NULL;
+	struct list_head *elt, *elt2;
+	int id = get_id(file);
+	struct pmem_data *data;
+
+	DLOG("operation %#x, region offset %ld, region len %ld\n",
+		operation, region->offset, region->len);
+
+	if (!is_pmem_file(file)) {
+#if PMEM_DEBUG
+		pr_err("pmem: remap request for non-pmem file descriptor\n");
+#endif
+		return -EINVAL;
+	}
+
+	/* is_pmem_file fails if !file */
+	data = file->private_data;
+
+	/* pmem region must be aligned on a page boundry */
+	if (unlikely(!PMEM_IS_PAGE_ALIGNED(region->offset) ||
+		 !PMEM_IS_PAGE_ALIGNED(region->len))) {
+#if PMEM_DEBUG
+		pr_err("pmem: request for unaligned pmem"
+			"suballocation %lx %lx\n",
+			region->offset, region->len);
+#endif
+		return -EINVAL;
+	}
+
+	/* if userspace requests a region of len 0, there's nothing to do */
+	if (region->len == 0)
+		return 0;
+
+	/* lock the mm and data */
+	ret = pmem_lock_data_and_mm(file, data, &mm);
+	if (ret)
+		return 0;
+
+	/* only the owner of the master file can remap the client fds
+	 * that back in it */
+	if (!is_master_owner(file)) {
+#if PMEM_DEBUG
+		pr_err("pmem: remap requested from non-master process\n");
+#endif
+		ret = -EINVAL;
+		goto err;
+	}
+
+	/* check that the requested range is within the src allocation */
+	if (unlikely((region->offset > pmem[id].len(id, data)) ||
+		     (region->len > pmem[id].len(id, data)) ||
+		     (region->offset + region->len > pmem[id].len(id, data)))) {
+#if PMEM_DEBUG
+		pr_err("pmem: suballoc doesn't fit in src_file!\n");
+#endif
+		ret = -EINVAL;
+		goto err;
+	}
+
+	if (operation == PMEM_MAP) {
+		region_node = kmalloc(sizeof(struct pmem_region_node),
+			      GFP_KERNEL);
+		if (!region_node) {
+			ret = -ENOMEM;
+#if PMEM_DEBUG
+			pr_alert("pmem: No space to allocate remap metadata!");
+#endif
+			goto err;
+		}
+		region_node->region = *region;
+		list_add(&region_node->list, &data->region_list);
+	} else if (operation == PMEM_UNMAP) {
+		int found = 0;
+		list_for_each_safe(elt, elt2, &data->region_list) {
+			region_node = list_entry(elt, struct pmem_region_node,
+				      list);
+			if (region->len == 0 ||
+			    (region_node->region.offset == region->offset &&
+			    region_node->region.len == region->len)) {
+				list_del(elt);
+				kfree(region_node);
+				found = 1;
+			}
+		}
+		if (!found) {
+#if PMEM_DEBUG
+			pr_err("pmem: Unmap region does not map any"
+				" mapped region!");
+#endif
+			ret = -EINVAL;
+			goto err;
+		}
+	}
+
+	if (data->vma && PMEM_IS_SUBMAP(data)) {
+		if (operation == PMEM_MAP)
+			ret = pmem_remap_pfn_range(id, data->vma, data,
+				   region->offset, region->len);
+		else if (operation == PMEM_UNMAP)
+			ret = pmem_unmap_pfn_range(id, data->vma, data,
+				   region->offset, region->len);
+	}
+
+err:
+	pmem_unlock_data_and_mm(data, mm);
+	return ret;
+}
+
+static void pmem_revoke(struct file *file, struct pmem_data *data)
+{
+	struct pmem_region_node *region_node;
+	struct list_head *elt, *elt2;
+	struct mm_struct *mm = NULL;
+	int id = get_id(file);
+	int ret = 0;
+
+	data->master_file = NULL;
+	ret = pmem_lock_data_and_mm(file, data, &mm);
+	/* if lock_data_and_mm fails either the task that mapped the fd, or
+	 * the vma that mapped it have already gone away, nothing more
+	 * needs to be done */
+	if (ret)
+		return;
+	/* unmap everything */
+	/* delete the regions and region list nothing is mapped any more */
+	if (data->vma)
+		list_for_each_safe(elt, elt2, &data->region_list) {
+			region_node = list_entry(elt, struct pmem_region_node,
+						 list);
+			pmem_unmap_pfn_range(id, data->vma, data,
+					     region_node->region.offset,
+					     region_node->region.len);
+			list_del(elt);
+			kfree(region_node);
+	}
+	/* delete the master file */
+	pmem_unlock_data_and_mm(data, mm);
+}
+
+static void pmem_get_size(struct pmem_region *region, struct file *file)
+{
+	/* called via ioctl file op, so file guaranteed to be not NULL */
+	struct pmem_data *data = file->private_data;
+	int id = get_id(file);
+
+	down_read(&data->sem);
+	if (!has_allocation(file)) {
+		region->offset = 0;
+		region->len = 0;
+	} else {
+		region->offset = pmem[id].start_addr(id, data);
+		region->len = pmem[id].len(id, data);
+	}
+	up_read(&data->sem);
+	DLOG("offset 0x%lx len 0x%lx\n", region->offset, region->len);
+}
+
+
+static long pmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	/* called from user space as file op, so file guaranteed to be not
+	 * NULL
+	 */
+	struct pmem_data *data = file->private_data;
+	int id = get_id(file);
+#if PMEM_DEBUG_MSGS
+	char currtask_name[
+		FIELD_SIZEOF(struct task_struct, comm) + 1];
+#endif
+
+	DLOG("pid %u(%s) file %p(%ld) cmd %#x, dev %s(id: %d)\n",
+		current->pid, get_task_comm(currtask_name, current),
+		file, file_count(file), cmd, get_name(file), id);
+
+	switch (cmd) {
+	case PMEM_GET_PHYS:
+		{
+			struct pmem_region region;
+
+			DLOG("get_phys\n");
+			down_read(&data->sem);
+			if (!has_allocation(file)) {
+				region.offset = 0;
+				region.len = 0;
+			} else {
+				region.offset = pmem[id].start_addr(id, data);
+				region.len = pmem[id].len(id, data);
+			}
+			up_read(&data->sem);
+
+			if (copy_to_user((void __user *)arg, &region,
+						sizeof(struct pmem_region)))
+				return -EFAULT;
+
+			DLOG("pmem: successful request for "
+				"physical address of pmem region id %d, "
+				"offset 0x%lx, len 0x%lx\n",
+				id, region.offset, region.len);
+
+			break;
+		}
+	case PMEM_MAP:
+		{
+			struct pmem_region region;
+			DLOG("map\n");
+			if (copy_from_user(&region, (void __user *)arg,
+						sizeof(struct pmem_region)))
+				return -EFAULT;
+			return pmem_remap(&region, file, PMEM_MAP);
+		}
+		break;
+	case PMEM_UNMAP:
+		{
+			struct pmem_region region;
+			DLOG("unmap\n");
+			if (copy_from_user(&region, (void __user *)arg,
+						sizeof(struct pmem_region)))
+				return -EFAULT;
+			return pmem_remap(&region, file, PMEM_UNMAP);
+			break;
+		}
+	case PMEM_GET_SIZE:
+		{
+			struct pmem_region region;
+			DLOG("get_size\n");
+			pmem_get_size(&region, file);
+			if (copy_to_user((void __user *)arg, &region,
+						sizeof(struct pmem_region)))
+				return -EFAULT;
+			break;
+		}
+	case PMEM_GET_TOTAL_SIZE:
+		{
+			struct pmem_region region;
+			DLOG("get total size\n");
+			region.offset = 0;
+			get_id(file);
+			region.len = pmem[id].size;
+			if (copy_to_user((void __user *)arg, &region,
+						sizeof(struct pmem_region)))
+				return -EFAULT;
+			break;
+		}
+	case PMEM_GET_FREE_SPACE:
+		{
+			struct pmem_freespace fs;
+			DLOG("get freespace on %s(id: %d)\n",
+				get_name(file), id);
+
+			mutex_lock(&pmem[id].arena_mutex);
+			pmem[id].free_space(id, &fs);
+			mutex_unlock(&pmem[id].arena_mutex);
+
+			DLOG("%s(id: %d) total free %lu, largest %lu\n",
+				get_name(file), id, fs.total, fs.largest);
+
+			if (copy_to_user((void __user *)arg, &fs,
+				sizeof(struct pmem_freespace)))
+				return -EFAULT;
+			break;
+	}
+
+	case PMEM_ALLOCATE:
+		{
+			int ret = 0;
+			DLOG("allocate, id %d\n", id);
+			down_write(&data->sem);
+			if (has_allocation(file)) {
+				pr_err("pmem: Existing allocation found on "
+					"this file descrpitor\n");
+				up_write(&data->sem);
+				return -EINVAL;
+			}
+
+			mutex_lock(&pmem[id].arena_mutex);
+			data->index = pmem_allocate_from_id(id,
+					arg,
+					SZ_4K);
+			mutex_unlock(&pmem[id].arena_mutex);
+			ret = data->index == -1 ? -ENOMEM :
+				data->index;
+			up_write(&data->sem);
+			return ret;
+		}
+	case PMEM_ALLOCATE_ALIGNED:
+		{
+			struct pmem_allocation alloc;
+			int ret = 0;
+
+			if (copy_from_user(&alloc, (void __user *)arg,
+						sizeof(struct pmem_allocation)))
+				return -EFAULT;
+			DLOG("allocate id align %d %u\n", id, alloc.align);
+			down_write(&data->sem);
+			if (has_allocation(file)) {
+				pr_err("pmem: Existing allocation found on "
+					"this file descrpitor\n");
+				up_write(&data->sem);
+				return -EINVAL;
+			}
+
+			if (alloc.align & (alloc.align - 1)) {
+				pr_err("pmem: Alignment is not a power of 2\n");
+				return -EINVAL;
+			}
+
+			if (alloc.align != SZ_4K &&
+					(pmem[id].allocator_type !=
+						PMEM_ALLOCATORTYPE_BITMAP)) {
+				pr_err("pmem: Non 4k alignment requires bitmap"
+					" allocator on %s\n", pmem[id].name);
+				return -EINVAL;
+			}
+
+			if (alloc.align > SZ_1M ||
+				alloc.align < SZ_4K) {
+				pr_err("pmem: Invalid Alignment (%u) "
+					"specified\n", alloc.align);
+				return -EINVAL;
+			}
+
+			mutex_lock(&pmem[id].arena_mutex);
+			data->index = pmem_allocate_from_id(id,
+				alloc.size,
+				alloc.align);
+			mutex_unlock(&pmem[id].arena_mutex);
+			ret = data->index == -1 ? -ENOMEM :
+				data->index;
+			up_write(&data->sem);
+			return ret;
+		}
+	case PMEM_CONNECT:
+		DLOG("connect\n");
+		return pmem_connect(arg, file);
+	case PMEM_CLEAN_INV_CACHES:
+	case PMEM_CLEAN_CACHES:
+	case PMEM_INV_CACHES:
+		{
+			struct pmem_addr pmem_addr;
+
+			if (copy_from_user(&pmem_addr, (void __user *)arg,
+						sizeof(struct pmem_addr)))
+				return -EFAULT;
+
+			return pmem_cache_maint(file, cmd, &pmem_addr);
+		}
+	default:
+		if (pmem[id].ioctl)
+			return pmem[id].ioctl(file, cmd, arg);
+
+		DLOG("ioctl invalid (%#x)\n", cmd);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static void ioremap_pmem(int id)
+{
+	unsigned long addr;
+	const struct mem_type *type;
+
+	DLOG("PMEMDEBUG: ioremaping for %s\n", pmem[id].name);
+	if (pmem[id].map_on_demand) {
+		addr = (unsigned long)pmem[id].area->addr;
+		if (pmem[id].cached)
+			type = get_mem_type(MT_DEVICE_CACHED);
+		else
+			type = get_mem_type(MT_DEVICE);
+		DLOG("PMEMDEBUG: Remap phys %lx to virt %lx on %s\n",
+			pmem[id].base, addr, pmem[id].name);
+		if (ioremap_pages(addr, pmem[id].base,  pmem[id].size, type)) {
+				pr_err("pmem: Failed to map pages\n");
+				BUG();
+		}
+		pmem[id].vbase = pmem[id].area->addr;
+		/* Flush the cache after installing page table entries to avoid
+		 * aliasing when these pages are remapped to user space.
+		 */
+		flush_cache_vmap(addr, addr + pmem[id].size);
+	} else {
+		if (pmem[id].cached)
+			pmem[id].vbase = ioremap_cached(pmem[id].base,
+						pmem[id].size);
+	#ifdef ioremap_ext_buffered
+		else if (pmem[id].buffered)
+			pmem[id].vbase = ioremap_ext_buffered(pmem[id].base,
+						pmem[id].size);
+	#endif
+		else
+			pmem[id].vbase = ioremap(pmem[id].base, pmem[id].size);
+	}
+}
+
+int pmem_setup(struct android_pmem_platform_data *pdata,
+	       long (*ioctl)(struct file *, unsigned int, unsigned long),
+	       int (*release)(struct inode *, struct file *))
+{
+	int i, index = 0, id;
+	struct vm_struct *pmem_vma = NULL;
+	struct page *page;
+
+	if (id_count >= PMEM_MAX_DEVICES) {
+		pr_alert("pmem: %s: unable to register driver(%s) - no more "
+			"devices available!\n", __func__, pdata->name);
+		goto err_no_mem;
+	}
+
+	if (!pdata->size) {
+		pr_alert("pmem: %s: unable to register pmem driver(%s) - zero "
+			"size passed in!\n", __func__, pdata->name);
+		goto err_no_mem;
+	}
+
+	id = id_count++;
+
+	pmem[id].id = id;
+
+	if (pmem[id].allocate) {
+		pr_alert("pmem: %s: unable to register pmem driver - "
+			"duplicate registration of %s!\n",
+			__func__, pdata->name);
+		goto err_no_mem;
+	}
+
+	pmem[id].allocator_type = pdata->allocator_type;
+
+	/* 'quantum' is a "hidden" variable that defaults to 0 in the board
+	 * files */
+	pmem[id].quantum = pdata->quantum ?: PMEM_MIN_ALLOC;
+	if (pmem[id].quantum < PMEM_MIN_ALLOC ||
+		!is_power_of_2(pmem[id].quantum)) {
+		pr_alert("pmem: %s: unable to register pmem driver %s - "
+			"invalid quantum value (%#x)!\n",
+			__func__, pdata->name, pmem[id].quantum);
+		goto err_reset_pmem_info;
+	}
+
+	if (pdata->size % pmem[id].quantum) {
+		/* bad alignment for size! */
+		pr_alert("pmem: %s: Unable to register driver %s - "
+			"memory region size (%#lx) is not a multiple of "
+			"quantum size(%#x)!\n", __func__, pdata->name,
+			pdata->size, pmem[id].quantum);
+		goto err_reset_pmem_info;
+	}
+
+	pmem[id].cached = pdata->cached;
+	pmem[id].buffered = pdata->buffered;
+	pmem[id].size = pdata->size;
+	pmem[id].memory_type = pdata->memory_type;
+	strlcpy(pmem[id].name, pdata->name, PMEM_NAME_SIZE);
+
+	pmem[id].num_entries = pmem[id].size / pmem[id].quantum;
+
+	memset(&pmem[id].kobj, 0, sizeof(pmem[0].kobj));
+	pmem[id].kobj.kset = pmem_kset;
+
+	switch (pmem[id].allocator_type) {
+	case PMEM_ALLOCATORTYPE_ALLORNOTHING:
+		pmem[id].allocate = pmem_allocator_all_or_nothing;
+		pmem[id].free = pmem_free_all_or_nothing;
+		pmem[id].free_space = pmem_free_space_all_or_nothing;
+		pmem[id].len = pmem_len_all_or_nothing;
+		pmem[id].start_addr = pmem_start_addr_all_or_nothing;
+		pmem[id].num_entries = 1;
+		pmem[id].quantum = pmem[id].size;
+		pmem[id].allocator.all_or_nothing.allocated = 0;
+
+		if (kobject_init_and_add(&pmem[id].kobj,
+				&pmem_allornothing_ktype, NULL,
+				"%s", pdata->name))
+			goto out_put_kobj;
+
+		break;
+
+	case PMEM_ALLOCATORTYPE_BUDDYBESTFIT:
+		pmem[id].allocator.buddy_bestfit.buddy_bitmap = kmalloc(
+			pmem[id].num_entries * sizeof(struct pmem_bits),
+			GFP_KERNEL);
+		if (!pmem[id].allocator.buddy_bestfit.buddy_bitmap)
+			goto err_reset_pmem_info;
+
+		memset(pmem[id].allocator.buddy_bestfit.buddy_bitmap, 0,
+			sizeof(struct pmem_bits) * pmem[id].num_entries);
+
+		for (i = sizeof(pmem[id].num_entries) * 8 - 1; i >= 0; i--)
+			if ((pmem[id].num_entries) &  1<<i) {
+				PMEM_BUDDY_ORDER(id, index) = i;
+				index = PMEM_BUDDY_NEXT_INDEX(id, index);
+			}
+		pmem[id].allocate = pmem_allocator_buddy_bestfit;
+		pmem[id].free = pmem_free_buddy_bestfit;
+		pmem[id].free_space = pmem_free_space_buddy_bestfit;
+		pmem[id].len = pmem_len_buddy_bestfit;
+		pmem[id].start_addr = pmem_start_addr_buddy_bestfit;
+		if (kobject_init_and_add(&pmem[id].kobj,
+				&pmem_buddy_bestfit_ktype, NULL,
+				"%s", pdata->name))
+			goto out_put_kobj;
+
+		break;
+
+	case PMEM_ALLOCATORTYPE_BITMAP: /* 0, default if not explicit */
+		pmem[id].allocator.bitmap.bitm_alloc = kmalloc(
+			PMEM_INITIAL_NUM_BITMAP_ALLOCATIONS *
+				sizeof(*pmem[id].allocator.bitmap.bitm_alloc),
+			GFP_KERNEL);
+		if (!pmem[id].allocator.bitmap.bitm_alloc) {
+			pr_alert("pmem: %s: Unable to register pmem "
+					"driver %s - can't allocate "
+					"bitm_alloc!\n",
+					__func__, pdata->name);
+			goto err_reset_pmem_info;
+		}
+
+		if (kobject_init_and_add(&pmem[id].kobj,
+				&pmem_bitmap_ktype, NULL,
+				"%s", pdata->name))
+			goto out_put_kobj;
+
+		for (i = 0; i < PMEM_INITIAL_NUM_BITMAP_ALLOCATIONS; i++) {
+			pmem[id].allocator.bitmap.bitm_alloc[i].bit = -1;
+			pmem[id].allocator.bitmap.bitm_alloc[i].quanta = 0;
+		}
+
+		pmem[id].allocator.bitmap.bitmap_allocs =
+			PMEM_INITIAL_NUM_BITMAP_ALLOCATIONS;
+
+		pmem[id].allocator.bitmap.bitmap =
+			kcalloc((pmem[id].num_entries + 31) / 32,
+				sizeof(unsigned int), GFP_KERNEL);
+		if (!pmem[id].allocator.bitmap.bitmap) {
+			pr_alert("pmem: %s: Unable to register pmem "
+				"driver - can't allocate bitmap!\n",
+				__func__);
+			goto err_cant_register_device;
+		}
+		pmem[id].allocator.bitmap.bitmap_free = pmem[id].num_entries;
+
+		pmem[id].allocate = pmem_allocator_bitmap;
+		pmem[id].free = pmem_free_bitmap;
+		pmem[id].free_space = pmem_free_space_bitmap;
+		pmem[id].len = pmem_len_bitmap;
+		pmem[id].start_addr = pmem_start_addr_bitmap;
+
+		DLOG("bitmap allocator id %d (%s), num_entries %u, raw size "
+			"%lu, quanta size %u\n",
+			id, pdata->name, pmem[id].allocator.bitmap.bitmap_free,
+			pmem[id].size, pmem[id].quantum);
+		break;
+
+	case PMEM_ALLOCATORTYPE_SYSTEM:
+
+		INIT_LIST_HEAD(&pmem[id].allocator.system_mem.alist);
+
+		pmem[id].allocator.system_mem.used = 0;
+		pmem[id].vbase = NULL;
+
+		if (kobject_init_and_add(&pmem[id].kobj,
+				&pmem_system_ktype, NULL,
+				"%s", pdata->name))
+			goto out_put_kobj;
+
+		pmem[id].allocate = pmem_allocator_system;
+		pmem[id].free = pmem_free_system;
+		pmem[id].free_space = pmem_free_space_system;
+		pmem[id].len = pmem_len_system;
+		pmem[id].start_addr = pmem_start_addr_system;
+		pmem[id].num_entries = 0;
+		pmem[id].quantum = PAGE_SIZE;
+
+		DLOG("system allocator id %d (%s), raw size %lu\n",
+			id, pdata->name, pmem[id].size);
+		break;
+
+	default:
+		pr_alert("Invalid allocator type (%d) for pmem driver\n",
+			pdata->allocator_type);
+		goto err_reset_pmem_info;
+	}
+
+	pmem[id].ioctl = ioctl;
+	pmem[id].release = release;
+	mutex_init(&pmem[id].arena_mutex);
+	mutex_init(&pmem[id].data_list_mutex);
+	INIT_LIST_HEAD(&pmem[id].data_list);
+
+	pmem[id].dev.name = pdata->name;
+	pmem[id].dev.minor = id;
+	pmem[id].dev.fops = &pmem_fops;
+	pmem[id].reusable = pdata->reusable;
+	pr_info("pmem: Initializing %s as %s\n",
+		pdata->name, pdata->cached ? "cached" : "non-cached");
+
+	if (misc_register(&pmem[id].dev)) {
+		pr_alert("Unable to register pmem driver!\n");
+		goto err_cant_register_device;
+	}
+
+	if (!pmem[id].reusable) {
+		pmem[id].base = allocate_contiguous_memory_nomap(pmem[id].size,
+			pmem[id].memory_type, PAGE_SIZE);
+		if (!pmem[id].base) {
+			pr_err("pmem: Cannot allocate from reserved memory for %s\n",
+				pdata->name);
+			goto err_misc_deregister;
+		}
+	}
+
+	/* reusable pmem requires map on demand */
+	pmem[id].map_on_demand = pdata->map_on_demand || pdata->reusable;
+	if (pmem[id].map_on_demand) {
+		if (pmem[id].reusable) {
+			const struct fmem_data *fmem_info = fmem_get_info();
+			pmem[id].area = fmem_info->area;
+			pmem[id].base = fmem_info->phys;
+		} else {
+			pmem_vma = get_vm_area(pmem[id].size, VM_IOREMAP);
+			if (!pmem_vma) {
+				pr_err("pmem: Failed to allocate virtual space for "
+					"%s\n", pdata->name);
+				goto err_free;
+			}
+			pr_err("pmem: Reserving virtual address range %lx - %lx for"
+				" %s\n", (unsigned long) pmem_vma->addr,
+				(unsigned long) pmem_vma->addr + pmem[id].size,
+				pdata->name);
+			pmem[id].area = pmem_vma;
+		}
+	} else
+		pmem[id].area = NULL;
+
+	page = alloc_page(GFP_KERNEL);
+	if (!page) {
+		pr_err("pmem: Failed to allocate page for %s\n", pdata->name);
+		goto cleanup_vm;
+	}
+	pmem[id].garbage_pfn = page_to_pfn(page);
+	atomic_set(&pmem[id].allocation_cnt, 0);
+
+	if (pdata->setup_region)
+		pmem[id].region_data = pdata->setup_region();
+
+	if (pdata->request_region)
+		pmem[id].mem_request = pdata->request_region;
+
+	if (pdata->release_region)
+		pmem[id].mem_release = pdata->release_region;
+
+	pr_info("allocating %lu bytes at %lx physical for %s\n",
+		pmem[id].size, pmem[id].base, pmem[id].name);
+
+	return 0;
+
+cleanup_vm:
+	if (!pmem[id].reusable)
+		remove_vm_area(pmem_vma);
+err_free:
+	if (!pmem[id].reusable)
+		free_contiguous_memory_by_paddr(pmem[id].base);
+err_misc_deregister:
+	misc_deregister(&pmem[id].dev);
+err_cant_register_device:
+out_put_kobj:
+	kobject_put(&pmem[id].kobj);
+	if (pmem[id].allocator_type == PMEM_ALLOCATORTYPE_BUDDYBESTFIT)
+		kfree(pmem[id].allocator.buddy_bestfit.buddy_bitmap);
+	else if (pmem[id].allocator_type == PMEM_ALLOCATORTYPE_BITMAP) {
+		kfree(pmem[id].allocator.bitmap.bitmap);
+		kfree(pmem[id].allocator.bitmap.bitm_alloc);
+	}
+err_reset_pmem_info:
+	pmem[id].allocate = 0;
+	pmem[id].dev.minor = -1;
+err_no_mem:
+	return -1;
+}
+
+static int pmem_probe(struct platform_device *pdev)
+{
+	struct android_pmem_platform_data *pdata;
+
+	if (!pdev || !pdev->dev.platform_data) {
+		pr_alert("Unable to probe pmem!\n");
+		return -1;
+	}
+	pdata = pdev->dev.platform_data;
+
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+
+	return pmem_setup(pdata, NULL, NULL);
+}
+
+static int pmem_remove(struct platform_device *pdev)
+{
+	int id = pdev->id;
+	__free_page(pfn_to_page(pmem[id].garbage_pfn));
+	pm_runtime_disable(&pdev->dev);
+	if (pmem[id].vbase)
+		iounmap(pmem[id].vbase);
+	if (pmem[id].map_on_demand && !pmem[id].reusable && pmem[id].area)
+		free_vm_area(pmem[id].area);
+	if (pmem[id].base)
+		free_contiguous_memory_by_paddr(pmem[id].base);
+	kobject_put(&pmem[id].kobj);
+	if (pmem[id].allocator_type == PMEM_ALLOCATORTYPE_BUDDYBESTFIT)
+		kfree(pmem[id].allocator.buddy_bestfit.buddy_bitmap);
+	else if (pmem[id].allocator_type == PMEM_ALLOCATORTYPE_BITMAP) {
+		kfree(pmem[id].allocator.bitmap.bitmap);
+		kfree(pmem[id].allocator.bitmap.bitm_alloc);
+	}
+	misc_deregister(&pmem[id].dev);
+	return 0;
+}
+
+static int pmem_runtime_suspend(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: suspending...\n");
+	return 0;
+}
+
+static int pmem_runtime_resume(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: resuming...\n");
+	return 0;
+}
+
+static const struct dev_pm_ops pmem_dev_pm_ops = {
+	.runtime_suspend = pmem_runtime_suspend,
+	.runtime_resume = pmem_runtime_resume,
+};
+
+static struct platform_driver pmem_driver = {
+	.probe = pmem_probe,
+	.remove = pmem_remove,
+	.driver = { .name = "android_pmem",
+		    .pm = &pmem_dev_pm_ops,
+  }
+};
+
+
+static int __init pmem_init(void)
+{
+	/* create /sys/kernel/<PMEM_SYSFS_DIR_NAME> directory */
+	pmem_kset = kset_create_and_add(PMEM_SYSFS_DIR_NAME,
+		NULL, kernel_kobj);
+	if (!pmem_kset) {
+		pr_err("pmem(%s):kset_create_and_add fail\n", __func__);
+		return -ENOMEM;
+	}
+
+	return platform_driver_register(&pmem_driver);
+}
+
+static void __exit pmem_exit(void)
+{
+	platform_driver_unregister(&pmem_driver);
+}
+
+module_init(pmem_init);
+module_exit(pmem_exit);
+
diff --git a/drivers/misc/pmic8058-pwm.c b/drivers/misc/pmic8058-pwm.c
new file mode 100644
index 0000000..33407b7
--- /dev/null
+++ b/drivers/misc/pmic8058-pwm.c
@@ -0,0 +1,1085 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+/*
+ * Qualcomm PMIC8058 PWM driver
+ *
+ */
+
+#define pr_fmt(fmt)	"%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/pwm.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/pmic8058-pwm.h>
+
+#define	PM8058_LPG_BANKS		8
+#define	PM8058_PWM_CHANNELS		PM8058_LPG_BANKS	/* MAX=8 */
+
+#define	PM8058_LPG_CTL_REGS		7
+
+/* PMIC8058 LPG/PWM */
+#define	SSBI_REG_ADDR_LPG_CTL_BASE	0x13C
+#define	SSBI_REG_ADDR_LPG_CTL(n)	(SSBI_REG_ADDR_LPG_CTL_BASE + (n))
+#define	SSBI_REG_ADDR_LPG_BANK_SEL	0x143
+#define	SSBI_REG_ADDR_LPG_BANK_EN	0x144
+#define	SSBI_REG_ADDR_LPG_LUT_CFG0	0x145
+#define	SSBI_REG_ADDR_LPG_LUT_CFG1	0x146
+#define	SSBI_REG_ADDR_LPG_TEST		0x147
+
+/* Control 0 */
+#define	PM8058_PWM_1KHZ_COUNT_MASK	0xF0
+#define	PM8058_PWM_1KHZ_COUNT_SHIFT	4
+
+#define	PM8058_PWM_1KHZ_COUNT_MAX	15
+
+#define	PM8058_PWM_OUTPUT_EN		0x08
+#define	PM8058_PWM_PWM_EN		0x04
+#define	PM8058_PWM_RAMP_GEN_EN		0x02
+#define	PM8058_PWM_RAMP_START		0x01
+
+#define	PM8058_PWM_PWM_START		(PM8058_PWM_OUTPUT_EN \
+					| PM8058_PWM_PWM_EN)
+#define	PM8058_PWM_RAMP_GEN_START	(PM8058_PWM_RAMP_GEN_EN \
+					| PM8058_PWM_RAMP_START)
+
+/* Control 1 */
+#define	PM8058_PWM_REVERSE_EN		0x80
+#define	PM8058_PWM_BYPASS_LUT		0x40
+#define	PM8058_PWM_HIGH_INDEX_MASK	0x3F
+
+/* Control 2 */
+#define	PM8058_PWM_LOOP_EN		0x80
+#define	PM8058_PWM_RAMP_UP		0x40
+#define	PM8058_PWM_LOW_INDEX_MASK	0x3F
+
+/* Control 3 */
+#define	PM8058_PWM_VALUE_BIT7_0		0xFF
+#define	PM8058_PWM_VALUE_BIT5_0		0x3F
+
+/* Control 4 */
+#define	PM8058_PWM_VALUE_BIT8		0x80
+
+#define	PM8058_PWM_CLK_SEL_MASK		0x60
+#define	PM8058_PWM_CLK_SEL_SHIFT	5
+
+#define	PM8058_PWM_CLK_SEL_NO		0
+#define	PM8058_PWM_CLK_SEL_1KHZ		1
+#define	PM8058_PWM_CLK_SEL_32KHZ	2
+#define	PM8058_PWM_CLK_SEL_19P2MHZ	3
+
+#define	PM8058_PWM_PREDIVIDE_MASK	0x18
+#define	PM8058_PWM_PREDIVIDE_SHIFT	3
+
+#define	PM8058_PWM_PREDIVIDE_2		0
+#define	PM8058_PWM_PREDIVIDE_3		1
+#define	PM8058_PWM_PREDIVIDE_5		2
+#define	PM8058_PWM_PREDIVIDE_6		3
+
+#define	PM8058_PWM_M_MASK	0x07
+#define	PM8058_PWM_M_MIN	0
+#define	PM8058_PWM_M_MAX	7
+
+/* Control 5 */
+#define	PM8058_PWM_PAUSE_COUNT_HI_MASK		0xFC
+#define	PM8058_PWM_PAUSE_COUNT_HI_SHIFT		2
+
+#define	PM8058_PWM_PAUSE_ENABLE_HIGH		0x02
+#define	PM8058_PWM_SIZE_9_BIT			0x01
+
+/* Control 6 */
+#define	PM8058_PWM_PAUSE_COUNT_LO_MASK		0xFC
+#define	PM8058_PWM_PAUSE_COUNT_LO_SHIFT		2
+
+#define	PM8058_PWM_PAUSE_ENABLE_LOW		0x02
+#define	PM8058_PWM_RESERVED			0x01
+
+#define	PM8058_PWM_PAUSE_COUNT_MAX		56 /* < 2^6 = 64*/
+
+/* LUT_CFG1 */
+#define	PM8058_PWM_LUT_READ			0x40
+
+/* TEST */
+#define	PM8058_PWM_DTEST_MASK		0x38
+#define	PM8058_PWM_DTEST_SHIFT		3
+
+#define	PM8058_PWM_DTEST_BANK_MASK	0x07
+
+/* PWM frequency support
+ *
+ * PWM Frequency = Clock Frequency / (N * T)
+ * 	or
+ * PWM Period = Clock Period * (N * T)
+ * 	where
+ * N = 2^9 or 2^6 for 9-bit or 6-bit PWM size
+ * T = Pre-divide * 2^m, m = 0..7 (exponent)
+ *
+ * We use this formula to figure out m for the best pre-divide and clock:
+ * (PWM Period / N) / 2^m = (Pre-divide * Clock Period)
+*/
+#define	NUM_CLOCKS	3
+
+#define	NSEC_1000HZ	(NSEC_PER_SEC / 1000)
+#define	NSEC_32768HZ	(NSEC_PER_SEC / 32768)
+#define	NSEC_19P2MHZ	(NSEC_PER_SEC / 19200000)
+
+#define	CLK_PERIOD_MIN	NSEC_19P2MHZ
+#define	CLK_PERIOD_MAX	NSEC_1000HZ
+
+#define	NUM_PRE_DIVIDE	3	/* No default support for pre-divide = 6 */
+
+#define	PRE_DIVIDE_0		2
+#define	PRE_DIVIDE_1		3
+#define	PRE_DIVIDE_2		5
+
+#define	PRE_DIVIDE_MIN		PRE_DIVIDE_0
+#define	PRE_DIVIDE_MAX		PRE_DIVIDE_2
+
+static char *clks[NUM_CLOCKS] = {
+	"1K", "32768", "19.2M"
+};
+
+static unsigned pre_div[NUM_PRE_DIVIDE] = {
+	PRE_DIVIDE_0, PRE_DIVIDE_1, PRE_DIVIDE_2
+};
+
+static unsigned int pt_t[NUM_PRE_DIVIDE][NUM_CLOCKS] = {
+	{	PRE_DIVIDE_0 * NSEC_1000HZ,
+		PRE_DIVIDE_0 * NSEC_32768HZ,
+		PRE_DIVIDE_0 * NSEC_19P2MHZ,
+	},
+	{	PRE_DIVIDE_1 * NSEC_1000HZ,
+		PRE_DIVIDE_1 * NSEC_32768HZ,
+		PRE_DIVIDE_1 * NSEC_19P2MHZ,
+	},
+	{	PRE_DIVIDE_2 * NSEC_1000HZ,
+		PRE_DIVIDE_2 * NSEC_32768HZ,
+		PRE_DIVIDE_2 * NSEC_19P2MHZ,
+	},
+};
+
+#define	MIN_MPT	((PRE_DIVIDE_MIN * CLK_PERIOD_MIN) << PM8058_PWM_M_MIN)
+#define	MAX_MPT	((PRE_DIVIDE_MAX * CLK_PERIOD_MAX) << PM8058_PWM_M_MAX)
+
+#define	CHAN_LUT_SIZE		(PM_PWM_LUT_SIZE / PM8058_PWM_CHANNELS)
+
+/* Private data */
+struct pm8058_pwm_chip;
+
+struct pwm_device {
+	struct device		*dev;
+	int			pwm_id;		/* = bank/channel id */
+	int			in_use;
+	const char		*label;
+	struct pm8058_pwm_period	period;
+	int			pwm_value;
+	int			pwm_period;
+	int			use_lut;	/* Use LUT to output PWM */
+	u8			pwm_ctl[PM8058_LPG_CTL_REGS];
+	int			irq;
+	struct pm8058_pwm_chip  *chip;
+};
+
+struct pm8058_pwm_chip {
+	struct pwm_device	pwm_dev[PM8058_PWM_CHANNELS];
+	u8			bank_mask;
+	struct mutex		pwm_mutex;
+	struct pm8058_pwm_pdata	*pdata;
+};
+
+static struct pm8058_pwm_chip	*pwm_chip;
+
+struct pm8058_pwm_lut {
+	/* LUT parameters */
+	int	lut_duty_ms;
+	int	lut_lo_index;
+	int	lut_hi_index;
+	int	lut_pause_hi;
+	int	lut_pause_lo;
+	int	flags;
+};
+
+static u16 duty_msec[PM8058_PWM_1KHZ_COUNT_MAX + 1] = {
+	0, 1, 2, 3, 4, 6, 8, 16, 18, 24, 32, 36, 64, 128, 256, 512
+};
+
+static u16 pause_count[PM8058_PWM_PAUSE_COUNT_MAX + 1] = {
+	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+	23, 28, 31, 42, 47, 56, 63, 83, 94, 111, 125, 167, 188, 222, 250, 333,
+	375, 500, 667, 750, 800, 900, 1000, 1100,
+	1200, 1300, 1400, 1500, 1600, 1800, 2000, 2500,
+	3000, 3500, 4000, 4500, 5000, 5500, 6000, 6500,
+	7000
+};
+
+/* Internal functions */
+static void pm8058_pwm_save(u8 *u8p, u8 mask, u8 val)
+{
+	*u8p &= ~mask;
+	*u8p |= val & mask;
+}
+
+static int pm8058_pwm_bank_enable(struct pwm_device *pwm, int enable)
+{
+	int	rc;
+	u8	reg;
+	struct pm8058_pwm_chip	*chip;
+
+	chip = pwm->chip;
+
+	if (enable)
+		reg = chip->bank_mask | (1 << pwm->pwm_id);
+	else
+		reg = chip->bank_mask & ~(1 << pwm->pwm_id);
+
+	rc = pm8xxx_writeb(pwm->dev->parent,
+				SSBI_REG_ADDR_LPG_BANK_EN, reg);
+	if (rc) {
+		pr_err("pm8xxx_write(): rc=%d (Enable LPG Bank)\n", rc);
+		goto bail_out;
+	}
+	chip->bank_mask = reg;
+
+bail_out:
+	return rc;
+}
+
+static int pm8058_pwm_bank_sel(struct pwm_device *pwm)
+{
+	int	rc;
+	u8	reg;
+
+	reg = pwm->pwm_id;
+	rc = pm8xxx_writeb(pwm->dev->parent,
+			SSBI_REG_ADDR_LPG_BANK_SEL, reg);
+	if (rc)
+		pr_err("pm8xxx_write(): rc=%d (Select PWM Bank)\n", rc);
+	return rc;
+}
+
+static int pm8058_pwm_start(struct pwm_device *pwm, int start, int ramp_start)
+{
+	int	rc;
+	u8	reg;
+
+	if (start) {
+		reg = pwm->pwm_ctl[0] | PM8058_PWM_PWM_START;
+		if (ramp_start)
+			reg |= PM8058_PWM_RAMP_GEN_START;
+		else
+			reg &= ~PM8058_PWM_RAMP_GEN_START;
+	} else {
+		reg = pwm->pwm_ctl[0] & ~PM8058_PWM_PWM_START;
+		reg &= ~PM8058_PWM_RAMP_GEN_START;
+	}
+
+	rc = pm8xxx_writeb(pwm->dev->parent, SSBI_REG_ADDR_LPG_CTL(0),
+									reg);
+	if (rc)
+		pr_err("pm8xxx_write(): rc=%d (Enable PWM Ctl 0)\n", rc);
+	else
+		pwm->pwm_ctl[0] = reg;
+	return rc;
+}
+
+static void pm8058_pwm_calc_period(unsigned int period_us,
+				   struct pm8058_pwm_period *period)
+{
+	int	n, m, clk, div;
+	int	best_m, best_div, best_clk;
+	int	last_err, cur_err, better_err, better_m;
+	unsigned int	tmp_p, last_p, min_err, period_n;
+
+	/* PWM Period / N : handle underflow or overflow */
+	if (period_us < (PM_PWM_PERIOD_MAX / NSEC_PER_USEC))
+		period_n = (period_us * NSEC_PER_USEC) >> 6;
+	else
+		period_n = (period_us >> 6) * NSEC_PER_USEC;
+	if (period_n >= MAX_MPT) {
+		n = 9;
+		period_n >>= 3;
+	} else
+		n = 6;
+
+	min_err = MAX_MPT;
+	best_m = 0;
+	best_clk = 0;
+	best_div = 0;
+	for (clk = 0; clk < NUM_CLOCKS; clk++) {
+		for (div = 0; div < NUM_PRE_DIVIDE; div++) {
+			tmp_p = period_n;
+			last_p = tmp_p;
+			for (m = 0; m <= PM8058_PWM_M_MAX; m++) {
+				if (tmp_p <= pt_t[div][clk]) {
+					/* Found local best */
+					if (!m) {
+						better_err = pt_t[div][clk] -
+							tmp_p;
+						better_m = m;
+					} else {
+						last_err = last_p -
+							pt_t[div][clk];
+						cur_err = pt_t[div][clk] -
+							tmp_p;
+
+						if (cur_err < last_err) {
+							better_err = cur_err;
+							better_m = m;
+						} else {
+							better_err = last_err;
+							better_m = m - 1;
+						}
+					}
+
+					if (better_err < min_err) {
+						min_err = better_err;
+						best_m = better_m;
+						best_clk = clk;
+						best_div = div;
+					}
+					break;
+				} else {
+					last_p = tmp_p;
+					tmp_p >>= 1;
+				}
+			}
+		}
+	}
+
+	/* Use higher resolution */
+	if (best_m >= 3 && n == 6) {
+		n += 3;
+		best_m -= 3;
+	}
+
+	period->pwm_size = n;
+	period->clk = best_clk;
+	period->pre_div = best_div;
+	period->pre_div_exp = best_m;
+
+	pr_debug("period=%u: n=%d, m=%d, clk[%d]=%s, div[%d]=%d\n",
+		 (unsigned)period_us, n, best_m,
+		 best_clk, clks[best_clk], best_div, pre_div[best_div]);
+}
+
+static void pm8058_pwm_calc_pwm_value(struct pwm_device *pwm,
+				      unsigned int period_us,
+				      unsigned int duty_us)
+{
+	unsigned int max_pwm_value, tmp;
+
+	/* Figure out pwm_value with overflow handling */
+	tmp = 1 << (sizeof(tmp) * 8 - pwm->period.pwm_size);
+	if (duty_us < tmp) {
+		tmp = duty_us << pwm->period.pwm_size;
+		pwm->pwm_value = tmp / period_us;
+	} else {
+		tmp = period_us >> pwm->period.pwm_size;
+		pwm->pwm_value = duty_us / tmp;
+	}
+	max_pwm_value = (1 << pwm->period.pwm_size) - 1;
+	if (pwm->pwm_value > max_pwm_value)
+		pwm->pwm_value = max_pwm_value;
+}
+
+static int pm8058_pwm_change_table(struct pwm_device *pwm, int duty_pct[],
+				   int start_idx, int len, int raw_value)
+{
+	unsigned int pwm_value, max_pwm_value;
+	u8	cfg0, cfg1;
+	int	i, pwm_size;
+	int	rc = 0;
+
+	pwm_size = (pwm->pwm_ctl[5] & PM8058_PWM_SIZE_9_BIT) ? 9 : 6;
+	max_pwm_value = (1 << pwm_size) - 1;
+	for (i = 0; i < len; i++) {
+		if (raw_value)
+			pwm_value = duty_pct[i];
+		else
+			pwm_value = (duty_pct[i] << pwm_size) / 100;
+
+		if (pwm_value > max_pwm_value)
+			pwm_value = max_pwm_value;
+		cfg0 = pwm_value;
+		cfg1 = (pwm_value >> 1) & 0x80;
+		cfg1 |= start_idx + i;
+
+		rc = pm8xxx_writeb(pwm->dev->parent,
+			     SSBI_REG_ADDR_LPG_LUT_CFG0, cfg0);
+		if (rc)
+			break;
+
+		rc = pm8xxx_writeb(pwm->dev->parent,
+			     SSBI_REG_ADDR_LPG_LUT_CFG1, cfg1);
+		if (rc)
+			break;
+	}
+	return rc;
+}
+
+static void pm8058_pwm_save_index(struct pwm_device *pwm,
+				   int low_idx, int high_idx, int flags)
+{
+	pwm->pwm_ctl[1] = high_idx & PM8058_PWM_HIGH_INDEX_MASK;
+	pwm->pwm_ctl[2] = low_idx & PM8058_PWM_LOW_INDEX_MASK;
+
+	if (flags & PM_PWM_LUT_REVERSE)
+		pwm->pwm_ctl[1] |= PM8058_PWM_REVERSE_EN;
+	if (flags & PM_PWM_LUT_RAMP_UP)
+		pwm->pwm_ctl[2] |= PM8058_PWM_RAMP_UP;
+	if (flags & PM_PWM_LUT_LOOP)
+		pwm->pwm_ctl[2] |= PM8058_PWM_LOOP_EN;
+}
+
+static void pm8058_pwm_save_period(struct pwm_device *pwm)
+{
+	u8	mask, val;
+
+	val = ((pwm->period.clk + 1) << PM8058_PWM_CLK_SEL_SHIFT)
+		& PM8058_PWM_CLK_SEL_MASK;
+	val |= (pwm->period.pre_div << PM8058_PWM_PREDIVIDE_SHIFT)
+		& PM8058_PWM_PREDIVIDE_MASK;
+	val |= pwm->period.pre_div_exp & PM8058_PWM_M_MASK;
+	mask = PM8058_PWM_CLK_SEL_MASK | PM8058_PWM_PREDIVIDE_MASK |
+		PM8058_PWM_M_MASK;
+	pm8058_pwm_save(&pwm->pwm_ctl[4], mask, val);
+
+	val = (pwm->period.pwm_size > 6) ? PM8058_PWM_SIZE_9_BIT : 0;
+	mask = PM8058_PWM_SIZE_9_BIT;
+	pm8058_pwm_save(&pwm->pwm_ctl[5], mask, val);
+}
+
+static void pm8058_pwm_save_pwm_value(struct pwm_device *pwm)
+{
+	u8	mask, val;
+
+	pwm->pwm_ctl[3] = pwm->pwm_value;
+
+	val = (pwm->period.pwm_size > 6) ? (pwm->pwm_value >> 1) : 0;
+	mask = PM8058_PWM_VALUE_BIT8;
+	pm8058_pwm_save(&pwm->pwm_ctl[4], mask, val);
+}
+
+static void pm8058_pwm_save_duty_time(struct pwm_device *pwm,
+				      struct pm8058_pwm_lut *lut)
+{
+	int	i;
+	u8	mask, val;
+
+	/* Linear search for duty time */
+	for (i = 0; i < PM8058_PWM_1KHZ_COUNT_MAX; i++) {
+		if (duty_msec[i] >= lut->lut_duty_ms)
+			break;
+	}
+	val = i << PM8058_PWM_1KHZ_COUNT_SHIFT;
+
+	mask = PM8058_PWM_1KHZ_COUNT_MASK;
+	pm8058_pwm_save(&pwm->pwm_ctl[0], mask, val);
+}
+
+static void pm8058_pwm_save_pause(struct pwm_device *pwm,
+				  struct pm8058_pwm_lut *lut)
+{
+	int	i, pause_cnt, time_cnt;
+	u8	mask, val;
+
+	time_cnt = (pwm->pwm_ctl[0] & PM8058_PWM_1KHZ_COUNT_MASK)
+				>> PM8058_PWM_1KHZ_COUNT_SHIFT;
+	if (lut->flags & PM_PWM_LUT_PAUSE_HI_EN) {
+		pause_cnt = (lut->lut_pause_hi + duty_msec[time_cnt] / 2)
+				/ duty_msec[time_cnt];
+		/* Linear search for pause time */
+		for (i = 0; i < PM8058_PWM_PAUSE_COUNT_MAX; i++) {
+			if (pause_count[i] >= pause_cnt)
+				break;
+		}
+		val = (i << PM8058_PWM_PAUSE_COUNT_HI_SHIFT) &
+			PM8058_PWM_PAUSE_COUNT_HI_MASK;
+		val |= PM8058_PWM_PAUSE_ENABLE_HIGH;
+	} else
+		val = 0;
+
+	mask = PM8058_PWM_PAUSE_COUNT_HI_MASK | PM8058_PWM_PAUSE_ENABLE_HIGH;
+	pm8058_pwm_save(&pwm->pwm_ctl[5], mask, val);
+
+	if (lut->flags & PM_PWM_LUT_PAUSE_LO_EN) {
+		/* Linear search for pause time */
+		pause_cnt = (lut->lut_pause_lo + duty_msec[time_cnt] / 2)
+				/ duty_msec[time_cnt];
+		for (i = 0; i < PM8058_PWM_PAUSE_COUNT_MAX; i++) {
+			if (pause_count[i] >= pause_cnt)
+				break;
+		}
+		val = (i << PM8058_PWM_PAUSE_COUNT_LO_SHIFT) &
+			PM8058_PWM_PAUSE_COUNT_LO_MASK;
+		val |= PM8058_PWM_PAUSE_ENABLE_LOW;
+	} else
+		val = 0;
+
+	mask = PM8058_PWM_PAUSE_COUNT_LO_MASK | PM8058_PWM_PAUSE_ENABLE_LOW;
+	pm8058_pwm_save(&pwm->pwm_ctl[6], mask, val);
+}
+
+static int pm8058_pwm_write(struct pwm_device *pwm, int start, int end)
+{
+	int	i, rc;
+
+	/* Write in reverse way so 0 would be the last */
+	for (i = end - 1; i >= start; i--) {
+		rc = pm8xxx_writeb(pwm->dev->parent,
+				  SSBI_REG_ADDR_LPG_CTL(i),
+				  pwm->pwm_ctl[i]);
+		if (rc) {
+			pr_err("pm8xxx_write(): rc=%d (PWM Ctl[%d])\n", rc, i);
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+static int pm8058_pwm_change_lut(struct pwm_device *pwm,
+				 struct pm8058_pwm_lut *lut)
+{
+	int	rc;
+
+	pm8058_pwm_save_index(pwm, lut->lut_lo_index,
+			     lut->lut_hi_index, lut->flags);
+	pm8058_pwm_save_duty_time(pwm, lut);
+	pm8058_pwm_save_pause(pwm, lut);
+	pm8058_pwm_save(&pwm->pwm_ctl[1], PM8058_PWM_BYPASS_LUT, 0);
+
+	pm8058_pwm_bank_sel(pwm);
+	rc = pm8058_pwm_write(pwm, 0, 7);
+
+	return rc;
+}
+
+/* APIs */
+/*
+ * pwm_request - request a PWM device
+ */
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+	struct pwm_device	*pwm;
+
+	if (pwm_id > PM8058_PWM_CHANNELS || pwm_id < 0)
+		return ERR_PTR(-EINVAL);
+	if (pwm_chip == NULL)
+		return ERR_PTR(-ENODEV);
+
+	mutex_lock(&pwm_chip->pwm_mutex);
+	pwm = &pwm_chip->pwm_dev[pwm_id];
+	if (!pwm->in_use) {
+		pwm->in_use = 1;
+		pwm->label = label;
+		pwm->use_lut = 0;
+
+		if (pwm_chip->pdata && pwm_chip->pdata->config)
+			pwm_chip->pdata->config(pwm, pwm_id, 1);
+	} else
+		pwm = ERR_PTR(-EBUSY);
+	mutex_unlock(&pwm_chip->pwm_mutex);
+
+	return pwm;
+}
+EXPORT_SYMBOL(pwm_request);
+
+/*
+ * pwm_free - free a PWM device
+ */
+void pwm_free(struct pwm_device *pwm)
+{
+	if (pwm == NULL || IS_ERR(pwm) || pwm->chip == NULL)
+		return;
+
+	mutex_lock(&pwm->chip->pwm_mutex);
+	if (pwm->in_use) {
+		pm8058_pwm_bank_sel(pwm);
+		pm8058_pwm_start(pwm, 0, 0);
+
+		if (pwm->chip->pdata && pwm->chip->pdata->config)
+			pwm->chip->pdata->config(pwm, pwm->pwm_id, 0);
+
+		pwm->in_use = 0;
+		pwm->label = NULL;
+	}
+	pm8058_pwm_bank_enable(pwm, 0);
+	mutex_unlock(&pwm->chip->pwm_mutex);
+}
+EXPORT_SYMBOL(pwm_free);
+
+/*
+ * pwm_config - change a PWM device configuration
+ *
+ * @pwm: the PWM device
+ * @period_us: period in micro second
+ * @duty_us: duty cycle in micro second
+ */
+int pwm_config(struct pwm_device *pwm, int duty_us, int period_us)
+{
+	int				rc;
+
+	if (pwm == NULL || IS_ERR(pwm) ||
+		duty_us > period_us ||
+		(unsigned)period_us > PM_PWM_PERIOD_MAX ||
+		(unsigned)period_us < PM_PWM_PERIOD_MIN)
+		return -EINVAL;
+	if (pwm->chip == NULL)
+		return -ENODEV;
+
+	mutex_lock(&pwm->chip->pwm_mutex);
+
+	if (!pwm->in_use) {
+		rc = -EINVAL;
+		goto out_unlock;
+	}
+
+	if (pwm->pwm_period != period_us) {
+		pm8058_pwm_calc_period(period_us, &pwm->period);
+		pm8058_pwm_save_period(pwm);
+		pwm->pwm_period = period_us;
+	}
+
+	pm8058_pwm_calc_pwm_value(pwm, period_us, duty_us);
+	pm8058_pwm_save_pwm_value(pwm);
+	pm8058_pwm_save(&pwm->pwm_ctl[1],
+			PM8058_PWM_BYPASS_LUT, PM8058_PWM_BYPASS_LUT);
+
+	pm8058_pwm_bank_sel(pwm);
+	rc = pm8058_pwm_write(pwm, 1, 6);
+
+	pr_debug("duty/period=%u/%u usec: pwm_value=%d (of %d)\n",
+		 (unsigned)duty_us, (unsigned)period_us,
+		 pwm->pwm_value, 1 << pwm->period.pwm_size);
+
+out_unlock:
+	mutex_unlock(&pwm->chip->pwm_mutex);
+	return rc;
+}
+EXPORT_SYMBOL(pwm_config);
+
+/*
+ * pwm_enable - start a PWM output toggling
+ */
+int pwm_enable(struct pwm_device *pwm)
+{
+	int	rc;
+
+	if (pwm == NULL || IS_ERR(pwm))
+		return -EINVAL;
+	if (pwm->chip == NULL)
+		return -ENODEV;
+
+	mutex_lock(&pwm->chip->pwm_mutex);
+	if (!pwm->in_use)
+		rc = -EINVAL;
+	else {
+		if (pwm->chip->pdata && pwm->chip->pdata->enable)
+			pwm->chip->pdata->enable(pwm, pwm->pwm_id, 1);
+
+		rc = pm8058_pwm_bank_enable(pwm, 1);
+
+		pm8058_pwm_bank_sel(pwm);
+		pm8058_pwm_start(pwm, 1, 0);
+	}
+	mutex_unlock(&pwm->chip->pwm_mutex);
+	return rc;
+}
+EXPORT_SYMBOL(pwm_enable);
+
+/*
+ * pwm_disable - stop a PWM output toggling
+ */
+void pwm_disable(struct pwm_device *pwm)
+{
+	if (pwm == NULL || IS_ERR(pwm) || pwm->chip == NULL)
+		return;
+
+	mutex_lock(&pwm->chip->pwm_mutex);
+	if (pwm->in_use) {
+		pm8058_pwm_bank_sel(pwm);
+		pm8058_pwm_start(pwm, 0, 0);
+
+		pm8058_pwm_bank_enable(pwm, 0);
+
+		if (pwm->chip->pdata && pwm->chip->pdata->enable)
+			pwm->chip->pdata->enable(pwm, pwm->pwm_id, 0);
+	}
+	mutex_unlock(&pwm->chip->pwm_mutex);
+}
+EXPORT_SYMBOL(pwm_disable);
+
+/**
+ * pm8058_pwm_config_period - change PWM period
+ *
+ * @pwm: the PWM device
+ * @pwm_p: period in struct pm8058_pwm_period
+ */
+int pm8058_pwm_config_period(struct pwm_device *pwm,
+			     struct pm8058_pwm_period *period)
+{
+	int			rc;
+
+	if (pwm == NULL || IS_ERR(pwm) || period == NULL)
+		return -EINVAL;
+	if (pwm->chip == NULL)
+		return -ENODEV;
+
+	mutex_lock(&pwm->chip->pwm_mutex);
+
+	if (!pwm->in_use) {
+		rc = -EINVAL;
+		goto out_unlock;
+	}
+
+	pwm->period.pwm_size = period->pwm_size;
+	pwm->period.clk = period->clk;
+	pwm->period.pre_div = period->pre_div;
+	pwm->period.pre_div_exp = period->pre_div_exp;
+
+	pm8058_pwm_save_period(pwm);
+	pm8058_pwm_bank_sel(pwm);
+	rc = pm8058_pwm_write(pwm, 4, 6);
+
+out_unlock:
+	mutex_unlock(&pwm->chip->pwm_mutex);
+	return rc;
+}
+EXPORT_SYMBOL(pm8058_pwm_config_period);
+
+/**
+ * pm8058_pwm_config_duty_cycle - change PWM duty cycle
+ *
+ * @pwm: the PWM device
+ * @pwm_value: the duty cycle in raw PWM value (< 2^pwm_size)
+ */
+int pm8058_pwm_config_duty_cycle(struct pwm_device *pwm, int pwm_value)
+{
+	struct pm8058_pwm_lut	lut;
+	int		flags, start_idx;
+	int		rc = 0;
+
+	if (pwm == NULL || IS_ERR(pwm))
+		return -EINVAL;
+	if (pwm->chip == NULL)
+		return -ENODEV;
+
+	mutex_lock(&pwm->chip->pwm_mutex);
+
+	if (!pwm->in_use || !pwm->pwm_period) {
+		rc = -EINVAL;
+		goto out_unlock;
+	}
+
+	if (pwm->pwm_value == pwm_value)
+		goto out_unlock;
+
+	pwm->pwm_value = pwm_value;
+	flags = PM_PWM_LUT_RAMP_UP;
+
+	start_idx = pwm->pwm_id * CHAN_LUT_SIZE;
+	pm8058_pwm_change_table(pwm, &pwm_value, start_idx, 1, 1);
+
+	if (!pwm->use_lut) {
+		pwm->use_lut = 1;
+
+		lut.lut_duty_ms = 1;
+		lut.lut_lo_index = start_idx;
+		lut.lut_hi_index = start_idx;
+		lut.lut_pause_lo = 0;
+		lut.lut_pause_hi = 0;
+		lut.flags = flags;
+
+		rc = pm8058_pwm_change_lut(pwm, &lut);
+	} else {
+		pm8058_pwm_save_index(pwm, start_idx, start_idx, flags);
+		pm8058_pwm_save(&pwm->pwm_ctl[1], PM8058_PWM_BYPASS_LUT, 0);
+
+		pm8058_pwm_bank_sel(pwm);
+		rc = pm8058_pwm_write(pwm, 0, 3);
+	}
+
+	if (rc)
+		pr_err("[%d]: pm8058_pwm_write: rc=%d\n", pwm->pwm_id, rc);
+
+out_unlock:
+	mutex_unlock(&pwm->chip->pwm_mutex);
+	return rc;
+}
+EXPORT_SYMBOL(pm8058_pwm_config_duty_cycle);
+
+/**
+ * pm8058_pwm_lut_config - change a PWM device configuration to use LUT
+ *
+ * @pwm: the PWM device
+ * @period_us: period in micro second
+ * @duty_pct: arrary of duty cycles in percent, like 20, 50.
+ * @duty_time_ms: time for each duty cycle in millisecond
+ * @start_idx: start index in lookup table from 0 to MAX-1
+ * @idx_len: number of index
+ * @pause_lo: pause time in millisecond at low index
+ * @pause_hi: pause time in millisecond at high index
+ * @flags: control flags
+ */
+int pm8058_pwm_lut_config(struct pwm_device *pwm, int period_us,
+			  int duty_pct[], int duty_time_ms, int start_idx,
+			  int idx_len, int pause_lo, int pause_hi, int flags)
+{
+	struct pm8058_pwm_lut		lut;
+	int	len;
+	int	rc;
+
+	if (pwm == NULL || IS_ERR(pwm) || !idx_len)
+		return -EINVAL;
+	if (duty_pct == NULL && !(flags & PM_PWM_LUT_NO_TABLE))
+		return -EINVAL;
+	if (pwm->chip == NULL)
+		return -ENODEV;
+	if (idx_len >= PM_PWM_LUT_SIZE && start_idx)
+		return -EINVAL;
+	if ((start_idx + idx_len) > PM_PWM_LUT_SIZE)
+		return -EINVAL;
+	if ((unsigned)period_us > PM_PWM_PERIOD_MAX ||
+		(unsigned)period_us < PM_PWM_PERIOD_MIN)
+		return -EINVAL;
+
+	mutex_lock(&pwm->chip->pwm_mutex);
+
+	if (!pwm->in_use) {
+		rc = -EINVAL;
+		goto out_unlock;
+	}
+
+	if (pwm->pwm_period != period_us) {
+		pm8058_pwm_calc_period(period_us, &pwm->period);
+		pm8058_pwm_save_period(pwm);
+		pwm->pwm_period = period_us;
+	}
+
+	len = (idx_len > PM_PWM_LUT_SIZE) ? PM_PWM_LUT_SIZE : idx_len;
+
+	if (flags & PM_PWM_LUT_NO_TABLE)
+		goto after_table_write;
+
+	rc = pm8058_pwm_change_table(pwm, duty_pct, start_idx, len, 0);
+	if (rc) {
+		pr_err("pm8058_pwm_change_table: rc=%d\n", rc);
+		goto out_unlock;
+	}
+
+after_table_write:
+	lut.lut_duty_ms = duty_time_ms;
+	lut.lut_lo_index = start_idx;
+	lut.lut_hi_index = start_idx + len - 1;
+	lut.lut_pause_lo = pause_lo;
+	lut.lut_pause_hi = pause_hi;
+	lut.flags = flags;
+
+	rc = pm8058_pwm_change_lut(pwm, &lut);
+
+out_unlock:
+	mutex_unlock(&pwm->chip->pwm_mutex);
+	return rc;
+}
+EXPORT_SYMBOL(pm8058_pwm_lut_config);
+
+/**
+ * pm8058_pwm_lut_enable - control a PWM device to start/stop LUT ramp
+ *
+ * @pwm: the PWM device
+ * @start: to start (1), or stop (0)
+ */
+int pm8058_pwm_lut_enable(struct pwm_device *pwm, int start)
+{
+	if (pwm == NULL || IS_ERR(pwm))
+		return -EINVAL;
+	if (pwm->chip == NULL)
+		return -ENODEV;
+
+	mutex_lock(&pwm->chip->pwm_mutex);
+	if (start) {
+		pm8058_pwm_bank_enable(pwm, 1);
+
+		pm8058_pwm_bank_sel(pwm);
+		pm8058_pwm_start(pwm, 1, 1);
+	} else {
+		pm8058_pwm_bank_sel(pwm);
+		pm8058_pwm_start(pwm, 0, 0);
+
+		pm8058_pwm_bank_enable(pwm, 0);
+	}
+	mutex_unlock(&pwm->chip->pwm_mutex);
+	return 0;
+}
+EXPORT_SYMBOL(pm8058_pwm_lut_enable);
+
+#define SSBI_REG_ADDR_LED_BASE		0x131
+#define SSBI_REG_ADDR_LED(n)		(SSBI_REG_ADDR_LED_BASE + (n))
+#define SSBI_REG_ADDR_FLASH_BASE	0x48
+#define SSBI_REG_ADDR_FLASH_DRV_1	0xFB
+#define SSBI_REG_ADDR_FLASH(n)		(((n) < 2 ? \
+					    SSBI_REG_ADDR_FLASH_BASE + (n) : \
+					    SSBI_REG_ADDR_FLASH_DRV_1))
+
+#define PM8058_LED_CURRENT_SHIFT	3
+#define PM8058_LED_MODE_MASK		0x07
+
+#define PM8058_FLASH_CURRENT_SHIFT	4
+#define PM8058_FLASH_MODE_MASK		0x03
+#define PM8058_FLASH_MODE_NONE		0
+#define PM8058_FLASH_MODE_DTEST1	1
+#define PM8058_FLASH_MODE_DTEST2	2
+#define PM8058_FLASH_MODE_PWM		3
+
+int pm8058_pwm_config_led(struct pwm_device *pwm, int id,
+			  int mode, int max_current)
+{
+	int	rc;
+	u8	conf;
+
+	switch (id) {
+	case PM_PWM_LED_0:
+	case PM_PWM_LED_1:
+	case PM_PWM_LED_2:
+		conf = mode & PM8058_LED_MODE_MASK;
+		conf |= (max_current / 2) << PM8058_LED_CURRENT_SHIFT;
+		rc = pm8xxx_writeb(pwm->dev->parent,
+				  SSBI_REG_ADDR_LED(id), conf);
+		break;
+
+	case PM_PWM_LED_KPD:
+	case PM_PWM_LED_FLASH:
+	case PM_PWM_LED_FLASH1:
+		switch (mode) {
+		case PM_PWM_CONF_PWM1:
+		case PM_PWM_CONF_PWM2:
+		case PM_PWM_CONF_PWM3:
+			conf = PM8058_FLASH_MODE_PWM;
+			break;
+		case PM_PWM_CONF_DTEST1:
+			conf = PM8058_FLASH_MODE_DTEST1;
+			break;
+		case PM_PWM_CONF_DTEST2:
+			conf = PM8058_FLASH_MODE_DTEST2;
+			break;
+		default:
+			conf = PM8058_FLASH_MODE_NONE;
+			break;
+		}
+		conf |= (max_current / 20) << PM8058_FLASH_CURRENT_SHIFT;
+		id -= PM_PWM_LED_KPD;
+		rc = pm8xxx_writeb(pwm->dev->parent,
+				  SSBI_REG_ADDR_FLASH(id), conf);
+		break;
+	default:
+		rc = -EINVAL;
+		break;
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(pm8058_pwm_config_led);
+
+int pm8058_pwm_set_dtest(struct pwm_device *pwm, int enable)
+{
+	int	rc;
+	u8	reg;
+
+	if (pwm == NULL || IS_ERR(pwm))
+		return -EINVAL;
+	if (pwm->chip == NULL)
+		return -ENODEV;
+
+	if (!pwm->in_use)
+		rc = -EINVAL;
+	else {
+		reg = pwm->pwm_id & PM8058_PWM_DTEST_BANK_MASK;
+		if (enable)
+			/* Only Test 1 available */
+			reg |= (1 << PM8058_PWM_DTEST_SHIFT) &
+				PM8058_PWM_DTEST_MASK;
+		rc = pm8xxx_writeb(pwm->dev->parent,
+			SSBI_REG_ADDR_LPG_TEST, reg);
+		if (rc)
+			pr_err("pm8xxx_write(DTEST=0x%x): rc=%d\n", reg, rc);
+
+	}
+	return rc;
+}
+EXPORT_SYMBOL(pm8058_pwm_set_dtest);
+
+static int __devinit pmic8058_pwm_probe(struct platform_device *pdev)
+{
+	struct pm8058_pwm_chip	*chip;
+	int	i;
+
+	chip = kzalloc(sizeof *chip, GFP_KERNEL);
+	if (chip == NULL) {
+		pr_err("kzalloc() failed.\n");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < PM8058_PWM_CHANNELS; i++) {
+		chip->pwm_dev[i].pwm_id = i;
+		chip->pwm_dev[i].chip = chip;
+		chip->pwm_dev[i].dev = &pdev->dev;
+	}
+
+	mutex_init(&chip->pwm_mutex);
+
+	chip->pdata = pdev->dev.platform_data;
+	pwm_chip = chip;
+	platform_set_drvdata(pdev, chip);
+
+	pr_notice("OK\n");
+	return 0;
+}
+
+static int __devexit pmic8058_pwm_remove(struct platform_device *pdev)
+{
+	struct pm8058_pwm_chip	*chip = platform_get_drvdata(pdev);
+
+	platform_set_drvdata(pdev, NULL);
+	kfree(chip);
+	return 0;
+}
+
+static struct platform_driver pmic8058_pwm_driver = {
+	.probe		= pmic8058_pwm_probe,
+	.remove		= __devexit_p(pmic8058_pwm_remove),
+	.driver		= {
+		.name = "pm8058-pwm",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init pm8058_pwm_init(void)
+{
+	return platform_driver_register(&pmic8058_pwm_driver);
+}
+
+static void __exit pm8058_pwm_exit(void)
+{
+	platform_driver_unregister(&pmic8058_pwm_driver);
+}
+
+subsys_initcall(pm8058_pwm_init);
+module_exit(pm8058_pwm_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC8058 PWM driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pmic8058_pwm");
diff --git a/drivers/misc/pmic8058-xoadc.c b/drivers/misc/pmic8058-xoadc.c
new file mode 100644
index 0000000..3452672
--- /dev/null
+++ b/drivers/misc/pmic8058-xoadc.c
@@ -0,0 +1,800 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/msm_adc.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pmic8058.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/ratelimit.h>
+#include <linux/delay.h>
+#include <linux/wakelock.h>
+
+#include <mach/mpp.h>
+#include <mach/msm_xo.h>
+
+#define ADC_DRIVER_NAME			"pm8058-xoadc"
+
+#define MAX_QUEUE_LENGTH        0X15
+#define MAX_CHANNEL_PROPERTIES_QUEUE    0X7
+#define MAX_QUEUE_SLOT		0x1
+
+/* User Processor */
+#define ADC_ARB_USRP_CNTRL                      0x197
+	#define ADC_ARB_USRP_CNTRL_EN_ARB	BIT(0)
+	#define ADC_ARB_USRP_CNTRL_RSV1		BIT(1)
+	#define ADC_ARB_USRP_CNTRL_RSV2		BIT(2)
+	#define ADC_ARB_USRP_CNTRL_RSV3		BIT(3)
+	#define ADC_ARB_USRP_CNTRL_RSV4		BIT(4)
+	#define ADC_ARB_USRP_CNTRL_RSV5		BIT(5)
+	#define ADC_ARB_USRP_CNTRL_EOC		BIT(6)
+	#define ADC_ARB_USRP_CNTRL_REQ		BIT(7)
+
+#define ADC_ARB_USRP_AMUX_CNTRL         0x198
+#define ADC_ARB_USRP_ANA_PARAM          0x199
+#define ADC_ARB_USRP_DIG_PARAM          0x19A
+#define ADC_ARB_USRP_RSV                        0x19B
+
+#define ADC_ARB_USRP_DATA0                      0x19D
+#define ADC_ARB_USRP_DATA1                      0x19C
+
+struct pmic8058_adc {
+	struct device *dev;
+	struct xoadc_platform_data *pdata;
+	struct adc_properties *adc_prop;
+	struct xoadc_conv_state	conv[2];
+	int xoadc_queue_count;
+	int adc_irq;
+	struct linear_graph *adc_graph;
+	struct xoadc_conv_state *conv_slot_request;
+	struct xoadc_conv_state *conv_queue_list;
+	struct adc_conv_slot conv_queue_elements[MAX_QUEUE_LENGTH];
+	int xoadc_num;
+	struct msm_xo_voter *adc_voter;
+	struct wake_lock adc_wakelock;
+	/* flag to warn/bug if wakelocks are taken after suspend_noirq */
+	int msm_suspend_check;
+};
+
+static struct pmic8058_adc *pmic_adc[XOADC_PMIC_0 + 1];
+
+static bool xoadc_initialized, xoadc_calib_first_adc;
+
+DEFINE_RATELIMIT_STATE(pm8058_xoadc_msg_ratelimit,
+		DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST);
+
+static inline int pm8058_xoadc_can_print(void)
+{
+	return __ratelimit(&pm8058_xoadc_msg_ratelimit);
+}
+
+int32_t pm8058_xoadc_registered(void)
+{
+	return xoadc_initialized;
+}
+EXPORT_SYMBOL(pm8058_xoadc_registered);
+
+void pm8058_xoadc_restore_slot(uint32_t adc_instance,
+					struct adc_conv_slot *slot)
+{
+	struct pmic8058_adc *adc_pmic = pmic_adc[adc_instance];
+	struct xoadc_conv_state *slot_state = adc_pmic->conv_slot_request;
+
+	mutex_lock(&slot_state->list_lock);
+	list_add(&slot->list, &slot_state->slots);
+	mutex_unlock(&slot_state->list_lock);
+}
+EXPORT_SYMBOL(pm8058_xoadc_restore_slot);
+
+void pm8058_xoadc_slot_request(uint32_t adc_instance,
+					struct adc_conv_slot **slot)
+{
+	struct pmic8058_adc *adc_pmic = pmic_adc[adc_instance];
+	struct xoadc_conv_state *slot_state = adc_pmic->conv_slot_request;
+
+	mutex_lock(&slot_state->list_lock);
+
+	if (!list_empty(&slot_state->slots)) {
+		*slot = list_first_entry(&slot_state->slots,
+				struct adc_conv_slot, list);
+		list_del(&(*slot)->list);
+	} else
+		*slot = NULL;
+
+	mutex_unlock(&slot_state->list_lock);
+}
+EXPORT_SYMBOL(pm8058_xoadc_slot_request);
+
+static int32_t pm8058_xoadc_arb_cntrl(uint32_t arb_cntrl,
+				uint32_t adc_instance, uint32_t channel)
+{
+	struct pmic8058_adc *adc_pmic = pmic_adc[adc_instance];
+	int i, rc;
+	u8 data_arb_cntrl;
+
+	data_arb_cntrl = ADC_ARB_USRP_CNTRL_EOC |
+			ADC_ARB_USRP_CNTRL_RSV5 |
+			ADC_ARB_USRP_CNTRL_RSV4;
+
+	if (arb_cntrl) {
+		if (adc_pmic->msm_suspend_check)
+			pr_err("XOADC request being made after suspend irq "
+				 "with channel id:%d\n", channel);
+		data_arb_cntrl |= ADC_ARB_USRP_CNTRL_EN_ARB;
+		msm_xo_mode_vote(adc_pmic->adc_voter, MSM_XO_MODE_ON);
+		adc_pmic->pdata->xoadc_mpp_config();
+		wake_lock(&adc_pmic->adc_wakelock);
+	}
+
+	/* Write twice to the CNTRL register for the arbiter settings
+	   to take into effect */
+	for (i = 0; i < 2; i++) {
+		rc = pm8xxx_writeb(adc_pmic->dev->parent, ADC_ARB_USRP_CNTRL,
+							data_arb_cntrl);
+		if (rc < 0) {
+			pr_debug("%s: PM8058 write failed\n", __func__);
+			return rc;
+		}
+	}
+
+	if (!arb_cntrl) {
+		msm_xo_mode_vote(adc_pmic->adc_voter, MSM_XO_MODE_OFF);
+		wake_unlock(&adc_pmic->adc_wakelock);
+	}
+
+	return 0;
+}
+
+static int32_t pm8058_xoadc_configure(uint32_t adc_instance,
+					struct adc_conv_slot *slot)
+{
+
+	struct pmic8058_adc *adc_pmic = pmic_adc[adc_instance];
+	u8 data_arb_cntrl = 0, data_amux_chan = 0, data_arb_rsv = 0;
+	u8 data_dig_param = 0, data_ana_param2 = 0, data_ana_param = 0;
+	int rc;
+
+	rc = pm8058_xoadc_arb_cntrl(1, adc_instance, slot->chan_path);
+	if (rc < 0) {
+		pr_debug("%s: Configuring ADC Arbiter"
+				"enable failed\n", __func__);
+		return rc;
+	}
+
+	switch (slot->chan_path) {
+
+	case CHAN_PATH_TYPE1:
+		data_amux_chan = CHANNEL_VCOIN << 4;
+		data_arb_rsv = 0x20;
+		slot->chan_properties.gain_numerator = 1;
+		slot->chan_properties.gain_denominator = 2;
+		slot->chan_properties.adc_graph = &adc_pmic->adc_graph[0];
+		break;
+
+	case CHAN_PATH_TYPE2:
+		data_amux_chan = CHANNEL_VBAT << 4;
+		data_arb_rsv = 0x20;
+		slot->chan_properties.gain_numerator = 1;
+		slot->chan_properties.gain_denominator = 3;
+		slot->chan_properties.adc_graph = &adc_pmic->adc_graph[0];
+		break;
+
+	case CHAN_PATH_TYPE3:
+		data_amux_chan = CHANNEL_VCHG << 4;
+		data_arb_rsv = 0x20;
+		slot->chan_properties.gain_numerator = 1;
+		slot->chan_properties.gain_denominator = 10;
+		slot->chan_properties.adc_graph = &adc_pmic->adc_graph[0];
+		break;
+
+	case CHAN_PATH_TYPE4:
+		data_amux_chan = CHANNEL_CHG_MONITOR << 4;
+		data_arb_rsv = 0x20;
+		slot->chan_properties.gain_numerator = 1;
+		slot->chan_properties.gain_denominator = 1;
+		slot->chan_properties.adc_graph = &adc_pmic->adc_graph[0];
+		break;
+
+	case CHAN_PATH_TYPE5:
+		data_amux_chan = CHANNEL_VPH_PWR << 4;
+		data_arb_rsv = 0x20;
+		slot->chan_properties.gain_numerator = 1;
+		slot->chan_properties.gain_denominator = 3;
+		slot->chan_properties.adc_graph = &adc_pmic->adc_graph[0];
+		break;
+
+	case CHAN_PATH_TYPE6:
+		data_amux_chan = CHANNEL_MPP5 << 4;
+		data_arb_rsv = 0x20;
+		slot->chan_properties.gain_numerator = 1;
+		slot->chan_properties.gain_denominator = 1;
+		slot->chan_properties.adc_graph = &adc_pmic->adc_graph[1];
+		break;
+
+	case CHAN_PATH_TYPE7:
+		data_amux_chan = CHANNEL_MPP6 << 4;
+		data_arb_rsv = 0x20;
+		slot->chan_properties.gain_numerator = 1;
+		slot->chan_properties.gain_denominator = 1;
+		slot->chan_properties.adc_graph = &adc_pmic->adc_graph[0];
+		break;
+
+	case CHAN_PATH_TYPE8:
+		data_amux_chan = CHANNEL_MPP7 << 4;
+		data_arb_rsv = 0x20;
+		slot->chan_properties.gain_numerator = 1;
+		slot->chan_properties.gain_denominator = 2;
+		slot->chan_properties.adc_graph = &adc_pmic->adc_graph[0];
+		break;
+
+	case CHAN_PATH_TYPE9:
+		data_amux_chan = CHANNEL_MPP8 << 4;
+		data_arb_rsv = 0x20;
+		slot->chan_properties.gain_numerator = 1;
+		slot->chan_properties.gain_denominator = 2;
+		slot->chan_properties.adc_graph = &adc_pmic->adc_graph[0];
+		break;
+
+	case CHAN_PATH_TYPE10:
+		data_amux_chan = CHANNEL_MPP9 << 4;
+		data_arb_rsv = 0x20;
+		slot->chan_properties.gain_numerator = 1;
+		slot->chan_properties.gain_denominator = 3;
+		slot->chan_properties.adc_graph = &adc_pmic->adc_graph[0];
+		break;
+
+	case CHAN_PATH_TYPE11:
+		data_amux_chan = CHANNEL_USB_VBUS << 4;
+		data_arb_rsv = 0x20;
+		slot->chan_properties.gain_numerator = 1;
+		slot->chan_properties.gain_denominator = 3;
+		slot->chan_properties.adc_graph = &adc_pmic->adc_graph[0];
+		break;
+
+	case CHAN_PATH_TYPE12:
+		data_amux_chan = CHANNEL_DIE_TEMP << 4;
+		data_arb_rsv = 0x20;
+		slot->chan_properties.gain_numerator = 1;
+		slot->chan_properties.gain_denominator = 1;
+		slot->chan_properties.adc_graph = &adc_pmic->adc_graph[0];
+		break;
+
+	case CHAN_PATH_TYPE13:
+		data_amux_chan = CHANNEL_125V << 4;
+		data_arb_rsv = 0x20;
+		slot->chan_properties.gain_numerator = 1;
+		slot->chan_properties.gain_denominator = 1;
+		slot->chan_properties.adc_graph = &adc_pmic->adc_graph[0];
+		break;
+
+	case CHAN_PATH_TYPE14:
+		data_amux_chan = CHANNEL_INTERNAL_2 << 4;
+		data_arb_rsv = 0x20;
+		slot->chan_properties.gain_numerator = 1;
+		slot->chan_properties.gain_denominator = 1;
+		slot->chan_properties.adc_graph = &adc_pmic->adc_graph[0];
+		break;
+
+	case CHAN_PATH_TYPE_NONE:
+		data_amux_chan = CHANNEL_MUXOFF << 4;
+		data_arb_rsv = 0x10;
+		slot->chan_properties.gain_numerator = 1;
+		slot->chan_properties.gain_denominator = 1;
+		slot->chan_properties.adc_graph = &adc_pmic->adc_graph[1];
+		break;
+
+	case CHAN_PATH_TYPE15:
+		data_amux_chan = CHANNEL_INTERNAL << 4;
+		data_arb_rsv = 0x20;
+		slot->chan_properties.gain_numerator = 1;
+		slot->chan_properties.gain_denominator = 1;
+		slot->chan_properties.adc_graph = &adc_pmic->adc_graph[0];
+		break;
+	}
+
+	rc = pm8xxx_writeb(adc_pmic->dev->parent,
+			ADC_ARB_USRP_AMUX_CNTRL, data_amux_chan);
+	if (rc < 0) {
+		pr_debug("%s: PM8058 write failed\n", __func__);
+		return rc;
+	}
+
+	rc = pm8xxx_writeb(adc_pmic->dev->parent,
+			ADC_ARB_USRP_RSV, data_arb_rsv);
+	if (rc < 0) {
+		pr_debug("%s: PM8058 write failed\n", __func__);
+		return rc;
+	}
+
+	/* Set default clock rate to 2.4 MHz XO ADC clock digital */
+	switch (slot->chan_adc_config) {
+
+	case ADC_CONFIG_TYPE1:
+		data_ana_param = 0xFE;
+		data_dig_param = 0x23;
+		data_ana_param2 = 0xFF;
+		/* AMUX register data to start the ADC conversion */
+		data_arb_cntrl = 0xF1;
+		break;
+
+	case ADC_CONFIG_TYPE2:
+		data_ana_param = 0xFE;
+		data_dig_param = 0x03;
+		data_ana_param2 = 0xFF;
+		/* AMUX register data to start the ADC conversion */
+		data_arb_cntrl = 0xF1;
+		break;
+	}
+
+	rc = pm8xxx_writeb(adc_pmic->dev->parent,
+				ADC_ARB_USRP_ANA_PARAM, data_ana_param);
+	if (rc < 0) {
+		pr_debug("%s: PM8058 write failed\n", __func__);
+		return rc;
+	}
+
+	rc = pm8xxx_writeb(adc_pmic->dev->parent,
+			ADC_ARB_USRP_DIG_PARAM, data_dig_param);
+	if (rc < 0) {
+		pr_debug("%s: PM8058 write failed\n", __func__);
+		return rc;
+	}
+
+	rc = pm8xxx_writeb(adc_pmic->dev->parent,
+			ADC_ARB_USRP_ANA_PARAM, data_ana_param2);
+	if (rc < 0) {
+		pr_debug("%s: PM8058 write failed\n", __func__);
+		return rc;
+	}
+
+	enable_irq(adc_pmic->adc_irq);
+
+	rc = pm8xxx_writeb(adc_pmic->dev->parent,
+				ADC_ARB_USRP_CNTRL, data_arb_cntrl);
+	if (rc < 0) {
+		pr_debug("%s: PM8058 write failed\n", __func__);
+		return rc;
+	}
+
+	return 0;
+}
+
+int32_t pm8058_xoadc_select_chan_and_start_conv(uint32_t adc_instance,
+					struct adc_conv_slot *slot)
+{
+	struct pmic8058_adc *adc_pmic = pmic_adc[adc_instance];
+	struct xoadc_conv_state *slot_state = adc_pmic->conv_queue_list;
+
+	if (!xoadc_initialized)
+		return -ENODEV;
+
+	mutex_lock(&slot_state->list_lock);
+	list_add_tail(&slot->list, &slot_state->slots);
+	if (adc_pmic->xoadc_queue_count == 0) {
+		if (adc_pmic->pdata->xoadc_vreg_set != NULL)
+			adc_pmic->pdata->xoadc_vreg_set(1);
+		pm8058_xoadc_configure(adc_instance, slot);
+	}
+	adc_pmic->xoadc_queue_count++;
+	mutex_unlock(&slot_state->list_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(pm8058_xoadc_select_chan_and_start_conv);
+
+static int32_t pm8058_xoadc_dequeue_slot_request(uint32_t adc_instance,
+				struct adc_conv_slot **slot)
+{
+	struct pmic8058_adc *adc_pmic = pmic_adc[adc_instance];
+	struct xoadc_conv_state *slot_state = adc_pmic->conv_queue_list;
+	int rc = 0;
+
+	mutex_lock(&slot_state->list_lock);
+	if (adc_pmic->xoadc_queue_count > 0 &&
+			!list_empty(&slot_state->slots)) {
+		*slot = list_first_entry(&slot_state->slots,
+			struct adc_conv_slot, list);
+		list_del(&(*slot)->list);
+	} else
+		rc = -EINVAL;
+	mutex_unlock(&slot_state->list_lock);
+
+	if (rc < 0) {
+		if (pm8058_xoadc_can_print())
+			pr_err("Pmic 8058 xoadc spurious interrupt detected\n");
+		return rc;
+	}
+
+	return 0;
+}
+
+int32_t pm8058_xoadc_read_adc_code(uint32_t adc_instance, int32_t *data)
+{
+	struct pmic8058_adc *adc_pmic = pmic_adc[adc_instance];
+	struct xoadc_conv_state *slot_state = adc_pmic->conv_queue_list;
+	uint8_t rslt_lsb, rslt_msb;
+	struct adc_conv_slot *slot;
+	int32_t rc, max_ideal_adc_code = 1 << adc_pmic->adc_prop->bitresolution;
+
+	if (!xoadc_initialized)
+		return -ENODEV;
+
+	rc = pm8xxx_readb(adc_pmic->dev->parent, ADC_ARB_USRP_DATA0,
+							&rslt_lsb);
+	if (rc < 0) {
+		pr_debug("%s: PM8058 read failed\n", __func__);
+		return rc;
+	}
+
+	rc = pm8xxx_readb(adc_pmic->dev->parent, ADC_ARB_USRP_DATA1,
+							&rslt_msb);
+	if (rc < 0) {
+		pr_debug("%s: PM8058 read failed\n", __func__);
+		return rc;
+	}
+
+	*data = (rslt_msb << 8) | rslt_lsb;
+
+	/* Use the midpoint to determine underflow or overflow */
+	if (*data > max_ideal_adc_code + (max_ideal_adc_code >> 1))
+		*data |= ((1 << (8 * sizeof(*data) -
+			adc_pmic->adc_prop->bitresolution)) - 1) <<
+			adc_pmic->adc_prop->bitresolution;
+	/* Return if this is a calibration run since there
+	 * is no need to check requests in the waiting queue */
+	if (xoadc_calib_first_adc)
+		return 0;
+
+	mutex_lock(&slot_state->list_lock);
+	adc_pmic->xoadc_queue_count--;
+	if (adc_pmic->xoadc_queue_count > 0) {
+		slot = list_first_entry(&slot_state->slots,
+				struct adc_conv_slot, list);
+		pm8058_xoadc_configure(adc_instance, slot);
+	}
+	mutex_unlock(&slot_state->list_lock);
+
+	mutex_lock(&slot_state->list_lock);
+	/* Default value for switching off the arbiter after reading
+	   the ADC value. Bit 0 set to 0. */
+	if (adc_pmic->xoadc_queue_count == 0) {
+		rc = pm8058_xoadc_arb_cntrl(0, adc_instance, CHANNEL_MUXOFF);
+		if (rc < 0) {
+			pr_debug("%s: Configuring ADC Arbiter disable"
+						"failed\n", __func__);
+			return rc;
+		}
+		if (adc_pmic->pdata->xoadc_vreg_set != NULL)
+			adc_pmic->pdata->xoadc_vreg_set(0);
+	}
+	mutex_unlock(&slot_state->list_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(pm8058_xoadc_read_adc_code);
+
+static irqreturn_t pm8058_xoadc(int irq, void *dev_id)
+{
+	struct pmic8058_adc *xoadc_8058 = dev_id;
+	struct adc_conv_slot *slot = NULL;
+	int rc;
+
+	disable_irq_nosync(xoadc_8058->adc_irq);
+
+	if (xoadc_calib_first_adc)
+		return IRQ_HANDLED;
+
+	rc = pm8058_xoadc_dequeue_slot_request(xoadc_8058->xoadc_num, &slot);
+
+	if (rc < 0)
+		return IRQ_NONE;
+
+	if (rc == 0)
+		msm_adc_conv_cb(slot, 0, NULL, 0);
+
+	return IRQ_HANDLED;
+}
+
+struct adc_properties *pm8058_xoadc_get_properties(uint32_t dev_instance)
+{
+	struct pmic8058_adc *xoadc_8058 = pmic_adc[dev_instance];
+
+	return xoadc_8058->adc_prop;
+}
+EXPORT_SYMBOL(pm8058_xoadc_get_properties);
+
+int32_t pm8058_xoadc_calib_device(uint32_t adc_instance)
+{
+	struct pmic8058_adc *adc_pmic = pmic_adc[adc_instance];
+	struct adc_conv_slot *slot;
+	int rc, offset_xoadc, slope_xoadc, calib_read_1, calib_read_2;
+
+	if (adc_pmic->pdata->xoadc_vreg_set != NULL)
+		adc_pmic->pdata->xoadc_vreg_set(1);
+
+	pm8058_xoadc_slot_request(adc_instance, &slot);
+	if (slot) {
+		slot->chan_path = CHAN_PATH_TYPE13;
+		slot->chan_adc_config = ADC_CONFIG_TYPE2;
+		slot->chan_adc_calib = ADC_CONFIG_TYPE2;
+		xoadc_calib_first_adc = true;
+		rc = pm8058_xoadc_configure(adc_instance, slot);
+		if (rc) {
+			pr_err("pm8058_xoadc configure failed\n");
+			goto fail;
+		}
+	} else {
+		rc = -EINVAL;
+		goto fail;
+	}
+
+	msleep(3);
+
+	rc = pm8058_xoadc_read_adc_code(adc_instance, &calib_read_1);
+	if (rc) {
+		pr_err("pm8058_xoadc read adc failed\n");
+		xoadc_calib_first_adc = false;
+		goto fail;
+	}
+	xoadc_calib_first_adc = false;
+
+	pm8058_xoadc_slot_request(adc_instance, &slot);
+	if (slot) {
+		slot->chan_path = CHAN_PATH_TYPE15;
+		slot->chan_adc_config = ADC_CONFIG_TYPE2;
+		slot->chan_adc_calib = ADC_CONFIG_TYPE2;
+		xoadc_calib_first_adc = true;
+		rc = pm8058_xoadc_configure(adc_instance, slot);
+		if (rc) {
+			pr_err("pm8058_xoadc configure failed\n");
+			goto fail;
+		}
+	} else {
+		rc = -EINVAL;
+		goto fail;
+	}
+
+	msleep(3);
+
+	rc = pm8058_xoadc_read_adc_code(adc_instance, &calib_read_2);
+	if (rc) {
+		pr_err("pm8058_xoadc read adc failed\n");
+		xoadc_calib_first_adc = false;
+		goto fail;
+	}
+	xoadc_calib_first_adc = false;
+
+	pm8058_xoadc_restore_slot(adc_instance, slot);
+
+	slope_xoadc = (((calib_read_1 - calib_read_2) << 10)/
+					CHANNEL_ADC_625_MV);
+	offset_xoadc = calib_read_2 -
+			((slope_xoadc * CHANNEL_ADC_625_MV) >> 10);
+
+	printk(KERN_INFO"pmic8058_xoadc:The offset for AMUX calibration"
+						"was %d\n", offset_xoadc);
+
+	adc_pmic->adc_graph[0].offset = offset_xoadc;
+	adc_pmic->adc_graph[0].dy = (calib_read_1 - calib_read_2);
+	adc_pmic->adc_graph[0].dx = CHANNEL_ADC_625_MV;
+
+	/* Retain ideal calibration settings for therm readings */
+	adc_pmic->adc_graph[1].offset = 0 ;
+	adc_pmic->adc_graph[1].dy = (1 << 15) - 1;
+	adc_pmic->adc_graph[1].dx = 2200;
+
+	if (adc_pmic->pdata->xoadc_vreg_set != NULL)
+		adc_pmic->pdata->xoadc_vreg_set(0);
+
+	return 0;
+fail:
+	if (adc_pmic->pdata->xoadc_vreg_set != NULL)
+		adc_pmic->pdata->xoadc_vreg_set(0);
+
+	return rc;
+}
+EXPORT_SYMBOL(pm8058_xoadc_calib_device);
+
+int32_t pm8058_xoadc_calibrate(uint32_t dev_instance,
+				struct adc_conv_slot *slot, int *calib_status)
+{
+	*calib_status = CALIB_NOT_REQUIRED;
+
+	return 0;
+}
+EXPORT_SYMBOL(pm8058_xoadc_calibrate);
+
+#ifdef CONFIG_PM
+static int pm8058_xoadc_suspend_noirq(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct pmic8058_adc *adc_pmic = platform_get_drvdata(pdev);
+
+	adc_pmic->msm_suspend_check = 1;
+
+	return 0;
+}
+
+static int pm8058_xoadc_resume_noirq(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct pmic8058_adc *adc_pmic = platform_get_drvdata(pdev);
+
+	adc_pmic->msm_suspend_check = 0;
+
+	return 0;
+}
+
+static const struct dev_pm_ops pm8058_xoadc_dev_pm_ops = {
+	.suspend_noirq = pm8058_xoadc_suspend_noirq,
+	.resume_noirq = pm8058_xoadc_resume_noirq,
+};
+
+#define PM8058_XOADC_DEV_PM_OPS	(&pm8058_xoadc_dev_pm_ops)
+#else
+#define PM8058_XOADC_DEV_PM_OPS NULL
+#endif
+
+static int __devinit pm8058_xoadc_probe(struct platform_device *pdev)
+{
+	struct xoadc_platform_data *pdata = pdev->dev.platform_data;
+	struct pmic8058_adc *adc_pmic;
+	int i, rc = 0;
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "no platform data?\n");
+		return -EINVAL;
+	}
+
+	adc_pmic = devm_kzalloc(&pdev->dev, sizeof(*adc_pmic), GFP_KERNEL);
+	if (!adc_pmic) {
+		dev_err(&pdev->dev, "Unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	adc_pmic->dev = &pdev->dev;
+	adc_pmic->adc_prop = pdata->xoadc_prop;
+	adc_pmic->xoadc_num = pdata->xoadc_num;
+	adc_pmic->xoadc_queue_count = 0;
+
+	platform_set_drvdata(pdev, adc_pmic);
+
+	if (adc_pmic->xoadc_num > XOADC_PMIC_0) {
+		dev_err(&pdev->dev, "ADC device not supported\n");
+		return -EINVAL;
+	}
+
+	adc_pmic->pdata = pdata;
+	adc_pmic->adc_graph = devm_kzalloc(&pdev->dev,
+		sizeof(struct linear_graph) * MAX_CHANNEL_PROPERTIES_QUEUE,
+		GFP_KERNEL);
+	if (!adc_pmic->adc_graph) {
+		dev_err(&pdev->dev, "Unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	/* Will be replaced by individual channel calibration */
+	for (i = 0; i < MAX_CHANNEL_PROPERTIES_QUEUE; i++) {
+		adc_pmic->adc_graph[i].offset = 0 ;
+		adc_pmic->adc_graph[i].dy = (1 << 15) - 1;
+		adc_pmic->adc_graph[i].dx = 2200;
+	}
+
+	if (pdata->xoadc_mpp_config != NULL)
+		pdata->xoadc_mpp_config();
+
+	adc_pmic->conv_slot_request = &adc_pmic->conv[0];
+	adc_pmic->conv_slot_request->context =
+		&adc_pmic->conv_queue_elements[0];
+
+	mutex_init(&adc_pmic->conv_slot_request->list_lock);
+	INIT_LIST_HEAD(&adc_pmic->conv_slot_request->slots);
+
+	/* tie each slot and initwork them */
+	for (i = 0; i < MAX_QUEUE_LENGTH; i++) {
+		list_add(&adc_pmic->conv_slot_request->context[i].list,
+					&adc_pmic->conv_slot_request->slots);
+		INIT_WORK(&adc_pmic->conv_slot_request->context[i].work,
+							msm_adc_wq_work);
+		init_completion(&adc_pmic->conv_slot_request->context[i].comp);
+		adc_pmic->conv_slot_request->context[i].idx = i;
+	}
+
+	adc_pmic->conv_queue_list = &adc_pmic->conv[1];
+
+	mutex_init(&adc_pmic->conv_queue_list->list_lock);
+	INIT_LIST_HEAD(&adc_pmic->conv_queue_list->slots);
+
+	adc_pmic->adc_irq = platform_get_irq(pdev, 0);
+	if (adc_pmic->adc_irq < 0)
+		return -ENXIO;
+
+	rc = request_threaded_irq(adc_pmic->adc_irq,
+				NULL, pm8058_xoadc,
+		IRQF_TRIGGER_RISING, "pm8058_adc_interrupt", adc_pmic);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to request adc irq\n");
+		return rc;
+	}
+
+	disable_irq(adc_pmic->adc_irq);
+
+	if (adc_pmic->adc_voter == NULL) {
+		adc_pmic->adc_voter = msm_xo_get(MSM_XO_TCXO_D1,
+							"pmic8058_xoadc");
+		if (IS_ERR(adc_pmic->adc_voter)) {
+			dev_err(&pdev->dev, "Failed to get XO vote\n");
+			return PTR_ERR(adc_pmic->adc_voter);
+		}
+	}
+
+	device_init_wakeup(&pdev->dev, pdata->xoadc_wakeup);
+	wake_lock_init(&adc_pmic->adc_wakelock, WAKE_LOCK_SUSPEND,
+					"pmic8058_xoadc_wakelock");
+
+	pmic_adc[adc_pmic->xoadc_num] = adc_pmic;
+
+	if (pdata->xoadc_vreg_setup != NULL)
+		pdata->xoadc_vreg_setup();
+
+	xoadc_initialized = true;
+	xoadc_calib_first_adc = false;
+
+	return 0;
+}
+
+static int __devexit pm8058_xoadc_teardown(struct platform_device *pdev)
+{
+	struct pmic8058_adc *adc_pmic = platform_get_drvdata(pdev);
+
+	if (adc_pmic->pdata->xoadc_vreg_shutdown != NULL)
+		adc_pmic->pdata->xoadc_vreg_shutdown();
+
+	wake_lock_destroy(&adc_pmic->adc_wakelock);
+	msm_xo_put(adc_pmic->adc_voter);
+	device_init_wakeup(&pdev->dev, 0);
+	xoadc_initialized = false;
+
+	return 0;
+}
+
+static struct platform_driver pm8058_xoadc_driver = {
+	.probe = pm8058_xoadc_probe,
+	.remove = __devexit_p(pm8058_xoadc_teardown),
+	.driver = {
+		.name = "pm8058-xoadc",
+		.owner = THIS_MODULE,
+		.pm = PM8058_XOADC_DEV_PM_OPS,
+	},
+};
+
+static int __init pm8058_xoadc_init(void)
+{
+	return platform_driver_register(&pm8058_xoadc_driver);
+}
+module_init(pm8058_xoadc_init);
+
+static void __exit pm8058_xoadc_exit(void)
+{
+	platform_driver_unregister(&pm8058_xoadc_driver);
+}
+module_exit(pm8058_xoadc_exit);
+
+MODULE_ALIAS("platform:pmic8058_xoadc");
+MODULE_DESCRIPTION("PMIC8058 XOADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/qfp_fuse.c b/drivers/misc/qfp_fuse.c
new file mode 100644
index 0000000..341e5b2
--- /dev/null
+++ b/drivers/misc/qfp_fuse.c
@@ -0,0 +1,410 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/miscdevice.h>
+#include <linux/delay.h>
+#include <linux/qfp_fuse.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/regulator/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+
+/*
+ * Time QFPROM requires to reliably burn a fuse.
+ */
+#define QFPROM_BLOW_TIMEOUT_US      10
+#define QFPROM_BLOW_TIMER_OFFSET    0x2038
+/*
+ * Denotes number of cycles required to blow the fuse.
+ */
+#define QFPROM_BLOW_TIMER_VALUE     (QFPROM_BLOW_TIMEOUT_US * 83)
+
+#define QFPROM_BLOW_STATUS_OFFSET   0x204C
+#define QFPROM_BLOW_STATUS_BUSY     0x01
+#define QFPROM_BLOW_STATUS_ERROR    0x02
+
+#define QFP_FUSE_READY              0x01
+#define QFP_FUSE_OFF                0x00
+
+struct qfp_priv_t {
+	uint32_t base;
+	uint32_t end;
+	struct mutex lock;
+	struct regulator *fuse_vdd;
+	u8 state;
+};
+
+/* We need only one instance of this for the driver */
+static struct qfp_priv_t *qfp_priv;
+
+
+static int qfp_fuse_open(struct inode *inode, struct file *filp)
+{
+	if (qfp_priv == NULL)
+		return -ENODEV;
+
+	filp->private_data = qfp_priv;
+
+	return 0;
+}
+
+static int qfp_fuse_release(struct inode *inode, struct file *filp)
+{
+
+	filp->private_data = NULL;
+
+	return 0;
+}
+
+static inline int qfp_fuse_wait_for_fuse_blow(u32 *status)
+{
+	u32 timeout = QFPROM_BLOW_TIMEOUT_US;
+	/* wait for 400us before checking for the first time */
+	udelay(400);
+	do {
+		*status = readl_relaxed(
+			qfp_priv->base + QFPROM_BLOW_STATUS_OFFSET);
+
+		if (!(*status & QFPROM_BLOW_STATUS_BUSY))
+			return 0;
+
+		timeout--;
+		udelay(1);
+	} while (timeout);
+	pr_err("Timeout waiting for FUSE blow, status = %x\n", *status);
+	return -ETIMEDOUT;
+}
+
+static inline int qfp_fuse_enable_regulator(void)
+{
+	int err;
+	err = regulator_enable(qfp_priv->fuse_vdd);
+	if (err != 0)
+		pr_err("Error (%d) enabling regulator\n", err);
+	return err;
+}
+
+static inline int qfp_fuse_disable_regulator(void)
+{
+	int err;
+	err = regulator_disable(qfp_priv->fuse_vdd);
+	if (err != 0)
+		pr_err("Error (%d) disabling regulator\n", err);
+	return err;
+}
+
+static int qfp_fuse_write_word(u32 *addr, u32 data)
+{
+	u32 blow_status = 0;
+	u32 read_data;
+	int err;
+
+	/* Set QFPROM  blow timer register */
+	writel_relaxed(QFPROM_BLOW_TIMER_VALUE,
+			qfp_priv->base + QFPROM_BLOW_TIMER_OFFSET);
+	mb();
+
+	/* Enable LVS0 regulator */
+	err = qfp_fuse_enable_regulator();
+	if (err != 0)
+		return err;
+
+	/*
+	 * Wait for about 1ms. However msleep(1) can sleep for
+	 * up to 20ms as per Documentation/timers/timers-howto.txt.
+	 * Time is not a constraint here.
+	 */
+
+	msleep(20);
+
+	/* Write data */
+	__raw_writel(data, addr);
+	mb();
+
+	/* blow_status = QFPROM_BLOW_STATUS_BUSY; */
+	err = qfp_fuse_wait_for_fuse_blow(&blow_status);
+	if (err) {
+		qfp_fuse_disable_regulator();
+		return err;
+	}
+
+	/* Check error status */
+	if (blow_status & QFPROM_BLOW_STATUS_ERROR) {
+		pr_err("Fuse blow status error: %d\n", blow_status);
+		qfp_fuse_disable_regulator();
+		return -EFAULT;
+	}
+
+	/* Disable regulator */
+	qfp_fuse_disable_regulator();
+	/*
+	 * Wait for about 1ms. However msleep(1) can sleep for
+	 * up to 20ms as per Documentation/timers/timers-howto.txt.
+	 * Time is not a constraint here.
+	 */
+	msleep(20);
+
+	/* Verify written data */
+	read_data = readl_relaxed(addr);
+	if (read_data != data) {
+		pr_err("Error: read/write data mismatch\n");
+		pr_err("Address = %p written data = %x read data = %x\n",
+			addr, data, read_data);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static long
+qfp_fuse_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	int err = 0;
+	struct qfp_fuse_req req;
+	u32 *buf = NULL;
+	int i;
+
+	/* Verify user arguments. */
+	if (_IOC_TYPE(cmd) != QFP_FUSE_IOC_MAGIC)
+		return -ENOTTY;
+
+	switch (cmd) {
+	case QFP_FUSE_IOC_READ:
+		if (arg == 0) {
+			pr_err("user space arg not supplied\n");
+			err = -EFAULT;
+			break;
+		}
+
+		if (copy_from_user(&req, (void __user *)arg, sizeof(req))) {
+			pr_err("Error copying req from user space\n");
+			err = -EFAULT;
+			break;
+		}
+
+		/* Check for limits */
+		if (!req.size) {
+			pr_err("Request size zero.\n");
+			err = -EFAULT;
+			break;
+		}
+
+		if (qfp_priv->base + req.offset + (req.size - 1) * 4 >
+				qfp_priv->end) {
+			pr_err("Req size exceeds QFPROM addr space\n");
+			err = -EFAULT;
+			break;
+		}
+
+		/* Allocate memory for buffer */
+		buf = kzalloc(req.size * 4, GFP_KERNEL);
+		if (buf == NULL) {
+			pr_alert("No memory for data\n");
+			err = -ENOMEM;
+			break;
+		}
+
+		if (mutex_lock_interruptible(&qfp_priv->lock)) {
+			err = -ERESTARTSYS;
+			break;
+		}
+
+		/* Read data */
+		for (i = 0; i < req.size; i++)
+			buf[i] = readl_relaxed(
+				((u32 *) (qfp_priv->base + req.offset)) + i);
+
+		if (copy_to_user((void __user *)req.data, buf, 4*(req.size))) {
+			pr_err("Error copying to user space\n");
+			err = -EFAULT;
+		}
+
+		mutex_unlock(&qfp_priv->lock);
+		break;
+
+	case QFP_FUSE_IOC_WRITE:
+		if (arg == 0) {
+			pr_err("user space arg not supplied\n");
+			err = -EFAULT;
+			break;
+		}
+
+		if (copy_from_user(&req, (void __user *)arg, sizeof(req))) {
+			pr_err("Error copying req from user space\n");
+			err = -EFAULT;
+			break;
+		}
+		/* Check for limits */
+		if (!req.size) {
+			pr_err("Request size zero.\n");
+			err = -EFAULT;
+			break;
+		}
+		if (qfp_priv->base + req.offset + (req.size - 1) * 4 >
+				qfp_priv->end) {
+			pr_err("Req size exceeds QFPROM space\n");
+			err = -EFAULT;
+			break;
+		}
+
+		/* Allocate memory for buffer */
+		buf = kzalloc(4 * (req.size), GFP_KERNEL);
+		if (buf == NULL) {
+			pr_alert("No memory for data\n");
+			err = -ENOMEM;
+			break;
+		}
+
+		/* Copy user data to local buffer */
+		if (copy_from_user(buf, (void __user *)req.data,
+				4 * (req.size))) {
+			pr_err("Error copying data from user space\n");
+			err = -EFAULT;
+			break;
+		}
+
+		if (mutex_lock_interruptible(&qfp_priv->lock)) {
+			err = -ERESTARTSYS;
+			break;
+		}
+
+		/* Write data word at a time */
+		for (i = 0; i < req.size && !err; i++) {
+			err = qfp_fuse_write_word(((u32 *) (
+				qfp_priv->base + req.offset) + i), buf[i]);
+		}
+
+		mutex_unlock(&qfp_priv->lock);
+		break;
+	default:
+		pr_err("Invalid ioctl command.\n");
+		return -ENOTTY;
+	}
+	kfree(buf);
+	return err;
+}
+
+static const struct file_operations qfp_fuse_fops = {
+	.owner = THIS_MODULE,
+	.unlocked_ioctl = qfp_fuse_ioctl,
+	.open = qfp_fuse_open,
+	.release = qfp_fuse_release
+};
+
+static struct miscdevice qfp_fuse_dev = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "qfpfuse",
+	.fops = &qfp_fuse_fops
+};
+
+
+static int qfp_fuse_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct resource *res;
+	const char *regulator_name = pdev->dev.platform_data;
+
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	if (!regulator_name)
+		return -EINVAL;
+
+	/* Initialize */
+	qfp_priv = kzalloc(sizeof(struct qfp_priv_t), GFP_KERNEL);
+
+	if (qfp_priv == NULL) {
+		pr_alert("Not enough memory to initialize device\n");
+		return -ENOMEM;
+	}
+
+	/* The driver is passed ioremapped address */
+	qfp_priv->base = res->start;
+	qfp_priv->end = res->end;
+
+	/* Get regulator for QFPROM writes */
+	qfp_priv->fuse_vdd = regulator_get(NULL, regulator_name);
+	if (IS_ERR(qfp_priv->fuse_vdd)) {
+		ret = PTR_ERR(qfp_priv->fuse_vdd);
+		pr_err("Err (%d) getting %s\n", ret, regulator_name);
+		qfp_priv->fuse_vdd = NULL;
+		goto err;
+	}
+
+	mutex_init(&qfp_priv->lock);
+
+	ret = misc_register(&qfp_fuse_dev);
+	if (ret < 0)
+		goto err;
+
+	pr_info("Fuse driver base:%x end:%x\n", qfp_priv->base, qfp_priv->end);
+	return 0;
+
+err:
+	if (qfp_priv->fuse_vdd)
+		regulator_put(qfp_priv->fuse_vdd);
+
+	kfree(qfp_priv);
+	qfp_priv = NULL;
+
+	return ret;
+
+}
+
+static int __devexit qfp_fuse_remove(struct platform_device *plat)
+{
+	if (qfp_priv && qfp_priv->fuse_vdd)
+		regulator_put(qfp_priv->fuse_vdd);
+
+	kfree(qfp_priv);
+	qfp_priv = NULL;
+
+	misc_deregister(&qfp_fuse_dev);
+	pr_info("Removing Fuse driver\n");
+	return 0;
+}
+
+static struct platform_driver qfp_fuse_driver = {
+	.probe = qfp_fuse_probe,
+	.remove = qfp_fuse_remove,
+	.driver = {
+		.name = "qfp_fuse_driver",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init qfp_fuse_init(void)
+{
+	return platform_driver_register(&qfp_fuse_driver);
+}
+
+static void __exit qfp_fuse_exit(void)
+{
+	platform_driver_unregister(&qfp_fuse_driver);
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Rohit Vaswani <rvaswani@codeaurora.org>");
+MODULE_DESCRIPTION("Driver to read/write to QFPROM fuses.");
+MODULE_VERSION("1.01");
+
+module_init(qfp_fuse_init);
+module_exit(qfp_fuse_exit);
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
new file mode 100644
index 0000000..4c92ee5
--- /dev/null
+++ b/drivers/misc/qseecom.c
@@ -0,0 +1,1781 @@
+
+
+/* Qualcomm Secure Execution Environment Communicator (QSEECOM) driver
+ *
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "QSEECOM: %s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/cdev.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+#include <linux/ion.h>
+#include <linux/types.h>
+#include <linux/clk.h>
+#include <linux/qseecom.h>
+#include <linux/freezer.h>
+#include <mach/board.h>
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
+#include <mach/scm.h>
+#include <mach/peripheral-loader.h>
+#include "qseecom_legacy.h"
+
+#define QSEECOM_DEV			"qseecom"
+#define QSEOS_VERSION_13		0x13
+#define QSEOS_VERSION_14		0x14
+#define QSEOS_CHECK_VERSION_CMD		0x00001803;
+
+enum qseecom_command_scm_resp_type {
+	QSEOS_APP_ID = 0xEE01,
+	QSEOS_LISTENER_ID
+};
+
+enum qseecom_qceos_cmd_id {
+	QSEOS_APP_START_COMMAND      = 0x01,
+	QSEOS_APP_SHUTDOWN_COMMAND,
+	QSEOS_APP_LOOKUP_COMMAND,
+	QSEOS_REGISTER_LISTENER,
+	QSEOS_DEREGISTER_LISTENER,
+	QSEOS_CLIENT_SEND_DATA_COMMAND,
+	QSEOS_LISTENER_DATA_RSP_COMMAND,
+	QSEOS_LOAD_EXTERNAL_ELF_COMMAND,
+	QSEOS_UNLOAD_EXTERNAL_ELF_COMMAND,
+	QSEOS_CMD_MAX     = 0xEFFFFFFF
+};
+
+enum qseecom_qceos_cmd_status {
+	QSEOS_RESULT_SUCCESS = 0,
+	QSEOS_RESULT_INCOMPLETE,
+	QSEOS_RESULT_FAILURE  = 0xFFFFFFFF
+};
+
+enum qseecom_clk_definitions {
+	CLK_DFAB = 0,
+	CLK_SFPB,
+};
+
+__packed struct qseecom_check_app_ireq {
+	uint32_t qsee_cmd_id;
+	char     app_name[MAX_APP_NAME_SIZE];
+};
+
+__packed struct qseecom_load_app_ireq {
+	uint32_t qsee_cmd_id;
+	uint32_t mdt_len;		/* Length of the mdt file */
+	uint32_t img_len;		/* Length of .bxx and .mdt files */
+	uint32_t phy_addr;		/* phy addr of the start of image */
+	char     app_name[MAX_APP_NAME_SIZE];	/* application name*/
+};
+
+__packed struct qseecom_unload_app_ireq {
+	uint32_t qsee_cmd_id;
+	uint32_t  app_id;
+};
+
+__packed struct qseecom_register_listener_ireq {
+	uint32_t qsee_cmd_id;
+	uint32_t listener_id;
+	void *sb_ptr;
+	uint32_t sb_len;
+};
+
+__packed struct qseecom_unregister_listener_ireq {
+	uint32_t qsee_cmd_id;
+	uint32_t  listener_id;
+};
+
+__packed struct qseecom_client_send_data_ireq {
+	uint32_t qsee_cmd_id;
+	uint32_t app_id;
+	void *req_ptr;
+	uint32_t req_len;
+	void *rsp_ptr;   /* First 4 bytes should always be the return status */
+	uint32_t rsp_len;
+};
+
+/* send_data resp */
+__packed struct qseecom_client_listener_data_irsp {
+	uint32_t qsee_cmd_id;
+	uint32_t listener_id;
+};
+
+/*
+ * struct qseecom_command_scm_resp - qseecom response buffer
+ * @cmd_status: value from enum tz_sched_cmd_status
+ * @sb_in_rsp_addr: points to physical location of response
+ *                buffer
+ * @sb_in_rsp_len: length of command response
+ */
+__packed struct qseecom_command_scm_resp {
+	uint32_t result;
+	enum qseecom_command_scm_resp_type resp_type;
+	unsigned int data;
+};
+
+static struct class *driver_class;
+static dev_t qseecom_device_no;
+static struct cdev qseecom_cdev;
+
+/* Data structures used in legacy support */
+static void *pil;
+static uint32_t pil_ref_cnt;
+static DEFINE_MUTEX(pil_access_lock);
+
+static DEFINE_MUTEX(send_msg_lock);
+static DEFINE_MUTEX(qsee_bw_mutex);
+static DEFINE_MUTEX(qsee_sfpb_bw_mutex);
+static DEFINE_MUTEX(app_access_lock);
+
+static int qsee_bw_count;
+static int qsee_sfpb_bw_count;
+static struct clk *qseecom_bus_clk;
+static uint32_t qsee_perf_client;
+
+struct qseecom_registered_listener_list {
+	struct list_head                 list;
+	struct qseecom_register_listener_req svc;
+	u8  *sb_reg_req;
+	u8 *sb_virt;
+	s32 sb_phys;
+	size_t sb_length;
+	struct ion_handle *ihandle; /* Retrieve phy addr */
+
+	wait_queue_head_t          rcv_req_wq;
+	int                        rcv_req_flag;
+};
+
+struct qseecom_registered_app_list {
+	struct list_head                 list;
+	u32  app_id;
+	u32  ref_cnt;
+};
+
+struct qseecom_control {
+	struct ion_client *ion_clnt;		/* Ion client */
+	struct list_head  registered_listener_list_head;
+	spinlock_t        registered_listener_list_lock;
+
+	struct list_head  registered_app_list_head;
+	spinlock_t        registered_app_list_lock;
+
+	wait_queue_head_t send_resp_wq;
+	int               send_resp_flag;
+
+	uint32_t          qseos_version;
+};
+
+struct qseecom_client_handle {
+	u32  app_id;
+	u8 *sb_virt;
+	s32 sb_phys;
+	uint32_t user_virt_sb_base;
+	size_t sb_length;
+	struct ion_handle *ihandle;		/* Retrieve phy addr */
+};
+
+struct qseecom_listener_handle {
+	u32               id;
+};
+
+static struct qseecom_control qseecom;
+
+struct qseecom_dev_handle {
+	bool               service;
+	union {
+		struct qseecom_client_handle client;
+		struct qseecom_listener_handle listener;
+	};
+	bool released;
+	int               abort;
+	wait_queue_head_t abort_wq;
+	atomic_t          ioctl_count;
+};
+
+/* Function proto types */
+static int qsee_vote_for_clock(int32_t);
+static void qsee_disable_clock_vote(int32_t);
+
+static int __qseecom_is_svc_unique(struct qseecom_dev_handle *data,
+		struct qseecom_register_listener_req *svc)
+{
+	struct qseecom_registered_listener_list *ptr;
+	int unique = 1;
+	unsigned long flags;
+
+	spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
+	list_for_each_entry(ptr, &qseecom.registered_listener_list_head, list) {
+		if (ptr->svc.listener_id == svc->listener_id) {
+			pr_err("Service id: %u is already registered\n",
+					ptr->svc.listener_id);
+			unique = 0;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
+	return unique;
+}
+
+static struct qseecom_registered_listener_list *__qseecom_find_svc(
+						int32_t listener_id)
+{
+	struct qseecom_registered_listener_list *entry = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
+	list_for_each_entry(entry, &qseecom.registered_listener_list_head, list)
+	{
+		if (entry->svc.listener_id == listener_id)
+			break;
+	}
+	spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
+	return entry;
+}
+
+static int __qseecom_set_sb_memory(struct qseecom_registered_listener_list *svc,
+				struct qseecom_dev_handle *handle,
+				struct qseecom_register_listener_req *listener)
+{
+	int ret = 0;
+	unsigned int flags = 0;
+	struct qseecom_register_listener_ireq req;
+	struct qseecom_command_scm_resp resp;
+	ion_phys_addr_t pa;
+
+	/* Get the handle of the shared fd */
+	svc->ihandle = ion_import_fd(qseecom.ion_clnt, listener->ifd_data_fd);
+	if (svc->ihandle == NULL) {
+		pr_err("Ion client could not retrieve the handle\n");
+		return -ENOMEM;
+	}
+
+	/* Get the physical address of the ION BUF */
+	ret = ion_phys(qseecom.ion_clnt, svc->ihandle, &pa, &svc->sb_length);
+
+	/* Populate the structure for sending scm call to load image */
+	svc->sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt,
+							svc->ihandle, flags);
+	svc->sb_phys = pa;
+
+	if (qseecom.qseos_version == QSEOS_VERSION_14) {
+		req.qsee_cmd_id = QSEOS_REGISTER_LISTENER;
+		req.listener_id = svc->svc.listener_id;
+		req.sb_len = svc->sb_length;
+		req.sb_ptr = (void *)svc->sb_phys;
+
+		resp.result = QSEOS_RESULT_INCOMPLETE;
+
+		ret = scm_call(SCM_SVC_TZSCHEDULER, 1,  &req,
+					sizeof(req), &resp, sizeof(resp));
+		if (ret) {
+			pr_err("qseecom_scm_call failed with err: %d\n", ret);
+			return -EINVAL;
+		}
+
+		if (resp.result != QSEOS_RESULT_SUCCESS) {
+			pr_err("Error SB registration req: resp.result = %d\n",
+					resp.result);
+			return -EPERM;
+		}
+	} else {
+		struct qseecom_command cmd;
+		struct qseecom_response resp;
+		struct qse_pr_init_sb_req_s sb_init_req;
+		struct qse_pr_init_sb_rsp_s sb_init_rsp;
+
+		svc->sb_reg_req = kzalloc((sizeof(sb_init_req) +
+					sizeof(sb_init_rsp)), GFP_KERNEL);
+
+		sb_init_req.pr_cmd = TZ_SCHED_CMD_ID_REGISTER_LISTENER;
+		sb_init_req.listener_id = svc->svc.listener_id;
+		sb_init_req.sb_len = svc->sb_length;
+		sb_init_req.sb_ptr = svc->sb_phys;
+
+		memcpy(svc->sb_reg_req, &sb_init_req, sizeof(sb_init_req));
+
+		/* It will always be a new cmd from this method */
+		cmd.cmd_type = TZ_SCHED_CMD_NEW;
+		cmd.sb_in_cmd_addr = (u8 *)(virt_to_phys(svc->sb_reg_req));
+		cmd.sb_in_cmd_len = sizeof(sb_init_req);
+
+		resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
+
+		ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &cmd, sizeof(cmd)
+				, &resp, sizeof(resp));
+
+		if (ret) {
+			pr_err("qseecom_scm_call failed with err: %d\n", ret);
+			return -EINVAL;
+		}
+
+		if (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
+			pr_err("SB registration fail resp.cmd_status %d\n",
+							resp.cmd_status);
+			return -EINVAL;
+		}
+		memset(svc->sb_virt, 0, svc->sb_length);
+	}
+	return 0;
+}
+
+static int qseecom_register_listener(struct qseecom_dev_handle *data,
+					void __user *argp)
+{
+	int ret = 0;
+	unsigned long flags;
+	struct qseecom_register_listener_req rcvd_lstnr;
+	struct qseecom_registered_listener_list *new_entry;
+
+	ret = copy_from_user(&rcvd_lstnr, argp, sizeof(rcvd_lstnr));
+	if (ret) {
+		pr_err("copy_from_user failed\n");
+		return ret;
+	}
+	data->listener.id = 0;
+	data->service = true;
+	if (!__qseecom_is_svc_unique(data, &rcvd_lstnr)) {
+		pr_err("Service is not unique and is already registered\n");
+		data->released = true;
+		return -EBUSY;
+	}
+
+	new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
+	if (!new_entry) {
+		pr_err("kmalloc failed\n");
+		return -ENOMEM;
+	}
+	memcpy(&new_entry->svc, &rcvd_lstnr, sizeof(rcvd_lstnr));
+	new_entry->rcv_req_flag = 0;
+
+	new_entry->svc.listener_id = rcvd_lstnr.listener_id;
+	new_entry->sb_length = rcvd_lstnr.sb_size;
+	if (__qseecom_set_sb_memory(new_entry, data, &rcvd_lstnr)) {
+		pr_err("qseecom_set_sb_memoryfailed\n");
+		kzfree(new_entry);
+		return -ENOMEM;
+	}
+
+	data->listener.id = rcvd_lstnr.listener_id;
+	init_waitqueue_head(&new_entry->rcv_req_wq);
+
+	spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
+	list_add_tail(&new_entry->list, &qseecom.registered_listener_list_head);
+	spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
+
+	return ret;
+}
+
+static int qseecom_unregister_listener(struct qseecom_dev_handle *data)
+{
+	int ret = 0;
+	unsigned long flags;
+	uint32_t unmap_mem = 0;
+	struct qseecom_register_listener_ireq req;
+	struct qseecom_registered_listener_list *ptr_svc = NULL;
+	struct qseecom_command_scm_resp resp;
+	struct ion_handle *ihandle = NULL;		/* Retrieve phy addr */
+
+	if (qseecom.qseos_version == QSEOS_VERSION_14) {
+		req.qsee_cmd_id = QSEOS_DEREGISTER_LISTENER;
+		req.listener_id = data->listener.id;
+		resp.result = QSEOS_RESULT_INCOMPLETE;
+
+		ret = scm_call(SCM_SVC_TZSCHEDULER, 1,  &req,
+					sizeof(req), &resp, sizeof(resp));
+		if (ret) {
+			pr_err("qseecom_scm_call failed with err: %d\n", ret);
+			return ret;
+		}
+
+		if (resp.result != QSEOS_RESULT_SUCCESS) {
+			pr_err("SB deregistartion: result=%d\n", resp.result);
+			return -EPERM;
+		}
+	} else {
+		struct qse_pr_init_sb_req_s sb_init_req;
+		struct qseecom_command cmd;
+		struct qseecom_response resp;
+		struct qseecom_registered_listener_list *svc;
+
+		svc = __qseecom_find_svc(data->listener.id);
+		sb_init_req.pr_cmd = TZ_SCHED_CMD_ID_REGISTER_LISTENER;
+		sb_init_req.listener_id = data->listener.id;
+		sb_init_req.sb_len = 0;
+		sb_init_req.sb_ptr = 0;
+
+		memcpy(svc->sb_reg_req, &sb_init_req, sizeof(sb_init_req));
+
+		/* It will always be a new cmd from this method */
+		cmd.cmd_type = TZ_SCHED_CMD_NEW;
+		cmd.sb_in_cmd_addr = (u8 *)(virt_to_phys(svc->sb_reg_req));
+		cmd.sb_in_cmd_len = sizeof(sb_init_req);
+		resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
+
+		ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &cmd, sizeof(cmd),
+					&resp, sizeof(resp));
+		if (ret) {
+			pr_err("qseecom_scm_call failed with err: %d\n", ret);
+			return ret;
+		}
+		kzfree(svc->sb_reg_req);
+		if (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
+			pr_err("Error with SB initialization\n");
+			return -EPERM;
+		}
+	}
+	data->abort = 1;
+	spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
+	list_for_each_entry(ptr_svc, &qseecom.registered_listener_list_head,
+			list) {
+		if (ptr_svc->svc.listener_id == data->listener.id) {
+			wake_up_all(&ptr_svc->rcv_req_wq);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
+
+	while (atomic_read(&data->ioctl_count) > 1) {
+		if (wait_event_freezable(data->abort_wq,
+				atomic_read(&data->ioctl_count) <= 1)) {
+			pr_err("Interrupted from abort\n");
+			ret = -ERESTARTSYS;
+			break;
+		}
+	}
+
+	spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
+	list_for_each_entry(ptr_svc,
+			&qseecom.registered_listener_list_head,
+			list)
+	{
+		if (ptr_svc->svc.listener_id == data->listener.id) {
+			if (ptr_svc->sb_virt) {
+				unmap_mem = 1;
+				ihandle = ptr_svc->ihandle;
+				}
+			list_del(&ptr_svc->list);
+			kzfree(ptr_svc);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
+
+	/* Unmap the memory */
+	if (unmap_mem) {
+		if (!IS_ERR_OR_NULL(ihandle)) {
+			ion_unmap_kernel(qseecom.ion_clnt, ihandle);
+			ion_free(qseecom.ion_clnt, ihandle);
+			}
+	}
+	data->released = true;
+	return ret;
+}
+
+static int qseecom_set_client_mem_param(struct qseecom_dev_handle *data,
+						void __user *argp)
+{
+	ion_phys_addr_t pa;
+	int32_t ret;
+	unsigned int flags = 0;
+	struct qseecom_set_sb_mem_param_req req;
+	uint32_t len;
+
+	/* Copy the relevant information needed for loading the image */
+	if (__copy_from_user(&req, (void __user *)argp, sizeof(req)))
+		return -EFAULT;
+
+	/* Get the handle of the shared fd */
+	data->client.ihandle = ion_import_fd(qseecom.ion_clnt, req.ifd_data_fd);
+	if (IS_ERR_OR_NULL(data->client.ihandle)) {
+		pr_err("Ion client could not retrieve the handle\n");
+		return -ENOMEM;
+	}
+	/* Get the physical address of the ION BUF */
+	ret = ion_phys(qseecom.ion_clnt, data->client.ihandle, &pa, &len);
+	/* Populate the structure for sending scm call to load image */
+	data->client.sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt,
+							data->client.ihandle,
+							flags);
+	data->client.sb_phys = pa;
+	data->client.sb_length = req.sb_len;
+	data->client.user_virt_sb_base = req.virt_sb_base;
+	return 0;
+}
+
+
+static int __qseecom_listener_has_sent_rsp(struct qseecom_dev_handle *data)
+{
+	int ret;
+	ret = (qseecom.send_resp_flag != 0);
+	return ret || data->abort;
+}
+
+static int __qseecom_process_incomplete_cmd(struct qseecom_dev_handle *data,
+					struct qseecom_command_scm_resp *resp)
+{
+	int ret = 0;
+	uint32_t lstnr;
+	unsigned long flags;
+	struct qseecom_client_listener_data_irsp send_data_rsp;
+	struct qseecom_registered_listener_list *ptr_svc = NULL;
+
+
+	while (resp->result == QSEOS_RESULT_INCOMPLETE) {
+		lstnr = resp->data;
+		/*
+		 * Wake up blocking lsitener service with the lstnr id
+		 */
+		spin_lock_irqsave(&qseecom.registered_listener_list_lock,
+					flags);
+		list_for_each_entry(ptr_svc,
+				&qseecom.registered_listener_list_head, list) {
+			if (ptr_svc->svc.listener_id == lstnr) {
+				ptr_svc->rcv_req_flag = 1;
+				wake_up_interruptible(&ptr_svc->rcv_req_wq);
+				break;
+			}
+		}
+		spin_unlock_irqrestore(&qseecom.registered_listener_list_lock,
+				flags);
+		if (ptr_svc->svc.listener_id != lstnr) {
+			pr_warning("Service requested for does on exist\n");
+			return -ERESTARTSYS;
+		}
+		pr_debug("waking up rcv_req_wq and "
+				"waiting for send_resp_wq\n");
+		if (wait_event_freezable(qseecom.send_resp_wq,
+				__qseecom_listener_has_sent_rsp(data))) {
+			pr_warning("Interrupted: exiting send_cmd loop\n");
+			return -ERESTARTSYS;
+		}
+
+		if (data->abort) {
+			pr_err("Aborting listener service %d\n",
+				data->listener.id);
+			return -ENODEV;
+		}
+		qseecom.send_resp_flag = 0;
+		send_data_rsp.qsee_cmd_id = QSEOS_LISTENER_DATA_RSP_COMMAND;
+		send_data_rsp.listener_id  = lstnr ;
+
+		ret = scm_call(SCM_SVC_TZSCHEDULER, 1,
+					(const void *)&send_data_rsp,
+					sizeof(send_data_rsp), resp,
+					sizeof(*resp));
+		if (ret) {
+			pr_err("qseecom_scm_call failed with err: %d\n", ret);
+			return ret;
+		}
+		if (resp->result == QSEOS_RESULT_FAILURE) {
+			pr_err("Response result %d not supported\n",
+							resp->result);
+			return -EINVAL;
+		}
+	}
+	return ret;
+}
+
+static int qseecom_load_app(struct qseecom_dev_handle *data, void __user *argp)
+{
+	struct qseecom_registered_app_list *entry = NULL;
+	unsigned long flags = 0;
+	u32 app_id = 0;
+	struct ion_handle *ihandle;	/* Ion handle */
+	struct qseecom_load_img_req load_img_req;
+	int32_t ret;
+	ion_phys_addr_t pa = 0;
+	uint32_t len;
+	struct qseecom_command_scm_resp resp;
+	struct qseecom_check_app_ireq req;
+	/* Copy the relevant information needed for loading the image */
+	if (__copy_from_user(&load_img_req,
+				(void __user *)argp,
+				sizeof(struct qseecom_load_img_req))) {
+		pr_err("copy_from_user failed\n");
+		return -EFAULT;
+	}
+	/* Vote for the SFPB clock */
+	ret = qsee_vote_for_clock(CLK_SFPB);
+	if (ret)
+		pr_warning("Unable to vote for SFPB clock");
+
+	req.qsee_cmd_id = QSEOS_APP_LOOKUP_COMMAND;
+	memcpy(req.app_name, load_img_req.img_name, MAX_APP_NAME_SIZE);
+
+	/*  SCM_CALL  to check if app_id for the mentioned app exists */
+	ret = scm_call(SCM_SVC_TZSCHEDULER, 1,  &req,
+				sizeof(struct qseecom_check_app_ireq),
+				&resp, sizeof(resp));
+	if (ret) {
+		pr_err("scm_call to check if app is already loaded failed\n");
+		return -EINVAL;
+	}
+
+	if (resp.result == QSEOS_RESULT_FAILURE)
+		app_id = 0;
+	else
+		app_id = resp.data;
+
+	if (app_id) {
+		pr_warn("App id %d (%s) already exists\n", app_id,
+			(char *)(req.app_name));
+		spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
+		list_for_each_entry(entry,
+				&qseecom.registered_app_list_head, list){
+			if (entry->app_id == app_id) {
+				entry->ref_cnt++;
+				break;
+			}
+		}
+		spin_unlock_irqrestore(
+				&qseecom.registered_app_list_lock, flags);
+	} else {
+		struct qseecom_load_app_ireq load_req;
+
+		pr_warn("App (%s) does not exist, loading apps for first time\n",
+			(char *)(req.app_name));
+		/* Get the handle of the shared fd */
+		ihandle = ion_import_fd(qseecom.ion_clnt,
+					load_img_req.ifd_data_fd);
+		if (IS_ERR_OR_NULL(ihandle)) {
+			pr_err("Ion client could not retrieve the handle\n");
+			qsee_disable_clock_vote(CLK_SFPB);
+			return -ENOMEM;
+		}
+
+		/* Get the physical address of the ION BUF */
+		ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &len);
+
+		/* Populate the structure for sending scm call to load image */
+		load_req.qsee_cmd_id = QSEOS_APP_START_COMMAND;
+		load_req.mdt_len = load_img_req.mdt_len;
+		load_req.img_len = load_img_req.img_len;
+		load_req.phy_addr = pa;
+
+		/*  SCM_CALL  to load the app and get the app_id back */
+		ret = scm_call(SCM_SVC_TZSCHEDULER, 1,  &load_req,
+				sizeof(struct qseecom_load_app_ireq),
+				&resp, sizeof(resp));
+		if (ret) {
+			pr_err("scm_call to load app failed\n");
+			return -EINVAL;
+		}
+
+		if (resp.result == QSEOS_RESULT_FAILURE) {
+			pr_err("scm_call rsp.result is QSEOS_RESULT_FAILURE\n");
+			if (!IS_ERR_OR_NULL(ihandle))
+				ion_free(qseecom.ion_clnt, ihandle);
+			qsee_disable_clock_vote(CLK_SFPB);
+			return -EFAULT;
+		}
+
+		if (resp.result == QSEOS_RESULT_INCOMPLETE) {
+			ret = __qseecom_process_incomplete_cmd(data, &resp);
+			if (ret) {
+				pr_err("process_incomplete_cmd failed err: %d\n",
+						ret);
+				if (!IS_ERR_OR_NULL(ihandle))
+					ion_free(qseecom.ion_clnt, ihandle);
+				qsee_disable_clock_vote(CLK_SFPB);
+				return ret;
+			}
+		}
+		if (resp.result != QSEOS_RESULT_SUCCESS) {
+			pr_err("scm_call failed resp.result unknown, %d\n",
+					resp.result);
+			if (!IS_ERR_OR_NULL(ihandle))
+				ion_free(qseecom.ion_clnt, ihandle);
+			qsee_disable_clock_vote(CLK_SFPB);
+			return -EFAULT;
+		}
+
+		app_id = resp.data;
+
+		entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+		if (!entry) {
+			pr_err("kmalloc failed\n");
+			qsee_disable_clock_vote(CLK_SFPB);
+			return -ENOMEM;
+		}
+		entry->app_id = app_id;
+		entry->ref_cnt = 1;
+
+		/* Deallocate the handle */
+		if (!IS_ERR_OR_NULL(ihandle))
+			ion_free(qseecom.ion_clnt, ihandle);
+
+		spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
+		list_add_tail(&entry->list, &qseecom.registered_app_list_head);
+		spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
+					flags);
+
+		pr_warn("App with id %d (%s) now loaded\n", app_id,
+			(char *)(req.app_name));
+	}
+	data->client.app_id = app_id;
+	load_img_req.app_id = app_id;
+	if (copy_to_user(argp, &load_img_req, sizeof(load_img_req))) {
+		pr_err("copy_to_user failed\n");
+		kzfree(entry);
+		qsee_disable_clock_vote(CLK_SFPB);
+		return -EFAULT;
+	}
+	qsee_disable_clock_vote(CLK_SFPB);
+	return 0;
+}
+
+static int __qseecom_cleanup_app(struct qseecom_dev_handle *data)
+{
+	wake_up_all(&qseecom.send_resp_wq);
+	while (atomic_read(&data->ioctl_count) > 1) {
+		if (wait_event_freezable(data->abort_wq,
+					atomic_read(&data->ioctl_count) <= 1)) {
+			pr_err("Interrupted from abort\n");
+			return -ERESTARTSYS;
+			break;
+		}
+	}
+	/* Set unload app */
+	return 1;
+}
+
+static int qseecom_unload_app(struct qseecom_dev_handle *data)
+{
+	unsigned long flags;
+	int ret = 0;
+	struct qseecom_command_scm_resp resp;
+	struct qseecom_registered_app_list *ptr_app;
+	uint32_t unload = 0;
+
+	if (qseecom.qseos_version == QSEOS_VERSION_14) {
+		spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
+		list_for_each_entry(ptr_app, &qseecom.registered_app_list_head,
+								list) {
+			if (ptr_app->app_id == data->client.app_id) {
+				if (ptr_app->ref_cnt == 1) {
+					unload = __qseecom_cleanup_app(data);
+					list_del(&ptr_app->list);
+					kzfree(ptr_app);
+					break;
+				} else {
+					ptr_app->ref_cnt--;
+					data->released = true;
+					pr_warn("Can't unload app with id %d (it is inuse)\n",
+							ptr_app->app_id);
+					break;
+				}
+			}
+		}
+		spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
+								flags);
+	}
+	if (!IS_ERR_OR_NULL(data->client.ihandle)) {
+		ion_unmap_kernel(qseecom.ion_clnt, data->client.ihandle);
+		ion_free(qseecom.ion_clnt, data->client.ihandle);
+	}
+
+	if ((unload) && (qseecom.qseos_version == QSEOS_VERSION_14)) {
+		struct qseecom_unload_app_ireq req;
+
+		/* Populate the structure for sending scm call to load image */
+		req.qsee_cmd_id = QSEOS_APP_SHUTDOWN_COMMAND;
+		req.app_id = data->client.app_id;
+
+		/* SCM_CALL to unload the app */
+		ret = scm_call(SCM_SVC_TZSCHEDULER, 1,  &req,
+				sizeof(struct qseecom_unload_app_ireq),
+				&resp, sizeof(resp));
+		if (ret) {
+			pr_err("scm_call to unload app (id = %d) failed\n",
+							req.app_id);
+			return -EFAULT;
+		} else {
+			pr_warn("App id %d now unloaded\n", req.app_id);
+		}
+		if (resp.result == QSEOS_RESULT_INCOMPLETE) {
+			ret = __qseecom_process_incomplete_cmd(data, &resp);
+			if (ret) {
+				pr_err("process_incomplete_cmd fail err: %d\n",
+						ret);
+				return ret;
+			}
+		}
+	}
+
+	if (qseecom.qseos_version == QSEOS_VERSION_13) {
+		data->abort = 1;
+		wake_up_all(&qseecom.send_resp_wq);
+		while (atomic_read(&data->ioctl_count) > 0) {
+			if (wait_event_freezable(data->abort_wq,
+					atomic_read(&data->ioctl_count) <= 0)) {
+				pr_err("Interrupted from abort\n");
+				ret = -ERESTARTSYS;
+				break;
+			}
+		}
+	}
+	data->released = true;
+	return ret;
+}
+
+static uint32_t __qseecom_uvirt_to_kphys(struct qseecom_dev_handle *data,
+						uint32_t virt)
+{
+	return data->client.sb_phys + (virt - data->client.user_virt_sb_base);
+}
+
+static int __qseecom_send_cmd_legacy(struct qseecom_dev_handle *data,
+				struct qseecom_send_cmd_req *req)
+{
+	int ret = 0;
+	unsigned long flags;
+	u32 reqd_len_sb_in = 0;
+	struct qseecom_command cmd;
+	struct qseecom_response resp;
+
+
+	if (req->cmd_req_buf == NULL || req->resp_buf == NULL) {
+		pr_err("cmd buffer or response buffer is null\n");
+		return -EINVAL;
+	}
+
+	if (req->cmd_req_len <= 0 ||
+		req->resp_len <= 0 ||
+		req->cmd_req_len > data->client.sb_length ||
+		req->resp_len > data->client.sb_length) {
+		pr_err("cmd buffer length or "
+				"response buffer length not valid\n");
+		return -EINVAL;
+	}
+
+	reqd_len_sb_in = req->cmd_req_len + req->resp_len;
+	if (reqd_len_sb_in > data->client.sb_length) {
+		pr_debug("Not enough memory to fit cmd_buf and "
+			"resp_buf. Required: %u, Available: %u\n",
+				reqd_len_sb_in, data->client.sb_length);
+		return -ENOMEM;
+	}
+	cmd.cmd_type = TZ_SCHED_CMD_NEW;
+	cmd.sb_in_cmd_addr = (u8 *) data->client.sb_phys;
+	cmd.sb_in_cmd_len = req->cmd_req_len;
+
+	resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
+	resp.sb_in_rsp_addr = (u8 *)data->client.sb_phys + req->cmd_req_len;
+	resp.sb_in_rsp_len = req->resp_len;
+
+	ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *)&cmd,
+					sizeof(cmd), &resp, sizeof(resp));
+
+	if (ret) {
+		pr_err("qseecom_scm_call_legacy failed with err: %d\n", ret);
+		return ret;
+	}
+
+	while (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
+		/*
+		 * If cmd is incomplete, get the callback cmd out from SB out
+		 * and put it on the list
+		 */
+		struct qseecom_registered_listener_list *ptr_svc = NULL;
+		/*
+		 * We don't know which service can handle the command. so we
+		 * wake up all blocking services and let them figure out if
+		 * they can handle the given command.
+		 */
+		spin_lock_irqsave(&qseecom.registered_listener_list_lock,
+					flags);
+		list_for_each_entry(ptr_svc,
+				&qseecom.registered_listener_list_head, list) {
+				ptr_svc->rcv_req_flag = 1;
+				wake_up_interruptible(&ptr_svc->rcv_req_wq);
+		}
+		spin_unlock_irqrestore(&qseecom.registered_listener_list_lock,
+				flags);
+
+		pr_debug("waking up rcv_req_wq and "
+				"waiting for send_resp_wq\n");
+		if (wait_event_freezable(qseecom.send_resp_wq,
+				__qseecom_listener_has_sent_rsp(data))) {
+			pr_warning("qseecom Interrupted: exiting send_cmd loop\n");
+			return -ERESTARTSYS;
+		}
+
+		if (data->abort) {
+			pr_err("Aborting driver\n");
+			return -ENODEV;
+		}
+		qseecom.send_resp_flag = 0;
+		cmd.cmd_type = TZ_SCHED_CMD_PENDING;
+		ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *)&cmd,
+					sizeof(cmd), &resp, sizeof(resp));
+		if (ret) {
+			pr_err("qseecom_scm_call failed with err: %d\n", ret);
+			return ret;
+		}
+	}
+	return ret;
+}
+
+static int __qseecom_send_cmd(struct qseecom_dev_handle *data,
+				struct qseecom_send_cmd_req *req)
+{
+	int ret = 0;
+	u32 reqd_len_sb_in = 0;
+	struct qseecom_client_send_data_ireq send_data_req;
+	struct qseecom_command_scm_resp resp;
+
+	if (req->cmd_req_buf == NULL || req->resp_buf == NULL) {
+		pr_err("cmd buffer or response buffer is null\n");
+		return -EINVAL;
+	}
+
+	if (req->cmd_req_len <= 0 ||
+		req->resp_len <= 0 ||
+		req->cmd_req_len > data->client.sb_length ||
+		req->resp_len > data->client.sb_length) {
+		pr_err("cmd buffer length or "
+				"response buffer length not valid\n");
+		return -EINVAL;
+	}
+
+	reqd_len_sb_in = req->cmd_req_len + req->resp_len;
+	if (reqd_len_sb_in > data->client.sb_length) {
+		pr_debug("Not enough memory to fit cmd_buf and "
+			"resp_buf. Required: %u, Available: %u\n",
+				reqd_len_sb_in, data->client.sb_length);
+		return -ENOMEM;
+	}
+
+	send_data_req.qsee_cmd_id = QSEOS_CLIENT_SEND_DATA_COMMAND;
+	send_data_req.app_id = data->client.app_id;
+	send_data_req.req_ptr = (void *)(__qseecom_uvirt_to_kphys(data,
+					(uint32_t)req->cmd_req_buf));
+	send_data_req.req_len = req->cmd_req_len;
+	send_data_req.rsp_ptr = (void *)(__qseecom_uvirt_to_kphys(data,
+					(uint32_t)req->resp_buf));
+	send_data_req.rsp_len = req->resp_len;
+
+	ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *) &send_data_req,
+					sizeof(send_data_req),
+					&resp, sizeof(resp));
+	if (ret) {
+		pr_err("qseecom_scm_call failed with err: %d\n", ret);
+		return ret;
+	}
+
+	if (resp.result == QSEOS_RESULT_INCOMPLETE) {
+		ret = __qseecom_process_incomplete_cmd(data, &resp);
+		if (ret) {
+			pr_err("process_incomplete_cmd failed err: %d\n", ret);
+			return ret;
+		}
+	} else {
+		if (resp.result != QSEOS_RESULT_SUCCESS) {
+			pr_err("Response result %d not supported\n",
+							resp.result);
+			ret = -EINVAL;
+		}
+	}
+	return ret;
+}
+
+
+static int qseecom_send_cmd(struct qseecom_dev_handle *data, void __user *argp)
+{
+	int ret = 0;
+	struct qseecom_send_cmd_req req;
+
+	ret = copy_from_user(&req, argp, sizeof(req));
+	if (ret) {
+		pr_err("copy_from_user failed\n");
+		return ret;
+	}
+	if (qseecom.qseos_version == QSEOS_VERSION_14)
+		ret = __qseecom_send_cmd(data, &req);
+	else
+		ret = __qseecom_send_cmd_legacy(data, &req);
+	if (ret)
+		return ret;
+
+	pr_debug("sending cmd_req->rsp size: %u, ptr: 0x%p\n",
+			req.resp_len, req.resp_buf);
+	return ret;
+}
+
+static int __qseecom_send_cmd_req_clean_up(
+			struct qseecom_send_modfd_cmd_req *req)
+{
+	char *field;
+	uint32_t *update;
+	int ret = 0;
+	int i = 0;
+
+	for (i = 0; i < MAX_ION_FD; i++) {
+		if (req->ifd_data[i].fd > 0) {
+			field = (char *)req->cmd_req_buf +
+					req->ifd_data[i].cmd_buf_offset;
+			update = (uint32_t *) field;
+			*update = 0;
+		}
+	}
+	return ret;
+}
+
+static int __qseecom_update_with_phy_addr(
+			struct qseecom_send_modfd_cmd_req *req)
+{
+	struct ion_handle *ihandle;
+	char *field;
+	uint32_t *update;
+	ion_phys_addr_t pa;
+	int ret = 0;
+	int i = 0;
+	uint32_t length;
+
+	for (i = 0; i < MAX_ION_FD; i++) {
+		if (req->ifd_data[i].fd > 0) {
+			/* Get the handle of the shared fd */
+			ihandle = ion_import_fd(qseecom.ion_clnt,
+						req->ifd_data[i].fd);
+			if (IS_ERR_OR_NULL(ihandle)) {
+				pr_err("Ion client can't retrieve the handle\n");
+				return -ENOMEM;
+			}
+			field = (char *) req->cmd_req_buf +
+						req->ifd_data[i].cmd_buf_offset;
+			update = (uint32_t *) field;
+
+			/* Populate the cmd data structure with the phys_addr */
+			ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &length);
+			if (ret)
+				return -ENOMEM;
+
+			*update = (uint32_t)pa;
+			/* Deallocate the handle */
+			if (!IS_ERR_OR_NULL(ihandle))
+				ion_free(qseecom.ion_clnt, ihandle);
+		}
+	}
+	return ret;
+}
+
+static int qseecom_send_modfd_cmd(struct qseecom_dev_handle *data,
+					void __user *argp)
+{
+	int ret = 0;
+	struct qseecom_send_modfd_cmd_req req;
+	struct qseecom_send_cmd_req send_cmd_req;
+
+	ret = copy_from_user(&req, argp, sizeof(req));
+	if (ret) {
+		pr_err("copy_from_user failed\n");
+		return ret;
+	}
+	send_cmd_req.cmd_req_buf = req.cmd_req_buf;
+	send_cmd_req.cmd_req_len = req.cmd_req_len;
+	send_cmd_req.resp_buf = req.resp_buf;
+	send_cmd_req.resp_len = req.resp_len;
+
+	ret = __qseecom_update_with_phy_addr(&req);
+	if (ret)
+		return ret;
+	if (qseecom.qseos_version == QSEOS_VERSION_14)
+		ret = __qseecom_send_cmd(data, &send_cmd_req);
+	else
+		ret = __qseecom_send_cmd_legacy(data, &send_cmd_req);
+	__qseecom_send_cmd_req_clean_up(&req);
+
+	if (ret)
+		return ret;
+
+	pr_debug("sending cmd_req->rsp size: %u, ptr: 0x%p\n",
+			req.resp_len, req.resp_buf);
+	return ret;
+}
+
+static int __qseecom_listener_has_rcvd_req(struct qseecom_dev_handle *data,
+		struct qseecom_registered_listener_list *svc)
+{
+	int ret;
+	ret = (svc->rcv_req_flag != 0);
+	return ret || data->abort;
+}
+
+static int qseecom_receive_req(struct qseecom_dev_handle *data)
+{
+	int ret = 0;
+	struct qseecom_registered_listener_list *this_lstnr;
+
+	this_lstnr = __qseecom_find_svc(data->listener.id);
+	while (1) {
+		if (wait_event_freezable(this_lstnr->rcv_req_wq,
+				__qseecom_listener_has_rcvd_req(data,
+				this_lstnr))) {
+			pr_warning("Interrupted: exiting wait_rcv_req loop\n");
+			/* woken up for different reason */
+			return -ERESTARTSYS;
+		}
+
+		if (data->abort) {
+			pr_err("Aborting driver!\n");
+			return -ENODEV;
+		}
+		this_lstnr->rcv_req_flag = 0;
+		if (qseecom.qseos_version == QSEOS_VERSION_13) {
+			if (*((uint32_t *)this_lstnr->sb_virt) != 0)
+				break;
+		} else {
+			break;
+		}
+	}
+	return ret;
+}
+
+static int qseecom_send_resp(void)
+{
+	qseecom.send_resp_flag = 1;
+	wake_up_interruptible(&qseecom.send_resp_wq);
+	return 0;
+}
+
+static int qseecom_get_qseos_version(struct qseecom_dev_handle *data,
+						void __user *argp)
+{
+	struct qseecom_qseos_version_req req;
+
+	if (copy_from_user(&req, argp, sizeof(req))) {
+		pr_err("copy_from_user failed");
+		return -EINVAL;
+	}
+	req.qseos_version = qseecom.qseos_version;
+	if (copy_to_user(argp, &req, sizeof(req))) {
+		pr_err("copy_to_user failed");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int qsee_vote_for_clock(int32_t clk_type)
+{
+	int ret = 0;
+
+	if (!qsee_perf_client)
+		return ret;
+
+	switch (clk_type) {
+	case CLK_DFAB:
+		/* Check if the clk is valid */
+		if (IS_ERR_OR_NULL(qseecom_bus_clk)) {
+			pr_warn("qseecom bus clock is null or error");
+			return -EINVAL;
+		}
+		mutex_lock(&qsee_bw_mutex);
+		if (!qsee_bw_count) {
+			ret = msm_bus_scale_client_update_request(
+					qsee_perf_client, 1);
+			if (ret)
+				pr_err("DFAB Bandwidth req failed (%d)\n",
+								ret);
+			else
+				qsee_bw_count++;
+		}
+		mutex_unlock(&qsee_bw_mutex);
+		break;
+	case CLK_SFPB:
+		mutex_lock(&qsee_sfpb_bw_mutex);
+		if (!qsee_sfpb_bw_count) {
+			ret = msm_bus_scale_client_update_request(
+					qsee_perf_client, 2);
+			if (ret)
+				pr_err("SFPB Bandwidth req failed (%d)\n",
+								ret);
+			else
+				qsee_sfpb_bw_count++;
+		}
+		mutex_unlock(&qsee_sfpb_bw_mutex);
+		break;
+	default:
+		pr_err("Clock type not defined\n");
+		break;
+	}
+	return ret;
+}
+
+static void qsee_disable_clock_vote(int32_t clk_type)
+{
+	int32_t ret = 0;
+
+	if (!qsee_perf_client)
+		return;
+
+	switch (clk_type) {
+	case CLK_DFAB:
+		/* Check if the DFAB clk is valid */
+		if (IS_ERR_OR_NULL(qseecom_bus_clk)) {
+			pr_warn("qseecom bus clock is null or error");
+			return;
+		}
+		mutex_lock(&qsee_bw_mutex);
+		if (qsee_bw_count > 0) {
+			if (qsee_bw_count-- == 1) {
+				ret = msm_bus_scale_client_update_request(
+						qsee_perf_client, 0);
+				if (ret)
+					pr_err("SFPB Bandwidth req fail (%d)\n",
+								ret);
+			}
+		}
+		mutex_unlock(&qsee_bw_mutex);
+		break;
+	case CLK_SFPB:
+		mutex_lock(&qsee_sfpb_bw_mutex);
+		if (qsee_sfpb_bw_count > 0) {
+			if (qsee_sfpb_bw_count-- == 1) {
+				ret = msm_bus_scale_client_update_request(
+						qsee_perf_client, 0);
+				if (ret)
+					pr_err("SFPB Bandwidth req fail (%d)\n",
+								ret);
+			}
+		}
+		mutex_unlock(&qsee_sfpb_bw_mutex);
+		break;
+	default:
+		pr_err("Clock type not defined\n");
+		break;
+	}
+
+}
+
+static int qseecom_load_external_elf(struct qseecom_dev_handle *data,
+				void __user *argp)
+{
+	struct ion_handle *ihandle;	/* Ion handle */
+	struct qseecom_load_img_req load_img_req;
+	int ret;
+	int set_cpu_ret = 0;
+	ion_phys_addr_t pa = 0;
+	uint32_t len;
+	struct cpumask mask;
+	struct qseecom_load_app_ireq load_req;
+	struct qseecom_command_scm_resp resp;
+
+	/* Copy the relevant information needed for loading the image */
+	if (__copy_from_user(&load_img_req,
+				(void __user *)argp,
+				sizeof(struct qseecom_load_img_req))) {
+		pr_err("copy_from_user failed\n");
+		return -EFAULT;
+	}
+
+	/* Get the handle of the shared fd */
+	ihandle = ion_import_fd(qseecom.ion_clnt,
+				load_img_req.ifd_data_fd);
+	if (IS_ERR_OR_NULL(ihandle)) {
+		pr_err("Ion client could not retrieve the handle\n");
+		return -ENOMEM;
+	}
+
+	/* Get the physical address of the ION BUF */
+	ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &len);
+
+	/* Populate the structure for sending scm call to load image */
+	load_req.qsee_cmd_id = QSEOS_LOAD_EXTERNAL_ELF_COMMAND;
+	load_req.mdt_len = load_img_req.mdt_len;
+	load_req.img_len = load_img_req.img_len;
+	load_req.phy_addr = pa;
+
+	/* SCM_CALL tied to Core0 */
+	mask = CPU_MASK_CPU0;
+	set_cpu_ret = set_cpus_allowed_ptr(current, &mask);
+	if (set_cpu_ret) {
+		pr_err("set_cpus_allowed_ptr failed : ret %d\n",
+				set_cpu_ret);
+		ret = -EFAULT;
+		goto qseecom_load_external_elf_set_cpu_err;
+	}
+
+	/*  SCM_CALL to load the external elf */
+	ret = scm_call(SCM_SVC_TZSCHEDULER, 1,  &load_req,
+			sizeof(struct qseecom_load_app_ireq),
+			&resp, sizeof(resp));
+	if (ret) {
+		pr_err("scm_call to load failed : ret %d\n",
+				ret);
+		ret = -EFAULT;
+		goto qseecom_load_external_elf_scm_err;
+	}
+
+	if (resp.result == QSEOS_RESULT_INCOMPLETE) {
+		ret = __qseecom_process_incomplete_cmd(data, &resp);
+		if (ret)
+			pr_err("process_incomplete_cmd failed err: %d\n",
+					ret);
+	} else {
+		if (resp.result != QSEOS_RESULT_SUCCESS) {
+			pr_err("scm_call to load image failed resp.result =%d\n",
+						resp.result);
+			ret = -EFAULT;
+		}
+	}
+
+qseecom_load_external_elf_scm_err:
+	/* Restore the CPU mask */
+	mask = CPU_MASK_ALL;
+	set_cpu_ret = set_cpus_allowed_ptr(current, &mask);
+	if (set_cpu_ret) {
+		pr_err("set_cpus_allowed_ptr failed to restore mask: ret %d\n",
+				set_cpu_ret);
+		ret = -EFAULT;
+	}
+
+qseecom_load_external_elf_set_cpu_err:
+	/* Deallocate the handle */
+	if (!IS_ERR_OR_NULL(ihandle))
+		ion_free(qseecom.ion_clnt, ihandle);
+
+	return ret;
+}
+
+static int qseecom_unload_external_elf(struct qseecom_dev_handle *data)
+{
+	int ret = 0;
+	int set_cpu_ret = 0;
+	struct qseecom_command_scm_resp resp;
+	struct qseecom_unload_app_ireq req;
+	struct cpumask mask;
+
+	/* Populate the structure for sending scm call to unload image */
+	req.qsee_cmd_id = QSEOS_UNLOAD_EXTERNAL_ELF_COMMAND;
+
+	/* SCM_CALL tied to Core0 */
+	mask = CPU_MASK_CPU0;
+	ret = set_cpus_allowed_ptr(current, &mask);
+	if (ret) {
+		pr_err("set_cpus_allowed_ptr failed : ret %d\n",
+				ret);
+		return -EFAULT;
+	}
+
+	/* SCM_CALL to unload the external elf */
+	ret = scm_call(SCM_SVC_TZSCHEDULER, 1,  &req,
+			sizeof(struct qseecom_unload_app_ireq),
+			&resp, sizeof(resp));
+	if (ret) {
+		pr_err("scm_call to unload failed : ret %d\n",
+				ret);
+		ret = -EFAULT;
+		goto qseecom_unload_external_elf_scm_err;
+	}
+	if (resp.result == QSEOS_RESULT_INCOMPLETE) {
+		ret = __qseecom_process_incomplete_cmd(data, &resp);
+		if (ret)
+			pr_err("process_incomplete_cmd fail err: %d\n",
+					ret);
+	} else {
+		if (resp.result != QSEOS_RESULT_SUCCESS) {
+			pr_err("scm_call to unload image failed resp.result =%d\n",
+						resp.result);
+			ret = -EFAULT;
+		}
+	}
+
+qseecom_unload_external_elf_scm_err:
+	/* Restore the CPU mask */
+	mask = CPU_MASK_ALL;
+	set_cpu_ret = set_cpus_allowed_ptr(current, &mask);
+	if (set_cpu_ret) {
+		pr_err("set_cpus_allowed_ptr failed to restore mask: ret %d\n",
+				set_cpu_ret);
+		ret = -EFAULT;
+	}
+
+	return ret;
+}
+
+static long qseecom_ioctl(struct file *file, unsigned cmd,
+		unsigned long arg)
+{
+	int ret = 0;
+	struct qseecom_dev_handle *data = file->private_data;
+	void __user *argp = (void __user *) arg;
+
+	if (data->abort) {
+		pr_err("Aborting qseecom driver\n");
+		return -ENODEV;
+	}
+
+	switch (cmd) {
+	case QSEECOM_IOCTL_REGISTER_LISTENER_REQ: {
+		pr_debug("ioctl register_listener_req()\n");
+		atomic_inc(&data->ioctl_count);
+		ret = qseecom_register_listener(data, argp);
+		atomic_dec(&data->ioctl_count);
+		wake_up_all(&data->abort_wq);
+		if (ret)
+			pr_err("failed qseecom_register_listener: %d\n", ret);
+		break;
+	}
+	case QSEECOM_IOCTL_UNREGISTER_LISTENER_REQ: {
+		pr_debug("ioctl unregister_listener_req()\n");
+		atomic_inc(&data->ioctl_count);
+		ret = qseecom_unregister_listener(data);
+		atomic_dec(&data->ioctl_count);
+		wake_up_all(&data->abort_wq);
+		if (ret)
+			pr_err("failed qseecom_unregister_listener: %d\n", ret);
+		break;
+	}
+	case QSEECOM_IOCTL_SEND_CMD_REQ: {
+		/* Only one client allowed here at a time */
+		mutex_lock(&send_msg_lock);
+		atomic_inc(&data->ioctl_count);
+		ret = qseecom_send_cmd(data, argp);
+		atomic_dec(&data->ioctl_count);
+		wake_up_all(&data->abort_wq);
+		mutex_unlock(&send_msg_lock);
+		if (ret)
+			pr_err("failed qseecom_send_cmd: %d\n", ret);
+		break;
+	}
+	case QSEECOM_IOCTL_SEND_MODFD_CMD_REQ: {
+		/* Only one client allowed here at a time */
+		mutex_lock(&send_msg_lock);
+		atomic_inc(&data->ioctl_count);
+		ret = qseecom_send_modfd_cmd(data, argp);
+		atomic_dec(&data->ioctl_count);
+		wake_up_all(&data->abort_wq);
+		mutex_unlock(&send_msg_lock);
+		if (ret)
+			pr_err("failed qseecom_send_cmd: %d\n", ret);
+		break;
+	}
+	case QSEECOM_IOCTL_RECEIVE_REQ: {
+		atomic_inc(&data->ioctl_count);
+		ret = qseecom_receive_req(data);
+		atomic_dec(&data->ioctl_count);
+		wake_up_all(&data->abort_wq);
+		if (ret)
+			pr_err("failed qseecom_receive_req: %d\n", ret);
+		break;
+	}
+	case QSEECOM_IOCTL_SEND_RESP_REQ: {
+		atomic_inc(&data->ioctl_count);
+		ret = qseecom_send_resp();
+		atomic_dec(&data->ioctl_count);
+		wake_up_all(&data->abort_wq);
+		if (ret)
+			pr_err("failed qseecom_send_resp: %d\n", ret);
+		break;
+	}
+	case QSEECOM_IOCTL_SET_MEM_PARAM_REQ: {
+		ret = qseecom_set_client_mem_param(data, argp);
+		if (ret)
+			pr_err("failed Qqseecom_set_mem_param request: %d\n",
+								ret);
+		break;
+	}
+	case QSEECOM_IOCTL_LOAD_APP_REQ: {
+		mutex_lock(&app_access_lock);
+		atomic_inc(&data->ioctl_count);
+		ret = qseecom_load_app(data, argp);
+		atomic_dec(&data->ioctl_count);
+		mutex_unlock(&app_access_lock);
+		if (ret)
+			pr_err("failed load_app request: %d\n", ret);
+		break;
+	}
+	case QSEECOM_IOCTL_UNLOAD_APP_REQ: {
+		mutex_lock(&app_access_lock);
+		atomic_inc(&data->ioctl_count);
+		ret = qseecom_unload_app(data);
+		atomic_dec(&data->ioctl_count);
+		mutex_unlock(&app_access_lock);
+		if (ret)
+			pr_err("failed unload_app request: %d\n", ret);
+		break;
+	}
+	case QSEECOM_IOCTL_GET_QSEOS_VERSION_REQ: {
+		atomic_inc(&data->ioctl_count);
+		ret = qseecom_get_qseos_version(data, argp);
+		if (ret)
+			pr_err("qseecom_get_qseos_version: %d\n", ret);
+		atomic_dec(&data->ioctl_count);
+		break;
+	}
+	case QSEECOM_IOCTL_PERF_ENABLE_REQ:{
+		atomic_inc(&data->ioctl_count);
+		ret = qsee_vote_for_clock(CLK_DFAB);
+		if (ret)
+			pr_err("Failed to vote for DFAB clock%d\n", ret);
+		atomic_dec(&data->ioctl_count);
+		break;
+	}
+	case QSEECOM_IOCTL_PERF_DISABLE_REQ:{
+		atomic_inc(&data->ioctl_count);
+		qsee_disable_clock_vote(CLK_DFAB);
+		atomic_dec(&data->ioctl_count);
+		break;
+	}
+	case QSEECOM_IOCTL_LOAD_EXTERNAL_ELF_REQ: {
+		data->released = true;
+		if (qseecom.qseos_version == QSEOS_VERSION_13) {
+			pr_err("Loading External elf image unsupported in rev 0x13\n");
+			ret = -EINVAL;
+			break;
+		}
+		mutex_lock(&app_access_lock);
+		atomic_inc(&data->ioctl_count);
+		ret = qseecom_load_external_elf(data, argp);
+		atomic_dec(&data->ioctl_count);
+		mutex_unlock(&app_access_lock);
+		if (ret)
+			pr_err("failed load_external_elf request: %d\n", ret);
+		break;
+	}
+	case QSEECOM_IOCTL_UNLOAD_EXTERNAL_ELF_REQ: {
+		data->released = true;
+		if (qseecom.qseos_version == QSEOS_VERSION_13) {
+			pr_err("Unloading External elf image unsupported in rev 0x13\n");
+			ret = -EINVAL;
+			break;
+		}
+		mutex_lock(&app_access_lock);
+		atomic_inc(&data->ioctl_count);
+		ret = qseecom_unload_external_elf(data);
+		atomic_dec(&data->ioctl_count);
+		mutex_unlock(&app_access_lock);
+		if (ret)
+			pr_err("failed unload_app request: %d\n", ret);
+		break;
+	}
+	default:
+		return -EINVAL;
+	}
+	return ret;
+}
+
+static int qseecom_open(struct inode *inode, struct file *file)
+{
+	int ret = 0;
+	struct qseecom_dev_handle *data;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data) {
+		pr_err("kmalloc failed\n");
+		return -ENOMEM;
+	}
+	file->private_data = data;
+	data->abort = 0;
+	data->service = false;
+	data->released = false;
+	init_waitqueue_head(&data->abort_wq);
+	atomic_set(&data->ioctl_count, 0);
+	if (qseecom.qseos_version == QSEOS_VERSION_13) {
+		int pil_error;
+		mutex_lock(&pil_access_lock);
+		if (pil_ref_cnt == 0) {
+			pil = pil_get("tzapps");
+			if (IS_ERR(pil)) {
+				pr_err("Playready PIL image load failed\n");
+				pil_error = PTR_ERR(pil);
+				pil = NULL;
+				pr_debug("tzapps image load FAILED\n");
+				mutex_unlock(&pil_access_lock);
+				return pil_error;
+			}
+		}
+		pil_ref_cnt++;
+		mutex_unlock(&pil_access_lock);
+	}
+	return ret;
+}
+
+static int qseecom_release(struct inode *inode, struct file *file)
+{
+	struct qseecom_dev_handle *data = file->private_data;
+	int ret = 0;
+
+	if (data->released == false) {
+		pr_warn("data->released == false\n");
+		if (data->service)
+			ret = qseecom_unregister_listener(data);
+		else
+			ret = qseecom_unload_app(data);
+		if (ret) {
+			pr_err("Close failed\n");
+			return ret;
+		}
+	}
+	if (qseecom.qseos_version == QSEOS_VERSION_13) {
+		mutex_lock(&pil_access_lock);
+		if (pil_ref_cnt == 1)
+			pil_put(pil);
+		pil_ref_cnt--;
+		mutex_unlock(&pil_access_lock);
+	}
+	kfree(data);
+	qsee_disable_clock_vote(CLK_DFAB);
+
+	return ret;
+}
+
+static const struct file_operations qseecom_fops = {
+		.owner = THIS_MODULE,
+		.unlocked_ioctl = qseecom_ioctl,
+		.open = qseecom_open,
+		.release = qseecom_release
+};
+
+static int __devinit qseecom_probe(struct platform_device *pdev)
+{
+	int rc;
+	struct device *class_dev;
+	char qsee_not_legacy = 0;
+	struct msm_bus_scale_pdata *qseecom_platform_support;
+	uint32_t system_call_id = QSEOS_CHECK_VERSION_CMD;
+
+	qsee_bw_count = 0;
+	qseecom_bus_clk = NULL;
+	qsee_perf_client = 0;
+
+	rc = alloc_chrdev_region(&qseecom_device_no, 0, 1, QSEECOM_DEV);
+	if (rc < 0) {
+		pr_err("alloc_chrdev_region failed %d\n", rc);
+		return rc;
+	}
+
+	driver_class = class_create(THIS_MODULE, QSEECOM_DEV);
+	if (IS_ERR(driver_class)) {
+		rc = -ENOMEM;
+		pr_err("class_create failed %d\n", rc);
+		goto unregister_chrdev_region;
+	}
+
+	class_dev = device_create(driver_class, NULL, qseecom_device_no, NULL,
+			QSEECOM_DEV);
+	if (!class_dev) {
+		pr_err("class_device_create failed %d\n", rc);
+		rc = -ENOMEM;
+		goto class_destroy;
+	}
+
+	cdev_init(&qseecom_cdev, &qseecom_fops);
+	qseecom_cdev.owner = THIS_MODULE;
+
+	rc = cdev_add(&qseecom_cdev, MKDEV(MAJOR(qseecom_device_no), 0), 1);
+	if (rc < 0) {
+		pr_err("cdev_add failed %d\n", rc);
+		goto err;
+	}
+
+	INIT_LIST_HEAD(&qseecom.registered_listener_list_head);
+	spin_lock_init(&qseecom.registered_listener_list_lock);
+	INIT_LIST_HEAD(&qseecom.registered_app_list_head);
+	spin_lock_init(&qseecom.registered_app_list_lock);
+	init_waitqueue_head(&qseecom.send_resp_wq);
+	qseecom.send_resp_flag = 0;
+
+	rc = scm_call(6, 1, &system_call_id, sizeof(system_call_id),
+				&qsee_not_legacy, sizeof(qsee_not_legacy));
+	if (rc) {
+		pr_err("Failed to retrieve QSEE version information %d\n", rc);
+		goto err;
+	}
+	if (qsee_not_legacy)
+		qseecom.qseos_version = QSEOS_VERSION_14;
+	else {
+		qseecom.qseos_version = QSEOS_VERSION_13;
+		pil = NULL;
+		pil_ref_cnt = 0;
+	}
+	/* Create ION msm client */
+	qseecom.ion_clnt = msm_ion_client_create(0x03, "qseecom-kernel");
+	if (qseecom.ion_clnt == NULL) {
+		pr_err("Ion client cannot be created\n");
+		rc = -ENOMEM;
+		goto err;
+	}
+
+	/* register client for bus scaling */
+	qseecom_platform_support = (struct msm_bus_scale_pdata *)
+					pdev->dev.platform_data;
+	qsee_perf_client = msm_bus_scale_register_client(
+					qseecom_platform_support);
+	if (!qsee_perf_client) {
+		pr_err("Unable to register bus client\n");
+	} else {
+		qseecom_bus_clk = clk_get(class_dev, "bus_clk");
+		if (IS_ERR(qseecom_bus_clk)) {
+			qseecom_bus_clk = NULL;
+		} else if (qseecom_bus_clk != NULL) {
+			pr_debug("Enabled DFAB clock");
+			clk_set_rate(qseecom_bus_clk, 64000000);
+		}
+	}
+	return 0;
+
+err:
+	device_destroy(driver_class, qseecom_device_no);
+class_destroy:
+	class_destroy(driver_class);
+unregister_chrdev_region:
+	unregister_chrdev_region(qseecom_device_no, 1);
+	return rc;
+}
+
+static int __devinit qseecom_remove(struct platform_device *pdev)
+{
+	if (pdev->dev.platform_data != NULL)
+		msm_bus_scale_unregister_client(qsee_perf_client);
+	return 0;
+};
+
+static struct platform_driver qseecom_plat_driver = {
+	.probe = qseecom_probe,
+	.remove = qseecom_remove,
+	.driver = {
+		.name = "qseecom",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __devinit qseecom_init(void)
+{
+	return platform_driver_register(&qseecom_plat_driver);
+}
+
+static void __devexit qseecom_exit(void)
+{
+	clk_put(qseecom_bus_clk);
+
+	device_destroy(driver_class, qseecom_device_no);
+	class_destroy(driver_class);
+	unregister_chrdev_region(qseecom_device_no, 1);
+	ion_client_destroy(qseecom.ion_clnt);
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Qualcomm Secure Execution Environment Communicator");
+
+module_init(qseecom_init);
+module_exit(qseecom_exit);
diff --git a/drivers/misc/qseecom_legacy.h b/drivers/misc/qseecom_legacy.h
new file mode 100644
index 0000000..66f87e9
--- /dev/null
+++ b/drivers/misc/qseecom_legacy.h
@@ -0,0 +1,79 @@
+/* Qualcomm Secure Execution Environment Communicator (QSEECOM) driver
+ *
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __QSEECOM_LEGACY_H_
+#define __QSEECOM_LEGACY_H_
+
+#include <linux/types.h>
+
+#define TZ_SCHED_CMD_ID_REGISTER_LISTENER    0x04
+
+enum tz_sched_cmd_type {
+	TZ_SCHED_CMD_INVALID = 0,
+	TZ_SCHED_CMD_NEW,      /* New TZ Scheduler Command */
+	TZ_SCHED_CMD_PENDING,  /* Pending cmd...sched will restore stack */
+	TZ_SCHED_CMD_COMPLETE, /* TZ sched command is complete */
+	TZ_SCHED_CMD_MAX     = 0x7FFFFFFF
+};
+
+enum tz_sched_cmd_status {
+	TZ_SCHED_STATUS_INCOMPLETE = 0,
+	TZ_SCHED_STATUS_COMPLETE,
+	TZ_SCHED_STATUS_MAX  = 0x7FFFFFFF
+};
+/* Command structure for initializing shared buffers */
+__packed struct qse_pr_init_sb_req_s {
+	/* First 4 bytes should always be command id */
+	uint32_t                  pr_cmd;
+	/* Pointer to the physical location of sb buffer */
+	uint32_t                  sb_ptr;
+	/* length of shared buffer */
+	uint32_t                  sb_len;
+	uint32_t                  listener_id;
+};
+
+__packed struct qse_pr_init_sb_rsp_s {
+	/* First 4 bytes should always be command id */
+	uint32_t                  pr_cmd;
+	/* Return code, 0 for success, Approp error code otherwise */
+	int32_t                   ret;
+};
+
+/*
+ * struct QSEECom_command - QSECom command buffer
+ * @cmd_type: value from enum tz_sched_cmd_type
+ * @sb_in_cmd_addr: points to physical location of command
+ *                buffer
+ * @sb_in_cmd_len: length of command buffer
+ */
+__packed struct qseecom_command {
+	uint32_t               cmd_type;
+	uint8_t                *sb_in_cmd_addr;
+	uint32_t               sb_in_cmd_len;
+};
+
+/*
+ * struct QSEECom_response - QSECom response buffer
+ * @cmd_status: value from enum tz_sched_cmd_status
+ * @sb_in_rsp_addr: points to physical location of response
+ *                buffer
+ * @sb_in_rsp_len: length of command response
+ */
+__packed struct qseecom_response {
+	uint32_t                 cmd_status;
+	uint8_t                  *sb_in_rsp_addr;
+	uint32_t                 sb_in_rsp_len;
+};
+
+#endif /* __QSEECOM_LEGACY_H_ */
diff --git a/drivers/misc/smsc_hub.c b/drivers/misc/smsc_hub.c
new file mode 100644
index 0000000..bde25d9
--- /dev/null
+++ b/drivers/misc/smsc_hub.c
@@ -0,0 +1,372 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/smsc3503.h>
+#include <linux/module.h>
+#include <mach/msm_xo.h>
+
+#define SMSC3503_I2C_ADDR 0x08
+#define SMSC_GSBI_I2C_BUS_ID 10
+static const unsigned short normal_i2c[] = {
+SMSC3503_I2C_ADDR, I2C_CLIENT_END };
+
+struct hsic_hub {
+	struct regulator *hsic_hub_reg;
+	struct device *dev;
+	struct i2c_client *client;
+	struct msm_xo_voter *xo_handle;
+};
+static struct hsic_hub *smsc_hub;
+
+/* APIs for setting/clearing bits and for reading/writing values */
+static inline int hsic_hub_get_u8(struct i2c_client *client, u8 reg)
+{
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(client, reg);
+	if (ret < 0)
+		pr_err("%s:i2c_read8 failed\n", __func__);
+	return ret;
+}
+
+static inline int hsic_hub_get_u16(struct i2c_client *client, u8 reg)
+{
+	int ret;
+
+	ret = i2c_smbus_read_word_data(client, reg);
+	if (ret < 0)
+		pr_err("%s:i2c_read16 failed\n", __func__);
+	return ret;
+}
+
+static inline int hsic_hub_write_word_data(struct i2c_client *client, u8 reg,
+						u16 value)
+{
+	int ret;
+
+	ret = i2c_smbus_write_word_data(client, reg, value);
+	if (ret)
+		pr_err("%s:i2c_write16 failed\n", __func__);
+	return ret;
+}
+
+static inline int hsic_hub_write_byte_data(struct i2c_client *client, u8 reg,
+						u8 value)
+{
+	int ret;
+
+	ret = i2c_smbus_write_byte_data(client, reg, value);
+	if (ret)
+		pr_err("%s:i2c_write_byte_data failed\n", __func__);
+	return ret;
+}
+
+static inline int hsic_hub_set_bits(struct i2c_client *client, u8 reg,
+					u8 value)
+{
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(client, reg);
+	if (ret < 0) {
+		pr_err("%s:i2c_read_byte_data failed\n", __func__);
+		return ret;
+	}
+	return i2c_smbus_write_byte_data(client, reg, (ret | value));
+}
+
+static inline int hsic_hub_clear_bits(struct i2c_client *client, u8 reg,
+					u8 value)
+{
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(client, reg);
+	if (ret < 0) {
+		pr_err("%s:i2c_read_byte_data failed\n", __func__);
+		return ret;
+	}
+	return i2c_smbus_write_byte_data(client, reg, (ret & ~value));
+}
+
+static int i2c_hsic_hub_probe(struct i2c_client *client,
+				const struct i2c_device_id *id)
+{
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+				     I2C_FUNC_SMBUS_WORD_DATA))
+		return -EIO;
+
+	/* CONFIG_N bit in SP_ILOCK register has to be set before changing
+	 * other registers to change default configuration of hsic hub.
+	 */
+	hsic_hub_set_bits(client, SMSC3503_SP_ILOCK, CONFIG_N);
+
+	/* Can change default configuartion like VID,PID, strings etc
+	 * by writing new values to hsic hub registers.
+	 */
+	hsic_hub_write_word_data(client, SMSC3503_VENDORID, 0x05C6);
+
+	/* CONFIG_N bit in SP_ILOCK register has to be cleared for new
+	 * values in registers to be effective after writing to
+	 * other registers.
+	 */
+	hsic_hub_clear_bits(client, SMSC3503_SP_ILOCK, CONFIG_N);
+
+	return 0;
+}
+
+static int i2c_hsic_hub_remove(struct i2c_client *client)
+{
+	return 0;
+}
+
+static const struct i2c_device_id hsic_hub_id[] = {
+	{"i2c_hsic_hub", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, hsichub_id);
+
+static struct i2c_driver hsic_hub_driver = {
+	.driver = {
+		.name = "i2c_hsic_hub",
+	},
+	.probe    = i2c_hsic_hub_probe,
+	.remove   = i2c_hsic_hub_remove,
+	.id_table = hsic_hub_id,
+};
+
+#define HSIC_HUB_VDD_VOL_MIN	1650000 /* uV */
+#define HSIC_HUB_VDD_VOL_MAX	1950000 /* uV */
+#define HSIC_HUB_VDD_LOAD	36000	/* uA */
+static int __devinit smsc_hub_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	const struct smsc_hub_platform_data *pdata;
+	struct i2c_adapter *i2c_adap;
+	struct i2c_board_info i2c_info;
+
+	if (!pdev->dev.platform_data) {
+		dev_err(&pdev->dev, "No platform data\n");
+		return -ENODEV;
+	}
+
+	pdata = pdev->dev.platform_data;
+	if (!pdata->hub_reset)
+		return -EINVAL;
+
+	smsc_hub = kzalloc(sizeof(*smsc_hub), GFP_KERNEL);
+	if (!smsc_hub)
+		return -ENOMEM;
+
+	smsc_hub->hsic_hub_reg = regulator_get(&pdev->dev, "EXT_HUB_VDDIO");
+	if (IS_ERR(smsc_hub->hsic_hub_reg)) {
+		dev_err(&pdev->dev, "unable to get ext hub vddcx\n");
+		ret = PTR_ERR(smsc_hub->hsic_hub_reg);
+		goto free_mem;
+	}
+
+	ret = gpio_request(pdata->hub_reset, "HSIC_HUB_RESET_GPIO");
+	if (ret < 0) {
+		dev_err(&pdev->dev, "gpio request failed for GPIO%d\n",
+							pdata->hub_reset);
+		goto gpio_req_fail;
+	}
+
+	ret = regulator_set_voltage(smsc_hub->hsic_hub_reg,
+			HSIC_HUB_VDD_VOL_MIN,
+			HSIC_HUB_VDD_VOL_MAX);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to set the voltage"
+				"for hsic hub reg\n");
+		goto reg_set_voltage_fail;
+	}
+
+	ret = regulator_set_optimum_mode(smsc_hub->hsic_hub_reg,
+				HSIC_HUB_VDD_LOAD);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Unable to set optimum mode of regulator:"
+							"VDDCX\n");
+		goto reg_optimum_mode_fail;
+	}
+
+	ret = regulator_enable(smsc_hub->hsic_hub_reg);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to enable ext hub vddcx\n");
+		goto reg_enable_fail;
+	}
+
+	smsc_hub->xo_handle = msm_xo_get(MSM_XO_TCXO_D1, "hsic_hub");
+	if (IS_ERR(smsc_hub->xo_handle)) {
+		dev_err(&pdev->dev, "not able to get the handle"
+					 "for TCXO D1 buffer\n");
+			goto disable_regulator;
+	}
+
+	ret = msm_xo_mode_vote(smsc_hub->xo_handle, MSM_XO_MODE_ON);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to vote for TCXO"
+			"D1 buffer\n");
+		goto xo_vote_fail;
+	}
+
+	gpio_direction_output(pdata->hub_reset, 0);
+	/* Hub reset should be asserted for minimum 2microsec
+	 * before deasserting.
+	 */
+	udelay(5);
+	gpio_direction_output(pdata->hub_reset, 1);
+
+	ret = i2c_add_driver(&hsic_hub_driver);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to add I2C hsic_hub_driver\n");
+		goto i2c_add_fail;
+	}
+	usleep_range(10000, 12000);
+	i2c_adap = i2c_get_adapter(SMSC_GSBI_I2C_BUS_ID);
+
+	if (!i2c_adap) {
+		dev_err(&pdev->dev, "failed to get i2c adapter\n");
+		i2c_del_driver(&hsic_hub_driver);
+		goto i2c_add_fail;
+	}
+
+	memset(&i2c_info, 0, sizeof(struct i2c_board_info));
+	strlcpy(i2c_info.type, "i2c_hsic_hub", I2C_NAME_SIZE);
+
+	smsc_hub->client = i2c_new_probed_device(i2c_adap, &i2c_info,
+						   normal_i2c, NULL);
+	i2c_put_adapter(i2c_adap);
+	if (!smsc_hub->client)
+		dev_err(&pdev->dev, "failed to connect to smsc_hub"
+			 "through I2C\n");
+
+i2c_add_fail:
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+
+	return 0;
+
+xo_vote_fail:
+	msm_xo_put(smsc_hub->xo_handle);
+disable_regulator:
+	regulator_disable(smsc_hub->hsic_hub_reg);
+reg_enable_fail:
+	regulator_set_optimum_mode(smsc_hub->hsic_hub_reg, 0);
+reg_optimum_mode_fail:
+	regulator_set_voltage(smsc_hub->hsic_hub_reg, 0,
+				HSIC_HUB_VDD_VOL_MIN);
+reg_set_voltage_fail:
+	gpio_free(pdata->hub_reset);
+gpio_req_fail:
+	regulator_put(smsc_hub->hsic_hub_reg);
+free_mem:
+	kfree(smsc_hub);
+
+	return ret;
+}
+
+static int smsc_hub_remove(struct platform_device *pdev)
+{
+	const struct smsc_hub_platform_data *pdata;
+
+	pdata = pdev->dev.platform_data;
+	if (smsc_hub->client) {
+		i2c_unregister_device(smsc_hub->client);
+		smsc_hub->client = NULL;
+		i2c_del_driver(&hsic_hub_driver);
+	}
+	pm_runtime_disable(&pdev->dev);
+	msm_xo_put(smsc_hub->xo_handle);
+
+	regulator_disable(smsc_hub->hsic_hub_reg);
+	regulator_set_optimum_mode(smsc_hub->hsic_hub_reg, 0);
+	regulator_set_voltage(smsc_hub->hsic_hub_reg, 0,
+				HSIC_HUB_VDD_VOL_MIN);
+	gpio_free(pdata->hub_reset);
+	regulator_put(smsc_hub->hsic_hub_reg);
+	kfree(smsc_hub);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_RUNTIME
+static int msm_smsc_runtime_idle(struct device *dev)
+{
+	dev_dbg(dev, "SMSC HUB runtime idle\n");
+
+	return 0;
+}
+
+static int smsc_hub_lpm_enter(struct device *dev)
+{
+	int ret;
+
+	ret = msm_xo_mode_vote(smsc_hub->xo_handle, MSM_XO_MODE_OFF);
+	if (ret) {
+		pr_err("%s: failed to devote for TCXO"
+			"D1 buffer%d\n", __func__, ret);
+	}
+	return ret;
+}
+
+static int smsc_hub_lpm_exit(struct device *dev)
+{
+	int ret;
+
+	ret = msm_xo_mode_vote(smsc_hub->xo_handle, MSM_XO_MODE_ON);
+	if (ret) {
+		pr_err("%s: failed to vote for TCXO"
+			"D1 buffer%d\n", __func__, ret);
+	}
+	return ret;
+}
+#endif
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops smsc_hub_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(smsc_hub_lpm_enter, smsc_hub_lpm_exit)
+	SET_RUNTIME_PM_OPS(smsc_hub_lpm_enter, smsc_hub_lpm_exit,
+				msm_smsc_runtime_idle)
+};
+#endif
+
+static struct platform_driver smsc_hub_driver = {
+	.driver = {
+		.name = "msm_smsc_hub",
+		.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm = &smsc_hub_dev_pm_ops,
+#endif
+	},
+	.remove = smsc_hub_remove,
+};
+
+static int __init smsc_hub_init(void)
+{
+	return platform_driver_probe(&smsc_hub_driver, smsc_hub_probe);
+}
+
+static void __exit smsc_hub_exit(void)
+{
+	platform_driver_unregister(&smsc_hub_driver);
+}
+subsys_initcall(smsc_hub_init);
+module_exit(smsc_hub_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("SMSC HSIC HUB driver");
diff --git a/drivers/misc/tsif.c b/drivers/misc/tsif.c
new file mode 100644
index 0000000..2b09d7c
--- /dev/null
+++ b/drivers/misc/tsif.c
@@ -0,0 +1,1580 @@
+/*
+ * TSIF Driver
+ *
+ * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/module.h>       /* Needed by all modules */
+#include <linux/kernel.h>       /* Needed for KERN_INFO */
+#include <linux/init.h>         /* Needed for the macros */
+#include <linux/err.h>          /* IS_ERR etc. */
+#include <linux/platform_device.h>
+
+#include <linux/ioport.h>       /* XXX_mem_region */
+#include <linux/debugfs.h>
+#include <linux/dma-mapping.h>  /* dma_XXX */
+#include <linux/delay.h>        /* msleep */
+
+#include <linux/io.h>             /* ioXXX */
+#include <linux/uaccess.h>        /* copy_from_user */
+#include <linux/clk.h>
+#include <linux/wakelock.h>
+#include <linux/tsif_api.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>          /* kfree, kzalloc */
+#include <linux/gpio.h>
+
+#include <mach/dma.h>
+#include <mach/msm_tsif.h>
+
+/*
+ * TSIF register offsets
+ */
+#define TSIF_STS_CTL_OFF               (0x0)
+#define TSIF_TIME_LIMIT_OFF            (0x4)
+#define TSIF_CLK_REF_OFF               (0x8)
+#define TSIF_LPBK_FLAGS_OFF            (0xc)
+#define TSIF_LPBK_DATA_OFF            (0x10)
+#define TSIF_TEST_CTL_OFF             (0x14)
+#define TSIF_TEST_MODE_OFF            (0x18)
+#define TSIF_TEST_RESET_OFF           (0x1c)
+#define TSIF_TEST_EXPORT_OFF          (0x20)
+#define TSIF_TEST_CURRENT_OFF         (0x24)
+
+#define TSIF_DATA_PORT_OFF            (0x100)
+
+/* bits for TSIF_STS_CTL register */
+#define TSIF_STS_CTL_EN_IRQ       (1 << 28)
+#define TSIF_STS_CTL_PACK_AVAIL   (1 << 27)
+#define TSIF_STS_CTL_1ST_PACKET   (1 << 26)
+#define TSIF_STS_CTL_OVERFLOW     (1 << 25)
+#define TSIF_STS_CTL_LOST_SYNC    (1 << 24)
+#define TSIF_STS_CTL_TIMEOUT      (1 << 23)
+#define TSIF_STS_CTL_INV_SYNC     (1 << 21)
+#define TSIF_STS_CTL_INV_NULL     (1 << 20)
+#define TSIF_STS_CTL_INV_ERROR    (1 << 19)
+#define TSIF_STS_CTL_INV_ENABLE   (1 << 18)
+#define TSIF_STS_CTL_INV_DATA     (1 << 17)
+#define TSIF_STS_CTL_INV_CLOCK    (1 << 16)
+#define TSIF_STS_CTL_SPARE        (1 << 15)
+#define TSIF_STS_CTL_EN_NULL      (1 << 11)
+#define TSIF_STS_CTL_EN_ERROR     (1 << 10)
+#define TSIF_STS_CTL_LAST_BIT     (1 <<  9)
+#define TSIF_STS_CTL_EN_TIME_LIM  (1 <<  8)
+#define TSIF_STS_CTL_EN_TCR       (1 <<  7)
+#define TSIF_STS_CTL_TEST_MODE    (3 <<  5)
+#define TSIF_STS_CTL_EN_DM        (1 <<  4)
+#define TSIF_STS_CTL_STOP         (1 <<  3)
+#define TSIF_STS_CTL_START        (1 <<  0)
+
+/*
+ * Data buffering parameters
+ *
+ * Data stored in cyclic buffer;
+ *
+ * Data organized in chunks of packets.
+ * One chunk processed at a time by the data mover
+ *
+ */
+#define TSIF_PKTS_IN_CHUNK_DEFAULT  (16)  /**< packets in one DM chunk */
+#define TSIF_CHUNKS_IN_BUF_DEFAULT   (8)
+#define TSIF_PKTS_IN_CHUNK        (tsif_device->pkts_per_chunk)
+#define TSIF_CHUNKS_IN_BUF        (tsif_device->chunks_per_buf)
+#define TSIF_PKTS_IN_BUF          (TSIF_PKTS_IN_CHUNK * TSIF_CHUNKS_IN_BUF)
+#define TSIF_BUF_SIZE             (TSIF_PKTS_IN_BUF * TSIF_PKT_SIZE)
+#define TSIF_MAX_ID               1
+
+#define ROW_RESET                 (MSM_CLK_CTL_BASE + 0x214)
+#define GLBL_CLK_ENA              (MSM_CLK_CTL_BASE + 0x000)
+#define CLK_HALT_STATEB           (MSM_CLK_CTL_BASE + 0x104)
+#define TSIF_NS_REG               (MSM_CLK_CTL_BASE + 0x0b4)
+#define TV_NS_REG                 (MSM_CLK_CTL_BASE + 0x0bc)
+
+/* used to create debugfs entries */
+static const struct {
+	const char *name;
+	mode_t mode;
+	int offset;
+} debugfs_tsif_regs[] = {
+	{"sts_ctl",      S_IRUGO | S_IWUSR, TSIF_STS_CTL_OFF},
+	{"time_limit",   S_IRUGO | S_IWUSR, TSIF_TIME_LIMIT_OFF},
+	{"clk_ref",      S_IRUGO | S_IWUSR, TSIF_CLK_REF_OFF},
+	{"lpbk_flags",   S_IRUGO | S_IWUSR, TSIF_LPBK_FLAGS_OFF},
+	{"lpbk_data",    S_IRUGO | S_IWUSR, TSIF_LPBK_DATA_OFF},
+	{"test_ctl",     S_IRUGO | S_IWUSR, TSIF_TEST_CTL_OFF},
+	{"test_mode",    S_IRUGO | S_IWUSR, TSIF_TEST_MODE_OFF},
+	{"test_reset",             S_IWUSR, TSIF_TEST_RESET_OFF},
+	{"test_export",  S_IRUGO | S_IWUSR, TSIF_TEST_EXPORT_OFF},
+	{"test_current", S_IRUGO,           TSIF_TEST_CURRENT_OFF},
+	{"data_port",    S_IRUSR,           TSIF_DATA_PORT_OFF},
+};
+
+/* structures for Data Mover */
+struct tsif_dmov_cmd {
+	dmov_box box;
+	dma_addr_t box_ptr;
+};
+
+struct msm_tsif_device;
+
+struct tsif_xfer {
+	struct msm_dmov_cmd hdr;
+	struct msm_tsif_device *tsif_device;
+	int busy;
+	int wi;   /**< set devices's write index after xfer */
+};
+
+struct msm_tsif_device {
+	struct list_head devlist;
+	struct platform_device *pdev;
+	struct resource *memres;
+	void __iomem *base;
+	unsigned int irq;
+	int mode;
+	u32 time_limit;
+	enum tsif_state state;
+	struct wake_lock wake_lock;
+	/* clocks */
+	struct clk *tsif_clk;
+	struct clk *tsif_pclk;
+	struct clk *tsif_ref_clk;
+	/* debugfs */
+	struct dentry *dent_tsif;
+	struct dentry *debugfs_tsif_regs[ARRAY_SIZE(debugfs_tsif_regs)];
+	struct dentry *debugfs_gpio;
+	struct dentry *debugfs_action;
+	struct dentry *debugfs_dma;
+	struct dentry *debugfs_databuf;
+	struct debugfs_blob_wrapper blob_wrapper_databuf;
+	/* DMA related */
+	int dma;
+	int crci;
+	void *data_buffer;
+	dma_addr_t data_buffer_dma;
+	u32 pkts_per_chunk;
+	u32 chunks_per_buf;
+	int ri;
+	int wi;
+	int dmwi;  /**< DataMover write index */
+	struct tsif_dmov_cmd *dmov_cmd[2];
+	dma_addr_t dmov_cmd_dma[2];
+	struct tsif_xfer xfer[2];
+	struct tasklet_struct dma_refill;
+	/* statistics */
+	u32 stat_rx;
+	u32 stat_overflow;
+	u32 stat_lost_sync;
+	u32 stat_timeout;
+	u32 stat_dmov_err;
+	u32 stat_soft_drop;
+	int stat_ifi; /* inter frame interval */
+	u32 stat0, stat1;
+	/* client */
+	void *client_data;
+	void (*client_notify)(void *client_data);
+};
+
+/* ===clocks begin=== */
+
+static void tsif_put_clocks(struct msm_tsif_device *tsif_device)
+{
+	if (tsif_device->tsif_clk) {
+		clk_put(tsif_device->tsif_clk);
+		tsif_device->tsif_clk = NULL;
+	}
+	if (tsif_device->tsif_pclk) {
+		clk_put(tsif_device->tsif_pclk);
+		tsif_device->tsif_pclk = NULL;
+	}
+
+	if (tsif_device->tsif_ref_clk) {
+		clk_put(tsif_device->tsif_ref_clk);
+		tsif_device->tsif_ref_clk = NULL;
+	}
+}
+
+static int tsif_get_clocks(struct msm_tsif_device *tsif_device)
+{
+	struct msm_tsif_platform_data *pdata =
+		tsif_device->pdev->dev.platform_data;
+	int rc = 0;
+
+	if (pdata->tsif_clk) {
+		tsif_device->tsif_clk = clk_get(&tsif_device->pdev->dev,
+						pdata->tsif_clk);
+		if (IS_ERR(tsif_device->tsif_clk)) {
+			dev_err(&tsif_device->pdev->dev, "failed to get %s\n",
+				pdata->tsif_clk);
+			rc = PTR_ERR(tsif_device->tsif_clk);
+			tsif_device->tsif_clk = NULL;
+			goto ret;
+		}
+	}
+	if (pdata->tsif_pclk) {
+		tsif_device->tsif_pclk = clk_get(&tsif_device->pdev->dev,
+						 pdata->tsif_pclk);
+		if (IS_ERR(tsif_device->tsif_pclk)) {
+			dev_err(&tsif_device->pdev->dev, "failed to get %s\n",
+				pdata->tsif_pclk);
+			rc = PTR_ERR(tsif_device->tsif_pclk);
+			tsif_device->tsif_pclk = NULL;
+			goto ret;
+		}
+	}
+	if (pdata->tsif_ref_clk) {
+		tsif_device->tsif_ref_clk = clk_get(&tsif_device->pdev->dev,
+						    pdata->tsif_ref_clk);
+		if (IS_ERR(tsif_device->tsif_ref_clk)) {
+			dev_err(&tsif_device->pdev->dev, "failed to get %s\n",
+				pdata->tsif_ref_clk);
+			rc = PTR_ERR(tsif_device->tsif_ref_clk);
+			tsif_device->tsif_ref_clk = NULL;
+			goto ret;
+		}
+	}
+	return 0;
+ret:
+	tsif_put_clocks(tsif_device);
+	return rc;
+}
+
+static void tsif_clock(struct msm_tsif_device *tsif_device, int on)
+{
+	if (on) {
+		if (tsif_device->tsif_clk)
+			clk_enable(tsif_device->tsif_clk);
+		if (tsif_device->tsif_pclk)
+			clk_enable(tsif_device->tsif_pclk);
+		clk_enable(tsif_device->tsif_ref_clk);
+	} else {
+		if (tsif_device->tsif_clk)
+			clk_disable(tsif_device->tsif_clk);
+		if (tsif_device->tsif_pclk)
+			clk_disable(tsif_device->tsif_pclk);
+		clk_disable(tsif_device->tsif_ref_clk);
+	}
+}
+/* ===clocks end=== */
+/* ===gpio begin=== */
+
+static void tsif_gpios_free(const struct msm_gpio *table, int size)
+{
+	int i;
+	const struct msm_gpio *g;
+	for (i = size-1; i >= 0; i--) {
+		g = table + i;
+		gpio_free(GPIO_PIN(g->gpio_cfg));
+	}
+}
+
+static int tsif_gpios_request(const struct msm_gpio *table, int size)
+{
+	int rc;
+	int i;
+	const struct msm_gpio *g;
+	for (i = 0; i < size; i++) {
+		g = table + i;
+		rc = gpio_request(GPIO_PIN(g->gpio_cfg), g->label);
+		if (rc) {
+			pr_err("gpio_request(%d) <%s> failed: %d\n",
+			       GPIO_PIN(g->gpio_cfg), g->label ?: "?", rc);
+			goto err;
+		}
+	}
+	return 0;
+err:
+	tsif_gpios_free(table, i);
+	return rc;
+}
+
+static int tsif_gpios_disable(const struct msm_gpio *table, int size)
+{
+	int rc = 0;
+	int i;
+	const struct msm_gpio *g;
+	for (i = size-1; i >= 0; i--) {
+		int tmp;
+		g = table + i;
+		tmp = gpio_tlmm_config(g->gpio_cfg, GPIO_CFG_DISABLE);
+		if (tmp) {
+			pr_err("gpio_tlmm_config(0x%08x, GPIO_CFG_DISABLE)"
+			       " <%s> failed: %d\n",
+			       g->gpio_cfg, g->label ?: "?", rc);
+			pr_err("pin %d func %d dir %d pull %d drvstr %d\n",
+			       GPIO_PIN(g->gpio_cfg), GPIO_FUNC(g->gpio_cfg),
+			       GPIO_DIR(g->gpio_cfg), GPIO_PULL(g->gpio_cfg),
+			       GPIO_DRVSTR(g->gpio_cfg));
+			if (!rc)
+				rc = tmp;
+		}
+	}
+
+	return rc;
+}
+
+static int tsif_gpios_enable(const struct msm_gpio *table, int size)
+{
+	int rc;
+	int i;
+	const struct msm_gpio *g;
+	for (i = 0; i < size; i++) {
+		g = table + i;
+		rc = gpio_tlmm_config(g->gpio_cfg, GPIO_CFG_ENABLE);
+		if (rc) {
+			pr_err("gpio_tlmm_config(0x%08x, GPIO_CFG_ENABLE)"
+			       " <%s> failed: %d\n",
+			       g->gpio_cfg, g->label ?: "?", rc);
+			pr_err("pin %d func %d dir %d pull %d drvstr %d\n",
+			       GPIO_PIN(g->gpio_cfg), GPIO_FUNC(g->gpio_cfg),
+			       GPIO_DIR(g->gpio_cfg), GPIO_PULL(g->gpio_cfg),
+			       GPIO_DRVSTR(g->gpio_cfg));
+			goto err;
+		}
+	}
+	return 0;
+err:
+	tsif_gpios_disable(table, i);
+	return rc;
+}
+
+static int tsif_gpios_request_enable(const struct msm_gpio *table, int size)
+{
+	int rc = tsif_gpios_request(table, size);
+	if (rc)
+		return rc;
+	rc = tsif_gpios_enable(table, size);
+	if (rc)
+		tsif_gpios_free(table, size);
+	return rc;
+}
+
+static void tsif_gpios_disable_free(const struct msm_gpio *table, int size)
+{
+	tsif_gpios_disable(table, size);
+	tsif_gpios_free(table, size);
+}
+
+static int tsif_start_gpios(struct msm_tsif_device *tsif_device)
+{
+	struct msm_tsif_platform_data *pdata =
+		tsif_device->pdev->dev.platform_data;
+	return tsif_gpios_request_enable(pdata->gpios, pdata->num_gpios);
+}
+
+static void tsif_stop_gpios(struct msm_tsif_device *tsif_device)
+{
+	struct msm_tsif_platform_data *pdata =
+		tsif_device->pdev->dev.platform_data;
+	tsif_gpios_disable_free(pdata->gpios, pdata->num_gpios);
+}
+
+/* ===gpio end=== */
+
+static int tsif_start_hw(struct msm_tsif_device *tsif_device)
+{
+	u32 ctl = TSIF_STS_CTL_EN_IRQ |
+		  TSIF_STS_CTL_EN_TIME_LIM |
+		  TSIF_STS_CTL_EN_TCR |
+		  TSIF_STS_CTL_EN_DM;
+	dev_info(&tsif_device->pdev->dev, "%s\n", __func__);
+	switch (tsif_device->mode) {
+	case 1: /* mode 1 */
+		ctl |= (0 << 5);
+		break;
+	case 2: /* mode 2 */
+		ctl |= (1 << 5);
+		break;
+	case 3: /* manual - control from debugfs */
+		return 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+	iowrite32(ctl, tsif_device->base + TSIF_STS_CTL_OFF);
+	iowrite32(tsif_device->time_limit,
+		  tsif_device->base + TSIF_TIME_LIMIT_OFF);
+	wmb();
+	iowrite32(ctl | TSIF_STS_CTL_START,
+		  tsif_device->base + TSIF_STS_CTL_OFF);
+	wmb();
+	ctl = ioread32(tsif_device->base + TSIF_STS_CTL_OFF);
+	return (ctl & TSIF_STS_CTL_START) ? 0 : -EFAULT;
+}
+
+static void tsif_stop_hw(struct msm_tsif_device *tsif_device)
+{
+	iowrite32(TSIF_STS_CTL_STOP, tsif_device->base + TSIF_STS_CTL_OFF);
+	wmb();
+}
+
+/* ===DMA begin=== */
+/**
+ * TSIF DMA theory of operation
+ *
+ * Circular memory buffer \a tsif_mem_buffer allocated;
+ * 4 pointers points to and moved forward on:
+ * - \a ri index of first ready to read packet.
+ *      Updated by client's call to tsif_reclaim_packets()
+ * - \a wi points to the next packet to be written by DM.
+ *      Data below is valid and will not be overriden by DMA.
+ *      Moved on DM callback
+ * - \a dmwi points to the next packet not scheduled yet for DM
+ *      moved when packet scheduled for DM
+ *
+ * In addition, DM xfer keep internal \a wi - copy of \a tsif_device->dmwi
+ * at time immediately after scheduling.
+ *
+ * Initially, 2 packets get scheduled for the DM.
+ *
+ * Upon packet receive, DM writes packet to the pre-programmed
+ * location and invoke its callback.
+ *
+ * DM callback moves sets wi pointer to \a xfer->wi;
+ * then it schedules next packet for DM and moves \a dmwi pointer.
+ *
+ * Buffer overflow handling
+ *
+ * If \a dmwi == \a ri-1, buffer is full and \a dmwi can't be advanced.
+ * DMA re-scheduled to the same index.
+ * Callback check and not move \a wi to become equal to \a ri
+ *
+ * On \a read request, data between \a ri and \a wi pointers may be read;
+ * \ri pointer moved accordingly.
+ *
+ * It is always granted, on modulo sizeof(tsif_mem_buffer), that
+ * \a wi is between [\a ri, \a dmwi]
+ *
+ * Amount of data available is (wi-ri)*TSIF_PKT_SIZE
+ *
+ * Number of scheduled packets for DM: (dmwi-wi)
+ */
+
+/**
+ * tsif_dma_schedule - schedule DMA transfers
+ *
+ * @tsif_device: device
+ *
+ * Executed from process context on init, or from tasklet when
+ * re-scheduling upon DMA completion.
+ * This prevent concurrent execution from several CPU's
+ */
+static void tsif_dma_schedule(struct msm_tsif_device *tsif_device)
+{
+	int i, dmwi0, dmwi1, found = 0;
+	/* find free entry */
+	for (i = 0; i < 2; i++) {
+		struct tsif_xfer *xfer = &tsif_device->xfer[i];
+		if (xfer->busy)
+			continue;
+		found++;
+		xfer->busy = 1;
+		dmwi0 = tsif_device->dmwi;
+		tsif_device->dmov_cmd[i]->box.dst_row_addr =
+			tsif_device->data_buffer_dma + TSIF_PKT_SIZE * dmwi0;
+		/* proposed value for dmwi */
+		dmwi1 = (dmwi0 + TSIF_PKTS_IN_CHUNK) % TSIF_PKTS_IN_BUF;
+		/**
+		 * If dmwi going to overlap with ri,
+		 * overflow occurs because data was not read.
+		 * Still get this packet, to not interrupt TSIF
+		 * hardware, but do not advance dmwi.
+		 *
+		 * Upon receive, packet will be dropped.
+		 */
+		if (dmwi1 != tsif_device->ri) {
+			tsif_device->dmwi = dmwi1;
+		} else {
+			dev_info(&tsif_device->pdev->dev,
+				 "Overflow detected\n");
+		}
+		xfer->wi = tsif_device->dmwi;
+#ifdef CONFIG_TSIF_DEBUG
+		dev_info(&tsif_device->pdev->dev,
+			"schedule xfer[%d] -> [%2d]{%2d}\n",
+			i, dmwi0, xfer->wi);
+#endif
+		/* complete all the writes to box */
+		dma_coherent_pre_ops();
+		msm_dmov_enqueue_cmd(tsif_device->dma, &xfer->hdr);
+	}
+	if (!found)
+		dev_info(&tsif_device->pdev->dev,
+			 "All xfer entries are busy\n");
+}
+
+/**
+ * tsif_dmov_complete_func - DataMover completion callback
+ *
+ * @cmd:      original DM command
+ * @result:   DM result
+ * @err:      optional error buffer
+ *
+ * Executed in IRQ context (Data Mover's IRQ)
+ * DataMover's spinlock @msm_dmov_lock held.
+ */
+static void tsif_dmov_complete_func(struct msm_dmov_cmd *cmd,
+				    unsigned int result,
+				    struct msm_dmov_errdata *err)
+{
+	int i;
+	u32 data_offset;
+	struct tsif_xfer *xfer;
+	struct msm_tsif_device *tsif_device;
+	int reschedule = 0;
+	if (!(result & DMOV_RSLT_VALID)) { /* can I trust to @cmd? */
+		pr_err("Invalid DMOV result: rc=0x%08x, cmd = %p", result, cmd);
+		return;
+	}
+	/* restore original context */
+	xfer = container_of(cmd, struct tsif_xfer, hdr);
+	tsif_device = xfer->tsif_device;
+	i = xfer - tsif_device->xfer;
+	data_offset = tsif_device->dmov_cmd[i]->box.dst_row_addr -
+		      tsif_device->data_buffer_dma;
+
+	/* order reads from the xferred buffer */
+	dma_coherent_post_ops();
+	if (result & DMOV_RSLT_DONE) {
+		int w = data_offset / TSIF_PKT_SIZE;
+		tsif_device->stat_rx++;
+		/*
+		 * sowtware overflow when I was scheduled?
+		 *
+		 * @w is where this xfer was actually written to;
+		 * @xfer->wi is where device's @wi will be set;
+		 *
+		 * if these 2 are equal, we are short in space and
+		 * going to overwrite this xfer - this is "soft drop"
+		 */
+		if (w == xfer->wi)
+			tsif_device->stat_soft_drop++;
+		reschedule = (tsif_device->state == tsif_state_running);
+#ifdef CONFIG_TSIF_DEBUG
+		/* IFI calculation */
+		/*
+		 * update stat_ifi (inter frame interval)
+		 *
+		 * Calculate time difference between last and 1-st
+		 * packets in chunk
+		 *
+		 * To be removed after tuning
+		 */
+		if (TSIF_PKTS_IN_CHUNK > 1) {
+			void *ptr = tsif_device->data_buffer + data_offset;
+			u32 *p0 = ptr;
+			u32 *p1 = ptr + (TSIF_PKTS_IN_CHUNK - 1) *
+				TSIF_PKT_SIZE;
+			u32 tts0 = TSIF_STATUS_TTS(tsif_device->stat0 =
+						   tsif_pkt_status(p0));
+			u32 tts1 = TSIF_STATUS_TTS(tsif_device->stat1 =
+						   tsif_pkt_status(p1));
+			tsif_device->stat_ifi = (tts1 - tts0) /
+				(TSIF_PKTS_IN_CHUNK - 1);
+		}
+#endif
+	} else {
+		/**
+		 *  Error or flush
+		 *
+		 *  To recover - re-open TSIF device.
+		 */
+		/* mark status "not valid" in data buffer */
+		int n;
+		void *ptr = tsif_device->data_buffer + data_offset;
+		for (n = 0; n < TSIF_PKTS_IN_CHUNK; n++) {
+			u32 *p = ptr + (n * TSIF_PKT_SIZE);
+			/* last dword is status + TTS */
+			p[TSIF_PKT_SIZE / sizeof(*p) - 1] = 0;
+		}
+		if (result & DMOV_RSLT_ERROR) {
+			dev_err(&tsif_device->pdev->dev,
+				"DMA error (0x%08x)\n", result);
+			tsif_device->stat_dmov_err++;
+			/* force device close */
+			if (tsif_device->state == tsif_state_running) {
+				tsif_stop_hw(tsif_device);
+				/*
+				 * Clocks _may_ be stopped right from IRQ
+				 * context. This is far from optimal w.r.t
+				 * latency.
+				 *
+				 * But, this branch taken only in case of
+				 * severe hardware problem (I don't even know
+				 * what should happens for DMOV_RSLT_ERROR);
+				 * thus I prefer code simplicity over
+				 * performance.
+				 */
+				tsif_clock(tsif_device, 0);
+				tsif_device->state = tsif_state_flushing;
+			}
+		}
+		if (result & DMOV_RSLT_FLUSH) {
+			/*
+			 * Flushing normally happens in process of
+			 * @tsif_stop(), when we are waiting for outstanding
+			 * DMA commands to be flushed.
+			 */
+			dev_info(&tsif_device->pdev->dev,
+				 "DMA channel flushed (0x%08x)\n", result);
+			if (tsif_device->state == tsif_state_flushing) {
+				if ((!tsif_device->xfer[0].busy) &&
+				    (!tsif_device->xfer[1].busy)) {
+					tsif_device->state = tsif_state_stopped;
+				}
+			}
+		}
+		if (err)
+			dev_err(&tsif_device->pdev->dev,
+				"Flush data: %08x %08x %08x %08x %08x %08x\n",
+				err->flush[0], err->flush[1], err->flush[2],
+				err->flush[3], err->flush[4], err->flush[5]);
+	}
+	tsif_device->wi = xfer->wi;
+	xfer->busy = 0;
+	if (tsif_device->client_notify)
+		tsif_device->client_notify(tsif_device->client_data);
+	/*
+	 * Can't schedule next DMA -
+	 * DataMover driver still hold its semaphore,
+	 * deadlock will occur.
+	 */
+	if (reschedule)
+		tasklet_schedule(&tsif_device->dma_refill);
+}
+
+/**
+ * tsif_dma_refill - tasklet function for tsif_device->dma_refill
+ *
+ * @data:   tsif_device
+ *
+ * Reschedule DMA requests
+ *
+ * Executed in tasklet
+ */
+static void tsif_dma_refill(unsigned long data)
+{
+	struct msm_tsif_device *tsif_device = (struct msm_tsif_device *) data;
+	if (tsif_device->state == tsif_state_running)
+		tsif_dma_schedule(tsif_device);
+}
+
+/**
+ * tsif_dma_flush - flush DMA channel
+ *
+ * @tsif_device:
+ *
+ * busy wait till DMA flushed
+ */
+static void tsif_dma_flush(struct msm_tsif_device *tsif_device)
+{
+	if (tsif_device->xfer[0].busy || tsif_device->xfer[1].busy) {
+		tsif_device->state = tsif_state_flushing;
+		while (tsif_device->xfer[0].busy ||
+		       tsif_device->xfer[1].busy) {
+			msm_dmov_flush(tsif_device->dma, 1);
+			msleep(10);
+		}
+	}
+	tsif_device->state = tsif_state_stopped;
+	if (tsif_device->client_notify)
+		tsif_device->client_notify(tsif_device->client_data);
+}
+
+static void tsif_dma_exit(struct msm_tsif_device *tsif_device)
+{
+	int i;
+	tsif_device->state = tsif_state_flushing;
+	tasklet_kill(&tsif_device->dma_refill);
+	tsif_dma_flush(tsif_device);
+	for (i = 0; i < 2; i++) {
+		if (tsif_device->dmov_cmd[i]) {
+			dma_free_coherent(NULL, sizeof(struct tsif_dmov_cmd),
+					  tsif_device->dmov_cmd[i],
+					  tsif_device->dmov_cmd_dma[i]);
+			tsif_device->dmov_cmd[i] = NULL;
+		}
+	}
+	if (tsif_device->data_buffer) {
+		tsif_device->blob_wrapper_databuf.data = NULL;
+		tsif_device->blob_wrapper_databuf.size = 0;
+		dma_free_coherent(NULL, TSIF_BUF_SIZE,
+				  tsif_device->data_buffer,
+				  tsif_device->data_buffer_dma);
+		tsif_device->data_buffer = NULL;
+	}
+}
+
+static int tsif_dma_init(struct msm_tsif_device *tsif_device)
+{
+	int i;
+	/* TODO: allocate all DMA memory in one buffer */
+	/* Note: don't pass device,
+	   it require coherent_dma_mask id device definition */
+	tsif_device->data_buffer = dma_alloc_coherent(NULL, TSIF_BUF_SIZE,
+				&tsif_device->data_buffer_dma, GFP_KERNEL);
+	if (!tsif_device->data_buffer)
+		goto err;
+	dev_info(&tsif_device->pdev->dev, "data_buffer: %p phys 0x%08x\n",
+		 tsif_device->data_buffer, tsif_device->data_buffer_dma);
+	tsif_device->blob_wrapper_databuf.data = tsif_device->data_buffer;
+	tsif_device->blob_wrapper_databuf.size = TSIF_BUF_SIZE;
+	tsif_device->ri = 0;
+	tsif_device->wi = 0;
+	tsif_device->dmwi = 0;
+	for (i = 0; i < 2; i++) {
+		dmov_box *box;
+		struct msm_dmov_cmd *hdr;
+		tsif_device->dmov_cmd[i] = dma_alloc_coherent(NULL,
+			sizeof(struct tsif_dmov_cmd),
+			&tsif_device->dmov_cmd_dma[i], GFP_KERNEL);
+		if (!tsif_device->dmov_cmd[i])
+			goto err;
+		dev_info(&tsif_device->pdev->dev, "dma[%i]: %p phys 0x%08x\n",
+			 i, tsif_device->dmov_cmd[i],
+			 tsif_device->dmov_cmd_dma[i]);
+		/* dst in 16 LSB, src in 16 MSB */
+		box = &(tsif_device->dmov_cmd[i]->box);
+		box->cmd = CMD_MODE_BOX | CMD_LC |
+			   CMD_SRC_CRCI(tsif_device->crci);
+		box->src_row_addr =
+			tsif_device->memres->start + TSIF_DATA_PORT_OFF;
+		box->src_dst_len = (TSIF_PKT_SIZE << 16) | TSIF_PKT_SIZE;
+		box->num_rows = (TSIF_PKTS_IN_CHUNK << 16) | TSIF_PKTS_IN_CHUNK;
+		box->row_offset = (0 << 16) | TSIF_PKT_SIZE;
+
+		tsif_device->dmov_cmd[i]->box_ptr = CMD_PTR_LP |
+			DMOV_CMD_ADDR(tsif_device->dmov_cmd_dma[i] +
+				      offsetof(struct tsif_dmov_cmd, box));
+		tsif_device->xfer[i].tsif_device = tsif_device;
+		hdr = &tsif_device->xfer[i].hdr;
+		hdr->cmdptr = DMOV_CMD_ADDR(tsif_device->dmov_cmd_dma[i] +
+			      offsetof(struct tsif_dmov_cmd, box_ptr));
+		hdr->complete_func = tsif_dmov_complete_func;
+	}
+	msm_dmov_flush(tsif_device->dma, 1);
+	return 0;
+err:
+	dev_err(&tsif_device->pdev->dev, "Failed to allocate DMA buffers\n");
+	tsif_dma_exit(tsif_device);
+	return -ENOMEM;
+}
+
+/* ===DMA end=== */
+
+/* ===IRQ begin=== */
+
+static irqreturn_t tsif_irq(int irq, void *dev_id)
+{
+	struct msm_tsif_device *tsif_device = dev_id;
+	u32 sts_ctl = ioread32(tsif_device->base + TSIF_STS_CTL_OFF);
+	if (!(sts_ctl & (TSIF_STS_CTL_PACK_AVAIL |
+			 TSIF_STS_CTL_OVERFLOW |
+			 TSIF_STS_CTL_LOST_SYNC |
+			 TSIF_STS_CTL_TIMEOUT))) {
+		dev_warn(&tsif_device->pdev->dev, "Spurious interrupt\n");
+		return IRQ_NONE;
+	}
+	if (sts_ctl & TSIF_STS_CTL_PACK_AVAIL) {
+		dev_info(&tsif_device->pdev->dev, "TSIF IRQ: PACK_AVAIL\n");
+		tsif_device->stat_rx++;
+	}
+	if (sts_ctl & TSIF_STS_CTL_OVERFLOW) {
+		dev_info(&tsif_device->pdev->dev, "TSIF IRQ: OVERFLOW\n");
+		tsif_device->stat_overflow++;
+	}
+	if (sts_ctl & TSIF_STS_CTL_LOST_SYNC) {
+		dev_info(&tsif_device->pdev->dev, "TSIF IRQ: LOST SYNC\n");
+		tsif_device->stat_lost_sync++;
+	}
+	if (sts_ctl & TSIF_STS_CTL_TIMEOUT) {
+		dev_info(&tsif_device->pdev->dev, "TSIF IRQ: TIMEOUT\n");
+		tsif_device->stat_timeout++;
+	}
+	iowrite32(sts_ctl, tsif_device->base + TSIF_STS_CTL_OFF);
+	wmb();
+	return IRQ_HANDLED;
+}
+
+/* ===IRQ end=== */
+
+/* ===Device attributes begin=== */
+
+static ssize_t show_stats(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	struct msm_tsif_device *tsif_device = dev_get_drvdata(dev);
+	char *state_string;
+	switch (tsif_device->state) {
+	case tsif_state_stopped:
+		state_string = "stopped";
+		break;
+	case tsif_state_running:
+		state_string = "running";
+		break;
+	case tsif_state_flushing:
+		state_string = "flushing";
+		break;
+	default:
+		state_string = "???";
+	}
+	return snprintf(buf, PAGE_SIZE,
+			"Device       %s\n"
+			"Mode       = %d\n"
+			"Time limit = %d\n"
+			"State        %s\n"
+			"Client     = %p\n"
+			"Pkt/Buf    = %d\n"
+			"Pkt/chunk  = %d\n"
+			"--statistics--\n"
+			"Rx chunks  = %d\n"
+			"Overflow   = %d\n"
+			"Lost sync  = %d\n"
+			"Timeout    = %d\n"
+			"DMA error  = %d\n"
+			"Soft drop  = %d\n"
+			"IFI        = %d\n"
+			"(0x%08x - 0x%08x) / %d\n"
+			"--debug--\n"
+			"GLBL_CLK_ENA     = 0x%08x\n"
+			"ROW_RESET        = 0x%08x\n"
+			"CLK_HALT_STATEB  = 0x%08x\n"
+			"TV_NS_REG        = 0x%08x\n"
+			"TSIF_NS_REG      = 0x%08x\n",
+			dev_name(dev),
+			tsif_device->mode,
+			tsif_device->time_limit,
+			state_string,
+			tsif_device->client_data,
+			TSIF_PKTS_IN_BUF,
+			TSIF_PKTS_IN_CHUNK,
+			tsif_device->stat_rx,
+			tsif_device->stat_overflow,
+			tsif_device->stat_lost_sync,
+			tsif_device->stat_timeout,
+			tsif_device->stat_dmov_err,
+			tsif_device->stat_soft_drop,
+			tsif_device->stat_ifi,
+			tsif_device->stat1,
+			tsif_device->stat0,
+			TSIF_PKTS_IN_CHUNK - 1,
+			ioread32(GLBL_CLK_ENA),
+			ioread32(ROW_RESET),
+			ioread32(CLK_HALT_STATEB),
+			ioread32(TV_NS_REG),
+			ioread32(TSIF_NS_REG)
+			);
+}
+/**
+ * set_stats - reset statistics on write
+ *
+ * @dev:
+ * @attr:
+ * @buf:
+ * @count:
+ */
+static ssize_t set_stats(struct device *dev, struct device_attribute *attr,
+			 const char *buf, size_t count)
+{
+	struct msm_tsif_device *tsif_device = dev_get_drvdata(dev);
+	tsif_device->stat_rx = 0;
+	tsif_device->stat_overflow = 0;
+	tsif_device->stat_lost_sync = 0;
+	tsif_device->stat_timeout = 0;
+	tsif_device->stat_dmov_err = 0;
+	tsif_device->stat_soft_drop = 0;
+	tsif_device->stat_ifi = 0;
+	return count;
+}
+static DEVICE_ATTR(stats, S_IRUGO | S_IWUSR, show_stats, set_stats);
+
+static ssize_t show_mode(struct device *dev, struct device_attribute *attr,
+			 char *buf)
+{
+	struct msm_tsif_device *tsif_device = dev_get_drvdata(dev);
+	return snprintf(buf, PAGE_SIZE, "%d\n", tsif_device->mode);
+}
+
+static ssize_t set_mode(struct device *dev, struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	struct msm_tsif_device *tsif_device = dev_get_drvdata(dev);
+	int value;
+	int rc;
+	if (1 != sscanf(buf, "%d", &value)) {
+		dev_err(&tsif_device->pdev->dev,
+			"Failed to parse integer: <%s>\n", buf);
+		return -EINVAL;
+	}
+	rc = tsif_set_mode(tsif_device, value);
+	if (!rc)
+		rc = count;
+	return rc;
+}
+static DEVICE_ATTR(mode, S_IRUGO | S_IWUSR, show_mode, set_mode);
+
+static ssize_t show_time_limit(struct device *dev,
+			       struct device_attribute *attr,
+			       char *buf)
+{
+	struct msm_tsif_device *tsif_device = dev_get_drvdata(dev);
+	return snprintf(buf, PAGE_SIZE, "%d\n", tsif_device->time_limit);
+}
+
+static ssize_t set_time_limit(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *buf, size_t count)
+{
+	struct msm_tsif_device *tsif_device = dev_get_drvdata(dev);
+	int value;
+	int rc;
+	if (1 != sscanf(buf, "%d", &value)) {
+		dev_err(&tsif_device->pdev->dev,
+			"Failed to parse integer: <%s>\n", buf);
+		return -EINVAL;
+	}
+	rc = tsif_set_time_limit(tsif_device, value);
+	if (!rc)
+		rc = count;
+	return rc;
+}
+static DEVICE_ATTR(time_limit, S_IRUGO | S_IWUSR,
+		   show_time_limit, set_time_limit);
+
+static ssize_t show_buf_config(struct device *dev,
+			       struct device_attribute *attr,
+			       char *buf)
+{
+	struct msm_tsif_device *tsif_device = dev_get_drvdata(dev);
+	return snprintf(buf, PAGE_SIZE, "%d * %d\n",
+			tsif_device->pkts_per_chunk,
+			tsif_device->chunks_per_buf);
+}
+
+static ssize_t set_buf_config(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *buf, size_t count)
+{
+	struct msm_tsif_device *tsif_device = dev_get_drvdata(dev);
+	u32 p, c;
+	int rc;
+	if (2 != sscanf(buf, "%d * %d", &p, &c)) {
+		dev_err(&tsif_device->pdev->dev,
+			"Failed to parse integer: <%s>\n", buf);
+		return -EINVAL;
+	}
+	rc = tsif_set_buf_config(tsif_device, p, c);
+	if (!rc)
+		rc = count;
+	return rc;
+}
+static DEVICE_ATTR(buf_config, S_IRUGO | S_IWUSR,
+		   show_buf_config, set_buf_config);
+
+static struct attribute *dev_attrs[] = {
+	&dev_attr_stats.attr,
+	&dev_attr_mode.attr,
+	&dev_attr_time_limit.attr,
+	&dev_attr_buf_config.attr,
+	NULL,
+};
+static struct attribute_group dev_attr_grp = {
+	.attrs = dev_attrs,
+};
+/* ===Device attributes end=== */
+
+/* ===debugfs begin=== */
+
+static int debugfs_iomem_x32_set(void *data, u64 val)
+{
+	iowrite32(val, data);
+	wmb();
+	return 0;
+}
+
+static int debugfs_iomem_x32_get(void *data, u64 *val)
+{
+	*val = ioread32(data);
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x32, debugfs_iomem_x32_get,
+			debugfs_iomem_x32_set, "0x%08llx\n");
+
+struct dentry *debugfs_create_iomem_x32(const char *name, mode_t mode,
+					struct dentry *parent, u32 *value)
+{
+	return debugfs_create_file(name, mode, parent, value, &fops_iomem_x32);
+}
+
+static int action_open(struct msm_tsif_device *tsif_device)
+{
+	int rc = -EINVAL;
+	int result;
+
+	struct msm_tsif_platform_data *pdata =
+		tsif_device->pdev->dev.platform_data;
+	dev_info(&tsif_device->pdev->dev, "%s\n", __func__);
+	if (tsif_device->state != tsif_state_stopped)
+		return -EAGAIN;
+	rc = tsif_dma_init(tsif_device);
+	if (rc) {
+		dev_err(&tsif_device->pdev->dev, "failed to init DMA\n");
+		return rc;
+	}
+	tsif_device->state = tsif_state_running;
+	/*
+	 * DMA should be scheduled prior to TSIF hardware initialization,
+	 * otherwise "bus error" will be reported by Data Mover
+	 */
+	enable_irq(tsif_device->irq);
+	tsif_clock(tsif_device, 1);
+	tsif_dma_schedule(tsif_device);
+	/*
+	 * init the device if required
+	 */
+	if (pdata->init)
+		pdata->init(pdata);
+	rc = tsif_start_hw(tsif_device);
+	if (rc) {
+		dev_err(&tsif_device->pdev->dev, "Unable to start HW\n");
+		tsif_dma_exit(tsif_device);
+		tsif_clock(tsif_device, 0);
+		return rc;
+	}
+
+	result = pm_runtime_get(&tsif_device->pdev->dev);
+	if (result < 0) {
+		dev_err(&tsif_device->pdev->dev,
+			"Runtime PM: Unable to wake up the device, rc = %d\n",
+			result);
+		return result;
+	}
+
+	wake_lock(&tsif_device->wake_lock);
+	return rc;
+}
+
+static int action_close(struct msm_tsif_device *tsif_device)
+{
+	dev_info(&tsif_device->pdev->dev, "%s, state %d\n", __func__,
+		 (int)tsif_device->state);
+	/*
+	 * DMA should be flushed/stopped prior to TSIF hardware stop,
+	 * otherwise "bus error" will be reported by Data Mover
+	 */
+	tsif_stop_hw(tsif_device);
+	tsif_dma_exit(tsif_device);
+	tsif_clock(tsif_device, 0);
+	disable_irq(tsif_device->irq);
+
+	pm_runtime_put(&tsif_device->pdev->dev);
+	wake_unlock(&tsif_device->wake_lock);
+	return 0;
+}
+
+
+static struct {
+	int (*func)(struct msm_tsif_device *);
+	const char *name;
+} actions[] = {
+	{ action_open,  "open"},
+	{ action_close, "close"},
+};
+
+static ssize_t tsif_debugfs_action_write(struct file *filp,
+					 const char __user *userbuf,
+					 size_t count, loff_t *f_pos)
+{
+	int i;
+	struct msm_tsif_device *tsif_device = filp->private_data;
+	char s[40];
+	int len = min(sizeof(s) - 1, count);
+	if (copy_from_user(s, userbuf, len))
+		return -EFAULT;
+	s[len] = '\0';
+	dev_info(&tsif_device->pdev->dev, "%s:%s\n", __func__, s);
+	for (i = 0; i < ARRAY_SIZE(actions); i++) {
+		if (!strncmp(s, actions[i].name,
+		    min(count, strlen(actions[i].name)))) {
+			int rc = actions[i].func(tsif_device);
+			if (!rc)
+				rc = count;
+			return rc;
+		}
+	}
+	return -EINVAL;
+}
+
+static int tsif_debugfs_generic_open(struct inode *inode, struct file *filp)
+{
+	filp->private_data = inode->i_private;
+	return 0;
+}
+
+static const struct file_operations fops_debugfs_action = {
+	.open  = tsif_debugfs_generic_open,
+	.write = tsif_debugfs_action_write,
+};
+
+static ssize_t tsif_debugfs_dma_read(struct file *filp, char __user *userbuf,
+				     size_t count, loff_t *f_pos)
+{
+	static char bufa[200];
+	static char *buf = bufa;
+	int sz = sizeof(bufa);
+	struct msm_tsif_device *tsif_device = filp->private_data;
+	int len = 0;
+	if (tsif_device) {
+		int i;
+		len += snprintf(buf + len, sz - len,
+				"ri %3d | wi %3d | dmwi %3d |",
+				tsif_device->ri, tsif_device->wi,
+				tsif_device->dmwi);
+		for (i = 0; i < 2; i++) {
+			struct tsif_xfer *xfer = &tsif_device->xfer[i];
+			if (xfer->busy) {
+				u32 dst =
+				    tsif_device->dmov_cmd[i]->box.dst_row_addr;
+				u32 base = tsif_device->data_buffer_dma;
+				int w = (dst - base) / TSIF_PKT_SIZE;
+				len += snprintf(buf + len, sz - len,
+						" [%3d]{%3d}",
+						w, xfer->wi);
+			} else {
+				len += snprintf(buf + len, sz - len,
+						" ---idle---");
+			}
+		}
+			len += snprintf(buf + len, sz - len, "\n");
+	} else {
+		len += snprintf(buf + len, sz - len, "No TSIF device???\n");
+	}
+	return simple_read_from_buffer(userbuf, count, f_pos, buf, len);
+}
+
+static const struct file_operations fops_debugfs_dma = {
+	.open = tsif_debugfs_generic_open,
+	.read = tsif_debugfs_dma_read,
+};
+
+static ssize_t tsif_debugfs_gpios_read(struct file *filp, char __user *userbuf,
+				       size_t count, loff_t *f_pos)
+{
+	static char bufa[300];
+	static char *buf = bufa;
+	int sz = sizeof(bufa);
+	struct msm_tsif_device *tsif_device = filp->private_data;
+	int len = 0;
+	if (tsif_device) {
+		struct msm_tsif_platform_data *pdata =
+			tsif_device->pdev->dev.platform_data;
+		int i;
+		for (i = 0; i < pdata->num_gpios; i++) {
+			if (pdata->gpios[i].gpio_cfg) {
+				int x = !!gpio_get_value(GPIO_PIN(
+					pdata->gpios[i].gpio_cfg));
+				len += snprintf(buf + len, sz - len,
+						"%15s: %d\n",
+						pdata->gpios[i].label, x);
+			}
+		}
+	} else {
+		len += snprintf(buf + len, sz - len, "No TSIF device???\n");
+	}
+	return simple_read_from_buffer(userbuf, count, f_pos, buf, len);
+}
+
+static const struct file_operations fops_debugfs_gpios = {
+	.open = tsif_debugfs_generic_open,
+	.read = tsif_debugfs_gpios_read,
+};
+
+
+static void tsif_debugfs_init(struct msm_tsif_device *tsif_device)
+{
+	tsif_device->dent_tsif = debugfs_create_dir(
+	      dev_name(&tsif_device->pdev->dev), NULL);
+	if (tsif_device->dent_tsif) {
+		int i;
+		void __iomem *base = tsif_device->base;
+		for (i = 0; i < ARRAY_SIZE(debugfs_tsif_regs); i++) {
+			tsif_device->debugfs_tsif_regs[i] =
+			   debugfs_create_iomem_x32(
+				debugfs_tsif_regs[i].name,
+				debugfs_tsif_regs[i].mode,
+				tsif_device->dent_tsif,
+				base + debugfs_tsif_regs[i].offset);
+		}
+		tsif_device->debugfs_gpio = debugfs_create_file("gpios",
+		    S_IRUGO,
+		    tsif_device->dent_tsif, tsif_device, &fops_debugfs_gpios);
+		tsif_device->debugfs_action = debugfs_create_file("action",
+		    S_IWUSR,
+		    tsif_device->dent_tsif, tsif_device, &fops_debugfs_action);
+		tsif_device->debugfs_dma = debugfs_create_file("dma",
+		    S_IRUGO,
+		    tsif_device->dent_tsif, tsif_device, &fops_debugfs_dma);
+		tsif_device->debugfs_databuf = debugfs_create_blob("data_buf",
+		    S_IRUGO,
+		    tsif_device->dent_tsif, &tsif_device->blob_wrapper_databuf);
+	}
+}
+
+static void tsif_debugfs_exit(struct msm_tsif_device *tsif_device)
+{
+	if (tsif_device->dent_tsif) {
+		int i;
+		debugfs_remove_recursive(tsif_device->dent_tsif);
+		tsif_device->dent_tsif = NULL;
+		for (i = 0; i < ARRAY_SIZE(debugfs_tsif_regs); i++)
+			tsif_device->debugfs_tsif_regs[i] = NULL;
+		tsif_device->debugfs_gpio = NULL;
+		tsif_device->debugfs_action = NULL;
+		tsif_device->debugfs_dma = NULL;
+		tsif_device->debugfs_databuf = NULL;
+	}
+}
+/* ===debugfs end=== */
+
+/* ===module begin=== */
+static LIST_HEAD(tsif_devices);
+
+static struct msm_tsif_device *tsif_find_by_id(int id)
+{
+	struct msm_tsif_device *tsif_device;
+	list_for_each_entry(tsif_device, &tsif_devices, devlist) {
+		if (tsif_device->pdev->id == id)
+			return tsif_device;
+	}
+	return NULL;
+}
+
+static int __devinit msm_tsif_probe(struct platform_device *pdev)
+{
+	int rc = -ENODEV;
+	struct msm_tsif_platform_data *plat = pdev->dev.platform_data;
+	struct msm_tsif_device *tsif_device;
+	struct resource *res;
+	/* check device validity */
+	/* must have platform data */
+	if (!plat) {
+		dev_err(&pdev->dev, "Platform data not available\n");
+		rc = -EINVAL;
+		goto out;
+	}
+
+	if ((pdev->id < 0) || (pdev->id > TSIF_MAX_ID)) {
+		dev_err(&pdev->dev, "Invalid device ID %d\n", pdev->id);
+		rc = -EINVAL;
+		goto out;
+	}
+	/* OK, we will use this device */
+	tsif_device = kzalloc(sizeof(struct msm_tsif_device), GFP_KERNEL);
+	if (!tsif_device) {
+		dev_err(&pdev->dev, "Failed to allocate memory for device\n");
+		rc = -ENOMEM;
+		goto out;
+	}
+	/* cross links */
+	tsif_device->pdev = pdev;
+	platform_set_drvdata(pdev, tsif_device);
+	tsif_device->mode = 1;
+	tsif_device->pkts_per_chunk = TSIF_PKTS_IN_CHUNK_DEFAULT;
+	tsif_device->chunks_per_buf = TSIF_CHUNKS_IN_BUF_DEFAULT;
+	tasklet_init(&tsif_device->dma_refill, tsif_dma_refill,
+		     (unsigned long)tsif_device);
+	if (tsif_get_clocks(tsif_device))
+		goto err_clocks;
+/* map I/O memory */
+	tsif_device->memres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!tsif_device->memres) {
+		dev_err(&pdev->dev, "Missing MEM resource\n");
+		rc = -ENXIO;
+		goto err_rgn;
+	}
+	res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "Missing DMA resource\n");
+		rc = -ENXIO;
+		goto err_rgn;
+	}
+	tsif_device->dma = res->start;
+	tsif_device->crci = res->end;
+	tsif_device->base = ioremap(tsif_device->memres->start,
+				    resource_size(tsif_device->memres));
+	if (!tsif_device->base) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		goto err_ioremap;
+	}
+	dev_info(&pdev->dev, "remapped phys 0x%08x => virt %p\n",
+		 tsif_device->memres->start, tsif_device->base);
+	rc = tsif_start_gpios(tsif_device);
+	if (rc)
+		goto err_gpio;
+
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+
+	tsif_debugfs_init(tsif_device);
+	rc = platform_get_irq(pdev, 0);
+	if (rc > 0) {
+		tsif_device->irq = rc;
+		rc = request_irq(tsif_device->irq, tsif_irq, IRQF_SHARED,
+				 dev_name(&pdev->dev), tsif_device);
+		disable_irq(tsif_device->irq);
+	}
+	if (rc) {
+		dev_err(&pdev->dev, "failed to request IRQ %d : %d\n",
+			tsif_device->irq, rc);
+		goto err_irq;
+	}
+	rc = sysfs_create_group(&pdev->dev.kobj, &dev_attr_grp);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to create dev. attrs : %d\n", rc);
+		goto err_attrs;
+	}
+	wake_lock_init(&tsif_device->wake_lock, WAKE_LOCK_SUSPEND,
+		       dev_name(&pdev->dev));
+	dev_info(&pdev->dev, "Configured irq %d memory 0x%08x DMA %d CRCI %d\n",
+		 tsif_device->irq, tsif_device->memres->start,
+		 tsif_device->dma, tsif_device->crci);
+	list_add(&tsif_device->devlist, &tsif_devices);
+	return 0;
+/* error path */
+	sysfs_remove_group(&pdev->dev.kobj, &dev_attr_grp);
+err_attrs:
+	free_irq(tsif_device->irq, tsif_device);
+err_irq:
+	tsif_debugfs_exit(tsif_device);
+	tsif_stop_gpios(tsif_device);
+err_gpio:
+	iounmap(tsif_device->base);
+err_ioremap:
+err_rgn:
+	tsif_put_clocks(tsif_device);
+err_clocks:
+	kfree(tsif_device);
+out:
+	return rc;
+}
+
+static int __devexit msm_tsif_remove(struct platform_device *pdev)
+{
+	struct msm_tsif_device *tsif_device = platform_get_drvdata(pdev);
+	dev_info(&pdev->dev, "Unload\n");
+	list_del(&tsif_device->devlist);
+	wake_lock_destroy(&tsif_device->wake_lock);
+	sysfs_remove_group(&pdev->dev.kobj, &dev_attr_grp);
+	free_irq(tsif_device->irq, tsif_device);
+	tsif_debugfs_exit(tsif_device);
+	tsif_dma_exit(tsif_device);
+	tsif_stop_gpios(tsif_device);
+	iounmap(tsif_device->base);
+	tsif_put_clocks(tsif_device);
+
+	pm_runtime_put(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+	kfree(tsif_device);
+	return 0;
+}
+
+static int tsif_runtime_suspend(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: suspending...\n");
+	return 0;
+}
+
+static int tsif_runtime_resume(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: resuming...\n");
+	return 0;
+}
+
+static const struct dev_pm_ops tsif_dev_pm_ops = {
+	.runtime_suspend = tsif_runtime_suspend,
+	.runtime_resume = tsif_runtime_resume,
+};
+
+
+static struct platform_driver msm_tsif_driver = {
+	.probe          = msm_tsif_probe,
+	.remove         = __exit_p(msm_tsif_remove),
+	.driver         = {
+		.name   = "msm_tsif",
+		.pm     = &tsif_dev_pm_ops,
+	},
+};
+
+static int __init mod_init(void)
+{
+	int rc = platform_driver_register(&msm_tsif_driver);
+	if (rc)
+		pr_err("TSIF: platform_driver_register failed: %d\n", rc);
+	return rc;
+}
+
+static void __exit mod_exit(void)
+{
+	platform_driver_unregister(&msm_tsif_driver);
+}
+/* ===module end=== */
+
+/* public API */
+
+int tsif_get_active(void)
+{
+	struct msm_tsif_device *tsif_device;
+	list_for_each_entry(tsif_device, &tsif_devices, devlist) {
+		return tsif_device->pdev->id;
+	}
+	return -ENODEV;
+}
+EXPORT_SYMBOL(tsif_get_active);
+
+void *tsif_attach(int id, void (*notify)(void *client_data), void *data)
+{
+	struct msm_tsif_device *tsif_device = tsif_find_by_id(id);
+	if (!tsif_device)
+		return ERR_PTR(-ENODEV);
+	if (tsif_device->client_notify || tsif_device->client_data)
+		return ERR_PTR(-EBUSY);
+	tsif_device->client_notify = notify;
+	tsif_device->client_data = data;
+	/* prevent from unloading */
+	get_device(&tsif_device->pdev->dev);
+	return tsif_device;
+}
+EXPORT_SYMBOL(tsif_attach);
+
+void tsif_detach(void *cookie)
+{
+	struct msm_tsif_device *tsif_device = cookie;
+	tsif_device->client_notify = NULL;
+	tsif_device->client_data = NULL;
+	put_device(&tsif_device->pdev->dev);
+}
+EXPORT_SYMBOL(tsif_detach);
+
+void tsif_get_info(void *cookie, void **pdata, int *psize)
+{
+	struct msm_tsif_device *tsif_device = cookie;
+	if (pdata)
+		*pdata = tsif_device->data_buffer;
+	if (psize)
+		*psize = TSIF_PKTS_IN_BUF;
+}
+EXPORT_SYMBOL(tsif_get_info);
+
+int tsif_set_mode(void *cookie, int mode)
+{
+	struct msm_tsif_device *tsif_device = cookie;
+	if (tsif_device->state != tsif_state_stopped) {
+		dev_err(&tsif_device->pdev->dev,
+			"Can't change mode while device is active\n");
+		return -EBUSY;
+	}
+	switch (mode) {
+	case 1:
+	case 2:
+	case 3:
+		tsif_device->mode = mode;
+		break;
+	default:
+		dev_err(&tsif_device->pdev->dev, "Invalid mode: %d\n", mode);
+		return -EINVAL;
+	}
+	return 0;
+}
+EXPORT_SYMBOL(tsif_set_mode);
+
+int tsif_set_time_limit(void *cookie, u32 value)
+{
+	struct msm_tsif_device *tsif_device = cookie;
+	if (tsif_device->state != tsif_state_stopped) {
+		dev_err(&tsif_device->pdev->dev,
+			"Can't change time limit while device is active\n");
+		return -EBUSY;
+	}
+	if (value != (value & 0xFFFFFF)) {
+		dev_err(&tsif_device->pdev->dev,
+			"Invalid time limit (should be 24 bit): %#x\n", value);
+		return -EINVAL;
+	}
+	tsif_device->time_limit = value;
+	return 0;
+}
+EXPORT_SYMBOL(tsif_set_time_limit);
+
+int tsif_set_buf_config(void *cookie, u32 pkts_in_chunk, u32 chunks_in_buf)
+{
+	struct msm_tsif_device *tsif_device = cookie;
+	if (tsif_device->data_buffer) {
+		dev_err(&tsif_device->pdev->dev,
+			"Data buffer already allocated: %p\n",
+			tsif_device->data_buffer);
+		return -EBUSY;
+	}
+	/* check for crazy user */
+	if (pkts_in_chunk * chunks_in_buf > 10240) {
+		dev_err(&tsif_device->pdev->dev,
+			"Buffer requested is too large: %d * %d\n",
+			pkts_in_chunk,
+			chunks_in_buf);
+		return -EINVAL;
+	}
+	/* parameters are OK, execute */
+	tsif_device->pkts_per_chunk = pkts_in_chunk;
+	tsif_device->chunks_per_buf = chunks_in_buf;
+	return 0;
+}
+EXPORT_SYMBOL(tsif_set_buf_config);
+
+void tsif_get_state(void *cookie, int *ri, int *wi, enum tsif_state *state)
+{
+	struct msm_tsif_device *tsif_device = cookie;
+	if (ri)
+		*ri    = tsif_device->ri;
+	if (wi)
+		*wi    = tsif_device->wi;
+	if (state)
+		*state = tsif_device->state;
+}
+EXPORT_SYMBOL(tsif_get_state);
+
+int tsif_start(void *cookie)
+{
+	struct msm_tsif_device *tsif_device = cookie;
+	return action_open(tsif_device);
+}
+EXPORT_SYMBOL(tsif_start);
+
+void tsif_stop(void *cookie)
+{
+	struct msm_tsif_device *tsif_device = cookie;
+	action_close(tsif_device);
+}
+EXPORT_SYMBOL(tsif_stop);
+
+void tsif_reclaim_packets(void *cookie, int read_index)
+{
+	struct msm_tsif_device *tsif_device = cookie;
+	tsif_device->ri = read_index;
+}
+EXPORT_SYMBOL(tsif_reclaim_packets);
+
+module_init(mod_init);
+module_exit(mod_exit);
+
+MODULE_DESCRIPTION("TSIF (Transport Stream Interface)"
+		   " Driver for the MSM chipset");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/misc/tsif_chrdev.c b/drivers/misc/tsif_chrdev.c
new file mode 100644
index 0000000..122c7ed
--- /dev/null
+++ b/drivers/misc/tsif_chrdev.c
@@ -0,0 +1,230 @@
+/**
+ * TSIF driver client
+ *
+ * Character device that, being read
+ * returns stream of TSIF packets.
+ *
+ * Copyright (c) 2009-2011, Code Aurora Forum. All rights
+ * reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>       /* Needed by all modules */
+#include <linux/kernel.h>       /* Needed for KERN_INFO */
+#include <linux/cdev.h>
+#include <linux/err.h>          /* IS_ERR etc. */
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/sched.h>        /* TASK_INTERRUPTIBLE */
+
+#include <linux/uaccess.h>        /* copy_to_user */
+
+#include <linux/tsif_api.h>
+
+struct tsif_chrdev {
+	struct cdev cdev;
+	struct device *dev;
+	wait_queue_head_t wq_read;
+	void *cookie;
+	/* mirror for tsif data */
+	void *data_buffer;
+	unsigned buf_size_packets; /**< buffer size in packets */
+	unsigned ri, wi;
+	enum tsif_state state;
+	unsigned rptr;
+};
+
+static ssize_t tsif_open(struct inode *inode, struct file *file)
+{
+	int rc;
+	struct tsif_chrdev *the_dev =
+	       container_of(inode->i_cdev, struct tsif_chrdev, cdev);
+	if (!the_dev->cookie)  /* not bound yet */
+		return -ENODEV;
+	file->private_data = the_dev;
+	rc = tsif_start(the_dev->cookie);
+	if (rc)
+		return rc;
+	tsif_get_info(the_dev->cookie, &the_dev->data_buffer,
+		      &the_dev->buf_size_packets);
+	the_dev->rptr = 0;
+	return nonseekable_open(inode, file);
+}
+
+static ssize_t tsif_release(struct inode *inode, struct file *filp)
+{
+	struct tsif_chrdev *the_dev = filp->private_data;
+	tsif_stop(the_dev->cookie);
+	return 0;
+}
+
+static ssize_t tsif_read(struct file *filp, char __user *buf, size_t count,
+			 loff_t *f_pos)
+{
+	int avail = 0;
+	int wi;
+	struct tsif_chrdev *the_dev = filp->private_data;
+	tsif_get_state(the_dev->cookie, &the_dev->ri, &the_dev->wi,
+		       &the_dev->state);
+	/* consistency check */
+	if (the_dev->ri != (the_dev->rptr / TSIF_PKT_SIZE)) {
+		dev_err(the_dev->dev,
+			"%s: inconsistent read pointers: ri %d rptr %d\n",
+			__func__, the_dev->ri, the_dev->rptr);
+		the_dev->rptr = the_dev->ri * TSIF_PKT_SIZE;
+	}
+	/* ri == wi if no data */
+	if (the_dev->ri == the_dev->wi) {
+		/* shall I block waiting for data? */
+		if (filp->f_flags & O_NONBLOCK) {
+			if (the_dev->state == tsif_state_running) {
+				return -EAGAIN;
+			} else {
+				/* not running -> EOF */
+				return 0;
+			}
+		}
+		if (wait_event_interruptible(the_dev->wq_read,
+		      (the_dev->ri != the_dev->wi) ||
+		      (the_dev->state != tsif_state_running))) {
+			/* got signal -> tell FS to handle it */
+			return -ERESTARTSYS;
+		}
+		if (the_dev->ri == the_dev->wi) {
+			/* still no data -> EOF */
+			return 0;
+		}
+	}
+	/* contiguous chunk last up to wi or end of buffer */
+	wi = (the_dev->wi > the_dev->ri) ?
+		the_dev->wi : the_dev->buf_size_packets;
+	avail = min(wi * TSIF_PKT_SIZE - the_dev->rptr, count);
+	if (copy_to_user(buf, the_dev->data_buffer + the_dev->rptr, avail))
+		return -EFAULT;
+	the_dev->rptr = (the_dev->rptr + avail) %
+		(TSIF_PKT_SIZE * the_dev->buf_size_packets);
+	the_dev->ri = the_dev->rptr / TSIF_PKT_SIZE;
+	*f_pos += avail;
+	tsif_reclaim_packets(the_dev->cookie, the_dev->ri);
+	return avail;
+}
+
+static void tsif_notify(void *data)
+{
+	struct tsif_chrdev *the_dev = data;
+	tsif_get_state(the_dev->cookie, &the_dev->ri, &the_dev->wi,
+		       &the_dev->state);
+	wake_up_interruptible(&the_dev->wq_read);
+}
+
+static const struct file_operations tsif_fops = {
+	.owner   = THIS_MODULE,
+	.read    = tsif_read,
+	.open    = tsif_open,
+	.release = tsif_release,
+};
+
+static struct class *tsif_class;
+static dev_t tsif_dev;  /**< 1-st dev_t from allocated range */
+static dev_t tsif_dev0; /**< next not yet assigned dev_t */
+
+static int tsif_init_one(struct tsif_chrdev *the_dev, int index)
+{
+	int rc;
+	pr_info("%s[%d]\n", __func__, index);
+	cdev_init(&the_dev->cdev, &tsif_fops);
+	the_dev->cdev.owner = THIS_MODULE;
+	init_waitqueue_head(&the_dev->wq_read);
+	rc = cdev_add(&the_dev->cdev, tsif_dev0++, 1);
+	the_dev->dev = device_create(tsif_class, NULL, the_dev->cdev.dev,
+				     the_dev, "tsif%d", index);
+	if (IS_ERR(the_dev->dev)) {
+		rc = PTR_ERR(the_dev->dev);
+		pr_err("device_create failed: %d\n", rc);
+		goto err_create;
+	}
+	the_dev->cookie = tsif_attach(index, tsif_notify, the_dev);
+	if (IS_ERR(the_dev->cookie)) {
+		rc = PTR_ERR(the_dev->cookie);
+		pr_err("tsif_attach failed: %d\n", rc);
+		goto err_attach;
+	}
+	/* now data buffer is not allocated yet */
+	tsif_get_info(the_dev->cookie, &the_dev->data_buffer, NULL);
+	dev_info(the_dev->dev,
+		 "Device %d.%d attached to TSIF, buffer size %d\n",
+		 MAJOR(the_dev->cdev.dev), MINOR(the_dev->cdev.dev),
+		 the_dev->buf_size_packets);
+	return 0;
+err_attach:
+	device_destroy(tsif_class, the_dev->cdev.dev);
+err_create:
+	cdev_del(&the_dev->cdev);
+	return rc;
+}
+
+static void tsif_exit_one(struct tsif_chrdev *the_dev)
+{
+	dev_info(the_dev->dev, "%s\n", __func__);
+	tsif_detach(the_dev->cookie);
+	device_destroy(tsif_class, the_dev->cdev.dev);
+	cdev_del(&the_dev->cdev);
+}
+
+#define TSIF_NUM_DEVS 1 /**< support this many devices */
+
+struct tsif_chrdev the_devices[TSIF_NUM_DEVS];
+
+static int __init mod_init(void)
+{
+	int rc;
+	int instance;
+	rc = alloc_chrdev_region(&tsif_dev, 0, TSIF_NUM_DEVS, "tsif");
+	if (rc) {
+		pr_err("alloc_chrdev_region failed: %d\n", rc);
+		goto err_devrgn;
+	}
+	tsif_dev0 = tsif_dev;
+	tsif_class = class_create(THIS_MODULE, "tsif");
+	if (IS_ERR(tsif_class)) {
+		rc = PTR_ERR(tsif_class);
+		pr_err("Error creating tsif class: %d\n", rc);
+		goto err_class;
+	}
+	instance = tsif_get_active();
+	if (instance >= 0)
+		rc = tsif_init_one(&the_devices[0], instance);
+	else
+		rc = instance;
+	if (rc)
+		goto err_init1;
+	return 0;
+err_init1:
+	class_destroy(tsif_class);
+err_class:
+	unregister_chrdev_region(tsif_dev, TSIF_NUM_DEVS);
+err_devrgn:
+	return rc;
+}
+
+static void __exit mod_exit(void)
+{
+	tsif_exit_one(&the_devices[0]);
+	class_destroy(tsif_class);
+	unregister_chrdev_region(tsif_dev, TSIF_NUM_DEVS);
+}
+
+module_init(mod_init);
+module_exit(mod_exit);
+
+MODULE_DESCRIPTION("TSIF character device interface");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/misc/tspp.c b/drivers/misc/tspp.c
new file mode 100644
index 0000000..81c6b65
--- /dev/null
+++ b/drivers/misc/tspp.c
@@ -0,0 +1,2019 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>			/* Needed by all modules */
+#include <linux/kernel.h>			/* Needed for KERN_INFO */
+#include <linux/init.h>				/* Needed for the macros */
+#include <linux/cdev.h>
+#include <linux/err.h>				/* IS_ERR */
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/sched.h>		/* TASK_INTERRUPTIBLE */
+#include <linux/uaccess.h>        /* copy_to_user */
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>          /* kfree, kzalloc */
+#include <linux/wakelock.h>
+#include <linux/io.h>            /* ioXXX */
+#include <linux/ioport.h>		/* XXX_ mem_region */
+#include <linux/dma-mapping.h>		/* dma_XXX */
+#include <linux/delay.h>		/* msleep */
+#include <linux/clk.h>
+#include <linux/poll.h>				/* poll() file op */
+#include <linux/wait.h>				/* wait() macros, sleeping */
+#include <linux/tspp.h>				/* tspp functions */
+#include <linux/bitops.h>        /* BIT() macro */
+#include <mach/sps.h>				/* BAM stuff */
+#include <mach/gpio.h>
+#include <mach/dma.h>
+#include <mach/msm_tspp.h>
+
+#define TSPP_USE_DEBUGFS
+#ifdef TSPP_USE_DEBUGFS
+#include <linux/debugfs.h>
+#endif /* TSPP_USE_DEBUGFS */
+
+/*
+ * General defines
+ */
+#define TSPP_USE_DMA_ALLOC_COHERENT
+#define TSPP_TSIF_INSTANCES            2
+#define TSPP_FILTER_TABLES             3
+#define TSPP_MAX_DEVICES               3
+#define TSPP_NUM_CHANNELS              16
+#define TSPP_NUM_PRIORITIES            16
+#define TSPP_NUM_KEYS                  8
+#define INVALID_CHANNEL                0xFFFFFFFF
+#define TSPP_SPS_DESCRIPTOR_COUNT      32
+#define TSPP_PACKET_LENGTH             188
+#define TSPP_MIN_BUFFER_SIZE           (TSPP_PACKET_LENGTH)
+#define TSPP_MAX_BUFFER_SIZE           (16 * 1024)	 /* maybe allow 64K? */
+#define TSPP_NUM_BUFFERS               16
+#define TSPP_TSIF_DEFAULT_TIME_LIMIT   60
+#define SPS_DESCRIPTOR_SIZE            8
+#define MIN_ACCEPTABLE_BUFFER_COUNT    2
+#define TSPP_DEBUG(msg...)             pr_info(msg)
+
+/*
+ * TSIF register offsets
+ */
+#define TSIF_STS_CTL_OFF               (0x0)
+#define TSIF_TIME_LIMIT_OFF            (0x4)
+#define TSIF_CLK_REF_OFF               (0x8)
+#define TSIF_LPBK_FLAGS_OFF            (0xc)
+#define TSIF_LPBK_DATA_OFF            (0x10)
+#define TSIF_TEST_CTL_OFF             (0x14)
+#define TSIF_TEST_MODE_OFF            (0x18)
+#define TSIF_TEST_RESET_OFF           (0x1c)
+#define TSIF_TEST_EXPORT_OFF          (0x20)
+#define TSIF_TEST_CURRENT_OFF         (0x24)
+
+#define TSIF_DATA_PORT_OFF            (0x100)
+
+/* bits for TSIF_STS_CTL register */
+#define TSIF_STS_CTL_EN_IRQ       BIT(28)
+#define TSIF_STS_CTL_PACK_AVAIL   BIT(27)
+#define TSIF_STS_CTL_1ST_PACKET   BIT(26)
+#define TSIF_STS_CTL_OVERFLOW     BIT(25)
+#define TSIF_STS_CTL_LOST_SYNC    BIT(24)
+#define TSIF_STS_CTL_TIMEOUT      BIT(23)
+#define TSIF_STS_CTL_INV_SYNC     BIT(21)
+#define TSIF_STS_CTL_INV_NULL     BIT(20)
+#define TSIF_STS_CTL_INV_ERROR    BIT(19)
+#define TSIF_STS_CTL_INV_ENABLE   BIT(18)
+#define TSIF_STS_CTL_INV_DATA     BIT(17)
+#define TSIF_STS_CTL_INV_CLOCK    BIT(16)
+#define TSIF_STS_CTL_SPARE        BIT(15)
+#define TSIF_STS_CTL_EN_NULL      BIT(11)
+#define TSIF_STS_CTL_EN_ERROR     BIT(10)
+#define TSIF_STS_CTL_LAST_BIT     BIT(9)
+#define TSIF_STS_CTL_EN_TIME_LIM  BIT(8)
+#define TSIF_STS_CTL_EN_TCR       BIT(7)
+#define TSIF_STS_CTL_TEST_MODE    BIT(6)
+#define TSIF_STS_CTL_EN_DM        BIT(4)
+#define TSIF_STS_CTL_STOP         BIT(3)
+#define TSIF_STS_CTL_START        BIT(0)
+
+/*
+ * TSPP register offsets
+ */
+#define TSPP_RST					0x00
+#define TSPP_CLK_CONTROL		0x04
+#define TSPP_CONFIG				0x08
+#define TSPP_CONTROL				0x0C
+#define TSPP_PS_DISABLE			0x10
+#define TSPP_MSG_IRQ_STATUS	0x14
+#define TSPP_MSG_IRQ_MASK		0x18
+#define TSPP_IRQ_STATUS			0x1C
+#define TSPP_IRQ_MASK			0x20
+#define TSPP_IRQ_CLEAR			0x24
+#define TSPP_PIPE_ERROR_STATUS(_n)	(0x28 + (_n << 2))
+#define TSPP_STATUS				0x68
+#define TSPP_CURR_TSP_HEADER	0x6C
+#define TSPP_CURR_PID_FILTER	0x70
+#define TSPP_SYSTEM_KEY(_n)	(0x74 + (_n << 2))
+#define TSPP_CBC_INIT_VAL(_n)	(0x94 + (_n << 2))
+#define TSPP_DATA_KEY_RESET	0x9C
+#define TSPP_KEY_VALID			0xA0
+#define TSPP_KEY_ERROR			0xA4
+#define TSPP_TEST_CTRL			0xA8
+#define TSPP_VERSION				0xAC
+#define TSPP_GENERICS			0xB0
+#define TSPP_NOP					0xB4
+
+/*
+ * Register bit definitions
+ */
+/* TSPP_RST */
+#define TSPP_RST_RESET                    BIT(0)
+
+/* TSPP_CLK_CONTROL	*/
+#define TSPP_CLK_CONTROL_FORCE_CRYPTO     BIT(9)
+#define TSPP_CLK_CONTROL_FORCE_PES_PL     BIT(8)
+#define TSPP_CLK_CONTROL_FORCE_PES_AF     BIT(7)
+#define TSPP_CLK_CONTROL_FORCE_RAW_CTRL   BIT(6)
+#define TSPP_CLK_CONTROL_FORCE_PERF_CNT   BIT(5)
+#define TSPP_CLK_CONTROL_FORCE_CTX_SEARCH BIT(4)
+#define TSPP_CLK_CONTROL_FORCE_TSP_PROC   BIT(3)
+#define TSPP_CLK_CONTROL_FORCE_CONS_AHB2MEM BIT(2)
+#define TSPP_CLK_CONTROL_FORCE_TS_AHB2MEM BIT(1)
+#define TSPP_CLK_CONTROL_SET_CLKON        BIT(0)
+
+/* TSPP_CONFIG	*/
+#define TSPP_CONFIG_SET_PACKET_LENGTH(_a, _b) (_a = (_a & 0xF0) | \
+((_b & 0xF) << 8))
+#define TSPP_CONFIG_GET_PACKET_LENGTH(_a) ((_a >> 8) & 0xF)
+#define TSPP_CONFIG_DUP_WITH_DISC_EN		BIT(7)
+#define TSPP_CONFIG_PES_SYNC_ERROR_MASK   BIT(6)
+#define TSPP_CONFIG_PS_LEN_ERR_MASK       BIT(5)
+#define TSPP_CONFIG_PS_CONT_ERR_UNSP_MASK BIT(4)
+#define TSPP_CONFIG_PS_CONT_ERR_MASK      BIT(3)
+#define TSPP_CONFIG_PS_DUP_TSP_MASK       BIT(2)
+#define TSPP_CONFIG_TSP_ERR_IND_MASK      BIT(1)
+#define TSPP_CONFIG_TSP_SYNC_ERR_MASK     BIT(0)
+
+/* TSPP_CONTROL */
+#define TSPP_CONTROL_PID_FILTER_LOCK      BIT(5)
+#define TSPP_CONTROL_FORCE_KEY_CALC       BIT(4)
+#define TSPP_CONTROL_TSP_CONS_SRC_DIS     BIT(3)
+#define TSPP_CONTROL_TSP_TSIF1_SRC_DIS    BIT(2)
+#define TSPP_CONTROL_TSP_TSIF0_SRC_DIS    BIT(1)
+#define TSPP_CONTROL_PERF_COUNT_INIT      BIT(0)
+
+/* TSPP_MSG_IRQ_STATUS + TSPP_MSG_IRQ_MASK */
+#define TSPP_MSG_TSPP_IRQ                 BIT(2)
+#define TSPP_MSG_TSIF_1_IRQ               BIT(1)
+#define TSPP_MSG_TSIF_0_IRQ               BIT(0)
+
+/* TSPP_IRQ_STATUS + TSPP_IRQ_MASK + TSPP_IRQ_CLEAR */
+#define TSPP_IRQ_STATUS_TSP_RD_CMPL			BIT(19)
+#define TSPP_IRQ_STATUS_KEY_ERROR			BIT(18)
+#define TSPP_IRQ_STATUS_KEY_SWITCHED_BAD	BIT(17)
+#define TSPP_IRQ_STATUS_KEY_SWITCHED		BIT(16)
+#define TSPP_IRQ_STATUS_PS_BROKEN(_n)		BIT((_n))
+
+/* TSPP_PIPE_ERROR_STATUS */
+#define TSPP_PIPE_PES_SYNC_ERROR				BIT(3)
+#define TSPP_PIPE_PS_LENGTH_ERROR			BIT(2)
+#define TSPP_PIPE_PS_CONTINUITY_ERROR		BIT(1)
+#define TSPP_PIP_PS_LOST_START				BIT(0)
+
+/* TSPP_STATUS			*/
+#define TSPP_STATUS_TSP_PKT_AVAIL			BIT(10)
+#define TSPP_STATUS_TSIF1_DM_REQ				BIT(6)
+#define TSPP_STATUS_TSIF0_DM_REQ				BIT(2)
+#define TSPP_CURR_FILTER_TABLE				BIT(0)
+
+/* TSPP_GENERICS		*/
+#define TSPP_GENERICS_CRYPTO_GEN				BIT(12)
+#define TSPP_GENERICS_MAX_CONS_PIPES		BIT(7)
+#define TSPP_GENERICS_MAX_PIPES				BIT(2)
+#define TSPP_GENERICS_TSIF_1_GEN				BIT(1)
+#define TSPP_GENERICS_TSIF_0_GEN				BIT(0)
+
+/*
+ * TSPP memory regions
+ */
+#define TSPP_PID_FILTER_TABLE0      0x800
+#define TSPP_PID_FILTER_TABLE1      0x880
+#define TSPP_PID_FILTER_TABLE2      0x900
+#define TSPP_GLOBAL_PERFORMANCE     0x980 /* see tspp_global_performance */
+#define TSPP_PIPE_CONTEXT           0x990 /* see tspp_pipe_context */
+#define TSPP_PIPE_PERFORMANCE       0x998 /* see tspp_pipe_performance */
+#define TSPP_TSP_BUFF_WORD(_n)      (0xC10 + (_n << 2))
+#define TSPP_DATA_KEY               0xCD0
+
+#ifdef TSPP_USE_DEBUGFS
+struct debugfs_entry {
+	const char *name;
+	mode_t mode;
+	int offset;
+};
+
+static const struct debugfs_entry debugfs_tsif_regs[] = {
+	{"sts_ctl",             S_IRUGO | S_IWUSR, TSIF_STS_CTL_OFF},
+	{"time_limit",          S_IRUGO | S_IWUSR, TSIF_TIME_LIMIT_OFF},
+	{"clk_ref",             S_IRUGO | S_IWUSR, TSIF_CLK_REF_OFF},
+	{"lpbk_flags",          S_IRUGO | S_IWUSR, TSIF_LPBK_FLAGS_OFF},
+	{"lpbk_data",           S_IRUGO | S_IWUSR, TSIF_LPBK_DATA_OFF},
+	{"test_ctl",            S_IRUGO | S_IWUSR, TSIF_TEST_CTL_OFF},
+	{"test_mode",           S_IRUGO | S_IWUSR, TSIF_TEST_MODE_OFF},
+	{"test_reset",                    S_IWUSR, TSIF_TEST_RESET_OFF},
+	{"test_export",         S_IRUGO | S_IWUSR, TSIF_TEST_EXPORT_OFF},
+	{"test_current",        S_IRUGO,           TSIF_TEST_CURRENT_OFF},
+	{"data_port",           S_IRUSR,           TSIF_DATA_PORT_OFF},
+};
+
+static const struct debugfs_entry debugfs_tspp_regs[] = {
+	{"rst",                 S_IRUGO | S_IWUSR, TSPP_RST},
+	{"clk_control",         S_IRUGO | S_IWUSR, TSPP_CLK_CONTROL},
+	{"config",              S_IRUGO | S_IWUSR, TSPP_CONFIG},
+	{"control",             S_IRUGO | S_IWUSR, TSPP_CONTROL},
+	{"ps_disable",          S_IRUGO | S_IWUSR, TSPP_PS_DISABLE},
+	{"msg_irq_status",      S_IRUGO | S_IWUSR, TSPP_MSG_IRQ_STATUS},
+	{"msg_irq_mask",        S_IRUGO | S_IWUSR, TSPP_MSG_IRQ_MASK},
+	{"irq_status",          S_IRUGO | S_IWUSR, TSPP_IRQ_STATUS},
+	{"irq_mask",            S_IRUGO | S_IWUSR, TSPP_IRQ_MASK},
+	{"irq_clear",           S_IRUGO | S_IWUSR, TSPP_IRQ_CLEAR},
+	/* {"pipe_error_status",S_IRUGO | S_IWUSR, TSPP_PIPE_ERROR_STATUS}, */
+	{"status",              S_IRUGO | S_IWUSR, TSPP_STATUS},
+	{"curr_tsp_header",     S_IRUGO | S_IWUSR, TSPP_CURR_TSP_HEADER},
+	{"curr_pid_filter",     S_IRUGO | S_IWUSR, TSPP_CURR_PID_FILTER},
+	/* {"system_key",       S_IRUGO | S_IWUSR, TSPP_SYSTEM_KEY}, */
+	/* {"cbc_init_val",     S_IRUGO | S_IWUSR, TSPP_CBC_INIT_VAL}, */
+	{"data_key_reset",      S_IRUGO | S_IWUSR, TSPP_DATA_KEY_RESET},
+	{"key_valid",           S_IRUGO | S_IWUSR, TSPP_KEY_VALID},
+	{"key_error",           S_IRUGO | S_IWUSR, TSPP_KEY_ERROR},
+	{"test_ctrl",           S_IRUGO | S_IWUSR, TSPP_TEST_CTRL},
+	{"version",             S_IRUGO | S_IWUSR, TSPP_VERSION},
+	{"generics",            S_IRUGO | S_IWUSR, TSPP_GENERICS},
+	{"pid_filter_table0",   S_IRUGO | S_IWUSR, TSPP_PID_FILTER_TABLE0},
+	{"pid_filter_table1",   S_IRUGO | S_IWUSR, TSPP_PID_FILTER_TABLE1},
+	{"pid_filter_table2",   S_IRUGO | S_IWUSR, TSPP_PID_FILTER_TABLE2},
+	{"global_performance",  S_IRUGO | S_IWUSR, TSPP_GLOBAL_PERFORMANCE},
+	{"pipe_context",        S_IRUGO | S_IWUSR, TSPP_PIPE_CONTEXT},
+	{"pipe_performance",    S_IRUGO | S_IWUSR, TSPP_PIPE_PERFORMANCE},
+	{"data_key",            S_IRUGO | S_IWUSR, TSPP_DATA_KEY}
+};
+
+#endif /* TSPP_USE_DEBUGFS */
+
+struct tspp_pid_filter {
+	u32 filter;			/* see FILTER_ macros */
+	u32 config;			/* see FILTER_ macros */
+};
+
+/* tsp_info */
+#define FILTER_HEADER_ERROR_MASK          BIT(7)
+#define FILTER_TRANS_END_DISABLE          BIT(6)
+#define FILTER_DEC_ON_ERROR_EN            BIT(5)
+#define FILTER_DECRYPT                    BIT(4)
+#define FILTER_HAS_ENCRYPTION(_p)         (_p->config & FILTER_DECRYPT)
+#define FILTER_GET_PIPE_NUMBER0(_p)       (_p->config & 0xF)
+#define FILTER_SET_PIPE_NUMBER0(_p, _b)   (_p->config = \
+			(_p->config & ~0xF) | (_b & 0xF))
+#define FILTER_GET_PIPE_PROCESS0(_p)      ((_p->filter >> 30) & 0x3)
+#define FILTER_SET_PIPE_PROCESS0(_p, _b)  (_p->filter = \
+			(_p->filter & ~(0x3<<30)) | ((_b & 0x3) << 30))
+#define FILTER_GET_PIPE_PID(_p)           ((_p->filter >> 13) & 0x1FFF)
+#define FILTER_SET_PIPE_PID(_p, _b)       (_p->filter = \
+			(_p->filter & ~(0x1FFF<<13)) | ((_b & 0x1FFF) << 13))
+#define FILTER_GET_PID_MASK(_p)           (_p->filter & 0x1FFF)
+#define FILTER_SET_PID_MASK(_p, _b)       (_p->filter = \
+			(_p->filter & ~0x1FFF) | (_b & 0x1FFF))
+#define FILTER_GET_PIPE_PROCESS1(_p)      ((_p->config >> 30) & 0x3)
+#define FILTER_SET_PIPE_PROCESS1(_p, _b)  (_p->config = \
+			(_p->config & ~(0x3<<30)) | ((_b & 0x3) << 30))
+#define FILTER_GET_KEY_NUMBER(_p)         ((_p->config >> 8) & 0x7)
+#define FILTER_SET_KEY_NUMBER(_p, _b)     (_p->config = \
+			(_p->config & ~(0x7<<8)) | ((_b & 0x7) << 8))
+
+struct tspp_global_performance_regs {
+	u32 tsp_total;
+	u32 tsp_ignored;
+	u32 tsp_error;
+	u32 tsp_sync;
+};
+
+struct tspp_pipe_context_regs {
+	u16 pes_bytes_left;
+	u16 count;
+	u32 tsif_suffix;
+} __packed;
+#define CONTEXT_GET_STATE(_a)					(_a & 0x3)
+#define CONTEXT_UNSPEC_LENGTH					BIT(11)
+#define CONTEXT_GET_CONT_COUNT(_a)			((_a >> 12) & 0xF)
+
+struct tspp_pipe_performance_regs {
+	u32 tsp_total;
+	u32 ps_duplicate_tsp;
+	u32 tsp_no_payload;
+	u32 tsp_broken_ps;
+	u32 ps_total_num;
+	u32 ps_continuity_error;
+	u32 ps_length_error;
+	u32 pes_sync_error;
+};
+
+struct tspp_tsif_device {
+	void __iomem *base;
+	u32 time_limit;
+	u32 ref_count;
+
+	/* debugfs */
+#ifdef TSPP_USE_DEBUGFS
+	struct dentry *dent_tsif;
+	struct dentry *debugfs_tsif_regs[ARRAY_SIZE(debugfs_tsif_regs)];
+#endif /* TSPP_USE_DEBUGFS */
+};
+
+/* this represents the actual hardware device */
+struct tspp_device {
+	struct platform_device *pdev;
+	void __iomem *base;
+	unsigned int tspp_irq;
+	unsigned int bam_irq;
+	u32 bam_handle;
+	struct sps_bam_props bam_props;
+	struct wake_lock wake_lock;
+	spinlock_t spinlock;
+	struct tasklet_struct tlet;
+	struct tspp_tsif_device tsif[TSPP_TSIF_INSTANCES];
+	/* clocks */
+	struct clk *tsif_pclk;
+	struct clk *tsif_ref_clk;
+
+#ifdef TSPP_USE_DEBUGFS
+	struct dentry *dent;
+	struct dentry *debugfs_regs[ARRAY_SIZE(debugfs_tspp_regs)];
+#endif /* TSPP_USE_DEBUGFS */
+};
+
+enum tspp_buf_state {
+	TSPP_BUF_STATE_EMPTY,	/* buffer has been allocated, but not waiting */
+	TSPP_BUF_STATE_WAITING, /* buffer is waiting to be filled */
+	TSPP_BUF_STATE_DATA     /* buffer is not empty and can be read */
+};
+
+struct tspp_mem_buffer {
+	struct sps_mem_buffer mem;
+	enum tspp_buf_state state;
+	size_t filled;          /* how much data this buffer is holding */
+	int read_index;         /* where to start reading data from */
+};
+
+/* this represents each char device 'channel' */
+struct tspp_channel {
+	struct cdev cdev;
+	struct device *dd;
+	struct tspp_device *pdev;
+	struct sps_pipe *pipe;
+	struct sps_connect config;
+	struct sps_register_event event;
+	struct tspp_mem_buffer buffer[TSPP_NUM_BUFFERS];
+	wait_queue_head_t in_queue; /* set when data is received */
+	int read;  /* index into mem showing buffers ready to be read by user */
+	int waiting;	/* index into mem showing outstanding transfers */
+	int id;			/* channel id (0-15) */
+	int used;		/* is this channel in use? */
+	int key;			/* which encryption key index is used */
+	u32 bufsize;	/* size of the sps transfer buffers */
+	int buffer_count; /* how many buffers are actually allocated */
+	int filter_count; /* how many filters have been added to this channel */
+	enum tspp_source src;
+	enum tspp_mode mode;
+};
+
+struct tspp_pid_filter_table {
+	struct tspp_pid_filter filter[TSPP_NUM_PRIORITIES];
+};
+
+struct tspp_key_entry {
+	u32 even_lsb;
+	u32 even_msb;
+	u32 odd_lsb;
+	u32 odd_msb;
+};
+
+struct tspp_key_table {
+	struct tspp_key_entry entry[TSPP_NUM_KEYS];
+};
+
+static struct tspp_pid_filter_table *tspp_filter_table[TSPP_FILTER_TABLES];
+static struct tspp_channel channel_list[TSPP_NUM_CHANNELS];
+static struct tspp_key_table *tspp_key_table;
+static struct tspp_global_performance_regs *tspp_global_performance;
+static struct tspp_pipe_context_regs *tspp_pipe_context;
+static struct tspp_pipe_performance_regs *tspp_pipe_performance;
+static struct class *tspp_class;
+static int tspp_key_entry;
+static dev_t tspp_minor;  /* next minor number to assign */
+static int loopback_mode; /* put tsif interfaces into loopback mode */
+
+/*** IRQ ***/
+static irqreturn_t tspp_isr(int irq, void *dev_id)
+{
+	struct tspp_device *device = dev_id;
+	u32 status, mask;
+	u32 data;
+
+	status = readl_relaxed(device->base + TSPP_IRQ_STATUS);
+	mask = readl_relaxed(device->base + TSPP_IRQ_MASK);
+	status &= mask;
+
+	if (!status) {
+		dev_warn(&device->pdev->dev, "Spurious interrupt");
+		return IRQ_NONE;
+	}
+
+	/* if (status & TSPP_IRQ_STATUS_TSP_RD_CMPL) */
+
+	if (status & TSPP_IRQ_STATUS_KEY_ERROR) {
+		/* read the key error info */
+		data = readl_relaxed(device->base + TSPP_KEY_ERROR);
+		dev_info(&device->pdev->dev, "key error 0x%x", data);
+	}
+	if (status & TSPP_IRQ_STATUS_KEY_SWITCHED_BAD) {
+		data = readl_relaxed(device->base + TSPP_KEY_VALID);
+		dev_info(&device->pdev->dev, "key invalidated: 0x%x", data);
+	}
+	if (status & TSPP_IRQ_STATUS_KEY_SWITCHED)
+		dev_info(&device->pdev->dev, "key switched");
+
+	if (status & 0xffff)
+		dev_info(&device->pdev->dev, "broken pipe");
+
+	writel_relaxed(status, device->base + TSPP_IRQ_CLEAR);
+	wmb();
+	return IRQ_HANDLED;
+}
+
+/*** callbacks ***/
+static void tspp_sps_complete_cb(struct sps_event_notify *notify)
+{
+	struct tspp_channel *channel = notify->user;
+	tasklet_schedule(&channel->pdev->tlet);
+}
+
+/*** tasklet ***/
+static void tspp_sps_complete_tlet(unsigned long data)
+{
+	int i;
+	int complete;
+	unsigned long flags;
+	struct sps_iovec iovec;
+	struct tspp_channel *channel;
+	struct tspp_device *device = (struct tspp_device *)data;
+	struct tspp_mem_buffer *buffer;
+
+	spin_lock_irqsave(&device->spinlock, flags);
+
+	for (i = 0; i < TSPP_NUM_CHANNELS; i++) {
+		complete = 0;
+		channel = &channel_list[i];
+		buffer = &channel->buffer[channel->waiting];
+
+		/* get completions */
+		if (buffer->state == TSPP_BUF_STATE_WAITING) {
+			if (sps_get_iovec(channel->pipe, &iovec) != 0) {
+				pr_err("tspp: Error in iovec on channel %i",
+					channel->id);
+				break;
+			}
+			if (iovec.size == 0)
+				break;
+
+			if (iovec.addr != buffer->mem.phys_base)
+				pr_err("tspp: buffer mismatch 0x%08x",
+					buffer->mem.phys_base);
+
+			complete = 1;
+			buffer->state = TSPP_BUF_STATE_DATA;
+			buffer->filled = iovec.size;
+			buffer->read_index = 0;
+			channel->waiting++;
+			if (channel->waiting == TSPP_NUM_BUFFERS)
+				channel->waiting = 0;
+		}
+
+		if (complete) {
+			/* wake any waiting processes */
+			wake_up_interruptible(&channel->in_queue);
+		}
+	}
+
+	spin_unlock_irqrestore(&device->spinlock, flags);
+}
+
+/*** GPIO functions ***/
+static void tspp_gpios_free(const struct msm_gpio *table, int size)
+{
+	int i;
+	const struct msm_gpio *g;
+	for (i = size-1; i >= 0; i--) {
+		g = table + i;
+		gpio_free(GPIO_PIN(g->gpio_cfg));
+	}
+}
+
+static int tspp_gpios_request(const struct msm_gpio *table, int size)
+{
+	int rc;
+	int i;
+	const struct msm_gpio *g;
+	for (i = 0; i < size; i++) {
+		g = table + i;
+		rc = gpio_request(GPIO_PIN(g->gpio_cfg), g->label);
+		if (rc) {
+			pr_err("tspp: gpio_request(%d) <%s> failed: %d\n",
+			       GPIO_PIN(g->gpio_cfg), g->label ?: "?", rc);
+			goto err;
+		}
+	}
+	return 0;
+err:
+	tspp_gpios_free(table, i);
+	return rc;
+}
+
+static int tspp_gpios_disable(const struct msm_gpio *table, int size)
+{
+	int rc = 0;
+	int i;
+	const struct msm_gpio *g;
+	for (i = size-1; i >= 0; i--) {
+		int tmp;
+		g = table + i;
+		tmp = gpio_tlmm_config(g->gpio_cfg, GPIO_CFG_DISABLE);
+		if (tmp) {
+			pr_err("tspp_gpios_disable(0x%08x, GPIO_CFG_DISABLE)"
+			       " <%s> failed: %d\n",
+			       g->gpio_cfg, g->label ?: "?", rc);
+			pr_err("tspp: pin %d func %d dir %d pull %d drvstr %d\n",
+			       GPIO_PIN(g->gpio_cfg), GPIO_FUNC(g->gpio_cfg),
+			       GPIO_DIR(g->gpio_cfg), GPIO_PULL(g->gpio_cfg),
+			       GPIO_DRVSTR(g->gpio_cfg));
+			if (!rc)
+				rc = tmp;
+		}
+	}
+
+	return rc;
+}
+
+static int tspp_gpios_enable(const struct msm_gpio *table, int size)
+{
+	int rc;
+	int i;
+	const struct msm_gpio *g;
+	for (i = 0; i < size; i++) {
+		g = table + i;
+		rc = gpio_tlmm_config(g->gpio_cfg, GPIO_CFG_ENABLE);
+		if (rc) {
+			pr_err("tspp: gpio_tlmm_config(0x%08x, GPIO_CFG_ENABLE)"
+			       " <%s> failed: %d\n",
+			       g->gpio_cfg, g->label ?: "?", rc);
+			pr_err("tspp: pin %d func %d dir %d pull %d drvstr %d\n",
+			       GPIO_PIN(g->gpio_cfg), GPIO_FUNC(g->gpio_cfg),
+			       GPIO_DIR(g->gpio_cfg), GPIO_PULL(g->gpio_cfg),
+			       GPIO_DRVSTR(g->gpio_cfg));
+			goto err;
+		}
+	}
+	return 0;
+err:
+	tspp_gpios_disable(table, i);
+	return rc;
+}
+
+static int tspp_gpios_request_enable(const struct msm_gpio *table, int size)
+{
+	int rc = tspp_gpios_request(table, size);
+	if (rc)
+		return rc;
+	rc = tspp_gpios_enable(table, size);
+	if (rc)
+		tspp_gpios_free(table, size);
+	return rc;
+}
+
+static void tspp_gpios_disable_free(const struct msm_gpio *table, int size)
+{
+	tspp_gpios_disable(table, size);
+	tspp_gpios_free(table, size);
+}
+
+static int tspp_start_gpios(struct tspp_device *device)
+{
+	struct msm_tspp_platform_data *pdata =
+		device->pdev->dev.platform_data;
+	return tspp_gpios_request_enable(pdata->gpios, pdata->num_gpios);
+}
+
+static void tspp_stop_gpios(struct tspp_device *device)
+{
+	struct msm_tspp_platform_data *pdata =
+		device->pdev->dev.platform_data;
+	tspp_gpios_disable_free(pdata->gpios, pdata->num_gpios);
+}
+
+/*** TSIF functions ***/
+static int tspp_start_tsif(struct tspp_tsif_device *tsif_device)
+{
+	int start_hardware = 0;
+	u32 ctl;
+
+	if (tsif_device->ref_count == 0) {
+		start_hardware = 1;
+	} else if (tsif_device->ref_count > 0) {
+		ctl = readl_relaxed(tsif_device->base + TSIF_STS_CTL_OFF);
+		if ((ctl & TSIF_STS_CTL_START) != 1) {
+			/* this hardware should already be running */
+			pr_warn("tspp: tsif hw not started but ref count > 0");
+			start_hardware = 1;
+		}
+	}
+
+	if (start_hardware) {
+		if (loopback_mode) {
+			ctl = TSIF_STS_CTL_EN_IRQ |
+				TSIF_STS_CTL_EN_NULL |
+				TSIF_STS_CTL_EN_ERROR |
+				TSIF_STS_CTL_TEST_MODE |
+				TSIF_STS_CTL_EN_DM;
+	TSPP_DEBUG("tspp: starting tsif hw in loopback mode 0x%x", ctl);
+		} else {
+			ctl = TSIF_STS_CTL_EN_IRQ |
+				TSIF_STS_CTL_EN_TIME_LIM |
+				TSIF_STS_CTL_EN_TCR |
+				TSIF_STS_CTL_EN_DM;
+		}
+		writel_relaxed(ctl, tsif_device->base + TSIF_STS_CTL_OFF);
+		writel_relaxed(tsif_device->time_limit,
+			  tsif_device->base + TSIF_TIME_LIMIT_OFF);
+		wmb();
+		writel_relaxed(ctl | TSIF_STS_CTL_START,
+			  tsif_device->base + TSIF_STS_CTL_OFF);
+		wmb();
+		ctl = readl_relaxed(tsif_device->base + TSIF_STS_CTL_OFF);
+	}
+
+	tsif_device->ref_count++;
+
+	return (ctl & TSIF_STS_CTL_START) ? 0 : -EFAULT;
+}
+
+static void tspp_stop_tsif(struct tspp_tsif_device *tsif_device)
+{
+	if (tsif_device->ref_count == 0)
+		return;
+
+	tsif_device->ref_count--;
+
+	if (tsif_device->ref_count == 0) {
+		writel_relaxed(TSIF_STS_CTL_STOP,
+			tsif_device->base + TSIF_STS_CTL_OFF);
+		wmb();
+	}
+}
+
+/*** TSPP functions ***/
+static int tspp_get_key_entry(void)
+{
+	int i;
+	for (i = 0; i < TSPP_NUM_KEYS; i++) {
+		if (!(tspp_key_entry & (1 << i))) {
+			tspp_key_entry |= (1 << i);
+			return i;
+		}
+	}
+	return 1;
+}
+
+static void tspp_free_key_entry(int entry)
+{
+	if (entry > TSPP_NUM_KEYS) {
+		pr_err("tspp_free_key_entry: index out of bounds");
+		return;
+	}
+
+	tspp_key_entry &= ~(1 << entry);
+}
+
+static int tspp_alloc_buffer(struct sps_mem_buffer *mem,
+	struct tspp_channel *channel)
+{
+	if (channel->bufsize < TSPP_MIN_BUFFER_SIZE ||
+		channel->bufsize > TSPP_MAX_BUFFER_SIZE) {
+		pr_err("tspp: bad buffer size");
+		return 1;
+	}
+
+	switch (channel->mode) {
+	case TSPP_MODE_DISABLED:
+		mem->size = 0;
+		pr_err("tspp: channel is disabled");
+		return 1;
+
+	case TSPP_MODE_PES:
+		/* give the user what he asks for */
+		mem->size = channel->bufsize;
+		break;
+
+	case TSPP_MODE_RAW:
+		/* must be a multiple of 192 */
+		if (channel->bufsize < (TSPP_PACKET_LENGTH+4))
+			mem->size = (TSPP_PACKET_LENGTH+4);
+		else
+			mem->size = (channel->bufsize /
+				(TSPP_PACKET_LENGTH+4)) *
+				(TSPP_PACKET_LENGTH+4);
+		break;
+
+	case TSPP_MODE_RAW_NO_SUFFIX:
+		/* must be a multiple of 188 */
+		mem->size = (channel->bufsize / TSPP_PACKET_LENGTH) *
+			TSPP_PACKET_LENGTH;
+		break;
+	}
+
+#ifdef TSPP_USE_DMA_ALLOC_COHERENT
+	mem->base = dma_alloc_coherent(NULL, mem->size,
+		&mem->phys_base, GFP_KERNEL);
+	if (mem->base == 0) {
+		pr_err("tspp dma alloc coherent failed %i", mem->size);
+		return -ENOMEM;
+	}
+#else
+	mem->base = kmalloc(mem->size, GFP_KERNEL);
+	if (mem->base == 0) {
+		pr_err("tspp buffer allocation failed %i", mem->size);
+		return -ENOMEM;
+	}
+	mem->phys_base = dma_map_single(NULL,
+		mem->base,
+		mem->size,
+		DMA_FROM_DEVICE);
+#endif
+
+	return 0;
+}
+
+static int tspp_global_reset(struct tspp_device *pdev)
+{
+	u32 i, val;
+
+	/* stop all TSIFs */
+	for (i = 0; i < TSPP_TSIF_INSTANCES; i++) {
+		pdev->tsif[i].ref_count = 1; /* allows stopping hw */
+		tspp_stop_tsif(&pdev->tsif[i]); /* will reset ref_count to 0 */
+		pdev->tsif[i].time_limit = TSPP_TSIF_DEFAULT_TIME_LIMIT;
+	}
+	writel_relaxed(TSPP_RST_RESET, pdev->base + TSPP_RST);
+	wmb();
+
+	/* BAM */
+	if (sps_device_reset(pdev->bam_handle) != 0) {
+		pr_err("tspp: error resetting bam");
+		return 1;
+	}
+
+	/* TSPP tables */
+	for (i = 0; i < TSPP_FILTER_TABLES; i++)
+		memset(tspp_filter_table[i],
+			0, sizeof(struct tspp_pid_filter_table));
+
+	/* disable all filters */
+	val = (2 << TSPP_NUM_CHANNELS) - 1;
+	writel_relaxed(val, pdev->base + TSPP_PS_DISABLE);
+
+	/* TSPP registers */
+	val = readl_relaxed(pdev->base + TSPP_CONTROL);
+	writel_relaxed(val | TSPP_CLK_CONTROL_FORCE_PERF_CNT,
+		pdev->base + TSPP_CONTROL);
+	wmb();
+	memset(tspp_global_performance, 0,
+		sizeof(struct tspp_global_performance_regs));
+	memset(tspp_pipe_context, 0,
+		sizeof(struct tspp_pipe_context_regs));
+	memset(tspp_pipe_performance, 0,
+		sizeof(struct tspp_pipe_performance_regs));
+	wmb();
+	writel_relaxed(val & ~TSPP_CLK_CONTROL_FORCE_PERF_CNT,
+		pdev->base + TSPP_CONTROL);
+	wmb();
+
+	val = readl_relaxed(pdev->base + TSPP_CONFIG);
+	val &= ~(TSPP_CONFIG_PS_LEN_ERR_MASK |
+			TSPP_CONFIG_PS_CONT_ERR_UNSP_MASK |
+			TSPP_CONFIG_PS_CONT_ERR_MASK);
+	TSPP_CONFIG_SET_PACKET_LENGTH(val, TSPP_PACKET_LENGTH);
+	writel_relaxed(val, pdev->base + TSPP_CONFIG);
+	writel_relaxed(0x000fffff, pdev->base + TSPP_IRQ_MASK);
+	writel_relaxed(0x000fffff, pdev->base + TSPP_IRQ_CLEAR);
+	writel_relaxed(0, pdev->base + TSPP_RST);
+	wmb();
+
+	tspp_key_entry = 0;
+
+	return 0;
+}
+
+int tspp_open_stream(struct tspp_channel *channel, enum tspp_source src)
+{
+	u32 val;
+	struct tspp_device *pdev;
+
+	if (!channel)
+		return 1;
+
+	pdev = channel->pdev;
+
+	switch (src) {
+	case TSPP_SOURCE_TSIF0:
+		/* make sure TSIF0 is running & enabled */
+		if (tspp_start_tsif(&pdev->tsif[0]) != 0) {
+			pr_err("tspp: error starting tsif0");
+			return 1;
+		}
+		val = readl_relaxed(pdev->base + TSPP_CONTROL);
+		writel_relaxed(val & ~TSPP_CONTROL_TSP_TSIF0_SRC_DIS,
+			pdev->base + TSPP_CONTROL);
+		wmb();
+		break;
+	case TSPP_SOURCE_TSIF1:
+		/* make sure TSIF1 is running & enabled */
+		if (tspp_start_tsif(&pdev->tsif[1]) != 0) {
+			pr_err("tspp: error starting tsif1");
+			return 1;
+		}
+		val = readl_relaxed(pdev->base + TSPP_CONTROL);
+		writel_relaxed(val & ~TSPP_CONTROL_TSP_TSIF1_SRC_DIS,
+			pdev->base + TSPP_CONTROL);
+		wmb();
+		break;
+	case TSPP_SOURCE_MEM:
+		break;
+	default:
+		pr_warn("tspp: channel %i invalid source %i", channel->id, src);
+		return 1;
+	}
+
+	channel->src = src;
+
+	return 0;
+}
+EXPORT_SYMBOL(tspp_open_stream);
+
+int tspp_close_stream(struct tspp_channel *channel)
+{
+	u32 val;
+	struct tspp_device *pdev;
+
+	pdev = channel->pdev;
+
+	switch (channel->src) {
+	case TSPP_SOURCE_TSIF0:
+		tspp_stop_tsif(&pdev->tsif[0]);
+		val = readl_relaxed(pdev->base + TSPP_CONTROL);
+		writel_relaxed(val | TSPP_CONTROL_TSP_TSIF0_SRC_DIS,
+			pdev->base + TSPP_CONTROL);
+		wmb();
+		break;
+	case TSPP_SOURCE_TSIF1:
+		tspp_stop_tsif(&pdev->tsif[1]);
+		val = readl_relaxed(pdev->base + TSPP_CONTROL);
+		writel_relaxed(val | TSPP_CONTROL_TSP_TSIF1_SRC_DIS,
+			pdev->base + TSPP_CONTROL);
+		break;
+	case TSPP_SOURCE_MEM:
+		break;
+	case TSPP_SOURCE_NONE:
+		break;
+	}
+
+	channel->src = -1;
+	return 0;
+}
+EXPORT_SYMBOL(tspp_close_stream);
+
+int tspp_open_channel(struct tspp_channel *channel)
+{
+	int rc = 0;
+	struct sps_connect *config = &channel->config;
+	struct sps_register_event *event = &channel->event;
+
+	if (channel->used) {
+		pr_err("tspp channel already in use");
+		return 1;
+	}
+
+	/* mark it as used */
+	channel->used = 1;
+
+	/* start the bam  */
+	channel->pipe = sps_alloc_endpoint();
+	if (channel->pipe == 0) {
+		pr_err("tspp: error allocating endpoint");
+		rc = -ENOMEM;
+		goto err_sps_alloc;
+	}
+
+	/* get default configuration */
+	sps_get_config(channel->pipe, config);
+
+	config->source = channel->pdev->bam_handle;
+	config->destination = SPS_DEV_HANDLE_MEM;
+	config->mode = SPS_MODE_SRC;
+	config->options = SPS_O_AUTO_ENABLE |
+		SPS_O_EOT | SPS_O_ACK_TRANSFERS;
+	config->src_pipe_index = channel->id;
+	config->desc.size =
+		(TSPP_SPS_DESCRIPTOR_COUNT + 1) * SPS_DESCRIPTOR_SIZE;
+	config->desc.base = dma_alloc_coherent(NULL,
+						config->desc.size,
+						&config->desc.phys_base,
+						GFP_KERNEL);
+	if (config->desc.base == 0) {
+		pr_err("tspp: error allocating sps descriptors");
+		rc = -ENOMEM;
+		goto err_desc_alloc;
+	}
+
+	memset(config->desc.base, 0, config->desc.size);
+
+	rc = sps_connect(channel->pipe, config);
+	if (rc) {
+		pr_err("tspp: error connecting bam");
+		goto err_connect;
+	}
+
+	event->mode = SPS_TRIGGER_CALLBACK;
+	event->options = SPS_O_EOT;
+	event->callback = tspp_sps_complete_cb;
+	event->xfer_done = NULL;
+	event->user = channel;
+
+	rc = sps_register_event(channel->pipe, event);
+	if (rc) {
+		pr_err("tspp: error registering event");
+		goto err_event;
+	}
+
+	rc = pm_runtime_get(&channel->pdev->pdev->dev);
+	if (rc < 0) {
+		dev_err(&channel->pdev->pdev->dev,
+			"Runtime PM: Unable to wake up tspp device, rc = %d",
+			rc);
+	}
+
+	wake_lock(&channel->pdev->wake_lock);
+	return 0;
+
+err_event:
+	sps_disconnect(channel->pipe);
+err_connect:
+	dma_free_coherent(NULL, config->desc.size, config->desc.base,
+		config->desc.phys_base);
+err_desc_alloc:
+	sps_free_endpoint(channel->pipe);
+err_sps_alloc:
+	return rc;
+}
+EXPORT_SYMBOL(tspp_open_channel);
+
+int tspp_close_channel(struct tspp_channel *channel)
+{
+	int i;
+	int id;
+	u32 val;
+	struct sps_connect *config = &channel->config;
+	struct tspp_device *pdev = channel->pdev;
+
+	TSPP_DEBUG("tspp_close_channel");
+	channel->used = 0;
+
+	/* disable pipe (channel) */
+	val = readl_relaxed(pdev->base + TSPP_PS_DISABLE);
+	writel_relaxed(val | channel->id, pdev->base + TSPP_PS_DISABLE);
+	wmb();
+
+	/* unregister all filters for this channel */
+	for (i = 0; i < TSPP_NUM_PRIORITIES; i++) {
+		struct tspp_pid_filter *tspp_filter =
+			&tspp_filter_table[channel->src]->filter[i];
+		id = FILTER_GET_PIPE_NUMBER0(tspp_filter);
+		if (id == channel->id) {
+			if (FILTER_HAS_ENCRYPTION(tspp_filter))
+				tspp_free_key_entry(
+					FILTER_GET_KEY_NUMBER(tspp_filter));
+			tspp_filter->config = 0;
+			tspp_filter->filter = 0;
+		}
+	}
+	channel->filter_count = 0;
+
+	/* stop the stream */
+	tspp_close_stream(channel);
+
+	/* disconnect the bam */
+	if (sps_disconnect(channel->pipe) != 0)
+		pr_warn("tspp: Error freeing sps endpoint (%i)", channel->id);
+
+	/* destroy the buffers */
+	dma_free_coherent(NULL, config->desc.size, config->desc.base,
+		config->desc.phys_base);
+
+	for (i = 0; i < TSPP_NUM_BUFFERS; i++) {
+		if (channel->buffer[i].mem.phys_base) {
+#ifdef TSPP_USE_DMA_ALLOC_COHERENT
+			dma_free_coherent(NULL,
+				channel->buffer[i].mem.size,
+				channel->buffer[i].mem.base,
+				channel->buffer[i].mem.phys_base);
+#else
+			dma_unmap_single(channel->dd,
+			channel->buffer[i].mem.phys_base,
+			channel->buffer[i].mem.size,
+			0);
+			kfree(channel->buffer[i].mem.base);
+#endif
+			channel->buffer[i].mem.phys_base = 0;
+		}
+		channel->buffer[i].mem.base = 0;
+		channel->buffer[i].state = TSPP_BUF_STATE_EMPTY;
+	}
+	channel->buffer_count = 0;
+
+	wake_unlock(&channel->pdev->wake_lock);
+	return 0;
+}
+EXPORT_SYMBOL(tspp_close_channel);
+
+/* picks a stream for this channel */
+int tspp_select_source(struct tspp_channel *channel,
+	struct tspp_select_source *src)
+{
+	/* make sure the requested src id is in bounds */
+	if (src->source > TSPP_SOURCE_MEM) {
+		pr_err("tspp source out of bounds");
+		return 1;
+	}
+
+	/* open the stream */
+	tspp_open_stream(channel, src->source);
+
+	return 0;
+}
+EXPORT_SYMBOL(tspp_select_source);
+
+int tspp_add_filter(struct tspp_channel *channel,
+	struct tspp_filter *filter)
+{
+	int i;
+	int other_channel;
+	int entry;
+	u32 val, pid, enabled;
+	struct tspp_device *pdev = channel->pdev;
+	struct tspp_pid_filter p;
+
+	TSPP_DEBUG("tspp_add_filter");
+	if (filter->source > TSPP_SOURCE_MEM) {
+		pr_err("tspp invalid source");
+		return 1;
+	}
+
+	if (filter->priority >= TSPP_NUM_PRIORITIES) {
+		pr_err("tspp invalid source");
+		return 1;
+	}
+
+	/* make sure this filter mode matches the channel mode */
+	switch (channel->mode) {
+	case TSPP_MODE_DISABLED:
+		channel->mode = filter->mode;
+		break;
+	case TSPP_MODE_RAW:
+	case TSPP_MODE_PES:
+	case TSPP_MODE_RAW_NO_SUFFIX:
+		if (filter->mode != channel->mode) {
+			pr_err("tspp: wrong filter mode");
+			return 1;
+		}
+	}
+
+	if (filter->mode == TSPP_MODE_PES) {
+		for (i = 0; i < TSPP_NUM_PRIORITIES; i++) {
+			struct tspp_pid_filter *tspp_filter =
+				&tspp_filter_table[channel->src]->filter[i];
+			pid = FILTER_GET_PIPE_PID((tspp_filter));
+			enabled = FILTER_GET_PIPE_PROCESS0(tspp_filter);
+			if (enabled && (pid == filter->pid)) {
+				other_channel =
+					FILTER_GET_PIPE_NUMBER0(tspp_filter);
+				pr_err("tspp: pid 0x%x already in use by channel %i",
+					filter->pid, other_channel);
+				return 1;
+			}
+		}
+	}
+
+	/* make sure this priority is not already in use */
+	enabled = FILTER_GET_PIPE_PROCESS0(
+		(&(tspp_filter_table[channel->src]->filter[filter->priority])));
+	if (enabled) {
+		pr_err("tspp: filter priority %i source %i is already enabled\n",
+			filter->priority, channel->src);
+		return 1;
+	}
+
+	if (channel->mode == TSPP_MODE_PES) {
+		/* if we are already processing in PES mode, disable pipe
+		(channel) and filter to be updated */
+		val = readl_relaxed(pdev->base + TSPP_PS_DISABLE);
+		writel_relaxed(val | (1 << channel->id),
+			pdev->base + TSPP_PS_DISABLE);
+		wmb();
+	}
+
+	/* update entry */
+	p.filter = 0;
+	p.config = 0;
+	FILTER_SET_PIPE_PROCESS0((&p), filter->mode);
+	FILTER_SET_PIPE_PID((&p), filter->pid);
+	FILTER_SET_PID_MASK((&p), filter->mask);
+	FILTER_SET_PIPE_NUMBER0((&p), channel->id);
+	FILTER_SET_PIPE_PROCESS1((&p), TSPP_MODE_DISABLED);
+	if (filter->decrypt) {
+		entry = tspp_get_key_entry();
+		if (entry == -1) {
+			pr_err("tspp: no more keys available!");
+		} else {
+			p.config |= FILTER_DECRYPT;
+			FILTER_SET_KEY_NUMBER((&p), entry);
+		}
+	}
+	TSPP_DEBUG("tspp_add_filter: mode=%i pid=%i mask=%i channel=%i",
+		filter->mode, filter->pid, filter->mask, channel->id);
+
+	tspp_filter_table[channel->src]->
+		filter[filter->priority].config = p.config;
+	tspp_filter_table[channel->src]->
+		filter[filter->priority].filter = p.filter;
+
+	/* reenable pipe */
+	val = readl_relaxed(pdev->base + TSPP_PS_DISABLE);
+	writel_relaxed(val & ~(1 << channel->id), pdev->base + TSPP_PS_DISABLE);
+	wmb();
+	val = readl_relaxed(pdev->base + TSPP_PS_DISABLE);
+
+	/* allocate buffers if needed */
+	if (channel->buffer_count == 0) {
+		TSPP_DEBUG("tspp: no buffers need %i", TSPP_NUM_BUFFERS);
+		for (i = 0; i < TSPP_NUM_BUFFERS; i++) {
+			if (tspp_alloc_buffer(&channel->buffer[i].mem,
+				channel) != 0) {
+				pr_warn("tspp: Can't allocate buffer %i", i);
+			} else {
+				channel->buffer[i].filled = 0;
+				channel->buffer[i].read_index = 0;
+				channel->buffer_count++;
+
+				/* start the transfer */
+				if (sps_transfer_one(channel->pipe,
+					channel->buffer[i].mem.phys_base,
+					channel->buffer[i].mem.size,
+					channel,
+					SPS_IOVEC_FLAG_INT |
+					SPS_IOVEC_FLAG_EOB))
+					pr_err("tspp: can't submit transfer");
+				else
+					channel->buffer[i].state =
+						TSPP_BUF_STATE_WAITING;
+			}
+		}
+	}
+
+	if (channel->buffer_count < MIN_ACCEPTABLE_BUFFER_COUNT) {
+		pr_err("failed to allocate at least %i buffers",
+			MIN_ACCEPTABLE_BUFFER_COUNT);
+		return -ENOMEM;
+	}
+
+	channel->filter_count++;
+
+	return 0;
+}
+EXPORT_SYMBOL(tspp_add_filter);
+
+int tspp_remove_filter(struct tspp_channel *channel,
+	struct tspp_filter *filter)
+{
+	int entry;
+	u32 val;
+	struct tspp_device *pdev = channel->pdev;
+	int src = channel->src;
+	struct tspp_pid_filter *tspp_filter =
+		&(tspp_filter_table[src]->filter[filter->priority]);
+
+	/* disable pipe (channel) */
+	val = readl_relaxed(pdev->base + TSPP_PS_DISABLE);
+	writel_relaxed(val | channel->id, pdev->base + TSPP_PS_DISABLE);
+	wmb();
+
+	/* update data keys */
+	if (tspp_filter->config & FILTER_DECRYPT) {
+		entry = FILTER_GET_KEY_NUMBER(tspp_filter);
+		tspp_free_key_entry(entry);
+	}
+
+	/* update pid table */
+	tspp_filter->config = 0;
+	tspp_filter->filter = 0;
+
+	channel->filter_count--;
+
+	/* reenable pipe */
+	val = readl_relaxed(pdev->base + TSPP_PS_DISABLE);
+	writel_relaxed(val & ~(1 << channel->id),
+		pdev->base + TSPP_PS_DISABLE);
+	wmb();
+	val = readl_relaxed(pdev->base + TSPP_PS_DISABLE);
+
+	return 0;
+}
+EXPORT_SYMBOL(tspp_remove_filter);
+
+int tspp_set_key(struct tspp_channel *channel, struct tspp_key* key)
+{
+	int i;
+	int id;
+	int key_index;
+	int data;
+
+	/* read the key index used by this channel */
+	for (i = 0; i < TSPP_NUM_PRIORITIES; i++) {
+		struct tspp_pid_filter *tspp_filter =
+			&(tspp_filter_table[channel->src]->filter[i]);
+		id = FILTER_GET_PIPE_NUMBER0(tspp_filter);
+		if (id == channel->id) {
+			if (FILTER_HAS_ENCRYPTION(tspp_filter)) {
+				key_index = FILTER_GET_KEY_NUMBER(tspp_filter);
+				break;
+			}
+		}
+	}
+	if (i == TSPP_NUM_PRIORITIES) {
+		pr_err("tspp: no encryption on this channel");
+		return 1;
+	}
+
+	if (key->parity == TSPP_KEY_PARITY_EVEN) {
+		tspp_key_table->entry[key_index].even_lsb = key->lsb;
+		tspp_key_table->entry[key_index].even_msb = key->msb;
+	} else {
+		tspp_key_table->entry[key_index].odd_lsb = key->lsb;
+		tspp_key_table->entry[key_index].odd_msb = key->msb;
+	}
+	data = readl_relaxed(channel->pdev->base + TSPP_KEY_VALID);
+
+	return 0;
+}
+EXPORT_SYMBOL(tspp_set_key);
+
+static int tspp_set_iv(struct tspp_channel *channel, struct tspp_iv *iv)
+{
+	struct tspp_device *pdev = channel->pdev;
+
+	writel_relaxed(iv->data[0], pdev->base + TSPP_CBC_INIT_VAL(0));
+	writel_relaxed(iv->data[1], pdev->base + TSPP_CBC_INIT_VAL(1));
+	return 0;
+}
+
+static int tspp_set_system_keys(struct tspp_channel *channel,
+	struct tspp_system_keys *keys)
+{
+	int i;
+	struct tspp_device *pdev = channel->pdev;
+
+	for (i = 0; i < TSPP_NUM_SYSTEM_KEYS; i++)
+		writel_relaxed(keys->data[i], pdev->base + TSPP_SYSTEM_KEY(i));
+
+	return 0;
+}
+
+static int tspp_set_buffer_size(struct tspp_channel *channel,
+	struct tspp_buffer *buf)
+{
+	if (buf->size < TSPP_MIN_BUFFER_SIZE)
+		channel->bufsize = TSPP_MIN_BUFFER_SIZE;
+	else if (buf->size > TSPP_MAX_BUFFER_SIZE)
+		channel->bufsize = TSPP_MAX_BUFFER_SIZE;
+	else
+		channel->bufsize = buf->size;
+
+	TSPP_DEBUG("tspp channel %i buffer size %i",
+		channel->id, channel->bufsize);
+
+	return 0;
+}
+
+/*** File Operations ***/
+static ssize_t tspp_open(struct inode *inode, struct file *filp)
+{
+	struct tspp_channel *channel;
+	channel = container_of(inode->i_cdev, struct tspp_channel, cdev);
+	filp->private_data = channel;
+
+	/* if this channel is already in use, quit */
+	if (channel->used) {
+		pr_err("tspp channel %i already in use",
+			MINOR(channel->cdev.dev));
+		return -EACCES;
+	}
+
+	if (tspp_open_channel(channel) != 0) {
+		pr_err("tspp: error opening channel");
+		return -EACCES;
+	}
+
+	return 0;
+}
+
+static unsigned int tspp_poll(struct file *filp, struct poll_table_struct *p)
+{
+	unsigned long flags;
+	unsigned int mask = 0;
+	struct tspp_channel *channel;
+	channel = filp->private_data;
+
+	/* register the wait queue for this channel */
+	poll_wait(filp, &channel->in_queue, p);
+
+	spin_lock_irqsave(&channel->pdev->spinlock, flags);
+	if (channel->buffer[channel->read].state == TSPP_BUF_STATE_DATA)
+		mask = POLLIN | POLLRDNORM;
+
+	spin_unlock_irqrestore(&channel->pdev->spinlock, flags);
+
+	return mask;
+}
+
+static ssize_t tspp_release(struct inode *inode, struct file *filp)
+{
+	struct tspp_channel *channel;
+	channel = filp->private_data;
+
+	pr_info("tspp_release");
+	tspp_close_channel(channel);
+
+	return 0;
+}
+
+static ssize_t tspp_read(struct file *filp, char __user *buf, size_t count,
+			 loff_t *f_pos)
+{
+	size_t size = 0;
+	size_t transferred = 0;
+	struct tspp_channel *channel;
+	struct tspp_mem_buffer *buffer;
+	channel = filp->private_data;
+
+	TSPP_DEBUG("tspp_read");
+	buffer = &channel->buffer[channel->read];
+	/* see if we have any buffers ready to read */
+	while (buffer->state != TSPP_BUF_STATE_DATA) {
+		if (filp->f_flags & O_NONBLOCK) {
+			pr_warn("tspp: nothing to read on channel %i!",
+				channel->id);
+			return -EAGAIN;
+		}
+		/* go to sleep if there is nothing to read */
+	   TSPP_DEBUG("tspp: sleeping");
+		if (wait_event_interruptible(channel->in_queue,
+			(buffer->state == TSPP_BUF_STATE_DATA))) {
+			pr_err("tspp: rude awakening\n");
+			return -ERESTARTSYS;
+		}
+	}
+
+	while (buffer->state == TSPP_BUF_STATE_DATA) {
+		size = min(count, buffer->filled);
+		TSPP_DEBUG("tspp: reading channel %i buffer %i size %i",
+			channel->id, channel->read, size);
+		if (size == 0)
+			break;
+
+#ifndef TSPP_USE_DMA_ALLOC_COHERENT
+		/* unmap buffer (invalidates processor cache) */
+		if (buffer->mem.phys_base) {
+			dma_unmap_single(NULL,
+				buffer->mem.phys_base,
+				buffer->mem.size,
+				DMA_FROM_DEVICE);
+			buffer->mem.phys_base = 0;
+		}
+#endif
+
+		if (copy_to_user(buf, buffer->mem.base +
+			buffer->read_index, size)) {
+			pr_err("tspp: error copying to user buffer");
+			return -EFAULT;
+		}
+		buf += size;
+		count -= size;
+		transferred += size;
+		buffer->read_index += size;
+
+		/* after reading the end of the buffer, requeue it,
+			and set up for reading the next one */
+		if (buffer->read_index ==
+			channel->buffer[channel->read].filled) {
+			buffer->state = TSPP_BUF_STATE_WAITING;
+#ifndef TSPP_USE_DMA_ALLOC_COHERENT
+			buffer->mem.phys_base = dma_map_single(NULL,
+				buffer->mem.base,
+				buffer->mem.size,
+				DMA_FROM_DEVICE);
+			if (!dma_mapping_error(NULL,
+			buffer->mem.phys_base)) {
+#endif
+				if (sps_transfer_one(channel->pipe,
+					buffer->mem.phys_base,
+					buffer->mem.size,
+					channel,
+					SPS_IOVEC_FLAG_INT |
+					SPS_IOVEC_FLAG_EOT))
+					pr_err("tspp: can't submit transfer");
+				else {
+					channel->read++;
+					if (channel->read == TSPP_NUM_BUFFERS)
+						channel->read = 0;
+				}
+#ifndef TSPP_USE_DMA_ALLOC_COHERENT
+			}
+#endif
+		}
+	}
+
+	return transferred;
+}
+
+static long tspp_ioctl(struct file *filp,
+			unsigned int param0, unsigned long param1)
+{
+	int rc = -1;
+	struct tspp_channel *channel;
+	channel = filp->private_data;
+
+	if (!param1)
+		return -EINVAL;
+
+	switch (param0) {
+	case TSPP_IOCTL_SELECT_SOURCE:
+		rc = tspp_select_source(channel,
+			(struct tspp_select_source *)param1);
+		break;
+	case TSPP_IOCTL_ADD_FILTER:
+		rc = tspp_add_filter(channel,
+			(struct tspp_filter *)param1);
+		break;
+	case TSPP_IOCTL_REMOVE_FILTER:
+		rc = tspp_remove_filter(channel,
+			(struct tspp_filter *)param1);
+		break;
+	case TSPP_IOCTL_SET_KEY:
+		rc = tspp_set_key(channel,
+			(struct tspp_key *)param1);
+		break;
+	case TSPP_IOCTL_SET_IV:
+		rc = tspp_set_iv(channel,
+			(struct tspp_iv *)param1);
+		break;
+	case TSPP_IOCTL_SET_SYSTEM_KEYS:
+		rc = tspp_set_system_keys(channel,
+			(struct tspp_system_keys *)param1);
+		break;
+	case TSPP_IOCTL_BUFFER_SIZE:
+		rc = tspp_set_buffer_size(channel,
+			(struct tspp_buffer *)param1);
+		break;
+	case TSPP_IOCTL_LOOPBACK:
+		loopback_mode = param1;
+		rc = 0;
+		break;
+	default:
+		pr_err("tspp: Unknown ioctl %i", param0);
+	}
+
+	/* normalize the return code in case one of the subfunctions does
+		something weird */
+	if (rc != 0)
+		rc = 1;
+
+	return rc;
+}
+
+/*** debugfs ***/
+#ifdef TSPP_USE_DEBUGFS
+static int debugfs_iomem_x32_set(void *data, u64 val)
+{
+	writel_relaxed(val, data);
+	wmb();
+	return 0;
+}
+
+static int debugfs_iomem_x32_get(void *data, u64 *val)
+{
+	*val = readl_relaxed(data);
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x32, debugfs_iomem_x32_get,
+			debugfs_iomem_x32_set, "0x%08llx");
+
+static void tsif_debugfs_init(struct tspp_tsif_device *tsif_device,
+	int instance)
+{
+	char name[10];
+	snprintf(name, 10, "tsif%i", instance);
+	tsif_device->dent_tsif = debugfs_create_dir(
+	      name, NULL);
+	if (tsif_device->dent_tsif) {
+		int i;
+		void __iomem *base = tsif_device->base;
+		for (i = 0; i < ARRAY_SIZE(debugfs_tsif_regs); i++) {
+			tsif_device->debugfs_tsif_regs[i] =
+			   debugfs_create_file(
+				debugfs_tsif_regs[i].name,
+				debugfs_tsif_regs[i].mode,
+				tsif_device->dent_tsif,
+				base + debugfs_tsif_regs[i].offset,
+				&fops_iomem_x32);
+		}
+	}
+}
+
+static void tsif_debugfs_exit(struct tspp_tsif_device *tsif_device)
+{
+	if (tsif_device->dent_tsif) {
+		int i;
+		debugfs_remove_recursive(tsif_device->dent_tsif);
+		tsif_device->dent_tsif = NULL;
+		for (i = 0; i < ARRAY_SIZE(debugfs_tsif_regs); i++)
+			tsif_device->debugfs_tsif_regs[i] = NULL;
+	}
+}
+
+static void tspp_debugfs_init(struct tspp_device *device, int instance)
+{
+	char name[10];
+	snprintf(name, 10, "tspp%i", instance);
+	device->dent = debugfs_create_dir(
+	      name, NULL);
+	if (device->dent) {
+		int i;
+		void __iomem *base = device->base;
+		for (i = 0; i < ARRAY_SIZE(debugfs_tspp_regs); i++) {
+			device->debugfs_regs[i] =
+			   debugfs_create_file(
+				debugfs_tspp_regs[i].name,
+				debugfs_tspp_regs[i].mode,
+				device->dent,
+				base + debugfs_tspp_regs[i].offset,
+				&fops_iomem_x32);
+		}
+	}
+}
+
+static void tspp_debugfs_exit(struct tspp_device *device)
+{
+	if (device->dent) {
+		int i;
+		debugfs_remove_recursive(device->dent);
+		device->dent = NULL;
+		for (i = 0; i < ARRAY_SIZE(debugfs_tspp_regs); i++)
+			device->debugfs_regs[i] = NULL;
+	}
+}
+#endif /* TSPP_USE_DEBUGFS */
+
+static const struct file_operations tspp_fops = {
+	.owner   = THIS_MODULE,
+	.read    = tspp_read,
+	.open    = tspp_open,
+	.poll    = tspp_poll,
+	.release = tspp_release,
+	.unlocked_ioctl   = tspp_ioctl,
+};
+
+static int tspp_channel_init(struct tspp_channel *channel)
+{
+	channel->bufsize = TSPP_MIN_BUFFER_SIZE;
+	channel->read = 0;
+	channel->waiting = 0;
+	cdev_init(&channel->cdev, &tspp_fops);
+	channel->cdev.owner = THIS_MODULE;
+	channel->id = MINOR(tspp_minor);
+	init_waitqueue_head(&channel->in_queue);
+	channel->buffer_count = 0;
+	channel->filter_count = 0;
+
+	if (cdev_add(&channel->cdev, tspp_minor++, 1) != 0) {
+		pr_err("tspp: cdev_add failed");
+		return 1;
+	}
+
+	channel->dd = device_create(tspp_class, NULL, channel->cdev.dev,
+		channel, "tspp%02d", channel->id);
+	if (IS_ERR(channel->dd)) {
+		pr_err("tspp: device_create failed: %i",
+			(int)PTR_ERR(channel->dd));
+		cdev_del(&channel->cdev);
+		return 1;
+	}
+
+	return 0;
+}
+
+static int __devinit msm_tspp_probe(struct platform_device *pdev)
+{
+	int rc = -ENODEV;
+	u32 version;
+	u32 i;
+	struct msm_tspp_platform_data *data;
+	struct tspp_device *device;
+	struct resource *mem_tsif0;
+	struct resource *mem_tsif1;
+	struct resource *mem_tspp;
+	struct resource *mem_bam;
+	struct tspp_channel *channel;
+
+	/* must have platform data */
+	data = pdev->dev.platform_data;
+	if (!data) {
+		pr_err("tspp: Platform data not available");
+		rc = -EINVAL;
+		goto out;
+	}
+
+	/* check for valid device id */
+	if ((pdev->id < 0) || (pdev->id >= 1)) {
+		pr_err("tspp: Invalid device ID %d", pdev->id);
+		rc = -EINVAL;
+		goto out;
+	}
+
+	/* OK, we will use this device */
+	device = kzalloc(sizeof(struct tspp_device), GFP_KERNEL);
+	if (!device) {
+		pr_err("tspp: Failed to allocate memory for device");
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	/* set up references */
+	device->pdev = pdev;
+	platform_set_drvdata(pdev, device);
+
+	/* map clocks */
+	if (data->tsif_pclk) {
+		device->tsif_pclk = clk_get(NULL, data->tsif_pclk);
+		if (IS_ERR(device->tsif_pclk)) {
+			pr_err("tspp: failed to get %s",
+				data->tsif_pclk);
+			rc = PTR_ERR(device->tsif_pclk);
+			device->tsif_pclk = NULL;
+			goto err_pclock;
+		}
+	}
+	if (data->tsif_ref_clk) {
+		device->tsif_ref_clk = clk_get(NULL, data->tsif_ref_clk);
+		if (IS_ERR(device->tsif_ref_clk)) {
+			pr_err("tspp: failed to get %s",
+				data->tsif_ref_clk);
+			rc = PTR_ERR(device->tsif_ref_clk);
+			device->tsif_ref_clk = NULL;
+			goto err_refclock;
+		}
+	}
+
+	/* map I/O memory */
+	mem_tsif0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem_tsif0) {
+		pr_err("tspp: Missing tsif0 MEM resource");
+		rc = -ENXIO;
+		goto err_res_tsif0;
+	}
+	device->tsif[0].base = ioremap(mem_tsif0->start,
+		resource_size(mem_tsif0));
+	if (!device->tsif[0].base) {
+		pr_err("tspp: ioremap failed");
+		goto err_map_tsif0;
+	}
+
+	mem_tsif1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!mem_tsif1) {
+		dev_err(&pdev->dev, "Missing tsif1 MEM resource");
+		rc = -ENXIO;
+		goto err_res_tsif1;
+	}
+	device->tsif[1].base = ioremap(mem_tsif1->start,
+		resource_size(mem_tsif1));
+	if (!device->tsif[1].base) {
+		dev_err(&pdev->dev, "ioremap failed");
+		goto err_map_tsif1;
+	}
+
+	mem_tspp = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+	if (!mem_tspp) {
+		dev_err(&pdev->dev, "Missing MEM resource");
+		rc = -ENXIO;
+		goto err_res_dev;
+	}
+	device->base = ioremap(mem_tspp->start, resource_size(mem_tspp));
+	if (!device->base) {
+		dev_err(&pdev->dev, "ioremap failed");
+		goto err_map_dev;
+	}
+
+	mem_bam = platform_get_resource(pdev, IORESOURCE_MEM, 3);
+	if (!mem_bam) {
+		pr_err("tspp: Missing bam MEM resource");
+		rc = -ENXIO;
+		goto err_res_bam;
+	}
+	memset(&device->bam_props, 0, sizeof(device->bam_props));
+	device->bam_props.phys_addr = mem_bam->start;
+	device->bam_props.virt_addr = ioremap(mem_bam->start,
+		resource_size(mem_bam));
+	if (!device->bam_props.virt_addr) {
+		dev_err(&pdev->dev, "ioremap failed");
+		goto err_map_bam;
+	}
+
+	/* map TSPP IRQ */
+	rc = platform_get_irq(pdev, 0);
+	if (rc > 0) {
+		device->tspp_irq = rc;
+		rc = request_irq(device->tspp_irq, tspp_isr, IRQF_SHARED,
+				 dev_name(&pdev->dev), device);
+		if (rc) {
+			dev_err(&pdev->dev, "failed to request IRQ %d : %d",
+				device->tspp_irq, rc);
+			goto err_irq;
+		}
+	} else {
+		dev_err(&pdev->dev, "failed to get tspp IRQ");
+		goto err_irq;
+	}
+
+	/* BAM IRQ */
+	device->bam_irq = TSIF_BAM_IRQ;
+
+	/* GPIOs */
+	rc = tspp_start_gpios(device);
+	if (rc)
+		goto err_gpio;
+
+	/* power management */
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+
+#ifdef TSPP_USE_DEBUGFS
+	tspp_debugfs_init(device, 0);
+
+	for (i = 0; i < TSPP_TSIF_INSTANCES; i++)
+		tsif_debugfs_init(&device->tsif[i], i);
+#endif /* TSPP_USE_DEBUGFS */
+
+	wake_lock_init(&device->wake_lock, WAKE_LOCK_SUSPEND,
+		dev_name(&pdev->dev));
+
+	/* set up pointers to ram-based 'registers' */
+	tspp_filter_table[0] = TSPP_PID_FILTER_TABLE0 + device->base;
+	tspp_filter_table[1] = TSPP_PID_FILTER_TABLE1 + device->base;
+	tspp_filter_table[2] = TSPP_PID_FILTER_TABLE2 + device->base;
+	tspp_key_table = TSPP_DATA_KEY + device->base;
+	tspp_global_performance = TSPP_GLOBAL_PERFORMANCE + device->base;
+	tspp_pipe_context = TSPP_PIPE_CONTEXT + device->base;
+	tspp_pipe_performance = TSPP_PIPE_PERFORMANCE + device->base;
+
+	device->bam_props.summing_threshold = 0x10;
+	device->bam_props.irq = device->bam_irq;
+	device->bam_props.manage = SPS_BAM_MGR_LOCAL;
+
+	if (sps_register_bam_device(&device->bam_props,
+		&device->bam_handle) != 0) {
+		pr_err("tspp: failed to register bam");
+		goto err_bam;
+	}
+
+	if (device->tsif_pclk && clk_enable(device->tsif_pclk) != 0) {
+		dev_err(&pdev->dev, "Can't start pclk");
+		goto err_pclk;
+	}
+	if (device->tsif_ref_clk && clk_enable(device->tsif_ref_clk) != 0) {
+		dev_err(&pdev->dev, "Can't start ref clk");
+		goto err_refclk;
+	}
+
+	spin_lock_init(&device->spinlock);
+	tasklet_init(&device->tlet, tspp_sps_complete_tlet,
+			(unsigned long)device);
+
+	/* initialize everything to a known state */
+	tspp_global_reset(device);
+
+	version = readl_relaxed(device->base + TSPP_VERSION);
+	if (version != 1)
+		pr_warn("tspp: unrecognized hw version=%i", version);
+
+	/* update the channels with the device */
+	for (i = 0; i < TSPP_NUM_CHANNELS; i++) {
+		channel = &channel_list[i];
+		channel->pdev = device;
+	}
+
+	/* everything is ok */
+	return 0;
+
+err_refclk:
+	if (device->tsif_pclk)
+		clk_disable(device->tsif_pclk);
+err_pclk:
+	sps_deregister_bam_device(device->bam_handle);
+err_bam:
+#ifdef TSPP_USE_DEBUGFS
+	tspp_debugfs_exit(device);
+	for (i = 0; i < TSPP_TSIF_INSTANCES; i++)
+		tsif_debugfs_exit(&device->tsif[i]);
+#endif /* TSPP_USE_DEBUGFS */
+err_gpio:
+err_irq:
+	tspp_stop_gpios(device);
+	iounmap(device->bam_props.virt_addr);
+err_map_bam:
+err_res_bam:
+	iounmap(device->base);
+err_map_dev:
+err_res_dev:
+	iounmap(device->tsif[1].base);
+err_map_tsif1:
+err_res_tsif1:
+	iounmap(device->tsif[0].base);
+err_map_tsif0:
+err_res_tsif0:
+	if (device->tsif_ref_clk)
+		clk_put(device->tsif_ref_clk);
+err_refclock:
+	if (device->tsif_pclk)
+		clk_put(device->tsif_pclk);
+err_pclock:
+	kfree(device);
+
+out:
+	return rc;
+}
+
+static int __devexit msm_tspp_remove(struct platform_device *pdev)
+{
+#ifdef TSPP_USE_DEBUGFS
+	u32 i;
+#endif /* TSPP_USE_DEBUGFS */
+
+	struct tspp_device *device = platform_get_drvdata(pdev);
+
+	sps_deregister_bam_device(device->bam_handle);
+
+#ifdef TSPP_USE_DEBUGFS
+	for (i = 0; i < TSPP_TSIF_INSTANCES; i++)
+		tsif_debugfs_exit(&device->tsif[i]);
+#endif /* TSPP_USE_DEBUGFS */
+
+	wake_lock_destroy(&device->wake_lock);
+	free_irq(device->tspp_irq, device);
+	tspp_stop_gpios(device);
+
+	iounmap(device->bam_props.virt_addr);
+	iounmap(device->base);
+	for (i = 0; i < TSPP_TSIF_INSTANCES; i++)
+		iounmap(device->tsif[i].base);
+
+	if (device->tsif_ref_clk)
+		clk_put(device->tsif_ref_clk);
+
+	if (device->tsif_pclk)
+		clk_put(device->tsif_pclk);
+
+	pm_runtime_disable(&pdev->dev);
+	pm_runtime_put(&pdev->dev);
+	kfree(device);
+
+	return 0;
+}
+
+/*** power management ***/
+
+static int tspp_runtime_suspend(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: suspending...");
+	return 0;
+}
+
+static int tspp_runtime_resume(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: resuming...");
+	return 0;
+}
+
+static const struct dev_pm_ops tspp_dev_pm_ops = {
+	.runtime_suspend = tspp_runtime_suspend,
+	.runtime_resume = tspp_runtime_resume,
+};
+
+static struct platform_driver msm_tspp_driver = {
+	.probe          = msm_tspp_probe,
+	.remove         = __exit_p(msm_tspp_remove),
+	.driver         = {
+		.name   = "msm_tspp",
+		.pm     = &tspp_dev_pm_ops,
+	},
+};
+
+
+static int __init mod_init(void)
+{
+	u32 i;
+	int rc;
+
+	/* first register the driver, and check hardware */
+	rc = platform_driver_register(&msm_tspp_driver);
+	if (rc) {
+		pr_err("tspp: platform_driver_register failed: %d", rc);
+		goto err_register;
+	}
+
+	/* now make the char devs (channels) */
+	rc = alloc_chrdev_region(&tspp_minor, 0, TSPP_NUM_CHANNELS, "tspp");
+	if (rc) {
+		pr_err("tspp: alloc_chrdev_region failed: %d", rc);
+		goto err_devrgn;
+	}
+
+	tspp_class = class_create(THIS_MODULE, "tspp");
+	if (IS_ERR(tspp_class)) {
+		rc = PTR_ERR(tspp_class);
+		pr_err("tspp: Error creating class: %d", rc);
+		goto err_class;
+	}
+
+	for (i = 0; i < TSPP_NUM_CHANNELS; i++) {
+		if (tspp_channel_init(&channel_list[i]) != 0) {
+			pr_err("tspp_channel_init failed");
+			break;
+		}
+	}
+
+	return 0;
+
+err_class:
+	unregister_chrdev_region(0, TSPP_NUM_CHANNELS);
+err_devrgn:
+	platform_driver_unregister(&msm_tspp_driver);
+err_register:
+	return rc;
+}
+
+static void __exit mod_exit(void)
+{
+	u32 i;
+	struct tspp_channel *channel;
+
+	/* first delete upper layer interface */
+	for (i = 0; i < TSPP_NUM_CHANNELS; i++) {
+		channel = &channel_list[i];
+		device_destroy(tspp_class, channel->cdev.dev);
+		cdev_del(&channel->cdev);
+	}
+	class_destroy(tspp_class);
+	unregister_chrdev_region(0, TSPP_NUM_CHANNELS);
+
+	/* now delete low level driver */
+	platform_driver_unregister(&msm_tspp_driver);
+}
+
+module_init(mod_init);
+module_exit(mod_exit);
+
+MODULE_DESCRIPTION("TSPP character device interface");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/tzcom.c b/drivers/misc/tzcom.c
new file mode 100644
index 0000000..3b943c8
--- /dev/null
+++ b/drivers/misc/tzcom.c
@@ -0,0 +1,1248 @@
+/* Qualcomm TrustZone communicator driver
+ *
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define KMSG_COMPONENT "TZCOM"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/cdev.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/android_pmem.h>
+#include <linux/io.h>
+#include <linux/ion.h>
+#include <linux/tzcom.h>
+#include <linux/clk.h>
+#include <mach/scm.h>
+#include <mach/peripheral-loader.h>
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
+#include <mach/socinfo.h>
+#include "tzcomi.h"
+
+#define TZCOM_DEV "tzcom"
+
+#define TZSCHEDULER_CMD_ID 1 /* CMD id of the trustzone scheduler */
+
+#undef PDEBUG
+#define PDEBUG(fmt, args...) pr_debug("%s(%i, %s): " fmt "\n", \
+		__func__, current->pid, current->comm, ## args)
+
+#undef PERR
+#define PERR(fmt, args...) pr_err("%s(%i, %s): " fmt "\n", \
+		__func__, current->pid, current->comm, ## args)
+
+#undef PWARN
+#define PWARN(fmt, args...) pr_warning("%s(%i, %s): " fmt "\n", \
+		__func__, current->pid, current->comm, ## args)
+
+
+static uint32_t tzcom_perf_client;
+static struct class *driver_class;
+static dev_t tzcom_device_no;
+static struct cdev tzcom_cdev;
+struct ion_client *ion_clnt;
+static u8 *sb_in_virt;
+static s32 sb_in_phys;
+static size_t sb_in_length = 20 * SZ_1K;
+static u8 *sb_out_virt;
+static s32 sb_out_phys;
+static size_t sb_out_length = 20 * SZ_1K;
+
+static void *pil;
+
+static atomic_t svc_instance_ctr = ATOMIC_INIT(0);
+static DEFINE_MUTEX(sb_in_lock);
+static DEFINE_MUTEX(sb_out_lock);
+static DEFINE_MUTEX(send_cmd_lock);
+static DEFINE_MUTEX(tzcom_bw_mutex);
+static int tzcom_bw_count;
+static struct clk *tzcom_bus_clk;
+struct tzcom_callback_list {
+	struct list_head      list;
+	struct tzcom_callback callback;
+};
+
+struct tzcom_registered_svc_list {
+	struct list_head                 list;
+	struct tzcom_register_svc_op_req svc;
+	wait_queue_head_t                next_cmd_wq;
+	int                              next_cmd_flag;
+};
+
+struct tzcom_data_t {
+	struct list_head  callback_list_head;
+	struct mutex      callback_list_lock;
+	struct list_head  registered_svc_list_head;
+	spinlock_t        registered_svc_list_lock;
+	wait_queue_head_t cont_cmd_wq;
+	int               cont_cmd_flag;
+	u32               handled_cmd_svc_instance_id;
+	int               abort;
+	wait_queue_head_t abort_wq;
+	atomic_t          ioctl_count;
+};
+
+static int tzcom_enable_bus_scaling(void)
+{
+	int ret = 0;
+	if (!tzcom_perf_client)
+		return -EINVAL;
+
+	if (IS_ERR_OR_NULL(tzcom_bus_clk))
+		return -EINVAL;
+
+	mutex_lock(&tzcom_bw_mutex);
+	if (!tzcom_bw_count) {
+		ret = msm_bus_scale_client_update_request(
+				tzcom_perf_client, 1);
+		if (ret) {
+			pr_err("Bandwidth request failed (%d)\n", ret);
+		} else {
+			ret = clk_enable(tzcom_bus_clk);
+			if (ret)
+				pr_err("Clock enable failed\n");
+		}
+	}
+	if (ret)
+		msm_bus_scale_client_update_request(tzcom_perf_client, 0);
+	else
+		tzcom_bw_count++;
+	mutex_unlock(&tzcom_bw_mutex);
+	return ret;
+}
+
+static void tzcom_disable_bus_scaling(void)
+{
+	if (!tzcom_perf_client)
+		return ;
+
+	if (IS_ERR_OR_NULL(tzcom_bus_clk))
+		return ;
+
+	mutex_lock(&tzcom_bw_mutex);
+	if (tzcom_bw_count > 0)
+		if (tzcom_bw_count-- == 1) {
+			msm_bus_scale_client_update_request(tzcom_perf_client,
+								0);
+			clk_disable(tzcom_bus_clk);
+		}
+	mutex_unlock(&tzcom_bw_mutex);
+}
+
+static int tzcom_scm_call(const void *cmd_buf, size_t cmd_len,
+		void *resp_buf, size_t resp_len)
+{
+	return scm_call(SCM_SVC_TZSCHEDULER, TZSCHEDULER_CMD_ID,
+			cmd_buf, cmd_len, resp_buf, resp_len);
+}
+
+static s32 tzcom_virt_to_phys(u8 *virt)
+{
+	if (virt >= sb_in_virt &&
+			virt < (sb_in_virt + sb_in_length)) {
+		return sb_in_phys + (virt - sb_in_virt);
+	} else if (virt >= sb_out_virt &&
+			virt < (sb_out_virt + sb_out_length)) {
+		return sb_out_phys + (virt - sb_out_virt);
+	} else {
+		return virt_to_phys(virt);
+	}
+}
+
+static u8 *tzcom_phys_to_virt(s32 phys)
+{
+	if (phys >= sb_in_phys &&
+			phys < (sb_in_phys + sb_in_length)) {
+		return sb_in_virt + (phys - sb_in_phys);
+	} else if (phys >= sb_out_phys &&
+			phys < (sb_out_phys + sb_out_length)) {
+		return sb_out_virt + (phys - sb_out_phys);
+	} else {
+		return phys_to_virt(phys);
+	}
+}
+
+static int __tzcom_is_svc_unique(struct tzcom_data_t *data,
+		struct tzcom_register_svc_op_req svc)
+{
+	struct tzcom_registered_svc_list *ptr;
+	int unique = 1;
+	unsigned long flags;
+
+	spin_lock_irqsave(&data->registered_svc_list_lock, flags);
+	list_for_each_entry(ptr, &data->registered_svc_list_head, list) {
+		if (ptr->svc.svc_id == svc.svc_id) {
+			PERR("Service id: %u is already registered",
+					ptr->svc.svc_id);
+			unique = 0;
+			break;
+		} else if (svc.cmd_id_low >= ptr->svc.cmd_id_low &&
+				svc.cmd_id_low <= ptr->svc.cmd_id_high) {
+			PERR("Cmd id low falls in the range of another"
+					"registered service");
+			unique = 0;
+			break;
+		} else if (svc.cmd_id_high >= ptr->svc.cmd_id_low &&
+				svc.cmd_id_high <= ptr->svc.cmd_id_high) {
+			PERR("Cmd id high falls in the range of another"
+					"registered service");
+			unique = 0;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&data->registered_svc_list_lock, flags);
+	return unique;
+}
+
+static int tzcom_register_service(struct tzcom_data_t *data, void __user *argp)
+{
+	int ret;
+	unsigned long flags;
+	struct tzcom_register_svc_op_req rcvd_svc;
+	struct tzcom_registered_svc_list *new_entry;
+
+	ret = copy_from_user(&rcvd_svc, argp, sizeof(rcvd_svc));
+
+	if (ret) {
+		PERR("copy_from_user failed");
+		return ret;
+	}
+
+	PDEBUG("svc_id: %u, cmd_id_low: %u, cmd_id_high: %u",
+			rcvd_svc.svc_id, rcvd_svc.cmd_id_low,
+			rcvd_svc.cmd_id_high);
+	if (!__tzcom_is_svc_unique(data, rcvd_svc)) {
+		PERR("Provided service is not unique");
+		return -EINVAL;
+	}
+
+	rcvd_svc.instance_id = atomic_inc_return(&svc_instance_ctr);
+
+	ret = copy_to_user(argp, &rcvd_svc, sizeof(rcvd_svc));
+	if (ret) {
+		PERR("copy_to_user failed");
+		return ret;
+	}
+
+	new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
+	if (!new_entry) {
+		PERR("kmalloc failed");
+		return -ENOMEM;
+	}
+	memcpy(&new_entry->svc, &rcvd_svc, sizeof(rcvd_svc));
+	new_entry->next_cmd_flag = 0;
+	init_waitqueue_head(&new_entry->next_cmd_wq);
+
+	spin_lock_irqsave(&data->registered_svc_list_lock, flags);
+	list_add_tail(&new_entry->list, &data->registered_svc_list_head);
+	spin_unlock_irqrestore(&data->registered_svc_list_lock, flags);
+
+
+	return ret;
+}
+
+static int tzcom_unregister_service(struct tzcom_data_t *data,
+		void __user *argp)
+{
+	int ret = 0;
+	unsigned long flags;
+	struct tzcom_unregister_svc_op_req req;
+	struct tzcom_registered_svc_list *ptr, *next;
+	ret = copy_from_user(&req, argp, sizeof(req));
+	if (ret) {
+		PERR("copy_from_user failed");
+		return ret;
+	}
+
+	spin_lock_irqsave(&data->registered_svc_list_lock, flags);
+	list_for_each_entry_safe(ptr, next, &data->registered_svc_list_head,
+			list) {
+		if (req.svc_id == ptr->svc.svc_id &&
+				req.instance_id == ptr->svc.instance_id) {
+			wake_up_all(&ptr->next_cmd_wq);
+			list_del(&ptr->list);
+			kfree(ptr);
+			spin_unlock_irqrestore(&data->registered_svc_list_lock,
+					flags);
+			return 0;
+		}
+	}
+	spin_unlock_irqrestore(&data->registered_svc_list_lock, flags);
+
+	return -EINVAL;
+}
+
+static int __tzcom_is_cont_cmd(struct tzcom_data_t *data)
+{
+	int ret;
+	ret = (data->cont_cmd_flag != 0);
+	return ret || data->abort;
+}
+
+/**
+ *   +---------+                              +-----+       +-----------------+
+ *   |  TZCOM  |                              | SCM |       | TZCOM_SCHEDULER |
+ *   +----+----+                              +--+--+       +--------+--------+
+ *        |                                      |                   |
+ *        |        scm_call                      |                   |
+ *        |------------------------------------->|                   |
+ *        |  cmd_buf = struct tzcom_command {    |                   |
+ *        |              cmd_type,               |------------------>|
+ * +------+------------- sb_in_cmd_addr,         |                   |
+ * |      |              sb_in_cmd_len           |                   |
+ * |      |            }                         |                   |
+ * |      |   resp_buf = struct tzcom_response { |                   |
+ * |                         cmd_status,         |                   |
+ * |             +---------- sb_in_rsp_addr,     |                   |
+ * |             |           sb_in_rsp_len       |<------------------|
+ * |             |         }
+ * |             |                            struct tzcom_callback {---------+
+ * |             |                                uint32_t cmd_id;            |
+ * |             |                                uint32_t sb_out_cb_data_len;|
+ * |             +---------------+                uint32_t sb_out_cb_data_off;|
+ * |                             |            }                               |
+ * |    _________________________|_______________________________             |
+ * |    +-----------------------+| +----------------------+                   |
+ * +--->+ copy from req.cmd_buf |+>| copy to req.resp_buf |                   |
+ *      +-----------------------+  +----------------------+                   |
+ *      _________________________________________________________             |
+ *                               INPUT SHARED BUFFER                          |
+ *   +------------------------------------------------------------------------+
+ *   |  _________________________________________________________
+ *   |  +---------------------------------------------+
+ *   +->| cmd_id | data_len | data_off |   data...    |
+ *      +---------------------------------------------+
+ *                                     |<------------>|copy to next_cmd.req_buf
+ *      _________________________________________________________
+ *                              OUTPUT SHARED BUFFER
+ */
+static int __tzcom_send_cmd(struct tzcom_data_t *data,
+			struct tzcom_send_cmd_op_req *req)
+{
+	int ret = 0;
+	unsigned long flags;
+	u32 reqd_len_sb_in = 0;
+	u32 reqd_len_sb_out = 0;
+	struct tzcom_command cmd;
+	struct tzcom_response resp;
+	struct tzcom_callback *next_callback;
+	void *cb_data = NULL;
+	struct tzcom_callback_list *new_entry;
+	struct tzcom_callback *cb;
+	size_t new_entry_len = 0;
+	struct tzcom_registered_svc_list *ptr_svc;
+
+	if (req->cmd_buf == NULL || req->resp_buf == NULL) {
+		PERR("cmd buffer or response buffer is null");
+		return -EINVAL;
+	}
+
+	if (req->cmd_len <= 0 || req->resp_len <= 0 ||
+		req->cmd_len > sb_in_length || req->resp_len > sb_in_length) {
+		PERR("cmd buffer length or "
+				"response buffer length not valid");
+		return -EINVAL;
+	}
+	PDEBUG("received cmd_req.req: 0x%p",
+				req->cmd_buf);
+	PDEBUG("received cmd_req.rsp size: %u, ptr: 0x%p",
+			req->resp_len,
+			req->resp_buf);
+
+	reqd_len_sb_in = req->cmd_len + req->resp_len;
+	if (reqd_len_sb_in > sb_in_length) {
+		PDEBUG("Not enough memory to fit cmd_buf and "
+				"resp_buf. Required: %u, Available: %u",
+				reqd_len_sb_in, sb_in_length);
+		return -ENOMEM;
+	}
+
+	/* Copy req->cmd_buf to SB in and set
+	 * req->resp_buf to SB in + cmd_len
+	 */
+	mutex_lock(&sb_in_lock);
+	PDEBUG("Before memcpy on sb_in");
+	memcpy(sb_in_virt, req->cmd_buf, req->cmd_len);
+	PDEBUG("After memcpy on sb_in");
+
+	/* cmd_type will always be a new here */
+	cmd.cmd_type = TZ_SCHED_CMD_NEW;
+	cmd.sb_in_cmd_addr = (u8 *) tzcom_virt_to_phys(sb_in_virt);
+	cmd.sb_in_cmd_len = req->cmd_len;
+
+	resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
+	resp.sb_in_rsp_addr = (u8 *) tzcom_virt_to_phys(sb_in_virt +
+			req->cmd_len);
+	resp.sb_in_rsp_len = req->resp_len;
+
+	PDEBUG("before call tzcom_scm_call, cmd_id = : %u", req->cmd_id);
+	PDEBUG("before call tzcom_scm_call, sizeof(cmd) = : %u", sizeof(cmd));
+
+	ret = tzcom_scm_call((const void *) &cmd, sizeof(cmd),
+			&resp, sizeof(resp));
+	mutex_unlock(&sb_in_lock);
+
+	if (ret) {
+		PERR("tzcom_scm_call failed with err: %d", ret);
+		return ret;
+	}
+
+	while (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
+		/*
+		 * If cmd is incomplete, get the callback cmd out from SB out
+		 * and put it on the list
+		 */
+		PDEBUG("cmd_status is incomplete.");
+		next_callback = (struct tzcom_callback *)sb_out_virt;
+
+		mutex_lock(&sb_out_lock);
+		reqd_len_sb_out = sizeof(*next_callback)
+					+ next_callback->sb_out_cb_data_len;
+		if (reqd_len_sb_out > sb_out_length ||
+			reqd_len_sb_out < sizeof(*next_callback) ||
+			next_callback->sb_out_cb_data_len > sb_out_length) {
+			PERR("Incorrect callback data length"
+					" Required: %u, Available: %u, Min: %u",
+					reqd_len_sb_out, sb_out_length,
+					sizeof(*next_callback));
+			mutex_unlock(&sb_out_lock);
+			return -ENOMEM;
+		}
+
+		/* Assumption is cb_data_off is sizeof(tzcom_callback) */
+		new_entry_len = sizeof(*new_entry)
+					+ next_callback->sb_out_cb_data_len;
+		new_entry = kmalloc(new_entry_len, GFP_KERNEL);
+		if (!new_entry) {
+			PERR("kmalloc failed");
+			mutex_unlock(&sb_out_lock);
+			return -ENOMEM;
+		}
+
+		cb = &new_entry->callback;
+		cb->cmd_id = next_callback->cmd_id;
+		cb->sb_out_cb_data_len = next_callback->sb_out_cb_data_len;
+		cb->sb_out_cb_data_off = sizeof(*cb);
+
+		cb_data = (u8 *)next_callback
+				+ next_callback->sb_out_cb_data_off;
+		memcpy((u8 *)cb + cb->sb_out_cb_data_off, cb_data,
+				next_callback->sb_out_cb_data_len);
+		mutex_unlock(&sb_out_lock);
+
+		mutex_lock(&data->callback_list_lock);
+		list_add_tail(&new_entry->list, &data->callback_list_head);
+		mutex_unlock(&data->callback_list_lock);
+
+		/*
+		 * We don't know which service can handle the command. so we
+		 * wake up all blocking services and let them figure out if
+		 * they can handle the given command.
+		 */
+		spin_lock_irqsave(&data->registered_svc_list_lock, flags);
+		list_for_each_entry(ptr_svc,
+				&data->registered_svc_list_head, list) {
+			ptr_svc->next_cmd_flag = 1;
+			wake_up_interruptible(&ptr_svc->next_cmd_wq);
+		}
+		spin_unlock_irqrestore(&data->registered_svc_list_lock,
+				flags);
+
+		PDEBUG("waking up next_cmd_wq and "
+				"waiting for cont_cmd_wq");
+		if (wait_event_interruptible(data->cont_cmd_wq,
+				__tzcom_is_cont_cmd(data))) {
+			PWARN("Interrupted: exiting send_cmd loop");
+			return -ERESTARTSYS;
+		}
+
+		if (data->abort) {
+			PERR("Aborting driver");
+			return -ENODEV;
+		}
+		data->cont_cmd_flag = 0;
+		cmd.cmd_type = TZ_SCHED_CMD_PENDING;
+		mutex_lock(&sb_in_lock);
+		ret = tzcom_scm_call((const void *) &cmd, sizeof(cmd), &resp,
+				sizeof(resp));
+		mutex_unlock(&sb_in_lock);
+		if (ret) {
+			PERR("tzcom_scm_call failed with err: %d", ret);
+			return ret;
+		}
+	}
+
+	mutex_lock(&sb_in_lock);
+	resp.sb_in_rsp_addr = sb_in_virt + cmd.sb_in_cmd_len;
+	resp.sb_in_rsp_len = req->resp_len;
+	memcpy(req->resp_buf, resp.sb_in_rsp_addr, resp.sb_in_rsp_len);
+	/* Zero out memory for security purpose */
+	memset(sb_in_virt, 0, reqd_len_sb_in);
+	mutex_unlock(&sb_in_lock);
+
+	return ret;
+}
+
+
+static int tzcom_send_cmd(struct tzcom_data_t *data, void __user *argp)
+{
+	int ret = 0;
+	struct tzcom_send_cmd_op_req req;
+
+	ret = copy_from_user(&req, argp, sizeof(req));
+	if (ret) {
+		PERR("copy_from_user failed");
+		return ret;
+	}
+	ret = __tzcom_send_cmd(data, &req);
+	if (ret)
+		return ret;
+
+	PDEBUG("sending cmd_req->rsp "
+			"size: %u, ptr: 0x%p", req.resp_len,
+			req.resp_buf);
+	ret = copy_to_user(argp, &req, sizeof(req));
+	if (ret) {
+		PDEBUG("copy_to_user failed");
+		return ret;
+	}
+	return ret;
+}
+
+static int __tzcom_send_cmd_req_clean_up(
+			struct tzcom_send_cmd_fd_op_req *req)
+{
+	char *field;
+	uint32_t *update;
+	int ret = 0;
+	int i = 0;
+
+	for (i = 0; i < MAX_ION_FD; i++) {
+		if (req->ifd_data[i].fd != 0) {
+			field = (char *)req->cmd_buf +
+					req->ifd_data[i].cmd_buf_offset;
+			update = (uint32_t *) field;
+			*update = 0;
+		}
+	}
+	return ret;
+}
+
+static int __tzcom_update_with_phy_addr(
+			struct tzcom_send_cmd_fd_op_req *req)
+{
+	struct ion_handle *ihandle;
+	char *field;
+	uint32_t *update;
+	ion_phys_addr_t pa;
+	int ret = 0;
+	int i = 0;
+	uint32_t length;
+
+	for (i = 0; i < MAX_ION_FD; i++) {
+		if (req->ifd_data[i].fd != 0) {
+			/* Get the handle of the shared fd */
+			ihandle = ion_import_fd(ion_clnt, req->ifd_data[i].fd);
+			if (ihandle == NULL) {
+				PERR("Ion client can't retrieve the handle\n");
+				return -ENOMEM;
+			}
+			field = (char *) req->cmd_buf +
+						req->ifd_data[i].cmd_buf_offset;
+			update = (uint32_t *) field;
+
+			/* Populate the cmd data structure with the phys_addr */
+			ret = ion_phys(ion_clnt, ihandle, &pa, &length);
+			if (ret)
+				return -ENOMEM;
+
+			*update = (uint32_t)pa;
+			ion_free(ion_clnt, ihandle);
+		}
+	}
+	return ret;
+}
+
+static int tzcom_send_cmd_with_fd(struct tzcom_data_t *data,
+					void __user *argp)
+{
+	int ret = 0;
+	struct tzcom_send_cmd_fd_op_req req;
+	struct tzcom_send_cmd_op_req send_cmd_req;
+
+	ret = copy_from_user(&req, argp, sizeof(req));
+	if (ret) {
+		PERR("copy_from_user failed");
+		return ret;
+	}
+
+	send_cmd_req.cmd_id = req.cmd_id;
+	send_cmd_req.cmd_buf = req.cmd_buf;
+	send_cmd_req.cmd_len = req.cmd_len;
+	send_cmd_req.resp_buf = req.resp_buf;
+	send_cmd_req.resp_len = req.resp_len;
+
+	ret = __tzcom_update_with_phy_addr(&req);
+	if (ret)
+		return ret;
+	ret = __tzcom_send_cmd(data, &send_cmd_req);
+	__tzcom_send_cmd_req_clean_up(&req);
+
+	if (ret)
+		return ret;
+
+	PDEBUG("sending cmd_req->rsp "
+			"size: %u, ptr: 0x%p", req.resp_len,
+			req.resp_buf);
+	ret = copy_to_user(argp, &req, sizeof(req));
+	if (ret) {
+		PDEBUG("copy_to_user failed");
+		return ret;
+	}
+	return ret;
+}
+
+static struct tzcom_registered_svc_list *__tzcom_find_svc(
+		struct tzcom_data_t *data,
+		uint32_t instance_id)
+{
+	struct tzcom_registered_svc_list *entry;
+	unsigned long flags;
+
+	spin_lock_irqsave(&data->registered_svc_list_lock, flags);
+	list_for_each_entry(entry,
+			&data->registered_svc_list_head, list) {
+		if (entry->svc.instance_id == instance_id)
+			break;
+	}
+	spin_unlock_irqrestore(&data->registered_svc_list_lock, flags);
+
+	return entry;
+}
+
+static int __tzcom_copy_cmd(struct tzcom_data_t *data,
+		struct tzcom_next_cmd_op_req *req,
+		struct tzcom_registered_svc_list *ptr_svc)
+{
+	int found = 0;
+	int ret = -EAGAIN;
+	struct tzcom_callback_list *entry, *next;
+	struct tzcom_callback *cb;
+
+	PDEBUG("In here");
+	mutex_lock(&data->callback_list_lock);
+	PDEBUG("Before looping through cmd and svc lists.");
+	list_for_each_entry_safe(entry, next, &data->callback_list_head, list) {
+		cb = &entry->callback;
+		if (req->svc_id == ptr_svc->svc.svc_id &&
+			req->instance_id == ptr_svc->svc.instance_id &&
+			cb->cmd_id >= ptr_svc->svc.cmd_id_low &&
+			cb->cmd_id <= ptr_svc->svc.cmd_id_high) {
+			PDEBUG("Found matching entry");
+			found = 1;
+			if (cb->sb_out_cb_data_len <= req->req_len) {
+				PDEBUG("copying cmd buffer %p to req "
+					"buffer %p, length: %u",
+					(u8 *)cb + cb->sb_out_cb_data_off,
+					req->req_buf, cb->sb_out_cb_data_len);
+				req->cmd_id = cb->cmd_id;
+				ret = copy_to_user(req->req_buf,
+					(u8 *)cb + cb->sb_out_cb_data_off,
+					cb->sb_out_cb_data_len);
+				if (ret) {
+					PERR("copy_to_user failed");
+					break;
+				}
+				list_del(&entry->list);
+				kfree(entry);
+				ret = 0;
+			} else {
+				PERR("callback data buffer is "
+					"larger than provided buffer."
+					"Required: %u, Provided: %u",
+					cb->sb_out_cb_data_len,
+					req->req_len);
+				ret = -ENOMEM;
+			}
+			break;
+		}
+	}
+	PDEBUG("After looping through cmd and svc lists.");
+	mutex_unlock(&data->callback_list_lock);
+	return ret;
+}
+
+static int __tzcom_is_next_cmd(struct tzcom_data_t *data,
+		struct tzcom_registered_svc_list *svc)
+{
+	int ret;
+	ret = (svc->next_cmd_flag != 0);
+	return ret || data->abort;
+}
+
+static int tzcom_read_next_cmd(struct tzcom_data_t *data, void __user *argp)
+{
+	int ret = 0;
+	struct tzcom_next_cmd_op_req req;
+	struct tzcom_registered_svc_list *this_svc;
+
+	ret = copy_from_user(&req, argp, sizeof(req));
+	if (ret) {
+		PERR("copy_from_user failed");
+		return ret;
+	}
+
+	if (req.instance_id > atomic_read(&svc_instance_ctr)) {
+		PERR("Invalid instance_id for the request");
+		return -EINVAL;
+	}
+
+	if (!req.req_buf || req.req_len == 0) {
+		PERR("Invalid request buffer or buffer length");
+		return -EINVAL;
+	}
+
+	PDEBUG("Before next_cmd loop");
+	this_svc = __tzcom_find_svc(data, req.instance_id);
+
+	while (1) {
+		PDEBUG("Before wait_event next_cmd.");
+		if (wait_event_interruptible(this_svc->next_cmd_wq,
+				__tzcom_is_next_cmd(data, this_svc))) {
+			PWARN("Interrupted: exiting wait_next_cmd loop");
+			/* woken up for different reason */
+			return -ERESTARTSYS;
+		}
+
+		if (data->abort) {
+			PERR("Aborting driver");
+			return -ENODEV;
+		}
+		PDEBUG("After wait_event next_cmd.");
+		this_svc->next_cmd_flag = 0;
+
+		ret = __tzcom_copy_cmd(data, &req, this_svc);
+		if (ret == 0) {
+			PDEBUG("Successfully found svc for cmd");
+			data->handled_cmd_svc_instance_id = req.instance_id;
+			break;
+		} else if (ret == -ENOMEM) {
+			PERR("Not enough memory");
+			return ret;
+		}
+	}
+	ret = copy_to_user(argp, &req, sizeof(req));
+	if (ret) {
+		PERR("copy_to_user failed");
+		return ret;
+	}
+	PDEBUG("copy_to_user is done.");
+	return ret;
+}
+
+static int tzcom_cont_cmd(struct tzcom_data_t *data, void __user *argp)
+{
+	int ret = 0;
+	struct tzcom_cont_cmd_op_req req;
+	ret = copy_from_user(&req, argp, sizeof(req));
+	if (ret) {
+		PERR("copy_from_user failed");
+		return ret;
+	}
+
+	/*
+	 * Only the svc instance that handled the cmd (in read_next_cmd method)
+	 * can call continue cmd
+	 */
+	if (data->handled_cmd_svc_instance_id != req.instance_id) {
+		PWARN("Only the service instance that handled the last "
+				"callback can continue cmd. "
+				"Expected: %u, Received: %u",
+				data->handled_cmd_svc_instance_id,
+				req.instance_id);
+		return -EINVAL;
+	}
+
+	if (req.resp_buf) {
+		mutex_lock(&sb_out_lock);
+		memcpy(sb_out_virt, req.resp_buf, req.resp_len);
+		mutex_unlock(&sb_out_lock);
+	}
+
+	data->cont_cmd_flag = 1;
+	wake_up_interruptible(&data->cont_cmd_wq);
+	return ret;
+}
+
+static int tzcom_abort(struct tzcom_data_t *data)
+{
+	int ret = 0;
+	unsigned long flags;
+	struct tzcom_registered_svc_list *lsvc, *nsvc;
+	if (data->abort) {
+		PERR("Already aborting");
+		return -EINVAL;
+	}
+
+	data->abort = 1;
+
+	PDEBUG("Waking up cont_cmd_wq");
+	wake_up_all(&data->cont_cmd_wq);
+
+	spin_lock_irqsave(&data->registered_svc_list_lock, flags);
+	PDEBUG("Before waking up service wait queues");
+	list_for_each_entry_safe(lsvc, nsvc,
+			&data->registered_svc_list_head, list) {
+		wake_up_all(&lsvc->next_cmd_wq);
+	}
+	spin_unlock_irqrestore(&data->registered_svc_list_lock, flags);
+
+	PDEBUG("ioctl_count before loop: %d", atomic_read(&data->ioctl_count));
+	while (atomic_read(&data->ioctl_count) > 0) {
+		if (wait_event_interruptible(data->abort_wq,
+				atomic_read(&data->ioctl_count) <= 0)) {
+			PERR("Interrupted from abort");
+			ret = -ERESTARTSYS;
+			break;
+		}
+	}
+	return ret;
+}
+
+static long tzcom_ioctl(struct file *file, unsigned cmd,
+		unsigned long arg)
+{
+	int ret = 0;
+	struct tzcom_data_t *tzcom_data = file->private_data;
+	void __user *argp = (void __user *) arg;
+	PDEBUG("enter tzcom_ioctl()");
+	if (tzcom_data->abort) {
+		PERR("Aborting tzcom driver");
+		return -ENODEV;
+	}
+
+	switch (cmd) {
+	case TZCOM_IOCTL_REGISTER_SERVICE_REQ: {
+		PDEBUG("ioctl register_service_req()");
+		atomic_inc(&tzcom_data->ioctl_count);
+		ret = tzcom_register_service(tzcom_data, argp);
+		atomic_dec(&tzcom_data->ioctl_count);
+		wake_up_interruptible(&tzcom_data->abort_wq);
+		if (ret)
+			PERR("failed tzcom_register_service: %d", ret);
+		break;
+	}
+	case TZCOM_IOCTL_UNREGISTER_SERVICE_REQ: {
+		PDEBUG("ioctl unregister_service_req()");
+		atomic_inc(&tzcom_data->ioctl_count);
+		ret = tzcom_unregister_service(tzcom_data, argp);
+		atomic_dec(&tzcom_data->ioctl_count);
+		wake_up_interruptible(&tzcom_data->abort_wq);
+		if (ret)
+			PERR("failed tzcom_unregister_service: %d", ret);
+		break;
+	}
+	case TZCOM_IOCTL_SEND_CMD_REQ: {
+		PDEBUG("ioctl send_cmd_req()");
+		/* Only one client allowed here at a time */
+		mutex_lock(&send_cmd_lock);
+		atomic_inc(&tzcom_data->ioctl_count);
+		ret = tzcom_send_cmd(tzcom_data, argp);
+		atomic_dec(&tzcom_data->ioctl_count);
+		wake_up_interruptible(&tzcom_data->abort_wq);
+		mutex_unlock(&send_cmd_lock);
+		if (ret)
+			PERR("failed tzcom_send_cmd: %d", ret);
+		break;
+	}
+	case TZCOM_IOCTL_SEND_CMD_FD_REQ: {
+		PDEBUG("ioctl send_cmd_req()");
+		/* Only one client allowed here at a time */
+		mutex_lock(&send_cmd_lock);
+		atomic_inc(&tzcom_data->ioctl_count);
+		ret = tzcom_send_cmd_with_fd(tzcom_data, argp);
+		atomic_dec(&tzcom_data->ioctl_count);
+		wake_up_interruptible(&tzcom_data->abort_wq);
+		mutex_unlock(&send_cmd_lock);
+		if (ret)
+			PERR("failed tzcom_send_cmd: %d", ret);
+		break;
+	}
+	case TZCOM_IOCTL_READ_NEXT_CMD_REQ: {
+		PDEBUG("ioctl read_next_cmd_req()");
+		atomic_inc(&tzcom_data->ioctl_count);
+		ret = tzcom_read_next_cmd(tzcom_data, argp);
+		atomic_dec(&tzcom_data->ioctl_count);
+		wake_up_interruptible(&tzcom_data->abort_wq);
+		if (ret)
+			PERR("failed tzcom_read_next: %d", ret);
+		break;
+	}
+	case TZCOM_IOCTL_CONTINUE_CMD_REQ: {
+		PDEBUG("ioctl continue_cmd_req()");
+		atomic_inc(&tzcom_data->ioctl_count);
+		ret = tzcom_cont_cmd(tzcom_data, argp);
+		atomic_dec(&tzcom_data->ioctl_count);
+		wake_up_interruptible(&tzcom_data->abort_wq);
+		if (ret)
+			PERR("failed tzcom_cont_cmd: %d", ret);
+		break;
+	}
+	case TZCOM_IOCTL_ABORT_REQ: {
+		PDEBUG("ioctl abort_req()");
+		ret = tzcom_abort(tzcom_data);
+		if (ret)
+			PERR("failed tzcom_abort: %d", ret);
+		break;
+	}
+	default:
+		return -EINVAL;
+	}
+	return ret;
+}
+
+static int tzcom_open(struct inode *inode, struct file *file)
+{
+	int ret;
+	long pil_error;
+	struct tz_pr_init_sb_req_s sb_out_init_req;
+	struct tz_pr_init_sb_rsp_s sb_out_init_rsp;
+	void *rsp_addr_virt;
+	struct tzcom_command cmd;
+	struct tzcom_response resp;
+	struct tzcom_data_t *tzcom_data;
+
+	PDEBUG("In here");
+
+	ret = tzcom_enable_bus_scaling();
+
+	if (pil == NULL) {
+		pil = pil_get("tzapps");
+		if (IS_ERR(pil)) {
+			PERR("Playready PIL image load failed");
+			pil_error = PTR_ERR(pil);
+			pil = NULL;
+			return pil_error;
+		}
+		PDEBUG("tzapps image loaded successfully");
+	}
+
+	sb_out_init_req.pr_cmd = TZ_SCHED_CMD_ID_INIT_SB_OUT;
+	sb_out_init_req.sb_len = sb_out_length;
+	sb_out_init_req.sb_ptr = tzcom_virt_to_phys(sb_out_virt);
+	PDEBUG("sb_out_init_req { pr_cmd: %d, sb_len: %u, "
+			"sb_ptr (phys): 0x%x }",
+			sb_out_init_req.pr_cmd,
+			sb_out_init_req.sb_len,
+			sb_out_init_req.sb_ptr);
+
+	mutex_lock(&sb_in_lock);
+	PDEBUG("Before memcpy on sb_in");
+	memcpy(sb_in_virt, &sb_out_init_req, sizeof(sb_out_init_req));
+	PDEBUG("After memcpy on sb_in");
+
+	/* It will always be a new cmd from this method */
+	cmd.cmd_type = TZ_SCHED_CMD_NEW;
+	cmd.sb_in_cmd_addr = (u8 *) tzcom_virt_to_phys(sb_in_virt);
+	cmd.sb_in_cmd_len = sizeof(sb_out_init_req);
+	PDEBUG("tzcom_command { cmd_type: %u, sb_in_cmd_addr: %p, "
+			"sb_in_cmd_len: %u }",
+			cmd.cmd_type, cmd.sb_in_cmd_addr, cmd.sb_in_cmd_len);
+
+	resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
+
+	PDEBUG("Before scm_call for sb_init");
+	ret = tzcom_scm_call(&cmd, sizeof(cmd), &resp, sizeof(resp));
+	if (ret) {
+		PERR("tzcom_scm_call failed with err: %d", ret);
+		return ret;
+	}
+	PDEBUG("After scm_call for sb_init");
+
+	PDEBUG("tzcom_response after scm cmd_status: %u", resp.cmd_status);
+	if (resp.cmd_status == TZ_SCHED_STATUS_COMPLETE) {
+		resp.sb_in_rsp_addr = (u8 *)cmd.sb_in_cmd_addr +
+				cmd.sb_in_cmd_len;
+		resp.sb_in_rsp_len = sizeof(sb_out_init_rsp);
+		PDEBUG("tzcom_response sb_in_rsp_addr: %p, sb_in_rsp_len: %u",
+				resp.sb_in_rsp_addr, resp.sb_in_rsp_len);
+		rsp_addr_virt = tzcom_phys_to_virt((unsigned long)
+				resp.sb_in_rsp_addr);
+		PDEBUG("Received response phys: %p, virt: %p",
+				resp.sb_in_rsp_addr, rsp_addr_virt);
+		memcpy(&sb_out_init_rsp, rsp_addr_virt, resp.sb_in_rsp_len);
+	} else {
+		PERR("Error with SB initialization");
+		mutex_unlock(&sb_in_lock);
+		return -EPERM;
+	}
+	mutex_unlock(&sb_in_lock);
+
+	PDEBUG("sb_out_init_rsp { pr_cmd: %d, ret: %d }",
+			sb_out_init_rsp.pr_cmd, sb_out_init_rsp.ret);
+
+	if (sb_out_init_rsp.ret) {
+		PERR("sb_out_init_req failed: %d", sb_out_init_rsp.ret);
+		return -EPERM;
+	}
+
+	tzcom_data = kmalloc(sizeof(*tzcom_data), GFP_KERNEL);
+	if (!tzcom_data) {
+		PERR("kmalloc failed");
+		return -ENOMEM;
+	}
+	file->private_data = tzcom_data;
+
+	INIT_LIST_HEAD(&tzcom_data->callback_list_head);
+	mutex_init(&tzcom_data->callback_list_lock);
+
+	INIT_LIST_HEAD(&tzcom_data->registered_svc_list_head);
+	spin_lock_init(&tzcom_data->registered_svc_list_lock);
+
+	init_waitqueue_head(&tzcom_data->cont_cmd_wq);
+	tzcom_data->cont_cmd_flag = 0;
+	tzcom_data->handled_cmd_svc_instance_id = 0;
+	tzcom_data->abort = 0;
+	init_waitqueue_head(&tzcom_data->abort_wq);
+	atomic_set(&tzcom_data->ioctl_count, 0);
+	return 0;
+}
+
+static int tzcom_release(struct inode *inode, struct file *file)
+{
+	struct tzcom_data_t *tzcom_data = file->private_data;
+	struct tzcom_callback_list *lcb, *ncb;
+	struct tzcom_registered_svc_list *lsvc, *nsvc;
+	unsigned long flags;
+	PDEBUG("In here");
+
+	if (!tzcom_data->abort) {
+		PDEBUG("Calling abort");
+		tzcom_abort(tzcom_data);
+	}
+
+	PDEBUG("Before removing callback list");
+	mutex_lock(&tzcom_data->callback_list_lock);
+	list_for_each_entry_safe(lcb, ncb,
+			&tzcom_data->callback_list_head, list) {
+		list_del(&lcb->list);
+		kfree(lcb);
+	}
+	mutex_unlock(&tzcom_data->callback_list_lock);
+	PDEBUG("After removing callback list");
+
+	PDEBUG("Before removing svc list");
+	spin_lock_irqsave(&tzcom_data->registered_svc_list_lock, flags);
+	list_for_each_entry_safe(lsvc, nsvc,
+			&tzcom_data->registered_svc_list_head, list) {
+		list_del(&lsvc->list);
+		kfree(lsvc);
+	}
+	spin_unlock_irqrestore(&tzcom_data->registered_svc_list_lock, flags);
+	PDEBUG("After removing svc list");
+	if (pil != NULL) {
+		pil_put(pil);
+		pil = NULL;
+	}
+	PDEBUG("Freeing tzcom data");
+	kfree(tzcom_data);
+	tzcom_disable_bus_scaling();
+	return 0;
+}
+
+static struct msm_bus_paths tzcom_bw_table[] = {
+	{
+		.vectors = (struct msm_bus_vectors[]){
+			{
+				.src = MSM_BUS_MASTER_SPS,
+				.dst = MSM_BUS_SLAVE_EBI_CH0,
+			},
+		},
+		.num_paths = 1,
+	},
+	{
+		.vectors = (struct msm_bus_vectors[]){
+			{
+				.src = MSM_BUS_MASTER_SPS,
+				.dst = MSM_BUS_SLAVE_EBI_CH0,
+				.ib = (492 * 8) * 1000000UL,
+				.ab = (492 * 8) *  100000UL,
+			},
+		},
+		.num_paths = 1,
+	},
+
+};
+
+static struct msm_bus_scale_pdata tzcom_bus_pdata = {
+	.usecase = tzcom_bw_table,
+	.num_usecases = ARRAY_SIZE(tzcom_bw_table),
+	.name = "tzcom",
+};
+static const struct file_operations tzcom_fops = {
+		.owner = THIS_MODULE,
+		.unlocked_ioctl = tzcom_ioctl,
+		.open = tzcom_open,
+		.release = tzcom_release
+};
+
+static int __init tzcom_init(void)
+{
+	int rc;
+	struct device *class_dev;
+
+	PDEBUG("Hello tzcom");
+
+	rc = alloc_chrdev_region(&tzcom_device_no, 0, 1, TZCOM_DEV);
+	if (rc < 0) {
+		PERR("alloc_chrdev_region failed %d", rc);
+		return rc;
+	}
+
+	driver_class = class_create(THIS_MODULE, TZCOM_DEV);
+	if (IS_ERR(driver_class)) {
+		rc = -ENOMEM;
+		PERR("class_create failed %d", rc);
+		goto unregister_chrdev_region;
+	}
+
+	class_dev = device_create(driver_class, NULL, tzcom_device_no, NULL,
+			TZCOM_DEV);
+	if (!class_dev) {
+		PERR("class_device_create failed %d", rc);
+		rc = -ENOMEM;
+		goto class_destroy;
+	}
+
+	cdev_init(&tzcom_cdev, &tzcom_fops);
+	tzcom_cdev.owner = THIS_MODULE;
+
+	rc = cdev_add(&tzcom_cdev, MKDEV(MAJOR(tzcom_device_no), 0), 1);
+	if (rc < 0) {
+		PERR("cdev_add failed %d", rc);
+		goto class_device_destroy;
+	}
+
+	sb_in_phys = pmem_kalloc(sb_in_length, PMEM_MEMTYPE_EBI1 |
+			PMEM_ALIGNMENT_4K);
+	if (IS_ERR((void *)sb_in_phys)) {
+		PERR("could not allocte in kernel pmem buffers for sb_in");
+		sb_in_phys = 0;
+		rc = -ENOMEM;
+		goto class_device_destroy;
+	}
+	PDEBUG("physical_addr for sb_in: 0x%x", sb_in_phys);
+
+	sb_in_virt = (u8 *) ioremap((unsigned long)sb_in_phys,
+			sb_in_length);
+	if (!sb_in_virt) {
+		PERR("Shared buffer IN allocation failed.");
+		rc = -ENOMEM;
+		goto class_device_destroy;
+	}
+	PDEBUG("sb_in virt address: %p, phys address: 0x%x",
+			sb_in_virt, tzcom_virt_to_phys(sb_in_virt));
+
+	sb_out_phys = pmem_kalloc(sb_out_length, PMEM_MEMTYPE_EBI1 |
+			PMEM_ALIGNMENT_4K);
+	if (IS_ERR((void *)sb_out_phys)) {
+		PERR("could not allocte in kernel pmem buffers for sb_out");
+		sb_out_phys = 0;
+		rc = -ENOMEM;
+		goto class_device_destroy;
+	}
+	PDEBUG("physical_addr for sb_out: 0x%x", sb_out_phys);
+
+	sb_out_virt = (u8 *) ioremap((unsigned long)sb_out_phys,
+			sb_out_length);
+	if (!sb_out_virt) {
+		PERR("Shared buffer OUT allocation failed.");
+		rc = -ENOMEM;
+		goto class_device_destroy;
+	}
+	PDEBUG("sb_out virt address: %p, phys address: 0x%x",
+			sb_out_virt, tzcom_virt_to_phys(sb_out_virt));
+	ion_clnt = msm_ion_client_create(0x03, "tzcom");
+	/* Initialized in tzcom_open */
+	pil = NULL;
+
+	tzcom_perf_client = msm_bus_scale_register_client(
+					&tzcom_bus_pdata);
+	if (!tzcom_perf_client)
+		pr_err("Unable to register bus client");
+
+	tzcom_bus_clk = clk_get(class_dev, "bus_clk");
+	if (IS_ERR(tzcom_bus_clk)) {
+		tzcom_bus_clk = NULL;
+	} else  if (tzcom_bus_clk != NULL) {
+		pr_debug("Enabled DFAB clock\n");
+		clk_set_rate(tzcom_bus_clk, 64000000);
+	}
+	return 0;
+
+class_device_destroy:
+	if (sb_in_virt)
+		iounmap(sb_in_virt);
+	if (sb_in_phys)
+		pmem_kfree(sb_in_phys);
+	if (sb_out_virt)
+		iounmap(sb_out_virt);
+	if (sb_out_phys)
+		pmem_kfree(sb_out_phys);
+	device_destroy(driver_class, tzcom_device_no);
+class_destroy:
+	class_destroy(driver_class);
+unregister_chrdev_region:
+	unregister_chrdev_region(tzcom_device_no, 1);
+	return rc;
+}
+
+static void __exit tzcom_exit(void)
+{
+	PDEBUG("Goodbye tzcom");
+	if (sb_in_virt)
+		iounmap(sb_in_virt);
+	if (sb_in_phys)
+		pmem_kfree(sb_in_phys);
+	if (sb_out_virt)
+		iounmap(sb_out_virt);
+	if (sb_out_phys)
+		pmem_kfree(sb_out_phys);
+	if (pil != NULL) {
+		pil_put(pil);
+		pil = NULL;
+	}
+	clk_put(tzcom_bus_clk);
+	device_destroy(driver_class, tzcom_device_no);
+	class_destroy(driver_class);
+	unregister_chrdev_region(tzcom_device_no, 1);
+	ion_client_destroy(ion_clnt);
+}
+
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Sachin Shah <sachins@codeaurora.org>");
+MODULE_DESCRIPTION("Qualcomm TrustZone Communicator");
+MODULE_VERSION("1.00");
+
+module_init(tzcom_init);
+module_exit(tzcom_exit);
diff --git a/drivers/misc/tzcomi.h b/drivers/misc/tzcomi.h
new file mode 100644
index 0000000..33634cf
--- /dev/null
+++ b/drivers/misc/tzcomi.h
@@ -0,0 +1,112 @@
+/* Qualcomm TrustZone communicator driver
+ *
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __TZCOMI_H_
+#define __TZCOMI_H_
+
+#include <linux/types.h>
+
+enum tz_sched_cmd_id {
+	TZ_SCHED_CMD_ID_INVALID      = 0,
+	TZ_SCHED_CMD_ID_INIT_SB_OUT,    /**< Initialize the shared buffer */
+	TZ_SCHED_CMD_ID_INIT_SB_LOG,    /**< Initialize the logging shared buf */
+	TZ_SCHED_CMD_ID_UNKNOWN         = 0x7FFFFFFE,
+	TZ_SCHED_CMD_ID_MAX             = 0x7FFFFFFF
+};
+
+enum tz_sched_cmd_type {
+	TZ_SCHED_CMD_INVALID = 0,
+	TZ_SCHED_CMD_NEW,      /** New TZ Scheduler Command */
+	TZ_SCHED_CMD_PENDING,  /** Pending cmd...sched will restore stack */
+	TZ_SCHED_CMD_COMPLETE, /** TZ sched command is complete */
+	TZ_SCHED_CMD_MAX     = 0x7FFFFFFF
+};
+
+enum tz_sched_cmd_status {
+	TZ_SCHED_STATUS_INCOMPLETE = 0,
+	TZ_SCHED_STATUS_COMPLETE,
+	TZ_SCHED_STATUS_MAX  = 0x7FFFFFFF
+};
+
+/** Command structure for initializing shared buffers (SB_OUT
+    and SB_LOG)
+*/
+__packed struct tz_pr_init_sb_req_s {
+	/** First 4 bytes should always be command id
+	 * from enum tz_sched_cmd_id */
+	uint32_t                  pr_cmd;
+	/** Pointer to the physical location of sb_out buffer */
+	uint32_t                  sb_ptr;
+	/** length of shared buffer */
+	uint32_t                  sb_len;
+};
+
+
+__packed struct tz_pr_init_sb_rsp_s {
+	/** First 4 bytes should always be command id
+	 * from enum tz_sched_cmd_id */
+	uint32_t                  pr_cmd;
+	/** Return code, 0 for success, Approp error code otherwise */
+	int32_t                   ret;
+};
+
+
+/**
+ * struct tzcom_command - tzcom command buffer
+ * @cmd_type: value from enum tz_sched_cmd_type
+ * @sb_in_cmd_addr: points to physical location of command
+ *                buffer
+ * @sb_in_cmd_len: length of command buffer
+ */
+__packed struct tzcom_command {
+	uint32_t               cmd_type;
+	uint8_t                *sb_in_cmd_addr;
+	uint32_t               sb_in_cmd_len;
+};
+
+/**
+ * struct tzcom_response - tzcom response buffer
+ * @cmd_status: value from enum tz_sched_cmd_status
+ * @sb_in_rsp_addr: points to physical location of response
+ *                buffer
+ * @sb_in_rsp_len: length of command response
+ */
+__packed struct tzcom_response {
+	uint32_t                 cmd_status;
+	uint8_t                  *sb_in_rsp_addr;
+	uint32_t                 sb_in_rsp_len;
+};
+
+/**
+ * struct tzcom_callback - tzcom callback buffer
+ * @cmd_id: command to run in registered service
+ * @sb_out_rsp_addr: points to physical location of response
+ *                buffer
+ * @sb_in_cmd_len: length of command response
+ *
+ * A callback buffer would be laid out in sb_out as follows:
+ *
+ *     --------------------- <--- struct tzcom_callback
+ *     | callback header   |
+ *     --------------------- <--- tzcom_callback.sb_out_cb_data_off
+ *     | callback data     |
+ *     ---------------------
+ */
+__packed struct tzcom_callback {
+	uint32_t cmd_id;
+	uint32_t sb_out_cb_data_len;
+	uint32_t sb_out_cb_data_off;
+};
+
+#endif /* __TZCOMI_H_ */
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index f2eeb38..9116551 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -19,6 +19,14 @@
 	  This is an option for use by developers; most people should
 	  say N here.  This enables MMC core and driver debugging.
 
+config MMC_PERF_PROFILING
+	bool "MMC performance profiling"
+	depends on MMC != n
+	default n
+	help
+	  If you say Y here, support will be added for collecting
+	  performance numbers at the MMC Queue and Host layers.
+
 if MMC
 
 source "drivers/mmc/core/Kconfig"
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 54c8322..990faeb 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -58,6 +58,18 @@
 #define INAND_CMD38_ARG_SECTRIM1 0x81
 #define INAND_CMD38_ARG_SECTRIM2 0x88
 
+#define MMC_SANITIZE_REQ_TIMEOUT 240000 /* msec */
+#define mmc_req_rel_wr(req)	(((req->cmd_flags & REQ_FUA) || \
+			(req->cmd_flags & REQ_META)) && \
+			(rq_data_dir(req) == WRITE))
+#define PACKED_CMD_VER		0x01
+#define PACKED_CMD_WR		0x02
+#define MMC_BLK_UPDATE_STOP_REASON(stats, reason)			\
+	do {								\
+		if (stats->enabled)					\
+			stats->pack_stop_reason[reason]++;		\
+	} while (0)
+
 static DEFINE_MUTEX(block_mutex);
 
 /*
@@ -108,6 +120,7 @@
 	struct device_attribute force_ro;
 	struct device_attribute power_ro_lock;
 	int	area_type;
+	struct device_attribute num_wr_reqs_to_start_packing;
 };
 
 static DEFINE_MUTEX(open_lock);
@@ -123,9 +136,21 @@
 	MMC_BLK_NOMEDIUM,
 };
 
+enum {
+        MMC_PACKED_N_IDX = -1,
+        MMC_PACKED_N_ZERO,
+        MMC_PACKED_N_SINGLE,
+};
+
 module_param(perdev_minors, int, 0444);
 MODULE_PARM_DESC(perdev_minors, "Minors numbers to allocate per device");
 
+static inline void mmc_blk_clear_packed(struct mmc_queue_req *mqrq)
+{
+        mqrq->packed_cmd = MMC_PACKED_NONE;
+        mqrq->packed_num = MMC_PACKED_N_ZERO;
+}
+
 static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk)
 {
 	struct mmc_blk_data *md;
@@ -259,6 +284,38 @@
 	return ret;
 }
 
+static ssize_t
+num_wr_reqs_to_start_packing_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+	int num_wr_reqs_to_start_packing;
+	int ret;
+
+	num_wr_reqs_to_start_packing = md->queue.num_wr_reqs_to_start_packing;
+
+	ret = snprintf(buf, PAGE_SIZE, "%d\n", num_wr_reqs_to_start_packing);
+
+	mmc_blk_put(md);
+	return ret;
+}
+
+static ssize_t
+num_wr_reqs_to_start_packing_store(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf, size_t count)
+{
+	int value;
+	struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+
+	sscanf(buf, "%d", &value);
+	if (value >= 0)
+		md->queue.num_wr_reqs_to_start_packing = value;
+
+	mmc_blk_put(md);
+	return count;
+}
+
 static int mmc_blk_open(struct block_device *bdev, fmode_t mode)
 {
 	struct mmc_blk_data *md = mmc_blk_get(bdev->bd_disk);
@@ -873,10 +930,10 @@
 {
 	struct mmc_blk_data *md = mq->data;
 	struct mmc_card *card = md->queue.card;
-	unsigned int from, nr, arg, trim_arg, erase_arg;
+	unsigned int from, nr, arg;
 	int err = 0, type = MMC_BLK_SECDISCARD;
 
-	if (!(mmc_can_secure_erase_trim(card) || mmc_can_sanitize(card))) {
+	if (!(mmc_can_secure_erase_trim(card))) {
 		err = -EOPNOTSUPP;
 		goto out;
 	}
@@ -884,23 +941,10 @@
 	from = blk_rq_pos(req);
 	nr = blk_rq_sectors(req);
 
-	/* The sanitize operation is supported at v4.5 only */
-	if (mmc_can_sanitize(card)) {
-		erase_arg = MMC_ERASE_ARG;
-		trim_arg = MMC_TRIM_ARG;
-	} else {
-		erase_arg = MMC_SECURE_ERASE_ARG;
-		trim_arg = MMC_SECURE_TRIM1_ARG;
-	}
-
-	if (mmc_erase_group_aligned(card, from, nr))
-		arg = erase_arg;
-	else if (mmc_can_trim(card))
-		arg = trim_arg;
-	else {
-		err = -EINVAL;
-		goto out;
-	}
+	if (mmc_can_trim(card) && !mmc_erase_group_aligned(card, from, nr))
+		arg = MMC_SECURE_TRIM1_ARG;
+	else
+		arg = MMC_SECURE_ERASE_ARG;
 retry:
 	if (card->quirks & MMC_QUIRK_INAND_CMD38) {
 		err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
@@ -952,6 +996,47 @@
 	return err ? 0 : 1;
 }
 
+static int mmc_blk_issue_sanitize_rq(struct mmc_queue *mq,
+				      struct request *req)
+{
+	struct mmc_blk_data *md = mq->data;
+	struct mmc_card *card = md->queue.card;
+	int err = 0;
+
+	BUG_ON(!card);
+	BUG_ON(!card->host);
+
+	if (!(mmc_can_sanitize(card) &&
+	     (card->host->caps2 & MMC_CAP2_SANITIZE))) {
+			pr_warning("%s: %s - SANITIZE is not supported\n",
+				   mmc_hostname(card->host), __func__);
+			err = -EOPNOTSUPP;
+			goto out;
+	}
+
+	pr_debug("%s: %s - SANITIZE IN PROGRESS...\n",
+		mmc_hostname(card->host), __func__);
+
+	err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+					EXT_CSD_SANITIZE_START, 1,
+					MMC_SANITIZE_REQ_TIMEOUT);
+
+	if (err)
+		pr_err("%s: %s - mmc_switch() with "
+		       "EXT_CSD_SANITIZE_START failed. err=%d\n",
+		       mmc_hostname(card->host), __func__, err);
+
+	pr_debug("%s: %s - SANITIZE COMPLETED\n", mmc_hostname(card->host),
+					     __func__);
+
+out:
+	spin_lock_irq(&md->lock);
+	__blk_end_request(req, err, blk_rq_bytes(req));
+	spin_unlock_irq(&md->lock);
+
+	return err ? 0 : 1;
+}
+
 static int mmc_blk_issue_flush(struct mmc_queue *mq, struct request *req)
 {
 	struct mmc_blk_data *md = mq->data;
@@ -1086,12 +1171,60 @@
 	if (!brq->data.bytes_xfered)
 		return MMC_BLK_RETRY;
 
+	if (mq_mrq->packed_cmd != MMC_PACKED_NONE) {
+		if (unlikely(brq->data.blocks << 9 != brq->data.bytes_xfered))
+			return MMC_BLK_PARTIAL;
+		else
+			return MMC_BLK_SUCCESS;
+	}
+
 	if (blk_rq_bytes(req) != brq->data.bytes_xfered)
 		return MMC_BLK_PARTIAL;
 
 	return MMC_BLK_SUCCESS;
 }
 
+static int mmc_blk_packed_err_check(struct mmc_card *card,
+				    struct mmc_async_req *areq)
+{
+	struct mmc_queue_req *mq_rq = container_of(areq, struct mmc_queue_req,
+			mmc_active);
+	struct request *req = mq_rq->req;
+	int err, check, status;
+	u8 ext_csd[512];
+
+	check = mmc_blk_err_check(card, areq);
+	err = get_card_status(card, &status, 0);
+	if (err) {
+		pr_err("%s: error %d sending status command\n",
+				req->rq_disk->disk_name, err);
+		return MMC_BLK_ABORT;
+	}
+
+	if (status & R1_EXP_EVENT) {
+		err = mmc_send_ext_csd(card, ext_csd);
+		if (err) {
+			pr_err("%s: error %d sending ext_csd\n",
+					req->rq_disk->disk_name, err);
+			return MMC_BLK_ABORT;
+		}
+
+		if ((ext_csd[EXT_CSD_EXP_EVENTS_STATUS] &
+					EXT_CSD_PACKED_FAILURE) &&
+				(ext_csd[EXT_CSD_PACKED_CMD_STATUS] &
+				 EXT_CSD_PACKED_GENERIC_ERROR)) {
+			if (ext_csd[EXT_CSD_PACKED_CMD_STATUS] &
+					EXT_CSD_PACKED_INDEXED_ERROR) {
+				mq_rq->packed_fail_idx =
+				  ext_csd[EXT_CSD_PACKED_FAILURE_INDEX] - 1;
+				return MMC_BLK_PARTIAL;
+			}
+		}
+	}
+
+	return check;
+}
+
 static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
 			       struct mmc_card *card,
 			       int disable_multi,
@@ -1246,10 +1379,286 @@
 	mmc_queue_bounce_pre(mqrq);
 }
 
+static void mmc_blk_write_packing_control(struct mmc_queue *mq,
+					  struct request *req)
+{
+	struct mmc_host *host = mq->card->host;
+	int data_dir;
+
+	if (!(host->caps2 & MMC_CAP2_PACKED_WR))
+		return;
+
+	/*
+	 * In case the packing control is not supported by the host, it should
+	 * not have an effect on the write packing. Therefore we have to enable
+	 * the write packing
+	 */
+	if (!(host->caps2 & MMC_CAP2_PACKED_WR_CONTROL)) {
+		mq->wr_packing_enabled = true;
+		return;
+	}
+
+	if (!req || (req && (req->cmd_flags & REQ_FLUSH))) {
+		if (mq->num_of_potential_packed_wr_reqs >
+				mq->num_wr_reqs_to_start_packing)
+			mq->wr_packing_enabled = true;
+		return;
+	}
+
+	data_dir = rq_data_dir(req);
+
+	if (data_dir == READ) {
+		mq->num_of_potential_packed_wr_reqs = 0;
+		mq->wr_packing_enabled = false;
+		return;
+	} else if (data_dir == WRITE) {
+		mq->num_of_potential_packed_wr_reqs++;
+	}
+
+	if (mq->num_of_potential_packed_wr_reqs >
+			mq->num_wr_reqs_to_start_packing)
+		mq->wr_packing_enabled = true;
+
+}
+
+struct mmc_wr_pack_stats *mmc_blk_get_packed_statistics(struct mmc_card *card)
+{
+	if (!card)
+		return NULL;
+
+	return &card->wr_pack_stats;
+}
+EXPORT_SYMBOL(mmc_blk_get_packed_statistics);
+
+void mmc_blk_init_packed_statistics(struct mmc_card *card)
+{
+	int max_num_of_packed_reqs = 0;
+
+	if (!card || !card->wr_pack_stats.packing_events)
+		return;
+
+	max_num_of_packed_reqs = card->ext_csd.max_packed_writes;
+
+	spin_lock(&card->wr_pack_stats.lock);
+	memset(card->wr_pack_stats.packing_events, 0,
+		(max_num_of_packed_reqs + 1) *
+	       sizeof(*card->wr_pack_stats.packing_events));
+	memset(&card->wr_pack_stats.pack_stop_reason, 0,
+		sizeof(card->wr_pack_stats.pack_stop_reason));
+	card->wr_pack_stats.enabled = true;
+	spin_unlock(&card->wr_pack_stats.lock);
+}
+EXPORT_SYMBOL(mmc_blk_init_packed_statistics);
+
+static u8 mmc_blk_prep_packed_list(struct mmc_queue *mq, struct request *req)
+{
+	struct request_queue *q = mq->queue;
+	struct mmc_card *card = mq->card;
+	struct request *cur = req, *next = NULL;
+	struct mmc_blk_data *md = mq->data;
+	bool en_rel_wr = card->ext_csd.rel_param & EXT_CSD_WR_REL_PARAM_EN;
+	unsigned int req_sectors = 0, phys_segments = 0;
+	unsigned int max_blk_count, max_phys_segs;
+	u8 put_back = 0;
+	u8 max_packed_rw = 0;
+	u8 reqs = 0;
+	struct mmc_wr_pack_stats *stats = &card->wr_pack_stats;
+
+	mmc_blk_clear_packed(mq->mqrq_cur);
+
+	if (!(md->flags & MMC_BLK_CMD23) ||
+			!card->ext_csd.packed_event_en)
+		goto no_packed;
+
+	if (!mq->wr_packing_enabled)
+		goto no_packed;
+
+	if ((rq_data_dir(cur) == WRITE) &&
+			(card->host->caps2 & MMC_CAP2_PACKED_WR))
+		max_packed_rw = card->ext_csd.max_packed_writes;
+
+	if (max_packed_rw == 0)
+		goto no_packed;
+
+	if (mmc_req_rel_wr(cur) &&
+			(md->flags & MMC_BLK_REL_WR) &&
+			!en_rel_wr) {
+		goto no_packed;
+	}
+
+	max_blk_count = min(card->host->max_blk_count,
+			card->host->max_req_size >> 9);
+	if (unlikely(max_blk_count > 0xffff))
+		max_blk_count = 0xffff;
+
+	max_phys_segs = queue_max_segments(q);
+	req_sectors += blk_rq_sectors(cur);
+	phys_segments += cur->nr_phys_segments;
+
+	if (rq_data_dir(cur) == WRITE) {
+		req_sectors++;
+		phys_segments++;
+	}
+
+	spin_lock(&stats->lock);
+
+	while (reqs < max_packed_rw - 1) {
+		spin_lock_irq(q->queue_lock);
+		next = blk_fetch_request(q);
+		spin_unlock_irq(q->queue_lock);
+		if (!next) {
+			MMC_BLK_UPDATE_STOP_REASON(stats, EMPTY_QUEUE);
+			break;
+		}
+
+		if (next->cmd_flags & REQ_DISCARD ||
+				next->cmd_flags & REQ_FLUSH) {
+			MMC_BLK_UPDATE_STOP_REASON(stats, FLUSH_OR_DISCARD);
+			put_back = 1;
+			break;
+		}
+
+		if (rq_data_dir(cur) != rq_data_dir(next)) {
+			MMC_BLK_UPDATE_STOP_REASON(stats, WRONG_DATA_DIR);
+			put_back = 1;
+			break;
+		}
+
+		if (mmc_req_rel_wr(next) &&
+				(md->flags & MMC_BLK_REL_WR) &&
+				!en_rel_wr) {
+			MMC_BLK_UPDATE_STOP_REASON(stats, REL_WRITE);
+			put_back = 1;
+			break;
+		}
+
+		req_sectors += blk_rq_sectors(next);
+		if (req_sectors > max_blk_count) {
+			if (stats->enabled)
+				stats->pack_stop_reason[EXCEEDS_SECTORS]++;
+			put_back = 1;
+			break;
+		}
+
+		phys_segments +=  next->nr_phys_segments;
+		if (phys_segments > max_phys_segs) {
+			MMC_BLK_UPDATE_STOP_REASON(stats, EXCEEDS_SEGMENTS);
+			put_back = 1;
+			break;
+		}
+
+		if (rq_data_dir(next) == WRITE)
+			mq->num_of_potential_packed_wr_reqs++;
+		list_add_tail(&next->queuelist, &mq->mqrq_cur->packed_list);
+		cur = next;
+		reqs++;
+	}
+
+	if (put_back) {
+		spin_lock_irq(q->queue_lock);
+		blk_requeue_request(q, next);
+		spin_unlock_irq(q->queue_lock);
+	}
+
+	if (stats->enabled) {
+		if (reqs + 1 <= card->ext_csd.max_packed_writes)
+			stats->packing_events[reqs + 1]++;
+		if (reqs + 1 == max_packed_rw)
+			MMC_BLK_UPDATE_STOP_REASON(stats, THRESHOLD);
+	}
+
+	spin_unlock(&stats->lock);
+
+	if (reqs > 0) {
+		list_add(&req->queuelist, &mq->mqrq_cur->packed_list);
+		mq->mqrq_cur->packed_num = ++reqs;
+		return reqs;
+	}
+
+no_packed:
+	mmc_blk_clear_packed(mq->mqrq_cur);
+	return 0;
+}
+
+static void mmc_blk_packed_hdr_wrq_prep(struct mmc_queue_req *mqrq,
+					struct mmc_card *card,
+					struct mmc_queue *mq)
+{
+	struct mmc_blk_request *brq = &mqrq->brq;
+	struct request *req = mqrq->req;
+	struct request *prq;
+	struct mmc_blk_data *md = mq->data;
+	bool do_rel_wr;
+	u32 *packed_cmd_hdr = mqrq->packed_cmd_hdr;
+	u8 i = 1;
+
+	mqrq->packed_cmd = MMC_PACKED_WRITE;
+	mqrq->packed_blocks = 0;
+	mqrq->packed_fail_idx = MMC_PACKED_N_IDX;
+
+	memset(packed_cmd_hdr, 0, sizeof(mqrq->packed_cmd_hdr));
+	packed_cmd_hdr[0] = (mqrq->packed_num << 16) |
+		(PACKED_CMD_WR << 8) | PACKED_CMD_VER;
+
+	/*
+	 * Argument for each entry of packed group
+	 */
+	list_for_each_entry(prq, &mqrq->packed_list, queuelist) {
+		do_rel_wr = mmc_req_rel_wr(prq) && (md->flags & MMC_BLK_REL_WR);
+		/* Argument of CMD23*/
+		packed_cmd_hdr[(i * 2)] =
+			(do_rel_wr ? MMC_CMD23_ARG_REL_WR : 0) |
+			blk_rq_sectors(prq);
+		/* Argument of CMD18 or CMD25 */
+		packed_cmd_hdr[((i * 2)) + 1] =
+			mmc_card_blockaddr(card) ?
+			blk_rq_pos(prq) : blk_rq_pos(prq) << 9;
+		mqrq->packed_blocks += blk_rq_sectors(prq);
+		i++;
+	}
+
+	memset(brq, 0, sizeof(struct mmc_blk_request));
+	brq->mrq.cmd = &brq->cmd;
+	brq->mrq.data = &brq->data;
+	brq->mrq.sbc = &brq->sbc;
+	brq->mrq.stop = &brq->stop;
+
+	brq->sbc.opcode = MMC_SET_BLOCK_COUNT;
+	brq->sbc.arg = MMC_CMD23_ARG_PACKED | (mqrq->packed_blocks + 1);
+	brq->sbc.flags = MMC_RSP_R1 | MMC_CMD_AC;
+
+	brq->cmd.opcode = MMC_WRITE_MULTIPLE_BLOCK;
+	brq->cmd.arg = blk_rq_pos(req);
+	if (!mmc_card_blockaddr(card))
+		brq->cmd.arg <<= 9;
+	brq->cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+
+	brq->data.blksz = 512;
+	brq->data.blocks = mqrq->packed_blocks + 1;
+	brq->data.flags |= MMC_DATA_WRITE;
+
+	brq->stop.opcode = MMC_STOP_TRANSMISSION;
+	brq->stop.arg = 0;
+	brq->stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
+
+	mmc_set_data_timeout(&brq->data, card);
+
+	brq->data.sg = mqrq->sg;
+	brq->data.sg_len = mmc_queue_map_sg(mq, mqrq);
+
+	mqrq->mmc_active.mrq = &brq->mrq;
+	mqrq->mmc_active.err_check = mmc_blk_packed_err_check;
+
+	mmc_queue_bounce_pre(mqrq);
+}
+
 static int mmc_blk_cmd_err(struct mmc_blk_data *md, struct mmc_card *card,
 			   struct mmc_blk_request *brq, struct request *req,
 			   int ret)
 {
+	struct mmc_queue_req *mq_rq;
+	mq_rq = container_of(brq, struct mmc_queue_req, brq);
+
 	/*
 	 * If this is an SD card and we're writing, we can first
 	 * mark the known good sectors as ok.
@@ -1268,13 +1677,48 @@
 			spin_unlock_irq(&md->lock);
 		}
 	} else {
-		spin_lock_irq(&md->lock);
-		ret = __blk_end_request(req, 0, brq->data.bytes_xfered);
-		spin_unlock_irq(&md->lock);
+		if (mq_rq->packed_cmd == MMC_PACKED_NONE) {
+			spin_lock_irq(&md->lock);
+			ret = __blk_end_request(req, 0, brq->data.bytes_xfered);
+			spin_unlock_irq(&md->lock);
+		}
 	}
 	return ret;
 }
 
+static int mmc_blk_end_packed_req(struct mmc_queue *mq,
+				  struct mmc_queue_req *mq_rq)
+{
+	struct mmc_blk_data *md = mq->data;
+	struct request *prq;
+	int idx = mq_rq->packed_fail_idx, i = 0;
+	int ret = 0;
+
+	while (!list_empty(&mq_rq->packed_list)) {
+		prq = list_entry_rq(mq_rq->packed_list.next);
+		if (idx == i) {
+			/* retry from error index */
+			mq_rq->packed_num -= idx;
+			mq_rq->req = prq;
+			ret = 1;
+
+			if (mq_rq->packed_num == MMC_PACKED_N_SINGLE) {
+				list_del_init(&prq->queuelist);
+				mmc_blk_clear_packed(mq_rq);
+			}
+			return ret;
+		}
+		list_del_init(&prq->queuelist);
+		spin_lock_irq(&md->lock);
+		__blk_end_request(prq, 0, blk_rq_bytes(prq));
+		spin_unlock_irq(&md->lock);
+		i++;
+	}
+
+	mmc_blk_clear_packed(mq_rq);
+	return ret;
+}
+
 static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
 {
 	struct mmc_blk_data *md = mq->data;
@@ -1283,15 +1727,24 @@
 	int ret = 1, disable_multi = 0, retry = 0, type;
 	enum mmc_blk_status status;
 	struct mmc_queue_req *mq_rq;
-	struct request *req;
+	struct request *req, *prq;
 	struct mmc_async_req *areq;
+	const u8 packed_num = 2;
+	u8 reqs = 0;
 
 	if (!rqc && !mq->mqrq_prev->req)
 		return 0;
 
+	if (rqc)
+		reqs = mmc_blk_prep_packed_list(mq, rqc);
+
 	do {
 		if (rqc) {
-			mmc_blk_rw_rq_prep(mq->mqrq_cur, card, 0, mq);
+			if (reqs >= packed_num)
+				mmc_blk_packed_hdr_wrq_prep(mq->mqrq_cur,
+						card, mq);
+			else
+				mmc_blk_rw_rq_prep(mq->mqrq_cur, card, 0, mq);
 			areq = &mq->mqrq_cur->mmc_active;
 		} else
 			areq = NULL;
@@ -1305,6 +1758,13 @@
 		type = rq_data_dir(req) == READ ? MMC_BLK_READ : MMC_BLK_WRITE;
 		mmc_queue_bounce_post(mq_rq);
 
+		/*
+		 * Check BKOPS urgency from each R1 response
+		 */
+		if (mmc_card_mmc(card) &&
+			(brq->cmd.resp[0] & R1_EXCEPTION_EVENT))
+			mmc_card_set_check_bkops(card);
+
 		switch (status) {
 		case MMC_BLK_SUCCESS:
 		case MMC_BLK_PARTIAL:
@@ -1312,10 +1772,17 @@
 			 * A block was successfully transferred.
 			 */
 			mmc_blk_reset_success(md, type);
-			spin_lock_irq(&md->lock);
-			ret = __blk_end_request(req, 0,
+
+			if (mq_rq->packed_cmd != MMC_PACKED_NONE) {
+				ret = mmc_blk_end_packed_req(mq, mq_rq);
+				break;
+			} else {
+				spin_lock_irq(&md->lock);
+				ret = __blk_end_request(req, 0,
 						brq->data.bytes_xfered);
-			spin_unlock_irq(&md->lock);
+				spin_unlock_irq(&md->lock);
+			}
+
 			/*
 			 * If the blk_end_request function returns non-zero even
 			 * though all data has been transferred and no errors
@@ -1348,7 +1815,8 @@
 			err = mmc_blk_reset(md, card->host, type);
 			if (!err)
 				break;
-			if (err == -ENODEV)
+			if (err == -ENODEV ||
+				mq_rq->packed_cmd != MMC_PACKED_NONE)
 				goto cmd_abort;
 			/* Fall through */
 		}
@@ -1377,27 +1845,66 @@
 		}
 
 		if (ret) {
-			/*
-			 * In case of a incomplete request
-			 * prepare it again and resend.
-			 */
-			mmc_blk_rw_rq_prep(mq_rq, card, disable_multi, mq);
-			mmc_start_req(card->host, &mq_rq->mmc_active, NULL);
+			if (mq_rq->packed_cmd == MMC_PACKED_NONE) {
+				/*
+				 * In case of a incomplete request
+				 * prepare it again and resend.
+				 */
+				mmc_blk_rw_rq_prep(mq_rq, card,
+						disable_multi, mq);
+				mmc_start_req(card->host,
+						&mq_rq->mmc_active, NULL);
+			} else {
+				mmc_blk_packed_hdr_wrq_prep(mq_rq, card, mq);
+				mmc_start_req(card->host,
+						&mq_rq->mmc_active, NULL);
+			}
 		}
 	} while (ret);
 
 	return 1;
 
  cmd_abort:
-	spin_lock_irq(&md->lock);
-	if (mmc_card_removed(card))
-		req->cmd_flags |= REQ_QUIET;
-	while (ret)
-		ret = __blk_end_request(req, -EIO, blk_rq_cur_bytes(req));
-	spin_unlock_irq(&md->lock);
+	if (mq_rq->packed_cmd == MMC_PACKED_NONE) {
+		spin_lock_irq(&md->lock);
+		if (mmc_card_removed(card))
+			req->cmd_flags |= REQ_QUIET;
+		while (ret)
+			ret = __blk_end_request(req, -EIO,
+					blk_rq_cur_bytes(req));
+		spin_unlock_irq(&md->lock);
+	} else {
+		while (!list_empty(&mq_rq->packed_list)) {
+			prq = list_entry_rq(mq_rq->packed_list.next);
+			list_del_init(&prq->queuelist);
+			spin_lock_irq(&md->lock);
+			__blk_end_request(prq, -EIO, blk_rq_bytes(prq));
+			spin_unlock_irq(&md->lock);
+		}
+		mmc_blk_clear_packed(mq_rq);
+	}
 
  start_new_req:
 	if (rqc) {
+		/*
+		 * If current request is packed, it needs to put back.
+		 */
+		if (mq->mqrq_cur->packed_cmd != MMC_PACKED_NONE) {
+			while (!list_empty(&mq->mqrq_cur->packed_list)) {
+				prq = list_entry_rq(
+					mq->mqrq_cur->packed_list.prev);
+				if (prq->queuelist.prev !=
+						&mq->mqrq_cur->packed_list) {
+					list_del_init(&prq->queuelist);
+					spin_lock_irq(mq->queue->queue_lock);
+					blk_requeue_request(mq->queue, prq);
+					spin_unlock_irq(mq->queue->queue_lock);
+				} else {
+					list_del_init(&prq->queuelist);
+				}
+			}
+			mmc_blk_clear_packed(mq->mqrq_cur);
+		}
 		mmc_blk_rw_rq_prep(mq->mqrq_cur, card, 0, mq);
 		mmc_start_req(card->host, &mq->mqrq_cur->mmc_active, NULL);
 	}
@@ -1405,9 +1912,6 @@
 	return 0;
 }
 
-static int
-mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card);
-
 static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
 {
 	int ret;
@@ -1436,7 +1940,14 @@
 		goto out;
 	}
 
-	if (req && req->cmd_flags & REQ_DISCARD) {
+	mmc_blk_write_packing_control(mq, req);
+
+	if (req && req->cmd_flags & REQ_SANITIZE) {
+		/* complete ongoing async transfer before issuing sanitize */
+		if (card->host && card->host->areq)
+			mmc_blk_issue_rw_rq(mq, NULL);
+		ret = mmc_blk_issue_sanitize_rq(mq, req);
+	} else if (req && req->cmd_flags & REQ_DISCARD) {
 		/* complete ongoing async transfer before issuing discard */
 		if (card->host->areq)
 			mmc_blk_issue_rw_rq(mq, NULL);
@@ -1662,6 +2173,8 @@
 
 	if (md) {
 		card = md->queue.card;
+		device_remove_file(disk_to_dev(md->disk),
+				   &md->num_wr_reqs_to_start_packing);
 		if (md->disk->flags & GENHD_FL_UP) {
 			device_remove_file(disk_to_dev(md->disk), &md->force_ro);
 			if ((md->area_type & MMC_BLK_DATA_AREA_BOOT) &&
@@ -1728,12 +2241,26 @@
 		if (ret)
 			goto power_ro_lock_fail;
 	}
+
+	md->num_wr_reqs_to_start_packing.show =
+		num_wr_reqs_to_start_packing_show;
+	md->num_wr_reqs_to_start_packing.store =
+		num_wr_reqs_to_start_packing_store;
+	sysfs_attr_init(&md->num_wr_reqs_to_start_packing.attr);
+	md->num_wr_reqs_to_start_packing.attr.name =
+		"num_wr_reqs_to_start_packing";
+	md->num_wr_reqs_to_start_packing.attr.mode = S_IRUGO | S_IWUSR;
+	ret = device_create_file(disk_to_dev(md->disk),
+				 &md->num_wr_reqs_to_start_packing);
+	if (ret)
+		goto power_ro_lock_fail;
+
 	return ret;
 
 power_ro_lock_fail:
-	device_remove_file(disk_to_dev(md->disk), &md->force_ro);
+		device_remove_file(disk_to_dev(md->disk), &md->force_ro);
 force_ro_fail:
-	del_gendisk(md->disk);
+		del_gendisk(md->disk);
 
 	return ret;
 }
@@ -1777,6 +2304,10 @@
 	MMC_FIXUP(CID_NAME_ANY, CID_MANFID_MICRON, 0x200, add_quirk_mmc,
 		  MMC_QUIRK_LONG_READ_TIME),
 
+	/* Some INAND MCP devices advertise incorrect timeout values */
+	MMC_FIXUP("SEM04G", 0x45, CID_OEMID_ANY, add_quirk_mmc,
+		  MMC_QUIRK_INAND_DATA_TIMEOUT),
+
 	END_FIXUP
 };
 
diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c
index 759714e..58efd5e 100644
--- a/drivers/mmc/card/mmc_test.c
+++ b/drivers/mmc/card/mmc_test.c
@@ -2890,7 +2890,8 @@
 	}
 
 #ifdef CONFIG_HIGHMEM
-	__free_pages(test->highmem, BUFFER_ORDER);
+	if (test->highmem)
+		__free_pages(test->highmem, BUFFER_ORDER);
 #endif
 	kfree(test->buffer);
 	kfree(test);
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index 996f8e3..ebcf7ed 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -25,6 +25,13 @@
 #define MMC_QUEUE_SUSPENDED	(1 << 0)
 
 /*
+ * Based on benchmark tests the default num of requests to trigger the write
+ * packing was determined, to keep the read latency as low as possible and
+ * manage to keep the high write throughput.
+ */
+#define DEFAULT_NUM_REQS_TO_START_PACK 17
+
+/*
  * Prepare a MMC request. This just filters out odd stuff.
  */
 static int mmc_prep_request(struct request_queue *q, struct request *req)
@@ -51,13 +58,14 @@
 {
 	struct mmc_queue *mq = d;
 	struct request_queue *q = mq->queue;
+	struct request *req;
 
 	current->flags |= PF_MEMALLOC;
 
 	down(&mq->thread_sem);
 	do {
-		struct request *req = NULL;
 		struct mmc_queue_req *tmp;
+		req = NULL;	/* Must be set to NULL at each iteration */
 
 		spin_lock_irq(q->queue_lock);
 		set_current_state(TASK_INTERRUPTIBLE);
@@ -66,6 +74,9 @@
 		spin_unlock_irq(q->queue_lock);
 
 		if (req || mq->mqrq_prev->req) {
+			if (mmc_card_doing_bkops(mq->card))
+				mmc_interrupt_bkops(mq->card);
+
 			set_current_state(TASK_RUNNING);
 			mq->issue_fn(mq, req);
 		} else {
@@ -73,6 +84,8 @@
 				set_current_state(TASK_RUNNING);
 				break;
 			}
+
+			mmc_start_bkops(mq->card);
 			up(&mq->thread_sem);
 			schedule();
 			down(&mq->thread_sem);
@@ -145,10 +158,15 @@
 	/* granularity must not be greater than max. discard */
 	if (card->pref_erase > max_discard)
 		q->limits.discard_granularity = 0;
-	if (mmc_can_secure_erase_trim(card) || mmc_can_sanitize(card))
+	if (mmc_can_secure_erase_trim(card))
 		queue_flag_set_unlocked(QUEUE_FLAG_SECDISCARD, q);
 }
 
+static void mmc_queue_setup_sanitize(struct request_queue *q)
+{
+	queue_flag_set_unlocked(QUEUE_FLAG_SANITIZE, q);
+}
+
 /**
  * mmc_init_queue - initialise a queue structure.
  * @mq: mmc queue
@@ -177,15 +195,21 @@
 
 	memset(&mq->mqrq_cur, 0, sizeof(mq->mqrq_cur));
 	memset(&mq->mqrq_prev, 0, sizeof(mq->mqrq_prev));
+	INIT_LIST_HEAD(&mqrq_cur->packed_list);
+	INIT_LIST_HEAD(&mqrq_prev->packed_list);
 	mq->mqrq_cur = mqrq_cur;
 	mq->mqrq_prev = mqrq_prev;
 	mq->queue->queuedata = mq;
+	mq->num_wr_reqs_to_start_packing = DEFAULT_NUM_REQS_TO_START_PACK;
 
 	blk_queue_prep_rq(mq->queue, mmc_prep_request);
 	queue_flag_set_unlocked(QUEUE_FLAG_NONROT, mq->queue);
 	if (mmc_can_erase(card))
 		mmc_queue_setup_discard(mq->queue, card);
 
+	if ((mmc_can_sanitize(card) && (host->caps2 & MMC_CAP2_SANITIZE)))
+		mmc_queue_setup_sanitize(mq->queue);
+
 #ifdef CONFIG_MMC_BLOCK_BOUNCE
 	if (host->max_segs == 1) {
 		unsigned int bouncesz;
@@ -377,6 +401,35 @@
 	}
 }
 
+static unsigned int mmc_queue_packed_map_sg(struct mmc_queue *mq,
+					    struct mmc_queue_req *mqrq,
+					    struct scatterlist *sg)
+{
+	struct scatterlist *__sg;
+	unsigned int sg_len = 0;
+	struct request *req;
+	enum mmc_packed_cmd cmd;
+
+	cmd = mqrq->packed_cmd;
+
+	if (cmd == MMC_PACKED_WRITE) {
+		__sg = sg;
+		sg_set_buf(__sg, mqrq->packed_cmd_hdr,
+				sizeof(mqrq->packed_cmd_hdr));
+		sg_len++;
+		__sg->page_link &= ~0x02;
+	}
+
+	__sg = sg + sg_len;
+	list_for_each_entry(req, &mqrq->packed_list, queuelist) {
+		sg_len += blk_rq_map_sg(mq->queue, req, __sg);
+		__sg = sg + (sg_len - 1);
+		(__sg++)->page_link &= ~0x02;
+	}
+	sg_mark_end(sg + (sg_len - 1));
+	return sg_len;
+}
+
 /*
  * Prepare the sg list(s) to be handed of to the host driver
  */
@@ -387,12 +440,19 @@
 	struct scatterlist *sg;
 	int i;
 
-	if (!mqrq->bounce_buf)
-		return blk_rq_map_sg(mq->queue, mqrq->req, mqrq->sg);
+	if (!mqrq->bounce_buf) {
+		if (!list_empty(&mqrq->packed_list))
+			return mmc_queue_packed_map_sg(mq, mqrq, mqrq->sg);
+		else
+			return blk_rq_map_sg(mq->queue, mqrq->req, mqrq->sg);
+	}
 
 	BUG_ON(!mqrq->bounce_sg);
 
-	sg_len = blk_rq_map_sg(mq->queue, mqrq->req, mqrq->bounce_sg);
+	if (!list_empty(&mqrq->packed_list))
+		sg_len = mmc_queue_packed_map_sg(mq, mqrq, mqrq->bounce_sg);
+	else
+		sg_len = blk_rq_map_sg(mq->queue, mqrq->req, mqrq->bounce_sg);
 
 	mqrq->bounce_sg_len = sg_len;
 
diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
index d2a1eb4..6c29e0e 100644
--- a/drivers/mmc/card/queue.h
+++ b/drivers/mmc/card/queue.h
@@ -12,6 +12,11 @@
 	struct mmc_data		data;
 };
 
+enum mmc_packed_cmd {
+	MMC_PACKED_NONE = 0,
+	MMC_PACKED_WRITE,
+};
+
 struct mmc_queue_req {
 	struct request		*req;
 	struct mmc_blk_request	brq;
@@ -20,6 +25,12 @@
 	struct scatterlist	*bounce_sg;
 	unsigned int		bounce_sg_len;
 	struct mmc_async_req	mmc_active;
+	struct list_head	packed_list;
+	u32			packed_cmd_hdr[128];
+	unsigned int		packed_blocks;
+	enum mmc_packed_cmd	packed_cmd;
+	int		packed_fail_idx;
+	u8		packed_num;
 };
 
 struct mmc_queue {
@@ -33,6 +44,9 @@
 	struct mmc_queue_req	mqrq[2];
 	struct mmc_queue_req	*mqrq_cur;
 	struct mmc_queue_req	*mqrq_prev;
+	bool			wr_packing_enabled;
+	int			num_of_potential_packed_wr_reqs;
+	int			num_wr_reqs_to_start_packing;
 };
 
 extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *,
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index c60cee9..0592f9d 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -122,6 +122,7 @@
 	return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
 static int mmc_bus_suspend(struct device *dev)
 {
 	struct mmc_driver *drv = to_mmc_driver(dev->driver);
@@ -143,6 +144,10 @@
 		ret = drv->resume(card);
 	return ret;
 }
+#else
+#define mmc_bus_suspend NULL
+#define mmc_bus_resume NULL
+#endif
 
 #ifdef CONFIG_PM_RUNTIME
 
@@ -249,6 +254,8 @@
 	card->dev.release = mmc_release_card;
 	card->dev.type = type;
 
+	spin_lock_init(&card->wr_pack_stats.lock);
+
 	return card;
 }
 
@@ -351,6 +358,8 @@
 		device_del(&card->dev);
 	}
 
+	kfree(card->wr_pack_stats.packing_events);
+
 	put_device(&card->dev);
 }
 
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 95902a7..973ece6 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -27,6 +27,7 @@
 #include <linux/fault-inject.h>
 #include <linux/random.h>
 #include <linux/wakelock.h>
+#include <linux/pm.h>
 
 #include <linux/mmc/card.h>
 #include <linux/mmc/host.h>
@@ -42,6 +43,12 @@
 #include "sd_ops.h"
 #include "sdio_ops.h"
 
+/*
+ * The Background operations can take a long time, depends on the house keeping
+ * operations the card has to perform
+ */
+#define MMC_BKOPS_MAX_TIMEOUT    (4 * 60 * 1000) /* max time to wait in ms */
+
 static struct workqueue_struct *workqueue;
 
 /*
@@ -135,6 +142,9 @@
 {
 	struct mmc_command *cmd = mrq->cmd;
 	int err = cmd->error;
+#ifdef CONFIG_MMC_PERF_PROFILING
+	ktime_t diff;
+#endif
 
 	if (err && cmd->retries && mmc_host_is_spi(host)) {
 		if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
@@ -159,6 +169,24 @@
 			cmd->resp[2], cmd->resp[3]);
 
 		if (mrq->data) {
+#ifdef CONFIG_MMC_PERF_PROFILING
+			if (host->perf_enable) {
+				diff = ktime_sub(ktime_get(), host->perf.start);
+				if (mrq->data->flags == MMC_DATA_READ) {
+					host->perf.rbytes_drv +=
+							mrq->data->bytes_xfered;
+					host->perf.rtime_drv =
+						ktime_add(host->perf.rtime_drv,
+							diff);
+				} else {
+					host->perf.wbytes_drv +=
+						mrq->data->bytes_xfered;
+					host->perf.wtime_drv =
+						ktime_add(host->perf.wtime_drv,
+							diff);
+				}
+			}
+#endif
 			pr_debug("%s:     %d bytes transferred: %d\n",
 				mmc_hostname(host),
 				mrq->data->bytes_xfered, mrq->data->error);
@@ -239,12 +267,84 @@
 			mrq->stop->error = 0;
 			mrq->stop->mrq = mrq;
 		}
+#ifdef CONFIG_MMC_PERF_PROFILING
+		if (host->perf_enable)
+			host->perf.start = ktime_get();
+#endif
 	}
 	mmc_host_clk_hold(host);
 	led_trigger_event(host->led, LED_FULL);
 	host->ops->request(host, mrq);
 }
 
+/**
+ *	mmc_start_bkops - start BKOPS for supported cards
+ *	@card: MMC card to start BKOPS
+ *
+ *	Start background operations whenever requested.
+ *	when the urgent BKOPS bit is set in a R1 command response
+ *	then background operations should be started immediately.
+*/
+void mmc_start_bkops(struct mmc_card *card)
+{
+	int err;
+	unsigned long flags;
+	int timeout;
+
+	BUG_ON(!card);
+	if (!card->ext_csd.bkops_en || !(card->host->caps2 & MMC_CAP2_BKOPS))
+		return;
+
+	if (mmc_card_check_bkops(card)) {
+		spin_lock_irqsave(&card->host->lock, flags);
+		mmc_card_clr_check_bkops(card);
+		spin_unlock_irqrestore(&card->host->lock, flags);
+		if (mmc_is_exception_event(card, EXT_CSD_URGENT_BKOPS))
+			if (card->ext_csd.raw_bkops_status)
+				mmc_card_set_need_bkops(card);
+	}
+
+	/*
+	 * If card is already doing bkops or need for
+	 * bkops flag is not set, then do nothing just
+	 * return
+	 */
+	if (mmc_card_doing_bkops(card) || !mmc_card_need_bkops(card))
+		return;
+
+	mmc_claim_host(card->host);
+
+	timeout = (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2) ?
+		MMC_BKOPS_MAX_TIMEOUT : 0;
+
+	err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+			EXT_CSD_BKOPS_START, 1, timeout);
+	if (err) {
+		pr_warning("%s: error %d starting bkops\n",
+			   mmc_hostname(card->host), err);
+		mmc_card_clr_need_bkops(card);
+		goto out;
+	}
+
+	spin_lock_irqsave(&card->host->lock, flags);
+	mmc_card_clr_need_bkops(card);
+
+	/*
+	 * For urgent bkops status (LEVEL_2 and more)
+	 * bkops executed synchronously, otherwise
+	 * the operation is in progress
+	 */
+	if (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2)
+		mmc_card_set_check_bkops(card);
+	else
+		mmc_card_set_doing_bkops(card);
+
+	spin_unlock_irqrestore(&card->host->lock, flags);
+out:
+	mmc_release_host(card->host);
+}
+EXPORT_SYMBOL(mmc_start_bkops);
+
 static void mmc_wait_done(struct mmc_request *mrq)
 {
 	complete(&mrq->completion);
@@ -362,7 +462,7 @@
 	if (host->areq)
 		mmc_post_req(host, host->areq->mrq, 0);
 
-	 /* Cancel a prepared request if it was not started. */
+	/* Cancel a prepared request if it was not started. */
 	if ((err || start_err) && areq)
 			mmc_post_req(host, areq->mrq, -EINVAL);
 
@@ -480,6 +580,69 @@
 EXPORT_SYMBOL(mmc_wait_for_cmd);
 
 /**
+ *	mmc_interrupt_bkops - interrupt ongoing BKOPS
+ *	@card: MMC card to check BKOPS
+ *
+ *	Send HPI command to interrupt ongoing background operations,
+ *	to allow rapid servicing of foreground operations,e.g. read/
+ *	writes. Wait until the card comes out of the programming state
+ *	to avoid errors in servicing read/write requests.
+ */
+int mmc_interrupt_bkops(struct mmc_card *card)
+{
+	int err = 0;
+	unsigned long flags;
+
+	BUG_ON(!card);
+
+	err = mmc_interrupt_hpi(card);
+
+	spin_lock_irqsave(&card->host->lock, flags);
+	mmc_card_clr_doing_bkops(card);
+	spin_unlock_irqrestore(&card->host->lock, flags);
+
+	return err;
+}
+EXPORT_SYMBOL(mmc_interrupt_bkops);
+
+int mmc_read_bkops_status(struct mmc_card *card)
+{
+	int err;
+	u8 ext_csd[512];
+
+	mmc_claim_host(card->host);
+	err = mmc_send_ext_csd(card, ext_csd);
+	mmc_release_host(card->host);
+	if (err)
+		return err;
+
+	card->ext_csd.raw_bkops_status = ext_csd[EXT_CSD_BKOPS_STATUS];
+	card->ext_csd.raw_exception_status = ext_csd[EXT_CSD_EXP_EVENTS_STATUS];
+
+	return 0;
+}
+EXPORT_SYMBOL(mmc_read_bkops_status);
+
+int mmc_is_exception_event(struct mmc_card *card, unsigned int value)
+{
+	int err;
+
+	err = mmc_read_bkops_status(card);
+	if (err) {
+		pr_err("%s: Didn't read bkops status : %d\n",
+		       mmc_hostname(card->host), err);
+		return 0;
+	}
+
+	/* In eMMC 4.41, R1_EXCEPTION_EVENT is URGENT_BKOPS */
+	if (card->ext_csd.rev == 5)
+		return 1;
+
+	return (card->ext_csd.raw_exception_status & value) ? 1 : 0;
+}
+EXPORT_SYMBOL(mmc_is_exception_event);
+
+/**
  *	mmc_set_data_timeout - set the timeout for a data command
  *	@data: data phase for command
  *	@card: the MMC card associated with the data transfer
@@ -574,6 +737,11 @@
 				data->timeout_ns =  100000000;	/* 100ms */
 		}
 	}
+	/* Increase the timeout values for some bad INAND MCP devices */
+	if (card->quirks & MMC_QUIRK_INAND_DATA_TIMEOUT) {
+		data->timeout_ns = 4000000000u; /* 4s */
+		data->timeout_clks = 0;
+	}
 }
 EXPORT_SYMBOL(mmc_set_data_timeout);
 
@@ -623,6 +791,7 @@
 	might_sleep();
 
 	add_wait_queue(&host->wq, &wait);
+
 	spin_lock_irqsave(&host->lock, flags);
 	while (1) {
 		set_current_state(TASK_UNINTERRUPTIBLE);
@@ -707,7 +876,7 @@
  * Internal function that does the actual ios call to the host driver,
  * optionally printing some debug output.
  */
-static inline void mmc_set_ios(struct mmc_host *host)
+void mmc_set_ios(struct mmc_host *host)
 {
 	struct mmc_ios *ios = &host->ios;
 
@@ -721,6 +890,7 @@
 		mmc_set_ungated(host);
 	host->ops->set_ios(host, ios);
 }
+EXPORT_SYMBOL(mmc_set_ios);
 
 /*
  * Control chip select pin on a host.
@@ -763,6 +933,8 @@
 {
 	unsigned long flags;
 
+	WARN_ON(!host->ios.clock);
+
 	spin_lock_irqsave(&host->clk_lock, flags);
 	host->clk_old = host->ios.clock;
 	host->ios.clock = 0;
@@ -785,7 +957,7 @@
 	 * we just ignore the call.
 	 */
 	if (host->clk_old) {
-		BUG_ON(host->ios.clock);
+		WARN_ON(host->ios.clock);
 		/* This call will also set host->clk_gated to false */
 		__mmc_set_clock(host, host->clk_old);
 	}
@@ -1140,7 +1312,7 @@
 		/* Set the card state to no notification after the poweroff */
 		card->poweroff_notify_state = MMC_NO_POWER_NOTIFICATION;
 	}
-	mmc_release_host(host);
+	 mmc_release_host(host);
 }
 
 /*
@@ -1154,7 +1326,7 @@
  * If a host does all the power sequencing itself, ignore the
  * initial MMC_POWER_UP stage.
  */
-static void mmc_power_up(struct mmc_host *host)
+void mmc_power_up(struct mmc_host *host)
 {
 	int bit;
 
@@ -1167,11 +1339,12 @@
 		bit = fls(host->ocr_avail) - 1;
 
 	host->ios.vdd = bit;
-	if (mmc_host_is_spi(host))
+	if (mmc_host_is_spi(host)) 
 		host->ios.chip_select = MMC_CS_HIGH;
-	else
+	else {
 		host->ios.chip_select = MMC_CS_DONTCARE;
-	host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
+		host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
+	}
 	host->ios.power_mode = MMC_POWER_UP;
 	host->ios.bus_width = MMC_BUS_WIDTH_1;
 	host->ios.timing = MMC_TIMING_LEGACY;
@@ -1204,7 +1377,8 @@
 
 	host->ios.clock = 0;
 	host->ios.vdd = 0;
-
+	
+	mmc_poweroff_notify(host);
 	/*
 	 * For eMMC 4.5 device send AWAKE command before
 	 * POWER_OFF_NOTIFY command, because in sleep state
@@ -1972,8 +2146,16 @@
 	/* Order's important: probe SDIO, then SD, then MMC */
 	if (!mmc_attach_sdio(host))
 		return 0;
+
+	if (!host->ios.vdd)
+		mmc_power_up(host);
+
 	if (!mmc_attach_sd(host))
 		return 0;
+
+	if (!host->ios.vdd)
+		mmc_power_up(host);
+
 	if (!mmc_attach_mmc(host))
 		return 0;
 
@@ -2038,10 +2220,8 @@
 
 void mmc_rescan(struct work_struct *work)
 {
-	static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
 	struct mmc_host *host =
 		container_of(work, struct mmc_host, detect.work);
-	int i;
 	bool extend_wakelock = false;
 
 	if (host->rescan_disable)
@@ -2058,6 +2238,12 @@
 		host->bus_ops->detect(host);
 
 	host->detect_change = 0;
+	/* If the card was removed the bus will be marked
+	 * as dead - extend the wakelock so userspace
+	 * can respond */
+	if (host->bus_dead)
+		extend_wakelock = 1;
+
 
 	/* If the card was removed the bus will be marked
 	 * as dead - extend the wakelock so userspace
@@ -2088,14 +2274,8 @@
 		goto out;
 
 	mmc_claim_host(host);
-	for (i = 0; i < ARRAY_SIZE(freqs); i++) {
-		if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) {
-			extend_wakelock = true;
-			break;
-		}
-		if (freqs[i] <= host->f_min)
-			break;
-	}
+	if (!mmc_rescan_try_freq(host, host->f_min))
+		extend_wakelock = true;
 	mmc_release_host(host);
 
  out:
@@ -2327,32 +2507,58 @@
 	if (cancel_delayed_work(&host->detect))
 		wake_unlock(&host->detect_wake_lock);
 	mmc_flush_scheduled_work();
-
 	err = mmc_cache_ctrl(host, 0);
 	if (err)
 		goto out;
 
 	mmc_bus_get(host);
 	if (host->bus_ops && !host->bus_dead) {
+		/*
+		 * A long response time is not acceptable for device drivers
+		 * when doing suspend. Prevent mmc_claim_host in the suspend
+		 * sequence, to potentially wait "forever" by trying to
+		 * pre-claim the host.
+		 *
+		 * Skip try claim host for SDIO cards, doing so fixes deadlock
+		 * conditions. The function driver suspend may again call into
+		 * SDIO driver within a different context for enabling power
+		 * save mode in the card and hence wait in mmc_claim_host
+		 * causing deadlock.
+		 */
+		if (!(host->card && mmc_card_sdio(host->card)))
+			if (!mmc_try_claim_host(host))
+				err = -EBUSY;
 
-		if (host->bus_ops->suspend)
-			err = host->bus_ops->suspend(host);
+		if (!err) {
+			if (host->bus_ops->suspend) {
+				/*
+				 * For eMMC 4.5 device send notify command
+				 * before sleep, because in sleep state eMMC 4.5
+				 * devices respond to only RESET and AWAKE cmd
+				 */
+				mmc_poweroff_notify(host);
+				err = host->bus_ops->suspend(host);
+			}
+			if (!(host->card && mmc_card_sdio(host->card)))
+				mmc_do_release_host(host);
 
-		if (err == -ENOSYS || !host->bus_ops->resume) {
-			/*
-			 * We simply "remove" the card in this case.
-			 * It will be redetected on resume.  (Calling
-			 * bus_ops->remove() with a claimed host can
-			 * deadlock.)
-			 */
-			if (host->bus_ops->remove)
-				host->bus_ops->remove(host);
-			mmc_claim_host(host);
-			mmc_detach_bus(host);
-			mmc_power_off(host);
-			mmc_release_host(host);
-			host->pm_flags = 0;
-			err = 0;
+			if (err == -ENOSYS || !host->bus_ops->resume) {
+				/*
+				 * We simply "remove" the card in this case.
+				 * It will be redetected on resume.  (Calling
+				 * bus_ops->remove() with a claimed host can
+				 * deadlock.)
+				 * It will be redetected on resume.
+				 */
+				if (host->bus_ops->remove)
+					host->bus_ops->remove(host);
+				mmc_claim_host(host);
+				mmc_detach_bus(host);
+				mmc_power_off(host);
+				mmc_release_host(host);
+				host->pm_flags = 0;
+				err = 0;
+			}
 		}
 	}
 	mmc_bus_put(host);
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index 3bdafbc..85d2737 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -32,6 +32,8 @@
 
 void mmc_init_erase(struct mmc_card *card);
 
+void mmc_power_up(struct mmc_host *host);
+void mmc_power_off(struct mmc_host *host);
 void mmc_set_chip_select(struct mmc_host *host, int mode);
 void mmc_set_clock(struct mmc_host *host, unsigned int hz);
 void mmc_gate_clock(struct mmc_host *host);
@@ -51,6 +53,8 @@
 	if (ms < 1000 / HZ) {
 		cond_resched();
 		mdelay(ms);
+	} else if (ms < jiffies_to_msecs(2)) {
+		usleep_range(ms * 1000, (ms + 1) * 1000);
 	} else {
 		msleep(ms);
 	}
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
index 9ab5b17..898e358 100644
--- a/drivers/mmc/core/debugfs.c
+++ b/drivers/mmc/core/debugfs.c
@@ -318,6 +318,164 @@
 	.llseek		= default_llseek,
 };
 
+static int mmc_wr_pack_stats_open(struct inode *inode, struct file *filp)
+{
+	struct mmc_card *card = inode->i_private;
+
+	filp->private_data = card;
+	card->wr_pack_stats.print_in_read = 1;
+	return 0;
+}
+
+#define TEMP_BUF_SIZE 256
+static ssize_t mmc_wr_pack_stats_read(struct file *filp, char __user *ubuf,
+				size_t cnt, loff_t *ppos)
+{
+	struct mmc_card *card = filp->private_data;
+	struct mmc_wr_pack_stats *pack_stats;
+	int i;
+	int max_num_of_packed_reqs = 0;
+	char *temp_buf;
+
+	if (!card)
+		return cnt;
+
+	if (!card->wr_pack_stats.print_in_read)
+		return 0;
+
+	if (!card->wr_pack_stats.enabled) {
+		pr_info("%s: write packing statistics are disabled\n",
+			 mmc_hostname(card->host));
+		goto exit;
+	}
+
+	pack_stats = &card->wr_pack_stats;
+
+	if (!pack_stats->packing_events) {
+		pr_info("%s: NULL packing_events\n", mmc_hostname(card->host));
+		goto exit;
+	}
+
+	max_num_of_packed_reqs = card->ext_csd.max_packed_writes;
+
+	temp_buf = kmalloc(TEMP_BUF_SIZE, GFP_KERNEL);
+	if (!temp_buf)
+		goto exit;
+
+	spin_lock(&pack_stats->lock);
+
+	snprintf(temp_buf, TEMP_BUF_SIZE, "%s: write packing statistics:\n",
+		mmc_hostname(card->host));
+	strlcat(ubuf, temp_buf, cnt);
+
+	for (i = 1 ; i <= max_num_of_packed_reqs ; ++i) {
+		if (pack_stats->packing_events[i]) {
+			snprintf(temp_buf, TEMP_BUF_SIZE,
+				 "%s: Packed %d reqs - %d times\n",
+				mmc_hostname(card->host), i,
+				pack_stats->packing_events[i]);
+			strlcat(ubuf, temp_buf, cnt);
+		}
+	}
+
+	snprintf(temp_buf, TEMP_BUF_SIZE,
+		 "%s: stopped packing due to the following reasons:\n",
+		 mmc_hostname(card->host));
+	strlcat(ubuf, temp_buf, cnt);
+
+	if (pack_stats->pack_stop_reason[EXCEEDS_SEGMENTS]) {
+		snprintf(temp_buf, TEMP_BUF_SIZE,
+			 "%s: %d times: exceed max num of segments\n",
+			 mmc_hostname(card->host),
+			 pack_stats->pack_stop_reason[EXCEEDS_SEGMENTS]);
+		strlcat(ubuf, temp_buf, cnt);
+	}
+	if (pack_stats->pack_stop_reason[EXCEEDS_SECTORS]) {
+		snprintf(temp_buf, TEMP_BUF_SIZE,
+			 "%s: %d times: exceed max num of sectors\n",
+			mmc_hostname(card->host),
+			pack_stats->pack_stop_reason[EXCEEDS_SECTORS]);
+		strlcat(ubuf, temp_buf, cnt);
+	}
+	if (pack_stats->pack_stop_reason[WRONG_DATA_DIR]) {
+		snprintf(temp_buf, TEMP_BUF_SIZE,
+			 "%s: %d times: wrong data direction\n",
+			mmc_hostname(card->host),
+			pack_stats->pack_stop_reason[WRONG_DATA_DIR]);
+		strlcat(ubuf, temp_buf, cnt);
+	}
+	if (pack_stats->pack_stop_reason[FLUSH_OR_DISCARD]) {
+		snprintf(temp_buf, TEMP_BUF_SIZE,
+			 "%s: %d times: flush or discard\n",
+			mmc_hostname(card->host),
+			pack_stats->pack_stop_reason[FLUSH_OR_DISCARD]);
+		strlcat(ubuf, temp_buf, cnt);
+	}
+	if (pack_stats->pack_stop_reason[EMPTY_QUEUE]) {
+		snprintf(temp_buf, TEMP_BUF_SIZE,
+			 "%s: %d times: empty queue\n",
+			mmc_hostname(card->host),
+			pack_stats->pack_stop_reason[EMPTY_QUEUE]);
+		strlcat(ubuf, temp_buf, cnt);
+	}
+	if (pack_stats->pack_stop_reason[REL_WRITE]) {
+		snprintf(temp_buf, TEMP_BUF_SIZE,
+			 "%s: %d times: rel write\n",
+			mmc_hostname(card->host),
+			pack_stats->pack_stop_reason[REL_WRITE]);
+		strlcat(ubuf, temp_buf, cnt);
+	}
+	if (pack_stats->pack_stop_reason[THRESHOLD]) {
+		snprintf(temp_buf, TEMP_BUF_SIZE,
+			 "%s: %d times: Threshold\n",
+			mmc_hostname(card->host),
+			pack_stats->pack_stop_reason[THRESHOLD]);
+		strlcat(ubuf, temp_buf, cnt);
+	}
+
+	spin_unlock(&pack_stats->lock);
+
+	kfree(temp_buf);
+
+	pr_info("%s", ubuf);
+
+exit:
+	if (card->wr_pack_stats.print_in_read == 1) {
+		card->wr_pack_stats.print_in_read = 0;
+		return strnlen(ubuf, cnt);
+	}
+
+	return 0;
+}
+
+static ssize_t mmc_wr_pack_stats_write(struct file *filp,
+				       const char __user *ubuf, size_t cnt,
+				       loff_t *ppos)
+{
+	struct mmc_card *card = filp->private_data;
+	int value;
+
+	if (!card)
+		return cnt;
+
+	sscanf(ubuf, "%d", &value);
+	if (value) {
+		mmc_blk_init_packed_statistics(card);
+	} else {
+		spin_lock(&card->wr_pack_stats.lock);
+		card->wr_pack_stats.enabled = false;
+		spin_unlock(&card->wr_pack_stats.lock);
+	}
+
+	return cnt;
+}
+
+static const struct file_operations mmc_dbg_wr_pack_stats_fops = {
+	.open		= mmc_wr_pack_stats_open,
+	.read		= mmc_wr_pack_stats_read,
+	.write		= mmc_wr_pack_stats_write,
+};
+
 void mmc_add_card_debugfs(struct mmc_card *card)
 {
 	struct mmc_host	*host = card->host;
@@ -350,6 +508,12 @@
 					&mmc_dbg_ext_csd_fops))
 			goto err;
 
+	if (mmc_card_mmc(card) && (card->ext_csd.rev >= 6) &&
+	    (card->host->caps2 & MMC_CAP2_PACKED_WR))
+		if (!debugfs_create_file("wr_pack_stats", S_IRUSR, root, card,
+					 &mmc_dbg_wr_pack_stats_fops))
+			goto err;
+
 	return;
 
 err:
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index dd7b120..850872d 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -58,7 +58,8 @@
 		struct device_attribute *attr, char *buf)
 {
 	struct mmc_host *host = cls_dev_to_mmc_host(dev);
-	return snprintf(buf, PAGE_SIZE, "%lu\n", host->clkgate_delay);
+	return snprintf(buf, PAGE_SIZE, "%lu\n",
+			host->clkgate_delay);
 }
 
 static ssize_t clkgate_delay_store(struct device *dev,
@@ -73,6 +74,9 @@
 	spin_lock_irqsave(&host->clk_lock, flags);
 	host->clkgate_delay = value;
 	spin_unlock_irqrestore(&host->clk_lock, flags);
+
+	pr_info("%s: clock gate delay set to %lu ms\n",
+			mmc_hostname(host), value);
 	return count;
 }
 
@@ -355,6 +359,66 @@
 }
 
 EXPORT_SYMBOL(mmc_alloc_host);
+#ifdef CONFIG_MMC_PERF_PROFILING
+static ssize_t
+show_perf(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct mmc_host *host = dev_get_drvdata(dev);
+	int64_t rtime_drv, wtime_drv;
+	unsigned long rbytes_drv, wbytes_drv;
+
+	spin_lock(&host->lock);
+
+	rbytes_drv = host->perf.rbytes_drv;
+	wbytes_drv = host->perf.wbytes_drv;
+
+	rtime_drv = ktime_to_us(host->perf.rtime_drv);
+	wtime_drv = ktime_to_us(host->perf.wtime_drv);
+
+	spin_unlock(&host->lock);
+
+	return snprintf(buf, PAGE_SIZE, "Write performance at driver Level:"
+					"%lu bytes in %lld microseconds\n"
+					"Read performance at driver Level:"
+					"%lu bytes in %lld microseconds\n",
+					wbytes_drv, wtime_drv,
+					rbytes_drv, rtime_drv);
+}
+
+static ssize_t
+set_perf(struct device *dev, struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	int64_t value;
+	struct mmc_host *host = dev_get_drvdata(dev);
+
+	sscanf(buf, "%lld", &value);
+	spin_lock(&host->lock);
+	if (!value) {
+		memset(&host->perf, 0, sizeof(host->perf));
+		host->perf_enable = false;
+	} else {
+		host->perf_enable = true;
+	}
+	spin_unlock(&host->lock);
+
+	return count;
+}
+
+static DEVICE_ATTR(perf, S_IRUGO | S_IWUSR,
+		show_perf, set_perf);
+
+#endif
+
+static struct attribute *dev_attrs[] = {
+#ifdef CONFIG_MMC_PERF_PROFILING
+	&dev_attr_perf.attr,
+#endif
+	NULL,
+};
+static struct attribute_group dev_attr_grp = {
+	.attrs = dev_attrs,
+};
 
 /**
  *	mmc_add_host - initialise host hardware
@@ -382,6 +446,11 @@
 #endif
 	mmc_host_clk_sysfs_init(host);
 
+	err = sysfs_create_group(&host->parent->kobj, &dev_attr_grp);
+	if (err)
+		pr_err("%s: failed to create sysfs group with err %d\n",
+							 __func__, err);
+
 	mmc_start_host(host);
 	if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY))
 		register_pm_notifier(&host->pm_notify);
@@ -409,6 +478,8 @@
 #ifdef CONFIG_DEBUG_FS
 	mmc_remove_host_debugfs(host);
 #endif
+	sysfs_remove_group(&host->parent->kobj, &dev_attr_grp);
+
 
 	device_del(&host->class_dev);
 
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 54df5ad..1baa0c2 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -263,7 +263,7 @@
 
 	card->ext_csd.rev = ext_csd[EXT_CSD_REV];
 	if (card->ext_csd.rev > 6) {
-		pr_err("%s: unrecognised EXT_CSD revision %d\n",
+		printk(KERN_ERR "%s: unrecognised EXT_CSD revision %d\n",
 			mmc_hostname(card->host), card->ext_csd.rev);
 		err = -EINVAL;
 		goto out;
@@ -480,6 +480,24 @@
 	}
 
 	if (card->ext_csd.rev >= 5) {
+		/* check whether the eMMC card support BKOPS */
+		if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) {
+			card->ext_csd.bkops = 1;
+			card->ext_csd.bkops_en = ext_csd[EXT_CSD_BKOPS_EN];
+			card->ext_csd.raw_bkops_status =
+				ext_csd[EXT_CSD_BKOPS_STATUS];
+			if (!card->ext_csd.bkops_en &&
+				card->host->caps2 & MMC_CAP2_INIT_BKOPS) {
+				err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+					EXT_CSD_BKOPS_EN, 1, 0);
+				if (err)
+					pr_warning("%s: Enabling BKOPS failed\n",
+						mmc_hostname(card->host));
+				else
+					card->ext_csd.bkops_en = 1;
+			}
+		}
+
 		/* check whether the eMMC card supports HPI */
 		if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1) {
 			card->ext_csd.hpi = 1;
@@ -533,6 +551,10 @@
 		} else {
 			card->ext_csd.data_tag_unit_size = 0;
 		}
+		card->ext_csd.max_packed_writes =
+			ext_csd[EXT_CSD_MAX_PACKED_WRITES];
+		card->ext_csd.max_packed_reads =
+			ext_csd[EXT_CSD_MAX_PACKED_READS];
 	}
 
 out:
@@ -951,6 +973,9 @@
 
 		/* Erase size depends on CSD and Extended CSD */
 		mmc_set_erase_size(card);
+
+		if (card->ext_csd.sectors && (rocr & MMC_CARD_SECTOR_ADDR))
+			mmc_card_set_blockaddr(card);
 	}
 
 	/*
@@ -1267,6 +1292,43 @@
 		}
 	}
 
+	if ((host->caps2 & MMC_CAP2_PACKED_CMD) &&
+			(card->ext_csd.max_packed_writes > 0) &&
+			(card->ext_csd.max_packed_reads > 0)) {
+		err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+				EXT_CSD_EXP_EVENTS_CTRL,
+				EXT_CSD_PACKED_EVENT_EN,
+				card->ext_csd.generic_cmd6_time);
+		if (err && err != -EBADMSG)
+			goto free_card;
+		if (err) {
+			pr_warning("%s: Enabling packed event failed\n",
+					mmc_hostname(card->host));
+			card->ext_csd.packed_event_en = 0;
+			err = 0;
+		} else {
+			card->ext_csd.packed_event_en = 1;
+		}
+
+	}
+
+	if (!oldcard) {
+		if ((host->caps2 & MMC_CAP2_PACKED_CMD) &&
+		    (card->ext_csd.max_packed_writes > 0)) {
+			/*
+			 * We would like to keep the statistics in an index
+			 * that equals the num of packed requests
+			 * (1 to max_packed_writes)
+			 */
+			card->wr_pack_stats.packing_events = kzalloc(
+				(card->ext_csd.max_packed_writes + 1) *
+				sizeof(*card->wr_pack_stats.packing_events),
+				GFP_KERNEL);
+			if (!card->wr_pack_stats.packing_events)
+				goto free_card;
+		}
+	}
+
 	if (!oldcard)
 		host->card = card;
 
@@ -1291,7 +1353,10 @@
 	BUG_ON(!host->card);
 
 	mmc_remove_card(host->card);
+
+	mmc_claim_host(host);
 	host->card = NULL;
+	mmc_release_host(host);
 }
 
 /*
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 69370f4..590aa58 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -335,6 +335,7 @@
 	return mmc_send_cxd_data(card, card->host, MMC_SEND_EXT_CSD,
 			ext_csd, 512);
 }
+EXPORT_SYMBOL_GPL(mmc_send_ext_csd);
 
 int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp)
 {
@@ -392,13 +393,23 @@
 		  (index << 16) |
 		  (value << 8) |
 		  set;
-	cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
+		cmd.flags = MMC_CMD_AC;
+	if (index == EXT_CSD_BKOPS_START &&
+	    card->ext_csd.raw_bkops_status < EXT_CSD_BKOPS_LEVEL_2)
+		cmd.flags |= MMC_RSP_SPI_R1 | MMC_RSP_R1;
+	else
+		cmd.flags |= MMC_RSP_SPI_R1B | MMC_RSP_R1B;
 	cmd.cmd_timeout_ms = timeout_ms;
 
 	err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
 	if (err)
 		return err;
 
+	/* No need to check card status in case of BKOPS switch*/
+	if (index == EXT_CSD_BKOPS_START)
+		return 0;
+
+	mmc_delay(1);
 	/* Must check status to be sure of no errors */
 	do {
 		err = mmc_send_status(card, &status);
@@ -507,6 +518,9 @@
 
 	data.sg = &sg;
 	data.sg_len = 1;
+	data.timeout_ns = 1000000;
+	data.timeout_clks = 0;
+
 	sg_init_one(&sg, data_buf, len);
 	mmc_wait_for_req(host, &mrq);
 	err = 0;
diff --git a/drivers/mmc/core/quirks.c b/drivers/mmc/core/quirks.c
index 06ee1ae..59f0340 100644
--- a/drivers/mmc/core/quirks.c
+++ b/drivers/mmc/core/quirks.c
@@ -30,6 +30,26 @@
 #define SDIO_DEVICE_ID_STE_CW1200	0x2280
 #endif
 
+#ifndef SDIO_VENDOR_ID_MSM
+#define SDIO_VENDOR_ID_MSM		0x0070
+#endif
+
+#ifndef SDIO_DEVICE_ID_MSM_WCN1314
+#define SDIO_DEVICE_ID_MSM_WCN1314	0x2881
+#endif
+
+#ifndef SDIO_VENDOR_ID_MSM_QCA
+#define SDIO_VENDOR_ID_MSM_QCA		0x271
+#endif
+
+#ifndef SDIO_DEVICE_ID_MSM_QCA_AR6003_1
+#define SDIO_DEVICE_ID_MSM_QCA_AR6003_1	0x300
+#endif
+
+#ifndef SDIO_DEVICE_ID_MSM_QCA_AR6003_2
+#define SDIO_DEVICE_ID_MSM_QCA_AR6003_2	0x301
+#endif
+
 /*
  * This hook just adds a quirk for all sdio devices
  */
@@ -49,6 +69,15 @@
 	SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
 		   remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING),
 
+	SDIO_FIXUP(SDIO_VENDOR_ID_MSM, SDIO_DEVICE_ID_MSM_WCN1314,
+		   remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING),
+
+	SDIO_FIXUP(SDIO_VENDOR_ID_MSM_QCA, SDIO_DEVICE_ID_MSM_QCA_AR6003_1,
+		   remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING),
+
+	SDIO_FIXUP(SDIO_VENDOR_ID_MSM_QCA, SDIO_DEVICE_ID_MSM_QCA_AR6003_2,
+		   remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING),
+
 	SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
 		   add_quirk, MMC_QUIRK_NONSTD_FUNC_IF),
 
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 7c76a45..ff5821b 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -878,9 +878,9 @@
 		int ro = -1;
 
 		if (host->ops->get_ro) {
-			mmc_host_clk_hold(card->host);
+			mmc_host_clk_hold(host);
 			ro = host->ops->get_ro(host);
-			mmc_host_clk_release(card->host);
+			mmc_host_clk_release(host);
 		}
 
 		if (ro < 0) {
@@ -1001,9 +1001,9 @@
 		 * value registers for UHS-I cards.
 		 */
 		if (host->ops->enable_preset_value) {
-			mmc_host_clk_hold(card->host);
+			mmc_host_clk_hold(host);
 			host->ops->enable_preset_value(host, true);
-			mmc_host_clk_release(card->host);
+			mmc_host_clk_release(host);
 		}
 	} else {
 		/*
@@ -1052,7 +1052,10 @@
 	BUG_ON(!host->card);
 
 	mmc_remove_card(host->card);
+
+	mmc_claim_host(host);
 	host->card = NULL;
+	mmc_release_host(host);
 }
 
 /*
@@ -1152,8 +1155,11 @@
 		if (err) {
 			printk(KERN_ERR "%s: Re-init card rc = %d (retries = %d)\n",
 			       mmc_hostname(host), err, retries);
-			mdelay(5);
 			retries--;
+			mmc_power_off(host);
+			usleep_range(5000, 5500);
+			mmc_power_up(host);
+			mmc_select_voltage(host, host->ocr);
 			continue;
 		}
 		break;
@@ -1285,6 +1291,10 @@
 		err = mmc_sd_init_card(host, host->ocr, NULL);
 		if (err) {
 			retries--;
+			mmc_power_off(host);
+			usleep_range(5000, 5500);
+			mmc_power_up(host);
+			mmc_select_voltage(host, host->ocr);
 			continue;
 		}
 		break;
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index c44f276..81a4ba0 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -213,7 +213,7 @@
 	int ret;
 	u8 ctrl;
 
-	if (!(card->host->caps & MMC_CAP_4_BIT_DATA))
+	if (!(card->host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)))
 		return 0;
 
 	if (card->cccr.low_speed && !card->cccr.wide_bus)
@@ -223,7 +223,10 @@
 	if (ret)
 		return ret;
 
-	ctrl |= SDIO_BUS_WIDTH_4BIT;
+	if (card->host->caps & MMC_CAP_8_BIT_DATA)
+		ctrl |= SDIO_BUS_WIDTH_8BIT;
+	else if (card->host->caps & MMC_CAP_4_BIT_DATA)
+		ctrl |= SDIO_BUS_WIDTH_4BIT;
 
 	ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_IF, ctrl, NULL);
 	if (ret)
@@ -264,7 +267,7 @@
 	int ret;
 	u8 ctrl;
 
-	if (!(card->host->caps & MMC_CAP_4_BIT_DATA))
+	if (!(card->host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)))
 		return 0;
 
 	if (card->cccr.low_speed && !card->cccr.wide_bus)
@@ -274,10 +277,10 @@
 	if (ret)
 		return ret;
 
-	if (!(ctrl & SDIO_BUS_WIDTH_4BIT))
+	if (!(ctrl & (SDIO_BUS_WIDTH_4BIT | SDIO_BUS_WIDTH_8BIT)))
 		return 0;
 
-	ctrl &= ~SDIO_BUS_WIDTH_4BIT;
+	ctrl &= ~(SDIO_BUS_WIDTH_4BIT | SDIO_BUS_WIDTH_8BIT);
 	ctrl |= SDIO_BUS_ASYNC_INT;
 
 	ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_IF, ctrl, NULL);
@@ -637,8 +640,11 @@
 	/*
 	 * Call the optional HC's init_card function to handle quirks.
 	 */
-	if (host->ops->init_card)
+	if (host->ops->init_card) {
+		mmc_host_clk_hold(host);
 		host->ops->init_card(host, card);
+		mmc_host_clk_release(host);
+	}
 
 	/*
 	 * If the host and card support UHS-I mode request the card
@@ -807,9 +813,12 @@
 		 * Switch to wider bus (if supported).
 		 */
 		err = sdio_enable_4bit_bus(card);
-		if (err > 0)
-			mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
-		else if (err)
+		if (err > 0) {
+			if (card->host->caps & MMC_CAP_8_BIT_DATA)
+				mmc_set_bus_width(card->host, MMC_BUS_WIDTH_8);
+			else if (card->host->caps & MMC_CAP_4_BIT_DATA)
+				mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
+		} else if (err)
 			goto remove;
 	}
 finish:
@@ -962,13 +971,16 @@
 		/* We may have switched to 1-bit mode during suspend */
 		err = sdio_enable_4bit_bus(host->card);
 		if (err > 0) {
-			mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
+			if (host->caps & MMC_CAP_8_BIT_DATA)
+				mmc_set_bus_width(host, MMC_BUS_WIDTH_8);
+			else if (host->caps & MMC_CAP_4_BIT_DATA)
+				mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
 			err = 0;
 		}
 	}
 
 	if (!err && host->sdio_irqs)
-		mmc_signal_sdio_irq(host);
+		wake_up_process(host->sdio_irq_thread);
 	mmc_release_host(host);
 
 	/*
@@ -1282,8 +1294,12 @@
 	mmc_set_clock(host, mmc_sdio_get_max_clock(card));
 
 	err = sdio_enable_4bit_bus(card);
-	if (err > 0)
-		mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
+	if (err > 0) {
+		if (host->caps & MMC_CAP_8_BIT_DATA)
+			mmc_set_bus_width(host, MMC_BUS_WIDTH_8);
+		else if (host->caps & MMC_CAP_4_BIT_DATA)
+			mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
+	}
 	else if (err)
 		goto err;
 
diff --git a/drivers/mmc/core/sdio_cis.c b/drivers/mmc/core/sdio_cis.c
index f1c7ed8..2ca585d 100644
--- a/drivers/mmc/core/sdio_cis.c
+++ b/drivers/mmc/core/sdio_cis.c
@@ -55,7 +55,7 @@
 
 	for (i = 0; i < nr_strings; i++) {
 		buffer[i] = string;
-		strcpy(string, buf);
+		strlcpy(string, buf, sizeof(string));
 		string += strlen(string) + 1;
 		buf += strlen(buf) + 1;
 	}
@@ -270,8 +270,16 @@
 			break;
 
 		/* null entries have no link field or data */
-		if (tpl_code == 0x00)
-			continue;
+		if (tpl_code == 0x00) {
+			if (card->cis.vendor == 0x70 &&
+				(card->cis.device == 0x2460 ||
+				 card->cis.device == 0x0460 ||
+				 card->cis.device == 0x23F1 ||
+				 card->cis.device == 0x23F0))
+				break;
+			else
+				continue;
+		}
 
 		ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_link);
 		if (ret)
diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c
index f573e7f..fca3274 100644
--- a/drivers/mmc/core/sdio_irq.c
+++ b/drivers/mmc/core/sdio_irq.c
@@ -28,18 +28,20 @@
 
 #include "sdio_ops.h"
 
-static int process_sdio_pending_irqs(struct mmc_card *card)
+static int process_sdio_pending_irqs(struct mmc_host *host)
 {
+	struct mmc_card *card = host->card;
 	int i, ret, count;
 	unsigned char pending;
 	struct sdio_func *func;
 
 	/*
 	 * Optimization, if there is only 1 function interrupt registered
-	 * call irq handler directly
+	 * and we know an IRQ was signaled then call irq handler directly.
+	 * Otherwise do the full probe.
 	 */
 	func = card->sdio_single_irq;
-	if (func) {
+	if (func && host->sdio_irq_pending) {
 		func->irq_handler(func);
 		return 1;
 	}
@@ -116,7 +118,8 @@
 		ret = __mmc_claim_host(host, &host->sdio_irq_thread_abort);
 		if (ret)
 			break;
-		ret = process_sdio_pending_irqs(host->card);
+		ret = process_sdio_pending_irqs(host);
+		host->sdio_irq_pending = false;
 		mmc_release_host(host);
 
 		/*
@@ -212,14 +215,14 @@
 
 	card->sdio_single_irq = NULL;
 	if ((card->host->caps & MMC_CAP_SDIO_IRQ) &&
-	    card->host->sdio_irqs == 1)
+			card->host->sdio_irqs == 1)
 		for (i = 0; i < card->sdio_funcs; i++) {
-		       func = card->sdio_func[i];
-		       if (func && func->irq_handler) {
-			       card->sdio_single_irq = func;
-			       break;
-		       }
-	       }
+			func = card->sdio_func[i];
+			if (func && func->irq_handler) {
+				card->sdio_single_irq = func;
+				break;
+			}
+		}
 }
 
 /**
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 2bc06e7..96eae7d 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -458,6 +458,104 @@
 config MMC_TMIO_CORE
 	tristate
 
+config MMC_MSM
+	tristate "Qualcomm SDCC Controller Support"
+	depends on MMC && ARCH_MSM
+	help
+	  This provides support for the SD/MMC cell found in the
+          MSM and QSD SOCs from Qualcomm.
+
+config MMC_MSM_CARD_HW_DETECTION
+	boolean "Qualcomm MMC Hardware detection support"
+	depends on MMC_MSM
+	default n
+	help
+	  Select Y if the hardware has support to detect card insertion/removal.
+
+config MMC_MSM_SDC1_SUPPORT
+	boolean "Qualcomm SDC1 support"
+	depends on MMC_MSM
+	default y
+	help
+	  Select Y to enable Slot 1.
+
+config MMC_MSM_SDC1_8_BIT_SUPPORT
+	boolean "Qualcomm SDC1 8bit support"
+	depends on MMC_MSM_SDC1_SUPPORT
+	default n
+	help
+	  Select Y to enable 8bit support for Slot 1.
+
+config MMC_MSM_SDC2_SUPPORT
+	boolean "Qualcomm SDC2 support"
+	depends on MMC_MSM
+	default y
+	help
+	  Select Y to enable Slot 2.
+
+config MMC_MSM_SDC2_8_BIT_SUPPORT
+	boolean "Qualcomm SDC2 8bit support"
+	depends on MMC_MSM_SDC2_SUPPORT
+	default n
+	help
+	  Select Y to enable 8bit support for Slot 2.
+
+config MMC_MSM_SDC3_SUPPORT
+	boolean "Qualcomm SDC3 support"
+	depends on MMC_MSM
+	default n
+	help
+	  Select Y to enable Slot 3.
+
+config MMC_MSM_SDC3_8_BIT_SUPPORT
+	boolean "Qualcomm SDC3 8bit support"
+	depends on MMC_MSM_SDC3_SUPPORT
+	default n
+	help
+	  Select Y to enable 8bit support for Slot 3.
+
+config MMC_MSM_SDC3_WP_SUPPORT
+	boolean "Qualcomm SDC3 write protection support"
+	depends on MMC_MSM_SDC3_SUPPORT
+	default n
+	help
+	  Select Y to enable write protection support for Slot 3.
+
+config MMC_MSM_SDC4_SUPPORT
+	boolean "Qualcomm SDC4 support"
+	depends on MMC_MSM
+	default n
+	help
+	  Select Y to enable Slot 4.
+
+config MMC_MSM_SDC4_8_BIT_SUPPORT
+	boolean "Qualcomm SDC4 8bit support"
+	depends on MMC_MSM_SDC4_SUPPORT
+	default n
+	help
+	  Select Y to enable 8bit support for Slot 4.
+
+config MMC_MSM_SDC5_SUPPORT
+	boolean "Qualcomm SDC5 support"
+	depends on MMC_MSM
+	default n
+	help
+	  Select Y to enable Slot 5.
+
+config MMC_MSM_SDC5_8_BIT_SUPPORT
+	boolean "Qualcomm SDC5 8bit support"
+	depends on MMC_MSM_SDC5_SUPPORT
+	default n
+	help
+	  Select Y to enable 8bit support for Slot 5.
+
+config MMC_MSM_SPS_SUPPORT
+	bool "Use SPS BAM as data mover"
+	depends on MMC_MSM && SPS
+	default n
+	help
+	  Select Y to use SPS BAM as data mover
+
 config MMC_TMIO
 	tristate "Toshiba Mobile IO Controller (TMIO) MMC/SD function support"
 	depends on MFD_TMIO || MFD_ASIC3
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 3e7e26d..843ce06 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -36,6 +36,9 @@
 tmio_mmc_core-$(subst m,y,$(CONFIG_MMC_SDHI))	+= tmio_mmc_dma.o
 obj-$(CONFIG_MMC_SDHI)		+= sh_mobile_sdhi.o
 obj-$(CONFIG_MMC_CB710)		+= cb710-mmc.o
+obj-$(CONFIG_MMC_MSM)		+= msm_sdcc.o
+obj-$(CONFIG_MMC_MSM_SPS_SUPPORT) += msm_sdcc_dml.o
+obj-$(CONFIG_MMC_CB710)	+= cb710-mmc.o
 obj-$(CONFIG_MMC_VIA_SDMMC)	+= via-sdmmc.o
 obj-$(CONFIG_SDH_BFIN)		+= bfin_sdh.o
 obj-$(CONFIG_MMC_DW)		+= dw_mmc.o
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index 1d14cda..ac8b164 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -3,7 +3,7 @@
  *
  *  Copyright (C) 2007 Google Inc,
  *  Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved.
- *  Copyright (C) 2009, Code Aurora Forum. All Rights Reserved.
+ *  Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -19,14 +19,17 @@
 #include <linux/moduleparam.h>
 #include <linux/init.h>
 #include <linux/ioport.h>
+#include <linux/of.h>
 #include <linux/device.h>
 #include <linux/interrupt.h>
+#include <linux/irq.h>
 #include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/highmem.h>
 #include <linux/log2.h>
 #include <linux/mmc/host.h>
 #include <linux/mmc/card.h>
+#include <linux/mmc/mmc.h>
 #include <linux/mmc/sdio.h>
 #include <linux/clk.h>
 #include <linux/scatterlist.h>
@@ -35,149 +38,328 @@
 #include <linux/debugfs.h>
 #include <linux/io.h>
 #include <linux/memory.h>
-#include <linux/gfp.h>
+#include <linux/pm_runtime.h>
+#include <linux/wakelock.h>
 #include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/pm_qos.h>
 
 #include <asm/cacheflush.h>
 #include <asm/div64.h>
 #include <asm/sizes.h>
 
-#include <mach/mmc.h>
+#include <asm/mach/mmc.h>
 #include <mach/msm_iomap.h>
-#include <mach/dma.h>
 #include <mach/clk.h>
+#include <mach/dma.h>
+#include <mach/sdio_al.h>
+#include <mach/mpm.h>
+#include <mach/msm_bus.h>
 
 #include "msm_sdcc.h"
+#include "msm_sdcc_dml.h"
 
 #define DRIVER_NAME "msm-sdcc"
 
-#define BUSCLK_PWRSAVE 1
-#define BUSCLK_TIMEOUT (HZ)
-static unsigned int msmsdcc_fmin = 144000;
-static unsigned int msmsdcc_fmax = 50000000;
-static unsigned int msmsdcc_4bit = 1;
+#define DBG(host, fmt, args...)	\
+	pr_debug("%s: %s: " fmt "\n", mmc_hostname(host->mmc), __func__ , args)
+
+#define IRQ_DEBUG 0
+#define SPS_SDCC_PRODUCER_PIPE_INDEX	1
+#define SPS_SDCC_CONSUMER_PIPE_INDEX	2
+#define SPS_CONS_PERIPHERAL		0
+#define SPS_PROD_PERIPHERAL		1
+/* Use SPS only if transfer size is more than this macro */
+#define SPS_MIN_XFER_SIZE		MCI_FIFOSIZE
+
+#define MSM_MMC_BUS_VOTING_DELAY	200 /* msecs */
+
+#if defined(CONFIG_DEBUG_FS)
+static void msmsdcc_dbg_createhost(struct msmsdcc_host *);
+static struct dentry *debugfs_dir;
+static struct dentry *debugfs_file;
+static int  msmsdcc_dbg_init(void);
+#endif
+
+static int msmsdcc_prep_xfer(struct msmsdcc_host *host, struct mmc_data
+			     *data);
+
+static u64 dma_mask = DMA_BIT_MASK(32);
 static unsigned int msmsdcc_pwrsave = 1;
-static unsigned int msmsdcc_piopoll = 1;
-static unsigned int msmsdcc_sdioirq;
 
-#define PIO_SPINMAX 30
-#define CMD_SPINMAX 20
+static struct mmc_command dummy52cmd;
+static struct mmc_request dummy52mrq = {
+	.cmd = &dummy52cmd,
+	.data = NULL,
+	.stop = NULL,
+};
+static struct mmc_command dummy52cmd = {
+	.opcode = SD_IO_RW_DIRECT,
+	.flags = MMC_RSP_PRESENT,
+	.data = NULL,
+	.mrq = &dummy52mrq,
+};
+/*
+ * An array holding the Tuning pattern to compare with when
+ * executing a tuning cycle.
+ */
+static const u32 tuning_block_64[] = {
+	0x00FF0FFF, 0xCCC3CCFF, 0xFFCC3CC3, 0xEFFEFFFE,
+	0xDDFFDFFF, 0xFBFFFBFF, 0xFF7FFFBF, 0xEFBDF777,
+	0xF0FFF0FF, 0x3CCCFC0F, 0xCFCC33CC, 0xEEFFEFFF,
+	0xFDFFFDFF, 0xFFBFFFDF, 0xFFF7FFBB, 0xDE7B7FF7
+};
 
+static const u32 tuning_block_128[] = {
+	0xFF00FFFF, 0x0000FFFF, 0xCCCCFFFF, 0xCCCC33CC,
+	0xCC3333CC, 0xFFFFCCCC, 0xFFFFEEFF, 0xFFEEEEFF,
+	0xFFDDFFFF, 0xDDDDFFFF, 0xBBFFFFFF, 0xBBFFFFFF,
+	0xFFFFFFBB, 0xFFFFFF77, 0x77FF7777, 0xFFEEDDBB,
+	0x00FFFFFF, 0x00FFFFFF, 0xCCFFFF00, 0xCC33CCCC,
+	0x3333CCCC, 0xFFCCCCCC, 0xFFEEFFFF, 0xEEEEFFFF,
+	0xDDFFFFFF, 0xDDFFFFFF, 0xFFFFFFDD, 0xFFFFFFBB,
+	0xFFFFBBBB, 0xFFFF77FF, 0xFF7777FF, 0xEEDDBB77
+};
 
-static inline void
-msmsdcc_disable_clocks(struct msmsdcc_host *host, int deferr)
+#if IRQ_DEBUG == 1
+static char *irq_status_bits[] = { "cmdcrcfail", "datcrcfail", "cmdtimeout",
+				   "dattimeout", "txunderrun", "rxoverrun",
+				   "cmdrespend", "cmdsent", "dataend", NULL,
+				   "datablkend", "cmdactive", "txactive",
+				   "rxactive", "txhalfempty", "rxhalffull",
+				   "txfifofull", "rxfifofull", "txfifoempty",
+				   "rxfifoempty", "txdataavlbl", "rxdataavlbl",
+				   "sdiointr", "progdone", "atacmdcompl",
+				   "sdiointrope", "ccstimeout", NULL, NULL,
+				   NULL, NULL, NULL };
+
+static void
+msmsdcc_print_status(struct msmsdcc_host *host, char *hdr, uint32_t status)
 {
-	WARN_ON(!host->clks_on);
+	int i;
 
-	BUG_ON(host->curr.mrq);
-
-	if (deferr) {
-		mod_timer(&host->busclk_timer, jiffies + BUSCLK_TIMEOUT);
-	} else {
-		del_timer_sync(&host->busclk_timer);
-		/* Need to check clks_on again in case the busclk
-		 * timer fired
-		 */
-		if (host->clks_on) {
-			clk_disable(host->clk);
-			clk_disable(host->pclk);
-			host->clks_on = 0;
-		}
+	pr_debug("%s-%s ", mmc_hostname(host->mmc), hdr);
+	for (i = 0; i < 32; i++) {
+		if (status & (1 << i))
+			pr_debug("%s ", irq_status_bits[i]);
 	}
+	pr_debug("\n");
 }
-
-static inline int
-msmsdcc_enable_clocks(struct msmsdcc_host *host)
-{
-	int rc;
-
-	del_timer_sync(&host->busclk_timer);
-
-	if (!host->clks_on) {
-		rc = clk_enable(host->pclk);
-		if (rc)
-			return rc;
-		rc = clk_enable(host->clk);
-		if (rc) {
-			clk_disable(host->pclk);
-			return rc;
-		}
-		udelay(1 + ((3 * USEC_PER_SEC) /
-		       (host->clk_rate ? host->clk_rate : msmsdcc_fmin)));
-		host->clks_on = 1;
-	}
-	return 0;
-}
-
-static inline unsigned int
-msmsdcc_readl(struct msmsdcc_host *host, unsigned int reg)
-{
-	return readl(host->base + reg);
-}
-
-static inline void
-msmsdcc_writel(struct msmsdcc_host *host, u32 data, unsigned int reg)
-{
-	writel(data, host->base + reg);
-	/* 3 clk delay required! */
-	udelay(1 + ((3 * USEC_PER_SEC) /
-	       (host->clk_rate ? host->clk_rate : msmsdcc_fmin)));
-}
+#endif
 
 static void
 msmsdcc_start_command(struct msmsdcc_host *host, struct mmc_command *cmd,
 		      u32 c);
+static inline void msmsdcc_sync_reg_wr(struct msmsdcc_host *host);
+static inline void msmsdcc_delay(struct msmsdcc_host *host);
+static void msmsdcc_dump_sdcc_state(struct msmsdcc_host *host);
+static void msmsdcc_sg_start(struct msmsdcc_host *host);
+static int msmsdcc_vreg_reset(struct msmsdcc_host *host);
+static int msmsdcc_runtime_resume(struct device *dev);
 
-static void msmsdcc_reset_and_restore(struct msmsdcc_host *host)
+static inline unsigned short msmsdcc_get_nr_sg(struct msmsdcc_host *host)
 {
-	u32	mci_clk = 0;
-	u32	mci_mask0 = 0;
-	int	ret = 0;
+	unsigned short ret = NR_SG;
 
-	/* Save the controller state */
-	mci_clk = readl(host->base + MMCICLOCK);
-	mci_mask0 = readl(host->base + MMCIMASK0);
+	if (host->is_sps_mode) {
+		ret = SPS_MAX_DESCS;
+	} else { /* DMA or PIO mode */
+		if (NR_SG > MAX_NR_SG_DMA_PIO)
+			ret = MAX_NR_SG_DMA_PIO;
+	}
+
+	return ret;
+}
+
+/* Prevent idle power collapse(pc) while operating in peripheral mode */
+static void msmsdcc_pm_qos_update_latency(struct msmsdcc_host *host, int vote)
+{
+	if (!host->cpu_dma_latency)
+		return;
+
+	if (vote)
+		pm_qos_update_request(&host->pm_qos_req_dma,
+				host->cpu_dma_latency);
+	else
+		pm_qos_update_request(&host->pm_qos_req_dma,
+					PM_QOS_DEFAULT_VALUE);
+}
+
+#ifdef CONFIG_MMC_MSM_SPS_SUPPORT
+static int msmsdcc_sps_reset_ep(struct msmsdcc_host *host,
+				struct msmsdcc_sps_ep_conn_data *ep);
+static int msmsdcc_sps_restore_ep(struct msmsdcc_host *host,
+				struct msmsdcc_sps_ep_conn_data *ep);
+#else
+static inline int msmsdcc_sps_init_ep_conn(struct msmsdcc_host *host,
+				struct msmsdcc_sps_ep_conn_data *ep,
+				bool is_producer) { return 0; }
+static inline void msmsdcc_sps_exit_ep_conn(struct msmsdcc_host *host,
+				struct msmsdcc_sps_ep_conn_data *ep) { }
+static inline int msmsdcc_sps_reset_ep(struct msmsdcc_host *host,
+				struct msmsdcc_sps_ep_conn_data *ep)
+{
+	return 0;
+}
+static inline int msmsdcc_sps_restore_ep(struct msmsdcc_host *host,
+				struct msmsdcc_sps_ep_conn_data *ep)
+{
+	return 0;
+}
+static inline int msmsdcc_sps_init(struct msmsdcc_host *host) { return 0; }
+static inline void msmsdcc_sps_exit(struct msmsdcc_host *host) {}
+#endif /* CONFIG_MMC_MSM_SPS_SUPPORT */
+
+/**
+ * Apply soft reset to all SDCC BAM pipes
+ *
+ * This function applies soft reset to SDCC BAM pipe.
+ *
+ * This function should be called to recover from error
+ * conditions encountered during CMD/DATA tranfsers with card.
+ *
+ * @host - Pointer to driver's host structure
+ *
+ */
+static void msmsdcc_sps_pipes_reset_and_restore(struct msmsdcc_host *host)
+{
+	int rc;
+
+	/* Reset all SDCC BAM pipes */
+	rc = msmsdcc_sps_reset_ep(host, &host->sps.prod);
+	if (rc)
+		pr_err("%s:msmsdcc_sps_reset_ep(prod) error=%d\n",
+				mmc_hostname(host->mmc), rc);
+	rc = msmsdcc_sps_reset_ep(host, &host->sps.cons);
+	if (rc)
+		pr_err("%s:msmsdcc_sps_reset_ep(cons) error=%d\n",
+				mmc_hostname(host->mmc), rc);
+
+	/* Restore all BAM pipes connections */
+	rc = msmsdcc_sps_restore_ep(host, &host->sps.prod);
+	if (rc)
+		pr_err("%s:msmsdcc_sps_restore_ep(prod) error=%d\n",
+				mmc_hostname(host->mmc), rc);
+	rc = msmsdcc_sps_restore_ep(host, &host->sps.cons);
+	if (rc)
+		pr_err("%s:msmsdcc_sps_restore_ep(cons) error=%d\n",
+				mmc_hostname(host->mmc), rc);
+}
+
+/**
+ * Apply soft reset
+ *
+ * This function applies soft reset to SDCC core and DML core.
+ *
+ * This function should be called to recover from error
+ * conditions encountered with CMD/DATA tranfsers with card.
+ *
+ * Soft reset should only be used with SDCC controller v4.
+ *
+ * @host - Pointer to driver's host structure
+ *
+ */
+static void msmsdcc_soft_reset(struct msmsdcc_host *host)
+{
+	/*
+	 * Reset SDCC controller's DPSM (data path state machine
+	 * and CPSM (command path state machine).
+	 */
+	writel_relaxed(0, host->base + MMCICOMMAND);
+	msmsdcc_sync_reg_wr(host);
+	writel_relaxed(0, host->base + MMCIDATACTRL);
+	msmsdcc_sync_reg_wr(host);
+}
+
+static void msmsdcc_hard_reset(struct msmsdcc_host *host)
+{
+	int ret;
 
 	/* Reset the controller */
 	ret = clk_reset(host->clk, CLK_RESET_ASSERT);
 	if (ret)
-		pr_err("%s: Clock assert failed at %u Hz with err %d\n",
-				mmc_hostname(host->mmc), host->clk_rate, ret);
+		pr_err("%s: Clock assert failed at %u Hz"
+			" with err %d\n", mmc_hostname(host->mmc),
+				host->clk_rate, ret);
 
 	ret = clk_reset(host->clk, CLK_RESET_DEASSERT);
 	if (ret)
-		pr_err("%s: Clock deassert failed at %u Hz with err %d\n",
-				mmc_hostname(host->mmc), host->clk_rate, ret);
+		pr_err("%s: Clock deassert failed at %u Hz"
+			" with err %d\n", mmc_hostname(host->mmc),
+			host->clk_rate, ret);
 
-	pr_info("%s: Controller has been re-initialiazed\n",
-			mmc_hostname(host->mmc));
-
-	/* Restore the contoller state */
-	writel(host->pwr, host->base + MMCIPOWER);
-	writel(mci_clk, host->base + MMCICLOCK);
-	writel(mci_mask0, host->base + MMCIMASK0);
-	ret = clk_set_rate(host->clk, host->clk_rate);
-	if (ret)
-		pr_err("%s: Failed to set clk rate %u Hz (%d)\n",
-				mmc_hostname(host->mmc), host->clk_rate, ret);
+	mb();
+	/* Give some delay for clock reset to propogate to controller */
+	msmsdcc_delay(host);
 }
 
-static void
+static void msmsdcc_reset_and_restore(struct msmsdcc_host *host)
+{
+	if (host->sdcc_version) {
+		if (host->is_sps_mode) {
+			/* Reset DML first */
+			msmsdcc_dml_reset(host);
+			/*
+			 * delay the SPS pipe reset in thread context as
+			 * sps_connect/sps_disconnect APIs can be called
+			 * only from non-atomic context.
+			 */
+			host->sps.pipe_reset_pending = true;
+		}
+		mb();
+		msmsdcc_soft_reset(host);
+
+		pr_debug("%s: Applied soft reset to Controller\n",
+				mmc_hostname(host->mmc));
+
+		if (host->is_sps_mode)
+			msmsdcc_dml_init(host);
+	} else {
+		/* Give Clock reset (hard reset) to controller */
+		u32	mci_clk = 0;
+		u32	mci_mask0 = 0;
+
+		/* Save the controller state */
+		mci_clk = readl_relaxed(host->base + MMCICLOCK);
+		mci_mask0 = readl_relaxed(host->base + MMCIMASK0);
+		host->pwr = readl_relaxed(host->base + MMCIPOWER);
+		mb();
+
+		msmsdcc_hard_reset(host);
+		pr_debug("%s: Controller has been reinitialized\n",
+				mmc_hostname(host->mmc));
+
+		/* Restore the contoller state */
+		writel_relaxed(host->pwr, host->base + MMCIPOWER);
+		msmsdcc_sync_reg_wr(host);
+		writel_relaxed(mci_clk, host->base + MMCICLOCK);
+		msmsdcc_sync_reg_wr(host);
+		writel_relaxed(mci_mask0, host->base + MMCIMASK0);
+		mb(); /* no delay required after writing to MASK0 register */
+	}
+
+	if (host->dummy_52_needed)
+		host->dummy_52_needed = 0;
+}
+
+static int
 msmsdcc_request_end(struct msmsdcc_host *host, struct mmc_request *mrq)
 {
+	int retval = 0;
+
 	BUG_ON(host->curr.data);
 
-	host->curr.mrq = NULL;
-	host->curr.cmd = NULL;
+	del_timer(&host->req_tout_timer);
 
 	if (mrq->data)
 		mrq->data->bytes_xfered = host->curr.data_xfered;
 	if (mrq->cmd->error == -ETIMEDOUT)
 		mdelay(5);
 
-#if BUSCLK_PWRSAVE
-	msmsdcc_disable_clocks(host, 1);
-#endif
+	/* Clear current request information as current request has ended */
+	memset(&host->curr, 0, sizeof(struct msmsdcc_curr_req));
+
 	/*
 	 * Need to drop the host lock here; mmc_request_done may call
 	 * back into the driver...
@@ -185,6 +367,8 @@
 	spin_unlock(&host->lock);
 	mmc_request_done(host->mmc, mrq);
 	spin_lock(&host->lock);
+
+	return retval;
 }
 
 static void
@@ -192,37 +376,80 @@
 {
 	host->curr.data = NULL;
 	host->curr.got_dataend = 0;
+	host->curr.wait_for_auto_prog_done = 0;
+	host->curr.got_auto_prog_done = 0;
+	writel_relaxed(readl_relaxed(host->base + MMCIDATACTRL) &
+			(~(MCI_DPSM_ENABLE)), host->base + MMCIDATACTRL);
+	msmsdcc_sync_reg_wr(host); /* Allow the DPSM to be reset */
 }
 
-uint32_t msmsdcc_fifo_addr(struct msmsdcc_host *host)
+static inline uint32_t msmsdcc_fifo_addr(struct msmsdcc_host *host)
 {
-	return host->memres->start + MMCIFIFO;
+	return host->core_memres->start + MMCIFIFO;
+}
+
+static inline unsigned int msmsdcc_get_min_sup_clk_rate(
+					struct msmsdcc_host *host);
+
+static inline void msmsdcc_sync_reg_wr(struct msmsdcc_host *host)
+{
+	mb();
+	if (!host->sdcc_version)
+		udelay(host->reg_write_delay);
+	else if (readl_relaxed(host->base + MCI_STATUS2) &
+			MCI_MCLK_REG_WR_ACTIVE) {
+		ktime_t start, diff;
+
+		start = ktime_get();
+		while (readl_relaxed(host->base + MCI_STATUS2) &
+			MCI_MCLK_REG_WR_ACTIVE) {
+			diff = ktime_sub(ktime_get(), start);
+			/* poll for max. 1 ms */
+			if (ktime_to_us(diff) > 1000) {
+				pr_warning("%s: previous reg. write is"
+					" still active\n",
+					mmc_hostname(host->mmc));
+				break;
+			}
+		}
+	}
+}
+
+static inline void msmsdcc_delay(struct msmsdcc_host *host)
+{
+	udelay(host->reg_write_delay);
+
 }
 
 static inline void
-msmsdcc_start_command_exec(struct msmsdcc_host *host, u32 arg, u32 c) {
-       msmsdcc_writel(host, arg, MMCIARGUMENT);
-       msmsdcc_writel(host, c, MMCICOMMAND);
+msmsdcc_start_command_exec(struct msmsdcc_host *host, u32 arg, u32 c)
+{
+	writel_relaxed(arg, host->base + MMCIARGUMENT);
+	writel_relaxed(c, host->base + MMCICOMMAND);
+	/*
+	 * As after sending the command, we don't write any of the
+	 * controller registers and just wait for the
+	 * CMD_RESPOND_END/CMD_SENT/Command failure notication
+	 * from Controller.
+	 */
+	mb();
 }
 
 static void
 msmsdcc_dma_exec_func(struct msm_dmov_cmd *cmd)
 {
-	struct msmsdcc_host *host = (struct msmsdcc_host *)cmd->data;
+	struct msmsdcc_host *host = (struct msmsdcc_host *)cmd->user;
 
-	msmsdcc_writel(host, host->cmd_timeout, MMCIDATATIMER);
-	msmsdcc_writel(host, (unsigned int)host->curr.xfer_size,
-		       MMCIDATALENGTH);
-	msmsdcc_writel(host, (msmsdcc_readl(host, MMCIMASK0) &
-			(~MCI_IRQ_PIO)) | host->cmd_pio_irqmask, MMCIMASK0);
-	msmsdcc_writel(host, host->cmd_datactrl, MMCIDATACTRL);
+	writel_relaxed(host->cmd_timeout, host->base + MMCIDATATIMER);
+	writel_relaxed((unsigned int)host->curr.xfer_size,
+			host->base + MMCIDATALENGTH);
+	writel_relaxed(host->cmd_datactrl, host->base + MMCIDATACTRL);
+	msmsdcc_sync_reg_wr(host); /* Force delay prior to ADM or command */
 
 	if (host->cmd_cmd) {
 		msmsdcc_start_command_exec(host,
-					   (u32) host->cmd_cmd->arg,
-					   (u32) host->cmd_c);
+			(u32)host->cmd_cmd->arg, (u32)host->cmd_c);
 	}
-	host->dma.active = 1;
 }
 
 static void
@@ -231,15 +458,10 @@
 	struct msmsdcc_host *host = (struct msmsdcc_host *)data;
 	unsigned long		flags;
 	struct mmc_request	*mrq;
-	struct msm_dmov_errdata err;
 
 	spin_lock_irqsave(&host->lock, flags);
-	host->dma.active = 0;
-
-	err = host->dma.err;
 	mrq = host->curr.mrq;
 	BUG_ON(!mrq);
-	WARN_ON(!mrq->data);
 
 	if (!(host->dma.result & DMOV_RSLT_VALID)) {
 		pr_err("msmsdcc: Invalid DataMover result\n");
@@ -248,6 +470,7 @@
 
 	if (host->dma.result & DMOV_RSLT_DONE) {
 		host->curr.data_xfered = host->curr.xfer_size;
+		host->curr.xfer_remain -= host->curr.xfer_size;
 	} else {
 		/* Error or flush  */
 		if (host->dma.result & DMOV_RSLT_ERROR)
@@ -256,44 +479,67 @@
 		if (host->dma.result & DMOV_RSLT_FLUSH)
 			pr_err("%s: DMA channel flushed (0x%.8x)\n",
 			       mmc_hostname(host->mmc), host->dma.result);
-
 		pr_err("Flush data: %.8x %.8x %.8x %.8x %.8x %.8x\n",
-		       err.flush[0], err.flush[1], err.flush[2],
-		       err.flush[3], err.flush[4], err.flush[5]);
-
+		       host->dma.err.flush[0], host->dma.err.flush[1],
+		       host->dma.err.flush[2], host->dma.err.flush[3],
+		       host->dma.err.flush[4],
+		       host->dma.err.flush[5]);
 		msmsdcc_reset_and_restore(host);
 		if (!mrq->data->error)
 			mrq->data->error = -EIO;
 	}
-	dma_unmap_sg(mmc_dev(host->mmc), host->dma.sg, host->dma.num_ents,
-		     host->dma.dir);
+	if (!mrq->data->host_cookie)
+		dma_unmap_sg(mmc_dev(host->mmc), host->dma.sg,
+			     host->dma.num_ents, host->dma.dir);
+
+	if (host->curr.user_pages) {
+		struct scatterlist *sg = host->dma.sg;
+		int i;
+
+		for (i = 0; i < host->dma.num_ents; i++, sg++)
+			flush_dcache_page(sg_page(sg));
+	}
 
 	host->dma.sg = NULL;
 	host->dma.busy = 0;
 
-	if (host->curr.got_dataend || mrq->data->error) {
-
+	if ((host->curr.got_dataend && (!host->curr.wait_for_auto_prog_done ||
+		(host->curr.wait_for_auto_prog_done &&
+		host->curr.got_auto_prog_done))) || mrq->data->error) {
 		/*
 		 * If we've already gotten our DATAEND / DATABLKEND
 		 * for this request, then complete it through here.
 		 */
-		msmsdcc_stop_data(host);
 
-		if (!mrq->data->error)
+		if (!mrq->data->error) {
 			host->curr.data_xfered = host->curr.xfer_size;
-		if (!mrq->data->stop || mrq->cmd->error) {
-			host->curr.mrq = NULL;
-			host->curr.cmd = NULL;
+			host->curr.xfer_remain -= host->curr.xfer_size;
+		}
+		if (host->dummy_52_needed) {
 			mrq->data->bytes_xfered = host->curr.data_xfered;
-
+			host->dummy_52_sent = 1;
+			msmsdcc_start_command(host, &dummy52cmd,
+					      MCI_CPSM_PROGENA);
+			goto out;
+		}
+		msmsdcc_stop_data(host);
+		if (!mrq->data->stop || mrq->cmd->error ||
+			(mrq->sbc && !mrq->data->error)) {
+			mrq->data->bytes_xfered = host->curr.data_xfered;
+			del_timer(&host->req_tout_timer);
+			/*
+			 * Clear current request information as current
+			 * request has ended
+			 */
+			memset(&host->curr, 0, sizeof(struct msmsdcc_curr_req));
 			spin_unlock_irqrestore(&host->lock, flags);
-#if BUSCLK_PWRSAVE
-			msmsdcc_disable_clocks(host, 1);
-#endif
+
 			mmc_request_done(host->mmc, mrq);
 			return;
-		} else
+		} else if (mrq->data->stop && ((mrq->sbc && mrq->data->error)
+				|| !mrq->sbc)) {
 			msmsdcc_start_command(host, mrq->data->stop, 0);
+		}
 	}
 
 out:
@@ -301,6 +547,211 @@
 	return;
 }
 
+#ifdef CONFIG_MMC_MSM_SPS_SUPPORT
+/**
+ * Callback notification from SPS driver
+ *
+ * This callback function gets triggered called from
+ * SPS driver when requested SPS data transfer is
+ * completed.
+ *
+ * SPS driver invokes this callback in BAM irq context so
+ * SDCC driver schedule a tasklet for further processing
+ * this callback notification at later point of time in
+ * tasklet context and immediately returns control back
+ * to SPS driver.
+ *
+ * @nofity - Pointer to sps event notify sturcture
+ *
+ */
+static void
+msmsdcc_sps_complete_cb(struct sps_event_notify *notify)
+{
+	struct msmsdcc_host *host =
+		(struct msmsdcc_host *)
+		((struct sps_event_notify *)notify)->user;
+
+	host->sps.notify = *notify;
+	pr_debug("%s: %s: sps ev_id=%d, addr=0x%x, size=0x%x, flags=0x%x\n",
+		mmc_hostname(host->mmc), __func__, notify->event_id,
+		notify->data.transfer.iovec.addr,
+		notify->data.transfer.iovec.size,
+		notify->data.transfer.iovec.flags);
+	/* Schedule a tasklet for completing data transfer */
+	tasklet_schedule(&host->sps.tlet);
+}
+
+/**
+ * Tasklet handler for processing SPS callback event
+ *
+ * This function processing SPS event notification and
+ * checks if the SPS transfer is completed or not and
+ * then accordingly notifies status to MMC core layer.
+ *
+ * This function is called in tasklet context.
+ *
+ * @data - Pointer to sdcc driver data
+ *
+ */
+static void msmsdcc_sps_complete_tlet(unsigned long data)
+{
+	unsigned long flags;
+	int i, rc;
+	u32 data_xfered = 0;
+	struct mmc_request *mrq;
+	struct sps_iovec iovec;
+	struct sps_pipe *sps_pipe_handle;
+	struct msmsdcc_host *host = (struct msmsdcc_host *)data;
+	struct sps_event_notify *notify = &host->sps.notify;
+
+	spin_lock_irqsave(&host->lock, flags);
+	if (host->sps.dir == DMA_FROM_DEVICE)
+		sps_pipe_handle = host->sps.prod.pipe_handle;
+	else
+		sps_pipe_handle = host->sps.cons.pipe_handle;
+	mrq = host->curr.mrq;
+
+	if (!mrq) {
+		spin_unlock_irqrestore(&host->lock, flags);
+		return;
+	}
+
+	pr_debug("%s: %s: sps event_id=%d\n",
+		mmc_hostname(host->mmc), __func__,
+		notify->event_id);
+
+	if (msmsdcc_is_dml_busy(host)) {
+		/* oops !!! this should never happen. */
+		pr_err("%s: %s: Received SPS EOT event"
+			" but DML HW is still busy !!!\n",
+			mmc_hostname(host->mmc), __func__);
+	}
+	/*
+	 * Got End of transfer event!!! Check if all of the data
+	 * has been transferred?
+	 */
+	for (i = 0; i < host->sps.xfer_req_cnt; i++) {
+		rc = sps_get_iovec(sps_pipe_handle, &iovec);
+		if (rc) {
+			pr_err("%s: %s: sps_get_iovec() failed rc=%d, i=%d",
+				mmc_hostname(host->mmc), __func__, rc, i);
+			break;
+		}
+		data_xfered += iovec.size;
+	}
+
+	if (data_xfered == host->curr.xfer_size) {
+		host->curr.data_xfered = host->curr.xfer_size;
+		host->curr.xfer_remain -= host->curr.xfer_size;
+		pr_debug("%s: Data xfer success. data_xfered=0x%x",
+			mmc_hostname(host->mmc),
+			host->curr.xfer_size);
+	} else {
+		pr_err("%s: Data xfer failed. data_xfered=0x%x,"
+			" xfer_size=%d", mmc_hostname(host->mmc),
+			data_xfered, host->curr.xfer_size);
+		msmsdcc_reset_and_restore(host);
+		if (!mrq->data->error)
+			mrq->data->error = -EIO;
+	}
+
+	/* Unmap sg buffers */
+	if (!mrq->data->host_cookie)
+		dma_unmap_sg(mmc_dev(host->mmc), host->sps.sg,
+			     host->sps.num_ents, host->sps.dir);
+	host->sps.sg = NULL;
+	host->sps.busy = 0;
+
+	if ((host->curr.got_dataend && (!host->curr.wait_for_auto_prog_done ||
+		(host->curr.wait_for_auto_prog_done &&
+		host->curr.got_auto_prog_done))) || mrq->data->error) {
+		/*
+		 * If we've already gotten our DATAEND / DATABLKEND
+		 * for this request, then complete it through here.
+		 */
+
+		if (!mrq->data->error) {
+			host->curr.data_xfered = host->curr.xfer_size;
+			host->curr.xfer_remain -= host->curr.xfer_size;
+		}
+		if (host->dummy_52_needed) {
+			mrq->data->bytes_xfered = host->curr.data_xfered;
+			host->dummy_52_sent = 1;
+			msmsdcc_start_command(host, &dummy52cmd,
+					      MCI_CPSM_PROGENA);
+			spin_unlock_irqrestore(&host->lock, flags);
+			return;
+		}
+		msmsdcc_stop_data(host);
+		if (!mrq->data->stop || mrq->cmd->error ||
+			(mrq->sbc && !mrq->data->error)) {
+			mrq->data->bytes_xfered = host->curr.data_xfered;
+			del_timer(&host->req_tout_timer);
+			/*
+			 * Clear current request information as current
+			 * request has ended
+			 */
+			memset(&host->curr, 0, sizeof(struct msmsdcc_curr_req));
+			spin_unlock_irqrestore(&host->lock, flags);
+
+			mmc_request_done(host->mmc, mrq);
+			return;
+		} else if (mrq->data->stop && ((mrq->sbc && mrq->data->error)
+				|| !mrq->sbc)) {
+			msmsdcc_start_command(host, mrq->data->stop, 0);
+		}
+	}
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+/**
+ * Exit from current SPS data transfer
+ *
+ * This function exits from current SPS data transfer.
+ *
+ * This function should be called when error condition
+ * is encountered during data transfer.
+ *
+ * @host - Pointer to sdcc host structure
+ *
+ */
+static void msmsdcc_sps_exit_curr_xfer(struct msmsdcc_host *host)
+{
+	struct mmc_request *mrq;
+
+	mrq = host->curr.mrq;
+	BUG_ON(!mrq);
+
+	msmsdcc_reset_and_restore(host);
+	if (!mrq->data->error)
+		mrq->data->error = -EIO;
+
+	/* Unmap sg buffers */
+	if (!mrq->data->host_cookie)
+		dma_unmap_sg(mmc_dev(host->mmc), host->sps.sg,
+			     host->sps.num_ents, host->sps.dir);
+
+	host->sps.sg = NULL;
+	host->sps.busy = 0;
+	if (host->curr.data)
+		msmsdcc_stop_data(host);
+
+	if (!mrq->data->stop || mrq->cmd->error ||
+		(mrq->sbc && !mrq->data->error))
+		msmsdcc_request_end(host, mrq);
+	else if (mrq->data->stop && ((mrq->sbc && mrq->data->error)
+			|| !mrq->sbc))
+		msmsdcc_start_command(host, mrq->data->stop, 0);
+
+}
+#else
+static inline void msmsdcc_sps_complete_cb(struct sps_event_notify *notify) { }
+static inline void msmsdcc_sps_complete_tlet(unsigned long data) { }
+static inline void msmsdcc_sps_exit_curr_xfer(struct msmsdcc_host *host) { }
+#endif /* CONFIG_MMC_MSM_SPS_SUPPORT */
+
+static int msmsdcc_enable_cdr_cm_sdc4_dll(struct msmsdcc_host *host);
+
 static void
 msmsdcc_dma_complete_func(struct msm_dmov_cmd *cmd,
 			  unsigned int result,
@@ -317,16 +768,32 @@
 	tasklet_schedule(&host->dma_tlet);
 }
 
-static int validate_dma(struct msmsdcc_host *host, struct mmc_data *data)
+static bool msmsdcc_is_dma_possible(struct msmsdcc_host *host,
+				    struct mmc_data *data)
 {
-	if (host->dma.channel == -1)
-		return -ENOENT;
+	bool ret = true;
+	u32 xfer_size = data->blksz * data->blocks;
 
-	if ((data->blksz * data->blocks) < MCI_FIFOSIZE)
-		return -EINVAL;
-	if ((data->blksz * data->blocks) % MCI_FIFOSIZE)
-		return -EINVAL;
-	return 0;
+	if (host->is_sps_mode) {
+		/*
+		 * BAM Mode: Fall back on PIO if size is less
+		 * than or equal to SPS_MIN_XFER_SIZE bytes.
+		 */
+		if (xfer_size <= SPS_MIN_XFER_SIZE)
+			ret = false;
+	} else if (host->is_dma_mode) {
+		/*
+		 * ADM Mode: Fall back on PIO if size is less than FIFO size
+		 * or not integer multiple of FIFO size
+		 */
+		if (xfer_size % MCI_FIFOSIZE)
+			ret = false;
+	} else {
+		/* PIO Mode */
+		ret = false;
+	}
+
+	return ret;
 }
 
 static int msmsdcc_config_dma(struct msmsdcc_host *host, struct mmc_data *data)
@@ -334,49 +801,90 @@
 	struct msmsdcc_nc_dmadata *nc;
 	dmov_box *box;
 	uint32_t rows;
-	uint32_t crci;
 	unsigned int n;
-	int i, rc;
+	int i, err = 0, box_cmd_cnt = 0;
 	struct scatterlist *sg = data->sg;
+	unsigned int len, offset;
 
-	rc = validate_dma(host, data);
-	if (rc)
-		return rc;
+	if ((host->dma.channel == -1) || (host->dma.crci == -1))
+		return -ENOENT;
+
+	BUG_ON((host->pdev_id < 1) || (host->pdev_id > 5));
 
 	host->dma.sg = data->sg;
 	host->dma.num_ents = data->sg_len;
 
-       BUG_ON(host->dma.num_ents > NR_SG); /* Prevent memory corruption */
+	/* Prevent memory corruption */
+	BUG_ON(host->dma.num_ents > msmsdcc_get_nr_sg(host));
 
 	nc = host->dma.nc;
 
-	switch (host->pdev_id) {
-	case 1:
-		crci = MSMSDCC_CRCI_SDC1;
-		break;
-	case 2:
-		crci = MSMSDCC_CRCI_SDC2;
-		break;
-	case 3:
-		crci = MSMSDCC_CRCI_SDC3;
-		break;
-	case 4:
-		crci = MSMSDCC_CRCI_SDC4;
-		break;
-	default:
-		host->dma.sg = NULL;
-		host->dma.num_ents = 0;
-		return -ENOENT;
-	}
-
 	if (data->flags & MMC_DATA_READ)
 		host->dma.dir = DMA_FROM_DEVICE;
 	else
 		host->dma.dir = DMA_TO_DEVICE;
 
-	host->curr.user_pages = 0;
+	if (!data->host_cookie) {
+		n = msmsdcc_prep_xfer(host, data);
+		if (unlikely(n < 0)) {
+			host->dma.sg = NULL;
+			host->dma.num_ents = 0;
+			return -ENOMEM;
+		}
+	}
 
+	/* host->curr.user_pages = (data->flags & MMC_DATA_USERPAGE); */
+	host->curr.user_pages = 0;
 	box = &nc->cmd[0];
+	for (i = 0; i < host->dma.num_ents; i++) {
+		len = sg_dma_len(sg);
+		offset = 0;
+
+		do {
+			/* Check if we can do DMA */
+			if (!len || (box_cmd_cnt >= MMC_MAX_DMA_CMDS)) {
+				err = -ENOTSUPP;
+				goto unmap;
+			}
+
+			box->cmd = CMD_MODE_BOX;
+
+			if (len >= MMC_MAX_DMA_BOX_LENGTH) {
+				len = MMC_MAX_DMA_BOX_LENGTH;
+				len -= len % data->blksz;
+			}
+			rows = (len % MCI_FIFOSIZE) ?
+				(len / MCI_FIFOSIZE) + 1 :
+				(len / MCI_FIFOSIZE);
+
+			if (data->flags & MMC_DATA_READ) {
+				box->src_row_addr = msmsdcc_fifo_addr(host);
+				box->dst_row_addr = sg_dma_address(sg) + offset;
+				box->src_dst_len = (MCI_FIFOSIZE << 16) |
+						(MCI_FIFOSIZE);
+				box->row_offset = MCI_FIFOSIZE;
+				box->num_rows = rows * ((1 << 16) + 1);
+				box->cmd |= CMD_SRC_CRCI(host->dma.crci);
+			} else {
+				box->src_row_addr = sg_dma_address(sg) + offset;
+				box->dst_row_addr = msmsdcc_fifo_addr(host);
+				box->src_dst_len = (MCI_FIFOSIZE << 16) |
+						(MCI_FIFOSIZE);
+				box->row_offset = (MCI_FIFOSIZE << 16);
+				box->num_rows = rows * ((1 << 16) + 1);
+				box->cmd |= CMD_DST_CRCI(host->dma.crci);
+			}
+
+			offset += len;
+			len = sg_dma_len(sg) - offset;
+			box++;
+			box_cmd_cnt++;
+		} while (len);
+		sg++;
+	}
+	/* Mark last command */
+	box--;
+	box->cmd |= CMD_LC;
 
 	/* location of command block must be 64 bit aligned */
 	BUG_ON(host->dma.cmd_busaddr & 0x07);
@@ -386,67 +894,156 @@
 			       DMOV_CMD_ADDR(host->dma.cmdptr_busaddr);
 	host->dma.hdr.complete_func = msmsdcc_dma_complete_func;
 
-	n = dma_map_sg(mmc_dev(host->mmc), host->dma.sg,
-			host->dma.num_ents, host->dma.dir);
-	if (n == 0) {
-		pr_err("%s: Unable to map in all sg elements\n",
-			mmc_hostname(host->mmc));
-		host->dma.sg = NULL;
-		host->dma.num_ents = 0;
-		return -ENOMEM;
+	/* Flush all data to memory before starting dma */
+	mb();
+
+unmap:
+	if (err) {
+		if (!data->host_cookie)
+			dma_unmap_sg(mmc_dev(host->mmc), host->dma.sg,
+				     host->dma.num_ents, host->dma.dir);
+		pr_err("%s: cannot do DMA, fall back to PIO mode err=%d\n",
+				mmc_hostname(host->mmc), err);
 	}
 
-	for_each_sg(host->dma.sg, sg, n, i) {
-
-		box->cmd = CMD_MODE_BOX;
-
-		if (i == n - 1)
-			box->cmd |= CMD_LC;
-		rows = (sg_dma_len(sg) % MCI_FIFOSIZE) ?
-			(sg_dma_len(sg) / MCI_FIFOSIZE) + 1 :
-			(sg_dma_len(sg) / MCI_FIFOSIZE) ;
-
-		if (data->flags & MMC_DATA_READ) {
-			box->src_row_addr = msmsdcc_fifo_addr(host);
-			box->dst_row_addr = sg_dma_address(sg);
-
-			box->src_dst_len = (MCI_FIFOSIZE << 16) |
-					   (MCI_FIFOSIZE);
-			box->row_offset = MCI_FIFOSIZE;
-
-			box->num_rows = rows * ((1 << 16) + 1);
-			box->cmd |= CMD_SRC_CRCI(crci);
-		} else {
-			box->src_row_addr = sg_dma_address(sg);
-			box->dst_row_addr = msmsdcc_fifo_addr(host);
-
-			box->src_dst_len = (MCI_FIFOSIZE << 16) |
-					   (MCI_FIFOSIZE);
-			box->row_offset = (MCI_FIFOSIZE << 16);
-
-			box->num_rows = rows * ((1 << 16) + 1);
-			box->cmd |= CMD_DST_CRCI(crci);
-		}
-		box++;
-	}
-
-	return 0;
+	return err;
 }
 
-static int
-snoop_cccr_abort(struct mmc_command *cmd)
+static int msmsdcc_prep_xfer(struct msmsdcc_host *host,
+			     struct mmc_data *data)
 {
-	if ((cmd->opcode == 52) &&
-	    (cmd->arg & 0x80000000) &&
-	    (((cmd->arg >> 9) & 0x1ffff) == SDIO_CCCR_ABORT))
-		return 1;
-	return 0;
+	int rc = 0;
+	unsigned int dir;
+
+	/* Prevent memory corruption */
+	BUG_ON(data->sg_len > msmsdcc_get_nr_sg(host));
+
+	if (data->flags & MMC_DATA_READ)
+		dir = DMA_FROM_DEVICE;
+	else
+		dir = DMA_TO_DEVICE;
+
+	/* Make sg buffers DMA ready */
+	rc = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+			dir);
+
+	if (unlikely(rc != data->sg_len)) {
+		pr_err("%s: Unable to map in all sg elements, rc=%d\n",
+		       mmc_hostname(host->mmc), rc);
+		rc = -ENOMEM;
+		goto dma_map_err;
+	}
+
+	pr_debug("%s: %s: %s: sg_len=%d\n",
+		mmc_hostname(host->mmc), __func__,
+		dir == DMA_FROM_DEVICE ? "READ" : "WRITE",
+		data->sg_len);
+
+	goto out;
+
+dma_map_err:
+	dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+		     data->flags);
+out:
+	return rc;
 }
+#ifdef CONFIG_MMC_MSM_SPS_SUPPORT
+/**
+ * Submits data transfer request to SPS driver
+ *
+ * This function make sg (scatter gather) data buffers
+ * DMA ready and then submits them to SPS driver for
+ * transfer.
+ *
+ * @host - Pointer to sdcc host structure
+ * @data - Pointer to mmc_data structure
+ *
+ * @return 0 if success else negative value
+ */
+static int msmsdcc_sps_start_xfer(struct msmsdcc_host *host,
+				  struct mmc_data *data)
+{
+	int rc = 0;
+	u32 flags;
+	int i;
+	u32 addr, len, data_cnt;
+	struct scatterlist *sg = data->sg;
+	struct sps_pipe *sps_pipe_handle;
+
+	host->sps.sg = data->sg;
+	host->sps.num_ents = data->sg_len;
+	host->sps.xfer_req_cnt = 0;
+	if (data->flags & MMC_DATA_READ) {
+		host->sps.dir = DMA_FROM_DEVICE;
+		sps_pipe_handle = host->sps.prod.pipe_handle;
+	} else {
+		host->sps.dir = DMA_TO_DEVICE;
+		sps_pipe_handle = host->sps.cons.pipe_handle;
+	}
+
+	if (!data->host_cookie) {
+		rc = msmsdcc_prep_xfer(host, data);
+		if (unlikely(rc < 0)) {
+			host->dma.sg = NULL;
+			host->dma.num_ents = 0;
+			goto out;
+		}
+	}
+
+	for (i = 0; i < data->sg_len; i++) {
+		/*
+		 * Check if this is the last buffer to transfer?
+		 * If yes then set the INT and EOT flags.
+		 */
+		len = sg_dma_len(sg);
+		addr = sg_dma_address(sg);
+		flags = 0;
+		while (len > 0) {
+			if (len > SPS_MAX_DESC_SIZE) {
+				data_cnt = SPS_MAX_DESC_SIZE;
+			} else {
+				data_cnt = len;
+				if (i == data->sg_len - 1)
+					flags = SPS_IOVEC_FLAG_INT |
+						SPS_IOVEC_FLAG_EOT;
+			}
+			rc = sps_transfer_one(sps_pipe_handle, addr,
+						data_cnt, host, flags);
+			if (rc) {
+				pr_err("%s: sps_transfer_one() error! rc=%d,"
+					" pipe=0x%x, sg=0x%x, sg_buf_no=%d\n",
+					mmc_hostname(host->mmc), rc,
+					(u32)sps_pipe_handle, (u32)sg, i);
+				goto dma_map_err;
+			}
+			addr += data_cnt;
+			len -= data_cnt;
+			host->sps.xfer_req_cnt++;
+		}
+		sg++;
+	}
+	goto out;
+
+dma_map_err:
+	/* unmap sg buffers */
+	if (!data->host_cookie)
+		dma_unmap_sg(mmc_dev(host->mmc), host->sps.sg,
+			     host->sps.num_ents, host->sps.dir);
+out:
+	return rc;
+}
+#else
+static int msmsdcc_sps_start_xfer(struct msmsdcc_host *host,
+				struct mmc_data *data) { return 0; }
+#endif /* CONFIG_MMC_MSM_SPS_SUPPORT */
 
 static void
 msmsdcc_start_command_deferred(struct msmsdcc_host *host,
 				struct mmc_command *cmd, u32 *c)
 {
+	DBG(host, "op %02x arg %08x flags %08x\n",
+	    cmd->opcode, cmd->arg, cmd->flags);
+
 	*c |= (cmd->opcode | MCI_CPSM_ENABLE);
 
 	if (cmd->flags & MMC_RSP_PRESENT) {
@@ -458,25 +1055,48 @@
 	if (/*interrupt*/0)
 		*c |= MCI_CPSM_INTERRUPT;
 
-	if ((((cmd->opcode == 17) || (cmd->opcode == 18))  ||
-	     ((cmd->opcode == 24) || (cmd->opcode == 25))) ||
-	      (cmd->opcode == 53))
+	/* DAT_CMD bit should be set for all ADTC */
+	if (mmc_cmd_type(cmd) == MMC_CMD_ADTC)
 		*c |= MCI_CSPM_DATCMD;
 
-	if (host->prog_scan && (cmd->opcode == 12)) {
+	/* Check if AUTO CMD19 is required or not? */
+	if (host->tuning_needed &&
+		!(host->mmc->ios.timing == MMC_TIMING_MMC_HS200)) {
+
+		/*
+		 * For open ended block read operation (without CMD23),
+		 * AUTO_CMD19 bit should be set while sending the READ command.
+		 * For close ended block read operation (with CMD23),
+		 * AUTO_CMD19 bit should be set while sending CMD23.
+		 */
+		if ((cmd->opcode == MMC_SET_BLOCK_COUNT &&
+			host->curr.mrq->cmd->opcode ==
+				MMC_READ_MULTIPLE_BLOCK) ||
+			(!host->curr.mrq->sbc &&
+			(cmd->opcode == MMC_READ_SINGLE_BLOCK ||
+			cmd->opcode == MMC_READ_MULTIPLE_BLOCK))) {
+			msmsdcc_enable_cdr_cm_sdc4_dll(host);
+			*c |= MCI_CSPM_AUTO_CMD19;
+		}
+	}
+
+	/* Clear CDR_EN bit for write operations */
+	if (host->tuning_needed && cmd->mrq->data &&
+			(cmd->mrq->data->flags & MMC_DATA_WRITE))
+		writel_relaxed((readl_relaxed(host->base + MCI_DLL_CONFIG) &
+				~MCI_CDR_EN), host->base + MCI_DLL_CONFIG);
+
+	if ((cmd->flags & MMC_RSP_R1B) == MMC_RSP_R1B) {
 		*c |= MCI_CPSM_PROGENA;
-		host->prog_enable = true;
+		host->prog_enable = 1;
 	}
 
 	if (cmd == cmd->mrq->stop)
 		*c |= MCI_CSPM_MCIABORT;
 
-	if (snoop_cccr_abort(cmd))
-		*c |= MCI_CSPM_MCIABORT;
-
 	if (host->curr.cmd != NULL) {
 		pr_err("%s: Overlapping command requests\n",
-			mmc_hostname(host->mmc));
+		       mmc_hostname(host->mmc));
 	}
 	host->curr.cmd = cmd;
 }
@@ -485,73 +1105,120 @@
 msmsdcc_start_data(struct msmsdcc_host *host, struct mmc_data *data,
 			struct mmc_command *cmd, u32 c)
 {
-	unsigned int datactrl, timeout;
+	unsigned int datactrl = 0, timeout;
 	unsigned long long clks;
+	void __iomem *base = host->base;
 	unsigned int pio_irqmask = 0;
 
+	BUG_ON(!data->sg);
+	BUG_ON(!data->sg_len);
+
 	host->curr.data = data;
 	host->curr.xfer_size = data->blksz * data->blocks;
 	host->curr.xfer_remain = host->curr.xfer_size;
 	host->curr.data_xfered = 0;
 	host->curr.got_dataend = 0;
-
-	memset(&host->pio, 0, sizeof(host->pio));
+	host->curr.got_auto_prog_done = 0;
 
 	datactrl = MCI_DPSM_ENABLE | (data->blksz << 4);
 
-	if (!msmsdcc_config_dma(host, data))
-		datactrl |= MCI_DPSM_DMAENABLE;
-	else {
-		host->pio.sg = data->sg;
-		host->pio.sg_len = data->sg_len;
-		host->pio.sg_off = 0;
+	if (host->curr.wait_for_auto_prog_done)
+		datactrl |= MCI_AUTO_PROG_DONE;
 
+	if (msmsdcc_is_dma_possible(host, data)) {
+		if (host->is_dma_mode && !msmsdcc_config_dma(host, data)) {
+			datactrl |= MCI_DPSM_DMAENABLE;
+		} else if (host->is_sps_mode) {
+			if (!msmsdcc_is_dml_busy(host)) {
+				if (!msmsdcc_sps_start_xfer(host, data)) {
+					/* Now kick start DML transfer */
+					mb();
+					msmsdcc_dml_start_xfer(host, data);
+					datactrl |= MCI_DPSM_DMAENABLE;
+					host->sps.busy = 1;
+				}
+			} else {
+				/*
+				 * Can't proceed with new transfer as
+				 * previous trasnfer is already in progress.
+				 * There is no point of going into PIO mode
+				 * as well. Is this a time to do kernel panic?
+				 */
+				pr_err("%s: %s: DML HW is busy!!!"
+					" Can't perform new SPS transfers"
+					" now\n", mmc_hostname(host->mmc),
+					__func__);
+			}
+		}
+	}
+
+	/* Is data transfer in PIO mode required? */
+	if (!(datactrl & MCI_DPSM_DMAENABLE)) {
 		if (data->flags & MMC_DATA_READ) {
 			pio_irqmask = MCI_RXFIFOHALFFULLMASK;
 			if (host->curr.xfer_remain < MCI_FIFOSIZE)
 				pio_irqmask |= MCI_RXDATAAVLBLMASK;
 		} else
-			pio_irqmask = MCI_TXFIFOHALFEMPTYMASK;
+			pio_irqmask = MCI_TXFIFOHALFEMPTYMASK |
+					MCI_TXFIFOEMPTYMASK;
+
+		msmsdcc_sg_start(host);
 	}
 
 	if (data->flags & MMC_DATA_READ)
-		datactrl |= MCI_DPSM_DIRECTION;
+		datactrl |= (MCI_DPSM_DIRECTION | MCI_RX_DATA_PEND);
+	else if (host->curr.use_wr_data_pend)
+		datactrl |= MCI_DATA_PEND;
 
 	clks = (unsigned long long)data->timeout_ns * host->clk_rate;
-	do_div(clks, NSEC_PER_SEC);
+	do_div(clks, 1000000000UL);
 	timeout = data->timeout_clks + (unsigned int)clks*2 ;
 
-	if (datactrl & MCI_DPSM_DMAENABLE) {
-		/* Save parameters for the exec function */
+	if (host->is_dma_mode && (datactrl & MCI_DPSM_DMAENABLE)) {
+		/* Use ADM (Application Data Mover) HW for Data transfer */
+		/* Save parameters for the dma exec function */
 		host->cmd_timeout = timeout;
 		host->cmd_pio_irqmask = pio_irqmask;
 		host->cmd_datactrl = datactrl;
 		host->cmd_cmd = cmd;
 
-		host->dma.hdr.execute_func = msmsdcc_dma_exec_func;
-		host->dma.hdr.data = (void *)host;
+		host->dma.hdr.exec_func = msmsdcc_dma_exec_func;
+		host->dma.hdr.user = (void *)host;
 		host->dma.busy = 1;
 
 		if (cmd) {
 			msmsdcc_start_command_deferred(host, cmd, &c);
 			host->cmd_c = c;
 		}
-		msm_dmov_enqueue_cmd(host->dma.channel, &host->dma.hdr);
-		if (data->flags & MMC_DATA_WRITE)
-			host->prog_scan = true;
+		writel_relaxed((readl_relaxed(host->base + MMCIMASK0) &
+				(~(MCI_IRQ_PIO))) | host->cmd_pio_irqmask,
+				host->base + MMCIMASK0);
+		mb();
+		msm_dmov_enqueue_cmd_ext(host->dma.channel, &host->dma.hdr);
 	} else {
-		msmsdcc_writel(host, timeout, MMCIDATATIMER);
+		/* SPS-BAM mode or PIO mode */
+		writel_relaxed(timeout, base + MMCIDATATIMER);
 
-		msmsdcc_writel(host, host->curr.xfer_size, MMCIDATALENGTH);
+		writel_relaxed(host->curr.xfer_size, base + MMCIDATALENGTH);
 
-		msmsdcc_writel(host, (msmsdcc_readl(host, MMCIMASK0) &
-				(~MCI_IRQ_PIO)) | pio_irqmask, MMCIMASK0);
-
-		msmsdcc_writel(host, datactrl, MMCIDATACTRL);
+		writel_relaxed((readl_relaxed(host->base + MMCIMASK0) &
+				(~(MCI_IRQ_PIO))) | pio_irqmask,
+				host->base + MMCIMASK0);
+		writel_relaxed(datactrl, base + MMCIDATACTRL);
 
 		if (cmd) {
+			/* Delay between data/command */
+			msmsdcc_sync_reg_wr(host);
 			/* Daisy-chain the command if requested */
 			msmsdcc_start_command(host, cmd, c);
+		} else {
+			/*
+			 * We don't need delay after writing to DATA_CTRL
+			 * register if we are not writing to CMD register
+			 * immediately after this. As we already have delay
+			 * before sending the command, we just need mb() here.
+			 */
+			mb();
 		}
 	}
 }
@@ -559,11 +1226,6 @@
 static void
 msmsdcc_start_command(struct msmsdcc_host *host, struct mmc_command *cmd, u32 c)
 {
-	if (cmd == cmd->mrq->stop)
-		c |= MCI_CSPM_MCIABORT;
-
-	host->stats.cmds++;
-
 	msmsdcc_start_command_deferred(host, cmd, &c);
 	msmsdcc_start_command_exec(host, cmd->arg, c);
 }
@@ -573,15 +1235,32 @@
 		 unsigned int status)
 {
 	if (status & MCI_DATACRCFAIL) {
-		pr_err("%s: Data CRC error\n", mmc_hostname(host->mmc));
-		pr_err("%s: opcode 0x%.8x\n", __func__,
-		       data->mrq->cmd->opcode);
-		pr_err("%s: blksz %d, blocks %d\n", __func__,
-		       data->blksz, data->blocks);
-		data->error = -EILSEQ;
+		if (!(data->mrq->cmd->opcode == MMC_BUS_TEST_W
+			|| data->mrq->cmd->opcode == MMC_BUS_TEST_R
+			|| data->mrq->cmd->opcode ==
+				MMC_SEND_TUNING_BLOCK_HS200)) {
+			pr_err("%s: Data CRC error\n",
+			       mmc_hostname(host->mmc));
+			pr_err("%s: opcode 0x%.8x\n", __func__,
+			       data->mrq->cmd->opcode);
+			pr_err("%s: blksz %d, blocks %d\n", __func__,
+			       data->blksz, data->blocks);
+			data->error = -EILSEQ;
+		}
 	} else if (status & MCI_DATATIMEOUT) {
-		pr_err("%s: Data timeout\n", mmc_hostname(host->mmc));
-		data->error = -ETIMEDOUT;
+		/* CRC is optional for the bus test commands, not all
+		 * cards respond back with CRC. However controller
+		 * waits for the CRC and times out. Hence ignore the
+		 * data timeouts during the Bustest.
+		 */
+		if (!(data->mrq->cmd->opcode == MMC_BUS_TEST_W
+			|| data->mrq->cmd->opcode == MMC_BUS_TEST_R)) {
+			pr_err("%s: CMD%d: Data timeout\n",
+				 mmc_hostname(host->mmc),
+				 data->mrq->cmd->opcode);
+			data->error = -ETIMEDOUT;
+			msmsdcc_dump_sdcc_state(host);
+		}
 	} else if (status & MCI_RXOVERRUN) {
 		pr_err("%s: RX overrun\n", mmc_hostname(host->mmc));
 		data->error = -EIO;
@@ -590,23 +1269,28 @@
 		data->error = -EIO;
 	} else {
 		pr_err("%s: Unknown error (0x%.8x)\n",
-		       mmc_hostname(host->mmc), status);
+		      mmc_hostname(host->mmc), status);
 		data->error = -EIO;
 	}
-}
 
+	/* Dummy CMD52 is not needed when CMD53 has errors */
+	if (host->dummy_52_needed)
+		host->dummy_52_needed = 0;
+}
 
 static int
 msmsdcc_pio_read(struct msmsdcc_host *host, char *buffer, unsigned int remain)
 {
+	void __iomem	*base = host->base;
 	uint32_t	*ptr = (uint32_t *) buffer;
 	int		count = 0;
 
 	if (remain % 4)
 		remain = ((remain >> 2) + 1) << 2;
 
-	while (msmsdcc_readl(host, MMCISTATUS) & MCI_RXDATAAVLBL) {
-		*ptr = msmsdcc_readl(host, MMCIFIFO + (count % MCI_FIFOSIZE));
+	while (readl_relaxed(base + MMCISTATUS) & MCI_RXDATAAVLBL) {
+
+		*ptr = readl_relaxed(base + MMCIFIFO + (count % MCI_FIFOSIZE));
 		ptr++;
 		count += sizeof(uint32_t);
 
@@ -619,16 +1303,16 @@
 
 static int
 msmsdcc_pio_write(struct msmsdcc_host *host, char *buffer,
-		  unsigned int remain, u32 status)
+		  unsigned int remain)
 {
 	void __iomem *base = host->base;
 	char *ptr = buffer;
+	unsigned int maxcnt = MCI_FIFOHALFSIZE;
 
-	do {
-		unsigned int count, maxcnt, sz;
+	while (readl_relaxed(base + MMCISTATUS) &
+		(MCI_TXFIFOEMPTY | MCI_TXFIFOHALFEMPTY)) {
+		unsigned int count, sz;
 
-		maxcnt = status & MCI_TXFIFOEMPTY ? MCI_FIFOSIZE :
-						    MCI_FIFOHALFSIZE;
 		count = min(remain, maxcnt);
 
 		sz = count % 4 ? (count >> 2) + 1 : (count >> 2);
@@ -638,127 +1322,315 @@
 
 		if (remain == 0)
 			break;
-
-		status = msmsdcc_readl(host, MMCISTATUS);
-	} while (status & MCI_TXFIFOHALFEMPTY);
+	}
+	mb();
 
 	return ptr - buffer;
 }
 
-static int
-msmsdcc_spin_on_status(struct msmsdcc_host *host, uint32_t mask, int maxspin)
+/*
+ * Copy up to a word (4 bytes) between a scatterlist
+ * and a temporary bounce buffer when the word lies across
+ * two pages. The temporary buffer can then be read to/
+ * written from the FIFO once.
+ */
+static void _msmsdcc_sg_consume_word(struct msmsdcc_host *host)
 {
-	while (maxspin) {
-		if ((msmsdcc_readl(host, MMCISTATUS) & mask))
-			return 0;
-		udelay(1);
-		--maxspin;
+	struct msmsdcc_pio_data *pio = &host->pio;
+	unsigned int bytes_avail;
+
+	if (host->curr.data->flags & MMC_DATA_READ)
+		memcpy(pio->sg_miter.addr, pio->bounce_buf,
+		       pio->bounce_buf_len);
+	else
+		memcpy(pio->bounce_buf, pio->sg_miter.addr,
+		       pio->bounce_buf_len);
+
+	while (pio->bounce_buf_len != 4) {
+		if (!sg_miter_next(&pio->sg_miter))
+			break;
+		bytes_avail = min_t(unsigned int, pio->sg_miter.length,
+			4 - pio->bounce_buf_len);
+		if (host->curr.data->flags & MMC_DATA_READ)
+			memcpy(pio->sg_miter.addr,
+			       &pio->bounce_buf[pio->bounce_buf_len],
+			       bytes_avail);
+		else
+			memcpy(&pio->bounce_buf[pio->bounce_buf_len],
+			       pio->sg_miter.addr, bytes_avail);
+
+		pio->sg_miter.consumed = bytes_avail;
+		pio->bounce_buf_len += bytes_avail;
 	}
-	return -ETIMEDOUT;
+}
+
+/*
+ * Use sg_miter_next to return as many 4-byte aligned
+ * chunks as possible, using a temporary 4 byte buffer
+ * for alignment if necessary
+ */
+static int msmsdcc_sg_next(struct msmsdcc_host *host, char **buf, int *len)
+{
+	struct msmsdcc_pio_data *pio = &host->pio;
+	unsigned int length, rlength;
+	char *buffer;
+
+	if (!sg_miter_next(&pio->sg_miter))
+		return 0;
+
+	buffer = pio->sg_miter.addr;
+	length = pio->sg_miter.length;
+
+	if (length < host->curr.xfer_remain) {
+		rlength = round_down(length, 4);
+		if (rlength) {
+			/*
+			 * We have a 4-byte aligned chunk.
+			 * The rounding will be reflected by
+			 * a call to msmsdcc_sg_consumed
+			 */
+			length = rlength;
+			goto sg_next_end;
+		}
+		/*
+		 * We have a length less than 4 bytes. Check to
+		 * see if more buffer is available, and combine
+		 * to make 4 bytes if possible.
+		 */
+		pio->bounce_buf_len = length;
+		memset(pio->bounce_buf, 0, 4);
+
+		/*
+		 * On a read, get 4 bytes from FIFO, and distribute
+		 * (4-bouce_buf_len) bytes into consecutive
+		 * sgl buffers when msmsdcc_sg_consumed is called
+		 */
+		if (host->curr.data->flags & MMC_DATA_READ) {
+			buffer = pio->bounce_buf;
+			length = 4;
+			goto sg_next_end;
+		} else {
+			_msmsdcc_sg_consume_word(host);
+			buffer = pio->bounce_buf;
+			length = pio->bounce_buf_len;
+		}
+	}
+
+sg_next_end:
+	*buf = buffer;
+	*len = length;
+	return 1;
+}
+
+/*
+ * Update sg_miter.consumed based on how many bytes were
+ * consumed. If the bounce buffer was used to read from FIFO,
+ * redistribute into sgls.
+ */
+static void msmsdcc_sg_consumed(struct msmsdcc_host *host,
+				unsigned int length)
+{
+	struct msmsdcc_pio_data *pio = &host->pio;
+
+	if (host->curr.data->flags & MMC_DATA_READ) {
+		if (length > pio->sg_miter.consumed)
+			/*
+			 * consumed 4 bytes, but sgl
+			 * describes < 4 bytes
+			 */
+			_msmsdcc_sg_consume_word(host);
+		else
+			pio->sg_miter.consumed = length;
+	} else
+		if (length < pio->sg_miter.consumed)
+			pio->sg_miter.consumed = length;
+}
+
+static void msmsdcc_sg_start(struct msmsdcc_host *host)
+{
+	unsigned int sg_miter_flags = SG_MITER_ATOMIC;
+
+	host->pio.bounce_buf_len = 0;
+
+	if (host->curr.data->flags & MMC_DATA_READ)
+		sg_miter_flags |= SG_MITER_TO_SG;
+	else
+		sg_miter_flags |= SG_MITER_FROM_SG;
+
+	sg_miter_start(&host->pio.sg_miter, host->curr.data->sg,
+		       host->curr.data->sg_len, sg_miter_flags);
+}
+
+static void msmsdcc_sg_stop(struct msmsdcc_host *host)
+{
+	sg_miter_stop(&host->pio.sg_miter);
 }
 
 static irqreturn_t
 msmsdcc_pio_irq(int irq, void *dev_id)
 {
 	struct msmsdcc_host	*host = dev_id;
+	void __iomem		*base = host->base;
 	uint32_t		status;
-	u32 mci_mask0;
+	unsigned long flags;
+	unsigned int remain;
+	char *buffer;
 
-	status = msmsdcc_readl(host, MMCISTATUS);
-	mci_mask0 = msmsdcc_readl(host, MMCIMASK0);
+	spin_lock(&host->lock);
 
-	if (((mci_mask0 & status) & MCI_IRQ_PIO) == 0)
+	status = readl_relaxed(base + MMCISTATUS);
+
+	if (((readl_relaxed(host->base + MMCIMASK0) & status) &
+				(MCI_IRQ_PIO)) == 0) {
+		spin_unlock(&host->lock);
 		return IRQ_NONE;
+	}
+#if IRQ_DEBUG
+	msmsdcc_print_status(host, "irq1-r", status);
+#endif
+	local_irq_save(flags);
 
 	do {
-		unsigned long flags;
-		unsigned int remain, len;
-		char *buffer;
+		unsigned int len;
 
-		if (!(status & (MCI_TXFIFOHALFEMPTY | MCI_RXDATAAVLBL))) {
-			if (host->curr.xfer_remain == 0 || !msmsdcc_piopoll)
-				break;
+		if (!(status & (MCI_TXFIFOHALFEMPTY | MCI_TXFIFOEMPTY
+				| MCI_RXDATAAVLBL)))
+			break;
 
-			if (msmsdcc_spin_on_status(host,
-						   (MCI_TXFIFOHALFEMPTY |
-						   MCI_RXDATAAVLBL),
-						   PIO_SPINMAX)) {
-				break;
-			}
-		}
+		if (!msmsdcc_sg_next(host, &buffer, &remain))
+			break;
 
-		/* Map the current scatter buffer */
-		local_irq_save(flags);
-		buffer = kmap_atomic(sg_page(host->pio.sg))
-				     + host->pio.sg->offset;
-		buffer += host->pio.sg_off;
-		remain = host->pio.sg->length - host->pio.sg_off;
 		len = 0;
 		if (status & MCI_RXACTIVE)
 			len = msmsdcc_pio_read(host, buffer, remain);
 		if (status & MCI_TXACTIVE)
-			len = msmsdcc_pio_write(host, buffer, remain, status);
+			len = msmsdcc_pio_write(host, buffer, remain);
 
-		/* Unmap the buffer */
-		kunmap_atomic(buffer);
-		local_irq_restore(flags);
+		/* len might have aligned to 32bits above */
+		if (len > remain)
+			len = remain;
 
-		host->pio.sg_off += len;
 		host->curr.xfer_remain -= len;
 		host->curr.data_xfered += len;
 		remain -= len;
+		msmsdcc_sg_consumed(host, len);
 
-		if (remain == 0) {
-			/* This sg page is full - do some housekeeping */
-			if (status & MCI_RXACTIVE && host->curr.user_pages)
-				flush_dcache_page(sg_page(host->pio.sg));
+		if (remain) /* Done with this page? */
+			break; /* Nope */
 
-			if (!--host->pio.sg_len) {
-				memset(&host->pio, 0, sizeof(host->pio));
-				break;
-			}
-
-			/* Advance to next sg */
-			host->pio.sg++;
-			host->pio.sg_off = 0;
-		}
-
-		status = msmsdcc_readl(host, MMCISTATUS);
+		status = readl_relaxed(base + MMCISTATUS);
 	} while (1);
 
-	if (status & MCI_RXACTIVE && host->curr.xfer_remain < MCI_FIFOSIZE)
-		msmsdcc_writel(host, (mci_mask0 & (~MCI_IRQ_PIO)) |
-					MCI_RXDATAAVLBLMASK, MMCIMASK0);
+	msmsdcc_sg_stop(host);
+	local_irq_restore(flags);
 
-	if (!host->curr.xfer_remain)
-		msmsdcc_writel(host, (mci_mask0 & (~MCI_IRQ_PIO)) | 0,
-					MMCIMASK0);
+	if (status & MCI_RXACTIVE && host->curr.xfer_remain < MCI_FIFOSIZE) {
+		writel_relaxed((readl_relaxed(host->base + MMCIMASK0) &
+				(~(MCI_IRQ_PIO))) | MCI_RXDATAAVLBLMASK,
+				host->base + MMCIMASK0);
+		if (!host->curr.xfer_remain) {
+			/*
+			 * back to back write to MASK0 register don't need
+			 * synchronization delay.
+			 */
+			writel_relaxed((readl_relaxed(host->base + MMCIMASK0) &
+				(~(MCI_IRQ_PIO))) | 0, host->base + MMCIMASK0);
+		}
+		mb();
+	} else if (!host->curr.xfer_remain) {
+		writel_relaxed((readl_relaxed(host->base + MMCIMASK0) &
+				(~(MCI_IRQ_PIO))) | 0, host->base + MMCIMASK0);
+		mb();
+	}
+
+	spin_unlock(&host->lock);
 
 	return IRQ_HANDLED;
 }
 
+static void
+msmsdcc_request_start(struct msmsdcc_host *host, struct mmc_request *mrq);
+
+static void msmsdcc_wait_for_rxdata(struct msmsdcc_host *host,
+					struct mmc_data *data)
+{
+	u32 loop_cnt = 0;
+
+	/*
+	 * For read commands with data less than fifo size, it is possible to
+	 * get DATAEND first and RXDATA_AVAIL might be set later because of
+	 * synchronization delay through the asynchronous RX FIFO. Thus, for
+	 * such cases, even after DATAEND interrupt is received software
+	 * should poll for RXDATA_AVAIL until the requested data is read out
+	 * of FIFO. This change is needed to get around this abnormal but
+	 * sometimes expected behavior of SDCC3 controller.
+	 *
+	 * We can expect RXDATAAVAIL bit to be set after 6HCLK clock cycles
+	 * after the data is loaded into RX FIFO. This would amount to less
+	 * than a microsecond and thus looping for 1000 times is good enough
+	 * for that delay.
+	 */
+	while (((int)host->curr.xfer_remain > 0) && (++loop_cnt < 1000)) {
+		if (readl_relaxed(host->base + MMCISTATUS) & MCI_RXDATAAVLBL) {
+			spin_unlock(&host->lock);
+			msmsdcc_pio_irq(1, host);
+			spin_lock(&host->lock);
+		}
+	}
+	if (loop_cnt == 1000) {
+		pr_info("%s: Timed out while polling for Rx Data\n",
+				mmc_hostname(host->mmc));
+		data->error = -ETIMEDOUT;
+		msmsdcc_reset_and_restore(host);
+	}
+}
+
 static void msmsdcc_do_cmdirq(struct msmsdcc_host *host, uint32_t status)
 {
 	struct mmc_command *cmd = host->curr.cmd;
 
 	host->curr.cmd = NULL;
-	cmd->resp[0] = msmsdcc_readl(host, MMCIRESPONSE0);
-	cmd->resp[1] = msmsdcc_readl(host, MMCIRESPONSE1);
-	cmd->resp[2] = msmsdcc_readl(host, MMCIRESPONSE2);
-	cmd->resp[3] = msmsdcc_readl(host, MMCIRESPONSE3);
+	if (mmc_resp_type(cmd))
+		cmd->resp[0] = readl_relaxed(host->base + MMCIRESPONSE0);
+	/*
+	 * Read rest of the response registers only if
+	 * long response is expected for this command
+	 */
+	if (mmc_resp_type(cmd) & MMC_RSP_136) {
+		cmd->resp[1] = readl_relaxed(host->base + MMCIRESPONSE1);
+		cmd->resp[2] = readl_relaxed(host->base + MMCIRESPONSE2);
+		cmd->resp[3] = readl_relaxed(host->base + MMCIRESPONSE3);
+	}
 
-	if (status & MCI_CMDTIMEOUT) {
+	if (status & (MCI_CMDTIMEOUT | MCI_AUTOCMD19TIMEOUT)) {
+		pr_debug("%s: CMD%d: Command timeout\n",
+				mmc_hostname(host->mmc), cmd->opcode);
 		cmd->error = -ETIMEDOUT;
-	} else if (status & MCI_CMDCRCFAIL &&
-		   cmd->flags & MMC_RSP_CRC) {
-		pr_err("%s: Command CRC error\n", mmc_hostname(host->mmc));
+	} else if ((status & MCI_CMDCRCFAIL && cmd->flags & MMC_RSP_CRC) &&
+			!host->tuning_in_progress) {
+		pr_err("%s: CMD%d: Command CRC error\n",
+			mmc_hostname(host->mmc), cmd->opcode);
+		msmsdcc_dump_sdcc_state(host);
 		cmd->error = -EILSEQ;
 	}
 
+	if (!cmd->error) {
+		if (cmd->cmd_timeout_ms > host->curr.req_tout_ms) {
+			host->curr.req_tout_ms = cmd->cmd_timeout_ms;
+			mod_timer(&host->req_tout_timer, (jiffies +
+				  msecs_to_jiffies(host->curr.req_tout_ms)));
+		}
+	}
+
 	if (!cmd->data || cmd->error) {
-		if (host->curr.data && host->dma.sg)
-			msm_dmov_stop_cmd(host->dma.channel,
-					  &host->dma.hdr, 0);
+		if (host->curr.data && host->dma.sg &&
+			host->is_dma_mode)
+			msm_dmov_flush(host->dma.channel, 0);
+		else if (host->curr.data && host->sps.sg &&
+			host->is_sps_mode){
+			/* Stop current SPS transfer */
+			msmsdcc_sps_exit_curr_xfer(host);
+		}
 		else if (host->curr.data) { /* Non DMA */
 			msmsdcc_reset_and_restore(host);
 			msmsdcc_stop_data(host);
@@ -766,87 +1638,26 @@
 		} else { /* host->data == NULL */
 			if (!cmd->error && host->prog_enable) {
 				if (status & MCI_PROGDONE) {
-					host->prog_scan = false;
-					host->prog_enable = false;
+					host->prog_enable = 0;
 					msmsdcc_request_end(host, cmd->mrq);
-				} else {
+				} else
 					host->curr.cmd = cmd;
-				}
 			} else {
-				if (host->prog_enable) {
-					host->prog_scan = false;
-					host->prog_enable = false;
-				}
+				host->prog_enable = 0;
+				host->curr.wait_for_auto_prog_done = 0;
+				if (host->dummy_52_needed)
+					host->dummy_52_needed = 0;
+				if (cmd->data && cmd->error)
+					msmsdcc_reset_and_restore(host);
 				msmsdcc_request_end(host, cmd->mrq);
 			}
 		}
-	} else if (cmd->data)
-		if (!(cmd->data->flags & MMC_DATA_READ))
-			msmsdcc_start_data(host, cmd->data,
-						NULL, 0);
-}
-
-static void
-msmsdcc_handle_irq_data(struct msmsdcc_host *host, u32 status,
-			void __iomem *base)
-{
-	struct mmc_data *data = host->curr.data;
-
-	if (status & (MCI_CMDSENT | MCI_CMDRESPEND | MCI_CMDCRCFAIL |
-			MCI_CMDTIMEOUT | MCI_PROGDONE) && host->curr.cmd) {
-		msmsdcc_do_cmdirq(host, status);
-	}
-
-	if (!data)
-		return;
-
-	/* Check for data errors */
-	if (status & (MCI_DATACRCFAIL | MCI_DATATIMEOUT |
-		      MCI_TXUNDERRUN | MCI_RXOVERRUN)) {
-		msmsdcc_data_err(host, data, status);
-		host->curr.data_xfered = 0;
-		if (host->dma.sg)
-			msm_dmov_stop_cmd(host->dma.channel,
-					  &host->dma.hdr, 0);
-		else {
-			msmsdcc_reset_and_restore(host);
-			if (host->curr.data)
-				msmsdcc_stop_data(host);
-			if (!data->stop)
-				msmsdcc_request_end(host, data->mrq);
-			else
-				msmsdcc_start_command(host, data->stop, 0);
-		}
-	}
-
-	/* Check for data done */
-	if (!host->curr.got_dataend && (status & MCI_DATAEND))
-		host->curr.got_dataend = 1;
-
-	/*
-	 * If DMA is still in progress, we complete via the completion handler
-	 */
-	if (host->curr.got_dataend && !host->dma.busy) {
-		/*
-		 * There appears to be an issue in the controller where
-		 * if you request a small block transfer (< fifo size),
-		 * you may get your DATAEND/DATABLKEND irq without the
-		 * PIO data irq.
-		 *
-		 * Check to see if there is still data to be read,
-		 * and simulate a PIO irq.
-		 */
-		if (readl(base + MMCISTATUS) & MCI_RXDATAAVLBL)
-			msmsdcc_pio_irq(1, host);
-
-		msmsdcc_stop_data(host);
-		if (!data->error)
-			host->curr.data_xfered = host->curr.xfer_size;
-
-		if (!data->stop)
-			msmsdcc_request_end(host, data->mrq);
-		else
-			msmsdcc_start_command(host, data->stop, 0);
+	} else if (cmd->data) {
+		if (cmd == host->curr.mrq->sbc)
+			msmsdcc_start_command(host, host->curr.mrq->cmd, 0);
+		else if ((cmd->data->flags & MMC_DATA_WRITE) &&
+			   !host->curr.use_wr_data_pend)
+			msmsdcc_start_data(host, cmd->data, NULL, 0);
 	}
 }
 
@@ -854,60 +1665,315 @@
 msmsdcc_irq(int irq, void *dev_id)
 {
 	struct msmsdcc_host	*host = dev_id;
-	void __iomem		*base = host->base;
 	u32			status;
 	int			ret = 0;
-	int			cardint = 0;
+	int			timer = 0;
 
 	spin_lock(&host->lock);
 
 	do {
-		status = msmsdcc_readl(host, MMCISTATUS);
-		status &= msmsdcc_readl(host, MMCIMASK0);
-		if ((status & (~MCI_IRQ_PIO)) == 0)
-			break;
-		msmsdcc_writel(host, status, MMCICLEAR);
+		struct mmc_command *cmd;
+		struct mmc_data *data;
 
-		if (status & MCI_SDIOINTR)
-			status &= ~MCI_SDIOINTR;
-
-		if (!status)
-			break;
-
-		msmsdcc_handle_irq_data(host, status, base);
-
-		if (status & MCI_SDIOINTOPER) {
-			cardint = 1;
-			status &= ~MCI_SDIOINTOPER;
+		if (timer) {
+			timer = 0;
+			msmsdcc_delay(host);
 		}
+
+		if (!host->clks_on) {
+			pr_debug("%s: %s: SDIO async irq received\n",
+					mmc_hostname(host->mmc), __func__);
+
+			/*
+			 * Only async interrupt can come when clocks are off,
+			 * disable further interrupts and enable them when
+			 * clocks are on.
+			 */
+			if (!host->sdcc_irq_disabled) {
+				disable_irq_nosync(irq);
+				host->sdcc_irq_disabled = 1;
+			}
+
+			/*
+			 * If mmc_card_wake_sdio_irq() is set, mmc core layer
+			 * will take care of signaling sdio irq during
+			 * mmc_sdio_resume().
+			 */
+			if (host->sdcc_suspended) {
+				/*
+				 * This is a wakeup interrupt so hold wakelock
+				 * until SDCC resume is handled.
+				 */
+				wake_lock(&host->sdio_wlock);
+			} else {
+				spin_unlock(&host->lock);
+				mmc_signal_sdio_irq(host->mmc);
+				spin_lock(&host->lock);
+			}
+			ret = 1;
+			break;
+		}
+
+		status = readl_relaxed(host->base + MMCISTATUS);
+
+		if (((readl_relaxed(host->base + MMCIMASK0) & status) &
+						(~(MCI_IRQ_PIO))) == 0)
+			break;
+
+#if IRQ_DEBUG
+		msmsdcc_print_status(host, "irq0-r", status);
+#endif
+		status &= readl_relaxed(host->base + MMCIMASK0);
+		writel_relaxed(status, host->base + MMCICLEAR);
+		/* Allow clear to take effect*/
+		if (host->clk_rate <=
+				msmsdcc_get_min_sup_clk_rate(host))
+			msmsdcc_sync_reg_wr(host);
+#if IRQ_DEBUG
+		msmsdcc_print_status(host, "irq0-p", status);
+#endif
+
+		if (status & MCI_SDIOINTROPE) {
+			if (host->sdcc_suspending)
+				wake_lock(&host->sdio_suspend_wlock);
+			spin_unlock(&host->lock);
+			mmc_signal_sdio_irq(host->mmc);
+			spin_lock(&host->lock);
+		}
+		data = host->curr.data;
+
+		if (host->dummy_52_sent) {
+			if (status & (MCI_PROGDONE | MCI_CMDCRCFAIL |
+					  MCI_CMDTIMEOUT)) {
+				if (status & MCI_CMDTIMEOUT)
+					pr_debug("%s: dummy CMD52 timeout\n",
+						mmc_hostname(host->mmc));
+				if (status & MCI_CMDCRCFAIL)
+					pr_debug("%s: dummy CMD52 CRC failed\n",
+						mmc_hostname(host->mmc));
+				host->dummy_52_sent = 0;
+				host->dummy_52_needed = 0;
+				if (data) {
+					msmsdcc_stop_data(host);
+					msmsdcc_request_end(host, data->mrq);
+				}
+				WARN(!data, "No data cmd for dummy CMD52\n");
+				spin_unlock(&host->lock);
+				return IRQ_HANDLED;
+			}
+			break;
+		}
+
+		/*
+		 * Check for proper command response
+		 */
+		cmd = host->curr.cmd;
+		if ((status & (MCI_CMDSENT | MCI_CMDRESPEND | MCI_CMDCRCFAIL |
+			MCI_CMDTIMEOUT | MCI_PROGDONE |
+			MCI_AUTOCMD19TIMEOUT)) && host->curr.cmd) {
+			msmsdcc_do_cmdirq(host, status);
+		}
+
+		if (host->curr.data) {
+			/* Check for data errors */
+			if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|
+				      MCI_TXUNDERRUN|MCI_RXOVERRUN)) {
+				msmsdcc_data_err(host, data, status);
+				host->curr.data_xfered = 0;
+				if (host->dma.sg && host->is_dma_mode)
+					msm_dmov_flush(host->dma.channel, 0);
+				else if (host->sps.sg && host->is_sps_mode) {
+					/* Stop current SPS transfer */
+					msmsdcc_sps_exit_curr_xfer(host);
+				} else {
+					msmsdcc_reset_and_restore(host);
+					if (host->curr.data)
+						msmsdcc_stop_data(host);
+					if (!data->stop || (host->curr.mrq->sbc
+						&& !data->error))
+						timer |=
+						 msmsdcc_request_end(host,
+								    data->mrq);
+					else if ((host->curr.mrq->sbc
+						&& data->error) ||
+						!host->curr.mrq->sbc) {
+						msmsdcc_start_command(host,
+								     data->stop,
+								     0);
+						timer = 1;
+					}
+				}
+			}
+
+			/* Check for prog done */
+			if (host->curr.wait_for_auto_prog_done &&
+				(status & MCI_PROGDONE))
+				host->curr.got_auto_prog_done = 1;
+
+			/* Check for data done */
+			if (!host->curr.got_dataend && (status & MCI_DATAEND))
+				host->curr.got_dataend = 1;
+
+			if (host->curr.got_dataend &&
+				(!host->curr.wait_for_auto_prog_done ||
+				(host->curr.wait_for_auto_prog_done &&
+				host->curr.got_auto_prog_done))) {
+				/*
+				 * If DMA is still in progress, we complete
+				 * via the completion handler
+				 */
+				if (!host->dma.busy && !host->sps.busy) {
+					/*
+					 * There appears to be an issue in the
+					 * controller where if you request a
+					 * small block transfer (< fifo size),
+					 * you may get your DATAEND/DATABLKEND
+					 * irq without the PIO data irq.
+					 *
+					 * Check to see if theres still data
+					 * to be read, and simulate a PIO irq.
+					 */
+					if (data->flags & MMC_DATA_READ)
+						msmsdcc_wait_for_rxdata(host,
+								data);
+					if (!data->error) {
+						host->curr.data_xfered =
+							host->curr.xfer_size;
+						host->curr.xfer_remain -=
+							host->curr.xfer_size;
+					}
+
+					if (!host->dummy_52_needed) {
+						msmsdcc_stop_data(host);
+						if (!data->stop ||
+							(host->curr.mrq->sbc
+							&& !data->error))
+							msmsdcc_request_end(
+								  host,
+								  data->mrq);
+						else if ((host->curr.mrq->sbc
+							&& data->error) ||
+							!host->curr.mrq->sbc) {
+							msmsdcc_start_command(
+								host,
+								data->stop, 0);
+							timer = 1;
+						}
+					} else {
+						host->dummy_52_sent = 1;
+						msmsdcc_start_command(host,
+							&dummy52cmd,
+							MCI_CPSM_PROGENA);
+					}
+				}
+			}
+		}
+
 		ret = 1;
 	} while (status);
 
 	spin_unlock(&host->lock);
 
-	/*
-	 * We have to delay handling the card interrupt as it calls
-	 * back into the driver.
-	 */
-	if (cardint)
-		mmc_signal_sdio_irq(host->mmc);
-
 	return IRQ_RETVAL(ret);
 }
 
 static void
+msmsdcc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
+		bool is_first_request)
+{
+	struct msmsdcc_host *host = mmc_priv(mmc);
+	struct mmc_data *data = mrq->data;
+	int rc = 0;
+
+	if (unlikely(!data)) {
+		pr_err("%s: %s cannot prepare null data\n", mmc_hostname(mmc),
+		       __func__);
+		return;
+	}
+	if (unlikely(data->host_cookie)) {
+		/* Very wrong */
+		data->host_cookie = 0;
+		pr_err("%s: %s Request reposted for prepare\n",
+		       mmc_hostname(mmc), __func__);
+		return;
+	}
+
+	if (!msmsdcc_is_dma_possible(host, data))
+		return;
+
+	rc = msmsdcc_prep_xfer(host, data);
+	if (unlikely(rc < 0)) {
+		data->host_cookie = 0;
+		return;
+	}
+
+	data->host_cookie = 1;
+}
+
+static void
+msmsdcc_post_req(struct mmc_host *mmc, struct mmc_request *mrq, int err)
+{
+	struct msmsdcc_host *host = mmc_priv(mmc);
+	unsigned int dir;
+	struct mmc_data *data = mrq->data;
+
+	if (unlikely(!data)) {
+		pr_err("%s: %s cannot cleanup null data\n", mmc_hostname(mmc),
+		       __func__);
+		return;
+	}
+	if (data->flags & MMC_DATA_READ)
+		dir = DMA_FROM_DEVICE;
+	else
+		dir = DMA_TO_DEVICE;
+
+	if (data->host_cookie)
+		dma_unmap_sg(mmc_dev(host->mmc), data->sg,
+			     data->sg_len, dir);
+
+	data->host_cookie = 0;
+}
+
+static void
+msmsdcc_request_start(struct msmsdcc_host *host, struct mmc_request *mrq)
+{
+	if (mrq->data) {
+		/* Queue/read data, daisy-chain command when data starts */
+		if ((mrq->data->flags & MMC_DATA_READ) ||
+		    host->curr.use_wr_data_pend)
+			msmsdcc_start_data(host, mrq->data,
+					   mrq->sbc ? mrq->sbc : mrq->cmd,
+					   0);
+		else
+			msmsdcc_start_command(host,
+					      mrq->sbc ? mrq->sbc : mrq->cmd,
+					      0);
+	} else {
+		msmsdcc_start_command(host, mrq->cmd, 0);
+	}
+}
+
+static void
 msmsdcc_request(struct mmc_host *mmc, struct mmc_request *mrq)
 {
 	struct msmsdcc_host *host = mmc_priv(mmc);
-	unsigned long flags;
+	unsigned long		flags;
 
-	WARN_ON(host->curr.mrq != NULL);
-	WARN_ON(host->pwr == 0);
+	/*
+	 * Get the SDIO AL client out of LPM.
+	 */
+	WARN(host->dummy_52_sent, "Dummy CMD52 in progress\n");
+	if (host->plat->is_sdio_al_client)
+		msmsdcc_sdio_al_lpm(mmc, false);
+
+	/* check if sps pipe reset is pending? */
+	if (host->is_sps_mode && host->sps.pipe_reset_pending) {
+		msmsdcc_sps_pipes_reset_and_restore(host);
+		host->sps.pipe_reset_pending = false;
+	}
 
 	spin_lock_irqsave(&host->lock, flags);
 
-	host->stats.reqs++;
-
 	if (host->eject) {
 		if (mrq->data && !(mrq->data->flags & MMC_DATA_READ)) {
 			mrq->cmd->error = 0;
@@ -921,42 +1987,475 @@
 		return;
 	}
 
-	msmsdcc_enable_clocks(host);
+	/*
+	 * Don't start the request if SDCC is not in proper state to handle it
+	 */
+	if (!host->pwr || !host->clks_on || host->sdcc_irq_disabled) {
+		WARN(1, "%s: %s: SDCC is in bad state. don't process"
+		     " new request (CMD%d)\n", mmc_hostname(host->mmc),
+		     __func__, mrq->cmd->opcode);
+		msmsdcc_dump_sdcc_state(host);
+		mrq->cmd->error = -EIO;
+		if (mrq->data) {
+			mrq->data->error = -EIO;
+			mrq->data->bytes_xfered = 0;
+		}
+		spin_unlock_irqrestore(&host->lock, flags);
+		mmc_request_done(mmc, mrq);
+		return;
+	}
+
+	WARN(host->curr.mrq, "%s: %s: New request (CMD%d) received while"
+	     " other request (CMD%d) is in progress\n",
+	     mmc_hostname(host->mmc), __func__,
+	     mrq->cmd->opcode, host->curr.mrq->cmd->opcode);
+
+	/*
+	 * Set timeout value to 10 secs (or more in case of buggy cards)
+	 */
+	if ((mmc->card) && (mmc->card->quirks & MMC_QUIRK_INAND_DATA_TIMEOUT))
+		host->curr.req_tout_ms = 20000;
+	else
+		host->curr.req_tout_ms = MSM_MMC_REQ_TIMEOUT;
+	/*
+	 * Kick the software request timeout timer here with the timeout
+	 * value identified above
+	 */
+	mod_timer(&host->req_tout_timer,
+			(jiffies +
+			 msecs_to_jiffies(host->curr.req_tout_ms)));
 
 	host->curr.mrq = mrq;
-
-	if (mrq->data && mrq->data->flags & MMC_DATA_READ)
-		/* Queue/read data, daisy-chain command when data starts */
-		msmsdcc_start_data(host, mrq->data, mrq->cmd, 0);
-	else
-		msmsdcc_start_command(host, mrq->cmd, 0);
-
-	if (host->cmdpoll && !msmsdcc_spin_on_status(host,
-				MCI_CMDRESPEND|MCI_CMDCRCFAIL|MCI_CMDTIMEOUT,
-				CMD_SPINMAX)) {
-		uint32_t status = msmsdcc_readl(host, MMCISTATUS);
-		msmsdcc_do_cmdirq(host, status);
-		msmsdcc_writel(host,
-			       MCI_CMDRESPEND | MCI_CMDCRCFAIL | MCI_CMDTIMEOUT,
-			       MMCICLEAR);
-		host->stats.cmdpoll_hits++;
-	} else {
-		host->stats.cmdpoll_misses++;
+	if (mrq->data && (mrq->data->flags & MMC_DATA_WRITE)) {
+		if (mrq->cmd->opcode == SD_IO_RW_EXTENDED ||
+			mrq->cmd->opcode == 54) {
+			if (!host->sdcc_version)
+				host->dummy_52_needed = 1;
+			else
+				/*
+				 * SDCCv4 supports AUTO_PROG_DONE bit for SDIO
+				 * write operations using CMD53 and CMD54.
+				 * Setting this bit with CMD53 would
+				 * automatically triggers PROG_DONE interrupt
+				 * without the need of sending dummy CMD52.
+				 */
+				host->curr.wait_for_auto_prog_done = 1;
+		} else if (mrq->cmd->opcode == MMC_WRITE_BLOCK &&
+				host->sdcc_version) {
+			host->curr.wait_for_auto_prog_done = 1;
+		}
+		if ((mrq->cmd->opcode == MMC_WRITE_BLOCK) ||
+		    (mrq->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK))
+			host->curr.use_wr_data_pend = true;
 	}
+
+	if (mrq->data && mrq->sbc) {
+		mrq->sbc->mrq = mrq;
+		mrq->sbc->data = mrq->data;
+		if (mrq->data->flags & MMC_DATA_WRITE)
+			host->curr.wait_for_auto_prog_done = 1;
+	}
+	msmsdcc_request_start(host, mrq);
+
 	spin_unlock_irqrestore(&host->lock, flags);
 }
 
-static void msmsdcc_setup_gpio(struct msmsdcc_host *host, bool enable)
+static inline int msmsdcc_vreg_set_voltage(struct msm_mmc_reg_data *vreg,
+					int min_uV, int max_uV)
+{
+	int rc = 0;
+
+	if (vreg->set_voltage_sup) {
+		rc = regulator_set_voltage(vreg->reg, min_uV, max_uV);
+		if (rc) {
+			pr_err("%s: regulator_set_voltage(%s) failed."
+				" min_uV=%d, max_uV=%d, rc=%d\n",
+				__func__, vreg->name, min_uV, max_uV, rc);
+		}
+	}
+
+	return rc;
+}
+
+static inline int msmsdcc_vreg_set_optimum_mode(struct msm_mmc_reg_data *vreg,
+						int uA_load)
+{
+	int rc = 0;
+
+	/* regulators that do not support regulator_set_voltage also
+	   do not support regulator_set_optimum_mode */
+	if (vreg->set_voltage_sup) {
+		rc = regulator_set_optimum_mode(vreg->reg, uA_load);
+		if (rc < 0)
+			pr_err("%s: regulator_set_optimum_mode(reg=%s, "
+				"uA_load=%d) failed. rc=%d\n", __func__,
+				vreg->name, uA_load, rc);
+		else
+			/* regulator_set_optimum_mode() can return non zero
+			 * value even for success case.
+			 */
+			rc = 0;
+	}
+
+	return rc;
+}
+
+static inline int msmsdcc_vreg_init_reg(struct msm_mmc_reg_data *vreg,
+				struct device *dev)
+{
+	int rc = 0;
+
+	/* check if regulator is already initialized? */
+	if (vreg->reg)
+		goto out;
+
+	/* Get the regulator handle */
+	vreg->reg = regulator_get(dev, vreg->name);
+	if (IS_ERR(vreg->reg)) {
+		rc = PTR_ERR(vreg->reg);
+		pr_err("%s: regulator_get(%s) failed. rc=%d\n",
+			__func__, vreg->name, rc);
+		goto out;
+	}
+
+	if (regulator_count_voltages(vreg->reg) > 0)
+		vreg->set_voltage_sup = 1;
+
+out:
+	return rc;
+}
+
+static inline void msmsdcc_vreg_deinit_reg(struct msm_mmc_reg_data *vreg)
+{
+	if (vreg->reg)
+		regulator_put(vreg->reg);
+}
+
+/* This init function should be called only once for each SDCC slot */
+static int msmsdcc_vreg_init(struct msmsdcc_host *host, bool is_init)
+{
+	int rc = 0;
+	struct msm_mmc_slot_reg_data *curr_slot;
+	struct msm_mmc_reg_data *curr_vdd_reg, *curr_vccq_reg, *curr_vddp_reg;
+	struct device *dev = mmc_dev(host->mmc);
+
+	curr_slot = host->plat->vreg_data;
+	if (!curr_slot)
+		goto out;
+
+	curr_vdd_reg = curr_slot->vdd_data;
+	curr_vccq_reg = curr_slot->vccq_data;
+	curr_vddp_reg = curr_slot->vddp_data;
+
+	if (is_init) {
+		/*
+		 * Get the regulator handle from voltage regulator framework
+		 * and then try to set the voltage level for the regulator
+		 */
+		if (curr_vdd_reg) {
+			rc = msmsdcc_vreg_init_reg(curr_vdd_reg, dev);
+			if (rc)
+				goto out;
+		}
+		if (curr_vccq_reg) {
+			rc = msmsdcc_vreg_init_reg(curr_vccq_reg, dev);
+			if (rc)
+				goto vdd_reg_deinit;
+		}
+		if (curr_vddp_reg) {
+			rc = msmsdcc_vreg_init_reg(curr_vddp_reg, dev);
+			if (rc)
+				goto vccq_reg_deinit;
+		}
+		rc = msmsdcc_vreg_reset(host);
+		if (rc)
+			pr_err("msmsdcc.%d vreg reset failed (%d)\n",
+			       host->pdev_id, rc);
+		goto out;
+	} else {
+		/* Deregister all regulators from regulator framework */
+		goto vddp_reg_deinit;
+	}
+vddp_reg_deinit:
+	if (curr_vddp_reg)
+		msmsdcc_vreg_deinit_reg(curr_vddp_reg);
+vccq_reg_deinit:
+	if (curr_vccq_reg)
+		msmsdcc_vreg_deinit_reg(curr_vccq_reg);
+vdd_reg_deinit:
+	if (curr_vdd_reg)
+		msmsdcc_vreg_deinit_reg(curr_vdd_reg);
+out:
+	return rc;
+}
+
+static int msmsdcc_vreg_enable(struct msm_mmc_reg_data *vreg)
+{
+	int rc = 0;
+
+	/* Put regulator in HPM (high power mode) */
+	rc = msmsdcc_vreg_set_optimum_mode(vreg, vreg->hpm_uA);
+	if (rc < 0)
+		goto out;
+
+	if (!vreg->is_enabled) {
+		/* Set voltage level */
+		rc = msmsdcc_vreg_set_voltage(vreg, vreg->high_vol_level,
+						vreg->high_vol_level);
+		if (rc)
+			goto out;
+
+		rc = regulator_enable(vreg->reg);
+		if (rc) {
+			pr_err("%s: regulator_enable(%s) failed. rc=%d\n",
+			__func__, vreg->name, rc);
+			goto out;
+		}
+		vreg->is_enabled = true;
+	}
+
+out:
+	return rc;
+}
+
+static int msmsdcc_vreg_disable(struct msm_mmc_reg_data *vreg)
+{
+	int rc = 0;
+
+	/* Never disable regulator marked as always_on */
+	if (vreg->is_enabled && !vreg->always_on) {
+		rc = regulator_disable(vreg->reg);
+		if (rc) {
+			pr_err("%s: regulator_disable(%s) failed. rc=%d\n",
+				__func__, vreg->name, rc);
+			goto out;
+		}
+		vreg->is_enabled = false;
+
+		rc = msmsdcc_vreg_set_optimum_mode(vreg, 0);
+		if (rc < 0)
+			goto out;
+
+		/* Set min. voltage level to 0 */
+		rc = msmsdcc_vreg_set_voltage(vreg, 0, vreg->high_vol_level);
+		if (rc)
+			goto out;
+	} else if (vreg->is_enabled && vreg->always_on && vreg->lpm_sup) {
+		/* Put always_on regulator in LPM (low power mode) */
+		rc = msmsdcc_vreg_set_optimum_mode(vreg, vreg->lpm_uA);
+		if (rc < 0)
+			goto out;
+	}
+out:
+	return rc;
+}
+
+static int msmsdcc_setup_vreg(struct msmsdcc_host *host, bool enable)
+{
+	int rc = 0, i;
+	struct msm_mmc_slot_reg_data *curr_slot;
+	struct msm_mmc_reg_data *curr_vdd_reg, *curr_vccq_reg, *curr_vddp_reg;
+	struct msm_mmc_reg_data *vreg_table[3];
+
+	curr_slot = host->plat->vreg_data;
+	if (!curr_slot)
+		goto out;
+
+	curr_vdd_reg = vreg_table[0] = curr_slot->vdd_data;
+	curr_vccq_reg = vreg_table[1] = curr_slot->vccq_data;
+	curr_vddp_reg = vreg_table[2] = curr_slot->vddp_data;
+
+	for (i = 0; i < ARRAY_SIZE(vreg_table); i++) {
+		if (vreg_table[i]) {
+			if (enable)
+				rc = msmsdcc_vreg_enable(vreg_table[i]);
+			else
+				rc = msmsdcc_vreg_disable(vreg_table[i]);
+			if (rc)
+				goto out;
+		}
+	}
+out:
+	return rc;
+}
+
+/*
+ * Reset vreg by ensuring it is off during probe. A call
+ * to enable vreg is needed to balance disable vreg
+ */
+static int msmsdcc_vreg_reset(struct msmsdcc_host *host)
+{
+	int rc;
+
+	rc = msmsdcc_setup_vreg(host, 1);
+	if (rc)
+		return rc;
+	rc = msmsdcc_setup_vreg(host, 0);
+	return rc;
+}
+
+static int msmsdcc_set_vddp_level(struct msmsdcc_host *host, int level)
+{
+	int rc = 0;
+
+	if (host->plat->vreg_data) {
+		struct msm_mmc_reg_data *vddp_reg =
+			host->plat->vreg_data->vddp_data;
+
+		if (vddp_reg && vddp_reg->is_enabled)
+			rc = msmsdcc_vreg_set_voltage(vddp_reg, level, level);
+	}
+
+	return rc;
+}
+
+static inline int msmsdcc_set_vddp_low_vol(struct msmsdcc_host *host)
+{
+	struct msm_mmc_slot_reg_data *curr_slot = host->plat->vreg_data;
+	int rc = 0;
+
+	if (curr_slot && curr_slot->vddp_data) {
+		rc = msmsdcc_set_vddp_level(host,
+			curr_slot->vddp_data->low_vol_level);
+
+		if (rc)
+			pr_err("%s: %s: failed to change vddp level to %d",
+				mmc_hostname(host->mmc), __func__,
+				curr_slot->vddp_data->low_vol_level);
+	}
+
+	return rc;
+}
+
+static inline int msmsdcc_set_vddp_high_vol(struct msmsdcc_host *host)
+{
+	struct msm_mmc_slot_reg_data *curr_slot = host->plat->vreg_data;
+	int rc = 0;
+
+	if (curr_slot && curr_slot->vddp_data) {
+		rc = msmsdcc_set_vddp_level(host,
+			curr_slot->vddp_data->high_vol_level);
+
+		if (rc)
+			pr_err("%s: %s: failed to change vddp level to %d",
+				mmc_hostname(host->mmc), __func__,
+				curr_slot->vddp_data->high_vol_level);
+	}
+
+	return rc;
+}
+
+static inline int msmsdcc_set_vccq_vol(struct msmsdcc_host *host, int level)
+{
+	struct msm_mmc_slot_reg_data *curr_slot = host->plat->vreg_data;
+	int rc = 0;
+
+	if (curr_slot && curr_slot->vccq_data) {
+		rc = msmsdcc_vreg_set_voltage(curr_slot->vccq_data,
+				level, level);
+		if (rc)
+			pr_err("%s: %s: failed to change vccq level to %d",
+				mmc_hostname(host->mmc), __func__, level);
+	}
+
+	return rc;
+}
+
+static inline int msmsdcc_is_pwrsave(struct msmsdcc_host *host)
+{
+	if (host->clk_rate > 400000 && msmsdcc_pwrsave)
+		return 1;
+	return 0;
+}
+
+/*
+ * Any function calling msmsdcc_setup_clocks must
+ * acquire clk_mutex. May sleep.
+ */
+static inline void msmsdcc_setup_clocks(struct msmsdcc_host *host, bool enable)
+{
+	if (enable) {
+		if (!IS_ERR_OR_NULL(host->dfab_pclk))
+			clk_prepare_enable(host->dfab_pclk);
+		if (!IS_ERR(host->pclk))
+			clk_prepare_enable(host->pclk);
+		clk_prepare_enable(host->clk);
+		mb();
+		msmsdcc_delay(host);
+	} else {
+		mb();
+		msmsdcc_delay(host);
+		clk_disable_unprepare(host->clk);
+		if (!IS_ERR(host->pclk))
+			clk_disable_unprepare(host->pclk);
+		if (!IS_ERR_OR_NULL(host->dfab_pclk))
+			clk_disable_unprepare(host->dfab_pclk);
+	}
+}
+
+static inline unsigned int msmsdcc_get_sup_clk_rate(struct msmsdcc_host *host,
+						unsigned int req_clk)
+{
+	unsigned int sel_clk = -1;
+
+	if (req_clk < msmsdcc_get_min_sup_clk_rate(host)) {
+		sel_clk = msmsdcc_get_min_sup_clk_rate(host);
+		goto out;
+	}
+
+	if (host->plat->sup_clk_table && host->plat->sup_clk_cnt) {
+		unsigned char cnt;
+
+		for (cnt = 0; cnt < host->plat->sup_clk_cnt; cnt++) {
+			if (host->plat->sup_clk_table[cnt] > req_clk)
+				break;
+			else if (host->plat->sup_clk_table[cnt] == req_clk) {
+				sel_clk = host->plat->sup_clk_table[cnt];
+				break;
+			} else
+				sel_clk = host->plat->sup_clk_table[cnt];
+		}
+	} else {
+		if ((req_clk < host->plat->msmsdcc_fmax) &&
+			(req_clk > host->plat->msmsdcc_fmid))
+			sel_clk = host->plat->msmsdcc_fmid;
+		else
+			sel_clk = req_clk;
+	}
+
+out:
+	return sel_clk;
+}
+
+static inline unsigned int msmsdcc_get_min_sup_clk_rate(
+				struct msmsdcc_host *host)
+{
+	if (host->plat->sup_clk_table && host->plat->sup_clk_cnt)
+		return host->plat->sup_clk_table[0];
+	else
+		return host->plat->msmsdcc_fmin;
+}
+
+static inline unsigned int msmsdcc_get_max_sup_clk_rate(
+				struct msmsdcc_host *host)
+{
+	if (host->plat->sup_clk_table && host->plat->sup_clk_cnt)
+		return host->plat->sup_clk_table[host->plat->sup_clk_cnt - 1];
+	else
+		return host->plat->msmsdcc_fmax;
+}
+
+static int msmsdcc_setup_gpio(struct msmsdcc_host *host, bool enable)
 {
 	struct msm_mmc_gpio_data *curr;
 	int i, rc = 0;
 
-	if (!host->plat->gpio_data || host->gpio_config_status == enable)
-		return;
-
-	curr = host->plat->gpio_data;
+	curr = host->plat->pin_data->gpio_data;
 	for (i = 0; i < curr->size; i++) {
 		if (enable) {
+			if (curr->gpio[i].is_always_on &&
+				curr->gpio[i].is_enabled)
+				continue;
 			rc = gpio_request(curr->gpio[i].no,
 						curr->gpio[i].name);
 			if (rc) {
@@ -966,16 +2465,426 @@
 					curr->gpio[i].name, rc);
 				goto free_gpios;
 			}
+			curr->gpio[i].is_enabled = true;
 		} else {
+			if (curr->gpio[i].is_always_on)
+				continue;
 			gpio_free(curr->gpio[i].no);
+			curr->gpio[i].is_enabled = false;
 		}
 	}
-	host->gpio_config_status = enable;
-	return;
+	goto out;
 
 free_gpios:
-	for (; i >= 0; i--)
+	for (; i >= 0; i--) {
 		gpio_free(curr->gpio[i].no);
+		curr->gpio[i].is_enabled = false;
+	}
+out:
+	return rc;
+}
+
+static int msmsdcc_setup_pad(struct msmsdcc_host *host, bool enable)
+{
+	struct msm_mmc_pad_data *curr;
+	int i;
+
+	curr = host->plat->pin_data->pad_data;
+	for (i = 0; i < curr->drv->size; i++) {
+		if (enable)
+			msm_tlmm_set_hdrive(curr->drv->on[i].no,
+				curr->drv->on[i].val);
+		else
+			msm_tlmm_set_hdrive(curr->drv->off[i].no,
+				curr->drv->off[i].val);
+	}
+
+	for (i = 0; i < curr->pull->size; i++) {
+		if (enable)
+			msm_tlmm_set_pull(curr->pull->on[i].no,
+				curr->pull->on[i].val);
+		else
+			msm_tlmm_set_pull(curr->pull->off[i].no,
+				curr->pull->off[i].val);
+	}
+
+	return 0;
+}
+
+static u32 msmsdcc_setup_pins(struct msmsdcc_host *host, bool enable)
+{
+	int rc = 0;
+
+	if (!host->plat->pin_data || host->plat->pin_data->cfg_sts == enable)
+		return 0;
+
+	if (host->plat->pin_data->is_gpio)
+		rc = msmsdcc_setup_gpio(host, enable);
+	else
+		rc = msmsdcc_setup_pad(host, enable);
+
+	if (!rc)
+		host->plat->pin_data->cfg_sts = enable;
+
+	return rc;
+}
+
+static int msmsdcc_cfg_mpm_sdiowakeup(struct msmsdcc_host *host,
+				      unsigned mode)
+{
+	int ret = 0;
+	unsigned int pin = host->plat->mpm_sdiowakeup_int;
+
+	if (!pin)
+		return 0;
+
+	switch (mode) {
+	case SDC_DAT1_DISABLE:
+		ret = msm_mpm_enable_pin(pin, 0);
+		break;
+	case SDC_DAT1_ENABLE:
+		ret = msm_mpm_set_pin_type(pin, IRQ_TYPE_LEVEL_LOW);
+		ret = msm_mpm_enable_pin(pin, 1);
+		break;
+	case SDC_DAT1_ENWAKE:
+		ret = msm_mpm_set_pin_wake(pin, 1);
+		break;
+	case SDC_DAT1_DISWAKE:
+		ret = msm_mpm_set_pin_wake(pin, 0);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static u32 msmsdcc_setup_pwr(struct msmsdcc_host *host, struct mmc_ios *ios)
+{
+	u32 pwr = 0;
+	int ret = 0;
+	struct mmc_host *mmc = host->mmc;
+
+	if (host->plat->translate_vdd && !host->sdio_gpio_lpm)
+		ret = host->plat->translate_vdd(mmc_dev(mmc), ios->vdd);
+	else if (!host->plat->translate_vdd && !host->sdio_gpio_lpm)
+		ret = msmsdcc_setup_vreg(host, !!ios->vdd);
+
+	if (ret) {
+		pr_err("%s: Failed to setup voltage regulators\n",
+				mmc_hostname(host->mmc));
+		goto out;
+	}
+
+	switch (ios->power_mode) {
+	case MMC_POWER_OFF:
+		pwr = MCI_PWR_OFF;
+		msmsdcc_cfg_mpm_sdiowakeup(host, SDC_DAT1_DISABLE);
+		/*
+		 * As VDD pad rail is always on, set low voltage for VDD
+		 * pad rail when slot is unused (when card is not present
+		 * or during system suspend).
+		 */
+		msmsdcc_set_vddp_low_vol(host);
+		msmsdcc_setup_pins(host, false);
+		break;
+	case MMC_POWER_UP:
+		/* writing PWR_UP bit is redundant */
+		pwr = MCI_PWR_UP;
+		msmsdcc_cfg_mpm_sdiowakeup(host, SDC_DAT1_ENABLE);
+
+		msmsdcc_set_vddp_high_vol(host);
+		msmsdcc_setup_pins(host, true);
+		break;
+	case MMC_POWER_ON:
+		pwr = MCI_PWR_ON;
+		break;
+	}
+
+out:
+	return pwr;
+}
+
+static void msmsdcc_enable_irq_wake(struct msmsdcc_host *host)
+{
+	unsigned int wakeup_irq;
+
+	wakeup_irq = (host->plat->sdiowakeup_irq) ?
+			host->plat->sdiowakeup_irq :
+			host->core_irqres->start;
+
+	if (!host->irq_wake_enabled) {
+		enable_irq_wake(wakeup_irq);
+		host->irq_wake_enabled = true;
+	}
+}
+
+static void msmsdcc_disable_irq_wake(struct msmsdcc_host *host)
+{
+	unsigned int wakeup_irq;
+
+	wakeup_irq = (host->plat->sdiowakeup_irq) ?
+			host->plat->sdiowakeup_irq :
+			host->core_irqres->start;
+
+	if (host->irq_wake_enabled) {
+		disable_irq_wake(wakeup_irq);
+		host->irq_wake_enabled = false;
+	}
+}
+
+/* Returns required bandwidth in Bytes per Sec */
+static unsigned int msmsdcc_get_bw_required(struct msmsdcc_host *host,
+					    struct mmc_ios *ios)
+{
+	unsigned int bw;
+
+	bw = host->clk_rate;
+	/*
+	 * For DDR mode, SDCC controller clock will be at
+	 * the double rate than the actual clock that goes to card.
+	 */
+	if (ios->bus_width == MMC_BUS_WIDTH_4)
+		bw /= 2;
+	else if (ios->bus_width == MMC_BUS_WIDTH_1)
+		bw /= 8;
+
+	return bw;
+}
+
+static int msmsdcc_msm_bus_get_vote_for_bw(struct msmsdcc_host *host,
+					   unsigned int bw)
+{
+	unsigned int *table = host->plat->msm_bus_voting_data->bw_vecs;
+	unsigned int size = host->plat->msm_bus_voting_data->bw_vecs_size;
+	int i;
+
+	if (host->msm_bus_vote.is_max_bw_needed && bw)
+		return host->msm_bus_vote.max_bw_vote;
+
+	for (i = 0; i < size; i++) {
+		if (bw <= table[i])
+			break;
+	}
+
+	if (i && (i == size))
+		i--;
+
+	return i;
+}
+
+static int msmsdcc_msm_bus_register(struct msmsdcc_host *host)
+{
+	int rc = 0;
+	struct msm_bus_scale_pdata *use_cases;
+
+	if (host->plat->msm_bus_voting_data &&
+	    host->plat->msm_bus_voting_data->use_cases &&
+	    host->plat->msm_bus_voting_data->bw_vecs &&
+	    host->plat->msm_bus_voting_data->bw_vecs_size) {
+		use_cases = host->plat->msm_bus_voting_data->use_cases;
+		host->msm_bus_vote.client_handle =
+				msm_bus_scale_register_client(use_cases);
+	} else {
+		return 0;
+	}
+
+	if (!host->msm_bus_vote.client_handle) {
+		pr_err("%s: msm_bus_scale_register_client() failed\n",
+		       mmc_hostname(host->mmc));
+		rc = -EFAULT;
+	} else {
+		/* cache the vote index for minimum and maximum bandwidth */
+		host->msm_bus_vote.min_bw_vote =
+				msmsdcc_msm_bus_get_vote_for_bw(host, 0);
+		host->msm_bus_vote.max_bw_vote =
+				msmsdcc_msm_bus_get_vote_for_bw(host, UINT_MAX);
+	}
+
+	return rc;
+}
+
+static void msmsdcc_msm_bus_unregister(struct msmsdcc_host *host)
+{
+	if (host->msm_bus_vote.client_handle)
+		msm_bus_scale_unregister_client(
+			host->msm_bus_vote.client_handle);
+}
+
+/*
+ * This function must be called with host lock acquired.
+ * Caller of this function should also ensure that msm bus client
+ * handle is not null.
+ */
+static inline int msmsdcc_msm_bus_set_vote(struct msmsdcc_host *host,
+					     int vote,
+					     unsigned long flags)
+{
+	int rc = 0;
+
+	if (vote != host->msm_bus_vote.curr_vote) {
+		spin_unlock_irqrestore(&host->lock, flags);
+		rc = msm_bus_scale_client_update_request(
+				host->msm_bus_vote.client_handle, vote);
+		if (rc)
+			pr_err("%s: msm_bus_scale_client_update_request() failed."
+			       " bus_client_handle=0x%x, vote=%d, err=%d\n",
+			       mmc_hostname(host->mmc),
+			       host->msm_bus_vote.client_handle, vote, rc);
+		spin_lock_irqsave(&host->lock, flags);
+		if (!rc)
+			host->msm_bus_vote.curr_vote = vote;
+	}
+
+	return rc;
+}
+
+/*
+ * Internal work. Work to set 0 bandwidth for msm bus.
+ */
+static void msmsdcc_msm_bus_work(struct work_struct *work)
+{
+	struct msmsdcc_host *host = container_of(work,
+					struct msmsdcc_host,
+					msm_bus_vote.vote_work.work);
+	unsigned long flags;
+
+	if (!host->msm_bus_vote.client_handle)
+		return;
+
+	spin_lock_irqsave(&host->lock, flags);
+	/* don't vote for 0 bandwidth if any request is in progress */
+	if (!host->curr.mrq)
+		msmsdcc_msm_bus_set_vote(host,
+			host->msm_bus_vote.min_bw_vote, flags);
+	else
+		pr_warning("%s: %s: SDCC transfer in progress. skipping"
+			   " bus voting to 0 bandwidth\n",
+			   mmc_hostname(host->mmc), __func__);
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+/*
+ * This function cancels any scheduled delayed work
+ * and sets the bus vote based on ios argument.
+ * If "ios" argument is NULL, bandwidth required is 0 else
+ * calculate the bandwidth based on ios parameters.
+ */
+static void msmsdcc_msm_bus_cancel_work_and_set_vote(
+					struct msmsdcc_host *host,
+					struct mmc_ios *ios)
+{
+	unsigned long flags;
+	unsigned int bw;
+	int vote;
+
+	if (!host->msm_bus_vote.client_handle)
+		return;
+
+	bw = ios ? msmsdcc_get_bw_required(host, ios) : 0;
+
+	cancel_delayed_work_sync(&host->msm_bus_vote.vote_work);
+	spin_lock_irqsave(&host->lock, flags);
+	vote = msmsdcc_msm_bus_get_vote_for_bw(host, bw);
+	msmsdcc_msm_bus_set_vote(host, vote, flags);
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+/* This function queues a work which will set the bandwidth requiement to 0 */
+static void msmsdcc_msm_bus_queue_work(struct msmsdcc_host *host)
+{
+	unsigned long flags;
+
+	if (!host->msm_bus_vote.client_handle)
+		return;
+
+	spin_lock_irqsave(&host->lock, flags);
+	if (host->msm_bus_vote.min_bw_vote != host->msm_bus_vote.curr_vote)
+		queue_delayed_work(system_nrt_wq,
+				   &host->msm_bus_vote.vote_work,
+				   msecs_to_jiffies(MSM_MMC_BUS_VOTING_DELAY));
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void
+msmsdcc_cfg_sdio_wakeup(struct msmsdcc_host *host, bool enable_wakeup_irq)
+{
+	struct mmc_host *mmc = host->mmc;
+
+	/*
+	 * SDIO_AL clients has different mechanism of handling LPM through
+	 * sdio_al driver itself. The sdio wakeup interrupt is configured as
+	 * part of that. Here, we are interested only in clients like WLAN.
+	 */
+	if (!(mmc->card && mmc_card_sdio(mmc->card))
+			|| host->plat->is_sdio_al_client)
+		goto out;
+
+	if (!host->sdcc_suspended) {
+		/*
+		 * When MSM is not in power collapse and we
+		 * are disabling clocks, enable bit 22 in MASK0
+		 * to handle asynchronous SDIO interrupts.
+		 */
+		if (enable_wakeup_irq) {
+			writel_relaxed(MCI_SDIOINTMASK, host->base + MMCIMASK0);
+			mb();
+		} else {
+			writel_relaxed(MCI_SDIOINTMASK, host->base + MMCICLEAR);
+			msmsdcc_sync_reg_wr(host);
+		}
+		goto out;
+	} else if (!mmc_card_wake_sdio_irq(mmc)) {
+		/*
+		 * Wakeup MSM only if SDIO function drivers set
+		 * MMC_PM_WAKE_SDIO_IRQ flag in their suspend call.
+		 */
+		goto out;
+	}
+
+	if (enable_wakeup_irq) {
+		if (!host->plat->sdiowakeup_irq) {
+			/*
+			 * When there is no gpio line that can be configured
+			 * as wakeup interrupt handle it by configuring
+			 * asynchronous sdio interrupts and DAT1 line.
+			 */
+			writel_relaxed(MCI_SDIOINTMASK,
+					host->base + MMCIMASK0);
+			mb();
+			msmsdcc_cfg_mpm_sdiowakeup(host, SDC_DAT1_ENWAKE);
+			/* configure sdcc core interrupt as wakeup interrupt */
+			msmsdcc_enable_irq_wake(host);
+		} else {
+			/* Let gpio line handle wakeup interrupt */
+			writel_relaxed(0, host->base + MMCIMASK0);
+			mb();
+			if (host->sdio_wakeupirq_disabled) {
+				host->sdio_wakeupirq_disabled = 0;
+				/* configure gpio line as wakeup interrupt */
+				msmsdcc_enable_irq_wake(host);
+				enable_irq(host->plat->sdiowakeup_irq);
+			}
+		}
+	} else {
+		if (!host->plat->sdiowakeup_irq) {
+			/*
+			 * We may not have cleared bit 22 in the interrupt
+			 * handler as the clocks might be off at that time.
+			 */
+			writel_relaxed(MCI_SDIOINTMASK, host->base + MMCICLEAR);
+			msmsdcc_sync_reg_wr(host);
+			msmsdcc_cfg_mpm_sdiowakeup(host, SDC_DAT1_DISWAKE);
+			msmsdcc_disable_irq_wake(host);
+		} else if (!host->sdio_wakeupirq_disabled) {
+			disable_irq_nosync(host->plat->sdiowakeup_irq);
+			msmsdcc_disable_irq_wake(host);
+			host->sdio_wakeupirq_disabled = 1;
+		}
+	}
+out:
+	return;
 }
 
 static void
@@ -985,127 +2894,1031 @@
 	u32 clk = 0, pwr = 0;
 	int rc;
 	unsigned long flags;
+	unsigned int clock;
 
+
+	/*
+	 * Disable SDCC core interrupt until set_ios is completed.
+	 * This avoids any race conditions with interrupt raised
+	 * when turning on/off the clocks. One possible
+	 * scenario is SDIO operational interrupt while the clock
+	 * is turned off.
+	 * host->lock is being released intermittently below.
+	 * Thus, prevent concurrent access to host.
+	 */
+
+	mutex_lock(&host->clk_mutex);
+	DBG(host, "ios->clock = %u\n", ios->clock);
 	spin_lock_irqsave(&host->lock, flags);
-
-	msmsdcc_enable_clocks(host);
-
+	if (!host->sdcc_irq_disabled) {
+		spin_unlock_irqrestore(&host->lock, flags);
+		disable_irq(host->core_irqres->start);
+		spin_lock_irqsave(&host->lock, flags);
+		host->sdcc_irq_disabled = 1;
+	}
 	spin_unlock_irqrestore(&host->lock, flags);
 
+	pwr = msmsdcc_setup_pwr(host, ios);
+
+	spin_lock_irqsave(&host->lock, flags);
 	if (ios->clock) {
-		if (ios->clock != host->clk_rate) {
-			rc = clk_set_rate(host->clk, ios->clock);
-			if (rc < 0)
-				pr_err("%s: Error setting clock rate (%d)\n",
-				       mmc_hostname(host->mmc), rc);
-			else
-				host->clk_rate = ios->clock;
+		if (!host->clks_on) {
+			spin_unlock_irqrestore(&host->lock, flags);
+			msmsdcc_setup_clocks(host, true);
+			spin_lock_irqsave(&host->lock, flags);
+			host->clks_on = 1;
+			writel_relaxed(host->mci_irqenable,
+					host->base + MMCIMASK0);
+			mb();
+			msmsdcc_cfg_sdio_wakeup(host, false);
 		}
+
+		clock = msmsdcc_get_sup_clk_rate(host, ios->clock);
+		/*
+		 * For DDR50 mode, controller needs clock rate to be
+		 * double than what is required on the SD card CLK pin.
+		 */
+		if (ios->timing == MMC_TIMING_UHS_DDR50) {
+			/*
+			 * Make sure that we don't double the clock if
+			 * doubled clock rate is already set
+			 */
+			if (!host->ddr_doubled_clk_rate ||
+				(host->ddr_doubled_clk_rate &&
+				(host->ddr_doubled_clk_rate != ios->clock))) {
+				host->ddr_doubled_clk_rate =
+					msmsdcc_get_sup_clk_rate(
+						host, (ios->clock * 2));
+				clock = host->ddr_doubled_clk_rate;
+			}
+		} else {
+			host->ddr_doubled_clk_rate = 0;
+		}
+
+		if (clock != host->clk_rate) {
+			spin_unlock_irqrestore(&host->lock, flags);
+			rc = clk_set_rate(host->clk, clock);
+			spin_lock_irqsave(&host->lock, flags);
+			if (rc < 0)
+				pr_err("%s: failed to set clk rate %u\n",
+						mmc_hostname(mmc), clock);
+			host->clk_rate = clock;
+			host->reg_write_delay =
+				(1 + ((3 * USEC_PER_SEC) /
+				      (host->clk_rate ? host->clk_rate :
+				       msmsdcc_get_min_sup_clk_rate(host))));
+		}
+		/*
+		 * give atleast 2 MCLK cycles delay for clocks
+		 * and SDCC core to stabilize
+		 */
+		mb();
+		msmsdcc_delay(host);
 		clk |= MCI_CLK_ENABLE;
 	}
 
-	if (ios->bus_width == MMC_BUS_WIDTH_4)
-		clk |= (2 << 10); /* Set WIDEBUS */
+	if (ios->bus_width == MMC_BUS_WIDTH_8)
+		clk |= MCI_CLK_WIDEBUS_8;
+	else if (ios->bus_width == MMC_BUS_WIDTH_4)
+		clk |= MCI_CLK_WIDEBUS_4;
+	else
+		clk |= MCI_CLK_WIDEBUS_1;
 
-	if (ios->clock > 400000 && msmsdcc_pwrsave)
-		clk |= (1 << 9); /* PWRSAVE */
+	if (msmsdcc_is_pwrsave(host))
+		clk |= MCI_CLK_PWRSAVE;
 
-	clk |= (1 << 12); /* FLOW_ENA */
-	clk |= (1 << 15); /* feedback clock */
+	clk |= MCI_CLK_FLOWENA;
 
-	if (host->plat->translate_vdd)
-		pwr |= host->plat->translate_vdd(mmc_dev(mmc), ios->vdd);
-
-	switch (ios->power_mode) {
-	case MMC_POWER_OFF:
-		msmsdcc_setup_gpio(host, false);
-		break;
-	case MMC_POWER_UP:
-		pwr |= MCI_PWR_UP;
-		msmsdcc_setup_gpio(host, true);
-		break;
-	case MMC_POWER_ON:
-		pwr |= MCI_PWR_ON;
-		break;
+	host->tuning_needed = 0;
+	/*
+	 * Select the controller timing mode according
+	 * to current bus speed mode
+	 */
+	if ((ios->timing == MMC_TIMING_UHS_SDR104) ||
+		(ios->timing == MMC_TIMING_MMC_HS200)) {
+		clk |= (4 << 14);
+		host->tuning_needed = 1;
+	} else if (ios->timing == MMC_TIMING_UHS_DDR50) {
+		clk |= (3 << 14);
+	} else {
+		clk |= (2 << 14); /* feedback clock */
 	}
 
-	if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
-		pwr |= MCI_OD;
+	/* Select free running MCLK as input clock of cm_dll_sdc4 */
+	clk |= (2 << 23);
 
-	msmsdcc_writel(host, clk, MMCICLOCK);
+	/* Clear IO_PAD_PWR_SWITCH while powering off the card */
+	if (!ios->vdd)
+		host->io_pad_pwr_switch = 0;
 
-	if (host->pwr != pwr) {
-		host->pwr = pwr;
-		msmsdcc_writel(host, pwr, MMCIPOWER);
+	if (host->io_pad_pwr_switch)
+		clk |= IO_PAD_PWR_SWITCH;
+
+	/* Don't write into registers if clocks are disabled */
+	if (host->clks_on) {
+		if (readl_relaxed(host->base + MMCICLOCK) != clk) {
+			writel_relaxed(clk, host->base + MMCICLOCK);
+			msmsdcc_sync_reg_wr(host);
+		}
+		if (readl_relaxed(host->base + MMCIPOWER) != pwr) {
+			host->pwr = pwr;
+			writel_relaxed(pwr, host->base + MMCIPOWER);
+			msmsdcc_sync_reg_wr(host);
+		}
 	}
-#if BUSCLK_PWRSAVE
-	spin_lock_irqsave(&host->lock, flags);
-	msmsdcc_disable_clocks(host, 1);
+
+	if (!(clk & MCI_CLK_ENABLE) && host->clks_on) {
+		msmsdcc_cfg_sdio_wakeup(host, true);
+		spin_unlock_irqrestore(&host->lock, flags);
+		/*
+		 * May get a wake-up interrupt the instant we disable the
+		 * clocks. This would disable the wake-up interrupt.
+		 */
+		msmsdcc_setup_clocks(host, false);
+		spin_lock_irqsave(&host->lock, flags);
+		host->clks_on = 0;
+	}
+
+	if (host->tuning_in_progress)
+		WARN(!host->clks_on,
+			"tuning_in_progress but SDCC clocks are OFF\n");
+
+	/* Let interrupts be disabled if the host is powered off */
+	if (ios->power_mode != MMC_POWER_OFF && host->sdcc_irq_disabled) {
+		enable_irq(host->core_irqres->start);
+		host->sdcc_irq_disabled = 0;
+	}
+
 	spin_unlock_irqrestore(&host->lock, flags);
-#endif
+	mutex_unlock(&host->clk_mutex);
+}
+
+int msmsdcc_set_pwrsave(struct mmc_host *mmc, int pwrsave)
+{
+	struct msmsdcc_host *host = mmc_priv(mmc);
+	u32 clk;
+
+	clk = readl_relaxed(host->base + MMCICLOCK);
+	pr_debug("Changing to pwr_save=%d", pwrsave);
+	if (pwrsave && msmsdcc_is_pwrsave(host))
+		clk |= MCI_CLK_PWRSAVE;
+	else
+		clk &= ~MCI_CLK_PWRSAVE;
+	writel_relaxed(clk, host->base + MMCICLOCK);
+	msmsdcc_sync_reg_wr(host);
+
+	return 0;
+}
+
+static int msmsdcc_get_ro(struct mmc_host *mmc)
+{
+	int status = -ENOSYS;
+	struct msmsdcc_host *host = mmc_priv(mmc);
+
+	if (host->plat->wpswitch) {
+		status = host->plat->wpswitch(mmc_dev(mmc));
+	} else if (host->plat->wpswitch_gpio) {
+		status = gpio_request(host->plat->wpswitch_gpio,
+					"SD_WP_Switch");
+		if (status) {
+			pr_err("%s: %s: Failed to request GPIO %d\n",
+				mmc_hostname(mmc), __func__,
+				host->plat->wpswitch_gpio);
+		} else {
+			status = gpio_direction_input(
+					host->plat->wpswitch_gpio);
+			if (!status) {
+				/*
+				 * Wait for atleast 300ms as debounce
+				 * time for GPIO input to stabilize.
+				 */
+				msleep(300);
+				status = gpio_get_value_cansleep(
+						host->plat->wpswitch_gpio);
+				status ^= !host->plat->wpswitch_polarity;
+			}
+			gpio_free(host->plat->wpswitch_gpio);
+		}
+	}
+
+	if (status < 0)
+		status = -ENOSYS;
+	pr_debug("%s: Card read-only status %d\n", __func__, status);
+
+	return status;
 }
 
 static void msmsdcc_enable_sdio_irq(struct mmc_host *mmc, int enable)
 {
 	struct msmsdcc_host *host = mmc_priv(mmc);
 	unsigned long flags;
-	u32 status;
+
+	/*
+	 * We may come here with clocks turned off in that case don't
+	 * attempt to write into MASK0 register. While turning on the
+	 * clocks mci_irqenable will be written to MASK0 register.
+	 */
 
 	spin_lock_irqsave(&host->lock, flags);
-	if (msmsdcc_sdioirq == 1) {
-		status = msmsdcc_readl(host, MMCIMASK0);
-		if (enable)
-			status |= MCI_SDIOINTOPERMASK;
-		else
-			status &= ~MCI_SDIOINTOPERMASK;
-		host->saved_irq0mask = status;
-		msmsdcc_writel(host, status, MMCIMASK0);
+	if (enable) {
+		host->mci_irqenable |= MCI_SDIOINTOPERMASK;
+		if (host->clks_on) {
+			writel_relaxed(readl_relaxed(host->base + MMCIMASK0) |
+				MCI_SDIOINTOPERMASK, host->base + MMCIMASK0);
+			mb();
+		}
+	} else {
+		host->mci_irqenable &= ~MCI_SDIOINTOPERMASK;
+		if (host->clks_on) {
+			writel_relaxed(readl_relaxed(host->base + MMCIMASK0) &
+				~MCI_SDIOINTOPERMASK, host->base + MMCIMASK0);
+			mb();
+		}
 	}
 	spin_unlock_irqrestore(&host->lock, flags);
 }
 
-static void msmsdcc_init_card(struct mmc_host *mmc, struct mmc_card *card)
+#ifdef CONFIG_PM_RUNTIME
+static void msmsdcc_print_rpm_info(struct msmsdcc_host *host)
 {
+	struct device *dev = mmc_dev(host->mmc);
+
+	pr_info("%s: RPM: runtime_status=%d, usage_count=%d,"
+		" is_suspended=%d, disable_depth=%d, runtime_error=%d,"
+		" request_pending=%d, request=%d\n",
+		mmc_hostname(host->mmc), dev->power.runtime_status,
+		atomic_read(&dev->power.usage_count),
+		dev->power.is_suspended, dev->power.disable_depth,
+		dev->power.runtime_error, dev->power.request_pending,
+		dev->power.request);
+}
+
+static int msmsdcc_enable(struct mmc_host *mmc)
+{
+	int rc = 0;
+	struct device *dev = mmc->parent;
 	struct msmsdcc_host *host = mmc_priv(mmc);
 
-	if (host->plat->init_card)
-		host->plat->init_card(card);
+	msmsdcc_pm_qos_update_latency(host, 1);
+
+	if (mmc->card && mmc_card_sdio(mmc->card))
+		goto out;
+
+	if (host->sdcc_suspended && host->pending_resume &&
+			!pm_runtime_suspended(dev)) {
+		host->pending_resume = false;
+		pm_runtime_get_noresume(dev);
+		rc = msmsdcc_runtime_resume(dev);
+		goto skip_get_sync;
+	}
+
+	if (dev->power.runtime_status == RPM_SUSPENDING) {
+		if (mmc->suspend_task == current) {
+			pm_runtime_get_noresume(dev);
+			goto out;
+		}
+	}
+
+	rc = pm_runtime_get_sync(dev);
+
+skip_get_sync:
+	if (rc < 0) {
+		pr_info("%s: %s: failed with error %d", mmc_hostname(mmc),
+				__func__, rc);
+		msmsdcc_print_rpm_info(host);
+		return rc;
+	}
+out:
+	msmsdcc_msm_bus_cancel_work_and_set_vote(host, &mmc->ios);
+	return 0;
+}
+
+static int msmsdcc_disable(struct mmc_host *mmc)
+{
+	int rc;
+	struct msmsdcc_host *host = mmc_priv(mmc);
+
+	msmsdcc_pm_qos_update_latency(host, 0);
+
+	if (mmc->card && mmc_card_sdio(mmc->card)) {
+		rc = 0;
+		goto out;
+	}
+
+	if (host->plat->disable_runtime_pm)
+		return -ENOTSUPP;
+
+	rc = pm_runtime_put_sync(mmc->parent);
+
+	/*
+	 * Ignore -EAGAIN as that is not fatal, it means that
+	 * either runtime usage count is non-zero or the runtime
+	 * pm itself is disabled or not in proper state to process
+	 * idle notification.
+	 */
+	if (rc < 0 && (rc != -EAGAIN)) {
+		pr_info("%s: %s: failed with error %d", mmc_hostname(mmc),
+				__func__, rc);
+		msmsdcc_print_rpm_info(host);
+		return rc;
+	}
+
+out:
+	msmsdcc_msm_bus_queue_work(host);
+	return rc;
+}
+#else
+static void msmsdcc_print_rpm_info(struct msmsdcc_host *host) {}
+
+static int msmsdcc_enable(struct mmc_host *mmc)
+{
+	struct device *dev = mmc->parent;
+	struct msmsdcc_host *host = mmc_priv(mmc);
+	unsigned long flags;
+	int rc = 0;
+
+	msmsdcc_pm_qos_update_latency(host, 1);
+
+	if (mmc->card && mmc_card_sdio(mmc->card)) {
+		rc = 0;
+		goto out;
+	}
+
+	if (host->sdcc_suspended && host->pending_resume) {
+		host->pending_resume = false;
+		rc = msmsdcc_runtime_resume(dev);
+		goto out;
+	}
+
+	mutex_lock(&host->clk_mutex);
+	spin_lock_irqsave(&host->lock, flags);
+	if (!host->clks_on) {
+		spin_unlock_irqrestore(&host->lock, flags);
+		msmsdcc_setup_clocks(host, true);
+		spin_lock_irqsave(&host->lock, flags);
+		host->clks_on = 1;
+	}
+	spin_unlock_irqrestore(&host->lock, flags);
+	mutex_unlock(&host->clk_mutex);
+
+out:
+	if (rc < 0) {
+		pr_info("%s: %s: failed with error %d", mmc_hostname(mmc),
+				__func__, rc);
+		return rc;
+	}
+	msmsdcc_msm_bus_cancel_work_and_set_vote(host, &mmc->ios);
+	return 0;
+}
+
+static int msmsdcc_disable(struct mmc_host *mmc)
+{
+	struct msmsdcc_host *host = mmc_priv(mmc);
+	unsigned long flags;
+
+	msmsdcc_pm_qos_update_latency(host, 0);
+
+	if (mmc->card && mmc_card_sdio(mmc->card))
+		goto out;
+
+	mutex_lock(&host->clk_mutex);
+	spin_lock_irqsave(&host->lock, flags);
+	if (host->clks_on) {
+		spin_unlock_irqrestore(&host->lock, flags);
+		msmsdcc_setup_clocks(host, false);
+		spin_lock_irqsave(&host->lock, flags);
+		host->clks_on = 0;
+	}
+	spin_unlock_irqrestore(&host->lock, flags);
+	mutex_unlock(&host->clk_mutex);
+
+out:
+	msmsdcc_msm_bus_queue_work(host);
+	return 0;
+}
+#endif
+
+static int msmsdcc_start_signal_voltage_switch(struct mmc_host *mmc,
+						struct mmc_ios *ios)
+{
+	struct msmsdcc_host *host = mmc_priv(mmc);
+	unsigned long flags;
+	int rc = 0;
+
+	spin_lock_irqsave(&host->lock, flags);
+	host->io_pad_pwr_switch = 0;
+	spin_unlock_irqrestore(&host->lock, flags);
+
+	/*
+	 * For eMMC cards, VccQ voltage range must be changed
+	 * only if it operates in HS200 SDR 1.2V mode or in
+	 * DDR 1.2V mode.
+	 */
+	if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_120) {
+		rc = msmsdcc_set_vccq_vol(host, 1200000);
+		goto out;
+	 }
+
+	if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
+		/* Change voltage level of VDDPX to high voltage */
+		rc = msmsdcc_set_vddp_high_vol(host);
+		goto out;
+	} else if (ios->signal_voltage != MMC_SIGNAL_VOLTAGE_180) {
+		/* invalid selection. don't do anything */
+		rc = -EINVAL;
+		goto out;
+	}
+
+	spin_lock_irqsave(&host->lock, flags);
+	/*
+	 * If we are here means voltage switch from high voltage to
+	 * low voltage is required
+	 */
+
+	/*
+	 * Poll on MCIDATIN_3_0 and MCICMDIN bits of MCI_TEST_INPUT
+	 * register until they become all zeros.
+	 */
+	if (readl_relaxed(host->base + MCI_TEST_INPUT) & (0xF << 1)) {
+		rc = -EAGAIN;
+		pr_err("%s: %s: MCIDATIN_3_0 is still not all zeros",
+			mmc_hostname(mmc), __func__);
+		goto out_unlock;
+	}
+
+	/* Stop SD CLK output. */
+	writel_relaxed((readl_relaxed(host->base + MMCICLOCK) |
+			MCI_CLK_PWRSAVE), host->base + MMCICLOCK);
+	msmsdcc_sync_reg_wr(host);
+	spin_unlock_irqrestore(&host->lock, flags);
+
+	/*
+	 * Switch VDDPX from high voltage to low voltage
+	 * to change the VDD of the SD IO pads.
+	 */
+	rc = msmsdcc_set_vddp_low_vol(host);
+	if (rc)
+		goto out;
+
+	spin_lock_irqsave(&host->lock, flags);
+	writel_relaxed((readl_relaxed(host->base + MMCICLOCK) |
+			IO_PAD_PWR_SWITCH), host->base + MMCICLOCK);
+	msmsdcc_sync_reg_wr(host);
+	host->io_pad_pwr_switch = 1;
+	spin_unlock_irqrestore(&host->lock, flags);
+
+	/* Wait 5 ms for the voltage regulater in the card to become stable. */
+	usleep_range(5000, 5500);
+
+	spin_lock_irqsave(&host->lock, flags);
+	/* Disable PWRSAVE would make sure that SD CLK is always running */
+	writel_relaxed((readl_relaxed(host->base + MMCICLOCK)
+			& ~MCI_CLK_PWRSAVE), host->base + MMCICLOCK);
+	msmsdcc_sync_reg_wr(host);
+	spin_unlock_irqrestore(&host->lock, flags);
+
+	/*
+	 * If MCIDATIN_3_0 and MCICMDIN bits of MCI_TEST_INPUT register
+	 * don't become all ones within 1 ms then a Voltage Switch
+	 * sequence has failed and a power cycle to the card is required.
+	 * Otherwise Voltage Switch sequence is completed successfully.
+	 */
+	usleep_range(1000, 1500);
+
+	spin_lock_irqsave(&host->lock, flags);
+	if ((readl_relaxed(host->base + MCI_TEST_INPUT) & (0xF << 1))
+				!= (0xF << 1)) {
+		pr_err("%s: %s: MCIDATIN_3_0 are still not all ones",
+			mmc_hostname(mmc), __func__);
+		rc = -EAGAIN;
+		goto out_unlock;
+	}
+
+out_unlock:
+	/* Enable PWRSAVE */
+	writel_relaxed((readl_relaxed(host->base + MMCICLOCK) |
+			MCI_CLK_PWRSAVE), host->base + MMCICLOCK);
+	msmsdcc_sync_reg_wr(host);
+	spin_unlock_irqrestore(&host->lock, flags);
+out:
+	return rc;
+}
+
+static inline void msmsdcc_cm_sdc4_dll_set_freq(struct msmsdcc_host *host)
+{
+	u32 mclk_freq = 0;
+
+	/* Program the MCLK value to MCLK_FREQ bit field */
+	if (host->clk_rate <= 112000000)
+		mclk_freq = 0;
+	else if (host->clk_rate <= 125000000)
+		mclk_freq = 1;
+	else if (host->clk_rate <= 137000000)
+		mclk_freq = 2;
+	else if (host->clk_rate <= 150000000)
+		mclk_freq = 3;
+	else if (host->clk_rate <= 162000000)
+		mclk_freq = 4;
+	else if (host->clk_rate <= 175000000)
+		mclk_freq = 5;
+	else if (host->clk_rate <= 187000000)
+		mclk_freq = 6;
+	else if (host->clk_rate <= 200000000)
+		mclk_freq = 7;
+
+	writel_relaxed(((readl_relaxed(host->base + MCI_DLL_CONFIG)
+			& ~(7 << 24)) | (mclk_freq << 24)),
+			host->base + MCI_DLL_CONFIG);
+}
+
+/* Initialize the DLL (Programmable Delay Line ) */
+static int msmsdcc_init_cm_sdc4_dll(struct msmsdcc_host *host)
+{
+	int rc = 0;
+	unsigned long flags;
+	u32 wait_cnt;
+
+	spin_lock_irqsave(&host->lock, flags);
+	/*
+	 * Make sure that clock is always enabled when DLL
+	 * tuning is in progress. Keeping PWRSAVE ON may
+	 * turn off the clock. So let's disable the PWRSAVE
+	 * here and re-enable it once tuning is completed.
+	 */
+	writel_relaxed((readl_relaxed(host->base + MMCICLOCK)
+			& ~MCI_CLK_PWRSAVE), host->base + MMCICLOCK);
+	msmsdcc_sync_reg_wr(host);
+
+	/* Write 1 to DLL_RST bit of MCI_DLL_CONFIG register */
+	writel_relaxed((readl_relaxed(host->base + MCI_DLL_CONFIG)
+			| MCI_DLL_RST), host->base + MCI_DLL_CONFIG);
+
+	/* Write 1 to DLL_PDN bit of MCI_DLL_CONFIG register */
+	writel_relaxed((readl_relaxed(host->base + MCI_DLL_CONFIG)
+			| MCI_DLL_PDN), host->base + MCI_DLL_CONFIG);
+
+	msmsdcc_cm_sdc4_dll_set_freq(host);
+
+	/* Write 0 to DLL_RST bit of MCI_DLL_CONFIG register */
+	writel_relaxed((readl_relaxed(host->base + MCI_DLL_CONFIG)
+			& ~MCI_DLL_RST), host->base + MCI_DLL_CONFIG);
+
+	/* Write 0 to DLL_PDN bit of MCI_DLL_CONFIG register */
+	writel_relaxed((readl_relaxed(host->base + MCI_DLL_CONFIG)
+			& ~MCI_DLL_PDN), host->base + MCI_DLL_CONFIG);
+
+	/* Set DLL_EN bit to 1. */
+	writel_relaxed((readl_relaxed(host->base + MCI_DLL_CONFIG)
+			| MCI_DLL_EN), host->base + MCI_DLL_CONFIG);
+
+	/* Set CK_OUT_EN bit to 1. */
+	writel_relaxed((readl_relaxed(host->base + MCI_DLL_CONFIG)
+			| MCI_CK_OUT_EN), host->base + MCI_DLL_CONFIG);
+
+	wait_cnt = 50;
+	/* Wait until DLL_LOCK bit of MCI_DLL_STATUS register becomes '1' */
+	while (!(readl_relaxed(host->base + MCI_DLL_STATUS) & MCI_DLL_LOCK)) {
+		/* max. wait for 50us sec for LOCK bit to be set */
+		if (--wait_cnt == 0) {
+			pr_err("%s: %s: DLL failed to LOCK\n",
+				mmc_hostname(host->mmc), __func__);
+			rc = -ETIMEDOUT;
+			goto out;
+		}
+		/* wait for 1us before polling again */
+		udelay(1);
+	}
+
+out:
+	/* re-enable PWRSAVE */
+	writel_relaxed((readl_relaxed(host->base + MMCICLOCK) |
+			MCI_CLK_PWRSAVE), host->base + MMCICLOCK);
+	msmsdcc_sync_reg_wr(host);
+	spin_unlock_irqrestore(&host->lock, flags);
+
+	return rc;
+}
+
+static inline int msmsdcc_dll_poll_ck_out_en(struct msmsdcc_host *host,
+						u8 poll)
+{
+	int rc = 0;
+	u32 wait_cnt = 50;
+	u8 ck_out_en = 0;
+
+	/* poll for MCI_CK_OUT_EN bit.  max. poll time = 50us */
+	ck_out_en = !!(readl_relaxed(host->base + MCI_DLL_CONFIG) &
+			MCI_CK_OUT_EN);
+
+	while (ck_out_en != poll) {
+		if (--wait_cnt == 0) {
+			pr_err("%s: %s: CK_OUT_EN bit is not %d\n",
+				mmc_hostname(host->mmc), __func__, poll);
+			rc = -ETIMEDOUT;
+			goto out;
+		}
+		udelay(1);
+
+		ck_out_en = !!(readl_relaxed(host->base + MCI_DLL_CONFIG) &
+			MCI_CK_OUT_EN);
+	}
+out:
+	return rc;
+}
+
+/*
+ * Enable a CDR circuit in CM_SDC4_DLL block to enable automatic
+ * calibration sequence. This function should be called before
+ * enabling AUTO_CMD19 bit in MCI_CMD register for block read
+ * commands (CMD17/CMD18).
+ *
+ * This function gets called when host spinlock acquired.
+ */
+static int msmsdcc_enable_cdr_cm_sdc4_dll(struct msmsdcc_host *host)
+{
+	int rc = 0;
+	u32 config;
+
+	config = readl_relaxed(host->base + MCI_DLL_CONFIG);
+	config |= MCI_CDR_EN;
+	config &= ~(MCI_CDR_EXT_EN | MCI_CK_OUT_EN);
+	writel_relaxed(config, host->base + MCI_DLL_CONFIG);
+
+	/* Wait until CK_OUT_EN bit of MCI_DLL_CONFIG register becomes '0' */
+	rc = msmsdcc_dll_poll_ck_out_en(host, 0);
+	if (rc)
+		goto err_out;
+
+	/* Set CK_OUT_EN bit of MCI_DLL_CONFIG register to 1. */
+	writel_relaxed((readl_relaxed(host->base + MCI_DLL_CONFIG)
+			| MCI_CK_OUT_EN), host->base + MCI_DLL_CONFIG);
+
+	/* Wait until CK_OUT_EN bit of MCI_DLL_CONFIG register becomes '1' */
+	rc = msmsdcc_dll_poll_ck_out_en(host, 1);
+	if (rc)
+		goto err_out;
+
+	goto out;
+
+err_out:
+	pr_err("%s: %s: Failed\n", mmc_hostname(host->mmc), __func__);
+out:
+	return rc;
+}
+
+static int msmsdcc_config_cm_sdc4_dll_phase(struct msmsdcc_host *host,
+						u8 phase)
+{
+	int rc = 0;
+	u8 grey_coded_phase_table[] = {0x0, 0x1, 0x3, 0x2, 0x6, 0x7, 0x5, 0x4,
+					0xC, 0xD, 0xF, 0xE, 0xA, 0xB, 0x9,
+					0x8};
+	unsigned long flags;
+	u32 config;
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	config = readl_relaxed(host->base + MCI_DLL_CONFIG);
+	config &= ~(MCI_CDR_EN | MCI_CK_OUT_EN);
+	config |= (MCI_CDR_EXT_EN | MCI_DLL_EN);
+	writel_relaxed(config, host->base + MCI_DLL_CONFIG);
+
+	/* Wait until CK_OUT_EN bit of MCI_DLL_CONFIG register becomes '0' */
+	rc = msmsdcc_dll_poll_ck_out_en(host, 0);
+	if (rc)
+		goto err_out;
+
+	/*
+	 * Write the selected DLL clock output phase (0 ... 15)
+	 * to CDR_SELEXT bit field of MCI_DLL_CONFIG register.
+	 */
+	writel_relaxed(((readl_relaxed(host->base + MCI_DLL_CONFIG)
+			& ~(0xF << 20))
+			| (grey_coded_phase_table[phase] << 20)),
+			host->base + MCI_DLL_CONFIG);
+
+	/* Set CK_OUT_EN bit of MCI_DLL_CONFIG register to 1. */
+	writel_relaxed((readl_relaxed(host->base + MCI_DLL_CONFIG)
+			| MCI_CK_OUT_EN), host->base + MCI_DLL_CONFIG);
+
+	/* Wait until CK_OUT_EN bit of MCI_DLL_CONFIG register becomes '1' */
+	rc = msmsdcc_dll_poll_ck_out_en(host, 1);
+	if (rc)
+		goto err_out;
+
+	config = readl_relaxed(host->base + MCI_DLL_CONFIG);
+	config |= MCI_CDR_EN;
+	config &= ~MCI_CDR_EXT_EN;
+	writel_relaxed(config, host->base + MCI_DLL_CONFIG);
+	goto out;
+
+err_out:
+	pr_err("%s: %s: Failed to set DLL phase: %d\n",
+		mmc_hostname(host->mmc), __func__, phase);
+out:
+	spin_unlock_irqrestore(&host->lock, flags);
+	return rc;
+}
+
+/*
+ * Find out the greatest range of consecuitive selected
+ * DLL clock output phases that can be used as sampling
+ * setting for SD3.0 UHS-I card read operation (in SDR104
+ * timing mode) or for eMMC4.5 card read operation (in HS200
+ * timing mode).
+ * Select the 3/4 of the range and configure the DLL with the
+ * selected DLL clock output phase.
+*/
+static int find_most_appropriate_phase(struct msmsdcc_host *host,
+				u8 *phase_table, u8 total_phases)
+{
+	#define MAX_PHASES 16
+	int ret;
+	u8 ranges[MAX_PHASES][MAX_PHASES] = { {0}, {0} };
+	u8 phases_per_row[MAX_PHASES] = {0};
+	int row_index = 0, col_index = 0, selected_row_index = 0, curr_max = 0;
+	int i, cnt, phase_0_raw_index = 0, phase_15_raw_index = 0;
+	bool phase_0_found = false, phase_15_found = false;
+
+	if (!total_phases || (total_phases > MAX_PHASES)) {
+		pr_err("%s: %s: invalid argument: total_phases=%d\n",
+			mmc_hostname(host->mmc), __func__, total_phases);
+		return -EINVAL;
+	}
+
+	for (cnt = 0; cnt < total_phases; cnt++) {
+		ranges[row_index][col_index] = phase_table[cnt];
+		phases_per_row[row_index] += 1;
+		col_index++;
+
+		if ((cnt + 1) == total_phases) {
+			continue;
+		/* check if next phase in phase_table is consecutive or not */
+		} else if ((phase_table[cnt] + 1) != phase_table[cnt + 1]) {
+			row_index++;
+			col_index = 0;
+		}
+	}
+
+	if (row_index >= MAX_PHASES)
+		return -EINVAL;
+
+	/* Check if phase-0 is present in first valid window? */
+	if (!ranges[0][0]) {
+		phase_0_found = true;
+		phase_0_raw_index = 0;
+		/* Check if cycle exist between 2 valid windows */
+		for (cnt = 1; cnt <= row_index; cnt++) {
+			if (phases_per_row[cnt]) {
+				for (i = 0; i < phases_per_row[cnt]; i++) {
+					if (ranges[cnt][i] == 15) {
+						phase_15_found = true;
+						phase_15_raw_index = cnt;
+						break;
+					}
+				}
+			}
+		}
+	}
+
+	/* If 2 valid windows form cycle then merge them as single window */
+	if (phase_0_found && phase_15_found) {
+		/* number of phases in raw where phase 0 is present */
+		u8 phases_0 = phases_per_row[phase_0_raw_index];
+		/* number of phases in raw where phase 15 is present */
+		u8 phases_15 = phases_per_row[phase_15_raw_index];
+
+		if (phases_0 + phases_15 >= MAX_PHASES)
+			/*
+			 * If there are more than 1 phase windows then total
+			 * number of phases in both the windows should not be
+			 * more than or equal to MAX_PHASES.
+			 */
+			return -EINVAL;
+
+		/* Merge 2 cyclic windows */
+		i = phases_15;
+		for (cnt = 0; cnt < phases_0; cnt++) {
+			ranges[phase_15_raw_index][i] =
+				ranges[phase_0_raw_index][cnt];
+			if (++i >= MAX_PHASES)
+				break;
+		}
+
+		phases_per_row[phase_0_raw_index] = 0;
+		phases_per_row[phase_15_raw_index] = phases_15 + phases_0;
+	}
+
+	for (cnt = 0; cnt <= row_index; cnt++) {
+		if (phases_per_row[cnt] > curr_max) {
+			curr_max = phases_per_row[cnt];
+			selected_row_index = cnt;
+		}
+	}
+
+	i = ((curr_max * 3) / 4);
+	if (i)
+		i--;
+
+	ret = (int)ranges[selected_row_index][i];
+
+	if (ret >= MAX_PHASES) {
+		ret = -EINVAL;
+		pr_err("%s: %s: invalid phase selected=%d\n",
+			mmc_hostname(host->mmc), __func__, ret);
+	}
+
+	return ret;
+}
+
+static int msmsdcc_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+	int rc = 0;
+	struct msmsdcc_host *host = mmc_priv(mmc);
+	unsigned long	flags;
+	u8 phase, *data_buf, tuned_phases[16], tuned_phase_cnt = 0;
+	const u32 *tuning_block_pattern = tuning_block_64;
+	int size = sizeof(tuning_block_64); /* Tuning pattern size in bytes */
+
+	pr_debug("%s: Enter %s\n", mmc_hostname(mmc), __func__);
+
+	/* Tuning is only required for SDR104 modes */
+	if (!host->tuning_needed) {
+		rc = 0;
+		goto exit;
+	}
+
+	spin_lock_irqsave(&host->lock, flags);
+	WARN(!host->pwr, "SDCC power is turned off\n");
+	WARN(!host->clks_on, "SDCC clocks are turned off\n");
+	WARN(host->sdcc_irq_disabled, "SDCC IRQ is disabled\n");
+
+	host->tuning_in_progress = 1;
+	if ((opcode == MMC_SEND_TUNING_BLOCK_HS200) &&
+		(mmc->ios.bus_width == MMC_BUS_WIDTH_8)) {
+		tuning_block_pattern = tuning_block_128;
+		size = sizeof(tuning_block_128);
+	}
+	spin_unlock_irqrestore(&host->lock, flags);
+
+	/* first of all reset the tuning block */
+	rc = msmsdcc_init_cm_sdc4_dll(host);
+	if (rc)
+		goto out;
+
+	data_buf = kmalloc(size, GFP_KERNEL);
+	if (!data_buf) {
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	phase = 0;
+	do {
+		struct mmc_command cmd = {0};
+		struct mmc_data data = {0};
+		struct mmc_request mrq = {
+			.cmd = &cmd,
+			.data = &data
+		};
+		struct scatterlist sg;
+
+		/* set the phase in delay line hw block */
+		rc = msmsdcc_config_cm_sdc4_dll_phase(host, phase);
+		if (rc)
+			goto kfree;
+
+		cmd.opcode = opcode;
+		cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+		data.blksz = size;
+		data.blocks = 1;
+		data.flags = MMC_DATA_READ;
+		data.timeout_ns = 1000 * 1000 * 1000; /* 1 sec */
+
+		data.sg = &sg;
+		data.sg_len = 1;
+		sg_init_one(&sg, data_buf, size);
+		memset(data_buf, 0, size);
+		mmc_wait_for_req(mmc, &mrq);
+
+		if (!cmd.error && !data.error &&
+			!memcmp(data_buf, tuning_block_pattern, size)) {
+			/* tuning is successful at this tuning point */
+			tuned_phases[tuned_phase_cnt++] = phase;
+			pr_debug("%s: %s: found good phase = %d\n",
+				mmc_hostname(mmc), __func__, phase);
+		}
+	} while (++phase < 16);
+
+	if (tuned_phase_cnt) {
+		rc = find_most_appropriate_phase(host, tuned_phases,
+							tuned_phase_cnt);
+		if (rc < 0)
+			goto kfree;
+		else
+			phase = (u8)rc;
+
+		/*
+		 * Finally set the selected phase in delay
+		 * line hw block.
+		 */
+		rc = msmsdcc_config_cm_sdc4_dll_phase(host, phase);
+		if (rc)
+			goto kfree;
+		pr_debug("%s: %s: finally setting the tuning phase to %d\n",
+				mmc_hostname(mmc), __func__, phase);
+	} else {
+		/* tuning failed */
+		pr_err("%s: %s: no tuning point found\n",
+			mmc_hostname(mmc), __func__);
+		msmsdcc_dump_sdcc_state(host);
+		rc = -EAGAIN;
+	}
+
+kfree:
+	kfree(data_buf);
+out:
+	spin_lock_irqsave(&host->lock, flags);
+	host->tuning_in_progress = 0;
+	spin_unlock_irqrestore(&host->lock, flags);
+exit:
+	pr_debug("%s: Exit %s\n", mmc_hostname(mmc), __func__);
+	return rc;
 }
 
 static const struct mmc_host_ops msmsdcc_ops = {
+	.enable		= msmsdcc_enable,
+	.disable	= msmsdcc_disable,
+	.pre_req        = msmsdcc_pre_req,
+	.post_req       = msmsdcc_post_req,
 	.request	= msmsdcc_request,
 	.set_ios	= msmsdcc_set_ios,
+	.get_ro		= msmsdcc_get_ro,
 	.enable_sdio_irq = msmsdcc_enable_sdio_irq,
-	.init_card	= msmsdcc_init_card,
+	.start_signal_voltage_switch = msmsdcc_start_signal_voltage_switch,
+	.execute_tuning = msmsdcc_execute_tuning
 };
 
+static unsigned int
+msmsdcc_slot_status(struct msmsdcc_host *host)
+{
+	int status;
+	unsigned int gpio_no = host->plat->status_gpio;
+
+	status = gpio_request(gpio_no, "SD_HW_Detect");
+	if (status) {
+		pr_err("%s: %s: Failed to request GPIO %d\n",
+			mmc_hostname(host->mmc), __func__, gpio_no);
+	} else {
+		status = gpio_direction_input(gpio_no);
+		if (!status) {
+			status = gpio_get_value_cansleep(gpio_no);
+			if (host->plat->is_status_gpio_active_low)
+				status = !status;
+		}
+		gpio_free(gpio_no);
+	}
+	return status;
+}
+
 static void
 msmsdcc_check_status(unsigned long data)
 {
 	struct msmsdcc_host *host = (struct msmsdcc_host *)data;
 	unsigned int status;
 
-	if (!host->plat->status) {
-		mmc_detect_change(host->mmc, 0);
-		goto out;
-	}
-
-	status = host->plat->status(mmc_dev(host->mmc));
-	host->eject = !status;
-	if (status ^ host->oldstat) {
-		pr_info("%s: Slot status change detected (%d -> %d)\n",
-			mmc_hostname(host->mmc), host->oldstat, status);
-		if (status)
-			mmc_detect_change(host->mmc, (5 * HZ) / 2);
+	if (host->plat->status || host->plat->status_gpio) {
+		if (host->plat->status)
+			status = host->plat->status(mmc_dev(host->mmc));
 		else
+			status = msmsdcc_slot_status(host);
+
+		host->eject = !status;
+
+		if (status ^ host->oldstat) {
+			if (host->plat->status)
+				pr_info("%s: Slot status change detected "
+					"(%d -> %d)\n",
+					mmc_hostname(host->mmc),
+					host->oldstat, status);
+			else if (host->plat->is_status_gpio_active_low)
+				pr_info("%s: Slot status change detected "
+					"(%d -> %d) and the card detect GPIO"
+					" is ACTIVE_LOW\n",
+					mmc_hostname(host->mmc),
+					host->oldstat, status);
+			else
+				pr_info("%s: Slot status change detected "
+					"(%d -> %d) and the card detect GPIO"
+					" is ACTIVE_HIGH\n",
+					mmc_hostname(host->mmc),
+					host->oldstat, status);
 			mmc_detect_change(host->mmc, 0);
+		}
+		host->oldstat = status;
+	} else {
+		mmc_detect_change(host->mmc, 0);
 	}
-
-	host->oldstat = status;
-
-out:
-	if (host->timer.function)
-		mod_timer(&host->timer, jiffies + HZ);
 }
 
 static irqreturn_t
@@ -1118,6 +3931,33 @@
 	return IRQ_HANDLED;
 }
 
+static irqreturn_t
+msmsdcc_platform_sdiowakeup_irq(int irq, void *dev_id)
+{
+	struct msmsdcc_host	*host = dev_id;
+
+	pr_debug("%s: SDIO Wake up IRQ : %d\n", mmc_hostname(host->mmc), irq);
+	spin_lock(&host->lock);
+	if (!host->sdio_wakeupirq_disabled) {
+		disable_irq_nosync(irq);
+		if (host->sdcc_suspended) {
+			wake_lock(&host->sdio_wlock);
+			msmsdcc_disable_irq_wake(host);
+		}
+		host->sdio_wakeupirq_disabled = 1;
+	}
+	if (host->plat->is_sdio_al_client) {
+		wake_lock(&host->sdio_wlock);
+		spin_unlock(&host->lock);
+		mmc_signal_sdio_irq(host->mmc);
+		goto out_unlocked;
+	}
+	spin_unlock(&host->lock);
+
+out_unlocked:
+	return IRQ_HANDLED;
+}
+
 static void
 msmsdcc_status_notify_cb(int card_present, void *dev_id)
 {
@@ -1128,21 +3968,13 @@
 	msmsdcc_check_status((unsigned long) host);
 }
 
-static void
-msmsdcc_busclk_expired(unsigned long _data)
-{
-	struct msmsdcc_host	*host = (struct msmsdcc_host *) _data;
-
-	if (host->clks_on)
-		msmsdcc_disable_clocks(host, 0);
-}
-
 static int
 msmsdcc_init_dma(struct msmsdcc_host *host)
 {
 	memset(&host->dma, 0, sizeof(struct msmsdcc_dma_data));
 	host->dma.host = host;
 	host->dma.channel = -1;
+	host->dma.crci = -1;
 
 	if (!host->dmares)
 		return -ENODEV;
@@ -1160,21 +3992,743 @@
 	host->dma.cmdptr_busaddr = host->dma.nc_busaddr +
 				offsetof(struct msmsdcc_nc_dmadata, cmdptr);
 	host->dma.channel = host->dmares->start;
+	host->dma.crci = host->dma_crci_res->start;
 
 	return 0;
 }
 
+#ifdef CONFIG_MMC_MSM_SPS_SUPPORT
+/**
+ * Allocate and Connect a SDCC peripheral's SPS endpoint
+ *
+ * This function allocates endpoint context and
+ * connect it with memory endpoint by calling
+ * appropriate SPS driver APIs.
+ *
+ * Also registers a SPS callback function with
+ * SPS driver
+ *
+ * This function should only be called once typically
+ * during driver probe.
+ *
+ * @host - Pointer to sdcc host structure
+ * @ep   - Pointer to sps endpoint data structure
+ * @is_produce - 1 means Producer endpoint
+ *		 0 means Consumer endpoint
+ *
+ * @return - 0 if successful else negative value.
+ *
+ */
+static int msmsdcc_sps_init_ep_conn(struct msmsdcc_host *host,
+				struct msmsdcc_sps_ep_conn_data *ep,
+				bool is_producer)
+{
+	int rc = 0;
+	struct sps_pipe *sps_pipe_handle;
+	struct sps_connect *sps_config = &ep->config;
+	struct sps_register_event *sps_event = &ep->event;
+
+	/* Allocate endpoint context */
+	sps_pipe_handle = sps_alloc_endpoint();
+	if (!sps_pipe_handle) {
+		pr_err("%s: sps_alloc_endpoint() failed!!! is_producer=%d",
+			   mmc_hostname(host->mmc), is_producer);
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	/* Get default connection configuration for an endpoint */
+	rc = sps_get_config(sps_pipe_handle, sps_config);
+	if (rc) {
+		pr_err("%s: sps_get_config() failed!!! pipe_handle=0x%x,"
+			" rc=%d", mmc_hostname(host->mmc),
+			(u32)sps_pipe_handle, rc);
+		goto get_config_err;
+	}
+
+	/* Modify the default connection configuration */
+	if (is_producer) {
+		/*
+		 * For SDCC producer transfer, source should be
+		 * SDCC peripheral where as destination should
+		 * be system memory.
+		 */
+		sps_config->source = host->sps.bam_handle;
+		sps_config->destination = SPS_DEV_HANDLE_MEM;
+		/* Producer pipe will handle this connection */
+		sps_config->mode = SPS_MODE_SRC;
+		sps_config->options =
+			SPS_O_AUTO_ENABLE | SPS_O_EOT | SPS_O_ACK_TRANSFERS;
+	} else {
+		/*
+		 * For SDCC consumer transfer, source should be
+		 * system memory where as destination should
+		 * SDCC peripheral
+		 */
+		sps_config->source = SPS_DEV_HANDLE_MEM;
+		sps_config->destination = host->sps.bam_handle;
+		sps_config->mode = SPS_MODE_DEST;
+		sps_config->options =
+			SPS_O_AUTO_ENABLE | SPS_O_EOT | SPS_O_ACK_TRANSFERS;
+	}
+
+	/* Producer pipe index */
+	sps_config->src_pipe_index = host->sps.src_pipe_index;
+	/* Consumer pipe index */
+	sps_config->dest_pipe_index = host->sps.dest_pipe_index;
+	/*
+	 * This event thresold value is only significant for BAM-to-BAM
+	 * transfer. It's ignored for BAM-to-System mode transfer.
+	 */
+	sps_config->event_thresh = 0x10;
+
+	/* Allocate maximum descriptor fifo size */
+	sps_config->desc.size = SPS_MAX_DESC_FIFO_SIZE -
+		(SPS_MAX_DESC_FIFO_SIZE % SPS_MAX_DESC_LENGTH);
+	sps_config->desc.base = dma_alloc_coherent(mmc_dev(host->mmc),
+						sps_config->desc.size,
+						&sps_config->desc.phys_base,
+						GFP_KERNEL);
+
+	if (!sps_config->desc.base) {
+		rc = -ENOMEM;
+		pr_err("%s: dma_alloc_coherent() failed!!! Can't allocate buffer\n"
+			, mmc_hostname(host->mmc));
+		goto get_config_err;
+	}
+	memset(sps_config->desc.base, 0x00, sps_config->desc.size);
+
+	/* Establish connection between peripheral and memory endpoint */
+	rc = sps_connect(sps_pipe_handle, sps_config);
+	if (rc) {
+		pr_err("%s: sps_connect() failed!!! pipe_handle=0x%x,"
+			" rc=%d", mmc_hostname(host->mmc),
+			(u32)sps_pipe_handle, rc);
+		goto sps_connect_err;
+	}
+
+	sps_event->mode = SPS_TRIGGER_CALLBACK;
+	sps_event->options = SPS_O_EOT;
+	sps_event->callback = msmsdcc_sps_complete_cb;
+	sps_event->xfer_done = NULL;
+	sps_event->user = (void *)host;
+
+	/* Register callback event for EOT (End of transfer) event. */
+	rc = sps_register_event(sps_pipe_handle, sps_event);
+	if (rc) {
+		pr_err("%s: sps_connect() failed!!! pipe_handle=0x%x,"
+			" rc=%d", mmc_hostname(host->mmc),
+			(u32)sps_pipe_handle, rc);
+		goto reg_event_err;
+	}
+	/* Now save the sps pipe handle */
+	ep->pipe_handle = sps_pipe_handle;
+	pr_debug("%s: %s, success !!! %s: pipe_handle=0x%x,"
+		" desc_fifo.phys_base=0x%x\n", mmc_hostname(host->mmc),
+		__func__, is_producer ? "READ" : "WRITE",
+		(u32)sps_pipe_handle, sps_config->desc.phys_base);
+	goto out;
+
+reg_event_err:
+	sps_disconnect(sps_pipe_handle);
+sps_connect_err:
+	dma_free_coherent(mmc_dev(host->mmc),
+			sps_config->desc.size,
+			sps_config->desc.base,
+			sps_config->desc.phys_base);
+get_config_err:
+	sps_free_endpoint(sps_pipe_handle);
+out:
+	return rc;
+}
+
+/**
+ * Disconnect and Deallocate a SDCC peripheral's SPS endpoint
+ *
+ * This function disconnect endpoint and deallocates
+ * endpoint context.
+ *
+ * This function should only be called once typically
+ * during driver remove.
+ *
+ * @host - Pointer to sdcc host structure
+ * @ep   - Pointer to sps endpoint data structure
+ *
+ */
+static void msmsdcc_sps_exit_ep_conn(struct msmsdcc_host *host,
+				struct msmsdcc_sps_ep_conn_data *ep)
+{
+	struct sps_pipe *sps_pipe_handle = ep->pipe_handle;
+	struct sps_connect *sps_config = &ep->config;
+	struct sps_register_event *sps_event = &ep->event;
+
+	sps_event->xfer_done = NULL;
+	sps_event->callback = NULL;
+	sps_register_event(sps_pipe_handle, sps_event);
+	sps_disconnect(sps_pipe_handle);
+	dma_free_coherent(mmc_dev(host->mmc),
+			sps_config->desc.size,
+			sps_config->desc.base,
+			sps_config->desc.phys_base);
+	sps_free_endpoint(sps_pipe_handle);
+}
+
+/**
+ * Reset SDCC peripheral's SPS endpoint
+ *
+ * This function disconnects an endpoint.
+ *
+ * This function should be called for reseting
+ * SPS endpoint when data transfer error is
+ * encountered during data transfer. This
+ * can be considered as soft reset to endpoint.
+ *
+ * This function should only be called if
+ * msmsdcc_sps_init() is already called.
+ *
+ * @host - Pointer to sdcc host structure
+ * @ep   - Pointer to sps endpoint data structure
+ *
+ * @return - 0 if successful else negative value.
+ */
+static int msmsdcc_sps_reset_ep(struct msmsdcc_host *host,
+				struct msmsdcc_sps_ep_conn_data *ep)
+{
+	int rc = 0;
+	struct sps_pipe *sps_pipe_handle = ep->pipe_handle;
+
+	rc = sps_disconnect(sps_pipe_handle);
+	if (rc) {
+		pr_err("%s: %s: sps_disconnect() failed!!! pipe_handle=0x%x,"
+			" rc=%d", mmc_hostname(host->mmc), __func__,
+			(u32)sps_pipe_handle, rc);
+		goto out;
+	}
+ out:
+	return rc;
+}
+
+/**
+ * Restore SDCC peripheral's SPS endpoint
+ *
+ * This function connects an endpoint.
+ *
+ * This function should be called for restoring
+ * SPS endpoint after data transfer error is
+ * encountered during data transfer. This
+ * can be considered as soft reset to endpoint.
+ *
+ * This function should only be called if
+ * msmsdcc_sps_reset_ep() is called before.
+ *
+ * @host - Pointer to sdcc host structure
+ * @ep   - Pointer to sps endpoint data structure
+ *
+ * @return - 0 if successful else negative value.
+ */
+static int msmsdcc_sps_restore_ep(struct msmsdcc_host *host,
+				struct msmsdcc_sps_ep_conn_data *ep)
+{
+	int rc = 0;
+	struct sps_pipe *sps_pipe_handle = ep->pipe_handle;
+	struct sps_connect *sps_config = &ep->config;
+	struct sps_register_event *sps_event = &ep->event;
+
+	/* Establish connection between peripheral and memory endpoint */
+	rc = sps_connect(sps_pipe_handle, sps_config);
+	if (rc) {
+		pr_err("%s: %s: sps_connect() failed!!! pipe_handle=0x%x,"
+			" rc=%d", mmc_hostname(host->mmc), __func__,
+			(u32)sps_pipe_handle, rc);
+		goto out;
+	}
+
+	/* Register callback event for EOT (End of transfer) event. */
+	rc = sps_register_event(sps_pipe_handle, sps_event);
+	if (rc) {
+		pr_err("%s: %s: sps_register_event() failed!!!"
+			" pipe_handle=0x%x, rc=%d",
+			mmc_hostname(host->mmc), __func__,
+			(u32)sps_pipe_handle, rc);
+		goto reg_event_err;
+	}
+	goto out;
+
+reg_event_err:
+	sps_disconnect(sps_pipe_handle);
+out:
+	return rc;
+}
+
+/**
+ * Initialize SPS HW connected with SDCC core
+ *
+ * This function register BAM HW resources with
+ * SPS driver and then initialize 2 SPS endpoints
+ *
+ * This function should only be called once typically
+ * during driver probe.
+ *
+ * @host - Pointer to sdcc host structure
+ *
+ * @return - 0 if successful else negative value.
+ *
+ */
+static int msmsdcc_sps_init(struct msmsdcc_host *host)
+{
+	int rc = 0;
+	struct sps_bam_props bam = {0};
+
+	host->bam_base = ioremap(host->bam_memres->start,
+				resource_size(host->bam_memres));
+	if (!host->bam_base) {
+		pr_err("%s: BAM ioremap() failed!!! phys_addr=0x%x,"
+			" size=0x%x", mmc_hostname(host->mmc),
+			host->bam_memres->start,
+			(host->bam_memres->end -
+			host->bam_memres->start));
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	bam.phys_addr = host->bam_memres->start;
+	bam.virt_addr = host->bam_base;
+	/*
+	 * This event thresold value is only significant for BAM-to-BAM
+	 * transfer. It's ignored for BAM-to-System mode transfer.
+	 */
+	bam.event_threshold = 0x10;	/* Pipe event threshold */
+	/*
+	 * This threshold controls when the BAM publish
+	 * the descriptor size on the sideband interface.
+	 * SPS HW will be used for data transfer size even
+	 * less than SDCC FIFO size. So let's set BAM summing
+	 * thresold to SPS_MIN_XFER_SIZE bytes.
+	 */
+	bam.summing_threshold = SPS_MIN_XFER_SIZE;
+	/* SPS driver wll handle the SDCC BAM IRQ */
+	bam.irq = (u32)host->bam_irqres->start;
+	bam.manage = SPS_BAM_MGR_LOCAL;
+
+	pr_info("%s: bam physical base=0x%x\n", mmc_hostname(host->mmc),
+			(u32)bam.phys_addr);
+	pr_info("%s: bam virtual base=0x%x\n", mmc_hostname(host->mmc),
+			(u32)bam.virt_addr);
+
+	/* Register SDCC Peripheral BAM device to SPS driver */
+	rc = sps_register_bam_device(&bam, &host->sps.bam_handle);
+	if (rc) {
+		pr_err("%s: sps_register_bam_device() failed!!! err=%d",
+			   mmc_hostname(host->mmc), rc);
+		goto reg_bam_err;
+	}
+	pr_info("%s: BAM device registered. bam_handle=0x%x",
+		mmc_hostname(host->mmc), host->sps.bam_handle);
+
+	host->sps.src_pipe_index = SPS_SDCC_PRODUCER_PIPE_INDEX;
+	host->sps.dest_pipe_index = SPS_SDCC_CONSUMER_PIPE_INDEX;
+
+	rc = msmsdcc_sps_init_ep_conn(host, &host->sps.prod,
+					SPS_PROD_PERIPHERAL);
+	if (rc)
+		goto sps_reset_err;
+	rc = msmsdcc_sps_init_ep_conn(host, &host->sps.cons,
+					SPS_CONS_PERIPHERAL);
+	if (rc)
+		goto cons_conn_err;
+
+	pr_info("%s: Qualcomm MSM SDCC-BAM at 0x%016llx irq %d\n",
+		mmc_hostname(host->mmc),
+		(unsigned long long)host->bam_memres->start,
+		(unsigned int)host->bam_irqres->start);
+	goto out;
+
+cons_conn_err:
+	msmsdcc_sps_exit_ep_conn(host, &host->sps.prod);
+sps_reset_err:
+	sps_deregister_bam_device(host->sps.bam_handle);
+reg_bam_err:
+	iounmap(host->bam_base);
+out:
+	return rc;
+}
+
+/**
+ * De-initialize SPS HW connected with SDCC core
+ *
+ * This function deinitialize SPS endpoints and then
+ * deregisters BAM resources from SPS driver.
+ *
+ * This function should only be called once typically
+ * during driver remove.
+ *
+ * @host - Pointer to sdcc host structure
+ *
+ */
+static void msmsdcc_sps_exit(struct msmsdcc_host *host)
+{
+	msmsdcc_sps_exit_ep_conn(host, &host->sps.cons);
+	msmsdcc_sps_exit_ep_conn(host, &host->sps.prod);
+	sps_deregister_bam_device(host->sps.bam_handle);
+	iounmap(host->bam_base);
+}
+#endif /* CONFIG_MMC_MSM_SPS_SUPPORT */
+
+static ssize_t
+show_polling(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct mmc_host *mmc = dev_get_drvdata(dev);
+	struct msmsdcc_host *host = mmc_priv(mmc);
+	int poll;
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->lock, flags);
+	poll = !!(mmc->caps & MMC_CAP_NEEDS_POLL);
+	spin_unlock_irqrestore(&host->lock, flags);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", poll);
+}
+
+static ssize_t
+set_polling(struct device *dev, struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	struct mmc_host *mmc = dev_get_drvdata(dev);
+	struct msmsdcc_host *host = mmc_priv(mmc);
+	int value;
+	unsigned long flags;
+
+	sscanf(buf, "%d", &value);
+
+	spin_lock_irqsave(&host->lock, flags);
+	if (value) {
+		mmc->caps |= MMC_CAP_NEEDS_POLL;
+		mmc_detect_change(host->mmc, 0);
+	} else {
+		mmc->caps &= ~MMC_CAP_NEEDS_POLL;
+	}
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	host->polling_enabled = mmc->caps & MMC_CAP_NEEDS_POLL;
+#endif
+	spin_unlock_irqrestore(&host->lock, flags);
+	return count;
+}
+
+static DEVICE_ATTR(polling, S_IRUGO | S_IWUSR,
+		show_polling, set_polling);
+
+static ssize_t
+show_sdcc_to_mem_max_bus_bw(struct device *dev, struct device_attribute *attr,
+			char *buf)
+{
+	struct mmc_host *mmc = dev_get_drvdata(dev);
+	struct msmsdcc_host *host = mmc_priv(mmc);
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			host->msm_bus_vote.is_max_bw_needed);
+}
+
+static ssize_t
+set_sdcc_to_mem_max_bus_bw(struct device *dev, struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	struct mmc_host *mmc = dev_get_drvdata(dev);
+	struct msmsdcc_host *host = mmc_priv(mmc);
+	uint32_t value;
+	unsigned long flags;
+
+	if (!kstrtou32(buf, 0, &value)) {
+		spin_lock_irqsave(&host->lock, flags);
+		host->msm_bus_vote.is_max_bw_needed = !!value;
+		spin_unlock_irqrestore(&host->lock, flags);
+	}
+
+	return count;
+}
+
+static DEVICE_ATTR(max_bus_bw, S_IRUGO | S_IWUSR,
+		show_sdcc_to_mem_max_bus_bw, set_sdcc_to_mem_max_bus_bw);
+
+static struct attribute *dev_attrs[] = {
+	&dev_attr_max_bus_bw.attr,
+	/* if polling is enabled, this will be filled with dev_attr_polling */
+	NULL,
+	NULL,
+};
+
+static struct attribute_group dev_attr_grp = {
+	.attrs = dev_attrs,
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void msmsdcc_early_suspend(struct early_suspend *h)
+{
+	struct msmsdcc_host *host =
+		container_of(h, struct msmsdcc_host, early_suspend);
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->lock, flags);
+	host->polling_enabled = host->mmc->caps & MMC_CAP_NEEDS_POLL;
+	host->mmc->caps &= ~MMC_CAP_NEEDS_POLL;
+	spin_unlock_irqrestore(&host->lock, flags);
+};
+static void msmsdcc_late_resume(struct early_suspend *h)
+{
+	struct msmsdcc_host *host =
+		container_of(h, struct msmsdcc_host, early_suspend);
+	unsigned long flags;
+
+	if (host->polling_enabled) {
+		spin_lock_irqsave(&host->lock, flags);
+		host->mmc->caps |= MMC_CAP_NEEDS_POLL;
+		mmc_detect_change(host->mmc, 0);
+		spin_unlock_irqrestore(&host->lock, flags);
+	}
+};
+#endif
+
+static void msmsdcc_print_regs(const char *name, void __iomem *base,
+			       u32 phys_base, unsigned int no_of_regs)
+{
+	unsigned int i;
+
+	if (!base)
+		return;
+
+	pr_info("===== %s: Register Dumps @phys_base=0x%x, @virt_base=0x%x"
+		" =====\n", name, phys_base, (u32)base);
+	for (i = 0; i < no_of_regs; i = i + 4) {
+		pr_info("Reg=0x%.2x: 0x%.8x, 0x%.8x, 0x%.8x, 0x%.8x\n", i*4,
+			(u32)readl_relaxed(base + i*4),
+			(u32)readl_relaxed(base + ((i+1)*4)),
+			(u32)readl_relaxed(base + ((i+2)*4)),
+			(u32)readl_relaxed(base + ((i+3)*4)));
+	}
+}
+
+static void msmsdcc_dump_sdcc_state(struct msmsdcc_host *host)
+{
+	/* Dump current state of SDCC clocks, power and irq */
+	pr_info("%s: SDCC PWR is %s\n", mmc_hostname(host->mmc),
+		(host->pwr ? "ON" : "OFF"));
+	pr_info("%s: SDCC clks are %s, MCLK rate=%d\n",
+		mmc_hostname(host->mmc), (host->clks_on ? "ON" : "OFF"),
+		(u32)clk_get_rate(host->clk));
+	pr_info("%s: SDCC irq is %s\n", mmc_hostname(host->mmc),
+		(host->sdcc_irq_disabled ? "disabled" : "enabled"));
+
+	/* Now dump SDCC registers. Don't print FIFO registers */
+	if (host->clks_on)
+		msmsdcc_print_regs("SDCC-CORE", host->base,
+				   host->core_memres->start, 28);
+
+	if (host->curr.data) {
+		if (!msmsdcc_is_dma_possible(host, host->curr.data))
+			pr_info("%s: PIO mode\n", mmc_hostname(host->mmc));
+		else if (host->is_dma_mode)
+			pr_info("%s: ADM mode: busy=%d, chnl=%d, crci=%d\n",
+				mmc_hostname(host->mmc), host->dma.busy,
+				host->dma.channel, host->dma.crci);
+		else if (host->is_sps_mode) {
+			if (host->sps.busy && host->clks_on)
+				msmsdcc_print_regs("SDCC-DML", host->dml_base,
+						   host->dml_memres->start,
+						   16);
+			pr_info("%s: SPS mode: busy=%d\n",
+				mmc_hostname(host->mmc), host->sps.busy);
+		}
+
+		pr_info("%s: xfer_size=%d, data_xfered=%d, xfer_remain=%d\n",
+			mmc_hostname(host->mmc), host->curr.xfer_size,
+			host->curr.data_xfered, host->curr.xfer_remain);
+	}
+
+	pr_info("%s: got_dataend=%d, prog_enable=%d,"
+		" wait_for_auto_prog_done=%d, got_auto_prog_done=%d,"
+		" req_tout_ms=%d\n", mmc_hostname(host->mmc),
+		host->curr.got_dataend, host->prog_enable,
+		host->curr.wait_for_auto_prog_done,
+		host->curr.got_auto_prog_done, host->curr.req_tout_ms);
+	msmsdcc_print_rpm_info(host);
+}
+
+static void msmsdcc_req_tout_timer_hdlr(unsigned long data)
+{
+	struct msmsdcc_host *host = (struct msmsdcc_host *)data;
+	struct mmc_request *mrq;
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->lock, flags);
+	if (host->dummy_52_sent) {
+		pr_info("%s: %s: dummy CMD52 timeout\n",
+				mmc_hostname(host->mmc), __func__);
+		host->dummy_52_sent = 0;
+	}
+
+	mrq = host->curr.mrq;
+
+	if (mrq && mrq->cmd) {
+		pr_info("%s: CMD%d: Request timeout\n", mmc_hostname(host->mmc),
+				mrq->cmd->opcode);
+		msmsdcc_dump_sdcc_state(host);
+
+		if (!mrq->cmd->error)
+			mrq->cmd->error = -ETIMEDOUT;
+		host->dummy_52_needed = 0;
+		if (host->curr.data) {
+			if (mrq->data && !mrq->data->error)
+				mrq->data->error = -ETIMEDOUT;
+			host->curr.data_xfered = 0;
+			if (host->dma.sg && host->is_dma_mode) {
+				msm_dmov_flush(host->dma.channel, 0);
+			} else if (host->sps.sg && host->is_sps_mode) {
+				/* Stop current SPS transfer */
+				msmsdcc_sps_exit_curr_xfer(host);
+			} else {
+				msmsdcc_reset_and_restore(host);
+				msmsdcc_stop_data(host);
+				if (mrq->data && mrq->data->stop)
+					msmsdcc_start_command(host,
+							mrq->data->stop, 0);
+				else
+					msmsdcc_request_end(host, mrq);
+			}
+		} else {
+			host->prog_enable = 0;
+			host->curr.wait_for_auto_prog_done = 0;
+			msmsdcc_reset_and_restore(host);
+			msmsdcc_request_end(host, mrq);
+		}
+	}
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static struct mmc_platform_data *msmsdcc_populate_pdata(struct device *dev)
+{
+	int i, ret;
+	struct mmc_platform_data *pdata;
+	struct device_node *np = dev->of_node;
+	u32 bus_width = 0;
+	u32 *clk_table;
+	int clk_table_len;
+	u32 *sup_voltages;
+	int sup_volt_len;
+
+	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata) {
+		dev_err(dev, "could not allocate memory for platform data\n");
+		goto err;
+	}
+
+	of_property_read_u32(np, "qcom,sdcc-bus-width", &bus_width);
+	if (bus_width == 8) {
+		pdata->mmc_bus_width = MMC_CAP_8_BIT_DATA;
+	} else if (bus_width == 4) {
+		pdata->mmc_bus_width = MMC_CAP_4_BIT_DATA;
+	} else {
+		dev_notice(dev, "Invalid bus width, default to 1 bit mode\n");
+		pdata->mmc_bus_width = 0;
+	}
+
+	if (of_get_property(np, "qcom,sdcc-sup-voltages", &sup_volt_len)) {
+		size_t sz;
+		sz = sup_volt_len / sizeof(*sup_voltages);
+		if (sz > 0) {
+			sup_voltages = devm_kzalloc(dev,
+					sz * sizeof(*sup_voltages), GFP_KERNEL);
+			if (!sup_voltages) {
+				dev_err(dev, "No memory for supported voltage\n");
+				goto err;
+			}
+
+			ret = of_property_read_u32_array(np,
+				"qcom,sdcc-sup-voltages", sup_voltages, sz);
+			if (ret < 0) {
+				dev_err(dev, "error while reading voltage"
+						"ranges %d\n", ret);
+				goto err;
+			}
+		} else {
+			dev_err(dev, "No supported voltages\n");
+			goto err;
+		}
+		for (i = 0; i < sz; i += 2) {
+			u32 mask;
+
+			mask = mmc_vddrange_to_ocrmask(sup_voltages[i],
+					sup_voltages[i + 1]);
+			if (!mask)
+				dev_err(dev, "Invalide voltage range %d\n", i);
+			pdata->ocr_mask |= mask;
+		}
+		dev_dbg(dev, "OCR mask=0x%x\n", pdata->ocr_mask);
+	} else {
+		dev_err(dev, "Supported voltage range not specified\n");
+	}
+
+	if (of_get_property(np, "qcom,sdcc-clk-rates", &clk_table_len)) {
+		size_t sz;
+		sz = clk_table_len / sizeof(*clk_table);
+
+		if (sz > 0) {
+			clk_table = devm_kzalloc(dev, sz * sizeof(*clk_table),
+					GFP_KERNEL);
+			if (!clk_table) {
+				dev_err(dev, "No memory for clock table\n");
+				goto err;
+			}
+
+			ret = of_property_read_u32_array(np,
+				"qcom,sdcc-clk-rates", clk_table, sz);
+			if (ret < 0) {
+				dev_err(dev, "error while reading clk"
+						"table %d\n", ret);
+				goto err;
+			}
+		} else {
+			dev_err(dev, "clk_table not specified\n");
+			goto err;
+		}
+		pdata->sup_clk_table = clk_table;
+		pdata->sup_clk_cnt = sz;
+	} else {
+		dev_err(dev, "Supported clock rates not specified\n");
+	}
+
+	if (of_get_property(np, "qcom,sdcc-nonremovable", NULL))
+		pdata->nonremovable = true;
+	if (of_get_property(np, "qcom,sdcc-disable_cmd23", NULL))
+		pdata->disable_cmd23 = true;
+
+	return pdata;
+err:
+	return NULL;
+}
+
 static int
 msmsdcc_probe(struct platform_device *pdev)
 {
-	struct msm_mmc_platform_data *plat = pdev->dev.platform_data;
+	struct mmc_platform_data *plat;
 	struct msmsdcc_host *host;
 	struct mmc_host *mmc;
-	struct resource *cmd_irqres = NULL;
-	struct resource *stat_irqres = NULL;
-	struct resource *memres = NULL;
+	unsigned long flags;
+	struct resource *core_irqres = NULL;
+	struct resource *bam_irqres = NULL;
+	struct resource *core_memres = NULL;
+	struct resource *dml_memres = NULL;
+	struct resource *bam_memres = NULL;
 	struct resource *dmares = NULL;
-	int ret;
+	struct resource *dma_crci_res = NULL;
+	int ret = 0;
+	int i;
+
+	if (pdev->dev.of_node) {
+		plat = msmsdcc_populate_pdata(&pdev->dev);
+		of_property_read_u32((&pdev->dev)->of_node,
+				"cell-index", &pdev->id);
+	} else {
+		plat = pdev->dev.platform_data;
+	}
 
 	/* must have platform data */
 	if (!plat) {
@@ -1183,30 +4737,88 @@
 		goto out;
 	}
 
-	if (pdev->id < 1 || pdev->id > 4)
+	if (pdev->id < 1 || pdev->id > 5)
 		return -EINVAL;
 
+	if (plat->is_sdio_al_client && !plat->sdiowakeup_irq) {
+		pr_err("%s: No wakeup IRQ for sdio_al client\n", __func__);
+		return -EINVAL;
+	}
+
 	if (pdev->resource == NULL || pdev->num_resources < 2) {
 		pr_err("%s: Invalid resource\n", __func__);
 		return -ENXIO;
 	}
+	if (pdev->dev.of_node) {
+		/*
+		 * Device tree iomem resources are only accessible by index.
+		 * index = 0 -> SDCC register interface
+		 * index = 1 -> DML register interface
+		 * index = 2 -> BAM register interface
+		 * IRQ resources:
+		 * index = 0 -> SDCC IRQ
+		 * index = 1 -> BAM IRQ
+		 */
+		core_memres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+		dml_memres = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+		bam_memres = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+		core_irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+		bam_irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+	} else {
+		for (i = 0; i < pdev->num_resources; i++) {
+			if (pdev->resource[i].flags & IORESOURCE_MEM) {
+				if (!strncmp(pdev->resource[i].name,
+						"sdcc_dml_addr",
+						sizeof("sdcc_dml_addr")))
+					dml_memres = &pdev->resource[i];
+				else if (!strncmp(pdev->resource[i].name,
+						"sdcc_bam_addr",
+						sizeof("sdcc_bam_addr")))
+					bam_memres = &pdev->resource[i];
+				else
+					core_memres = &pdev->resource[i];
 
-	memres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
-	cmd_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
-						  "cmd_irq");
-	stat_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
-						   "status_irq");
+			}
+			if (pdev->resource[i].flags & IORESOURCE_IRQ) {
+				if (!strncmp(pdev->resource[i].name,
+						"sdcc_bam_irq",
+						sizeof("sdcc_bam_irq")))
+					bam_irqres = &pdev->resource[i];
+				else
+					core_irqres = &pdev->resource[i];
+			}
+			if (pdev->resource[i].flags & IORESOURCE_DMA) {
+				if (!strncmp(pdev->resource[i].name,
+						"sdcc_dma_chnl",
+						sizeof("sdcc_dma_chnl")))
+					dmares = &pdev->resource[i];
+				else if (!strncmp(pdev->resource[i].name,
+						"sdcc_dma_crci",
+						sizeof("sdcc_dma_crci")))
+					dma_crci_res = &pdev->resource[i];
+			}
+		}
+	}
 
-	if (!cmd_irqres || !memres) {
-		pr_err("%s: Invalid resource\n", __func__);
+	if (!core_irqres || !core_memres) {
+		pr_err("%s: Invalid sdcc core resource\n", __func__);
+		return -ENXIO;
+	}
+
+	/*
+	 * Both BAM and DML memory resource should be preset.
+	 * BAM IRQ resource should also be present.
+	 */
+	if ((bam_memres && !dml_memres) ||
+		(!bam_memres && dml_memres) ||
+		((bam_memres && dml_memres) && !bam_irqres)) {
+		pr_err("%s: Invalid sdcc BAM/DML resource\n", __func__);
 		return -ENXIO;
 	}
 
 	/*
 	 * Setup our host structure
 	 */
-
 	mmc = mmc_alloc_host(sizeof(struct msmsdcc_host), &pdev->dev);
 	if (!mmc) {
 		ret = -ENOMEM;
@@ -1218,185 +4830,452 @@
 	host->plat = plat;
 	host->mmc = mmc;
 	host->curr.cmd = NULL;
-	init_timer(&host->busclk_timer);
-	host->busclk_timer.data = (unsigned long) host;
-	host->busclk_timer.function = msmsdcc_busclk_expired;
 
+	if (!plat->disable_bam && bam_memres && dml_memres && bam_irqres)
+		host->is_sps_mode = 1;
+	else if (dmares)
+		host->is_dma_mode = 1;
 
-	host->cmdpoll = 1;
-
-	host->base = ioremap(memres->start, PAGE_SIZE);
+	host->base = ioremap(core_memres->start,
+			resource_size(core_memres));
 	if (!host->base) {
 		ret = -ENOMEM;
 		goto host_free;
 	}
 
-	host->cmd_irqres = cmd_irqres;
-	host->memres = memres;
+	host->core_irqres = core_irqres;
+	host->bam_irqres = bam_irqres;
+	host->core_memres = core_memres;
+	host->dml_memres = dml_memres;
+	host->bam_memres = bam_memres;
 	host->dmares = dmares;
+	host->dma_crci_res = dma_crci_res;
 	spin_lock_init(&host->lock);
+	mutex_init(&host->clk_mutex);
+
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+	if (plat->embedded_sdio)
+		mmc_set_embedded_sdio_data(mmc,
+					   &plat->embedded_sdio->cis,
+					   &plat->embedded_sdio->cccr,
+					   plat->embedded_sdio->funcs,
+					   plat->embedded_sdio->num_funcs);
+#endif
 
 	tasklet_init(&host->dma_tlet, msmsdcc_dma_complete_tlet,
 			(unsigned long)host);
 
-	/*
-	 * Setup DMA
-	 */
-	if (host->dmares) {
+	tasklet_init(&host->sps.tlet, msmsdcc_sps_complete_tlet,
+			(unsigned long)host);
+	if (host->is_dma_mode) {
+		/* Setup DMA */
 		ret = msmsdcc_init_dma(host);
 		if (ret)
 			goto ioremap_free;
 	} else {
 		host->dma.channel = -1;
+		host->dma.crci = -1;
 	}
 
-	/* Get our clocks */
-	host->pclk = clk_get(&pdev->dev, "sdc_pclk");
-	if (IS_ERR(host->pclk)) {
-		ret = PTR_ERR(host->pclk);
-		goto dma_free;
+	/*
+	 * Setup SDCC clock if derived from Dayatona
+	 * fabric core clock.
+	 */
+	if (plat->pclk_src_dfab) {
+		host->dfab_pclk = clk_get(&pdev->dev, "bus_clk");
+		if (!IS_ERR(host->dfab_pclk)) {
+			/* Set the clock rate to 64MHz for max. performance */
+			ret = clk_set_rate(host->dfab_pclk, 64000000);
+			if (ret)
+				goto dfab_pclk_put;
+			ret = clk_prepare_enable(host->dfab_pclk);
+			if (ret)
+				goto dfab_pclk_put;
+		} else
+			goto dma_free;
 	}
 
-	host->clk = clk_get(&pdev->dev, "sdc_clk");
+	/*
+	 * Setup main peripheral bus clock
+	 */
+	host->pclk = clk_get(&pdev->dev, "iface_clk");
+	if (!IS_ERR(host->pclk)) {
+		ret = clk_prepare_enable(host->pclk);
+		if (ret)
+			goto pclk_put;
+
+		host->pclk_rate = clk_get_rate(host->pclk);
+	}
+
+	/*
+	 * Setup SDC MMC clock
+	 */
+	host->clk = clk_get(&pdev->dev, "core_clk");
 	if (IS_ERR(host->clk)) {
 		ret = PTR_ERR(host->clk);
-		goto pclk_put;
+		goto pclk_disable;
 	}
 
-	ret = clk_set_rate(host->clk, msmsdcc_fmin);
+	ret = clk_set_rate(host->clk, msmsdcc_get_min_sup_clk_rate(host));
 	if (ret) {
 		pr_err("%s: Clock rate set failed (%d)\n", __func__, ret);
 		goto clk_put;
 	}
 
-	/* Enable clocks */
-	ret = msmsdcc_enable_clocks(host);
+	ret = clk_prepare_enable(host->clk);
 	if (ret)
 		goto clk_put;
 
-	host->pclk_rate = clk_get_rate(host->pclk);
 	host->clk_rate = clk_get_rate(host->clk);
+	if (!host->clk_rate)
+		dev_err(&pdev->dev, "Failed to read MCLK\n");
+
+	/*
+	* Lookup the Controller Version, to identify the supported features
+	* Version number read as 0 would indicate SDCC3 or earlier versions
+	*/
+	host->sdcc_version = readl_relaxed(host->base + MCI_VERSION);
+	pr_info("%s: mci-version: %x\n", mmc_hostname(host->mmc),
+		host->sdcc_version);
+	/*
+	 * Set the register write delay according to min. clock frequency
+	 * supported and update later when the host->clk_rate changes.
+	 */
+	host->reg_write_delay =
+		(1 + ((3 * USEC_PER_SEC) /
+		      msmsdcc_get_min_sup_clk_rate(host)));
+
+	host->clks_on = 1;
+	/* Apply Hard reset to SDCC to put it in power on default state */
+	msmsdcc_hard_reset(host);
+
+#define MSM_MMC_DEFAULT_CPUDMA_LATENCY 200 /* usecs */
+	/* pm qos request to prevent apps idle power collapse */
+	if (host->plat->cpu_dma_latency)
+		host->cpu_dma_latency = host->plat->cpu_dma_latency;
+	else
+		host->cpu_dma_latency = MSM_MMC_DEFAULT_CPUDMA_LATENCY;
+	pm_qos_add_request(&host->pm_qos_req_dma,
+			PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);
+
+	ret = msmsdcc_msm_bus_register(host);
+	if (ret)
+		goto pm_qos_remove;
+
+	if (host->msm_bus_vote.client_handle)
+		INIT_DELAYED_WORK(&host->msm_bus_vote.vote_work,
+				  msmsdcc_msm_bus_work);
+
+	ret = msmsdcc_vreg_init(host, true);
+	if (ret) {
+		pr_err("%s: msmsdcc_vreg_init() failed (%d)\n", __func__, ret);
+		goto clk_disable;
+	}
+
+
+	/* Clocks has to be running before accessing SPS/DML HW blocks */
+	if (host->is_sps_mode) {
+		/* Initialize SPS */
+		ret = msmsdcc_sps_init(host);
+		if (ret)
+			goto vreg_deinit;
+		/* Initialize DML */
+		ret = msmsdcc_dml_init(host);
+		if (ret)
+			goto sps_exit;
+	}
+	mmc_dev(mmc)->dma_mask = &dma_mask;
 
 	/*
 	 * Setup MMC host structure
 	 */
 	mmc->ops = &msmsdcc_ops;
-	mmc->f_min = msmsdcc_fmin;
-	mmc->f_max = msmsdcc_fmax;
+	mmc->f_min = msmsdcc_get_min_sup_clk_rate(host);
+	mmc->f_max = msmsdcc_get_max_sup_clk_rate(host);
 	mmc->ocr_avail = plat->ocr_mask;
+	mmc->pm_caps |= MMC_PM_KEEP_POWER | MMC_PM_WAKE_SDIO_IRQ;
+	mmc->caps |= plat->mmc_bus_width;
 
-	if (msmsdcc_4bit)
-		mmc->caps |= MMC_CAP_4_BIT_DATA;
-	if (msmsdcc_sdioirq)
-		mmc->caps |= MMC_CAP_SDIO_IRQ;
 	mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
+	mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE;
 
-	mmc->max_segs = NR_SG;
-	mmc->max_blk_size = 4096;	/* MCI_DATA_CTL BLOCKSIZE up to 4096 */
-	mmc->max_blk_count = 65536;
+	/*
+	 * If we send the CMD23 before multi block write/read command
+	 * then we need not to send CMD12 at the end of the transfer.
+	 * If we don't send the CMD12 then only way to detect the PROG_DONE
+	 * status is to use the AUTO_PROG_DONE status provided by SDCC4
+	 * controller. So let's enable the CMD23 for SDCC4 only.
+	 */
+	if (!plat->disable_cmd23 && host->sdcc_version)
+		mmc->caps |= MMC_CAP_CMD23;
 
-	mmc->max_req_size = 33554432;	/* MCI_DATA_LENGTH is 25 bits */
+	mmc->caps |= plat->uhs_caps;
+	/*
+	 * XPC controls the maximum current in the default speed mode of SDXC
+	 * card. XPC=0 means 100mA (max.) but speed class is not supported.
+	 * XPC=1 means 150mA (max.) and speed class is supported.
+	 */
+	if (plat->xpc_cap)
+		mmc->caps |= (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300 |
+				MMC_CAP_SET_XPC_180);
+
+	mmc->caps2 |= MMC_CAP2_PACKED_WR;
+	mmc->caps2 |= MMC_CAP2_PACKED_WR_CONTROL;
+	mmc->caps2 |= (MMC_CAP2_BOOTPART_NOACC | MMC_CAP2_DETECT_ON_ERR);
+	mmc->caps2 |= MMC_CAP2_SANITIZE;
+
+	if (pdev->dev.of_node) {
+		if (of_get_property((&pdev->dev)->of_node,
+					"qcom,sdcc-hs200", NULL))
+			mmc->caps2 |= MMC_CAP2_HS200_1_8V_SDR;
+	}
+
+	if (plat->nonremovable)
+		mmc->caps |= MMC_CAP_NONREMOVABLE;
+	mmc->caps |= MMC_CAP_SDIO_IRQ;
+
+	mmc->caps2 |= MMC_CAP2_INIT_BKOPS | MMC_CAP2_BKOPS;
+
+	if (plat->is_sdio_al_client)
+		mmc->pm_flags |= MMC_PM_IGNORE_PM_NOTIFY;
+
+	mmc->max_segs = msmsdcc_get_nr_sg(host);
+	mmc->max_blk_size = MMC_MAX_BLK_SIZE;
+	mmc->max_blk_count = MMC_MAX_BLK_CNT;
+
+	mmc->max_req_size = MMC_MAX_REQ_SIZE;
 	mmc->max_seg_size = mmc->max_req_size;
 
-	msmsdcc_writel(host, 0, MMCIMASK0);
-	msmsdcc_writel(host, 0x5e007ff, MMCICLEAR);
+	writel_relaxed(0, host->base + MMCIMASK0);
+	writel_relaxed(MCI_CLEAR_STATIC_MASK, host->base + MMCICLEAR);
+	msmsdcc_sync_reg_wr(host);
 
-	msmsdcc_writel(host, MCI_IRQENABLE, MMCIMASK0);
-	host->saved_irq0mask = MCI_IRQENABLE;
+	writel_relaxed(MCI_IRQENABLE, host->base + MMCIMASK0);
+	mb();
+	host->mci_irqenable = MCI_IRQENABLE;
 
+	ret = request_irq(core_irqres->start, msmsdcc_irq, IRQF_SHARED,
+			  DRIVER_NAME " (cmd)", host);
+	if (ret)
+		goto dml_exit;
+
+	ret = request_irq(core_irqres->start, msmsdcc_pio_irq, IRQF_SHARED,
+			  DRIVER_NAME " (pio)", host);
+	if (ret)
+		goto irq_free;
+
+	/*
+	 * Enable SDCC IRQ only when host is powered on. Otherwise, this
+	 * IRQ is un-necessarily being monitored by MPM (Modem power
+	 * management block) during idle-power collapse.  The MPM will be
+	 * configured to monitor the DATA1 GPIO line with level-low trigger
+	 * and thus depending on the GPIO status, it prevents TCXO shutdown
+	 * during idle-power collapse.
+	 */
+	disable_irq(core_irqres->start);
+	host->sdcc_irq_disabled = 1;
+
+	if (plat->sdiowakeup_irq) {
+		wake_lock_init(&host->sdio_wlock, WAKE_LOCK_SUSPEND,
+				mmc_hostname(mmc));
+		ret = request_irq(plat->sdiowakeup_irq,
+			msmsdcc_platform_sdiowakeup_irq,
+			IRQF_SHARED | IRQF_TRIGGER_LOW,
+			DRIVER_NAME "sdiowakeup", host);
+		if (ret) {
+			pr_err("Unable to get sdio wakeup IRQ %d (%d)\n",
+				plat->sdiowakeup_irq, ret);
+			goto pio_irq_free;
+		} else {
+			spin_lock_irqsave(&host->lock, flags);
+			if (!host->sdio_wakeupirq_disabled) {
+				disable_irq_nosync(plat->sdiowakeup_irq);
+				host->sdio_wakeupirq_disabled = 1;
+			}
+			spin_unlock_irqrestore(&host->lock, flags);
+		}
+	}
+
+	if (host->plat->mpm_sdiowakeup_int) {
+		wake_lock_init(&host->sdio_wlock, WAKE_LOCK_SUSPEND,
+				mmc_hostname(mmc));
+	}
+
+	wake_lock_init(&host->sdio_suspend_wlock, WAKE_LOCK_SUSPEND,
+			mmc_hostname(mmc));
 	/*
 	 * Setup card detect change
 	 */
 
-	memset(&host->timer, 0, sizeof(host->timer));
+	if (plat->status || plat->status_gpio) {
+		if (plat->status)
+			host->oldstat = plat->status(mmc_dev(host->mmc));
+		else
+			host->oldstat = msmsdcc_slot_status(host);
 
-	if (stat_irqres && !(stat_irqres->flags & IORESOURCE_DISABLED)) {
-		unsigned long irqflags = IRQF_SHARED |
-			(stat_irqres->flags & IRQF_TRIGGER_MASK);
+		host->eject = !host->oldstat;
+	}
 
-		host->stat_irq = stat_irqres->start;
-		ret = request_irq(host->stat_irq,
+	if (plat->status_irq) {
+		ret = request_threaded_irq(plat->status_irq, NULL,
 				  msmsdcc_platform_status_irq,
-				  irqflags,
+				  plat->irq_flags,
 				  DRIVER_NAME " (slot)",
 				  host);
 		if (ret) {
-			pr_err("%s: Unable to get slot IRQ %d (%d)\n",
-			       mmc_hostname(mmc), host->stat_irq, ret);
-			goto clk_disable;
+			pr_err("Unable to get slot IRQ %d (%d)\n",
+			       plat->status_irq, ret);
+			goto sdiowakeup_irq_free;
 		}
 	} else if (plat->register_status_notify) {
 		plat->register_status_notify(msmsdcc_status_notify_cb, host);
 	} else if (!plat->status)
 		pr_err("%s: No card detect facilities available\n",
 		       mmc_hostname(mmc));
-	else {
-		init_timer(&host->timer);
-		host->timer.data = (unsigned long)host;
-		host->timer.function = msmsdcc_check_status;
-		host->timer.expires = jiffies + HZ;
-		add_timer(&host->timer);
-	}
-
-	if (plat->status) {
-		host->oldstat = host->plat->status(mmc_dev(host->mmc));
-		host->eject = !host->oldstat;
-	}
-
-	ret = request_irq(cmd_irqres->start, msmsdcc_irq, IRQF_SHARED,
-			  DRIVER_NAME " (cmd)", host);
-	if (ret)
-		goto stat_irq_free;
-
-	ret = request_irq(cmd_irqres->start, msmsdcc_pio_irq, IRQF_SHARED,
-			  DRIVER_NAME " (pio)", host);
-	if (ret)
-		goto cmd_irq_free;
 
 	mmc_set_drvdata(pdev, mmc);
+
+	ret = pm_runtime_set_active(&(pdev)->dev);
+	if (ret < 0)
+		pr_info("%s: %s: failed with error %d", mmc_hostname(mmc),
+				__func__, ret);
+	/*
+	 * There is no notion of suspend/resume for SD/MMC/SDIO
+	 * cards. So host can be suspended/resumed with out
+	 * worrying about its children.
+	 */
+	pm_suspend_ignore_children(&(pdev)->dev, true);
+
+	/*
+	 * MMC/SD/SDIO bus suspend/resume operations are defined
+	 * only for the slots that will be used for non-removable
+	 * media or for all slots when CONFIG_MMC_UNSAFE_RESUME is
+	 * defined. Otherwise, they simply become card removal and
+	 * insertion events during suspend and resume respectively.
+	 * Hence, enable run-time PM only for slots for which bus
+	 * suspend/resume operations are defined.
+	 */
+#ifdef CONFIG_MMC_UNSAFE_RESUME
+	/*
+	 * If this capability is set, MMC core will enable/disable host
+	 * for every claim/release operation on a host. We use this
+	 * notification to increment/decrement runtime pm usage count.
+	 */
+	pm_runtime_enable(&(pdev)->dev);
+#else
+	if (mmc->caps & MMC_CAP_NONREMOVABLE) {
+		pm_runtime_enable(&(pdev)->dev);
+	}
+#endif
+	setup_timer(&host->req_tout_timer, msmsdcc_req_tout_timer_hdlr,
+			(unsigned long)host);
+
 	mmc_add_host(mmc);
 
-	pr_info("%s: Qualcomm MSM SDCC at 0x%016llx irq %d,%d dma %d\n",
-		mmc_hostname(mmc), (unsigned long long)memres->start,
-		(unsigned int) cmd_irqres->start,
-		(unsigned int) host->stat_irq, host->dma.channel);
-	pr_info("%s: 4 bit data mode %s\n", mmc_hostname(mmc),
-		(mmc->caps & MMC_CAP_4_BIT_DATA ? "enabled" : "disabled"));
-	pr_info("%s: MMC clock %u -> %u Hz, PCLK %u Hz\n",
-		mmc_hostname(mmc), msmsdcc_fmin, msmsdcc_fmax, host->pclk_rate);
-	pr_info("%s: Slot eject status = %d\n", mmc_hostname(mmc), host->eject);
-	pr_info("%s: Power save feature enable = %d\n",
-		mmc_hostname(mmc), msmsdcc_pwrsave);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	host->early_suspend.suspend = msmsdcc_early_suspend;
+	host->early_suspend.resume  = msmsdcc_late_resume;
+	host->early_suspend.level   = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+	register_early_suspend(&host->early_suspend);
+#endif
 
-	if (host->dma.channel != -1) {
+	pr_info("%s: Qualcomm MSM SDCC-core at 0x%016llx irq %d,%d dma %d"
+		" dmacrcri %d\n", mmc_hostname(mmc),
+		(unsigned long long)core_memres->start,
+		(unsigned int) core_irqres->start,
+		(unsigned int) plat->status_irq, host->dma.channel,
+		host->dma.crci);
+
+	pr_info("%s: 8 bit data mode %s\n", mmc_hostname(mmc),
+		(mmc->caps & MMC_CAP_8_BIT_DATA ? "enabled" : "disabled"));
+	pr_info("%s: 4 bit data mode %s\n", mmc_hostname(mmc),
+	       (mmc->caps & MMC_CAP_4_BIT_DATA ? "enabled" : "disabled"));
+	pr_info("%s: polling status mode %s\n", mmc_hostname(mmc),
+	       (mmc->caps & MMC_CAP_NEEDS_POLL ? "enabled" : "disabled"));
+	pr_info("%s: MMC clock %u -> %u Hz, PCLK %u Hz\n",
+	       mmc_hostname(mmc), msmsdcc_get_min_sup_clk_rate(host),
+		msmsdcc_get_max_sup_clk_rate(host), host->pclk_rate);
+	pr_info("%s: Slot eject status = %d\n", mmc_hostname(mmc),
+	       host->eject);
+	pr_info("%s: Power save feature enable = %d\n",
+	       mmc_hostname(mmc), msmsdcc_pwrsave);
+
+	if (host->is_dma_mode && host->dma.channel != -1
+			&& host->dma.crci != -1) {
 		pr_info("%s: DM non-cached buffer at %p, dma_addr 0x%.8x\n",
-			mmc_hostname(mmc), host->dma.nc, host->dma.nc_busaddr);
+		       mmc_hostname(mmc), host->dma.nc, host->dma.nc_busaddr);
 		pr_info("%s: DM cmd busaddr 0x%.8x, cmdptr busaddr 0x%.8x\n",
-			mmc_hostname(mmc), host->dma.cmd_busaddr,
-			host->dma.cmdptr_busaddr);
+		       mmc_hostname(mmc), host->dma.cmd_busaddr,
+		       host->dma.cmdptr_busaddr);
+	} else if (host->is_sps_mode) {
+		pr_info("%s: SPS-BAM data transfer mode available\n",
+			mmc_hostname(mmc));
 	} else
 		pr_info("%s: PIO transfer enabled\n", mmc_hostname(mmc));
-	if (host->timer.function)
-		pr_info("%s: Polling status mode enabled\n", mmc_hostname(mmc));
 
+#if defined(CONFIG_DEBUG_FS)
+	msmsdcc_dbg_createhost(host);
+#endif
+	if (!plat->status_irq)
+		dev_attrs[1] = &dev_attr_polling.attr;
+
+	ret = sysfs_create_group(&pdev->dev.kobj, &dev_attr_grp);
+	if (ret)
+		goto platform_irq_free;
 	return 0;
- cmd_irq_free:
-	free_irq(cmd_irqres->start, host);
- stat_irq_free:
-	if (host->stat_irq)
-		free_irq(host->stat_irq, host);
+
+ platform_irq_free:
+	del_timer_sync(&host->req_tout_timer);
+	pm_runtime_disable(&(pdev)->dev);
+	pm_runtime_set_suspended(&(pdev)->dev);
+
+	if (plat->status_irq)
+		free_irq(plat->status_irq, host);
+ sdiowakeup_irq_free:
+	wake_lock_destroy(&host->sdio_suspend_wlock);
+	if (plat->sdiowakeup_irq)
+		free_irq(plat->sdiowakeup_irq, host);
+ pio_irq_free:
+	if (plat->sdiowakeup_irq)
+		wake_lock_destroy(&host->sdio_wlock);
+	free_irq(core_irqres->start, host);
+ irq_free:
+	free_irq(core_irqres->start, host);
+ dml_exit:
+	if (host->is_sps_mode)
+		msmsdcc_dml_exit(host);
+ sps_exit:
+	if (host->is_sps_mode)
+		msmsdcc_sps_exit(host);
+ vreg_deinit:
+	msmsdcc_vreg_init(host, false);
  clk_disable:
-	msmsdcc_disable_clocks(host, 0);
+	clk_disable(host->clk);
+	msmsdcc_msm_bus_unregister(host);
+ pm_qos_remove:
+	if (host->cpu_dma_latency)
+		pm_qos_remove_request(&host->pm_qos_req_dma);
  clk_put:
 	clk_put(host->clk);
+ pclk_disable:
+	if (!IS_ERR(host->pclk))
+		clk_disable_unprepare(host->pclk);
  pclk_put:
-	clk_put(host->pclk);
-dma_free:
-	if (host->dmares)
-		dma_free_coherent(NULL, sizeof(struct msmsdcc_nc_dmadata),
-					host->dma.nc, host->dma.nc_busaddr);
-ioremap_free:
-	tasklet_kill(&host->dma_tlet);
+	if (!IS_ERR(host->pclk))
+		clk_put(host->pclk);
+	if (!IS_ERR_OR_NULL(host->dfab_pclk))
+		clk_disable_unprepare(host->dfab_pclk);
+ dfab_pclk_put:
+	if (!IS_ERR_OR_NULL(host->dfab_pclk))
+		clk_put(host->dfab_pclk);
+ dma_free:
+	if (host->is_dma_mode) {
+		if (host->dmares)
+			dma_free_coherent(NULL,
+				sizeof(struct msmsdcc_nc_dmadata),
+				host->dma.nc, host->dma.nc_busaddr);
+	}
+ ioremap_free:
 	iounmap(host->base);
  host_free:
 	mmc_free_host(mmc);
@@ -1404,83 +5283,515 @@
 	return ret;
 }
 
-#ifdef CONFIG_PM
-#ifdef CONFIG_MMC_MSM7X00A_RESUME_IN_WQ
-static void
-do_resume_work(struct work_struct *work)
+static int msmsdcc_remove(struct platform_device *pdev)
 {
-	struct msmsdcc_host *host =
-		container_of(work, struct msmsdcc_host, resume_task);
-	struct mmc_host	*mmc = host->mmc;
+	struct mmc_host *mmc = mmc_get_drvdata(pdev);
+	struct mmc_platform_data *plat;
+	struct msmsdcc_host *host;
 
-	if (mmc) {
-		mmc_resume_host(mmc);
-		if (host->stat_irq)
-			enable_irq(host->stat_irq);
+	if (!mmc)
+		return -ENXIO;
+
+	if (pm_runtime_suspended(&(pdev)->dev))
+		pm_runtime_resume(&(pdev)->dev);
+
+	host = mmc_priv(mmc);
+
+	DBG(host, "Removing SDCC device = %d\n", pdev->id);
+	plat = host->plat;
+
+	if (!plat->status_irq)
+		sysfs_remove_group(&pdev->dev.kobj, &dev_attr_grp);
+
+	del_timer_sync(&host->req_tout_timer);
+	tasklet_kill(&host->dma_tlet);
+	tasklet_kill(&host->sps.tlet);
+	mmc_remove_host(mmc);
+
+	if (plat->status_irq)
+		free_irq(plat->status_irq, host);
+
+	wake_lock_destroy(&host->sdio_suspend_wlock);
+	if (plat->sdiowakeup_irq) {
+		wake_lock_destroy(&host->sdio_wlock);
+		irq_set_irq_wake(plat->sdiowakeup_irq, 0);
+		free_irq(plat->sdiowakeup_irq, host);
 	}
+
+	free_irq(host->core_irqres->start, host);
+	free_irq(host->core_irqres->start, host);
+
+	clk_put(host->clk);
+	if (!IS_ERR(host->pclk))
+		clk_put(host->pclk);
+	if (!IS_ERR_OR_NULL(host->dfab_pclk))
+		clk_put(host->dfab_pclk);
+
+	if (host->cpu_dma_latency)
+		pm_qos_remove_request(&host->pm_qos_req_dma);
+
+	if (host->msm_bus_vote.client_handle) {
+		msmsdcc_msm_bus_cancel_work_and_set_vote(host, NULL);
+		msmsdcc_msm_bus_unregister(host);
+	}
+
+	msmsdcc_vreg_init(host, false);
+
+	if (host->is_dma_mode) {
+		if (host->dmares)
+			dma_free_coherent(NULL,
+					sizeof(struct msmsdcc_nc_dmadata),
+					host->dma.nc, host->dma.nc_busaddr);
+	}
+
+	if (host->is_sps_mode) {
+		msmsdcc_dml_exit(host);
+		msmsdcc_sps_exit(host);
+	}
+
+	iounmap(host->base);
+	mmc_free_host(mmc);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	unregister_early_suspend(&host->early_suspend);
+#endif
+	pm_runtime_disable(&(pdev)->dev);
+	pm_runtime_set_suspended(&(pdev)->dev);
+
+	return 0;
+}
+
+#ifdef CONFIG_MSM_SDIO_AL
+int msmsdcc_sdio_al_lpm(struct mmc_host *mmc, bool enable)
+{
+	struct msmsdcc_host *host = mmc_priv(mmc);
+	unsigned long flags;
+
+	mutex_lock(&host->clk_mutex);
+	spin_lock_irqsave(&host->lock, flags);
+	pr_debug("%s: %sabling LPM\n", mmc_hostname(mmc),
+			enable ? "En" : "Dis");
+
+	if (enable) {
+		if (!host->sdcc_irq_disabled) {
+			writel_relaxed(0, host->base + MMCIMASK0);
+			disable_irq_nosync(host->core_irqres->start);
+			host->sdcc_irq_disabled = 1;
+		}
+
+		if (host->clks_on) {
+			spin_unlock_irqrestore(&host->lock, flags);
+			msmsdcc_setup_clocks(host, false);
+			spin_lock_irqsave(&host->lock, flags);
+			host->clks_on = 0;
+		}
+
+		if (host->plat->sdio_lpm_gpio_setup &&
+				!host->sdio_gpio_lpm) {
+			spin_unlock_irqrestore(&host->lock, flags);
+			host->plat->sdio_lpm_gpio_setup(mmc_dev(mmc), 0);
+			spin_lock_irqsave(&host->lock, flags);
+			host->sdio_gpio_lpm = 1;
+		}
+
+		if (host->sdio_wakeupirq_disabled) {
+			msmsdcc_enable_irq_wake(host);
+			enable_irq(host->plat->sdiowakeup_irq);
+			host->sdio_wakeupirq_disabled = 0;
+		}
+	} else {
+		if (!host->sdio_wakeupirq_disabled) {
+			disable_irq_nosync(host->plat->sdiowakeup_irq);
+			host->sdio_wakeupirq_disabled = 1;
+			msmsdcc_disable_irq_wake(host);
+		}
+
+		if (host->plat->sdio_lpm_gpio_setup &&
+				host->sdio_gpio_lpm) {
+			spin_unlock_irqrestore(&host->lock, flags);
+			host->plat->sdio_lpm_gpio_setup(mmc_dev(mmc), 1);
+			spin_lock_irqsave(&host->lock, flags);
+			host->sdio_gpio_lpm = 0;
+		}
+
+		if (!host->clks_on) {
+			spin_unlock_irqrestore(&host->lock, flags);
+			msmsdcc_setup_clocks(host, true);
+			spin_lock_irqsave(&host->lock, flags);
+			host->clks_on = 1;
+		}
+
+		if (host->sdcc_irq_disabled) {
+			writel_relaxed(host->mci_irqenable,
+				       host->base + MMCIMASK0);
+			mb();
+			enable_irq(host->core_irqres->start);
+			host->sdcc_irq_disabled = 0;
+		}
+	}
+	spin_unlock_irqrestore(&host->lock, flags);
+	mutex_unlock(&host->clk_mutex);
+	return 0;
+}
+#else
+int msmsdcc_sdio_al_lpm(struct mmc_host *mmc, bool enable)
+{
+	return 0;
 }
 #endif
 
-
+#ifdef CONFIG_PM
 static int
-msmsdcc_suspend(struct platform_device *dev, pm_message_t state)
+msmsdcc_runtime_suspend(struct device *dev)
 {
-	struct mmc_host *mmc = mmc_get_drvdata(dev);
+	struct mmc_host *mmc = dev_get_drvdata(dev);
+	struct msmsdcc_host *host = mmc_priv(mmc);
 	int rc = 0;
+	unsigned long flags;
 
-	if (mmc) {
-		struct msmsdcc_host *host = mmc_priv(mmc);
-
-		if (host->stat_irq)
-			disable_irq(host->stat_irq);
-
-		if (mmc->card && mmc->card->type != MMC_TYPE_SDIO)
-			rc = mmc_suspend_host(mmc);
-		if (!rc)
-			msmsdcc_writel(host, 0, MMCIMASK0);
-		if (host->clks_on)
-			msmsdcc_disable_clocks(host, 0);
+	if (host->plat->is_sdio_al_client) {
+		rc = 0;
+		goto out;
 	}
+
+	pr_debug("%s: %s: start\n", mmc_hostname(mmc), __func__);
+	if (mmc) {
+		host->sdcc_suspending = 1;
+		mmc->suspend_task = current;
+
+		/*
+		 * MMC core thinks that host is disabled by now since
+		 * runtime suspend is scheduled after msmsdcc_disable()
+		 * is called. Thus, MMC core will try to enable the host
+		 * while suspending it. This results in a synchronous
+		 * runtime resume request while in runtime suspending
+		 * context and hence inorder to complete this resume
+		 * requet, it will wait for suspend to be complete,
+		 * but runtime suspend also can not proceed further
+		 * until the host is resumed. Thus, it leads to a hang.
+		 * Hence, increase the pm usage count before suspending
+		 * the host so that any resume requests after this will
+		 * simple become pm usage counter increment operations.
+		 */
+		pm_runtime_get_noresume(dev);
+		/* If there is pending detect work abort runtime suspend */
+		if (unlikely(work_busy(&mmc->detect.work)))
+			rc = -EAGAIN;
+		else
+			rc = mmc_suspend_host(mmc);
+		pm_runtime_put_noidle(dev);
+
+		if (!rc) {
+			spin_lock_irqsave(&host->lock, flags);
+			host->sdcc_suspended = true;
+			spin_unlock_irqrestore(&host->lock, flags);
+			if (mmc->card && mmc_card_sdio(mmc->card) &&
+				mmc->ios.clock) {
+#ifdef CONFIG_MMC_CLKGATE
+				/*
+				 * If SDIO function driver doesn't want
+				 * to power off the card, atleast turn off
+				 * clocks to allow deep sleep (TCXO shutdown).
+				 */
+				mmc_host_clk_hold(mmc);
+				spin_lock_irqsave(&mmc->clk_lock, flags);
+				mmc->clk_old = mmc->ios.clock;
+				mmc->ios.clock = 0;
+				mmc->clk_gated = true;
+				spin_unlock_irqrestore(&mmc->clk_lock, flags);
+				mmc_set_ios(mmc);
+				mmc_host_clk_release(mmc);
+#endif
+			}
+		}
+		host->sdcc_suspending = 0;
+		mmc->suspend_task = NULL;
+		if (rc && wake_lock_active(&host->sdio_suspend_wlock))
+			wake_unlock(&host->sdio_suspend_wlock);
+	}
+	pr_debug("%s: %s: ends with err=%d\n", mmc_hostname(mmc), __func__, rc);
+out:
+	/* set bus bandwidth to 0 immediately */
+	msmsdcc_msm_bus_cancel_work_and_set_vote(host, NULL);
 	return rc;
 }
 
 static int
-msmsdcc_resume(struct platform_device *dev)
+msmsdcc_runtime_resume(struct device *dev)
 {
-	struct mmc_host *mmc = mmc_get_drvdata(dev);
+	struct mmc_host *mmc = dev_get_drvdata(dev);
+	struct msmsdcc_host *host = mmc_priv(mmc);
+	unsigned long flags;
 
+	if (host->plat->is_sdio_al_client)
+		return 0;
+
+	pr_debug("%s: %s: start\n", mmc_hostname(mmc), __func__);
 	if (mmc) {
-		struct msmsdcc_host *host = mmc_priv(mmc);
+		if (mmc->card && mmc_card_sdio(mmc->card) &&
+				mmc_card_keep_power(mmc)) {
+			mmc_host_clk_hold(mmc);
+			mmc->ios.clock = host->clk_rate;
+			mmc_set_ios(mmc);
+			mmc_host_clk_release(mmc);
+		}
 
-		msmsdcc_enable_clocks(host);
+		mmc_resume_host(mmc);
 
-		msmsdcc_writel(host, host->saved_irq0mask, MMCIMASK0);
+		/*
+		 * FIXME: Clearing of flags must be handled in clients
+		 * resume handler.
+		 */
+		spin_lock_irqsave(&host->lock, flags);
+		mmc->pm_flags = 0;
+		host->sdcc_suspended = false;
+		spin_unlock_irqrestore(&host->lock, flags);
 
-		if (mmc->card && mmc->card->type != MMC_TYPE_SDIO)
-			mmc_resume_host(mmc);
-		if (host->stat_irq)
-			enable_irq(host->stat_irq);
-#if BUSCLK_PWRSAVE
-		msmsdcc_disable_clocks(host, 1);
-#endif
+		/*
+		 * After resuming the host wait for sometime so that
+		 * the SDIO work will be processed.
+		 */
+		if (mmc->card && mmc_card_sdio(mmc->card)) {
+			if ((host->plat->mpm_sdiowakeup_int ||
+					host->plat->sdiowakeup_irq) &&
+					wake_lock_active(&host->sdio_wlock))
+				wake_lock_timeout(&host->sdio_wlock, 1);
+		}
+
+		wake_unlock(&host->sdio_suspend_wlock);
 	}
+	pr_debug("%s: %s: end\n", mmc_hostname(mmc), __func__);
 	return 0;
 }
+
+static int msmsdcc_runtime_idle(struct device *dev)
+{
+	struct mmc_host *mmc = dev_get_drvdata(dev);
+	struct msmsdcc_host *host = mmc_priv(mmc);
+
+	if (host->plat->is_sdio_al_client)
+		return 0;
+
+	/* Idle timeout is not configurable for now */
+	pm_schedule_suspend(dev, MSM_MMC_IDLE_TIMEOUT);
+
+	return -EAGAIN;
+}
+
+static int msmsdcc_pm_suspend(struct device *dev)
+{
+	struct mmc_host *mmc = dev_get_drvdata(dev);
+	struct msmsdcc_host *host = mmc_priv(mmc);
+	int rc = 0;
+
+	if (host->plat->is_sdio_al_client)
+		return 0;
+
+
+	if (host->plat->status_irq)
+		disable_irq(host->plat->status_irq);
+
+	if (!pm_runtime_suspended(dev))
+		rc = msmsdcc_runtime_suspend(dev);
+
+	return rc;
+}
+
+static int msmsdcc_suspend_noirq(struct device *dev)
+{
+	struct mmc_host *mmc = dev_get_drvdata(dev);
+	struct msmsdcc_host *host = mmc_priv(mmc);
+	int rc = 0;
+
+	/*
+	 * After platform suspend there may be active request
+	 * which might have enabled clocks. For example, in SDIO
+	 * case, ksdioirq thread might have scheduled after sdcc
+	 * suspend but before system freeze. In that case abort
+	 * suspend and retry instead of keeping the clocks on
+	 * during suspend and not allowing TCXO.
+	 */
+
+	if (host->clks_on && !host->plat->is_sdio_al_client) {
+		pr_warn("%s: clocks are on after suspend, aborting system "
+				"suspend\n", mmc_hostname(mmc));
+		rc = -EAGAIN;
+	}
+
+	return rc;
+}
+
+static int msmsdcc_pm_resume(struct device *dev)
+{
+	struct mmc_host *mmc = dev_get_drvdata(dev);
+	struct msmsdcc_host *host = mmc_priv(mmc);
+	int rc = 0;
+
+	if (host->plat->is_sdio_al_client)
+		return 0;
+
+	if (mmc->card && mmc_card_sdio(mmc->card))
+		rc = msmsdcc_runtime_resume(dev);
+	else
+		host->pending_resume = true;
+
+	if (host->plat->status_irq) {
+		msmsdcc_check_status((unsigned long)host);
+		enable_irq(host->plat->status_irq);
+	}
+
+	return rc;
+}
+
 #else
-#define msmsdcc_suspend	0
-#define msmsdcc_resume 0
+static int msmsdcc_runtime_suspend(struct device *dev)
+{
+	return 0;
+}
+static int msmsdcc_runtime_idle(struct device *dev)
+{
+	return 0;
+}
+static int msmsdcc_pm_suspend(struct device *dev)
+{
+	return 0;
+}
+static int msmsdcc_pm_resume(struct device *dev)
+{
+	return 0;
+}
+static int msmsdcc_suspend_noirq(struct device *dev)
+{
+	return 0;
+}
+static int msmsdcc_runtime_resume(struct device *dev)
+{
+	return 0;
+}
 #endif
 
+static const struct dev_pm_ops msmsdcc_dev_pm_ops = {
+	.runtime_suspend = msmsdcc_runtime_suspend,
+	.runtime_resume  = msmsdcc_runtime_resume,
+	.runtime_idle    = msmsdcc_runtime_idle,
+	.suspend 	 = msmsdcc_pm_suspend,
+	.resume		 = msmsdcc_pm_resume,
+	.suspend_noirq	 = msmsdcc_suspend_noirq,
+};
+
+static const struct of_device_id msmsdcc_dt_match[] = {
+	{.compatible = "qcom,msm-sdcc"},
+
+};
+MODULE_DEVICE_TABLE(of, msmsdcc_dt_match);
+
 static struct platform_driver msmsdcc_driver = {
 	.probe		= msmsdcc_probe,
-	.suspend	= msmsdcc_suspend,
-	.resume		= msmsdcc_resume,
+	.remove		= msmsdcc_remove,
 	.driver		= {
 		.name	= "msm_sdcc",
+		.pm	= &msmsdcc_dev_pm_ops,
+		.of_match_table = msmsdcc_dt_match,
 	},
 };
 
-module_platform_driver(msmsdcc_driver);
+static int __init msmsdcc_init(void)
+{
+#if defined(CONFIG_DEBUG_FS)
+	int ret = 0;
+	ret = msmsdcc_dbg_init();
+	if (ret) {
+		pr_err("Failed to create debug fs dir \n");
+		return ret;
+	}
+#endif
+	return platform_driver_register(&msmsdcc_driver);
+}
 
-MODULE_DESCRIPTION("Qualcomm MSM 7X00A Multimedia Card Interface driver");
+static void __exit msmsdcc_exit(void)
+{
+	platform_driver_unregister(&msmsdcc_driver);
+
+#if defined(CONFIG_DEBUG_FS)
+	debugfs_remove(debugfs_file);
+	debugfs_remove(debugfs_dir);
+#endif
+}
+
+module_init(msmsdcc_init);
+module_exit(msmsdcc_exit);
+
+MODULE_DESCRIPTION("Qualcomm Multimedia Card Interface driver");
 MODULE_LICENSE("GPL");
+
+#if defined(CONFIG_DEBUG_FS)
+
+static int
+msmsdcc_dbg_state_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t
+msmsdcc_dbg_state_read(struct file *file, char __user *ubuf,
+		       size_t count, loff_t *ppos)
+{
+	struct msmsdcc_host *host = (struct msmsdcc_host *) file->private_data;
+	char buf[200];
+	int max, i;
+
+	i = 0;
+	max = sizeof(buf) - 1;
+
+	i += scnprintf(buf + i, max - i, "STAT: %p %p %p\n", host->curr.mrq,
+		       host->curr.cmd, host->curr.data);
+	if (host->curr.cmd) {
+		struct mmc_command *cmd = host->curr.cmd;
+
+		i += scnprintf(buf + i, max - i, "CMD : %.8x %.8x %.8x\n",
+			      cmd->opcode, cmd->arg, cmd->flags);
+	}
+	if (host->curr.data) {
+		struct mmc_data *data = host->curr.data;
+		i += scnprintf(buf + i, max - i,
+			      "DAT0: %.8x %.8x %.8x %.8x %.8x %.8x\n",
+			      data->timeout_ns, data->timeout_clks,
+			      data->blksz, data->blocks, data->error,
+			      data->flags);
+		i += scnprintf(buf + i, max - i, "DAT1: %.8x %.8x %.8x %p\n",
+			      host->curr.xfer_size, host->curr.xfer_remain,
+			      host->curr.data_xfered, host->dma.sg);
+	}
+
+	return simple_read_from_buffer(ubuf, count, ppos, buf, i);
+}
+
+static const struct file_operations msmsdcc_dbg_state_ops = {
+	.read	= msmsdcc_dbg_state_read,
+	.open	= msmsdcc_dbg_state_open,
+};
+
+static void msmsdcc_dbg_createhost(struct msmsdcc_host *host)
+{
+	if (debugfs_dir) {
+		debugfs_file = debugfs_create_file(mmc_hostname(host->mmc),
+							0644, debugfs_dir, host,
+							&msmsdcc_dbg_state_ops);
+	}
+}
+
+static int __init msmsdcc_dbg_init(void)
+{
+	int err;
+
+	debugfs_dir = debugfs_create_dir("msmsdcc", 0);
+	if (IS_ERR(debugfs_dir)) {
+		err = PTR_ERR(debugfs_dir);
+		debugfs_dir = NULL;
+		return err;
+	}
+
+	return 0;
+}
+#endif
diff --git a/drivers/mmc/host/msm_sdcc.h b/drivers/mmc/host/msm_sdcc.h
index 402028d..ecf4950 100644
--- a/drivers/mmc/host/msm_sdcc.h
+++ b/drivers/mmc/host/msm_sdcc.h
@@ -2,6 +2,7 @@
  *  linux/drivers/mmc/host/msmsdcc.h - QCT MSM7K SDC Controller
  *
  *  Copyright (C) 2008 Google, All Rights Reserved.
+ *  Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -13,10 +14,24 @@
 #ifndef _MSM_SDCC_H
 #define _MSM_SDCC_H
 
-#define MSMSDCC_CRCI_SDC1	6
-#define MSMSDCC_CRCI_SDC2	7
-#define MSMSDCC_CRCI_SDC3	12
-#define MSMSDCC_CRCI_SDC4	13
+#include <linux/types.h>
+
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sdio.h>
+#include <linux/scatterlist.h>
+#include <linux/dma-mapping.h>
+#include <linux/wakelock.h>
+#include <linux/earlysuspend.h>
+#include <linux/pm_qos.h>
+#include <mach/sps.h>
+
+#include <asm/sizes.h>
+#include <asm/mach/mmc.h>
+#include <mach/dma.h>
 
 #define MMCIPOWER		0x000
 #define MCI_PWR_OFF		0x00
@@ -27,10 +42,13 @@
 #define MMCICLOCK		0x004
 #define MCI_CLK_ENABLE		(1 << 8)
 #define MCI_CLK_PWRSAVE		(1 << 9)
-#define MCI_CLK_WIDEBUS		(1 << 10)
+#define MCI_CLK_WIDEBUS_1	(0 << 10)
+#define MCI_CLK_WIDEBUS_4	(2 << 10)
+#define MCI_CLK_WIDEBUS_8	(3 << 10)
 #define MCI_CLK_FLOWENA		(1 << 12)
 #define MCI_CLK_INVERTOUT	(1 << 13)
-#define MCI_CLK_SELECTIN	(1 << 14)
+#define MCI_CLK_SELECTIN	(1 << 15)
+#define IO_PAD_PWR_SWITCH	(1 << 21)
 
 #define MMCIARGUMENT		0x008
 #define MMCICOMMAND		0x00c
@@ -44,6 +62,7 @@
 #define MCI_CSPM_MCIABORT	(1 << 13)
 #define MCI_CSPM_CCSENABLE	(1 << 14)
 #define MCI_CSPM_CCSDISABLE	(1 << 15)
+#define MCI_CSPM_AUTO_CMD19	(1 << 16)
 
 
 #define MMCIRESPCMD		0x010
@@ -59,6 +78,9 @@
 #define MCI_DPSM_DIRECTION	(1 << 1)
 #define MCI_DPSM_MODE		(1 << 2)
 #define MCI_DPSM_DMAENABLE	(1 << 3)
+#define MCI_DATA_PEND		(1 << 17)
+#define MCI_AUTO_PROG_DONE	(1 << 19)
+#define MCI_RX_DATA_PEND	(1 << 20)
 
 #define MMCIDATACNT		0x030
 #define MMCISTATUS		0x034
@@ -86,8 +108,9 @@
 #define MCI_SDIOINTR		(1 << 22)
 #define MCI_PROGDONE		(1 << 23)
 #define MCI_ATACMDCOMPL		(1 << 24)
-#define MCI_SDIOINTOPER		(1 << 25)
+#define MCI_SDIOINTROPE		(1 << 25)
 #define MCI_CCSTIMEOUT		(1 << 26)
+#define MCI_AUTOCMD19TIMEOUT	(1 << 30)
 
 #define MMCICLEAR		0x038
 #define MCI_CMDCRCFAILCLR	(1 << 0)
@@ -99,8 +122,23 @@
 #define MCI_CMDRESPENDCLR	(1 << 6)
 #define MCI_CMDSENTCLR		(1 << 7)
 #define MCI_DATAENDCLR		(1 << 8)
+#define MCI_STARTBITERRCLR	(1 << 9)
 #define MCI_DATABLOCKENDCLR	(1 << 10)
 
+#define MCI_SDIOINTRCLR		(1 << 22)
+#define MCI_PROGDONECLR		(1 << 23)
+#define MCI_ATACMDCOMPLCLR	(1 << 24)
+#define MCI_SDIOINTROPECLR	(1 << 25)
+#define MCI_CCSTIMEOUTCLR 	(1 << 26)
+
+#define MCI_CLEAR_STATIC_MASK	\
+	(MCI_CMDCRCFAILCLR|MCI_DATACRCFAILCLR|MCI_CMDTIMEOUTCLR|\
+	MCI_DATATIMEOUTCLR|MCI_TXUNDERRUNCLR|MCI_RXOVERRUNCLR|  \
+	MCI_CMDRESPENDCLR|MCI_CMDSENTCLR|MCI_DATAENDCLR|	\
+	MCI_STARTBITERRCLR|MCI_DATABLOCKENDCLR|MCI_SDIOINTRCLR|	\
+	MCI_SDIOINTROPECLR|MCI_PROGDONECLR|MCI_ATACMDCOMPLCLR|	\
+	MCI_CCSTIMEOUTCLR)
+
 #define MMCIMASK0		0x03c
 #define MCI_CMDCRCFAILMASK	(1 << 0)
 #define MCI_DATACRCFAILMASK	(1 << 1)
@@ -128,23 +166,42 @@
 #define MCI_ATACMDCOMPLMASK	(1 << 24)
 #define MCI_SDIOINTOPERMASK	(1 << 25)
 #define MCI_CCSTIMEOUTMASK	(1 << 26)
+#define MCI_AUTOCMD19TIMEOUTMASK (1 << 30)
 
 #define MMCIMASK1		0x040
 #define MMCIFIFOCNT		0x044
+#define MCI_VERSION		0x050
 #define MCICCSTIMER		0x058
+#define MCI_DLL_CONFIG		0x060
+#define MCI_DLL_EN		(1 << 16)
+#define MCI_CDR_EN		(1 << 17)
+#define MCI_CK_OUT_EN		(1 << 18)
+#define MCI_CDR_EXT_EN		(1 << 19)
+#define MCI_DLL_PDN		(1 << 29)
+#define MCI_DLL_RST		(1 << 30)
+
+#define MCI_DLL_STATUS		0x068
+#define MCI_DLL_LOCK		(1 << 7)
+
+#define MCI_STATUS2		0x06C
+#define MCI_MCLK_REG_WR_ACTIVE	(1 << 0)
 
 #define MMCIFIFO		0x080 /* to 0x0bc */
 
+#define MCI_TEST_INPUT		0x0D4
+
 #define MCI_IRQENABLE	\
 	(MCI_CMDCRCFAILMASK|MCI_DATACRCFAILMASK|MCI_CMDTIMEOUTMASK|	\
 	MCI_DATATIMEOUTMASK|MCI_TXUNDERRUNMASK|MCI_RXOVERRUNMASK|	\
-	MCI_CMDRESPENDMASK|MCI_CMDSENTMASK|MCI_DATAENDMASK|MCI_PROGDONEMASK)
+	MCI_CMDRESPENDMASK|MCI_CMDSENTMASK|MCI_DATAENDMASK|		\
+	MCI_PROGDONEMASK|MCI_AUTOCMD19TIMEOUTMASK)
 
-#define MCI_IRQ_PIO \
-	(MCI_RXDATAAVLBLMASK | MCI_TXDATAAVLBLMASK | MCI_RXFIFOEMPTYMASK | \
-	 MCI_TXFIFOEMPTYMASK | MCI_RXFIFOFULLMASK | MCI_TXFIFOFULLMASK | \
-	 MCI_RXFIFOHALFFULLMASK | MCI_TXFIFOHALFEMPTYMASK | \
-	 MCI_RXACTIVEMASK | MCI_TXACTIVEMASK)
+#define MCI_IRQ_PIO 	\
+	(MCI_RXDATAAVLBLMASK | MCI_TXDATAAVLBLMASK | 	\
+	MCI_RXFIFOEMPTYMASK | MCI_TXFIFOEMPTYMASK | MCI_RXFIFOFULLMASK |\
+	MCI_TXFIFOFULLMASK | MCI_RXFIFOHALFFULLMASK |			\
+	MCI_TXFIFOHALFEMPTYMASK | MCI_RXACTIVEMASK | MCI_TXACTIVEMASK)
+
 /*
  * The size of the FIFO in bytes.
  */
@@ -152,12 +209,52 @@
 
 #define MCI_FIFOHALFSIZE (MCI_FIFOSIZE / 2)
 
-#define NR_SG		32
+#define NR_SG		128
+
+#define MSM_MMC_IDLE_TIMEOUT	5000 /* msecs */
+
+/* Set the request timeout to 10secs */
+#define MSM_MMC_REQ_TIMEOUT	10000 /* msecs */
+#define MSM_MMC_DISABLE_TIMEOUT        200 /* msecs */
+
+/*
+ * Controller HW limitations
+ */
+#define MCI_DATALENGTH_BITS	25
+#define MMC_MAX_REQ_SIZE	((1 << MCI_DATALENGTH_BITS) - 1)
+/* MCI_DATA_CTL BLOCKSIZE up to 4096 */
+#define MMC_MAX_BLK_SIZE	4096
+#define MMC_MIN_BLK_SIZE	512
+#define MMC_MAX_BLK_CNT		(MMC_MAX_REQ_SIZE / MMC_MIN_BLK_SIZE)
+
+/* 64KiB */
+#define MAX_SG_SIZE		(64 * 1024)
+#define MAX_NR_SG_DMA_PIO	(MMC_MAX_REQ_SIZE / MAX_SG_SIZE)
+
+/*
+ * BAM limitations
+ */
+/* upto 16 bits (64K - 1) */
+#define SPS_MAX_DESC_FIFO_SIZE	65535
+/* 16KiB */
+#define SPS_MAX_DESC_SIZE	(16 * 1024)
+/* Each descriptor is of length 8 bytes */
+#define SPS_MAX_DESC_LENGTH	8
+#define SPS_MAX_DESCS		(SPS_MAX_DESC_FIFO_SIZE / SPS_MAX_DESC_LENGTH)
+
+/*
+ * DMA limitations
+ */
+/* upto 16 bits (64K - 1) */
+#define MMC_MAX_DMA_ROWS (64 * 1024 - 1)
+#define MMC_MAX_DMA_BOX_LENGTH (MMC_MAX_DMA_ROWS * MCI_FIFOSIZE)
+#define MMC_MAX_DMA_CMDS (MAX_NR_SG_DMA_PIO * (MMC_MAX_REQ_SIZE / \
+		MMC_MAX_DMA_BOX_LENGTH))
 
 struct clk;
 
 struct msmsdcc_nc_dmadata {
-	dmov_box	cmd[NR_SG];
+	dmov_box	cmd[MMC_MAX_DMA_CMDS];
 	uint32_t	cmdptr;
 };
 
@@ -174,17 +271,18 @@
 	int				num_ents;
 
 	int				channel;
+	int				crci;
 	struct msmsdcc_host		*host;
 	int				busy; /* Set if DM is busy */
-	int				active;
-	unsigned int			result;
+	unsigned int 			result;
 	struct msm_dmov_errdata		err;
 };
 
 struct msmsdcc_pio_data {
-	struct scatterlist	*sg;
-	unsigned int		sg_len;
-	unsigned int		sg_off;
+	struct sg_mapping_iter		sg_miter;
+	char				bounce_buf[4];
+	/* valid bytes in bounce_buf */
+	int				bounce_buf_len;
 };
 
 struct msmsdcc_curr_req {
@@ -195,31 +293,65 @@
 	unsigned int		xfer_remain;	/* Bytes remaining to send */
 	unsigned int		data_xfered;	/* Bytes acked by BLKEND irq */
 	int			got_dataend;
+	int			wait_for_auto_prog_done;
+	int			got_auto_prog_done;
+	bool			use_wr_data_pend;
 	int			user_pages;
+	u32			req_tout_ms;
 };
 
-struct msmsdcc_stats {
-	unsigned int reqs;
-	unsigned int cmds;
-	unsigned int cmdpoll_hits;
-	unsigned int cmdpoll_misses;
+struct msmsdcc_sps_ep_conn_data {
+	struct sps_pipe			*pipe_handle;
+	struct sps_connect		config;
+	struct sps_register_event	event;
+};
+
+struct msmsdcc_sps_data {
+	struct msmsdcc_sps_ep_conn_data	prod;
+	struct msmsdcc_sps_ep_conn_data	cons;
+	struct sps_event_notify		notify;
+	enum dma_data_direction		dir;
+	struct scatterlist		*sg;
+	int				num_ents;
+	u32				bam_handle;
+	unsigned int			src_pipe_index;
+	unsigned int			dest_pipe_index;
+	unsigned int			busy;
+	unsigned int			xfer_req_cnt;
+	bool				pipe_reset_pending;
+	struct tasklet_struct		tlet;
+};
+
+struct msmsdcc_msm_bus_vote {
+	uint32_t client_handle;
+	uint32_t curr_vote;
+	int min_bw_vote;
+	int max_bw_vote;
+	bool is_max_bw_needed;
+	struct delayed_work vote_work;
 };
 
 struct msmsdcc_host {
-	struct resource		*cmd_irqres;
-	struct resource		*memres;
+	struct resource		*core_irqres;
+	struct resource		*bam_irqres;
+	struct resource		*core_memres;
+	struct resource		*bam_memres;
+	struct resource		*dml_memres;
 	struct resource		*dmares;
+	struct resource		*dma_crci_res;
 	void __iomem		*base;
+	void __iomem		*dml_base;
+	void __iomem		*bam_base;
+
 	int			pdev_id;
-	unsigned int		stat_irq;
 
 	struct msmsdcc_curr_req	curr;
 
 	struct mmc_host		*mmc;
 	struct clk		*clk;		/* main MMC bus clock */
 	struct clk		*pclk;		/* SDCC peripheral bus clock */
+	struct clk		*dfab_pclk;	/* Daytona Fabric SDCC clock */
 	unsigned int		clks_on;	/* set if clocks are enabled */
-	struct timer_list	busclk_timer;
 
 	unsigned int		eject;		/* eject state */
 
@@ -227,30 +359,79 @@
 
 	unsigned int		clk_rate;	/* Current clock rate */
 	unsigned int		pclk_rate;
+	unsigned int		ddr_doubled_clk_rate;
 
 	u32			pwr;
-	u32			saved_irq0mask;	/* MMCIMASK0 reg value */
-	struct msm_mmc_platform_data *plat;
+	struct mmc_platform_data *plat;
+	u32			sdcc_version;
 
-	struct timer_list	timer;
 	unsigned int		oldstat;
 
 	struct msmsdcc_dma_data	dma;
+	struct msmsdcc_sps_data sps;
+	bool			is_dma_mode;
+	bool			is_sps_mode;
 	struct msmsdcc_pio_data	pio;
-	int			cmdpoll;
-	struct msmsdcc_stats	stats;
 
-	struct tasklet_struct	dma_tlet;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	struct early_suspend early_suspend;
+	int polling_enabled;
+#endif
+
+	struct tasklet_struct 	dma_tlet;
+
+	unsigned int prog_enable;
+
 	/* Command parameters */
 	unsigned int		cmd_timeout;
 	unsigned int		cmd_pio_irqmask;
 	unsigned int		cmd_datactrl;
 	struct mmc_command	*cmd_cmd;
-	u32			cmd_c;
-	bool			gpio_config_status;
+	u32					cmd_c;
 
-	bool prog_scan;
-	bool prog_enable;
+	unsigned int	mci_irqenable;
+	unsigned int	dummy_52_needed;
+	unsigned int	dummy_52_sent;
+
+	struct wake_lock	sdio_wlock;
+	struct wake_lock	sdio_suspend_wlock;
+	struct timer_list req_tout_timer;
+	unsigned long reg_write_delay;
+	bool io_pad_pwr_switch;
+	bool tuning_in_progress;
+	bool tuning_needed;
+	bool sdio_gpio_lpm;
+	bool irq_wake_enabled;
+	struct pm_qos_request pm_qos_req_dma;
+	u32 cpu_dma_latency;
+	bool sdcc_suspending;
+	bool sdcc_irq_disabled;
+	bool sdcc_suspended;
+	bool sdio_wakeupirq_disabled;
+	struct mutex clk_mutex;
+	bool pending_resume;
+	struct msmsdcc_msm_bus_vote msm_bus_vote;
 };
 
+int msmsdcc_set_pwrsave(struct mmc_host *mmc, int pwrsave);
+int msmsdcc_sdio_al_lpm(struct mmc_host *mmc, bool enable);
+
+#ifdef CONFIG_MSM_SDIO_AL
+
+static inline int msmsdcc_lpm_enable(struct mmc_host *mmc)
+{
+	return msmsdcc_sdio_al_lpm(mmc, true);
+}
+
+static inline int msmsdcc_lpm_disable(struct mmc_host *mmc)
+{
+	struct msmsdcc_host *host = mmc_priv(mmc);
+	int ret;
+
+	ret = msmsdcc_sdio_al_lpm(mmc, false);
+	wake_unlock(&host->sdio_wlock);
+	return ret;
+}
+#endif
+
 #endif
diff --git a/drivers/mmc/host/msm_sdcc_dml.c b/drivers/mmc/host/msm_sdcc_dml.c
new file mode 100644
index 0000000..320f52e
--- /dev/null
+++ b/drivers/mmc/host/msm_sdcc_dml.c
@@ -0,0 +1,303 @@
+/*
+ * linux/drivers/mmc/host/msm_sdcc_dml.c - Qualcomm MSM SDCC DML Driver
+ *
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/io.h>
+#include <asm/sizes.h>
+#include <mach/msm_iomap.h>
+
+#include "msm_sdcc_dml.h"
+
+/*
+ * DML registers definations
+ */
+
+/* DML config register defination */
+#define DML_CONFIG 0x0000
+#define PRODUCER_CRCI_DIS   0x00
+#define PRODUCER_CRCI_X_SEL 0x01
+#define PRODUCER_CRCI_Y_SEL 0x02
+#define PRODUCER_CRCI_MSK   0x3
+#define CONSUMER_CRCI_DIS   (0x00 << 2)
+#define CONSUMER_CRCI_X_SEL (0x01 << 2)
+#define CONSUMER_CRCI_Y_SEL (0x02 << 2)
+#define CONSUMER_CRCI_MSK   (0x3 << 2)
+#define PRODUCER_TRANS_END_EN (1 << 4)
+#define BYPASS (1 << 16)
+#define DIRECT_MODE (1 << 17)
+#define INFINITE_CONS_TRANS (1 << 18)
+
+/* DML status register defination */
+#define DML_STATUS 0x0004
+#define PRODUCER_IDLE (1 << 0)
+#define CONSUMER_IDLE (1 << 16)
+
+/*
+ * DML SW RESET register defination
+ * NOTE: write to this register resets the DML core.
+ * All internal state information will be lost and all
+ * register values will be reset as well
+ */
+#define DML_SW_RESET 0x0008
+
+/*
+ * DML PRODUCER START register defination
+ * NOTE: A write to this register triggers the DML
+ * Producer state machine. No SW register values will be
+ * altered.
+ */
+#define DML_PRODUCER_START 0x000C
+
+/*
+ * DML CONSUMER START register defination
+ * NOTE: A write to this register triggers the DML
+ * Consumer state machine. No SW register values will be
+ * altered.
+ */
+#define DML_CONSUMER_START 0x0010
+
+/*
+ * DML producer pipe logical size register defination
+ * NOTE: This register holds the size of the producer pipe
+ * (in units of bytes) _to_ which the peripheral can
+ * keep writing data to when its the PRODUCER.
+ */
+#define DML_PRODUCER_PIPE_LOGICAL_SIZE 0x0014
+
+/*
+ * DML producer pipe logical size register defination
+ * NOTE: This register holds the size of the consumer pipe
+ * (in units of bytes) _from_ which the peripheral
+ * can keep _reading_ data from when its the CONSUMER.
+ */
+#define DML_CONSUMER_PIPE_LOGICAL_SIZE 0x00018
+
+/*
+ * DML PIPE ID register
+ * This register holds pipe IDs that services
+ * the producer and consumer side of the peripheral
+ */
+#define DML_PIPE_ID 0x0001C
+#define PRODUCER_PIPE_ID_SHFT 0
+#define PRODUCER_PIPE_ID_MSK 0x1f
+#define CONSUMER_PIPE_ID_SHFT 16
+#define CONSUMER_PIPE_ID_MSK (0x1f << 16)
+
+/*
+ * DML Producer trackers register defination.
+ * This register is for debug purposes only. They reflect
+ * the value of the producer block and transaction counters
+ * when read. The values may be dynamically changing when
+ * a transaction is in progress.
+ */
+#define DML_PRODUCER_TRACKERS 0x00020
+#define PROD_BLOCK_CNT_SHFT 0
+#define PROD_BLOCK_CNT_MSK  0xffff
+#define PROD_TRANS_CNT_SHFT 16
+#define PROD_TRANS_CNT_MSK  (0xffff << 16)
+
+/*
+ * DML Producer BAM block size register defination.
+ * This regsiter holds the block size, in units of bytes,
+ * associated with the Producer BAM. The DML asserts the
+ * block_end side band signal to the BAM whenever the producer
+ * side of the peripheral has generated the said amount of data.
+ * This register value should be an integral multiple of the
+ * Producer CRCI Block Size.
+ */
+#define DML_PRODUCER_BAM_BLOCK_SIZE 0x00024
+
+/*
+ * DML Producer BAM Transaction size defination.
+ * This regsiter holds the transaction size, in units of bytes,
+ * associated with the Producer BAM. The DML asserts the transaction_end
+ * side band signal to the BAM whenever the producer side of the peripheral
+ * has generated the said amount of data.
+ */
+#define DML_PRODUCER_BAM_TRANS_SIZE 0x00028
+
+/*
+ * DML Direct mode base address defination
+ * This register is used whenever the DIRECT_MODE bit
+ * in config register is set.
+ */
+#define DML_DIRECT_MODE_BASE_ADDR 0x002C
+#define PRODUCER_BASE_ADDR_BSHFT 0
+#define PRODUCER_BASE_ADDR_BMSK  0xffff
+#define CONSUMER_BASE_ADDR_BSHFT 16
+#define CONSUMER_BASE_ADDR_BMSK  (0xffff << 16)
+
+/*
+ * DMA Debug and status register defination.
+ * These are the read-only registers useful debugging.
+ */
+#define DML_DEBUG 0x0030
+#define DML_BAM_SIDE_STATUS_1 0x0034
+#define DML_BAM_SIDE_STATUS_2 0x0038
+
+/* other definations */
+#define PRODUCER_PIPE_LOGICAL_SIZE 4096
+#define CONSUMER_PIPE_LOGICAL_SIZE 4096
+
+#ifdef CONFIG_MMC_MSM_SPS_SUPPORT
+/**
+ * Initialize DML HW connected with SDCC core
+ *
+ */
+int msmsdcc_dml_init(struct msmsdcc_host *host)
+{
+	int rc = 0;
+	u32 config = 0;
+	void __iomem *dml_base;
+
+	if (!host->dml_base) {
+		host->dml_base = ioremap(host->dml_memres->start,
+					resource_size(host->dml_memres));
+		if (!host->dml_base) {
+			pr_err("%s: DML ioremap() failed!!! phys_addr=0x%x,"
+				" size=0x%x", mmc_hostname(host->mmc),
+				host->dml_memres->start,
+				(host->dml_memres->end -
+				host->dml_memres->start));
+			rc = -ENOMEM;
+			goto out;
+		}
+		pr_info("%s: Qualcomm MSM SDCC-DML at 0x%016llx\n",
+			mmc_hostname(host->mmc),
+			(unsigned long long)host->dml_memres->start);
+	}
+
+	dml_base = host->dml_base;
+	/* Reset the DML block */
+	writel_relaxed(1, (dml_base + DML_SW_RESET));
+
+	/* Disable the producer and consumer CRCI */
+	config = (PRODUCER_CRCI_DIS | CONSUMER_CRCI_DIS);
+	/*
+	 * Disable the bypass mode. Bypass mode will only be used
+	 * if data transfer is to happen in PIO mode and don't
+	 * want the BAM interface to connect with SDCC-DML.
+	 */
+	config &= ~BYPASS;
+	/*
+	 * Disable direct mode as we don't DML to MASTER the AHB bus.
+	 * BAM connected with DML should MASTER the AHB bus.
+	 */
+	config &= ~DIRECT_MODE;
+	/*
+	 * Disable infinite mode transfer as we won't be doing any
+	 * infinite size data transfers. All data transfer will be
+	 * of finite data size.
+	 */
+	config &= ~INFINITE_CONS_TRANS;
+	writel_relaxed(config, (dml_base + DML_CONFIG));
+
+	/*
+	 * Initialize the logical BAM pipe size for producer
+	 * and consumer.
+	 */
+	writel_relaxed(PRODUCER_PIPE_LOGICAL_SIZE,
+		(dml_base + DML_PRODUCER_PIPE_LOGICAL_SIZE));
+	writel_relaxed(CONSUMER_PIPE_LOGICAL_SIZE,
+		(dml_base + DML_CONSUMER_PIPE_LOGICAL_SIZE));
+
+	/* Initialize Producer/consumer pipe id */
+	writel_relaxed(host->sps.src_pipe_index |
+		(host->sps.dest_pipe_index << CONSUMER_PIPE_ID_SHFT),
+		(dml_base + DML_PIPE_ID));
+	mb();
+out:
+	return rc;
+}
+
+/**
+ * Soft reset DML HW
+ *
+ */
+void msmsdcc_dml_reset(struct msmsdcc_host *host)
+{
+	/* Reset the DML block */
+	writel_relaxed(1, (host->dml_base + DML_SW_RESET));
+	mb();
+}
+
+/**
+ * Checks if DML HW is busy or not?
+ *
+ */
+bool msmsdcc_is_dml_busy(struct msmsdcc_host *host)
+{
+	return !(readl_relaxed(host->dml_base + DML_STATUS) & PRODUCER_IDLE) ||
+		!(readl_relaxed(host->dml_base + DML_STATUS) & CONSUMER_IDLE);
+}
+
+/**
+ * Start data transfer.
+ *
+ */
+void msmsdcc_dml_start_xfer(struct msmsdcc_host *host, struct mmc_data *data)
+{
+	u32 config;
+	void __iomem *dml_base = host->dml_base;
+
+	if (data->flags & MMC_DATA_READ) {
+		/* Read operation: configure DML for producer operation */
+		/* Set producer CRCI-x and disable consumer CRCI */
+		config = readl_relaxed(dml_base + DML_CONFIG);
+		config = (config & ~PRODUCER_CRCI_MSK) | PRODUCER_CRCI_X_SEL;
+		config = (config & ~CONSUMER_CRCI_MSK) | CONSUMER_CRCI_DIS;
+		writel_relaxed(config, (dml_base + DML_CONFIG));
+
+		/* Set the Producer BAM block size */
+		writel_relaxed(data->blksz, (dml_base +
+					DML_PRODUCER_BAM_BLOCK_SIZE));
+
+		/* Set Producer BAM Transaction size */
+		writel_relaxed(host->curr.xfer_size,
+			(dml_base + DML_PRODUCER_BAM_TRANS_SIZE));
+		/* Set Producer Transaction End bit */
+		writel_relaxed((readl_relaxed(dml_base + DML_CONFIG)
+			| PRODUCER_TRANS_END_EN),
+			(dml_base + DML_CONFIG));
+		/* Trigger producer */
+		writel_relaxed(1, (dml_base + DML_PRODUCER_START));
+	} else {
+		/* Write operation: configure DML for consumer operation */
+		/* Set consumer CRCI-x and disable producer CRCI*/
+		config = readl_relaxed(dml_base + DML_CONFIG);
+		config = (config & ~CONSUMER_CRCI_MSK) | CONSUMER_CRCI_X_SEL;
+		config = (config & ~PRODUCER_CRCI_MSK) | PRODUCER_CRCI_DIS;
+		writel_relaxed(config, (dml_base + DML_CONFIG));
+		/* Clear Producer Transaction End bit */
+		writel_relaxed((readl_relaxed(dml_base + DML_CONFIG)
+			& ~PRODUCER_TRANS_END_EN),
+			(dml_base + DML_CONFIG));
+		/* Trigger consumer */
+		writel_relaxed(1, (dml_base + DML_CONSUMER_START));
+	}
+	mb();
+}
+
+/**
+ * Deinitialize DML HW connected with SDCC core
+ *
+ */
+void msmsdcc_dml_exit(struct msmsdcc_host *host)
+{
+	/* Put DML block in reset state before exiting */
+	msmsdcc_dml_reset(host);
+	iounmap(host->dml_base);
+}
+#endif /* CONFIG_MMC_MSM_SPS_SUPPORT */
diff --git a/drivers/mmc/host/msm_sdcc_dml.h b/drivers/mmc/host/msm_sdcc_dml.h
new file mode 100644
index 0000000..f0e1b78
--- /dev/null
+++ b/drivers/mmc/host/msm_sdcc_dml.h
@@ -0,0 +1,105 @@
+/*
+ *  linux/drivers/mmc/host/msm_sdcc_dml.h - Qualcomm SDCC DML driver
+ *					    header file
+ *
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _MSM_SDCC_DML_H
+#define _MSM_SDCC_DML_H
+
+#include <linux/types.h>
+#include <linux/mmc/host.h>
+
+#include "msm_sdcc.h"
+
+#ifdef CONFIG_MMC_MSM_SPS_SUPPORT
+/**
+ * Initialize DML HW connected with SDCC core
+ *
+ * This function initialize DML HW.
+ *
+ * This function should only be called once
+ * typically during driver probe.
+ *
+ * @host - Pointer to sdcc host structure
+ *
+ * @return - 0 if successful else negative value.
+ *
+ */
+int msmsdcc_dml_init(struct msmsdcc_host *host);
+
+/**
+ * Start data transfer.
+ *
+ * This function configure DML HW registers with
+ * data transfer direction and data transfer size.
+ *
+ * This function should be called after submitting
+ * data transfer request to SPS HW and before kick
+ * starting data transfer in SDCC core.
+ *
+ * @host - Pointer to sdcc host structure
+ * @data - Pointer to mmc_data structure
+ *
+ */
+void msmsdcc_dml_start_xfer(struct msmsdcc_host *host, struct mmc_data *data);
+
+/**
+ * Checks if DML HW is busy or not?
+ *
+ * @host - Pointer to sdcc host structure
+ *
+ * @return - 1 if DML HW is busy with data transfer
+ *           0 if DML HW is IDLE.
+ *
+ */
+bool msmsdcc_is_dml_busy(struct msmsdcc_host *host);
+
+/**
+ * Soft reset DML HW
+ *
+ * This function give soft reset to DML HW.
+ *
+ * This function should be called to reset DML HW
+ * if data transfer error is detected.
+ *
+ * @host - Pointer to sdcc host structure
+ *
+ */
+void msmsdcc_dml_reset(struct msmsdcc_host *host);
+
+/**
+ * Deinitialize DML HW connected with SDCC core
+ *
+ * This function resets DML HW and unmap DML
+ * register region.
+ *
+ * This function should only be called once
+ * typically during driver remove.
+ *
+ * @host - Pointer to sdcc host structure
+ *
+ */
+void msmsdcc_dml_exit(struct msmsdcc_host *host);
+#else
+static inline int msmsdcc_dml_init(struct msmsdcc_host *host) { return 0; }
+static inline int msmsdcc_dml_start_xfer(struct msmsdcc_host *host,
+				struct mmc_data *data) { return 0; }
+static inline bool msmsdcc_is_dml_busy(
+				struct msmsdcc_host *host) { return 0; }
+static inline void msmsdcc_dml_reset(struct msmsdcc_host *host) { }
+static inline void msmsdcc_dml_exit(struct msmsdcc_host *host) { }
+#endif /* CONFIG_MMC_MSM_SPS_SUPPORT */
+
+#endif /* _MSM_SDCC_DML_H */
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 56d4499..1c4697d 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -1389,6 +1389,8 @@
 			dto -= 13;
 		else
 			dto = 0;
+		/* Use the maximum timeout value allowed in the standard of 14
+		   or 0xE */
 		if (dto > 14)
 			dto = 14;
 	}
diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c
index 69ef0be..b34b069 100644
--- a/drivers/mmc/host/sdhci-pci.c
+++ b/drivers/mmc/host/sdhci-pci.c
@@ -367,6 +367,8 @@
 		pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch);
 	}
 
+	slot->host->mmc->caps2 = MMC_CAP2_BOOTPART_NOACC;
+
 	return 0;
 }
 
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 37601ec..2f7a2f3 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -21,7 +21,6 @@
 #include <linux/slab.h>
 #include <linux/scatterlist.h>
 #include <linux/regulator/consumer.h>
-#include <linux/pm_runtime.h>
 
 #include <linux/leds.h>
 
@@ -43,76 +42,61 @@
 #define MAX_TUNING_LOOP 40
 
 static unsigned int debug_quirks = 0;
-static unsigned int debug_quirks2;
 
 static void sdhci_finish_data(struct sdhci_host *);
 
 static void sdhci_send_command(struct sdhci_host *, struct mmc_command *);
 static void sdhci_finish_command(struct sdhci_host *);
-static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
+static int sdhci_execute_tuning(struct mmc_host *mmc);
 static void sdhci_tuning_timer(unsigned long data);
 
-#ifdef CONFIG_PM_RUNTIME
-static int sdhci_runtime_pm_get(struct sdhci_host *host);
-static int sdhci_runtime_pm_put(struct sdhci_host *host);
-#else
-static inline int sdhci_runtime_pm_get(struct sdhci_host *host)
-{
-	return 0;
-}
-static inline int sdhci_runtime_pm_put(struct sdhci_host *host)
-{
-	return 0;
-}
-#endif
-
 static void sdhci_dumpregs(struct sdhci_host *host)
 {
-	pr_debug(DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n",
+	printk(KERN_DEBUG DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n",
 		mmc_hostname(host->mmc));
 
-	pr_debug(DRIVER_NAME ": Sys addr: 0x%08x | Version:  0x%08x\n",
+	printk(KERN_DEBUG DRIVER_NAME ": Sys addr: 0x%08x | Version:  0x%08x\n",
 		sdhci_readl(host, SDHCI_DMA_ADDRESS),
 		sdhci_readw(host, SDHCI_HOST_VERSION));
-	pr_debug(DRIVER_NAME ": Blk size: 0x%08x | Blk cnt:  0x%08x\n",
+	printk(KERN_DEBUG DRIVER_NAME ": Blk size: 0x%08x | Blk cnt:  0x%08x\n",
 		sdhci_readw(host, SDHCI_BLOCK_SIZE),
 		sdhci_readw(host, SDHCI_BLOCK_COUNT));
-	pr_debug(DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n",
+	printk(KERN_DEBUG DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n",
 		sdhci_readl(host, SDHCI_ARGUMENT),
 		sdhci_readw(host, SDHCI_TRANSFER_MODE));
-	pr_debug(DRIVER_NAME ": Present:  0x%08x | Host ctl: 0x%08x\n",
+	printk(KERN_DEBUG DRIVER_NAME ": Present:  0x%08x | Host ctl: 0x%08x\n",
 		sdhci_readl(host, SDHCI_PRESENT_STATE),
 		sdhci_readb(host, SDHCI_HOST_CONTROL));
-	pr_debug(DRIVER_NAME ": Power:    0x%08x | Blk gap:  0x%08x\n",
+	printk(KERN_DEBUG DRIVER_NAME ": Power:    0x%08x | Blk gap:  0x%08x\n",
 		sdhci_readb(host, SDHCI_POWER_CONTROL),
 		sdhci_readb(host, SDHCI_BLOCK_GAP_CONTROL));
-	pr_debug(DRIVER_NAME ": Wake-up:  0x%08x | Clock:    0x%08x\n",
+	printk(KERN_DEBUG DRIVER_NAME ": Wake-up:  0x%08x | Clock:    0x%08x\n",
 		sdhci_readb(host, SDHCI_WAKE_UP_CONTROL),
 		sdhci_readw(host, SDHCI_CLOCK_CONTROL));
-	pr_debug(DRIVER_NAME ": Timeout:  0x%08x | Int stat: 0x%08x\n",
+	printk(KERN_DEBUG DRIVER_NAME ": Timeout:  0x%08x | Int stat: 0x%08x\n",
 		sdhci_readb(host, SDHCI_TIMEOUT_CONTROL),
 		sdhci_readl(host, SDHCI_INT_STATUS));
-	pr_debug(DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n",
+	printk(KERN_DEBUG DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n",
 		sdhci_readl(host, SDHCI_INT_ENABLE),
 		sdhci_readl(host, SDHCI_SIGNAL_ENABLE));
-	pr_debug(DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n",
+	printk(KERN_DEBUG DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n",
 		sdhci_readw(host, SDHCI_ACMD12_ERR),
 		sdhci_readw(host, SDHCI_SLOT_INT_STATUS));
-	pr_debug(DRIVER_NAME ": Caps:     0x%08x | Caps_1:   0x%08x\n",
+	printk(KERN_DEBUG DRIVER_NAME ": Caps:     0x%08x | Caps_1:   0x%08x\n",
 		sdhci_readl(host, SDHCI_CAPABILITIES),
 		sdhci_readl(host, SDHCI_CAPABILITIES_1));
-	pr_debug(DRIVER_NAME ": Cmd:      0x%08x | Max curr: 0x%08x\n",
+	printk(KERN_DEBUG DRIVER_NAME ": Cmd:      0x%08x | Max curr: 0x%08x\n",
 		sdhci_readw(host, SDHCI_COMMAND),
 		sdhci_readl(host, SDHCI_MAX_CURRENT));
-	pr_debug(DRIVER_NAME ": Host ctl2: 0x%08x\n",
+	printk(KERN_DEBUG DRIVER_NAME ": Host ctl2: 0x%08x\n",
 		sdhci_readw(host, SDHCI_HOST_CONTROL2));
 
 	if (host->flags & SDHCI_USE_ADMA)
-		pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n",
+		printk(KERN_DEBUG DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n",
 		       readl(host->ioaddr + SDHCI_ADMA_ERROR),
 		       readl(host->ioaddr + SDHCI_ADMA_ADDRESS));
 
-	pr_debug(DRIVER_NAME ": ===========================================\n");
+	printk(KERN_DEBUG DRIVER_NAME ": ===========================================\n");
 }
 
 /*****************************************************************************\
@@ -144,16 +128,12 @@
 
 static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
 {
-	u32 present, irqs;
+	u32 irqs = SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT;
 
 	if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) ||
 	    (host->mmc->caps & MMC_CAP_NONREMOVABLE))
 		return;
 
-	present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
-			      SDHCI_CARD_PRESENT;
-	irqs = present ? SDHCI_INT_CARD_REMOVE : SDHCI_INT_CARD_INSERT;
-
 	if (enable)
 		sdhci_unmask_irqs(host, irqs);
 	else
@@ -198,7 +178,7 @@
 	/* hw clears the bit when it's done */
 	while (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) {
 		if (timeout == 0) {
-			pr_err("%s: Reset 0x%x never completed.\n",
+			printk(KERN_ERR "%s: Reset 0x%x never completed.\n",
 				mmc_hostname(host->mmc), (int)mask);
 			sdhci_dumpregs(host);
 			return;
@@ -212,11 +192,6 @@
 
 	if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET)
 		sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK, ier);
-
-	if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
-		if ((host->ops->enable_dma) && (mask & SDHCI_RESET_ALL))
-			host->ops->enable_dma(host);
-	}
 }
 
 static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);
@@ -274,14 +249,11 @@
 
 	spin_lock_irqsave(&host->lock, flags);
 
-	if (host->runtime_suspended)
-		goto out;
-
 	if (brightness == LED_OFF)
 		sdhci_deactivate_led(host);
 	else
 		sdhci_activate_led(host);
-out:
+
 	spin_unlock_irqrestore(&host->lock, flags);
 }
 #endif
@@ -426,12 +398,12 @@
 static char *sdhci_kmap_atomic(struct scatterlist *sg, unsigned long *flags)
 {
 	local_irq_save(*flags);
-	return kmap_atomic(sg_page(sg)) + sg->offset;
+	return kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset;
 }
 
 static void sdhci_kunmap_atomic(void *buffer, unsigned long *flags)
 {
-	kunmap_atomic(buffer);
+	kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
 	local_irq_restore(*flags);
 }
 
@@ -654,11 +626,12 @@
 	/* timeout in us */
 	if (!data)
 		target_timeout = cmd->cmd_timeout_ms * 1000;
-	else {
-		target_timeout = data->timeout_ns / 1000;
-		if (host->clock)
-			target_timeout += data->timeout_clks / host->clock;
-	}
+	else
+		target_timeout = data->timeout_ns / 1000 +
+			data->timeout_clks / host->clock;
+
+	if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)
+		host->timeout_clk = host->clock / 1000;
 
 	/*
 	 * Figure out needed cycles.
@@ -670,6 +643,7 @@
 	 *     =>
 	 *     (1) / (2) > 2^6
 	 */
+	BUG_ON(!host->timeout_clk);
 	count = 0;
 	current_timeout = (1 << 13) * 1000 / host->timeout_clk;
 	while (current_timeout < target_timeout) {
@@ -679,7 +653,9 @@
 			break;
 	}
 
-	if (count >= 0xF)
+	if (count >= 0xF) {
+		printk(KERN_WARNING "%s: Too large timeout requested for CMD%d!\n",
+		       mmc_hostname(host->mmc), cmd->opcode);
 		count = 0xE;
 
 	return count;
@@ -972,7 +948,7 @@
 
 	while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) {
 		if (timeout == 0) {
-			pr_err("%s: Controller never released "
+			printk(KERN_ERR "%s: Controller never released "
 				"inhibit bit(s).\n", mmc_hostname(host->mmc));
 			sdhci_dumpregs(host);
 			cmd->error = -EIO;
@@ -994,7 +970,7 @@
 	sdhci_set_transfer_mode(host, cmd);
 
 	if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
-		pr_err("%s: Unsupported response type!\n",
+		printk(KERN_ERR "%s: Unsupported response type!\n",
 			mmc_hostname(host->mmc));
 		cmd->error = -EINVAL;
 		tasklet_schedule(&host->finish_tasklet);
@@ -1016,8 +992,7 @@
 		flags |= SDHCI_CMD_INDEX;
 
 	/* CMD19 is special in that the Data Present Select should be set */
-	if (cmd->data || cmd->opcode == MMC_SEND_TUNING_BLOCK ||
-	    cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200)
+	if (cmd->data || (cmd->opcode == MMC_SEND_TUNING_BLOCK))
 		flags |= SDHCI_CMD_DATA;
 
 	sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
@@ -1067,15 +1042,12 @@
 static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
 {
 	int div = 0; /* Initialized for compiler warning */
-	int real_div = div, clk_mul = 1;
 	u16 clk = 0;
 	unsigned long timeout;
 
 	if (clock && clock == host->clock)
 		return;
 
-	host->mmc->actual_clock = 0;
-
 	if (host->ops->set_clock) {
 		host->ops->set_clock(host, clock);
 		if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK)
@@ -1113,8 +1085,6 @@
 				 * Control register.
 				 */
 				clk = SDHCI_PROG_CLOCK_MODE;
-				real_div = div;
-				clk_mul = host->clk_mul;
 				div--;
 			}
 		} else {
@@ -1128,7 +1098,6 @@
 						break;
 				}
 			}
-			real_div = div;
 			div >>= 1;
 		}
 	} else {
@@ -1137,13 +1106,9 @@
 			if ((host->max_clk / div) <= clock)
 				break;
 		}
-		real_div = div;
 		div >>= 1;
 	}
 
-	if (real_div)
-		host->mmc->actual_clock = (host->max_clk * clk_mul) / real_div;
-
 	clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT;
 	clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN)
 		<< SDHCI_DIVIDER_HI_SHIFT;
@@ -1155,7 +1120,7 @@
 	while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
 		& SDHCI_CLOCK_INT_STABLE)) {
 		if (timeout == 0) {
-			pr_err("%s: Internal clock never "
+			printk(KERN_ERR "%s: Internal clock never "
 				"stabilised.\n", mmc_hostname(host->mmc));
 			sdhci_dumpregs(host);
 			return;
@@ -1171,7 +1136,7 @@
 	host->clock = clock;
 }
 
-static int sdhci_set_power(struct sdhci_host *host, unsigned short power)
+static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
 {
 	u8 pwr = 0;
 
@@ -1194,13 +1159,13 @@
 	}
 
 	if (host->pwr == pwr)
-		return -1;
+		return;
 
 	host->pwr = pwr;
 
 	if (pwr == 0) {
 		sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
-		return 0;
+		return;
 	}
 
 	/*
@@ -1227,8 +1192,6 @@
 	 */
 	if (host->quirks & SDHCI_QUIRK_DELAY_AFTER_POWER)
 		mdelay(10);
-
-	return power;
 }
 
 /*****************************************************************************\
@@ -1245,8 +1208,6 @@
 
 	host = mmc_priv(mmc);
 
-	sdhci_runtime_pm_get(host);
-
 	spin_lock_irqsave(&host->lock, flags);
 
 	WARN_ON(host->mrq != NULL);
@@ -1290,7 +1251,7 @@
 		if ((host->flags & SDHCI_NEEDS_RETUNING) &&
 		    !(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ))) {
 			spin_unlock_irqrestore(&host->lock, flags);
-			sdhci_execute_tuning(mmc, mrq->cmd->opcode);
+			sdhci_execute_tuning(mmc);
 			spin_lock_irqsave(&host->lock, flags);
 
 			/* Restore original mmc_request structure */
@@ -1307,20 +1268,18 @@
 	spin_unlock_irqrestore(&host->lock, flags);
 }
 
-static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
+static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 {
+	struct sdhci_host *host;
 	unsigned long flags;
-	int vdd_bit = -1;
 	u8 ctrl;
 
+	host = mmc_priv(mmc);
+
 	spin_lock_irqsave(&host->lock, flags);
 
-	if (host->flags & SDHCI_DEVICE_DEAD) {
-		spin_unlock_irqrestore(&host->lock, flags);
-		if (host->vmmc && ios->power_mode == MMC_POWER_OFF)
-			mmc_regulator_set_ocr(host->mmc, host->vmmc, 0);
-		return;
-	}
+	if (host->flags & SDHCI_DEVICE_DEAD)
+		goto out;
 
 	/*
 	 * Reset the chip on each power off.
@@ -1334,15 +1293,9 @@
 	sdhci_set_clock(host, ios->clock);
 
 	if (ios->power_mode == MMC_POWER_OFF)
-		vdd_bit = sdhci_set_power(host, -1);
+		sdhci_set_power(host, -1);
 	else
-		vdd_bit = sdhci_set_power(host, ios->vdd);
-
-	if (host->vmmc && vdd_bit != -1) {
-		spin_unlock_irqrestore(&host->lock, flags);
-		mmc_regulator_set_ocr(host->mmc, host->vmmc, vdd_bit);
-		spin_lock_irqsave(&host->lock, flags);
-	}
+		sdhci_set_power(host, ios->vdd);
 
 	if (host->ops->platform_send_init_74_clocks)
 		host->ops->platform_send_init_74_clocks(host, ios->power_mode);
@@ -1385,8 +1338,7 @@
 		unsigned int clock;
 
 		/* In case of UHS-I modes, set High Speed Enable */
-		if ((ios->timing == MMC_TIMING_MMC_HS200) ||
-		    (ios->timing == MMC_TIMING_UHS_SDR50) ||
+		if ((ios->timing == MMC_TIMING_UHS_SDR50) ||
 		    (ios->timing == MMC_TIMING_UHS_SDR104) ||
 		    (ios->timing == MMC_TIMING_UHS_DDR50) ||
 		    (ios->timing == MMC_TIMING_UHS_SDR25))
@@ -1439,9 +1391,7 @@
 			ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
 			/* Select Bus Speed Mode for host */
 			ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
-			if (ios->timing == MMC_TIMING_MMC_HS200)
-				ctrl_2 |= SDHCI_CTRL_HS_SDR200;
-			else if (ios->timing == MMC_TIMING_UHS_SDR12)
+			if (ios->timing == MMC_TIMING_UHS_SDR12)
 				ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
 			else if (ios->timing == MMC_TIMING_UHS_SDR25)
 				ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
@@ -1469,20 +1419,12 @@
 	if(host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS)
 		sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
 
+out:
 	mmiowb();
 	spin_unlock_irqrestore(&host->lock, flags);
 }
 
-static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
-{
-	struct sdhci_host *host = mmc_priv(mmc);
-
-	sdhci_runtime_pm_get(host);
-	sdhci_do_set_ios(host, ios);
-	sdhci_runtime_pm_put(host);
-}
-
-static int sdhci_check_ro(struct sdhci_host *host)
+static int check_ro(struct sdhci_host *host)
 {
 	unsigned long flags;
 	int is_readonly;
@@ -1506,16 +1448,19 @@
 
 #define SAMPLE_COUNT	5
 
-static int sdhci_do_get_ro(struct sdhci_host *host)
+static int sdhci_get_ro(struct mmc_host *mmc)
 {
+	struct sdhci_host *host;
 	int i, ro_count;
 
+	host = mmc_priv(mmc);
+
 	if (!(host->quirks & SDHCI_QUIRK_UNSTABLE_RO_DETECT))
-		return sdhci_check_ro(host);
+		return check_ro(host);
 
 	ro_count = 0;
 	for (i = 0; i < SAMPLE_COUNT; i++) {
-		if (sdhci_check_ro(host)) {
+		if (check_ro(host)) {
 			if (++ro_count > SAMPLE_COUNT / 2)
 				return 1;
 		}
@@ -1524,64 +1469,38 @@
 	return 0;
 }
 
-static void sdhci_hw_reset(struct mmc_host *mmc)
+static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
 {
-	struct sdhci_host *host = mmc_priv(mmc);
+	struct sdhci_host *host;
+	unsigned long flags;
 
-	if (host->ops && host->ops->hw_reset)
-		host->ops->hw_reset(host);
-}
+	host = mmc_priv(mmc);
 
-static int sdhci_get_ro(struct mmc_host *mmc)
-{
-	struct sdhci_host *host = mmc_priv(mmc);
-	int ret;
+	spin_lock_irqsave(&host->lock, flags);
 
-	sdhci_runtime_pm_get(host);
-	ret = sdhci_do_get_ro(host);
-	sdhci_runtime_pm_put(host);
-	return ret;
-}
-
-static void sdhci_enable_sdio_irq_nolock(struct sdhci_host *host, int enable)
-{
 	if (host->flags & SDHCI_DEVICE_DEAD)
 		goto out;
 
 	if (enable)
-		host->flags |= SDHCI_SDIO_IRQ_ENABLED;
-	else
-		host->flags &= ~SDHCI_SDIO_IRQ_ENABLED;
-
-	/* SDIO IRQ will be enabled as appropriate in runtime resume */
-	if (host->runtime_suspended)
-		goto out;
-
-	if (enable)
 		sdhci_unmask_irqs(host, SDHCI_INT_CARD_INT);
 	else
 		sdhci_mask_irqs(host, SDHCI_INT_CARD_INT);
 out:
 	mmiowb();
-}
 
-static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
-{
-	struct sdhci_host *host = mmc_priv(mmc);
-	unsigned long flags;
-
-	spin_lock_irqsave(&host->lock, flags);
-	sdhci_enable_sdio_irq_nolock(host, enable);
 	spin_unlock_irqrestore(&host->lock, flags);
 }
 
-static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
-						struct mmc_ios *ios)
+static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
+	struct mmc_ios *ios)
 {
+	struct sdhci_host *host;
 	u8 pwr;
 	u16 clk, ctrl;
 	u32 present_state;
 
+	host = mmc_priv(mmc);
+
 	/*
 	 * Signal Voltage Switching is only applicable for Host Controllers
 	 * v3.00 and above.
@@ -1607,7 +1526,7 @@
 		if (!(ctrl & SDHCI_CTRL_VDD_180))
 			return 0;
 		else {
-			pr_info(DRIVER_NAME ": Switching to 3.3V "
+			printk(KERN_INFO DRIVER_NAME ": Switching to 3.3V "
 				"signalling voltage failed\n");
 			return -EIO;
 		}
@@ -1666,7 +1585,7 @@
 		pwr |= SDHCI_POWER_ON;
 		sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
 
-		pr_info(DRIVER_NAME ": Switching to 1.8V signalling "
+		printk(KERN_INFO DRIVER_NAME ": Switching to 1.8V signalling "
 			"voltage failed, retrying with S18R set to 0\n");
 		return -EAGAIN;
 	} else
@@ -1674,21 +1593,7 @@
 		return 0;
 }
 
-static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
-	struct mmc_ios *ios)
-{
-	struct sdhci_host *host = mmc_priv(mmc);
-	int err;
-
-	if (host->version < SDHCI_SPEC_300)
-		return 0;
-	sdhci_runtime_pm_get(host);
-	err = sdhci_do_start_signal_voltage_switch(host, ios);
-	sdhci_runtime_pm_put(host);
-	return err;
-}
-
-static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
+static int sdhci_execute_tuning(struct mmc_host *mmc)
 {
 	struct sdhci_host *host;
 	u16 ctrl;
@@ -1696,35 +1601,26 @@
 	int tuning_loop_counter = MAX_TUNING_LOOP;
 	unsigned long timeout;
 	int err = 0;
-	bool requires_tuning_nonuhs = false;
 
 	host = mmc_priv(mmc);
 
-	sdhci_runtime_pm_get(host);
 	disable_irq(host->irq);
 	spin_lock(&host->lock);
 
 	ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
 
 	/*
-	 * The Host Controller needs tuning only in case of SDR104 mode
-	 * and for SDR50 mode when Use Tuning for SDR50 is set in the
+	 * Host Controller needs tuning only in case of SDR104 mode
+	 * and for SDR50 mode when Use Tuning for SDR50 is set in
 	 * Capabilities register.
-	 * If the Host Controller supports the HS200 mode then the
-	 * tuning function has to be executed.
 	 */
-	if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
-	    (host->flags & SDHCI_SDR50_NEEDS_TUNING ||
-	     host->flags & SDHCI_HS200_NEEDS_TUNING))
-		requires_tuning_nonuhs = true;
-
 	if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) ||
-	    requires_tuning_nonuhs)
+	    (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
+	    (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
 		ctrl |= SDHCI_CTRL_EXEC_TUNING;
 	else {
 		spin_unlock(&host->lock);
 		enable_irq(host->irq);
-		sdhci_runtime_pm_put(host);
 		return 0;
 	}
 
@@ -1750,12 +1646,12 @@
 	timeout = 150;
 	do {
 		struct mmc_command cmd = {0};
-		struct mmc_request mrq = {NULL};
+		struct mmc_request mrq = {0};
 
 		if (!tuning_loop_counter && !timeout)
 			break;
 
-		cmd.opcode = opcode;
+		cmd.opcode = MMC_SEND_TUNING_BLOCK;
 		cmd.arg = 0;
 		cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
 		cmd.retries = 0;
@@ -1770,17 +1666,7 @@
 		 * block to the Host Controller. So we set the block size
 		 * to 64 here.
 		 */
-		if (cmd.opcode == MMC_SEND_TUNING_BLOCK_HS200) {
-			if (mmc->ios.bus_width == MMC_BUS_WIDTH_8)
-				sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 128),
-					     SDHCI_BLOCK_SIZE);
-			else if (mmc->ios.bus_width == MMC_BUS_WIDTH_4)
-				sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
-					     SDHCI_BLOCK_SIZE);
-		} else {
-			sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
-				     SDHCI_BLOCK_SIZE);
-		}
+		sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64), SDHCI_BLOCK_SIZE);
 
 		/*
 		 * The tuning block is sent by the card to the host controller.
@@ -1806,7 +1692,7 @@
 		spin_lock(&host->lock);
 
 		if (!host->tuning_done) {
-			pr_info(DRIVER_NAME ": Timeout waiting for "
+			printk(KERN_INFO DRIVER_NAME ": Timeout waiting for "
 				"Buffer Read Ready interrupt during tuning "
 				"procedure, falling back to fixed sampling "
 				"clock\n");
@@ -1836,7 +1722,7 @@
 		sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
 	} else {
 		if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) {
-			pr_info(DRIVER_NAME ": Tuning procedure"
+			printk(KERN_INFO DRIVER_NAME ": Tuning procedure"
 				" failed, falling back to fixed sampling"
 				" clock\n");
 			err = -EIO;
@@ -1878,16 +1764,18 @@
 	sdhci_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier);
 	spin_unlock(&host->lock);
 	enable_irq(host->irq);
-	sdhci_runtime_pm_put(host);
 
 	return err;
 }
 
-static void sdhci_do_enable_preset_value(struct sdhci_host *host, bool enable)
+static void sdhci_enable_preset_value(struct mmc_host *mmc, bool enable)
 {
+	struct sdhci_host *host;
 	u16 ctrl;
 	unsigned long flags;
 
+	host = mmc_priv(mmc);
+
 	/* Host Controller v3.00 defines preset value registers */
 	if (host->version < SDHCI_SPEC_300)
 		return;
@@ -1903,30 +1791,18 @@
 	if (enable && !(ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
 		ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE;
 		sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
-		host->flags |= SDHCI_PV_ENABLED;
 	} else if (!enable && (ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
 		ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
 		sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
-		host->flags &= ~SDHCI_PV_ENABLED;
 	}
 
 	spin_unlock_irqrestore(&host->lock, flags);
 }
 
-static void sdhci_enable_preset_value(struct mmc_host *mmc, bool enable)
-{
-	struct sdhci_host *host = mmc_priv(mmc);
-
-	sdhci_runtime_pm_get(host);
-	sdhci_do_enable_preset_value(host, enable);
-	sdhci_runtime_pm_put(host);
-}
-
 static const struct mmc_host_ops sdhci_ops = {
 	.request	= sdhci_request,
 	.set_ios	= sdhci_set_ios,
 	.get_ro		= sdhci_get_ro,
-	.hw_reset	= sdhci_hw_reset,
 	.enable_sdio_irq = sdhci_enable_sdio_irq,
 	.start_signal_voltage_switch	= sdhci_start_signal_voltage_switch,
 	.execute_tuning			= sdhci_execute_tuning,
@@ -1948,19 +1824,19 @@
 
 	spin_lock_irqsave(&host->lock, flags);
 
-	/* Check host->mrq first in case we are runtime suspended */
-	if (host->mrq &&
-	    !(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
-		pr_err("%s: Card removed during transfer!\n",
-			mmc_hostname(host->mmc));
-		pr_err("%s: Resetting controller.\n",
-			mmc_hostname(host->mmc));
+	if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
+		if (host->mrq) {
+			printk(KERN_ERR "%s: Card removed during transfer!\n",
+				mmc_hostname(host->mmc));
+			printk(KERN_ERR "%s: Resetting controller.\n",
+				mmc_hostname(host->mmc));
 
-		sdhci_reset(host, SDHCI_RESET_CMD);
-		sdhci_reset(host, SDHCI_RESET_DATA);
+			sdhci_reset(host, SDHCI_RESET_CMD);
+			sdhci_reset(host, SDHCI_RESET_DATA);
 
-		host->mrq->cmd->error = -ENOMEDIUM;
-		tasklet_schedule(&host->finish_tasklet);
+			host->mrq->cmd->error = -ENOMEDIUM;
+			tasklet_schedule(&host->finish_tasklet);
+		}
 	}
 
 	spin_unlock_irqrestore(&host->lock, flags);
@@ -1976,16 +1852,14 @@
 
 	host = (struct sdhci_host*)param;
 
-	spin_lock_irqsave(&host->lock, flags);
-
         /*
          * If this tasklet gets rescheduled while running, it will
          * be run again afterwards but without any active request.
          */
-	if (!host->mrq) {
-		spin_unlock_irqrestore(&host->lock, flags);
+	if (!host->mrq)
 		return;
-	}
+
+	spin_lock_irqsave(&host->lock, flags);
 
 	del_timer(&host->timer);
 
@@ -2029,7 +1903,6 @@
 	spin_unlock_irqrestore(&host->lock, flags);
 
 	mmc_request_done(host->mmc, mrq);
-	sdhci_runtime_pm_put(host);
 }
 
 static void sdhci_timeout_timer(unsigned long data)
@@ -2042,7 +1915,7 @@
 	spin_lock_irqsave(&host->lock, flags);
 
 	if (host->mrq) {
-		pr_err("%s: Timeout waiting for hardware "
+		printk(KERN_ERR "%s: Timeout waiting for hardware "
 			"interrupt.\n", mmc_hostname(host->mmc));
 		sdhci_dumpregs(host);
 
@@ -2088,7 +1961,7 @@
 	BUG_ON(intmask == 0);
 
 	if (!host->cmd) {
-		pr_err("%s: Got command interrupt 0x%08x even "
+		printk(KERN_ERR "%s: Got command interrupt 0x%08x even "
 			"though no command operation was in progress.\n",
 			mmc_hostname(host->mmc), (unsigned)intmask);
 		sdhci_dumpregs(host);
@@ -2163,14 +2036,12 @@
 
 static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
 {
-	u32 command;
 	BUG_ON(intmask == 0);
 
 	/* CMD19 generates _only_ Buffer Read Ready interrupt */
 	if (intmask & SDHCI_INT_DATA_AVAIL) {
-		command = SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND));
-		if (command == MMC_SEND_TUNING_BLOCK ||
-		    command == MMC_SEND_TUNING_BLOCK_HS200) {
+		if (SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)) ==
+		    MMC_SEND_TUNING_BLOCK) {
 			host->tuning_done = 1;
 			wake_up(&host->buf_ready_int);
 			return;
@@ -2190,7 +2061,7 @@
 			}
 		}
 
-		pr_err("%s: Got data interrupt 0x%08x even "
+		printk(KERN_ERR "%s: Got data interrupt 0x%08x even "
 			"though no data operation was in progress.\n",
 			mmc_hostname(host->mmc), (unsigned)intmask);
 		sdhci_dumpregs(host);
@@ -2207,7 +2078,7 @@
 			!= MMC_BUS_TEST_R)
 		host->data->error = -EILSEQ;
 	else if (intmask & SDHCI_INT_ADMA_ERROR) {
-		pr_err("%s: ADMA error\n", mmc_hostname(host->mmc));
+		printk(KERN_ERR "%s: ADMA error\n", mmc_hostname(host->mmc));
 		sdhci_show_adma_error(host);
 		host->data->error = -EIO;
 	}
@@ -2269,13 +2140,6 @@
 
 	spin_lock(&host->lock);
 
-	if (host->runtime_suspended) {
-		spin_unlock(&host->lock);
-		pr_warning("%s: got irq while runtime suspended\n",
-		       mmc_hostname(host->mmc));
-		return IRQ_HANDLED;
-	}
-
 	intmask = sdhci_readl(host, SDHCI_INT_STATUS);
 
 	if (!intmask || intmask == 0xffffffff) {
@@ -2288,30 +2152,13 @@
 		mmc_hostname(host->mmc), intmask);
 
 	if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
-		u32 present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
-			      SDHCI_CARD_PRESENT;
-
-		/*
-		 * There is a observation on i.mx esdhc.  INSERT bit will be
-		 * immediately set again when it gets cleared, if a card is
-		 * inserted.  We have to mask the irq to prevent interrupt
-		 * storm which will freeze the system.  And the REMOVE gets
-		 * the same situation.
-		 *
-		 * More testing are needed here to ensure it works for other
-		 * platforms though.
-		 */
-		sdhci_mask_irqs(host, present ? SDHCI_INT_CARD_INSERT :
-						SDHCI_INT_CARD_REMOVE);
-		sdhci_unmask_irqs(host, present ? SDHCI_INT_CARD_REMOVE :
-						  SDHCI_INT_CARD_INSERT);
-
 		sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT |
-			     SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS);
-		intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
+			SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS);
 		tasklet_schedule(&host->card_tasklet);
 	}
 
+	intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
+
 	if (intmask & SDHCI_INT_CMD_MASK) {
 		sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK,
 			SDHCI_INT_STATUS);
@@ -2329,7 +2176,7 @@
 	intmask &= ~SDHCI_INT_ERROR;
 
 	if (intmask & SDHCI_INT_BUS_POWER) {
-		pr_err("%s: Card is consuming too much power!\n",
+		printk(KERN_ERR "%s: Card is consuming too much power!\n",
 			mmc_hostname(host->mmc));
 		sdhci_writel(host, SDHCI_INT_BUS_POWER, SDHCI_INT_STATUS);
 	}
@@ -2342,6 +2189,9 @@
 	intmask &= ~SDHCI_INT_CARD_INT;
 
 	if (intmask) {
+		printk(KERN_ERR "%s: Unexpected interrupt 0x%08x.\n",
+			mmc_hostname(host->mmc), intmask);
+		sdhci_dumpregs(host);
 		unexpected |= intmask;
 		sdhci_writel(host, intmask, SDHCI_INT_STATUS);
 	}
@@ -2376,10 +2226,9 @@
 
 #ifdef CONFIG_PM
 
-int sdhci_suspend_host(struct sdhci_host *host)
+int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state)
 {
 	int ret;
-	bool has_tuning_timer;
 
 	if (host->ops->platform_suspend)
 		host->ops->platform_suspend(host);
@@ -2387,28 +2236,21 @@
 	sdhci_disable_card_detection(host);
 
 	/* Disable tuning since we are suspending */
-	has_tuning_timer = host->version >= SDHCI_SPEC_300 &&
-		host->tuning_count && host->tuning_mode == SDHCI_TUNING_MODE_1;
-	if (has_tuning_timer) {
+	if (host->version >= SDHCI_SPEC_300 && host->tuning_count &&
+	    host->tuning_mode == SDHCI_TUNING_MODE_1) {
 		del_timer_sync(&host->tuning_timer);
 		host->flags &= ~SDHCI_NEEDS_RETUNING;
 	}
 
 	ret = mmc_suspend_host(host->mmc);
-	if (ret) {
-		if (has_tuning_timer) {
-			host->flags |= SDHCI_NEEDS_RETUNING;
-			mod_timer(&host->tuning_timer, jiffies +
-					host->tuning_count * HZ);
-		}
-
-		sdhci_enable_card_detection(host);
-
+	if (ret)
 		return ret;
-	}
 
 	free_irq(host->irq, host);
 
+	if (host->vmmc)
+		ret = regulator_disable(host->vmmc);
+
 	return ret;
 }
 
@@ -2418,6 +2260,13 @@
 {
 	int ret;
 
+	if (host->vmmc) {
+		int ret = regulator_enable(host->vmmc);
+		if (ret)
+			return ret;
+	}
+
+
 	if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
 		if (host->ops->enable_dma)
 			host->ops->enable_dma(host);
@@ -2468,90 +2317,6 @@
 
 #endif /* CONFIG_PM */
 
-#ifdef CONFIG_PM_RUNTIME
-
-static int sdhci_runtime_pm_get(struct sdhci_host *host)
-{
-	return pm_runtime_get_sync(host->mmc->parent);
-}
-
-static int sdhci_runtime_pm_put(struct sdhci_host *host)
-{
-	pm_runtime_mark_last_busy(host->mmc->parent);
-	return pm_runtime_put_autosuspend(host->mmc->parent);
-}
-
-int sdhci_runtime_suspend_host(struct sdhci_host *host)
-{
-	unsigned long flags;
-	int ret = 0;
-
-	/* Disable tuning since we are suspending */
-	if (host->version >= SDHCI_SPEC_300 &&
-	    host->tuning_mode == SDHCI_TUNING_MODE_1) {
-		del_timer_sync(&host->tuning_timer);
-		host->flags &= ~SDHCI_NEEDS_RETUNING;
-	}
-
-	spin_lock_irqsave(&host->lock, flags);
-	sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK);
-	spin_unlock_irqrestore(&host->lock, flags);
-
-	synchronize_irq(host->irq);
-
-	spin_lock_irqsave(&host->lock, flags);
-	host->runtime_suspended = true;
-	spin_unlock_irqrestore(&host->lock, flags);
-
-	return ret;
-}
-EXPORT_SYMBOL_GPL(sdhci_runtime_suspend_host);
-
-int sdhci_runtime_resume_host(struct sdhci_host *host)
-{
-	unsigned long flags;
-	int ret = 0, host_flags = host->flags;
-
-	if (host_flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
-		if (host->ops->enable_dma)
-			host->ops->enable_dma(host);
-	}
-
-	sdhci_init(host, 0);
-
-	/* Force clock and power re-program */
-	host->pwr = 0;
-	host->clock = 0;
-	sdhci_do_set_ios(host, &host->mmc->ios);
-
-	sdhci_do_start_signal_voltage_switch(host, &host->mmc->ios);
-	if (host_flags & SDHCI_PV_ENABLED)
-		sdhci_do_enable_preset_value(host, true);
-
-	/* Set the re-tuning expiration flag */
-	if ((host->version >= SDHCI_SPEC_300) && host->tuning_count &&
-	    (host->tuning_mode == SDHCI_TUNING_MODE_1))
-		host->flags |= SDHCI_NEEDS_RETUNING;
-
-	spin_lock_irqsave(&host->lock, flags);
-
-	host->runtime_suspended = false;
-
-	/* Enable SDIO IRQ */
-	if ((host->flags & SDHCI_SDIO_IRQ_ENABLED))
-		sdhci_enable_sdio_irq_nolock(host, true);
-
-	/* Enable Card Detection */
-	sdhci_enable_card_detection(host);
-
-	spin_unlock_irqrestore(&host->lock, flags);
-
-	return ret;
-}
-EXPORT_SYMBOL_GPL(sdhci_runtime_resume_host);
-
-#endif
-
 /*****************************************************************************\
  *                                                                           *
  * Device allocation/registration                                            *
@@ -2594,8 +2359,6 @@
 
 	if (debug_quirks)
 		host->quirks = debug_quirks;
-	if (debug_quirks2)
-		host->quirks2 = debug_quirks2;
 
 	sdhci_reset(host, SDHCI_RESET_ALL);
 
@@ -2603,7 +2366,7 @@
 	host->version = (host->version & SDHCI_SPEC_VER_MASK)
 				>> SDHCI_SPEC_VER_SHIFT;
 	if (host->version > SDHCI_SPEC_300) {
-		pr_err("%s: Unknown controller version (%d). "
+		printk(KERN_ERR "%s: Unknown controller version (%d). "
 			"You may experience problems.\n", mmc_hostname(mmc),
 			host->version);
 	}
@@ -2640,7 +2403,7 @@
 	if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
 		if (host->ops->enable_dma) {
 			if (host->ops->enable_dma(host)) {
-				pr_warning("%s: No suitable DMA "
+				printk(KERN_WARNING "%s: No suitable DMA "
 					"available. Falling back to PIO.\n",
 					mmc_hostname(mmc));
 				host->flags &=
@@ -2660,7 +2423,7 @@
 		if (!host->adma_desc || !host->align_buffer) {
 			kfree(host->adma_desc);
 			kfree(host->align_buffer);
-			pr_warning("%s: Unable to allocate ADMA "
+			printk(KERN_WARNING "%s: Unable to allocate ADMA "
 				"buffers. Falling back to standard DMA.\n",
 				mmc_hostname(mmc));
 			host->flags &= ~SDHCI_USE_ADMA;
@@ -2688,13 +2451,46 @@
 	if (host->max_clk == 0 || host->quirks &
 			SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN) {
 		if (!host->ops->get_max_clock) {
-			pr_err("%s: Hardware doesn't specify base clock "
+			printk(KERN_ERR
+			       "%s: Hardware doesn't specify base clock "
 			       "frequency.\n", mmc_hostname(mmc));
 			return -ENODEV;
 		}
 		host->max_clk = host->ops->get_max_clock(host);
 	}
 
+	host->timeout_clk =
+		(caps[0] & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT;
+	if (host->timeout_clk == 0) {
+		if (host->ops->get_timeout_clock) {
+			host->timeout_clk = host->ops->get_timeout_clock(host);
+		} else if (!(host->quirks &
+				SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) {
+			printk(KERN_ERR
+			       "%s: Hardware doesn't specify timeout clock "
+			       "frequency.\n", mmc_hostname(mmc));
+			return -ENODEV;
+		}
+	}
+	if (caps[0] & SDHCI_TIMEOUT_CLK_UNIT)
+		host->timeout_clk *= 1000;
+
+ 	/*
+	 * In case of Host Controller v3.00, find out whether clock
+	 * multiplier is supported.
+	 */
+	host->clk_mul = (caps[1] & SDHCI_CLOCK_MUL_MASK) >>
+		SDHCI_CLOCK_MUL_SHIFT;
+
+	/*
+	 * In case the value in Clock Multiplier is 0, then programmable
+	 * clock mode is not supported, otherwise the actual clock
+	 * multiplier is one more than the value of Clock Multiplier
+	 * in the Capabilities Register.
+	 */
+	if (host->clk_mul)
+		host->clk_mul += 1;
+
 	/*
 	 * In case of Host Controller v3.00, find out whether clock
 	 * multiplier is supported.
@@ -2727,26 +2523,6 @@
 	} else
 		mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_200;
 
-	host->timeout_clk =
-		(caps[0] & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT;
-	if (host->timeout_clk == 0) {
-		if (host->ops->get_timeout_clock) {
-			host->timeout_clk = host->ops->get_timeout_clock(host);
-		} else if (!(host->quirks &
-				SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) {
-			pr_err("%s: Hardware doesn't specify timeout clock "
-			       "frequency.\n", mmc_hostname(mmc));
-			return -ENODEV;
-		}
-	}
-	if (caps[0] & SDHCI_TIMEOUT_CLK_UNIT)
-		host->timeout_clk *= 1000;
-
-	if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)
-		host->timeout_clk = mmc->f_max / 1000;
-
-	mmc->max_discard_to = (1 << 27) / host->timeout_clk;
-
 	mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23;
 
 	if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)
@@ -2793,14 +2569,10 @@
 	if (caps[1] & SDHCI_SUPPORT_DDR50)
 		mmc->caps |= MMC_CAP_UHS_DDR50;
 
-	/* Does the host need tuning for SDR50? */
+	/* Does the host needs tuning for SDR50? */
 	if (caps[1] & SDHCI_USE_SDR50_TUNING)
 		host->flags |= SDHCI_SDR50_NEEDS_TUNING;
 
-	/* Does the host need tuning for HS200? */
-	if (mmc->caps2 & MMC_CAP2_HS200)
-		host->flags |= SDHCI_HS200_NEEDS_TUNING;
-
 	/* Driver Type(s) (A, C, D) supported by the host */
 	if (caps[1] & SDHCI_DRIVER_TYPE_A)
 		mmc->caps |= MMC_CAP_DRIVER_TYPE_A;
@@ -2907,7 +2679,7 @@
 		mmc->ocr_avail_mmc &= host->ocr_avail_mmc;
 
 	if (mmc->ocr_avail == 0) {
-		pr_err("%s: Hardware doesn't report any "
+		printk(KERN_ERR "%s: Hardware doesn't report any "
 			"support voltages.\n", mmc_hostname(mmc));
 		return -ENODEV;
 	}
@@ -2955,7 +2727,7 @@
 		mmc->max_blk_size = (caps[0] & SDHCI_MAX_BLOCK_MASK) >>
 				SDHCI_MAX_BLOCK_SHIFT;
 		if (mmc->max_blk_size >= 3) {
-			pr_warning("%s: Invalid maximum block size, "
+			printk(KERN_WARNING "%s: Invalid maximum block size, "
 				"assuming 512 bytes\n", mmc_hostname(mmc));
 			mmc->max_blk_size = 0;
 		}
@@ -2994,8 +2766,10 @@
 
 	host->vmmc = regulator_get(mmc_dev(mmc), "vmmc");
 	if (IS_ERR(host->vmmc)) {
-		pr_info("%s: no vmmc regulator found\n", mmc_hostname(mmc));
+		printk(KERN_INFO "%s: no vmmc regulator found\n", mmc_hostname(mmc));
 		host->vmmc = NULL;
+	} else {
+		regulator_enable(host->vmmc);
 	}
 
 	sdhci_init(host, 0);
@@ -3021,7 +2795,7 @@
 
 	mmc_add_host(mmc);
 
-	pr_info("%s: SDHCI controller on %s [%s] using %s\n",
+	printk(KERN_INFO "%s: SDHCI controller on %s [%s] using %s\n",
 		mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)),
 		(host->flags & SDHCI_USE_ADMA) ? "ADMA" :
 		(host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO");
@@ -3054,7 +2828,7 @@
 		host->flags |= SDHCI_DEVICE_DEAD;
 
 		if (host->mrq) {
-			pr_err("%s: Controller removed during "
+			printk(KERN_ERR "%s: Controller removed during "
 				" transfer!\n", mmc_hostname(host->mmc));
 
 			host->mrq->cmd->error = -ENOMEDIUM;
@@ -3084,8 +2858,10 @@
 	tasklet_kill(&host->card_tasklet);
 	tasklet_kill(&host->finish_tasklet);
 
-	if (host->vmmc)
+	if (host->vmmc) {
+		regulator_disable(host->vmmc);
 		regulator_put(host->vmmc);
+	}
 
 	kfree(host->adma_desc);
 	kfree(host->align_buffer);
@@ -3111,9 +2887,9 @@
 
 static int __init sdhci_drv_init(void)
 {
-	pr_info(DRIVER_NAME
+	printk(KERN_INFO DRIVER_NAME
 		": Secure Digital Host Controller Interface driver\n");
-	pr_info(DRIVER_NAME ": Copyright(c) Pierre Ossman\n");
+	printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n");
 
 	return 0;
 }
@@ -3126,11 +2902,9 @@
 module_exit(sdhci_drv_exit);
 
 module_param(debug_quirks, uint, 0444);
-module_param(debug_quirks2, uint, 0444);
 
 MODULE_AUTHOR("Pierre Ossman <pierre@ossman.eu>");
 MODULE_DESCRIPTION("Secure Digital Host Controller Interface core driver");
 MODULE_LICENSE("GPL");
 
 MODULE_PARM_DESC(debug_quirks, "Force certain quirks.");
-MODULE_PARM_DESC(debug_quirks2, "Force certain other quirks.");
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index 5760c1a..04796af 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -314,6 +314,17 @@
 	  The driver provides wear leveling by storing erase counter into the
 	  OOB.
 
+config MTD_LAZYECCSTATS
+	bool "MTD Lazy ECC Stats collection support"
+	default y
+	help
+	  Normally bad block counts for ECC stats are collected at boot time.
+	  This option delays the badblock stats collection until ECCGETSTATS
+	  ioctl is invoked on the partition.
+
+	  This can significantly decrease boot times depending on the size of
+	  the partition.  If unsure, say 'N'.
+
 source "drivers/mtd/chips/Kconfig"
 
 source "drivers/mtd/maps/Kconfig"
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index 4cdb2af..21f146f 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -50,6 +50,16 @@
 	  say M here and read <file:Documentation/kbuild/modules.txt>.
 	  The module will be called ms02-nv.
 
+config MTD_MSM_NAND
+	tristate "MSM NAND Device Support"
+	depends on MTD && ARCH_MSM
+	select CRC16
+	select BITREVERSE
+	select MTD_NAND_IDS
+	default y
+	help
+	  Support for some NAND chips connected to the MSM NAND controller.
+
 config MTD_DATAFLASH
 	tristate "Support for AT45xxx DataFlash"
 	depends on SPI_MASTER && EXPERIMENTAL
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
index a4dd1d8..8497c5f 100644
--- a/drivers/mtd/devices/Makefile
+++ b/drivers/mtd/devices/Makefile
@@ -12,6 +12,7 @@
 obj-$(CONFIG_MTD_PHRAM)		+= phram.o
 obj-$(CONFIG_MTD_PMC551)	+= pmc551.o
 obj-$(CONFIG_MTD_MS02NV)	+= ms02-nv.o
+obj-$(CONFIG_MTD_MSM_NAND)	+= msm_nand.o
 obj-$(CONFIG_MTD_MTDRAM)	+= mtdram.o
 obj-$(CONFIG_MTD_LART)		+= lart.o
 obj-$(CONFIG_MTD_BLOCK2MTD)	+= block2mtd.o
@@ -20,4 +21,4 @@
 obj-$(CONFIG_MTD_SPEAR_SMI)	+= spear_smi.o
 obj-$(CONFIG_MTD_SST25L)	+= sst25l.o
 
-CFLAGS_docg3.o			+= -I$(src)
\ No newline at end of file
+CFLAGS_docg3.o			+= -I$(src)
diff --git a/drivers/mtd/devices/msm_nand.c b/drivers/mtd/devices/msm_nand.c
new file mode 100644
index 0000000..1748df7
--- /dev/null
+++ b/drivers/mtd/devices/msm_nand.c
@@ -0,0 +1,7128 @@
+/*
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ *
+ * 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/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/crc16.h>
+#include <linux/bitrev.h>
+
+#include <asm/dma.h>
+#include <asm/mach/flash.h>
+
+#include <mach/dma.h>
+
+#include "msm_nand.h"
+
+unsigned long msm_nand_phys;
+unsigned long msm_nandc01_phys;
+unsigned long msm_nandc10_phys;
+unsigned long msm_nandc11_phys;
+unsigned long ebi2_register_base;
+uint32_t dual_nand_ctlr_present;
+uint32_t interleave_enable;
+uint32_t enable_bch_ecc;
+
+#define MSM_NAND_DMA_BUFFER_SIZE SZ_8K
+#define MSM_NAND_DMA_BUFFER_SLOTS \
+	(MSM_NAND_DMA_BUFFER_SIZE / (sizeof(((atomic_t *)0)->counter) * 8))
+
+#define MSM_NAND_CFG0_RAW_ONFI_IDENTIFIER 0x88000800
+#define MSM_NAND_CFG0_RAW_ONFI_PARAM_INFO 0x88040000
+#define MSM_NAND_CFG1_RAW_ONFI_IDENTIFIER 0x0005045d
+#define MSM_NAND_CFG1_RAW_ONFI_PARAM_INFO 0x0005045d
+
+#define ONFI_IDENTIFIER_LENGTH 0x0004
+#define ONFI_PARAM_INFO_LENGTH 0x0200
+#define ONFI_PARAM_PAGE_LENGTH 0x0100
+
+#define ONFI_PARAMETER_PAGE_SIGNATURE 0x49464E4F
+
+#define FLASH_READ_ONFI_IDENTIFIER_COMMAND 0x90
+#define FLASH_READ_ONFI_IDENTIFIER_ADDRESS 0x20
+#define FLASH_READ_ONFI_PARAMETERS_COMMAND 0xEC
+#define FLASH_READ_ONFI_PARAMETERS_ADDRESS 0x00
+
+#define VERBOSE 0
+
+struct msm_nand_chip {
+	struct device *dev;
+	wait_queue_head_t wait_queue;
+	atomic_t dma_buffer_busy;
+	unsigned dma_channel;
+	uint8_t *dma_buffer;
+	dma_addr_t dma_addr;
+	unsigned CFG0, CFG1, CFG0_RAW, CFG1_RAW;
+	uint32_t ecc_buf_cfg;
+	uint32_t ecc_bch_cfg;
+	uint32_t ecc_parity_bytes;
+	unsigned cw_size;
+};
+
+#define CFG1_WIDE_FLASH (1U << 1)
+
+/* TODO: move datamover code out */
+
+#define SRC_CRCI_NAND_CMD  CMD_SRC_CRCI(DMOV_NAND_CRCI_CMD)
+#define DST_CRCI_NAND_CMD  CMD_DST_CRCI(DMOV_NAND_CRCI_CMD)
+#define SRC_CRCI_NAND_DATA CMD_SRC_CRCI(DMOV_NAND_CRCI_DATA)
+#define DST_CRCI_NAND_DATA CMD_DST_CRCI(DMOV_NAND_CRCI_DATA)
+
+#define msm_virt_to_dma(chip, vaddr) \
+	((chip)->dma_addr + \
+	 ((uint8_t *)(vaddr) - (chip)->dma_buffer))
+
+/**
+ * msm_nand_oob_64 - oob info for 2KB page
+ */
+static struct nand_ecclayout msm_nand_oob_64 = {
+	.eccbytes	= 40,
+	.eccpos		= {
+		0,  1,  2,  3,  4,  5,  6,  7,  8,  9,
+		10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+		20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+		46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
+		},
+	.oobavail	= 16,
+	.oobfree	= {
+		{30, 16},
+	}
+};
+
+/**
+ * msm_nand_oob_128 - oob info for 4KB page
+ */
+static struct nand_ecclayout msm_nand_oob_128 = {
+	.eccbytes	= 80,
+	.eccpos		= {
+		  0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
+		 10,  11,  12,  13,  14,  15,  16,  17,  18,  19,
+		 20,  21,  22,  23,  24,  25,  26,  27,  28,  29,
+		 30,  31,  32,  33,  34,  35,  36,  37,  38,  39,
+		 40,  41,  42,  43,  44,  45,  46,  47,  48,  49,
+		 50,  51,  52,  53,  54,  55,  56,  57,  58,  59,
+		 60,  61,  62,  63,  64,  65,  66,  67,  68,  69,
+		102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
+		},
+	.oobavail	= 32,
+	.oobfree	= {
+		{70, 32},
+	}
+};
+
+/**
+ * msm_nand_oob_224 - oob info for 4KB page 8Bit interface
+ */
+static struct nand_ecclayout msm_nand_oob_224_x8 = {
+	.eccbytes	= 104,
+	.eccpos		= {
+		  0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,
+		 13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,
+		 26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,
+		 39,  40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,
+		 52,  53,  54,  55,  56,  57,  58,  59,	 60,  61,  62,  63,  64,
+		 65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,
+		 78,  79,  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90,
+		123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135,
+		},
+	.oobavail	= 32,
+	.oobfree	= {
+		{91, 32},
+	}
+};
+
+/**
+ * msm_nand_oob_224 - oob info for 4KB page 16Bit interface
+ */
+static struct nand_ecclayout msm_nand_oob_224_x16 = {
+	.eccbytes	= 112,
+	.eccpos		= {
+	  0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,
+	 14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,
+	 28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,
+	 42,  43,  44,  45,  46,  47,  48,  49,  50,  51,  52,  53,  54,  55,
+	 56,  57,  58,  59,  60,  61,  62,  63,  64,  65,  66,  67,  68,  69,
+	 70,  71,  72,  73,  74,  75,  76,  77,  78,  79,  80,  81,  82,  83,
+	 84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95,  96,  97,
+	130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
+	},
+	.oobavail	= 32,
+	.oobfree	= {
+		{98, 32},
+	}
+};
+
+/**
+ * msm_nand_oob_256 - oob info for 8KB page
+ */
+static struct nand_ecclayout msm_nand_oob_256 = {
+	.eccbytes 	= 160,
+	.eccpos 	= {
+		  0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
+		 10,  11,  12,  13,  14,  15,  16,  17,  18,  19,
+		 20,  21,  22,  23,  24,  25,  26,  27,  28,  29,
+		 30,  31,  32,  33,  34,  35,  36,  37,  38,  39,
+		 40,  41,  42,  43,  44,  45,  46,  47,  48,  49,
+		 50,  51,  52,  53,  54,  55,  56,  57,  58,  59,
+		 60,  61,  62,  63,  64,  65,  66,  67,  68,  69,
+		 70,  71,  72,  73,  74,  75,  76,  77,  78,  79,
+		 80,  81,  82,  83,  84,  85,  86,  87,  88,  89,
+		 90,  91,  92,  93,  94,  96,  97,  98 , 99, 100,
+		101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
+		111, 112, 113, 114, 115, 116, 117, 118, 119, 120,
+		121, 122, 123, 124, 125, 126, 127, 128, 129, 130,
+		131, 132, 133, 134, 135, 136, 137, 138, 139, 140,
+		141, 142, 143, 144, 145, 146, 147, 148, 149, 150,
+		215, 216, 217, 218, 219, 220, 221, 222, 223, 224,
+		},
+	.oobavail	= 64,
+	.oobfree	= {
+		{151, 64},
+	}
+};
+
+/**
+ * msm_onenand_oob_64 - oob info for large (2KB) page
+ */
+static struct nand_ecclayout msm_onenand_oob_64 = {
+	.eccbytes	= 20,
+	.eccpos		= {
+		8, 9, 10, 11, 12,
+		24, 25, 26, 27, 28,
+		40, 41, 42, 43, 44,
+		56, 57, 58, 59, 60,
+		},
+	.oobavail	= 20,
+	.oobfree	= {
+		{2, 3}, {14, 2}, {18, 3}, {30, 2},
+		{34, 3}, {46, 2}, {50, 3}, {62, 2}
+	}
+};
+
+static void *msm_nand_get_dma_buffer(struct msm_nand_chip *chip, size_t size)
+{
+	unsigned int bitmask, free_bitmask, old_bitmask;
+	unsigned int need_mask, current_need_mask;
+	int free_index;
+
+	need_mask = (1UL << DIV_ROUND_UP(size, MSM_NAND_DMA_BUFFER_SLOTS)) - 1;
+	bitmask = atomic_read(&chip->dma_buffer_busy);
+	free_bitmask = ~bitmask;
+	do {
+		free_index = __ffs(free_bitmask);
+		current_need_mask = need_mask << free_index;
+
+		if (size + free_index * MSM_NAND_DMA_BUFFER_SLOTS >=
+						 MSM_NAND_DMA_BUFFER_SIZE)
+			return NULL;
+
+		if ((bitmask & current_need_mask) == 0) {
+			old_bitmask =
+				atomic_cmpxchg(&chip->dma_buffer_busy,
+					       bitmask,
+					       bitmask | current_need_mask);
+			if (old_bitmask == bitmask)
+				return chip->dma_buffer +
+					free_index * MSM_NAND_DMA_BUFFER_SLOTS;
+			free_bitmask = 0; /* force return */
+		}
+		/* current free range was too small, clear all free bits */
+		/* below the top busy bit within current_need_mask */
+		free_bitmask &=
+			~(~0U >> (32 - fls(bitmask & current_need_mask)));
+	} while (free_bitmask);
+
+	return NULL;
+}
+
+static void msm_nand_release_dma_buffer(struct msm_nand_chip *chip,
+					void *buffer, size_t size)
+{
+	int index;
+	unsigned int used_mask;
+
+	used_mask = (1UL << DIV_ROUND_UP(size, MSM_NAND_DMA_BUFFER_SLOTS)) - 1;
+	index = ((uint8_t *)buffer - chip->dma_buffer) /
+		MSM_NAND_DMA_BUFFER_SLOTS;
+	atomic_sub(used_mask << index, &chip->dma_buffer_busy);
+
+	wake_up(&chip->wait_queue);
+}
+
+
+unsigned flash_rd_reg(struct msm_nand_chip *chip, unsigned addr)
+{
+	struct {
+		dmov_s cmd;
+		unsigned cmdptr;
+		unsigned data;
+	} *dma_buffer;
+	unsigned rv;
+
+	wait_event(chip->wait_queue,
+		   (dma_buffer = msm_nand_get_dma_buffer(
+			    chip, sizeof(*dma_buffer))));
+
+	dma_buffer->cmd.cmd = CMD_LC | CMD_OCB | CMD_OCU;
+	dma_buffer->cmd.src = addr;
+	dma_buffer->cmd.dst = msm_virt_to_dma(chip, &dma_buffer->data);
+	dma_buffer->cmd.len = 4;
+
+	dma_buffer->cmdptr =
+		(msm_virt_to_dma(chip, &dma_buffer->cmd) >> 3) | CMD_PTR_LP;
+	dma_buffer->data = 0xeeeeeeee;
+
+	mb();
+	msm_dmov_exec_cmd(
+		chip->dma_channel, DMOV_CMD_PTR_LIST |
+		DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
+	mb();
+
+	rv = dma_buffer->data;
+
+	msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+
+	return rv;
+}
+
+void flash_wr_reg(struct msm_nand_chip *chip, unsigned addr, unsigned val)
+{
+	struct {
+		dmov_s cmd;
+		unsigned cmdptr;
+		unsigned data;
+	} *dma_buffer;
+
+	wait_event(chip->wait_queue,
+		   (dma_buffer = msm_nand_get_dma_buffer(
+			    chip, sizeof(*dma_buffer))));
+
+	dma_buffer->cmd.cmd = CMD_LC | CMD_OCB | CMD_OCU;
+	dma_buffer->cmd.src = msm_virt_to_dma(chip, &dma_buffer->data);
+	dma_buffer->cmd.dst = addr;
+	dma_buffer->cmd.len = 4;
+
+	dma_buffer->cmdptr =
+		(msm_virt_to_dma(chip, &dma_buffer->cmd) >> 3) | CMD_PTR_LP;
+	dma_buffer->data = val;
+
+	mb();
+	msm_dmov_exec_cmd(
+		chip->dma_channel, DMOV_CMD_PTR_LIST |
+		DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
+	mb();
+
+	msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+}
+
+static dma_addr_t
+msm_nand_dma_map(struct device *dev, void *addr, size_t size,
+		 enum dma_data_direction dir)
+{
+	struct page *page;
+	unsigned long offset = (unsigned long)addr & ~PAGE_MASK;
+	if (virt_addr_valid(addr))
+		page = virt_to_page(addr);
+	else {
+		if (WARN_ON(size + offset > PAGE_SIZE))
+			return ~0;
+		page = vmalloc_to_page(addr);
+	}
+	return dma_map_page(dev, page, offset, size, dir);
+}
+
+uint32_t flash_read_id(struct msm_nand_chip *chip)
+{
+	struct {
+		dmov_s cmd[7];
+		unsigned cmdptr;
+		unsigned data[7];
+	} *dma_buffer;
+	uint32_t rv;
+
+	wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer
+				(chip, sizeof(*dma_buffer))));
+
+	dma_buffer->data[0] = 0 | 4;
+	dma_buffer->data[1] = MSM_NAND_CMD_FETCH_ID;
+	dma_buffer->data[2] = 1;
+	dma_buffer->data[3] = 0xeeeeeeee;
+	dma_buffer->data[4] = 0xeeeeeeee;
+	dma_buffer->data[5] = flash_rd_reg(chip, MSM_NAND_SFLASHC_BURST_CFG);
+	dma_buffer->data[6] = 0x00000000;
+	BUILD_BUG_ON(6 != ARRAY_SIZE(dma_buffer->data) - 1);
+
+	dma_buffer->cmd[0].cmd = 0 | CMD_OCB;
+	dma_buffer->cmd[0].src = msm_virt_to_dma(chip, &dma_buffer->data[6]);
+	dma_buffer->cmd[0].dst = MSM_NAND_SFLASHC_BURST_CFG;
+	dma_buffer->cmd[0].len = 4;
+
+	dma_buffer->cmd[1].cmd = 0;
+	dma_buffer->cmd[1].src = msm_virt_to_dma(chip, &dma_buffer->data[0]);
+	dma_buffer->cmd[1].dst = MSM_NAND_FLASH_CHIP_SELECT;
+	dma_buffer->cmd[1].len = 4;
+
+	dma_buffer->cmd[2].cmd = DST_CRCI_NAND_CMD;
+	dma_buffer->cmd[2].src = msm_virt_to_dma(chip, &dma_buffer->data[1]);
+	dma_buffer->cmd[2].dst = MSM_NAND_FLASH_CMD;
+	dma_buffer->cmd[2].len = 4;
+
+	dma_buffer->cmd[3].cmd = 0;
+	dma_buffer->cmd[3].src = msm_virt_to_dma(chip, &dma_buffer->data[2]);
+	dma_buffer->cmd[3].dst = MSM_NAND_EXEC_CMD;
+	dma_buffer->cmd[3].len = 4;
+
+	dma_buffer->cmd[4].cmd = SRC_CRCI_NAND_DATA;
+	dma_buffer->cmd[4].src = MSM_NAND_FLASH_STATUS;
+	dma_buffer->cmd[4].dst = msm_virt_to_dma(chip, &dma_buffer->data[3]);
+	dma_buffer->cmd[4].len = 4;
+
+	dma_buffer->cmd[5].cmd = 0;
+	dma_buffer->cmd[5].src = MSM_NAND_READ_ID;
+	dma_buffer->cmd[5].dst = msm_virt_to_dma(chip, &dma_buffer->data[4]);
+	dma_buffer->cmd[5].len = 4;
+
+	dma_buffer->cmd[6].cmd = CMD_OCU | CMD_LC;
+	dma_buffer->cmd[6].src = msm_virt_to_dma(chip, &dma_buffer->data[5]);
+	dma_buffer->cmd[6].dst = MSM_NAND_SFLASHC_BURST_CFG;
+	dma_buffer->cmd[6].len = 4;
+
+	BUILD_BUG_ON(6 != ARRAY_SIZE(dma_buffer->cmd) - 1);
+
+	dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3
+			) | CMD_PTR_LP;
+
+	mb();
+	msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST |
+		DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
+	mb();
+
+	pr_info("status: %x\n", dma_buffer->data[3]);
+	pr_info("nandid: %x maker %02x device %02x\n",
+	       dma_buffer->data[4], dma_buffer->data[4] & 0xff,
+	       (dma_buffer->data[4] >> 8) & 0xff);
+	rv = dma_buffer->data[4];
+	msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+	return rv;
+}
+
+struct flash_identification {
+	uint32_t flash_id;
+	uint32_t density;
+	uint32_t widebus;
+	uint32_t pagesize;
+	uint32_t blksize;
+	uint32_t oobsize;
+	uint32_t ecc_correctability;
+} supported_flash;
+
+uint16_t flash_onfi_crc_check(uint8_t *buffer, uint16_t count)
+{
+	int i;
+	uint16_t result;
+
+	for (i = 0; i < count; i++)
+		buffer[i] = bitrev8(buffer[i]);
+
+	result = bitrev16(crc16(bitrev16(0x4f4e), buffer, count));
+
+	for (i = 0; i < count; i++)
+		buffer[i] = bitrev8(buffer[i]);
+
+	return result;
+}
+
+
+uint32_t flash_onfi_probe(struct msm_nand_chip *chip)
+{
+	struct onfi_param_page {
+		uint32_t parameter_page_signature;
+		uint16_t revision_number;
+		uint16_t features_supported;
+		uint16_t optional_commands_supported;
+		uint8_t  reserved0[22];
+		uint8_t  device_manufacturer[12];
+		uint8_t  device_model[20];
+		uint8_t  jedec_manufacturer_id;
+		uint16_t date_code;
+		uint8_t  reserved1[13];
+		uint32_t number_of_data_bytes_per_page;
+		uint16_t number_of_spare_bytes_per_page;
+		uint32_t number_of_data_bytes_per_partial_page;
+		uint16_t number_of_spare_bytes_per_partial_page;
+		uint32_t number_of_pages_per_block;
+		uint32_t number_of_blocks_per_logical_unit;
+		uint8_t  number_of_logical_units;
+		uint8_t  number_of_address_cycles;
+		uint8_t  number_of_bits_per_cell;
+		uint16_t maximum_bad_blocks_per_logical_unit;
+		uint16_t block_endurance;
+		uint8_t  guaranteed_valid_begin_blocks;
+		uint16_t guaranteed_valid_begin_blocks_endurance;
+		uint8_t  number_of_programs_per_page;
+		uint8_t  partial_program_attributes;
+		uint8_t  number_of_bits_ecc_correctability;
+		uint8_t  number_of_interleaved_address_bits;
+		uint8_t  interleaved_operation_attributes;
+		uint8_t  reserved2[13];
+		uint8_t  io_pin_capacitance;
+		uint16_t timing_mode_support;
+		uint16_t program_cache_timing_mode_support;
+		uint16_t maximum_page_programming_time;
+		uint16_t maximum_block_erase_time;
+		uint16_t maximum_page_read_time;
+		uint16_t maximum_change_column_setup_time;
+		uint8_t  reserved3[23];
+		uint16_t vendor_specific_revision_number;
+		uint8_t  vendor_specific[88];
+		uint16_t integrity_crc;
+
+	} __attribute__((__packed__));
+
+	struct onfi_param_page *onfi_param_page_ptr;
+	uint8_t *onfi_identifier_buf = NULL;
+	uint8_t *onfi_param_info_buf = NULL;
+
+	struct {
+		dmov_s cmd[11];
+		unsigned cmdptr;
+		struct {
+			uint32_t cmd;
+			uint32_t addr0;
+			uint32_t addr1;
+			uint32_t cfg0;
+			uint32_t cfg1;
+			uint32_t exec;
+			uint32_t flash_status;
+			uint32_t devcmd1_orig;
+			uint32_t devcmdvld_orig;
+			uint32_t devcmd1_mod;
+			uint32_t devcmdvld_mod;
+			uint32_t sflash_bcfg_orig;
+			uint32_t sflash_bcfg_mod;
+		} data;
+	} *dma_buffer;
+	dmov_s *cmd;
+
+	unsigned page_address = 0;
+	int err = 0;
+	dma_addr_t dma_addr_param_info = 0;
+	dma_addr_t dma_addr_identifier = 0;
+	unsigned cmd_set_count = 2;
+	unsigned crc_chk_count = 0;
+
+	if (msm_nand_data.nr_parts) {
+		page_address = ((msm_nand_data.parts[0]).offset << 6);
+	} else {
+		pr_err("flash_onfi_probe: "
+				"No partition info available\n");
+		err = -EIO;
+		return err;
+	}
+
+	wait_event(chip->wait_queue, (onfi_identifier_buf =
+		msm_nand_get_dma_buffer(chip, ONFI_IDENTIFIER_LENGTH)));
+	dma_addr_identifier = msm_virt_to_dma(chip, onfi_identifier_buf);
+
+	wait_event(chip->wait_queue, (onfi_param_info_buf =
+		msm_nand_get_dma_buffer(chip, ONFI_PARAM_INFO_LENGTH)));
+	dma_addr_param_info = msm_virt_to_dma(chip, onfi_param_info_buf);
+
+	wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer
+				(chip, sizeof(*dma_buffer))));
+
+	dma_buffer->data.sflash_bcfg_orig = flash_rd_reg
+				(chip, MSM_NAND_SFLASHC_BURST_CFG);
+	dma_buffer->data.devcmd1_orig = flash_rd_reg(chip, MSM_NAND_DEV_CMD1);
+	dma_buffer->data.devcmdvld_orig = flash_rd_reg(chip,
+						 MSM_NAND_DEV_CMD_VLD);
+
+	while (cmd_set_count-- > 0) {
+		cmd = dma_buffer->cmd;
+
+		dma_buffer->data.devcmd1_mod = (dma_buffer->data.devcmd1_orig &
+				0xFFFFFF00) | (cmd_set_count
+				? FLASH_READ_ONFI_IDENTIFIER_COMMAND
+				: FLASH_READ_ONFI_PARAMETERS_COMMAND);
+		dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ;
+		dma_buffer->data.addr0 = (page_address << 16) | (cmd_set_count
+				? FLASH_READ_ONFI_IDENTIFIER_ADDRESS
+				: FLASH_READ_ONFI_PARAMETERS_ADDRESS);
+		dma_buffer->data.addr1 = (page_address >> 16) & 0xFF;
+		dma_buffer->data.cfg0 =	(cmd_set_count
+				? MSM_NAND_CFG0_RAW_ONFI_IDENTIFIER
+				: MSM_NAND_CFG0_RAW_ONFI_PARAM_INFO);
+		dma_buffer->data.cfg1 =	(cmd_set_count
+				? MSM_NAND_CFG1_RAW_ONFI_IDENTIFIER
+				: MSM_NAND_CFG1_RAW_ONFI_PARAM_INFO);
+		dma_buffer->data.sflash_bcfg_mod = 0x00000000;
+		dma_buffer->data.devcmdvld_mod = (dma_buffer->
+				data.devcmdvld_orig & 0xFFFFFFFE);
+		dma_buffer->data.exec = 1;
+		dma_buffer->data.flash_status = 0xeeeeeeee;
+
+		/* Put the Nand ctlr in Async mode and disable SFlash ctlr */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip,
+				&dma_buffer->data.sflash_bcfg_mod);
+		cmd->dst = MSM_NAND_SFLASHC_BURST_CFG;
+		cmd->len = 4;
+		cmd++;
+
+		/* Block on cmd ready, & write CMD,ADDR0,ADDR1,CHIPSEL regs */
+		cmd->cmd = DST_CRCI_NAND_CMD;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd);
+		cmd->dst = MSM_NAND_FLASH_CMD;
+		cmd->len = 12;
+		cmd++;
+
+		/* Configure the CFG0 and CFG1 registers */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip,
+				&dma_buffer->data.cfg0);
+		cmd->dst = MSM_NAND_DEV0_CFG0;
+		cmd->len = 8;
+		cmd++;
+
+		/* Configure the DEV_CMD_VLD register */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip,
+				&dma_buffer->data.devcmdvld_mod);
+		cmd->dst = MSM_NAND_DEV_CMD_VLD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Configure the DEV_CMD1 register */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip,
+				&dma_buffer->data.devcmd1_mod);
+		cmd->dst = MSM_NAND_DEV_CMD1;
+		cmd->len = 4;
+		cmd++;
+
+		/* Kick the execute command */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip,
+				&dma_buffer->data.exec);
+		cmd->dst = MSM_NAND_EXEC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Block on data ready, and read the two status registers */
+		cmd->cmd = SRC_CRCI_NAND_DATA;
+		cmd->src = MSM_NAND_FLASH_STATUS;
+		cmd->dst = msm_virt_to_dma(chip,
+				&dma_buffer->data.flash_status);
+		cmd->len = 4;
+		cmd++;
+
+		/* Read data block - valid only if status says success */
+		cmd->cmd = 0;
+		cmd->src = MSM_NAND_FLASH_BUFFER;
+		cmd->dst = (cmd_set_count ? dma_addr_identifier :
+				dma_addr_param_info);
+		cmd->len = (cmd_set_count ? ONFI_IDENTIFIER_LENGTH :
+				ONFI_PARAM_INFO_LENGTH);
+		cmd++;
+
+		/* Restore the DEV_CMD1 register */
+		cmd->cmd = 0 ;
+		cmd->src = msm_virt_to_dma(chip,
+				&dma_buffer->data.devcmd1_orig);
+		cmd->dst = MSM_NAND_DEV_CMD1;
+		cmd->len = 4;
+		cmd++;
+
+		/* Restore the DEV_CMD_VLD register */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip,
+				&dma_buffer->data.devcmdvld_orig);
+		cmd->dst = MSM_NAND_DEV_CMD_VLD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Restore the SFLASH_BURST_CONFIG register */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip,
+				&dma_buffer->data.sflash_bcfg_orig);
+		cmd->dst = MSM_NAND_SFLASHC_BURST_CFG;
+		cmd->len = 4;
+		cmd++;
+
+		BUILD_BUG_ON(11 != ARRAY_SIZE(dma_buffer->cmd));
+		BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
+		dma_buffer->cmd[0].cmd |= CMD_OCB;
+		cmd[-1].cmd |= CMD_OCU | CMD_LC;
+
+		dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd)
+				>> 3) | CMD_PTR_LP;
+
+		mb();
+		msm_dmov_exec_cmd(chip->dma_channel,
+			DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip,
+			&dma_buffer->cmdptr)));
+		mb();
+
+		/* Check for errors, protection violations etc */
+		if (dma_buffer->data.flash_status & 0x110) {
+			pr_info("MPU/OP error (0x%x) during "
+					"ONFI probe\n",
+					dma_buffer->data.flash_status);
+			err = -EIO;
+			break;
+		}
+
+		if (cmd_set_count) {
+			onfi_param_page_ptr = (struct onfi_param_page *)
+				(&(onfi_identifier_buf[0]));
+			if (onfi_param_page_ptr->parameter_page_signature !=
+					ONFI_PARAMETER_PAGE_SIGNATURE) {
+				pr_info("ONFI probe : Found a non"
+						"ONFI Compliant device \n");
+				err = -EIO;
+				break;
+			}
+		} else {
+			for (crc_chk_count = 0; crc_chk_count <
+					ONFI_PARAM_INFO_LENGTH
+					/ ONFI_PARAM_PAGE_LENGTH;
+					crc_chk_count++) {
+				onfi_param_page_ptr =
+					(struct onfi_param_page *)
+					(&(onfi_param_info_buf
+					[ONFI_PARAM_PAGE_LENGTH *
+					crc_chk_count]));
+				if (flash_onfi_crc_check(
+					(uint8_t *)onfi_param_page_ptr,
+					ONFI_PARAM_PAGE_LENGTH - 2) ==
+					onfi_param_page_ptr->integrity_crc) {
+					break;
+				}
+			}
+			if (crc_chk_count >= ONFI_PARAM_INFO_LENGTH
+					/ ONFI_PARAM_PAGE_LENGTH) {
+				pr_info("ONFI probe : CRC Check "
+						"failed on ONFI Parameter "
+						"data \n");
+				err = -EIO;
+				break;
+			} else {
+				supported_flash.flash_id =
+					flash_read_id(chip);
+				supported_flash.widebus  =
+					onfi_param_page_ptr->
+					features_supported & 0x01;
+				supported_flash.pagesize =
+					onfi_param_page_ptr->
+					number_of_data_bytes_per_page;
+				supported_flash.blksize  =
+					onfi_param_page_ptr->
+					number_of_pages_per_block *
+					supported_flash.pagesize;
+				supported_flash.oobsize  =
+					onfi_param_page_ptr->
+					number_of_spare_bytes_per_page;
+				supported_flash.density  =
+					onfi_param_page_ptr->
+					number_of_blocks_per_logical_unit
+					* supported_flash.blksize;
+				supported_flash.ecc_correctability =
+					onfi_param_page_ptr->
+					number_of_bits_ecc_correctability;
+
+				pr_info("ONFI probe : Found an ONFI "
+					"compliant device %s\n",
+					onfi_param_page_ptr->device_model);
+
+				/* Temporary hack for MT29F4G08ABC device.
+				 * Since the device is not properly adhering
+				 * to ONFi specification it is reporting
+				 * as 16 bit device though it is 8 bit device!!!
+				 */
+				if (!strncmp(onfi_param_page_ptr->device_model,
+					"MT29F4G08ABC", 12))
+					supported_flash.widebus  = 0;
+			}
+		}
+	}
+
+	msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+	msm_nand_release_dma_buffer(chip, onfi_param_info_buf,
+			ONFI_PARAM_INFO_LENGTH);
+	msm_nand_release_dma_buffer(chip, onfi_identifier_buf,
+			ONFI_IDENTIFIER_LENGTH);
+
+	return err;
+}
+
+static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from,
+			     struct mtd_oob_ops *ops)
+{
+	struct msm_nand_chip *chip = mtd->priv;
+
+	struct {
+		dmov_s cmd[8 * 5 + 2];
+		unsigned cmdptr;
+		struct {
+			uint32_t cmd;
+			uint32_t addr0;
+			uint32_t addr1;
+			uint32_t chipsel;
+			uint32_t cfg0;
+			uint32_t cfg1;
+			uint32_t eccbchcfg;
+			uint32_t exec;
+			uint32_t ecccfg;
+			struct {
+				uint32_t flash_status;
+				uint32_t buffer_status;
+			} result[8];
+		} data;
+	} *dma_buffer;
+	dmov_s *cmd;
+	unsigned n;
+	unsigned page = 0;
+	uint32_t oob_len;
+	uint32_t sectordatasize;
+	uint32_t sectoroobsize;
+	int err, pageerr, rawerr;
+	dma_addr_t data_dma_addr = 0;
+	dma_addr_t oob_dma_addr = 0;
+	dma_addr_t data_dma_addr_curr = 0;
+	dma_addr_t oob_dma_addr_curr = 0;
+	uint32_t oob_col = 0;
+	unsigned page_count;
+	unsigned pages_read = 0;
+	unsigned start_sector = 0;
+	uint32_t ecc_errors;
+	uint32_t total_ecc_errors = 0;
+	unsigned cwperpage;
+#if VERBOSE
+	pr_info("================================================="
+			"================\n");
+	pr_info("%s:\nfrom 0x%llx mode %d\ndatbuf 0x%p datlen 0x%x"
+			"\noobbuf 0x%p ooblen 0x%x\n",
+			__func__, from, ops->mode, ops->datbuf, ops->len,
+			ops->oobbuf, ops->ooblen);
+#endif
+
+	if (mtd->writesize == 2048)
+		page = from >> 11;
+
+	if (mtd->writesize == 4096)
+		page = from >> 12;
+
+	oob_len = ops->ooblen;
+	cwperpage = (mtd->writesize >> 9);
+
+	if (from & (mtd->writesize - 1)) {
+		pr_err("%s: unsupported from, 0x%llx\n",
+		       __func__, from);
+		return -EINVAL;
+	}
+	if (ops->mode != MTD_OPS_RAW) {
+		if (ops->datbuf != NULL && (ops->len % mtd->writesize) != 0) {
+			/* when ops->datbuf is NULL, ops->len can be ooblen */
+			pr_err("%s: unsupported ops->len, %d\n",
+			       __func__, ops->len);
+			return -EINVAL;
+		}
+	} else {
+		if (ops->datbuf != NULL &&
+			(ops->len % (mtd->writesize + mtd->oobsize)) != 0) {
+			pr_err("%s: unsupported ops->len,"
+				" %d for MTD_OPS_RAW\n", __func__, ops->len);
+			return -EINVAL;
+		}
+	}
+
+	if (ops->mode != MTD_OPS_RAW && ops->ooblen != 0 && ops->ooboffs != 0) {
+		pr_err("%s: unsupported ops->ooboffs, %d\n",
+		       __func__, ops->ooboffs);
+		return -EINVAL;
+	}
+
+	if (ops->oobbuf && !ops->datbuf && ops->mode == MTD_OPS_AUTO_OOB)
+		start_sector = cwperpage - 1;
+
+	if (ops->oobbuf && !ops->datbuf) {
+		page_count = ops->ooblen / ((ops->mode == MTD_OPS_AUTO_OOB) ?
+			mtd->oobavail : mtd->oobsize);
+		if ((page_count == 0) && (ops->ooblen))
+			page_count = 1;
+	} else if (ops->mode != MTD_OPS_RAW)
+		page_count = ops->len / mtd->writesize;
+	else
+		page_count = ops->len / (mtd->writesize + mtd->oobsize);
+
+	if (ops->datbuf) {
+		data_dma_addr_curr = data_dma_addr =
+			msm_nand_dma_map(chip->dev, ops->datbuf, ops->len,
+				       DMA_FROM_DEVICE);
+		if (dma_mapping_error(chip->dev, data_dma_addr)) {
+			pr_err("msm_nand_read_oob: failed to get dma addr "
+			       "for %p\n", ops->datbuf);
+			return -EIO;
+		}
+	}
+	if (ops->oobbuf) {
+		memset(ops->oobbuf, 0xff, ops->ooblen);
+		oob_dma_addr_curr = oob_dma_addr =
+			msm_nand_dma_map(chip->dev, ops->oobbuf,
+				       ops->ooblen, DMA_BIDIRECTIONAL);
+		if (dma_mapping_error(chip->dev, oob_dma_addr)) {
+			pr_err("msm_nand_read_oob: failed to get dma addr "
+			       "for %p\n", ops->oobbuf);
+			err = -EIO;
+			goto err_dma_map_oobbuf_failed;
+		}
+	}
+
+	wait_event(chip->wait_queue,
+		   (dma_buffer = msm_nand_get_dma_buffer(
+			    chip, sizeof(*dma_buffer))));
+
+	oob_col = start_sector * chip->cw_size;
+	if (chip->CFG1 & CFG1_WIDE_FLASH)
+		oob_col >>= 1;
+
+	err = 0;
+	while (page_count-- > 0) {
+		cmd = dma_buffer->cmd;
+
+		/* CMD / ADDR0 / ADDR1 / CHIPSEL program values */
+		if (ops->mode != MTD_OPS_RAW) {
+			dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ_ECC;
+			dma_buffer->data.cfg0 =
+			(chip->CFG0 & ~(7U << 6))
+				| (((cwperpage-1) - start_sector) << 6);
+			dma_buffer->data.cfg1 = chip->CFG1;
+			if (enable_bch_ecc)
+				dma_buffer->data.eccbchcfg = chip->ecc_bch_cfg;
+		} else {
+			dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ;
+			dma_buffer->data.cfg0 = (chip->CFG0_RAW
+					& ~(7U << 6)) | ((cwperpage-1) << 6);
+			dma_buffer->data.cfg1 = chip->CFG1_RAW |
+					(chip->CFG1 & CFG1_WIDE_FLASH);
+		}
+
+		dma_buffer->data.addr0 = (page << 16) | oob_col;
+		dma_buffer->data.addr1 = (page >> 16) & 0xff;
+		/* chipsel_0 + enable DM interface */
+		dma_buffer->data.chipsel = 0 | 4;
+
+
+		/* GO bit for the EXEC register */
+		dma_buffer->data.exec = 1;
+
+
+		BUILD_BUG_ON(8 != ARRAY_SIZE(dma_buffer->data.result));
+
+		for (n = start_sector; n < cwperpage; n++) {
+			/* flash + buffer status return words */
+			dma_buffer->data.result[n].flash_status = 0xeeeeeeee;
+			dma_buffer->data.result[n].buffer_status = 0xeeeeeeee;
+
+			/* block on cmd ready, then
+			 * write CMD / ADDR0 / ADDR1 / CHIPSEL
+			 * regs in a burst
+			 */
+			cmd->cmd = DST_CRCI_NAND_CMD;
+			cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd);
+			cmd->dst = MSM_NAND_FLASH_CMD;
+			if (n == start_sector)
+				cmd->len = 16;
+			else
+				cmd->len = 4;
+			cmd++;
+
+			if (n == start_sector) {
+				cmd->cmd = 0;
+				cmd->src = msm_virt_to_dma(chip,
+							&dma_buffer->data.cfg0);
+				cmd->dst = MSM_NAND_DEV0_CFG0;
+				if (enable_bch_ecc)
+					cmd->len = 12;
+				else
+					cmd->len = 8;
+				cmd++;
+
+				dma_buffer->data.ecccfg = chip->ecc_buf_cfg;
+				cmd->cmd = 0;
+				cmd->src = msm_virt_to_dma(chip,
+						&dma_buffer->data.ecccfg);
+				cmd->dst = MSM_NAND_EBI2_ECC_BUF_CFG;
+				cmd->len = 4;
+				cmd++;
+			}
+
+			/* kick the execute register */
+			cmd->cmd = 0;
+			cmd->src =
+				msm_virt_to_dma(chip, &dma_buffer->data.exec);
+			cmd->dst = MSM_NAND_EXEC_CMD;
+			cmd->len = 4;
+			cmd++;
+
+			/* block on data ready, then
+			 * read the status register
+			 */
+			cmd->cmd = SRC_CRCI_NAND_DATA;
+			cmd->src = MSM_NAND_FLASH_STATUS;
+			cmd->dst = msm_virt_to_dma(chip,
+						   &dma_buffer->data.result[n]);
+			/* MSM_NAND_FLASH_STATUS + MSM_NAND_BUFFER_STATUS */
+			cmd->len = 8;
+			cmd++;
+
+			/* read data block
+			 * (only valid if status says success)
+			 */
+			if (ops->datbuf) {
+				if (ops->mode != MTD_OPS_RAW)
+					sectordatasize = (n < (cwperpage - 1))
+					? 516 : (512 - ((cwperpage - 1) << 2));
+				else
+					sectordatasize = chip->cw_size;
+
+				cmd->cmd = 0;
+				cmd->src = MSM_NAND_FLASH_BUFFER;
+				cmd->dst = data_dma_addr_curr;
+				data_dma_addr_curr += sectordatasize;
+				cmd->len = sectordatasize;
+				cmd++;
+			}
+
+			if (ops->oobbuf && (n == (cwperpage - 1)
+			     || ops->mode != MTD_OPS_AUTO_OOB)) {
+				cmd->cmd = 0;
+				if (n == (cwperpage - 1)) {
+					cmd->src = MSM_NAND_FLASH_BUFFER +
+						(512 - ((cwperpage - 1) << 2));
+					sectoroobsize = (cwperpage << 2);
+					if (ops->mode != MTD_OPS_AUTO_OOB)
+						sectoroobsize +=
+							chip->ecc_parity_bytes;
+				} else {
+					cmd->src = MSM_NAND_FLASH_BUFFER + 516;
+					sectoroobsize = chip->ecc_parity_bytes;
+				}
+
+				cmd->dst = oob_dma_addr_curr;
+				if (sectoroobsize < oob_len)
+					cmd->len = sectoroobsize;
+				else
+					cmd->len = oob_len;
+				oob_dma_addr_curr += cmd->len;
+				oob_len -= cmd->len;
+				if (cmd->len > 0)
+					cmd++;
+			}
+		}
+
+		BUILD_BUG_ON(8 * 5 + 2 != ARRAY_SIZE(dma_buffer->cmd));
+		BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
+		dma_buffer->cmd[0].cmd |= CMD_OCB;
+		cmd[-1].cmd |= CMD_OCU | CMD_LC;
+
+		dma_buffer->cmdptr =
+			(msm_virt_to_dma(chip, dma_buffer->cmd) >> 3)
+			| CMD_PTR_LP;
+
+		mb();
+		msm_dmov_exec_cmd(chip->dma_channel,
+			DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip,
+			&dma_buffer->cmdptr)));
+		mb();
+
+		/* if any of the writes failed (0x10), or there
+		 * was a protection violation (0x100), we lose
+		 */
+		pageerr = rawerr = 0;
+		for (n = start_sector; n < cwperpage; n++) {
+			if (dma_buffer->data.result[n].flash_status & 0x110) {
+				rawerr = -EIO;
+				break;
+			}
+		}
+		if (rawerr) {
+			if (ops->datbuf && ops->mode != MTD_OPS_RAW) {
+				uint8_t *datbuf = ops->datbuf +
+					pages_read * mtd->writesize;
+
+				dma_sync_single_for_cpu(chip->dev,
+					data_dma_addr_curr-mtd->writesize,
+					mtd->writesize, DMA_BIDIRECTIONAL);
+
+				for (n = 0; n < mtd->writesize; n++) {
+					/* empty blocks read 0x54 at
+					 * these offsets
+					 */
+					if ((n % 516 == 3 || n % 516 == 175)
+							&& datbuf[n] == 0x54)
+						datbuf[n] = 0xff;
+					if (datbuf[n] != 0xff) {
+						pageerr = rawerr;
+						break;
+					}
+				}
+
+				dma_sync_single_for_device(chip->dev,
+					data_dma_addr_curr-mtd->writesize,
+					mtd->writesize, DMA_BIDIRECTIONAL);
+
+			}
+			if (ops->oobbuf) {
+				dma_sync_single_for_cpu(chip->dev,
+				oob_dma_addr_curr - (ops->ooblen - oob_len),
+				ops->ooblen - oob_len, DMA_BIDIRECTIONAL);
+
+				for (n = 0; n < ops->ooblen; n++) {
+					if (ops->oobbuf[n] != 0xff) {
+						pageerr = rawerr;
+						break;
+					}
+				}
+
+				dma_sync_single_for_device(chip->dev,
+				oob_dma_addr_curr - (ops->ooblen - oob_len),
+				ops->ooblen - oob_len, DMA_BIDIRECTIONAL);
+			}
+		}
+		if (pageerr) {
+			for (n = start_sector; n < cwperpage; n++) {
+				if (enable_bch_ecc ?
+			(dma_buffer->data.result[n].buffer_status & 0x10) :
+			(dma_buffer->data.result[n].buffer_status & 0x8)) {
+					/* not thread safe */
+					mtd->ecc_stats.failed++;
+					pageerr = -EBADMSG;
+					break;
+				}
+			}
+		}
+		if (!rawerr) { /* check for corretable errors */
+			for (n = start_sector; n < cwperpage; n++) {
+				ecc_errors = enable_bch_ecc ?
+			(dma_buffer->data.result[n].buffer_status & 0xF) :
+			(dma_buffer->data.result[n].buffer_status & 0x7);
+				if (ecc_errors) {
+					total_ecc_errors += ecc_errors;
+					/* not thread safe */
+					mtd->ecc_stats.corrected += ecc_errors;
+					if (ecc_errors > 1)
+						pageerr = -EUCLEAN;
+				}
+			}
+		}
+		if (pageerr && (pageerr != -EUCLEAN || err == 0))
+			err = pageerr;
+
+#if VERBOSE
+		if (rawerr && !pageerr) {
+			pr_err("msm_nand_read_oob %llx %x %x empty page\n",
+			       (loff_t)page * mtd->writesize, ops->len,
+			       ops->ooblen);
+		} else {
+			for (n = start_sector; n < cwperpage; n++)
+				pr_info("flash_status[%d] = %x,\
+				buffr_status[%d] = %x\n",
+				n, dma_buffer->data.result[n].flash_status,
+				n, dma_buffer->data.result[n].buffer_status);
+		}
+#endif
+		if (err && err != -EUCLEAN && err != -EBADMSG)
+			break;
+		pages_read++;
+		page++;
+	}
+	msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+
+	if (ops->oobbuf) {
+		dma_unmap_page(chip->dev, oob_dma_addr,
+				 ops->ooblen, DMA_FROM_DEVICE);
+	}
+err_dma_map_oobbuf_failed:
+	if (ops->datbuf) {
+		dma_unmap_page(chip->dev, data_dma_addr,
+				 ops->len, DMA_BIDIRECTIONAL);
+	}
+
+	if (ops->mode != MTD_OPS_RAW)
+		ops->retlen = mtd->writesize * pages_read;
+	else
+		ops->retlen = (mtd->writesize +  mtd->oobsize) *
+							pages_read;
+	ops->oobretlen = ops->ooblen - oob_len;
+	if (err)
+		pr_err("msm_nand_read_oob %llx %x %x failed %d, corrected %d\n",
+		       from, ops->datbuf ? ops->len : 0, ops->ooblen, err,
+		       total_ecc_errors);
+#if VERBOSE
+	pr_info("\n%s: ret %d, retlen %d oobretlen %d\n",
+			__func__, err, ops->retlen, ops->oobretlen);
+
+	pr_info("==================================================="
+			"==============\n");
+#endif
+	return err;
+}
+
+static int msm_nand_read_oob_dualnandc(struct mtd_info *mtd, loff_t from,
+			struct mtd_oob_ops *ops)
+{
+	struct msm_nand_chip *chip = mtd->priv;
+
+	struct {
+		dmov_s cmd[16 * 6 + 20];
+		unsigned cmdptr;
+		struct {
+			uint32_t cmd;
+			uint32_t nandc01_addr0;
+			uint32_t nandc10_addr0;
+			uint32_t nandc11_addr1;
+			uint32_t chipsel_cs0;
+			uint32_t chipsel_cs1;
+			uint32_t cfg0;
+			uint32_t cfg1;
+			uint32_t eccbchcfg;
+			uint32_t exec;
+			uint32_t ecccfg;
+			uint32_t ebi2_chip_select_cfg0;
+			uint32_t adm_mux_data_ack_req_nc01;
+			uint32_t adm_mux_cmd_ack_req_nc01;
+			uint32_t adm_mux_data_ack_req_nc10;
+			uint32_t adm_mux_cmd_ack_req_nc10;
+			uint32_t adm_default_mux;
+			uint32_t default_ebi2_chip_select_cfg0;
+			uint32_t nc10_flash_dev_cmd_vld;
+			uint32_t nc10_flash_dev_cmd1;
+			uint32_t nc10_flash_dev_cmd_vld_default;
+			uint32_t nc10_flash_dev_cmd1_default;
+			struct {
+				uint32_t flash_status;
+				uint32_t buffer_status;
+			} result[16];
+		} data;
+	} *dma_buffer;
+	dmov_s *cmd;
+	unsigned n;
+	unsigned page = 0;
+	uint32_t oob_len;
+	uint32_t sectordatasize;
+	uint32_t sectoroobsize;
+	int err, pageerr, rawerr;
+	dma_addr_t data_dma_addr = 0;
+	dma_addr_t oob_dma_addr = 0;
+	dma_addr_t data_dma_addr_curr = 0;
+	dma_addr_t oob_dma_addr_curr = 0;
+	uint32_t oob_col = 0;
+	unsigned page_count;
+	unsigned pages_read = 0;
+	unsigned start_sector = 0;
+	uint32_t ecc_errors;
+	uint32_t total_ecc_errors = 0;
+	unsigned cwperpage;
+	unsigned cw_offset = chip->cw_size;
+#if VERBOSE
+		pr_info("================================================="
+				"============\n");
+		pr_info("%s:\nfrom 0x%llx mode %d\ndatbuf 0x%p datlen 0x%x"
+				"\noobbuf 0x%p ooblen 0x%x\n\n",
+				__func__, from, ops->mode, ops->datbuf,
+				ops->len, ops->oobbuf, ops->ooblen);
+#endif
+
+	if (mtd->writesize == 2048)
+		page = from >> 11;
+
+	if (mtd->writesize == 4096)
+		page = from >> 12;
+
+	if (interleave_enable)
+		page = (from >> 1) >> 12;
+
+	oob_len = ops->ooblen;
+	cwperpage = (mtd->writesize >> 9);
+
+	if (from & (mtd->writesize - 1)) {
+		pr_err("%s: unsupported from, 0x%llx\n",
+		       __func__, from);
+		return -EINVAL;
+	}
+	if (ops->mode != MTD_OPS_RAW) {
+		if (ops->datbuf != NULL && (ops->len % mtd->writesize) != 0) {
+			pr_err("%s: unsupported ops->len, %d\n",
+			       __func__, ops->len);
+			return -EINVAL;
+		}
+	} else {
+		if (ops->datbuf != NULL &&
+			(ops->len % (mtd->writesize + mtd->oobsize)) != 0) {
+			pr_err("%s: unsupported ops->len,"
+				" %d for MTD_OPS_RAW\n", __func__, ops->len);
+			return -EINVAL;
+		}
+	}
+
+	if (ops->mode != MTD_OPS_RAW && ops->ooblen != 0 && ops->ooboffs != 0) {
+		pr_err("%s: unsupported ops->ooboffs, %d\n",
+		       __func__, ops->ooboffs);
+		return -EINVAL;
+	}
+
+	if (ops->oobbuf && !ops->datbuf && ops->mode == MTD_OPS_AUTO_OOB)
+		start_sector = cwperpage - 1;
+
+	if (ops->oobbuf && !ops->datbuf) {
+		page_count = ops->ooblen / ((ops->mode == MTD_OPS_AUTO_OOB) ?
+			mtd->oobavail : mtd->oobsize);
+		if ((page_count == 0) && (ops->ooblen))
+			page_count = 1;
+	} else if (ops->mode != MTD_OPS_RAW)
+		page_count = ops->len / mtd->writesize;
+	else
+		page_count = ops->len / (mtd->writesize + mtd->oobsize);
+
+	if (ops->datbuf) {
+		data_dma_addr_curr = data_dma_addr =
+			msm_nand_dma_map(chip->dev, ops->datbuf, ops->len,
+				       DMA_FROM_DEVICE);
+		if (dma_mapping_error(chip->dev, data_dma_addr)) {
+			pr_err("msm_nand_read_oob_dualnandc: "
+				"failed to get dma addr for %p\n",
+				ops->datbuf);
+			return -EIO;
+		}
+	}
+	if (ops->oobbuf) {
+		memset(ops->oobbuf, 0xff, ops->ooblen);
+		oob_dma_addr_curr = oob_dma_addr =
+			msm_nand_dma_map(chip->dev, ops->oobbuf,
+				       ops->ooblen, DMA_BIDIRECTIONAL);
+		if (dma_mapping_error(chip->dev, oob_dma_addr)) {
+			pr_err("msm_nand_read_oob_dualnandc: "
+				"failed to get dma addr for %p\n",
+				ops->oobbuf);
+			err = -EIO;
+			goto err_dma_map_oobbuf_failed;
+		}
+	}
+
+	wait_event(chip->wait_queue,
+		   (dma_buffer = msm_nand_get_dma_buffer(
+			    chip, sizeof(*dma_buffer))));
+
+	oob_col = start_sector * chip->cw_size;
+	if (chip->CFG1 & CFG1_WIDE_FLASH) {
+		oob_col >>= 1;
+		cw_offset >>= 1;
+	}
+
+	err = 0;
+	while (page_count-- > 0) {
+		cmd = dma_buffer->cmd;
+
+		if (ops->mode != MTD_OPS_RAW) {
+			dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ_ECC;
+			if (start_sector == (cwperpage - 1)) {
+				dma_buffer->data.cfg0 = (chip->CFG0 &
+							~(7U << 6));
+			} else {
+				dma_buffer->data.cfg0 = (chip->CFG0 &
+				~(7U << 6))
+				| (((cwperpage >> 1)-1) << 6);
+			}
+			dma_buffer->data.cfg1 = chip->CFG1;
+			if (enable_bch_ecc)
+				dma_buffer->data.eccbchcfg = chip->ecc_bch_cfg;
+		} else {
+			dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ;
+			dma_buffer->data.cfg0 = ((chip->CFG0_RAW &
+				~(7U << 6)) | ((((cwperpage >> 1)-1) << 6)));
+			dma_buffer->data.cfg1 = chip->CFG1_RAW |
+					(chip->CFG1 & CFG1_WIDE_FLASH);
+		}
+
+		if (!interleave_enable) {
+			if (start_sector == (cwperpage - 1)) {
+				dma_buffer->data.nandc10_addr0 =
+							(page << 16) | oob_col;
+				dma_buffer->data.nc10_flash_dev_cmd_vld = 0xD;
+				dma_buffer->data.nc10_flash_dev_cmd1 =
+								0xF00F3000;
+			} else {
+				dma_buffer->data.nandc01_addr0 = page << 16;
+				/* NC10 ADDR0 points to the next code word */
+				dma_buffer->data.nandc10_addr0 = (page << 16) |
+								cw_offset;
+				dma_buffer->data.nc10_flash_dev_cmd_vld = 0x1D;
+				dma_buffer->data.nc10_flash_dev_cmd1 =
+								0xF00FE005;
+			}
+		} else {
+			dma_buffer->data.nandc01_addr0 =
+			dma_buffer->data.nandc10_addr0 =
+						(page << 16) | oob_col;
+		}
+		/* ADDR1 */
+		dma_buffer->data.nandc11_addr1 = (page >> 16) & 0xff;
+
+		dma_buffer->data.adm_mux_data_ack_req_nc01 = 0x00000A3C;
+		dma_buffer->data.adm_mux_cmd_ack_req_nc01  = 0x0000053C;
+		dma_buffer->data.adm_mux_data_ack_req_nc10 = 0x00000F28;
+		dma_buffer->data.adm_mux_cmd_ack_req_nc10  = 0x00000F14;
+		dma_buffer->data.adm_default_mux = 0x00000FC0;
+		dma_buffer->data.nc10_flash_dev_cmd_vld_default = 0x1D;
+		dma_buffer->data.nc10_flash_dev_cmd1_default = 0xF00F3000;
+
+		dma_buffer->data.ebi2_chip_select_cfg0 = 0x00000805;
+		dma_buffer->data.default_ebi2_chip_select_cfg0 = 0x00000801;
+
+		/* chipsel_0 + enable DM interface */
+		dma_buffer->data.chipsel_cs0 = (1<<4) | 4;
+		/* chipsel_1 + enable DM interface */
+		dma_buffer->data.chipsel_cs1 = (1<<4) | 5;
+
+		/* GO bit for the EXEC register */
+		dma_buffer->data.exec = 1;
+
+		BUILD_BUG_ON(16 != ARRAY_SIZE(dma_buffer->data.result));
+
+		for (n = start_sector; n < cwperpage; n++) {
+			/* flash + buffer status return words */
+			dma_buffer->data.result[n].flash_status = 0xeeeeeeee;
+			dma_buffer->data.result[n].buffer_status = 0xeeeeeeee;
+
+			if (n == start_sector) {
+				if (!interleave_enable) {
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+					&dma_buffer->
+						data.nc10_flash_dev_cmd_vld);
+					cmd->dst = NC10(MSM_NAND_DEV_CMD_VLD);
+					cmd->len = 4;
+					cmd++;
+
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+					&dma_buffer->data.nc10_flash_dev_cmd1);
+					cmd->dst = NC10(MSM_NAND_DEV_CMD1);
+					cmd->len = 4;
+					cmd++;
+
+					/* NC01, NC10 --> ADDR1 */
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+					&dma_buffer->data.nandc11_addr1);
+					cmd->dst = NC11(MSM_NAND_ADDR1);
+					cmd->len = 8;
+					cmd++;
+
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+						&dma_buffer->data.cfg0);
+					cmd->dst = NC11(MSM_NAND_DEV0_CFG0);
+					if (enable_bch_ecc)
+						cmd->len = 12;
+					else
+						cmd->len = 8;
+					cmd++;
+				} else {
+					/* enable CS0 & CS1 */
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+					&dma_buffer->
+						data.ebi2_chip_select_cfg0);
+					cmd->dst = EBI2_CHIP_SELECT_CFG0;
+					cmd->len = 4;
+					cmd++;
+
+					/* NC01, NC10 --> ADDR1 */
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+					&dma_buffer->data.nandc11_addr1);
+					cmd->dst = NC11(MSM_NAND_ADDR1);
+					cmd->len = 4;
+					cmd++;
+
+					/* Enable CS0 for NC01 */
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+					&dma_buffer->data.chipsel_cs0);
+					cmd->dst =
+					NC01(MSM_NAND_FLASH_CHIP_SELECT);
+					cmd->len = 4;
+					cmd++;
+
+					/* Enable CS1 for NC10 */
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+					&dma_buffer->data.chipsel_cs1);
+					cmd->dst =
+					NC10(MSM_NAND_FLASH_CHIP_SELECT);
+					cmd->len = 4;
+					cmd++;
+
+					/* config DEV0_CFG0 & CFG1 for CS0 */
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+					&dma_buffer->data.cfg0);
+					cmd->dst = NC01(MSM_NAND_DEV0_CFG0);
+					cmd->len = 8;
+					cmd++;
+
+					/* config DEV1_CFG0 & CFG1 for CS1 */
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+					&dma_buffer->data.cfg0);
+					cmd->dst = NC10(MSM_NAND_DEV1_CFG0);
+					cmd->len = 8;
+					cmd++;
+				}
+
+				dma_buffer->data.ecccfg = chip->ecc_buf_cfg;
+				cmd->cmd = 0;
+				cmd->src = msm_virt_to_dma(chip,
+						&dma_buffer->data.ecccfg);
+				cmd->dst = NC11(MSM_NAND_EBI2_ECC_BUF_CFG);
+				cmd->len = 4;
+				cmd++;
+
+				/* if 'only' the last code word */
+				if (n == cwperpage - 1) {
+					/* MASK CMD ACK/REQ --> NC01 (0x53C)*/
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+					&dma_buffer->
+						data.adm_mux_cmd_ack_req_nc01);
+					cmd->dst = EBI2_NAND_ADM_MUX;
+					cmd->len = 4;
+					cmd++;
+
+					/* CMD */
+					cmd->cmd = DST_CRCI_NAND_CMD;
+					cmd->src = msm_virt_to_dma(chip,
+							&dma_buffer->data.cmd);
+					cmd->dst = NC10(MSM_NAND_FLASH_CMD);
+					cmd->len = 4;
+					cmd++;
+
+					/* NC10 --> ADDR0 ( 0x0 ) */
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+					&dma_buffer->data.nandc10_addr0);
+					cmd->dst = NC10(MSM_NAND_ADDR0);
+					cmd->len = 4;
+					cmd++;
+
+					/* kick the execute reg for NC10 */
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+						&dma_buffer->data.exec);
+					cmd->dst = NC10(MSM_NAND_EXEC_CMD);
+					cmd->len = 4;
+					cmd++;
+
+					/* MASK DATA ACK/REQ --> NC01 (0xA3C)*/
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+					&dma_buffer->
+					data.adm_mux_data_ack_req_nc01);
+					cmd->dst = EBI2_NAND_ADM_MUX;
+					cmd->len = 4;
+					cmd++;
+
+					/* block on data ready from NC10, then
+					 * read the status register
+					 */
+					cmd->cmd = SRC_CRCI_NAND_DATA;
+					cmd->src = NC10(MSM_NAND_FLASH_STATUS);
+					cmd->dst = msm_virt_to_dma(chip,
+						&dma_buffer->data.result[n]);
+					/* MSM_NAND_FLASH_STATUS +
+					 * MSM_NAND_BUFFER_STATUS
+					 */
+					cmd->len = 8;
+					cmd++;
+				} else {
+					/* NC01 --> ADDR0 */
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+					&dma_buffer->data.nandc01_addr0);
+					cmd->dst = NC01(MSM_NAND_ADDR0);
+					cmd->len = 4;
+					cmd++;
+
+					/* NC10 --> ADDR1 */
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+					&dma_buffer->data.nandc10_addr0);
+					cmd->dst = NC10(MSM_NAND_ADDR0);
+					cmd->len = 4;
+					cmd++;
+
+					/* MASK CMD ACK/REQ --> NC10 (0xF14)*/
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+					&dma_buffer->
+						data.adm_mux_cmd_ack_req_nc10);
+					cmd->dst = EBI2_NAND_ADM_MUX;
+					cmd->len = 4;
+					cmd++;
+
+					/* CMD */
+					cmd->cmd = DST_CRCI_NAND_CMD;
+					cmd->src = msm_virt_to_dma(chip,
+							&dma_buffer->data.cmd);
+					cmd->dst = NC01(MSM_NAND_FLASH_CMD);
+					cmd->len = 4;
+					cmd++;
+
+					/* kick the execute register for NC01*/
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+						 &dma_buffer->data.exec);
+					cmd->dst = NC01(MSM_NAND_EXEC_CMD);
+					cmd->len = 4;
+					cmd++;
+				}
+			}
+
+			/* read data block
+			 * (only valid if status says success)
+			 */
+			if (ops->datbuf || (ops->oobbuf &&
+						 ops->mode != MTD_OPS_AUTO_OOB)) {
+				if (ops->mode != MTD_OPS_RAW)
+					sectordatasize = (n < (cwperpage - 1))
+					? 516 : (512 - ((cwperpage - 1) << 2));
+				else
+					sectordatasize = chip->cw_size;
+
+				if (n % 2 == 0) {
+					/* MASK DATA ACK/REQ --> NC10 (0xF28)*/
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+					&dma_buffer->
+					data.adm_mux_data_ack_req_nc10);
+					cmd->dst = EBI2_NAND_ADM_MUX;
+					cmd->len = 4;
+					cmd++;
+
+					/* block on data ready from NC01, then
+					 * read the status register
+					 */
+					cmd->cmd = SRC_CRCI_NAND_DATA;
+					cmd->src = NC01(MSM_NAND_FLASH_STATUS);
+					cmd->dst = msm_virt_to_dma(chip,
+						&dma_buffer->data.result[n]);
+					/* MSM_NAND_FLASH_STATUS +
+					 * MSM_NAND_BUFFER_STATUS
+					 */
+					cmd->len = 8;
+					cmd++;
+
+					/* MASK CMD ACK/REQ --> NC01 (0x53C)*/
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+					&dma_buffer->
+						data.adm_mux_cmd_ack_req_nc01);
+					cmd->dst = EBI2_NAND_ADM_MUX;
+					cmd->len = 4;
+					cmd++;
+
+					/* CMD */
+					cmd->cmd = DST_CRCI_NAND_CMD;
+					cmd->src = msm_virt_to_dma(chip,
+							&dma_buffer->data.cmd);
+					cmd->dst = NC10(MSM_NAND_FLASH_CMD);
+					cmd->len = 4;
+					cmd++;
+
+					/* kick the execute register for NC10 */
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+						&dma_buffer->data.exec);
+					cmd->dst = NC10(MSM_NAND_EXEC_CMD);
+					cmd->len = 4;
+					cmd++;
+
+					/* Read only when there is data
+					 * buffer
+					 */
+					if (ops->datbuf) {
+						cmd->cmd = 0;
+						cmd->src =
+						NC01(MSM_NAND_FLASH_BUFFER);
+						cmd->dst = data_dma_addr_curr;
+						data_dma_addr_curr +=
+						sectordatasize;
+						cmd->len = sectordatasize;
+						cmd++;
+					}
+				} else {
+					/* MASK DATA ACK/REQ -->
+					 * NC01 (0xA3C)
+					 */
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+					&dma_buffer->
+					data.adm_mux_data_ack_req_nc01);
+					cmd->dst = EBI2_NAND_ADM_MUX;
+					cmd->len = 4;
+					cmd++;
+
+					/* block on data ready from NC10
+					 * then read the status register
+					 */
+					cmd->cmd = SRC_CRCI_NAND_DATA;
+					cmd->src =
+					NC10(MSM_NAND_FLASH_STATUS);
+					cmd->dst = msm_virt_to_dma(chip,
+					   &dma_buffer->data.result[n]);
+					/* MSM_NAND_FLASH_STATUS +
+					 * MSM_NAND_BUFFER_STATUS
+					 */
+					cmd->len = 8;
+					cmd++;
+					if (n != cwperpage - 1) {
+						/* MASK CMD ACK/REQ -->
+						 * NC10 (0xF14)
+						 */
+						cmd->cmd = 0;
+						cmd->src =
+						msm_virt_to_dma(chip,
+						&dma_buffer->
+						data.adm_mux_cmd_ack_req_nc10);
+						cmd->dst = EBI2_NAND_ADM_MUX;
+						cmd->len = 4;
+						cmd++;
+
+						/* CMD */
+						cmd->cmd = DST_CRCI_NAND_CMD;
+						cmd->src = msm_virt_to_dma(chip,
+							&dma_buffer->data.cmd);
+						cmd->dst =
+						NC01(MSM_NAND_FLASH_CMD);
+						cmd->len = 4;
+						cmd++;
+
+						/* EXEC */
+						cmd->cmd = 0;
+						cmd->src = msm_virt_to_dma(chip,
+							&dma_buffer->data.exec);
+						cmd->dst =
+						NC01(MSM_NAND_EXEC_CMD);
+						cmd->len = 4;
+						cmd++;
+					}
+
+					/* Read only when there is data
+					 * buffer
+					 */
+					if (ops->datbuf) {
+						cmd->cmd = 0;
+						cmd->src =
+						NC10(MSM_NAND_FLASH_BUFFER);
+						cmd->dst = data_dma_addr_curr;
+						data_dma_addr_curr +=
+						sectordatasize;
+						cmd->len = sectordatasize;
+						cmd++;
+					}
+				}
+			}
+
+			if (ops->oobbuf && (n == (cwperpage - 1)
+			     || ops->mode != MTD_OPS_AUTO_OOB)) {
+				cmd->cmd = 0;
+				if (n == (cwperpage - 1)) {
+					/* Use NC10 for reading the
+					 * last codeword!!!
+					 */
+					cmd->src = NC10(MSM_NAND_FLASH_BUFFER) +
+						(512 - ((cwperpage - 1) << 2));
+					sectoroobsize = (cwperpage << 2);
+					if (ops->mode != MTD_OPS_AUTO_OOB)
+						sectoroobsize +=
+							chip->ecc_parity_bytes;
+				} else {
+					if (n % 2 == 0)
+						cmd->src =
+						NC01(MSM_NAND_FLASH_BUFFER)
+						+ 516;
+					else
+						cmd->src =
+						NC10(MSM_NAND_FLASH_BUFFER)
+						+ 516;
+					sectoroobsize = chip->ecc_parity_bytes;
+				}
+				cmd->dst = oob_dma_addr_curr;
+				if (sectoroobsize < oob_len)
+					cmd->len = sectoroobsize;
+				else
+					cmd->len = oob_len;
+				oob_dma_addr_curr += cmd->len;
+				oob_len -= cmd->len;
+				if (cmd->len > 0)
+					cmd++;
+			}
+		}
+		/* ADM --> Default mux state (0xFC0) */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip,
+			&dma_buffer->data.adm_default_mux);
+		cmd->dst = EBI2_NAND_ADM_MUX;
+		cmd->len = 4;
+		cmd++;
+
+		if (!interleave_enable) {
+			cmd->cmd = 0;
+			cmd->src = msm_virt_to_dma(chip,
+			&dma_buffer->data.nc10_flash_dev_cmd_vld_default);
+			cmd->dst = NC10(MSM_NAND_DEV_CMD_VLD);
+			cmd->len = 4;
+			cmd++;
+
+			cmd->cmd = 0;
+			cmd->src = msm_virt_to_dma(chip,
+			&dma_buffer->data.nc10_flash_dev_cmd1_default);
+			cmd->dst = NC10(MSM_NAND_DEV_CMD1);
+			cmd->len = 4;
+			cmd++;
+		} else {
+			/* disable CS1 */
+			cmd->cmd = 0;
+			cmd->src = msm_virt_to_dma(chip,
+			&dma_buffer->data.default_ebi2_chip_select_cfg0);
+			cmd->dst = EBI2_CHIP_SELECT_CFG0;
+			cmd->len = 4;
+			cmd++;
+		}
+
+		BUILD_BUG_ON(16 * 6 + 20 != ARRAY_SIZE(dma_buffer->cmd));
+		BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
+		dma_buffer->cmd[0].cmd |= CMD_OCB;
+		cmd[-1].cmd |= CMD_OCU | CMD_LC;
+
+		dma_buffer->cmdptr =
+			(msm_virt_to_dma(chip, dma_buffer->cmd) >> 3)
+			| CMD_PTR_LP;
+
+		mb();
+		msm_dmov_exec_cmd(chip->dma_channel,
+			DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip,
+			&dma_buffer->cmdptr)));
+		mb();
+
+		/* if any of the writes failed (0x10), or there
+		 * was a protection violation (0x100), we lose
+		 */
+		pageerr = rawerr = 0;
+		for (n = start_sector; n < cwperpage; n++) {
+			if (dma_buffer->data.result[n].flash_status & 0x110) {
+				rawerr = -EIO;
+				break;
+			}
+		}
+		if (rawerr) {
+			if (ops->datbuf && ops->mode != MTD_OPS_RAW) {
+				uint8_t *datbuf = ops->datbuf +
+					pages_read * mtd->writesize;
+
+				dma_sync_single_for_cpu(chip->dev,
+					data_dma_addr_curr-mtd->writesize,
+					mtd->writesize, DMA_BIDIRECTIONAL);
+
+				for (n = 0; n < mtd->writesize; n++) {
+					/* empty blocks read 0x54 at
+					 * these offsets
+					 */
+					if ((n % 516 == 3 || n % 516 == 175)
+							&& datbuf[n] == 0x54)
+						datbuf[n] = 0xff;
+					if (datbuf[n] != 0xff) {
+						pageerr = rawerr;
+						break;
+					}
+				}
+
+				dma_sync_single_for_device(chip->dev,
+					data_dma_addr_curr-mtd->writesize,
+					mtd->writesize, DMA_BIDIRECTIONAL);
+
+			}
+			if (ops->oobbuf) {
+				dma_sync_single_for_cpu(chip->dev,
+				oob_dma_addr_curr - (ops->ooblen - oob_len),
+				ops->ooblen - oob_len, DMA_BIDIRECTIONAL);
+
+				for (n = 0; n < ops->ooblen; n++) {
+					if (ops->oobbuf[n] != 0xff) {
+						pageerr = rawerr;
+						break;
+					}
+				}
+
+				dma_sync_single_for_device(chip->dev,
+				oob_dma_addr_curr - (ops->ooblen - oob_len),
+				ops->ooblen - oob_len, DMA_BIDIRECTIONAL);
+			}
+		}
+		if (pageerr) {
+			for (n = start_sector; n < cwperpage; n++) {
+				if (dma_buffer->data.result[n].buffer_status
+					& MSM_NAND_BUF_STAT_UNCRCTBL_ERR) {
+					/* not thread safe */
+					mtd->ecc_stats.failed++;
+					pageerr = -EBADMSG;
+					break;
+				}
+			}
+		}
+		if (!rawerr) { /* check for corretable errors */
+			for (n = start_sector; n < cwperpage; n++) {
+				ecc_errors = dma_buffer->data.
+					result[n].buffer_status
+					& MSM_NAND_BUF_STAT_NUM_ERR_MASK;
+				if (ecc_errors) {
+					total_ecc_errors += ecc_errors;
+					/* not thread safe */
+					mtd->ecc_stats.corrected += ecc_errors;
+					if (ecc_errors > 1)
+						pageerr = -EUCLEAN;
+				}
+			}
+		}
+		if (pageerr && (pageerr != -EUCLEAN || err == 0))
+			err = pageerr;
+
+#if VERBOSE
+		if (rawerr && !pageerr) {
+			pr_err("msm_nand_read_oob_dualnandc "
+				"%llx %x %x empty page\n",
+			       (loff_t)page * mtd->writesize, ops->len,
+			       ops->ooblen);
+		} else {
+			for (n = start_sector; n < cwperpage; n++) {
+				if (n%2) {
+					pr_info("NC10: flash_status[%d] = %x, "
+					 "buffr_status[%d] = %x\n",
+					n, dma_buffer->
+						data.result[n].flash_status,
+					n, dma_buffer->
+						data.result[n].buffer_status);
+				} else {
+					pr_info("NC01: flash_status[%d] = %x, "
+					 "buffr_status[%d] = %x\n",
+					n, dma_buffer->
+						data.result[n].flash_status,
+					n, dma_buffer->
+						data.result[n].buffer_status);
+				}
+			}
+		}
+#endif
+		if (err && err != -EUCLEAN && err != -EBADMSG)
+			break;
+		pages_read++;
+		page++;
+	}
+
+	msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+
+	if (ops->oobbuf) {
+		dma_unmap_page(chip->dev, oob_dma_addr,
+				 ops->ooblen, DMA_FROM_DEVICE);
+	}
+err_dma_map_oobbuf_failed:
+	if (ops->datbuf) {
+		dma_unmap_page(chip->dev, data_dma_addr,
+				 ops->len, DMA_BIDIRECTIONAL);
+	}
+
+	if (ops->mode != MTD_OPS_RAW)
+		ops->retlen = mtd->writesize * pages_read;
+	else
+		ops->retlen = (mtd->writesize +  mtd->oobsize) *
+							pages_read;
+	ops->oobretlen = ops->ooblen - oob_len;
+	if (err)
+		pr_err("msm_nand_read_oob_dualnandc "
+			"%llx %x %x failed %d, corrected %d\n",
+			from, ops->datbuf ? ops->len : 0, ops->ooblen, err,
+			total_ecc_errors);
+#if VERBOSE
+	pr_info("\n%s: ret %d, retlen %d oobretlen %d\n",
+			__func__, err, ops->retlen, ops->oobretlen);
+
+	pr_info("==================================================="
+			"==========\n");
+#endif
+	return err;
+}
+
+static int
+msm_nand_read(struct mtd_info *mtd, loff_t from, size_t len,
+	      size_t *retlen, u_char *buf)
+{
+	int ret;
+	struct mtd_oob_ops ops;
+
+	/* printk("msm_nand_read %llx %x\n", from, len); */
+
+	ops.mode = MTD_OPS_PLACE_OOB;
+	ops.len = len;
+	ops.retlen = 0;
+	ops.ooblen = 0;
+	ops.datbuf = buf;
+	ops.oobbuf = NULL;
+	if (!dual_nand_ctlr_present)
+		ret =  msm_nand_read_oob(mtd, from, &ops);
+	else
+		ret = msm_nand_read_oob_dualnandc(mtd, from, &ops);
+	*retlen = ops.retlen;
+	return ret;
+}
+
+static int
+msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)
+{
+	struct msm_nand_chip *chip = mtd->priv;
+	struct {
+		dmov_s cmd[8 * 7 + 2];
+		unsigned cmdptr;
+		struct {
+			uint32_t cmd;
+			uint32_t addr0;
+			uint32_t addr1;
+			uint32_t chipsel;
+			uint32_t cfg0;
+			uint32_t cfg1;
+			uint32_t eccbchcfg;
+			uint32_t exec;
+			uint32_t ecccfg;
+			uint32_t clrfstatus;
+			uint32_t clrrstatus;
+			uint32_t flash_status[8];
+		} data;
+	} *dma_buffer;
+	dmov_s *cmd;
+	unsigned n;
+	unsigned page = 0;
+	uint32_t oob_len;
+	uint32_t sectordatawritesize;
+	int err = 0;
+	dma_addr_t data_dma_addr = 0;
+	dma_addr_t oob_dma_addr = 0;
+	dma_addr_t data_dma_addr_curr = 0;
+	dma_addr_t oob_dma_addr_curr = 0;
+	unsigned page_count;
+	unsigned pages_written = 0;
+	unsigned cwperpage;
+#if VERBOSE
+	pr_info("================================================="
+			"================\n");
+	pr_info("%s:\nto 0x%llx mode %d\ndatbuf 0x%p datlen 0x%x"
+			"\noobbuf 0x%p ooblen 0x%x\n",
+			__func__, to, ops->mode, ops->datbuf, ops->len,
+			ops->oobbuf, ops->ooblen);
+#endif
+
+	if (mtd->writesize == 2048)
+		page = to >> 11;
+
+	if (mtd->writesize == 4096)
+		page = to >> 12;
+
+	oob_len = ops->ooblen;
+	cwperpage = (mtd->writesize >> 9);
+
+	if (to & (mtd->writesize - 1)) {
+		pr_err("%s: unsupported to, 0x%llx\n", __func__, to);
+		return -EINVAL;
+	}
+
+	if (ops->mode != MTD_OPS_RAW) {
+		if (ops->ooblen != 0 && ops->mode != MTD_OPS_AUTO_OOB) {
+			pr_err("%s: unsupported ops->mode,%d\n",
+					 __func__, ops->mode);
+			return -EINVAL;
+		}
+		if ((ops->len % mtd->writesize) != 0) {
+			pr_err("%s: unsupported ops->len, %d\n",
+					__func__, ops->len);
+			return -EINVAL;
+		}
+	} else {
+		if ((ops->len % (mtd->writesize + mtd->oobsize)) != 0) {
+			pr_err("%s: unsupported ops->len, "
+				"%d for MTD_OPS_RAW mode\n",
+				 __func__, ops->len);
+			return -EINVAL;
+		}
+	}
+
+	if (ops->datbuf == NULL) {
+		pr_err("%s: unsupported ops->datbuf == NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (ops->mode != MTD_OPS_RAW && ops->ooblen != 0 && ops->ooboffs != 0) {
+		pr_err("%s: unsupported ops->ooboffs, %d\n",
+		       __func__, ops->ooboffs);
+		return -EINVAL;
+	}
+
+	if (ops->datbuf) {
+		data_dma_addr_curr = data_dma_addr =
+			msm_nand_dma_map(chip->dev, ops->datbuf,
+				       ops->len, DMA_TO_DEVICE);
+		if (dma_mapping_error(chip->dev, data_dma_addr)) {
+			pr_err("msm_nand_write_oob: failed to get dma addr "
+			       "for %p\n", ops->datbuf);
+			return -EIO;
+		}
+	}
+	if (ops->oobbuf) {
+		oob_dma_addr_curr = oob_dma_addr =
+			msm_nand_dma_map(chip->dev, ops->oobbuf,
+				       ops->ooblen, DMA_TO_DEVICE);
+		if (dma_mapping_error(chip->dev, oob_dma_addr)) {
+			pr_err("msm_nand_write_oob: failed to get dma addr "
+			       "for %p\n", ops->oobbuf);
+			err = -EIO;
+			goto err_dma_map_oobbuf_failed;
+		}
+	}
+	if (ops->mode != MTD_OPS_RAW)
+		page_count = ops->len / mtd->writesize;
+	else
+		page_count = ops->len / (mtd->writesize + mtd->oobsize);
+
+	wait_event(chip->wait_queue, (dma_buffer =
+			msm_nand_get_dma_buffer(chip, sizeof(*dma_buffer))));
+
+	while (page_count-- > 0) {
+		cmd = dma_buffer->cmd;
+
+		if (ops->mode != MTD_OPS_RAW) {
+			dma_buffer->data.cfg0 = chip->CFG0;
+			dma_buffer->data.cfg1 = chip->CFG1;
+			if (enable_bch_ecc)
+				dma_buffer->data.eccbchcfg = chip->ecc_bch_cfg;
+		} else {
+			dma_buffer->data.cfg0 = (chip->CFG0_RAW &
+					~(7U << 6)) | ((cwperpage-1) << 6);
+			dma_buffer->data.cfg1 = chip->CFG1_RAW |
+						(chip->CFG1 & CFG1_WIDE_FLASH);
+		}
+
+		/* CMD / ADDR0 / ADDR1 / CHIPSEL program values */
+		dma_buffer->data.cmd = MSM_NAND_CMD_PRG_PAGE;
+		dma_buffer->data.addr0 = page << 16;
+		dma_buffer->data.addr1 = (page >> 16) & 0xff;
+		/* chipsel_0 + enable DM interface */
+		dma_buffer->data.chipsel = 0 | 4;
+
+
+		/* GO bit for the EXEC register */
+		dma_buffer->data.exec = 1;
+		dma_buffer->data.clrfstatus = 0x00000020;
+		dma_buffer->data.clrrstatus = 0x000000C0;
+
+		BUILD_BUG_ON(8 != ARRAY_SIZE(dma_buffer->data.flash_status));
+
+		for (n = 0; n < cwperpage ; n++) {
+			/* status return words */
+			dma_buffer->data.flash_status[n] = 0xeeeeeeee;
+			/* block on cmd ready, then
+			 * write CMD / ADDR0 / ADDR1 / CHIPSEL regs in a burst
+			 */
+			cmd->cmd = DST_CRCI_NAND_CMD;
+			cmd->src =
+				msm_virt_to_dma(chip, &dma_buffer->data.cmd);
+			cmd->dst = MSM_NAND_FLASH_CMD;
+			if (n == 0)
+				cmd->len = 16;
+			else
+				cmd->len = 4;
+			cmd++;
+
+			if (n == 0) {
+				cmd->cmd = 0;
+				cmd->src = msm_virt_to_dma(chip,
+							&dma_buffer->data.cfg0);
+				cmd->dst = MSM_NAND_DEV0_CFG0;
+				if (enable_bch_ecc)
+					cmd->len = 12;
+				else
+					cmd->len = 8;
+				cmd++;
+
+				dma_buffer->data.ecccfg = chip->ecc_buf_cfg;
+				cmd->cmd = 0;
+				cmd->src = msm_virt_to_dma(chip,
+						 &dma_buffer->data.ecccfg);
+				cmd->dst = MSM_NAND_EBI2_ECC_BUF_CFG;
+				cmd->len = 4;
+				cmd++;
+			}
+
+			/* write data block */
+			if (ops->mode != MTD_OPS_RAW)
+				sectordatawritesize = (n < (cwperpage - 1)) ?
+					516 : (512 - ((cwperpage - 1) << 2));
+			else
+				sectordatawritesize = chip->cw_size;
+
+			cmd->cmd = 0;
+			cmd->src = data_dma_addr_curr;
+			data_dma_addr_curr += sectordatawritesize;
+			cmd->dst = MSM_NAND_FLASH_BUFFER;
+			cmd->len = sectordatawritesize;
+			cmd++;
+
+			if (ops->oobbuf) {
+				if (n == (cwperpage - 1)) {
+					cmd->cmd = 0;
+					cmd->src = oob_dma_addr_curr;
+					cmd->dst = MSM_NAND_FLASH_BUFFER +
+						(512 - ((cwperpage - 1) << 2));
+					if ((cwperpage << 2) < oob_len)
+						cmd->len = (cwperpage << 2);
+					else
+						cmd->len = oob_len;
+					oob_dma_addr_curr += cmd->len;
+					oob_len -= cmd->len;
+					if (cmd->len > 0)
+						cmd++;
+				}
+				if (ops->mode != MTD_OPS_AUTO_OOB) {
+					/* skip ecc bytes in oobbuf */
+					if (oob_len < chip->ecc_parity_bytes) {
+						oob_dma_addr_curr +=
+							chip->ecc_parity_bytes;
+						oob_len -=
+							chip->ecc_parity_bytes;
+					} else {
+						oob_dma_addr_curr += oob_len;
+						oob_len = 0;
+					}
+				}
+			}
+
+			/* kick the execute register */
+			cmd->cmd = 0;
+			cmd->src =
+				msm_virt_to_dma(chip, &dma_buffer->data.exec);
+			cmd->dst = MSM_NAND_EXEC_CMD;
+			cmd->len = 4;
+			cmd++;
+
+			/* block on data ready, then
+			 * read the status register
+			 */
+			cmd->cmd = SRC_CRCI_NAND_DATA;
+			cmd->src = MSM_NAND_FLASH_STATUS;
+			cmd->dst = msm_virt_to_dma(chip,
+					     &dma_buffer->data.flash_status[n]);
+			cmd->len = 4;
+			cmd++;
+
+			cmd->cmd = 0;
+			cmd->src = msm_virt_to_dma(chip,
+						&dma_buffer->data.clrfstatus);
+			cmd->dst = MSM_NAND_FLASH_STATUS;
+			cmd->len = 4;
+			cmd++;
+
+			cmd->cmd = 0;
+			cmd->src = msm_virt_to_dma(chip,
+						&dma_buffer->data.clrrstatus);
+			cmd->dst = MSM_NAND_READ_STATUS;
+			cmd->len = 4;
+			cmd++;
+
+		}
+
+		dma_buffer->cmd[0].cmd |= CMD_OCB;
+		cmd[-1].cmd |= CMD_OCU | CMD_LC;
+		BUILD_BUG_ON(8 * 7 + 2 != ARRAY_SIZE(dma_buffer->cmd));
+		BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
+		dma_buffer->cmdptr =
+			(msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) |
+			CMD_PTR_LP;
+
+		mb();
+		msm_dmov_exec_cmd(chip->dma_channel,
+			DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(
+				msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
+		mb();
+
+		/* if any of the writes failed (0x10), or there was a
+		 * protection violation (0x100), or the program success
+		 * bit (0x80) is unset, we lose
+		 */
+		err = 0;
+		for (n = 0; n < cwperpage; n++) {
+			if (dma_buffer->data.flash_status[n] & 0x110) {
+				err = -EIO;
+				break;
+			}
+			if (!(dma_buffer->data.flash_status[n] & 0x80)) {
+				err = -EIO;
+				break;
+			}
+		}
+
+#if VERBOSE
+		for (n = 0; n < cwperpage; n++)
+			pr_info("write pg %d: flash_status[%d] = %x\n", page,
+				n, dma_buffer->data.flash_status[n]);
+
+#endif
+		if (err)
+			break;
+		pages_written++;
+		page++;
+	}
+	if (ops->mode != MTD_OPS_RAW)
+		ops->retlen = mtd->writesize * pages_written;
+	else
+		ops->retlen = (mtd->writesize + mtd->oobsize) * pages_written;
+
+	ops->oobretlen = ops->ooblen - oob_len;
+
+	msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+
+	if (ops->oobbuf)
+		dma_unmap_page(chip->dev, oob_dma_addr,
+				 ops->ooblen, DMA_TO_DEVICE);
+err_dma_map_oobbuf_failed:
+	if (ops->datbuf)
+		dma_unmap_page(chip->dev, data_dma_addr, ops->len,
+				DMA_TO_DEVICE);
+	if (err)
+		pr_err("msm_nand_write_oob %llx %x %x failed %d\n",
+		       to, ops->len, ops->ooblen, err);
+
+#if VERBOSE
+		pr_info("\n%s: ret %d, retlen %d oobretlen %d\n",
+				__func__, err, ops->retlen, ops->oobretlen);
+
+		pr_info("==================================================="
+				"==============\n");
+#endif
+	return err;
+}
+
+static int
+msm_nand_write_oob_dualnandc(struct mtd_info *mtd, loff_t to,
+				struct mtd_oob_ops *ops)
+{
+	struct msm_nand_chip *chip = mtd->priv;
+	struct {
+		dmov_s cmd[16 * 6 + 18];
+		unsigned cmdptr;
+		struct {
+			uint32_t cmd;
+			uint32_t nandc01_addr0;
+			uint32_t nandc10_addr0;
+			uint32_t nandc11_addr1;
+			uint32_t chipsel_cs0;
+			uint32_t chipsel_cs1;
+			uint32_t cfg0;
+			uint32_t cfg1;
+			uint32_t eccbchcfg;
+			uint32_t exec;
+			uint32_t ecccfg;
+			uint32_t cfg0_nc01;
+			uint32_t ebi2_chip_select_cfg0;
+			uint32_t adm_mux_data_ack_req_nc01;
+			uint32_t adm_mux_cmd_ack_req_nc01;
+			uint32_t adm_mux_data_ack_req_nc10;
+			uint32_t adm_mux_cmd_ack_req_nc10;
+			uint32_t adm_default_mux;
+			uint32_t default_ebi2_chip_select_cfg0;
+			uint32_t nc01_flash_dev_cmd_vld;
+			uint32_t nc10_flash_dev_cmd0;
+			uint32_t nc01_flash_dev_cmd_vld_default;
+			uint32_t nc10_flash_dev_cmd0_default;
+			uint32_t flash_status[16];
+			uint32_t clrfstatus;
+			uint32_t clrrstatus;
+		} data;
+	} *dma_buffer;
+	dmov_s *cmd;
+	unsigned n;
+	unsigned page = 0;
+	uint32_t oob_len;
+	uint32_t sectordatawritesize;
+	int err = 0;
+	dma_addr_t data_dma_addr = 0;
+	dma_addr_t oob_dma_addr = 0;
+	dma_addr_t data_dma_addr_curr = 0;
+	dma_addr_t oob_dma_addr_curr = 0;
+	unsigned page_count;
+	unsigned pages_written = 0;
+	unsigned cwperpage;
+	unsigned cw_offset = chip->cw_size;
+#if VERBOSE
+		pr_info("================================================="
+				"============\n");
+		pr_info("%s:\nto 0x%llx mode %d\ndatbuf 0x%p datlen 0x%x"
+				"\noobbuf 0x%p ooblen 0x%x\n\n",
+				__func__, to, ops->mode, ops->datbuf, ops->len,
+				ops->oobbuf, ops->ooblen);
+#endif
+
+	if (mtd->writesize == 2048)
+		page = to >> 11;
+
+	if (mtd->writesize == 4096)
+		page = to >> 12;
+
+	if (interleave_enable)
+		page = (to >> 1) >> 12;
+
+	oob_len = ops->ooblen;
+	cwperpage = (mtd->writesize >> 9);
+
+	if (to & (mtd->writesize - 1)) {
+		pr_err("%s: unsupported to, 0x%llx\n", __func__, to);
+		return -EINVAL;
+	}
+
+	if (ops->mode != MTD_OPS_RAW) {
+		if (ops->ooblen != 0 && ops->mode != MTD_OPS_AUTO_OOB) {
+			pr_err("%s: unsupported ops->mode,%d\n",
+					 __func__, ops->mode);
+			return -EINVAL;
+		}
+		if ((ops->len % mtd->writesize) != 0) {
+			pr_err("%s: unsupported ops->len, %d\n",
+					__func__, ops->len);
+			return -EINVAL;
+		}
+	} else {
+		if ((ops->len % (mtd->writesize + mtd->oobsize)) != 0) {
+			pr_err("%s: unsupported ops->len, "
+				"%d for MTD_OPS_RAW mode\n",
+				 __func__, ops->len);
+			return -EINVAL;
+		}
+	}
+
+	if (ops->datbuf == NULL) {
+		pr_err("%s: unsupported ops->datbuf == NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	if (ops->mode != MTD_OPS_RAW && ops->ooblen != 0 && ops->ooboffs != 0) {
+		pr_err("%s: unsupported ops->ooboffs, %d\n",
+		       __func__, ops->ooboffs);
+		return -EINVAL;
+	}
+
+	if (ops->datbuf) {
+		data_dma_addr_curr = data_dma_addr =
+			msm_nand_dma_map(chip->dev, ops->datbuf,
+				       ops->len, DMA_TO_DEVICE);
+		if (dma_mapping_error(chip->dev, data_dma_addr)) {
+			pr_err("msm_nand_write_oob_dualnandc:"
+				"failed to get dma addr "
+			       "for %p\n", ops->datbuf);
+			return -EIO;
+		}
+	}
+	if (ops->oobbuf) {
+		oob_dma_addr_curr = oob_dma_addr =
+			msm_nand_dma_map(chip->dev, ops->oobbuf,
+				       ops->ooblen, DMA_TO_DEVICE);
+		if (dma_mapping_error(chip->dev, oob_dma_addr)) {
+			pr_err("msm_nand_write_oob_dualnandc:"
+				"failed to get dma addr "
+			       "for %p\n", ops->oobbuf);
+			err = -EIO;
+			goto err_dma_map_oobbuf_failed;
+		}
+	}
+	if (ops->mode != MTD_OPS_RAW)
+		page_count = ops->len / mtd->writesize;
+	else
+		page_count = ops->len / (mtd->writesize + mtd->oobsize);
+
+	wait_event(chip->wait_queue, (dma_buffer =
+			msm_nand_get_dma_buffer(chip, sizeof(*dma_buffer))));
+
+	if (chip->CFG1 & CFG1_WIDE_FLASH)
+		cw_offset >>= 1;
+
+	dma_buffer->data.ebi2_chip_select_cfg0 = 0x00000805;
+	dma_buffer->data.adm_mux_data_ack_req_nc01 = 0x00000A3C;
+	dma_buffer->data.adm_mux_cmd_ack_req_nc01  = 0x0000053C;
+	dma_buffer->data.adm_mux_data_ack_req_nc10 = 0x00000F28;
+	dma_buffer->data.adm_mux_cmd_ack_req_nc10  = 0x00000F14;
+	dma_buffer->data.adm_default_mux = 0x00000FC0;
+	dma_buffer->data.default_ebi2_chip_select_cfg0 = 0x00000801;
+	dma_buffer->data.nc01_flash_dev_cmd_vld = 0x9;
+	dma_buffer->data.nc10_flash_dev_cmd0 = 0x1085D060;
+	dma_buffer->data.nc01_flash_dev_cmd_vld_default = 0x1D;
+	dma_buffer->data.nc10_flash_dev_cmd0_default = 0x1080D060;
+	dma_buffer->data.clrfstatus = 0x00000020;
+	dma_buffer->data.clrrstatus = 0x000000C0;
+
+	while (page_count-- > 0) {
+		cmd = dma_buffer->cmd;
+
+		if (ops->mode != MTD_OPS_RAW) {
+			dma_buffer->data.cfg0 = ((chip->CFG0 & ~(7U << 6))
+				& ~(1 << 4)) | ((((cwperpage >> 1)-1)) << 6);
+			dma_buffer->data.cfg1 = chip->CFG1;
+			if (enable_bch_ecc)
+				dma_buffer->data.eccbchcfg = chip->ecc_bch_cfg;
+		} else {
+			dma_buffer->data.cfg0 = ((chip->CFG0_RAW &
+			~(7U << 6)) & ~(1 << 4)) | (((cwperpage >> 1)-1) << 6);
+			dma_buffer->data.cfg1 = chip->CFG1_RAW |
+					(chip->CFG1 & CFG1_WIDE_FLASH);
+		}
+
+		/* Disables the automatic issuing of the read
+		 * status command for first NAND controller.
+		 */
+		if (!interleave_enable)
+			dma_buffer->data.cfg0_nc01 = dma_buffer->data.cfg0
+							| (1 << 4);
+		else
+			dma_buffer->data.cfg0 |= (1 << 4);
+
+		dma_buffer->data.cmd = MSM_NAND_CMD_PRG_PAGE;
+		dma_buffer->data.chipsel_cs0 = (1<<4) | 4;
+		dma_buffer->data.chipsel_cs1 = (1<<4) | 5;
+
+		/* GO bit for the EXEC register */
+		dma_buffer->data.exec = 1;
+
+		if (!interleave_enable) {
+			dma_buffer->data.nandc01_addr0 = (page << 16) | 0x0;
+			/* NC10 ADDR0 points to the next code word */
+			dma_buffer->data.nandc10_addr0 =
+					(page << 16) | cw_offset;
+		} else {
+			dma_buffer->data.nandc01_addr0 =
+			dma_buffer->data.nandc10_addr0 = (page << 16) | 0x0;
+		}
+		/* ADDR1 */
+		dma_buffer->data.nandc11_addr1 = (page >> 16) & 0xff;
+
+		BUILD_BUG_ON(16 != ARRAY_SIZE(dma_buffer->data.flash_status));
+
+		for (n = 0; n < cwperpage; n++) {
+			/* status return words */
+			dma_buffer->data.flash_status[n] = 0xeeeeeeee;
+
+			if (n == 0) {
+				if (!interleave_enable) {
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+					&dma_buffer->
+					data.nc01_flash_dev_cmd_vld);
+					cmd->dst = NC01(MSM_NAND_DEV_CMD_VLD);
+					cmd->len = 4;
+					cmd++;
+
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+					&dma_buffer->data.nc10_flash_dev_cmd0);
+					cmd->dst = NC10(MSM_NAND_DEV_CMD0);
+					cmd->len = 4;
+					cmd++;
+
+					/* common settings for both NC01 & NC10
+					 * NC01, NC10 --> ADDR1 / CHIPSEL
+					 */
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+					&dma_buffer->data.nandc11_addr1);
+					cmd->dst = NC11(MSM_NAND_ADDR1);
+					cmd->len = 8;
+					cmd++;
+
+					/* Disables the automatic issue of the
+					 * read status command after the write
+					 * operation.
+					 */
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+						&dma_buffer->data.cfg0_nc01);
+					cmd->dst = NC01(MSM_NAND_DEV0_CFG0);
+					cmd->len = 4;
+					cmd++;
+
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+						&dma_buffer->data.cfg0);
+					cmd->dst = NC10(MSM_NAND_DEV0_CFG0);
+					cmd->len = 4;
+					cmd++;
+
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+						&dma_buffer->data.cfg1);
+					cmd->dst = NC11(MSM_NAND_DEV0_CFG1);
+					if (enable_bch_ecc)
+						cmd->len = 8;
+					else
+						cmd->len = 4;
+					cmd++;
+				} else {
+					/* enable CS1 */
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+					&dma_buffer->
+					data.ebi2_chip_select_cfg0);
+					cmd->dst = EBI2_CHIP_SELECT_CFG0;
+					cmd->len = 4;
+					cmd++;
+
+					/* NC11 --> ADDR1 */
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+					&dma_buffer->data.nandc11_addr1);
+					cmd->dst = NC11(MSM_NAND_ADDR1);
+					cmd->len = 4;
+					cmd++;
+
+					/* Enable CS0 for NC01 */
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+					&dma_buffer->data.chipsel_cs0);
+					cmd->dst =
+					NC01(MSM_NAND_FLASH_CHIP_SELECT);
+					cmd->len = 4;
+					cmd++;
+
+					/* Enable CS1 for NC10 */
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+					&dma_buffer->data.chipsel_cs1);
+					cmd->dst =
+					NC10(MSM_NAND_FLASH_CHIP_SELECT);
+					cmd->len = 4;
+					cmd++;
+
+					/* config DEV0_CFG0 & CFG1 for CS0 */
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+						&dma_buffer->data.cfg0);
+					cmd->dst = NC01(MSM_NAND_DEV0_CFG0);
+					cmd->len = 8;
+					cmd++;
+
+					/* config DEV1_CFG0 & CFG1 for CS1 */
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+					&dma_buffer->data.cfg0);
+					cmd->dst = NC10(MSM_NAND_DEV1_CFG0);
+					cmd->len = 8;
+					cmd++;
+				}
+
+				dma_buffer->data.ecccfg = chip->ecc_buf_cfg;
+				cmd->cmd = 0;
+				cmd->src = msm_virt_to_dma(chip,
+						&dma_buffer->data.ecccfg);
+				cmd->dst = NC11(MSM_NAND_EBI2_ECC_BUF_CFG);
+				cmd->len = 4;
+				cmd++;
+
+				/* NC01 --> ADDR0 */
+				cmd->cmd = 0;
+				cmd->src = msm_virt_to_dma(chip,
+					&dma_buffer->data.nandc01_addr0);
+				cmd->dst = NC01(MSM_NAND_ADDR0);
+				cmd->len = 4;
+				cmd++;
+
+				/* NC10 --> ADDR0 */
+				cmd->cmd = 0;
+				cmd->src = msm_virt_to_dma(chip,
+				&dma_buffer->data.nandc10_addr0);
+				cmd->dst = NC10(MSM_NAND_ADDR0);
+				cmd->len = 4;
+				cmd++;
+			}
+
+			if (n % 2 == 0) {
+				/* MASK CMD ACK/REQ --> NC10 (0xF14)*/
+				cmd->cmd = 0;
+				cmd->src = msm_virt_to_dma(chip,
+				&dma_buffer->data.adm_mux_cmd_ack_req_nc10);
+				cmd->dst = EBI2_NAND_ADM_MUX;
+				cmd->len = 4;
+				cmd++;
+
+				/* CMD */
+				cmd->cmd = DST_CRCI_NAND_CMD;
+				cmd->src = msm_virt_to_dma(chip,
+						&dma_buffer->data.cmd);
+				cmd->dst = NC01(MSM_NAND_FLASH_CMD);
+				cmd->len = 4;
+				cmd++;
+			} else {
+				/* MASK CMD ACK/REQ --> NC01 (0x53C)*/
+				cmd->cmd = 0;
+				cmd->src = msm_virt_to_dma(chip,
+				&dma_buffer->data.adm_mux_cmd_ack_req_nc01);
+				cmd->dst = EBI2_NAND_ADM_MUX;
+				cmd->len = 4;
+				cmd++;
+
+				/* CMD */
+				cmd->cmd = DST_CRCI_NAND_CMD;
+				cmd->src = msm_virt_to_dma(chip,
+						&dma_buffer->data.cmd);
+				cmd->dst = NC10(MSM_NAND_FLASH_CMD);
+				cmd->len = 4;
+				cmd++;
+			}
+
+			if (ops->mode != MTD_OPS_RAW)
+				sectordatawritesize = (n < (cwperpage - 1)) ?
+					516 : (512 - ((cwperpage - 1) << 2));
+			else
+				sectordatawritesize = chip->cw_size;
+
+			cmd->cmd = 0;
+			cmd->src = data_dma_addr_curr;
+			data_dma_addr_curr += sectordatawritesize;
+
+			if (n % 2 == 0)
+				cmd->dst = NC01(MSM_NAND_FLASH_BUFFER);
+			else
+				cmd->dst = NC10(MSM_NAND_FLASH_BUFFER);
+			cmd->len = sectordatawritesize;
+			cmd++;
+
+			if (ops->oobbuf) {
+				if (n == (cwperpage - 1)) {
+					cmd->cmd = 0;
+					cmd->src = oob_dma_addr_curr;
+					cmd->dst = NC10(MSM_NAND_FLASH_BUFFER) +
+						(512 - ((cwperpage - 1) << 2));
+					if ((cwperpage << 2) < oob_len)
+						cmd->len = (cwperpage << 2);
+					else
+						cmd->len = oob_len;
+					oob_dma_addr_curr += cmd->len;
+					oob_len -= cmd->len;
+					if (cmd->len > 0)
+						cmd++;
+				}
+				if (ops->mode != MTD_OPS_AUTO_OOB) {
+					/* skip ecc bytes in oobbuf */
+					if (oob_len < chip->ecc_parity_bytes) {
+						oob_dma_addr_curr +=
+							chip->ecc_parity_bytes;
+						oob_len -=
+							chip->ecc_parity_bytes;
+					} else {
+						oob_dma_addr_curr += oob_len;
+						oob_len = 0;
+					}
+				}
+			}
+
+			if (n % 2 == 0) {
+				if (n != 0) {
+					/* MASK DATA ACK/REQ --> NC01 (0xA3C)*/
+					cmd->cmd = 0;
+					cmd->src = msm_virt_to_dma(chip,
+					&dma_buffer->
+					data.adm_mux_data_ack_req_nc01);
+					cmd->dst = EBI2_NAND_ADM_MUX;
+					cmd->len = 4;
+					cmd++;
+
+					/* block on data ready from NC10, then
+					* read the status register
+					*/
+					cmd->cmd = SRC_CRCI_NAND_DATA;
+					cmd->src = NC10(MSM_NAND_FLASH_STATUS);
+					cmd->dst = msm_virt_to_dma(chip,
+					&dma_buffer->data.flash_status[n-1]);
+					cmd->len = 4;
+					cmd++;
+				}
+				/* kick the NC01 execute register */
+				cmd->cmd = 0;
+				cmd->src = msm_virt_to_dma(chip,
+						&dma_buffer->data.exec);
+				cmd->dst = NC01(MSM_NAND_EXEC_CMD);
+				cmd->len = 4;
+				cmd++;
+			} else {
+				/* MASK DATA ACK/REQ --> NC10 (0xF28)*/
+				cmd->cmd = 0;
+				cmd->src = msm_virt_to_dma(chip,
+				&dma_buffer->data.adm_mux_data_ack_req_nc10);
+				cmd->dst = EBI2_NAND_ADM_MUX;
+				cmd->len = 4;
+				cmd++;
+
+				/* block on data ready from NC01, then
+				 * read the status register
+				 */
+				cmd->cmd = SRC_CRCI_NAND_DATA;
+				cmd->src = NC01(MSM_NAND_FLASH_STATUS);
+				cmd->dst = msm_virt_to_dma(chip,
+				&dma_buffer->data.flash_status[n-1]);
+				cmd->len = 4;
+				cmd++;
+
+				/* kick the execute register */
+				cmd->cmd = 0;
+				cmd->src =
+				msm_virt_to_dma(chip, &dma_buffer->data.exec);
+				cmd->dst = NC10(MSM_NAND_EXEC_CMD);
+				cmd->len = 4;
+				cmd++;
+			}
+		}
+
+		/* MASK DATA ACK/REQ --> NC01 (0xA3C)*/
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip,
+				&dma_buffer->data.adm_mux_data_ack_req_nc01);
+		cmd->dst = EBI2_NAND_ADM_MUX;
+		cmd->len = 4;
+		cmd++;
+
+		/* we should process outstanding request */
+		/* block on data ready, then
+		 * read the status register
+		 */
+		cmd->cmd = SRC_CRCI_NAND_DATA;
+		cmd->src = NC10(MSM_NAND_FLASH_STATUS);
+		cmd->dst = msm_virt_to_dma(chip,
+			     &dma_buffer->data.flash_status[n-1]);
+		cmd->len = 4;
+		cmd++;
+
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.clrfstatus);
+		cmd->dst = NC11(MSM_NAND_FLASH_STATUS);
+		cmd->len = 4;
+		cmd++;
+
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.clrrstatus);
+		cmd->dst = NC11(MSM_NAND_READ_STATUS);
+		cmd->len = 4;
+		cmd++;
+
+		/* MASK DATA ACK/REQ --> NC01 (0xFC0)*/
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip,
+				&dma_buffer->data.adm_default_mux);
+		cmd->dst = EBI2_NAND_ADM_MUX;
+		cmd->len = 4;
+		cmd++;
+
+		if (!interleave_enable) {
+			/* setting to defalut values back */
+			cmd->cmd = 0;
+			cmd->src = msm_virt_to_dma(chip,
+			&dma_buffer->data.nc01_flash_dev_cmd_vld_default);
+			cmd->dst = NC01(MSM_NAND_DEV_CMD_VLD);
+			cmd->len = 4;
+			cmd++;
+
+			cmd->cmd = 0;
+			cmd->src = msm_virt_to_dma(chip,
+			&dma_buffer->data.nc10_flash_dev_cmd0_default);
+			cmd->dst = NC10(MSM_NAND_DEV_CMD0);
+			cmd->len = 4;
+			cmd++;
+		} else {
+			/* disable CS1 */
+			cmd->cmd = 0;
+			cmd->src = msm_virt_to_dma(chip,
+			&dma_buffer->data.default_ebi2_chip_select_cfg0);
+			cmd->dst = EBI2_CHIP_SELECT_CFG0;
+			cmd->len = 4;
+			cmd++;
+		}
+
+		dma_buffer->cmd[0].cmd |= CMD_OCB;
+		cmd[-1].cmd |= CMD_OCU | CMD_LC;
+		BUILD_BUG_ON(16 * 6 + 18 != ARRAY_SIZE(dma_buffer->cmd));
+		BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
+		dma_buffer->cmdptr =
+		((msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP);
+
+		mb();
+		msm_dmov_exec_cmd(chip->dma_channel,
+			DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(
+				msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
+		mb();
+
+		/* if any of the writes failed (0x10), or there was a
+		 * protection violation (0x100), or the program success
+		 * bit (0x80) is unset, we lose
+		 */
+		err = 0;
+		for (n = 0; n < cwperpage; n++) {
+			if (dma_buffer->data.flash_status[n] & 0x110) {
+				err = -EIO;
+				break;
+			}
+			if (!(dma_buffer->data.flash_status[n] & 0x80)) {
+				err = -EIO;
+				break;
+			}
+		}
+		/* check for flash status busy for the last codeword */
+		if (!interleave_enable)
+			if (!(dma_buffer->data.flash_status[cwperpage - 1]
+								& 0x20)) {
+				err = -EIO;
+				break;
+			}
+#if VERBOSE
+	for (n = 0; n < cwperpage; n++) {
+		if (n%2) {
+			pr_info("NC10: write pg %d: flash_status[%d] = %x\n",
+				page, n, dma_buffer->data.flash_status[n]);
+		} else {
+			pr_info("NC01: write pg %d: flash_status[%d] = %x\n",
+				page, n, dma_buffer->data.flash_status[n]);
+		}
+	}
+#endif
+		if (err)
+			break;
+		pages_written++;
+		page++;
+	}
+	if (ops->mode != MTD_OPS_RAW)
+		ops->retlen = mtd->writesize * pages_written;
+	else
+		ops->retlen = (mtd->writesize + mtd->oobsize) * pages_written;
+
+	ops->oobretlen = ops->ooblen - oob_len;
+
+	msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+
+	if (ops->oobbuf)
+		dma_unmap_page(chip->dev, oob_dma_addr,
+				 ops->ooblen, DMA_TO_DEVICE);
+err_dma_map_oobbuf_failed:
+	if (ops->datbuf)
+		dma_unmap_page(chip->dev, data_dma_addr, ops->len,
+				DMA_TO_DEVICE);
+	if (err)
+		pr_err("msm_nand_write_oob_dualnandc %llx %x %x failed %d\n",
+		       to, ops->len, ops->ooblen, err);
+
+#if VERBOSE
+	pr_info("\n%s: ret %d, retlen %d oobretlen %d\n",
+			__func__, err, ops->retlen, ops->oobretlen);
+
+	pr_info("==================================================="
+			"==========\n");
+#endif
+	return err;
+}
+
+static int msm_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
+			  size_t *retlen, const u_char *buf)
+{
+	int ret;
+	struct mtd_oob_ops ops;
+
+	ops.mode = MTD_OPS_PLACE_OOB;
+	ops.len = len;
+	ops.retlen = 0;
+	ops.ooblen = 0;
+	ops.datbuf = (uint8_t *)buf;
+	ops.oobbuf = NULL;
+	if (!dual_nand_ctlr_present)
+		ret =  msm_nand_write_oob(mtd, to, &ops);
+	else
+		ret =  msm_nand_write_oob_dualnandc(mtd, to, &ops);
+	*retlen = ops.retlen;
+	return ret;
+}
+
+static int
+msm_nand_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+	int err;
+	struct msm_nand_chip *chip = mtd->priv;
+	struct {
+		dmov_s cmd[6];
+		unsigned cmdptr;
+		struct {
+			uint32_t cmd;
+			uint32_t addr0;
+			uint32_t addr1;
+			uint32_t chipsel;
+			uint32_t cfg0;
+			uint32_t cfg1;
+			uint32_t exec;
+			uint32_t flash_status;
+			uint32_t clrfstatus;
+			uint32_t clrrstatus;
+		} data;
+	} *dma_buffer;
+	dmov_s *cmd;
+	unsigned page = 0;
+
+	if (mtd->writesize == 2048)
+		page = instr->addr >> 11;
+
+	if (mtd->writesize == 4096)
+		page = instr->addr >> 12;
+
+	if (instr->addr & (mtd->erasesize - 1)) {
+		pr_err("%s: unsupported erase address, 0x%llx\n",
+		       __func__, instr->addr);
+		return -EINVAL;
+	}
+	if (instr->len != mtd->erasesize) {
+		pr_err("%s: unsupported erase len, %lld\n",
+		       __func__, instr->len);
+		return -EINVAL;
+	}
+
+	wait_event(chip->wait_queue,
+		   (dma_buffer = msm_nand_get_dma_buffer(
+			    chip, sizeof(*dma_buffer))));
+
+	cmd = dma_buffer->cmd;
+
+	dma_buffer->data.cmd = MSM_NAND_CMD_BLOCK_ERASE;
+	dma_buffer->data.addr0 = page;
+	dma_buffer->data.addr1 = 0;
+	dma_buffer->data.chipsel = 0 | 4;
+	dma_buffer->data.exec = 1;
+	dma_buffer->data.flash_status = 0xeeeeeeee;
+	dma_buffer->data.cfg0 = chip->CFG0 & (~(7 << 6));  /* CW_PER_PAGE = 0 */
+	dma_buffer->data.cfg1 = chip->CFG1;
+	dma_buffer->data.clrfstatus = 0x00000020;
+	dma_buffer->data.clrrstatus = 0x000000C0;
+
+	cmd->cmd = DST_CRCI_NAND_CMD | CMD_OCB;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd);
+	cmd->dst = MSM_NAND_FLASH_CMD;
+	cmd->len = 16;
+	cmd++;
+
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0);
+	cmd->dst = MSM_NAND_DEV0_CFG0;
+	cmd->len = 8;
+	cmd++;
+
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec);
+	cmd->dst = MSM_NAND_EXEC_CMD;
+	cmd->len = 4;
+	cmd++;
+
+	cmd->cmd = SRC_CRCI_NAND_DATA;
+	cmd->src = MSM_NAND_FLASH_STATUS;
+	cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.flash_status);
+	cmd->len = 4;
+	cmd++;
+
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.clrfstatus);
+	cmd->dst = MSM_NAND_FLASH_STATUS;
+	cmd->len = 4;
+	cmd++;
+
+	cmd->cmd = CMD_OCU | CMD_LC;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.clrrstatus);
+	cmd->dst = MSM_NAND_READ_STATUS;
+	cmd->len = 4;
+	cmd++;
+
+	BUILD_BUG_ON(5 != ARRAY_SIZE(dma_buffer->cmd) - 1);
+	BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
+	dma_buffer->cmdptr =
+		(msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP;
+
+	mb();
+	msm_dmov_exec_cmd(
+		chip->dma_channel, DMOV_CMD_PTR_LIST |
+		DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
+	mb();
+
+	/* we fail if there was an operation error, a mpu error, or the
+	 * erase success bit was not set.
+	 */
+
+	if (dma_buffer->data.flash_status & 0x110 ||
+			!(dma_buffer->data.flash_status & 0x80))
+		err = -EIO;
+	else
+		err = 0;
+
+	msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+	if (err) {
+		pr_err("%s: erase failed, 0x%llx\n", __func__, instr->addr);
+		instr->fail_addr = instr->addr;
+		instr->state = MTD_ERASE_FAILED;
+	} else {
+		instr->state = MTD_ERASE_DONE;
+		instr->fail_addr = 0xffffffff;
+		mtd_erase_callback(instr);
+	}
+	return err;
+}
+
+static int
+msm_nand_erase_dualnandc(struct mtd_info *mtd, struct erase_info *instr)
+{
+	int err;
+	struct msm_nand_chip *chip = mtd->priv;
+	struct {
+		dmov_s cmd[18];
+		unsigned cmdptr;
+		struct {
+			uint32_t cmd;
+			uint32_t addr0;
+			uint32_t addr1;
+			uint32_t chipsel_cs0;
+			uint32_t chipsel_cs1;
+			uint32_t cfg0;
+			uint32_t cfg1;
+			uint32_t exec;
+			uint32_t ecccfg;
+			uint32_t ebi2_chip_select_cfg0;
+			uint32_t adm_mux_data_ack_req_nc01;
+			uint32_t adm_mux_cmd_ack_req_nc01;
+			uint32_t adm_mux_data_ack_req_nc10;
+			uint32_t adm_mux_cmd_ack_req_nc10;
+			uint32_t adm_default_mux;
+			uint32_t default_ebi2_chip_select_cfg0;
+			uint32_t nc01_flash_dev_cmd0;
+			uint32_t nc01_flash_dev_cmd0_default;
+			uint32_t flash_status[2];
+			uint32_t clrfstatus;
+			uint32_t clrrstatus;
+		} data;
+	} *dma_buffer;
+	dmov_s *cmd;
+	unsigned page = 0;
+
+	if (mtd->writesize == 2048)
+		page = instr->addr >> 11;
+
+	if (mtd->writesize == 4096)
+		page = instr->addr >> 12;
+
+	if (mtd->writesize == 8192)
+		page = (instr->addr >> 1) >> 12;
+
+	if (instr->addr & (mtd->erasesize - 1)) {
+		pr_err("%s: unsupported erase address, 0x%llx\n",
+		       __func__, instr->addr);
+		return -EINVAL;
+	}
+	if (instr->len != mtd->erasesize) {
+		pr_err("%s: unsupported erase len, %lld\n",
+		       __func__, instr->len);
+		return -EINVAL;
+	}
+
+	wait_event(chip->wait_queue,
+		   (dma_buffer = msm_nand_get_dma_buffer(
+			    chip, sizeof(*dma_buffer))));
+
+	cmd = dma_buffer->cmd;
+
+	dma_buffer->data.cmd = MSM_NAND_CMD_BLOCK_ERASE;
+	dma_buffer->data.addr0 = page;
+	dma_buffer->data.addr1 = 0;
+	dma_buffer->data.chipsel_cs0 = (1<<4) | 4;
+	dma_buffer->data.chipsel_cs1 = (1<<4) | 5;
+	dma_buffer->data.exec = 1;
+	dma_buffer->data.flash_status[0] = 0xeeeeeeee;
+	dma_buffer->data.flash_status[1] = 0xeeeeeeee;
+	dma_buffer->data.cfg0 = chip->CFG0 & (~(7 << 6));  /* CW_PER_PAGE = 0 */
+	dma_buffer->data.cfg1 = chip->CFG1;
+	dma_buffer->data.clrfstatus = 0x00000020;
+	dma_buffer->data.clrrstatus = 0x000000C0;
+
+	dma_buffer->data.ebi2_chip_select_cfg0 = 0x00000805;
+	dma_buffer->data.adm_mux_data_ack_req_nc01 = 0x00000A3C;
+	dma_buffer->data.adm_mux_cmd_ack_req_nc01  = 0x0000053C;
+	dma_buffer->data.adm_mux_data_ack_req_nc10 = 0x00000F28;
+	dma_buffer->data.adm_mux_cmd_ack_req_nc10  = 0x00000F14;
+	dma_buffer->data.adm_default_mux = 0x00000FC0;
+	dma_buffer->data.default_ebi2_chip_select_cfg0 = 0x00000801;
+
+	/* enable CS1 */
+	cmd->cmd = 0 | CMD_OCB;
+	cmd->src = msm_virt_to_dma(chip,
+			&dma_buffer->data.ebi2_chip_select_cfg0);
+	cmd->dst = EBI2_CHIP_SELECT_CFG0;
+	cmd->len = 4;
+	cmd++;
+
+	/* erase CS0 block now !!! */
+	/* 0xF14 */
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip,
+			&dma_buffer->data.adm_mux_cmd_ack_req_nc10);
+	cmd->dst = EBI2_NAND_ADM_MUX;
+	cmd->len = 4;
+	cmd++;
+
+	cmd->cmd = DST_CRCI_NAND_CMD;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd);
+	cmd->dst = NC01(MSM_NAND_FLASH_CMD);
+	cmd->len = 16;
+	cmd++;
+
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0);
+	cmd->dst = NC01(MSM_NAND_DEV0_CFG0);
+	cmd->len = 8;
+	cmd++;
+
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec);
+	cmd->dst = NC01(MSM_NAND_EXEC_CMD);
+	cmd->len = 4;
+	cmd++;
+
+	/* 0xF28 */
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip,
+			&dma_buffer->data.adm_mux_data_ack_req_nc10);
+	cmd->dst = EBI2_NAND_ADM_MUX;
+	cmd->len = 4;
+	cmd++;
+
+	cmd->cmd = SRC_CRCI_NAND_DATA;
+	cmd->src = NC01(MSM_NAND_FLASH_STATUS);
+	cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.flash_status[0]);
+	cmd->len = 4;
+	cmd++;
+
+	/* erase CS1 block now !!! */
+	/* 0x53C */
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip,
+			       &dma_buffer->data.adm_mux_cmd_ack_req_nc01);
+	cmd->dst = EBI2_NAND_ADM_MUX;
+	cmd->len = 4;
+	cmd++;
+
+	cmd->cmd = DST_CRCI_NAND_CMD;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd);
+	cmd->dst = NC10(MSM_NAND_FLASH_CMD);
+	cmd->len = 12;
+	cmd++;
+
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.chipsel_cs1);
+	cmd->dst = NC10(MSM_NAND_FLASH_CHIP_SELECT);
+	cmd->len = 4;
+	cmd++;
+
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0);
+	cmd->dst = NC10(MSM_NAND_DEV1_CFG0);
+	cmd->len = 8;
+
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec);
+	cmd->dst = NC10(MSM_NAND_EXEC_CMD);
+	cmd->len = 4;
+	cmd++;
+
+	/* 0xA3C */
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip,
+			     &dma_buffer->data.adm_mux_data_ack_req_nc01);
+	cmd->dst = EBI2_NAND_ADM_MUX;
+	cmd->len = 4;
+	cmd++;
+
+	cmd->cmd = SRC_CRCI_NAND_DATA;
+	cmd->src = NC10(MSM_NAND_FLASH_STATUS);
+	cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.flash_status[1]);
+	cmd->len = 4;
+	cmd++;
+
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.clrfstatus);
+	cmd->dst = NC11(MSM_NAND_FLASH_STATUS);
+	cmd->len = 4;
+	cmd++;
+
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.clrrstatus);
+	cmd->dst = NC11(MSM_NAND_READ_STATUS);
+	cmd->len = 4;
+	cmd++;
+
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip,
+			&dma_buffer->data.adm_default_mux);
+	cmd->dst = EBI2_NAND_ADM_MUX;
+	cmd->len = 4;
+	cmd++;
+
+	/* disable CS1 */
+	cmd->cmd = CMD_OCU | CMD_LC;
+	cmd->src = msm_virt_to_dma(chip,
+			&dma_buffer->data.default_ebi2_chip_select_cfg0);
+	cmd->dst = EBI2_CHIP_SELECT_CFG0;
+	cmd->len = 4;
+	cmd++;
+
+	BUILD_BUG_ON(17 != ARRAY_SIZE(dma_buffer->cmd) - 1);
+	BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
+
+	dma_buffer->cmdptr =
+		(msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP;
+
+	mb();
+	msm_dmov_exec_cmd(
+		chip->dma_channel, DMOV_CMD_PTR_LIST |
+		DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
+	mb();
+
+	/* we fail if there was an operation error, a mpu error, or the
+	 * erase success bit was not set.
+	 */
+
+	if (dma_buffer->data.flash_status[0] & 0x110 ||
+			!(dma_buffer->data.flash_status[0] & 0x80) ||
+			dma_buffer->data.flash_status[1] & 0x110 ||
+			!(dma_buffer->data.flash_status[1] & 0x80))
+		err = -EIO;
+	else
+		err = 0;
+
+	msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+	if (err) {
+		pr_err("%s: erase failed, 0x%llx\n", __func__, instr->addr);
+		instr->fail_addr = instr->addr;
+		instr->state = MTD_ERASE_FAILED;
+	} else {
+		instr->state = MTD_ERASE_DONE;
+		instr->fail_addr = 0xffffffff;
+		mtd_erase_callback(instr);
+	}
+	return err;
+}
+
+static int
+msm_nand_block_isbad(struct mtd_info *mtd, loff_t ofs)
+{
+	struct msm_nand_chip *chip = mtd->priv;
+	int ret;
+	struct {
+		dmov_s cmd[5];
+		unsigned cmdptr;
+		struct {
+			uint32_t cmd;
+			uint32_t addr0;
+			uint32_t addr1;
+			uint32_t chipsel;
+			uint32_t cfg0;
+			uint32_t cfg1;
+			uint32_t eccbchcfg;
+			uint32_t exec;
+			uint32_t ecccfg;
+			struct {
+				uint32_t flash_status;
+				uint32_t buffer_status;
+			} result;
+		} data;
+	} *dma_buffer;
+	dmov_s *cmd;
+	uint8_t *buf;
+	unsigned page = 0;
+	unsigned cwperpage;
+
+	if (mtd->writesize == 2048)
+		page = ofs >> 11;
+
+	if (mtd->writesize == 4096)
+		page = ofs >> 12;
+
+	cwperpage = (mtd->writesize >> 9);
+
+	/* Check for invalid offset */
+	if (ofs > mtd->size)
+		return -EINVAL;
+	if (ofs & (mtd->erasesize - 1)) {
+		pr_err("%s: unsupported block address, 0x%x\n",
+			 __func__, (uint32_t)ofs);
+		return -EINVAL;
+	}
+
+	wait_event(chip->wait_queue,
+		(dma_buffer = msm_nand_get_dma_buffer(chip ,
+					 sizeof(*dma_buffer) + 4)));
+	buf = (uint8_t *)dma_buffer + sizeof(*dma_buffer);
+
+	/* Read 4 bytes starting from the bad block marker location
+	 * in the last code word of the page
+	 */
+
+	cmd = dma_buffer->cmd;
+
+	dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ;
+	dma_buffer->data.cfg0 = chip->CFG0_RAW & ~(7U << 6);
+	dma_buffer->data.cfg1 = chip->CFG1_RAW |
+				(chip->CFG1 & CFG1_WIDE_FLASH);
+	if (enable_bch_ecc)
+		dma_buffer->data.eccbchcfg = chip->ecc_bch_cfg;
+
+	if (chip->CFG1 & CFG1_WIDE_FLASH)
+		dma_buffer->data.addr0 = (page << 16) |
+			((chip->cw_size * (cwperpage-1)) >> 1);
+	else
+		dma_buffer->data.addr0 = (page << 16) |
+			(chip->cw_size * (cwperpage-1));
+
+	dma_buffer->data.addr1 = (page >> 16) & 0xff;
+	dma_buffer->data.chipsel = 0 | 4;
+
+	dma_buffer->data.exec = 1;
+
+	dma_buffer->data.result.flash_status = 0xeeeeeeee;
+	dma_buffer->data.result.buffer_status = 0xeeeeeeee;
+
+	cmd->cmd = DST_CRCI_NAND_CMD;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd);
+	cmd->dst = MSM_NAND_FLASH_CMD;
+	cmd->len = 16;
+	cmd++;
+
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0);
+	cmd->dst = MSM_NAND_DEV0_CFG0;
+	if (enable_bch_ecc)
+		cmd->len = 12;
+	else
+		cmd->len = 8;
+	cmd++;
+
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec);
+	cmd->dst = MSM_NAND_EXEC_CMD;
+	cmd->len = 4;
+	cmd++;
+
+	cmd->cmd = SRC_CRCI_NAND_DATA;
+	cmd->src = MSM_NAND_FLASH_STATUS;
+	cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.result);
+	cmd->len = 8;
+	cmd++;
+
+	cmd->cmd = 0;
+	cmd->src = MSM_NAND_FLASH_BUFFER +
+	(mtd->writesize - (chip->cw_size * (cwperpage-1)));
+	cmd->dst = msm_virt_to_dma(chip, buf);
+	cmd->len = 4;
+	cmd++;
+
+	BUILD_BUG_ON(5 != ARRAY_SIZE(dma_buffer->cmd));
+	BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
+	dma_buffer->cmd[0].cmd |= CMD_OCB;
+	cmd[-1].cmd |= CMD_OCU | CMD_LC;
+
+	dma_buffer->cmdptr = (msm_virt_to_dma(chip,
+				dma_buffer->cmd) >> 3) | CMD_PTR_LP;
+
+	mb();
+	msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST |
+		DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
+	mb();
+
+	ret = 0;
+	if (dma_buffer->data.result.flash_status & 0x110)
+		ret = -EIO;
+
+	if (!ret) {
+		/* Check for bad block marker byte */
+		if (chip->CFG1 & CFG1_WIDE_FLASH) {
+			if (buf[0] != 0xFF || buf[1] != 0xFF)
+				ret = 1;
+		} else {
+			if (buf[0] != 0xFF)
+				ret = 1;
+		}
+	}
+
+	msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer) + 4);
+	return ret;
+}
+
+static int
+msm_nand_block_isbad_dualnandc(struct mtd_info *mtd, loff_t ofs)
+{
+	struct msm_nand_chip *chip = mtd->priv;
+	int ret;
+	struct {
+		dmov_s cmd[18];
+		unsigned cmdptr;
+		struct {
+			uint32_t cmd;
+			uint32_t addr0;
+			uint32_t addr1;
+			uint32_t chipsel_cs0;
+			uint32_t chipsel_cs1;
+			uint32_t cfg0;
+			uint32_t cfg1;
+			uint32_t exec;
+			uint32_t ecccfg;
+			uint32_t ebi2_chip_select_cfg0;
+			uint32_t adm_mux_data_ack_req_nc01;
+			uint32_t adm_mux_cmd_ack_req_nc01;
+			uint32_t adm_mux_data_ack_req_nc10;
+			uint32_t adm_mux_cmd_ack_req_nc10;
+			uint32_t adm_default_mux;
+			uint32_t default_ebi2_chip_select_cfg0;
+			struct {
+				uint32_t flash_status;
+				uint32_t buffer_status;
+			} result[2];
+		} data;
+	} *dma_buffer;
+	dmov_s *cmd;
+	uint8_t *buf01;
+	uint8_t *buf10;
+	unsigned page = 0;
+	unsigned cwperpage;
+
+	if (mtd->writesize == 2048)
+		page = ofs >> 11;
+
+	if (mtd->writesize == 4096)
+		page = ofs >> 12;
+
+	if (mtd->writesize == 8192)
+		page = (ofs >> 1) >> 12;
+
+	cwperpage = ((mtd->writesize >> 1) >> 9);
+
+	/* Check for invalid offset */
+	if (ofs > mtd->size)
+		return -EINVAL;
+	if (ofs & (mtd->erasesize - 1)) {
+		pr_err("%s: unsupported block address, 0x%x\n",
+			 __func__, (uint32_t)ofs);
+		return -EINVAL;
+	}
+
+	wait_event(chip->wait_queue,
+		(dma_buffer = msm_nand_get_dma_buffer(chip ,
+					 sizeof(*dma_buffer) + 8)));
+	buf01 = (uint8_t *)dma_buffer + sizeof(*dma_buffer);
+	buf10 = buf01 + 4;
+
+	/* Read 4 bytes starting from the bad block marker location
+	 * in the last code word of the page
+	 */
+	cmd = dma_buffer->cmd;
+
+	dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ;
+	dma_buffer->data.cfg0 = chip->CFG0_RAW & ~(7U << 6);
+	dma_buffer->data.cfg1 = chip->CFG1_RAW |
+				(chip->CFG1 & CFG1_WIDE_FLASH);
+
+	if (chip->CFG1 & CFG1_WIDE_FLASH)
+		dma_buffer->data.addr0 = (page << 16) |
+			((528*(cwperpage-1)) >> 1);
+	else
+		dma_buffer->data.addr0 = (page << 16) |
+			(528*(cwperpage-1));
+
+	dma_buffer->data.addr1 = (page >> 16) & 0xff;
+	dma_buffer->data.chipsel_cs0 = (1<<4) | 4;
+	dma_buffer->data.chipsel_cs1 = (1<<4) | 5;
+
+	dma_buffer->data.exec = 1;
+
+	dma_buffer->data.result[0].flash_status = 0xeeeeeeee;
+	dma_buffer->data.result[0].buffer_status = 0xeeeeeeee;
+	dma_buffer->data.result[1].flash_status = 0xeeeeeeee;
+	dma_buffer->data.result[1].buffer_status = 0xeeeeeeee;
+
+	dma_buffer->data.ebi2_chip_select_cfg0 = 0x00000805;
+	dma_buffer->data.adm_mux_data_ack_req_nc01 = 0x00000A3C;
+	dma_buffer->data.adm_mux_cmd_ack_req_nc01  = 0x0000053C;
+	dma_buffer->data.adm_mux_data_ack_req_nc10 = 0x00000F28;
+	dma_buffer->data.adm_mux_cmd_ack_req_nc10  = 0x00000F14;
+	dma_buffer->data.adm_default_mux = 0x00000FC0;
+	dma_buffer->data.default_ebi2_chip_select_cfg0 = 0x00000801;
+
+	/* Reading last code word from NC01 */
+	/* enable CS1 */
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip,
+			&dma_buffer->data.ebi2_chip_select_cfg0);
+	cmd->dst = EBI2_CHIP_SELECT_CFG0;
+	cmd->len = 4;
+	cmd++;
+
+	/* 0xF14 */
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip,
+			&dma_buffer->data.adm_mux_cmd_ack_req_nc10);
+	cmd->dst = EBI2_NAND_ADM_MUX;
+	cmd->len = 4;
+	cmd++;
+
+	cmd->cmd = DST_CRCI_NAND_CMD;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd);
+	cmd->dst = NC01(MSM_NAND_FLASH_CMD);
+	cmd->len = 16;
+	cmd++;
+
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0);
+	cmd->dst = NC01(MSM_NAND_DEV0_CFG0);
+	cmd->len = 8;
+	cmd++;
+
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec);
+	cmd->dst = NC01(MSM_NAND_EXEC_CMD);
+	cmd->len = 4;
+	cmd++;
+
+	/* 0xF28 */
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip,
+			&dma_buffer->data.adm_mux_data_ack_req_nc10);
+	cmd->dst = EBI2_NAND_ADM_MUX;
+	cmd->len = 4;
+	cmd++;
+
+	cmd->cmd = SRC_CRCI_NAND_DATA;
+	cmd->src = NC01(MSM_NAND_FLASH_STATUS);
+	cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.result[0]);
+	cmd->len = 8;
+	cmd++;
+
+	cmd->cmd = 0;
+	cmd->src = NC01(MSM_NAND_FLASH_BUFFER) + ((mtd->writesize >> 1) -
+							(528*(cwperpage-1)));
+	cmd->dst = msm_virt_to_dma(chip, buf01);
+	cmd->len = 4;
+	cmd++;
+
+	/* Reading last code word from NC10 */
+	/* 0x53C */
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip,
+	&dma_buffer->data.adm_mux_cmd_ack_req_nc01);
+	cmd->dst = EBI2_NAND_ADM_MUX;
+	cmd->len = 4;
+	cmd++;
+
+	cmd->cmd = DST_CRCI_NAND_CMD;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd);
+	cmd->dst = NC10(MSM_NAND_FLASH_CMD);
+	cmd->len = 12;
+	cmd++;
+
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.chipsel_cs1);
+	cmd->dst = NC10(MSM_NAND_FLASH_CHIP_SELECT);
+	cmd->len = 4;
+	cmd++;
+
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0);
+	cmd->dst = NC10(MSM_NAND_DEV1_CFG0);
+	cmd->len = 8;
+	cmd++;
+
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec);
+	cmd->dst = NC10(MSM_NAND_EXEC_CMD);
+	cmd->len = 4;
+	cmd++;
+
+	/* A3C */
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip,
+			&dma_buffer->data.adm_mux_data_ack_req_nc01);
+	cmd->dst = EBI2_NAND_ADM_MUX;
+	cmd->len = 4;
+	cmd++;
+
+	cmd->cmd = SRC_CRCI_NAND_DATA;
+	cmd->src = NC10(MSM_NAND_FLASH_STATUS);
+	cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.result[1]);
+	cmd->len = 8;
+	cmd++;
+
+	cmd->cmd = 0;
+	cmd->src = NC10(MSM_NAND_FLASH_BUFFER) + ((mtd->writesize >> 1) -
+							(528*(cwperpage-1)));
+	cmd->dst = msm_virt_to_dma(chip, buf10);
+	cmd->len = 4;
+	cmd++;
+
+	/* FC0 */
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip,
+			&dma_buffer->data.adm_default_mux);
+	cmd->dst = EBI2_NAND_ADM_MUX;
+	cmd->len = 4;
+	cmd++;
+
+	/* disble CS1 */
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip,
+			&dma_buffer->data.ebi2_chip_select_cfg0);
+	cmd->dst = EBI2_CHIP_SELECT_CFG0;
+	cmd->len = 4;
+	cmd++;
+
+	BUILD_BUG_ON(18 != ARRAY_SIZE(dma_buffer->cmd));
+	BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
+	dma_buffer->cmd[0].cmd |= CMD_OCB;
+	cmd[-1].cmd |= CMD_OCU | CMD_LC;
+
+	dma_buffer->cmdptr = (msm_virt_to_dma(chip,
+				dma_buffer->cmd) >> 3) | CMD_PTR_LP;
+
+	mb();
+	msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST |
+		DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
+	mb();
+
+	ret = 0;
+	if ((dma_buffer->data.result[0].flash_status & 0x110) ||
+			(dma_buffer->data.result[1].flash_status & 0x110))
+		ret = -EIO;
+
+	if (!ret) {
+		/* Check for bad block marker byte for NC01 & NC10 */
+		if (chip->CFG1 & CFG1_WIDE_FLASH) {
+			if ((buf01[0] != 0xFF || buf01[1] != 0xFF) ||
+				(buf10[0] != 0xFF || buf10[1] != 0xFF))
+				ret = 1;
+		} else {
+			if (buf01[0] != 0xFF || buf10[0] != 0xFF)
+				ret = 1;
+		}
+	}
+
+	msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer) + 8);
+	return ret;
+}
+
+static int
+msm_nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+	struct mtd_oob_ops ops;
+	int ret;
+	uint8_t *buf;
+
+	/* Check for invalid offset */
+	if (ofs > mtd->size)
+		return -EINVAL;
+	if (ofs & (mtd->erasesize - 1)) {
+		pr_err("%s: unsupported block address, 0x%x\n",
+				 __func__, (uint32_t)ofs);
+		return -EINVAL;
+	}
+
+	/*
+	Write all 0s to the first page
+	This will set the BB marker to 0
+	*/
+	buf = page_address(ZERO_PAGE());
+
+	ops.mode = MTD_OPS_RAW;
+	ops.len = mtd->writesize + mtd->oobsize;
+	ops.retlen = 0;
+	ops.ooblen = 0;
+	ops.datbuf = buf;
+	ops.oobbuf = NULL;
+	if (!interleave_enable)
+		ret =  msm_nand_write_oob(mtd, ofs, &ops);
+	else
+		ret = msm_nand_write_oob_dualnandc(mtd, ofs, &ops);
+
+	return ret;
+}
+
+/**
+ * msm_nand_suspend - [MTD Interface] Suspend the msm_nand flash
+ * @param mtd		MTD device structure
+ */
+static int msm_nand_suspend(struct mtd_info *mtd)
+{
+	return 0;
+}
+
+/**
+ * msm_nand_resume - [MTD Interface] Resume the msm_nand flash
+ * @param mtd		MTD device structure
+ */
+static void msm_nand_resume(struct mtd_info *mtd)
+{
+}
+
+struct onenand_information {
+	uint16_t manufacturer_id;
+	uint16_t device_id;
+	uint16_t version_id;
+	uint16_t data_buf_size;
+	uint16_t boot_buf_size;
+	uint16_t num_of_buffers;
+	uint16_t technology;
+};
+
+static struct onenand_information onenand_info;
+static uint32_t nand_sfcmd_mode;
+
+uint32_t flash_onenand_probe(struct msm_nand_chip *chip)
+{
+	struct {
+		dmov_s cmd[7];
+		unsigned cmdptr;
+		struct {
+			uint32_t bcfg;
+			uint32_t cmd;
+			uint32_t exec;
+			uint32_t status;
+			uint32_t addr0;
+			uint32_t addr1;
+			uint32_t addr2;
+			uint32_t addr3;
+			uint32_t addr4;
+			uint32_t addr5;
+			uint32_t addr6;
+			uint32_t data0;
+			uint32_t data1;
+			uint32_t data2;
+			uint32_t data3;
+			uint32_t data4;
+			uint32_t data5;
+			uint32_t data6;
+		} data;
+	} *dma_buffer;
+	dmov_s *cmd;
+
+	int err = 0;
+	uint32_t initialsflashcmd = 0;
+
+	initialsflashcmd = flash_rd_reg(chip, MSM_NAND_SFLASHC_CMD);
+
+	if ((initialsflashcmd & 0x10) == 0x10)
+		nand_sfcmd_mode = MSM_NAND_SFCMD_ASYNC;
+	else
+		nand_sfcmd_mode = MSM_NAND_SFCMD_BURST;
+
+	printk(KERN_INFO "SFLASHC Async Mode bit: %x \n", nand_sfcmd_mode);
+
+	wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer
+				(chip, sizeof(*dma_buffer))));
+
+	cmd = dma_buffer->cmd;
+
+	dma_buffer->data.bcfg = SFLASH_BCFG |
+					(nand_sfcmd_mode ? 0 : (1 << 24));
+	dma_buffer->data.cmd = SFLASH_PREPCMD(7, 0, 0,
+						MSM_NAND_SFCMD_DATXS,
+						nand_sfcmd_mode,
+						MSM_NAND_SFCMD_REGRD);
+	dma_buffer->data.exec = 1;
+	dma_buffer->data.status = CLEAN_DATA_32;
+	dma_buffer->data.addr0 = (ONENAND_DEVICE_ID << 16) |
+						(ONENAND_MANUFACTURER_ID);
+	dma_buffer->data.addr1 = (ONENAND_DATA_BUFFER_SIZE << 16) |
+						(ONENAND_VERSION_ID);
+	dma_buffer->data.addr2 = (ONENAND_AMOUNT_OF_BUFFERS << 16) |
+						(ONENAND_BOOT_BUFFER_SIZE);
+	dma_buffer->data.addr3 = (CLEAN_DATA_16 << 16) |
+						(ONENAND_TECHNOLOGY << 0);
+	dma_buffer->data.data0 = CLEAN_DATA_32;
+	dma_buffer->data.data1 = CLEAN_DATA_32;
+	dma_buffer->data.data2 = CLEAN_DATA_32;
+	dma_buffer->data.data3 = CLEAN_DATA_32;
+
+	/* Enable and configure the SFlash controller */
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.bcfg);
+	cmd->dst = MSM_NAND_SFLASHC_BURST_CFG;
+	cmd->len = 4;
+	cmd++;
+
+	/* Block on cmd ready and write CMD register */
+	cmd->cmd = DST_CRCI_NAND_CMD;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd);
+	cmd->dst = MSM_NAND_SFLASHC_CMD;
+	cmd->len = 4;
+	cmd++;
+
+	/* Configure the ADDR0 and ADDR1 registers */
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr0);
+	cmd->dst = MSM_NAND_ADDR0;
+	cmd->len = 8;
+	cmd++;
+
+	/* Configure the ADDR2 and ADDR3 registers */
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr2);
+	cmd->dst = MSM_NAND_ADDR2;
+	cmd->len = 8;
+	cmd++;
+
+	/* Kick the execute command */
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec);
+	cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
+	cmd->len = 4;
+	cmd++;
+
+	/* Block on data ready, and read the two status registers */
+	cmd->cmd = SRC_CRCI_NAND_DATA;
+	cmd->src = MSM_NAND_SFLASHC_STATUS;
+	cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.status);
+	cmd->len = 4;
+	cmd++;
+
+	/* Read data registers - valid only if status says success */
+	cmd->cmd = 0;
+	cmd->src = MSM_NAND_GENP_REG0;
+	cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data0);
+	cmd->len = 16;
+	cmd++;
+
+	BUILD_BUG_ON(7 != ARRAY_SIZE(dma_buffer->cmd));
+	BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
+	dma_buffer->cmd[0].cmd |= CMD_OCB;
+	cmd[-1].cmd |= CMD_OCU | CMD_LC;
+
+	dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd)
+			>> 3) | CMD_PTR_LP;
+
+	mb();
+	msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST
+			| DMOV_CMD_ADDR(msm_virt_to_dma(chip,
+			&dma_buffer->cmdptr)));
+	mb();
+
+	/* Check for errors, protection violations etc */
+	if (dma_buffer->data.status & 0x110) {
+		pr_info("%s: MPU/OP error"
+				"(0x%x) during Onenand probe\n",
+				__func__, dma_buffer->data.status);
+		err = -EIO;
+	} else {
+
+		onenand_info.manufacturer_id =
+			(dma_buffer->data.data0 >> 0) & 0x0000FFFF;
+		onenand_info.device_id =
+			(dma_buffer->data.data0 >> 16) & 0x0000FFFF;
+		onenand_info.version_id =
+			(dma_buffer->data.data1 >> 0) & 0x0000FFFF;
+		onenand_info.data_buf_size =
+			(dma_buffer->data.data1 >> 16) & 0x0000FFFF;
+		onenand_info.boot_buf_size =
+			(dma_buffer->data.data2 >> 0) & 0x0000FFFF;
+		onenand_info.num_of_buffers =
+			(dma_buffer->data.data2 >> 16) & 0x0000FFFF;
+		onenand_info.technology =
+			(dma_buffer->data.data3 >> 0) & 0x0000FFFF;
+
+
+		pr_info("======================================="
+				"==========================\n");
+
+		pr_info("%s: manufacturer_id = 0x%x\n"
+				, __func__, onenand_info.manufacturer_id);
+		pr_info("%s: device_id = 0x%x\n"
+				, __func__, onenand_info.device_id);
+		pr_info("%s: version_id = 0x%x\n"
+				, __func__, onenand_info.version_id);
+		pr_info("%s: data_buf_size = 0x%x\n"
+				, __func__, onenand_info.data_buf_size);
+		pr_info("%s: boot_buf_size = 0x%x\n"
+				, __func__, onenand_info.boot_buf_size);
+		pr_info("%s: num_of_buffers = 0x%x\n"
+				, __func__, onenand_info.num_of_buffers);
+		pr_info("%s: technology = 0x%x\n"
+				, __func__, onenand_info.technology);
+
+		pr_info("======================================="
+				"==========================\n");
+
+		if ((onenand_info.manufacturer_id != 0x00EC)
+			|| ((onenand_info.device_id & 0x0040) != 0x0040)
+			|| (onenand_info.data_buf_size != 0x0800)
+			|| (onenand_info.boot_buf_size != 0x0200)
+			|| (onenand_info.num_of_buffers != 0x0201)
+			|| (onenand_info.technology != 0)) {
+
+			pr_info("%s: Detected an unsupported device\n"
+				, __func__);
+			err = -EIO;
+		}
+	}
+
+	msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+
+	return err;
+}
+
+int msm_onenand_read_oob(struct mtd_info *mtd,
+		loff_t from, struct mtd_oob_ops *ops)
+{
+	struct msm_nand_chip *chip = mtd->priv;
+
+	struct {
+		dmov_s cmd[53];
+		unsigned cmdptr;
+		struct {
+			uint32_t sfbcfg;
+			uint32_t sfcmd[9];
+			uint32_t sfexec;
+			uint32_t sfstat[9];
+			uint32_t addr0;
+			uint32_t addr1;
+			uint32_t addr2;
+			uint32_t addr3;
+			uint32_t addr4;
+			uint32_t addr5;
+			uint32_t addr6;
+			uint32_t data0;
+			uint32_t data1;
+			uint32_t data2;
+			uint32_t data3;
+			uint32_t data4;
+			uint32_t data5;
+			uint32_t data6;
+			uint32_t macro[5];
+		} data;
+	} *dma_buffer;
+	dmov_s *cmd;
+
+	int err = 0;
+	int i;
+	dma_addr_t data_dma_addr = 0;
+	dma_addr_t oob_dma_addr = 0;
+	dma_addr_t data_dma_addr_curr = 0;
+	dma_addr_t oob_dma_addr_curr = 0;
+
+	loff_t from_curr = 0;
+	unsigned page_count;
+	unsigned pages_read = 0;
+
+	uint16_t onenand_startaddr1;
+	uint16_t onenand_startaddr8;
+	uint16_t onenand_startaddr2;
+	uint16_t onenand_startbuffer;
+	uint16_t onenand_sysconfig1;
+	uint16_t controller_status;
+	uint16_t interrupt_status;
+	uint16_t ecc_status;
+#if VERBOSE
+	pr_info("================================================="
+			"================\n");
+	pr_info("%s: from 0x%llx mode %d \ndatbuf 0x%p datlen 0x%x"
+			"\noobbuf 0x%p ooblen 0x%x\n",
+			__func__, from, ops->mode, ops->datbuf, ops->len,
+			ops->oobbuf, ops->ooblen);
+#endif
+	if (!mtd) {
+		pr_err("%s: invalid mtd pointer, 0x%x\n", __func__,
+				(uint32_t)mtd);
+		return -EINVAL;
+	}
+	if (from & (mtd->writesize - 1)) {
+		pr_err("%s: unsupported from, 0x%llx\n", __func__,
+				from);
+		return -EINVAL;
+	}
+
+	if ((ops->mode != MTD_OPS_PLACE_OOB) && (ops->mode != MTD_OPS_AUTO_OOB) &&
+			(ops->mode != MTD_OPS_RAW)) {
+		pr_err("%s: unsupported ops->mode, %d\n", __func__,
+				ops->mode);
+		return -EINVAL;
+	}
+
+	if (((ops->datbuf == NULL) || (ops->len == 0)) &&
+			((ops->oobbuf == NULL) || (ops->ooblen == 0))) {
+		pr_err("%s: incorrect ops fields - nothing to do\n",
+				__func__);
+		return -EINVAL;
+	}
+
+	if ((ops->datbuf != NULL) && (ops->len == 0)) {
+		pr_err("%s: data buffer passed but length 0\n",
+				__func__);
+		return -EINVAL;
+	}
+
+	if ((ops->oobbuf != NULL) && (ops->ooblen == 0)) {
+		pr_err("%s: oob buffer passed but length 0\n",
+				__func__);
+		return -EINVAL;
+	}
+
+	if (ops->mode != MTD_OPS_RAW) {
+		if (ops->datbuf != NULL && (ops->len % mtd->writesize) != 0) {
+			/* when ops->datbuf is NULL, ops->len can be ooblen */
+			pr_err("%s: unsupported ops->len, %d\n", __func__,
+					ops->len);
+			return -EINVAL;
+		}
+	} else {
+		if (ops->datbuf != NULL &&
+			(ops->len % (mtd->writesize + mtd->oobsize)) != 0) {
+			pr_err("%s: unsupported ops->len,"
+				" %d for MTD_OPS_RAW\n", __func__, ops->len);
+			return -EINVAL;
+		}
+	}
+
+	if ((ops->mode == MTD_OPS_RAW) && (ops->oobbuf)) {
+		pr_err("%s: unsupported operation, oobbuf pointer "
+				"passed in for RAW mode, %x\n", __func__,
+				(uint32_t)ops->oobbuf);
+		return -EINVAL;
+	}
+
+	if (ops->oobbuf && !ops->datbuf) {
+		page_count = ops->ooblen / ((ops->mode == MTD_OPS_AUTO_OOB) ?
+			mtd->oobavail : mtd->oobsize);
+		if ((page_count == 0) && (ops->ooblen))
+			page_count = 1;
+	} else if (ops->mode != MTD_OPS_RAW)
+			page_count = ops->len / mtd->writesize;
+		else
+			page_count = ops->len / (mtd->writesize + mtd->oobsize);
+
+	if ((ops->mode == MTD_OPS_PLACE_OOB) && (ops->oobbuf != NULL)) {
+		if (page_count * mtd->oobsize > ops->ooblen) {
+			pr_err("%s: unsupported ops->ooblen for "
+				"PLACE, %d\n", __func__, ops->ooblen);
+			return -EINVAL;
+		}
+	}
+
+	if ((ops->mode == MTD_OPS_PLACE_OOB) && (ops->ooblen != 0) &&
+							(ops->ooboffs != 0)) {
+		pr_err("%s: unsupported ops->ooboffs, %d\n", __func__,
+				ops->ooboffs);
+		return -EINVAL;
+	}
+
+	if (ops->datbuf) {
+		memset(ops->datbuf, 0x55, ops->len);
+		data_dma_addr_curr = data_dma_addr = msm_nand_dma_map(chip->dev,
+				ops->datbuf, ops->len, DMA_FROM_DEVICE);
+		if (dma_mapping_error(chip->dev, data_dma_addr)) {
+			pr_err("%s: failed to get dma addr for %p\n",
+					__func__, ops->datbuf);
+			return -EIO;
+		}
+	}
+	if (ops->oobbuf) {
+		memset(ops->oobbuf, 0x55, ops->ooblen);
+		oob_dma_addr_curr = oob_dma_addr = msm_nand_dma_map(chip->dev,
+				ops->oobbuf, ops->ooblen, DMA_FROM_DEVICE);
+		if (dma_mapping_error(chip->dev, oob_dma_addr)) {
+			pr_err("%s: failed to get dma addr for %p\n",
+					__func__, ops->oobbuf);
+			err = -EIO;
+			goto err_dma_map_oobbuf_failed;
+		}
+	}
+
+	wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer
+				(chip, sizeof(*dma_buffer))));
+
+	from_curr = from;
+
+	while (page_count-- > 0) {
+
+		cmd = dma_buffer->cmd;
+
+		if ((onenand_info.device_id & ONENAND_DEVICE_IS_DDP)
+			&& (from_curr >= (mtd->size>>1))) { /* DDP Device */
+				onenand_startaddr1 = DEVICE_FLASHCORE_1 |
+					(((uint32_t)(from_curr-(mtd->size>>1))
+					/ mtd->erasesize));
+				onenand_startaddr2 = DEVICE_BUFFERRAM_1;
+		} else {
+				onenand_startaddr1 = DEVICE_FLASHCORE_0 |
+				((uint32_t)from_curr / mtd->erasesize) ;
+				onenand_startaddr2 = DEVICE_BUFFERRAM_0;
+		}
+
+		onenand_startaddr8 = (((uint32_t)from_curr &
+				(mtd->erasesize - 1)) / mtd->writesize) << 2;
+		onenand_startbuffer = DATARAM0_0 << 8;
+		onenand_sysconfig1 = (ops->mode == MTD_OPS_RAW) ?
+			ONENAND_SYSCFG1_ECCDIS(nand_sfcmd_mode) :
+			ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode);
+
+		dma_buffer->data.sfbcfg = SFLASH_BCFG |
+					(nand_sfcmd_mode ? 0 : (1 << 24));
+		dma_buffer->data.sfcmd[0] =  SFLASH_PREPCMD(7, 0, 0,
+							MSM_NAND_SFCMD_CMDXS,
+							nand_sfcmd_mode,
+							MSM_NAND_SFCMD_REGWR);
+		dma_buffer->data.sfcmd[1] =  SFLASH_PREPCMD(0, 0, 32,
+							MSM_NAND_SFCMD_CMDXS,
+							nand_sfcmd_mode,
+							MSM_NAND_SFCMD_INTHI);
+		dma_buffer->data.sfcmd[2] =  SFLASH_PREPCMD(3, 7, 0,
+							MSM_NAND_SFCMD_DATXS,
+							nand_sfcmd_mode,
+							MSM_NAND_SFCMD_REGRD);
+		dma_buffer->data.sfcmd[3] =  SFLASH_PREPCMD(256, 0, 0,
+							MSM_NAND_SFCMD_DATXS,
+							nand_sfcmd_mode,
+							MSM_NAND_SFCMD_DATRD);
+		dma_buffer->data.sfcmd[4] =  SFLASH_PREPCMD(256, 0, 0,
+							MSM_NAND_SFCMD_DATXS,
+							nand_sfcmd_mode,
+							MSM_NAND_SFCMD_DATRD);
+		dma_buffer->data.sfcmd[5] =  SFLASH_PREPCMD(256, 0, 0,
+							MSM_NAND_SFCMD_DATXS,
+							nand_sfcmd_mode,
+							MSM_NAND_SFCMD_DATRD);
+		dma_buffer->data.sfcmd[6] =  SFLASH_PREPCMD(256, 0, 0,
+							MSM_NAND_SFCMD_DATXS,
+							nand_sfcmd_mode,
+							MSM_NAND_SFCMD_DATRD);
+		dma_buffer->data.sfcmd[7] =  SFLASH_PREPCMD(32, 0, 0,
+							MSM_NAND_SFCMD_DATXS,
+							nand_sfcmd_mode,
+							MSM_NAND_SFCMD_DATRD);
+		dma_buffer->data.sfcmd[8] =  SFLASH_PREPCMD(4, 10, 0,
+							MSM_NAND_SFCMD_CMDXS,
+							nand_sfcmd_mode,
+							MSM_NAND_SFCMD_REGWR);
+		dma_buffer->data.sfexec = 1;
+		dma_buffer->data.sfstat[0] = CLEAN_DATA_32;
+		dma_buffer->data.sfstat[1] = CLEAN_DATA_32;
+		dma_buffer->data.sfstat[2] = CLEAN_DATA_32;
+		dma_buffer->data.sfstat[3] = CLEAN_DATA_32;
+		dma_buffer->data.sfstat[4] = CLEAN_DATA_32;
+		dma_buffer->data.sfstat[5] = CLEAN_DATA_32;
+		dma_buffer->data.sfstat[6] = CLEAN_DATA_32;
+		dma_buffer->data.sfstat[7] = CLEAN_DATA_32;
+		dma_buffer->data.sfstat[8] = CLEAN_DATA_32;
+		dma_buffer->data.addr0 = (ONENAND_INTERRUPT_STATUS << 16) |
+						(ONENAND_SYSTEM_CONFIG_1);
+		dma_buffer->data.addr1 = (ONENAND_START_ADDRESS_8 << 16) |
+						(ONENAND_START_ADDRESS_1);
+		dma_buffer->data.addr2 = (ONENAND_START_BUFFER << 16) |
+						(ONENAND_START_ADDRESS_2);
+		dma_buffer->data.addr3 = (ONENAND_ECC_STATUS << 16) |
+						(ONENAND_COMMAND);
+		dma_buffer->data.addr4 = (ONENAND_CONTROLLER_STATUS << 16) |
+						(ONENAND_INTERRUPT_STATUS);
+		dma_buffer->data.addr5 = (ONENAND_INTERRUPT_STATUS << 16) |
+						(ONENAND_SYSTEM_CONFIG_1);
+		dma_buffer->data.addr6 = (ONENAND_START_ADDRESS_3 << 16) |
+						(ONENAND_START_ADDRESS_1);
+		dma_buffer->data.data0 = (ONENAND_CLRINTR << 16) |
+						(onenand_sysconfig1);
+		dma_buffer->data.data1 = (onenand_startaddr8 << 16) |
+						(onenand_startaddr1);
+		dma_buffer->data.data2 = (onenand_startbuffer << 16) |
+						(onenand_startaddr2);
+		dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) |
+						(ONENAND_CMDLOADSPARE);
+		dma_buffer->data.data4 = (CLEAN_DATA_16 << 16) |
+						(CLEAN_DATA_16);
+		dma_buffer->data.data5 = (ONENAND_CLRINTR << 16) |
+				(ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode));
+		dma_buffer->data.data6 = (ONENAND_STARTADDR3_RES << 16) |
+						(ONENAND_STARTADDR1_RES);
+		dma_buffer->data.macro[0] = 0x0200;
+		dma_buffer->data.macro[1] = 0x0300;
+		dma_buffer->data.macro[2] = 0x0400;
+		dma_buffer->data.macro[3] = 0x0500;
+		dma_buffer->data.macro[4] = 0x8010;
+
+		/*************************************************************/
+		/* Write necessary address registers in the onenand device   */
+		/*************************************************************/
+
+		/* Enable and configure the SFlash controller */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfbcfg);
+		cmd->dst = MSM_NAND_SFLASHC_BURST_CFG;
+		cmd->len = 4;
+		cmd++;
+
+		/* Block on cmd ready and write CMD register */
+		cmd->cmd = DST_CRCI_NAND_CMD;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[0]);
+		cmd->dst = MSM_NAND_SFLASHC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Write the ADDR0 and ADDR1 registers */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr0);
+		cmd->dst = MSM_NAND_ADDR0;
+		cmd->len = 8;
+		cmd++;
+
+		/* Write the ADDR2 ADDR3 ADDR4 ADDR5 registers */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr2);
+		cmd->dst = MSM_NAND_ADDR2;
+		cmd->len = 16;
+		cmd++;
+
+		/* Write the ADDR6 registers */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr6);
+		cmd->dst = MSM_NAND_ADDR6;
+		cmd->len = 4;
+		cmd++;
+
+		/* Write the GENP0, GENP1, GENP2, GENP3 registers */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data0);
+		cmd->dst = MSM_NAND_GENP_REG0;
+		cmd->len = 16;
+		cmd++;
+
+		/* Write the FLASH_DEV_CMD4,5,6 registers */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data4);
+		cmd->dst = MSM_NAND_DEV_CMD4;
+		cmd->len = 12;
+		cmd++;
+
+		/* Kick the execute command */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
+		cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Block on data ready, and read the status register */
+		cmd->cmd = SRC_CRCI_NAND_DATA;
+		cmd->src = MSM_NAND_SFLASHC_STATUS;
+		cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[0]);
+		cmd->len = 4;
+		cmd++;
+
+		/*************************************************************/
+		/* Wait for the interrupt from the Onenand device controller */
+		/*************************************************************/
+
+		/* Block on cmd ready and write CMD register */
+		cmd->cmd = DST_CRCI_NAND_CMD;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[1]);
+		cmd->dst = MSM_NAND_SFLASHC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Kick the execute command */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
+		cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Block on data ready, and read the status register */
+		cmd->cmd = SRC_CRCI_NAND_DATA;
+		cmd->src = MSM_NAND_SFLASHC_STATUS;
+		cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[1]);
+		cmd->len = 4;
+		cmd++;
+
+		/*************************************************************/
+		/* Read necessary status registers from the onenand device   */
+		/*************************************************************/
+
+		/* Block on cmd ready and write CMD register */
+		cmd->cmd = DST_CRCI_NAND_CMD;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[2]);
+		cmd->dst = MSM_NAND_SFLASHC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Kick the execute command */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
+		cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Block on data ready, and read the status register */
+		cmd->cmd = SRC_CRCI_NAND_DATA;
+		cmd->src = MSM_NAND_SFLASHC_STATUS;
+		cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[2]);
+		cmd->len = 4;
+		cmd++;
+
+		/* Read the GENP3 register */
+		cmd->cmd = 0;
+		cmd->src = MSM_NAND_GENP_REG3;
+		cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data3);
+		cmd->len = 4;
+		cmd++;
+
+		/* Read the DEVCMD4 register */
+		cmd->cmd = 0;
+		cmd->src = MSM_NAND_DEV_CMD4;
+		cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data4);
+		cmd->len = 4;
+		cmd++;
+
+		/*************************************************************/
+		/* Read the data ram area from the onenand buffer ram        */
+		/*************************************************************/
+
+		if (ops->datbuf) {
+
+			dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) |
+							(ONENAND_CMDLOAD);
+
+			for (i = 0; i < 4; i++) {
+
+				/* Block on cmd ready and write CMD register */
+				cmd->cmd = DST_CRCI_NAND_CMD;
+				cmd->src = msm_virt_to_dma(chip,
+						&dma_buffer->data.sfcmd[3+i]);
+				cmd->dst = MSM_NAND_SFLASHC_CMD;
+				cmd->len = 4;
+				cmd++;
+
+				/* Write the MACRO1 register */
+				cmd->cmd = 0;
+				cmd->src = msm_virt_to_dma(chip,
+						&dma_buffer->data.macro[i]);
+				cmd->dst = MSM_NAND_MACRO1_REG;
+				cmd->len = 4;
+				cmd++;
+
+				/* Kick the execute command */
+				cmd->cmd = 0;
+				cmd->src = msm_virt_to_dma(chip,
+						&dma_buffer->data.sfexec);
+				cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
+				cmd->len = 4;
+				cmd++;
+
+				/* Block on data rdy, & read status register */
+				cmd->cmd = SRC_CRCI_NAND_DATA;
+				cmd->src = MSM_NAND_SFLASHC_STATUS;
+				cmd->dst = msm_virt_to_dma(chip,
+						&dma_buffer->data.sfstat[3+i]);
+				cmd->len = 4;
+				cmd++;
+
+				/* Transfer nand ctlr buf contents to usr buf */
+				cmd->cmd = 0;
+				cmd->src = MSM_NAND_FLASH_BUFFER;
+				cmd->dst = data_dma_addr_curr;
+				cmd->len = 512;
+				data_dma_addr_curr += 512;
+				cmd++;
+			}
+		}
+
+		if ((ops->oobbuf) || (ops->mode == MTD_OPS_RAW)) {
+
+			/* Block on cmd ready and write CMD register */
+			cmd->cmd = DST_CRCI_NAND_CMD;
+			cmd->src = msm_virt_to_dma(chip,
+					&dma_buffer->data.sfcmd[7]);
+			cmd->dst = MSM_NAND_SFLASHC_CMD;
+			cmd->len = 4;
+			cmd++;
+
+			/* Write the MACRO1 register */
+			cmd->cmd = 0;
+			cmd->src = msm_virt_to_dma(chip,
+					&dma_buffer->data.macro[4]);
+			cmd->dst = MSM_NAND_MACRO1_REG;
+			cmd->len = 4;
+			cmd++;
+
+			/* Kick the execute command */
+			cmd->cmd = 0;
+			cmd->src = msm_virt_to_dma(chip,
+					&dma_buffer->data.sfexec);
+			cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
+			cmd->len = 4;
+			cmd++;
+
+			/* Block on data ready, and read status register */
+			cmd->cmd = SRC_CRCI_NAND_DATA;
+			cmd->src = MSM_NAND_SFLASHC_STATUS;
+			cmd->dst = msm_virt_to_dma(chip,
+					&dma_buffer->data.sfstat[7]);
+			cmd->len = 4;
+			cmd++;
+
+			/* Transfer nand ctlr buffer contents into usr buf */
+			if (ops->mode == MTD_OPS_AUTO_OOB) {
+				for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES; i++) {
+					cmd->cmd = 0;
+					cmd->src = MSM_NAND_FLASH_BUFFER +
+					mtd->ecclayout->oobfree[i].offset;
+					cmd->dst = oob_dma_addr_curr;
+					cmd->len =
+					mtd->ecclayout->oobfree[i].length;
+					oob_dma_addr_curr +=
+					mtd->ecclayout->oobfree[i].length;
+					cmd++;
+				}
+			}
+			if (ops->mode == MTD_OPS_PLACE_OOB) {
+					cmd->cmd = 0;
+					cmd->src = MSM_NAND_FLASH_BUFFER;
+					cmd->dst = oob_dma_addr_curr;
+					cmd->len = mtd->oobsize;
+					oob_dma_addr_curr += mtd->oobsize;
+					cmd++;
+			}
+			if (ops->mode == MTD_OPS_RAW) {
+					cmd->cmd = 0;
+					cmd->src = MSM_NAND_FLASH_BUFFER;
+					cmd->dst = data_dma_addr_curr;
+					cmd->len = mtd->oobsize;
+					data_dma_addr_curr += mtd->oobsize;
+					cmd++;
+			}
+		}
+
+		/*************************************************************/
+		/* Restore the necessary registers to proper values          */
+		/*************************************************************/
+
+		/* Block on cmd ready and write CMD register */
+		cmd->cmd = DST_CRCI_NAND_CMD;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[8]);
+		cmd->dst = MSM_NAND_SFLASHC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Kick the execute command */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
+		cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Block on data ready, and read the status register */
+		cmd->cmd = SRC_CRCI_NAND_DATA;
+		cmd->src = MSM_NAND_SFLASHC_STATUS;
+		cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[8]);
+		cmd->len = 4;
+		cmd++;
+
+
+		BUILD_BUG_ON(53 != ARRAY_SIZE(dma_buffer->cmd));
+		BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
+		dma_buffer->cmd[0].cmd |= CMD_OCB;
+		cmd[-1].cmd |= CMD_OCU | CMD_LC;
+
+		dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd)
+				>> 3) | CMD_PTR_LP;
+
+		mb();
+		msm_dmov_exec_cmd(chip->dma_channel,
+			DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip,
+				&dma_buffer->cmdptr)));
+		mb();
+
+		ecc_status = (dma_buffer->data.data3 >> 16) &
+							0x0000FFFF;
+		interrupt_status = (dma_buffer->data.data4 >> 0) &
+							0x0000FFFF;
+		controller_status = (dma_buffer->data.data4 >> 16) &
+							0x0000FFFF;
+
+#if VERBOSE
+		pr_info("\n%s: sflash status %x %x %x %x %x %x %x"
+				"%x %x\n", __func__,
+					dma_buffer->data.sfstat[0],
+					dma_buffer->data.sfstat[1],
+					dma_buffer->data.sfstat[2],
+					dma_buffer->data.sfstat[3],
+					dma_buffer->data.sfstat[4],
+					dma_buffer->data.sfstat[5],
+					dma_buffer->data.sfstat[6],
+					dma_buffer->data.sfstat[7],
+					dma_buffer->data.sfstat[8]);
+
+		pr_info("%s: controller_status = %x\n", __func__,
+					controller_status);
+		pr_info("%s: interrupt_status = %x\n", __func__,
+					interrupt_status);
+		pr_info("%s: ecc_status = %x\n", __func__,
+					ecc_status);
+#endif
+		/* Check for errors, protection violations etc */
+		if ((controller_status != 0)
+				|| (dma_buffer->data.sfstat[0] & 0x110)
+				|| (dma_buffer->data.sfstat[1] & 0x110)
+				|| (dma_buffer->data.sfstat[2] & 0x110)
+				|| (dma_buffer->data.sfstat[8] & 0x110)
+				|| ((dma_buffer->data.sfstat[3] & 0x110) &&
+								(ops->datbuf))
+				|| ((dma_buffer->data.sfstat[4] & 0x110) &&
+								(ops->datbuf))
+				|| ((dma_buffer->data.sfstat[5] & 0x110) &&
+								(ops->datbuf))
+				|| ((dma_buffer->data.sfstat[6] & 0x110) &&
+								(ops->datbuf))
+				|| ((dma_buffer->data.sfstat[7] & 0x110) &&
+								((ops->oobbuf)
+					|| (ops->mode == MTD_OPS_RAW)))) {
+			pr_info("%s: ECC/MPU/OP error\n", __func__);
+			err = -EIO;
+		}
+
+		if (err)
+			break;
+		pages_read++;
+		from_curr += mtd->writesize;
+	}
+
+	msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+
+	if (ops->oobbuf) {
+		dma_unmap_page(chip->dev, oob_dma_addr, ops->ooblen,
+				DMA_FROM_DEVICE);
+	}
+err_dma_map_oobbuf_failed:
+	if (ops->datbuf) {
+		dma_unmap_page(chip->dev, data_dma_addr, ops->len,
+				DMA_FROM_DEVICE);
+	}
+
+	if (err) {
+		pr_err("%s: %llx %x %x failed\n", __func__, from_curr,
+				ops->datbuf ? ops->len : 0, ops->ooblen);
+	} else {
+		ops->retlen = ops->oobretlen = 0;
+		if (ops->datbuf != NULL) {
+			if (ops->mode != MTD_OPS_RAW)
+				ops->retlen = mtd->writesize * pages_read;
+			else
+				ops->retlen = (mtd->writesize +  mtd->oobsize)
+							* pages_read;
+		}
+		if (ops->oobbuf != NULL) {
+			if (ops->mode == MTD_OPS_AUTO_OOB)
+				ops->oobretlen = mtd->oobavail * pages_read;
+			else
+				ops->oobretlen = mtd->oobsize * pages_read;
+		}
+	}
+
+#if VERBOSE
+	pr_info("\n%s: ret %d, retlen %d oobretlen %d\n",
+			__func__, err, ops->retlen, ops->oobretlen);
+
+	pr_info("==================================================="
+			"==============\n");
+#endif
+	return err;
+}
+
+int msm_onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
+		size_t *retlen, u_char *buf)
+{
+	int ret;
+	struct mtd_oob_ops ops;
+
+	ops.mode = MTD_OPS_PLACE_OOB;
+	ops.datbuf = buf;
+	ops.len = len;
+	ops.retlen = 0;
+	ops.oobbuf = NULL;
+	ops.ooblen = 0;
+	ops.oobretlen = 0;
+	ret =  msm_onenand_read_oob(mtd, from, &ops);
+	*retlen = ops.retlen;
+
+	return ret;
+}
+
+static int msm_onenand_write_oob(struct mtd_info *mtd, loff_t to,
+		struct mtd_oob_ops *ops)
+{
+	struct msm_nand_chip *chip = mtd->priv;
+
+	struct {
+		dmov_s cmd[53];
+		unsigned cmdptr;
+		struct {
+			uint32_t sfbcfg;
+			uint32_t sfcmd[10];
+			uint32_t sfexec;
+			uint32_t sfstat[10];
+			uint32_t addr0;
+			uint32_t addr1;
+			uint32_t addr2;
+			uint32_t addr3;
+			uint32_t addr4;
+			uint32_t addr5;
+			uint32_t addr6;
+			uint32_t data0;
+			uint32_t data1;
+			uint32_t data2;
+			uint32_t data3;
+			uint32_t data4;
+			uint32_t data5;
+			uint32_t data6;
+			uint32_t macro[5];
+		} data;
+	} *dma_buffer;
+	dmov_s *cmd;
+
+	int err = 0;
+	int i, j, k;
+	dma_addr_t data_dma_addr = 0;
+	dma_addr_t oob_dma_addr = 0;
+	dma_addr_t init_dma_addr = 0;
+	dma_addr_t data_dma_addr_curr = 0;
+	dma_addr_t oob_dma_addr_curr = 0;
+	uint8_t *init_spare_bytes;
+
+	loff_t to_curr = 0;
+	unsigned page_count;
+	unsigned pages_written = 0;
+
+	uint16_t onenand_startaddr1;
+	uint16_t onenand_startaddr8;
+	uint16_t onenand_startaddr2;
+	uint16_t onenand_startbuffer;
+	uint16_t onenand_sysconfig1;
+
+	uint16_t controller_status;
+	uint16_t interrupt_status;
+	uint16_t ecc_status;
+
+#if VERBOSE
+	pr_info("================================================="
+			"================\n");
+	pr_info("%s: to 0x%llx mode %d \ndatbuf 0x%p datlen 0x%x"
+			"\noobbuf 0x%p ooblen 0x%x\n",
+			__func__, to, ops->mode, ops->datbuf, ops->len,
+			ops->oobbuf, ops->ooblen);
+#endif
+	if (!mtd) {
+		pr_err("%s: invalid mtd pointer, 0x%x\n", __func__,
+				(uint32_t)mtd);
+		return -EINVAL;
+	}
+	if (to & (mtd->writesize - 1)) {
+		pr_err("%s: unsupported to, 0x%llx\n", __func__, to);
+		return -EINVAL;
+	}
+
+	if ((ops->mode != MTD_OPS_PLACE_OOB) && (ops->mode != MTD_OPS_AUTO_OOB) &&
+			(ops->mode != MTD_OPS_RAW)) {
+		pr_err("%s: unsupported ops->mode, %d\n", __func__,
+				ops->mode);
+		return -EINVAL;
+	}
+
+	if (((ops->datbuf == NULL) || (ops->len == 0)) &&
+			((ops->oobbuf == NULL) || (ops->ooblen == 0))) {
+		pr_err("%s: incorrect ops fields - nothing to do\n",
+				__func__);
+		return -EINVAL;
+	}
+
+	if ((ops->datbuf != NULL) && (ops->len == 0)) {
+		pr_err("%s: data buffer passed but length 0\n",
+				__func__);
+		return -EINVAL;
+	}
+
+	if ((ops->oobbuf != NULL) && (ops->ooblen == 0)) {
+		pr_err("%s: oob buffer passed but length 0\n",
+				__func__);
+		return -EINVAL;
+	}
+
+	if (ops->mode != MTD_OPS_RAW) {
+		if (ops->datbuf != NULL && (ops->len % mtd->writesize) != 0) {
+			/* when ops->datbuf is NULL, ops->len can be ooblen */
+			pr_err("%s: unsupported ops->len, %d\n", __func__,
+					ops->len);
+			return -EINVAL;
+		}
+	} else {
+		if (ops->datbuf != NULL &&
+			(ops->len % (mtd->writesize + mtd->oobsize)) != 0) {
+			pr_err("%s: unsupported ops->len,"
+				" %d for MTD_OPS_RAW\n", __func__, ops->len);
+			return -EINVAL;
+		}
+	}
+
+	if ((ops->mode == MTD_OPS_RAW) && (ops->oobbuf)) {
+		pr_err("%s: unsupported operation, oobbuf pointer "
+				"passed in for RAW mode, %x\n", __func__,
+				(uint32_t)ops->oobbuf);
+		return -EINVAL;
+	}
+
+	if (ops->oobbuf && !ops->datbuf) {
+		page_count = ops->ooblen / ((ops->mode == MTD_OPS_AUTO_OOB) ?
+			mtd->oobavail : mtd->oobsize);
+		if ((page_count == 0) && (ops->ooblen))
+			page_count = 1;
+	} else if (ops->mode != MTD_OPS_RAW)
+			page_count = ops->len / mtd->writesize;
+		else
+			page_count = ops->len / (mtd->writesize + mtd->oobsize);
+
+	if ((ops->mode == MTD_OPS_AUTO_OOB) && (ops->oobbuf != NULL)) {
+		if (page_count > 1) {
+			pr_err("%s: unsupported ops->ooblen for"
+				"AUTO, %d\n", __func__, ops->ooblen);
+			return -EINVAL;
+		}
+	}
+
+	if ((ops->mode == MTD_OPS_PLACE_OOB) && (ops->oobbuf != NULL)) {
+		if (page_count * mtd->oobsize > ops->ooblen) {
+			pr_err("%s: unsupported ops->ooblen for"
+				"PLACE,	%d\n", __func__, ops->ooblen);
+			return -EINVAL;
+		}
+	}
+
+	if ((ops->mode == MTD_OPS_PLACE_OOB) && (ops->ooblen != 0) &&
+						(ops->ooboffs != 0)) {
+		pr_err("%s: unsupported ops->ooboffs, %d\n",
+				__func__, ops->ooboffs);
+		return -EINVAL;
+	}
+
+	init_spare_bytes = kmalloc(64, GFP_KERNEL);
+	if (!init_spare_bytes) {
+		pr_err("%s: failed to alloc init_spare_bytes buffer\n",
+				__func__);
+		return -ENOMEM;
+	}
+	for (i = 0; i < 64; i++)
+		init_spare_bytes[i] = 0xFF;
+
+	if ((ops->oobbuf) && (ops->mode == MTD_OPS_AUTO_OOB)) {
+		for (i = 0, k = 0; i < MTD_MAX_OOBFREE_ENTRIES; i++)
+			for (j = 0; j < mtd->ecclayout->oobfree[i].length;
+					j++) {
+				init_spare_bytes[j +
+					mtd->ecclayout->oobfree[i].offset]
+						= (ops->oobbuf)[k];
+				k++;
+			}
+	}
+
+	if (ops->datbuf) {
+		data_dma_addr_curr = data_dma_addr = msm_nand_dma_map(chip->dev,
+				ops->datbuf, ops->len, DMA_TO_DEVICE);
+		if (dma_mapping_error(chip->dev, data_dma_addr)) {
+			pr_err("%s: failed to get dma addr for %p\n",
+					__func__, ops->datbuf);
+			return -EIO;
+		}
+	}
+	if (ops->oobbuf) {
+		oob_dma_addr_curr = oob_dma_addr = msm_nand_dma_map(chip->dev,
+				ops->oobbuf, ops->ooblen, DMA_TO_DEVICE);
+		if (dma_mapping_error(chip->dev, oob_dma_addr)) {
+			pr_err("%s: failed to get dma addr for %p\n",
+					__func__, ops->oobbuf);
+			err = -EIO;
+			goto err_dma_map_oobbuf_failed;
+		}
+	}
+
+	init_dma_addr = msm_nand_dma_map(chip->dev, init_spare_bytes, 64,
+			DMA_TO_DEVICE);
+	if (dma_mapping_error(chip->dev, init_dma_addr)) {
+		pr_err("%s: failed to get dma addr for %p\n",
+				__func__, init_spare_bytes);
+		err = -EIO;
+		goto err_dma_map_initbuf_failed;
+	}
+
+
+	wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer
+				(chip, sizeof(*dma_buffer))));
+
+	to_curr = to;
+
+	while (page_count-- > 0) {
+		cmd = dma_buffer->cmd;
+
+		if ((onenand_info.device_id & ONENAND_DEVICE_IS_DDP)
+			&& (to_curr >= (mtd->size>>1))) { /* DDP Device */
+				onenand_startaddr1 = DEVICE_FLASHCORE_1 |
+					(((uint32_t)(to_curr-(mtd->size>>1))
+					/ mtd->erasesize));
+				onenand_startaddr2 = DEVICE_BUFFERRAM_1;
+		} else {
+				onenand_startaddr1 = DEVICE_FLASHCORE_0 |
+					((uint32_t)to_curr / mtd->erasesize) ;
+				onenand_startaddr2 = DEVICE_BUFFERRAM_0;
+		}
+
+		onenand_startaddr8 = (((uint32_t)to_curr &
+				(mtd->erasesize - 1)) / mtd->writesize) << 2;
+		onenand_startbuffer = DATARAM0_0 << 8;
+		onenand_sysconfig1 = (ops->mode == MTD_OPS_RAW) ?
+			ONENAND_SYSCFG1_ECCDIS(nand_sfcmd_mode) :
+			ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode);
+
+		dma_buffer->data.sfbcfg = SFLASH_BCFG |
+					(nand_sfcmd_mode ? 0 : (1 << 24));
+		dma_buffer->data.sfcmd[0] =  SFLASH_PREPCMD(6, 0, 0,
+							MSM_NAND_SFCMD_CMDXS,
+							nand_sfcmd_mode,
+							MSM_NAND_SFCMD_REGWR);
+		dma_buffer->data.sfcmd[1] =  SFLASH_PREPCMD(256, 0, 0,
+							MSM_NAND_SFCMD_CMDXS,
+							nand_sfcmd_mode,
+							MSM_NAND_SFCMD_DATWR);
+		dma_buffer->data.sfcmd[2] =  SFLASH_PREPCMD(256, 0, 0,
+							MSM_NAND_SFCMD_CMDXS,
+							nand_sfcmd_mode,
+							MSM_NAND_SFCMD_DATWR);
+		dma_buffer->data.sfcmd[3] =  SFLASH_PREPCMD(256, 0, 0,
+							MSM_NAND_SFCMD_CMDXS,
+							nand_sfcmd_mode,
+							MSM_NAND_SFCMD_DATWR);
+		dma_buffer->data.sfcmd[4] =  SFLASH_PREPCMD(256, 0, 0,
+							MSM_NAND_SFCMD_CMDXS,
+							nand_sfcmd_mode,
+							MSM_NAND_SFCMD_DATWR);
+		dma_buffer->data.sfcmd[5] =  SFLASH_PREPCMD(32, 0, 0,
+							MSM_NAND_SFCMD_CMDXS,
+							nand_sfcmd_mode,
+							MSM_NAND_SFCMD_DATWR);
+		dma_buffer->data.sfcmd[6] =  SFLASH_PREPCMD(1, 6, 0,
+							MSM_NAND_SFCMD_CMDXS,
+							nand_sfcmd_mode,
+							MSM_NAND_SFCMD_REGWR);
+		dma_buffer->data.sfcmd[7] =  SFLASH_PREPCMD(0, 0, 32,
+							MSM_NAND_SFCMD_CMDXS,
+							nand_sfcmd_mode,
+							MSM_NAND_SFCMD_INTHI);
+		dma_buffer->data.sfcmd[8] =  SFLASH_PREPCMD(3, 7, 0,
+							MSM_NAND_SFCMD_DATXS,
+							nand_sfcmd_mode,
+							MSM_NAND_SFCMD_REGRD);
+		dma_buffer->data.sfcmd[9] =  SFLASH_PREPCMD(4, 10, 0,
+							MSM_NAND_SFCMD_CMDXS,
+							nand_sfcmd_mode,
+							MSM_NAND_SFCMD_REGWR);
+		dma_buffer->data.sfexec = 1;
+		dma_buffer->data.sfstat[0] = CLEAN_DATA_32;
+		dma_buffer->data.sfstat[1] = CLEAN_DATA_32;
+		dma_buffer->data.sfstat[2] = CLEAN_DATA_32;
+		dma_buffer->data.sfstat[3] = CLEAN_DATA_32;
+		dma_buffer->data.sfstat[4] = CLEAN_DATA_32;
+		dma_buffer->data.sfstat[5] = CLEAN_DATA_32;
+		dma_buffer->data.sfstat[6] = CLEAN_DATA_32;
+		dma_buffer->data.sfstat[7] = CLEAN_DATA_32;
+		dma_buffer->data.sfstat[8] = CLEAN_DATA_32;
+		dma_buffer->data.sfstat[9] = CLEAN_DATA_32;
+		dma_buffer->data.addr0 = (ONENAND_INTERRUPT_STATUS << 16) |
+						(ONENAND_SYSTEM_CONFIG_1);
+		dma_buffer->data.addr1 = (ONENAND_START_ADDRESS_8 << 16) |
+						(ONENAND_START_ADDRESS_1);
+		dma_buffer->data.addr2 = (ONENAND_START_BUFFER << 16) |
+						(ONENAND_START_ADDRESS_2);
+		dma_buffer->data.addr3 = (ONENAND_ECC_STATUS << 16) |
+						(ONENAND_COMMAND);
+		dma_buffer->data.addr4 = (ONENAND_CONTROLLER_STATUS << 16) |
+						(ONENAND_INTERRUPT_STATUS);
+		dma_buffer->data.addr5 = (ONENAND_INTERRUPT_STATUS << 16) |
+						(ONENAND_SYSTEM_CONFIG_1);
+		dma_buffer->data.addr6 = (ONENAND_START_ADDRESS_3 << 16) |
+						(ONENAND_START_ADDRESS_1);
+		dma_buffer->data.data0 = (ONENAND_CLRINTR << 16) |
+						(onenand_sysconfig1);
+		dma_buffer->data.data1 = (onenand_startaddr8 << 16) |
+						(onenand_startaddr1);
+		dma_buffer->data.data2 = (onenand_startbuffer << 16) |
+						(onenand_startaddr2);
+		dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) |
+						(ONENAND_CMDPROGSPARE);
+		dma_buffer->data.data4 = (CLEAN_DATA_16 << 16) |
+						(CLEAN_DATA_16);
+		dma_buffer->data.data5 = (ONENAND_CLRINTR << 16) |
+				(ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode));
+		dma_buffer->data.data6 = (ONENAND_STARTADDR3_RES << 16) |
+						(ONENAND_STARTADDR1_RES);
+		dma_buffer->data.macro[0] = 0x0200;
+		dma_buffer->data.macro[1] = 0x0300;
+		dma_buffer->data.macro[2] = 0x0400;
+		dma_buffer->data.macro[3] = 0x0500;
+		dma_buffer->data.macro[4] = 0x8010;
+
+
+		/*************************************************************/
+		/* Write necessary address registers in the onenand device   */
+		/*************************************************************/
+
+		/* Enable and configure the SFlash controller */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfbcfg);
+		cmd->dst = MSM_NAND_SFLASHC_BURST_CFG;
+		cmd->len = 4;
+		cmd++;
+
+		/* Block on cmd ready and write CMD register */
+		cmd->cmd = DST_CRCI_NAND_CMD;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[0]);
+		cmd->dst = MSM_NAND_SFLASHC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Write the ADDR0 and ADDR1 registers */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr0);
+		cmd->dst = MSM_NAND_ADDR0;
+		cmd->len = 8;
+		cmd++;
+
+		/* Write the ADDR2 ADDR3 ADDR4 ADDR5 registers */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr2);
+		cmd->dst = MSM_NAND_ADDR2;
+		cmd->len = 16;
+		cmd++;
+
+		/* Write the ADDR6 registers */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr6);
+		cmd->dst = MSM_NAND_ADDR6;
+		cmd->len = 4;
+		cmd++;
+
+		/* Write the GENP0, GENP1, GENP2, GENP3 registers */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data0);
+		cmd->dst = MSM_NAND_GENP_REG0;
+		cmd->len = 16;
+		cmd++;
+
+		/* Write the FLASH_DEV_CMD4,5,6 registers */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data4);
+		cmd->dst = MSM_NAND_DEV_CMD4;
+		cmd->len = 12;
+		cmd++;
+
+		/* Kick the execute command */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
+		cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Block on data ready, and read the status register */
+		cmd->cmd = SRC_CRCI_NAND_DATA;
+		cmd->src = MSM_NAND_SFLASHC_STATUS;
+		cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[0]);
+		cmd->len = 4;
+		cmd++;
+
+		/*************************************************************/
+		/* Write the data ram area in the onenand buffer ram         */
+		/*************************************************************/
+
+		if (ops->datbuf) {
+			dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) |
+							(ONENAND_CMDPROG);
+
+			for (i = 0; i < 4; i++) {
+
+				/* Block on cmd ready and write CMD register */
+				cmd->cmd = DST_CRCI_NAND_CMD;
+				cmd->src = msm_virt_to_dma(chip,
+						&dma_buffer->data.sfcmd[1+i]);
+				cmd->dst = MSM_NAND_SFLASHC_CMD;
+				cmd->len = 4;
+				cmd++;
+
+				/* Trnsfr usr buf contents to nand ctlr buf */
+				cmd->cmd = 0;
+				cmd->src = data_dma_addr_curr;
+				cmd->dst = MSM_NAND_FLASH_BUFFER;
+				cmd->len = 512;
+				data_dma_addr_curr += 512;
+				cmd++;
+
+				/* Write the MACRO1 register */
+				cmd->cmd = 0;
+				cmd->src = msm_virt_to_dma(chip,
+						&dma_buffer->data.macro[i]);
+				cmd->dst = MSM_NAND_MACRO1_REG;
+				cmd->len = 4;
+				cmd++;
+
+				/* Kick the execute command */
+				cmd->cmd = 0;
+				cmd->src = msm_virt_to_dma(chip,
+						&dma_buffer->data.sfexec);
+				cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
+				cmd->len = 4;
+				cmd++;
+
+				/* Block on data rdy, & read status register */
+				cmd->cmd = SRC_CRCI_NAND_DATA;
+				cmd->src = MSM_NAND_SFLASHC_STATUS;
+				cmd->dst = msm_virt_to_dma(chip,
+						&dma_buffer->data.sfstat[1+i]);
+				cmd->len = 4;
+				cmd++;
+
+			}
+		}
+
+		/* Block on cmd ready and write CMD register */
+		cmd->cmd = DST_CRCI_NAND_CMD;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[5]);
+		cmd->dst = MSM_NAND_SFLASHC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		if ((ops->oobbuf) || (ops->mode == MTD_OPS_RAW)) {
+
+			/* Transfer user buf contents into nand ctlr buffer */
+			if (ops->mode == MTD_OPS_AUTO_OOB) {
+				cmd->cmd = 0;
+				cmd->src = init_dma_addr;
+				cmd->dst = MSM_NAND_FLASH_BUFFER;
+				cmd->len = mtd->oobsize;
+				cmd++;
+			}
+			if (ops->mode == MTD_OPS_PLACE_OOB) {
+				cmd->cmd = 0;
+				cmd->src = oob_dma_addr_curr;
+				cmd->dst = MSM_NAND_FLASH_BUFFER;
+				cmd->len = mtd->oobsize;
+				oob_dma_addr_curr += mtd->oobsize;
+				cmd++;
+			}
+			if (ops->mode == MTD_OPS_RAW) {
+				cmd->cmd = 0;
+				cmd->src = data_dma_addr_curr;
+				cmd->dst = MSM_NAND_FLASH_BUFFER;
+				cmd->len = mtd->oobsize;
+				data_dma_addr_curr += mtd->oobsize;
+				cmd++;
+			}
+		} else {
+				cmd->cmd = 0;
+				cmd->src = init_dma_addr;
+				cmd->dst = MSM_NAND_FLASH_BUFFER;
+				cmd->len = mtd->oobsize;
+				cmd++;
+		}
+
+		/* Write the MACRO1 register */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.macro[4]);
+		cmd->dst = MSM_NAND_MACRO1_REG;
+		cmd->len = 4;
+		cmd++;
+
+		/* Kick the execute command */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
+		cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Block on data ready, and read the status register */
+		cmd->cmd = SRC_CRCI_NAND_DATA;
+		cmd->src = MSM_NAND_SFLASHC_STATUS;
+		cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[5]);
+		cmd->len = 4;
+		cmd++;
+
+		/*********************************************************/
+		/* Issuing write command                                 */
+		/*********************************************************/
+
+		/* Block on cmd ready and write CMD register */
+		cmd->cmd = DST_CRCI_NAND_CMD;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[6]);
+		cmd->dst = MSM_NAND_SFLASHC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Kick the execute command */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
+		cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Block on data ready, and read the status register */
+		cmd->cmd = SRC_CRCI_NAND_DATA;
+		cmd->src = MSM_NAND_SFLASHC_STATUS;
+		cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[6]);
+		cmd->len = 4;
+		cmd++;
+
+		/*************************************************************/
+		/* Wait for the interrupt from the Onenand device controller */
+		/*************************************************************/
+
+		/* Block on cmd ready and write CMD register */
+		cmd->cmd = DST_CRCI_NAND_CMD;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[7]);
+		cmd->dst = MSM_NAND_SFLASHC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Kick the execute command */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
+		cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Block on data ready, and read the status register */
+		cmd->cmd = SRC_CRCI_NAND_DATA;
+		cmd->src = MSM_NAND_SFLASHC_STATUS;
+		cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[7]);
+		cmd->len = 4;
+		cmd++;
+
+		/*************************************************************/
+		/* Read necessary status registers from the onenand device   */
+		/*************************************************************/
+
+		/* Block on cmd ready and write CMD register */
+		cmd->cmd = DST_CRCI_NAND_CMD;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[8]);
+		cmd->dst = MSM_NAND_SFLASHC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Kick the execute command */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
+		cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Block on data ready, and read the status register */
+		cmd->cmd = SRC_CRCI_NAND_DATA;
+		cmd->src = MSM_NAND_SFLASHC_STATUS;
+		cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[8]);
+		cmd->len = 4;
+		cmd++;
+
+		/* Read the GENP3 register */
+		cmd->cmd = 0;
+		cmd->src = MSM_NAND_GENP_REG3;
+		cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data3);
+		cmd->len = 4;
+		cmd++;
+
+		/* Read the DEVCMD4 register */
+		cmd->cmd = 0;
+		cmd->src = MSM_NAND_DEV_CMD4;
+		cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data4);
+		cmd->len = 4;
+		cmd++;
+
+		/*************************************************************/
+		/* Restore the necessary registers to proper values          */
+		/*************************************************************/
+
+		/* Block on cmd ready and write CMD register */
+		cmd->cmd = DST_CRCI_NAND_CMD;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[9]);
+		cmd->dst = MSM_NAND_SFLASHC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Kick the execute command */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
+		cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Block on data ready, and read the status register */
+		cmd->cmd = SRC_CRCI_NAND_DATA;
+		cmd->src = MSM_NAND_SFLASHC_STATUS;
+		cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[9]);
+		cmd->len = 4;
+		cmd++;
+
+
+		BUILD_BUG_ON(53 != ARRAY_SIZE(dma_buffer->cmd));
+		BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
+		dma_buffer->cmd[0].cmd |= CMD_OCB;
+		cmd[-1].cmd |= CMD_OCU | CMD_LC;
+
+		dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd)
+				>> 3) | CMD_PTR_LP;
+
+		mb();
+		msm_dmov_exec_cmd(chip->dma_channel,
+			DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip,
+				&dma_buffer->cmdptr)));
+		mb();
+
+		ecc_status = (dma_buffer->data.data3 >> 16) & 0x0000FFFF;
+		interrupt_status = (dma_buffer->data.data4 >> 0)&0x0000FFFF;
+		controller_status = (dma_buffer->data.data4 >> 16)&0x0000FFFF;
+
+#if VERBOSE
+		pr_info("\n%s: sflash status %x %x %x %x %x %x %x"
+				" %x %x %x\n", __func__,
+					dma_buffer->data.sfstat[0],
+					dma_buffer->data.sfstat[1],
+					dma_buffer->data.sfstat[2],
+					dma_buffer->data.sfstat[3],
+					dma_buffer->data.sfstat[4],
+					dma_buffer->data.sfstat[5],
+					dma_buffer->data.sfstat[6],
+					dma_buffer->data.sfstat[7],
+					dma_buffer->data.sfstat[8],
+					dma_buffer->data.sfstat[9]);
+
+		pr_info("%s: controller_status = %x\n", __func__,
+					controller_status);
+		pr_info("%s: interrupt_status = %x\n", __func__,
+					interrupt_status);
+		pr_info("%s: ecc_status = %x\n", __func__,
+					ecc_status);
+#endif
+		/* Check for errors, protection violations etc */
+		if ((controller_status != 0)
+				|| (dma_buffer->data.sfstat[0] & 0x110)
+				|| (dma_buffer->data.sfstat[6] & 0x110)
+				|| (dma_buffer->data.sfstat[7] & 0x110)
+				|| (dma_buffer->data.sfstat[8] & 0x110)
+				|| (dma_buffer->data.sfstat[9] & 0x110)
+				|| ((dma_buffer->data.sfstat[1] & 0x110) &&
+								(ops->datbuf))
+				|| ((dma_buffer->data.sfstat[2] & 0x110) &&
+								(ops->datbuf))
+				|| ((dma_buffer->data.sfstat[3] & 0x110) &&
+								(ops->datbuf))
+				|| ((dma_buffer->data.sfstat[4] & 0x110) &&
+								(ops->datbuf))
+				|| ((dma_buffer->data.sfstat[5] & 0x110) &&
+								((ops->oobbuf)
+					|| (ops->mode == MTD_OPS_RAW)))) {
+			pr_info("%s: ECC/MPU/OP error\n", __func__);
+			err = -EIO;
+		}
+
+		if (err)
+			break;
+		pages_written++;
+		to_curr += mtd->writesize;
+	}
+
+	msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+
+	dma_unmap_page(chip->dev, init_dma_addr, 64, DMA_TO_DEVICE);
+
+err_dma_map_initbuf_failed:
+	if (ops->oobbuf) {
+		dma_unmap_page(chip->dev, oob_dma_addr, ops->ooblen,
+							DMA_TO_DEVICE);
+	}
+err_dma_map_oobbuf_failed:
+	if (ops->datbuf) {
+		dma_unmap_page(chip->dev, data_dma_addr, ops->len,
+							DMA_TO_DEVICE);
+	}
+
+	if (err) {
+		pr_err("%s: %llx %x %x failed\n", __func__, to_curr,
+				ops->datbuf ? ops->len : 0, ops->ooblen);
+	} else {
+		ops->retlen = ops->oobretlen = 0;
+		if (ops->datbuf != NULL) {
+			if (ops->mode != MTD_OPS_RAW)
+				ops->retlen = mtd->writesize * pages_written;
+			else
+				ops->retlen = (mtd->writesize +  mtd->oobsize)
+							* pages_written;
+		}
+		if (ops->oobbuf != NULL) {
+			if (ops->mode == MTD_OPS_AUTO_OOB)
+				ops->oobretlen = mtd->oobavail * pages_written;
+			else
+				ops->oobretlen = mtd->oobsize * pages_written;
+		}
+	}
+
+#if VERBOSE
+	pr_info("\n%s: ret %d, retlen %d oobretlen %d\n",
+			__func__, err, ops->retlen, ops->oobretlen);
+
+	pr_info("================================================="
+			"================\n");
+#endif
+	kfree(init_spare_bytes);
+	return err;
+}
+
+static int msm_onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
+		size_t *retlen, const u_char *buf)
+{
+	int ret;
+	struct mtd_oob_ops ops;
+
+	ops.mode = MTD_OPS_PLACE_OOB;
+	ops.datbuf = (uint8_t *)buf;
+	ops.len = len;
+	ops.retlen = 0;
+	ops.oobbuf = NULL;
+	ops.ooblen = 0;
+	ops.oobretlen = 0;
+	ret =  msm_onenand_write_oob(mtd, to, &ops);
+	*retlen = ops.retlen;
+
+	return ret;
+}
+
+static int msm_onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+	struct msm_nand_chip *chip = mtd->priv;
+
+	struct {
+		dmov_s cmd[20];
+		unsigned cmdptr;
+		struct {
+			uint32_t sfbcfg;
+			uint32_t sfcmd[4];
+			uint32_t sfexec;
+			uint32_t sfstat[4];
+			uint32_t addr0;
+			uint32_t addr1;
+			uint32_t addr2;
+			uint32_t addr3;
+			uint32_t addr4;
+			uint32_t addr5;
+			uint32_t addr6;
+			uint32_t data0;
+			uint32_t data1;
+			uint32_t data2;
+			uint32_t data3;
+			uint32_t data4;
+			uint32_t data5;
+			uint32_t data6;
+		} data;
+	} *dma_buffer;
+	dmov_s *cmd;
+
+	int err = 0;
+
+	uint16_t onenand_startaddr1;
+	uint16_t onenand_startaddr8;
+	uint16_t onenand_startaddr2;
+	uint16_t onenand_startbuffer;
+
+	uint16_t controller_status;
+	uint16_t interrupt_status;
+	uint16_t ecc_status;
+
+	uint64_t temp;
+
+#if VERBOSE
+	pr_info("================================================="
+			"================\n");
+	pr_info("%s: addr 0x%llx len 0x%llx\n",
+			__func__, instr->addr, instr->len);
+#endif
+	if (instr->addr & (mtd->erasesize - 1)) {
+		pr_err("%s: Unsupported erase address, 0x%llx\n",
+				__func__, instr->addr);
+		return -EINVAL;
+	}
+	if (instr->len != mtd->erasesize) {
+		pr_err("%s: Unsupported erase len, %lld\n",
+				__func__, instr->len);
+		return -EINVAL;
+	}
+
+	wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer
+				(chip, sizeof(*dma_buffer))));
+
+	cmd = dma_buffer->cmd;
+
+	temp = instr->addr;
+
+	if ((onenand_info.device_id & ONENAND_DEVICE_IS_DDP)
+		&& (temp >= (mtd->size>>1))) { /* DDP Device */
+			onenand_startaddr1 = DEVICE_FLASHCORE_1 |
+				(((uint32_t)(temp-(mtd->size>>1))
+						/ mtd->erasesize));
+			onenand_startaddr2 = DEVICE_BUFFERRAM_1;
+	} else {
+		onenand_startaddr1 = DEVICE_FLASHCORE_0 |
+			((uint32_t)temp / mtd->erasesize) ;
+		onenand_startaddr2 = DEVICE_BUFFERRAM_0;
+	}
+
+	onenand_startaddr8 = 0x0000;
+	onenand_startbuffer = DATARAM0_0 << 8;
+
+	dma_buffer->data.sfbcfg = SFLASH_BCFG |
+					(nand_sfcmd_mode ? 0 : (1 << 24));
+	dma_buffer->data.sfcmd[0] = SFLASH_PREPCMD(7, 0, 0,
+						MSM_NAND_SFCMD_CMDXS,
+						nand_sfcmd_mode,
+						MSM_NAND_SFCMD_REGWR);
+	dma_buffer->data.sfcmd[1] = SFLASH_PREPCMD(0, 0, 32,
+						MSM_NAND_SFCMD_CMDXS,
+						nand_sfcmd_mode,
+						MSM_NAND_SFCMD_INTHI);
+	dma_buffer->data.sfcmd[2] = SFLASH_PREPCMD(3, 7, 0,
+						MSM_NAND_SFCMD_DATXS,
+						nand_sfcmd_mode,
+						MSM_NAND_SFCMD_REGRD);
+	dma_buffer->data.sfcmd[3] = SFLASH_PREPCMD(4, 10, 0,
+						MSM_NAND_SFCMD_CMDXS,
+						nand_sfcmd_mode,
+						MSM_NAND_SFCMD_REGWR);
+	dma_buffer->data.sfexec = 1;
+	dma_buffer->data.sfstat[0] = CLEAN_DATA_32;
+	dma_buffer->data.sfstat[1] = CLEAN_DATA_32;
+	dma_buffer->data.sfstat[2] = CLEAN_DATA_32;
+	dma_buffer->data.sfstat[3] = CLEAN_DATA_32;
+	dma_buffer->data.addr0 = (ONENAND_INTERRUPT_STATUS << 16) |
+						(ONENAND_SYSTEM_CONFIG_1);
+	dma_buffer->data.addr1 = (ONENAND_START_ADDRESS_8 << 16) |
+						(ONENAND_START_ADDRESS_1);
+	dma_buffer->data.addr2 = (ONENAND_START_BUFFER << 16) |
+						(ONENAND_START_ADDRESS_2);
+	dma_buffer->data.addr3 = (ONENAND_ECC_STATUS << 16) |
+						(ONENAND_COMMAND);
+	dma_buffer->data.addr4 = (ONENAND_CONTROLLER_STATUS << 16) |
+						(ONENAND_INTERRUPT_STATUS);
+	dma_buffer->data.addr5 = (ONENAND_INTERRUPT_STATUS << 16) |
+						(ONENAND_SYSTEM_CONFIG_1);
+	dma_buffer->data.addr6 = (ONENAND_START_ADDRESS_3 << 16) |
+						(ONENAND_START_ADDRESS_1);
+	dma_buffer->data.data0 = (ONENAND_CLRINTR << 16) |
+				(ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode));
+	dma_buffer->data.data1 = (onenand_startaddr8 << 16) |
+						(onenand_startaddr1);
+	dma_buffer->data.data2 = (onenand_startbuffer << 16) |
+						(onenand_startaddr2);
+	dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) |
+						(ONENAND_CMDERAS);
+	dma_buffer->data.data4 = (CLEAN_DATA_16 << 16) |
+						(CLEAN_DATA_16);
+	dma_buffer->data.data5 = (ONENAND_CLRINTR << 16) |
+				(ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode));
+	dma_buffer->data.data6 = (ONENAND_STARTADDR3_RES << 16) |
+						(ONENAND_STARTADDR1_RES);
+
+	/***************************************************************/
+	/* Write the necessary address registers in the onenand device */
+	/***************************************************************/
+
+	/* Enable and configure the SFlash controller */
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfbcfg);
+	cmd->dst = MSM_NAND_SFLASHC_BURST_CFG;
+	cmd->len = 4;
+	cmd++;
+
+	/* Block on cmd ready and write CMD register */
+	cmd->cmd = DST_CRCI_NAND_CMD;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[0]);
+	cmd->dst = MSM_NAND_SFLASHC_CMD;
+	cmd->len = 4;
+	cmd++;
+
+	/* Write the ADDR0 and ADDR1 registers */
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr0);
+	cmd->dst = MSM_NAND_ADDR0;
+	cmd->len = 8;
+	cmd++;
+
+	/* Write the ADDR2 ADDR3 ADDR4 ADDR5 registers */
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr2);
+	cmd->dst = MSM_NAND_ADDR2;
+	cmd->len = 16;
+	cmd++;
+
+	/* Write the ADDR6 registers */
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr6);
+	cmd->dst = MSM_NAND_ADDR6;
+	cmd->len = 4;
+	cmd++;
+
+	/* Write the GENP0, GENP1, GENP2, GENP3, GENP4 registers */
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data0);
+	cmd->dst = MSM_NAND_GENP_REG0;
+	cmd->len = 16;
+	cmd++;
+
+	/* Write the FLASH_DEV_CMD4,5,6 registers */
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data4);
+	cmd->dst = MSM_NAND_DEV_CMD4;
+	cmd->len = 12;
+	cmd++;
+
+	/* Kick the execute command */
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
+	cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
+	cmd->len = 4;
+	cmd++;
+
+	/* Block on data ready, and read the status register */
+	cmd->cmd = SRC_CRCI_NAND_DATA;
+	cmd->src = MSM_NAND_SFLASHC_STATUS;
+	cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[0]);
+	cmd->len = 4;
+	cmd++;
+
+	/***************************************************************/
+	/* Wait for the interrupt from the Onenand device controller   */
+	/***************************************************************/
+
+	/* Block on cmd ready and write CMD register */
+	cmd->cmd = DST_CRCI_NAND_CMD;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[1]);
+	cmd->dst = MSM_NAND_SFLASHC_CMD;
+	cmd->len = 4;
+	cmd++;
+
+	/* Kick the execute command */
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
+	cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
+	cmd->len = 4;
+	cmd++;
+
+	/* Block on data ready, and read the status register */
+	cmd->cmd = SRC_CRCI_NAND_DATA;
+	cmd->src = MSM_NAND_SFLASHC_STATUS;
+	cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[1]);
+	cmd->len = 4;
+	cmd++;
+
+	/***************************************************************/
+	/* Read the necessary status registers from the onenand device */
+	/***************************************************************/
+
+	/* Block on cmd ready and write CMD register */
+	cmd->cmd = DST_CRCI_NAND_CMD;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[2]);
+	cmd->dst = MSM_NAND_SFLASHC_CMD;
+	cmd->len = 4;
+	cmd++;
+
+	/* Kick the execute command */
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
+	cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
+	cmd->len = 4;
+	cmd++;
+
+	/* Block on data ready, and read the status register */
+	cmd->cmd = SRC_CRCI_NAND_DATA;
+	cmd->src = MSM_NAND_SFLASHC_STATUS;
+	cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[2]);
+	cmd->len = 4;
+	cmd++;
+
+	/* Read the GENP3 register */
+	cmd->cmd = 0;
+	cmd->src = MSM_NAND_GENP_REG3;
+	cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data3);
+	cmd->len = 4;
+	cmd++;
+
+	/* Read the DEVCMD4 register */
+	cmd->cmd = 0;
+	cmd->src = MSM_NAND_DEV_CMD4;
+	cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data4);
+	cmd->len = 4;
+	cmd++;
+
+	/***************************************************************/
+	/* Restore the necessary registers to proper values            */
+	/***************************************************************/
+
+	/* Block on cmd ready and write CMD register */
+	cmd->cmd = DST_CRCI_NAND_CMD;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[3]);
+	cmd->dst = MSM_NAND_SFLASHC_CMD;
+	cmd->len = 4;
+	cmd++;
+
+	/* Kick the execute command */
+	cmd->cmd = 0;
+	cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
+	cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
+	cmd->len = 4;
+	cmd++;
+
+	/* Block on data ready, and read the status register */
+	cmd->cmd = SRC_CRCI_NAND_DATA;
+	cmd->src = MSM_NAND_SFLASHC_STATUS;
+	cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[3]);
+	cmd->len = 4;
+	cmd++;
+
+
+	BUILD_BUG_ON(20 != ARRAY_SIZE(dma_buffer->cmd));
+	BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
+	dma_buffer->cmd[0].cmd |= CMD_OCB;
+	cmd[-1].cmd |= CMD_OCU | CMD_LC;
+
+	dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd)
+			>> 3) | CMD_PTR_LP;
+
+	mb();
+	msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST
+			| DMOV_CMD_ADDR(msm_virt_to_dma(chip,
+			&dma_buffer->cmdptr)));
+	mb();
+
+	ecc_status = (dma_buffer->data.data3 >> 16) & 0x0000FFFF;
+	interrupt_status = (dma_buffer->data.data4 >> 0) & 0x0000FFFF;
+	controller_status = (dma_buffer->data.data4 >> 16) & 0x0000FFFF;
+
+#if VERBOSE
+	pr_info("\n%s: sflash status %x %x %x %x\n", __func__,
+				dma_buffer->data.sfstat[0],
+				dma_buffer->data.sfstat[1],
+				dma_buffer->data.sfstat[2],
+				dma_buffer->data.sfstat[3]);
+
+	pr_info("%s: controller_status = %x\n", __func__,
+				controller_status);
+	pr_info("%s: interrupt_status = %x\n", __func__,
+				interrupt_status);
+	pr_info("%s: ecc_status = %x\n", __func__,
+				ecc_status);
+#endif
+	/* Check for errors, protection violations etc */
+	if ((controller_status != 0)
+			|| (dma_buffer->data.sfstat[0] & 0x110)
+			|| (dma_buffer->data.sfstat[1] & 0x110)
+			|| (dma_buffer->data.sfstat[2] & 0x110)
+			|| (dma_buffer->data.sfstat[3] & 0x110)) {
+		pr_err("%s: ECC/MPU/OP error\n", __func__);
+		err = -EIO;
+	}
+
+	msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+
+	if (err) {
+		pr_err("%s: Erase failed, 0x%llx\n", __func__,
+				instr->addr);
+		instr->fail_addr = instr->addr;
+		instr->state = MTD_ERASE_FAILED;
+	} else {
+		instr->state = MTD_ERASE_DONE;
+		instr->fail_addr = 0xffffffff;
+		mtd_erase_callback(instr);
+	}
+
+#if VERBOSE
+	pr_info("\n%s: ret %d\n", __func__, err);
+	pr_info("===================================================="
+			"=============\n");
+#endif
+	return err;
+}
+
+static int msm_onenand_block_isbad(struct mtd_info *mtd, loff_t ofs)
+{
+	struct mtd_oob_ops ops;
+	int rval, i;
+	int ret = 0;
+	uint8_t *buffer;
+	uint8_t *oobptr;
+
+	if ((ofs > mtd->size) || (ofs & (mtd->erasesize - 1))) {
+		pr_err("%s: unsupported block address, 0x%x\n",
+			 __func__, (uint32_t)ofs);
+		return -EINVAL;
+	}
+
+	buffer = kmalloc(2112, GFP_KERNEL|GFP_DMA);
+	if (buffer == 0) {
+		pr_err("%s: Could not kmalloc for buffer\n",
+				__func__);
+		return -ENOMEM;
+	}
+
+	memset(buffer, 0x00, 2112);
+	oobptr = &(buffer[2048]);
+
+	ops.mode = MTD_OPS_RAW;
+	ops.len = 2112;
+	ops.retlen = 0;
+	ops.ooblen = 0;
+	ops.oobretlen = 0;
+	ops.ooboffs = 0;
+	ops.datbuf = buffer;
+	ops.oobbuf = NULL;
+
+	for (i = 0; i < 2; i++) {
+		ofs = ofs + i*mtd->writesize;
+		rval = msm_onenand_read_oob(mtd, ofs, &ops);
+		if (rval) {
+			pr_err("%s: Error in reading bad blk info\n",
+					__func__);
+			ret = rval;
+			break;
+		}
+		if ((oobptr[0] != 0xFF) || (oobptr[1] != 0xFF) ||
+		    (oobptr[16] != 0xFF) || (oobptr[17] != 0xFF) ||
+		    (oobptr[32] != 0xFF) || (oobptr[33] != 0xFF) ||
+		    (oobptr[48] != 0xFF) || (oobptr[49] != 0xFF)
+		   ) {
+			ret = 1;
+			break;
+		}
+	}
+
+	kfree(buffer);
+
+#if VERBOSE
+	if (ret == 1)
+		pr_info("%s : Block containing 0x%x is bad\n",
+				__func__, (unsigned int)ofs);
+#endif
+	return ret;
+}
+
+static int msm_onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+	struct mtd_oob_ops ops;
+	int rval, i;
+	int ret = 0;
+	uint8_t *buffer;
+
+	if ((ofs > mtd->size) || (ofs & (mtd->erasesize - 1))) {
+		pr_err("%s: unsupported block address, 0x%x\n",
+			 __func__, (uint32_t)ofs);
+		return -EINVAL;
+	}
+
+	buffer = page_address(ZERO_PAGE());
+
+	ops.mode = MTD_OPS_RAW;
+	ops.len = 2112;
+	ops.retlen = 0;
+	ops.ooblen = 0;
+	ops.oobretlen = 0;
+	ops.ooboffs = 0;
+	ops.datbuf = buffer;
+	ops.oobbuf = NULL;
+
+	for (i = 0; i < 2; i++) {
+		ofs = ofs + i*mtd->writesize;
+		rval = msm_onenand_write_oob(mtd, ofs, &ops);
+		if (rval) {
+			pr_err("%s: Error in writing bad blk info\n",
+					__func__);
+			ret = rval;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int msm_onenand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+{
+	struct msm_nand_chip *chip = mtd->priv;
+
+	struct {
+		dmov_s cmd[20];
+		unsigned cmdptr;
+		struct {
+			uint32_t sfbcfg;
+			uint32_t sfcmd[4];
+			uint32_t sfexec;
+			uint32_t sfstat[4];
+			uint32_t addr0;
+			uint32_t addr1;
+			uint32_t addr2;
+			uint32_t addr3;
+			uint32_t addr4;
+			uint32_t addr5;
+			uint32_t addr6;
+			uint32_t data0;
+			uint32_t data1;
+			uint32_t data2;
+			uint32_t data3;
+			uint32_t data4;
+			uint32_t data5;
+			uint32_t data6;
+		} data;
+	} *dma_buffer;
+	dmov_s *cmd;
+
+	int err = 0;
+
+	uint16_t onenand_startaddr1;
+	uint16_t onenand_startaddr8;
+	uint16_t onenand_startaddr2;
+	uint16_t onenand_startblock;
+
+	uint16_t controller_status;
+	uint16_t interrupt_status;
+	uint16_t write_prot_status;
+
+	uint64_t start_ofs;
+
+#if VERBOSE
+	pr_info("===================================================="
+			"=============\n");
+	pr_info("%s: ofs 0x%llx len %lld\n", __func__, ofs, len);
+#endif
+	/* 'ofs' & 'len' should align to block size */
+	if (ofs&(mtd->erasesize - 1)) {
+		pr_err("%s: Unsupported ofs address, 0x%llx\n",
+				__func__, ofs);
+		return -EINVAL;
+	}
+
+	if (len&(mtd->erasesize - 1)) {
+		pr_err("%s: Unsupported len, %lld\n",
+				__func__, len);
+		return -EINVAL;
+	}
+
+	if (ofs+len > mtd->size) {
+		pr_err("%s: Maximum chip size exceeded\n", __func__);
+		return -EINVAL;
+	}
+
+	wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer
+				(chip, sizeof(*dma_buffer))));
+
+	for (start_ofs = ofs; ofs < start_ofs+len; ofs = ofs+mtd->erasesize) {
+#if VERBOSE
+		pr_info("%s: ofs 0x%llx len %lld\n", __func__, ofs, len);
+#endif
+
+		cmd = dma_buffer->cmd;
+		if ((onenand_info.device_id & ONENAND_DEVICE_IS_DDP)
+			&& (ofs >= (mtd->size>>1))) { /* DDP Device */
+			onenand_startaddr1 = DEVICE_FLASHCORE_1 |
+				(((uint32_t)(ofs - (mtd->size>>1))
+						/ mtd->erasesize));
+			onenand_startaddr2 = DEVICE_BUFFERRAM_1;
+			onenand_startblock = ((uint32_t)(ofs - (mtd->size>>1))
+						/ mtd->erasesize);
+		} else {
+			onenand_startaddr1 = DEVICE_FLASHCORE_0 |
+					((uint32_t)ofs / mtd->erasesize) ;
+			onenand_startaddr2 = DEVICE_BUFFERRAM_0;
+			onenand_startblock = ((uint32_t)ofs
+						/ mtd->erasesize);
+		}
+
+		onenand_startaddr8 = 0x0000;
+		dma_buffer->data.sfbcfg = SFLASH_BCFG |
+					(nand_sfcmd_mode ? 0 : (1 << 24));
+		dma_buffer->data.sfcmd[0] = SFLASH_PREPCMD(7, 0, 0,
+							MSM_NAND_SFCMD_CMDXS,
+							nand_sfcmd_mode,
+							MSM_NAND_SFCMD_REGWR);
+		dma_buffer->data.sfcmd[1] = SFLASH_PREPCMD(0, 0, 32,
+							MSM_NAND_SFCMD_CMDXS,
+							nand_sfcmd_mode,
+							MSM_NAND_SFCMD_INTHI);
+		dma_buffer->data.sfcmd[2] = SFLASH_PREPCMD(3, 7, 0,
+							MSM_NAND_SFCMD_DATXS,
+							nand_sfcmd_mode,
+							MSM_NAND_SFCMD_REGRD);
+		dma_buffer->data.sfcmd[3] = SFLASH_PREPCMD(4, 10, 0,
+							MSM_NAND_SFCMD_CMDXS,
+							nand_sfcmd_mode,
+							MSM_NAND_SFCMD_REGWR);
+		dma_buffer->data.sfexec = 1;
+		dma_buffer->data.sfstat[0] = CLEAN_DATA_32;
+		dma_buffer->data.sfstat[1] = CLEAN_DATA_32;
+		dma_buffer->data.sfstat[2] = CLEAN_DATA_32;
+		dma_buffer->data.sfstat[3] = CLEAN_DATA_32;
+		dma_buffer->data.addr0 = (ONENAND_INTERRUPT_STATUS << 16) |
+						(ONENAND_SYSTEM_CONFIG_1);
+		dma_buffer->data.addr1 = (ONENAND_START_ADDRESS_8 << 16) |
+						(ONENAND_START_ADDRESS_1);
+		dma_buffer->data.addr2 = (ONENAND_START_BLOCK_ADDRESS << 16) |
+						(ONENAND_START_ADDRESS_2);
+		dma_buffer->data.addr3 = (ONENAND_WRITE_PROT_STATUS << 16) |
+						(ONENAND_COMMAND);
+		dma_buffer->data.addr4 = (ONENAND_CONTROLLER_STATUS << 16) |
+						(ONENAND_INTERRUPT_STATUS);
+		dma_buffer->data.addr5 = (ONENAND_INTERRUPT_STATUS << 16) |
+						(ONENAND_SYSTEM_CONFIG_1);
+		dma_buffer->data.addr6 = (ONENAND_START_ADDRESS_3 << 16) |
+						(ONENAND_START_ADDRESS_1);
+		dma_buffer->data.data0 = (ONENAND_CLRINTR << 16) |
+				(ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode));
+		dma_buffer->data.data1 = (onenand_startaddr8 << 16) |
+						(onenand_startaddr1);
+		dma_buffer->data.data2 = (onenand_startblock << 16) |
+						(onenand_startaddr2);
+		dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) |
+						(ONENAND_CMD_UNLOCK);
+		dma_buffer->data.data4 = (CLEAN_DATA_16 << 16) |
+						(CLEAN_DATA_16);
+		dma_buffer->data.data5 = (ONENAND_CLRINTR << 16) |
+				(ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode));
+		dma_buffer->data.data6 = (ONENAND_STARTADDR3_RES << 16) |
+						(ONENAND_STARTADDR1_RES);
+
+		/*************************************************************/
+		/* Write the necessary address reg in the onenand device     */
+		/*************************************************************/
+
+		/* Enable and configure the SFlash controller */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfbcfg);
+		cmd->dst = MSM_NAND_SFLASHC_BURST_CFG;
+		cmd->len = 4;
+		cmd++;
+
+		/* Block on cmd ready and write CMD register */
+		cmd->cmd = DST_CRCI_NAND_CMD;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[0]);
+		cmd->dst = MSM_NAND_SFLASHC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Write the ADDR0 and ADDR1 registers */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr0);
+		cmd->dst = MSM_NAND_ADDR0;
+		cmd->len = 8;
+		cmd++;
+
+		/* Write the ADDR2 ADDR3 ADDR4 ADDR5 registers */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr2);
+		cmd->dst = MSM_NAND_ADDR2;
+		cmd->len = 16;
+		cmd++;
+
+		/* Write the ADDR6 registers */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr6);
+		cmd->dst = MSM_NAND_ADDR6;
+		cmd->len = 4;
+		cmd++;
+
+		/* Write the GENP0, GENP1, GENP2, GENP3, GENP4 registers */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data0);
+		cmd->dst = MSM_NAND_GENP_REG0;
+		cmd->len = 16;
+		cmd++;
+
+		/* Write the FLASH_DEV_CMD4,5,6 registers */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data4);
+		cmd->dst = MSM_NAND_DEV_CMD4;
+		cmd->len = 12;
+		cmd++;
+
+		/* Kick the execute command */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
+		cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Block on data ready, and read the status register */
+		cmd->cmd = SRC_CRCI_NAND_DATA;
+		cmd->src = MSM_NAND_SFLASHC_STATUS;
+		cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[0]);
+		cmd->len = 4;
+		cmd++;
+
+		/*************************************************************/
+		/* Wait for the interrupt from the Onenand device controller */
+		/*************************************************************/
+
+		/* Block on cmd ready and write CMD register */
+		cmd->cmd = DST_CRCI_NAND_CMD;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[1]);
+		cmd->dst = MSM_NAND_SFLASHC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Kick the execute command */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
+		cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Block on data ready, and read the status register */
+		cmd->cmd = SRC_CRCI_NAND_DATA;
+		cmd->src = MSM_NAND_SFLASHC_STATUS;
+		cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[1]);
+		cmd->len = 4;
+		cmd++;
+
+		/*********************************************************/
+		/* Read the necessary status reg from the onenand device */
+		/*********************************************************/
+
+		/* Block on cmd ready and write CMD register */
+		cmd->cmd = DST_CRCI_NAND_CMD;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[2]);
+		cmd->dst = MSM_NAND_SFLASHC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Kick the execute command */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
+		cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Block on data ready, and read the status register */
+		cmd->cmd = SRC_CRCI_NAND_DATA;
+		cmd->src = MSM_NAND_SFLASHC_STATUS;
+		cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[2]);
+		cmd->len = 4;
+		cmd++;
+
+		/* Read the GENP3 register */
+		cmd->cmd = 0;
+		cmd->src = MSM_NAND_GENP_REG3;
+		cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data3);
+		cmd->len = 4;
+		cmd++;
+
+		/* Read the DEVCMD4 register */
+		cmd->cmd = 0;
+		cmd->src = MSM_NAND_DEV_CMD4;
+		cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data4);
+		cmd->len = 4;
+		cmd++;
+
+		/************************************************************/
+		/* Restore the necessary registers to proper values         */
+		/************************************************************/
+
+		/* Block on cmd ready and write CMD register */
+		cmd->cmd = DST_CRCI_NAND_CMD;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[3]);
+		cmd->dst = MSM_NAND_SFLASHC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Kick the execute command */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
+		cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Block on data ready, and read the status register */
+		cmd->cmd = SRC_CRCI_NAND_DATA;
+		cmd->src = MSM_NAND_SFLASHC_STATUS;
+		cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[3]);
+		cmd->len = 4;
+		cmd++;
+
+
+		BUILD_BUG_ON(20 != ARRAY_SIZE(dma_buffer->cmd));
+		BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
+		dma_buffer->cmd[0].cmd |= CMD_OCB;
+		cmd[-1].cmd |= CMD_OCU | CMD_LC;
+
+		dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd)
+				>> 3) | CMD_PTR_LP;
+
+		mb();
+		msm_dmov_exec_cmd(chip->dma_channel,
+			DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip,
+				&dma_buffer->cmdptr)));
+		mb();
+
+		write_prot_status = (dma_buffer->data.data3 >> 16) & 0x0000FFFF;
+		interrupt_status = (dma_buffer->data.data4 >> 0) & 0x0000FFFF;
+		controller_status = (dma_buffer->data.data4 >> 16) & 0x0000FFFF;
+
+#if VERBOSE
+		pr_info("\n%s: sflash status %x %x %x %x\n", __func__,
+					dma_buffer->data.sfstat[0],
+					dma_buffer->data.sfstat[1],
+					dma_buffer->data.sfstat[2],
+					dma_buffer->data.sfstat[3]);
+
+		pr_info("%s: controller_status = %x\n", __func__,
+					controller_status);
+		pr_info("%s: interrupt_status = %x\n", __func__,
+					interrupt_status);
+		pr_info("%s: write_prot_status = %x\n", __func__,
+					write_prot_status);
+#endif
+		/* Check for errors, protection violations etc */
+		if ((controller_status != 0)
+				|| (dma_buffer->data.sfstat[0] & 0x110)
+				|| (dma_buffer->data.sfstat[1] & 0x110)
+				|| (dma_buffer->data.sfstat[2] & 0x110)
+				|| (dma_buffer->data.sfstat[3] & 0x110)) {
+			pr_err("%s: ECC/MPU/OP error\n", __func__);
+			err = -EIO;
+		}
+
+		if (!(write_prot_status & ONENAND_WP_US)) {
+			pr_err("%s: Unexpected status ofs = 0x%llx,"
+				"wp_status = %x\n",
+				__func__, ofs, write_prot_status);
+			err = -EIO;
+		}
+
+		if (err)
+			break;
+	}
+
+	msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+
+#if VERBOSE
+	pr_info("\n%s: ret %d\n", __func__, err);
+	pr_info("===================================================="
+			"=============\n");
+#endif
+	return err;
+}
+
+static int msm_onenand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+{
+	struct msm_nand_chip *chip = mtd->priv;
+
+	struct {
+		dmov_s cmd[20];
+		unsigned cmdptr;
+		struct {
+			uint32_t sfbcfg;
+			uint32_t sfcmd[4];
+			uint32_t sfexec;
+			uint32_t sfstat[4];
+			uint32_t addr0;
+			uint32_t addr1;
+			uint32_t addr2;
+			uint32_t addr3;
+			uint32_t addr4;
+			uint32_t addr5;
+			uint32_t addr6;
+			uint32_t data0;
+			uint32_t data1;
+			uint32_t data2;
+			uint32_t data3;
+			uint32_t data4;
+			uint32_t data5;
+			uint32_t data6;
+		} data;
+	} *dma_buffer;
+	dmov_s *cmd;
+
+	int err = 0;
+
+	uint16_t onenand_startaddr1;
+	uint16_t onenand_startaddr8;
+	uint16_t onenand_startaddr2;
+	uint16_t onenand_startblock;
+
+	uint16_t controller_status;
+	uint16_t interrupt_status;
+	uint16_t write_prot_status;
+
+	uint64_t start_ofs;
+
+#if VERBOSE
+	pr_info("===================================================="
+			"=============\n");
+	pr_info("%s: ofs 0x%llx len %lld\n", __func__, ofs, len);
+#endif
+	/* 'ofs' & 'len' should align to block size */
+	if (ofs&(mtd->erasesize - 1)) {
+		pr_err("%s: Unsupported ofs address, 0x%llx\n",
+				__func__, ofs);
+		return -EINVAL;
+	}
+
+	if (len&(mtd->erasesize - 1)) {
+		pr_err("%s: Unsupported len, %lld\n",
+				__func__, len);
+		return -EINVAL;
+	}
+
+	if (ofs+len > mtd->size) {
+		pr_err("%s: Maximum chip size exceeded\n", __func__);
+		return -EINVAL;
+	}
+
+	wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer
+				(chip, sizeof(*dma_buffer))));
+
+	for (start_ofs = ofs; ofs < start_ofs+len; ofs = ofs+mtd->erasesize) {
+#if VERBOSE
+		pr_info("%s: ofs 0x%llx len %lld\n", __func__, ofs, len);
+#endif
+
+		cmd = dma_buffer->cmd;
+		if ((onenand_info.device_id & ONENAND_DEVICE_IS_DDP)
+			&& (ofs >= (mtd->size>>1))) { /* DDP Device */
+			onenand_startaddr1 = DEVICE_FLASHCORE_1 |
+				(((uint32_t)(ofs - (mtd->size>>1))
+						/ mtd->erasesize));
+			onenand_startaddr2 = DEVICE_BUFFERRAM_1;
+			onenand_startblock = ((uint32_t)(ofs - (mtd->size>>1))
+						/ mtd->erasesize);
+		} else {
+			onenand_startaddr1 = DEVICE_FLASHCORE_0 |
+					((uint32_t)ofs / mtd->erasesize) ;
+			onenand_startaddr2 = DEVICE_BUFFERRAM_0;
+			onenand_startblock = ((uint32_t)ofs
+						/ mtd->erasesize);
+		}
+
+		onenand_startaddr8 = 0x0000;
+		dma_buffer->data.sfbcfg = SFLASH_BCFG |
+					(nand_sfcmd_mode ? 0 : (1 << 24));
+		dma_buffer->data.sfcmd[0] = SFLASH_PREPCMD(7, 0, 0,
+							MSM_NAND_SFCMD_CMDXS,
+							nand_sfcmd_mode,
+							MSM_NAND_SFCMD_REGWR);
+		dma_buffer->data.sfcmd[1] = SFLASH_PREPCMD(0, 0, 32,
+							MSM_NAND_SFCMD_CMDXS,
+							nand_sfcmd_mode,
+							MSM_NAND_SFCMD_INTHI);
+		dma_buffer->data.sfcmd[2] = SFLASH_PREPCMD(3, 7, 0,
+							MSM_NAND_SFCMD_DATXS,
+							nand_sfcmd_mode,
+							MSM_NAND_SFCMD_REGRD);
+		dma_buffer->data.sfcmd[3] = SFLASH_PREPCMD(4, 10, 0,
+							MSM_NAND_SFCMD_CMDXS,
+							nand_sfcmd_mode,
+							MSM_NAND_SFCMD_REGWR);
+		dma_buffer->data.sfexec = 1;
+		dma_buffer->data.sfstat[0] = CLEAN_DATA_32;
+		dma_buffer->data.sfstat[1] = CLEAN_DATA_32;
+		dma_buffer->data.sfstat[2] = CLEAN_DATA_32;
+		dma_buffer->data.sfstat[3] = CLEAN_DATA_32;
+		dma_buffer->data.addr0 = (ONENAND_INTERRUPT_STATUS << 16) |
+						(ONENAND_SYSTEM_CONFIG_1);
+		dma_buffer->data.addr1 = (ONENAND_START_ADDRESS_8 << 16) |
+						(ONENAND_START_ADDRESS_1);
+		dma_buffer->data.addr2 = (ONENAND_START_BLOCK_ADDRESS << 16) |
+						(ONENAND_START_ADDRESS_2);
+		dma_buffer->data.addr3 = (ONENAND_WRITE_PROT_STATUS << 16) |
+						(ONENAND_COMMAND);
+		dma_buffer->data.addr4 = (ONENAND_CONTROLLER_STATUS << 16) |
+						(ONENAND_INTERRUPT_STATUS);
+		dma_buffer->data.addr5 = (ONENAND_INTERRUPT_STATUS << 16) |
+						(ONENAND_SYSTEM_CONFIG_1);
+		dma_buffer->data.addr6 = (ONENAND_START_ADDRESS_3 << 16) |
+						(ONENAND_START_ADDRESS_1);
+		dma_buffer->data.data0 = (ONENAND_CLRINTR << 16) |
+				(ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode));
+		dma_buffer->data.data1 = (onenand_startaddr8 << 16) |
+						(onenand_startaddr1);
+		dma_buffer->data.data2 = (onenand_startblock << 16) |
+						(onenand_startaddr2);
+		dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) |
+						(ONENAND_CMD_LOCK);
+		dma_buffer->data.data4 = (CLEAN_DATA_16 << 16) |
+						(CLEAN_DATA_16);
+		dma_buffer->data.data5 = (ONENAND_CLRINTR << 16) |
+				(ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode));
+		dma_buffer->data.data6 = (ONENAND_STARTADDR3_RES << 16) |
+						(ONENAND_STARTADDR1_RES);
+
+		/*************************************************************/
+		/* Write the necessary address reg in the onenand device     */
+		/*************************************************************/
+
+		/* Enable and configure the SFlash controller */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfbcfg);
+		cmd->dst = MSM_NAND_SFLASHC_BURST_CFG;
+		cmd->len = 4;
+		cmd++;
+
+		/* Block on cmd ready and write CMD register */
+		cmd->cmd = DST_CRCI_NAND_CMD;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[0]);
+		cmd->dst = MSM_NAND_SFLASHC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Write the ADDR0 and ADDR1 registers */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr0);
+		cmd->dst = MSM_NAND_ADDR0;
+		cmd->len = 8;
+		cmd++;
+
+		/* Write the ADDR2 ADDR3 ADDR4 ADDR5 registers */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr2);
+		cmd->dst = MSM_NAND_ADDR2;
+		cmd->len = 16;
+		cmd++;
+
+		/* Write the ADDR6 registers */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr6);
+		cmd->dst = MSM_NAND_ADDR6;
+		cmd->len = 4;
+		cmd++;
+
+		/* Write the GENP0, GENP1, GENP2, GENP3, GENP4 registers */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data0);
+		cmd->dst = MSM_NAND_GENP_REG0;
+		cmd->len = 16;
+		cmd++;
+
+		/* Write the FLASH_DEV_CMD4,5,6 registers */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data4);
+		cmd->dst = MSM_NAND_DEV_CMD4;
+		cmd->len = 12;
+		cmd++;
+
+		/* Kick the execute command */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
+		cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Block on data ready, and read the status register */
+		cmd->cmd = SRC_CRCI_NAND_DATA;
+		cmd->src = MSM_NAND_SFLASHC_STATUS;
+		cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[0]);
+		cmd->len = 4;
+		cmd++;
+
+		/*************************************************************/
+		/* Wait for the interrupt from the Onenand device controller */
+		/*************************************************************/
+
+		/* Block on cmd ready and write CMD register */
+		cmd->cmd = DST_CRCI_NAND_CMD;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[1]);
+		cmd->dst = MSM_NAND_SFLASHC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Kick the execute command */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
+		cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Block on data ready, and read the status register */
+		cmd->cmd = SRC_CRCI_NAND_DATA;
+		cmd->src = MSM_NAND_SFLASHC_STATUS;
+		cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[1]);
+		cmd->len = 4;
+		cmd++;
+
+		/*********************************************************/
+		/* Read the necessary status reg from the onenand device */
+		/*********************************************************/
+
+		/* Block on cmd ready and write CMD register */
+		cmd->cmd = DST_CRCI_NAND_CMD;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[2]);
+		cmd->dst = MSM_NAND_SFLASHC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Kick the execute command */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
+		cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Block on data ready, and read the status register */
+		cmd->cmd = SRC_CRCI_NAND_DATA;
+		cmd->src = MSM_NAND_SFLASHC_STATUS;
+		cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[2]);
+		cmd->len = 4;
+		cmd++;
+
+		/* Read the GENP3 register */
+		cmd->cmd = 0;
+		cmd->src = MSM_NAND_GENP_REG3;
+		cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data3);
+		cmd->len = 4;
+		cmd++;
+
+		/* Read the DEVCMD4 register */
+		cmd->cmd = 0;
+		cmd->src = MSM_NAND_DEV_CMD4;
+		cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data4);
+		cmd->len = 4;
+		cmd++;
+
+		/************************************************************/
+		/* Restore the necessary registers to proper values         */
+		/************************************************************/
+
+		/* Block on cmd ready and write CMD register */
+		cmd->cmd = DST_CRCI_NAND_CMD;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[3]);
+		cmd->dst = MSM_NAND_SFLASHC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Kick the execute command */
+		cmd->cmd = 0;
+		cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
+		cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
+		cmd->len = 4;
+		cmd++;
+
+		/* Block on data ready, and read the status register */
+		cmd->cmd = SRC_CRCI_NAND_DATA;
+		cmd->src = MSM_NAND_SFLASHC_STATUS;
+		cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[3]);
+		cmd->len = 4;
+		cmd++;
+
+
+		BUILD_BUG_ON(20 != ARRAY_SIZE(dma_buffer->cmd));
+		BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
+		dma_buffer->cmd[0].cmd |= CMD_OCB;
+		cmd[-1].cmd |= CMD_OCU | CMD_LC;
+
+		dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd)
+				>> 3) | CMD_PTR_LP;
+
+		mb();
+		msm_dmov_exec_cmd(chip->dma_channel,
+			DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip,
+				&dma_buffer->cmdptr)));
+		mb();
+
+		write_prot_status = (dma_buffer->data.data3 >> 16) & 0x0000FFFF;
+		interrupt_status = (dma_buffer->data.data4 >> 0) & 0x0000FFFF;
+		controller_status = (dma_buffer->data.data4 >> 16) & 0x0000FFFF;
+
+#if VERBOSE
+		pr_info("\n%s: sflash status %x %x %x %x\n", __func__,
+					dma_buffer->data.sfstat[0],
+					dma_buffer->data.sfstat[1],
+					dma_buffer->data.sfstat[2],
+					dma_buffer->data.sfstat[3]);
+
+		pr_info("%s: controller_status = %x\n", __func__,
+					controller_status);
+		pr_info("%s: interrupt_status = %x\n", __func__,
+					interrupt_status);
+		pr_info("%s: write_prot_status = %x\n", __func__,
+					write_prot_status);
+#endif
+		/* Check for errors, protection violations etc */
+		if ((controller_status != 0)
+				|| (dma_buffer->data.sfstat[0] & 0x110)
+				|| (dma_buffer->data.sfstat[1] & 0x110)
+				|| (dma_buffer->data.sfstat[2] & 0x110)
+				|| (dma_buffer->data.sfstat[3] & 0x110)) {
+			pr_err("%s: ECC/MPU/OP error\n", __func__);
+			err = -EIO;
+		}
+
+		if (!(write_prot_status & ONENAND_WP_LS)) {
+			pr_err("%s: Unexpected status ofs = 0x%llx,"
+				"wp_status = %x\n",
+				__func__, ofs, write_prot_status);
+			err = -EIO;
+		}
+
+		if (err)
+			break;
+	}
+
+	msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+
+#if VERBOSE
+	pr_info("\n%s: ret %d\n", __func__, err);
+	pr_info("===================================================="
+			"=============\n");
+#endif
+	return err;
+}
+
+static int msm_onenand_suspend(struct mtd_info *mtd)
+{
+	return 0;
+}
+
+static void msm_onenand_resume(struct mtd_info *mtd)
+{
+}
+
+int msm_onenand_scan(struct mtd_info *mtd, int maxchips)
+{
+	struct msm_nand_chip *chip = mtd->priv;
+
+	/* Probe and check whether onenand device is present */
+	if (flash_onenand_probe(chip))
+		return -ENODEV;
+
+	mtd->size = 0x1000000 << ((onenand_info.device_id & 0xF0) >> 4);
+	mtd->writesize = onenand_info.data_buf_size;
+	mtd->oobsize = mtd->writesize >> 5;
+	mtd->erasesize = mtd->writesize << 6;
+	mtd->oobavail = msm_onenand_oob_64.oobavail;
+	mtd->ecclayout = &msm_onenand_oob_64;
+
+	mtd->type = MTD_NANDFLASH;
+	mtd->flags = MTD_CAP_NANDFLASH;
+	mtd->_erase = msm_onenand_erase;
+	mtd->_point = NULL;
+	mtd->_unpoint = NULL;
+	mtd->_read = msm_onenand_read;
+	mtd->_write = msm_onenand_write;
+	mtd->_read_oob = msm_onenand_read_oob;
+	mtd->_write_oob = msm_onenand_write_oob;
+	mtd->_lock = msm_onenand_lock;
+	mtd->_unlock = msm_onenand_unlock;
+	mtd->_suspend = msm_onenand_suspend;
+	mtd->_resume = msm_onenand_resume;
+	mtd->_block_isbad = msm_onenand_block_isbad;
+	mtd->_block_markbad = msm_onenand_block_markbad;
+	mtd->owner = THIS_MODULE;
+
+	pr_info("Found a supported onenand device\n");
+
+	return 0;
+}
+
+/**
+ * msm_nand_scan - [msm_nand Interface] Scan for the msm_nand device
+ * @param mtd		MTD device structure
+ * @param maxchips	Number of chips to scan for
+ *
+ * This fills out all the not initialized function pointers
+ * with the defaults.
+ * The flash ID is read and the mtd/chip structures are
+ * filled with the appropriate values.
+ */
+int msm_nand_scan(struct mtd_info *mtd, int maxchips)
+{
+	struct msm_nand_chip *chip = mtd->priv;
+	uint32_t flash_id = 0, i, mtd_writesize;
+	uint8_t dev_found = 0;
+	uint8_t wide_bus;
+	uint32_t manid;
+	uint32_t devid;
+	uint32_t devcfg;
+	struct nand_flash_dev *flashdev = NULL;
+	struct nand_manufacturers  *flashman = NULL;
+
+	/* Probe the Flash device for ONFI compliance */
+	if (!flash_onfi_probe(chip)) {
+		dev_found = 1;
+	} else {
+		/* Read the Flash ID from the Nand Flash Device */
+		flash_id = flash_read_id(chip);
+		manid = flash_id & 0xFF;
+		devid = (flash_id >> 8) & 0xFF;
+		devcfg = (flash_id >> 24) & 0xFF;
+
+		for (i = 0; !flashman && nand_manuf_ids[i].id; ++i)
+			if (nand_manuf_ids[i].id == manid)
+				flashman = &nand_manuf_ids[i];
+		for (i = 0; !flashdev && nand_flash_ids[i].id; ++i)
+			if (nand_flash_ids[i].id == devid)
+				flashdev = &nand_flash_ids[i];
+		if (!flashdev || !flashman) {
+			pr_err("ERROR: unknown nand device manuf=%x devid=%x\n",
+				manid, devid);
+			return -ENOENT;
+		} else
+			dev_found = 1;
+
+		if (!flashdev->pagesize) {
+			supported_flash.flash_id = flash_id;
+			supported_flash.density = flashdev->chipsize << 20;
+			supported_flash.widebus = devcfg & (1 << 6) ? 1 : 0;
+			supported_flash.pagesize = 1024 << (devcfg & 0x3);
+			supported_flash.blksize = (64 * 1024) <<
+							((devcfg >> 4) & 0x3);
+			supported_flash.oobsize = (8 << ((devcfg >> 2) & 0x3)) *
+				(supported_flash.pagesize >> 9);
+
+			if ((supported_flash.oobsize > 64) &&
+				(supported_flash.pagesize == 2048)) {
+				pr_info("msm_nand: Found a 2K page device with"
+					" %d oobsize - changing oobsize to 64 "
+					"bytes.\n", supported_flash.oobsize);
+				supported_flash.oobsize = 64;
+			}
+		} else {
+			supported_flash.flash_id = flash_id;
+			supported_flash.density = flashdev->chipsize << 20;
+			supported_flash.widebus = flashdev->options &
+					 NAND_BUSWIDTH_16 ? 1 : 0;
+			supported_flash.pagesize = flashdev->pagesize;
+			supported_flash.blksize = flashdev->erasesize;
+			supported_flash.oobsize = flashdev->pagesize >> 5;
+		}
+	}
+
+	if (dev_found) {
+		(!interleave_enable) ? (i = 1) : (i = 2);
+		wide_bus       = supported_flash.widebus;
+		mtd->size      = supported_flash.density  * i;
+		mtd->writesize = supported_flash.pagesize * i;
+		mtd->oobsize   = supported_flash.oobsize  * i;
+		mtd->erasesize = supported_flash.blksize  * i;
+
+		if (!interleave_enable)
+			mtd_writesize = mtd->writesize;
+		else
+			mtd_writesize = mtd->writesize >> 1;
+
+		/* Check whether controller and NAND device support 8bit ECC*/
+		if ((flash_rd_reg(chip, MSM_NAND_HW_INFO) == 0x307)
+				&& (supported_flash.ecc_correctability >= 8)) {
+			pr_info("Found supported NAND device for %dbit ECC\n",
+					supported_flash.ecc_correctability);
+			enable_bch_ecc = 1;
+		} else {
+			pr_info("Found a supported NAND device\n");
+		}
+		pr_info("NAND Id  : 0x%x\n", supported_flash.flash_id);
+		pr_info("Buswidth : %d Bits\n", (wide_bus) ? 16 : 8);
+		pr_info("Density  : %lld MByte\n", (mtd->size>>20));
+		pr_info("Pagesize : %d Bytes\n", mtd->writesize);
+		pr_info("Erasesize: %d Bytes\n", mtd->erasesize);
+		pr_info("Oobsize  : %d Bytes\n", mtd->oobsize);
+	} else {
+		pr_err("Unsupported Nand,Id: 0x%x \n", flash_id);
+		return -ENODEV;
+	}
+
+	/* Size of each codeword is 532Bytes incase of 8bit BCH ECC*/
+	chip->cw_size = enable_bch_ecc ? 532 : 528;
+	chip->CFG0 = (((mtd_writesize >> 9)-1) << 6) /* 4/8 cw/pg for 2/4k */
+		|  (516 <<  9)  /* 516 user data bytes */
+		|   (10 << 19)  /* 10 parity bytes */
+		|    (5 << 27)  /* 5 address cycles */
+		|    (0 << 30)  /* Do not read status before data */
+		|    (1 << 31)  /* Send read cmd */
+		/* 0 spare bytes for 16 bit nand or 1/2 spare bytes for 8 bit */
+		| (wide_bus ? 0 << 23 : (enable_bch_ecc ? 2 << 23 : 1 << 23));
+
+	chip->CFG1 = (0 <<  0)  /* Enable ecc */
+		|    (7 <<  2)  /* 8 recovery cycles */
+		|    (0 <<  5)  /* Allow CS deassertion */
+		/* Bad block marker location */
+		|  ((mtd_writesize - (chip->cw_size * (
+					(mtd_writesize >> 9) - 1)) + 1) <<  6)
+		|    (0 << 16)  /* Bad block in user data area */
+		|    (2 << 17)  /* 6 cycle tWB/tRB */
+		| ((wide_bus) ? CFG1_WIDE_FLASH : 0); /* Wide flash bit */
+
+	chip->ecc_buf_cfg = 0x203;
+	chip->CFG0_RAW = 0xA80420C0;
+	chip->CFG1_RAW = 0x5045D;
+
+	if (enable_bch_ecc) {
+		chip->CFG1 |= (1 << 27); /* Enable BCH engine */
+		chip->ecc_bch_cfg = (0 << 0) /* Enable ECC*/
+			|   (0 << 1) /* Enable/Disable SW reset of ECC engine */
+			|   (1 << 4) /* 8bit ecc*/
+			|   ((wide_bus) ? (14 << 8) : (13 << 8))/*parity bytes*/
+			|   (516 << 16) /* 516 user data bytes */
+			|   (1 << 30); /* Turn on ECC engine clocks always */
+		chip->CFG0_RAW = 0xA80428C0; /* CW size is increased to 532B */
+	}
+
+	/*
+	 * For 4bit RS ECC (default ECC), parity bytes = 10 (for x8 and x16 I/O)
+	 * For 8bit BCH ECC, parity bytes = 13 (x8) or 14 (x16 I/O).
+	 */
+	chip->ecc_parity_bytes = enable_bch_ecc ? (wide_bus ? 14 : 13) : 10;
+
+	pr_info("CFG0 Init  : 0x%08x\n", chip->CFG0);
+	pr_info("CFG1 Init  : 0x%08x\n", chip->CFG1);
+	pr_info("ECCBUFCFG  : 0x%08x\n", chip->ecc_buf_cfg);
+
+	if (mtd->oobsize == 64) {
+		mtd->oobavail = msm_nand_oob_64.oobavail;
+		mtd->ecclayout = &msm_nand_oob_64;
+	} else if (mtd->oobsize == 128) {
+		mtd->oobavail = msm_nand_oob_128.oobavail;
+		mtd->ecclayout = &msm_nand_oob_128;
+	} else if (mtd->oobsize == 224) {
+		mtd->oobavail = wide_bus ? msm_nand_oob_224_x16.oobavail :
+			msm_nand_oob_224_x8.oobavail;
+		mtd->ecclayout = wide_bus ? &msm_nand_oob_224_x16 :
+			&msm_nand_oob_224_x8;
+	} else if (mtd->oobsize == 256) {
+		mtd->oobavail = msm_nand_oob_256.oobavail;
+		mtd->ecclayout = &msm_nand_oob_256;
+	} else {
+		pr_err("Unsupported Nand, oobsize: 0x%x \n",
+		       mtd->oobsize);
+		return -ENODEV;
+	}
+
+	/* Fill in remaining MTD driver data */
+	mtd->type = MTD_NANDFLASH;
+	mtd->flags = MTD_CAP_NANDFLASH;
+	/* mtd->ecctype = MTD_ECC_SW; */
+	mtd->_erase = msm_nand_erase;
+	mtd->_block_isbad = msm_nand_block_isbad;
+	mtd->_block_markbad = msm_nand_block_markbad;
+	mtd->_point = NULL;
+	mtd->_unpoint = NULL;
+	mtd->_read = msm_nand_read;
+	mtd->_write = msm_nand_write;
+	mtd->_read_oob  = msm_nand_read_oob;
+	mtd->_write_oob = msm_nand_write_oob;
+	if (dual_nand_ctlr_present) {
+		mtd->_read_oob = msm_nand_read_oob_dualnandc;
+		mtd->_write_oob = msm_nand_write_oob_dualnandc;
+		if (interleave_enable) {
+			mtd->_erase = msm_nand_erase_dualnandc;
+			mtd->_block_isbad = msm_nand_block_isbad_dualnandc;
+		}
+	}
+
+	/* mtd->sync = msm_nand_sync; */
+	mtd->_lock = NULL;
+	/* mtd->_unlock = msm_nand_unlock; */
+	mtd->_suspend = msm_nand_suspend;
+	mtd->_resume = msm_nand_resume;
+	mtd->owner = THIS_MODULE;
+
+	/* Unlock whole block */
+	/* msm_nand_unlock_all(mtd); */
+
+	/* return this->scan_bbt(mtd); */
+	return 0;
+}
+EXPORT_SYMBOL_GPL(msm_nand_scan);
+
+/**
+ * msm_nand_release - [msm_nand Interface] Free resources held by the msm_nand device
+ * @param mtd		MTD device structure
+ */
+void msm_nand_release(struct mtd_info *mtd)
+{
+	/* struct msm_nand_chip *this = mtd->priv; */
+
+	/* Deregister the device */
+	mtd_device_unregister(mtd);
+}
+EXPORT_SYMBOL_GPL(msm_nand_release);
+
+struct msm_nand_info {
+	struct mtd_info		mtd;
+	struct mtd_partition	*parts;
+	struct msm_nand_chip	msm_nand;
+};
+
+/* duplicating the NC01 XFR contents to NC10 */
+static int msm_nand_nc10_xfr_settings(struct mtd_info *mtd)
+{
+	struct msm_nand_chip *chip = mtd->priv;
+
+	struct {
+		dmov_s cmd[2];
+		unsigned cmdptr;
+	} *dma_buffer;
+	dmov_s *cmd;
+
+	wait_event(chip->wait_queue,
+		(dma_buffer = msm_nand_get_dma_buffer(
+				chip, sizeof(*dma_buffer))));
+
+	cmd = dma_buffer->cmd;
+
+	/* Copying XFR register contents from NC01 --> NC10 */
+	cmd->cmd = 0;
+	cmd->src = NC01(MSM_NAND_XFR_STEP1);
+	cmd->dst = NC10(MSM_NAND_XFR_STEP1);
+	cmd->len = 28;
+	cmd++;
+
+	BUILD_BUG_ON(2 != ARRAY_SIZE(dma_buffer->cmd));
+	BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
+	dma_buffer->cmd[0].cmd |= CMD_OCB;
+	cmd[-1].cmd |= CMD_OCU | CMD_LC;
+	dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3)
+				| CMD_PTR_LP;
+
+	mb();
+	msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST
+			| DMOV_CMD_ADDR(msm_virt_to_dma(chip,
+			&dma_buffer->cmdptr)));
+	mb();
+	msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+	return 0;
+}
+
+static int setup_mtd_device(struct platform_device *pdev,
+			     struct msm_nand_info *info)
+{
+	int i, err;
+	struct flash_platform_data *pdata = pdev->dev.platform_data;
+
+	if (pdata) {
+		for (i = 0; i < pdata->nr_parts; i++) {
+			pdata->parts[i].offset = pdata->parts[i].offset
+				* info->mtd.erasesize;
+			pdata->parts[i].size = pdata->parts[i].size
+				* info->mtd.erasesize;
+		}
+		err = mtd_device_register(&info->mtd, pdata->parts,
+				pdata->nr_parts);
+	} else {
+		err = mtd_device_register(&info->mtd, NULL, 0);
+	}
+	return err;
+}
+
+static int __devinit msm_nand_probe(struct platform_device *pdev)
+{
+	struct msm_nand_info *info;
+	struct resource *res;
+	int err;
+	struct flash_platform_data *plat_data;
+
+	plat_data = pdev->dev.platform_data;
+
+	res = platform_get_resource_byname(pdev,
+					IORESOURCE_MEM, "msm_nand_phys");
+	if (!res || !res->start) {
+		pr_err("%s: msm_nand_phys resource invalid/absent\n",
+				__func__);
+		return -ENODEV;
+	}
+	msm_nand_phys = res->start;
+	pr_info("%s: phys addr 0x%lx \n", __func__, msm_nand_phys);
+
+	res = platform_get_resource_byname(pdev,
+					IORESOURCE_MEM, "msm_nandc01_phys");
+	if (!res || !res->start)
+		goto no_dual_nand_ctlr_support;
+	msm_nandc01_phys = res->start;
+
+	res = platform_get_resource_byname(pdev,
+					IORESOURCE_MEM, "msm_nandc10_phys");
+	if (!res || !res->start)
+		goto no_dual_nand_ctlr_support;
+	msm_nandc10_phys = res->start;
+
+	res = platform_get_resource_byname(pdev,
+					IORESOURCE_MEM, "msm_nandc11_phys");
+	if (!res || !res->start)
+		goto no_dual_nand_ctlr_support;
+	msm_nandc11_phys = res->start;
+
+	res = platform_get_resource_byname(pdev,
+					IORESOURCE_MEM, "ebi2_reg_base");
+	if (!res || !res->start)
+		goto no_dual_nand_ctlr_support;
+	ebi2_register_base = res->start;
+
+	dual_nand_ctlr_present = 1;
+	if (plat_data != NULL)
+		interleave_enable = plat_data->interleave;
+	else
+		interleave_enable = 0;
+
+	if (!interleave_enable)
+		pr_info("%s: Dual Nand Ctrl in ping-pong mode\n", __func__);
+	else
+		pr_info("%s: Dual Nand Ctrl in interleave mode\n", __func__);
+
+no_dual_nand_ctlr_support:
+	res = platform_get_resource_byname(pdev,
+					IORESOURCE_DMA, "msm_nand_dmac");
+	if (!res || !res->start) {
+		pr_err("%s: invalid msm_nand_dmac resource\n", __func__);
+		return -ENODEV;
+	}
+
+	info = kzalloc(sizeof(struct msm_nand_info), GFP_KERNEL);
+	if (!info) {
+		pr_err("%s: No memory for msm_nand_info\n", __func__);
+		return -ENOMEM;
+	}
+
+	info->msm_nand.dev = &pdev->dev;
+
+	init_waitqueue_head(&info->msm_nand.wait_queue);
+
+	info->msm_nand.dma_channel = res->start;
+	pr_info("%s: dmac 0x%x\n", __func__, info->msm_nand.dma_channel);
+
+	/* this currently fails if dev is passed in */
+	info->msm_nand.dma_buffer =
+		dma_alloc_coherent(/*dev*/ NULL, MSM_NAND_DMA_BUFFER_SIZE,
+				&info->msm_nand.dma_addr, GFP_KERNEL);
+	if (info->msm_nand.dma_buffer == NULL) {
+		pr_err("%s: No memory for msm_nand.dma_buffer\n", __func__);
+		err = -ENOMEM;
+		goto out_free_info;
+	}
+
+	pr_info("%s: allocated dma buffer at %p, dma_addr %x\n",
+		__func__, info->msm_nand.dma_buffer, info->msm_nand.dma_addr);
+
+	info->mtd.name = dev_name(&pdev->dev);
+	info->mtd.priv = &info->msm_nand;
+	info->mtd.owner = THIS_MODULE;
+
+	/* config ebi2_cfg register only for ping pong mode!!! */
+	if (!interleave_enable && dual_nand_ctlr_present)
+		flash_wr_reg(&info->msm_nand, EBI2_CFG_REG, 0x4010080);
+
+	if (dual_nand_ctlr_present)
+		msm_nand_nc10_xfr_settings(&info->mtd);
+
+	if (msm_nand_scan(&info->mtd, 1))
+		if (msm_onenand_scan(&info->mtd, 1)) {
+			pr_err("%s: No nand device found\n", __func__);
+			err = -ENXIO;
+			goto out_free_dma_buffer;
+		}
+
+	err = setup_mtd_device(pdev, info);
+	if (err < 0) {
+		pr_err("%s: setup_mtd_device failed with err=%d\n",
+				__func__, err);
+		goto out_free_dma_buffer;
+	}
+
+	dev_set_drvdata(&pdev->dev, info);
+
+	return 0;
+
+out_free_dma_buffer:
+	dma_free_coherent(NULL, MSM_NAND_DMA_BUFFER_SIZE,
+			info->msm_nand.dma_buffer,
+			info->msm_nand.dma_addr);
+out_free_info:
+	kfree(info);
+
+	return err;
+}
+
+static int __devexit msm_nand_remove(struct platform_device *pdev)
+{
+	struct msm_nand_info *info = dev_get_drvdata(&pdev->dev);
+
+	dev_set_drvdata(&pdev->dev, NULL);
+
+	if (info) {
+		msm_nand_release(&info->mtd);
+		dma_free_coherent(NULL, MSM_NAND_DMA_BUFFER_SIZE,
+				  info->msm_nand.dma_buffer,
+				  info->msm_nand.dma_addr);
+		kfree(info);
+	}
+
+	return 0;
+}
+
+#define DRIVER_NAME "msm_nand"
+
+static struct platform_driver msm_nand_driver = {
+	.probe		= msm_nand_probe,
+	.remove		= __devexit_p(msm_nand_remove),
+	.driver = {
+		.name		= DRIVER_NAME,
+	}
+};
+
+MODULE_ALIAS(DRIVER_NAME);
+
+static int __init msm_nand_init(void)
+{
+	return platform_driver_register(&msm_nand_driver);
+}
+
+static void __exit msm_nand_exit(void)
+{
+	platform_driver_unregister(&msm_nand_driver);
+}
+
+module_init(msm_nand_init);
+module_exit(msm_nand_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("msm_nand flash driver code");
diff --git a/drivers/mtd/devices/msm_nand.h b/drivers/mtd/devices/msm_nand.h
new file mode 100644
index 0000000..2729c6b
--- /dev/null
+++ b/drivers/mtd/devices/msm_nand.h
@@ -0,0 +1,195 @@
+/* drivers/mtd/devices/msm_nand.h
+ *
+ * Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (C) 2007 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.
+ *
+ */
+
+#ifndef __DRIVERS_MTD_DEVICES_MSM_NAND_H
+#define __DRIVERS_MTD_DEVICES_MSM_NAND_H
+
+#include <mach/msm_iomap.h>
+
+extern unsigned long msm_nand_phys;
+extern unsigned long msm_nandc01_phys;
+extern unsigned long msm_nandc10_phys;
+extern unsigned long msm_nandc11_phys;
+extern unsigned long ebi2_register_base;
+
+#define NC01(X) ((X) + msm_nandc01_phys - msm_nand_phys)
+#define NC10(X) ((X) + msm_nandc10_phys - msm_nand_phys)
+#define NC11(X) ((X) + msm_nandc11_phys - msm_nand_phys)
+
+#define MSM_NAND_REG(off) (msm_nand_phys + (off))
+
+#define MSM_NAND_FLASH_CMD            MSM_NAND_REG(0x0000)
+#define MSM_NAND_ADDR0                MSM_NAND_REG(0x0004)
+#define MSM_NAND_ADDR1                MSM_NAND_REG(0x0008)
+#define MSM_NAND_FLASH_CHIP_SELECT    MSM_NAND_REG(0x000C)
+#define MSM_NAND_EXEC_CMD             MSM_NAND_REG(0x0010)
+#define MSM_NAND_FLASH_STATUS         MSM_NAND_REG(0x0014)
+#define MSM_NAND_BUFFER_STATUS        MSM_NAND_REG(0x0018)
+#define MSM_NAND_SFLASHC_STATUS       MSM_NAND_REG(0x001C)
+#define MSM_NAND_DEV0_CFG0            MSM_NAND_REG(0x0020)
+#define MSM_NAND_DEV0_CFG1            MSM_NAND_REG(0x0024)
+#define MSM_NAND_DEV0_ECC_CFG	      MSM_NAND_REG(0x0028)
+#define MSM_NAND_DEV1_ECC_CFG	      MSM_NAND_REG(0x002C)
+#define MSM_NAND_DEV1_CFG0            MSM_NAND_REG(0x0030)
+#define MSM_NAND_DEV1_CFG1            MSM_NAND_REG(0x0034)
+#define MSM_NAND_SFLASHC_CMD          MSM_NAND_REG(0x0038)
+#define MSM_NAND_SFLASHC_EXEC_CMD     MSM_NAND_REG(0x003C)
+#define MSM_NAND_READ_ID              MSM_NAND_REG(0x0040)
+#define MSM_NAND_READ_STATUS          MSM_NAND_REG(0x0044)
+#define MSM_NAND_CONFIG_DATA          MSM_NAND_REG(0x0050)
+#define MSM_NAND_CONFIG               MSM_NAND_REG(0x0054)
+#define MSM_NAND_CONFIG_MODE          MSM_NAND_REG(0x0058)
+#define MSM_NAND_CONFIG_STATUS        MSM_NAND_REG(0x0060)
+#define MSM_NAND_MACRO1_REG           MSM_NAND_REG(0x0064)
+#define MSM_NAND_XFR_STEP1            MSM_NAND_REG(0x0070)
+#define MSM_NAND_XFR_STEP2            MSM_NAND_REG(0x0074)
+#define MSM_NAND_XFR_STEP3            MSM_NAND_REG(0x0078)
+#define MSM_NAND_XFR_STEP4            MSM_NAND_REG(0x007C)
+#define MSM_NAND_XFR_STEP5            MSM_NAND_REG(0x0080)
+#define MSM_NAND_XFR_STEP6            MSM_NAND_REG(0x0084)
+#define MSM_NAND_XFR_STEP7            MSM_NAND_REG(0x0088)
+#define MSM_NAND_GENP_REG0            MSM_NAND_REG(0x0090)
+#define MSM_NAND_GENP_REG1            MSM_NAND_REG(0x0094)
+#define MSM_NAND_GENP_REG2            MSM_NAND_REG(0x0098)
+#define MSM_NAND_GENP_REG3            MSM_NAND_REG(0x009C)
+#define MSM_NAND_DEV_CMD0             MSM_NAND_REG(0x00A0)
+#define MSM_NAND_DEV_CMD1             MSM_NAND_REG(0x00A4)
+#define MSM_NAND_DEV_CMD2             MSM_NAND_REG(0x00A8)
+#define MSM_NAND_DEV_CMD_VLD          MSM_NAND_REG(0x00AC)
+#define MSM_NAND_EBI2_MISR_SIG_REG    MSM_NAND_REG(0x00B0)
+#define MSM_NAND_ADDR2                MSM_NAND_REG(0x00C0)
+#define MSM_NAND_ADDR3                MSM_NAND_REG(0x00C4)
+#define MSM_NAND_ADDR4                MSM_NAND_REG(0x00C8)
+#define MSM_NAND_ADDR5                MSM_NAND_REG(0x00CC)
+#define MSM_NAND_DEV_CMD3             MSM_NAND_REG(0x00D0)
+#define MSM_NAND_DEV_CMD4             MSM_NAND_REG(0x00D4)
+#define MSM_NAND_DEV_CMD5             MSM_NAND_REG(0x00D8)
+#define MSM_NAND_DEV_CMD6             MSM_NAND_REG(0x00DC)
+#define MSM_NAND_SFLASHC_BURST_CFG    MSM_NAND_REG(0x00E0)
+#define MSM_NAND_ADDR6                MSM_NAND_REG(0x00E4)
+#define MSM_NAND_EBI2_ECC_BUF_CFG     MSM_NAND_REG(0x00F0)
+#define MSM_NAND_HW_INFO	      MSM_NAND_REG(0x00FC)
+#define MSM_NAND_FLASH_BUFFER         MSM_NAND_REG(0x0100)
+
+/* device commands */
+
+#define MSM_NAND_CMD_SOFT_RESET         0x01
+#define MSM_NAND_CMD_PAGE_READ          0x32
+#define MSM_NAND_CMD_PAGE_READ_ECC      0x33
+#define MSM_NAND_CMD_PAGE_READ_ALL      0x34
+#define MSM_NAND_CMD_SEQ_PAGE_READ      0x15
+#define MSM_NAND_CMD_PRG_PAGE           0x36
+#define MSM_NAND_CMD_PRG_PAGE_ECC       0x37
+#define MSM_NAND_CMD_PRG_PAGE_ALL       0x39
+#define MSM_NAND_CMD_BLOCK_ERASE        0x3A
+#define MSM_NAND_CMD_FETCH_ID           0x0B
+#define MSM_NAND_CMD_STATUS             0x0C
+#define MSM_NAND_CMD_RESET              0x0D
+
+/* Sflash Commands */
+
+#define MSM_NAND_SFCMD_DATXS            0x0
+#define MSM_NAND_SFCMD_CMDXS            0x1
+#define MSM_NAND_SFCMD_BURST            0x0
+#define MSM_NAND_SFCMD_ASYNC            0x1
+#define MSM_NAND_SFCMD_ABORT            0x1
+#define MSM_NAND_SFCMD_REGRD            0x2
+#define MSM_NAND_SFCMD_REGWR            0x3
+#define MSM_NAND_SFCMD_INTLO            0x4
+#define MSM_NAND_SFCMD_INTHI            0x5
+#define MSM_NAND_SFCMD_DATRD            0x6
+#define MSM_NAND_SFCMD_DATWR            0x7
+
+#define SFLASH_PREPCMD(numxfr, offval, delval, trnstp, mode, opcode) \
+	((numxfr<<20)|(offval<<12)|(delval<<6)|(trnstp<<5)|(mode<<4)|opcode)
+
+#define SFLASH_BCFG			0x20100327
+
+/* Onenand addresses */
+
+#define ONENAND_MANUFACTURER_ID		0xF000
+#define ONENAND_DEVICE_ID		0xF001
+#define ONENAND_VERSION_ID		0xF002
+#define ONENAND_DATA_BUFFER_SIZE	0xF003
+#define ONENAND_BOOT_BUFFER_SIZE	0xF004
+#define ONENAND_AMOUNT_OF_BUFFERS	0xF005
+#define ONENAND_TECHNOLOGY		0xF006
+#define ONENAND_START_ADDRESS_1		0xF100
+#define ONENAND_START_ADDRESS_2		0xF101
+#define ONENAND_START_ADDRESS_3		0xF102
+#define ONENAND_START_ADDRESS_4		0xF103
+#define ONENAND_START_ADDRESS_5		0xF104
+#define ONENAND_START_ADDRESS_6		0xF105
+#define ONENAND_START_ADDRESS_7		0xF106
+#define ONENAND_START_ADDRESS_8		0xF107
+#define ONENAND_START_BUFFER		0xF200
+#define ONENAND_COMMAND			0xF220
+#define ONENAND_SYSTEM_CONFIG_1		0xF221
+#define ONENAND_SYSTEM_CONFIG_2		0xF222
+#define ONENAND_CONTROLLER_STATUS	0xF240
+#define ONENAND_INTERRUPT_STATUS	0xF241
+#define ONENAND_START_BLOCK_ADDRESS	0xF24C
+#define ONENAND_WRITE_PROT_STATUS	0xF24E
+#define ONENAND_ECC_STATUS		0xFF00
+#define ONENAND_ECC_ERRPOS_MAIN0	0xFF01
+#define ONENAND_ECC_ERRPOS_SPARE0	0xFF02
+#define ONENAND_ECC_ERRPOS_MAIN1	0xFF03
+#define ONENAND_ECC_ERRPOS_SPARE1	0xFF04
+#define ONENAND_ECC_ERRPOS_MAIN2	0xFF05
+#define ONENAND_ECC_ERRPOS_SPARE2	0xFF06
+#define ONENAND_ECC_ERRPOS_MAIN3	0xFF07
+#define ONENAND_ECC_ERRPOS_SPARE3	0xFF08
+
+/* Onenand commands */
+#define ONENAND_WP_US                   (1 << 2)
+#define ONENAND_WP_LS                   (1 << 1)
+
+#define ONENAND_CMDLOAD			0x0000
+#define ONENAND_CMDLOADSPARE		0x0013
+#define ONENAND_CMDPROG			0x0080
+#define ONENAND_CMDPROGSPARE		0x001A
+#define ONENAND_CMDERAS			0x0094
+#define ONENAND_CMD_UNLOCK              0x0023
+#define ONENAND_CMD_LOCK                0x002A
+
+#define ONENAND_SYSCFG1_ECCENA(mode)	(0x40E0 | (mode ? 0 : 0x8002))
+#define ONENAND_SYSCFG1_ECCDIS(mode)	(0x41E0 | (mode ? 0 : 0x8002))
+
+#define ONENAND_CLRINTR			0x0000
+#define ONENAND_STARTADDR1_RES		0x07FF
+#define ONENAND_STARTADDR3_RES		0x07FF
+
+#define DATARAM0_0			0x8
+#define DEVICE_FLASHCORE_0              (0 << 15)
+#define DEVICE_FLASHCORE_1              (1 << 15)
+#define DEVICE_BUFFERRAM_0              (0 << 15)
+#define DEVICE_BUFFERRAM_1              (1 << 15)
+#define ONENAND_DEVICE_IS_DDP		(1 << 3)
+
+#define CLEAN_DATA_16			0xFFFF
+#define CLEAN_DATA_32			0xFFFFFFFF
+
+#define EBI2_REG(off)   		(ebi2_register_base + (off))
+#define EBI2_CHIP_SELECT_CFG0           EBI2_REG(0x0000)
+#define EBI2_CFG_REG		       	EBI2_REG(0x0004)
+#define EBI2_NAND_ADM_MUX       	EBI2_REG(0x005C)
+
+#define MSM_NAND_BUF_STAT_UNCRCTBL_ERR	(1 << 8)
+#define MSM_NAND_BUF_STAT_NUM_ERR_MASK	(enable_bch_ecc ? 0x1F : 0x0F)
+
+extern struct flash_platform_data msm_nand_data;
+
+#endif
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index f1f0671..11c9dce 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -426,6 +426,13 @@
 		goto error3;
 
 	new->rq->queuedata = new;
+
+	/*
+	 * Empirical measurements revealed that read ahead values larger than
+	 * 4 slowed down boot time, so start out with this small value.
+	 */
+	new->rq->backing_dev_info.ra_pages = (4 * 1024) / PAGE_CACHE_SIZE;
+
 	blk_queue_logical_block_size(new->rq, tr->blksize);
 
 	queue_flag_set_unlocked(QUEUE_FLAG_NONROT, new->rq);
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index f2f482b..4e12bb7 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -35,6 +35,7 @@
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
 #include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
 
 #include <asm/uaccess.h>
 
@@ -968,6 +969,9 @@
 
 	case ECCGETSTATS:
 	{
+#ifdef CONFIG_MTD_LAZYECCSTATS
+		part_fill_badblockstats(mtd);
+#endif
 		if (copy_to_user(argp, &mtd->ecc_stats,
 				 sizeof(struct mtd_ecc_stats)))
 			return -EFAULT;
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index 9651c06..0c634ca 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -313,6 +313,21 @@
 	kfree(p);
 }
 
+void part_fill_badblockstats(struct mtd_info *mtd)
+{
+	struct mtd_part *part = PART(mtd);
+	if (part->master->_block_isbad) {
+		uint64_t offs = 0;
+		mtd->ecc_stats.badblocks = 0;
+		while (offs < mtd->size) {
+			if (mtd_block_isbad(part->master,
+						offs + part->offset))
+				mtd->ecc_stats.badblocks++;
+			offs += mtd->erasesize;
+		}
+	}
+}
+
 /*
  * This function unregisters and destroy all slave MTD objects which are
  * attached to the given master MTD object.
@@ -517,6 +532,10 @@
 
 	slave->mtd.ecclayout = master->ecclayout;
 	slave->mtd.ecc_strength = master->ecc_strength;
+
+#ifndef CONFIG_MTD_LAZYECCSTATS
+	part_fill_badblockstats(&(slave->mtd));
+#endif
 	if (master->_block_isbad) {
 		uint64_t offs = 0;
 
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 47b19c0..315efbb 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -3219,6 +3219,44 @@
 }
 EXPORT_SYMBOL(nand_scan_ident);
 
+static void nand_panic_wait(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd->priv;
+	int i;
+
+	if (chip->state != FL_READY)
+		for (i = 0; i < 40; i++) {
+			if (chip->dev_ready(mtd))
+				break;
+			mdelay(10);
+		}
+	chip->state = FL_READY;
+}
+
+static int nand_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
+			    size_t *retlen, const u_char *buf)
+{
+	struct nand_chip *chip = mtd->priv;
+	int ret;
+
+	/* Do not allow reads past end of device */
+	if ((to + len) > mtd->size)
+		return -EINVAL;
+	if (!len)
+		return 0;
+
+	nand_panic_wait(mtd);
+
+	chip->ops.len = len;
+	chip->ops.datbuf = (uint8_t *)buf;
+	chip->ops.oobbuf = NULL;
+
+	ret = nand_do_write_ops(mtd, to, &chip->ops);
+
+	*retlen = chip->ops.retlen;
+	return ret;
+}
+
 
 /**
  * nand_scan_tail - [NAND Interface] Scan for the NAND device
diff --git a/drivers/mtd/tests/Makefile b/drivers/mtd/tests/Makefile
index b44dcab..8089d9d 100644
--- a/drivers/mtd/tests/Makefile
+++ b/drivers/mtd/tests/Makefile
@@ -6,3 +6,4 @@
 obj-$(CONFIG_MTD_TESTS) += mtd_subpagetest.o
 obj-$(CONFIG_MTD_TESTS) += mtd_torturetest.o
 obj-$(CONFIG_MTD_TESTS) += mtd_nandecctest.o
+obj-$(CONFIG_MTD_TESTS) += mtd_erasepart.o
diff --git a/drivers/mtd/tests/mtd_erasepart.c b/drivers/mtd/tests/mtd_erasepart.c
new file mode 100644
index 0000000..67f0f84
--- /dev/null
+++ b/drivers/mtd/tests/mtd_erasepart.c
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ * Copyright (C) 2006-2008 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Erase the given MTD partition.
+ *
+ */
+
+#include <asm/div64.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/err.h>
+#include <linux/mtd/mtd.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#define PRINT_PREF KERN_INFO "mtd_erasepart: "
+
+static int dev;
+module_param(dev, int, S_IRUGO);
+MODULE_PARM_DESC(dev, "MTD device number to use");
+
+static struct mtd_info *mtd;
+static unsigned char *bbt;
+static int ebcnt;
+
+static int erase_eraseblock(int ebnum)
+{
+	int err;
+	struct erase_info ei;
+	loff_t addr = ebnum * mtd->erasesize;
+
+	memset(&ei, 0, sizeof(struct erase_info));
+	ei.mtd  = mtd;
+	ei.addr = addr;
+	ei.len  = mtd->erasesize;
+
+	err = mtd_erase(mtd, &ei);
+	if (err) {
+		printk(PRINT_PREF "error %d while erasing EB %d\n", err, ebnum);
+		return err;
+	}
+
+	if (ei.state == MTD_ERASE_FAILED) {
+		printk(PRINT_PREF "some erase error occurred at EB %d\n",
+		       ebnum);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int erase_whole_device(void)
+{
+	int err;
+	unsigned int i;
+
+	printk(PRINT_PREF "erasing whole device\n");
+	for (i = 0; i < ebcnt; ++i) {
+		if (bbt[i])
+			continue;
+		err = erase_eraseblock(i);
+		if (err)
+			return err;
+		cond_resched();
+	}
+	printk(PRINT_PREF "erased %u eraseblocks\n", i);
+	return 0;
+}
+
+static int is_block_bad(int ebnum)
+{
+	int ret;
+	loff_t addr = ebnum * mtd->erasesize;
+
+	ret = mtd_block_isbad(mtd, addr);
+	if (ret)
+		printk(PRINT_PREF "block %d is bad\n", ebnum);
+	return ret;
+}
+
+static int scan_for_bad_eraseblocks(void)
+{
+	int i, bad = 0;
+
+	bbt = kmalloc(ebcnt, GFP_KERNEL);
+	if (!bbt) {
+		printk(PRINT_PREF "error: cannot allocate memory\n");
+		return -ENOMEM;
+	}
+	memset(bbt, 0 , ebcnt);
+
+	printk(PRINT_PREF "scanning for bad eraseblocks\n");
+	for (i = 0; i < ebcnt; ++i) {
+		bbt[i] = is_block_bad(i) ? 1 : 0;
+		if (bbt[i])
+			bad += 1;
+		cond_resched();
+	}
+	printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad);
+	return 0;
+}
+
+static int __init mtd_erasepart_init(void)
+{
+	int err = 0;
+	uint64_t tmp;
+
+	printk(KERN_INFO "\n");
+	printk(KERN_INFO "=================================================\n");
+	printk(PRINT_PREF "MTD device: %d\n", dev);
+
+	mtd = get_mtd_device(NULL, dev);
+	if (IS_ERR(mtd)) {
+		err = PTR_ERR(mtd);
+		printk(PRINT_PREF "error: cannot get MTD device\n");
+		return err;
+	}
+
+	if (mtd->type != MTD_NANDFLASH) {
+		printk(PRINT_PREF "this test requires NAND flash\n");
+		err = -ENODEV;
+		goto out2;
+	}
+
+	tmp = mtd->size;
+	do_div(tmp, mtd->erasesize);
+	ebcnt = tmp;
+
+	printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, "
+	       "page size %u, count of eraseblocks %u",
+	       (unsigned long long)mtd->size, mtd->erasesize,
+	       mtd->writesize, ebcnt);
+
+	err = scan_for_bad_eraseblocks();
+	if (err)
+		goto out1;
+
+	printk(PRINT_PREF "Erasing the whole mtd partition\n");
+
+	err = erase_whole_device();
+out1:
+	kfree(bbt);
+out2:
+	put_mtd_device(mtd);
+	if (err)
+		printk(PRINT_PREF "error %d occurred\n", err);
+	printk(KERN_INFO "=================================================\n");
+	return err;
+}
+module_init(mtd_erasepart_init);
+
+static void __exit mtd_erasepart_exit(void)
+{
+	return;
+}
+module_exit(mtd_erasepart_exit);
+
+MODULE_DESCRIPTION("Erase a given MTD partition");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index c63a64c..c82f7d1 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -98,6 +98,7 @@
 	  emulated by the MIPS Simulator.
 	  If you are not using a MIPSsim or are unsure, say N.
 
+source "drivers/net/ethernet/msm/Kconfig"
 source "drivers/net/ethernet/myricom/Kconfig"
 
 config FEALNX
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index 9676a51..50fdf5e 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -41,6 +41,7 @@
 obj-$(CONFIG_NET_VENDOR_MICREL) += micrel/
 obj-$(CONFIG_NET_VENDOR_MICROCHIP) += microchip/
 obj-$(CONFIG_MIPS_SIM_NET) += mipsnet.o
+obj-$(CONFIG_ARCH_MSM) += msm/
 obj-$(CONFIG_NET_VENDOR_MYRI) += myricom/
 obj-$(CONFIG_FEALNX) += fealnx.o
 obj-$(CONFIG_NET_VENDOR_NATSEMI) += natsemi/
diff --git a/drivers/net/ethernet/msm/Kconfig b/drivers/net/ethernet/msm/Kconfig
new file mode 100644
index 0000000..095cb4d
--- /dev/null
+++ b/drivers/net/ethernet/msm/Kconfig
@@ -0,0 +1,52 @@
+#
+# msm network device configuration
+#
+
+config MSM_RMNET
+	tristate "MSM RMNET Virtual Network Device"
+	depends on ARCH_MSM
+	default y
+	help
+	  Virtual ethernet interface for MSM RMNET transport.
+
+config MSM_RMNET_SDIO
+	bool "RMNET SDIO Driver"
+	depends on MSM_SDIO_DMUX
+	default n
+	help
+	  Implements RMNET over SDIO interface.
+
+config MSM_RMNET_BAM
+	bool "RMNET BAM Driver"
+	depends on MSM_BAM_DMUX
+	default n
+	help
+	  Implements RMNET over BAM interface.
+	  RMNET provides a virtual ethernet interface
+	  for routing IP packets within the MSM using
+	  BAM as a physical transport.
+
+config MSM_RMNET_SMUX
+	bool "RMNET SMUX Driver"
+	depends on N_SMUX
+	help
+	  Implements RMNET over SMUX interface.
+	  RMNET provides a virtual ethernet interface
+	  for routing IP packets within the MSM using
+	  HSUART as a physical transport.
+
+config MSM_RMNET_DEBUG
+	bool "MSM RMNET debug interface"
+	depends on MSM_RMNET
+	default n
+	help
+	  Debug stats on wakeup counts.
+
+config QFEC
+	tristate "QFEC ethernet driver"
+	select MII
+	depends on ARM
+	help
+	  This driver supports Ethernet in the FSM9xxx.
+	  To compile this driver as a module, choose M here: the
+	  module will be called qfec.
diff --git a/drivers/net/ethernet/msm/Makefile b/drivers/net/ethernet/msm/Makefile
new file mode 100644
index 0000000..7d9d4c6
--- /dev/null
+++ b/drivers/net/ethernet/msm/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for the msm networking support.
+#
+
+obj-$(CONFIG_MSM_RMNET) += msm_rmnet.o
+obj-$(CONFIG_MSM_RMNET_SDIO) += msm_rmnet_sdio.o
+obj-$(CONFIG_MSM_RMNET_BAM) += msm_rmnet_bam.o
+obj-$(CONFIG_MSM_RMNET_SMUX) += msm_rmnet_smux.o
+obj-$(CONFIG_QFEC) += qfec.o
diff --git a/drivers/net/ethernet/msm/msm_rmnet.c b/drivers/net/ethernet/msm/msm_rmnet.c
new file mode 100644
index 0000000..61df241
--- /dev/null
+++ b/drivers/net/ethernet/msm/msm_rmnet.c
@@ -0,0 +1,846 @@
+/* linux/drivers/net/msm_rmnet.c
+ *
+ * Virtual Ethernet Interface for MSM7K Networking
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ * Author: Brian Swetland <swetland@google.com>
+ *
+ * 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/kernel.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/wakelock.h>
+#include <linux/platform_device.h>
+#include <linux/if_arp.h>
+#include <linux/msm_rmnet.h>
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+#include <mach/msm_smd.h>
+#include <mach/peripheral-loader.h>
+
+/* Debug message support */
+static int msm_rmnet_debug_mask;
+module_param_named(debug_enable, msm_rmnet_debug_mask,
+			int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+#define DEBUG_MASK_LVL0 (1U << 0)
+#define DEBUG_MASK_LVL1 (1U << 1)
+#define DEBUG_MASK_LVL2 (1U << 2)
+
+#define DBG(m, x...) do {			\
+		if (msm_rmnet_debug_mask & m)   \
+			pr_info(x);		\
+} while (0)
+#define DBG0(x...) DBG(DEBUG_MASK_LVL0, x)
+#define DBG1(x...) DBG(DEBUG_MASK_LVL1, x)
+#define DBG2(x...) DBG(DEBUG_MASK_LVL2, x)
+
+/* Configure device instances */
+#define RMNET_DEVICE_COUNT (8)
+static const char *ch_name[RMNET_DEVICE_COUNT] = {
+	"DATA5",
+	"DATA6",
+	"DATA7",
+	"DATA8",
+	"DATA9",
+	"DATA12",
+	"DATA13",
+	"DATA14",
+};
+
+/* XXX should come from smd headers */
+#define SMD_PORT_ETHER0 11
+
+/* allow larger frames */
+#define RMNET_DATA_LEN 2000
+
+#define HEADROOM_FOR_QOS    8
+
+static struct completion *port_complete[RMNET_DEVICE_COUNT];
+
+struct rmnet_private
+{
+	smd_channel_t *ch;
+	struct net_device_stats stats;
+	const char *chname;
+	struct wake_lock wake_lock;
+#ifdef CONFIG_MSM_RMNET_DEBUG
+	ktime_t last_packet;
+	unsigned long wakeups_xmit;
+	unsigned long wakeups_rcv;
+	unsigned long timeout_us;
+#endif
+	struct sk_buff *skb;
+	spinlock_t lock;
+	struct tasklet_struct tsklt;
+	u32 operation_mode;    /* IOCTL specified mode (protocol, QoS header) */
+	struct platform_driver pdrv;
+	struct completion complete;
+	void *pil;
+	struct mutex pil_lock;
+};
+
+static uint msm_rmnet_modem_wait;
+module_param_named(modem_wait, msm_rmnet_modem_wait,
+		   uint, S_IRUGO | S_IWUSR | S_IWGRP);
+
+/* Forward declaration */
+static int rmnet_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+
+static int count_this_packet(void *_hdr, int len)
+{
+	struct ethhdr *hdr = _hdr;
+
+	if (len >= ETH_HLEN && hdr->h_proto == htons(ETH_P_ARP))
+		return 0;
+
+	return 1;
+}
+
+#ifdef CONFIG_MSM_RMNET_DEBUG
+static unsigned long timeout_us;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+/*
+ * If early suspend is enabled then we specify two timeout values,
+ * screen on (default), and screen is off.
+ */
+static unsigned long timeout_suspend_us;
+static struct device *rmnet0;
+
+/* Set timeout in us when the screen is off. */
+static ssize_t timeout_suspend_store(struct device *d, struct device_attribute *attr, const char *buf, size_t n)
+{
+	timeout_suspend_us = simple_strtoul(buf, NULL, 10);
+	return n;
+}
+
+static ssize_t timeout_suspend_show(struct device *d,
+				    struct device_attribute *attr,
+				    char *buf)
+{
+	return sprintf(buf, "%lu\n", (unsigned long) timeout_suspend_us);
+}
+
+static DEVICE_ATTR(timeout_suspend, 0664, timeout_suspend_show, timeout_suspend_store);
+
+static void rmnet_early_suspend(struct early_suspend *handler) {
+	if (rmnet0) {
+		struct rmnet_private *p = netdev_priv(to_net_dev(rmnet0));
+		p->timeout_us = timeout_suspend_us;
+	}
+}
+
+static void rmnet_late_resume(struct early_suspend *handler) {
+	if (rmnet0) {
+		struct rmnet_private *p = netdev_priv(to_net_dev(rmnet0));
+		p->timeout_us = timeout_us;
+	}
+}
+
+static struct early_suspend rmnet_power_suspend = {
+	.suspend = rmnet_early_suspend,
+	.resume = rmnet_late_resume,
+};
+
+static int __init rmnet_late_init(void)
+{
+	register_early_suspend(&rmnet_power_suspend);
+	return 0;
+}
+
+late_initcall(rmnet_late_init);
+#endif
+
+/* Returns 1 if packet caused rmnet to wakeup, 0 otherwise. */
+static int rmnet_cause_wakeup(struct rmnet_private *p) {
+	int ret = 0;
+	ktime_t now;
+	if (p->timeout_us == 0) /* Check if disabled */
+		return 0;
+
+	/* Use real (wall) time. */
+	now = ktime_get_real();
+
+	if (ktime_us_delta(now, p->last_packet) > p->timeout_us) {
+		ret = 1;
+	}
+	p->last_packet = now;
+	return ret;
+}
+
+static ssize_t wakeups_xmit_show(struct device *d,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	struct rmnet_private *p = netdev_priv(to_net_dev(d));
+	return sprintf(buf, "%lu\n", p->wakeups_xmit);
+}
+
+DEVICE_ATTR(wakeups_xmit, 0444, wakeups_xmit_show, NULL);
+
+static ssize_t wakeups_rcv_show(struct device *d, struct device_attribute *attr,
+		char *buf)
+{
+	struct rmnet_private *p = netdev_priv(to_net_dev(d));
+	return sprintf(buf, "%lu\n", p->wakeups_rcv);
+}
+
+DEVICE_ATTR(wakeups_rcv, 0444, wakeups_rcv_show, NULL);
+
+/* Set timeout in us. */
+static ssize_t timeout_store(struct device *d, struct device_attribute *attr,
+		const char *buf, size_t n)
+{
+#ifndef CONFIG_HAS_EARLYSUSPEND
+	struct rmnet_private *p = netdev_priv(to_net_dev(d));
+	p->timeout_us = timeout_us = simple_strtoul(buf, NULL, 10);
+#else
+/* If using early suspend/resume hooks do not write the value on store. */
+	timeout_us = simple_strtoul(buf, NULL, 10);
+#endif
+	return n;
+}
+
+static ssize_t timeout_show(struct device *d, struct device_attribute *attr,
+			    char *buf)
+{
+	struct rmnet_private *p = netdev_priv(to_net_dev(d));
+	p = netdev_priv(to_net_dev(d));
+	return sprintf(buf, "%lu\n", timeout_us);
+}
+
+DEVICE_ATTR(timeout, 0664, timeout_show, timeout_store);
+#endif
+
+static __be16 rmnet_ip_type_trans(struct sk_buff *skb, struct net_device *dev)
+{
+	__be16 protocol = 0;
+
+	skb->dev = dev;
+
+	/* Determine L3 protocol */
+	switch (skb->data[0] & 0xf0) {
+	case 0x40:
+		protocol = htons(ETH_P_IP);
+		break;
+	case 0x60:
+		protocol = htons(ETH_P_IPV6);
+		break;
+	default:
+		pr_err("[%s] rmnet_recv() L3 protocol decode error: 0x%02x",
+		       dev->name, skb->data[0] & 0xf0);
+		/* skb will be dropped in uppder layer for unknown protocol */
+	}
+	return protocol;
+}
+
+static void smd_net_data_handler(unsigned long arg);
+static DECLARE_TASKLET(smd_net_data_tasklet, smd_net_data_handler, 0);
+
+/* Called in soft-irq context */
+static void smd_net_data_handler(unsigned long arg)
+{
+	struct net_device *dev = (struct net_device *) arg;
+	struct rmnet_private *p = netdev_priv(dev);
+	struct sk_buff *skb;
+	void *ptr = 0;
+	int sz;
+	u32 opmode = p->operation_mode;
+	unsigned long flags;
+
+	for (;;) {
+		sz = smd_cur_packet_size(p->ch);
+		if (sz == 0) break;
+		if (smd_read_avail(p->ch) < sz) break;
+
+		skb = dev_alloc_skb(sz + NET_IP_ALIGN);
+		if (skb == NULL) {
+			pr_err("[%s] rmnet_recv() cannot allocate skb\n",
+			       dev->name);
+			/* out of memory, reschedule a later attempt */
+			smd_net_data_tasklet.data = (unsigned long)dev;
+			tasklet_schedule(&smd_net_data_tasklet);
+			break;
+		} else {
+			skb->dev = dev;
+			skb_reserve(skb, NET_IP_ALIGN);
+			ptr = skb_put(skb, sz);
+			wake_lock_timeout(&p->wake_lock, HZ / 2);
+			if (smd_read(p->ch, ptr, sz) != sz) {
+				pr_err("[%s] rmnet_recv() smd lied about avail?!",
+					dev->name);
+				ptr = 0;
+				dev_kfree_skb_irq(skb);
+			} else {
+				/* Handle Rx frame format */
+				spin_lock_irqsave(&p->lock, flags);
+				opmode = p->operation_mode;
+				spin_unlock_irqrestore(&p->lock, flags);
+
+				if (RMNET_IS_MODE_IP(opmode)) {
+					/* Driver in IP mode */
+					skb->protocol =
+					  rmnet_ip_type_trans(skb, dev);
+				} else {
+					/* Driver in Ethernet mode */
+					skb->protocol =
+					  eth_type_trans(skb, dev);
+				}
+				if (RMNET_IS_MODE_IP(opmode) ||
+				    count_this_packet(ptr, skb->len)) {
+#ifdef CONFIG_MSM_RMNET_DEBUG
+					p->wakeups_rcv +=
+					rmnet_cause_wakeup(p);
+#endif
+					p->stats.rx_packets++;
+					p->stats.rx_bytes += skb->len;
+				}
+				DBG1("[%s] Rx packet #%lu len=%d\n",
+					dev->name, p->stats.rx_packets,
+					skb->len);
+
+				/* Deliver to network stack */
+				netif_rx(skb);
+			}
+			continue;
+		}
+		if (smd_read(p->ch, ptr, sz) != sz)
+			pr_err("[%s] rmnet_recv() smd lied about avail?!",
+				dev->name);
+	}
+}
+
+static int _rmnet_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct rmnet_private *p = netdev_priv(dev);
+	smd_channel_t *ch = p->ch;
+	int smd_ret;
+	struct QMI_QOS_HDR_S *qmih;
+	u32 opmode;
+	unsigned long flags;
+
+	/* For QoS mode, prepend QMI header and assign flow ID from skb->mark */
+	spin_lock_irqsave(&p->lock, flags);
+	opmode = p->operation_mode;
+	spin_unlock_irqrestore(&p->lock, flags);
+
+	if (RMNET_IS_MODE_QOS(opmode)) {
+		qmih = (struct QMI_QOS_HDR_S *)
+			skb_push(skb, sizeof(struct QMI_QOS_HDR_S));
+		qmih->version = 1;
+		qmih->flags = 0;
+		qmih->flow_id = skb->mark;
+	}
+
+	dev->trans_start = jiffies;
+	smd_ret = smd_write(ch, skb->data, skb->len);
+	if (smd_ret != skb->len) {
+		pr_err("[%s] %s: smd_write returned error %d",
+			dev->name, __func__, smd_ret);
+		p->stats.tx_errors++;
+		goto xmit_out;
+	}
+
+	if (RMNET_IS_MODE_IP(opmode) ||
+	    count_this_packet(skb->data, skb->len)) {
+		p->stats.tx_packets++;
+		p->stats.tx_bytes += skb->len;
+#ifdef CONFIG_MSM_RMNET_DEBUG
+		p->wakeups_xmit += rmnet_cause_wakeup(p);
+#endif
+	}
+	DBG1("[%s] Tx packet #%lu len=%d mark=0x%x\n",
+	    dev->name, p->stats.tx_packets, skb->len, skb->mark);
+
+xmit_out:
+	/* data xmited, safe to release skb */
+	dev_kfree_skb_irq(skb);
+	return 0;
+}
+
+static void _rmnet_resume_flow(unsigned long param)
+{
+	struct net_device *dev = (struct net_device *)param;
+	struct rmnet_private *p = netdev_priv(dev);
+	struct sk_buff *skb = NULL;
+	unsigned long flags;
+
+	/* xmit and enable the flow only once even if
+	   multiple tasklets were scheduled by smd_net_notify */
+	spin_lock_irqsave(&p->lock, flags);
+	if (p->skb && (smd_write_avail(p->ch) >= p->skb->len)) {
+		skb = p->skb;
+		p->skb = NULL;
+		spin_unlock_irqrestore(&p->lock, flags);
+		_rmnet_xmit(skb, dev);
+		netif_wake_queue(dev);
+	} else
+		spin_unlock_irqrestore(&p->lock, flags);
+}
+
+static void msm_rmnet_unload_modem(void *pil)
+{
+	if (pil)
+		pil_put(pil);
+}
+
+static void *msm_rmnet_load_modem(struct net_device *dev)
+{
+	void *pil;
+	int rc;
+	struct rmnet_private *p = netdev_priv(dev);
+
+	pil = pil_get("modem");
+	if (IS_ERR(pil))
+		pr_err("[%s] %s: modem load failed\n",
+			dev->name, __func__);
+	else if (msm_rmnet_modem_wait) {
+		rc = wait_for_completion_interruptible_timeout(
+			&p->complete,
+			msecs_to_jiffies(msm_rmnet_modem_wait * 1000));
+		if (!rc)
+			rc = -ETIMEDOUT;
+		if (rc < 0) {
+			pr_err("[%s] %s: wait for rmnet port failed %d\n",
+			       dev->name, __func__, rc);
+			msm_rmnet_unload_modem(pil);
+			pil = ERR_PTR(rc);
+		}
+	}
+
+	return pil;
+}
+
+static void smd_net_notify(void *_dev, unsigned event)
+{
+	struct rmnet_private *p = netdev_priv((struct net_device *)_dev);
+
+	switch (event) {
+	case SMD_EVENT_DATA:
+		spin_lock(&p->lock);
+		if (p->skb && (smd_write_avail(p->ch) >= p->skb->len)) {
+			smd_disable_read_intr(p->ch);
+			tasklet_hi_schedule(&p->tsklt);
+		}
+
+		spin_unlock(&p->lock);
+
+		if (smd_read_avail(p->ch) &&
+			(smd_read_avail(p->ch) >= smd_cur_packet_size(p->ch))) {
+			smd_net_data_tasklet.data = (unsigned long) _dev;
+			tasklet_schedule(&smd_net_data_tasklet);
+		}
+		break;
+
+	case SMD_EVENT_OPEN:
+		DBG0("%s: opening SMD port\n", __func__);
+		netif_carrier_on(_dev);
+		if (netif_queue_stopped(_dev)) {
+			DBG0("%s: re-starting if queue\n", __func__);
+			netif_wake_queue(_dev);
+		}
+		break;
+
+	case SMD_EVENT_CLOSE:
+		DBG0("%s: closing SMD port\n", __func__);
+		netif_carrier_off(_dev);
+		break;
+	}
+}
+
+static int __rmnet_open(struct net_device *dev)
+{
+	int r;
+	void *pil;
+	struct rmnet_private *p = netdev_priv(dev);
+
+	mutex_lock(&p->pil_lock);
+	if (!p->pil) {
+		pil = msm_rmnet_load_modem(dev);
+		if (IS_ERR(pil)) {
+			mutex_unlock(&p->pil_lock);
+			return PTR_ERR(pil);
+		}
+		p->pil = pil;
+	}
+	mutex_unlock(&p->pil_lock);
+
+	if (!p->ch) {
+		r = smd_open(p->chname, &p->ch, dev, smd_net_notify);
+
+		if (r < 0)
+			return -ENODEV;
+	}
+
+	smd_disable_read_intr(p->ch);
+	return 0;
+}
+
+static int __rmnet_close(struct net_device *dev)
+{
+	struct rmnet_private *p = netdev_priv(dev);
+	int rc;
+	unsigned long flags;
+
+	if (p->ch) {
+		rc = smd_close(p->ch);
+		spin_lock_irqsave(&p->lock, flags);
+		p->ch = 0;
+		spin_unlock_irqrestore(&p->lock, flags);
+		return rc;
+	} else
+		return -EBADF;
+}
+
+static int rmnet_open(struct net_device *dev)
+{
+	int rc = 0;
+
+	DBG0("[%s] rmnet_open()\n", dev->name);
+
+	rc = __rmnet_open(dev);
+	if (rc == 0)
+		netif_start_queue(dev);
+
+	return rc;
+}
+
+static int rmnet_stop(struct net_device *dev)
+{
+	struct rmnet_private *p = netdev_priv(dev);
+
+	DBG0("[%s] rmnet_stop()\n", dev->name);
+
+	netif_stop_queue(dev);
+	tasklet_kill(&p->tsklt);
+
+	/* TODO: unload modem safely,
+	   currently, this causes unnecessary unloads */
+	/*
+	mutex_lock(&p->pil_lock);
+	msm_rmnet_unload_modem(p->pil);
+	p->pil = NULL;
+	mutex_unlock(&p->pil_lock);
+	*/
+
+	return 0;
+}
+
+static int rmnet_change_mtu(struct net_device *dev, int new_mtu)
+{
+	if (0 > new_mtu || RMNET_DATA_LEN < new_mtu)
+		return -EINVAL;
+
+	DBG0("[%s] MTU change: old=%d new=%d\n",
+		dev->name, dev->mtu, new_mtu);
+	dev->mtu = new_mtu;
+
+	return 0;
+}
+
+static int rmnet_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct rmnet_private *p = netdev_priv(dev);
+	smd_channel_t *ch = p->ch;
+	unsigned long flags;
+
+	if (netif_queue_stopped(dev)) {
+		pr_err("[%s] fatal: rmnet_xmit called when netif_queue is stopped",
+			dev->name);
+		return 0;
+	}
+
+	spin_lock_irqsave(&p->lock, flags);
+	smd_enable_read_intr(ch);
+	if (smd_write_avail(ch) < skb->len) {
+		netif_stop_queue(dev);
+		p->skb = skb;
+		spin_unlock_irqrestore(&p->lock, flags);
+		return 0;
+	}
+	smd_disable_read_intr(ch);
+	spin_unlock_irqrestore(&p->lock, flags);
+
+	_rmnet_xmit(skb, dev);
+
+	return 0;
+}
+
+static struct net_device_stats *rmnet_get_stats(struct net_device *dev)
+{
+	struct rmnet_private *p = netdev_priv(dev);
+	return &p->stats;
+}
+
+static void rmnet_set_multicast_list(struct net_device *dev)
+{
+}
+
+static void rmnet_tx_timeout(struct net_device *dev)
+{
+	pr_warning("[%s] rmnet_tx_timeout()\n", dev->name);
+}
+
+
+static const struct net_device_ops rmnet_ops_ether = {
+	.ndo_open		= rmnet_open,
+	.ndo_stop		= rmnet_stop,
+	.ndo_start_xmit		= rmnet_xmit,
+	.ndo_get_stats		= rmnet_get_stats,
+	.ndo_set_rx_mode	= rmnet_set_multicast_list,
+	.ndo_tx_timeout		= rmnet_tx_timeout,
+	.ndo_do_ioctl		= rmnet_ioctl,
+	.ndo_change_mtu		= rmnet_change_mtu,
+	.ndo_set_mac_address	= eth_mac_addr,
+	.ndo_validate_addr	= eth_validate_addr,
+};
+
+static const struct net_device_ops rmnet_ops_ip = {
+	.ndo_open		= rmnet_open,
+	.ndo_stop		= rmnet_stop,
+	.ndo_start_xmit		= rmnet_xmit,
+	.ndo_get_stats		= rmnet_get_stats,
+	.ndo_set_rx_mode	= rmnet_set_multicast_list,
+	.ndo_tx_timeout		= rmnet_tx_timeout,
+	.ndo_do_ioctl		= rmnet_ioctl,
+	.ndo_change_mtu		= rmnet_change_mtu,
+	.ndo_set_mac_address	= 0,
+	.ndo_validate_addr	= 0,
+};
+
+static int rmnet_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	struct rmnet_private *p = netdev_priv(dev);
+	u32 old_opmode = p->operation_mode;
+	unsigned long flags;
+	int prev_mtu = dev->mtu;
+	int rc = 0;
+
+	/* Process IOCTL command */
+	switch (cmd) {
+	case RMNET_IOCTL_SET_LLP_ETHERNET:  /* Set Ethernet protocol   */
+		/* Perform Ethernet config only if in IP mode currently*/
+		if (p->operation_mode & RMNET_MODE_LLP_IP) {
+			ether_setup(dev);
+			random_ether_addr(dev->dev_addr);
+			dev->mtu = prev_mtu;
+
+			dev->netdev_ops = &rmnet_ops_ether;
+			spin_lock_irqsave(&p->lock, flags);
+			p->operation_mode &= ~RMNET_MODE_LLP_IP;
+			p->operation_mode |= RMNET_MODE_LLP_ETH;
+			spin_unlock_irqrestore(&p->lock, flags);
+			DBG0("[%s] rmnet_ioctl(): "
+				"set Ethernet protocol mode\n",
+				dev->name);
+		}
+		break;
+
+	case RMNET_IOCTL_SET_LLP_IP:        /* Set RAWIP protocol      */
+		/* Perform IP config only if in Ethernet mode currently*/
+		if (p->operation_mode & RMNET_MODE_LLP_ETH) {
+
+			/* Undo config done in ether_setup() */
+			dev->header_ops         = 0;  /* No header */
+			dev->type               = ARPHRD_RAWIP;
+			dev->hard_header_len    = 0;
+			dev->mtu                = prev_mtu;
+			dev->addr_len           = 0;
+			dev->flags              &= ~(IFF_BROADCAST|
+						     IFF_MULTICAST);
+
+			dev->netdev_ops = &rmnet_ops_ip;
+			spin_lock_irqsave(&p->lock, flags);
+			p->operation_mode &= ~RMNET_MODE_LLP_ETH;
+			p->operation_mode |= RMNET_MODE_LLP_IP;
+			spin_unlock_irqrestore(&p->lock, flags);
+			DBG0("[%s] rmnet_ioctl(): set IP protocol mode\n",
+				dev->name);
+		}
+		break;
+
+	case RMNET_IOCTL_GET_LLP:           /* Get link protocol state */
+		ifr->ifr_ifru.ifru_data =
+			(void *)(p->operation_mode &
+				(RMNET_MODE_LLP_ETH|RMNET_MODE_LLP_IP));
+		break;
+
+	case RMNET_IOCTL_SET_QOS_ENABLE:    /* Set QoS header enabled  */
+		spin_lock_irqsave(&p->lock, flags);
+		p->operation_mode |= RMNET_MODE_QOS;
+		spin_unlock_irqrestore(&p->lock, flags);
+		DBG0("[%s] rmnet_ioctl(): set QMI QOS header enable\n",
+			dev->name);
+		break;
+
+	case RMNET_IOCTL_SET_QOS_DISABLE:   /* Set QoS header disabled */
+		spin_lock_irqsave(&p->lock, flags);
+		p->operation_mode &= ~RMNET_MODE_QOS;
+		spin_unlock_irqrestore(&p->lock, flags);
+		DBG0("[%s] rmnet_ioctl(): set QMI QOS header disable\n",
+			dev->name);
+		break;
+
+	case RMNET_IOCTL_GET_QOS:           /* Get QoS header state    */
+		ifr->ifr_ifru.ifru_data =
+			(void *)(p->operation_mode & RMNET_MODE_QOS);
+		break;
+
+	case RMNET_IOCTL_GET_OPMODE:        /* Get operation mode      */
+		ifr->ifr_ifru.ifru_data = (void *)p->operation_mode;
+		break;
+
+	case RMNET_IOCTL_OPEN:              /* Open transport port     */
+		rc = __rmnet_open(dev);
+		DBG0("[%s] rmnet_ioctl(): open transport port\n",
+			dev->name);
+		break;
+
+	case RMNET_IOCTL_CLOSE:             /* Close transport port    */
+		rc = __rmnet_close(dev);
+		DBG0("[%s] rmnet_ioctl(): close transport port\n",
+			dev->name);
+		break;
+
+	default:
+		pr_err("[%s] error: rmnet_ioct called for unsupported cmd[%d]",
+			dev->name, cmd);
+		return -EINVAL;
+	}
+
+	DBG2("[%s] %s: cmd=0x%x opmode old=0x%08x new=0x%08x\n",
+		dev->name, __func__, cmd, old_opmode, p->operation_mode);
+	return rc;
+}
+
+
+static void __init rmnet_setup(struct net_device *dev)
+{
+	/* Using Ethernet mode by default */
+	dev->netdev_ops = &rmnet_ops_ether;
+	ether_setup(dev);
+
+	/* set this after calling ether_setup */
+	dev->mtu = RMNET_DATA_LEN;
+	dev->needed_headroom = HEADROOM_FOR_QOS;
+
+	random_ether_addr(dev->dev_addr);
+
+	dev->watchdog_timeo = 1000; /* 10 seconds? */
+}
+
+static int msm_rmnet_smd_probe(struct platform_device *pdev)
+{
+	int i;
+
+	for (i = 0; i < RMNET_DEVICE_COUNT; i++)
+		if (!strcmp(pdev->name, ch_name[i])) {
+			complete_all(port_complete[i]);
+			break;
+		}
+
+	return 0;
+}
+
+static int __init rmnet_init(void)
+{
+	int ret;
+	struct device *d;
+	struct net_device *dev;
+	struct rmnet_private *p;
+	unsigned n;
+
+	pr_info("%s: SMD devices[%d]\n", __func__, RMNET_DEVICE_COUNT);
+
+#ifdef CONFIG_MSM_RMNET_DEBUG
+	timeout_us = 0;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	timeout_suspend_us = 0;
+#endif
+#endif
+
+	for (n = 0; n < RMNET_DEVICE_COUNT; n++) {
+		dev = alloc_netdev(sizeof(struct rmnet_private),
+				   "rmnet%d", rmnet_setup);
+
+		if (!dev)
+			return -ENOMEM;
+
+		d = &(dev->dev);
+		p = netdev_priv(dev);
+		p->chname = ch_name[n];
+		/* Initial config uses Ethernet */
+		p->operation_mode = RMNET_MODE_LLP_ETH;
+		p->skb = NULL;
+		spin_lock_init(&p->lock);
+		tasklet_init(&p->tsklt, _rmnet_resume_flow,
+				(unsigned long)dev);
+		wake_lock_init(&p->wake_lock, WAKE_LOCK_SUSPEND, ch_name[n]);
+#ifdef CONFIG_MSM_RMNET_DEBUG
+		p->timeout_us = timeout_us;
+		p->wakeups_xmit = p->wakeups_rcv = 0;
+#endif
+
+		init_completion(&p->complete);
+		port_complete[n] = &p->complete;
+		mutex_init(&p->pil_lock);
+		p->pdrv.probe = msm_rmnet_smd_probe;
+		p->pdrv.driver.name = ch_name[n];
+		p->pdrv.driver.owner = THIS_MODULE;
+		ret = platform_driver_register(&p->pdrv);
+		if (ret) {
+			free_netdev(dev);
+			return ret;
+		}
+
+		ret = register_netdev(dev);
+		if (ret) {
+			platform_driver_unregister(&p->pdrv);
+			free_netdev(dev);
+			return ret;
+		}
+
+
+#ifdef CONFIG_MSM_RMNET_DEBUG
+		if (device_create_file(d, &dev_attr_timeout))
+			continue;
+		if (device_create_file(d, &dev_attr_wakeups_xmit))
+			continue;
+		if (device_create_file(d, &dev_attr_wakeups_rcv))
+			continue;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+		if (device_create_file(d, &dev_attr_timeout_suspend))
+			continue;
+
+		/* Only care about rmnet0 for suspend/resume tiemout hooks. */
+		if (n == 0)
+			rmnet0 = d;
+#endif
+#endif
+	}
+	return 0;
+}
+
+module_init(rmnet_init);
diff --git a/drivers/net/ethernet/msm/msm_rmnet_bam.c b/drivers/net/ethernet/msm/msm_rmnet_bam.c
new file mode 100644
index 0000000..fbe8d3c
--- /dev/null
+++ b/drivers/net/ethernet/msm/msm_rmnet_bam.c
@@ -0,0 +1,826 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*
+ * RMNET BAM Module.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/wakelock.h>
+#include <linux/if_arp.h>
+#include <linux/msm_rmnet.h>
+#include <linux/platform_device.h>
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+#include <mach/bam_dmux.h>
+
+/* Debug message support */
+static int msm_rmnet_bam_debug_mask;
+module_param_named(debug_enable, msm_rmnet_bam_debug_mask,
+			int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+#define DEBUG_MASK_LVL0 (1U << 0)
+#define DEBUG_MASK_LVL1 (1U << 1)
+#define DEBUG_MASK_LVL2 (1U << 2)
+
+#define DBG(m, x...) do {			   \
+		if (msm_rmnet_bam_debug_mask & m) \
+			pr_info(x);		   \
+} while (0)
+#define DBG0(x...) DBG(DEBUG_MASK_LVL0, x)
+#define DBG1(x...) DBG(DEBUG_MASK_LVL1, x)
+#define DBG2(x...) DBG(DEBUG_MASK_LVL2, x)
+
+/* Configure device instances */
+#define RMNET_DEVICE_COUNT (8)
+
+/* allow larger frames */
+#define RMNET_DATA_LEN 2000
+
+#define DEVICE_ID_INVALID   -1
+
+#define DEVICE_INACTIVE      0
+#define DEVICE_ACTIVE        1
+
+#define HEADROOM_FOR_BAM   8 /* for mux header */
+#define HEADROOM_FOR_QOS    8
+#define TAILROOM            8 /* for padding by mux layer */
+
+struct rmnet_private {
+	struct net_device_stats stats;
+	uint32_t ch_id;
+#ifdef CONFIG_MSM_RMNET_DEBUG
+	ktime_t last_packet;
+	unsigned long wakeups_xmit;
+	unsigned long wakeups_rcv;
+	unsigned long timeout_us;
+#endif
+	struct sk_buff *waiting_for_ul_skb;
+	spinlock_t lock;
+	spinlock_t tx_queue_lock;
+	struct tasklet_struct tsklt;
+	u32 operation_mode; /* IOCTL specified mode (protocol, QoS header) */
+	uint8_t device_up;
+	uint8_t in_reset;
+};
+
+#ifdef CONFIG_MSM_RMNET_DEBUG
+static unsigned long timeout_us;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+/*
+ * If early suspend is enabled then we specify two timeout values,
+ * screen on (default), and screen is off.
+ */
+static unsigned long timeout_suspend_us;
+static struct device *rmnet0;
+
+/* Set timeout in us when the screen is off. */
+static ssize_t timeout_suspend_store(struct device *d,
+				     struct device_attribute *attr,
+				     const char *buf, size_t n)
+{
+	timeout_suspend_us = strict_strtoul(buf, NULL, 10);
+	return n;
+}
+
+static ssize_t timeout_suspend_show(struct device *d,
+				    struct device_attribute *attr,
+				    char *buf)
+{
+	return sprintf(buf, "%lu\n", (unsigned long) timeout_suspend_us);
+}
+
+static DEVICE_ATTR(timeout_suspend, 0664, timeout_suspend_show,
+		   timeout_suspend_store);
+
+static void rmnet_early_suspend(struct early_suspend *handler)
+{
+	if (rmnet0) {
+		struct rmnet_private *p = netdev_priv(to_net_dev(rmnet0));
+		p->timeout_us = timeout_suspend_us;
+	}
+}
+
+static void rmnet_late_resume(struct early_suspend *handler)
+{
+	if (rmnet0) {
+		struct rmnet_private *p = netdev_priv(to_net_dev(rmnet0));
+		p->timeout_us = timeout_us;
+	}
+}
+
+static struct early_suspend rmnet_power_suspend = {
+	.suspend = rmnet_early_suspend,
+	.resume = rmnet_late_resume,
+};
+
+static int __init rmnet_late_init(void)
+{
+	register_early_suspend(&rmnet_power_suspend);
+	return 0;
+}
+
+late_initcall(rmnet_late_init);
+#endif
+
+/* Returns 1 if packet caused rmnet to wakeup, 0 otherwise. */
+static int rmnet_cause_wakeup(struct rmnet_private *p)
+{
+	int ret = 0;
+	ktime_t now;
+	if (p->timeout_us == 0) /* Check if disabled */
+		return 0;
+
+	/* Use real (wall) time. */
+	now = ktime_get_real();
+
+	if (ktime_us_delta(now, p->last_packet) > p->timeout_us)
+		ret = 1;
+
+	p->last_packet = now;
+	return ret;
+}
+
+static ssize_t wakeups_xmit_show(struct device *d,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	struct rmnet_private *p = netdev_priv(to_net_dev(d));
+	return sprintf(buf, "%lu\n", p->wakeups_xmit);
+}
+
+DEVICE_ATTR(wakeups_xmit, 0444, wakeups_xmit_show, NULL);
+
+static ssize_t wakeups_rcv_show(struct device *d, struct device_attribute *attr,
+				char *buf)
+{
+	struct rmnet_private *p = netdev_priv(to_net_dev(d));
+	return sprintf(buf, "%lu\n", p->wakeups_rcv);
+}
+
+DEVICE_ATTR(wakeups_rcv, 0444, wakeups_rcv_show, NULL);
+
+/* Set timeout in us. */
+static ssize_t timeout_store(struct device *d, struct device_attribute *attr,
+			     const char *buf, size_t n)
+{
+#ifndef CONFIG_HAS_EARLYSUSPEND
+	struct rmnet_private *p = netdev_priv(to_net_dev(d));
+	p->timeout_us = timeout_us = strict_strtoul(buf, NULL, 10);
+#else
+/* If using early suspend/resume hooks do not write the value on store. */
+	timeout_us = strict_strtoul(buf, NULL, 10);
+#endif
+	return n;
+}
+
+static ssize_t timeout_show(struct device *d, struct device_attribute *attr,
+			    char *buf)
+{
+	struct rmnet_private *p = netdev_priv(to_net_dev(d));
+	p = netdev_priv(to_net_dev(d));
+	return sprintf(buf, "%lu\n", timeout_us);
+}
+
+DEVICE_ATTR(timeout, 0664, timeout_show, timeout_store);
+#endif
+
+
+/* Forward declaration */
+static int rmnet_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+
+static __be16 rmnet_ip_type_trans(struct sk_buff *skb, struct net_device *dev)
+{
+	__be16 protocol = 0;
+
+	skb->dev = dev;
+
+	/* Determine L3 protocol */
+	switch (skb->data[0] & 0xf0) {
+	case 0x40:
+		protocol = htons(ETH_P_IP);
+		break;
+	case 0x60:
+		protocol = htons(ETH_P_IPV6);
+		break;
+	default:
+		pr_err("[%s] rmnet_recv() L3 protocol decode error: 0x%02x",
+		       dev->name, skb->data[0] & 0xf0);
+		/* skb will be dropped in upper layer for unknown protocol */
+	}
+	return protocol;
+}
+
+static int count_this_packet(void *_hdr, int len)
+{
+	struct ethhdr *hdr = _hdr;
+
+	if (len >= ETH_HLEN && hdr->h_proto == htons(ETH_P_ARP))
+		return 0;
+
+	return 1;
+}
+
+/* Rx Callback, Called in Work Queue context */
+static void bam_recv_notify(void *dev, struct sk_buff *skb)
+{
+	struct rmnet_private *p = netdev_priv(dev);
+	unsigned long flags;
+	u32 opmode;
+
+	if (skb) {
+		skb->dev = dev;
+		/* Handle Rx frame format */
+		spin_lock_irqsave(&p->lock, flags);
+		opmode = p->operation_mode;
+		spin_unlock_irqrestore(&p->lock, flags);
+
+		if (RMNET_IS_MODE_IP(opmode)) {
+			/* Driver in IP mode */
+			skb->protocol = rmnet_ip_type_trans(skb, dev);
+		} else {
+			/* Driver in Ethernet mode */
+			skb->protocol = eth_type_trans(skb, dev);
+		}
+		if (RMNET_IS_MODE_IP(opmode) ||
+		    count_this_packet(skb->data, skb->len)) {
+#ifdef CONFIG_MSM_RMNET_DEBUG
+			p->wakeups_rcv += rmnet_cause_wakeup(p);
+#endif
+			p->stats.rx_packets++;
+			p->stats.rx_bytes += skb->len;
+		}
+		DBG1("[%s] Rx packet #%lu len=%d\n",
+			((struct net_device *)dev)->name,
+			p->stats.rx_packets, skb->len);
+
+		/* Deliver to network stack */
+		netif_rx(skb);
+	} else
+		pr_err("[%s] %s: No skb received",
+			((struct net_device *)dev)->name, __func__);
+}
+
+static int _rmnet_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct rmnet_private *p = netdev_priv(dev);
+	int bam_ret;
+	struct QMI_QOS_HDR_S *qmih;
+	u32 opmode;
+	unsigned long flags;
+
+	/* For QoS mode, prepend QMI header and assign flow ID from skb->mark */
+	spin_lock_irqsave(&p->lock, flags);
+	opmode = p->operation_mode;
+	spin_unlock_irqrestore(&p->lock, flags);
+
+	if (RMNET_IS_MODE_QOS(opmode)) {
+		qmih = (struct QMI_QOS_HDR_S *)
+			skb_push(skb, sizeof(struct QMI_QOS_HDR_S));
+		qmih->version = 1;
+		qmih->flags = 0;
+		qmih->flow_id = skb->mark;
+	}
+
+	dev->trans_start = jiffies;
+	/* if write() succeeds, skb access is unsafe in this process */
+	bam_ret = msm_bam_dmux_write(p->ch_id, skb);
+
+	if (bam_ret != 0 && bam_ret != -EAGAIN && bam_ret != -EFAULT) {
+		pr_err("[%s] %s: write returned error %d",
+			dev->name, __func__, bam_ret);
+		return -EPERM;
+	}
+
+	return bam_ret;
+}
+
+static void bam_write_done(void *dev, struct sk_buff *skb)
+{
+	struct rmnet_private *p = netdev_priv(dev);
+	u32 opmode = p->operation_mode;
+	unsigned long flags;
+
+	DBG1("%s: write complete\n", __func__);
+	if (RMNET_IS_MODE_IP(opmode) ||
+				count_this_packet(skb->data, skb->len)) {
+		p->stats.tx_packets++;
+		p->stats.tx_bytes += skb->len;
+#ifdef CONFIG_MSM_RMNET_DEBUG
+		p->wakeups_xmit += rmnet_cause_wakeup(p);
+#endif
+	}
+	DBG1("[%s] Tx packet #%lu len=%d mark=0x%x\n",
+	    ((struct net_device *)(dev))->name, p->stats.tx_packets,
+	    skb->len, skb->mark);
+	dev_kfree_skb_any(skb);
+
+	spin_lock_irqsave(&p->tx_queue_lock, flags);
+	if (netif_queue_stopped(dev) &&
+	    msm_bam_dmux_is_ch_low(p->ch_id)) {
+		DBG0("%s: Low WM hit, waking queue=%p\n",
+		      __func__, skb);
+		netif_wake_queue(dev);
+	}
+	spin_unlock_irqrestore(&p->tx_queue_lock, flags);
+}
+
+static void bam_notify(void *dev, int event, unsigned long data)
+{
+	struct rmnet_private *p = netdev_priv(dev);
+	unsigned long flags;
+
+	switch (event) {
+	case BAM_DMUX_RECEIVE:
+		bam_recv_notify(dev, (struct sk_buff *)(data));
+		break;
+	case BAM_DMUX_WRITE_DONE:
+		bam_write_done(dev, (struct sk_buff *)(data));
+		break;
+	case BAM_DMUX_UL_CONNECTED:
+		spin_lock_irqsave(&p->lock, flags);
+		if (p->waiting_for_ul_skb != NULL) {
+			struct sk_buff *skb;
+			int ret;
+
+			skb = p->waiting_for_ul_skb;
+			p->waiting_for_ul_skb = NULL;
+			spin_unlock_irqrestore(&p->lock, flags);
+			ret = _rmnet_xmit(skb, dev);
+			if (ret) {
+				pr_err("%s: error %d dropping delayed TX SKB %p\n",
+						__func__, ret, skb);
+				dev_kfree_skb_any(skb);
+			}
+			netif_wake_queue(dev);
+		} else {
+			spin_unlock_irqrestore(&p->lock, flags);
+		}
+		break;
+	case BAM_DMUX_UL_DISCONNECTED:
+		break;
+	}
+}
+
+static int __rmnet_open(struct net_device *dev)
+{
+	int r;
+	struct rmnet_private *p = netdev_priv(dev);
+
+	DBG0("[%s] __rmnet_open()\n", dev->name);
+
+	if (!p->device_up) {
+		r = msm_bam_dmux_open(p->ch_id, dev, bam_notify);
+
+		if (r < 0) {
+			DBG0("%s: ch=%d failed with rc %d\n",
+					__func__, p->ch_id, r);
+			return -ENODEV;
+		}
+	}
+
+	p->device_up = DEVICE_ACTIVE;
+	return 0;
+}
+
+static int rmnet_open(struct net_device *dev)
+{
+	int rc = 0;
+
+	DBG0("[%s] rmnet_open()\n", dev->name);
+
+	rc = __rmnet_open(dev);
+
+	if (rc == 0)
+		netif_start_queue(dev);
+
+	return rc;
+}
+
+
+static int __rmnet_close(struct net_device *dev)
+{
+	struct rmnet_private *p = netdev_priv(dev);
+	int rc = 0;
+
+	if (p->device_up) {
+		/* do not close rmnet port once up,  this causes
+		   remote side to hang if tried to open again */
+		p->device_up = DEVICE_INACTIVE;
+		return rc;
+	} else
+		return -EBADF;
+}
+
+
+static int rmnet_stop(struct net_device *dev)
+{
+	DBG0("[%s] rmnet_stop()\n", dev->name);
+
+	__rmnet_close(dev);
+	netif_stop_queue(dev);
+
+	return 0;
+}
+
+static int rmnet_change_mtu(struct net_device *dev, int new_mtu)
+{
+	if (0 > new_mtu || RMNET_DATA_LEN < new_mtu)
+		return -EINVAL;
+
+	DBG0("[%s] MTU change: old=%d new=%d\n",
+		dev->name, dev->mtu, new_mtu);
+	dev->mtu = new_mtu;
+
+	return 0;
+}
+
+static int rmnet_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct rmnet_private *p = netdev_priv(dev);
+	unsigned long flags;
+	int awake;
+	int ret = 0;
+
+	if (netif_queue_stopped(dev)) {
+		pr_err("[%s]fatal: rmnet_xmit called when "
+			"netif_queue is stopped", dev->name);
+		return 0;
+	}
+
+	spin_lock_irqsave(&p->lock, flags);
+	awake = msm_bam_dmux_ul_power_vote();
+	if (!awake) {
+		/* send SKB once wakeup is complete */
+		netif_stop_queue(dev);
+		p->waiting_for_ul_skb = skb;
+		spin_unlock_irqrestore(&p->lock, flags);
+		ret = 0;
+		goto exit;
+	}
+	spin_unlock_irqrestore(&p->lock, flags);
+
+	ret = _rmnet_xmit(skb, dev);
+	if (ret == -EPERM) {
+		ret = NETDEV_TX_BUSY;
+		goto exit;
+	}
+
+	/*
+	 * detected SSR a bit early.  shut some things down now, and leave
+	 * the rest to the main ssr handling code when that happens later
+	 */
+	if (ret == -EFAULT) {
+		netif_carrier_off(dev);
+		dev_kfree_skb_any(skb);
+		ret = 0;
+		goto exit;
+	}
+
+	if (ret == -EAGAIN) {
+		/*
+		 * This should not happen
+		 * EAGAIN means we attempted to overflow the high watermark
+		 * Clearly the queue is not stopped like it should be, so
+		 * stop it and return BUSY to the TCP/IP framework.  It will
+		 * retry this packet with the queue is restarted which happens
+		 * in the write_done callback when the low watermark is hit.
+		 */
+		netif_stop_queue(dev);
+		ret = NETDEV_TX_BUSY;
+		goto exit;
+	}
+
+	spin_lock_irqsave(&p->tx_queue_lock, flags);
+	if (msm_bam_dmux_is_ch_full(p->ch_id)) {
+		netif_stop_queue(dev);
+		DBG0("%s: High WM hit, stopping queue=%p\n",    __func__, skb);
+	}
+	spin_unlock_irqrestore(&p->tx_queue_lock, flags);
+
+exit:
+	msm_bam_dmux_ul_power_unvote();
+	return ret;
+}
+
+static struct net_device_stats *rmnet_get_stats(struct net_device *dev)
+{
+	struct rmnet_private *p = netdev_priv(dev);
+	return &p->stats;
+}
+
+static void rmnet_tx_timeout(struct net_device *dev)
+{
+	pr_warning("[%s] rmnet_tx_timeout()\n", dev->name);
+}
+
+static const struct net_device_ops rmnet_ops_ether = {
+	.ndo_open = rmnet_open,
+	.ndo_stop = rmnet_stop,
+	.ndo_start_xmit = rmnet_xmit,
+	.ndo_get_stats = rmnet_get_stats,
+	.ndo_tx_timeout = rmnet_tx_timeout,
+	.ndo_do_ioctl = rmnet_ioctl,
+	.ndo_change_mtu = rmnet_change_mtu,
+	.ndo_set_mac_address = eth_mac_addr,
+	.ndo_validate_addr = eth_validate_addr,
+};
+
+static const struct net_device_ops rmnet_ops_ip = {
+	.ndo_open = rmnet_open,
+	.ndo_stop = rmnet_stop,
+	.ndo_start_xmit = rmnet_xmit,
+	.ndo_get_stats = rmnet_get_stats,
+	.ndo_tx_timeout = rmnet_tx_timeout,
+	.ndo_do_ioctl = rmnet_ioctl,
+	.ndo_change_mtu = rmnet_change_mtu,
+	.ndo_set_mac_address = 0,
+	.ndo_validate_addr = 0,
+};
+
+static int rmnet_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	struct rmnet_private *p = netdev_priv(dev);
+	u32 old_opmode = p->operation_mode;
+	unsigned long flags;
+	int prev_mtu = dev->mtu;
+	int rc = 0;
+
+	/* Process IOCTL command */
+	switch (cmd) {
+	case RMNET_IOCTL_SET_LLP_ETHERNET:  /* Set Ethernet protocol   */
+		/* Perform Ethernet config only if in IP mode currently*/
+		if (p->operation_mode & RMNET_MODE_LLP_IP) {
+			ether_setup(dev);
+			random_ether_addr(dev->dev_addr);
+			dev->mtu = prev_mtu;
+
+			dev->netdev_ops = &rmnet_ops_ether;
+			spin_lock_irqsave(&p->lock, flags);
+			p->operation_mode &= ~RMNET_MODE_LLP_IP;
+			p->operation_mode |= RMNET_MODE_LLP_ETH;
+			spin_unlock_irqrestore(&p->lock, flags);
+			DBG0("[%s] rmnet_ioctl(): "
+				"set Ethernet protocol mode\n",
+				dev->name);
+		}
+		break;
+
+	case RMNET_IOCTL_SET_LLP_IP:        /* Set RAWIP protocol      */
+		/* Perform IP config only if in Ethernet mode currently*/
+		if (p->operation_mode & RMNET_MODE_LLP_ETH) {
+
+			/* Undo config done in ether_setup() */
+			dev->header_ops         = 0;  /* No header */
+			dev->type               = ARPHRD_RAWIP;
+			dev->hard_header_len    = 0;
+			dev->mtu                = prev_mtu;
+			dev->addr_len           = 0;
+			dev->flags              &= ~(IFF_BROADCAST|
+						     IFF_MULTICAST);
+
+			dev->needed_headroom = HEADROOM_FOR_BAM +
+			  HEADROOM_FOR_QOS;
+			dev->needed_tailroom = TAILROOM;
+			dev->netdev_ops = &rmnet_ops_ip;
+			spin_lock_irqsave(&p->lock, flags);
+			p->operation_mode &= ~RMNET_MODE_LLP_ETH;
+			p->operation_mode |= RMNET_MODE_LLP_IP;
+			spin_unlock_irqrestore(&p->lock, flags);
+			DBG0("[%s] rmnet_ioctl(): "
+				"set IP protocol mode\n",
+				dev->name);
+		}
+		break;
+
+	case RMNET_IOCTL_GET_LLP:           /* Get link protocol state */
+		ifr->ifr_ifru.ifru_data =
+			(void *)(p->operation_mode &
+				 (RMNET_MODE_LLP_ETH|RMNET_MODE_LLP_IP));
+		break;
+
+	case RMNET_IOCTL_SET_QOS_ENABLE:    /* Set QoS header enabled  */
+		spin_lock_irqsave(&p->lock, flags);
+		p->operation_mode |= RMNET_MODE_QOS;
+		spin_unlock_irqrestore(&p->lock, flags);
+		DBG0("[%s] rmnet_ioctl(): set QMI QOS header enable\n",
+			dev->name);
+		break;
+
+	case RMNET_IOCTL_SET_QOS_DISABLE:   /* Set QoS header disabled */
+		spin_lock_irqsave(&p->lock, flags);
+		p->operation_mode &= ~RMNET_MODE_QOS;
+		spin_unlock_irqrestore(&p->lock, flags);
+		DBG0("[%s] rmnet_ioctl(): set QMI QOS header disable\n",
+			dev->name);
+		break;
+
+	case RMNET_IOCTL_GET_QOS:           /* Get QoS header state    */
+		ifr->ifr_ifru.ifru_data =
+			(void *)(p->operation_mode & RMNET_MODE_QOS);
+		break;
+
+	case RMNET_IOCTL_GET_OPMODE:        /* Get operation mode      */
+		ifr->ifr_ifru.ifru_data = (void *)p->operation_mode;
+		break;
+
+	case RMNET_IOCTL_OPEN:              /* Open transport port     */
+		rc = __rmnet_open(dev);
+		DBG0("[%s] rmnet_ioctl(): open transport port\n",
+			dev->name);
+		break;
+
+	case RMNET_IOCTL_CLOSE:             /* Close transport port    */
+		rc = __rmnet_close(dev);
+		DBG0("[%s] rmnet_ioctl(): close transport port\n",
+			dev->name);
+		break;
+
+	default:
+		pr_err("[%s] error: rmnet_ioct called for unsupported cmd[%d]",
+			dev->name, cmd);
+		return -EINVAL;
+	}
+
+	DBG2("[%s] %s: cmd=0x%x opmode old=0x%08x new=0x%08x\n",
+		dev->name, __func__, cmd, old_opmode, p->operation_mode);
+	return rc;
+}
+
+static void __init rmnet_setup(struct net_device *dev)
+{
+	/* Using Ethernet mode by default */
+	dev->netdev_ops = &rmnet_ops_ether;
+	ether_setup(dev);
+
+	/* set this after calling ether_setup */
+	dev->mtu = RMNET_DATA_LEN;
+	dev->needed_headroom = HEADROOM_FOR_BAM + HEADROOM_FOR_QOS ;
+	dev->needed_tailroom = TAILROOM;
+	random_ether_addr(dev->dev_addr);
+
+	dev->watchdog_timeo = 1000; /* 10 seconds? */
+}
+
+static struct net_device *netdevs[RMNET_DEVICE_COUNT];
+static struct platform_driver bam_rmnet_drivers[RMNET_DEVICE_COUNT];
+
+static int bam_rmnet_probe(struct platform_device *pdev)
+{
+	int i;
+	char name[BAM_DMUX_CH_NAME_MAX_LEN];
+	struct rmnet_private *p;
+
+	for (i = 0; i < RMNET_DEVICE_COUNT; ++i) {
+		scnprintf(name, BAM_DMUX_CH_NAME_MAX_LEN, "bam_dmux_ch_%d", i);
+		if (!strncmp(pdev->name, name, BAM_DMUX_CH_NAME_MAX_LEN))
+			break;
+	}
+
+	p = netdev_priv(netdevs[i]);
+	if (p->in_reset) {
+		p->in_reset = 0;
+		msm_bam_dmux_open(p->ch_id, netdevs[i], bam_notify);
+		netif_carrier_on(netdevs[i]);
+		netif_start_queue(netdevs[i]);
+	}
+
+	return 0;
+}
+
+static int bam_rmnet_remove(struct platform_device *pdev)
+{
+	int i;
+	char name[BAM_DMUX_CH_NAME_MAX_LEN];
+	struct rmnet_private *p;
+
+	for (i = 0; i < RMNET_DEVICE_COUNT; ++i) {
+		scnprintf(name, BAM_DMUX_CH_NAME_MAX_LEN, "bam_dmux_ch_%d", i);
+		if (!strncmp(pdev->name, name, BAM_DMUX_CH_NAME_MAX_LEN))
+			break;
+	}
+
+	p = netdev_priv(netdevs[i]);
+	p->in_reset = 1;
+	if (p->waiting_for_ul_skb != NULL) {
+		dev_kfree_skb_any(p->waiting_for_ul_skb);
+		p->waiting_for_ul_skb = NULL;
+	}
+	msm_bam_dmux_close(p->ch_id);
+	netif_carrier_off(netdevs[i]);
+	netif_stop_queue(netdevs[i]);
+	return 0;
+}
+
+static int __init rmnet_init(void)
+{
+	int ret;
+	struct device *d;
+	struct net_device *dev;
+	struct rmnet_private *p;
+	unsigned n;
+	char *tempname;
+
+	pr_info("%s: BAM devices[%d]\n", __func__, RMNET_DEVICE_COUNT);
+
+#ifdef CONFIG_MSM_RMNET_DEBUG
+	timeout_us = 0;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	timeout_suspend_us = 0;
+#endif
+#endif
+
+	for (n = 0; n < RMNET_DEVICE_COUNT; n++) {
+		dev = alloc_netdev(sizeof(struct rmnet_private),
+				   "rmnet%d", rmnet_setup);
+
+		if (!dev) {
+			pr_err("%s: no memory for netdev %d\n", __func__, n);
+			return -ENOMEM;
+		}
+
+		netdevs[n] = dev;
+		d = &(dev->dev);
+		p = netdev_priv(dev);
+		/* Initial config uses Ethernet */
+		p->operation_mode = RMNET_MODE_LLP_ETH;
+		p->ch_id = n;
+		p->waiting_for_ul_skb = NULL;
+		p->in_reset = 0;
+		spin_lock_init(&p->lock);
+		spin_lock_init(&p->tx_queue_lock);
+#ifdef CONFIG_MSM_RMNET_DEBUG
+		p->timeout_us = timeout_us;
+		p->wakeups_xmit = p->wakeups_rcv = 0;
+#endif
+
+		ret = register_netdev(dev);
+		if (ret) {
+			pr_err("%s: unable to register netdev"
+				   " %d rc=%d\n", __func__, n, ret);
+			free_netdev(dev);
+			return ret;
+		}
+
+#ifdef CONFIG_MSM_RMNET_DEBUG
+		if (device_create_file(d, &dev_attr_timeout))
+			continue;
+		if (device_create_file(d, &dev_attr_wakeups_xmit))
+			continue;
+		if (device_create_file(d, &dev_attr_wakeups_rcv))
+			continue;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+		if (device_create_file(d, &dev_attr_timeout_suspend))
+			continue;
+
+		/* Only care about rmnet0 for suspend/resume tiemout hooks. */
+		if (n == 0)
+			rmnet0 = d;
+#endif
+#endif
+		bam_rmnet_drivers[n].probe = bam_rmnet_probe;
+		bam_rmnet_drivers[n].remove = bam_rmnet_remove;
+		tempname = kmalloc(BAM_DMUX_CH_NAME_MAX_LEN, GFP_KERNEL);
+		if (tempname == NULL)
+			return -ENOMEM;
+		scnprintf(tempname, BAM_DMUX_CH_NAME_MAX_LEN, "bam_dmux_ch_%d",
+									n);
+		bam_rmnet_drivers[n].driver.name = tempname;
+		bam_rmnet_drivers[n].driver.owner = THIS_MODULE;
+		ret = platform_driver_register(&bam_rmnet_drivers[n]);
+		if (ret) {
+			pr_err("%s: registration failed n=%d rc=%d\n",
+					__func__, n, ret);
+			return ret;
+		}
+	}
+	return 0;
+}
+
+module_init(rmnet_init);
+MODULE_DESCRIPTION("MSM RMNET BAM TRANSPORT");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/net/ethernet/msm/msm_rmnet_sdio.c b/drivers/net/ethernet/msm/msm_rmnet_sdio.c
new file mode 100644
index 0000000..14fb612
--- /dev/null
+++ b/drivers/net/ethernet/msm/msm_rmnet_sdio.c
@@ -0,0 +1,712 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*
+ * RMNET SDIO Module.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/wakelock.h>
+#include <linux/if_arp.h>
+#include <linux/msm_rmnet.h>
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+#include <mach/sdio_dmux.h>
+
+/* Debug message support */
+static int msm_rmnet_sdio_debug_mask;
+module_param_named(debug_enable, msm_rmnet_sdio_debug_mask,
+			int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+#define DEBUG_MASK_LVL0 (1U << 0)
+#define DEBUG_MASK_LVL1 (1U << 1)
+#define DEBUG_MASK_LVL2 (1U << 2)
+
+#define DBG(m, x...) do {			   \
+		if (msm_rmnet_sdio_debug_mask & m) \
+			pr_info(x);		   \
+} while (0)
+#define DBG0(x...) DBG(DEBUG_MASK_LVL0, x)
+#define DBG1(x...) DBG(DEBUG_MASK_LVL1, x)
+#define DBG2(x...) DBG(DEBUG_MASK_LVL2, x)
+
+/* Configure device instances */
+#define RMNET_DEVICE_COUNT (8)
+
+/* allow larger frames */
+#define RMNET_DATA_LEN 2000
+
+#define DEVICE_ID_INVALID   -1
+
+#define DEVICE_INACTIVE      0
+#define DEVICE_ACTIVE        1
+
+#define HEADROOM_FOR_SDIO   8 /* for mux header */
+#define HEADROOM_FOR_QOS    8
+#define TAILROOM            8 /* for padding by mux layer */
+
+struct rmnet_private {
+	struct net_device_stats stats;
+	uint32_t ch_id;
+#ifdef CONFIG_MSM_RMNET_DEBUG
+	ktime_t last_packet;
+	unsigned long wakeups_xmit;
+	unsigned long wakeups_rcv;
+	unsigned long timeout_us;
+#endif
+	struct sk_buff *skb;
+	spinlock_t lock;
+	spinlock_t tx_queue_lock;
+	struct tasklet_struct tsklt;
+	u32 operation_mode; /* IOCTL specified mode (protocol, QoS header) */
+	uint8_t device_up;
+	uint8_t in_reset;
+};
+
+#ifdef CONFIG_MSM_RMNET_DEBUG
+static unsigned long timeout_us;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+/*
+ * If early suspend is enabled then we specify two timeout values,
+ * screen on (default), and screen is off.
+ */
+static unsigned long timeout_suspend_us;
+static struct device *rmnet0;
+
+/* Set timeout in us when the screen is off. */
+static ssize_t timeout_suspend_store(struct device *d,
+				     struct device_attribute *attr,
+				     const char *buf, size_t n)
+{
+	timeout_suspend_us = strict_strtoul(buf, NULL, 10);
+	return n;
+}
+
+static ssize_t timeout_suspend_show(struct device *d,
+				    struct device_attribute *attr,
+				    char *buf)
+{
+	return sprintf(buf, "%lu\n", (unsigned long) timeout_suspend_us);
+}
+
+static DEVICE_ATTR(timeout_suspend, 0664, timeout_suspend_show,
+		   timeout_suspend_store);
+
+static void rmnet_early_suspend(struct early_suspend *handler)
+{
+	if (rmnet0) {
+		struct rmnet_private *p = netdev_priv(to_net_dev(rmnet0));
+		p->timeout_us = timeout_suspend_us;
+	}
+}
+
+static void rmnet_late_resume(struct early_suspend *handler)
+{
+	if (rmnet0) {
+		struct rmnet_private *p = netdev_priv(to_net_dev(rmnet0));
+		p->timeout_us = timeout_us;
+	}
+}
+
+static struct early_suspend rmnet_power_suspend = {
+	.suspend = rmnet_early_suspend,
+	.resume = rmnet_late_resume,
+};
+
+static int __init rmnet_late_init(void)
+{
+	register_early_suspend(&rmnet_power_suspend);
+	return 0;
+}
+
+late_initcall(rmnet_late_init);
+#endif
+
+/* Returns 1 if packet caused rmnet to wakeup, 0 otherwise. */
+static int rmnet_cause_wakeup(struct rmnet_private *p)
+{
+	int ret = 0;
+	ktime_t now;
+	if (p->timeout_us == 0) /* Check if disabled */
+		return 0;
+
+	/* Use real (wall) time. */
+	now = ktime_get_real();
+
+	if (ktime_us_delta(now, p->last_packet) > p->timeout_us)
+		ret = 1;
+
+	p->last_packet = now;
+	return ret;
+}
+
+static ssize_t wakeups_xmit_show(struct device *d,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	struct rmnet_private *p = netdev_priv(to_net_dev(d));
+	return sprintf(buf, "%lu\n", p->wakeups_xmit);
+}
+
+DEVICE_ATTR(wakeups_xmit, 0444, wakeups_xmit_show, NULL);
+
+static ssize_t wakeups_rcv_show(struct device *d, struct device_attribute *attr,
+				char *buf)
+{
+	struct rmnet_private *p = netdev_priv(to_net_dev(d));
+	return sprintf(buf, "%lu\n", p->wakeups_rcv);
+}
+
+DEVICE_ATTR(wakeups_rcv, 0444, wakeups_rcv_show, NULL);
+
+/* Set timeout in us. */
+static ssize_t timeout_store(struct device *d, struct device_attribute *attr,
+			     const char *buf, size_t n)
+{
+#ifndef CONFIG_HAS_EARLYSUSPEND
+	struct rmnet_private *p = netdev_priv(to_net_dev(d));
+	p->timeout_us = timeout_us = strict_strtoul(buf, NULL, 10);
+#else
+/* If using early suspend/resume hooks do not write the value on store. */
+	timeout_us = strict_strtoul(buf, NULL, 10);
+#endif
+	return n;
+}
+
+static ssize_t timeout_show(struct device *d, struct device_attribute *attr,
+			    char *buf)
+{
+	struct rmnet_private *p = netdev_priv(to_net_dev(d));
+	p = netdev_priv(to_net_dev(d));
+	return sprintf(buf, "%lu\n", timeout_us);
+}
+
+DEVICE_ATTR(timeout, 0664, timeout_show, timeout_store);
+#endif
+
+
+/* Forward declaration */
+static int rmnet_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+
+static __be16 rmnet_ip_type_trans(struct sk_buff *skb, struct net_device *dev)
+{
+	__be16 protocol = 0;
+
+	skb->dev = dev;
+
+	/* Determine L3 protocol */
+	switch (skb->data[0] & 0xf0) {
+	case 0x40:
+		protocol = htons(ETH_P_IP);
+		break;
+	case 0x60:
+		protocol = htons(ETH_P_IPV6);
+		break;
+	default:
+		pr_err("[%s] rmnet_recv() L3 protocol decode error: 0x%02x",
+		       dev->name, skb->data[0] & 0xf0);
+		/* skb will be dropped in upper layer for unknown protocol */
+	}
+	return protocol;
+}
+
+static int count_this_packet(void *_hdr, int len)
+{
+	struct ethhdr *hdr = _hdr;
+
+	if (len >= ETH_HLEN && hdr->h_proto == htons(ETH_P_ARP))
+		return 0;
+
+	return 1;
+}
+
+static int sdio_update_reset_state(struct net_device *dev)
+{
+	struct rmnet_private *p = netdev_priv(dev);
+	int	new_state;
+
+	new_state = msm_sdio_is_channel_in_reset(p->ch_id);
+
+	if (p->in_reset != new_state) {
+		p->in_reset = (uint8_t)new_state;
+
+		if (p->in_reset)
+			netif_carrier_off(dev);
+		else
+			netif_carrier_on(dev);
+		return 1;
+	}
+	return 0;
+}
+
+/* Rx Callback, Called in Work Queue context */
+static void sdio_recv_notify(void *dev, struct sk_buff *skb)
+{
+	struct rmnet_private *p = netdev_priv(dev);
+	unsigned long flags;
+	u32 opmode;
+
+	if (skb) {
+		skb->dev = dev;
+		/* Handle Rx frame format */
+		spin_lock_irqsave(&p->lock, flags);
+		opmode = p->operation_mode;
+		spin_unlock_irqrestore(&p->lock, flags);
+
+		if (RMNET_IS_MODE_IP(opmode)) {
+			/* Driver in IP mode */
+			skb->protocol = rmnet_ip_type_trans(skb, dev);
+		} else {
+			/* Driver in Ethernet mode */
+			skb->protocol = eth_type_trans(skb, dev);
+		}
+		if (RMNET_IS_MODE_IP(opmode) ||
+		    count_this_packet(skb->data, skb->len)) {
+#ifdef CONFIG_MSM_RMNET_DEBUG
+			p->wakeups_rcv += rmnet_cause_wakeup(p);
+#endif
+			p->stats.rx_packets++;
+			p->stats.rx_bytes += skb->len;
+		}
+		DBG1("[%s] Rx packet #%lu len=%d\n",
+			((struct net_device *)dev)->name,
+			p->stats.rx_packets, skb->len);
+
+		/* Deliver to network stack */
+		netif_rx(skb);
+	} else {
+		spin_lock_irqsave(&p->lock, flags);
+		if (!sdio_update_reset_state((struct net_device *)dev))
+			pr_err("[%s] %s: No skb received",
+				((struct net_device *)dev)->name, __func__);
+		spin_unlock_irqrestore(&p->lock, flags);
+	}
+}
+
+static int _rmnet_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct rmnet_private *p = netdev_priv(dev);
+	int sdio_ret;
+	struct QMI_QOS_HDR_S *qmih;
+	u32 opmode;
+	unsigned long flags;
+
+	if (!netif_carrier_ok(dev)) {
+		pr_err("[%s] %s: channel in reset",
+			dev->name, __func__);
+		goto xmit_out;
+	}
+
+	/* For QoS mode, prepend QMI header and assign flow ID from skb->mark */
+	spin_lock_irqsave(&p->lock, flags);
+	opmode = p->operation_mode;
+	spin_unlock_irqrestore(&p->lock, flags);
+
+	if (RMNET_IS_MODE_QOS(opmode)) {
+		qmih = (struct QMI_QOS_HDR_S *)
+			skb_push(skb, sizeof(struct QMI_QOS_HDR_S));
+		qmih->version = 1;
+		qmih->flags = 0;
+		qmih->flow_id = skb->mark;
+	}
+
+	dev->trans_start = jiffies;
+	sdio_ret = msm_sdio_dmux_write(p->ch_id, skb);
+
+	if (sdio_ret != 0) {
+		pr_err("[%s] %s: write returned error %d",
+			dev->name, __func__, sdio_ret);
+		goto xmit_out;
+	}
+
+	if (count_this_packet(skb->data, skb->len)) {
+		p->stats.tx_packets++;
+		p->stats.tx_bytes += skb->len;
+#ifdef CONFIG_MSM_RMNET_DEBUG
+		p->wakeups_xmit += rmnet_cause_wakeup(p);
+#endif
+	}
+	DBG1("[%s] Tx packet #%lu len=%d mark=0x%x\n",
+	    dev->name, p->stats.tx_packets, skb->len, skb->mark);
+
+	return 0;
+xmit_out:
+	dev_kfree_skb_any(skb);
+	p->stats.tx_errors++;
+	return 0;
+}
+
+static void sdio_write_done(void *dev, struct sk_buff *skb)
+{
+	struct rmnet_private *p = netdev_priv(dev);
+	unsigned long flags;
+
+	if (skb)
+		dev_kfree_skb_any(skb);
+
+	if (!p->in_reset) {
+		DBG1("%s: write complete skb=%p\n",	__func__, skb);
+
+		spin_lock_irqsave(&p->tx_queue_lock, flags);
+		if (netif_queue_stopped(dev) &&
+				msm_sdio_dmux_is_ch_low(p->ch_id)) {
+			DBG0("%s: Low WM hit, waking queue=%p\n",
+					__func__, skb);
+			netif_wake_queue(dev);
+		}
+		spin_unlock_irqrestore(&p->tx_queue_lock, flags);
+	} else {
+		DBG1("%s: write in reset skb=%p\n",	__func__, skb);
+	}
+}
+
+static int __rmnet_open(struct net_device *dev)
+{
+	int r;
+	struct rmnet_private *p = netdev_priv(dev);
+
+	DBG0("[%s] __rmnet_open()\n", dev->name);
+
+	if (!p->device_up) {
+		r = msm_sdio_dmux_open(p->ch_id, dev,
+				       sdio_recv_notify, sdio_write_done);
+
+		if (r < 0)
+			return -ENODEV;
+	}
+
+	p->device_up = DEVICE_ACTIVE;
+	return 0;
+}
+
+static int rmnet_open(struct net_device *dev)
+{
+	int rc = 0;
+
+	DBG0("[%s] rmnet_open()\n", dev->name);
+
+	rc = __rmnet_open(dev);
+
+	if (rc == 0)
+		netif_start_queue(dev);
+
+	return rc;
+}
+
+
+static int __rmnet_close(struct net_device *dev)
+{
+	struct rmnet_private *p = netdev_priv(dev);
+	int rc = 0;
+
+	if (p->device_up) {
+		/* do not close rmnet port once up,  this causes
+		   remote side to hang if tried to open again */
+		/* rc = msm_sdio_dmux_close(p->ch_id); */
+		p->device_up = DEVICE_INACTIVE;
+		return rc;
+	} else
+		return -EBADF;
+}
+
+
+static int rmnet_stop(struct net_device *dev)
+{
+	DBG0("[%s] rmnet_stop()\n", dev->name);
+
+	__rmnet_close(dev);
+	netif_stop_queue(dev);
+
+	return 0;
+}
+
+static int rmnet_change_mtu(struct net_device *dev, int new_mtu)
+{
+	if (0 > new_mtu || RMNET_DATA_LEN < new_mtu)
+		return -EINVAL;
+
+	DBG0("[%s] MTU change: old=%d new=%d\n",
+		dev->name, dev->mtu, new_mtu);
+	dev->mtu = new_mtu;
+
+	return 0;
+}
+
+static int rmnet_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct rmnet_private *p = netdev_priv(dev);
+	unsigned long flags;
+
+	if (netif_queue_stopped(dev)) {
+		pr_err("[%s]fatal: rmnet_xmit called when "
+			"netif_queue is stopped", dev->name);
+		return 0;
+	}
+
+	_rmnet_xmit(skb, dev);
+
+	spin_lock_irqsave(&p->tx_queue_lock, flags);
+	if (msm_sdio_dmux_is_ch_full(p->ch_id)) {
+		netif_stop_queue(dev);
+		DBG0("%s: High WM hit, stopping queue=%p\n",	__func__, skb);
+	}
+	spin_unlock_irqrestore(&p->tx_queue_lock, flags);
+
+	return 0;
+}
+
+static struct net_device_stats *rmnet_get_stats(struct net_device *dev)
+{
+	struct rmnet_private *p = netdev_priv(dev);
+	return &p->stats;
+}
+
+static void rmnet_set_multicast_list(struct net_device *dev)
+{
+}
+
+static void rmnet_tx_timeout(struct net_device *dev)
+{
+	pr_warning("[%s] rmnet_tx_timeout()\n", dev->name);
+}
+
+static const struct net_device_ops rmnet_ops_ether = {
+	.ndo_open = rmnet_open,
+	.ndo_stop = rmnet_stop,
+	.ndo_start_xmit = rmnet_xmit,
+	.ndo_get_stats = rmnet_get_stats,
+	.ndo_set_rx_mode = rmnet_set_multicast_list,
+	.ndo_tx_timeout = rmnet_tx_timeout,
+	.ndo_do_ioctl = rmnet_ioctl,
+	.ndo_change_mtu = rmnet_change_mtu,
+	.ndo_set_mac_address = eth_mac_addr,
+	.ndo_validate_addr = eth_validate_addr,
+};
+
+static const struct net_device_ops rmnet_ops_ip = {
+	.ndo_open = rmnet_open,
+	.ndo_stop = rmnet_stop,
+	.ndo_start_xmit = rmnet_xmit,
+	.ndo_get_stats = rmnet_get_stats,
+	.ndo_set_rx_mode = rmnet_set_multicast_list,
+	.ndo_tx_timeout = rmnet_tx_timeout,
+	.ndo_do_ioctl = rmnet_ioctl,
+	.ndo_change_mtu = rmnet_change_mtu,
+	.ndo_set_mac_address = 0,
+	.ndo_validate_addr = 0,
+};
+
+static int rmnet_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	struct rmnet_private *p = netdev_priv(dev);
+	u32 old_opmode = p->operation_mode;
+	unsigned long flags;
+	int prev_mtu = dev->mtu;
+	int rc = 0;
+
+	/* Process IOCTL command */
+	switch (cmd) {
+	case RMNET_IOCTL_SET_LLP_ETHERNET:  /* Set Ethernet protocol   */
+		/* Perform Ethernet config only if in IP mode currently*/
+		if (p->operation_mode & RMNET_MODE_LLP_IP) {
+			ether_setup(dev);
+			random_ether_addr(dev->dev_addr);
+			dev->mtu = prev_mtu;
+
+			dev->netdev_ops = &rmnet_ops_ether;
+			spin_lock_irqsave(&p->lock, flags);
+			p->operation_mode &= ~RMNET_MODE_LLP_IP;
+			p->operation_mode |= RMNET_MODE_LLP_ETH;
+			spin_unlock_irqrestore(&p->lock, flags);
+			DBG0("[%s] rmnet_ioctl(): "
+				"set Ethernet protocol mode\n",
+				dev->name);
+		}
+		break;
+
+	case RMNET_IOCTL_SET_LLP_IP:        /* Set RAWIP protocol      */
+		/* Perform IP config only if in Ethernet mode currently*/
+		if (p->operation_mode & RMNET_MODE_LLP_ETH) {
+
+			/* Undo config done in ether_setup() */
+			dev->header_ops         = 0;  /* No header */
+			dev->type               = ARPHRD_RAWIP;
+			dev->hard_header_len    = 0;
+			dev->mtu                = prev_mtu;
+			dev->addr_len           = 0;
+			dev->flags              &= ~(IFF_BROADCAST|
+						     IFF_MULTICAST);
+
+			dev->needed_headroom = HEADROOM_FOR_SDIO +
+			  HEADROOM_FOR_QOS;
+			dev->needed_tailroom = TAILROOM;
+			dev->netdev_ops = &rmnet_ops_ip;
+			spin_lock_irqsave(&p->lock, flags);
+			p->operation_mode &= ~RMNET_MODE_LLP_ETH;
+			p->operation_mode |= RMNET_MODE_LLP_IP;
+			spin_unlock_irqrestore(&p->lock, flags);
+			DBG0("[%s] rmnet_ioctl(): "
+				"set IP protocol mode\n",
+				dev->name);
+		}
+		break;
+
+	case RMNET_IOCTL_GET_LLP:           /* Get link protocol state */
+		ifr->ifr_ifru.ifru_data =
+			(void *)(p->operation_mode &
+				 (RMNET_MODE_LLP_ETH|RMNET_MODE_LLP_IP));
+		break;
+
+	case RMNET_IOCTL_SET_QOS_ENABLE:    /* Set QoS header enabled  */
+		spin_lock_irqsave(&p->lock, flags);
+		p->operation_mode |= RMNET_MODE_QOS;
+		spin_unlock_irqrestore(&p->lock, flags);
+		DBG0("[%s] rmnet_ioctl(): set QMI QOS header enable\n",
+			dev->name);
+		break;
+
+	case RMNET_IOCTL_SET_QOS_DISABLE:   /* Set QoS header disabled */
+		spin_lock_irqsave(&p->lock, flags);
+		p->operation_mode &= ~RMNET_MODE_QOS;
+		spin_unlock_irqrestore(&p->lock, flags);
+		DBG0("[%s] rmnet_ioctl(): set QMI QOS header disable\n",
+			dev->name);
+		break;
+
+	case RMNET_IOCTL_GET_QOS:           /* Get QoS header state    */
+		ifr->ifr_ifru.ifru_data =
+			(void *)(p->operation_mode & RMNET_MODE_QOS);
+		break;
+
+	case RMNET_IOCTL_GET_OPMODE:        /* Get operation mode      */
+		ifr->ifr_ifru.ifru_data = (void *)p->operation_mode;
+		break;
+
+	case RMNET_IOCTL_OPEN:              /* Open transport port     */
+		rc = __rmnet_open(dev);
+		DBG0("[%s] rmnet_ioctl(): open transport port\n",
+			dev->name);
+		break;
+
+	case RMNET_IOCTL_CLOSE:             /* Close transport port    */
+		rc = __rmnet_close(dev);
+		DBG0("[%s] rmnet_ioctl(): close transport port\n",
+			dev->name);
+		break;
+
+	default:
+		pr_err("[%s] error: rmnet_ioct called for unsupported cmd[%d]",
+			dev->name, cmd);
+		return -EINVAL;
+	}
+
+	DBG2("[%s] %s: cmd=0x%x opmode old=0x%08x new=0x%08x\n",
+		dev->name, __func__, cmd, old_opmode, p->operation_mode);
+	return rc;
+}
+
+static void __init rmnet_setup(struct net_device *dev)
+{
+	/* Using Ethernet mode by default */
+	dev->netdev_ops = &rmnet_ops_ether;
+	ether_setup(dev);
+
+	/* set this after calling ether_setup */
+	dev->mtu = RMNET_DATA_LEN;
+	dev->needed_headroom = HEADROOM_FOR_SDIO + HEADROOM_FOR_QOS ;
+	dev->needed_tailroom = TAILROOM;
+	random_ether_addr(dev->dev_addr);
+
+	dev->watchdog_timeo = 1000; /* 10 seconds? */
+}
+
+
+static int __init rmnet_init(void)
+{
+	int ret;
+	struct device *d;
+	struct net_device *dev;
+	struct rmnet_private *p;
+	unsigned n;
+
+	pr_info("%s: SDIO devices[%d]\n", __func__, RMNET_DEVICE_COUNT);
+
+#ifdef CONFIG_MSM_RMNET_DEBUG
+	timeout_us = 0;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	timeout_suspend_us = 0;
+#endif
+#endif
+
+	for (n = 0; n < RMNET_DEVICE_COUNT; n++) {
+		dev = alloc_netdev(sizeof(struct rmnet_private),
+				   "rmnet_sdio%d", rmnet_setup);
+
+		if (!dev)
+			return -ENOMEM;
+
+		d = &(dev->dev);
+		p = netdev_priv(dev);
+		/* Initial config uses Ethernet */
+		p->operation_mode = RMNET_MODE_LLP_ETH;
+		p->ch_id = n;
+		spin_lock_init(&p->lock);
+		spin_lock_init(&p->tx_queue_lock);
+#ifdef CONFIG_MSM_RMNET_DEBUG
+		p->timeout_us = timeout_us;
+		p->wakeups_xmit = p->wakeups_rcv = 0;
+#endif
+
+		ret = register_netdev(dev);
+		if (ret) {
+			free_netdev(dev);
+			return ret;
+		}
+
+#ifdef CONFIG_MSM_RMNET_DEBUG
+		if (device_create_file(d, &dev_attr_timeout))
+			continue;
+		if (device_create_file(d, &dev_attr_wakeups_xmit))
+			continue;
+		if (device_create_file(d, &dev_attr_wakeups_rcv))
+			continue;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+		if (device_create_file(d, &dev_attr_timeout_suspend))
+			continue;
+
+		/* Only care about rmnet0 for suspend/resume tiemout hooks. */
+		if (n == 0)
+			rmnet0 = d;
+#endif
+#endif
+	}
+	return 0;
+}
+
+module_init(rmnet_init);
+MODULE_DESCRIPTION("MSM RMNET SDIO TRANSPORT");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/net/ethernet/msm/msm_rmnet_smux.c b/drivers/net/ethernet/msm/msm_rmnet_smux.c
new file mode 100644
index 0000000..fbb3489
--- /dev/null
+++ b/drivers/net/ethernet/msm/msm_rmnet_smux.c
@@ -0,0 +1,938 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*
+ * RMNET SMUX Module.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/wakelock.h>
+#include <linux/if_arp.h>
+#include <linux/msm_rmnet.h>
+#include <linux/platform_device.h>
+#include <linux/smux.h>
+#include <linux/ip.h>
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+
+/* Debug message support */
+static int msm_rmnet_smux_debug_mask;
+module_param_named(debug_enable, msm_rmnet_smux_debug_mask,
+				   int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+#define DEBUG_MASK_LVL0 (1U << 0)
+#define DEBUG_MASK_LVL1 (1U << 1)
+#define DEBUG_MASK_LVL2 (1U << 2)
+
+#define DBG(m, x...) do {			   \
+		if (msm_rmnet_smux_debug_mask & m) \
+			pr_info(x);		   \
+} while (0)
+
+#define DBG0(x...) DBG(DEBUG_MASK_LVL0, x)
+#define DBG1(x...) DBG(DEBUG_MASK_LVL1, x)
+#define DBG2(x...) DBG(DEBUG_MASK_LVL2, x)
+
+/* Configure device instances */
+#define RMNET_SMUX_DEVICE_COUNT (1)
+
+/* allow larger frames */
+#define RMNET_DATA_LEN 2000
+
+#define DEVICE_ID_INVALID   -1
+
+#define DEVICE_INACTIVE     0x00
+#define DEVICE_ACTIVE       0x01
+
+#define HEADROOM_FOR_SMUX    8 /* for mux header */
+#define HEADROOM_FOR_QOS     8
+#define TAILROOM             8 /* for padding by mux layer */
+
+struct rmnet_private {
+	struct net_device_stats stats;
+	uint32_t ch_id;
+#ifdef CONFIG_MSM_RMNET_DEBUG
+	ktime_t last_packet;
+	unsigned long wakeups_xmit;
+	unsigned long wakeups_rcv;
+	unsigned long timeout_us;
+#endif
+	spinlock_t lock;
+	struct tasklet_struct tsklt;
+	/* IOCTL specified mode (protocol, QoS header) */
+	u32 operation_mode;
+	uint8_t device_state;
+	uint8_t in_reset;
+};
+
+static struct net_device *netdevs[RMNET_SMUX_DEVICE_COUNT];
+
+#ifdef CONFIG_MSM_RMNET_DEBUG
+static unsigned long timeout_us;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+/*
+ * If early suspend is enabled then we specify two timeout values,
+ * screen on (default), and screen is off.
+ */
+static unsigned long timeout_suspend_us;
+static struct device *rmnet0;
+
+/* Set timeout in us when the screen is off. */
+static ssize_t timeout_suspend_store(struct device *d,
+				     struct device_attribute *attr,
+				     const char *buf, size_t n)
+{
+	timeout_suspend_us = strict_strtoul(buf, NULL, 10);
+	return n;
+}
+
+static ssize_t timeout_suspend_show(struct device *d,
+				    struct device_attribute *attr,
+				    char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%lu\n",
+			(unsigned long) timeout_suspend_us);
+}
+
+static DEVICE_ATTR(timeout_suspend, 0664, timeout_suspend_show,
+				   timeout_suspend_store);
+
+static void rmnet_early_suspend(struct early_suspend *handler)
+{
+	if (rmnet0) {
+		struct rmnet_private *p = netdev_priv(to_net_dev(rmnet0));
+		p->timeout_us = timeout_suspend_us;
+	}
+}
+
+static void rmnet_late_resume(struct early_suspend *handler)
+{
+	if (rmnet0) {
+		struct rmnet_private *p = netdev_priv(to_net_dev(rmnet0));
+		p->timeout_us = timeout_us;
+	}
+}
+
+static struct early_suspend rmnet_power_suspend = {
+	.suspend = rmnet_early_suspend,
+	.resume = rmnet_late_resume,
+};
+
+static int __init rmnet_late_init(void)
+{
+	register_early_suspend(&rmnet_power_suspend);
+	return 0;
+}
+
+late_initcall(rmnet_late_init);
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+
+/* Returns 1 if packet caused rmnet to wakeup, 0 otherwise. */
+static int rmnet_cause_wakeup(struct rmnet_private *p)
+{
+	int ret = 0;
+	ktime_t now;
+	if (p->timeout_us == 0)	/* Check if disabled */
+		return 0;
+
+	/* Use real (wall) time. */
+	now = ktime_get_real();
+
+	if (ktime_us_delta(now, p->last_packet) > p->timeout_us)
+		ret = 1;
+
+	p->last_packet = now;
+	return ret;
+}
+
+static ssize_t wakeups_xmit_show(struct device *d,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	struct rmnet_private *p = netdev_priv(to_net_dev(d));
+	return snprintf(buf, PAGE_SIZE, "%lu\n", p->wakeups_xmit);
+}
+
+DEVICE_ATTR(wakeups_xmit, 0444, wakeups_xmit_show, NULL);
+
+static ssize_t wakeups_rcv_show(struct device *d,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct rmnet_private *p = netdev_priv(to_net_dev(d));
+	return snprintf(buf, PAGE_SIZE, "%lu\n", p->wakeups_rcv);
+}
+
+DEVICE_ATTR(wakeups_rcv, 0444, wakeups_rcv_show, NULL);
+
+/* Set timeout in us. */
+static ssize_t timeout_store(struct device *d,
+			     struct device_attribute *attr,
+			     const char *buf, size_t n)
+{
+#ifndef CONFIG_HAS_EARLYSUSPEND
+	struct rmnet_private *p = netdev_priv(to_net_dev(d));
+	p->timeout_us = timeout_us = strict_strtoul(buf, NULL, 10);
+#else
+/* If using early suspend/resume hooks do not write the value on store. */
+	timeout_us = strict_strtoul(buf, NULL, 10);
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+	return n;
+}
+
+static ssize_t timeout_show(struct device *d,
+			    struct device_attribute *attr,
+			    char *buf)
+{
+	struct rmnet_private *p = netdev_priv(to_net_dev(d));
+	p = netdev_priv(to_net_dev(d));
+	return snprintf(buf, PAGE_SIZE, "%lu\n", timeout_us);
+}
+
+DEVICE_ATTR(timeout, 0664, timeout_show, timeout_store);
+#endif /* CONFIG_MSM_RMNET_DEBUG */
+
+/* Forward declaration */
+static int rmnet_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+
+
+
+
+static int count_this_packet(void *_hdr, int len)
+{
+	struct ethhdr *hdr = _hdr;
+
+	if (len >= ETH_HLEN && hdr->h_proto == htons(ETH_P_ARP))
+		return 0;
+
+	return 1;
+}
+
+static __be16 rmnet_ip_type_trans(struct sk_buff *skb, struct net_device *dev)
+{
+	__be16 protocol = 0;
+
+	skb->dev = dev;
+
+	/* Determine L3 protocol */
+	switch (skb->data[0] & 0xf0) {
+	case 0x40:
+		protocol = htons(ETH_P_IP);
+		break;
+	case 0x60:
+		protocol = htons(ETH_P_IPV6);
+		break;
+	default:
+		pr_err("[%s] rmnet_recv() L3 protocol decode error: 0x%02x",
+			   dev->name, skb->data[0] & 0xf0);
+		/* skb will be dropped in upper layer for unknown protocol */
+	}
+	return protocol;
+}
+
+static void smux_read_done(void *rcv_dev, const void *meta_data)
+{
+	struct rmnet_private *p;
+	struct net_device *dev = rcv_dev;
+	u32 opmode;
+	unsigned long flags;
+	struct sk_buff *skb = NULL;
+	const struct smux_meta_read  *read_meta_info = meta_data;
+
+	if (!dev || !read_meta_info) {
+		DBG1("%s:invalid read_done callback recieved", __func__);
+		return;
+	}
+
+	p = netdev_priv(dev);
+
+	skb = (struct sk_buff *) read_meta_info->pkt_priv;
+
+	if (!skb || skb->dev != dev) {
+		DBG1("%s: ERR:skb pointer NULL in READ_DONE CALLBACK",
+		      __func__);
+		return;
+	}
+
+	/* Handle Rx frame format */
+	spin_lock_irqsave(&p->lock, flags);
+	opmode = p->operation_mode;
+	spin_unlock_irqrestore(&p->lock, flags);
+
+	if (RMNET_IS_MODE_IP(opmode)) {
+		/* Driver in IP mode */
+		skb->protocol =
+		rmnet_ip_type_trans(skb, dev);
+	} else {
+		/* Driver in Ethernet mode */
+		skb->protocol =
+		eth_type_trans(skb, dev);
+	}
+	if (RMNET_IS_MODE_IP(opmode) ||
+		count_this_packet(skb->data, skb->len)) {
+#ifdef CONFIG_MSM_RMNET_DEBUG
+		p->wakeups_rcv +=
+		rmnet_cause_wakeup(p);
+#endif
+		p->stats.rx_packets++;
+		p->stats.rx_bytes += skb->len;
+	}
+	DBG2("[%s] Rx packet #%lu len=%d\n",
+		 dev->name, p->stats.rx_packets,
+		 skb->len);
+	/* Deliver to network stack */
+	netif_rx(skb);
+
+	return;
+}
+
+static void smux_write_done(void *dev, const void *meta_data)
+{
+	struct rmnet_private *p = netdev_priv(dev);
+	u32 opmode;
+	struct sk_buff *skb = NULL;
+	const struct smux_meta_write  *write_meta_info = meta_data;
+	unsigned long flags;
+
+	if (!dev || !write_meta_info) {
+		DBG1("%s: ERR:invalid WRITE_DONE callback recieved", __func__);
+		return;
+	}
+
+	skb = (struct sk_buff *) write_meta_info->pkt_priv;
+
+	if (!skb) {
+		DBG1("%s: ERR:skb pointer NULL in WRITE_DONE"
+		     " CALLBACK", __func__);
+		return;
+	}
+
+	spin_lock_irqsave(&p->lock, flags);
+	opmode = p->operation_mode;
+	spin_unlock_irqrestore(&p->lock, flags);
+
+	DBG1("%s: write complete\n", __func__);
+	if (RMNET_IS_MODE_IP(opmode) ||
+		count_this_packet(skb->data, skb->len)) {
+		p->stats.tx_packets++;
+		p->stats.tx_bytes += skb->len;
+#ifdef CONFIG_MSM_RMNET_DEBUG
+		p->wakeups_xmit += rmnet_cause_wakeup(p);
+#endif
+	}
+	DBG1("[%s] Tx packet #%lu len=%d mark=0x%x\n",
+		 ((struct net_device *)(dev))->name, p->stats.tx_packets,
+		 skb->len, skb->mark);
+	dev_kfree_skb_any(skb);
+	if (netif_queue_stopped(dev) &&
+		msm_smux_is_ch_low(p->ch_id)) {
+		DBG0("%s: Low WM hit, waking queue=%p\n",
+			 __func__, skb);
+		netif_wake_queue(dev);
+	}
+}
+
+void rmnet_smux_notify(void *priv, int event_type, const void *metadata)
+{
+	struct rmnet_private *p;
+	struct net_device *dev;
+	unsigned long flags;
+	struct sk_buff *skb = NULL;
+	u32 opmode;
+	const struct smux_meta_disconnected *ssr_info;
+	const struct smux_meta_read *read_meta_info;
+	const struct smux_meta_write *write_meta_info = metadata;
+
+
+	if (!priv)
+		DBG0("%s: priv(cookie) NULL, ignoring notification:"
+		     " %d\n", __func__, event_type);
+
+	switch (event_type) {
+	case SMUX_CONNECTED:
+		p = netdev_priv(priv);
+		dev = priv;
+
+		DBG0("[%s] SMUX_CONNECTED event dev:%s\n", __func__, dev->name);
+
+		netif_carrier_on(dev);
+		netif_start_queue(dev);
+
+		spin_lock_irqsave(&p->lock, flags);
+		p->device_state = DEVICE_ACTIVE;
+		spin_unlock_irqrestore(&p->lock, flags);
+		break;
+
+	case SMUX_DISCONNECTED:
+		p = netdev_priv(priv);
+		dev = priv;
+		ssr_info = metadata;
+
+		DBG0("[%s] SMUX_DISCONNECTED event dev:%s\n",
+		      __func__, dev->name);
+
+		if (ssr_info && ssr_info->is_ssr == 1)
+			DBG0("SSR detected on :%s\n", dev->name);
+
+		netif_carrier_off(dev);
+		netif_stop_queue(dev);
+
+		spin_lock_irqsave(&p->lock, flags);
+		p->device_state = DEVICE_INACTIVE;
+		spin_unlock_irqrestore(&p->lock, flags);
+		break;
+
+	case SMUX_READ_DONE:
+		smux_read_done(priv, metadata);
+		break;
+
+	case SMUX_READ_FAIL:
+		p = netdev_priv(priv);
+		dev = priv;
+		read_meta_info = metadata;
+
+		if (!dev || !read_meta_info) {
+			DBG1("%s: ERR:invalid read failed callback"
+			     " recieved", __func__);
+			return;
+		}
+
+		skb = (struct sk_buff *) read_meta_info->pkt_priv;
+
+		if (!skb) {
+			DBG1("%s: ERR:skb pointer NULL in read fail"
+			     " CALLBACK", __func__);
+			return;
+		}
+
+		DBG0("%s: read failed\n", __func__);
+
+		opmode = p->operation_mode;
+
+		if (RMNET_IS_MODE_IP(opmode) ||
+		    count_this_packet(skb->data, skb->len))
+			p->stats.rx_dropped++;
+
+		dev_kfree_skb_any(skb);
+		break;
+
+	case SMUX_WRITE_DONE:
+		smux_write_done(priv, metadata);
+		break;
+
+	case SMUX_WRITE_FAIL:
+		p = netdev_priv(priv);
+		dev = priv;
+		write_meta_info = metadata;
+
+		if (!dev || !write_meta_info) {
+			DBG1("%s: ERR:invalid WRITE_DONE"
+			     "callback recieved", __func__);
+			return;
+		}
+
+		skb = (struct sk_buff *) write_meta_info->pkt_priv;
+
+		if (!skb) {
+			DBG1("%s: ERR:skb pointer NULL in"
+			     " WRITE_DONE CALLBACK", __func__);
+			return;
+		}
+
+		DBG0("%s: write failed\n", __func__);
+
+		opmode = p->operation_mode;
+
+		if (RMNET_IS_MODE_IP(opmode) ||
+		    count_this_packet(skb->data, skb->len)) {
+			p->stats.tx_dropped++;
+		}
+
+		dev_kfree_skb_any(skb);
+		break;
+
+	case SMUX_LOW_WM_HIT:
+		dev = priv;
+		DBG0("[%s] Low WM hit dev:%s\n", __func__, dev->name);
+		netif_start_queue(dev);
+		break;
+
+	case SMUX_HIGH_WM_HIT:
+		dev = priv;
+		DBG0("[%s] Low WM hit dev:%s\n", __func__, dev->name);
+		netif_stop_queue(dev);
+		break;
+
+	default:
+		dev = priv;
+		DBG0("[%s] Invalid event:%d received on"
+		     " dev: %s\n", __func__, event_type, dev->name);
+		break;
+	}
+
+	return;
+}
+
+int get_rx_buffers(void *priv, void **pkt_priv, void **buffer, int size)
+{
+	struct net_device *dev = (struct net_device *) priv;
+	struct sk_buff *skb = NULL;
+	void *ptr = NULL;
+
+	DBG0("[%s] dev:%s\n", __func__, dev->name);
+	skb = __dev_alloc_skb(size, GFP_ATOMIC);
+	if (skb == NULL) {
+		DBG0("%s: unable to alloc skb\n", __func__);
+		return -ENOMEM;
+	}
+
+	/* TODO skb_reserve(skb, NET_IP_ALIGN); for ethernet mode */
+	/* Populate some params now. */
+	skb->dev = dev;
+	ptr = skb_put(skb, size);
+
+	skb_set_network_header(skb, 0);
+
+	/* done with skb setup, return the buffer pointer. */
+	*pkt_priv = skb;
+	*buffer = ptr;
+
+	return 0;
+}
+
+static int __rmnet_open(struct net_device *dev)
+{
+	struct rmnet_private *p = netdev_priv(dev);
+
+	DBG0("[%s] __rmnet_open()\n", dev->name);
+
+	if (p->device_state == DEVICE_ACTIVE) {
+		return 0;
+	} else {
+		DBG0("[%s] Platform inactive\n", dev->name);
+		return -ENODEV;
+	}
+}
+
+static int rmnet_open(struct net_device *dev)
+{
+	int rc = 0;
+
+	DBG0("[%s] rmnet_open()\n", dev->name);
+
+	rc = __rmnet_open(dev);
+
+	if (rc == 0)
+		netif_start_queue(dev);
+
+	return rc;
+}
+
+static int rmnet_stop(struct net_device *dev)
+{
+	DBG0("[%s] rmnet_stop()\n", dev->name);
+
+	netif_stop_queue(dev);
+	return 0;
+}
+
+static int rmnet_change_mtu(struct net_device *dev, int new_mtu)
+{
+	if (0 > new_mtu || RMNET_DATA_LEN < new_mtu)
+		return -EINVAL;
+
+	DBG0("[%s] MTU change: old=%d new=%d\n",
+		 dev->name, dev->mtu, new_mtu);
+	dev->mtu = new_mtu;
+
+	return 0;
+}
+
+static int _rmnet_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct rmnet_private *p = netdev_priv(dev);
+	int smux_ret;
+	struct QMI_QOS_HDR_S *qmih;
+	u32 opmode;
+	unsigned long flags;
+
+	/* For QoS mode, prepend QMI header and assign flow ID from skb->mark */
+	spin_lock_irqsave(&p->lock, flags);
+	opmode = p->operation_mode;
+	spin_unlock_irqrestore(&p->lock, flags);
+
+	if (RMNET_IS_MODE_QOS(opmode)) {
+		qmih = (struct QMI_QOS_HDR_S *)
+			   skb_push(skb, sizeof(struct QMI_QOS_HDR_S));
+		qmih->version = 1;
+		qmih->flags = 0;
+		qmih->flow_id = skb->mark;
+	}
+
+	dev->trans_start = jiffies;
+
+	/* if write() succeeds, skb access is unsafe in this process */
+	smux_ret = msm_smux_write(p->ch_id, skb, skb->data, skb->len);
+
+	if (smux_ret != 0 && smux_ret != -EAGAIN) {
+		pr_err("[%s] %s: write returned error %d",
+			   dev->name, __func__, smux_ret);
+		return -EPERM;
+	}
+
+	return smux_ret;
+}
+
+static int rmnet_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct rmnet_private *p = netdev_priv(dev);
+	int ret = 0;
+
+	if (netif_queue_stopped(dev) || (p->device_state == DEVICE_INACTIVE)) {
+		pr_err("[%s]fatal: rmnet_xmit called when "
+			   "netif_queue is stopped", dev->name);
+		return 0;
+	}
+
+	ret = _rmnet_xmit(skb, dev);
+
+	if (ret == -EPERM) {
+		/* Do not stop the queue here.
+		 * It will lead to ir-recoverable state.
+		 */
+		ret = NETDEV_TX_BUSY;
+		goto exit;
+	}
+
+	if (msm_smux_is_ch_full(p->ch_id) || (ret == -EAGAIN)) {
+		/*
+		 * EAGAIN means we attempted to overflow the high watermark
+		 * Clearly the queue is not stopped like it should be, so
+		 * stop it and return BUSY to the TCP/IP framework.  It will
+		 * retry this packet with the queue is restarted which happens
+		 * low watermark is called.
+		 */
+		netif_stop_queue(dev);
+		ret = NETDEV_TX_BUSY;
+		goto exit;
+	}
+exit:
+	return ret;
+}
+
+static struct net_device_stats *rmnet_get_stats(struct net_device *dev)
+{
+	struct rmnet_private *p = netdev_priv(dev);
+	return &p->stats;
+}
+
+static void rmnet_tx_timeout(struct net_device *dev)
+{
+	pr_warning("[%s] rmnet_tx_timeout()\n", dev->name);
+}
+
+static const struct net_device_ops rmnet_ops_ether = {
+	.ndo_open = rmnet_open,
+	.ndo_stop = rmnet_stop,
+	.ndo_start_xmit = rmnet_xmit,
+	.ndo_get_stats = rmnet_get_stats,
+	.ndo_tx_timeout = rmnet_tx_timeout,
+	.ndo_do_ioctl = rmnet_ioctl,
+	.ndo_change_mtu = rmnet_change_mtu,
+	.ndo_set_mac_address = eth_mac_addr,
+	.ndo_validate_addr = eth_validate_addr,
+};
+
+static const struct net_device_ops rmnet_ops_ip = {
+	.ndo_open = rmnet_open,
+	.ndo_stop = rmnet_stop,
+	.ndo_start_xmit = rmnet_xmit,
+	.ndo_get_stats = rmnet_get_stats,
+	.ndo_tx_timeout = rmnet_tx_timeout,
+	.ndo_do_ioctl = rmnet_ioctl,
+	.ndo_change_mtu = rmnet_change_mtu,
+	.ndo_set_mac_address = 0,
+	.ndo_validate_addr = 0,
+};
+
+static int rmnet_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	struct rmnet_private *p = netdev_priv(dev);
+	u32 old_opmode = p->operation_mode;
+	unsigned long flags;
+	int prev_mtu = dev->mtu;
+	int rc = 0;
+
+	/* Process IOCTL command */
+	switch (cmd) {
+	case RMNET_IOCTL_SET_LLP_ETHERNET:	/* Set Ethernet protocol   */
+		/* Perform Ethernet config only if in IP mode currently*/
+		if (p->operation_mode & RMNET_MODE_LLP_IP) {
+			ether_setup(dev);
+			random_ether_addr(dev->dev_addr);
+			dev->mtu = prev_mtu;
+
+			dev->netdev_ops = &rmnet_ops_ether;
+			spin_lock_irqsave(&p->lock, flags);
+			p->operation_mode &= ~RMNET_MODE_LLP_IP;
+			p->operation_mode |= RMNET_MODE_LLP_ETH;
+			spin_unlock_irqrestore(&p->lock, flags);
+			DBG0("[%s] rmnet_ioctl(): "
+				 "set Ethernet protocol mode\n",
+				 dev->name);
+		}
+		break;
+
+	case RMNET_IOCTL_SET_LLP_IP:		/* Set RAWIP protocol      */
+		/* Perform IP config only if in Ethernet mode currently*/
+		if (p->operation_mode & RMNET_MODE_LLP_ETH) {
+
+			/* Undo config done in ether_setup() */
+			dev->header_ops         = 0;  /* No header */
+			dev->type               = ARPHRD_RAWIP;
+			dev->hard_header_len    = 0;
+			dev->mtu                = prev_mtu;
+			dev->addr_len           = 0;
+			dev->flags              &= ~(IFF_BROADCAST |
+						     IFF_MULTICAST);
+
+			dev->needed_headroom = HEADROOM_FOR_SMUX +
+							HEADROOM_FOR_QOS;
+			dev->needed_tailroom = TAILROOM;
+			dev->netdev_ops = &rmnet_ops_ip;
+			spin_lock_irqsave(&p->lock, flags);
+			p->operation_mode &= ~RMNET_MODE_LLP_ETH;
+			p->operation_mode |= RMNET_MODE_LLP_IP;
+			spin_unlock_irqrestore(&p->lock, flags);
+			DBG0("[%s] rmnet_ioctl(): "
+				 "set IP protocol mode\n",
+				 dev->name);
+		}
+		break;
+
+	case RMNET_IOCTL_GET_LLP:	/* Get link protocol state */
+		ifr->ifr_ifru.ifru_data =
+		(void *)(p->operation_mode &
+				 (RMNET_MODE_LLP_ETH|RMNET_MODE_LLP_IP));
+		break;
+
+	case RMNET_IOCTL_SET_QOS_ENABLE:	/* Set QoS header enabled */
+		spin_lock_irqsave(&p->lock, flags);
+		p->operation_mode |= RMNET_MODE_QOS;
+		spin_unlock_irqrestore(&p->lock, flags);
+		DBG0("[%s] rmnet_ioctl(): set QMI QOS header enable\n",
+			 dev->name);
+		break;
+
+	case RMNET_IOCTL_SET_QOS_DISABLE:	/* Set QoS header disabled */
+		spin_lock_irqsave(&p->lock, flags);
+		p->operation_mode &= ~RMNET_MODE_QOS;
+		spin_unlock_irqrestore(&p->lock, flags);
+		DBG0("[%s] rmnet_ioctl(): set QMI QOS header disable\n",
+			 dev->name);
+		break;
+
+	case RMNET_IOCTL_GET_QOS:	/* Get QoS header state */
+		ifr->ifr_ifru.ifru_data =
+		(void *)(p->operation_mode & RMNET_MODE_QOS);
+		break;
+
+	case RMNET_IOCTL_GET_OPMODE:	/* Get operation mode */
+		ifr->ifr_ifru.ifru_data = (void *)p->operation_mode;
+		break;
+
+	case RMNET_IOCTL_OPEN:		/* Open transport port */
+		rc = __rmnet_open(dev);
+		DBG0("[%s] rmnet_ioctl(): open transport port\n",
+			 dev->name);
+		break;
+
+	case RMNET_IOCTL_CLOSE:		/* Close transport port */
+		DBG0("[%s] rmnet_ioctl(): close transport port\n",
+			 dev->name);
+		break;
+
+	default:
+		pr_err("[%s] error: rmnet_ioct called for unsupported cmd[%d]",
+			   dev->name, cmd);
+		return -EINVAL;
+	}
+
+	DBG2("[%s] %s: cmd=0x%x opmode old=0x%08x new=0x%08x\n",
+		 dev->name, __func__, cmd, old_opmode, p->operation_mode);
+	return rc;
+}
+
+static void __init rmnet_setup(struct net_device *dev)
+{
+	/* Using Ethernet mode by default */
+	dev->netdev_ops = &rmnet_ops_ether;
+	ether_setup(dev);
+
+	/* set this after calling ether_setup */
+	dev->mtu = RMNET_DATA_LEN;
+	dev->needed_headroom = HEADROOM_FOR_SMUX + HEADROOM_FOR_QOS ;
+	dev->needed_tailroom = TAILROOM;
+	random_ether_addr(dev->dev_addr);
+
+	dev->watchdog_timeo = 1000;	/* 10 seconds? */
+}
+
+
+static int smux_rmnet_probe(struct platform_device *pdev)
+{
+	int i;
+	int r;
+	struct rmnet_private *p;
+
+	for (i = 0; i < RMNET_SMUX_DEVICE_COUNT; i++) {
+		p = netdev_priv(netdevs[i]);
+
+		if ((p != NULL) && (p->device_state == DEVICE_INACTIVE)) {
+			r =  msm_smux_open(p->ch_id,
+					   netdevs[i],
+					   rmnet_smux_notify,
+					   get_rx_buffers);
+
+			if (r < 0) {
+				DBG0("%s: ch=%d open failed with rc %d\n",
+				      __func__, p->ch_id, r);
+			}
+		}
+	}
+	return 0;
+}
+
+static int smux_rmnet_remove(struct platform_device *pdev)
+{
+	int i;
+	int r;
+	struct rmnet_private *p;
+
+	for (i = 0; i < RMNET_SMUX_DEVICE_COUNT; i++) {
+		p = netdev_priv(netdevs[i]);
+
+		if ((p != NULL) && (p->device_state == DEVICE_ACTIVE)) {
+			r =  msm_smux_close(p->ch_id);
+
+			if (r < 0) {
+				DBG0("%s: ch=%d close failed with rc %d\n",
+				     __func__, p->ch_id, r);
+				continue;
+			}
+			netif_carrier_off(netdevs[i]);
+			netif_stop_queue(netdevs[i]);
+		}
+	}
+	return 0;
+}
+
+
+static struct platform_driver smux_rmnet_driver = {
+	.probe  = smux_rmnet_probe,
+	.remove = smux_rmnet_remove,
+	.driver = {
+		.name = "SMUX_RMNET",
+		.owner = THIS_MODULE,
+	},
+};
+
+
+static int __init rmnet_init(void)
+{
+	int ret;
+	struct device *d;
+	struct net_device *dev;
+	struct rmnet_private *p;
+	unsigned n;
+
+#ifdef CONFIG_MSM_RMNET_DEBUG
+	timeout_us = 0;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	timeout_suspend_us = 0;
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+#endif /* CONFIG_MSM_RMNET_DEBUG */
+
+	for (n = 0; n < RMNET_SMUX_DEVICE_COUNT; n++) {
+		dev = alloc_netdev(sizeof(struct rmnet_private),
+				   "rmnet_smux%d", rmnet_setup);
+
+		if (!dev) {
+			pr_err("%s: no memory for netdev %d\n", __func__, n);
+			return -ENOMEM;
+		}
+
+		netdevs[n] = dev;
+		d = &(dev->dev);
+		p = netdev_priv(dev);
+		/* Initial config uses Ethernet */
+		p->operation_mode = RMNET_MODE_LLP_ETH;
+		p->ch_id = n;
+		p->in_reset = 0;
+		spin_lock_init(&p->lock);
+#ifdef CONFIG_MSM_RMNET_DEBUG
+		p->timeout_us = timeout_us;
+		p->wakeups_xmit = p->wakeups_rcv = 0;
+#endif
+
+		ret = register_netdev(dev);
+		if (ret) {
+			pr_err("%s: unable to register netdev"
+				   " %d rc=%d\n", __func__, n, ret);
+			free_netdev(dev);
+			return ret;
+		}
+
+#ifdef CONFIG_MSM_RMNET_DEBUG
+		if (device_create_file(d, &dev_attr_timeout))
+			continue;
+		if (device_create_file(d, &dev_attr_wakeups_xmit))
+			continue;
+		if (device_create_file(d, &dev_attr_wakeups_rcv))
+			continue;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+		if (device_create_file(d, &dev_attr_timeout_suspend))
+			continue;
+
+		/* Only care about rmnet0 for suspend/resume tiemout hooks. */
+		if (n == 0)
+			rmnet0 = d;
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+#endif /* CONFIG_MSM_RMNET_DEBUG */
+
+	}
+
+	ret = platform_driver_register(&smux_rmnet_driver);
+	if (ret) {
+		pr_err("%s: registration failed n=%d rc=%d\n",
+			   __func__, n, ret);
+		return ret;
+	}
+	return 0;
+}
+
+module_init(rmnet_init);
+MODULE_DESCRIPTION("MSM RMNET SMUX TRANSPORT");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/ethernet/msm/qfec.c b/drivers/net/ethernet/msm/qfec.c
new file mode 100644
index 0000000..112e16a
--- /dev/null
+++ b/drivers/net/ethernet/msm/qfec.c
@@ -0,0 +1,2792 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+
+#include <linux/platform_device.h>
+
+#include <linux/types.h>        /* size_t */
+#include <linux/interrupt.h>    /* mark_bh */
+
+#include <linux/netdevice.h>   /* struct device, and other headers */
+#include <linux/etherdevice.h> /* eth_type_trans */
+#include <linux/skbuff.h>
+
+#include <linux/proc_fs.h>
+#include <linux/timer.h>
+#include <linux/mii.h>
+
+#include <linux/ethtool.h>
+#include <linux/net_tstamp.h>
+#include <linux/phy.h>
+#include <linux/inet.h>
+
+#include "qfec.h"
+
+#define QFEC_NAME       "qfec"
+#define QFEC_DRV_VER    "Nov 29 2011"
+
+#define ETH_BUF_SIZE    0x600
+#define MAX_N_BD        50
+#define MAC_ADDR_SIZE	6
+
+#define RX_TX_BD_RATIO  8
+#define TX_BD_NUM       256
+#define RX_BD_NUM       256
+#define TX_BD_TI_RATIO  4
+#define MAX_MDIO_REG    32
+
+#define H_DPLX     0
+#define F_DPLX     1
+/*
+ * logging macros
+ */
+#define QFEC_LOG_PR     1
+#define QFEC_LOG_DBG    2
+#define QFEC_LOG_DBG2   4
+#define QFEC_LOG_MDIO_W 8
+#define QFEC_LOG_MDIO_R 16
+#define QFEC_MII_EXP_MASK (EXPANSION_LCWP | EXPANSION_ENABLENPAGE \
+							| EXPANSION_NPCAPABLE)
+
+static int qfec_debug = QFEC_LOG_PR;
+
+#ifdef QFEC_DEBUG
+# define QFEC_LOG(flag, ...)                    \
+	do {                                    \
+		if (flag & qfec_debug)          \
+			pr_info(__VA_ARGS__);  \
+	} while (0)
+#else
+# define QFEC_LOG(flag, ...)
+#endif
+
+#define QFEC_LOG_ERR(...) pr_err(__VA_ARGS__)
+
+/*
+ * driver buffer-descriptor
+ *   contains the 4 word HW descriptor plus an additional 4-words.
+ *   (See the DSL bits in the BUS-Mode register).
+ */
+#define BD_FLAG_LAST_BD     1
+
+struct buf_desc {
+	struct qfec_buf_desc   *p_desc;
+	struct sk_buff         *skb;
+	void                   *buf_virt_addr;
+	void                   *buf_phys_addr;
+	uint32_t                last_bd_flag;
+};
+
+/*
+ *inline functions accessing non-struct qfec_buf_desc elements
+ */
+
+/* skb */
+static inline struct sk_buff *qfec_bd_skbuf_get(struct buf_desc *p_bd)
+{
+	return p_bd->skb;
+};
+
+static inline void qfec_bd_skbuf_set(struct buf_desc *p_bd, struct sk_buff *p)
+{
+	p_bd->skb   = p;
+};
+
+/* virtual addr  */
+static inline void qfec_bd_virt_set(struct buf_desc *p_bd, void *addr)
+{
+	p_bd->buf_virt_addr = addr;
+};
+
+static inline void *qfec_bd_virt_get(struct buf_desc *p_bd)
+{
+	return p_bd->buf_virt_addr;
+};
+
+/* physical addr  */
+static inline void qfec_bd_phys_set(struct buf_desc *p_bd, void *addr)
+{
+	p_bd->buf_phys_addr = addr;
+};
+
+static inline void *qfec_bd_phys_get(struct buf_desc *p_bd)
+{
+	return p_bd->buf_phys_addr;
+};
+
+/* last_bd_flag */
+static inline uint32_t qfec_bd_last_bd(struct buf_desc *p_bd)
+{
+	return (p_bd->last_bd_flag != 0);
+};
+
+static inline void qfec_bd_last_bd_set(struct buf_desc *p_bd)
+{
+	p_bd->last_bd_flag = BD_FLAG_LAST_BD;
+};
+
+/*
+ *inline functions accessing struct qfec_buf_desc elements
+ */
+
+/* ownership bit */
+static inline uint32_t qfec_bd_own(struct buf_desc *p_bd)
+{
+	return p_bd->p_desc->status & BUF_OWN;
+};
+
+static inline void qfec_bd_own_set(struct buf_desc *p_bd)
+{
+	p_bd->p_desc->status |= BUF_OWN ;
+};
+
+static inline void qfec_bd_own_clr(struct buf_desc *p_bd)
+{
+	p_bd->p_desc->status &= ~(BUF_OWN);
+};
+
+static inline uint32_t qfec_bd_status_get(struct buf_desc *p_bd)
+{
+	return p_bd->p_desc->status;
+};
+
+static inline void qfec_bd_status_set(struct buf_desc *p_bd, uint32_t status)
+{
+	p_bd->p_desc->status = status;
+};
+
+static inline uint32_t qfec_bd_status_len(struct buf_desc *p_bd)
+{
+	return BUF_RX_FL_GET((*p_bd->p_desc));
+};
+
+/* control register */
+static inline void qfec_bd_ctl_reset(struct buf_desc *p_bd)
+{
+	p_bd->p_desc->ctl  = 0;
+};
+
+static inline uint32_t qfec_bd_ctl_get(struct buf_desc *p_bd)
+{
+	return p_bd->p_desc->ctl;
+};
+
+static inline void qfec_bd_ctl_set(struct buf_desc *p_bd, uint32_t val)
+{
+	p_bd->p_desc->ctl |= val;
+};
+
+static inline void qfec_bd_ctl_wr(struct buf_desc *p_bd, uint32_t val)
+{
+	p_bd->p_desc->ctl = val;
+};
+
+/* pbuf register  */
+static inline void *qfec_bd_pbuf_get(struct buf_desc *p_bd)
+{
+	return p_bd->p_desc->p_buf;
+}
+
+static inline void qfec_bd_pbuf_set(struct buf_desc *p_bd, void *p)
+{
+	p_bd->p_desc->p_buf = p;
+}
+
+/* next register */
+static inline void *qfec_bd_next_get(struct buf_desc *p_bd)
+{
+	return p_bd->p_desc->next;
+};
+
+/*
+ * initialize an RX BD w/ a new buf
+ */
+static int qfec_rbd_init(struct net_device *dev, struct buf_desc *p_bd)
+{
+	struct sk_buff     *skb;
+	void               *p;
+	void               *v;
+
+	/* allocate and record ptrs for sk buff */
+	skb   = dev_alloc_skb(ETH_BUF_SIZE);
+	if (!skb)
+		goto err;
+
+	qfec_bd_skbuf_set(p_bd, skb);
+
+	v = skb_put(skb, ETH_BUF_SIZE);
+	qfec_bd_virt_set(p_bd, v);
+
+	p = (void *) dma_map_single(&dev->dev,
+		(void *)skb->data, ETH_BUF_SIZE, DMA_FROM_DEVICE);
+	qfec_bd_pbuf_set(p_bd, p);
+	qfec_bd_phys_set(p_bd, p);
+
+	/* populate control register */
+	/* mark the last BD and set end-of-ring bit */
+	qfec_bd_ctl_wr(p_bd, ETH_BUF_SIZE |
+		(qfec_bd_last_bd(p_bd) ? BUF_RX_RER : 0));
+
+	qfec_bd_status_set(p_bd, BUF_OWN);
+
+	if (!(qfec_debug & QFEC_LOG_DBG2))
+		return 0;
+
+	/* debug messages */
+	QFEC_LOG(QFEC_LOG_DBG2, "%s: %p bd\n", __func__, p_bd);
+
+	QFEC_LOG(QFEC_LOG_DBG2, "%s: %p skb\n", __func__, skb);
+
+	QFEC_LOG(QFEC_LOG_DBG2,
+		"%s: %p p_bd, %p data, %p skb_put, %p virt, %p p_buf, %p p\n",
+		__func__, (void *)p_bd,
+		(void *)skb->data, v, /*(void *)skb_put(skb, ETH_BUF_SIZE), */
+		(void *)qfec_bd_virt_get(p_bd), (void *)qfec_bd_pbuf_get(p_bd),
+		(void *)p);
+
+	return 0;
+
+err:
+	return -ENOMEM;
+};
+
+/*
+ * ring structure used to maintain indices of buffer-descriptor (BD) usage
+ *
+ *   The RX BDs are normally all pre-allocated with buffers available to be
+ *   DMA'd into with received frames.  The head indicates the first BD/buffer
+ *   containing a received frame, and the tail indicates the oldest BD/buffer
+ *   that needs to be restored for use.   Head and tail are both initialized
+ *   to zero, and n_free is initialized to zero, since all BD are initialized.
+ *
+ *   The TX BDs are normally available for use, only being initialized as
+ *   TX frames are requested for transmission.   The head indicates the
+ *   first available BD, and the tail indicate the oldest BD that has
+ *   not been acknowledged as transmitted.    Head and tail are both initialized
+ *   to zero, and n_free is initialized to len, since all are available for use.
+ */
+struct ring {
+	int     head;
+	int     tail;
+	int     n_free;
+	int     len;
+};
+
+/* accessory in line functions for struct ring */
+static inline void qfec_ring_init(struct ring *p_ring, int size, int free)
+{
+	p_ring->head  = p_ring->tail = 0;
+	p_ring->len   = size;
+	p_ring->n_free = free;
+}
+
+static inline int qfec_ring_full(struct ring *p_ring)
+{
+	return (p_ring->n_free == 0);
+};
+
+static inline int qfec_ring_empty(struct ring *p_ring)
+{
+	return (p_ring->n_free == p_ring->len);
+}
+
+static inline void qfec_ring_head_adv(struct ring *p_ring)
+{
+	if (++p_ring->head == p_ring->len)
+		p_ring->head = 0;
+	p_ring->n_free--;
+};
+
+static inline void qfec_ring_tail_adv(struct ring *p_ring)
+{
+	if (++p_ring->tail == p_ring->len)
+		p_ring->tail = 0;
+	p_ring->n_free++;
+};
+
+static inline int qfec_ring_head(struct ring *p_ring)
+{
+
+	return p_ring->head;
+};
+
+static inline int qfec_ring_tail(struct ring *p_ring)
+{
+	return p_ring->tail;
+};
+
+static inline int qfec_ring_room(struct ring *p_ring)
+{
+	return p_ring->n_free;
+};
+
+/*
+ * counters track normal and abnormal driver events and activity
+ */
+enum cntr {
+	isr                  =  0,
+	fatal_bus,
+
+	early_tx,
+	tx_no_resource,
+	tx_proc_stopped,
+	tx_jabber_tmout,
+
+	xmit,
+	tx_int,
+	tx_isr,
+	tx_owned,
+	tx_underflow,
+
+	tx_replenish,
+	tx_skb_null,
+	tx_timeout,
+	tx_too_large,
+
+	gmac_isr,
+
+	/* half */
+	norm_int,
+	abnorm_int,
+
+	early_rx,
+	rx_buf_unavail,
+	rx_proc_stopped,
+	rx_watchdog,
+
+	netif_rx_cntr,
+	rx_int,
+	rx_isr,
+	rx_owned,
+	rx_overflow,
+
+	rx_dropped,
+	rx_skb_null,
+	queue_start,
+	queue_stop,
+
+	rx_paddr_nok,
+	ts_ioctl,
+	ts_tx_en,
+	ts_tx_rtn,
+
+	ts_rec,
+	cntr_last,
+};
+
+static char *cntr_name[]  = {
+	"isr",
+	"fatal_bus",
+
+	"early_tx",
+	"tx_no_resource",
+	"tx_proc_stopped",
+	"tx_jabber_tmout",
+
+	"xmit",
+	"tx_int",
+	"tx_isr",
+	"tx_owned",
+	"tx_underflow",
+
+	"tx_replenish",
+	"tx_skb_null",
+	"tx_timeout",
+	"tx_too_large",
+
+	"gmac_isr",
+
+	/* half */
+	"norm_int",
+	"abnorm_int",
+
+	"early_rx",
+	"rx_buf_unavail",
+	"rx_proc_stopped",
+	"rx_watchdog",
+
+	"netif_rx",
+	"rx_int",
+	"rx_isr",
+	"rx_owned",
+	"rx_overflow",
+
+	"rx_dropped",
+	"rx_skb_null",
+	"queue_start",
+	"queue_stop",
+
+	"rx_paddr_nok",
+	"ts_ioctl",
+	"ts_tx_en",
+	"ts_tx_rtn",
+
+	"ts_rec",
+	""
+};
+
+/*
+ * private data
+ */
+
+static struct net_device  *qfec_dev;
+
+enum qfec_state {
+	timestamping  = 0x04,
+};
+
+struct qfec_priv {
+	struct net_device      *net_dev;
+	struct net_device_stats stats;            /* req statistics */
+
+	struct device           dev;
+
+	spinlock_t              xmit_lock;
+	spinlock_t              mdio_lock;
+
+	unsigned int            state;            /* driver state */
+
+	unsigned int            bd_size;          /* buf-desc alloc size */
+	struct qfec_buf_desc   *bd_base;          /* * qfec-buf-desc */
+	dma_addr_t              tbd_dma;          /* dma/phy-addr buf-desc */
+	dma_addr_t              rbd_dma;          /* dma/phy-addr buf-desc */
+
+	struct resource        *mac_res;
+	void                   *mac_base;         /* mac (virt) base address */
+
+	struct resource        *clk_res;
+	void                   *clk_base;         /* clk (virt) base address */
+
+	struct resource        *fuse_res;
+	void                   *fuse_base;        /* mac addr fuses */
+
+	unsigned int            n_tbd;            /* # of TX buf-desc */
+	struct ring             ring_tbd;         /* TX ring */
+	struct buf_desc        *p_tbd;
+	unsigned int            tx_ic_mod;        /* (%) val for setting IC */
+
+	unsigned int            n_rbd;            /* # of RX buf-desc */
+	struct ring             ring_rbd;         /* RX ring */
+	struct buf_desc        *p_rbd;
+
+	struct buf_desc        *p_latest_rbd;
+	struct buf_desc        *p_ending_rbd;
+
+	unsigned long           cntr[cntr_last];  /* activity counters */
+
+	struct mii_if_info      mii;              /* used by mii lib */
+
+	int                     mdio_clk;         /* phy mdio clock rate */
+	int                     phy_id;           /* default PHY addr (0) */
+	struct timer_list       phy_tmr;          /* monitor PHY state */
+};
+
+/*
+ * cntrs display
+ */
+
+static int qfec_cntrs_show(struct device *dev, struct device_attribute *attr,
+			      char *buf)
+{
+	struct qfec_priv        *priv = netdev_priv(to_net_dev(dev));
+	int                      h    = (cntr_last + 1) / 2;
+	int                      l;
+	int                      n;
+	int                      count = PAGE_SIZE;
+
+	QFEC_LOG(QFEC_LOG_DBG2, "%s:\n", __func__);
+
+	l = snprintf(&buf[0], count, "%s:\n", __func__);
+	for (n = 0; n < h; n++)  {
+		l += snprintf(&buf[l], count - l,
+			"      %12lu  %-16s %12lu  %s\n",
+			priv->cntr[n],   cntr_name[n],
+			priv->cntr[n+h], cntr_name[n+h]);
+	}
+
+	return l;
+}
+
+# define CNTR_INC(priv, name)  (priv->cntr[name]++)
+
+/*
+ * functions that manage state
+ */
+static inline void qfec_queue_start(struct net_device *dev)
+{
+	struct qfec_priv  *priv = netdev_priv(dev);
+
+	if (netif_queue_stopped(dev)) {
+		netif_wake_queue(dev);
+		CNTR_INC(priv, queue_start);
+	}
+};
+
+static inline void qfec_queue_stop(struct net_device *dev)
+{
+	struct qfec_priv  *priv = netdev_priv(dev);
+
+	netif_stop_queue(dev);
+	CNTR_INC(priv, queue_stop);
+};
+
+/*
+ * functions to access and initialize the MAC registers
+ */
+static inline uint32_t qfec_reg_read(struct qfec_priv *priv, uint32_t reg)
+{
+	return ioread32((void *) (priv->mac_base + reg));
+}
+
+static void qfec_reg_write(struct qfec_priv *priv, uint32_t reg, uint32_t val)
+{
+	uint32_t    addr = (uint32_t)priv->mac_base + reg;
+
+	QFEC_LOG(QFEC_LOG_DBG2, "%s: %08x <- %08x\n", __func__, addr, val);
+	iowrite32(val, (void *)addr);
+}
+
+/*
+ * speed/duplex/pause  settings
+ */
+static int qfec_config_show(struct device *dev, struct device_attribute *attr,
+			      char *buf)
+{
+	struct qfec_priv        *priv = netdev_priv(to_net_dev(dev));
+	int                      cfg  = qfec_reg_read(priv, MAC_CONFIG_REG);
+	int                      flow = qfec_reg_read(priv, FLOW_CONTROL_REG);
+	int                      l    = 0;
+	int                      count = PAGE_SIZE;
+
+	QFEC_LOG(QFEC_LOG_DBG2, "%s:\n", __func__);
+
+	l += snprintf(&buf[l], count, "%s:", __func__);
+
+	l += snprintf(&buf[l], count - l, "  [0x%08x] %4dM %s %s", cfg,
+		(cfg & MAC_CONFIG_REG_PS)
+			? ((cfg & MAC_CONFIG_REG_FES) ? 100 : 10) : 1000,
+		cfg & MAC_CONFIG_REG_DM ? "FD" : "HD",
+		cfg & MAC_CONFIG_REG_IPC ? "IPC" : "NoIPC");
+
+	flow &= FLOW_CONTROL_RFE | FLOW_CONTROL_TFE;
+	l += snprintf(&buf[l], count - l, "  [0x%08x] %s", flow,
+		(flow == (FLOW_CONTROL_RFE | FLOW_CONTROL_TFE)) ? "PAUSE"
+			: ((flow == FLOW_CONTROL_RFE) ? "RX-PAUSE"
+			: ((flow == FLOW_CONTROL_TFE) ? "TX-PAUSE" : "")));
+
+	l += snprintf(&buf[l], count - l, " %s", QFEC_DRV_VER);
+	l += snprintf(&buf[l], count - l, "\n");
+	return l;
+}
+
+
+/*
+ * table and functions to initialize controller registers
+ */
+
+struct reg_entry {
+	unsigned int  rdonly;
+	unsigned int  addr;
+	char         *label;
+	unsigned int  val;
+};
+
+static struct reg_entry  qfec_reg_tbl[] = {
+	{ 0, BUS_MODE_REG,           "BUS_MODE_REG",     BUS_MODE_REG_DEFAULT },
+	{ 0, AXI_BUS_MODE_REG,       "AXI_BUS_MODE_REG", AXI_BUS_MODE_DEFAULT },
+	{ 0, AXI_STATUS_REG,         "AXI_STATUS_REG",     0 },
+
+	{ 0, MAC_ADR_0_HIGH_REG,     "MAC_ADR_0_HIGH_REG", 0x00000302 },
+	{ 0, MAC_ADR_0_LOW_REG,      "MAC_ADR_0_LOW_REG",  0x01350702 },
+
+	{ 1, RX_DES_LST_ADR_REG,     "RX_DES_LST_ADR_REG", 0 },
+	{ 1, TX_DES_LST_ADR_REG,     "TX_DES_LST_ADR_REG", 0 },
+	{ 1, STATUS_REG,             "STATUS_REG",         0 },
+	{ 1, DEBUG_REG,              "DEBUG_REG",          0 },
+
+	{ 0, INTRP_EN_REG,           "INTRP_EN_REG",       QFEC_INTRP_SETUP},
+
+	{ 1, CUR_HOST_TX_DES_REG,    "CUR_HOST_TX_DES_REG",    0 },
+	{ 1, CUR_HOST_RX_DES_REG,    "CUR_HOST_RX_DES_REG",    0 },
+	{ 1, CUR_HOST_TX_BU_ADR_REG, "CUR_HOST_TX_BU_ADR_REG", 0 },
+	{ 1, CUR_HOST_RX_BU_ADR_REG, "CUR_HOST_RX_BU_ADR_REG", 0 },
+
+	{ 1, MAC_FR_FILTER_REG,      "MAC_FR_FILTER_REG",      0 },
+
+	{ 0, MAC_CONFIG_REG,         "MAC_CONFIG_REG",    MAC_CONFIG_REG_SPD_1G
+							| MAC_CONFIG_REG_DM
+							| MAC_CONFIG_REG_TE
+							| MAC_CONFIG_REG_RE
+							| MAC_CONFIG_REG_IPC },
+
+	{ 1, INTRP_STATUS_REG,       "INTRP_STATUS_REG",   0 },
+	{ 1, INTRP_MASK_REG,         "INTRP_MASK_REG",     0 },
+
+	{ 0, OPER_MODE_REG,          "OPER_MODE_REG",  OPER_MODE_REG_DEFAULT },
+
+	{ 1, GMII_ADR_REG,           "GMII_ADR_REG",           0 },
+	{ 1, GMII_DATA_REG,          "GMII_DATA_REG",          0 },
+
+	{ 0, MMC_INTR_MASK_RX_REG,   "MMC_INTR_MASK_RX_REG",   0xFFFFFFFF },
+	{ 0, MMC_INTR_MASK_TX_REG,   "MMC_INTR_MASK_TX_REG",   0xFFFFFFFF },
+
+	{ 1, TS_HIGH_REG,            "TS_HIGH_REG",            0 },
+	{ 1, TS_LOW_REG,             "TS_LOW_REG",             0 },
+
+	{ 1, TS_HI_UPDT_REG,         "TS_HI_UPDATE_REG",       0 },
+	{ 1, TS_LO_UPDT_REG,         "TS_LO_UPDATE_REG",       0 },
+	{ 0, TS_SUB_SEC_INCR_REG,    "TS_SUB_SEC_INCR_REG",    1 },
+	{ 0, TS_CTL_REG,             "TS_CTL_REG",        TS_CTL_TSENALL
+							| TS_CTL_TSCTRLSSR
+							| TS_CTL_TSINIT
+							| TS_CTL_TSENA },
+};
+
+static void qfec_reg_init(struct qfec_priv *priv)
+{
+	struct reg_entry *p = qfec_reg_tbl;
+	int         n = ARRAY_SIZE(qfec_reg_tbl);
+
+	QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
+
+	for  (; n--; p++) {
+		if (!p->rdonly)
+			qfec_reg_write(priv, p->addr, p->val);
+	}
+}
+
+/*
+ * display registers thru sysfs
+ */
+static int qfec_reg_show(struct device *dev, struct device_attribute *attr,
+			      char *buf)
+{
+	struct qfec_priv   *priv = netdev_priv(to_net_dev(dev));
+	struct reg_entry   *p = qfec_reg_tbl;
+	int                 n = ARRAY_SIZE(qfec_reg_tbl);
+	int                 l = 0;
+	int                 count = PAGE_SIZE;
+
+	QFEC_LOG(QFEC_LOG_DBG2, "%s:\n", __func__);
+
+	for (; n--; p++) {
+		l += snprintf(&buf[l], count - l, "    %8p   %04x %08x  %s\n",
+			(void *)priv->mac_base + p->addr, p->addr,
+			qfec_reg_read(priv, p->addr), p->label);
+	}
+
+	return  l;
+}
+
+/*
+ * set the MAC-0 address
+ */
+static void qfec_set_adr_regs(struct qfec_priv *priv, uint8_t *addr)
+{
+	uint32_t        h = 0;
+	uint32_t        l = 0;
+
+	h = h << 8 | addr[5];
+	h = h << 8 | addr[4];
+
+	l = l << 8 | addr[3];
+	l = l << 8 | addr[2];
+	l = l << 8 | addr[1];
+	l = l << 8 | addr[0];
+
+	qfec_reg_write(priv, MAC_ADR_0_HIGH_REG, h);
+	qfec_reg_write(priv, MAC_ADR_0_LOW_REG,  l);
+
+	QFEC_LOG(QFEC_LOG_DBG, "%s: %08x %08x\n", __func__, h, l);
+}
+
+/*
+ * set up the RX filter
+ */
+static void qfec_set_rx_mode(struct net_device *dev)
+{
+	struct qfec_priv *priv = netdev_priv(dev);
+	uint32_t filter_conf;
+	int index;
+
+	/* Clear address filter entries */
+	for (index = 1; index < MAC_ADR_MAX; ++index) {
+		qfec_reg_write(priv, MAC_ADR_HIGH_REG_N(index), 0);
+		qfec_reg_write(priv, MAC_ADR_LOW_REG_N(index), 0);
+	}
+
+	if (dev->flags & IFF_PROMISC) {
+		/* Receive all frames */
+		filter_conf = MAC_FR_FILTER_RA;
+	} else if ((dev->flags & IFF_MULTICAST) == 0) {
+		/* Unicast filtering only */
+		filter_conf = MAC_FR_FILTER_HPF;
+	} else if ((netdev_mc_count(dev) > MAC_ADR_MAX - 1) ||
+		   (dev->flags & IFF_ALLMULTI)) {
+		/* Unicast filtering is enabled, Pass all multicast frames */
+		filter_conf = MAC_FR_FILTER_HPF | MAC_FR_FILTER_PM;
+	} else {
+		struct netdev_hw_addr *ha;
+
+		/* Both unicast and multicast filtering are enabled */
+		filter_conf = MAC_FR_FILTER_HPF;
+
+		index = 1;
+
+		netdev_for_each_mc_addr(ha, dev) {
+			uint32_t high, low;
+
+			high = (1 << 31) | (ha->addr[5] << 8) | (ha->addr[4]);
+			low = (ha->addr[3] << 24) | (ha->addr[2] << 16) |
+				(ha->addr[1] << 8) | (ha->addr[0]);
+
+			qfec_reg_write(priv, MAC_ADR_HIGH_REG_N(index), high);
+			qfec_reg_write(priv, MAC_ADR_LOW_REG_N(index), low);
+
+			index++;
+		}
+	}
+
+	qfec_reg_write(priv, MAC_FR_FILTER_REG, filter_conf);
+}
+
+/*
+ * reset the controller
+ */
+
+#define QFEC_RESET_TIMEOUT   10000
+	/* reset should always clear but did not w/o test/delay
+	 * in RgMii mode.  there is no spec'd max timeout
+	 */
+
+static int qfec_hw_reset(struct qfec_priv *priv)
+{
+	int             timeout = QFEC_RESET_TIMEOUT;
+
+	QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
+
+	qfec_reg_write(priv, BUS_MODE_REG, BUS_MODE_SWR);
+
+	while (qfec_reg_read(priv, BUS_MODE_REG) & BUS_MODE_SWR) {
+		if (timeout-- == 0) {
+			QFEC_LOG_ERR("%s: timeout\n", __func__);
+			return -ETIME;
+		}
+
+		/* there were problems resetting the controller
+		 * in RGMII mode when there wasn't sufficient
+		 * delay between register reads
+		 */
+		usleep_range(100, 200);
+	}
+
+	return 0;
+}
+
+/*
+ * initialize controller
+ */
+static int qfec_hw_init(struct qfec_priv *priv)
+{
+	int  res = 0;
+
+	QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
+
+	res = qfec_hw_reset(priv);
+	if (res)
+		return res;
+
+	qfec_reg_init(priv);
+
+	/* config buf-desc locations */
+	qfec_reg_write(priv, TX_DES_LST_ADR_REG, priv->tbd_dma);
+	qfec_reg_write(priv, RX_DES_LST_ADR_REG, priv->rbd_dma);
+
+	/* clear interrupts */
+	qfec_reg_write(priv, STATUS_REG, INTRP_EN_REG_NIE | INTRP_EN_REG_RIE
+		| INTRP_EN_REG_TIE | INTRP_EN_REG_TUE | INTRP_EN_REG_ETE);
+
+	if (priv->mii.supports_gmii) {
+		/* Clear RGMII */
+		qfec_reg_read(priv, SG_RG_SMII_STATUS_REG);
+		/* Disable RGMII int */
+		qfec_reg_write(priv, INTRP_MASK_REG, 1);
+	}
+
+	return res;
+}
+
+/*
+ * en/disable controller
+ */
+static void qfec_hw_enable(struct qfec_priv *priv)
+{
+	QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
+
+	qfec_reg_write(priv, OPER_MODE_REG,
+	qfec_reg_read(priv, OPER_MODE_REG)
+		| OPER_MODE_REG_ST | OPER_MODE_REG_SR);
+}
+
+static void qfec_hw_disable(struct qfec_priv *priv)
+{
+	QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
+
+	qfec_reg_write(priv, OPER_MODE_REG,
+	qfec_reg_read(priv, OPER_MODE_REG)
+		& ~(OPER_MODE_REG_ST | OPER_MODE_REG_SR));
+}
+
+/*
+ * interface selection
+ */
+struct intf_config  {
+	uint32_t     intf_sel;
+	uint32_t     emac_ns;
+	uint32_t     eth_x_en_ns;
+	uint32_t     clkmux_sel;
+};
+
+#define ETH_X_EN_NS_REVMII      (ETH_X_EN_NS_DEFAULT | ETH_TX_CLK_INV)
+#define CLKMUX_REVMII           (EMAC_CLKMUX_SEL_0 | EMAC_CLKMUX_SEL_1)
+
+static struct intf_config intf_config_tbl[] = {
+	{ EMAC_PHY_INTF_SEL_MII,    EMAC_NS_DEFAULT, ETH_X_EN_NS_DEFAULT, 0 },
+	{ EMAC_PHY_INTF_SEL_RGMII,  EMAC_NS_DEFAULT, ETH_X_EN_NS_DEFAULT, 0 },
+	{ EMAC_PHY_INTF_SEL_REVMII, EMAC_NS_DEFAULT, ETH_X_EN_NS_REVMII,
+								CLKMUX_REVMII }
+};
+
+/*
+ * emac clk register read and write functions
+ */
+static inline uint32_t qfec_clkreg_read(struct qfec_priv *priv, uint32_t reg)
+{
+	return ioread32((void *) (priv->clk_base + reg));
+}
+
+static inline void qfec_clkreg_write(struct qfec_priv *priv,
+	uint32_t reg, uint32_t val)
+{
+	uint32_t   addr = (uint32_t)priv->clk_base + reg;
+
+	QFEC_LOG(QFEC_LOG_DBG2, "%s: %08x <- %08x\n", __func__, addr, val);
+	iowrite32(val, (void *)addr);
+}
+
+/*
+ * configure the PHY interface and clock routing and signal bits
+ */
+enum phy_intfc  {
+	INTFC_MII     = 0,
+	INTFC_RGMII   = 1,
+	INTFC_REVMII  = 2,
+};
+
+static int qfec_intf_sel(struct qfec_priv *priv, unsigned int intfc)
+{
+	struct intf_config   *p;
+
+	QFEC_LOG(QFEC_LOG_DBG2, "%s: %d\n", __func__, intfc);
+
+	if (intfc > INTFC_REVMII)  {
+		QFEC_LOG_ERR("%s: range\n", __func__);
+		return -ENXIO;
+	}
+
+	p = &intf_config_tbl[intfc];
+
+	qfec_clkreg_write(priv, EMAC_PHY_INTF_SEL_REG, p->intf_sel);
+	qfec_clkreg_write(priv, EMAC_NS_REG,           p->emac_ns);
+	qfec_clkreg_write(priv, ETH_X_EN_NS_REG,       p->eth_x_en_ns);
+	qfec_clkreg_write(priv, EMAC_CLKMUX_SEL_REG,   p->clkmux_sel);
+
+	return 0;
+}
+
+/*
+ * display registers thru proc-fs
+ */
+static struct qfec_clk_reg {
+	uint32_t        offset;
+	char           *label;
+} qfec_clk_regs[] = {
+	{ ETH_MD_REG,                  "ETH_MD_REG"  },
+	{ ETH_NS_REG,                  "ETH_NS_REG"  },
+	{ ETH_X_EN_NS_REG,             "ETH_X_EN_NS_REG"  },
+	{ EMAC_PTP_MD_REG,             "EMAC_PTP_MD_REG"  },
+	{ EMAC_PTP_NS_REG,             "EMAC_PTP_NS_REG"  },
+	{ EMAC_NS_REG,                 "EMAC_NS_REG"  },
+	{ EMAC_TX_FS_REG,              "EMAC_TX_FS_REG"  },
+	{ EMAC_RX_FS_REG,              "EMAC_RX_FS_REG"  },
+	{ EMAC_PHY_INTF_SEL_REG,       "EMAC_PHY_INTF_SEL_REG"  },
+	{ EMAC_PHY_ADDR_REG,           "EMAC_PHY_ADDR_REG"  },
+	{ EMAC_REVMII_PHY_ADDR_REG,    "EMAC_REVMII_PHY_ADDR_REG"  },
+	{ EMAC_CLKMUX_SEL_REG,         "EMAC_CLKMUX_SEL_REG"  },
+};
+
+static int qfec_clk_reg_show(struct device *dev, struct device_attribute *attr,
+			      char *buf)
+{
+	struct qfec_priv        *priv = netdev_priv(to_net_dev(dev));
+	struct qfec_clk_reg     *p = qfec_clk_regs;
+	int                      n = ARRAY_SIZE(qfec_clk_regs);
+	int                      l = 0;
+	int                      count = PAGE_SIZE;
+
+	QFEC_LOG(QFEC_LOG_DBG2, "%s:\n", __func__);
+
+	for (; n--; p++) {
+		l += snprintf(&buf[l], count - l, "    %8p  %8x  %08x  %s\n",
+			(void *)priv->clk_base + p->offset, p->offset,
+			qfec_clkreg_read(priv, p->offset), p->label);
+	}
+
+	return  l;
+}
+
+/*
+ * speed selection
+ */
+
+struct qfec_pll_cfg {
+	uint32_t    spd;
+	uint32_t    eth_md;     /* M [31:16], NOT 2*D [15:0] */
+	uint32_t    eth_ns;     /* NOT(M-N) [31:16], ctl bits [11:0]  */
+};
+
+static struct qfec_pll_cfg qfec_pll_cfg_tbl[] = {
+	/* 2.5 MHz */
+	{ MAC_CONFIG_REG_SPD_10,   ETH_MD_M(1)  | ETH_MD_2D_N(100),
+						  ETH_NS_NM(100-1)
+						| ETH_NS_MCNTR_EN
+						| ETH_NS_MCNTR_MODE_DUAL
+						| ETH_NS_PRE_DIV(0)
+						| CLK_SRC_PLL_EMAC },
+	/* 25 MHz */
+	{ MAC_CONFIG_REG_SPD_100,  ETH_MD_M(1)  | ETH_MD_2D_N(10),
+						  ETH_NS_NM(10-1)
+						| ETH_NS_MCNTR_EN
+						| ETH_NS_MCNTR_MODE_DUAL
+						| ETH_NS_PRE_DIV(0)
+						| CLK_SRC_PLL_EMAC },
+	/* 125 MHz */
+	{MAC_CONFIG_REG_SPD_1G,    0,             ETH_NS_PRE_DIV(1)
+						| CLK_SRC_PLL_EMAC },
+};
+
+enum speed  {
+	SPD_10   = 0,
+	SPD_100  = 1,
+	SPD_1000 = 2,
+};
+
+/*
+ * configure the PHY interface and clock routing and signal bits
+ */
+static int qfec_speed_cfg(struct net_device *dev, unsigned int spd,
+	unsigned int dplx)
+{
+	struct qfec_priv       *priv = netdev_priv(dev);
+	struct qfec_pll_cfg    *p;
+
+	QFEC_LOG(QFEC_LOG_DBG2, "%s: %d spd, %d dplx\n", __func__, spd, dplx);
+
+	if (spd > SPD_1000)  {
+		QFEC_LOG_ERR("%s: range\n", __func__);
+		return -ENODEV;
+	}
+
+	p = &qfec_pll_cfg_tbl[spd];
+
+	/* set the MAC speed bits */
+	qfec_reg_write(priv, MAC_CONFIG_REG,
+	(qfec_reg_read(priv, MAC_CONFIG_REG)
+		& ~(MAC_CONFIG_REG_SPD | MAC_CONFIG_REG_DM))
+			| p->spd | (dplx ? MAC_CONFIG_REG_DM : H_DPLX));
+
+	qfec_clkreg_write(priv, ETH_MD_REG, p->eth_md);
+	qfec_clkreg_write(priv, ETH_NS_REG, p->eth_ns);
+
+	return 0;
+}
+
+/*
+ * configure PTP divider for 25 MHz assuming EMAC PLL 250 MHz
+ */
+
+static struct qfec_pll_cfg qfec_pll_ptp = {
+	/* 19.2 MHz  tcxo */
+	0,      0,                                ETH_NS_PRE_DIV(0)
+						| EMAC_PTP_NS_ROOT_EN
+						| EMAC_PTP_NS_CLK_EN
+						| CLK_SRC_TCXO
+};
+
+#define PLLTEST_PAD_CFG     0x01E0
+#define PLLTEST_PLL_7       0x3700
+
+#define CLKTEST_REG         0x01EC
+#define CLKTEST_EMAC_RX     0x3fc07f7a
+
+static int qfec_ptp_cfg(struct qfec_priv *priv)
+{
+	struct qfec_pll_cfg    *p    = &qfec_pll_ptp;
+
+	QFEC_LOG(QFEC_LOG_DBG2, "%s: %08x md, %08x ns\n",
+		__func__, p->eth_md, p->eth_ns);
+
+	qfec_clkreg_write(priv, EMAC_PTP_MD_REG, p->eth_md);
+	qfec_clkreg_write(priv, EMAC_PTP_NS_REG, p->eth_ns);
+
+	/* configure HS/LS clk test ports to verify clks */
+	qfec_clkreg_write(priv, CLKTEST_REG,     CLKTEST_EMAC_RX);
+	qfec_clkreg_write(priv, PLLTEST_PAD_CFG, PLLTEST_PLL_7);
+
+	return 0;
+}
+
+/*
+ * MDIO operations
+ */
+
+/*
+ * wait reasonable amount of time for MDIO operation to complete, not busy
+ */
+static int qfec_mdio_busy(struct net_device *dev)
+{
+	int     i;
+
+	for (i = 100; i > 0; i--)  {
+		if (!(qfec_reg_read(
+			netdev_priv(dev), GMII_ADR_REG) & GMII_ADR_REG_GB))  {
+			return 0;
+		}
+		udelay(1);
+	}
+
+	return -ETIME;
+}
+
+/*
+ * initiate either a read or write MDIO operation
+ */
+
+static int qfec_mdio_oper(struct net_device *dev, int phy_id, int reg, int wr)
+{
+	struct qfec_priv   *priv = netdev_priv(dev);
+	int                 res = 0;
+
+	/* insure phy not busy */
+	res = qfec_mdio_busy(dev);
+	if (res)  {
+		QFEC_LOG_ERR("%s: busy\n", __func__);
+		goto done;
+	}
+
+	/* initiate operation */
+	qfec_reg_write(priv, GMII_ADR_REG,
+		GMII_ADR_REG_ADR_SET(phy_id)
+		| GMII_ADR_REG_REG_SET(reg)
+		| GMII_ADR_REG_CSR_SET(priv->mdio_clk)
+		| (wr ? GMII_ADR_REG_GW : 0)
+		| GMII_ADR_REG_GB);
+
+	/* wait for operation to complete */
+	res = qfec_mdio_busy(dev);
+	if (res)
+		QFEC_LOG_ERR("%s: timeout\n", __func__);
+
+done:
+	return res;
+}
+
+/*
+ * read MDIO register
+ */
+static int qfec_mdio_read(struct net_device *dev, int phy_id, int reg)
+{
+	struct qfec_priv   *priv = netdev_priv(dev);
+	int                 res = 0;
+	unsigned long       flags;
+
+	spin_lock_irqsave(&priv->mdio_lock, flags);
+
+	res = qfec_mdio_oper(dev, phy_id, reg, 0);
+	if (res)  {
+		QFEC_LOG_ERR("%s: oper\n", __func__);
+		goto done;
+	}
+
+	res = qfec_reg_read(priv, GMII_DATA_REG);
+	QFEC_LOG(QFEC_LOG_MDIO_R, "%s: %2d reg, 0x%04x val\n",
+		__func__, reg, res);
+
+done:
+	spin_unlock_irqrestore(&priv->mdio_lock, flags);
+	return res;
+}
+
+/*
+ * write MDIO register
+ */
+static void qfec_mdio_write(struct net_device *dev, int phy_id, int reg,
+	int val)
+{
+	struct qfec_priv   *priv = netdev_priv(dev);
+	unsigned long       flags;
+
+	spin_lock_irqsave(&priv->mdio_lock, flags);
+
+	QFEC_LOG(QFEC_LOG_MDIO_W, "%s: %2d reg, %04x\n",
+		__func__, reg, val);
+
+	qfec_reg_write(priv, GMII_DATA_REG, val);
+
+	if (qfec_mdio_oper(dev, phy_id, reg, 1))
+		QFEC_LOG_ERR("%s: oper\n", __func__);
+
+	spin_unlock_irqrestore(&priv->mdio_lock, flags);
+}
+
+/*
+ * MDIO show
+ */
+static int qfec_mdio_show(struct device *dev, struct device_attribute *attr,
+					char *buf)
+{
+	struct qfec_priv        *priv = netdev_priv(to_net_dev(dev));
+	int                      n;
+	int                      l = 0;
+	int                      count = PAGE_SIZE;
+
+	QFEC_LOG(QFEC_LOG_DBG2, "%s:\n", __func__);
+
+	for (n = 0; n < MAX_MDIO_REG; n++) {
+		if (!(n % 8))
+			l += snprintf(&buf[l], count - l, "\n   %02x: ", n);
+
+		l += snprintf(&buf[l], count - l, " %04x",
+			qfec_mdio_read(to_net_dev(dev), priv->phy_id, n));
+	}
+	l += snprintf(&buf[l], count - l, "\n");
+
+	return  l;
+}
+
+/*
+ * get auto-negotiation results
+ */
+#define QFEC_100        (LPA_100HALF | LPA_100FULL | LPA_100HALF)
+#define QFEC_100_FD     (LPA_100FULL | LPA_100BASE4)
+#define QFEC_10         (LPA_10HALF  | LPA_10FULL)
+#define QFEC_10_FD       LPA_10FULL
+
+static void qfec_get_an(struct net_device *dev, uint32_t *spd, uint32_t *dplx)
+{
+	struct qfec_priv   *priv = netdev_priv(dev);
+	uint32_t  advert   = qfec_mdio_read(dev, priv->phy_id, MII_ADVERTISE);
+	uint32_t  lpa      = qfec_mdio_read(dev, priv->phy_id, MII_LPA);
+	uint32_t  mastCtrl = qfec_mdio_read(dev, priv->phy_id, MII_CTRL1000);
+	uint32_t  mastStat = qfec_mdio_read(dev, priv->phy_id, MII_STAT1000);
+	uint32_t  anExp    = qfec_mdio_read(dev, priv->phy_id, MII_EXPANSION);
+	uint32_t  status   = advert & lpa;
+	uint32_t  flow;
+
+	if (priv->mii.supports_gmii) {
+		if (((anExp & QFEC_MII_EXP_MASK) == QFEC_MII_EXP_MASK)
+			&& (mastCtrl & ADVERTISE_1000FULL)
+			&& (mastStat & LPA_1000FULL)) {
+			*spd  = SPD_1000;
+			*dplx = F_DPLX;
+			goto pause;
+		}
+
+		else if (((anExp & QFEC_MII_EXP_MASK) == QFEC_MII_EXP_MASK)
+			&& (mastCtrl & ADVERTISE_1000HALF)
+			&& (mastStat & LPA_1000HALF)) {
+			*spd  = SPD_1000;
+			*dplx = H_DPLX;
+			goto pause;
+		}
+	}
+
+	/* mii speeds */
+	if (status & QFEC_100)  {
+		*spd  = SPD_100;
+		*dplx = status & QFEC_100_FD ? F_DPLX : H_DPLX;
+	}
+
+	else if (status & QFEC_10)  {
+		*spd  = SPD_10;
+		*dplx = status & QFEC_10_FD ? F_DPLX : H_DPLX;
+	}
+
+	/* check pause */
+pause:
+	flow  = qfec_reg_read(priv, FLOW_CONTROL_REG);
+	flow &= ~(FLOW_CONTROL_TFE | FLOW_CONTROL_RFE);
+
+	if (status & ADVERTISE_PAUSE_CAP)  {
+		flow |= FLOW_CONTROL_RFE | FLOW_CONTROL_TFE;
+	} else if (status & ADVERTISE_PAUSE_ASYM)  {
+		if (lpa & ADVERTISE_PAUSE_CAP)
+			flow |= FLOW_CONTROL_TFE;
+		else if (advert & ADVERTISE_PAUSE_CAP)
+			flow |= FLOW_CONTROL_RFE;
+	}
+
+	qfec_reg_write(priv, FLOW_CONTROL_REG, flow);
+}
+
+/*
+ * monitor phy status, and process auto-neg results when changed
+ */
+
+static void qfec_phy_monitor(unsigned long data)
+{
+	struct net_device  *dev  = (struct net_device *) data;
+	struct qfec_priv   *priv = netdev_priv(dev);
+	unsigned int        spd  = H_DPLX;
+	unsigned int        dplx = F_DPLX;
+
+	mod_timer(&priv->phy_tmr, jiffies + HZ);
+
+	if (mii_link_ok(&priv->mii) && !netif_carrier_ok(priv->net_dev))  {
+		qfec_get_an(dev, &spd, &dplx);
+		qfec_speed_cfg(dev, spd, dplx);
+		QFEC_LOG(QFEC_LOG_DBG, "%s: link up, %d spd, %d dplx\n",
+			__func__, spd, dplx);
+
+		netif_carrier_on(dev);
+	}
+
+	else if (!mii_link_ok(&priv->mii) && netif_carrier_ok(priv->net_dev))  {
+		QFEC_LOG(QFEC_LOG_DBG, "%s: link down\n", __func__);
+		netif_carrier_off(dev);
+	}
+}
+
+/*
+ * dealloc buffer descriptor memory
+ */
+
+static void qfec_mem_dealloc(struct net_device *dev)
+{
+	struct qfec_priv   *priv = netdev_priv(dev);
+
+	dma_free_coherent(&dev->dev,
+		priv->bd_size, priv->bd_base, priv->tbd_dma);
+	priv->bd_base = 0;
+}
+
+/*
+ * allocate shared device memory for TX/RX buf-desc (and buffers)
+ */
+
+static int qfec_mem_alloc(struct net_device *dev)
+{
+	struct qfec_priv   *priv = netdev_priv(dev);
+
+	QFEC_LOG(QFEC_LOG_DBG, "%s: %p dev\n", __func__, dev);
+
+	priv->bd_size =
+		(priv->n_tbd + priv->n_rbd) * sizeof(struct qfec_buf_desc);
+
+	priv->p_tbd = kcalloc(priv->n_tbd, sizeof(struct buf_desc), GFP_KERNEL);
+	if (!priv->p_tbd)  {
+		QFEC_LOG_ERR("%s: kcalloc failed p_tbd\n", __func__);
+		return -ENOMEM;
+	}
+
+	priv->p_rbd = kcalloc(priv->n_rbd, sizeof(struct buf_desc), GFP_KERNEL);
+	if (!priv->p_rbd)  {
+		QFEC_LOG_ERR("%s: kcalloc failed p_rbd\n", __func__);
+		return -ENOMEM;
+	}
+
+	/* alloc mem for buf-desc, if not already alloc'd */
+	if (!priv->bd_base)  {
+		priv->bd_base = dma_alloc_coherent(&dev->dev,
+			priv->bd_size, &priv->tbd_dma,
+			GFP_KERNEL | __GFP_DMA);
+	}
+
+	if (!priv->bd_base)  {
+		QFEC_LOG_ERR("%s: dma_alloc_coherent failed\n", __func__);
+		return -ENOMEM;
+	}
+
+	priv->rbd_dma   = priv->tbd_dma
+			+ (priv->n_tbd * sizeof(struct qfec_buf_desc));
+
+	QFEC_LOG(QFEC_LOG_DBG,
+		" %s: 0x%08x size, %d n_tbd, %d n_rbd\n",
+		__func__, priv->bd_size, priv->n_tbd, priv->n_rbd);
+
+	return 0;
+}
+
+/*
+ * display buffer descriptors
+ */
+
+static int qfec_bd_fmt(char *buf, int size, struct buf_desc *p_bd)
+{
+	return snprintf(buf, size,
+		"%8p: %08x %08x %8p %8p  %8p %8p %8p %x",
+		p_bd,                     qfec_bd_status_get(p_bd),
+		qfec_bd_ctl_get(p_bd),    qfec_bd_pbuf_get(p_bd),
+		qfec_bd_next_get(p_bd),   qfec_bd_skbuf_get(p_bd),
+		qfec_bd_virt_get(p_bd),   qfec_bd_phys_get(p_bd),
+		qfec_bd_last_bd(p_bd));
+}
+
+static int qfec_bd_show(char *buf, int count, struct buf_desc *p_bd, int n_bd,
+	struct ring *p_ring, char *label)
+{
+	int     l = 0;
+	int     n;
+
+	QFEC_LOG(QFEC_LOG_DBG2, "%s: %s\n", __func__, label);
+
+	l += snprintf(&buf[l], count, "%s: %s\n", __func__, label);
+	if (!p_bd)
+		return l;
+
+	n_bd = n_bd > MAX_N_BD ? MAX_N_BD : n_bd;
+
+	for (n = 0; n < n_bd; n++, p_bd++) {
+		l += qfec_bd_fmt(&buf[l], count - l, p_bd);
+		l += snprintf(&buf[l], count - l, "%s%s\n",
+			(qfec_ring_head(p_ring) == n ? " < h" : ""),
+			(qfec_ring_tail(p_ring) == n ? " < t" : ""));
+	}
+
+	return l;
+}
+
+/*
+ * display TX BDs
+ */
+static int qfec_bd_tx_show(struct device *dev, struct device_attribute *attr,
+			      char *buf)
+{
+	struct qfec_priv   *priv = netdev_priv(to_net_dev(dev));
+	int                 count = PAGE_SIZE;
+
+	return qfec_bd_show(buf, count, priv->p_tbd, priv->n_tbd,
+				&priv->ring_tbd, "TX");
+}
+
+/*
+ * display RX BDs
+ */
+static int qfec_bd_rx_show(struct device *dev, struct device_attribute *attr,
+			      char *buf)
+{
+	struct qfec_priv   *priv = netdev_priv(to_net_dev(dev));
+	int                 count = PAGE_SIZE;
+
+	return  qfec_bd_show(buf, count, priv->p_rbd, priv->n_rbd,
+				&priv->ring_rbd, "RX");
+}
+
+/*
+ * process timestamp values
+ *    The pbuf and next fields of the buffer descriptors are overwritten
+ *    with the timestamp high and low register values.
+ *
+ *    The low register is incremented by the value in the subsec_increment
+ *    register and overflows at 0x8000 0000 causing the high register to
+ *    increment.
+ *
+ *    The subsec_increment register is recommended to be set to the number
+ *    of nanosec corresponding to each clock tic, scaled by 2^31 / 10^9
+ *    (e.g. 40 * 2^32 / 10^9 = 85.9, or 86 for 25 MHz).  However, the
+ *    rounding error in this case will result in a 1 sec error / ~14 mins.
+ *
+ *    An alternate approach is used.  The subsec_increment is set to 1,
+ *    and the concatenation of the 2 timestamp registers used to count
+ *    clock tics.  The 63-bit result is manipulated to determine the number
+ *    of sec and ns.
+ */
+
+/*
+ * convert 19.2 MHz clock tics into sec/ns
+ */
+#define TS_LOW_REG_BITS    31
+
+#define MILLION            1000000UL
+#define BILLION            1000000000UL
+
+#define F_CLK              19200000UL
+#define F_CLK_PRE_SC       24
+#define F_CLK_INV_Q        56
+#define F_CLK_INV          (((unsigned long long)1 << F_CLK_INV_Q) / F_CLK)
+#define F_CLK_TO_NS_Q      25
+#define F_CLK_TO_NS \
+	(((((unsigned long long)1<<F_CLK_TO_NS_Q)*BILLION)+(F_CLK-1))/F_CLK)
+#define US_TO_F_CLK_Q      20
+#define US_TO_F_CLK \
+	(((((unsigned long long)1<<US_TO_F_CLK_Q)*F_CLK)+(MILLION-1))/MILLION)
+
+static inline void qfec_get_sec(uint64_t *cnt,
+			uint32_t  *sec, uint32_t  *ns)
+{
+	unsigned long long  t;
+	unsigned long long  subsec;
+
+	t       = *cnt >> F_CLK_PRE_SC;
+	t      *= F_CLK_INV;
+	t     >>= F_CLK_INV_Q - F_CLK_PRE_SC;
+	*sec    = t;
+
+	t       = *cnt - (t * F_CLK);
+	subsec  = t;
+
+	if (subsec >= F_CLK)  {
+		subsec -= F_CLK;
+		*sec   += 1;
+	}
+
+	subsec  *= F_CLK_TO_NS;
+	subsec >>= F_CLK_TO_NS_Q;
+	*ns      = subsec;
+}
+
+/*
+ * read ethernet timestamp registers, pass up raw register values
+ * and values converted to sec/ns
+ */
+static void qfec_read_timestamp(struct buf_desc *p_bd,
+	struct skb_shared_hwtstamps *ts)
+{
+	unsigned long long  cnt;
+	unsigned int        sec;
+	unsigned int        subsec;
+
+	cnt    = (unsigned long)qfec_bd_next_get(p_bd);
+	cnt  <<= TS_LOW_REG_BITS;
+	cnt   |= (unsigned long)qfec_bd_pbuf_get(p_bd);
+
+	/* report raw counts as concatenated 63 bits */
+	sec    = cnt >> 32;
+	subsec = cnt & 0xffffffff;
+
+	ts->hwtstamp  = ktime_set(sec, subsec);
+
+	/* translate counts to sec and ns */
+	qfec_get_sec(&cnt, &sec, &subsec);
+
+	ts->syststamp = ktime_set(sec, subsec);
+}
+
+/*
+ * capture the current system time in the timestamp registers
+ */
+static int qfec_cmd(struct device *dev, struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct qfec_priv  *priv = netdev_priv(to_net_dev(dev));
+	struct timeval     tv;
+
+	if (!strncmp(buf, "setTs", 5))  {
+		unsigned long long  cnt;
+		uint32_t            ts_hi;
+		uint32_t            ts_lo;
+		unsigned long long  subsec;
+
+		do_gettimeofday(&tv);
+
+		/* convert raw sec/usec to ns */
+		subsec   = tv.tv_usec;
+		subsec  *= US_TO_F_CLK;
+		subsec >>= US_TO_F_CLK_Q;
+
+		cnt     = tv.tv_sec;
+		cnt    *= F_CLK;
+		cnt    += subsec;
+
+		ts_hi   = cnt >> 31;
+		ts_lo   = cnt & 0x7FFFFFFF;
+
+		qfec_reg_write(priv, TS_HI_UPDT_REG, ts_hi);
+		qfec_reg_write(priv, TS_LO_UPDT_REG, ts_lo);
+
+		qfec_reg_write(priv, TS_CTL_REG,
+			qfec_reg_read(priv, TS_CTL_REG) | TS_CTL_TSINIT);
+	} else
+		pr_err("%s: unknown cmd, %s.\n", __func__, buf);
+
+	return strnlen(buf, count);
+}
+
+/*
+ * display ethernet tstamp and system time
+ */
+static int qfec_tstamp_show(struct device *dev, struct device_attribute *attr,
+				char *buf)
+{
+	struct qfec_priv   *priv = netdev_priv(to_net_dev(dev));
+	int                 count = PAGE_SIZE;
+	int                 l;
+	struct timeval      tv;
+	unsigned long long  cnt;
+	uint32_t            sec;
+	uint32_t            ns;
+	uint32_t            ts_hi;
+	uint32_t            ts_lo;
+
+	/* insure that ts_hi didn't increment during read */
+	do {
+		ts_hi = qfec_reg_read(priv, TS_HIGH_REG);
+		ts_lo = qfec_reg_read(priv, TS_LOW_REG);
+	} while (ts_hi != qfec_reg_read(priv, TS_HIGH_REG));
+
+	cnt    = ts_hi;
+	cnt  <<= TS_LOW_REG_BITS;
+	cnt   |= ts_lo;
+
+	do_gettimeofday(&tv);
+
+	ts_hi  = cnt >> 32;
+	ts_lo  = cnt & 0xffffffff;
+
+	qfec_get_sec(&cnt, &sec, &ns);
+
+	l = snprintf(buf, count,
+		"%12u.%09u sec 0x%08x 0x%08x tstamp  %12u.%06u time-of-day\n",
+		sec, ns, ts_hi, ts_lo, (int)tv.tv_sec, (int)tv.tv_usec);
+
+	return l;
+}
+
+/*
+ * free transmitted skbufs from buffer-descriptor no owned by HW
+ */
+static int qfec_tx_replenish(struct net_device *dev)
+{
+	struct qfec_priv   *priv   = netdev_priv(dev);
+	struct ring        *p_ring = &priv->ring_tbd;
+	struct buf_desc    *p_bd   = &priv->p_tbd[qfec_ring_tail(p_ring)];
+	struct sk_buff     *skb;
+	unsigned long      flags;
+
+	CNTR_INC(priv, tx_replenish);
+
+	spin_lock_irqsave(&priv->xmit_lock, flags);
+
+	while (!qfec_ring_empty(p_ring))  {
+		if (qfec_bd_own(p_bd))
+			break;          /* done for now */
+
+		skb = qfec_bd_skbuf_get(p_bd);
+		if (unlikely(skb == NULL))  {
+			QFEC_LOG_ERR("%s: null sk_buff\n", __func__);
+			CNTR_INC(priv, tx_skb_null);
+			break;
+		}
+
+		qfec_reg_write(priv, STATUS_REG,
+			STATUS_REG_TU | STATUS_REG_TI);
+
+		/* retrieve timestamp if requested */
+		if (qfec_bd_status_get(p_bd) & BUF_TX_TTSS)  {
+			CNTR_INC(priv, ts_tx_rtn);
+			qfec_read_timestamp(p_bd, skb_hwtstamps(skb));
+			skb_tstamp_tx(skb, skb_hwtstamps(skb));
+		}
+
+		/* update statistics before freeing skb */
+		priv->stats.tx_packets++;
+		priv->stats.tx_bytes  += skb->len;
+
+		dma_unmap_single(&dev->dev, (dma_addr_t) qfec_bd_pbuf_get(p_bd),
+				skb->len, DMA_TO_DEVICE);
+
+		dev_kfree_skb_any(skb);
+		qfec_bd_skbuf_set(p_bd, NULL);
+
+		qfec_ring_tail_adv(p_ring);
+		p_bd   = &priv->p_tbd[qfec_ring_tail(p_ring)];
+	}
+
+	spin_unlock_irqrestore(&priv->xmit_lock, flags);
+
+	qfec_queue_start(dev);
+
+	return 0;
+}
+
+/*
+ * clear ownership bits of all TX buf-desc and release the sk-bufs
+ */
+static void qfec_tx_timeout(struct net_device *dev)
+{
+	struct qfec_priv   *priv   = netdev_priv(dev);
+	struct buf_desc    *bd     = priv->p_tbd;
+	int                 n;
+
+	QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
+	CNTR_INC(priv, tx_timeout);
+
+	for (n = 0; n < priv->n_tbd; n++, bd++)
+		qfec_bd_own_clr(bd);
+
+	qfec_tx_replenish(dev);
+}
+
+/*
+ * rx() - process a received frame
+ */
+static void qfec_rx_int(struct net_device *dev)
+{
+	struct qfec_priv   *priv   = netdev_priv(dev);
+	struct ring        *p_ring = &priv->ring_rbd;
+	struct buf_desc    *p_bd   = priv->p_latest_rbd;
+	uint32_t desc_status;
+	uint32_t mis_fr_reg;
+
+	desc_status = qfec_bd_status_get(p_bd);
+	mis_fr_reg = qfec_reg_read(priv, MIS_FR_REG);
+
+	CNTR_INC(priv, rx_int);
+
+	/* check that valid interrupt occurred */
+	if (unlikely(desc_status & BUF_OWN))
+		return;
+
+	/* accumulate missed-frame count (reg reset when read) */
+	priv->stats.rx_missed_errors += mis_fr_reg
+					& MIS_FR_REG_MISS_CNT;
+
+	/* process all unowned frames */
+	while (!(desc_status & BUF_OWN) && (!qfec_ring_full(p_ring)))  {
+		struct sk_buff     *skb;
+		struct buf_desc    *p_bd_next;
+
+		skb = qfec_bd_skbuf_get(p_bd);
+
+		if (unlikely(skb == NULL))  {
+			QFEC_LOG_ERR("%s: null sk_buff\n", __func__);
+			CNTR_INC(priv, rx_skb_null);
+			break;
+		}
+
+		/* cache coherency before skb->data is accessed */
+		dma_unmap_single(&dev->dev,
+			(dma_addr_t) qfec_bd_phys_get(p_bd),
+			ETH_BUF_SIZE, DMA_FROM_DEVICE);
+		prefetch(skb->data);
+
+		if (unlikely(desc_status & BUF_RX_ES)) {
+			priv->stats.rx_dropped++;
+			CNTR_INC(priv, rx_dropped);
+			dev_kfree_skb(skb);
+		} else  {
+			qfec_reg_write(priv, STATUS_REG, STATUS_REG_RI);
+
+			skb->len = BUF_RX_FL_GET_FROM_STATUS(desc_status);
+
+			if (priv->state & timestamping)  {
+				CNTR_INC(priv, ts_rec);
+				qfec_read_timestamp(p_bd, skb_hwtstamps(skb));
+			}
+
+			/* update statistics before freeing skb */
+			priv->stats.rx_packets++;
+			priv->stats.rx_bytes  += skb->len;
+
+			skb->dev        = dev;
+			skb->protocol   = eth_type_trans(skb, dev);
+			skb->ip_summed  = CHECKSUM_UNNECESSARY;
+
+			if (NET_RX_DROP == netif_rx(skb))  {
+				priv->stats.rx_dropped++;
+				CNTR_INC(priv, rx_dropped);
+			}
+			CNTR_INC(priv, netif_rx_cntr);
+		}
+
+		if (p_bd != priv->p_ending_rbd)
+			p_bd_next = p_bd + 1;
+		else
+			p_bd_next = priv->p_rbd;
+		desc_status = qfec_bd_status_get(p_bd_next);
+
+		qfec_bd_skbuf_set(p_bd, NULL);
+
+		qfec_ring_head_adv(p_ring);
+		p_bd = p_bd_next;
+	}
+
+	priv->p_latest_rbd = p_bd;
+
+	/* replenish bufs */
+	while (!qfec_ring_empty(p_ring))  {
+		if (qfec_rbd_init(dev, &priv->p_rbd[qfec_ring_tail(p_ring)]))
+			break;
+		qfec_ring_tail_adv(p_ring);
+	}
+
+	qfec_reg_write(priv, STATUS_REG, STATUS_REG_RI);
+}
+
+/*
+ * isr() - interrupt service routine
+ *          determine cause of interrupt and invoke/schedule appropriate
+ *          processing or error handling
+ */
+#define ISR_ERR_CHK(priv, status, interrupt, cntr) \
+	if (status & interrupt) \
+		CNTR_INC(priv, cntr)
+
+static irqreturn_t qfec_int(int irq, void *dev_id)
+{
+	struct net_device  *dev      = dev_id;
+	struct qfec_priv   *priv     = netdev_priv(dev);
+	uint32_t            status   = qfec_reg_read(priv, STATUS_REG);
+	uint32_t            int_bits = STATUS_REG_NIS | STATUS_REG_AIS;
+
+	QFEC_LOG(QFEC_LOG_DBG2, "%s: %s\n", __func__, dev->name);
+
+	/* abnormal interrupt */
+	if (status & STATUS_REG_AIS)  {
+		QFEC_LOG(QFEC_LOG_DBG, "%s: abnormal status 0x%08x\n",
+			__func__, status);
+
+		ISR_ERR_CHK(priv, status, STATUS_REG_RU,  rx_buf_unavail);
+		ISR_ERR_CHK(priv, status, STATUS_REG_FBI, fatal_bus);
+
+		ISR_ERR_CHK(priv, status, STATUS_REG_RWT, rx_watchdog);
+		ISR_ERR_CHK(priv, status, STATUS_REG_RPS, rx_proc_stopped);
+		ISR_ERR_CHK(priv, status, STATUS_REG_UNF, tx_underflow);
+
+		ISR_ERR_CHK(priv, status, STATUS_REG_OVF, rx_overflow);
+		ISR_ERR_CHK(priv, status, STATUS_REG_TJT, tx_jabber_tmout);
+		ISR_ERR_CHK(priv, status, STATUS_REG_TPS, tx_proc_stopped);
+
+		int_bits |= STATUS_REG_AIS_BITS;
+		CNTR_INC(priv, abnorm_int);
+	}
+
+	if (status & STATUS_REG_NIS)
+		CNTR_INC(priv, norm_int);
+
+	/* receive interrupt */
+	if (status & STATUS_REG_RI) {
+		CNTR_INC(priv, rx_isr);
+		qfec_rx_int(dev);
+	}
+
+	/* transmit interrupt */
+	if (status & STATUS_REG_TI)  {
+		CNTR_INC(priv, tx_isr);
+		qfec_tx_replenish(dev);
+	}
+
+	/* gmac interrupt */
+	if (status & (STATUS_REG_GPI | STATUS_REG_GMI | STATUS_REG_GLI))  {
+		status &= ~(STATUS_REG_GPI | STATUS_REG_GMI | STATUS_REG_GLI);
+		CNTR_INC(priv, gmac_isr);
+		int_bits |= STATUS_REG_GPI | STATUS_REG_GMI | STATUS_REG_GLI;
+		qfec_reg_read(priv, SG_RG_SMII_STATUS_REG);
+	}
+
+	/* clear interrupts */
+	qfec_reg_write(priv, STATUS_REG, int_bits);
+	CNTR_INC(priv, isr);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * open () - register system resources (IRQ, DMA, ...)
+ *   turn on HW, perform device setup.
+ */
+static int qfec_open(struct net_device *dev)
+{
+	struct qfec_priv   *priv = netdev_priv(dev);
+	struct buf_desc    *p_bd;
+	struct ring        *p_ring;
+	struct qfec_buf_desc *p_desc;
+	int                 n;
+	int                 res = 0;
+
+	QFEC_LOG(QFEC_LOG_DBG, "%s: %p dev\n", __func__, dev);
+
+	if (!dev)  {
+		res = -EINVAL;
+		goto err;
+	}
+
+	/* allocate TX/RX buffer-descriptors and buffers */
+
+	res = qfec_mem_alloc(dev);
+	if (res)
+		goto err;
+
+	/* initialize TX */
+	p_desc = priv->bd_base;
+
+	for (n = 0, p_bd = priv->p_tbd; n < priv->n_tbd; n++, p_bd++) {
+		p_bd->p_desc = p_desc++;
+
+		if (n == (priv->n_tbd - 1))
+			qfec_bd_last_bd_set(p_bd);
+
+		qfec_bd_own_clr(p_bd);      /* clear ownership */
+	}
+
+	qfec_ring_init(&priv->ring_tbd, priv->n_tbd, priv->n_tbd);
+
+	priv->tx_ic_mod = priv->n_tbd / TX_BD_TI_RATIO;
+	if (priv->tx_ic_mod == 0)
+		priv->tx_ic_mod = 1;
+
+	/* initialize RX buffer descriptors and allocate sk_bufs */
+	p_ring = &priv->ring_rbd;
+	qfec_ring_init(p_ring, priv->n_rbd, 0);
+	qfec_bd_last_bd_set(&priv->p_rbd[priv->n_rbd - 1]);
+
+	for (n = 0, p_bd = priv->p_rbd; n < priv->n_rbd; n++, p_bd++) {
+		p_bd->p_desc = p_desc++;
+
+		if (qfec_rbd_init(dev, p_bd))
+			break;
+		qfec_ring_tail_adv(p_ring);
+	}
+
+	priv->p_latest_rbd = priv->p_rbd;
+	priv->p_ending_rbd = priv->p_rbd + priv->n_rbd - 1;
+
+	/* config ptp clock */
+	qfec_ptp_cfg(priv);
+
+	/* configure PHY - must be set before reset/hw_init */
+	priv->mii.supports_gmii = mii_check_gmii_support(&priv->mii);
+	if (priv->mii.supports_gmii) {
+		QFEC_LOG_ERR("%s: RGMII\n", __func__);
+		qfec_intf_sel(priv, INTFC_RGMII);
+	} else {
+		QFEC_LOG_ERR("%s: MII\n", __func__);
+		qfec_intf_sel(priv, INTFC_MII);
+	}
+
+	/* initialize controller after BDs allocated */
+	res = qfec_hw_init(priv);
+	if (res)
+		goto err1;
+
+	/* get/set (primary) MAC address */
+	qfec_set_adr_regs(priv, dev->dev_addr);
+	qfec_set_rx_mode(dev);
+
+	/* start phy monitor */
+	QFEC_LOG(QFEC_LOG_DBG, " %s: start timer\n", __func__);
+	netif_carrier_off(priv->net_dev);
+	setup_timer(&priv->phy_tmr, qfec_phy_monitor, (unsigned long)dev);
+	mod_timer(&priv->phy_tmr, jiffies + HZ);
+
+	/* driver supports AN capable PHY only */
+	qfec_mdio_write(dev, priv->phy_id, MII_BMCR, BMCR_RESET);
+	res = (BMCR_ANENABLE|BMCR_ANRESTART);
+	qfec_mdio_write(dev, priv->phy_id, MII_BMCR, res);
+
+	/* initialize interrupts */
+	QFEC_LOG(QFEC_LOG_DBG, " %s: request irq %d\n", __func__, dev->irq);
+	res = request_irq(dev->irq, qfec_int, 0, dev->name, dev);
+	if (res)
+		goto err1;
+
+	/* enable controller */
+	qfec_hw_enable(priv);
+	netif_start_queue(dev);
+
+	QFEC_LOG(QFEC_LOG_DBG, "%s: %08x link, %08x carrier\n", __func__,
+		mii_link_ok(&priv->mii), netif_carrier_ok(priv->net_dev));
+
+	QFEC_LOG(QFEC_LOG_DBG, " %s: done\n", __func__);
+	return 0;
+
+err1:
+	qfec_mem_dealloc(dev);
+err:
+	QFEC_LOG_ERR("%s: error - %d\n", __func__, res);
+	return res;
+}
+
+/*
+ * stop() - "reverse operations performed at open time"
+ */
+static int qfec_stop(struct net_device *dev)
+{
+	struct qfec_priv   *priv = netdev_priv(dev);
+	struct buf_desc    *p_bd;
+	struct sk_buff     *skb;
+	int                 n;
+
+	QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
+
+	del_timer_sync(&priv->phy_tmr);
+
+	qfec_hw_disable(priv);
+	qfec_queue_stop(dev);
+	free_irq(dev->irq, dev);
+
+	/* free all pending sk_bufs */
+	for (n = priv->n_rbd, p_bd = priv->p_rbd; n > 0; n--, p_bd++) {
+		skb = qfec_bd_skbuf_get(p_bd);
+		if (skb)
+			dev_kfree_skb(skb);
+	}
+
+	for (n = priv->n_tbd, p_bd = priv->p_tbd; n > 0; n--, p_bd++) {
+		skb = qfec_bd_skbuf_get(p_bd);
+		if (skb)
+			dev_kfree_skb(skb);
+	}
+
+	qfec_mem_dealloc(dev);
+
+	QFEC_LOG(QFEC_LOG_DBG, " %s: done\n", __func__);
+
+	return 0;
+}
+
+static int qfec_set_config(struct net_device *dev, struct ifmap *map)
+{
+	QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
+	return 0;
+}
+
+/*
+ * pass data from skbuf to buf-desc
+ */
+static int qfec_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct qfec_priv   *priv   = netdev_priv(dev);
+	struct ring        *p_ring = &priv->ring_tbd;
+	struct buf_desc    *p_bd;
+	uint32_t            ctrl   = 0;
+	int                 ret    = NETDEV_TX_OK;
+	unsigned long       flags;
+
+	CNTR_INC(priv, xmit);
+
+	spin_lock_irqsave(&priv->xmit_lock, flags);
+
+	/* If there is no room, on the ring try to free some up */
+	if (qfec_ring_room(p_ring) == 0)
+		qfec_tx_replenish(dev);
+
+	/* stop queuing if no resources available */
+	if (qfec_ring_room(p_ring) == 0)  {
+		qfec_queue_stop(dev);
+		CNTR_INC(priv, tx_no_resource);
+
+		ret = NETDEV_TX_BUSY;
+		goto done;
+	}
+
+	/* locate and save *sk_buff */
+	p_bd = &priv->p_tbd[qfec_ring_head(p_ring)];
+	qfec_bd_skbuf_set(p_bd, skb);
+
+	/* set DMA ptr to sk_buff data and write cache to memory */
+	qfec_bd_pbuf_set(p_bd, (void *)
+	dma_map_single(&dev->dev,
+		(void *)skb->data, skb->len, DMA_TO_DEVICE));
+
+	ctrl  = skb->len;
+	if (!(qfec_ring_head(p_ring) % priv->tx_ic_mod))
+		ctrl |= BUF_TX_IC; /* interrupt on complete */
+
+	/* check if timestamping enabled and requested */
+	if (priv->state & timestamping)  {
+		if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) {
+			CNTR_INC(priv, ts_tx_en);
+			ctrl |= BUF_TX_IC;	/* interrupt on complete */
+			ctrl |= BUF_TX_TTSE;	/* enable timestamp */
+			skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+		}
+	}
+
+	if (qfec_bd_last_bd(p_bd))
+		ctrl |= BUF_RX_RER;
+
+	/* no gather, no multi buf frames */
+	ctrl |= BUF_TX_FS | BUF_TX_LS;  /* 1st and last segment */
+
+	qfec_bd_ctl_wr(p_bd, ctrl);
+	qfec_bd_status_set(p_bd, BUF_OWN);
+
+	qfec_ring_head_adv(p_ring);
+	qfec_reg_write(priv, TX_POLL_DEM_REG, 1);      /* poll */
+
+done:
+	spin_unlock_irqrestore(&priv->xmit_lock, flags);
+
+	return ret;
+}
+
+static int qfec_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	struct qfec_priv        *priv = netdev_priv(dev);
+	struct hwtstamp_config  *cfg  = (struct hwtstamp_config *) ifr;
+
+	QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
+
+	if (cmd == SIOCSHWTSTAMP) {
+		CNTR_INC(priv, ts_ioctl);
+		QFEC_LOG(QFEC_LOG_DBG,
+			"%s: SIOCSHWTSTAMP - %x flags  %x tx  %x rx\n",
+			__func__, cfg->flags, cfg->tx_type, cfg->rx_filter);
+
+		cfg->flags      = 0;
+		cfg->tx_type    = HWTSTAMP_TX_ON;
+		cfg->rx_filter  = HWTSTAMP_FILTER_ALL;
+
+		priv->state |= timestamping;
+		qfec_reg_write(priv, TS_CTL_REG,
+			qfec_reg_read(priv, TS_CTL_REG) | TS_CTL_TSENALL);
+
+		return 0;
+	}
+
+	return generic_mii_ioctl(&priv->mii, if_mii(ifr), cmd, NULL);
+}
+
+static struct net_device_stats *qfec_get_stats(struct net_device *dev)
+{
+	struct qfec_priv   *priv = netdev_priv(dev);
+
+	QFEC_LOG(QFEC_LOG_DBG2, "qfec_stats:\n");
+
+	priv->stats.multicast = qfec_reg_read(priv, NUM_MULTCST_FRM_RCVD_G);
+
+	return &priv->stats;
+}
+
+/*
+ * accept new mac address
+ */
+static int qfec_set_mac_address(struct net_device *dev, void *p)
+{
+	struct qfec_priv   *priv = netdev_priv(dev);
+	struct sockaddr    *addr = p;
+
+	QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
+
+	memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+
+	qfec_set_adr_regs(priv, dev->dev_addr);
+
+	return 0;
+}
+
+/*
+ *  read discontinuous MAC address from corrected fuse memory region
+ */
+
+static int qfec_get_mac_address(char *buf, char *mac_base, int nBytes)
+{
+	static int  offset[] = { 0, 1, 2, 3, 4, 8 };
+	int         n;
+
+	QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
+
+	for (n = 0; n < nBytes; n++)
+		buf[n] = ioread8(mac_base + offset[n]);
+
+	/* check that MAC programmed  */
+	if ((buf[0] + buf[1] + buf[2] + buf[3] + buf[4] + buf[5]) == 0)  {
+		QFEC_LOG_ERR("%s: null MAC address\n", __func__);
+		return -ENODATA;
+	}
+
+	return 0;
+}
+
+/*
+ * static definition of driver functions
+ */
+static const struct net_device_ops qfec_netdev_ops = {
+	.ndo_open               = qfec_open,
+	.ndo_stop               = qfec_stop,
+	.ndo_start_xmit         = qfec_xmit,
+
+	.ndo_do_ioctl           = qfec_do_ioctl,
+	.ndo_tx_timeout         = qfec_tx_timeout,
+	.ndo_set_mac_address    = qfec_set_mac_address,
+	.ndo_set_rx_mode        = qfec_set_rx_mode,
+
+	.ndo_change_mtu         = eth_change_mtu,
+	.ndo_validate_addr      = eth_validate_addr,
+
+	.ndo_get_stats          = qfec_get_stats,
+	.ndo_set_config         = qfec_set_config,
+};
+
+/*
+ * ethtool functions
+ */
+
+static int qfec_nway_reset(struct net_device *dev)
+{
+	struct qfec_priv  *priv = netdev_priv(dev);
+	return mii_nway_restart(&priv->mii);
+}
+
+/*
+ * speed, duplex, auto-neg settings
+ */
+static void qfec_ethtool_getpauseparam(struct net_device *dev,
+			struct ethtool_pauseparam *pp)
+{
+	struct qfec_priv  *priv = netdev_priv(dev);
+	u32                flow = qfec_reg_read(priv, FLOW_CONTROL_REG);
+	u32                advert;
+
+	QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
+
+	/* report current settings */
+	pp->tx_pause = (flow & FLOW_CONTROL_TFE) != 0;
+	pp->rx_pause = (flow & FLOW_CONTROL_RFE) != 0;
+
+	/* report if pause is being advertised */
+	advert = qfec_mdio_read(dev, priv->phy_id, MII_ADVERTISE);
+	pp->autoneg =
+		(advert & (ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM)) != 0;
+}
+
+static int qfec_ethtool_setpauseparam(struct net_device *dev,
+			struct ethtool_pauseparam *pp)
+{
+	struct qfec_priv  *priv = netdev_priv(dev);
+	u32                advert;
+
+	QFEC_LOG(QFEC_LOG_DBG, "%s: %d aneg, %d rx, %d tx\n", __func__,
+		pp->autoneg, pp->rx_pause, pp->tx_pause);
+
+	advert  =  qfec_mdio_read(dev, priv->phy_id, MII_ADVERTISE);
+	advert &= ~(ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
+
+	/* If pause autonegotiation is enabled, but both rx and tx are not
+	 * because neither was specified in the ethtool cmd,
+	 * enable both symetrical and asymetrical pause.
+	 * otherwise, only enable the pause mode indicated by rx/tx.
+	 */
+	if (pp->autoneg)  {
+		if (pp->rx_pause)
+			advert |= ADVERTISE_PAUSE_ASYM | ADVERTISE_PAUSE_CAP;
+		else if (pp->tx_pause)
+			advert |= ADVERTISE_PAUSE_ASYM;
+		else
+			advert |= ADVERTISE_PAUSE_CAP;
+	}
+
+	qfec_mdio_write(dev, priv->phy_id, MII_ADVERTISE, advert);
+
+	return 0;
+}
+
+/*
+ * ethtool ring parameter (-g/G) support
+ */
+
+/*
+ * setringparamam - change the tx/rx ring lengths
+ */
+#define MIN_RING_SIZE	3
+#define MAX_RING_SIZE	1000
+static int qfec_ethtool_setringparam(struct net_device *dev,
+	struct ethtool_ringparam *ring)
+{
+	struct qfec_priv  *priv    = netdev_priv(dev);
+	u32                timeout = 20;
+
+	/* notify stack the link is down */
+	netif_carrier_off(dev);
+
+	/* allow tx to complete & free skbufs on the tx ring */
+	do {
+		usleep_range(10000, 100000);
+		qfec_tx_replenish(dev);
+
+		if (timeout-- == 0)  {
+			QFEC_LOG_ERR("%s: timeout\n", __func__);
+			return -ETIME;
+		}
+	} while (!qfec_ring_empty(&priv->ring_tbd));
+
+
+	qfec_stop(dev);
+
+	/* set tx ring size */
+	if (ring->tx_pending < MIN_RING_SIZE)
+		ring->tx_pending = MIN_RING_SIZE;
+	else if (ring->tx_pending > MAX_RING_SIZE)
+		ring->tx_pending = MAX_RING_SIZE;
+	priv->n_tbd = ring->tx_pending;
+
+	/* set rx ring size */
+	if (ring->rx_pending < MIN_RING_SIZE)
+		ring->rx_pending = MIN_RING_SIZE;
+	else if (ring->rx_pending > MAX_RING_SIZE)
+		ring->rx_pending = MAX_RING_SIZE;
+	priv->n_rbd = ring->rx_pending;
+
+
+	qfec_open(dev);
+
+	return 0;
+}
+
+/*
+ * getringparamam - returns local values
+ */
+static void qfec_ethtool_getringparam(struct net_device *dev,
+	struct ethtool_ringparam *ring)
+{
+	struct qfec_priv  *priv = netdev_priv(dev);
+
+	QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
+
+	ring->rx_max_pending       = MAX_RING_SIZE;
+	ring->rx_mini_max_pending  = 0;
+	ring->rx_jumbo_max_pending = 0;
+	ring->tx_max_pending       = MAX_RING_SIZE;
+
+	ring->rx_pending           = priv->n_rbd;
+	ring->rx_mini_pending      = 0;
+	ring->rx_jumbo_pending     = 0;
+	ring->tx_pending           = priv->n_tbd;
+}
+
+/*
+ * speed, duplex, auto-neg settings
+ */
+static int
+qfec_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct qfec_priv  *priv = netdev_priv(dev);
+
+	QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
+
+	cmd->maxrxpkt = priv->n_rbd;
+	cmd->maxtxpkt = priv->n_tbd;
+
+	return mii_ethtool_gset(&priv->mii, cmd);
+}
+
+static int
+qfec_ethtool_setsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct qfec_priv  *priv = netdev_priv(dev);
+
+	QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
+
+	return mii_ethtool_sset(&priv->mii, cmd);
+}
+
+/*
+ * msg/debug level
+ */
+static u32 qfec_ethtool_getmsglevel(struct net_device *dev)
+{
+	return qfec_debug;
+}
+
+static void qfec_ethtool_setmsglevel(struct net_device *dev, u32 level)
+{
+	qfec_debug ^= level;	/* toggle on/off */
+}
+
+/*
+ * register dump
+ */
+#define DMA_DMP_OFFSET  0x0000
+#define DMA_REG_OFFSET  0x1000
+#define DMA_REG_LEN     23
+
+#define MAC_DMP_OFFSET  0x0080
+#define MAC_REG_OFFSET  0x0000
+#define MAC_REG_LEN     55
+
+#define TS_DMP_OFFSET   0x0180
+#define TS_REG_OFFSET   0x0700
+#define TS_REG_LEN      15
+
+#define MDIO_DMP_OFFSET 0x0200
+#define MDIO_REG_LEN    16
+
+#define REG_SIZE    (MDIO_DMP_OFFSET + (MDIO_REG_LEN * sizeof(short)))
+
+static int qfec_ethtool_getregs_len(struct net_device *dev)
+{
+	return REG_SIZE;
+}
+
+static void
+qfec_ethtool_getregs(struct net_device *dev, struct ethtool_regs *regs,
+			 void *buf)
+{
+	struct qfec_priv  *priv   = netdev_priv(dev);
+	u32               *data   = buf;
+	u16               *data16;
+	unsigned int       i;
+	unsigned int       j;
+	unsigned int       n;
+
+	memset(buf, 0, REG_SIZE);
+
+	j = DMA_DMP_OFFSET / sizeof(u32);
+	for (i = DMA_REG_OFFSET, n = DMA_REG_LEN; n--; i += sizeof(u32))
+		data[j++] = htonl(qfec_reg_read(priv, i));
+
+	j = MAC_DMP_OFFSET / sizeof(u32);
+	for (i = MAC_REG_OFFSET, n = MAC_REG_LEN; n--; i += sizeof(u32))
+		data[j++] = htonl(qfec_reg_read(priv, i));
+
+	j = TS_DMP_OFFSET / sizeof(u32);
+	for (i = TS_REG_OFFSET, n = TS_REG_LEN; n--; i += sizeof(u32))
+		data[j++] = htonl(qfec_reg_read(priv, i));
+
+	data16 = (u16 *)&data[MDIO_DMP_OFFSET / sizeof(u32)];
+	for (i = 0, n = 0; i < MDIO_REG_LEN; i++)
+		data16[n++] = htons(qfec_mdio_read(dev, 0, i));
+
+	regs->len     = REG_SIZE;
+
+	QFEC_LOG(QFEC_LOG_DBG, "%s: %d bytes\n", __func__, regs->len);
+}
+
+/*
+ * statistics
+ *   return counts of various ethernet activity.
+ *   many of these are same as in struct net_device_stats
+ *
+ *   missed-frames indicates the number of attempts made by the ethernet
+ *      controller to write to a buffer-descriptor when the BD ownership
+ *      bit was not set.   The rxfifooverflow counter (0x1D4) is not
+ *      available.  The Missed Frame and Buffer Overflow Counter register
+ *      (0x1020) is used, but has only 16-bits and is reset when read.
+ *      It is read and updates the value in priv->stats.rx_missed_errors
+ *      in qfec_rx_int().
+ */
+static char qfec_stats_strings[][ETH_GSTRING_LEN] = {
+	"TX good/bad Bytes         ",
+	"TX Bytes                  ",
+	"TX good/bad Frames        ",
+	"TX Bcast Frames           ",
+	"TX Mcast Frames           ",
+	"TX Unicast Frames         ",
+	"TX Pause Frames           ",
+	"TX Vlan Frames            ",
+	"TX Frames 64              ",
+	"TX Frames 65-127          ",
+	"TX Frames 128-255         ",
+	"TX Frames 256-511         ",
+	"TX Frames 512-1023        ",
+	"TX Frames 1024+           ",
+	"TX Pause Frames           ",
+	"TX Collisions             ",
+	"TX Late Collisions        ",
+	"TX Excessive Collisions   ",
+
+	"RX good/bad Bytes         ",
+	"RX Bytes                  ",
+	"RX good/bad Frames        ",
+	"RX Bcast Frames           ",
+	"RX Mcast Frames           ",
+	"RX Unicast Frames         ",
+	"RX Pause Frames           ",
+	"RX Vlan Frames            ",
+	"RX Frames 64              ",
+	"RX Frames 65-127          ",
+	"RX Frames 128-255         ",
+	"RX Frames 256-511         ",
+	"RX Frames 512-1023        ",
+	"RX Frames 1024+           ",
+	"RX Pause Frames           ",
+	"RX Crc error Frames       ",
+	"RX Length error Frames    ",
+	"RX Alignment error Frames ",
+	"RX Runt Frames            ",
+	"RX Oversize Frames        ",
+	"RX Missed Frames          ",
+
+};
+
+static u32 qfec_stats_regs[] =  {
+
+	     69,     89,     70,     71,     72,     90,     92,     93,
+	     73,     74,     75,     76,     77,     78,     92,     84,
+	     86,     87,
+
+	     97,     98,     96,     99,    100,    113,    116,    118,
+	    107,    108,    109,    110,    111,    112,    116,    101,
+	    114,    102,    103,    106
+};
+
+static int qfec_stats_show(struct device *dev, struct device_attribute *attr,
+				char *buf)
+{
+	struct qfec_priv  *priv = netdev_priv(to_net_dev(dev));
+	int                count = PAGE_SIZE;
+	int                l     = 0;
+	int                n;
+
+	QFEC_LOG(QFEC_LOG_DBG2, "%s:\n", __func__);
+
+	for (n = 0; n < ARRAY_SIZE(qfec_stats_regs); n++)  {
+		l += snprintf(&buf[l], count - l, "      %12u  %s\n",
+			qfec_reg_read(priv,
+				qfec_stats_regs[n] * sizeof(uint32_t)),
+			qfec_stats_strings[n]);
+	}
+
+	return l;
+}
+
+static int qfec_get_sset_count(struct net_device *dev, int sset)
+{
+	switch (sset) {
+	case ETH_SS_STATS:
+		return ARRAY_SIZE(qfec_stats_regs) + 1;	/* missed frames */
+
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static void qfec_ethtool_getstrings(struct net_device *dev, u32 stringset,
+		u8 *buf)
+{
+	QFEC_LOG(QFEC_LOG_DBG, "%s: %d bytes\n", __func__,
+		sizeof(qfec_stats_strings));
+
+	memcpy(buf, qfec_stats_strings, sizeof(qfec_stats_strings));
+}
+
+static void qfec_ethtool_getstats(struct net_device *dev,
+		struct ethtool_stats *stats, uint64_t *data)
+{
+	struct qfec_priv        *priv = netdev_priv(dev);
+	int                      j = 0;
+	int                      n;
+
+	for (n = 0; n < ARRAY_SIZE(qfec_stats_regs); n++)
+		data[j++] = qfec_reg_read(priv,
+				qfec_stats_regs[n] * sizeof(uint32_t));
+
+	data[j++] = priv->stats.rx_missed_errors;
+
+	stats->n_stats = j;
+}
+
+static void qfec_ethtool_getdrvinfo(struct net_device *dev,
+					struct ethtool_drvinfo *info)
+{
+	strlcpy(info->driver,  QFEC_NAME,    sizeof(info->driver));
+	strlcpy(info->version, QFEC_DRV_VER, sizeof(info->version));
+	strlcpy(info->bus_info, dev_name(dev->dev.parent),
+		sizeof(info->bus_info));
+
+	info->eedump_len  = 0;
+	info->regdump_len = qfec_ethtool_getregs_len(dev);
+}
+
+/*
+ * ethtool ops table
+ */
+static const struct ethtool_ops qfec_ethtool_ops = {
+	.nway_reset         = qfec_nway_reset,
+
+	.get_settings       = qfec_ethtool_getsettings,
+	.set_settings       = qfec_ethtool_setsettings,
+	.get_link           = ethtool_op_get_link,
+	.get_drvinfo        = qfec_ethtool_getdrvinfo,
+	.get_msglevel       = qfec_ethtool_getmsglevel,
+	.set_msglevel       = qfec_ethtool_setmsglevel,
+	.get_regs_len       = qfec_ethtool_getregs_len,
+	.get_regs           = qfec_ethtool_getregs,
+
+	.get_ringparam      = qfec_ethtool_getringparam,
+	.set_ringparam      = qfec_ethtool_setringparam,
+
+	.get_pauseparam     = qfec_ethtool_getpauseparam,
+	.set_pauseparam     = qfec_ethtool_setpauseparam,
+
+	.get_sset_count     = qfec_get_sset_count,
+	.get_strings        = qfec_ethtool_getstrings,
+	.get_ethtool_stats  = qfec_ethtool_getstats,
+};
+
+/*
+ *  create sysfs entries
+ */
+static DEVICE_ATTR(bd_tx,   0444, qfec_bd_tx_show,   NULL);
+static DEVICE_ATTR(bd_rx,   0444, qfec_bd_rx_show,   NULL);
+static DEVICE_ATTR(cfg,     0444, qfec_config_show,  NULL);
+static DEVICE_ATTR(clk_reg, 0444, qfec_clk_reg_show, NULL);
+static DEVICE_ATTR(cmd,     0222, NULL,              qfec_cmd);
+static DEVICE_ATTR(cntrs,   0444, qfec_cntrs_show,   NULL);
+static DEVICE_ATTR(reg,     0444, qfec_reg_show,     NULL);
+static DEVICE_ATTR(mdio,    0444, qfec_mdio_show,    NULL);
+static DEVICE_ATTR(stats,   0444, qfec_stats_show,   NULL);
+static DEVICE_ATTR(tstamp,  0444, qfec_tstamp_show,  NULL);
+
+static void qfec_sysfs_create(struct net_device *dev)
+{
+	if (device_create_file(&(dev->dev), &dev_attr_bd_tx) ||
+		device_create_file(&(dev->dev), &dev_attr_bd_rx) ||
+		device_create_file(&(dev->dev), &dev_attr_cfg) ||
+		device_create_file(&(dev->dev), &dev_attr_clk_reg) ||
+		device_create_file(&(dev->dev), &dev_attr_cmd) ||
+		device_create_file(&(dev->dev), &dev_attr_cntrs) ||
+		device_create_file(&(dev->dev), &dev_attr_mdio) ||
+		device_create_file(&(dev->dev), &dev_attr_reg) ||
+		device_create_file(&(dev->dev), &dev_attr_stats) ||
+		device_create_file(&(dev->dev), &dev_attr_tstamp))
+		pr_err("qfec_sysfs_create failed to create sysfs files\n");
+}
+
+/*
+ * map a specified resource
+ */
+static int qfec_map_resource(struct platform_device *plat, int resource,
+	struct resource **priv_res,
+	void                   **addr)
+{
+	struct resource         *res;
+
+	QFEC_LOG(QFEC_LOG_DBG, "%s: 0x%x resource\n", __func__, resource);
+
+	/* allocate region to access controller registers */
+	*priv_res = res = platform_get_resource(plat, resource, 0);
+	if (!res) {
+		QFEC_LOG_ERR("%s: platform_get_resource failed\n", __func__);
+		return -ENODEV;
+	}
+
+	res = request_mem_region(res->start, res->end - res->start, QFEC_NAME);
+	if (!res) {
+		QFEC_LOG_ERR("%s: request_mem_region failed, %08x %08x\n",
+			__func__, res->start, res->end - res->start);
+		return -EBUSY;
+	}
+
+	*addr = ioremap(res->start, res->end - res->start);
+	if (!*addr)
+		return -ENOMEM;
+
+	QFEC_LOG(QFEC_LOG_DBG, " %s: io mapped from %p to %p\n",
+		__func__, (void *)res->start, *addr);
+
+	return 0;
+};
+
+/*
+ * free allocated io regions
+ */
+static void qfec_free_res(struct resource *res, void *base)
+{
+
+	if (res)  {
+		if (base)
+			iounmap((void __iomem *)base);
+
+		release_mem_region(res->start, res->end - res->start);
+	}
+};
+
+/*
+ * probe function that obtain configuration info and allocate net_device
+ */
+static int __devinit qfec_probe(struct platform_device *plat)
+{
+	struct net_device  *dev;
+	struct qfec_priv   *priv;
+	int                 ret = 0;
+
+	/* allocate device */
+	dev = alloc_etherdev(sizeof(struct qfec_priv));
+	if (!dev) {
+		QFEC_LOG_ERR("%s: alloc_etherdev failed\n", __func__);
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	QFEC_LOG(QFEC_LOG_DBG, "%s: %08x dev\n",      __func__, (int)dev);
+
+	qfec_dev = dev;
+	SET_NETDEV_DEV(dev, &plat->dev);
+
+	dev->netdev_ops      = &qfec_netdev_ops;
+	dev->ethtool_ops     = &qfec_ethtool_ops;
+	dev->watchdog_timeo  = 2 * HZ;
+	dev->irq             = platform_get_irq(plat, 0);
+
+	dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+
+	/* initialize private data */
+	priv = (struct qfec_priv *)netdev_priv(dev);
+	memset((void *)priv, 0, sizeof(priv));
+
+	priv->net_dev   = dev;
+	platform_set_drvdata(plat, dev);
+
+	priv->n_tbd     = TX_BD_NUM;
+	priv->n_rbd     = RX_BD_NUM;
+
+	/* initialize phy structure */
+	priv->mii.phy_id_mask   = 0x1F;
+	priv->mii.reg_num_mask  = 0x1F;
+	priv->mii.dev           = dev;
+	priv->mii.mdio_read     = qfec_mdio_read;
+	priv->mii.mdio_write    = qfec_mdio_write;
+
+	/* map register regions */
+	ret = qfec_map_resource(
+		plat, IORESOURCE_MEM, &priv->mac_res, &priv->mac_base);
+	if (ret)  {
+		QFEC_LOG_ERR("%s: IORESOURCE_MEM mac failed\n", __func__);
+		goto err1;
+	}
+
+	ret = qfec_map_resource(
+		plat, IORESOURCE_IO, &priv->clk_res, &priv->clk_base);
+	if (ret)  {
+		QFEC_LOG_ERR("%s: IORESOURCE_IO clk failed\n", __func__);
+		goto err2;
+	}
+
+	ret = qfec_map_resource(
+		plat, IORESOURCE_DMA, &priv->fuse_res, &priv->fuse_base);
+	if (ret)  {
+		QFEC_LOG_ERR("%s: IORESOURCE_DMA fuse failed\n", __func__);
+		goto err3;
+	}
+
+	/* initialize MAC addr */
+	ret = qfec_get_mac_address(dev->dev_addr, priv->fuse_base,
+		MAC_ADDR_SIZE);
+	if (ret)
+		goto err4;
+
+	QFEC_LOG(QFEC_LOG_DBG, "%s: mac  %02x:%02x:%02x:%02x:%02x:%02x\n",
+		__func__,
+		dev->dev_addr[0], dev->dev_addr[1],
+		dev->dev_addr[2], dev->dev_addr[3],
+		dev->dev_addr[4], dev->dev_addr[5]);
+
+	ret = register_netdev(dev);
+	if (ret)  {
+		QFEC_LOG_ERR("%s: register_netdev failed\n", __func__);
+		goto err4;
+	}
+
+	spin_lock_init(&priv->mdio_lock);
+	spin_lock_init(&priv->xmit_lock);
+	qfec_sysfs_create(dev);
+
+	return 0;
+
+	/* error handling */
+err4:
+	qfec_free_res(priv->fuse_res, priv->fuse_base);
+err3:
+	qfec_free_res(priv->clk_res, priv->clk_base);
+err2:
+	qfec_free_res(priv->mac_res, priv->mac_base);
+err1:
+	free_netdev(dev);
+err:
+	QFEC_LOG_ERR("%s: err\n", __func__);
+	return ret;
+}
+
+/*
+ * module remove
+ */
+static int __devexit qfec_remove(struct platform_device *plat)
+{
+	struct net_device  *dev  = platform_get_drvdata(plat);
+	struct qfec_priv   *priv = netdev_priv(dev);
+
+	QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
+
+	platform_set_drvdata(plat, NULL);
+
+	qfec_free_res(priv->fuse_res, priv->fuse_base);
+	qfec_free_res(priv->clk_res, priv->clk_base);
+	qfec_free_res(priv->mac_res, priv->mac_base);
+
+	unregister_netdev(dev);
+	free_netdev(dev);
+
+	return 0;
+}
+
+/*
+ * module support
+ *     the FSM9xxx is not a mobile device does not support power management
+ */
+
+static struct platform_driver qfec_driver = {
+	.probe  = qfec_probe,
+	.remove = __devexit_p(qfec_remove),
+	.driver = {
+		.name   = QFEC_NAME,
+		.owner  = THIS_MODULE,
+	},
+};
+
+/*
+ * module init
+ */
+static int __init qfec_init_module(void)
+{
+	int  res;
+
+	QFEC_LOG(QFEC_LOG_DBG, "%s: %s\n", __func__, qfec_driver.driver.name);
+
+	res = platform_driver_register(&qfec_driver);
+
+	QFEC_LOG(QFEC_LOG_DBG, "%s: %d - platform_driver_register\n",
+		__func__, res);
+
+	return  res;
+}
+
+/*
+ * module exit
+ */
+static void __exit qfec_exit_module(void)
+{
+	QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
+
+	platform_driver_unregister(&qfec_driver);
+}
+
+MODULE_DESCRIPTION("FSM Network Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Rohit Vaswani <rvaswani@codeaurora.org>");
+MODULE_VERSION("1.0");
+
+module_init(qfec_init_module);
+module_exit(qfec_exit_module);
diff --git a/drivers/net/ethernet/msm/qfec.h b/drivers/net/ethernet/msm/qfec.h
new file mode 100644
index 0000000..310406a
--- /dev/null
+++ b/drivers/net/ethernet/msm/qfec.h
@@ -0,0 +1,800 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/* qualcomm fast Ethernet controller HW description */
+
+#ifndef _QFEC_EMAC_H_
+# define _QFEC_EMAC_H_
+
+# ifndef __KERNEL__
+#   include "stdint.h"
+# endif
+
+# define MskBits(nBits, pos)     (((1 << nBits)-1)<<pos)
+
+/* Rx/Tx Ethernet Buffer Descriptors
+ *     status contains the ownership, status and receive length bits
+ *     ctl    contains control and size bits for two buffers
+ *     p_buf  contains a ptr to the data buffer
+ *            MAC writes timestamp low into p_buf
+ *     next   contains either ptr to 2nd buffer or next buffer-desc
+ *            MAC writes timestamp high into next
+ *
+ *     status/ctl bit definition depend on RX or TX usage
+ */
+
+
+struct qfec_buf_desc {
+	uint32_t            status;
+	uint32_t            ctl;
+	void               *p_buf;
+	void               *next;
+};
+
+/* ownership bit operations */
+# define BUF_OWN                     0x80000000 /* DMA owns buffer */
+# define BUF_OWN_DMA                 BUF_OWN
+
+/* RX buffer status bits */
+# define BUF_RX_AFM               0x40000000 /* dest addr filt fail */
+
+# define BUF_RX_FL                0x3fff0000 /* frame length */
+# define BUF_RX_FL_GET(p)         ((p.status & BUF_RX_FL) >> 16)
+# define BUF_RX_FL_SET(p, x) \
+	(p.status = (p.status & ~BUF_RX_FL) | ((x << 16) & BUF_RX_FL))
+# define BUF_RX_FL_GET_FROM_STATUS(status) \
+				  (((status) & BUF_RX_FL) >> 16)
+
+# define BUF_RX_ES                0x00008000 /* error summary */
+# define BUF_RX_DE                0x00004000 /* error descriptor (es) */
+# define BUF_RX_SAF               0x00002000 /* source addr filt fail */
+# define BUF_RX_LE                0x00001000 /* length error */
+
+# define BUF_RX_OE                0x00000800 /* overflow error (es) */
+# define BUF_RX_VLAN              0x00000400 /* vlan tag */
+# define BUF_RX_FS                0x00000200 /* first descriptor */
+# define BUF_RX_LS                0x00000100 /* last  descriptor */
+
+# define BUF_RX_IPC               0x00000080 /* cksum-err/giant-frame (es) */
+# define BUF_RX_LC                0x00000040 /* late collision (es) */
+# define BUF_RX_FT                0x00000020 /* frame type */
+# define BUF_RX_RWT               0x00000010 /* rec watchdog timeout (es) */
+
+# define BUF_RX_RE                0x00000008 /* rec error (es) */
+# define BUF_RX_DBE               0x00000004 /* dribble bit err */
+# define BUF_RX_CE                0x00000002 /* crc err (es) */
+# define BUF_RX_CSE               0x00000001 /* checksum err */
+
+# define BUF_RX_ERRORS  \
+	(BUF_RX_DE  | BUF_RX_SAF | BUF_RX_LE  | BUF_RX_OE \
+	| BUF_RX_IPC | BUF_RX_LC  | BUF_RX_RWT | BUF_RX_RE \
+	| BUF_RX_DBE | BUF_RX_CE  | BUF_RX_CSE)
+
+/* RX buffer control bits */
+# define BUF_RX_DI                0x80000000 /* disable intrp on compl */
+# define BUF_RX_RER               0x02000000 /* rec end of ring */
+# define BUF_RX_RCH               0x01000000 /* 2nd addr chained */
+
+# define BUF_RX_SIZ2              0x003ff800 /* buffer 2 size */
+# define BUF_RX_SIZ2_GET(p)       ((p.control&BUF_RX_SIZ2) >> 11)
+
+# define BUF_RX_SIZ               0x000007ff /* rx buf 1 size */
+# define BUF_RX_SIZ_GET(p)        (p.ctl&BUF_RX_SIZ)
+
+/* TX buffer status bits */
+# define BUF_TX_TTSS              0x00020000 /* time stamp status */
+# define BUF_TX_IHE               0x00010000 /* IP hdr err */
+
+# define BUF_TX_ES                0x00008000 /* error summary */
+# define BUF_TX_JT                0x00004000 /* jabber timeout (es) */
+# define BUF_TX_FF                0x00002000 /* frame flushed (es) */
+# define BUF_TX_PCE               0x00001000 /* payld cksum err */
+
+# define BUF_TX_LOC               0x00000800 /* loss carrier (es) */
+# define BUF_TX_NC                0x00000400 /* no carrier (es) */
+# define BUF_TX_LC                0x00000200 /* late collision (es) */
+# define BUF_TX_EC                0x00000100 /* excessive collision (es) */
+
+# define BUF_TX_VLAN              0x00000080 /* VLAN frame */
+# define BUF_TX_CC                MskBits(4, 3) /* collision count */
+# define BUF_TX_CC_GET(p)         ((p.status&BUF_TX_CC)>>3)
+
+# define BUF_TX_ED                0x00000004 /* excessive deferral (es) */
+# define BUF_TX_UF                0x00000002 /* underflow err (es) */
+# define BUF_TX_DB                0x00000001 /* deferred bit */
+
+/* TX buffer control bits */
+# define BUF_TX_IC                0x80000000 /* intrpt on compl */
+# define BUF_TX_LS                0x40000000 /* last segment */
+# define BUF_TX_FS                0x20000000 /* first segment */
+# define BUF_TX_CIC               0x18000000 /* cksum insert control */
+# define BUF_TX_CIC_SET(n)        (BUF_TX_CIC&(n<<27))
+
+# define BUF_TX_DC                0x04000000 /* disable CRC */
+# define BUF_TX_TER               0x02000000 /* end of ring */
+# define BUF_TX_TCH               0x01000000 /* 2nd addr chained */
+
+# define BUF_TX_DP                0x00800000 /* disable padding */
+# define BUF_TX_TTSE              0x00400000 /* timestamp enable */
+
+# define BUF_TX_SIZ2              0x003ff800 /* buffer 2 size */
+# define BUF_TX_SIZ2_SET(n)       (BUF_TX_SIZ2(n<<11))
+
+# define BUF_TX_SIZ               0x000007ff /* buffer 1 size */
+# define BUF_TX_SIZ_SET(n)        (BUF_TX_SI1 & n)
+
+
+/* Ethernet Controller Registers */
+# define BUS_MODE_REG             0x1000
+
+# define BUS_MODE_MB              0x04000000  /* mixed burst */
+# define BUS_MODE_AAL             0x02000000  /* address alignment beats */
+# define BUS_MODE_8XPBL           0x01000000  /*  */
+
+# define BUS_MODE_USP             0x00800000  /* use separate PBL */
+# define BUS_MODE_RPBL            0x007e0000  /* rxDMA PBL */
+# define BUS_MODE_FB              0x00010000  /* fixed burst */
+
+# define BUS_MODE_PR              0x0000c000  /* tx/rx priority */
+# define BUS_MODE_PR4             0x0000c000  /* tx/rx priority 4:1 */
+# define BUS_MODE_PR3             0x00008000  /* tx/rx priority 3:1 */
+# define BUS_MODE_PR2             0x00004000  /* tx/rx priority 2:1 */
+# define BUS_MODE_PR1             0x00000000  /* tx/rx priority 1:1 */
+
+# define BUS_MODE_PBL             0x00003f00  /* programmable burst length */
+# define BUS_MODE_PBLSET(n)       (BUS_MODE_PBL&(n<<8))
+
+# define BUS_MODE_DSL             0x0000007c  /* descriptor skip length */
+# define BUS_MODE_DSL_SET(n)      (BUS_MODE_DSL & (n << 2))
+
+# define BUS_MODE_DA              0x00000002  /* DMA arbitration scheme  */
+# define BUS_MODE_SWR             0x00000001  /* software reset */
+
+#define BUS_MODE_REG_DEFAULT     (BUS_MODE_FB \
+				| BUS_MODE_AAL \
+				| BUS_MODE_PBLSET(16) \
+				| BUS_MODE_DA \
+				| BUS_MODE_DSL_SET(0))
+
+# define TX_POLL_DEM_REG          0x1004      /* transmit poll demand */
+# define RX_POLL_DEM_REG          0x1008      /* receive poll demand */
+
+# define RX_DES_LST_ADR_REG       0x100c      /* receive buffer descriptor */
+# define TX_DES_LST_ADR_REG       0x1010      /* transmit buffer descriptor */
+
+# define STATUS_REG               0x1014
+
+# define STATUS_REG_RSVRD_1       0xc0000000  /* reserved */
+# define STATUS_REG_TTI           0x20000000  /* time-stamp trigger intrpt */
+# define STATUS_REG_GPI           0x10000000  /* gmac PMT interrupt */
+
+# define STATUS_REG_GMI           0x08000000  /* gmac MMC interrupt */
+# define STATUS_REG_GLI           0x04000000  /* gmac line interface intrpt */
+
+# define STATUS_REG_EB            0x03800000  /* error bits */
+# define STATUS_REG_EB_DATA       0x00800000  /* error during data transfer */
+# define STATUS_REG_EB_RDWR       0x01000000  /* error during rd/wr transfer */
+# define STATUS_REG_EB_DESC       0x02000000  /* error during desc access */
+
+# define STATUS_REG_TS            0x00700000  /* transmit process state */
+
+# define STATUS_REG_TS_STOP       0x00000000  /*   stopped */
+# define STATUS_REG_TS_FETCH_DESC 0x00100000  /*   fetching descriptor */
+# define STATUS_REG_TS_WAIT       0x00200000  /*   waiting for status */
+# define STATUS_REG_TS_READ       0x00300000  /*   reading host memory */
+# define STATUS_REG_TS_TIMESTAMP  0x00400000  /*   timestamp write status */
+# define STATUS_REG_TS_RSVRD      0x00500000  /*   reserved */
+# define STATUS_REG_TS_SUSPEND    0x00600000  /*   desc-unavail/buffer-unflw */
+# define STATUS_REG_TS_CLOSE      0x00700000  /*   closing desc */
+
+# define STATUS_REG_RS            0x000e0000  /* receive process state */
+
+# define STATUS_REG_RS_STOP       0x00000000  /*   stopped */
+# define STATUS_REG_RS_FETCH_DESC 0x00020000  /*   fetching descriptor */
+# define STATUS_REG_RS_RSVRD_1    0x00040000  /*   reserved */
+# define STATUS_REG_RS_WAIT       0x00060000  /*   waiting for packet */
+# define STATUS_REG_RS_SUSPEND    0x00080000  /*   desc unavail */
+# define STATUS_REG_RS_CLOSE      0x000a0000  /*   closing desc */
+# define STATUS_REG_RS_TIMESTAMP  0x000c0000  /*   timestamp write status */
+# define STATUS_REG_RS_RSVRD_2    0x000e0000  /*   writing host memory */
+
+# define STATUS_REG_NIS           0x00010000  /* normal intrpt   14|6|2|0 */
+# define STATUS_REG_AIS           0x00008000  /* intrpts 13|10|9|8|7|5|4|3|1 */
+
+# define STATUS_REG_ERI           0x00004000  /* early receive interrupt */
+# define STATUS_REG_FBI           0x00002000  /* fatal bus error interrupt */
+# define STATUS_REG_RSVRD_2       0x00001800  /* reserved */
+
+# define STATUS_REG_ETI           0x00000400  /* early transmit interrupt */
+# define STATUS_REG_RWT           0x00000200  /* receive watchdog timeout */
+# define STATUS_REG_RPS           0x00000100  /* receive process stopped */
+
+# define STATUS_REG_RU            0x00000080  /* receive buffer unavailable */
+# define STATUS_REG_RI            0x00000040  /* receive interrupt */
+# define STATUS_REG_UNF           0x00000020  /* transmit underflow */
+# define STATUS_REG_OVF           0x00000010  /* receive overflow */
+
+# define STATUS_REG_TJT           0x00000008  /* transmit jabber timeout */
+# define STATUS_REG_TU            0x00000004  /* transmit buffer unavailable */
+# define STATUS_REG_TPS           0x00000002  /* transmit process stopped */
+# define STATUS_REG_TI            0x00000001  /* transmit interrupt */
+
+# define STATUS_REG_AIS_BITS    (STATUS_REG_FBI | STATUS_REG_ETI \
+				| STATUS_REG_RWT | STATUS_REG_RPS \
+				| STATUS_REG_RU | STATUS_REG_UNF \
+				| STATUS_REG_OVF | STATUS_REG_TJT \
+				| STATUS_REG_TPS | STATUS_REG_AIS)
+
+# define OPER_MODE_REG             0x1018
+
+# define OPER_MODE_REG_DT          0x04000000 /* disab drop ip cksum err fr */
+# define OPER_MODE_REG_RSF         0x02000000 /* rec store and forward */
+# define OPER_MODE_REG_DFF         0x01000000 /* disable flush of rec frames */
+
+# define OPER_MODE_REG_RFA2        0x00800000 /* thresh MSB for act flow-ctl */
+# define OPER_MODE_REG_RFD2        0x00400000 /* thresh MSB deAct flow-ctl */
+# define OPER_MODE_REG_TSF         0x00200000 /* tx store and forward */
+# define OPER_MODE_REG_FTF         0x00100000 /* flush tx FIFO */
+
+# define OPER_MODE_REG_RSVD1       0x000e0000 /* reserved */
+# define OPER_MODE_REG_TTC         0x0001c000 /* transmit threshold control */
+# define OPER_MODE_REG_TTC_SET(x)  (OPER_MODE_REG_TTC & (x << 14))
+# define OPER_MODE_REG_ST          0x00002000 /* start/stop transmission cmd */
+
+# define OPER_MODE_REG_RFD         0x00001800 /* thresh for deAct flow-ctl */
+# define OPER_MODE_REG_RFA         0x00000600 /* threshold for act flow-ctl */
+# define OPER_MODE_REG_EFC         0x00000100 /* enable HW flow-ctl */
+
+# define OPER_MODE_REG_FEF         0x00000080 /* forward error frames */
+# define OPER_MODE_REG_FUF         0x00000040 /* forward undersize good fr */
+# define OPER_MODE_REG_RSVD2       0x00000020 /* reserved */
+# define OPER_MODE_REG_RTC         0x00000018 /* receive threshold control */
+# define OPER_MODE_REG_RTC_SET(x)  (OPER_MODE_REG_RTC & (x << 3))
+
+# define OPER_MODE_REG_OSF         0x00000004 /* operate on second frame */
+# define OPER_MODE_REG_SR          0x00000002 /* start/stop receive */
+# define OPER_MODE_REG_RSVD3       0x00000001 /* reserved */
+
+
+#define OPER_MODE_REG_DEFAULT    (OPER_MODE_REG_RSF \
+				| OPER_MODE_REG_TSF \
+				| OPER_MODE_REG_TTC_SET(5) \
+				| OPER_MODE_REG_RTC_SET(1) \
+				| OPER_MODE_REG_OSF)
+
+# define INTRP_EN_REG              0x101c
+
+# define INTRP_EN_REG_RSVD1        0xfffc0000 /* */
+# define INTRP_EN_REG_NIE          0x00010000 /* normal intrpt summ enable */
+
+# define INTRP_EN_REG_AIE          0x00008000 /* abnormal intrpt summary en */
+# define INTRP_EN_REG_ERE          0x00004000 /* early receive intrpt enable */
+# define INTRP_EN_REG_FBE          0x00002000 /* fatal bus error enable */
+
+# define INTRP_EN_REG_RSVD2        0x00001800 /* */
+
+# define INTRP_EN_REG_ETE          0x00000400 /* early tx intrpt enable */
+# define INTRP_EN_REG_RWE          0x00000200 /* rx watchdog timeout enable */
+# define INTRP_EN_REG_RSE          0x00000100 /* rx stopped enable */
+
+# define INTRP_EN_REG_RUE          0x00000080 /* rx buf unavailable enable */
+# define INTRP_EN_REG_RIE          0x00000040 /* rx interrupt enable */
+# define INTRP_EN_REG_UNE          0x00000020 /* underflow interrupt enable */
+# define INTRP_EN_REG_OVE          0x00000010 /* overflow interrupt enable */
+
+# define INTRP_EN_REG_TJE          0x00000008 /* tx jabber timeout enable */
+# define INTRP_EN_REG_TUE          0x00000004 /* tx buf unavailable enable */
+# define INTRP_EN_REG_TSE          0x00000002 /* tx stopped enable */
+# define INTRP_EN_REG_TIE          0x00000001 /* tx interrupt enable */
+
+# define INTRP_EN_REG_All          (~(INTRP_EN_REG_RSVD1))
+
+# define MIS_FR_REG                0x1020
+
+# define MIS_FR_REG_FIFO_OVFL      0x10000000  /* fifo overflow */
+# define MIS_FR_REG_FIFO_CNT       0x0FFE0000  /* fifo cnt */
+
+# define MIS_FR_REG_MISS_OVFL      0x00010000  /* missed-frame overflow */
+# define MIS_FR_REG_MISS_CNT       0x0000FFFF  /* missed-frame cnt */
+
+# define RX_INTRP_WTCHDOG_REG      0x1024
+# define AXI_BUS_MODE_REG          0x1028
+
+# define AXI_BUS_MODE_EN_LPI       0x80000000  /* enable low power interface */
+# define AXI_BUS_MODE_UNLK_MGC_PKT 0x40000000  /* unlock-magic-pkt/rem-wk-up */
+# define AXI_BUS_MODE_WR_OSR_LMT   0x00F00000  /* max wr out stndg req limit */
+# define AXI_BUS_MODE_RD_OSR_LMT   0x000F0000  /* max rd out stndg req limit */
+# define AXI_BUS_MODE_AXI_AAL      0x00001000  /* address aligned beats */
+# define AXI_BUS_MODE_BLEN256      0x00000080  /* axi burst length 256 */
+# define AXI_BUS_MODE_BLEN128      0x00000040  /* axi burst length 128 */
+# define AXI_BUS_MODE_BLEN64       0x00000020  /* axi burst length 64  */
+# define AXI_BUS_MODE_BLEN32       0x00000010  /* axi burst length 32  */
+# define AXI_BUS_MODE_BLEN16       0x00000008  /* axi burst length 16  */
+# define AXI_BUS_MODE_BLEN8        0x00000004  /* axi burst length 8   */
+# define AXI_BUS_MODE_BLEN4        0x00000002  /* axi burst length 4   */
+# define AXI_BUS_MODE_UNDEF        0x00000001  /* axi undef burst length */
+
+#define AXI_BUS_MODE_DEFAULT     (AXI_BUS_MODE_WR_OSR_LMT \
+				| AXI_BUS_MODE_RD_OSR_LMT \
+				| AXI_BUS_MODE_BLEN16 \
+				| AXI_BUS_MODE_BLEN8 \
+				| AXI_BUS_MODE_BLEN4)
+
+# define AXI_STATUS_REG            0x102c
+
+/*     0x1030-0x1044 reserved */
+# define CUR_HOST_TX_DES_REG       0x1048
+# define CUR_HOST_RX_DES_REG       0x104c
+# define CUR_HOST_TX_BU_ADR_REG    0x1050
+# define CUR_HOST_RX_BU_ADR_REG    0x1054
+
+# define HW_FEATURE_REG            0x1058
+
+# define MAC_CONFIG_REG            0x0000
+
+# define MAC_CONFIG_REG_RSVD1      0xf8000000 /* */
+
+# define MAC_CONFIG_REG_SFTERR     0x04000000 /* smii force tx error */
+# define MAC_CONFIG_REG_CST        0x02000000 /* crc strip for type frame */
+# define MAC_CONFIG_REG_TC         0x01000000 /* tx cfg in rgmii/sgmii/smii */
+
+# define MAC_CONFIG_REG_WD         0x00800000 /* watchdog disable */
+# define MAC_CONFIG_REG_JD         0x00400000 /* jabber disable */
+# define MAC_CONFIG_REG_BE         0x00200000 /* frame burst enable */
+# define MAC_CONFIG_REG_JE         0x00100000 /* jumbo frame enable */
+
+# define MAC_CONFIG_REG_IFG        0x000e0000 /* inter frame gap, 96-(8*n) */
+# define MAC_CONFIG_REG_DCRS       0x00010000 /* dis carrier sense during tx */
+
+# define MAC_CONFIG_REG_PS         0x00008000 /* port select: 0/1 g/(10/100) */
+# define MAC_CONFIG_REG_FES        0x00004000 /* speed 100 mbps */
+# define MAC_CONFIG_REG_SPD        (MAC_CONFIG_REG_PS | MAC_CONFIG_REG_FES)
+# define MAC_CONFIG_REG_SPD_1G     (0)
+# define MAC_CONFIG_REG_SPD_100    (MAC_CONFIG_REG_PS | MAC_CONFIG_REG_FES)
+# define MAC_CONFIG_REG_SPD_10     (MAC_CONFIG_REG_PS)
+# define MAC_CONFIG_REG_SPD_SET(x) (MAC_CONFIG_REG_PS_FES & (x << 14))
+
+# define MAC_CONFIG_REG_DO         0x00002000 /* disable receive own */
+# define MAC_CONFIG_REG_LM         0x00001000 /* loopback mode */
+
+# define MAC_CONFIG_REG_DM         0x00000800 /* (full) duplex mode */
+# define MAC_CONFIG_REG_IPC        0x00000400 /* checksum offload */
+# define MAC_CONFIG_REG_DR         0x00000200 /* disable retry */
+# define MAC_CONFIG_REG_LUD        0x00000100 /* link up/down */
+
+# define MAC_CONFIG_REG_ACS        0x00000080 /* auto pad/crc stripping */
+# define MAC_CONFIG_REG_BL         0x00000060 /* back-off limit */
+# define MAC_CONFIG_REG_BL_10      0x00000000 /*          10 */
+# define MAC_CONFIG_REG_BL_8       0x00000020 /*          8  */
+# define MAC_CONFIG_REG_BL_4       0x00000040 /*          4  */
+# define MAC_CONFIG_REG_BL_1       0x00000060 /*          1  */
+# define MAC_CONFIG_REG_DC         0x00000010 /* deferral check */
+
+# define MAC_CONFIG_REG_TE         0x00000008 /* transmitter enable */
+# define MAC_CONFIG_REG_RE         0x00000004 /* receiver enable */
+# define MAC_CONFIG_REG_RSVD2      0x00000003 /* */
+
+# define MAC_FR_FILTER_REG         0x0004
+
+# define MAC_FR_FILTER_RA          0x80000000 /* receive all */
+
+# define MAC_FR_FILTER_HPF         0x00000400 /* hash or perfect filter */
+# define MAC_FR_FILTER_SAF         0x00000200 /* source addr filt en */
+# define MAC_FR_FILTER_SAIF        0x00000100 /* SA inverse filter */
+# define MAC_FR_FILTER_PCF_MASK    0x000000c0 /* pass control frames */
+# define MAC_FR_FILTER_PCF_0       0x00000000 /*    */
+# define MAC_FR_FILTER_PCF_1       0x00000040 /*    */
+# define MAC_FR_FILTER_PCF_2       0x00000080 /*    */
+# define MAC_FR_FILTER_PCF_3       0x000000c0 /*    */
+# define MAC_FR_FILTER_DBF         0x00000020 /* disable broadcast frames */
+# define MAC_FR_FILTER_PM          0x00000010 /* pass all multicast */
+# define MAC_FR_FILTER_DAIF        0x00000008 /* DA inverse filtering */
+# define MAC_FR_FILTER_HMC         0x00000004 /* hash multicast */
+# define MAC_FR_FILTER_HUC         0x00000002 /* hash unicast */
+# define MAC_FR_FILTER_PR          0x00000001 /* promiscuous mode */
+
+# define HASH_TABLE_HIGH_REG       0x0008
+# define HASH_TABLE_LOW_REG        0x000c
+
+# define GMII_ADR_REG              0x0010
+
+# define GMII_ADR_REG_PA           0x0000f800 /* addr bits */
+# define GMII_ADR_REG_GR           0x000007c0 /* addr bits */
+# define GMII_ADR_REG_RSVRD1       0x00000020 /* */
+# define GMII_ADR_REG_CR           0x0000001c /* csr clock range */
+# define GMII_ADR_REG_GW           0x00000002 /* gmii write */
+# define GMII_ADR_REG_GB           0x00000001 /* gmii busy */
+
+# define GMII_ADR_REG_ADR_SET(x)    (GMII_ADR_REG_PA & (x << 11))
+# define GMII_ADR_REG_ADR_GET(x)    ((x & GMII_ADR_REG_PA) >> 11)
+
+# define GMII_ADR_REG_REG_SET(x)    (GMII_ADR_REG_GR & (x << 6))
+# define GMII_ADR_REG_REG_GET(x)    (((x & GMII_ADR_REG_GR) >> 6)
+
+# define GMII_ADR_REG_CSR_SET(x)    (GMII_ADR_REG_CR & (x << 2))
+# define GMII_ADR_REG_CSR_GET(x)    (((x & GMII_ADR_REG_CR) >> 2)
+
+# define GMII_DATA_REG             0x0014
+
+# define GMII_DATA_REG_DATA        0x0000ffff /* gmii data */
+
+# define FLOW_CONTROL_REG          0x0018
+
+# define FLOW_CONTROL_PT           0xFFFF0000 /* pause time */
+# define FLOW_CONTROL_DZPQ         0x00000080 /* disable zero-quanta pause */
+# define FLOW_CONTROL_PLT          0x00000030 /* pause level threshold */
+
+# define FLOW_CONTROL_UP           0x00000008 /* unicast pause frame detect */
+# define FLOW_CONTROL_RFE          0x00000004 /* receive flow control enable */
+# define FLOW_CONTROL_TFE          0x00000002 /* transmit flow control enable */
+# define FLOW_CONTROL_FCB          0x00000001 /* flow control busy (BPA) */
+
+# define VLAN_TAG_REG              0x001c
+
+# define VERSION_REG               0x0020
+
+/* don't define these until HW if finished */
+/* # define VERSION_USER              0x10 */
+/* # define VERSION_QFEC              0x36 */
+
+# define VERSION_REG_USER(x)       (0xFF & (x >> 8))
+# define VERSION_REG_QFEC(x)       (0xFF & x)
+
+# define DEBUG_REG                 0x0024
+
+# define DEBUG_REG_RSVD1           0xfc000000 /* */
+# define DEBUG_REG_TX_FIFO_FULL    0x02000000 /* Tx fifo full */
+# define DEBUG_REG_TX_FIFO_NEMP    0x01000000 /* Tx fifo not empty */
+
+# define DEBUG_REG_RSVD2           0x00800000 /* */
+# define DEBUG_REG_TX_WR_ACTIVE    0x00400000 /* Tx fifo write ctrl active */
+
+# define DEBUG_REG_TX_RD_STATE     0x00300000 /* Tx fifo rd ctrl state */
+# define DEBUG_REG_TX_RD_IDLE      0x00000000 /*         idle */
+# define DEBUG_REG_TX_RD_WAIT      0x00100000 /*         waiting for status */
+# define DEBUG_REG_TX_RD_PASUE     0x00200000 /*         generating pause */
+# define DEBUG_REG_TX_RD_WRTG      0x00300000 /*         wr stat flush fifo */
+
+# define DEBUG_REG_TX_PAUSE        0x00080000 /* Tx in pause condition */
+
+# define DEBUG_REG_TX_CTRL_STATE   0x00060000 /* Tx frame controller state */
+# define DEBUG_REG_TX_CTRL_IDLE    0x00090000 /*         idle */
+# define DEBUG_REG_TX_CTRL_WAIT    0x00020000 /*         waiting for status*/
+# define DEBUG_REG_TX_CTRL_PAUSE   0x00040000 /*         generating pause */
+# define DEBUG_REG_TX_CTRL_XFER    0x00060000 /*         transferring input */
+
+# define DEBUG_REG_TX_ACTIVE       0x00010000 /* Tx actively transmitting */
+# define DEBUG_REG_RSVD3           0x0000fc00 /* */
+
+# define DEBUG_REG_RX_STATE        0x00000300 /* Rx fifo state */
+# define DEBUG_REG_RX_EMPTY        0x00000000 /*         empty */
+# define DEBUG_REG_RX_LOW          0x00000100 /*         below threshold */
+# define DEBUG_REG_RX_HIGH         0x00000200 /*         above threshold */
+# define DEBUG_REG_RX_FULL         0x00000300 /*         full */
+
+# define DEBUG_REG_RSVD4           0x00000080 /* */
+
+# define DEBUG_REG_RX_RD_STATE     0x00000060 /* Rx rd ctrl state */
+# define DEBUG_REG_RX_RD_IDLE      0x00000000 /*         idle */
+# define DEBUG_REG_RX_RD_RDG_FR    0x00000020 /*         reading frame data */
+# define DEBUG_REG_RX_RD_RDG_STA   0x00000040 /*         reading status */
+# define DEBUG_REG_RX_RD_FLUSH     0x00000060 /*         flush fr data/stat */
+
+# define DEBUG_REG_RX_ACTIVE       0x00000010 /* Rx wr ctlr active */
+
+# define DEBUG_REG_RSVD5           0x00000008 /* */
+# define DEBUG_REG_SM_FIFO_RW_STA  0x00000006 /* small fifo rd/wr state */
+# define DEBUG_REG_RX_RECVG        0x00000001 /* Rx actively receiving data */
+
+# define REM_WAKEUP_FR_REG         0x0028
+# define PMT_CTRL_STAT_REG         0x002c
+/*   0x0030-0x0034 reserved */
+
+# define INTRP_STATUS_REG          0x0038
+
+# define INTRP_STATUS_REG_RSVD1    0x0000fc00 /* */
+# define INTRP_STATUS_REG_TSI      0x00000200 /* time stamp int stat */
+# define INTRP_STATUS_REG_RSVD2    0x00000100 /* */
+
+# define INTRP_STATUS_REG_RCOI     0x00000080 /* rec checksum offload int */
+# define INTRP_STATUS_REG_TI       0x00000040 /* tx int stat */
+# define INTRP_STATUS_REG_RI       0x00000020 /* rx int stat */
+# define INTRP_STATUS_REG_NI       0x00000010 /* normal int summary */
+
+# define INTRP_STATUS_REG_PMTI     0x00000008 /* PMT int */
+# define INTRP_STATUS_REG_ANC      0x00000004 /* auto negotiation complete */
+# define INTRP_STATUS_REG_LSC      0x00000002 /* link status change */
+# define INTRP_STATUS_REG_MII      0x00000001 /* rgMii/sgMii int */
+
+# define INTRP_MASK_REG            0x003c
+
+# define INTRP_MASK_REG_RSVD1      0xfc00     /* */
+# define INTRP_MASK_REG_TSIM       0x0200     /* time stamp int mask */
+# define INTRP_MASK_REG_RSVD2      0x01f0     /* */
+
+# define INTRP_MASK_REG_PMTIM      0x0000     /* PMT int mask */
+# define INTRP_MASK_REG_ANCM       0x0000     /* auto negotiation compl mask */
+# define INTRP_MASK_REG_LSCM       0x0000     /* link status change mask */
+# define INTRP_MASK_REG_MIIM       0x0000     /* rgMii/sgMii int mask */
+
+# define MAC_ADR_0_HIGH_REG        0x0040
+# define MAC_ADR_0_LOW_REG         0x0044
+/* additional pairs of registers for MAC addresses 1-15 */
+# define MAC_ADR_HIGH_REG_N(n)     (((n) < 16) ? \
+				    (MAC_ADR_0_HIGH_REG + (n) * 8) : \
+				    (MAC_ADR16_HIGH_REG + ((n) - 16) * 8))
+# define MAC_ADR_LOW_REG_N(n)      (((n) < 16) ? \
+				     (MAC_ADR_0_LOW_REG + (n) * 8) : \
+				     (MAC_ADR16_LOW_REG + ((n) - 16) * 8))
+
+# define AN_CONTROL_REG            0x00c0
+
+# define AN_CONTROL_REG_RSVRD1     0xfff80000 /* */
+# define AN_CONTROL_REG_SGM_RAL    0x00040000 /* sgmii ral control */
+# define AN_CONTROL_REG_LR         0x00020000 /* lock to reference */
+# define AN_CONTROL_REG_ECD        0x00010000 /* enable comma detect */
+
+# define AN_CONTROL_REG_RSVRD2     0x00008000 /* */
+# define AN_CONTROL_REG_ELE        0x00004000 /* external loopback enable */
+# define AN_CONTROL_REG_RSVRD3     0x00002000 /* */
+# define AN_CONTROL_REG_ANE        0x00001000 /* auto negotiation enable */
+
+# define AN_CONTROL_REG_RSRVD4     0x00000c00 /* */
+# define AN_CONTROL_REG_RAN        0x00000200 /* restart auto negotiation */
+# define AN_CONTROL_REG_RSVRD5     0x000001ff /* */
+
+# define AN_STATUS_REG             0x00c4
+
+# define AN_STATUS_REG_RSVRD1      0xfffffe00 /* */
+# define AN_STATUS_REG_ES          0x00000100 /* extended status */
+# define AN_STATUS_REG_RSVRD2      0x000000c0 /* */
+# define AN_STATUS_REG_ANC         0x00000020 /* auto-negotiation complete */
+# define AN_STATUS_REG_RSVRD3      0x00000010 /* */
+# define AN_STATUS_REG_ANA         0x00000008 /* auto-negotiation ability */
+# define AN_STATUS_REG_LS          0x00000004 /* link status */
+# define AN_STATUS_REG_RSVRD4      0x00000003 /* */
+
+# define AN_ADVERTISE_REG          0x00c8
+# define AN_LNK_PRTNR_ABIL_REG     0x00cc
+# define AN_EXPANDSION_REG         0x00d0
+# define TBI_EXT_STATUS_REG        0x00d4
+
+# define SG_RG_SMII_STATUS_REG     0x00d8
+
+# define LINK_STATUS_REG           0x00d8
+
+# define LINK_STATUS_REG_RSVRD1    0xffffffc0 /* */
+# define LINK_STATUS_REG_FCD       0x00000020 /* false carrier detect */
+# define LINK_STATUS_REG_JT        0x00000010 /* jabber timeout */
+# define LINK_STATUS_REG_UP        0x00000008 /* link status */
+
+# define LINK_STATUS_REG_SPD       0x00000006 /* link speed */
+# define LINK_STATUS_REG_SPD_2_5   0x00000000 /* 10M   2.5M * 4 */
+# define LINK_STATUS_REG_SPD_25    0x00000002 /* 100M   25M * 4 */
+# define LINK_STATUS_REG_SPD_125   0x00000004 /* 1G    125M * 8 */
+
+# define LINK_STATUS_REG_F_DUPLEX  0x00000001 /* full duplex */
+
+/*     0x00dc-0x00fc reserved */
+
+/* MMC Register Map is from     0x0100-0x02fc */
+# define MMC_CNTRL_REG             0x0100
+# define MMC_INTR_RX_REG           0x0104
+# define MMC_INTR_TX_REG           0x0108
+# define MMC_INTR_MASK_RX_REG      0x010C
+# define MMC_INTR_MASK_TX_REG      0x0110
+# define NUM_MULTCST_FRM_RCVD_G    0x0190
+
+/*     0x0300-0x06fc reserved */
+
+/* precision time protocol   time stamp registers */
+
+# define TS_CTL_REG                 0x0700
+
+# define TS_CTL_ATSFC               0x00080000
+# define TS_CTL_TSENMAC             0x00040000
+
+# define TS_CTL_TSCLKTYPE           0x00030000
+# define TS_CTL_TSCLK_ORD           0x00000000
+# define TS_CTL_TSCLK_BND           0x00010000
+# define TS_CTL_TSCLK_ETE           0x00020000
+# define TS_CTL_TSCLK_PTP           0x00030000
+
+# define TS_CTL_TSMSTRENA           0x00008000
+# define TS_CTL_TSEVNTENA           0x00004000
+# define TS_CTL_TSIPV4ENA           0x00002000
+# define TS_CTL_TSIPV6ENA           0x00001000
+
+# define TS_CTL_TSIPENA             0x00000800
+# define TS_CTL_TSVER2ENA           0x00000400
+# define TS_CTL_TSCTRLSSR           0x00000200
+# define TS_CTL_TSENALL             0x00000100
+
+# define TS_CTL_TSADDREG            0x00000020
+# define TS_CTL_TSTRIG              0x00000010
+
+# define TS_CTL_TSUPDT              0x00000008
+# define TS_CTL_TSINIT              0x00000004
+# define TS_CTL_TSCFUPDT            0x00000002
+# define TS_CTL_TSENA               0x00000001
+
+
+# define TS_SUB_SEC_INCR_REG        0x0704
+# define TS_HIGH_REG                0x0708
+# define TS_LOW_REG                 0x070c
+# define TS_HI_UPDT_REG             0x0710
+# define TS_LO_UPDT_REG             0x0714
+# define TS_APPEND_REG              0x0718
+# define TS_TARG_TIME_HIGH_REG      0x071c
+# define TS_TARG_TIME_LOW_REG       0x0720
+# define TS_HIGHER_WD_REG           0x0724
+# define TS_STATUS_REG              0x072c
+
+/*     0x0730-0x07fc reserved */
+
+# define MAC_ADR16_HIGH_REG        0x0800
+# define MAC_ADR16_LOW_REG         0x0804
+/* additional pairs of registers for MAC addresses 17-31 */
+
+# define MAC_ADR_MAX             32
+
+
+# define  QFEC_INTRP_SETUP               (INTRP_EN_REG_AIE    \
+					| INTRP_EN_REG_FBE \
+					| INTRP_EN_REG_RWE \
+					| INTRP_EN_REG_RSE \
+					| INTRP_EN_REG_RUE \
+					| INTRP_EN_REG_UNE \
+					| INTRP_EN_REG_OVE \
+					| INTRP_EN_REG_TJE \
+					| INTRP_EN_REG_TSE \
+					| INTRP_EN_REG_NIE \
+					| INTRP_EN_REG_RIE \
+					| INTRP_EN_REG_TIE)
+
+/*
+ * ASIC Ethernet clock register definitions:
+ *     address offsets and some register definitions
+ */
+
+# define EMAC_CLK_REG_BASE           0x94020000
+
+/*
+ * PHY clock PLL register locations
+ */
+# define ETH_MD_REG                  0x02A4
+# define ETH_NS_REG                  0x02A8
+
+/* definitions of NS_REG control bits
+ */
+# define ETH_NS_SRC_SEL              0x0007
+
+# define ETH_NS_PRE_DIV_MSK          0x0018
+# define ETH_NS_PRE_DIV(x)           (ETH_NS_PRE_DIV_MSK & (x << 3))
+
+# define ETH_NS_MCNTR_MODE_MSK       0x0060
+# define ETH_NS_MCNTR_MODE_BYPASS    0x0000
+# define ETH_NS_MCNTR_MODE_SWALLOW   0x0020
+# define ETH_NS_MCNTR_MODE_DUAL      0x0040
+# define ETH_NS_MCNTR_MODE_SINGLE    0x0060
+
+# define ETH_NS_MCNTR_RST            0x0080
+# define ETH_NS_MCNTR_EN             0x0100
+
+# define EMAC_PTP_NS_CLK_EN          0x0200
+# define EMAC_PTP_NS_CLK_INV         0x0400
+# define EMAC_PTP_NS_ROOT_EN         0x0800
+
+/* clock sources
+ */
+# define CLK_SRC_TCXO                0x0
+# define CLK_SRC_PLL_GLOBAL          0x1
+# define CLK_SRC_PLL_ARM             0x2
+# define CLK_SRC_PLL_QDSP6           0x3
+# define CLK_SRC_PLL_EMAC            0x4
+# define CLK_SRC_EXT_CLK2            0x5
+# define CLK_SRC_EXT_CLK1            0x6
+# define CLK_SRC_CORE_TEST           0x7
+
+# define ETH_MD_M(x)                 (x << 16)
+# define ETH_MD_2D_N(x)              ((~(x) & 0xffff))
+# define ETH_NS_NM(x)                ((~(x) << 16) & 0xffff0000)
+
+/*
+ * PHY interface clock divider
+ */
+# define ETH_X_EN_NS_REG             0x02AC
+
+# define ETH_RX_CLK_FB_INV           0x80
+# define ETH_RX_CLK_FB_EN            0x40
+# define ETH_TX_CLK_FB_INV           0x20
+# define ETH_TX_CLK_FB_EN            0x10
+# define ETH_RX_CLK_INV              0x08
+# define ETH_RX_CLK_EN               0x04
+# define ETH_TX_CLK_INV              0x02
+# define ETH_TX_CLK_EN               0x01
+
+# define ETH_X_EN_NS_DEFAULT \
+	(ETH_RX_CLK_FB_EN | ETH_TX_CLK_FB_EN | ETH_RX_CLK_EN | ETH_TX_CLK_EN)
+
+# define EMAC_PTP_MD_REG             0x02B0
+
+/* PTP clock divider
+ */
+# define EMAC_PTP_NS_REG             0x02B4
+
+/*
+ * clock interface pin controls
+ */
+# define EMAC_NS_REG                 0x02B8
+
+# define EMAC_RX_180_CLK_INV         0x2000
+# define EMAC_RX_180_CLK_EN          0x1000
+# define EMAC_RX_180_CLK_EN_INV      (EMAC_RX_180_CLK_INV | EMAC_RX_180_CLK_EN)
+
+# define EMAC_TX_180_CLK_INV         0x0800
+# define EMAC_TX_180_CLK_EN          0x0400
+# define EMAC_TX_180_CLK_EN_INV      (EMAC_TX_180_CLK_INV | EMAC_TX_180_CLK_EN)
+
+# define EMAC_REVMII_RX_CLK_INV      0x0200
+# define EMAC_REVMII_RX_CLK_EN       0x0100
+
+# define EMAC_RX_CLK_INV             0x0080
+# define EMAC_RX_CLK_EN              0x0040
+
+# define EMAC_REVMII_TX_CLK_INV      0x0020
+# define EMAC_REVMII_TX_CLK_EN       0x0010
+
+# define EMAC_TX_CLK_INV             0x0008
+# define EMAC_TX_CLK_EN              0x0004
+
+# define EMAC_RX_R_CLK_EN            0x0002
+# define EMAC_TX_R_CLK_EN            0x0001
+
+# define EMAC_NS_DEFAULT \
+	(EMAC_RX_180_CLK_EN_INV | EMAC_TX_180_CLK_EN_INV \
+	| EMAC_REVMII_RX_CLK_EN | EMAC_REVMII_TX_CLK_EN \
+	| EMAC_RX_CLK_EN | EMAC_TX_CLK_EN \
+	| EMAC_RX_R_CLK_EN | EMAC_TX_R_CLK_EN)
+
+/*
+ *
+ */
+# define EMAC_TX_FS_REG              0x02BC
+# define EMAC_RX_FS_REG              0x02C0
+
+/*
+ * Ethernet controller PHY interface select
+ */
+# define EMAC_PHY_INTF_SEL_REG       0x18030
+
+# define EMAC_PHY_INTF_SEL_MII       0x0
+# define EMAC_PHY_INTF_SEL_RGMII     0x1
+# define EMAC_PHY_INTF_SEL_REVMII    0x7
+# define EMAC_PHY_INTF_SEL_MASK      0x7
+
+/*
+ * MDIO addresses
+ */
+# define EMAC_PHY_ADDR_REG           0x18034
+# define EMAC_REVMII_PHY_ADDR_REG    0x18038
+
+/*
+ * clock routing
+ */
+# define EMAC_CLKMUX_SEL_REG         0x1803c
+
+# define EMAC_CLKMUX_SEL_0           0x1
+# define EMAC_CLKMUX_SEL_1           0x2
+
+
+#endif
diff --git a/drivers/net/ethernet/smsc/smc91x.h b/drivers/net/ethernet/smsc/smc91x.h
index 5f53fbb..1e1617e 100644
--- a/drivers/net/ethernet/smsc/smc91x.h
+++ b/drivers/net/ethernet/smsc/smc91x.h
@@ -223,6 +223,20 @@
 #define SMC_outsl(a, r, p, l)	writesl((a) + (r), p, l)
 #define SMC_IRQ_FLAGS		(-1)	/* from resource */
 
+#elif defined(CONFIG_ARCH_MSM)
+
+#define SMC_CAN_USE_8BIT	0
+#define SMC_CAN_USE_16BIT	1
+#define SMC_CAN_USE_32BIT	0
+#define SMC_NOWAIT		1
+
+#define SMC_inw(a, r)		readw((a) + (r))
+#define SMC_outw(v, a, r)	writew(v, (a) + (r))
+#define SMC_insw(a, r, p, l)	readsw((a) + (r), p, l)
+#define SMC_outsw(a, r, p, l)	writesw((a) + (r), p, l)
+
+#define SMC_IRQ_FLAGS		IRQF_TRIGGER_HIGH
+
 #elif defined(CONFIG_MN10300)
 
 /*
diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c
index cd3defb..b0d4c4c 100644
--- a/drivers/net/ethernet/smsc/smsc911x.c
+++ b/drivers/net/ethernet/smsc/smsc911x.c
@@ -44,6 +44,7 @@
 #include <linux/module.h>
 #include <linux/netdevice.h>
 #include <linux/platform_device.h>
+#include <linux/gpio.h>
 #include <linux/regulator/consumer.h>
 #include <linux/sched.h>
 #include <linux/timer.h>
@@ -954,7 +955,7 @@
 			    (!pdata->using_extphy)) {
 				/* Restore original GPIO configuration */
 				pdata->gpio_setting = pdata->gpio_orig_setting;
-				smsc911x_reg_write(pdata, GPIO_CFG,
+				smsc911x_reg_write(pdata, SMSC_GPIO_CFG,
 					pdata->gpio_setting);
 			}
 		} else {
@@ -962,7 +963,7 @@
 			/* Check global setting that LED1
 			 * usage is 10/100 indicator */
 			pdata->gpio_setting = smsc911x_reg_read(pdata,
-				GPIO_CFG);
+				SMSC_GPIO_CFG);
 			if ((pdata->gpio_setting & GPIO_CFG_LED1_EN_) &&
 			    (!pdata->using_extphy)) {
 				/* Force 10/100 LED off, after saving
@@ -973,7 +974,7 @@
 				pdata->gpio_setting |= (GPIO_CFG_GPIOBUF0_
 							| GPIO_CFG_GPIODIR0_
 							| GPIO_CFG_GPIOD0_);
-				smsc911x_reg_write(pdata, GPIO_CFG,
+				smsc911x_reg_write(pdata, SMSC_GPIO_CFG,
 					pdata->gpio_setting);
 			}
 		}
@@ -1485,7 +1486,7 @@
 		SMSC_WARN(pdata, ifup,
 			  "Timed out waiting for EEPROM busy bit to clear");
 
-	smsc911x_reg_write(pdata, GPIO_CFG, 0x70070000);
+	smsc911x_reg_write(pdata, SMSC_GPIO_CFG, 0x70070000);
 
 	/* The soft reset above cleared the device's MAC address,
 	 * restore it from local copy (set in probe) */
@@ -1931,9 +1932,9 @@
 
 static void smsc911x_eeprom_enable_access(struct smsc911x_data *pdata)
 {
-	unsigned int temp = smsc911x_reg_read(pdata, GPIO_CFG);
+	unsigned int temp = smsc911x_reg_read(pdata, SMSC_GPIO_CFG);
 	temp &= ~GPIO_CFG_EEPR_EN_;
-	smsc911x_reg_write(pdata, GPIO_CFG, temp);
+	smsc911x_reg_write(pdata, SMSC_GPIO_CFG, temp);
 	msleep(1);
 }
 
@@ -2241,6 +2242,12 @@
 
 	SMSC_TRACE(pdata, ifdown, "Stopping driver");
 
+	if (pdata->config.has_reset_gpio) {
+		gpio_set_value_cansleep(pdata->config.reset_gpio, 0);
+		gpio_free(pdata->config.reset_gpio);
+	}
+
+
 	phy_disconnect(pdata->phy_dev);
 	pdata->phy_dev = NULL;
 	mdiobus_unregister(pdata->mii_bus);
@@ -2436,9 +2443,10 @@
 	smsc911x_reg_write(pdata, INT_EN, 0);
 	smsc911x_reg_write(pdata, INT_STS, 0xFFFFFFFF);
 
-	retval = request_irq(dev->irq, smsc911x_irqhandler,
-			     irq_flags | IRQF_SHARED, dev->name, dev);
-	if (retval) {
+	retval = request_any_context_irq(dev->irq, smsc911x_irqhandler,
+					 irq_flags | IRQF_SHARED, dev->name,
+					 dev);
+	if (retval < 0) {
 		SMSC_WARN(pdata, probe,
 			  "Unable to claim requested irq: %d", dev->irq);
 		goto out_disable_resources;
@@ -2528,6 +2536,10 @@
 		PMT_CTRL_PM_MODE_D1_ | PMT_CTRL_WOL_EN_ |
 		PMT_CTRL_ED_EN_ | PMT_CTRL_PME_EN_);
 
+	/* Drive the GPIO Ethernet_Reset Line low to Suspend */
+	if (pdata->config.has_reset_gpio)
+		gpio_set_value_cansleep(pdata->config.reset_gpio, 0);
+
 	return 0;
 }
 
@@ -2537,6 +2549,10 @@
 	struct smsc911x_data *pdata = netdev_priv(ndev);
 	unsigned int to = 100;
 
+	if (pdata->config.has_reset_gpio)
+		gpio_set_value_cansleep(pdata->config.reset_gpio, 1);
+
+
 	/* Note 3.11 from the datasheet:
 	 * 	"When the LAN9220 is in a power saving state, a write of any
 	 * 	 data to the BYTE_TEST register will wake-up the device."
diff --git a/drivers/net/ethernet/smsc/smsc911x.h b/drivers/net/ethernet/smsc/smsc911x.h
index 9ad5e5d..43e5398 100644
--- a/drivers/net/ethernet/smsc/smsc911x.h
+++ b/drivers/net/ethernet/smsc/smsc911x.h
@@ -236,7 +236,7 @@
 #define PMT_CTRL_PME_EN_		0x00000002
 #define PMT_CTRL_READY_			0x00000001
 
-#define GPIO_CFG			0x88
+#define SMSC_GPIO_CFG			0x88
 #define GPIO_CFG_LED3_EN_		0x40000000
 #define GPIO_CFG_LED2_EN_		0x20000000
 #define GPIO_CFG_LED1_EN_		0x10000000
diff --git a/drivers/net/pppolac.c b/drivers/net/pppolac.c
new file mode 100644
index 0000000..c94b850
--- /dev/null
+++ b/drivers/net/pppolac.c
@@ -0,0 +1,449 @@
+/* drivers/net/pppolac.c
+ *
+ * Driver for PPP on L2TP Access Concentrator / PPPoLAC Socket (RFC 2661)
+ *
+ * Copyright (C) 2009 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.
+ */
+
+/* This driver handles L2TP data packets between a UDP socket and a PPP channel.
+ * The socket must keep connected, and only one session per socket is permitted.
+ * Sequencing of outgoing packets is controlled by LNS. Incoming packets with
+ * sequences are reordered within a sliding window of one second. Currently
+ * reordering only happens when a packet is received. It is done for simplicity
+ * since no additional locks or threads are required. This driver only works on
+ * IPv4 due to the lack of UDP encapsulation support in IPv6. */
+
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/workqueue.h>
+#include <linux/skbuff.h>
+#include <linux/file.h>
+#include <linux/netdevice.h>
+#include <linux/net.h>
+#include <linux/udp.h>
+#include <linux/ppp_defs.h>
+#include <linux/if_ppp.h>
+#include <linux/if_pppox.h>
+#include <linux/ppp_channel.h>
+#include <net/tcp_states.h>
+#include <asm/uaccess.h>
+
+#define L2TP_CONTROL_BIT	0x80
+#define L2TP_LENGTH_BIT		0x40
+#define L2TP_SEQUENCE_BIT	0x08
+#define L2TP_OFFSET_BIT		0x02
+#define L2TP_VERSION		0x02
+#define L2TP_VERSION_MASK	0x0F
+
+#define PPP_ADDR	0xFF
+#define PPP_CTRL	0x03
+
+union unaligned {
+	__u32 u32;
+} __attribute__((packed));
+
+static inline union unaligned *unaligned(void *ptr)
+{
+	return (union unaligned *)ptr;
+}
+
+struct meta {
+	__u32 sequence;
+	__u32 timestamp;
+};
+
+static inline struct meta *skb_meta(struct sk_buff *skb)
+{
+	return (struct meta *)skb->cb;
+}
+
+/******************************************************************************/
+
+static int pppolac_recv_core(struct sock *sk_udp, struct sk_buff *skb)
+{
+	struct sock *sk = (struct sock *)sk_udp->sk_user_data;
+	struct pppolac_opt *opt = &pppox_sk(sk)->proto.lac;
+	struct meta *meta = skb_meta(skb);
+	__u32 now = jiffies;
+	__u8 bits;
+	__u8 *ptr;
+
+	/* Drop the packet if L2TP header is missing. */
+	if (skb->len < sizeof(struct udphdr) + 6)
+		goto drop;
+
+	/* Put it back if it is a control packet. */
+	if (skb->data[sizeof(struct udphdr)] & L2TP_CONTROL_BIT)
+		return opt->backlog_rcv(sk_udp, skb);
+
+	/* Skip UDP header. */
+	skb_pull(skb, sizeof(struct udphdr));
+
+	/* Check the version. */
+	if ((skb->data[1] & L2TP_VERSION_MASK) != L2TP_VERSION)
+		goto drop;
+	bits = skb->data[0];
+	ptr = &skb->data[2];
+
+	/* Check the length if it is present. */
+	if (bits & L2TP_LENGTH_BIT) {
+		if ((ptr[0] << 8 | ptr[1]) != skb->len)
+			goto drop;
+		ptr += 2;
+	}
+
+	/* Skip all fields including optional ones. */
+	if (!skb_pull(skb, 6 + (bits & L2TP_SEQUENCE_BIT ? 4 : 0) +
+			(bits & L2TP_LENGTH_BIT ? 2 : 0) +
+			(bits & L2TP_OFFSET_BIT ? 2 : 0)))
+		goto drop;
+
+	/* Skip the offset padding if it is present. */
+	if (bits & L2TP_OFFSET_BIT &&
+			!skb_pull(skb, skb->data[-2] << 8 | skb->data[-1]))
+		goto drop;
+
+	/* Check the tunnel and the session. */
+	if (unaligned(ptr)->u32 != opt->local)
+		goto drop;
+
+	/* Check the sequence if it is present. */
+	if (bits & L2TP_SEQUENCE_BIT) {
+		meta->sequence = ptr[4] << 8 | ptr[5];
+		if ((__s16)(meta->sequence - opt->recv_sequence) < 0)
+			goto drop;
+	}
+
+	/* Skip PPP address and control if they are present. */
+	if (skb->len >= 2 && skb->data[0] == PPP_ADDR &&
+			skb->data[1] == PPP_CTRL)
+		skb_pull(skb, 2);
+
+	/* Fix PPP protocol if it is compressed. */
+	if (skb->len >= 1 && skb->data[0] & 1)
+		skb_push(skb, 1)[0] = 0;
+
+	/* Drop the packet if PPP protocol is missing. */
+	if (skb->len < 2)
+		goto drop;
+
+	/* Perform reordering if sequencing is enabled. */
+	atomic_set(&opt->sequencing, bits & L2TP_SEQUENCE_BIT);
+	if (bits & L2TP_SEQUENCE_BIT) {
+		struct sk_buff *skb1;
+
+		/* Insert the packet into receive queue in order. */
+		skb_set_owner_r(skb, sk);
+		skb_queue_walk(&sk->sk_receive_queue, skb1) {
+			struct meta *meta1 = skb_meta(skb1);
+			__s16 order = meta->sequence - meta1->sequence;
+			if (order == 0)
+				goto drop;
+			if (order < 0) {
+				meta->timestamp = meta1->timestamp;
+				skb_insert(skb1, skb, &sk->sk_receive_queue);
+				skb = NULL;
+				break;
+			}
+		}
+		if (skb) {
+			meta->timestamp = now;
+			skb_queue_tail(&sk->sk_receive_queue, skb);
+		}
+
+		/* Remove packets from receive queue as long as
+		 * 1. the receive buffer is full,
+		 * 2. they are queued longer than one second, or
+		 * 3. there are no missing packets before them. */
+		skb_queue_walk_safe(&sk->sk_receive_queue, skb, skb1) {
+			meta = skb_meta(skb);
+			if (atomic_read(&sk->sk_rmem_alloc) < sk->sk_rcvbuf &&
+					now - meta->timestamp < HZ &&
+					meta->sequence != opt->recv_sequence)
+				break;
+			skb_unlink(skb, &sk->sk_receive_queue);
+			opt->recv_sequence = (__u16)(meta->sequence + 1);
+			skb_orphan(skb);
+			ppp_input(&pppox_sk(sk)->chan, skb);
+		}
+		return NET_RX_SUCCESS;
+	}
+
+	/* Flush receive queue if sequencing is disabled. */
+	skb_queue_purge(&sk->sk_receive_queue);
+	skb_orphan(skb);
+	ppp_input(&pppox_sk(sk)->chan, skb);
+	return NET_RX_SUCCESS;
+drop:
+	kfree_skb(skb);
+	return NET_RX_DROP;
+}
+
+static int pppolac_recv(struct sock *sk_udp, struct sk_buff *skb)
+{
+	sock_hold(sk_udp);
+	sk_receive_skb(sk_udp, skb, 0);
+	return 0;
+}
+
+static struct sk_buff_head delivery_queue;
+
+static void pppolac_xmit_core(struct work_struct *delivery_work)
+{
+	mm_segment_t old_fs = get_fs();
+	struct sk_buff *skb;
+
+	set_fs(KERNEL_DS);
+	while ((skb = skb_dequeue(&delivery_queue))) {
+		struct sock *sk_udp = skb->sk;
+		struct kvec iov = {.iov_base = skb->data, .iov_len = skb->len};
+		struct msghdr msg = {
+			.msg_iov = (struct iovec *)&iov,
+			.msg_iovlen = 1,
+			.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT,
+		};
+		sk_udp->sk_prot->sendmsg(NULL, sk_udp, &msg, skb->len);
+		kfree_skb(skb);
+	}
+	set_fs(old_fs);
+}
+
+static DECLARE_WORK(delivery_work, pppolac_xmit_core);
+
+static int pppolac_xmit(struct ppp_channel *chan, struct sk_buff *skb)
+{
+	struct sock *sk_udp = (struct sock *)chan->private;
+	struct pppolac_opt *opt = &pppox_sk(sk_udp->sk_user_data)->proto.lac;
+
+	/* Install PPP address and control. */
+	skb_push(skb, 2);
+	skb->data[0] = PPP_ADDR;
+	skb->data[1] = PPP_CTRL;
+
+	/* Install L2TP header. */
+	if (atomic_read(&opt->sequencing)) {
+		skb_push(skb, 10);
+		skb->data[0] = L2TP_SEQUENCE_BIT;
+		skb->data[6] = opt->xmit_sequence >> 8;
+		skb->data[7] = opt->xmit_sequence;
+		skb->data[8] = 0;
+		skb->data[9] = 0;
+		opt->xmit_sequence++;
+	} else {
+		skb_push(skb, 6);
+		skb->data[0] = 0;
+	}
+	skb->data[1] = L2TP_VERSION;
+	unaligned(&skb->data[2])->u32 = opt->remote;
+
+	/* Now send the packet via the delivery queue. */
+	skb_set_owner_w(skb, sk_udp);
+	skb_queue_tail(&delivery_queue, skb);
+	schedule_work(&delivery_work);
+	return 1;
+}
+
+/******************************************************************************/
+
+static struct ppp_channel_ops pppolac_channel_ops = {
+	.start_xmit = pppolac_xmit,
+};
+
+static int pppolac_connect(struct socket *sock, struct sockaddr *useraddr,
+	int addrlen, int flags)
+{
+	struct sock *sk = sock->sk;
+	struct pppox_sock *po = pppox_sk(sk);
+	struct sockaddr_pppolac *addr = (struct sockaddr_pppolac *)useraddr;
+	struct socket *sock_udp = NULL;
+	struct sock *sk_udp;
+	int error;
+
+	if (addrlen != sizeof(struct sockaddr_pppolac) ||
+			!addr->local.tunnel || !addr->local.session ||
+			!addr->remote.tunnel || !addr->remote.session) {
+		return -EINVAL;
+	}
+
+	lock_sock(sk);
+	error = -EALREADY;
+	if (sk->sk_state != PPPOX_NONE)
+		goto out;
+
+	sock_udp = sockfd_lookup(addr->udp_socket, &error);
+	if (!sock_udp)
+		goto out;
+	sk_udp = sock_udp->sk;
+	lock_sock(sk_udp);
+
+	/* Remove this check when IPv6 supports UDP encapsulation. */
+	error = -EAFNOSUPPORT;
+	if (sk_udp->sk_family != AF_INET)
+		goto out;
+	error = -EPROTONOSUPPORT;
+	if (sk_udp->sk_protocol != IPPROTO_UDP)
+		goto out;
+	error = -EDESTADDRREQ;
+	if (sk_udp->sk_state != TCP_ESTABLISHED)
+		goto out;
+	error = -EBUSY;
+	if (udp_sk(sk_udp)->encap_type || sk_udp->sk_user_data)
+		goto out;
+	if (!sk_udp->sk_bound_dev_if) {
+		struct dst_entry *dst = sk_dst_get(sk_udp);
+		error = -ENODEV;
+		if (!dst)
+			goto out;
+		sk_udp->sk_bound_dev_if = dst->dev->ifindex;
+		dst_release(dst);
+	}
+
+	po->chan.hdrlen = 12;
+	po->chan.private = sk_udp;
+	po->chan.ops = &pppolac_channel_ops;
+	po->chan.mtu = PPP_MTU - 80;
+	po->proto.lac.local = unaligned(&addr->local)->u32;
+	po->proto.lac.remote = unaligned(&addr->remote)->u32;
+	atomic_set(&po->proto.lac.sequencing, 1);
+	po->proto.lac.backlog_rcv = sk_udp->sk_backlog_rcv;
+
+	error = ppp_register_channel(&po->chan);
+	if (error)
+		goto out;
+
+	sk->sk_state = PPPOX_CONNECTED;
+	udp_sk(sk_udp)->encap_type = UDP_ENCAP_L2TPINUDP;
+	udp_sk(sk_udp)->encap_rcv = pppolac_recv;
+	sk_udp->sk_backlog_rcv = pppolac_recv_core;
+	sk_udp->sk_user_data = sk;
+out:
+	if (sock_udp) {
+		release_sock(sk_udp);
+		if (error)
+			sockfd_put(sock_udp);
+	}
+	release_sock(sk);
+	return error;
+}
+
+static int pppolac_release(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+
+	if (!sk)
+		return 0;
+
+	lock_sock(sk);
+	if (sock_flag(sk, SOCK_DEAD)) {
+		release_sock(sk);
+		return -EBADF;
+	}
+
+	if (sk->sk_state != PPPOX_NONE) {
+		struct sock *sk_udp = (struct sock *)pppox_sk(sk)->chan.private;
+		lock_sock(sk_udp);
+		skb_queue_purge(&sk->sk_receive_queue);
+		pppox_unbind_sock(sk);
+		udp_sk(sk_udp)->encap_type = 0;
+		udp_sk(sk_udp)->encap_rcv = NULL;
+		sk_udp->sk_backlog_rcv = pppox_sk(sk)->proto.lac.backlog_rcv;
+		sk_udp->sk_user_data = NULL;
+		release_sock(sk_udp);
+		sockfd_put(sk_udp->sk_socket);
+	}
+
+	sock_orphan(sk);
+	sock->sk = NULL;
+	release_sock(sk);
+	sock_put(sk);
+	return 0;
+}
+
+/******************************************************************************/
+
+static struct proto pppolac_proto = {
+	.name = "PPPOLAC",
+	.owner = THIS_MODULE,
+	.obj_size = sizeof(struct pppox_sock),
+};
+
+static struct proto_ops pppolac_proto_ops = {
+	.family = PF_PPPOX,
+	.owner = THIS_MODULE,
+	.release = pppolac_release,
+	.bind = sock_no_bind,
+	.connect = pppolac_connect,
+	.socketpair = sock_no_socketpair,
+	.accept = sock_no_accept,
+	.getname = sock_no_getname,
+	.poll = sock_no_poll,
+	.ioctl = pppox_ioctl,
+	.listen = sock_no_listen,
+	.shutdown = sock_no_shutdown,
+	.setsockopt = sock_no_setsockopt,
+	.getsockopt = sock_no_getsockopt,
+	.sendmsg = sock_no_sendmsg,
+	.recvmsg = sock_no_recvmsg,
+	.mmap = sock_no_mmap,
+};
+
+static int pppolac_create(struct net *net, struct socket *sock)
+{
+	struct sock *sk;
+
+	sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pppolac_proto);
+	if (!sk)
+		return -ENOMEM;
+
+	sock_init_data(sock, sk);
+	sock->state = SS_UNCONNECTED;
+	sock->ops = &pppolac_proto_ops;
+	sk->sk_protocol = PX_PROTO_OLAC;
+	sk->sk_state = PPPOX_NONE;
+	return 0;
+}
+
+/******************************************************************************/
+
+static struct pppox_proto pppolac_pppox_proto = {
+	.create = pppolac_create,
+	.owner = THIS_MODULE,
+};
+
+static int __init pppolac_init(void)
+{
+	int error;
+
+	error = proto_register(&pppolac_proto, 0);
+	if (error)
+		return error;
+
+	error = register_pppox_proto(PX_PROTO_OLAC, &pppolac_pppox_proto);
+	if (error)
+		proto_unregister(&pppolac_proto);
+	else
+		skb_queue_head_init(&delivery_queue);
+	return error;
+}
+
+static void __exit pppolac_exit(void)
+{
+	unregister_pppox_proto(PX_PROTO_OLAC);
+	proto_unregister(&pppolac_proto);
+}
+
+module_init(pppolac_init);
+module_exit(pppolac_exit);
+
+MODULE_DESCRIPTION("PPP on L2TP Access Concentrator (PPPoLAC)");
+MODULE_AUTHOR("Chia-chi Yeh <chiachi@android.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/pppopns.c b/drivers/net/pppopns.c
new file mode 100644
index 0000000..fb81984
--- /dev/null
+++ b/drivers/net/pppopns.c
@@ -0,0 +1,428 @@
+/* drivers/net/pppopns.c
+ *
+ * Driver for PPP on PPTP Network Server / PPPoPNS Socket (RFC 2637)
+ *
+ * Copyright (C) 2009 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.
+ */
+
+/* This driver handles PPTP data packets between a RAW socket and a PPP channel.
+ * The socket is created in the kernel space and connected to the same address
+ * of the control socket. Outgoing packets are always sent with sequences but
+ * without acknowledgements. Incoming packets with sequences are reordered
+ * within a sliding window of one second. Currently reordering only happens when
+ * a packet is received. It is done for simplicity since no additional locks or
+ * threads are required. This driver should work on both IPv4 and IPv6. */
+
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/workqueue.h>
+#include <linux/skbuff.h>
+#include <linux/file.h>
+#include <linux/netdevice.h>
+#include <linux/net.h>
+#include <linux/ppp_defs.h>
+#include <linux/if.h>
+#include <linux/if_ppp.h>
+#include <linux/if_pppox.h>
+#include <linux/ppp_channel.h>
+#include <asm/uaccess.h>
+
+#define GRE_HEADER_SIZE		8
+
+#define PPTP_GRE_BITS		htons(0x2001)
+#define PPTP_GRE_BITS_MASK	htons(0xEF7F)
+#define PPTP_GRE_SEQ_BIT	htons(0x1000)
+#define PPTP_GRE_ACK_BIT	htons(0x0080)
+#define PPTP_GRE_TYPE		htons(0x880B)
+
+#define PPP_ADDR	0xFF
+#define PPP_CTRL	0x03
+
+struct header {
+	__u16	bits;
+	__u16	type;
+	__u16	length;
+	__u16	call;
+	__u32	sequence;
+} __attribute__((packed));
+
+struct meta {
+	__u32 sequence;
+	__u32 timestamp;
+};
+
+static inline struct meta *skb_meta(struct sk_buff *skb)
+{
+	return (struct meta *)skb->cb;
+}
+
+/******************************************************************************/
+
+static int pppopns_recv_core(struct sock *sk_raw, struct sk_buff *skb)
+{
+	struct sock *sk = (struct sock *)sk_raw->sk_user_data;
+	struct pppopns_opt *opt = &pppox_sk(sk)->proto.pns;
+	struct meta *meta = skb_meta(skb);
+	__u32 now = jiffies;
+	struct header *hdr;
+
+	/* Skip transport header */
+	skb_pull(skb, skb_transport_header(skb) - skb->data);
+
+	/* Drop the packet if GRE header is missing. */
+	if (skb->len < GRE_HEADER_SIZE)
+		goto drop;
+	hdr = (struct header *)skb->data;
+
+	/* Check the header. */
+	if (hdr->type != PPTP_GRE_TYPE || hdr->call != opt->local ||
+			(hdr->bits & PPTP_GRE_BITS_MASK) != PPTP_GRE_BITS)
+		goto drop;
+
+	/* Skip all fields including optional ones. */
+	if (!skb_pull(skb, GRE_HEADER_SIZE +
+			(hdr->bits & PPTP_GRE_SEQ_BIT ? 4 : 0) +
+			(hdr->bits & PPTP_GRE_ACK_BIT ? 4 : 0)))
+		goto drop;
+
+	/* Check the length. */
+	if (skb->len != ntohs(hdr->length))
+		goto drop;
+
+	/* Check the sequence if it is present. */
+	if (hdr->bits & PPTP_GRE_SEQ_BIT) {
+		meta->sequence = ntohl(hdr->sequence);
+		if ((__s32)(meta->sequence - opt->recv_sequence) < 0)
+			goto drop;
+	}
+
+	/* Skip PPP address and control if they are present. */
+	if (skb->len >= 2 && skb->data[0] == PPP_ADDR &&
+			skb->data[1] == PPP_CTRL)
+		skb_pull(skb, 2);
+
+	/* Fix PPP protocol if it is compressed. */
+	if (skb->len >= 1 && skb->data[0] & 1)
+		skb_push(skb, 1)[0] = 0;
+
+	/* Drop the packet if PPP protocol is missing. */
+	if (skb->len < 2)
+		goto drop;
+
+	/* Perform reordering if sequencing is enabled. */
+	if (hdr->bits & PPTP_GRE_SEQ_BIT) {
+		struct sk_buff *skb1;
+
+		/* Insert the packet into receive queue in order. */
+		skb_set_owner_r(skb, sk);
+		skb_queue_walk(&sk->sk_receive_queue, skb1) {
+			struct meta *meta1 = skb_meta(skb1);
+			__s32 order = meta->sequence - meta1->sequence;
+			if (order == 0)
+				goto drop;
+			if (order < 0) {
+				meta->timestamp = meta1->timestamp;
+				skb_insert(skb1, skb, &sk->sk_receive_queue);
+				skb = NULL;
+				break;
+			}
+		}
+		if (skb) {
+			meta->timestamp = now;
+			skb_queue_tail(&sk->sk_receive_queue, skb);
+		}
+
+		/* Remove packets from receive queue as long as
+		 * 1. the receive buffer is full,
+		 * 2. they are queued longer than one second, or
+		 * 3. there are no missing packets before them. */
+		skb_queue_walk_safe(&sk->sk_receive_queue, skb, skb1) {
+			meta = skb_meta(skb);
+			if (atomic_read(&sk->sk_rmem_alloc) < sk->sk_rcvbuf &&
+					now - meta->timestamp < HZ &&
+					meta->sequence != opt->recv_sequence)
+				break;
+			skb_unlink(skb, &sk->sk_receive_queue);
+			opt->recv_sequence = meta->sequence + 1;
+			skb_orphan(skb);
+			ppp_input(&pppox_sk(sk)->chan, skb);
+		}
+		return NET_RX_SUCCESS;
+	}
+
+	/* Flush receive queue if sequencing is disabled. */
+	skb_queue_purge(&sk->sk_receive_queue);
+	skb_orphan(skb);
+	ppp_input(&pppox_sk(sk)->chan, skb);
+	return NET_RX_SUCCESS;
+drop:
+	kfree_skb(skb);
+	return NET_RX_DROP;
+}
+
+static void pppopns_recv(struct sock *sk_raw, int length)
+{
+	struct sk_buff *skb;
+	while ((skb = skb_dequeue(&sk_raw->sk_receive_queue))) {
+		sock_hold(sk_raw);
+		sk_receive_skb(sk_raw, skb, 0);
+	}
+}
+
+static struct sk_buff_head delivery_queue;
+
+static void pppopns_xmit_core(struct work_struct *delivery_work)
+{
+	mm_segment_t old_fs = get_fs();
+	struct sk_buff *skb;
+
+	set_fs(KERNEL_DS);
+	while ((skb = skb_dequeue(&delivery_queue))) {
+		struct sock *sk_raw = skb->sk;
+		struct kvec iov = {.iov_base = skb->data, .iov_len = skb->len};
+		struct msghdr msg = {
+			.msg_iov = (struct iovec *)&iov,
+			.msg_iovlen = 1,
+			.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT,
+		};
+		sk_raw->sk_prot->sendmsg(NULL, sk_raw, &msg, skb->len);
+		kfree_skb(skb);
+	}
+	set_fs(old_fs);
+}
+
+static DECLARE_WORK(delivery_work, pppopns_xmit_core);
+
+static int pppopns_xmit(struct ppp_channel *chan, struct sk_buff *skb)
+{
+	struct sock *sk_raw = (struct sock *)chan->private;
+	struct pppopns_opt *opt = &pppox_sk(sk_raw->sk_user_data)->proto.pns;
+	struct header *hdr;
+	__u16 length;
+
+	/* Install PPP address and control. */
+	skb_push(skb, 2);
+	skb->data[0] = PPP_ADDR;
+	skb->data[1] = PPP_CTRL;
+	length = skb->len;
+
+	/* Install PPTP GRE header. */
+	hdr = (struct header *)skb_push(skb, 12);
+	hdr->bits = PPTP_GRE_BITS | PPTP_GRE_SEQ_BIT;
+	hdr->type = PPTP_GRE_TYPE;
+	hdr->length = htons(length);
+	hdr->call = opt->remote;
+	hdr->sequence = htonl(opt->xmit_sequence);
+	opt->xmit_sequence++;
+
+	/* Now send the packet via the delivery queue. */
+	skb_set_owner_w(skb, sk_raw);
+	skb_queue_tail(&delivery_queue, skb);
+	schedule_work(&delivery_work);
+	return 1;
+}
+
+/******************************************************************************/
+
+static struct ppp_channel_ops pppopns_channel_ops = {
+	.start_xmit = pppopns_xmit,
+};
+
+static int pppopns_connect(struct socket *sock, struct sockaddr *useraddr,
+	int addrlen, int flags)
+{
+	struct sock *sk = sock->sk;
+	struct pppox_sock *po = pppox_sk(sk);
+	struct sockaddr_pppopns *addr = (struct sockaddr_pppopns *)useraddr;
+	struct sockaddr_storage ss;
+	struct socket *sock_tcp = NULL;
+	struct socket *sock_raw = NULL;
+	struct sock *sk_tcp;
+	struct sock *sk_raw;
+	int error;
+
+	if (addrlen != sizeof(struct sockaddr_pppopns))
+		return -EINVAL;
+
+	lock_sock(sk);
+	error = -EALREADY;
+	if (sk->sk_state != PPPOX_NONE)
+		goto out;
+
+	sock_tcp = sockfd_lookup(addr->tcp_socket, &error);
+	if (!sock_tcp)
+		goto out;
+	sk_tcp = sock_tcp->sk;
+	error = -EPROTONOSUPPORT;
+	if (sk_tcp->sk_protocol != IPPROTO_TCP)
+		goto out;
+	addrlen = sizeof(struct sockaddr_storage);
+	error = kernel_getpeername(sock_tcp, (struct sockaddr *)&ss, &addrlen);
+	if (error)
+		goto out;
+	if (!sk_tcp->sk_bound_dev_if) {
+		struct dst_entry *dst = sk_dst_get(sk_tcp);
+		error = -ENODEV;
+		if (!dst)
+			goto out;
+		sk_tcp->sk_bound_dev_if = dst->dev->ifindex;
+		dst_release(dst);
+	}
+
+	error = sock_create(ss.ss_family, SOCK_RAW, IPPROTO_GRE, &sock_raw);
+	if (error)
+		goto out;
+	sk_raw = sock_raw->sk;
+	sk_raw->sk_bound_dev_if = sk_tcp->sk_bound_dev_if;
+	error = kernel_connect(sock_raw, (struct sockaddr *)&ss, addrlen, 0);
+	if (error)
+		goto out;
+
+	po->chan.hdrlen = 14;
+	po->chan.private = sk_raw;
+	po->chan.ops = &pppopns_channel_ops;
+	po->chan.mtu = PPP_MTU - 80;
+	po->proto.pns.local = addr->local;
+	po->proto.pns.remote = addr->remote;
+	po->proto.pns.data_ready = sk_raw->sk_data_ready;
+	po->proto.pns.backlog_rcv = sk_raw->sk_backlog_rcv;
+
+	error = ppp_register_channel(&po->chan);
+	if (error)
+		goto out;
+
+	sk->sk_state = PPPOX_CONNECTED;
+	lock_sock(sk_raw);
+	sk_raw->sk_data_ready = pppopns_recv;
+	sk_raw->sk_backlog_rcv = pppopns_recv_core;
+	sk_raw->sk_user_data = sk;
+	release_sock(sk_raw);
+out:
+	if (sock_tcp)
+		sockfd_put(sock_tcp);
+	if (error && sock_raw)
+		sock_release(sock_raw);
+	release_sock(sk);
+	return error;
+}
+
+static int pppopns_release(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+
+	if (!sk)
+		return 0;
+
+	lock_sock(sk);
+	if (sock_flag(sk, SOCK_DEAD)) {
+		release_sock(sk);
+		return -EBADF;
+	}
+
+	if (sk->sk_state != PPPOX_NONE) {
+		struct sock *sk_raw = (struct sock *)pppox_sk(sk)->chan.private;
+		lock_sock(sk_raw);
+		skb_queue_purge(&sk->sk_receive_queue);
+		pppox_unbind_sock(sk);
+		sk_raw->sk_data_ready = pppox_sk(sk)->proto.pns.data_ready;
+		sk_raw->sk_backlog_rcv = pppox_sk(sk)->proto.pns.backlog_rcv;
+		sk_raw->sk_user_data = NULL;
+		release_sock(sk_raw);
+		sock_release(sk_raw->sk_socket);
+	}
+
+	sock_orphan(sk);
+	sock->sk = NULL;
+	release_sock(sk);
+	sock_put(sk);
+	return 0;
+}
+
+/******************************************************************************/
+
+static struct proto pppopns_proto = {
+	.name = "PPPOPNS",
+	.owner = THIS_MODULE,
+	.obj_size = sizeof(struct pppox_sock),
+};
+
+static struct proto_ops pppopns_proto_ops = {
+	.family = PF_PPPOX,
+	.owner = THIS_MODULE,
+	.release = pppopns_release,
+	.bind = sock_no_bind,
+	.connect = pppopns_connect,
+	.socketpair = sock_no_socketpair,
+	.accept = sock_no_accept,
+	.getname = sock_no_getname,
+	.poll = sock_no_poll,
+	.ioctl = pppox_ioctl,
+	.listen = sock_no_listen,
+	.shutdown = sock_no_shutdown,
+	.setsockopt = sock_no_setsockopt,
+	.getsockopt = sock_no_getsockopt,
+	.sendmsg = sock_no_sendmsg,
+	.recvmsg = sock_no_recvmsg,
+	.mmap = sock_no_mmap,
+};
+
+static int pppopns_create(struct net *net, struct socket *sock)
+{
+	struct sock *sk;
+
+	sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pppopns_proto);
+	if (!sk)
+		return -ENOMEM;
+
+	sock_init_data(sock, sk);
+	sock->state = SS_UNCONNECTED;
+	sock->ops = &pppopns_proto_ops;
+	sk->sk_protocol = PX_PROTO_OPNS;
+	sk->sk_state = PPPOX_NONE;
+	return 0;
+}
+
+/******************************************************************************/
+
+static struct pppox_proto pppopns_pppox_proto = {
+	.create = pppopns_create,
+	.owner = THIS_MODULE,
+};
+
+static int __init pppopns_init(void)
+{
+	int error;
+
+	error = proto_register(&pppopns_proto, 0);
+	if (error)
+		return error;
+
+	error = register_pppox_proto(PX_PROTO_OPNS, &pppopns_pppox_proto);
+	if (error)
+		proto_unregister(&pppopns_proto);
+	else
+		skb_queue_head_init(&delivery_queue);
+	return error;
+}
+
+static void __exit pppopns_exit(void)
+{
+	unregister_pppox_proto(PX_PROTO_OPNS);
+	proto_unregister(&pppopns_proto);
+}
+
+module_init(pppopns_init);
+module_exit(pppopns_exit);
+
+MODULE_DESCRIPTION("PPP on PPTP Network Server (PPPoPNS)");
+MODULE_AUTHOR("Chia-chi Yeh <chiachi@android.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index 833e32f..4f026b0 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -483,5 +483,14 @@
 
 	  http://ubuntuforums.org/showpost.php?p=10589647&postcount=17
 
+config MSM_RMNET_USB
+	tristate "RMNET USB Driver"
+	depends on USB_USBNET
+	help
+	Select this if you have a Qualcomm modem device connected via USB
+	supporting RMNET network interface.
+
+	To compile this driver as a module, choose M here: the module
+	will be called rmnet_usb. If unsure, choose N.
 
 endmenu
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index a2e2d72..6296304 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -29,5 +29,7 @@
 obj-$(CONFIG_USB_NET_CX82310_ETH)	+= cx82310_eth.o
 obj-$(CONFIG_USB_NET_CDC_NCM)	+= cdc_ncm.o
 obj-$(CONFIG_USB_VL600)		+= lg-vl600.o
+rmnet_usb-y			:= rmnet_usb_ctrl.o rmnet_usb_data.o
+obj-$(CONFIG_MSM_RMNET_USB)	+= rmnet_usb.o
 obj-$(CONFIG_USB_NET_QMI_WWAN)	+= qmi_wwan.o
 
diff --git a/drivers/net/usb/rmnet_usb_ctrl.c b/drivers/net/usb/rmnet_usb_ctrl.c
new file mode 100644
index 0000000..23ba900
--- /dev/null
+++ b/drivers/net/usb/rmnet_usb_ctrl.c
@@ -0,0 +1,1060 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/uaccess.h>
+#include <linux/termios.h>
+#include <linux/ratelimit.h>
+#include <linux/debugfs.h>
+#include "rmnet_usb_ctrl.h"
+
+#define DEVICE_NAME			"hsicctl"
+#define NUM_CTRL_CHANNELS		4
+#define DEFAULT_READ_URB_LENGTH		0x1000
+
+/*Output control lines.*/
+#define ACM_CTRL_DTR		BIT(0)
+#define ACM_CTRL_RTS		BIT(1)
+
+
+/*Input control lines.*/
+#define ACM_CTRL_DSR		BIT(0)
+#define ACM_CTRL_CTS		BIT(1)
+#define ACM_CTRL_RI		BIT(2)
+#define ACM_CTRL_CD		BIT(3)
+
+/* polling interval for Interrupt ep */
+#define HS_INTERVAL		7
+#define FS_LS_INTERVAL		3
+
+/*echo modem_wait > /sys/class/hsicctl/hsicctlx/modem_wait*/
+static ssize_t modem_wait_store(struct device *d, struct device_attribute *attr,
+		const char *buf, size_t n)
+{
+	unsigned int		mdm_wait;
+	struct rmnet_ctrl_dev	*dev = dev_get_drvdata(d);
+
+	if (!dev)
+		return -ENODEV;
+
+	sscanf(buf, "%u", &mdm_wait);
+
+	dev->mdm_wait_timeout = mdm_wait;
+
+	return n;
+}
+
+static ssize_t modem_wait_show(struct device *d, struct device_attribute *attr,
+		char *buf)
+{
+	struct rmnet_ctrl_dev	*dev = dev_get_drvdata(d);
+
+	if (!dev)
+		return -ENODEV;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", dev->mdm_wait_timeout);
+}
+
+static DEVICE_ATTR(modem_wait, 0664, modem_wait_show, modem_wait_store);
+
+static int	ctl_msg_dbg_mask;
+module_param_named(dump_ctrl_msg, ctl_msg_dbg_mask, int,
+		S_IRUGO | S_IWUSR | S_IWGRP);
+
+enum {
+	MSM_USB_CTL_DEBUG = 1U << 0,
+	MSM_USB_CTL_DUMP_BUFFER = 1U << 1,
+};
+
+#define DUMP_BUFFER(prestr, cnt, buf) \
+do { \
+	if (ctl_msg_dbg_mask & MSM_USB_CTL_DUMP_BUFFER) \
+			print_hex_dump(KERN_INFO, prestr, DUMP_PREFIX_NONE, \
+					16, 1, buf, cnt, false); \
+} while (0)
+
+#define DBG(x...) \
+		do { \
+			if (ctl_msg_dbg_mask & MSM_USB_CTL_DEBUG) \
+				pr_info(x); \
+		} while (0)
+
+struct rmnet_ctrl_dev		*ctrl_dev[NUM_CTRL_CHANNELS];
+struct class			*ctrldev_classp;
+static dev_t			ctrldev_num;
+
+struct ctrl_pkt {
+	size_t	data_size;
+	void	*data;
+};
+
+struct ctrl_pkt_list_elem {
+	struct list_head	list;
+	struct ctrl_pkt		cpkt;
+};
+
+static void resp_avail_cb(struct urb *);
+
+static int is_dev_connected(struct rmnet_ctrl_dev *dev)
+{
+	if (dev) {
+		mutex_lock(&dev->dev_lock);
+		if (!dev->intf) {
+			mutex_unlock(&dev->dev_lock);
+			return 0;
+		}
+		mutex_unlock(&dev->dev_lock);
+		return 1;
+	}
+	return 0;
+}
+
+static void notification_available_cb(struct urb *urb)
+{
+	int				status;
+	struct usb_cdc_notification	*ctrl;
+	struct usb_device		*udev;
+	struct rmnet_ctrl_dev		*dev = urb->context;
+
+	udev = interface_to_usbdev(dev->intf);
+
+	switch (urb->status) {
+	case 0:
+		/*success*/
+		break;
+
+	/*do not resubmit*/
+	case -ESHUTDOWN:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPROTO:
+		return;
+	case -EPIPE:
+		pr_err_ratelimited("%s: Stall on int endpoint\n", __func__);
+		/* TBD : halt to be cleared in work */
+		return;
+
+	/*resubmit*/
+	case -EOVERFLOW:
+		pr_err_ratelimited("%s: Babble error happened\n", __func__);
+	default:
+		 pr_debug_ratelimited("%s: Non zero urb status = %d\n",
+			__func__, urb->status);
+		goto resubmit_int_urb;
+	}
+
+	ctrl = urb->transfer_buffer;
+
+	switch (ctrl->bNotificationType) {
+	case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:
+		dev->resp_avail_cnt++;
+		usb_fill_control_urb(dev->rcvurb, udev,
+					usb_rcvctrlpipe(udev, 0),
+					(unsigned char *)dev->in_ctlreq,
+					dev->rcvbuf,
+					DEFAULT_READ_URB_LENGTH,
+					resp_avail_cb, dev);
+
+		status = usb_submit_urb(dev->rcvurb, GFP_ATOMIC);
+		if (status) {
+			dev_err(dev->devicep,
+			"%s: Error submitting Read URB %d\n", __func__, status);
+			goto resubmit_int_urb;
+		}
+
+		if (!dev->resp_available) {
+			dev->resp_available = true;
+			wake_up(&dev->open_wait_queue);
+		}
+
+		return;
+	default:
+		 dev_err(dev->devicep,
+			"%s:Command not implemented\n", __func__);
+	}
+
+resubmit_int_urb:
+	status = usb_submit_urb(urb, GFP_ATOMIC);
+	if (status)
+		dev_err(dev->devicep, "%s: Error re-submitting Int URB %d\n",
+		__func__, status);
+
+	return;
+}
+
+static void resp_avail_cb(struct urb *urb)
+{
+	struct usb_device		*udev;
+	struct ctrl_pkt_list_elem	*list_elem = NULL;
+	struct rmnet_ctrl_dev		*dev = urb->context;
+	void				*cpkt;
+	int				status = 0;
+	size_t				cpkt_size = 0;
+
+	udev = interface_to_usbdev(dev->intf);
+
+	switch (urb->status) {
+	case 0:
+		/*success*/
+		dev->get_encap_resp_cnt++;
+		break;
+
+	/*do not resubmit*/
+	case -ESHUTDOWN:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPROTO:
+		return;
+
+	/*resubmit*/
+	case -EOVERFLOW:
+		pr_err_ratelimited("%s: Babble error happened\n", __func__);
+	default:
+		pr_debug_ratelimited("%s: Non zero urb status = %d\n",
+				__func__, urb->status);
+		goto resubmit_int_urb;
+	}
+
+	dev_dbg(dev->devicep, "Read %d bytes for %s\n",
+		urb->actual_length, dev->name);
+
+	cpkt = urb->transfer_buffer;
+	cpkt_size = urb->actual_length;
+	if (!cpkt_size) {
+		dev->zlp_cnt++;
+		dev_dbg(dev->devicep, "%s: zero length pkt received\n",
+				__func__);
+		goto resubmit_int_urb;
+	}
+
+	list_elem = kmalloc(sizeof(struct ctrl_pkt_list_elem), GFP_ATOMIC);
+	if (!list_elem) {
+		dev_err(dev->devicep, "%s: list_elem alloc failed\n", __func__);
+		return;
+	}
+	list_elem->cpkt.data = kmalloc(cpkt_size, GFP_ATOMIC);
+	if (!list_elem->cpkt.data) {
+		dev_err(dev->devicep, "%s: list_elem->data alloc failed\n",
+			__func__);
+		kfree(list_elem);
+		return;
+	}
+	memcpy(list_elem->cpkt.data, cpkt, cpkt_size);
+	list_elem->cpkt.data_size = cpkt_size;
+	spin_lock(&dev->rx_lock);
+	list_add_tail(&list_elem->list, &dev->rx_list);
+	spin_unlock(&dev->rx_lock);
+
+	wake_up(&dev->read_wait_queue);
+
+resubmit_int_urb:
+	/*re-submit int urb to check response available*/
+	status = usb_submit_urb(dev->inturb, GFP_ATOMIC);
+	if (status)
+		dev_err(dev->devicep, "%s: Error re-submitting Int URB %d\n",
+			__func__, status);
+}
+
+static int rmnet_usb_ctrl_start_rx(struct rmnet_ctrl_dev *dev)
+{
+	int	retval = 0;
+
+	retval = usb_submit_urb(dev->inturb, GFP_KERNEL);
+	if (retval < 0)
+		dev_err(dev->devicep, "%s Intr submit %d\n", __func__, retval);
+
+	return retval;
+}
+
+int rmnet_usb_ctrl_stop_rx(struct rmnet_ctrl_dev *dev)
+{
+	if (!is_dev_connected(dev)) {
+		dev_dbg(dev->devicep, "%s: Ctrl device disconnected\n",
+			__func__);
+		return -ENODEV;
+	}
+
+	dev_dbg(dev->devicep, "%s\n", __func__);
+
+	usb_kill_urb(dev->rcvurb);
+	usb_kill_urb(dev->inturb);
+
+	return 0;
+}
+
+int rmnet_usb_ctrl_start(struct rmnet_ctrl_dev *dev)
+{
+	int	status = 0;
+
+	mutex_lock(&dev->dev_lock);
+	if (dev->is_opened)
+		status = rmnet_usb_ctrl_start_rx(dev);
+	mutex_unlock(&dev->dev_lock);
+
+	return status;
+}
+
+static int rmnet_usb_ctrl_alloc_rx(struct rmnet_ctrl_dev *dev)
+{
+	int	retval = -ENOMEM;
+
+	dev->rcvurb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->rcvurb) {
+		pr_err("%s: Error allocating read urb\n", __func__);
+		goto nomem;
+	}
+
+	dev->rcvbuf = kmalloc(DEFAULT_READ_URB_LENGTH, GFP_KERNEL);
+	if (!dev->rcvbuf) {
+		pr_err("%s: Error allocating read buffer\n", __func__);
+		goto nomem;
+	}
+
+	dev->in_ctlreq = kmalloc(sizeof(*dev->in_ctlreq), GFP_KERNEL);
+	if (!dev->in_ctlreq) {
+		pr_err("%s: Error allocating setup packet buffer\n", __func__);
+		goto nomem;
+	}
+
+	return 0;
+
+nomem:
+	usb_free_urb(dev->rcvurb);
+	kfree(dev->rcvbuf);
+	kfree(dev->in_ctlreq);
+
+	return retval;
+
+}
+static int rmnet_usb_ctrl_write_cmd(struct rmnet_ctrl_dev *dev)
+{
+	struct usb_device	*udev;
+
+	if (!is_dev_connected(dev))
+		return -ENODEV;
+
+	udev = interface_to_usbdev(dev->intf);
+	dev->set_ctrl_line_state_cnt++;
+	return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+		USB_CDC_REQ_SET_CONTROL_LINE_STATE,
+		(USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE),
+		dev->cbits_tomdm,
+		dev->intf->cur_altsetting->desc.bInterfaceNumber,
+		NULL, 0, USB_CTRL_SET_TIMEOUT);
+}
+
+static void ctrl_write_callback(struct urb *urb)
+{
+	struct rmnet_ctrl_dev	*dev = urb->context;
+
+	if (urb->status) {
+		dev->tx_ctrl_err_cnt++;
+		pr_debug_ratelimited("Write status/size %d/%d\n",
+				urb->status, urb->actual_length);
+	}
+
+	kfree(urb->setup_packet);
+	kfree(urb->transfer_buffer);
+	usb_free_urb(urb);
+	usb_autopm_put_interface_async(dev->intf);
+}
+
+static int rmnet_usb_ctrl_write(struct rmnet_ctrl_dev *dev, char *buf,
+		size_t size)
+{
+	int			result;
+	struct urb		*sndurb;
+	struct usb_ctrlrequest	*out_ctlreq;
+	struct usb_device	*udev;
+
+	if (!is_dev_connected(dev))
+		return -ENETRESET;
+
+	udev = interface_to_usbdev(dev->intf);
+
+	sndurb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!sndurb) {
+		dev_err(dev->devicep, "Error allocating read urb\n");
+		return -ENOMEM;
+	}
+
+	out_ctlreq = kmalloc(sizeof(*out_ctlreq), GFP_KERNEL);
+	if (!out_ctlreq) {
+		usb_free_urb(sndurb);
+		dev_err(dev->devicep, "Error allocating setup packet buffer\n");
+		return -ENOMEM;
+	}
+
+	/* CDC Send Encapsulated Request packet */
+	out_ctlreq->bRequestType = (USB_DIR_OUT | USB_TYPE_CLASS |
+			     USB_RECIP_INTERFACE);
+	out_ctlreq->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND;
+	out_ctlreq->wValue = 0;
+	out_ctlreq->wIndex = dev->intf->cur_altsetting->desc.bInterfaceNumber;
+	out_ctlreq->wLength = cpu_to_le16(size);
+
+	usb_fill_control_urb(sndurb, udev,
+			     usb_sndctrlpipe(udev, 0),
+			     (unsigned char *)out_ctlreq, (void *)buf, size,
+			     ctrl_write_callback, dev);
+
+	result = usb_autopm_get_interface(dev->intf);
+	if (result < 0) {
+		dev_err(dev->devicep, "%s: Unable to resume interface: %d\n",
+			__func__, result);
+
+		/*
+		* Revisit:  if (result == -EPERM)
+		*		rmnet_usb_suspend(dev->intf, PMSG_SUSPEND);
+		*/
+
+		usb_free_urb(sndurb);
+		kfree(out_ctlreq);
+		return result;
+	}
+
+	usb_anchor_urb(sndurb, &dev->tx_submitted);
+	dev->snd_encap_cmd_cnt++;
+	result = usb_submit_urb(sndurb, GFP_KERNEL);
+	if (result < 0) {
+		dev_err(dev->devicep, "%s: Submit URB error %d\n",
+			__func__, result);
+		dev->snd_encap_cmd_cnt--;
+		usb_autopm_put_interface(dev->intf);
+		usb_unanchor_urb(sndurb);
+		usb_free_urb(sndurb);
+		kfree(out_ctlreq);
+		return result;
+	}
+
+	return size;
+}
+
+static int rmnet_ctl_open(struct inode *inode, struct file *file)
+{
+	int			retval = 0;
+	struct rmnet_ctrl_dev	*dev =
+		container_of(inode->i_cdev, struct rmnet_ctrl_dev, cdev);
+
+	if (!dev)
+		return -ENODEV;
+
+	if (dev->is_opened)
+		goto already_opened;
+
+	/*block open to get first response available from mdm*/
+	if (dev->mdm_wait_timeout && !dev->resp_available) {
+		retval = wait_event_interruptible_timeout(
+					dev->open_wait_queue,
+					dev->resp_available,
+					msecs_to_jiffies(dev->mdm_wait_timeout *
+									1000));
+		if (retval == 0) {
+			dev_err(dev->devicep, "%s: Timeout opening %s\n",
+						__func__, dev->name);
+			return -ETIMEDOUT;
+		} else if (retval < 0) {
+			dev_err(dev->devicep, "%s: Error waiting for %s\n",
+						__func__, dev->name);
+			return retval;
+		}
+	}
+
+	if (!dev->resp_available) {
+		dev_dbg(dev->devicep, "%s: Connection timedout opening %s\n",
+					__func__, dev->name);
+		return -ETIMEDOUT;
+	}
+
+	mutex_lock(&dev->dev_lock);
+	dev->is_opened = 1;
+	mutex_unlock(&dev->dev_lock);
+
+	file->private_data = dev;
+
+already_opened:
+	DBG("%s: Open called for %s\n", __func__, dev->name);
+
+	return 0;
+}
+
+static int rmnet_ctl_release(struct inode *inode, struct file *file)
+{
+	struct ctrl_pkt_list_elem	*list_elem = NULL;
+	struct rmnet_ctrl_dev		*dev;
+	unsigned long			flag;
+
+	dev = file->private_data;
+	if (!dev)
+		return -ENODEV;
+
+	DBG("%s Called on %s device\n", __func__, dev->name);
+
+	spin_lock_irqsave(&dev->rx_lock, flag);
+	while (!list_empty(&dev->rx_list)) {
+		list_elem = list_first_entry(
+				&dev->rx_list,
+				struct ctrl_pkt_list_elem,
+				list);
+		list_del(&list_elem->list);
+		kfree(list_elem->cpkt.data);
+		kfree(list_elem);
+	}
+	spin_unlock_irqrestore(&dev->rx_lock, flag);
+
+	mutex_lock(&dev->dev_lock);
+	dev->is_opened = 0;
+	mutex_unlock(&dev->dev_lock);
+
+	rmnet_usb_ctrl_stop_rx(dev);
+
+	if (is_dev_connected(dev))
+		usb_kill_anchored_urbs(&dev->tx_submitted);
+
+	file->private_data = NULL;
+
+	return 0;
+}
+
+static ssize_t rmnet_ctl_read(struct file *file, char __user *buf, size_t count,
+		loff_t *ppos)
+{
+	int				retval = 0;
+	int				bytes_to_read;
+	struct rmnet_ctrl_dev		*dev;
+	struct ctrl_pkt_list_elem	*list_elem = NULL;
+	unsigned long			flags;
+
+	dev = file->private_data;
+	if (!dev)
+		return -ENODEV;
+
+	DBG("%s: Read from %s\n", __func__, dev->name);
+
+ctrl_read:
+	if (!is_dev_connected(dev)) {
+		dev_dbg(dev->devicep, "%s: Device not connected\n",
+			__func__);
+		return -ENETRESET;
+	}
+	spin_lock_irqsave(&dev->rx_lock, flags);
+	if (list_empty(&dev->rx_list)) {
+		spin_unlock_irqrestore(&dev->rx_lock, flags);
+
+		retval = wait_event_interruptible(dev->read_wait_queue,
+					!list_empty(&dev->rx_list) ||
+					!is_dev_connected(dev));
+		if (retval < 0)
+			return retval;
+
+		goto ctrl_read;
+	}
+
+	list_elem = list_first_entry(&dev->rx_list,
+				     struct ctrl_pkt_list_elem, list);
+	bytes_to_read = (uint32_t)(list_elem->cpkt.data_size);
+	if (bytes_to_read > count) {
+		spin_unlock_irqrestore(&dev->rx_lock, flags);
+		dev_err(dev->devicep, "%s: Packet size %d > buf size %d\n",
+			__func__, bytes_to_read, count);
+		return -ENOMEM;
+	}
+	spin_unlock_irqrestore(&dev->rx_lock, flags);
+
+	if (copy_to_user(buf, list_elem->cpkt.data, bytes_to_read)) {
+			dev_err(dev->devicep,
+				"%s: copy_to_user failed for %s\n",
+				__func__, dev->name);
+		return -EFAULT;
+	}
+	spin_lock_irqsave(&dev->rx_lock, flags);
+	list_del(&list_elem->list);
+	spin_unlock_irqrestore(&dev->rx_lock, flags);
+
+	kfree(list_elem->cpkt.data);
+	kfree(list_elem);
+	DBG("%s: Returning %d bytes to %s\n", __func__, bytes_to_read,
+			dev->name);
+	DUMP_BUFFER("Read: ", bytes_to_read, buf);
+
+	return bytes_to_read;
+}
+
+static ssize_t rmnet_ctl_write(struct file *file, const char __user * buf,
+		size_t size, loff_t *pos)
+{
+	int			status;
+	void			*wbuf;
+	struct rmnet_ctrl_dev	*dev = file->private_data;
+
+	if (!dev)
+		return -ENODEV;
+
+	if (size <= 0)
+		return -EINVAL;
+
+	if (!is_dev_connected(dev))
+		return -ENETRESET;
+
+	DBG("%s: Writing %i bytes on %s\n", __func__, size, dev->name);
+
+	wbuf = kmalloc(size , GFP_KERNEL);
+	if (!wbuf)
+		return -ENOMEM;
+
+	status = copy_from_user(wbuf , buf, size);
+	if (status) {
+		dev_err(dev->devicep,
+		"%s: Unable to copy data from userspace %d\n",
+		__func__, status);
+		kfree(wbuf);
+		return status;
+	}
+	DUMP_BUFFER("Write: ", size, buf);
+
+	status = rmnet_usb_ctrl_write(dev, wbuf, size);
+	if (status == size)
+		return size;
+
+	return status;
+}
+
+static int rmnet_ctrl_tiocmset(struct rmnet_ctrl_dev *dev, unsigned int set,
+		unsigned int clear)
+{
+	int retval;
+
+	mutex_lock(&dev->dev_lock);
+	if (set & TIOCM_DTR)
+		dev->cbits_tomdm |= ACM_CTRL_DTR;
+
+	/*
+	 * TBD if (set & TIOCM_RTS)
+	 *	dev->cbits_tomdm |= ACM_CTRL_RTS;
+	 */
+
+	if (clear & TIOCM_DTR)
+		dev->cbits_tomdm &= ~ACM_CTRL_DTR;
+
+	/*
+	 * (clear & TIOCM_RTS)
+	 *	dev->cbits_tomdm &= ~ACM_CTRL_RTS;
+	 */
+
+	mutex_unlock(&dev->dev_lock);
+
+	retval = usb_autopm_get_interface(dev->intf);
+	if (retval < 0) {
+		dev_err(dev->devicep, "%s: Unable to resume interface: %d\n",
+			__func__, retval);
+		return retval;
+	}
+
+	retval = rmnet_usb_ctrl_write_cmd(dev);
+
+	usb_autopm_put_interface(dev->intf);
+	return retval;
+}
+
+static int rmnet_ctrl_tiocmget(struct rmnet_ctrl_dev *dev)
+{
+	int	ret;
+
+	mutex_lock(&dev->dev_lock);
+	ret =
+	/*
+	 * TBD(dev->cbits_tolocal & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
+	 * (dev->cbits_tolocal & ACM_CTRL_CTS ? TIOCM_CTS : 0) |
+	 */
+	(dev->cbits_tolocal & ACM_CTRL_CD ? TIOCM_CD : 0) |
+	/*
+	 * TBD (dev->cbits_tolocal & ACM_CTRL_RI ? TIOCM_RI : 0) |
+	 *(dev->cbits_tomdm & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
+	*/
+	(dev->cbits_tomdm & ACM_CTRL_DTR ? TIOCM_DTR : 0);
+	mutex_unlock(&dev->dev_lock);
+
+	return ret;
+}
+
+static long rmnet_ctrl_ioctl(struct file *file, unsigned int cmd,
+		unsigned long arg)
+{
+	int			ret;
+	struct rmnet_ctrl_dev	*dev;
+
+	dev = file->private_data;
+	if (!dev)
+		return -ENODEV;
+
+	switch (cmd) {
+	case TIOCMGET:
+
+		ret = rmnet_ctrl_tiocmget(dev);
+		break;
+	case TIOCMSET:
+		ret = rmnet_ctrl_tiocmset(dev, arg, ~arg);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static const struct file_operations ctrldev_fops = {
+	.owner = THIS_MODULE,
+	.read  = rmnet_ctl_read,
+	.write = rmnet_ctl_write,
+	.unlocked_ioctl = rmnet_ctrl_ioctl,
+	.open  = rmnet_ctl_open,
+	.release = rmnet_ctl_release,
+};
+
+int rmnet_usb_ctrl_probe(struct usb_interface *intf,
+		struct usb_host_endpoint *int_in, struct rmnet_ctrl_dev *dev)
+{
+	u16				wMaxPacketSize;
+	struct usb_endpoint_descriptor	*ep;
+	struct usb_device		*udev;
+	int				interval;
+	int				ret = 0;
+
+	udev = interface_to_usbdev(intf);
+
+	if (!dev) {
+		pr_err("%s: Ctrl device not found\n", __func__);
+		return -ENODEV;
+	}
+	dev->int_pipe = usb_rcvintpipe(udev,
+		int_in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+
+	mutex_lock(&dev->dev_lock);
+	dev->intf = intf;
+
+	/*TBD: for now just update CD status*/
+	dev->cbits_tolocal = ACM_CTRL_CD;
+
+	/*send DTR high to modem*/
+	dev->cbits_tomdm = ACM_CTRL_DTR;
+	mutex_unlock(&dev->dev_lock);
+
+	dev->resp_available = false;
+	dev->snd_encap_cmd_cnt = 0;
+	dev->get_encap_resp_cnt = 0;
+	dev->resp_avail_cnt = 0;
+	dev->tx_ctrl_err_cnt = 0;
+	dev->set_ctrl_line_state_cnt = 0;
+
+	ret = rmnet_usb_ctrl_write_cmd(dev);
+	if (ret < 0)
+		return ret;
+
+	dev->inturb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->inturb) {
+		dev_err(dev->devicep, "Error allocating int urb\n");
+		return -ENOMEM;
+	}
+
+	/*use max pkt size from ep desc*/
+	ep = &dev->intf->cur_altsetting->endpoint[0].desc;
+	wMaxPacketSize = le16_to_cpu(ep->wMaxPacketSize);
+
+	dev->intbuf = kmalloc(wMaxPacketSize, GFP_KERNEL);
+	if (!dev->intbuf) {
+		usb_free_urb(dev->inturb);
+		dev_err(dev->devicep, "Error allocating int buffer\n");
+		return -ENOMEM;
+	}
+
+	dev->in_ctlreq->bRequestType =
+		(USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
+	dev->in_ctlreq->bRequest  = USB_CDC_GET_ENCAPSULATED_RESPONSE;
+	dev->in_ctlreq->wValue = 0;
+	dev->in_ctlreq->wIndex =
+		dev->intf->cur_altsetting->desc.bInterfaceNumber;
+	dev->in_ctlreq->wLength = cpu_to_le16(DEFAULT_READ_URB_LENGTH);
+
+	interval = max((int)int_in->desc.bInterval,
+			(udev->speed == USB_SPEED_HIGH) ? HS_INTERVAL
+							: FS_LS_INTERVAL);
+
+	usb_fill_int_urb(dev->inturb, udev,
+			 dev->int_pipe,
+			 dev->intbuf, wMaxPacketSize,
+			 notification_available_cb, dev, interval);
+
+	return rmnet_usb_ctrl_start_rx(dev);
+}
+
+void rmnet_usb_ctrl_disconnect(struct rmnet_ctrl_dev *dev)
+{
+	rmnet_usb_ctrl_stop_rx(dev);
+
+	mutex_lock(&dev->dev_lock);
+
+	/*TBD: for now just update CD status*/
+	dev->cbits_tolocal = ~ACM_CTRL_CD;
+
+	dev->cbits_tomdm = ~ACM_CTRL_DTR;
+	dev->intf = NULL;
+	mutex_unlock(&dev->dev_lock);
+
+	wake_up(&dev->read_wait_queue);
+
+	usb_free_urb(dev->inturb);
+	dev->inturb = NULL;
+
+	kfree(dev->intbuf);
+	dev->intbuf = NULL;
+
+	usb_kill_anchored_urbs(&dev->tx_submitted);
+}
+
+#if defined(CONFIG_DEBUG_FS)
+#define DEBUG_BUF_SIZE	4096
+static ssize_t rmnet_usb_ctrl_read_stats(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	struct rmnet_ctrl_dev	*dev;
+	char			*buf;
+	int			ret;
+	int			i;
+	int			temp = 0;
+
+	buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	for (i = 0; i < NUM_CTRL_CHANNELS; i++) {
+		dev = ctrl_dev[i];
+		if (!dev)
+			continue;
+
+		temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+				"\n#ctrl_dev: %p     Name: %s#\n"
+				"snd encap cmd cnt         %u\n"
+				"resp avail cnt:           %u\n"
+				"get encap resp cnt:       %u\n"
+				"set ctrl line state cnt:  %u\n"
+				"tx_err_cnt:               %u\n"
+				"cbits_tolocal:            %d\n"
+				"cbits_tomdm:              %d\n"
+				"mdm_wait_timeout:         %u\n"
+				"zlp_cnt:                  %u\n"
+				"dev opened:               %s\n",
+				dev, dev->name,
+				dev->snd_encap_cmd_cnt,
+				dev->resp_avail_cnt,
+				dev->get_encap_resp_cnt,
+				dev->set_ctrl_line_state_cnt,
+				dev->tx_ctrl_err_cnt,
+				dev->cbits_tolocal,
+				dev->cbits_tomdm,
+				dev->mdm_wait_timeout,
+				dev->zlp_cnt,
+				dev->is_opened ? "OPEN" : "CLOSE");
+
+	}
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+	kfree(buf);
+
+	return ret;
+}
+
+static ssize_t rmnet_usb_ctrl_reset_stats(struct file *file, const char __user *
+		buf, size_t count, loff_t *ppos)
+{
+	struct rmnet_ctrl_dev	*dev;
+	int			i;
+
+	for (i = 0; i < NUM_CTRL_CHANNELS; i++) {
+		dev = ctrl_dev[i];
+		if (!dev)
+			continue;
+
+		dev->snd_encap_cmd_cnt = 0;
+		dev->resp_avail_cnt = 0;
+		dev->get_encap_resp_cnt = 0;
+		dev->set_ctrl_line_state_cnt = 0;
+		dev->tx_ctrl_err_cnt = 0;
+		dev->zlp_cnt = 0;
+	}
+	return count;
+}
+
+const struct file_operations rmnet_usb_ctrl_stats_ops = {
+	.read = rmnet_usb_ctrl_read_stats,
+	.write = rmnet_usb_ctrl_reset_stats,
+};
+
+struct dentry	*usb_ctrl_dent;
+struct dentry	*usb_ctrl_dfile;
+static void rmnet_usb_ctrl_debugfs_init(void)
+{
+	usb_ctrl_dent = debugfs_create_dir("rmnet_usb_ctrl", 0);
+	if (IS_ERR(usb_ctrl_dent))
+		return;
+
+	usb_ctrl_dfile = debugfs_create_file("status", 0644, usb_ctrl_dent, 0,
+			&rmnet_usb_ctrl_stats_ops);
+	if (!usb_ctrl_dfile || IS_ERR(usb_ctrl_dfile))
+		debugfs_remove(usb_ctrl_dent);
+}
+
+static void rmnet_usb_ctrl_debugfs_exit(void)
+{
+	debugfs_remove(usb_ctrl_dfile);
+	debugfs_remove(usb_ctrl_dent);
+}
+
+#else
+static void rmnet_usb_ctrl_debugfs_init(void) { }
+static void rmnet_usb_ctrl_debugfs_exit(void) { }
+#endif
+
+int rmnet_usb_ctrl_init(void)
+{
+	struct rmnet_ctrl_dev	*dev;
+	int			n;
+	int			status;
+
+	for (n = 0; n < NUM_CTRL_CHANNELS; ++n) {
+
+		dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+		if (!dev) {
+			status = -ENOMEM;
+			goto error0;
+		}
+		/*for debug purpose*/
+		snprintf(dev->name, CTRL_DEV_MAX_LEN, "hsicctl%d", n);
+
+		mutex_init(&dev->dev_lock);
+		spin_lock_init(&dev->rx_lock);
+		init_waitqueue_head(&dev->read_wait_queue);
+		init_waitqueue_head(&dev->open_wait_queue);
+		INIT_LIST_HEAD(&dev->rx_list);
+		init_usb_anchor(&dev->tx_submitted);
+
+		status = rmnet_usb_ctrl_alloc_rx(dev);
+		if (status < 0) {
+			kfree(dev);
+			goto error0;
+		}
+
+		ctrl_dev[n] = dev;
+	}
+
+	status = alloc_chrdev_region(&ctrldev_num, 0, NUM_CTRL_CHANNELS,
+			DEVICE_NAME);
+	if (IS_ERR_VALUE(status)) {
+		pr_err("ERROR:%s: alloc_chrdev_region() ret %i.\n",
+		       __func__, status);
+		goto error0;
+	}
+
+	ctrldev_classp = class_create(THIS_MODULE, DEVICE_NAME);
+	if (IS_ERR(ctrldev_classp)) {
+		pr_err("ERROR:%s: class_create() ENOMEM\n", __func__);
+		status = -ENOMEM;
+		goto error1;
+	}
+	for (n = 0; n < NUM_CTRL_CHANNELS; ++n) {
+		cdev_init(&ctrl_dev[n]->cdev, &ctrldev_fops);
+		ctrl_dev[n]->cdev.owner = THIS_MODULE;
+
+		status = cdev_add(&ctrl_dev[n]->cdev, (ctrldev_num + n), 1);
+
+		if (IS_ERR_VALUE(status)) {
+			pr_err("%s: cdev_add() ret %i\n", __func__, status);
+			kfree(ctrl_dev[n]);
+			goto error2;
+		}
+
+		ctrl_dev[n]->devicep =
+				device_create(ctrldev_classp, NULL,
+				(ctrldev_num + n), NULL,
+				DEVICE_NAME "%d", n);
+
+		if (IS_ERR(ctrl_dev[n]->devicep)) {
+			pr_err("%s: device_create() ENOMEM\n", __func__);
+			status = -ENOMEM;
+			cdev_del(&ctrl_dev[n]->cdev);
+			kfree(ctrl_dev[n]);
+			goto error2;
+		}
+		/*create /sys/class/hsicctl/hsicctlx/modem_wait*/
+		status = device_create_file(ctrl_dev[n]->devicep,
+					&dev_attr_modem_wait);
+		if (status) {
+			device_destroy(ctrldev_classp,
+				MKDEV(MAJOR(ctrldev_num), n));
+			cdev_del(&ctrl_dev[n]->cdev);
+			kfree(ctrl_dev[n]);
+			goto error2;
+		}
+		dev_set_drvdata(ctrl_dev[n]->devicep, ctrl_dev[n]);
+	}
+
+	rmnet_usb_ctrl_debugfs_init();
+	pr_info("rmnet usb ctrl Initialized.\n");
+	return 0;
+
+error2:
+		while (--n >= 0) {
+			cdev_del(&ctrl_dev[n]->cdev);
+			device_destroy(ctrldev_classp,
+				MKDEV(MAJOR(ctrldev_num), n));
+		}
+
+		class_destroy(ctrldev_classp);
+		n = NUM_CTRL_CHANNELS;
+error1:
+	unregister_chrdev_region(MAJOR(ctrldev_num), NUM_CTRL_CHANNELS);
+error0:
+	while (--n >= 0)
+		kfree(ctrl_dev[n]);
+
+	return status;
+}
+
+void rmnet_usb_ctrl_exit(void)
+{
+	int	i;
+
+	for (i = 0; i < NUM_CTRL_CHANNELS; ++i) {
+		if (!ctrl_dev[i])
+			return;
+
+		kfree(ctrl_dev[i]->in_ctlreq);
+		kfree(ctrl_dev[i]->rcvbuf);
+		kfree(ctrl_dev[i]->intbuf);
+		usb_free_urb(ctrl_dev[i]->rcvurb);
+		usb_free_urb(ctrl_dev[i]->inturb);
+#if defined(DEBUG)
+		device_remove_file(ctrl_dev[i]->devicep, &dev_attr_modem_wait);
+#endif
+		cdev_del(&ctrl_dev[i]->cdev);
+		kfree(ctrl_dev[i]);
+		ctrl_dev[i] = NULL;
+		device_destroy(ctrldev_classp, MKDEV(MAJOR(ctrldev_num), i));
+	}
+
+	class_destroy(ctrldev_classp);
+	unregister_chrdev_region(MAJOR(ctrldev_num), NUM_CTRL_CHANNELS);
+	rmnet_usb_ctrl_debugfs_exit();
+}
diff --git a/drivers/net/usb/rmnet_usb_ctrl.h b/drivers/net/usb/rmnet_usb_ctrl.h
new file mode 100644
index 0000000..bc07726
--- /dev/null
+++ b/drivers/net/usb/rmnet_usb_ctrl.h
@@ -0,0 +1,83 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __RMNET_USB_CTRL_H
+#define __RMNET_USB_CTRL_H
+
+#include <linux/mutex.h>
+#include <linux/usb.h>
+#include <linux/cdev.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/cdc.h>
+
+#define CTRL_DEV_MAX_LEN 10
+
+struct rmnet_ctrl_dev {
+
+	/*for debugging purpose*/
+	char			name[CTRL_DEV_MAX_LEN];
+
+	struct cdev		cdev;
+	struct device		*devicep;
+
+	struct usb_interface	*intf;
+	unsigned int		int_pipe;
+	struct urb		*rcvurb;
+	struct urb		*inturb;
+	struct usb_anchor	tx_submitted;
+	void			*rcvbuf;
+	void			*intbuf;
+	struct usb_ctrlrequest	*in_ctlreq;
+
+	spinlock_t		rx_lock;
+	struct mutex		dev_lock;
+	struct list_head	rx_list;
+	wait_queue_head_t	read_wait_queue;
+	wait_queue_head_t	open_wait_queue;
+
+	unsigned		is_opened;
+
+	/*input control lines (DSR, CTS, CD, RI)*/
+	unsigned int		cbits_tolocal;
+
+	/*output control lines (DTR, RTS)*/
+	unsigned int		cbits_tomdm;
+
+	/*
+	 * track first resp available from mdm when it boots up
+	 * to avoid bigger  timeout value used by qmuxd
+	 */
+	bool			resp_available;
+
+	unsigned int		mdm_wait_timeout;
+
+	/*counters*/
+	unsigned int		snd_encap_cmd_cnt;
+	unsigned int		get_encap_resp_cnt;
+	unsigned int		resp_avail_cnt;
+	unsigned int		set_ctrl_line_state_cnt;
+	unsigned int		tx_ctrl_err_cnt;
+	unsigned int		zlp_cnt;
+};
+
+extern struct rmnet_ctrl_dev *ctrl_dev[];
+
+extern int rmnet_usb_ctrl_start(struct rmnet_ctrl_dev *);
+extern int rmnet_usb_ctrl_stop_rx(struct rmnet_ctrl_dev *);
+extern int rmnet_usb_ctrl_init(void);
+extern void rmnet_usb_ctrl_exit(void);
+extern int rmnet_usb_ctrl_probe(struct usb_interface *intf,
+		struct usb_host_endpoint *status,
+		struct rmnet_ctrl_dev *dev);
+extern void rmnet_usb_ctrl_disconnect(struct rmnet_ctrl_dev *);
+
+#endif /* __RMNET_USB_H*/
diff --git a/drivers/net/usb/rmnet_usb_data.c b/drivers/net/usb/rmnet_usb_data.c
new file mode 100644
index 0000000..2183ee6
--- /dev/null
+++ b/drivers/net/usb/rmnet_usb_data.c
@@ -0,0 +1,688 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/mii.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/usb.h>
+#include <linux/usb/usbnet.h>
+#include <linux/msm_rmnet.h>
+
+#include "rmnet_usb_ctrl.h"
+
+#define RMNET_DATA_LEN			2000
+#define HEADROOM_FOR_QOS		8
+
+static int	data_msg_dbg_mask;
+
+enum {
+	DEBUG_MASK_LVL0 = 1U << 0,
+	DEBUG_MASK_LVL1 = 1U << 1,
+	DEBUG_MASK_LVL2 = 1U << 2,
+};
+
+#define DBG(m, x...) do { \
+		if (data_msg_dbg_mask & m) \
+			pr_info(x); \
+} while (0)
+
+/*echo dbg_mask > /sys/class/net/rmnet_usbx/dbg_mask*/
+static ssize_t dbg_mask_store(struct device *d,
+		struct device_attribute *attr,
+		const char *buf, size_t n)
+{
+	unsigned int		dbg_mask;
+	struct net_device	*dev = to_net_dev(d);
+	struct usbnet		*unet = netdev_priv(dev);
+
+	if (!dev)
+		return -ENODEV;
+
+	sscanf(buf, "%u", &dbg_mask);
+	/*enable dbg msgs for data driver*/
+	data_msg_dbg_mask = dbg_mask;
+
+	/*set default msg level*/
+	unet->msg_enable = NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK;
+
+	/*enable netif_xxx msgs*/
+	if (dbg_mask & DEBUG_MASK_LVL0)
+		unet->msg_enable |= NETIF_MSG_IFUP | NETIF_MSG_IFDOWN;
+	if (dbg_mask & DEBUG_MASK_LVL1)
+		unet->msg_enable |= NETIF_MSG_TX_ERR | NETIF_MSG_RX_ERR
+			| NETIF_MSG_TX_QUEUED | NETIF_MSG_TX_DONE
+			| NETIF_MSG_RX_STATUS;
+
+	return n;
+}
+
+static ssize_t dbg_mask_show(struct device *d,
+		struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", data_msg_dbg_mask);
+}
+
+static DEVICE_ATTR(dbg_mask, 0644, dbg_mask_show, dbg_mask_store);
+
+#define DBG0(x...) DBG(DEBUG_MASK_LVL0, x)
+#define DBG1(x...) DBG(DEBUG_MASK_LVL1, x)
+#define DBG2(x...) DBG(DEBUG_MASK_LVL2, x)
+
+static void rmnet_usb_setup(struct net_device *);
+static int rmnet_ioctl(struct net_device *, struct ifreq *, int);
+
+static int rmnet_usb_suspend(struct usb_interface *iface, pm_message_t message)
+{
+	struct usbnet		*unet;
+	struct rmnet_ctrl_dev	*dev;
+	int			time = 0;
+	int			retval = 0;
+
+	unet = usb_get_intfdata(iface);
+	if (!unet) {
+		pr_err("%s:data device not found\n", __func__);
+		retval = -ENODEV;
+		goto fail;
+	}
+
+	dev = (struct rmnet_ctrl_dev *)unet->data[1];
+	if (!dev) {
+		dev_err(&unet->udev->dev, "%s: ctrl device not found\n",
+				__func__);
+		retval = -ENODEV;
+		goto fail;
+	}
+
+	retval = usbnet_suspend(iface, message);
+	if (!retval) {
+		if (message.event & PM_EVENT_SUSPEND) {
+			time = usb_wait_anchor_empty_timeout(&dev->tx_submitted,
+								1000);
+			if (!time)
+				usb_kill_anchored_urbs(&dev->tx_submitted);
+
+			retval = rmnet_usb_ctrl_stop_rx(dev);
+			iface->dev.power.power_state.event = message.event;
+		}
+		/*  TBD : do we need to set/clear usbnet->udev->reset_resume*/
+		} else
+		dev_dbg(&unet->udev->dev,
+			"%s: device is busy can not suspend\n", __func__);
+
+fail:
+	return retval;
+}
+
+static int rmnet_usb_resume(struct usb_interface *iface)
+{
+	int			retval = 0;
+	int			oldstate;
+	struct usbnet		*unet;
+	struct rmnet_ctrl_dev	*dev;
+
+	unet = usb_get_intfdata(iface);
+	if (!unet) {
+		pr_err("%s:data device not found\n", __func__);
+		retval = -ENODEV;
+		goto fail;
+	}
+
+	dev = (struct rmnet_ctrl_dev *)unet->data[1];
+	if (!dev) {
+		dev_err(&unet->udev->dev, "%s: ctrl device not found\n",
+				__func__);
+		retval = -ENODEV;
+		goto fail;
+	}
+	oldstate = iface->dev.power.power_state.event;
+	iface->dev.power.power_state.event = PM_EVENT_ON;
+
+	retval = usbnet_resume(iface);
+	if (!retval) {
+		if (oldstate & PM_EVENT_SUSPEND)
+			retval = rmnet_usb_ctrl_start(dev);
+	}
+fail:
+	return retval;
+}
+
+static int rmnet_usb_bind(struct usbnet *usbnet, struct usb_interface *iface)
+{
+	struct usb_host_endpoint	*endpoint = NULL;
+	struct usb_host_endpoint	*bulk_in = NULL;
+	struct usb_host_endpoint	*bulk_out = NULL;
+	struct usb_host_endpoint	*int_in = NULL;
+	struct usb_device		*udev;
+	int				status = 0;
+	int				i;
+	int				numends;
+
+	udev = interface_to_usbdev(iface);
+	numends = iface->cur_altsetting->desc.bNumEndpoints;
+	for (i = 0; i < numends; i++) {
+		endpoint = iface->cur_altsetting->endpoint + i;
+		if (!endpoint) {
+			dev_err(&udev->dev, "%s: invalid endpoint %u\n",
+				__func__, i);
+			status = -EINVAL;
+			goto out;
+		}
+		if (usb_endpoint_is_bulk_in(&endpoint->desc))
+			bulk_in = endpoint;
+		else if (usb_endpoint_is_bulk_out(&endpoint->desc))
+			bulk_out = endpoint;
+		else if (usb_endpoint_is_int_in(&endpoint->desc))
+			int_in = endpoint;
+	}
+
+	if (!bulk_in || !bulk_out || !int_in) {
+		dev_err(&udev->dev, "%s: invalid endpoints\n", __func__);
+		status = -EINVAL;
+		goto out;
+	}
+	usbnet->in = usb_rcvbulkpipe(usbnet->udev,
+		bulk_in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+	usbnet->out = usb_sndbulkpipe(usbnet->udev,
+		bulk_out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+	usbnet->status = int_in;
+
+	/*change name of net device to rmnet_usbx here*/
+	strlcpy(usbnet->net->name, "rmnet_usb%d", IFNAMSIZ);
+
+	/*TBD: update rx_urb_size, curently set to eth frame len by usbnet*/
+out:
+	return status;
+}
+
+static struct sk_buff *rmnet_usb_tx_fixup(struct usbnet *dev,
+		struct sk_buff *skb, gfp_t flags)
+{
+	struct QMI_QOS_HDR_S	*qmih;
+
+	if (test_bit(RMNET_MODE_QOS, &dev->data[0])) {
+		qmih = (struct QMI_QOS_HDR_S *)
+		skb_push(skb, sizeof(struct QMI_QOS_HDR_S));
+		qmih->version = 1;
+		qmih->flags = 0;
+		qmih->flow_id = skb->mark;
+	 }
+
+	DBG1("[%s] Tx packet #%lu len=%d mark=0x%x\n",
+	    dev->net->name, dev->net->stats.tx_packets, skb->len, skb->mark);
+
+	return skb;
+}
+
+static __be16 rmnet_ip_type_trans(struct sk_buff *skb,
+	struct net_device *dev)
+{
+	__be16	protocol = 0;
+
+	skb->dev = dev;
+
+	switch (skb->data[0] & 0xf0) {
+	case 0x40:
+		protocol = htons(ETH_P_IP);
+		break;
+	case 0x60:
+		protocol = htons(ETH_P_IPV6);
+		break;
+	default:
+		pr_err("[%s] rmnet_recv() L3 protocol decode error: 0x%02x",
+		       dev->name, skb->data[0] & 0xf0);
+	}
+
+	return protocol;
+}
+
+static int rmnet_usb_rx_fixup(struct usbnet *dev,
+	struct sk_buff *skb)
+{
+
+	if (test_bit(RMNET_MODE_LLP_IP, &dev->data[0]))
+		skb->protocol = rmnet_ip_type_trans(skb, dev->net);
+	else /*set zero for eth mode*/
+		skb->protocol = 0;
+
+	DBG1("[%s] Rx packet #%lu len=%d\n",
+		dev->net->name, dev->net->stats.rx_packets, skb->len);
+
+	return 1;
+}
+
+static int rmnet_usb_manage_power(struct usbnet *dev, int on)
+{
+	dev->intf->needs_remote_wakeup = on;
+	return 0;
+}
+
+static int rmnet_change_mtu(struct net_device *dev, int new_mtu)
+{
+	if (0 > new_mtu || RMNET_DATA_LEN < new_mtu)
+		return -EINVAL;
+
+	DBG0("[%s] MTU change: old=%d new=%d\n", dev->name, dev->mtu, new_mtu);
+
+	dev->mtu = new_mtu;
+
+	return 0;
+}
+
+static struct net_device_stats *rmnet_get_stats(struct net_device *dev)
+{
+		return &dev->stats;
+}
+
+static const struct net_device_ops rmnet_usb_ops_ether = {
+	.ndo_open = usbnet_open,
+	.ndo_stop = usbnet_stop,
+	.ndo_start_xmit = usbnet_start_xmit,
+	.ndo_get_stats = rmnet_get_stats,
+	/*.ndo_set_multicast_list = rmnet_set_multicast_list,*/
+	.ndo_tx_timeout = usbnet_tx_timeout,
+	.ndo_do_ioctl = rmnet_ioctl,
+	.ndo_change_mtu = usbnet_change_mtu,
+	.ndo_set_mac_address = eth_mac_addr,
+	.ndo_validate_addr = eth_validate_addr,
+};
+
+static const struct net_device_ops rmnet_usb_ops_ip = {
+	.ndo_open = usbnet_open,
+	.ndo_stop = usbnet_stop,
+	.ndo_start_xmit = usbnet_start_xmit,
+	.ndo_get_stats = rmnet_get_stats,
+	/*.ndo_set_multicast_list = rmnet_set_multicast_list,*/
+	.ndo_tx_timeout = usbnet_tx_timeout,
+	.ndo_do_ioctl = rmnet_ioctl,
+	.ndo_change_mtu = rmnet_change_mtu,
+	.ndo_set_mac_address = 0,
+	.ndo_validate_addr = 0,
+};
+
+
+static int rmnet_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	struct usbnet	*unet = netdev_priv(dev);
+	u32		old_opmode;
+	int		prev_mtu = dev->mtu;
+	int		rc = 0;
+
+	old_opmode = unet->data[0]; /*data[0] saves operation mode*/
+	/* Process IOCTL command */
+	switch (cmd) {
+	case RMNET_IOCTL_SET_LLP_ETHERNET:	/*Set Ethernet protocol*/
+		/* Perform Ethernet config only if in IP mode currently*/
+		if (test_bit(RMNET_MODE_LLP_IP, &unet->data[0])) {
+			ether_setup(dev);
+			random_ether_addr(dev->dev_addr);
+			dev->mtu = prev_mtu;
+			dev->netdev_ops = &rmnet_usb_ops_ether;
+			clear_bit(RMNET_MODE_LLP_IP, &unet->data[0]);
+			set_bit(RMNET_MODE_LLP_ETH, &unet->data[0]);
+			DBG0("[%s] rmnet_ioctl(): set Ethernet protocol mode\n",
+					dev->name);
+		}
+		break;
+
+	case RMNET_IOCTL_SET_LLP_IP:		/* Set RAWIP protocol*/
+		/* Perform IP config only if in Ethernet mode currently*/
+		if (test_bit(RMNET_MODE_LLP_ETH, &unet->data[0])) {
+
+			/* Undo config done in ether_setup() */
+			dev->header_ops = 0;  /* No header */
+			dev->type = ARPHRD_RAWIP;
+			dev->hard_header_len = 0;
+			dev->mtu = prev_mtu;
+			dev->addr_len = 0;
+			dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
+			dev->needed_headroom = HEADROOM_FOR_QOS;
+			dev->netdev_ops = &rmnet_usb_ops_ip;
+			clear_bit(RMNET_MODE_LLP_ETH, &unet->data[0]);
+			set_bit(RMNET_MODE_LLP_IP, &unet->data[0]);
+			DBG0("[%s] rmnet_ioctl(): set IP protocol mode\n",
+					dev->name);
+		}
+		break;
+
+	case RMNET_IOCTL_GET_LLP:	/* Get link protocol state */
+		ifr->ifr_ifru.ifru_data = (void *)(unet->data[0]
+						& (RMNET_MODE_LLP_ETH
+						| RMNET_MODE_LLP_IP));
+		break;
+
+	case RMNET_IOCTL_SET_QOS_ENABLE:	/* Set QoS header enabled*/
+		set_bit(RMNET_MODE_QOS, &unet->data[0]);
+		DBG0("[%s] rmnet_ioctl(): set QMI QOS header enable\n",
+				dev->name);
+		break;
+
+	case RMNET_IOCTL_SET_QOS_DISABLE:	/* Set QoS header disabled */
+		clear_bit(RMNET_MODE_QOS, &unet->data[0]);
+		DBG0("[%s] rmnet_ioctl(): set QMI QOS header disable\n",
+				dev->name);
+		break;
+
+	case RMNET_IOCTL_GET_QOS:		/* Get QoS header state */
+		ifr->ifr_ifru.ifru_data = (void *)(unet->data[0]
+						& RMNET_MODE_QOS);
+		break;
+
+	case RMNET_IOCTL_GET_OPMODE:		/* Get operation mode*/
+		ifr->ifr_ifru.ifru_data = (void *)unet->data[0];
+		break;
+
+	case RMNET_IOCTL_OPEN:			/* Open transport port */
+		rc = usbnet_open(dev);
+		DBG0("[%s] rmnet_ioctl(): open transport port\n", dev->name);
+		break;
+
+	case RMNET_IOCTL_CLOSE:			/* Close transport port*/
+		rc = usbnet_stop(dev);
+		DBG0("[%s] rmnet_ioctl(): close transport port\n", dev->name);
+		break;
+
+	default:
+		dev_err(&unet->udev->dev, "[%s] error: "
+			"rmnet_ioct called for unsupported cmd[%d]",
+			dev->name, cmd);
+		return -EINVAL;
+	}
+
+	DBG2("[%s] %s: cmd=0x%x opmode old=0x%08x new=0x%08lx\n",
+		dev->name, __func__, cmd, old_opmode, unet->data[0]);
+
+	return rc;
+}
+
+static void rmnet_usb_setup(struct net_device *dev)
+{
+	/* Using Ethernet mode by default */
+	dev->netdev_ops = &rmnet_usb_ops_ether;
+
+	/* set this after calling ether_setup */
+	dev->mtu = RMNET_DATA_LEN;
+
+	dev->needed_headroom = HEADROOM_FOR_QOS;
+	random_ether_addr(dev->dev_addr);
+	dev->watchdog_timeo = 1000; /* 10 seconds? */
+}
+
+static int rmnet_usb_data_status(struct seq_file *s, void *unused)
+{
+	struct usbnet *unet = s->private;
+
+	seq_printf(s, "RMNET_MODE_LLP_IP:  %d\n",
+			test_bit(RMNET_MODE_LLP_IP, &unet->data[0]));
+	seq_printf(s, "RMNET_MODE_LLP_ETH: %d\n",
+			test_bit(RMNET_MODE_LLP_ETH, &unet->data[0]));
+	seq_printf(s, "RMNET_MODE_QOS:     %d\n",
+			test_bit(RMNET_MODE_QOS, &unet->data[0]));
+	seq_printf(s, "Net MTU:            %u\n", unet->net->mtu);
+	seq_printf(s, "rx_urb_size:        %u\n", unet->rx_urb_size);
+	seq_printf(s, "rx skb q len:       %u\n", unet->rxq.qlen);
+	seq_printf(s, "rx skb done q len:  %u\n", unet->done.qlen);
+	seq_printf(s, "rx errors:          %lu\n", unet->net->stats.rx_errors);
+	seq_printf(s, "rx over errors:     %lu\n",
+			unet->net->stats.rx_over_errors);
+	seq_printf(s, "rx length errors:   %lu\n",
+			unet->net->stats.rx_length_errors);
+	seq_printf(s, "rx packets:         %lu\n", unet->net->stats.rx_packets);
+	seq_printf(s, "rx bytes:           %lu\n", unet->net->stats.rx_bytes);
+	seq_printf(s, "tx skb q len:       %u\n", unet->txq.qlen);
+	seq_printf(s, "tx errors:          %lu\n", unet->net->stats.tx_errors);
+	seq_printf(s, "tx packets:         %lu\n", unet->net->stats.tx_packets);
+	seq_printf(s, "tx bytes:           %lu\n", unet->net->stats.tx_bytes);
+	seq_printf(s, "suspend count:      %d\n", unet->suspend_count);
+	seq_printf(s, "EVENT_DEV_OPEN:     %d\n",
+			test_bit(EVENT_DEV_OPEN, &unet->flags));
+	seq_printf(s, "EVENT_TX_HALT:      %d\n",
+			test_bit(EVENT_TX_HALT, &unet->flags));
+	seq_printf(s, "EVENT_RX_HALT:      %d\n",
+			test_bit(EVENT_RX_HALT, &unet->flags));
+	seq_printf(s, "EVENT_RX_MEMORY:    %d\n",
+			test_bit(EVENT_RX_MEMORY, &unet->flags));
+	seq_printf(s, "EVENT_DEV_ASLEEP:   %d\n",
+			test_bit(EVENT_DEV_ASLEEP, &unet->flags));
+
+	return 0;
+}
+
+static int rmnet_usb_data_status_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, rmnet_usb_data_status, inode->i_private);
+}
+
+const struct file_operations rmnet_usb_data_fops = {
+	.open = rmnet_usb_data_status_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static int rmnet_usb_data_debugfs_init(struct usbnet *unet)
+{
+	struct dentry *rmnet_usb_data_dbg_root;
+	struct dentry *rmnet_usb_data_dentry;
+
+	rmnet_usb_data_dbg_root = debugfs_create_dir(unet->net->name, NULL);
+	if (!rmnet_usb_data_dbg_root || IS_ERR(rmnet_usb_data_dbg_root))
+		return -ENODEV;
+
+	rmnet_usb_data_dentry = debugfs_create_file("status",
+		S_IRUGO | S_IWUSR,
+		rmnet_usb_data_dbg_root, unet,
+		&rmnet_usb_data_fops);
+
+	if (!rmnet_usb_data_dentry) {
+		debugfs_remove_recursive(rmnet_usb_data_dbg_root);
+		return -ENODEV;
+	}
+
+	unet->data[2] = (unsigned long)rmnet_usb_data_dbg_root;
+
+	return 0;
+}
+
+static void rmnet_usb_data_debugfs_cleanup(struct usbnet *unet)
+{
+	struct dentry *root = (struct dentry *)unet->data[2];
+
+	debugfs_remove_recursive(root);
+	unet->data[2] = 0;
+}
+
+static int rmnet_usb_probe(struct usb_interface *iface,
+		const struct usb_device_id *prod)
+{
+	struct usbnet		*unet;
+	struct usb_device	*udev;
+	struct driver_info	*info;
+	unsigned int		iface_num;
+	static int		first_rmnet_iface_num = -EINVAL;
+	int			status = 0;
+
+	udev = interface_to_usbdev(iface);
+	iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
+	if (iface->num_altsetting != 1) {
+		dev_err(&udev->dev, "%s invalid num_altsetting %u\n",
+			__func__, iface->num_altsetting);
+		status = -EINVAL;
+		goto out;
+	}
+
+	info = (struct driver_info *)prod->driver_info;
+	if (!test_bit(iface_num, &info->data))
+		return -ENODEV;
+
+	status = usbnet_probe(iface, prod);
+	if (status < 0) {
+		dev_err(&udev->dev, "usbnet_probe failed %d\n", status);
+		goto out;
+	}
+	unet = usb_get_intfdata(iface);
+
+	/*set rmnet operation mode to eth by default*/
+	set_bit(RMNET_MODE_LLP_ETH, &unet->data[0]);
+
+	/*update net device*/
+	rmnet_usb_setup(unet->net);
+
+	/*create /sys/class/net/rmnet_usbx/dbg_mask*/
+	status = device_create_file(&unet->net->dev, &dev_attr_dbg_mask);
+	if (status)
+		goto out;
+
+	if (first_rmnet_iface_num == -EINVAL)
+		first_rmnet_iface_num = iface_num;
+
+	/*save control device intstance */
+	unet->data[1] = (unsigned long)ctrl_dev	\
+			[iface_num - first_rmnet_iface_num];
+
+	status = rmnet_usb_ctrl_probe(iface, unet->status,
+		(struct rmnet_ctrl_dev *)unet->data[1]);
+	if (status)
+		goto out;
+
+	status = rmnet_usb_data_debugfs_init(unet);
+	if (status)
+		dev_dbg(&udev->dev, "mode debugfs file is not available\n");
+
+	/* allow modem to wake up suspended system */
+	device_set_wakeup_enable(&udev->dev, 1);
+out:
+	return status;
+}
+
+static void rmnet_usb_disconnect(struct usb_interface *intf)
+{
+	struct usbnet		*unet;
+	struct usb_device	*udev;
+	struct rmnet_ctrl_dev	*dev;
+
+	udev = interface_to_usbdev(intf);
+	device_set_wakeup_enable(&udev->dev, 0);
+
+	unet = usb_get_intfdata(intf);
+	if (!unet) {
+		dev_err(&udev->dev, "%s:data device not found\n", __func__);
+		return;
+	}
+
+	rmnet_usb_data_debugfs_cleanup(unet);
+
+	dev = (struct rmnet_ctrl_dev *)unet->data[1];
+	if (!dev) {
+		dev_err(&udev->dev, "%s:ctrl device not found\n", __func__);
+		return;
+	}
+	unet->data[0] = 0;
+	unet->data[1] = 0;
+	rmnet_usb_ctrl_disconnect(dev);
+	device_remove_file(&unet->net->dev, &dev_attr_dbg_mask);
+	usbnet_disconnect(intf);
+}
+
+/*bit position represents interface number*/
+#define PID9034_IFACE_MASK	0xF0
+#define PID9048_IFACE_MASK	0x1E0
+#define PID904C_IFACE_MASK	0x1C0
+
+static const struct driver_info rmnet_info_pid9034 = {
+	.description   = "RmNET net device",
+	.bind          = rmnet_usb_bind,
+	.tx_fixup      = rmnet_usb_tx_fixup,
+	.rx_fixup      = rmnet_usb_rx_fixup,
+	.manage_power  = rmnet_usb_manage_power,
+	.data          = PID9034_IFACE_MASK,
+};
+
+static const struct driver_info rmnet_info_pid9048 = {
+	.description   = "RmNET net device",
+	.bind          = rmnet_usb_bind,
+	.tx_fixup      = rmnet_usb_tx_fixup,
+	.rx_fixup      = rmnet_usb_rx_fixup,
+	.manage_power  = rmnet_usb_manage_power,
+	.data          = PID9048_IFACE_MASK,
+};
+
+static const struct driver_info rmnet_info_pid904c = {
+	.description   = "RmNET net device",
+	.bind          = rmnet_usb_bind,
+	.tx_fixup      = rmnet_usb_tx_fixup,
+	.rx_fixup      = rmnet_usb_rx_fixup,
+	.manage_power  = rmnet_usb_manage_power,
+	.data          = PID904C_IFACE_MASK,
+};
+
+static const struct usb_device_id vidpids[] = {
+	{
+		USB_DEVICE(0x05c6, 0x9034), /* MDM9x15*/
+		.driver_info = (unsigned long)&rmnet_info_pid9034,
+	},
+	{
+		USB_DEVICE(0x05c6, 0x9048), /* MDM9x15*/
+		.driver_info = (unsigned long)&rmnet_info_pid9048,
+	},
+	{
+		USB_DEVICE(0x05c6, 0x904c), /* MDM9x15*/
+		.driver_info = (unsigned long)&rmnet_info_pid904c,
+	},
+
+	{ }, /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, vidpids);
+
+static struct usb_driver rmnet_usb = {
+	.name       = "rmnet_usb",
+	.id_table   = vidpids,
+	.probe      = rmnet_usb_probe,
+	.disconnect = rmnet_usb_disconnect,
+	.suspend    = rmnet_usb_suspend,
+	.resume     = rmnet_usb_resume,
+	.supports_autosuspend = true,
+};
+
+static int __init rmnet_usb_init(void)
+{
+	int	retval;
+
+	retval = usb_register(&rmnet_usb);
+	if (retval) {
+		err("usb_register failed: %d", retval);
+		return retval;
+	}
+	/* initialize rmnet ctrl device here*/
+	retval = rmnet_usb_ctrl_init();
+	if (retval) {
+		usb_deregister(&rmnet_usb);
+		err("rmnet_usb_cmux_init failed: %d", retval);
+		return retval;
+	}
+
+	return 0;
+}
+module_init(rmnet_usb_init);
+
+static void __exit rmnet_usb_exit(void)
+{
+	rmnet_usb_ctrl_exit();
+	usb_deregister(&rmnet_usb);
+}
+module_exit(rmnet_usb_exit);
+
+MODULE_DESCRIPTION("msm rmnet usb device");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index b38db48..2b73d99 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -35,6 +35,7 @@
 
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/if_arp.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/ctype.h>
@@ -232,7 +233,9 @@
 		return;
 	}
 
-	skb->protocol = eth_type_trans (skb, dev->net);
+	if (!skb->protocol)
+		skb->protocol = eth_type_trans(skb, dev->net);
+
 	dev->net->stats.rx_packets++;
 	dev->net->stats.rx_bytes += skb->len;
 
@@ -353,6 +356,9 @@
 		return -ENOMEM;
 	}
 
+	if (dev->net->type != ARPHRD_RAWIP)
+		skb_reserve(skb, NET_IP_ALIGN);
+
 	entry = (struct skb_data *) skb->cb;
 	entry->urb = urb;
 	entry->dev = dev;
@@ -387,6 +393,7 @@
 			tasklet_schedule (&dev->bh);
 			break;
 		case 0:
+			usb_mark_last_busy(dev->udev);
 			__usbnet_queue_skb(&dev->rxq, skb, rx_start);
 		}
 	} else {
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index d9252ff..914f4fb 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -273,6 +273,36 @@
 	help
 	  Enables Power/Reset/Carddetect function abstraction
 
+config LIBRA_SDIOIF
+	tristate "Qualcomm libra wlan SDIO driver"
+	select WIRELESS_EXT
+	select WEXT_PRIV
+	select WEXT_CORE
+	select WEXT_SPY
+	depends on MMC_MSM
+	---help---
+	  A driver for Qualcomm WLAN SDIO Libra chipset.
+
+config ATH6K_LEGACY_EXT
+	tristate "QCA AR6003 wlan SDIO driver - External"
+	depends on MMC_MSM && WLAN
+	select WIRELESS_EXT
+	select WEXT_PRIV
+	---help---
+	  This module adds support for wireless adapters based on QCA AR6003 chipset
+	  running over SDIO. Driver is built outside of kernel tree, this config
+	  only enables configurations required for QCA AR6003 wlan driver.
+
+config WCNSS_CORE
+	tristate "Qualcomm WCNSS CORE driver"
+	depends on ARCH_MSM8960
+	select WIRELESS_EXT
+	select WEXT_PRIV
+	select WEXT_CORE
+	select WEXT_SPY
+	---help---
+	  Core driver for the Qualcomm WCNSS triple play connectivity subsystem
+
 source "drivers/net/wireless/ath/Kconfig"
 source "drivers/net/wireless/b43/Kconfig"
 source "drivers/net/wireless/b43legacy/Kconfig"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 8db066f..9392311 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -63,3 +63,6 @@
 
 obj-$(CONFIG_BRCMFMAC)	+= brcm80211/
 obj-$(CONFIG_BRCMSMAC)	+= brcm80211/
+
+obj-$(CONFIG_LIBRA_SDIOIF)	+= libra/
+obj-$(CONFIG_WCNSS_CORE)	+= wcnss/
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index fa84e37..4f96a69 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -538,6 +538,17 @@
 	ah->WARegVal |= (AR_WA_D3_L1_DISABLE |
 			 AR_WA_ASPM_TIMER_BASED_DISABLE);
 
+	/*
+	 * Read back AR_WA into a permanent copy and set bits 14 and 17.
+	 * We need to do this to avoid RMW of this register. We cannot
+	 * read the reg when chip is asleep.
+	 */
+	ah->WARegVal = REG_READ(ah, AR_WA);
+	ah->WARegVal |= (AR_WA_D3_L1_DISABLE |
+			 AR_WA_ASPM_TIMER_BASED_DISABLE);
+
+	ath9k_hw_read_revisions(ah);
+
 	if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON)) {
 		ath_err(common, "Couldn't reset chip\n");
 		return -EIO;
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index cb00645..740f09b 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -621,8 +621,10 @@
 static void ath9k_init_txpower_limits(struct ath_softc *sc)
 {
 	struct ath_hw *ah = sc->sc_ah;
+	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
 	struct ath9k_channel *curchan = ah->curchan;
 
+	ah->txchainmask = common->tx_chainmask;
 	if (ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ)
 		ath9k_init_band_txpower(sc, IEEE80211_BAND_2GHZ);
 	if (ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ)
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index 77dc327..5e66310 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -148,6 +148,31 @@
 	}
 }
 
+static void ath_pci_aspm_init(struct ath_common *common)
+{
+	struct ath_softc *sc = (struct ath_softc *) common->priv;
+	struct ath_hw *ah = sc->sc_ah;
+	struct pci_dev *pdev = to_pci_dev(sc->dev);
+	struct pci_dev *parent;
+	int pos;
+	u8 aspm;
+
+	if (!pci_is_pcie(pdev))
+		return;
+
+	parent = pdev->bus->self;
+	if (WARN_ON(!parent))
+		return;
+
+	pos = pci_pcie_cap(parent);
+	pci_read_config_byte(parent, pos +  PCI_EXP_LNKCTL, &aspm);
+	if (aspm & (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1)) {
+		ah->aspm_enabled = true;
+		/* Initialize PCIe PM and SERDES registers. */
+		ath9k_hw_configpcipowersave(ah, 0, 0);
+	}
+}
+
 static const struct ath_bus_ops ath_pci_bus_ops = {
 	.ath_bus_type = ATH_PCI,
 	.read_cachesize = ath_pci_read_cachesize,
diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c
index e4d6dc2..0cd9f47 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -2527,6 +2527,13 @@
 		b43_print_fw_helptext(dev->wl, 1);
 		err = -EOPNOTSUPP;
 		goto error;
+	} else if (fwrev >= 598) {
+		b43err(dev->wl, "YOUR FIRMWARE IS TOO NEW. Support for "
+		       "firmware 598 and up requires kernel 3.2 or newer. You "
+		       "have to install older firmware or upgrade kernel.\n");
+		b43_print_fw_helptext(dev->wl, 1);
+		err = -EOPNOTSUPP;
+		goto error;
 	}
 	dev->fw.rev = fwrev;
 	dev->fw.patch = fwpatch;
diff --git a/drivers/net/wireless/bcm4329/Kconfig b/drivers/net/wireless/bcm4329/Kconfig
new file mode 100644
index 0000000..ca5760d
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/Kconfig
@@ -0,0 +1,27 @@
+config BCM4329
+	tristate "Broadcom 4329 wireless cards support"
+	depends on MMC
+	select WIRELESS_EXT
+	select WEXT_PRIV
+	---help---
+	  This module adds support for wireless adapters based on
+	  Broadcom 4329 chipset.
+
+	  This driver uses the kernel's wireless extensions subsystem.
+
+	  If you choose to build a module, it'll be called dhd. Say M if
+	  unsure.
+
+config BCM4329_FW_PATH
+	depends on BCM4329
+	string "Firmware path"
+	default "/system/etc/firmware/fw_bcm4329.bin"
+	---help---
+	  Path to the firmware file.
+
+config BCM4329_NVRAM_PATH
+	depends on BCM4329
+	string "NVRAM path"
+	default "/proc/calibration"
+	---help---
+	  Path to the calibration file.
diff --git a/drivers/net/wireless/bcm4329/Makefile b/drivers/net/wireless/bcm4329/Makefile
new file mode 100644
index 0000000..5a662be
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/Makefile
@@ -0,0 +1,21 @@
+# bcm4329
+DHDCFLAGS = -DLINUX -DBCMDRIVER -DBCMDONGLEHOST -DDHDTHREAD -DBCMWPA2         \
+	-DUNRELEASEDCHIP -Dlinux -DDHD_SDALIGN=64 -DMAX_HDR_READ=64           \
+	-DDHD_FIRSTREAD=64 -DDHD_GPL -DDHD_SCHED -DBDC -DTOE -DDHD_BCMEVENTS  \
+	-DSHOW_EVENTS -DBCMSDIO -DDHD_GPL -DBCMLXSDMMC -DBCMPLATFORM_BUS      \
+	-Wall -Wstrict-prototypes -Werror -DOOB_INTR_ONLY -DCUSTOMER_HW2      \
+	-DDHD_USE_STATIC_BUF -DMMC_SDIO_ABORT -DDHD_DEBUG_TRAP -DSOFTAP       \
+	-DEMBEDDED_PLATFORM -DARP_OFFLOAD_SUPPORT -DPKT_FILTER_SUPPORT        \
+	-DGET_CUSTOM_MAC_ENABLE -DSET_RANDOM_MAC_SOFTAP -DCSCAN -DHW_OOB      \
+	-DKEEP_ALIVE -DPNO_SUPPORT                                            \
+	-Idrivers/net/wireless/bcm4329 -Idrivers/net/wireless/bcm4329/include
+
+DHDOFILES = dhd_linux.o linux_osl.o bcmutils.o dhd_common.o dhd_custom_gpio.o \
+	wl_iw.o siutils.o sbutils.o aiutils.o hndpmu.o bcmwifi.o dhd_sdio.o   \
+	dhd_linux_sched.o dhd_cdc.o bcmsdh_sdmmc.o bcmsdh.o bcmsdh_linux.o    \
+	bcmsdh_sdmmc_linux.o
+
+obj-$(CONFIG_BCM4329) += bcm4329.o
+bcm4329-objs += $(DHDOFILES)
+EXTRA_CFLAGS = $(DHDCFLAGS)
+EXTRA_LDFLAGS += --strip-debug
diff --git a/drivers/net/wireless/bcm4329/aiutils.c b/drivers/net/wireless/bcm4329/aiutils.c
new file mode 100644
index 0000000..df48ac0
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/aiutils.c
@@ -0,0 +1,686 @@
+/*
+ * Misc utility routines for accessing chip-specific features
+ * of the SiliconBackplane-based Broadcom chips.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: aiutils.c,v 1.6.4.7.4.6 2010/04/21 20:43:47 Exp $
+ */
+
+#include <typedefs.h>
+#include <bcmdefs.h>
+#include <osl.h>
+#include <bcmutils.h>
+#include <siutils.h>
+#include <hndsoc.h>
+#include <sbchipc.h>
+#include <pcicfg.h>
+
+#include "siutils_priv.h"
+
+STATIC uint32
+get_asd(si_t *sih, uint32 *eromptr, uint sp, uint ad, uint st,
+	uint32 *addrl, uint32 *addrh, uint32 *sizel, uint32 *sizeh);
+
+
+/* EROM parsing */
+
+static uint32
+get_erom_ent(si_t *sih, uint32 *eromptr, uint32 mask, uint32 match)
+{
+	uint32 ent;
+	uint inv = 0, nom = 0;
+
+	while (TRUE) {
+		ent = R_REG(si_osh(sih), (uint32 *)(uintptr)(*eromptr));
+		*eromptr += sizeof(uint32);
+
+		if (mask == 0)
+			break;
+
+		if ((ent & ER_VALID) == 0) {
+			inv++;
+			continue;
+		}
+
+		if (ent == (ER_END | ER_VALID))
+			break;
+
+		if ((ent & mask) == match)
+			break;
+
+		nom++;
+	}
+
+	SI_MSG(("%s: Returning ent 0x%08x\n", __FUNCTION__, ent));
+	if (inv + nom)
+		SI_MSG(("  after %d invalid and %d non-matching entries\n", inv, nom));
+	return ent;
+}
+
+STATIC uint32
+get_asd(si_t *sih, uint32 *eromptr, uint sp, uint ad, uint st,
+	uint32 *addrl, uint32 *addrh, uint32 *sizel, uint32 *sizeh)
+{
+	uint32 asd, sz, szd;
+
+	asd = get_erom_ent(sih, eromptr, ER_VALID, ER_VALID);
+	if (((asd & ER_TAG1) != ER_ADD) ||
+	    (((asd & AD_SP_MASK) >> AD_SP_SHIFT) != sp) ||
+	    ((asd & AD_ST_MASK) != st)) {
+		/* This is not what we want, "push" it back */
+		*eromptr -= sizeof(uint32);
+		return 0;
+	}
+	*addrl = asd & AD_ADDR_MASK;
+	if (asd & AD_AG32)
+		*addrh = get_erom_ent(sih, eromptr, 0, 0);
+	else
+		*addrh = 0;
+	*sizeh = 0;
+	sz = asd & AD_SZ_MASK;
+	if (sz == AD_SZ_SZD) {
+		szd = get_erom_ent(sih, eromptr, 0, 0);
+		*sizel = szd & SD_SZ_MASK;
+		if (szd & SD_SG32)
+			*sizeh = get_erom_ent(sih, eromptr, 0, 0);
+	} else
+		*sizel = AD_SZ_BASE << (sz >> AD_SZ_SHIFT);
+
+	SI_MSG(("  SP %d, ad %d: st = %d, 0x%08x_0x%08x @ 0x%08x_0x%08x\n",
+	        sp, ad, st, *sizeh, *sizel, *addrh, *addrl));
+
+	return asd;
+}
+
+/* parse the enumeration rom to identify all cores */
+void
+ai_scan(si_t *sih, void *regs, uint devid)
+{
+	si_info_t *sii = SI_INFO(sih);
+	chipcregs_t *cc = (chipcregs_t *)regs;
+	uint32 erombase, eromptr, eromlim;
+
+	erombase = R_REG(sii->osh, &cc->eromptr);
+
+	switch (BUSTYPE(sih->bustype)) {
+	case SI_BUS:
+		eromptr = (uintptr)REG_MAP(erombase, SI_CORE_SIZE);
+		break;
+
+	case PCI_BUS:
+		/* Set wrappers address */
+		sii->curwrap = (void *)((uintptr)regs + SI_CORE_SIZE);
+
+		/* Now point the window at the erom */
+		OSL_PCI_WRITE_CONFIG(sii->osh, PCI_BAR0_WIN, 4, erombase);
+		eromptr = (uint32)(uintptr)regs;
+		break;
+
+	case SPI_BUS:
+	case SDIO_BUS:
+		eromptr = erombase;
+		break;
+
+	case PCMCIA_BUS:
+	default:
+		SI_ERROR(("Don't know how to do AXI enumertion on bus %d\n", sih->bustype));
+		ASSERT(0);
+		return;
+	}
+	eromlim = eromptr + ER_REMAPCONTROL;
+
+	SI_MSG(("ai_scan: regs = 0x%p, erombase = 0x%08x, eromptr = 0x%08x, eromlim = 0x%08x\n",
+	        regs, erombase, eromptr, eromlim));
+	while (eromptr < eromlim) {
+		uint32 cia, cib, base, cid, mfg, crev, nmw, nsw, nmp, nsp;
+		uint32 mpd, asd, addrl, addrh, sizel, sizeh;
+		uint i, j, idx;
+		bool br;
+
+		br = FALSE;
+
+		/* Grok a component */
+		cia = get_erom_ent(sih, &eromptr, ER_TAG, ER_CI);
+		if (cia == (ER_END | ER_VALID)) {
+			SI_MSG(("Found END of erom after %d cores\n", sii->numcores));
+			return;
+		}
+		base = eromptr - sizeof(uint32);
+		cib = get_erom_ent(sih, &eromptr, 0, 0);
+
+		if ((cib & ER_TAG) != ER_CI) {
+			SI_ERROR(("CIA not followed by CIB\n"));
+			goto error;
+		}
+
+		cid = (cia & CIA_CID_MASK) >> CIA_CID_SHIFT;
+		mfg = (cia & CIA_MFG_MASK) >> CIA_MFG_SHIFT;
+		crev = (cib & CIB_REV_MASK) >> CIB_REV_SHIFT;
+		nmw = (cib & CIB_NMW_MASK) >> CIB_NMW_SHIFT;
+		nsw = (cib & CIB_NSW_MASK) >> CIB_NSW_SHIFT;
+		nmp = (cib & CIB_NMP_MASK) >> CIB_NMP_SHIFT;
+		nsp = (cib & CIB_NSP_MASK) >> CIB_NSP_SHIFT;
+
+		SI_MSG(("Found component 0x%04x/0x%4x rev %d at erom addr 0x%08x, with nmw = %d, "
+		        "nsw = %d, nmp = %d & nsp = %d\n",
+		        mfg, cid, crev, base, nmw, nsw, nmp, nsp));
+
+		if (((mfg == MFGID_ARM) && (cid == DEF_AI_COMP)) || (nsp == 0))
+			continue;
+		if ((nmw + nsw == 0)) {
+			/* A component which is not a core */
+			if (cid == OOB_ROUTER_CORE_ID) {
+				asd = get_asd(sih, &eromptr, 0, 0, AD_ST_SLAVE,
+					&addrl, &addrh, &sizel, &sizeh);
+				if (asd != 0) {
+					sii->common_info->oob_router = addrl;
+				}
+			}
+			continue;
+		}
+
+		idx = sii->numcores;
+/*		sii->eromptr[idx] = base; */
+		sii->common_info->cia[idx] = cia;
+		sii->common_info->cib[idx] = cib;
+		sii->common_info->coreid[idx] = cid;
+
+		for (i = 0; i < nmp; i++) {
+			mpd = get_erom_ent(sih, &eromptr, ER_VALID, ER_VALID);
+			if ((mpd & ER_TAG) != ER_MP) {
+				SI_ERROR(("Not enough MP entries for component 0x%x\n", cid));
+				goto error;
+			}
+			SI_MSG(("  Master port %d, mp: %d id: %d\n", i,
+			        (mpd & MPD_MP_MASK) >> MPD_MP_SHIFT,
+			        (mpd & MPD_MUI_MASK) >> MPD_MUI_SHIFT));
+		}
+
+		/* First Slave Address Descriptor should be port 0:
+		 * the main register space for the core
+		 */
+		asd = get_asd(sih, &eromptr, 0, 0, AD_ST_SLAVE, &addrl, &addrh, &sizel, &sizeh);
+		if (asd == 0) {
+			/* Try again to see if it is a bridge */
+			asd = get_asd(sih, &eromptr, 0, 0, AD_ST_BRIDGE, &addrl, &addrh,
+			              &sizel, &sizeh);
+			if (asd != 0)
+				br = TRUE;
+			else
+				if ((addrh != 0) || (sizeh != 0) || (sizel != SI_CORE_SIZE)) {
+					SI_ERROR(("First Slave ASD for core 0x%04x malformed "
+					          "(0x%08x)\n", cid, asd));
+					goto error;
+				}
+		}
+		sii->common_info->coresba[idx] = addrl;
+		sii->common_info->coresba_size[idx] = sizel;
+		/* Get any more ASDs in port 0 */
+		j = 1;
+		do {
+			asd = get_asd(sih, &eromptr, 0, j, AD_ST_SLAVE, &addrl, &addrh,
+			              &sizel, &sizeh);
+			if ((asd != 0) && (j == 1) && (sizel == SI_CORE_SIZE))
+				sii->common_info->coresba2[idx] = addrl;
+				sii->common_info->coresba2_size[idx] = sizel;
+			j++;
+		} while (asd != 0);
+
+		/* Go through the ASDs for other slave ports */
+		for (i = 1; i < nsp; i++) {
+			j = 0;
+			do {
+				asd = get_asd(sih, &eromptr, i, j++, AD_ST_SLAVE, &addrl, &addrh,
+				              &sizel, &sizeh);
+			} while (asd != 0);
+			if (j == 0) {
+				SI_ERROR((" SP %d has no address descriptors\n", i));
+				goto error;
+			}
+		}
+
+		/* Now get master wrappers */
+		for (i = 0; i < nmw; i++) {
+			asd = get_asd(sih, &eromptr, i, 0, AD_ST_MWRAP, &addrl, &addrh,
+			              &sizel, &sizeh);
+			if (asd == 0) {
+				SI_ERROR(("Missing descriptor for MW %d\n", i));
+				goto error;
+			}
+			if ((sizeh != 0) || (sizel != SI_CORE_SIZE)) {
+				SI_ERROR(("Master wrapper %d is not 4KB\n", i));
+				goto error;
+			}
+			if (i == 0)
+				sii->common_info->wrapba[idx] = addrl;
+		}
+
+		/* And finally slave wrappers */
+		for (i = 0; i < nsw; i++) {
+			uint fwp = (nsp == 1) ? 0 : 1;
+			asd = get_asd(sih, &eromptr, fwp + i, 0, AD_ST_SWRAP, &addrl, &addrh,
+			              &sizel, &sizeh);
+			if (asd == 0) {
+				SI_ERROR(("Missing descriptor for SW %d\n", i));
+				goto error;
+			}
+			if ((sizeh != 0) || (sizel != SI_CORE_SIZE)) {
+				SI_ERROR(("Slave wrapper %d is not 4KB\n", i));
+				goto error;
+			}
+			if ((nmw == 0) && (i == 0))
+				sii->common_info->wrapba[idx] = addrl;
+		}
+
+		/* Don't record bridges */
+		if (br)
+			continue;
+
+		/* Done with core */
+		sii->numcores++;
+	}
+
+	SI_ERROR(("Reached end of erom without finding END"));
+
+error:
+	sii->numcores = 0;
+	return;
+}
+
+/* This function changes the logical "focus" to the indicated core.
+ * Return the current core's virtual address.
+ */
+void *
+ai_setcoreidx(si_t *sih, uint coreidx)
+{
+	si_info_t *sii = SI_INFO(sih);
+	uint32 addr = sii->common_info->coresba[coreidx];
+	uint32 wrap = sii->common_info->wrapba[coreidx];
+	void *regs;
+
+	if (coreidx >= sii->numcores)
+		return (NULL);
+
+	/*
+	 * If the user has provided an interrupt mask enabled function,
+	 * then assert interrupts are disabled before switching the core.
+	 */
+	ASSERT((sii->intrsenabled_fn == NULL) || !(*(sii)->intrsenabled_fn)((sii)->intr_arg));
+
+	switch (BUSTYPE(sih->bustype)) {
+	case SI_BUS:
+		/* map new one */
+		if (!sii->common_info->regs[coreidx]) {
+			sii->common_info->regs[coreidx] = REG_MAP(addr, SI_CORE_SIZE);
+			ASSERT(GOODREGS(sii->common_info->regs[coreidx]));
+		}
+		sii->curmap = regs = sii->common_info->regs[coreidx];
+		if (!sii->common_info->wrappers[coreidx]) {
+			sii->common_info->wrappers[coreidx] = REG_MAP(wrap, SI_CORE_SIZE);
+			ASSERT(GOODREGS(sii->common_info->wrappers[coreidx]));
+		}
+		sii->curwrap = sii->common_info->wrappers[coreidx];
+		break;
+
+
+	case SPI_BUS:
+	case SDIO_BUS:
+		sii->curmap = regs = (void *)((uintptr)addr);
+		sii->curwrap = (void *)((uintptr)wrap);
+		break;
+
+	case PCMCIA_BUS:
+	default:
+		ASSERT(0);
+		regs = NULL;
+		break;
+	}
+
+	sii->curmap = regs;
+	sii->curidx = coreidx;
+
+	return regs;
+}
+
+/* Return the number of address spaces in current core */
+int
+ai_numaddrspaces(si_t *sih)
+{
+	return 2;
+}
+
+/* Return the address of the nth address space in the current core */
+uint32
+ai_addrspace(si_t *sih, uint asidx)
+{
+	si_info_t *sii;
+	uint cidx;
+
+	sii = SI_INFO(sih);
+	cidx = sii->curidx;
+
+	if (asidx == 0)
+		return sii->common_info->coresba[cidx];
+	else if (asidx == 1)
+		return sii->common_info->coresba2[cidx];
+	else {
+		SI_ERROR(("%s: Need to parse the erom again to find addr space %d\n",
+		          __FUNCTION__, asidx));
+		return 0;
+	}
+}
+
+/* Return the size of the nth address space in the current core */
+uint32
+ai_addrspacesize(si_t *sih, uint asidx)
+{
+	si_info_t *sii;
+	uint cidx;
+
+	sii = SI_INFO(sih);
+	cidx = sii->curidx;
+
+	if (asidx == 0)
+		return sii->common_info->coresba_size[cidx];
+	else if (asidx == 1)
+		return sii->common_info->coresba2_size[cidx];
+	else {
+		SI_ERROR(("%s: Need to parse the erom again to find addr space %d\n",
+		          __FUNCTION__, asidx));
+		return 0;
+	}
+}
+
+uint
+ai_flag(si_t *sih)
+{
+	si_info_t *sii;
+	aidmp_t *ai;
+
+	sii = SI_INFO(sih);
+	ai = sii->curwrap;
+
+	return (R_REG(sii->osh, &ai->oobselouta30) & 0x1f);
+}
+
+void
+ai_setint(si_t *sih, int siflag)
+{
+}
+
+void
+ai_write_wrap_reg(si_t *sih, uint32 offset, uint32 val)
+{
+	si_info_t *sii = SI_INFO(sih);
+	aidmp_t *ai = sii->curwrap;
+	W_REG(sii->osh, (uint32 *)((uint8 *)ai+offset), val);
+	return;
+}
+
+uint
+ai_corevendor(si_t *sih)
+{
+	si_info_t *sii;
+	uint32 cia;
+
+	sii = SI_INFO(sih);
+	cia = sii->common_info->cia[sii->curidx];
+	return ((cia & CIA_MFG_MASK) >> CIA_MFG_SHIFT);
+}
+
+uint
+ai_corerev(si_t *sih)
+{
+	si_info_t *sii;
+	uint32 cib;
+
+	sii = SI_INFO(sih);
+	cib = sii->common_info->cib[sii->curidx];
+	return ((cib & CIB_REV_MASK) >> CIB_REV_SHIFT);
+}
+
+bool
+ai_iscoreup(si_t *sih)
+{
+	si_info_t *sii;
+	aidmp_t *ai;
+
+	sii = SI_INFO(sih);
+	ai = sii->curwrap;
+
+	return (((R_REG(sii->osh, &ai->ioctrl) & (SICF_FGC | SICF_CLOCK_EN)) == SICF_CLOCK_EN) &&
+	        ((R_REG(sii->osh, &ai->resetctrl) & AIRC_RESET) == 0));
+}
+
+/*
+ * Switch to 'coreidx', issue a single arbitrary 32bit register mask&set operation,
+ * switch back to the original core, and return the new value.
+ *
+ * When using the silicon backplane, no fidleing with interrupts or core switches are needed.
+ *
+ * Also, when using pci/pcie, we can optimize away the core switching for pci registers
+ * and (on newer pci cores) chipcommon registers.
+ */
+uint
+ai_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val)
+{
+	uint origidx = 0;
+	uint32 *r = NULL;
+	uint w;
+	uint intr_val = 0;
+	bool fast = FALSE;
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+
+	ASSERT(GOODIDX(coreidx));
+	ASSERT(regoff < SI_CORE_SIZE);
+	ASSERT((val & ~mask) == 0);
+
+	if (coreidx >= SI_MAXCORES)
+		return 0;
+
+	if (BUSTYPE(sih->bustype) == SI_BUS) {
+		/* If internal bus, we can always get at everything */
+		fast = TRUE;
+		/* map if does not exist */
+		if (!sii->common_info->wrappers[coreidx]) {
+			sii->common_info->regs[coreidx] =
+			    REG_MAP(sii->common_info->coresba[coreidx], SI_CORE_SIZE);
+			ASSERT(GOODREGS(sii->common_info->regs[coreidx]));
+		}
+		r = (uint32 *)((uchar *)sii->common_info->regs[coreidx] + regoff);
+	} else if (BUSTYPE(sih->bustype) == PCI_BUS) {
+		/* If pci/pcie, we can get at pci/pcie regs and on newer cores to chipc */
+
+		if ((sii->common_info->coreid[coreidx] == CC_CORE_ID) && SI_FAST(sii)) {
+			/* Chipc registers are mapped at 12KB */
+
+			fast = TRUE;
+			r = (uint32 *)((char *)sii->curmap + PCI_16KB0_CCREGS_OFFSET + regoff);
+		} else if (sii->pub.buscoreidx == coreidx) {
+			/* pci registers are at either in the last 2KB of an 8KB window
+			 * or, in pcie and pci rev 13 at 8KB
+			 */
+			fast = TRUE;
+			if (SI_FAST(sii))
+				r = (uint32 *)((char *)sii->curmap +
+				               PCI_16KB0_PCIREGS_OFFSET + regoff);
+			else
+				r = (uint32 *)((char *)sii->curmap +
+				               ((regoff >= SBCONFIGOFF) ?
+				                PCI_BAR0_PCISBR_OFFSET : PCI_BAR0_PCIREGS_OFFSET) +
+				               regoff);
+		}
+	}
+
+	if (!fast) {
+		INTR_OFF(sii, intr_val);
+
+		/* save current core index */
+		origidx = si_coreidx(&sii->pub);
+
+		/* switch core */
+		r = (uint32*) ((uchar*) ai_setcoreidx(&sii->pub, coreidx) + regoff);
+	}
+	ASSERT(r != NULL);
+
+	/* mask and set */
+	if (mask || val) {
+		w = (R_REG(sii->osh, r) & ~mask) | val;
+		W_REG(sii->osh, r, w);
+	}
+
+	/* readback */
+	w = R_REG(sii->osh, r);
+
+	if (!fast) {
+		/* restore core index */
+		if (origidx != coreidx)
+			ai_setcoreidx(&sii->pub, origidx);
+
+		INTR_RESTORE(sii, intr_val);
+	}
+
+	return (w);
+}
+
+void
+ai_core_disable(si_t *sih, uint32 bits)
+{
+	si_info_t *sii;
+	volatile uint32 dummy;
+	aidmp_t *ai;
+
+	sii = SI_INFO(sih);
+
+	ASSERT(GOODREGS(sii->curwrap));
+	ai = sii->curwrap;
+
+	/* if core is already in reset, just return */
+	if (R_REG(sii->osh, &ai->resetctrl) & AIRC_RESET)
+		return;
+
+	W_REG(sii->osh, &ai->ioctrl, bits);
+	dummy = R_REG(sii->osh, &ai->ioctrl);
+	OSL_DELAY(10);
+
+	W_REG(sii->osh, &ai->resetctrl, AIRC_RESET);
+	OSL_DELAY(1);
+}
+
+/* reset and re-enable a core
+ * inputs:
+ * bits - core specific bits that are set during and after reset sequence
+ * resetbits - core specific bits that are set only during reset sequence
+ */
+void
+ai_core_reset(si_t *sih, uint32 bits, uint32 resetbits)
+{
+	si_info_t *sii;
+	aidmp_t *ai;
+	volatile uint32 dummy;
+
+	sii = SI_INFO(sih);
+	ASSERT(GOODREGS(sii->curwrap));
+	ai = sii->curwrap;
+
+	/*
+	 * Must do the disable sequence first to work for arbitrary current core state.
+	 */
+	ai_core_disable(sih, (bits | resetbits));
+
+	/*
+	 * Now do the initialization sequence.
+	 */
+	W_REG(sii->osh, &ai->ioctrl, (bits | SICF_FGC | SICF_CLOCK_EN));
+	dummy = R_REG(sii->osh, &ai->ioctrl);
+	W_REG(sii->osh, &ai->resetctrl, 0);
+	OSL_DELAY(1);
+
+	W_REG(sii->osh, &ai->ioctrl, (bits | SICF_CLOCK_EN));
+	dummy = R_REG(sii->osh, &ai->ioctrl);
+	OSL_DELAY(1);
+}
+
+
+void
+ai_core_cflags_wo(si_t *sih, uint32 mask, uint32 val)
+{
+	si_info_t *sii;
+	aidmp_t *ai;
+	uint32 w;
+
+	sii = SI_INFO(sih);
+	ASSERT(GOODREGS(sii->curwrap));
+	ai = sii->curwrap;
+
+	ASSERT((val & ~mask) == 0);
+
+	if (mask || val) {
+		w = ((R_REG(sii->osh, &ai->ioctrl) & ~mask) | val);
+		W_REG(sii->osh, &ai->ioctrl, w);
+	}
+}
+
+uint32
+ai_core_cflags(si_t *sih, uint32 mask, uint32 val)
+{
+	si_info_t *sii;
+	aidmp_t *ai;
+	uint32 w;
+
+	sii = SI_INFO(sih);
+	ASSERT(GOODREGS(sii->curwrap));
+	ai = sii->curwrap;
+
+	ASSERT((val & ~mask) == 0);
+
+	if (mask || val) {
+		w = ((R_REG(sii->osh, &ai->ioctrl) & ~mask) | val);
+		W_REG(sii->osh, &ai->ioctrl, w);
+	}
+
+	return R_REG(sii->osh, &ai->ioctrl);
+}
+
+uint32
+ai_core_sflags(si_t *sih, uint32 mask, uint32 val)
+{
+	si_info_t *sii;
+	aidmp_t *ai;
+	uint32 w;
+
+	sii = SI_INFO(sih);
+	ASSERT(GOODREGS(sii->curwrap));
+	ai = sii->curwrap;
+
+	ASSERT((val & ~mask) == 0);
+	ASSERT((mask & ~SISF_CORE_BITS) == 0);
+
+	if (mask || val) {
+		w = ((R_REG(sii->osh, &ai->iostatus) & ~mask) | val);
+		W_REG(sii->osh, &ai->iostatus, w);
+	}
+
+	return R_REG(sii->osh, &ai->iostatus);
+}
diff --git a/drivers/net/wireless/bcm4329/bcmpcispi.c b/drivers/net/wireless/bcm4329/bcmpcispi.c
new file mode 100644
index 0000000..1a8b671
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/bcmpcispi.c
@@ -0,0 +1,630 @@
+/*
+ * Broadcom SPI over PCI-SPI Host Controller, low-level hardware driver
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmpcispi.c,v 1.22.2.4.4.5.6.1 2010/08/13 00:26:05 Exp $
+ */
+
+#include <typedefs.h>
+#include <bcmutils.h>
+
+#include <sdio.h>		/* SDIO Specs */
+#include <bcmsdbus.h>		/* bcmsdh to/from specific controller APIs */
+#include <sdiovar.h>		/* to get msglevel bit values */
+
+#include <pcicfg.h>
+#include <bcmsdspi.h>
+#include <bcmspi.h>
+#include <bcmpcispi.h>		/* BRCM PCI-SPI Host Controller Register definitions */
+
+
+/* ndis_osl.h needs to do a runtime check of the osh to map
+ * R_REG/W_REG to bus specific access similar to linux_osl.h.
+ * Until then...
+ */
+/* linux */
+
+#define SPIPCI_RREG R_REG
+#define SPIPCI_WREG W_REG
+
+
+#define	SPIPCI_ANDREG(osh, r, v) SPIPCI_WREG(osh, (r), (SPIPCI_RREG(osh, r) & (v)))
+#define	SPIPCI_ORREG(osh, r, v)	SPIPCI_WREG(osh, (r), (SPIPCI_RREG(osh, r) | (v)))
+
+
+int bcmpcispi_dump = 0;		/* Set to dump complete trace of all SPI bus transactions */
+
+typedef struct spih_info_ {
+	uint		bar0;		/* BAR0 of PCI Card */
+	uint		bar1;		/* BAR1 of PCI Card */
+	osl_t 		*osh;		/* osh handle */
+	spih_pciregs_t	*pciregs;	/* PCI Core Registers */
+	spih_regs_t	*regs;		/* SPI Controller Registers */
+	uint8		rev;		/* PCI Card Revision ID */
+} spih_info_t;
+
+
+/* Attach to PCI-SPI Host Controller Hardware */
+bool
+spi_hw_attach(sdioh_info_t *sd)
+{
+	osl_t *osh;
+	spih_info_t *si;
+
+	sd_trace(("%s: enter\n", __FUNCTION__));
+
+	osh = sd->osh;
+
+	if ((si = (spih_info_t *)MALLOC(osh, sizeof(spih_info_t))) == NULL) {
+		sd_err(("%s: out of memory, malloced %d bytes\n", __FUNCTION__, MALLOCED(osh)));
+		return FALSE;
+	}
+
+	bzero(si, sizeof(spih_info_t));
+
+	sd->controller = si;
+
+	si->osh = sd->osh;
+	si->rev = OSL_PCI_READ_CONFIG(sd->osh, PCI_CFG_REV, 4) & 0xFF;
+
+	if (si->rev < 3) {
+		sd_err(("Host controller %d not supported, please upgrade to rev >= 3\n", si->rev));
+		MFREE(osh, si, sizeof(spih_info_t));
+		return (FALSE);
+	}
+
+	sd_err(("Attaching to Generic PCI SPI Host Controller Rev %d\n", si->rev));
+
+	/* FPGA Revision < 3 not supported by driver anymore. */
+	ASSERT(si->rev >= 3);
+
+	si->bar0 = sd->bar0;
+
+	/* Rev < 10 PciSpiHost has 2 BARs:
+	 *    BAR0 = PCI Core Registers
+	 *    BAR1 = PciSpiHost Registers (all other cores on backplane)
+	 *
+	 * Rev 10 and up use a different PCI core which only has a single
+	 * BAR0 which contains the PciSpiHost Registers.
+	 */
+	if (si->rev < 10) {
+		si->pciregs = (spih_pciregs_t *)spi_reg_map(osh,
+		                                              (uintptr)si->bar0,
+		                                              sizeof(spih_pciregs_t));
+		sd_err(("Mapped PCI Core regs to BAR0 at %p\n", si->pciregs));
+
+		si->bar1 = OSL_PCI_READ_CONFIG(sd->osh, PCI_CFG_BAR1, 4);
+		si->regs = (spih_regs_t *)spi_reg_map(osh,
+		                                        (uintptr)si->bar1,
+		                                        sizeof(spih_regs_t));
+		sd_err(("Mapped SPI Controller regs to BAR1 at %p\n", si->regs));
+	} else {
+		si->regs = (spih_regs_t *)spi_reg_map(osh,
+		                                              (uintptr)si->bar0,
+		                                              sizeof(spih_regs_t));
+		sd_err(("Mapped SPI Controller regs to BAR0 at %p\n", si->regs));
+		si->pciregs = NULL;
+	}
+	/* Enable SPI Controller, 16.67MHz SPI Clock */
+	SPIPCI_WREG(osh, &si->regs->spih_ctrl, 0x000000d1);
+
+	/* Set extended feature register to defaults */
+	SPIPCI_WREG(osh, &si->regs->spih_ext, 0x00000000);
+
+	/* Set GPIO CS# High (de-asserted) */
+	SPIPCI_WREG(osh, &si->regs->spih_gpio_data, SPIH_CS);
+
+	/* set GPIO[0] to output for CS# */
+	/* set GPIO[1] to output for power control */
+	/* set GPIO[2] to input for card detect */
+	SPIPCI_WREG(osh, &si->regs->spih_gpio_ctrl, (SPIH_CS | SPIH_SLOT_POWER));
+
+	/* Clear out the Read FIFO in case there is any stuff left in there from a previous run. */
+	while ((SPIPCI_RREG(osh, &si->regs->spih_stat) & SPIH_RFEMPTY) == 0) {
+		SPIPCI_RREG(osh, &si->regs->spih_data);
+	}
+
+	/* Wait for power to stabilize to the SDIO Card (100msec was insufficient) */
+	OSL_DELAY(250000);
+
+	/* Check card detect on FPGA Revision >= 4 */
+	if (si->rev >= 4) {
+		if (SPIPCI_RREG(osh, &si->regs->spih_gpio_data) & SPIH_CARD_DETECT) {
+			sd_err(("%s: no card detected in SD slot\n", __FUNCTION__));
+			spi_reg_unmap(osh, (uintptr)si->regs, sizeof(spih_regs_t));
+			if (si->pciregs) {
+				spi_reg_unmap(osh, (uintptr)si->pciregs, sizeof(spih_pciregs_t));
+			}
+			MFREE(osh, si, sizeof(spih_info_t));
+			return FALSE;
+		}
+	}
+
+	/* Interrupts are level sensitive */
+	SPIPCI_WREG(osh, &si->regs->spih_int_edge, 0x80000000);
+
+	/* Interrupts are active low. */
+	SPIPCI_WREG(osh, &si->regs->spih_int_pol, 0x40000004);
+
+	/* Enable interrupts through PCI Core. */
+	if (si->pciregs) {
+		SPIPCI_WREG(osh, &si->pciregs->ICR, PCI_INT_PROP_EN);
+	}
+
+	sd_trace(("%s: exit\n", __FUNCTION__));
+	return TRUE;
+}
+
+/* Detach and return PCI-SPI Hardware to unconfigured state */
+bool
+spi_hw_detach(sdioh_info_t *sd)
+{
+	spih_info_t *si = (spih_info_t *)sd->controller;
+	osl_t *osh = si->osh;
+	spih_regs_t *regs = si->regs;
+	spih_pciregs_t *pciregs = si->pciregs;
+
+	sd_trace(("%s: enter\n", __FUNCTION__));
+
+	SPIPCI_WREG(osh, &regs->spih_ctrl, 0x00000010);
+	SPIPCI_WREG(osh, &regs->spih_gpio_ctrl, 0x00000000);	/* Disable GPIO for CS# */
+	SPIPCI_WREG(osh, &regs->spih_int_mask, 0x00000000);	/* Clear Intmask */
+	SPIPCI_WREG(osh, &regs->spih_hex_disp, 0x0000DEAF);
+	SPIPCI_WREG(osh, &regs->spih_int_edge, 0x00000000);
+	SPIPCI_WREG(osh, &regs->spih_int_pol, 0x00000000);
+	SPIPCI_WREG(osh, &regs->spih_hex_disp, 0x0000DEAD);
+
+	/* Disable interrupts through PCI Core. */
+	if (si->pciregs) {
+		SPIPCI_WREG(osh, &pciregs->ICR, 0x00000000);
+		spi_reg_unmap(osh, (uintptr)pciregs, sizeof(spih_pciregs_t));
+	}
+	spi_reg_unmap(osh, (uintptr)regs, sizeof(spih_regs_t));
+
+	MFREE(osh, si, sizeof(spih_info_t));
+
+	sd->controller = NULL;
+
+	sd_trace(("%s: exit\n", __FUNCTION__));
+	return TRUE;
+}
+
+/* Switch between internal (PCI) and external clock oscillator */
+static bool
+sdspi_switch_clock(sdioh_info_t *sd, bool ext_clk)
+{
+	spih_info_t *si = (spih_info_t *)sd->controller;
+	osl_t *osh = si->osh;
+	spih_regs_t *regs = si->regs;
+
+	/* Switch to desired clock, and reset the PLL. */
+	SPIPCI_WREG(osh, &regs->spih_pll_ctrl, ext_clk ? SPIH_EXT_CLK : 0);
+
+	SPINWAIT(((SPIPCI_RREG(osh, &regs->spih_pll_status) & SPIH_PLL_LOCKED)
+	          != SPIH_PLL_LOCKED), 1000);
+	if ((SPIPCI_RREG(osh, &regs->spih_pll_status) & SPIH_PLL_LOCKED) != SPIH_PLL_LOCKED) {
+		sd_err(("%s: timeout waiting for PLL to lock\n", __FUNCTION__));
+		return (FALSE);
+	}
+	return (TRUE);
+
+}
+
+/* Configure PCI-SPI Host Controller's SPI Clock rate as a divisor into the
+ * base clock rate.  The base clock is either the PCI Clock (33MHz) or the
+ * external clock oscillator at U17 on the PciSpiHost.
+ */
+bool
+spi_start_clock(sdioh_info_t *sd, uint16 div)
+{
+	spih_info_t *si = (spih_info_t *)sd->controller;
+	osl_t *osh = si->osh;
+	spih_regs_t *regs = si->regs;
+	uint32 t, espr, disp;
+	uint32 disp_xtal_freq;
+	bool	ext_clock = FALSE;
+	char disp_string[5];
+
+	if (div > 2048) {
+		sd_err(("%s: divisor %d too large; using max of 2048\n", __FUNCTION__, div));
+		div = 2048;
+	} else if (div & (div - 1)) {	/* Not a power of 2? */
+		/* Round up to a power of 2 */
+		while ((div + 1) & div)
+			div |= div >> 1;
+		div++;
+	}
+
+	/* For FPGA Rev >= 5, the use of an external clock oscillator is supported.
+	 * If the oscillator is populated, use it to provide the SPI base clock,
+	 * otherwise, default to the PCI clock as the SPI base clock.
+	 */
+	if (si->rev >= 5) {
+		uint32 clk_tick;
+		/* Enable the External Clock Oscillator as PLL clock source. */
+		if (!sdspi_switch_clock(sd, TRUE)) {
+			sd_err(("%s: error switching to external clock\n", __FUNCTION__));
+		}
+
+		/* Check to make sure the external clock is running.  If not, then it
+		 * is not populated on the card, so we will default to the PCI clock.
+		 */
+		clk_tick = SPIPCI_RREG(osh, &regs->spih_clk_count);
+		if (clk_tick == SPIPCI_RREG(osh, &regs->spih_clk_count)) {
+
+			/* Switch back to the PCI clock as the clock source. */
+			if (!sdspi_switch_clock(sd, FALSE)) {
+				sd_err(("%s: error switching to external clock\n", __FUNCTION__));
+			}
+		} else {
+			ext_clock = TRUE;
+		}
+	}
+
+	/* Hack to allow hot-swapping oscillators:
+	 * 1. Force PCI clock as clock source, using sd_divisor of 0.
+	 * 2. Swap oscillator
+	 * 3. Set desired sd_divisor (will switch to external oscillator as clock source.
+	 */
+	if (div == 0) {
+		ext_clock = FALSE;
+		div = 2;
+
+		/* Select PCI clock as the clock source. */
+		if (!sdspi_switch_clock(sd, FALSE)) {
+			sd_err(("%s: error switching to external clock\n", __FUNCTION__));
+		}
+
+		sd_err(("%s: Ok to hot-swap oscillators.\n", __FUNCTION__));
+	}
+
+	/* If using the external oscillator, read the clock frequency from the controller
+	 * The value read is in units of 10000Hz, and it's not a nice round number because
+	 * it is calculated by the FPGA.  So to make up for that, we round it off.
+	 */
+	if (ext_clock == TRUE) {
+		uint32 xtal_freq;
+
+		OSL_DELAY(1000);
+		xtal_freq = SPIPCI_RREG(osh, &regs->spih_xtal_freq) * 10000;
+
+		sd_info(("%s: Oscillator is %dHz\n", __FUNCTION__, xtal_freq));
+
+
+		disp_xtal_freq = xtal_freq / 10000;
+
+		/* Round it off to a nice number. */
+		if ((disp_xtal_freq % 100) > 50) {
+			disp_xtal_freq += 100;
+		}
+
+		disp_xtal_freq = (disp_xtal_freq / 100) * 100;
+	} else {
+		sd_err(("%s: no external oscillator installed, using PCI clock.\n", __FUNCTION__));
+		disp_xtal_freq = 3333;
+	}
+
+	/* Convert the SPI Clock frequency to BCD format. */
+	sprintf(disp_string, "%04d", disp_xtal_freq / div);
+
+	disp  = (disp_string[0] - '0') << 12;
+	disp |= (disp_string[1] - '0') << 8;
+	disp |= (disp_string[2] - '0') << 4;
+	disp |= (disp_string[3] - '0');
+
+	/* Select the correct ESPR register value based on the divisor. */
+	switch (div) {
+		case 1:		espr = 0x0; break;
+		case 2:		espr = 0x1; break;
+		case 4:		espr = 0x2; break;
+		case 8:		espr = 0x5; break;
+		case 16:	espr = 0x3; break;
+		case 32:	espr = 0x4; break;
+		case 64:	espr = 0x6; break;
+		case 128:	espr = 0x7; break;
+		case 256:	espr = 0x8; break;
+		case 512:	espr = 0x9; break;
+		case 1024:	espr = 0xa; break;
+		case 2048:	espr = 0xb; break;
+		default:	espr = 0x0; ASSERT(0); break;
+	}
+
+	t = SPIPCI_RREG(osh, &regs->spih_ctrl);
+	t &= ~3;
+	t |= espr & 3;
+	SPIPCI_WREG(osh, &regs->spih_ctrl, t);
+
+	t = SPIPCI_RREG(osh, &regs->spih_ext);
+	t &= ~3;
+	t |= (espr >> 2) & 3;
+	SPIPCI_WREG(osh, &regs->spih_ext, t);
+
+	SPIPCI_WREG(osh, &regs->spih_hex_disp, disp);
+
+	/* For Rev 8, writing to the PLL_CTRL register resets
+	 * the PLL, and it can re-acquire in 200uS.  For
+	 * Rev 7 and older, we use a software delay to allow
+	 * the PLL to re-acquire, which takes more than 2mS.
+	 */
+	if (si->rev < 8) {
+		/* Wait for clock to settle. */
+		OSL_DELAY(5000);
+	}
+
+	sd_info(("%s: SPI_CTRL=0x%08x SPI_EXT=0x%08x\n",
+	         __FUNCTION__,
+	         SPIPCI_RREG(osh, &regs->spih_ctrl),
+	         SPIPCI_RREG(osh, &regs->spih_ext)));
+
+	return TRUE;
+}
+
+/* Configure PCI-SPI Host Controller High-Speed Clocking mode setting */
+bool
+spi_controller_highspeed_mode(sdioh_info_t *sd, bool hsmode)
+{
+	spih_info_t *si = (spih_info_t *)sd->controller;
+	osl_t *osh = si->osh;
+	spih_regs_t *regs = si->regs;
+
+	if (si->rev >= 10) {
+		if (hsmode) {
+			SPIPCI_ORREG(osh, &regs->spih_ext, 0x10);
+		} else {
+			SPIPCI_ANDREG(osh, &regs->spih_ext, ~0x10);
+		}
+	}
+
+	return TRUE;
+}
+
+/* Disable device interrupt */
+void
+spi_devintr_off(sdioh_info_t *sd)
+{
+	spih_info_t *si = (spih_info_t *)sd->controller;
+	osl_t *osh = si->osh;
+	spih_regs_t *regs = si->regs;
+
+	sd_trace(("%s: %d\n", __FUNCTION__, sd->use_client_ints));
+	if (sd->use_client_ints) {
+		sd->intmask &= ~SPIH_DEV_INTR;
+		SPIPCI_WREG(osh, &regs->spih_int_mask, sd->intmask);	/* Clear Intmask */
+	}
+}
+
+/* Enable device interrupt */
+void
+spi_devintr_on(sdioh_info_t *sd)
+{
+	spih_info_t *si = (spih_info_t *)sd->controller;
+	osl_t *osh = si->osh;
+	spih_regs_t *regs = si->regs;
+
+	ASSERT(sd->lockcount == 0);
+	sd_trace(("%s: %d\n", __FUNCTION__, sd->use_client_ints));
+	if (sd->use_client_ints) {
+		if (SPIPCI_RREG(osh, &regs->spih_ctrl) & 0x02) {
+			/* Ack in case one was pending but is no longer... */
+			SPIPCI_WREG(osh, &regs->spih_int_status, SPIH_DEV_INTR);
+		}
+		sd->intmask |= SPIH_DEV_INTR;
+		/* Set device intr in Intmask */
+		SPIPCI_WREG(osh, &regs->spih_int_mask, sd->intmask);
+	}
+}
+
+/* Check to see if an interrupt belongs to the PCI-SPI Host or a SPI Device */
+bool
+spi_check_client_intr(sdioh_info_t *sd, int *is_dev_intr)
+{
+	spih_info_t *si = (spih_info_t *)sd->controller;
+	osl_t *osh = si->osh;
+	spih_regs_t *regs = si->regs;
+	bool ours = FALSE;
+
+	uint32 raw_int, cur_int;
+	ASSERT(sd);
+
+	if (is_dev_intr)
+		*is_dev_intr = FALSE;
+	raw_int = SPIPCI_RREG(osh, &regs->spih_int_status);
+	cur_int = raw_int & sd->intmask;
+	if (cur_int & SPIH_DEV_INTR) {
+		if (sd->client_intr_enabled && sd->use_client_ints) {
+			sd->intrcount++;
+			ASSERT(sd->intr_handler);
+			ASSERT(sd->intr_handler_arg);
+			(sd->intr_handler)(sd->intr_handler_arg);
+			if (is_dev_intr)
+				*is_dev_intr = TRUE;
+		} else {
+			sd_trace(("%s: Not ready for intr: enabled %d, handler 0x%p\n",
+			        __FUNCTION__, sd->client_intr_enabled, sd->intr_handler));
+		}
+		SPIPCI_WREG(osh, &regs->spih_int_status, SPIH_DEV_INTR);
+		SPIPCI_RREG(osh, &regs->spih_int_status);
+		ours = TRUE;
+	} else if (cur_int & SPIH_CTLR_INTR) {
+		/* Interrupt is from SPI FIFO... just clear and ack it... */
+		sd_trace(("%s: SPI CTLR interrupt: raw_int 0x%08x cur_int 0x%08x\n",
+		          __FUNCTION__, raw_int, cur_int));
+
+		/* Clear the interrupt in the SPI_STAT register */
+		SPIPCI_WREG(osh, &regs->spih_stat, 0x00000080);
+
+		/* Ack the interrupt in the interrupt controller */
+		SPIPCI_WREG(osh, &regs->spih_int_status, SPIH_CTLR_INTR);
+		SPIPCI_RREG(osh, &regs->spih_int_status);
+
+		ours = TRUE;
+	} else if (cur_int & SPIH_WFIFO_INTR) {
+		sd_trace(("%s: SPI WR FIFO Empty interrupt: raw_int 0x%08x cur_int 0x%08x\n",
+		          __FUNCTION__, raw_int, cur_int));
+
+		/* Disable the FIFO Empty Interrupt */
+		sd->intmask &= ~SPIH_WFIFO_INTR;
+		SPIPCI_WREG(osh, &regs->spih_int_mask, sd->intmask);
+
+		sd->local_intrcount++;
+		sd->got_hcint = TRUE;
+		ours = TRUE;
+	} else {
+		/* Not an error: can share interrupts... */
+		sd_trace(("%s: Not my interrupt: raw_int 0x%08x cur_int 0x%08x\n",
+		          __FUNCTION__, raw_int, cur_int));
+		ours = FALSE;
+	}
+
+	return ours;
+}
+
+static void
+hexdump(char *pfx, unsigned char *msg, int msglen)
+{
+	int i, col;
+	char buf[80];
+
+	ASSERT(strlen(pfx) + 49 <= sizeof(buf));
+
+	col = 0;
+
+	for (i = 0; i < msglen; i++, col++) {
+		if (col % 16 == 0)
+			strcpy(buf, pfx);
+		sprintf(buf + strlen(buf), "%02x", msg[i]);
+		if ((col + 1) % 16 == 0)
+			printf("%s\n", buf);
+		else
+			sprintf(buf + strlen(buf), " ");
+	}
+
+	if (col % 16 != 0)
+		printf("%s\n", buf);
+}
+
+/* Send/Receive an SPI Packet */
+void
+spi_sendrecv(sdioh_info_t *sd, uint8 *msg_out, uint8 *msg_in, int msglen)
+{
+	spih_info_t *si = (spih_info_t *)sd->controller;
+	osl_t *osh = si->osh;
+	spih_regs_t *regs = si->regs;
+	uint32 count;
+	uint32 spi_data_out;
+	uint32 spi_data_in;
+	bool yield;
+
+	sd_trace(("%s: enter\n", __FUNCTION__));
+
+	if (bcmpcispi_dump) {
+		printf("SENDRECV(len=%d)\n", msglen);
+		hexdump(" OUT: ", msg_out, msglen);
+	}
+
+#ifdef BCMSDYIELD
+	/* Only yield the CPU and wait for interrupt on Rev 8 and newer FPGA images. */
+	yield = ((msglen > 500) && (si->rev >= 8));
+#else
+	yield = FALSE;
+#endif /* BCMSDYIELD */
+
+	ASSERT(msglen % 4 == 0);
+
+
+	SPIPCI_ANDREG(osh, &regs->spih_gpio_data, ~SPIH_CS);	/* Set GPIO CS# Low (asserted) */
+
+	for (count = 0; count < (uint32)msglen/4; count++) {
+		spi_data_out = ((uint32)((uint32 *)msg_out)[count]);
+		SPIPCI_WREG(osh, &regs->spih_data, spi_data_out);
+	}
+
+#ifdef BCMSDYIELD
+	if (yield) {
+		/* Ack the interrupt in the interrupt controller */
+		SPIPCI_WREG(osh, &regs->spih_int_status, SPIH_WFIFO_INTR);
+		SPIPCI_RREG(osh, &regs->spih_int_status);
+
+		/* Enable the FIFO Empty Interrupt */
+		sd->intmask |= SPIH_WFIFO_INTR;
+		sd->got_hcint = FALSE;
+		SPIPCI_WREG(osh, &regs->spih_int_mask, sd->intmask);
+
+	}
+#endif /* BCMSDYIELD */
+
+	/* Wait for write fifo to empty... */
+	SPIPCI_ANDREG(osh, &regs->spih_gpio_data, ~0x00000020);	/* Set GPIO 5 Low */
+
+	if (yield) {
+		ASSERT((SPIPCI_RREG(sd->osh, &regs->spih_stat) & SPIH_WFEMPTY) == 0);
+	}
+
+	spi_waitbits(sd, yield);
+	SPIPCI_ORREG(osh, &regs->spih_gpio_data, 0x00000020);	/* Set GPIO 5 High (de-asserted) */
+
+	for (count = 0; count < (uint32)msglen/4; count++) {
+		spi_data_in = SPIPCI_RREG(osh, &regs->spih_data);
+		((uint32 *)msg_in)[count] = spi_data_in;
+	}
+
+	/* Set GPIO CS# High (de-asserted) */
+	SPIPCI_ORREG(osh, &regs->spih_gpio_data, SPIH_CS);
+
+	if (bcmpcispi_dump) {
+		hexdump(" IN : ", msg_in, msglen);
+	}
+}
+
+void
+spi_spinbits(sdioh_info_t *sd)
+{
+	spih_info_t *si = (spih_info_t *)sd->controller;
+	osl_t *osh = si->osh;
+	spih_regs_t *regs = si->regs;
+	uint spin_count; /* Spin loop bound check */
+
+	spin_count = 0;
+	while ((SPIPCI_RREG(sd->osh, &regs->spih_stat) & SPIH_WFEMPTY) == 0) {
+		if (spin_count > SPI_SPIN_BOUND) {
+			sd_err(("%s: SPIH_WFEMPTY spin bits out of bound %u times \n",
+				__FUNCTION__, spin_count));
+			ASSERT(FALSE);
+		}
+		spin_count++;
+	}
+
+	/* Wait for SPI Transfer state machine to return to IDLE state.
+	 * The state bits are only implemented in Rev >= 5 FPGA.  These
+	 * bits are hardwired to 00 for Rev < 5, so this check doesn't cause
+	 * any problems.
+	 */
+	spin_count = 0;
+	while ((SPIPCI_RREG(osh, &regs->spih_stat) & SPIH_STATE_MASK) != 0) {
+		if (spin_count > SPI_SPIN_BOUND) {
+			sd_err(("%s: SPIH_STATE_MASK spin bits out of bound %u times \n",
+				__FUNCTION__, spin_count));
+			ASSERT(FALSE);
+		}
+		spin_count++;
+	}
+}
diff --git a/drivers/net/wireless/bcm4329/bcmsdh.c b/drivers/net/wireless/bcm4329/bcmsdh.c
new file mode 100644
index 0000000..4bf5889
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/bcmsdh.c
@@ -0,0 +1,652 @@
+/*
+ *  BCMSDH interface glue
+ *  implement bcmsdh API for SDIOH driver
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmsdh.c,v 1.35.2.1.4.8.6.13 2010/04/06 03:26:57 Exp $
+ */
+/* ****************** BCMSDH Interface Functions *************************** */
+
+#include <typedefs.h>
+#include <bcmdevs.h>
+#include <bcmendian.h>
+#include <bcmutils.h>
+#include <hndsoc.h>
+#include <siutils.h>
+#include <osl.h>
+
+#include <bcmsdh.h>	/* BRCM API for SDIO clients (such as wl, dhd) */
+#include <bcmsdbus.h>	/* common SDIO/controller interface */
+#include <sbsdio.h>	/* BRCM sdio device core */
+
+#include <sdio.h>	/* sdio spec */
+
+#define SDIOH_API_ACCESS_RETRY_LIMIT	2
+const uint bcmsdh_msglevel = BCMSDH_ERROR_VAL;
+
+
+struct bcmsdh_info
+{
+	bool	init_success;	/* underlying driver successfully attached */
+	void	*sdioh;		/* handler for sdioh */
+	uint32  vendevid;	/* Target Vendor and Device ID on SD bus */
+	osl_t   *osh;
+	bool	regfail;	/* Save status of last reg_read/reg_write call */
+	uint32	sbwad;		/* Save backplane window address */
+};
+/* local copy of bcm sd handler */
+bcmsdh_info_t * l_bcmsdh = NULL;
+
+#if defined(OOB_INTR_ONLY) && defined(HW_OOB)
+extern int
+sdioh_enable_hw_oob_intr(void *sdioh, bool enable);
+
+void
+bcmsdh_enable_hw_oob_intr(bcmsdh_info_t *sdh, bool enable)
+{
+	sdioh_enable_hw_oob_intr(sdh->sdioh, enable);
+}
+#endif
+
+bcmsdh_info_t *
+bcmsdh_attach(osl_t *osh, void *cfghdl, void **regsva, uint irq)
+{
+	bcmsdh_info_t *bcmsdh;
+
+	if ((bcmsdh = (bcmsdh_info_t *)MALLOC(osh, sizeof(bcmsdh_info_t))) == NULL) {
+		BCMSDH_ERROR(("bcmsdh_attach: out of memory, malloced %d bytes\n", MALLOCED(osh)));
+		return NULL;
+	}
+	bzero((char *)bcmsdh, sizeof(bcmsdh_info_t));
+
+	/* save the handler locally */
+	l_bcmsdh = bcmsdh;
+
+	if (!(bcmsdh->sdioh = sdioh_attach(osh, cfghdl, irq))) {
+		bcmsdh_detach(osh, bcmsdh);
+		return NULL;
+	}
+
+	bcmsdh->osh = osh;
+	bcmsdh->init_success = TRUE;
+
+	*regsva = (uint32 *)SI_ENUM_BASE;
+
+	/* Report the BAR, to fix if needed */
+	bcmsdh->sbwad = SI_ENUM_BASE;
+	return bcmsdh;
+}
+
+int
+bcmsdh_detach(osl_t *osh, void *sdh)
+{
+	bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+
+	if (bcmsdh != NULL) {
+		if (bcmsdh->sdioh) {
+			sdioh_detach(osh, bcmsdh->sdioh);
+			bcmsdh->sdioh = NULL;
+		}
+		MFREE(osh, bcmsdh, sizeof(bcmsdh_info_t));
+	}
+
+	l_bcmsdh = NULL;
+	return 0;
+}
+
+int
+bcmsdh_iovar_op(void *sdh, const char *name,
+                void *params, int plen, void *arg, int len, bool set)
+{
+	bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+	return sdioh_iovar_op(bcmsdh->sdioh, name, params, plen, arg, len, set);
+}
+
+bool
+bcmsdh_intr_query(void *sdh)
+{
+	bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+	SDIOH_API_RC status;
+	bool on;
+
+	ASSERT(bcmsdh);
+	status = sdioh_interrupt_query(bcmsdh->sdioh, &on);
+	if (SDIOH_API_SUCCESS(status))
+		return FALSE;
+	else
+		return on;
+}
+
+int
+bcmsdh_intr_enable(void *sdh)
+{
+	bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+	SDIOH_API_RC status;
+	ASSERT(bcmsdh);
+
+	status = sdioh_interrupt_set(bcmsdh->sdioh, TRUE);
+	return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR);
+}
+
+int
+bcmsdh_intr_disable(void *sdh)
+{
+	bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+	SDIOH_API_RC status;
+	ASSERT(bcmsdh);
+
+	status = sdioh_interrupt_set(bcmsdh->sdioh, FALSE);
+	return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR);
+}
+
+int
+bcmsdh_intr_reg(void *sdh, bcmsdh_cb_fn_t fn, void *argh)
+{
+	bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+	SDIOH_API_RC status;
+	ASSERT(bcmsdh);
+
+	status = sdioh_interrupt_register(bcmsdh->sdioh, fn, argh);
+	return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR);
+}
+
+int
+bcmsdh_intr_dereg(void *sdh)
+{
+	bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+	SDIOH_API_RC status;
+	ASSERT(bcmsdh);
+
+	status = sdioh_interrupt_deregister(bcmsdh->sdioh);
+	return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR);
+}
+
+#if defined(DHD_DEBUG)
+bool
+bcmsdh_intr_pending(void *sdh)
+{
+	bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+
+	ASSERT(sdh);
+	return sdioh_interrupt_pending(bcmsdh->sdioh);
+}
+#endif
+
+
+int
+bcmsdh_devremove_reg(void *sdh, bcmsdh_cb_fn_t fn, void *argh)
+{
+	ASSERT(sdh);
+
+	/* don't support yet */
+	return BCME_UNSUPPORTED;
+}
+
+uint8
+bcmsdh_cfg_read(void *sdh, uint fnc_num, uint32 addr, int *err)
+{
+	bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+	SDIOH_API_RC status;
+#ifdef SDIOH_API_ACCESS_RETRY_LIMIT
+	int32 retry = 0;
+#endif
+	uint8 data = 0;
+
+	if (!bcmsdh)
+		bcmsdh = l_bcmsdh;
+
+	ASSERT(bcmsdh->init_success);
+
+#ifdef SDIOH_API_ACCESS_RETRY_LIMIT
+	do {
+		if (retry)	/* wait for 1 ms till bus get settled down */
+			OSL_DELAY(1000);
+#endif
+	status = sdioh_cfg_read(bcmsdh->sdioh, fnc_num, addr, (uint8 *)&data);
+#ifdef SDIOH_API_ACCESS_RETRY_LIMIT
+	} while (!SDIOH_API_SUCCESS(status) && (retry++ < SDIOH_API_ACCESS_RETRY_LIMIT));
+#endif
+	if (err)
+		*err = (SDIOH_API_SUCCESS(status) ? 0 : BCME_SDIO_ERROR);
+
+	BCMSDH_INFO(("%s:fun = %d, addr = 0x%x, uint8data = 0x%x\n", __FUNCTION__,
+	            fnc_num, addr, data));
+
+	return data;
+}
+
+void
+bcmsdh_cfg_write(void *sdh, uint fnc_num, uint32 addr, uint8 data, int *err)
+{
+	bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+	SDIOH_API_RC status;
+#ifdef SDIOH_API_ACCESS_RETRY_LIMIT
+	int32 retry = 0;
+#endif
+
+	if (!bcmsdh)
+		bcmsdh = l_bcmsdh;
+
+	ASSERT(bcmsdh->init_success);
+
+#ifdef SDIOH_API_ACCESS_RETRY_LIMIT
+	do {
+		if (retry)	/* wait for 1 ms till bus get settled down */
+			OSL_DELAY(1000);
+#endif
+	status = sdioh_cfg_write(bcmsdh->sdioh, fnc_num, addr, (uint8 *)&data);
+#ifdef SDIOH_API_ACCESS_RETRY_LIMIT
+	} while (!SDIOH_API_SUCCESS(status) && (retry++ < SDIOH_API_ACCESS_RETRY_LIMIT));
+#endif
+	if (err)
+		*err = SDIOH_API_SUCCESS(status) ? 0 : BCME_SDIO_ERROR;
+
+	BCMSDH_INFO(("%s:fun = %d, addr = 0x%x, uint8data = 0x%x\n", __FUNCTION__,
+	            fnc_num, addr, data));
+}
+
+uint32
+bcmsdh_cfg_read_word(void *sdh, uint fnc_num, uint32 addr, int *err)
+{
+	bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+	SDIOH_API_RC status;
+	uint32 data = 0;
+
+	if (!bcmsdh)
+		bcmsdh = l_bcmsdh;
+
+	ASSERT(bcmsdh->init_success);
+
+	status = sdioh_request_word(bcmsdh->sdioh, SDIOH_CMD_TYPE_NORMAL, SDIOH_READ, fnc_num,
+	                            addr, &data, 4);
+
+	if (err)
+		*err = (SDIOH_API_SUCCESS(status) ? 0 : BCME_SDIO_ERROR);
+
+	BCMSDH_INFO(("%s:fun = %d, addr = 0x%x, uint32data = 0x%x\n", __FUNCTION__,
+	            fnc_num, addr, data));
+
+	return data;
+}
+
+void
+bcmsdh_cfg_write_word(void *sdh, uint fnc_num, uint32 addr, uint32 data, int *err)
+{
+	bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+	SDIOH_API_RC status;
+
+	if (!bcmsdh)
+		bcmsdh = l_bcmsdh;
+
+	ASSERT(bcmsdh->init_success);
+
+	status = sdioh_request_word(bcmsdh->sdioh, SDIOH_CMD_TYPE_NORMAL, SDIOH_WRITE, fnc_num,
+	                            addr, &data, 4);
+
+	if (err)
+		*err = (SDIOH_API_SUCCESS(status) ? 0 : BCME_SDIO_ERROR);
+
+	BCMSDH_INFO(("%s:fun = %d, addr = 0x%x, uint32data = 0x%x\n", __FUNCTION__, fnc_num,
+	             addr, data));
+}
+
+
+int
+bcmsdh_cis_read(void *sdh, uint func, uint8 *cis, uint length)
+{
+	bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+	SDIOH_API_RC status;
+
+	uint8 *tmp_buf, *tmp_ptr;
+	uint8 *ptr;
+	bool ascii = func & ~0xf;
+	func &= 0x7;
+
+	if (!bcmsdh)
+		bcmsdh = l_bcmsdh;
+
+	ASSERT(bcmsdh->init_success);
+	ASSERT(cis);
+	ASSERT(length <= SBSDIO_CIS_SIZE_LIMIT);
+
+	status = sdioh_cis_read(bcmsdh->sdioh, func, cis, length);
+
+	if (ascii) {
+		/* Move binary bits to tmp and format them into the provided buffer. */
+		if ((tmp_buf = (uint8 *)MALLOC(bcmsdh->osh, length)) == NULL) {
+			BCMSDH_ERROR(("%s: out of memory\n", __FUNCTION__));
+			return BCME_NOMEM;
+		}
+		bcopy(cis, tmp_buf, length);
+		for (tmp_ptr = tmp_buf, ptr = cis; ptr < (cis + length - 4); tmp_ptr++) {
+			ptr += sprintf((char*)ptr, "%.2x ", *tmp_ptr & 0xff);
+			if ((((tmp_ptr - tmp_buf) + 1) & 0xf) == 0)
+				ptr += sprintf((char *)ptr, "\n");
+		}
+		MFREE(bcmsdh->osh, tmp_buf, length);
+	}
+
+	return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR);
+}
+
+
+static int
+bcmsdhsdio_set_sbaddr_window(void *sdh, uint32 address)
+{
+	int err = 0;
+	bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+	bcmsdh_cfg_write(bcmsdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRLOW,
+	                 (address >> 8) & SBSDIO_SBADDRLOW_MASK, &err);
+	if (!err)
+		bcmsdh_cfg_write(bcmsdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRMID,
+		                 (address >> 16) & SBSDIO_SBADDRMID_MASK, &err);
+	if (!err)
+		bcmsdh_cfg_write(bcmsdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRHIGH,
+		                 (address >> 24) & SBSDIO_SBADDRHIGH_MASK, &err);
+
+
+	return err;
+}
+
+uint32
+bcmsdh_reg_read(void *sdh, uint32 addr, uint size)
+{
+	bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+	SDIOH_API_RC status;
+	uint32 word = 0;
+	uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK;
+
+	BCMSDH_INFO(("%s:fun = 1, addr = 0x%x, ", __FUNCTION__, addr));
+
+	if (!bcmsdh)
+		bcmsdh = l_bcmsdh;
+
+	ASSERT(bcmsdh->init_success);
+
+	if (bar0 != bcmsdh->sbwad) {
+		if (bcmsdhsdio_set_sbaddr_window(bcmsdh, bar0))
+			return 0xFFFFFFFF;
+
+		bcmsdh->sbwad = bar0;
+	}
+
+	addr &= SBSDIO_SB_OFT_ADDR_MASK;
+	if (size == 4)
+		addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
+
+	status = sdioh_request_word(bcmsdh->sdioh, SDIOH_CMD_TYPE_NORMAL,
+		SDIOH_READ, SDIO_FUNC_1, addr, &word, size);
+
+	bcmsdh->regfail = !(SDIOH_API_SUCCESS(status));
+
+	BCMSDH_INFO(("uint32data = 0x%x\n", word));
+
+	/* if ok, return appropriately masked word */
+	if (SDIOH_API_SUCCESS(status)) {
+		switch (size) {
+			case sizeof(uint8):
+				return (word & 0xff);
+			case sizeof(uint16):
+				return (word & 0xffff);
+			case sizeof(uint32):
+				return word;
+			default:
+				bcmsdh->regfail = TRUE;
+
+		}
+	}
+
+	/* otherwise, bad sdio access or invalid size */
+	BCMSDH_ERROR(("%s: error reading addr 0x%04x size %d\n", __FUNCTION__, addr, size));
+	return 0xFFFFFFFF;
+}
+
+uint32
+bcmsdh_reg_write(void *sdh, uint32 addr, uint size, uint32 data)
+{
+	bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+	SDIOH_API_RC status;
+	uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK;
+	int err = 0;
+
+	BCMSDH_INFO(("%s:fun = 1, addr = 0x%x, uint%ddata = 0x%x\n",
+	             __FUNCTION__, addr, size*8, data));
+
+	if (!bcmsdh)
+		bcmsdh = l_bcmsdh;
+
+	ASSERT(bcmsdh->init_success);
+
+	if (bar0 != bcmsdh->sbwad) {
+		if ((err = bcmsdhsdio_set_sbaddr_window(bcmsdh, bar0)))
+			return err;
+
+		bcmsdh->sbwad = bar0;
+	}
+
+	addr &= SBSDIO_SB_OFT_ADDR_MASK;
+	if (size == 4)
+		addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
+	status = sdioh_request_word(bcmsdh->sdioh, SDIOH_CMD_TYPE_NORMAL, SDIOH_WRITE, SDIO_FUNC_1,
+	                            addr, &data, size);
+	bcmsdh->regfail = !(SDIOH_API_SUCCESS(status));
+
+	if (SDIOH_API_SUCCESS(status))
+		return 0;
+
+	BCMSDH_ERROR(("%s: error writing 0x%08x to addr 0x%04x size %d\n",
+	              __FUNCTION__, data, addr, size));
+	return 0xFFFFFFFF;
+}
+
+bool
+bcmsdh_regfail(void *sdh)
+{
+	return ((bcmsdh_info_t *)sdh)->regfail;
+}
+
+int
+bcmsdh_recv_buf(void *sdh, uint32 addr, uint fn, uint flags,
+                uint8 *buf, uint nbytes, void *pkt,
+                bcmsdh_cmplt_fn_t complete, void *handle)
+{
+	bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+	SDIOH_API_RC status;
+	uint incr_fix;
+	uint width;
+	uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK;
+	int err = 0;
+
+	ASSERT(bcmsdh);
+	ASSERT(bcmsdh->init_success);
+
+	BCMSDH_INFO(("%s:fun = %d, addr = 0x%x, size = %d\n",
+	             __FUNCTION__, fn, addr, nbytes));
+
+	/* Async not implemented yet */
+	ASSERT(!(flags & SDIO_REQ_ASYNC));
+	if (flags & SDIO_REQ_ASYNC)
+		return BCME_UNSUPPORTED;
+
+	if (bar0 != bcmsdh->sbwad) {
+		if ((err = bcmsdhsdio_set_sbaddr_window(bcmsdh, bar0)))
+			return err;
+
+		bcmsdh->sbwad = bar0;
+	}
+
+	addr &= SBSDIO_SB_OFT_ADDR_MASK;
+
+	incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC;
+	width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
+	if (width == 4)
+		addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
+
+	status = sdioh_request_buffer(bcmsdh->sdioh, SDIOH_DATA_PIO, incr_fix,
+	                              SDIOH_READ, fn, addr, width, nbytes, buf, pkt);
+
+	return (SDIOH_API_SUCCESS(status) ? 0 : BCME_SDIO_ERROR);
+}
+
+int
+bcmsdh_send_buf(void *sdh, uint32 addr, uint fn, uint flags,
+                uint8 *buf, uint nbytes, void *pkt,
+                bcmsdh_cmplt_fn_t complete, void *handle)
+{
+	bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+	SDIOH_API_RC status;
+	uint incr_fix;
+	uint width;
+	uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK;
+	int err = 0;
+
+	ASSERT(bcmsdh);
+	ASSERT(bcmsdh->init_success);
+
+	BCMSDH_INFO(("%s:fun = %d, addr = 0x%x, size = %d\n",
+	            __FUNCTION__, fn, addr, nbytes));
+
+	/* Async not implemented yet */
+	ASSERT(!(flags & SDIO_REQ_ASYNC));
+	if (flags & SDIO_REQ_ASYNC)
+		return BCME_UNSUPPORTED;
+
+	if (bar0 != bcmsdh->sbwad) {
+		if ((err = bcmsdhsdio_set_sbaddr_window(bcmsdh, bar0)))
+			return err;
+
+		bcmsdh->sbwad = bar0;
+	}
+
+	addr &= SBSDIO_SB_OFT_ADDR_MASK;
+
+	incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC;
+	width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
+	if (width == 4)
+		addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
+
+	status = sdioh_request_buffer(bcmsdh->sdioh, SDIOH_DATA_PIO, incr_fix,
+	                              SDIOH_WRITE, fn, addr, width, nbytes, buf, pkt);
+
+	return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR);
+}
+
+int
+bcmsdh_rwdata(void *sdh, uint rw, uint32 addr, uint8 *buf, uint nbytes)
+{
+	bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+	SDIOH_API_RC status;
+
+	ASSERT(bcmsdh);
+	ASSERT(bcmsdh->init_success);
+	ASSERT((addr & SBSDIO_SBWINDOW_MASK) == 0);
+
+	addr &= SBSDIO_SB_OFT_ADDR_MASK;
+	addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
+
+	status = sdioh_request_buffer(bcmsdh->sdioh, SDIOH_DATA_PIO, SDIOH_DATA_INC,
+	                              (rw ? SDIOH_WRITE : SDIOH_READ), SDIO_FUNC_1,
+	                              addr, 4, nbytes, buf, NULL);
+
+	return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR);
+}
+
+int
+bcmsdh_abort(void *sdh, uint fn)
+{
+	bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+
+	return sdioh_abort(bcmsdh->sdioh, fn);
+}
+
+int
+bcmsdh_start(void *sdh, int stage)
+{
+	bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+
+	return sdioh_start(bcmsdh->sdioh, stage);
+}
+
+int
+bcmsdh_stop(void *sdh)
+{
+	bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+
+	return sdioh_stop(bcmsdh->sdioh);
+}
+
+
+int
+bcmsdh_query_device(void *sdh)
+{
+	bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+	bcmsdh->vendevid = (VENDOR_BROADCOM << 16) | 0;
+	return (bcmsdh->vendevid);
+}
+
+uint
+bcmsdh_query_iofnum(void *sdh)
+{
+	bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+
+	if (!bcmsdh)
+		bcmsdh = l_bcmsdh;
+
+	return (sdioh_query_iofnum(bcmsdh->sdioh));
+}
+
+int
+bcmsdh_reset(bcmsdh_info_t *sdh)
+{
+	bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+
+	return sdioh_sdio_reset(bcmsdh->sdioh);
+}
+
+void *bcmsdh_get_sdioh(bcmsdh_info_t *sdh)
+{
+	ASSERT(sdh);
+	return sdh->sdioh;
+}
+
+/* Function to pass device-status bits to DHD. */
+uint32
+bcmsdh_get_dstatus(void *sdh)
+{
+	return 0;
+}
+uint32
+bcmsdh_cur_sbwad(void *sdh)
+{
+	bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+
+	if (!bcmsdh)
+		bcmsdh = l_bcmsdh;
+
+	return (bcmsdh->sbwad);
+}
+
+void
+bcmsdh_chipinfo(void *sdh, uint32 chip, uint32 chiprev)
+{
+	return;
+}
diff --git a/drivers/net/wireless/bcm4329/bcmsdh_linux.c b/drivers/net/wireless/bcm4329/bcmsdh_linux.c
new file mode 100644
index 0000000..6d6097b
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/bcmsdh_linux.c
@@ -0,0 +1,735 @@
+/*
+ * SDIO access interface for drivers - linux specific (pci only)
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmsdh_linux.c,v 1.42.10.10.2.14.4.2 2010/09/15 00:30:11 Exp $
+ */
+
+/**
+ * @file bcmsdh_linux.c
+ */
+
+#define __UNDEF_NO_VERSION__
+
+#include <typedefs.h>
+#include <linuxver.h>
+
+#include <linux/pci.h>
+#include <linux/completion.h>
+
+#include <osl.h>
+#include <pcicfg.h>
+#include <bcmdefs.h>
+#include <bcmdevs.h>
+
+#if defined(OOB_INTR_ONLY)
+#include <linux/irq.h>
+extern void dhdsdio_isr(void * args);
+#include <bcmutils.h>
+#include <dngl_stats.h>
+#include <dhd.h>
+#endif /* defined(OOB_INTR_ONLY) */
+#if defined(CONFIG_MACH_SANDGATE2G) || defined(CONFIG_MACH_LOGICPD_PXA270)
+#if !defined(BCMPLATFORM_BUS)
+#define BCMPLATFORM_BUS
+#endif /* !defined(BCMPLATFORM_BUS) */
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19))
+#include <linux/platform_device.h>
+#endif /* KERNEL_VERSION(2, 6, 19) */
+#endif /* CONFIG_MACH_SANDGATE2G || CONFIG_MACH_LOGICPD_PXA270 */
+
+/**
+ * SDIO Host Controller info
+ */
+typedef struct bcmsdh_hc bcmsdh_hc_t;
+
+struct bcmsdh_hc {
+	bcmsdh_hc_t *next;
+#ifdef BCMPLATFORM_BUS
+	struct device *dev;			/* platform device handle */
+#else
+	struct pci_dev *dev;		/* pci device handle */
+#endif /* BCMPLATFORM_BUS */
+	osl_t *osh;
+	void *regs;			/* SDIO Host Controller address */
+	bcmsdh_info_t *sdh;		/* SDIO Host Controller handle */
+	void *ch;
+	unsigned int oob_irq;
+	unsigned long oob_flags; /* OOB Host specifiction as edge and etc */
+	bool oob_irq_registered;
+#if defined(OOB_INTR_ONLY)
+	spinlock_t irq_lock;
+#endif
+};
+static bcmsdh_hc_t *sdhcinfo = NULL;
+
+/* driver info, initialized when bcmsdh_register is called */
+static bcmsdh_driver_t drvinfo = {NULL, NULL};
+
+/* debugging macros */
+#define SDLX_MSG(x)
+
+/**
+ * Checks to see if vendor and device IDs match a supported SDIO Host Controller.
+ */
+bool
+bcmsdh_chipmatch(uint16 vendor, uint16 device)
+{
+	/* Add other vendors and devices as required */
+
+#ifdef BCMSDIOH_STD
+	/* Check for Arasan host controller */
+	if (vendor == VENDOR_SI_IMAGE) {
+		return (TRUE);
+	}
+	/* Check for BRCM 27XX Standard host controller */
+	if (device == BCM27XX_SDIOH_ID && vendor == VENDOR_BROADCOM) {
+		return (TRUE);
+	}
+	/* Check for BRCM Standard host controller */
+	if (device == SDIOH_FPGA_ID && vendor == VENDOR_BROADCOM) {
+		return (TRUE);
+	}
+	/* Check for TI PCIxx21 Standard host controller */
+	if (device == PCIXX21_SDIOH_ID && vendor == VENDOR_TI) {
+		return (TRUE);
+	}
+	if (device == PCIXX21_SDIOH0_ID && vendor == VENDOR_TI) {
+		return (TRUE);
+	}
+	/* Ricoh R5C822 Standard SDIO Host */
+	if (device == R5C822_SDIOH_ID && vendor == VENDOR_RICOH) {
+		return (TRUE);
+	}
+	/* JMicron Standard SDIO Host */
+	if (device == JMICRON_SDIOH_ID && vendor == VENDOR_JMICRON) {
+		return (TRUE);
+	}
+
+#endif /* BCMSDIOH_STD */
+#ifdef BCMSDIOH_SPI
+	/* This is the PciSpiHost. */
+	if (device == SPIH_FPGA_ID && vendor == VENDOR_BROADCOM) {
+		printf("Found PCI SPI Host Controller\n");
+		return (TRUE);
+	}
+
+#endif /* BCMSDIOH_SPI */
+
+	return (FALSE);
+}
+
+#if defined(BCMPLATFORM_BUS)
+#if defined(BCMLXSDMMC)
+/* forward declarations */
+int bcmsdh_probe(struct device *dev);
+int bcmsdh_remove(struct device *dev);
+
+EXPORT_SYMBOL(bcmsdh_probe);
+EXPORT_SYMBOL(bcmsdh_remove);
+
+#else
+/* forward declarations */
+static int __devinit bcmsdh_probe(struct device *dev);
+static int __devexit bcmsdh_remove(struct device *dev);
+#endif /* BCMLXSDMMC */
+
+#ifndef BCMLXSDMMC
+static struct device_driver bcmsdh_driver = {
+	.name		= "pxa2xx-mci",
+	.bus		= &platform_bus_type,
+	.probe		= bcmsdh_probe,
+	.remove		= bcmsdh_remove,
+	.suspend	= NULL,
+	.resume		= NULL,
+	};
+#endif /* BCMLXSDMMC */
+
+#ifndef BCMLXSDMMC
+static
+#endif /* BCMLXSDMMC */
+int bcmsdh_probe(struct device *dev)
+{
+	osl_t *osh = NULL;
+	bcmsdh_hc_t *sdhc = NULL;
+	ulong regs = 0;
+	bcmsdh_info_t *sdh = NULL;
+#if !defined(BCMLXSDMMC) && defined(BCMPLATFORM_BUS)
+	struct platform_device *pdev;
+	struct resource *r;
+#endif /* BCMLXSDMMC */
+	int irq = 0;
+	uint32 vendevid;
+	unsigned long irq_flags = 0;
+
+#if !defined(BCMLXSDMMC) && defined(BCMPLATFORM_BUS)
+	pdev = to_platform_device(dev);
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	irq = platform_get_irq(pdev, 0);
+	if (!r || irq == NO_IRQ)
+		return -ENXIO;
+#endif /* BCMLXSDMMC */
+
+#if defined(OOB_INTR_ONLY)
+#ifdef HW_OOB
+	irq_flags = \
+		IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE;
+#else
+	 irq_flags = IRQF_TRIGGER_FALLING;
+#endif /* HW_OOB */
+	irq = dhd_customer_oob_irq_map(&irq_flags);
+	if  (irq < 0) {
+		SDLX_MSG(("%s: Host irq is not defined\n", __FUNCTION__));
+		return 1;
+	}
+#endif /* defined(OOB_INTR_ONLY) */
+	/* allocate SDIO Host Controller state info */
+	if (!(osh = osl_attach(dev, PCI_BUS, FALSE))) {
+		SDLX_MSG(("%s: osl_attach failed\n", __FUNCTION__));
+		goto err;
+	}
+	if (!(sdhc = MALLOC(osh, sizeof(bcmsdh_hc_t)))) {
+		SDLX_MSG(("%s: out of memory, allocated %d bytes\n",
+			__FUNCTION__,
+			MALLOCED(osh)));
+		goto err;
+	}
+	bzero(sdhc, sizeof(bcmsdh_hc_t));
+	sdhc->osh = osh;
+
+	sdhc->dev = (void *)dev;
+
+#ifdef BCMLXSDMMC
+	if (!(sdh = bcmsdh_attach(osh, (void *)0,
+	                          (void **)&regs, irq))) {
+		SDLX_MSG(("%s: bcmsdh_attach failed\n", __FUNCTION__));
+		goto err;
+	}
+#else
+	if (!(sdh = bcmsdh_attach(osh, (void *)r->start,
+	                          (void **)&regs, irq))) {
+		SDLX_MSG(("%s: bcmsdh_attach failed\n", __FUNCTION__));
+		goto err;
+	}
+#endif /* BCMLXSDMMC */
+	sdhc->sdh = sdh;
+	sdhc->oob_irq = irq;
+	sdhc->oob_flags = irq_flags;
+	sdhc->oob_irq_registered = FALSE;	/* to make sure.. */
+#if defined(OOB_INTR_ONLY)
+	spin_lock_init(&sdhc->irq_lock);
+#endif
+
+	/* chain SDIO Host Controller info together */
+	sdhc->next = sdhcinfo;
+	sdhcinfo = sdhc;
+	/* Read the vendor/device ID from the CIS */
+	vendevid = bcmsdh_query_device(sdh);
+
+	/* try to attach to the target device */
+	if (!(sdhc->ch = drvinfo.attach((vendevid >> 16),
+	                                 (vendevid & 0xFFFF), 0, 0, 0, 0,
+	                                (void *)regs, NULL, sdh))) {
+		SDLX_MSG(("%s: device attach failed\n", __FUNCTION__));
+		goto err;
+	}
+
+	return 0;
+
+	/* error handling */
+err:
+	if (sdhc) {
+		if (sdhc->sdh)
+			bcmsdh_detach(sdhc->osh, sdhc->sdh);
+		MFREE(osh, sdhc, sizeof(bcmsdh_hc_t));
+	}
+	if (osh)
+		osl_detach(osh);
+	return -ENODEV;
+}
+
+#ifndef BCMLXSDMMC
+static
+#endif /* BCMLXSDMMC */
+int bcmsdh_remove(struct device *dev)
+{
+	bcmsdh_hc_t *sdhc, *prev;
+	osl_t *osh;
+
+	sdhc = sdhcinfo;
+	drvinfo.detach(sdhc->ch);
+	bcmsdh_detach(sdhc->osh, sdhc->sdh);
+	/* find the SDIO Host Controller state for this pdev and take it out from the list */
+	for (sdhc = sdhcinfo, prev = NULL; sdhc; sdhc = sdhc->next) {
+		if (sdhc->dev == (void *)dev) {
+			if (prev)
+				prev->next = sdhc->next;
+			else
+				sdhcinfo = NULL;
+			break;
+		}
+		prev = sdhc;
+	}
+	if (!sdhc) {
+		SDLX_MSG(("%s: failed\n", __FUNCTION__));
+		return 0;
+	}
+
+
+	/* release SDIO Host Controller info */
+	osh = sdhc->osh;
+	MFREE(osh, sdhc, sizeof(bcmsdh_hc_t));
+	osl_detach(osh);
+
+#if !defined(BCMLXSDMMC) || defined(OOB_INTR_ONLY)
+	dev_set_drvdata(dev, NULL);
+#endif /* !defined(BCMLXSDMMC) */
+
+	return 0;
+}
+
+#else /* BCMPLATFORM_BUS */
+
+#if !defined(BCMLXSDMMC)
+/* forward declarations for PCI probe and remove functions. */
+static int __devinit bcmsdh_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent);
+static void __devexit bcmsdh_pci_remove(struct pci_dev *pdev);
+
+/**
+ * pci id table
+ */
+static struct pci_device_id bcmsdh_pci_devid[] __devinitdata = {
+	{ vendor: PCI_ANY_ID,
+	device: PCI_ANY_ID,
+	subvendor: PCI_ANY_ID,
+	subdevice: PCI_ANY_ID,
+	class: 0,
+	class_mask: 0,
+	driver_data: 0,
+	},
+	{ 0, }
+};
+MODULE_DEVICE_TABLE(pci, bcmsdh_pci_devid);
+
+/**
+ * SDIO Host Controller pci driver info
+ */
+static struct pci_driver bcmsdh_pci_driver = {
+	node:		{},
+	name:		"bcmsdh",
+	id_table:	bcmsdh_pci_devid,
+	probe:		bcmsdh_pci_probe,
+	remove:		bcmsdh_pci_remove,
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
+	save_state:	NULL,
+#endif
+	suspend:	NULL,
+	resume:		NULL,
+};
+
+
+extern uint sd_pci_slot;	/* Force detection to a particular PCI */
+				/* slot only . Allows for having multiple */
+				/* WL devices at once in a PC */
+				/* Only one instance of dhd will be */
+				/* usable at a time */
+				/* Upper word is bus number, */
+				/* lower word is slot number */
+				/* Default value of 0xFFFFffff turns this */
+				/* off */
+module_param(sd_pci_slot, uint, 0);
+
+
+/**
+ * Detect supported SDIO Host Controller and attach if found.
+ *
+ * Determine if the device described by pdev is a supported SDIO Host
+ * Controller.  If so, attach to it and attach to the target device.
+ */
+static int __devinit
+bcmsdh_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	osl_t *osh = NULL;
+	bcmsdh_hc_t *sdhc = NULL;
+	ulong regs;
+	bcmsdh_info_t *sdh = NULL;
+	int rc;
+
+	if (sd_pci_slot != 0xFFFFffff) {
+		if (pdev->bus->number != (sd_pci_slot>>16) ||
+			PCI_SLOT(pdev->devfn) != (sd_pci_slot&0xffff)) {
+			SDLX_MSG(("%s: %s: bus %X, slot %X, vend %X, dev %X\n",
+			          __FUNCTION__,
+			          bcmsdh_chipmatch(pdev->vendor, pdev->device) ?
+			          "Found compatible SDIOHC" :
+			          "Probing unknown device",
+			          pdev->bus->number, PCI_SLOT(pdev->devfn),
+			          pdev->vendor, pdev->device));
+			return -ENODEV;
+		}
+		SDLX_MSG(("%s: %s: bus %X, slot %X, vendor %X, device %X (good PCI location)\n",
+		          __FUNCTION__,
+		          bcmsdh_chipmatch(pdev->vendor, pdev->device) ?
+		          "Using compatible SDIOHC" :
+		          "WARNING, forced use of unkown device",
+		          pdev->bus->number, PCI_SLOT(pdev->devfn),
+		          pdev->vendor, pdev->device));
+	}
+
+	if ((pdev->vendor == VENDOR_TI) && ((pdev->device == PCIXX21_FLASHMEDIA_ID) ||
+	    (pdev->device == PCIXX21_FLASHMEDIA0_ID))) {
+		uint32 config_reg;
+
+		SDLX_MSG(("%s: Disabling TI FlashMedia Controller.\n", __FUNCTION__));
+		if (!(osh = osl_attach(pdev, PCI_BUS, FALSE))) {
+			SDLX_MSG(("%s: osl_attach failed\n", __FUNCTION__));
+			goto err;
+		}
+
+		config_reg = OSL_PCI_READ_CONFIG(osh, 0x4c, 4);
+
+		/*
+		 * Set MMC_SD_DIS bit in FlashMedia Controller.
+		 * Disbling the SD/MMC Controller in the FlashMedia Controller
+		 * allows the Standard SD Host Controller to take over control
+		 * of the SD Slot.
+		 */
+		config_reg |= 0x02;
+		OSL_PCI_WRITE_CONFIG(osh, 0x4c, 4, config_reg);
+		osl_detach(osh);
+	}
+	/* match this pci device with what we support */
+	/* we can't solely rely on this to believe it is our SDIO Host Controller! */
+	if (!bcmsdh_chipmatch(pdev->vendor, pdev->device)) {
+		return -ENODEV;
+	}
+
+	/* this is a pci device we might support */
+	SDLX_MSG(("%s: Found possible SDIO Host Controller: bus %d slot %d func %d irq %d\n",
+		__FUNCTION__,
+		pdev->bus->number, PCI_SLOT(pdev->devfn),
+		PCI_FUNC(pdev->devfn), pdev->irq));
+
+	/* use bcmsdh_query_device() to get the vendor ID of the target device so
+	 * it will eventually appear in the Broadcom string on the console
+	 */
+
+	/* allocate SDIO Host Controller state info */
+	if (!(osh = osl_attach(pdev, PCI_BUS, FALSE))) {
+		SDLX_MSG(("%s: osl_attach failed\n", __FUNCTION__));
+		goto err;
+	}
+	if (!(sdhc = MALLOC(osh, sizeof(bcmsdh_hc_t)))) {
+		SDLX_MSG(("%s: out of memory, allocated %d bytes\n",
+			__FUNCTION__,
+			MALLOCED(osh)));
+		goto err;
+	}
+	bzero(sdhc, sizeof(bcmsdh_hc_t));
+	sdhc->osh = osh;
+
+	sdhc->dev = pdev;
+
+	/* map to address where host can access */
+	pci_set_master(pdev);
+	rc = pci_enable_device(pdev);
+	if (rc) {
+		SDLX_MSG(("%s: Cannot enable PCI device\n", __FUNCTION__));
+		goto err;
+	}
+	if (!(sdh = bcmsdh_attach(osh, (void *)(uintptr)pci_resource_start(pdev, 0),
+	                          (void **)&regs, pdev->irq))) {
+		SDLX_MSG(("%s: bcmsdh_attach failed\n", __FUNCTION__));
+		goto err;
+	}
+
+	sdhc->sdh = sdh;
+
+	/* try to attach to the target device */
+	if (!(sdhc->ch = drvinfo.attach(VENDOR_BROADCOM, /* pdev->vendor, */
+	                                bcmsdh_query_device(sdh) & 0xFFFF, 0, 0, 0, 0,
+	                                (void *)regs, NULL, sdh))) {
+		SDLX_MSG(("%s: device attach failed\n", __FUNCTION__));
+		goto err;
+	}
+
+	/* chain SDIO Host Controller info together */
+	sdhc->next = sdhcinfo;
+	sdhcinfo = sdhc;
+
+	return 0;
+
+	/* error handling */
+err:
+	if (sdhc->sdh)
+		bcmsdh_detach(sdhc->osh, sdhc->sdh);
+	if (sdhc)
+		MFREE(osh, sdhc, sizeof(bcmsdh_hc_t));
+	if (osh)
+		osl_detach(osh);
+	return -ENODEV;
+}
+
+
+/**
+ * Detach from target devices and SDIO Host Controller
+ */
+static void __devexit
+bcmsdh_pci_remove(struct pci_dev *pdev)
+{
+	bcmsdh_hc_t *sdhc, *prev;
+	osl_t *osh;
+
+	/* find the SDIO Host Controller state for this pdev and take it out from the list */
+	for (sdhc = sdhcinfo, prev = NULL; sdhc; sdhc = sdhc->next) {
+		if (sdhc->dev == pdev) {
+			if (prev)
+				prev->next = sdhc->next;
+			else
+				sdhcinfo = NULL;
+			break;
+		}
+		prev = sdhc;
+	}
+	if (!sdhc)
+		return;
+
+	drvinfo.detach(sdhc->ch);
+
+	bcmsdh_detach(sdhc->osh, sdhc->sdh);
+
+	/* release SDIO Host Controller info */
+	osh = sdhc->osh;
+	MFREE(osh, sdhc, sizeof(bcmsdh_hc_t));
+	osl_detach(osh);
+}
+#endif /* BCMLXSDMMC */
+#endif /* BCMPLATFORM_BUS */
+
+extern int sdio_function_init(void);
+
+int
+bcmsdh_register(bcmsdh_driver_t *driver)
+{
+	int error = 0;
+
+	drvinfo = *driver;
+
+#if defined(BCMPLATFORM_BUS)
+#if defined(BCMLXSDMMC)
+	SDLX_MSG(("Linux Kernel SDIO/MMC Driver\n"));
+	error = sdio_function_init();
+#else
+	SDLX_MSG(("Intel PXA270 SDIO Driver\n"));
+	error = driver_register(&bcmsdh_driver);
+#endif /* defined(BCMLXSDMMC) */
+	return error;
+#endif /* defined(BCMPLATFORM_BUS) */
+
+#if !defined(BCMPLATFORM_BUS) && !defined(BCMLXSDMMC)
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
+	if (!(error = pci_module_init(&bcmsdh_pci_driver)))
+		return 0;
+#else
+	if (!(error = pci_register_driver(&bcmsdh_pci_driver)))
+		return 0;
+#endif
+
+	SDLX_MSG(("%s: pci_module_init failed 0x%x\n", __FUNCTION__, error));
+#endif /* BCMPLATFORM_BUS */
+
+	return error;
+}
+
+extern void sdio_function_cleanup(void);
+
+void
+bcmsdh_unregister(void)
+{
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
+	if (bcmsdh_pci_driver.node.next)
+#endif
+
+#if defined(BCMPLATFORM_BUS) && !defined(BCMLXSDMMC)
+		driver_unregister(&bcmsdh_driver);
+#endif
+#if defined(BCMLXSDMMC)
+	sdio_function_cleanup();
+#endif /* BCMLXSDMMC */
+#if !defined(BCMPLATFORM_BUS) && !defined(BCMLXSDMMC)
+		pci_unregister_driver(&bcmsdh_pci_driver);
+#endif /* BCMPLATFORM_BUS */
+}
+
+#if defined(OOB_INTR_ONLY)
+void bcmsdh_oob_intr_set(bool enable)
+{
+	static bool curstate = 1;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sdhcinfo->irq_lock, flags);
+	if (curstate != enable) {
+		if (enable)
+			enable_irq(sdhcinfo->oob_irq);
+		else
+			disable_irq_nosync(sdhcinfo->oob_irq);
+		curstate = enable;
+	}
+	spin_unlock_irqrestore(&sdhcinfo->irq_lock, flags);
+}
+
+static irqreturn_t wlan_oob_irq(int irq, void *dev_id)
+{
+	dhd_pub_t *dhdp;
+
+	dhdp = (dhd_pub_t *)dev_get_drvdata(sdhcinfo->dev);
+
+	bcmsdh_oob_intr_set(0);
+
+	if (dhdp == NULL) {
+		SDLX_MSG(("Out of band GPIO interrupt fired way too early\n"));
+		return IRQ_HANDLED;
+	}
+
+	dhdsdio_isr((void *)dhdp->bus);
+
+	return IRQ_HANDLED;
+}
+
+int bcmsdh_register_oob_intr(void * dhdp)
+{
+	int error = 0;
+
+	SDLX_MSG(("%s Enter\n", __FUNCTION__));
+
+/* Example of  HW_OOB for HW2: please refer to your host  specifiction */
+/* sdhcinfo->oob_flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE; */
+
+	dev_set_drvdata(sdhcinfo->dev, dhdp);
+
+	if (!sdhcinfo->oob_irq_registered) {
+		SDLX_MSG(("%s IRQ=%d Type=%X \n", __FUNCTION__, \
+				(int)sdhcinfo->oob_irq, (int)sdhcinfo->oob_flags));
+		/* Refer to customer Host IRQ docs about proper irqflags definition */
+		error = request_irq(sdhcinfo->oob_irq, wlan_oob_irq, sdhcinfo->oob_flags,
+			"bcmsdh_sdmmc", NULL);
+		if (error)
+			return -ENODEV;
+
+		enable_irq_wake(sdhcinfo->oob_irq);
+		sdhcinfo->oob_irq_registered = TRUE;
+	}
+
+	return 0;
+}
+
+void bcmsdh_set_irq(int flag)
+{
+	if (sdhcinfo->oob_irq_registered) {
+		SDLX_MSG(("%s Flag = %d", __FUNCTION__, flag));
+		if (flag) {
+			enable_irq(sdhcinfo->oob_irq);
+			enable_irq_wake(sdhcinfo->oob_irq);
+		} else {
+			disable_irq_wake(sdhcinfo->oob_irq);
+			disable_irq(sdhcinfo->oob_irq);
+		}
+	}
+}
+
+void bcmsdh_unregister_oob_intr(void)
+{
+	SDLX_MSG(("%s: Enter\n", __FUNCTION__));
+
+	if (sdhcinfo->oob_irq_registered) {
+		disable_irq_wake(sdhcinfo->oob_irq);
+		disable_irq(sdhcinfo->oob_irq);	/* just in case.. */
+		free_irq(sdhcinfo->oob_irq, NULL);
+		sdhcinfo->oob_irq_registered = FALSE;
+	}
+}
+#endif /* defined(OOB_INTR_ONLY) */
+/* Module parameters specific to each host-controller driver */
+
+extern uint sd_msglevel;	/* Debug message level */
+module_param(sd_msglevel, uint, 0);
+
+extern uint sd_power;	/* 0 = SD Power OFF, 1 = SD Power ON. */
+module_param(sd_power, uint, 0);
+
+extern uint sd_clock;	/* SD Clock Control, 0 = SD Clock OFF, 1 = SD Clock ON */
+module_param(sd_clock, uint, 0);
+
+extern uint sd_divisor;	/* Divisor (-1 means external clock) */
+module_param(sd_divisor, uint, 0);
+
+extern uint sd_sdmode;	/* Default is SD4, 0=SPI, 1=SD1, 2=SD4 */
+module_param(sd_sdmode, uint, 0);
+
+extern uint sd_hiok;	/* Ok to use hi-speed mode */
+module_param(sd_hiok, uint, 0);
+
+extern uint sd_f2_blocksize;
+module_param(sd_f2_blocksize, int, 0);
+
+
+#ifdef BCMSDH_MODULE
+EXPORT_SYMBOL(bcmsdh_attach);
+EXPORT_SYMBOL(bcmsdh_detach);
+EXPORT_SYMBOL(bcmsdh_intr_query);
+EXPORT_SYMBOL(bcmsdh_intr_enable);
+EXPORT_SYMBOL(bcmsdh_intr_disable);
+EXPORT_SYMBOL(bcmsdh_intr_reg);
+EXPORT_SYMBOL(bcmsdh_intr_dereg);
+
+#if defined(DHD_DEBUG)
+EXPORT_SYMBOL(bcmsdh_intr_pending);
+#endif
+
+EXPORT_SYMBOL(bcmsdh_devremove_reg);
+EXPORT_SYMBOL(bcmsdh_cfg_read);
+EXPORT_SYMBOL(bcmsdh_cfg_write);
+EXPORT_SYMBOL(bcmsdh_cis_read);
+EXPORT_SYMBOL(bcmsdh_reg_read);
+EXPORT_SYMBOL(bcmsdh_reg_write);
+EXPORT_SYMBOL(bcmsdh_regfail);
+EXPORT_SYMBOL(bcmsdh_send_buf);
+EXPORT_SYMBOL(bcmsdh_recv_buf);
+
+EXPORT_SYMBOL(bcmsdh_rwdata);
+EXPORT_SYMBOL(bcmsdh_abort);
+EXPORT_SYMBOL(bcmsdh_query_device);
+EXPORT_SYMBOL(bcmsdh_query_iofnum);
+EXPORT_SYMBOL(bcmsdh_iovar_op);
+EXPORT_SYMBOL(bcmsdh_register);
+EXPORT_SYMBOL(bcmsdh_unregister);
+EXPORT_SYMBOL(bcmsdh_chipmatch);
+EXPORT_SYMBOL(bcmsdh_reset);
+
+EXPORT_SYMBOL(bcmsdh_get_dstatus);
+EXPORT_SYMBOL(bcmsdh_cfg_read_word);
+EXPORT_SYMBOL(bcmsdh_cfg_write_word);
+EXPORT_SYMBOL(bcmsdh_cur_sbwad);
+EXPORT_SYMBOL(bcmsdh_chipinfo);
+
+#endif /* BCMSDH_MODULE */
diff --git a/drivers/net/wireless/bcm4329/bcmsdh_sdmmc.c b/drivers/net/wireless/bcm4329/bcmsdh_sdmmc.c
new file mode 100644
index 0000000..031367b
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/bcmsdh_sdmmc.c
@@ -0,0 +1,1304 @@
+/*
+ * BCMSDH Function Driver for the native SDIO/MMC driver in the Linux Kernel
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmsdh_sdmmc.c,v 1.1.2.5.6.30.4.1 2010/09/02 23:12:21 Exp $
+ */
+#include <typedefs.h>
+
+#include <bcmdevs.h>
+#include <bcmendian.h>
+#include <bcmutils.h>
+#include <osl.h>
+#include <sdio.h>	/* SDIO Device and Protocol Specs */
+#include <sdioh.h>	/* SDIO Host Controller Specification */
+#include <bcmsdbus.h>	/* bcmsdh to/from specific controller APIs */
+#include <sdiovar.h>	/* ioctl/iovars */
+
+#include <linux/mmc/core.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+
+#include <dngl_stats.h>
+#include <dhd.h>
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP)
+#include <linux/suspend.h>
+extern volatile bool dhd_mmc_suspend;
+#endif
+#include "bcmsdh_sdmmc.h"
+
+#ifndef BCMSDH_MODULE
+extern int sdio_function_init(void);
+extern void sdio_function_cleanup(void);
+#endif /* BCMSDH_MODULE */
+
+#if !defined(OOB_INTR_ONLY)
+static void IRQHandler(struct sdio_func *func);
+static void IRQHandlerF2(struct sdio_func *func);
+#endif /* !defined(OOB_INTR_ONLY) */
+static int sdioh_sdmmc_get_cisaddr(sdioh_info_t *sd, uint32 regaddr);
+extern int sdio_reset_comm(struct mmc_card *card);
+
+extern PBCMSDH_SDMMC_INSTANCE gInstance;
+
+uint sd_sdmode = SDIOH_MODE_SD4;	/* Use SD4 mode by default */
+uint sd_f2_blocksize = 512;		/* Default blocksize */
+
+uint sd_divisor = 2;			/* Default 48MHz/2 = 24MHz */
+
+uint sd_power = 1;		/* Default to SD Slot powered ON */
+uint sd_clock = 1;		/* Default to SD Clock turned ON */
+uint sd_hiok = FALSE;	/* Don't use hi-speed mode by default */
+uint sd_msglevel = 0x01;
+uint sd_use_dma = TRUE;
+DHD_PM_RESUME_WAIT_INIT(sdioh_request_byte_wait);
+DHD_PM_RESUME_WAIT_INIT(sdioh_request_word_wait);
+DHD_PM_RESUME_WAIT_INIT(sdioh_request_packet_wait);
+DHD_PM_RESUME_WAIT_INIT(sdioh_request_buffer_wait);
+
+#define DMA_ALIGN_MASK	0x03
+
+int sdioh_sdmmc_card_regread(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 *data);
+
+static int
+sdioh_sdmmc_card_enablefuncs(sdioh_info_t *sd)
+{
+	int err_ret;
+	uint32 fbraddr;
+	uint8 func;
+
+	sd_trace(("%s\n", __FUNCTION__));
+
+	/* Get the Card's common CIS address */
+	sd->com_cis_ptr = sdioh_sdmmc_get_cisaddr(sd, SDIOD_CCCR_CISPTR_0);
+	sd->func_cis_ptr[0] = sd->com_cis_ptr;
+	sd_info(("%s: Card's Common CIS Ptr = 0x%x\n", __FUNCTION__, sd->com_cis_ptr));
+
+	/* Get the Card's function CIS (for each function) */
+	for (fbraddr = SDIOD_FBR_STARTADDR, func = 1;
+	     func <= sd->num_funcs; func++, fbraddr += SDIOD_FBR_SIZE) {
+		sd->func_cis_ptr[func] = sdioh_sdmmc_get_cisaddr(sd, SDIOD_FBR_CISPTR_0 + fbraddr);
+		sd_info(("%s: Function %d CIS Ptr = 0x%x\n",
+		         __FUNCTION__, func, sd->func_cis_ptr[func]));
+	}
+
+	sd->func_cis_ptr[0] = sd->com_cis_ptr;
+	sd_info(("%s: Card's Common CIS Ptr = 0x%x\n", __FUNCTION__, sd->com_cis_ptr));
+
+	/* Enable Function 1 */
+	sdio_claim_host(gInstance->func[1]);
+	err_ret = sdio_enable_func(gInstance->func[1]);
+	sdio_release_host(gInstance->func[1]);
+	if (err_ret) {
+		sd_err(("bcmsdh_sdmmc: Failed to enable F1 Err: 0x%08x", err_ret));
+	}
+
+	return FALSE;
+}
+
+/*
+ *	Public entry points & extern's
+ */
+extern sdioh_info_t *
+sdioh_attach(osl_t *osh, void *bar0, uint irq)
+{
+	sdioh_info_t *sd;
+	int err_ret;
+
+	sd_trace(("%s\n", __FUNCTION__));
+
+	if (gInstance == NULL) {
+		sd_err(("%s: SDIO Device not present\n", __FUNCTION__));
+		return NULL;
+	}
+
+	if ((sd = (sdioh_info_t *)MALLOC(osh, sizeof(sdioh_info_t))) == NULL) {
+		sd_err(("sdioh_attach: out of memory, malloced %d bytes\n", MALLOCED(osh)));
+		return NULL;
+	}
+	bzero((char *)sd, sizeof(sdioh_info_t));
+	sd->osh = osh;
+	if (sdioh_sdmmc_osinit(sd) != 0) {
+		sd_err(("%s:sdioh_sdmmc_osinit() failed\n", __FUNCTION__));
+		MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+		return NULL;
+	}
+
+	sd->num_funcs = 2;
+	sd->sd_blockmode = TRUE;
+	sd->use_client_ints = TRUE;
+	sd->client_block_size[0] = 64;
+
+	gInstance->sd = sd;
+
+	/* Claim host controller */
+	sdio_claim_host(gInstance->func[1]);
+
+	sd->client_block_size[1] = 64;
+	err_ret = sdio_set_block_size(gInstance->func[1], 64);
+	if (err_ret) {
+		sd_err(("bcmsdh_sdmmc: Failed to set F1 blocksize\n"));
+	}
+
+	/* Release host controller F1 */
+	sdio_release_host(gInstance->func[1]);
+
+	if (gInstance->func[2]) {
+		/* Claim host controller F2 */
+		sdio_claim_host(gInstance->func[2]);
+
+		sd->client_block_size[2] = sd_f2_blocksize;
+		err_ret = sdio_set_block_size(gInstance->func[2], sd_f2_blocksize);
+		if (err_ret) {
+			sd_err(("bcmsdh_sdmmc: Failed to set F2 blocksize to %d\n",
+				sd_f2_blocksize));
+		}
+
+		/* Release host controller F2 */
+		sdio_release_host(gInstance->func[2]);
+	}
+
+	sdioh_sdmmc_card_enablefuncs(sd);
+
+	sd_trace(("%s: Done\n", __FUNCTION__));
+	return sd;
+}
+
+
+extern SDIOH_API_RC
+sdioh_detach(osl_t *osh, sdioh_info_t *sd)
+{
+	sd_trace(("%s\n", __FUNCTION__));
+
+	if (sd) {
+
+		/* Disable Function 2 */
+		sdio_claim_host(gInstance->func[2]);
+		sdio_disable_func(gInstance->func[2]);
+		sdio_release_host(gInstance->func[2]);
+
+		/* Disable Function 1 */
+		sdio_claim_host(gInstance->func[1]);
+		sdio_disable_func(gInstance->func[1]);
+		sdio_release_host(gInstance->func[1]);
+
+		/* deregister irq */
+		sdioh_sdmmc_osfree(sd);
+
+		MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+	}
+	return SDIOH_API_RC_SUCCESS;
+}
+
+#if defined(OOB_INTR_ONLY) && defined(HW_OOB)
+
+extern SDIOH_API_RC
+sdioh_enable_func_intr(void)
+{
+	uint8 reg;
+	int err;
+
+	if (gInstance->func[0]) {
+		sdio_claim_host(gInstance->func[0]);
+
+		reg = sdio_readb(gInstance->func[0], SDIOD_CCCR_INTEN, &err);
+		if (err) {
+			sd_err(("%s: error for read SDIO_CCCR_IENx : 0x%x\n", __FUNCTION__, err));
+			sdio_release_host(gInstance->func[0]);
+			return SDIOH_API_RC_FAIL;
+		}
+
+		/* Enable F1 and F2 interrupts, set master enable */
+		reg |= (INTR_CTL_FUNC1_EN | INTR_CTL_FUNC2_EN | INTR_CTL_MASTER_EN);
+
+		sdio_writeb(gInstance->func[0], reg, SDIOD_CCCR_INTEN, &err);
+		sdio_release_host(gInstance->func[0]);
+
+		if (err) {
+			sd_err(("%s: error for write SDIO_CCCR_IENx : 0x%x\n", __FUNCTION__, err));
+			return SDIOH_API_RC_FAIL;
+		}
+	}
+
+	return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_disable_func_intr(void)
+{
+	uint8 reg;
+	int err;
+
+	if (gInstance->func[0]) {
+		sdio_claim_host(gInstance->func[0]);
+		reg = sdio_readb(gInstance->func[0], SDIOD_CCCR_INTEN, &err);
+		if (err) {
+			sd_err(("%s: error for read SDIO_CCCR_IENx : 0x%x\n", __FUNCTION__, err));
+			sdio_release_host(gInstance->func[0]);
+			return SDIOH_API_RC_FAIL;
+		}
+
+		reg &= ~(INTR_CTL_FUNC1_EN | INTR_CTL_FUNC2_EN);
+		/* Disable master interrupt with the last function interrupt */
+		if (!(reg & 0xFE))
+			reg = 0;
+		sdio_writeb(gInstance->func[0], reg, SDIOD_CCCR_INTEN, &err);
+
+		sdio_release_host(gInstance->func[0]);
+		if (err) {
+			sd_err(("%s: error for write SDIO_CCCR_IENx : 0x%x\n", __FUNCTION__, err));
+			return SDIOH_API_RC_FAIL;
+		}
+	}
+	return SDIOH_API_RC_SUCCESS;
+}
+#endif /* defined(OOB_INTR_ONLY) && defined(HW_OOB) */
+
+/* Configure callback to client when we recieve client interrupt */
+extern SDIOH_API_RC
+sdioh_interrupt_register(sdioh_info_t *sd, sdioh_cb_fn_t fn, void *argh)
+{
+	sd_trace(("%s: Entering\n", __FUNCTION__));
+	if (fn == NULL) {
+		sd_err(("%s: interrupt handler is NULL, not registering\n", __FUNCTION__));
+		return SDIOH_API_RC_FAIL;
+	}
+#if !defined(OOB_INTR_ONLY)
+	sd->intr_handler = fn;
+	sd->intr_handler_arg = argh;
+	sd->intr_handler_valid = TRUE;
+
+	/* register and unmask irq */
+	if (gInstance->func[2]) {
+		sdio_claim_host(gInstance->func[2]);
+		sdio_claim_irq(gInstance->func[2], IRQHandlerF2);
+		sdio_release_host(gInstance->func[2]);
+	}
+
+	if (gInstance->func[1]) {
+		sdio_claim_host(gInstance->func[1]);
+		sdio_claim_irq(gInstance->func[1], IRQHandler);
+		sdio_release_host(gInstance->func[1]);
+	}
+#elif defined(HW_OOB)
+	sdioh_enable_func_intr();
+#endif /* defined(OOB_INTR_ONLY) */
+	return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_interrupt_deregister(sdioh_info_t *sd)
+{
+	sd_trace(("%s: Entering\n", __FUNCTION__));
+
+#if !defined(OOB_INTR_ONLY)
+	if (gInstance->func[1]) {
+		/* register and unmask irq */
+		sdio_claim_host(gInstance->func[1]);
+		sdio_release_irq(gInstance->func[1]);
+		sdio_release_host(gInstance->func[1]);
+	}
+
+	if (gInstance->func[2]) {
+		/* Claim host controller F2 */
+		sdio_claim_host(gInstance->func[2]);
+		sdio_release_irq(gInstance->func[2]);
+		/* Release host controller F2 */
+		sdio_release_host(gInstance->func[2]);
+	}
+
+	sd->intr_handler_valid = FALSE;
+	sd->intr_handler = NULL;
+	sd->intr_handler_arg = NULL;
+#elif defined(HW_OOB)
+	sdioh_disable_func_intr();
+#endif /*  !defined(OOB_INTR_ONLY) */
+	return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_interrupt_query(sdioh_info_t *sd, bool *onoff)
+{
+	sd_trace(("%s: Entering\n", __FUNCTION__));
+	*onoff = sd->client_intr_enabled;
+	return SDIOH_API_RC_SUCCESS;
+}
+
+#if defined(DHD_DEBUG)
+extern bool
+sdioh_interrupt_pending(sdioh_info_t *sd)
+{
+	return (0);
+}
+#endif
+
+uint
+sdioh_query_iofnum(sdioh_info_t *sd)
+{
+	return sd->num_funcs;
+}
+
+/* IOVar table */
+enum {
+	IOV_MSGLEVEL = 1,
+	IOV_BLOCKMODE,
+	IOV_BLOCKSIZE,
+	IOV_DMA,
+	IOV_USEINTS,
+	IOV_NUMINTS,
+	IOV_NUMLOCALINTS,
+	IOV_HOSTREG,
+	IOV_DEVREG,
+	IOV_DIVISOR,
+	IOV_SDMODE,
+	IOV_HISPEED,
+	IOV_HCIREGS,
+	IOV_POWER,
+	IOV_CLOCK,
+	IOV_RXCHAIN
+};
+
+const bcm_iovar_t sdioh_iovars[] = {
+	{"sd_msglevel", IOV_MSGLEVEL,	0,	IOVT_UINT32,	0 },
+	{"sd_blockmode", IOV_BLOCKMODE, 0,	IOVT_BOOL,	0 },
+	{"sd_blocksize", IOV_BLOCKSIZE, 0,	IOVT_UINT32,	0 }, /* ((fn << 16) | size) */
+	{"sd_dma",	IOV_DMA,	0,	IOVT_BOOL,	0 },
+	{"sd_ints", 	IOV_USEINTS,	0,	IOVT_BOOL,	0 },
+	{"sd_numints",	IOV_NUMINTS,	0,	IOVT_UINT32,	0 },
+	{"sd_numlocalints", IOV_NUMLOCALINTS, 0, IOVT_UINT32,	0 },
+	{"sd_hostreg",	IOV_HOSTREG,	0,	IOVT_BUFFER,	sizeof(sdreg_t) },
+	{"sd_devreg",	IOV_DEVREG, 	0,	IOVT_BUFFER,	sizeof(sdreg_t) },
+	{"sd_divisor",	IOV_DIVISOR,	0,	IOVT_UINT32,	0 },
+	{"sd_power",	IOV_POWER,	0,	IOVT_UINT32,	0 },
+	{"sd_clock",	IOV_CLOCK,	0,	IOVT_UINT32,	0 },
+	{"sd_mode", 	IOV_SDMODE, 	0,	IOVT_UINT32,	100},
+	{"sd_highspeed", IOV_HISPEED,	0,	IOVT_UINT32,	0 },
+	{"sd_rxchain",  IOV_RXCHAIN,    0, 	IOVT_BOOL,	0 },
+	{NULL, 0, 0, 0, 0 }
+};
+
+int
+sdioh_iovar_op(sdioh_info_t *si, const char *name,
+                           void *params, int plen, void *arg, int len, bool set)
+{
+	const bcm_iovar_t *vi = NULL;
+	int bcmerror = 0;
+	int val_size;
+	int32 int_val = 0;
+	bool bool_val;
+	uint32 actionid;
+
+	ASSERT(name);
+	ASSERT(len >= 0);
+
+	/* Get must have return space; Set does not take qualifiers */
+	ASSERT(set || (arg && len));
+	ASSERT(!set || (!params && !plen));
+
+	sd_trace(("%s: Enter (%s %s)\n", __FUNCTION__, (set ? "set" : "get"), name));
+
+	if ((vi = bcm_iovar_lookup(sdioh_iovars, name)) == NULL) {
+		bcmerror = BCME_UNSUPPORTED;
+		goto exit;
+	}
+
+	if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, set)) != 0)
+		goto exit;
+
+	/* Set up params so get and set can share the convenience variables */
+	if (params == NULL) {
+		params = arg;
+		plen = len;
+	}
+
+	if (vi->type == IOVT_VOID)
+		val_size = 0;
+	else if (vi->type == IOVT_BUFFER)
+		val_size = len;
+	else
+		val_size = sizeof(int);
+
+	if (plen >= (int)sizeof(int_val))
+		bcopy(params, &int_val, sizeof(int_val));
+
+	bool_val = (int_val != 0) ? TRUE : FALSE;
+
+	actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid);
+	switch (actionid) {
+	case IOV_GVAL(IOV_MSGLEVEL):
+		int_val = (int32)sd_msglevel;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_MSGLEVEL):
+		sd_msglevel = int_val;
+		break;
+
+	case IOV_GVAL(IOV_BLOCKMODE):
+		int_val = (int32)si->sd_blockmode;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_BLOCKMODE):
+		si->sd_blockmode = (bool)int_val;
+		/* Haven't figured out how to make non-block mode with DMA */
+		break;
+
+	case IOV_GVAL(IOV_BLOCKSIZE):
+		if ((uint32)int_val > si->num_funcs) {
+			bcmerror = BCME_BADARG;
+			break;
+		}
+		int_val = (int32)si->client_block_size[int_val];
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_BLOCKSIZE):
+	{
+		uint func = ((uint32)int_val >> 16);
+		uint blksize = (uint16)int_val;
+		uint maxsize;
+
+		if (func > si->num_funcs) {
+			bcmerror = BCME_BADARG;
+			break;
+		}
+
+		switch (func) {
+		case 0: maxsize = 32; break;
+		case 1: maxsize = BLOCK_SIZE_4318; break;
+		case 2: maxsize = BLOCK_SIZE_4328; break;
+		default: maxsize = 0;
+		}
+		if (blksize > maxsize) {
+			bcmerror = BCME_BADARG;
+			break;
+		}
+		if (!blksize) {
+			blksize = maxsize;
+		}
+
+		/* Now set it */
+		si->client_block_size[func] = blksize;
+
+		break;
+	}
+
+	case IOV_GVAL(IOV_RXCHAIN):
+		int_val = FALSE;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_GVAL(IOV_DMA):
+		int_val = (int32)si->sd_use_dma;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_DMA):
+		si->sd_use_dma = (bool)int_val;
+		break;
+
+	case IOV_GVAL(IOV_USEINTS):
+		int_val = (int32)si->use_client_ints;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_USEINTS):
+		si->use_client_ints = (bool)int_val;
+		if (si->use_client_ints)
+			si->intmask |= CLIENT_INTR;
+		else
+			si->intmask &= ~CLIENT_INTR;
+
+		break;
+
+	case IOV_GVAL(IOV_DIVISOR):
+		int_val = (uint32)sd_divisor;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_DIVISOR):
+		sd_divisor = int_val;
+		break;
+
+	case IOV_GVAL(IOV_POWER):
+		int_val = (uint32)sd_power;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_POWER):
+		sd_power = int_val;
+		break;
+
+	case IOV_GVAL(IOV_CLOCK):
+		int_val = (uint32)sd_clock;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_CLOCK):
+		sd_clock = int_val;
+		break;
+
+	case IOV_GVAL(IOV_SDMODE):
+		int_val = (uint32)sd_sdmode;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_SDMODE):
+		sd_sdmode = int_val;
+		break;
+
+	case IOV_GVAL(IOV_HISPEED):
+		int_val = (uint32)sd_hiok;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_HISPEED):
+		sd_hiok = int_val;
+		break;
+
+	case IOV_GVAL(IOV_NUMINTS):
+		int_val = (int32)si->intrcount;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_GVAL(IOV_NUMLOCALINTS):
+		int_val = (int32)0;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_GVAL(IOV_HOSTREG):
+	{
+		sdreg_t *sd_ptr = (sdreg_t *)params;
+
+		if (sd_ptr->offset < SD_SysAddr || sd_ptr->offset > SD_MaxCurCap) {
+			sd_err(("%s: bad offset 0x%x\n", __FUNCTION__, sd_ptr->offset));
+			bcmerror = BCME_BADARG;
+			break;
+		}
+
+		sd_trace(("%s: rreg%d at offset %d\n", __FUNCTION__,
+		                  (sd_ptr->offset & 1) ? 8 : ((sd_ptr->offset & 2) ? 16 : 32),
+		                  sd_ptr->offset));
+		if (sd_ptr->offset & 1)
+			int_val = 8; /* sdioh_sdmmc_rreg8(si, sd_ptr->offset); */
+		else if (sd_ptr->offset & 2)
+			int_val = 16; /* sdioh_sdmmc_rreg16(si, sd_ptr->offset); */
+		else
+			int_val = 32; /* sdioh_sdmmc_rreg(si, sd_ptr->offset); */
+
+		bcopy(&int_val, arg, sizeof(int_val));
+		break;
+	}
+
+	case IOV_SVAL(IOV_HOSTREG):
+	{
+		sdreg_t *sd_ptr = (sdreg_t *)params;
+
+		if (sd_ptr->offset < SD_SysAddr || sd_ptr->offset > SD_MaxCurCap) {
+			sd_err(("%s: bad offset 0x%x\n", __FUNCTION__, sd_ptr->offset));
+			bcmerror = BCME_BADARG;
+			break;
+		}
+
+		sd_trace(("%s: wreg%d value 0x%08x at offset %d\n", __FUNCTION__, sd_ptr->value,
+		                  (sd_ptr->offset & 1) ? 8 : ((sd_ptr->offset & 2) ? 16 : 32),
+		                  sd_ptr->offset));
+		break;
+	}
+
+	case IOV_GVAL(IOV_DEVREG):
+	{
+		sdreg_t *sd_ptr = (sdreg_t *)params;
+		uint8 data = 0;
+
+		if (sdioh_cfg_read(si, sd_ptr->func, sd_ptr->offset, &data)) {
+			bcmerror = BCME_SDIO_ERROR;
+			break;
+		}
+
+		int_val = (int)data;
+		bcopy(&int_val, arg, sizeof(int_val));
+		break;
+	}
+
+	case IOV_SVAL(IOV_DEVREG):
+	{
+		sdreg_t *sd_ptr = (sdreg_t *)params;
+		uint8 data = (uint8)sd_ptr->value;
+
+		if (sdioh_cfg_write(si, sd_ptr->func, sd_ptr->offset, &data)) {
+			bcmerror = BCME_SDIO_ERROR;
+			break;
+		}
+		break;
+	}
+
+	default:
+		bcmerror = BCME_UNSUPPORTED;
+		break;
+	}
+exit:
+
+	return bcmerror;
+}
+
+#if defined(OOB_INTR_ONLY) && defined(HW_OOB)
+
+SDIOH_API_RC
+sdioh_enable_hw_oob_intr(sdioh_info_t *sd, bool enable)
+{
+	SDIOH_API_RC status;
+	uint8 data;
+
+	if (enable)
+		data = 3;	/* enable hw oob interrupt */
+	else
+		data = 4;	/* disable hw oob interrupt */
+	data |= 4;		/* Active HIGH */
+
+	status = sdioh_request_byte(sd, SDIOH_WRITE, 0, 0xf2, &data);
+	return status;
+}
+#endif /* defined(OOB_INTR_ONLY) && defined(HW_OOB) */
+
+extern SDIOH_API_RC
+sdioh_cfg_read(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data)
+{
+	SDIOH_API_RC status;
+	/* No lock needed since sdioh_request_byte does locking */
+	status = sdioh_request_byte(sd, SDIOH_READ, fnc_num, addr, data);
+	return status;
+}
+
+extern SDIOH_API_RC
+sdioh_cfg_write(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data)
+{
+	/* No lock needed since sdioh_request_byte does locking */
+	SDIOH_API_RC status;
+	status = sdioh_request_byte(sd, SDIOH_WRITE, fnc_num, addr, data);
+	return status;
+}
+
+static int
+sdioh_sdmmc_get_cisaddr(sdioh_info_t *sd, uint32 regaddr)
+{
+	/* read 24 bits and return valid 17 bit addr */
+	int i;
+	uint32 scratch, regdata;
+	uint8 *ptr = (uint8 *)&scratch;
+	for (i = 0; i < 3; i++) {
+		if ((sdioh_sdmmc_card_regread (sd, 0, regaddr, 1, &regdata)) != SUCCESS)
+			sd_err(("%s: Can't read!\n", __FUNCTION__));
+
+		*ptr++ = (uint8) regdata;
+		regaddr++;
+	}
+
+	/* Only the lower 17-bits are valid */
+	scratch = ltoh32(scratch);
+	scratch &= 0x0001FFFF;
+	return (scratch);
+}
+
+extern SDIOH_API_RC
+sdioh_cis_read(sdioh_info_t *sd, uint func, uint8 *cisd, uint32 length)
+{
+	uint32 count;
+	int offset;
+	uint32 foo;
+	uint8 *cis = cisd;
+
+	sd_trace(("%s: Func = %d\n", __FUNCTION__, func));
+
+	if (!sd->func_cis_ptr[func]) {
+		bzero(cis, length);
+		sd_err(("%s: no func_cis_ptr[%d]\n", __FUNCTION__, func));
+		return SDIOH_API_RC_FAIL;
+	}
+
+	sd_err(("%s: func_cis_ptr[%d]=0x%04x\n", __FUNCTION__, func, sd->func_cis_ptr[func]));
+
+	for (count = 0; count < length; count++) {
+		offset =  sd->func_cis_ptr[func] + count;
+		if (sdioh_sdmmc_card_regread (sd, 0, offset, 1, &foo) < 0) {
+			sd_err(("%s: regread failed: Can't read CIS\n", __FUNCTION__));
+			return SDIOH_API_RC_FAIL;
+		}
+
+		*cis = (uint8)(foo & 0xff);
+		cis++;
+	}
+
+	return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_request_byte(sdioh_info_t *sd, uint rw, uint func, uint regaddr, uint8 *byte)
+{
+	int err_ret;
+
+	sd_info(("%s: rw=%d, func=%d, addr=0x%05x\n", __FUNCTION__, rw, func, regaddr));
+
+	DHD_PM_RESUME_WAIT(sdioh_request_byte_wait);
+	DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL);
+	if(rw) { /* CMD52 Write */
+		if (func == 0) {
+			/* Can only directly write to some F0 registers.  Handle F2 enable
+			 * as a special case.
+			 */
+			if (regaddr == SDIOD_CCCR_IOEN) {
+				if (gInstance->func[2]) {
+					sdio_claim_host(gInstance->func[2]);
+					if (*byte & SDIO_FUNC_ENABLE_2) {
+						/* Enable Function 2 */
+						err_ret = sdio_enable_func(gInstance->func[2]);
+						if (err_ret) {
+							sd_err(("bcmsdh_sdmmc: enable F2 failed:%d",
+								err_ret));
+						}
+					} else {
+						/* Disable Function 2 */
+						err_ret = sdio_disable_func(gInstance->func[2]);
+						if (err_ret) {
+							sd_err(("bcmsdh_sdmmc: Disab F2 failed:%d",
+								err_ret));
+						}
+					}
+					sdio_release_host(gInstance->func[2]);
+				}
+			}
+#if defined(MMC_SDIO_ABORT)
+			/* to allow abort command through F1 */
+			else if (regaddr == SDIOD_CCCR_IOABORT) {
+				sdio_claim_host(gInstance->func[func]);
+				/*
+				* this sdio_f0_writeb() can be replaced with another api
+				* depending upon MMC driver change.
+				* As of this time, this is temporaray one
+				*/
+				sdio_writeb(gInstance->func[func], *byte, regaddr, &err_ret);
+				sdio_release_host(gInstance->func[func]);
+			}
+#endif /* MMC_SDIO_ABORT */
+			else if (regaddr < 0xF0) {
+				sd_err(("bcmsdh_sdmmc: F0 Wr:0x%02x: write disallowed\n", regaddr));
+			} else {
+				/* Claim host controller, perform F0 write, and release */
+				sdio_claim_host(gInstance->func[func]);
+				sdio_f0_writeb(gInstance->func[func], *byte, regaddr, &err_ret);
+				sdio_release_host(gInstance->func[func]);
+			}
+		} else {
+			/* Claim host controller, perform Fn write, and release */
+			sdio_claim_host(gInstance->func[func]);
+			sdio_writeb(gInstance->func[func], *byte, regaddr, &err_ret);
+			sdio_release_host(gInstance->func[func]);
+		}
+	} else { /* CMD52 Read */
+		/* Claim host controller, perform Fn read, and release */
+		sdio_claim_host(gInstance->func[func]);
+
+		if (func == 0) {
+			*byte = sdio_f0_readb(gInstance->func[func], regaddr, &err_ret);
+		} else {
+			*byte = sdio_readb(gInstance->func[func], regaddr, &err_ret);
+		}
+
+		sdio_release_host(gInstance->func[func]);
+	}
+
+	if (err_ret) {
+		sd_err(("bcmsdh_sdmmc: Failed to %s byte F%d:@0x%05x=%02x, Err: %d\n",
+		                        rw ? "Write" : "Read", func, regaddr, *byte, err_ret));
+	}
+
+	return ((err_ret == 0) ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL);
+}
+
+extern SDIOH_API_RC
+sdioh_request_word(sdioh_info_t *sd, uint cmd_type, uint rw, uint func, uint addr,
+                                   uint32 *word, uint nbytes)
+{
+	int err_ret = SDIOH_API_RC_FAIL;
+
+	if (func == 0) {
+		sd_err(("%s: Only CMD52 allowed to F0.\n", __FUNCTION__));
+		return SDIOH_API_RC_FAIL;
+	}
+
+	sd_info(("%s: cmd_type=%d, rw=%d, func=%d, addr=0x%05x, nbytes=%d\n",
+	         __FUNCTION__, cmd_type, rw, func, addr, nbytes));
+
+	DHD_PM_RESUME_WAIT(sdioh_request_word_wait);
+	DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL);
+	/* Claim host controller */
+	sdio_claim_host(gInstance->func[func]);
+
+	if(rw) { /* CMD52 Write */
+		if (nbytes == 4) {
+			sdio_writel(gInstance->func[func], *word, addr, &err_ret);
+		} else if (nbytes == 2) {
+			sdio_writew(gInstance->func[func], (*word & 0xFFFF), addr, &err_ret);
+		} else {
+			sd_err(("%s: Invalid nbytes: %d\n", __FUNCTION__, nbytes));
+		}
+	} else { /* CMD52 Read */
+		if (nbytes == 4) {
+			*word = sdio_readl(gInstance->func[func], addr, &err_ret);
+		} else if (nbytes == 2) {
+			*word = sdio_readw(gInstance->func[func], addr, &err_ret) & 0xFFFF;
+		} else {
+			sd_err(("%s: Invalid nbytes: %d\n", __FUNCTION__, nbytes));
+		}
+	}
+
+	/* Release host controller */
+	sdio_release_host(gInstance->func[func]);
+
+	if (err_ret) {
+		sd_err(("bcmsdh_sdmmc: Failed to %s word, Err: 0x%08x",
+		                        rw ? "Write" : "Read", err_ret));
+	}
+
+	return ((err_ret == 0) ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL);
+}
+
+static SDIOH_API_RC
+sdioh_request_packet(sdioh_info_t *sd, uint fix_inc, uint write, uint func,
+                     uint addr, void *pkt)
+{
+	bool fifo = (fix_inc == SDIOH_DATA_FIX);
+	uint32	SGCount = 0;
+	int err_ret = 0;
+
+	void *pnext;
+
+	sd_trace(("%s: Enter\n", __FUNCTION__));
+
+	ASSERT(pkt);
+	DHD_PM_RESUME_WAIT(sdioh_request_packet_wait);
+	DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL);
+
+	/* Claim host controller */
+	sdio_claim_host(gInstance->func[func]);
+	for (pnext = pkt; pnext; pnext = PKTNEXT(sd->osh, pnext)) {
+		uint pkt_len = PKTLEN(sd->osh, pnext);
+		pkt_len += 3;
+		pkt_len &= 0xFFFFFFFC;
+
+#ifdef CONFIG_MMC_MSM7X00A
+		if ((pkt_len % 64) == 32) {
+			sd_trace(("%s: Rounding up TX packet +=32\n", __FUNCTION__));
+			pkt_len += 32;
+		}
+#endif /* CONFIG_MMC_MSM7X00A */
+		/* Make sure the packet is aligned properly. If it isn't, then this
+		 * is the fault of sdioh_request_buffer() which is supposed to give
+		 * us something we can work with.
+		 */
+		ASSERT(((uint32)(PKTDATA(sd->osh, pkt)) & DMA_ALIGN_MASK) == 0);
+
+		if ((write) && (!fifo)) {
+			err_ret = sdio_memcpy_toio(gInstance->func[func], addr,
+				((uint8*)PKTDATA(sd->osh, pnext)),
+				pkt_len);
+		} else if (write) {
+			err_ret = sdio_memcpy_toio(gInstance->func[func], addr,
+				((uint8*)PKTDATA(sd->osh, pnext)),
+				pkt_len);
+		} else if (fifo) {
+			err_ret = sdio_readsb(gInstance->func[func],
+				((uint8*)PKTDATA(sd->osh, pnext)),
+				addr,
+				pkt_len);
+		} else {
+			err_ret = sdio_memcpy_fromio(gInstance->func[func],
+				((uint8*)PKTDATA(sd->osh, pnext)),
+				addr,
+				pkt_len);
+		}
+
+		if (err_ret) {
+			sd_err(("%s: %s FAILED %p[%d], addr=0x%05x, pkt_len=%d, ERR=0x%08x\n",
+				__FUNCTION__,
+				(write) ? "TX" : "RX",
+				pnext, SGCount, addr, pkt_len, err_ret));
+		} else {
+			sd_trace(("%s: %s xfr'd %p[%d], addr=0x%05x, len=%d\n",
+				__FUNCTION__,
+				(write) ? "TX" : "RX",
+				pnext, SGCount, addr, pkt_len));
+		}
+
+		if (!fifo) {
+			addr += pkt_len;
+		}
+		SGCount ++;
+
+	}
+
+	/* Release host controller */
+	sdio_release_host(gInstance->func[func]);
+
+	sd_trace(("%s: Exit\n", __FUNCTION__));
+	return ((err_ret == 0) ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL);
+}
+
+
+/*
+ * This function takes a buffer or packet, and fixes everything up so that in the
+ * end, a DMA-able packet is created.
+ *
+ * A buffer does not have an associated packet pointer, and may or may not be aligned.
+ * A packet may consist of a single packet, or a packet chain.  If it is a packet chain,
+ * then all the packets in the chain must be properly aligned.  If the packet data is not
+ * aligned, then there may only be one packet, and in this case, it is copied to a new
+ * aligned packet.
+ *
+ */
+extern SDIOH_API_RC
+sdioh_request_buffer(sdioh_info_t *sd, uint pio_dma, uint fix_inc, uint write, uint func,
+                     uint addr, uint reg_width, uint buflen_u, uint8 *buffer, void *pkt)
+{
+	SDIOH_API_RC Status;
+	void *mypkt = NULL;
+
+	sd_trace(("%s: Enter\n", __FUNCTION__));
+
+	DHD_PM_RESUME_WAIT(sdioh_request_buffer_wait);
+	DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL);
+	/* Case 1: we don't have a packet. */
+	if (pkt == NULL) {
+		sd_data(("%s: Creating new %s Packet, len=%d\n",
+		         __FUNCTION__, write ? "TX" : "RX", buflen_u));
+#ifdef DHD_USE_STATIC_BUF
+		if (!(mypkt = PKTGET_STATIC(sd->osh, buflen_u, write ? TRUE : FALSE))) {
+#else
+		if (!(mypkt = PKTGET(sd->osh, buflen_u, write ? TRUE : FALSE))) {
+#endif /* DHD_USE_STATIC_BUF */
+			sd_err(("%s: PKTGET failed: len %d\n",
+			           __FUNCTION__, buflen_u));
+			return SDIOH_API_RC_FAIL;
+		}
+
+		/* For a write, copy the buffer data into the packet. */
+		if (write) {
+			bcopy(buffer, PKTDATA(sd->osh, mypkt), buflen_u);
+		}
+
+		Status = sdioh_request_packet(sd, fix_inc, write, func, addr, mypkt);
+
+		/* For a read, copy the packet data back to the buffer. */
+		if (!write) {
+			bcopy(PKTDATA(sd->osh, mypkt), buffer, buflen_u);
+		}
+#ifdef DHD_USE_STATIC_BUF
+		PKTFREE_STATIC(sd->osh, mypkt, write ? TRUE : FALSE);
+#else
+		PKTFREE(sd->osh, mypkt, write ? TRUE : FALSE);
+#endif /* DHD_USE_STATIC_BUF */
+	} else if (((uint32)(PKTDATA(sd->osh, pkt)) & DMA_ALIGN_MASK) != 0) {
+		/* Case 2: We have a packet, but it is unaligned. */
+
+		/* In this case, we cannot have a chain. */
+		ASSERT(PKTNEXT(sd->osh, pkt) == NULL);
+
+		sd_data(("%s: Creating aligned %s Packet, len=%d\n",
+		         __FUNCTION__, write ? "TX" : "RX", PKTLEN(sd->osh, pkt)));
+#ifdef DHD_USE_STATIC_BUF
+		if (!(mypkt = PKTGET_STATIC(sd->osh, PKTLEN(sd->osh, pkt), write ? TRUE : FALSE))) {
+#else
+		if (!(mypkt = PKTGET(sd->osh, PKTLEN(sd->osh, pkt), write ? TRUE : FALSE))) {
+#endif /* DHD_USE_STATIC_BUF */
+			sd_err(("%s: PKTGET failed: len %d\n",
+			           __FUNCTION__, PKTLEN(sd->osh, pkt)));
+			return SDIOH_API_RC_FAIL;
+		}
+
+		/* For a write, copy the buffer data into the packet. */
+		if (write) {
+			bcopy(PKTDATA(sd->osh, pkt),
+			      PKTDATA(sd->osh, mypkt),
+			      PKTLEN(sd->osh, pkt));
+		}
+
+		Status = sdioh_request_packet(sd, fix_inc, write, func, addr, mypkt);
+
+		/* For a read, copy the packet data back to the buffer. */
+		if (!write) {
+			bcopy(PKTDATA(sd->osh, mypkt),
+			      PKTDATA(sd->osh, pkt),
+			      PKTLEN(sd->osh, mypkt));
+		}
+#ifdef DHD_USE_STATIC_BUF
+		PKTFREE_STATIC(sd->osh, mypkt, write ? TRUE : FALSE);
+#else
+		PKTFREE(sd->osh, mypkt, write ? TRUE : FALSE);
+#endif /* DHD_USE_STATIC_BUF */
+	} else { /* case 3: We have a packet and it is aligned. */
+		sd_data(("%s: Aligned %s Packet, direct DMA\n",
+		         __FUNCTION__, write ? "Tx" : "Rx"));
+		Status = sdioh_request_packet(sd, fix_inc, write, func, addr, pkt);
+	}
+
+	return (Status);
+}
+
+/* this function performs "abort" for both of host & device */
+extern int
+sdioh_abort(sdioh_info_t *sd, uint func)
+{
+#if defined(MMC_SDIO_ABORT)
+	char t_func = (char) func;
+#endif /* defined(MMC_SDIO_ABORT) */
+	sd_trace(("%s: Enter\n", __FUNCTION__));
+
+#if defined(MMC_SDIO_ABORT)
+	/* issue abort cmd52 command through F1 */
+	sdioh_request_byte(sd, SD_IO_OP_WRITE, SDIO_FUNC_0, SDIOD_CCCR_IOABORT, &t_func);
+#endif /* defined(MMC_SDIO_ABORT) */
+
+	sd_trace(("%s: Exit\n", __FUNCTION__));
+	return SDIOH_API_RC_SUCCESS;
+}
+
+/* Reset and re-initialize the device */
+int sdioh_sdio_reset(sdioh_info_t *si)
+{
+	sd_trace(("%s: Enter\n", __FUNCTION__));
+	sd_trace(("%s: Exit\n", __FUNCTION__));
+	return SDIOH_API_RC_SUCCESS;
+}
+
+/* Disable device interrupt */
+void
+sdioh_sdmmc_devintr_off(sdioh_info_t *sd)
+{
+	sd_trace(("%s: %d\n", __FUNCTION__, sd->use_client_ints));
+	sd->intmask &= ~CLIENT_INTR;
+}
+
+/* Enable device interrupt */
+void
+sdioh_sdmmc_devintr_on(sdioh_info_t *sd)
+{
+	sd_trace(("%s: %d\n", __FUNCTION__, sd->use_client_ints));
+	sd->intmask |= CLIENT_INTR;
+}
+
+/* Read client card reg */
+int
+sdioh_sdmmc_card_regread(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 *data)
+{
+
+	if ((func == 0) || (regsize == 1)) {
+		uint8 temp = 0;
+
+		sdioh_request_byte(sd, SDIOH_READ, func, regaddr, &temp);
+		*data = temp;
+		*data &= 0xff;
+		sd_data(("%s: byte read data=0x%02x\n",
+		         __FUNCTION__, *data));
+	} else {
+		sdioh_request_word(sd, 0, SDIOH_READ, func, regaddr, data, regsize);
+		if (regsize == 2)
+			*data &= 0xffff;
+
+		sd_data(("%s: word read data=0x%08x\n",
+		         __FUNCTION__, *data));
+	}
+
+	return SUCCESS;
+}
+
+#if !defined(OOB_INTR_ONLY)
+/* bcmsdh_sdmmc interrupt handler */
+static void IRQHandler(struct sdio_func *func)
+{
+	sdioh_info_t *sd;
+
+	sd_trace(("bcmsdh_sdmmc: ***IRQHandler\n"));
+	sd = gInstance->sd;
+
+	ASSERT(sd != NULL);
+	sdio_release_host(gInstance->func[0]);
+
+	if (sd->use_client_ints) {
+		sd->intrcount++;
+		ASSERT(sd->intr_handler);
+		ASSERT(sd->intr_handler_arg);
+		(sd->intr_handler)(sd->intr_handler_arg);
+	} else {
+		sd_err(("bcmsdh_sdmmc: ***IRQHandler\n"));
+
+		sd_err(("%s: Not ready for intr: enabled %d, handler %p\n",
+		        __FUNCTION__, sd->client_intr_enabled, sd->intr_handler));
+	}
+
+	sdio_claim_host(gInstance->func[0]);
+}
+
+/* bcmsdh_sdmmc interrupt handler for F2 (dummy handler) */
+static void IRQHandlerF2(struct sdio_func *func)
+{
+	sdioh_info_t *sd;
+
+	sd_trace(("bcmsdh_sdmmc: ***IRQHandlerF2\n"));
+
+	sd = gInstance->sd;
+
+	ASSERT(sd != NULL);
+}
+#endif /* !defined(OOB_INTR_ONLY) */
+
+#ifdef NOTUSED
+/* Write client card reg */
+static int
+sdioh_sdmmc_card_regwrite(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 data)
+{
+
+	if ((func == 0) || (regsize == 1)) {
+		uint8 temp;
+
+		temp = data & 0xff;
+		sdioh_request_byte(sd, SDIOH_READ, func, regaddr, &temp);
+		sd_data(("%s: byte write data=0x%02x\n",
+		         __FUNCTION__, data));
+	} else {
+		if (regsize == 2)
+			data &= 0xffff;
+
+		sdioh_request_word(sd, 0, SDIOH_READ, func, regaddr, &data, regsize);
+
+		sd_data(("%s: word write data=0x%08x\n",
+		         __FUNCTION__, data));
+	}
+
+	return SUCCESS;
+}
+#endif /* NOTUSED */
+
+int
+sdioh_start(sdioh_info_t *si, int stage)
+{
+	int ret;
+	sdioh_info_t *sd = gInstance->sd;
+
+	/* Need to do this stages as we can't enable the interrupt till
+		downloading of the firmware is complete, other wise polling
+		sdio access will come in way
+	*/
+	if (gInstance->func[0]) {
+			if (stage == 0) {
+		/* Since the power to the chip is killed, we will have
+			re enumerate the device again. Set the block size
+			and enable the fucntion 1 for in preparation for
+			downloading the code
+		*/
+		/* sdio_reset_comm() - has been fixed in latest kernel/msm.git for Linux
+		   2.6.27. The implementation prior to that is buggy, and needs broadcom's
+		   patch for it
+		*/
+		if ((ret = sdio_reset_comm(gInstance->func[0]->card)))
+			sd_err(("%s Failed, error = %d\n", __FUNCTION__, ret));
+		else {
+			sd->num_funcs = 2;
+			sd->sd_blockmode = TRUE;
+			sd->use_client_ints = TRUE;
+			sd->client_block_size[0] = 64;
+
+			/* Claim host controller */
+			sdio_claim_host(gInstance->func[1]);
+
+			sd->client_block_size[1] = 64;
+			if (sdio_set_block_size(gInstance->func[1], 64)) {
+				sd_err(("bcmsdh_sdmmc: Failed to set F1 blocksize\n"));
+			}
+
+			/* Release host controller F1 */
+			sdio_release_host(gInstance->func[1]);
+
+			if (gInstance->func[2]) {
+				/* Claim host controller F2 */
+				sdio_claim_host(gInstance->func[2]);
+
+				sd->client_block_size[2] = sd_f2_blocksize;
+				if (sdio_set_block_size(gInstance->func[2],
+					sd_f2_blocksize)) {
+					sd_err(("bcmsdh_sdmmc: Failed to set F2 "
+						"blocksize to %d\n", sd_f2_blocksize));
+				}
+
+				/* Release host controller F2 */
+				sdio_release_host(gInstance->func[2]);
+			}
+
+			sdioh_sdmmc_card_enablefuncs(sd);
+			}
+		} else {
+#if !defined(OOB_INTR_ONLY)
+			sdio_claim_host(gInstance->func[0]);
+			sdio_claim_irq(gInstance->func[2], IRQHandlerF2);
+			sdio_claim_irq(gInstance->func[1], IRQHandler);
+			sdio_release_host(gInstance->func[0]);
+#else /* defined(OOB_INTR_ONLY) */
+#if defined(HW_OOB)
+			sdioh_enable_func_intr();
+#endif
+			bcmsdh_oob_intr_set(TRUE);
+#endif /* !defined(OOB_INTR_ONLY) */
+		}
+	}
+	else
+		sd_err(("%s Failed\n", __FUNCTION__));
+
+	return (0);
+}
+
+int
+sdioh_stop(sdioh_info_t *si)
+{
+	/* MSM7201A Android sdio stack has bug with interrupt
+		So internaly within SDIO stack they are polling
+		which cause issue when device is turned off. So
+		unregister interrupt with SDIO stack to stop the
+		polling
+	*/
+	if (gInstance->func[0]) {
+#if !defined(OOB_INTR_ONLY)
+		sdio_claim_host(gInstance->func[0]);
+		sdio_release_irq(gInstance->func[1]);
+		sdio_release_irq(gInstance->func[2]);
+		sdio_release_host(gInstance->func[0]);
+#else /* defined(OOB_INTR_ONLY) */
+#if defined(HW_OOB)
+		sdioh_disable_func_intr();
+#endif
+		bcmsdh_oob_intr_set(FALSE);
+#endif /* !defined(OOB_INTR_ONLY) */
+	}
+	else
+		sd_err(("%s Failed\n", __FUNCTION__));
+	return (0);
+}
diff --git a/drivers/net/wireless/bcm4329/bcmsdh_sdmmc_linux.c b/drivers/net/wireless/bcm4329/bcmsdh_sdmmc_linux.c
new file mode 100644
index 0000000..5a1a46c
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/bcmsdh_sdmmc_linux.c
@@ -0,0 +1,269 @@
+/*
+ * BCMSDH Function Driver for the native SDIO/MMC driver in the Linux Kernel
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmsdh_sdmmc_linux.c,v 1.1.2.5.6.17 2010/08/13 00:36:19 Exp $
+ */
+
+#include <typedefs.h>
+#include <bcmutils.h>
+#include <sdio.h>	/* SDIO Specs */
+#include <bcmsdbus.h>	/* bcmsdh to/from specific controller APIs */
+#include <sdiovar.h>	/* to get msglevel bit values */
+
+#include <linux/sched.h>	/* request_irq() */
+
+#include <linux/mmc/core.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+
+#if !defined(SDIO_VENDOR_ID_BROADCOM)
+#define SDIO_VENDOR_ID_BROADCOM		0x02d0
+#endif /* !defined(SDIO_VENDOR_ID_BROADCOM) */
+
+#define SDIO_DEVICE_ID_BROADCOM_DEFAULT	0x0000
+
+#if !defined(SDIO_DEVICE_ID_BROADCOM_4325_SDGWB)
+#define SDIO_DEVICE_ID_BROADCOM_4325_SDGWB	0x0492	/* BCM94325SDGWB */
+#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) */
+#if !defined(SDIO_DEVICE_ID_BROADCOM_4325)
+#define SDIO_DEVICE_ID_BROADCOM_4325	0x0493
+#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4325) */
+#if !defined(SDIO_DEVICE_ID_BROADCOM_4329)
+#define SDIO_DEVICE_ID_BROADCOM_4329	0x4329
+#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4329) */
+#if !defined(SDIO_DEVICE_ID_BROADCOM_4319)
+#define SDIO_DEVICE_ID_BROADCOM_4319	0x4319
+#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4329) */
+
+#include <bcmsdh_sdmmc.h>
+
+#include <dhd_dbg.h>
+
+extern void sdioh_sdmmc_devintr_off(sdioh_info_t *sd);
+extern void sdioh_sdmmc_devintr_on(sdioh_info_t *sd);
+
+int sdio_function_init(void);
+void sdio_function_cleanup(void);
+
+#define DESCRIPTION "bcmsdh_sdmmc Driver"
+#define AUTHOR "Broadcom Corporation"
+
+/* module param defaults */
+static int clockoverride = 0;
+
+module_param(clockoverride, int, 0644);
+MODULE_PARM_DESC(clockoverride, "SDIO card clock override");
+
+PBCMSDH_SDMMC_INSTANCE gInstance;
+
+/* Maximum number of bcmsdh_sdmmc devices supported by driver */
+#define BCMSDH_SDMMC_MAX_DEVICES 1
+
+extern int bcmsdh_probe(struct device *dev);
+extern int bcmsdh_remove(struct device *dev);
+
+static int bcmsdh_sdmmc_probe(struct sdio_func *func,
+                              const struct sdio_device_id *id)
+{
+	int ret = 0;
+	static struct sdio_func sdio_func_0;
+	sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__));
+	sd_trace(("sdio_bcmsdh: func->class=%x\n", func->class));
+	sd_trace(("sdio_vendor: 0x%04x\n", func->vendor));
+	sd_trace(("sdio_device: 0x%04x\n", func->device));
+	sd_trace(("Function#: 0x%04x\n", func->num));
+
+	if (func->num == 1) {
+		sdio_func_0.num = 0;
+		sdio_func_0.card = func->card;
+		gInstance->func[0] = &sdio_func_0;
+		if(func->device == 0x4) { /* 4318 */
+			gInstance->func[2] = NULL;
+			sd_trace(("NIC found, calling bcmsdh_probe...\n"));
+			ret = bcmsdh_probe(&func->dev);
+		}
+	}
+
+	gInstance->func[func->num] = func;
+
+	if (func->num == 2) {
+		sd_trace(("F2 found, calling bcmsdh_probe...\n"));
+		ret = bcmsdh_probe(&func->dev);
+	}
+
+	return ret;
+}
+
+static void bcmsdh_sdmmc_remove(struct sdio_func *func)
+{
+	sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__));
+	sd_info(("sdio_bcmsdh: func->class=%x\n", func->class));
+	sd_info(("sdio_vendor: 0x%04x\n", func->vendor));
+	sd_info(("sdio_device: 0x%04x\n", func->device));
+	sd_info(("Function#: 0x%04x\n", func->num));
+
+	if (func->num == 2) {
+		sd_trace(("F2 found, calling bcmsdh_remove...\n"));
+		bcmsdh_remove(&func->dev);
+	}
+}
+
+/* devices we support, null terminated */
+static const struct sdio_device_id bcmsdh_sdmmc_ids[] = {
+	{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_DEFAULT) },
+	{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) },
+	{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325) },
+	{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329) },
+	{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4319) },
+	{ /* end: all zeroes */				},
+};
+
+MODULE_DEVICE_TABLE(sdio, bcmsdh_sdmmc_ids);
+
+static struct sdio_driver bcmsdh_sdmmc_driver = {
+	.probe		= bcmsdh_sdmmc_probe,
+	.remove		= bcmsdh_sdmmc_remove,
+	.name		= "bcmsdh_sdmmc",
+	.id_table	= bcmsdh_sdmmc_ids,
+	};
+
+struct sdos_info {
+	sdioh_info_t *sd;
+	spinlock_t lock;
+};
+
+
+int
+sdioh_sdmmc_osinit(sdioh_info_t *sd)
+{
+	struct sdos_info *sdos;
+
+	sdos = (struct sdos_info*)MALLOC(sd->osh, sizeof(struct sdos_info));
+	sd->sdos_info = (void*)sdos;
+	if (sdos == NULL)
+		return BCME_NOMEM;
+
+	sdos->sd = sd;
+	spin_lock_init(&sdos->lock);
+	return BCME_OK;
+}
+
+void
+sdioh_sdmmc_osfree(sdioh_info_t *sd)
+{
+	struct sdos_info *sdos;
+	ASSERT(sd && sd->sdos_info);
+
+	sdos = (struct sdos_info *)sd->sdos_info;
+	MFREE(sd->osh, sdos, sizeof(struct sdos_info));
+}
+
+/* Interrupt enable/disable */
+SDIOH_API_RC
+sdioh_interrupt_set(sdioh_info_t *sd, bool enable)
+{
+	ulong flags;
+	struct sdos_info *sdos;
+
+	sd_trace(("%s: %s\n", __FUNCTION__, enable ? "Enabling" : "Disabling"));
+
+	sdos = (struct sdos_info *)sd->sdos_info;
+	ASSERT(sdos);
+
+#if !defined(OOB_INTR_ONLY)
+	if (enable && !(sd->intr_handler && sd->intr_handler_arg)) {
+		sd_err(("%s: no handler registered, will not enable\n", __FUNCTION__));
+		return SDIOH_API_RC_FAIL;
+	}
+#endif /* !defined(OOB_INTR_ONLY) */
+
+	/* Ensure atomicity for enable/disable calls */
+	spin_lock_irqsave(&sdos->lock, flags);
+
+	sd->client_intr_enabled = enable;
+	if (enable) {
+		sdioh_sdmmc_devintr_on(sd);
+	} else {
+		sdioh_sdmmc_devintr_off(sd);
+	}
+
+	spin_unlock_irqrestore(&sdos->lock, flags);
+
+	return SDIOH_API_RC_SUCCESS;
+}
+
+
+#ifdef BCMSDH_MODULE
+static int __init
+bcmsdh_module_init(void)
+{
+	int error = 0;
+	sdio_function_init();
+	return error;
+}
+
+static void __exit
+bcmsdh_module_cleanup(void)
+{
+	sdio_function_cleanup();
+}
+
+module_init(bcmsdh_module_init);
+module_exit(bcmsdh_module_cleanup);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION(DESCRIPTION);
+MODULE_AUTHOR(AUTHOR);
+
+#endif /* BCMSDH_MODULE */
+/*
+ * module init
+*/
+int sdio_function_init(void)
+{
+	int error = 0;
+	sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__));
+
+	gInstance = kzalloc(sizeof(BCMSDH_SDMMC_INSTANCE), GFP_KERNEL);
+	if (!gInstance)
+		return -ENOMEM;
+
+	error = sdio_register_driver(&bcmsdh_sdmmc_driver);
+
+	return error;
+}
+
+/*
+ * module cleanup
+*/
+extern int bcmsdh_remove(struct device *dev);
+void sdio_function_cleanup(void)
+{
+	sd_trace(("%s Enter\n", __FUNCTION__));
+
+	sdio_unregister_driver(&bcmsdh_sdmmc_driver);
+
+	if (gInstance)
+		kfree(gInstance);
+}
diff --git a/drivers/net/wireless/bcm4329/bcmsdspi.c b/drivers/net/wireless/bcm4329/bcmsdspi.c
new file mode 100644
index 0000000..636539b
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/bcmsdspi.c
@@ -0,0 +1,1596 @@
+/*
+ * Broadcom BCMSDH to SPI Protocol Conversion Layer
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmsdspi.c,v 1.14.4.2.4.4.6.5 2010/03/10 03:09:48 Exp $
+ */
+
+#include <typedefs.h>
+
+#include <bcmdevs.h>
+#include <bcmendian.h>
+#include <bcmutils.h>
+#include <osl.h>
+#include <siutils.h>
+#include <sdio.h>		/* SDIO Device and Protocol Specs */
+#include <sdioh.h>		/* SDIO Host Controller Specification */
+#include <bcmsdbus.h>		/* bcmsdh to/from specific controller APIs */
+#include <sdiovar.h>		/* ioctl/iovars */
+
+#include <pcicfg.h>
+
+
+#include <bcmsdspi.h>
+#include <bcmspi.h>
+
+#include <proto/sdspi.h>
+
+#define SD_PAGE 4096
+
+/* Globals */
+
+uint sd_msglevel = SDH_ERROR_VAL;
+uint sd_hiok = FALSE;		/* Use hi-speed mode if available? */
+uint sd_sdmode = SDIOH_MODE_SPI;		/* Use SD4 mode by default */
+uint sd_f2_blocksize = 512;	/* Default blocksize */
+
+uint sd_divisor = 2;		/* Default 33MHz/2 = 16MHz for dongle */
+uint sd_power = 1;		/* Default to SD Slot powered ON */
+uint sd_clock = 1;		/* Default to SD Clock turned ON */
+uint sd_crc = 0;		/* Default to SPI CRC Check turned OFF */
+uint sd_pci_slot = 0xFFFFffff; /* Used to force selection of a particular PCI slot */
+
+uint sd_toctl = 7;
+
+/* Prototypes */
+static bool sdspi_start_power(sdioh_info_t *sd);
+static int sdspi_set_highspeed_mode(sdioh_info_t *sd, bool HSMode);
+static int sdspi_card_enablefuncs(sdioh_info_t *sd);
+static void sdspi_cmd_getrsp(sdioh_info_t *sd, uint32 *rsp_buffer, int count);
+static int sdspi_cmd_issue(sdioh_info_t *sd, bool use_dma, uint32 cmd, uint32 arg,
+                           uint32 *data, uint32 datalen);
+static int sdspi_card_regread(sdioh_info_t *sd, int func, uint32 regaddr,
+                              int regsize, uint32 *data);
+static int sdspi_card_regwrite(sdioh_info_t *sd, int func, uint32 regaddr,
+                               int regsize, uint32 data);
+static int sdspi_driver_init(sdioh_info_t *sd);
+static bool sdspi_reset(sdioh_info_t *sd, bool host_reset, bool client_reset);
+static int sdspi_card_buf(sdioh_info_t *sd, int rw, int func, bool fifo,
+                          uint32 addr, int nbytes, uint32 *data);
+static int sdspi_abort(sdioh_info_t *sd, uint func);
+
+static int set_client_block_size(sdioh_info_t *sd, int func, int blocksize);
+
+static uint8 sdspi_crc7(unsigned char* p, uint32 len);
+static uint16 sdspi_crc16(unsigned char* p, uint32 len);
+static int sdspi_crc_onoff(sdioh_info_t *sd, bool use_crc);
+
+/*
+ *  Public entry points & extern's
+ */
+extern sdioh_info_t *
+sdioh_attach(osl_t *osh, void *bar0, uint irq)
+{
+	sdioh_info_t *sd;
+
+	sd_trace(("%s\n", __FUNCTION__));
+	if ((sd = (sdioh_info_t *)MALLOC(osh, sizeof(sdioh_info_t))) == NULL) {
+		sd_err(("sdioh_attach: out of memory, malloced %d bytes\n", MALLOCED(osh)));
+		return NULL;
+	}
+	bzero((char *)sd, sizeof(sdioh_info_t));
+	sd->osh = osh;
+
+	if (spi_osinit(sd) != 0) {
+		sd_err(("%s: spi_osinit() failed\n", __FUNCTION__));
+		MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+		return NULL;
+	}
+
+	sd->bar0 = (uintptr)bar0;
+	sd->irq = irq;
+	sd->intr_handler = NULL;
+	sd->intr_handler_arg = NULL;
+	sd->intr_handler_valid = FALSE;
+
+	/* Set defaults */
+	sd->sd_blockmode = FALSE;
+	sd->use_client_ints = TRUE;
+	sd->sd_use_dma = FALSE;	/* DMA Not supported */
+
+	/* Haven't figured out how to make bytemode work with dma */
+	if (!sd->sd_blockmode)
+		sd->sd_use_dma = 0;
+
+	if (!spi_hw_attach(sd)) {
+		sd_err(("%s: spi_hw_attach() failed\n", __FUNCTION__));
+		spi_osfree(sd);
+		MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+		return NULL;
+	}
+
+	if (sdspi_driver_init(sd) != SUCCESS) {
+		if (sdspi_driver_init(sd) != SUCCESS) {
+			sd_err(("%s:sdspi_driver_init() failed()\n", __FUNCTION__));
+			spi_hw_detach(sd);
+			spi_osfree(sd);
+			MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+			return (NULL);
+		}
+	}
+
+	if (spi_register_irq(sd, irq) != SUCCESS) {
+		sd_err(("%s: spi_register_irq() failed for irq = %d\n", __FUNCTION__, irq));
+		spi_hw_detach(sd);
+		spi_osfree(sd);
+		MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+		return (NULL);
+	}
+
+	sd_trace(("%s: Done\n", __FUNCTION__));
+	return sd;
+}
+
+extern SDIOH_API_RC
+sdioh_detach(osl_t *osh, sdioh_info_t *sd)
+{
+	sd_trace(("%s\n", __FUNCTION__));
+
+	if (sd) {
+		if (sd->card_init_done)
+			sdspi_reset(sd, 1, 1);
+
+		sd_info(("%s: detaching from hardware\n", __FUNCTION__));
+		spi_free_irq(sd->irq, sd);
+		spi_hw_detach(sd);
+		spi_osfree(sd);
+		MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+	}
+
+	return SDIOH_API_RC_SUCCESS;
+}
+
+/* Configure callback to client when we recieve client interrupt */
+extern SDIOH_API_RC
+sdioh_interrupt_register(sdioh_info_t *sd, sdioh_cb_fn_t fn, void *argh)
+{
+	sd_trace(("%s: Entering\n", __FUNCTION__));
+
+	sd->intr_handler = fn;
+	sd->intr_handler_arg = argh;
+	sd->intr_handler_valid = TRUE;
+
+	return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_interrupt_deregister(sdioh_info_t *sd)
+{
+	sd_trace(("%s: Entering\n", __FUNCTION__));
+
+	sd->intr_handler_valid = FALSE;
+	sd->intr_handler = NULL;
+	sd->intr_handler_arg = NULL;
+
+	return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_interrupt_query(sdioh_info_t *sd, bool *onoff)
+{
+	sd_trace(("%s: Entering\n", __FUNCTION__));
+
+	*onoff = sd->client_intr_enabled;
+
+	return SDIOH_API_RC_SUCCESS;
+}
+
+#if defined(DHD_DEBUG)
+extern bool
+sdioh_interrupt_pending(sdioh_info_t *sd)
+{
+	return 0;
+}
+#endif
+
+uint
+sdioh_query_iofnum(sdioh_info_t *sd)
+{
+	return sd->num_funcs;
+}
+
+/* IOVar table */
+enum {
+	IOV_MSGLEVEL = 1,
+	IOV_BLOCKMODE,
+	IOV_BLOCKSIZE,
+	IOV_DMA,
+	IOV_USEINTS,
+	IOV_NUMINTS,
+	IOV_NUMLOCALINTS,
+	IOV_HOSTREG,
+	IOV_DEVREG,
+	IOV_DIVISOR,
+	IOV_SDMODE,
+	IOV_HISPEED,
+	IOV_HCIREGS,
+	IOV_POWER,
+	IOV_CLOCK,
+	IOV_CRC
+};
+
+const bcm_iovar_t sdioh_iovars[] = {
+	{"sd_msglevel",	IOV_MSGLEVEL, 	0,	IOVT_UINT32,	0 },
+	{"sd_blockmode", IOV_BLOCKMODE,	0,	IOVT_BOOL,	0 },
+	{"sd_blocksize", IOV_BLOCKSIZE, 0,	IOVT_UINT32,	0 }, /* ((fn << 16) | size) */
+	{"sd_dma",	IOV_DMA,	0,	IOVT_BOOL,	0 },
+	{"sd_ints",	IOV_USEINTS,	0,	IOVT_BOOL,	0 },
+	{"sd_numints",	IOV_NUMINTS,	0,	IOVT_UINT32,	0 },
+	{"sd_numlocalints", IOV_NUMLOCALINTS, 0, IOVT_UINT32,	0 },
+	{"sd_hostreg",	IOV_HOSTREG,	0,	IOVT_BUFFER,	sizeof(sdreg_t) },
+	{"sd_devreg",	IOV_DEVREG,	0,	IOVT_BUFFER,	sizeof(sdreg_t)	},
+	{"sd_divisor",	IOV_DIVISOR,	0,	IOVT_UINT32,	0 },
+	{"sd_power",	IOV_POWER,	0,	IOVT_UINT32,	0 },
+	{"sd_clock",	IOV_CLOCK,	0,	IOVT_UINT32,	0 },
+	{"sd_crc",	IOV_CRC,	0,	IOVT_UINT32,	0 },
+	{"sd_mode",	IOV_SDMODE,	0,	IOVT_UINT32,	100},
+	{"sd_highspeed",	IOV_HISPEED,	0,	IOVT_UINT32,	0},
+	{NULL, 0, 0, 0, 0 }
+};
+
+int
+sdioh_iovar_op(sdioh_info_t *si, const char *name,
+               void *params, int plen, void *arg, int len, bool set)
+{
+	const bcm_iovar_t *vi = NULL;
+	int bcmerror = 0;
+	int val_size;
+	int32 int_val = 0;
+	bool bool_val;
+	uint32 actionid;
+
+	ASSERT(name);
+	ASSERT(len >= 0);
+
+	/* Get must have return space; Set does not take qualifiers */
+	ASSERT(set || (arg && len));
+	ASSERT(!set || (!params && !plen));
+
+	sd_trace(("%s: Enter (%s %s)\n", __FUNCTION__, (set ? "set" : "get"), name));
+
+	if ((vi = bcm_iovar_lookup(sdioh_iovars, name)) == NULL) {
+		bcmerror = BCME_UNSUPPORTED;
+		goto exit;
+	}
+
+	if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, set)) != 0)
+		goto exit;
+
+	/* Set up params so get and set can share the convenience variables */
+	if (params == NULL) {
+		params = arg;
+		plen = len;
+	}
+
+	if (vi->type == IOVT_VOID)
+		val_size = 0;
+	else if (vi->type == IOVT_BUFFER)
+		val_size = len;
+	else
+		val_size = sizeof(int);
+
+	if (plen >= (int)sizeof(int_val))
+		bcopy(params, &int_val, sizeof(int_val));
+
+	bool_val = (int_val != 0) ? TRUE : FALSE;
+
+	actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid);
+	switch (actionid) {
+	case IOV_GVAL(IOV_MSGLEVEL):
+		int_val = (int32)sd_msglevel;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_MSGLEVEL):
+		sd_msglevel = int_val;
+		break;
+
+	case IOV_GVAL(IOV_BLOCKMODE):
+		int_val = (int32)si->sd_blockmode;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_BLOCKMODE):
+		si->sd_blockmode = (bool)int_val;
+		/* Haven't figured out how to make non-block mode with DMA */
+		if (!si->sd_blockmode)
+			si->sd_use_dma = 0;
+		break;
+
+	case IOV_GVAL(IOV_BLOCKSIZE):
+		if ((uint32)int_val > si->num_funcs) {
+			bcmerror = BCME_BADARG;
+			break;
+		}
+		int_val = (int32)si->client_block_size[int_val];
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_BLOCKSIZE):
+	{
+		uint func = ((uint32)int_val >> 16);
+		uint blksize = (uint16)int_val;
+		uint maxsize;
+
+		if (func > si->num_funcs) {
+			bcmerror = BCME_BADARG;
+			break;
+		}
+
+		switch (func) {
+		case 0: maxsize = 32; break;
+		case 1: maxsize = BLOCK_SIZE_4318; break;
+		case 2: maxsize = BLOCK_SIZE_4328; break;
+		default: maxsize = 0;
+		}
+		if (blksize > maxsize) {
+			bcmerror = BCME_BADARG;
+			break;
+		}
+		if (!blksize) {
+			blksize = maxsize;
+		}
+
+		/* Now set it */
+		spi_lock(si);
+		bcmerror = set_client_block_size(si, func, blksize);
+		spi_unlock(si);
+		break;
+	}
+
+	case IOV_GVAL(IOV_DMA):
+		int_val = (int32)si->sd_use_dma;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_DMA):
+		si->sd_use_dma = (bool)int_val;
+		break;
+
+	case IOV_GVAL(IOV_USEINTS):
+		int_val = (int32)si->use_client_ints;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_USEINTS):
+		break;
+
+	case IOV_GVAL(IOV_DIVISOR):
+		int_val = (uint32)sd_divisor;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_DIVISOR):
+		sd_divisor = int_val;
+		if (!spi_start_clock(si, (uint16)sd_divisor)) {
+			sd_err(("set clock failed!\n"));
+			bcmerror = BCME_ERROR;
+		}
+		break;
+
+	case IOV_GVAL(IOV_POWER):
+		int_val = (uint32)sd_power;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_POWER):
+		sd_power = int_val;
+		break;
+
+	case IOV_GVAL(IOV_CLOCK):
+		int_val = (uint32)sd_clock;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_CLOCK):
+		sd_clock = int_val;
+		break;
+
+	case IOV_GVAL(IOV_CRC):
+		int_val = (uint32)sd_crc;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_CRC):
+		/* Apply new setting, but don't change sd_crc until
+		 * after the CRC-mode is selected in the device.  This
+		 * is required because the software must generate a
+		 * correct CRC for the CMD59 in order to be able to
+		 * turn OFF the CRC.
+		 */
+		sdspi_crc_onoff(si, int_val ? 1 : 0);
+		sd_crc = int_val;
+		break;
+
+	case IOV_GVAL(IOV_SDMODE):
+		int_val = (uint32)sd_sdmode;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_SDMODE):
+		sd_sdmode = int_val;
+		break;
+
+	case IOV_GVAL(IOV_HISPEED):
+		int_val = (uint32)sd_hiok;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_HISPEED):
+		sd_hiok = int_val;
+
+		if (!sdspi_set_highspeed_mode(si, (bool)sd_hiok)) {
+			sd_err(("Failed changing highspeed mode to %d.\n", sd_hiok));
+			bcmerror = BCME_ERROR;
+			return ERROR;
+		}
+		break;
+
+	case IOV_GVAL(IOV_NUMINTS):
+		int_val = (int32)si->intrcount;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_GVAL(IOV_NUMLOCALINTS):
+		int_val = (int32)si->local_intrcount;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_GVAL(IOV_HOSTREG):
+	{
+		break;
+	}
+
+	case IOV_SVAL(IOV_HOSTREG):
+	{
+		sd_err(("IOV_HOSTREG unsupported\n"));
+		break;
+	}
+
+	case IOV_GVAL(IOV_DEVREG):
+	{
+		sdreg_t *sd_ptr = (sdreg_t *)params;
+		uint8 data;
+
+		if (sdioh_cfg_read(si, sd_ptr->func, sd_ptr->offset, &data)) {
+			bcmerror = BCME_SDIO_ERROR;
+			break;
+		}
+
+		int_val = (int)data;
+		bcopy(&int_val, arg, sizeof(int_val));
+		break;
+	}
+
+	case IOV_SVAL(IOV_DEVREG):
+	{
+		sdreg_t *sd_ptr = (sdreg_t *)params;
+		uint8 data = (uint8)sd_ptr->value;
+
+		if (sdioh_cfg_write(si, sd_ptr->func, sd_ptr->offset, &data)) {
+			bcmerror = BCME_SDIO_ERROR;
+			break;
+		}
+		break;
+	}
+
+
+	default:
+		bcmerror = BCME_UNSUPPORTED;
+		break;
+	}
+exit:
+
+	return bcmerror;
+}
+
+extern SDIOH_API_RC
+sdioh_cfg_read(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data)
+{
+	SDIOH_API_RC status;
+	/* No lock needed since sdioh_request_byte does locking */
+	status = sdioh_request_byte(sd, SDIOH_READ, fnc_num, addr, data);
+	return status;
+}
+
+extern SDIOH_API_RC
+sdioh_cfg_write(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data)
+{
+	/* No lock needed since sdioh_request_byte does locking */
+	SDIOH_API_RC status;
+	status = sdioh_request_byte(sd, SDIOH_WRITE, fnc_num, addr, data);
+	return status;
+}
+
+extern SDIOH_API_RC
+sdioh_cis_read(sdioh_info_t *sd, uint func, uint8 *cisd, uint32 length)
+{
+	uint32 count;
+	int offset;
+	uint32 foo;
+	uint8 *cis = cisd;
+
+	sd_trace(("%s: Func = %d\n", __FUNCTION__, func));
+
+	if (!sd->func_cis_ptr[func]) {
+		bzero(cis, length);
+		return SDIOH_API_RC_FAIL;
+	}
+
+	spi_lock(sd);
+	*cis = 0;
+	for (count = 0; count < length; count++) {
+		offset =  sd->func_cis_ptr[func] + count;
+		if (sdspi_card_regread (sd, 0, offset, 1, &foo) < 0) {
+			sd_err(("%s: regread failed: Can't read CIS\n", __FUNCTION__));
+			spi_unlock(sd);
+			return SDIOH_API_RC_FAIL;
+		}
+		*cis = (uint8)(foo & 0xff);
+		cis++;
+	}
+	spi_unlock(sd);
+	return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_request_byte(sdioh_info_t *sd, uint rw, uint func, uint regaddr, uint8 *byte)
+{
+	int status;
+	uint32 cmd_arg;
+	uint32 rsp5;
+
+	spi_lock(sd);
+
+	cmd_arg = 0;
+	cmd_arg = SFIELD(cmd_arg, CMD52_FUNCTION, func);
+	cmd_arg = SFIELD(cmd_arg, CMD52_REG_ADDR, regaddr);
+	cmd_arg = SFIELD(cmd_arg, CMD52_RW_FLAG, rw == SDIOH_READ ? 0 : 1);
+	cmd_arg = SFIELD(cmd_arg, CMD52_RAW, 0);
+	cmd_arg = SFIELD(cmd_arg, CMD52_DATA, rw == SDIOH_READ ? 0 : *byte);
+
+	sd_trace(("%s: rw=%d, func=%d, regaddr=0x%08x\n", __FUNCTION__, rw, func, regaddr));
+
+	if ((status = sdspi_cmd_issue(sd, sd->sd_use_dma,
+	                              SDIOH_CMD_52, cmd_arg, NULL, 0)) != SUCCESS) {
+		spi_unlock(sd);
+		return status;
+	}
+
+	sdspi_cmd_getrsp(sd, &rsp5, 1);
+	if (rsp5 != 0x00) {
+		sd_err(("%s: rsp5 flags is 0x%x func=%d\n",
+		        __FUNCTION__, rsp5, func));
+		/* ASSERT(0); */
+		spi_unlock(sd);
+		return SDIOH_API_RC_FAIL;
+	}
+
+	if (rw == SDIOH_READ)
+		*byte = sd->card_rsp_data >> 24;
+
+	spi_unlock(sd);
+	return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_request_word(sdioh_info_t *sd, uint cmd_type, uint rw, uint func, uint addr,
+                   uint32 *word, uint nbytes)
+{
+	int status;
+
+	spi_lock(sd);
+
+	if (rw == SDIOH_READ)
+		status = sdspi_card_regread(sd, func, addr, nbytes, word);
+	else
+		status = sdspi_card_regwrite(sd, func, addr, nbytes, *word);
+
+	spi_unlock(sd);
+	return (status == SUCCESS ?  SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL);
+}
+
+extern SDIOH_API_RC
+sdioh_request_buffer(sdioh_info_t *sd, uint pio_dma, uint fix_inc, uint rw, uint func,
+                     uint addr, uint reg_width, uint buflen_u, uint8 *buffer, void *pkt)
+{
+	int len;
+	int buflen = (int)buflen_u;
+	bool fifo = (fix_inc == SDIOH_DATA_FIX);
+
+	spi_lock(sd);
+
+	ASSERT(reg_width == 4);
+	ASSERT(buflen_u < (1 << 30));
+	ASSERT(sd->client_block_size[func]);
+
+	sd_data(("%s: %c len %d r_cnt %d t_cnt %d, pkt @0x%p\n",
+	         __FUNCTION__, rw == SDIOH_READ ? 'R' : 'W',
+	         buflen_u, sd->r_cnt, sd->t_cnt, pkt));
+
+	/* Break buffer down into blocksize chunks:
+	 * Bytemode: 1 block at a time.
+	 */
+	while (buflen > 0) {
+		if (sd->sd_blockmode) {
+			/* Max xfer is Page size */
+			len = MIN(SD_PAGE, buflen);
+
+			/* Round down to a block boundry */
+			if (buflen > sd->client_block_size[func])
+				len = (len/sd->client_block_size[func]) *
+				        sd->client_block_size[func];
+		} else {
+			/* Byte mode: One block at a time */
+			len = MIN(sd->client_block_size[func], buflen);
+		}
+
+		if (sdspi_card_buf(sd, rw, func, fifo, addr, len, (uint32 *)buffer) != SUCCESS) {
+			spi_unlock(sd);
+			return SDIOH_API_RC_FAIL;
+		}
+		buffer += len;
+		buflen -= len;
+		if (!fifo)
+			addr += len;
+	}
+	spi_unlock(sd);
+	return SDIOH_API_RC_SUCCESS;
+}
+
+static int
+sdspi_abort(sdioh_info_t *sd, uint func)
+{
+	uint8 spi_databuf[] = { 0x74, 0x80, 0x00, 0x0C, 0xFF, 0x95, 0xFF, 0xFF,
+	                        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+	uint8 spi_rspbuf[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	                       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+	int err = 0;
+
+	sd_err(("Sending SPI Abort to F%d\n", func));
+	spi_databuf[4] = func & 0x7;
+	/* write to function 0, addr 6 (IOABORT) func # in 3 LSBs. */
+	spi_sendrecv(sd, spi_databuf, spi_rspbuf, sizeof(spi_databuf));
+
+	return err;
+}
+
+extern int
+sdioh_abort(sdioh_info_t *sd, uint fnum)
+{
+	int ret;
+
+	spi_lock(sd);
+	ret = sdspi_abort(sd, fnum);
+	spi_unlock(sd);
+
+	return ret;
+}
+
+int
+sdioh_start(sdioh_info_t *sd, int stage)
+{
+	return SUCCESS;
+}
+
+int
+sdioh_stop(sdioh_info_t *sd)
+{
+	return SUCCESS;
+}
+
+
+/*
+ * Private/Static work routines
+ */
+static bool
+sdspi_reset(sdioh_info_t *sd, bool host_reset, bool client_reset)
+{
+	if (!sd)
+		return TRUE;
+
+	spi_lock(sd);
+	/* Reset client card */
+	if (client_reset && (sd->adapter_slot != -1)) {
+		if (sdspi_card_regwrite(sd, 0, SDIOD_CCCR_IOABORT, 1, 0x8) != SUCCESS)
+			sd_err(("%s: Cannot write to card reg 0x%x\n",
+			        __FUNCTION__, SDIOD_CCCR_IOABORT));
+		else
+			sd->card_rca = 0;
+	}
+
+	/* The host reset is a NOP in the sd-spi case. */
+	if (host_reset) {
+		sd->sd_mode = SDIOH_MODE_SPI;
+	}
+	spi_unlock(sd);
+	return TRUE;
+}
+
+static int
+sdspi_host_init(sdioh_info_t *sd)
+{
+	sdspi_reset(sd, 1, 0);
+
+	/* Default power on mode is SD1 */
+	sd->sd_mode = SDIOH_MODE_SPI;
+	sd->polled_mode = TRUE;
+	sd->host_init_done = TRUE;
+	sd->card_init_done = FALSE;
+	sd->adapter_slot = 1;
+
+	return (SUCCESS);
+}
+
+#define CMD0_RETRIES 3
+#define CMD5_RETRIES 10
+
+static int
+get_ocr(sdioh_info_t *sd, uint32 *cmd_arg, uint32 *cmd_rsp)
+{
+	uint32 rsp5;
+	int retries, status;
+
+	/* First issue a CMD0 to get the card into SPI mode. */
+	for (retries = 0; retries <= CMD0_RETRIES; retries++) {
+		if ((status = sdspi_cmd_issue(sd, sd->sd_use_dma,
+		                              SDIOH_CMD_0, *cmd_arg, NULL, 0)) != SUCCESS) {
+			sd_err(("%s: No response to CMD0\n", __FUNCTION__));
+			continue;
+		}
+
+		sdspi_cmd_getrsp(sd, &rsp5, 1);
+
+		if (GFIELD(rsp5, SPI_RSP_ILL_CMD)) {
+			printf("%s: Card already initialized (continuing)\n", __FUNCTION__);
+			break;
+		}
+
+		if (GFIELD(rsp5, SPI_RSP_IDLE)) {
+			printf("%s: Card in SPI mode\n", __FUNCTION__);
+			break;
+		}
+	}
+
+	if (retries > CMD0_RETRIES) {
+		sd_err(("%s: Too many retries for CMD0\n", __FUNCTION__));
+		return ERROR;
+	}
+
+	/* Get the Card's Operation Condition. */
+	/* Occasionally the board takes a while to become ready. */
+	for (retries = 0; retries <= CMD5_RETRIES; retries++) {
+		if ((status = sdspi_cmd_issue(sd, sd->sd_use_dma,
+		                              SDIOH_CMD_5, *cmd_arg, NULL, 0)) != SUCCESS) {
+			sd_err(("%s: No response to CMD5\n", __FUNCTION__));
+			continue;
+		}
+
+		printf("CMD5 response data was: 0x%08x\n", sd->card_rsp_data);
+
+		if (GFIELD(sd->card_rsp_data, RSP4_CARD_READY)) {
+			printf("%s: Card ready\n", __FUNCTION__);
+			break;
+		}
+	}
+
+	if (retries > CMD5_RETRIES) {
+		sd_err(("%s: Too many retries for CMD5\n", __FUNCTION__));
+		return ERROR;
+	}
+
+	*cmd_rsp = sd->card_rsp_data;
+
+	sdspi_crc_onoff(sd, sd_crc ? 1 : 0);
+
+	return (SUCCESS);
+}
+
+static int
+sdspi_crc_onoff(sdioh_info_t *sd, bool use_crc)
+{
+	uint32 args;
+	int status;
+
+	args = use_crc ? 1 : 0;
+	if ((status = sdspi_cmd_issue(sd, sd->sd_use_dma,
+	                              SDIOH_CMD_59, args, NULL, 0)) != SUCCESS) {
+		sd_err(("%s: No response to CMD59\n", __FUNCTION__));
+	}
+
+	sd_info(("CMD59 response data was: 0x%08x\n", sd->card_rsp_data));
+
+	sd_err(("SD-SPI CRC turned %s\n", use_crc ? "ON" : "OFF"));
+	return (SUCCESS);
+}
+
+static int
+sdspi_client_init(sdioh_info_t *sd)
+{
+	uint8 fn_ints;
+
+	sd_trace(("%s: Powering up slot %d\n", __FUNCTION__, sd->adapter_slot));
+
+	/* Start at ~400KHz clock rate for initialization */
+	if (!spi_start_clock(sd, 128)) {
+		sd_err(("spi_start_clock failed\n"));
+		return ERROR;
+	}
+
+	if (!sdspi_start_power(sd)) {
+		sd_err(("sdspi_start_power failed\n"));
+		return ERROR;
+	}
+
+	if (sd->num_funcs == 0) {
+		sd_err(("%s: No IO funcs!\n", __FUNCTION__));
+		return ERROR;
+	}
+
+	sdspi_card_enablefuncs(sd);
+
+	set_client_block_size(sd, 1, BLOCK_SIZE_4318);
+	fn_ints = INTR_CTL_FUNC1_EN;
+
+	if (sd->num_funcs >= 2) {
+		set_client_block_size(sd, 2, sd_f2_blocksize /* BLOCK_SIZE_4328 */);
+		fn_ints |= INTR_CTL_FUNC2_EN;
+	}
+
+	/* Enable/Disable Client interrupts */
+	/* Turn on here but disable at host controller */
+	if (sdspi_card_regwrite(sd, 0, SDIOD_CCCR_INTEN, 1,
+	                        (fn_ints | INTR_CTL_MASTER_EN)) != SUCCESS) {
+		sd_err(("%s: Could not enable ints in CCCR\n", __FUNCTION__));
+		return ERROR;
+	}
+
+	/* Switch to High-speed clocking mode if both host and device support it */
+	sdspi_set_highspeed_mode(sd, (bool)sd_hiok);
+
+	/* After configuring for High-Speed mode, set the desired clock rate. */
+	if (!spi_start_clock(sd, (uint16)sd_divisor)) {
+		sd_err(("spi_start_clock failed\n"));
+		return ERROR;
+	}
+
+	sd->card_init_done = TRUE;
+
+	return SUCCESS;
+}
+
+static int
+sdspi_set_highspeed_mode(sdioh_info_t *sd, bool HSMode)
+{
+	uint32 regdata;
+	int status;
+	bool hsmode;
+
+	if (HSMode == TRUE) {
+
+		sd_err(("Attempting to enable High-Speed mode.\n"));
+
+		if ((status = sdspi_card_regread(sd, 0, SDIOD_CCCR_SPEED_CONTROL,
+		                                 1, &regdata)) != SUCCESS) {
+			return status;
+		}
+		if (regdata & SDIO_SPEED_SHS) {
+			sd_err(("Device supports High-Speed mode.\n"));
+
+			regdata |= SDIO_SPEED_EHS;
+
+			sd_err(("Writing %08x to Card at %08x\n",
+			         regdata, SDIOD_CCCR_SPEED_CONTROL));
+			if ((status = sdspi_card_regwrite(sd, 0, SDIOD_CCCR_SPEED_CONTROL,
+			                                  1, regdata)) != BCME_OK) {
+				return status;
+			}
+
+			hsmode = 1;
+
+			sd_err(("High-speed clocking mode enabled.\n"));
+		}
+		else {
+			sd_err(("Device does not support High-Speed Mode.\n"));
+			hsmode = 0;
+		}
+	} else {
+		if ((status = sdspi_card_regread(sd, 0, SDIOD_CCCR_SPEED_CONTROL,
+		                                 1, &regdata)) != SUCCESS) {
+			return status;
+		}
+
+		regdata = ~SDIO_SPEED_EHS;
+
+		sd_err(("Writing %08x to Card at %08x\n",
+		         regdata, SDIOD_CCCR_SPEED_CONTROL));
+		if ((status = sdspi_card_regwrite(sd, 0, SDIOD_CCCR_SPEED_CONTROL,
+		                                  1, regdata)) != BCME_OK) {
+			return status;
+		}
+
+		sd_err(("Low-speed clocking mode enabled.\n"));
+		hsmode = 0;
+	}
+
+	spi_controller_highspeed_mode(sd, hsmode);
+
+	return TRUE;
+}
+
+bool
+sdspi_start_power(sdioh_info_t *sd)
+{
+	uint32 cmd_arg;
+	uint32 cmd_rsp;
+
+	sd_trace(("%s\n", __FUNCTION__));
+
+	/* Get the Card's Operation Condition.  Occasionally the board
+	 * takes a while to become ready
+	 */
+
+	cmd_arg = 0;
+	if (get_ocr(sd, &cmd_arg, &cmd_rsp) != SUCCESS) {
+		sd_err(("%s: Failed to get OCR; bailing\n", __FUNCTION__));
+		return FALSE;
+	}
+
+	sd_err(("mem_present = %d\n", GFIELD(cmd_rsp, RSP4_MEM_PRESENT)));
+	sd_err(("num_funcs = %d\n", GFIELD(cmd_rsp, RSP4_NUM_FUNCS)));
+	sd_err(("card_ready = %d\n", GFIELD(cmd_rsp, RSP4_CARD_READY)));
+	sd_err(("OCR = 0x%x\n", GFIELD(cmd_rsp, RSP4_IO_OCR)));
+
+	/* Verify that the card supports I/O mode */
+	if (GFIELD(cmd_rsp, RSP4_NUM_FUNCS) == 0) {
+		sd_err(("%s: Card does not support I/O\n", __FUNCTION__));
+		return ERROR;
+	}
+
+	sd->num_funcs = GFIELD(cmd_rsp, RSP4_NUM_FUNCS);
+
+	/* Examine voltage: Arasan only supports 3.3 volts,
+	 * so look for 3.2-3.3 Volts and also 3.3-3.4 volts.
+	 */
+
+	if ((GFIELD(cmd_rsp, RSP4_IO_OCR) & (0x3 << 20)) == 0) {
+		sd_err(("This client does not support 3.3 volts!\n"));
+		return ERROR;
+	}
+
+
+	return TRUE;
+}
+
+static int
+sdspi_driver_init(sdioh_info_t *sd)
+{
+	sd_trace(("%s\n", __FUNCTION__));
+
+	if ((sdspi_host_init(sd)) != SUCCESS) {
+		return ERROR;
+	}
+
+	if (sdspi_client_init(sd) != SUCCESS) {
+		return ERROR;
+	}
+
+	return SUCCESS;
+}
+
+static int
+sdspi_card_enablefuncs(sdioh_info_t *sd)
+{
+	int status;
+	uint32 regdata;
+	uint32 regaddr, fbraddr;
+	uint8 func;
+	uint8 *ptr;
+
+	sd_trace(("%s\n", __FUNCTION__));
+	/* Get the Card's common CIS address */
+	ptr = (uint8 *) &sd->com_cis_ptr;
+	for (regaddr = SDIOD_CCCR_CISPTR_0; regaddr <= SDIOD_CCCR_CISPTR_2; regaddr++) {
+		if ((status = sdspi_card_regread (sd, 0, regaddr, 1, &regdata)) != SUCCESS)
+			return status;
+
+		*ptr++ = (uint8) regdata;
+	}
+
+	/* Only the lower 17-bits are valid */
+	sd->com_cis_ptr &= 0x0001FFFF;
+	sd->func_cis_ptr[0] = sd->com_cis_ptr;
+	sd_info(("%s: Card's Common CIS Ptr = 0x%x\n", __FUNCTION__, sd->com_cis_ptr));
+
+	/* Get the Card's function CIS (for each function) */
+	for (fbraddr = SDIOD_FBR_STARTADDR, func = 1;
+	     func <= sd->num_funcs; func++, fbraddr += SDIOD_FBR_SIZE) {
+		ptr = (uint8 *) &sd->func_cis_ptr[func];
+		for (regaddr = SDIOD_FBR_CISPTR_0; regaddr <= SDIOD_FBR_CISPTR_2; regaddr++) {
+			if ((status = sdspi_card_regread (sd, 0, regaddr + fbraddr, 1, &regdata))
+			    != SUCCESS)
+				return status;
+
+			*ptr++ = (uint8) regdata;
+		}
+
+		/* Only the lower 17-bits are valid */
+		sd->func_cis_ptr[func] &= 0x0001FFFF;
+		sd_info(("%s: Function %d CIS Ptr = 0x%x\n",
+		         __FUNCTION__, func, sd->func_cis_ptr[func]));
+	}
+
+	sd_info(("%s: write ESCI bit\n", __FUNCTION__));
+	/* Enable continuous SPI interrupt (ESCI bit) */
+	sdspi_card_regwrite(sd, 0, SDIOD_CCCR_BICTRL, 1, 0x60);
+
+	sd_info(("%s: enable f1\n", __FUNCTION__));
+	/* Enable function 1 on the card */
+	regdata = SDIO_FUNC_ENABLE_1;
+	if ((status = sdspi_card_regwrite(sd, 0, SDIOD_CCCR_IOEN, 1, regdata)) != SUCCESS)
+		return status;
+
+	sd_info(("%s: done\n", __FUNCTION__));
+	return SUCCESS;
+}
+
+/* Read client card reg */
+static int
+sdspi_card_regread(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 *data)
+{
+	int status;
+	uint32 cmd_arg;
+	uint32 rsp5;
+
+	cmd_arg = 0;
+
+	if ((func == 0) || (regsize == 1)) {
+		cmd_arg = SFIELD(cmd_arg, CMD52_FUNCTION, func);
+		cmd_arg = SFIELD(cmd_arg, CMD52_REG_ADDR, regaddr);
+		cmd_arg = SFIELD(cmd_arg, CMD52_RW_FLAG, SDIOH_XFER_TYPE_READ);
+		cmd_arg = SFIELD(cmd_arg, CMD52_RAW, 0);
+		cmd_arg = SFIELD(cmd_arg, CMD52_DATA, 0);
+
+		if ((status = sdspi_cmd_issue(sd, sd->sd_use_dma, SDIOH_CMD_52, cmd_arg, NULL, 0))
+		    != SUCCESS)
+			return status;
+
+		sdspi_cmd_getrsp(sd, &rsp5, 1);
+
+		if (rsp5 != 0x00)
+			sd_err(("%s: rsp5 flags is 0x%x\t %d\n",
+			        __FUNCTION__, rsp5, func));
+
+		*data = sd->card_rsp_data >> 24;
+	} else {
+		cmd_arg = SFIELD(cmd_arg, CMD53_BYTE_BLK_CNT, regsize);
+		cmd_arg = SFIELD(cmd_arg, CMD53_OP_CODE, 1);
+		cmd_arg = SFIELD(cmd_arg, CMD53_BLK_MODE, 0);
+		cmd_arg = SFIELD(cmd_arg, CMD53_FUNCTION, func);
+		cmd_arg = SFIELD(cmd_arg, CMD53_REG_ADDR, regaddr);
+		cmd_arg = SFIELD(cmd_arg, CMD53_RW_FLAG, SDIOH_XFER_TYPE_READ);
+
+		sd->data_xfer_count = regsize;
+
+		/* sdspi_cmd_issue() returns with the command complete bit
+		 * in the ISR already cleared
+		 */
+		if ((status = sdspi_cmd_issue(sd, sd->sd_use_dma, SDIOH_CMD_53, cmd_arg, NULL, 0))
+		    != SUCCESS)
+			return status;
+
+		sdspi_cmd_getrsp(sd, &rsp5, 1);
+
+		if (rsp5 != 0x00)
+			sd_err(("%s: rsp5 flags is 0x%x\t %d\n",
+			        __FUNCTION__, rsp5, func));
+
+		*data = sd->card_rsp_data;
+		if (regsize == 2) {
+			*data &= 0xffff;
+		}
+
+		sd_info(("%s: CMD53 func %d, addr 0x%x, size %d, data 0x%08x\n",
+		         __FUNCTION__, func, regaddr, regsize, *data));
+
+
+	}
+
+	return SUCCESS;
+}
+
+/* write a client register */
+static int
+sdspi_card_regwrite(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 data)
+{
+	int status;
+	uint32 cmd_arg, rsp5, flags;
+
+	cmd_arg = 0;
+
+	if ((func == 0) || (regsize == 1)) {
+		cmd_arg = SFIELD(cmd_arg, CMD52_FUNCTION, func);
+		cmd_arg = SFIELD(cmd_arg, CMD52_REG_ADDR, regaddr);
+		cmd_arg = SFIELD(cmd_arg, CMD52_RW_FLAG, SDIOH_XFER_TYPE_WRITE);
+		cmd_arg = SFIELD(cmd_arg, CMD52_RAW, 0);
+		cmd_arg = SFIELD(cmd_arg, CMD52_DATA, data & 0xff);
+		if ((status = sdspi_cmd_issue(sd, sd->sd_use_dma, SDIOH_CMD_52, cmd_arg, NULL, 0))
+		    != SUCCESS)
+			return status;
+
+		sdspi_cmd_getrsp(sd, &rsp5, 1);
+		flags = GFIELD(rsp5, RSP5_FLAGS);
+		if (flags && (flags != 0x10))
+			sd_err(("%s: rsp5.rsp5.flags = 0x%x, expecting 0x10\n",
+			        __FUNCTION__,  flags));
+	}
+	else {
+		cmd_arg = SFIELD(cmd_arg, CMD53_BYTE_BLK_CNT, regsize);
+		cmd_arg = SFIELD(cmd_arg, CMD53_OP_CODE, 1);
+		cmd_arg = SFIELD(cmd_arg, CMD53_BLK_MODE, 0);
+		cmd_arg = SFIELD(cmd_arg, CMD53_FUNCTION, func);
+		cmd_arg = SFIELD(cmd_arg, CMD53_REG_ADDR, regaddr);
+		cmd_arg = SFIELD(cmd_arg, CMD53_RW_FLAG, SDIOH_XFER_TYPE_WRITE);
+
+		sd->data_xfer_count = regsize;
+		sd->cmd53_wr_data = data;
+
+		sd_info(("%s: CMD53 func %d, addr 0x%x, size %d, data 0x%08x\n",
+		         __FUNCTION__, func, regaddr, regsize, data));
+
+		/* sdspi_cmd_issue() returns with the command complete bit
+		 * in the ISR already cleared
+		 */
+		if ((status = sdspi_cmd_issue(sd, sd->sd_use_dma, SDIOH_CMD_53, cmd_arg, NULL, 0))
+		    != SUCCESS)
+			return status;
+
+		sdspi_cmd_getrsp(sd, &rsp5, 1);
+
+		if (rsp5 != 0x00)
+			sd_err(("%s: rsp5 flags = 0x%x, expecting 0x00\n",
+			        __FUNCTION__,  rsp5));
+
+	}
+	return SUCCESS;
+}
+
+void
+sdspi_cmd_getrsp(sdioh_info_t *sd, uint32 *rsp_buffer, int count /* num 32 bit words */)
+{
+	*rsp_buffer = sd->card_response;
+}
+
+int max_errors = 0;
+
+#define SPI_MAX_PKT_LEN		768
+uint8	spi_databuf[SPI_MAX_PKT_LEN];
+uint8	spi_rspbuf[SPI_MAX_PKT_LEN];
+
+/* datalen is used for CMD53 length only (0 for sd->data_xfer_count) */
+static int
+sdspi_cmd_issue(sdioh_info_t *sd, bool use_dma, uint32 cmd, uint32 arg,
+                uint32 *data, uint32 datalen)
+{
+	uint32 cmd_reg;
+	uint32 cmd_arg = arg;
+	uint8 cmd_crc = 0x95;		/* correct CRC for CMD0 and don't care for others. */
+	uint16 dat_crc;
+	uint8 cmd52data = 0;
+	uint32 i, j;
+	uint32 spi_datalen = 0;
+	uint32 spi_pre_cmd_pad	= 0;
+	uint32 spi_max_response_pad = 128;
+
+	cmd_reg = 0;
+	cmd_reg = SFIELD(cmd_reg, SPI_DIR, 1);
+	cmd_reg = SFIELD(cmd_reg, SPI_CMD_INDEX, cmd);
+
+	if (GFIELD(cmd_arg, CMD52_RW_FLAG) == 1) {	/* Same for CMD52 and CMD53 */
+		cmd_reg = SFIELD(cmd_reg, SPI_RW, 1);
+	}
+
+	switch (cmd) {
+	case SDIOH_CMD_59:	/* CRC_ON_OFF (SPI Mode Only) - Response R1 */
+		cmd52data = arg & 0x1;
+	case SDIOH_CMD_0:	/* Set Card to Idle State - No Response */
+	case SDIOH_CMD_5:	/* Send Operation condition - Response R4 */
+		sd_trace(("%s: CMD%d\n", __FUNCTION__, cmd));
+		spi_datalen = 44;
+		spi_pre_cmd_pad = 12;
+		spi_max_response_pad = 28;
+		break;
+
+	case SDIOH_CMD_3:	/* Ask card to send RCA - Response R6 */
+	case SDIOH_CMD_7:	/* Select card - Response R1 */
+	case SDIOH_CMD_15:	/* Set card to inactive state - Response None */
+		sd_err(("%s: CMD%d is invalid for SPI Mode.\n", __FUNCTION__, cmd));
+		return ERROR;
+		break;
+
+	case SDIOH_CMD_52:	/* IO R/W Direct (single byte) - Response R5 */
+		cmd52data = GFIELD(cmd_arg, CMD52_DATA);
+		cmd_arg = arg;
+		cmd_reg = SFIELD(cmd_reg, SPI_FUNC, GFIELD(cmd_arg, CMD52_FUNCTION));
+		cmd_reg = SFIELD(cmd_reg, SPI_ADDR, GFIELD(cmd_arg, CMD52_REG_ADDR));
+		/* Display trace for byte write */
+		if (GFIELD(cmd_arg, CMD52_RW_FLAG) == 1) {
+			sd_trace(("%s: CMD52: Wr F:%d @0x%04x=%02x\n",
+			          __FUNCTION__,
+			          GFIELD(cmd_arg, CMD52_FUNCTION),
+			          GFIELD(cmd_arg, CMD52_REG_ADDR),
+			          cmd52data));
+		}
+
+		spi_datalen = 32;
+		spi_max_response_pad = 28;
+
+		break;
+	case SDIOH_CMD_53:	/* IO R/W Extended (multiple bytes/blocks) */
+		cmd_arg = arg;
+		cmd_reg = SFIELD(cmd_reg, SPI_FUNC, GFIELD(cmd_arg, CMD53_FUNCTION));
+		cmd_reg = SFIELD(cmd_reg, SPI_ADDR, GFIELD(cmd_arg, CMD53_REG_ADDR));
+		cmd_reg = SFIELD(cmd_reg, SPI_BLKMODE, 0);
+		cmd_reg = SFIELD(cmd_reg, SPI_OPCODE, GFIELD(cmd_arg, CMD53_OP_CODE));
+		cmd_reg = SFIELD(cmd_reg, SPI_STUFF0, (sd->data_xfer_count>>8));
+		cmd52data = (uint8)sd->data_xfer_count;
+
+		/* Set upper bit in byte count if necessary, but don't set it for 512 bytes. */
+		if ((sd->data_xfer_count > 255) && (sd->data_xfer_count < 512)) {
+			cmd_reg |= 1;
+		}
+
+		if (GFIELD(cmd_reg, SPI_RW) == 1) { /* Write */
+			spi_max_response_pad = 32;
+			spi_datalen = (sd->data_xfer_count + spi_max_response_pad) & 0xFFFC;
+		} else { /* Read */
+
+			spi_max_response_pad = 32;
+			spi_datalen = (sd->data_xfer_count + spi_max_response_pad) & 0xFFFC;
+		}
+		sd_trace(("%s: CMD53: %s F:%d @0x%04x len=0x%02x\n",
+		          __FUNCTION__,
+		          (GFIELD(cmd_reg, SPI_RW) == 1 ? "Wr" : "Rd"),
+		          GFIELD(cmd_arg, CMD53_FUNCTION),
+		          GFIELD(cmd_arg, CMD53_REG_ADDR),
+		          cmd52data));
+		break;
+
+	default:
+		sd_err(("%s: Unknown command %d\n", __FUNCTION__, cmd));
+		return ERROR;
+	}
+
+	/* Set up and issue the SDIO command */
+	memset(spi_databuf, SDSPI_IDLE_PAD, spi_datalen);
+	spi_databuf[spi_pre_cmd_pad + 0] = (cmd_reg & 0xFF000000) >> 24;
+	spi_databuf[spi_pre_cmd_pad + 1] = (cmd_reg & 0x00FF0000) >> 16;
+	spi_databuf[spi_pre_cmd_pad + 2] = (cmd_reg & 0x0000FF00) >> 8;
+	spi_databuf[spi_pre_cmd_pad + 3] = (cmd_reg & 0x000000FF);
+	spi_databuf[spi_pre_cmd_pad + 4] = cmd52data;
+
+	/* Generate CRC7 for command, if CRC is enabled, otherwise, a
+	 * default CRC7 of 0x95, which is correct for CMD0, is used.
+	 */
+	if (sd_crc) {
+		cmd_crc = sdspi_crc7(&spi_databuf[spi_pre_cmd_pad], 5);
+	}
+	spi_databuf[spi_pre_cmd_pad + 5] = cmd_crc;
+#define SPI_STOP_TRAN		0xFD
+
+	/* for CMD53 Write, put the data into the output buffer  */
+	if ((cmd == SDIOH_CMD_53) && (GFIELD(cmd_arg, CMD53_RW_FLAG) == 1)) {
+		if (datalen != 0) {
+			spi_databuf[spi_pre_cmd_pad + 9] = SDSPI_IDLE_PAD;
+			spi_databuf[spi_pre_cmd_pad + 10] = SDSPI_START_BLOCK;
+
+			for (i = 0; i < sd->data_xfer_count; i++) {
+				spi_databuf[i + 11 + spi_pre_cmd_pad] = ((uint8 *)data)[i];
+			}
+			if (sd_crc) {
+				dat_crc = sdspi_crc16(&spi_databuf[spi_pre_cmd_pad+11], i);
+			} else {
+				dat_crc = 0xAAAA;
+			}
+			spi_databuf[i + 11 + spi_pre_cmd_pad] = (dat_crc >> 8) & 0xFF;
+			spi_databuf[i + 12 + spi_pre_cmd_pad] = dat_crc & 0xFF;
+		} else if (sd->data_xfer_count == 2) {
+			spi_databuf[spi_pre_cmd_pad + 9] = SDSPI_IDLE_PAD;
+			spi_databuf[spi_pre_cmd_pad + 10] = SDSPI_START_BLOCK;
+			spi_databuf[spi_pre_cmd_pad + 11]  = sd->cmd53_wr_data & 0xFF;
+			spi_databuf[spi_pre_cmd_pad + 12] = (sd->cmd53_wr_data & 0x0000FF00) >> 8;
+			if (sd_crc) {
+				dat_crc = sdspi_crc16(&spi_databuf[spi_pre_cmd_pad+11], 2);
+			} else {
+				dat_crc = 0x22AA;
+			}
+			spi_databuf[spi_pre_cmd_pad + 13] = (dat_crc >> 8) & 0xFF;
+			spi_databuf[spi_pre_cmd_pad + 14] = (dat_crc & 0xFF);
+		} else if (sd->data_xfer_count == 4) {
+			spi_databuf[spi_pre_cmd_pad + 9] = SDSPI_IDLE_PAD;
+			spi_databuf[spi_pre_cmd_pad + 10] = SDSPI_START_BLOCK;
+			spi_databuf[spi_pre_cmd_pad + 11]  = sd->cmd53_wr_data & 0xFF;
+			spi_databuf[spi_pre_cmd_pad + 12] = (sd->cmd53_wr_data & 0x0000FF00) >> 8;
+			spi_databuf[spi_pre_cmd_pad + 13] = (sd->cmd53_wr_data & 0x00FF0000) >> 16;
+			spi_databuf[spi_pre_cmd_pad + 14] = (sd->cmd53_wr_data & 0xFF000000) >> 24;
+			if (sd_crc) {
+				dat_crc = sdspi_crc16(&spi_databuf[spi_pre_cmd_pad+11], 4);
+			} else {
+				dat_crc = 0x44AA;
+			}
+			spi_databuf[spi_pre_cmd_pad + 15] = (dat_crc >> 8) & 0xFF;
+			spi_databuf[spi_pre_cmd_pad + 16] = (dat_crc & 0xFF);
+		} else {
+			printf("CMD53 Write: size %d unsupported\n", sd->data_xfer_count);
+		}
+	}
+
+	spi_sendrecv(sd, spi_databuf, spi_rspbuf, spi_datalen);
+
+	for (i = spi_pre_cmd_pad + SDSPI_COMMAND_LEN; i < spi_max_response_pad; i++) {
+		if ((spi_rspbuf[i] & SDSPI_START_BIT_MASK) == 0) {
+			break;
+		}
+	}
+
+	if (i == spi_max_response_pad) {
+		sd_err(("%s: Did not get a response for CMD%d\n", __FUNCTION__, cmd));
+		return ERROR;
+	}
+
+	/* Extract the response. */
+	sd->card_response = spi_rspbuf[i];
+
+	/* for CMD53 Read, find the start of the response data... */
+	if ((cmd == SDIOH_CMD_53) && (GFIELD(cmd_arg, CMD52_RW_FLAG) == 0)) {
+		for (; i < spi_max_response_pad; i++) {
+			if (spi_rspbuf[i] == SDSPI_START_BLOCK) {
+				break;
+			}
+		}
+
+		if (i == spi_max_response_pad) {
+			printf("Did not get a start of data phase for CMD%d\n", cmd);
+			max_errors++;
+			sdspi_abort(sd, GFIELD(cmd_arg, CMD53_FUNCTION));
+		}
+		sd->card_rsp_data = spi_rspbuf[i+1];
+		sd->card_rsp_data |= spi_rspbuf[i+2] << 8;
+		sd->card_rsp_data |= spi_rspbuf[i+3] << 16;
+		sd->card_rsp_data |= spi_rspbuf[i+4] << 24;
+
+		if (datalen != 0) {
+			i++;
+			for (j = 0; j < sd->data_xfer_count; j++) {
+				((uint8 *)data)[j] = spi_rspbuf[i+j];
+			}
+			if (sd_crc) {
+				uint16 recv_crc;
+
+				recv_crc = spi_rspbuf[i+j] << 8 | spi_rspbuf[i+j+1];
+				dat_crc = sdspi_crc16((uint8 *)data, datalen);
+				if (dat_crc != recv_crc) {
+					sd_err(("%s: Incorrect data CRC: expected 0x%04x, "
+					        "received 0x%04x\n",
+					        __FUNCTION__, dat_crc, recv_crc));
+				}
+			}
+		}
+		return SUCCESS;
+	}
+
+	sd->card_rsp_data = spi_rspbuf[i+4];
+	sd->card_rsp_data |= spi_rspbuf[i+3] << 8;
+	sd->card_rsp_data |= spi_rspbuf[i+2] << 16;
+	sd->card_rsp_data |= spi_rspbuf[i+1] << 24;
+
+	/* Display trace for byte read */
+	if ((cmd == SDIOH_CMD_52) && (GFIELD(cmd_arg, CMD52_RW_FLAG) == 0)) {
+		sd_trace(("%s: CMD52: Rd F:%d @0x%04x=%02x\n",
+		          __FUNCTION__,
+		          GFIELD(cmd_arg, CMD53_FUNCTION),
+		          GFIELD(cmd_arg, CMD53_REG_ADDR),
+		          sd->card_rsp_data >> 24));
+	}
+
+	return SUCCESS;
+}
+
+/*
+ * On entry: if single-block or non-block, buffer size <= block size.
+ * If multi-block, buffer size is unlimited.
+ * Question is how to handle the left-overs in either single- or multi-block.
+ * I think the caller should break the buffer up so this routine will always
+ * use block size == buffer size to handle the end piece of the buffer
+ */
+
+static int
+sdspi_card_buf(sdioh_info_t *sd, int rw, int func, bool fifo, uint32 addr, int nbytes, uint32 *data)
+{
+	int status;
+	uint32 cmd_arg;
+	uint32 rsp5;
+	int num_blocks, blocksize;
+	bool local_blockmode, local_dma;
+	bool read = rw == SDIOH_READ ? 1 : 0;
+
+	ASSERT(nbytes);
+
+	cmd_arg = 0;
+	sd_data(("%s: %s 53 func %d, %s, addr 0x%x, len %d bytes, r_cnt %d t_cnt %d\n",
+	         __FUNCTION__, read ? "Rd" : "Wr", func, fifo ? "FIXED" : "INCR",
+	         addr, nbytes, sd->r_cnt, sd->t_cnt));
+
+	if (read) sd->r_cnt++; else sd->t_cnt++;
+
+	local_blockmode = sd->sd_blockmode;
+	local_dma = sd->sd_use_dma;
+
+	/* Don't bother with block mode on small xfers */
+	if (nbytes < sd->client_block_size[func]) {
+		sd_info(("setting local blockmode to false: nbytes (%d) != block_size (%d)\n",
+		         nbytes, sd->client_block_size[func]));
+		local_blockmode = FALSE;
+		local_dma = FALSE;
+	}
+
+	if (local_blockmode) {
+		blocksize = MIN(sd->client_block_size[func], nbytes);
+		num_blocks = nbytes/blocksize;
+		cmd_arg = SFIELD(cmd_arg, CMD53_BYTE_BLK_CNT, num_blocks);
+		cmd_arg = SFIELD(cmd_arg, CMD53_BLK_MODE, 1);
+	} else {
+		num_blocks =  1;
+		blocksize = nbytes;
+		cmd_arg = SFIELD(cmd_arg, CMD53_BYTE_BLK_CNT, nbytes);
+		cmd_arg = SFIELD(cmd_arg, CMD53_BLK_MODE, 0);
+	}
+
+	if (fifo)
+		cmd_arg = SFIELD(cmd_arg, CMD53_OP_CODE, 0);
+	else
+		cmd_arg = SFIELD(cmd_arg, CMD53_OP_CODE, 1);
+
+	cmd_arg = SFIELD(cmd_arg, CMD53_FUNCTION, func);
+	cmd_arg = SFIELD(cmd_arg, CMD53_REG_ADDR, addr);
+	if (read)
+		cmd_arg = SFIELD(cmd_arg, CMD53_RW_FLAG, SDIOH_XFER_TYPE_READ);
+	else
+		cmd_arg = SFIELD(cmd_arg, CMD53_RW_FLAG, SDIOH_XFER_TYPE_WRITE);
+
+	sd->data_xfer_count = nbytes;
+	if ((func == 2) && (fifo == 1)) {
+		sd_data(("%s: %s 53 func %d, %s, addr 0x%x, len %d bytes, r_cnt %d t_cnt %d\n",
+		         __FUNCTION__, read ? "Rd" : "Wr", func, fifo ? "FIXED" : "INCR",
+		         addr, nbytes, sd->r_cnt, sd->t_cnt));
+	}
+
+	/* sdspi_cmd_issue() returns with the command complete bit
+	 * in the ISR already cleared
+	 */
+	if ((status = sdspi_cmd_issue(sd, local_dma,
+	                              SDIOH_CMD_53, cmd_arg,
+	                              data, nbytes)) != SUCCESS) {
+		sd_err(("%s: cmd_issue failed for %s\n", __FUNCTION__, (read ? "read" : "write")));
+		return status;
+	}
+
+	sdspi_cmd_getrsp(sd, &rsp5, 1);
+
+	if (rsp5 != 0x00) {
+		sd_err(("%s: rsp5 flags = 0x%x, expecting 0x00\n",
+		        __FUNCTION__,  rsp5));
+		return ERROR;
+	}
+
+	return SUCCESS;
+}
+
+static int
+set_client_block_size(sdioh_info_t *sd, int func, int block_size)
+{
+	int base;
+	int err = 0;
+
+	sd_err(("%s: Setting block size %d, func %d\n", __FUNCTION__, block_size, func));
+	sd->client_block_size[func] = block_size;
+
+	/* Set the block size in the SDIO Card register */
+	base = func * SDIOD_FBR_SIZE;
+	err = sdspi_card_regwrite(sd, 0, base + SDIOD_CCCR_BLKSIZE_0, 1, block_size & 0xff);
+	if (!err) {
+		err = sdspi_card_regwrite(sd, 0, base + SDIOD_CCCR_BLKSIZE_1, 1,
+		                          (block_size >> 8) & 0xff);
+	}
+
+	/*
+	 * Do not set the block size in the SDIO Host register; that
+	 * is func dependent and will get done on an individual
+	 * transaction basis.
+	 */
+
+	return (err ? BCME_SDIO_ERROR : 0);
+}
+
+/* Reset and re-initialize the device */
+int
+sdioh_sdio_reset(sdioh_info_t *si)
+{
+	si->card_init_done = FALSE;
+	return sdspi_client_init(si);
+}
+
+#define CRC7_POLYNOM	0x09
+#define CRC7_CRCHIGHBIT	0x40
+
+static uint8 sdspi_crc7(unsigned char* p, uint32 len)
+{
+	uint8 c, j, bit, crc = 0;
+	uint32 i;
+
+	for (i = 0; i < len; i++) {
+		c = *p++;
+		for (j = 0x80; j; j >>= 1) {
+			bit = crc & CRC7_CRCHIGHBIT;
+			crc <<= 1;
+			if (c & j) bit ^= CRC7_CRCHIGHBIT;
+			if (bit) crc ^= CRC7_POLYNOM;
+		}
+	}
+
+	/* Convert the CRC7 to an 8-bit SD CRC */
+	crc = (crc << 1) | 1;
+
+	return (crc);
+}
+
+#define CRC16_POLYNOM	0x1021
+#define CRC16_CRCHIGHBIT	0x8000
+
+static uint16 sdspi_crc16(unsigned char* p, uint32 len)
+{
+	uint32 i;
+	uint16 j, c, bit;
+	uint16 crc = 0;
+
+	for (i = 0; i < len; i++) {
+		c = *p++;
+		for (j = 0x80; j; j >>= 1) {
+			bit = crc & CRC16_CRCHIGHBIT;
+			crc <<= 1;
+			if (c & j) bit ^= CRC16_CRCHIGHBIT;
+			if (bit) crc ^= CRC16_POLYNOM;
+		}
+	}
+
+	return (crc);
+}
diff --git a/drivers/net/wireless/bcm4329/bcmsdspi_linux.c b/drivers/net/wireless/bcm4329/bcmsdspi_linux.c
new file mode 100644
index 0000000..e2e0ca6
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/bcmsdspi_linux.c
@@ -0,0 +1,252 @@
+/*
+ * Broadcom SPI Host Controller Driver - Linux Per-port
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmsdspi_linux.c,v 1.7.2.1.4.3 2008/06/30 21:09:36 Exp $
+ */
+
+#include <typedefs.h>
+#include <pcicfg.h>
+#include <bcmutils.h>
+
+#include <sdio.h>		/* SDIO Specs */
+#include <bcmsdbus.h>		/* bcmsdh to/from specific controller APIs */
+#include <sdiovar.h>		/* to get msglevel bit values */
+
+#include <linux/sched.h>	/* request_irq(), free_irq() */
+
+#include <bcmsdspi.h>
+#include <bcmspi.h>
+
+extern uint sd_crc;
+module_param(sd_crc, uint, 0);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+#define KERNEL26
+#endif
+
+struct sdos_info {
+	sdioh_info_t *sd;
+	spinlock_t lock;
+	wait_queue_head_t intr_wait_queue;
+};
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+#define BLOCKABLE()	(!in_atomic())
+#else
+#define BLOCKABLE()	(!in_interrupt())
+#endif
+
+/* Interrupt handler */
+static irqreturn_t
+sdspi_isr(int irq, void *dev_id
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+, struct pt_regs *ptregs
+#endif
+)
+{
+	sdioh_info_t *sd;
+	struct sdos_info *sdos;
+	bool ours;
+
+	sd = (sdioh_info_t *)dev_id;
+	sd->local_intrcount++;
+
+	if (!sd->card_init_done) {
+		sd_err(("%s: Hey Bogus intr...not even initted: irq %d\n", __FUNCTION__, irq));
+		return IRQ_RETVAL(FALSE);
+	} else {
+		ours = spi_check_client_intr(sd, NULL);
+
+		/* For local interrupts, wake the waiting process */
+		if (ours && sd->got_hcint) {
+			sdos = (struct sdos_info *)sd->sdos_info;
+			wake_up_interruptible(&sdos->intr_wait_queue);
+		}
+
+		return IRQ_RETVAL(ours);
+	}
+}
+
+/* Register with Linux for interrupts */
+int
+spi_register_irq(sdioh_info_t *sd, uint irq)
+{
+	sd_trace(("Entering %s: irq == %d\n", __FUNCTION__, irq));
+	if (request_irq(irq, sdspi_isr, IRQF_SHARED, "bcmsdspi", sd) < 0) {
+		sd_err(("%s: request_irq() failed\n", __FUNCTION__));
+		return ERROR;
+	}
+	return SUCCESS;
+}
+
+/* Free Linux irq */
+void
+spi_free_irq(uint irq, sdioh_info_t *sd)
+{
+	free_irq(irq, sd);
+}
+
+/* Map Host controller registers */
+
+uint32 *
+spi_reg_map(osl_t *osh, uintptr addr, int size)
+{
+	return (uint32 *)REG_MAP(addr, size);
+}
+
+void
+spi_reg_unmap(osl_t *osh, uintptr addr, int size)
+{
+	REG_UNMAP((void*)(uintptr)addr);
+}
+
+int
+spi_osinit(sdioh_info_t *sd)
+{
+	struct sdos_info *sdos;
+
+	sdos = (struct sdos_info*)MALLOC(sd->osh, sizeof(struct sdos_info));
+	sd->sdos_info = (void*)sdos;
+	if (sdos == NULL)
+		return BCME_NOMEM;
+
+	sdos->sd = sd;
+	spin_lock_init(&sdos->lock);
+	init_waitqueue_head(&sdos->intr_wait_queue);
+	return BCME_OK;
+}
+
+void
+spi_osfree(sdioh_info_t *sd)
+{
+	struct sdos_info *sdos;
+	ASSERT(sd && sd->sdos_info);
+
+	sdos = (struct sdos_info *)sd->sdos_info;
+	MFREE(sd->osh, sdos, sizeof(struct sdos_info));
+}
+
+/* Interrupt enable/disable */
+SDIOH_API_RC
+sdioh_interrupt_set(sdioh_info_t *sd, bool enable)
+{
+	ulong flags;
+	struct sdos_info *sdos;
+
+	sd_trace(("%s: %s\n", __FUNCTION__, enable ? "Enabling" : "Disabling"));
+
+	sdos = (struct sdos_info *)sd->sdos_info;
+	ASSERT(sdos);
+
+	if (!(sd->host_init_done && sd->card_init_done)) {
+		sd_err(("%s: Card & Host are not initted - bailing\n", __FUNCTION__));
+		return SDIOH_API_RC_FAIL;
+	}
+
+	if (enable && !(sd->intr_handler && sd->intr_handler_arg)) {
+		sd_err(("%s: no handler registered, will not enable\n", __FUNCTION__));
+		return SDIOH_API_RC_FAIL;
+	}
+
+	/* Ensure atomicity for enable/disable calls */
+	spin_lock_irqsave(&sdos->lock, flags);
+
+	sd->client_intr_enabled = enable;
+	if (enable && !sd->lockcount)
+		spi_devintr_on(sd);
+	else
+		spi_devintr_off(sd);
+
+	spin_unlock_irqrestore(&sdos->lock, flags);
+
+	return SDIOH_API_RC_SUCCESS;
+}
+
+/* Protect against reentrancy (disable device interrupts while executing) */
+void
+spi_lock(sdioh_info_t *sd)
+{
+	ulong flags;
+	struct sdos_info *sdos;
+
+	sdos = (struct sdos_info *)sd->sdos_info;
+	ASSERT(sdos);
+
+	sd_trace(("%s: %d\n", __FUNCTION__, sd->lockcount));
+
+	spin_lock_irqsave(&sdos->lock, flags);
+	if (sd->lockcount) {
+		sd_err(("%s: Already locked!\n", __FUNCTION__));
+		ASSERT(sd->lockcount == 0);
+	}
+	spi_devintr_off(sd);
+	sd->lockcount++;
+	spin_unlock_irqrestore(&sdos->lock, flags);
+}
+
+/* Enable client interrupt */
+void
+spi_unlock(sdioh_info_t *sd)
+{
+	ulong flags;
+	struct sdos_info *sdos;
+
+	sd_trace(("%s: %d, %d\n", __FUNCTION__, sd->lockcount, sd->client_intr_enabled));
+	ASSERT(sd->lockcount > 0);
+
+	sdos = (struct sdos_info *)sd->sdos_info;
+	ASSERT(sdos);
+
+	spin_lock_irqsave(&sdos->lock, flags);
+	if (--sd->lockcount == 0 && sd->client_intr_enabled) {
+		spi_devintr_on(sd);
+	}
+	spin_unlock_irqrestore(&sdos->lock, flags);
+}
+
+void spi_waitbits(sdioh_info_t *sd, bool yield)
+{
+	struct sdos_info *sdos;
+
+	sdos = (struct sdos_info *)sd->sdos_info;
+
+#ifndef BCMSDYIELD
+	ASSERT(!yield);
+#endif
+	sd_trace(("%s: yield %d canblock %d\n",
+	          __FUNCTION__, yield, BLOCKABLE()));
+
+	/* Clear the "interrupt happened" flag and last intrstatus */
+	sd->got_hcint = FALSE;
+
+#ifdef BCMSDYIELD
+	if (yield && BLOCKABLE()) {
+		/* Wait for the indication, the interrupt will be masked when the ISR fires. */
+		wait_event_interruptible(sdos->intr_wait_queue, (sd->got_hcint));
+	} else
+#endif /* BCMSDYIELD */
+	{
+		spi_spinbits(sd);
+	}
+
+}
diff --git a/drivers/net/wireless/bcm4329/bcmsdstd.c b/drivers/net/wireless/bcm4329/bcmsdstd.c
new file mode 100644
index 0000000..0ca1f8f
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/bcmsdstd.c
@@ -0,0 +1,3127 @@
+/*
+ *  'Standard' SDIO HOST CONTROLLER driver
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmsdstd.c,v 1.64.4.1.4.4.2.18 2010/08/17 17:00:48 Exp $
+ */
+
+#include <typedefs.h>
+
+#include <bcmdevs.h>
+#include <bcmendian.h>
+#include <bcmutils.h>
+#include <osl.h>
+#include <siutils.h>
+#include <sdio.h>	/* SDIO Device and Protocol Specs */
+#include <sdioh.h>	/* SDIO Host Controller Specification */
+#include <bcmsdbus.h>	/* bcmsdh to/from specific controller APIs */
+#include <sdiovar.h>	/* ioctl/iovars */
+#include <pcicfg.h>
+
+
+#define SD_PAGE_BITS	12
+#define SD_PAGE 	(1 << SD_PAGE_BITS)
+
+#include <bcmsdstd.h>
+
+/* Globals */
+uint sd_msglevel = SDH_ERROR_VAL;
+uint sd_hiok = TRUE;			/* Use hi-speed mode if available? */
+uint sd_sdmode = SDIOH_MODE_SD4;	/* Use SD4 mode by default */
+uint sd_f2_blocksize = 64;		/* Default blocksize */
+
+#ifdef BCMSDYIELD
+bool sd_yieldcpu = TRUE;		/* Allow CPU yielding for buffer requests */
+uint sd_minyield = 0;			/* Minimum xfer size to allow CPU yield */
+bool sd_forcerb = FALSE;		/* Force sync readback in intrs_on/off */
+#endif
+
+uint sd_divisor = 2;			/* Default 48MHz/2 = 24MHz */
+
+uint sd_power = 1;		/* Default to SD Slot powered ON */
+uint sd_clock = 1;		/* Default to SD Clock turned ON */
+uint sd_pci_slot = 0xFFFFffff; /* Used to force selection of a particular PCI slot */
+uint8 sd_dma_mode = DMA_MODE_SDMA; /* Default to SDMA for now */
+
+uint sd_toctl = 7;
+
+static bool trap_errs = FALSE;
+
+static const char *dma_mode_description[] = { "PIO", "SDMA", "ADMA1", "32b ADMA2", "64b ADMA2" };
+
+/* Prototypes */
+static bool sdstd_start_clock(sdioh_info_t *sd, uint16 divisor);
+static bool sdstd_start_power(sdioh_info_t *sd);
+static bool sdstd_bus_width(sdioh_info_t *sd, int width);
+static int sdstd_set_highspeed_mode(sdioh_info_t *sd, bool HSMode);
+static int sdstd_set_dma_mode(sdioh_info_t *sd, int8 dma_mode);
+static int sdstd_card_enablefuncs(sdioh_info_t *sd);
+static void sdstd_cmd_getrsp(sdioh_info_t *sd, uint32 *rsp_buffer, int count);
+static int sdstd_cmd_issue(sdioh_info_t *sd, bool use_dma, uint32 cmd, uint32 arg);
+static int sdstd_card_regread(sdioh_info_t *sd, int func, uint32 regaddr,
+                              int regsize, uint32 *data);
+static int sdstd_card_regwrite(sdioh_info_t *sd, int func, uint32 regaddr,
+                               int regsize, uint32 data);
+static int sdstd_driver_init(sdioh_info_t *sd);
+static bool sdstd_reset(sdioh_info_t *sd, bool host_reset, bool client_reset);
+static int sdstd_card_buf(sdioh_info_t *sd, int rw, int func, bool fifo,
+                          uint32 addr, int nbytes, uint32 *data);
+static int sdstd_abort(sdioh_info_t *sd, uint func);
+static int sdstd_check_errs(sdioh_info_t *sdioh_info, uint32 cmd, uint32 arg);
+static int set_client_block_size(sdioh_info_t *sd, int func, int blocksize);
+static void sd_map_dma(sdioh_info_t * sd);
+static void sd_unmap_dma(sdioh_info_t * sd);
+static void sd_clear_adma_dscr_buf(sdioh_info_t *sd);
+static void sd_fill_dma_data_buf(sdioh_info_t *sd, uint8 data);
+static void sd_create_adma_descriptor(sdioh_info_t *sd,
+                                      uint32 index, uint32 addr_phys,
+                                      uint16 length, uint16 flags);
+static void sd_dump_adma_dscr(sdioh_info_t *sd);
+static void sdstd_dumpregs(sdioh_info_t *sd);
+
+
+/*
+ * Private register access routines.
+ */
+
+/* 16 bit PCI regs */
+
+extern uint16 sdstd_rreg16(sdioh_info_t *sd, uint reg);
+uint16
+sdstd_rreg16(sdioh_info_t *sd, uint reg)
+{
+
+	volatile uint16 data = *(volatile uint16 *)(sd->mem_space + reg);
+	sd_ctrl(("16: R Reg 0x%02x, Data 0x%x\n", reg, data));
+	return data;
+}
+
+extern void sdstd_wreg16(sdioh_info_t *sd, uint reg, uint16 data);
+void
+sdstd_wreg16(sdioh_info_t *sd, uint reg, uint16 data)
+{
+	*(volatile uint16 *)(sd->mem_space + reg) = (uint16)data;
+	sd_ctrl(("16: W Reg 0x%02x, Data 0x%x\n", reg, data));
+}
+
+static void
+sdstd_or_reg16(sdioh_info_t *sd, uint reg, uint16 val)
+{
+	volatile uint16 data = *(volatile uint16 *)(sd->mem_space + reg);
+	sd_ctrl(("16: OR Reg 0x%02x, Val 0x%x\n", reg, val));
+	data |= val;
+	*(volatile uint16 *)(sd->mem_space + reg) = (uint16)data;
+
+}
+static void
+sdstd_mod_reg16(sdioh_info_t *sd, uint reg, int16 mask, uint16 val)
+{
+
+	volatile uint16 data = *(volatile uint16 *)(sd->mem_space + reg);
+	sd_ctrl(("16: MOD Reg 0x%02x, Mask 0x%x, Val 0x%x\n", reg, mask, val));
+	data &= ~mask;
+	data |= (val & mask);
+	*(volatile uint16 *)(sd->mem_space + reg) = (uint16)data;
+}
+
+
+/* 32 bit PCI regs */
+static uint32
+sdstd_rreg(sdioh_info_t *sd, uint reg)
+{
+	volatile uint32 data = *(volatile uint32 *)(sd->mem_space + reg);
+	sd_ctrl(("32: R Reg 0x%02x, Data 0x%x\n", reg, data));
+	return data;
+}
+static inline void
+sdstd_wreg(sdioh_info_t *sd, uint reg, uint32 data)
+{
+	*(volatile uint32 *)(sd->mem_space + reg) = (uint32)data;
+	sd_ctrl(("32: W Reg 0x%02x, Data 0x%x\n", reg, data));
+
+}
+
+/* 8 bit PCI regs */
+static inline void
+sdstd_wreg8(sdioh_info_t *sd, uint reg, uint8 data)
+{
+	*(volatile uint8 *)(sd->mem_space + reg) = (uint8)data;
+	sd_ctrl(("08: W Reg 0x%02x, Data 0x%x\n", reg, data));
+}
+static uint8
+sdstd_rreg8(sdioh_info_t *sd, uint reg)
+{
+	volatile uint8 data = *(volatile uint8 *)(sd->mem_space + reg);
+	sd_ctrl(("08: R Reg 0x%02x, Data 0x%x\n", reg, data));
+	return data;
+}
+
+/*
+ * Private work routines
+ */
+
+sdioh_info_t *glob_sd;
+
+/*
+ *  Public entry points & extern's
+ */
+extern sdioh_info_t *
+sdioh_attach(osl_t *osh, void *bar0, uint irq)
+{
+	sdioh_info_t *sd;
+
+	sd_trace(("%s\n", __FUNCTION__));
+	if ((sd = (sdioh_info_t *)MALLOC(osh, sizeof(sdioh_info_t))) == NULL) {
+		sd_err(("sdioh_attach: out of memory, malloced %d bytes\n", MALLOCED(osh)));
+		return NULL;
+	}
+	bzero((char *)sd, sizeof(sdioh_info_t));
+	glob_sd = sd;
+	sd->osh = osh;
+	if (sdstd_osinit(sd) != 0) {
+		sd_err(("%s:sdstd_osinit() failed\n", __FUNCTION__));
+		MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+		return NULL;
+	}
+	sd->mem_space = (volatile char *)sdstd_reg_map(osh, (uintptr)bar0, SDIOH_REG_WINSZ);
+	sd_init_dma(sd);
+	sd->irq = irq;
+	if (sd->mem_space == NULL) {
+		sd_err(("%s:ioremap() failed\n", __FUNCTION__));
+		sdstd_osfree(sd);
+		MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+		return NULL;
+	}
+	sd_info(("%s:sd->mem_space = %p\n", __FUNCTION__, sd->mem_space));
+	sd->intr_handler = NULL;
+	sd->intr_handler_arg = NULL;
+	sd->intr_handler_valid = FALSE;
+
+	/* Set defaults */
+	sd->sd_blockmode = TRUE;
+	sd->use_client_ints = TRUE;
+	sd->sd_dma_mode = sd_dma_mode;
+
+	if (!sd->sd_blockmode)
+		sd->sd_dma_mode = DMA_MODE_NONE;
+
+	if (sdstd_driver_init(sd) != SUCCESS) {
+		/* If host CPU was reset without resetting SD bus or
+		   SD device, the device will still have its RCA but
+		   driver no longer knows what it is (since driver has been restarted).
+		   go through once to clear the RCA and a gain reassign it.
+		 */
+		sd_info(("driver_init failed - Reset RCA and try again\n"));
+		if (sdstd_driver_init(sd) != SUCCESS) {
+			sd_err(("%s:driver_init() failed()\n", __FUNCTION__));
+			if (sd->mem_space) {
+				sdstd_reg_unmap(osh, (uintptr)sd->mem_space, SDIOH_REG_WINSZ);
+				sd->mem_space = NULL;
+			}
+			sdstd_osfree(sd);
+			MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+			return (NULL);
+		}
+	}
+
+	OSL_DMADDRWIDTH(osh, 32);
+
+	/* Always map DMA buffers, so we can switch between DMA modes. */
+	sd_map_dma(sd);
+
+	if (sdstd_register_irq(sd, irq) != SUCCESS) {
+		sd_err(("%s: sdstd_register_irq() failed for irq = %d\n", __FUNCTION__, irq));
+		sdstd_free_irq(sd->irq, sd);
+		if (sd->mem_space) {
+			sdstd_reg_unmap(osh, (uintptr)sd->mem_space, SDIOH_REG_WINSZ);
+			sd->mem_space = NULL;
+		}
+
+		sdstd_osfree(sd);
+		MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+		return (NULL);
+	}
+
+	sd_trace(("%s: Done\n", __FUNCTION__));
+	return sd;
+}
+
+extern SDIOH_API_RC
+sdioh_detach(osl_t *osh, sdioh_info_t *sd)
+{
+	sd_trace(("%s\n", __FUNCTION__));
+	if (sd) {
+		sd_unmap_dma(sd);
+		sdstd_wreg16(sd, SD_IntrSignalEnable, 0);
+		sd_trace(("%s: freeing irq %d\n", __FUNCTION__, sd->irq));
+		sdstd_free_irq(sd->irq, sd);
+		if (sd->card_init_done)
+			sdstd_reset(sd, 1, 1);
+		if (sd->mem_space) {
+			sdstd_reg_unmap(osh, (uintptr)sd->mem_space, SDIOH_REG_WINSZ);
+			sd->mem_space = NULL;
+		}
+
+		sdstd_osfree(sd);
+		MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+	}
+	return SDIOH_API_RC_SUCCESS;
+}
+
+/* Configure callback to client when we receive client interrupt */
+extern SDIOH_API_RC
+sdioh_interrupt_register(sdioh_info_t *sd, sdioh_cb_fn_t fn, void *argh)
+{
+	sd_trace(("%s: Entering\n", __FUNCTION__));
+	sd->intr_handler = fn;
+	sd->intr_handler_arg = argh;
+	sd->intr_handler_valid = TRUE;
+	return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_interrupt_deregister(sdioh_info_t *sd)
+{
+	sd_trace(("%s: Entering\n", __FUNCTION__));
+	sd->intr_handler_valid = FALSE;
+	sd->intr_handler = NULL;
+	sd->intr_handler_arg = NULL;
+	return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_interrupt_query(sdioh_info_t *sd, bool *onoff)
+{
+	sd_trace(("%s: Entering\n", __FUNCTION__));
+	*onoff = sd->client_intr_enabled;
+	return SDIOH_API_RC_SUCCESS;
+}
+
+#if defined(DHD_DEBUG)
+extern bool
+sdioh_interrupt_pending(sdioh_info_t *sd)
+{
+	uint16 intrstatus;
+	intrstatus = sdstd_rreg16(sd, SD_IntrStatus);
+	return !!(intrstatus & CLIENT_INTR);
+}
+#endif
+
+uint
+sdioh_query_iofnum(sdioh_info_t *sd)
+{
+	return sd->num_funcs;
+}
+
+/* IOVar table */
+enum {
+	IOV_MSGLEVEL = 1,
+	IOV_BLOCKMODE,
+	IOV_BLOCKSIZE,
+	IOV_DMA,
+	IOV_USEINTS,
+	IOV_NUMINTS,
+	IOV_NUMLOCALINTS,
+	IOV_HOSTREG,
+	IOV_DEVREG,
+	IOV_DIVISOR,
+	IOV_SDMODE,
+	IOV_HISPEED,
+	IOV_HCIREGS,
+	IOV_POWER,
+	IOV_YIELDCPU,
+	IOV_MINYIELD,
+	IOV_FORCERB,
+	IOV_CLOCK
+};
+
+const bcm_iovar_t sdioh_iovars[] = {
+	{"sd_msglevel",	IOV_MSGLEVEL, 	0,	IOVT_UINT32,	0 },
+	{"sd_blockmode", IOV_BLOCKMODE,	0,	IOVT_BOOL,	0 },
+	{"sd_blocksize", IOV_BLOCKSIZE, 0,	IOVT_UINT32,	0 }, /* ((fn << 16) | size) */
+	{"sd_dma",	IOV_DMA,	0,	IOVT_UINT32,	0 },
+#ifdef BCMSDYIELD
+	{"sd_yieldcpu",	IOV_YIELDCPU,	0,	IOVT_BOOL,	0 },
+	{"sd_minyield",	IOV_MINYIELD,	0,	IOVT_UINT32,	0 },
+	{"sd_forcerb",	IOV_FORCERB,	0,	IOVT_BOOL,	0 },
+#endif
+	{"sd_ints",	IOV_USEINTS,	0,	IOVT_BOOL,	0 },
+	{"sd_numints",	IOV_NUMINTS,	0,	IOVT_UINT32,	0 },
+	{"sd_numlocalints", IOV_NUMLOCALINTS, 0, IOVT_UINT32,	0 },
+	{"sd_hostreg",	IOV_HOSTREG,	0,	IOVT_BUFFER,	sizeof(sdreg_t) },
+	{"sd_devreg",	IOV_DEVREG,	0,	IOVT_BUFFER,	sizeof(sdreg_t)	},
+	{"sd_divisor",	IOV_DIVISOR,	0,	IOVT_UINT32,	0 },
+	{"sd_power",	IOV_POWER,	0,	IOVT_UINT32,	0 },
+	{"sd_clock",	IOV_CLOCK,	0,	IOVT_UINT32,	0 },
+	{"sd_mode",	IOV_SDMODE,	0,	IOVT_UINT32,	100},
+	{"sd_highspeed",	IOV_HISPEED,	0,	IOVT_UINT32,	0},
+	{NULL, 0, 0, 0, 0 }
+};
+
+int
+sdioh_iovar_op(sdioh_info_t *si, const char *name,
+               void *params, int plen, void *arg, int len, bool set)
+{
+	const bcm_iovar_t *vi = NULL;
+	int bcmerror = 0;
+	int val_size;
+	int32 int_val = 0;
+	bool bool_val;
+	uint32 actionid;
+
+	ASSERT(name);
+	ASSERT(len >= 0);
+
+	/* Get must have return space; Set does not take qualifiers */
+	ASSERT(set || (arg && len));
+	ASSERT(!set || (!params && !plen));
+
+	sd_trace(("%s: Enter (%s %s)\n", __FUNCTION__, (set ? "set" : "get"), name));
+
+	if ((vi = bcm_iovar_lookup(sdioh_iovars, name)) == NULL) {
+		bcmerror = BCME_UNSUPPORTED;
+		goto exit;
+	}
+
+	if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, set)) != 0)
+		goto exit;
+
+	/* Set up params so get and set can share the convenience variables */
+	if (params == NULL) {
+		params = arg;
+		plen = len;
+	}
+
+	if (vi->type == IOVT_VOID)
+		val_size = 0;
+	else if (vi->type == IOVT_BUFFER)
+		val_size = len;
+	else
+		val_size = sizeof(int);
+
+	if (plen >= (int)sizeof(int_val))
+		bcopy(params, &int_val, sizeof(int_val));
+
+	bool_val = (int_val != 0) ? TRUE : FALSE;
+
+	actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid);
+	switch (actionid) {
+	case IOV_GVAL(IOV_MSGLEVEL):
+		int_val = (int32)sd_msglevel;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_MSGLEVEL):
+		sd_msglevel = int_val;
+		break;
+
+	case IOV_GVAL(IOV_BLOCKMODE):
+		int_val = (int32)si->sd_blockmode;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_BLOCKMODE):
+		si->sd_blockmode = (bool)int_val;
+		/* Haven't figured out how to make non-block mode with DMA */
+		if (!si->sd_blockmode)
+			si->sd_dma_mode = DMA_MODE_NONE;
+		break;
+
+#ifdef BCMSDYIELD
+	case IOV_GVAL(IOV_YIELDCPU):
+		int_val = sd_yieldcpu;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_YIELDCPU):
+		sd_yieldcpu = (bool)int_val;
+		break;
+
+	case IOV_GVAL(IOV_MINYIELD):
+		int_val = sd_minyield;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_MINYIELD):
+		sd_minyield = (bool)int_val;
+		break;
+
+	case IOV_GVAL(IOV_FORCERB):
+		int_val = sd_forcerb;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_FORCERB):
+		sd_forcerb = (bool)int_val;
+		break;
+#endif /* BCMSDYIELD */
+
+	case IOV_GVAL(IOV_BLOCKSIZE):
+		if ((uint32)int_val > si->num_funcs) {
+			bcmerror = BCME_BADARG;
+			break;
+		}
+		int_val = (int32)si->client_block_size[int_val];
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_BLOCKSIZE):
+	{
+		uint func = ((uint32)int_val >> 16);
+		uint blksize = (uint16)int_val;
+		uint maxsize;
+
+		if (func > si->num_funcs) {
+			bcmerror = BCME_BADARG;
+			break;
+		}
+
+		switch (func) {
+		case 0: maxsize = 32; break;
+		case 1: maxsize = BLOCK_SIZE_4318; break;
+		case 2: maxsize = BLOCK_SIZE_4328; break;
+		default: maxsize = 0;
+		}
+		if (blksize > maxsize) {
+			bcmerror = BCME_BADARG;
+			break;
+		}
+		if (!blksize) {
+			blksize = maxsize;
+		}
+
+		/* Now set it */
+		sdstd_lock(si);
+		bcmerror = set_client_block_size(si, func, blksize);
+		sdstd_unlock(si);
+		break;
+	}
+
+	case IOV_GVAL(IOV_DMA):
+		int_val = (int32)si->sd_dma_mode;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_DMA):
+		si->sd_dma_mode = (char)int_val;
+		sdstd_set_dma_mode(si, si->sd_dma_mode);
+		break;
+
+	case IOV_GVAL(IOV_USEINTS):
+		int_val = (int32)si->use_client_ints;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_USEINTS):
+		si->use_client_ints = (bool)int_val;
+		if (si->use_client_ints)
+			si->intmask |= CLIENT_INTR;
+		else
+			si->intmask &= ~CLIENT_INTR;
+		break;
+
+	case IOV_GVAL(IOV_DIVISOR):
+		int_val = (uint32)sd_divisor;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_DIVISOR):
+		sd_divisor = int_val;
+		if (!sdstd_start_clock(si, (uint16)sd_divisor)) {
+			sd_err(("set clock failed!\n"));
+			bcmerror = BCME_ERROR;
+		}
+		break;
+
+	case IOV_GVAL(IOV_POWER):
+		int_val = (uint32)sd_power;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_POWER):
+		sd_power = int_val;
+		if (sd_power == 1) {
+			if (sdstd_driver_init(si) != SUCCESS) {
+				sd_err(("set SD Slot power failed!\n"));
+				bcmerror = BCME_ERROR;
+			} else {
+				sd_err(("SD Slot Powered ON.\n"));
+			}
+		} else {
+			uint8 pwr = 0;
+
+			pwr = SFIELD(pwr, PWR_BUS_EN, 0);
+			sdstd_wreg8(si, SD_PwrCntrl, pwr); /* Set Voltage level */
+			sd_err(("SD Slot Powered OFF.\n"));
+		}
+		break;
+
+	case IOV_GVAL(IOV_CLOCK):
+		int_val = (uint32)sd_clock;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_CLOCK):
+		sd_clock = int_val;
+		if (sd_clock == 1) {
+			sd_info(("SD Clock turned ON.\n"));
+			if (!sdstd_start_clock(si, (uint16)sd_divisor)) {
+				sd_err(("sdstd_start_clock failed\n"));
+				bcmerror = BCME_ERROR;
+			}
+		} else {
+			/* turn off HC clock */
+			sdstd_wreg16(si, SD_ClockCntrl,
+			             sdstd_rreg16(si, SD_ClockCntrl) & ~((uint16)0x4));
+
+			sd_info(("SD Clock turned OFF.\n"));
+		}
+		break;
+
+	case IOV_GVAL(IOV_SDMODE):
+		int_val = (uint32)sd_sdmode;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_SDMODE):
+		sd_sdmode = int_val;
+
+		if (!sdstd_bus_width(si, sd_sdmode)) {
+			sd_err(("sdstd_bus_width failed\n"));
+			bcmerror = BCME_ERROR;
+		}
+		break;
+
+	case IOV_GVAL(IOV_HISPEED):
+		int_val = (uint32)sd_hiok;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_HISPEED):
+		sd_hiok = int_val;
+		bcmerror = sdstd_set_highspeed_mode(si, (bool)sd_hiok);
+		break;
+
+	case IOV_GVAL(IOV_NUMINTS):
+		int_val = (int32)si->intrcount;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_GVAL(IOV_NUMLOCALINTS):
+		int_val = (int32)si->local_intrcount;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_GVAL(IOV_HOSTREG):
+	{
+		sdreg_t *sd_ptr = (sdreg_t *)params;
+
+		if (sd_ptr->offset < SD_SysAddr || sd_ptr->offset > SD_MaxCurCap) {
+			sd_err(("%s: bad offset 0x%x\n", __FUNCTION__, sd_ptr->offset));
+			bcmerror = BCME_BADARG;
+			break;
+		}
+
+		sd_trace(("%s: rreg%d at offset %d\n", __FUNCTION__,
+		          (sd_ptr->offset & 1) ? 8 : ((sd_ptr->offset & 2) ? 16 : 32),
+		          sd_ptr->offset));
+		if (sd_ptr->offset & 1)
+			int_val = sdstd_rreg8(si, sd_ptr->offset);
+		else if (sd_ptr->offset & 2)
+			int_val = sdstd_rreg16(si, sd_ptr->offset);
+		else
+			int_val = sdstd_rreg(si, sd_ptr->offset);
+
+		bcopy(&int_val, arg, sizeof(int_val));
+		break;
+	}
+
+	case IOV_SVAL(IOV_HOSTREG):
+	{
+		sdreg_t *sd_ptr = (sdreg_t *)params;
+
+		if (sd_ptr->offset < SD_SysAddr || sd_ptr->offset > SD_MaxCurCap) {
+			sd_err(("%s: bad offset 0x%x\n", __FUNCTION__, sd_ptr->offset));
+			bcmerror = BCME_BADARG;
+			break;
+		}
+
+		sd_trace(("%s: wreg%d value 0x%08x at offset %d\n", __FUNCTION__, sd_ptr->value,
+		          (sd_ptr->offset & 1) ? 8 : ((sd_ptr->offset & 2) ? 16 : 32),
+		          sd_ptr->offset));
+		if (sd_ptr->offset & 1)
+			sdstd_wreg8(si, sd_ptr->offset, (uint8)sd_ptr->value);
+		else if (sd_ptr->offset & 2)
+			sdstd_wreg16(si, sd_ptr->offset, (uint16)sd_ptr->value);
+		else
+			sdstd_wreg(si, sd_ptr->offset, (uint32)sd_ptr->value);
+
+		break;
+	}
+
+	case IOV_GVAL(IOV_DEVREG):
+	{
+		sdreg_t *sd_ptr = (sdreg_t *)params;
+		uint8 data;
+
+		if (sdioh_cfg_read(si, sd_ptr->func, sd_ptr->offset, &data)) {
+			bcmerror = BCME_SDIO_ERROR;
+			break;
+		}
+
+		int_val = (int)data;
+		bcopy(&int_val, arg, sizeof(int_val));
+		break;
+	}
+
+	case IOV_SVAL(IOV_DEVREG):
+	{
+		sdreg_t *sd_ptr = (sdreg_t *)params;
+		uint8 data = (uint8)sd_ptr->value;
+
+		if (sdioh_cfg_write(si, sd_ptr->func, sd_ptr->offset, &data)) {
+			bcmerror = BCME_SDIO_ERROR;
+			break;
+		}
+		break;
+	}
+
+
+	default:
+		bcmerror = BCME_UNSUPPORTED;
+		break;
+	}
+exit:
+
+	return bcmerror;
+}
+
+extern SDIOH_API_RC
+sdioh_cfg_read(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data)
+{
+	SDIOH_API_RC status;
+	/* No lock needed since sdioh_request_byte does locking */
+	status = sdioh_request_byte(sd, SDIOH_READ, fnc_num, addr, data);
+	return status;
+}
+
+extern SDIOH_API_RC
+sdioh_cfg_write(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data)
+{
+	/* No lock needed since sdioh_request_byte does locking */
+	SDIOH_API_RC status;
+	status = sdioh_request_byte(sd, SDIOH_WRITE, fnc_num, addr, data);
+	return status;
+}
+
+extern SDIOH_API_RC
+sdioh_cis_read(sdioh_info_t *sd, uint func, uint8 *cisd, uint32 length)
+{
+	uint32 count;
+	int offset;
+	uint32 foo;
+	uint8 *cis = cisd;
+
+	sd_trace(("%s: Func = %d\n", __FUNCTION__, func));
+
+	if (!sd->func_cis_ptr[func]) {
+		bzero(cis, length);
+		return SDIOH_API_RC_FAIL;
+	}
+
+	sdstd_lock(sd);
+	*cis = 0;
+	for (count = 0; count < length; count++) {
+		offset =  sd->func_cis_ptr[func] + count;
+		if (sdstd_card_regread(sd, 0, offset, 1, &foo)) {
+			sd_err(("%s: regread failed: Can't read CIS\n", __FUNCTION__));
+			sdstd_unlock(sd);
+			return SDIOH_API_RC_FAIL;
+		}
+		*cis = (uint8)(foo & 0xff);
+		cis++;
+	}
+	sdstd_unlock(sd);
+	return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_request_byte(sdioh_info_t *sd, uint rw, uint func, uint regaddr, uint8 *byte)
+{
+	int status;
+	uint32 cmd_arg;
+	uint32 rsp5;
+
+	sdstd_lock(sd);
+	cmd_arg = 0;
+	cmd_arg = SFIELD(cmd_arg, CMD52_FUNCTION, func);
+	cmd_arg = SFIELD(cmd_arg, CMD52_REG_ADDR, regaddr);
+	cmd_arg = SFIELD(cmd_arg, CMD52_RW_FLAG, rw == SDIOH_READ ? 0 : 1);
+	cmd_arg = SFIELD(cmd_arg, CMD52_RAW, 0);
+	cmd_arg = SFIELD(cmd_arg, CMD52_DATA, rw == SDIOH_READ ? 0 : *byte);
+
+	if ((status = sdstd_cmd_issue(sd, USE_DMA(sd), SDIOH_CMD_52, cmd_arg)) != SUCCESS) {
+		sdstd_unlock(sd);
+		return status;
+	}
+
+	sdstd_cmd_getrsp(sd, &rsp5, 1);
+	if (sdstd_rreg16 (sd, SD_ErrorIntrStatus) != 0) {
+		sd_err(("%s: 1: ErrorintrStatus 0x%x\n",
+		        __FUNCTION__, sdstd_rreg16(sd, SD_ErrorIntrStatus)));
+	}
+	if (GFIELD(rsp5, RSP5_FLAGS) != 0x10)
+		sd_err(("%s: rsp5 flags is 0x%x\t %d\n",
+		        __FUNCTION__, GFIELD(rsp5, RSP5_FLAGS), func));
+
+	if (GFIELD(rsp5, RSP5_STUFF))
+		sd_err(("%s: rsp5 stuff is 0x%x: should be 0\n",
+		        __FUNCTION__, GFIELD(rsp5, RSP5_STUFF)));
+
+	if (rw == SDIOH_READ)
+		*byte = GFIELD(rsp5, RSP5_DATA);
+
+	sdstd_unlock(sd);
+	return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_request_word(sdioh_info_t *sd, uint cmd_type, uint rw, uint func, uint addr,
+                   uint32 *word, uint nbytes)
+{
+	int status;
+	bool swap = FALSE;
+
+	sdstd_lock(sd);
+
+	if (rw == SDIOH_READ) {
+		status = sdstd_card_regread(sd, func, addr, nbytes, word);
+		if (swap)
+			*word = BCMSWAP32(*word);
+	} else {
+		if (swap)
+			*word = BCMSWAP32(*word);
+		status = sdstd_card_regwrite(sd, func, addr, nbytes, *word);
+	}
+
+	sdstd_unlock(sd);
+	return (status == SUCCESS ?  SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL);
+}
+
+extern SDIOH_API_RC
+sdioh_request_buffer(sdioh_info_t *sd, uint pio_dma, uint fix_inc, uint rw, uint func,
+                     uint addr, uint reg_width, uint buflen_u, uint8 *buffer, void *pkt)
+{
+	int len;
+	int buflen = (int)buflen_u;
+	bool fifo = (fix_inc == SDIOH_DATA_FIX);
+	uint8 *localbuf = NULL, *tmpbuf = NULL;
+	uint tmplen = 0;
+	bool local_blockmode = sd->sd_blockmode;
+
+	sdstd_lock(sd);
+
+	ASSERT(reg_width == 4);
+	ASSERT(buflen_u < (1 << 30));
+	ASSERT(sd->client_block_size[func]);
+
+	sd_data(("%s: %c len %d r_cnt %d t_cnt %d, pkt @0x%p\n",
+	         __FUNCTION__, rw == SDIOH_READ ? 'R' : 'W',
+	         buflen_u, sd->r_cnt, sd->t_cnt, pkt));
+
+	/* Break buffer down into blocksize chunks:
+	 * Bytemode: 1 block at a time.
+	 * Blockmode: Multiples of blocksizes at a time w/ max of SD_PAGE.
+	 * Both: leftovers are handled last (will be sent via bytemode).
+	 */
+	while (buflen > 0) {
+		if (local_blockmode) {
+			/* Max xfer is Page size */
+			len = MIN(SD_PAGE, buflen);
+
+			/* Round down to a block boundry */
+			if (buflen > sd->client_block_size[func])
+				len = (len/sd->client_block_size[func]) *
+				        sd->client_block_size[func];
+			if ((func == SDIO_FUNC_1) && ((len % 4) == 3) && (rw == SDIOH_WRITE)) {
+				tmplen = len;
+				sd_err(("%s: Rounding up buffer to mod4 length.\n", __FUNCTION__));
+				len++;
+				tmpbuf = buffer;
+				if ((localbuf = (uint8 *)MALLOC(sd->osh, len)) == NULL) {
+					sd_err(("out of memory, malloced %d bytes\n",
+					        MALLOCED(sd->osh)));
+					sdstd_unlock(sd);
+					return SDIOH_API_RC_FAIL;
+				}
+				bcopy(buffer, localbuf, len);
+				buffer = localbuf;
+			}
+		} else {
+			/* Byte mode: One block at a time */
+			len = MIN(sd->client_block_size[func], buflen);
+		}
+
+		if (sdstd_card_buf(sd, rw, func, fifo, addr, len, (uint32 *)buffer) != SUCCESS) {
+			sdstd_unlock(sd);
+			return SDIOH_API_RC_FAIL;
+		}
+
+		if (local_blockmode) {
+			if ((func == SDIO_FUNC_1) && ((tmplen % 4) == 3) && (rw == SDIOH_WRITE)) {
+				if (localbuf)
+					MFREE(sd->osh, localbuf, len);
+				len--;
+				buffer = tmpbuf;
+				sd_err(("%s: Restoring back buffer ptr and len.\n", __FUNCTION__));
+			}
+		}
+
+		buffer += len;
+		buflen -= len;
+		if (!fifo)
+			addr += len;
+	}
+	sdstd_unlock(sd);
+	return SDIOH_API_RC_SUCCESS;
+}
+
+static
+int sdstd_abort(sdioh_info_t *sd, uint func)
+{
+	int err = 0;
+	int retries;
+
+	uint16 cmd_reg;
+	uint32 cmd_arg;
+	uint32 rsp5;
+	uint8 rflags;
+
+	uint16 int_reg = 0;
+	uint16 plain_intstatus;
+
+	/* Argument is write to F0 (CCCR) IOAbort with function number */
+	cmd_arg = 0;
+	cmd_arg = SFIELD(cmd_arg, CMD52_FUNCTION, SDIO_FUNC_0);
+	cmd_arg = SFIELD(cmd_arg, CMD52_REG_ADDR, SDIOD_CCCR_IOABORT);
+	cmd_arg = SFIELD(cmd_arg, CMD52_RW_FLAG, SD_IO_OP_WRITE);
+	cmd_arg = SFIELD(cmd_arg, CMD52_RAW, 0);
+	cmd_arg = SFIELD(cmd_arg, CMD52_DATA, func);
+
+	/* Command is CMD52 write */
+	cmd_reg = 0;
+	cmd_reg = SFIELD(cmd_reg, CMD_RESP_TYPE, RESP_TYPE_48_BUSY);
+	cmd_reg = SFIELD(cmd_reg, CMD_CRC_EN, 1);
+	cmd_reg = SFIELD(cmd_reg, CMD_INDEX_EN, 1);
+	cmd_reg = SFIELD(cmd_reg, CMD_DATA_EN, 0);
+	cmd_reg = SFIELD(cmd_reg, CMD_TYPE, CMD_TYPE_ABORT);
+	cmd_reg = SFIELD(cmd_reg, CMD_INDEX, SDIOH_CMD_52);
+
+	if (sd->sd_mode == SDIOH_MODE_SPI) {
+		cmd_reg = SFIELD(cmd_reg, CMD_CRC_EN, 0);
+		cmd_reg = SFIELD(cmd_reg, CMD_INDEX_EN, 0);
+	}
+
+	/* Wait for CMD_INHIBIT to go away as per spec section 3.6.1.1 */
+	retries = RETRIES_SMALL;
+	while (GFIELD(sdstd_rreg(sd, SD_PresentState), PRES_CMD_INHIBIT)) {
+		if (retries == RETRIES_SMALL)
+			sd_err(("%s: Waiting for Command Inhibit, state 0x%08x\n",
+			        __FUNCTION__, sdstd_rreg(sd, SD_PresentState)));
+		if (!--retries) {
+			sd_err(("%s: Command Inhibit timeout, state 0x%08x\n",
+			        __FUNCTION__, sdstd_rreg(sd, SD_PresentState)));
+			if (trap_errs)
+				ASSERT(0);
+			err = BCME_SDIO_ERROR;
+			goto done;
+		}
+	}
+
+	/* Clear errors from any previous commands */
+	if ((plain_intstatus = sdstd_rreg16(sd, SD_ErrorIntrStatus)) != 0) {
+		sd_err(("abort: clearing errstat 0x%04x\n", plain_intstatus));
+		sdstd_wreg16(sd, SD_ErrorIntrStatus, plain_intstatus);
+	}
+	plain_intstatus = sdstd_rreg16(sd, SD_IntrStatus);
+	if (plain_intstatus & ~(SFIELD(0, INTSTAT_CARD_INT, 1))) {
+		sd_err(("abort: intstatus 0x%04x\n", plain_intstatus));
+		if (GFIELD(plain_intstatus, INTSTAT_CMD_COMPLETE)) {
+			sd_err(("SDSTD_ABORT: CMD COMPLETE SET BEFORE COMMAND GIVEN!!!\n"));
+		}
+		if (GFIELD(plain_intstatus, INTSTAT_CARD_REMOVAL)) {
+			sd_err(("SDSTD_ABORT: INTSTAT_CARD_REMOVAL\n"));
+			err = BCME_NODEVICE;
+			goto done;
+		}
+	}
+
+	/* Issue the command */
+	sdstd_wreg(sd, SD_Arg0, cmd_arg);
+	sdstd_wreg16(sd, SD_Command, cmd_reg);
+
+	/* In interrupt mode return, expect later CMD_COMPLETE interrupt */
+	if (!sd->polled_mode)
+		return err;
+
+	/* Otherwise, wait for the command to complete */
+	retries = RETRIES_LARGE;
+	do {
+		int_reg = sdstd_rreg16(sd, SD_IntrStatus);
+	} while (--retries &&
+	         (GFIELD(int_reg, INTSTAT_ERROR_INT) == 0) &&
+	         (GFIELD(int_reg, INTSTAT_CMD_COMPLETE) == 0));
+
+	/* If command completion fails, do a cmd reset and note the error */
+	if (!retries) {
+		sd_err(("%s: CMD_COMPLETE timeout: intr 0x%04x err 0x%04x state 0x%08x\n",
+		        __FUNCTION__, int_reg,
+		        sdstd_rreg16(sd, SD_ErrorIntrStatus),
+		        sdstd_rreg(sd, SD_PresentState)));
+
+		sdstd_wreg8(sd, SD_SoftwareReset, SFIELD(0, SW_RESET_CMD, 1));
+		retries = RETRIES_LARGE;
+		do {
+			sd_trace(("%s: waiting for CMD line reset\n", __FUNCTION__));
+		} while ((GFIELD(sdstd_rreg8(sd, SD_SoftwareReset),
+		                 SW_RESET_CMD)) && retries--);
+
+		if (!retries) {
+			sd_err(("%s: Timeout waiting for CMD line reset\n", __FUNCTION__));
+		}
+
+		if (trap_errs)
+			ASSERT(0);
+
+		err = BCME_SDIO_ERROR;
+	}
+
+	/* Clear Command Complete interrupt */
+	int_reg = SFIELD(0, INTSTAT_CMD_COMPLETE, 1);
+	sdstd_wreg16(sd, SD_IntrStatus, int_reg);
+
+	/* Check for Errors */
+	if ((plain_intstatus = sdstd_rreg16 (sd, SD_ErrorIntrStatus)) != 0) {
+		sd_err(("%s: ErrorintrStatus: 0x%x, "
+		        "(intrstatus = 0x%x, present state 0x%x) clearing\n",
+		        __FUNCTION__, plain_intstatus,
+		        sdstd_rreg16(sd, SD_IntrStatus),
+		        sdstd_rreg(sd, SD_PresentState)));
+
+		sdstd_wreg16(sd, SD_ErrorIntrStatus, plain_intstatus);
+
+		sdstd_wreg8(sd, SD_SoftwareReset, SFIELD(0, SW_RESET_DAT, 1));
+		retries = RETRIES_LARGE;
+		do {
+			sd_trace(("%s: waiting for DAT line reset\n", __FUNCTION__));
+		} while ((GFIELD(sdstd_rreg8(sd, SD_SoftwareReset),
+		                 SW_RESET_DAT)) && retries--);
+
+		if (!retries) {
+			sd_err(("%s: Timeout waiting for DAT line reset\n", __FUNCTION__));
+		}
+
+		if (trap_errs)
+			ASSERT(0);
+
+		/* ABORT is dataless, only cmd errs count */
+		if (plain_intstatus & ERRINT_CMD_ERRS)
+			err = BCME_SDIO_ERROR;
+	}
+
+	/* If command failed don't bother looking at response */
+	if (err)
+		goto done;
+
+	/* Otherwise, check the response */
+	sdstd_cmd_getrsp(sd, &rsp5, 1);
+	rflags = GFIELD(rsp5, RSP5_FLAGS);
+
+	if (rflags & SD_RSP_R5_ERRBITS) {
+		sd_err(("%s: R5 flags include errbits: 0x%02x\n", __FUNCTION__, rflags));
+
+		/* The CRC error flag applies to the previous command */
+		if (rflags & (SD_RSP_R5_ERRBITS & ~SD_RSP_R5_COM_CRC_ERROR)) {
+			err = BCME_SDIO_ERROR;
+			goto done;
+		}
+	}
+
+	if (((rflags & (SD_RSP_R5_IO_CURRENTSTATE0 | SD_RSP_R5_IO_CURRENTSTATE1)) != 0x10) &&
+	    ((rflags & (SD_RSP_R5_IO_CURRENTSTATE0 | SD_RSP_R5_IO_CURRENTSTATE1)) != 0x20)) {
+		sd_err(("%s: R5 flags has bad state: 0x%02x\n", __FUNCTION__, rflags));
+		err = BCME_SDIO_ERROR;
+		goto done;
+	}
+
+	if (GFIELD(rsp5, RSP5_STUFF)) {
+		sd_err(("%s: rsp5 stuff is 0x%x: should be 0\n",
+		        __FUNCTION__, GFIELD(rsp5, RSP5_STUFF)));
+		err = BCME_SDIO_ERROR;
+		goto done;
+	}
+
+done:
+	if (err == BCME_NODEVICE)
+		return err;
+
+	sdstd_wreg8(sd, SD_SoftwareReset,
+	            SFIELD(SFIELD(0, SW_RESET_DAT, 1), SW_RESET_CMD, 1));
+
+	retries = RETRIES_LARGE;
+	do {
+		rflags = sdstd_rreg8(sd, SD_SoftwareReset);
+		if (!GFIELD(rflags, SW_RESET_DAT) && !GFIELD(rflags, SW_RESET_CMD))
+			break;
+	} while (--retries);
+
+	if (!retries) {
+		sd_err(("%s: Timeout waiting for DAT/CMD reset: 0x%02x\n",
+		        __FUNCTION__, rflags));
+		err = BCME_SDIO_ERROR;
+	}
+
+	return err;
+}
+
+extern int
+sdioh_abort(sdioh_info_t *sd, uint fnum)
+{
+	int ret;
+
+	sdstd_lock(sd);
+	ret = sdstd_abort(sd, fnum);
+	sdstd_unlock(sd);
+
+	return ret;
+}
+
+int
+sdioh_start(sdioh_info_t *sd, int stage)
+{
+	return SUCCESS;
+}
+
+int
+sdioh_stop(sdioh_info_t *sd)
+{
+	return SUCCESS;
+}
+
+static int
+sdstd_check_errs(sdioh_info_t *sdioh_info, uint32 cmd, uint32 arg)
+{
+	uint16 regval;
+	uint retries;
+	uint function = 0;
+
+	/* If no errors, we're done */
+	if ((regval = sdstd_rreg16(sdioh_info, SD_ErrorIntrStatus)) == 0)
+		return SUCCESS;
+
+	sd_info(("%s: ErrorIntrStatus 0x%04x (clearing), IntrStatus 0x%04x PresentState 0x%08x\n",
+	        __FUNCTION__, regval, sdstd_rreg16(sdioh_info, SD_IntrStatus),
+	        sdstd_rreg(sdioh_info, SD_PresentState)));
+	sdstd_wreg16(sdioh_info, SD_ErrorIntrStatus, regval);
+
+	/* On command error, issue CMD reset */
+	if (regval & ERRINT_CMD_ERRS) {
+		sd_trace(("%s: issuing CMD reset\n", __FUNCTION__));
+		sdstd_wreg8(sdioh_info, SD_SoftwareReset, SFIELD(0, SW_RESET_CMD, 1));
+		for (retries = RETRIES_LARGE; retries; retries--)
+			if (!(GFIELD(sdstd_rreg8(sdioh_info, SD_SoftwareReset), SW_RESET_CMD)))
+				break;
+		if (!retries) {
+			sd_err(("%s: Timeout waiting for CMD line reset\n", __FUNCTION__));
+		}
+	}
+
+	/* On data error, issue DAT reset */
+	if (regval & ERRINT_DATA_ERRS) {
+		sd_trace(("%s: issuing DAT reset\n", __FUNCTION__));
+		sdstd_wreg8(sdioh_info, SD_SoftwareReset, SFIELD(0, SW_RESET_DAT, 1));
+		for (retries = RETRIES_LARGE; retries; retries--)
+			if (!(GFIELD(sdstd_rreg8(sdioh_info, SD_SoftwareReset), SW_RESET_DAT)))
+				break;
+		if (!retries) {
+			sd_err(("%s: Timeout waiting for DAT line reset\n", __FUNCTION__));
+		}
+	}
+
+	/* For an IO command (CMD52 or CMD53) issue an abort to the appropriate function */
+	if (cmd == SDIOH_CMD_53)
+		function = GFIELD(arg, CMD53_FUNCTION);
+	else if (cmd == SDIOH_CMD_52)
+		function = GFIELD(arg, CMD52_FUNCTION);
+	if (function) {
+		sd_trace(("%s: requesting abort for function %d after cmd %d\n",
+		          __FUNCTION__, function, cmd));
+		sdstd_abort(sdioh_info, function);
+	}
+
+	if (trap_errs)
+		ASSERT(0);
+
+	return ERROR;
+}
+
+
+
+/*
+ * Private/Static work routines
+ */
+static bool
+sdstd_reset(sdioh_info_t *sd, bool host_reset, bool client_reset)
+{
+	int retries = RETRIES_LARGE;
+	uchar regval;
+
+	if (!sd)
+		return TRUE;
+
+	sdstd_lock(sd);
+	/* Reset client card */
+	if (client_reset && (sd->adapter_slot != -1)) {
+		if (sdstd_card_regwrite(sd, 0, SDIOD_CCCR_IOABORT, 1, 0x8) != SUCCESS)
+			sd_err(("%s: Cannot write to card reg 0x%x\n",
+			        __FUNCTION__, SDIOD_CCCR_IOABORT));
+		else
+			sd->card_rca = 0;
+	}
+
+	/* Reset host controller */
+	if (host_reset) {
+		regval = SFIELD(0, SW_RESET_ALL, 1);
+		sdstd_wreg8(sd, SD_SoftwareReset, regval);
+		do {
+			sd_trace(("%s: waiting for reset\n", __FUNCTION__));
+		} while ((sdstd_rreg8(sd, SD_SoftwareReset) & regval) && retries--);
+
+		if (!retries) {
+			sd_err(("%s: Timeout waiting for host reset\n", __FUNCTION__));
+			sdstd_unlock(sd);
+			return (FALSE);
+		}
+
+		/* A reset should reset bus back to 1 bit mode */
+		sd->sd_mode = SDIOH_MODE_SD1;
+		sdstd_set_dma_mode(sd, sd->sd_dma_mode);
+	}
+	sdstd_unlock(sd);
+	return TRUE;
+}
+
+/* Disable device interrupt */
+void
+sdstd_devintr_off(sdioh_info_t *sd)
+{
+	sd_trace(("%s: %d\n", __FUNCTION__, sd->use_client_ints));
+	if (sd->use_client_ints) {
+		sd->intmask &= ~CLIENT_INTR;
+		sdstd_wreg16(sd, SD_IntrSignalEnable, sd->intmask);
+		sdstd_rreg16(sd, SD_IntrSignalEnable); /* Sync readback */
+	}
+}
+
+/* Enable device interrupt */
+void
+sdstd_devintr_on(sdioh_info_t *sd)
+{
+	ASSERT(sd->lockcount == 0);
+	sd_trace(("%s: %d\n", __FUNCTION__, sd->use_client_ints));
+	if (sd->use_client_ints) {
+		uint16 status = sdstd_rreg16(sd, SD_IntrStatusEnable);
+		sdstd_wreg16(sd, SD_IntrStatusEnable, SFIELD(status, INTSTAT_CARD_INT, 0));
+		sdstd_wreg16(sd, SD_IntrStatusEnable, status);
+
+		sd->intmask |= CLIENT_INTR;
+		sdstd_wreg16(sd, SD_IntrSignalEnable, sd->intmask);
+		sdstd_rreg16(sd, SD_IntrSignalEnable); /* Sync readback */
+	}
+}
+
+#ifdef BCMSDYIELD
+/* Enable/disable other interrupts */
+void
+sdstd_intrs_on(sdioh_info_t *sd, uint16 norm, uint16 err)
+{
+	if (err) {
+		norm = SFIELD(norm, INTSTAT_ERROR_INT, 1);
+		sdstd_wreg16(sd, SD_ErrorIntrSignalEnable, err);
+	}
+
+	sd->intmask |= norm;
+	sdstd_wreg16(sd, SD_IntrSignalEnable, sd->intmask);
+	if (sd_forcerb)
+		sdstd_rreg16(sd, SD_IntrSignalEnable); /* Sync readback */
+}
+
+void
+sdstd_intrs_off(sdioh_info_t *sd, uint16 norm, uint16 err)
+{
+	if (err) {
+		norm = SFIELD(norm, INTSTAT_ERROR_INT, 1);
+		sdstd_wreg16(sd, SD_ErrorIntrSignalEnable, 0);
+	}
+
+	sd->intmask &= ~norm;
+	sdstd_wreg16(sd, SD_IntrSignalEnable, sd->intmask);
+	if (sd_forcerb)
+		sdstd_rreg16(sd, SD_IntrSignalEnable); /* Sync readback */
+}
+#endif /* BCMSDYIELD */
+
+static int
+sdstd_host_init(sdioh_info_t *sd)
+{
+	int 		num_slots, full_slot;
+	uint8		reg8;
+
+	uint32		card_ins;
+	int			slot, first_bar = 0;
+	bool		detect_slots = FALSE;
+	uint		bar;
+
+	/* Check for Arasan ID */
+	if ((OSL_PCI_READ_CONFIG(sd->osh, PCI_CFG_VID, 4) & 0xFFFF) == VENDOR_SI_IMAGE) {
+		sd_info(("%s: Found Arasan Standard SDIO Host Controller\n", __FUNCTION__));
+		sd->controller_type = SDIOH_TYPE_ARASAN_HDK;
+		detect_slots = TRUE;
+	} else if ((OSL_PCI_READ_CONFIG(sd->osh, PCI_CFG_VID, 4) & 0xFFFF) == VENDOR_BROADCOM) {
+		sd_info(("%s: Found Broadcom 27xx Standard SDIO Host Controller\n", __FUNCTION__));
+		sd->controller_type = SDIOH_TYPE_BCM27XX;
+		detect_slots = FALSE;
+	} else if ((OSL_PCI_READ_CONFIG(sd->osh, PCI_CFG_VID, 4) & 0xFFFF) == VENDOR_TI) {
+		sd_info(("%s: Found TI PCIxx21 Standard SDIO Host Controller\n", __FUNCTION__));
+		sd->controller_type = SDIOH_TYPE_TI_PCIXX21;
+		detect_slots = TRUE;
+	} else if ((OSL_PCI_READ_CONFIG(sd->osh, PCI_CFG_VID, 4) & 0xFFFF) == VENDOR_RICOH) {
+		sd_info(("%s: Ricoh Co Ltd R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter\n",
+			__FUNCTION__));
+		sd->controller_type = SDIOH_TYPE_RICOH_R5C822;
+		detect_slots = TRUE;
+	} else if ((OSL_PCI_READ_CONFIG(sd->osh, PCI_CFG_VID, 4) & 0xFFFF) == VENDOR_JMICRON) {
+		sd_info(("%s: JMicron Standard SDIO Host Controller\n",
+			__FUNCTION__));
+		sd->controller_type = SDIOH_TYPE_JMICRON;
+		detect_slots = TRUE;
+	} else {
+		return ERROR;
+	}
+
+	/*
+	 * Determine num of slots
+	 * Search each slot
+	 */
+
+	first_bar = OSL_PCI_READ_CONFIG(sd->osh, SD_SlotInfo, 4) & 0x7;
+	num_slots = (OSL_PCI_READ_CONFIG(sd->osh, SD_SlotInfo, 4) & 0xff) >> 4;
+	num_slots &= 7;
+	num_slots++;   	/* map bits to num slots according to spec */
+
+	if (OSL_PCI_READ_CONFIG(sd->osh, PCI_CFG_VID, 4) ==
+	    ((SDIOH_FPGA_ID << 16) | VENDOR_BROADCOM)) {
+		sd_err(("%s: Found Broadcom Standard SDIO Host Controller FPGA\n", __FUNCTION__));
+		/* Set BAR0 Window to SDIOSTH core */
+		OSL_PCI_WRITE_CONFIG(sd->osh, PCI_BAR0_WIN, 4, 0x18001000);
+
+		/* Set defaults particular to this controller. */
+		detect_slots = TRUE;
+		num_slots = 1;
+		first_bar = 0;
+
+		/* Controller supports ADMA2, so turn it on here. */
+		sd->sd_dma_mode = DMA_MODE_ADMA2;
+	}
+
+	/* Map in each slot on the board and query it to see if a
+	 * card is inserted.  Use the first populated slot found.
+	 */
+	if (sd->mem_space) {
+		sdstd_reg_unmap(sd->osh, (uintptr)sd->mem_space, SDIOH_REG_WINSZ);
+		sd->mem_space = NULL;
+	}
+
+	full_slot = -1;
+
+	for (slot = 0; slot < num_slots; slot++) {
+		bar = OSL_PCI_READ_CONFIG(sd->osh, PCI_CFG_BAR0 + (4*(slot + first_bar)), 4);
+		sd->mem_space = (volatile char *)sdstd_reg_map(sd->osh,
+		                                               (uintptr)bar, SDIOH_REG_WINSZ);
+
+		sd->adapter_slot = -1;
+
+		if (detect_slots) {
+			card_ins = GFIELD(sdstd_rreg(sd, SD_PresentState), PRES_CARD_PRESENT);
+		} else {
+			card_ins = TRUE;
+		}
+
+		if (card_ins) {
+			sd_info(("%s: SDIO slot %d: Full\n", __FUNCTION__, slot));
+			if (full_slot < 0)
+				full_slot = slot;
+		} else {
+			sd_info(("%s: SDIO slot %d: Empty\n", __FUNCTION__, slot));
+		}
+
+		if (sd->mem_space) {
+			sdstd_reg_unmap(sd->osh, (uintptr)sd->mem_space, SDIOH_REG_WINSZ);
+			sd->mem_space = NULL;
+		}
+	}
+
+	if (full_slot < 0) {
+		sd_err(("No slots on SDIO controller are populated\n"));
+		return -1;
+	}
+
+	bar = OSL_PCI_READ_CONFIG(sd->osh, PCI_CFG_BAR0 + (4*(full_slot + first_bar)), 4);
+	sd->mem_space = (volatile char *)sdstd_reg_map(sd->osh, (uintptr)bar, SDIOH_REG_WINSZ);
+
+	sd_err(("Using slot %d at BAR%d [0x%08x] mem_space 0x%p\n",
+		full_slot,
+		(full_slot + first_bar),
+		OSL_PCI_READ_CONFIG(sd->osh, PCI_CFG_BAR0 + (4*(full_slot + first_bar)), 4),
+		sd->mem_space));
+
+
+	sd->adapter_slot = full_slot;
+
+	sd->version = sdstd_rreg16(sd, SD_HostControllerVersion) & 0xFF;
+	switch (sd->version) {
+		case 0:
+			sd_err(("Host Controller version 1.0, Vendor Revision: 0x%02x\n",
+				sdstd_rreg16(sd, SD_HostControllerVersion) >> 8));
+			break;
+		case 1:
+		case 2:
+			sd_err(("Host Controller version 2.0, Vendor Revision: 0x%02x\n",
+				sdstd_rreg16(sd, SD_HostControllerVersion) >> 8));
+			break;
+		default:
+			sd_err(("%s: Host Controller version 0x%02x not supported.\n",
+			    __FUNCTION__, sd->version));
+			break;
+	}
+
+	sd->caps = sdstd_rreg(sd, SD_Capabilities);	/* Cache this for later use */
+	sd->curr_caps = sdstd_rreg(sd, SD_MaxCurCap);
+
+	sdstd_set_dma_mode(sd, sd->sd_dma_mode);
+
+
+	sdstd_reset(sd, 1, 0);
+
+	/* Read SD4/SD1 mode */
+	if ((reg8 = sdstd_rreg8(sd, SD_HostCntrl))) {
+		if (reg8 & SD4_MODE) {
+			sd_err(("%s: Host cntrlr already in 4 bit mode: 0x%x\n",
+			        __FUNCTION__,  reg8));
+		}
+	}
+
+	/* Default power on mode is SD1 */
+	sd->sd_mode = SDIOH_MODE_SD1;
+	sd->polled_mode = TRUE;
+	sd->host_init_done = TRUE;
+	sd->card_init_done = FALSE;
+	sd->adapter_slot = full_slot;
+
+	return (SUCCESS);
+}
+#define CMD5_RETRIES 200
+static int
+get_ocr(sdioh_info_t *sd, uint32 *cmd_arg, uint32 *cmd_rsp)
+{
+	int retries, status;
+
+	/* Get the Card's Operation Condition.  Occasionally the board
+	 * takes a while to become ready
+	 */
+	retries = CMD5_RETRIES;
+	do {
+		*cmd_rsp = 0;
+		if ((status = sdstd_cmd_issue(sd, USE_DMA(sd), SDIOH_CMD_5, *cmd_arg))
+		    != SUCCESS) {
+			sd_err(("%s: CMD5 failed\n", __FUNCTION__));
+			return status;
+		}
+		sdstd_cmd_getrsp(sd, cmd_rsp, 1);
+		if (!GFIELD(*cmd_rsp, RSP4_CARD_READY))
+			sd_trace(("%s: Waiting for card to become ready\n", __FUNCTION__));
+	} while ((!GFIELD(*cmd_rsp, RSP4_CARD_READY)) && --retries);
+	if (!retries)
+		return ERROR;
+
+	return (SUCCESS);
+}
+
+static int
+sdstd_client_init(sdioh_info_t *sd)
+{
+	uint32 cmd_arg, cmd_rsp;
+	int status;
+	uint8 fn_ints;
+
+
+	sd_trace(("%s: Powering up slot %d\n", __FUNCTION__, sd->adapter_slot));
+
+	/* Clear any pending ints */
+	sdstd_wreg16(sd, SD_IntrStatus, 0x1ff);
+	sdstd_wreg16(sd, SD_ErrorIntrStatus, 0x0fff);
+
+	/* Enable both Normal and Error Status.  This does not enable
+	 * interrupts, it only enables the status bits to
+	 * become 'live'
+	 */
+	sdstd_wreg16(sd, SD_IntrStatusEnable, 0x1ff);
+	sdstd_wreg16(sd, SD_ErrorIntrStatusEnable, 0xffff);
+
+	sdstd_wreg16(sd, SD_IntrSignalEnable, 0);	  /* Disable ints for now. */
+
+	/* Start at ~400KHz clock rate for initialization */
+	if (!sdstd_start_clock(sd, 128)) {
+		sd_err(("sdstd_start_clock failed\n"));
+		return ERROR;
+	}
+	if (!sdstd_start_power(sd)) {
+		sd_err(("sdstd_start_power failed\n"));
+		return ERROR;
+	}
+
+	if (sd->num_funcs == 0) {
+		sd_err(("%s: No IO funcs!\n", __FUNCTION__));
+		return ERROR;
+	}
+
+	/* In SPI mode, issue CMD0 first */
+	if (sd->sd_mode == SDIOH_MODE_SPI) {
+		cmd_arg = 0;
+		if ((status = sdstd_cmd_issue(sd, USE_DMA(sd), SDIOH_CMD_0, cmd_arg))
+		    != SUCCESS) {
+			sd_err(("BCMSDIOH: cardinit: CMD0 failed!\n"));
+			return status;
+		}
+	}
+
+	if (sd->sd_mode != SDIOH_MODE_SPI) {
+		uint16 rsp6_status;
+
+		/* Card is operational. Ask it to send an RCA */
+		cmd_arg = 0;
+		if ((status = sdstd_cmd_issue(sd, USE_DMA(sd), SDIOH_CMD_3, cmd_arg))
+		    != SUCCESS) {
+			sd_err(("%s: CMD3 failed!\n", __FUNCTION__));
+			return status;
+		}
+
+		/* Verify the card status returned with the cmd response */
+		sdstd_cmd_getrsp(sd, &cmd_rsp, 1);
+		rsp6_status = GFIELD(cmd_rsp, RSP6_STATUS);
+		if (GFIELD(rsp6_status, RSP6STAT_COM_CRC_ERROR) ||
+		    GFIELD(rsp6_status, RSP6STAT_ILLEGAL_CMD) ||
+		    GFIELD(rsp6_status, RSP6STAT_ERROR)) {
+			sd_err(("%s: CMD3 response error. Response = 0x%x!\n",
+			        __FUNCTION__, rsp6_status));
+			return ERROR;
+		}
+
+		/* Save the Card's RCA */
+		sd->card_rca = GFIELD(cmd_rsp, RSP6_IO_RCA);
+		sd_info(("RCA is 0x%x\n", sd->card_rca));
+
+		if (rsp6_status)
+			sd_err(("raw status is 0x%x\n", rsp6_status));
+
+		/* Select the card */
+		cmd_arg = SFIELD(0, CMD7_RCA, sd->card_rca);
+		if ((status = sdstd_cmd_issue(sd, USE_DMA(sd), SDIOH_CMD_7, cmd_arg))
+		    != SUCCESS) {
+			sd_err(("%s: CMD7 failed!\n", __FUNCTION__));
+			return status;
+		}
+		sdstd_cmd_getrsp(sd, &cmd_rsp, 1);
+		if (cmd_rsp != SDIOH_CMD7_EXP_STATUS) {
+			sd_err(("%s: CMD7 response error. Response = 0x%x!\n",
+			        __FUNCTION__, cmd_rsp));
+			return ERROR;
+		}
+	}
+
+	sdstd_card_enablefuncs(sd);
+
+	if (!sdstd_bus_width(sd, sd_sdmode)) {
+		sd_err(("sdstd_bus_width failed\n"));
+		return ERROR;
+	}
+
+	set_client_block_size(sd, 1, BLOCK_SIZE_4318);
+	fn_ints = INTR_CTL_FUNC1_EN;
+
+	if (sd->num_funcs >= 2) {
+		set_client_block_size(sd, 2, sd_f2_blocksize /* BLOCK_SIZE_4328 */);
+		fn_ints |= INTR_CTL_FUNC2_EN;
+	}
+
+	/* Enable/Disable Client interrupts */
+	/* Turn on here but disable at host controller? */
+	if (sdstd_card_regwrite(sd, 0, SDIOD_CCCR_INTEN, 1,
+	                        (fn_ints | INTR_CTL_MASTER_EN)) != SUCCESS) {
+		sd_err(("%s: Could not enable ints in CCCR\n", __FUNCTION__));
+		return ERROR;
+	}
+
+	/* Switch to High-speed clocking mode if both host and device support it */
+	sdstd_set_highspeed_mode(sd, (bool)sd_hiok);
+
+	/* After configuring for High-Speed mode, set the desired clock rate. */
+	if (!sdstd_start_clock(sd, (uint16)sd_divisor)) {
+		sd_err(("sdstd_start_clock failed\n"));
+		return ERROR;
+	}
+
+	sd->card_init_done = TRUE;
+
+	return SUCCESS;
+}
+
+static int
+sdstd_set_highspeed_mode(sdioh_info_t *sd, bool HSMode)
+{
+	uint32 regdata;
+	int status;
+	uint8 reg8;
+
+	reg8 = sdstd_rreg8(sd, SD_HostCntrl);
+
+
+	if (HSMode == TRUE) {
+		if (sd_hiok && (GFIELD(sd->caps, CAP_HIGHSPEED)) == 0) {
+			sd_err(("Host Controller does not support hi-speed mode.\n"));
+			return BCME_ERROR;
+		}
+
+		sd_info(("Attempting to enable High-Speed mode.\n"));
+
+		if ((status = sdstd_card_regread(sd, 0, SDIOD_CCCR_SPEED_CONTROL,
+		                                 1, &regdata)) != SUCCESS) {
+			return BCME_SDIO_ERROR;
+		}
+		if (regdata & SDIO_SPEED_SHS) {
+			sd_info(("Device supports High-Speed mode.\n"));
+
+			regdata |= SDIO_SPEED_EHS;
+
+			sd_info(("Writing %08x to Card at %08x\n",
+			         regdata, SDIOD_CCCR_SPEED_CONTROL));
+			if ((status = sdstd_card_regwrite(sd, 0, SDIOD_CCCR_SPEED_CONTROL,
+			                                  1, regdata)) != BCME_OK) {
+				return BCME_SDIO_ERROR;
+			}
+
+			if ((status = sdstd_card_regread(sd, 0, SDIOD_CCCR_SPEED_CONTROL,
+			                                 1, &regdata)) != BCME_OK) {
+				return BCME_SDIO_ERROR;
+			}
+
+			sd_info(("Read %08x to Card at %08x\n", regdata, SDIOD_CCCR_SPEED_CONTROL));
+
+			reg8 = SFIELD(reg8, HOST_HI_SPEED_EN, 1);
+
+			sd_err(("High-speed clocking mode enabled.\n"));
+		}
+		else {
+			sd_err(("Device does not support High-Speed Mode.\n"));
+			reg8 = SFIELD(reg8, HOST_HI_SPEED_EN, 0);
+		}
+	} else {
+		/* Force off device bit */
+		if ((status = sdstd_card_regread(sd, 0, SDIOD_CCCR_SPEED_CONTROL,
+		                                 1, &regdata)) != BCME_OK) {
+			return status;
+		}
+		if (regdata & SDIO_SPEED_EHS) {
+			regdata &= ~SDIO_SPEED_EHS;
+			if ((status = sdstd_card_regwrite(sd, 0, SDIOD_CCCR_SPEED_CONTROL,
+			                                  1, regdata)) != BCME_OK) {
+				return status;
+			}
+		}
+
+		sd_err(("High-speed clocking mode disabled.\n"));
+		reg8 = SFIELD(reg8, HOST_HI_SPEED_EN, 0);
+	}
+
+	sdstd_wreg8(sd, SD_HostCntrl, reg8);
+
+	return BCME_OK;
+}
+
+/* Select DMA Mode:
+ * If dma_mode == DMA_MODE_AUTO, pick the "best" mode.
+ * Otherwise, pick the selected mode if supported.
+ * If not supported, use PIO mode.
+ */
+static int
+sdstd_set_dma_mode(sdioh_info_t *sd, int8 dma_mode)
+{
+	uint8 reg8, dma_sel_bits = SDIOH_SDMA_MODE;
+	int8 prev_dma_mode = sd->sd_dma_mode;
+
+	switch (prev_dma_mode) {
+		case DMA_MODE_AUTO:
+			sd_dma(("%s: Selecting best DMA mode supported by controller.\n",
+			          __FUNCTION__));
+			if (GFIELD(sd->caps, CAP_ADMA2)) {
+				sd->sd_dma_mode = DMA_MODE_ADMA2;
+				dma_sel_bits = SDIOH_ADMA2_MODE;
+			} else if (GFIELD(sd->caps, CAP_ADMA1)) {
+				sd->sd_dma_mode = DMA_MODE_ADMA1;
+				dma_sel_bits = SDIOH_ADMA1_MODE;
+			} else if (GFIELD(sd->caps, CAP_DMA)) {
+				sd->sd_dma_mode = DMA_MODE_SDMA;
+			} else {
+				sd->sd_dma_mode = DMA_MODE_NONE;
+			}
+			break;
+		case DMA_MODE_NONE:
+			sd->sd_dma_mode = DMA_MODE_NONE;
+			break;
+		case DMA_MODE_SDMA:
+			if (GFIELD(sd->caps, CAP_DMA)) {
+				sd->sd_dma_mode = DMA_MODE_SDMA;
+			} else {
+				sd_err(("%s: SDMA not supported by controller.\n", __FUNCTION__));
+				sd->sd_dma_mode = DMA_MODE_NONE;
+			}
+			break;
+		case DMA_MODE_ADMA1:
+			if (GFIELD(sd->caps, CAP_ADMA1)) {
+				sd->sd_dma_mode = DMA_MODE_ADMA1;
+				dma_sel_bits = SDIOH_ADMA1_MODE;
+			} else {
+				sd_err(("%s: ADMA1 not supported by controller.\n", __FUNCTION__));
+				sd->sd_dma_mode = DMA_MODE_NONE;
+			}
+			break;
+		case DMA_MODE_ADMA2:
+			if (GFIELD(sd->caps, CAP_ADMA2)) {
+				sd->sd_dma_mode = DMA_MODE_ADMA2;
+				dma_sel_bits = SDIOH_ADMA2_MODE;
+			} else {
+				sd_err(("%s: ADMA2 not supported by controller.\n", __FUNCTION__));
+				sd->sd_dma_mode = DMA_MODE_NONE;
+			}
+			break;
+		case DMA_MODE_ADMA2_64:
+			sd_err(("%s: 64b ADMA2 not supported by driver.\n", __FUNCTION__));
+			sd->sd_dma_mode = DMA_MODE_NONE;
+			break;
+		default:
+			sd_err(("%s: Unsupported DMA Mode %d requested.\n", __FUNCTION__,
+			        prev_dma_mode));
+			sd->sd_dma_mode = DMA_MODE_NONE;
+			break;
+	}
+
+	/* clear SysAddr, only used for SDMA */
+	sdstd_wreg(sd, SD_SysAddr, 0);
+
+	sd_err(("%s: %s mode selected.\n", __FUNCTION__, dma_mode_description[sd->sd_dma_mode]));
+
+	reg8 = sdstd_rreg8(sd, SD_HostCntrl);
+	reg8 = SFIELD(reg8, HOST_DMA_SEL, dma_sel_bits);
+	sdstd_wreg8(sd, SD_HostCntrl, reg8);
+	sd_dma(("%s: SD_HostCntrl=0x%02x\n", __FUNCTION__, reg8));
+
+	return BCME_OK;
+}
+
+
+bool
+sdstd_start_clock(sdioh_info_t *sd, uint16 new_sd_divisor)
+{
+	uint rc, count;
+	uint16 divisor;
+
+	/* turn off HC clock */
+	sdstd_wreg16(sd, SD_ClockCntrl,
+	             sdstd_rreg16(sd, SD_ClockCntrl) & ~((uint16)0x4)); /*  Disable the HC clock */
+
+	/* Set divisor */
+
+	divisor = (new_sd_divisor >> 1) << 8;
+
+	sd_info(("Clock control is 0x%x\n", sdstd_rreg16(sd, SD_ClockCntrl)));
+	sdstd_mod_reg16(sd, SD_ClockCntrl, 0xff00, divisor);
+	sd_info(("%s: Using clock divisor of %d (regval 0x%04x)\n", __FUNCTION__,
+	         new_sd_divisor, divisor));
+
+	sd_info(("Primary Clock Freq = %d MHz\n", GFIELD(sd->caps, CAP_TO_CLKFREQ)));
+
+	if (GFIELD(sd->caps, CAP_TO_CLKFREQ) == 50) {
+		sd_info(("%s: Resulting SDIO clock is %d %s\n", __FUNCTION__,
+		        ((50 % new_sd_divisor) ? (50000 / new_sd_divisor) : (50 / new_sd_divisor)),
+		        ((50 % new_sd_divisor) ? "KHz" : "MHz")));
+	} else if (GFIELD(sd->caps, CAP_TO_CLKFREQ) == 48) {
+		sd_info(("%s: Resulting SDIO clock is %d %s\n", __FUNCTION__,
+		        ((48 % new_sd_divisor) ? (48000 / new_sd_divisor) : (48 / new_sd_divisor)),
+		        ((48 % new_sd_divisor) ? "KHz" : "MHz")));
+	} else if (GFIELD(sd->caps, CAP_TO_CLKFREQ) == 33) {
+		sd_info(("%s: Resulting SDIO clock is %d %s\n", __FUNCTION__,
+		        ((33 % new_sd_divisor) ? (33000 / new_sd_divisor) : (33 / new_sd_divisor)),
+		        ((33 % new_sd_divisor) ? "KHz" : "MHz")));
+
+	} else if (sd->controller_type == SDIOH_TYPE_BCM27XX) {
+	} else {
+		sd_err(("Need to determine divisor for %d MHz clocks\n",
+		        GFIELD(sd->caps, CAP_TO_CLKFREQ)));
+		sd_err(("Consult SD Host Controller Spec: Clock Control Register\n"));
+		return (FALSE);
+	}
+
+	sdstd_or_reg16(sd, SD_ClockCntrl, 0x1); /*  Enable the clock */
+
+	/* Wait for clock to stabilize */
+	rc = (sdstd_rreg16(sd, SD_ClockCntrl) & 2);
+	count = 0;
+	while (!rc) {
+		OSL_DELAY(1);
+		sd_info(("Waiting for clock to become stable 0x%x\n", rc));
+		rc = (sdstd_rreg16(sd, SD_ClockCntrl) & 2);
+		count++;
+		if (count > 10000) {
+			sd_err(("%s:Clocks failed to stabilize after %u attempts",
+			        __FUNCTION__, count));
+			return (FALSE);
+		}
+	}
+	/* Turn on clock */
+	sdstd_or_reg16(sd, SD_ClockCntrl, 0x4);
+
+	/* Set timeout control (adjust default value based on divisor).
+	 * Disabling timeout interrupts during setting is advised by host spec.
+	 */
+	{
+		uint16 regdata;
+		uint toval;
+
+		toval = sd_toctl;
+		divisor = new_sd_divisor;
+
+		while (toval && !(divisor & 1)) {
+			toval -= 1;
+			divisor >>= 1;
+		}
+
+		regdata = sdstd_rreg16(sd, SD_ErrorIntrStatusEnable);
+		sdstd_wreg16(sd, SD_ErrorIntrStatusEnable, (regdata & ~ERRINT_DATA_TIMEOUT_BIT));
+		sdstd_wreg8(sd, SD_TimeoutCntrl, (uint8)toval);
+		sdstd_wreg16(sd, SD_ErrorIntrStatusEnable, regdata);
+	}
+
+	OSL_DELAY(2);
+
+	sd_info(("Final Clock control is 0x%x\n", sdstd_rreg16(sd, SD_ClockCntrl)));
+
+	return TRUE;
+}
+
+bool
+sdstd_start_power(sdioh_info_t *sd)
+{
+	char *s;
+	uint32 cmd_arg;
+	uint32 cmd_rsp;
+	uint8 pwr = 0;
+	int volts;
+
+	volts = 0;
+	s = NULL;
+	if (GFIELD(sd->caps, CAP_VOLT_1_8)) {
+		volts = 5;
+		s = "1.8";
+	}
+	if (GFIELD(sd->caps, CAP_VOLT_3_0)) {
+		volts = 6;
+		s = "3.0";
+	}
+	if (GFIELD(sd->caps, CAP_VOLT_3_3)) {
+		volts = 7;
+		s = "3.3";
+	}
+
+	pwr = SFIELD(pwr, PWR_VOLTS, volts);
+	pwr = SFIELD(pwr, PWR_BUS_EN, 1);
+	sdstd_wreg8(sd, SD_PwrCntrl, pwr); /* Set Voltage level */
+	sd_info(("Setting Bus Power to %s Volts\n", s));
+
+	/* Wait for power to stabilize, Dongle takes longer than NIC. */
+	OSL_DELAY(250000);
+
+	/* Get the Card's Operation Condition.  Occasionally the board
+	 * takes a while to become ready
+	 */
+	cmd_arg = 0;
+	cmd_rsp = 0;
+	if (get_ocr(sd, &cmd_arg, &cmd_rsp) != SUCCESS) {
+		sd_err(("%s: Failed to get OCR bailing\n", __FUNCTION__));
+		sdstd_reset(sd, 0, 1);
+		return FALSE;
+	}
+
+	sd_info(("mem_present = %d\n", GFIELD(cmd_rsp, RSP4_MEM_PRESENT)));
+	sd_info(("num_funcs = %d\n", GFIELD(cmd_rsp, RSP4_NUM_FUNCS)));
+	sd_info(("card_ready = %d\n", GFIELD(cmd_rsp, RSP4_CARD_READY)));
+	sd_info(("OCR = 0x%x\n", GFIELD(cmd_rsp, RSP4_IO_OCR)));
+
+	/* Verify that the card supports I/O mode */
+	if (GFIELD(cmd_rsp, RSP4_NUM_FUNCS) == 0) {
+		sd_err(("%s: Card does not support I/O\n", __FUNCTION__));
+		return ERROR;
+	}
+	sd->num_funcs = GFIELD(cmd_rsp, RSP4_NUM_FUNCS);
+
+	/* Examine voltage: Arasan only supports 3.3 volts,
+	 * so look for 3.2-3.3 Volts and also 3.3-3.4 volts.
+	 */
+
+	if ((GFIELD(cmd_rsp, RSP4_IO_OCR) & (0x3 << 20)) == 0) {
+		sd_err(("This client does not support 3.3 volts!\n"));
+		return ERROR;
+	}
+	sd_info(("Leaving bus power at 3.3 Volts\n"));
+
+	cmd_arg = SFIELD(0, CMD5_OCR, 0xfff000);
+	cmd_rsp = 0;
+	get_ocr(sd, &cmd_arg, &cmd_rsp);
+	sd_info(("OCR = 0x%x\n", GFIELD(cmd_rsp, RSP4_IO_OCR)));
+	return TRUE;
+}
+
+bool
+sdstd_bus_width(sdioh_info_t *sd, int new_mode)
+{
+	uint32 regdata;
+	int status;
+	uint8 reg8;
+
+	sd_trace(("%s\n", __FUNCTION__));
+	if (sd->sd_mode == new_mode) {
+		sd_info(("%s: Already at width %d\n", __FUNCTION__, new_mode));
+		/* Could exit, but continue just in case... */
+	}
+
+	/* Set client side via reg 0x7 in CCCR */
+	if ((status = sdstd_card_regread (sd, 0, SDIOD_CCCR_BICTRL, 1, &regdata)) != SUCCESS)
+		return (bool)status;
+	regdata &= ~BUS_SD_DATA_WIDTH_MASK;
+	if (new_mode == SDIOH_MODE_SD4) {
+		sd_info(("Changing to SD4 Mode\n"));
+		regdata |= SD4_MODE;
+	} else if (new_mode == SDIOH_MODE_SD1) {
+		sd_info(("Changing to SD1 Mode\n"));
+	} else {
+		sd_err(("SPI Mode not supported by Standard Host Controller\n"));
+	}
+
+	if ((status = sdstd_card_regwrite (sd, 0, SDIOD_CCCR_BICTRL, 1, regdata)) != SUCCESS)
+		return (bool)status;
+
+	/* Set host side via Host reg */
+	reg8 = sdstd_rreg8(sd, SD_HostCntrl) & ~SD4_MODE;
+	if (new_mode == SDIOH_MODE_SD4)
+		reg8 |= SD4_MODE;
+	sdstd_wreg8(sd, SD_HostCntrl, reg8);
+
+	sd->sd_mode = new_mode;
+
+	return TRUE;
+}
+
+static int
+sdstd_driver_init(sdioh_info_t *sd)
+{
+	sd_trace(("%s\n", __FUNCTION__));
+	if ((sdstd_host_init(sd)) != SUCCESS) {
+		return ERROR;
+	}
+
+	if (sdstd_client_init(sd) != SUCCESS) {
+		return ERROR;
+	}
+
+	return SUCCESS;
+}
+
+static int
+sdstd_get_cisaddr(sdioh_info_t *sd, uint32 regaddr)
+{
+	/* read 24 bits and return valid 17 bit addr */
+	int i;
+	uint32 scratch, regdata;
+	uint8 *ptr = (uint8 *)&scratch;
+	for (i = 0; i < 3; i++) {
+		if ((sdstd_card_regread (sd, 0, regaddr, 1, &regdata)) != SUCCESS)
+			sd_err(("%s: Can't read!\n", __FUNCTION__));
+
+		*ptr++ = (uint8) regdata;
+		regaddr++;
+	}
+	/* Only the lower 17-bits are valid */
+	scratch = ltoh32(scratch);
+	scratch &= 0x0001FFFF;
+	return (scratch);
+}
+
+static int
+sdstd_card_enablefuncs(sdioh_info_t *sd)
+{
+	int status;
+	uint32 regdata;
+	uint32 fbraddr;
+	uint8 func;
+
+	sd_trace(("%s\n", __FUNCTION__));
+
+	/* Get the Card's common CIS address */
+	sd->com_cis_ptr = sdstd_get_cisaddr(sd, SDIOD_CCCR_CISPTR_0);
+	sd->func_cis_ptr[0] = sd->com_cis_ptr;
+	sd_info(("%s: Card's Common CIS Ptr = 0x%x\n", __FUNCTION__, sd->com_cis_ptr));
+
+	/* Get the Card's function CIS (for each function) */
+	for (fbraddr = SDIOD_FBR_STARTADDR, func = 1;
+	     func <= sd->num_funcs; func++, fbraddr += SDIOD_FBR_SIZE) {
+		sd->func_cis_ptr[func] = sdstd_get_cisaddr(sd, SDIOD_FBR_CISPTR_0 + fbraddr);
+		sd_info(("%s: Function %d CIS Ptr = 0x%x\n",
+		         __FUNCTION__, func, sd->func_cis_ptr[func]));
+	}
+
+	/* Enable function 1 on the card */
+	regdata = SDIO_FUNC_ENABLE_1;
+	if ((status = sdstd_card_regwrite(sd, 0, SDIOD_CCCR_IOEN, 1, regdata)) != SUCCESS)
+		return status;
+
+	return SUCCESS;
+}
+
+/* Read client card reg */
+static int
+sdstd_card_regread(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 *data)
+{
+	int status;
+	uint32 cmd_arg;
+	uint32 rsp5;
+
+
+	cmd_arg = 0;
+
+	if ((func == 0) || (regsize == 1)) {
+		cmd_arg = SFIELD(cmd_arg, CMD52_FUNCTION, func);
+		cmd_arg = SFIELD(cmd_arg, CMD52_REG_ADDR, regaddr);
+		cmd_arg = SFIELD(cmd_arg, CMD52_RW_FLAG, SDIOH_XFER_TYPE_READ);
+		cmd_arg = SFIELD(cmd_arg, CMD52_RAW, 0);
+		cmd_arg = SFIELD(cmd_arg, CMD52_DATA, 0);
+
+		if ((status = sdstd_cmd_issue(sd, USE_DMA(sd), SDIOH_CMD_52, cmd_arg))
+		    != SUCCESS)
+			return status;
+
+		sdstd_cmd_getrsp(sd, &rsp5, 1);
+		if (sdstd_rreg16(sd, SD_ErrorIntrStatus) != 0) {
+			sd_err(("%s: 1: ErrorintrStatus 0x%x\n",
+			        __FUNCTION__, sdstd_rreg16(sd, SD_ErrorIntrStatus)));
+		}
+
+		if (GFIELD(rsp5, RSP5_FLAGS) != 0x10)
+			sd_err(("%s: rsp5 flags is 0x%x\t %d\n",
+			        __FUNCTION__, GFIELD(rsp5, RSP5_FLAGS), func));
+
+		if (GFIELD(rsp5, RSP5_STUFF))
+			sd_err(("%s: rsp5 stuff is 0x%x: should be 0\n",
+			        __FUNCTION__, GFIELD(rsp5, RSP5_STUFF)));
+		*data = GFIELD(rsp5, RSP5_DATA);
+	} else {
+		cmd_arg = SFIELD(cmd_arg, CMD53_BYTE_BLK_CNT, regsize);
+		cmd_arg = SFIELD(cmd_arg, CMD53_OP_CODE, 1);
+		cmd_arg = SFIELD(cmd_arg, CMD53_BLK_MODE, 0);
+		cmd_arg = SFIELD(cmd_arg, CMD53_FUNCTION, func);
+		cmd_arg = SFIELD(cmd_arg, CMD53_REG_ADDR, regaddr);
+		cmd_arg = SFIELD(cmd_arg, CMD53_RW_FLAG, SDIOH_XFER_TYPE_READ);
+
+		sd->data_xfer_count = regsize;
+
+		/* sdstd_cmd_issue() returns with the command complete bit
+		 * in the ISR already cleared
+		 */
+		if ((status = sdstd_cmd_issue(sd, USE_DMA(sd), SDIOH_CMD_53, cmd_arg))
+		    != SUCCESS)
+			return status;
+
+		sdstd_cmd_getrsp(sd, &rsp5, 1);
+
+		if (GFIELD(rsp5, RSP5_FLAGS) != 0x10)
+			sd_err(("%s: rsp5 flags is 0x%x\t %d\n",
+			        __FUNCTION__, GFIELD(rsp5, RSP5_FLAGS), func));
+
+		if (GFIELD(rsp5, RSP5_STUFF))
+			sd_err(("%s: rsp5 stuff is 0x%x: should be 0\n",
+			        __FUNCTION__, GFIELD(rsp5, RSP5_STUFF)));
+
+		if (sd->polled_mode) {
+			volatile uint16 int_reg;
+			int retries = RETRIES_LARGE;
+
+			/* Wait for Read Buffer to become ready */
+			do {
+				int_reg = sdstd_rreg16(sd, SD_IntrStatus);
+			} while (--retries && (GFIELD(int_reg, INTSTAT_BUF_READ_READY) == 0));
+
+			if (!retries) {
+				sd_err(("%s: Timeout on Buf_Read_Ready: "
+				        "intStat: 0x%x errint: 0x%x PresentState 0x%x\n",
+				        __FUNCTION__, int_reg,
+				        sdstd_rreg16(sd, SD_ErrorIntrStatus),
+				        sdstd_rreg(sd, SD_PresentState)));
+				sdstd_check_errs(sd, SDIOH_CMD_53, cmd_arg);
+				return (ERROR);
+			}
+
+			/* Have Buffer Ready, so clear it and read the data */
+			sdstd_wreg16(sd, SD_IntrStatus, SFIELD(0, INTSTAT_BUF_READ_READY, 1));
+			if (regsize == 2)
+				*data = sdstd_rreg16(sd, SD_BufferDataPort0);
+			else
+				*data = sdstd_rreg(sd, SD_BufferDataPort0);
+
+			/* Check Status.
+			 * After the data is read, the Transfer Complete bit should be on
+			 */
+			retries = RETRIES_LARGE;
+			do {
+				int_reg = sdstd_rreg16(sd, SD_IntrStatus);
+			} while (--retries && (GFIELD(int_reg, INTSTAT_XFER_COMPLETE) == 0));
+
+			/* Check for any errors from the data phase */
+			if (sdstd_check_errs(sd, SDIOH_CMD_53, cmd_arg))
+				return ERROR;
+
+			if (!retries) {
+				sd_err(("%s: Timeout on xfer complete: "
+				        "intr 0x%04x err 0x%04x state 0x%08x\n",
+				        __FUNCTION__, int_reg,
+				        sdstd_rreg16(sd, SD_ErrorIntrStatus),
+				        sdstd_rreg(sd, SD_PresentState)));
+				return (ERROR);
+			}
+
+			sdstd_wreg16(sd, SD_IntrStatus, SFIELD(0, INTSTAT_XFER_COMPLETE, 1));
+		}
+	}
+	if (sd->polled_mode) {
+		if (regsize == 2)
+			*data &= 0xffff;
+	}
+	return SUCCESS;
+}
+
+bool
+check_client_intr(sdioh_info_t *sd)
+{
+	uint16 raw_int, cur_int, old_int;
+
+	raw_int = sdstd_rreg16(sd, SD_IntrStatus);
+	cur_int = raw_int & sd->intmask;
+
+	if (!cur_int) {
+		/* Not an error -- might share interrupts... */
+		return FALSE;
+	}
+
+	if (GFIELD(cur_int, INTSTAT_CARD_INT)) {
+		old_int = sdstd_rreg16(sd, SD_IntrStatusEnable);
+		sdstd_wreg16(sd, SD_IntrStatusEnable, SFIELD(old_int, INTSTAT_CARD_INT, 0));
+
+		if (sd->client_intr_enabled && sd->use_client_ints) {
+			sd->intrcount++;
+			ASSERT(sd->intr_handler);
+			ASSERT(sd->intr_handler_arg);
+			(sd->intr_handler)(sd->intr_handler_arg);
+		} else {
+			sd_err(("%s: Not ready for intr: enabled %d, handler %p\n",
+			        __FUNCTION__, sd->client_intr_enabled, sd->intr_handler));
+		}
+		sdstd_wreg16(sd, SD_IntrStatusEnable, old_int);
+	} else {
+		/* Local interrupt: disable, set flag, and save intrstatus */
+		sdstd_wreg16(sd, SD_IntrSignalEnable, 0);
+		sdstd_wreg16(sd, SD_ErrorIntrSignalEnable, 0);
+		sd->local_intrcount++;
+		sd->got_hcint = TRUE;
+		sd->last_intrstatus = cur_int;
+	}
+
+	return TRUE;
+}
+
+void
+sdstd_spinbits(sdioh_info_t *sd, uint16 norm, uint16 err)
+{
+	uint16 int_reg, err_reg;
+	int retries = RETRIES_LARGE;
+
+	do {
+		int_reg = sdstd_rreg16(sd, SD_IntrStatus);
+		err_reg = sdstd_rreg16(sd, SD_ErrorIntrStatus);
+	} while (--retries && !(int_reg & norm) && !(err_reg & err));
+
+	norm |= sd->intmask;
+	if (err_reg & err)
+		norm = SFIELD(norm, INTSTAT_ERROR_INT, 1);
+	sd->last_intrstatus = int_reg & norm;
+}
+
+/* write a client register */
+static int
+sdstd_card_regwrite(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 data)
+{
+	int status;
+	uint32 cmd_arg, rsp5, flags;
+
+	cmd_arg = 0;
+
+	if ((func == 0) || (regsize == 1)) {
+		cmd_arg = SFIELD(cmd_arg, CMD52_FUNCTION, func);
+		cmd_arg = SFIELD(cmd_arg, CMD52_REG_ADDR, regaddr);
+		cmd_arg = SFIELD(cmd_arg, CMD52_RW_FLAG, SDIOH_XFER_TYPE_WRITE);
+		cmd_arg = SFIELD(cmd_arg, CMD52_RAW, 0);
+		cmd_arg = SFIELD(cmd_arg, CMD52_DATA, data & 0xff);
+		if ((status = sdstd_cmd_issue(sd, USE_DMA(sd), SDIOH_CMD_52, cmd_arg))
+		    != SUCCESS)
+			return status;
+
+		sdstd_cmd_getrsp(sd, &rsp5, 1);
+		flags = GFIELD(rsp5, RSP5_FLAGS);
+		if (flags && (flags != 0x10))
+			sd_err(("%s: rsp5.rsp5.flags = 0x%x, expecting 0x10\n",
+			        __FUNCTION__,  flags));
+	}
+	else {
+		cmd_arg = SFIELD(cmd_arg, CMD53_BYTE_BLK_CNT, regsize);
+		cmd_arg = SFIELD(cmd_arg, CMD53_OP_CODE, 1);
+		cmd_arg = SFIELD(cmd_arg, CMD53_BLK_MODE, 0);
+		cmd_arg = SFIELD(cmd_arg, CMD53_FUNCTION, func);
+		cmd_arg = SFIELD(cmd_arg, CMD53_REG_ADDR, regaddr);
+		cmd_arg = SFIELD(cmd_arg, CMD53_RW_FLAG, SDIOH_XFER_TYPE_WRITE);
+
+		sd->data_xfer_count = regsize;
+
+		/* sdstd_cmd_issue() returns with the command complete bit
+		 * in the ISR already cleared
+		 */
+		if ((status = sdstd_cmd_issue(sd, USE_DMA(sd), SDIOH_CMD_53, cmd_arg))
+		    != SUCCESS)
+			return status;
+
+		sdstd_cmd_getrsp(sd, &rsp5, 1);
+
+		if (GFIELD(rsp5, RSP5_FLAGS) != 0x10)
+			sd_err(("%s: rsp5 flags = 0x%x, expecting 0x10\n",
+			        __FUNCTION__,  GFIELD(rsp5, RSP5_FLAGS)));
+		if (GFIELD(rsp5, RSP5_STUFF))
+			sd_err(("%s: rsp5 stuff is 0x%x: expecting 0\n",
+			        __FUNCTION__, GFIELD(rsp5, RSP5_STUFF)));
+
+		if (sd->polled_mode) {
+			uint16 int_reg;
+			int retries = RETRIES_LARGE;
+
+			/* Wait for Write Buffer to become ready */
+			do {
+				int_reg = sdstd_rreg16(sd, SD_IntrStatus);
+			} while (--retries && (GFIELD(int_reg, INTSTAT_BUF_WRITE_READY) == 0));
+
+			if (!retries) {
+				sd_err(("%s: Timeout on Buf_Write_Ready: intStat: 0x%x "
+				        "errint: 0x%x PresentState 0x%x\n",
+				        __FUNCTION__, int_reg,
+				        sdstd_rreg16(sd, SD_ErrorIntrStatus),
+				        sdstd_rreg(sd, SD_PresentState)));
+				sdstd_check_errs(sd, SDIOH_CMD_53, cmd_arg);
+				return (ERROR);
+			}
+			/* Clear Write Buf Ready bit */
+			int_reg = 0;
+			int_reg = SFIELD(int_reg, INTSTAT_BUF_WRITE_READY, 1);
+			sdstd_wreg16(sd, SD_IntrStatus, int_reg);
+
+			/* At this point we have Buffer Ready, so write the data */
+			if (regsize == 2)
+				sdstd_wreg16(sd, SD_BufferDataPort0, (uint16) data);
+			else
+				sdstd_wreg(sd, SD_BufferDataPort0, data);
+
+			/* Wait for Transfer Complete */
+			retries = RETRIES_LARGE;
+			do {
+				int_reg = sdstd_rreg16(sd, SD_IntrStatus);
+			} while (--retries && (GFIELD(int_reg, INTSTAT_XFER_COMPLETE) == 0));
+
+			/* Check for any errors from the data phase */
+			if (sdstd_check_errs(sd, SDIOH_CMD_53, cmd_arg))
+				return ERROR;
+
+			if (retries == 0) {
+				sd_err(("%s: Timeout for xfer complete; State = 0x%x, "
+				        "intr state=0x%x, Errintstatus 0x%x rcnt %d, tcnt %d\n",
+				        __FUNCTION__, sdstd_rreg(sd, SD_PresentState),
+				        int_reg, sdstd_rreg16(sd, SD_ErrorIntrStatus),
+				        sd->r_cnt, sd->t_cnt));
+			}
+			/* Clear the status bits */
+			sdstd_wreg16(sd, SD_IntrStatus, SFIELD(int_reg, INTSTAT_CARD_INT, 0));
+		}
+	}
+	return SUCCESS;
+}
+
+void
+sdstd_cmd_getrsp(sdioh_info_t *sd, uint32 *rsp_buffer, int count /* num 32 bit words */)
+{
+	int rsp_count;
+	int respaddr = SD_Response0;
+
+	if (count > 4)
+		count = 4;
+
+	for (rsp_count = 0; rsp_count < count; rsp_count++) {
+		*rsp_buffer++ = sdstd_rreg(sd, respaddr);
+		respaddr += 4;
+	}
+}
+
+static int
+sdstd_cmd_issue(sdioh_info_t *sdioh_info, bool use_dma, uint32 cmd, uint32 arg)
+{
+	uint16 cmd_reg;
+	int retries;
+	uint32 cmd_arg;
+	uint16 xfer_reg = 0;
+
+
+	if ((sdioh_info->sd_mode == SDIOH_MODE_SPI) &&
+	    ((cmd == SDIOH_CMD_3) || (cmd == SDIOH_CMD_7) || (cmd == SDIOH_CMD_15))) {
+		sd_err(("%s: Cmd %d is not for SPI\n", __FUNCTION__, cmd));
+		return ERROR;
+	}
+
+	retries = RETRIES_SMALL;
+	while ((GFIELD(sdstd_rreg(sdioh_info, SD_PresentState), PRES_CMD_INHIBIT)) && --retries) {
+		if (retries == RETRIES_SMALL)
+			sd_err(("%s: Waiting for Command Inhibit cmd = %d 0x%x\n",
+			        __FUNCTION__, cmd, sdstd_rreg(sdioh_info, SD_PresentState)));
+	}
+	if (!retries) {
+		sd_err(("%s: Command Inhibit timeout\n", __FUNCTION__));
+		if (trap_errs)
+			ASSERT(0);
+		return ERROR;
+	}
+
+
+	cmd_reg = 0;
+	switch (cmd) {
+	case SDIOH_CMD_0:       /* Set Card to Idle State - No Response */
+		sd_data(("%s: CMD%d\n", __FUNCTION__, cmd));
+		cmd_reg = SFIELD(cmd_reg, CMD_RESP_TYPE, RESP_TYPE_NONE);
+		cmd_reg = SFIELD(cmd_reg, CMD_CRC_EN, 0);
+		cmd_reg = SFIELD(cmd_reg, CMD_INDEX_EN, 0);
+		cmd_reg = SFIELD(cmd_reg, CMD_DATA_EN, 0);
+		cmd_reg = SFIELD(cmd_reg, CMD_TYPE, CMD_TYPE_NORMAL);
+		cmd_reg = SFIELD(cmd_reg, CMD_INDEX, cmd);
+		break;
+
+	case SDIOH_CMD_3:	/* Ask card to send RCA - Response R6 */
+		sd_data(("%s: CMD%d\n", __FUNCTION__, cmd));
+		cmd_reg = SFIELD(cmd_reg, CMD_RESP_TYPE, RESP_TYPE_48);
+		cmd_reg = SFIELD(cmd_reg, CMD_CRC_EN, 0);
+		cmd_reg = SFIELD(cmd_reg, CMD_INDEX_EN, 0);
+		cmd_reg = SFIELD(cmd_reg, CMD_DATA_EN, 0);
+		cmd_reg = SFIELD(cmd_reg, CMD_TYPE, CMD_TYPE_NORMAL);
+		cmd_reg = SFIELD(cmd_reg, CMD_INDEX, cmd);
+		break;
+
+	case SDIOH_CMD_5:	/* Send Operation condition - Response R4 */
+		sd_data(("%s: CMD%d\n", __FUNCTION__, cmd));
+		cmd_reg = SFIELD(cmd_reg, CMD_RESP_TYPE, RESP_TYPE_48);
+		cmd_reg = SFIELD(cmd_reg, CMD_CRC_EN, 0);
+		cmd_reg = SFIELD(cmd_reg, CMD_INDEX_EN, 0);
+		cmd_reg = SFIELD(cmd_reg, CMD_DATA_EN, 0);
+		cmd_reg = SFIELD(cmd_reg, CMD_TYPE, CMD_TYPE_NORMAL);
+		cmd_reg = SFIELD(cmd_reg, CMD_INDEX, cmd);
+		break;
+
+	case SDIOH_CMD_7:	/* Select card - Response R1 */
+		sd_data(("%s: CMD%d\n", __FUNCTION__, cmd));
+		cmd_reg = SFIELD(cmd_reg, CMD_RESP_TYPE, RESP_TYPE_48);
+		cmd_reg = SFIELD(cmd_reg, CMD_CRC_EN, 1);
+		cmd_reg = SFIELD(cmd_reg, CMD_INDEX_EN, 1);
+		cmd_reg = SFIELD(cmd_reg, CMD_DATA_EN, 0);
+		cmd_reg = SFIELD(cmd_reg, CMD_TYPE, CMD_TYPE_NORMAL);
+		cmd_reg = SFIELD(cmd_reg, CMD_INDEX, cmd);
+		break;
+
+	case SDIOH_CMD_15:	/* Set card to inactive state - Response None */
+		sd_data(("%s: CMD%d\n", __FUNCTION__, cmd));
+		cmd_reg = SFIELD(cmd_reg, CMD_RESP_TYPE, RESP_TYPE_NONE);
+		cmd_reg = SFIELD(cmd_reg, CMD_CRC_EN, 0);
+		cmd_reg = SFIELD(cmd_reg, CMD_INDEX_EN, 0);
+		cmd_reg = SFIELD(cmd_reg, CMD_DATA_EN, 0);
+		cmd_reg = SFIELD(cmd_reg, CMD_TYPE, CMD_TYPE_NORMAL);
+		cmd_reg = SFIELD(cmd_reg, CMD_INDEX, cmd);
+		break;
+
+	case SDIOH_CMD_52:	/* IO R/W Direct (single byte) - Response R5 */
+
+		sd_data(("%s: CMD52 func(%d) addr(0x%x) %s data(0x%x)\n",
+			__FUNCTION__,
+			GFIELD(arg, CMD52_FUNCTION),
+			GFIELD(arg, CMD52_REG_ADDR),
+			GFIELD(arg, CMD52_RW_FLAG) ? "W" : "R",
+			GFIELD(arg, CMD52_DATA)));
+
+		cmd_reg = SFIELD(cmd_reg, CMD_RESP_TYPE, RESP_TYPE_48);
+		cmd_reg = SFIELD(cmd_reg, CMD_CRC_EN, 1);
+		cmd_reg = SFIELD(cmd_reg, CMD_INDEX_EN, 1);
+		cmd_reg = SFIELD(cmd_reg, CMD_DATA_EN, 0);
+		cmd_reg = SFIELD(cmd_reg, CMD_TYPE, CMD_TYPE_NORMAL);
+		cmd_reg = SFIELD(cmd_reg, CMD_INDEX, cmd);
+		break;
+
+	case SDIOH_CMD_53:	/* IO R/W Extended (multiple bytes/blocks) */
+
+		sd_data(("%s: CMD53 func(%d) addr(0x%x) %s mode(%s) cnt(%d), %s\n",
+			__FUNCTION__,
+			GFIELD(arg, CMD53_FUNCTION),
+			GFIELD(arg, CMD53_REG_ADDR),
+			GFIELD(arg, CMD53_RW_FLAG) ? "W" : "R",
+			GFIELD(arg, CMD53_BLK_MODE) ? "Block" : "Byte",
+			GFIELD(arg, CMD53_BYTE_BLK_CNT),
+			GFIELD(arg, CMD53_OP_CODE) ? "Incrementing addr" : "Single addr"));
+
+		cmd_arg = arg;
+		xfer_reg = 0;
+
+		cmd_reg = SFIELD(cmd_reg, CMD_RESP_TYPE, RESP_TYPE_48);
+		cmd_reg = SFIELD(cmd_reg, CMD_CRC_EN, 1);
+		cmd_reg = SFIELD(cmd_reg, CMD_INDEX_EN, 1);
+		cmd_reg = SFIELD(cmd_reg, CMD_DATA_EN, 1);
+		cmd_reg = SFIELD(cmd_reg, CMD_TYPE, CMD_TYPE_NORMAL);
+		cmd_reg = SFIELD(cmd_reg, CMD_INDEX, cmd);
+
+		use_dma = USE_DMA(sdioh_info) && GFIELD(cmd_arg, CMD53_BLK_MODE);
+
+		if (GFIELD(cmd_arg, CMD53_BLK_MODE)) {
+			uint16 blocksize;
+			uint16 blockcount;
+			int func;
+
+			ASSERT(sdioh_info->sd_blockmode);
+
+			func = GFIELD(cmd_arg, CMD53_FUNCTION);
+			blocksize = MIN((int)sdioh_info->data_xfer_count,
+			                sdioh_info->client_block_size[func]);
+			blockcount = GFIELD(cmd_arg, CMD53_BYTE_BLK_CNT);
+
+			/* data_xfer_cnt is already setup so that for multiblock mode,
+			 * it is the entire buffer length.  For non-block or single block,
+			 * it is < 64 bytes
+			 */
+			if (use_dma) {
+				switch (sdioh_info->sd_dma_mode) {
+				case DMA_MODE_SDMA:
+					sd_dma(("%s: SDMA: SysAddr reg was 0x%x now 0x%x\n",
+					      __FUNCTION__, sdstd_rreg(sdioh_info, SD_SysAddr),
+					     (uint32)sdioh_info->dma_phys));
+				sdstd_wreg(sdioh_info, SD_SysAddr, sdioh_info->dma_phys);
+					break;
+				case DMA_MODE_ADMA1:
+				case DMA_MODE_ADMA2:
+					sd_dma(("%s: ADMA: Using ADMA\n", __FUNCTION__));
+						sd_create_adma_descriptor(sdioh_info, 0,
+						sdioh_info->dma_phys, blockcount*blocksize,
+						ADMA2_ATTRIBUTE_VALID | ADMA2_ATTRIBUTE_END |
+						ADMA2_ATTRIBUTE_INT | ADMA2_ATTRIBUTE_ACT_TRAN);
+					/* Dump descriptor if DMA debugging is enabled. */
+					if (sd_msglevel & SDH_DMA_VAL) {
+						sd_dump_adma_dscr(sdioh_info);
+					}
+
+					sdstd_wreg(sdioh_info, SD_ADMA_SysAddr,
+					           sdioh_info->adma2_dscr_phys);
+					break;
+				default:
+					sd_err(("%s: unsupported DMA mode %d.\n",
+						__FUNCTION__, sdioh_info->sd_dma_mode));
+					break;
+				}
+			}
+
+			sd_trace(("%s: Setting block count %d, block size %d bytes\n",
+			          __FUNCTION__, blockcount, blocksize));
+			sdstd_wreg16(sdioh_info, SD_BlockSize, blocksize);
+			sdstd_wreg16(sdioh_info, SD_BlockCount, blockcount);
+
+			xfer_reg = SFIELD(xfer_reg, XFER_DMA_ENABLE, use_dma);
+
+			if (sdioh_info->client_block_size[func] != blocksize)
+				set_client_block_size(sdioh_info, 1, blocksize);
+
+			if (blockcount > 1) {
+				xfer_reg = SFIELD(xfer_reg, XFER_MULTI_BLOCK, 1);
+				xfer_reg = SFIELD(xfer_reg, XFER_BLK_COUNT_EN, 1);
+				xfer_reg = SFIELD(xfer_reg, XFER_CMD_12_EN, 0);
+			} else {
+				xfer_reg = SFIELD(xfer_reg, XFER_MULTI_BLOCK, 0);
+				xfer_reg = SFIELD(xfer_reg, XFER_BLK_COUNT_EN, 0);
+				xfer_reg = SFIELD(xfer_reg, XFER_CMD_12_EN, 0);
+			}
+
+			if (GFIELD(cmd_arg, CMD53_RW_FLAG) == SDIOH_XFER_TYPE_READ)
+				xfer_reg = SFIELD(xfer_reg, XFER_DATA_DIRECTION, 1);
+			else
+				xfer_reg = SFIELD(xfer_reg, XFER_DATA_DIRECTION, 0);
+
+			retries = RETRIES_SMALL;
+			while (GFIELD(sdstd_rreg(sdioh_info, SD_PresentState),
+			              PRES_DAT_INHIBIT) && --retries)
+				sd_err(("%s: Waiting for Data Inhibit cmd = %d\n",
+				        __FUNCTION__, cmd));
+			if (!retries) {
+				sd_err(("%s: Data Inhibit timeout\n", __FUNCTION__));
+				if (trap_errs)
+					ASSERT(0);
+				return ERROR;
+			}
+			sdstd_wreg16(sdioh_info, SD_TransferMode, xfer_reg);
+
+		} else {	/* Non block mode */
+			uint16 bytes = GFIELD(cmd_arg, CMD53_BYTE_BLK_CNT);
+			/* The byte/block count field only has 9 bits,
+			 * so, to do a 512-byte bytemode transfer, this
+			 * field will contain 0, but we need to tell the
+			 * controller we're transferring 512 bytes.
+			 */
+			if (bytes == 0) bytes = 512;
+
+			if (use_dma)
+				sdstd_wreg(sdioh_info, SD_SysAddr, sdioh_info->dma_phys);
+
+			/* PCI: Transfer Mode register 0x0c */
+			xfer_reg = SFIELD(xfer_reg, XFER_DMA_ENABLE, bytes <= 4 ? 0 : use_dma);
+			xfer_reg = SFIELD(xfer_reg, XFER_CMD_12_EN, 0);
+			if (GFIELD(cmd_arg, CMD53_RW_FLAG) == SDIOH_XFER_TYPE_READ)
+				xfer_reg = SFIELD(xfer_reg, XFER_DATA_DIRECTION, 1);
+			else
+				xfer_reg = SFIELD(xfer_reg, XFER_DATA_DIRECTION, 0);
+			/* See table 2-8 Host Controller spec ver 1.00 */
+			xfer_reg = SFIELD(xfer_reg, XFER_BLK_COUNT_EN, 0); /* Dont care */
+			xfer_reg = SFIELD(xfer_reg, XFER_MULTI_BLOCK, 0);
+
+			sdstd_wreg16(sdioh_info, SD_BlockSize,  bytes);
+
+			sdstd_wreg16(sdioh_info, SD_BlockCount, 1);
+
+			retries = RETRIES_SMALL;
+			while (GFIELD(sdstd_rreg(sdioh_info, SD_PresentState),
+			              PRES_DAT_INHIBIT) && --retries)
+				sd_err(("%s: Waiting for Data Inhibit cmd = %d\n",
+				        __FUNCTION__, cmd));
+			if (!retries) {
+				sd_err(("%s: Data Inhibit timeout\n", __FUNCTION__));
+				if (trap_errs)
+					ASSERT(0);
+				return ERROR;
+			}
+			sdstd_wreg16(sdioh_info, SD_TransferMode, xfer_reg);
+		}
+		break;
+
+	default:
+		sd_err(("%s: Unknown command\n", __FUNCTION__));
+		return ERROR;
+	}
+
+	if (sdioh_info->sd_mode == SDIOH_MODE_SPI) {
+		cmd_reg = SFIELD(cmd_reg, CMD_CRC_EN, 0);
+		cmd_reg = SFIELD(cmd_reg, CMD_INDEX_EN, 0);
+	}
+
+	/* Setup and issue the SDIO command */
+	sdstd_wreg(sdioh_info, SD_Arg0, arg);
+	sdstd_wreg16(sdioh_info, SD_Command, cmd_reg);
+
+	/* If we are in polled mode, wait for the command to complete.
+	 * In interrupt mode, return immediately. The calling function will
+	 * know that the command has completed when the CMDATDONE interrupt
+	 * is asserted
+	 */
+	if (sdioh_info->polled_mode) {
+		uint16 int_reg = 0;
+		int retries = RETRIES_LARGE;
+
+		do {
+			int_reg = sdstd_rreg16(sdioh_info, SD_IntrStatus);
+		} while (--retries &&
+		         (GFIELD(int_reg, INTSTAT_ERROR_INT) == 0) &&
+		         (GFIELD(int_reg, INTSTAT_CMD_COMPLETE) == 0));
+
+		if (!retries) {
+			sd_err(("%s: CMD_COMPLETE timeout: intrStatus: 0x%x "
+			        "error stat 0x%x state 0x%x\n",
+			        __FUNCTION__, int_reg,
+			        sdstd_rreg16(sdioh_info, SD_ErrorIntrStatus),
+			        sdstd_rreg(sdioh_info, SD_PresentState)));
+
+			/* Attempt to reset CMD line when we get a CMD timeout */
+			sdstd_wreg8(sdioh_info, SD_SoftwareReset, SFIELD(0, SW_RESET_CMD, 1));
+			retries = RETRIES_LARGE;
+			do {
+				sd_trace(("%s: waiting for CMD line reset\n", __FUNCTION__));
+			} while ((GFIELD(sdstd_rreg8(sdioh_info, SD_SoftwareReset),
+			                 SW_RESET_CMD)) && retries--);
+
+			if (!retries) {
+				sd_err(("%s: Timeout waiting for CMD line reset\n", __FUNCTION__));
+			}
+
+			if (trap_errs)
+				ASSERT(0);
+			return (ERROR);
+		}
+
+		/* Clear Command Complete interrupt */
+		int_reg = SFIELD(0, INTSTAT_CMD_COMPLETE, 1);
+		sdstd_wreg16(sdioh_info, SD_IntrStatus, int_reg);
+
+		/* Check for Errors */
+		if (sdstd_check_errs(sdioh_info, cmd, arg)) {
+			if (trap_errs)
+				ASSERT(0);
+			return ERROR;
+		}
+	}
+	return SUCCESS;
+}
+
+
+static int
+sdstd_card_buf(sdioh_info_t *sd, int rw, int func, bool fifo, uint32 addr, int nbytes, uint32 *data)
+{
+	int status;
+	uint32 cmd_arg;
+	uint32 rsp5;
+	uint16 int_reg, int_bit;
+	uint flags;
+	int num_blocks, blocksize;
+	bool local_blockmode, local_dma;
+	bool read = rw == SDIOH_READ ? 1 : 0;
+	bool yield = FALSE;
+
+	ASSERT(nbytes);
+
+	cmd_arg = 0;
+
+	sd_data(("%s: %s 53 addr 0x%x, len %d bytes, r_cnt %d t_cnt %d\n",
+	         __FUNCTION__, read ? "Rd" : "Wr", addr, nbytes, sd->r_cnt, sd->t_cnt));
+
+	if (read) sd->r_cnt++; else sd->t_cnt++;
+
+	local_blockmode = sd->sd_blockmode;
+	local_dma = USE_DMA(sd);
+
+	/* Don't bother with block mode on small xfers */
+	if (nbytes < sd->client_block_size[func]) {
+		sd_data(("setting local blockmode to false: nbytes (%d) != block_size (%d)\n",
+		         nbytes, sd->client_block_size[func]));
+		local_blockmode = FALSE;
+		local_dma = FALSE;
+	}
+
+	if (local_blockmode) {
+		blocksize = MIN(sd->client_block_size[func], nbytes);
+		num_blocks = nbytes/blocksize;
+		cmd_arg = SFIELD(cmd_arg, CMD53_BYTE_BLK_CNT, num_blocks);
+		cmd_arg = SFIELD(cmd_arg, CMD53_BLK_MODE, 1);
+	} else {
+		num_blocks =  1;
+		blocksize = nbytes;
+		cmd_arg = SFIELD(cmd_arg, CMD53_BYTE_BLK_CNT, nbytes);
+		cmd_arg = SFIELD(cmd_arg, CMD53_BLK_MODE, 0);
+	}
+
+	if (local_dma && !read) {
+		bcopy(data, sd->dma_buf, nbytes);
+		sd_sync_dma(sd, read, nbytes);
+	}
+
+	if (fifo)
+		cmd_arg = SFIELD(cmd_arg, CMD53_OP_CODE, 0);
+	else
+		cmd_arg = SFIELD(cmd_arg, CMD53_OP_CODE, 1);
+
+	cmd_arg = SFIELD(cmd_arg, CMD53_FUNCTION, func);
+	cmd_arg = SFIELD(cmd_arg, CMD53_REG_ADDR, addr);
+	if (read)
+		cmd_arg = SFIELD(cmd_arg, CMD53_RW_FLAG, SDIOH_XFER_TYPE_READ);
+	else
+		cmd_arg = SFIELD(cmd_arg, CMD53_RW_FLAG, SDIOH_XFER_TYPE_WRITE);
+
+	sd->data_xfer_count = nbytes;
+
+	/* sdstd_cmd_issue() returns with the command complete bit
+	 * in the ISR already cleared
+	 */
+	if ((status = sdstd_cmd_issue(sd, local_dma, SDIOH_CMD_53, cmd_arg)) != SUCCESS) {
+		sd_err(("%s: cmd_issue failed for %s\n", __FUNCTION__, (read ? "read" : "write")));
+		return status;
+	}
+
+	sdstd_cmd_getrsp(sd, &rsp5, 1);
+
+	if ((flags = GFIELD(rsp5, RSP5_FLAGS)) != 0x10) {
+		sd_err(("%s: Rsp5: nbytes %d, dma %d blockmode %d, read %d "
+		        "numblocks %d, blocksize %d\n",
+		        __FUNCTION__, nbytes, local_dma, local_dma, read, num_blocks, blocksize));
+
+		if (flags & 1)
+			sd_err(("%s: rsp5: Command not accepted: arg out of range 0x%x, "
+			        "bytes %d dma %d\n",
+			        __FUNCTION__, flags, GFIELD(cmd_arg, CMD53_BYTE_BLK_CNT),
+			        GFIELD(cmd_arg, CMD53_BLK_MODE)));
+		if (flags & 0x8)
+			sd_err(("%s: Rsp5: General Error\n", __FUNCTION__));
+
+		sd_err(("%s: rsp5 flags = 0x%x, expecting 0x10 returning error\n",
+		        __FUNCTION__,  flags));
+		if (trap_errs)
+			ASSERT(0);
+		return ERROR;
+	}
+
+	if (GFIELD(rsp5, RSP5_STUFF))
+		sd_err(("%s: rsp5 stuff is 0x%x: expecting 0\n",
+		        __FUNCTION__, GFIELD(rsp5, RSP5_STUFF)));
+
+#ifdef BCMSDYIELD
+	yield = sd_yieldcpu && ((uint)nbytes >= sd_minyield);
+#endif
+
+	if (!local_dma) {
+		int bytes, i;
+		uint32 tmp;
+		for (i = 0; i < num_blocks; i++) {
+			int words;
+
+			/* Decide which status bit we're waiting for */
+			if (read)
+				int_bit = SFIELD(0, INTSTAT_BUF_READ_READY, 1);
+			else
+				int_bit = SFIELD(0, INTSTAT_BUF_WRITE_READY, 1);
+
+			/* If not on, wait for it (or for xfer error) */
+			int_reg = sdstd_rreg16(sd, SD_IntrStatus);
+			if (!(int_reg & int_bit))
+				int_reg = sdstd_waitbits(sd, int_bit, ERRINT_TRANSFER_ERRS, yield);
+
+			/* Confirm we got the bit w/o error */
+			if (!(int_reg & int_bit) || GFIELD(int_reg, INTSTAT_ERROR_INT)) {
+				sd_err(("%s: Error or timeout for Buf_%s_Ready: intStat: 0x%x "
+				        "errint: 0x%x PresentState 0x%x\n",
+				        __FUNCTION__, read ? "Read" : "Write", int_reg,
+				        sdstd_rreg16(sd, SD_ErrorIntrStatus),
+				        sdstd_rreg(sd, SD_PresentState)));
+				sdstd_dumpregs(sd);
+				sdstd_check_errs(sd, SDIOH_CMD_53, cmd_arg);
+				return (ERROR);
+			}
+
+			/* Clear Buf Ready bit */
+			sdstd_wreg16(sd, SD_IntrStatus, int_bit);
+
+			/* At this point we have Buffer Ready, write the data 4 bytes at a time */
+			for (words = blocksize/4; words; words--) {
+				if (read)
+					*data = sdstd_rreg(sd, SD_BufferDataPort0);
+				else
+					sdstd_wreg(sd, SD_BufferDataPort0, *data);
+				data++;
+			}
+
+			bytes = blocksize % 4;
+
+			/* If no leftover bytes, go to next block */
+			if (!bytes)
+				continue;
+
+			switch (bytes) {
+			case 1:
+				/* R/W 8 bits */
+				if (read)
+					*(data++) = (uint32)(sdstd_rreg8(sd, SD_BufferDataPort0));
+				else
+					sdstd_wreg8(sd, SD_BufferDataPort0,
+					            (uint8)(*(data++) & 0xff));
+				break;
+			case 2:
+				/* R/W 16 bits */
+				if (read)
+					*(data++) = (uint32)sdstd_rreg16(sd, SD_BufferDataPort0);
+				else
+					sdstd_wreg16(sd, SD_BufferDataPort0, (uint16)(*(data++)));
+				break;
+			case 3:
+				/* R/W 24 bits:
+				 * SD_BufferDataPort0[0-15] | SD_BufferDataPort1[16-23]
+				 */
+				if (read) {
+					tmp = (uint32)sdstd_rreg16(sd, SD_BufferDataPort0);
+					tmp |= ((uint32)(sdstd_rreg8(sd,
+					                             SD_BufferDataPort1)) << 16);
+					*(data++) = tmp;
+				} else {
+					tmp = *(data++);
+					sdstd_wreg16(sd, SD_BufferDataPort0, (uint16)tmp & 0xffff);
+					sdstd_wreg8(sd, SD_BufferDataPort1,
+					            (uint8)((tmp >> 16) & 0xff));
+				}
+				break;
+			default:
+				sd_err(("%s: Unexpected bytes leftover %d\n",
+				        __FUNCTION__, bytes));
+				ASSERT(0);
+				break;
+			}
+		}
+	}	/* End PIO processing */
+
+	/* Wait for Transfer Complete or Transfer Error */
+	int_bit = SFIELD(0, INTSTAT_XFER_COMPLETE, 1);
+
+	/* If not on, wait for it (or for xfer error) */
+	int_reg = sdstd_rreg16(sd, SD_IntrStatus);
+	if (!(int_reg & int_bit))
+		int_reg = sdstd_waitbits(sd, int_bit, ERRINT_TRANSFER_ERRS, yield);
+
+	/* Check for any errors from the data phase */
+	if (sdstd_check_errs(sd, SDIOH_CMD_53, cmd_arg))
+		return ERROR;
+
+	/* May have gotten a software timeout if not blocking? */
+	int_reg = sdstd_rreg16(sd, SD_IntrStatus);
+	if (!(int_reg & int_bit)) {
+		sd_err(("%s: Error or Timeout for xfer complete; %s, dma %d, State 0x%08x, "
+		        "intr 0x%04x, Err 0x%04x, len = %d, rcnt %d, tcnt %d\n",
+		        __FUNCTION__, read ? "R" : "W", local_dma,
+		        sdstd_rreg(sd, SD_PresentState), int_reg,
+		        sdstd_rreg16(sd, SD_ErrorIntrStatus), nbytes,
+		        sd->r_cnt, sd->t_cnt));
+		sdstd_dumpregs(sd);
+		return ERROR;
+	}
+
+	/* Clear the status bits */
+	int_reg = int_bit;
+	if (local_dma) {
+		/* DMA Complete */
+		/* Reads in particular don't have DMA_COMPLETE set */
+		int_reg = SFIELD(int_reg, INTSTAT_DMA_INT, 1);
+	}
+	sdstd_wreg16(sd, SD_IntrStatus, int_reg);
+
+	/* Fetch data */
+	if (local_dma && read) {
+		sd_sync_dma(sd, read, nbytes);
+		bcopy(sd->dma_buf, data, nbytes);
+	}
+	return SUCCESS;
+}
+
+static int
+set_client_block_size(sdioh_info_t *sd, int func, int block_size)
+{
+	int base;
+	int err = 0;
+
+
+	sd_err(("%s: Setting block size %d, func %d\n", __FUNCTION__, block_size, func));
+	sd->client_block_size[func] = block_size;
+
+	/* Set the block size in the SDIO Card register */
+	base = func * SDIOD_FBR_SIZE;
+	err = sdstd_card_regwrite(sd, 0, base+SDIOD_CCCR_BLKSIZE_0, 1, block_size & 0xff);
+	if (!err) {
+		err = sdstd_card_regwrite(sd, 0, base+SDIOD_CCCR_BLKSIZE_1, 1,
+		                          (block_size >> 8) & 0xff);
+	}
+
+	/* Do not set the block size in the SDIO Host register, that
+	 * is func dependent and will get done on an individual
+	 * transaction basis
+	 */
+
+	return (err ? BCME_SDIO_ERROR : 0);
+}
+
+/* Reset and re-initialize the device */
+int
+sdioh_sdio_reset(sdioh_info_t *si)
+{
+	uint8 hreg;
+
+	/* Reset the attached device (use slower clock for safety) */
+	sdstd_start_clock(si, 128);
+	sdstd_reset(si, 0, 1);
+
+	/* Reset portions of the host state accordingly */
+	hreg = sdstd_rreg8(si, SD_HostCntrl);
+	hreg = SFIELD(hreg, HOST_HI_SPEED_EN, 0);
+	hreg = SFIELD(hreg, HOST_DATA_WIDTH, 0);
+	si->sd_mode = SDIOH_MODE_SD1;
+
+	/* Reinitialize the card */
+	si->card_init_done = FALSE;
+	return sdstd_client_init(si);
+}
+
+
+static void
+sd_map_dma(sdioh_info_t * sd)
+{
+
+	void *va;
+
+	if ((va = DMA_ALLOC_CONSISTENT(sd->osh, SD_PAGE,
+		&sd->dma_start_phys, 0x12, 12)) == NULL) {
+		sd->sd_dma_mode = DMA_MODE_NONE;
+		sd->dma_start_buf = 0;
+		sd->dma_buf = (void *)0;
+		sd->dma_phys = 0;
+		sd->alloced_dma_size = SD_PAGE;
+		sd_err(("%s: DMA_ALLOC failed. Disabling DMA support.\n", __FUNCTION__));
+	} else {
+		sd->dma_start_buf = va;
+		sd->dma_buf = (void *)ROUNDUP((uintptr)va, SD_PAGE);
+		sd->dma_phys = ROUNDUP((sd->dma_start_phys), SD_PAGE);
+		sd->alloced_dma_size = SD_PAGE;
+		sd_err(("%s: Mapped DMA Buffer %dbytes @virt/phys: %p/0x%lx\n",
+		        __FUNCTION__, sd->alloced_dma_size, sd->dma_buf, sd->dma_phys));
+		sd_fill_dma_data_buf(sd, 0xA5);
+	}
+
+	if ((va = DMA_ALLOC_CONSISTENT(sd->osh, SD_PAGE,
+		&sd->adma2_dscr_start_phys, 0x12, 12)) == NULL) {
+		sd->sd_dma_mode = DMA_MODE_NONE;
+		sd->adma2_dscr_start_buf = 0;
+		sd->adma2_dscr_buf = (void *)0;
+		sd->adma2_dscr_phys = 0;
+		sd->alloced_adma2_dscr_size = 0;
+		sd_err(("%s: DMA_ALLOC failed for descriptor buffer. "
+		        "Disabling DMA support.\n", __FUNCTION__));
+	} else {
+		sd->adma2_dscr_start_buf = va;
+		sd->adma2_dscr_buf = (void *)ROUNDUP((uintptr)va, SD_PAGE);
+		sd->adma2_dscr_phys = ROUNDUP((sd->adma2_dscr_start_phys), SD_PAGE);
+		sd->alloced_adma2_dscr_size = SD_PAGE;
+	}
+
+	sd_err(("%s: Mapped ADMA2 Descriptor Buffer %dbytes @virt/phys: %p/0x%lx\n",
+	        __FUNCTION__, sd->alloced_adma2_dscr_size, sd->adma2_dscr_buf,
+	        sd->adma2_dscr_phys));
+	sd_clear_adma_dscr_buf(sd);
+}
+
+static void
+sd_unmap_dma(sdioh_info_t * sd)
+{
+	if (sd->dma_start_buf) {
+		DMA_FREE_CONSISTENT(sd->osh, sd->dma_start_buf, sd->alloced_dma_size,
+			sd->dma_start_phys, 0x12);
+	}
+
+	if (sd->adma2_dscr_start_buf) {
+		DMA_FREE_CONSISTENT(sd->osh, sd->adma2_dscr_start_buf, sd->alloced_adma2_dscr_size,
+		                    sd->adma2_dscr_start_phys, 0x12);
+	}
+}
+
+static void sd_clear_adma_dscr_buf(sdioh_info_t *sd)
+{
+	bzero((char *)sd->adma2_dscr_buf, SD_PAGE);
+	sd_dump_adma_dscr(sd);
+}
+
+static void sd_fill_dma_data_buf(sdioh_info_t *sd, uint8 data)
+{
+	memset((char *)sd->dma_buf, data, SD_PAGE);
+}
+
+
+static void sd_create_adma_descriptor(sdioh_info_t *sd, uint32 index,
+                                      uint32 addr_phys, uint16 length, uint16 flags)
+{
+	adma2_dscr_32b_t *adma2_dscr_table;
+	adma1_dscr_t *adma1_dscr_table;
+
+	adma2_dscr_table = sd->adma2_dscr_buf;
+	adma1_dscr_table = sd->adma2_dscr_buf;
+
+	switch (sd->sd_dma_mode) {
+		case DMA_MODE_ADMA2:
+			sd_dma(("%s: creating ADMA2 descriptor for index %d\n",
+				__FUNCTION__, index));
+
+			adma2_dscr_table[index].phys_addr = addr_phys;
+			adma2_dscr_table[index].len_attr = length << 16;
+			adma2_dscr_table[index].len_attr |= flags;
+			break;
+		case DMA_MODE_ADMA1:
+			/* ADMA1 requires two descriptors, one for len
+			 * and the other for data transfer
+			 */
+			index <<= 1;
+
+			sd_dma(("%s: creating ADMA1 descriptor for index %d\n",
+				__FUNCTION__, index));
+
+			adma1_dscr_table[index].phys_addr_attr = length << 12;
+			adma1_dscr_table[index].phys_addr_attr |= (ADMA1_ATTRIBUTE_ACT_SET |
+			                                           ADMA2_ATTRIBUTE_VALID);
+			adma1_dscr_table[index+1].phys_addr_attr = addr_phys & 0xFFFFF000;
+			adma1_dscr_table[index+1].phys_addr_attr |= (flags & 0x3f);
+			break;
+		default:
+			sd_err(("%s: cannot create ADMA descriptor for DMA mode %d\n",
+				__FUNCTION__, sd->sd_dma_mode));
+			break;
+	}
+}
+
+
+static void sd_dump_adma_dscr(sdioh_info_t *sd)
+{
+	adma2_dscr_32b_t *adma2_dscr_table;
+	adma1_dscr_t *adma1_dscr_table;
+	uint32 i = 0;
+	uint16 flags;
+	char flags_str[32];
+
+	ASSERT(sd->adma2_dscr_buf != NULL);
+
+	adma2_dscr_table = sd->adma2_dscr_buf;
+	adma1_dscr_table = sd->adma2_dscr_buf;
+
+	switch (sd->sd_dma_mode) {
+		case DMA_MODE_ADMA2:
+			sd_err(("ADMA2 Descriptor Table (%dbytes) @virt/phys: %p/0x%lx\n",
+				SD_PAGE, sd->adma2_dscr_buf, sd->adma2_dscr_phys));
+			sd_err((" #[Descr VA  ]  Buffer PA  | Len    | Flags  (5:4  2   1   0)"
+			        "     |\n"));
+			while (adma2_dscr_table->len_attr & ADMA2_ATTRIBUTE_VALID) {
+				flags = adma2_dscr_table->len_attr & 0xFFFF;
+				sprintf(flags_str, "%s%s%s%s",
+					((flags & ADMA2_ATTRIBUTE_ACT_LINK) ==
+					ADMA2_ATTRIBUTE_ACT_LINK) ? "LINK " :
+					((flags & ADMA2_ATTRIBUTE_ACT_LINK) ==
+					ADMA2_ATTRIBUTE_ACT_TRAN) ? "TRAN " :
+					((flags & ADMA2_ATTRIBUTE_ACT_LINK) ==
+					ADMA2_ATTRIBUTE_ACT_NOP) ? "NOP  " : "RSV  ",
+					(flags & ADMA2_ATTRIBUTE_INT ? "INT " : "    "),
+					(flags & ADMA2_ATTRIBUTE_END ? "END " : "    "),
+					(flags & ADMA2_ATTRIBUTE_VALID ? "VALID" : ""));
+				sd_err(("%2d[0x%p]: 0x%08x | 0x%04x | 0x%04x (%s) |\n",
+				        i, adma2_dscr_table, adma2_dscr_table->phys_addr,
+				        adma2_dscr_table->len_attr >> 16, flags, flags_str));
+				i++;
+
+				/* Follow LINK descriptors or skip to next. */
+				if ((flags & ADMA2_ATTRIBUTE_ACT_LINK) ==
+				     ADMA2_ATTRIBUTE_ACT_LINK) {
+					adma2_dscr_table = phys_to_virt(
+					    adma2_dscr_table->phys_addr);
+				} else {
+					adma2_dscr_table++;
+				}
+
+			}
+			break;
+		case DMA_MODE_ADMA1:
+			sd_err(("ADMA1 Descriptor Table (%dbytes) @virt/phys: %p/0x%lx\n",
+			         SD_PAGE, sd->adma2_dscr_buf, sd->adma2_dscr_phys));
+			sd_err((" #[Descr VA  ]  Buffer PA  | Flags  (5:4  2   1   0)     |\n"));
+
+			for (i = 0; adma1_dscr_table->phys_addr_attr & ADMA2_ATTRIBUTE_VALID; i++) {
+				flags = adma1_dscr_table->phys_addr_attr & 0x3F;
+				sprintf(flags_str, "%s%s%s%s",
+					((flags & ADMA2_ATTRIBUTE_ACT_LINK) ==
+					ADMA2_ATTRIBUTE_ACT_LINK) ? "LINK " :
+					((flags & ADMA2_ATTRIBUTE_ACT_LINK) ==
+					ADMA2_ATTRIBUTE_ACT_TRAN) ? "TRAN " :
+					((flags & ADMA2_ATTRIBUTE_ACT_LINK) ==
+					ADMA2_ATTRIBUTE_ACT_NOP) ? "NOP  " : "SET  ",
+					(flags & ADMA2_ATTRIBUTE_INT ? "INT " : "    "),
+					(flags & ADMA2_ATTRIBUTE_END ? "END " : "    "),
+					(flags & ADMA2_ATTRIBUTE_VALID ? "VALID" : ""));
+				sd_err(("%2d[0x%p]: 0x%08x | 0x%04x | (%s) |\n",
+				        i, adma1_dscr_table,
+				        adma1_dscr_table->phys_addr_attr & 0xFFFFF000,
+				        flags, flags_str));
+
+				/* Follow LINK descriptors or skip to next. */
+				if ((flags & ADMA2_ATTRIBUTE_ACT_LINK) ==
+				     ADMA2_ATTRIBUTE_ACT_LINK) {
+					adma1_dscr_table = phys_to_virt(
+						adma1_dscr_table->phys_addr_attr & 0xFFFFF000);
+				} else {
+					adma1_dscr_table++;
+				}
+			}
+			break;
+		default:
+			sd_err(("Unknown DMA Descriptor Table Format.\n"));
+			break;
+	}
+}
+
+static void sdstd_dumpregs(sdioh_info_t *sd)
+{
+	sd_err(("IntrStatus:       0x%04x ErrorIntrStatus       0x%04x\n",
+	            sdstd_rreg16(sd, SD_IntrStatus),
+	            sdstd_rreg16(sd, SD_ErrorIntrStatus)));
+	sd_err(("IntrStatusEnable: 0x%04x ErrorIntrStatusEnable 0x%04x\n",
+	            sdstd_rreg16(sd, SD_IntrStatusEnable),
+	            sdstd_rreg16(sd, SD_ErrorIntrStatusEnable)));
+	sd_err(("IntrSignalEnable: 0x%04x ErrorIntrSignalEnable 0x%04x\n",
+	            sdstd_rreg16(sd, SD_IntrSignalEnable),
+	            sdstd_rreg16(sd, SD_ErrorIntrSignalEnable)));
+}
diff --git a/drivers/net/wireless/bcm4329/bcmsdstd_linux.c b/drivers/net/wireless/bcm4329/bcmsdstd_linux.c
new file mode 100644
index 0000000..a8b98e2
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/bcmsdstd_linux.c
@@ -0,0 +1,251 @@
+/*
+ *  'Standard' SDIO HOST CONTROLLER driver - linux portion
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmsdstd_linux.c,v 1.11.18.2.16.1 2010/08/17 17:03:13 Exp $
+ */
+
+#include <typedefs.h>
+#include <pcicfg.h>
+#include <bcmutils.h>
+#include <sdio.h>	/* SDIO Specs */
+#include <bcmsdbus.h>	/* bcmsdh to/from specific controller APIs */
+#include <sdiovar.h>	/* to get msglevel bit values */
+
+#include <linux/sched.h>	/* request_irq() */
+
+#include <bcmsdstd.h>
+
+struct sdos_info {
+	sdioh_info_t *sd;
+	spinlock_t lock;
+	wait_queue_head_t intr_wait_queue;
+};
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+#define BLOCKABLE()	(!in_atomic())
+#else
+#define BLOCKABLE()	(!in_interrupt())
+#endif
+
+/* Interrupt handler */
+static irqreturn_t
+sdstd_isr(int irq, void *dev_id
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+, struct pt_regs *ptregs
+#endif
+)
+{
+	sdioh_info_t *sd;
+	struct sdos_info *sdos;
+	bool ours;
+
+	sd = (sdioh_info_t *)dev_id;
+
+	if (!sd->card_init_done) {
+		sd_err(("%s: Hey Bogus intr...not even initted: irq %d\n", __FUNCTION__, irq));
+		return IRQ_RETVAL(FALSE);
+	} else {
+		ours = check_client_intr(sd);
+
+		/* For local interrupts, wake the waiting process */
+		if (ours && sd->got_hcint) {
+			sd_trace(("INTR->WAKE\n"));
+			sdos = (struct sdos_info *)sd->sdos_info;
+			wake_up_interruptible(&sdos->intr_wait_queue);
+		}
+		return IRQ_RETVAL(ours);
+	}
+}
+
+/* Register with Linux for interrupts */
+int
+sdstd_register_irq(sdioh_info_t *sd, uint irq)
+{
+	sd_trace(("Entering %s: irq == %d\n", __FUNCTION__, irq));
+	if (request_irq(irq, sdstd_isr, IRQF_SHARED, "bcmsdstd", sd) < 0) {
+		sd_err(("%s: request_irq() failed\n", __FUNCTION__));
+		return ERROR;
+	}
+	return SUCCESS;
+}
+
+/* Free Linux irq */
+void
+sdstd_free_irq(uint irq, sdioh_info_t *sd)
+{
+	free_irq(irq, sd);
+}
+
+/* Map Host controller registers */
+
+uint32 *
+sdstd_reg_map(osl_t *osh, int32 addr, int size)
+{
+	return (uint32 *)REG_MAP(addr, size);
+}
+
+void
+sdstd_reg_unmap(osl_t *osh, int32 addr, int size)
+{
+	REG_UNMAP((void*)(uintptr)addr);
+}
+
+int
+sdstd_osinit(sdioh_info_t *sd)
+{
+	struct sdos_info *sdos;
+
+	sdos = (struct sdos_info*)MALLOC(sd->osh, sizeof(struct sdos_info));
+	sd->sdos_info = (void*)sdos;
+	if (sdos == NULL)
+		return BCME_NOMEM;
+
+	sdos->sd = sd;
+	spin_lock_init(&sdos->lock);
+	init_waitqueue_head(&sdos->intr_wait_queue);
+	return BCME_OK;
+}
+
+void
+sdstd_osfree(sdioh_info_t *sd)
+{
+	struct sdos_info *sdos;
+	ASSERT(sd && sd->sdos_info);
+
+	sdos = (struct sdos_info *)sd->sdos_info;
+	MFREE(sd->osh, sdos, sizeof(struct sdos_info));
+}
+
+/* Interrupt enable/disable */
+SDIOH_API_RC
+sdioh_interrupt_set(sdioh_info_t *sd, bool enable)
+{
+	ulong flags;
+	struct sdos_info *sdos;
+
+	sd_trace(("%s: %s\n", __FUNCTION__, enable ? "Enabling" : "Disabling"));
+
+	sdos = (struct sdos_info *)sd->sdos_info;
+	ASSERT(sdos);
+
+	if (!(sd->host_init_done && sd->card_init_done)) {
+		sd_err(("%s: Card & Host are not initted - bailing\n", __FUNCTION__));
+		return SDIOH_API_RC_FAIL;
+	}
+
+	if (enable && !(sd->intr_handler && sd->intr_handler_arg)) {
+		sd_err(("%s: no handler registered, will not enable\n", __FUNCTION__));
+		return SDIOH_API_RC_FAIL;
+	}
+
+	/* Ensure atomicity for enable/disable calls */
+	spin_lock_irqsave(&sdos->lock, flags);
+
+	sd->client_intr_enabled = enable;
+	if (enable && !sd->lockcount)
+		sdstd_devintr_on(sd);
+	else
+		sdstd_devintr_off(sd);
+
+	spin_unlock_irqrestore(&sdos->lock, flags);
+
+	return SDIOH_API_RC_SUCCESS;
+}
+
+/* Protect against reentrancy (disable device interrupts while executing) */
+void
+sdstd_lock(sdioh_info_t *sd)
+{
+	ulong flags;
+	struct sdos_info *sdos;
+
+	sdos = (struct sdos_info *)sd->sdos_info;
+	ASSERT(sdos);
+
+	sd_trace(("%s: %d\n", __FUNCTION__, sd->lockcount));
+
+	spin_lock_irqsave(&sdos->lock, flags);
+	if (sd->lockcount) {
+		sd_err(("%s: Already locked! called from %p\n",
+		       __FUNCTION__,
+		       __builtin_return_address(0)));
+		ASSERT(sd->lockcount == 0);
+	}
+	sdstd_devintr_off(sd);
+	sd->lockcount++;
+	spin_unlock_irqrestore(&sdos->lock, flags);
+}
+
+/* Enable client interrupt */
+void
+sdstd_unlock(sdioh_info_t *sd)
+{
+	ulong flags;
+	struct sdos_info *sdos;
+
+	sd_trace(("%s: %d, %d\n", __FUNCTION__, sd->lockcount, sd->client_intr_enabled));
+	ASSERT(sd->lockcount > 0);
+
+	sdos = (struct sdos_info *)sd->sdos_info;
+	ASSERT(sdos);
+
+	spin_lock_irqsave(&sdos->lock, flags);
+	if (--sd->lockcount == 0 && sd->client_intr_enabled) {
+		sdstd_devintr_on(sd);
+	}
+	spin_unlock_irqrestore(&sdos->lock, flags);
+}
+
+uint16
+sdstd_waitbits(sdioh_info_t *sd, uint16 norm, uint16 err, bool yield)
+{
+	struct sdos_info *sdos;
+
+	sdos = (struct sdos_info *)sd->sdos_info;
+
+#ifndef BCMSDYIELD
+	ASSERT(!yield);
+#endif
+	sd_trace(("%s: int 0x%02x err 0x%02x yield %d canblock %d\n",
+	          __FUNCTION__, norm, err, yield, BLOCKABLE()));
+
+	/* Clear the "interrupt happened" flag and last intrstatus */
+	sd->got_hcint = FALSE;
+	sd->last_intrstatus = 0;
+
+#ifdef BCMSDYIELD
+	if (yield && BLOCKABLE()) {
+		/* Enable interrupts, wait for the indication, then disable */
+		sdstd_intrs_on(sd, norm, err);
+		wait_event_interruptible(sdos->intr_wait_queue, (sd->got_hcint));
+		sdstd_intrs_off(sd, norm, err);
+	} else
+#endif /* BCMSDYIELD */
+	{
+		sdstd_spinbits(sd, norm, err);
+	}
+
+	sd_trace(("%s: last_intrstatus 0x%04x\n", __FUNCTION__, sd->last_intrstatus));
+
+	return sd->last_intrstatus;
+}
diff --git a/drivers/net/wireless/bcm4329/bcmspibrcm.c b/drivers/net/wireless/bcm4329/bcmspibrcm.c
new file mode 100644
index 0000000..0f131a4
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/bcmspibrcm.c
@@ -0,0 +1,1726 @@
+/*
+ * Broadcom BCMSDH to gSPI Protocol Conversion Layer
+ *
+ * Copyright (C) 2010, Broadcom Corporation
+ * All Rights Reserved.
+ * 
+ * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation;
+ * the contents of this file may not be disclosed to third parties, copied
+ * or duplicated in any form, in whole or in part, without the prior
+ * written permission of Broadcom Corporation.
+ *
+ * $Id: bcmspibrcm.c,v 1.11.2.10.2.9.6.11 2009/05/21 13:21:57 Exp $
+ */
+
+#define HSMODE
+
+#include <typedefs.h>
+
+#include <bcmdevs.h>
+#include <bcmendian.h>
+#include <bcmutils.h>
+#include <osl.h>
+#include <hndsoc.h>
+#include <siutils.h>
+#include <sbchipc.h>
+#include <sbsdio.h>
+#include <spid.h>
+
+#include <bcmsdbus.h>	/* bcmsdh to/from specific controller APIs */
+#include <sdiovar.h>	/* ioctl/iovars */
+#include <sdio.h>
+
+#include <pcicfg.h>
+
+
+#include <bcmspibrcm.h>
+#include <bcmspi.h>
+
+#define F0_RESPONSE_DELAY	16
+#define F1_RESPONSE_DELAY	16
+#define F2_RESPONSE_DELAY	F0_RESPONSE_DELAY
+
+#define CMDLEN		4
+
+#define DWORDMODE_ON (sd->chip == BCM4329_CHIP_ID) && (sd->chiprev == 2) && (sd->dwordmode == TRUE)
+
+/* Globals */
+uint sd_msglevel = 0;
+
+uint sd_hiok = FALSE;		/* Use hi-speed mode if available? */
+uint sd_sdmode = SDIOH_MODE_SPI;		/* Use SD4 mode by default */
+uint sd_f2_blocksize = 64;		/* Default blocksize */
+
+
+uint sd_divisor = 2;
+uint sd_power = 1;		/* Default to SD Slot powered ON */
+uint sd_clock = 1;		/* Default to SD Clock turned ON */
+uint sd_crc = 0;		/* Default to SPI CRC Check turned OFF */
+
+uint8	spi_outbuf[SPI_MAX_PKT_LEN];
+uint8	spi_inbuf[SPI_MAX_PKT_LEN];
+
+/* 128bytes buffer is enough to clear data-not-available and program response-delay F0 bits
+ * assuming we will not exceed F0 response delay > 100 bytes at 48MHz.
+ */
+#define BUF2_PKT_LEN	128
+uint8	spi_outbuf2[BUF2_PKT_LEN];
+uint8	spi_inbuf2[BUF2_PKT_LEN];
+
+/* Prototypes */
+static bool bcmspi_test_card(sdioh_info_t *sd);
+static bool bcmspi_host_device_init_adapt(sdioh_info_t *sd);
+static int bcmspi_set_highspeed_mode(sdioh_info_t *sd, bool hsmode);
+static int bcmspi_cmd_issue(sdioh_info_t *sd, bool use_dma, uint32 cmd_arg,
+                           uint32 *data, uint32 datalen);
+static int bcmspi_card_regread(sdioh_info_t *sd, int func, uint32 regaddr,
+                              int regsize, uint32 *data);
+static int bcmspi_card_regwrite(sdioh_info_t *sd, int func, uint32 regaddr,
+                               int regsize, uint32 data);
+static int bcmspi_card_bytewrite(sdioh_info_t *sd, int func, uint32 regaddr,
+                               uint8 *data);
+static int bcmspi_driver_init(sdioh_info_t *sd);
+static int bcmspi_card_buf(sdioh_info_t *sd, int rw, int func, bool fifo,
+                          uint32 addr, int nbytes, uint32 *data);
+static int bcmspi_card_regread_fixedaddr(sdioh_info_t *sd, int func, uint32 regaddr, int regsize,
+                                 uint32 *data);
+static void bcmspi_cmd_getdstatus(sdioh_info_t *sd, uint32 *dstatus_buffer);
+static int bcmspi_update_stats(sdioh_info_t *sd, uint32 cmd_arg);
+
+/*
+ *  Public entry points & extern's
+ */
+extern sdioh_info_t *
+sdioh_attach(osl_t *osh, void *bar0, uint irq)
+{
+	sdioh_info_t *sd;
+
+	sd_trace(("%s\n", __FUNCTION__));
+	if ((sd = (sdioh_info_t *)MALLOC(osh, sizeof(sdioh_info_t))) == NULL) {
+		sd_err(("%s: out of memory, malloced %d bytes\n", __FUNCTION__, MALLOCED(osh)));
+		return NULL;
+	}
+	bzero((char *)sd, sizeof(sdioh_info_t));
+	sd->osh = osh;
+	if (spi_osinit(sd) != 0) {
+		sd_err(("%s: spi_osinit() failed\n", __FUNCTION__));
+		MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+		return NULL;
+	}
+
+	sd->bar0 = bar0;
+	sd->irq = irq;
+	sd->intr_handler = NULL;
+	sd->intr_handler_arg = NULL;
+	sd->intr_handler_valid = FALSE;
+
+	/* Set defaults */
+	sd->use_client_ints = TRUE;
+	sd->sd_use_dma = FALSE;	/* DMA Not supported */
+
+	/* Spi device default is 16bit mode, change to 4 when device is changed to 32bit
+	 * mode
+	 */
+	sd->wordlen = 2;
+
+	if (!spi_hw_attach(sd)) {
+		sd_err(("%s: spi_hw_attach() failed\n", __FUNCTION__));
+		spi_osfree(sd);
+		MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+		return (NULL);
+	}
+
+	if (bcmspi_driver_init(sd) != SUCCESS) {
+		sd_err(("%s: bcmspi_driver_init() failed()\n", __FUNCTION__));
+		spi_hw_detach(sd);
+		spi_osfree(sd);
+		MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+		return (NULL);
+	}
+
+	if (spi_register_irq(sd, irq) != SUCCESS) {
+		sd_err(("%s: spi_register_irq() failed for irq = %d\n", __FUNCTION__, irq));
+		spi_hw_detach(sd);
+		spi_osfree(sd);
+		MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+		return (NULL);
+	}
+
+	sd_trace(("%s: Done\n", __FUNCTION__));
+
+	return sd;
+}
+
+extern SDIOH_API_RC
+sdioh_detach(osl_t *osh, sdioh_info_t *sd)
+{
+	sd_trace(("%s\n", __FUNCTION__));
+	if (sd) {
+		sd_err(("%s: detaching from hardware\n", __FUNCTION__));
+		spi_free_irq(sd->irq, sd);
+		spi_hw_detach(sd);
+		spi_osfree(sd);
+		MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+	}
+	return SDIOH_API_RC_SUCCESS;
+}
+
+/* Configure callback to client when we recieve client interrupt */
+extern SDIOH_API_RC
+sdioh_interrupt_register(sdioh_info_t *sd, sdioh_cb_fn_t fn, void *argh)
+{
+	sd_trace(("%s: Entering\n", __FUNCTION__));
+	sd->intr_handler = fn;
+	sd->intr_handler_arg = argh;
+	sd->intr_handler_valid = TRUE;
+	return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_interrupt_deregister(sdioh_info_t *sd)
+{
+	sd_trace(("%s: Entering\n", __FUNCTION__));
+	sd->intr_handler_valid = FALSE;
+	sd->intr_handler = NULL;
+	sd->intr_handler_arg = NULL;
+	return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_interrupt_query(sdioh_info_t *sd, bool *onoff)
+{
+	sd_trace(("%s: Entering\n", __FUNCTION__));
+	*onoff = sd->client_intr_enabled;
+	return SDIOH_API_RC_SUCCESS;
+}
+
+#if defined(DHD_DEBUG)
+extern bool
+sdioh_interrupt_pending(sdioh_info_t *sd)
+{
+	return 0;
+}
+#endif
+
+extern SDIOH_API_RC
+sdioh_query_device(sdioh_info_t *sd)
+{
+	/* Return a BRCM ID appropriate to the dongle class */
+	return (sd->num_funcs > 1) ? BCM4329_D11NDUAL_ID : BCM4318_D11G_ID;
+}
+
+/* Provide dstatus bits of spi-transaction for dhd layers. */
+extern uint32
+sdioh_get_dstatus(sdioh_info_t *sd)
+{
+	return sd->card_dstatus;
+}
+
+extern void
+sdioh_chipinfo(sdioh_info_t *sd, uint32 chip, uint32 chiprev)
+{
+	sd->chip = chip;
+	sd->chiprev = chiprev;
+}
+
+extern void
+sdioh_dwordmode(sdioh_info_t *sd, bool set)
+{
+	uint8 reg = 0;
+	int status;
+
+	if ((status = sdioh_request_byte(sd, SDIOH_READ, SPI_FUNC_0, SPID_STATUS_ENABLE, &reg)) !=
+	     SUCCESS) {
+		sd_err(("%s: Failed to set dwordmode in gSPI\n", __FUNCTION__));
+		return;
+	}
+
+	if (set) {
+		reg |= DWORD_PKT_LEN_EN;
+		sd->dwordmode = TRUE;
+		sd->client_block_size[SPI_FUNC_2] = 4096; /* h2spi's limit is 4KB, we support 8KB */
+	} else {
+		reg &= ~DWORD_PKT_LEN_EN;
+		sd->dwordmode = FALSE;
+		sd->client_block_size[SPI_FUNC_2] = 2048;
+	}
+
+	if ((status = sdioh_request_byte(sd, SDIOH_WRITE, SPI_FUNC_0, SPID_STATUS_ENABLE, &reg)) !=
+	     SUCCESS) {
+		sd_err(("%s: Failed to set dwordmode in gSPI\n", __FUNCTION__));
+		return;
+	}
+}
+
+
+uint
+sdioh_query_iofnum(sdioh_info_t *sd)
+{
+	return sd->num_funcs;
+}
+
+/* IOVar table */
+enum {
+	IOV_MSGLEVEL = 1,
+	IOV_BLOCKMODE,
+	IOV_BLOCKSIZE,
+	IOV_DMA,
+	IOV_USEINTS,
+	IOV_NUMINTS,
+	IOV_NUMLOCALINTS,
+	IOV_HOSTREG,
+	IOV_DEVREG,
+	IOV_DIVISOR,
+	IOV_SDMODE,
+	IOV_HISPEED,
+	IOV_HCIREGS,
+	IOV_POWER,
+	IOV_CLOCK,
+	IOV_SPIERRSTATS,
+	IOV_RESP_DELAY_ALL
+};
+
+const bcm_iovar_t sdioh_iovars[] = {
+	{"sd_msglevel",	IOV_MSGLEVEL, 	0,	IOVT_UINT32,	0 },
+	{"sd_blocksize", IOV_BLOCKSIZE, 0,	IOVT_UINT32,	0 }, /* ((fn << 16) | size) */
+	{"sd_dma",	IOV_DMA,	0,	IOVT_BOOL,	0 },
+	{"sd_ints",	IOV_USEINTS,	0,	IOVT_BOOL,	0 },
+	{"sd_numints",	IOV_NUMINTS,	0,	IOVT_UINT32,	0 },
+	{"sd_numlocalints", IOV_NUMLOCALINTS, 0, IOVT_UINT32,	0 },
+	{"sd_hostreg",	IOV_HOSTREG,	0,	IOVT_BUFFER,	sizeof(sdreg_t) },
+	{"sd_devreg",	IOV_DEVREG,	0,	IOVT_BUFFER,	sizeof(sdreg_t)	},
+	{"sd_divisor",	IOV_DIVISOR,	0,	IOVT_UINT32,	0 },
+	{"sd_power",	IOV_POWER,	0,	IOVT_UINT32,	0 },
+	{"sd_clock",	IOV_CLOCK,	0,	IOVT_UINT32,	0 },
+	{"sd_mode",	IOV_SDMODE,	0,	IOVT_UINT32,	100},
+	{"sd_highspeed",	IOV_HISPEED,	0,	IOVT_UINT32,	0},
+	{"spi_errstats", IOV_SPIERRSTATS, 0, IOVT_BUFFER, sizeof(struct spierrstats_t) },
+	{"spi_respdelay",	IOV_RESP_DELAY_ALL,	0,	IOVT_BOOL,	0 },
+	{NULL, 0, 0, 0, 0 }
+};
+
+int
+sdioh_iovar_op(sdioh_info_t *si, const char *name,
+               void *params, int plen, void *arg, int len, bool set)
+{
+	const bcm_iovar_t *vi = NULL;
+	int bcmerror = 0;
+	int val_size;
+	int32 int_val = 0;
+	bool bool_val;
+	uint32 actionid;
+/*
+	sdioh_regs_t *regs;
+*/
+
+	ASSERT(name);
+	ASSERT(len >= 0);
+
+	/* Get must have return space; Set does not take qualifiers */
+	ASSERT(set || (arg && len));
+	ASSERT(!set || (!params && !plen));
+
+	sd_trace(("%s: Enter (%s %s)\n", __FUNCTION__, (set ? "set" : "get"), name));
+
+	if ((vi = bcm_iovar_lookup(sdioh_iovars, name)) == NULL) {
+		bcmerror = BCME_UNSUPPORTED;
+		goto exit;
+	}
+
+	if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, set)) != 0)
+		goto exit;
+
+	/* Set up params so get and set can share the convenience variables */
+	if (params == NULL) {
+		params = arg;
+		plen = len;
+	}
+
+	if (vi->type == IOVT_VOID)
+		val_size = 0;
+	else if (vi->type == IOVT_BUFFER)
+		val_size = len;
+	else
+		val_size = sizeof(int);
+
+	if (plen >= (int)sizeof(int_val))
+		bcopy(params, &int_val, sizeof(int_val));
+
+	bool_val = (int_val != 0) ? TRUE : FALSE;
+
+	actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid);
+	switch (actionid) {
+	case IOV_GVAL(IOV_MSGLEVEL):
+		int_val = (int32)sd_msglevel;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_MSGLEVEL):
+		sd_msglevel = int_val;
+		break;
+
+	case IOV_GVAL(IOV_BLOCKSIZE):
+		if ((uint32)int_val > si->num_funcs) {
+			bcmerror = BCME_BADARG;
+			break;
+		}
+		int_val = (int32)si->client_block_size[int_val];
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_GVAL(IOV_DMA):
+		int_val = (int32)si->sd_use_dma;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_DMA):
+		si->sd_use_dma = (bool)int_val;
+		break;
+
+	case IOV_GVAL(IOV_USEINTS):
+		int_val = (int32)si->use_client_ints;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_USEINTS):
+		break;
+
+	case IOV_GVAL(IOV_DIVISOR):
+		int_val = (uint32)sd_divisor;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_DIVISOR):
+		sd_divisor = int_val;
+		if (!spi_start_clock(si, (uint16)sd_divisor)) {
+			sd_err(("%s: set clock failed\n", __FUNCTION__));
+			bcmerror = BCME_ERROR;
+		}
+		break;
+
+	case IOV_GVAL(IOV_POWER):
+		int_val = (uint32)sd_power;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_POWER):
+		sd_power = int_val;
+		break;
+
+	case IOV_GVAL(IOV_CLOCK):
+		int_val = (uint32)sd_clock;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_CLOCK):
+		sd_clock = int_val;
+		break;
+
+	case IOV_GVAL(IOV_SDMODE):
+		int_val = (uint32)sd_sdmode;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_SDMODE):
+		sd_sdmode = int_val;
+		break;
+
+	case IOV_GVAL(IOV_HISPEED):
+		int_val = (uint32)sd_hiok;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_HISPEED):
+		sd_hiok = int_val;
+
+		if (!bcmspi_set_highspeed_mode(si, (bool)sd_hiok)) {
+			sd_err(("%s: Failed changing highspeed mode to %d.\n",
+			        __FUNCTION__, sd_hiok));
+			bcmerror = BCME_ERROR;
+			return ERROR;
+		}
+		break;
+
+	case IOV_GVAL(IOV_NUMINTS):
+		int_val = (int32)si->intrcount;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_GVAL(IOV_NUMLOCALINTS):
+		int_val = (int32)si->local_intrcount;
+		bcopy(&int_val, arg, val_size);
+		break;
+	case IOV_GVAL(IOV_DEVREG):
+	{
+		sdreg_t *sd_ptr = (sdreg_t *)params;
+		uint8 data;
+
+		if (sdioh_cfg_read(si, sd_ptr->func, sd_ptr->offset, &data)) {
+			bcmerror = BCME_SDIO_ERROR;
+			break;
+		}
+
+		int_val = (int)data;
+		bcopy(&int_val, arg, sizeof(int_val));
+		break;
+	}
+
+	case IOV_SVAL(IOV_DEVREG):
+	{
+		sdreg_t *sd_ptr = (sdreg_t *)params;
+		uint8 data = (uint8)sd_ptr->value;
+
+		if (sdioh_cfg_write(si, sd_ptr->func, sd_ptr->offset, &data)) {
+			bcmerror = BCME_SDIO_ERROR;
+			break;
+		}
+		break;
+	}
+
+
+	case IOV_GVAL(IOV_SPIERRSTATS):
+	{
+		bcopy(&si->spierrstats, arg, sizeof(struct spierrstats_t));
+		break;
+	}
+
+	case IOV_SVAL(IOV_SPIERRSTATS):
+	{
+		bzero(&si->spierrstats, sizeof(struct spierrstats_t));
+		break;
+	}
+
+	case IOV_GVAL(IOV_RESP_DELAY_ALL):
+		int_val = (int32)si->resp_delay_all;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_RESP_DELAY_ALL):
+		si->resp_delay_all = (bool)int_val;
+		int_val = STATUS_ENABLE|INTR_WITH_STATUS;
+		if (si->resp_delay_all)
+			int_val |= RESP_DELAY_ALL;
+		else {
+			if (bcmspi_card_regwrite(si, SPI_FUNC_0, SPID_RESPONSE_DELAY, 1,
+			     F1_RESPONSE_DELAY) != SUCCESS) {
+				sd_err(("%s: Unable to set response delay.\n", __FUNCTION__));
+				bcmerror = BCME_SDIO_ERROR;
+				break;
+			}
+		}
+
+		if (bcmspi_card_regwrite(si, SPI_FUNC_0, SPID_STATUS_ENABLE, 1, int_val)
+		     != SUCCESS) {
+			sd_err(("%s: Unable to set response delay.\n", __FUNCTION__));
+			bcmerror = BCME_SDIO_ERROR;
+			break;
+		}
+		break;
+
+	default:
+		bcmerror = BCME_UNSUPPORTED;
+		break;
+	}
+exit:
+
+	return bcmerror;
+}
+
+extern SDIOH_API_RC
+sdioh_cfg_read(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data)
+{
+	SDIOH_API_RC status;
+	/* No lock needed since sdioh_request_byte does locking */
+	status = sdioh_request_byte(sd, SDIOH_READ, fnc_num, addr, data);
+	return status;
+}
+
+extern SDIOH_API_RC
+sdioh_cfg_write(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data)
+{
+	/* No lock needed since sdioh_request_byte does locking */
+	SDIOH_API_RC status;
+
+	if ((fnc_num == SPI_FUNC_1) && (addr == SBSDIO_FUNC1_FRAMECTRL)) {
+		uint8 dummy_data;
+		status = sdioh_cfg_read(sd, fnc_num, addr, &dummy_data);
+		if (status) {
+			sd_err(("sdioh_cfg_read() failed.\n"));
+			return status;
+		}
+	}
+
+	status = sdioh_request_byte(sd, SDIOH_WRITE, fnc_num, addr, data);
+	return status;
+}
+
+extern SDIOH_API_RC
+sdioh_cis_read(sdioh_info_t *sd, uint func, uint8 *cisd, uint32 length)
+{
+	uint32 count;
+	int offset;
+	uint32 cis_byte;
+	uint16 *cis = (uint16 *)cisd;
+	uint bar0 = SI_ENUM_BASE;
+	int status;
+	uint8 data;
+
+	sd_trace(("%s: Func %d\n", __FUNCTION__, func));
+
+	spi_lock(sd);
+
+	/* Set sb window address to 0x18000000 */
+	data = (bar0 >> 8) & SBSDIO_SBADDRLOW_MASK;
+	status = bcmspi_card_bytewrite(sd, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRLOW, &data);
+	if (status == SUCCESS) {
+		data = (bar0 >> 16) & SBSDIO_SBADDRMID_MASK;
+		status = bcmspi_card_bytewrite(sd, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRMID, &data);
+	} else {
+		sd_err(("%s: Unable to set sb-addr-windows\n", __FUNCTION__));
+		spi_unlock(sd);
+		return (BCME_ERROR);
+	}
+	if (status == SUCCESS) {
+		data = (bar0 >> 24) & SBSDIO_SBADDRHIGH_MASK;
+		status = bcmspi_card_bytewrite(sd, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRHIGH, &data);
+	} else {
+		sd_err(("%s: Unable to set sb-addr-windows\n", __FUNCTION__));
+		spi_unlock(sd);
+		return (BCME_ERROR);
+	}
+
+	offset =  CC_OTP; /* OTP offset in chipcommon. */
+	for (count = 0; count < length/2; count++) {
+		if (bcmspi_card_regread (sd, SDIO_FUNC_1, offset, 2, &cis_byte) < 0) {
+			sd_err(("%s: regread failed: Can't read CIS\n", __FUNCTION__));
+			spi_unlock(sd);
+			return (BCME_ERROR);
+		}
+
+		*cis = (uint16)cis_byte;
+		cis++;
+		offset += 2;
+	}
+
+	spi_unlock(sd);
+
+	return (BCME_OK);
+}
+
+extern SDIOH_API_RC
+sdioh_request_byte(sdioh_info_t *sd, uint rw, uint func, uint regaddr, uint8 *byte)
+{
+	int status;
+	uint32 cmd_arg;
+	uint32 dstatus;
+	uint32 data = (uint32)(*byte);
+
+	spi_lock(sd);
+
+	cmd_arg = 0;
+	cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func);
+	cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1);	/* Incremental access */
+	cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr);
+	cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, rw == SDIOH_READ ? 0 : 1);
+	cmd_arg = SFIELD(cmd_arg, SPI_LEN, 1);
+
+	sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg));
+	sd_trace(("%s: rw=%d, func=%d, regaddr=0x%08x, data=0x%x\n", __FUNCTION__, rw, func,
+	         regaddr, data));
+
+	if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma,
+	                              cmd_arg, &data, 1)) != SUCCESS) {
+		spi_unlock(sd);
+		return status;
+	}
+
+	if (rw == SDIOH_READ)
+		*byte = (uint8)data;
+
+	bcmspi_cmd_getdstatus(sd, &dstatus);
+	if (dstatus)
+		sd_trace(("dstatus =0x%x\n", dstatus));
+
+	spi_unlock(sd);
+	return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_request_word(sdioh_info_t *sd, uint cmd_type, uint rw, uint func, uint addr,
+                   uint32 *word, uint nbytes)
+{
+	int status;
+
+	spi_lock(sd);
+
+	if (rw == SDIOH_READ)
+		status = bcmspi_card_regread(sd, func, addr, nbytes, word);
+	else
+		status = bcmspi_card_regwrite(sd, func, addr, nbytes, *word);
+
+	spi_unlock(sd);
+	return (status == SUCCESS ?  SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL);
+}
+
+extern SDIOH_API_RC
+sdioh_request_buffer(sdioh_info_t *sd, uint pio_dma, uint fix_inc, uint rw, uint func,
+                     uint addr, uint reg_width, uint buflen_u, uint8 *buffer, void *pkt)
+{
+	int len;
+	int buflen = (int)buflen_u;
+	bool fifo = (fix_inc == SDIOH_DATA_FIX);
+
+	spi_lock(sd);
+
+	ASSERT(reg_width == 4);
+	ASSERT(buflen_u < (1 << 30));
+	ASSERT(sd->client_block_size[func]);
+
+	sd_data(("%s: %c len %d r_cnt %d t_cnt %d, pkt @0x%p\n",
+	         __FUNCTION__, rw == SDIOH_READ ? 'R' : 'W',
+	         buflen_u, sd->r_cnt, sd->t_cnt, pkt));
+
+	/* Break buffer down into blocksize chunks. */
+	while (buflen > 0) {
+		len = MIN(sd->client_block_size[func], buflen);
+		if (bcmspi_card_buf(sd, rw, func, fifo, addr, len, (uint32 *)buffer) != SUCCESS) {
+			sd_err(("%s: bcmspi_card_buf %s failed\n",
+				__FUNCTION__, rw == SDIOH_READ ? "Read" : "Write"));
+			spi_unlock(sd);
+			return SDIOH_API_RC_FAIL;
+		}
+		buffer += len;
+		buflen -= len;
+		if (!fifo)
+			addr += len;
+	}
+	spi_unlock(sd);
+	return SDIOH_API_RC_SUCCESS;
+}
+
+/* This function allows write to gspi bus when another rd/wr function is deep down the call stack.
+ * Its main aim is to have simpler spi writes rather than recursive writes.
+ * e.g. When there is a need to program response delay on the fly after detecting the SPI-func
+ * this call will allow to program the response delay.
+ */
+static int
+bcmspi_card_byterewrite(sdioh_info_t *sd, int func, uint32 regaddr, uint8 byte)
+{
+	uint32 cmd_arg;
+	uint32 datalen = 1;
+	uint32 hostlen;
+
+	cmd_arg = 0;
+
+	cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, 1);
+	cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1);	/* Incremental access */
+	cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func);
+	cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr);
+	cmd_arg = SFIELD(cmd_arg, SPI_LEN, datalen);
+
+	sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg));
+
+
+	/* Set up and issue the SPI command.  MSByte goes out on bus first.  Increase datalen
+	 * according to the wordlen mode(16/32bit) the device is in.
+	 */
+	ASSERT(sd->wordlen == 4 || sd->wordlen == 2);
+	datalen = ROUNDUP(datalen, sd->wordlen);
+
+	/* Start by copying command in the spi-outbuffer */
+	if (sd->wordlen == 4) { /* 32bit spid */
+		*(uint32 *)spi_outbuf2 = bcmswap32(cmd_arg);
+		if (datalen & 0x3)
+			datalen += (4 - (datalen & 0x3));
+	} else if (sd->wordlen == 2) { /* 16bit spid */
+		*(uint16 *)spi_outbuf2 = bcmswap16(cmd_arg & 0xffff);
+		*(uint16 *)&spi_outbuf2[2] = bcmswap16((cmd_arg & 0xffff0000) >> 16);
+		if (datalen & 0x1)
+			datalen++;
+	} else {
+		sd_err(("%s: Host is %d bit spid, could not create SPI command.\n",
+		        __FUNCTION__, 8 * sd->wordlen));
+		return ERROR;
+	}
+
+	/* for Write, put the data into the output buffer  */
+	if (datalen != 0) {
+			if (sd->wordlen == 4) { /* 32bit spid */
+				*(uint32 *)&spi_outbuf2[CMDLEN] = bcmswap32(byte);
+			} else if (sd->wordlen == 2) { /* 16bit spid */
+				*(uint16 *)&spi_outbuf2[CMDLEN] = bcmswap16(byte & 0xffff);
+				*(uint16 *)&spi_outbuf2[CMDLEN + 2] =
+					bcmswap16((byte & 0xffff0000) >> 16);
+			}
+	}
+
+	/* +4 for cmd, +4 for dstatus */
+	hostlen = datalen + 8;
+	hostlen += (4 - (hostlen & 0x3));
+	spi_sendrecv(sd, spi_outbuf2, spi_inbuf2, hostlen);
+
+	/* Last 4bytes are dstatus.  Device is configured to return status bits. */
+	if (sd->wordlen == 4) { /* 32bit spid */
+		sd->card_dstatus = bcmswap32(*(uint32 *)&spi_inbuf2[datalen + CMDLEN ]);
+	} else if (sd->wordlen == 2) { /* 16bit spid */
+		sd->card_dstatus = (bcmswap16(*(uint16 *)&spi_inbuf2[datalen + CMDLEN ]) |
+		                   (bcmswap16(*(uint16 *)&spi_inbuf2[datalen + CMDLEN + 2]) << 16));
+	} else {
+		sd_err(("%s: Host is %d bit machine, could not read SPI dstatus.\n",
+		        __FUNCTION__, 8 * sd->wordlen));
+		return ERROR;
+	}
+
+	if (sd->card_dstatus)
+		sd_trace(("dstatus after byte rewrite = 0x%x\n", sd->card_dstatus));
+
+	return (BCME_OK);
+}
+
+/* Program the response delay corresponding to the spi function */
+static int
+bcmspi_prog_resp_delay(sdioh_info_t *sd, int func, uint8 resp_delay)
+{
+	if (sd->resp_delay_all == FALSE)
+		return (BCME_OK);
+
+	if (sd->prev_fun == func)
+		return (BCME_OK);
+
+	if (F0_RESPONSE_DELAY == F1_RESPONSE_DELAY)
+		return (BCME_OK);
+
+	bcmspi_card_byterewrite(sd, SPI_FUNC_0, SPID_RESPONSE_DELAY, resp_delay);
+
+	/* Remember function for which to avoid reprogramming resp-delay in next iteration */
+	sd->prev_fun = func;
+
+	return (BCME_OK);
+
+}
+
+#define GSPI_RESYNC_PATTERN	0x0
+
+/* A resync pattern is a 32bit MOSI line with all zeros. Its a special command in gSPI.
+ * It resets the spi-bkplane logic so that all F1 related ping-pong buffer logic is
+ * synchronised and all queued resuests are cancelled.
+ */
+static int
+bcmspi_resync_f1(sdioh_info_t *sd)
+{
+	uint32 cmd_arg = GSPI_RESYNC_PATTERN, data = 0, datalen = 0;
+
+
+	/* Set up and issue the SPI command.  MSByte goes out on bus first.  Increase datalen
+	 * according to the wordlen mode(16/32bit) the device is in.
+	 */
+	ASSERT(sd->wordlen == 4 || sd->wordlen == 2);
+	datalen = ROUNDUP(datalen, sd->wordlen);
+
+	/* Start by copying command in the spi-outbuffer */
+	*(uint32 *)spi_outbuf2 = cmd_arg;
+
+	/* for Write, put the data into the output buffer  */
+	*(uint32 *)&spi_outbuf2[CMDLEN] = data;
+
+	/* +4 for cmd, +4 for dstatus */
+	spi_sendrecv(sd, spi_outbuf2, spi_inbuf2, datalen + 8);
+
+	/* Last 4bytes are dstatus.  Device is configured to return status bits. */
+	if (sd->wordlen == 4) { /* 32bit spid */
+		sd->card_dstatus = bcmswap32(*(uint32 *)&spi_inbuf2[datalen + CMDLEN ]);
+	} else if (sd->wordlen == 2) { /* 16bit spid */
+		sd->card_dstatus = (bcmswap16(*(uint16 *)&spi_inbuf2[datalen + CMDLEN ]) |
+		                   (bcmswap16(*(uint16 *)&spi_inbuf2[datalen + CMDLEN + 2]) << 16));
+	} else {
+		sd_err(("%s: Host is %d bit machine, could not read SPI dstatus.\n",
+		        __FUNCTION__, 8 * sd->wordlen));
+		return ERROR;
+	}
+
+	if (sd->card_dstatus)
+		sd_trace(("dstatus after resync pattern write = 0x%x\n", sd->card_dstatus));
+
+	return (BCME_OK);
+}
+
+uint32 dstatus_count = 0;
+
+static int
+bcmspi_update_stats(sdioh_info_t *sd, uint32 cmd_arg)
+{
+	uint32 dstatus = sd->card_dstatus;
+	struct spierrstats_t *spierrstats = &sd->spierrstats;
+	int err = SUCCESS;
+
+	sd_trace(("cmd = 0x%x, dstatus = 0x%x\n", cmd_arg, dstatus));
+
+	/* Store dstatus of last few gSPI transactions */
+	spierrstats->dstatus[dstatus_count % NUM_PREV_TRANSACTIONS] = dstatus;
+	spierrstats->spicmd[dstatus_count % NUM_PREV_TRANSACTIONS] = cmd_arg;
+	dstatus_count++;
+
+	if (sd->card_init_done == FALSE)
+		return err;
+
+	if (dstatus & STATUS_DATA_NOT_AVAILABLE) {
+		spierrstats->dna++;
+		sd_trace(("Read data not available on F1 addr = 0x%x\n",
+		        GFIELD(cmd_arg, SPI_REG_ADDR)));
+		/* Clear dna bit */
+		bcmspi_card_byterewrite(sd, SPI_FUNC_0, SPID_INTR_REG, DATA_UNAVAILABLE);
+	}
+
+	if (dstatus & STATUS_UNDERFLOW) {
+		spierrstats->rdunderflow++;
+		sd_err(("FIFO underflow happened due to current F2 read command.\n"));
+	}
+
+	if (dstatus & STATUS_OVERFLOW) {
+		spierrstats->wroverflow++;
+		sd_err(("FIFO overflow happened due to current (F1/F2) write command.\n"));
+		if ((sd->chip == BCM4329_CHIP_ID) && (sd->chiprev == 0)) {
+			bcmspi_card_byterewrite(sd, SPI_FUNC_0, SPID_INTR_REG, F1_OVERFLOW);
+			bcmspi_resync_f1(sd);
+			sd_err(("Recovering from F1 FIFO overflow.\n"));
+		} else {
+			err = ERROR_OF;
+		}
+	}
+
+	if (dstatus & STATUS_F2_INTR) {
+		spierrstats->f2interrupt++;
+		sd_trace(("Interrupt from F2.  SW should clear corresponding IntStatus bits\n"));
+	}
+
+	if (dstatus & STATUS_F3_INTR) {
+		spierrstats->f3interrupt++;
+		sd_err(("Interrupt from F3.  SW should clear corresponding IntStatus bits\n"));
+	}
+
+	if (dstatus & STATUS_HOST_CMD_DATA_ERR) {
+		spierrstats->hostcmddataerr++;
+		sd_err(("Error in CMD or Host data, detected by CRC/Checksum (optional)\n"));
+	}
+
+	if (dstatus & STATUS_F2_PKT_AVAILABLE) {
+		spierrstats->f2pktavailable++;
+		sd_trace(("Packet is available/ready in F2 TX FIFO\n"));
+		sd_trace(("Packet length = %d\n", sd->dwordmode ?
+		         ((dstatus & STATUS_F2_PKT_LEN_MASK) >> (STATUS_F2_PKT_LEN_SHIFT - 2)) :
+		         ((dstatus & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT)));
+	}
+
+	if (dstatus & STATUS_F3_PKT_AVAILABLE) {
+		spierrstats->f3pktavailable++;
+		sd_err(("Packet is available/ready in F3 TX FIFO\n"));
+		sd_err(("Packet length = %d\n",
+		        (dstatus & STATUS_F3_PKT_LEN_MASK) >> STATUS_F3_PKT_LEN_SHIFT));
+	}
+
+	return err;
+}
+
+extern int
+sdioh_abort(sdioh_info_t *sd, uint func)
+{
+	return 0;
+}
+
+int
+sdioh_start(sdioh_info_t *sd, int stage)
+{
+	return SUCCESS;
+}
+
+int
+sdioh_stop(sdioh_info_t *sd)
+{
+	return SUCCESS;
+}
+
+
+
+/*
+ * Private/Static work routines
+ */
+static int
+bcmspi_host_init(sdioh_info_t *sd)
+{
+
+	/* Default power on mode */
+	sd->sd_mode = SDIOH_MODE_SPI;
+	sd->polled_mode = TRUE;
+	sd->host_init_done = TRUE;
+	sd->card_init_done = FALSE;
+	sd->adapter_slot = 1;
+
+	return (SUCCESS);
+}
+
+static int
+get_client_blocksize(sdioh_info_t *sd)
+{
+	uint32 regdata[2];
+	int status;
+
+	/* Find F1/F2/F3 max packet size */
+	if ((status = bcmspi_card_regread(sd, 0, SPID_F1_INFO_REG,
+	                                 8, regdata)) != SUCCESS) {
+		return status;
+	}
+
+	sd_trace(("pkt_size regdata[0] = 0x%x, regdata[1] = 0x%x\n",
+	        regdata[0], regdata[1]));
+
+	sd->client_block_size[1] = (regdata[0] & F1_MAX_PKT_SIZE) >> 2;
+	sd_trace(("Func1 blocksize = %d\n", sd->client_block_size[1]));
+	ASSERT(sd->client_block_size[1] == BLOCK_SIZE_F1);
+
+	sd->client_block_size[2] = ((regdata[0] >> 16) & F2_MAX_PKT_SIZE) >> 2;
+	sd_trace(("Func2 blocksize = %d\n", sd->client_block_size[2]));
+	ASSERT(sd->client_block_size[2] == BLOCK_SIZE_F2);
+
+	sd->client_block_size[3] = (regdata[1] & F3_MAX_PKT_SIZE) >> 2;
+	sd_trace(("Func3 blocksize = %d\n", sd->client_block_size[3]));
+	ASSERT(sd->client_block_size[3] == BLOCK_SIZE_F3);
+
+	return 0;
+}
+
+static int
+bcmspi_client_init(sdioh_info_t *sd)
+{
+	uint32	status_en_reg = 0;
+	sd_trace(("%s: Powering up slot %d\n", __FUNCTION__, sd->adapter_slot));
+
+#ifdef HSMODE
+	if (!spi_start_clock(sd, (uint16)sd_divisor)) {
+		sd_err(("spi_start_clock failed\n"));
+		return ERROR;
+	}
+#else
+	/* Start at ~400KHz clock rate for initialization */
+	if (!spi_start_clock(sd, 128)) {
+		sd_err(("spi_start_clock failed\n"));
+		return ERROR;
+	}
+#endif /* HSMODE */
+
+	if (!bcmspi_host_device_init_adapt(sd)) {
+		sd_err(("bcmspi_host_device_init_adapt failed\n"));
+		return ERROR;
+	}
+
+	if (!bcmspi_test_card(sd)) {
+		sd_err(("bcmspi_test_card failed\n"));
+		return ERROR;
+	}
+
+	sd->num_funcs = SPI_MAX_IOFUNCS;
+
+	get_client_blocksize(sd);
+
+	/* Apply resync pattern cmd with all zeros to reset spi-bkplane F1 logic */
+	bcmspi_resync_f1(sd);
+
+	sd->dwordmode = FALSE;
+
+	bcmspi_card_regread(sd, 0, SPID_STATUS_ENABLE, 1, &status_en_reg);
+
+	sd_trace(("%s: Enabling interrupt with dstatus \n", __FUNCTION__));
+	status_en_reg |= INTR_WITH_STATUS;
+
+
+	if (bcmspi_card_regwrite(sd, SPI_FUNC_0, SPID_STATUS_ENABLE, 1,
+	    status_en_reg & 0xff) != SUCCESS) {
+		sd_err(("%s: Unable to set response delay for all fun's.\n", __FUNCTION__));
+		return ERROR;
+	}
+
+
+#ifndef HSMODE
+	/* After configuring for High-Speed mode, set the desired clock rate. */
+	if (!spi_start_clock(sd, 4)) {
+		sd_err(("spi_start_clock failed\n"));
+		return ERROR;
+	}
+#endif /* HSMODE */
+
+	sd->card_init_done = TRUE;
+
+
+	return SUCCESS;
+}
+
+static int
+bcmspi_set_highspeed_mode(sdioh_info_t *sd, bool hsmode)
+{
+	uint32 regdata;
+	int status;
+
+	if ((status = bcmspi_card_regread(sd, 0, SPID_CONFIG,
+	                                 4, &regdata)) != SUCCESS)
+		return status;
+
+	sd_trace(("In %s spih-ctrl = 0x%x \n", __FUNCTION__, regdata));
+
+
+	if (hsmode == TRUE) {
+		sd_trace(("Attempting to enable High-Speed mode.\n"));
+
+		if (regdata & HIGH_SPEED_MODE) {
+			sd_trace(("Device is already in High-Speed mode.\n"));
+			return status;
+		} else {
+			regdata |= HIGH_SPEED_MODE;
+			sd_trace(("Writing %08x to device at %08x\n", regdata, SPID_CONFIG));
+			if ((status = bcmspi_card_regwrite(sd, 0, SPID_CONFIG,
+			                                  4, regdata)) != SUCCESS) {
+				return status;
+			}
+		}
+	} else {
+		sd_trace(("Attempting to disable High-Speed mode.\n"));
+
+		if (regdata & HIGH_SPEED_MODE) {
+			regdata &= ~HIGH_SPEED_MODE;
+			sd_trace(("Writing %08x to device at %08x\n", regdata, SPID_CONFIG));
+			if ((status = bcmspi_card_regwrite(sd, 0, SPID_CONFIG,
+			                                  4, regdata)) != SUCCESS)
+				return status;
+		}
+		 else {
+			sd_trace(("Device is already in Low-Speed mode.\n"));
+			return status;
+		}
+	}
+
+	spi_controller_highspeed_mode(sd, hsmode);
+
+	return TRUE;
+}
+
+#define bcmspi_find_curr_mode(sd) { \
+	sd->wordlen = 2; \
+	status = bcmspi_card_regread_fixedaddr(sd, 0, SPID_TEST_READ, 4, &regdata); \
+	regdata &= 0xff; \
+	if ((regdata == 0xad) || (regdata == 0x5b) || \
+	    (regdata == 0x5d) || (regdata == 0x5a)) \
+		break; \
+	sd->wordlen = 4; \
+	status = bcmspi_card_regread_fixedaddr(sd, 0, SPID_TEST_READ, 4, &regdata); \
+	regdata &= 0xff; \
+	if ((regdata == 0xad) || (regdata == 0x5b) || \
+	    (regdata == 0x5d) || (regdata == 0x5a)) \
+		break; \
+	sd_trace(("Silicon testability issue: regdata = 0x%x." \
+	          " Expected 0xad, 0x5a, 0x5b or 0x5d.\n", regdata));	\
+	OSL_DELAY(100000); \
+}
+
+#define INIT_ADAPT_LOOP		100
+
+/* Adapt clock-phase-speed-bitwidth between host and device */
+static bool
+bcmspi_host_device_init_adapt(sdioh_info_t *sd)
+{
+	uint32 wrregdata, regdata = 0;
+	int status;
+	int i;
+
+	/* Due to a silicon testability issue, the first command from the Host
+	 * to the device will get corrupted (first bit will be lost). So the
+	 * Host should poll the device with a safe read request. ie: The Host
+	 * should try to read F0 addr 0x14 using the Fixed address mode
+	 * (This will prevent a unintended write command to be detected by device)
+	 */
+	for (i = 0; i < INIT_ADAPT_LOOP; i++) {
+		/* If device was not power-cycled it will stay in 32bit mode with
+		 * response-delay-all bit set.  Alternate the iteration so that
+		 * read either with or without response-delay for F0 to succeed.
+		 */
+		bcmspi_find_curr_mode(sd);
+		sd->resp_delay_all = (i & 0x1) ? TRUE : FALSE;
+
+		bcmspi_find_curr_mode(sd);
+		sd->dwordmode = TRUE;
+
+		bcmspi_find_curr_mode(sd);
+		sd->dwordmode = FALSE;
+	}
+
+	/* Bail out, device not detected */
+	if (i == INIT_ADAPT_LOOP)
+		return FALSE;
+
+	/* Softreset the spid logic */
+	if ((sd->dwordmode) || (sd->wordlen == 4)) {
+		bcmspi_card_regwrite(sd, 0, SPID_RESET_BP, 1, RESET_ON_WLAN_BP_RESET|RESET_SPI);
+		bcmspi_card_regread(sd, 0, SPID_RESET_BP, 1, &regdata);
+		sd_trace(("reset reg read = 0x%x\n", regdata));
+		sd_trace(("dwordmode = %d, wordlen = %d, resp_delay_all = %d\n", sd->dwordmode,
+		       sd->wordlen, sd->resp_delay_all));
+		/* Restore default state after softreset */
+		sd->wordlen = 2;
+		sd->dwordmode = FALSE;
+	}
+
+	if (sd->wordlen == 4) {
+		if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_READ, 4, &regdata)) !=
+		     SUCCESS)
+				return FALSE;
+		if (regdata == TEST_RO_DATA_32BIT_LE) {
+			sd_trace(("Spid is already in 32bit LE mode. Value read = 0x%x\n",
+			          regdata));
+			sd_trace(("Spid power was left on.\n"));
+		} else {
+			sd_err(("Spid power was left on but signature read failed."
+			        " Value read = 0x%x\n", regdata));
+			return FALSE;
+		}
+	} else {
+		sd->wordlen = 2;
+
+#define CTRL_REG_DEFAULT	0x00010430 /* according to the host m/c */
+
+		wrregdata = (CTRL_REG_DEFAULT);
+		sd->resp_delay_all = TRUE;
+		if (sd->resp_delay_all == TRUE) {
+			/* Enable response delay for all */
+			wrregdata |= (RESP_DELAY_ALL << 16);
+			/* Program response delay value */
+			wrregdata &= 0xffff00ff;
+			wrregdata |= (F1_RESPONSE_DELAY << 8);
+			sd->prev_fun = SPI_FUNC_1;
+			bcmspi_card_regwrite(sd, 0, SPID_CONFIG, 4, wrregdata);
+		}
+
+		if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_READ, 4, &regdata)) != SUCCESS)
+			return FALSE;
+		sd_trace(("(we are still in 16bit mode) 32bit READ LE regdata = 0x%x\n", regdata));
+
+#ifndef HSMODE
+		wrregdata |= (CLOCK_PHASE | CLOCK_POLARITY);
+		wrregdata &= ~HIGH_SPEED_MODE;
+		bcmspi_card_regwrite(sd, 0, SPID_CONFIG, 4, wrregdata);
+#endif /* HSMODE */
+
+		for (i = 0; i < INIT_ADAPT_LOOP; i++) {
+			if ((regdata == 0xfdda7d5b) || (regdata == 0xfdda7d5a)) {
+				sd_trace(("0xfeedbead was leftshifted by 1-bit.\n"));
+				if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_READ, 4,
+				     &regdata)) != SUCCESS)
+					return FALSE;
+			}
+			OSL_DELAY(1000);
+		}
+
+
+		/* Change to host controller intr-polarity of active-low */
+		wrregdata &= ~INTR_POLARITY;
+		sd_trace(("(we are still in 16bit mode) 32bit Write LE reg-ctrl-data = 0x%x\n",
+		        wrregdata));
+		/* Change to 32bit mode */
+		wrregdata |= WORD_LENGTH_32;
+		bcmspi_card_regwrite(sd, 0, SPID_CONFIG, 4, wrregdata);
+
+		/* Change command/data packaging in 32bit LE mode */
+		sd->wordlen = 4;
+
+		if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_READ, 4, &regdata)) != SUCCESS)
+			return FALSE;
+
+		if (regdata == TEST_RO_DATA_32BIT_LE) {
+			sd_trace(("Read spid passed. Value read = 0x%x\n", regdata));
+			sd_trace(("Spid had power-on cycle OR spi was soft-resetted \n"));
+		} else {
+			sd_err(("Stale spid reg values read as it was kept powered. Value read ="
+			  "0x%x\n", regdata));
+			return FALSE;
+		}
+	}
+
+
+	return TRUE;
+}
+
+static bool
+bcmspi_test_card(sdioh_info_t *sd)
+{
+	uint32 regdata;
+	int status;
+
+	if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_READ, 4, &regdata)) != SUCCESS)
+		return FALSE;
+
+	if (regdata == (TEST_RO_DATA_32BIT_LE))
+		sd_trace(("32bit LE regdata = 0x%x\n", regdata));
+	else {
+		sd_trace(("Incorrect 32bit LE regdata = 0x%x\n", regdata));
+		return FALSE;
+	}
+
+
+#define RW_PATTERN1	0xA0A1A2A3
+#define RW_PATTERN2	0x4B5B6B7B
+
+	regdata = RW_PATTERN1;
+	if ((status = bcmspi_card_regwrite(sd, 0, SPID_TEST_RW, 4, regdata)) != SUCCESS)
+		return FALSE;
+	regdata = 0;
+	if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_RW, 4, &regdata)) != SUCCESS)
+		return FALSE;
+	if (regdata != RW_PATTERN1) {
+		sd_err(("Write-Read spid failed. Value wrote = 0x%x, Value read = 0x%x\n",
+			RW_PATTERN1, regdata));
+		return FALSE;
+	} else
+		sd_trace(("R/W spid passed. Value read = 0x%x\n", regdata));
+
+	regdata = RW_PATTERN2;
+	if ((status = bcmspi_card_regwrite(sd, 0, SPID_TEST_RW, 4, regdata)) != SUCCESS)
+		return FALSE;
+	regdata = 0;
+	if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_RW, 4, &regdata)) != SUCCESS)
+		return FALSE;
+	if (regdata != RW_PATTERN2) {
+		sd_err(("Write-Read spid failed. Value wrote = 0x%x, Value read = 0x%x\n",
+			RW_PATTERN2, regdata));
+		return FALSE;
+	} else
+		sd_trace(("R/W spid passed. Value read = 0x%x\n", regdata));
+
+	return TRUE;
+}
+
+static int
+bcmspi_driver_init(sdioh_info_t *sd)
+{
+	sd_trace(("%s\n", __FUNCTION__));
+	if ((bcmspi_host_init(sd)) != SUCCESS) {
+		return ERROR;
+	}
+
+	if (bcmspi_client_init(sd) != SUCCESS) {
+		return ERROR;
+	}
+
+	return SUCCESS;
+}
+
+/* Read device reg */
+static int
+bcmspi_card_regread(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 *data)
+{
+	int status;
+	uint32 cmd_arg, dstatus;
+
+	ASSERT(regsize);
+
+	if (func == 2)
+		sd_trace(("Reg access on F2 will generate error indication in dstatus bits.\n"));
+
+	cmd_arg = 0;
+	cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, 0);
+	cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1);	/* Incremental access */
+	cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func);
+	cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr);
+	cmd_arg = SFIELD(cmd_arg, SPI_LEN, regsize == BLOCK_SIZE_F2 ? 0 : regsize);
+
+	sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg));
+	sd_trace(("%s: rw=%d, func=%d, regaddr=0x%08x, data=0x%x\n", __FUNCTION__, 0, func,
+	         regaddr, *data));
+
+	if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma, cmd_arg, data, regsize))
+	    != SUCCESS)
+		return status;
+
+	bcmspi_cmd_getdstatus(sd, &dstatus);
+	if (dstatus)
+		sd_trace(("dstatus =0x%x\n", dstatus));
+
+	return SUCCESS;
+}
+
+static int
+bcmspi_card_regread_fixedaddr(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 *data)
+{
+
+	int status;
+	uint32 cmd_arg;
+	uint32 dstatus;
+
+	ASSERT(regsize);
+
+	if (func == 2)
+		sd_trace(("Reg access on F2 will generate error indication in dstatus bits.\n"));
+
+	cmd_arg = 0;
+	cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, 0);
+	cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 0);	/* Fixed access */
+	cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func);
+	cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr);
+	cmd_arg = SFIELD(cmd_arg, SPI_LEN, regsize);
+
+	sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg));
+
+	if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma, cmd_arg, data, regsize))
+	    != SUCCESS)
+		return status;
+
+	sd_trace(("%s: rw=%d, func=%d, regaddr=0x%08x, data=0x%x\n", __FUNCTION__, 0, func,
+	         regaddr, *data));
+
+	bcmspi_cmd_getdstatus(sd, &dstatus);
+	sd_trace(("dstatus =0x%x\n", dstatus));
+	return SUCCESS;
+}
+
+/* write a device register */
+static int
+bcmspi_card_regwrite(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 data)
+{
+	int status;
+	uint32 cmd_arg, dstatus;
+
+	ASSERT(regsize);
+
+	cmd_arg = 0;
+
+	cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, 1);
+	cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1);	/* Incremental access */
+	cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func);
+	cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr);
+	cmd_arg = SFIELD(cmd_arg, SPI_LEN, regsize == BLOCK_SIZE_F2 ? 0 : regsize);
+
+	sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg));
+	sd_trace(("%s: rw=%d, func=%d, regaddr=0x%08x, data=0x%x\n", __FUNCTION__, 1, func,
+	         regaddr, data));
+
+
+	if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma, cmd_arg, &data, regsize))
+	    != SUCCESS)
+		return status;
+
+	bcmspi_cmd_getdstatus(sd, &dstatus);
+	if (dstatus)
+		sd_trace(("dstatus =0x%x\n", dstatus));
+
+	return SUCCESS;
+}
+
+/* write a device register - 1 byte */
+static int
+bcmspi_card_bytewrite(sdioh_info_t *sd, int func, uint32 regaddr, uint8 *byte)
+{
+	int status;
+	uint32 cmd_arg;
+	uint32 dstatus;
+	uint32 data = (uint32)(*byte);
+
+	cmd_arg = 0;
+	cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func);
+	cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1);	/* Incremental access */
+	cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr);
+	cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, 1);
+	cmd_arg = SFIELD(cmd_arg, SPI_LEN, 1);
+
+	sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg));
+	sd_trace(("%s: func=%d, regaddr=0x%08x, data=0x%x\n", __FUNCTION__, func,
+	         regaddr, data));
+
+	if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma,
+	                              cmd_arg, &data, 1)) != SUCCESS) {
+		return status;
+	}
+
+	bcmspi_cmd_getdstatus(sd, &dstatus);
+	if (dstatus)
+		sd_trace(("dstatus =0x%x\n", dstatus));
+
+	return SUCCESS;
+}
+
+void
+bcmspi_cmd_getdstatus(sdioh_info_t *sd, uint32 *dstatus_buffer)
+{
+	*dstatus_buffer = sd->card_dstatus;
+}
+
+/* 'data' is of type uint32 whereas other buffers are of type uint8 */
+static int
+bcmspi_cmd_issue(sdioh_info_t *sd, bool use_dma, uint32 cmd_arg,
+                uint32 *data, uint32 datalen)
+{
+	uint32	i, j;
+	uint8	resp_delay = 0;
+	int	err = SUCCESS;
+	uint32	hostlen;
+	uint32 spilen = 0;
+	uint32 dstatus_idx = 0;
+	uint16 templen, buslen, len, *ptr = NULL;
+
+	sd_trace(("spi cmd = 0x%x\n", cmd_arg));
+
+	if (DWORDMODE_ON) {
+		spilen = GFIELD(cmd_arg, SPI_LEN);
+		if ((GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_0) ||
+		    (GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_1))
+			dstatus_idx = spilen * 3;
+
+		if ((GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_2) &&
+		    (GFIELD(cmd_arg, SPI_RW_FLAG) == 1)) {
+			spilen = spilen << 2;
+			dstatus_idx = (spilen % 16) ? (16 - (spilen % 16)) : 0;
+			/* convert len to mod16 size */
+			spilen = ROUNDUP(spilen, 16);
+			cmd_arg = SFIELD(cmd_arg, SPI_LEN, (spilen >> 2));
+		}
+	}
+
+	/* Set up and issue the SPI command.  MSByte goes out on bus first.  Increase datalen
+	 * according to the wordlen mode(16/32bit) the device is in.
+	 */
+	if (sd->wordlen == 4) { /* 32bit spid */
+		*(uint32 *)spi_outbuf = bcmswap32(cmd_arg);
+		if (datalen & 0x3)
+			datalen += (4 - (datalen & 0x3));
+	} else if (sd->wordlen == 2) { /* 16bit spid */
+		*(uint16 *)spi_outbuf = bcmswap16(cmd_arg & 0xffff);
+		*(uint16 *)&spi_outbuf[2] = bcmswap16((cmd_arg & 0xffff0000) >> 16);
+		if (datalen & 0x1)
+			datalen++;
+		if (datalen < 4)
+			datalen = ROUNDUP(datalen, 4);
+	} else {
+		sd_err(("Host is %d bit spid, could not create SPI command.\n",
+			8 * sd->wordlen));
+		return ERROR;
+	}
+
+	/* for Write, put the data into the output buffer */
+	if (GFIELD(cmd_arg, SPI_RW_FLAG) == 1) {
+		/* We send len field of hw-header always a mod16 size, both from host and dongle */
+		if (DWORDMODE_ON) {
+			if (GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_2) {
+				ptr = (uint16 *)&data[0];
+				templen = *ptr;
+				/* ASSERT(*ptr == ~*(ptr + 1)); */
+				templen = ROUNDUP(templen, 16);
+				*ptr = templen;
+				sd_trace(("actual tx len = %d\n", (uint16)(~*(ptr+1))));
+			}
+		}
+
+		if (datalen != 0) {
+			for (i = 0; i < datalen/4; i++) {
+				if (sd->wordlen == 4) { /* 32bit spid */
+					*(uint32 *)&spi_outbuf[i * 4 + CMDLEN] =
+						bcmswap32(data[i]);
+				} else if (sd->wordlen == 2) { /* 16bit spid */
+					*(uint16 *)&spi_outbuf[i * 4 + CMDLEN] =
+						bcmswap16(data[i] & 0xffff);
+					*(uint16 *)&spi_outbuf[i * 4 + CMDLEN + 2] =
+						bcmswap16((data[i] & 0xffff0000) >> 16);
+				}
+			}
+		}
+	}
+
+	/* Append resp-delay number of bytes and clock them out for F0/1/2 reads. */
+	if (GFIELD(cmd_arg, SPI_RW_FLAG) == 0) {
+		int func = GFIELD(cmd_arg, SPI_FUNCTION);
+		switch (func) {
+			case 0:
+				resp_delay = sd->resp_delay_all ? F0_RESPONSE_DELAY : 0;
+				break;
+			case 1:
+				resp_delay = F1_RESPONSE_DELAY;
+				break;
+			case 2:
+				resp_delay = sd->resp_delay_all ? F2_RESPONSE_DELAY : 0;
+				break;
+			default:
+				ASSERT(0);
+				break;
+		}
+		/* Program response delay */
+	        bcmspi_prog_resp_delay(sd, func, resp_delay);
+	}
+
+	/* +4 for cmd and +4 for dstatus */
+	hostlen = datalen + 8 + resp_delay;
+	hostlen += dstatus_idx;
+	hostlen += (4 - (hostlen & 0x3));
+	spi_sendrecv(sd, spi_outbuf, spi_inbuf, hostlen);
+
+	/* for Read, get the data into the input buffer */
+	if (datalen != 0) {
+		if (GFIELD(cmd_arg, SPI_RW_FLAG) == 0) { /* if read cmd */
+			for (j = 0; j < datalen/4; j++) {
+				if (sd->wordlen == 4) { /* 32bit spid */
+					data[j] = bcmswap32(*(uint32 *)&spi_inbuf[j * 4 +
+					            CMDLEN + resp_delay]);
+				} else if (sd->wordlen == 2) { /* 16bit spid */
+					data[j] = (bcmswap16(*(uint16 *)&spi_inbuf[j * 4 +
+					            CMDLEN + resp_delay])) |
+					         ((bcmswap16(*(uint16 *)&spi_inbuf[j * 4 +
+					            CMDLEN + resp_delay + 2])) << 16);
+				}
+			}
+
+			if ((DWORDMODE_ON) && (GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_2)) {
+				ptr = (uint16 *)&data[0];
+				templen = *ptr;
+				buslen = len = ~(*(ptr + 1));
+				buslen = ROUNDUP(buslen, 16);
+				/* populate actual len in hw-header */
+				if (templen == buslen)
+					*ptr = len;
+			}
+		}
+	}
+
+	/* Restore back the len field of the hw header */
+	if (DWORDMODE_ON) {
+		if ((GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_2) &&
+		    (GFIELD(cmd_arg, SPI_RW_FLAG) == 1)) {
+			ptr = (uint16 *)&data[0];
+			*ptr = (uint16)(~*(ptr+1));
+		}
+	}
+
+	dstatus_idx += (datalen + CMDLEN + resp_delay);
+	/* Last 4bytes are dstatus.  Device is configured to return status bits. */
+	if (sd->wordlen == 4) { /* 32bit spid */
+		sd->card_dstatus = bcmswap32(*(uint32 *)&spi_inbuf[dstatus_idx]);
+	} else if (sd->wordlen == 2) { /* 16bit spid */
+		sd->card_dstatus = (bcmswap16(*(uint16 *)&spi_inbuf[dstatus_idx]) |
+		                   (bcmswap16(*(uint16 *)&spi_inbuf[dstatus_idx + 2]) << 16));
+	} else {
+		sd_err(("Host is %d bit machine, could not read SPI dstatus.\n",
+			8 * sd->wordlen));
+		return ERROR;
+	}
+	if (sd->card_dstatus == 0xffffffff) {
+		sd_err(("looks like not a GSPI device or device is not powered.\n"));
+	}
+
+	err = bcmspi_update_stats(sd, cmd_arg);
+
+	return err;
+
+}
+
+static int
+bcmspi_card_buf(sdioh_info_t *sd, int rw, int func, bool fifo,
+                uint32 addr, int nbytes, uint32 *data)
+{
+	int status;
+	uint32 cmd_arg;
+	bool write = rw == SDIOH_READ ? 0 : 1;
+	uint retries = 0;
+
+	bool enable;
+	uint32	spilen;
+
+	cmd_arg = 0;
+
+	ASSERT(nbytes);
+	ASSERT(nbytes <= sd->client_block_size[func]);
+
+	if (write) sd->t_cnt++; else sd->r_cnt++;
+
+	if (func == 2) {
+		/* Frame len check limited by gSPI. */
+		if ((nbytes > 2000) && write) {
+			sd_trace((">2KB write: F2 wr of %d bytes\n", nbytes));
+		}
+		/* ASSERT(nbytes <= 2048); Fix bigger len gspi issue and uncomment. */
+		/* If F2 fifo on device is not ready to receive data, don't do F2 transfer */
+		if (write) {
+			uint32 dstatus;
+			/* check F2 ready with cached one */
+			bcmspi_cmd_getdstatus(sd, &dstatus);
+			if ((dstatus & STATUS_F2_RX_READY) == 0) {
+				retries = WAIT_F2RXFIFORDY;
+				enable = 0;
+				while (retries-- && !enable) {
+					OSL_DELAY(WAIT_F2RXFIFORDY_DELAY * 1000);
+					bcmspi_card_regread(sd, SPI_FUNC_0, SPID_STATUS_REG, 4,
+					                   &dstatus);
+					if (dstatus & STATUS_F2_RX_READY)
+						enable = TRUE;
+				}
+				if (!enable) {
+					struct spierrstats_t *spierrstats = &sd->spierrstats;
+					spierrstats->f2rxnotready++;
+					sd_err(("F2 FIFO is not ready to receive data.\n"));
+					return ERROR;
+				}
+				sd_trace(("No of retries on F2 ready %d\n",
+					(WAIT_F2RXFIFORDY - retries)));
+			}
+		}
+	}
+
+	/* F2 transfers happen on 0 addr */
+	addr = (func == 2) ? 0 : addr;
+
+	/* In pio mode buffer is read using fixed address fifo in func 1 */
+	if ((func == 1) && (fifo))
+		cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 0);
+	else
+		cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1);
+
+	cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func);
+	cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, addr);
+	cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, write);
+	spilen = sd->data_xfer_count = MIN(sd->client_block_size[func], nbytes);
+	if ((sd->dwordmode == TRUE) && (GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_2)) {
+		/* convert len to mod4 size */
+		spilen = spilen + ((spilen & 0x3) ? (4 - (spilen & 0x3)): 0);
+		cmd_arg = SFIELD(cmd_arg, SPI_LEN, (spilen >> 2));
+	} else
+		cmd_arg = SFIELD(cmd_arg, SPI_LEN, spilen);
+
+	if ((func == 2) && (fifo == 1)) {
+		sd_data(("%s: %s func %d, %s, addr 0x%x, len %d bytes, r_cnt %d t_cnt %d\n",
+		          __FUNCTION__, write ? "Wr" : "Rd", func, "INCR",
+		          addr, nbytes, sd->r_cnt, sd->t_cnt));
+	}
+
+	sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg));
+	sd_data(("%s: %s func %d, %s, addr 0x%x, len %d bytes, r_cnt %d t_cnt %d\n",
+	         __FUNCTION__, write ? "Wd" : "Rd", func, "INCR",
+	         addr, nbytes, sd->r_cnt, sd->t_cnt));
+
+
+	if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma, cmd_arg,
+	     data, nbytes)) != SUCCESS) {
+		sd_err(("%s: cmd_issue failed for %s\n", __FUNCTION__,
+			(write ? "write" : "read")));
+		return status;
+	}
+
+	/* gSPI expects that hw-header-len is equal to spi-command-len */
+	if ((func == 2) && (rw == SDIOH_WRITE) && (sd->dwordmode == FALSE)) {
+		ASSERT((uint16)sd->data_xfer_count == (uint16)(*data & 0xffff));
+		ASSERT((uint16)sd->data_xfer_count == (uint16)(~((*data & 0xffff0000) >> 16)));
+	}
+
+	if ((nbytes > 2000) && !write) {
+		sd_trace((">2KB read: F2 rd of %d bytes\n", nbytes));
+	}
+
+	return SUCCESS;
+}
+
+/* Reset and re-initialize the device */
+int
+sdioh_sdio_reset(sdioh_info_t *si)
+{
+	si->card_init_done = FALSE;
+	return bcmspi_client_init(si);
+}
diff --git a/drivers/net/wireless/bcm4329/bcmutils.c b/drivers/net/wireless/bcm4329/bcmutils.c
new file mode 100644
index 0000000..43c04ee
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/bcmutils.c
@@ -0,0 +1,1838 @@
+/*
+ * Driver O/S-independent utility routines
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ * $Id: bcmutils.c,v 1.210.4.5.2.4.6.19 2010/04/26 06:05:25 Exp $
+ */
+
+#include <typedefs.h>
+#include <bcmdefs.h>
+#include <stdarg.h>
+#include <bcmutils.h>
+#ifdef BCMDRIVER
+#include <osl.h>
+#include <siutils.h>
+#else
+#include <stdio.h>
+#include <string.h>
+/* This case for external supplicant use */
+#if defined(BCMEXTSUP)
+#include <bcm_osl.h>
+#endif
+
+#endif /* BCMDRIVER */
+#include <bcmendian.h>
+#include <bcmdevs.h>
+#include <proto/ethernet.h>
+#include <proto/vlan.h>
+#include <proto/bcmip.h>
+#include <proto/802.1d.h>
+#include <proto/802.11.h>
+
+
+#ifdef BCMDRIVER
+
+
+/* copy a pkt buffer chain into a buffer */
+uint
+pktcopy(osl_t *osh, void *p, uint offset, int len, uchar *buf)
+{
+	uint n, ret = 0;
+
+	if (len < 0)
+		len = 4096;	/* "infinite" */
+
+	/* skip 'offset' bytes */
+	for (; p && offset; p = PKTNEXT(osh, p)) {
+		if (offset < (uint)PKTLEN(osh, p))
+			break;
+		offset -= PKTLEN(osh, p);
+	}
+
+	if (!p)
+		return 0;
+
+	/* copy the data */
+	for (; p && len; p = PKTNEXT(osh, p)) {
+		n = MIN((uint)PKTLEN(osh, p) - offset, (uint)len);
+		bcopy(PKTDATA(osh, p) + offset, buf, n);
+		buf += n;
+		len -= n;
+		ret += n;
+		offset = 0;
+	}
+
+	return ret;
+}
+
+/* copy a buffer into a pkt buffer chain */
+uint
+pktfrombuf(osl_t *osh, void *p, uint offset, int len, uchar *buf)
+{
+	uint n, ret = 0;
+
+	/* skip 'offset' bytes */
+	for (; p && offset; p = PKTNEXT(osh, p)) {
+		if (offset < (uint)PKTLEN(osh, p))
+			break;
+		offset -= PKTLEN(osh, p);
+	}
+
+	if (!p)
+		return 0;
+
+	/* copy the data */
+	for (; p && len; p = PKTNEXT(osh, p)) {
+		n = MIN((uint)PKTLEN(osh, p) - offset, (uint)len);
+		bcopy(buf, PKTDATA(osh, p) + offset, n);
+		buf += n;
+		len -= n;
+		ret += n;
+		offset = 0;
+	}
+
+	return ret;
+}
+
+
+
+/* return total length of buffer chain */
+uint
+pkttotlen(osl_t *osh, void *p)
+{
+	uint total;
+
+	total = 0;
+	for (; p; p = PKTNEXT(osh, p))
+		total += PKTLEN(osh, p);
+	return (total);
+}
+
+/* return the last buffer of chained pkt */
+void *
+pktlast(osl_t *osh, void *p)
+{
+	for (; PKTNEXT(osh, p); p = PKTNEXT(osh, p))
+		;
+
+	return (p);
+}
+
+/* count segments of a chained packet */
+uint
+pktsegcnt(osl_t *osh, void *p)
+{
+	uint cnt;
+
+	for (cnt = 0; p; p = PKTNEXT(osh, p))
+		cnt++;
+
+	return cnt;
+}
+
+
+/*
+ * osl multiple-precedence packet queue
+ * hi_prec is always >= the number of the highest non-empty precedence
+ */
+void *
+pktq_penq(struct pktq *pq, int prec, void *p)
+{
+	struct pktq_prec *q;
+
+	ASSERT(prec >= 0 && prec < pq->num_prec);
+	ASSERT(PKTLINK(p) == NULL);         /* queueing chains not allowed */
+
+	ASSERT(!pktq_full(pq));
+	ASSERT(!pktq_pfull(pq, prec));
+
+	q = &pq->q[prec];
+
+	if (q->head)
+		PKTSETLINK(q->tail, p);
+	else
+		q->head = p;
+
+	q->tail = p;
+	q->len++;
+
+	pq->len++;
+
+	if (pq->hi_prec < prec)
+		pq->hi_prec = (uint8)prec;
+
+	return p;
+}
+
+void *
+pktq_penq_head(struct pktq *pq, int prec, void *p)
+{
+	struct pktq_prec *q;
+
+	ASSERT(prec >= 0 && prec < pq->num_prec);
+	ASSERT(PKTLINK(p) == NULL);         /* queueing chains not allowed */
+
+	ASSERT(!pktq_full(pq));
+	ASSERT(!pktq_pfull(pq, prec));
+
+	q = &pq->q[prec];
+
+	if (q->head == NULL)
+		q->tail = p;
+
+	PKTSETLINK(p, q->head);
+	q->head = p;
+	q->len++;
+
+	pq->len++;
+
+	if (pq->hi_prec < prec)
+		pq->hi_prec = (uint8)prec;
+
+	return p;
+}
+
+void *
+pktq_pdeq(struct pktq *pq, int prec)
+{
+	struct pktq_prec *q;
+	void *p;
+
+	ASSERT(prec >= 0 && prec < pq->num_prec);
+
+	q = &pq->q[prec];
+
+	if ((p = q->head) == NULL)
+		return NULL;
+
+	if ((q->head = PKTLINK(p)) == NULL)
+		q->tail = NULL;
+
+	q->len--;
+
+	pq->len--;
+
+	PKTSETLINK(p, NULL);
+
+	return p;
+}
+
+void *
+pktq_pdeq_tail(struct pktq *pq, int prec)
+{
+	struct pktq_prec *q;
+	void *p, *prev;
+
+	ASSERT(prec >= 0 && prec < pq->num_prec);
+
+	q = &pq->q[prec];
+
+	if ((p = q->head) == NULL)
+		return NULL;
+
+	for (prev = NULL; p != q->tail; p = PKTLINK(p))
+		prev = p;
+
+	if (prev)
+		PKTSETLINK(prev, NULL);
+	else
+		q->head = NULL;
+
+	q->tail = prev;
+	q->len--;
+
+	pq->len--;
+
+	return p;
+}
+
+void
+pktq_pflush(osl_t *osh, struct pktq *pq, int prec, bool dir)
+{
+	struct pktq_prec *q;
+	void *p;
+
+	q = &pq->q[prec];
+	p = q->head;
+	while (p) {
+		q->head = PKTLINK(p);
+		PKTSETLINK(p, NULL);
+		PKTFREE(osh, p, dir);
+		q->len--;
+		pq->len--;
+		p = q->head;
+	}
+	ASSERT(q->len == 0);
+	q->tail = NULL;
+}
+
+bool
+pktq_pdel(struct pktq *pq, void *pktbuf, int prec)
+{
+	struct pktq_prec *q;
+	void *p;
+
+	ASSERT(prec >= 0 && prec < pq->num_prec);
+
+	if (!pktbuf)
+		return FALSE;
+
+	q = &pq->q[prec];
+
+	if (q->head == pktbuf) {
+		if ((q->head = PKTLINK(pktbuf)) == NULL)
+			q->tail = NULL;
+	} else {
+		for (p = q->head; p && PKTLINK(p) != pktbuf; p = PKTLINK(p))
+			;
+		if (p == NULL)
+			return FALSE;
+
+		PKTSETLINK(p, PKTLINK(pktbuf));
+		if (q->tail == pktbuf)
+			q->tail = p;
+	}
+
+	q->len--;
+	pq->len--;
+	PKTSETLINK(pktbuf, NULL);
+	return TRUE;
+}
+
+void
+pktq_init(struct pktq *pq, int num_prec, int max_len)
+{
+	int prec;
+
+	ASSERT(num_prec > 0 && num_prec <= PKTQ_MAX_PREC);
+
+	/* pq is variable size; only zero out what's requested */
+	bzero(pq, OFFSETOF(struct pktq, q) + (sizeof(struct pktq_prec) * num_prec));
+
+	pq->num_prec = (uint16)num_prec;
+
+	pq->max = (uint16)max_len;
+
+	for (prec = 0; prec < num_prec; prec++)
+		pq->q[prec].max = pq->max;
+}
+
+void *
+pktq_deq(struct pktq *pq, int *prec_out)
+{
+	struct pktq_prec *q;
+	void *p;
+	int prec;
+
+	if (pq->len == 0)
+		return NULL;
+
+	while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL)
+		pq->hi_prec--;
+
+	q = &pq->q[prec];
+
+	if ((p = q->head) == NULL)
+		return NULL;
+
+	if ((q->head = PKTLINK(p)) == NULL)
+		q->tail = NULL;
+
+	q->len--;
+
+	pq->len--;
+
+	if (prec_out)
+		*prec_out = prec;
+
+	PKTSETLINK(p, NULL);
+
+	return p;
+}
+
+void *
+pktq_deq_tail(struct pktq *pq, int *prec_out)
+{
+	struct pktq_prec *q;
+	void *p, *prev;
+	int prec;
+
+	if (pq->len == 0)
+		return NULL;
+
+	for (prec = 0; prec < pq->hi_prec; prec++)
+		if (pq->q[prec].head)
+			break;
+
+	q = &pq->q[prec];
+
+	if ((p = q->head) == NULL)
+		return NULL;
+
+	for (prev = NULL; p != q->tail; p = PKTLINK(p))
+		prev = p;
+
+	if (prev)
+		PKTSETLINK(prev, NULL);
+	else
+		q->head = NULL;
+
+	q->tail = prev;
+	q->len--;
+
+	pq->len--;
+
+	if (prec_out)
+		*prec_out = prec;
+
+	PKTSETLINK(p, NULL);
+
+	return p;
+}
+
+void *
+pktq_peek(struct pktq *pq, int *prec_out)
+{
+	int prec;
+
+	if (pq->len == 0)
+		return NULL;
+
+	while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL)
+		pq->hi_prec--;
+
+	if (prec_out)
+		*prec_out = prec;
+
+	return (pq->q[prec].head);
+}
+
+void *
+pktq_peek_tail(struct pktq *pq, int *prec_out)
+{
+	int prec;
+
+	if (pq->len == 0)
+		return NULL;
+
+	for (prec = 0; prec < pq->hi_prec; prec++)
+		if (pq->q[prec].head)
+			break;
+
+	if (prec_out)
+		*prec_out = prec;
+
+	return (pq->q[prec].tail);
+}
+
+void
+pktq_flush(osl_t *osh, struct pktq *pq, bool dir)
+{
+	int prec;
+	for (prec = 0; prec < pq->num_prec; prec++)
+		pktq_pflush(osh, pq, prec, dir);
+	ASSERT(pq->len == 0);
+}
+
+/* Return sum of lengths of a specific set of precedences */
+int
+pktq_mlen(struct pktq *pq, uint prec_bmp)
+{
+	int prec, len;
+
+	len = 0;
+
+	for (prec = 0; prec <= pq->hi_prec; prec++)
+		if (prec_bmp & (1 << prec))
+			len += pq->q[prec].len;
+
+	return len;
+}
+
+/* Priority dequeue from a specific set of precedences */
+void *
+pktq_mdeq(struct pktq *pq, uint prec_bmp, int *prec_out)
+{
+	struct pktq_prec *q;
+	void *p;
+	int prec;
+
+	if (pq->len == 0)
+		return NULL;
+
+	while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL)
+		pq->hi_prec--;
+
+	while ((prec_bmp & (1 << prec)) == 0 || pq->q[prec].head == NULL)
+		if (prec-- == 0)
+			return NULL;
+
+	q = &pq->q[prec];
+
+	if ((p = q->head) == NULL)
+		return NULL;
+
+	if ((q->head = PKTLINK(p)) == NULL)
+		q->tail = NULL;
+
+	q->len--;
+
+	if (prec_out)
+		*prec_out = prec;
+
+	pq->len--;
+
+	PKTSETLINK(p, NULL);
+
+	return p;
+}
+#endif /* BCMDRIVER */
+
+
+
+const unsigned char bcm_ctype[] = {
+	_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,			/* 0-7 */
+	_BCM_C, _BCM_C|_BCM_S, _BCM_C|_BCM_S, _BCM_C|_BCM_S, _BCM_C|_BCM_S, _BCM_C|_BCM_S, _BCM_C,
+	_BCM_C,	/* 8-15 */
+	_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,			/* 16-23 */
+	_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,			/* 24-31 */
+	_BCM_S|_BCM_SP,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,		/* 32-39 */
+	_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,			/* 40-47 */
+	_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,			/* 48-55 */
+	_BCM_D,_BCM_D,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,			/* 56-63 */
+	_BCM_P, _BCM_U|_BCM_X, _BCM_U|_BCM_X, _BCM_U|_BCM_X, _BCM_U|_BCM_X, _BCM_U|_BCM_X,
+	_BCM_U|_BCM_X, _BCM_U, /* 64-71 */
+	_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,			/* 72-79 */
+	_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,			/* 80-87 */
+	_BCM_U,_BCM_U,_BCM_U,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,			/* 88-95 */
+	_BCM_P, _BCM_L|_BCM_X, _BCM_L|_BCM_X, _BCM_L|_BCM_X, _BCM_L|_BCM_X, _BCM_L|_BCM_X,
+	_BCM_L|_BCM_X, _BCM_L, /* 96-103 */
+	_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L, /* 104-111 */
+	_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L, /* 112-119 */
+	_BCM_L,_BCM_L,_BCM_L,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_C, /* 120-127 */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,		/* 128-143 */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,		/* 144-159 */
+	_BCM_S|_BCM_SP, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P,
+	_BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P,	/* 160-175 */
+	_BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P,
+	_BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P,	/* 176-191 */
+	_BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U,
+	_BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U,	/* 192-207 */
+	_BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_P, _BCM_U, _BCM_U, _BCM_U,
+	_BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_L,	/* 208-223 */
+	_BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L,
+	_BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L,	/* 224-239 */
+	_BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_P, _BCM_L, _BCM_L, _BCM_L,
+	_BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L /* 240-255 */
+};
+
+ulong
+bcm_strtoul(char *cp, char **endp, uint base)
+{
+	ulong result, last_result = 0, value;
+	bool minus;
+
+	minus = FALSE;
+
+	while (bcm_isspace(*cp))
+		cp++;
+
+	if (cp[0] == '+')
+		cp++;
+	else if (cp[0] == '-') {
+		minus = TRUE;
+		cp++;
+	}
+
+	if (base == 0) {
+		if (cp[0] == '0') {
+			if ((cp[1] == 'x') || (cp[1] == 'X')) {
+				base = 16;
+				cp = &cp[2];
+			} else {
+				base = 8;
+				cp = &cp[1];
+			}
+		} else
+			base = 10;
+	} else if (base == 16 && (cp[0] == '0') && ((cp[1] == 'x') || (cp[1] == 'X'))) {
+		cp = &cp[2];
+	}
+
+	result = 0;
+
+	while (bcm_isxdigit(*cp) &&
+	       (value = bcm_isdigit(*cp) ? *cp-'0' : bcm_toupper(*cp)-'A'+10) < base) {
+		result = result*base + value;
+		/* Detected overflow */
+		if (result < last_result && !minus)
+			return (ulong)-1;
+		last_result = result;
+		cp++;
+	}
+
+	if (minus)
+		result = (ulong)(-(long)result);
+
+	if (endp)
+		*endp = (char *)cp;
+
+	return (result);
+}
+
+int
+bcm_atoi(char *s)
+{
+	return (int)bcm_strtoul(s, NULL, 10);
+}
+
+/* return pointer to location of substring 'needle' in 'haystack' */
+char*
+bcmstrstr(char *haystack, char *needle)
+{
+	int len, nlen;
+	int i;
+
+	if ((haystack == NULL) || (needle == NULL))
+		return (haystack);
+
+	nlen = strlen(needle);
+	len = strlen(haystack) - nlen + 1;
+
+	for (i = 0; i < len; i++)
+		if (memcmp(needle, &haystack[i], nlen) == 0)
+			return (&haystack[i]);
+	return (NULL);
+}
+
+char*
+bcmstrcat(char *dest, const char *src)
+{
+	char *p;
+
+	p = dest + strlen(dest);
+
+	while ((*p++ = *src++) != '\0')
+		;
+
+	return (dest);
+}
+
+char*
+bcmstrncat(char *dest, const char *src, uint size)
+{
+	char *endp;
+	char *p;
+
+	p = dest + strlen(dest);
+	endp = p + size;
+
+	while (p != endp && (*p++ = *src++) != '\0')
+		;
+
+	return (dest);
+}
+
+
+/****************************************************************************
+* Function:   bcmstrtok
+*
+* Purpose:
+*  Tokenizes a string. This function is conceptually similiar to ANSI C strtok(),
+*  but allows strToken() to be used by different strings or callers at the same
+*  time. Each call modifies '*string' by substituting a NULL character for the
+*  first delimiter that is encountered, and updates 'string' to point to the char
+*  after the delimiter. Leading delimiters are skipped.
+*
+* Parameters:
+*  string      (mod) Ptr to string ptr, updated by token.
+*  delimiters  (in)  Set of delimiter characters.
+*  tokdelim    (out) Character that delimits the returned token. (May
+*                    be set to NULL if token delimiter is not required).
+*
+* Returns:  Pointer to the next token found. NULL when no more tokens are found.
+*****************************************************************************
+*/
+char *
+bcmstrtok(char **string, const char *delimiters, char *tokdelim)
+{
+	unsigned char *str;
+	unsigned long map[8];
+	int count;
+	char *nextoken;
+
+	if (tokdelim != NULL) {
+		/* Prime the token delimiter */
+		*tokdelim = '\0';
+	}
+
+	/* Clear control map */
+	for (count = 0; count < 8; count++) {
+		map[count] = 0;
+	}
+
+	/* Set bits in delimiter table */
+	do {
+		map[*delimiters >> 5] |= (1 << (*delimiters & 31));
+	}
+	while (*delimiters++);
+
+	str = (unsigned char*)*string;
+
+	/* Find beginning of token (skip over leading delimiters). Note that
+	 * there is no token iff this loop sets str to point to the terminal
+	 * null (*str == '\0')
+	 */
+	while (((map[*str >> 5] & (1 << (*str & 31))) && *str) || (*str == ' ')) {
+		str++;
+	}
+
+	nextoken = (char*)str;
+
+	/* Find the end of the token. If it is not the end of the string,
+	 * put a null there.
+	 */
+	for (; *str; str++) {
+		if (map[*str >> 5] & (1 << (*str & 31))) {
+			if (tokdelim != NULL) {
+				*tokdelim = *str;
+			}
+
+			*str++ = '\0';
+			break;
+		}
+	}
+
+	*string = (char*)str;
+
+	/* Determine if a token has been found. */
+	if (nextoken == (char *) str) {
+		return NULL;
+	}
+	else {
+		return nextoken;
+	}
+}
+
+
+#define xToLower(C) \
+	((C >= 'A' && C <= 'Z') ? (char)((int)C - (int)'A' + (int)'a') : C)
+
+
+/****************************************************************************
+* Function:   bcmstricmp
+*
+* Purpose:    Compare to strings case insensitively.
+*
+* Parameters: s1 (in) First string to compare.
+*             s2 (in) Second string to compare.
+*
+* Returns:    Return 0 if the two strings are equal, -1 if t1 < t2 and 1 if
+*             t1 > t2, when ignoring case sensitivity.
+*****************************************************************************
+*/
+int
+bcmstricmp(const char *s1, const char *s2)
+{
+	char dc, sc;
+
+	while (*s2 && *s1) {
+		dc = xToLower(*s1);
+		sc = xToLower(*s2);
+		if (dc < sc) return -1;
+		if (dc > sc) return 1;
+		s1++;
+		s2++;
+	}
+
+	if (*s1 && !*s2) return 1;
+	if (!*s1 && *s2) return -1;
+	return 0;
+}
+
+
+/****************************************************************************
+* Function:   bcmstrnicmp
+*
+* Purpose:    Compare to strings case insensitively, upto a max of 'cnt'
+*             characters.
+*
+* Parameters: s1  (in) First string to compare.
+*             s2  (in) Second string to compare.
+*             cnt (in) Max characters to compare.
+*
+* Returns:    Return 0 if the two strings are equal, -1 if t1 < t2 and 1 if
+*             t1 > t2, when ignoring case sensitivity.
+*****************************************************************************
+*/
+int
+bcmstrnicmp(const char* s1, const char* s2, int cnt)
+{
+	char dc, sc;
+
+	while (*s2 && *s1 && cnt) {
+		dc = xToLower(*s1);
+		sc = xToLower(*s2);
+		if (dc < sc) return -1;
+		if (dc > sc) return 1;
+		s1++;
+		s2++;
+		cnt--;
+	}
+
+	if (!cnt) return 0;
+	if (*s1 && !*s2) return 1;
+	if (!*s1 && *s2) return -1;
+	return 0;
+}
+
+/* parse a xx:xx:xx:xx:xx:xx format ethernet address */
+int
+bcm_ether_atoe(char *p, struct ether_addr *ea)
+{
+	int i = 0;
+
+	for (;;) {
+		ea->octet[i++] = (char) bcm_strtoul(p, &p, 16);
+		if (!*p++ || i == 6)
+			break;
+	}
+
+	return (i == 6);
+}
+
+
+#if defined(CONFIG_USBRNDIS_RETAIL) || defined(NDIS_MINIPORT_DRIVER)
+/* registry routine buffer preparation utility functions:
+ * parameter order is like strncpy, but returns count
+ * of bytes copied. Minimum bytes copied is null char(1)/wchar(2)
+ */
+ulong
+wchar2ascii(char *abuf, ushort *wbuf, ushort wbuflen, ulong abuflen)
+{
+	ulong copyct = 1;
+	ushort i;
+
+	if (abuflen == 0)
+		return 0;
+
+	/* wbuflen is in bytes */
+	wbuflen /= sizeof(ushort);
+
+	for (i = 0; i < wbuflen; ++i) {
+		if (--abuflen == 0)
+			break;
+		*abuf++ = (char) *wbuf++;
+		++copyct;
+	}
+	*abuf = '\0';
+
+	return copyct;
+}
+#endif /* CONFIG_USBRNDIS_RETAIL || NDIS_MINIPORT_DRIVER */
+
+char *
+bcm_ether_ntoa(const struct ether_addr *ea, char *buf)
+{
+	static const char template[] = "%02x:%02x:%02x:%02x:%02x:%02x";
+	snprintf(buf, 18, template,
+		ea->octet[0]&0xff, ea->octet[1]&0xff, ea->octet[2]&0xff,
+		ea->octet[3]&0xff, ea->octet[4]&0xff, ea->octet[5]&0xff);
+	return (buf);
+}
+
+char *
+bcm_ip_ntoa(struct ipv4_addr *ia, char *buf)
+{
+	snprintf(buf, 16, "%d.%d.%d.%d",
+	         ia->addr[0], ia->addr[1], ia->addr[2], ia->addr[3]);
+	return (buf);
+}
+
+#ifdef BCMDRIVER
+
+void
+bcm_mdelay(uint ms)
+{
+	uint i;
+
+	for (i = 0; i < ms; i++) {
+		OSL_DELAY(1000);
+	}
+}
+
+
+
+
+
+
+#if defined(DHD_DEBUG)
+/* pretty hex print a pkt buffer chain */
+void
+prpkt(const char *msg, osl_t *osh, void *p0)
+{
+	void *p;
+
+	if (msg && (msg[0] != '\0'))
+		printf("%s:\n", msg);
+
+	for (p = p0; p; p = PKTNEXT(osh, p))
+		prhex(NULL, PKTDATA(osh, p), PKTLEN(osh, p));
+}
+#endif	
+
+/* Takes an Ethernet frame and sets out-of-bound PKTPRIO.
+ * Also updates the inplace vlan tag if requested.
+ * For debugging, it returns an indication of what it did.
+ */
+uint
+pktsetprio(void *pkt, bool update_vtag)
+{
+	struct ether_header *eh;
+	struct ethervlan_header *evh;
+	uint8 *pktdata;
+	int priority = 0;
+	int rc = 0;
+
+	pktdata = (uint8 *) PKTDATA(NULL, pkt);
+	ASSERT(ISALIGNED((uintptr)pktdata, sizeof(uint16)));
+
+	eh = (struct ether_header *) pktdata;
+
+	if (ntoh16(eh->ether_type) == ETHER_TYPE_8021Q) {
+		uint16 vlan_tag;
+		int vlan_prio, dscp_prio = 0;
+
+		evh = (struct ethervlan_header *)eh;
+
+		vlan_tag = ntoh16(evh->vlan_tag);
+		vlan_prio = (int) (vlan_tag >> VLAN_PRI_SHIFT) & VLAN_PRI_MASK;
+
+		if (ntoh16(evh->ether_type) == ETHER_TYPE_IP) {
+			uint8 *ip_body = pktdata + sizeof(struct ethervlan_header);
+			uint8 tos_tc = IP_TOS(ip_body);
+			dscp_prio = (int)(tos_tc >> IPV4_TOS_PREC_SHIFT);
+		}
+
+		/* DSCP priority gets precedence over 802.1P (vlan tag) */
+		if (dscp_prio != 0) {
+			priority = dscp_prio;
+			rc |= PKTPRIO_VDSCP;
+		} else {
+			priority = vlan_prio;
+			rc |= PKTPRIO_VLAN;
+		}
+		/*
+		 * If the DSCP priority is not the same as the VLAN priority,
+		 * then overwrite the priority field in the vlan tag, with the
+		 * DSCP priority value. This is required for Linux APs because
+		 * the VLAN driver on Linux, overwrites the skb->priority field
+		 * with the priority value in the vlan tag
+		 */
+		if (update_vtag && (priority != vlan_prio)) {
+			vlan_tag &= ~(VLAN_PRI_MASK << VLAN_PRI_SHIFT);
+			vlan_tag |= (uint16)priority << VLAN_PRI_SHIFT;
+			evh->vlan_tag = hton16(vlan_tag);
+			rc |= PKTPRIO_UPD;
+		}
+	} else if (ntoh16(eh->ether_type) == ETHER_TYPE_IP) {
+		uint8 *ip_body = pktdata + sizeof(struct ether_header);
+		uint8 tos_tc = IP_TOS(ip_body);
+		priority = (int)(tos_tc >> IPV4_TOS_PREC_SHIFT);
+		rc |= PKTPRIO_DSCP;
+	}
+
+	ASSERT(priority >= 0 && priority <= MAXPRIO);
+	PKTSETPRIO(pkt, priority);
+	return (rc | priority);
+}
+
+static char bcm_undeferrstr[BCME_STRLEN];
+
+static const char *bcmerrorstrtable[] = BCMERRSTRINGTABLE;
+
+/* Convert the error codes into related error strings  */
+const char *
+bcmerrorstr(int bcmerror)
+{
+	/* check if someone added a bcmerror code but forgot to add errorstring */
+	ASSERT(ABS(BCME_LAST) == (ARRAYSIZE(bcmerrorstrtable) - 1));
+
+	if (bcmerror > 0 || bcmerror < BCME_LAST) {
+		snprintf(bcm_undeferrstr, BCME_STRLEN, "Undefined error %d", bcmerror);
+		return bcm_undeferrstr;
+	}
+
+	ASSERT(strlen(bcmerrorstrtable[-bcmerror]) < BCME_STRLEN);
+
+	return bcmerrorstrtable[-bcmerror];
+}
+
+
+
+/* iovar table lookup */
+const bcm_iovar_t*
+bcm_iovar_lookup(const bcm_iovar_t *table, const char *name)
+{
+	const bcm_iovar_t *vi;
+	const char *lookup_name;
+
+	/* skip any ':' delimited option prefixes */
+	lookup_name = strrchr(name, ':');
+	if (lookup_name != NULL)
+		lookup_name++;
+	else
+		lookup_name = name;
+
+	ASSERT(table != NULL);
+
+	for (vi = table; vi->name; vi++) {
+		if (!strcmp(vi->name, lookup_name))
+			return vi;
+	}
+	/* ran to end of table */
+
+	return NULL; /* var name not found */
+}
+
+int
+bcm_iovar_lencheck(const bcm_iovar_t *vi, void *arg, int len, bool set)
+{
+	int bcmerror = 0;
+
+	/* length check on io buf */
+	switch (vi->type) {
+	case IOVT_BOOL:
+	case IOVT_INT8:
+	case IOVT_INT16:
+	case IOVT_INT32:
+	case IOVT_UINT8:
+	case IOVT_UINT16:
+	case IOVT_UINT32:
+		/* all integers are int32 sized args at the ioctl interface */
+		if (len < (int)sizeof(int)) {
+			bcmerror = BCME_BUFTOOSHORT;
+		}
+		break;
+
+	case IOVT_BUFFER:
+		/* buffer must meet minimum length requirement */
+		if (len < vi->minlen) {
+			bcmerror = BCME_BUFTOOSHORT;
+		}
+		break;
+
+	case IOVT_VOID:
+		if (!set) {
+			/* Cannot return nil... */
+			bcmerror = BCME_UNSUPPORTED;
+		} else if (len) {
+			/* Set is an action w/o parameters */
+			bcmerror = BCME_BUFTOOLONG;
+		}
+		break;
+
+	default:
+		/* unknown type for length check in iovar info */
+		ASSERT(0);
+		bcmerror = BCME_UNSUPPORTED;
+	}
+
+	return bcmerror;
+}
+
+#endif	/* BCMDRIVER */
+
+/*******************************************************************************
+ * crc8
+ *
+ * Computes a crc8 over the input data using the polynomial:
+ *
+ *       x^8 + x^7 +x^6 + x^4 + x^2 + 1
+ *
+ * The caller provides the initial value (either CRC8_INIT_VALUE
+ * or the previous returned value) to allow for processing of
+ * discontiguous blocks of data.  When generating the CRC the
+ * caller is responsible for complementing the final return value
+ * and inserting it into the byte stream.  When checking, a final
+ * return value of CRC8_GOOD_VALUE indicates a valid CRC.
+ *
+ * Reference: Dallas Semiconductor Application Note 27
+ *   Williams, Ross N., "A Painless Guide to CRC Error Detection Algorithms",
+ *     ver 3, Aug 1993, ross@guest.adelaide.edu.au, Rocksoft Pty Ltd.,
+ *     ftp://ftp.rocksoft.com/clients/rocksoft/papers/crc_v3.txt
+ *
+ * ****************************************************************************
+ */
+
+STATIC const uint8 crc8_table[256] = {
+    0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B,
+    0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21,
+    0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF,
+    0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5,
+    0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14,
+    0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E,
+    0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80,
+    0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA,
+    0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95,
+    0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF,
+    0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01,
+    0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B,
+    0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA,
+    0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0,
+    0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E,
+    0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34,
+    0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0,
+    0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A,
+    0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54,
+    0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E,
+    0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF,
+    0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5,
+    0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B,
+    0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61,
+    0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E,
+    0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74,
+    0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA,
+    0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0,
+    0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41,
+    0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B,
+    0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5,
+    0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F
+};
+
+#define CRC_INNER_LOOP(n, c, x) \
+	(c) = ((c) >> 8) ^ crc##n##_table[((c) ^ (x)) & 0xff]
+
+uint8
+hndcrc8(
+	uint8 *pdata,	/* pointer to array of data to process */
+	uint  nbytes,	/* number of input data bytes to process */
+	uint8 crc	/* either CRC8_INIT_VALUE or previous return value */
+)
+{
+	/* hard code the crc loop instead of using CRC_INNER_LOOP macro
+	 * to avoid the undefined and unnecessary (uint8 >> 8) operation.
+	 */
+	while (nbytes-- > 0)
+		crc = crc8_table[(crc ^ *pdata++) & 0xff];
+
+	return crc;
+}
+
+/*******************************************************************************
+ * crc16
+ *
+ * Computes a crc16 over the input data using the polynomial:
+ *
+ *       x^16 + x^12 +x^5 + 1
+ *
+ * The caller provides the initial value (either CRC16_INIT_VALUE
+ * or the previous returned value) to allow for processing of
+ * discontiguous blocks of data.  When generating the CRC the
+ * caller is responsible for complementing the final return value
+ * and inserting it into the byte stream.  When checking, a final
+ * return value of CRC16_GOOD_VALUE indicates a valid CRC.
+ *
+ * Reference: Dallas Semiconductor Application Note 27
+ *   Williams, Ross N., "A Painless Guide to CRC Error Detection Algorithms",
+ *     ver 3, Aug 1993, ross@guest.adelaide.edu.au, Rocksoft Pty Ltd.,
+ *     ftp://ftp.rocksoft.com/clients/rocksoft/papers/crc_v3.txt
+ *
+ * ****************************************************************************
+ */
+
+static const uint16 crc16_table[256] = {
+    0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF,
+    0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7,
+    0x1081, 0x0108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E,
+    0x9CC9, 0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876,
+    0x2102, 0x308B, 0x0210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD,
+    0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5,
+    0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, 0x54B5, 0x453C,
+    0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974,
+    0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB,
+    0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3,
+    0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A,
+    0xDECD, 0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72,
+    0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9,
+    0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1,
+    0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x0738,
+    0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70,
+    0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7,
+    0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF,
+    0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036,
+    0x18C1, 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E,
+    0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5,
+    0x2942, 0x38CB, 0x0A50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD,
+    0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134,
+    0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C,
+    0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3,
+    0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, 0x3EFB,
+    0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232,
+    0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A,
+    0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1,
+    0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9,
+    0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330,
+    0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0x0F78
+};
+
+uint16
+hndcrc16(
+    uint8 *pdata,  /* pointer to array of data to process */
+    uint nbytes, /* number of input data bytes to process */
+    uint16 crc     /* either CRC16_INIT_VALUE or previous return value */
+)
+{
+	while (nbytes-- > 0)
+		CRC_INNER_LOOP(16, crc, *pdata++);
+	return crc;
+}
+
+STATIC const uint32 crc32_table[256] = {
+    0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
+    0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
+    0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
+    0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
+    0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
+    0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
+    0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
+    0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
+    0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
+    0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
+    0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
+    0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
+    0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
+    0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
+    0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
+    0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
+    0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
+    0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
+    0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
+    0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
+    0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
+    0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
+    0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
+    0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
+    0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
+    0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
+    0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
+    0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
+    0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
+    0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
+    0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
+    0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
+    0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
+    0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
+    0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
+    0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
+    0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
+    0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
+    0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
+    0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
+    0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
+    0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
+    0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
+    0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
+    0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
+    0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
+    0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
+    0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
+    0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
+    0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
+    0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
+    0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
+    0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
+    0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
+    0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
+    0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
+    0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
+    0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
+    0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
+    0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
+    0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
+    0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
+    0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
+    0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
+};
+
+uint32
+hndcrc32(
+    uint8 *pdata,  /* pointer to array of data to process */
+    uint   nbytes, /* number of input data bytes to process */
+    uint32 crc     /* either CRC32_INIT_VALUE or previous return value */
+)
+{
+	uint8 *pend;
+#ifdef __mips__
+	uint8 tmp[4];
+	ulong *tptr = (ulong *)tmp;
+
+	/* in case the beginning of the buffer isn't aligned */
+	pend = (uint8 *)((uint)(pdata + 3) & 0xfffffffc);
+	nbytes -= (pend - pdata);
+	while (pdata < pend)
+		CRC_INNER_LOOP(32, crc, *pdata++);
+
+	/* handle bulk of data as 32-bit words */
+	pend = pdata + (nbytes & 0xfffffffc);
+	while (pdata < pend) {
+		*tptr = *(ulong *)pdata;
+		pdata += sizeof(ulong *);
+		CRC_INNER_LOOP(32, crc, tmp[0]);
+		CRC_INNER_LOOP(32, crc, tmp[1]);
+		CRC_INNER_LOOP(32, crc, tmp[2]);
+		CRC_INNER_LOOP(32, crc, tmp[3]);
+	}
+
+	/* 1-3 bytes at end of buffer */
+	pend = pdata + (nbytes & 0x03);
+	while (pdata < pend)
+		CRC_INNER_LOOP(32, crc, *pdata++);
+#else
+	pend = pdata + nbytes;
+	while (pdata < pend)
+		CRC_INNER_LOOP(32, crc, *pdata++);
+#endif /* __mips__ */
+
+	return crc;
+}
+
+#ifdef notdef
+#define CLEN 	1499 	/*  CRC Length */
+#define CBUFSIZ 	(CLEN+4)
+#define CNBUFS		5 /* # of bufs */
+
+void testcrc32(void)
+{
+	uint j, k, l;
+	uint8 *buf;
+	uint len[CNBUFS];
+	uint32 crcr;
+	uint32 crc32tv[CNBUFS] =
+		{0xd2cb1faa, 0xd385c8fa, 0xf5b4f3f3, 0x55789e20, 0x00343110};
+
+	ASSERT((buf = MALLOC(CBUFSIZ*CNBUFS)) != NULL);
+
+	/* step through all possible alignments */
+	for (l = 0; l <= 4; l++) {
+		for (j = 0; j < CNBUFS; j++) {
+			len[j] = CLEN;
+			for (k = 0; k < len[j]; k++)
+				*(buf + j*CBUFSIZ + (k+l)) = (j+k) & 0xff;
+		}
+
+		for (j = 0; j < CNBUFS; j++) {
+			crcr = crc32(buf + j*CBUFSIZ + l, len[j], CRC32_INIT_VALUE);
+			ASSERT(crcr == crc32tv[j]);
+		}
+	}
+
+	MFREE(buf, CBUFSIZ*CNBUFS);
+	return;
+}
+#endif /* notdef */
+
+/*
+ * Advance from the current 1-byte tag/1-byte length/variable-length value
+ * triple, to the next, returning a pointer to the next.
+ * If the current or next TLV is invalid (does not fit in given buffer length),
+ * NULL is returned.
+ * *buflen is not modified if the TLV elt parameter is invalid, or is decremented
+ * by the TLV parameter's length if it is valid.
+ */
+bcm_tlv_t *
+bcm_next_tlv(bcm_tlv_t *elt, int *buflen)
+{
+	int len;
+
+	/* validate current elt */
+	if (!bcm_valid_tlv(elt, *buflen))
+		return NULL;
+
+	/* advance to next elt */
+	len = elt->len;
+	elt = (bcm_tlv_t*)(elt->data + len);
+	*buflen -= (2 + len);
+
+	/* validate next elt */
+	if (!bcm_valid_tlv(elt, *buflen))
+		return NULL;
+
+	return elt;
+}
+
+/*
+ * Traverse a string of 1-byte tag/1-byte length/variable-length value
+ * triples, returning a pointer to the substring whose first element
+ * matches tag
+ */
+bcm_tlv_t *
+bcm_parse_tlvs(void *buf, int buflen, uint key)
+{
+	bcm_tlv_t *elt;
+	int totlen;
+
+	elt = (bcm_tlv_t*)buf;
+	totlen = buflen;
+
+	/* find tagged parameter */
+	while (totlen >= 2) {
+		int len = elt->len;
+
+		/* validate remaining totlen */
+		if ((elt->id == key) && (totlen >= (len + 2)))
+			return (elt);
+
+		elt = (bcm_tlv_t*)((uint8*)elt + (len + 2));
+		totlen -= (len + 2);
+	}
+
+	return NULL;
+}
+
+/*
+ * Traverse a string of 1-byte tag/1-byte length/variable-length value
+ * triples, returning a pointer to the substring whose first element
+ * matches tag.  Stop parsing when we see an element whose ID is greater
+ * than the target key.
+ */
+bcm_tlv_t *
+bcm_parse_ordered_tlvs(void *buf, int buflen, uint key)
+{
+	bcm_tlv_t *elt;
+	int totlen;
+
+	elt = (bcm_tlv_t*)buf;
+	totlen = buflen;
+
+	/* find tagged parameter */
+	while (totlen >= 2) {
+		uint id = elt->id;
+		int len = elt->len;
+
+		/* Punt if we start seeing IDs > than target key */
+		if (id > key)
+			return (NULL);
+
+		/* validate remaining totlen */
+		if ((id == key) && (totlen >= (len + 2)))
+			return (elt);
+
+		elt = (bcm_tlv_t*)((uint8*)elt + (len + 2));
+		totlen -= (len + 2);
+	}
+	return NULL;
+}
+
+#if defined(WLMSG_PRHDRS) || defined(WLMSG_PRPKT) || defined(WLMSG_ASSOC) || \
+	defined(DHD_DEBUG)
+int
+bcm_format_flags(const bcm_bit_desc_t *bd, uint32 flags, char* buf, int len)
+{
+	int i;
+	char* p = buf;
+	char hexstr[16];
+	int slen = 0;
+	uint32 bit;
+	const char* name;
+
+	if (len < 2 || !buf)
+		return 0;
+
+	buf[0] = '\0';
+	len -= 1;
+
+	for (i = 0; flags != 0; i++) {
+		bit = bd[i].bit;
+		name = bd[i].name;
+		if (bit == 0 && flags) {
+			/* print any unnamed bits */
+			sprintf(hexstr, "0x%X", flags);
+			name = hexstr;
+			flags = 0;	/* exit loop */
+		} else if ((flags & bit) == 0)
+			continue;
+		slen += strlen(name);
+		if (len < slen)
+			break;
+		if (p != buf) p += sprintf(p, " "); /* btwn flag space */
+		strcat(p, name);
+		p += strlen(name);
+		flags &= ~bit;
+		len -= slen;
+		slen = 1;	/* account for btwn flag space */
+	}
+
+	/* indicate the str was too short */
+	if (flags != 0) {
+		if (len == 0)
+			p--;	/* overwrite last char */
+		p += sprintf(p, ">");
+	}
+
+	return (int)(p - buf);
+}
+
+/* print bytes formatted as hex to a string. return the resulting string length */
+int
+bcm_format_hex(char *str, const void *bytes, int len)
+{
+	int i;
+	char *p = str;
+	const uint8 *src = (const uint8*)bytes;
+
+	for (i = 0; i < len; i++) {
+		p += sprintf(p, "%02X", *src);
+		src++;
+	}
+	return (int)(p - str);
+}
+
+/* pretty hex print a contiguous buffer */
+void
+prhex(const char *msg, uchar *buf, uint nbytes)
+{
+	char line[128], *p;
+	uint i;
+
+	if (msg && (msg[0] != '\0'))
+		printf("%s:\n", msg);
+
+	p = line;
+	for (i = 0; i < nbytes; i++) {
+		if (i % 16 == 0) {
+			p += sprintf(p, "  %04d: ", i);	/* line prefix */
+		}
+		p += sprintf(p, "%02x ", buf[i]);
+		if (i % 16 == 15) {
+			printf("%s\n", line);		/* flush line */
+			p = line;
+		}
+	}
+
+	/* flush last partial line */
+	if (p != line)
+		printf("%s\n", line);
+}
+#endif 
+
+
+/* Produce a human-readable string for boardrev */
+char *
+bcm_brev_str(uint32 brev, char *buf)
+{
+	if (brev < 0x100)
+		snprintf(buf, 8, "%d.%d", (brev & 0xf0) >> 4, brev & 0xf);
+	else
+		snprintf(buf, 8, "%c%03x", ((brev & 0xf000) == 0x1000) ? 'P' : 'A', brev & 0xfff);
+
+	return (buf);
+}
+
+#define BUFSIZE_TODUMP_ATONCE 512 /* Buffer size */
+
+/* dump large strings to console */
+void
+printbig(char *buf)
+{
+	uint len, max_len;
+	char c;
+
+	len = strlen(buf);
+
+	max_len = BUFSIZE_TODUMP_ATONCE;
+
+	while (len > max_len) {
+		c = buf[max_len];
+		buf[max_len] = '\0';
+		printf("%s", buf);
+		buf[max_len] = c;
+
+		buf += max_len;
+		len -= max_len;
+	}
+	/* print the remaining string */
+	printf("%s\n", buf);
+	return;
+}
+
+/* routine to dump fields in a fileddesc structure */
+uint
+bcmdumpfields(bcmutl_rdreg_rtn read_rtn, void *arg0, uint arg1, struct fielddesc *fielddesc_array,
+	char *buf, uint32 bufsize)
+{
+	uint  filled_len;
+	int len;
+	struct fielddesc *cur_ptr;
+
+	filled_len = 0;
+	cur_ptr = fielddesc_array;
+
+	while (bufsize > 1) {
+		if (cur_ptr->nameandfmt == NULL)
+			break;
+		len = snprintf(buf, bufsize, cur_ptr->nameandfmt,
+		               read_rtn(arg0, arg1, cur_ptr->offset));
+		/* check for snprintf overflow or error */
+		if (len < 0 || (uint32)len >= bufsize)
+			len = bufsize - 1;
+		buf += len;
+		bufsize -= len;
+		filled_len += len;
+		cur_ptr++;
+	}
+	return filled_len;
+}
+
+uint
+bcm_mkiovar(char *name, char *data, uint datalen, char *buf, uint buflen)
+{
+	uint len;
+
+	len = strlen(name) + 1;
+
+	if ((len + datalen) > buflen)
+		return 0;
+
+	strncpy(buf, name, buflen);
+
+	/* append data onto the end of the name string */
+	memcpy(&buf[len], data, datalen);
+	len += datalen;
+
+	return len;
+}
+
+/* Quarter dBm units to mW
+ * Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=153
+ * Table is offset so the last entry is largest mW value that fits in
+ * a uint16.
+ */
+
+#define QDBM_OFFSET 153		/* Offset for first entry */
+#define QDBM_TABLE_LEN 40	/* Table size */
+
+/* Smallest mW value that will round up to the first table entry, QDBM_OFFSET.
+ * Value is ( mW(QDBM_OFFSET - 1) + mW(QDBM_OFFSET) ) / 2
+ */
+#define QDBM_TABLE_LOW_BOUND 6493 /* Low bound */
+
+/* Largest mW value that will round down to the last table entry,
+ * QDBM_OFFSET + QDBM_TABLE_LEN-1.
+ * Value is ( mW(QDBM_OFFSET + QDBM_TABLE_LEN - 1) + mW(QDBM_OFFSET + QDBM_TABLE_LEN) ) / 2.
+ */
+#define QDBM_TABLE_HIGH_BOUND 64938 /* High bound */
+
+static const uint16 nqdBm_to_mW_map[QDBM_TABLE_LEN] = {
+/* qdBm: 	+0 	+1 	+2 	+3 	+4 	+5 	+6 	+7 */
+/* 153: */      6683,	7079,	7499,	7943,	8414,	8913,	9441,	10000,
+/* 161: */      10593,	11220,	11885,	12589,	13335,	14125,	14962,	15849,
+/* 169: */      16788,	17783,	18836,	19953,	21135,	22387,	23714,	25119,
+/* 177: */      26607,	28184,	29854,	31623,	33497,	35481,	37584,	39811,
+/* 185: */      42170,	44668,	47315,	50119,	53088,	56234,	59566,	63096
+};
+
+uint16
+bcm_qdbm_to_mw(uint8 qdbm)
+{
+	uint factor = 1;
+	int idx = qdbm - QDBM_OFFSET;
+
+	if (idx >= QDBM_TABLE_LEN) {
+		/* clamp to max uint16 mW value */
+		return 0xFFFF;
+	}
+
+	/* scale the qdBm index up to the range of the table 0-40
+	 * where an offset of 40 qdBm equals a factor of 10 mW.
+	 */
+	while (idx < 0) {
+		idx += 40;
+		factor *= 10;
+	}
+
+	/* return the mW value scaled down to the correct factor of 10,
+	 * adding in factor/2 to get proper rounding.
+	 */
+	return ((nqdBm_to_mW_map[idx] + factor/2) / factor);
+}
+
+uint8
+bcm_mw_to_qdbm(uint16 mw)
+{
+	uint8 qdbm;
+	int offset;
+	uint mw_uint = mw;
+	uint boundary;
+
+	/* handle boundary case */
+	if (mw_uint <= 1)
+		return 0;
+
+	offset = QDBM_OFFSET;
+
+	/* move mw into the range of the table */
+	while (mw_uint < QDBM_TABLE_LOW_BOUND) {
+		mw_uint *= 10;
+		offset -= 40;
+	}
+
+	for (qdbm = 0; qdbm < QDBM_TABLE_LEN-1; qdbm++) {
+		boundary = nqdBm_to_mW_map[qdbm] + (nqdBm_to_mW_map[qdbm+1] -
+		                                    nqdBm_to_mW_map[qdbm])/2;
+		if (mw_uint < boundary) break;
+	}
+
+	qdbm += (uint8)offset;
+
+	return (qdbm);
+}
+
+
+uint
+bcm_bitcount(uint8 *bitmap, uint length)
+{
+	uint bitcount = 0, i;
+	uint8 tmp;
+	for (i = 0; i < length; i++) {
+		tmp = bitmap[i];
+		while (tmp) {
+			bitcount++;
+			tmp &= (tmp - 1);
+		}
+	}
+	return bitcount;
+}
+
+#ifdef BCMDRIVER
+
+/* Initialization of bcmstrbuf structure */
+void
+bcm_binit(struct bcmstrbuf *b, char *buf, uint size)
+{
+	b->origsize = b->size = size;
+	b->origbuf = b->buf = buf;
+}
+
+/* Buffer sprintf wrapper to guard against buffer overflow */
+int
+bcm_bprintf(struct bcmstrbuf *b, const char *fmt, ...)
+{
+	va_list ap;
+	int r;
+
+	va_start(ap, fmt);
+	r = vsnprintf(b->buf, b->size, fmt, ap);
+
+	/* Non Ansi C99 compliant returns -1,
+	 * Ansi compliant return r >= b->size,
+	 * bcmstdlib returns 0, handle all
+	 */
+	if ((r == -1) || (r >= (int)b->size) || (r == 0)) {
+		b->size = 0;
+	} else {
+		b->size -= r;
+		b->buf += r;
+	}
+
+	va_end(ap);
+
+	return r;
+}
+
+void
+bcm_inc_bytes(uchar *num, int num_bytes, uint8 amount)
+{
+	int i;
+
+	for (i = 0; i < num_bytes; i++) {
+		num[i] += amount;
+		if (num[i] >= amount)
+			break;
+		amount = 1;
+	}
+}
+
+int
+bcm_cmp_bytes(uchar *arg1, uchar *arg2, uint8 nbytes)
+{
+	int i;
+
+	for (i = nbytes - 1; i >= 0; i--) {
+		if (arg1[i] != arg2[i])
+			return (arg1[i] - arg2[i]);
+	}
+	return 0;
+}
+
+void
+bcm_print_bytes(char *name, const uchar *data, int len)
+{
+	int i;
+	int per_line = 0;
+
+	printf("%s: %d \n", name ? name : "", len);
+	for (i = 0; i < len; i++) {
+		printf("%02x ", *data++);
+		per_line++;
+		if (per_line == 16) {
+			per_line = 0;
+			printf("\n");
+		}
+	}
+	printf("\n");
+}
+
+/*
+ * buffer length needed for wlc_format_ssid
+ * 32 SSID chars, max of 4 chars for each SSID char "\xFF", plus NULL.
+ */
+
+#if defined(WLTINYDUMP) || defined(WLMSG_INFORM) || defined(WLMSG_ASSOC) || \
+	defined(WLMSG_PRPKT) || defined(WLMSG_WSEC)
+int
+bcm_format_ssid(char* buf, const uchar ssid[], uint ssid_len)
+{
+	uint i, c;
+	char *p = buf;
+	char *endp = buf + SSID_FMT_BUF_LEN;
+
+	if (ssid_len > DOT11_MAX_SSID_LEN) ssid_len = DOT11_MAX_SSID_LEN;
+
+	for (i = 0; i < ssid_len; i++) {
+		c = (uint)ssid[i];
+		if (c == '\\') {
+			*p++ = '\\';
+			*p++ = '\\';
+		} else if (bcm_isprint((uchar)c)) {
+			*p++ = (char)c;
+		} else {
+			p += snprintf(p, (endp - p), "\\x%02X", c);
+		}
+	}
+	*p = '\0';
+	ASSERT(p < endp);
+
+	return (int)(p - buf);
+}
+#endif 
+
+#endif /* BCMDRIVER */
diff --git a/drivers/net/wireless/bcm4329/bcmwifi.c b/drivers/net/wireless/bcm4329/bcmwifi.c
new file mode 100644
index 0000000..803acf8
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/bcmwifi.c
@@ -0,0 +1,199 @@
+/*
+ * Misc utility routines used by kernel or app-level.
+ * Contents are wifi-specific, used by any kernel or app-level
+ * software that might want wifi things as it grows.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ * $Id: bcmwifi.c,v 1.18.24.2.4.1 2009/09/25 00:32:01 Exp $
+ */
+
+
+#include <typedefs.h>
+
+#ifdef BCMDRIVER
+#include <osl.h>
+#include <bcmutils.h>
+#define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base))
+#define tolower(c) (bcm_isupper((c)) ? ((c) + 'a' - 'A') : (c))
+#else
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#endif 
+#include <bcmwifi.h>
+
+#if defined(WIN32) && (defined(BCMDLL) || defined(WLMDLL))
+#include <bcmstdlib.h> 	
+#endif
+
+
+
+
+
+char *
+wf_chspec_ntoa(chanspec_t chspec, char *buf)
+{
+	const char *band, *bw, *sb;
+	uint channel;
+
+	band = "";
+	bw = "";
+	sb = "";
+	channel = CHSPEC_CHANNEL(chspec);
+	
+	if ((CHSPEC_IS2G(chspec) && channel > CH_MAX_2G_CHANNEL) ||
+	    (CHSPEC_IS5G(chspec) && channel <= CH_MAX_2G_CHANNEL))
+		band = (CHSPEC_IS2G(chspec)) ? "b" : "a";
+	if (CHSPEC_IS40(chspec)) {
+		if (CHSPEC_SB_UPPER(chspec)) {
+			sb = "u";
+			channel += CH_10MHZ_APART;
+		} else {
+			sb = "l";
+			channel -= CH_10MHZ_APART;
+		}
+	} else if (CHSPEC_IS10(chspec)) {
+		bw = "n";
+	}
+
+	
+	snprintf(buf, 6, "%d%s%s%s", channel, band, bw, sb);
+	return (buf);
+}
+
+
+chanspec_t
+wf_chspec_aton(char *a)
+{
+	char *endp = NULL;
+	uint channel, band, bw, ctl_sb;
+	char c;
+
+	channel = strtoul(a, &endp, 10);
+
+	
+	if (endp == a)
+		return 0;
+
+	if (channel > MAXCHANNEL)
+		return 0;
+
+	band = ((channel <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G);
+	bw = WL_CHANSPEC_BW_20;
+	ctl_sb = WL_CHANSPEC_CTL_SB_NONE;
+
+	a = endp;
+
+	c = tolower(a[0]);
+	if (c == '\0')
+		goto done;
+
+	
+	if (c == 'a' || c == 'b') {
+		band = (c == 'a') ? WL_CHANSPEC_BAND_5G : WL_CHANSPEC_BAND_2G;
+		a++;
+		c = tolower(a[0]);
+		if (c == '\0')
+			goto done;
+	}
+
+	
+	if (c == 'n') {
+		bw = WL_CHANSPEC_BW_10;
+	} else if (c == 'l') {
+		bw = WL_CHANSPEC_BW_40;
+		ctl_sb = WL_CHANSPEC_CTL_SB_LOWER;
+		
+		if (channel <= (MAXCHANNEL - CH_20MHZ_APART))
+			channel += CH_10MHZ_APART;
+		else
+			return 0;
+	} else if (c == 'u') {
+		bw = WL_CHANSPEC_BW_40;
+		ctl_sb = WL_CHANSPEC_CTL_SB_UPPER;
+		
+		if (channel > CH_20MHZ_APART)
+			channel -= CH_10MHZ_APART;
+		else
+			return 0;
+	} else {
+		return 0;
+	}
+
+done:
+	return (channel | band | bw | ctl_sb);
+}
+
+
+int
+wf_mhz2channel(uint freq, uint start_factor)
+{
+	int ch = -1;
+	uint base;
+	int offset;
+
+	
+	if (start_factor == 0) {
+		if (freq >= 2400 && freq <= 2500)
+			start_factor = WF_CHAN_FACTOR_2_4_G;
+		else if (freq >= 5000 && freq <= 6000)
+			start_factor = WF_CHAN_FACTOR_5_G;
+	}
+
+	if (freq == 2484 && start_factor == WF_CHAN_FACTOR_2_4_G)
+		return 14;
+
+	base = start_factor / 2;
+
+	
+	if ((freq < base) || (freq > base + 1000))
+		return -1;
+
+	offset = freq - base;
+	ch = offset / 5;
+
+	
+	if (offset != (ch * 5))
+		return -1;
+
+	
+	if (start_factor == WF_CHAN_FACTOR_2_4_G && (ch < 1 || ch > 13))
+		return -1;
+
+	return ch;
+}
+
+
+int
+wf_channel2mhz(uint ch, uint start_factor)
+{
+	int freq;
+
+	if ((start_factor == WF_CHAN_FACTOR_2_4_G && (ch < 1 || ch > 14)) ||
+	    (ch <= 200))
+		freq = -1;
+	if ((start_factor == WF_CHAN_FACTOR_2_4_G) && (ch == 14))
+		freq = 2484;
+	else
+		freq = ch * 5 + start_factor / 2;
+
+	return freq;
+}
diff --git a/drivers/net/wireless/bcm4329/dhd.h b/drivers/net/wireless/bcm4329/dhd.h
new file mode 100644
index 0000000..0b2e9c2
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/dhd.h
@@ -0,0 +1,465 @@
+/*
+ * Header file describing the internal (inter-module) DHD interfaces.
+ *
+ * Provides type definitions and function prototypes used to link the
+ * DHD OS, bus, and protocol modules.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: dhd.h,v 1.32.4.7.2.4.14.49.4.9 2011/01/14 22:40:45 Exp $
+ */
+
+/****************
+ * Common types *
+ */
+
+#ifndef _dhd_h_
+#define _dhd_h_
+
+#if defined(LINUX)
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/random.h>
+#include <linux/spinlock.h>
+#include <linux/ethtool.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+
+/* The kernel threading is sdio-specific */
+#else /* LINUX */
+#define ENOMEM		1
+#define EFAULT      2
+#define EINVAL		3
+#define EIO			4
+#define ETIMEDOUT	5
+#define ERESTARTSYS 6
+#endif /* LINUX */
+
+#include <wlioctl.h>
+
+#ifdef DHD_DEBUG
+#ifndef DHD_DEBUG_TRAP
+#define DHD_DEBUG_TRAP
+#endif
+#endif
+
+/* Forward decls */
+struct dhd_bus;
+struct dhd_prot;
+struct dhd_info;
+
+/* The level of bus communication with the dongle */
+enum dhd_bus_state {
+	DHD_BUS_DOWN,		/* Not ready for frame transfers */
+	DHD_BUS_LOAD,		/* Download access only (CPU reset) */
+	DHD_BUS_DATA		/* Ready for frame transfers */
+};
+
+enum dhd_bus_wake_state {
+	WAKE_LOCK_OFF,
+	WAKE_LOCK_PRIV,
+	WAKE_LOCK_DPC,
+	WAKE_LOCK_IOCTL,
+	WAKE_LOCK_DOWNLOAD,
+	WAKE_LOCK_TMOUT,
+	WAKE_LOCK_WATCHDOG,
+	WAKE_LOCK_LINK_DOWN_TMOUT,
+	WAKE_LOCK_PNO_FIND_TMOUT,
+	WAKE_LOCK_SOFTAP_SET,
+	WAKE_LOCK_SOFTAP_STOP,
+	WAKE_LOCK_SOFTAP_START,
+	WAKE_LOCK_SOFTAP_THREAD,
+	WAKE_LOCK_MAX
+};
+enum dhd_prealloc_index {
+	DHD_PREALLOC_PROT = 0,
+	DHD_PREALLOC_RXBUF,
+	DHD_PREALLOC_DATABUF,
+	DHD_PREALLOC_OSL_BUF
+};
+#ifdef DHD_USE_STATIC_BUF
+extern void * dhd_os_prealloc(int section, unsigned long size);
+#endif
+/* Common structure for module and instance linkage */
+typedef struct dhd_pub {
+	/* Linkage ponters */
+	osl_t *osh;		/* OSL handle */
+	struct dhd_bus *bus;	/* Bus module handle */
+	struct dhd_prot *prot;	/* Protocol module handle */
+	struct dhd_info  *info; /* Info module handle */
+
+	/* Internal dhd items */
+	bool up;		/* Driver up/down (to OS) */
+	bool txoff;		/* Transmit flow-controlled */
+	bool dongle_reset;  /* TRUE = DEVRESET put dongle into reset */
+	enum dhd_bus_state busstate;
+	uint hdrlen;		/* Total DHD header length (proto + bus) */
+	uint maxctl;		/* Max size rxctl request from proto to bus */
+	uint rxsz;		/* Rx buffer size bus module should use */
+	uint8 wme_dp;	/* wme discard priority */
+
+	/* Dongle media info */
+	bool iswl;		/* Dongle-resident driver is wl */
+	ulong drv_version;	/* Version of dongle-resident driver */
+	struct ether_addr mac;	/* MAC address obtained from dongle */
+	dngl_stats_t dstats;	/* Stats for dongle-based data */
+
+	/* Additional stats for the bus level */
+	ulong tx_packets;	/* Data packets sent to dongle */
+	ulong tx_multicast;	/* Multicast data packets sent to dongle */
+	ulong tx_errors;	/* Errors in sending data to dongle */
+	ulong tx_ctlpkts;	/* Control packets sent to dongle */
+	ulong tx_ctlerrs;	/* Errors sending control frames to dongle */
+	ulong rx_packets;	/* Packets sent up the network interface */
+	ulong rx_multicast;	/* Multicast packets sent up the network interface */
+	ulong rx_errors;	/* Errors processing rx data packets */
+	ulong rx_ctlpkts;	/* Control frames processed from dongle */
+	ulong rx_ctlerrs;	/* Errors in processing rx control frames */
+	ulong rx_dropped;	/* Packets dropped locally (no memory) */
+	ulong rx_flushed;  /* Packets flushed due to unscheduled sendup thread */
+	ulong wd_dpc_sched;   /* Number of times dhd dpc scheduled by watchdog timer */
+
+	ulong rx_readahead_cnt;	/* Number of packets where header read-ahead was used. */
+	ulong tx_realloc;	/* Number of tx packets we had to realloc for headroom */
+	ulong fc_packets;	/* Number of flow control pkts recvd */
+
+	/* Last error return */
+	int bcmerror;
+	uint tickcnt;
+
+	/* Last error from dongle */
+	int dongle_error;
+
+	/* Suspend disable flag and "in suspend" flag */
+	int suspend_disable_flag; /* "1" to disable all extra powersaving during suspend */
+	int in_suspend;			/* flag set to 1 when early suspend called */
+	int hang_was_sent;	/* flag that message was send at least once */
+#ifdef PNO_SUPPORT
+	int pno_enable;                 /* pno status : "1" is pno enable */
+#endif /* PNO_SUPPORT */
+	int dtim_skip;         /* dtim skip , default 0 means wake each dtim */
+
+	/* Pkt filter defination */
+	char * pktfilter[100];
+	int pktfilter_count;
+
+	wl_country_t dhd_cspec;		/* Current Locale info */
+	char eventmask[WL_EVENTING_MASK_LEN];
+
+} dhd_pub_t;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP)
+
+	#define DHD_PM_RESUME_WAIT_INIT(a) DECLARE_WAIT_QUEUE_HEAD(a);
+	#define _DHD_PM_RESUME_WAIT(a, b) do { \
+			int retry = 0; \
+			smp_mb(); \
+			while (dhd_mmc_suspend && retry++ != b) { \
+				wait_event_interruptible_timeout(a, FALSE, HZ/100); \
+			} \
+		} 	while (0)
+	#define DHD_PM_RESUME_WAIT(a) 		_DHD_PM_RESUME_WAIT(a, 200)
+	#define DHD_PM_RESUME_WAIT_FOREVER(a) 	_DHD_PM_RESUME_WAIT(a, ~0)
+	#define DHD_PM_RESUME_RETURN_ERROR(a)	do { if (dhd_mmc_suspend) return a; } while (0)
+	#define DHD_PM_RESUME_RETURN		do { if (dhd_mmc_suspend) return; } while (0)
+
+	#define DHD_SPINWAIT_SLEEP_INIT(a) DECLARE_WAIT_QUEUE_HEAD(a);
+	#define SPINWAIT_SLEEP(a, exp, us) do { \
+		uint countdown = (us) + 9999; \
+		while ((exp) && (countdown >= 10000)) { \
+			wait_event_interruptible_timeout(a, FALSE, HZ/100); \
+			countdown -= 10000; \
+		} \
+	} while (0)
+
+#else
+
+	#define DHD_PM_RESUME_WAIT_INIT(a)
+	#define DHD_PM_RESUME_WAIT(a)
+	#define DHD_PM_RESUME_WAIT_FOREVER(a)
+	#define DHD_PM_RESUME_RETURN_ERROR(a)
+	#define DHD_PM_RESUME_RETURN
+
+	#define DHD_SPINWAIT_SLEEP_INIT(a)
+	#define SPINWAIT_SLEEP(a, exp, us)  do { \
+		uint countdown = (us) + 9; \
+		while ((exp) && (countdown >= 10)) { \
+			OSL_DELAY(10);  \
+			countdown -= 10;  \
+		} \
+	} while (0)
+
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */
+
+#define DHD_IF_VIF	0x01	/* Virtual IF (Hidden from user) */
+
+inline static void NETIF_ADDR_LOCK(struct net_device *dev)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29))
+	netif_addr_lock_bh(dev);
+#endif
+}
+
+inline static void NETIF_ADDR_UNLOCK(struct net_device *dev)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29))
+	netif_addr_unlock_bh(dev);
+#endif
+}
+
+/* Wakelock Functions */
+extern int dhd_os_wake_lock(dhd_pub_t *pub);
+extern int dhd_os_wake_unlock(dhd_pub_t *pub);
+extern int dhd_os_wake_lock_timeout(dhd_pub_t *pub);
+extern int dhd_os_wake_lock_timeout_enable(dhd_pub_t *pub);
+
+extern void dhd_os_start_lock(dhd_pub_t *pub);
+extern void dhd_os_start_unlock(dhd_pub_t *pub);
+extern unsigned long dhd_os_spin_lock(dhd_pub_t *pub);
+extern void dhd_os_spin_unlock(dhd_pub_t *pub, unsigned long flags);
+
+typedef struct dhd_if_event {
+	uint8 ifidx;
+	uint8 action;
+	uint8 flags;
+	uint8 bssidx;
+} dhd_if_event_t;
+
+/*
+ * Exported from dhd OS modules (dhd_linux/dhd_ndis)
+ */
+
+/* To allow osl_attach/detach calls from os-independent modules */
+osl_t *dhd_osl_attach(void *pdev, uint bustype);
+void dhd_osl_detach(osl_t *osh);
+
+/* Indication from bus module regarding presence/insertion of dongle.
+ * Return dhd_pub_t pointer, used as handle to OS module in later calls.
+ * Returned structure should have bus and prot pointers filled in.
+ * bus_hdrlen specifies required headroom for bus module header.
+ */
+extern dhd_pub_t *dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen);
+extern int dhd_net_attach(dhd_pub_t *dhdp, int idx);
+
+/* Indication from bus module regarding removal/absence of dongle */
+extern void dhd_detach(dhd_pub_t *dhdp);
+
+/* Indication from bus module to change flow-control state */
+extern void dhd_txflowcontrol(dhd_pub_t *dhdp, int ifidx, bool on);
+
+extern bool dhd_prec_enq(dhd_pub_t *dhdp, struct pktq *q, void *pkt, int prec);
+
+/* Receive frame for delivery to OS.  Callee disposes of rxp. */
+extern void dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *rxp, int numpkt);
+
+/* Return pointer to interface name */
+extern char *dhd_ifname(dhd_pub_t *dhdp, int idx);
+
+/* Request scheduling of the bus dpc */
+extern void dhd_sched_dpc(dhd_pub_t *dhdp);
+
+/* Notify tx completion */
+extern void dhd_txcomplete(dhd_pub_t *dhdp, void *txp, bool success);
+
+/* Query ioctl */
+extern int  dhdcdc_query_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len);
+
+/* OS independent layer functions */
+extern int dhd_os_proto_block(dhd_pub_t * pub);
+extern int dhd_os_proto_unblock(dhd_pub_t * pub);
+extern int dhd_os_ioctl_resp_wait(dhd_pub_t * pub, uint * condition, bool * pending);
+extern int dhd_os_ioctl_resp_wake(dhd_pub_t * pub);
+extern unsigned int dhd_os_get_ioctl_resp_timeout(void);
+extern void dhd_os_set_ioctl_resp_timeout(unsigned int timeout_msec);
+extern void * dhd_os_open_image(char * filename);
+extern int dhd_os_get_image_block(char * buf, int len, void * image);
+extern void dhd_os_close_image(void * image);
+extern void dhd_os_wd_timer(void *bus, uint wdtick);
+extern void dhd_os_sdlock(dhd_pub_t * pub);
+extern void dhd_os_sdunlock(dhd_pub_t * pub);
+extern void dhd_os_sdlock_txq(dhd_pub_t * pub);
+extern void dhd_os_sdunlock_txq(dhd_pub_t * pub);
+extern void dhd_os_sdlock_rxq(dhd_pub_t * pub);
+extern void dhd_os_sdunlock_rxq(dhd_pub_t * pub);
+extern void dhd_os_sdlock_sndup_rxq(dhd_pub_t * pub);
+extern void dhd_customer_gpio_wlan_ctrl(int onoff);
+extern int dhd_custom_get_mac_address(unsigned char *buf);
+extern void dhd_os_sdunlock_sndup_rxq(dhd_pub_t * pub);
+extern void dhd_os_sdlock_eventq(dhd_pub_t * pub);
+extern void dhd_os_sdunlock_eventq(dhd_pub_t * pub);
+#ifdef DHD_DEBUG
+extern int write_to_file(dhd_pub_t *dhd, uint8 *buf, int size);
+#endif /* DHD_DEBUG */
+#if defined(OOB_INTR_ONLY)
+extern int dhd_customer_oob_irq_map(unsigned long *irq_flags_ptr);
+#endif /* defined(OOB_INTR_ONLY) */
+extern void dhd_os_sdtxlock(dhd_pub_t * pub);
+extern void dhd_os_sdtxunlock(dhd_pub_t * pub);
+
+int setScheduler(struct task_struct *p, int policy, struct sched_param *param);
+
+typedef struct {
+	uint32 limit;		/* Expiration time (usec) */
+	uint32 increment;	/* Current expiration increment (usec) */
+	uint32 elapsed;		/* Current elapsed time (usec) */
+	uint32 tick;		/* O/S tick time (usec) */
+} dhd_timeout_t;
+
+extern void dhd_timeout_start(dhd_timeout_t *tmo, uint usec);
+extern int dhd_timeout_expired(dhd_timeout_t *tmo);
+
+extern int dhd_ifname2idx(struct dhd_info *dhd, char *name);
+extern uint8 *dhd_bssidx2bssid(dhd_pub_t *dhd, int idx);
+extern int wl_host_event(struct dhd_info *dhd, int *idx, void *pktdata,
+                         wl_event_msg_t *, void **data_ptr);
+extern void wl_event_to_host_order(wl_event_msg_t * evt);
+
+extern void dhd_common_init(void);
+
+extern int dhd_add_if(struct dhd_info *dhd, int ifidx, void *handle,
+	char *name, uint8 *mac_addr, uint32 flags, uint8 bssidx);
+extern void dhd_del_if(struct dhd_info *dhd, int ifidx);
+
+extern void dhd_vif_add(struct dhd_info *dhd, int ifidx, char * name);
+extern void dhd_vif_del(struct dhd_info *dhd, int ifidx);
+
+extern void dhd_event(struct dhd_info *dhd, char *evpkt, int evlen, int ifidx);
+extern void dhd_vif_sendup(struct dhd_info *dhd, int ifidx, uchar *cp, int len);
+
+
+/* Send packet to dongle via data channel */
+extern int dhd_sendpkt(dhd_pub_t *dhdp, int ifidx, void *pkt);
+
+/* Send event to host */
+extern void dhd_sendup_event(dhd_pub_t *dhdp, wl_event_msg_t *event, void *data);
+extern int dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag);
+extern uint dhd_bus_status(dhd_pub_t *dhdp);
+extern int  dhd_bus_start(dhd_pub_t *dhdp);
+
+extern void print_buf(void *pbuf, int len, int bytes_per_line);
+
+
+typedef enum cust_gpio_modes {
+	WLAN_RESET_ON,
+	WLAN_RESET_OFF,
+	WLAN_POWER_ON,
+	WLAN_POWER_OFF
+} cust_gpio_modes_t;
+
+extern int wl_iw_iscan_set_scan_broadcast_prep(struct net_device *dev, uint flag);
+extern int wl_iw_send_priv_event(struct net_device *dev, char *flag);
+extern int net_os_send_hang_message(struct net_device *dev);
+
+/*
+ * Insmod parameters for debug/test
+ */
+
+/* Watchdog timer interval */
+extern uint dhd_watchdog_ms;
+
+#if defined(DHD_DEBUG)
+/* Console output poll interval */
+extern uint dhd_console_ms;
+#endif /* defined(DHD_DEBUG) */
+
+/* Use interrupts */
+extern uint dhd_intr;
+
+/* Use polling */
+extern uint dhd_poll;
+
+/* ARP offload agent mode */
+extern uint dhd_arp_mode;
+
+/* ARP offload enable */
+extern uint dhd_arp_enable;
+
+/* Pkt filte enable control */
+extern uint dhd_pkt_filter_enable;
+
+/*  Pkt filter init setup */
+extern uint dhd_pkt_filter_init;
+
+/* Pkt filter mode control */
+extern uint dhd_master_mode;
+
+/* Roaming mode control */
+extern uint dhd_roam;
+
+/* Roaming mode control */
+extern uint dhd_radio_up;
+
+/* Initial idletime ticks (may be -1 for immediate idle, 0 for no idle) */
+extern int dhd_idletime;
+#define DHD_IDLETIME_TICKS 1
+
+/* SDIO Drive Strength */
+extern uint dhd_sdiod_drive_strength;
+
+/* Override to force tx queueing all the time */
+extern uint dhd_force_tx_queueing;
+
+/* Default KEEP_ALIVE Period is 55 sec to prevent AP from sending Keep Alive probe frame */
+#define KEEP_ALIVE_PERIOD 55000
+#define NULL_PKT_STR	"null_pkt"
+
+#ifdef SDTEST
+/* Echo packet generator (SDIO), pkts/s */
+extern uint dhd_pktgen;
+
+/* Echo packet len (0 => sawtooth, max 1800) */
+extern uint dhd_pktgen_len;
+#define MAX_PKTGEN_LEN 1800
+#endif
+
+
+/* optionally set by a module_param_string() */
+#define MOD_PARAM_PATHLEN	2048
+extern char fw_path[MOD_PARAM_PATHLEN];
+extern char nv_path[MOD_PARAM_PATHLEN];
+
+/* For supporting multiple interfaces */
+#define DHD_MAX_IFS	16
+#define DHD_DEL_IF	-0xe
+#define DHD_BAD_IF	-0xf
+
+
+extern void dhd_wait_for_event(dhd_pub_t *dhd, bool *lockvar);
+extern void dhd_wait_event_wakeup(dhd_pub_t*dhd);
+
+/* dhd_commn arp offload wrapers */
+extern void dhd_arp_cleanup(dhd_pub_t *dhd);
+int dhd_arp_get_arp_hostip_table(dhd_pub_t *dhd, void *buf, int buflen);
+void dhd_arp_offload_add_ip(dhd_pub_t *dhd, u32 ipaddr);
+
+#define DHD_UNICAST_FILTER_NUM         0
+#define DHD_BROADCAST_FILTER_NUM       1
+#define DHD_MULTICAST4_FILTER_NUM      2
+#define DHD_MULTICAST6_FILTER_NUM      3
+extern int net_os_set_packet_filter(struct net_device *dev, int val);
+extern int net_os_rxfilter_add_remove(struct net_device *dev, int val, int num);
+
+#endif /* _dhd_h_ */
diff --git a/drivers/net/wireless/bcm4329/dhd_bus.h b/drivers/net/wireless/bcm4329/dhd_bus.h
new file mode 100644
index 0000000..97af41b
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/dhd_bus.h
@@ -0,0 +1,93 @@
+/*
+ * Header file describing the internal (inter-module) DHD interfaces.
+ *
+ * Provides type definitions and function prototypes used to link the
+ * DHD OS, bus, and protocol modules.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: dhd_bus.h,v 1.4.6.3.2.3.6.7 2010/08/13 01:35:24 Exp $
+ */
+
+#ifndef _dhd_bus_h_
+#define _dhd_bus_h_
+
+/*
+ * Exported from dhd bus module (dhd_usb, dhd_sdio)
+ */
+
+/* Indicate (dis)interest in finding dongles. */
+extern int dhd_bus_register(void);
+extern void dhd_bus_unregister(void);
+
+/* Download firmware image and nvram image */
+extern bool dhd_bus_download_firmware(struct dhd_bus *bus, osl_t *osh,
+	char *fw_path, char *nv_path);
+
+/* Stop bus module: clear pending frames, disable data flow */
+extern void dhd_bus_stop(struct dhd_bus *bus, bool enforce_mutex);
+
+/* Initialize bus module: prepare for communication w/dongle */
+extern int dhd_bus_init(dhd_pub_t *dhdp, bool enforce_mutex);
+
+/* Send a data frame to the dongle.  Callee disposes of txp. */
+extern int dhd_bus_txdata(struct dhd_bus *bus, void *txp);
+
+/* Send/receive a control message to/from the dongle.
+ * Expects caller to enforce a single outstanding transaction.
+ */
+extern int dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen);
+extern int dhd_bus_rxctl(struct dhd_bus *bus, uchar *msg, uint msglen);
+
+/* Watchdog timer function */
+extern bool dhd_bus_watchdog(dhd_pub_t *dhd);
+
+#ifdef DHD_DEBUG
+/* Device console input function */
+extern int dhd_bus_console_in(dhd_pub_t *dhd, uchar *msg, uint msglen);
+#endif /* DHD_DEBUG */
+
+/* Deferred processing for the bus, return TRUE requests reschedule */
+extern bool dhd_bus_dpc(struct dhd_bus *bus);
+extern void dhd_bus_isr(bool * InterruptRecognized, bool * QueueMiniportHandleInterrupt, void *arg);
+
+
+/* Check for and handle local prot-specific iovar commands */
+extern int dhd_bus_iovar_op(dhd_pub_t *dhdp, const char *name,
+                            void *params, int plen, void *arg, int len, bool set);
+
+/* Add bus dump output to a buffer */
+extern void dhd_bus_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf);
+
+/* Clear any bus counters */
+extern void dhd_bus_clearcounts(dhd_pub_t *dhdp);
+
+/* return the dongle chipid */
+extern uint dhd_bus_chip(struct dhd_bus *bus);
+
+/* Set user-specified nvram parameters. */
+extern void dhd_bus_set_nvram_params(struct dhd_bus * bus, const char *nvram_params);
+
+extern void *dhd_bus_pub(struct dhd_bus *bus);
+extern void *dhd_bus_txq(struct dhd_bus *bus);
+extern uint dhd_bus_hdrlen(struct dhd_bus *bus);
+
+#endif /* _dhd_bus_h_ */
diff --git a/drivers/net/wireless/bcm4329/dhd_cdc.c b/drivers/net/wireless/bcm4329/dhd_cdc.c
new file mode 100644
index 0000000..4bec0b6
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/dhd_cdc.c
@@ -0,0 +1,535 @@
+/*
+ * DHD Protocol Module for CDC and BDC.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: dhd_cdc.c,v 1.22.4.2.4.7.2.41 2010/06/23 19:58:18 Exp $
+ *
+ * BDC is like CDC, except it includes a header for data packets to convey
+ * packet priority over the bus, and flags (e.g. to indicate checksum status
+ * for dongle offload).
+ */
+
+#include <typedefs.h>
+#include <osl.h>
+
+#include <bcmutils.h>
+#include <bcmcdc.h>
+#include <bcmendian.h>
+
+#include <dngl_stats.h>
+#include <dhd.h>
+#include <dhd_proto.h>
+#include <dhd_bus.h>
+#include <dhd_dbg.h>
+
+extern int dhd_preinit_ioctls(dhd_pub_t *dhd);
+
+/* Packet alignment for most efficient SDIO (can change based on platform) */
+#ifndef DHD_SDALIGN
+#define DHD_SDALIGN	32
+#endif
+#if !ISPOWEROF2(DHD_SDALIGN)
+#error DHD_SDALIGN is not a power of 2!
+#endif
+
+#define RETRIES 2		/* # of retries to retrieve matching ioctl response */
+#define BUS_HEADER_LEN	(16+DHD_SDALIGN)	/* Must be atleast SDPCM_RESERVE
+				 * defined in dhd_sdio.c (amount of header tha might be added)
+				 * plus any space that might be needed for alignment padding.
+				 */
+#define ROUND_UP_MARGIN	2048 	/* Biggest SDIO block size possible for
+				 * round off at the end of buffer
+				 */
+
+typedef struct dhd_prot {
+	uint16 reqid;
+	uint8 pending;
+	uint32 lastcmd;
+	uint8 bus_header[BUS_HEADER_LEN];
+	cdc_ioctl_t msg;
+	unsigned char buf[WLC_IOCTL_MAXLEN + ROUND_UP_MARGIN];
+} dhd_prot_t;
+
+static int
+dhdcdc_msg(dhd_pub_t *dhd)
+{
+	dhd_prot_t *prot = dhd->prot;
+	int len = ltoh32(prot->msg.len) + sizeof(cdc_ioctl_t);
+	int ret;
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	dhd_os_wake_lock(dhd);
+
+	/* NOTE : cdc->msg.len holds the desired length of the buffer to be
+	 *        returned. Only up to CDC_MAX_MSG_SIZE of this buffer area
+	 *	  is actually sent to the dongle
+	 */
+	if (len > CDC_MAX_MSG_SIZE)
+		len = CDC_MAX_MSG_SIZE;
+
+	/* Send request */
+	ret = dhd_bus_txctl(dhd->bus, (uchar*)&prot->msg, len);
+	dhd_os_wake_unlock(dhd);
+	return ret;
+}
+
+static int
+dhdcdc_cmplt(dhd_pub_t *dhd, uint32 id, uint32 len)
+{
+	int ret;
+	dhd_prot_t *prot = dhd->prot;
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	do {
+		ret = dhd_bus_rxctl(dhd->bus, (uchar*)&prot->msg, len+sizeof(cdc_ioctl_t));
+		if (ret < 0)
+			break;
+	} while (CDC_IOC_ID(ltoh32(prot->msg.flags)) != id);
+
+	return ret;
+}
+
+int
+dhdcdc_query_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len)
+{
+	dhd_prot_t *prot = dhd->prot;
+	cdc_ioctl_t *msg = &prot->msg;
+	void *info;
+	int ret = 0, retries = 0;
+	uint32 id, flags = 0;
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+	DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len));
+
+
+	/* Respond "bcmerror" and "bcmerrorstr" with local cache */
+	if (cmd == WLC_GET_VAR && buf)
+	{
+		if (!strcmp((char *)buf, "bcmerrorstr"))
+		{
+			strncpy((char *)buf, bcmerrorstr(dhd->dongle_error), BCME_STRLEN);
+			goto done;
+		}
+		else if (!strcmp((char *)buf, "bcmerror"))
+		{
+			*(int *)buf = dhd->dongle_error;
+			goto done;
+		}
+	}
+
+	memset(msg, 0, sizeof(cdc_ioctl_t));
+
+	msg->cmd = htol32(cmd);
+	msg->len = htol32(len);
+	msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT);
+	CDC_SET_IF_IDX(msg, ifidx);
+	msg->flags = htol32(msg->flags);
+
+	if (buf)
+		memcpy(prot->buf, buf, len);
+
+	if ((ret = dhdcdc_msg(dhd)) < 0) {
+		if (!dhd->hang_was_sent)
+			DHD_ERROR(("dhdcdc_query_ioctl: dhdcdc_msg failed w/status %d\n", ret));
+		goto done;
+	}
+
+retry:
+	/* wait for interrupt and get first fragment */
+	if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0)
+		goto done;
+
+	flags = ltoh32(msg->flags);
+	id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;
+
+	if ((id < prot->reqid) && (++retries < RETRIES))
+		goto retry;
+	if (id != prot->reqid) {
+		DHD_ERROR(("%s: %s: unexpected request id %d (expected %d)\n",
+		           dhd_ifname(dhd, ifidx), __FUNCTION__, id, prot->reqid));
+		ret = -EINVAL;
+		goto done;
+	}
+
+	/* Check info buffer */
+	info = (void*)&msg[1];
+
+	/* Copy info buffer */
+	if (buf)
+	{
+		if (ret < (int)len)
+			len = ret;
+		memcpy(buf, info, len);
+	}
+
+	/* Check the ERROR flag */
+	if (flags & CDCF_IOC_ERROR)
+	{
+		ret = ltoh32(msg->status);
+		/* Cache error from dongle */
+		dhd->dongle_error = ret;
+	}
+
+done:
+	return ret;
+}
+
+int
+dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len)
+{
+	dhd_prot_t *prot = dhd->prot;
+	cdc_ioctl_t *msg = &prot->msg;
+	int ret = 0;
+	uint32 flags, id;
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+	DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len));
+
+	if (dhd->busstate == DHD_BUS_DOWN) {
+		DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__));
+		return -EIO;
+	}
+
+	/* don't talk to the dongle if fw is about to be reloaded */
+	if (dhd->hang_was_sent) {
+		DHD_ERROR(("%s: HANG was sent up earlier. Not talking to the chip\n",
+			__FUNCTION__));
+		return -EIO;
+	}
+
+	memset(msg, 0, sizeof(cdc_ioctl_t));
+
+	msg->cmd = htol32(cmd);
+	msg->len = htol32(len);
+	msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT) | CDCF_IOC_SET;
+	CDC_SET_IF_IDX(msg, ifidx);
+	msg->flags = htol32(msg->flags);
+
+	if (buf)
+		memcpy(prot->buf, buf, len);
+
+	if ((ret = dhdcdc_msg(dhd)) < 0)
+		goto done;
+
+	if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0)
+		goto done;
+
+	flags = ltoh32(msg->flags);
+	id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;
+
+	if (id != prot->reqid) {
+		DHD_ERROR(("%s: %s: unexpected request id %d (expected %d)\n",
+		           dhd_ifname(dhd, ifidx), __FUNCTION__, id, prot->reqid));
+		ret = -EINVAL;
+		goto done;
+	}
+
+	/* Check the ERROR flag */
+	if (flags & CDCF_IOC_ERROR)
+	{
+		ret = ltoh32(msg->status);
+		/* Cache error from dongle */
+		dhd->dongle_error = ret;
+	}
+
+done:
+	return ret;
+}
+
+extern int dhd_bus_interface(struct dhd_bus *bus, uint arg, void* arg2);
+int
+dhd_prot_ioctl(dhd_pub_t *dhd, int ifidx, wl_ioctl_t * ioc, void * buf, int len)
+{
+	dhd_prot_t *prot = dhd->prot;
+	int ret = -1;
+
+	if ((dhd->busstate == DHD_BUS_DOWN) || dhd->hang_was_sent) {
+		DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__));
+		return ret;
+	}
+	dhd_os_proto_block(dhd);
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	ASSERT(len <= WLC_IOCTL_MAXLEN);
+
+	if (len > WLC_IOCTL_MAXLEN)
+		goto done;
+
+	if (prot->pending == TRUE) {
+		DHD_TRACE(("CDC packet is pending!!!! cmd=0x%x (%lu) lastcmd=0x%x (%lu)\n",
+			ioc->cmd, (unsigned long)ioc->cmd, prot->lastcmd,
+			(unsigned long)prot->lastcmd));
+		if ((ioc->cmd == WLC_SET_VAR) || (ioc->cmd == WLC_GET_VAR)) {
+			DHD_TRACE(("iovar cmd=%s\n", (char*)buf));
+		}
+		goto done;
+	}
+
+	prot->pending = TRUE;
+	prot->lastcmd = ioc->cmd;
+	if (ioc->set)
+		ret = dhdcdc_set_ioctl(dhd, ifidx, ioc->cmd, buf, len);
+	else {
+		ret = dhdcdc_query_ioctl(dhd, ifidx, ioc->cmd, buf, len);
+		if (ret > 0)
+			ioc->used = ret - sizeof(cdc_ioctl_t);
+	}
+
+	/* Too many programs assume ioctl() returns 0 on success */
+	if (ret >= 0)
+		ret = 0;
+	else {
+		cdc_ioctl_t *msg = &prot->msg;
+		ioc->needed = ltoh32(msg->len); /* len == needed when set/query fails from dongle */
+	}
+
+	/* Intercept the wme_dp ioctl here */
+	if ((!ret) && (ioc->cmd == WLC_SET_VAR) && (!strcmp(buf, "wme_dp"))) {
+		int slen, val = 0;
+
+		slen = strlen("wme_dp") + 1;
+		if (len >= (int)(slen + sizeof(int)))
+			bcopy(((char *)buf + slen), &val, sizeof(int));
+		dhd->wme_dp = (uint8) ltoh32(val);
+	}
+
+	prot->pending = FALSE;
+
+done:
+	dhd_os_proto_unblock(dhd);
+
+	return ret;
+}
+
+int
+dhd_prot_iovar_op(dhd_pub_t *dhdp, const char *name,
+                  void *params, int plen, void *arg, int len, bool set)
+{
+	return BCME_UNSUPPORTED;
+}
+
+void
+dhd_prot_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf)
+{
+	bcm_bprintf(strbuf, "Protocol CDC: reqid %d\n", dhdp->prot->reqid);
+}
+
+
+void
+dhd_prot_hdrpush(dhd_pub_t *dhd, int ifidx, void *pktbuf)
+{
+#ifdef BDC
+	struct bdc_header *h;
+#endif /* BDC */
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+#ifdef BDC
+	/* Push BDC header used to convey priority for buses that don't */
+
+
+	PKTPUSH(dhd->osh, pktbuf, BDC_HEADER_LEN);
+
+	h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
+
+	h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT);
+	if (PKTSUMNEEDED(pktbuf))
+		h->flags |= BDC_FLAG_SUM_NEEDED;
+
+
+	h->priority = (PKTPRIO(pktbuf) & BDC_PRIORITY_MASK);
+	h->flags2 = 0;
+	h->rssi = 0;
+#endif /* BDC */
+	BDC_SET_IF_IDX(h, ifidx);
+}
+
+
+bool
+dhd_proto_fcinfo(dhd_pub_t *dhd, void *pktbuf, uint8 *fcbits)
+{
+#ifdef BDC
+	struct bdc_header *h;
+
+	if (PKTLEN(dhd->osh, pktbuf) < BDC_HEADER_LEN) {
+		DHD_ERROR(("%s: rx data too short (%d < %d)\n",
+			__FUNCTION__, PKTLEN(dhd->osh, pktbuf), BDC_HEADER_LEN));
+		return BCME_ERROR;
+	}
+
+	h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
+
+	*fcbits = h->priority >> BDC_PRIORITY_FC_SHIFT;
+	if ((h->flags2 & BDC_FLAG2_FC_FLAG) == BDC_FLAG2_FC_FLAG)
+		return TRUE;
+#endif
+	return FALSE;
+}
+
+
+int
+dhd_prot_hdrpull(dhd_pub_t *dhd, int *ifidx, void *pktbuf)
+{
+#ifdef BDC
+	struct bdc_header *h;
+#endif
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+#ifdef BDC
+	/* Pop BDC header used to convey priority for buses that don't */
+
+	if (PKTLEN(dhd->osh, pktbuf) < BDC_HEADER_LEN) {
+		DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__,
+		           PKTLEN(dhd->osh, pktbuf), BDC_HEADER_LEN));
+		return BCME_ERROR;
+	}
+
+	h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
+
+	if ((*ifidx = BDC_GET_IF_IDX(h)) >= DHD_MAX_IFS) {
+		DHD_ERROR(("%s: rx data ifnum out of range (%d)\n",
+		           __FUNCTION__, *ifidx));
+		return BCME_ERROR;
+	}
+
+	if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) != BDC_PROTO_VER) {
+		DHD_ERROR(("%s: non-BDC packet received, flags 0x%x\n",
+		           dhd_ifname(dhd, *ifidx), h->flags));
+		return BCME_ERROR;
+	}
+
+	if (h->flags & BDC_FLAG_SUM_GOOD) {
+		DHD_INFO(("%s: BDC packet received with good rx-csum, flags 0x%x\n",
+		          dhd_ifname(dhd, *ifidx), h->flags));
+		PKTSETSUMGOOD(pktbuf, TRUE);
+	}
+
+	PKTSETPRIO(pktbuf, (h->priority & BDC_PRIORITY_MASK));
+
+	PKTPULL(dhd->osh, pktbuf, BDC_HEADER_LEN);
+#endif /* BDC */
+
+	return 0;
+}
+
+int
+dhd_prot_attach(dhd_pub_t *dhd)
+{
+	dhd_prot_t *cdc;
+
+#ifndef DHD_USE_STATIC_BUF
+	if (!(cdc = (dhd_prot_t *)MALLOC(dhd->osh, sizeof(dhd_prot_t)))) {
+		DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
+		goto fail;
+	}
+#else
+	if (!(cdc = (dhd_prot_t *)dhd_os_prealloc(DHD_PREALLOC_PROT, sizeof(dhd_prot_t)))) {
+		DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
+		goto fail;
+	}
+#endif /* DHD_USE_STATIC_BUF */
+	memset(cdc, 0, sizeof(dhd_prot_t));
+
+	/* ensure that the msg buf directly follows the cdc msg struct */
+	if ((uintptr)(&cdc->msg + 1) != (uintptr)cdc->buf) {
+		DHD_ERROR(("dhd_prot_t is not correctly defined\n"));
+		goto fail;
+	}
+
+	dhd->prot = cdc;
+#ifdef BDC
+	dhd->hdrlen += BDC_HEADER_LEN;
+#endif
+	dhd->maxctl = WLC_IOCTL_MAXLEN + sizeof(cdc_ioctl_t) + ROUND_UP_MARGIN;
+	return 0;
+
+fail:
+#ifndef DHD_USE_STATIC_BUF
+	if (cdc != NULL)
+		MFREE(dhd->osh, cdc, sizeof(dhd_prot_t));
+#endif
+	return BCME_NOMEM;
+}
+
+/* ~NOTE~ What if another thread is waiting on the semaphore?  Holding it? */
+void
+dhd_prot_detach(dhd_pub_t *dhd)
+{
+#ifndef DHD_USE_STATIC_BUF
+	MFREE(dhd->osh, dhd->prot, sizeof(dhd_prot_t));
+#endif
+	dhd->prot = NULL;
+}
+
+void
+dhd_prot_dstats(dhd_pub_t *dhd)
+{
+	/* No stats from dongle added yet, copy bus stats */
+	dhd->dstats.tx_packets = dhd->tx_packets;
+	dhd->dstats.tx_errors = dhd->tx_errors;
+	dhd->dstats.rx_packets = dhd->rx_packets;
+	dhd->dstats.rx_errors = dhd->rx_errors;
+	dhd->dstats.rx_dropped = dhd->rx_dropped;
+	dhd->dstats.multicast = dhd->rx_multicast;
+	return;
+}
+
+int
+dhd_prot_init(dhd_pub_t *dhd)
+{
+	int ret = 0;
+	char buf[128];
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	dhd_os_proto_block(dhd);
+
+	/* Get the device MAC address */
+	strcpy(buf, "cur_etheraddr");
+	ret = dhdcdc_query_ioctl(dhd, 0, WLC_GET_VAR, buf, sizeof(buf));
+	if (ret < 0) {
+		dhd_os_proto_unblock(dhd);
+		return ret;
+	}
+	memcpy(dhd->mac.octet, buf, ETHER_ADDR_LEN);
+
+	dhd_os_proto_unblock(dhd);
+
+#ifdef EMBEDDED_PLATFORM
+	ret = dhd_preinit_ioctls(dhd);
+#endif /* EMBEDDED_PLATFORM */
+
+	/* Always assumes wl for now */
+	dhd->iswl = TRUE;
+
+	return ret;
+}
+
+void
+dhd_prot_stop(dhd_pub_t *dhd)
+{
+	/* Nothing to do for CDC */
+}
diff --git a/drivers/net/wireless/bcm4329/dhd_common.c b/drivers/net/wireless/bcm4329/dhd_common.c
new file mode 100644
index 0000000..8b89e39
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/dhd_common.c
@@ -0,0 +1,2461 @@
+/*
+ * Broadcom Dongle Host Driver (DHD), common DHD core.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: dhd_common.c,v 1.5.6.8.2.6.6.69.4.25 2011-02-11 21:16:02 Exp $
+ */
+#include <typedefs.h>
+#include <osl.h>
+
+#include <epivers.h>
+#include <bcmutils.h>
+
+#include <bcmendian.h>
+#include <dngl_stats.h>
+#include <dhd.h>
+#include <dhd_bus.h>
+#include <dhd_proto.h>
+#include <dhd_dbg.h>
+#include <msgtrace.h>
+
+#include <wlioctl.h>
+
+#ifdef SET_RANDOM_MAC_SOFTAP
+#include <linux/random.h>
+#include <linux/jiffies.h>
+#endif
+
+#ifdef GET_CUSTOM_MAC_ENABLE
+int wifi_get_mac_addr(unsigned char *buf);
+#endif /* GET_CUSTOM_MAC_ENABLE */
+
+int dhd_msg_level;
+
+#include <wl_iw.h>
+
+char fw_path[MOD_PARAM_PATHLEN];
+char nv_path[MOD_PARAM_PATHLEN];
+
+/* Last connection success/failure status */
+uint32 dhd_conn_event;
+uint32 dhd_conn_status;
+uint32 dhd_conn_reason;
+
+#define htod32(i) i
+#define htod16(i) i
+#define dtoh32(i) i
+#define dtoh16(i) i
+
+extern int dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len);
+extern void dhd_ind_scan_confirm(void *h, bool status);
+extern int dhd_wl_ioctl(dhd_pub_t *dhd, uint cmd, char *buf, uint buflen);
+void dhd_iscan_lock(void);
+void dhd_iscan_unlock(void);
+
+#if defined(SOFTAP)
+extern bool ap_fw_loaded;
+#endif
+#if defined(KEEP_ALIVE)
+int dhd_keep_alive_onoff(dhd_pub_t *dhd, int ka_on);
+#endif /* KEEP_ALIVE */
+
+/* Packet alignment for most efficient SDIO (can change based on platform) */
+#ifndef DHD_SDALIGN
+#define DHD_SDALIGN	32
+#endif
+#if !ISPOWEROF2(DHD_SDALIGN)
+#error DHD_SDALIGN is not a power of 2!
+#endif
+
+#ifdef DHD_DEBUG
+const char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR "\nCompiled on "
+	__DATE__ " at " __TIME__;
+#else
+const char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR;
+#endif
+
+void dhd_set_timer(void *bus, uint wdtick);
+
+/* IOVar table */
+enum {
+	IOV_VERSION = 1,
+	IOV_MSGLEVEL,
+	IOV_BCMERRORSTR,
+	IOV_BCMERROR,
+	IOV_WDTICK,
+	IOV_DUMP,
+#ifdef DHD_DEBUG
+	IOV_CONS,
+	IOV_DCONSOLE_POLL,
+#endif
+	IOV_CLEARCOUNTS,
+	IOV_LOGDUMP,
+	IOV_LOGCAL,
+	IOV_LOGSTAMP,
+	IOV_GPIOOB,
+	IOV_IOCTLTIMEOUT,
+	IOV_LAST
+};
+
+const bcm_iovar_t dhd_iovars[] = {
+	{"version", 	IOV_VERSION,	0,	IOVT_BUFFER,	sizeof(dhd_version) },
+#ifdef DHD_DEBUG
+	{"msglevel",	IOV_MSGLEVEL,	0,	IOVT_UINT32,	0 },
+#endif /* DHD_DEBUG */
+	{"bcmerrorstr", IOV_BCMERRORSTR, 0, IOVT_BUFFER,	BCME_STRLEN },
+	{"bcmerror",	IOV_BCMERROR,	0,	IOVT_INT8,	0 },
+	{"wdtick",	IOV_WDTICK, 0,	IOVT_UINT32,	0 },
+	{"dump",	IOV_DUMP,	0,	IOVT_BUFFER,	DHD_IOCTL_MAXLEN },
+#ifdef DHD_DEBUG
+	{"dconpoll",	IOV_DCONSOLE_POLL, 0,	IOVT_UINT32,	0 },
+	{"cons",	IOV_CONS,	0,	IOVT_BUFFER,	0 },
+#endif
+	{"clearcounts", IOV_CLEARCOUNTS, 0, IOVT_VOID,	0 },
+	{"gpioob",	IOV_GPIOOB,	0,	IOVT_UINT32,	0 },
+	{"ioctl_timeout",	IOV_IOCTLTIMEOUT,	0,	IOVT_UINT32,	0 },
+	{NULL, 0, 0, 0, 0 }
+};
+
+void
+dhd_common_init(void)
+{
+	/* Init global variables at run-time, not as part of the declaration.
+	 * This is required to support init/de-init of the driver. Initialization
+	 * of globals as part of the declaration results in non-deterministic
+	 * behaviour since the value of the globals may be different on the
+	 * first time that the driver is initialized vs subsequent initializations.
+	 */
+	dhd_msg_level = DHD_ERROR_VAL;
+#ifdef CONFIG_BCM4329_FW_PATH
+	strncpy(fw_path, CONFIG_BCM4329_FW_PATH, MOD_PARAM_PATHLEN-1);
+#else
+	fw_path[0] = '\0';
+#endif
+#ifdef CONFIG_BCM4329_NVRAM_PATH
+	strncpy(nv_path, CONFIG_BCM4329_NVRAM_PATH, MOD_PARAM_PATHLEN-1);
+#else
+	nv_path[0] = '\0';
+#endif
+}
+
+static int
+dhd_dump(dhd_pub_t *dhdp, char *buf, int buflen)
+{
+	char eabuf[ETHER_ADDR_STR_LEN];
+
+	struct bcmstrbuf b;
+	struct bcmstrbuf *strbuf = &b;
+
+	bcm_binit(strbuf, buf, buflen);
+
+	/* Base DHD info */
+	bcm_bprintf(strbuf, "%s\n", dhd_version);
+	bcm_bprintf(strbuf, "\n");
+	bcm_bprintf(strbuf, "pub.up %d pub.txoff %d pub.busstate %d\n",
+	            dhdp->up, dhdp->txoff, dhdp->busstate);
+	bcm_bprintf(strbuf, "pub.hdrlen %d pub.maxctl %d pub.rxsz %d\n",
+	            dhdp->hdrlen, dhdp->maxctl, dhdp->rxsz);
+	bcm_bprintf(strbuf, "pub.iswl %d pub.drv_version %ld pub.mac %s\n",
+	            dhdp->iswl, dhdp->drv_version, bcm_ether_ntoa(&dhdp->mac, eabuf));
+	bcm_bprintf(strbuf, "pub.bcmerror %d tickcnt %d\n", dhdp->bcmerror, dhdp->tickcnt);
+
+	bcm_bprintf(strbuf, "dongle stats:\n");
+	bcm_bprintf(strbuf, "tx_packets %ld tx_bytes %ld tx_errors %ld tx_dropped %ld\n",
+	            dhdp->dstats.tx_packets, dhdp->dstats.tx_bytes,
+	            dhdp->dstats.tx_errors, dhdp->dstats.tx_dropped);
+	bcm_bprintf(strbuf, "rx_packets %ld rx_bytes %ld rx_errors %ld rx_dropped %ld\n",
+	            dhdp->dstats.rx_packets, dhdp->dstats.rx_bytes,
+	            dhdp->dstats.rx_errors, dhdp->dstats.rx_dropped);
+	bcm_bprintf(strbuf, "multicast %ld\n", dhdp->dstats.multicast);
+
+	bcm_bprintf(strbuf, "bus stats:\n");
+	bcm_bprintf(strbuf, "tx_packets %ld tx_multicast %ld tx_errors %ld\n",
+	            dhdp->tx_packets, dhdp->tx_multicast, dhdp->tx_errors);
+	bcm_bprintf(strbuf, "tx_ctlpkts %ld tx_ctlerrs %ld\n",
+	            dhdp->tx_ctlpkts, dhdp->tx_ctlerrs);
+	bcm_bprintf(strbuf, "rx_packets %ld rx_multicast %ld rx_errors %ld \n",
+	            dhdp->rx_packets, dhdp->rx_multicast, dhdp->rx_errors);
+	bcm_bprintf(strbuf, "rx_ctlpkts %ld rx_ctlerrs %ld rx_dropped %ld rx_flushed %ld\n",
+	            dhdp->rx_ctlpkts, dhdp->rx_ctlerrs, dhdp->rx_dropped, dhdp->rx_flushed);
+	bcm_bprintf(strbuf, "rx_readahead_cnt %ld tx_realloc %ld fc_packets %ld\n",
+	            dhdp->rx_readahead_cnt, dhdp->tx_realloc, dhdp->fc_packets);
+	bcm_bprintf(strbuf, "wd_dpc_sched %ld\n", dhdp->wd_dpc_sched);
+	bcm_bprintf(strbuf, "\n");
+
+	/* Add any prot info */
+	dhd_prot_dump(dhdp, strbuf);
+	bcm_bprintf(strbuf, "\n");
+
+	/* Add any bus info */
+	dhd_bus_dump(dhdp, strbuf);
+
+	return (!strbuf->size ? BCME_BUFTOOSHORT : 0);
+}
+
+static int
+dhd_doiovar(dhd_pub_t *dhd_pub, const bcm_iovar_t *vi, uint32 actionid, const char *name,
+            void *params, int plen, void *arg, int len, int val_size)
+{
+	int bcmerror = 0;
+	int32 int_val = 0;
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, IOV_ISSET(actionid))) != 0)
+		goto exit;
+
+	if (plen >= (int)sizeof(int_val))
+		bcopy(params, &int_val, sizeof(int_val));
+
+	switch (actionid) {
+	case IOV_GVAL(IOV_VERSION):
+		/* Need to have checked buffer length */
+		strncpy((char*)arg, dhd_version, len);
+		break;
+
+	case IOV_GVAL(IOV_MSGLEVEL):
+		int_val = (int32)dhd_msg_level;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_MSGLEVEL):
+		dhd_msg_level = int_val;
+		break;
+
+	case IOV_GVAL(IOV_BCMERRORSTR):
+		strncpy((char *)arg, bcmerrorstr(dhd_pub->bcmerror), BCME_STRLEN);
+		((char *)arg)[BCME_STRLEN - 1] = 0x00;
+		break;
+
+	case IOV_GVAL(IOV_BCMERROR):
+		int_val = (int32)dhd_pub->bcmerror;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_GVAL(IOV_WDTICK):
+		int_val = (int32)dhd_watchdog_ms;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_WDTICK):
+		if (!dhd_pub->up) {
+			bcmerror = BCME_NOTUP;
+			break;
+		}
+		dhd_os_wd_timer(dhd_pub, (uint)int_val);
+		break;
+
+	case IOV_GVAL(IOV_DUMP):
+		bcmerror = dhd_dump(dhd_pub, arg, len);
+		break;
+
+#ifdef DHD_DEBUG
+	case IOV_GVAL(IOV_DCONSOLE_POLL):
+		int_val = (int32)dhd_console_ms;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_DCONSOLE_POLL):
+		dhd_console_ms = (uint)int_val;
+		break;
+
+	case IOV_SVAL(IOV_CONS):
+		if (len > 0)
+			bcmerror = dhd_bus_console_in(dhd_pub, arg, len - 1);
+		break;
+#endif
+
+	case IOV_SVAL(IOV_CLEARCOUNTS):
+		dhd_pub->tx_packets = dhd_pub->rx_packets = 0;
+		dhd_pub->tx_errors = dhd_pub->rx_errors = 0;
+		dhd_pub->tx_ctlpkts = dhd_pub->rx_ctlpkts = 0;
+		dhd_pub->tx_ctlerrs = dhd_pub->rx_ctlerrs = 0;
+		dhd_pub->rx_dropped = 0;
+		dhd_pub->rx_readahead_cnt = 0;
+		dhd_pub->tx_realloc = 0;
+		dhd_pub->wd_dpc_sched = 0;
+		memset(&dhd_pub->dstats, 0, sizeof(dhd_pub->dstats));
+		dhd_bus_clearcounts(dhd_pub);
+		break;
+
+
+	case IOV_GVAL(IOV_IOCTLTIMEOUT): {
+		int_val = (int32)dhd_os_get_ioctl_resp_timeout();
+		bcopy(&int_val, arg, sizeof(int_val));
+		break;
+	}
+
+	case IOV_SVAL(IOV_IOCTLTIMEOUT): {
+		if (int_val <= 0)
+			bcmerror = BCME_BADARG;
+		else
+			dhd_os_set_ioctl_resp_timeout((unsigned int)int_val);
+		break;
+	}
+
+
+	default:
+		bcmerror = BCME_UNSUPPORTED;
+		break;
+	}
+
+exit:
+	return bcmerror;
+}
+
+/* Store the status of a connection attempt for later retrieval by an iovar */
+void
+dhd_store_conn_status(uint32 event, uint32 status, uint32 reason)
+{
+	/* Do not overwrite a WLC_E_PRUNE with a WLC_E_SET_SSID
+	 * because an encryption/rsn mismatch results in both events, and
+	 * the important information is in the WLC_E_PRUNE.
+	 */
+	if (!(event == WLC_E_SET_SSID && status == WLC_E_STATUS_FAIL &&
+	      dhd_conn_event == WLC_E_PRUNE)) {
+		dhd_conn_event = event;
+		dhd_conn_status = status;
+		dhd_conn_reason = reason;
+	}
+}
+
+bool
+dhd_prec_enq(dhd_pub_t *dhdp, struct pktq *q, void *pkt, int prec)
+{
+	void *p;
+	int eprec = -1;		/* precedence to evict from */
+	bool discard_oldest;
+
+	/* Fast case, precedence queue is not full and we are also not
+	 * exceeding total queue length
+	 */
+	if (!pktq_pfull(q, prec) && !pktq_full(q)) {
+		pktq_penq(q, prec, pkt);
+		return TRUE;
+	}
+
+	/* Determine precedence from which to evict packet, if any */
+	if (pktq_pfull(q, prec))
+		eprec = prec;
+	else if (pktq_full(q)) {
+		p = pktq_peek_tail(q, &eprec);
+		ASSERT(p);
+		if (eprec > prec)
+			return FALSE;
+	}
+
+	/* Evict if needed */
+	if (eprec >= 0) {
+		/* Detect queueing to unconfigured precedence */
+		ASSERT(!pktq_pempty(q, eprec));
+		discard_oldest = AC_BITMAP_TST(dhdp->wme_dp, eprec);
+		if (eprec == prec && !discard_oldest)
+			return FALSE;		/* refuse newer (incoming) packet */
+		/* Evict packet according to discard policy */
+		p = discard_oldest ? pktq_pdeq(q, eprec) : pktq_pdeq_tail(q, eprec);
+		if (p == NULL) {
+			DHD_ERROR(("%s: pktq_penq() failed, oldest %d.",
+				__FUNCTION__, discard_oldest));
+			ASSERT(p);
+		}
+
+		PKTFREE(dhdp->osh, p, TRUE);
+	}
+
+	/* Enqueue */
+	p = pktq_penq(q, prec, pkt);
+	if (p == NULL) {
+		DHD_ERROR(("%s: pktq_penq() failed.", __FUNCTION__));
+		ASSERT(p);
+	}
+
+	return TRUE;
+}
+
+static int
+dhd_iovar_op(dhd_pub_t *dhd_pub, const char *name,
+             void *params, int plen, void *arg, int len, bool set)
+{
+	int bcmerror = 0;
+	int val_size;
+	const bcm_iovar_t *vi = NULL;
+	uint32 actionid;
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	ASSERT(name);
+	ASSERT(len >= 0);
+
+	/* Get MUST have return space */
+	ASSERT(set || (arg && len));
+
+	/* Set does NOT take qualifiers */
+	ASSERT(!set || (!params && !plen));
+
+	if ((vi = bcm_iovar_lookup(dhd_iovars, name)) == NULL) {
+		bcmerror = BCME_UNSUPPORTED;
+		goto exit;
+	}
+
+	DHD_CTL(("%s: %s %s, len %d plen %d\n", __FUNCTION__,
+	         name, (set ? "set" : "get"), len, plen));
+
+	/* set up 'params' pointer in case this is a set command so that
+	 * the convenience int and bool code can be common to set and get
+	 */
+	if (params == NULL) {
+		params = arg;
+		plen = len;
+	}
+
+	if (vi->type == IOVT_VOID)
+		val_size = 0;
+	else if (vi->type == IOVT_BUFFER)
+		val_size = len;
+	else
+		/* all other types are integer sized */
+		val_size = sizeof(int);
+
+	actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid);
+	bcmerror = dhd_doiovar(dhd_pub, vi, actionid, name, params, plen, arg, len, val_size);
+
+exit:
+	return bcmerror;
+}
+
+int
+dhd_ioctl(dhd_pub_t *dhd_pub, dhd_ioctl_t *ioc, void *buf, uint buflen)
+{
+	int bcmerror = 0;
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	if (!buf) return BCME_BADARG;
+
+	switch (ioc->cmd) {
+	case DHD_GET_MAGIC:
+		if (buflen < sizeof(int))
+			bcmerror = BCME_BUFTOOSHORT;
+		else
+			*(int*)buf = DHD_IOCTL_MAGIC;
+		break;
+
+	case DHD_GET_VERSION:
+		if (buflen < sizeof(int))
+			bcmerror = -BCME_BUFTOOSHORT;
+		else
+			*(int*)buf = DHD_IOCTL_VERSION;
+		break;
+
+	case DHD_GET_VAR:
+	case DHD_SET_VAR: {
+		char *arg;
+		uint arglen;
+
+		/* scan past the name to any arguments */
+		for (arg = buf, arglen = buflen; *arg && arglen; arg++, arglen--);
+
+		if (*arg) {
+			bcmerror = BCME_BUFTOOSHORT;
+			break;
+		}
+
+		/* account for the NUL terminator */
+		arg++, arglen--;
+
+		/* call with the appropriate arguments */
+		if (ioc->cmd == DHD_GET_VAR)
+			bcmerror = dhd_iovar_op(dhd_pub, buf, arg, arglen,
+			buf, buflen, IOV_GET);
+		else
+			bcmerror = dhd_iovar_op(dhd_pub, buf, NULL, 0, arg, arglen, IOV_SET);
+		if (bcmerror != BCME_UNSUPPORTED)
+			break;
+
+		/* not in generic table, try protocol module */
+		if (ioc->cmd == DHD_GET_VAR)
+			bcmerror = dhd_prot_iovar_op(dhd_pub, buf, arg,
+			                             arglen, buf, buflen, IOV_GET);
+		else
+			bcmerror = dhd_prot_iovar_op(dhd_pub, buf,
+			                             NULL, 0, arg, arglen, IOV_SET);
+		if (bcmerror != BCME_UNSUPPORTED)
+			break;
+
+		/* if still not found, try bus module */
+		if (ioc->cmd == DHD_GET_VAR)
+			bcmerror = dhd_bus_iovar_op(dhd_pub, buf,
+			                            arg, arglen, buf, buflen, IOV_GET);
+		else
+			bcmerror = dhd_bus_iovar_op(dhd_pub, buf,
+			                            NULL, 0, arg, arglen, IOV_SET);
+
+		break;
+	}
+
+	default:
+		bcmerror = BCME_UNSUPPORTED;
+	}
+
+	return bcmerror;
+}
+
+
+#ifdef SHOW_EVENTS
+static void
+wl_show_host_event(wl_event_msg_t *event, void *event_data)
+{
+	uint i, status, reason;
+	bool group = FALSE, flush_txq = FALSE, link = FALSE;
+	char *auth_str, *event_name;
+	uchar *buf;
+	char err_msg[256], eabuf[ETHER_ADDR_STR_LEN];
+	static struct {uint event; char *event_name;} event_names[] = {
+		{WLC_E_SET_SSID, "SET_SSID"},
+		{WLC_E_JOIN, "JOIN"},
+		{WLC_E_START, "START"},
+		{WLC_E_AUTH, "AUTH"},
+		{WLC_E_AUTH_IND, "AUTH_IND"},
+		{WLC_E_DEAUTH, "DEAUTH"},
+		{WLC_E_DEAUTH_IND, "DEAUTH_IND"},
+		{WLC_E_ASSOC, "ASSOC"},
+		{WLC_E_ASSOC_IND, "ASSOC_IND"},
+		{WLC_E_REASSOC, "REASSOC"},
+		{WLC_E_REASSOC_IND, "REASSOC_IND"},
+		{WLC_E_DISASSOC, "DISASSOC"},
+		{WLC_E_DISASSOC_IND, "DISASSOC_IND"},
+		{WLC_E_QUIET_START, "START_QUIET"},
+		{WLC_E_QUIET_END, "END_QUIET"},
+		{WLC_E_BEACON_RX, "BEACON_RX"},
+		{WLC_E_LINK, "LINK"},
+		{WLC_E_MIC_ERROR, "MIC_ERROR"},
+		{WLC_E_NDIS_LINK, "NDIS_LINK"},
+		{WLC_E_ROAM, "ROAM"},
+		{WLC_E_TXFAIL, "TXFAIL"},
+		{WLC_E_PMKID_CACHE, "PMKID_CACHE"},
+		{WLC_E_RETROGRADE_TSF, "RETROGRADE_TSF"},
+		{WLC_E_PRUNE, "PRUNE"},
+		{WLC_E_AUTOAUTH, "AUTOAUTH"},
+		{WLC_E_EAPOL_MSG, "EAPOL_MSG"},
+		{WLC_E_SCAN_COMPLETE, "SCAN_COMPLETE"},
+		{WLC_E_ADDTS_IND, "ADDTS_IND"},
+		{WLC_E_DELTS_IND, "DELTS_IND"},
+		{WLC_E_BCNSENT_IND, "BCNSENT_IND"},
+		{WLC_E_BCNRX_MSG, "BCNRX_MSG"},
+		{WLC_E_BCNLOST_MSG, "BCNLOST_MSG"},
+		{WLC_E_ROAM_PREP, "ROAM_PREP"},
+		{WLC_E_PFN_NET_FOUND, "PNO_NET_FOUND"},
+		{WLC_E_PFN_NET_LOST, "PNO_NET_LOST"},
+		{WLC_E_RESET_COMPLETE, "RESET_COMPLETE"},
+		{WLC_E_JOIN_START, "JOIN_START"},
+		{WLC_E_ROAM_START, "ROAM_START"},
+		{WLC_E_ASSOC_START, "ASSOC_START"},
+		{WLC_E_IBSS_ASSOC, "IBSS_ASSOC"},
+		{WLC_E_RADIO, "RADIO"},
+		{WLC_E_PSM_WATCHDOG, "PSM_WATCHDOG"},
+		{WLC_E_PROBREQ_MSG, "PROBREQ_MSG"},
+		{WLC_E_SCAN_CONFIRM_IND, "SCAN_CONFIRM_IND"},
+		{WLC_E_PSK_SUP, "PSK_SUP"},
+		{WLC_E_COUNTRY_CODE_CHANGED, "COUNTRY_CODE_CHANGED"},
+		{WLC_E_EXCEEDED_MEDIUM_TIME, "EXCEEDED_MEDIUM_TIME"},
+		{WLC_E_ICV_ERROR, "ICV_ERROR"},
+		{WLC_E_UNICAST_DECODE_ERROR, "UNICAST_DECODE_ERROR"},
+		{WLC_E_MULTICAST_DECODE_ERROR, "MULTICAST_DECODE_ERROR"},
+		{WLC_E_TRACE, "TRACE"},
+		{WLC_E_ACTION_FRAME, "ACTION FRAME"},
+		{WLC_E_ACTION_FRAME_COMPLETE, "ACTION FRAME TX COMPLETE"},
+		{WLC_E_IF, "IF"},
+		{WLC_E_RSSI, "RSSI"},
+		{WLC_E_PFN_SCAN_COMPLETE, "SCAN_COMPLETE"}
+	};
+	uint event_type, flags, auth_type, datalen;
+	event_type = ntoh32(event->event_type);
+	flags = ntoh16(event->flags);
+	status = ntoh32(event->status);
+	reason = ntoh32(event->reason);
+	auth_type = ntoh32(event->auth_type);
+	datalen = ntoh32(event->datalen);
+	/* debug dump of event messages */
+	sprintf(eabuf, "%02x:%02x:%02x:%02x:%02x:%02x",
+	        (uchar)event->addr.octet[0]&0xff,
+	        (uchar)event->addr.octet[1]&0xff,
+	        (uchar)event->addr.octet[2]&0xff,
+	        (uchar)event->addr.octet[3]&0xff,
+	        (uchar)event->addr.octet[4]&0xff,
+	        (uchar)event->addr.octet[5]&0xff);
+
+	event_name = "UNKNOWN";
+	for (i = 0; i < ARRAYSIZE(event_names); i++) {
+		if (event_names[i].event == event_type)
+			event_name = event_names[i].event_name;
+	}
+
+	DHD_EVENT(("EVENT: %s, event ID = %d\n", event_name, event_type));
+
+	if (flags & WLC_EVENT_MSG_LINK)
+		link = TRUE;
+	if (flags & WLC_EVENT_MSG_GROUP)
+		group = TRUE;
+	if (flags & WLC_EVENT_MSG_FLUSHTXQ)
+		flush_txq = TRUE;
+
+	switch (event_type) {
+	case WLC_E_START:
+	case WLC_E_DEAUTH:
+	case WLC_E_DISASSOC:
+		DHD_EVENT(("MACEVENT: %s, MAC %s\n", event_name, eabuf));
+		break;
+
+	case WLC_E_ASSOC_IND:
+	case WLC_E_REASSOC_IND:
+		DHD_EVENT(("MACEVENT: %s, MAC %s\n", event_name, eabuf));
+		break;
+
+	case WLC_E_ASSOC:
+	case WLC_E_REASSOC:
+		if (status == WLC_E_STATUS_SUCCESS) {
+			DHD_EVENT(("MACEVENT: %s, MAC %s, SUCCESS\n", event_name, eabuf));
+		} else if (status == WLC_E_STATUS_TIMEOUT) {
+			DHD_EVENT(("MACEVENT: %s, MAC %s, TIMEOUT\n", event_name, eabuf));
+		} else if (status == WLC_E_STATUS_FAIL) {
+			DHD_EVENT(("MACEVENT: %s, MAC %s, FAILURE, reason %d\n",
+			       event_name, eabuf, (int)reason));
+		} else {
+			DHD_EVENT(("MACEVENT: %s, MAC %s, unexpected status %d\n",
+			       event_name, eabuf, (int)status));
+		}
+		break;
+
+	case WLC_E_DEAUTH_IND:
+	case WLC_E_DISASSOC_IND:
+		DHD_EVENT(("MACEVENT: %s, MAC %s, reason %d\n", event_name, eabuf, (int)reason));
+		break;
+
+	case WLC_E_AUTH:
+	case WLC_E_AUTH_IND:
+		if (auth_type == DOT11_OPEN_SYSTEM)
+			auth_str = "Open System";
+		else if (auth_type == DOT11_SHARED_KEY)
+			auth_str = "Shared Key";
+		else {
+			sprintf(err_msg, "AUTH unknown: %d", (int)auth_type);
+			auth_str = err_msg;
+		}
+		if (event_type == WLC_E_AUTH_IND) {
+			DHD_EVENT(("MACEVENT: %s, MAC %s, %s\n", event_name, eabuf, auth_str));
+		} else if (status == WLC_E_STATUS_SUCCESS) {
+			DHD_EVENT(("MACEVENT: %s, MAC %s, %s, SUCCESS\n",
+				event_name, eabuf, auth_str));
+		} else if (status == WLC_E_STATUS_TIMEOUT) {
+			DHD_EVENT(("MACEVENT: %s, MAC %s, %s, TIMEOUT\n",
+				event_name, eabuf, auth_str));
+		} else if (status == WLC_E_STATUS_FAIL) {
+			DHD_EVENT(("MACEVENT: %s, MAC %s, %s, FAILURE, reason %d\n",
+			       event_name, eabuf, auth_str, (int)reason));
+		}
+
+		break;
+
+	case WLC_E_JOIN:
+	case WLC_E_ROAM:
+	case WLC_E_SET_SSID:
+		if (status == WLC_E_STATUS_SUCCESS) {
+			DHD_EVENT(("MACEVENT: %s, MAC %s\n", event_name, eabuf));
+		} else if (status == WLC_E_STATUS_FAIL) {
+			DHD_EVENT(("MACEVENT: %s, failed\n", event_name));
+		} else if (status == WLC_E_STATUS_NO_NETWORKS) {
+			DHD_EVENT(("MACEVENT: %s, no networks found\n", event_name));
+		} else {
+			DHD_EVENT(("MACEVENT: %s, unexpected status %d\n",
+				event_name, (int)status));
+		}
+		break;
+
+	case WLC_E_BEACON_RX:
+		if (status == WLC_E_STATUS_SUCCESS) {
+			DHD_EVENT(("MACEVENT: %s, SUCCESS\n", event_name));
+		} else if (status == WLC_E_STATUS_FAIL) {
+			DHD_EVENT(("MACEVENT: %s, FAIL\n", event_name));
+		} else {
+			DHD_EVENT(("MACEVENT: %s, status %d\n", event_name, status));
+		}
+		break;
+
+	case WLC_E_LINK:
+		DHD_EVENT(("MACEVENT: %s %s\n", event_name, link?"UP":"DOWN"));
+		break;
+
+	case WLC_E_MIC_ERROR:
+		DHD_EVENT(("MACEVENT: %s, MAC %s, Group %d, Flush %d\n",
+		       event_name, eabuf, group, flush_txq));
+		break;
+
+	case WLC_E_ICV_ERROR:
+	case WLC_E_UNICAST_DECODE_ERROR:
+	case WLC_E_MULTICAST_DECODE_ERROR:
+		DHD_EVENT(("MACEVENT: %s, MAC %s\n",
+		       event_name, eabuf));
+		break;
+
+	case WLC_E_TXFAIL:
+		DHD_EVENT(("MACEVENT: %s, RA %s\n", event_name, eabuf));
+		break;
+
+	case WLC_E_SCAN_COMPLETE:
+	case WLC_E_PMKID_CACHE:
+		DHD_EVENT(("MACEVENT: %s\n", event_name));
+		break;
+
+	case WLC_E_PFN_NET_FOUND:
+	case WLC_E_PFN_NET_LOST:
+	case WLC_E_PFN_SCAN_COMPLETE:
+		DHD_EVENT(("PNOEVENT: %s\n", event_name));
+		break;
+
+	case WLC_E_PSK_SUP:
+	case WLC_E_PRUNE:
+		DHD_EVENT(("MACEVENT: %s, status %d, reason %d\n",
+		           event_name, (int)status, (int)reason));
+		break;
+
+	case WLC_E_TRACE:
+		{
+			static uint32 seqnum_prev = 0;
+			msgtrace_hdr_t hdr;
+			uint32 nblost;
+			char *s, *p;
+
+			buf = (uchar *) event_data;
+			memcpy(&hdr, buf, MSGTRACE_HDRLEN);
+
+			if (hdr.version != MSGTRACE_VERSION) {
+				printf("\nMACEVENT: %s [unsupported version --> "
+				       "dhd version:%d dongle version:%d]\n",
+				       event_name, MSGTRACE_VERSION, hdr.version);
+				/* Reset datalen to avoid display below */
+				datalen = 0;
+				break;
+			}
+
+			/* There are 2 bytes available at the end of data */
+			buf[MSGTRACE_HDRLEN + ntoh16(hdr.len)] = '\0';
+
+			if (ntoh32(hdr.discarded_bytes) || ntoh32(hdr.discarded_printf)) {
+				printf("\nWLC_E_TRACE: [Discarded traces in dongle -->"
+				       "discarded_bytes %d discarded_printf %d]\n",
+				       ntoh32(hdr.discarded_bytes), ntoh32(hdr.discarded_printf));
+			}
+
+			nblost = ntoh32(hdr.seqnum) - seqnum_prev - 1;
+			if (nblost > 0) {
+				printf("\nWLC_E_TRACE: [Event lost --> seqnum %d nblost %d\n",
+				        ntoh32(hdr.seqnum), nblost);
+			}
+			seqnum_prev = ntoh32(hdr.seqnum);
+
+			/* Display the trace buffer. Advance from \n to \n to avoid display big
+			 * printf (issue with Linux printk )
+			 */
+			p = (char *)&buf[MSGTRACE_HDRLEN];
+			while ((s = strstr(p, "\n")) != NULL) {
+				*s = '\0';
+				printf("%s\n", p);
+				p = s + 1;
+			}
+			printf("%s\n", p);
+
+			/* Reset datalen to avoid display below */
+			datalen = 0;
+		}
+		break;
+
+
+	case WLC_E_RSSI:
+		DHD_EVENT(("MACEVENT: %s %d\n", event_name, ntoh32(*((int *)event_data))));
+		break;
+
+	default:
+		DHD_EVENT(("MACEVENT: %s %d, MAC %s, status %d, reason %d, auth %d\n",
+		       event_name, event_type, eabuf, (int)status, (int)reason,
+		       (int)auth_type));
+		break;
+	}
+
+	/* show any appended data */
+	if (datalen) {
+		buf = (uchar *) event_data;
+		DHD_EVENT((" data (%d) : ", datalen));
+		for (i = 0; i < datalen; i++)
+			DHD_EVENT((" 0x%02x ", *buf++));
+		DHD_EVENT(("\n"));
+	}
+}
+#endif /* SHOW_EVENTS */
+
+int
+wl_host_event(struct dhd_info *dhd, int *ifidx, void *pktdata,
+              wl_event_msg_t *event, void **data_ptr)
+{
+	/* check whether packet is a BRCM event pkt */
+	bcm_event_t *pvt_data = (bcm_event_t *)pktdata;
+	char *event_data;
+	uint32 type, status;
+	uint16 flags;
+	int evlen;
+
+	if (bcmp(BRCM_OUI, &pvt_data->bcm_hdr.oui[0], DOT11_OUI_LEN)) {
+		DHD_ERROR(("%s: mismatched OUI, bailing\n", __FUNCTION__));
+		return (BCME_ERROR);
+	}
+
+	/* BRCM event pkt may be unaligned - use xxx_ua to load user_subtype. */
+	if (ntoh16_ua((void *)&pvt_data->bcm_hdr.usr_subtype) != BCMILCP_BCM_SUBTYPE_EVENT) {
+		DHD_ERROR(("%s: mismatched subtype, bailing\n", __FUNCTION__));
+		return (BCME_ERROR);
+	}
+
+	*data_ptr = &pvt_data[1];
+	event_data = *data_ptr;
+
+	/* memcpy since BRCM event pkt may be unaligned. */
+	memcpy(event, &pvt_data->event, sizeof(wl_event_msg_t));
+
+	type = ntoh32_ua((void *)&event->event_type);
+	flags = ntoh16_ua((void *)&event->flags);
+	status = ntoh32_ua((void *)&event->status);
+	evlen = ntoh32_ua((void *)&event->datalen) + sizeof(bcm_event_t);
+
+	switch (type) {
+		case WLC_E_IF:
+			{
+				dhd_if_event_t *ifevent = (dhd_if_event_t *)event_data;
+				DHD_TRACE(("%s: if event\n", __FUNCTION__));
+
+				if (ifevent->ifidx > 0 && ifevent->ifidx < DHD_MAX_IFS)
+				{
+					if (ifevent->action == WLC_E_IF_ADD)
+						dhd_add_if(dhd, ifevent->ifidx,
+							NULL, event->ifname,
+							pvt_data->eth.ether_dhost,
+							ifevent->flags, ifevent->bssidx);
+					else
+						dhd_del_if(dhd, ifevent->ifidx);
+				} else {
+					DHD_ERROR(("%s: Invalid ifidx %d for %s\n",
+						__FUNCTION__, ifevent->ifidx, event->ifname));
+				}
+			}
+			/* send up the if event: btamp user needs it */
+			*ifidx = dhd_ifname2idx(dhd, event->ifname);
+			/* push up to external supp/auth */
+			dhd_event(dhd, (char *)pvt_data, evlen, *ifidx);
+			break;
+
+
+#ifdef P2P
+		case WLC_E_NDIS_LINK:
+			break;
+#endif
+		/* fall through */
+		/* These are what external supplicant/authenticator wants */
+		case WLC_E_LINK:
+		case WLC_E_ASSOC_IND:
+		case WLC_E_REASSOC_IND:
+		case WLC_E_DISASSOC_IND:
+		case WLC_E_MIC_ERROR:
+		default:
+		/* Fall through: this should get _everything_  */
+
+			*ifidx = dhd_ifname2idx(dhd, event->ifname);
+			/* push up to external supp/auth */
+			dhd_event(dhd, (char *)pvt_data, evlen, *ifidx);
+			DHD_TRACE(("%s: MAC event %d, flags %x, status %x\n",
+			           __FUNCTION__, type, flags, status));
+
+			/* put it back to WLC_E_NDIS_LINK */
+			if (type == WLC_E_NDIS_LINK) {
+				uint32 temp;
+
+				temp = ntoh32_ua((void *)&event->event_type);
+				DHD_TRACE(("Converted to WLC_E_LINK type %d\n", temp));
+
+				temp = ntoh32(WLC_E_NDIS_LINK);
+				memcpy((void *)(&pvt_data->event.event_type), &temp,
+					sizeof(pvt_data->event.event_type));
+			}
+			break;
+	}
+
+#ifdef SHOW_EVENTS
+	wl_show_host_event(event, event_data);
+#endif /* SHOW_EVENTS */
+
+	return (BCME_OK);
+}
+
+
+void
+wl_event_to_host_order(wl_event_msg_t *evt)
+{
+	/* Event struct members passed from dongle to host are stored in network
+	 * byte order. Convert all members to host-order.
+	 */
+	evt->event_type = ntoh32(evt->event_type);
+	evt->flags = ntoh16(evt->flags);
+	evt->status = ntoh32(evt->status);
+	evt->reason = ntoh32(evt->reason);
+	evt->auth_type = ntoh32(evt->auth_type);
+	evt->datalen = ntoh32(evt->datalen);
+	evt->version = ntoh16(evt->version);
+}
+
+void print_buf(void *pbuf, int len, int bytes_per_line)
+{
+	int i, j = 0;
+	unsigned char *buf = pbuf;
+
+	if (bytes_per_line == 0) {
+		bytes_per_line = len;
+	}
+
+	for (i = 0; i < len; i++) {
+		printf("%2.2x", *buf++);
+		j++;
+		if (j == bytes_per_line) {
+			printf("\n");
+			j = 0;
+		} else {
+			printf(":");
+		}
+	}
+	printf("\n");
+}
+
+#define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base))
+
+#ifdef PKT_FILTER_SUPPORT
+/* Convert user's input in hex pattern to byte-size mask */
+static int
+wl_pattern_atoh(char *src, char *dst)
+{
+	int i;
+	if (strncmp(src, "0x", 2) != 0 &&
+	    strncmp(src, "0X", 2) != 0) {
+		DHD_ERROR(("Mask invalid format. Needs to start with 0x\n"));
+		return -1;
+	}
+	src = src + 2; /* Skip past 0x */
+	if (strlen(src) % 2 != 0) {
+		DHD_ERROR(("Mask invalid format. Needs to be of even length\n"));
+		return -1;
+	}
+	for (i = 0; *src != '\0'; i++) {
+		char num[3];
+		strncpy(num, src, 2);
+		num[2] = '\0';
+		dst[i] = (uint8)strtoul(num, NULL, 16);
+		src += 2;
+	}
+	return i;
+}
+
+void
+dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode)
+{
+	char				*argv[8];
+	int					i = 0;
+	const char 			*str;
+	int					buf_len;
+	int					str_len;
+	char				*arg_save = 0, *arg_org = 0;
+	int					rc;
+	char				buf[128];
+	wl_pkt_filter_enable_t	enable_parm;
+	wl_pkt_filter_enable_t	* pkt_filterp;
+
+	if (!arg)
+		return;
+
+	if (!(arg_save = MALLOC(dhd->osh, strlen(arg) + 1))) {
+		DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
+		goto fail;
+	}
+	arg_org = arg_save;
+	memcpy(arg_save, arg, strlen(arg) + 1);
+
+	argv[i] = bcmstrtok(&arg_save, " ", 0);
+
+	i = 0;
+	if (NULL == argv[i]) {
+		DHD_ERROR(("No args provided\n"));
+		goto fail;
+	}
+
+	str = "pkt_filter_enable";
+	str_len = strlen(str);
+	strncpy(buf, str, str_len);
+	buf[str_len] = '\0';
+	buf_len = str_len + 1;
+
+	pkt_filterp = (wl_pkt_filter_enable_t *)(buf + str_len + 1);
+
+	/* Parse packet filter id. */
+	enable_parm.id = htod32(strtoul(argv[i], NULL, 0));
+
+	/* Parse enable/disable value. */
+	enable_parm.enable = htod32(enable);
+
+	buf_len += sizeof(enable_parm);
+	memcpy((char *)pkt_filterp,
+	       &enable_parm,
+	       sizeof(enable_parm));
+
+	/* Enable/disable the specified filter. */
+	rc = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, buf, buf_len);
+	rc = rc >= 0 ? 0 : rc;
+	if (rc)
+		DHD_TRACE(("%s: failed to add pktfilter %s, retcode = %d\n",
+		__FUNCTION__, arg, rc));
+	else
+		DHD_TRACE(("%s: successfully added pktfilter %s\n",
+		__FUNCTION__, arg));
+
+	/* Contorl the master mode */
+	bcm_mkiovar("pkt_filter_mode", (char *)&master_mode, 4, buf, sizeof(buf));
+	rc = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, buf, sizeof(buf));
+	rc = rc >= 0 ? 0 : rc;
+	if (rc)
+		DHD_TRACE(("%s: failed to add pktfilter %s, retcode = %d\n",
+		__FUNCTION__, arg, rc));
+
+fail:
+	if (arg_org)
+		MFREE(dhd->osh, arg_org, strlen(arg) + 1);
+}
+
+void
+dhd_pktfilter_offload_set(dhd_pub_t * dhd, char *arg)
+{
+	const char 			*str;
+	wl_pkt_filter_t		pkt_filter;
+	wl_pkt_filter_t		*pkt_filterp;
+	int					buf_len;
+	int					str_len;
+	int 				rc;
+	uint32				mask_size;
+	uint32				pattern_size;
+	char				*argv[8], * buf = 0;
+	int					i = 0;
+	char				*arg_save = 0, *arg_org = 0;
+#define BUF_SIZE		2048
+
+	if (!arg)
+		return;
+
+	if (!(arg_save = MALLOC(dhd->osh, strlen(arg) + 1))) {
+		DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
+		goto fail;
+	}
+
+	arg_org = arg_save;
+
+	if (!(buf = MALLOC(dhd->osh, BUF_SIZE))) {
+		DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
+		goto fail;
+	}
+
+	memcpy(arg_save, arg, strlen(arg) + 1);
+
+	if (strlen(arg) > BUF_SIZE) {
+		DHD_ERROR(("Not enough buffer %d < %d\n", (int)strlen(arg), (int)sizeof(buf)));
+		goto fail;
+	}
+
+	argv[i] = bcmstrtok(&arg_save, " ", 0);
+	while (argv[i++])
+		argv[i] = bcmstrtok(&arg_save, " ", 0);
+
+	i = 0;
+	if (NULL == argv[i]) {
+		DHD_ERROR(("No args provided\n"));
+		goto fail;
+	}
+
+	str = "pkt_filter_add";
+	str_len = strlen(str);
+	strncpy(buf, str, str_len);
+	buf[ str_len ] = '\0';
+	buf_len = str_len + 1;
+
+	pkt_filterp = (wl_pkt_filter_t *) (buf + str_len + 1);
+
+	/* Parse packet filter id. */
+	pkt_filter.id = htod32(strtoul(argv[i], NULL, 0));
+
+	if (NULL == argv[++i]) {
+		DHD_ERROR(("Polarity not provided\n"));
+		goto fail;
+	}
+
+	/* Parse filter polarity. */
+	pkt_filter.negate_match = htod32(strtoul(argv[i], NULL, 0));
+
+	if (NULL == argv[++i]) {
+		DHD_ERROR(("Filter type not provided\n"));
+		goto fail;
+	}
+
+	/* Parse filter type. */
+	pkt_filter.type = htod32(strtoul(argv[i], NULL, 0));
+
+	if (NULL == argv[++i]) {
+		DHD_ERROR(("Offset not provided\n"));
+		goto fail;
+	}
+
+	/* Parse pattern filter offset. */
+	pkt_filter.u.pattern.offset = htod32(strtoul(argv[i], NULL, 0));
+
+	if (NULL == argv[++i]) {
+		DHD_ERROR(("Bitmask not provided\n"));
+		goto fail;
+	}
+
+	/* Parse pattern filter mask. */
+	mask_size =
+		htod32(wl_pattern_atoh(argv[i], (char *) pkt_filterp->u.pattern.mask_and_pattern));
+
+	if (NULL == argv[++i]) {
+		DHD_ERROR(("Pattern not provided\n"));
+		goto fail;
+	}
+
+	/* Parse pattern filter pattern. */
+	pattern_size =
+		htod32(wl_pattern_atoh(argv[i],
+	         (char *) &pkt_filterp->u.pattern.mask_and_pattern[mask_size]));
+
+	if (mask_size != pattern_size) {
+		DHD_ERROR(("Mask and pattern not the same size\n"));
+		goto fail;
+	}
+
+	pkt_filter.u.pattern.size_bytes = mask_size;
+	buf_len += WL_PKT_FILTER_FIXED_LEN;
+	buf_len += (WL_PKT_FILTER_PATTERN_FIXED_LEN + 2 * mask_size);
+
+	/* Keep-alive attributes are set in local	variable (keep_alive_pkt), and
+	** then memcpy'ed into buffer (keep_alive_pktp) since there is no
+	** guarantee that the buffer is properly aligned.
+	*/
+	memcpy((char *)pkt_filterp,
+	       &pkt_filter,
+	       WL_PKT_FILTER_FIXED_LEN + WL_PKT_FILTER_PATTERN_FIXED_LEN);
+
+	rc = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, buf, buf_len);
+	rc = rc >= 0 ? 0 : rc;
+
+	if (rc)
+		DHD_TRACE(("%s: failed to add pktfilter %s, retcode = %d\n",
+		__FUNCTION__, arg, rc));
+	else
+		DHD_TRACE(("%s: successfully added pktfilter %s\n",
+		__FUNCTION__, arg));
+
+fail:
+	if (arg_org)
+		MFREE(dhd->osh, arg_org, strlen(arg) + 1);
+
+	if (buf)
+		MFREE(dhd->osh, buf, BUF_SIZE);
+}
+#endif
+
+#ifdef ARP_OFFLOAD_SUPPORT
+void
+dhd_arp_offload_set(dhd_pub_t * dhd, int arp_mode)
+{
+	char iovbuf[32];
+	int retcode;
+
+	bcm_mkiovar("arp_ol", (char *)&arp_mode, 4, iovbuf, sizeof(iovbuf));
+	retcode = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
+	retcode = retcode >= 0 ? 0 : retcode;
+	if (retcode)
+		DHD_TRACE(("%s: failed to set ARP offload mode to 0x%x, retcode = %d\n",
+		__FUNCTION__, arp_mode, retcode));
+	else
+		DHD_TRACE(("%s: successfully set ARP offload mode to 0x%x\n",
+		__FUNCTION__, arp_mode));
+}
+
+void
+dhd_arp_offload_enable(dhd_pub_t * dhd, int arp_enable)
+{
+	char iovbuf[32];
+	int retcode;
+
+	bcm_mkiovar("arpoe", (char *)&arp_enable, 4, iovbuf, sizeof(iovbuf));
+	retcode = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
+	retcode = retcode >= 0 ? 0 : retcode;
+	if (retcode)
+		DHD_TRACE(("%s: failed to enabe ARP offload to %d, retcode = %d\n",
+		__FUNCTION__, arp_enable, retcode));
+	else
+		DHD_TRACE(("%s: successfully enabed ARP offload to %d\n",
+		__FUNCTION__, arp_enable));
+}
+#endif
+
+
+void dhd_arp_cleanup(dhd_pub_t *dhd)
+{
+#ifdef ARP_OFFLOAD_SUPPORT
+	int ret = 0;
+	int iov_len = 0;
+	char iovbuf[128];
+
+	if (dhd == NULL) return;
+
+	dhd_os_proto_block(dhd);
+
+	iov_len = bcm_mkiovar("arp_hostip_clear", 0, 0, iovbuf, sizeof(iovbuf));
+	if ((ret  = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, iov_len)) < 0)
+		DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret));
+
+	iov_len = bcm_mkiovar("arp_table_clear", 0, 0, iovbuf, sizeof(iovbuf));
+	if ((ret  = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, iov_len)) < 0)
+		DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret));
+
+	dhd_os_proto_unblock(dhd);
+
+#endif /* ARP_OFFLOAD_SUPPORT */
+}
+
+void dhd_arp_offload_add_ip(dhd_pub_t *dhd, u32 ipaddr)
+{
+#ifdef ARP_OFFLOAD_SUPPORT
+	int iov_len = 0;
+	char iovbuf[32];
+	int retcode;
+
+	dhd_os_proto_block(dhd);
+
+	iov_len = bcm_mkiovar("arp_hostip", (char *)&ipaddr, 4, iovbuf, sizeof(iovbuf));
+	retcode = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, iov_len);
+
+	dhd_os_proto_unblock(dhd);
+
+	if (retcode)
+		DHD_TRACE(("%s: ARP ip addr add failed, retcode = %d\n",
+		__FUNCTION__, retcode));
+	else
+		DHD_TRACE(("%s: ARP ipaddr entry added\n",
+		__FUNCTION__));
+#endif /* ARP_OFFLOAD_SUPPORT */
+}
+
+
+int dhd_arp_get_arp_hostip_table(dhd_pub_t *dhd, void *buf, int buflen)
+{
+#ifdef ARP_OFFLOAD_SUPPORT
+	int retcode;
+	int iov_len = 0;
+
+	if (!buf)
+		return -1;
+
+	dhd_os_proto_block(dhd);
+
+	iov_len = bcm_mkiovar("arp_hostip", 0, 0, buf, buflen);
+	retcode = dhdcdc_query_ioctl(dhd, 0, WLC_GET_VAR, buf, buflen);
+
+	dhd_os_proto_unblock(dhd);
+
+	if (retcode) {
+		DHD_TRACE(("%s: ioctl WLC_GET_VAR error %d\n",
+		__FUNCTION__, retcode));
+
+		return -1;
+	}
+#endif /* ARP_OFFLOAD_SUPPORT */
+	return 0;
+}
+
+
+int
+dhd_preinit_ioctls(dhd_pub_t *dhd)
+{
+	char iovbuf[WL_EVENTING_MASK_LEN + 12];	/*  Room for "event_msgs" + '\0' + bitvec  */
+	uint up = 0;
+	char buf[128], *ptr;
+	uint power_mode = PM_FAST;
+	uint32 dongle_align = DHD_SDALIGN;
+	uint32 glom = 0;
+	uint bcn_timeout = 4;
+	int scan_assoc_time = 40;
+	int scan_unassoc_time = 40;
+	uint32 listen_interval = LISTEN_INTERVAL; /* Default Listen Interval in Beacons */
+#if defined(SOFTAP)
+	uint dtim = 1;
+#endif
+	int ret = 0;
+#ifdef GET_CUSTOM_MAC_ENABLE
+	struct ether_addr ea_addr;
+#endif /* GET_CUSTOM_MAC_ENABLE */
+
+	dhd_os_proto_block(dhd);
+
+#ifdef GET_CUSTOM_MAC_ENABLE
+	/*
+	** Read MAC address from external customer place
+	** NOTE that default mac address has to be present in otp or nvram file
+	** to bring up firmware but unique per board mac address maybe provided
+	** by customer code
+	*/
+	ret = dhd_custom_get_mac_address(ea_addr.octet);
+	if (!ret) {
+		bcm_mkiovar("cur_etheraddr", (void *)&ea_addr, ETHER_ADDR_LEN, buf, sizeof(buf));
+		ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, buf, sizeof(buf));
+		if (ret < 0) {
+			DHD_ERROR(("%s: can't set MAC address , error=%d\n", __FUNCTION__, ret));
+		} else
+			memcpy(dhd->mac.octet, (void *)&ea_addr, ETHER_ADDR_LEN);
+	}
+#endif /* GET_CUSTOM_MAC_ENABLE */
+
+#ifdef SET_RANDOM_MAC_SOFTAP
+	if (strstr(fw_path, "apsta") != NULL) {
+		uint rand_mac;
+
+		srandom32((uint)jiffies);
+		rand_mac = random32();
+		iovbuf[0] = 0x02;              /* locally administered bit */
+		iovbuf[1] = 0x1A;
+		iovbuf[2] = 0x11;
+		iovbuf[3] = (unsigned char)(rand_mac & 0x0F) | 0xF0;
+		iovbuf[4] = (unsigned char)(rand_mac >> 8);
+		iovbuf[5] = (unsigned char)(rand_mac >> 16);
+
+		printk("Broadcom Dongle Host Driver mac=%02x:%02x:%02x:%02x:%02x:%02x\n",
+			iovbuf[0], iovbuf[1], iovbuf[2], iovbuf[3], iovbuf[4], iovbuf[5]);
+
+		bcm_mkiovar("cur_etheraddr", (void *)iovbuf, ETHER_ADDR_LEN, buf, sizeof(buf));
+		ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, buf, sizeof(buf));
+		if (ret < 0) {
+			DHD_ERROR(("%s: can't set MAC address , error=%d\n", __FUNCTION__, ret));
+		} else
+			memcpy(dhd->mac.octet, iovbuf, ETHER_ADDR_LEN);
+	}
+#endif /* SET_RANDOM_MAC_SOFTAP */
+
+	/* Set Country code */
+	if (dhd->dhd_cspec.ccode[0] != 0) {
+		bcm_mkiovar("country", (char *)&dhd->dhd_cspec, \
+			sizeof(wl_country_t), iovbuf, sizeof(iovbuf));
+		if ((ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf))) < 0) {
+			DHD_ERROR(("%s: country code setting failed\n", __FUNCTION__));
+		}
+	}
+
+	/* Set Listen Interval */
+	bcm_mkiovar("assoc_listen", (char *)&listen_interval, 4, iovbuf, sizeof(iovbuf));
+	if ((ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf))) < 0)
+		DHD_ERROR(("%s assoc_listen failed %d\n", __FUNCTION__, ret));
+
+	/* query for 'ver' to get version info from firmware */
+	memset(buf, 0, sizeof(buf));
+	ptr = buf;
+	bcm_mkiovar("ver", 0, 0, buf, sizeof(buf));
+	dhdcdc_query_ioctl(dhd, 0, WLC_GET_VAR, buf, sizeof(buf));
+	bcmstrtok(&ptr, "\n", 0);
+	/* Print fw version info */
+	DHD_ERROR(("Firmware version = %s\n", buf));
+
+	/* Set PowerSave mode */
+	dhdcdc_set_ioctl(dhd, 0, WLC_SET_PM, (char *)&power_mode, sizeof(power_mode));
+
+	/* Match Host and Dongle rx alignment */
+	bcm_mkiovar("bus:txglomalign", (char *)&dongle_align, 4, iovbuf, sizeof(iovbuf));
+	dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
+
+	/* disable glom option per default */
+	bcm_mkiovar("bus:txglom", (char *)&glom, 4, iovbuf, sizeof(iovbuf));
+	dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
+
+	/* Setup timeout if Beacons are lost and roam is off to report link down */
+	bcm_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf, sizeof(iovbuf));
+	dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
+
+	/* Enable/Disable build-in roaming to allowed ext supplicant to take of romaing */
+	bcm_mkiovar("roam_off", (char *)&dhd_roam, 4, iovbuf, sizeof(iovbuf));
+	dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
+
+#if defined(SOFTAP)
+	if (ap_fw_loaded == TRUE) {
+		dhdcdc_set_ioctl(dhd, 0, WLC_SET_DTIMPRD, (char *)&dtim, sizeof(dtim));
+	}
+#endif
+
+	if (dhd_roam == 0)
+	{
+		/* set internal roaming roaming parameters */
+		int roam_scan_period = 30; /* in sec */
+		int roam_fullscan_period = 120; /* in sec */
+		int roam_trigger = -85;
+		int roam_delta = 15;
+		int band;
+		int band_temp_set = WLC_BAND_2G;
+
+		if (dhdcdc_set_ioctl(dhd, 0, WLC_SET_ROAM_SCAN_PERIOD, \
+			(char *)&roam_scan_period, sizeof(roam_scan_period)) < 0)
+			DHD_ERROR(("%s: roam scan setup failed\n", __FUNCTION__));
+
+		bcm_mkiovar("fullroamperiod", (char *)&roam_fullscan_period, \
+					 4, iovbuf, sizeof(iovbuf));
+		if (dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, \
+			iovbuf, sizeof(iovbuf)) < 0)
+			DHD_ERROR(("%s: roam fullscan setup failed\n", __FUNCTION__));
+
+		if (dhdcdc_query_ioctl(dhd, 0, WLC_GET_BAND, \
+				(char *)&band, sizeof(band)) < 0)
+			DHD_ERROR(("%s: roam delta setting failed\n", __FUNCTION__));
+		else {
+			if ((band == WLC_BAND_AUTO) || (band == WLC_BAND_ALL))
+			{
+				/* temp set band to insert new roams values */
+				if (dhdcdc_set_ioctl(dhd, 0, WLC_SET_BAND, \
+					(char *)&band_temp_set, sizeof(band_temp_set)) < 0)
+					DHD_ERROR(("%s: local band seting failed\n", __FUNCTION__));
+			}
+			if (dhdcdc_set_ioctl(dhd, 0, WLC_SET_ROAM_DELTA, \
+				(char *)&roam_delta, sizeof(roam_delta)) < 0)
+				DHD_ERROR(("%s: roam delta setting failed\n", __FUNCTION__));
+
+			if (dhdcdc_set_ioctl(dhd, 0, WLC_SET_ROAM_TRIGGER, \
+				(char *)&roam_trigger, sizeof(roam_trigger)) < 0)
+				DHD_ERROR(("%s: roam trigger setting failed\n", __FUNCTION__));
+
+			/* Restore original band settinngs */
+			if (dhdcdc_set_ioctl(dhd, 0, WLC_SET_BAND, \
+				(char *)&band, sizeof(band)) < 0)
+				DHD_ERROR(("%s: Original band restore failed\n", __FUNCTION__));
+		}
+	}
+
+	/* Force STA UP */
+	if (dhd_radio_up)
+		dhdcdc_set_ioctl(dhd, 0, WLC_UP, (char *)&up, sizeof(up));
+
+	/* Setup event_msgs */
+	bcm_mkiovar("event_msgs", dhd->eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf));
+	dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
+
+	dhdcdc_set_ioctl(dhd, 0, WLC_SET_SCAN_CHANNEL_TIME, (char *)&scan_assoc_time,
+		sizeof(scan_assoc_time));
+	dhdcdc_set_ioctl(dhd, 0, WLC_SET_SCAN_UNASSOC_TIME, (char *)&scan_unassoc_time,
+		sizeof(scan_unassoc_time));
+
+#ifdef ARP_OFFLOAD_SUPPORT
+	/* Set and enable ARP offload feature */
+	if (dhd_arp_enable)
+		dhd_arp_offload_set(dhd, dhd_arp_mode);
+	dhd_arp_offload_enable(dhd, dhd_arp_enable);
+#endif /* ARP_OFFLOAD_SUPPORT */
+
+#ifdef PKT_FILTER_SUPPORT
+	{
+		int i;
+		/* Set up pkt filter */
+		if (dhd_pkt_filter_enable) {
+			for (i = 0; i < dhd->pktfilter_count; i++) {
+				dhd_pktfilter_offload_set(dhd, dhd->pktfilter[i]);
+				dhd_pktfilter_offload_enable(dhd, dhd->pktfilter[i],
+					dhd_pkt_filter_init, dhd_master_mode);
+			}
+		}
+	}
+#endif /* PKT_FILTER_SUPPORT */
+
+#if defined(KEEP_ALIVE)
+	{
+	/* Set Keep Alive : be sure to use FW with -keepalive */
+	int res;
+
+	if (ap_fw_loaded == FALSE) {
+		if ((res = dhd_keep_alive_onoff(dhd, 1)) < 0)
+			DHD_ERROR(("%s set keeplive failed %d\n", \
+		__FUNCTION__, res));
+		}
+	}
+#endif
+
+	dhd_os_proto_unblock(dhd);
+
+	return 0;
+}
+
+#ifdef SIMPLE_ISCAN
+
+uint iscan_thread_id;
+iscan_buf_t * iscan_chain = 0;
+
+iscan_buf_t *
+dhd_iscan_allocate_buf(dhd_pub_t *dhd, iscan_buf_t **iscanbuf)
+{
+	iscan_buf_t *iscanbuf_alloc = 0;
+	iscan_buf_t *iscanbuf_head;
+
+	dhd_iscan_lock();
+
+	iscanbuf_alloc = (iscan_buf_t*)MALLOC(dhd->osh, sizeof(iscan_buf_t));
+	if (iscanbuf_alloc == NULL)
+		goto fail;
+
+	iscanbuf_alloc->next = NULL;
+	iscanbuf_head = *iscanbuf;
+
+	DHD_ISCAN(("%s: addr of allocated node = 0x%X"
+		   "addr of iscanbuf_head = 0x%X dhd = 0x%X\n",
+		   __FUNCTION__, iscanbuf_alloc, iscanbuf_head, dhd));
+
+	if (iscanbuf_head == NULL) {
+		*iscanbuf = iscanbuf_alloc;
+		DHD_ISCAN(("%s: Head is allocated\n", __FUNCTION__));
+		goto fail;
+	}
+
+	while (iscanbuf_head->next)
+		iscanbuf_head = iscanbuf_head->next;
+
+	iscanbuf_head->next = iscanbuf_alloc;
+
+fail:
+	dhd_iscan_unlock();
+	return iscanbuf_alloc;
+}
+
+void
+dhd_iscan_free_buf(void *dhdp, iscan_buf_t *iscan_delete)
+{
+	iscan_buf_t *iscanbuf_free = 0;
+	iscan_buf_t *iscanbuf_prv = 0;
+	iscan_buf_t *iscanbuf_cur = iscan_chain;
+	dhd_pub_t *dhd = dhd_bus_pub(dhdp);
+
+	dhd_iscan_lock();
+	/* If iscan_delete is null then delete the entire
+	 * chain or else delete specific one provided
+	 */
+	if (!iscan_delete) {
+		while (iscanbuf_cur) {
+			iscanbuf_free = iscanbuf_cur;
+			iscanbuf_cur = iscanbuf_cur->next;
+			iscanbuf_free->next = 0;
+			MFREE(dhd->osh, iscanbuf_free, sizeof(iscan_buf_t));
+		}
+		iscan_chain = 0;
+	} else {
+		while (iscanbuf_cur) {
+			if (iscanbuf_cur == iscan_delete)
+				break;
+			iscanbuf_prv = iscanbuf_cur;
+			iscanbuf_cur = iscanbuf_cur->next;
+		}
+		if (iscanbuf_prv)
+			iscanbuf_prv->next = iscan_delete->next;
+
+		iscan_delete->next = 0;
+		MFREE(dhd->osh, iscan_delete, sizeof(iscan_buf_t));
+
+		if (!iscanbuf_prv)
+			iscan_chain = 0;
+	}
+	dhd_iscan_unlock();
+}
+
+iscan_buf_t *
+dhd_iscan_result_buf(void)
+{
+	return iscan_chain;
+}
+
+
+
+/*
+* print scan cache
+* print partial iscan_skip list differently
+*/
+int
+dhd_iscan_print_cache(iscan_buf_t *iscan_skip)
+{
+	int i = 0, l = 0;
+	iscan_buf_t *iscan_cur;
+	wl_iscan_results_t *list;
+	wl_scan_results_t *results;
+	wl_bss_info_t UNALIGNED *bi;
+
+	dhd_iscan_lock();
+
+	iscan_cur = dhd_iscan_result_buf();
+
+	while (iscan_cur) {
+		list = (wl_iscan_results_t *)iscan_cur->iscan_buf;
+		if (!list)
+			break;
+
+		results = (wl_scan_results_t *)&list->results;
+		if (!results)
+			break;
+
+		if (results->version != WL_BSS_INFO_VERSION) {
+			DHD_ISCAN(("%s: results->version %d != WL_BSS_INFO_VERSION\n",
+				__FUNCTION__, results->version));
+			goto done;
+		}
+
+		bi = results->bss_info;
+		for (i = 0; i < results->count; i++) {
+			if (!bi)
+				break;
+
+			DHD_ISCAN(("%s[%2.2d:%2.2d] %X:%X:%X:%X:%X:%X\n",
+				iscan_cur != iscan_skip?"BSS":"bss", l, i,
+				bi->BSSID.octet[0], bi->BSSID.octet[1], bi->BSSID.octet[2],
+				bi->BSSID.octet[3], bi->BSSID.octet[4], bi->BSSID.octet[5]));
+
+			bi = (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length));
+		}
+		iscan_cur = iscan_cur->next;
+		l++;
+	}
+
+done:
+	dhd_iscan_unlock();
+	return 0;
+}
+
+/*
+* delete disappeared AP from specific scan cache but skip partial list in iscan_skip
+*/
+int
+dhd_iscan_delete_bss(void *dhdp, void *addr, iscan_buf_t *iscan_skip)
+{
+	int i = 0, j = 0, l = 0;
+	iscan_buf_t *iscan_cur;
+	wl_iscan_results_t *list;
+	wl_scan_results_t *results;
+	wl_bss_info_t UNALIGNED *bi, *bi_new, *bi_next;
+
+	uchar *s_addr = addr;
+
+	dhd_iscan_lock();
+	DHD_ISCAN(("%s: BSS to remove %X:%X:%X:%X:%X:%X\n",
+		__FUNCTION__, s_addr[0], s_addr[1], s_addr[2],
+		s_addr[3], s_addr[4], s_addr[5]));
+
+	iscan_cur = dhd_iscan_result_buf();
+
+	while (iscan_cur) {
+		if (iscan_cur != iscan_skip) {
+			list = (wl_iscan_results_t *)iscan_cur->iscan_buf;
+			if (!list)
+				break;
+
+			results = (wl_scan_results_t *)&list->results;
+			if (!results)
+				break;
+
+			if (results->version != WL_BSS_INFO_VERSION) {
+				DHD_ERROR(("%s: results->version %d != WL_BSS_INFO_VERSION\n",
+				__FUNCTION__, results->version));
+				goto done;
+			}
+
+			bi = results->bss_info;
+			for (i = 0; i < results->count; i++) {
+				if (!bi)
+					break;
+
+				if (!memcmp(bi->BSSID.octet, addr, ETHER_ADDR_LEN)) {
+					DHD_ISCAN(("%s: Del BSS[%2.2d:%2.2d] %X:%X:%X:%X:%X:%X\n",
+					__FUNCTION__, l, i, bi->BSSID.octet[0],
+					bi->BSSID.octet[1], bi->BSSID.octet[2],
+					bi->BSSID.octet[3], bi->BSSID.octet[4],
+					bi->BSSID.octet[5]));
+
+					bi_new = bi;
+					bi = (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length));
+/*
+					if(bi && bi_new) {
+						bcopy(bi, bi_new, results->buflen -
+						dtoh32(bi_new->length));
+						results->buflen -= dtoh32(bi_new->length);
+					}
+*/
+					results->buflen -= dtoh32(bi_new->length);
+					results->count--;
+
+					for (j = i; j < results->count; j++) {
+						if (bi && bi_new) {
+							DHD_ISCAN(("%s: Moved up BSS[%2.2d:%2.2d]"
+							"%X:%X:%X:%X:%X:%X\n",
+							__FUNCTION__, l, j, bi->BSSID.octet[0],
+							bi->BSSID.octet[1], bi->BSSID.octet[2],
+							bi->BSSID.octet[3], bi->BSSID.octet[4],
+							bi->BSSID.octet[5]));
+
+							bi_next = (wl_bss_info_t *)((uintptr)bi +
+								dtoh32(bi->length));
+							bcopy(bi, bi_new, dtoh32(bi->length));
+							bi_new = (wl_bss_info_t *)((uintptr)bi_new +
+								dtoh32(bi_new->length));
+							bi = bi_next;
+						}
+					}
+
+					if (results->count == 0) {
+						/* Prune now empty partial scan list */
+						dhd_iscan_free_buf(dhdp, iscan_cur);
+						goto done;
+					}
+					break;
+				}
+				bi = (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length));
+			}
+		}
+		iscan_cur = iscan_cur->next;
+		l++;
+	}
+
+done:
+	dhd_iscan_unlock();
+	return 0;
+}
+
+int
+dhd_iscan_remove_duplicates(void * dhdp, iscan_buf_t *iscan_cur)
+{
+	int i = 0;
+	wl_iscan_results_t *list;
+	wl_scan_results_t *results;
+	wl_bss_info_t UNALIGNED *bi, *bi_new, *bi_next;
+
+	dhd_iscan_lock();
+
+	DHD_ISCAN(("%s: Scan cache before delete\n",
+		__FUNCTION__));
+	dhd_iscan_print_cache(iscan_cur);
+
+	if (!iscan_cur)
+		goto done;
+
+	list = (wl_iscan_results_t *)iscan_cur->iscan_buf;
+	if (!list)
+		goto done;
+
+	results = (wl_scan_results_t *)&list->results;
+	if (!results)
+		goto done;
+
+	if (results->version != WL_BSS_INFO_VERSION) {
+		DHD_ERROR(("%s: results->version %d != WL_BSS_INFO_VERSION\n",
+			__FUNCTION__, results->version));
+		goto done;
+	}
+
+	bi = results->bss_info;
+	for (i = 0; i < results->count; i++) {
+		if (!bi)
+			break;
+
+		DHD_ISCAN(("%s: Find dups for BSS[%2.2d] %X:%X:%X:%X:%X:%X\n",
+			__FUNCTION__, i, bi->BSSID.octet[0], bi->BSSID.octet[1], bi->BSSID.octet[2],
+			bi->BSSID.octet[3], bi->BSSID.octet[4], bi->BSSID.octet[5]));
+
+		dhd_iscan_delete_bss(dhdp, bi->BSSID.octet, iscan_cur);
+
+		bi = (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length));
+	}
+
+done:
+	DHD_ISCAN(("%s: Scan cache after delete\n", __FUNCTION__));
+	dhd_iscan_print_cache(iscan_cur);
+	dhd_iscan_unlock();
+	return 0;
+}
+
+void
+dhd_iscan_ind_scan_confirm(void *dhdp, bool status)
+{
+
+	dhd_ind_scan_confirm(dhdp, status);
+}
+
+int
+dhd_iscan_request(void * dhdp, uint16 action)
+{
+	int rc;
+	wl_iscan_params_t params;
+	dhd_pub_t *dhd = dhd_bus_pub(dhdp);
+	char buf[WLC_IOCTL_SMLEN];
+
+
+	memset(&params, 0, sizeof(wl_iscan_params_t));
+	memcpy(&params.params.bssid, &ether_bcast, ETHER_ADDR_LEN);
+
+	params.params.bss_type = DOT11_BSSTYPE_ANY;
+	params.params.scan_type = DOT11_SCANTYPE_ACTIVE;
+
+	params.params.nprobes = htod32(-1);
+	params.params.active_time = htod32(-1);
+	params.params.passive_time = htod32(-1);
+	params.params.home_time = htod32(-1);
+	params.params.channel_num = htod32(0);
+
+	params.version = htod32(ISCAN_REQ_VERSION);
+	params.action = htod16(action);
+	params.scan_duration = htod16(0);
+
+	bcm_mkiovar("iscan", (char *)&params, sizeof(wl_iscan_params_t), buf, WLC_IOCTL_SMLEN);
+	rc = dhd_wl_ioctl(dhdp, WLC_SET_VAR, buf, WLC_IOCTL_SMLEN);
+
+	return rc;
+}
+
+static int
+dhd_iscan_get_partial_result(void *dhdp, uint *scan_count)
+{
+	wl_iscan_results_t *list_buf;
+	wl_iscan_results_t list;
+	wl_scan_results_t *results;
+	iscan_buf_t *iscan_cur;
+	int status = -1;
+	dhd_pub_t *dhd = dhd_bus_pub(dhdp);
+	int rc;
+
+
+	iscan_cur = dhd_iscan_allocate_buf(dhd, &iscan_chain);
+	if (!iscan_cur) {
+		DHD_ERROR(("%s: Failed to allocate node\n", __FUNCTION__));
+		dhd_iscan_free_buf(dhdp, 0);
+		dhd_iscan_request(dhdp, WL_SCAN_ACTION_ABORT);
+		goto fail;
+	}
+
+	dhd_iscan_lock();
+
+	memset(iscan_cur->iscan_buf, 0, WLC_IW_ISCAN_MAXLEN);
+	list_buf = (wl_iscan_results_t*)iscan_cur->iscan_buf;
+	results = &list_buf->results;
+	results->buflen = WL_ISCAN_RESULTS_FIXED_SIZE;
+	results->version = 0;
+	results->count = 0;
+
+	memset(&list, 0, sizeof(list));
+	list.results.buflen = htod32(WLC_IW_ISCAN_MAXLEN);
+	bcm_mkiovar("iscanresults", (char *)&list, WL_ISCAN_RESULTS_FIXED_SIZE,
+		iscan_cur->iscan_buf, WLC_IW_ISCAN_MAXLEN);
+	rc = dhd_wl_ioctl(dhdp, WLC_GET_VAR, iscan_cur->iscan_buf, WLC_IW_ISCAN_MAXLEN);
+
+	results->buflen = dtoh32(results->buflen);
+	results->version = dtoh32(results->version);
+	*scan_count = results->count = dtoh32(results->count);
+	status = dtoh32(list_buf->status);
+
+	dhd_iscan_unlock();
+
+	if (!(*scan_count))
+		dhd_iscan_free_buf(dhdp, iscan_cur);
+	else
+		dhd_iscan_remove_duplicates(dhdp, iscan_cur);
+
+
+fail:
+	return status;
+}
+
+#endif
+
+/*
+ * returns = TRUE if associated, FALSE if not associated
+ */
+bool is_associated(dhd_pub_t *dhd, void *bss_buf)
+{
+	char bssid[ETHER_ADDR_LEN], zbuf[ETHER_ADDR_LEN];
+	int ret = -1;
+
+	bzero(bssid, ETHER_ADDR_LEN);
+	bzero(zbuf, ETHER_ADDR_LEN);
+
+	ret = dhdcdc_set_ioctl(dhd, 0, WLC_GET_BSSID, (char *)bssid, ETHER_ADDR_LEN);
+	DHD_TRACE((" %s WLC_GET_BSSID ioctl res = %d\n", __FUNCTION__, ret));
+
+	if (ret == BCME_NOTASSOCIATED) {
+		DHD_TRACE(("%s: not associated! res:%d\n", __FUNCTION__, ret));
+	}
+
+	if (ret < 0)
+		return FALSE;
+
+	if ((memcmp(bssid, zbuf, ETHER_ADDR_LEN) != 0)) {
+		/*  STA is assocoated BSSID is non zero */
+
+		if (bss_buf) {
+			/* return bss if caller provided buf */
+			memcpy(bss_buf, bssid, ETHER_ADDR_LEN);
+		}
+		return TRUE;
+	} else {
+		DHD_TRACE(("%s: WLC_GET_BSSID ioctl returned zero bssid\n", __FUNCTION__));
+		return FALSE;
+	}
+}
+
+/* Function to estimate possible DTIM_SKIP value */
+int dhd_get_dtim_skip(dhd_pub_t *dhd)
+{
+	int bcn_li_dtim;
+	char buf[128];
+	int ret;
+	int dtim_assoc = 0;
+
+	if ((dhd->dtim_skip == 0) || (dhd->dtim_skip == 1))
+		bcn_li_dtim = 3;
+	else
+		bcn_li_dtim = dhd->dtim_skip;
+
+	/* Read DTIM value if associated */
+	memset(buf, 0, sizeof(buf));
+	bcm_mkiovar("dtim_assoc", 0, 0, buf, sizeof(buf));
+	if ((ret = dhdcdc_query_ioctl(dhd, 0, WLC_GET_VAR, buf, sizeof(buf))) < 0) {
+		DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret));
+		bcn_li_dtim = 1;
+		goto exit;
+	}
+	else
+		dtim_assoc = dtoh32(*(int *)buf);
+
+	DHD_ERROR(("%s bcn_li_dtim=%d DTIM=%d Listen=%d\n", \
+			 __FUNCTION__, bcn_li_dtim, dtim_assoc, LISTEN_INTERVAL));
+
+	/* if not assocated just eixt */
+	if (dtim_assoc == 0) {
+		goto exit;
+	}
+
+	/* check if sta listen interval fits into AP dtim */
+	if (dtim_assoc > LISTEN_INTERVAL) {
+		/* AP DTIM to big for our Listen Interval : no dtim skiping */
+		bcn_li_dtim = 1;
+		DHD_ERROR(("%s DTIM=%d > Listen=%d : too big ...\n", \
+				 __FUNCTION__, dtim_assoc, LISTEN_INTERVAL));
+		goto exit;
+	}
+
+	if ((bcn_li_dtim * dtim_assoc) > LISTEN_INTERVAL) {
+		/* Round up dtim_skip to fit into STAs Listen Interval */
+		bcn_li_dtim = (int)(LISTEN_INTERVAL / dtim_assoc);
+		DHD_TRACE(("%s agjust dtim_skip as %d\n", __FUNCTION__, bcn_li_dtim));
+	}
+
+exit:
+	return bcn_li_dtim;
+}
+
+#ifdef PNO_SUPPORT
+int dhd_pno_clean(dhd_pub_t *dhd)
+{
+	char iovbuf[128];
+	int pfn_enabled = 0;
+	int iov_len = 0;
+	int ret;
+
+	/* Disable pfn */
+	iov_len = bcm_mkiovar("pfn", (char *)&pfn_enabled, 4, iovbuf, sizeof(iovbuf));
+	if ((ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf))) >= 0) {
+		/* clear pfn */
+		iov_len = bcm_mkiovar("pfnclear", 0, 0, iovbuf, sizeof(iovbuf));
+		if (iov_len) {
+			if ((ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, iov_len)) < 0) {
+				DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret));
+			}
+		}
+		else {
+			ret = -1;
+			DHD_ERROR(("%s failed code %d\n", __FUNCTION__, iov_len));
+		}
+	}
+	else
+		DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret));
+
+	return ret;
+}
+
+int dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled)
+{
+	char iovbuf[128];
+	int ret = -1;
+
+	if ((!dhd) && ((pfn_enabled != 0) || (pfn_enabled != 1))) {
+		DHD_ERROR(("%s error exit\n", __FUNCTION__));
+		return ret;
+	}
+
+	memset(iovbuf, 0, sizeof(iovbuf));
+
+	/* Check if disassoc to enable pno */
+	if (pfn_enabled && (is_associated(dhd, NULL) == TRUE)) {
+		DHD_ERROR(("%s pno enable called in assoc mode ret=%d\n", \
+			__FUNCTION__, ret));
+		return ret;
+	}
+
+	/* Enable/disable PNO */
+	if ((ret = bcm_mkiovar("pfn", (char *)&pfn_enabled, 4, iovbuf, sizeof(iovbuf))) > 0) {
+		if ((ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf))) < 0) {
+			DHD_ERROR(("%s failed for error=%d\n", __FUNCTION__, ret));
+			return ret;
+		}
+		else {
+			dhd->pno_enable = pfn_enabled;
+			DHD_TRACE(("%s set pno as %d\n", __FUNCTION__, dhd->pno_enable));
+		}
+	}
+	else DHD_ERROR(("%s failed err=%d\n", __FUNCTION__, ret));
+
+	return ret;
+}
+
+/* Function to execute combined scan */
+int
+dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, ushort scan_fr, \
+			int pno_repeat, int pno_freq_expo_max)
+{
+	int err = -1;
+	char iovbuf[128];
+	int k, i;
+	wl_pfn_param_t pfn_param;
+	wl_pfn_t	pfn_element;
+
+	DHD_TRACE(("%s nssid=%d nchan=%d\n", __FUNCTION__, nssid, scan_fr));
+
+	if ((!dhd) && (!ssids_local)) {
+		DHD_ERROR(("%s error exit\n", __FUNCTION__));
+		err = -1;
+	}
+
+	/* Check for broadcast ssid */
+	for (k = 0; k < nssid; k++) {
+		if (!ssids_local[k].SSID_len) {
+			DHD_ERROR(("%d: Broadcast SSID is ilegal for PNO setting\n", k));
+			return err;
+		}
+	}
+/* #define  PNO_DUMP 1 */
+#ifdef PNO_DUMP
+	{
+		int j;
+		for (j = 0; j < nssid; j++) {
+			DHD_ERROR(("%d: scan  for  %s size =%d\n", j,
+				ssids_local[j].SSID, ssids_local[j].SSID_len));
+		}
+	}
+#endif /* PNO_DUMP */
+
+	/* clean up everything */
+	if  ((err = dhd_pno_clean(dhd)) < 0) {
+		DHD_ERROR(("%s failed error=%d\n", __FUNCTION__, err));
+		return err;
+	}
+	memset(&pfn_param, 0, sizeof(pfn_param));
+	memset(&pfn_element, 0, sizeof(pfn_element));
+
+	/* set pfn parameters */
+	pfn_param.version = htod32(PFN_VERSION);
+	pfn_param.flags = htod16((PFN_LIST_ORDER << SORT_CRITERIA_BIT));
+
+	/* check and set extra pno params */
+	if ((pno_repeat != 0) || (pno_freq_expo_max != 0)) {
+		pfn_param.flags |= htod16(ENABLE << ENABLE_ADAPTSCAN_BIT);
+		pfn_param.repeat_scan = htod32(pno_repeat);
+		pfn_param.max_freq_adjust = htod32(pno_freq_expo_max);
+	}
+
+	/* set up pno scan fr */
+	if (scan_fr  != 0)
+		pfn_param.scan_freq = htod32(scan_fr);
+
+	if (pfn_param.scan_freq > PNO_SCAN_MAX_FW_SEC) {
+		DHD_ERROR(("%s pno freq above %d sec\n", __FUNCTION__, PNO_SCAN_MAX_FW_SEC));
+		return err;
+	}
+	if (pfn_param.scan_freq < PNO_SCAN_MIN_FW_SEC) {
+		DHD_ERROR(("%s pno freq less %d sec\n", __FUNCTION__, PNO_SCAN_MIN_FW_SEC));
+		return err;
+	}
+
+	bcm_mkiovar("pfn_set", (char *)&pfn_param, sizeof(pfn_param), iovbuf, sizeof(iovbuf));
+	dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
+
+	/* set all pfn ssid */
+	for (i = 0; i < nssid; i++) {
+
+		pfn_element.bss_type = htod32(DOT11_BSSTYPE_INFRASTRUCTURE);
+		pfn_element.auth = (DOT11_OPEN_SYSTEM);
+		pfn_element.infra = htod32(1);
+
+		memcpy((char *)pfn_element.ssid.SSID, ssids_local[i].SSID, ssids_local[i].SSID_len);
+		pfn_element.ssid.SSID_len = ssids_local[i].SSID_len;
+
+		if ((err =
+		bcm_mkiovar("pfn_add", (char *)&pfn_element,
+			sizeof(pfn_element), iovbuf, sizeof(iovbuf))) > 0) {
+			if ((err =
+			dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf))) < 0) {
+				DHD_ERROR(("%s failed for i=%d error=%d\n",
+					__FUNCTION__, i, err));
+				return err;
+			}
+			else
+				DHD_ERROR(("%s set OK with PNO time=%d repeat=%d max_adjust=%d\n", \
+					__FUNCTION__, pfn_param.scan_freq, \
+					pfn_param.repeat_scan, pfn_param.max_freq_adjust));
+		}
+		else DHD_ERROR(("%s failed err=%d\n", __FUNCTION__, err));
+	}
+
+	/* Enable PNO */
+	/* dhd_pno_enable(dhd, 1); */
+	return err;
+}
+
+int dhd_pno_get_status(dhd_pub_t *dhd)
+{
+	int ret = -1;
+
+	if (!dhd)
+		return ret;
+	else
+		return (dhd->pno_enable);
+}
+
+#endif /* PNO_SUPPORT */
+
+#if defined(KEEP_ALIVE)
+int dhd_keep_alive_onoff(dhd_pub_t *dhd, int ka_on)
+{
+	char buf[256];
+	char *buf_ptr = buf;
+	wl_keep_alive_pkt_t keep_alive_pkt;
+	char * str;
+	int str_len, buf_len;
+	int res = 0;
+	int keep_alive_period = KEEP_ALIVE_PERIOD; /* in ms */
+
+	DHD_TRACE(("%s: ka:%d\n", __FUNCTION__, ka_on));
+
+	if (ka_on) { /* on suspend */
+		keep_alive_pkt.period_msec = keep_alive_period;
+
+	} else {
+		/* on resume, turn off keep_alive packets  */
+		keep_alive_pkt.period_msec = 0;
+	}
+
+	/* IOC var name  */
+	str = "keep_alive";
+	str_len = strlen(str);
+	strncpy(buf, str, str_len);
+	buf[str_len] = '\0';
+	buf_len = str_len + 1;
+
+	/* set ptr to IOCTL payload after the var name */
+	buf_ptr += buf_len; /* include term Z */
+
+	/* copy Keep-alive attributes from local var keep_alive_pkt */
+	str = NULL_PKT_STR;
+	keep_alive_pkt.len_bytes = strlen(str);
+
+	memcpy(buf_ptr, &keep_alive_pkt, WL_KEEP_ALIVE_FIXED_LEN);
+	buf_ptr += WL_KEEP_ALIVE_FIXED_LEN;
+
+	/* copy packet data */
+	memcpy(buf_ptr, str, keep_alive_pkt.len_bytes);
+	buf_len += (WL_KEEP_ALIVE_FIXED_LEN + keep_alive_pkt.len_bytes);
+
+	res = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, buf, buf_len);
+	return res;
+}
+#endif /* defined(KEEP_ALIVE) */
+
+#if defined(CSCAN)
+
+/* Androd ComboSCAN support */
+/*
+ *  data parsing from ComboScan tlv list
+*/
+int
+wl_iw_parse_data_tlv(char** list_str, void *dst, int dst_size, const char token,
+                     int input_size, int *bytes_left)
+{
+	char* str = *list_str;
+	uint16 short_temp;
+	uint32 int_temp;
+
+	if ((list_str == NULL) || (*list_str == NULL) ||(bytes_left == NULL) || (*bytes_left < 0)) {
+		DHD_ERROR(("%s error paramters\n", __FUNCTION__));
+		return -1;
+	}
+
+	/* Clean all dest bytes */
+	memset(dst, 0, dst_size);
+	while (*bytes_left > 0) {
+
+		if (str[0] != token) {
+			DHD_TRACE(("%s NOT Type=%d get=%d left_parse=%d \n",
+				__FUNCTION__, token, str[0], *bytes_left));
+			return -1;
+		}
+
+		*bytes_left -= 1;
+		str += 1;
+
+		if (input_size == 1) {
+			memcpy(dst, str, input_size);
+		}
+		else if (input_size == 2) {
+			memcpy(dst, (char *)htod16(memcpy(&short_temp, str, input_size)),
+				input_size);
+		}
+		else if (input_size == 4) {
+			memcpy(dst, (char *)htod32(memcpy(&int_temp, str, input_size)),
+				input_size);
+		}
+
+		*bytes_left -= input_size;
+		str += input_size;
+		*list_str = str;
+		return 1;
+	}
+	return 1;
+}
+
+/*
+ *  channel list parsing from cscan tlv list
+*/
+int
+wl_iw_parse_channel_list_tlv(char** list_str, uint16* channel_list,
+                             int channel_num, int *bytes_left)
+{
+	char* str = *list_str;
+	int idx = 0;
+
+	if ((list_str == NULL) || (*list_str == NULL) ||(bytes_left == NULL) || (*bytes_left < 0)) {
+		DHD_ERROR(("%s error paramters\n", __FUNCTION__));
+		return -1;
+	}
+
+	while (*bytes_left > 0) {
+
+		if (str[0] != CSCAN_TLV_TYPE_CHANNEL_IE) {
+			*list_str = str;
+			DHD_TRACE(("End channel=%d left_parse=%d %d\n", idx, *bytes_left, str[0]));
+			return idx;
+		}
+		/* Get proper CSCAN_TLV_TYPE_CHANNEL_IE */
+		*bytes_left -= 1;
+		str += 1;
+
+		if (str[0] == 0) {
+			/* All channels */
+			channel_list[idx] = 0x0;
+		}
+		else {
+			channel_list[idx] = (uint16)str[0];
+			DHD_TRACE(("%s channel=%d \n", __FUNCTION__,  channel_list[idx]));
+		}
+		*bytes_left -= 1;
+		str += 1;
+
+		if (idx++ > 255) {
+			DHD_ERROR(("%s Too many channels \n", __FUNCTION__));
+			return -1;
+		}
+	}
+
+	*list_str = str;
+	return idx;
+}
+
+/*
+ *  SSIDs list parsing from cscan tlv list
+ */
+int
+wl_iw_parse_ssid_list_tlv(char** list_str, wlc_ssid_t* ssid, int max, int *bytes_left)
+{
+	char* str =  *list_str;
+	int idx = 0;
+
+	if ((list_str == NULL) || (*list_str == NULL) || (*bytes_left < 0)) {
+		DHD_ERROR(("%s error paramters\n", __FUNCTION__));
+		return -1;
+	}
+
+	while (*bytes_left > 0) {
+
+		if (str[0] != CSCAN_TLV_TYPE_SSID_IE) {
+			*list_str = str;
+			DHD_TRACE(("nssid=%d left_parse=%d %d\n", idx, *bytes_left, str[0]));
+			return idx;
+		}
+
+		/* Get proper CSCAN_TLV_TYPE_SSID_IE */
+		*bytes_left -= 1;
+		str += 1;
+
+		if (str[0] == 0) {
+			/* Broadcast SSID */
+			ssid[idx].SSID_len = 0;
+			memset((char*)ssid[idx].SSID, 0x0, DOT11_MAX_SSID_LEN);
+			*bytes_left -= 1;
+			str += 1;
+
+			DHD_TRACE(("BROADCAST SCAN  left=%d\n", *bytes_left));
+		}
+		else if (str[0] <= DOT11_MAX_SSID_LEN) {
+			/* Get proper SSID size */
+			ssid[idx].SSID_len = str[0];
+			*bytes_left -= 1;
+			str += 1;
+
+			/* Get SSID */
+			if (ssid[idx].SSID_len > *bytes_left) {
+				DHD_ERROR(("%s out of memory range len=%d but left=%d\n",
+				__FUNCTION__, ssid[idx].SSID_len, *bytes_left));
+				return -1;
+			}
+
+			memcpy((char*)ssid[idx].SSID, str, ssid[idx].SSID_len);
+
+			*bytes_left -= ssid[idx].SSID_len;
+			str += ssid[idx].SSID_len;
+
+			DHD_TRACE(("%s :size=%d left=%d\n",
+				(char*)ssid[idx].SSID, ssid[idx].SSID_len, *bytes_left));
+		}
+		else {
+			DHD_ERROR(("### SSID size more that %d\n", str[0]));
+			return -1;
+		}
+
+		if (idx++ >  max) {
+			DHD_ERROR(("%s number of SSIDs more that %d\n", __FUNCTION__, idx));
+			return -1;
+		}
+	}
+
+	*list_str = str;
+	return idx;
+}
+
+/* Parse a comma-separated list from list_str into ssid array, starting
+ * at index idx.  Max specifies size of the ssid array.  Parses ssids
+ * and returns updated idx; if idx >= max not all fit, the excess have
+ * not been copied.  Returns -1 on empty string, or on ssid too long.
+ */
+int
+wl_iw_parse_ssid_list(char** list_str, wlc_ssid_t* ssid, int idx, int max)
+{
+	char* str, *ptr;
+
+	if ((list_str == NULL) || (*list_str == NULL))
+		return -1;
+
+	for (str = *list_str; str != NULL; str = ptr) {
+
+		/* check for next TAG */
+		if (!strncmp(str, GET_CHANNEL, strlen(GET_CHANNEL))) {
+			*list_str	 = str + strlen(GET_CHANNEL);
+			return idx;
+		}
+
+		if ((ptr = strchr(str, ',')) != NULL) {
+			*ptr++ = '\0';
+		}
+
+		if (strlen(str) > DOT11_MAX_SSID_LEN) {
+			DHD_ERROR(("ssid <%s> exceeds %d\n", str, DOT11_MAX_SSID_LEN));
+			return -1;
+		}
+
+		if (strlen(str) == 0)
+			ssid[idx].SSID_len = 0;
+
+		if (idx < max) {
+			strcpy((char*)ssid[idx].SSID, str);
+			ssid[idx].SSID_len = strlen(str);
+		}
+		idx++;
+	}
+	return idx;
+}
+
+/*
+ * Parse channel list from iwpriv CSCAN
+ */
+int
+wl_iw_parse_channel_list(char** list_str, uint16* channel_list, int channel_num)
+{
+	int num;
+	int val;
+	char* str;
+	char* endptr = NULL;
+
+	if ((list_str == NULL)||(*list_str == NULL))
+		return -1;
+
+	str = *list_str;
+	num = 0;
+	while (strncmp(str, GET_NPROBE, strlen(GET_NPROBE))) {
+		val = (int)strtoul(str, &endptr, 0);
+		if (endptr == str) {
+			printf("could not parse channel number starting at"
+				" substring \"%s\" in list:\n%s\n",
+				str, *list_str);
+			return -1;
+		}
+		str = endptr + strspn(endptr, " ,");
+
+		if (num == channel_num) {
+			DHD_ERROR(("too many channels (more than %d) in channel list:\n%s\n",
+				channel_num, *list_str));
+			return -1;
+		}
+
+		channel_list[num++] = (uint16)val;
+	}
+	*list_str = str;
+	return num;
+}
+
+#endif
diff --git a/drivers/net/wireless/bcm4329/dhd_custom_gpio.c b/drivers/net/wireless/bcm4329/dhd_custom_gpio.c
new file mode 100644
index 0000000..4d32863
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/dhd_custom_gpio.c
@@ -0,0 +1,272 @@
+/*
+* Customer code to add GPIO control during WLAN start/stop
+* Copyright (C) 1999-2010, Broadcom Corporation
+* 
+*      Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2 (the "GPL"),
+* available at http://www.broadcom.com/licenses/GPLv2.php, with the
+* following added to such license:
+* 
+*      As a special exception, the copyright holders of this software give you
+* permission to link this software with independent modules, and to copy and
+* distribute the resulting executable under terms of your choice, provided that
+* you also meet, for each linked independent module, the terms and conditions of
+* the license of that module.  An independent module is a module which is not
+* derived from this software.  The special exception does not apply to any
+* modifications of the software.
+* 
+*      Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a license
+* other than the GPL, without Broadcom's express prior written consent.
+*
+* $Id: dhd_custom_gpio.c,v 1.1.4.8.4.4 2011/01/20 20:23:09 Exp $
+*/
+
+
+#include <typedefs.h>
+#include <linuxver.h>
+#include <osl.h>
+#include <bcmutils.h>
+
+#include <dngl_stats.h>
+#include <dhd.h>
+
+#include <wlioctl.h>
+#include <wl_iw.h>
+
+#define WL_ERROR(x) printf x
+#define WL_TRACE(x)
+
+#ifdef CUSTOMER_HW
+extern  void bcm_wlan_power_off(int);
+extern  void bcm_wlan_power_on(int);
+#endif /* CUSTOMER_HW */
+#ifdef CUSTOMER_HW2
+int wifi_set_carddetect(int on);
+int wifi_set_power(int on, unsigned long msec);
+int wifi_get_irq_number(unsigned long *irq_flags_ptr);
+int wifi_get_mac_addr(unsigned char *buf);
+void *wifi_get_country_code(char *ccode);
+#endif
+
+#if defined(OOB_INTR_ONLY)
+
+#if defined(BCMLXSDMMC)
+extern int sdioh_mmc_irq(int irq);
+#endif /* (BCMLXSDMMC)  */
+
+#ifdef CUSTOMER_HW3
+#include <mach/gpio.h>
+#endif
+
+/* Customer specific Host GPIO defintion  */
+static int dhd_oob_gpio_num = -1; /* GG 19 */
+
+module_param(dhd_oob_gpio_num, int, 0644);
+MODULE_PARM_DESC(dhd_oob_gpio_num, "DHD oob gpio number");
+
+int dhd_customer_oob_irq_map(unsigned long *irq_flags_ptr)
+{
+	int  host_oob_irq = 0;
+
+#ifdef CUSTOMER_HW2
+	host_oob_irq = wifi_get_irq_number(irq_flags_ptr);
+
+#else /* for NOT  CUSTOMER_HW2 */
+#if defined(CUSTOM_OOB_GPIO_NUM)
+	if (dhd_oob_gpio_num < 0) {
+		dhd_oob_gpio_num = CUSTOM_OOB_GPIO_NUM;
+	}
+#endif
+
+	if (dhd_oob_gpio_num < 0) {
+		WL_ERROR(("%s: ERROR customer specific Host GPIO is NOT defined \n",
+			__FUNCTION__));
+		return (dhd_oob_gpio_num);
+	}
+
+	WL_ERROR(("%s: customer specific Host GPIO number is (%d)\n",
+	         __FUNCTION__, dhd_oob_gpio_num));
+
+#if defined CUSTOMER_HW
+	host_oob_irq = MSM_GPIO_TO_INT(dhd_oob_gpio_num);
+#elif defined CUSTOMER_HW3
+	gpio_request(dhd_oob_gpio_num, "oob irq");
+	host_oob_irq = gpio_to_irq(dhd_oob_gpio_num);
+	gpio_direction_input(dhd_oob_gpio_num);
+#endif /* CUSTOMER_HW */
+#endif /* CUSTOMER_HW2 */
+
+	return (host_oob_irq);
+}
+#endif /* defined(OOB_INTR_ONLY) */
+
+/* Customer function to control hw specific wlan gpios */
+void
+dhd_customer_gpio_wlan_ctrl(int onoff)
+{
+	switch (onoff) {
+		case WLAN_RESET_OFF:
+			WL_TRACE(("%s: call customer specific GPIO to insert WLAN RESET\n",
+				__FUNCTION__));
+#ifdef CUSTOMER_HW
+			bcm_wlan_power_off(2);
+#endif /* CUSTOMER_HW */
+#ifdef CUSTOMER_HW2
+			wifi_set_power(0, 0);
+#endif
+			WL_ERROR(("=========== WLAN placed in RESET ========\n"));
+		break;
+
+		case WLAN_RESET_ON:
+			WL_TRACE(("%s: callc customer specific GPIO to remove WLAN RESET\n",
+				__FUNCTION__));
+#ifdef CUSTOMER_HW
+			bcm_wlan_power_on(2);
+#endif /* CUSTOMER_HW */
+#ifdef CUSTOMER_HW2
+			wifi_set_power(1, 0);
+#endif
+			WL_ERROR(("=========== WLAN going back to live  ========\n"));
+		break;
+
+		case WLAN_POWER_OFF:
+			WL_TRACE(("%s: call customer specific GPIO to turn off WL_REG_ON\n",
+				__FUNCTION__));
+#ifdef CUSTOMER_HW
+			bcm_wlan_power_off(1);
+#endif /* CUSTOMER_HW */
+		break;
+
+		case WLAN_POWER_ON:
+			WL_TRACE(("%s: call customer specific GPIO to turn on WL_REG_ON\n",
+				__FUNCTION__));
+#ifdef CUSTOMER_HW
+			bcm_wlan_power_on(1);
+			/* Lets customer power to get stable */
+			OSL_DELAY(50);
+#endif /* CUSTOMER_HW */
+		break;
+	}
+}
+
+#ifdef GET_CUSTOM_MAC_ENABLE
+/* Function to get custom MAC address */
+int
+dhd_custom_get_mac_address(unsigned char *buf)
+{
+	int ret = 0;
+
+	WL_TRACE(("%s Enter\n", __FUNCTION__));
+	if (!buf)
+		return -EINVAL;
+
+	/* Customer access to MAC address stored outside of DHD driver */
+#ifdef CUSTOMER_HW2
+	ret = wifi_get_mac_addr(buf);
+#endif
+
+#ifdef EXAMPLE_GET_MAC
+	/* EXAMPLE code */
+	{
+		struct ether_addr ea_example = {{0x00, 0x11, 0x22, 0x33, 0x44, 0xFF}};
+		bcopy((char *)&ea_example, buf, sizeof(struct ether_addr));
+	}
+#endif /* EXAMPLE_GET_MAC */
+
+	return ret;
+}
+#endif /* GET_CUSTOM_MAC_ENABLE */
+
+/* Customized Locale table : OPTIONAL feature */
+const struct cntry_locales_custom translate_custom_table[] = {
+/* Table should be filled out based on custom platform regulatory requirement */
+#ifdef EXAMPLE_TABLE
+	{"",   "XY", 4},  /* universal */
+	{"US", "US", 69}, /* input ISO "US" to : US regrev 69 */
+	{"CA", "US", 69}, /* input ISO "CA" to : US regrev 69 */
+	{"EU", "EU", 5},  /* European union countries */
+	{"AT", "EU", 5},
+	{"BE", "EU", 5},
+	{"BG", "EU", 5},
+	{"CY", "EU", 5},
+	{"CZ", "EU", 5},
+	{"DK", "EU", 5},
+	{"EE", "EU", 5},
+	{"FI", "EU", 5},
+	{"FR", "EU", 5},
+	{"DE", "EU", 5},
+	{"GR", "EU", 5},
+	{"HU", "EU", 5},
+	{"IE", "EU", 5},
+	{"IT", "EU", 5},
+	{"LV", "EU", 5},
+	{"LI", "EU", 5},
+	{"LT", "EU", 5},
+	{"LU", "EU", 5},
+	{"MT", "EU", 5},
+	{"NL", "EU", 5},
+	{"PL", "EU", 5},
+	{"PT", "EU", 5},
+	{"RO", "EU", 5},
+	{"SK", "EU", 5},
+	{"SI", "EU", 5},
+	{"ES", "EU", 5},
+	{"SE", "EU", 5},
+	{"GB", "EU", 5},  /* input ISO "GB" to : EU regrev 05 */
+	{"IL", "IL", 0},
+	{"CH", "CH", 0},
+	{"TR", "TR", 0},
+	{"NO", "NO", 0},
+	{"KR", "XY", 3},
+	{"AU", "XY", 3},
+	{"CN", "XY", 3},  /* input ISO "CN" to : XY regrev 03 */
+	{"TW", "XY", 3},
+	{"AR", "XY", 3},
+	{"MX", "XY", 3}
+#endif /* EXAMPLE_TABLE */
+};
+
+
+/* Customized Locale convertor
+*  input : ISO 3166-1 country abbreviation
+*  output: customized cspec
+*/
+void get_customized_country_code(char *country_iso_code, wl_country_t *cspec)
+{
+#ifdef CUSTOMER_HW2
+	struct cntry_locales_custom *cloc_ptr;
+
+	if (!cspec)
+		return;
+
+	cloc_ptr = wifi_get_country_code(country_iso_code);
+	if (cloc_ptr) {
+		strlcpy(cspec->ccode, cloc_ptr->custom_locale, WLC_CNTRY_BUF_SZ);
+		cspec->rev = cloc_ptr->custom_locale_rev;
+	}
+	return;
+#else
+	int size, i;
+
+	size = ARRAYSIZE(translate_custom_table);
+
+	if (cspec == 0)
+		return;
+
+	if (size == 0)
+		return;
+
+	for (i = 0; i < size; i++) {
+		if (strcmp(country_iso_code, translate_custom_table[i].iso_abbrev) == 0) {
+			memcpy(cspec->ccode, translate_custom_table[i].custom_locale, WLC_CNTRY_BUF_SZ);
+			cspec->rev = translate_custom_table[i].custom_locale_rev;
+			return;
+		}
+	}
+	memcpy(cspec->ccode, translate_custom_table[0].custom_locale, WLC_CNTRY_BUF_SZ);
+	cspec->rev = translate_custom_table[0].custom_locale_rev;
+	return;
+#endif
+}
diff --git a/drivers/net/wireless/bcm4329/dhd_dbg.h b/drivers/net/wireless/bcm4329/dhd_dbg.h
new file mode 100644
index 0000000..b48c1d7
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/dhd_dbg.h
@@ -0,0 +1,100 @@
+/*
+ * Debug/trace/assert driver definitions for Dongle Host Driver.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: dhd_dbg.h,v 1.5.6.2.4.2.14.10 2010/05/21 21:49:38 Exp $
+ */
+
+#ifndef _dhd_dbg_
+#define _dhd_dbg_
+
+#ifdef DHD_DEBUG
+
+#define DHD_ERROR(args)	       do {if ((dhd_msg_level & DHD_ERROR_VAL) && (net_ratelimit())) \
+								printf args;} while (0)
+#define DHD_TRACE(args)		do {if (dhd_msg_level & DHD_TRACE_VAL) printf args;} while (0)
+#define DHD_INFO(args)		do {if (dhd_msg_level & DHD_INFO_VAL) printf args;} while (0)
+#define DHD_DATA(args)		do {if (dhd_msg_level & DHD_DATA_VAL) printf args;} while (0)
+#define DHD_CTL(args)		do {if (dhd_msg_level & DHD_CTL_VAL) printf args;} while (0)
+#define DHD_TIMER(args)		do {if (dhd_msg_level & DHD_TIMER_VAL) printf args;} while (0)
+#define DHD_HDRS(args)		do {if (dhd_msg_level & DHD_HDRS_VAL) printf args;} while (0)
+#define DHD_BYTES(args)		do {if (dhd_msg_level & DHD_BYTES_VAL) printf args;} while (0)
+#define DHD_INTR(args)		do {if (dhd_msg_level & DHD_INTR_VAL) printf args;} while (0)
+#define DHD_GLOM(args)		do {if (dhd_msg_level & DHD_GLOM_VAL) printf args;} while (0)
+#define DHD_EVENT(args)		do {if (dhd_msg_level & DHD_EVENT_VAL) printf args;} while (0)
+#define DHD_BTA(args)		do {if (dhd_msg_level & DHD_BTA_VAL) printf args;} while (0)
+#define DHD_ISCAN(args)		do {if (dhd_msg_level & DHD_ISCAN_VAL) printf args;} while (0)
+
+#define DHD_ERROR_ON()		(dhd_msg_level & DHD_ERROR_VAL)
+#define DHD_TRACE_ON()		(dhd_msg_level & DHD_TRACE_VAL)
+#define DHD_INFO_ON()		(dhd_msg_level & DHD_INFO_VAL)
+#define DHD_DATA_ON()		(dhd_msg_level & DHD_DATA_VAL)
+#define DHD_CTL_ON()		(dhd_msg_level & DHD_CTL_VAL)
+#define DHD_TIMER_ON()		(dhd_msg_level & DHD_TIMER_VAL)
+#define DHD_HDRS_ON()		(dhd_msg_level & DHD_HDRS_VAL)
+#define DHD_BYTES_ON()		(dhd_msg_level & DHD_BYTES_VAL)
+#define DHD_INTR_ON()		(dhd_msg_level & DHD_INTR_VAL)
+#define DHD_GLOM_ON()		(dhd_msg_level & DHD_GLOM_VAL)
+#define DHD_EVENT_ON()		(dhd_msg_level & DHD_EVENT_VAL)
+#define DHD_BTA_ON()		(dhd_msg_level & DHD_BTA_VAL)
+#define DHD_ISCAN_ON()		(dhd_msg_level & DHD_ISCAN_VAL)
+
+#else /* DHD_DEBUG */
+
+#define DHD_ERROR(args)    	do {if (net_ratelimit()) printf args;} while (0)
+#define DHD_TRACE(args)
+#define DHD_INFO(args)
+#define DHD_DATA(args)
+#define DHD_CTL(args)
+#define DHD_TIMER(args)
+#define DHD_HDRS(args)
+#define DHD_BYTES(args)
+#define DHD_INTR(args)
+#define DHD_GLOM(args)
+#define DHD_EVENT(args)
+#define DHD_BTA(args)
+#define DHD_ISCAN(args)
+
+#define DHD_ERROR_ON()		0
+#define DHD_TRACE_ON()		0
+#define DHD_INFO_ON()		0
+#define DHD_DATA_ON()		0
+#define DHD_CTL_ON()		0
+#define DHD_TIMER_ON()		0
+#define DHD_HDRS_ON()		0
+#define DHD_BYTES_ON()		0
+#define DHD_INTR_ON()		0
+#define DHD_GLOM_ON()		0
+#define DHD_EVENT_ON()		0
+#define DHD_BTA_ON()		0
+#define DHD_ISCAN_ON()		0
+#endif /* DHD_DEBUG */
+
+#define DHD_LOG(args)
+
+#define DHD_NONE(args)
+extern int dhd_msg_level;
+
+/* Defines msg bits */
+#include <dhdioctl.h>
+
+#endif /* _dhd_dbg_ */
diff --git a/drivers/net/wireless/bcm4329/dhd_linux.c b/drivers/net/wireless/bcm4329/dhd_linux.c
new file mode 100644
index 0000000..5f5b418
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/dhd_linux.c
@@ -0,0 +1,3442 @@
+/*
+ * Broadcom Dongle Host Driver (DHD), Linux-specific network interface
+ * Basically selected code segments from usb-cdc.c and usb-rndis.c
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: dhd_linux.c,v 1.65.4.9.2.12.2.104.4.40 2011/02/03 19:55:18 Exp $
+ */
+
+#include <typedefs.h>
+#include <linuxver.h>
+#include <osl.h>
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/random.h>
+#include <linux/spinlock.h>
+#include <linux/ethtool.h>
+#include <linux/fcntl.h>
+#include <linux/fs.h>
+#include <linux/inetdevice.h>
+#include <linux/mutex.h>
+
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+
+#include <epivers.h>
+#include <bcmutils.h>
+#include <bcmendian.h>
+
+#include <proto/ethernet.h>
+#include <dngl_stats.h>
+#include <dhd.h>
+#include <dhd_bus.h>
+#include <dhd_proto.h>
+#include <dhd_dbg.h>
+#include <wl_iw.h>
+#ifdef CONFIG_HAS_WAKELOCK
+#include <linux/wakelock.h>
+#endif
+#ifdef CUSTOMER_HW2
+#include <linux/platform_device.h>
+#ifdef CONFIG_WIFI_CONTROL_FUNC
+#include <linux/wlan_plat.h>
+static struct wifi_platform_data *wifi_control_data = NULL;
+#endif
+struct semaphore wifi_control_sem;
+
+static struct resource *wifi_irqres = NULL;
+
+int wifi_get_irq_number(unsigned long *irq_flags_ptr)
+{
+	if (wifi_irqres) {
+		*irq_flags_ptr = wifi_irqres->flags & IRQF_TRIGGER_MASK;
+		return (int)wifi_irqres->start;
+	}
+#ifdef CUSTOM_OOB_GPIO_NUM
+	return CUSTOM_OOB_GPIO_NUM;
+#else
+	return -1;
+#endif
+}
+
+int wifi_set_carddetect(int on)
+{
+	printk("%s = %d\n", __FUNCTION__, on);
+#ifdef CONFIG_WIFI_CONTROL_FUNC
+	if (wifi_control_data && wifi_control_data->set_carddetect) {
+		wifi_control_data->set_carddetect(on);
+	}
+#endif
+	return 0;
+}
+
+int wifi_set_power(int on, unsigned long msec)
+{
+	printk("%s = %d\n", __FUNCTION__, on);
+#ifdef CONFIG_WIFI_CONTROL_FUNC
+	if (wifi_control_data && wifi_control_data->set_power) {
+		wifi_control_data->set_power(on);
+	}
+#endif
+	if (msec)
+		mdelay(msec);
+	return 0;
+}
+
+int wifi_set_reset(int on, unsigned long msec)
+{
+	DHD_TRACE(("%s = %d\n", __FUNCTION__, on));
+#ifdef CONFIG_WIFI_CONTROL_FUNC
+	if (wifi_control_data && wifi_control_data->set_reset) {
+		wifi_control_data->set_reset(on);
+	}
+#endif
+	if (msec)
+		mdelay(msec);
+	return 0;
+}
+
+int wifi_get_mac_addr(unsigned char *buf)
+{
+	DHD_TRACE(("%s\n", __FUNCTION__));
+	if (!buf)
+		return -EINVAL;
+#ifdef CONFIG_WIFI_CONTROL_FUNC
+	if (wifi_control_data && wifi_control_data->get_mac_addr) {
+		return wifi_control_data->get_mac_addr(buf);
+	}
+#endif
+	return -EOPNOTSUPP;
+}
+
+void *wifi_get_country_code(char *ccode)
+{
+	DHD_TRACE(("%s\n", __FUNCTION__));
+#ifdef CONFIG_WIFI_CONTROL_FUNC
+	if (!ccode)
+		return NULL;
+	if (wifi_control_data && wifi_control_data->get_country_code) {
+		return wifi_control_data->get_country_code(ccode);
+	}
+#endif
+	return NULL;
+}
+
+static int wifi_probe(struct platform_device *pdev)
+{
+#ifdef CONFIG_WIFI_CONTROL_FUNC
+	struct wifi_platform_data *wifi_ctrl =
+		(struct wifi_platform_data *)(pdev->dev.platform_data);
+
+	wifi_control_data = wifi_ctrl;
+#endif
+
+	DHD_TRACE(("## %s\n", __FUNCTION__));
+	wifi_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "bcm4329_wlan_irq");
+
+	wifi_set_power(1, 0);	/* Power On */
+	wifi_set_carddetect(1);	/* CardDetect (0->1) */
+
+	up(&wifi_control_sem);
+	return 0;
+}
+
+static int wifi_remove(struct platform_device *pdev)
+{
+#ifdef CONFIG_WIFI_CONTROL_FUNC
+	struct wifi_platform_data *wifi_ctrl =
+		(struct wifi_platform_data *)(pdev->dev.platform_data);
+
+	wifi_control_data = wifi_ctrl;
+#endif
+	DHD_TRACE(("## %s\n", __FUNCTION__));
+	wifi_set_power(0, 0);	/* Power Off */
+	wifi_set_carddetect(0);	/* CardDetect (1->0) */
+
+	up(&wifi_control_sem);
+	return 0;
+}
+
+static int wifi_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	DHD_TRACE(("##> %s\n", __FUNCTION__));
+#if defined(OOB_INTR_ONLY)
+	bcmsdh_oob_intr_set(0);
+#endif /* (OOB_INTR_ONLY) */
+	return 0;
+}
+static int wifi_resume(struct platform_device *pdev)
+{
+	DHD_TRACE(("##> %s\n", __FUNCTION__));
+#if defined(OOB_INTR_ONLY)
+	bcmsdh_oob_intr_set(1);
+#endif /* (OOB_INTR_ONLY) */
+	return 0;
+}
+
+static struct platform_driver wifi_device = {
+	.probe          = wifi_probe,
+	.remove         = wifi_remove,
+	.suspend        = wifi_suspend,
+	.resume         = wifi_resume,
+	.driver         = {
+	.name   = "bcm4329_wlan",
+	}
+};
+
+int wifi_add_dev(void)
+{
+	DHD_TRACE(("## Calling platform_driver_register\n"));
+	return platform_driver_register(&wifi_device);
+}
+
+void wifi_del_dev(void)
+{
+	DHD_TRACE(("## Unregister platform_driver_register\n"));
+	platform_driver_unregister(&wifi_device);
+}
+#endif /* defined(CUSTOMER_HW2) */
+
+static int dhd_device_event(struct notifier_block *this, unsigned long event,
+				void *ptr);
+
+static struct notifier_block dhd_notifier = {
+	.notifier_call = dhd_device_event
+};
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP)
+#include <linux/suspend.h>
+volatile bool dhd_mmc_suspend = FALSE;
+DECLARE_WAIT_QUEUE_HEAD(dhd_dpc_wait);
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */
+
+#if defined(OOB_INTR_ONLY)
+extern void dhd_enable_oob_intr(struct dhd_bus *bus, bool enable);
+#endif /* defined(OOB_INTR_ONLY) */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+MODULE_LICENSE("GPL v2");
+#endif /* LinuxVer */
+
+#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 15)
+const char *
+print_tainted()
+{
+	return "";
+}
+#endif	/* LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 15) */
+
+/* Linux wireless extension support */
+#if defined(CONFIG_WIRELESS_EXT)
+#include <wl_iw.h>
+#endif /* defined(CONFIG_WIRELESS_EXT) */
+
+extern int dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len);
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+#include <linux/earlysuspend.h>
+#endif /* defined(CONFIG_HAS_EARLYSUSPEND) */
+
+#ifdef PKT_FILTER_SUPPORT
+extern void dhd_pktfilter_offload_set(dhd_pub_t * dhd, char *arg);
+extern void dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode);
+#endif
+
+/* Interface control information */
+typedef struct dhd_if {
+	struct dhd_info *info;			/* back pointer to dhd_info */
+	/* OS/stack specifics */
+	struct net_device *net;
+	struct net_device_stats stats;
+	int 			idx;			/* iface idx in dongle */
+	int 			state;			/* interface state */
+	uint 			subunit;		/* subunit */
+	uint8			mac_addr[ETHER_ADDR_LEN];	/* assigned MAC address */
+	bool			attached;		/* Delayed attachment when unset */
+	bool			txflowcontrol;	/* Per interface flow control indicator */
+	char			name[IFNAMSIZ+1]; /* linux interface name */
+} dhd_if_t;
+
+/* Local private structure (extension of pub) */
+typedef struct dhd_info {
+#if defined(CONFIG_WIRELESS_EXT)
+	wl_iw_t		iw;		/* wireless extensions state (must be first) */
+#endif /* defined(CONFIG_WIRELESS_EXT) */
+
+	dhd_pub_t pub;
+
+	/* OS/stack specifics */
+	dhd_if_t *iflist[DHD_MAX_IFS];
+
+	struct mutex proto_sem;
+	wait_queue_head_t ioctl_resp_wait;
+	struct timer_list timer;
+	bool wd_timer_valid;
+	struct tasklet_struct tasklet;
+	spinlock_t	sdlock;
+	spinlock_t	txqlock;
+	spinlock_t	dhd_lock;
+
+	/* Thread based operation */
+	bool threads_only;
+	struct mutex sdsem;
+	long watchdog_pid;
+	struct semaphore watchdog_sem;
+	struct completion watchdog_exited;
+	long dpc_pid;
+	struct semaphore dpc_sem;
+	struct completion dpc_exited;
+
+	/* Wakelocks */
+#ifdef CONFIG_HAS_WAKELOCK
+	struct wake_lock wl_wifi;   /* Wifi wakelock */
+	struct wake_lock wl_rxwake; /* Wifi rx wakelock */
+#endif
+	spinlock_t wl_lock;
+	int wl_count;
+	int wl_packet;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25))
+	struct mutex wl_start_lock; /* mutex when START called to prevent any other Linux calls */
+#endif
+	/* Thread to issue ioctl for multicast */
+	long sysioc_pid;
+	struct semaphore sysioc_sem;
+	struct completion sysioc_exited;
+	bool set_multicast;
+	bool set_macaddress;
+	struct ether_addr macvalue;
+	wait_queue_head_t ctrl_wait;
+	atomic_t pend_8021x_cnt;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	struct early_suspend early_suspend;
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+} dhd_info_t;
+
+/* Definitions to provide path to the firmware and nvram
+ * example nvram_path[MOD_PARAM_PATHLEN]="/projects/wlan/nvram.txt"
+ */
+char firmware_path[MOD_PARAM_PATHLEN];
+char nvram_path[MOD_PARAM_PATHLEN];
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+struct semaphore dhd_registration_sem;
+#define DHD_REGISTRATION_TIMEOUT  12000  /* msec : allowed time to finished dhd registration */
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */
+/* load firmware and/or nvram values from the filesystem */
+module_param_string(firmware_path, firmware_path, MOD_PARAM_PATHLEN, 0);
+module_param_string(nvram_path, nvram_path, MOD_PARAM_PATHLEN, 0);
+
+/* Error bits */
+module_param(dhd_msg_level, int, 0);
+
+/* Spawn a thread for system ioctls (set mac, set mcast) */
+uint dhd_sysioc = TRUE;
+module_param(dhd_sysioc, uint, 0);
+
+/* Watchdog interval */
+uint dhd_watchdog_ms = 10;
+module_param(dhd_watchdog_ms, uint, 0);
+
+#ifdef DHD_DEBUG
+/* Console poll interval */
+uint dhd_console_ms = 0;
+module_param(dhd_console_ms, uint, 0);
+#endif /* DHD_DEBUG */
+
+/* ARP offload agent mode : Enable ARP Host Auto-Reply and ARP Peer Auto-Reply */
+uint dhd_arp_mode = 0xb;
+module_param(dhd_arp_mode, uint, 0);
+
+/* ARP offload enable */
+uint dhd_arp_enable = TRUE;
+module_param(dhd_arp_enable, uint, 0);
+
+/* Global Pkt filter enable control */
+uint dhd_pkt_filter_enable = TRUE;
+module_param(dhd_pkt_filter_enable, uint, 0);
+
+/*  Pkt filter init setup */
+uint dhd_pkt_filter_init = 0;
+module_param(dhd_pkt_filter_init, uint, 0);
+
+/* Pkt filter mode control */
+uint dhd_master_mode = TRUE;
+module_param(dhd_master_mode, uint, 1);
+
+/* Watchdog thread priority, -1 to use kernel timer */
+int dhd_watchdog_prio = 97;
+module_param(dhd_watchdog_prio, int, 0);
+
+/* DPC thread priority, -1 to use tasklet */
+int dhd_dpc_prio = 98;
+module_param(dhd_dpc_prio, int, 0);
+
+/* DPC thread priority, -1 to use tasklet */
+extern int dhd_dongle_memsize;
+module_param(dhd_dongle_memsize, int, 0);
+
+/* Control fw roaming */
+#ifdef CUSTOMER_HW2
+uint dhd_roam = 0;
+#else
+uint dhd_roam = 1;
+#endif
+
+/* Control radio state */
+uint dhd_radio_up = 1;
+
+/* Network inteface name */
+char iface_name[IFNAMSIZ];
+module_param_string(iface_name, iface_name, IFNAMSIZ, 0);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+#define DAEMONIZE(a) daemonize(a); \
+	allow_signal(SIGKILL); \
+	allow_signal(SIGTERM);
+#else /* Linux 2.4 (w/o preemption patch) */
+#define RAISE_RX_SOFTIRQ() \
+	cpu_raise_softirq(smp_processor_id(), NET_RX_SOFTIRQ)
+#define DAEMONIZE(a) daemonize(); \
+	do { if (a) \
+		strncpy(current->comm, a, MIN(sizeof(current->comm), (strlen(a) + 1))); \
+	} while (0);
+#endif /* LINUX_VERSION_CODE  */
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+#define BLOCKABLE()	(!in_atomic())
+#else
+#define BLOCKABLE()	(!in_interrupt())
+#endif
+
+/* The following are specific to the SDIO dongle */
+
+/* IOCTL response timeout */
+int dhd_ioctl_timeout_msec = IOCTL_RESP_TIMEOUT;
+
+/* Idle timeout for backplane clock */
+int dhd_idletime = DHD_IDLETIME_TICKS;
+module_param(dhd_idletime, int, 0);
+
+/* Use polling */
+uint dhd_poll = FALSE;
+module_param(dhd_poll, uint, 0);
+
+/* Use interrupts */
+uint dhd_intr = TRUE;
+module_param(dhd_intr, uint, 0);
+
+/* SDIO Drive Strength (in milliamps) */
+uint dhd_sdiod_drive_strength = 6;
+module_param(dhd_sdiod_drive_strength, uint, 0);
+
+/* Tx/Rx bounds */
+extern uint dhd_txbound;
+extern uint dhd_rxbound;
+module_param(dhd_txbound, uint, 0);
+module_param(dhd_rxbound, uint, 0);
+
+/* Deferred transmits */
+extern uint dhd_deferred_tx;
+module_param(dhd_deferred_tx, uint, 0);
+
+
+
+#ifdef SDTEST
+/* Echo packet generator (pkts/s) */
+uint dhd_pktgen = 0;
+module_param(dhd_pktgen, uint, 0);
+
+/* Echo packet len (0 => sawtooth, max 2040) */
+uint dhd_pktgen_len = 0;
+module_param(dhd_pktgen_len, uint, 0);
+#endif
+
+/* Version string to report */
+#ifdef DHD_DEBUG
+#ifndef SRCBASE
+#define SRCBASE        "drivers/net/wireless/bcm4329"
+#endif
+#define DHD_COMPILED "\nCompiled in " SRCBASE
+#else
+#define DHD_COMPILED
+#endif
+
+static char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR
+#ifdef DHD_DEBUG
+"\nCompiled in " SRCBASE " on " __DATE__ " at " __TIME__
+#endif
+;
+
+
+#if defined(CONFIG_WIRELESS_EXT)
+struct iw_statistics *dhd_get_wireless_stats(struct net_device *dev);
+#endif /* defined(CONFIG_WIRELESS_EXT) */
+
+static void dhd_dpc(ulong data);
+/* forward decl */
+extern int dhd_wait_pend8021x(struct net_device *dev);
+
+#ifdef TOE
+#ifndef BDC
+#error TOE requires BDC
+#endif /* !BDC */
+static int dhd_toe_get(dhd_info_t *dhd, int idx, uint32 *toe_ol);
+static int dhd_toe_set(dhd_info_t *dhd, int idx, uint32 toe_ol);
+#endif /* TOE */
+
+static int dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata,
+                             wl_event_msg_t *event_ptr, void **data_ptr);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP)
+static int dhd_sleep_pm_callback(struct notifier_block *nfb, unsigned long action, void *ignored)
+{
+	int ret = NOTIFY_DONE;
+
+	switch (action) {
+	case PM_HIBERNATION_PREPARE:
+	case PM_SUSPEND_PREPARE:
+		dhd_mmc_suspend = TRUE;
+		ret = NOTIFY_OK;
+		break;
+	case PM_POST_HIBERNATION:
+	case PM_POST_SUSPEND:
+		dhd_mmc_suspend = FALSE;
+		ret = NOTIFY_OK;
+		break;
+	}
+	smp_mb();
+	return ret;
+}
+
+static struct notifier_block dhd_sleep_pm_notifier = {
+	.notifier_call = dhd_sleep_pm_callback,
+	.priority = 0
+};
+extern int register_pm_notifier(struct notifier_block *nb);
+extern int unregister_pm_notifier(struct notifier_block *nb);
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */
+
+static void dhd_set_packet_filter(int value, dhd_pub_t *dhd)
+{
+#ifdef PKT_FILTER_SUPPORT
+	DHD_TRACE(("%s: %d\n", __FUNCTION__, value));
+	/* 1 - Enable packet filter, only allow unicast packet to send up */
+	/* 0 - Disable packet filter */
+	if (dhd_pkt_filter_enable) {
+		int i;
+
+		for (i = 0; i < dhd->pktfilter_count; i++) {
+			dhd_pktfilter_offload_set(dhd, dhd->pktfilter[i]);
+			dhd_pktfilter_offload_enable(dhd, dhd->pktfilter[i],
+					value, dhd_master_mode);
+		}
+	}
+#endif
+}
+
+
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+static int dhd_set_suspend(int value, dhd_pub_t *dhd)
+{
+	int power_mode = PM_MAX;
+	/* wl_pkt_filter_enable_t	enable_parm; */
+	char iovbuf[32];
+	int bcn_li_dtim = 3;
+#ifdef CUSTOMER_HW2
+	uint roamvar = 1;
+#endif /* CUSTOMER_HW2 */
+
+	DHD_TRACE(("%s: enter, value = %d in_suspend = %d\n",
+			__FUNCTION__, value, dhd->in_suspend));
+
+	if (dhd && dhd->up) {
+		if (value && dhd->in_suspend) {
+
+			/* Kernel suspended */
+			DHD_TRACE(("%s: force extra Suspend setting \n", __FUNCTION__));
+
+			dhdcdc_set_ioctl(dhd, 0, WLC_SET_PM,
+				(char *)&power_mode, sizeof(power_mode));
+
+			/* Enable packet filter, only allow unicast packet to send up */
+			dhd_set_packet_filter(1, dhd);
+
+			/* if dtim skip setup as default force it to wake each thrid dtim
+			 *  for better power saving.
+			 *  Note that side effect is chance to miss BC/MC packet
+			*/
+			bcn_li_dtim = dhd_get_dtim_skip(dhd);
+			bcm_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim,
+				4, iovbuf, sizeof(iovbuf));
+			dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
+#ifdef CUSTOMER_HW2
+			/* Disable build-in roaming during suspend */
+			bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, sizeof(iovbuf));
+			dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
+#endif /* CUSTOMER_HW2 */
+
+		} else {
+
+			/* Kernel resumed  */
+			DHD_TRACE(("%s: Remove extra suspend setting \n", __FUNCTION__));
+
+			power_mode = PM_FAST;
+			dhdcdc_set_ioctl(dhd, 0, WLC_SET_PM, (char *)&power_mode,
+				sizeof(power_mode));
+
+			/* disable pkt filter */
+			dhd_set_packet_filter(0, dhd);
+
+			/* restore pre-suspend setting for dtim_skip */
+			bcm_mkiovar("bcn_li_dtim", (char *)&dhd->dtim_skip,
+				4, iovbuf, sizeof(iovbuf));
+
+			dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
+#ifdef CUSTOMER_HW2
+			roamvar = dhd_roam;
+			bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, sizeof(iovbuf));
+			dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
+#endif /* CUSTOMER_HW2 */
+		}
+	}
+
+	return 0;
+}
+
+static void dhd_suspend_resume_helper(struct dhd_info *dhd, int val)
+{
+	dhd_pub_t *dhdp = &dhd->pub;
+
+	dhd_os_wake_lock(dhdp);
+	dhd_os_proto_block(dhdp);
+	/* Set flag when early suspend was called */
+	dhdp->in_suspend = val;
+	if (!dhdp->suspend_disable_flag)
+		dhd_set_suspend(val, dhdp);
+	dhd_os_proto_unblock(dhdp);
+	dhd_os_wake_unlock(dhdp);
+}
+
+static void dhd_early_suspend(struct early_suspend *h)
+{
+	struct dhd_info *dhd = container_of(h, struct dhd_info, early_suspend);
+
+	DHD_TRACE(("%s: enter\n", __FUNCTION__));
+
+	if (dhd)
+		dhd_suspend_resume_helper(dhd, 1);
+}
+
+static void dhd_late_resume(struct early_suspend *h)
+{
+	struct dhd_info *dhd = container_of(h, struct dhd_info, early_suspend);
+
+	DHD_TRACE(("%s: enter\n", __FUNCTION__));
+
+	if (dhd)
+		dhd_suspend_resume_helper(dhd, 0);
+}
+#endif /* defined(CONFIG_HAS_EARLYSUSPEND) */
+
+/*
+ * Generalized timeout mechanism.  Uses spin sleep with exponential back-off until
+ * the sleep time reaches one jiffy, then switches over to task delay.  Usage:
+ *
+ *      dhd_timeout_start(&tmo, usec);
+ *      while (!dhd_timeout_expired(&tmo))
+ *              if (poll_something())
+ *                      break;
+ *      if (dhd_timeout_expired(&tmo))
+ *              fatal();
+ */
+
+void
+dhd_timeout_start(dhd_timeout_t *tmo, uint usec)
+{
+	tmo->limit = usec;
+	tmo->increment = 0;
+	tmo->elapsed = 0;
+	tmo->tick = 1000000 / HZ;
+}
+
+int
+dhd_timeout_expired(dhd_timeout_t *tmo)
+{
+	/* Does nothing the first call */
+	if (tmo->increment == 0) {
+		tmo->increment = 1;
+		return 0;
+	}
+
+	if (tmo->elapsed >= tmo->limit)
+		return 1;
+
+	/* Add the delay that's about to take place */
+	tmo->elapsed += tmo->increment;
+
+	if (tmo->increment < tmo->tick) {
+		OSL_DELAY(tmo->increment);
+		tmo->increment *= 2;
+		if (tmo->increment > tmo->tick)
+			tmo->increment = tmo->tick;
+	} else {
+		wait_queue_head_t delay_wait;
+		DECLARE_WAITQUEUE(wait, current);
+		int pending;
+		init_waitqueue_head(&delay_wait);
+		add_wait_queue(&delay_wait, &wait);
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(1);
+		pending = signal_pending(current);
+		remove_wait_queue(&delay_wait, &wait);
+		set_current_state(TASK_RUNNING);
+		if (pending)
+			return 1;	/* Interrupted */
+	}
+
+	return 0;
+}
+
+static int
+dhd_net2idx(dhd_info_t *dhd, struct net_device *net)
+{
+	int i = 0;
+
+	ASSERT(dhd);
+	while (i < DHD_MAX_IFS) {
+		if (dhd->iflist[i] && (dhd->iflist[i]->net == net))
+			return i;
+		i++;
+	}
+
+	return DHD_BAD_IF;
+}
+
+int
+dhd_ifname2idx(dhd_info_t *dhd, char *name)
+{
+	int i = DHD_MAX_IFS;
+
+	ASSERT(dhd);
+
+	if (name == NULL || *name == '\0')
+		return 0;
+
+	while (--i > 0)
+		if (dhd->iflist[i] && !strncmp(dhd->iflist[i]->name, name, IFNAMSIZ))
+				break;
+
+	DHD_TRACE(("%s: return idx %d for \"%s\"\n", __FUNCTION__, i, name));
+
+	return i;	/* default - the primary interface */
+}
+
+char *
+dhd_ifname(dhd_pub_t *dhdp, int ifidx)
+{
+	dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
+
+	ASSERT(dhd);
+
+	if (ifidx < 0 || ifidx >= DHD_MAX_IFS) {
+		DHD_ERROR(("%s: ifidx %d out of range\n", __FUNCTION__, ifidx));
+		return "<if_bad>";
+	}
+
+	if (dhd->iflist[ifidx] == NULL) {
+		DHD_ERROR(("%s: null i/f %d\n", __FUNCTION__, ifidx));
+		return "<if_null>";
+	}
+
+	if (dhd->iflist[ifidx]->net)
+		return dhd->iflist[ifidx]->net->name;
+
+	return "<if_none>";
+}
+
+static void
+_dhd_set_multicast_list(dhd_info_t *dhd, int ifidx)
+{
+	struct net_device *dev;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
+	struct netdev_hw_addr *ha;
+#else
+	struct dev_mc_list *mclist;
+#endif
+	uint32 allmulti, cnt;
+
+	wl_ioctl_t ioc;
+	char *buf, *bufp;
+	uint buflen;
+	int ret;
+
+	ASSERT(dhd && dhd->iflist[ifidx]);
+	dev = dhd->iflist[ifidx]->net;
+
+	NETIF_ADDR_LOCK(dev);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
+	cnt = netdev_mc_count(dev);
+#else
+	cnt = dev->mc_count;
+#endif
+	NETIF_ADDR_UNLOCK(dev);
+
+	/* Determine initial value of allmulti flag */
+	allmulti = (dev->flags & IFF_ALLMULTI) ? TRUE : FALSE;
+
+	/* Send down the multicast list first. */
+	buflen = sizeof("mcast_list") + sizeof(cnt) + (cnt * ETHER_ADDR_LEN);
+	if (!(bufp = buf = MALLOC(dhd->pub.osh, buflen))) {
+		DHD_ERROR(("%s: out of memory for mcast_list, cnt %d\n",
+		           dhd_ifname(&dhd->pub, ifidx), cnt));
+		return;
+	}
+
+	strcpy(bufp, "mcast_list");
+	bufp += strlen("mcast_list") + 1;
+
+	cnt = htol32(cnt);
+	memcpy(bufp, &cnt, sizeof(cnt));
+	bufp += sizeof(cnt);
+
+	NETIF_ADDR_LOCK(dev);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
+	netdev_for_each_mc_addr(ha, dev) {
+		if (!cnt)
+			break;
+		memcpy(bufp, ha->addr, ETHER_ADDR_LEN);
+		bufp += ETHER_ADDR_LEN;
+		cnt--;
+	}
+#else
+	for (mclist = dev->mc_list; (mclist && (cnt > 0)); cnt--, mclist = mclist->next) {
+		memcpy(bufp, (void *)mclist->dmi_addr, ETHER_ADDR_LEN);
+		bufp += ETHER_ADDR_LEN;
+	}
+#endif
+	NETIF_ADDR_UNLOCK(dev);
+
+	memset(&ioc, 0, sizeof(ioc));
+	ioc.cmd = WLC_SET_VAR;
+	ioc.buf = buf;
+	ioc.len = buflen;
+	ioc.set = TRUE;
+
+	ret = dhd_prot_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
+	if (ret < 0) {
+		DHD_ERROR(("%s: set mcast_list failed, cnt %d\n",
+			dhd_ifname(&dhd->pub, ifidx), cnt));
+		allmulti = cnt ? TRUE : allmulti;
+	}
+
+	MFREE(dhd->pub.osh, buf, buflen);
+
+	/* Now send the allmulti setting.  This is based on the setting in the
+	 * net_device flags, but might be modified above to be turned on if we
+	 * were trying to set some addresses and dongle rejected it...
+	 */
+
+	buflen = sizeof("allmulti") + sizeof(allmulti);
+	if (!(buf = MALLOC(dhd->pub.osh, buflen))) {
+		DHD_ERROR(("%s: out of memory for allmulti\n", dhd_ifname(&dhd->pub, ifidx)));
+		return;
+	}
+	allmulti = htol32(allmulti);
+
+	if (!bcm_mkiovar("allmulti", (void*)&allmulti, sizeof(allmulti), buf, buflen)) {
+		DHD_ERROR(("%s: mkiovar failed for allmulti, datalen %d buflen %u\n",
+		           dhd_ifname(&dhd->pub, ifidx), (int)sizeof(allmulti), buflen));
+		MFREE(dhd->pub.osh, buf, buflen);
+		return;
+	}
+
+
+	memset(&ioc, 0, sizeof(ioc));
+	ioc.cmd = WLC_SET_VAR;
+	ioc.buf = buf;
+	ioc.len = buflen;
+	ioc.set = TRUE;
+
+	ret = dhd_prot_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
+	if (ret < 0) {
+		DHD_ERROR(("%s: set allmulti %d failed\n",
+		           dhd_ifname(&dhd->pub, ifidx), ltoh32(allmulti)));
+	}
+
+	MFREE(dhd->pub.osh, buf, buflen);
+
+	/* Finally, pick up the PROMISC flag as well, like the NIC driver does */
+
+	allmulti = (dev->flags & IFF_PROMISC) ? TRUE : FALSE;
+	allmulti = htol32(allmulti);
+
+	memset(&ioc, 0, sizeof(ioc));
+	ioc.cmd = WLC_SET_PROMISC;
+	ioc.buf = &allmulti;
+	ioc.len = sizeof(allmulti);
+	ioc.set = TRUE;
+
+	ret = dhd_prot_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
+	if (ret < 0) {
+		DHD_ERROR(("%s: set promisc %d failed\n",
+		           dhd_ifname(&dhd->pub, ifidx), ltoh32(allmulti)));
+	}
+}
+
+static int
+_dhd_set_mac_address(dhd_info_t *dhd, int ifidx, struct ether_addr *addr)
+{
+	char buf[32];
+	wl_ioctl_t ioc;
+	int ret;
+
+	DHD_TRACE(("%s enter\n", __FUNCTION__));
+	if (!bcm_mkiovar("cur_etheraddr", (char*)addr, ETHER_ADDR_LEN, buf, 32)) {
+		DHD_ERROR(("%s: mkiovar failed for cur_etheraddr\n", dhd_ifname(&dhd->pub, ifidx)));
+		return -1;
+	}
+	memset(&ioc, 0, sizeof(ioc));
+	ioc.cmd = WLC_SET_VAR;
+	ioc.buf = buf;
+	ioc.len = 32;
+	ioc.set = TRUE;
+
+	ret = dhd_prot_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
+	if (ret < 0) {
+		DHD_ERROR(("%s: set cur_etheraddr failed\n", dhd_ifname(&dhd->pub, ifidx)));
+	} else {
+		memcpy(dhd->iflist[ifidx]->net->dev_addr, addr, ETHER_ADDR_LEN);
+	}
+
+	return ret;
+}
+
+#ifdef SOFTAP
+extern struct net_device *ap_net_dev;
+/* semaphore that the soft AP CODE waits on */
+extern struct semaphore ap_eth_sema;
+#endif
+
+static void
+dhd_op_if(dhd_if_t *ifp)
+{
+	dhd_info_t *dhd;
+	int ret = 0, err = 0;
+#ifdef SOFTAP
+	unsigned long flags;
+#endif
+
+	ASSERT(ifp && ifp->info && ifp->idx);	/* Virtual interfaces only */
+
+	dhd = ifp->info;
+
+	DHD_TRACE(("%s: idx %d, state %d\n", __FUNCTION__, ifp->idx, ifp->state));
+
+	switch (ifp->state) {
+	case WLC_E_IF_ADD:
+		/*
+		 * Delete the existing interface before overwriting it
+		 * in case we missed the WLC_E_IF_DEL event.
+		 */
+		if (ifp->net != NULL) {
+			DHD_ERROR(("%s: ERROR: netdev:%s already exists, try free & unregister \n",
+			 __FUNCTION__, ifp->net->name));
+			netif_stop_queue(ifp->net);
+			unregister_netdev(ifp->net);
+			free_netdev(ifp->net);
+		}
+		/* Allocate etherdev, including space for private structure */
+		if (!(ifp->net = alloc_etherdev(sizeof(dhd)))) {
+			DHD_ERROR(("%s: OOM - alloc_etherdev\n", __FUNCTION__));
+			ret = -ENOMEM;
+		}
+		if (ret == 0) {
+			strcpy(ifp->net->name, ifp->name);
+			memcpy(netdev_priv(ifp->net), &dhd, sizeof(dhd));
+			if ((err = dhd_net_attach(&dhd->pub, ifp->idx)) != 0) {
+				DHD_ERROR(("%s: dhd_net_attach failed, err %d\n",
+					__FUNCTION__, err));
+				ret = -EOPNOTSUPP;
+			} else {
+#ifdef SOFTAP
+				flags = dhd_os_spin_lock(&dhd->pub);
+				/* save ptr to wl0.1 netdev for use in wl_iw.c  */
+				ap_net_dev = ifp->net;
+				 /* signal to the SOFTAP 'sleeper' thread, wl0.1 is ready */
+				up(&ap_eth_sema);
+				dhd_os_spin_unlock(&dhd->pub, flags);
+#endif
+				DHD_TRACE(("\n ==== pid:%x, net_device for if:%s created ===\n\n",
+					current->pid, ifp->net->name));
+				ifp->state = 0;
+			}
+		}
+		break;
+	case WLC_E_IF_DEL:
+		if (ifp->net != NULL) {
+			DHD_TRACE(("\n%s: got 'WLC_E_IF_DEL' state\n", __FUNCTION__));
+			netif_stop_queue(ifp->net);
+			unregister_netdev(ifp->net);
+			ret = DHD_DEL_IF;	/* Make sure the free_netdev() is called */
+		}
+		break;
+	default:
+		DHD_ERROR(("%s: bad op %d\n", __FUNCTION__, ifp->state));
+		ASSERT(!ifp->state);
+		break;
+	}
+
+	if (ret < 0) {
+		if (ifp->net) {
+			free_netdev(ifp->net);
+		}
+		dhd->iflist[ifp->idx] = NULL;
+		MFREE(dhd->pub.osh, ifp, sizeof(*ifp));
+#ifdef SOFTAP
+		flags = dhd_os_spin_lock(&dhd->pub);
+		if (ifp->net == ap_net_dev)
+			ap_net_dev = NULL;     /* NULL SOFTAP global as well */
+		dhd_os_spin_unlock(&dhd->pub, flags);
+#endif /*  SOFTAP */
+	}
+}
+
+static int
+_dhd_sysioc_thread(void *data)
+{
+	dhd_info_t *dhd = (dhd_info_t *)data;
+	int i;
+#ifdef SOFTAP
+	bool in_ap = FALSE;
+	unsigned long flags;
+#endif
+
+	DAEMONIZE("dhd_sysioc");
+
+	while (down_interruptible(&dhd->sysioc_sem) == 0) {
+		dhd_os_start_lock(&dhd->pub);
+		dhd_os_wake_lock(&dhd->pub);
+		for (i = 0; i < DHD_MAX_IFS; i++) {
+			if (dhd->iflist[i]) {
+				DHD_TRACE(("%s: interface %d\n",__FUNCTION__, i));
+#ifdef SOFTAP
+				flags = dhd_os_spin_lock(&dhd->pub);
+				in_ap = (ap_net_dev != NULL);
+				dhd_os_spin_unlock(&dhd->pub, flags);
+#endif /* SOFTAP */
+				if (dhd->iflist[i]->state)
+					dhd_op_if(dhd->iflist[i]);
+#ifdef SOFTAP
+				if (dhd->iflist[i] == NULL) {
+					DHD_TRACE(("%s: interface %d just been removed!\n\n", __FUNCTION__, i));
+					continue;
+				}
+
+				if (in_ap && dhd->set_macaddress) {
+					DHD_TRACE(("attempt to set MAC for %s in AP Mode blocked.\n", dhd->iflist[i]->net->name));
+					dhd->set_macaddress = FALSE;
+					continue;
+				}
+
+				if (in_ap && dhd->set_multicast)  {
+					DHD_TRACE(("attempt to set MULTICAST list for %s in AP Mode blocked.\n", dhd->iflist[i]->net->name));
+					dhd->set_multicast = FALSE;
+					continue;
+				}
+#endif /* SOFTAP */
+				if (dhd->set_multicast) {
+					dhd->set_multicast = FALSE;
+					_dhd_set_multicast_list(dhd, i);
+				}
+				if (dhd->set_macaddress) {
+					dhd->set_macaddress = FALSE;
+					_dhd_set_mac_address(dhd, i, &dhd->macvalue);
+				}
+			}
+		}
+		dhd_os_wake_unlock(&dhd->pub);
+		dhd_os_start_unlock(&dhd->pub);
+	}
+	DHD_TRACE(("%s: stopped\n",__FUNCTION__));
+	complete_and_exit(&dhd->sysioc_exited, 0);
+}
+
+static int
+dhd_set_mac_address(struct net_device *dev, void *addr)
+{
+	int ret = 0;
+
+	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+	struct sockaddr *sa = (struct sockaddr *)addr;
+	int ifidx;
+
+	DHD_TRACE(("%s: Enter\n",__FUNCTION__));
+	ifidx = dhd_net2idx(dhd, dev);
+	if (ifidx == DHD_BAD_IF)
+		return -1;
+
+	ASSERT(dhd->sysioc_pid >= 0);
+	memcpy(&dhd->macvalue, sa->sa_data, ETHER_ADDR_LEN);
+	dhd->set_macaddress = TRUE;
+	up(&dhd->sysioc_sem);
+
+	return ret;
+}
+
+static void
+dhd_set_multicast_list(struct net_device *dev)
+{
+	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+	int ifidx;
+
+	DHD_TRACE(("%s: Enter\n",__FUNCTION__));
+	ifidx = dhd_net2idx(dhd, dev);
+	if (ifidx == DHD_BAD_IF)
+		return;
+
+	ASSERT(dhd->sysioc_pid >= 0);
+	dhd->set_multicast = TRUE;
+	up(&dhd->sysioc_sem);
+}
+
+int
+dhd_sendpkt(dhd_pub_t *dhdp, int ifidx, void *pktbuf)
+{
+	int ret;
+	dhd_info_t *dhd = (dhd_info_t *)(dhdp->info);
+
+	/* Reject if down */
+	if (!dhdp->up || (dhdp->busstate == DHD_BUS_DOWN)) {
+		return -ENODEV;
+	}
+
+	/* Update multicast statistic */
+	if (PKTLEN(dhdp->osh, pktbuf) >= ETHER_ADDR_LEN) {
+		uint8 *pktdata = (uint8 *)PKTDATA(dhdp->osh, pktbuf);
+		struct ether_header *eh = (struct ether_header *)pktdata;
+
+		if (ETHER_ISMULTI(eh->ether_dhost))
+			dhdp->tx_multicast++;
+		if (ntoh16(eh->ether_type) == ETHER_TYPE_802_1X)
+			atomic_inc(&dhd->pend_8021x_cnt);
+	}
+
+	/* Look into the packet and update the packet priority */
+	if ((PKTPRIO(pktbuf) == 0))
+		pktsetprio(pktbuf, FALSE);
+
+	/* If the protocol uses a data header, apply it */
+	dhd_prot_hdrpush(dhdp, ifidx, pktbuf);
+
+	/* Use bus module to send data frame */
+#ifdef BCMDBUS
+	ret = dbus_send_pkt(dhdp->dbus, pktbuf, NULL /* pktinfo */);
+#else
+	ret = dhd_bus_txdata(dhdp->bus, pktbuf);
+#endif /* BCMDBUS */
+
+	return ret;
+}
+
+static int
+dhd_start_xmit(struct sk_buff *skb, struct net_device *net)
+{
+	int ret;
+	void *pktbuf;
+	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
+	int ifidx;
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	dhd_os_wake_lock(&dhd->pub);
+
+	/* Reject if down */
+	if (!dhd->pub.up || (dhd->pub.busstate == DHD_BUS_DOWN)) {
+		DHD_ERROR(("%s: xmit rejected pub.up=%d busstate=%d\n",
+			 __FUNCTION__, dhd->pub.up, dhd->pub.busstate));
+		netif_stop_queue(net);
+		/* Send Event when bus down detected during data session */
+		if (dhd->pub.busstate == DHD_BUS_DOWN)  {
+			DHD_ERROR(("%s: Event HANG send up\n", __FUNCTION__));
+			net_os_send_hang_message(net);
+		}
+		dhd_os_wake_unlock(&dhd->pub);
+		return -ENODEV;
+	}
+
+	ifidx = dhd_net2idx(dhd, net);
+	if (ifidx == DHD_BAD_IF) {
+		DHD_ERROR(("%s: bad ifidx %d\n", __FUNCTION__, ifidx));
+		netif_stop_queue(net);
+		dhd_os_wake_unlock(&dhd->pub);
+		return -ENODEV;
+	}
+
+	/* Make sure there's enough room for any header */
+	if (skb_headroom(skb) < dhd->pub.hdrlen) {
+		struct sk_buff *skb2;
+
+		DHD_INFO(("%s: insufficient headroom\n",
+		          dhd_ifname(&dhd->pub, ifidx)));
+		dhd->pub.tx_realloc++;
+		skb2 = skb_realloc_headroom(skb, dhd->pub.hdrlen);
+		dev_kfree_skb(skb);
+		if ((skb = skb2) == NULL) {
+			DHD_ERROR(("%s: skb_realloc_headroom failed\n",
+			           dhd_ifname(&dhd->pub, ifidx)));
+			ret = -ENOMEM;
+			goto done;
+		}
+	}
+
+	/* Convert to packet */
+	if (!(pktbuf = PKTFRMNATIVE(dhd->pub.osh, skb))) {
+		DHD_ERROR(("%s: PKTFRMNATIVE failed\n",
+		           dhd_ifname(&dhd->pub, ifidx)));
+		dev_kfree_skb_any(skb);
+		ret = -ENOMEM;
+		goto done;
+	}
+
+	ret = dhd_sendpkt(&dhd->pub, ifidx, pktbuf);
+
+done:
+	if (ret)
+		dhd->pub.dstats.tx_dropped++;
+	else
+		dhd->pub.tx_packets++;
+
+	dhd_os_wake_unlock(&dhd->pub);
+
+	/* Return ok: we always eat the packet */
+	return 0;
+}
+
+void
+dhd_txflowcontrol(dhd_pub_t *dhdp, int ifidx, bool state)
+{
+	struct net_device *net;
+	dhd_info_t *dhd = dhdp->info;
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	dhdp->txoff = state;
+	ASSERT(dhd && dhd->iflist[ifidx]);
+	net = dhd->iflist[ifidx]->net;
+	if (state == ON)
+		netif_stop_queue(net);
+	else
+		netif_wake_queue(net);
+}
+
+void
+dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt)
+{
+	dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
+	struct sk_buff *skb;
+	uchar *eth;
+	uint len;
+	void * data, *pnext, *save_pktbuf;
+	int i;
+	dhd_if_t *ifp;
+	wl_event_msg_t event;
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	save_pktbuf = pktbuf;
+
+	for (i = 0; pktbuf && i < numpkt; i++, pktbuf = pnext) {
+
+		pnext = PKTNEXT(dhdp->osh, pktbuf);
+		PKTSETNEXT(wl->sh.osh, pktbuf, NULL);
+
+
+		skb = PKTTONATIVE(dhdp->osh, pktbuf);
+
+		/* Get the protocol, maintain skb around eth_type_trans()
+		 * The main reason for this hack is for the limitation of
+		 * Linux 2.4 where 'eth_type_trans' uses the 'net->hard_header_len'
+		 * to perform skb_pull inside vs ETH_HLEN. Since to avoid
+		 * coping of the packet coming from the network stack to add
+		 * BDC, Hardware header etc, during network interface registration
+		 * we set the 'net->hard_header_len' to ETH_HLEN + extra space required
+		 * for BDC, Hardware header etc. and not just the ETH_HLEN
+		 */
+		eth = skb->data;
+		len = skb->len;
+
+		ifp = dhd->iflist[ifidx];
+		if (ifp == NULL)
+			ifp = dhd->iflist[0];
+
+		ASSERT(ifp);
+		skb->dev = ifp->net;
+		skb->protocol = eth_type_trans(skb, skb->dev);
+
+		if (skb->pkt_type == PACKET_MULTICAST) {
+			dhd->pub.rx_multicast++;
+		}
+
+		skb->data = eth;
+		skb->len = len;
+
+		/* Strip header, count, deliver upward */
+		skb_pull(skb, ETH_HLEN);
+
+		/* Process special event packets and then discard them */
+		if (ntoh16(skb->protocol) == ETHER_TYPE_BRCM)
+			dhd_wl_host_event(dhd, &ifidx,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
+			skb->mac_header,
+#else
+			skb->mac.raw,
+#endif
+			&event,
+			&data);
+
+		ASSERT(ifidx < DHD_MAX_IFS && dhd->iflist[ifidx]);
+		if (dhd->iflist[ifidx] && !dhd->iflist[ifidx]->state)
+			ifp = dhd->iflist[ifidx];
+
+		if (ifp->net)
+			ifp->net->last_rx = jiffies;
+
+		dhdp->dstats.rx_bytes += skb->len;
+		dhdp->rx_packets++; /* Local count */
+
+		if (in_interrupt()) {
+			netif_rx(skb);
+		} else {
+			/* If the receive is not processed inside an ISR,
+			 * the softirqd must be woken explicitly to service
+			 * the NET_RX_SOFTIRQ.  In 2.6 kernels, this is handled
+			 * by netif_rx_ni(), but in earlier kernels, we need
+			 * to do it manually.
+			 */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+			netif_rx_ni(skb);
+#else
+			ulong flags;
+			netif_rx(skb);
+			local_irq_save(flags);
+			RAISE_RX_SOFTIRQ();
+			local_irq_restore(flags);
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) */
+		}
+	}
+	dhd_os_wake_lock_timeout_enable(dhdp);
+}
+
+void
+dhd_event(struct dhd_info *dhd, char *evpkt, int evlen, int ifidx)
+{
+	/* Linux version has nothing to do */
+	return;
+}
+
+void
+dhd_txcomplete(dhd_pub_t *dhdp, void *txp, bool success)
+{
+	uint ifidx;
+	dhd_info_t *dhd = (dhd_info_t *)(dhdp->info);
+	struct ether_header *eh;
+	uint16 type;
+
+	dhd_prot_hdrpull(dhdp, &ifidx, txp);
+
+	eh = (struct ether_header *)PKTDATA(dhdp->osh, txp);
+	type  = ntoh16(eh->ether_type);
+
+	if (type == ETHER_TYPE_802_1X)
+		atomic_dec(&dhd->pend_8021x_cnt);
+
+}
+
+static struct net_device_stats *
+dhd_get_stats(struct net_device *net)
+{
+	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
+	dhd_if_t *ifp;
+	int ifidx;
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	ifidx = dhd_net2idx(dhd, net);
+	if (ifidx == DHD_BAD_IF)
+		return NULL;
+
+	ifp = dhd->iflist[ifidx];
+	ASSERT(dhd && ifp);
+
+	if (dhd->pub.up) {
+		/* Use the protocol to get dongle stats */
+		dhd_prot_dstats(&dhd->pub);
+	}
+
+	/* Copy dongle stats to net device stats */
+	ifp->stats.rx_packets = dhd->pub.dstats.rx_packets;
+	ifp->stats.tx_packets = dhd->pub.dstats.tx_packets;
+	ifp->stats.rx_bytes = dhd->pub.dstats.rx_bytes;
+	ifp->stats.tx_bytes = dhd->pub.dstats.tx_bytes;
+	ifp->stats.rx_errors = dhd->pub.dstats.rx_errors;
+	ifp->stats.tx_errors = dhd->pub.dstats.tx_errors;
+	ifp->stats.rx_dropped = dhd->pub.dstats.rx_dropped;
+	ifp->stats.tx_dropped = dhd->pub.dstats.tx_dropped;
+	ifp->stats.multicast = dhd->pub.dstats.multicast;
+
+	return &ifp->stats;
+}
+
+static int
+dhd_watchdog_thread(void *data)
+{
+	dhd_info_t *dhd = (dhd_info_t *)data;
+
+	/* This thread doesn't need any user-level access,
+	 * so get rid of all our resources
+	 */
+#ifdef DHD_SCHED
+	if (dhd_watchdog_prio > 0) {
+		struct sched_param param;
+		param.sched_priority = (dhd_watchdog_prio < MAX_RT_PRIO)?
+			dhd_watchdog_prio:(MAX_RT_PRIO-1);
+		setScheduler(current, SCHED_FIFO, &param);
+	}
+#endif /* DHD_SCHED */
+
+	DAEMONIZE("dhd_watchdog");
+
+	/* Run until signal received */
+	while (1) {
+		if (down_interruptible (&dhd->watchdog_sem) == 0) {
+			dhd_os_sdlock(&dhd->pub);
+			if (dhd->pub.dongle_reset == FALSE) {
+				DHD_TIMER(("%s:\n", __FUNCTION__));
+				/* Call the bus module watchdog */
+				dhd_bus_watchdog(&dhd->pub);
+
+				/* Count the tick for reference */
+				dhd->pub.tickcnt++;
+
+				/* Reschedule the watchdog */
+				if (dhd->wd_timer_valid)
+					mod_timer(&dhd->timer, jiffies + dhd_watchdog_ms * HZ / 1000);
+			}
+			dhd_os_sdunlock(&dhd->pub);
+			dhd_os_wake_unlock(&dhd->pub);
+		} else {
+			break;
+		}
+	}
+
+	complete_and_exit(&dhd->watchdog_exited, 0);
+}
+
+static void
+dhd_watchdog(ulong data)
+{
+	dhd_info_t *dhd = (dhd_info_t *)data;
+
+	dhd_os_wake_lock(&dhd->pub);
+	if (dhd->pub.dongle_reset) {
+		dhd_os_wake_unlock(&dhd->pub);
+		return;
+	}
+
+	if (dhd->watchdog_pid >= 0) {
+		up(&dhd->watchdog_sem);
+		return;
+	}
+
+	dhd_os_sdlock(&dhd->pub);
+	/* Call the bus module watchdog */
+	dhd_bus_watchdog(&dhd->pub);
+
+	/* Count the tick for reference */
+	dhd->pub.tickcnt++;
+
+	/* Reschedule the watchdog */
+	if (dhd->wd_timer_valid)
+		mod_timer(&dhd->timer, jiffies + dhd_watchdog_ms * HZ / 1000);
+	dhd_os_sdunlock(&dhd->pub);
+	dhd_os_wake_unlock(&dhd->pub);
+}
+
+static int
+dhd_dpc_thread(void *data)
+{
+	dhd_info_t *dhd = (dhd_info_t *)data;
+
+	/* This thread doesn't need any user-level access,
+	 * so get rid of all our resources
+	 */
+#ifdef DHD_SCHED
+	if (dhd_dpc_prio > 0)
+	{
+		struct sched_param param;
+		param.sched_priority = (dhd_dpc_prio < MAX_RT_PRIO)?dhd_dpc_prio:(MAX_RT_PRIO-1);
+		setScheduler(current, SCHED_FIFO, &param);
+	}
+#endif /* DHD_SCHED */
+
+	DAEMONIZE("dhd_dpc");
+
+	/* Run until signal received */
+	while (1) {
+		if (down_interruptible(&dhd->dpc_sem) == 0) {
+			/* Call bus dpc unless it indicated down (then clean stop) */
+			if (dhd->pub.busstate != DHD_BUS_DOWN) {
+				if (dhd_bus_dpc(dhd->pub.bus)) {
+					up(&dhd->dpc_sem);
+				}
+				else {
+					dhd_os_wake_unlock(&dhd->pub);
+				}
+			} else {
+				if (dhd->pub.up)
+					dhd_bus_stop(dhd->pub.bus, TRUE);
+				dhd_os_wake_unlock(&dhd->pub);
+			}
+		}
+		else
+			break;
+	}
+
+	complete_and_exit(&dhd->dpc_exited, 0);
+}
+
+static void
+dhd_dpc(ulong data)
+{
+	dhd_info_t *dhd;
+
+	dhd = (dhd_info_t *)data;
+
+	/* Call bus dpc unless it indicated down (then clean stop) */
+	if (dhd->pub.busstate != DHD_BUS_DOWN) {
+		if (dhd_bus_dpc(dhd->pub.bus))
+			tasklet_schedule(&dhd->tasklet);
+	} else {
+		dhd_bus_stop(dhd->pub.bus, TRUE);
+	}
+}
+
+void
+dhd_sched_dpc(dhd_pub_t *dhdp)
+{
+	dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
+
+	dhd_os_wake_lock(dhdp);
+	if (dhd->dpc_pid >= 0) {
+		up(&dhd->dpc_sem);
+		return;
+	}
+
+	tasklet_schedule(&dhd->tasklet);
+}
+
+#ifdef TOE
+/* Retrieve current toe component enables, which are kept as a bitmap in toe_ol iovar */
+static int
+dhd_toe_get(dhd_info_t *dhd, int ifidx, uint32 *toe_ol)
+{
+	wl_ioctl_t ioc;
+	char buf[32];
+	int ret;
+
+	memset(&ioc, 0, sizeof(ioc));
+
+	ioc.cmd = WLC_GET_VAR;
+	ioc.buf = buf;
+	ioc.len = (uint)sizeof(buf);
+	ioc.set = FALSE;
+
+	strcpy(buf, "toe_ol");
+	if ((ret = dhd_prot_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) {
+		/* Check for older dongle image that doesn't support toe_ol */
+		if (ret == -EIO) {
+			DHD_ERROR(("%s: toe not supported by device\n",
+				dhd_ifname(&dhd->pub, ifidx)));
+			return -EOPNOTSUPP;
+		}
+
+		DHD_INFO(("%s: could not get toe_ol: ret=%d\n", dhd_ifname(&dhd->pub, ifidx), ret));
+		return ret;
+	}
+
+	memcpy(toe_ol, buf, sizeof(uint32));
+	return 0;
+}
+
+/* Set current toe component enables in toe_ol iovar, and set toe global enable iovar */
+static int
+dhd_toe_set(dhd_info_t *dhd, int ifidx, uint32 toe_ol)
+{
+	wl_ioctl_t ioc;
+	char buf[32];
+	int toe, ret;
+
+	memset(&ioc, 0, sizeof(ioc));
+
+	ioc.cmd = WLC_SET_VAR;
+	ioc.buf = buf;
+	ioc.len = (uint)sizeof(buf);
+	ioc.set = TRUE;
+
+	/* Set toe_ol as requested */
+
+	strcpy(buf, "toe_ol");
+	memcpy(&buf[sizeof("toe_ol")], &toe_ol, sizeof(uint32));
+
+	if ((ret = dhd_prot_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) {
+		DHD_ERROR(("%s: could not set toe_ol: ret=%d\n",
+			dhd_ifname(&dhd->pub, ifidx), ret));
+		return ret;
+	}
+
+	/* Enable toe globally only if any components are enabled. */
+
+	toe = (toe_ol != 0);
+
+	strcpy(buf, "toe");
+	memcpy(&buf[sizeof("toe")], &toe, sizeof(uint32));
+
+	if ((ret = dhd_prot_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) {
+		DHD_ERROR(("%s: could not set toe: ret=%d\n", dhd_ifname(&dhd->pub, ifidx), ret));
+		return ret;
+	}
+
+	return 0;
+}
+#endif /* TOE */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
+static void dhd_ethtool_get_drvinfo(struct net_device *net,
+                                    struct ethtool_drvinfo *info)
+{
+	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
+
+	sprintf(info->driver, "wl");
+	sprintf(info->version, "%lu", dhd->pub.drv_version);
+}
+
+struct ethtool_ops dhd_ethtool_ops = {
+	.get_drvinfo = dhd_ethtool_get_drvinfo
+};
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */
+
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2)
+static int
+dhd_ethtool(dhd_info_t *dhd, void *uaddr)
+{
+	struct ethtool_drvinfo info;
+	char drvname[sizeof(info.driver)];
+	uint32 cmd;
+#ifdef TOE
+	struct ethtool_value edata;
+	uint32 toe_cmpnt, csum_dir;
+	int ret;
+#endif
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	/* all ethtool calls start with a cmd word */
+	if (copy_from_user(&cmd, uaddr, sizeof (uint32)))
+		return -EFAULT;
+
+	switch (cmd) {
+	case ETHTOOL_GDRVINFO:
+		/* Copy out any request driver name */
+		if (copy_from_user(&info, uaddr, sizeof(info)))
+			return -EFAULT;
+		strncpy(drvname, info.driver, sizeof(info.driver));
+		drvname[sizeof(info.driver)-1] = '\0';
+
+		/* clear struct for return */
+		memset(&info, 0, sizeof(info));
+		info.cmd = cmd;
+
+		/* if dhd requested, identify ourselves */
+		if (strcmp(drvname, "?dhd") == 0) {
+			sprintf(info.driver, "dhd");
+			strcpy(info.version, EPI_VERSION_STR);
+		}
+
+		/* otherwise, require dongle to be up */
+		else if (!dhd->pub.up) {
+			DHD_ERROR(("%s: dongle is not up\n", __FUNCTION__));
+			return -ENODEV;
+		}
+
+		/* finally, report dongle driver type */
+		else if (dhd->pub.iswl)
+			sprintf(info.driver, "wl");
+		else
+			sprintf(info.driver, "xx");
+
+		sprintf(info.version, "%lu", dhd->pub.drv_version);
+		if (copy_to_user(uaddr, &info, sizeof(info)))
+			return -EFAULT;
+		DHD_CTL(("%s: given %*s, returning %s\n", __FUNCTION__,
+		         (int)sizeof(drvname), drvname, info.driver));
+		break;
+
+#ifdef TOE
+	/* Get toe offload components from dongle */
+	case ETHTOOL_GRXCSUM:
+	case ETHTOOL_GTXCSUM:
+		if ((ret = dhd_toe_get(dhd, 0, &toe_cmpnt)) < 0)
+			return ret;
+
+		csum_dir = (cmd == ETHTOOL_GTXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL;
+
+		edata.cmd = cmd;
+		edata.data = (toe_cmpnt & csum_dir) ? 1 : 0;
+
+		if (copy_to_user(uaddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		break;
+
+	/* Set toe offload components in dongle */
+	case ETHTOOL_SRXCSUM:
+	case ETHTOOL_STXCSUM:
+		if (copy_from_user(&edata, uaddr, sizeof(edata)))
+			return -EFAULT;
+
+		/* Read the current settings, update and write back */
+		if ((ret = dhd_toe_get(dhd, 0, &toe_cmpnt)) < 0)
+			return ret;
+
+		csum_dir = (cmd == ETHTOOL_STXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL;
+
+		if (edata.data != 0)
+			toe_cmpnt |= csum_dir;
+		else
+			toe_cmpnt &= ~csum_dir;
+
+		if ((ret = dhd_toe_set(dhd, 0, toe_cmpnt)) < 0)
+			return ret;
+
+		/* If setting TX checksum mode, tell Linux the new mode */
+		if (cmd == ETHTOOL_STXCSUM) {
+			if (edata.data)
+				dhd->iflist[0]->net->features |= NETIF_F_IP_CSUM;
+			else
+				dhd->iflist[0]->net->features &= ~NETIF_F_IP_CSUM;
+		}
+
+		break;
+#endif /* TOE */
+
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) */
+
+static int
+dhd_ioctl_entry(struct net_device *net, struct ifreq *ifr, int cmd)
+{
+	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
+	dhd_ioctl_t ioc;
+	int bcmerror = 0;
+	int buflen = 0;
+	void *buf = NULL;
+	uint driver = 0;
+	int ifidx;
+	bool is_set_key_cmd;
+	int ret;
+
+	dhd_os_wake_lock(&dhd->pub);
+
+	/* send to dongle only if we are not waiting for reload already */
+	if (dhd->pub.hang_was_sent) {
+		DHD_ERROR(("%s: HANG was sent up earlier\n", __FUNCTION__));
+		dhd_os_wake_lock_timeout_enable(&dhd->pub);
+		dhd_os_wake_unlock(&dhd->pub);
+		return OSL_ERROR(BCME_DONGLE_DOWN);
+	}
+
+	ifidx = dhd_net2idx(dhd, net);
+	DHD_TRACE(("%s: ifidx %d, cmd 0x%04x\n", __FUNCTION__, ifidx, cmd));
+
+	if (ifidx == DHD_BAD_IF) {
+		dhd_os_wake_unlock(&dhd->pub);
+		return -1;
+	}
+
+#if defined(CONFIG_WIRELESS_EXT)
+	/* linux wireless extensions */
+	if ((cmd >= SIOCIWFIRST) && (cmd <= SIOCIWLAST)) {
+		/* may recurse, do NOT lock */
+		ret = wl_iw_ioctl(net, ifr, cmd);
+		dhd_os_wake_unlock(&dhd->pub);
+		return ret;
+	}
+#endif /* defined(CONFIG_WIRELESS_EXT) */
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2)
+	if (cmd == SIOCETHTOOL) {
+		ret = dhd_ethtool(dhd, (void*)ifr->ifr_data);
+		dhd_os_wake_unlock(&dhd->pub);
+		return ret;
+	}
+#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) */
+
+	if (cmd != SIOCDEVPRIVATE) {
+		dhd_os_wake_unlock(&dhd->pub);
+		return -EOPNOTSUPP;
+	}
+
+	memset(&ioc, 0, sizeof(ioc));
+
+	/* Copy the ioc control structure part of ioctl request */
+	if (copy_from_user(&ioc, ifr->ifr_data, sizeof(wl_ioctl_t))) {
+		bcmerror = -BCME_BADADDR;
+		goto done;
+	}
+
+	/* Copy out any buffer passed */
+	if (ioc.buf) {
+		buflen = MIN(ioc.len, DHD_IOCTL_MAXLEN);
+		/* optimization for direct ioctl calls from kernel */
+		/*
+		if (segment_eq(get_fs(), KERNEL_DS)) {
+			buf = ioc.buf;
+		} else {
+		*/
+		{
+			if (!(buf = (char*)MALLOC(dhd->pub.osh, buflen))) {
+				bcmerror = -BCME_NOMEM;
+				goto done;
+			}
+			if (copy_from_user(buf, ioc.buf, buflen)) {
+				bcmerror = -BCME_BADADDR;
+				goto done;
+			}
+		}
+	}
+
+	/* To differentiate between wl and dhd read 4 more byes */
+	if ((copy_from_user(&driver, (char *)ifr->ifr_data + sizeof(wl_ioctl_t),
+		sizeof(uint)) != 0)) {
+		bcmerror = -BCME_BADADDR;
+		goto done;
+	}
+
+	if (!capable(CAP_NET_ADMIN)) {
+		bcmerror = -BCME_EPERM;
+		goto done;
+	}
+
+	/* check for local dhd ioctl and handle it */
+	if (driver == DHD_IOCTL_MAGIC) {
+		bcmerror = dhd_ioctl((void *)&dhd->pub, &ioc, buf, buflen);
+		if (bcmerror)
+			dhd->pub.bcmerror = bcmerror;
+		goto done;
+	}
+
+	/* send to dongle (must be up, and wl) */
+	if (dhd->pub.busstate != DHD_BUS_DATA) {
+		DHD_ERROR(("%s DONGLE_DOWN\n", __FUNCTION__));
+		bcmerror = BCME_DONGLE_DOWN;
+		goto done;
+	}
+
+	if (!dhd->pub.iswl) {
+		bcmerror = BCME_DONGLE_DOWN;
+		goto done;
+	}
+
+	/* Intercept WLC_SET_KEY IOCTL - serialize M4 send and set key IOCTL to
+	 * prevent M4 encryption.
+	 */
+	is_set_key_cmd = ((ioc.cmd == WLC_SET_KEY) ||
+	                 ((ioc.cmd == WLC_SET_VAR) &&
+	                        !(strncmp("wsec_key", ioc.buf, 9))) ||
+	                 ((ioc.cmd == WLC_SET_VAR) &&
+	                        !(strncmp("bsscfg:wsec_key", ioc.buf, 15))));
+	if (is_set_key_cmd) {
+		dhd_wait_pend8021x(net);
+	}
+
+	bcmerror = dhd_prot_ioctl(&dhd->pub, ifidx, (wl_ioctl_t *)&ioc, buf, buflen);
+
+done:
+	if ((bcmerror == -ETIMEDOUT) || ((dhd->pub.busstate == DHD_BUS_DOWN) &&
+			(!dhd->pub.dongle_reset))) {
+		DHD_ERROR(("%s: Event HANG send up\n", __FUNCTION__));
+		net_os_send_hang_message(net);
+	}
+
+	if (!bcmerror && buf && ioc.buf) {
+		if (copy_to_user(ioc.buf, buf, buflen))
+			bcmerror = -EFAULT;
+	}
+
+	if (buf)
+		MFREE(dhd->pub.osh, buf, buflen);
+
+	dhd_os_wake_unlock(&dhd->pub);
+
+	return OSL_ERROR(bcmerror);
+}
+
+static int
+dhd_stop(struct net_device *net)
+{
+#if !defined(IGNORE_ETH0_DOWN)
+	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
+
+	DHD_TRACE(("%s: Enter %s\n", __FUNCTION__, net->name));
+	if (dhd->pub.up == 0) {
+		return 0;
+	}
+
+	/* Set state and stop OS transmissions */
+	dhd->pub.up = 0;
+	netif_stop_queue(net);
+#else
+	DHD_ERROR(("BYPASS %s:due to BRCM compilation : under investigation ...\n", __FUNCTION__));
+#endif /* !defined(IGNORE_ETH0_DOWN) */
+	dhd->pub.hang_was_sent = 0;
+	OLD_MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int
+dhd_open(struct net_device *net)
+{
+	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
+#ifdef TOE
+	uint32 toe_ol;
+#endif
+	int ifidx;
+
+	/*  Force start if ifconfig_up gets called before START command */
+	wl_control_wl_start(net);
+
+	ifidx = dhd_net2idx(dhd, net);
+	DHD_TRACE(("%s: ifidx %d\n", __FUNCTION__, ifidx));
+
+	if (ifidx == DHD_BAD_IF)
+		return -1;
+
+	if ((dhd->iflist[ifidx]) && (dhd->iflist[ifidx]->state == WLC_E_IF_DEL)) {
+		DHD_ERROR(("%s: Error: called when IF already deleted\n", __FUNCTION__));
+		return -1;
+	}
+
+	if (ifidx == 0) { /* do it only for primary eth0 */
+
+		atomic_set(&dhd->pend_8021x_cnt, 0);
+
+	memcpy(net->dev_addr, dhd->pub.mac.octet, ETHER_ADDR_LEN);
+
+#ifdef TOE
+	/* Get current TOE mode from dongle */
+	if (dhd_toe_get(dhd, ifidx, &toe_ol) >= 0 && (toe_ol & TOE_TX_CSUM_OL) != 0)
+		dhd->iflist[ifidx]->net->features |= NETIF_F_IP_CSUM;
+	else
+		dhd->iflist[ifidx]->net->features &= ~NETIF_F_IP_CSUM;
+#endif
+	}
+	/* Allow transmit calls */
+	netif_start_queue(net);
+	dhd->pub.up = 1;
+
+	OLD_MOD_INC_USE_COUNT;
+	return 0;
+}
+
+osl_t *
+dhd_osl_attach(void *pdev, uint bustype)
+{
+	return osl_attach(pdev, bustype, TRUE);
+}
+
+void
+dhd_osl_detach(osl_t *osh)
+{
+	if (MALLOCED(osh)) {
+		DHD_ERROR(("%s: MEMORY LEAK %d bytes\n", __FUNCTION__, MALLOCED(osh)));
+	}
+	osl_detach(osh);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && 1
+	up(&dhd_registration_sem);
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */
+}
+
+int
+dhd_add_if(dhd_info_t *dhd, int ifidx, void *handle, char *name,
+	uint8 *mac_addr, uint32 flags, uint8 bssidx)
+{
+	dhd_if_t *ifp;
+
+	DHD_TRACE(("%s: idx %d, handle->%p\n", __FUNCTION__, ifidx, handle));
+
+	ASSERT(dhd && (ifidx < DHD_MAX_IFS));
+
+	ifp = dhd->iflist[ifidx];
+	if (!ifp && !(ifp = MALLOC(dhd->pub.osh, sizeof(dhd_if_t)))) {
+		DHD_ERROR(("%s: OOM - dhd_if_t\n", __FUNCTION__));
+		return -ENOMEM;
+	}
+
+	memset(ifp, 0, sizeof(dhd_if_t));
+	ifp->info = dhd;
+	dhd->iflist[ifidx] = ifp;
+	strncpy(ifp->name, name, IFNAMSIZ);
+	ifp->name[IFNAMSIZ] = '\0';
+	if (mac_addr != NULL)
+		memcpy(&ifp->mac_addr, mac_addr, ETHER_ADDR_LEN);
+
+	if (handle == NULL) {
+		ifp->state = WLC_E_IF_ADD;
+		ifp->idx = ifidx;
+		ASSERT(dhd->sysioc_pid >= 0);
+		up(&dhd->sysioc_sem);
+	} else
+		ifp->net = (struct net_device *)handle;
+
+	return 0;
+}
+
+void
+dhd_del_if(dhd_info_t *dhd, int ifidx)
+{
+	dhd_if_t *ifp;
+
+	DHD_TRACE(("%s: idx %d\n", __FUNCTION__, ifidx));
+
+	ASSERT(dhd && ifidx && (ifidx < DHD_MAX_IFS));
+	ifp = dhd->iflist[ifidx];
+	if (!ifp) {
+		DHD_ERROR(("%s: Null interface\n", __FUNCTION__));
+		return;
+	}
+
+	ifp->state = WLC_E_IF_DEL;
+	ifp->idx = ifidx;
+	ASSERT(dhd->sysioc_pid >= 0);
+	up(&dhd->sysioc_sem);
+}
+
+
+dhd_pub_t *
+dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen)
+{
+	dhd_info_t *dhd = NULL;
+	struct net_device *net;
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+	/* updates firmware nvram path if it was provided as module paramters */
+	if ((firmware_path != NULL) && (firmware_path[0] != '\0'))
+		strcpy(fw_path, firmware_path);
+	if ((nvram_path != NULL) && (nvram_path[0] != '\0'))
+		strcpy(nv_path, nvram_path);
+
+	/* Allocate etherdev, including space for private structure */
+	if (!(net = alloc_etherdev(sizeof(dhd)))) {
+		DHD_ERROR(("%s: OOM - alloc_etherdev\n", __FUNCTION__));
+		goto fail;
+	}
+
+	/* Allocate primary dhd_info */
+	if (!(dhd = MALLOC(osh, sizeof(dhd_info_t)))) {
+		DHD_ERROR(("%s: OOM - alloc dhd_info\n", __FUNCTION__));
+		goto fail;
+	}
+
+	memset(dhd, 0, sizeof(dhd_info_t));
+
+	/*
+	 * Save the dhd_info into the priv
+	 */
+	memcpy(netdev_priv(net), &dhd, sizeof(dhd));
+	dhd->pub.osh = osh;
+
+	/* Set network interface name if it was provided as module parameter */
+	if (iface_name[0]) {
+		int len;
+		char ch;
+		strncpy(net->name, iface_name, IFNAMSIZ);
+		net->name[IFNAMSIZ - 1] = 0;
+		len = strlen(net->name);
+		ch = net->name[len - 1];
+		if ((ch > '9' || ch < '0') && (len < IFNAMSIZ - 2))
+			strcat(net->name, "%d");
+	}
+
+	if (dhd_add_if(dhd, 0, (void *)net, net->name, NULL, 0, 0) == DHD_BAD_IF)
+		goto fail;
+
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 31))
+	net->open = NULL;
+#else
+	net->netdev_ops = NULL;
+#endif
+
+	mutex_init(&dhd->proto_sem);
+	/* Initialize other structure content */
+	init_waitqueue_head(&dhd->ioctl_resp_wait);
+	init_waitqueue_head(&dhd->ctrl_wait);
+
+	/* Initialize the spinlocks */
+	spin_lock_init(&dhd->sdlock);
+	spin_lock_init(&dhd->txqlock);
+	spin_lock_init(&dhd->dhd_lock);
+
+	/* Initialize Wakelock stuff */
+	spin_lock_init(&dhd->wl_lock);
+	dhd->wl_count = 0;
+	dhd->wl_packet = 0;
+#ifdef CONFIG_HAS_WAKELOCK
+	wake_lock_init(&dhd->wl_wifi, WAKE_LOCK_SUSPEND, "wlan_wake");
+	wake_lock_init(&dhd->wl_rxwake, WAKE_LOCK_SUSPEND, "wlan_rx_wake");
+#endif
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25))
+	mutex_init(&dhd->wl_start_lock);
+#endif
+	/* Link to info module */
+	dhd->pub.info = dhd;
+
+	/* Link to bus module */
+	dhd->pub.bus = bus;
+	dhd->pub.hdrlen = bus_hdrlen;
+
+	/* Attach and link in the protocol */
+	if (dhd_prot_attach(&dhd->pub) != 0) {
+		DHD_ERROR(("dhd_prot_attach failed\n"));
+		goto fail;
+	}
+#if defined(CONFIG_WIRELESS_EXT)
+	/* Attach and link in the iw */
+	if (wl_iw_attach(net, (void *)&dhd->pub) != 0) {
+		DHD_ERROR(("wl_iw_attach failed\n"));
+		goto fail;
+	}
+#endif /* defined(CONFIG_WIRELESS_EXT) */
+
+	/* Set up the watchdog timer */
+	init_timer(&dhd->timer);
+	dhd->timer.data = (ulong)dhd;
+	dhd->timer.function = dhd_watchdog;
+
+	/* Initialize thread based operation and lock */
+	mutex_init(&dhd->sdsem);
+	if ((dhd_watchdog_prio >= 0) && (dhd_dpc_prio >= 0)) {
+		dhd->threads_only = TRUE;
+	}
+	else {
+		dhd->threads_only = FALSE;
+	}
+
+	if (dhd_dpc_prio >= 0) {
+		/* Initialize watchdog thread */
+		sema_init(&dhd->watchdog_sem, 0);
+		init_completion(&dhd->watchdog_exited);
+		dhd->watchdog_pid = kernel_thread(dhd_watchdog_thread, dhd, 0);
+	} else {
+		dhd->watchdog_pid = -1;
+	}
+
+	/* Set up the bottom half handler */
+	if (dhd_dpc_prio >= 0) {
+		/* Initialize DPC thread */
+		sema_init(&dhd->dpc_sem, 0);
+		init_completion(&dhd->dpc_exited);
+		dhd->dpc_pid = kernel_thread(dhd_dpc_thread, dhd, 0);
+	} else {
+		tasklet_init(&dhd->tasklet, dhd_dpc, (ulong)dhd);
+		dhd->dpc_pid = -1;
+	}
+
+	if (dhd_sysioc) {
+		sema_init(&dhd->sysioc_sem, 0);
+		init_completion(&dhd->sysioc_exited);
+		dhd->sysioc_pid = kernel_thread(_dhd_sysioc_thread, dhd, 0);
+	} else {
+		dhd->sysioc_pid = -1;
+	}
+
+	/*
+	 * Save the dhd_info into the priv
+	 */
+	memcpy(netdev_priv(net), &dhd, sizeof(dhd));
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP)
+	register_pm_notifier(&dhd_sleep_pm_notifier);
+#endif /*  (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	dhd->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 20;
+	dhd->early_suspend.suspend = dhd_early_suspend;
+	dhd->early_suspend.resume = dhd_late_resume;
+	register_early_suspend(&dhd->early_suspend);
+#endif
+
+	register_inetaddr_notifier(&dhd_notifier);
+
+	return &dhd->pub;
+
+fail:
+	if (net)
+		free_netdev(net);
+	if (dhd)
+		dhd_detach(&dhd->pub);
+
+	return NULL;
+}
+
+
+int
+dhd_bus_start(dhd_pub_t *dhdp)
+{
+	int ret = -1;
+	dhd_info_t *dhd = (dhd_info_t*)dhdp->info;
+#ifdef EMBEDDED_PLATFORM
+	char iovbuf[WL_EVENTING_MASK_LEN + 12];	/*  Room for "event_msgs" + '\0' + bitvec  */
+#endif /* EMBEDDED_PLATFORM */
+
+	ASSERT(dhd);
+
+	DHD_TRACE(("%s: \n", __FUNCTION__));
+
+	dhd_os_sdlock(dhdp);
+
+	/* try to download image and nvram to the dongle */
+	if  (dhd->pub.busstate == DHD_BUS_DOWN) {
+		if (!(dhd_bus_download_firmware(dhd->pub.bus, dhd->pub.osh,
+		                                fw_path, nv_path))) {
+			DHD_ERROR(("%s: dhdsdio_probe_download failed. firmware = %s nvram = %s\n",
+			           __FUNCTION__, fw_path, nv_path));
+			dhd_os_sdunlock(dhdp);
+			return -1;
+		}
+	}
+
+	/* Start the watchdog timer */
+	dhd->pub.tickcnt = 0;
+	dhd_os_wd_timer(&dhd->pub, dhd_watchdog_ms);
+
+	/* Bring up the bus */
+	if ((ret = dhd_bus_init(&dhd->pub, FALSE)) != 0) {
+		DHD_ERROR(("%s, dhd_bus_init failed %d\n", __FUNCTION__, ret));
+		dhd_os_sdunlock(dhdp);
+		return ret;
+	}
+#if defined(OOB_INTR_ONLY)
+	/* Host registration for OOB interrupt */
+	if (bcmsdh_register_oob_intr(dhdp)) {
+		dhd->wd_timer_valid = FALSE;
+		del_timer_sync(&dhd->timer);
+		DHD_ERROR(("%s Host failed to resgister for OOB\n", __FUNCTION__));
+		dhd_os_sdunlock(dhdp);
+		return -ENODEV;
+	}
+
+	/* Enable oob at firmware */
+	dhd_enable_oob_intr(dhd->pub.bus, TRUE);
+#endif /* defined(OOB_INTR_ONLY) */
+
+	/* If bus is not ready, can't come up */
+	if (dhd->pub.busstate != DHD_BUS_DATA) {
+		dhd->wd_timer_valid = FALSE;
+		del_timer_sync(&dhd->timer);
+		DHD_ERROR(("%s failed bus is not ready\n", __FUNCTION__));
+		dhd_os_sdunlock(dhdp);
+		return -ENODEV;
+	}
+
+	dhd_os_sdunlock(dhdp);
+
+#ifdef EMBEDDED_PLATFORM
+	bcm_mkiovar("event_msgs", dhdp->eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf));
+	dhdcdc_query_ioctl(dhdp, 0, WLC_GET_VAR, iovbuf, sizeof(iovbuf));
+	bcopy(iovbuf, dhdp->eventmask, WL_EVENTING_MASK_LEN);
+
+	setbit(dhdp->eventmask, WLC_E_SET_SSID);
+	setbit(dhdp->eventmask, WLC_E_PRUNE);
+	setbit(dhdp->eventmask, WLC_E_AUTH);
+	setbit(dhdp->eventmask, WLC_E_REASSOC);
+	setbit(dhdp->eventmask, WLC_E_REASSOC_IND);
+	setbit(dhdp->eventmask, WLC_E_DEAUTH_IND);
+	setbit(dhdp->eventmask, WLC_E_DISASSOC_IND);
+	setbit(dhdp->eventmask, WLC_E_DISASSOC);
+	setbit(dhdp->eventmask, WLC_E_JOIN);
+	setbit(dhdp->eventmask, WLC_E_ASSOC_IND);
+	setbit(dhdp->eventmask, WLC_E_PSK_SUP);
+	setbit(dhdp->eventmask, WLC_E_LINK);
+	setbit(dhdp->eventmask, WLC_E_NDIS_LINK);
+	setbit(dhdp->eventmask, WLC_E_MIC_ERROR);
+	setbit(dhdp->eventmask, WLC_E_PMKID_CACHE);
+	setbit(dhdp->eventmask, WLC_E_TXFAIL);
+	setbit(dhdp->eventmask, WLC_E_JOIN_START);
+	setbit(dhdp->eventmask, WLC_E_SCAN_COMPLETE);
+	setbit(dhdp->eventmask, WLC_E_RELOAD);
+#ifdef PNO_SUPPORT
+	setbit(dhdp->eventmask, WLC_E_PFN_NET_FOUND);
+#endif /* PNO_SUPPORT */
+
+/* enable dongle roaming event */
+	setbit(dhdp->eventmask, WLC_E_ROAM);
+
+	dhdp->pktfilter_count = 4;
+	/* Setup filter to allow only unicast */
+	dhdp->pktfilter[0] = "100 0 0 0 0x01 0x00";
+	dhdp->pktfilter[1] = NULL;
+	dhdp->pktfilter[2] = NULL;
+	dhdp->pktfilter[3] = NULL;
+#endif /* EMBEDDED_PLATFORM */
+
+	/* Bus is ready, do any protocol initialization */
+	if ((ret = dhd_prot_init(&dhd->pub)) < 0)
+		return ret;
+
+	return 0;
+}
+
+int
+dhd_iovar(dhd_pub_t *pub, int ifidx, char *name, char *cmd_buf, uint cmd_len, int set)
+{
+	char buf[strlen(name) + 1 + cmd_len];
+	int len = sizeof(buf);
+	wl_ioctl_t ioc;
+	int ret;
+
+	len = bcm_mkiovar(name, cmd_buf, cmd_len, buf, len);
+
+	memset(&ioc, 0, sizeof(ioc));
+
+	ioc.cmd = set? WLC_SET_VAR : WLC_GET_VAR;
+	ioc.buf = buf;
+	ioc.len = len;
+	ioc.set = set;
+
+	ret = dhd_prot_ioctl(pub, ifidx, &ioc, ioc.buf, ioc.len);
+	if (!set && ret >= 0)
+		memcpy(cmd_buf, buf, cmd_len);
+
+	return ret;
+}
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 31))
+static struct net_device_ops dhd_ops_pri = {
+	.ndo_open = dhd_open,
+	.ndo_stop = dhd_stop,
+	.ndo_get_stats = dhd_get_stats,
+	.ndo_do_ioctl = dhd_ioctl_entry,
+	.ndo_start_xmit = dhd_start_xmit,
+	.ndo_set_mac_address = dhd_set_mac_address,
+	.ndo_set_multicast_list = dhd_set_multicast_list,
+};
+
+static struct net_device_ops dhd_ops_virt = {
+	.ndo_get_stats = dhd_get_stats,
+	.ndo_do_ioctl = dhd_ioctl_entry,
+	.ndo_start_xmit = dhd_start_xmit,
+	.ndo_set_mac_address = dhd_set_mac_address,
+	.ndo_set_multicast_list = dhd_set_multicast_list,
+};
+#endif
+
+static int dhd_device_event(struct notifier_block *this, unsigned long event,
+				void *ptr)
+{
+	struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
+	dhd_info_t *dhd;
+	dhd_pub_t *dhd_pub;
+
+	if (!ifa)
+		return NOTIFY_DONE;
+
+	dhd = *(dhd_info_t **)netdev_priv(ifa->ifa_dev->dev);
+	dhd_pub = &dhd->pub;
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 31))
+	if (ifa->ifa_dev->dev->netdev_ops == &dhd_ops_pri) {
+#else
+	if (ifa->ifa_dev->dev->open == &dhd_open) {
+#endif
+		switch (event) {
+		case NETDEV_UP:
+			DHD_TRACE(("%s: [%s] Up IP: 0x%x\n",
+			    __FUNCTION__, ifa->ifa_label, ifa->ifa_address));
+
+			dhd_arp_cleanup(dhd_pub);
+			break;
+
+		case NETDEV_DOWN:
+			DHD_TRACE(("%s: [%s] Down IP: 0x%x\n",
+			    __FUNCTION__, ifa->ifa_label, ifa->ifa_address));
+
+			dhd_arp_cleanup(dhd_pub);
+			break;
+
+		default:
+			DHD_TRACE(("%s: [%s] Event: %lu\n",
+			    __FUNCTION__, ifa->ifa_label, event));
+			break;
+		}
+	}
+	return NOTIFY_DONE;
+}
+
+int
+dhd_net_attach(dhd_pub_t *dhdp, int ifidx)
+{
+	dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
+	struct net_device *net;
+	uint8 temp_addr[ETHER_ADDR_LEN] = { 0x00, 0x90, 0x4c, 0x11, 0x22, 0x33 };
+
+	DHD_TRACE(("%s: ifidx %d\n", __FUNCTION__, ifidx));
+
+	ASSERT(dhd && dhd->iflist[ifidx]);
+	net = dhd->iflist[ifidx]->net;
+
+	ASSERT(net);
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 31))
+	ASSERT(!net->open);
+	net->get_stats = dhd_get_stats;
+	net->do_ioctl = dhd_ioctl_entry;
+	net->hard_start_xmit = dhd_start_xmit;
+	net->set_mac_address = dhd_set_mac_address;
+	net->set_multicast_list = dhd_set_multicast_list;
+	net->open = net->stop = NULL;
+#else
+	ASSERT(!net->netdev_ops);
+	net->netdev_ops = &dhd_ops_virt;
+#endif
+
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 31))
+		net->open = dhd_open;
+		net->stop = dhd_stop;
+#else
+		net->netdev_ops = &dhd_ops_pri;
+#endif
+
+	/*
+	 * We have to use the primary MAC for virtual interfaces
+	 */
+	if (ifidx != 0) {
+		/* for virtual interfaces use the primary MAC  */
+		memcpy(temp_addr, dhd->pub.mac.octet, ETHER_ADDR_LEN);
+	}
+
+	if (ifidx == 1) {
+		DHD_TRACE(("%s ACCESS POINT MAC: \n", __FUNCTION__));
+		/*  ACCESSPOINT INTERFACE CASE */
+		temp_addr[0] |= 0x02;  /* set bit 2 , - Locally Administered address  */
+	}
+	net->hard_header_len = ETH_HLEN + dhd->pub.hdrlen;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
+	net->ethtool_ops = &dhd_ethtool_ops;
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */
+
+#if defined(CONFIG_WIRELESS_EXT)
+#if WIRELESS_EXT < 19
+	net->get_wireless_stats = dhd_get_wireless_stats;
+#endif /* WIRELESS_EXT < 19 */
+#if WIRELESS_EXT > 12
+	net->wireless_handlers = (struct iw_handler_def *)&wl_iw_handler_def;
+#endif /* WIRELESS_EXT > 12 */
+#endif /* defined(CONFIG_WIRELESS_EXT) */
+
+	dhd->pub.rxsz = net->mtu + net->hard_header_len + dhd->pub.hdrlen;
+
+	memcpy(net->dev_addr, temp_addr, ETHER_ADDR_LEN);
+
+	if (register_netdev(net) != 0) {
+		DHD_ERROR(("%s: couldn't register the net device\n", __FUNCTION__));
+		goto fail;
+	}
+
+	printf("%s: Broadcom Dongle Host Driver mac=%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", net->name,
+	       dhd->pub.mac.octet[0], dhd->pub.mac.octet[1], dhd->pub.mac.octet[2],
+	       dhd->pub.mac.octet[3], dhd->pub.mac.octet[4], dhd->pub.mac.octet[5]);
+
+
+#if defined(CONFIG_WIRELESS_EXT)
+#if defined(CONFIG_FIRST_SCAN)
+#ifdef SOFTAP
+	if (ifidx == 0)
+		/* Don't call for SOFTAP Interface in SOFTAP MODE */
+		wl_iw_iscan_set_scan_broadcast_prep(net, 1);
+#else
+		wl_iw_iscan_set_scan_broadcast_prep(net, 1);
+#endif /* SOFTAP */
+#endif /* CONFIG_FIRST_SCAN */
+#endif /* CONFIG_WIRELESS_EXT */
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+	up(&dhd_registration_sem);
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */
+	return 0;
+
+fail:
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 31))
+	net->open = NULL;
+#else
+	net->netdev_ops = NULL;
+#endif
+	return BCME_ERROR;
+}
+
+void
+dhd_bus_detach(dhd_pub_t *dhdp)
+{
+	dhd_info_t *dhd;
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	if (dhdp) {
+		dhd = (dhd_info_t *)dhdp->info;
+		if (dhd) {
+			/* Stop the protocol module */
+			dhd_prot_stop(&dhd->pub);
+
+			/* Stop the bus module */
+			dhd_bus_stop(dhd->pub.bus, TRUE);
+#if defined(OOB_INTR_ONLY)
+			bcmsdh_unregister_oob_intr();
+#endif /* defined(OOB_INTR_ONLY) */
+
+			/* Clear the watchdog timer */
+			dhd->wd_timer_valid = FALSE;
+			del_timer_sync(&dhd->timer);
+		}
+	}
+}
+
+void
+dhd_detach(dhd_pub_t *dhdp)
+{
+	dhd_info_t *dhd;
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	if (dhdp) {
+		dhd = (dhd_info_t *)dhdp->info;
+		if (dhd) {
+			dhd_if_t *ifp;
+			int i;
+
+			unregister_inetaddr_notifier(&dhd_notifier);
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+			if (dhd->early_suspend.suspend)
+				unregister_early_suspend(&dhd->early_suspend);
+#endif	/* defined(CONFIG_HAS_EARLYSUSPEND) */
+#if defined(CONFIG_WIRELESS_EXT)
+			/* Attach and link in the iw */
+			wl_iw_detach();
+#endif
+			if (dhd->sysioc_pid >= 0) {
+				KILL_PROC(dhd->sysioc_pid, SIGTERM);
+				wait_for_completion(&dhd->sysioc_exited);
+			}
+
+			for (i = 1; i < DHD_MAX_IFS; i++)
+				if (dhd->iflist[i]) {
+					dhd->iflist[i]->state = WLC_E_IF_DEL;
+					dhd->iflist[i]->idx = i;
+					dhd_op_if(dhd->iflist[i]);
+				}
+
+			ifp = dhd->iflist[0];
+			ASSERT(ifp);
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 31))
+			if (ifp->net->open) {
+#else
+			if (ifp->net->netdev_ops == &dhd_ops_pri) {
+#endif
+				dhd_stop(ifp->net);
+				unregister_netdev(ifp->net);
+			}
+
+			if (dhd->watchdog_pid >= 0)
+			{
+				KILL_PROC(dhd->watchdog_pid, SIGTERM);
+				wait_for_completion(&dhd->watchdog_exited);
+			}
+
+			if (dhd->dpc_pid >= 0)
+			{
+				KILL_PROC(dhd->dpc_pid, SIGTERM);
+				wait_for_completion(&dhd->dpc_exited);
+			}
+			else
+				tasklet_kill(&dhd->tasklet);
+
+			dhd_bus_detach(dhdp);
+
+			if (dhdp->prot)
+				dhd_prot_detach(dhdp);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP)
+			unregister_pm_notifier(&dhd_sleep_pm_notifier);
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */
+			free_netdev(ifp->net);
+#ifdef CONFIG_HAS_WAKELOCK
+			wake_lock_destroy(&dhd->wl_wifi);
+			wake_lock_destroy(&dhd->wl_rxwake);
+#endif
+			MFREE(dhd->pub.osh, ifp, sizeof(*ifp));
+			MFREE(dhd->pub.osh, dhd, sizeof(*dhd));
+		}
+	}
+}
+
+static void __exit
+dhd_module_cleanup(void)
+{
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	dhd_bus_unregister();
+#if defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC)
+	wifi_del_dev();
+#endif
+	/* Call customer gpio to turn off power with WL_REG_ON signal */
+	dhd_customer_gpio_wlan_ctrl(WLAN_POWER_OFF);
+}
+
+static int __init
+dhd_module_init(void)
+{
+	int error;
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	/* Sanity check on the module parameters */
+	do {
+		/* Both watchdog and DPC as tasklets are ok */
+		if ((dhd_watchdog_prio < 0) && (dhd_dpc_prio < 0))
+			break;
+
+		/* If both watchdog and DPC are threads, TX must be deferred */
+		if ((dhd_watchdog_prio >= 0) && (dhd_dpc_prio >= 0) && dhd_deferred_tx)
+			break;
+
+		DHD_ERROR(("Invalid module parameters.\n"));
+		return -EINVAL;
+	} while (0);
+
+	/* Call customer gpio to turn on power with WL_REG_ON signal */
+	dhd_customer_gpio_wlan_ctrl(WLAN_POWER_ON);
+
+#if defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC)
+	sema_init(&wifi_control_sem, 0);
+
+	error = wifi_add_dev();
+	if (error) {
+		DHD_ERROR(("%s: platform_driver_register failed\n", __FUNCTION__));
+		goto fail_0;
+	}
+
+	/* Waiting callback after platform_driver_register is done or exit with error */
+	if (down_timeout(&wifi_control_sem,  msecs_to_jiffies(5000)) != 0) {
+		error = -EINVAL;
+		DHD_ERROR(("%s: platform_driver_register timeout\n", __FUNCTION__));
+		goto fail_1;
+	}
+#endif /* #if defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC) */
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+	sema_init(&dhd_registration_sem, 0);
+#endif 
+
+	error = dhd_bus_register();
+
+	if (!error)
+		printf("\n%s\n", dhd_version);
+	else {
+		DHD_ERROR(("%s: sdio_register_driver failed\n", __FUNCTION__));
+		goto fail_1;
+	}
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+	/*
+	 * Wait till MMC sdio_register_driver callback called and made driver attach.
+	 * It's needed to make sync up exit from dhd insmod  and
+	 * Kernel MMC sdio device callback registration
+	 */
+	if (down_timeout(&dhd_registration_sem,  msecs_to_jiffies(DHD_REGISTRATION_TIMEOUT)) != 0) {
+		error = -EINVAL;
+		DHD_ERROR(("%s: sdio_register_driver timeout\n", __FUNCTION__));
+		goto fail_2;
+	}
+#endif
+	return error;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+fail_2:
+	dhd_bus_unregister();
+#endif
+fail_1:
+#if defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC)
+	wifi_del_dev();
+fail_0:
+#endif /* defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC) */
+
+	/* Call customer gpio to turn off power with WL_REG_ON signal */
+	dhd_customer_gpio_wlan_ctrl(WLAN_POWER_OFF);
+
+	return error;
+}
+
+module_init(dhd_module_init);
+module_exit(dhd_module_cleanup);
+
+/*
+ * OS specific functions required to implement DHD driver in OS independent way
+ */
+int
+dhd_os_proto_block(dhd_pub_t *pub)
+{
+	dhd_info_t *dhd = (dhd_info_t *)(pub->info);
+
+	if (dhd) {
+		mutex_lock(&dhd->proto_sem);
+		return 1;
+	}
+
+	return 0;
+}
+
+int
+dhd_os_proto_unblock(dhd_pub_t *pub)
+{
+	dhd_info_t *dhd = (dhd_info_t *)(pub->info);
+
+	if (dhd) {
+		mutex_unlock(&dhd->proto_sem);
+		return 1;
+	}
+
+	return 0;
+}
+
+unsigned int
+dhd_os_get_ioctl_resp_timeout(void)
+{
+	return ((unsigned int)dhd_ioctl_timeout_msec);
+}
+
+void
+dhd_os_set_ioctl_resp_timeout(unsigned int timeout_msec)
+{
+	dhd_ioctl_timeout_msec = (int)timeout_msec;
+}
+
+int
+dhd_os_ioctl_resp_wait(dhd_pub_t *pub, uint *condition, bool *pending)
+{
+	dhd_info_t *dhd = (dhd_info_t *)(pub->info);
+	DECLARE_WAITQUEUE(wait, current);
+	int timeout = dhd_ioctl_timeout_msec;
+
+	/* Convert timeout in millsecond to jiffies */
+	/* timeout = timeout * HZ / 1000; */
+	timeout = msecs_to_jiffies(timeout);
+
+	/* Wait until control frame is available */
+	add_wait_queue(&dhd->ioctl_resp_wait, &wait);
+	set_current_state(TASK_INTERRUPTIBLE);
+	smp_mb();
+	while (!(*condition) && (!signal_pending(current) && timeout)) {
+		timeout = schedule_timeout(timeout);
+		smp_mb();
+	}
+
+	if (signal_pending(current))
+		*pending = TRUE;
+
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&dhd->ioctl_resp_wait, &wait);
+
+	return timeout;
+}
+
+int
+dhd_os_ioctl_resp_wake(dhd_pub_t *pub)
+{
+	dhd_info_t *dhd = (dhd_info_t *)(pub->info);
+
+	if (waitqueue_active(&dhd->ioctl_resp_wait)) {
+		wake_up_interruptible(&dhd->ioctl_resp_wait);
+	}
+
+	return 0;
+}
+
+void
+dhd_os_wd_timer(void *bus, uint wdtick)
+{
+	dhd_pub_t *pub = bus;
+	dhd_info_t *dhd = (dhd_info_t *)pub->info;
+	unsigned long flags;
+	int del_timer_flag = FALSE;
+
+	flags = dhd_os_spin_lock(pub);
+
+	/* don't start the wd until fw is loaded */
+	if (pub->busstate != DHD_BUS_DOWN) {
+		if (wdtick) {
+			dhd_watchdog_ms = (uint)wdtick;
+			dhd->wd_timer_valid = TRUE;
+			/* Re arm the timer, at last watchdog period */
+			mod_timer(&dhd->timer, jiffies + dhd_watchdog_ms * HZ / 1000);
+		} else if (dhd->wd_timer_valid == TRUE) {
+			/* Totally stop the timer */
+			dhd->wd_timer_valid = FALSE;
+			del_timer_flag = TRUE;
+		}
+	}
+	dhd_os_spin_unlock(pub, flags);
+	if (del_timer_flag) {
+		del_timer_sync(&dhd->timer);
+	}
+}
+
+void *
+dhd_os_open_image(char *filename)
+{
+	struct file *fp;
+
+	fp = filp_open(filename, O_RDONLY, 0);
+	/*
+	 * 2.6.11 (FC4) supports filp_open() but later revs don't?
+	 * Alternative:
+	 * fp = open_namei(AT_FDCWD, filename, O_RD, 0);
+	 * ???
+	 */
+	 if (IS_ERR(fp))
+		 fp = NULL;
+
+	 return fp;
+}
+
+int
+dhd_os_get_image_block(char *buf, int len, void *image)
+{
+	struct file *fp = (struct file *)image;
+	int rdlen;
+
+	if (!image)
+		return 0;
+
+	rdlen = kernel_read(fp, fp->f_pos, buf, len);
+	if (rdlen > 0)
+		fp->f_pos += rdlen;
+
+	return rdlen;
+}
+
+void
+dhd_os_close_image(void *image)
+{
+	if (image)
+		filp_close((struct file *)image, NULL);
+}
+
+
+void
+dhd_os_sdlock(dhd_pub_t *pub)
+{
+	dhd_info_t *dhd;
+
+	dhd = (dhd_info_t *)(pub->info);
+
+	if (dhd->threads_only)
+		mutex_lock(&dhd->sdsem);
+	else
+		spin_lock_bh(&dhd->sdlock);
+}
+
+void
+dhd_os_sdunlock(dhd_pub_t *pub)
+{
+	dhd_info_t *dhd;
+
+	dhd = (dhd_info_t *)(pub->info);
+
+	if (dhd->threads_only)
+		mutex_unlock(&dhd->sdsem);
+	else
+		spin_unlock_bh(&dhd->sdlock);
+}
+
+void
+dhd_os_sdlock_txq(dhd_pub_t *pub)
+{
+	dhd_info_t *dhd;
+
+	dhd = (dhd_info_t *)(pub->info);
+	spin_lock_bh(&dhd->txqlock);
+}
+
+void
+dhd_os_sdunlock_txq(dhd_pub_t *pub)
+{
+	dhd_info_t *dhd;
+
+	dhd = (dhd_info_t *)(pub->info);
+	spin_unlock_bh(&dhd->txqlock);
+}
+void
+dhd_os_sdlock_rxq(dhd_pub_t *pub)
+{
+}
+void
+dhd_os_sdunlock_rxq(dhd_pub_t *pub)
+{
+}
+
+void
+dhd_os_sdtxlock(dhd_pub_t *pub)
+{
+	dhd_os_sdlock(pub);
+}
+
+void
+dhd_os_sdtxunlock(dhd_pub_t *pub)
+{
+	dhd_os_sdunlock(pub);
+}
+
+#ifdef DHD_USE_STATIC_BUF
+void * dhd_os_prealloc(int section, unsigned long size)
+{
+#if defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC)
+	void *alloc_ptr = NULL;
+	if (wifi_control_data && wifi_control_data->mem_prealloc)
+	{
+		alloc_ptr = wifi_control_data->mem_prealloc(section, size);
+		if (alloc_ptr)
+		{
+			DHD_INFO(("success alloc section %d\n", section));
+			bzero(alloc_ptr, size);
+			return alloc_ptr;
+		}
+	}
+
+	DHD_ERROR(("can't alloc section %d\n", section));
+	return 0;
+#else
+return MALLOC(0, size);
+#endif /* #if defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC) */
+}
+#endif /* DHD_USE_STATIC_BUF */
+#if defined(CONFIG_WIRELESS_EXT)
+struct iw_statistics *
+dhd_get_wireless_stats(struct net_device *dev)
+{
+	int res = 0;
+	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+
+	res = wl_iw_get_wireless_stats(dev, &dhd->iw.wstats);
+
+	if (res == 0)
+		return &dhd->iw.wstats;
+	else
+		return NULL;
+}
+#endif /* defined(CONFIG_WIRELESS_EXT) */
+
+static int
+dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata,
+	wl_event_msg_t *event, void **data)
+{
+	int bcmerror = 0;
+
+	ASSERT(dhd != NULL);
+
+	bcmerror = wl_host_event(dhd, ifidx, pktdata, event, data);
+	if (bcmerror != BCME_OK)
+		return (bcmerror);
+
+#if defined(CONFIG_WIRELESS_EXT)
+	ASSERT(dhd->iflist[*ifidx] != NULL);
+
+	if (ntoh32(event->event_type) == WLC_E_IF) {
+		DHD_INFO(("<0> interface:%d OP:%d don't pass to wext,"
+			"net_device might not be created yet\n",
+				*ifidx, ntoh32(event->event_type)));
+		return bcmerror;
+	}
+
+	ASSERT(dhd->iflist[*ifidx]->net != NULL);
+
+	if (dhd->iflist[*ifidx]->net)
+		wl_iw_event(dhd->iflist[*ifidx]->net, event, *data);
+#endif /* defined(CONFIG_WIRELESS_EXT) */
+
+	return (bcmerror);
+}
+
+/* send up locally generated event */
+void
+dhd_sendup_event(dhd_pub_t *dhdp, wl_event_msg_t *event, void *data)
+{
+	switch (ntoh32(event->event_type)) {
+	default:
+		break;
+	}
+}
+
+void dhd_wait_for_event(dhd_pub_t *dhd, bool *lockvar)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+	struct dhd_info *dhdinfo =  dhd->info;
+	dhd_os_sdunlock(dhd);
+	wait_event_interruptible_timeout(dhdinfo->ctrl_wait, (*lockvar == FALSE), HZ * 2);
+	dhd_os_sdlock(dhd);
+#endif
+	return;
+}
+
+void dhd_wait_event_wakeup(dhd_pub_t *dhd)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+	struct dhd_info *dhdinfo =  dhd->info;
+	if (waitqueue_active(&dhdinfo->ctrl_wait))
+		wake_up_interruptible(&dhdinfo->ctrl_wait);
+#endif
+	return;
+}
+
+int
+dhd_dev_reset(struct net_device *dev, uint8 flag)
+{
+	int ret;
+
+	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+
+	ret = dhd_bus_devreset(&dhd->pub, flag);
+	if (ret) {
+		DHD_ERROR(("%s: dhd_bus_devreset: %d\n", __FUNCTION__, ret));
+		return ret;
+	}
+	DHD_ERROR(("%s: WLAN %s DONE\n", __FUNCTION__, flag ? "OFF" : "ON"));
+
+	return ret;
+}
+
+int net_os_set_suspend_disable(struct net_device *dev, int val)
+{
+	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+	int ret = 0;
+
+	if (dhd) {
+		ret = dhd->pub.suspend_disable_flag;
+		dhd->pub.suspend_disable_flag = val;
+	}
+	return ret;
+}
+
+int net_os_set_suspend(struct net_device *dev, int val)
+{
+	int ret = 0;
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+
+	if (dhd) {
+		dhd_os_proto_block(&dhd->pub);
+		ret = dhd_set_suspend(val, &dhd->pub);
+		dhd_os_proto_unblock(&dhd->pub);
+	}
+#endif /* defined(CONFIG_HAS_EARLYSUSPEND) */
+	return ret;
+}
+
+int net_os_set_dtim_skip(struct net_device *dev, int val)
+{
+	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+
+	if (dhd)
+		dhd->pub.dtim_skip = val;
+
+	return 0;
+}
+
+int net_os_rxfilter_add_remove(struct net_device *dev, int add_remove, int num)
+{
+	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+	char *filterp = NULL;
+	int ret = 0;
+
+	if (!dhd || (num == DHD_UNICAST_FILTER_NUM))
+		return ret;
+	if (num >= dhd->pub.pktfilter_count)
+		return -EINVAL;
+	if (add_remove) {
+		switch (num) {
+		case DHD_BROADCAST_FILTER_NUM:
+			filterp = "101 0 0 0 0xFFFFFFFFFFFF 0xFFFFFFFFFFFF";
+			break;
+		case DHD_MULTICAST4_FILTER_NUM:
+			filterp = "102 0 0 0 0xFFFFFF 0x01005E";
+			break;
+		case DHD_MULTICAST6_FILTER_NUM:
+			filterp = "103 0 0 0 0xFFFF 0x3333";
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+	dhd->pub.pktfilter[num] = filterp;
+	return ret;
+}
+
+int net_os_set_packet_filter(struct net_device *dev, int val)
+{
+	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+	int ret = 0;
+
+	/* Packet filtering is set only if we still in early-suspend and
+	 * we need either to turn it ON or turn it OFF
+	 * We can always turn it OFF in case of early-suspend, but we turn it
+	 * back ON only if suspend_disable_flag was not set
+	*/
+	if (dhd && dhd->pub.up) {
+		dhd_os_proto_block(&dhd->pub);
+		if (dhd->pub.in_suspend) {
+			if (!val || (val && !dhd->pub.suspend_disable_flag))
+				dhd_set_packet_filter(val, &dhd->pub);
+		}
+		dhd_os_proto_unblock(&dhd->pub);
+	}
+	return ret;
+}
+
+
+void
+dhd_dev_init_ioctl(struct net_device *dev)
+{
+	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+
+	dhd_preinit_ioctls(&dhd->pub);
+}
+
+#ifdef PNO_SUPPORT
+/* Linux wrapper to call common dhd_pno_clean */
+int
+dhd_dev_pno_reset(struct net_device *dev)
+{
+	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+
+	return (dhd_pno_clean(&dhd->pub));
+}
+
+
+/* Linux wrapper to call common dhd_pno_enable */
+int
+dhd_dev_pno_enable(struct net_device *dev,  int pfn_enabled)
+{
+	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+
+	return (dhd_pno_enable(&dhd->pub, pfn_enabled));
+}
+
+
+/* Linux wrapper to call common dhd_pno_set */
+int
+dhd_dev_pno_set(struct net_device *dev, wlc_ssid_t* ssids_local, int nssid,
+		ushort scan_fr, int pno_repeat, int pno_freq_expo_max)
+{
+	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+
+	return (dhd_pno_set(&dhd->pub, ssids_local, nssid, scan_fr, pno_repeat, pno_freq_expo_max));
+}
+
+/* Linux wrapper to get  pno status */
+int
+dhd_dev_get_pno_status(struct net_device *dev)
+{
+	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+
+	return (dhd_pno_get_status(&dhd->pub));
+}
+
+#endif /* PNO_SUPPORT */
+
+int net_os_send_hang_message(struct net_device *dev)
+{
+	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+	int ret = 0;
+
+	if (dhd) {
+		if (!dhd->pub.hang_was_sent) {
+			dhd->pub.hang_was_sent = 1;
+			ret = wl_iw_send_priv_event(dev, "HANG");
+		}
+	}
+	return ret;
+}
+
+void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec)
+{
+	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+
+	if (dhd && dhd->pub.up)
+		memcpy(&dhd->pub.dhd_cspec, cspec, sizeof(wl_country_t));
+}
+
+char *dhd_bus_country_get(struct net_device *dev)
+{
+	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+
+	if (dhd && (dhd->pub.dhd_cspec.ccode[0] != 0))
+		return dhd->pub.dhd_cspec.ccode;
+	return NULL;
+}
+
+void dhd_os_start_lock(dhd_pub_t *pub)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25))
+	dhd_info_t *dhd = (dhd_info_t *)(pub->info);
+
+	if (dhd)
+		mutex_lock(&dhd->wl_start_lock);
+#endif
+}
+
+void dhd_os_start_unlock(dhd_pub_t *pub)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25))
+	dhd_info_t *dhd = (dhd_info_t *)(pub->info);
+
+	if (dhd)
+		mutex_unlock(&dhd->wl_start_lock);
+#endif
+}
+
+static int
+dhd_get_pend_8021x_cnt(dhd_info_t *dhd)
+{
+	return (atomic_read(&dhd->pend_8021x_cnt));
+}
+
+#define MAX_WAIT_FOR_8021X_TX	10
+
+int
+dhd_wait_pend8021x(struct net_device *dev)
+{
+	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+	int timeout = 10 * HZ / 1000;
+	int ntimes = MAX_WAIT_FOR_8021X_TX;
+	int pend = dhd_get_pend_8021x_cnt(dhd);
+
+	while (ntimes && pend) {
+		if (pend) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule_timeout(timeout);
+			set_current_state(TASK_RUNNING);
+			ntimes--;
+		}
+		pend = dhd_get_pend_8021x_cnt(dhd);
+	}
+	return pend;
+}
+
+#ifdef DHD_DEBUG
+int
+write_to_file(dhd_pub_t *dhd, uint8 *buf, int size)
+{
+	int ret = 0;
+	struct file *fp;
+	mm_segment_t old_fs;
+	loff_t pos = 0;
+
+	/* change to KERNEL_DS address limit */
+	old_fs = get_fs();
+	set_fs(KERNEL_DS);
+
+	/* open file to write */
+	fp = filp_open("/tmp/mem_dump", O_WRONLY|O_CREAT, 0640);
+	if (!fp) {
+		printf("%s: open file error\n", __FUNCTION__);
+		ret = -1;
+		goto exit;
+	}
+
+	/* Write buf to file */
+	fp->f_op->write(fp, buf, size, &pos);
+
+exit:
+	/* free buf before return */
+	MFREE(dhd->osh, buf, size);
+	/* close file before return */
+	if (fp)
+		filp_close(fp, current->files);
+	/* restore previous address limit */
+	set_fs(old_fs);
+
+	return ret;
+}
+#endif /* DHD_DEBUG */
+
+int dhd_os_wake_lock_timeout(dhd_pub_t *pub)
+{
+	dhd_info_t *dhd = (dhd_info_t *)(pub->info);
+	unsigned long flags;
+	int ret = 0;
+
+	if (dhd) {
+		spin_lock_irqsave(&dhd->wl_lock, flags);
+		ret = dhd->wl_packet;
+#ifdef CONFIG_HAS_WAKELOCK
+		if (dhd->wl_packet)
+			wake_lock_timeout(&dhd->wl_rxwake, HZ);
+#endif
+		dhd->wl_packet = 0;
+		spin_unlock_irqrestore(&dhd->wl_lock, flags);
+	}
+	/* printk("%s: %d\n", __FUNCTION__, ret); */
+	return ret;
+}
+
+int net_os_wake_lock_timeout(struct net_device *dev)
+{
+	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+	int ret = 0;
+
+	if (dhd)
+		ret = dhd_os_wake_lock_timeout(&dhd->pub);
+	return ret;
+}
+
+int dhd_os_wake_lock_timeout_enable(dhd_pub_t *pub)
+{
+	dhd_info_t *dhd = (dhd_info_t *)(pub->info);
+	unsigned long flags;
+
+	if (dhd) {
+		spin_lock_irqsave(&dhd->wl_lock, flags);
+		dhd->wl_packet = 1;
+		spin_unlock_irqrestore(&dhd->wl_lock, flags);
+	}
+	/* printk("%s\n",__func__); */
+	return 0;
+}
+
+int net_os_wake_lock_timeout_enable(struct net_device *dev)
+{
+	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+	int ret = 0;
+
+	if (dhd)
+		ret = dhd_os_wake_lock_timeout_enable(&dhd->pub);
+	return ret;
+}
+
+int dhd_os_wake_lock(dhd_pub_t *pub)
+{
+	dhd_info_t *dhd = (dhd_info_t *)(pub->info);
+	unsigned long flags;
+	int ret = 0;
+
+	if (dhd) {
+		spin_lock_irqsave(&dhd->wl_lock, flags);
+#ifdef CONFIG_HAS_WAKELOCK
+		if (!dhd->wl_count)
+			wake_lock(&dhd->wl_wifi);
+#endif
+		dhd->wl_count++;
+		ret = dhd->wl_count;
+		spin_unlock_irqrestore(&dhd->wl_lock, flags);
+	}
+	/* printk("%s: %d\n", __FUNCTION__, ret); */
+	return ret;
+}
+
+int net_os_wake_lock(struct net_device *dev)
+{
+	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+	int ret = 0;
+
+	if (dhd)
+		ret = dhd_os_wake_lock(&dhd->pub);
+	return ret;
+}
+
+int dhd_os_wake_unlock(dhd_pub_t *pub)
+{
+	dhd_info_t *dhd = (dhd_info_t *)(pub->info);
+	unsigned long flags;
+	int ret = 0;
+
+	dhd_os_wake_lock_timeout(pub);
+	if (dhd) {
+		spin_lock_irqsave(&dhd->wl_lock, flags);
+		if (dhd->wl_count) {
+			dhd->wl_count--;
+#ifdef CONFIG_HAS_WAKELOCK
+			if (!dhd->wl_count)
+				wake_unlock(&dhd->wl_wifi);
+#endif
+			ret = dhd->wl_count;
+		}
+		spin_unlock_irqrestore(&dhd->wl_lock, flags);
+	}
+	/* printk("%s: %d\n", __FUNCTION__, ret); */
+	return ret;
+}
+
+int net_os_wake_unlock(struct net_device *dev)
+{
+	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+	int ret = 0;
+
+	if (dhd)
+		ret = dhd_os_wake_unlock(&dhd->pub);
+	return ret;
+}
+
+unsigned long dhd_os_spin_lock(dhd_pub_t *pub)
+{
+	dhd_info_t *dhd = (dhd_info_t *)(pub->info);
+	unsigned long flags = 0;
+
+	if (dhd)
+		spin_lock_irqsave(&dhd->dhd_lock, flags);
+
+	return flags;
+}
+
+void dhd_os_spin_unlock(dhd_pub_t *pub, unsigned long flags)
+{
+	dhd_info_t *dhd = (dhd_info_t *)(pub->info);
+
+	if (dhd)
+		spin_unlock_irqrestore(&dhd->dhd_lock, flags);
+}
diff --git a/drivers/net/wireless/bcm4329/dhd_linux_sched.c b/drivers/net/wireless/bcm4329/dhd_linux_sched.c
new file mode 100644
index 0000000..480b416
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/dhd_linux_sched.c
@@ -0,0 +1,38 @@
+/*
+ * Expose some of the kernel scheduler routines
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: dhd_linux_sched.c,v 1.1.34.1.6.1 2009/01/16 01:17:40 Exp $
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linuxver.h>
+
+int setScheduler(struct task_struct *p, int policy, struct sched_param *param)
+{
+	int rc = 0;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+	rc = sched_setscheduler(p, policy, param);
+#endif /* LinuxVer */
+	return rc;
+}
diff --git a/drivers/net/wireless/bcm4329/dhd_proto.h b/drivers/net/wireless/bcm4329/dhd_proto.h
new file mode 100644
index 0000000..7ef6929
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/dhd_proto.h
@@ -0,0 +1,102 @@
+/*
+ * Header file describing the internal (inter-module) DHD interfaces.
+ *
+ * Provides type definitions and function prototypes used to link the
+ * DHD OS, bus, and protocol modules.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: dhd_proto.h,v 1.2.82.1.4.1.16.7 2010/05/10 12:54:59 Exp $
+ */
+
+#ifndef _dhd_proto_h_
+#define _dhd_proto_h_
+
+#include <dhdioctl.h>
+#include <wlioctl.h>
+
+#ifndef IOCTL_RESP_TIMEOUT
+#define IOCTL_RESP_TIMEOUT  3000 /* In milli second */
+#endif
+
+#ifndef IOCTL_CHIP_ACTIVE_TIMEOUT
+#define IOCTL_CHIP_ACTIVE_TIMEOUT  10 /* In milli second */
+#endif
+
+/*
+ * Exported from the dhd protocol module (dhd_cdc, dhd_rndis)
+ */
+
+/* Linkage, sets prot link and updates hdrlen in pub */
+extern int dhd_prot_attach(dhd_pub_t *dhdp);
+
+/* Unlink, frees allocated protocol memory (including dhd_prot) */
+extern void dhd_prot_detach(dhd_pub_t *dhdp);
+
+/* Initialize protocol: sync w/dongle state.
+ * Sets dongle media info (iswl, drv_version, mac address).
+ */
+extern int dhd_prot_init(dhd_pub_t *dhdp);
+
+/* Stop protocol: sync w/dongle state. */
+extern void dhd_prot_stop(dhd_pub_t *dhdp);
+
+extern bool dhd_proto_fcinfo(dhd_pub_t *dhd, void *pktbuf, uint8 *fcbits);
+
+/* Add any protocol-specific data header.
+ * Caller must reserve prot_hdrlen prepend space.
+ */
+extern void dhd_prot_hdrpush(dhd_pub_t *, int ifidx, void *txp);
+
+/* Remove any protocol-specific data header. */
+extern int dhd_prot_hdrpull(dhd_pub_t *, int *ifidx, void *rxp);
+
+/* Use protocol to issue ioctl to dongle */
+extern int dhd_prot_ioctl(dhd_pub_t *dhd, int ifidx, wl_ioctl_t * ioc, void * buf, int len);
+
+/* Check for and handle local prot-specific iovar commands */
+extern int dhd_prot_iovar_op(dhd_pub_t *dhdp, const char *name,
+                             void *params, int plen, void *arg, int len, bool set);
+
+/* Add prot dump output to a buffer */
+extern void dhd_prot_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf);
+
+/* Update local copy of dongle statistics */
+extern void dhd_prot_dstats(dhd_pub_t *dhdp);
+
+extern int dhd_ioctl(dhd_pub_t * dhd_pub, dhd_ioctl_t *ioc, void * buf, uint buflen);
+
+extern int dhd_preinit_ioctls(dhd_pub_t *dhd);
+
+/********************************
+ * For version-string expansion *
+ */
+#if defined(BDC)
+#define DHD_PROTOCOL "bdc"
+#elif defined(CDC)
+#define DHD_PROTOCOL "cdc"
+#elif defined(RNDIS)
+#define DHD_PROTOCOL "rndis"
+#else
+#define DHD_PROTOCOL "unknown"
+#endif /* proto */
+
+#endif /* _dhd_proto_h_ */
diff --git a/drivers/net/wireless/bcm4329/dhd_sdio.c b/drivers/net/wireless/bcm4329/dhd_sdio.c
new file mode 100644
index 0000000..e9093e8
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/dhd_sdio.c
@@ -0,0 +1,5824 @@
+/*
+ * DHD Bus Module for SDIO
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: dhd_sdio.c,v 1.157.2.27.2.33.2.129.4.1 2010/09/02 23:13:16 Exp $
+ */
+
+#include <typedefs.h>
+#include <osl.h>
+#include <bcmsdh.h>
+
+#ifdef BCMEMBEDIMAGE
+#include BCMEMBEDIMAGE
+#endif /* BCMEMBEDIMAGE */
+
+#include <bcmdefs.h>
+#include <bcmutils.h>
+#include <bcmendian.h>
+#include <bcmdevs.h>
+#include <siutils.h>
+#include <hndpmu.h>
+#include <hndsoc.h>
+#include <sbchipc.h>
+#include <sbhnddma.h>
+#include <sdio.h>
+#include <sbsdio.h>
+#include <sbsdpcmdev.h>
+#include <bcmsdpcm.h>
+
+#include <proto/ethernet.h>
+#include <proto/802.1d.h>
+#include <proto/802.11.h>
+
+#include <dngl_stats.h>
+#include <dhd.h>
+#include <dhd_bus.h>
+#include <dhd_proto.h>
+#include <dhd_dbg.h>
+#include <dhdioctl.h>
+#include <sdiovar.h>
+
+#ifdef DHD_DEBUG
+#include <hndrte_cons.h>
+#endif /* DHD_DEBUG */
+#ifdef DHD_DEBUG_TRAP
+#include <hndrte_armtrap.h>
+#endif /* DHD_DEBUG_TRAP */
+
+#define QLEN		256	/* bulk rx and tx queue lengths */
+#define FCHI		(QLEN - 10)
+#define FCLOW		(FCHI / 2)
+#define PRIOMASK	7
+
+#define TXRETRIES	2	/* # of retries for tx frames */
+
+#if defined(CONFIG_MACH_SANDGATE2G)
+#define DHD_RXBOUND	250	/* Default for max rx frames in one scheduling */
+#else
+#define DHD_RXBOUND	50	/* Default for max rx frames in one scheduling */
+#endif /* defined(CONFIG_MACH_SANDGATE2G) */
+
+#define DHD_TXBOUND	20	/* Default for max tx frames in one scheduling */
+
+#define DHD_TXMINMAX	1	/* Max tx frames if rx still pending */
+
+#define MEMBLOCK	2048		/* Block size used for downloading of dongle image */
+#define MAX_DATA_BUF	(32 * 1024)	/* Must be large enough to hold biggest possible glom */
+
+/* Packet alignment for most efficient SDIO (can change based on platform) */
+#ifndef DHD_SDALIGN
+#define DHD_SDALIGN	32
+#endif
+#if !ISPOWEROF2(DHD_SDALIGN)
+#error DHD_SDALIGN is not a power of 2!
+#endif
+
+#ifndef DHD_FIRSTREAD
+#define DHD_FIRSTREAD	32
+#endif
+#if !ISPOWEROF2(DHD_FIRSTREAD)
+#error DHD_FIRSTREAD is not a power of 2!
+#endif
+
+/* Total length of frame header for dongle protocol */
+#define SDPCM_HDRLEN	(SDPCM_FRAMETAG_LEN + SDPCM_SWHEADER_LEN)
+#ifdef SDTEST
+#define SDPCM_RESERVE	(SDPCM_HDRLEN + SDPCM_TEST_HDRLEN + DHD_SDALIGN)
+#else
+#define SDPCM_RESERVE	(SDPCM_HDRLEN + DHD_SDALIGN)
+#endif
+
+/* Space for header read, limit for data packets */
+#ifndef MAX_HDR_READ
+#define MAX_HDR_READ	32
+#endif
+#if !ISPOWEROF2(MAX_HDR_READ)
+#error MAX_HDR_READ is not a power of 2!
+#endif
+
+#define MAX_RX_DATASZ	2048
+
+/* Maximum milliseconds to wait for F2 to come up */
+#define DHD_WAIT_F2RDY	3000
+
+/* Bump up limit on waiting for HT to account for first startup;
+ * if the image is doing a CRC calculation before programming the PMU
+ * for HT availability, it could take a couple hundred ms more, so
+ * max out at a 1 second (1000000us).
+ */
+#if (PMU_MAX_TRANSITION_DLY < 1000000)
+#undef PMU_MAX_TRANSITION_DLY
+#define PMU_MAX_TRANSITION_DLY 1000000
+#endif
+
+/* Value for ChipClockCSR during initial setup */
+#define DHD_INIT_CLKCTL1	(SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ)
+#define DHD_INIT_CLKCTL2	(SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP)
+
+/* Flags for SDH calls */
+#define F2SYNC	(SDIO_REQ_4BYTE | SDIO_REQ_FIXED)
+
+/* Packet free applicable unconditionally for sdio and sdspi.  Conditional if
+ * bufpool was present for gspi bus.
+ */
+#define PKTFREE2()		if ((bus->bus != SPI_BUS) || bus->usebufpool) \
+					PKTFREE(bus->dhd->osh, pkt, FALSE);
+DHD_SPINWAIT_SLEEP_INIT(sdioh_spinwait_sleep);
+extern int dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len);
+
+extern void bcmsdh_set_irq(int flag);
+
+#ifdef DHD_DEBUG
+/* Device console log buffer state */
+typedef struct dhd_console {
+	uint		count;			/* Poll interval msec counter */
+	uint		log_addr;		/* Log struct address (fixed) */
+	hndrte_log_t	log;			/* Log struct (host copy) */
+	uint		bufsize;		/* Size of log buffer */
+	uint8		*buf;			/* Log buffer (host copy) */
+	uint		last;			/* Last buffer read index */
+} dhd_console_t;
+#endif /* DHD_DEBUG */
+
+/* Private data for SDIO bus interaction */
+typedef struct dhd_bus {
+	dhd_pub_t	*dhd;
+
+	bcmsdh_info_t	*sdh;			/* Handle for BCMSDH calls */
+	si_t		*sih;			/* Handle for SI calls */
+	char		*vars;			/* Variables (from CIS and/or other) */
+	uint		varsz;			/* Size of variables buffer */
+	uint32		sbaddr;			/* Current SB window pointer (-1, invalid) */
+
+	sdpcmd_regs_t	*regs;			/* Registers for SDIO core */
+	uint		sdpcmrev;		/* SDIO core revision */
+	uint		armrev;			/* CPU core revision */
+	uint		ramrev;			/* SOCRAM core revision */
+	uint32		ramsize;		/* Size of RAM in SOCRAM (bytes) */
+	uint32		orig_ramsize;		/* Size of RAM in SOCRAM (bytes) */
+
+	uint32		bus;			/* gSPI or SDIO bus */
+	uint32		hostintmask;		/* Copy of Host Interrupt Mask */
+	uint32		intstatus;		/* Intstatus bits (events) pending */
+	bool		dpc_sched;		/* Indicates DPC schedule (intrpt rcvd) */
+	bool		fcstate;		/* State of dongle flow-control */
+
+	uint16		cl_devid;		/* cached devid for dhdsdio_probe_attach() */
+	char		*fw_path; /* module_param: path to firmware image */
+	char		*nv_path; /* module_param: path to nvram vars file */
+	const char      *nvram_params;		/* user specified nvram params. */
+
+	uint		blocksize;		/* Block size of SDIO transfers */
+	uint		roundup;		/* Max roundup limit */
+
+	struct pktq	txq;			/* Queue length used for flow-control */
+	uint8		flowcontrol;		/* per prio flow control bitmask */
+	uint8		tx_seq;			/* Transmit sequence number (next) */
+	uint8		tx_max;			/* Maximum transmit sequence allowed */
+
+	uint8		hdrbuf[MAX_HDR_READ + DHD_SDALIGN];
+	uint8		*rxhdr;			/* Header of current rx frame (in hdrbuf) */
+	uint16		nextlen;		/* Next Read Len from last header */
+	uint8		rx_seq;			/* Receive sequence number (expected) */
+	bool		rxskip;			/* Skip receive (awaiting NAK ACK) */
+
+	void		*glomd;			/* Packet containing glomming descriptor */
+	void		*glom;			/* Packet chain for glommed superframe */
+	uint		glomerr;		/* Glom packet read errors */
+
+	uint8		*rxbuf;			/* Buffer for receiving control packets */
+	uint		rxblen;			/* Allocated length of rxbuf */
+	uint8		*rxctl;			/* Aligned pointer into rxbuf */
+	uint8		*databuf;		/* Buffer for receiving big glom packet */
+	uint8		*dataptr;		/* Aligned pointer into databuf */
+	uint		rxlen;			/* Length of valid data in buffer */
+
+	uint8		sdpcm_ver;		/* Bus protocol reported by dongle */
+
+	bool		intr;			/* Use interrupts */
+	bool		poll;			/* Use polling */
+	bool		ipend;			/* Device interrupt is pending */
+	bool		intdis;			/* Interrupts disabled by isr */
+	uint 		intrcount;		/* Count of device interrupt callbacks */
+	uint		lastintrs;		/* Count as of last watchdog timer */
+	uint		spurious;		/* Count of spurious interrupts */
+	uint		pollrate;		/* Ticks between device polls */
+	uint		polltick;		/* Tick counter */
+	uint		pollcnt;		/* Count of active polls */
+
+#ifdef DHD_DEBUG
+	dhd_console_t	console;		/* Console output polling support */
+	uint		console_addr;		/* Console address from shared struct */
+#endif /* DHD_DEBUG */
+
+	uint		regfails;		/* Count of R_REG/W_REG failures */
+
+	uint		clkstate;		/* State of sd and backplane clock(s) */
+	bool		activity;		/* Activity flag for clock down */
+	int32		idletime;		/* Control for activity timeout */
+	int32		idlecount;		/* Activity timeout counter */
+	int32		idleclock;		/* How to set bus driver when idle */
+	int32		sd_divisor;		/* Speed control to bus driver */
+	int32		sd_mode;		/* Mode control to bus driver */
+	int32		sd_rxchain;		/* If bcmsdh api accepts PKT chains */
+	bool		use_rxchain;		/* If dhd should use PKT chains */
+	bool		sleeping;		/* Is SDIO bus sleeping? */
+	bool		rxflow_mode;	/* Rx flow control mode */
+	bool		rxflow;			/* Is rx flow control on */
+	uint		prev_rxlim_hit;		/* Is prev rx limit exceeded (per dpc schedule) */
+	bool		alp_only;		/* Don't use HT clock (ALP only) */
+	/* Field to decide if rx of control frames happen in rxbuf or lb-pool */
+	bool		usebufpool;
+
+#ifdef SDTEST
+	/* external loopback */
+	bool		ext_loop;
+	uint8		loopid;
+
+	/* pktgen configuration */
+	uint		pktgen_freq;		/* Ticks between bursts */
+	uint		pktgen_count;		/* Packets to send each burst */
+	uint		pktgen_print;		/* Bursts between count displays */
+	uint		pktgen_total;		/* Stop after this many */
+	uint		pktgen_minlen;		/* Minimum packet data len */
+	uint		pktgen_maxlen;		/* Maximum packet data len */
+	uint		pktgen_mode;		/* Configured mode: tx, rx, or echo */
+	uint		pktgen_stop;		/* Number of tx failures causing stop */
+
+	/* active pktgen fields */
+	uint		pktgen_tick;		/* Tick counter for bursts */
+	uint		pktgen_ptick;		/* Burst counter for printing */
+	uint		pktgen_sent;		/* Number of test packets generated */
+	uint		pktgen_rcvd;		/* Number of test packets received */
+	uint		pktgen_fail;		/* Number of failed send attempts */
+	uint16		pktgen_len;		/* Length of next packet to send */
+#endif /* SDTEST */
+
+	/* Some additional counters */
+	uint		tx_sderrs;		/* Count of tx attempts with sd errors */
+	uint		fcqueued;		/* Tx packets that got queued */
+	uint		rxrtx;			/* Count of rtx requests (NAK to dongle) */
+	uint		rx_toolong;		/* Receive frames too long to receive */
+	uint		rxc_errors;		/* SDIO errors when reading control frames */
+	uint		rx_hdrfail;		/* SDIO errors on header reads */
+	uint		rx_badhdr;		/* Bad received headers (roosync?) */
+	uint		rx_badseq;		/* Mismatched rx sequence number */
+	uint		fc_rcvd;		/* Number of flow-control events received */
+	uint		fc_xoff;		/* Number which turned on flow-control */
+	uint		fc_xon;			/* Number which turned off flow-control */
+	uint		rxglomfail;		/* Failed deglom attempts */
+	uint		rxglomframes;		/* Number of glom frames (superframes) */
+	uint		rxglompkts;		/* Number of packets from glom frames */
+	uint		f2rxhdrs;		/* Number of header reads */
+	uint		f2rxdata;		/* Number of frame data reads */
+	uint		f2txdata;		/* Number of f2 frame writes */
+	uint		f1regdata;		/* Number of f1 register accesses */
+
+	uint8		*ctrl_frame_buf;
+	uint32		ctrl_frame_len;
+	bool		ctrl_frame_stat;
+} dhd_bus_t;
+
+/* clkstate */
+#define CLK_NONE	0
+#define CLK_SDONLY	1
+#define CLK_PENDING	2	/* Not used yet */
+#define CLK_AVAIL	3
+
+#define DHD_NOPMU(dhd)	(FALSE)
+
+#ifdef DHD_DEBUG
+static int qcount[NUMPRIO];
+static int tx_packets[NUMPRIO];
+#endif /* DHD_DEBUG */
+
+/* Deferred transmit */
+const uint dhd_deferred_tx = 1;
+
+extern uint dhd_watchdog_ms;
+extern void dhd_os_wd_timer(void *bus, uint wdtick);
+
+/* Tx/Rx bounds */
+uint dhd_txbound;
+uint dhd_rxbound;
+uint dhd_txminmax;
+
+/* override the RAM size if possible */
+#define DONGLE_MIN_MEMSIZE (128 *1024)
+int dhd_dongle_memsize;
+
+static bool dhd_doflow;
+static bool dhd_alignctl;
+
+static bool sd1idle;
+
+static bool retrydata;
+#define RETRYCHAN(chan) (((chan) == SDPCM_EVENT_CHANNEL) || retrydata)
+
+static const uint watermark = 8;
+static const uint firstread = DHD_FIRSTREAD;
+
+#define HDATLEN (firstread - (SDPCM_HDRLEN))
+
+/* Retry count for register access failures */
+static const uint retry_limit = 2;
+
+/* Force even SD lengths (some host controllers mess up on odd bytes) */
+static bool forcealign;
+
+#define ALIGNMENT  4
+
+#if defined(OOB_INTR_ONLY) && defined(HW_OOB)
+extern void bcmsdh_enable_hw_oob_intr(void *sdh, bool enable);
+#endif
+
+#if defined(OOB_INTR_ONLY) && defined(SDIO_ISR_THREAD)
+#error OOB_INTR_ONLY is NOT working with SDIO_ISR_THREAD
+#endif /* defined(OOB_INTR_ONLY) && defined(SDIO_ISR_THREAD) */
+#define PKTALIGN(osh, p, len, align)					\
+	do {								\
+		uint datalign;						\
+		datalign = (uintptr)PKTDATA((osh), (p));		\
+		datalign = ROUNDUP(datalign, (align)) - datalign;	\
+		ASSERT(datalign < (align));				\
+		ASSERT(PKTLEN((osh), (p)) >= ((len) + datalign));	\
+		if (datalign)						\
+			PKTPULL((osh), (p), datalign);			\
+		PKTSETLEN((osh), (p), (len));				\
+	} while (0)
+
+/* Limit on rounding up frames */
+static const uint max_roundup = 512;
+
+/* Try doing readahead */
+static bool dhd_readahead;
+
+
+/* To check if there's window offered */
+#define DATAOK(bus) \
+	(((uint8)(bus->tx_max - bus->tx_seq) != 0) && \
+	(((uint8)(bus->tx_max - bus->tx_seq) & 0x80) == 0))
+
+/* Macros to get register read/write status */
+/* NOTE: these assume a local dhdsdio_bus_t *bus! */
+#define R_SDREG(regvar, regaddr, retryvar) \
+do { \
+	retryvar = 0; \
+	do { \
+		regvar = R_REG(bus->dhd->osh, regaddr); \
+	} while (bcmsdh_regfail(bus->sdh) && (++retryvar <= retry_limit)); \
+	if (retryvar) { \
+		bus->regfails += (retryvar-1); \
+		if (retryvar > retry_limit) { \
+			DHD_ERROR(("%s: FAILED" #regvar "READ, LINE %d\n", \
+			           __FUNCTION__, __LINE__)); \
+			regvar = 0; \
+		} \
+	} \
+} while (0)
+
+#define W_SDREG(regval, regaddr, retryvar) \
+do { \
+	retryvar = 0; \
+	do { \
+		W_REG(bus->dhd->osh, regaddr, regval); \
+	} while (bcmsdh_regfail(bus->sdh) && (++retryvar <= retry_limit)); \
+	if (retryvar) { \
+		bus->regfails += (retryvar-1); \
+		if (retryvar > retry_limit) \
+			DHD_ERROR(("%s: FAILED REGISTER WRITE, LINE %d\n", \
+			           __FUNCTION__, __LINE__)); \
+	} \
+} while (0)
+
+
+#define DHD_BUS			SDIO_BUS
+
+#define PKT_AVAILABLE()		(intstatus & I_HMB_FRAME_IND)
+
+#define HOSTINTMASK		(I_HMB_SW_MASK | I_CHIPACTIVE)
+
+#define GSPI_PR55150_BAILOUT
+
+
+#ifdef SDTEST
+static void dhdsdio_testrcv(dhd_bus_t *bus, void *pkt, uint seq);
+static void dhdsdio_sdtest_set(dhd_bus_t *bus, bool start);
+#endif
+
+#ifdef DHD_DEBUG_TRAP
+static int dhdsdio_checkdied(dhd_bus_t *bus, uint8 *data, uint size);
+#endif /* DHD_DEBUG_TRAP */
+static int dhdsdio_download_state(dhd_bus_t *bus, bool enter);
+
+static void dhdsdio_release(dhd_bus_t *bus, osl_t *osh);
+static void dhdsdio_release_malloc(dhd_bus_t *bus, osl_t *osh);
+static void dhdsdio_disconnect(void *ptr);
+static bool dhdsdio_chipmatch(uint16 chipid);
+static bool dhdsdio_probe_attach(dhd_bus_t *bus, osl_t *osh, void *sdh,
+                                 void * regsva, uint16  devid);
+static bool dhdsdio_probe_malloc(dhd_bus_t *bus, osl_t *osh, void *sdh);
+static bool dhdsdio_probe_init(dhd_bus_t *bus, osl_t *osh, void *sdh);
+static void dhdsdio_release_dongle(dhd_bus_t *bus, osl_t *osh, int reset_flag);
+
+static uint process_nvram_vars(char *varbuf, uint len);
+
+static void dhd_dongle_setmemsize(struct dhd_bus *bus, int mem_size);
+static int dhd_bcmsdh_recv_buf(dhd_bus_t *bus, uint32 addr, uint fn, uint flags,
+	uint8 *buf, uint nbytes,
+	void *pkt, bcmsdh_cmplt_fn_t complete, void *handle);
+static int dhd_bcmsdh_send_buf(dhd_bus_t *bus, uint32 addr, uint fn, uint flags,
+	uint8 *buf, uint nbytes,
+	void *pkt, bcmsdh_cmplt_fn_t complete, void *handle);
+
+static bool dhdsdio_download_firmware(struct dhd_bus *bus, osl_t *osh, void *sdh);
+static int _dhdsdio_download_firmware(struct dhd_bus *bus);
+
+static int dhdsdio_download_code_file(struct dhd_bus *bus, char *image_path);
+static int dhdsdio_download_nvram(struct dhd_bus *bus);
+#ifdef BCMEMBEDIMAGE
+static int dhdsdio_download_code_array(struct dhd_bus *bus);
+#endif
+
+
+static void
+dhd_dongle_setmemsize(struct dhd_bus *bus, int mem_size)
+{
+	int32 min_size =  DONGLE_MIN_MEMSIZE;
+	/* Restrict the memsize to user specified limit */
+	DHD_ERROR(("user: Restrict the dongle ram size to %d, min accepted %d\n",
+		dhd_dongle_memsize, min_size));
+	if ((dhd_dongle_memsize > min_size) &&
+		(dhd_dongle_memsize < (int32)bus->orig_ramsize))
+		bus->ramsize = dhd_dongle_memsize;
+}
+
+static int
+dhdsdio_set_siaddr_window(dhd_bus_t *bus, uint32 address)
+{
+	int err = 0;
+	bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRLOW,
+	                 (address >> 8) & SBSDIO_SBADDRLOW_MASK, &err);
+	if (!err)
+		bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRMID,
+		                 (address >> 16) & SBSDIO_SBADDRMID_MASK, &err);
+	if (!err)
+		bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRHIGH,
+		                 (address >> 24) & SBSDIO_SBADDRHIGH_MASK, &err);
+	return err;
+}
+
+
+/* Turn backplane clock on or off */
+static int
+dhdsdio_htclk(dhd_bus_t *bus, bool on, bool pendok)
+{
+	int err;
+	uint8 clkctl, clkreq, devctl;
+	bcmsdh_info_t *sdh;
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+#if defined(OOB_INTR_ONLY)
+	pendok = FALSE;
+#endif
+	clkctl = 0;
+	sdh = bus->sdh;
+
+
+	if (on) {
+		/* Request HT Avail */
+		clkreq = bus->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ;
+
+		if ((bus->sih->chip == BCM4329_CHIP_ID) && (bus->sih->chiprev == 0))
+			clkreq |= SBSDIO_FORCE_ALP;
+
+
+
+
+		bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, clkreq, &err);
+		if (err) {
+			DHD_ERROR(("%s: HT Avail request error: %d\n", __FUNCTION__, err));
+			return BCME_ERROR;
+		}
+
+		if (pendok &&
+		    ((bus->sih->buscoretype == PCMCIA_CORE_ID) && (bus->sih->buscorerev == 9))) {
+			uint32 dummy, retries;
+			R_SDREG(dummy, &bus->regs->clockctlstatus, retries);
+		}
+
+		/* Check current status */
+		clkctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err);
+		if (err) {
+			DHD_ERROR(("%s: HT Avail read error: %d\n", __FUNCTION__, err));
+			return BCME_ERROR;
+		}
+
+		/* Go to pending and await interrupt if appropriate */
+		if (!SBSDIO_CLKAV(clkctl, bus->alp_only) && pendok) {
+			/* Allow only clock-available interrupt */
+			devctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err);
+			if (err) {
+				DHD_ERROR(("%s: Devctl access error setting CA: %d\n",
+				           __FUNCTION__, err));
+				return BCME_ERROR;
+			}
+
+			devctl |= SBSDIO_DEVCTL_CA_INT_ONLY;
+			bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, devctl, &err);
+			DHD_INFO(("CLKCTL: set PENDING\n"));
+			bus->clkstate = CLK_PENDING;
+			return BCME_OK;
+		} else if (bus->clkstate == CLK_PENDING) {
+			/* Cancel CA-only interrupt filter */
+			devctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err);
+			devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
+			bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, devctl, &err);
+		}
+
+		/* Otherwise, wait here (polling) for HT Avail */
+		if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) {
+			SPINWAIT_SLEEP(sdioh_spinwait_sleep,
+				((clkctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
+			                                    SBSDIO_FUNC1_CHIPCLKCSR, &err)),
+			          !SBSDIO_CLKAV(clkctl, bus->alp_only)), PMU_MAX_TRANSITION_DLY);
+		}
+		if (err) {
+			DHD_ERROR(("%s: HT Avail request error: %d\n", __FUNCTION__, err));
+			return BCME_ERROR;
+		}
+		if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) {
+			DHD_ERROR(("%s: HT Avail timeout (%d): clkctl 0x%02x\n",
+			           __FUNCTION__, PMU_MAX_TRANSITION_DLY, clkctl));
+			return BCME_ERROR;
+		}
+
+
+		/* Mark clock available */
+		bus->clkstate = CLK_AVAIL;
+		DHD_INFO(("CLKCTL: turned ON\n"));
+
+#if defined(DHD_DEBUG)
+		if (bus->alp_only == TRUE) {
+#if !defined(BCMLXSDMMC)
+			if (!SBSDIO_ALPONLY(clkctl)) {
+				DHD_ERROR(("%s: HT Clock, when ALP Only\n", __FUNCTION__));
+			}
+#endif /* !defined(BCMLXSDMMC) */
+		} else {
+			if (SBSDIO_ALPONLY(clkctl)) {
+				DHD_ERROR(("%s: HT Clock should be on.\n", __FUNCTION__));
+			}
+		}
+#endif /* defined (DHD_DEBUG) */
+
+		bus->activity = TRUE;
+	} else {
+		clkreq = 0;
+
+		if (bus->clkstate == CLK_PENDING) {
+			/* Cancel CA-only interrupt filter */
+			devctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err);
+			devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
+			bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, devctl, &err);
+		}
+
+		bus->clkstate = CLK_SDONLY;
+		bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, clkreq, &err);
+		DHD_INFO(("CLKCTL: turned OFF\n"));
+		if (err) {
+			DHD_ERROR(("%s: Failed access turning clock off: %d\n",
+			           __FUNCTION__, err));
+			return BCME_ERROR;
+		}
+	}
+	return BCME_OK;
+}
+
+/* Change idle/active SD state */
+static int
+dhdsdio_sdclk(dhd_bus_t *bus, bool on)
+{
+	int err;
+	int32 iovalue;
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	if (on) {
+		if (bus->idleclock == DHD_IDLE_STOP) {
+			/* Turn on clock and restore mode */
+			iovalue = 1;
+			err = bcmsdh_iovar_op(bus->sdh, "sd_clock", NULL, 0,
+			                      &iovalue, sizeof(iovalue), TRUE);
+			if (err) {
+				DHD_ERROR(("%s: error enabling sd_clock: %d\n",
+				           __FUNCTION__, err));
+				return BCME_ERROR;
+			}
+
+			iovalue = bus->sd_mode;
+			err = bcmsdh_iovar_op(bus->sdh, "sd_mode", NULL, 0,
+			                      &iovalue, sizeof(iovalue), TRUE);
+			if (err) {
+				DHD_ERROR(("%s: error changing sd_mode: %d\n",
+				           __FUNCTION__, err));
+				return BCME_ERROR;
+			}
+		} else if (bus->idleclock != DHD_IDLE_ACTIVE) {
+			/* Restore clock speed */
+			iovalue = bus->sd_divisor;
+			err = bcmsdh_iovar_op(bus->sdh, "sd_divisor", NULL, 0,
+			                      &iovalue, sizeof(iovalue), TRUE);
+			if (err) {
+				DHD_ERROR(("%s: error restoring sd_divisor: %d\n",
+				           __FUNCTION__, err));
+				return BCME_ERROR;
+			}
+		}
+		bus->clkstate = CLK_SDONLY;
+	} else {
+		/* Stop or slow the SD clock itself */
+		if ((bus->sd_divisor == -1) || (bus->sd_mode == -1)) {
+			DHD_TRACE(("%s: can't idle clock, divisor %d mode %d\n",
+			           __FUNCTION__, bus->sd_divisor, bus->sd_mode));
+			return BCME_ERROR;
+		}
+		if (bus->idleclock == DHD_IDLE_STOP) {
+			if (sd1idle) {
+				/* Change to SD1 mode and turn off clock */
+				iovalue = 1;
+				err = bcmsdh_iovar_op(bus->sdh, "sd_mode", NULL, 0,
+				                      &iovalue, sizeof(iovalue), TRUE);
+				if (err) {
+					DHD_ERROR(("%s: error changing sd_clock: %d\n",
+					           __FUNCTION__, err));
+					return BCME_ERROR;
+				}
+			}
+
+			iovalue = 0;
+			err = bcmsdh_iovar_op(bus->sdh, "sd_clock", NULL, 0,
+			                      &iovalue, sizeof(iovalue), TRUE);
+			if (err) {
+				DHD_ERROR(("%s: error disabling sd_clock: %d\n",
+				           __FUNCTION__, err));
+				return BCME_ERROR;
+			}
+		} else if (bus->idleclock != DHD_IDLE_ACTIVE) {
+			/* Set divisor to idle value */
+			iovalue = bus->idleclock;
+			err = bcmsdh_iovar_op(bus->sdh, "sd_divisor", NULL, 0,
+			                      &iovalue, sizeof(iovalue), TRUE);
+			if (err) {
+				DHD_ERROR(("%s: error changing sd_divisor: %d\n",
+				           __FUNCTION__, err));
+				return BCME_ERROR;
+			}
+		}
+		bus->clkstate = CLK_NONE;
+	}
+
+	return BCME_OK;
+}
+
+/* Transition SD and backplane clock readiness */
+static int
+dhdsdio_clkctl(dhd_bus_t *bus, uint target, bool pendok)
+{
+	int ret = BCME_OK;
+#ifdef DHD_DEBUG
+	uint oldstate = bus->clkstate;
+#endif /* DHD_DEBUG */
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	/* Early exit if we're already there */
+	if (bus->clkstate == target) {
+		if (target == CLK_AVAIL) {
+			dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms);
+			bus->activity = TRUE;
+		}
+		return ret;
+	}
+
+	switch (target) {
+	case CLK_AVAIL:
+		/* Make sure SD clock is available */
+		if (bus->clkstate == CLK_NONE)
+			dhdsdio_sdclk(bus, TRUE);
+		/* Now request HT Avail on the backplane */
+		ret = dhdsdio_htclk(bus, TRUE, pendok);
+		if (ret == BCME_OK) {
+			dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms);
+			bus->activity = TRUE;
+		}
+		break;
+
+	case CLK_SDONLY:
+		/* Remove HT request, or bring up SD clock */
+		if (bus->clkstate == CLK_NONE)
+			ret = dhdsdio_sdclk(bus, TRUE);
+		else if (bus->clkstate == CLK_AVAIL)
+			ret = dhdsdio_htclk(bus, FALSE, FALSE);
+		else
+			DHD_ERROR(("dhdsdio_clkctl: request for %d -> %d\n",
+			           bus->clkstate, target));
+		if (ret == BCME_OK)
+			dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms);
+		break;
+
+	case CLK_NONE:
+		/* Make sure to remove HT request */
+		if (bus->clkstate == CLK_AVAIL)
+			ret = dhdsdio_htclk(bus, FALSE, FALSE);
+		/* Now remove the SD clock */
+		ret = dhdsdio_sdclk(bus, FALSE);
+		dhd_os_wd_timer(bus->dhd, 0);
+		break;
+	}
+#ifdef DHD_DEBUG
+	DHD_INFO(("dhdsdio_clkctl: %d -> %d\n", oldstate, bus->clkstate));
+#endif /* DHD_DEBUG */
+
+	return ret;
+}
+
+int
+dhdsdio_bussleep(dhd_bus_t *bus, bool sleep)
+{
+	bcmsdh_info_t *sdh = bus->sdh;
+	sdpcmd_regs_t *regs = bus->regs;
+	uint retries = 0;
+
+	DHD_INFO(("dhdsdio_bussleep: request %s (currently %s)\n",
+	          (sleep ? "SLEEP" : "WAKE"),
+	          (bus->sleeping ? "SLEEP" : "WAKE")));
+
+	/* Done if we're already in the requested state */
+	if (sleep == bus->sleeping)
+		return BCME_OK;
+
+	/* Going to sleep: set the alarm and turn off the lights... */
+	if (sleep) {
+		/* Don't sleep if something is pending */
+		if (bus->dpc_sched || bus->rxskip || pktq_len(&bus->txq))
+			return BCME_BUSY;
+
+
+		/* Disable SDIO interrupts (no longer interested) */
+		bcmsdh_intr_disable(bus->sdh);
+
+		/* Make sure the controller has the bus up */
+		dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+
+		/* Tell device to start using OOB wakeup */
+		W_SDREG(SMB_USE_OOB, &regs->tosbmailbox, retries);
+		if (retries > retry_limit)
+			DHD_ERROR(("CANNOT SIGNAL CHIP, WILL NOT WAKE UP!!\n"));
+
+		/* Turn off our contribution to the HT clock request */
+		dhdsdio_clkctl(bus, CLK_SDONLY, FALSE);
+
+		bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
+		                 SBSDIO_FORCE_HW_CLKREQ_OFF, NULL);
+
+		/* Isolate the bus */
+		if (bus->sih->chip != BCM4329_CHIP_ID && bus->sih->chip != BCM4319_CHIP_ID) {
+				bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL,
+					SBSDIO_DEVCTL_PADS_ISO, NULL);
+		}
+
+		/* Change state */
+		bus->sleeping = TRUE;
+
+	} else {
+		/* Waking up: bus power up is ok, set local state */
+
+		bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
+		                 0, NULL);
+
+		/* Force pad isolation off if possible (in case power never toggled) */
+		if ((bus->sih->buscoretype == PCMCIA_CORE_ID) && (bus->sih->buscorerev >= 10))
+			bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, 0, NULL);
+
+
+		/* Make sure the controller has the bus up */
+		dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+
+		/* Send misc interrupt to indicate OOB not needed */
+		W_SDREG(0, &regs->tosbmailboxdata, retries);
+		if (retries <= retry_limit)
+			W_SDREG(SMB_DEV_INT, &regs->tosbmailbox, retries);
+
+		if (retries > retry_limit)
+			DHD_ERROR(("CANNOT SIGNAL CHIP TO CLEAR OOB!!\n"));
+
+		/* Make sure we have SD bus access */
+		dhdsdio_clkctl(bus, CLK_SDONLY, FALSE);
+
+		/* Change state */
+		bus->sleeping = FALSE;
+
+		/* Enable interrupts again */
+		if (bus->intr && (bus->dhd->busstate == DHD_BUS_DATA)) {
+			bus->intdis = FALSE;
+			bcmsdh_intr_enable(bus->sdh);
+		}
+	}
+
+	return BCME_OK;
+}
+#if defined(OOB_INTR_ONLY)
+void
+dhd_enable_oob_intr(struct dhd_bus *bus, bool enable)
+{
+#if defined(HW_OOB)
+	bcmsdh_enable_hw_oob_intr(bus->sdh, enable);
+#else
+	sdpcmd_regs_t *regs = bus->regs;
+	uint retries = 0;
+
+	dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+	if (enable == TRUE) {
+
+		/* Tell device to start using OOB wakeup */
+		W_SDREG(SMB_USE_OOB, &regs->tosbmailbox, retries);
+		if (retries > retry_limit)
+			DHD_ERROR(("CANNOT SIGNAL CHIP, WILL NOT WAKE UP!!\n"));
+
+	} else {
+		/* Send misc interrupt to indicate OOB not needed */
+		W_SDREG(0, &regs->tosbmailboxdata, retries);
+		if (retries <= retry_limit)
+			W_SDREG(SMB_DEV_INT, &regs->tosbmailbox, retries);
+	}
+
+	/* Turn off our contribution to the HT clock request */
+	dhdsdio_clkctl(bus, CLK_SDONLY, FALSE);
+#endif /* !defined(HW_OOB) */
+}
+#endif /* defined(OOB_INTR_ONLY) */
+
+#define BUS_WAKE(bus) \
+	do { \
+		if ((bus)->sleeping) \
+			dhdsdio_bussleep((bus), FALSE); \
+	} while (0);
+
+
+/* Writes a HW/SW header into the packet and sends it. */
+/* Assumes: (a) header space already there, (b) caller holds lock */
+static int
+dhdsdio_txpkt(dhd_bus_t *bus, void *pkt, uint chan, bool free_pkt)
+{
+	int ret;
+	osl_t *osh;
+	uint8 *frame;
+	uint16 len, pad = 0;
+	uint32 swheader;
+	uint retries = 0;
+	bcmsdh_info_t *sdh;
+	void *new;
+	int i;
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	sdh = bus->sdh;
+	osh = bus->dhd->osh;
+
+	if (bus->dhd->dongle_reset) {
+		ret = BCME_NOTREADY;
+		goto done;
+	}
+
+	frame = (uint8*)PKTDATA(osh, pkt);
+
+	/* Add alignment padding, allocate new packet if needed */
+	if ((pad = ((uintptr)frame % DHD_SDALIGN))) {
+		if (PKTHEADROOM(osh, pkt) < pad) {
+			DHD_INFO(("%s: insufficient headroom %d for %d pad\n",
+			          __FUNCTION__, (int)PKTHEADROOM(osh, pkt), pad));
+			bus->dhd->tx_realloc++;
+			new = PKTGET(osh, (PKTLEN(osh, pkt) + DHD_SDALIGN), TRUE);
+			if (!new) {
+				DHD_ERROR(("%s: couldn't allocate new %d-byte packet\n",
+				           __FUNCTION__, PKTLEN(osh, pkt) + DHD_SDALIGN));
+				ret = BCME_NOMEM;
+				goto done;
+			}
+
+			PKTALIGN(osh, new, PKTLEN(osh, pkt), DHD_SDALIGN);
+			bcopy(PKTDATA(osh, pkt), PKTDATA(osh, new), PKTLEN(osh, pkt));
+			if (free_pkt)
+				PKTFREE(osh, pkt, TRUE);
+			/* free the pkt if canned one is not used */
+			free_pkt = TRUE;
+			pkt = new;
+			frame = (uint8*)PKTDATA(osh, pkt);
+			ASSERT(((uintptr)frame % DHD_SDALIGN) == 0);
+			pad = 0;
+		} else {
+			PKTPUSH(osh, pkt, pad);
+			frame = (uint8*)PKTDATA(osh, pkt);
+
+			ASSERT((pad + SDPCM_HDRLEN) <= (int) PKTLEN(osh, pkt));
+			bzero(frame, pad + SDPCM_HDRLEN);
+		}
+	}
+	ASSERT(pad < DHD_SDALIGN);
+
+	/* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */
+	len = (uint16)PKTLEN(osh, pkt);
+	*(uint16*)frame = htol16(len);
+	*(((uint16*)frame) + 1) = htol16(~len);
+
+	/* Software tag: channel, sequence number, data offset */
+	swheader = ((chan << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) | bus->tx_seq |
+	        (((pad + SDPCM_HDRLEN) << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK);
+	htol32_ua_store(swheader, frame + SDPCM_FRAMETAG_LEN);
+	htol32_ua_store(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader));
+
+#ifdef DHD_DEBUG
+	tx_packets[PKTPRIO(pkt)]++;
+	if (DHD_BYTES_ON() &&
+	    (((DHD_CTL_ON() && (chan == SDPCM_CONTROL_CHANNEL)) ||
+	      (DHD_DATA_ON() && (chan != SDPCM_CONTROL_CHANNEL))))) {
+		prhex("Tx Frame", frame, len);
+	} else if (DHD_HDRS_ON()) {
+		prhex("TxHdr", frame, MIN(len, 16));
+	}
+#endif
+
+	/* Raise len to next SDIO block to eliminate tail command */
+	if (bus->roundup && bus->blocksize && (len > bus->blocksize)) {
+		uint16 pad = bus->blocksize - (len % bus->blocksize);
+		if ((pad <= bus->roundup) && (pad < bus->blocksize))
+#ifdef NOTUSED
+			if (pad <= PKTTAILROOM(osh, pkt))
+#endif /* NOTUSED */
+				len += pad;
+	} else if (len % DHD_SDALIGN) {
+		len += DHD_SDALIGN - (len % DHD_SDALIGN);
+	}
+
+	/* Some controllers have trouble with odd bytes -- round to even */
+	if (forcealign && (len & (ALIGNMENT - 1))) {
+#ifdef NOTUSED
+		if (PKTTAILROOM(osh, pkt))
+#endif
+			len = ROUNDUP(len, ALIGNMENT);
+#ifdef NOTUSED
+		else
+			DHD_ERROR(("%s: sending unrounded %d-byte packet\n", __FUNCTION__, len));
+#endif
+	}
+
+	do {
+		ret = dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC,
+		                      frame, len, pkt, NULL, NULL);
+		bus->f2txdata++;
+		ASSERT(ret != BCME_PENDING);
+
+		if (ret < 0) {
+			/* On failure, abort the command and terminate the frame */
+			DHD_INFO(("%s: sdio error %d, abort command and terminate frame.\n",
+			          __FUNCTION__, ret));
+			bus->tx_sderrs++;
+
+			bcmsdh_abort(sdh, SDIO_FUNC_2);
+			bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_FRAMECTRL,
+			                 SFC_WF_TERM, NULL);
+			bus->f1regdata++;
+
+			for (i = 0; i < 3; i++) {
+				uint8 hi, lo;
+				hi = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
+				                     SBSDIO_FUNC1_WFRAMEBCHI, NULL);
+				lo = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
+				                     SBSDIO_FUNC1_WFRAMEBCLO, NULL);
+				bus->f1regdata += 2;
+				if ((hi == 0) && (lo == 0))
+					break;
+			}
+
+		}
+		if (ret == 0) {
+			bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
+		}
+	} while ((ret < 0) && retrydata && retries++ < TXRETRIES);
+
+done:
+	/* restore pkt buffer pointer before calling tx complete routine */
+	PKTPULL(osh, pkt, SDPCM_HDRLEN + pad);
+	dhd_os_sdunlock(bus->dhd);
+	dhd_txcomplete(bus->dhd, pkt, ret != 0);
+	dhd_os_sdlock(bus->dhd);
+
+	if (free_pkt)
+		PKTFREE(osh, pkt, TRUE);
+
+	return ret;
+}
+
+int
+dhd_bus_txdata(struct dhd_bus *bus, void *pkt)
+{
+	int ret = BCME_ERROR;
+	osl_t *osh;
+	uint datalen, prec;
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	osh = bus->dhd->osh;
+	datalen = PKTLEN(osh, pkt);
+
+#ifdef SDTEST
+	/* Push the test header if doing loopback */
+	if (bus->ext_loop) {
+		uint8* data;
+		PKTPUSH(osh, pkt, SDPCM_TEST_HDRLEN);
+		data = PKTDATA(osh, pkt);
+		*data++ = SDPCM_TEST_ECHOREQ;
+		*data++ = (uint8)bus->loopid++;
+		*data++ = (datalen >> 0);
+		*data++ = (datalen >> 8);
+		datalen += SDPCM_TEST_HDRLEN;
+	}
+#endif /* SDTEST */
+
+	/* Add space for the header */
+	PKTPUSH(osh, pkt, SDPCM_HDRLEN);
+	ASSERT(ISALIGNED((uintptr)PKTDATA(osh, pkt), 2));
+
+	prec = PRIO2PREC((PKTPRIO(pkt) & PRIOMASK));
+
+
+	/* Check for existing queue, current flow-control, pending event, or pending clock */
+	if (dhd_deferred_tx || bus->fcstate || pktq_len(&bus->txq) || bus->dpc_sched ||
+	    (!DATAOK(bus)) || (bus->flowcontrol & NBITVAL(prec)) ||
+	    (bus->clkstate != CLK_AVAIL)) {
+		DHD_TRACE(("%s: deferring pktq len %d\n", __FUNCTION__,
+			pktq_len(&bus->txq)));
+		bus->fcqueued++;
+
+		/* Priority based enq */
+		dhd_os_sdlock_txq(bus->dhd);
+		if (dhd_prec_enq(bus->dhd, &bus->txq, pkt, prec) == FALSE) {
+			PKTPULL(osh, pkt, SDPCM_HDRLEN);
+			dhd_txcomplete(bus->dhd, pkt, FALSE);
+			PKTFREE(osh, pkt, TRUE);
+			DHD_ERROR(("%s: out of bus->txq !!!\n", __FUNCTION__));
+			ret = BCME_NORESOURCE;
+		} else {
+			ret = BCME_OK;
+		}
+		dhd_os_sdunlock_txq(bus->dhd);
+
+		if ((pktq_len(&bus->txq) >= FCHI) && dhd_doflow)
+			dhd_txflowcontrol(bus->dhd, 0, ON);
+
+#ifdef DHD_DEBUG
+		if (pktq_plen(&bus->txq, prec) > qcount[prec])
+			qcount[prec] = pktq_plen(&bus->txq, prec);
+#endif
+		/* Schedule DPC if needed to send queued packet(s) */
+		if (dhd_deferred_tx && !bus->dpc_sched) {
+			bus->dpc_sched = TRUE;
+			dhd_sched_dpc(bus->dhd);
+		}
+	} else {
+		/* Lock: we're about to use shared data/code (and SDIO) */
+		dhd_os_sdlock(bus->dhd);
+
+		/* Otherwise, send it now */
+		BUS_WAKE(bus);
+		/* Make sure back plane ht clk is on, no pending allowed */
+		dhdsdio_clkctl(bus, CLK_AVAIL, TRUE);
+
+#ifndef SDTEST
+		DHD_TRACE(("%s: calling txpkt\n", __FUNCTION__));
+		ret = dhdsdio_txpkt(bus, pkt, SDPCM_DATA_CHANNEL, TRUE);
+#else
+		ret = dhdsdio_txpkt(bus, pkt,
+		        (bus->ext_loop ? SDPCM_TEST_CHANNEL : SDPCM_DATA_CHANNEL), TRUE);
+#endif
+		if (ret)
+			bus->dhd->tx_errors++;
+		else
+			bus->dhd->dstats.tx_bytes += datalen;
+
+		if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) {
+			bus->activity = FALSE;
+			dhdsdio_clkctl(bus, CLK_NONE, TRUE);
+		}
+
+		dhd_os_sdunlock(bus->dhd);
+	}
+
+
+	return ret;
+}
+
+static uint
+dhdsdio_sendfromq(dhd_bus_t *bus, uint maxframes)
+{
+	void *pkt;
+	uint32 intstatus = 0;
+	uint retries = 0;
+	int ret = 0, prec_out;
+	uint cnt = 0;
+	uint datalen;
+	uint8 tx_prec_map;
+
+	dhd_pub_t *dhd = bus->dhd;
+	sdpcmd_regs_t *regs = bus->regs;
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	tx_prec_map = ~bus->flowcontrol;
+
+	/* Send frames until the limit or some other event */
+	for (cnt = 0; (cnt < maxframes) && DATAOK(bus); cnt++) {
+		dhd_os_sdlock_txq(bus->dhd);
+		if ((pkt = pktq_mdeq(&bus->txq, tx_prec_map, &prec_out)) == NULL) {
+			dhd_os_sdunlock_txq(bus->dhd);
+			break;
+		}
+		dhd_os_sdunlock_txq(bus->dhd);
+		datalen = PKTLEN(bus->dhd->osh, pkt) - SDPCM_HDRLEN;
+
+#ifndef SDTEST
+		ret = dhdsdio_txpkt(bus, pkt, SDPCM_DATA_CHANNEL, TRUE);
+#else
+		ret = dhdsdio_txpkt(bus, pkt,
+		        (bus->ext_loop ? SDPCM_TEST_CHANNEL : SDPCM_DATA_CHANNEL), TRUE);
+#endif
+		if (ret)
+			bus->dhd->tx_errors++;
+		else
+			bus->dhd->dstats.tx_bytes += datalen;
+
+		/* In poll mode, need to check for other events */
+		if (!bus->intr && cnt)
+		{
+			/* Check device status, signal pending interrupt */
+			R_SDREG(intstatus, &regs->intstatus, retries);
+			bus->f2txdata++;
+			if (bcmsdh_regfail(bus->sdh))
+				break;
+			if (intstatus & bus->hostintmask)
+				bus->ipend = TRUE;
+		}
+	}
+
+	/* Deflow-control stack if needed */
+	if (dhd_doflow && dhd->up && (dhd->busstate == DHD_BUS_DATA) &&
+	    dhd->txoff && (pktq_len(&bus->txq) < FCLOW))
+		dhd_txflowcontrol(dhd, 0, OFF);
+
+	return cnt;
+}
+
+int
+dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen)
+{
+	uint8 *frame;
+	uint16 len;
+	uint32 swheader;
+	uint retries = 0;
+	bcmsdh_info_t *sdh = bus->sdh;
+	uint8 doff = 0;
+	int ret = -1;
+	int i;
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	if (bus->dhd->dongle_reset)
+		return -EIO;
+
+	/* Back the pointer to make a room for bus header */
+	frame = msg - SDPCM_HDRLEN;
+	len = (msglen += SDPCM_HDRLEN);
+
+	/* Add alignment padding (optional for ctl frames) */
+	if (dhd_alignctl) {
+		if ((doff = ((uintptr)frame % DHD_SDALIGN))) {
+			frame -= doff;
+			len += doff;
+			msglen += doff;
+			bzero(frame, doff + SDPCM_HDRLEN);
+		}
+		ASSERT(doff < DHD_SDALIGN);
+	}
+	doff += SDPCM_HDRLEN;
+
+	/* Round send length to next SDIO block */
+	if (bus->roundup && bus->blocksize && (len > bus->blocksize)) {
+		uint16 pad = bus->blocksize - (len % bus->blocksize);
+		if ((pad <= bus->roundup) && (pad < bus->blocksize))
+			len += pad;
+	} else if (len % DHD_SDALIGN) {
+		len += DHD_SDALIGN - (len % DHD_SDALIGN);
+	}
+
+	/* Satisfy length-alignment requirements */
+	if (forcealign && (len & (ALIGNMENT - 1)))
+		len = ROUNDUP(len, ALIGNMENT);
+
+	ASSERT(ISALIGNED((uintptr)frame, 2));
+
+
+	/* Need to lock here to protect txseq and SDIO tx calls */
+	dhd_os_sdlock(bus->dhd);
+
+	BUS_WAKE(bus);
+
+	/* Make sure backplane clock is on */
+	dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+
+	/* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */
+	*(uint16*)frame = htol16((uint16)msglen);
+	*(((uint16*)frame) + 1) = htol16(~msglen);
+
+	/* Software tag: channel, sequence number, data offset */
+	swheader = ((SDPCM_CONTROL_CHANNEL << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK)
+	        | bus->tx_seq | ((doff << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK);
+	htol32_ua_store(swheader, frame + SDPCM_FRAMETAG_LEN);
+	htol32_ua_store(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader));
+
+	if (!DATAOK(bus)) {
+		DHD_INFO(("%s: No bus credit bus->tx_max %d, bus->tx_seq %d\n",
+			__FUNCTION__, bus->tx_max, bus->tx_seq));
+		bus->ctrl_frame_stat = TRUE;
+		/* Send from dpc */
+		bus->ctrl_frame_buf = frame;
+		bus->ctrl_frame_len = len;
+
+		dhd_wait_for_event(bus->dhd, &bus->ctrl_frame_stat);
+
+		if (bus->ctrl_frame_stat == FALSE) {
+			DHD_INFO(("%s: ctrl_frame_stat == FALSE\n", __FUNCTION__));
+			ret = 0;
+		} else {
+			if (!bus->dhd->hang_was_sent)
+				DHD_ERROR(("%s: ctrl_frame_stat == TRUE\n", __FUNCTION__));
+			ret = -1;
+		}
+	}
+
+	if (ret == -1) {
+#ifdef DHD_DEBUG
+		if (DHD_BYTES_ON() && DHD_CTL_ON()) {
+			prhex("Tx Frame", frame, len);
+		} else if (DHD_HDRS_ON()) {
+			prhex("TxHdr", frame, MIN(len, 16));
+		}
+#endif
+
+		do {
+			bus->ctrl_frame_stat = FALSE;
+			ret = dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC,
+			                          frame, len, NULL, NULL, NULL);
+			ASSERT(ret != BCME_PENDING);
+
+			if (ret < 0) {
+				/* On failure, abort the command and terminate the frame */
+				DHD_INFO(("%s: sdio error %d, abort command and terminate frame.\n",
+				          __FUNCTION__, ret));
+				bus->tx_sderrs++;
+
+				bcmsdh_abort(sdh, SDIO_FUNC_2);
+
+				bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_FRAMECTRL,
+				                 SFC_WF_TERM, NULL);
+				bus->f1regdata++;
+
+				for (i = 0; i < 3; i++) {
+					uint8 hi, lo;
+					hi = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
+					                     SBSDIO_FUNC1_WFRAMEBCHI, NULL);
+					lo = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
+					                     SBSDIO_FUNC1_WFRAMEBCLO, NULL);
+					bus->f1regdata += 2;
+					if ((hi == 0) && (lo == 0))
+						break;
+				}
+
+			}
+			if (ret == 0) {
+				bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
+			}
+		} while ((ret < 0) && retries++ < TXRETRIES);
+	}
+
+	if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) {
+		bus->activity = FALSE;
+		dhdsdio_clkctl(bus, CLK_NONE, TRUE);
+	}
+
+	dhd_os_sdunlock(bus->dhd);
+
+	if (ret)
+		bus->dhd->tx_ctlerrs++;
+	else
+		bus->dhd->tx_ctlpkts++;
+
+	return ret ? -EIO : 0;
+}
+
+int
+dhd_bus_rxctl(struct dhd_bus *bus, uchar *msg, uint msglen)
+{
+	int timeleft;
+	uint rxlen = 0;
+	bool pending;
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	if (bus->dhd->dongle_reset)
+		return -EIO;
+
+	/* Wait until control frame is available */
+	timeleft = dhd_os_ioctl_resp_wait(bus->dhd, &bus->rxlen, &pending);
+
+	dhd_os_sdlock(bus->dhd);
+	rxlen = bus->rxlen;
+	bcopy(bus->rxctl, msg, MIN(msglen, rxlen));
+	bus->rxlen = 0;
+	dhd_os_sdunlock(bus->dhd);
+
+	if (rxlen) {
+		DHD_CTL(("%s: resumed on rxctl frame, got %d expected %d\n",
+		         __FUNCTION__, rxlen, msglen));
+	} else if (timeleft == 0) {
+		DHD_ERROR(("%s: resumed on timeout\n", __FUNCTION__));
+#ifdef DHD_DEBUG_TRAP
+		dhd_os_sdlock(bus->dhd);
+		dhdsdio_checkdied(bus, NULL, 0);
+		dhd_os_sdunlock(bus->dhd);
+#endif /* DHD_DEBUG_TRAP */
+	} else if (pending == TRUE) {
+		DHD_CTL(("%s: cancelled\n", __FUNCTION__));
+		return -ERESTARTSYS;
+	} else {
+		DHD_CTL(("%s: resumed for unknown reason?\n", __FUNCTION__));
+#ifdef DHD_DEBUG_TRAP
+		dhd_os_sdlock(bus->dhd);
+		dhdsdio_checkdied(bus, NULL, 0);
+		dhd_os_sdunlock(bus->dhd);
+#endif /* DHD_DEBUG_TRAP */
+	}
+
+	if (rxlen)
+		bus->dhd->rx_ctlpkts++;
+	else
+		bus->dhd->rx_ctlerrs++;
+
+	return rxlen ? (int)rxlen : -ETIMEDOUT;
+}
+
+/* IOVar table */
+enum {
+	IOV_INTR = 1,
+	IOV_POLLRATE,
+	IOV_SDREG,
+	IOV_SBREG,
+	IOV_SDCIS,
+	IOV_MEMBYTES,
+	IOV_MEMSIZE,
+#ifdef DHD_DEBUG_TRAP
+	IOV_CHECKDIED,
+#endif
+	IOV_DOWNLOAD,
+	IOV_FORCEEVEN,
+	IOV_SDIOD_DRIVE,
+	IOV_READAHEAD,
+	IOV_SDRXCHAIN,
+	IOV_ALIGNCTL,
+	IOV_SDALIGN,
+	IOV_DEVRESET,
+	IOV_CPU,
+#ifdef SDTEST
+	IOV_PKTGEN,
+	IOV_EXTLOOP,
+#endif /* SDTEST */
+	IOV_SPROM,
+	IOV_TXBOUND,
+	IOV_RXBOUND,
+	IOV_TXMINMAX,
+	IOV_IDLETIME,
+	IOV_IDLECLOCK,
+	IOV_SD1IDLE,
+	IOV_SLEEP,
+	IOV_VARS
+};
+
+const bcm_iovar_t dhdsdio_iovars[] = {
+	{"intr",	IOV_INTR,	0,	IOVT_BOOL,	0 },
+	{"sleep",	IOV_SLEEP,	0,	IOVT_BOOL,	0 },
+	{"pollrate",	IOV_POLLRATE,	0,	IOVT_UINT32,	0 },
+	{"idletime",	IOV_IDLETIME,	0,	IOVT_INT32,	0 },
+	{"idleclock",	IOV_IDLECLOCK,	0,	IOVT_INT32,	0 },
+	{"sd1idle",	IOV_SD1IDLE,	0,	IOVT_BOOL,	0 },
+	{"membytes",	IOV_MEMBYTES,	0,	IOVT_BUFFER,	2 * sizeof(int) },
+	{"memsize",	IOV_MEMSIZE,	0,	IOVT_UINT32,	0 },
+	{"download",	IOV_DOWNLOAD,	0,	IOVT_BOOL,	0 },
+	{"vars",	IOV_VARS,	0,	IOVT_BUFFER,	0 },
+	{"sdiod_drive",	IOV_SDIOD_DRIVE, 0,	IOVT_UINT32,	0 },
+	{"readahead",	IOV_READAHEAD,	0,	IOVT_BOOL,	0 },
+	{"sdrxchain",	IOV_SDRXCHAIN,	0,	IOVT_BOOL,	0 },
+	{"alignctl",	IOV_ALIGNCTL,	0,	IOVT_BOOL,	0 },
+	{"sdalign",	IOV_SDALIGN,	0,	IOVT_BOOL,	0 },
+	{"devreset",	IOV_DEVRESET,	0,	IOVT_BOOL,	0 },
+#ifdef DHD_DEBUG
+	{"sdreg",	IOV_SDREG,	0,	IOVT_BUFFER,	sizeof(sdreg_t) },
+	{"sbreg",	IOV_SBREG,	0,	IOVT_BUFFER,	sizeof(sdreg_t) },
+	{"sd_cis",	IOV_SDCIS,	0,	IOVT_BUFFER,	DHD_IOCTL_MAXLEN },
+	{"forcealign",	IOV_FORCEEVEN,	0,	IOVT_BOOL,	0 },
+	{"txbound",	IOV_TXBOUND,	0,	IOVT_UINT32,	0 },
+	{"rxbound",	IOV_RXBOUND,	0,	IOVT_UINT32,	0 },
+	{"txminmax", IOV_TXMINMAX,	0,	IOVT_UINT32,	0 },
+	{"cpu",		IOV_CPU,	0,	IOVT_BOOL,	0 },
+#endif /* DHD_DEBUG */
+#ifdef DHD_DEBUG_TRAP
+	{"checkdied",	IOV_CHECKDIED,	0,	IOVT_BUFFER,	0 },
+#endif /* DHD_DEBUG_TRAP  */
+#ifdef SDTEST
+	{"extloop",	IOV_EXTLOOP,	0,	IOVT_BOOL,	0 },
+	{"pktgen",	IOV_PKTGEN,	0,	IOVT_BUFFER,	sizeof(dhd_pktgen_t) },
+#endif /* SDTEST */
+
+	{NULL, 0, 0, 0, 0 }
+};
+
+static void
+dhd_dump_pct(struct bcmstrbuf *strbuf, char *desc, uint num, uint div)
+{
+	uint q1, q2;
+
+	if (!div) {
+		bcm_bprintf(strbuf, "%s N/A", desc);
+	} else {
+		q1 = num / div;
+		q2 = (100 * (num - (q1 * div))) / div;
+		bcm_bprintf(strbuf, "%s %d.%02d", desc, q1, q2);
+	}
+}
+
+void
+dhd_bus_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf)
+{
+	dhd_bus_t *bus = dhdp->bus;
+
+	bcm_bprintf(strbuf, "Bus SDIO structure:\n");
+	bcm_bprintf(strbuf, "hostintmask 0x%08x intstatus 0x%08x sdpcm_ver %d\n",
+	            bus->hostintmask, bus->intstatus, bus->sdpcm_ver);
+	bcm_bprintf(strbuf, "fcstate %d qlen %d tx_seq %d, max %d, rxskip %d rxlen %d rx_seq %d\n",
+	            bus->fcstate, pktq_len(&bus->txq), bus->tx_seq, bus->tx_max, bus->rxskip,
+	            bus->rxlen, bus->rx_seq);
+	bcm_bprintf(strbuf, "intr %d intrcount %d lastintrs %d spurious %d\n",
+	            bus->intr, bus->intrcount, bus->lastintrs, bus->spurious);
+	bcm_bprintf(strbuf, "pollrate %d pollcnt %d regfails %d\n",
+	            bus->pollrate, bus->pollcnt, bus->regfails);
+
+	bcm_bprintf(strbuf, "\nAdditional counters:\n");
+	bcm_bprintf(strbuf, "tx_sderrs %d fcqueued %d rxrtx %d rx_toolong %d rxc_errors %d\n",
+	            bus->tx_sderrs, bus->fcqueued, bus->rxrtx, bus->rx_toolong,
+	            bus->rxc_errors);
+	bcm_bprintf(strbuf, "rx_hdrfail %d badhdr %d badseq %d\n",
+	            bus->rx_hdrfail, bus->rx_badhdr, bus->rx_badseq);
+	bcm_bprintf(strbuf, "fc_rcvd %d, fc_xoff %d, fc_xon %d\n",
+	            bus->fc_rcvd, bus->fc_xoff, bus->fc_xon);
+	bcm_bprintf(strbuf, "rxglomfail %d, rxglomframes %d, rxglompkts %d\n",
+	            bus->rxglomfail, bus->rxglomframes, bus->rxglompkts);
+	bcm_bprintf(strbuf, "f2rx (hdrs/data) %d (%d/%d), f2tx %d f1regs %d\n",
+	            (bus->f2rxhdrs + bus->f2rxdata), bus->f2rxhdrs, bus->f2rxdata,
+	            bus->f2txdata, bus->f1regdata);
+	{
+		dhd_dump_pct(strbuf, "\nRx: pkts/f2rd", bus->dhd->rx_packets,
+		             (bus->f2rxhdrs + bus->f2rxdata));
+		dhd_dump_pct(strbuf, ", pkts/f1sd", bus->dhd->rx_packets, bus->f1regdata);
+		dhd_dump_pct(strbuf, ", pkts/sd", bus->dhd->rx_packets,
+		             (bus->f2rxhdrs + bus->f2rxdata + bus->f1regdata));
+		dhd_dump_pct(strbuf, ", pkts/int", bus->dhd->rx_packets, bus->intrcount);
+		bcm_bprintf(strbuf, "\n");
+
+		dhd_dump_pct(strbuf, "Rx: glom pct", (100 * bus->rxglompkts),
+		             bus->dhd->rx_packets);
+		dhd_dump_pct(strbuf, ", pkts/glom", bus->rxglompkts, bus->rxglomframes);
+		bcm_bprintf(strbuf, "\n");
+
+		dhd_dump_pct(strbuf, "Tx: pkts/f2wr", bus->dhd->tx_packets, bus->f2txdata);
+		dhd_dump_pct(strbuf, ", pkts/f1sd", bus->dhd->tx_packets, bus->f1regdata);
+		dhd_dump_pct(strbuf, ", pkts/sd", bus->dhd->tx_packets,
+		             (bus->f2txdata + bus->f1regdata));
+		dhd_dump_pct(strbuf, ", pkts/int", bus->dhd->tx_packets, bus->intrcount);
+		bcm_bprintf(strbuf, "\n");
+
+		dhd_dump_pct(strbuf, "Total: pkts/f2rw",
+		             (bus->dhd->tx_packets + bus->dhd->rx_packets),
+		             (bus->f2txdata + bus->f2rxhdrs + bus->f2rxdata));
+		dhd_dump_pct(strbuf, ", pkts/f1sd",
+		             (bus->dhd->tx_packets + bus->dhd->rx_packets), bus->f1regdata);
+		dhd_dump_pct(strbuf, ", pkts/sd",
+		             (bus->dhd->tx_packets + bus->dhd->rx_packets),
+		             (bus->f2txdata + bus->f2rxhdrs + bus->f2rxdata + bus->f1regdata));
+		dhd_dump_pct(strbuf, ", pkts/int",
+		             (bus->dhd->tx_packets + bus->dhd->rx_packets), bus->intrcount);
+		bcm_bprintf(strbuf, "\n\n");
+	}
+
+#ifdef SDTEST
+	if (bus->pktgen_count) {
+		bcm_bprintf(strbuf, "pktgen config and count:\n");
+		bcm_bprintf(strbuf, "freq %d count %d print %d total %d min %d len %d\n",
+		            bus->pktgen_freq, bus->pktgen_count, bus->pktgen_print,
+		            bus->pktgen_total, bus->pktgen_minlen, bus->pktgen_maxlen);
+		bcm_bprintf(strbuf, "send attempts %d rcvd %d fail %d\n",
+		            bus->pktgen_sent, bus->pktgen_rcvd, bus->pktgen_fail);
+	}
+#endif /* SDTEST */
+#ifdef DHD_DEBUG
+	bcm_bprintf(strbuf, "dpc_sched %d host interrupt%spending\n",
+	            bus->dpc_sched, (bcmsdh_intr_pending(bus->sdh) ? " " : " not "));
+	bcm_bprintf(strbuf, "blocksize %d roundup %d\n", bus->blocksize, bus->roundup);
+#endif /* DHD_DEBUG */
+	bcm_bprintf(strbuf, "clkstate %d activity %d idletime %d idlecount %d sleeping %d\n",
+	            bus->clkstate, bus->activity, bus->idletime, bus->idlecount, bus->sleeping);
+}
+
+void
+dhd_bus_clearcounts(dhd_pub_t *dhdp)
+{
+	dhd_bus_t *bus = (dhd_bus_t *)dhdp->bus;
+
+	bus->intrcount = bus->lastintrs = bus->spurious = bus->regfails = 0;
+	bus->rxrtx = bus->rx_toolong = bus->rxc_errors = 0;
+	bus->rx_hdrfail = bus->rx_badhdr = bus->rx_badseq = 0;
+	bus->tx_sderrs = bus->fc_rcvd = bus->fc_xoff = bus->fc_xon = 0;
+	bus->rxglomfail = bus->rxglomframes = bus->rxglompkts = 0;
+	bus->f2rxhdrs = bus->f2rxdata = bus->f2txdata = bus->f1regdata = 0;
+}
+
+#ifdef SDTEST
+static int
+dhdsdio_pktgen_get(dhd_bus_t *bus, uint8 *arg)
+{
+	dhd_pktgen_t pktgen;
+
+	pktgen.version = DHD_PKTGEN_VERSION;
+	pktgen.freq = bus->pktgen_freq;
+	pktgen.count = bus->pktgen_count;
+	pktgen.print = bus->pktgen_print;
+	pktgen.total = bus->pktgen_total;
+	pktgen.minlen = bus->pktgen_minlen;
+	pktgen.maxlen = bus->pktgen_maxlen;
+	pktgen.numsent = bus->pktgen_sent;
+	pktgen.numrcvd = bus->pktgen_rcvd;
+	pktgen.numfail = bus->pktgen_fail;
+	pktgen.mode = bus->pktgen_mode;
+	pktgen.stop = bus->pktgen_stop;
+
+	bcopy(&pktgen, arg, sizeof(pktgen));
+
+	return 0;
+}
+
+static int
+dhdsdio_pktgen_set(dhd_bus_t *bus, uint8 *arg)
+{
+	dhd_pktgen_t pktgen;
+	uint oldcnt, oldmode;
+
+	bcopy(arg, &pktgen, sizeof(pktgen));
+	if (pktgen.version != DHD_PKTGEN_VERSION)
+		return BCME_BADARG;
+
+	oldcnt = bus->pktgen_count;
+	oldmode = bus->pktgen_mode;
+
+	bus->pktgen_freq = pktgen.freq;
+	bus->pktgen_count = pktgen.count;
+	bus->pktgen_print = pktgen.print;
+	bus->pktgen_total = pktgen.total;
+	bus->pktgen_minlen = pktgen.minlen;
+	bus->pktgen_maxlen = pktgen.maxlen;
+	bus->pktgen_mode = pktgen.mode;
+	bus->pktgen_stop = pktgen.stop;
+
+	bus->pktgen_tick = bus->pktgen_ptick = 0;
+	bus->pktgen_len = MAX(bus->pktgen_len, bus->pktgen_minlen);
+	bus->pktgen_len = MIN(bus->pktgen_len, bus->pktgen_maxlen);
+
+	/* Clear counts for a new pktgen (mode change, or was stopped) */
+	if (bus->pktgen_count && (!oldcnt || oldmode != bus->pktgen_mode))
+		bus->pktgen_sent = bus->pktgen_rcvd = bus->pktgen_fail = 0;
+
+	return 0;
+}
+#endif /* SDTEST */
+
+static int
+dhdsdio_membytes(dhd_bus_t *bus, bool write, uint32 address, uint8 *data, uint size)
+{
+	int bcmerror = 0;
+	uint32 sdaddr;
+	uint dsize;
+
+	/* Determine initial transfer parameters */
+	sdaddr = address & SBSDIO_SB_OFT_ADDR_MASK;
+	if ((sdaddr + size) & SBSDIO_SBWINDOW_MASK)
+		dsize = (SBSDIO_SB_OFT_ADDR_LIMIT - sdaddr);
+	else
+		dsize = size;
+
+	/* Set the backplane window to include the start address */
+	if ((bcmerror = dhdsdio_set_siaddr_window(bus, address))) {
+		DHD_ERROR(("%s: window change failed\n", __FUNCTION__));
+		goto xfer_done;
+	}
+
+	/* Do the transfer(s) */
+	while (size) {
+		DHD_INFO(("%s: %s %d bytes at offset 0x%08x in window 0x%08x\n",
+		          __FUNCTION__, (write ? "write" : "read"), dsize, sdaddr,
+		          (address & SBSDIO_SBWINDOW_MASK)));
+		if ((bcmerror = bcmsdh_rwdata(bus->sdh, write, sdaddr, data, dsize))) {
+			DHD_ERROR(("%s: membytes transfer failed\n", __FUNCTION__));
+			break;
+		}
+
+		/* Adjust for next transfer (if any) */
+		if ((size -= dsize)) {
+			data += dsize;
+			address += dsize;
+			if ((bcmerror = dhdsdio_set_siaddr_window(bus, address))) {
+				DHD_ERROR(("%s: window change failed\n", __FUNCTION__));
+				break;
+			}
+			sdaddr = 0;
+			dsize = MIN(SBSDIO_SB_OFT_ADDR_LIMIT, size);
+		}
+	}
+
+xfer_done:
+	/* Return the window to backplane enumeration space for core access */
+	if (dhdsdio_set_siaddr_window(bus, bcmsdh_cur_sbwad(bus->sdh))) {
+		DHD_ERROR(("%s: FAILED to set window back to 0x%x\n", __FUNCTION__,
+			bcmsdh_cur_sbwad(bus->sdh)));
+	}
+
+	return bcmerror;
+}
+
+#ifdef DHD_DEBUG_TRAP
+static int
+dhdsdio_readshared(dhd_bus_t *bus, sdpcm_shared_t *sh)
+{
+	uint32 addr;
+	int rv;
+
+	/* Read last word in memory to determine address of sdpcm_shared structure */
+	if ((rv = dhdsdio_membytes(bus, FALSE, bus->ramsize - 4, (uint8 *)&addr, 4)) < 0)
+		return rv;
+
+	addr = ltoh32(addr);
+
+	DHD_INFO(("sdpcm_shared address 0x%08X\n", addr));
+
+	/*
+	 * Check if addr is valid.
+	 * NVRAM length at the end of memory should have been overwritten.
+	 */
+	if (addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff)) {
+		DHD_ERROR(("%s: address (0x%08x) of sdpcm_shared invalid\n", __FUNCTION__, addr));
+		return BCME_ERROR;
+	}
+
+	/* Read hndrte_shared structure */
+	if ((rv = dhdsdio_membytes(bus, FALSE, addr, (uint8 *)sh, sizeof(sdpcm_shared_t))) < 0)
+		return rv;
+
+	/* Endianness */
+	sh->flags = ltoh32(sh->flags);
+	sh->trap_addr = ltoh32(sh->trap_addr);
+	sh->assert_exp_addr = ltoh32(sh->assert_exp_addr);
+	sh->assert_file_addr = ltoh32(sh->assert_file_addr);
+	sh->assert_line = ltoh32(sh->assert_line);
+	sh->console_addr = ltoh32(sh->console_addr);
+	sh->msgtrace_addr = ltoh32(sh->msgtrace_addr);
+
+	if ((sh->flags & SDPCM_SHARED_VERSION_MASK) != SDPCM_SHARED_VERSION) {
+		DHD_ERROR(("%s: sdpcm_shared version %d in dhd "
+		           "is different than sdpcm_shared version %d in dongle\n",
+		           __FUNCTION__, SDPCM_SHARED_VERSION,
+		           sh->flags & SDPCM_SHARED_VERSION_MASK));
+		return BCME_ERROR;
+	}
+
+	return BCME_OK;
+}
+
+static int
+dhdsdio_checkdied(dhd_bus_t *bus, uint8 *data, uint size)
+{
+	int bcmerror = 0;
+	uint msize = 512;
+	char *mbuffer = NULL;
+	uint maxstrlen = 256;
+	char *str = NULL;
+	trap_t tr;
+	sdpcm_shared_t sdpcm_shared;
+	struct bcmstrbuf strbuf;
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	if (data == NULL) {
+		/*
+		 * Called after a rx ctrl timeout. "data" is NULL.
+		 * allocate memory to trace the trap or assert.
+		 */
+		size = msize;
+		mbuffer = data = MALLOC(bus->dhd->osh, msize);
+		if (mbuffer == NULL) {
+			DHD_ERROR(("%s: MALLOC(%d) failed \n", __FUNCTION__, msize));
+			bcmerror = BCME_NOMEM;
+			goto done;
+		}
+	}
+
+	if ((str = MALLOC(bus->dhd->osh, maxstrlen)) == NULL) {
+		DHD_ERROR(("%s: MALLOC(%d) failed \n", __FUNCTION__, maxstrlen));
+		bcmerror = BCME_NOMEM;
+		goto done;
+	}
+
+	if ((bcmerror = dhdsdio_readshared(bus, &sdpcm_shared)) < 0)
+		goto done;
+
+	bcm_binit(&strbuf, data, size);
+
+	bcm_bprintf(&strbuf, "msgtrace address : 0x%08X\nconsole address  : 0x%08X\n",
+	            sdpcm_shared.msgtrace_addr, sdpcm_shared.console_addr);
+
+	if ((sdpcm_shared.flags & SDPCM_SHARED_ASSERT_BUILT) == 0) {
+		/* NOTE: Misspelled assert is intentional - DO NOT FIX.
+		 * (Avoids conflict with real asserts for programmatic parsing of output.)
+		 */
+		bcm_bprintf(&strbuf, "Assrt not built in dongle\n");
+	}
+
+	if ((sdpcm_shared.flags & (SDPCM_SHARED_ASSERT|SDPCM_SHARED_TRAP)) == 0) {
+		/* NOTE: Misspelled assert is intentional - DO NOT FIX.
+		 * (Avoids conflict with real asserts for programmatic parsing of output.)
+		 */
+		bcm_bprintf(&strbuf, "No trap%s in dongle",
+		          (sdpcm_shared.flags & SDPCM_SHARED_ASSERT_BUILT)
+		          ?"/assrt" :"");
+	} else {
+		if (sdpcm_shared.flags & SDPCM_SHARED_ASSERT) {
+			/* Download assert */
+			bcm_bprintf(&strbuf, "Dongle assert");
+			if (sdpcm_shared.assert_exp_addr != 0) {
+				str[0] = '\0';
+				if ((bcmerror = dhdsdio_membytes(bus, FALSE,
+				                                 sdpcm_shared.assert_exp_addr,
+				                                 (uint8 *)str, maxstrlen)) < 0)
+					goto done;
+
+				str[maxstrlen - 1] = '\0';
+				bcm_bprintf(&strbuf, " expr \"%s\"", str);
+			}
+
+			if (sdpcm_shared.assert_file_addr != 0) {
+				str[0] = '\0';
+				if ((bcmerror = dhdsdio_membytes(bus, FALSE,
+				                                 sdpcm_shared.assert_file_addr,
+				                                 (uint8 *)str, maxstrlen)) < 0)
+					goto done;
+
+				str[maxstrlen - 1] = '\0';
+				bcm_bprintf(&strbuf, " file \"%s\"", str);
+			}
+
+			bcm_bprintf(&strbuf, " line %d ", sdpcm_shared.assert_line);
+		}
+
+		if (sdpcm_shared.flags & SDPCM_SHARED_TRAP) {
+			if ((bcmerror = dhdsdio_membytes(bus, FALSE,
+			                                 sdpcm_shared.trap_addr,
+			                                 (uint8*)&tr, sizeof(trap_t))) < 0)
+				goto done;
+
+			bcm_bprintf(&strbuf,
+			"Dongle trap type 0x%x @ epc 0x%x, cpsr 0x%x, spsr 0x%x, sp 0x%x,"
+			"lp 0x%x, rpc 0x%x Trap offset 0x%x, "
+			"r0 0x%x, r1 0x%x, r2 0x%x, r3 0x%x, r4 0x%x, r5 0x%x, r6 0x%x, r7 0x%x\n",
+			tr.type, tr.epc, tr.cpsr, tr.spsr, tr.r13, tr.r14, tr.pc,
+			sdpcm_shared.trap_addr,
+			tr.r0, tr.r1, tr.r2, tr.r3, tr.r4, tr.r5, tr.r6, tr.r7);
+		}
+	}
+
+	if (sdpcm_shared.flags & (SDPCM_SHARED_ASSERT | SDPCM_SHARED_TRAP)) {
+		DHD_ERROR(("%s: %s\n", __FUNCTION__, strbuf.origbuf));
+	}
+
+done:
+	if (mbuffer)
+		MFREE(bus->dhd->osh, mbuffer, msize);
+	if (str)
+		MFREE(bus->dhd->osh, str, maxstrlen);
+
+	return bcmerror;
+}
+#endif /* DHD_DEBUG_TRAP */
+
+#ifdef DHD_DEBUG
+#define CONSOLE_LINE_MAX	192
+
+static int
+dhdsdio_readconsole(dhd_bus_t *bus)
+{
+	dhd_console_t *c = &bus->console;
+	uint8 line[CONSOLE_LINE_MAX], ch;
+	uint32 n, idx, addr;
+	int rv;
+
+	/* Don't do anything until FWREADY updates console address */
+	if (bus->console_addr == 0)
+		return 0;
+
+	/* Read console log struct */
+	addr = bus->console_addr + OFFSETOF(hndrte_cons_t, log);
+	if ((rv = dhdsdio_membytes(bus, FALSE, addr, (uint8 *)&c->log, sizeof(c->log))) < 0)
+		return rv;
+
+	/* Allocate console buffer (one time only) */
+	if (c->buf == NULL) {
+		c->bufsize = ltoh32(c->log.buf_size);
+		if ((c->buf = MALLOC(bus->dhd->osh, c->bufsize)) == NULL)
+			return BCME_NOMEM;
+	}
+
+	idx = ltoh32(c->log.idx);
+
+	/* Protect against corrupt value */
+	if (idx > c->bufsize)
+		return BCME_ERROR;
+
+	/* Skip reading the console buffer if the index pointer has not moved */
+	if (idx == c->last)
+		return BCME_OK;
+
+	/* Read the console buffer */
+	addr = ltoh32(c->log.buf);
+	if ((rv = dhdsdio_membytes(bus, FALSE, addr, c->buf, c->bufsize)) < 0)
+		return rv;
+
+	while (c->last != idx) {
+		for (n = 0; n < CONSOLE_LINE_MAX - 2; n++) {
+			if (c->last == idx) {
+				/* This would output a partial line.  Instead, back up
+				 * the buffer pointer and output this line next time around.
+				 */
+				if (c->last >= n)
+					c->last -= n;
+				else
+					c->last = c->bufsize - n;
+				goto break2;
+			}
+			ch = c->buf[c->last];
+			c->last = (c->last + 1) % c->bufsize;
+			if (ch == '\n')
+				break;
+			line[n] = ch;
+		}
+
+		if (n > 0) {
+			if (line[n - 1] == '\r')
+				n--;
+			line[n] = 0;
+			printf("CONSOLE: %s\n", line);
+		}
+	}
+break2:
+
+	return BCME_OK;
+}
+#endif /* DHD_DEBUG */
+
+int
+dhdsdio_downloadvars(dhd_bus_t *bus, void *arg, int len)
+{
+	int bcmerror = BCME_OK;
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	/* Basic sanity checks */
+	if (bus->dhd->up) {
+		bcmerror = BCME_NOTDOWN;
+		goto err;
+	}
+	if (!len) {
+		bcmerror = BCME_BUFTOOSHORT;
+		goto err;
+	}
+
+	/* Free the old ones and replace with passed variables */
+	if (bus->vars)
+		MFREE(bus->dhd->osh, bus->vars, bus->varsz);
+
+	bus->vars = MALLOC(bus->dhd->osh, len);
+	bus->varsz = bus->vars ? len : 0;
+	if (bus->vars == NULL) {
+		bcmerror = BCME_NOMEM;
+		goto err;
+	}
+
+	/* Copy the passed variables, which should include the terminating double-null */
+	bcopy(arg, bus->vars, bus->varsz);
+err:
+	return bcmerror;
+}
+
+static int
+dhdsdio_doiovar(dhd_bus_t *bus, const bcm_iovar_t *vi, uint32 actionid, const char *name,
+                void *params, int plen, void *arg, int len, int val_size)
+{
+	int bcmerror = 0;
+	int32 int_val = 0;
+	bool bool_val = 0;
+
+	DHD_TRACE(("%s: Enter, action %d name %s params %p plen %d arg %p len %d val_size %d\n",
+	           __FUNCTION__, actionid, name, params, plen, arg, len, val_size));
+
+	if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, IOV_ISSET(actionid))) != 0)
+		goto exit;
+
+	if (plen >= (int)sizeof(int_val))
+		bcopy(params, &int_val, sizeof(int_val));
+
+	bool_val = (int_val != 0) ? TRUE : FALSE;
+
+
+	/* Some ioctls use the bus */
+	dhd_os_sdlock(bus->dhd);
+
+	/* Check if dongle is in reset. If so, only allow DEVRESET iovars */
+	if (bus->dhd->dongle_reset && !(actionid == IOV_SVAL(IOV_DEVRESET) ||
+	                                actionid == IOV_GVAL(IOV_DEVRESET))) {
+		bcmerror = BCME_NOTREADY;
+		goto exit;
+	}
+
+	/* Handle sleep stuff before any clock mucking */
+	if (vi->varid == IOV_SLEEP) {
+		if (IOV_ISSET(actionid)) {
+			bcmerror = dhdsdio_bussleep(bus, bool_val);
+		} else {
+			int_val = (int32)bus->sleeping;
+			bcopy(&int_val, arg, val_size);
+		}
+		goto exit;
+	}
+
+	/* Request clock to allow SDIO accesses */
+	if (!bus->dhd->dongle_reset) {
+		BUS_WAKE(bus);
+		dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+	}
+
+	switch (actionid) {
+	case IOV_GVAL(IOV_INTR):
+		int_val = (int32)bus->intr;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_INTR):
+		bus->intr = bool_val;
+		bus->intdis = FALSE;
+		if (bus->dhd->up) {
+			if (bus->intr) {
+				DHD_INTR(("%s: enable SDIO device interrupts\n", __FUNCTION__));
+				bcmsdh_intr_enable(bus->sdh);
+			} else {
+				DHD_INTR(("%s: disable SDIO interrupts\n", __FUNCTION__));
+				bcmsdh_intr_disable(bus->sdh);
+			}
+		}
+		break;
+
+	case IOV_GVAL(IOV_POLLRATE):
+		int_val = (int32)bus->pollrate;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_POLLRATE):
+		bus->pollrate = (uint)int_val;
+		bus->poll = (bus->pollrate != 0);
+		break;
+
+	case IOV_GVAL(IOV_IDLETIME):
+		int_val = bus->idletime;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_IDLETIME):
+		if ((int_val < 0) && (int_val != DHD_IDLE_IMMEDIATE)) {
+			bcmerror = BCME_BADARG;
+		} else {
+			bus->idletime = int_val;
+		}
+		break;
+
+	case IOV_GVAL(IOV_IDLECLOCK):
+		int_val = (int32)bus->idleclock;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_IDLECLOCK):
+		bus->idleclock = int_val;
+		break;
+
+	case IOV_GVAL(IOV_SD1IDLE):
+		int_val = (int32)sd1idle;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_SD1IDLE):
+		sd1idle = bool_val;
+		break;
+
+
+	case IOV_SVAL(IOV_MEMBYTES):
+	case IOV_GVAL(IOV_MEMBYTES):
+	{
+		uint32 address;
+		uint size, dsize;
+		uint8 *data;
+
+		bool set = (actionid == IOV_SVAL(IOV_MEMBYTES));
+
+		ASSERT(plen >= 2*sizeof(int));
+
+		address = (uint32)int_val;
+		bcopy((char *)params + sizeof(int_val), &int_val, sizeof(int_val));
+		size = (uint)int_val;
+
+		/* Do some validation */
+		dsize = set ? plen - (2 * sizeof(int)) : len;
+		if (dsize < size) {
+			DHD_ERROR(("%s: error on %s membytes, addr 0x%08x size %d dsize %d\n",
+			           __FUNCTION__, (set ? "set" : "get"), address, size, dsize));
+			bcmerror = BCME_BADARG;
+			break;
+		}
+
+		DHD_INFO(("%s: Request to %s %d bytes at address 0x%08x\n", __FUNCTION__,
+		          (set ? "write" : "read"), size, address));
+
+		/* If we know about SOCRAM, check for a fit */
+		if ((bus->orig_ramsize) &&
+		    ((address > bus->orig_ramsize) || (address + size > bus->orig_ramsize))) {
+			DHD_ERROR(("%s: ramsize 0x%08x doesn't have %d bytes at 0x%08x\n",
+			           __FUNCTION__, bus->orig_ramsize, size, address));
+			bcmerror = BCME_BADARG;
+			break;
+		}
+
+		/* Generate the actual data pointer */
+		data = set ? (uint8*)params + 2 * sizeof(int): (uint8*)arg;
+
+		/* Call to do the transfer */
+		bcmerror = dhdsdio_membytes(bus, set, address, data, size);
+
+		break;
+	}
+
+	case IOV_GVAL(IOV_MEMSIZE):
+		int_val = (int32)bus->ramsize;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_GVAL(IOV_SDIOD_DRIVE):
+		int_val = (int32)dhd_sdiod_drive_strength;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_SDIOD_DRIVE):
+		dhd_sdiod_drive_strength = int_val;
+		si_sdiod_drive_strength_init(bus->sih, bus->dhd->osh, dhd_sdiod_drive_strength);
+		break;
+
+	case IOV_SVAL(IOV_DOWNLOAD):
+		bcmerror = dhdsdio_download_state(bus, bool_val);
+		break;
+
+	case IOV_SVAL(IOV_VARS):
+		bcmerror = dhdsdio_downloadvars(bus, arg, len);
+		break;
+
+	case IOV_GVAL(IOV_READAHEAD):
+		int_val = (int32)dhd_readahead;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_READAHEAD):
+		if (bool_val && !dhd_readahead)
+			bus->nextlen = 0;
+		dhd_readahead = bool_val;
+		break;
+
+	case IOV_GVAL(IOV_SDRXCHAIN):
+		int_val = (int32)bus->use_rxchain;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_SDRXCHAIN):
+		if (bool_val && !bus->sd_rxchain)
+			bcmerror = BCME_UNSUPPORTED;
+		else
+			bus->use_rxchain = bool_val;
+		break;
+	case IOV_GVAL(IOV_ALIGNCTL):
+		int_val = (int32)dhd_alignctl;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_ALIGNCTL):
+		dhd_alignctl = bool_val;
+		break;
+
+	case IOV_GVAL(IOV_SDALIGN):
+		int_val = DHD_SDALIGN;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+#ifdef DHD_DEBUG
+	case IOV_GVAL(IOV_VARS):
+		if (bus->varsz < (uint)len)
+			bcopy(bus->vars, arg, bus->varsz);
+		else
+			bcmerror = BCME_BUFTOOSHORT;
+		break;
+#endif /* DHD_DEBUG */
+
+#ifdef DHD_DEBUG
+	case IOV_GVAL(IOV_SDREG):
+	{
+		sdreg_t *sd_ptr;
+		uint32 addr, size;
+
+		sd_ptr = (sdreg_t *)params;
+
+		addr = (uintptr)bus->regs + sd_ptr->offset;
+		size = sd_ptr->func;
+		int_val = (int32)bcmsdh_reg_read(bus->sdh, addr, size);
+		if (bcmsdh_regfail(bus->sdh))
+			bcmerror = BCME_SDIO_ERROR;
+		bcopy(&int_val, arg, sizeof(int32));
+		break;
+	}
+
+	case IOV_SVAL(IOV_SDREG):
+	{
+		sdreg_t *sd_ptr;
+		uint32 addr, size;
+
+		sd_ptr = (sdreg_t *)params;
+
+		addr = (uintptr)bus->regs + sd_ptr->offset;
+		size = sd_ptr->func;
+		bcmsdh_reg_write(bus->sdh, addr, size, sd_ptr->value);
+		if (bcmsdh_regfail(bus->sdh))
+			bcmerror = BCME_SDIO_ERROR;
+		break;
+	}
+
+	/* Same as above, but offset is not backplane (not SDIO core) */
+	case IOV_GVAL(IOV_SBREG):
+	{
+		sdreg_t sdreg;
+		uint32 addr, size;
+
+		bcopy(params, &sdreg, sizeof(sdreg));
+
+		addr = SI_ENUM_BASE + sdreg.offset;
+		size = sdreg.func;
+		int_val = (int32)bcmsdh_reg_read(bus->sdh, addr, size);
+		if (bcmsdh_regfail(bus->sdh))
+			bcmerror = BCME_SDIO_ERROR;
+		bcopy(&int_val, arg, sizeof(int32));
+		break;
+	}
+
+	case IOV_SVAL(IOV_SBREG):
+	{
+		sdreg_t sdreg;
+		uint32 addr, size;
+
+		bcopy(params, &sdreg, sizeof(sdreg));
+
+		addr = SI_ENUM_BASE + sdreg.offset;
+		size = sdreg.func;
+		bcmsdh_reg_write(bus->sdh, addr, size, sdreg.value);
+		if (bcmsdh_regfail(bus->sdh))
+			bcmerror = BCME_SDIO_ERROR;
+		break;
+	}
+
+	case IOV_GVAL(IOV_SDCIS):
+	{
+		*(char *)arg = 0;
+
+		bcmstrcat(arg, "\nFunc 0\n");
+		bcmsdh_cis_read(bus->sdh, 0x10, (uint8 *)arg + strlen(arg), SBSDIO_CIS_SIZE_LIMIT);
+		bcmstrcat(arg, "\nFunc 1\n");
+		bcmsdh_cis_read(bus->sdh, 0x11, (uint8 *)arg + strlen(arg), SBSDIO_CIS_SIZE_LIMIT);
+		bcmstrcat(arg, "\nFunc 2\n");
+		bcmsdh_cis_read(bus->sdh, 0x12, (uint8 *)arg + strlen(arg), SBSDIO_CIS_SIZE_LIMIT);
+		break;
+	}
+
+	case IOV_GVAL(IOV_FORCEEVEN):
+		int_val = (int32)forcealign;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_FORCEEVEN):
+		forcealign = bool_val;
+		break;
+
+	case IOV_GVAL(IOV_TXBOUND):
+		int_val = (int32)dhd_txbound;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_TXBOUND):
+		dhd_txbound = (uint)int_val;
+		break;
+
+	case IOV_GVAL(IOV_RXBOUND):
+		int_val = (int32)dhd_rxbound;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_RXBOUND):
+		dhd_rxbound = (uint)int_val;
+		break;
+
+	case IOV_GVAL(IOV_TXMINMAX):
+		int_val = (int32)dhd_txminmax;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_TXMINMAX):
+		dhd_txminmax = (uint)int_val;
+		break;
+
+
+
+#endif /* DHD_DEBUG */
+
+
+#ifdef SDTEST
+	case IOV_GVAL(IOV_EXTLOOP):
+		int_val = (int32)bus->ext_loop;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_EXTLOOP):
+		bus->ext_loop = bool_val;
+		break;
+
+	case IOV_GVAL(IOV_PKTGEN):
+		bcmerror = dhdsdio_pktgen_get(bus, arg);
+		break;
+
+	case IOV_SVAL(IOV_PKTGEN):
+		bcmerror = dhdsdio_pktgen_set(bus, arg);
+		break;
+#endif /* SDTEST */
+
+
+	case IOV_SVAL(IOV_DEVRESET):
+		DHD_TRACE(("%s: Called set IOV_DEVRESET=%d dongle_reset=%d busstate=%d\n",
+		           __FUNCTION__, bool_val, bus->dhd->dongle_reset,
+		           bus->dhd->busstate));
+
+		ASSERT(bus->dhd->osh);
+		/* ASSERT(bus->cl_devid); */
+
+		dhd_bus_devreset(bus->dhd, (uint8)bool_val);
+
+		break;
+
+	case IOV_GVAL(IOV_DEVRESET):
+		DHD_TRACE(("%s: Called get IOV_DEVRESET\n", __FUNCTION__));
+
+		/* Get its status */
+		int_val = (bool) bus->dhd->dongle_reset;
+		bcopy(&int_val, arg, val_size);
+
+		break;
+
+	default:
+		bcmerror = BCME_UNSUPPORTED;
+		break;
+	}
+
+exit:
+	if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) {
+		bus->activity = FALSE;
+		dhdsdio_clkctl(bus, CLK_NONE, TRUE);
+	}
+
+	dhd_os_sdunlock(bus->dhd);
+
+	if (actionid == IOV_SVAL(IOV_DEVRESET) && bool_val == FALSE)
+		dhd_preinit_ioctls((dhd_pub_t *) bus->dhd);
+
+	return bcmerror;
+}
+
+static int
+dhdsdio_write_vars(dhd_bus_t *bus)
+{
+	int bcmerror = 0;
+	uint32 varsize;
+	uint32 varaddr;
+	uint8 *vbuffer;
+	uint32 varsizew;
+#ifdef DHD_DEBUG
+	char *nvram_ularray;
+#endif /* DHD_DEBUG */
+
+	/* Even if there are no vars are to be written, we still need to set the ramsize. */
+	varsize = bus->varsz ? ROUNDUP(bus->varsz, 4) : 0;
+	varaddr = (bus->ramsize - 4) - varsize;
+
+	if (bus->vars) {
+		vbuffer = (uint8 *)MALLOC(bus->dhd->osh, varsize);
+		if (!vbuffer)
+			return BCME_NOMEM;
+
+		bzero(vbuffer, varsize);
+		bcopy(bus->vars, vbuffer, bus->varsz);
+
+		/* Write the vars list */
+		bcmerror = dhdsdio_membytes(bus, TRUE, varaddr, vbuffer, varsize);
+#ifdef DHD_DEBUG
+		/* Verify NVRAM bytes */
+		DHD_INFO(("Compare NVRAM dl & ul; varsize=%d\n", varsize));
+		nvram_ularray = (char*)MALLOC(bus->dhd->osh, varsize);
+		if (!nvram_ularray)
+			return BCME_NOMEM;
+
+		/* Upload image to verify downloaded contents. */
+		memset(nvram_ularray, 0xaa, varsize);
+
+		/* Read the vars list to temp buffer for comparison */
+		bcmerror = dhdsdio_membytes(bus, FALSE, varaddr, nvram_ularray, varsize);
+		if (bcmerror) {
+				DHD_ERROR(("%s: error %d on reading %d nvram bytes at 0x%08x\n",
+					__FUNCTION__, bcmerror, varsize, varaddr));
+		}
+		/* Compare the org NVRAM with the one read from RAM */
+		if (memcmp(vbuffer, nvram_ularray, varsize)) {
+			DHD_ERROR(("%s: Downloaded NVRAM image is corrupted.\n", __FUNCTION__));
+		} else
+			DHD_ERROR(("%s: Download, Upload and compare of NVRAM succeeded.\n",
+			__FUNCTION__));
+
+		MFREE(bus->dhd->osh, nvram_ularray, varsize);
+#endif /* DHD_DEBUG */
+
+		MFREE(bus->dhd->osh, vbuffer, varsize);
+	}
+
+	/* adjust to the user specified RAM */
+	DHD_INFO(("Physical memory size: %d, usable memory size: %d\n",
+		bus->orig_ramsize, bus->ramsize));
+	DHD_INFO(("Vars are at %d, orig varsize is %d\n",
+		varaddr, varsize));
+	varsize = ((bus->orig_ramsize - 4) - varaddr);
+
+	/*
+	 * Determine the length token:
+	 * Varsize, converted to words, in lower 16-bits, checksum in upper 16-bits.
+	 */
+	if (bcmerror) {
+		varsizew = 0;
+	} else {
+		varsizew = varsize / 4;
+		varsizew = (~varsizew << 16) | (varsizew & 0x0000FFFF);
+		varsizew = htol32(varsizew);
+	}
+
+	DHD_INFO(("New varsize is %d, length token=0x%08x\n", varsize, varsizew));
+
+	/* Write the length token to the last word */
+	bcmerror = dhdsdio_membytes(bus, TRUE, (bus->orig_ramsize - 4),
+		(uint8*)&varsizew, 4);
+
+	return bcmerror;
+}
+
+static int
+dhdsdio_download_state(dhd_bus_t *bus, bool enter)
+{
+	uint retries;
+	int bcmerror = 0;
+
+	/* To enter download state, disable ARM and reset SOCRAM.
+	 * To exit download state, simply reset ARM (default is RAM boot).
+	 */
+	if (enter) {
+
+		bus->alp_only = TRUE;
+
+		if (!(si_setcore(bus->sih, ARM7S_CORE_ID, 0)) &&
+		    !(si_setcore(bus->sih, ARMCM3_CORE_ID, 0))) {
+			DHD_ERROR(("%s: Failed to find ARM core!\n", __FUNCTION__));
+			bcmerror = BCME_ERROR;
+			goto fail;
+		}
+
+		si_core_disable(bus->sih, 0);
+		if (bcmsdh_regfail(bus->sdh)) {
+			bcmerror = BCME_SDIO_ERROR;
+			goto fail;
+		}
+
+		if (!(si_setcore(bus->sih, SOCRAM_CORE_ID, 0))) {
+			DHD_ERROR(("%s: Failed to find SOCRAM core!\n", __FUNCTION__));
+			bcmerror = BCME_ERROR;
+			goto fail;
+		}
+
+		si_core_reset(bus->sih, 0, 0);
+		if (bcmsdh_regfail(bus->sdh)) {
+			DHD_ERROR(("%s: Failure trying reset SOCRAM core?\n", __FUNCTION__));
+			bcmerror = BCME_SDIO_ERROR;
+			goto fail;
+		}
+
+		/* Clear the top bit of memory */
+		if (bus->ramsize) {
+			uint32 zeros = 0;
+			dhdsdio_membytes(bus, TRUE, bus->ramsize - 4, (uint8*)&zeros, 4);
+		}
+	} else {
+		if (!(si_setcore(bus->sih, SOCRAM_CORE_ID, 0))) {
+			DHD_ERROR(("%s: Failed to find SOCRAM core!\n", __FUNCTION__));
+			bcmerror = BCME_ERROR;
+			goto fail;
+		}
+
+		if (!si_iscoreup(bus->sih)) {
+			DHD_ERROR(("%s: SOCRAM core is down after reset?\n", __FUNCTION__));
+			bcmerror = BCME_ERROR;
+			goto fail;
+		}
+
+		if ((bcmerror = dhdsdio_write_vars(bus))) {
+			DHD_ERROR(("%s: no vars written to RAM\n", __FUNCTION__));
+			bcmerror = 0;
+		}
+
+		if (!si_setcore(bus->sih, PCMCIA_CORE_ID, 0) &&
+		    !si_setcore(bus->sih, SDIOD_CORE_ID, 0)) {
+			DHD_ERROR(("%s: Can't change back to SDIO core?\n", __FUNCTION__));
+			bcmerror = BCME_ERROR;
+			goto fail;
+		}
+		W_SDREG(0xFFFFFFFF, &bus->regs->intstatus, retries);
+
+
+		if (!(si_setcore(bus->sih, ARM7S_CORE_ID, 0)) &&
+		    !(si_setcore(bus->sih, ARMCM3_CORE_ID, 0))) {
+			DHD_ERROR(("%s: Failed to find ARM core!\n", __FUNCTION__));
+			bcmerror = BCME_ERROR;
+			goto fail;
+		}
+
+		si_core_reset(bus->sih, 0, 0);
+		if (bcmsdh_regfail(bus->sdh)) {
+			DHD_ERROR(("%s: Failure trying to reset ARM core?\n", __FUNCTION__));
+			bcmerror = BCME_SDIO_ERROR;
+			goto fail;
+		}
+
+		/* Allow HT Clock now that the ARM is running. */
+		bus->alp_only = FALSE;
+
+		bus->dhd->busstate = DHD_BUS_LOAD;
+	}
+
+fail:
+	/* Always return to SDIOD core */
+	if (!si_setcore(bus->sih, PCMCIA_CORE_ID, 0))
+		si_setcore(bus->sih, SDIOD_CORE_ID, 0);
+
+	return bcmerror;
+}
+
+int
+dhd_bus_iovar_op(dhd_pub_t *dhdp, const char *name,
+                 void *params, int plen, void *arg, int len, bool set)
+{
+	dhd_bus_t *bus = dhdp->bus;
+	const bcm_iovar_t *vi = NULL;
+	int bcmerror = 0;
+	int val_size;
+	uint32 actionid;
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	ASSERT(name);
+	ASSERT(len >= 0);
+
+	/* Get MUST have return space */
+	ASSERT(set || (arg && len));
+
+	/* Set does NOT take qualifiers */
+	ASSERT(!set || (!params && !plen));
+
+	/* Look up var locally; if not found pass to host driver */
+	if ((vi = bcm_iovar_lookup(dhdsdio_iovars, name)) == NULL) {
+		dhd_os_sdlock(bus->dhd);
+
+		BUS_WAKE(bus);
+
+		/* Turn on clock in case SD command needs backplane */
+		dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+
+		bcmerror = bcmsdh_iovar_op(bus->sdh, name, params, plen, arg, len, set);
+
+		/* Check for bus configuration changes of interest */
+
+		/* If it was divisor change, read the new one */
+		if (set && strcmp(name, "sd_divisor") == 0) {
+			if (bcmsdh_iovar_op(bus->sdh, "sd_divisor", NULL, 0,
+			                    &bus->sd_divisor, sizeof(int32), FALSE) != BCME_OK) {
+				bus->sd_divisor = -1;
+				DHD_ERROR(("%s: fail on %s get\n", __FUNCTION__, name));
+			} else {
+				DHD_INFO(("%s: noted %s update, value now %d\n",
+				          __FUNCTION__, name, bus->sd_divisor));
+			}
+		}
+		/* If it was a mode change, read the new one */
+		if (set && strcmp(name, "sd_mode") == 0) {
+			if (bcmsdh_iovar_op(bus->sdh, "sd_mode", NULL, 0,
+			                    &bus->sd_mode, sizeof(int32), FALSE) != BCME_OK) {
+				bus->sd_mode = -1;
+				DHD_ERROR(("%s: fail on %s get\n", __FUNCTION__, name));
+			} else {
+				DHD_INFO(("%s: noted %s update, value now %d\n",
+				          __FUNCTION__, name, bus->sd_mode));
+			}
+		}
+		/* Similar check for blocksize change */
+		if (set && strcmp(name, "sd_blocksize") == 0) {
+			int32 fnum = 2;
+			if (bcmsdh_iovar_op(bus->sdh, "sd_blocksize", &fnum, sizeof(int32),
+			                    &bus->blocksize, sizeof(int32), FALSE) != BCME_OK) {
+				bus->blocksize = 0;
+				DHD_ERROR(("%s: fail on %s get\n", __FUNCTION__, "sd_blocksize"));
+			} else {
+				DHD_INFO(("%s: noted %s update, value now %d\n",
+				          __FUNCTION__, "sd_blocksize", bus->blocksize));
+			}
+		}
+		bus->roundup = MIN(max_roundup, bus->blocksize);
+
+		if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) {
+			bus->activity = FALSE;
+			dhdsdio_clkctl(bus, CLK_NONE, TRUE);
+		}
+
+		dhd_os_sdunlock(bus->dhd);
+		goto exit;
+	}
+
+	DHD_CTL(("%s: %s %s, len %d plen %d\n", __FUNCTION__,
+	         name, (set ? "set" : "get"), len, plen));
+
+	/* set up 'params' pointer in case this is a set command so that
+	 * the convenience int and bool code can be common to set and get
+	 */
+	if (params == NULL) {
+		params = arg;
+		plen = len;
+	}
+
+	if (vi->type == IOVT_VOID)
+		val_size = 0;
+	else if (vi->type == IOVT_BUFFER)
+		val_size = len;
+	else
+		/* all other types are integer sized */
+		val_size = sizeof(int);
+
+	actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid);
+	bcmerror = dhdsdio_doiovar(bus, vi, actionid, name, params, plen, arg, len, val_size);
+
+exit:
+	return bcmerror;
+}
+
+void
+dhd_bus_stop(struct dhd_bus *bus, bool enforce_mutex)
+{
+	osl_t *osh = bus->dhd->osh;
+	uint32 local_hostintmask;
+	uint8 saveclk;
+	uint retries;
+	int err;
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	if (enforce_mutex)
+		dhd_os_sdlock(bus->dhd);
+
+	BUS_WAKE(bus);
+
+	/* Change our idea of bus state */
+	bus->dhd->busstate = DHD_BUS_DOWN;
+
+	/* Enable clock for device interrupts */
+	dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+
+	/* Disable and clear interrupts at the chip level also */
+	W_SDREG(0, &bus->regs->hostintmask, retries);
+	local_hostintmask = bus->hostintmask;
+	bus->hostintmask = 0;
+
+	/* Force clocks on backplane to be sure F2 interrupt propagates */
+	saveclk = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err);
+	if (!err) {
+		bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
+		                 (saveclk | SBSDIO_FORCE_HT), &err);
+	}
+	if (err) {
+		DHD_ERROR(("%s: Failed to force clock for F2: err %d\n", __FUNCTION__, err));
+	}
+
+	/* Turn off the bus (F2), free any pending packets */
+	DHD_INTR(("%s: disable SDIO interrupts\n", __FUNCTION__));
+	bcmsdh_intr_disable(bus->sdh);
+	bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_IOEN, SDIO_FUNC_ENABLE_1, NULL);
+
+	/* Clear any pending interrupts now that F2 is disabled */
+	W_SDREG(local_hostintmask, &bus->regs->intstatus, retries);
+
+	/* Turn off the backplane clock (only) */
+	dhdsdio_clkctl(bus, CLK_SDONLY, FALSE);
+
+	/* Clear the data packet queues */
+	pktq_flush(osh, &bus->txq, TRUE);
+
+	/* Clear any held glomming stuff */
+	if (bus->glomd)
+		PKTFREE(osh, bus->glomd, FALSE);
+
+	if (bus->glom)
+		PKTFREE(osh, bus->glom, FALSE);
+
+	bus->glom = bus->glomd = NULL;
+
+	/* Clear rx control and wake any waiters */
+	bus->rxlen = 0;
+	dhd_os_ioctl_resp_wake(bus->dhd);
+
+	/* Reset some F2 state stuff */
+	bus->rxskip = FALSE;
+	bus->tx_seq = bus->rx_seq = 0;
+
+	if (enforce_mutex)
+		dhd_os_sdunlock(bus->dhd);
+}
+
+int
+dhd_bus_init(dhd_pub_t *dhdp, bool enforce_mutex)
+{
+	dhd_bus_t *bus = dhdp->bus;
+	dhd_timeout_t tmo;
+	uint retries = 0;
+	uint8 ready, enable;
+	int err, ret = BCME_ERROR;
+	uint8 saveclk;
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	ASSERT(bus->dhd);
+	if (!bus->dhd)
+		return BCME_OK;
+
+	if (enforce_mutex)
+		dhd_os_sdlock(bus->dhd);
+
+	/* Make sure backplane clock is on, needed to generate F2 interrupt */
+	err = dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+	if ((err != BCME_OK) || (bus->clkstate != CLK_AVAIL)) {
+		DHD_ERROR(("%s: Failed to set backplane clock: err %d\n", __FUNCTION__, err));
+		goto exit;
+	}
+
+	/* Force clocks on backplane to be sure F2 interrupt propagates */
+	saveclk = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err);
+	if (!err) {
+		bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
+		                 (saveclk | SBSDIO_FORCE_HT), &err);
+	}
+	if (err) {
+		DHD_ERROR(("%s: Failed to force clock for F2: err %d\n", __FUNCTION__, err));
+		goto exit;
+	}
+
+	/* Enable function 2 (frame transfers) */
+	W_SDREG((SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT),
+	        &bus->regs->tosbmailboxdata, retries);
+	enable = (SDIO_FUNC_ENABLE_1 | SDIO_FUNC_ENABLE_2);
+
+	bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_IOEN, enable, NULL);
+
+	/* Give the dongle some time to do its thing and set IOR2 */
+	dhd_timeout_start(&tmo, DHD_WAIT_F2RDY * 1000);
+
+	ready = 0;
+	while (ready != enable && !dhd_timeout_expired(&tmo))
+	        ready = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_IORDY, NULL);
+
+
+	DHD_INFO(("%s: enable 0x%02x, ready 0x%02x (waited %uus)\n",
+	          __FUNCTION__, enable, ready, tmo.elapsed));
+
+
+	/* If F2 successfully enabled, set core and enable interrupts */
+	if (ready == enable) {
+		/* Make sure we're talking to the core. */
+		if (!(bus->regs = si_setcore(bus->sih, PCMCIA_CORE_ID, 0)))
+			bus->regs = si_setcore(bus->sih, SDIOD_CORE_ID, 0);
+
+		/* Set up the interrupt mask and enable interrupts */
+		bus->hostintmask = HOSTINTMASK;
+		W_SDREG(bus->hostintmask, &bus->regs->hostintmask, retries);
+
+		bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_WATERMARK, (uint8)watermark, &err);
+
+		/* Set bus state according to enable result */
+		dhdp->busstate = DHD_BUS_DATA;
+
+		/* bcmsdh_intr_unmask(bus->sdh); */
+
+		bus->intdis = FALSE;
+		if (bus->intr) {
+			DHD_INTR(("%s: enable SDIO device interrupts\n", __FUNCTION__));
+			bcmsdh_intr_enable(bus->sdh);
+		} else {
+			DHD_INTR(("%s: disable SDIO interrupts\n", __FUNCTION__));
+			bcmsdh_intr_disable(bus->sdh);
+		}
+
+	}
+
+
+	else {
+		/* Disable F2 again */
+		enable = SDIO_FUNC_ENABLE_1;
+		bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_IOEN, enable, NULL);
+	}
+
+	/* Restore previous clock setting */
+	bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, saveclk, &err);
+
+
+	/* If we didn't come up, turn off backplane clock */
+	if (dhdp->busstate != DHD_BUS_DATA)
+		dhdsdio_clkctl(bus, CLK_NONE, FALSE);
+
+	ret = BCME_OK;
+exit:
+	if (enforce_mutex)
+		dhd_os_sdunlock(bus->dhd);
+
+	return ret;
+}
+
+static void
+dhdsdio_rxfail(dhd_bus_t *bus, bool abort, bool rtx)
+{
+	bcmsdh_info_t *sdh = bus->sdh;
+	sdpcmd_regs_t *regs = bus->regs;
+	uint retries = 0;
+	uint16 lastrbc;
+	uint8 hi, lo;
+	int err;
+
+	DHD_ERROR(("%s: %sterminate frame%s\n", __FUNCTION__,
+	           (abort ? "abort command, " : ""), (rtx ? ", send NAK" : "")));
+
+	if (abort) {
+		bcmsdh_abort(sdh, SDIO_FUNC_2);
+	}
+
+	bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_FRAMECTRL, SFC_RF_TERM, &err);
+	bus->f1regdata++;
+
+	/* Wait until the packet has been flushed (device/FIFO stable) */
+	for (lastrbc = retries = 0xffff; retries > 0; retries--) {
+		hi = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_RFRAMEBCHI, NULL);
+		lo = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_RFRAMEBCLO, NULL);
+		bus->f1regdata += 2;
+
+		if ((hi == 0) && (lo == 0))
+			break;
+
+		if ((hi > (lastrbc >> 8)) && (lo > (lastrbc & 0x00ff))) {
+			DHD_ERROR(("%s: count growing: last 0x%04x now 0x%04x\n",
+			           __FUNCTION__, lastrbc, ((hi << 8) + lo)));
+		}
+		lastrbc = (hi << 8) + lo;
+	}
+
+	if (!retries) {
+		DHD_ERROR(("%s: count never zeroed: last 0x%04x\n", __FUNCTION__, lastrbc));
+	} else {
+		DHD_INFO(("%s: flush took %d iterations\n", __FUNCTION__, (0xffff - retries)));
+	}
+
+	if (rtx) {
+		bus->rxrtx++;
+		W_SDREG(SMB_NAK, &regs->tosbmailbox, retries);
+		bus->f1regdata++;
+		if (retries <= retry_limit) {
+			bus->rxskip = TRUE;
+		}
+	}
+
+	/* Clear partial in any case */
+	bus->nextlen = 0;
+
+	/* If we can't reach the device, signal failure */
+	if (err || bcmsdh_regfail(sdh))
+		bus->dhd->busstate = DHD_BUS_DOWN;
+}
+
+static void
+dhdsdio_read_control(dhd_bus_t *bus, uint8 *hdr, uint len, uint doff)
+{
+	bcmsdh_info_t *sdh = bus->sdh;
+	uint rdlen, pad;
+
+	int sdret;
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	/* Control data already received in aligned rxctl */
+	if ((bus->bus == SPI_BUS) && (!bus->usebufpool))
+		goto gotpkt;
+
+	ASSERT(bus->rxbuf);
+	/* Set rxctl for frame (w/optional alignment) */
+	bus->rxctl = bus->rxbuf;
+	if (dhd_alignctl) {
+		bus->rxctl += firstread;
+		if ((pad = ((uintptr)bus->rxctl % DHD_SDALIGN)))
+			bus->rxctl += (DHD_SDALIGN - pad);
+		bus->rxctl -= firstread;
+	}
+	ASSERT(bus->rxctl >= bus->rxbuf);
+
+	/* Copy the already-read portion over */
+	bcopy(hdr, bus->rxctl, firstread);
+	if (len <= firstread)
+		goto gotpkt;
+
+	/* Copy the full data pkt in gSPI case and process ioctl. */
+	if (bus->bus == SPI_BUS) {
+		bcopy(hdr, bus->rxctl, len);
+		goto gotpkt;
+	}
+
+	/* Raise rdlen to next SDIO block to avoid tail command */
+	rdlen = len - firstread;
+	if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) {
+		pad = bus->blocksize - (rdlen % bus->blocksize);
+		if ((pad <= bus->roundup) && (pad < bus->blocksize) &&
+		    ((len + pad) < bus->dhd->maxctl))
+			rdlen += pad;
+	} else if (rdlen % DHD_SDALIGN) {
+		rdlen += DHD_SDALIGN - (rdlen % DHD_SDALIGN);
+	}
+
+	/* Satisfy length-alignment requirements */
+	if (forcealign && (rdlen & (ALIGNMENT - 1)))
+		rdlen = ROUNDUP(rdlen, ALIGNMENT);
+
+	/* Drop if the read is too big or it exceeds our maximum */
+	if ((rdlen + firstread) > bus->dhd->maxctl) {
+		DHD_ERROR(("%s: %d-byte control read exceeds %d-byte buffer\n",
+		           __FUNCTION__, rdlen, bus->dhd->maxctl));
+		bus->dhd->rx_errors++;
+		dhdsdio_rxfail(bus, FALSE, FALSE);
+		goto done;
+	}
+
+	if ((len - doff) > bus->dhd->maxctl) {
+		DHD_ERROR(("%s: %d-byte ctl frame (%d-byte ctl data) exceeds %d-byte limit\n",
+		           __FUNCTION__, len, (len - doff), bus->dhd->maxctl));
+		bus->dhd->rx_errors++; bus->rx_toolong++;
+		dhdsdio_rxfail(bus, FALSE, FALSE);
+		goto done;
+	}
+
+
+	/* Read remainder of frame body into the rxctl buffer */
+	sdret = dhd_bcmsdh_recv_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC,
+	                            (bus->rxctl + firstread), rdlen, NULL, NULL, NULL);
+	bus->f2rxdata++;
+	ASSERT(sdret != BCME_PENDING);
+
+	/* Control frame failures need retransmission */
+	if (sdret < 0) {
+		DHD_ERROR(("%s: read %d control bytes failed: %d\n", __FUNCTION__, rdlen, sdret));
+		bus->rxc_errors++; /* dhd.rx_ctlerrs is higher level */
+		dhdsdio_rxfail(bus, TRUE, TRUE);
+		goto done;
+	}
+
+gotpkt:
+
+#ifdef DHD_DEBUG
+	if (DHD_BYTES_ON() && DHD_CTL_ON()) {
+		prhex("RxCtrl", bus->rxctl, len);
+	}
+#endif
+
+	/* Point to valid data and indicate its length */
+	bus->rxctl += doff;
+	bus->rxlen = len - doff;
+
+done:
+	/* Awake any waiters */
+	dhd_os_ioctl_resp_wake(bus->dhd);
+}
+
+static uint8
+dhdsdio_rxglom(dhd_bus_t *bus, uint8 rxseq)
+{
+	uint16 dlen, totlen;
+	uint8 *dptr, num = 0;
+
+	uint16 sublen, check;
+	void *pfirst, *plast, *pnext, *save_pfirst;
+	osl_t *osh = bus->dhd->osh;
+
+	int errcode;
+	uint8 chan, seq, doff, sfdoff;
+	uint8 txmax;
+
+	int ifidx = 0;
+	bool usechain = bus->use_rxchain;
+
+	/* If packets, issue read(s) and send up packet chain */
+	/* Return sequence numbers consumed? */
+
+	DHD_TRACE(("dhdsdio_rxglom: start: glomd %p glom %p\n", bus->glomd, bus->glom));
+
+	/* If there's a descriptor, generate the packet chain */
+	if (bus->glomd) {
+		dhd_os_sdlock_rxq(bus->dhd);
+
+		pfirst = plast = pnext = NULL;
+		dlen = (uint16)PKTLEN(osh, bus->glomd);
+		dptr = PKTDATA(osh, bus->glomd);
+		if (!dlen || (dlen & 1)) {
+			DHD_ERROR(("%s: bad glomd len (%d), ignore descriptor\n",
+			           __FUNCTION__, dlen));
+			dlen = 0;
+		}
+
+		for (totlen = num = 0; dlen; num++) {
+			/* Get (and move past) next length */
+			sublen = ltoh16_ua(dptr);
+			dlen -= sizeof(uint16);
+			dptr += sizeof(uint16);
+			if ((sublen < SDPCM_HDRLEN) ||
+			    ((num == 0) && (sublen < (2 * SDPCM_HDRLEN)))) {
+				DHD_ERROR(("%s: descriptor len %d bad: %d\n",
+				           __FUNCTION__, num, sublen));
+				pnext = NULL;
+				break;
+			}
+			if (sublen % DHD_SDALIGN) {
+				DHD_ERROR(("%s: sublen %d not a multiple of %d\n",
+				           __FUNCTION__, sublen, DHD_SDALIGN));
+				usechain = FALSE;
+			}
+			totlen += sublen;
+
+			/* For last frame, adjust read len so total is a block multiple */
+			if (!dlen) {
+				sublen += (ROUNDUP(totlen, bus->blocksize) - totlen);
+				totlen = ROUNDUP(totlen, bus->blocksize);
+			}
+
+			/* Allocate/chain packet for next subframe */
+			if ((pnext = PKTGET(osh, sublen + DHD_SDALIGN, FALSE)) == NULL) {
+				DHD_ERROR(("%s: PKTGET failed, num %d len %d\n",
+				           __FUNCTION__, num, sublen));
+				break;
+			}
+			ASSERT(!PKTLINK(pnext));
+			if (!pfirst) {
+				ASSERT(!plast);
+				pfirst = plast = pnext;
+			} else {
+				ASSERT(plast);
+				PKTSETNEXT(osh, plast, pnext);
+				plast = pnext;
+			}
+
+			/* Adhere to start alignment requirements */
+			PKTALIGN(osh, pnext, sublen, DHD_SDALIGN);
+		}
+
+		/* If all allocations succeeded, save packet chain in bus structure */
+		if (pnext) {
+			DHD_GLOM(("%s: allocated %d-byte packet chain for %d subframes\n",
+			          __FUNCTION__, totlen, num));
+			if (DHD_GLOM_ON() && bus->nextlen) {
+				if (totlen != bus->nextlen) {
+					DHD_GLOM(("%s: glomdesc mismatch: nextlen %d glomdesc %d "
+					          "rxseq %d\n", __FUNCTION__, bus->nextlen,
+					          totlen, rxseq));
+				}
+			}
+			bus->glom = pfirst;
+			pfirst = pnext = NULL;
+		} else {
+			if (pfirst)
+				PKTFREE(osh, pfirst, FALSE);
+			bus->glom = NULL;
+			num = 0;
+		}
+
+		/* Done with descriptor packet */
+		PKTFREE(osh, bus->glomd, FALSE);
+		bus->glomd = NULL;
+		bus->nextlen = 0;
+
+		dhd_os_sdunlock_rxq(bus->dhd);
+	}
+
+	/* Ok -- either we just generated a packet chain, or had one from before */
+	if (bus->glom) {
+		if (DHD_GLOM_ON()) {
+			DHD_GLOM(("%s: attempt superframe read, packet chain:\n", __FUNCTION__));
+			for (pnext = bus->glom; pnext; pnext = PKTNEXT(osh, pnext)) {
+				DHD_GLOM(("    %p: %p len 0x%04x (%d)\n",
+				          pnext, (uint8*)PKTDATA(osh, pnext),
+				          PKTLEN(osh, pnext), PKTLEN(osh, pnext)));
+			}
+		}
+
+		pfirst = bus->glom;
+		dlen = (uint16)pkttotlen(osh, pfirst);
+
+		/* Do an SDIO read for the superframe.  Configurable iovar to
+		 * read directly into the chained packet, or allocate a large
+		 * packet and and copy into the chain.
+		 */
+		if (usechain) {
+			errcode = dhd_bcmsdh_recv_buf(bus,
+			                              bcmsdh_cur_sbwad(bus->sdh), SDIO_FUNC_2,
+			                              F2SYNC, (uint8*)PKTDATA(osh, pfirst),
+			                              dlen, pfirst, NULL, NULL);
+		} else if (bus->dataptr) {
+			errcode = dhd_bcmsdh_recv_buf(bus,
+			                              bcmsdh_cur_sbwad(bus->sdh), SDIO_FUNC_2,
+			                              F2SYNC, bus->dataptr,
+			                              dlen, NULL, NULL, NULL);
+			sublen = (uint16)pktfrombuf(osh, pfirst, 0, dlen, bus->dataptr);
+			if (sublen != dlen) {
+				DHD_ERROR(("%s: FAILED TO COPY, dlen %d sublen %d\n",
+				           __FUNCTION__, dlen, sublen));
+				errcode = -1;
+			}
+			pnext = NULL;
+		} else {
+			DHD_ERROR(("COULDN'T ALLOC %d-BYTE GLOM, FORCE FAILURE\n", dlen));
+			errcode = -1;
+		}
+		bus->f2rxdata++;
+		ASSERT(errcode != BCME_PENDING);
+
+		/* On failure, kill the superframe, allow a couple retries */
+		if (errcode < 0) {
+			DHD_ERROR(("%s: glom read of %d bytes failed: %d\n",
+			           __FUNCTION__, dlen, errcode));
+			bus->dhd->rx_errors++;
+
+			if (bus->glomerr++ < 3) {
+				dhdsdio_rxfail(bus, TRUE, TRUE);
+			} else {
+				bus->glomerr = 0;
+				dhdsdio_rxfail(bus, TRUE, FALSE);
+				dhd_os_sdlock_rxq(bus->dhd);
+				PKTFREE(osh, bus->glom, FALSE);
+				dhd_os_sdunlock_rxq(bus->dhd);
+				bus->rxglomfail++;
+				bus->glom = NULL;
+			}
+			return 0;
+		}
+
+#ifdef DHD_DEBUG
+		if (DHD_GLOM_ON()) {
+			prhex("SUPERFRAME", PKTDATA(osh, pfirst),
+			      MIN(PKTLEN(osh, pfirst), 48));
+		}
+#endif
+
+
+		/* Validate the superframe header */
+		dptr = (uint8 *)PKTDATA(osh, pfirst);
+		sublen = ltoh16_ua(dptr);
+		check = ltoh16_ua(dptr + sizeof(uint16));
+
+		chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]);
+		seq = SDPCM_PACKET_SEQUENCE(&dptr[SDPCM_FRAMETAG_LEN]);
+		bus->nextlen = dptr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET];
+		if ((bus->nextlen << 4) > MAX_RX_DATASZ) {
+			DHD_INFO(("%s: got frame w/nextlen too large (%d) seq %d\n",
+			          __FUNCTION__, bus->nextlen, seq));
+			bus->nextlen = 0;
+		}
+		doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
+		txmax = SDPCM_WINDOW_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
+
+		errcode = 0;
+		if ((uint16)~(sublen^check)) {
+			DHD_ERROR(("%s (superframe): HW hdr error: len/check 0x%04x/0x%04x\n",
+			           __FUNCTION__, sublen, check));
+			errcode = -1;
+		} else if (ROUNDUP(sublen, bus->blocksize) != dlen) {
+			DHD_ERROR(("%s (superframe): len 0x%04x, rounded 0x%04x, expect 0x%04x\n",
+			           __FUNCTION__, sublen, ROUNDUP(sublen, bus->blocksize), dlen));
+			errcode = -1;
+		} else if (SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]) != SDPCM_GLOM_CHANNEL) {
+			DHD_ERROR(("%s (superframe): bad channel %d\n", __FUNCTION__,
+			           SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN])));
+			errcode = -1;
+		} else if (SDPCM_GLOMDESC(&dptr[SDPCM_FRAMETAG_LEN])) {
+			DHD_ERROR(("%s (superframe): got second descriptor?\n", __FUNCTION__));
+			errcode = -1;
+		} else if ((doff < SDPCM_HDRLEN) ||
+		           (doff > (PKTLEN(osh, pfirst) - SDPCM_HDRLEN))) {
+			DHD_ERROR(("%s (superframe): Bad data offset %d: HW %d pkt %d min %d\n",
+			           __FUNCTION__, doff, sublen, PKTLEN(osh, pfirst), SDPCM_HDRLEN));
+			errcode = -1;
+		}
+
+		/* Check sequence number of superframe SW header */
+		if (rxseq != seq) {
+			DHD_INFO(("%s: (superframe) rx_seq %d, expected %d\n",
+			          __FUNCTION__, seq, rxseq));
+			bus->rx_badseq++;
+			rxseq = seq;
+		}
+
+		/* Check window for sanity */
+		if ((uint8)(txmax - bus->tx_seq) > 0x40) {
+			DHD_ERROR(("%s: got unlikely tx max %d with tx_seq %d\n",
+			           __FUNCTION__, txmax, bus->tx_seq));
+			txmax = bus->tx_seq + 2;
+		}
+		bus->tx_max = txmax;
+
+		/* Remove superframe header, remember offset */
+		PKTPULL(osh, pfirst, doff);
+		sfdoff = doff;
+
+		/* Validate all the subframe headers */
+		for (num = 0, pnext = pfirst; pnext && !errcode;
+		     num++, pnext = PKTNEXT(osh, pnext)) {
+			dptr = (uint8 *)PKTDATA(osh, pnext);
+			dlen = (uint16)PKTLEN(osh, pnext);
+			sublen = ltoh16_ua(dptr);
+			check = ltoh16_ua(dptr + sizeof(uint16));
+			chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]);
+			doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
+#ifdef DHD_DEBUG
+			if (DHD_GLOM_ON()) {
+				prhex("subframe", dptr, 32);
+			}
+#endif
+
+			if ((uint16)~(sublen^check)) {
+				DHD_ERROR(("%s (subframe %d): HW hdr error: "
+				           "len/check 0x%04x/0x%04x\n",
+				           __FUNCTION__, num, sublen, check));
+				errcode = -1;
+			} else if ((sublen > dlen) || (sublen < SDPCM_HDRLEN)) {
+				DHD_ERROR(("%s (subframe %d): length mismatch: "
+				           "len 0x%04x, expect 0x%04x\n",
+				           __FUNCTION__, num, sublen, dlen));
+				errcode = -1;
+			} else if ((chan != SDPCM_DATA_CHANNEL) &&
+			           (chan != SDPCM_EVENT_CHANNEL)) {
+				DHD_ERROR(("%s (subframe %d): bad channel %d\n",
+				           __FUNCTION__, num, chan));
+				errcode = -1;
+			} else if ((doff < SDPCM_HDRLEN) || (doff > sublen)) {
+				DHD_ERROR(("%s (subframe %d): Bad data offset %d: HW %d min %d\n",
+				           __FUNCTION__, num, doff, sublen, SDPCM_HDRLEN));
+				errcode = -1;
+			}
+		}
+
+		if (errcode) {
+			/* Terminate frame on error, request a couple retries */
+			if (bus->glomerr++ < 3) {
+				/* Restore superframe header space */
+				PKTPUSH(osh, pfirst, sfdoff);
+				dhdsdio_rxfail(bus, TRUE, TRUE);
+			} else {
+				bus->glomerr = 0;
+				dhdsdio_rxfail(bus, TRUE, FALSE);
+				dhd_os_sdlock_rxq(bus->dhd);
+				PKTFREE(osh, bus->glom, FALSE);
+				dhd_os_sdunlock_rxq(bus->dhd);
+				bus->rxglomfail++;
+				bus->glom = NULL;
+			}
+			bus->nextlen = 0;
+			return 0;
+		}
+
+		/* Basic SD framing looks ok - process each packet (header) */
+		save_pfirst = pfirst;
+		bus->glom = NULL;
+		plast = NULL;
+
+		dhd_os_sdlock_rxq(bus->dhd);
+		for (num = 0; pfirst; rxseq++, pfirst = pnext) {
+			pnext = PKTNEXT(osh, pfirst);
+			PKTSETNEXT(osh, pfirst, NULL);
+
+			dptr = (uint8 *)PKTDATA(osh, pfirst);
+			sublen = ltoh16_ua(dptr);
+			chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]);
+			seq = SDPCM_PACKET_SEQUENCE(&dptr[SDPCM_FRAMETAG_LEN]);
+			doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
+
+			DHD_GLOM(("%s: Get subframe %d, %p(%p/%d), sublen %d chan %d seq %d\n",
+			          __FUNCTION__, num, pfirst, PKTDATA(osh, pfirst),
+			          PKTLEN(osh, pfirst), sublen, chan, seq));
+
+			ASSERT((chan == SDPCM_DATA_CHANNEL) || (chan == SDPCM_EVENT_CHANNEL));
+
+			if (rxseq != seq) {
+				DHD_GLOM(("%s: rx_seq %d, expected %d\n",
+				          __FUNCTION__, seq, rxseq));
+				bus->rx_badseq++;
+				rxseq = seq;
+			}
+
+#ifdef DHD_DEBUG
+			if (DHD_BYTES_ON() && DHD_DATA_ON()) {
+				prhex("Rx Subframe Data", dptr, dlen);
+			}
+#endif
+
+			PKTSETLEN(osh, pfirst, sublen);
+			PKTPULL(osh, pfirst, doff);
+
+			if (PKTLEN(osh, pfirst) == 0) {
+				PKTFREE(bus->dhd->osh, pfirst, FALSE);
+				if (plast) {
+					PKTSETNEXT(osh, plast, pnext);
+				} else {
+					ASSERT(save_pfirst == pfirst);
+					save_pfirst = pnext;
+				}
+				continue;
+			} else if (dhd_prot_hdrpull(bus->dhd, &ifidx, pfirst) != 0) {
+				DHD_ERROR(("%s: rx protocol error\n", __FUNCTION__));
+				bus->dhd->rx_errors++;
+				PKTFREE(osh, pfirst, FALSE);
+				if (plast) {
+					PKTSETNEXT(osh, plast, pnext);
+				} else {
+					ASSERT(save_pfirst == pfirst);
+					save_pfirst = pnext;
+				}
+				continue;
+			}
+
+			/* this packet will go up, link back into chain and count it */
+			PKTSETNEXT(osh, pfirst, pnext);
+			plast = pfirst;
+			num++;
+
+#ifdef DHD_DEBUG
+			if (DHD_GLOM_ON()) {
+				DHD_GLOM(("%s subframe %d to stack, %p(%p/%d) nxt/lnk %p/%p\n",
+				          __FUNCTION__, num, pfirst,
+				          PKTDATA(osh, pfirst), PKTLEN(osh, pfirst),
+				          PKTNEXT(osh, pfirst), PKTLINK(pfirst)));
+				prhex("", (uint8 *)PKTDATA(osh, pfirst),
+				      MIN(PKTLEN(osh, pfirst), 32));
+			}
+#endif /* DHD_DEBUG */
+		}
+		dhd_os_sdunlock_rxq(bus->dhd);
+		if (num) {
+			dhd_os_sdunlock(bus->dhd);
+			dhd_rx_frame(bus->dhd, ifidx, save_pfirst, num);
+			dhd_os_sdlock(bus->dhd);
+		}
+
+		bus->rxglomframes++;
+		bus->rxglompkts += num;
+	}
+	return num;
+}
+
+/* Return TRUE if there may be more frames to read */
+static uint
+dhdsdio_readframes(dhd_bus_t *bus, uint maxframes, bool *finished)
+{
+	osl_t *osh = bus->dhd->osh;
+	bcmsdh_info_t *sdh = bus->sdh;
+
+	uint16 len, check;	/* Extracted hardware header fields */
+	uint8 chan, seq, doff;	/* Extracted software header fields */
+	uint8 fcbits;		/* Extracted fcbits from software header */
+	uint8 delta;
+
+	void *pkt;	/* Packet for event or data frames */
+	uint16 pad;	/* Number of pad bytes to read */
+	uint16 rdlen;	/* Total number of bytes to read */
+	uint8 rxseq;	/* Next sequence number to expect */
+	uint rxleft = 0;	/* Remaining number of frames allowed */
+	int sdret;	/* Return code from bcmsdh calls */
+	uint8 txmax;	/* Maximum tx sequence offered */
+	bool len_consistent; /* Result of comparing readahead len and len from hw-hdr */
+	uint8 *rxbuf;
+	int ifidx = 0;
+	uint rxcount = 0; /* Total frames read */
+
+#if defined(DHD_DEBUG) || defined(SDTEST)
+	bool sdtest = FALSE;	/* To limit message spew from test mode */
+#endif
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	ASSERT(maxframes);
+
+#ifdef SDTEST
+	/* Allow pktgen to override maxframes */
+	if (bus->pktgen_count && (bus->pktgen_mode == DHD_PKTGEN_RECV)) {
+		maxframes = bus->pktgen_count;
+		sdtest = TRUE;
+	}
+#endif
+
+	/* Not finished unless we encounter no more frames indication */
+	*finished = FALSE;
+
+
+	for (rxseq = bus->rx_seq, rxleft = maxframes;
+	     !bus->rxskip && rxleft && bus->dhd->busstate != DHD_BUS_DOWN;
+	     rxseq++, rxleft--) {
+
+		/* Handle glomming separately */
+		if (bus->glom || bus->glomd) {
+			uint8 cnt;
+			DHD_GLOM(("%s: calling rxglom: glomd %p, glom %p\n",
+			          __FUNCTION__, bus->glomd, bus->glom));
+			cnt = dhdsdio_rxglom(bus, rxseq);
+			DHD_GLOM(("%s: rxglom returned %d\n", __FUNCTION__, cnt));
+			rxseq += cnt - 1;
+			rxleft = (rxleft > cnt) ? (rxleft - cnt) : 1;
+			continue;
+		}
+
+		/* Try doing single read if we can */
+		if (dhd_readahead && bus->nextlen) {
+			uint16 nextlen = bus->nextlen;
+			bus->nextlen = 0;
+
+			if (bus->bus == SPI_BUS) {
+				rdlen = len = nextlen;
+			}
+			else {
+				rdlen = len = nextlen << 4;
+
+				/* Pad read to blocksize for efficiency */
+				if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) {
+					pad = bus->blocksize - (rdlen % bus->blocksize);
+					if ((pad <= bus->roundup) && (pad < bus->blocksize) &&
+						((rdlen + pad + firstread) < MAX_RX_DATASZ))
+						rdlen += pad;
+				} else if (rdlen % DHD_SDALIGN) {
+					rdlen += DHD_SDALIGN - (rdlen % DHD_SDALIGN);
+				}
+			}
+
+			/* We use bus->rxctl buffer in WinXP for initial control pkt receives.
+			 * Later we use buffer-poll for data as well as control packets.
+			 * This is required becuase dhd receives full frame in gSPI unlike SDIO.
+			 * After the frame is received we have to distinguish whether it is data
+			 * or non-data frame.
+			 */
+			/* Allocate a packet buffer */
+			dhd_os_sdlock_rxq(bus->dhd);
+			if (!(pkt = PKTGET(osh, rdlen + DHD_SDALIGN, FALSE))) {
+				if (bus->bus == SPI_BUS) {
+					bus->usebufpool = FALSE;
+					bus->rxctl = bus->rxbuf;
+					if (dhd_alignctl) {
+						bus->rxctl += firstread;
+						if ((pad = ((uintptr)bus->rxctl % DHD_SDALIGN)))
+							bus->rxctl += (DHD_SDALIGN - pad);
+						bus->rxctl -= firstread;
+					}
+					ASSERT(bus->rxctl >= bus->rxbuf);
+					rxbuf = bus->rxctl;
+					/* Read the entire frame */
+					sdret = dhd_bcmsdh_recv_buf(bus,
+					                            bcmsdh_cur_sbwad(sdh),
+					                            SDIO_FUNC_2,
+					                            F2SYNC, rxbuf, rdlen,
+					                            NULL, NULL, NULL);
+					bus->f2rxdata++;
+					ASSERT(sdret != BCME_PENDING);
+
+
+					/* Control frame failures need retransmission */
+					if (sdret < 0) {
+						DHD_ERROR(("%s: read %d control bytes failed: %d\n",
+						   __FUNCTION__, rdlen, sdret));
+						/* dhd.rx_ctlerrs is higher level */
+						bus->rxc_errors++;
+						dhd_os_sdunlock_rxq(bus->dhd);
+						dhdsdio_rxfail(bus, TRUE,
+						    (bus->bus == SPI_BUS) ? FALSE : TRUE);
+						continue;
+					}
+				} else {
+					/* Give up on data, request rtx of events */
+					DHD_ERROR(("%s (nextlen): PKTGET failed: len %d rdlen %d "
+					           "expected rxseq %d\n",
+					           __FUNCTION__, len, rdlen, rxseq));
+					/* Just go try again w/normal header read */
+					dhd_os_sdunlock_rxq(bus->dhd);
+					continue;
+				}
+			} else {
+				if (bus->bus == SPI_BUS)
+					bus->usebufpool = TRUE;
+
+				ASSERT(!PKTLINK(pkt));
+				PKTALIGN(osh, pkt, rdlen, DHD_SDALIGN);
+				rxbuf = (uint8 *)PKTDATA(osh, pkt);
+				/* Read the entire frame */
+				sdret = dhd_bcmsdh_recv_buf(bus, bcmsdh_cur_sbwad(sdh),
+				                            SDIO_FUNC_2,
+				                            F2SYNC, rxbuf, rdlen,
+				                            pkt, NULL, NULL);
+				bus->f2rxdata++;
+				ASSERT(sdret != BCME_PENDING);
+
+				if (sdret < 0) {
+					DHD_ERROR(("%s (nextlen): read %d bytes failed: %d\n",
+					   __FUNCTION__, rdlen, sdret));
+					PKTFREE(bus->dhd->osh, pkt, FALSE);
+					bus->dhd->rx_errors++;
+					dhd_os_sdunlock_rxq(bus->dhd);
+					/* Force retry w/normal header read.  Don't attemp NAK for
+					 * gSPI
+					 */
+					dhdsdio_rxfail(bus, TRUE,
+					      (bus->bus == SPI_BUS) ? FALSE : TRUE);
+					continue;
+				}
+			}
+			dhd_os_sdunlock_rxq(bus->dhd);
+
+			/* Now check the header */
+			bcopy(rxbuf, bus->rxhdr, SDPCM_HDRLEN);
+
+			/* Extract hardware header fields */
+			len = ltoh16_ua(bus->rxhdr);
+			check = ltoh16_ua(bus->rxhdr + sizeof(uint16));
+
+			/* All zeros means readahead info was bad */
+			if (!(len|check)) {
+				DHD_INFO(("%s (nextlen): read zeros in HW header???\n",
+				           __FUNCTION__));
+				dhd_os_sdlock_rxq(bus->dhd);
+				PKTFREE2();
+				dhd_os_sdunlock_rxq(bus->dhd);
+				GSPI_PR55150_BAILOUT;
+				continue;
+			}
+
+			/* Validate check bytes */
+			if ((uint16)~(len^check)) {
+				DHD_ERROR(("%s (nextlen): HW hdr error: nextlen/len/check"
+				           " 0x%04x/0x%04x/0x%04x\n", __FUNCTION__, nextlen,
+				           len, check));
+				dhd_os_sdlock_rxq(bus->dhd);
+				PKTFREE2();
+				dhd_os_sdunlock_rxq(bus->dhd);
+				bus->rx_badhdr++;
+				dhdsdio_rxfail(bus, FALSE, FALSE);
+				GSPI_PR55150_BAILOUT;
+				continue;
+			}
+
+			/* Validate frame length */
+			if (len < SDPCM_HDRLEN) {
+				DHD_ERROR(("%s (nextlen): HW hdr length invalid: %d\n",
+				           __FUNCTION__, len));
+				dhd_os_sdlock_rxq(bus->dhd);
+				PKTFREE2();
+				dhd_os_sdunlock_rxq(bus->dhd);
+				GSPI_PR55150_BAILOUT;
+				continue;
+			}
+
+			/* Check for consistency with readahead info */
+				len_consistent = (nextlen != (ROUNDUP(len, 16) >> 4));
+			if (len_consistent) {
+				/* Mismatch, force retry w/normal header (may be >4K) */
+				DHD_ERROR(("%s (nextlen): mismatch, nextlen %d len %d rnd %d; "
+				           "expected rxseq %d\n",
+				           __FUNCTION__, nextlen, len, ROUNDUP(len, 16), rxseq));
+				dhd_os_sdlock_rxq(bus->dhd);
+				PKTFREE2();
+				dhd_os_sdunlock_rxq(bus->dhd);
+				dhdsdio_rxfail(bus, TRUE, (bus->bus == SPI_BUS) ? FALSE : TRUE);
+				GSPI_PR55150_BAILOUT;
+				continue;
+			}
+
+
+			/* Extract software header fields */
+			chan = SDPCM_PACKET_CHANNEL(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+			seq = SDPCM_PACKET_SEQUENCE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+			doff = SDPCM_DOFFSET_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+			txmax = SDPCM_WINDOW_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+
+				bus->nextlen =
+				         bus->rxhdr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET];
+				if ((bus->nextlen << 4) > MAX_RX_DATASZ) {
+					DHD_INFO(("%s (nextlen): got frame w/nextlen too large"
+					          " (%d), seq %d\n", __FUNCTION__, bus->nextlen,
+					          seq));
+					bus->nextlen = 0;
+				}
+
+				bus->dhd->rx_readahead_cnt ++;
+			/* Handle Flow Control */
+			fcbits = SDPCM_FCMASK_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+
+			delta = 0;
+			if (~bus->flowcontrol & fcbits) {
+				bus->fc_xoff++;
+				delta = 1;
+			}
+			if (bus->flowcontrol & ~fcbits) {
+				bus->fc_xon++;
+				delta = 1;
+			}
+
+			if (delta) {
+				bus->fc_rcvd++;
+				bus->flowcontrol = fcbits;
+			}
+
+			/* Check and update sequence number */
+			if (rxseq != seq) {
+				DHD_INFO(("%s (nextlen): rx_seq %d, expected %d\n",
+				          __FUNCTION__, seq, rxseq));
+				bus->rx_badseq++;
+				rxseq = seq;
+			}
+
+			/* Check window for sanity */
+			if ((uint8)(txmax - bus->tx_seq) > 0x40) {
+					DHD_ERROR(("%s: got unlikely tx max %d with tx_seq %d\n",
+						__FUNCTION__, txmax, bus->tx_seq));
+					txmax = bus->tx_seq + 2;
+			}
+			bus->tx_max = txmax;
+
+#ifdef DHD_DEBUG
+			if (DHD_BYTES_ON() && DHD_DATA_ON()) {
+				prhex("Rx Data", rxbuf, len);
+			} else if (DHD_HDRS_ON()) {
+				prhex("RxHdr", bus->rxhdr, SDPCM_HDRLEN);
+			}
+#endif
+
+			if (chan == SDPCM_CONTROL_CHANNEL) {
+				if (bus->bus == SPI_BUS) {
+					dhdsdio_read_control(bus, rxbuf, len, doff);
+					if (bus->usebufpool) {
+						dhd_os_sdlock_rxq(bus->dhd);
+						PKTFREE(bus->dhd->osh, pkt, FALSE);
+						dhd_os_sdunlock_rxq(bus->dhd);
+					}
+					continue;
+				} else {
+					DHD_ERROR(("%s (nextlen): readahead on control"
+					           " packet %d?\n", __FUNCTION__, seq));
+					/* Force retry w/normal header read */
+					bus->nextlen = 0;
+					dhdsdio_rxfail(bus, FALSE, TRUE);
+					dhd_os_sdlock_rxq(bus->dhd);
+					PKTFREE2();
+					dhd_os_sdunlock_rxq(bus->dhd);
+					continue;
+				}
+			}
+
+			if ((bus->bus == SPI_BUS) && !bus->usebufpool) {
+				DHD_ERROR(("Received %d bytes on %d channel. Running out of "
+				           "rx pktbuf's or not yet malloced.\n", len, chan));
+				continue;
+			}
+
+			/* Validate data offset */
+			if ((doff < SDPCM_HDRLEN) || (doff > len)) {
+				DHD_ERROR(("%s (nextlen): bad data offset %d: HW len %d min %d\n",
+				           __FUNCTION__, doff, len, SDPCM_HDRLEN));
+				dhd_os_sdlock_rxq(bus->dhd);
+				PKTFREE2();
+				dhd_os_sdunlock_rxq(bus->dhd);
+				ASSERT(0);
+				dhdsdio_rxfail(bus, FALSE, FALSE);
+				continue;
+			}
+
+			/* All done with this one -- now deliver the packet */
+			goto deliver;
+		}
+		/* gSPI frames should not be handled in fractions */
+		if (bus->bus == SPI_BUS) {
+			break;
+		}
+
+		/* Read frame header (hardware and software) */
+		sdret = dhd_bcmsdh_recv_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC,
+		                            bus->rxhdr, firstread, NULL, NULL, NULL);
+		bus->f2rxhdrs++;
+		ASSERT(sdret != BCME_PENDING);
+
+		if (sdret < 0) {
+			DHD_ERROR(("%s: RXHEADER FAILED: %d\n", __FUNCTION__, sdret));
+			bus->rx_hdrfail++;
+			dhdsdio_rxfail(bus, TRUE, TRUE);
+			continue;
+		}
+
+#ifdef DHD_DEBUG
+		if (DHD_BYTES_ON() || DHD_HDRS_ON()) {
+			prhex("RxHdr", bus->rxhdr, SDPCM_HDRLEN);
+		}
+#endif
+
+		/* Extract hardware header fields */
+		len = ltoh16_ua(bus->rxhdr);
+		check = ltoh16_ua(bus->rxhdr + sizeof(uint16));
+
+		/* All zeros means no more frames */
+		if (!(len|check)) {
+			*finished = TRUE;
+			break;
+		}
+
+		/* Validate check bytes */
+		if ((uint16)~(len^check)) {
+			DHD_ERROR(("%s: HW hdr error: len/check 0x%04x/0x%04x\n",
+			           __FUNCTION__, len, check));
+			bus->rx_badhdr++;
+			dhdsdio_rxfail(bus, FALSE, FALSE);
+			continue;
+		}
+
+		/* Validate frame length */
+		if (len < SDPCM_HDRLEN) {
+			DHD_ERROR(("%s: HW hdr length invalid: %d\n", __FUNCTION__, len));
+			continue;
+		}
+
+		/* Extract software header fields */
+		chan = SDPCM_PACKET_CHANNEL(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+		seq = SDPCM_PACKET_SEQUENCE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+		doff = SDPCM_DOFFSET_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+		txmax = SDPCM_WINDOW_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+
+		/* Validate data offset */
+		if ((doff < SDPCM_HDRLEN) || (doff > len)) {
+			DHD_ERROR(("%s: Bad data offset %d: HW len %d, min %d seq %d\n",
+			           __FUNCTION__, doff, len, SDPCM_HDRLEN, seq));
+			bus->rx_badhdr++;
+			ASSERT(0);
+			dhdsdio_rxfail(bus, FALSE, FALSE);
+			continue;
+		}
+
+		/* Save the readahead length if there is one */
+		bus->nextlen = bus->rxhdr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET];
+		if ((bus->nextlen << 4) > MAX_RX_DATASZ) {
+			DHD_INFO(("%s (nextlen): got frame w/nextlen too large (%d), seq %d\n",
+			          __FUNCTION__, bus->nextlen, seq));
+			bus->nextlen = 0;
+		}
+
+		/* Handle Flow Control */
+		fcbits = SDPCM_FCMASK_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+
+		delta = 0;
+		if (~bus->flowcontrol & fcbits) {
+			bus->fc_xoff++;
+			delta = 1;
+		}
+		if (bus->flowcontrol & ~fcbits) {
+			bus->fc_xon++;
+			delta = 1;
+		}
+
+		if (delta) {
+			bus->fc_rcvd++;
+			bus->flowcontrol = fcbits;
+		}
+
+		/* Check and update sequence number */
+		if (rxseq != seq) {
+			DHD_INFO(("%s: rx_seq %d, expected %d\n", __FUNCTION__, seq, rxseq));
+			bus->rx_badseq++;
+			rxseq = seq;
+		}
+
+		/* Check window for sanity */
+		if ((uint8)(txmax - bus->tx_seq) > 0x40) {
+			DHD_ERROR(("%s: got unlikely tx max %d with tx_seq %d\n",
+			           __FUNCTION__, txmax, bus->tx_seq));
+			txmax = bus->tx_seq + 2;
+		}
+		bus->tx_max = txmax;
+
+		/* Call a separate function for control frames */
+		if (chan == SDPCM_CONTROL_CHANNEL) {
+			dhdsdio_read_control(bus, bus->rxhdr, len, doff);
+			continue;
+		}
+
+		ASSERT((chan == SDPCM_DATA_CHANNEL) || (chan == SDPCM_EVENT_CHANNEL) ||
+		       (chan == SDPCM_TEST_CHANNEL) || (chan == SDPCM_GLOM_CHANNEL));
+
+		/* Length to read */
+		rdlen = (len > firstread) ? (len - firstread) : 0;
+
+		/* May pad read to blocksize for efficiency */
+		if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) {
+			pad = bus->blocksize - (rdlen % bus->blocksize);
+			if ((pad <= bus->roundup) && (pad < bus->blocksize) &&
+			    ((rdlen + pad + firstread) < MAX_RX_DATASZ))
+				rdlen += pad;
+		} else if (rdlen % DHD_SDALIGN) {
+			rdlen += DHD_SDALIGN - (rdlen % DHD_SDALIGN);
+		}
+
+		/* Satisfy length-alignment requirements */
+		if (forcealign && (rdlen & (ALIGNMENT - 1)))
+			rdlen = ROUNDUP(rdlen, ALIGNMENT);
+
+		if ((rdlen + firstread) > MAX_RX_DATASZ) {
+			/* Too long -- skip this frame */
+			DHD_ERROR(("%s: too long: len %d rdlen %d\n", __FUNCTION__, len, rdlen));
+			bus->dhd->rx_errors++; bus->rx_toolong++;
+			dhdsdio_rxfail(bus, FALSE, FALSE);
+			continue;
+		}
+
+		dhd_os_sdlock_rxq(bus->dhd);
+		if (!(pkt = PKTGET(osh, (rdlen + firstread + DHD_SDALIGN), FALSE))) {
+			/* Give up on data, request rtx of events */
+			DHD_ERROR(("%s: PKTGET failed: rdlen %d chan %d\n",
+			           __FUNCTION__, rdlen, chan));
+			bus->dhd->rx_dropped++;
+			dhd_os_sdunlock_rxq(bus->dhd);
+			dhdsdio_rxfail(bus, FALSE, RETRYCHAN(chan));
+			continue;
+		}
+		dhd_os_sdunlock_rxq(bus->dhd);
+
+		ASSERT(!PKTLINK(pkt));
+
+		/* Leave room for what we already read, and align remainder */
+		ASSERT(firstread < (PKTLEN(osh, pkt)));
+		PKTPULL(osh, pkt, firstread);
+		PKTALIGN(osh, pkt, rdlen, DHD_SDALIGN);
+
+		/* Read the remaining frame data */
+		sdret = dhd_bcmsdh_recv_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC,
+		                            ((uint8 *)PKTDATA(osh, pkt)), rdlen, pkt, NULL, NULL);
+		bus->f2rxdata++;
+		ASSERT(sdret != BCME_PENDING);
+
+		if (sdret < 0) {
+			DHD_ERROR(("%s: read %d %s bytes failed: %d\n", __FUNCTION__, rdlen,
+			           ((chan == SDPCM_EVENT_CHANNEL) ? "event" :
+			            ((chan == SDPCM_DATA_CHANNEL) ? "data" : "test")), sdret));
+			dhd_os_sdlock_rxq(bus->dhd);
+			PKTFREE(bus->dhd->osh, pkt, FALSE);
+			dhd_os_sdunlock_rxq(bus->dhd);
+			bus->dhd->rx_errors++;
+			dhdsdio_rxfail(bus, TRUE, RETRYCHAN(chan));
+			continue;
+		}
+
+		/* Copy the already-read portion */
+		PKTPUSH(osh, pkt, firstread);
+		bcopy(bus->rxhdr, PKTDATA(osh, pkt), firstread);
+
+#ifdef DHD_DEBUG
+		if (DHD_BYTES_ON() && DHD_DATA_ON()) {
+			prhex("Rx Data", PKTDATA(osh, pkt), len);
+		}
+#endif
+
+deliver:
+		/* Save superframe descriptor and allocate packet frame */
+		if (chan == SDPCM_GLOM_CHANNEL) {
+			if (SDPCM_GLOMDESC(&bus->rxhdr[SDPCM_FRAMETAG_LEN])) {
+				DHD_GLOM(("%s: got glom descriptor, %d bytes:\n",
+				          __FUNCTION__, len));
+#ifdef DHD_DEBUG
+				if (DHD_GLOM_ON()) {
+					prhex("Glom Data", PKTDATA(osh, pkt), len);
+				}
+#endif
+				PKTSETLEN(osh, pkt, len);
+				ASSERT(doff == SDPCM_HDRLEN);
+				PKTPULL(osh, pkt, SDPCM_HDRLEN);
+				bus->glomd = pkt;
+			} else {
+				DHD_ERROR(("%s: glom superframe w/o descriptor!\n", __FUNCTION__));
+				dhdsdio_rxfail(bus, FALSE, FALSE);
+			}
+			continue;
+		}
+
+		/* Fill in packet len and prio, deliver upward */
+		PKTSETLEN(osh, pkt, len);
+		PKTPULL(osh, pkt, doff);
+
+#ifdef SDTEST
+		/* Test channel packets are processed separately */
+		if (chan == SDPCM_TEST_CHANNEL) {
+			dhdsdio_testrcv(bus, pkt, seq);
+			continue;
+		}
+#endif /* SDTEST */
+
+		if (PKTLEN(osh, pkt) == 0) {
+			dhd_os_sdlock_rxq(bus->dhd);
+			PKTFREE(bus->dhd->osh, pkt, FALSE);
+			dhd_os_sdunlock_rxq(bus->dhd);
+			continue;
+		} else if (dhd_prot_hdrpull(bus->dhd, &ifidx, pkt) != 0) {
+			DHD_ERROR(("%s: rx protocol error\n", __FUNCTION__));
+			dhd_os_sdlock_rxq(bus->dhd);
+			PKTFREE(bus->dhd->osh, pkt, FALSE);
+			dhd_os_sdunlock_rxq(bus->dhd);
+			bus->dhd->rx_errors++;
+			continue;
+		}
+
+
+		/* Unlock during rx call */
+		dhd_os_sdunlock(bus->dhd);
+		dhd_rx_frame(bus->dhd, ifidx, pkt, 1);
+		dhd_os_sdlock(bus->dhd);
+	}
+	rxcount = maxframes - rxleft;
+#ifdef DHD_DEBUG
+	/* Message if we hit the limit */
+	if (!rxleft && !sdtest)
+		DHD_DATA(("%s: hit rx limit of %d frames\n", __FUNCTION__, maxframes));
+	else
+#endif /* DHD_DEBUG */
+	DHD_DATA(("%s: processed %d frames\n", __FUNCTION__, rxcount));
+	/* Back off rxseq if awaiting rtx, update rx_seq */
+	if (bus->rxskip)
+		rxseq--;
+	bus->rx_seq = rxseq;
+
+	return rxcount;
+}
+
+static uint32
+dhdsdio_hostmail(dhd_bus_t *bus)
+{
+	sdpcmd_regs_t *regs = bus->regs;
+	uint32 intstatus = 0;
+	uint32 hmb_data;
+	uint8 fcbits;
+	uint retries = 0;
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	/* Read mailbox data and ack that we did so */
+	R_SDREG(hmb_data, &regs->tohostmailboxdata, retries);
+	if (retries <= retry_limit)
+		W_SDREG(SMB_INT_ACK, &regs->tosbmailbox, retries);
+	bus->f1regdata += 2;
+
+	/* Dongle recomposed rx frames, accept them again */
+	if (hmb_data & HMB_DATA_NAKHANDLED) {
+		DHD_INFO(("Dongle reports NAK handled, expect rtx of %d\n", bus->rx_seq));
+		if (!bus->rxskip) {
+			DHD_ERROR(("%s: unexpected NAKHANDLED!\n", __FUNCTION__));
+		}
+		bus->rxskip = FALSE;
+		intstatus |= I_HMB_FRAME_IND;
+	}
+
+	/*
+	 * DEVREADY does not occur with gSPI.
+	 */
+	if (hmb_data & (HMB_DATA_DEVREADY | HMB_DATA_FWREADY)) {
+		bus->sdpcm_ver = (hmb_data & HMB_DATA_VERSION_MASK) >> HMB_DATA_VERSION_SHIFT;
+		if (bus->sdpcm_ver != SDPCM_PROT_VERSION)
+			DHD_ERROR(("Version mismatch, dongle reports %d, expecting %d\n",
+			           bus->sdpcm_ver, SDPCM_PROT_VERSION));
+		else
+			DHD_INFO(("Dongle ready, protocol version %d\n", bus->sdpcm_ver));
+	}
+
+	/*
+	 * Flow Control has been moved into the RX headers and this out of band
+	 * method isn't used any more.  Leae this here for possibly remaining backward
+	 * compatible with older dongles
+	 */
+	if (hmb_data & HMB_DATA_FC) {
+		fcbits = (hmb_data & HMB_DATA_FCDATA_MASK) >> HMB_DATA_FCDATA_SHIFT;
+
+		if (fcbits & ~bus->flowcontrol)
+			bus->fc_xoff++;
+		if (bus->flowcontrol & ~fcbits)
+			bus->fc_xon++;
+
+		bus->fc_rcvd++;
+		bus->flowcontrol = fcbits;
+	}
+
+	/* Shouldn't be any others */
+	if (hmb_data & ~(HMB_DATA_DEVREADY |
+	                 HMB_DATA_NAKHANDLED |
+	                 HMB_DATA_FC |
+	                 HMB_DATA_FWREADY |
+	                 HMB_DATA_FCDATA_MASK |
+	                 HMB_DATA_VERSION_MASK)) {
+		DHD_ERROR(("Unknown mailbox data content: 0x%02x\n", hmb_data));
+	}
+
+	return intstatus;
+}
+
+bool
+dhdsdio_dpc(dhd_bus_t *bus)
+{
+	bcmsdh_info_t *sdh = bus->sdh;
+	sdpcmd_regs_t *regs = bus->regs;
+	uint32 intstatus, newstatus = 0;
+	uint retries = 0;
+	uint rxlimit = dhd_rxbound; /* Rx frames to read before resched */
+	uint txlimit = dhd_txbound; /* Tx frames to send before resched */
+	uint framecnt = 0;		  /* Temporary counter of tx/rx frames */
+	bool rxdone = TRUE;		  /* Flag for no more read data */
+	bool resched = FALSE;	  /* Flag indicating resched wanted */
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	/* Start with leftover status bits */
+	intstatus = bus->intstatus;
+
+	dhd_os_sdlock(bus->dhd);
+
+	/* If waiting for HTAVAIL, check status */
+	if (bus->clkstate == CLK_PENDING) {
+		int err;
+		uint8 clkctl, devctl = 0;
+
+#ifdef DHD_DEBUG
+		/* Check for inconsistent device control */
+		devctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err);
+		if (err) {
+			DHD_ERROR(("%s: error reading DEVCTL: %d\n", __FUNCTION__, err));
+			bus->dhd->busstate = DHD_BUS_DOWN;
+		} else {
+			ASSERT(devctl & SBSDIO_DEVCTL_CA_INT_ONLY);
+		}
+#endif /* DHD_DEBUG */
+
+		/* Read CSR, if clock on switch to AVAIL, else ignore */
+		clkctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err);
+		if (err) {
+			DHD_ERROR(("%s: error reading CSR: %d\n", __FUNCTION__, err));
+			bus->dhd->busstate = DHD_BUS_DOWN;
+		}
+
+		DHD_INFO(("DPC: PENDING, devctl 0x%02x clkctl 0x%02x\n", devctl, clkctl));
+
+		if (SBSDIO_HTAV(clkctl)) {
+			devctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err);
+			if (err) {
+				DHD_ERROR(("%s: error reading DEVCTL: %d\n",
+				           __FUNCTION__, err));
+				bus->dhd->busstate = DHD_BUS_DOWN;
+			}
+			devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
+			bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, devctl, &err);
+			if (err) {
+				DHD_ERROR(("%s: error writing DEVCTL: %d\n",
+				           __FUNCTION__, err));
+				bus->dhd->busstate = DHD_BUS_DOWN;
+			}
+			bus->clkstate = CLK_AVAIL;
+		} else {
+			goto clkwait;
+		}
+	}
+
+	BUS_WAKE(bus);
+
+	/* Make sure backplane clock is on */
+	dhdsdio_clkctl(bus, CLK_AVAIL, TRUE);
+	if (bus->clkstate == CLK_PENDING)
+		goto clkwait;
+
+	/* Pending interrupt indicates new device status */
+	if (bus->ipend) {
+		bus->ipend = FALSE;
+		R_SDREG(newstatus, &regs->intstatus, retries);
+		bus->f1regdata++;
+		if (bcmsdh_regfail(bus->sdh))
+			newstatus = 0;
+		newstatus &= bus->hostintmask;
+		bus->fcstate = !!(newstatus & I_HMB_FC_STATE);
+		if (newstatus) {
+			W_SDREG(newstatus, &regs->intstatus, retries);
+			bus->f1regdata++;
+		}
+	}
+
+	/* Merge new bits with previous */
+	intstatus |= newstatus;
+	bus->intstatus = 0;
+
+	/* Handle flow-control change: read new state in case our ack
+	 * crossed another change interrupt.  If change still set, assume
+	 * FC ON for safety, let next loop through do the debounce.
+	 */
+	if (intstatus & I_HMB_FC_CHANGE) {
+		intstatus &= ~I_HMB_FC_CHANGE;
+		W_SDREG(I_HMB_FC_CHANGE, &regs->intstatus, retries);
+		R_SDREG(newstatus, &regs->intstatus, retries);
+		bus->f1regdata += 2;
+		bus->fcstate = !!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE));
+		intstatus |= (newstatus & bus->hostintmask);
+	}
+
+	/* Handle host mailbox indication */
+	if (intstatus & I_HMB_HOST_INT) {
+		intstatus &= ~I_HMB_HOST_INT;
+		intstatus |= dhdsdio_hostmail(bus);
+	}
+
+	/* Generally don't ask for these, can get CRC errors... */
+	if (intstatus & I_WR_OOSYNC) {
+		DHD_ERROR(("Dongle reports WR_OOSYNC\n"));
+		intstatus &= ~I_WR_OOSYNC;
+	}
+
+	if (intstatus & I_RD_OOSYNC) {
+		DHD_ERROR(("Dongle reports RD_OOSYNC\n"));
+		intstatus &= ~I_RD_OOSYNC;
+	}
+
+	if (intstatus & I_SBINT) {
+		DHD_ERROR(("Dongle reports SBINT\n"));
+		intstatus &= ~I_SBINT;
+	}
+
+	/* Would be active due to wake-wlan in gSPI */
+	if (intstatus & I_CHIPACTIVE) {
+		DHD_INFO(("Dongle reports CHIPACTIVE\n"));
+		intstatus &= ~I_CHIPACTIVE;
+	}
+
+	/* Ignore frame indications if rxskip is set */
+	if (bus->rxskip)
+		intstatus &= ~I_HMB_FRAME_IND;
+
+	/* On frame indication, read available frames */
+	if (PKT_AVAILABLE()) {
+		framecnt = dhdsdio_readframes(bus, rxlimit, &rxdone);
+		if (rxdone || bus->rxskip)
+			intstatus &= ~I_HMB_FRAME_IND;
+		rxlimit -= MIN(framecnt, rxlimit);
+	}
+
+	/* Keep still-pending events for next scheduling */
+	bus->intstatus = intstatus;
+
+clkwait:
+	/* Re-enable interrupts to detect new device events (mailbox, rx frame)
+	 * or clock availability.  (Allows tx loop to check ipend if desired.)
+	 * (Unless register access seems hosed, as we may not be able to ACK...)
+	 */
+	if (bus->intr && bus->intdis && !bcmsdh_regfail(sdh)) {
+		DHD_INTR(("%s: enable SDIO interrupts, rxdone %d framecnt %d\n",
+		          __FUNCTION__, rxdone, framecnt));
+		bus->intdis = FALSE;
+#if defined(OOB_INTR_ONLY)
+		bcmsdh_oob_intr_set(1);
+#endif /* (OOB_INTR_ONLY) */
+		bcmsdh_intr_enable(sdh);
+	}
+
+	if (DATAOK(bus) && bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL)) {
+		int ret, i;
+
+		ret = dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC,
+		                      (uint8 *)bus->ctrl_frame_buf, (uint32)bus->ctrl_frame_len,
+			NULL, NULL, NULL);
+		ASSERT(ret != BCME_PENDING);
+
+		if (ret < 0) {
+			/* On failure, abort the command and terminate the frame */
+			DHD_INFO(("%s: sdio error %d, abort command and terminate frame.\n",
+			          __FUNCTION__, ret));
+			bus->tx_sderrs++;
+
+			bcmsdh_abort(sdh, SDIO_FUNC_2);
+
+			bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_FRAMECTRL,
+			                 SFC_WF_TERM, NULL);
+			bus->f1regdata++;
+
+			for (i = 0; i < 3; i++) {
+				uint8 hi, lo;
+				hi = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
+				                     SBSDIO_FUNC1_WFRAMEBCHI, NULL);
+				lo = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
+				                     SBSDIO_FUNC1_WFRAMEBCLO, NULL);
+				bus->f1regdata += 2;
+				if ((hi == 0) && (lo == 0))
+					break;
+			}
+
+		}
+		if (ret == 0) {
+				bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
+		}
+
+		printf("Return_dpc value is : %d\n", ret);
+		bus->ctrl_frame_stat = FALSE;
+		dhd_wait_event_wakeup(bus->dhd);
+	}
+	/* Send queued frames (limit 1 if rx may still be pending) */
+	else if ((bus->clkstate == CLK_AVAIL) && !bus->fcstate &&
+	    pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit && DATAOK(bus)) {
+		framecnt = rxdone ? txlimit : MIN(txlimit, dhd_txminmax);
+		framecnt = dhdsdio_sendfromq(bus, framecnt);
+		txlimit -= framecnt;
+	}
+
+	/* Resched if events or tx frames are pending, else await next interrupt */
+	/* On failed register access, all bets are off: no resched or interrupts */
+	if ((bus->dhd->busstate == DHD_BUS_DOWN) || bcmsdh_regfail(sdh)) {
+		DHD_ERROR(("%s: failed backplane access over SDIO, halting operation %d \n",
+		           __FUNCTION__, bcmsdh_regfail(sdh)));
+		bus->dhd->busstate = DHD_BUS_DOWN;
+		bus->intstatus = 0;
+	} else if (bus->clkstate == CLK_PENDING) {
+		DHD_INFO(("%s: rescheduled due to CLK_PENDING awaiting \
+			I_CHIPACTIVE interrupt", __FUNCTION__));
+			resched = TRUE;
+	} else if (bus->intstatus || bus->ipend ||
+	           (!bus->fcstate && pktq_mlen(&bus->txq, ~bus->flowcontrol) && DATAOK(bus)) ||
+			PKT_AVAILABLE()) {  /* Read multiple frames */
+		resched = TRUE;
+	}
+
+
+	bus->dpc_sched = resched;
+
+	/* If we're done for now, turn off clock request. */
+	if ((bus->clkstate != CLK_PENDING) && bus->idletime == DHD_IDLE_IMMEDIATE) {
+		bus->activity = FALSE;
+		dhdsdio_clkctl(bus, CLK_NONE, FALSE);
+	}
+
+	dhd_os_sdunlock(bus->dhd);
+
+	return resched;
+}
+
+bool
+dhd_bus_dpc(struct dhd_bus *bus)
+{
+	bool resched;
+
+	/* Call the DPC directly. */
+	DHD_TRACE(("Calling dhdsdio_dpc() from %s\n", __FUNCTION__));
+	resched = dhdsdio_dpc(bus);
+
+	return resched;
+}
+
+void
+dhdsdio_isr(void *arg)
+{
+	dhd_bus_t *bus = (dhd_bus_t*)arg;
+	bcmsdh_info_t *sdh;
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	if (!bus) {
+		DHD_ERROR(("%s : bus is null pointer , exit \n", __FUNCTION__));
+		return;
+	}
+	sdh = bus->sdh;
+
+	if (bus->dhd->busstate == DHD_BUS_DOWN) {
+		DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__));
+		return;
+	}
+	/* Count the interrupt call */
+	bus->intrcount++;
+	bus->ipend = TRUE;
+
+	/* Shouldn't get this interrupt if we're sleeping? */
+	if (bus->sleeping) {
+		DHD_ERROR(("INTERRUPT WHILE SLEEPING??\n"));
+		return;
+	}
+
+	/* Disable additional interrupts (is this needed now)? */
+	if (bus->intr) {
+		DHD_INTR(("%s: disable SDIO interrupts\n", __FUNCTION__));
+	} else {
+		DHD_ERROR(("dhdsdio_isr() w/o interrupt configured!\n"));
+	}
+
+	bcmsdh_intr_disable(sdh);
+	bus->intdis = TRUE;
+
+#if defined(SDIO_ISR_THREAD)
+	DHD_TRACE(("Calling dhdsdio_dpc() from %s\n", __FUNCTION__));
+	dhd_os_wake_lock(bus->dhd);
+	while (dhdsdio_dpc(bus));
+	dhd_os_wake_unlock(bus->dhd);
+#else
+	bus->dpc_sched = TRUE;
+	dhd_sched_dpc(bus->dhd);
+#endif 
+
+}
+
+#ifdef SDTEST
+static void
+dhdsdio_pktgen_init(dhd_bus_t *bus)
+{
+	/* Default to specified length, or full range */
+	if (dhd_pktgen_len) {
+		bus->pktgen_maxlen = MIN(dhd_pktgen_len, MAX_PKTGEN_LEN);
+		bus->pktgen_minlen = bus->pktgen_maxlen;
+	} else {
+		bus->pktgen_maxlen = MAX_PKTGEN_LEN;
+		bus->pktgen_minlen = 0;
+	}
+	bus->pktgen_len = (uint16)bus->pktgen_minlen;
+
+	/* Default to per-watchdog burst with 10s print time */
+	bus->pktgen_freq = 1;
+	bus->pktgen_print = 10000 / dhd_watchdog_ms;
+	bus->pktgen_count = (dhd_pktgen * dhd_watchdog_ms + 999) / 1000;
+
+	/* Default to echo mode */
+	bus->pktgen_mode = DHD_PKTGEN_ECHO;
+	bus->pktgen_stop = 1;
+}
+
+static void
+dhdsdio_pktgen(dhd_bus_t *bus)
+{
+	void *pkt;
+	uint8 *data;
+	uint pktcount;
+	uint fillbyte;
+	osl_t *osh = bus->dhd->osh;
+	uint16 len;
+
+	/* Display current count if appropriate */
+	if (bus->pktgen_print && (++bus->pktgen_ptick >= bus->pktgen_print)) {
+		bus->pktgen_ptick = 0;
+		printf("%s: send attempts %d rcvd %d\n",
+		       __FUNCTION__, bus->pktgen_sent, bus->pktgen_rcvd);
+	}
+
+	/* For recv mode, just make sure dongle has started sending */
+	if (bus->pktgen_mode == DHD_PKTGEN_RECV) {
+		if (!bus->pktgen_rcvd)
+			dhdsdio_sdtest_set(bus, TRUE);
+		return;
+	}
+
+	/* Otherwise, generate or request the specified number of packets */
+	for (pktcount = 0; pktcount < bus->pktgen_count; pktcount++) {
+		/* Stop if total has been reached */
+		if (bus->pktgen_total && (bus->pktgen_sent >= bus->pktgen_total)) {
+			bus->pktgen_count = 0;
+			break;
+		}
+
+		/* Allocate an appropriate-sized packet */
+		len = bus->pktgen_len;
+		if (!(pkt = PKTGET(osh, (len + SDPCM_HDRLEN + SDPCM_TEST_HDRLEN + DHD_SDALIGN),
+		                   TRUE))) {;
+			DHD_ERROR(("%s: PKTGET failed!\n", __FUNCTION__));
+			break;
+		}
+		PKTALIGN(osh, pkt, (len + SDPCM_HDRLEN + SDPCM_TEST_HDRLEN), DHD_SDALIGN);
+		data = (uint8*)PKTDATA(osh, pkt) + SDPCM_HDRLEN;
+
+		/* Write test header cmd and extra based on mode */
+		switch (bus->pktgen_mode) {
+		case DHD_PKTGEN_ECHO:
+			*data++ = SDPCM_TEST_ECHOREQ;
+			*data++ = (uint8)bus->pktgen_sent;
+			break;
+
+		case DHD_PKTGEN_SEND:
+			*data++ = SDPCM_TEST_DISCARD;
+			*data++ = (uint8)bus->pktgen_sent;
+			break;
+
+		case DHD_PKTGEN_RXBURST:
+			*data++ = SDPCM_TEST_BURST;
+			*data++ = (uint8)bus->pktgen_count;
+			break;
+
+		default:
+			DHD_ERROR(("Unrecognized pktgen mode %d\n", bus->pktgen_mode));
+			PKTFREE(osh, pkt, TRUE);
+			bus->pktgen_count = 0;
+			return;
+		}
+
+		/* Write test header length field */
+		*data++ = (len >> 0);
+		*data++ = (len >> 8);
+
+		/* Then fill in the remainder -- N/A for burst, but who cares... */
+		for (fillbyte = 0; fillbyte < len; fillbyte++)
+			*data++ = SDPCM_TEST_FILL(fillbyte, (uint8)bus->pktgen_sent);
+
+#ifdef DHD_DEBUG
+		if (DHD_BYTES_ON() && DHD_DATA_ON()) {
+			data = (uint8*)PKTDATA(osh, pkt) + SDPCM_HDRLEN;
+			prhex("dhdsdio_pktgen: Tx Data", data, PKTLEN(osh, pkt) - SDPCM_HDRLEN);
+		}
+#endif
+
+		/* Send it */
+		if (dhdsdio_txpkt(bus, pkt, SDPCM_TEST_CHANNEL, TRUE)) {
+			bus->pktgen_fail++;
+			if (bus->pktgen_stop && bus->pktgen_stop == bus->pktgen_fail)
+				bus->pktgen_count = 0;
+		}
+		bus->pktgen_sent++;
+
+		/* Bump length if not fixed, wrap at max */
+		if (++bus->pktgen_len > bus->pktgen_maxlen)
+			bus->pktgen_len = (uint16)bus->pktgen_minlen;
+
+		/* Special case for burst mode: just send one request! */
+		if (bus->pktgen_mode == DHD_PKTGEN_RXBURST)
+			break;
+	}
+}
+
+static void
+dhdsdio_sdtest_set(dhd_bus_t *bus, bool start)
+{
+	void *pkt;
+	uint8 *data;
+	osl_t *osh = bus->dhd->osh;
+
+	/* Allocate the packet */
+	if (!(pkt = PKTGET(osh, SDPCM_HDRLEN + SDPCM_TEST_HDRLEN + DHD_SDALIGN, TRUE))) {
+		DHD_ERROR(("%s: PKTGET failed!\n", __FUNCTION__));
+		return;
+	}
+	PKTALIGN(osh, pkt, (SDPCM_HDRLEN + SDPCM_TEST_HDRLEN), DHD_SDALIGN);
+	data = (uint8*)PKTDATA(osh, pkt) + SDPCM_HDRLEN;
+
+	/* Fill in the test header */
+	*data++ = SDPCM_TEST_SEND;
+	*data++ = start;
+	*data++ = (bus->pktgen_maxlen >> 0);
+	*data++ = (bus->pktgen_maxlen >> 8);
+
+	/* Send it */
+	if (dhdsdio_txpkt(bus, pkt, SDPCM_TEST_CHANNEL, TRUE))
+		bus->pktgen_fail++;
+}
+
+
+static void
+dhdsdio_testrcv(dhd_bus_t *bus, void *pkt, uint seq)
+{
+	osl_t *osh = bus->dhd->osh;
+	uint8 *data;
+	uint pktlen;
+
+	uint8 cmd;
+	uint8 extra;
+	uint16 len;
+	uint16 offset;
+
+	/* Check for min length */
+	if ((pktlen = PKTLEN(osh, pkt)) < SDPCM_TEST_HDRLEN) {
+		DHD_ERROR(("dhdsdio_restrcv: toss runt frame, pktlen %d\n", pktlen));
+		PKTFREE(osh, pkt, FALSE);
+		return;
+	}
+
+	/* Extract header fields */
+	data = PKTDATA(osh, pkt);
+	cmd = *data++;
+	extra = *data++;
+	len = *data++; len += *data++ << 8;
+
+	/* Check length for relevant commands */
+	if (cmd == SDPCM_TEST_DISCARD || cmd == SDPCM_TEST_ECHOREQ || cmd == SDPCM_TEST_ECHORSP) {
+		if (pktlen != len + SDPCM_TEST_HDRLEN) {
+			DHD_ERROR(("dhdsdio_testrcv: frame length mismatch, pktlen %d seq %d"
+			           " cmd %d extra %d len %d\n", pktlen, seq, cmd, extra, len));
+			PKTFREE(osh, pkt, FALSE);
+			return;
+		}
+	}
+
+	/* Process as per command */
+	switch (cmd) {
+	case SDPCM_TEST_ECHOREQ:
+		/* Rx->Tx turnaround ok (even on NDIS w/current implementation) */
+		*(uint8 *)(PKTDATA(osh, pkt)) = SDPCM_TEST_ECHORSP;
+		if (dhdsdio_txpkt(bus, pkt, SDPCM_TEST_CHANNEL, TRUE) == 0) {
+			bus->pktgen_sent++;
+		} else {
+			bus->pktgen_fail++;
+			PKTFREE(osh, pkt, FALSE);
+		}
+		bus->pktgen_rcvd++;
+		break;
+
+	case SDPCM_TEST_ECHORSP:
+		if (bus->ext_loop) {
+			PKTFREE(osh, pkt, FALSE);
+			bus->pktgen_rcvd++;
+			break;
+		}
+
+		for (offset = 0; offset < len; offset++, data++) {
+			if (*data != SDPCM_TEST_FILL(offset, extra)) {
+				DHD_ERROR(("dhdsdio_testrcv: echo data mismatch: "
+				           "offset %d (len %d) expect 0x%02x rcvd 0x%02x\n",
+				           offset, len, SDPCM_TEST_FILL(offset, extra), *data));
+				break;
+			}
+		}
+		PKTFREE(osh, pkt, FALSE);
+		bus->pktgen_rcvd++;
+		break;
+
+	case SDPCM_TEST_DISCARD:
+		PKTFREE(osh, pkt, FALSE);
+		bus->pktgen_rcvd++;
+		break;
+
+	case SDPCM_TEST_BURST:
+	case SDPCM_TEST_SEND:
+	default:
+		DHD_INFO(("dhdsdio_testrcv: unsupported or unknown command, pktlen %d seq %d"
+		          " cmd %d extra %d len %d\n", pktlen, seq, cmd, extra, len));
+		PKTFREE(osh, pkt, FALSE);
+		break;
+	}
+
+	/* For recv mode, stop at limie (and tell dongle to stop sending) */
+	if (bus->pktgen_mode == DHD_PKTGEN_RECV) {
+		if (bus->pktgen_total && (bus->pktgen_rcvd >= bus->pktgen_total)) {
+			bus->pktgen_count = 0;
+			dhdsdio_sdtest_set(bus, FALSE);
+		}
+	}
+}
+#endif /* SDTEST */
+
+extern bool
+dhd_bus_watchdog(dhd_pub_t *dhdp)
+{
+	dhd_bus_t *bus;
+
+	DHD_TIMER(("%s: Enter\n", __FUNCTION__));
+
+	bus = dhdp->bus;
+
+	if (bus->dhd->dongle_reset)
+		return FALSE;
+
+	/* Ignore the timer if simulating bus down */
+	if (bus->sleeping)
+		return FALSE;
+
+	/* Poll period: check device if appropriate. */
+	if (bus->poll && (++bus->polltick >= bus->pollrate)) {
+		uint32 intstatus = 0;
+
+		/* Reset poll tick */
+		bus->polltick = 0;
+
+		/* Check device if no interrupts */
+		if (!bus->intr || (bus->intrcount == bus->lastintrs)) {
+
+			if (!bus->dpc_sched) {
+				uint8 devpend;
+				devpend = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_0,
+				                          SDIOD_CCCR_INTPEND, NULL);
+				intstatus = devpend & (INTR_STATUS_FUNC1 | INTR_STATUS_FUNC2);
+			}
+
+			/* If there is something, make like the ISR and schedule the DPC */
+			if (intstatus) {
+				bus->pollcnt++;
+				bus->ipend = TRUE;
+				if (bus->intr) {
+					bcmsdh_intr_disable(bus->sdh);
+				}
+				bus->dpc_sched = TRUE;
+				dhd_sched_dpc(bus->dhd);
+
+			}
+		}
+
+		/* Update interrupt tracking */
+		bus->lastintrs = bus->intrcount;
+	}
+
+#ifdef DHD_DEBUG
+	/* Poll for console output periodically */
+	if (dhdp->busstate == DHD_BUS_DATA && dhd_console_ms != 0) {
+		bus->console.count += dhd_watchdog_ms;
+		if (bus->console.count >= dhd_console_ms) {
+			bus->console.count -= dhd_console_ms;
+			/* Make sure backplane clock is on */
+			dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+			if (dhdsdio_readconsole(bus) < 0)
+				dhd_console_ms = 0;	/* On error, stop trying */
+		}
+	}
+#endif /* DHD_DEBUG */
+
+#ifdef SDTEST
+	/* Generate packets if configured */
+	if (bus->pktgen_count && (++bus->pktgen_tick >= bus->pktgen_freq)) {
+		/* Make sure backplane clock is on */
+		dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+		bus->pktgen_tick = 0;
+		dhdsdio_pktgen(bus);
+	}
+#endif
+
+	/* On idle timeout clear activity flag and/or turn off clock */
+	if ((bus->idletime > 0) && (bus->clkstate == CLK_AVAIL)) {
+		if (++bus->idlecount >= bus->idletime) {
+			bus->idlecount = 0;
+			if (bus->activity) {
+				bus->activity = FALSE;
+				dhdsdio_clkctl(bus, CLK_NONE, FALSE);
+			}
+		}
+	}
+
+	return bus->ipend;
+}
+
+#ifdef DHD_DEBUG
+extern int
+dhd_bus_console_in(dhd_pub_t *dhdp, uchar *msg, uint msglen)
+{
+	dhd_bus_t *bus = dhdp->bus;
+	uint32 addr, val;
+	int rv;
+	void *pkt;
+
+	/* Address could be zero if CONSOLE := 0 in dongle Makefile */
+	if (bus->console_addr == 0)
+		return BCME_UNSUPPORTED;
+
+	/* Exclusive bus access */
+	dhd_os_sdlock(bus->dhd);
+
+	/* Don't allow input if dongle is in reset */
+	if (bus->dhd->dongle_reset) {
+		dhd_os_sdunlock(bus->dhd);
+		return BCME_NOTREADY;
+	}
+
+	/* Request clock to allow SDIO accesses */
+	BUS_WAKE(bus);
+	/* No pend allowed since txpkt is called later, ht clk has to be on */
+	dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+
+	/* Zero cbuf_index */
+	addr = bus->console_addr + OFFSETOF(hndrte_cons_t, cbuf_idx);
+	val = htol32(0);
+	if ((rv = dhdsdio_membytes(bus, TRUE, addr, (uint8 *)&val, sizeof(val))) < 0)
+		goto done;
+
+	/* Write message into cbuf */
+	addr = bus->console_addr + OFFSETOF(hndrte_cons_t, cbuf);
+	if ((rv = dhdsdio_membytes(bus, TRUE, addr, (uint8 *)msg, msglen)) < 0)
+		goto done;
+
+	/* Write length into vcons_in */
+	addr = bus->console_addr + OFFSETOF(hndrte_cons_t, vcons_in);
+	val = htol32(msglen);
+	if ((rv = dhdsdio_membytes(bus, TRUE, addr, (uint8 *)&val, sizeof(val))) < 0)
+		goto done;
+
+	/* Bump dongle by sending an empty event pkt.
+	 * sdpcm_sendup (RX) checks for virtual console input.
+	 */
+	if (((pkt = PKTGET(bus->dhd->osh, 4 + SDPCM_RESERVE, TRUE)) != NULL) &&
+		bus->clkstate == CLK_AVAIL)
+		dhdsdio_txpkt(bus, pkt, SDPCM_EVENT_CHANNEL, TRUE);
+
+done:
+	if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) {
+		bus->activity = FALSE;
+		dhdsdio_clkctl(bus, CLK_NONE, TRUE);
+	}
+
+	dhd_os_sdunlock(bus->dhd);
+
+	return rv;
+}
+#endif /* DHD_DEBUG */
+
+#ifdef DHD_DEBUG
+static void
+dhd_dump_cis(uint fn, uint8 *cis)
+{
+	uint byte, tag, tdata;
+	DHD_INFO(("Function %d CIS:\n", fn));
+
+	for (tdata = byte = 0; byte < SBSDIO_CIS_SIZE_LIMIT; byte++) {
+		if ((byte % 16) == 0)
+			DHD_INFO(("    "));
+		DHD_INFO(("%02x ", cis[byte]));
+		if ((byte % 16) == 15)
+			DHD_INFO(("\n"));
+		if (!tdata--) {
+			tag = cis[byte];
+			if (tag == 0xff)
+				break;
+			else if (!tag)
+				tdata = 0;
+			else if ((byte + 1) < SBSDIO_CIS_SIZE_LIMIT)
+				tdata = cis[byte + 1] + 1;
+			else
+				DHD_INFO(("]"));
+		}
+	}
+	if ((byte % 16) != 15)
+		DHD_INFO(("\n"));
+}
+#endif /* DHD_DEBUG */
+
+static bool
+dhdsdio_chipmatch(uint16 chipid)
+{
+	if (chipid == BCM4325_CHIP_ID)
+		return TRUE;
+	if (chipid == BCM4329_CHIP_ID)
+		return TRUE;
+	if (chipid == BCM4315_CHIP_ID)
+		return TRUE;
+	if (chipid == BCM4319_CHIP_ID)
+		return TRUE;
+	return FALSE;
+}
+
+static void *
+dhdsdio_probe(uint16 venid, uint16 devid, uint16 bus_no, uint16 slot,
+	uint16 func, uint bustype, void *regsva, osl_t * osh, void *sdh)
+{
+	int ret;
+	dhd_bus_t *bus;
+
+	/* Init global variables at run-time, not as part of the declaration.
+	 * This is required to support init/de-init of the driver. Initialization
+	 * of globals as part of the declaration results in non-deterministic
+	 * behavior since the value of the globals may be different on the
+	 * first time that the driver is initialized vs subsequent initializations.
+	 */
+	dhd_txbound = DHD_TXBOUND;
+	dhd_rxbound = DHD_RXBOUND;
+	dhd_alignctl = TRUE;
+	sd1idle = TRUE;
+	dhd_readahead = TRUE;
+	retrydata = FALSE;
+	dhd_doflow = FALSE;
+	dhd_dongle_memsize = 0;
+	dhd_txminmax = DHD_TXMINMAX;
+
+	forcealign = TRUE;
+
+
+	dhd_common_init();
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+	DHD_INFO(("%s: venid 0x%04x devid 0x%04x\n", __FUNCTION__, venid, devid));
+
+	/* We make assumptions about address window mappings */
+	ASSERT((uintptr)regsva == SI_ENUM_BASE);
+
+	/* BCMSDH passes venid and devid based on CIS parsing -- but low-power start
+	 * means early parse could fail, so here we should get either an ID
+	 * we recognize OR (-1) indicating we must request power first.
+	 */
+	/* Check the Vendor ID */
+	switch (venid) {
+		case 0x0000:
+		case VENDOR_BROADCOM:
+			break;
+		default:
+			DHD_ERROR(("%s: unknown vendor: 0x%04x\n",
+			           __FUNCTION__, venid));
+			return NULL;
+	}
+
+	/* Check the Device ID and make sure it's one that we support */
+	switch (devid) {
+		case BCM4325_D11DUAL_ID:		/* 4325 802.11a/g id */
+		case BCM4325_D11G_ID:			/* 4325 802.11g 2.4Ghz band id */
+		case BCM4325_D11A_ID:			/* 4325 802.11a 5Ghz band id */
+			DHD_INFO(("%s: found 4325 Dongle\n", __FUNCTION__));
+			break;
+		case BCM4329_D11NDUAL_ID:		/* 4329 802.11n dualband device */
+		case BCM4329_D11N2G_ID:		/* 4329 802.11n 2.4G device */
+		case BCM4329_D11N5G_ID:		/* 4329 802.11n 5G device */
+		case 0x4329:
+			DHD_INFO(("%s: found 4329 Dongle\n", __FUNCTION__));
+			break;
+		case BCM4315_D11DUAL_ID:		/* 4315 802.11a/g id */
+		case BCM4315_D11G_ID:			/* 4315 802.11g id */
+		case BCM4315_D11A_ID:			/* 4315 802.11a id */
+			DHD_INFO(("%s: found 4315 Dongle\n", __FUNCTION__));
+			break;
+		case BCM4319_D11N_ID:			/* 4319 802.11n id */
+		case BCM4319_D11N2G_ID:			/* 4319 802.11n2g id */
+		case BCM4319_D11N5G_ID:			/* 4319 802.11n5g id */
+			DHD_INFO(("%s: found 4319 Dongle\n", __FUNCTION__));
+			break;
+		case 0:
+			DHD_INFO(("%s: allow device id 0, will check chip internals\n",
+			          __FUNCTION__));
+			break;
+
+		default:
+			DHD_ERROR(("%s: skipping 0x%04x/0x%04x, not a dongle\n",
+			           __FUNCTION__, venid, devid));
+			return NULL;
+	}
+
+	if (osh == NULL) {
+		/* Ask the OS interface part for an OSL handle */
+		if (!(osh = dhd_osl_attach(sdh, DHD_BUS))) {
+			DHD_ERROR(("%s: osl_attach failed!\n", __FUNCTION__));
+			return NULL;
+		}
+	}
+
+	/* Allocate private bus interface state */
+	if (!(bus = MALLOC(osh, sizeof(dhd_bus_t)))) {
+		DHD_ERROR(("%s: MALLOC of dhd_bus_t failed\n", __FUNCTION__));
+		goto fail;
+	}
+	bzero(bus, sizeof(dhd_bus_t));
+	bus->sdh = sdh;
+	bus->cl_devid = (uint16)devid;
+	bus->bus = DHD_BUS;
+	bus->tx_seq = SDPCM_SEQUENCE_WRAP - 1;
+	bus->usebufpool = FALSE; /* Use bufpool if allocated, else use locally malloced rxbuf */
+
+	/* attempt to attach to the dongle */
+	if (!(dhdsdio_probe_attach(bus, osh, sdh, regsva, devid))) {
+		DHD_ERROR(("%s: dhdsdio_probe_attach failed\n", __FUNCTION__));
+		goto fail;
+	}
+
+	/* Attach to the dhd/OS/network interface */
+	if (!(bus->dhd = dhd_attach(osh, bus, SDPCM_RESERVE))) {
+		DHD_ERROR(("%s: dhd_attach failed\n", __FUNCTION__));
+		goto fail;
+	}
+
+	/* Allocate buffers */
+	if (!(dhdsdio_probe_malloc(bus, osh, sdh))) {
+		DHD_ERROR(("%s: dhdsdio_probe_malloc failed\n", __FUNCTION__));
+		goto fail;
+	}
+
+	if (!(dhdsdio_probe_init(bus, osh, sdh))) {
+		DHD_ERROR(("%s: dhdsdio_probe_init failed\n", __FUNCTION__));
+		goto fail;
+	}
+
+	/* Register interrupt callback, but mask it (not operational yet). */
+	DHD_INTR(("%s: disable SDIO interrupts (not interested yet)\n", __FUNCTION__));
+	bcmsdh_intr_disable(sdh);
+	if ((ret = bcmsdh_intr_reg(sdh, dhdsdio_isr, bus)) != 0) {
+		DHD_ERROR(("%s: FAILED: bcmsdh_intr_reg returned %d\n",
+		           __FUNCTION__, ret));
+		goto fail;
+	}
+	DHD_INTR(("%s: registered SDIO interrupt function ok\n", __FUNCTION__));
+
+	DHD_INFO(("%s: completed!!\n", __FUNCTION__));
+
+
+	/* if firmware path present try to download and bring up bus */
+	if ((ret = dhd_bus_start(bus->dhd)) != 0) {
+#if 1
+		DHD_ERROR(("%s: failed\n", __FUNCTION__));
+		goto fail;
+#else
+		if (ret == BCME_NOTUP)  {
+			DHD_ERROR(("%s: dongle is not responding\n", __FUNCTION__));
+			goto fail;
+		}
+#endif
+	}
+	/* Ok, have the per-port tell the stack we're open for business */
+	if (dhd_net_attach(bus->dhd, 0) != 0) {
+		DHD_ERROR(("%s: Net attach failed!!\n", __FUNCTION__));
+		goto fail;
+	}
+
+	return bus;
+
+fail:
+	dhdsdio_release(bus, osh);
+	return NULL;
+}
+
+
+static bool
+dhdsdio_probe_attach(struct dhd_bus *bus, osl_t *osh, void *sdh, void *regsva,
+                    uint16 devid)
+{
+	uint8 clkctl = 0;
+	int err = 0;
+
+	bus->alp_only = TRUE;
+
+	/* Return the window to backplane enumeration space for core access */
+	if (dhdsdio_set_siaddr_window(bus, SI_ENUM_BASE)) {
+		DHD_ERROR(("%s: FAILED to return to SI_ENUM_BASE\n", __FUNCTION__));
+	}
+
+#ifdef DHD_DEBUG
+	printf("F1 signature read @0x18000000=0x%4x\n",
+	       bcmsdh_reg_read(bus->sdh, SI_ENUM_BASE, 4));
+
+
+#endif /* DHD_DEBUG */
+
+
+	/* Force PLL off until si_attach() programs PLL control regs */
+
+
+
+	bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, DHD_INIT_CLKCTL1, &err);
+	if (!err)
+		clkctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err);
+
+	if (err || ((clkctl & ~SBSDIO_AVBITS) != DHD_INIT_CLKCTL1)) {
+		DHD_ERROR(("dhdsdio_probe: ChipClkCSR access: err %d wrote 0x%02x read 0x%02x\n",
+		           err, DHD_INIT_CLKCTL1, clkctl));
+		goto fail;
+	}
+
+
+#ifdef DHD_DEBUG
+	if (DHD_INFO_ON()) {
+		uint fn, numfn;
+		uint8 *cis[SDIOD_MAX_IOFUNCS];
+		int err = 0;
+
+		numfn = bcmsdh_query_iofnum(sdh);
+		ASSERT(numfn <= SDIOD_MAX_IOFUNCS);
+
+		/* Make sure ALP is available before trying to read CIS */
+		SPINWAIT(((clkctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
+		                                    SBSDIO_FUNC1_CHIPCLKCSR, NULL)),
+		          !SBSDIO_ALPAV(clkctl)), PMU_MAX_TRANSITION_DLY);
+
+		/* Now request ALP be put on the bus */
+		bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
+		                 DHD_INIT_CLKCTL2, &err);
+		OSL_DELAY(65);
+
+		for (fn = 0; fn <= numfn; fn++) {
+			if (!(cis[fn] = MALLOC(osh, SBSDIO_CIS_SIZE_LIMIT))) {
+				DHD_INFO(("dhdsdio_probe: fn %d cis malloc failed\n", fn));
+				break;
+			}
+			bzero(cis[fn], SBSDIO_CIS_SIZE_LIMIT);
+
+			if ((err = bcmsdh_cis_read(sdh, fn, cis[fn], SBSDIO_CIS_SIZE_LIMIT))) {
+				DHD_INFO(("dhdsdio_probe: fn %d cis read err %d\n", fn, err));
+				MFREE(osh, cis[fn], SBSDIO_CIS_SIZE_LIMIT);
+				break;
+			}
+			dhd_dump_cis(fn, cis[fn]);
+		}
+
+		while (fn-- > 0) {
+			ASSERT(cis[fn]);
+			MFREE(osh, cis[fn], SBSDIO_CIS_SIZE_LIMIT);
+		}
+
+		if (err) {
+			DHD_ERROR(("dhdsdio_probe: failure reading or parsing CIS\n"));
+			goto fail;
+		}
+	}
+#endif /* DHD_DEBUG */
+
+	/* si_attach() will provide an SI handle and scan the backplane */
+	if (!(bus->sih = si_attach((uint)devid, osh, regsva, DHD_BUS, sdh,
+	                           &bus->vars, &bus->varsz))) {
+		DHD_ERROR(("%s: si_attach failed!\n", __FUNCTION__));
+		goto fail;
+	}
+
+	bcmsdh_chipinfo(sdh, bus->sih->chip, bus->sih->chiprev);
+
+	if (!dhdsdio_chipmatch((uint16)bus->sih->chip)) {
+		DHD_ERROR(("%s: unsupported chip: 0x%04x\n",
+		           __FUNCTION__, bus->sih->chip));
+		goto fail;
+	}
+
+	si_sdiod_drive_strength_init(bus->sih, osh, dhd_sdiod_drive_strength);
+
+
+	/* Get info on the ARM and SOCRAM cores... */
+	if (!DHD_NOPMU(bus)) {
+		if ((si_setcore(bus->sih, ARM7S_CORE_ID, 0)) ||
+		    (si_setcore(bus->sih, ARMCM3_CORE_ID, 0))) {
+			bus->armrev = si_corerev(bus->sih);
+		} else {
+			DHD_ERROR(("%s: failed to find ARM core!\n", __FUNCTION__));
+			goto fail;
+		}
+		if (!(bus->orig_ramsize = si_socram_size(bus->sih))) {
+			DHD_ERROR(("%s: failed to find SOCRAM memory!\n", __FUNCTION__));
+			goto fail;
+		}
+		bus->ramsize = bus->orig_ramsize;
+		if (dhd_dongle_memsize)
+			dhd_dongle_setmemsize(bus, dhd_dongle_memsize);
+
+		DHD_ERROR(("DHD: dongle ram size is set to %d(orig %d)\n",
+			bus->ramsize, bus->orig_ramsize));
+	}
+
+	/* ...but normally deal with the SDPCMDEV core */
+	if (!(bus->regs = si_setcore(bus->sih, PCMCIA_CORE_ID, 0)) &&
+	    !(bus->regs = si_setcore(bus->sih, SDIOD_CORE_ID, 0))) {
+		DHD_ERROR(("%s: failed to find SDIODEV core!\n", __FUNCTION__));
+		goto fail;
+	}
+	bus->sdpcmrev = si_corerev(bus->sih);
+
+	/* Set core control so an SDIO reset does a backplane reset */
+	OR_REG(osh, &bus->regs->corecontrol, CC_BPRESEN);
+
+	pktq_init(&bus->txq, (PRIOMASK + 1), QLEN);
+
+	/* Locate an appropriately-aligned portion of hdrbuf */
+	bus->rxhdr = (uint8 *)ROUNDUP((uintptr)&bus->hdrbuf[0], DHD_SDALIGN);
+
+	/* Set the poll and/or interrupt flags */
+	bus->intr = (bool)dhd_intr;
+	if ((bus->poll = (bool)dhd_poll))
+		bus->pollrate = 1;
+
+	return TRUE;
+
+fail:
+	return FALSE;
+}
+
+static bool
+dhdsdio_probe_malloc(dhd_bus_t *bus, osl_t *osh, void *sdh)
+{
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+#ifndef DHD_USE_STATIC_BUF
+	if (bus->dhd->maxctl) {
+		bus->rxblen = ROUNDUP((bus->dhd->maxctl + SDPCM_HDRLEN), ALIGNMENT) + DHD_SDALIGN;
+		if (!(bus->rxbuf = MALLOC(osh, bus->rxblen))) {
+			DHD_ERROR(("%s: MALLOC of %d-byte rxbuf failed\n",
+			           __FUNCTION__, bus->rxblen));
+			goto fail;
+		}
+	}
+
+	/* Allocate buffer to receive glomed packet */
+	if (!(bus->databuf = MALLOC(osh, MAX_DATA_BUF))) {
+		DHD_ERROR(("%s: MALLOC of %d-byte databuf failed\n",
+			__FUNCTION__, MAX_DATA_BUF));
+		/* release rxbuf which was already located as above */
+		if (!bus->rxblen) MFREE(osh, bus->rxbuf, bus->rxblen);
+		goto fail;
+	}
+#else
+	if (bus->dhd->maxctl) {
+		bus->rxblen = ROUNDUP((bus->dhd->maxctl + SDPCM_HDRLEN), ALIGNMENT) + DHD_SDALIGN;
+		if (!(bus->rxbuf = dhd_os_prealloc(DHD_PREALLOC_RXBUF, bus->rxblen))) {
+			DHD_ERROR(("%s: MALLOC of %d-byte rxbuf failed\n",
+			           __FUNCTION__, bus->rxblen));
+			goto fail;
+		}
+	}
+	/* Allocate buffer to receive glomed packet */
+	if (!(bus->databuf = dhd_os_prealloc(DHD_PREALLOC_DATABUF, MAX_DATA_BUF))) {
+		DHD_ERROR(("%s: MALLOC of %d-byte databuf failed\n",
+			__FUNCTION__, MAX_DATA_BUF));
+		goto fail;
+	}
+#endif /* DHD_USE_STATIC_BUF */
+
+	/* Align the buffer */
+	if ((uintptr)bus->databuf % DHD_SDALIGN)
+		bus->dataptr = bus->databuf + (DHD_SDALIGN - ((uintptr)bus->databuf % DHD_SDALIGN));
+	else
+		bus->dataptr = bus->databuf;
+
+	return TRUE;
+
+fail:
+	return FALSE;
+}
+
+
+static bool
+dhdsdio_probe_init(dhd_bus_t *bus, osl_t *osh, void *sdh)
+{
+	int32 fnum;
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+#ifdef SDTEST
+	dhdsdio_pktgen_init(bus);
+#endif /* SDTEST */
+
+	/* Disable F2 to clear any intermediate frame state on the dongle */
+	bcmsdh_cfg_write(sdh, SDIO_FUNC_0, SDIOD_CCCR_IOEN, SDIO_FUNC_ENABLE_1, NULL);
+
+	bus->dhd->busstate = DHD_BUS_DOWN;
+	bus->sleeping = FALSE;
+	bus->rxflow = FALSE;
+	bus->prev_rxlim_hit = 0;
+
+
+	/* Done with backplane-dependent accesses, can drop clock... */
+	bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL);
+
+	/* ...and initialize clock/power states */
+	bus->clkstate = CLK_SDONLY;
+	bus->idletime = (int32)dhd_idletime;
+	bus->idleclock = DHD_IDLE_ACTIVE;
+
+	/* Query the SD clock speed */
+	if (bcmsdh_iovar_op(sdh, "sd_divisor", NULL, 0,
+	                    &bus->sd_divisor, sizeof(int32), FALSE) != BCME_OK) {
+		DHD_ERROR(("%s: fail on %s get\n", __FUNCTION__, "sd_divisor"));
+		bus->sd_divisor = -1;
+	} else {
+		DHD_INFO(("%s: Initial value for %s is %d\n",
+		          __FUNCTION__, "sd_divisor", bus->sd_divisor));
+	}
+
+	/* Query the SD bus mode */
+	if (bcmsdh_iovar_op(sdh, "sd_mode", NULL, 0,
+	                    &bus->sd_mode, sizeof(int32), FALSE) != BCME_OK) {
+		DHD_ERROR(("%s: fail on %s get\n", __FUNCTION__, "sd_mode"));
+		bus->sd_mode = -1;
+	} else {
+		DHD_INFO(("%s: Initial value for %s is %d\n",
+		          __FUNCTION__, "sd_mode", bus->sd_mode));
+	}
+
+	/* Query the F2 block size, set roundup accordingly */
+	fnum = 2;
+	if (bcmsdh_iovar_op(sdh, "sd_blocksize", &fnum, sizeof(int32),
+	                    &bus->blocksize, sizeof(int32), FALSE) != BCME_OK) {
+		bus->blocksize = 0;
+		DHD_ERROR(("%s: fail on %s get\n", __FUNCTION__, "sd_blocksize"));
+	} else {
+		DHD_INFO(("%s: Initial value for %s is %d\n",
+		          __FUNCTION__, "sd_blocksize", bus->blocksize));
+	}
+	bus->roundup = MIN(max_roundup, bus->blocksize);
+
+	/* Query if bus module supports packet chaining, default to use if supported */
+	if (bcmsdh_iovar_op(sdh, "sd_rxchain", NULL, 0,
+	                    &bus->sd_rxchain, sizeof(int32), FALSE) != BCME_OK) {
+		bus->sd_rxchain = FALSE;
+	} else {
+		DHD_INFO(("%s: bus module (through bcmsdh API) %s chaining\n",
+		          __FUNCTION__, (bus->sd_rxchain ? "supports" : "does not support")));
+	}
+	bus->use_rxchain = (bool)bus->sd_rxchain;
+
+	return TRUE;
+}
+
+bool
+dhd_bus_download_firmware(struct dhd_bus *bus, osl_t *osh,
+                          char *fw_path, char *nv_path)
+{
+	bool ret;
+	bus->fw_path = fw_path;
+	bus->nv_path = nv_path;
+
+	ret = dhdsdio_download_firmware(bus, osh, bus->sdh);
+
+	return ret;
+}
+
+static bool
+dhdsdio_download_firmware(struct dhd_bus *bus, osl_t *osh, void *sdh)
+{
+	bool ret;
+
+	/* Download the firmware */
+	dhd_os_wake_lock(bus->dhd);
+	dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+
+	ret = _dhdsdio_download_firmware(bus) == 0;
+
+	dhdsdio_clkctl(bus, CLK_SDONLY, FALSE);
+	dhd_os_wake_unlock(bus->dhd);
+	return ret;
+}
+
+/* Detach and free everything */
+static void
+dhdsdio_release(dhd_bus_t *bus, osl_t *osh)
+{
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	if (bus) {
+		ASSERT(osh);
+
+
+		/* De-register interrupt handler */
+		bcmsdh_intr_disable(bus->sdh);
+		bcmsdh_intr_dereg(bus->sdh);
+
+		if (bus->dhd) {
+
+			dhdsdio_release_dongle(bus, osh, TRUE);
+
+			dhd_detach(bus->dhd);
+			bus->dhd = NULL;
+		}
+
+		dhdsdio_release_malloc(bus, osh);
+
+
+		MFREE(osh, bus, sizeof(dhd_bus_t));
+	}
+
+	if (osh)
+		dhd_osl_detach(osh);
+
+	DHD_TRACE(("%s: Disconnected\n", __FUNCTION__));
+}
+
+static void
+dhdsdio_release_malloc(dhd_bus_t *bus, osl_t *osh)
+{
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	if (bus->dhd && bus->dhd->dongle_reset)
+		return;
+
+	if (bus->rxbuf) {
+#ifndef DHD_USE_STATIC_BUF
+		MFREE(osh, bus->rxbuf, bus->rxblen);
+#endif
+		bus->rxctl = bus->rxbuf = NULL;
+		bus->rxlen = 0;
+	}
+
+	if (bus->databuf) {
+#ifndef DHD_USE_STATIC_BUF
+		MFREE(osh, bus->databuf, MAX_DATA_BUF);
+#endif
+		bus->databuf = NULL;
+	}
+}
+
+
+static void
+dhdsdio_release_dongle(dhd_bus_t *bus, osl_t *osh, int reset_flag)
+{
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	if ((bus->dhd && bus->dhd->dongle_reset) && reset_flag)
+		return;
+
+	if (bus->sih) {
+		dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+#if !defined(BCMLXSDMMC)
+		si_watchdog(bus->sih, 4);
+#endif /* !defined(BCMLXSDMMC) */
+		dhdsdio_clkctl(bus, CLK_NONE, FALSE);
+		si_detach(bus->sih);
+		if (bus->vars && bus->varsz)
+			MFREE(osh, bus->vars, bus->varsz);
+		bus->vars = NULL;
+	}
+
+	DHD_TRACE(("%s: Disconnected\n", __FUNCTION__));
+}
+
+static void
+dhdsdio_disconnect(void *ptr)
+{
+	dhd_bus_t *bus = (dhd_bus_t *)ptr;
+
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	if (bus) {
+		ASSERT(bus->dhd);
+		dhdsdio_release(bus, bus->dhd->osh);
+	}
+
+	DHD_TRACE(("%s: Disconnected\n", __FUNCTION__));
+}
+
+
+/* Register/Unregister functions are called by the main DHD entry
+ * point (e.g. module insertion) to link with the bus driver, in
+ * order to look for or await the device.
+ */
+
+static bcmsdh_driver_t dhd_sdio = {
+	dhdsdio_probe,
+	dhdsdio_disconnect
+};
+
+int
+dhd_bus_register(void)
+{
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	return bcmsdh_register(&dhd_sdio);
+}
+
+void
+dhd_bus_unregister(void)
+{
+	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+	bcmsdh_unregister();
+}
+
+#ifdef BCMEMBEDIMAGE
+static int
+dhdsdio_download_code_array(struct dhd_bus *bus)
+{
+	int bcmerror = -1;
+	int offset = 0;
+
+	DHD_INFO(("%s: download embedded firmware...\n", __FUNCTION__));
+
+	/* Download image */
+	while ((offset + MEMBLOCK) < sizeof(dlarray)) {
+		bcmerror = dhdsdio_membytes(bus, TRUE, offset, dlarray + offset, MEMBLOCK);
+		if (bcmerror) {
+			DHD_ERROR(("%s: error %d on writing %d membytes at 0x%08x\n",
+			        __FUNCTION__, bcmerror, MEMBLOCK, offset));
+			goto err;
+		}
+
+		offset += MEMBLOCK;
+	}
+
+	if (offset < sizeof(dlarray)) {
+		bcmerror = dhdsdio_membytes(bus, TRUE, offset,
+			dlarray + offset, sizeof(dlarray) - offset);
+		if (bcmerror) {
+			DHD_ERROR(("%s: error %d on writing %d membytes at 0x%08x\n",
+			        __FUNCTION__, bcmerror, sizeof(dlarray) - offset, offset));
+			goto err;
+		}
+	}
+
+#ifdef DHD_DEBUG
+	/* Upload and compare the downloaded code */
+	{
+		unsigned char *ularray;
+
+		ularray = MALLOC(bus->dhd->osh, bus->ramsize);
+		/* Upload image to verify downloaded contents. */
+		offset = 0;
+		memset(ularray, 0xaa, bus->ramsize);
+		while ((offset + MEMBLOCK) < sizeof(dlarray)) {
+			bcmerror = dhdsdio_membytes(bus, FALSE, offset, ularray + offset, MEMBLOCK);
+			if (bcmerror) {
+				DHD_ERROR(("%s: error %d on reading %d membytes at 0x%08x\n",
+					__FUNCTION__, bcmerror, MEMBLOCK, offset));
+				goto err;
+			}
+
+			offset += MEMBLOCK;
+		}
+
+		if (offset < sizeof(dlarray)) {
+			bcmerror = dhdsdio_membytes(bus, FALSE, offset,
+				ularray + offset, sizeof(dlarray) - offset);
+			if (bcmerror) {
+				DHD_ERROR(("%s: error %d on reading %d membytes at 0x%08x\n",
+					__FUNCTION__, bcmerror, sizeof(dlarray) - offset, offset));
+				goto err;
+			}
+		}
+
+		if (memcmp(dlarray, ularray, sizeof(dlarray))) {
+			DHD_ERROR(("%s: Downloaded image is corrupted.\n", __FUNCTION__));
+			ASSERT(0);
+			goto err;
+		} else
+			DHD_ERROR(("%s: Download, Upload and compare succeeded.\n", __FUNCTION__));
+
+		MFREE(bus->dhd->osh, ularray, bus->ramsize);
+	}
+#endif /* DHD_DEBUG */
+
+err:
+	return bcmerror;
+}
+#endif /* BCMEMBEDIMAGE */
+
+static int
+dhdsdio_download_code_file(struct dhd_bus *bus, char *fw_path)
+{
+	int bcmerror = -1;
+	int offset = 0;
+	uint len;
+	void *image = NULL;
+	uint8 *memblock = NULL, *memptr;
+
+	DHD_INFO(("%s: download firmware %s\n", __FUNCTION__, fw_path));
+
+	image = dhd_os_open_image(fw_path);
+	if (image == NULL)
+		goto err;
+
+	memptr = memblock = MALLOC(bus->dhd->osh, MEMBLOCK + DHD_SDALIGN);
+	if (memblock == NULL) {
+		DHD_ERROR(("%s: Failed to allocate memory %d bytes\n", __FUNCTION__, MEMBLOCK));
+		goto err;
+	}
+	if ((uint32)(uintptr)memblock % DHD_SDALIGN)
+		memptr += (DHD_SDALIGN - ((uint32)(uintptr)memblock % DHD_SDALIGN));
+
+	/* Download image */
+	while ((len = dhd_os_get_image_block((char*)memptr, MEMBLOCK, image))) {
+		bcmerror = dhdsdio_membytes(bus, TRUE, offset, memptr, len);
+		if (bcmerror) {
+			DHD_ERROR(("%s: error %d on writing %d membytes at 0x%08x\n",
+			        __FUNCTION__, bcmerror, MEMBLOCK, offset));
+			goto err;
+		}
+
+		offset += MEMBLOCK;
+	}
+
+err:
+	if (memblock)
+		MFREE(bus->dhd->osh, memblock, MEMBLOCK + DHD_SDALIGN);
+
+	if (image)
+		dhd_os_close_image(image);
+
+	return bcmerror;
+}
+
+/*
+ * ProcessVars:Takes a buffer of "<var>=<value>\n" lines read from a file and ending in a NUL.
+ * Removes carriage returns, empty lines, comment lines, and converts newlines to NULs.
+ * Shortens buffer as needed and pads with NULs.  End of buffer is marked by two NULs.
+*/
+
+static uint
+process_nvram_vars(char *varbuf, uint len)
+{
+	char *dp;
+	bool findNewline;
+	int column;
+	uint buf_len, n;
+
+	dp = varbuf;
+
+	findNewline = FALSE;
+	column = 0;
+
+	for (n = 0; n < len; n++) {
+		if (varbuf[n] == 0)
+			break;
+		if (varbuf[n] == '\r')
+			continue;
+		if (findNewline && varbuf[n] != '\n')
+			continue;
+		findNewline = FALSE;
+		if (varbuf[n] == '#') {
+			findNewline = TRUE;
+			continue;
+		}
+		if (varbuf[n] == '\n') {
+			if (column == 0)
+				continue;
+			*dp++ = 0;
+			column = 0;
+			continue;
+		}
+		*dp++ = varbuf[n];
+		column++;
+	}
+	buf_len = dp - varbuf;
+
+	while (dp < varbuf + n)
+		*dp++ = 0;
+
+	return buf_len;
+}
+
+/*
+	EXAMPLE: nvram_array
+	nvram_arry format:
+	name=value
+	Use carriage return at the end of each assignment, and an empty string with
+	carriage return at the end of array.
+
+	For example:
+	unsigned char  nvram_array[] = {"name1=value1\n", "name2=value2\n", "\n"};
+	Hex values start with 0x, and mac addr format: xx:xx:xx:xx:xx:xx.
+
+	Search "EXAMPLE: nvram_array" to see how the array is activated.
+*/
+
+void
+dhd_bus_set_nvram_params(struct dhd_bus * bus, const char *nvram_params)
+{
+	bus->nvram_params = nvram_params;
+}
+
+static int
+dhdsdio_download_nvram(struct dhd_bus *bus)
+{
+	int bcmerror = -1;
+	uint len;
+	void * image = NULL;
+	char * memblock = NULL;
+	char *bufp;
+	char *nv_path;
+	bool nvram_file_exists;
+
+	nv_path = bus->nv_path;
+
+	nvram_file_exists = ((nv_path != NULL) && (nv_path[0] != '\0'));
+	if (!nvram_file_exists && (bus->nvram_params == NULL))
+		return (0);
+
+	if (nvram_file_exists) {
+		image = dhd_os_open_image(nv_path);
+		if (image == NULL)
+			goto err;
+	}
+
+	memblock = MALLOC(bus->dhd->osh, MEMBLOCK);
+	if (memblock == NULL) {
+		DHD_ERROR(("%s: Failed to allocate memory %d bytes\n",
+		           __FUNCTION__, MEMBLOCK));
+		goto err;
+	}
+
+	/* Download variables */
+	if (nvram_file_exists) {
+		len = dhd_os_get_image_block(memblock, MEMBLOCK, image);
+	}
+	else {
+		len = strlen(bus->nvram_params);
+		ASSERT(len <= MEMBLOCK);
+		if (len > MEMBLOCK)
+			len = MEMBLOCK;
+		memcpy(memblock, bus->nvram_params, len);
+	}
+
+	if (len > 0 && len < MEMBLOCK) {
+		bufp = (char *)memblock;
+		bufp[len] = 0;
+		len = process_nvram_vars(bufp, len);
+		bufp += len;
+		*bufp++ = 0;
+		if (len)
+			bcmerror = dhdsdio_downloadvars(bus, memblock, len + 1);
+		if (bcmerror) {
+			DHD_ERROR(("%s: error downloading vars: %d\n",
+			           __FUNCTION__, bcmerror));
+		}
+	}
+	else {
+		DHD_ERROR(("%s: error reading nvram file: %d\n",
+		           __FUNCTION__, len));
+		bcmerror = BCME_SDIO_ERROR;
+	}
+
+err:
+	if (memblock)
+		MFREE(bus->dhd->osh, memblock, MEMBLOCK);
+
+	if (image)
+		dhd_os_close_image(image);
+
+	return bcmerror;
+}
+
+static int
+_dhdsdio_download_firmware(struct dhd_bus *bus)
+{
+	int bcmerror = -1;
+
+	bool embed = FALSE;	/* download embedded firmware */
+	bool dlok = FALSE;	/* download firmware succeeded */
+
+	/* Out immediately if no image to download */
+	if ((bus->fw_path == NULL) || (bus->fw_path[0] == '\0')) {
+#ifdef BCMEMBEDIMAGE
+		embed = TRUE;
+#else
+		return bcmerror;
+#endif
+	}
+
+	/* Keep arm in reset */
+	if (dhdsdio_download_state(bus, TRUE)) {
+		DHD_ERROR(("%s: error placing ARM core in reset\n", __FUNCTION__));
+		goto err;
+	}
+
+	/* External image takes precedence if specified */
+	if ((bus->fw_path != NULL) && (bus->fw_path[0] != '\0')) {
+		if (dhdsdio_download_code_file(bus, bus->fw_path)) {
+			DHD_ERROR(("%s: dongle image file download failed\n", __FUNCTION__));
+#ifdef BCMEMBEDIMAGE
+			embed = TRUE;
+#else
+			goto err;
+#endif
+		}
+		else {
+			embed = FALSE;
+			dlok = TRUE;
+		}
+	}
+#ifdef BCMEMBEDIMAGE
+	if (embed) {
+		if (dhdsdio_download_code_array(bus)) {
+			DHD_ERROR(("%s: dongle image array download failed\n", __FUNCTION__));
+			goto err;
+		}
+		else {
+			dlok = TRUE;
+		}
+	}
+#endif
+	if (!dlok) {
+		DHD_ERROR(("%s: dongle image download failed\n", __FUNCTION__));
+		goto err;
+	}
+
+	/* EXAMPLE: nvram_array */
+	/* If a valid nvram_arry is specified as above, it can be passed down to dongle */
+	/* dhd_bus_set_nvram_params(bus, (char *)&nvram_array); */
+
+	/* External nvram takes precedence if specified */
+	if (dhdsdio_download_nvram(bus)) {
+		DHD_ERROR(("%s: dongle nvram file download failed\n", __FUNCTION__));
+	}
+
+	/* Take arm out of reset */
+	if (dhdsdio_download_state(bus, FALSE)) {
+		DHD_ERROR(("%s: error getting out of ARM core reset\n", __FUNCTION__));
+		goto err;
+	}
+
+	bcmerror = 0;
+
+err:
+	return bcmerror;
+}
+
+static int
+dhd_bcmsdh_recv_buf(dhd_bus_t *bus, uint32 addr, uint fn, uint flags, uint8 *buf, uint nbytes,
+	void *pkt, bcmsdh_cmplt_fn_t complete, void *handle)
+{
+	int status;
+
+	/* 4329: GSPI check */
+	status = bcmsdh_recv_buf(bus->sdh, addr, fn, flags, buf, nbytes, pkt, complete, handle);
+	return status;
+}
+
+static int
+dhd_bcmsdh_send_buf(dhd_bus_t *bus, uint32 addr, uint fn, uint flags, uint8 *buf, uint nbytes,
+	void *pkt, bcmsdh_cmplt_fn_t complete, void *handle)
+{
+	return (bcmsdh_send_buf(bus->sdh, addr, fn, flags, buf, nbytes, pkt, complete, handle));
+}
+
+uint
+dhd_bus_chip(struct dhd_bus *bus)
+{
+	ASSERT(bus->sih != NULL);
+	return bus->sih->chip;
+}
+
+void *
+dhd_bus_pub(struct dhd_bus *bus)
+{
+	return bus->dhd;
+}
+
+void *
+dhd_bus_txq(struct dhd_bus *bus)
+{
+	return &bus->txq;
+}
+
+uint
+dhd_bus_hdrlen(struct dhd_bus *bus)
+{
+	return SDPCM_HDRLEN;
+}
+
+int
+dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag)
+{
+	int bcmerror = 0;
+	dhd_bus_t *bus;
+
+	bus = dhdp->bus;
+
+	if (flag == TRUE) {
+		if (!bus->dhd->dongle_reset) {
+			dhd_os_sdlock(dhdp);
+			/* Turning off watchdog */
+			dhd_os_wd_timer(dhdp, 0);
+#if !defined(IGNORE_ETH0_DOWN)
+			/* Force flow control as protection when stop come before ifconfig_down */
+			dhd_txflowcontrol(bus->dhd, 0, ON);
+#endif /* !defined(IGNORE_ETH0_DOWN) */
+			/* Expect app to have torn down any connection before calling */
+			/* Stop the bus, disable F2 */
+			dhd_bus_stop(bus, FALSE);
+#if defined(OOB_INTR_ONLY)
+			bcmsdh_set_irq(FALSE);
+#endif /* defined(OOB_INTR_ONLY) */
+			/* Clean tx/rx buffer pointers, detach from the dongle */
+			dhdsdio_release_dongle(bus, bus->dhd->osh, TRUE);
+
+			bus->dhd->dongle_reset = TRUE;
+			bus->dhd->up = FALSE;
+			dhd_os_sdunlock(dhdp);
+
+			DHD_TRACE(("%s:  WLAN OFF DONE\n", __FUNCTION__));
+			/* App can now remove power from device */
+		} else
+			bcmerror = BCME_SDIO_ERROR;
+	} else {
+		/* App must have restored power to device before calling */
+
+		DHD_TRACE(("\n\n%s: == WLAN ON ==\n", __FUNCTION__));
+
+		if (bus->dhd->dongle_reset) {
+			/* Turn on WLAN */
+			dhd_os_sdlock(dhdp);
+
+			/* Reset SD client */
+			bcmsdh_reset(bus->sdh);
+
+			/* Attempt to re-attach & download */
+			if (dhdsdio_probe_attach(bus, bus->dhd->osh, bus->sdh,
+			                        (uint32 *)SI_ENUM_BASE,
+			                        bus->cl_devid)) {
+				/* Attempt to download binary to the dongle */
+				if (dhdsdio_probe_init(bus, bus->dhd->osh, bus->sdh) &&
+					dhdsdio_download_firmware(bus, bus->dhd->osh, bus->sdh)) {
+
+					/* Re-init bus, enable F2 transfer */
+					bcmerror = dhd_bus_init((dhd_pub_t *) bus->dhd, FALSE);
+					if (bcmerror == BCME_OK) {
+#if defined(OOB_INTR_ONLY)
+						bcmsdh_set_irq(TRUE);
+						dhd_enable_oob_intr(bus, TRUE);
+#endif /* defined(OOB_INTR_ONLY) */
+						bus->dhd->dongle_reset = FALSE;
+						bus->dhd->up = TRUE;
+#if !defined(IGNORE_ETH0_DOWN)
+						/* Restore flow control  */
+						dhd_txflowcontrol(bus->dhd, 0, OFF);
+#endif
+						/* Turning on watchdog back */
+						dhd_os_wd_timer(dhdp, dhd_watchdog_ms);
+
+						DHD_TRACE(("%s: WLAN ON DONE\n", __FUNCTION__));
+					} else {
+						dhd_bus_stop(bus, FALSE);
+						dhdsdio_release_dongle(bus, bus->dhd->osh, FALSE);
+					}
+				} else
+					bcmerror = BCME_SDIO_ERROR;
+			} else
+				bcmerror = BCME_SDIO_ERROR;
+
+			dhd_os_sdunlock(dhdp);
+		} else {
+			bcmerror = BCME_NOTDOWN;
+			DHD_ERROR(("%s: Set DEVRESET=FALSE invoked when device is on\n",
+				__FUNCTION__));
+			bcmerror = BCME_SDIO_ERROR;
+		}
+	}
+	return bcmerror;
+}
diff --git a/drivers/net/wireless/bcm4329/dngl_stats.h b/drivers/net/wireless/bcm4329/dngl_stats.h
new file mode 100644
index 0000000..e5db54e
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/dngl_stats.h
@@ -0,0 +1,43 @@
+/*
+ * Common stats definitions for clients of dongle
+ * ports
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: dngl_stats.h,v 1.2.140.3 2008/05/26 16:52:08 Exp $
+ */
+
+#ifndef _dngl_stats_h_
+#define _dngl_stats_h_
+
+typedef struct {
+	unsigned long	rx_packets;		/* total packets received */
+	unsigned long	tx_packets;		/* total packets transmitted */
+	unsigned long	rx_bytes;		/* total bytes received */
+	unsigned long	tx_bytes;		/* total bytes transmitted */
+	unsigned long	rx_errors;		/* bad packets received */
+	unsigned long	tx_errors;		/* packet transmit problems */
+	unsigned long	rx_dropped;		/* packets dropped by dongle */
+	unsigned long	tx_dropped;		/* packets dropped by dongle */
+	unsigned long   multicast;      /* multicast packets received */
+} dngl_stats_t;
+
+#endif /* _dngl_stats_h_ */
diff --git a/drivers/net/wireless/bcm4329/hndpmu.c b/drivers/net/wireless/bcm4329/hndpmu.c
new file mode 100644
index 0000000..307347a
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/hndpmu.c
@@ -0,0 +1,131 @@
+/*
+ * Misc utility routines for accessing PMU corerev specific features
+ * of the SiliconBackplane-based Broadcom chips.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: hndpmu.c,v 1.95.2.17.4.11.2.63 2010/07/21 13:55:09 Exp $
+ */
+
+#include <typedefs.h>
+#include <bcmdefs.h>
+#include <osl.h>
+#include <bcmutils.h>
+#include <siutils.h>
+#include <bcmdevs.h>
+#include <hndsoc.h>
+#include <sbchipc.h>
+#include <hndpmu.h>
+
+/* debug/trace */
+#define	PMU_ERROR(args)
+
+#define	PMU_MSG(args)
+
+
+/* SDIO Pad drive strength to select value mappings */
+typedef struct {
+	uint8 strength;			/* Pad Drive Strength in mA */
+	uint8 sel;			/* Chip-specific select value */
+} sdiod_drive_str_t;
+
+/* SDIO Drive Strength to sel value table for PMU Rev 1 */
+static const sdiod_drive_str_t sdiod_drive_strength_tab1[] = {
+	{4, 0x2},
+	{2, 0x3},
+	{1, 0x0},
+	{0, 0x0} };
+
+/* SDIO Drive Strength to sel value table for PMU Rev 2, 3 */
+static const sdiod_drive_str_t sdiod_drive_strength_tab2[] = {
+	{12, 0x7},
+	{10, 0x6},
+	{8, 0x5},
+	{6, 0x4},
+	{4, 0x2},
+	{2, 0x1},
+	{0, 0x0} };
+
+#define SDIOD_DRVSTR_KEY(chip, pmu)	(((chip) << 16) | (pmu))
+
+void
+si_sdiod_drive_strength_init(si_t *sih, osl_t *osh, uint32 drivestrength)
+{
+	chipcregs_t *cc;
+	uint origidx, intr_val = 0;
+	sdiod_drive_str_t *str_tab = NULL;
+	uint32 str_mask = 0;
+	uint32 str_shift = 0;
+
+	if (!(sih->cccaps & CC_CAP_PMU)) {
+		return;
+	}
+
+	/* Remember original core before switch to chipc */
+	cc = (chipcregs_t *) si_switch_core(sih, CC_CORE_ID, &origidx, &intr_val);
+
+	switch (SDIOD_DRVSTR_KEY(sih->chip, sih->pmurev)) {
+	case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 1):
+		str_tab = (sdiod_drive_str_t *)&sdiod_drive_strength_tab1;
+		str_mask = 0x30000000;
+		str_shift = 28;
+		break;
+	case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 2):
+	case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 3):
+	case SDIOD_DRVSTR_KEY(BCM4315_CHIP_ID, 4):
+		str_tab = (sdiod_drive_str_t *)&sdiod_drive_strength_tab2;
+		str_mask = 0x00003800;
+		str_shift = 11;
+		break;
+
+	default:
+		PMU_MSG(("No SDIO Drive strength init done for chip %x rev %d pmurev %d\n",
+		         sih->chip, sih->chiprev, sih->pmurev));
+
+		break;
+	}
+
+	if (str_tab != NULL) {
+		uint32 drivestrength_sel = 0;
+		uint32 cc_data_temp;
+		int i;
+
+		for (i = 0; str_tab[i].strength != 0; i ++) {
+			if (drivestrength >= str_tab[i].strength) {
+				drivestrength_sel = str_tab[i].sel;
+				break;
+			}
+		}
+
+		W_REG(osh, &cc->chipcontrol_addr, 1);
+		cc_data_temp = R_REG(osh, &cc->chipcontrol_data);
+		cc_data_temp &= ~str_mask;
+		drivestrength_sel <<= str_shift;
+		cc_data_temp |= drivestrength_sel;
+		W_REG(osh, &cc->chipcontrol_data, cc_data_temp);
+
+		PMU_MSG(("SDIO: %dmA drive strength selected, set to 0x%08x\n",
+		         drivestrength, cc_data_temp));
+	}
+
+	/* Return to original core */
+	si_restore_core(sih, origidx, intr_val);
+}
diff --git a/drivers/net/wireless/bcm4329/include/Makefile b/drivers/net/wireless/bcm4329/include/Makefile
new file mode 100644
index 0000000..439ead1
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/Makefile
@@ -0,0 +1,21 @@
+#
+# include/Makefile
+#
+# Copyright 2005, Broadcom, Inc.
+#
+# $Id: Makefile,v 13.5 2005/02/17 19:11:31 Exp $
+#
+
+SRCBASE	= ..
+
+TARGETS	= epivers.h
+
+
+all release:
+	bash epivers.sh
+
+clean:
+	rm -rf ${TARGETS} *.prev
+
+
+.PHONY: all release clean
diff --git a/drivers/net/wireless/bcm4329/include/aidmp.h b/drivers/net/wireless/bcm4329/include/aidmp.h
new file mode 100644
index 0000000..a927e5d
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/aidmp.h
@@ -0,0 +1,368 @@
+/*
+ * Broadcom AMBA Interconnect definitions.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: aidmp.h,v 13.2.10.1 2008/05/07 20:32:12 Exp $
+ */
+
+
+#ifndef	_AIDMP_H
+#define	_AIDMP_H
+
+
+#define	MFGID_ARM		0x43b
+#define	MFGID_BRCM		0x4bf
+#define	MFGID_MIPS		0x4a7
+
+
+#define	CC_SIM			0
+#define	CC_EROM			1
+#define	CC_CORESIGHT		9
+#define	CC_VERIF		0xb
+#define	CC_OPTIMO		0xd
+#define	CC_GEN			0xe
+#define	CC_PRIMECELL		0xf
+
+
+#define	ER_EROMENTRY		0x000
+#define	ER_REMAPCONTROL		0xe00
+#define	ER_REMAPSELECT		0xe04
+#define	ER_MASTERSELECT		0xe10
+#define	ER_ITCR			0xf00
+#define	ER_ITIP			0xf04
+
+
+#define	ER_TAG			0xe
+#define	ER_TAG1			0x6
+#define	ER_VALID		1
+#define	ER_CI			0
+#define	ER_MP			2
+#define	ER_ADD			4
+#define	ER_END			0xe
+#define	ER_BAD			0xffffffff
+
+
+#define	CIA_MFG_MASK		0xfff00000
+#define	CIA_MFG_SHIFT		20
+#define	CIA_CID_MASK		0x000fff00
+#define	CIA_CID_SHIFT		8
+#define	CIA_CCL_MASK		0x000000f0
+#define	CIA_CCL_SHIFT		4
+
+
+#define	CIB_REV_MASK		0xff000000
+#define	CIB_REV_SHIFT		24
+#define	CIB_NSW_MASK		0x00f80000
+#define	CIB_NSW_SHIFT		19
+#define	CIB_NMW_MASK		0x0007c000
+#define	CIB_NMW_SHIFT		14
+#define	CIB_NSP_MASK		0x00003e00
+#define	CIB_NSP_SHIFT		9
+#define	CIB_NMP_MASK		0x000001f0
+#define	CIB_NMP_SHIFT		4
+
+
+#define	MPD_MUI_MASK		0x0000ff00
+#define	MPD_MUI_SHIFT		8
+#define	MPD_MP_MASK		0x000000f0
+#define	MPD_MP_SHIFT		4
+
+
+#define	AD_ADDR_MASK		0xfffff000
+#define	AD_SP_MASK		0x00000f00
+#define	AD_SP_SHIFT		8
+#define	AD_ST_MASK		0x000000c0
+#define	AD_ST_SHIFT		6
+#define	AD_ST_SLAVE		0x00000000
+#define	AD_ST_BRIDGE		0x00000040
+#define	AD_ST_SWRAP		0x00000080
+#define	AD_ST_MWRAP		0x000000c0
+#define	AD_SZ_MASK		0x00000030
+#define	AD_SZ_SHIFT		4
+#define	AD_SZ_4K		0x00000000
+#define	AD_SZ_8K		0x00000010
+#define	AD_SZ_16K		0x00000020
+#define	AD_SZ_SZD		0x00000030
+#define	AD_AG32			0x00000008
+#define	AD_ADDR_ALIGN		0x00000fff
+#define	AD_SZ_BASE		0x00001000	
+
+
+#define	SD_SZ_MASK		0xfffff000
+#define	SD_SG32			0x00000008
+#define	SD_SZ_ALIGN		0x00000fff
+
+
+#ifndef _LANGUAGE_ASSEMBLY
+
+typedef volatile struct _aidmp {
+	uint32	oobselina30;	
+	uint32	oobselina74;	
+	uint32	PAD[6];
+	uint32	oobselinb30;	
+	uint32	oobselinb74;	
+	uint32	PAD[6];
+	uint32	oobselinc30;	
+	uint32	oobselinc74;	
+	uint32	PAD[6];
+	uint32	oobselind30;	
+	uint32	oobselind74;	
+	uint32	PAD[38];
+	uint32	oobselouta30;	
+	uint32	oobselouta74;	
+	uint32	PAD[6];
+	uint32	oobseloutb30;	
+	uint32	oobseloutb74;	
+	uint32	PAD[6];
+	uint32	oobseloutc30;	
+	uint32	oobseloutc74;	
+	uint32	PAD[6];
+	uint32	oobseloutd30;	
+	uint32	oobseloutd74;	
+	uint32	PAD[38];
+	uint32	oobsynca;	
+	uint32	oobseloutaen;	
+	uint32	PAD[6];
+	uint32	oobsyncb;	
+	uint32	oobseloutben;	
+	uint32	PAD[6];
+	uint32	oobsyncc;	
+	uint32	oobseloutcen;	
+	uint32	PAD[6];
+	uint32	oobsyncd;	
+	uint32	oobseloutden;	
+	uint32	PAD[38];
+	uint32	oobaextwidth;	
+	uint32	oobainwidth;	
+	uint32	oobaoutwidth;	
+	uint32	PAD[5];
+	uint32	oobbextwidth;	
+	uint32	oobbinwidth;	
+	uint32	oobboutwidth;	
+	uint32	PAD[5];
+	uint32	oobcextwidth;	
+	uint32	oobcinwidth;	
+	uint32	oobcoutwidth;	
+	uint32	PAD[5];
+	uint32	oobdextwidth;	
+	uint32	oobdinwidth;	
+	uint32	oobdoutwidth;	
+	uint32	PAD[37];
+	uint32	ioctrlset;	
+	uint32	ioctrlclear;	
+	uint32	ioctrl;		
+	uint32	PAD[61];
+	uint32	iostatus;	
+	uint32	PAD[127];
+	uint32	ioctrlwidth;	
+	uint32	iostatuswidth;	
+	uint32	PAD[62];
+	uint32	resetctrl;	
+	uint32	resetstatus;	
+	uint32	resetreadid;	
+	uint32	resetwriteid;	
+	uint32	PAD[60];
+	uint32	errlogctrl;	
+	uint32	errlogdone;	
+	uint32	errlogstatus;	
+	uint32	errlogaddrlo;	
+	uint32	errlogaddrhi;	
+	uint32	errlogid;	
+	uint32	errloguser;	
+	uint32	errlogflags;	
+	uint32	PAD[56];
+	uint32	intstatus;	
+	uint32	PAD[127];
+	uint32	config;		
+	uint32	PAD[63];
+	uint32	itcr;		
+	uint32	PAD[3];
+	uint32	itipooba;	
+	uint32	itipoobb;	
+	uint32	itipoobc;	
+	uint32	itipoobd;	
+	uint32	PAD[4];
+	uint32	itipoobaout;	
+	uint32	itipoobbout;	
+	uint32	itipoobcout;	
+	uint32	itipoobdout;	
+	uint32	PAD[4];
+	uint32	itopooba;	
+	uint32	itopoobb;	
+	uint32	itopoobc;	
+	uint32	itopoobd;	
+	uint32	PAD[4];
+	uint32	itopoobain;	
+	uint32	itopoobbin;	
+	uint32	itopoobcin;	
+	uint32	itopoobdin;	
+	uint32	PAD[4];
+	uint32	itopreset;	
+	uint32	PAD[15];
+	uint32	peripherialid4;	
+	uint32	peripherialid5;	
+	uint32	peripherialid6;	
+	uint32	peripherialid7;	
+	uint32	peripherialid0;	
+	uint32	peripherialid1;	
+	uint32	peripherialid2;	
+	uint32	peripherialid3;	
+	uint32	componentid0;	
+	uint32	componentid1;	
+	uint32	componentid2;	
+	uint32	componentid3;	
+} aidmp_t;
+
+#endif 
+
+
+#define	OOB_BUSCONFIG		0x020
+#define	OOB_STATUSA		0x100
+#define	OOB_STATUSB		0x104
+#define	OOB_STATUSC		0x108
+#define	OOB_STATUSD		0x10c
+#define	OOB_ENABLEA0		0x200
+#define	OOB_ENABLEA1		0x204
+#define	OOB_ENABLEA2		0x208
+#define	OOB_ENABLEA3		0x20c
+#define	OOB_ENABLEB0		0x280
+#define	OOB_ENABLEB1		0x284
+#define	OOB_ENABLEB2		0x288
+#define	OOB_ENABLEB3		0x28c
+#define	OOB_ENABLEC0		0x300
+#define	OOB_ENABLEC1		0x304
+#define	OOB_ENABLEC2		0x308
+#define	OOB_ENABLEC3		0x30c
+#define	OOB_ENABLED0		0x380
+#define	OOB_ENABLED1		0x384
+#define	OOB_ENABLED2		0x388
+#define	OOB_ENABLED3		0x38c
+#define	OOB_ITCR		0xf00
+#define	OOB_ITIPOOBA		0xf10
+#define	OOB_ITIPOOBB		0xf14
+#define	OOB_ITIPOOBC		0xf18
+#define	OOB_ITIPOOBD		0xf1c
+#define	OOB_ITOPOOBA		0xf30
+#define	OOB_ITOPOOBB		0xf34
+#define	OOB_ITOPOOBC		0xf38
+#define	OOB_ITOPOOBD		0xf3c
+
+
+#define	AI_OOBSELINA30		0x000
+#define	AI_OOBSELINA74		0x004
+#define	AI_OOBSELINB30		0x020
+#define	AI_OOBSELINB74		0x024
+#define	AI_OOBSELINC30		0x040
+#define	AI_OOBSELINC74		0x044
+#define	AI_OOBSELIND30		0x060
+#define	AI_OOBSELIND74		0x064
+#define	AI_OOBSELOUTA30		0x100
+#define	AI_OOBSELOUTA74		0x104
+#define	AI_OOBSELOUTB30		0x120
+#define	AI_OOBSELOUTB74		0x124
+#define	AI_OOBSELOUTC30		0x140
+#define	AI_OOBSELOUTC74		0x144
+#define	AI_OOBSELOUTD30		0x160
+#define	AI_OOBSELOUTD74		0x164
+#define	AI_OOBSYNCA		0x200
+#define	AI_OOBSELOUTAEN		0x204
+#define	AI_OOBSYNCB		0x220
+#define	AI_OOBSELOUTBEN		0x224
+#define	AI_OOBSYNCC		0x240
+#define	AI_OOBSELOUTCEN		0x244
+#define	AI_OOBSYNCD		0x260
+#define	AI_OOBSELOUTDEN		0x264
+#define	AI_OOBAEXTWIDTH		0x300
+#define	AI_OOBAINWIDTH		0x304
+#define	AI_OOBAOUTWIDTH		0x308
+#define	AI_OOBBEXTWIDTH		0x320
+#define	AI_OOBBINWIDTH		0x324
+#define	AI_OOBBOUTWIDTH		0x328
+#define	AI_OOBCEXTWIDTH		0x340
+#define	AI_OOBCINWIDTH		0x344
+#define	AI_OOBCOUTWIDTH		0x348
+#define	AI_OOBDEXTWIDTH		0x360
+#define	AI_OOBDINWIDTH		0x364
+#define	AI_OOBDOUTWIDTH		0x368
+#define	AI_IOCTRLSET		0x400
+#define	AI_IOCTRLCLEAR		0x404
+#define	AI_IOCTRL		0x408
+#define	AI_IOSTATUS		0x500
+#define	AI_IOCTRLWIDTH		0x700
+#define	AI_IOSTATUSWIDTH	0x704
+#define	AI_RESETCTRL		0x800
+#define	AI_RESETSTATUS		0x804
+#define	AI_RESETREADID		0x808
+#define	AI_RESETWRITEID		0x80c
+#define	AI_ERRLOGCTRL		0xa00
+#define	AI_ERRLOGDONE		0xa04
+#define	AI_ERRLOGSTATUS		0xa08
+#define	AI_ERRLOGADDRLO		0xa0c
+#define	AI_ERRLOGADDRHI		0xa10
+#define	AI_ERRLOGID		0xa14
+#define	AI_ERRLOGUSER		0xa18
+#define	AI_ERRLOGFLAGS		0xa1c
+#define	AI_INTSTATUS		0xa00
+#define	AI_CONFIG		0xe00
+#define	AI_ITCR			0xf00
+#define	AI_ITIPOOBA		0xf10
+#define	AI_ITIPOOBB		0xf14
+#define	AI_ITIPOOBC		0xf18
+#define	AI_ITIPOOBD		0xf1c
+#define	AI_ITIPOOBAOUT		0xf30
+#define	AI_ITIPOOBBOUT		0xf34
+#define	AI_ITIPOOBCOUT		0xf38
+#define	AI_ITIPOOBDOUT		0xf3c
+#define	AI_ITOPOOBA		0xf50
+#define	AI_ITOPOOBB		0xf54
+#define	AI_ITOPOOBC		0xf58
+#define	AI_ITOPOOBD		0xf5c
+#define	AI_ITOPOOBAIN		0xf70
+#define	AI_ITOPOOBBIN		0xf74
+#define	AI_ITOPOOBCIN		0xf78
+#define	AI_ITOPOOBDIN		0xf7c
+#define	AI_ITOPRESET		0xf90
+#define	AI_PERIPHERIALID4	0xfd0
+#define	AI_PERIPHERIALID5	0xfd4
+#define	AI_PERIPHERIALID6	0xfd8
+#define	AI_PERIPHERIALID7	0xfdc
+#define	AI_PERIPHERIALID0	0xfe0
+#define	AI_PERIPHERIALID1	0xfe4
+#define	AI_PERIPHERIALID2	0xfe8
+#define	AI_PERIPHERIALID3	0xfec
+#define	AI_COMPONENTID0		0xff0
+#define	AI_COMPONENTID1		0xff4
+#define	AI_COMPONENTID2		0xff8
+#define	AI_COMPONENTID3		0xffc
+
+
+#define	AIRC_RESET		1
+
+
+#define	AICFG_OOB		0x00000020
+#define	AICFG_IOS		0x00000010
+#define	AICFG_IOC		0x00000008
+#define	AICFG_TO		0x00000004
+#define	AICFG_ERRL		0x00000002
+#define	AICFG_RST		0x00000001
+
+#endif	
diff --git a/drivers/net/wireless/bcm4329/include/bcmcdc.h b/drivers/net/wireless/bcm4329/include/bcmcdc.h
new file mode 100644
index 0000000..c2a860b
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/bcmcdc.h
@@ -0,0 +1,100 @@
+/*
+ * CDC network driver ioctl/indication encoding
+ * Broadcom 802.11abg Networking Device Driver
+ *
+ * Definitions subject to change without notice.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmcdc.h,v 13.14.16.3.16.4 2009/04/12 16:58:45 Exp $
+ */
+#include <proto/ethernet.h>
+
+typedef struct cdc_ioctl {
+	uint32 cmd;      /* ioctl command value */
+	uint32 len;      /* lower 16: output buflen; upper 16: input buflen (excludes header) */
+	uint32 flags;    /* flag defns given below */
+	uint32 status;   /* status code returned from the device */
+} cdc_ioctl_t;
+
+/* Max valid buffer size that can be sent to the dongle */
+#define CDC_MAX_MSG_SIZE   ETHER_MAX_LEN
+
+/* len field is divided into input and output buffer lengths */
+#define CDCL_IOC_OUTLEN_MASK   0x0000FFFF  /* maximum or expected response length, */
+					   /* excluding IOCTL header */
+#define CDCL_IOC_OUTLEN_SHIFT  0
+#define CDCL_IOC_INLEN_MASK    0xFFFF0000   /* input buffer length, excluding IOCTL header */
+#define CDCL_IOC_INLEN_SHIFT   16
+
+/* CDC flag definitions */
+#define CDCF_IOC_ERROR		0x01	/* 0=success, 1=ioctl cmd failed */
+#define CDCF_IOC_SET		0x02	/* 0=get, 1=set cmd */
+#define CDCF_IOC_IF_MASK	0xF000	/* I/F index */
+#define CDCF_IOC_IF_SHIFT	12
+#define CDCF_IOC_ID_MASK	0xFFFF0000	/* used to uniquely id an ioctl req/resp pairing */
+#define CDCF_IOC_ID_SHIFT	16		/* # of bits of shift for ID Mask */
+
+#define CDC_IOC_IF_IDX(flags)	(((flags) & CDCF_IOC_IF_MASK) >> CDCF_IOC_IF_SHIFT)
+#define CDC_IOC_ID(flags)	(((flags) & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT)
+
+#define CDC_GET_IF_IDX(hdr) \
+	((int)((((hdr)->flags) & CDCF_IOC_IF_MASK) >> CDCF_IOC_IF_SHIFT))
+#define CDC_SET_IF_IDX(hdr, idx) \
+	((hdr)->flags = (((hdr)->flags & ~CDCF_IOC_IF_MASK) | ((idx) << CDCF_IOC_IF_SHIFT)))
+
+/*
+ * BDC header
+ *
+ *   The BDC header is used on data packets to convey priority across USB.
+ */
+
+#define	BDC_HEADER_LEN		4
+
+#define BDC_PROTO_VER		1	/* Protocol version */
+
+#define BDC_FLAG_VER_MASK	0xf0	/* Protocol version mask */
+#define BDC_FLAG_VER_SHIFT	4	/* Protocol version shift */
+
+#define BDC_FLAG__UNUSED	0x03	/* Unassigned */
+#define BDC_FLAG_SUM_GOOD	0x04	/* Dongle has verified good RX checksums */
+#define BDC_FLAG_SUM_NEEDED	0x08	/* Dongle needs to do TX checksums */
+
+#define BDC_PRIORITY_MASK	0x7
+
+#define BDC_FLAG2_FC_FLAG	0x10	/* flag to indicate if pkt contains */
+									/* FLOW CONTROL info only */
+#define BDC_PRIORITY_FC_SHIFT	4		/* flow control info shift */
+
+#define BDC_FLAG2_IF_MASK	0x0f	/* APSTA: interface on which the packet was received */
+#define BDC_FLAG2_IF_SHIFT	0
+
+#define BDC_GET_IF_IDX(hdr) \
+	((int)((((hdr)->flags2) & BDC_FLAG2_IF_MASK) >> BDC_FLAG2_IF_SHIFT))
+#define BDC_SET_IF_IDX(hdr, idx) \
+	((hdr)->flags2 = (((hdr)->flags2 & ~BDC_FLAG2_IF_MASK) | ((idx) << BDC_FLAG2_IF_SHIFT)))
+
+struct bdc_header {
+	uint8	flags;			/* Flags */
+	uint8	priority;	/* 802.1d Priority 0:2 bits, 4:7 flow control info for usb */
+	uint8	flags2;
+	uint8	rssi;
+};
diff --git a/drivers/net/wireless/bcm4329/include/bcmdefs.h b/drivers/net/wireless/bcm4329/include/bcmdefs.h
new file mode 100644
index 0000000..f4e9946
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/bcmdefs.h
@@ -0,0 +1,114 @@
+/*
+ * Misc system wide definitions
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ * $Id: bcmdefs.h,v 13.38.4.10.2.7.6.11 2010/02/01 05:51:55 Exp $
+ */
+
+
+#ifndef	_bcmdefs_h_
+#define	_bcmdefs_h_
+
+#define STATIC	static
+
+#define	SI_BUS			0	
+#define	PCI_BUS			1	
+#define	PCMCIA_BUS		2	
+#define SDIO_BUS		3	
+#define JTAG_BUS		4	
+#define USB_BUS			5	
+#define SPI_BUS			6	
+
+
+#ifdef BCMBUSTYPE
+#define BUSTYPE(bus) 	(BCMBUSTYPE)
+#else
+#define BUSTYPE(bus) 	(bus)
+#endif
+
+
+#ifdef BCMCHIPTYPE
+#define CHIPTYPE(bus) 	(BCMCHIPTYPE)
+#else
+#define CHIPTYPE(bus) 	(bus)
+#endif
+
+
+
+#if defined(BCMSPROMBUS)
+#define SPROMBUS	(BCMSPROMBUS)
+#elif defined(SI_PCMCIA_SROM)
+#define SPROMBUS	(PCMCIA_BUS)
+#else
+#define SPROMBUS	(PCI_BUS)
+#endif
+
+
+#ifdef BCMCHIPID
+#define CHIPID(chip)	(BCMCHIPID)
+#else
+#define CHIPID(chip)	(chip)
+#endif
+
+
+#define DMADDR_MASK_32 0x0		
+#define DMADDR_MASK_30 0xc0000000	
+#define DMADDR_MASK_0  0xffffffff	
+
+#define	DMADDRWIDTH_30  30 
+#define	DMADDRWIDTH_32  32 
+#define	DMADDRWIDTH_63  63 
+#define	DMADDRWIDTH_64  64 
+
+
+#define BCMEXTRAHDROOM 164
+
+
+#define BCMDONGLEHDRSZ 12
+#define BCMDONGLEPADSZ 16
+
+#define BCMDONGLEOVERHEAD	(BCMDONGLEHDRSZ + BCMDONGLEPADSZ)
+
+
+
+#define BITFIELD_MASK(width) \
+		(((unsigned)1 << (width)) - 1)
+#define GFIELD(val, field) \
+		(((val) >> field ## _S) & field ## _M)
+#define SFIELD(val, field, bits) \
+		(((val) & (~(field ## _M << field ## _S))) | \
+		 ((unsigned)(bits) << field ## _S))
+
+
+#ifdef BCMSMALL
+#undef	BCMSPACE
+#define bcmspace	FALSE	
+#else
+#define	BCMSPACE
+#define bcmspace	TRUE	
+#endif
+
+
+#define	MAXSZ_NVRAM_VARS	4096
+
+#define LOCATOR_EXTERN static
+
+#endif 
diff --git a/drivers/net/wireless/bcm4329/include/bcmdevs.h b/drivers/net/wireless/bcm4329/include/bcmdevs.h
new file mode 100644
index 0000000..14853f1
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/bcmdevs.h
@@ -0,0 +1,124 @@
+/*
+ * Broadcom device-specific manifest constants.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmdevs.h,v 13.172.4.5.4.10.2.36 2010/05/25 08:33:44 Exp $
+ */
+
+
+#ifndef	_BCMDEVS_H
+#define	_BCMDEVS_H
+
+
+#define	VENDOR_EPIGRAM		0xfeda
+#define	VENDOR_BROADCOM		0x14e4
+#define VENDOR_SI_IMAGE		0x1095		
+#define VENDOR_TI		0x104c		
+#define VENDOR_RICOH		0x1180		
+#define VENDOR_JMICRON		0x197b
+
+
+#define	VENDOR_BROADCOM_PCMCIA	0x02d0
+
+
+#define	VENDOR_BROADCOM_SDIO	0x00BF
+
+
+#define BCM_DNGL_VID            0xa5c
+#define BCM_DNGL_BL_PID_4320    0xbd11
+#define BCM_DNGL_BL_PID_4328    0xbd12
+#define BCM_DNGL_BL_PID_4322    0xbd13
+#define BCM_DNGL_BL_PID_4325    0xbd14
+#define BCM_DNGL_BL_PID_4315    0xbd15
+#define BCM_DNGL_BL_PID_4319    0xbd16
+#define BCM_DNGL_BDC_PID        0xbdc
+
+#define	BCM4325_D11DUAL_ID	0x431b		
+#define	BCM4325_D11G_ID		0x431c		
+#define	BCM4325_D11A_ID		0x431d		
+#define BCM4329_D11NDUAL_ID	0x432e		
+#define BCM4329_D11N2G_ID	0x432f		
+#define BCM4329_D11N5G_ID	0x4330		
+#define BCM4336_D11N_ID		0x4343		
+#define	BCM4315_D11DUAL_ID	0x4334		
+#define	BCM4315_D11G_ID		0x4335		
+#define	BCM4315_D11A_ID		0x4336		
+#define BCM4319_D11N_ID		0x4337		
+#define BCM4319_D11N2G_ID	0x4338		
+#define BCM4319_D11N5G_ID	0x4339		
+
+
+#define SDIOH_FPGA_ID		0x43f2		
+#define SPIH_FPGA_ID		0x43f5		
+#define	BCM4710_DEVICE_ID	0x4710		
+#define BCM27XX_SDIOH_ID	0x2702		
+#define PCIXX21_FLASHMEDIA0_ID	0x8033		
+#define PCIXX21_SDIOH0_ID	0x8034		
+#define PCIXX21_FLASHMEDIA_ID	0x803b		
+#define PCIXX21_SDIOH_ID	0x803c		
+#define R5C822_SDIOH_ID		0x0822		
+#define JMICRON_SDIOH_ID	0x2381		
+
+
+#define	BCM4306_CHIP_ID		0x4306		
+#define	BCM4311_CHIP_ID		0x4311		
+#define	BCM4312_CHIP_ID		0x4312		
+#define	BCM4315_CHIP_ID		0x4315		
+#define	BCM4318_CHIP_ID		0x4318		
+#define	BCM4319_CHIP_ID		0x4319		
+#define	BCM4320_CHIP_ID		0x4320		
+#define	BCM4321_CHIP_ID		0x4321		
+#define	BCM4322_CHIP_ID		0x4322		
+#define	BCM4325_CHIP_ID		0x4325		
+#define	BCM4328_CHIP_ID		0x4328		
+#define	BCM4329_CHIP_ID		0x4329		
+#define BCM4336_CHIP_ID		0x4336		
+#define	BCM4402_CHIP_ID		0x4402		
+#define	BCM4704_CHIP_ID		0x4704		
+#define	BCM4710_CHIP_ID		0x4710		
+#define	BCM4712_CHIP_ID		0x4712		
+#define BCM4785_CHIP_ID		0x4785		
+#define	BCM5350_CHIP_ID		0x5350		
+#define	BCM5352_CHIP_ID		0x5352		
+#define	BCM5354_CHIP_ID		0x5354		
+#define BCM5365_CHIP_ID		0x5365          
+
+
+
+#define	BCM4303_PKG_ID		2		
+#define	BCM4309_PKG_ID		1		
+#define	BCM4712LARGE_PKG_ID	0		
+#define	BCM4712SMALL_PKG_ID	1		
+#define	BCM4712MID_PKG_ID	2		
+#define BCM4328USBD11G_PKG_ID	2		
+#define BCM4328USBDUAL_PKG_ID	3		
+#define BCM4328SDIOD11G_PKG_ID	4		
+#define BCM4328SDIODUAL_PKG_ID	5		
+#define BCM4329_289PIN_PKG_ID	0		
+#define BCM4329_182PIN_PKG_ID	1		
+#define BCM5354E_PKG_ID		1		
+#define HDLSIM5350_PKG_ID	1		
+#define HDLSIM_PKG_ID		14		
+#define HWSIM_PKG_ID		15		
+
+
+#endif 
diff --git a/drivers/net/wireless/bcm4329/include/bcmendian.h b/drivers/net/wireless/bcm4329/include/bcmendian.h
new file mode 100644
index 0000000..ae46838
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/bcmendian.h
@@ -0,0 +1,205 @@
+/*
+ * Byte order utilities
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ *  $Id: bcmendian.h,v 1.31.302.1.16.1 2009/02/03 18:34:31 Exp $
+ *
+ * This file by default provides proper behavior on little-endian architectures.
+ * On big-endian architectures, IL_BIGENDIAN should be defined.
+ */
+
+
+#ifndef _BCMENDIAN_H_
+#define _BCMENDIAN_H_
+
+#include <typedefs.h>
+
+
+#define BCMSWAP16(val) \
+	((uint16)((((uint16)(val) & (uint16)0x00ffU) << 8) | \
+		  (((uint16)(val) & (uint16)0xff00U) >> 8)))
+
+
+#define BCMSWAP32(val) \
+	((uint32)((((uint32)(val) & (uint32)0x000000ffU) << 24) | \
+		  (((uint32)(val) & (uint32)0x0000ff00U) <<  8) | \
+		  (((uint32)(val) & (uint32)0x00ff0000U) >>  8) | \
+		  (((uint32)(val) & (uint32)0xff000000U) >> 24)))
+
+
+#define BCMSWAP32BY16(val) \
+	((uint32)((((uint32)(val) & (uint32)0x0000ffffU) << 16) | \
+		  (((uint32)(val) & (uint32)0xffff0000U) >> 16)))
+
+
+static INLINE uint16
+bcmswap16(uint16 val)
+{
+	return BCMSWAP16(val);
+}
+
+static INLINE uint32
+bcmswap32(uint32 val)
+{
+	return BCMSWAP32(val);
+}
+
+static INLINE uint32
+bcmswap32by16(uint32 val)
+{
+	return BCMSWAP32BY16(val);
+}
+
+
+
+
+static INLINE void
+bcmswap16_buf(uint16 *buf, uint len)
+{
+	len = len / 2;
+
+	while (len--) {
+		*buf = bcmswap16(*buf);
+		buf++;
+	}
+}
+
+#ifndef hton16
+#ifndef IL_BIGENDIAN
+#define HTON16(i) BCMSWAP16(i)
+#define HTON32(i) BCMSWAP32(i)
+#define	hton16(i) bcmswap16(i)
+#define	hton32(i) bcmswap32(i)
+#define	ntoh16(i) bcmswap16(i)
+#define	ntoh32(i) bcmswap32(i)
+#define HTOL16(i) (i)
+#define HTOL32(i) (i)
+#define ltoh16(i) (i)
+#define ltoh32(i) (i)
+#define htol16(i) (i)
+#define htol32(i) (i)
+#else
+#define HTON16(i) (i)
+#define HTON32(i) (i)
+#define	hton16(i) (i)
+#define	hton32(i) (i)
+#define	ntoh16(i) (i)
+#define	ntoh32(i) (i)
+#define HTOL16(i) BCMSWAP16(i)
+#define HTOL32(i) BCMSWAP32(i)
+#define	ltoh16(i) bcmswap16(i)
+#define	ltoh32(i) bcmswap32(i)
+#define htol16(i) bcmswap16(i)
+#define htol32(i) bcmswap32(i)
+#endif 
+#endif 
+
+#ifndef IL_BIGENDIAN
+#define ltoh16_buf(buf, i)
+#define htol16_buf(buf, i)
+#else
+#define ltoh16_buf(buf, i) bcmswap16_buf((uint16 *)buf, i)
+#define htol16_buf(buf, i) bcmswap16_buf((uint16 *)buf, i)
+#endif 
+
+
+static INLINE void
+htol16_ua_store(uint16 val, uint8 *bytes)
+{
+	bytes[0] = val & 0xff;
+	bytes[1] = val >> 8;
+}
+
+
+static INLINE void
+htol32_ua_store(uint32 val, uint8 *bytes)
+{
+	bytes[0] = val & 0xff;
+	bytes[1] = (val >> 8) & 0xff;
+	bytes[2] = (val >> 16) & 0xff;
+	bytes[3] = val >> 24;
+}
+
+
+static INLINE void
+hton16_ua_store(uint16 val, uint8 *bytes)
+{
+	bytes[0] = val >> 8;
+	bytes[1] = val & 0xff;
+}
+
+
+static INLINE void
+hton32_ua_store(uint32 val, uint8 *bytes)
+{
+	bytes[0] = val >> 24;
+	bytes[1] = (val >> 16) & 0xff;
+	bytes[2] = (val >> 8) & 0xff;
+	bytes[3] = val & 0xff;
+}
+
+#define _LTOH16_UA(cp)	((cp)[0] | ((cp)[1] << 8))
+#define _LTOH32_UA(cp)	((cp)[0] | ((cp)[1] << 8) | ((cp)[2] << 16) | ((cp)[3] << 24))
+#define _NTOH16_UA(cp)	(((cp)[0] << 8) | (cp)[1])
+#define _NTOH32_UA(cp)	(((cp)[0] << 24) | ((cp)[1] << 16) | ((cp)[2] << 8) | (cp)[3])
+
+
+static INLINE uint16
+ltoh16_ua(const void *bytes)
+{
+	return _LTOH16_UA((const uint8 *)bytes);
+}
+
+
+static INLINE uint32
+ltoh32_ua(const void *bytes)
+{
+	return _LTOH32_UA((const uint8 *)bytes);
+}
+
+
+static INLINE uint16
+ntoh16_ua(const void *bytes)
+{
+	return _NTOH16_UA((const uint8 *)bytes);
+}
+
+
+static INLINE uint32
+ntoh32_ua(const void *bytes)
+{
+	return _NTOH32_UA((const uint8 *)bytes);
+}
+
+#define ltoh_ua(ptr) \
+	(sizeof(*(ptr)) == sizeof(uint8) ? *(const uint8 *)ptr : \
+	 sizeof(*(ptr)) == sizeof(uint16) ? _LTOH16_UA((const uint8 *)ptr) : \
+	 sizeof(*(ptr)) == sizeof(uint32) ? _LTOH32_UA((const uint8 *)ptr) : \
+	 0xfeedf00d)
+
+#define ntoh_ua(ptr) \
+	(sizeof(*(ptr)) == sizeof(uint8) ? *(const uint8 *)ptr : \
+	 sizeof(*(ptr)) == sizeof(uint16) ? _NTOH16_UA((const uint8 *)ptr) : \
+	 sizeof(*(ptr)) == sizeof(uint32) ? _NTOH32_UA((const uint8 *)ptr) : \
+	 0xfeedf00d)
+
+#endif 
diff --git a/drivers/net/wireless/bcm4329/include/bcmpcispi.h b/drivers/net/wireless/bcm4329/include/bcmpcispi.h
new file mode 100644
index 0000000..7d98fb7
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/bcmpcispi.h
@@ -0,0 +1,205 @@
+/*
+ * Broadcom PCI-SPI Host Controller Register Definitions
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmpcispi.h,v 13.11.8.3 2008/07/09 21:23:29 Exp $
+ */
+
+/* cpp contortions to concatenate w/arg prescan */
+#ifndef PAD
+#define	_PADLINE(line)	pad ## line
+#define	_XSTR(line)	_PADLINE(line)
+#define	PAD		_XSTR(__LINE__)
+#endif	/* PAD */
+
+/*
++---------------------------------------------------------------------------+
+|                                                                           |
+|                     7     6     5     4     3     2     1       0         |
+| 0x0000  SPI_CTRL    SPIE  SPE   0     MSTR  CPOL  CPHA  SPR1    SPR0      |
+| 0x0004  SPI_STAT    SPIF  WCOL  ST1   ST0   WFFUL WFEMP RFFUL   RFEMP     |
+| 0x0008  SPI_DATA    Bits 31:0, data to send out on MOSI                   |
+| 0x000C  SPI_EXT     ICNT1 ICNT0 BSWAP *HSMODE           ESPR1   ESPR0     |
+| 0x0020  GPIO_OE     0=input, 1=output                   PWR_OE  CS_OE     |
+| 0x0024  GPIO_DATA   CARD:1=missing, 0=present     CARD  PWR_DAT CS_DAT    |
+| 0x0040  INT_EDGE    0=level, 1=edge                     DEV_E   SPI_E     |
+| 0x0044  INT_POL     1=active high, 0=active low         DEV_P   SPI_P     |
+| 0x0048  INTMASK                                         DEV     SPI       |
+| 0x004C  INTSTATUS                                       DEV     SPI       |
+| 0x0060  HEXDISP     Reset value: 0x14e443f5.  In hexdisp mode, value      |
+|                     shows on the Raggedstone1 4-digit 7-segment display.  |
+| 0x0064  CURRENT_MA  Low 16 bits indicate card current consumption in mA   |
+| 0x006C  DISP_SEL    Display mode (0=hexdisp, 1=current)         DSP       |
+| 0x00C0  PLL_CTL  bit31=ext_clk, remainder unused.                         |
+| 0x00C4  PLL_STAT                            LOCK                          |
+| 0x00C8  CLK_FREQ                                                          |
+| 0x00CC  CLK_CNT                                                           |
+|                                                                           |
+| *Notes: HSMODE is not implemented, never set this bit!                    |
+| BSWAP is available in rev >= 8                                            |
+|                                                                           |
++---------------------------------------------------------------------------+
+*/
+
+typedef volatile struct {
+	uint32 spih_ctrl;		/* 0x00 SPI Control Register */
+	uint32 spih_stat;		/* 0x04 SPI Status Register */
+	uint32 spih_data;		/* 0x08 SPI Data Register, 32-bits wide */
+	uint32 spih_ext;		/* 0x0C SPI Extension Register */
+	uint32 PAD[4];			/* 0x10-0x1F PADDING */
+
+	uint32 spih_gpio_ctrl;		/* 0x20 SPI GPIO Control Register */
+	uint32 spih_gpio_data;		/* 0x24 SPI GPIO Data Register */
+	uint32 PAD[6];			/* 0x28-0x3F PADDING */
+
+	uint32 spih_int_edge;		/* 0x40 SPI Interrupt Edge Register (0=Level, 1=Edge) */
+	uint32 spih_int_pol;		/* 0x44 SPI Interrupt Polarity Register (0=Active Low, */
+							/* 1=Active High) */
+	uint32 spih_int_mask;		/* 0x48 SPI Interrupt Mask */
+	uint32 spih_int_status;		/* 0x4C SPI Interrupt Status */
+	uint32 PAD[4];			/* 0x50-0x5F PADDING */
+
+	uint32 spih_hex_disp;		/* 0x60 SPI 4-digit hex display value */
+	uint32 spih_current_ma;		/* 0x64 SPI SD card current consumption in mA */
+	uint32 PAD[1];			/* 0x68 PADDING */
+	uint32 spih_disp_sel;		/* 0x6c SPI 4-digit hex display mode select (1=current) */
+	uint32 PAD[4];			/* 0x70-0x7F PADDING */
+	uint32 PAD[8];			/* 0x80-0x9F PADDING */
+	uint32 PAD[8];			/* 0xA0-0xBF PADDING */
+	uint32 spih_pll_ctrl;	/* 0xC0 PLL Control Register */
+	uint32 spih_pll_status;	/* 0xC4 PLL Status Register */
+	uint32 spih_xtal_freq;	/* 0xC8 External Clock Frequency in units of 10000Hz */
+	uint32 spih_clk_count;	/* 0xCC External Clock Count Register */
+
+} spih_regs_t;
+
+typedef volatile struct {
+	uint32 cfg_space[0x40];		/* 0x000-0x0FF PCI Configuration Space (Read Only) */
+	uint32 P_IMG_CTRL0;		/* 0x100 PCI Image0 Control Register */
+
+	uint32 P_BA0;			/* 0x104 32 R/W PCI Image0 Base Address register */
+	uint32 P_AM0;			/* 0x108 32 R/W PCI Image0 Address Mask register */
+	uint32 P_TA0;			/* 0x10C 32 R/W PCI Image0 Translation Address register */
+	uint32 P_IMG_CTRL1;		/* 0x110 32 R/W PCI Image1 Control register */
+	uint32 P_BA1;			/* 0x114 32 R/W PCI Image1 Base Address register */
+	uint32 P_AM1;			/* 0x118 32 R/W PCI Image1 Address Mask register */
+	uint32 P_TA1;			/* 0x11C 32 R/W PCI Image1 Translation Address register */
+	uint32 P_IMG_CTRL2;		/* 0x120 32 R/W PCI Image2 Control register */
+	uint32 P_BA2;			/* 0x124 32 R/W PCI Image2 Base Address register */
+	uint32 P_AM2;			/* 0x128 32 R/W PCI Image2 Address Mask register */
+	uint32 P_TA2;			/* 0x12C 32 R/W PCI Image2 Translation Address register */
+	uint32 P_IMG_CTRL3;		/* 0x130 32 R/W PCI Image3 Control register */
+	uint32 P_BA3;			/* 0x134 32 R/W PCI Image3 Base Address register */
+	uint32 P_AM3;			/* 0x138 32 R/W PCI Image3 Address Mask register */
+	uint32 P_TA3;			/* 0x13C 32 R/W PCI Image3 Translation Address register */
+	uint32 P_IMG_CTRL4;		/* 0x140 32 R/W PCI Image4 Control register */
+	uint32 P_BA4;			/* 0x144 32 R/W PCI Image4 Base Address register */
+	uint32 P_AM4;			/* 0x148 32 R/W PCI Image4 Address Mask register */
+	uint32 P_TA4;			/* 0x14C 32 R/W PCI Image4 Translation Address register */
+	uint32 P_IMG_CTRL5;		/* 0x150 32 R/W PCI Image5 Control register */
+	uint32 P_BA5;			/* 0x154 32 R/W PCI Image5 Base Address register */
+	uint32 P_AM5;			/* 0x158 32 R/W PCI Image5 Address Mask register */
+	uint32 P_TA5;			/* 0x15C 32 R/W PCI Image5 Translation Address register */
+	uint32 P_ERR_CS;		/* 0x160 32 R/W PCI Error Control and Status register */
+	uint32 P_ERR_ADDR;		/* 0x164 32 R PCI Erroneous Address register */
+	uint32 P_ERR_DATA;		/* 0x168 32 R PCI Erroneous Data register */
+
+	uint32 PAD[5];			/* 0x16C-0x17F PADDING */
+
+	uint32 WB_CONF_SPC_BAR;		/* 0x180 32 R WISHBONE Configuration Space Base Address */
+	uint32 W_IMG_CTRL1;		/* 0x184 32 R/W WISHBONE Image1 Control register */
+	uint32 W_BA1;			/* 0x188 32 R/W WISHBONE Image1 Base Address register */
+	uint32 W_AM1;			/* 0x18C 32 R/W WISHBONE Image1 Address Mask register */
+	uint32 W_TA1;			/* 0x190 32 R/W WISHBONE Image1 Translation Address reg */
+	uint32 W_IMG_CTRL2;		/* 0x194 32 R/W WISHBONE Image2 Control register */
+	uint32 W_BA2;			/* 0x198 32 R/W WISHBONE Image2 Base Address register */
+	uint32 W_AM2;			/* 0x19C 32 R/W WISHBONE Image2 Address Mask register */
+	uint32 W_TA2;			/* 0x1A0 32 R/W WISHBONE Image2 Translation Address reg */
+	uint32 W_IMG_CTRL3;		/* 0x1A4 32 R/W WISHBONE Image3 Control register */
+	uint32 W_BA3;			/* 0x1A8 32 R/W WISHBONE Image3 Base Address register */
+	uint32 W_AM3;			/* 0x1AC 32 R/W WISHBONE Image3 Address Mask register */
+	uint32 W_TA3;			/* 0x1B0 32 R/W WISHBONE Image3 Translation Address reg */
+	uint32 W_IMG_CTRL4;		/* 0x1B4 32 R/W WISHBONE Image4 Control register */
+	uint32 W_BA4;			/* 0x1B8 32 R/W WISHBONE Image4 Base Address register */
+	uint32 W_AM4;			/* 0x1BC 32 R/W WISHBONE Image4 Address Mask register */
+	uint32 W_TA4;			/* 0x1C0 32 R/W WISHBONE Image4 Translation Address reg */
+	uint32 W_IMG_CTRL5;		/* 0x1C4 32 R/W WISHBONE Image5 Control register */
+	uint32 W_BA5;			/* 0x1C8 32 R/W WISHBONE Image5 Base Address register */
+	uint32 W_AM5;			/* 0x1CC 32 R/W WISHBONE Image5 Address Mask register */
+	uint32 W_TA5;			/* 0x1D0 32 R/W WISHBONE Image5 Translation Address reg */
+	uint32 W_ERR_CS;		/* 0x1D4 32 R/W WISHBONE Error Control and Status reg */
+	uint32 W_ERR_ADDR;		/* 0x1D8 32 R WISHBONE Erroneous Address register */
+	uint32 W_ERR_DATA;		/* 0x1DC 32 R WISHBONE Erroneous Data register */
+	uint32 CNF_ADDR;		/* 0x1E0 32 R/W Configuration Cycle register */
+	uint32 CNF_DATA;		/* 0x1E4 32 R/W Configuration Cycle Generation Data reg */
+
+	uint32 INT_ACK;			/* 0x1E8 32 R Interrupt Acknowledge register */
+	uint32 ICR;			/* 0x1EC 32 R/W Interrupt Control register */
+	uint32 ISR;			/* 0x1F0 32 R/W Interrupt Status register */
+} spih_pciregs_t;
+
+/*
+ * PCI Core interrupt enable and status bit definitions.
+ */
+
+/* PCI Core ICR Register bit definitions */
+#define PCI_INT_PROP_EN		(1 << 0)	/* Interrupt Propagation Enable */
+#define PCI_WB_ERR_INT_EN	(1 << 1)	/* Wishbone Error Interrupt Enable */
+#define PCI_PCI_ERR_INT_EN	(1 << 2)	/* PCI Error Interrupt Enable */
+#define PCI_PAR_ERR_INT_EN	(1 << 3)	/* Parity Error Interrupt Enable */
+#define PCI_SYS_ERR_INT_EN	(1 << 4)	/* System Error Interrupt Enable */
+#define PCI_SOFTWARE_RESET	(1U << 31)	/* Software reset of the PCI Core. */
+
+
+/* PCI Core ISR Register bit definitions */
+#define PCI_INT_PROP_ST		(1 << 0)	/* Interrupt Propagation Status */
+#define PCI_WB_ERR_INT_ST	(1 << 1)	/* Wishbone Error Interrupt Status */
+#define PCI_PCI_ERR_INT_ST	(1 << 2)	/* PCI Error Interrupt Status */
+#define PCI_PAR_ERR_INT_ST	(1 << 3)	/* Parity Error Interrupt Status */
+#define PCI_SYS_ERR_INT_ST	(1 << 4)	/* System Error Interrupt Status */
+
+
+/* Registers on the Wishbone bus */
+#define SPIH_CTLR_INTR		(1 << 0)	/* SPI Host Controller Core Interrupt */
+#define SPIH_DEV_INTR		(1 << 1)	/* SPI Device Interrupt */
+#define SPIH_WFIFO_INTR		(1 << 2)	/* SPI Tx FIFO Empty Intr (FPGA Rev >= 8) */
+
+/* GPIO Bit definitions */
+#define SPIH_CS				(1 << 0)	/* SPI Chip Select (active low) */
+#define SPIH_SLOT_POWER		(1 << 1)	/* SD Card Slot Power Enable */
+#define SPIH_CARD_DETECT	(1 << 2)	/* SD Card Detect */
+
+/* SPI Status Register Bit definitions */
+#define SPIH_STATE_MASK		0x30		/* SPI Transfer State Machine state mask */
+#define SPIH_STATE_SHIFT	4		/* SPI Transfer State Machine state shift */
+#define SPIH_WFFULL			(1 << 3)	/* SPI Write FIFO Full */
+#define SPIH_WFEMPTY		(1 << 2)	/* SPI Write FIFO Empty */
+#define SPIH_RFFULL			(1 << 1)	/* SPI Read FIFO Full */
+#define SPIH_RFEMPTY		(1 << 0)	/* SPI Read FIFO Empty */
+
+#define SPIH_EXT_CLK		(1U << 31)	/* Use External Clock as PLL Clock source. */
+
+#define SPIH_PLL_NO_CLK		(1 << 1)	/* Set to 1 if the PLL's input clock is lost. */
+#define SPIH_PLL_LOCKED		(1 << 3)	/* Set to 1 when the PLL is locked. */
+
+/* Spin bit loop bound check */
+#define SPI_SPIN_BOUND		0xf4240		/* 1 million */
diff --git a/drivers/net/wireless/bcm4329/include/bcmperf.h b/drivers/net/wireless/bcm4329/include/bcmperf.h
new file mode 100644
index 0000000..2a78784
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/bcmperf.h
@@ -0,0 +1,36 @@
+/*
+ * Performance counters software interface.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmperf.h,v 13.5 2007/09/14 22:00:59 Exp $
+ */
+/* essai */
+#ifndef _BCMPERF_H_
+#define _BCMPERF_H_
+/* get cache hits and misses */
+#define BCMPERF_ENABLE_INSTRCOUNT()
+#define BCMPERF_ENABLE_ICACHE_MISS()
+#define BCMPERF_ENABLE_ICACHE_HIT()
+#define	BCMPERF_GETICACHE_MISS(x)	((x) = 0)
+#define	BCMPERF_GETICACHE_HIT(x)	((x) = 0)
+#define	BCMPERF_GETINSTRCOUNT(x)	((x) = 0)
+#endif /* _BCMPERF_H_ */
diff --git a/drivers/net/wireless/bcm4329/include/bcmsdbus.h b/drivers/net/wireless/bcm4329/include/bcmsdbus.h
new file mode 100644
index 0000000..b7b67bc
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/bcmsdbus.h
@@ -0,0 +1,117 @@
+/*
+ * Definitions for API from sdio common code (bcmsdh) to individual
+ * host controller drivers.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmsdbus.h,v 13.11.14.2.6.6 2009/10/27 17:20:28 Exp $
+ */
+
+#ifndef	_sdio_api_h_
+#define	_sdio_api_h_
+
+
+#define SDIOH_API_RC_SUCCESS                          (0x00)
+#define SDIOH_API_RC_FAIL	                      (0x01)
+#define SDIOH_API_SUCCESS(status) (status == 0)
+
+#define SDIOH_READ              0	/* Read request */
+#define SDIOH_WRITE             1	/* Write request */
+
+#define SDIOH_DATA_FIX          0	/* Fixed addressing */
+#define SDIOH_DATA_INC          1	/* Incremental addressing */
+
+#define SDIOH_CMD_TYPE_NORMAL   0       /* Normal command */
+#define SDIOH_CMD_TYPE_APPEND   1       /* Append command */
+#define SDIOH_CMD_TYPE_CUTTHRU  2       /* Cut-through command */
+
+#define SDIOH_DATA_PIO          0       /* PIO mode */
+#define SDIOH_DATA_DMA          1       /* DMA mode */
+
+
+typedef int SDIOH_API_RC;
+
+/* SDio Host structure */
+typedef struct sdioh_info sdioh_info_t;
+
+/* callback function, taking one arg */
+typedef void (*sdioh_cb_fn_t)(void *);
+
+/* attach, return handler on success, NULL if failed.
+ *  The handler shall be provided by all subsequent calls. No local cache
+ *  cfghdl points to the starting address of pci device mapped memory
+ */
+extern sdioh_info_t * sdioh_attach(osl_t *osh, void *cfghdl, uint irq);
+extern SDIOH_API_RC sdioh_detach(osl_t *osh, sdioh_info_t *si);
+extern SDIOH_API_RC sdioh_interrupt_register(sdioh_info_t *si, sdioh_cb_fn_t fn, void *argh);
+extern SDIOH_API_RC sdioh_interrupt_deregister(sdioh_info_t *si);
+
+/* query whether SD interrupt is enabled or not */
+extern SDIOH_API_RC sdioh_interrupt_query(sdioh_info_t *si, bool *onoff);
+
+/* enable or disable SD interrupt */
+extern SDIOH_API_RC sdioh_interrupt_set(sdioh_info_t *si, bool enable_disable);
+
+#if defined(DHD_DEBUG)
+extern bool sdioh_interrupt_pending(sdioh_info_t *si);
+#endif
+
+/* read or write one byte using cmd52 */
+extern SDIOH_API_RC sdioh_request_byte(sdioh_info_t *si, uint rw, uint fnc, uint addr, uint8 *byte);
+
+/* read or write 2/4 bytes using cmd53 */
+extern SDIOH_API_RC sdioh_request_word(sdioh_info_t *si, uint cmd_type, uint rw, uint fnc,
+	uint addr, uint32 *word, uint nbyte);
+
+/* read or write any buffer using cmd53 */
+extern SDIOH_API_RC sdioh_request_buffer(sdioh_info_t *si, uint pio_dma, uint fix_inc,
+	uint rw, uint fnc_num, uint32 addr, uint regwidth, uint32 buflen, uint8 *buffer,
+	void *pkt);
+
+/* get cis data */
+extern SDIOH_API_RC sdioh_cis_read(sdioh_info_t *si, uint fuc, uint8 *cis, uint32 length);
+
+extern SDIOH_API_RC sdioh_cfg_read(sdioh_info_t *si, uint fuc, uint32 addr, uint8 *data);
+extern SDIOH_API_RC sdioh_cfg_write(sdioh_info_t *si, uint fuc, uint32 addr, uint8 *data);
+
+/* query number of io functions */
+extern uint sdioh_query_iofnum(sdioh_info_t *si);
+
+/* handle iovars */
+extern int sdioh_iovar_op(sdioh_info_t *si, const char *name,
+                          void *params, int plen, void *arg, int len, bool set);
+
+/* Issue abort to the specified function and clear controller as needed */
+extern int sdioh_abort(sdioh_info_t *si, uint fnc);
+
+/* Start and Stop SDIO without re-enumerating the SD card. */
+extern int sdioh_start(sdioh_info_t *si, int stage);
+extern int sdioh_stop(sdioh_info_t *si);
+
+/* Reset and re-initialize the device */
+extern int sdioh_sdio_reset(sdioh_info_t *si);
+
+/* Helper function */
+void *bcmsdh_get_sdioh(bcmsdh_info_t *sdh);
+
+
+
+#endif /* _sdio_api_h_ */
diff --git a/drivers/net/wireless/bcm4329/include/bcmsdh.h b/drivers/net/wireless/bcm4329/include/bcmsdh.h
new file mode 100644
index 0000000..f5dee5c
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/bcmsdh.h
@@ -0,0 +1,208 @@
+/*
+ * SDIO host client driver interface of Broadcom HNBU
+ *     export functions to client drivers
+ *     abstract OS and BUS specific details of SDIO
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmsdh.h,v 13.35.14.7.6.8 2009/10/14 04:22:25 Exp $
+ */
+
+#ifndef	_bcmsdh_h_
+#define	_bcmsdh_h_
+
+#define BCMSDH_ERROR_VAL	0x0001 /* Error */
+#define BCMSDH_INFO_VAL		0x0002 /* Info */
+extern const uint bcmsdh_msglevel;
+
+#define BCMSDH_ERROR(x)
+#define BCMSDH_INFO(x)
+
+/* forward declarations */
+typedef struct bcmsdh_info bcmsdh_info_t;
+typedef void (*bcmsdh_cb_fn_t)(void *);
+
+/* Attach and build an interface to the underlying SD host driver.
+ *  - Allocates resources (structs, arrays, mem, OS handles, etc) needed by bcmsdh.
+ *  - Returns the bcmsdh handle and virtual address base for register access.
+ *    The returned handle should be used in all subsequent calls, but the bcmsh
+ *    implementation may maintain a single "default" handle (e.g. the first or
+ *    most recent one) to enable single-instance implementations to pass NULL.
+ */
+extern bcmsdh_info_t *bcmsdh_attach(osl_t *osh, void *cfghdl, void **regsva, uint irq);
+
+/* Detach - freeup resources allocated in attach */
+extern int bcmsdh_detach(osl_t *osh, void *sdh);
+
+/* Query if SD device interrupts are enabled */
+extern bool bcmsdh_intr_query(void *sdh);
+
+/* Enable/disable SD interrupt */
+extern int bcmsdh_intr_enable(void *sdh);
+extern int bcmsdh_intr_disable(void *sdh);
+
+/* Register/deregister device interrupt handler. */
+extern int bcmsdh_intr_reg(void *sdh, bcmsdh_cb_fn_t fn, void *argh);
+extern int bcmsdh_intr_dereg(void *sdh);
+
+#if defined(DHD_DEBUG)
+/* Query pending interrupt status from the host controller */
+extern bool bcmsdh_intr_pending(void *sdh);
+#endif
+
+#ifdef BCMLXSDMMC
+extern int bcmsdh_claim_host_and_lock(void *sdh);
+extern int bcmsdh_release_host_and_unlock(void *sdh);
+#endif /* BCMLXSDMMC */
+
+/* Register a callback to be called if and when bcmsdh detects
+ * device removal. No-op in the case of non-removable/hardwired devices.
+ */
+extern int bcmsdh_devremove_reg(void *sdh, bcmsdh_cb_fn_t fn, void *argh);
+
+/* Access SDIO address space (e.g. CCCR) using CMD52 (single-byte interface).
+ *   fn:   function number
+ *   addr: unmodified SDIO-space address
+ *   data: data byte to write
+ *   err:  pointer to error code (or NULL)
+ */
+extern uint8 bcmsdh_cfg_read(void *sdh, uint func, uint32 addr, int *err);
+extern void bcmsdh_cfg_write(void *sdh, uint func, uint32 addr, uint8 data, int *err);
+
+/* Read/Write 4bytes from/to cfg space */
+extern uint32 bcmsdh_cfg_read_word(void *sdh, uint fnc_num, uint32 addr, int *err);
+extern void bcmsdh_cfg_write_word(void *sdh, uint fnc_num, uint32 addr, uint32 data, int *err);
+
+/* Read CIS content for specified function.
+ *   fn:     function whose CIS is being requested (0 is common CIS)
+ *   cis:    pointer to memory location to place results
+ *   length: number of bytes to read
+ * Internally, this routine uses the values from the cis base regs (0x9-0xB)
+ * to form an SDIO-space address to read the data from.
+ */
+extern int bcmsdh_cis_read(void *sdh, uint func, uint8 *cis, uint length);
+
+/* Synchronous access to device (client) core registers via CMD53 to F1.
+ *   addr: backplane address (i.e. >= regsva from attach)
+ *   size: register width in bytes (2 or 4)
+ *   data: data for register write
+ */
+extern uint32 bcmsdh_reg_read(void *sdh, uint32 addr, uint size);
+extern uint32 bcmsdh_reg_write(void *sdh, uint32 addr, uint size, uint32 data);
+
+/* Indicate if last reg read/write failed */
+extern bool bcmsdh_regfail(void *sdh);
+
+/* Buffer transfer to/from device (client) core via cmd53.
+ *   fn:       function number
+ *   addr:     backplane address (i.e. >= regsva from attach)
+ *   flags:    backplane width, address increment, sync/async
+ *   buf:      pointer to memory data buffer
+ *   nbytes:   number of bytes to transfer to/from buf
+ *   pkt:      pointer to packet associated with buf (if any)
+ *   complete: callback function for command completion (async only)
+ *   handle:   handle for completion callback (first arg in callback)
+ * Returns 0 or error code.
+ * NOTE: Async operation is not currently supported.
+ */
+typedef void (*bcmsdh_cmplt_fn_t)(void *handle, int status, bool sync_waiting);
+extern int bcmsdh_send_buf(void *sdh, uint32 addr, uint fn, uint flags,
+                           uint8 *buf, uint nbytes, void *pkt,
+                           bcmsdh_cmplt_fn_t complete, void *handle);
+extern int bcmsdh_recv_buf(void *sdh, uint32 addr, uint fn, uint flags,
+                           uint8 *buf, uint nbytes, void *pkt,
+                           bcmsdh_cmplt_fn_t complete, void *handle);
+
+/* Flags bits */
+#define SDIO_REQ_4BYTE	0x1	/* Four-byte target (backplane) width (vs. two-byte) */
+#define SDIO_REQ_FIXED	0x2	/* Fixed address (FIFO) (vs. incrementing address) */
+#define SDIO_REQ_ASYNC	0x4	/* Async request (vs. sync request) */
+
+/* Pending (non-error) return code */
+#define BCME_PENDING	1
+
+/* Read/write to memory block (F1, no FIFO) via CMD53 (sync only).
+ *   rw:       read or write (0/1)
+ *   addr:     direct SDIO address
+ *   buf:      pointer to memory data buffer
+ *   nbytes:   number of bytes to transfer to/from buf
+ * Returns 0 or error code.
+ */
+extern int bcmsdh_rwdata(void *sdh, uint rw, uint32 addr, uint8 *buf, uint nbytes);
+
+/* Issue an abort to the specified function */
+extern int bcmsdh_abort(void *sdh, uint fn);
+
+/* Start SDIO Host Controller communication */
+extern int bcmsdh_start(void *sdh, int stage);
+
+/* Stop SDIO Host Controller communication */
+extern int bcmsdh_stop(void *sdh);
+
+/* Returns the "Device ID" of target device on the SDIO bus. */
+extern int bcmsdh_query_device(void *sdh);
+
+/* Returns the number of IO functions reported by the device */
+extern uint bcmsdh_query_iofnum(void *sdh);
+
+/* Miscellaneous knob tweaker. */
+extern int bcmsdh_iovar_op(void *sdh, const char *name,
+                           void *params, int plen, void *arg, int len, bool set);
+
+/* Reset and reinitialize the device */
+extern int bcmsdh_reset(bcmsdh_info_t *sdh);
+
+/* helper functions */
+
+extern void *bcmsdh_get_sdioh(bcmsdh_info_t *sdh);
+
+/* callback functions */
+typedef struct {
+	/* attach to device */
+	void *(*attach)(uint16 vend_id, uint16 dev_id, uint16 bus, uint16 slot,
+	                uint16 func, uint bustype, void * regsva, osl_t * osh,
+	                void * param);
+	/* detach from device */
+	void (*detach)(void *ch);
+} bcmsdh_driver_t;
+
+/* platform specific/high level functions */
+extern int bcmsdh_register(bcmsdh_driver_t *driver);
+extern void bcmsdh_unregister(void);
+extern bool bcmsdh_chipmatch(uint16 vendor, uint16 device);
+extern void bcmsdh_device_remove(void * sdh);
+
+#if defined(OOB_INTR_ONLY)
+extern int bcmsdh_register_oob_intr(void * dhdp);
+extern void bcmsdh_unregister_oob_intr(void);
+extern void bcmsdh_oob_intr_set(bool enable);
+#endif /* defined(OOB_INTR_ONLY) */
+/* Function to pass device-status bits to DHD. */
+extern uint32 bcmsdh_get_dstatus(void *sdh);
+
+/* Function to return current window addr */
+extern uint32 bcmsdh_cur_sbwad(void *sdh);
+
+/* Function to pass chipid and rev to lower layers for controlling pr's */
+extern void bcmsdh_chipinfo(void *sdh, uint32 chip, uint32 chiprev);
+
+
+#endif	/* _bcmsdh_h_ */
diff --git a/drivers/net/wireless/bcm4329/include/bcmsdh_sdmmc.h b/drivers/net/wireless/bcm4329/include/bcmsdh_sdmmc.h
new file mode 100644
index 0000000..4e6d1b5
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/bcmsdh_sdmmc.h
@@ -0,0 +1,122 @@
+/*
+ * BCMSDH Function Driver for the native SDIO/MMC driver in the Linux Kernel
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmsdh_sdmmc.h,v 13.1.2.1.8.7 2009/10/27 18:22:52 Exp $
+ */
+
+#ifndef __BCMSDH_SDMMC_H__
+#define __BCMSDH_SDMMC_H__
+
+#define sd_err(x)
+#define sd_trace(x)
+#define sd_info(x)
+#define sd_debug(x)
+#define sd_data(x)
+#define sd_ctrl(x)
+
+#define sd_sync_dma(sd, read, nbytes)
+#define sd_init_dma(sd)
+#define sd_ack_intr(sd)
+#define sd_wakeup(sd);
+
+/* Allocate/init/free per-OS private data */
+extern int sdioh_sdmmc_osinit(sdioh_info_t *sd);
+extern void sdioh_sdmmc_osfree(sdioh_info_t *sd);
+
+#define sd_log(x)
+
+#define SDIOH_ASSERT(exp) \
+	do { if (!(exp)) \
+		printf("!!!ASSERT fail: file %s lines %d", __FILE__, __LINE__); \
+	} while (0)
+
+#define BLOCK_SIZE_4318 64
+#define BLOCK_SIZE_4328 512
+
+/* internal return code */
+#define SUCCESS	0
+#define ERROR	1
+
+/* private bus modes */
+#define SDIOH_MODE_SD4		2
+#define CLIENT_INTR 		0x100	/* Get rid of this! */
+
+struct sdioh_info {
+	osl_t 		*osh;			/* osh handler */
+	bool		client_intr_enabled;	/* interrupt connnected flag */
+	bool		intr_handler_valid;	/* client driver interrupt handler valid */
+	sdioh_cb_fn_t	intr_handler;		/* registered interrupt handler */
+	void		*intr_handler_arg;	/* argument to call interrupt handler */
+	uint16		intmask;		/* Current active interrupts */
+	void		*sdos_info;		/* Pointer to per-OS private data */
+
+	uint 		irq;			/* Client irq */
+	int 		intrcount;		/* Client interrupts */
+
+	bool		sd_use_dma;		/* DMA on CMD53 */
+	bool 		sd_blockmode;		/* sd_blockmode == FALSE => 64 Byte Cmd 53s. */
+						/*  Must be on for sd_multiblock to be effective */
+	bool 		use_client_ints;	/* If this is false, make sure to restore */
+	int 		sd_mode;		/* SD1/SD4/SPI */
+	int 		client_block_size[SDIOD_MAX_IOFUNCS];		/* Blocksize */
+	uint8 		num_funcs;		/* Supported funcs on client */
+	uint32 		com_cis_ptr;
+	uint32 		func_cis_ptr[SDIOD_MAX_IOFUNCS];
+	uint		max_dma_len;
+	uint		max_dma_descriptors;	/* DMA Descriptors supported by this controller. */
+//	SDDMA_DESCRIPTOR	SGList[32];	/* Scatter/Gather DMA List */
+};
+
+/************************************************************
+ * Internal interfaces: per-port references into bcmsdh_sdmmc.c
+ */
+
+/* Global message bits */
+extern uint sd_msglevel;
+
+/* OS-independent interrupt handler */
+extern bool check_client_intr(sdioh_info_t *sd);
+
+/* Core interrupt enable/disable of device interrupts */
+extern void sdioh_sdmmc_devintr_on(sdioh_info_t *sd);
+extern void sdioh_sdmmc_devintr_off(sdioh_info_t *sd);
+
+
+/**************************************************************
+ * Internal interfaces: bcmsdh_sdmmc.c references to per-port code
+ */
+
+/* Register mapping routines */
+extern uint32 *sdioh_sdmmc_reg_map(osl_t *osh, int32 addr, int size);
+extern void sdioh_sdmmc_reg_unmap(osl_t *osh, int32 addr, int size);
+
+/* Interrupt (de)registration routines */
+extern int sdioh_sdmmc_register_irq(sdioh_info_t *sd, uint irq);
+extern void sdioh_sdmmc_free_irq(uint irq, sdioh_info_t *sd);
+
+typedef struct _BCMSDH_SDMMC_INSTANCE {
+	sdioh_info_t	*sd;
+	struct sdio_func *func[SDIOD_MAX_IOFUNCS];
+} BCMSDH_SDMMC_INSTANCE, *PBCMSDH_SDMMC_INSTANCE;
+
+#endif /* __BCMSDH_SDMMC_H__ */
diff --git a/drivers/net/wireless/bcm4329/include/bcmsdpcm.h b/drivers/net/wireless/bcm4329/include/bcmsdpcm.h
new file mode 100644
index 0000000..77aca45
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/bcmsdpcm.h
@@ -0,0 +1,263 @@
+/*
+ * Broadcom SDIO/PCMCIA
+ * Software-specific definitions shared between device and host side
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmsdpcm.h,v 1.1.2.4 2010/07/02 01:15:46 Exp $
+ */
+
+#ifndef	_bcmsdpcm_h_
+#define	_bcmsdpcm_h_
+
+/*
+ * Software allocation of To SB Mailbox resources
+ */
+
+/* intstatus bits */
+#define I_SMB_NAK	I_SMB_SW0	/* To SB Mailbox Frame NAK */
+#define I_SMB_INT_ACK	I_SMB_SW1	/* To SB Mailbox Host Interrupt ACK */
+#define I_SMB_USE_OOB	I_SMB_SW2	/* To SB Mailbox Use OOB Wakeup */
+#define I_SMB_DEV_INT	I_SMB_SW3	/* To SB Mailbox Miscellaneous Interrupt */
+
+/* tosbmailbox bits corresponding to intstatus bits */
+#define SMB_NAK		(1 << 0)	/* To SB Mailbox Frame NAK */
+#define SMB_INT_ACK	(1 << 1)	/* To SB Mailbox Host Interrupt ACK */
+#define SMB_USE_OOB	(1 << 2)	/* To SB Mailbox Use OOB Wakeup */
+#define SMB_DEV_INT	(1 << 3)	/* To SB Mailbox Miscellaneous Interrupt */
+#define SMB_MASK	0x0000000f	/* To SB Mailbox Mask */
+
+/* tosbmailboxdata */
+#define SMB_DATA_VERSION_MASK	0x00ff0000	/* host protocol version (sent with F2 enable) */
+#define SMB_DATA_VERSION_SHIFT	16		/* host protocol version (sent with F2 enable) */
+
+/*
+ * Software allocation of To Host Mailbox resources
+ */
+
+/* intstatus bits */
+#define I_HMB_FC_STATE	I_HMB_SW0	/* To Host Mailbox Flow Control State */
+#define I_HMB_FC_CHANGE	I_HMB_SW1	/* To Host Mailbox Flow Control State Changed */
+#define I_HMB_FRAME_IND	I_HMB_SW2	/* To Host Mailbox Frame Indication */
+#define I_HMB_HOST_INT	I_HMB_SW3	/* To Host Mailbox Miscellaneous Interrupt */
+
+/* tohostmailbox bits corresponding to intstatus bits */
+#define HMB_FC_ON	(1 << 0)	/* To Host Mailbox Flow Control State */
+#define HMB_FC_CHANGE	(1 << 1)	/* To Host Mailbox Flow Control State Changed */
+#define HMB_FRAME_IND	(1 << 2)	/* To Host Mailbox Frame Indication */
+#define HMB_HOST_INT	(1 << 3)	/* To Host Mailbox Miscellaneous Interrupt */
+#define HMB_MASK	0x0000000f	/* To Host Mailbox Mask */
+
+/* tohostmailboxdata */
+#define HMB_DATA_NAKHANDLED	1	/* we're ready to retransmit NAK'd frame to host */
+#define HMB_DATA_DEVREADY	2	/* we're ready to to talk to host after enable */
+#define HMB_DATA_FC		4	/* per prio flowcontrol update flag to host */
+#define HMB_DATA_FWREADY	8	/* firmware is ready for protocol activity */
+
+#define HMB_DATA_FCDATA_MASK	0xff000000	/* per prio flowcontrol data */
+#define HMB_DATA_FCDATA_SHIFT	24		/* per prio flowcontrol data */
+
+#define HMB_DATA_VERSION_MASK	0x00ff0000	/* device protocol version (with devready) */
+#define HMB_DATA_VERSION_SHIFT	16		/* device protocol version (with devready) */
+
+/*
+ * Software-defined protocol header
+ */
+
+/* Current protocol version */
+#define SDPCM_PROT_VERSION	4
+
+/* SW frame header */
+#define SDPCM_SEQUENCE_MASK		0x000000ff	/* Sequence Number Mask */
+#define SDPCM_PACKET_SEQUENCE(p) (((uint8 *)p)[0] & 0xff) /* p starts w/SW Header */
+
+#define SDPCM_CHANNEL_MASK		0x00000f00	/* Channel Number Mask */
+#define SDPCM_CHANNEL_SHIFT		8		/* Channel Number Shift */
+#define SDPCM_PACKET_CHANNEL(p) (((uint8 *)p)[1] & 0x0f) /* p starts w/SW Header */
+
+#define SDPCM_FLAGS_MASK		0x0000f000	/* Mask of flag bits */
+#define SDPCM_FLAGS_SHIFT		12		/* Flag bits shift */
+#define SDPCM_PACKET_FLAGS(p) ((((uint8 *)p)[1] & 0xf0) >> 4) /* p starts w/SW Header */
+
+/* Next Read Len: lookahead length of next frame, in 16-byte units (rounded up) */
+#define SDPCM_NEXTLEN_MASK		0x00ff0000	/* Next Read Len Mask */
+#define SDPCM_NEXTLEN_SHIFT		16		/* Next Read Len Shift */
+#define SDPCM_NEXTLEN_VALUE(p) ((((uint8 *)p)[2] & 0xff) << 4) /* p starts w/SW Header */
+#define SDPCM_NEXTLEN_OFFSET		2
+
+/* Data Offset from SOF (HW Tag, SW Tag, Pad) */
+#define SDPCM_DOFFSET_OFFSET		3		/* Data Offset */
+#define SDPCM_DOFFSET_VALUE(p) 		(((uint8 *)p)[SDPCM_DOFFSET_OFFSET] & 0xff)
+#define SDPCM_DOFFSET_MASK		0xff000000
+#define SDPCM_DOFFSET_SHIFT		24
+
+#define SDPCM_FCMASK_OFFSET		4		/* Flow control */
+#define SDPCM_FCMASK_VALUE(p)		(((uint8 *)p)[SDPCM_FCMASK_OFFSET ] & 0xff)
+#define SDPCM_WINDOW_OFFSET		5		/* Credit based fc */
+#define SDPCM_WINDOW_VALUE(p)		(((uint8 *)p)[SDPCM_WINDOW_OFFSET] & 0xff)
+#define SDPCM_VERSION_OFFSET		6		/* Version # */
+#define SDPCM_VERSION_VALUE(p)		(((uint8 *)p)[SDPCM_VERSION_OFFSET] & 0xff)
+#define SDPCM_UNUSED_OFFSET		7		/* Spare */
+#define SDPCM_UNUSED_VALUE(p)		(((uint8 *)p)[SDPCM_UNUSED_OFFSET] & 0xff)
+
+#define SDPCM_SWHEADER_LEN	8	/* SW header is 64 bits */
+
+/* logical channel numbers */
+#define SDPCM_CONTROL_CHANNEL	0	/* Control Request/Response Channel Id */
+#define SDPCM_EVENT_CHANNEL	1	/* Asyc Event Indication Channel Id */
+#define SDPCM_DATA_CHANNEL	2	/* Data Xmit/Recv Channel Id */
+#define SDPCM_GLOM_CHANNEL	3	/* For coalesced packets (superframes) */
+#define SDPCM_TEST_CHANNEL	15	/* Reserved for test/debug packets */
+#define SDPCM_MAX_CHANNEL	15
+
+#define SDPCM_SEQUENCE_WRAP	256	/* wrap-around val for eight-bit frame seq number */
+
+#define SDPCM_FLAG_RESVD0	0x01
+#define SDPCM_FLAG_RESVD1	0x02
+#define SDPCM_FLAG_GSPI_TXENAB	0x04
+#define SDPCM_FLAG_GLOMDESC	0x08	/* Superframe descriptor mask */
+
+/* For GLOM_CHANNEL frames, use a flag to indicate descriptor frame */
+#define SDPCM_GLOMDESC_FLAG	(SDPCM_FLAG_GLOMDESC << SDPCM_FLAGS_SHIFT)
+
+#define SDPCM_GLOMDESC(p)	(((uint8 *)p)[1] & 0x80)
+
+/* For TEST_CHANNEL packets, define another 4-byte header */
+#define SDPCM_TEST_HDRLEN	4	/* Generally: Cmd(1), Ext(1), Len(2);
+					 * Semantics of Ext byte depend on command.
+					 * Len is current or requested frame length, not
+					 * including test header; sent little-endian.
+					 */
+#define SDPCM_TEST_DISCARD	0x01	/* Receiver discards. Ext is a pattern id. */
+#define SDPCM_TEST_ECHOREQ	0x02	/* Echo request. Ext is a pattern id. */
+#define SDPCM_TEST_ECHORSP	0x03	/* Echo response. Ext is a pattern id. */
+#define SDPCM_TEST_BURST	0x04	/* Receiver to send a burst. Ext is a frame count */
+#define SDPCM_TEST_SEND		0x05	/* Receiver sets send mode. Ext is boolean on/off */
+
+/* Handy macro for filling in datagen packets with a pattern */
+#define SDPCM_TEST_FILL(byteno, id)	((uint8)(id + byteno))
+
+/*
+ * Software counters (first part matches hardware counters)
+ */
+
+typedef volatile struct {
+	uint32 cmd52rd;		/* Cmd52RdCount, SDIO: cmd52 reads */
+	uint32 cmd52wr;		/* Cmd52WrCount, SDIO: cmd52 writes */
+	uint32 cmd53rd;		/* Cmd53RdCount, SDIO: cmd53 reads */
+	uint32 cmd53wr;		/* Cmd53WrCount, SDIO: cmd53 writes */
+	uint32 abort;		/* AbortCount, SDIO: aborts */
+	uint32 datacrcerror;	/* DataCrcErrorCount, SDIO: frames w/CRC error */
+	uint32 rdoutofsync;	/* RdOutOfSyncCount, SDIO/PCMCIA: Rd Frm out of sync */
+	uint32 wroutofsync;	/* RdOutOfSyncCount, SDIO/PCMCIA: Wr Frm out of sync */
+	uint32 writebusy;	/* WriteBusyCount, SDIO: device asserted "busy" */
+	uint32 readwait;	/* ReadWaitCount, SDIO: no data ready for a read cmd */
+	uint32 readterm;	/* ReadTermCount, SDIO: read frame termination cmds */
+	uint32 writeterm;	/* WriteTermCount, SDIO: write frames termination cmds */
+	uint32 rxdescuflo;	/* receive descriptor underflows */
+	uint32 rxfifooflo;	/* receive fifo overflows */
+	uint32 txfifouflo;	/* transmit fifo underflows */
+	uint32 runt;		/* runt (too short) frames recv'd from bus */
+	uint32 badlen;		/* frame's rxh len does not match its hw tag len */
+	uint32 badcksum;	/* frame's hw tag chksum doesn't agree with len value */
+	uint32 seqbreak;	/* break in sequence # space from one rx frame to the next */
+	uint32 rxfcrc;		/* frame rx header indicates crc error */
+	uint32 rxfwoos;		/* frame rx header indicates write out of sync */
+	uint32 rxfwft;		/* frame rx header indicates write frame termination */
+	uint32 rxfabort;	/* frame rx header indicates frame aborted */
+	uint32 woosint;		/* write out of sync interrupt */
+	uint32 roosint;		/* read out of sync interrupt */
+	uint32 rftermint;	/* read frame terminate interrupt */
+	uint32 wftermint;	/* write frame terminate interrupt */
+} sdpcmd_cnt_t;
+
+/*
+ * Register Access Macros
+ */
+
+#define SDIODREV_IS(var, val)	((var) == (val))
+#define SDIODREV_GE(var, val)	((var) >= (val))
+#define SDIODREV_GT(var, val)	((var) > (val))
+#define SDIODREV_LT(var, val)	((var) < (val))
+#define SDIODREV_LE(var, val)	((var) <= (val))
+
+#define SDIODDMAREG32(h, dir, chnl) \
+	((dir) == DMA_TX ? \
+	 (void *)(uintptr)&((h)->regs->dma.sdiod32.dma32regs[chnl].xmt) : \
+	 (void *)(uintptr)&((h)->regs->dma.sdiod32.dma32regs[chnl].rcv))
+
+#define SDIODDMAREG64(h, dir, chnl) \
+	((dir) == DMA_TX ? \
+	 (void *)(uintptr)&((h)->regs->dma.sdiod64.dma64regs[chnl].xmt) : \
+	 (void *)(uintptr)&((h)->regs->dma.sdiod64.dma64regs[chnl].rcv))
+
+#define SDIODDMAREG(h, dir, chnl) \
+	(SDIODREV_LT((h)->corerev, 1) ? \
+	 SDIODDMAREG32((h), (dir), (chnl)) : \
+	 SDIODDMAREG64((h), (dir), (chnl)))
+
+#define PCMDDMAREG(h, dir, chnl) \
+	((dir) == DMA_TX ? \
+	 (void *)(uintptr)&((h)->regs->dma.pcm32.dmaregs.xmt) : \
+	 (void *)(uintptr)&((h)->regs->dma.pcm32.dmaregs.rcv))
+
+#define SDPCMDMAREG(h, dir, chnl, coreid) \
+	((coreid) == SDIOD_CORE_ID ? \
+	 SDIODDMAREG(h, dir, chnl) : \
+	 PCMDDMAREG(h, dir, chnl))
+
+#define SDIODFIFOREG(h, corerev) \
+	(SDIODREV_LT((corerev), 1) ? \
+	 ((dma32diag_t *)(uintptr)&((h)->regs->dma.sdiod32.dmafifo)) : \
+	 ((dma32diag_t *)(uintptr)&((h)->regs->dma.sdiod64.dmafifo)))
+
+#define PCMDFIFOREG(h) \
+	((dma32diag_t *)(uintptr)&((h)->regs->dma.pcm32.dmafifo))
+
+#define SDPCMFIFOREG(h, coreid, corerev) \
+	((coreid) == SDIOD_CORE_ID ? \
+	 SDIODFIFOREG(h, corerev) : \
+	 PCMDFIFOREG(h))
+
+/*
+ * Shared structure between dongle and the host
+ * The structure contains pointers to trap or assert information shared with the host
+ */
+#define SDPCM_SHARED_VERSION       0x0002
+#define SDPCM_SHARED_VERSION_MASK  0x00FF
+#define SDPCM_SHARED_ASSERT_BUILT  0x0100
+#define SDPCM_SHARED_ASSERT        0x0200
+#define SDPCM_SHARED_TRAP          0x0400
+
+typedef struct {
+	uint32	flags;
+	uint32  trap_addr;
+	uint32  assert_exp_addr;
+	uint32  assert_file_addr;
+	uint32  assert_line;
+	uint32	console_addr;		/* Address of hndrte_cons_t */
+	uint32  msgtrace_addr;
+	uint8   tag[32];
+} sdpcm_shared_t;
+
+extern sdpcm_shared_t sdpcm_shared;
+
+#endif	/* _bcmsdpcm_h_ */
diff --git a/drivers/net/wireless/bcm4329/include/bcmsdspi.h b/drivers/net/wireless/bcm4329/include/bcmsdspi.h
new file mode 100644
index 0000000..eaae10d
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/bcmsdspi.h
@@ -0,0 +1,131 @@
+/*
+ * SD-SPI Protocol Conversion - BCMSDH->SPI Translation Layer
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmsdspi.h,v 13.8.10.2 2008/06/30 21:09:40 Exp $
+ */
+
+/* global msglevel for debug messages - bitvals come from sdiovar.h */
+
+#define sd_err(x)
+#define sd_trace(x)
+#define sd_info(x)
+#define sd_debug(x)
+#define sd_data(x)
+#define sd_ctrl(x)
+
+#define sd_log(x)
+
+#define SDIOH_ASSERT(exp) \
+	do { if (!(exp)) \
+		printf("!!!ASSERT fail: file %s lines %d", __FILE__, __LINE__); \
+	} while (0)
+
+#define BLOCK_SIZE_4318 64
+#define BLOCK_SIZE_4328 512
+
+/* internal return code */
+#define SUCCESS	0
+#undef ERROR
+#define ERROR	1
+
+/* private bus modes */
+#define SDIOH_MODE_SPI		0
+
+#define USE_BLOCKMODE		0x2	/* Block mode can be single block or multi */
+#define USE_MULTIBLOCK		0x4
+
+struct sdioh_info {
+	uint cfg_bar;                   	/* pci cfg address for bar */
+	uint32 caps;                    	/* cached value of capabilities reg */
+	uint		bar0;			/* BAR0 for PCI Device */
+	osl_t 		*osh;			/* osh handler */
+	void		*controller;	/* Pointer to SPI Controller's private data struct */
+
+	uint		lockcount; 		/* nest count of sdspi_lock() calls */
+	bool		client_intr_enabled;	/* interrupt connnected flag */
+	bool		intr_handler_valid;	/* client driver interrupt handler valid */
+	sdioh_cb_fn_t	intr_handler;		/* registered interrupt handler */
+	void		*intr_handler_arg;	/* argument to call interrupt handler */
+	bool		initialized;		/* card initialized */
+	uint32		target_dev;		/* Target device ID */
+	uint32		intmask;		/* Current active interrupts */
+	void		*sdos_info;		/* Pointer to per-OS private data */
+
+	uint32		controller_type;	/* Host controller type */
+	uint8		version;		/* Host Controller Spec Compliance Version */
+	uint 		irq;			/* Client irq */
+	uint32 		intrcount;		/* Client interrupts */
+	uint32 		local_intrcount;	/* Controller interrupts */
+	bool 		host_init_done;		/* Controller initted */
+	bool 		card_init_done;		/* Client SDIO interface initted */
+	bool 		polled_mode;		/* polling for command completion */
+
+	bool		sd_use_dma;		/* DMA on CMD53 */
+	bool 		sd_blockmode;		/* sd_blockmode == FALSE => 64 Byte Cmd 53s. */
+						/*  Must be on for sd_multiblock to be effective */
+	bool 		use_client_ints;	/* If this is false, make sure to restore */
+	bool		got_hcint;		/* Host Controller interrupt. */
+						/*  polling hack in wl_linux.c:wl_timer() */
+	int 		adapter_slot;		/* Maybe dealing with multiple slots/controllers */
+	int 		sd_mode;		/* SD1/SD4/SPI */
+	int 		client_block_size[SDIOD_MAX_IOFUNCS];		/* Blocksize */
+	uint32 		data_xfer_count;	/* Current register transfer size */
+	uint32		cmd53_wr_data;		/* Used to pass CMD53 write data */
+	uint32		card_response;		/* Used to pass back response status byte */
+	uint32		card_rsp_data;		/* Used to pass back response data word */
+	uint16 		card_rca;		/* Current Address */
+	uint8 		num_funcs;		/* Supported funcs on client */
+	uint32 		com_cis_ptr;
+	uint32 		func_cis_ptr[SDIOD_MAX_IOFUNCS];
+	void		*dma_buf;
+	ulong		dma_phys;
+	int 		r_cnt;			/* rx count */
+	int 		t_cnt;			/* tx_count */
+};
+
+/************************************************************
+ * Internal interfaces: per-port references into bcmsdspi.c
+ */
+
+/* Global message bits */
+extern uint sd_msglevel;
+
+/**************************************************************
+ * Internal interfaces: bcmsdspi.c references to per-port code
+ */
+
+/* Register mapping routines */
+extern uint32 *spi_reg_map(osl_t *osh, uintptr addr, int size);
+extern void spi_reg_unmap(osl_t *osh, uintptr addr, int size);
+
+/* Interrupt (de)registration routines */
+extern int spi_register_irq(sdioh_info_t *sd, uint irq);
+extern void spi_free_irq(uint irq, sdioh_info_t *sd);
+
+/* OS-specific interrupt wrappers (atomic interrupt enable/disable) */
+extern void spi_lock(sdioh_info_t *sd);
+extern void spi_unlock(sdioh_info_t *sd);
+
+/* Allocate/init/free per-OS private data */
+extern int spi_osinit(sdioh_info_t *sd);
+extern void spi_osfree(sdioh_info_t *sd);
diff --git a/drivers/net/wireless/bcm4329/include/bcmsdstd.h b/drivers/net/wireless/bcm4329/include/bcmsdstd.h
new file mode 100644
index 0000000..974b3d4
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/bcmsdstd.h
@@ -0,0 +1,223 @@
+/*
+ *  'Standard' SDIO HOST CONTROLLER driver
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmsdstd.h,v 13.16.18.1.16.3 2009/12/10 01:09:23 Exp $
+ */
+
+/* global msglevel for debug messages - bitvals come from sdiovar.h */
+
+#define sd_err(x)	do { if (sd_msglevel & SDH_ERROR_VAL) printf x; } while (0)
+#define sd_trace(x)
+#define sd_info(x)
+#define sd_debug(x)
+#define sd_data(x)
+#define sd_ctrl(x)
+#define sd_dma(x)
+
+#define sd_sync_dma(sd, read, nbytes)
+#define sd_init_dma(sd)
+#define sd_ack_intr(sd)
+#define sd_wakeup(sd);
+/* Allocate/init/free per-OS private data */
+extern int sdstd_osinit(sdioh_info_t *sd);
+extern void sdstd_osfree(sdioh_info_t *sd);
+
+#define sd_log(x)
+
+#define SDIOH_ASSERT(exp) \
+	do { if (!(exp)) \
+		printf("!!!ASSERT fail: file %s lines %d", __FILE__, __LINE__); \
+	} while (0)
+
+#define BLOCK_SIZE_4318 64
+#define BLOCK_SIZE_4328 512
+
+/* internal return code */
+#define SUCCESS	0
+#define ERROR	1
+
+/* private bus modes */
+#define SDIOH_MODE_SPI		0
+#define SDIOH_MODE_SD1		1
+#define SDIOH_MODE_SD4		2
+
+#define MAX_SLOTS 6 	/* For PCI: Only 6 BAR entries => 6 slots */
+#define SDIOH_REG_WINSZ	0x100 /* Number of registers in Standard Host Controller */
+
+#define SDIOH_TYPE_ARASAN_HDK	1
+#define SDIOH_TYPE_BCM27XX	2
+#define SDIOH_TYPE_TI_PCIXX21	4	/* TI PCIxx21 Standard Host Controller */
+#define SDIOH_TYPE_RICOH_R5C822	5	/* Ricoh Co Ltd R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter */
+#define SDIOH_TYPE_JMICRON	6	/* JMicron Standard SDIO Host Controller */
+
+/* For linux, allow yielding for dongle */
+#define BCMSDYIELD
+
+/* Expected card status value for CMD7 */
+#define SDIOH_CMD7_EXP_STATUS   0x00001E00
+
+#define RETRIES_LARGE 100000
+#define RETRIES_SMALL 100
+
+
+#define USE_BLOCKMODE		0x2	/* Block mode can be single block or multi */
+#define USE_MULTIBLOCK		0x4
+
+#define USE_FIFO		0x8	/* Fifo vs non-fifo */
+
+#define CLIENT_INTR 		0x100	/* Get rid of this! */
+
+
+struct sdioh_info {
+	uint cfg_bar;                   	/* pci cfg address for bar */
+	uint32 caps;                    	/* cached value of capabilities reg */
+	uint32 curr_caps;                    	/* max current capabilities reg */
+
+	osl_t 		*osh;			/* osh handler */
+	volatile char 	*mem_space;		/* pci device memory va */
+	uint		lockcount; 		/* nest count of sdstd_lock() calls */
+	bool		client_intr_enabled;	/* interrupt connnected flag */
+	bool		intr_handler_valid;	/* client driver interrupt handler valid */
+	sdioh_cb_fn_t	intr_handler;		/* registered interrupt handler */
+	void		*intr_handler_arg;	/* argument to call interrupt handler */
+	bool		initialized;		/* card initialized */
+	uint		target_dev;		/* Target device ID */
+	uint16		intmask;		/* Current active interrupts */
+	void		*sdos_info;		/* Pointer to per-OS private data */
+
+	uint32		controller_type;	/* Host controller type */
+	uint8		version;		/* Host Controller Spec Compliance Version */
+	uint 		irq;			/* Client irq */
+	int 		intrcount;		/* Client interrupts */
+	int 		local_intrcount;	/* Controller interrupts */
+	bool 		host_init_done;		/* Controller initted */
+	bool 		card_init_done;		/* Client SDIO interface initted */
+	bool 		polled_mode;		/* polling for command completion */
+
+	bool 		sd_blockmode;		/* sd_blockmode == FALSE => 64 Byte Cmd 53s. */
+						/*  Must be on for sd_multiblock to be effective */
+	bool 		use_client_ints;	/* If this is false, make sure to restore */
+						/*  polling hack in wl_linux.c:wl_timer() */
+	int 		adapter_slot;		/* Maybe dealing with multiple slots/controllers */
+	int 		sd_mode;		/* SD1/SD4/SPI */
+	int 		client_block_size[SDIOD_MAX_IOFUNCS];		/* Blocksize */
+	uint32 		data_xfer_count;	/* Current transfer */
+	uint16 		card_rca;		/* Current Address */
+	int8		sd_dma_mode;		/* DMA Mode (PIO, SDMA, ... ADMA2) on CMD53 */
+	uint8 		num_funcs;		/* Supported funcs on client */
+	uint32 		com_cis_ptr;
+	uint32 		func_cis_ptr[SDIOD_MAX_IOFUNCS];
+	void		*dma_buf;		/* DMA Buffer virtual address */
+	ulong		dma_phys;		/* DMA Buffer physical address */
+	void		*adma2_dscr_buf;	/* ADMA2 Descriptor Buffer virtual address */
+	ulong		adma2_dscr_phys;	/* ADMA2 Descriptor Buffer physical address */
+
+	/* adjustments needed to make the dma align properly */
+	void		*dma_start_buf;
+	ulong		dma_start_phys;
+	uint		alloced_dma_size;
+	void		*adma2_dscr_start_buf;
+	ulong		adma2_dscr_start_phys;
+	uint		alloced_adma2_dscr_size;
+
+	int 		r_cnt;			/* rx count */
+	int 		t_cnt;			/* tx_count */
+	bool		got_hcint;		/* local interrupt flag */
+	uint16		last_intrstatus;	/* to cache intrstatus */
+};
+
+#define DMA_MODE_NONE	0
+#define DMA_MODE_SDMA	1
+#define DMA_MODE_ADMA1	2
+#define DMA_MODE_ADMA2	3
+#define DMA_MODE_ADMA2_64 4
+#define DMA_MODE_AUTO	-1
+
+#define USE_DMA(sd)		((bool)((sd->sd_dma_mode > 0) ? TRUE : FALSE))
+
+/* SDIO Host Control Register DMA Mode Definitions */
+#define SDIOH_SDMA_MODE			0
+#define SDIOH_ADMA1_MODE		1
+#define SDIOH_ADMA2_MODE		2
+#define SDIOH_ADMA2_64_MODE		3
+
+#define ADMA2_ATTRIBUTE_VALID		(1 << 0)	/* ADMA Descriptor line valid */
+#define ADMA2_ATTRIBUTE_END			(1 << 1)	/* End of Descriptor */
+#define ADMA2_ATTRIBUTE_INT			(1 << 2)	/* Interrupt when line is done */
+#define ADMA2_ATTRIBUTE_ACT_NOP		(0 << 4)	/* Skip current line, go to next. */
+#define ADMA2_ATTRIBUTE_ACT_RSV		(1 << 4)	/* Same as NOP */
+#define ADMA1_ATTRIBUTE_ACT_SET		(1 << 4)	/* ADMA1 Only - set transfer length */
+#define ADMA2_ATTRIBUTE_ACT_TRAN	(2 << 4)	/* Transfer Data of one descriptor line. */
+#define ADMA2_ATTRIBUTE_ACT_LINK	(3 << 4)	/* Link Descriptor */
+
+/* ADMA2 Descriptor Table Entry for 32-bit Address */
+typedef struct adma2_dscr_32b {
+	uint32 len_attr;
+	uint32 phys_addr;
+} adma2_dscr_32b_t;
+
+/* ADMA1 Descriptor Table Entry */
+typedef struct adma1_dscr {
+	uint32 phys_addr_attr;
+} adma1_dscr_t;
+
+/************************************************************
+ * Internal interfaces: per-port references into bcmsdstd.c
+ */
+
+/* Global message bits */
+extern uint sd_msglevel;
+
+/* OS-independent interrupt handler */
+extern bool check_client_intr(sdioh_info_t *sd);
+
+/* Core interrupt enable/disable of device interrupts */
+extern void sdstd_devintr_on(sdioh_info_t *sd);
+extern void sdstd_devintr_off(sdioh_info_t *sd);
+
+/* Enable/disable interrupts for local controller events */
+extern void sdstd_intrs_on(sdioh_info_t *sd, uint16 norm, uint16 err);
+extern void sdstd_intrs_off(sdioh_info_t *sd, uint16 norm, uint16 err);
+
+/* Wait for specified interrupt and error bits to be set */
+extern void sdstd_spinbits(sdioh_info_t *sd, uint16 norm, uint16 err);
+
+
+/**************************************************************
+ * Internal interfaces: bcmsdstd.c references to per-port code
+ */
+
+/* Register mapping routines */
+extern uint32 *sdstd_reg_map(osl_t *osh, int32 addr, int size);
+extern void sdstd_reg_unmap(osl_t *osh, int32 addr, int size);
+
+/* Interrupt (de)registration routines */
+extern int sdstd_register_irq(sdioh_info_t *sd, uint irq);
+extern void sdstd_free_irq(uint irq, sdioh_info_t *sd);
+
+/* OS-specific interrupt wrappers (atomic interrupt enable/disable) */
+extern void sdstd_lock(sdioh_info_t *sd);
+extern void sdstd_unlock(sdioh_info_t *sd);
+
+/* OS-specific wait-for-interrupt-or-status */
+extern uint16 sdstd_waitbits(sdioh_info_t *sd, uint16 norm, uint16 err, bool yield);
diff --git a/drivers/net/wireless/bcm4329/include/bcmspi.h b/drivers/net/wireless/bcm4329/include/bcmspi.h
new file mode 100644
index 0000000..2e2bc93
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/bcmspi.h
@@ -0,0 +1,36 @@
+/*
+ * Broadcom SPI Low-Level Hardware Driver API
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmspi.h,v 13.3.10.2 2008/06/30 21:09:40 Exp $
+ */
+
+extern void spi_devintr_off(sdioh_info_t *sd);
+extern void spi_devintr_on(sdioh_info_t *sd);
+extern bool spi_start_clock(sdioh_info_t *sd, uint16 new_sd_divisor);
+extern bool spi_controller_highspeed_mode(sdioh_info_t *sd, bool hsmode);
+extern bool spi_check_client_intr(sdioh_info_t *sd, int *is_dev_intr);
+extern bool spi_hw_attach(sdioh_info_t *sd);
+extern bool spi_hw_detach(sdioh_info_t *sd);
+extern void spi_sendrecv(sdioh_info_t *sd, uint8 *msg_out, uint8 *msg_in, int msglen);
+extern void spi_spinbits(sdioh_info_t *sd);
+extern void spi_waitbits(sdioh_info_t *sd, bool yield);
diff --git a/drivers/net/wireless/bcm4329/include/bcmspibrcm.h b/drivers/net/wireless/bcm4329/include/bcmspibrcm.h
new file mode 100644
index 0000000..9dce878
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/bcmspibrcm.h
@@ -0,0 +1,134 @@
+/*
+ * SD-SPI Protocol Conversion - BCMSDH->gSPI Translation Layer
+ *
+ * Copyright (C) 2010, Broadcom Corporation
+ * All Rights Reserved.
+ * 
+ * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation;
+ * the contents of this file may not be disclosed to third parties, copied
+ * or duplicated in any form, in whole or in part, without the prior
+ * written permission of Broadcom Corporation.
+ *
+ * $Id: bcmspibrcm.h,v 1.4.4.1.4.3.6.1 2008/09/27 17:03:25 Exp $
+ */
+
+/* global msglevel for debug messages - bitvals come from sdiovar.h */
+
+#define sd_err(x)
+#define sd_trace(x)
+#define sd_info(x)
+#define sd_debug(x)
+#define sd_data(x)
+#define sd_ctrl(x)
+
+#define sd_log(x)
+
+#define SDIOH_ASSERT(exp) \
+	do { if (!(exp)) \
+		printf("!!!ASSERT fail: file %s lines %d", __FILE__, __LINE__); \
+	} while (0)
+
+#define BLOCK_SIZE_F1		64
+#define BLOCK_SIZE_F2 		2048
+#define BLOCK_SIZE_F3 		2048
+
+/* internal return code */
+#define SUCCESS	0
+#undef ERROR
+#define ERROR	1
+#define ERROR_UF	2
+#define ERROR_OF	3
+
+/* private bus modes */
+#define SDIOH_MODE_SPI		0
+
+#define USE_BLOCKMODE		0x2	/* Block mode can be single block or multi */
+#define USE_MULTIBLOCK		0x4
+
+struct sdioh_info {
+	uint 		cfg_bar;		/* pci cfg address for bar */
+	uint32		caps;			/* cached value of capabilities reg */
+	void		*bar0;			/* BAR0 for PCI Device */
+	osl_t 		*osh;			/* osh handler */
+	void		*controller;	/* Pointer to SPI Controller's private data struct */
+
+	uint		lockcount; 		/* nest count of spi_lock() calls */
+	bool		client_intr_enabled;	/* interrupt connnected flag */
+	bool		intr_handler_valid;	/* client driver interrupt handler valid */
+	sdioh_cb_fn_t	intr_handler;		/* registered interrupt handler */
+	void		*intr_handler_arg;	/* argument to call interrupt handler */
+	bool		initialized;		/* card initialized */
+	uint32		target_dev;		/* Target device ID */
+	uint32		intmask;		/* Current active interrupts */
+	void		*sdos_info;		/* Pointer to per-OS private data */
+
+	uint32		controller_type;	/* Host controller type */
+	uint8		version;		/* Host Controller Spec Compliance Version */
+	uint 		irq;			/* Client irq */
+	uint32 		intrcount;		/* Client interrupts */
+	uint32 		local_intrcount;	/* Controller interrupts */
+	bool 		host_init_done;		/* Controller initted */
+	bool 		card_init_done;		/* Client SDIO interface initted */
+	bool 		polled_mode;		/* polling for command completion */
+
+	bool		sd_use_dma;		/* DMA on CMD53 */
+	bool 		sd_blockmode;		/* sd_blockmode == FALSE => 64 Byte Cmd 53s. */
+						/*  Must be on for sd_multiblock to be effective */
+	bool 		use_client_ints;	/* If this is false, make sure to restore */
+						/*  polling hack in wl_linux.c:wl_timer() */
+	int 		adapter_slot;		/* Maybe dealing with multiple slots/controllers */
+	int 		sd_mode;		/* SD1/SD4/SPI */
+	int 		client_block_size[SPI_MAX_IOFUNCS];		/* Blocksize */
+	uint32 		data_xfer_count;	/* Current transfer */
+	uint16 		card_rca;		/* Current Address */
+	uint8 		num_funcs;		/* Supported funcs on client */
+	uint32 		card_dstatus;		/* 32bit device status */
+	uint32 		com_cis_ptr;
+	uint32 		func_cis_ptr[SPI_MAX_IOFUNCS];
+	void		*dma_buf;
+	ulong		dma_phys;
+	int 		r_cnt;			/* rx count */
+	int 		t_cnt;			/* tx_count */
+	uint32		wordlen;			/* host processor 16/32bits */
+	uint32		prev_fun;
+	uint32		chip;
+	uint32		chiprev;
+	bool		resp_delay_all;
+	bool		dwordmode;
+
+	struct spierrstats_t spierrstats;
+};
+
+/************************************************************
+ * Internal interfaces: per-port references into bcmspibrcm.c
+ */
+
+/* Global message bits */
+extern uint sd_msglevel;
+
+/**************************************************************
+ * Internal interfaces: bcmspibrcm.c references to per-port code
+ */
+
+/* Interrupt (de)registration routines */
+extern int spi_register_irq(sdioh_info_t *sd, uint irq);
+extern void spi_free_irq(uint irq, sdioh_info_t *sd);
+
+/* OS-specific interrupt wrappers (atomic interrupt enable/disable) */
+extern void spi_lock(sdioh_info_t *sd);
+extern void spi_unlock(sdioh_info_t *sd);
+
+/* Allocate/init/free per-OS private data */
+extern int spi_osinit(sdioh_info_t *sd);
+extern void spi_osfree(sdioh_info_t *sd);
+
+#define SPI_RW_FLAG_M			BITFIELD_MASK(1)	/* Bit [31] - R/W Command Bit */
+#define SPI_RW_FLAG_S			31
+#define SPI_ACCESS_M			BITFIELD_MASK(1)	/* Bit [30] - Fixed/Incr Access */
+#define SPI_ACCESS_S			30
+#define SPI_FUNCTION_M			BITFIELD_MASK(2)	/* Bit [29:28] - Function Number */
+#define SPI_FUNCTION_S			28
+#define SPI_REG_ADDR_M			BITFIELD_MASK(17)	/* Bit [27:11] - Address */
+#define SPI_REG_ADDR_S			11
+#define SPI_LEN_M			BITFIELD_MASK(11)	/* Bit [10:0] - Packet length */
+#define SPI_LEN_S			0
diff --git a/drivers/net/wireless/bcm4329/include/bcmutils.h b/drivers/net/wireless/bcm4329/include/bcmutils.h
new file mode 100644
index 0000000..f85ed35
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/bcmutils.h
@@ -0,0 +1,637 @@
+/*
+ * Misc useful os-independent macros and functions.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ * $Id: bcmutils.h,v 13.184.4.6.2.1.18.25 2010/04/26 06:05:24 Exp $
+ */
+
+
+#ifndef	_bcmutils_h_
+#define	_bcmutils_h_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define _BCM_U	0x01	
+#define _BCM_L	0x02	
+#define _BCM_D	0x04	
+#define _BCM_C	0x08	
+#define _BCM_P	0x10	
+#define _BCM_S	0x20	
+#define _BCM_X	0x40	
+#define _BCM_SP	0x80	
+
+extern const unsigned char bcm_ctype[];
+#define bcm_ismask(x)	(bcm_ctype[(int)(unsigned char)(x)])
+
+#define bcm_isalnum(c)	((bcm_ismask(c)&(_BCM_U|_BCM_L|_BCM_D)) != 0)
+#define bcm_isalpha(c)	((bcm_ismask(c)&(_BCM_U|_BCM_L)) != 0)
+#define bcm_iscntrl(c)	((bcm_ismask(c)&(_BCM_C)) != 0)
+#define bcm_isdigit(c)	((bcm_ismask(c)&(_BCM_D)) != 0)
+#define bcm_isgraph(c)	((bcm_ismask(c)&(_BCM_P|_BCM_U|_BCM_L|_BCM_D)) != 0)
+#define bcm_islower(c)	((bcm_ismask(c)&(_BCM_L)) != 0)
+#define bcm_isprint(c)	((bcm_ismask(c)&(_BCM_P|_BCM_U|_BCM_L|_BCM_D|_BCM_SP)) != 0)
+#define bcm_ispunct(c)	((bcm_ismask(c)&(_BCM_P)) != 0)
+#define bcm_isspace(c)	((bcm_ismask(c)&(_BCM_S)) != 0)
+#define bcm_isupper(c)	((bcm_ismask(c)&(_BCM_U)) != 0)
+#define bcm_isxdigit(c)	((bcm_ismask(c)&(_BCM_D|_BCM_X)) != 0)
+#define bcm_tolower(c)	(bcm_isupper((c)) ? ((c) + 'a' - 'A') : (c))
+#define bcm_toupper(c)	(bcm_islower((c)) ? ((c) + 'A' - 'a') : (c))
+
+
+
+struct bcmstrbuf {
+	char *buf;	
+	unsigned int size;	
+	char *origbuf;	
+	unsigned int origsize;	
+};
+
+
+#ifdef BCMDRIVER
+#include <osl.h>
+
+#define GPIO_PIN_NOTDEFINED 	0x20	
+
+
+#define SPINWAIT(exp, us) { \
+	uint countdown = (us) + 9; \
+	while ((exp) && (countdown >= 10)) {\
+		OSL_DELAY(10); \
+		countdown -= 10; \
+	} \
+}
+
+
+
+#ifndef PKTQ_LEN_DEFAULT
+#define PKTQ_LEN_DEFAULT        128	
+#endif
+#ifndef PKTQ_MAX_PREC
+#define PKTQ_MAX_PREC           16	
+#endif
+
+typedef struct pktq_prec {
+	void *head;     
+	void *tail;     
+	uint16 len;     
+	uint16 max;     
+} pktq_prec_t;
+
+
+
+struct pktq {
+	uint16 num_prec;        
+	uint16 hi_prec;         
+	uint16 max;             
+	uint16 len;             
+	
+	struct pktq_prec q[PKTQ_MAX_PREC];
+};
+
+
+struct spktq {
+	uint16 num_prec;        
+	uint16 hi_prec;         
+	uint16 max;             
+	uint16 len;             
+	
+	struct pktq_prec q[1];
+};
+
+#define PKTQ_PREC_ITER(pq, prec)        for (prec = (pq)->num_prec - 1; prec >= 0; prec--)
+
+
+
+
+struct ether_addr;
+
+extern int ether_isbcast(const void *ea);
+extern int ether_isnulladdr(const void *ea);
+
+
+
+#define pktq_psetmax(pq, prec, _max)    ((pq)->q[prec].max = (_max))
+#define pktq_plen(pq, prec)             ((pq)->q[prec].len)
+#define pktq_pavail(pq, prec)           ((pq)->q[prec].max - (pq)->q[prec].len)
+#define pktq_pfull(pq, prec)            ((pq)->q[prec].len >= (pq)->q[prec].max)
+#define pktq_pempty(pq, prec)           ((pq)->q[prec].len == 0)
+
+#define pktq_ppeek(pq, prec)            ((pq)->q[prec].head)
+#define pktq_ppeek_tail(pq, prec)       ((pq)->q[prec].tail)
+
+extern void *pktq_penq(struct pktq *pq, int prec, void *p);
+extern void *pktq_penq_head(struct pktq *pq, int prec, void *p);
+extern void *pktq_pdeq(struct pktq *pq, int prec);
+extern void *pktq_pdeq_tail(struct pktq *pq, int prec);
+
+extern bool pktq_pdel(struct pktq *pq, void *p, int prec);
+
+
+extern void pktq_pflush(osl_t *osh, struct pktq *pq, int prec, bool dir);
+
+extern void pktq_flush(osl_t *osh, struct pktq *pq, bool dir);
+
+
+
+extern int pktq_mlen(struct pktq *pq, uint prec_bmp);
+extern void *pktq_mdeq(struct pktq *pq, uint prec_bmp, int *prec_out);
+
+
+
+#define pktq_len(pq)                    ((int)(pq)->len)
+#define pktq_max(pq)                    ((int)(pq)->max)
+#define pktq_avail(pq)                  ((int)((pq)->max - (pq)->len))
+#define pktq_full(pq)                   ((pq)->len >= (pq)->max)
+#define pktq_empty(pq)                  ((pq)->len == 0)
+
+
+#define pktenq(pq, p)		pktq_penq(((struct pktq *)pq), 0, (p))
+#define pktenq_head(pq, p)	pktq_penq_head(((struct pktq *)pq), 0, (p))
+#define pktdeq(pq)		pktq_pdeq(((struct pktq *)pq), 0)
+#define pktdeq_tail(pq)		pktq_pdeq_tail(((struct pktq *)pq), 0)
+#define pktqinit(pq, len) pktq_init(((struct pktq *)pq), 1, len)
+
+extern void pktq_init(struct pktq *pq, int num_prec, int max_len);
+
+extern void *pktq_deq(struct pktq *pq, int *prec_out);
+extern void *pktq_deq_tail(struct pktq *pq, int *prec_out);
+extern void *pktq_peek(struct pktq *pq, int *prec_out);
+extern void *pktq_peek_tail(struct pktq *pq, int *prec_out);
+
+
+
+extern uint pktcopy(osl_t *osh, void *p, uint offset, int len, uchar *buf);
+extern uint pktfrombuf(osl_t *osh, void *p, uint offset, int len, uchar *buf);
+extern uint pkttotlen(osl_t *osh, void *p);
+extern void *pktlast(osl_t *osh, void *p);
+extern uint pktsegcnt(osl_t *osh, void *p);
+
+
+extern uint pktsetprio(void *pkt, bool update_vtag);
+#define	PKTPRIO_VDSCP	0x100		
+#define	PKTPRIO_VLAN	0x200		
+#define	PKTPRIO_UPD	0x400		
+#define	PKTPRIO_DSCP	0x800		
+
+
+extern int bcm_atoi(char *s);
+extern ulong bcm_strtoul(char *cp, char **endp, uint base);
+extern char *bcmstrstr(char *haystack, char *needle);
+extern char *bcmstrcat(char *dest, const char *src);
+extern char *bcmstrncat(char *dest, const char *src, uint size);
+extern ulong wchar2ascii(char *abuf, ushort *wbuf, ushort wbuflen, ulong abuflen);
+char* bcmstrtok(char **string, const char *delimiters, char *tokdelim);
+int bcmstricmp(const char *s1, const char *s2);
+int bcmstrnicmp(const char* s1, const char* s2, int cnt);
+
+
+
+extern char *bcm_ether_ntoa(const struct ether_addr *ea, char *buf);
+extern int bcm_ether_atoe(char *p, struct ether_addr *ea);
+
+
+struct ipv4_addr;
+extern char *bcm_ip_ntoa(struct ipv4_addr *ia, char *buf);
+
+
+extern void bcm_mdelay(uint ms);
+
+extern char *getvar(char *vars, const char *name);
+extern int getintvar(char *vars, const char *name);
+extern uint getgpiopin(char *vars, char *pin_name, uint def_pin);
+#define bcm_perf_enable()
+#define bcmstats(fmt)
+#define	bcmlog(fmt, a1, a2)
+#define	bcmdumplog(buf, size)	*buf = '\0'
+#define	bcmdumplogent(buf, idx)	-1
+
+#define bcmtslog(tstamp, fmt, a1, a2)
+#define bcmprinttslogs()
+#define bcmprinttstamp(us)
+
+
+
+
+typedef struct bcm_iovar {
+	const char *name;	
+	uint16 varid;		
+	uint16 flags;		
+	uint16 type;		
+	uint16 minlen;		
+} bcm_iovar_t;
+
+
+
+
+#define IOV_GET 0 
+#define IOV_SET 1 
+
+
+#define IOV_GVAL(id)		((id)*2)
+#define IOV_SVAL(id)		(((id)*2)+IOV_SET)
+#define IOV_ISSET(actionid)	((actionid & IOV_SET) == IOV_SET)
+
+
+
+extern const bcm_iovar_t *bcm_iovar_lookup(const bcm_iovar_t *table, const char *name);
+extern int bcm_iovar_lencheck(const bcm_iovar_t *table, void *arg, int len, bool set);
+
+#endif	
+
+
+#define IOVT_VOID	0	
+#define IOVT_BOOL	1	
+#define IOVT_INT8	2	
+#define IOVT_UINT8	3	
+#define IOVT_INT16	4	
+#define IOVT_UINT16	5	
+#define IOVT_INT32	6	
+#define IOVT_UINT32	7	
+#define IOVT_BUFFER	8	
+#define BCM_IOVT_VALID(type) (((unsigned int)(type)) <= IOVT_BUFFER)
+
+
+#define BCM_IOV_TYPE_INIT { \
+	"void", \
+	"bool", \
+	"int8", \
+	"uint8", \
+	"int16", \
+	"uint16", \
+	"int32", \
+	"uint32", \
+	"buffer", \
+	"" }
+
+#define BCM_IOVT_IS_INT(type) (\
+	(type == IOVT_BOOL) || \
+	(type == IOVT_INT8) || \
+	(type == IOVT_UINT8) || \
+	(type == IOVT_INT16) || \
+	(type == IOVT_UINT16) || \
+	(type == IOVT_INT32) || \
+	(type == IOVT_UINT32))
+
+
+
+#define BCME_STRLEN 		64	
+#define VALID_BCMERROR(e)  ((e <= 0) && (e >= BCME_LAST))
+
+
+
+
+#define BCME_OK				0	
+#define BCME_ERROR			-1	
+#define BCME_BADARG			-2	
+#define BCME_BADOPTION			-3	
+#define BCME_NOTUP			-4	
+#define BCME_NOTDOWN			-5	
+#define BCME_NOTAP			-6	
+#define BCME_NOTSTA			-7	
+#define BCME_BADKEYIDX			-8	
+#define BCME_RADIOOFF 			-9	
+#define BCME_NOTBANDLOCKED		-10	
+#define BCME_NOCLK			-11	
+#define BCME_BADRATESET			-12	
+#define BCME_BADBAND			-13	
+#define BCME_BUFTOOSHORT		-14	
+#define BCME_BUFTOOLONG			-15	
+#define BCME_BUSY			-16	
+#define BCME_NOTASSOCIATED		-17	
+#define BCME_BADSSIDLEN			-18	
+#define BCME_OUTOFRANGECHAN		-19	
+#define BCME_BADCHAN			-20	
+#define BCME_BADADDR			-21	
+#define BCME_NORESOURCE			-22	
+#define BCME_UNSUPPORTED		-23	
+#define BCME_BADLEN			-24	
+#define BCME_NOTREADY			-25	
+#define BCME_EPERM			-26	
+#define BCME_NOMEM			-27	
+#define BCME_ASSOCIATED			-28	
+#define BCME_RANGE			-29	
+#define BCME_NOTFOUND			-30	
+#define BCME_WME_NOT_ENABLED		-31	
+#define BCME_TSPEC_NOTFOUND		-32	
+#define BCME_ACM_NOTSUPPORTED		-33	
+#define BCME_NOT_WME_ASSOCIATION	-34	
+#define BCME_SDIO_ERROR			-35	
+#define BCME_DONGLE_DOWN		-36	
+#define BCME_VERSION			-37	
+#define BCME_TXFAIL			-38	
+#define BCME_RXFAIL			-39	
+#define BCME_NODEVICE			-40	
+#define BCME_UNFINISHED			-41	
+#define BCME_LAST			BCME_UNFINISHED
+
+
+#define BCMERRSTRINGTABLE {		\
+	"OK",				\
+	"Undefined error",		\
+	"Bad Argument",			\
+	"Bad Option",			\
+	"Not up",			\
+	"Not down",			\
+	"Not AP",			\
+	"Not STA",			\
+	"Bad Key Index",		\
+	"Radio Off",			\
+	"Not band locked",		\
+	"No clock",			\
+	"Bad Rate valueset",		\
+	"Bad Band",			\
+	"Buffer too short",		\
+	"Buffer too long",		\
+	"Busy",				\
+	"Not Associated",		\
+	"Bad SSID len",			\
+	"Out of Range Channel",		\
+	"Bad Channel",			\
+	"Bad Address",			\
+	"Not Enough Resources",		\
+	"Unsupported",			\
+	"Bad length",			\
+	"Not Ready",			\
+	"Not Permitted",		\
+	"No Memory",			\
+	"Associated",			\
+	"Not In Range",			\
+	"Not Found",			\
+	"WME Not Enabled",		\
+	"TSPEC Not Found",		\
+	"ACM Not Supported",		\
+	"Not WME Association",		\
+	"SDIO Bus Error",		\
+	"Dongle Not Accessible",	\
+	"Incorrect version",		\
+	"TX Failure",			\
+	"RX Failure",			\
+	"Device Not Present",		\
+	"Command not finished",		\
+}
+
+#ifndef ABS
+#define	ABS(a)			(((a) < 0)?-(a):(a))
+#endif 
+
+#ifndef MIN
+#define	MIN(a, b)		(((a) < (b))?(a):(b))
+#endif 
+
+#ifndef MAX
+#define	MAX(a, b)		(((a) > (b))?(a):(b))
+#endif 
+
+#define CEIL(x, y)		(((x) + ((y)-1)) / (y))
+#define	ROUNDUP(x, y)		((((x)+((y)-1))/(y))*(y))
+#define	ISALIGNED(a, x)		(((a) & ((x)-1)) == 0)
+#define ALIGN_ADDR(addr, boundary) (void *)(((uintptr)(addr) + (boundary) - 1) \
+	                                         & ~((boundary) - 1))
+#define	ISPOWEROF2(x)		((((x)-1)&(x)) == 0)
+#define VALID_MASK(mask)	!((mask) & ((mask) + 1))
+#ifndef OFFSETOF
+#define	OFFSETOF(type, member)	((uint)(uintptr)&((type *)0)->member)
+#endif 
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(a)		(sizeof(a)/sizeof(a[0]))
+#endif
+
+
+#ifndef setbit
+#ifndef NBBY		      
+#define	NBBY	8	
+#endif 
+#define	setbit(a, i)	(((uint8 *)a)[(i)/NBBY] |= 1<<((i)%NBBY))
+#define	clrbit(a, i)	(((uint8 *)a)[(i)/NBBY] &= ~(1<<((i)%NBBY)))
+#define	isset(a, i)	(((const uint8 *)a)[(i)/NBBY] & (1<<((i)%NBBY)))
+#define	isclr(a, i)	((((const uint8 *)a)[(i)/NBBY] & (1<<((i)%NBBY))) == 0)
+#endif 
+
+#define	NBITS(type)	(sizeof(type) * 8)
+#define NBITVAL(nbits)	(1 << (nbits))
+#define MAXBITVAL(nbits)	((1 << (nbits)) - 1)
+#define	NBITMASK(nbits)	MAXBITVAL(nbits)
+#define MAXNBVAL(nbyte)	MAXBITVAL((nbyte) * 8)
+
+
+#define MUX(pred, true, false) ((pred) ? (true) : (false))
+
+
+#define MODDEC(x, bound) MUX((x) == 0, (bound) - 1, (x) - 1)
+#define MODINC(x, bound) MUX((x) == (bound) - 1, 0, (x) + 1)
+
+
+#define MODDEC_POW2(x, bound) (((x) - 1) & ((bound) - 1))
+#define MODINC_POW2(x, bound) (((x) + 1) & ((bound) - 1))
+
+
+#define MODADD(x, y, bound) \
+    MUX((x) + (y) >= (bound), (x) + (y) - (bound), (x) + (y))
+#define MODSUB(x, y, bound) \
+    MUX(((int)(x)) - ((int)(y)) < 0, (x) - (y) + (bound), (x) - (y))
+
+
+#define MODADD_POW2(x, y, bound) (((x) + (y)) & ((bound) - 1))
+#define MODSUB_POW2(x, y, bound) (((x) - (y)) & ((bound) - 1))
+
+
+#define CRC8_INIT_VALUE  0xff		
+#define CRC8_GOOD_VALUE  0x9f		
+#define CRC16_INIT_VALUE 0xffff		
+#define CRC16_GOOD_VALUE 0xf0b8		
+#define CRC32_INIT_VALUE 0xffffffff	
+#define CRC32_GOOD_VALUE 0xdebb20e3	
+
+
+typedef struct bcm_bit_desc {
+	uint32	bit;
+	const char* name;
+} bcm_bit_desc_t;
+
+
+typedef struct bcm_tlv {
+	uint8	id;
+	uint8	len;
+	uint8	data[1];
+} bcm_tlv_t;
+
+
+#define bcm_valid_tlv(elt, buflen) ((buflen) >= 2 && (int)(buflen) >= (int)(2 + (elt)->len))
+
+
+#define ETHER_ADDR_STR_LEN	18	
+
+
+#ifdef IL_BIGENDIAN
+static INLINE uint32
+load32_ua(uint8 *a)
+{
+	return ((a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3]);
+}
+
+static INLINE void
+store32_ua(uint8 *a, uint32 v)
+{
+	a[0] = (v >> 24) & 0xff;
+	a[1] = (v >> 16) & 0xff;
+	a[2] = (v >> 8) & 0xff;
+	a[3] = v & 0xff;
+}
+
+static INLINE uint16
+load16_ua(uint8 *a)
+{
+	return ((a[0] << 8) | a[1]);
+}
+
+static INLINE void
+store16_ua(uint8 *a, uint16 v)
+{
+	a[0] = (v >> 8) & 0xff;
+	a[1] = v & 0xff;
+}
+
+#else 
+
+static INLINE uint32
+load32_ua(uint8 *a)
+{
+	return ((a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0]);
+}
+
+static INLINE void
+store32_ua(uint8 *a, uint32 v)
+{
+	a[3] = (v >> 24) & 0xff;
+	a[2] = (v >> 16) & 0xff;
+	a[1] = (v >> 8) & 0xff;
+	a[0] = v & 0xff;
+}
+
+static INLINE uint16
+load16_ua(uint8 *a)
+{
+	return ((a[1] << 8) | a[0]);
+}
+
+static INLINE void
+store16_ua(uint8 *a, uint16 v)
+{
+	a[1] = (v >> 8) & 0xff;
+	a[0] = v & 0xff;
+}
+
+#endif 
+
+
+
+static INLINE void
+xor_128bit_block(const uint8 *src1, const uint8 *src2, uint8 *dst)
+{
+	if (
+#ifdef __i386__
+	    1 ||
+#endif
+	    (((uintptr)src1 | (uintptr)src2 | (uintptr)dst) & 3) == 0) {
+		
+		
+		((uint32 *)dst)[0] = ((uint32 *)src1)[0] ^ ((uint32 *)src2)[0];
+		((uint32 *)dst)[1] = ((uint32 *)src1)[1] ^ ((uint32 *)src2)[1];
+		((uint32 *)dst)[2] = ((uint32 *)src1)[2] ^ ((uint32 *)src2)[2];
+		((uint32 *)dst)[3] = ((uint32 *)src1)[3] ^ ((uint32 *)src2)[3];
+	} else {
+		
+		int k;
+		for (k = 0; k < 16; k++)
+			dst[k] = src1[k] ^ src2[k];
+	}
+}
+
+
+
+extern uint8 hndcrc8(uint8 *p, uint nbytes, uint8 crc);
+extern uint16 hndcrc16(uint8 *p, uint nbytes, uint16 crc);
+extern uint32 hndcrc32(uint8 *p, uint nbytes, uint32 crc);
+
+#if defined(DHD_DEBUG) || defined(WLMSG_PRHDRS) || defined(WLMSG_PRPKT) || \
+	defined(WLMSG_ASSOC)
+extern int bcm_format_flags(const bcm_bit_desc_t *bd, uint32 flags, char* buf, int len);
+extern int bcm_format_hex(char *str, const void *bytes, int len);
+extern void prhex(const char *msg, uchar *buf, uint len);
+#endif 
+extern char *bcm_brev_str(uint32 brev, char *buf);
+extern void printbig(char *buf);
+
+
+extern bcm_tlv_t *bcm_next_tlv(bcm_tlv_t *elt, int *buflen);
+extern bcm_tlv_t *bcm_parse_tlvs(void *buf, int buflen, uint key);
+extern bcm_tlv_t *bcm_parse_ordered_tlvs(void *buf, int buflen, uint key);
+
+
+extern const char *bcmerrorstr(int bcmerror);
+
+
+typedef uint32 mbool;
+#define mboolset(mb, bit)		((mb) |= (bit))		
+#define mboolclr(mb, bit)		((mb) &= ~(bit))	
+#define mboolisset(mb, bit)		(((mb) & (bit)) != 0)	
+#define	mboolmaskset(mb, mask, val)	((mb) = (((mb) & ~(mask)) | (val)))
+
+
+extern uint16 bcm_qdbm_to_mw(uint8 qdbm);
+extern uint8 bcm_mw_to_qdbm(uint16 mw);
+
+
+struct fielddesc {
+	const char *nameandfmt;
+	uint32 	offset;
+	uint32 	len;
+};
+
+extern void bcm_binit(struct bcmstrbuf *b, char *buf, uint size);
+extern int bcm_bprintf(struct bcmstrbuf *b, const char *fmt, ...);
+extern void bcm_inc_bytes(uchar *num, int num_bytes, uint8 amount);
+extern int bcm_cmp_bytes(uchar *arg1, uchar *arg2, uint8 nbytes);
+extern void bcm_print_bytes(char *name, const uchar *cdata, int len);
+
+typedef  uint32 (*bcmutl_rdreg_rtn)(void *arg0, uint arg1, uint32 offset);
+extern uint bcmdumpfields(bcmutl_rdreg_rtn func_ptr, void *arg0, uint arg1, struct fielddesc *str,
+                          char *buf, uint32 bufsize);
+
+extern uint bcm_mkiovar(char *name, char *data, uint datalen, char *buf, uint len);
+extern uint bcm_bitcount(uint8 *bitmap, uint bytelength);
+
+#if defined(WLTINYDUMP) || defined(WLMSG_INFORM) || defined(WLMSG_ASSOC) || \
+	defined(WLMSG_PRPKT) || defined(WLMSG_WSEC)
+extern int bcm_format_ssid(char* buf, const uchar ssid[], uint ssid_len);
+#endif 
+
+
+#define SSID_FMT_BUF_LEN	((4 * DOT11_MAX_SSID_LEN) + 1)
+
+#ifdef __cplusplus
+	}
+#endif
+
+#endif	
diff --git a/drivers/net/wireless/bcm4329/include/bcmwifi.h b/drivers/net/wireless/bcm4329/include/bcmwifi.h
new file mode 100644
index 0000000..038aedc
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/bcmwifi.h
@@ -0,0 +1,154 @@
+/*
+ * Misc utility routines for WL and Apps
+ * This header file housing the define and function prototype use by
+ * both the wl driver, tools & Apps.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ * $Id: bcmwifi.h,v 1.15.30.4 2010/03/10 20:10:52 Exp $
+ */
+
+
+#ifndef	_bcmwifi_h_
+#define	_bcmwifi_h_
+
+
+
+typedef uint16 chanspec_t;
+
+
+#define CH_UPPER_SB			0x01
+#define CH_LOWER_SB			0x02
+#define CH_EWA_VALID			0x04
+#define CH_20MHZ_APART			4
+#define CH_10MHZ_APART			2
+#define CH_5MHZ_APART			1
+#define CH_MAX_2G_CHANNEL		14	
+#define WLC_MAX_2G_CHANNEL		CH_MAX_2G_CHANNEL 
+#define	MAXCHANNEL		224	
+
+#define WL_CHANSPEC_CHAN_MASK		0x00ff
+#define WL_CHANSPEC_CHAN_SHIFT		0
+
+#define WL_CHANSPEC_CTL_SB_MASK		0x0300
+#define WL_CHANSPEC_CTL_SB_SHIFT	     8
+#define WL_CHANSPEC_CTL_SB_LOWER	0x0100
+#define WL_CHANSPEC_CTL_SB_UPPER	0x0200
+#define WL_CHANSPEC_CTL_SB_NONE		0x0300
+
+#define WL_CHANSPEC_BW_MASK		0x0C00
+#define WL_CHANSPEC_BW_SHIFT		    10
+#define WL_CHANSPEC_BW_10		0x0400
+#define WL_CHANSPEC_BW_20		0x0800
+#define WL_CHANSPEC_BW_40		0x0C00
+
+#define WL_CHANSPEC_BAND_MASK		0xf000
+#define WL_CHANSPEC_BAND_SHIFT		12
+#define WL_CHANSPEC_BAND_5G		0x1000
+#define WL_CHANSPEC_BAND_2G		0x2000
+#define INVCHANSPEC			255
+
+
+#define WF_CHAN_FACTOR_2_4_G		4814	
+#define WF_CHAN_FACTOR_5_G		10000	
+#define WF_CHAN_FACTOR_4_G		8000	
+
+
+#define LOWER_20_SB(channel)	((channel > CH_10MHZ_APART) ? (channel - CH_10MHZ_APART) : 0)
+#define UPPER_20_SB(channel)	((channel < (MAXCHANNEL - CH_10MHZ_APART)) ? \
+				(channel + CH_10MHZ_APART) : 0)
+#define CHSPEC_WLCBANDUNIT(chspec)	(CHSPEC_IS5G(chspec) ? BAND_5G_INDEX : BAND_2G_INDEX)
+#define CH20MHZ_CHSPEC(channel)	(chanspec_t)((chanspec_t)(channel) | WL_CHANSPEC_BW_20 | \
+				WL_CHANSPEC_CTL_SB_NONE | (((channel) <= CH_MAX_2G_CHANNEL) ? \
+				WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G))
+#define NEXT_20MHZ_CHAN(channel)	((channel < (MAXCHANNEL - CH_20MHZ_APART)) ? \
+					(channel + CH_20MHZ_APART) : 0)
+#define CH40MHZ_CHSPEC(channel, ctlsb)	(chanspec_t) \
+					((channel) | (ctlsb) | WL_CHANSPEC_BW_40 | \
+					((channel) <= CH_MAX_2G_CHANNEL ? WL_CHANSPEC_BAND_2G : \
+					WL_CHANSPEC_BAND_5G))
+#define CHSPEC_CHANNEL(chspec)	((uint8)(chspec & WL_CHANSPEC_CHAN_MASK))
+#define CHSPEC_BAND(chspec)	(chspec & WL_CHANSPEC_BAND_MASK)
+
+#ifdef WL20MHZ_ONLY
+
+#define CHSPEC_CTL_SB(chspec)	WL_CHANSPEC_CTL_SB_NONE
+#define CHSPEC_BW(chspec)	WL_CHANSPEC_BW_20
+#define CHSPEC_IS10(chspec)	0
+#define CHSPEC_IS20(chspec)	1
+#ifndef CHSPEC_IS40
+#define CHSPEC_IS40(chspec)	0
+#endif
+
+#else 
+
+#define CHSPEC_CTL_SB(chspec)	(chspec & WL_CHANSPEC_CTL_SB_MASK)
+#define CHSPEC_BW(chspec)	(chspec & WL_CHANSPEC_BW_MASK)
+#define CHSPEC_IS10(chspec)	((chspec & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_10)
+#define CHSPEC_IS20(chspec)	((chspec & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_20)
+#ifndef CHSPEC_IS40
+#define CHSPEC_IS40(chspec)	(((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_40)
+#endif
+
+#endif 
+
+#define CHSPEC_IS5G(chspec)	((chspec & WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_5G)
+#define CHSPEC_IS2G(chspec)	((chspec & WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_2G)
+#define CHSPEC_SB_NONE(chspec)	((chspec & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_NONE)
+#define CHSPEC_SB_UPPER(chspec)	((chspec & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_UPPER)
+#define CHSPEC_SB_LOWER(chspec)	((chspec & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_LOWER)
+#define CHSPEC_CTL_CHAN(chspec)  ((CHSPEC_SB_LOWER(chspec)) ? \
+				  (LOWER_20_SB(((chspec) & WL_CHANSPEC_CHAN_MASK))) : \
+				  (UPPER_20_SB(((chspec) & WL_CHANSPEC_CHAN_MASK))))
+
+#define CHSPEC2WLC_BAND(chspec) (CHSPEC_IS5G((chspec))? WLC_BAND_5G: WLC_BAND_2G)
+
+#define CHANSPEC_STR_LEN    8
+
+
+#define WLC_MAXRATE	108	
+#define WLC_RATE_1M	2	
+#define WLC_RATE_2M	4	
+#define WLC_RATE_5M5	11	
+#define WLC_RATE_11M	22	
+#define WLC_RATE_6M	12	
+#define WLC_RATE_9M	18	
+#define WLC_RATE_12M	24	
+#define WLC_RATE_18M	36	
+#define WLC_RATE_24M	48	
+#define WLC_RATE_36M	72	
+#define WLC_RATE_48M	96	
+#define WLC_RATE_54M	108	
+
+#define WLC_2G_25MHZ_OFFSET		5	
+
+
+extern char * wf_chspec_ntoa(chanspec_t chspec, char *buf);
+
+
+extern chanspec_t wf_chspec_aton(char *a);
+
+
+extern int wf_mhz2channel(uint freq, uint start_factor);
+
+
+extern int wf_channel2mhz(uint channel, uint start_factor);
+
+#endif	
diff --git a/drivers/net/wireless/bcm4329/include/dhdioctl.h b/drivers/net/wireless/bcm4329/include/dhdioctl.h
new file mode 100644
index 0000000..980a143
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/dhdioctl.h
@@ -0,0 +1,123 @@
+/*
+ * Definitions for ioctls to access DHD iovars.
+ * Based on wlioctl.h (for Broadcom 802.11abg driver).
+ * (Moves towards generic ioctls for BCM drivers/iovars.)
+ *
+ * Definitions subject to change without notice.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: dhdioctl.h,v 13.7.8.1.4.1.16.5 2010/05/21 21:49:38 Exp $
+ */
+
+#ifndef _dhdioctl_h_
+#define	_dhdioctl_h_
+
+#include <typedefs.h>
+
+
+/* require default structure packing */
+#define BWL_DEFAULT_PACKING
+#include <packed_section_start.h>
+
+
+/* Linux network driver ioctl encoding */
+typedef struct dhd_ioctl {
+	uint cmd;	/* common ioctl definition */
+	void *buf;	/* pointer to user buffer */
+	uint len;	/* length of user buffer */
+	bool set;	/* get or set request (optional) */
+	uint used;	/* bytes read or written (optional) */
+	uint needed;	/* bytes needed (optional) */
+	uint driver;	/* to identify target driver */
+} dhd_ioctl_t;
+
+/* per-driver magic numbers */
+#define DHD_IOCTL_MAGIC		0x00444944
+
+/* bump this number if you change the ioctl interface */
+#define DHD_IOCTL_VERSION	1
+
+#define	DHD_IOCTL_MAXLEN	8192		/* max length ioctl buffer required */
+#define	DHD_IOCTL_SMLEN		256		/* "small" length ioctl buffer required */
+
+/* common ioctl definitions */
+#define DHD_GET_MAGIC				0
+#define DHD_GET_VERSION				1
+#define DHD_GET_VAR				2
+#define DHD_SET_VAR				3
+
+/* message levels */
+#define DHD_ERROR_VAL	0x0001
+#define DHD_TRACE_VAL	0x0002
+#define DHD_INFO_VAL	0x0004
+#define DHD_DATA_VAL	0x0008
+#define DHD_CTL_VAL	0x0010
+#define DHD_TIMER_VAL	0x0020
+#define DHD_HDRS_VAL	0x0040
+#define DHD_BYTES_VAL	0x0080
+#define DHD_INTR_VAL	0x0100
+#define DHD_LOG_VAL	0x0200
+#define DHD_GLOM_VAL	0x0400
+#define DHD_EVENT_VAL	0x0800
+#define DHD_BTA_VAL	0x1000
+#define DHD_ISCAN_VAL 0x2000
+
+#ifdef SDTEST
+/* For pktgen iovar */
+typedef struct dhd_pktgen {
+	uint version;		/* To allow structure change tracking */
+	uint freq;		/* Max ticks between tx/rx attempts */
+	uint count;		/* Test packets to send/rcv each attempt */
+	uint print;		/* Print counts every <print> attempts */
+	uint total;		/* Total packets (or bursts) */
+	uint minlen;		/* Minimum length of packets to send */
+	uint maxlen;		/* Maximum length of packets to send */
+	uint numsent;		/* Count of test packets sent */
+	uint numrcvd;		/* Count of test packets received */
+	uint numfail;		/* Count of test send failures */
+	uint mode;		/* Test mode (type of test packets) */
+	uint stop;		/* Stop after this many tx failures */
+} dhd_pktgen_t;
+
+/* Version in case structure changes */
+#define DHD_PKTGEN_VERSION 2
+
+/* Type of test packets to use */
+#define DHD_PKTGEN_ECHO		1 /* Send echo requests */
+#define DHD_PKTGEN_SEND 	2 /* Send discard packets */
+#define DHD_PKTGEN_RXBURST	3 /* Request dongle send N packets */
+#define DHD_PKTGEN_RECV		4 /* Continuous rx from continuous tx dongle */
+#endif /* SDTEST */
+
+/* Enter idle immediately (no timeout) */
+#define DHD_IDLE_IMMEDIATE	(-1)
+
+/* Values for idleclock iovar: other values are the sd_divisor to use when idle */
+#define DHD_IDLE_ACTIVE	0	/* Do not request any SD clock change when idle */
+#define DHD_IDLE_STOP   (-1)	/* Request SD clock be stopped (and use SD1 mode) */
+
+
+/* require default structure packing */
+#include <packed_section_end.h>
+
+
+#endif /* _dhdioctl_h_ */
diff --git a/drivers/net/wireless/bcm4329/include/epivers.h b/drivers/net/wireless/bcm4329/include/epivers.h
new file mode 100644
index 0000000..cd66a95
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/epivers.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: epivers.h.in,v 13.25 2005/10/28 18:35:33 Exp $
+ *
+*/
+
+
+#ifndef _epivers_h_
+#define _epivers_h_
+
+#define	EPI_MAJOR_VERSION	4
+
+#define	EPI_MINOR_VERSION	218
+
+#define	EPI_RC_NUMBER		248
+
+#define	EPI_INCREMENTAL_NUMBER	23
+
+#define	EPI_BUILD_NUMBER	0
+
+#define	EPI_VERSION		4, 218, 248, 23
+
+#define	EPI_VERSION_NUM		0x04daf817
+
+
+#define	EPI_VERSION_STR		"4.218.248.23"
+#define	EPI_ROUTER_VERSION_STR	"4.219.248.23"
+
+#endif 
diff --git a/drivers/net/wireless/bcm4329/include/hndpmu.h b/drivers/net/wireless/bcm4329/include/hndpmu.h
new file mode 100644
index 0000000..e829b3d
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/hndpmu.h
@@ -0,0 +1,34 @@
+/*
+ * HND SiliconBackplane PMU support.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: hndpmu.h,v 13.14.4.3.4.3.8.7 2010/04/09 13:20:51 Exp $
+ */
+
+#ifndef _hndpmu_h_
+#define _hndpmu_h_
+
+
+extern void si_pmu_otp_power(si_t *sih, osl_t *osh, bool on);
+extern void si_sdiod_drive_strength_init(si_t *sih, osl_t *osh, uint32 drivestrength);
+
+#endif /* _hndpmu_h_ */
diff --git a/drivers/net/wireless/bcm4329/include/hndrte_armtrap.h b/drivers/net/wireless/bcm4329/include/hndrte_armtrap.h
new file mode 100644
index 0000000..ca3281b
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/hndrte_armtrap.h
@@ -0,0 +1,88 @@
+/*
+ * HNDRTE arm trap handling.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: hndrte_armtrap.h,v 13.3.196.2 2010/07/15 19:06:11 Exp $
+ */
+
+#ifndef	_hndrte_armtrap_h
+#define	_hndrte_armtrap_h
+
+
+/* ARM trap handling */
+
+/* Trap types defined by ARM (see arminc.h) */
+
+/* Trap locations in lo memory */
+#define	TRAP_STRIDE	4
+#define FIRST_TRAP	TR_RST
+#define LAST_TRAP	(TR_FIQ * TRAP_STRIDE)
+
+#if defined(__ARM_ARCH_4T__)
+#define	MAX_TRAP_TYPE	(TR_FIQ + 1)
+#elif defined(__ARM_ARCH_7M__)
+#define	MAX_TRAP_TYPE	(TR_ISR + ARMCM3_NUMINTS)
+#endif	/* __ARM_ARCH_7M__ */
+
+/* The trap structure is defined here as offsets for assembly */
+#define	TR_TYPE		0x00
+#define	TR_EPC		0x04
+#define	TR_CPSR		0x08
+#define	TR_SPSR		0x0c
+#define	TR_REGS		0x10
+#define	TR_REG(n)	(TR_REGS + (n) * 4)
+#define	TR_SP		TR_REG(13)
+#define	TR_LR		TR_REG(14)
+#define	TR_PC		TR_REG(15)
+
+#define	TRAP_T_SIZE	80
+
+#ifndef	_LANGUAGE_ASSEMBLY
+
+#include <typedefs.h>
+
+typedef struct _trap_struct {
+	uint32		type;
+	uint32		epc;
+	uint32		cpsr;
+	uint32		spsr;
+	uint32		r0;
+	uint32		r1;
+	uint32		r2;
+	uint32		r3;
+	uint32		r4;
+	uint32		r5;
+	uint32		r6;
+	uint32		r7;
+	uint32		r8;
+	uint32		r9;
+	uint32		r10;
+	uint32		r11;
+	uint32		r12;
+	uint32		r13;
+	uint32		r14;
+	uint32		pc;
+} trap_t;
+
+#endif	/* !_LANGUAGE_ASSEMBLY */
+
+#endif	/* _hndrte_armtrap_h */
diff --git a/drivers/net/wireless/bcm4329/include/hndrte_cons.h b/drivers/net/wireless/bcm4329/include/hndrte_cons.h
new file mode 100644
index 0000000..a424174
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/hndrte_cons.h
@@ -0,0 +1,63 @@
+/*
+ * Console support for hndrte.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: hndrte_cons.h,v 13.1.2.4 2010/07/15 19:06:11 Exp $
+ */
+
+#include <typedefs.h>
+
+#define CBUF_LEN	(128)
+
+#define LOG_BUF_LEN	1024
+
+typedef struct {
+	uint32		buf;		/* Can't be pointer on (64-bit) hosts */
+	uint		buf_size;
+	uint		idx;
+	char		*_buf_compat;	/* Redundant pointer for backward compat. */
+} hndrte_log_t;
+
+typedef struct {
+	/* Virtual UART
+	 *   When there is no UART (e.g. Quickturn), the host should write a complete
+	 *   input line directly into cbuf and then write the length into vcons_in.
+	 *   This may also be used when there is a real UART (at risk of conflicting with
+	 *   the real UART).  vcons_out is currently unused.
+	 */
+	volatile uint	vcons_in;
+	volatile uint	vcons_out;
+
+	/* Output (logging) buffer
+	 *   Console output is written to a ring buffer log_buf at index log_idx.
+	 *   The host may read the output when it sees log_idx advance.
+	 *   Output will be lost if the output wraps around faster than the host polls.
+	 */
+	hndrte_log_t	log;
+
+	/* Console input line buffer
+	 *   Characters are read one at a time into cbuf until <CR> is received, then
+	 *   the buffer is processed as a command line.  Also used for virtual UART.
+	 */
+	uint		cbuf_idx;
+	char		cbuf[CBUF_LEN];
+} hndrte_cons_t;
diff --git a/drivers/net/wireless/bcm4329/include/hndsoc.h b/drivers/net/wireless/bcm4329/include/hndsoc.h
new file mode 100644
index 0000000..3542417
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/hndsoc.h
@@ -0,0 +1,195 @@
+/*
+ * Broadcom HND chip & on-chip-interconnect-related definitions.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: hndsoc.h,v 13.3.10.3 2008/08/06 03:43:25 Exp $
+ */
+
+#ifndef	_HNDSOC_H
+#define	_HNDSOC_H
+
+/* Include the soci specific files */
+#include <sbconfig.h>
+#include <aidmp.h>
+
+/*
+ * SOC Interconnect Address Map.
+ * All regions may not exist on all chips.
+ */
+#define SI_SDRAM_BASE		0x00000000	/* Physical SDRAM */
+#define SI_PCI_MEM		0x08000000	/* Host Mode sb2pcitranslation0 (64 MB) */
+#define SI_PCI_MEM_SZ		(64 * 1024 * 1024)
+#define SI_PCI_CFG		0x0c000000	/* Host Mode sb2pcitranslation1 (64 MB) */
+#define	SI_SDRAM_SWAPPED	0x10000000	/* Byteswapped Physical SDRAM */
+
+#define SI_ENUM_BASE    	0x18000000	/* Enumeration space base */
+#define SI_CORE_SIZE    	0x1000		/* each core gets 4Kbytes for registers */
+#ifndef SI_MAXCORES
+#define	SI_MAXCORES		16		/* Max cores (this is arbitrary, for software
+						 * convenience and could be changed if we
+						 * make any larger chips
+						 */
+#endif
+
+#define	SI_FASTRAM		0x19000000	/* On-chip RAM on chips that also have DDR */
+
+#define	SI_FLASH2		0x1c000000	/* Flash Region 2 (region 1 shadowed here) */
+#define	SI_FLASH2_SZ		0x02000000	/* Size of Flash Region 2 */
+#define	SI_ARMCM3_ROM		0x1e000000	/* ARM Cortex-M3 ROM */
+#define	SI_FLASH1		0x1fc00000	/* MIPS Flash Region 1 */
+#define	SI_FLASH1_SZ		0x00400000	/* MIPS Size of Flash Region 1 */
+#define	SI_ARM7S_ROM		0x20000000	/* ARM7TDMI-S ROM */
+#define	SI_ARMCM3_SRAM2		0x60000000	/* ARM Cortex-M3 SRAM Region 2 */
+#define	SI_ARM7S_SRAM2		0x80000000	/* ARM7TDMI-S SRAM Region 2 */
+#define	SI_ARM_FLASH1		0xffff0000	/* ARM Flash Region 1 */
+#define	SI_ARM_FLASH1_SZ	0x00010000	/* ARM Size of Flash Region 1 */
+
+#define SI_PCI_DMA		0x40000000	/* Client Mode sb2pcitranslation2 (1 GB) */
+#define SI_PCI_DMA2		0x80000000	/* Client Mode sb2pcitranslation2 (1 GB) */
+#define SI_PCI_DMA_SZ		0x40000000	/* Client Mode sb2pcitranslation2 size in bytes */
+#define SI_PCIE_DMA_L32		0x00000000	/* PCIE Client Mode sb2pcitranslation2
+						 * (2 ZettaBytes), low 32 bits
+						 */
+#define SI_PCIE_DMA_H32		0x80000000	/* PCIE Client Mode sb2pcitranslation2
+						 * (2 ZettaBytes), high 32 bits
+						 */
+
+/* core codes */
+#define	NODEV_CORE_ID		0x700		/* Invalid coreid */
+#define	CC_CORE_ID		0x800		/* chipcommon core */
+#define	ILINE20_CORE_ID		0x801		/* iline20 core */
+#define	SRAM_CORE_ID		0x802		/* sram core */
+#define	SDRAM_CORE_ID		0x803		/* sdram core */
+#define	PCI_CORE_ID		0x804		/* pci core */
+#define	MIPS_CORE_ID		0x805		/* mips core */
+#define	ENET_CORE_ID		0x806		/* enet mac core */
+#define	CODEC_CORE_ID		0x807		/* v90 codec core */
+#define	USB_CORE_ID		0x808		/* usb 1.1 host/device core */
+#define	ADSL_CORE_ID		0x809		/* ADSL core */
+#define	ILINE100_CORE_ID	0x80a		/* iline100 core */
+#define	IPSEC_CORE_ID		0x80b		/* ipsec core */
+#define	UTOPIA_CORE_ID		0x80c		/* utopia core */
+#define	PCMCIA_CORE_ID		0x80d		/* pcmcia core */
+#define	SOCRAM_CORE_ID		0x80e		/* internal memory core */
+#define	MEMC_CORE_ID		0x80f		/* memc sdram core */
+#define	OFDM_CORE_ID		0x810		/* OFDM phy core */
+#define	EXTIF_CORE_ID		0x811		/* external interface core */
+#define	D11_CORE_ID		0x812		/* 802.11 MAC core */
+#define	APHY_CORE_ID		0x813		/* 802.11a phy core */
+#define	BPHY_CORE_ID		0x814		/* 802.11b phy core */
+#define	GPHY_CORE_ID		0x815		/* 802.11g phy core */
+#define	MIPS33_CORE_ID		0x816		/* mips3302 core */
+#define	USB11H_CORE_ID		0x817		/* usb 1.1 host core */
+#define	USB11D_CORE_ID		0x818		/* usb 1.1 device core */
+#define	USB20H_CORE_ID		0x819		/* usb 2.0 host core */
+#define	USB20D_CORE_ID		0x81a		/* usb 2.0 device core */
+#define	SDIOH_CORE_ID		0x81b		/* sdio host core */
+#define	ROBO_CORE_ID		0x81c		/* roboswitch core */
+#define	ATA100_CORE_ID		0x81d		/* parallel ATA core */
+#define	SATAXOR_CORE_ID		0x81e		/* serial ATA & XOR DMA core */
+#define	GIGETH_CORE_ID		0x81f		/* gigabit ethernet core */
+#define	PCIE_CORE_ID		0x820		/* pci express core */
+#define	NPHY_CORE_ID		0x821		/* 802.11n 2x2 phy core */
+#define	SRAMC_CORE_ID		0x822		/* SRAM controller core */
+#define	MINIMAC_CORE_ID		0x823		/* MINI MAC/phy core */
+#define	ARM11_CORE_ID		0x824		/* ARM 1176 core */
+#define	ARM7S_CORE_ID		0x825		/* ARM7tdmi-s core */
+#define	LPPHY_CORE_ID		0x826		/* 802.11a/b/g phy core */
+#define	PMU_CORE_ID		0x827		/* PMU core */
+#define	SSNPHY_CORE_ID		0x828		/* 802.11n single-stream phy core */
+#define	SDIOD_CORE_ID		0x829		/* SDIO device core */
+#define	ARMCM3_CORE_ID		0x82a		/* ARM Cortex M3 core */
+#define	QNPHY_CORE_ID		0x82b		/* 802.11n 4x4 phy core */
+#define	MIPS74K_CORE_ID		0x82c		/* mips 74k core */
+#define	GMAC_CORE_ID		0x82d		/* Gigabit MAC core */
+#define	DMEMC_CORE_ID		0x82e		/* DDR1/2 memory controller core */
+#define	PCIERC_CORE_ID		0x82f		/* PCIE Root Complex core */
+#define	OCP_CORE_ID		0x830		/* OCP2OCP bridge core */
+#define	SC_CORE_ID		0x831		/* shared common core */
+#define	AHB_CORE_ID		0x832		/* OCP2AHB bridge core */
+#define	SPIH_CORE_ID		0x833		/* SPI host core */
+#define	I2S_CORE_ID		0x834		/* I2S core */
+#define OOB_ROUTER_CORE_ID	0x367		/* OOB router core ID */
+#define	DEF_AI_COMP		0xfff		/* Default component, in ai chips it maps all
+						 * unused address ranges
+						 */
+
+/* There are TWO constants on all HND chips: SI_ENUM_BASE above,
+ * and chipcommon being the first core:
+ */
+#define	SI_CC_IDX		0
+
+/* SOC Interconnect types (aka chip types) */
+#define	SOCI_SB			0
+#define	SOCI_AI			1
+
+/* Common core control flags */
+#define	SICF_BIST_EN		0x8000
+#define	SICF_PME_EN		0x4000
+#define	SICF_CORE_BITS		0x3ffc
+#define	SICF_FGC		0x0002
+#define	SICF_CLOCK_EN		0x0001
+
+/* Common core status flags */
+#define	SISF_BIST_DONE		0x8000
+#define	SISF_BIST_ERROR		0x4000
+#define	SISF_GATED_CLK		0x2000
+#define	SISF_DMA64		0x1000
+#define	SISF_CORE_BITS		0x0fff
+
+/* A register that is common to all cores to
+ * communicate w/PMU regarding clock control.
+ */
+#define SI_CLK_CTL_ST		0x1e0		/* clock control and status */
+
+/* clk_ctl_st register */
+#define	CCS_FORCEALP		0x00000001	/* force ALP request */
+#define	CCS_FORCEHT		0x00000002	/* force HT request */
+#define	CCS_FORCEILP		0x00000004	/* force ILP request */
+#define	CCS_ALPAREQ		0x00000008	/* ALP Avail Request */
+#define	CCS_HTAREQ		0x00000010	/* HT Avail Request */
+#define	CCS_FORCEHWREQOFF	0x00000020	/* Force HW Clock Request Off */
+#define	CCS_ALPAVAIL		0x00010000	/* ALP is available */
+#define	CCS_HTAVAIL		0x00020000	/* HT is available */
+#define	CCS0_HTAVAIL		0x00010000	/* HT avail in chipc and pcmcia on 4328a0 */
+#define	CCS0_ALPAVAIL		0x00020000	/* ALP avail in chipc and pcmcia on 4328a0 */
+
+/* Not really related to SOC Interconnect, but a couple of software
+ * conventions for the use the flash space:
+ */
+
+/* Minumum amount of flash we support */
+#define FLASH_MIN		0x00020000	/* Minimum flash size */
+
+/* A boot/binary may have an embedded block that describes its size  */
+#define	BISZ_OFFSET		0x3e0		/* At this offset into the binary */
+#define	BISZ_MAGIC		0x4249535a	/* Marked with this value: 'BISZ' */
+#define	BISZ_MAGIC_IDX		0		/* Word 0: magic */
+#define	BISZ_TXTST_IDX		1		/*	1: text start */
+#define	BISZ_TXTEND_IDX		2		/*	2: text end */
+#define	BISZ_DATAST_IDX		3		/*	3: data start */
+#define	BISZ_DATAEND_IDX	4		/*	4: data end */
+#define	BISZ_BSSST_IDX		5		/*	5: bss start */
+#define	BISZ_BSSEND_IDX		6		/*	6: bss end */
+#define BISZ_SIZE		7		/* descriptor size in 32-bit intergers */
+
+#endif /* _HNDSOC_H */
diff --git a/drivers/net/wireless/bcm4329/include/linux_osl.h b/drivers/net/wireless/bcm4329/include/linux_osl.h
new file mode 100644
index 0000000..b059c2a
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/linux_osl.h
@@ -0,0 +1,322 @@
+/*
+ * Linux OS Independent Layer
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: linux_osl.h,v 13.131.30.8 2010/04/26 05:42:18 Exp $
+ */
+
+
+#ifndef _linux_osl_h_
+#define _linux_osl_h_
+
+#include <typedefs.h>
+
+
+#include <linuxver.h>
+
+
+#ifdef __GNUC__
+#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
+#if GCC_VERSION > 30100
+#define	ASSERT(exp)		do {} while (0)
+#else
+
+#define	ASSERT(exp)
+#endif 
+#endif 
+
+
+#define	OSL_DELAY(usec)		osl_delay(usec)
+extern void osl_delay(uint usec);
+
+
+
+#define	OSL_PCMCIA_READ_ATTR(osh, offset, buf, size) \
+	osl_pcmcia_read_attr((osh), (offset), (buf), (size))
+#define	OSL_PCMCIA_WRITE_ATTR(osh, offset, buf, size) \
+	osl_pcmcia_write_attr((osh), (offset), (buf), (size))
+extern void osl_pcmcia_read_attr(osl_t *osh, uint offset, void *buf, int size);
+extern void osl_pcmcia_write_attr(osl_t *osh, uint offset, void *buf, int size);
+
+
+#define	OSL_PCI_READ_CONFIG(osh, offset, size) \
+	osl_pci_read_config((osh), (offset), (size))
+#define	OSL_PCI_WRITE_CONFIG(osh, offset, size, val) \
+	osl_pci_write_config((osh), (offset), (size), (val))
+extern uint32 osl_pci_read_config(osl_t *osh, uint offset, uint size);
+extern void osl_pci_write_config(osl_t *osh, uint offset, uint size, uint val);
+
+
+#define OSL_PCI_BUS(osh)	osl_pci_bus(osh)
+#define OSL_PCI_SLOT(osh)	osl_pci_slot(osh)
+extern uint osl_pci_bus(osl_t *osh);
+extern uint osl_pci_slot(osl_t *osh);
+
+
+typedef struct {
+	bool pkttag;
+	uint pktalloced; 	
+	bool mmbus;		
+	pktfree_cb_fn_t tx_fn;  
+	void *tx_ctx;		
+} osl_pubinfo_t;
+
+
+extern osl_t *osl_attach(void *pdev, uint bustype, bool pkttag);
+extern void osl_detach(osl_t *osh);
+
+#define PKTFREESETCB(osh, _tx_fn, _tx_ctx) \
+	do { \
+	   ((osl_pubinfo_t*)osh)->tx_fn = _tx_fn; \
+	   ((osl_pubinfo_t*)osh)->tx_ctx = _tx_ctx; \
+	} while (0)
+
+
+#define BUS_SWAP32(v)		(v)
+
+
+#define	MALLOC(osh, size)	osl_malloc((osh), (size))
+#define	MFREE(osh, addr, size)	osl_mfree((osh), (addr), (size))
+#define MALLOCED(osh)		osl_malloced((osh))
+
+
+#define	MALLOC_FAILED(osh)	osl_malloc_failed((osh))
+
+extern void *osl_malloc(osl_t *osh, uint size);
+extern void osl_mfree(osl_t *osh, void *addr, uint size);
+extern uint osl_malloced(osl_t *osh);
+extern uint osl_malloc_failed(osl_t *osh);
+
+
+#define	DMA_CONSISTENT_ALIGN	PAGE_SIZE
+#define	DMA_ALLOC_CONSISTENT(osh, size, pap, dmah, alignbits) \
+	osl_dma_alloc_consistent((osh), (size), (pap))
+#define	DMA_FREE_CONSISTENT(osh, va, size, pa, dmah) \
+	osl_dma_free_consistent((osh), (void*)(va), (size), (pa))
+extern void *osl_dma_alloc_consistent(osl_t *osh, uint size, ulong *pap);
+extern void osl_dma_free_consistent(osl_t *osh, void *va, uint size, ulong pa);
+
+
+#define	DMA_TX	1	
+#define	DMA_RX	2	
+
+
+#define	DMA_MAP(osh, va, size, direction, p, dmah) \
+	osl_dma_map((osh), (va), (size), (direction))
+#define	DMA_UNMAP(osh, pa, size, direction, p, dmah) \
+	osl_dma_unmap((osh), (pa), (size), (direction))
+extern uint osl_dma_map(osl_t *osh, void *va, uint size, int direction);
+extern void osl_dma_unmap(osl_t *osh, uint pa, uint size, int direction);
+
+
+#define OSL_DMADDRWIDTH(osh, addrwidth) do {} while (0)
+
+
+#include <bcmsdh.h>
+#define OSL_WRITE_REG(osh, r, v) (bcmsdh_reg_write(NULL, (uintptr)(r), sizeof(*(r)), (v)))
+#define OSL_READ_REG(osh, r) (bcmsdh_reg_read(NULL, (uintptr)(r), sizeof(*(r))))
+
+#define SELECT_BUS_WRITE(osh, mmap_op, bus_op) if (((osl_pubinfo_t*)(osh))->mmbus) \
+	mmap_op else bus_op
+#define SELECT_BUS_READ(osh, mmap_op, bus_op) (((osl_pubinfo_t*)(osh))->mmbus) ? \
+	mmap_op : bus_op
+
+
+
+
+#ifndef printf
+#define	printf(fmt, args...)	printk(fmt, ## args)
+#endif 
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+
+#ifndef IL_BIGENDIAN
+#define R_REG(osh, r) (\
+	SELECT_BUS_READ(osh, sizeof(*(r)) == sizeof(uint8) ? readb((volatile uint8*)(r)) : \
+	sizeof(*(r)) == sizeof(uint16) ? readw((volatile uint16*)(r)) : \
+	readl((volatile uint32*)(r)), OSL_READ_REG(osh, r)) \
+)
+#define W_REG(osh, r, v) do { \
+	SELECT_BUS_WRITE(osh,  \
+		switch (sizeof(*(r))) { \
+			case sizeof(uint8):	writeb((uint8)(v), (volatile uint8*)(r)); break; \
+			case sizeof(uint16):	writew((uint16)(v), (volatile uint16*)(r)); break; \
+			case sizeof(uint32):	writel((uint32)(v), (volatile uint32*)(r)); break; \
+		}, \
+		(OSL_WRITE_REG(osh, r, v))); \
+	} while (0)
+#else	
+#define R_REG(osh, r) (\
+	SELECT_BUS_READ(osh, \
+		({ \
+			__typeof(*(r)) __osl_v; \
+			switch (sizeof(*(r))) { \
+				case sizeof(uint8):	__osl_v = \
+					readb((volatile uint8*)((uintptr)(r)^3)); break; \
+				case sizeof(uint16):	__osl_v = \
+					readw((volatile uint16*)((uintptr)(r)^2)); break; \
+				case sizeof(uint32):	__osl_v = \
+					readl((volatile uint32*)(r)); break; \
+			} \
+			__osl_v; \
+		}), \
+		OSL_READ_REG(osh, r)) \
+)
+#define W_REG(osh, r, v) do { \
+	SELECT_BUS_WRITE(osh,  \
+		switch (sizeof(*(r))) { \
+			case sizeof(uint8):	writeb((uint8)(v), \
+					(volatile uint8*)((uintptr)(r)^3)); break; \
+			case sizeof(uint16):	writew((uint16)(v), \
+					(volatile uint16*)((uintptr)(r)^2)); break; \
+			case sizeof(uint32):	writel((uint32)(v), \
+					(volatile uint32*)(r)); break; \
+		}, \
+		(OSL_WRITE_REG(osh, r, v))); \
+	} while (0)
+#endif 
+
+#define	AND_REG(osh, r, v)		W_REG(osh, (r), R_REG(osh, r) & (v))
+#define	OR_REG(osh, r, v)		W_REG(osh, (r), R_REG(osh, r) | (v))
+
+
+#define	bcopy(src, dst, len)	memcpy((dst), (src), (len))
+#define	bcmp(b1, b2, len)	memcmp((b1), (b2), (len))
+#define	bzero(b, len)		memset((b), '\0', (len))
+
+
+#define OSL_UNCACHED(va)	((void*)va)
+
+
+#if defined(__i386__)
+#define	OSL_GETCYCLES(x)	rdtscl((x))
+#else
+#define OSL_GETCYCLES(x)	((x) = 0)
+#endif 
+
+
+#define	BUSPROBE(val, addr)	({ (val) = R_REG(NULL, (addr)); 0; })
+
+
+#if !defined(CONFIG_MMC_MSM7X00A)
+#define	REG_MAP(pa, size)	ioremap_nocache((unsigned long)(pa), (unsigned long)(size))
+#else
+#define REG_MAP(pa, size)       (void *)(0)
+#endif 
+#define	REG_UNMAP(va)		iounmap((va))
+
+
+#define	R_SM(r)			*(r)
+#define	W_SM(r, v)		(*(r) = (v))
+#define	BZERO_SM(r, len)	memset((r), '\0', (len))
+
+
+#define	PKTGET(osh, len, send)		osl_pktget((osh), (len))
+#define	PKTFREE(osh, skb, send)		osl_pktfree((osh), (skb), (send))
+#ifdef DHD_USE_STATIC_BUF
+#define	PKTGET_STATIC(osh, len, send)		osl_pktget_static((osh), (len))
+#define	PKTFREE_STATIC(osh, skb, send)		osl_pktfree_static((osh), (skb), (send))
+#endif 
+#define	PKTDATA(osh, skb)		(((struct sk_buff*)(skb))->data)
+#define	PKTLEN(osh, skb)		(((struct sk_buff*)(skb))->len)
+#define PKTHEADROOM(osh, skb)		(PKTDATA(osh, skb)-(((struct sk_buff*)(skb))->head))
+#define PKTTAILROOM(osh, skb) ((((struct sk_buff*)(skb))->end)-(((struct sk_buff*)(skb))->tail))
+#define	PKTNEXT(osh, skb)		(((struct sk_buff*)(skb))->next)
+#define	PKTSETNEXT(osh, skb, x)		(((struct sk_buff*)(skb))->next = (struct sk_buff*)(x))
+#define	PKTSETLEN(osh, skb, len)	__skb_trim((struct sk_buff*)(skb), (len))
+#define	PKTPUSH(osh, skb, bytes)	skb_push((struct sk_buff*)(skb), (bytes))
+#define	PKTPULL(osh, skb, bytes)	skb_pull((struct sk_buff*)(skb), (bytes))
+#define	PKTDUP(osh, skb)		osl_pktdup((osh), (skb))
+#define	PKTTAG(skb)			((void*)(((struct sk_buff*)(skb))->cb))
+#define PKTALLOCED(osh)			((osl_pubinfo_t *)(osh))->pktalloced
+#define PKTSETPOOL(osh, skb, x, y)	do {} while (0)
+#define PKTPOOL(osh, skb)		FALSE
+#define PKTPOOLLEN(osh, pktp)		(0)
+#define PKTPOOLAVAIL(osh, pktp)		(0)
+#define PKTPOOLADD(osh, pktp, p)	BCME_ERROR
+#define PKTPOOLGET(osh, pktp)		NULL
+#define PKTLIST_DUMP(osh, buf)
+
+extern void *osl_pktget(osl_t *osh, uint len);
+extern void osl_pktfree(osl_t *osh, void *skb, bool send);
+extern void *osl_pktget_static(osl_t *osh, uint len);
+extern void osl_pktfree_static(osl_t *osh, void *skb, bool send);
+extern void *osl_pktdup(osl_t *osh, void *skb);
+
+
+
+static INLINE void *
+osl_pkt_frmnative(osl_pubinfo_t *osh, struct sk_buff *skb)
+{
+	struct sk_buff *nskb;
+
+	if (osh->pkttag)
+		bzero((void*)skb->cb, OSL_PKTTAG_SZ);
+
+	
+	for (nskb = skb; nskb; nskb = nskb->next) {
+		osh->pktalloced++;
+	}
+
+	return (void *)skb;
+}
+#define PKTFRMNATIVE(osh, skb)	osl_pkt_frmnative(((osl_pubinfo_t *)osh), (struct sk_buff*)(skb))
+
+
+static INLINE struct sk_buff *
+osl_pkt_tonative(osl_pubinfo_t *osh, void *pkt)
+{
+	struct sk_buff *nskb;
+
+	if (osh->pkttag)
+		bzero(((struct sk_buff*)pkt)->cb, OSL_PKTTAG_SZ);
+
+	
+	for (nskb = (struct sk_buff *)pkt; nskb; nskb = nskb->next) {
+		osh->pktalloced--;
+	}
+
+	return (struct sk_buff *)pkt;
+}
+#define PKTTONATIVE(osh, pkt)		osl_pkt_tonative((osl_pubinfo_t *)(osh), (pkt))
+
+#define	PKTLINK(skb)			(((struct sk_buff*)(skb))->prev)
+#define	PKTSETLINK(skb, x)		(((struct sk_buff*)(skb))->prev = (struct sk_buff*)(x))
+#define	PKTPRIO(skb)			(((struct sk_buff*)(skb))->priority)
+#define	PKTSETPRIO(skb, x)		(((struct sk_buff*)(skb))->priority = (x))
+#define PKTSUMNEEDED(skb)		(((struct sk_buff*)(skb))->ip_summed == CHECKSUM_HW)
+#define PKTSETSUMGOOD(skb, x)		(((struct sk_buff*)(skb))->ip_summed = \
+						((x) ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE))
+
+#define PKTSHARED(skb)                  (((struct sk_buff*)(skb))->cloned)
+
+
+#define OSL_ERROR(bcmerror)	osl_error(bcmerror)
+extern int osl_error(int bcmerror);
+
+
+#define	PKTBUFSZ	2048   
+
+
+#define OSL_SYSUPTIME()		((uint32)jiffies * (1000 / HZ))
+#endif	
diff --git a/drivers/net/wireless/bcm4329/include/linuxver.h b/drivers/net/wireless/bcm4329/include/linuxver.h
new file mode 100644
index 0000000..6ed2265
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/linuxver.h
@@ -0,0 +1,447 @@
+/*
+ * Linux-specific abstractions to gain some independence from linux kernel versions.
+ * Pave over some 2.2 versus 2.4 versus 2.6 kernel differences.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: linuxver.h,v 13.38.8.1.8.6 2010/04/29 05:00:46 Exp $
+ */
+
+
+#ifndef _linuxver_h_
+#define _linuxver_h_
+
+#include <linux/version.h>
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
+#include <linux/config.h>
+#elif (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33))
+#include <linux/autoconf.h>
+#endif
+#include <linux/module.h>
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 0))
+
+#ifdef __UNDEF_NO_VERSION__
+#undef __NO_VERSION__
+#else
+#define __NO_VERSION__
+#endif
+#endif	
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
+#define module_param(_name_, _type_, _perm_)	MODULE_PARM(_name_, "i")
+#define module_param_string(_name_, _string_, _size_, _perm_) \
+		MODULE_PARM(_string_, "c" __MODULE_STRING(_size_))
+#endif
+
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 9))
+#include <linux/malloc.h>
+#else
+#include <linux/slab.h>
+#endif
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/semaphore.h>
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28))
+#undef IP_TOS
+#endif 
+#include <asm/io.h>
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 41))
+#include <linux/workqueue.h>
+#else
+#include <linux/tqueue.h>
+#ifndef work_struct
+#define work_struct tq_struct
+#endif
+#ifndef INIT_WORK
+#define INIT_WORK(_work, _func, _data) INIT_TQUEUE((_work), (_func), (_data))
+#endif
+#ifndef schedule_work
+#define schedule_work(_work) schedule_task((_work))
+#endif
+#ifndef flush_scheduled_work
+#define flush_scheduled_work() flush_scheduled_tasks()
+#endif
+#endif	
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
+#define	MY_INIT_WORK(_work, _func, _data)	INIT_WORK(_work, _func)
+#else
+#define	MY_INIT_WORK(_work, _func, _data)	INIT_WORK(_work, _func, _data)
+typedef void (*work_func_t)(void *work);
+#endif	
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
+
+#ifndef IRQ_NONE
+typedef void irqreturn_t;
+#define IRQ_NONE
+#define IRQ_HANDLED
+#define IRQ_RETVAL(x)
+#endif
+#else
+typedef irqreturn_t(*FN_ISR) (int irq, void *dev_id, struct pt_regs *ptregs);
+#endif	
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
+#define IRQF_SHARED	SA_SHIRQ
+#endif 
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 17)
+#ifdef	CONFIG_NET_RADIO
+#define	CONFIG_WIRELESS_EXT
+#endif
+#endif	
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 67)
+#ifndef SANDGATE2G
+#define MOD_INC_USE_COUNT
+#endif 
+#endif 
+
+
+#ifndef __exit
+#define __exit
+#endif
+#ifndef __devexit
+#define __devexit
+#endif
+#ifndef __devinit
+#define __devinit	__init
+#endif
+#ifndef __devinitdata
+#define __devinitdata
+#endif
+#ifndef __devexit_p
+#define __devexit_p(x)	x
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0))
+
+#define pci_get_drvdata(dev)		(dev)->sysdata
+#define pci_set_drvdata(dev, value)	(dev)->sysdata = (value)
+
+
+
+struct pci_device_id {
+	unsigned int vendor, device;		
+	unsigned int subvendor, subdevice;	
+	unsigned int class, class_mask;		
+	unsigned long driver_data;		
+};
+
+struct pci_driver {
+	struct list_head node;
+	char *name;
+	const struct pci_device_id *id_table;	
+	int (*probe)(struct pci_dev *dev,
+	             const struct pci_device_id *id); 
+	void (*remove)(struct pci_dev *dev);	
+	void (*suspend)(struct pci_dev *dev);	
+	void (*resume)(struct pci_dev *dev);	
+};
+
+#define MODULE_DEVICE_TABLE(type, name)
+#define PCI_ANY_ID (~0)
+
+
+#define pci_module_init pci_register_driver
+extern int pci_register_driver(struct pci_driver *drv);
+extern void pci_unregister_driver(struct pci_driver *drv);
+
+#endif 
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18))
+#define pci_module_init pci_register_driver
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 2, 18))
+#ifdef MODULE
+#define module_init(x) int init_module(void) { return x(); }
+#define module_exit(x) void cleanup_module(void) { x(); }
+#else
+#define module_init(x)	__initcall(x);
+#define module_exit(x)	__exitcall(x);
+#endif
+#endif	
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 48))
+#define list_for_each(pos, head) \
+	for (pos = (head)->next; pos != (head); pos = pos->next)
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 13))
+#define pci_resource_start(dev, bar)	((dev)->base_address[(bar)])
+#elif (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 44))
+#define pci_resource_start(dev, bar)	((dev)->resource[(bar)].start)
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 23))
+#define pci_enable_device(dev) do { } while (0)
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 14))
+#define net_device device
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 42))
+
+
+
+#ifndef PCI_DMA_TODEVICE
+#define	PCI_DMA_TODEVICE	1
+#define	PCI_DMA_FROMDEVICE	2
+#endif
+
+typedef u32 dma_addr_t;
+
+
+static inline int get_order(unsigned long size)
+{
+	int order;
+
+	size = (size-1) >> (PAGE_SHIFT-1);
+	order = -1;
+	do {
+		size >>= 1;
+		order++;
+	} while (size);
+	return order;
+}
+
+static inline void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size,
+                                         dma_addr_t *dma_handle)
+{
+	void *ret;
+	int gfp = GFP_ATOMIC | GFP_DMA;
+
+	ret = (void *)__get_free_pages(gfp, get_order(size));
+
+	if (ret != NULL) {
+		memset(ret, 0, size);
+		*dma_handle = virt_to_bus(ret);
+	}
+	return ret;
+}
+static inline void pci_free_consistent(struct pci_dev *hwdev, size_t size,
+                                       void *vaddr, dma_addr_t dma_handle)
+{
+	free_pages((unsigned long)vaddr, get_order(size));
+}
+#define pci_map_single(cookie, address, size, dir)	virt_to_bus(address)
+#define pci_unmap_single(cookie, address, size, dir)
+
+#endif 
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 43))
+
+#define dev_kfree_skb_any(a)		dev_kfree_skb(a)
+#define netif_down(dev)			do { (dev)->start = 0; } while (0)
+
+
+#ifndef _COMPAT_NETDEVICE_H
+
+
+
+#define dev_kfree_skb_irq(a)	dev_kfree_skb(a)
+#define netif_wake_queue(dev) \
+		do { clear_bit(0, &(dev)->tbusy); mark_bh(NET_BH); } while (0)
+#define netif_stop_queue(dev)	set_bit(0, &(dev)->tbusy)
+
+static inline void netif_start_queue(struct net_device *dev)
+{
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	dev->start = 1;
+}
+
+#define netif_queue_stopped(dev)	(dev)->tbusy
+#define netif_running(dev)		(dev)->start
+
+#endif 
+
+#define netif_device_attach(dev)	netif_start_queue(dev)
+#define netif_device_detach(dev)	netif_stop_queue(dev)
+
+
+#define tasklet_struct				tq_struct
+static inline void tasklet_schedule(struct tasklet_struct *tasklet)
+{
+	queue_task(tasklet, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+static inline void tasklet_init(struct tasklet_struct *tasklet,
+                                void (*func)(unsigned long),
+                                unsigned long data)
+{
+	tasklet->next = NULL;
+	tasklet->sync = 0;
+	tasklet->routine = (void (*)(void *))func;
+	tasklet->data = (void *)data;
+}
+#define tasklet_kill(tasklet)	{ do {} while (0); }
+
+
+#define del_timer_sync(timer) del_timer(timer)
+
+#else
+
+#define netif_down(dev)
+
+#endif 
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 3))
+
+
+#define PREPARE_TQUEUE(_tq, _routine, _data)			\
+	do {							\
+		(_tq)->routine = _routine;			\
+		(_tq)->data = _data;				\
+	} while (0)
+
+
+#define INIT_TQUEUE(_tq, _routine, _data)			\
+	do {							\
+		INIT_LIST_HEAD(&(_tq)->list);			\
+		(_tq)->sync = 0;				\
+		PREPARE_TQUEUE((_tq), (_routine), (_data));	\
+	} while (0)
+
+#endif	
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 6))
+
+
+
+static inline int
+pci_save_state(struct pci_dev *dev, u32 *buffer)
+{
+	int i;
+	if (buffer) {
+		for (i = 0; i < 16; i++)
+			pci_read_config_dword(dev, i * 4, &buffer[i]);
+	}
+	return 0;
+}
+
+static inline int
+pci_restore_state(struct pci_dev *dev, u32 *buffer)
+{
+	int i;
+
+	if (buffer) {
+		for (i = 0; i < 16; i++)
+			pci_write_config_dword(dev, i * 4, buffer[i]);
+	}
+	
+	else {
+		for (i = 0; i < 6; i ++)
+			pci_write_config_dword(dev,
+			                       PCI_BASE_ADDRESS_0 + (i * 4),
+			                       pci_resource_start(dev, i));
+		pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
+	}
+	return 0;
+}
+
+#endif 
+
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 19))
+#define read_c0_count() read_32bit_cp0_register(CP0_COUNT)
+#endif
+
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24))
+#ifndef SET_MODULE_OWNER
+#define SET_MODULE_OWNER(dev)		do {} while (0)
+#define OLD_MOD_INC_USE_COUNT		MOD_INC_USE_COUNT
+#define OLD_MOD_DEC_USE_COUNT		MOD_DEC_USE_COUNT
+#else
+#define OLD_MOD_INC_USE_COUNT		do {} while (0)
+#define OLD_MOD_DEC_USE_COUNT		do {} while (0)
+#endif
+#else 
+#ifndef SET_MODULE_OWNER
+#define SET_MODULE_OWNER(dev)		do {} while (0)
+#endif
+#ifndef MOD_INC_USE_COUNT
+#define MOD_INC_USE_COUNT			do {} while (0)
+#endif
+#ifndef MOD_DEC_USE_COUNT
+#define MOD_DEC_USE_COUNT			do {} while (0)
+#endif
+#define OLD_MOD_INC_USE_COUNT		MOD_INC_USE_COUNT
+#define OLD_MOD_DEC_USE_COUNT		MOD_DEC_USE_COUNT
+#endif 
+
+#ifndef SET_NETDEV_DEV
+#define SET_NETDEV_DEV(net, pdev)	do {} while (0)
+#endif
+
+#ifndef HAVE_FREE_NETDEV
+#define free_netdev(dev)		kfree(dev)
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
+
+#define af_packet_priv			data
+#endif
+
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11)
+#define DRV_SUSPEND_STATE_TYPE pm_message_t
+#else
+#define DRV_SUSPEND_STATE_TYPE uint32
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
+#define CHECKSUM_HW	CHECKSUM_PARTIAL
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+#define KILL_PROC(pid, sig) \
+{ \
+	struct task_struct *tsk; \
+	tsk = pid_task(find_vpid(pid), PIDTYPE_PID); \
+	if (tsk) send_sig(sig, tsk, 1); \
+}
+#else
+#define KILL_PROC(pid, sig) \
+{ \
+	kill_proc(pid, sig, 1); \
+}
+#endif 
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
+#define netdev_priv(dev) dev->priv
+#endif
+
+#endif 
diff --git a/drivers/net/wireless/bcm4329/include/miniopt.h b/drivers/net/wireless/bcm4329/include/miniopt.h
new file mode 100644
index 0000000..3667fb1
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/miniopt.h
@@ -0,0 +1,77 @@
+/*
+ * Command line options parser.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ * $Id: miniopt.h,v 1.1.6.2 2009/01/14 23:52:48 Exp $
+ */
+
+
+#ifndef MINI_OPT_H
+#define MINI_OPT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ---- Include Files ---------------------------------------------------- */
+/* ---- Constants and Types ---------------------------------------------- */
+
+#define MINIOPT_MAXKEY	128	/* Max options */
+typedef struct miniopt {
+
+	/* These are persistent after miniopt_init() */
+	const char* name;		/* name for prompt in error strings */
+	const char* flags;		/* option chars that take no args */
+	bool longflags;		/* long options may be flags */
+	bool opt_end;		/* at end of options (passed a "--") */
+
+	/* These are per-call to miniopt() */
+
+	int consumed;		/* number of argv entries cosumed in
+				 * the most recent call to miniopt()
+				 */
+	bool positional;
+	bool good_int;		/* 'val' member is the result of a sucessful
+				 * strtol conversion of the option value
+				 */
+	char opt;
+	char key[MINIOPT_MAXKEY];
+	char* valstr;		/* positional param, or value for the option,
+				 * or null if the option had
+				 * no accompanying value
+				 */
+	uint uval;		/* strtol translation of valstr */
+	int  val;		/* strtol translation of valstr */
+} miniopt_t;
+
+void miniopt_init(miniopt_t *t, const char* name, const char* flags, bool longflags);
+int miniopt(miniopt_t *t, char **argv);
+
+
+/* ---- Variable Externs ------------------------------------------------- */
+/* ---- Function Prototypes ---------------------------------------------- */
+
+
+#ifdef __cplusplus
+	}
+#endif
+
+#endif  /* MINI_OPT_H  */
diff --git a/drivers/net/wireless/bcm4329/include/msgtrace.h b/drivers/net/wireless/bcm4329/include/msgtrace.h
new file mode 100644
index 0000000..1479086
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/msgtrace.h
@@ -0,0 +1,72 @@
+/*
+ * Trace messages sent over HBUS
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: msgtrace.h,v 1.1.2.4 2009/01/27 04:09:40 Exp $
+ */
+
+#ifndef	_MSGTRACE_H
+#define	_MSGTRACE_H
+
+#ifndef _TYPEDEFS_H_
+#include <typedefs.h>
+#endif
+
+
+/* This marks the start of a packed structure section. */
+#include <packed_section_start.h>
+
+#define MSGTRACE_VERSION 1
+
+/* Message trace header */
+typedef BWL_PRE_PACKED_STRUCT struct msgtrace_hdr {
+	uint8	version;
+	uint8   spare;
+	uint16	len;	/* Len of the trace */
+	uint32	seqnum;	/* Sequence number of message. Useful if the messsage has been lost
+			 * because of DMA error or a bus reset (ex: SDIO Func2)
+			 */
+	uint32  discarded_bytes;  /* Number of discarded bytes because of trace overflow  */
+	uint32  discarded_printf; /* Number of discarded printf because of trace overflow */
+} BWL_POST_PACKED_STRUCT msgtrace_hdr_t;
+
+#define MSGTRACE_HDRLEN 	sizeof(msgtrace_hdr_t)
+
+/* The hbus driver generates traces when sending a trace message. This causes endless traces.
+ * This flag must be set to TRUE in any hbus traces. The flag is reset in the function msgtrace_put.
+ * This prevents endless traces but generates hasardous lost of traces only in bus device code.
+ * It is recommendat to set this flag in macro SD_TRACE but not in SD_ERROR for avoiding missing
+ * hbus error traces. hbus error trace should not generates endless traces.
+ */
+extern bool msgtrace_hbus_trace;
+
+typedef void (*msgtrace_func_send_t)(void *hdl1, void *hdl2, uint8 *hdr,
+                                     uint16 hdrlen, uint8 *buf, uint16 buflen);
+
+extern void msgtrace_sent(void);
+extern void msgtrace_put(char *buf, int count);
+extern void msgtrace_init(void *hdl1, void *hdl2, msgtrace_func_send_t func_send);
+
+/* This marks the end of a packed structure section. */
+#include <packed_section_end.h>
+
+#endif	/* _MSGTRACE_H */
diff --git a/drivers/net/wireless/bcm4329/include/osl.h b/drivers/net/wireless/bcm4329/include/osl.h
new file mode 100644
index 0000000..5599e53
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/osl.h
@@ -0,0 +1,55 @@
+/*
+ * OS Abstraction Layer
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ * $Id: osl.h,v 13.37.32.1 2008/11/20 00:51:15 Exp $
+ */
+
+
+#ifndef _osl_h_
+#define _osl_h_
+
+
+typedef struct osl_info osl_t;
+typedef struct osl_dmainfo osldma_t;
+
+#define OSL_PKTTAG_SZ	32 
+
+
+typedef void (*pktfree_cb_fn_t)(void *ctx, void *pkt, unsigned int status);
+
+#include <linux_osl.h>
+
+
+
+
+#define	SET_REG(osh, r, mask, val)	W_REG((osh), (r), ((R_REG((osh), r) & ~(mask)) | (val)))
+
+#ifndef AND_REG
+#define AND_REG(osh, r, v)		W_REG(osh, (r), R_REG(osh, r) & (v))
+#endif   
+
+#ifndef OR_REG
+#define OR_REG(osh, r, v)		W_REG(osh, (r), R_REG(osh, r) | (v))
+#endif   
+
+
+#endif	
diff --git a/drivers/net/wireless/bcm4329/include/packed_section_end.h b/drivers/net/wireless/bcm4329/include/packed_section_end.h
new file mode 100644
index 0000000..5b61c18
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/packed_section_end.h
@@ -0,0 +1,54 @@
+/*
+ * Declare directives for structure packing. No padding will be provided
+ * between the members of packed structures, and therefore, there is no
+ * guarantee that structure members will be aligned.
+ *
+ * Declaring packed structures is compiler specific. In order to handle all
+ * cases, packed structures should be delared as:
+ *
+ * #include <packed_section_start.h>
+ *
+ * typedef BWL_PRE_PACKED_STRUCT struct foobar_t {
+ *    some_struct_members;
+ * } BWL_POST_PACKED_STRUCT foobar_t;
+ *
+ * #include <packed_section_end.h>
+ *
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ * $Id: packed_section_end.h,v 1.1.6.3 2008/12/10 00:27:54 Exp $
+ */
+
+
+
+
+#ifdef BWL_PACKED_SECTION
+	#undef BWL_PACKED_SECTION
+#else
+	#error "BWL_PACKED_SECTION is NOT defined!"
+#endif
+
+
+
+
+
+#undef	BWL_PRE_PACKED_STRUCT
+#undef	BWL_POST_PACKED_STRUCT
diff --git a/drivers/net/wireless/bcm4329/include/packed_section_start.h b/drivers/net/wireless/bcm4329/include/packed_section_start.h
new file mode 100644
index 0000000..cb93aa6
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/packed_section_start.h
@@ -0,0 +1,61 @@
+/*
+ * Declare directives for structure packing. No padding will be provided
+ * between the members of packed structures, and therefore, there is no
+ * guarantee that structure members will be aligned.
+ *
+ * Declaring packed structures is compiler specific. In order to handle all
+ * cases, packed structures should be delared as:
+ *
+ * #include <packed_section_start.h>
+ *
+ * typedef BWL_PRE_PACKED_STRUCT struct foobar_t {
+ *    some_struct_members;
+ * } BWL_POST_PACKED_STRUCT foobar_t;
+ *
+ * #include <packed_section_end.h>
+ *
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ * $Id: packed_section_start.h,v 1.1.6.3 2008/12/10 00:27:54 Exp $
+ */
+
+
+
+
+#ifdef BWL_PACKED_SECTION
+	#error "BWL_PACKED_SECTION is already defined!"
+#else
+	#define BWL_PACKED_SECTION
+#endif
+
+
+
+
+
+#if defined(__GNUC__)
+	#define	BWL_PRE_PACKED_STRUCT
+	#define	BWL_POST_PACKED_STRUCT	__attribute__((packed))
+#elif defined(__CC_ARM)
+	#define	BWL_PRE_PACKED_STRUCT	__packed
+	#define	BWL_POST_PACKED_STRUCT
+#else
+	#error "Unknown compiler!"
+#endif
diff --git a/drivers/net/wireless/bcm4329/include/pcicfg.h b/drivers/net/wireless/bcm4329/include/pcicfg.h
new file mode 100644
index 0000000..898962c
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/pcicfg.h
@@ -0,0 +1,52 @@
+/*
+ * pcicfg.h: PCI configuration constants and structures.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: pcicfg.h,v 1.41.12.3 2008/06/26 22:49:41 Exp $
+ */
+
+
+#ifndef	_h_pcicfg_
+#define	_h_pcicfg_
+
+
+#define	PCI_CFG_VID		0
+#define	PCI_CFG_CMD		4
+#define	PCI_CFG_REV		8
+#define	PCI_CFG_BAR0		0x10
+#define	PCI_CFG_BAR1		0x14
+#define	PCI_BAR0_WIN		0x80	
+#define	PCI_INT_STATUS		0x90	
+#define	PCI_INT_MASK		0x94	
+
+#define PCIE_EXTCFG_OFFSET	0x100
+#define	PCI_BAR0_PCIREGS_OFFSET	(6 * 1024)	
+#define	PCI_BAR0_PCISBR_OFFSET	(4 * 1024)	
+
+#define PCI_BAR0_WINSZ		(16 * 1024)	
+
+
+#define	PCI_16KB0_PCIREGS_OFFSET (8 * 1024)	
+#define	PCI_16KB0_CCREGS_OFFSET	(12 * 1024)	
+#define PCI_16KBB0_WINSZ	(16 * 1024)	
+
+#endif	
diff --git a/drivers/net/wireless/bcm4329/include/proto/802.11.h b/drivers/net/wireless/bcm4329/include/proto/802.11.h
new file mode 100644
index 0000000..fd26317
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/proto/802.11.h
@@ -0,0 +1,1433 @@
+/*
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * Fundamental types and constants relating to 802.11
+ *
+ * $Id: 802.11.h,v 9.219.4.1.4.5.6.11 2010/02/09 13:23:26 Exp $
+ */
+
+
+#ifndef _802_11_H_
+#define _802_11_H_
+
+#ifndef _TYPEDEFS_H_
+#include <typedefs.h>
+#endif
+
+#ifndef _NET_ETHERNET_H_
+#include <proto/ethernet.h>
+#endif
+
+#include <proto/wpa.h>
+
+
+#include <packed_section_start.h>
+
+
+#define DOT11_TU_TO_US			1024	
+
+
+#define DOT11_A3_HDR_LEN		24	
+#define DOT11_A4_HDR_LEN		30	
+#define DOT11_MAC_HDR_LEN		DOT11_A3_HDR_LEN	
+#define DOT11_FCS_LEN			4	
+#define DOT11_ICV_LEN			4	
+#define DOT11_ICV_AES_LEN		8	
+#define DOT11_QOS_LEN			2	
+#define DOT11_HTC_LEN			4	
+
+#define DOT11_KEY_INDEX_SHIFT		6	
+#define DOT11_IV_LEN			4	
+#define DOT11_IV_TKIP_LEN		8	
+#define DOT11_IV_AES_OCB_LEN		4	
+#define DOT11_IV_AES_CCM_LEN		8	
+#define DOT11_IV_MAX_LEN		8	
+
+
+#define DOT11_MAX_MPDU_BODY_LEN		2304	
+
+#define DOT11_MAX_MPDU_LEN		(DOT11_A4_HDR_LEN + \
+					 DOT11_QOS_LEN + \
+					 DOT11_IV_AES_CCM_LEN + \
+					 DOT11_MAX_MPDU_BODY_LEN + \
+					 DOT11_ICV_LEN + \
+					 DOT11_FCS_LEN)	
+
+#define DOT11_MAX_SSID_LEN		32	
+
+
+#define DOT11_DEFAULT_RTS_LEN		2347	
+#define DOT11_MAX_RTS_LEN		2347	
+
+
+#define DOT11_MIN_FRAG_LEN		256	
+#define DOT11_MAX_FRAG_LEN		2346	
+#define DOT11_DEFAULT_FRAG_LEN		2346	
+
+
+#define DOT11_MIN_BEACON_PERIOD		1	
+#define DOT11_MAX_BEACON_PERIOD		0xFFFF	
+
+
+#define DOT11_MIN_DTIM_PERIOD		1	
+#define DOT11_MAX_DTIM_PERIOD		0xFF	
+
+
+#define DOT11_LLC_SNAP_HDR_LEN		8	
+#define DOT11_OUI_LEN			3	
+BWL_PRE_PACKED_STRUCT struct dot11_llc_snap_header {
+	uint8	dsap;				
+	uint8	ssap;				
+	uint8	ctl;				
+	uint8	oui[DOT11_OUI_LEN];		
+	uint16	type;				
+} BWL_POST_PACKED_STRUCT;
+
+
+#define RFC1042_HDR_LEN	(ETHER_HDR_LEN + DOT11_LLC_SNAP_HDR_LEN)	
+
+
+
+BWL_PRE_PACKED_STRUCT struct dot11_header {
+	uint16			fc;		
+	uint16			durid;		
+	struct ether_addr	a1;		
+	struct ether_addr	a2;		
+	struct ether_addr	a3;		
+	uint16			seq;		
+	struct ether_addr	a4;		
+} BWL_POST_PACKED_STRUCT;
+
+
+
+BWL_PRE_PACKED_STRUCT struct dot11_rts_frame {
+	uint16			fc;		
+	uint16			durid;		
+	struct ether_addr	ra;		
+	struct ether_addr	ta;		
+} BWL_POST_PACKED_STRUCT;
+#define	DOT11_RTS_LEN		16		
+
+BWL_PRE_PACKED_STRUCT struct dot11_cts_frame {
+	uint16			fc;		
+	uint16			durid;		
+	struct ether_addr	ra;		
+} BWL_POST_PACKED_STRUCT;
+#define	DOT11_CTS_LEN		10		
+
+BWL_PRE_PACKED_STRUCT struct dot11_ack_frame {
+	uint16			fc;		
+	uint16			durid;		
+	struct ether_addr	ra;		
+} BWL_POST_PACKED_STRUCT;
+#define	DOT11_ACK_LEN		10		
+
+BWL_PRE_PACKED_STRUCT struct dot11_ps_poll_frame {
+	uint16			fc;		
+	uint16			durid;		
+	struct ether_addr	bssid;		
+	struct ether_addr	ta;		
+} BWL_POST_PACKED_STRUCT;
+#define	DOT11_PS_POLL_LEN	16		
+
+BWL_PRE_PACKED_STRUCT struct dot11_cf_end_frame {
+	uint16			fc;		
+	uint16			durid;		
+	struct ether_addr	ra;		
+	struct ether_addr	bssid;		
+} BWL_POST_PACKED_STRUCT;
+#define	DOT11_CS_END_LEN	16		
+
+BWL_PRE_PACKED_STRUCT struct dot11_action_wifi_vendor_specific {
+	uint8	category;
+	uint8	OUI[3];
+	uint8	type;
+	uint8	subtype;
+	uint8	data[1040];
+	struct dot11_action_wifi_vendor_specific* next_node;
+} BWL_POST_PACKED_STRUCT;
+
+typedef struct dot11_action_wifi_vendor_specific dot11_action_wifi_vendor_specific_t;
+
+#define DOT11_BA_CTL_POLICY_NORMAL	0x0000	
+#define DOT11_BA_CTL_POLICY_NOACK	0x0001	
+#define DOT11_BA_CTL_POLICY_MASK	0x0001	
+
+#define DOT11_BA_CTL_MTID		0x0002	
+#define DOT11_BA_CTL_COMPRESSED		0x0004	
+
+#define DOT11_BA_CTL_NUMMSDU_MASK	0x0FC0	
+#define DOT11_BA_CTL_NUMMSDU_SHIFT	6	
+
+#define DOT11_BA_CTL_TID_MASK		0xF000	
+#define DOT11_BA_CTL_TID_SHIFT		12	
+
+
+BWL_PRE_PACKED_STRUCT struct dot11_ctl_header {
+	uint16			fc;		
+	uint16			durid;		
+	struct ether_addr	ra;		
+	struct ether_addr	ta;		
+} BWL_POST_PACKED_STRUCT;
+#define DOT11_CTL_HDR_LEN	16		
+
+
+BWL_PRE_PACKED_STRUCT struct dot11_bar {
+	uint16			bar_control;	
+	uint16			seqnum;		
+} BWL_POST_PACKED_STRUCT;
+#define DOT11_BAR_LEN		4		
+
+#define DOT11_BA_BITMAP_LEN	128		
+#define DOT11_BA_CMP_BITMAP_LEN	8		
+
+BWL_PRE_PACKED_STRUCT struct dot11_ba {
+	uint16			ba_control;	
+	uint16			seqnum;		
+	uint8			bitmap[DOT11_BA_BITMAP_LEN];	
+} BWL_POST_PACKED_STRUCT;
+#define DOT11_BA_LEN		4		
+
+
+BWL_PRE_PACKED_STRUCT struct dot11_management_header {
+	uint16			fc;		
+	uint16			durid;		
+	struct ether_addr	da;		
+	struct ether_addr	sa;		
+	struct ether_addr	bssid;		
+	uint16			seq;		
+} BWL_POST_PACKED_STRUCT;
+#define	DOT11_MGMT_HDR_LEN	24		
+
+
+
+BWL_PRE_PACKED_STRUCT struct dot11_bcn_prb {
+	uint32			timestamp[2];
+	uint16			beacon_interval;
+	uint16			capability;
+} BWL_POST_PACKED_STRUCT;
+#define	DOT11_BCN_PRB_LEN	12		
+
+BWL_PRE_PACKED_STRUCT struct dot11_auth {
+	uint16			alg;		
+	uint16			seq;		
+	uint16			status;		
+} BWL_POST_PACKED_STRUCT;
+#define DOT11_AUTH_FIXED_LEN	6		
+
+BWL_PRE_PACKED_STRUCT struct dot11_assoc_req {
+	uint16			capability;	
+	uint16			listen;		
+} BWL_POST_PACKED_STRUCT;
+#define DOT11_ASSOC_REQ_FIXED_LEN	4	
+
+BWL_PRE_PACKED_STRUCT struct dot11_reassoc_req {
+	uint16			capability;	
+	uint16			listen;		
+	struct ether_addr	ap;		
+} BWL_POST_PACKED_STRUCT;
+#define DOT11_REASSOC_REQ_FIXED_LEN	10	
+
+BWL_PRE_PACKED_STRUCT struct dot11_assoc_resp {
+	uint16			capability;	
+	uint16			status;		
+	uint16			aid;		
+} BWL_POST_PACKED_STRUCT;
+#define DOT11_ASSOC_RESP_FIXED_LEN	6	
+
+BWL_PRE_PACKED_STRUCT struct dot11_action_measure {
+	uint8	category;
+	uint8	action;
+	uint8	token;
+	uint8	data[1];
+} BWL_POST_PACKED_STRUCT;
+#define DOT11_ACTION_MEASURE_LEN	3	
+
+BWL_PRE_PACKED_STRUCT struct dot11_action_ht_ch_width {
+	uint8	category;
+	uint8	action;
+	uint8	ch_width;
+} BWL_POST_PACKED_STRUCT;
+
+BWL_PRE_PACKED_STRUCT struct dot11_action_ht_mimops {
+	uint8	category;
+	uint8	action;
+	uint8	control;
+} BWL_POST_PACKED_STRUCT;
+
+#define SM_PWRSAVE_ENABLE	1
+#define SM_PWRSAVE_MODE		2
+
+
+BWL_PRE_PACKED_STRUCT struct dot11_power_cnst {
+	uint8 id;
+	uint8 len;
+	uint8 power;
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_power_cnst dot11_power_cnst_t;
+
+BWL_PRE_PACKED_STRUCT struct dot11_power_cap {
+	uint8 min;
+	uint8 max;
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_power_cap dot11_power_cap_t;
+
+BWL_PRE_PACKED_STRUCT struct dot11_tpc_rep {
+	uint8 id;
+	uint8 len;
+	uint8 tx_pwr;
+	uint8 margin;
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_tpc_rep dot11_tpc_rep_t;
+#define DOT11_MNG_IE_TPC_REPORT_LEN	2 	
+
+BWL_PRE_PACKED_STRUCT struct dot11_supp_channels {
+	uint8 id;
+	uint8 len;
+	uint8 first_channel;
+	uint8 num_channels;
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_supp_channels dot11_supp_channels_t;
+
+
+BWL_PRE_PACKED_STRUCT struct dot11_extch {
+	uint8	id;		
+	uint8	len;		
+	uint8	extch;
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_extch dot11_extch_ie_t;
+
+BWL_PRE_PACKED_STRUCT struct dot11_brcm_extch {
+	uint8	id;		
+	uint8	len;		
+	uint8	oui[3];		
+	uint8	type;           
+	uint8	extch;
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_brcm_extch dot11_brcm_extch_ie_t;
+
+#define BRCM_EXTCH_IE_LEN	5
+#define BRCM_EXTCH_IE_TYPE	53	
+#define DOT11_EXTCH_IE_LEN	1
+#define DOT11_EXT_CH_MASK	0x03	
+#define DOT11_EXT_CH_UPPER	0x01	
+#define DOT11_EXT_CH_LOWER	0x03	
+#define DOT11_EXT_CH_NONE	0x00	
+
+BWL_PRE_PACKED_STRUCT struct dot11_action_frmhdr {
+	uint8	category;
+	uint8	action;
+	uint8	data[1];
+} BWL_POST_PACKED_STRUCT;
+#define DOT11_ACTION_FRMHDR_LEN	2
+
+
+BWL_PRE_PACKED_STRUCT struct dot11_channel_switch {
+	uint8 id;	
+	uint8 len;	
+	uint8 mode;	
+	uint8 channel;	
+	uint8 count;	
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_channel_switch dot11_chan_switch_ie_t;
+
+#define DOT11_SWITCH_IE_LEN	3	
+
+#define DOT11_CSA_MODE_ADVISORY		0	
+#define DOT11_CSA_MODE_NO_TX		1	
+
+BWL_PRE_PACKED_STRUCT struct dot11_action_switch_channel {
+	uint8	category;
+	uint8	action;
+	dot11_chan_switch_ie_t chan_switch_ie;	
+	dot11_brcm_extch_ie_t extch_ie;		
+} BWL_POST_PACKED_STRUCT;
+
+BWL_PRE_PACKED_STRUCT struct dot11_csa_body {
+	uint8 mode;	
+	uint8 reg;	
+	uint8 channel;	
+	uint8 count;	
+} BWL_POST_PACKED_STRUCT;
+
+
+BWL_PRE_PACKED_STRUCT struct dot11_ext_csa {
+	uint8 id;	
+	uint8 len;	
+	struct dot11_csa_body b; 
+} BWL_POST_PACKED_STRUCT;
+
+BWL_PRE_PACKED_STRUCT struct dot11y_action_ext_csa {
+	uint8	category;
+	uint8	action;
+	struct dot11_csa_body b;	
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_ext_csa dot11_ext_csa_ie_t;
+#define DOT11_EXT_CSA_IE_LEN	4	
+
+BWL_PRE_PACKED_STRUCT struct dot11_action_ext_csa {
+	uint8	category;
+	uint8	action;
+	dot11_ext_csa_ie_t chan_switch_ie;	
+} BWL_POST_PACKED_STRUCT;
+
+BWL_PRE_PACKED_STRUCT struct dot11_obss_coex {
+	uint8	id;
+	uint8	len;
+	uint8	info;
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_obss_coex dot11_obss_coex_t;
+#define DOT11_OBSS_COEXINFO_LEN	1	
+
+#define	DOT11_OBSS_COEX_INFO_REQ		0x01
+#define	DOT11_OBSS_COEX_40MHZ_INTOLERANT	0x02
+#define	DOT11_OBSS_COEX_20MHZ_WIDTH_REQ	0x04
+
+BWL_PRE_PACKED_STRUCT struct dot11_obss_chanlist {
+	uint8	id;
+	uint8	len;
+	uint8	regclass;
+	uint8	chanlist[1];
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_obss_chanlist dot11_obss_chanlist_t;
+#define DOT11_OBSS_CHANLIST_FIXED_LEN	1	
+
+BWL_PRE_PACKED_STRUCT struct dot11_extcap_ie {
+	uint8 id;
+	uint8 len;
+	uint8 cap;
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_extcap_ie dot11_extcap_ie_t;
+#define DOT11_EXTCAP_LEN	1
+
+
+
+#define DOT11_MEASURE_TYPE_BASIC 	0	
+#define DOT11_MEASURE_TYPE_CCA 		1	
+#define DOT11_MEASURE_TYPE_RPI		2	
+
+
+#define DOT11_MEASURE_MODE_ENABLE 	(1<<1)	
+#define DOT11_MEASURE_MODE_REQUEST	(1<<2)	
+#define DOT11_MEASURE_MODE_REPORT 	(1<<3)	
+
+#define DOT11_MEASURE_MODE_LATE 	(1<<0)	
+#define DOT11_MEASURE_MODE_INCAPABLE	(1<<1)	
+#define DOT11_MEASURE_MODE_REFUSED	(1<<2)	
+
+#define DOT11_MEASURE_BASIC_MAP_BSS	((uint8)(1<<0))	
+#define DOT11_MEASURE_BASIC_MAP_OFDM	((uint8)(1<<1))	
+#define DOT11_MEASURE_BASIC_MAP_UKNOWN	((uint8)(1<<2))	
+#define DOT11_MEASURE_BASIC_MAP_RADAR	((uint8)(1<<3))	
+#define DOT11_MEASURE_BASIC_MAP_UNMEAS	((uint8)(1<<4))	
+
+BWL_PRE_PACKED_STRUCT struct dot11_meas_req {
+	uint8 id;
+	uint8 len;
+	uint8 token;
+	uint8 mode;
+	uint8 type;
+	uint8 channel;
+	uint8 start_time[8];
+	uint16 duration;
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_meas_req dot11_meas_req_t;
+#define DOT11_MNG_IE_MREQ_LEN 14	
+
+#define DOT11_MNG_IE_MREQ_FIXED_LEN 3	
+
+BWL_PRE_PACKED_STRUCT struct dot11_meas_rep {
+	uint8 id;
+	uint8 len;
+	uint8 token;
+	uint8 mode;
+	uint8 type;
+	BWL_PRE_PACKED_STRUCT union
+	{
+		BWL_PRE_PACKED_STRUCT struct {
+			uint8 channel;
+			uint8 start_time[8];
+			uint16 duration;
+			uint8 map;
+		} BWL_POST_PACKED_STRUCT basic;
+		uint8 data[1];
+	} BWL_POST_PACKED_STRUCT rep;
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_meas_rep dot11_meas_rep_t;
+
+
+#define DOT11_MNG_IE_MREP_FIXED_LEN	3	
+
+BWL_PRE_PACKED_STRUCT struct dot11_meas_rep_basic {
+	uint8 channel;
+	uint8 start_time[8];
+	uint16 duration;
+	uint8 map;
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_meas_rep_basic dot11_meas_rep_basic_t;
+#define DOT11_MEASURE_BASIC_REP_LEN	12	
+
+BWL_PRE_PACKED_STRUCT struct dot11_quiet {
+	uint8 id;
+	uint8 len;
+	uint8 count;	
+	uint8 period;	
+	uint16 duration;	
+	uint16 offset;	
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_quiet dot11_quiet_t;
+
+BWL_PRE_PACKED_STRUCT struct chan_map_tuple {
+	uint8 channel;
+	uint8 map;
+} BWL_POST_PACKED_STRUCT;
+typedef struct chan_map_tuple chan_map_tuple_t;
+
+BWL_PRE_PACKED_STRUCT struct dot11_ibss_dfs {
+	uint8 id;
+	uint8 len;
+	uint8 eaddr[ETHER_ADDR_LEN];
+	uint8 interval;
+	chan_map_tuple_t map[1];
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_ibss_dfs dot11_ibss_dfs_t;
+
+
+#define WME_OUI			"\x00\x50\xf2"	
+#define WME_VER			1	
+#define WME_TYPE		2	
+#define WME_SUBTYPE_IE		0	
+#define WME_SUBTYPE_PARAM_IE	1	
+#define WME_SUBTYPE_TSPEC	2	
+
+
+#define AC_BE			0	
+#define AC_BK			1	
+#define AC_VI			2	
+#define AC_VO			3	
+#define AC_COUNT		4	
+
+typedef uint8 ac_bitmap_t;	
+
+#define AC_BITMAP_NONE		0x0	
+#define AC_BITMAP_ALL		0xf	
+#define AC_BITMAP_TST(ab, ac)	(((ab) & (1 << (ac))) != 0)
+#define AC_BITMAP_SET(ab, ac)	(((ab) |= (1 << (ac))))
+#define AC_BITMAP_RESET(ab, ac) (((ab) &= ~(1 << (ac))))
+
+
+BWL_PRE_PACKED_STRUCT struct wme_ie {
+	uint8 oui[3];
+	uint8 type;
+	uint8 subtype;
+	uint8 version;
+	uint8 qosinfo;
+} BWL_POST_PACKED_STRUCT;
+typedef struct wme_ie wme_ie_t;
+#define WME_IE_LEN 7	
+
+BWL_PRE_PACKED_STRUCT struct edcf_acparam {
+	uint8	ACI;
+	uint8	ECW;
+	uint16  TXOP;		
+} BWL_POST_PACKED_STRUCT;
+typedef struct edcf_acparam edcf_acparam_t;
+
+
+BWL_PRE_PACKED_STRUCT struct wme_param_ie {
+	uint8 oui[3];
+	uint8 type;
+	uint8 subtype;
+	uint8 version;
+	uint8 qosinfo;
+	uint8 rsvd;
+	edcf_acparam_t acparam[AC_COUNT];
+} BWL_POST_PACKED_STRUCT;
+typedef struct wme_param_ie wme_param_ie_t;
+#define WME_PARAM_IE_LEN            24          
+
+
+#define WME_QI_AP_APSD_MASK         0x80        
+#define WME_QI_AP_APSD_SHIFT        7           
+#define WME_QI_AP_COUNT_MASK        0x0f        
+#define WME_QI_AP_COUNT_SHIFT       0           
+
+
+#define WME_QI_STA_MAXSPLEN_MASK    0x60        
+#define WME_QI_STA_MAXSPLEN_SHIFT   5           
+#define WME_QI_STA_APSD_ALL_MASK    0xf         
+#define WME_QI_STA_APSD_ALL_SHIFT   0           
+#define WME_QI_STA_APSD_BE_MASK     0x8         
+#define WME_QI_STA_APSD_BE_SHIFT    3           
+#define WME_QI_STA_APSD_BK_MASK     0x4         
+#define WME_QI_STA_APSD_BK_SHIFT    2           
+#define WME_QI_STA_APSD_VI_MASK     0x2         
+#define WME_QI_STA_APSD_VI_SHIFT    1           
+#define WME_QI_STA_APSD_VO_MASK     0x1         
+#define WME_QI_STA_APSD_VO_SHIFT    0           
+
+
+#define EDCF_AIFSN_MIN               1           
+#define EDCF_AIFSN_MAX               15          
+#define EDCF_AIFSN_MASK              0x0f        
+#define EDCF_ACM_MASK                0x10        
+#define EDCF_ACI_MASK                0x60        
+#define EDCF_ACI_SHIFT               5           
+#define EDCF_AIFSN_SHIFT             12          
+
+
+#define EDCF_ECW_MIN                 0           
+#define EDCF_ECW_MAX                 15          
+#define EDCF_ECW2CW(exp)             ((1 << (exp)) - 1)
+#define EDCF_ECWMIN_MASK             0x0f        
+#define EDCF_ECWMAX_MASK             0xf0        
+#define EDCF_ECWMAX_SHIFT            4           
+
+
+#define EDCF_TXOP_MIN                0           
+#define EDCF_TXOP_MAX                65535       
+#define EDCF_TXOP2USEC(txop)         ((txop) << 5)
+
+
+#define NON_EDCF_AC_BE_ACI_STA          0x02
+
+
+#define EDCF_AC_BE_ACI_STA           0x03	
+#define EDCF_AC_BE_ECW_STA           0xA4	
+#define EDCF_AC_BE_TXOP_STA          0x0000	
+#define EDCF_AC_BK_ACI_STA           0x27	
+#define EDCF_AC_BK_ECW_STA           0xA4	
+#define EDCF_AC_BK_TXOP_STA          0x0000	
+#define EDCF_AC_VI_ACI_STA           0x42	
+#define EDCF_AC_VI_ECW_STA           0x43	
+#define EDCF_AC_VI_TXOP_STA          0x005e	
+#define EDCF_AC_VO_ACI_STA           0x62	
+#define EDCF_AC_VO_ECW_STA           0x32	
+#define EDCF_AC_VO_TXOP_STA          0x002f	
+
+
+#define EDCF_AC_BE_ACI_AP            0x03	
+#define EDCF_AC_BE_ECW_AP            0x64	
+#define EDCF_AC_BE_TXOP_AP           0x0000	
+#define EDCF_AC_BK_ACI_AP            0x27	
+#define EDCF_AC_BK_ECW_AP            0xA4	
+#define EDCF_AC_BK_TXOP_AP           0x0000	
+#define EDCF_AC_VI_ACI_AP            0x41	
+#define EDCF_AC_VI_ECW_AP            0x43	
+#define EDCF_AC_VI_TXOP_AP           0x005e	
+#define EDCF_AC_VO_ACI_AP            0x61	
+#define EDCF_AC_VO_ECW_AP            0x32	
+#define EDCF_AC_VO_TXOP_AP           0x002f	
+
+
+BWL_PRE_PACKED_STRUCT struct edca_param_ie {
+	uint8 qosinfo;
+	uint8 rsvd;
+	edcf_acparam_t acparam[AC_COUNT];
+} BWL_POST_PACKED_STRUCT;
+typedef struct edca_param_ie edca_param_ie_t;
+#define EDCA_PARAM_IE_LEN            18          
+
+
+BWL_PRE_PACKED_STRUCT struct qos_cap_ie {
+	uint8 qosinfo;
+} BWL_POST_PACKED_STRUCT;
+typedef struct qos_cap_ie qos_cap_ie_t;
+
+BWL_PRE_PACKED_STRUCT struct dot11_qbss_load_ie {
+	uint8 id; 			
+	uint8 length;
+	uint16 station_count; 		
+	uint8 channel_utilization;	
+	uint16 aac; 			
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_qbss_load_ie dot11_qbss_load_ie_t;
+
+
+#define FIXED_MSDU_SIZE 0x8000		
+#define MSDU_SIZE_MASK	0x7fff		
+
+
+
+#define	INTEGER_SHIFT	13	
+#define FRACTION_MASK	0x1FFF	
+
+
+BWL_PRE_PACKED_STRUCT struct dot11_management_notification {
+	uint8 category;			
+	uint8 action;
+	uint8 token;
+	uint8 status;
+	uint8 data[1];			
+} BWL_POST_PACKED_STRUCT;
+#define DOT11_MGMT_NOTIFICATION_LEN 4	
+
+
+#define WME_ADDTS_REQUEST	0	
+#define WME_ADDTS_RESPONSE	1	
+#define WME_DELTS_REQUEST	2	
+
+
+#define WME_ADMISSION_ACCEPTED		0	
+#define WME_INVALID_PARAMETERS		1	
+#define WME_ADMISSION_REFUSED		3	
+
+
+#define BCN_PRB_SSID(body) ((char*)(body) + DOT11_BCN_PRB_LEN)
+
+
+#define DOT11_OPEN_SYSTEM	0	
+#define DOT11_SHARED_KEY	1	
+
+#define DOT11_OPEN_SHARED	2	
+#define DOT11_CHALLENGE_LEN	128	
+
+
+#define FC_PVER_MASK		0x3	
+#define FC_PVER_SHIFT		0	
+#define FC_TYPE_MASK		0xC	
+#define FC_TYPE_SHIFT		2	
+#define FC_SUBTYPE_MASK		0xF0	
+#define FC_SUBTYPE_SHIFT	4	
+#define FC_TODS			0x100	
+#define FC_TODS_SHIFT		8	
+#define FC_FROMDS		0x200	
+#define FC_FROMDS_SHIFT		9	
+#define FC_MOREFRAG		0x400	
+#define FC_MOREFRAG_SHIFT	10	
+#define FC_RETRY		0x800	
+#define FC_RETRY_SHIFT		11	
+#define FC_PM			0x1000	
+#define FC_PM_SHIFT		12	
+#define FC_MOREDATA		0x2000	
+#define FC_MOREDATA_SHIFT	13	
+#define FC_WEP			0x4000	
+#define FC_WEP_SHIFT		14	
+#define FC_ORDER		0x8000	
+#define FC_ORDER_SHIFT		15	
+
+
+#define SEQNUM_SHIFT		4	
+#define SEQNUM_MAX		0x1000	
+#define FRAGNUM_MASK		0xF	
+
+
+
+
+#define FC_TYPE_MNG		0	
+#define FC_TYPE_CTL		1	
+#define FC_TYPE_DATA		2	
+
+
+#define FC_SUBTYPE_ASSOC_REQ		0	
+#define FC_SUBTYPE_ASSOC_RESP		1	
+#define FC_SUBTYPE_REASSOC_REQ		2	
+#define FC_SUBTYPE_REASSOC_RESP		3	
+#define FC_SUBTYPE_PROBE_REQ		4	
+#define FC_SUBTYPE_PROBE_RESP		5	
+#define FC_SUBTYPE_BEACON		8	
+#define FC_SUBTYPE_ATIM			9	
+#define FC_SUBTYPE_DISASSOC		10	
+#define FC_SUBTYPE_AUTH			11	
+#define FC_SUBTYPE_DEAUTH		12	
+#define FC_SUBTYPE_ACTION		13	
+#define FC_SUBTYPE_ACTION_NOACK		14	
+
+
+#define FC_SUBTYPE_CTL_WRAPPER		7	
+#define FC_SUBTYPE_BLOCKACK_REQ		8	
+#define FC_SUBTYPE_BLOCKACK		9	
+#define FC_SUBTYPE_PS_POLL		10	
+#define FC_SUBTYPE_RTS			11	
+#define FC_SUBTYPE_CTS			12	
+#define FC_SUBTYPE_ACK			13	
+#define FC_SUBTYPE_CF_END		14	
+#define FC_SUBTYPE_CF_END_ACK		15	
+
+
+#define FC_SUBTYPE_DATA			0	
+#define FC_SUBTYPE_DATA_CF_ACK		1	
+#define FC_SUBTYPE_DATA_CF_POLL		2	
+#define FC_SUBTYPE_DATA_CF_ACK_POLL	3	
+#define FC_SUBTYPE_NULL			4	
+#define FC_SUBTYPE_CF_ACK		5	
+#define FC_SUBTYPE_CF_POLL		6	
+#define FC_SUBTYPE_CF_ACK_POLL		7	
+#define FC_SUBTYPE_QOS_DATA		8	
+#define FC_SUBTYPE_QOS_DATA_CF_ACK	9	
+#define FC_SUBTYPE_QOS_DATA_CF_POLL	10	
+#define FC_SUBTYPE_QOS_DATA_CF_ACK_POLL	11	
+#define FC_SUBTYPE_QOS_NULL		12	
+#define FC_SUBTYPE_QOS_CF_POLL		14	
+#define FC_SUBTYPE_QOS_CF_ACK_POLL	15	
+
+
+#define FC_SUBTYPE_ANY_QOS(s)		(((s) & 8) != 0)
+#define FC_SUBTYPE_ANY_NULL(s)		(((s) & 4) != 0)
+#define FC_SUBTYPE_ANY_CF_POLL(s)	(((s) & 2) != 0)
+#define FC_SUBTYPE_ANY_CF_ACK(s)	(((s) & 1) != 0)
+
+
+#define FC_KIND_MASK		(FC_TYPE_MASK | FC_SUBTYPE_MASK)	
+
+#define FC_KIND(t, s)	(((t) << FC_TYPE_SHIFT) | ((s) << FC_SUBTYPE_SHIFT))	
+
+#define FC_SUBTYPE(fc)	(((fc) & FC_SUBTYPE_MASK) >> FC_SUBTYPE_SHIFT)	
+#define FC_TYPE(fc)	(((fc) & FC_TYPE_MASK) >> FC_TYPE_SHIFT)	
+
+#define FC_ASSOC_REQ	FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_ASSOC_REQ)	
+#define FC_ASSOC_RESP	FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_ASSOC_RESP)	
+#define FC_REASSOC_REQ	FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_REASSOC_REQ)	
+#define FC_REASSOC_RESP	FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_REASSOC_RESP)	
+#define FC_PROBE_REQ	FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_PROBE_REQ)	
+#define FC_PROBE_RESP	FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_PROBE_RESP)	
+#define FC_BEACON	FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_BEACON)		
+#define FC_DISASSOC	FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_DISASSOC)	
+#define FC_AUTH		FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_AUTH)		
+#define FC_DEAUTH	FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_DEAUTH)		
+#define FC_ACTION	FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_ACTION)		
+#define FC_ACTION_NOACK	FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_ACTION_NOACK)	
+
+#define FC_CTL_WRAPPER	FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_CTL_WRAPPER)	
+#define FC_BLOCKACK_REQ	FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_BLOCKACK_REQ)	
+#define FC_BLOCKACK	FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_BLOCKACK)	
+#define FC_PS_POLL	FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_PS_POLL)	
+#define FC_RTS		FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_RTS)		
+#define FC_CTS		FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_CTS)		
+#define FC_ACK		FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_ACK)		
+#define FC_CF_END	FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_CF_END)		
+#define FC_CF_END_ACK	FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_CF_END_ACK)	
+
+#define FC_DATA		FC_KIND(FC_TYPE_DATA, FC_SUBTYPE_DATA)		
+#define FC_NULL_DATA	FC_KIND(FC_TYPE_DATA, FC_SUBTYPE_NULL)		
+#define FC_DATA_CF_ACK	FC_KIND(FC_TYPE_DATA, FC_SUBTYPE_DATA_CF_ACK)	
+#define FC_QOS_DATA	FC_KIND(FC_TYPE_DATA, FC_SUBTYPE_QOS_DATA)	
+#define FC_QOS_NULL	FC_KIND(FC_TYPE_DATA, FC_SUBTYPE_QOS_NULL)	
+
+
+
+
+#define QOS_PRIO_SHIFT		0	
+#define QOS_PRIO_MASK		0x0007	
+#define QOS_PRIO(qos)		(((qos) & QOS_PRIO_MASK) >> QOS_PRIO_SHIFT)	
+
+
+#define QOS_TID_SHIFT		0	
+#define QOS_TID_MASK		0x000f	
+#define QOS_TID(qos)		(((qos) & QOS_TID_MASK) >> QOS_TID_SHIFT)	
+
+
+#define QOS_EOSP_SHIFT		4	
+#define QOS_EOSP_MASK		0x0010	
+#define QOS_EOSP(qos)		(((qos) & QOS_EOSP_MASK) >> QOS_EOSP_SHIFT)	
+
+
+#define QOS_ACK_NORMAL_ACK	0	
+#define QOS_ACK_NO_ACK		1	
+#define QOS_ACK_NO_EXP_ACK	2	
+#define QOS_ACK_BLOCK_ACK	3	
+#define QOS_ACK_SHIFT		5	
+#define QOS_ACK_MASK		0x0060	
+#define QOS_ACK(qos)		(((qos) & QOS_ACK_MASK) >> QOS_ACK_SHIFT)	
+
+
+#define QOS_AMSDU_SHIFT		7	
+#define QOS_AMSDU_MASK		0x0080	
+
+
+
+
+
+
+#define DOT11_MNG_AUTH_ALGO_LEN		2	
+#define DOT11_MNG_AUTH_SEQ_LEN		2	
+#define DOT11_MNG_BEACON_INT_LEN	2	
+#define DOT11_MNG_CAP_LEN		2	
+#define DOT11_MNG_AP_ADDR_LEN		6	
+#define DOT11_MNG_LISTEN_INT_LEN	2	
+#define DOT11_MNG_REASON_LEN		2	
+#define DOT11_MNG_AID_LEN		2	
+#define DOT11_MNG_STATUS_LEN		2	
+#define DOT11_MNG_TIMESTAMP_LEN		8	
+
+
+#define DOT11_AID_MASK			0x3fff	
+
+
+#define DOT11_RC_RESERVED		0	
+#define DOT11_RC_UNSPECIFIED		1	
+#define DOT11_RC_AUTH_INVAL		2	
+#define DOT11_RC_DEAUTH_LEAVING		3	
+#define DOT11_RC_INACTIVITY		4	
+#define DOT11_RC_BUSY			5	
+#define DOT11_RC_INVAL_CLASS_2		6	
+#define DOT11_RC_INVAL_CLASS_3		7	
+#define DOT11_RC_DISASSOC_LEAVING	8	
+#define DOT11_RC_NOT_AUTH		9	
+#define DOT11_RC_BAD_PC			10	
+#define DOT11_RC_BAD_CHANNELS		11	
+
+
+
+#define DOT11_RC_UNSPECIFIED_QOS	32	
+#define DOT11_RC_INSUFFCIENT_BW		33	
+#define DOT11_RC_EXCESSIVE_FRAMES	34	
+#define DOT11_RC_TX_OUTSIDE_TXOP	35	
+#define DOT11_RC_LEAVING_QBSS		36	
+#define DOT11_RC_BAD_MECHANISM		37	
+#define DOT11_RC_SETUP_NEEDED		38	
+#define DOT11_RC_TIMEOUT		39	
+
+#define DOT11_RC_MAX			23	
+
+
+#define DOT11_SC_SUCCESS		0	
+#define DOT11_SC_FAILURE		1	
+#define DOT11_SC_CAP_MISMATCH		10	
+#define DOT11_SC_REASSOC_FAIL		11	
+#define DOT11_SC_ASSOC_FAIL		12	
+#define DOT11_SC_AUTH_MISMATCH		13	
+#define DOT11_SC_AUTH_SEQ		14	
+#define DOT11_SC_AUTH_CHALLENGE_FAIL	15	
+#define DOT11_SC_AUTH_TIMEOUT		16	
+#define DOT11_SC_ASSOC_BUSY_FAIL	17	
+#define DOT11_SC_ASSOC_RATE_MISMATCH	18	
+#define DOT11_SC_ASSOC_SHORT_REQUIRED	19	
+#define DOT11_SC_ASSOC_PBCC_REQUIRED	20	
+#define DOT11_SC_ASSOC_AGILITY_REQUIRED	21	
+#define DOT11_SC_ASSOC_SPECTRUM_REQUIRED	22	
+#define DOT11_SC_ASSOC_BAD_POWER_CAP	23	
+#define DOT11_SC_ASSOC_BAD_SUP_CHANNELS	24	
+#define DOT11_SC_ASSOC_SHORTSLOT_REQUIRED	25	
+#define DOT11_SC_ASSOC_ERPBCC_REQUIRED	26	
+#define DOT11_SC_ASSOC_DSSOFDM_REQUIRED	27	
+
+#define	DOT11_SC_DECLINED		37	
+#define	DOT11_SC_INVALID_PARAMS		38	
+
+
+#define DOT11_MNG_DS_PARAM_LEN			1	
+#define DOT11_MNG_IBSS_PARAM_LEN		2	
+
+
+#define DOT11_MNG_TIM_FIXED_LEN			3	
+#define DOT11_MNG_TIM_DTIM_COUNT		0	
+#define DOT11_MNG_TIM_DTIM_PERIOD		1	
+#define DOT11_MNG_TIM_BITMAP_CTL		2	
+#define DOT11_MNG_TIM_PVB			3	
+
+
+#define TLV_TAG_OFF		0	
+#define TLV_LEN_OFF		1	
+#define TLV_HDR_LEN		2	
+#define TLV_BODY_OFF		2	
+
+
+#define DOT11_MNG_SSID_ID			0	
+#define DOT11_MNG_RATES_ID			1	
+#define DOT11_MNG_FH_PARMS_ID			2	
+#define DOT11_MNG_DS_PARMS_ID			3	
+#define DOT11_MNG_CF_PARMS_ID			4	
+#define DOT11_MNG_TIM_ID			5	
+#define DOT11_MNG_IBSS_PARMS_ID			6	
+#define DOT11_MNG_COUNTRY_ID			7	
+#define DOT11_MNG_HOPPING_PARMS_ID		8	
+#define DOT11_MNG_HOPPING_TABLE_ID		9	
+#define DOT11_MNG_REQUEST_ID			10	
+#define DOT11_MNG_QBSS_LOAD_ID 			11	
+#define DOT11_MNG_EDCA_PARAM_ID			12	
+#define DOT11_MNG_CHALLENGE_ID			16	
+#define DOT11_MNG_PWR_CONSTRAINT_ID		32	
+#define DOT11_MNG_PWR_CAP_ID			33	
+#define DOT11_MNG_TPC_REQUEST_ID 		34	
+#define DOT11_MNG_TPC_REPORT_ID			35	
+#define DOT11_MNG_SUPP_CHANNELS_ID		36	
+#define DOT11_MNG_CHANNEL_SWITCH_ID		37	
+#define DOT11_MNG_MEASURE_REQUEST_ID		38	
+#define DOT11_MNG_MEASURE_REPORT_ID		39	
+#define DOT11_MNG_QUIET_ID			40	
+#define DOT11_MNG_IBSS_DFS_ID			41	
+#define DOT11_MNG_ERP_ID			42	
+#define DOT11_MNG_TS_DELAY_ID			43	
+#define	DOT11_MNG_HT_CAP			45	
+#define DOT11_MNG_QOS_CAP_ID			46	
+#define DOT11_MNG_NONERP_ID			47	
+#define DOT11_MNG_RSN_ID			48	
+#define DOT11_MNG_EXT_RATES_ID			50	
+#define	DOT11_MNG_REGCLASS_ID			59	
+#define DOT11_MNG_EXT_CSA_ID			60	
+#define	DOT11_MNG_HT_ADD			61	
+#define	DOT11_MNG_EXT_CHANNEL_OFFSET		62	
+#define DOT11_MNG_WAPI_ID				68	
+#define	DOT11_MNG_HT_BSS_COEXINFO_ID		72	
+#define	DOT11_MNG_HT_BSS_CHANNEL_REPORT_ID	73	
+#define	DOT11_MNG_HT_OBSS_ID			74	
+#define	DOT11_MNG_EXT_CAP			127	
+#define DOT11_MNG_WPA_ID			221	
+#define DOT11_MNG_PROPR_ID			221	
+
+
+#define DOT11_RATE_BASIC			0x80	
+#define DOT11_RATE_MASK				0x7F	
+
+
+#define DOT11_MNG_ERP_LEN			1	
+#define DOT11_MNG_NONERP_PRESENT		0x01	
+#define DOT11_MNG_USE_PROTECTION		0x02	
+#define DOT11_MNG_BARKER_PREAMBLE		0x04	
+
+#define DOT11_MGN_TS_DELAY_LEN		4	
+#define TS_DELAY_FIELD_SIZE			4	
+
+
+#define DOT11_CAP_ESS				0x0001	
+#define DOT11_CAP_IBSS				0x0002	
+#define DOT11_CAP_POLLABLE			0x0004	
+#define DOT11_CAP_POLL_RQ			0x0008	
+#define DOT11_CAP_PRIVACY			0x0010	
+#define DOT11_CAP_SHORT				0x0020	
+#define DOT11_CAP_PBCC				0x0040	
+#define DOT11_CAP_AGILITY			0x0080	
+#define DOT11_CAP_SPECTRUM			0x0100	
+#define DOT11_CAP_SHORTSLOT			0x0400	
+#define DOT11_CAP_CCK_OFDM			0x2000	
+
+
+#define DOT11_OBSS_COEX_MNG_SUPPORT	0x01	
+
+
+#define DOT11_ACTION_HDR_LEN		2	
+#define DOT11_ACTION_CAT_ERR_MASK	0x80	
+#define DOT11_ACTION_CAT_MASK		0x7F	
+#define DOT11_ACTION_CAT_SPECT_MNG	0	
+#define DOT11_ACTION_CAT_BLOCKACK	3	
+#define DOT11_ACTION_CAT_PUBLIC		4	
+#define DOT11_ACTION_CAT_HT		7	
+#define DOT11_ACTION_CAT_VS			127	
+#define DOT11_ACTION_NOTIFICATION	0x11	
+
+#define DOT11_ACTION_ID_M_REQ		0	
+#define DOT11_ACTION_ID_M_REP		1	
+#define DOT11_ACTION_ID_TPC_REQ		2	
+#define DOT11_ACTION_ID_TPC_REP		3	
+#define DOT11_ACTION_ID_CHANNEL_SWITCH	4	
+#define DOT11_ACTION_ID_EXT_CSA		5	
+
+
+#define DOT11_ACTION_ID_HT_CH_WIDTH	0	
+#define DOT11_ACTION_ID_HT_MIMO_PS	1	
+
+
+#define DOT11_PUB_ACTION_BSS_COEX_MNG	0	
+#define DOT11_PUB_ACTION_CHANNEL_SWITCH	4	
+
+
+#define DOT11_BA_ACTION_ADDBA_REQ	0	
+#define DOT11_BA_ACTION_ADDBA_RESP	1	
+#define DOT11_BA_ACTION_DELBA		2	
+
+
+#define DOT11_ADDBA_PARAM_AMSDU_SUP	0x0001	
+#define DOT11_ADDBA_PARAM_POLICY_MASK	0x0002	
+#define DOT11_ADDBA_PARAM_POLICY_SHIFT	1	
+#define DOT11_ADDBA_PARAM_TID_MASK	0x003c	
+#define DOT11_ADDBA_PARAM_TID_SHIFT	2	
+#define DOT11_ADDBA_PARAM_BSIZE_MASK	0xffc0	
+#define DOT11_ADDBA_PARAM_BSIZE_SHIFT	6	
+
+#define DOT11_ADDBA_POLICY_DELAYED	0	
+#define DOT11_ADDBA_POLICY_IMMEDIATE	1	
+
+BWL_PRE_PACKED_STRUCT struct dot11_addba_req {
+	uint8 category;				
+	uint8 action;				
+	uint8 token;				
+	uint16 addba_param_set;			
+	uint16 timeout;				
+	uint16 start_seqnum;			
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_addba_req dot11_addba_req_t;
+#define DOT11_ADDBA_REQ_LEN		9	
+
+BWL_PRE_PACKED_STRUCT struct dot11_addba_resp {
+	uint8 category;				
+	uint8 action;				
+	uint8 token;				
+	uint16 status;				
+	uint16 addba_param_set;			
+	uint16 timeout;				
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_addba_resp dot11_addba_resp_t;
+#define DOT11_ADDBA_RESP_LEN		9	
+
+
+#define DOT11_DELBA_PARAM_INIT_MASK	0x0800	
+#define DOT11_DELBA_PARAM_INIT_SHIFT	11	
+#define DOT11_DELBA_PARAM_TID_MASK	0xf000	
+#define DOT11_DELBA_PARAM_TID_SHIFT	12	
+
+BWL_PRE_PACKED_STRUCT struct dot11_delba {
+	uint8 category;				
+	uint8 action;				
+	uint16 delba_param_set;			
+	uint16 reason;				
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_delba dot11_delba_t;
+#define DOT11_DELBA_LEN			6	
+
+
+#define DOT11_BSSTYPE_INFRASTRUCTURE		0	
+#define DOT11_BSSTYPE_INDEPENDENT		1	
+#define DOT11_BSSTYPE_ANY			2	
+#define DOT11_SCANTYPE_ACTIVE			0	
+#define DOT11_SCANTYPE_PASSIVE			1	
+
+
+#define PREN_PREAMBLE		24	
+#define PREN_MM_EXT		8	
+#define PREN_PREAMBLE_EXT	4	
+
+
+#define NPHY_RIFS_TIME		2	
+
+
+#define APHY_SLOT_TIME		9	
+#define APHY_SIFS_TIME		16	
+#define APHY_DIFS_TIME		(APHY_SIFS_TIME + (2 * APHY_SLOT_TIME))	
+#define APHY_PREAMBLE_TIME	16	
+#define APHY_SIGNAL_TIME	4	
+#define APHY_SYMBOL_TIME	4	
+#define APHY_SERVICE_NBITS	16	
+#define APHY_TAIL_NBITS		6	
+#define	APHY_CWMIN		15	
+
+
+#define BPHY_SLOT_TIME		20	
+#define BPHY_SIFS_TIME		10	
+#define BPHY_DIFS_TIME		50	
+#define BPHY_PLCP_TIME		192	
+#define BPHY_PLCP_SHORT_TIME	96	
+#define	BPHY_CWMIN		31	
+
+
+#define DOT11_OFDM_SIGNAL_EXTENSION	6	
+
+#define PHY_CWMAX		1023	
+
+#define	DOT11_MAXNUMFRAGS	16	
+
+
+typedef struct d11cnt {
+	uint32		txfrag;		
+	uint32		txmulti;	
+	uint32		txfail;		
+	uint32		txretry;	
+	uint32		txretrie;	
+	uint32		rxdup;		
+	uint32		txrts;		
+	uint32		txnocts;	
+	uint32		txnoack;	
+	uint32		rxfrag;		
+	uint32		rxmulti;	
+	uint32		rxcrc;		
+	uint32		txfrmsnt;	
+	uint32		rxundec;	
+} d11cnt_t;
+
+
+#define BRCM_PROP_OUI		"\x00\x90\x4C"	
+
+
+
+
+BWL_PRE_PACKED_STRUCT struct brcm_prop_ie_s {
+	uint8 id;		
+	uint8 len;		
+	uint8 oui[3];		
+	uint8 type;		
+	uint16 cap;		
+} BWL_POST_PACKED_STRUCT;
+typedef struct brcm_prop_ie_s brcm_prop_ie_t;
+
+#define BRCM_PROP_IE_LEN	6	
+
+#define DPT_IE_TYPE		2
+
+
+#define BRCM_OUI		"\x00\x10\x18"	
+
+
+BWL_PRE_PACKED_STRUCT struct brcm_ie {
+	uint8	id;		
+	uint8	len;		
+	uint8	oui[3];		
+	uint8	ver;		
+	uint8	assoc;		
+	uint8	flags;		
+	uint8	flags1;		
+	uint16	amsdu_mtu_pref;	
+} BWL_POST_PACKED_STRUCT;
+typedef	struct brcm_ie brcm_ie_t;
+#define BRCM_IE_LEN		11	
+#define BRCM_IE_VER		2	
+#define BRCM_IE_LEGACY_AES_VER	1	
+
+
+#ifdef WLAFTERBURNER
+#define	BRF_ABCAP		0x1	
+#define	BRF_ABRQRD		0x2	
+#define BRF_ABCOUNTER_MASK	0xf0	
+#define BRF_ABCOUNTER_SHIFT	4	
+#endif 
+#define	BRF_LZWDS		0x4	
+#define	BRF_BLOCKACK		0x8	
+
+
+#define	BRF1_AMSDU		0x1	
+#define BRF1_WMEPS		0x4	
+#define BRF1_PSOFIX		0x8	
+
+#ifdef WLAFTERBURNER
+#define AB_WDS_TIMEOUT_MAX	15	
+#define AB_WDS_TIMEOUT_MIN	1	
+#endif 
+
+#define AB_GUARDCOUNT	10		
+
+#define MCSSET_LEN	16	
+#define MAX_MCS_NUM	(128)	
+
+BWL_PRE_PACKED_STRUCT struct ht_cap_ie {
+	uint16	cap;
+	uint8	params;
+	uint8	supp_mcs[MCSSET_LEN];
+	uint16	ext_htcap;
+	uint32	txbf_cap;
+	uint8	as_cap;
+} BWL_POST_PACKED_STRUCT;
+typedef struct ht_cap_ie ht_cap_ie_t;
+
+
+
+BWL_PRE_PACKED_STRUCT struct ht_prop_cap_ie {
+	uint8	id;		
+	uint8	len;		
+	uint8	oui[3];		
+	uint8	type;           
+	ht_cap_ie_t cap_ie;
+} BWL_POST_PACKED_STRUCT;
+typedef struct ht_prop_cap_ie ht_prop_cap_ie_t;
+#define HT_PROP_IE_OVERHEAD	4	
+#define HT_CAP_IE_LEN	26
+#define HT_CAP_IE_TYPE	51
+
+#define HT_CAP_LDPC_CODING	0x0001	
+#define HT_CAP_40MHZ		0x0002  
+#define HT_CAP_MIMO_PS_MASK	0x000C  
+#define HT_CAP_MIMO_PS_SHIFT	0x0002	
+#define HT_CAP_MIMO_PS_OFF	0x0003	
+#define HT_CAP_MIMO_PS_RTS	0x0001	
+#define HT_CAP_MIMO_PS_ON	0x0000	
+#define HT_CAP_GF		0x0010	
+#define HT_CAP_SHORT_GI_20	0x0020	
+#define HT_CAP_SHORT_GI_40	0x0040	
+#define HT_CAP_TX_STBC		0x0080	
+#define HT_CAP_RX_STBC_MASK	0x0300	
+#define HT_CAP_RX_STBC_SHIFT	8	
+#define HT_CAP_DELAYED_BA	0x0400	
+#define HT_CAP_MAX_AMSDU	0x0800	
+#define HT_CAP_DSSS_CCK	0x1000	
+#define HT_CAP_PSMP		0x2000	
+#define HT_CAP_40MHZ_INTOLERANT 0x4000	
+#define HT_CAP_LSIG_TXOP	0x8000	
+
+#define HT_CAP_RX_STBC_NO		0x0	
+#define HT_CAP_RX_STBC_ONE_STREAM	0x1	
+#define HT_CAP_RX_STBC_TWO_STREAM	0x2	
+#define HT_CAP_RX_STBC_THREE_STREAM	0x3	
+
+#define HT_MAX_AMSDU		7935	
+#define HT_MIN_AMSDU		3835	
+
+#define HT_PARAMS_RX_FACTOR_MASK	0x03	
+#define HT_PARAMS_DENSITY_MASK		0x1C	
+#define HT_PARAMS_DENSITY_SHIFT	2	
+
+
+#define AMPDU_MAX_MPDU_DENSITY	7	
+#define AMPDU_RX_FACTOR_64K	3	
+#define AMPDU_RX_FACTOR_BASE	8*1024	
+#define AMPDU_DELIMITER_LEN	4	
+
+#define HT_CAP_EXT_PCO			0x0001
+#define HT_CAP_EXT_PCO_TTIME_MASK	0x0006
+#define HT_CAP_EXT_PCO_TTIME_SHIFT	1
+#define HT_CAP_EXT_MCS_FEEDBACK_MASK	0x0300
+#define HT_CAP_EXT_MCS_FEEDBACK_SHIFT	8
+#define HT_CAP_EXT_HTC			0x0400
+#define HT_CAP_EXT_RD_RESP		0x0800
+
+BWL_PRE_PACKED_STRUCT struct ht_add_ie {
+	uint8	ctl_ch;			
+	uint8	byte1;			
+	uint16	opmode;			
+	uint16	misc_bits;		
+	uint8	basic_mcs[MCSSET_LEN];  
+} BWL_POST_PACKED_STRUCT;
+typedef struct ht_add_ie ht_add_ie_t;
+
+
+
+BWL_PRE_PACKED_STRUCT struct ht_prop_add_ie {
+	uint8	id;		
+	uint8	len;		
+	uint8	oui[3];		
+	uint8	type;		
+	ht_add_ie_t add_ie;
+} BWL_POST_PACKED_STRUCT;
+typedef struct ht_prop_add_ie ht_prop_add_ie_t;
+
+#define HT_ADD_IE_LEN	22
+#define HT_ADD_IE_TYPE	52
+
+
+#define HT_BW_ANY		0x04	
+#define HT_RIFS_PERMITTED     	0x08	
+
+
+#define HT_OPMODE_MASK	        0x0003	
+#define HT_OPMODE_SHIFT	0	
+#define HT_OPMODE_PURE		0x0000	
+#define HT_OPMODE_OPTIONAL	0x0001	
+#define HT_OPMODE_HT20IN40	0x0002	
+#define HT_OPMODE_MIXED	0x0003	
+#define HT_OPMODE_NONGF	0x0004	
+#define DOT11N_TXBURST		0x0008	
+#define DOT11N_OBSS_NONHT	0x0010	
+
+
+#define HT_BASIC_STBC_MCS	0x007f	
+#define HT_DUAL_STBC_PROT	0x0080	
+#define HT_SECOND_BCN		0x0100	
+#define HT_LSIG_TXOP		0x0200	
+#define HT_PCO_ACTIVE		0x0400	
+#define HT_PCO_PHASE		0x0800	
+#define HT_DUALCTS_PROTECTION	0x0080	
+
+
+#define DOT11N_2G_TXBURST_LIMIT	6160	
+#define DOT11N_5G_TXBURST_LIMIT	3080	
+
+
+#define GET_HT_OPMODE(add_ie)		((ltoh16_ua(&add_ie->opmode) & HT_OPMODE_MASK) \
+					>> HT_OPMODE_SHIFT)
+#define HT_MIXEDMODE_PRESENT(add_ie)	((ltoh16_ua(&add_ie->opmode) & HT_OPMODE_MASK) \
+					== HT_OPMODE_MIXED)	
+#define HT_HT20_PRESENT(add_ie)	((ltoh16_ua(&add_ie->opmode) & HT_OPMODE_MASK) \
+					== HT_OPMODE_HT20IN40)	
+#define HT_OPTIONAL_PRESENT(add_ie)	((ltoh16_ua(&add_ie->opmode) & HT_OPMODE_MASK) \
+					== HT_OPMODE_OPTIONAL)	
+#define HT_USE_PROTECTION(add_ie)	(HT_HT20_PRESENT((add_ie)) || \
+					HT_MIXEDMODE_PRESENT((add_ie))) 
+#define HT_NONGF_PRESENT(add_ie)	((ltoh16_ua(&add_ie->opmode) & HT_OPMODE_NONGF) \
+					== HT_OPMODE_NONGF)	
+#define DOT11N_TXBURST_PRESENT(add_ie)	((ltoh16_ua(&add_ie->opmode) & DOT11N_TXBURST) \
+					== DOT11N_TXBURST)	
+#define DOT11N_OBSS_NONHT_PRESENT(add_ie)	((ltoh16_ua(&add_ie->opmode) & DOT11N_OBSS_NONHT) \
+					== DOT11N_OBSS_NONHT)	
+
+BWL_PRE_PACKED_STRUCT struct obss_params {
+	uint16	passive_dwell;
+	uint16	active_dwell;
+	uint16	bss_widthscan_interval;
+	uint16	passive_total;
+	uint16	active_total;
+	uint16	chanwidth_transition_dly;
+	uint16	activity_threshold;
+} BWL_POST_PACKED_STRUCT;
+typedef struct obss_params obss_params_t;
+
+BWL_PRE_PACKED_STRUCT struct dot11_obss_ie {
+	uint8	id;
+	uint8	len;
+	obss_params_t obss_params;
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_obss_ie dot11_obss_ie_t;
+#define DOT11_OBSS_SCAN_IE_LEN	sizeof(obss_params_t)	
+
+
+BWL_PRE_PACKED_STRUCT struct vndr_ie {
+	uchar id;
+	uchar len;
+	uchar oui [3];
+	uchar data [1]; 	
+} BWL_POST_PACKED_STRUCT;
+typedef struct vndr_ie vndr_ie_t;
+
+#define VNDR_IE_HDR_LEN		2	
+#define VNDR_IE_MIN_LEN		3	
+#define VNDR_IE_MAX_LEN		256	
+
+
+#define WPA_VERSION		1	
+#define WPA_OUI			"\x00\x50\xF2"	
+
+#define WPA2_VERSION		1	
+#define WPA2_VERSION_LEN	2	
+#define WPA2_OUI		"\x00\x0F\xAC"	
+
+#define WPA_OUI_LEN	3	
+
+
+#define RSN_AKM_NONE		0	
+#define RSN_AKM_UNSPECIFIED	1	
+#define RSN_AKM_PSK		2	
+
+
+#define DOT11_MAX_DEFAULT_KEYS	4	
+#define DOT11_MAX_KEY_SIZE	32	
+#define DOT11_MAX_IV_SIZE	16	
+#define DOT11_EXT_IV_FLAG	(1<<5)	
+#define DOT11_WPA_KEY_RSC_LEN   8       
+
+#define WEP1_KEY_SIZE		5	
+#define WEP1_KEY_HEX_SIZE	10	
+#define WEP128_KEY_SIZE		13	
+#define WEP128_KEY_HEX_SIZE	26	
+#define TKIP_MIC_SIZE		8	
+#define TKIP_EOM_SIZE		7	
+#define TKIP_EOM_FLAG		0x5a	
+#define TKIP_KEY_SIZE		32	
+#define TKIP_MIC_AUTH_TX	16	
+#define TKIP_MIC_AUTH_RX	24	
+#define TKIP_MIC_SUP_RX		TKIP_MIC_AUTH_TX	
+#define TKIP_MIC_SUP_TX		TKIP_MIC_AUTH_RX	
+#define AES_KEY_SIZE		16	
+#define AES_MIC_SIZE		8	
+
+#define SMS4_KEY_LEN		16
+#define SMS4_WPI_CBC_MAC_LEN	16
+
+
+#include <packed_section_end.h>
+
+
+#endif 
diff --git a/drivers/net/wireless/bcm4329/include/proto/802.11e.h b/drivers/net/wireless/bcm4329/include/proto/802.11e.h
new file mode 100644
index 0000000..1dd6f45
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/proto/802.11e.h
@@ -0,0 +1,131 @@
+/*
+ * 802.11e protocol header file
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: 802.11e.h,v 1.5.56.1 2008/11/20 00:51:18 Exp $
+ */
+
+#ifndef _802_11e_H_
+#define _802_11e_H_
+
+#ifndef _TYPEDEFS_H_
+#include <typedefs.h>
+#endif
+
+/* This marks the start of a packed structure section. */
+#include <packed_section_start.h>
+
+
+/* WME Traffic Specification (TSPEC) element */
+#define WME_TSPEC_HDR_LEN           2           /* WME TSPEC header length */
+#define WME_TSPEC_BODY_OFF          2           /* WME TSPEC body offset */
+
+#define WME_CATEGORY_CODE_OFFSET	0		/* WME Category code offset */
+#define WME_ACTION_CODE_OFFSET		1		/* WME Action code offset */
+#define WME_TOKEN_CODE_OFFSET		2		/* WME Token code offset */
+#define WME_STATUS_CODE_OFFSET		3		/* WME Status code offset */
+
+BWL_PRE_PACKED_STRUCT struct tsinfo {
+	uint8 octets[3];
+} BWL_POST_PACKED_STRUCT;
+
+typedef struct tsinfo tsinfo_t;
+
+/* 802.11e TSPEC IE */
+typedef BWL_PRE_PACKED_STRUCT struct tspec {
+	uint8 oui[DOT11_OUI_LEN];	/* WME_OUI */
+	uint8 type;					/* WME_TYPE */
+	uint8 subtype;				/* WME_SUBTYPE_TSPEC */
+	uint8 version;				/* WME_VERSION */
+	tsinfo_t tsinfo;			/* TS Info bit field */
+	uint16 nom_msdu_size;		/* (Nominal or fixed) MSDU Size (bytes) */
+	uint16 max_msdu_size;		/* Maximum MSDU Size (bytes) */
+	uint32 min_srv_interval;	/* Minimum Service Interval (us) */
+	uint32 max_srv_interval;	/* Maximum Service Interval (us) */
+	uint32 inactivity_interval;	/* Inactivity Interval (us) */
+	uint32 suspension_interval; /* Suspension Interval (us) */
+	uint32 srv_start_time;		/* Service Start Time (us) */
+	uint32 min_data_rate;		/* Minimum Data Rate (bps) */
+	uint32 mean_data_rate;		/* Mean Data Rate (bps) */
+	uint32 peak_data_rate;		/* Peak Data Rate (bps) */
+	uint32 max_burst_size;		/* Maximum Burst Size (bytes) */
+	uint32 delay_bound;			/* Delay Bound (us) */
+	uint32 min_phy_rate;		/* Minimum PHY Rate (bps) */
+	uint16 surplus_bw;			/* Surplus Bandwidth Allowance (range 1.0-8.0) */
+	uint16 medium_time;			/* Medium Time (32 us/s periods) */
+} BWL_POST_PACKED_STRUCT tspec_t;
+
+#define WME_TSPEC_LEN	(sizeof(tspec_t))		/* not including 2-bytes of header */
+
+/* ts_info */
+/* 802.1D priority is duplicated - bits 13-11 AND bits 3-1 */
+#define TS_INFO_TID_SHIFT		1	/* TS info. TID shift */
+#define TS_INFO_TID_MASK		(0xf << TS_INFO_TID_SHIFT)	/* TS info. TID mask */
+#define TS_INFO_CONTENTION_SHIFT	7	/* TS info. contention shift */
+#define TS_INFO_CONTENTION_MASK	(0x1 << TS_INFO_CONTENTION_SHIFT) /* TS info. contention mask */
+#define TS_INFO_DIRECTION_SHIFT	5	/* TS info. direction shift */
+#define TS_INFO_DIRECTION_MASK	(0x3 << TS_INFO_DIRECTION_SHIFT) /* TS info. direction mask */
+#define TS_INFO_PSB_SHIFT		2		/* TS info. PSB bit Shift */
+#define TS_INFO_PSB_MASK		(1 << TS_INFO_PSB_SHIFT)	/* TS info. PSB mask */
+#define TS_INFO_UPLINK			(0 << TS_INFO_DIRECTION_SHIFT)	/* TS info. uplink */
+#define TS_INFO_DOWNLINK		(1 << TS_INFO_DIRECTION_SHIFT)	/* TS info. downlink */
+#define TS_INFO_BIDIRECTIONAL	(3 << TS_INFO_DIRECTION_SHIFT)	/* TS info. bidirectional */
+#define TS_INFO_USER_PRIO_SHIFT	3	/* TS info. user priority shift */
+/* TS info. user priority mask */
+#define TS_INFO_USER_PRIO_MASK	(0x7 << TS_INFO_USER_PRIO_SHIFT)
+
+/* Macro to get/set bit(s) field in TSINFO */
+#define WLC_CAC_GET_TID(pt)	((((pt).octets[0]) & TS_INFO_TID_MASK) >> TS_INFO_TID_SHIFT)
+#define WLC_CAC_GET_DIR(pt)	((((pt).octets[0]) & \
+	TS_INFO_DIRECTION_MASK) >> TS_INFO_DIRECTION_SHIFT)
+#define WLC_CAC_GET_PSB(pt)	((((pt).octets[1]) & TS_INFO_PSB_MASK) >> TS_INFO_PSB_SHIFT)
+#define WLC_CAC_GET_USER_PRIO(pt)	((((pt).octets[1]) & \
+	TS_INFO_USER_PRIO_MASK) >> TS_INFO_USER_PRIO_SHIFT)
+
+#define WLC_CAC_SET_TID(pt, id)	((((pt).octets[0]) & (~TS_INFO_TID_MASK)) | \
+	((id) << TS_INFO_TID_SHIFT))
+#define WLC_CAC_SET_USER_PRIO(pt, prio)	((((pt).octets[0]) & (~TS_INFO_USER_PRIO_MASK)) | \
+	((prio) << TS_INFO_USER_PRIO_SHIFT))
+
+/* 802.11e QBSS Load IE */
+#define QBSS_LOAD_IE_LEN		5	/* QBSS Load IE length */
+#define QBSS_LOAD_AAC_OFF		3	/* AAC offset in IE */
+
+#define CAC_ADDTS_RESP_TIMEOUT		300	/* default ADDTS response timeout in ms */
+
+/* 802.11e ADDTS status code */
+#define DOT11E_STATUS_ADMISSION_ACCEPTED	0	/* TSPEC Admission accepted status */
+#define DOT11E_STATUS_ADDTS_INVALID_PARAM	1	/* TSPEC invalid parameter status */
+#define DOT11E_STATUS_ADDTS_REFUSED_NSBW	3	/* ADDTS refused (non-sufficient BW) */
+#define DOT11E_STATUS_ADDTS_REFUSED_AWHILE	47	/* ADDTS refused but could retry later */
+
+/* 802.11e DELTS status code */
+#define DOT11E_STATUS_QSTA_LEAVE_QBSS		36	/* STA leave QBSS */
+#define DOT11E_STATUS_END_TS				37	/* END TS */
+#define DOT11E_STATUS_UNKNOWN_TS			38	/* UNKNOWN TS */
+#define DOT11E_STATUS_QSTA_REQ_TIMEOUT		39	/* STA ADDTS request timeout */
+
+
+/* This marks the end of a packed structure section. */
+#include <packed_section_end.h>
+
+#endif /* _802_11e_CAC_H_ */
diff --git a/drivers/net/wireless/bcm4329/include/proto/802.1d.h b/drivers/net/wireless/bcm4329/include/proto/802.1d.h
new file mode 100644
index 0000000..45c728b
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/proto/802.1d.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * Fundamental types and constants relating to 802.1D
+ *
+ * $Id: 802.1d.h,v 9.3 2007/04/10 21:33:06 Exp $
+ */
+
+
+#ifndef _802_1_D_
+#define _802_1_D_
+
+
+#define	PRIO_8021D_NONE		2	
+#define	PRIO_8021D_BK		1	
+#define	PRIO_8021D_BE		0	
+#define	PRIO_8021D_EE		3	
+#define	PRIO_8021D_CL		4	
+#define	PRIO_8021D_VI		5	
+#define	PRIO_8021D_VO		6	
+#define	PRIO_8021D_NC		7	
+#define	MAXPRIO			7	
+#define NUMPRIO			(MAXPRIO + 1)
+
+#define ALLPRIO		-1	
+
+
+#define PRIO2PREC(prio) \
+	(((prio) == PRIO_8021D_NONE || (prio) == PRIO_8021D_BE) ? ((prio^2)) : (prio))
+
+#endif 
diff --git a/drivers/net/wireless/bcm4329/include/proto/bcmeth.h b/drivers/net/wireless/bcm4329/include/proto/bcmeth.h
new file mode 100644
index 0000000..fdb5a2a
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/proto/bcmeth.h
@@ -0,0 +1,83 @@
+/*
+ * Broadcom Ethernettype  protocol definitions
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmeth.h,v 9.9.46.1 2008/11/20 00:51:20 Exp $
+ */
+
+
+
+
+#ifndef _BCMETH_H_
+#define _BCMETH_H_
+
+#ifndef _TYPEDEFS_H_
+#include <typedefs.h>
+#endif
+
+
+#include <packed_section_start.h>
+
+
+
+
+
+
+
+#define	BCMILCP_SUBTYPE_RATE		1
+#define	BCMILCP_SUBTYPE_LINK		2
+#define	BCMILCP_SUBTYPE_CSA		3
+#define	BCMILCP_SUBTYPE_LARQ		4
+#define BCMILCP_SUBTYPE_VENDOR		5
+#define	BCMILCP_SUBTYPE_FLH		17
+
+#define BCMILCP_SUBTYPE_VENDOR_LONG	32769
+#define BCMILCP_SUBTYPE_CERT		32770
+#define BCMILCP_SUBTYPE_SES		32771
+
+
+#define BCMILCP_BCM_SUBTYPE_RESERVED		0
+#define BCMILCP_BCM_SUBTYPE_EVENT		1
+#define BCMILCP_BCM_SUBTYPE_SES			2
+
+
+#define BCMILCP_BCM_SUBTYPE_DPT			4
+
+#define BCMILCP_BCM_SUBTYPEHDR_MINLENGTH	8
+#define BCMILCP_BCM_SUBTYPEHDR_VERSION		0
+
+
+typedef BWL_PRE_PACKED_STRUCT struct bcmeth_hdr
+{
+	uint16	subtype;	
+	uint16	length;
+	uint8	version;	
+	uint8	oui[3];		
+	
+	uint16	usr_subtype;
+} BWL_POST_PACKED_STRUCT bcmeth_hdr_t;
+
+
+
+#include <packed_section_end.h>
+
+#endif	
diff --git a/drivers/net/wireless/bcm4329/include/proto/bcmevent.h b/drivers/net/wireless/bcm4329/include/proto/bcmevent.h
new file mode 100644
index 0000000..1f8ecb1
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/proto/bcmevent.h
@@ -0,0 +1,212 @@
+/*
+ * Broadcom Event  protocol definitions
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ *
+ * Dependencies: proto/bcmeth.h
+ *
+ * $Id: bcmevent.h,v 9.34.4.1.20.16.64.1 2010/11/08 21:57:03 Exp $
+ *
+ */
+
+
+
+
+#ifndef _BCMEVENT_H_
+#define _BCMEVENT_H_
+
+#ifndef _TYPEDEFS_H_
+#include <typedefs.h>
+#endif
+
+
+#include <packed_section_start.h>
+
+#define BCM_EVENT_MSG_VERSION		1	
+#define BCM_MSG_IFNAME_MAX		16	
+
+
+#define WLC_EVENT_MSG_LINK		0x01	
+#define WLC_EVENT_MSG_FLUSHTXQ		0x02	
+#define WLC_EVENT_MSG_GROUP		0x04	
+
+
+typedef BWL_PRE_PACKED_STRUCT struct
+{
+	uint16	version;
+	uint16	flags;			
+	uint32	event_type;		
+	uint32	status;			
+	uint32	reason;			
+	uint32	auth_type;		
+	uint32	datalen;		
+	struct ether_addr	addr;	
+	char	ifname[BCM_MSG_IFNAME_MAX]; 
+} BWL_POST_PACKED_STRUCT wl_event_msg_t;
+
+
+typedef BWL_PRE_PACKED_STRUCT struct bcm_event {
+	struct ether_header eth;
+	bcmeth_hdr_t		bcm_hdr;
+	wl_event_msg_t		event;
+	
+} BWL_POST_PACKED_STRUCT bcm_event_t;
+
+#define BCM_MSG_LEN	(sizeof(bcm_event_t) - sizeof(bcmeth_hdr_t) - sizeof(struct ether_header))
+
+
+#define WLC_E_SET_SSID		0	
+#define WLC_E_JOIN		1	
+#define WLC_E_START		2	
+#define WLC_E_AUTH		3	
+#define WLC_E_AUTH_IND		4	
+#define WLC_E_DEAUTH		5	
+#define WLC_E_DEAUTH_IND	6	
+#define WLC_E_ASSOC		7	
+#define WLC_E_ASSOC_IND		8	
+#define WLC_E_REASSOC		9	
+#define WLC_E_REASSOC_IND	10	
+#define WLC_E_DISASSOC		11	
+#define WLC_E_DISASSOC_IND	12	
+#define WLC_E_QUIET_START	13	
+#define WLC_E_QUIET_END		14	
+#define WLC_E_BEACON_RX		15	
+#define WLC_E_LINK		16	
+#define WLC_E_MIC_ERROR		17	
+#define WLC_E_NDIS_LINK		18	
+#define WLC_E_ROAM		19	
+#define WLC_E_TXFAIL		20	
+#define WLC_E_PMKID_CACHE	21	
+#define WLC_E_RETROGRADE_TSF	22	
+#define WLC_E_PRUNE		23	
+#define WLC_E_AUTOAUTH		24	
+#define WLC_E_EAPOL_MSG		25	
+#define WLC_E_SCAN_COMPLETE	26	
+#define WLC_E_ADDTS_IND		27	
+#define WLC_E_DELTS_IND		28	
+#define WLC_E_BCNSENT_IND	29	
+#define WLC_E_BCNRX_MSG		30	
+#define WLC_E_BCNLOST_MSG	31	
+#define WLC_E_ROAM_PREP		32	
+#define WLC_E_PFN_NET_FOUND	33	
+#define WLC_E_PFN_NET_LOST	34	
+#define WLC_E_RESET_COMPLETE	35
+#define WLC_E_JOIN_START	36
+#define WLC_E_ROAM_START	37
+#define WLC_E_ASSOC_START	38
+#define WLC_E_IBSS_ASSOC	39
+#define WLC_E_RADIO		40
+#define WLC_E_PSM_WATCHDOG	41	
+#define WLC_E_PROBREQ_MSG       44      
+#define WLC_E_SCAN_CONFIRM_IND  45
+#define WLC_E_PSK_SUP	46	
+#define WLC_E_COUNTRY_CODE_CHANGED 47
+#define	WLC_E_EXCEEDED_MEDIUM_TIME 48	
+#define WLC_E_ICV_ERROR		49	
+#define WLC_E_UNICAST_DECODE_ERROR 50 
+#define WLC_E_MULTICAST_DECODE_ERROR 51 
+#define WLC_E_TRACE 52
+#define WLC_E_IF		54	
+#define WLC_E_RSSI		56	
+#define WLC_E_PFN_SCAN_COMPLETE	57	
+#define WLC_E_ACTION_FRAME      58      
+#define WLC_E_ACTION_FRAME_COMPLETE 59  
+
+#define WLC_E_ESCAN_RESULT	69
+#define WLC_E_WAKE_EVENT	70
+#define WLC_E_RELOAD		71
+#define WLC_E_LAST		72
+
+
+
+#define WLC_E_STATUS_SUCCESS		0	
+#define WLC_E_STATUS_FAIL		1	
+#define WLC_E_STATUS_TIMEOUT		2	
+#define WLC_E_STATUS_NO_NETWORKS	3	
+#define WLC_E_STATUS_ABORT		4	
+#define WLC_E_STATUS_NO_ACK		5	
+#define WLC_E_STATUS_UNSOLICITED	6	
+#define WLC_E_STATUS_ATTEMPT		7	
+#define WLC_E_STATUS_PARTIAL		8	
+#define WLC_E_STATUS_NEWSCAN	9	
+#define WLC_E_STATUS_NEWASSOC	10	
+#define WLC_E_STATUS_11HQUIET	11	
+#define WLC_E_STATUS_SUPPRESS	12	
+#define WLC_E_STATUS_NOCHANS	13	
+#define WLC_E_STATUS_CCXFASTRM	14	
+#define WLC_E_STATUS_CS_ABORT	15	
+
+
+#define WLC_E_REASON_INITIAL_ASSOC	0	
+#define WLC_E_REASON_LOW_RSSI		1	
+#define WLC_E_REASON_DEAUTH		2	
+#define WLC_E_REASON_DISASSOC		3	
+#define WLC_E_REASON_BCNS_LOST		4	
+#define WLC_E_REASON_FAST_ROAM_FAILED	5	
+#define WLC_E_REASON_DIRECTED_ROAM	6	
+#define WLC_E_REASON_TSPEC_REJECTED	7	
+#define WLC_E_REASON_BETTER_AP		8	
+
+
+#define WLC_E_PRUNE_ENCR_MISMATCH	1	
+#define WLC_E_PRUNE_BCAST_BSSID		2	
+#define WLC_E_PRUNE_MAC_DENY		3	
+#define WLC_E_PRUNE_MAC_NA		4	
+#define WLC_E_PRUNE_REG_PASSV		5	
+#define WLC_E_PRUNE_SPCT_MGMT		6	
+#define WLC_E_PRUNE_RADAR		7	
+#define WLC_E_RSN_MISMATCH		8	
+#define WLC_E_PRUNE_NO_COMMON_RATES	9	
+#define WLC_E_PRUNE_BASIC_RATES		10	
+#define WLC_E_PRUNE_CIPHER_NA		12	
+#define WLC_E_PRUNE_KNOWN_STA		13	
+#define WLC_E_PRUNE_WDS_PEER		15	
+#define WLC_E_PRUNE_QBSS_LOAD		16	
+#define WLC_E_PRUNE_HOME_AP		17	
+
+
+#define WLC_E_SUP_OTHER				0	
+#define WLC_E_SUP_DECRYPT_KEY_DATA	1	
+#define WLC_E_SUP_BAD_UCAST_WEP128	2	
+#define WLC_E_SUP_BAD_UCAST_WEP40	3	
+#define WLC_E_SUP_UNSUP_KEY_LEN		4	
+#define WLC_E_SUP_PW_KEY_CIPHER		5	
+#define WLC_E_SUP_MSG3_TOO_MANY_IE	6	
+#define WLC_E_SUP_MSG3_IE_MISMATCH	7	
+#define WLC_E_SUP_NO_INSTALL_FLAG	8	
+#define WLC_E_SUP_MSG3_NO_GTK		9	
+#define WLC_E_SUP_GRP_KEY_CIPHER	10	
+#define WLC_E_SUP_GRP_MSG1_NO_GTK	11	
+#define WLC_E_SUP_GTK_DECRYPT_FAIL	12	
+#define WLC_E_SUP_SEND_FAIL			13	
+#define WLC_E_SUP_DEAUTH			14	
+#define WLC_E_SUP_WPA_PSK_TMO       15  
+
+
+#define WLC_E_IF_ADD		1	
+#define WLC_E_IF_DEL		2	
+
+#define WLC_E_RELOAD_STATUS1	1
+
+#include <packed_section_end.h>
+
+#endif 
diff --git a/drivers/net/wireless/bcm4329/include/proto/bcmip.h b/drivers/net/wireless/bcm4329/include/proto/bcmip.h
new file mode 100644
index 0000000..9d2fd6f
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/proto/bcmip.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * Fundamental constants relating to IP Protocol
+ *
+ * $Id: bcmip.h,v 9.16.186.4 2009/01/27 04:25:25 Exp $
+ */
+
+
+#ifndef _bcmip_h_
+#define _bcmip_h_
+
+#ifndef _TYPEDEFS_H_
+#include <typedefs.h>
+#endif
+
+
+#include <packed_section_start.h>
+
+
+
+#define IP_VER_OFFSET		0x0	
+#define IP_VER_MASK		0xf0	
+#define IP_VER_SHIFT		4	
+#define IP_VER_4		4	
+#define IP_VER_6		6	
+
+#define IP_VER(ip_body) \
+	((((uint8 *)(ip_body))[IP_VER_OFFSET] & IP_VER_MASK) >> IP_VER_SHIFT)
+
+#define IP_PROT_ICMP		0x1	
+#define IP_PROT_TCP		0x6	
+#define IP_PROT_UDP		0x11	
+
+
+#define IPV4_VER_HL_OFFSET	0	
+#define IPV4_TOS_OFFSET		1	
+#define IPV4_PKTLEN_OFFSET	2	
+#define IPV4_PKTFLAG_OFFSET	6	
+#define IPV4_PROT_OFFSET	9	
+#define IPV4_CHKSUM_OFFSET	10	
+#define IPV4_SRC_IP_OFFSET	12	
+#define IPV4_DEST_IP_OFFSET	16	
+#define IPV4_OPTIONS_OFFSET	20	
+
+
+#define IPV4_VER_MASK		0xf0	
+#define IPV4_VER_SHIFT		4	
+
+#define IPV4_HLEN_MASK		0x0f	
+#define IPV4_HLEN(ipv4_body)	(4 * (((uint8 *)(ipv4_body))[IPV4_VER_HL_OFFSET] & IPV4_HLEN_MASK))
+
+#define IPV4_ADDR_LEN		4	
+
+#define IPV4_ADDR_NULL(a)	((((uint8 *)(a))[0] | ((uint8 *)(a))[1] | \
+				  ((uint8 *)(a))[2] | ((uint8 *)(a))[3]) == 0)
+
+#define IPV4_ADDR_BCAST(a)	((((uint8 *)(a))[0] & ((uint8 *)(a))[1] & \
+				  ((uint8 *)(a))[2] & ((uint8 *)(a))[3]) == 0xff)
+
+#define	IPV4_TOS_DSCP_MASK	0xfc	
+#define	IPV4_TOS_DSCP_SHIFT	2	
+
+#define	IPV4_TOS(ipv4_body)	(((uint8 *)(ipv4_body))[IPV4_TOS_OFFSET])
+
+#define	IPV4_TOS_PREC_MASK	0xe0	
+#define	IPV4_TOS_PREC_SHIFT	5	
+
+#define IPV4_TOS_LOWDELAY	0x10	
+#define IPV4_TOS_THROUGHPUT	0x8	
+#define IPV4_TOS_RELIABILITY	0x4	
+
+#define IPV4_PROT(ipv4_body)	(((uint8 *)(ipv4_body))[IPV4_PROT_OFFSET])
+
+#define IPV4_FRAG_RESV		0x8000	
+#define IPV4_FRAG_DONT		0x4000	
+#define IPV4_FRAG_MORE		0x2000	
+#define IPV4_FRAG_OFFSET_MASK	0x1fff	
+
+#define IPV4_ADDR_STR_LEN	16	
+
+
+BWL_PRE_PACKED_STRUCT struct ipv4_addr {
+	uint8	addr[IPV4_ADDR_LEN];
+} BWL_POST_PACKED_STRUCT;
+
+BWL_PRE_PACKED_STRUCT struct ipv4_hdr {
+	uint8	version_ihl;		
+	uint8	tos;			
+	uint16	tot_len;		
+	uint16	id;
+	uint16	frag;			
+	uint8	ttl;			
+	uint8	prot;			
+	uint16	hdr_chksum;		
+	uint8	src_ip[IPV4_ADDR_LEN];	
+	uint8	dst_ip[IPV4_ADDR_LEN];	
+} BWL_POST_PACKED_STRUCT;
+
+
+#define IPV6_PAYLOAD_LEN_OFFSET	4	
+#define IPV6_NEXT_HDR_OFFSET	6	
+#define IPV6_HOP_LIMIT_OFFSET	7	
+#define IPV6_SRC_IP_OFFSET	8	
+#define IPV6_DEST_IP_OFFSET	24	
+
+
+#define IPV6_TRAFFIC_CLASS(ipv6_body) \
+	(((((uint8 *)(ipv6_body))[0] & 0x0f) << 4) | \
+	 ((((uint8 *)(ipv6_body))[1] & 0xf0) >> 4))
+
+#define IPV6_FLOW_LABEL(ipv6_body) \
+	(((((uint8 *)(ipv6_body))[1] & 0x0f) << 16) | \
+	 (((uint8 *)(ipv6_body))[2] << 8) | \
+	 (((uint8 *)(ipv6_body))[3]))
+
+#define IPV6_PAYLOAD_LEN(ipv6_body) \
+	((((uint8 *)(ipv6_body))[IPV6_PAYLOAD_LEN_OFFSET + 0] << 8) | \
+	 ((uint8 *)(ipv6_body))[IPV6_PAYLOAD_LEN_OFFSET + 1])
+
+#define IPV6_NEXT_HDR(ipv6_body) \
+	(((uint8 *)(ipv6_body))[IPV6_NEXT_HDR_OFFSET])
+
+#define IPV6_PROT(ipv6_body)	IPV6_NEXT_HDR(ipv6_body)
+
+#define IPV6_ADDR_LEN		16	
+
+
+#ifndef IP_TOS
+#define IP_TOS(ip_body) \
+	(IP_VER(ip_body) == IP_VER_4 ? IPV4_TOS(ip_body) : \
+	 IP_VER(ip_body) == IP_VER_6 ? IPV6_TRAFFIC_CLASS(ip_body) : 0)
+#endif
+
+
+
+#include <packed_section_end.h>
+
+#endif	
diff --git a/drivers/net/wireless/bcm4329/include/proto/eapol.h b/drivers/net/wireless/bcm4329/include/proto/eapol.h
new file mode 100644
index 0000000..95e76ff
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/proto/eapol.h
@@ -0,0 +1,172 @@
+/*
+ * 802.1x EAPOL definitions
+ *
+ * See
+ * IEEE Std 802.1X-2001
+ * IEEE 802.1X RADIUS Usage Guidelines
+ *
+ * Copyright (C) 2002 Broadcom Corporation
+ *
+ * $Id: eapol.h,v 9.18.260.1.2.1.6.6 2009/04/08 05:00:08 Exp $
+ */
+
+#ifndef _eapol_h_
+#define _eapol_h_
+
+#ifndef _TYPEDEFS_H_
+#include <typedefs.h>
+#endif
+
+/* This marks the start of a packed structure section. */
+#include <packed_section_start.h>
+
+#define AKW_BLOCK_LEN	8	/* The only def we need here */
+
+/* EAPOL for 802.3/Ethernet */
+typedef struct {
+	struct ether_header eth;	/* 802.3/Ethernet header */
+	unsigned char version;		/* EAPOL protocol version */
+	unsigned char type;		/* EAPOL type */
+	unsigned short length;		/* Length of body */
+	unsigned char body[1];		/* Body (optional) */
+} eapol_header_t;
+
+#define EAPOL_HEADER_LEN 18
+
+/* EAPOL version */
+#define WPA2_EAPOL_VERSION	2
+#define WPA_EAPOL_VERSION	1
+#define LEAP_EAPOL_VERSION	1
+#define SES_EAPOL_VERSION	1
+
+/* EAPOL types */
+#define EAP_PACKET		0
+#define EAPOL_START		1
+#define EAPOL_LOGOFF		2
+#define EAPOL_KEY		3
+#define EAPOL_ASF		4
+
+/* EAPOL-Key types */
+#define EAPOL_RC4_KEY		1
+#define EAPOL_WPA2_KEY		2	/* 802.11i/WPA2 */
+#define EAPOL_WPA_KEY		254	/* WPA */
+
+/* RC4 EAPOL-Key header field sizes */
+#define EAPOL_KEY_REPLAY_LEN	8
+#define EAPOL_KEY_IV_LEN	16
+#define EAPOL_KEY_SIG_LEN	16
+
+/* RC4 EAPOL-Key */
+typedef BWL_PRE_PACKED_STRUCT struct {
+	unsigned char type;			/* Key Descriptor Type */
+	unsigned short length;			/* Key Length (unaligned) */
+	unsigned char replay[EAPOL_KEY_REPLAY_LEN];	/* Replay Counter */
+	unsigned char iv[EAPOL_KEY_IV_LEN];		/* Key IV */
+	unsigned char index;				/* Key Flags & Index */
+	unsigned char signature[EAPOL_KEY_SIG_LEN];	/* Key Signature */
+	unsigned char key[1];				/* Key (optional) */
+} BWL_POST_PACKED_STRUCT eapol_key_header_t;
+
+#define EAPOL_KEY_HEADER_LEN 	44
+
+/* RC4 EAPOL-Key flags */
+#define EAPOL_KEY_FLAGS_MASK	0x80
+#define EAPOL_KEY_BROADCAST	0
+#define EAPOL_KEY_UNICAST	0x80
+
+/* RC4 EAPOL-Key index */
+#define EAPOL_KEY_INDEX_MASK	0x7f
+
+/* WPA/802.11i/WPA2 EAPOL-Key header field sizes */
+#define EAPOL_WPA_KEY_REPLAY_LEN	8
+#define EAPOL_WPA_KEY_NONCE_LEN		32
+#define EAPOL_WPA_KEY_IV_LEN		16
+#define EAPOL_WPA_KEY_ID_LEN		8
+#define EAPOL_WPA_KEY_RSC_LEN		8
+#define EAPOL_WPA_KEY_MIC_LEN		16
+#define EAPOL_WPA_KEY_DATA_LEN		(EAPOL_WPA_MAX_KEY_SIZE + AKW_BLOCK_LEN)
+#define EAPOL_WPA_MAX_KEY_SIZE		32
+
+/* WPA EAPOL-Key */
+typedef BWL_PRE_PACKED_STRUCT struct {
+	unsigned char type;		/* Key Descriptor Type */
+	unsigned short key_info;	/* Key Information (unaligned) */
+	unsigned short key_len;		/* Key Length (unaligned) */
+	unsigned char replay[EAPOL_WPA_KEY_REPLAY_LEN];	/* Replay Counter */
+	unsigned char nonce[EAPOL_WPA_KEY_NONCE_LEN];	/* Nonce */
+	unsigned char iv[EAPOL_WPA_KEY_IV_LEN];		/* Key IV */
+	unsigned char rsc[EAPOL_WPA_KEY_RSC_LEN];	/* Key RSC */
+	unsigned char id[EAPOL_WPA_KEY_ID_LEN];		/* WPA:Key ID, 802.11i/WPA2: Reserved */
+	unsigned char mic[EAPOL_WPA_KEY_MIC_LEN];	/* Key MIC */
+	unsigned short data_len;			/* Key Data Length */
+	unsigned char data[EAPOL_WPA_KEY_DATA_LEN];	/* Key data */
+} BWL_POST_PACKED_STRUCT eapol_wpa_key_header_t;
+
+#define EAPOL_WPA_KEY_LEN 		95
+
+/* WPA/802.11i/WPA2 KEY KEY_INFO bits */
+#define WPA_KEY_DESC_V1		0x01
+#define WPA_KEY_DESC_V2		0x02
+#define WPA_KEY_PAIRWISE	0x08
+#define WPA_KEY_INSTALL		0x40
+#define WPA_KEY_ACK		0x80
+#define WPA_KEY_MIC		0x100
+#define WPA_KEY_SECURE		0x200
+#define WPA_KEY_ERROR		0x400
+#define WPA_KEY_REQ		0x800
+
+/* WPA-only KEY KEY_INFO bits */
+#define WPA_KEY_INDEX_0		0x00
+#define WPA_KEY_INDEX_1		0x10
+#define WPA_KEY_INDEX_2		0x20
+#define WPA_KEY_INDEX_3		0x30
+#define WPA_KEY_INDEX_MASK	0x30
+#define WPA_KEY_INDEX_SHIFT	0x04
+
+/* 802.11i/WPA2-only KEY KEY_INFO bits */
+#define WPA_KEY_ENCRYPTED_DATA	0x1000
+
+/* Key Data encapsulation */
+typedef BWL_PRE_PACKED_STRUCT struct {
+	uint8 type;
+	uint8 length;
+	uint8 oui[3];
+	uint8 subtype;
+	uint8 data[1];
+} BWL_POST_PACKED_STRUCT eapol_wpa2_encap_data_t;
+
+#define EAPOL_WPA2_ENCAP_DATA_HDR_LEN 	6
+
+#define WPA2_KEY_DATA_SUBTYPE_GTK	1
+#define WPA2_KEY_DATA_SUBTYPE_STAKEY	2
+#define WPA2_KEY_DATA_SUBTYPE_MAC	3
+#define WPA2_KEY_DATA_SUBTYPE_PMKID	4
+
+/* GTK encapsulation */
+typedef BWL_PRE_PACKED_STRUCT struct {
+	uint8	flags;
+	uint8	reserved;
+	uint8	gtk[EAPOL_WPA_MAX_KEY_SIZE];
+} BWL_POST_PACKED_STRUCT eapol_wpa2_key_gtk_encap_t;
+
+#define EAPOL_WPA2_KEY_GTK_ENCAP_HDR_LEN 	2
+
+#define WPA2_GTK_INDEX_MASK	0x03
+#define WPA2_GTK_INDEX_SHIFT	0x00
+
+#define WPA2_GTK_TRANSMIT	0x04
+
+/* STAKey encapsulation */
+typedef BWL_PRE_PACKED_STRUCT struct {
+	uint8	reserved[2];
+	uint8	mac[ETHER_ADDR_LEN];
+	uint8	stakey[EAPOL_WPA_MAX_KEY_SIZE];
+} BWL_POST_PACKED_STRUCT eapol_wpa2_key_stakey_encap_t;
+
+#define WPA2_KEY_DATA_PAD	0xdd
+
+
+/* This marks the end of a packed structure section. */
+#include <packed_section_end.h>
+
+#endif /* _eapol_h_ */
diff --git a/drivers/net/wireless/bcm4329/include/proto/ethernet.h b/drivers/net/wireless/bcm4329/include/proto/ethernet.h
new file mode 100644
index 0000000..9ad2ea0
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/proto/ethernet.h
@@ -0,0 +1,148 @@
+/*
+ * From FreeBSD 2.2.7: Fundamental constants relating to ethernet.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: ethernet.h,v 9.45.56.5 2010/02/22 22:04:36 Exp $
+ */
+
+
+#ifndef _NET_ETHERNET_H_	      
+#define _NET_ETHERNET_H_
+
+#ifndef _TYPEDEFS_H_
+#include "typedefs.h"
+#endif
+
+
+#include <packed_section_start.h>
+
+
+
+#define	ETHER_ADDR_LEN		6
+
+
+#define	ETHER_TYPE_LEN		2
+
+
+#define	ETHER_CRC_LEN		4
+
+
+#define	ETHER_HDR_LEN		(ETHER_ADDR_LEN * 2 + ETHER_TYPE_LEN)
+
+
+#define	ETHER_MIN_LEN		64
+
+
+#define	ETHER_MIN_DATA		46
+
+
+#define	ETHER_MAX_LEN		1518
+
+
+#define	ETHER_MAX_DATA		1500
+
+
+#define ETHER_TYPE_MIN		0x0600		
+#define	ETHER_TYPE_IP		0x0800		
+#define ETHER_TYPE_ARP		0x0806		
+#define ETHER_TYPE_8021Q	0x8100		
+#define	ETHER_TYPE_BRCM		0x886c		
+#define	ETHER_TYPE_802_1X	0x888e		
+#define ETHER_TYPE_WAI		0x88b4		
+#ifdef BCMWPA2
+#define	ETHER_TYPE_802_1X_PREAUTH 0x88c7	
+#endif
+
+
+#define	ETHER_BRCM_SUBTYPE_LEN	4	
+#define	ETHER_BRCM_CRAM		1	
+
+
+#define ETHER_DEST_OFFSET	(0 * ETHER_ADDR_LEN)	
+#define ETHER_SRC_OFFSET	(1 * ETHER_ADDR_LEN)	
+#define ETHER_TYPE_OFFSET	(2 * ETHER_ADDR_LEN)	
+
+
+#define	ETHER_IS_VALID_LEN(foo)	\
+	((foo) >= ETHER_MIN_LEN && (foo) <= ETHER_MAX_LEN)
+
+
+#ifndef __INCif_etherh       
+
+BWL_PRE_PACKED_STRUCT struct ether_header {
+	uint8	ether_dhost[ETHER_ADDR_LEN];
+	uint8	ether_shost[ETHER_ADDR_LEN];
+	uint16	ether_type;
+} BWL_POST_PACKED_STRUCT;
+
+
+BWL_PRE_PACKED_STRUCT struct	ether_addr {
+	uint8 octet[ETHER_ADDR_LEN];
+} BWL_POST_PACKED_STRUCT;
+#endif	
+
+
+#define ETHER_SET_LOCALADDR(ea)	(((uint8 *)(ea))[0] = (((uint8 *)(ea))[0] | 2))
+#define ETHER_IS_LOCALADDR(ea) 	(((uint8 *)(ea))[0] & 2)
+#define ETHER_CLR_LOCALADDR(ea)	(((uint8 *)(ea))[0] = (((uint8 *)(ea))[0] & 0xd))
+#define ETHER_TOGGLE_LOCALADDR(ea)	(((uint8 *)(ea))[0] = (((uint8 *)(ea))[0] ^ 2))
+
+
+#define ETHER_SET_UNICAST(ea)	(((uint8 *)(ea))[0] = (((uint8 *)(ea))[0] & ~1))
+
+
+#define ETHER_ISMULTI(ea) (((const uint8 *)(ea))[0] & 1)
+
+
+
+#define	ether_cmp(a, b)	(!(((short*)a)[0] == ((short*)b)[0]) | \
+			 !(((short*)a)[1] == ((short*)b)[1]) | \
+			 !(((short*)a)[2] == ((short*)b)[2]))
+
+
+#define	ether_copy(s, d) { \
+		((short*)d)[0] = ((short*)s)[0]; \
+		((short*)d)[1] = ((short*)s)[1]; \
+		((short*)d)[2] = ((short*)s)[2]; }
+
+
+static const struct ether_addr ether_bcast = {{255, 255, 255, 255, 255, 255}};
+static const struct ether_addr ether_null = {{0, 0, 0, 0, 0, 0}};
+
+#define ETHER_ISBCAST(ea)	((((uint8 *)(ea))[0] &		\
+	                          ((uint8 *)(ea))[1] &		\
+				  ((uint8 *)(ea))[2] &		\
+				  ((uint8 *)(ea))[3] &		\
+				  ((uint8 *)(ea))[4] &		\
+				  ((uint8 *)(ea))[5]) == 0xff)
+#define ETHER_ISNULLADDR(ea)	((((uint8 *)(ea))[0] |		\
+				  ((uint8 *)(ea))[1] |		\
+				  ((uint8 *)(ea))[2] |		\
+				  ((uint8 *)(ea))[3] |		\
+				  ((uint8 *)(ea))[4] |		\
+				  ((uint8 *)(ea))[5]) == 0)
+
+
+
+#include <packed_section_end.h>
+
+#endif 
diff --git a/drivers/net/wireless/bcm4329/include/proto/sdspi.h b/drivers/net/wireless/bcm4329/include/proto/sdspi.h
new file mode 100644
index 0000000..7739e68
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/proto/sdspi.h
@@ -0,0 +1,71 @@
+/*
+ * SD-SPI Protocol Standard
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: sdspi.h,v 9.1.20.1 2008/05/06 22:59:19 Exp $
+ */
+
+#define SPI_START_M		BITFIELD_MASK(1)	/* Bit [31] 	- Start Bit */
+#define SPI_START_S		31
+#define SPI_DIR_M		BITFIELD_MASK(1)	/* Bit [30] 	- Direction */
+#define SPI_DIR_S		30
+#define SPI_CMD_INDEX_M		BITFIELD_MASK(6)	/* Bits [29:24] - Command number */
+#define SPI_CMD_INDEX_S		24
+#define SPI_RW_M		BITFIELD_MASK(1)	/* Bit [23] 	- Read=0, Write=1 */
+#define SPI_RW_S		23
+#define SPI_FUNC_M		BITFIELD_MASK(3)	/* Bits [22:20]	- Function Number */
+#define SPI_FUNC_S		20
+#define SPI_RAW_M		BITFIELD_MASK(1)	/* Bit [19] 	- Read After Wr */
+#define SPI_RAW_S		19
+#define SPI_STUFF_M		BITFIELD_MASK(1)	/* Bit [18] 	- Stuff bit */
+#define SPI_STUFF_S		18
+#define SPI_BLKMODE_M		BITFIELD_MASK(1)	/* Bit [19] 	- Blockmode 1=blk */
+#define SPI_BLKMODE_S		19
+#define SPI_OPCODE_M		BITFIELD_MASK(1)	/* Bit [18] 	- OP Code */
+#define SPI_OPCODE_S		18
+#define SPI_ADDR_M		BITFIELD_MASK(17)	/* Bits [17:1] 	- Address */
+#define SPI_ADDR_S		1
+#define SPI_STUFF0_M		BITFIELD_MASK(1)	/* Bit [0] 	- Stuff bit */
+#define SPI_STUFF0_S		0
+
+#define SPI_RSP_START_M		BITFIELD_MASK(1)	/* Bit [7] 	- Start Bit (always 0) */
+#define SPI_RSP_START_S		7
+#define SPI_RSP_PARAM_ERR_M	BITFIELD_MASK(1)	/* Bit [6] 	- Parameter Error */
+#define SPI_RSP_PARAM_ERR_S	6
+#define SPI_RSP_RFU5_M		BITFIELD_MASK(1)	/* Bit [5] 	- RFU (Always 0) */
+#define SPI_RSP_RFU5_S		5
+#define SPI_RSP_FUNC_ERR_M	BITFIELD_MASK(1)	/* Bit [4] 	- Function number error */
+#define SPI_RSP_FUNC_ERR_S	4
+#define SPI_RSP_CRC_ERR_M	BITFIELD_MASK(1)	/* Bit [3] 	- COM CRC Error */
+#define SPI_RSP_CRC_ERR_S	3
+#define SPI_RSP_ILL_CMD_M	BITFIELD_MASK(1)	/* Bit [2] 	- Illegal Command error */
+#define SPI_RSP_ILL_CMD_S	2
+#define SPI_RSP_RFU1_M		BITFIELD_MASK(1)	/* Bit [1] 	- RFU (Always 0) */
+#define SPI_RSP_RFU1_S		1
+#define SPI_RSP_IDLE_M		BITFIELD_MASK(1)	/* Bit [0] 	- In idle state */
+#define SPI_RSP_IDLE_S		0
+
+/* SD-SPI Protocol Definitions */
+#define SDSPI_COMMAND_LEN	6	/* Number of bytes in an SD command */
+#define SDSPI_START_BLOCK	0xFE	/* SD Start Block Token */
+#define SDSPI_IDLE_PAD		0xFF	/* SD-SPI idle value for MOSI */
+#define SDSPI_START_BIT_MASK	0x80
diff --git a/drivers/net/wireless/bcm4329/include/proto/vlan.h b/drivers/net/wireless/bcm4329/include/proto/vlan.h
new file mode 100644
index 0000000..670bc44
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/proto/vlan.h
@@ -0,0 +1,63 @@
+/*
+ * 802.1Q VLAN protocol definitions
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: vlan.h,v 9.4.196.2 2008/12/07 21:19:20 Exp $
+ */
+
+
+#ifndef _vlan_h_
+#define _vlan_h_
+
+#ifndef _TYPEDEFS_H_
+#include <typedefs.h>
+#endif
+
+
+#include <packed_section_start.h>
+
+#define VLAN_VID_MASK		0xfff	
+#define	VLAN_CFI_SHIFT		12	
+#define VLAN_PRI_SHIFT		13	
+
+#define VLAN_PRI_MASK		7	
+
+#define	VLAN_TAG_LEN		4
+#define	VLAN_TAG_OFFSET		(2 * ETHER_ADDR_LEN)	
+
+#define VLAN_TPID		0x8100	
+
+struct ethervlan_header {
+	uint8	ether_dhost[ETHER_ADDR_LEN];
+	uint8	ether_shost[ETHER_ADDR_LEN];
+	uint16	vlan_type;		
+	uint16	vlan_tag;		
+	uint16	ether_type;
+};
+
+#define	ETHERVLAN_HDR_LEN	(ETHER_HDR_LEN + VLAN_TAG_LEN)
+
+
+
+#include <packed_section_end.h>
+
+#endif 
diff --git a/drivers/net/wireless/bcm4329/include/proto/wpa.h b/drivers/net/wireless/bcm4329/include/proto/wpa.h
new file mode 100644
index 0000000..f5d0cd5
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/proto/wpa.h
@@ -0,0 +1,159 @@
+/*
+ * Fundamental types and constants relating to WPA
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: wpa.h,v 1.16.166.1.20.1 2008/11/20 00:51:31 Exp $
+ */
+
+
+#ifndef _proto_wpa_h_
+#define _proto_wpa_h_
+
+#include <typedefs.h>
+#include <proto/ethernet.h>
+
+
+
+#include <packed_section_start.h>
+
+
+
+
+#define DOT11_RC_INVALID_WPA_IE		13	
+#define DOT11_RC_MIC_FAILURE		14	
+#define DOT11_RC_4WH_TIMEOUT		15	
+#define DOT11_RC_GTK_UPDATE_TIMEOUT	16	
+#define DOT11_RC_WPA_IE_MISMATCH	17	
+#define DOT11_RC_INVALID_MC_CIPHER	18	
+#define DOT11_RC_INVALID_UC_CIPHER	19	
+#define DOT11_RC_INVALID_AKMP		20	
+#define DOT11_RC_BAD_WPA_VERSION	21	
+#define DOT11_RC_INVALID_WPA_CAP	22	
+#define DOT11_RC_8021X_AUTH_FAIL	23	
+
+#define WPA2_PMKID_LEN	16
+
+
+typedef BWL_PRE_PACKED_STRUCT struct
+{
+	uint8 tag;	
+	uint8 length;	
+	uint8 oui[3];	
+	uint8 oui_type;	
+	BWL_PRE_PACKED_STRUCT struct {
+		uint8 low;
+		uint8 high;
+	} BWL_POST_PACKED_STRUCT version;	
+} BWL_POST_PACKED_STRUCT wpa_ie_fixed_t;
+#define WPA_IE_OUITYPE_LEN	4
+#define WPA_IE_FIXED_LEN	8
+#define WPA_IE_TAG_FIXED_LEN	6
+
+typedef BWL_PRE_PACKED_STRUCT struct {
+	uint8 tag;	
+	uint8 length;	
+	BWL_PRE_PACKED_STRUCT struct {
+		uint8 low;
+		uint8 high;
+	} BWL_POST_PACKED_STRUCT version;	
+} BWL_POST_PACKED_STRUCT wpa_rsn_ie_fixed_t;
+#define WPA_RSN_IE_FIXED_LEN	4
+#define WPA_RSN_IE_TAG_FIXED_LEN	2
+typedef uint8 wpa_pmkid_t[WPA2_PMKID_LEN];
+
+
+typedef BWL_PRE_PACKED_STRUCT struct
+{
+	uint8 oui[3];
+	uint8 type;
+} BWL_POST_PACKED_STRUCT wpa_suite_t, wpa_suite_mcast_t;
+#define WPA_SUITE_LEN	4
+
+
+typedef BWL_PRE_PACKED_STRUCT struct
+{
+	BWL_PRE_PACKED_STRUCT struct {
+		uint8 low;
+		uint8 high;
+	} BWL_POST_PACKED_STRUCT count;
+	wpa_suite_t list[1];
+} BWL_POST_PACKED_STRUCT wpa_suite_ucast_t, wpa_suite_auth_key_mgmt_t;
+#define WPA_IE_SUITE_COUNT_LEN	2
+typedef BWL_PRE_PACKED_STRUCT struct
+{
+	BWL_PRE_PACKED_STRUCT struct {
+		uint8 low;
+		uint8 high;
+	} BWL_POST_PACKED_STRUCT count;
+	wpa_pmkid_t list[1];
+} BWL_POST_PACKED_STRUCT wpa_pmkid_list_t;
+
+
+#define WPA_CIPHER_NONE		0	
+#define WPA_CIPHER_WEP_40	1	
+#define WPA_CIPHER_TKIP		2	
+#define WPA_CIPHER_AES_OCB	3	
+#define WPA_CIPHER_AES_CCM	4	
+#define WPA_CIPHER_WEP_104	5	
+
+#define IS_WPA_CIPHER(cipher)	((cipher) == WPA_CIPHER_NONE || \
+				 (cipher) == WPA_CIPHER_WEP_40 || \
+				 (cipher) == WPA_CIPHER_WEP_104 || \
+				 (cipher) == WPA_CIPHER_TKIP || \
+				 (cipher) == WPA_CIPHER_AES_OCB || \
+				 (cipher) == WPA_CIPHER_AES_CCM)
+
+
+#define WPA_TKIP_CM_DETECT	60	
+#define WPA_TKIP_CM_BLOCK	60	
+
+
+#define RSN_CAP_LEN		2	
+
+
+#define RSN_CAP_PREAUTH			0x0001
+#define RSN_CAP_NOPAIRWISE		0x0002
+#define RSN_CAP_PTK_REPLAY_CNTR_MASK	0x000C
+#define RSN_CAP_PTK_REPLAY_CNTR_SHIFT	2
+#define RSN_CAP_GTK_REPLAY_CNTR_MASK	0x0030
+#define RSN_CAP_GTK_REPLAY_CNTR_SHIFT	4
+#define RSN_CAP_1_REPLAY_CNTR		0
+#define RSN_CAP_2_REPLAY_CNTRS		1
+#define RSN_CAP_4_REPLAY_CNTRS		2
+#define RSN_CAP_16_REPLAY_CNTRS		3
+
+
+#define WPA_CAP_4_REPLAY_CNTRS		RSN_CAP_4_REPLAY_CNTRS
+#define WPA_CAP_16_REPLAY_CNTRS		RSN_CAP_16_REPLAY_CNTRS
+#define WPA_CAP_REPLAY_CNTR_SHIFT	RSN_CAP_PTK_REPLAY_CNTR_SHIFT
+#define WPA_CAP_REPLAY_CNTR_MASK	RSN_CAP_PTK_REPLAY_CNTR_MASK
+
+
+#define WPA_CAP_LEN	RSN_CAP_LEN	
+
+#define	WPA_CAP_WPA2_PREAUTH		RSN_CAP_PREAUTH
+
+
+
+#include <packed_section_end.h>
+
+#endif 
diff --git a/drivers/net/wireless/bcm4329/include/sbchipc.h b/drivers/net/wireless/bcm4329/include/sbchipc.h
new file mode 100644
index 0000000..39e5c8d
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/sbchipc.h
@@ -0,0 +1,1026 @@
+/*
+ * SiliconBackplane Chipcommon core hardware definitions.
+ *
+ * The chipcommon core provides chip identification, SB control,
+ * jtag, 0/1/2 uarts, clock frequency control, a watchdog interrupt timer,
+ * gpio interface, extbus, and support for serial and parallel flashes.
+ *
+ * $Id: sbchipc.h,v 13.103.2.5.4.5.2.9 2009/07/03 14:23:21 Exp $
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ */
+
+
+#ifndef	_SBCHIPC_H
+#define	_SBCHIPC_H
+
+#ifndef _LANGUAGE_ASSEMBLY
+
+
+#ifndef PAD
+#define	_PADLINE(line)	pad ## line
+#define	_XSTR(line)	_PADLINE(line)
+#define	PAD		_XSTR(__LINE__)
+#endif	
+
+typedef volatile struct {
+	uint32	chipid;			
+	uint32	capabilities;
+	uint32	corecontrol;		
+	uint32	bist;
+
+	
+	uint32	otpstatus;		
+	uint32	otpcontrol;
+	uint32	otpprog;
+	uint32	PAD;
+
+	
+	uint32	intstatus;		
+	uint32	intmask;
+	uint32	chipcontrol;		
+	uint32	chipstatus;		
+
+	
+	uint32	jtagcmd;		
+	uint32	jtagir;
+	uint32	jtagdr;
+	uint32	jtagctrl;
+
+	
+	uint32	flashcontrol;		
+	uint32	flashaddress;
+	uint32	flashdata;
+	uint32	PAD[1];
+
+	
+	uint32	broadcastaddress;	
+	uint32	broadcastdata;
+
+	
+	uint32	gpiopullup;		
+	uint32	gpiopulldown;		
+	uint32	gpioin;			
+	uint32	gpioout;
+	uint32	gpioouten;
+	uint32	gpiocontrol;
+	uint32	gpiointpolarity;
+	uint32	gpiointmask;
+
+	
+	uint32	gpioevent;
+	uint32	gpioeventintmask;
+
+	
+	uint32	watchdog;		
+
+	
+	uint32	gpioeventintpolarity;
+
+	
+	uint32  gpiotimerval;		
+	uint32  gpiotimeroutmask;
+
+	
+	uint32	clockcontrol_n;		
+	uint32	clockcontrol_sb;	
+	uint32	clockcontrol_pci;	
+	uint32	clockcontrol_m2;	
+	uint32	clockcontrol_m3;	
+	uint32	clkdiv;			
+	uint32	PAD[2];
+
+	
+	uint32	pll_on_delay;		
+	uint32	fref_sel_delay;
+	uint32	slow_clk_ctl;		
+	uint32	PAD[1];
+
+	
+	uint32	system_clk_ctl;		
+	uint32	clkstatestretch;
+	uint32	PAD[13];
+
+	
+	uint32	eromptr;
+
+	
+	uint32	pcmcia_config;		
+	uint32	pcmcia_memwait;
+	uint32	pcmcia_attrwait;
+	uint32	pcmcia_iowait;
+	uint32	ide_config;
+	uint32	ide_memwait;
+	uint32	ide_attrwait;
+	uint32	ide_iowait;
+	uint32	prog_config;
+	uint32	prog_waitcount;
+	uint32	flash_config;
+	uint32	flash_waitcount;
+	uint32	PAD[4];
+	uint32	PAD[40];
+
+
+	
+	uint32	clk_ctl_st;		
+	uint32	hw_war;
+	uint32	PAD[70];
+
+	
+	uint8	uart0data;		
+	uint8	uart0imr;
+	uint8	uart0fcr;
+	uint8	uart0lcr;
+	uint8	uart0mcr;
+	uint8	uart0lsr;
+	uint8	uart0msr;
+	uint8	uart0scratch;
+	uint8	PAD[248];		
+
+	uint8	uart1data;		
+	uint8	uart1imr;
+	uint8	uart1fcr;
+	uint8	uart1lcr;
+	uint8	uart1mcr;
+	uint8	uart1lsr;
+	uint8	uart1msr;
+	uint8	uart1scratch;
+	uint32	PAD[126];
+
+	
+	uint32	pmucontrol;		
+	uint32	pmucapabilities;
+	uint32	pmustatus;
+	uint32	res_state;
+	uint32	res_pending;
+	uint32	pmutimer;
+	uint32	min_res_mask;
+	uint32	max_res_mask;
+	uint32	res_table_sel;
+	uint32	res_dep_mask;
+	uint32	res_updn_timer;
+	uint32	res_timer;
+	uint32	clkstretch;
+	uint32	pmuwatchdog;
+	uint32	gpiosel;		
+	uint32	gpioenable;		
+	uint32	res_req_timer_sel;
+	uint32	res_req_timer;
+	uint32	res_req_mask;
+	uint32	PAD;
+	uint32	chipcontrol_addr;	
+	uint32	chipcontrol_data;	
+	uint32	regcontrol_addr;
+	uint32	regcontrol_data;
+	uint32	pllcontrol_addr;
+	uint32	pllcontrol_data;
+	uint32	PAD[102];
+	uint16	otp[768];
+} chipcregs_t;
+
+#endif 
+
+#define	CC_CHIPID		0
+#define	CC_CAPABILITIES		4
+#define CC_OTPST		0x10
+#define CC_CHIPST		0x2c
+#define	CC_JTAGCMD		0x30
+#define	CC_JTAGIR		0x34
+#define	CC_JTAGDR		0x38
+#define	CC_JTAGCTRL		0x3c
+#define	CC_WATCHDOG		0x80
+#define	CC_CLKC_N		0x90
+#define	CC_CLKC_M0		0x94
+#define	CC_CLKC_M1		0x98
+#define	CC_CLKC_M2		0x9c
+#define	CC_CLKC_M3		0xa0
+#define	CC_CLKDIV		0xa4
+#define	CC_SYS_CLK_CTL		0xc0
+#define	CC_CLK_CTL_ST		SI_CLK_CTL_ST
+#define	CC_EROMPTR		0xfc
+#define	PMU_CTL			0x600
+#define	PMU_CAP			0x604
+#define	PMU_ST			0x608
+#define PMU_RES_STATE		0x60c
+#define PMU_TIMER		0x614
+#define	PMU_MIN_RES_MASK	0x618
+#define	PMU_MAX_RES_MASK	0x61c
+#define PMU_REG_CONTROL_ADDR	0x658
+#define PMU_REG_CONTROL_DATA	0x65C
+#define PMU_PLL_CONTROL_ADDR 	0x660
+#define PMU_PLL_CONTROL_DATA 	0x664
+#define	CC_OTP			0x800		
+
+
+#define	CID_ID_MASK		0x0000ffff	
+#define	CID_REV_MASK		0x000f0000	
+#define	CID_REV_SHIFT		16		
+#define	CID_PKG_MASK		0x00f00000	
+#define	CID_PKG_SHIFT		20		
+#define	CID_CC_MASK		0x0f000000	
+#define CID_CC_SHIFT		24
+#define	CID_TYPE_MASK		0xf0000000	
+#define CID_TYPE_SHIFT		28
+
+
+#define	CC_CAP_UARTS_MASK	0x00000003	
+#define CC_CAP_MIPSEB		0x00000004	
+#define CC_CAP_UCLKSEL		0x00000018	
+#define CC_CAP_UINTCLK		0x00000008	
+#define CC_CAP_UARTGPIO		0x00000020	
+#define CC_CAP_EXTBUS_MASK	0x000000c0	
+#define CC_CAP_EXTBUS_NONE	0x00000000	
+#define CC_CAP_EXTBUS_FULL	0x00000040	
+#define CC_CAP_EXTBUS_PROG	0x00000080	
+#define	CC_CAP_FLASH_MASK	0x00000700	
+#define	CC_CAP_PLL_MASK		0x00038000	
+#define CC_CAP_PWR_CTL		0x00040000	
+#define CC_CAP_OTPSIZE		0x00380000	
+#define CC_CAP_OTPSIZE_SHIFT	19		
+#define CC_CAP_OTPSIZE_BASE	5		
+#define CC_CAP_JTAGP		0x00400000	
+#define CC_CAP_ROM		0x00800000	
+#define CC_CAP_BKPLN64		0x08000000	
+#define	CC_CAP_PMU		0x10000000	
+#define	CC_CAP_ECI		0x20000000	
+
+
+#define PLL_NONE		0x00000000
+#define PLL_TYPE1		0x00010000	
+#define PLL_TYPE2		0x00020000	
+#define PLL_TYPE3		0x00030000	
+#define PLL_TYPE4		0x00008000	
+#define PLL_TYPE5		0x00018000	
+#define PLL_TYPE6		0x00028000	
+#define PLL_TYPE7		0x00038000	
+
+
+#define	ILP_CLOCK		32000
+
+
+#define	ALP_CLOCK		20000000
+
+
+#define	HT_CLOCK		80000000
+
+
+#define CC_UARTCLKO		0x00000001	
+#define	CC_SE			0x00000002	
+#define CC_UARTCLKEN		0x00000008	
+
+
+#define CHIPCTRL_4321A0_DEFAULT	0x3a4
+#define CHIPCTRL_4321A1_DEFAULT	0x0a4
+#define CHIPCTRL_4321_PLL_DOWN	0x800000	
+
+
+#define OTPS_OL_MASK		0x000000ff
+#define OTPS_OL_MFG		0x00000001	
+#define OTPS_OL_OR1		0x00000002	
+#define OTPS_OL_OR2		0x00000004	
+#define OTPS_OL_GU		0x00000008	
+#define OTPS_GUP_MASK		0x00000f00
+#define OTPS_GUP_SHIFT		8
+#define OTPS_GUP_HW		0x00000100	
+#define OTPS_GUP_SW		0x00000200	
+#define OTPS_GUP_CI		0x00000400	
+#define OTPS_GUP_FUSE		0x00000800	
+#define OTPS_READY		0x00001000
+#define OTPS_RV(x)		(1 << (16 + (x)))	
+#define OTPS_RV_MASK		0x0fff0000
+
+
+#define OTPC_PROGSEL		0x00000001
+#define OTPC_PCOUNT_MASK	0x0000000e
+#define OTPC_PCOUNT_SHIFT	1
+#define OTPC_VSEL_MASK		0x000000f0
+#define OTPC_VSEL_SHIFT		4
+#define OTPC_TMM_MASK		0x00000700
+#define OTPC_TMM_SHIFT		8
+#define OTPC_ODM		0x00000800
+#define OTPC_PROGEN		0x80000000
+
+
+#define OTPP_COL_MASK		0x000000ff
+#define OTPP_COL_SHIFT		0
+#define OTPP_ROW_MASK		0x0000ff00
+#define OTPP_ROW_SHIFT		8
+#define OTPP_OC_MASK		0x0f000000
+#define OTPP_OC_SHIFT		24
+#define OTPP_READERR		0x10000000
+#define OTPP_VALUE_MASK		0x20000000
+#define OTPP_VALUE_SHIFT	29
+#define OTPP_START_BUSY		0x80000000
+
+
+#define OTPPOC_READ		0
+#define OTPPOC_BIT_PROG		1
+#define OTPPOC_VERIFY		3
+#define OTPPOC_INIT		4
+#define OTPPOC_SET		5
+#define OTPPOC_RESET		6
+#define OTPPOC_OCST		7
+#define OTPPOC_ROW_LOCK		8
+#define OTPPOC_PRESCN_TEST	9
+
+
+#define JCMD_START		0x80000000
+#define JCMD_BUSY		0x80000000
+#define JCMD_STATE_MASK		0x60000000
+#define JCMD_STATE_TLR		0x00000000	
+#define JCMD_STATE_PIR		0x20000000	
+#define JCMD_STATE_PDR		0x40000000	
+#define JCMD_STATE_RTI		0x60000000	
+#define JCMD0_ACC_MASK		0x0000f000
+#define JCMD0_ACC_IRDR		0x00000000
+#define JCMD0_ACC_DR		0x00001000
+#define JCMD0_ACC_IR		0x00002000
+#define JCMD0_ACC_RESET		0x00003000
+#define JCMD0_ACC_IRPDR		0x00004000
+#define JCMD0_ACC_PDR		0x00005000
+#define JCMD0_IRW_MASK		0x00000f00
+#define JCMD_ACC_MASK		0x000f0000	
+#define JCMD_ACC_IRDR		0x00000000
+#define JCMD_ACC_DR		0x00010000
+#define JCMD_ACC_IR		0x00020000
+#define JCMD_ACC_RESET		0x00030000
+#define JCMD_ACC_IRPDR		0x00040000
+#define JCMD_ACC_PDR		0x00050000
+#define JCMD_ACC_PIR		0x00060000
+#define JCMD_ACC_IRDR_I		0x00070000	
+#define JCMD_ACC_DR_I		0x00080000	
+#define JCMD_IRW_MASK		0x00001f00
+#define JCMD_IRW_SHIFT		8
+#define JCMD_DRW_MASK		0x0000003f
+
+
+#define JCTRL_FORCE_CLK		4		
+#define JCTRL_EXT_EN		2		
+#define JCTRL_EN		1		
+
+
+#define	CLKD_SFLASH		0x0f000000
+#define	CLKD_SFLASH_SHIFT	24
+#define	CLKD_OTP		0x000f0000
+#define	CLKD_OTP_SHIFT		16
+#define	CLKD_JTAG		0x00000f00
+#define	CLKD_JTAG_SHIFT		8
+#define	CLKD_UART		0x000000ff
+
+
+#define	CI_GPIO			0x00000001	
+#define	CI_EI			0x00000002	
+#define	CI_TEMP			0x00000004	
+#define	CI_SIRQ			0x00000008	
+#define	CI_ECI			0x00000010	
+#define	CI_PMU			0x00000020	
+#define	CI_UART			0x00000040	
+#define	CI_WDRESET		0x80000000	
+
+
+#define SCC_SS_MASK		0x00000007	
+#define	SCC_SS_LPO		0x00000000	
+#define	SCC_SS_XTAL		0x00000001	
+#define	SCC_SS_PCI		0x00000002	
+#define SCC_LF			0x00000200	
+#define SCC_LP			0x00000400	
+#define SCC_FS			0x00000800	
+#define SCC_IP			0x00001000	
+#define SCC_XC			0x00002000	
+#define SCC_XP			0x00004000	
+#define SCC_CD_MASK		0xffff0000	
+#define SCC_CD_SHIFT		16
+
+
+#define	SYCC_IE			0x00000001	
+#define	SYCC_AE			0x00000002	
+#define	SYCC_FP			0x00000004	
+#define	SYCC_AR			0x00000008	
+#define	SYCC_HR			0x00000010	
+#define SYCC_CD_MASK		0xffff0000	
+#define SYCC_CD_SHIFT		16
+
+
+#define	CF_EN			0x00000001	
+#define	CF_EM_MASK		0x0000000e	
+#define	CF_EM_SHIFT		1
+#define	CF_EM_FLASH		0		
+#define	CF_EM_SYNC		2		
+#define	CF_EM_PCMCIA		4		
+#define	CF_DS			0x00000010	
+#define	CF_BS			0x00000020	
+#define	CF_CD_MASK		0x000000c0	
+#define	CF_CD_SHIFT		6
+#define	CF_CD_DIV2		0x00000000	
+#define	CF_CD_DIV3		0x00000040	
+#define	CF_CD_DIV4		0x00000080	
+#define	CF_CE			0x00000100	
+#define	CF_SB			0x00000200	
+
+
+#define	PM_W0_MASK		0x0000003f	
+#define	PM_W1_MASK		0x00001f00	
+#define	PM_W1_SHIFT		8
+#define	PM_W2_MASK		0x001f0000	
+#define	PM_W2_SHIFT		16
+#define	PM_W3_MASK		0x1f000000	
+#define	PM_W3_SHIFT		24
+
+
+#define	PA_W0_MASK		0x0000003f	
+#define	PA_W1_MASK		0x00001f00	
+#define	PA_W1_SHIFT		8
+#define	PA_W2_MASK		0x001f0000	
+#define	PA_W2_SHIFT		16
+#define	PA_W3_MASK		0x1f000000	
+#define	PA_W3_SHIFT		24
+
+
+#define	PI_W0_MASK		0x0000003f	
+#define	PI_W1_MASK		0x00001f00	
+#define	PI_W1_SHIFT		8
+#define	PI_W2_MASK		0x001f0000	
+#define	PI_W2_SHIFT		16
+#define	PI_W3_MASK		0x1f000000	
+#define	PI_W3_SHIFT		24
+
+
+#define	PW_W0_MASK		0x0000001f	
+#define	PW_W1_MASK		0x00001f00	
+#define	PW_W1_SHIFT		8
+#define	PW_W2_MASK		0x001f0000	
+#define	PW_W2_SHIFT		16
+#define	PW_W3_MASK		0x1f000000	
+#define	PW_W3_SHIFT		24
+
+#define PW_W0       		0x0000000c
+#define PW_W1       		0x00000a00
+#define PW_W2       		0x00020000
+#define PW_W3       		0x01000000
+
+
+#define	FW_W0_MASK		0x0000003f	
+#define	FW_W1_MASK		0x00001f00	
+#define	FW_W1_SHIFT		8
+#define	FW_W2_MASK		0x001f0000	
+#define	FW_W2_SHIFT		16
+#define	FW_W3_MASK		0x1f000000	
+#define	FW_W3_SHIFT		24
+
+
+#define WATCHDOG_CLOCK		48000000	
+#define WATCHDOG_CLOCK_5354 	32000		
+
+
+#define	PCTL_ILP_DIV_MASK	0xffff0000
+#define	PCTL_ILP_DIV_SHIFT	16
+#define PCTL_PLL_PLLCTL_UPD	0x00000400	
+#define PCTL_NOILP_ON_WAIT	0x00000200	
+#define	PCTL_HT_REQ_EN		0x00000100
+#define	PCTL_ALP_REQ_EN		0x00000080
+#define	PCTL_XTALFREQ_MASK	0x0000007c
+#define	PCTL_XTALFREQ_SHIFT	2
+#define	PCTL_ILP_DIV_EN		0x00000002
+#define	PCTL_LPO_SEL		0x00000001
+
+
+#define CSTRETCH_HT		0xffff0000
+#define CSTRETCH_ALP		0x0000ffff
+
+
+#define GPIO_ONTIME_SHIFT	16
+
+
+#define	CN_N1_MASK		0x3f		
+#define	CN_N2_MASK		0x3f00		
+#define	CN_N2_SHIFT		8
+#define	CN_PLLC_MASK		0xf0000		
+#define	CN_PLLC_SHIFT		16
+
+
+#define	CC_M1_MASK		0x3f		
+#define	CC_M2_MASK		0x3f00		
+#define	CC_M2_SHIFT		8
+#define	CC_M3_MASK		0x3f0000	
+#define	CC_M3_SHIFT		16
+#define	CC_MC_MASK		0x1f000000	
+#define	CC_MC_SHIFT		24
+
+
+#define	CC_F6_2			0x02		
+#define	CC_F6_3			0x03		
+#define	CC_F6_4			0x05		
+#define	CC_F6_5			0x09
+#define	CC_F6_6			0x11
+#define	CC_F6_7			0x21
+
+#define	CC_F5_BIAS		5		
+
+#define	CC_MC_BYPASS		0x08
+#define	CC_MC_M1		0x04
+#define	CC_MC_M1M2		0x02
+#define	CC_MC_M1M2M3		0x01
+#define	CC_MC_M1M3		0x11
+
+
+#define	CC_T2_BIAS		2		
+#define	CC_T2M2_BIAS		3		
+
+#define	CC_T2MC_M1BYP		1
+#define	CC_T2MC_M2BYP		2
+#define	CC_T2MC_M3BYP		4
+
+
+#define	CC_T6_MMASK		1		
+#define	CC_T6_M0		120000000	
+#define	CC_T6_M1		100000000	
+#define	SB2MIPS_T6(sb)		(2 * (sb))
+
+
+#define	CC_CLOCK_BASE1		24000000	
+#define CC_CLOCK_BASE2		12500000	
+
+
+#define	CLKC_5350_N		0x0311
+#define	CLKC_5350_M		0x04020009
+
+
+#define FLASH_NONE		0x000		
+#define SFLASH_ST		0x100		
+#define SFLASH_AT		0x200		
+#define	PFLASH			0x700		
+
+
+#define	CC_CFG_EN		0x0001		
+#define	CC_CFG_EM_MASK		0x000e		
+#define	CC_CFG_EM_ASYNC		0x0000		
+#define	CC_CFG_EM_SYNC		0x0002		
+#define	CC_CFG_EM_PCMCIA	0x0004		
+#define	CC_CFG_EM_IDE		0x0006		
+#define	CC_CFG_DS		0x0010		
+#define	CC_CFG_CD_MASK		0x00e0		
+#define	CC_CFG_CE		0x0100		
+#define	CC_CFG_SB		0x0200		
+#define	CC_CFG_IS		0x0400		
+
+
+#define	CC_EB_BASE		0x1a000000	
+#define	CC_EB_PCMCIA_MEM	0x1a000000	
+#define	CC_EB_PCMCIA_IO		0x1a200000	
+#define	CC_EB_PCMCIA_CFG	0x1a400000	
+#define	CC_EB_IDE		0x1a800000	
+#define	CC_EB_PCMCIA1_MEM	0x1a800000	
+#define	CC_EB_PCMCIA1_IO	0x1aa00000	
+#define	CC_EB_PCMCIA1_CFG	0x1ac00000	
+#define	CC_EB_PROGIF		0x1b000000	
+
+
+
+#define SFLASH_OPCODE		0x000000ff
+#define SFLASH_ACTION		0x00000700
+#define	SFLASH_CS_ACTIVE	0x00001000	
+#define SFLASH_START		0x80000000
+#define SFLASH_BUSY		SFLASH_START
+
+
+#define	SFLASH_ACT_OPONLY	0x0000		
+#define	SFLASH_ACT_OP1D		0x0100		
+#define	SFLASH_ACT_OP3A		0x0200		
+#define	SFLASH_ACT_OP3A1D	0x0300		
+#define	SFLASH_ACT_OP3A4D	0x0400		
+#define	SFLASH_ACT_OP3A4X4D	0x0500		
+#define	SFLASH_ACT_OP3A1X4D	0x0700		
+
+
+#define SFLASH_ST_WREN		0x0006		
+#define SFLASH_ST_WRDIS		0x0004		
+#define SFLASH_ST_RDSR		0x0105		
+#define SFLASH_ST_WRSR		0x0101		
+#define SFLASH_ST_READ		0x0303		
+#define SFLASH_ST_PP		0x0302		
+#define SFLASH_ST_SE		0x02d8		
+#define SFLASH_ST_BE		0x00c7		
+#define SFLASH_ST_DP		0x00b9		
+#define SFLASH_ST_RES		0x03ab		
+#define SFLASH_ST_CSA		0x1000		
+
+
+#define SFLASH_ST_WIP		0x01		
+#define SFLASH_ST_WEL		0x02		
+#define SFLASH_ST_BP_MASK	0x1c		
+#define SFLASH_ST_BP_SHIFT	2
+#define SFLASH_ST_SRWD		0x80		
+
+
+#define SFLASH_AT_READ				0x07e8
+#define SFLASH_AT_PAGE_READ			0x07d2
+#define SFLASH_AT_BUF1_READ
+#define SFLASH_AT_BUF2_READ
+#define SFLASH_AT_STATUS			0x01d7
+#define SFLASH_AT_BUF1_WRITE			0x0384
+#define SFLASH_AT_BUF2_WRITE			0x0387
+#define SFLASH_AT_BUF1_ERASE_PROGRAM		0x0283
+#define SFLASH_AT_BUF2_ERASE_PROGRAM		0x0286
+#define SFLASH_AT_BUF1_PROGRAM			0x0288
+#define SFLASH_AT_BUF2_PROGRAM			0x0289
+#define SFLASH_AT_PAGE_ERASE			0x0281
+#define SFLASH_AT_BLOCK_ERASE			0x0250
+#define SFLASH_AT_BUF1_WRITE_ERASE_PROGRAM	0x0382
+#define SFLASH_AT_BUF2_WRITE_ERASE_PROGRAM	0x0385
+#define SFLASH_AT_BUF1_LOAD			0x0253
+#define SFLASH_AT_BUF2_LOAD			0x0255
+#define SFLASH_AT_BUF1_COMPARE			0x0260
+#define SFLASH_AT_BUF2_COMPARE			0x0261
+#define SFLASH_AT_BUF1_REPROGRAM		0x0258
+#define SFLASH_AT_BUF2_REPROGRAM		0x0259
+
+
+#define SFLASH_AT_READY				0x80
+#define SFLASH_AT_MISMATCH			0x40
+#define SFLASH_AT_ID_MASK			0x38
+#define SFLASH_AT_ID_SHIFT			3
+
+
+
+#define UART_RX		0	
+#define UART_TX		0	
+#define UART_DLL	0	
+#define UART_IER	1	
+#define UART_DLM	1	
+#define UART_IIR	2	
+#define UART_FCR	2	
+#define UART_LCR	3	
+#define UART_MCR	4	
+#define UART_LSR	5	
+#define UART_MSR	6	
+#define UART_SCR	7	
+#define UART_LCR_DLAB	0x80	
+#define UART_LCR_WLEN8	0x03	
+#define UART_MCR_OUT2	0x08	
+#define UART_MCR_LOOP	0x10	
+#define UART_LSR_RX_FIFO 	0x80	
+#define UART_LSR_TDHR		0x40	
+#define UART_LSR_THRE		0x20	
+#define UART_LSR_BREAK		0x10	
+#define UART_LSR_FRAMING	0x08	
+#define UART_LSR_PARITY		0x04	
+#define UART_LSR_OVERRUN	0x02	
+#define UART_LSR_RXRDY		0x01	
+#define UART_FCR_FIFO_ENABLE 1	
+
+
+#define UART_IIR_FIFO_MASK	0xc0	
+#define UART_IIR_INT_MASK	0xf	
+#define UART_IIR_MDM_CHG	0x0	
+#define UART_IIR_NOINT		0x1	
+#define UART_IIR_THRE		0x2	
+#define UART_IIR_RCVD_DATA	0x4	
+#define UART_IIR_RCVR_STATUS 	0x6	
+#define UART_IIR_CHAR_TIME 	0xc	
+
+
+#define UART_IER_EDSSI	8	
+#define UART_IER_ELSI	4	
+#define UART_IER_ETBEI  2	
+#define UART_IER_ERBFI	1	
+
+
+#define	PST_INTPEND	0x0040
+#define	PST_SBCLKST	0x0030
+#define	PST_SBCLKST_ILP	0x0010
+#define	PST_SBCLKST_ALP	0x0020
+#define	PST_SBCLKST_HT	0x0030
+#define	PST_ALPAVAIL	0x0008
+#define	PST_HTAVAIL	0x0004
+#define	PST_RESINIT	0x0003
+
+
+#define PCAP_REV_MASK	0x000000ff
+#define PCAP_RC_MASK	0x00001f00
+#define PCAP_RC_SHIFT	8
+#define PCAP_TC_MASK	0x0001e000
+#define PCAP_TC_SHIFT	13
+#define PCAP_PC_MASK	0x001e0000
+#define PCAP_PC_SHIFT	17
+#define PCAP_VC_MASK	0x01e00000
+#define PCAP_VC_SHIFT	21
+#define PCAP_CC_MASK	0x1e000000
+#define PCAP_CC_SHIFT	25
+#define PCAP5_PC_MASK	0x003e0000	
+#define PCAP5_PC_SHIFT	17
+#define PCAP5_VC_MASK	0x07c00000
+#define PCAP5_VC_SHIFT	22
+#define PCAP5_CC_MASK	0xf8000000
+#define PCAP5_CC_SHIFT	27
+
+
+
+#define	PRRT_TIME_MASK	0x03ff
+#define	PRRT_INTEN	0x0400
+#define	PRRT_REQ_ACTIVE	0x0800
+#define	PRRT_ALP_REQ	0x1000
+#define	PRRT_HT_REQ	0x2000
+
+
+#define PMURES_BIT(bit)	(1 << (bit))
+
+
+#define PMURES_MAX_RESNUM	30
+
+
+
+
+#define	PMU0_PLL0_PLLCTL0		0
+#define	PMU0_PLL0_PC0_PDIV_MASK		1
+#define	PMU0_PLL0_PC0_PDIV_FREQ		25000
+#define PMU0_PLL0_PC0_DIV_ARM_MASK	0x00000038
+#define PMU0_PLL0_PC0_DIV_ARM_SHIFT	3
+#define PMU0_PLL0_PC0_DIV_ARM_BASE	8
+
+
+#define PMU0_PLL0_PC0_DIV_ARM_110MHZ	0
+#define PMU0_PLL0_PC0_DIV_ARM_97_7MHZ	1
+#define PMU0_PLL0_PC0_DIV_ARM_88MHZ	2
+#define PMU0_PLL0_PC0_DIV_ARM_80MHZ	3 
+#define PMU0_PLL0_PC0_DIV_ARM_73_3MHZ	4
+#define PMU0_PLL0_PC0_DIV_ARM_67_7MHZ	5
+#define PMU0_PLL0_PC0_DIV_ARM_62_9MHZ	6
+#define PMU0_PLL0_PC0_DIV_ARM_58_6MHZ	7
+
+
+#define	PMU0_PLL0_PLLCTL1		1
+#define	PMU0_PLL0_PC1_WILD_INT_MASK	0xf0000000
+#define	PMU0_PLL0_PC1_WILD_INT_SHIFT	28
+#define	PMU0_PLL0_PC1_WILD_FRAC_MASK	0x0fffff00
+#define	PMU0_PLL0_PC1_WILD_FRAC_SHIFT	8
+#define	PMU0_PLL0_PC1_STOP_MOD		0x00000040
+
+
+#define	PMU0_PLL0_PLLCTL2		2
+#define	PMU0_PLL0_PC2_WILD_INT_MASK	0xf
+#define	PMU0_PLL0_PC2_WILD_INT_SHIFT	4
+
+
+#define RES4328_EXT_SWITCHER_PWM	0	
+#define RES4328_BB_SWITCHER_PWM		1	
+#define RES4328_BB_SWITCHER_BURST	2	
+#define RES4328_BB_EXT_SWITCHER_BURST	3	
+#define RES4328_ILP_REQUEST		4	
+#define RES4328_RADIO_SWITCHER_PWM	5	
+#define RES4328_RADIO_SWITCHER_BURST	6	
+#define RES4328_ROM_SWITCH		7	
+#define RES4328_PA_REF_LDO		8	
+#define RES4328_RADIO_LDO		9	
+#define RES4328_AFE_LDO			10	
+#define RES4328_PLL_LDO			11	
+#define RES4328_BG_FILTBYP		12	
+#define RES4328_TX_FILTBYP		13	
+#define RES4328_RX_FILTBYP		14	
+#define RES4328_XTAL_PU			15	
+#define RES4328_XTAL_EN			16	
+#define RES4328_BB_PLL_FILTBYP		17	
+#define RES4328_RF_PLL_FILTBYP		18	
+#define RES4328_BB_PLL_PU		19	
+
+#define RES5354_EXT_SWITCHER_PWM	0	
+#define RES5354_BB_SWITCHER_PWM		1	
+#define RES5354_BB_SWITCHER_BURST	2	
+#define RES5354_BB_EXT_SWITCHER_BURST	3	
+#define RES5354_ILP_REQUEST		4	
+#define RES5354_RADIO_SWITCHER_PWM	5	
+#define RES5354_RADIO_SWITCHER_BURST	6	
+#define RES5354_ROM_SWITCH		7	
+#define RES5354_PA_REF_LDO		8	
+#define RES5354_RADIO_LDO		9	
+#define RES5354_AFE_LDO			10	
+#define RES5354_PLL_LDO			11	
+#define RES5354_BG_FILTBYP		12	
+#define RES5354_TX_FILTBYP		13	
+#define RES5354_RX_FILTBYP		14	
+#define RES5354_XTAL_PU			15	
+#define RES5354_XTAL_EN			16	
+#define RES5354_BB_PLL_FILTBYP		17	
+#define RES5354_RF_PLL_FILTBYP		18	
+#define RES5354_BB_PLL_PU		19	
+
+
+
+#define DOT11MAC_880MHZ_CLK_DIVISOR_SHIFT 8
+#define DOT11MAC_880MHZ_CLK_DIVISOR_MASK (0xFF << DOT11MAC_880MHZ_CLK_DIVISOR_SHIFT)
+#define DOT11MAC_880MHZ_CLK_DIVISOR_VAL  (0xE << DOT11MAC_880MHZ_CLK_DIVISOR_SHIFT)
+
+
+#define PMU2_PHY_PLL_PLLCTL		4
+#define PMU2_SI_PLL_PLLCTL		10
+
+
+#define RES4325_BUCK_BOOST_BURST	0	
+#define RES4325_CBUCK_BURST		1	
+#define RES4325_CBUCK_PWM		2	
+#define RES4325_CLDO_CBUCK_BURST	3	
+#define RES4325_CLDO_CBUCK_PWM		4	
+#define RES4325_BUCK_BOOST_PWM		5	
+#define RES4325_ILP_REQUEST		6	
+#define RES4325_ABUCK_BURST		7	
+#define RES4325_ABUCK_PWM		8	
+#define RES4325_LNLDO1_PU		9	
+#define RES4325_OTP_PU			10	
+#define RES4325_LNLDO3_PU		11	
+#define RES4325_LNLDO4_PU		12	
+#define RES4325_XTAL_PU			13	
+#define RES4325_ALP_AVAIL		14	
+#define RES4325_RX_PWRSW_PU		15	
+#define RES4325_TX_PWRSW_PU		16	
+#define RES4325_RFPLL_PWRSW_PU		17	
+#define RES4325_LOGEN_PWRSW_PU		18	
+#define RES4325_AFE_PWRSW_PU		19	
+#define RES4325_BBPLL_PWRSW_PU		20	
+#define RES4325_HT_AVAIL		21	
+
+
+#define RES4325B0_CBUCK_LPOM		1	
+#define RES4325B0_CBUCK_BURST		2	
+#define RES4325B0_CBUCK_PWM		3	
+#define RES4325B0_CLDO_PU		4	
+
+
+#define RES4325C1_OTP_PWRSW_PU		10	
+#define RES4325C1_LNLDO2_PU		12	
+
+
+#define CST4325_SPROM_OTP_SEL_MASK	0x00000003
+#define CST4325_DEFCIS_SEL		0	
+#define CST4325_SPROM_SEL		1	
+#define CST4325_OTP_SEL			2	
+#define CST4325_OTP_PWRDN		3	
+#define CST4325_SDIO_USB_MODE_MASK	0x00000004
+#define CST4325_SDIO_USB_MODE_SHIFT	2
+#define CST4325_RCAL_VALID_MASK		0x00000008
+#define CST4325_RCAL_VALID_SHIFT	3
+#define CST4325_RCAL_VALUE_MASK		0x000001f0
+#define CST4325_RCAL_VALUE_SHIFT	4
+#define CST4325_PMUTOP_2B_MASK 		0x00000200	
+#define CST4325_PMUTOP_2B_SHIFT   	9
+
+#define RES4329_RESERVED0		0	
+#define RES4329_CBUCK_LPOM		1	
+#define RES4329_CBUCK_BURST		2	
+#define RES4329_CBUCK_PWM		3	
+#define RES4329_CLDO_PU			4	
+#define RES4329_PALDO_PU		5	
+#define RES4329_ILP_REQUEST		6	
+#define RES4329_RESERVED7		7	
+#define RES4329_RESERVED8		8	
+#define RES4329_LNLDO1_PU		9	
+#define RES4329_OTP_PU			10	
+#define RES4329_RESERVED11		11	
+#define RES4329_LNLDO2_PU		12	
+#define RES4329_XTAL_PU			13	
+#define RES4329_ALP_AVAIL		14	
+#define RES4329_RX_PWRSW_PU		15	
+#define RES4329_TX_PWRSW_PU		16	
+#define RES4329_RFPLL_PWRSW_PU		17	
+#define RES4329_LOGEN_PWRSW_PU		18	
+#define RES4329_AFE_PWRSW_PU		19	
+#define RES4329_BBPLL_PWRSW_PU		20	
+#define RES4329_HT_AVAIL		21	
+
+#define CST4329_SPROM_OTP_SEL_MASK	0x00000003
+#define CST4329_DEFCIS_SEL		0	
+#define CST4329_SPROM_SEL		1	
+#define CST4329_OTP_SEL			2	
+#define CST4329_OTP_PWRDN		3	
+#define CST4329_SPI_SDIO_MODE_MASK	0x00000004
+#define CST4329_SPI_SDIO_MODE_SHIFT	2
+
+
+#define RES4312_SWITCHER_BURST		0	
+#define RES4312_SWITCHER_PWM    	1	
+#define RES4312_PA_REF_LDO		2	
+#define RES4312_CORE_LDO_BURST		3	
+#define RES4312_CORE_LDO_PWM		4	
+#define RES4312_RADIO_LDO		5	
+#define RES4312_ILP_REQUEST		6	
+#define RES4312_BG_FILTBYP		7	
+#define RES4312_TX_FILTBYP		8	
+#define RES4312_RX_FILTBYP		9	
+#define RES4312_XTAL_PU			10	
+#define RES4312_ALP_AVAIL		11	
+#define RES4312_BB_PLL_FILTBYP		12	
+#define RES4312_RF_PLL_FILTBYP		13	
+#define RES4312_HT_AVAIL		14	
+
+#define RES4322_RF_LDO			0
+#define RES4322_ILP_REQUEST		1
+#define RES4322_XTAL_PU			2
+#define RES4322_ALP_AVAIL		3
+#define RES4322_SI_PLL_ON		4
+#define RES4322_HT_SI_AVAIL		5
+#define RES4322_PHY_PLL_ON		6
+#define RES4322_HT_PHY_AVAIL		7
+#define RES4322_OTP_PU			8
+
+
+#define CST4322_XTAL_FREQ_20_40MHZ	0x00000020
+#define CST4322_SPROM_OTP_SEL_MASK	0x000000c0
+#define CST4322_SPROM_OTP_SEL_SHIFT	6
+#define CST4322_NO_SPROM_OTP		0	
+#define CST4322_SPROM_PRESENT		1	
+#define CST4322_OTP_PRESENT		2	
+#define CST4322_PCI_OR_USB		0x00000100
+#define CST4322_BOOT_MASK		0x00000600
+#define CST4322_BOOT_SHIFT		9
+#define CST4322_BOOT_FROM_SRAM		0	
+#define CST4322_BOOT_FROM_ROM		1	
+#define CST4322_BOOT_FROM_FLASH		2	
+#define CST4322_BOOT_FROM_INVALID	3
+#define CST4322_ILP_DIV_EN		0x00000800
+#define CST4322_FLASH_TYPE_MASK		0x00001000
+#define CST4322_FLASH_TYPE_SHIFT	12
+#define CST4322_FLASH_TYPE_SHIFT_ST	0	
+#define CST4322_FLASH_TYPE_SHIFT_ATMEL	1	
+#define CST4322_ARM_TAP_SEL		0x00002000
+#define CST4322_RES_INIT_MODE_MASK	0x0000c000
+#define CST4322_RES_INIT_MODE_SHIFT	14
+#define CST4322_RES_INIT_MODE_ILPAVAIL	0	
+#define CST4322_RES_INIT_MODE_ILPREQ	1	
+#define CST4322_RES_INIT_MODE_ALPAVAIL	2	
+#define CST4322_RES_INIT_MODE_HTAVAIL	3	
+#define CST4322_PCIPLLCLK_GATING	0x00010000
+#define CST4322_CLK_SWITCH_PCI_TO_ALP	0x00020000
+#define CST4322_PCI_CARDBUS_MODE	0x00040000
+
+#define RES4315_CBUCK_LPOM		1	
+#define RES4315_CBUCK_BURST		2	
+#define RES4315_CBUCK_PWM		3	
+#define RES4315_CLDO_PU			4	
+#define RES4315_PALDO_PU		5	
+#define RES4315_ILP_REQUEST		6	
+#define RES4315_LNLDO1_PU		9	
+#define RES4315_OTP_PU			10	
+#define RES4315_LNLDO2_PU		12	
+#define RES4315_XTAL_PU			13	
+#define RES4315_ALP_AVAIL		14	
+#define RES4315_RX_PWRSW_PU		15	
+#define RES4315_TX_PWRSW_PU		16	
+#define RES4315_RFPLL_PWRSW_PU		17	
+#define RES4315_LOGEN_PWRSW_PU		18	
+#define RES4315_AFE_PWRSW_PU		19	
+#define RES4315_BBPLL_PWRSW_PU		20	
+#define RES4315_HT_AVAIL		21	
+
+#define CST4315_SPROM_OTP_SEL_MASK	0x00000003	
+#define CST4315_DEFCIS_SEL		0x00000000	
+#define CST4315_SPROM_SEL		0x00000001	
+#define CST4315_OTP_SEL			0x00000002	
+#define CST4315_OTP_PWRDN		0x00000003	
+#define CST4315_SDIO_MODE		0x00000004	
+#define CST4315_RCAL_VALID		0x00000008
+#define CST4315_RCAL_VALUE_MASK		0x000001f0
+#define CST4315_RCAL_VALUE_SHIFT	4
+#define CST4315_PALDO_EXTPNP		0x00000200	
+#define CST4315_CBUCK_MODE_MASK		0x00000c00
+#define CST4315_CBUCK_MODE_BURST	0x00000400
+#define CST4315_CBUCK_MODE_LPBURST	0x00000c00
+
+#define PMU_MAX_TRANSITION_DLY	15000
+
+
+#define PMURES_UP_TRANSITION	2
+
+
+
+
+
+#define ECI_BW_20   0x0
+#define ECI_BW_25   0x1
+#define ECI_BW_30   0x2
+#define ECI_BW_35   0x3
+#define ECI_BW_40   0x4
+#define ECI_BW_45   0x5
+#define ECI_BW_50   0x6
+#define ECI_BW_ALL  0x7
+
+
+#define WLAN_NUM_ANT1 TXANT_0
+#define WLAN_NUM_ANT2 TXANT_1
+
+#endif	
diff --git a/drivers/net/wireless/bcm4329/include/sbconfig.h b/drivers/net/wireless/bcm4329/include/sbconfig.h
new file mode 100644
index 0000000..da18ccb
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/sbconfig.h
@@ -0,0 +1,276 @@
+/*
+ * Broadcom SiliconBackplane hardware register definitions.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: sbconfig.h,v 13.67.30.1 2008/05/07 20:17:27 Exp $
+ */
+
+
+#ifndef	_SBCONFIG_H
+#define	_SBCONFIG_H
+
+
+#ifndef PAD
+#define	_PADLINE(line)	pad ## line
+#define	_XSTR(line)	_PADLINE(line)
+#define	PAD		_XSTR(__LINE__)
+#endif
+
+
+#define SB_BUS_SIZE		0x10000		
+#define SB_BUS_BASE(b)		(SI_ENUM_BASE + (b) * SB_BUS_SIZE)
+#define	SB_BUS_MAXCORES		(SB_BUS_SIZE / SI_CORE_SIZE)	
+
+
+#define	SBCONFIGOFF		0xf00		
+#define	SBCONFIGSIZE		256		
+
+#define SBIPSFLAG		0x08
+#define SBTPSFLAG		0x18
+#define	SBTMERRLOGA		0x48		
+#define	SBTMERRLOG		0x50		
+#define SBADMATCH3		0x60
+#define SBADMATCH2		0x68
+#define SBADMATCH1		0x70
+#define SBIMSTATE		0x90
+#define SBINTVEC		0x94
+#define SBTMSTATELOW		0x98
+#define SBTMSTATEHIGH		0x9c
+#define SBBWA0			0xa0
+#define SBIMCONFIGLOW		0xa8
+#define SBIMCONFIGHIGH		0xac
+#define SBADMATCH0		0xb0
+#define SBTMCONFIGLOW		0xb8
+#define SBTMCONFIGHIGH		0xbc
+#define SBBCONFIG		0xc0
+#define SBBSTATE		0xc8
+#define SBACTCNFG		0xd8
+#define	SBFLAGST		0xe8
+#define SBIDLOW			0xf8
+#define SBIDHIGH		0xfc
+
+
+
+#define SBIMERRLOGA		0xea8
+#define SBIMERRLOG		0xeb0
+#define SBTMPORTCONNID0		0xed8
+#define SBTMPORTLOCK0		0xef8
+
+#ifndef _LANGUAGE_ASSEMBLY
+
+typedef volatile struct _sbconfig {
+	uint32	PAD[2];
+	uint32	sbipsflag;		
+	uint32	PAD[3];
+	uint32	sbtpsflag;		
+	uint32	PAD[11];
+	uint32	sbtmerrloga;		
+	uint32	PAD;
+	uint32	sbtmerrlog;		
+	uint32	PAD[3];
+	uint32	sbadmatch3;		
+	uint32	PAD;
+	uint32	sbadmatch2;		
+	uint32	PAD;
+	uint32	sbadmatch1;		
+	uint32	PAD[7];
+	uint32	sbimstate;		
+	uint32	sbintvec;		
+	uint32	sbtmstatelow;		
+	uint32	sbtmstatehigh;		
+	uint32	sbbwa0;			
+	uint32	PAD;
+	uint32	sbimconfiglow;		
+	uint32	sbimconfighigh;		
+	uint32	sbadmatch0;		
+	uint32	PAD;
+	uint32	sbtmconfiglow;		
+	uint32	sbtmconfighigh;		
+	uint32	sbbconfig;		
+	uint32	PAD;
+	uint32	sbbstate;		
+	uint32	PAD[3];
+	uint32	sbactcnfg;		
+	uint32	PAD[3];
+	uint32	sbflagst;		
+	uint32	PAD[3];
+	uint32	sbidlow;		
+	uint32	sbidhigh;		
+} sbconfig_t;
+
+#endif 
+
+
+#define	SBIPS_INT1_MASK		0x3f		
+#define	SBIPS_INT1_SHIFT	0
+#define	SBIPS_INT2_MASK		0x3f00		
+#define	SBIPS_INT2_SHIFT	8
+#define	SBIPS_INT3_MASK		0x3f0000	
+#define	SBIPS_INT3_SHIFT	16
+#define	SBIPS_INT4_MASK		0x3f000000	
+#define	SBIPS_INT4_SHIFT	24
+
+
+#define	SBTPS_NUM0_MASK		0x3f		
+#define	SBTPS_F0EN0		0x40		
+
+
+#define	SBTMEL_CM		0x00000007	
+#define	SBTMEL_CI		0x0000ff00	
+#define	SBTMEL_EC		0x0f000000	
+#define	SBTMEL_ME		0x80000000	
+
+
+#define	SBIM_PC			0xf		
+#define	SBIM_AP_MASK		0x30		
+#define	SBIM_AP_BOTH		0x00		
+#define	SBIM_AP_TS		0x10		
+#define	SBIM_AP_TK		0x20		
+#define	SBIM_AP_RSV		0x30		
+#define	SBIM_IBE		0x20000		
+#define	SBIM_TO			0x40000		
+#define	SBIM_BY			0x01800000	
+#define	SBIM_RJ			0x02000000	
+
+
+#define	SBTML_RESET		0x0001		
+#define	SBTML_REJ_MASK		0x0006		
+#define	SBTML_REJ		0x0002		
+#define	SBTML_TMPREJ		0x0004		
+
+#define	SBTML_SICF_SHIFT	16		
+
+
+#define	SBTMH_SERR		0x0001		
+#define	SBTMH_INT		0x0002		
+#define	SBTMH_BUSY		0x0004		
+#define	SBTMH_TO		0x0020		
+
+#define	SBTMH_SISF_SHIFT	16		
+
+
+#define	SBBWA_TAB0_MASK		0xffff		
+#define	SBBWA_TAB1_MASK		0xffff		
+#define	SBBWA_TAB1_SHIFT	16
+
+
+#define	SBIMCL_STO_MASK		0x7		
+#define	SBIMCL_RTO_MASK		0x70		
+#define	SBIMCL_RTO_SHIFT	4
+#define	SBIMCL_CID_MASK		0xff0000	
+#define	SBIMCL_CID_SHIFT	16
+
+
+#define	SBIMCH_IEM_MASK		0xc		
+#define	SBIMCH_TEM_MASK		0x30		
+#define	SBIMCH_TEM_SHIFT	4
+#define	SBIMCH_BEM_MASK		0xc0		
+#define	SBIMCH_BEM_SHIFT	6
+
+
+#define	SBAM_TYPE_MASK		0x3		
+#define	SBAM_AD64		0x4		
+#define	SBAM_ADINT0_MASK	0xf8		
+#define	SBAM_ADINT0_SHIFT	3
+#define	SBAM_ADINT1_MASK	0x1f8		
+#define	SBAM_ADINT1_SHIFT	3
+#define	SBAM_ADINT2_MASK	0x1f8		
+#define	SBAM_ADINT2_SHIFT	3
+#define	SBAM_ADEN		0x400		
+#define	SBAM_ADNEG		0x800		
+#define	SBAM_BASE0_MASK		0xffffff00	
+#define	SBAM_BASE0_SHIFT	8
+#define	SBAM_BASE1_MASK		0xfffff000	
+#define	SBAM_BASE1_SHIFT	12
+#define	SBAM_BASE2_MASK		0xffff0000	
+#define	SBAM_BASE2_SHIFT	16
+
+
+#define	SBTMCL_CD_MASK		0xff		
+#define	SBTMCL_CO_MASK		0xf800		
+#define	SBTMCL_CO_SHIFT		11
+#define	SBTMCL_IF_MASK		0xfc0000	
+#define	SBTMCL_IF_SHIFT		18
+#define	SBTMCL_IM_MASK		0x3000000	
+#define	SBTMCL_IM_SHIFT		24
+
+
+#define	SBTMCH_BM_MASK		0x3		
+#define	SBTMCH_RM_MASK		0x3		
+#define	SBTMCH_RM_SHIFT		2
+#define	SBTMCH_SM_MASK		0x30		
+#define	SBTMCH_SM_SHIFT		4
+#define	SBTMCH_EM_MASK		0x300		
+#define	SBTMCH_EM_SHIFT		8
+#define	SBTMCH_IM_MASK		0xc00		
+#define	SBTMCH_IM_SHIFT		10
+
+
+#define	SBBC_LAT_MASK		0x3		
+#define	SBBC_MAX0_MASK		0xf0000		
+#define	SBBC_MAX0_SHIFT		16
+#define	SBBC_MAX1_MASK		0xf00000	
+#define	SBBC_MAX1_SHIFT		20
+
+
+#define	SBBS_SRD		0x1		
+#define	SBBS_HRD		0x2		
+
+
+#define	SBIDL_CS_MASK		0x3		
+#define	SBIDL_AR_MASK		0x38		
+#define	SBIDL_AR_SHIFT		3
+#define	SBIDL_SYNCH		0x40		
+#define	SBIDL_INIT		0x80		
+#define	SBIDL_MINLAT_MASK	0xf00		
+#define	SBIDL_MINLAT_SHIFT	8
+#define	SBIDL_MAXLAT		0xf000		
+#define	SBIDL_MAXLAT_SHIFT	12
+#define	SBIDL_FIRST		0x10000		
+#define	SBIDL_CW_MASK		0xc0000		
+#define	SBIDL_CW_SHIFT		18
+#define	SBIDL_TP_MASK		0xf00000	
+#define	SBIDL_TP_SHIFT		20
+#define	SBIDL_IP_MASK		0xf000000	
+#define	SBIDL_IP_SHIFT		24
+#define	SBIDL_RV_MASK		0xf0000000	
+#define	SBIDL_RV_SHIFT		28
+#define	SBIDL_RV_2_2		0x00000000	
+#define	SBIDL_RV_2_3		0x10000000	
+
+
+#define	SBIDH_RC_MASK		0x000f		
+#define	SBIDH_RCE_MASK		0x7000		
+#define	SBIDH_RCE_SHIFT		8
+#define	SBCOREREV(sbidh) \
+	((((sbidh) & SBIDH_RCE_MASK) >> SBIDH_RCE_SHIFT) | ((sbidh) & SBIDH_RC_MASK))
+#define	SBIDH_CC_MASK		0x8ff0		
+#define	SBIDH_CC_SHIFT		4
+#define	SBIDH_VC_MASK		0xffff0000	
+#define	SBIDH_VC_SHIFT		16
+
+#define	SB_COMMIT		0xfd8		
+
+
+#define	SB_VEND_BCM		0x4243		
+
+#endif	
diff --git a/drivers/net/wireless/bcm4329/include/sbhnddma.h b/drivers/net/wireless/bcm4329/include/sbhnddma.h
new file mode 100644
index 0000000..7681395
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/sbhnddma.h
@@ -0,0 +1,294 @@
+/*
+ * Generic Broadcom Home Networking Division (HND) DMA engine HW interface
+ * This supports the following chips: BCM42xx, 44xx, 47xx .
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: sbhnddma.h,v 13.11.250.5.16.1 2009/07/21 14:04:51 Exp $
+ */
+
+
+#ifndef	_sbhnddma_h_
+#define	_sbhnddma_h_
+
+
+
+
+
+
+
+typedef volatile struct {
+	uint32	control;		
+	uint32	addr;			
+	uint32	ptr;			
+	uint32	status;			
+} dma32regs_t;
+
+typedef volatile struct {
+	dma32regs_t	xmt;		
+	dma32regs_t	rcv;		
+} dma32regp_t;
+
+typedef volatile struct {	
+	uint32	fifoaddr;		
+	uint32	fifodatalow;		
+	uint32	fifodatahigh;		
+	uint32	pad;			
+} dma32diag_t;
+
+
+typedef volatile struct {
+	uint32	ctrl;		
+	uint32	addr;		
+} dma32dd_t;
+
+
+#define	D32RINGALIGN_BITS	12
+#define	D32MAXRINGSZ	(1 << D32RINGALIGN_BITS)
+#define	D32RINGALIGN	(1 << D32RINGALIGN_BITS)
+#define	D32MAXDD	(D32MAXRINGSZ / sizeof (dma32dd_t))
+
+
+#define	XC_XE		((uint32)1 << 0)	
+#define	XC_SE		((uint32)1 << 1)	
+#define	XC_LE		((uint32)1 << 2)	
+#define	XC_FL		((uint32)1 << 4)	
+#define	XC_PD		((uint32)1 << 11)	
+#define	XC_AE		((uint32)3 << 16)	
+#define	XC_AE_SHIFT	16
+
+
+#define	XP_LD_MASK	0xfff			
+
+
+#define	XS_CD_MASK	0x0fff			
+#define	XS_XS_MASK	0xf000			
+#define	XS_XS_SHIFT	12
+#define	XS_XS_DISABLED	0x0000			
+#define	XS_XS_ACTIVE	0x1000			
+#define	XS_XS_IDLE	0x2000			
+#define	XS_XS_STOPPED	0x3000			
+#define	XS_XS_SUSP	0x4000			
+#define	XS_XE_MASK	0xf0000			
+#define	XS_XE_SHIFT	16
+#define	XS_XE_NOERR	0x00000			
+#define	XS_XE_DPE	0x10000			
+#define	XS_XE_DFU	0x20000			
+#define	XS_XE_BEBR	0x30000			
+#define	XS_XE_BEDA	0x40000			
+#define	XS_AD_MASK	0xfff00000		
+#define	XS_AD_SHIFT	20
+
+
+#define	RC_RE		((uint32)1 << 0)	
+#define	RC_RO_MASK	0xfe			
+#define	RC_RO_SHIFT	1
+#define	RC_FM		((uint32)1 << 8)	
+#define	RC_SH		((uint32)1 << 9)	
+#define	RC_OC		((uint32)1 << 10)	
+#define	RC_PD		((uint32)1 << 11)	
+#define	RC_AE		((uint32)3 << 16)	
+#define	RC_AE_SHIFT	16
+
+
+#define	RP_LD_MASK	0xfff			
+
+
+#define	RS_CD_MASK	0x0fff			
+#define	RS_RS_MASK	0xf000			
+#define	RS_RS_SHIFT	12
+#define	RS_RS_DISABLED	0x0000			
+#define	RS_RS_ACTIVE	0x1000			
+#define	RS_RS_IDLE	0x2000			
+#define	RS_RS_STOPPED	0x3000			
+#define	RS_RE_MASK	0xf0000			
+#define	RS_RE_SHIFT	16
+#define	RS_RE_NOERR	0x00000			
+#define	RS_RE_DPE	0x10000			
+#define	RS_RE_DFO	0x20000			
+#define	RS_RE_BEBW	0x30000			
+#define	RS_RE_BEDA	0x40000			
+#define	RS_AD_MASK	0xfff00000		
+#define	RS_AD_SHIFT	20
+
+
+#define	FA_OFF_MASK	0xffff			
+#define	FA_SEL_MASK	0xf0000			
+#define	FA_SEL_SHIFT	16
+#define	FA_SEL_XDD	0x00000			
+#define	FA_SEL_XDP	0x10000			
+#define	FA_SEL_RDD	0x40000			
+#define	FA_SEL_RDP	0x50000			
+#define	FA_SEL_XFD	0x80000			
+#define	FA_SEL_XFP	0x90000			
+#define	FA_SEL_RFD	0xc0000			
+#define	FA_SEL_RFP	0xd0000			
+#define	FA_SEL_RSD	0xe0000			
+#define	FA_SEL_RSP	0xf0000			
+
+
+#define	CTRL_BC_MASK	0x1fff			
+#define	CTRL_AE		((uint32)3 << 16)	
+#define	CTRL_AE_SHIFT	16
+#define	CTRL_EOT	((uint32)1 << 28)	
+#define	CTRL_IOC	((uint32)1 << 29)	
+#define	CTRL_EOF	((uint32)1 << 30)	
+#define	CTRL_SOF	((uint32)1 << 31)	
+
+
+#define	CTRL_CORE_MASK	0x0ff00000
+
+
+
+
+typedef volatile struct {
+	uint32	control;		
+	uint32	ptr;			
+	uint32	addrlow;		
+	uint32	addrhigh;		
+	uint32	status0;		
+	uint32	status1;		
+} dma64regs_t;
+
+typedef volatile struct {
+	dma64regs_t	tx;		
+	dma64regs_t	rx;		
+} dma64regp_t;
+
+typedef volatile struct {		
+	uint32	fifoaddr;		
+	uint32	fifodatalow;		
+	uint32	fifodatahigh;		
+	uint32	pad;			
+} dma64diag_t;
+
+
+typedef volatile struct {
+	uint32	ctrl1;		
+	uint32	ctrl2;		
+	uint32	addrlow;	
+	uint32	addrhigh;	
+} dma64dd_t;
+
+
+#define D64RINGALIGN_BITS 13	
+#define	D64MAXRINGSZ	(1 << D64RINGALIGN_BITS)
+#define	D64RINGALIGN	(1 << D64RINGALIGN_BITS)
+#define	D64MAXDD	(D64MAXRINGSZ / sizeof (dma64dd_t))
+
+
+#define	D64_XC_XE		0x00000001	
+#define	D64_XC_SE		0x00000002	
+#define	D64_XC_LE		0x00000004	
+#define	D64_XC_FL		0x00000010	
+#define	D64_XC_PD		0x00000800	
+#define	D64_XC_AE		0x00030000	
+#define	D64_XC_AE_SHIFT		16
+
+
+#define	D64_XP_LD_MASK		0x00000fff	
+
+
+#define	D64_XS0_CD_MASK		0x00001fff	
+#define	D64_XS0_XS_MASK		0xf0000000     	
+#define	D64_XS0_XS_SHIFT		28
+#define	D64_XS0_XS_DISABLED	0x00000000	
+#define	D64_XS0_XS_ACTIVE	0x10000000	
+#define	D64_XS0_XS_IDLE		0x20000000	
+#define	D64_XS0_XS_STOPPED	0x30000000	
+#define	D64_XS0_XS_SUSP		0x40000000	
+
+#define	D64_XS1_AD_MASK		0x0001ffff	
+#define	D64_XS1_XE_MASK		0xf0000000     	
+#define	D64_XS1_XE_SHIFT		28
+#define	D64_XS1_XE_NOERR	0x00000000	
+#define	D64_XS1_XE_DPE		0x10000000	
+#define	D64_XS1_XE_DFU		0x20000000	
+#define	D64_XS1_XE_DTE		0x30000000	
+#define	D64_XS1_XE_DESRE	0x40000000	
+#define	D64_XS1_XE_COREE	0x50000000	
+
+
+#define	D64_RC_RE		0x00000001	
+#define	D64_RC_RO_MASK		0x000000fe	
+#define	D64_RC_RO_SHIFT		1
+#define	D64_RC_FM		0x00000100	
+#define	D64_RC_SH		0x00000200	
+#define	D64_RC_OC		0x00000400	
+#define	D64_RC_PD		0x00000800	
+#define	D64_RC_AE		0x00030000	
+#define	D64_RC_AE_SHIFT		16
+
+
+#define	D64_RP_LD_MASK		0x00000fff	
+
+
+#define	D64_RS0_CD_MASK		0x00001fff	
+#define	D64_RS0_RS_MASK		0xf0000000     	
+#define	D64_RS0_RS_SHIFT		28
+#define	D64_RS0_RS_DISABLED	0x00000000	
+#define	D64_RS0_RS_ACTIVE	0x10000000	
+#define	D64_RS0_RS_IDLE		0x20000000	
+#define	D64_RS0_RS_STOPPED	0x30000000	
+#define	D64_RS0_RS_SUSP		0x40000000	
+
+#define	D64_RS1_AD_MASK		0x0001ffff	
+#define	D64_RS1_RE_MASK		0xf0000000     	
+#define	D64_RS1_RE_SHIFT		28
+#define	D64_RS1_RE_NOERR	0x00000000	
+#define	D64_RS1_RE_DPO		0x10000000	
+#define	D64_RS1_RE_DFU		0x20000000	
+#define	D64_RS1_RE_DTE		0x30000000	
+#define	D64_RS1_RE_DESRE	0x40000000	
+#define	D64_RS1_RE_COREE	0x50000000	
+
+
+#define	D64_FA_OFF_MASK		0xffff		
+#define	D64_FA_SEL_MASK		0xf0000		
+#define	D64_FA_SEL_SHIFT	16
+#define	D64_FA_SEL_XDD		0x00000		
+#define	D64_FA_SEL_XDP		0x10000		
+#define	D64_FA_SEL_RDD		0x40000		
+#define	D64_FA_SEL_RDP		0x50000		
+#define	D64_FA_SEL_XFD		0x80000		
+#define	D64_FA_SEL_XFP		0x90000		
+#define	D64_FA_SEL_RFD		0xc0000		
+#define	D64_FA_SEL_RFP		0xd0000		
+#define	D64_FA_SEL_RSD		0xe0000		
+#define	D64_FA_SEL_RSP		0xf0000		
+
+
+#define	D64_CTRL1_EOT		((uint32)1 << 28)	
+#define	D64_CTRL1_IOC		((uint32)1 << 29)	
+#define	D64_CTRL1_EOF		((uint32)1 << 30)	
+#define	D64_CTRL1_SOF		((uint32)1 << 31)	
+
+
+#define	D64_CTRL2_BC_MASK	0x00007fff	
+#define	D64_CTRL2_AE		0x00030000	
+#define	D64_CTRL2_AE_SHIFT	16
+#define D64_CTRL2_PARITY	0x00040000      
+
+
+#define	D64_CTRL_CORE_MASK	0x0ff00000
+
+
+#endif	
diff --git a/drivers/net/wireless/bcm4329/include/sbpcmcia.h b/drivers/net/wireless/bcm4329/include/sbpcmcia.h
new file mode 100644
index 0000000..d6d8033
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/sbpcmcia.h
@@ -0,0 +1,109 @@
+/*
+ * BCM43XX Sonics SiliconBackplane PCMCIA core hardware definitions.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: sbpcmcia.h,v 13.31.4.1.2.3.8.7 2009/06/22 05:14:24 Exp $
+ */
+
+
+#ifndef	_SBPCMCIA_H
+#define	_SBPCMCIA_H
+
+
+
+
+#define	PCMCIA_FCR		(0x700 / 2)
+
+#define	FCR0_OFF		0
+#define	FCR1_OFF		(0x40 / 2)
+#define	FCR2_OFF		(0x80 / 2)
+#define	FCR3_OFF		(0xc0 / 2)
+
+#define	PCMCIA_FCR0		(0x700 / 2)
+#define	PCMCIA_FCR1		(0x740 / 2)
+#define	PCMCIA_FCR2		(0x780 / 2)
+#define	PCMCIA_FCR3		(0x7c0 / 2)
+
+
+
+#define	PCMCIA_COR		0
+
+#define	COR_RST			0x80
+#define	COR_LEV			0x40
+#define	COR_IRQEN		0x04
+#define	COR_BLREN		0x01
+#define	COR_FUNEN		0x01
+
+
+#define	PCICIA_FCSR		(2 / 2)
+#define	PCICIA_PRR		(4 / 2)
+#define	PCICIA_SCR		(6 / 2)
+#define	PCICIA_ESR		(8 / 2)
+
+
+#define PCM_MEMOFF		0x0000
+#define F0_MEMOFF		0x1000
+#define F1_MEMOFF		0x2000
+#define F2_MEMOFF		0x3000
+#define F3_MEMOFF		0x4000
+
+
+#define MEM_ADDR0		(0x728 / 2)
+#define MEM_ADDR1		(0x72a / 2)
+#define MEM_ADDR2		(0x72c / 2)
+
+
+#define PCMCIA_ADDR0		(0x072e / 2)
+#define PCMCIA_ADDR1		(0x0730 / 2)
+#define PCMCIA_ADDR2		(0x0732 / 2)
+
+#define MEM_SEG			(0x0734 / 2)
+#define SROM_CS			(0x0736 / 2)
+#define SROM_DATAL		(0x0738 / 2)
+#define SROM_DATAH		(0x073a / 2)
+#define SROM_ADDRL		(0x073c / 2)
+#define SROM_ADDRH		(0x073e / 2)
+#define	SROM_INFO2		(0x0772 / 2)	
+#define	SROM_INFO		(0x07be / 2)	
+
+
+#define SROM_IDLE		0
+#define SROM_WRITE		1
+#define SROM_READ		2
+#define SROM_WEN		4
+#define SROM_WDS		7
+#define SROM_DONE		8
+
+
+#define	SRI_SZ_MASK		0x03
+#define	SRI_BLANK		0x04
+#define	SRI_OTP			0x80
+
+
+
+#define SBTML_INT_ACK		0x40000		
+#define SBTML_INT_EN		0x20000		
+
+
+#define SBTMH_INT_STATUS	0x40000		
+
+#endif	
diff --git a/drivers/net/wireless/bcm4329/include/sbsdio.h b/drivers/net/wireless/bcm4329/include/sbsdio.h
new file mode 100644
index 0000000..75aaf4d
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/sbsdio.h
@@ -0,0 +1,166 @@
+/*
+ * SDIO device core hardware definitions.
+ * sdio is a portion of the pcmcia core in core rev 3 - rev 8
+ *
+ * SDIO core support 1bit, 4 bit SDIO mode as well as SPI mode.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: sbsdio.h,v 13.29.4.1.22.3 2009/03/11 20:26:57 Exp $
+ */
+
+#ifndef	_SBSDIO_H
+#define	_SBSDIO_H
+
+#define SBSDIO_NUM_FUNCTION		3	/* as of sdiod rev 0, supports 3 functions */
+
+/* function 1 miscellaneous registers */
+#define SBSDIO_SPROM_CS			0x10000		/* sprom command and status */
+#define SBSDIO_SPROM_INFO		0x10001		/* sprom info register */
+#define SBSDIO_SPROM_DATA_LOW		0x10002		/* sprom indirect access data byte 0 */
+#define SBSDIO_SPROM_DATA_HIGH		0x10003 	/* sprom indirect access data byte 1 */
+#define SBSDIO_SPROM_ADDR_LOW		0x10004		/* sprom indirect access addr byte 0 */
+#define SBSDIO_SPROM_ADDR_HIGH		0x10005		/* sprom indirect access addr byte 0 */
+#define SBSDIO_CHIP_CTRL_DATA		0x10006		/* xtal_pu (gpio) output */
+#define SBSDIO_CHIP_CTRL_EN		0x10007		/* xtal_pu (gpio) enable */
+#define SBSDIO_WATERMARK		0x10008		/* rev < 7, watermark for sdio device */
+#define SBSDIO_DEVICE_CTL		0x10009		/* control busy signal generation */
+
+/* registers introduced in rev 8, some content (mask/bits) defs in sbsdpcmdev.h */
+#define SBSDIO_FUNC1_SBADDRLOW		0x1000A		/* SB Address Window Low (b15) */
+#define SBSDIO_FUNC1_SBADDRMID		0x1000B		/* SB Address Window Mid (b23:b16) */
+#define SBSDIO_FUNC1_SBADDRHIGH		0x1000C		/* SB Address Window High (b31:b24)    */
+#define SBSDIO_FUNC1_FRAMECTRL		0x1000D		/* Frame Control (frame term/abort) */
+#define SBSDIO_FUNC1_CHIPCLKCSR		0x1000E		/* ChipClockCSR (ALP/HT ctl/status) */
+#define SBSDIO_FUNC1_SDIOPULLUP 	0x1000F		/* SdioPullUp (on cmd, d0-d2) */
+#define SBSDIO_FUNC1_WFRAMEBCLO		0x10019		/* Write Frame Byte Count Low */
+#define SBSDIO_FUNC1_WFRAMEBCHI		0x1001A		/* Write Frame Byte Count High */
+#define SBSDIO_FUNC1_RFRAMEBCLO		0x1001B		/* Read Frame Byte Count Low */
+#define SBSDIO_FUNC1_RFRAMEBCHI		0x1001C		/* Read Frame Byte Count High */
+
+#define SBSDIO_FUNC1_MISC_REG_START	0x10000 	/* f1 misc register start */
+#define SBSDIO_FUNC1_MISC_REG_LIMIT	0x1001C 	/* f1 misc register end */
+
+/* SBSDIO_SPROM_CS */
+#define SBSDIO_SPROM_IDLE		0
+#define SBSDIO_SPROM_WRITE		1
+#define SBSDIO_SPROM_READ		2
+#define SBSDIO_SPROM_WEN		4
+#define SBSDIO_SPROM_WDS		7
+#define SBSDIO_SPROM_DONE		8
+
+/* SBSDIO_SPROM_INFO */
+#define SROM_SZ_MASK			0x03		/* SROM size, 1: 4k, 2: 16k */
+#define SROM_BLANK			0x04		/* depreciated in corerev 6 */
+#define	SROM_OTP			0x80		/* OTP present */
+
+/* SBSDIO_CHIP_CTRL */
+#define SBSDIO_CHIP_CTRL_XTAL		0x01		/* or'd with onchip xtal_pu,
+							 * 1: power on oscillator
+							 * (for 4318 only)
+							 */
+/* SBSDIO_WATERMARK */
+#define SBSDIO_WATERMARK_MASK		0x7f		/* number of words - 1 for sd device
+							 * to wait before sending data to host
+							 */
+
+/* SBSDIO_DEVICE_CTL */
+#define SBSDIO_DEVCTL_SETBUSY		0x01		/* 1: device will assert busy signal when
+							 * receiving CMD53
+							 */
+#define SBSDIO_DEVCTL_SPI_INTR_SYNC	0x02		/* 1: assertion of sdio interrupt is
+							 * synchronous to the sdio clock
+							 */
+#define SBSDIO_DEVCTL_CA_INT_ONLY	0x04		/* 1: mask all interrupts to host
+							 * except the chipActive (rev 8)
+							 */
+#define SBSDIO_DEVCTL_PADS_ISO		0x08		/* 1: isolate internal sdio signals, put
+							 * external pads in tri-state; requires
+							 * sdio bus power cycle to clear (rev 9)
+							 */
+#define SBSDIO_DEVCTL_SB_RST_CTL	0x30		/* Force SD->SB reset mapping (rev 11) */
+#define SBSDIO_DEVCTL_RST_CORECTL	0x00		/*   Determined by CoreControl bit */
+#define SBSDIO_DEVCTL_RST_BPRESET	0x10		/*   Force backplane reset */
+#define SBSDIO_DEVCTL_RST_NOBPRESET	0x20		/*   Force no backplane reset */
+
+
+/* SBSDIO_FUNC1_CHIPCLKCSR */
+#define SBSDIO_FORCE_ALP		0x01		/* Force ALP request to backplane */
+#define SBSDIO_FORCE_HT			0x02		/* Force HT request to backplane */
+#define SBSDIO_FORCE_ILP		0x04		/* Force ILP request to backplane */
+#define SBSDIO_ALP_AVAIL_REQ		0x08		/* Make ALP ready (power up xtal) */
+#define SBSDIO_HT_AVAIL_REQ		0x10		/* Make HT ready (power up PLL) */
+#define SBSDIO_FORCE_HW_CLKREQ_OFF	0x20		/* Squelch clock requests from HW */
+#define SBSDIO_ALP_AVAIL		0x40		/* Status: ALP is ready */
+#define SBSDIO_HT_AVAIL			0x80		/* Status: HT is ready */
+/* In rev8, actual avail bits followed original docs */
+#define SBSDIO_Rev8_HT_AVAIL		0x40
+#define SBSDIO_Rev8_ALP_AVAIL		0x80
+
+#define SBSDIO_AVBITS			(SBSDIO_HT_AVAIL | SBSDIO_ALP_AVAIL)
+#define SBSDIO_ALPAV(regval)		((regval) & SBSDIO_AVBITS)
+#define SBSDIO_HTAV(regval)		(((regval) & SBSDIO_AVBITS) == SBSDIO_AVBITS)
+#define SBSDIO_ALPONLY(regval)		(SBSDIO_ALPAV(regval) && !SBSDIO_HTAV(regval))
+#define SBSDIO_CLKAV(regval, alponly)	(SBSDIO_ALPAV(regval) && \
+					(alponly ? 1 : SBSDIO_HTAV(regval)))
+
+/* SBSDIO_FUNC1_SDIOPULLUP */
+#define SBSDIO_PULLUP_D0		0x01		/* Enable D0/MISO pullup */
+#define SBSDIO_PULLUP_D1		0x02		/* Enable D1/INT# pullup */
+#define SBSDIO_PULLUP_D2		0x04		/* Enable D2 pullup */
+#define SBSDIO_PULLUP_CMD		0x08		/* Enable CMD/MOSI pullup */
+#define SBSDIO_PULLUP_ALL		0x0f		/* All valid bits */
+
+/* function 1 OCP space */
+#define SBSDIO_SB_OFT_ADDR_MASK		0x07FFF		/* sb offset addr is <= 15 bits, 32k */
+#define SBSDIO_SB_OFT_ADDR_LIMIT	0x08000
+#define SBSDIO_SB_ACCESS_2_4B_FLAG	0x08000		/* with b15, maps to 32-bit SB access */
+
+/* some duplication with sbsdpcmdev.h here */
+/* valid bits in SBSDIO_FUNC1_SBADDRxxx regs */
+#define SBSDIO_SBADDRLOW_MASK		0x80		/* Valid bits in SBADDRLOW */
+#define SBSDIO_SBADDRMID_MASK		0xff		/* Valid bits in SBADDRMID */
+#define SBSDIO_SBADDRHIGH_MASK		0xffU		/* Valid bits in SBADDRHIGH */
+#define SBSDIO_SBWINDOW_MASK		0xffff8000	/* Address bits from SBADDR regs */
+
+/* direct(mapped) cis space */
+#define SBSDIO_CIS_BASE_COMMON		0x1000		/* MAPPED common CIS address */
+#define SBSDIO_CIS_SIZE_LIMIT		0x200		/* maximum bytes in one CIS */
+#define SBSDIO_OTP_CIS_SIZE_LIMIT       0x078           /* maximum bytes OTP CIS */
+
+#define SBSDIO_CIS_OFT_ADDR_MASK	0x1FFFF		/* cis offset addr is < 17 bits */
+
+#define SBSDIO_CIS_MANFID_TUPLE_LEN	6		/* manfid tuple length, include tuple,
+							 * link bytes
+							 */
+
+/* indirect cis access (in sprom) */
+#define SBSDIO_SPROM_CIS_OFFSET		0x8		/* 8 control bytes first, CIS starts from
+							 * 8th byte
+							 */
+
+#define SBSDIO_BYTEMODE_DATALEN_MAX	64		/* sdio byte mode: maximum length of one
+							 * data comamnd
+							 */
+
+#define SBSDIO_CORE_ADDR_MASK		0x1FFFF		/* sdio core function one address mask */
+
+#endif	/* _SBSDIO_H */
diff --git a/drivers/net/wireless/bcm4329/include/sbsdpcmdev.h b/drivers/net/wireless/bcm4329/include/sbsdpcmdev.h
new file mode 100644
index 0000000..7c7c7e4
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/sbsdpcmdev.h
@@ -0,0 +1,288 @@
+/*
+ * Broadcom SiliconBackplane SDIO/PCMCIA hardware-specific device core support
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: sbsdpcmdev.h,v 13.29.4.1.4.6.6.2 2008/12/31 21:16:51 Exp $
+ */
+
+#ifndef	_sbsdpcmdev_h_
+#define	_sbsdpcmdev_h_
+
+/* cpp contortions to concatenate w/arg prescan */
+#ifndef PAD
+#define	_PADLINE(line)	pad ## line
+#define	_XSTR(line)	_PADLINE(line)
+#define	PAD		_XSTR(__LINE__)
+#endif	/* PAD */
+
+
+typedef volatile struct {
+	dma64regs_t	xmt;		/* dma tx */
+	uint32 PAD[2];
+	dma64regs_t	rcv;		/* dma rx */
+	uint32 PAD[2];
+} dma64p_t;
+
+/* dma64 sdiod corerev >= 1 */
+typedef volatile struct {
+	dma64p_t dma64regs[2];
+	dma64diag_t dmafifo;		/* DMA Diagnostic Regs, 0x280-0x28c */
+	uint32 PAD[92];
+} sdiodma64_t;
+
+/* dma32 sdiod corerev == 0 */
+typedef volatile struct {
+	dma32regp_t dma32regs[2];	/* dma tx & rx, 0x200-0x23c */
+	dma32diag_t dmafifo;		/* DMA Diagnostic Regs, 0x240-0x24c */
+	uint32 PAD[108];
+} sdiodma32_t;
+
+/* dma32 regs for pcmcia core */
+typedef volatile struct {
+	dma32regp_t dmaregs;		/* DMA Regs, 0x200-0x21c, rev8 */
+	dma32diag_t dmafifo;		/* DMA Diagnostic Regs, 0x220-0x22c */
+	uint32 PAD[116];
+} pcmdma32_t;
+
+/* core registers */
+typedef volatile struct {
+	uint32 corecontrol;		/* CoreControl, 0x000, rev8 */
+	uint32 corestatus;		/* CoreStatus, 0x004, rev8  */
+	uint32 PAD[1];
+	uint32 biststatus;		/* BistStatus, 0x00c, rev8  */
+
+	/* PCMCIA access */
+	uint16 pcmciamesportaladdr;	/* PcmciaMesPortalAddr, 0x010, rev8   */
+	uint16 PAD[1];
+	uint16 pcmciamesportalmask;	/* PcmciaMesPortalMask, 0x014, rev8   */
+	uint16 PAD[1];
+	uint16 pcmciawrframebc;		/* PcmciaWrFrameBC, 0x018, rev8   */
+	uint16 PAD[1];
+	uint16 pcmciaunderflowtimer;	/* PcmciaUnderflowTimer, 0x01c, rev8   */
+	uint16 PAD[1];
+
+	/* interrupt */
+	uint32 intstatus;		/* IntStatus, 0x020, rev8   */
+	uint32 hostintmask;		/* IntHostMask, 0x024, rev8   */
+	uint32 intmask;			/* IntSbMask, 0x028, rev8   */
+	uint32 sbintstatus;		/* SBIntStatus, 0x02c, rev8   */
+	uint32 sbintmask;		/* SBIntMask, 0x030, rev8   */
+	uint32 PAD[3];
+	uint32 tosbmailbox;		/* ToSBMailbox, 0x040, rev8   */
+	uint32 tohostmailbox;		/* ToHostMailbox, 0x044, rev8   */
+	uint32 tosbmailboxdata;		/* ToSbMailboxData, 0x048, rev8   */
+	uint32 tohostmailboxdata;	/* ToHostMailboxData, 0x04c, rev8   */
+
+	/* synchronized access to registers in SDIO clock domain */
+	uint32 sdioaccess;		/* SdioAccess, 0x050, rev8   */
+	uint32 PAD[3];
+
+	/* PCMCIA frame control */
+	uint8  pcmciaframectrl;		/* pcmciaFrameCtrl, 0x060, rev8   */
+	uint8  PAD[3];
+	uint8  pcmciawatermark;		/* pcmciaWaterMark, 0x064, rev8   */
+	uint8  PAD[155];
+
+	/* interrupt batching control */
+	uint32 intrcvlazy;		/* IntRcvLazy, 0x100, rev8 */
+	uint32 PAD[3];
+
+	/* counters */
+	uint32 cmd52rd;			/* Cmd52RdCount, 0x110, rev8, SDIO: cmd52 reads */
+	uint32 cmd52wr;			/* Cmd52WrCount, 0x114, rev8, SDIO: cmd52 writes */
+	uint32 cmd53rd;			/* Cmd53RdCount, 0x118, rev8, SDIO: cmd53 reads */
+	uint32 cmd53wr;			/* Cmd53WrCount, 0x11c, rev8, SDIO: cmd53 writes */
+	uint32 abort;			/* AbortCount, 0x120, rev8, SDIO: aborts */
+	uint32 datacrcerror;		/* DataCrcErrorCount, 0x124, rev8, SDIO: frames w/bad CRC */
+	uint32 rdoutofsync;		/* RdOutOfSyncCount, 0x128, rev8, SDIO/PCMCIA: Rd Frm OOS */
+	uint32 wroutofsync;		/* RdOutOfSyncCount, 0x12c, rev8, SDIO/PCMCIA: Wr Frm OOS */
+	uint32 writebusy;		/* WriteBusyCount, 0x130, rev8, SDIO: dev asserted "busy" */
+	uint32 readwait;		/* ReadWaitCount, 0x134, rev8, SDIO: read: no data avail */
+	uint32 readterm;		/* ReadTermCount, 0x138, rev8, SDIO: rd frm terminates */
+	uint32 writeterm;		/* WriteTermCount, 0x13c, rev8, SDIO: wr frm terminates */
+	uint32 PAD[40];
+	uint32 clockctlstatus;		/* ClockCtlStatus, 0x1e0, rev8 */
+	uint32 PAD[7];
+
+	/* DMA engines */
+	volatile union {
+		pcmdma32_t pcm32;
+		sdiodma32_t sdiod32;
+		sdiodma64_t sdiod64;
+	} dma;
+
+	/* SDIO/PCMCIA CIS region */
+	char cis[512];			/* 512 byte CIS, 0x400-0x5ff, rev6 */
+
+	/* PCMCIA function control registers */
+	char pcmciafcr[256];		/* PCMCIA FCR, 0x600-6ff, rev6 */
+	uint16 PAD[55];
+
+	/* PCMCIA backplane access */
+	uint16 backplanecsr;		/* BackplaneCSR, 0x76E, rev6 */
+	uint16 backplaneaddr0;		/* BackplaneAddr0, 0x770, rev6 */
+	uint16 backplaneaddr1;		/* BackplaneAddr1, 0x772, rev6 */
+	uint16 backplaneaddr2;		/* BackplaneAddr2, 0x774, rev6 */
+	uint16 backplaneaddr3;		/* BackplaneAddr3, 0x776, rev6 */
+	uint16 backplanedata0;		/* BackplaneData0, 0x778, rev6 */
+	uint16 backplanedata1;		/* BackplaneData1, 0x77a, rev6 */
+	uint16 backplanedata2;		/* BackplaneData2, 0x77c, rev6 */
+	uint16 backplanedata3;		/* BackplaneData3, 0x77e, rev6 */
+	uint16 PAD[31];
+
+	/* sprom "size" & "blank" info */
+	uint16 spromstatus;		/* SPROMStatus, 0x7BE, rev2 */
+	uint32 PAD[464];
+
+	/* Sonics SiliconBackplane registers */
+	sbconfig_t sbconfig;		/* SbConfig Regs, 0xf00-0xfff, rev8 */
+} sdpcmd_regs_t;
+
+/* corecontrol */
+#define CC_CISRDY	(1 << 0)	/* CIS Ready */
+#define CC_BPRESEN	(1 << 1)	/* CCCR RES signal causes backplane reset */
+#define CC_F2RDY	(1 << 2)	/* set CCCR IOR2 bit */
+#define CC_CLRPADSISO	(1 << 3)	/* clear SDIO pads isolation bit (rev 11) */
+
+/* corestatus */
+#define CS_PCMCIAMODE	(1 << 0)	/* Device Mode; 0=SDIO, 1=PCMCIA */
+#define CS_SMARTDEV	(1 << 1)	/* 1=smartDev enabled */
+#define CS_F2ENABLED	(1 << 2)	/* 1=host has enabled the device */
+
+#define PCMCIA_MES_PA_MASK	0x7fff	/* PCMCIA Message Portal Address Mask */
+#define PCMCIA_MES_PM_MASK	0x7fff	/* PCMCIA Message Portal Mask Mask */
+#define PCMCIA_WFBC_MASK	0xffff	/* PCMCIA Write Frame Byte Count Mask */
+#define PCMCIA_UT_MASK		0x07ff	/* PCMCIA Underflow Timer Mask */
+
+/* intstatus */
+#define I_SMB_SW0	(1 << 0)	/* To SB Mail S/W interrupt 0 */
+#define I_SMB_SW1	(1 << 1)	/* To SB Mail S/W interrupt 1 */
+#define I_SMB_SW2	(1 << 2)	/* To SB Mail S/W interrupt 2 */
+#define I_SMB_SW3	(1 << 3)	/* To SB Mail S/W interrupt 3 */
+#define I_SMB_SW_MASK	0x0000000f	/* To SB Mail S/W interrupts mask */
+#define I_SMB_SW_SHIFT	0		/* To SB Mail S/W interrupts shift */
+#define I_HMB_SW0	(1 << 4)	/* To Host Mail S/W interrupt 0 */
+#define I_HMB_SW1	(1 << 5)	/* To Host Mail S/W interrupt 1 */
+#define I_HMB_SW2	(1 << 6)	/* To Host Mail S/W interrupt 2 */
+#define I_HMB_SW3	(1 << 7)	/* To Host Mail S/W interrupt 3 */
+#define I_HMB_SW_MASK	0x000000f0	/* To Host Mail S/W interrupts mask */
+#define I_HMB_SW_SHIFT	4		/* To Host Mail S/W interrupts shift */
+#define I_WR_OOSYNC	(1 << 8)	/* Write Frame Out Of Sync */
+#define I_RD_OOSYNC	(1 << 9)	/* Read Frame Out Of Sync */
+#define	I_PC		(1 << 10)	/* descriptor error */
+#define	I_PD		(1 << 11)	/* data error */
+#define	I_DE		(1 << 12)	/* Descriptor protocol Error */
+#define	I_RU		(1 << 13)	/* Receive descriptor Underflow */
+#define	I_RO		(1 << 14)	/* Receive fifo Overflow */
+#define	I_XU		(1 << 15)	/* Transmit fifo Underflow */
+#define	I_RI		(1 << 16)	/* Receive Interrupt */
+#define I_BUSPWR	(1 << 17)	/* SDIO Bus Power Change (rev 9) */
+#define	I_XI		(1 << 24)	/* Transmit Interrupt */
+#define I_RF_TERM	(1 << 25)	/* Read Frame Terminate */
+#define I_WF_TERM	(1 << 26)	/* Write Frame Terminate */
+#define I_PCMCIA_XU	(1 << 27)	/* PCMCIA Transmit FIFO Underflow */
+#define I_SBINT		(1 << 28)	/* sbintstatus Interrupt */
+#define I_CHIPACTIVE	(1 << 29)	/* chip transitioned from doze to active state */
+#define I_SRESET	(1 << 30)	/* CCCR RES interrupt */
+#define I_IOE2		(1U << 31)	/* CCCR IOE2 Bit Changed */
+#define	I_ERRORS	(I_PC | I_PD | I_DE | I_RU | I_RO | I_XU)	/* DMA Errors */
+#define I_DMA		(I_RI | I_XI | I_ERRORS)
+
+/* sbintstatus */
+#define I_SB_SERR	(1 << 8)	/* Backplane SError (write) */
+#define I_SB_RESPERR	(1 << 9)	/* Backplane Response Error (read) */
+#define I_SB_SPROMERR	(1 << 10)	/* Error accessing the sprom */
+
+/* sdioaccess */
+#define SDA_DATA_MASK	0x000000ff	/* Read/Write Data Mask */
+#define SDA_ADDR_MASK	0x000fff00	/* Read/Write Address Mask */
+#define SDA_ADDR_SHIFT	8		/* Read/Write Address Shift */
+#define SDA_WRITE	0x01000000	/* Write bit  */
+#define SDA_READ	0x00000000	/* Write bit cleared for Read */
+#define SDA_BUSY	0x80000000	/* Busy bit */
+
+/* sdioaccess-accessible register address spaces */
+#define SDA_CCCR_SPACE		0x000	/* sdioAccess CCCR register space */
+#define SDA_F1_FBR_SPACE	0x100	/* sdioAccess F1 FBR register space */
+#define SDA_F2_FBR_SPACE	0x200	/* sdioAccess F2 FBR register space */
+#define SDA_F1_REG_SPACE	0x300	/* sdioAccess F1 core-specific register space */
+
+/* SDA_F1_REG_SPACE sdioaccess-accessible F1 reg space register offsets */
+#define SDA_CHIPCONTROLDATA	0x006	/* ChipControlData */
+#define SDA_CHIPCONTROLENAB	0x007	/* ChipControlEnable */
+#define SDA_F2WATERMARK		0x008	/* Function 2 Watermark */
+#define SDA_DEVICECONTROL	0x009	/* DeviceControl */
+#define SDA_SBADDRLOW		0x00a	/* SbAddrLow */
+#define SDA_SBADDRMID		0x00b	/* SbAddrMid */
+#define SDA_SBADDRHIGH		0x00c	/* SbAddrHigh */
+#define SDA_FRAMECTRL		0x00d	/* FrameCtrl */
+#define SDA_CHIPCLOCKCSR	0x00e	/* ChipClockCSR */
+#define SDA_SDIOPULLUP		0x00f	/* SdioPullUp */
+#define SDA_SDIOWRFRAMEBCLOW	0x019	/* SdioWrFrameBCLow */
+#define SDA_SDIOWRFRAMEBCHIGH	0x01a	/* SdioWrFrameBCHigh */
+#define SDA_SDIORDFRAMEBCLOW	0x01b	/* SdioRdFrameBCLow */
+#define SDA_SDIORDFRAMEBCHIGH	0x01c	/* SdioRdFrameBCHigh */
+
+/* SDA_F2WATERMARK */
+#define SDA_F2WATERMARK_MASK	0x7f	/* F2Watermark Mask */
+
+/* SDA_SBADDRLOW */
+#define SDA_SBADDRLOW_MASK	0x80	/* SbAddrLow Mask */
+
+/* SDA_SBADDRMID */
+#define SDA_SBADDRMID_MASK	0xff	/* SbAddrMid Mask */
+
+/* SDA_SBADDRHIGH */
+#define SDA_SBADDRHIGH_MASK	0xff	/* SbAddrHigh Mask */
+
+/* SDA_FRAMECTRL */
+#define SFC_RF_TERM	(1 << 0)	/* Read Frame Terminate */
+#define SFC_WF_TERM	(1 << 1)	/* Write Frame Terminate */
+#define SFC_CRC4WOOS	(1 << 2)	/* HW reports CRC error for write out of sync */
+#define SFC_ABORTALL	(1 << 3)	/* Abort cancels all in-progress frames */
+
+/* pcmciaframectrl */
+#define PFC_RF_TERM	(1 << 0)	/* Read Frame Terminate */
+#define PFC_WF_TERM	(1 << 1)	/* Write Frame Terminate */
+
+/* intrcvlazy */
+#define	IRL_TO_MASK	0x00ffffff	/* timeout */
+#define	IRL_FC_MASK	0xff000000	/* frame count */
+#define	IRL_FC_SHIFT	24		/* frame count */
+
+/* rx header */
+typedef volatile struct {
+	uint16 len;
+	uint16 flags;
+} sdpcmd_rxh_t;
+
+/* rx header flags */
+#define RXF_CRC		0x0001		/* CRC error detected */
+#define RXF_WOOS	0x0002		/* write frame out of sync */
+#define RXF_WF_TERM	0x0004		/* write frame terminated */
+#define RXF_ABORT	0x0008		/* write frame aborted */
+#define RXF_DISCARD	(RXF_CRC | RXF_WOOS | RXF_WF_TERM | RXF_ABORT)	/* bad frame */
+
+/* HW frame tag */
+#define SDPCM_FRAMETAG_LEN	4	/* HW frametag: 2 bytes len, 2 bytes check val */
+
+#endif	/* _sbsdpcmdev_h_ */
diff --git a/drivers/net/wireless/bcm4329/include/sbsocram.h b/drivers/net/wireless/bcm4329/include/sbsocram.h
new file mode 100644
index 0000000..5ede0b6
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/sbsocram.h
@@ -0,0 +1,150 @@
+/*
+ * BCM47XX Sonics SiliconBackplane embedded ram core
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: sbsocram.h,v 13.9.162.2 2008/12/12 14:13:27 Exp $
+ */
+
+
+#ifndef	_SBSOCRAM_H
+#define	_SBSOCRAM_H
+
+#ifndef _LANGUAGE_ASSEMBLY
+
+
+#ifndef PAD
+#define	_PADLINE(line)	pad ## line
+#define	_XSTR(line)	_PADLINE(line)
+#define	PAD		_XSTR(__LINE__)
+#endif	
+
+
+typedef volatile struct sbsocramregs {
+	uint32	coreinfo;
+	uint32	bwalloc;
+	uint32	extracoreinfo;
+	uint32	biststat;
+	uint32	bankidx;
+	uint32	standbyctrl;
+
+	uint32	errlogstatus;	
+	uint32	errlogaddr;	
+	
+	uint32	cambankidx;
+	uint32	cambankstandbyctrl;
+	uint32	cambankpatchctrl;
+	uint32	cambankpatchtblbaseaddr;
+	uint32	cambankcmdreg;
+	uint32	cambankdatareg;
+	uint32	cambankmaskreg;
+	uint32	PAD[17];
+	uint32	extmemconfig;
+	uint32	extmemparitycsr;
+	uint32	extmemparityerrdata;
+	uint32	extmemparityerrcnt;
+	uint32	extmemwrctrlandsize;
+	uint32	PAD[84];
+	uint32	workaround;
+	uint32	pwrctl;		
+} sbsocramregs_t;
+
+#endif	
+
+
+#define	SR_COREINFO		0x00
+#define	SR_BWALLOC		0x04
+#define	SR_BISTSTAT		0x0c
+#define	SR_BANKINDEX		0x10
+#define	SR_BANKSTBYCTL		0x14
+#define SR_PWRCTL		0x1e8
+
+
+#define	SRCI_PT_MASK		0x00070000	
+#define	SRCI_PT_SHIFT		16
+
+#define SRCI_PT_OCP_OCP		0
+#define SRCI_PT_AXI_OCP		1
+#define SRCI_PT_ARM7AHB_OCP	2
+#define SRCI_PT_CM3AHB_OCP	3
+#define SRCI_PT_AXI_AXI		4
+#define SRCI_PT_AHB_AXI		5
+
+#define SRCI_LSS_MASK		0x00f00000
+#define SRCI_LSS_SHIFT		20
+#define SRCI_LRS_MASK		0x0f000000
+#define SRCI_LRS_SHIFT		24
+
+
+#define	SRCI_MS0_MASK		0xf
+#define SR_MS0_BASE		16
+
+
+#define	SRCI_ROMNB_MASK		0xf000
+#define	SRCI_ROMNB_SHIFT	12
+#define	SRCI_ROMBSZ_MASK	0xf00
+#define	SRCI_ROMBSZ_SHIFT	8
+#define	SRCI_SRNB_MASK		0xf0
+#define	SRCI_SRNB_SHIFT		4
+#define	SRCI_SRBSZ_MASK		0xf
+#define	SRCI_SRBSZ_SHIFT	0
+
+#define SR_BSZ_BASE		14
+
+
+#define	SRSC_SBYOVR_MASK	0x80000000
+#define	SRSC_SBYOVR_SHIFT	31
+#define	SRSC_SBYOVRVAL_MASK	0x60000000
+#define	SRSC_SBYOVRVAL_SHIFT	29
+#define	SRSC_SBYEN_MASK		0x01000000	
+#define	SRSC_SBYEN_SHIFT	24
+
+
+#define SRPC_PMU_STBYDIS_MASK	0x00000010	
+#define SRPC_PMU_STBYDIS_SHIFT	4
+#define SRPC_STBYOVRVAL_MASK	0x00000008
+#define SRPC_STBYOVRVAL_SHIFT	3
+#define SRPC_STBYOVR_MASK	0x00000007
+#define SRPC_STBYOVR_SHIFT	0
+
+
+#define SRECC_NUM_BANKS_MASK   0x000000F0
+#define SRECC_NUM_BANKS_SHIFT  4
+#define SRECC_BANKSIZE_MASK    0x0000000F
+#define SRECC_BANKSIZE_SHIFT   0
+
+#define SRECC_BANKSIZE(value)	 (1 << (value))
+
+
+#define SRCBPC_PATCHENABLE 0x80000000
+
+#define SRP_ADDRESS   0x0001FFFC
+#define SRP_VALID     0x8000
+
+
+#define SRCMD_WRITE  0x00020000
+#define SRCMD_READ   0x00010000
+#define SRCMD_DONE   0x80000000
+
+#define SRCMD_DONE_DLY	1000
+
+
+#endif	
diff --git a/drivers/net/wireless/bcm4329/include/sdio.h b/drivers/net/wireless/bcm4329/include/sdio.h
new file mode 100644
index 0000000..280cb84
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/sdio.h
@@ -0,0 +1,566 @@
+/*
+ * SDIO spec header file
+ * Protocol and standard (common) device definitions
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: sdio.h,v 13.24.4.1.4.1.16.1 2009/08/12 01:08:02 Exp $
+ */
+
+#ifndef	_SDIO_H
+#define	_SDIO_H
+
+
+/* CCCR structure for function 0 */
+typedef volatile struct {
+	uint8	cccr_sdio_rev;		/* RO, cccr and sdio revision */
+	uint8	sd_rev;			/* RO, sd spec revision */
+	uint8	io_en;			/* I/O enable */
+	uint8	io_rdy;			/* I/O ready reg */
+	uint8	intr_ctl;		/* Master and per function interrupt enable control */
+	uint8	intr_status;		/* RO, interrupt pending status */
+	uint8	io_abort;		/* read/write abort or reset all functions */
+	uint8	bus_inter;		/* bus interface control */
+	uint8	capability;		/* RO, card capability */
+
+	uint8	cis_base_low;		/* 0x9 RO, common CIS base address, LSB */
+	uint8	cis_base_mid;
+	uint8	cis_base_high;		/* 0xB RO, common CIS base address, MSB */
+
+	/* suspend/resume registers */
+	uint8	bus_suspend;		/* 0xC */
+	uint8	func_select;		/* 0xD */
+	uint8	exec_flag;		/* 0xE */
+	uint8	ready_flag;		/* 0xF */
+
+	uint8	fn0_blk_size[2];	/* 0x10(LSB), 0x11(MSB) */
+
+	uint8	power_control;		/* 0x12 (SDIO version 1.10) */
+
+	uint8	speed_control;		/* 0x13 */
+} sdio_regs_t;
+
+/* SDIO Device CCCR offsets */
+#define SDIOD_CCCR_REV			0x00
+#define SDIOD_CCCR_SDREV		0x01
+#define SDIOD_CCCR_IOEN			0x02
+#define SDIOD_CCCR_IORDY		0x03
+#define SDIOD_CCCR_INTEN		0x04
+#define SDIOD_CCCR_INTPEND		0x05
+#define SDIOD_CCCR_IOABORT		0x06
+#define SDIOD_CCCR_BICTRL		0x07
+#define SDIOD_CCCR_CAPABLITIES		0x08
+#define SDIOD_CCCR_CISPTR_0		0x09
+#define SDIOD_CCCR_CISPTR_1		0x0A
+#define SDIOD_CCCR_CISPTR_2		0x0B
+#define SDIOD_CCCR_BUSSUSP		0x0C
+#define SDIOD_CCCR_FUNCSEL		0x0D
+#define SDIOD_CCCR_EXECFLAGS		0x0E
+#define SDIOD_CCCR_RDYFLAGS		0x0F
+#define SDIOD_CCCR_BLKSIZE_0		0x10
+#define SDIOD_CCCR_BLKSIZE_1		0x11
+#define SDIOD_CCCR_POWER_CONTROL	0x12
+#define SDIOD_CCCR_SPEED_CONTROL	0x13
+
+/* Broadcom extensions (corerev >= 1) */
+#define SDIOD_CCCR_BRCM_SEPINT		0xf2
+
+/* cccr_sdio_rev */
+#define SDIO_REV_SDIOID_MASK	0xf0	/* SDIO spec revision number */
+#define SDIO_REV_CCCRID_MASK	0x0f	/* CCCR format version number */
+
+/* sd_rev */
+#define SD_REV_PHY_MASK		0x0f	/* SD format version number */
+
+/* io_en */
+#define SDIO_FUNC_ENABLE_1	0x02	/* function 1 I/O enable */
+#define SDIO_FUNC_ENABLE_2	0x04	/* function 2 I/O enable */
+
+/* io_rdys */
+#define SDIO_FUNC_READY_1	0x02	/* function 1 I/O ready */
+#define SDIO_FUNC_READY_2	0x04	/* function 2 I/O ready */
+
+/* intr_ctl */
+#define INTR_CTL_MASTER_EN	0x1	/* interrupt enable master */
+#define INTR_CTL_FUNC1_EN	0x2	/* interrupt enable for function 1 */
+#define INTR_CTL_FUNC2_EN	0x4	/* interrupt enable for function 2 */
+
+/* intr_status */
+#define INTR_STATUS_FUNC1	0x2	/* interrupt pending for function 1 */
+#define INTR_STATUS_FUNC2	0x4	/* interrupt pending for function 2 */
+
+/* io_abort */
+#define IO_ABORT_RESET_ALL	0x08	/* I/O card reset */
+#define IO_ABORT_FUNC_MASK	0x07	/* abort selction: function x */
+
+/* bus_inter */
+#define BUS_CARD_DETECT_DIS	0x80	/* Card Detect disable */
+#define BUS_SPI_CONT_INTR_CAP	0x40	/* support continuous SPI interrupt */
+#define BUS_SPI_CONT_INTR_EN	0x20	/* continuous SPI interrupt enable */
+#define BUS_SD_DATA_WIDTH_MASK	0x03	/* bus width mask */
+#define BUS_SD_DATA_WIDTH_4BIT	0x02	/* bus width 4-bit mode */
+#define BUS_SD_DATA_WIDTH_1BIT	0x00	/* bus width 1-bit mode */
+
+/* capability */
+#define SDIO_CAP_4BLS		0x80	/* 4-bit support for low speed card */
+#define SDIO_CAP_LSC		0x40	/* low speed card */
+#define SDIO_CAP_E4MI		0x20	/* enable interrupt between block of data in 4-bit mode */
+#define SDIO_CAP_S4MI		0x10	/* support interrupt between block of data in 4-bit mode */
+#define SDIO_CAP_SBS		0x08	/* support suspend/resume */
+#define SDIO_CAP_SRW		0x04	/* support read wait */
+#define SDIO_CAP_SMB		0x02	/* support multi-block transfer */
+#define SDIO_CAP_SDC		0x01	/* Support Direct commands during multi-byte transfer */
+
+/* power_control */
+#define SDIO_POWER_SMPC		0x01	/* supports master power control (RO) */
+#define SDIO_POWER_EMPC		0x02	/* enable master power control (allow > 200mA) (RW) */
+
+/* speed_control (control device entry into high-speed clocking mode) */
+#define SDIO_SPEED_SHS		0x01	/* supports high-speed [clocking] mode (RO) */
+#define SDIO_SPEED_EHS		0x02	/* enable high-speed [clocking] mode (RW) */
+
+/* brcm sepint */
+#define SDIO_SEPINT_MASK	0x01	/* route sdpcmdev intr onto separate pad (chip-specific) */
+#define SDIO_SEPINT_OE		0x02	/* 1 asserts output enable for above pad */
+#define SDIO_SEPINT_ACT_HI	0x04	/* use active high interrupt level instead of active low */
+
+/* FBR structure for function 1-7, FBR addresses and register offsets */
+typedef volatile struct {
+	uint8	devctr;			/* device interface, CSA control */
+	uint8	ext_dev;		/* extended standard I/O device type code */
+	uint8	pwr_sel;		/* power selection support */
+	uint8	PAD[6];			/* reserved */
+
+	uint8	cis_low;		/* CIS LSB */
+	uint8	cis_mid;
+	uint8	cis_high;		/* CIS MSB */
+	uint8	csa_low;		/* code storage area, LSB */
+	uint8	csa_mid;
+	uint8	csa_high;		/* code storage area, MSB */
+	uint8	csa_dat_win;		/* data access window to function */
+
+	uint8	fnx_blk_size[2];	/* block size, little endian */
+} sdio_fbr_t;
+
+/* Maximum number of I/O funcs */
+#define SDIOD_MAX_IOFUNCS		7
+
+/* SDIO Device FBR Start Address  */
+#define SDIOD_FBR_STARTADDR		0x100
+
+/* SDIO Device FBR Size */
+#define SDIOD_FBR_SIZE			0x100
+
+/* Macro to calculate FBR register base */
+#define SDIOD_FBR_BASE(n)		((n) * 0x100)
+
+/* Function register offsets */
+#define SDIOD_FBR_DEVCTR		0x00	/* basic info for function */
+#define SDIOD_FBR_EXT_DEV		0x01	/* extended I/O device code */
+#define SDIOD_FBR_PWR_SEL		0x02	/* power selection bits */
+
+/* SDIO Function CIS ptr offset */
+#define SDIOD_FBR_CISPTR_0		0x09
+#define SDIOD_FBR_CISPTR_1		0x0A
+#define SDIOD_FBR_CISPTR_2		0x0B
+
+/* Code Storage Area pointer */
+#define SDIOD_FBR_CSA_ADDR_0		0x0C
+#define SDIOD_FBR_CSA_ADDR_1		0x0D
+#define SDIOD_FBR_CSA_ADDR_2		0x0E
+#define SDIOD_FBR_CSA_DATA		0x0F
+
+/* SDIO Function I/O Block Size */
+#define SDIOD_FBR_BLKSIZE_0		0x10
+#define SDIOD_FBR_BLKSIZE_1		0x11
+
+/* devctr */
+#define SDIOD_FBR_DEVCTR_DIC	0x0f	/* device interface code */
+#define SDIOD_FBR_DECVTR_CSA	0x40	/* CSA support flag */
+#define SDIOD_FBR_DEVCTR_CSA_EN	0x80	/* CSA enabled */
+/* interface codes */
+#define SDIOD_DIC_NONE		0	/* SDIO standard interface is not supported */
+#define SDIOD_DIC_UART		1
+#define SDIOD_DIC_BLUETOOTH_A	2
+#define SDIOD_DIC_BLUETOOTH_B	3
+#define SDIOD_DIC_GPS		4
+#define SDIOD_DIC_CAMERA	5
+#define SDIOD_DIC_PHS		6
+#define SDIOD_DIC_WLAN		7
+#define SDIOD_DIC_EXT		0xf	/* extended device interface, read ext_dev register */
+
+/* pwr_sel */
+#define SDIOD_PWR_SEL_SPS	0x01	/* supports power selection */
+#define SDIOD_PWR_SEL_EPS	0x02	/* enable power selection (low-current mode) */
+
+/* misc defines */
+#define SDIO_FUNC_0		0
+#define SDIO_FUNC_1		1
+#define SDIO_FUNC_2		2
+#define SDIO_FUNC_3		3
+#define SDIO_FUNC_4		4
+#define SDIO_FUNC_5		5
+#define SDIO_FUNC_6		6
+#define SDIO_FUNC_7		7
+
+#define SD_CARD_TYPE_UNKNOWN	0	/* bad type or unrecognized */
+#define SD_CARD_TYPE_IO		1	/* IO only card */
+#define SD_CARD_TYPE_MEMORY	2	/* memory only card */
+#define SD_CARD_TYPE_COMBO	3	/* IO and memory combo card */
+
+#define SDIO_MAX_BLOCK_SIZE	2048	/* maximum block size for block mode operation */
+#define SDIO_MIN_BLOCK_SIZE	1	/* minimum block size for block mode operation */
+
+/* Card registers: status bit position */
+#define CARDREG_STATUS_BIT_OUTOFRANGE		31
+#define CARDREG_STATUS_BIT_COMCRCERROR		23
+#define CARDREG_STATUS_BIT_ILLEGALCOMMAND	22
+#define CARDREG_STATUS_BIT_ERROR		19
+#define CARDREG_STATUS_BIT_IOCURRENTSTATE3	12
+#define CARDREG_STATUS_BIT_IOCURRENTSTATE2	11
+#define CARDREG_STATUS_BIT_IOCURRENTSTATE1	10
+#define CARDREG_STATUS_BIT_IOCURRENTSTATE0	9
+#define CARDREG_STATUS_BIT_FUN_NUM_ERROR	4
+
+
+
+#define SD_CMD_GO_IDLE_STATE		0	/* mandatory for SDIO */
+#define SD_CMD_SEND_OPCOND		1
+#define SD_CMD_MMC_SET_RCA		3
+#define SD_CMD_IO_SEND_OP_COND		5	/* mandatory for SDIO */
+#define SD_CMD_SELECT_DESELECT_CARD	7
+#define SD_CMD_SEND_CSD			9
+#define SD_CMD_SEND_CID			10
+#define SD_CMD_STOP_TRANSMISSION	12
+#define SD_CMD_SEND_STATUS		13
+#define SD_CMD_GO_INACTIVE_STATE	15
+#define SD_CMD_SET_BLOCKLEN		16
+#define SD_CMD_READ_SINGLE_BLOCK	17
+#define SD_CMD_READ_MULTIPLE_BLOCK	18
+#define SD_CMD_WRITE_BLOCK		24
+#define SD_CMD_WRITE_MULTIPLE_BLOCK	25
+#define SD_CMD_PROGRAM_CSD		27
+#define SD_CMD_SET_WRITE_PROT		28
+#define SD_CMD_CLR_WRITE_PROT		29
+#define SD_CMD_SEND_WRITE_PROT		30
+#define SD_CMD_ERASE_WR_BLK_START	32
+#define SD_CMD_ERASE_WR_BLK_END		33
+#define SD_CMD_ERASE			38
+#define SD_CMD_LOCK_UNLOCK		42
+#define SD_CMD_IO_RW_DIRECT		52	/* mandatory for SDIO */
+#define SD_CMD_IO_RW_EXTENDED		53	/* mandatory for SDIO */
+#define SD_CMD_APP_CMD			55
+#define SD_CMD_GEN_CMD			56
+#define SD_CMD_READ_OCR			58
+#define SD_CMD_CRC_ON_OFF		59	/* mandatory for SDIO */
+#define SD_ACMD_SD_STATUS		13
+#define SD_ACMD_SEND_NUM_WR_BLOCKS	22
+#define SD_ACMD_SET_WR_BLOCK_ERASE_CNT	23
+#define SD_ACMD_SD_SEND_OP_COND		41
+#define SD_ACMD_SET_CLR_CARD_DETECT	42
+#define SD_ACMD_SEND_SCR		51
+
+/* argument for SD_CMD_IO_RW_DIRECT and SD_CMD_IO_RW_EXTENDED */
+#define SD_IO_OP_READ		0   /* Read_Write: Read */
+#define SD_IO_OP_WRITE		1   /* Read_Write: Write */
+#define SD_IO_RW_NORMAL		0   /* no RAW */
+#define SD_IO_RW_RAW		1   /* RAW */
+#define SD_IO_BYTE_MODE		0   /* Byte Mode */
+#define SD_IO_BLOCK_MODE	1   /* BlockMode */
+#define SD_IO_FIXED_ADDRESS	0   /* fix Address */
+#define SD_IO_INCREMENT_ADDRESS	1   /* IncrementAddress */
+
+/* build SD_CMD_IO_RW_DIRECT Argument */
+#define SDIO_IO_RW_DIRECT_ARG(rw, raw, func, addr, data) \
+	((((rw) & 1) << 31) | (((func) & 0x7) << 28) | (((raw) & 1) << 27) | \
+	 (((addr) & 0x1FFFF) << 9) | ((data) & 0xFF))
+
+/* build SD_CMD_IO_RW_EXTENDED Argument */
+#define SDIO_IO_RW_EXTENDED_ARG(rw, blk, func, addr, inc_addr, count) \
+	((((rw) & 1) << 31) | (((func) & 0x7) << 28) | (((blk) & 1) << 27) | \
+	 (((inc_addr) & 1) << 26) | (((addr) & 0x1FFFF) << 9) | ((count) & 0x1FF))
+
+/* SDIO response parameters */
+#define SD_RSP_NO_NONE			0
+#define SD_RSP_NO_1			1
+#define SD_RSP_NO_2			2
+#define SD_RSP_NO_3			3
+#define SD_RSP_NO_4			4
+#define SD_RSP_NO_5			5
+#define SD_RSP_NO_6			6
+
+	/* Modified R6 response (to CMD3) */
+#define SD_RSP_MR6_COM_CRC_ERROR	0x8000
+#define SD_RSP_MR6_ILLEGAL_COMMAND	0x4000
+#define SD_RSP_MR6_ERROR		0x2000
+
+	/* Modified R1 in R4 Response (to CMD5) */
+#define SD_RSP_MR1_SBIT			0x80
+#define SD_RSP_MR1_PARAMETER_ERROR	0x40
+#define SD_RSP_MR1_RFU5			0x20
+#define SD_RSP_MR1_FUNC_NUM_ERROR	0x10
+#define SD_RSP_MR1_COM_CRC_ERROR	0x08
+#define SD_RSP_MR1_ILLEGAL_COMMAND	0x04
+#define SD_RSP_MR1_RFU1			0x02
+#define SD_RSP_MR1_IDLE_STATE		0x01
+
+	/* R5 response (to CMD52 and CMD53) */
+#define SD_RSP_R5_COM_CRC_ERROR		0x80
+#define SD_RSP_R5_ILLEGAL_COMMAND	0x40
+#define SD_RSP_R5_IO_CURRENTSTATE1	0x20
+#define SD_RSP_R5_IO_CURRENTSTATE0	0x10
+#define SD_RSP_R5_ERROR			0x08
+#define SD_RSP_R5_RFU			0x04
+#define SD_RSP_R5_FUNC_NUM_ERROR	0x02
+#define SD_RSP_R5_OUT_OF_RANGE		0x01
+
+#define SD_RSP_R5_ERRBITS		0xCB
+
+
+/* ------------------------------------------------
+ *  SDIO Commands and responses
+ *
+ *  I/O only commands are:
+ *      CMD0, CMD3, CMD5, CMD7, CMD15, CMD52, CMD53
+ * ------------------------------------------------
+ */
+
+/* SDIO Commands */
+#define SDIOH_CMD_0		0
+#define SDIOH_CMD_3		3
+#define SDIOH_CMD_5		5
+#define SDIOH_CMD_7		7
+#define SDIOH_CMD_15		15
+#define SDIOH_CMD_52		52
+#define SDIOH_CMD_53		53
+#define SDIOH_CMD_59		59
+
+/* SDIO Command Responses */
+#define SDIOH_RSP_NONE		0
+#define SDIOH_RSP_R1		1
+#define SDIOH_RSP_R2		2
+#define SDIOH_RSP_R3		3
+#define SDIOH_RSP_R4		4
+#define SDIOH_RSP_R5		5
+#define SDIOH_RSP_R6		6
+
+/*
+ *  SDIO Response Error flags
+ */
+#define SDIOH_RSP5_ERROR_FLAGS	0xCB
+
+/* ------------------------------------------------
+ * SDIO Command structures. I/O only commands are:
+ *
+ * 	CMD0, CMD3, CMD5, CMD7, CMD15, CMD52, CMD53
+ * ------------------------------------------------
+ */
+
+#define CMD5_OCR_M		BITFIELD_MASK(24)
+#define CMD5_OCR_S		0
+
+#define CMD7_RCA_M		BITFIELD_MASK(16)
+#define CMD7_RCA_S		16
+
+#define CMD_15_RCA_M		BITFIELD_MASK(16)
+#define CMD_15_RCA_S		16
+
+#define CMD52_DATA_M		BITFIELD_MASK(8)  /* Bits [7:0]    - Write Data/Stuff bits of CMD52
+						   */
+#define CMD52_DATA_S		0
+#define CMD52_REG_ADDR_M	BITFIELD_MASK(17) /* Bits [25:9]   - register address */
+#define CMD52_REG_ADDR_S	9
+#define CMD52_RAW_M		BITFIELD_MASK(1)  /* Bit  27       - Read after Write flag */
+#define CMD52_RAW_S		27
+#define CMD52_FUNCTION_M	BITFIELD_MASK(3)  /* Bits [30:28]  - Function number */
+#define CMD52_FUNCTION_S	28
+#define CMD52_RW_FLAG_M		BITFIELD_MASK(1)  /* Bit  31       - R/W flag */
+#define CMD52_RW_FLAG_S		31
+
+
+#define CMD53_BYTE_BLK_CNT_M	BITFIELD_MASK(9) /* Bits [8:0]     - Byte/Block Count of CMD53 */
+#define CMD53_BYTE_BLK_CNT_S	0
+#define CMD53_REG_ADDR_M	BITFIELD_MASK(17) /* Bits [25:9]   - register address */
+#define CMD53_REG_ADDR_S	9
+#define CMD53_OP_CODE_M		BITFIELD_MASK(1)  /* Bit  26       - R/W Operation Code */
+#define CMD53_OP_CODE_S		26
+#define CMD53_BLK_MODE_M	BITFIELD_MASK(1)  /* Bit  27       - Block Mode */
+#define CMD53_BLK_MODE_S	27
+#define CMD53_FUNCTION_M	BITFIELD_MASK(3)  /* Bits [30:28]  - Function number */
+#define CMD53_FUNCTION_S	28
+#define CMD53_RW_FLAG_M		BITFIELD_MASK(1)  /* Bit  31       - R/W flag */
+#define CMD53_RW_FLAG_S		31
+
+/* ------------------------------------------------------
+ * SDIO Command Response structures for SD1 and SD4 modes
+ *  -----------------------------------------------------
+ */
+#define RSP4_IO_OCR_M		BITFIELD_MASK(24) /* Bits [23:0]  - Card's OCR Bits [23:0] */
+#define RSP4_IO_OCR_S		0
+#define RSP4_STUFF_M		BITFIELD_MASK(3)  /* Bits [26:24] - Stuff bits */
+#define RSP4_STUFF_S		24
+#define RSP4_MEM_PRESENT_M	BITFIELD_MASK(1)  /* Bit  27      - Memory present */
+#define RSP4_MEM_PRESENT_S	27
+#define RSP4_NUM_FUNCS_M	BITFIELD_MASK(3)  /* Bits [30:28] - Number of I/O funcs */
+#define RSP4_NUM_FUNCS_S	28
+#define RSP4_CARD_READY_M	BITFIELD_MASK(1)  /* Bit  31      - SDIO card ready */
+#define RSP4_CARD_READY_S	31
+
+#define RSP6_STATUS_M		BITFIELD_MASK(16) /* Bits [15:0]  - Card status bits [19,22,23,12:0]
+						   */
+#define RSP6_STATUS_S		0
+#define RSP6_IO_RCA_M		BITFIELD_MASK(16) /* Bits [31:16] - RCA bits[31-16] */
+#define RSP6_IO_RCA_S		16
+
+#define RSP1_AKE_SEQ_ERROR_M	BITFIELD_MASK(1)  /* Bit 3       - Authentication seq error */
+#define RSP1_AKE_SEQ_ERROR_S	3
+#define RSP1_APP_CMD_M		BITFIELD_MASK(1)  /* Bit 5       - Card expects ACMD */
+#define RSP1_APP_CMD_S		5
+#define RSP1_READY_FOR_DATA_M	BITFIELD_MASK(1)  /* Bit 8       - Ready for data (buff empty) */
+#define RSP1_READY_FOR_DATA_S	8
+#define RSP1_CURR_STATE_M	BITFIELD_MASK(4)  /* Bits [12:9] - State of card
+						   * when Cmd was received
+						   */
+#define RSP1_CURR_STATE_S	9
+#define RSP1_EARSE_RESET_M	BITFIELD_MASK(1)  /* Bit 13   - Erase seq cleared */
+#define RSP1_EARSE_RESET_S	13
+#define RSP1_CARD_ECC_DISABLE_M	BITFIELD_MASK(1)  /* Bit 14   - Card ECC disabled */
+#define RSP1_CARD_ECC_DISABLE_S	14
+#define RSP1_WP_ERASE_SKIP_M	BITFIELD_MASK(1)  /* Bit 15   - Partial blocks erased due to W/P */
+#define RSP1_WP_ERASE_SKIP_S	15
+#define RSP1_CID_CSD_OVERW_M	BITFIELD_MASK(1)  /* Bit 16   - Illegal write to CID or R/O bits
+						   * of CSD
+						   */
+#define RSP1_CID_CSD_OVERW_S	16
+#define RSP1_ERROR_M		BITFIELD_MASK(1)  /* Bit 19   - General/Unknown error */
+#define RSP1_ERROR_S		19
+#define RSP1_CC_ERROR_M		BITFIELD_MASK(1)  /* Bit 20   - Internal Card Control error */
+#define RSP1_CC_ERROR_S		20
+#define RSP1_CARD_ECC_FAILED_M	BITFIELD_MASK(1)  /* Bit 21   - Card internal ECC failed
+						   * to correct data
+						   */
+#define RSP1_CARD_ECC_FAILED_S	21
+#define RSP1_ILLEGAL_CMD_M	BITFIELD_MASK(1)  /* Bit 22   - Cmd not legal for the card state */
+#define RSP1_ILLEGAL_CMD_S	22
+#define RSP1_COM_CRC_ERROR_M	BITFIELD_MASK(1)  /* Bit 23   - CRC check of previous command failed
+						   */
+#define RSP1_COM_CRC_ERROR_S	23
+#define RSP1_LOCK_UNLOCK_FAIL_M	BITFIELD_MASK(1)  /* Bit 24   - Card lock-unlock Cmd Seq error */
+#define RSP1_LOCK_UNLOCK_FAIL_S	24
+#define RSP1_CARD_LOCKED_M	BITFIELD_MASK(1)  /* Bit 25   - Card locked by the host */
+#define RSP1_CARD_LOCKED_S	25
+#define RSP1_WP_VIOLATION_M	BITFIELD_MASK(1)  /* Bit 26   - Attempt to program
+						   * write-protected blocks
+						   */
+#define RSP1_WP_VIOLATION_S	26
+#define RSP1_ERASE_PARAM_M	BITFIELD_MASK(1)  /* Bit 27   - Invalid erase blocks */
+#define RSP1_ERASE_PARAM_S	27
+#define RSP1_ERASE_SEQ_ERR_M	BITFIELD_MASK(1)  /* Bit 28   - Erase Cmd seq error */
+#define RSP1_ERASE_SEQ_ERR_S	28
+#define RSP1_BLK_LEN_ERR_M	BITFIELD_MASK(1)  /* Bit 29   - Block length error */
+#define RSP1_BLK_LEN_ERR_S	29
+#define RSP1_ADDR_ERR_M		BITFIELD_MASK(1)  /* Bit 30   - Misaligned address */
+#define RSP1_ADDR_ERR_S		30
+#define RSP1_OUT_OF_RANGE_M	BITFIELD_MASK(1)  /* Bit 31   - Cmd arg was out of range */
+#define RSP1_OUT_OF_RANGE_S	31
+
+
+#define RSP5_DATA_M		BITFIELD_MASK(8)  /* Bits [0:7]   - data */
+#define RSP5_DATA_S		0
+#define RSP5_FLAGS_M		BITFIELD_MASK(8)  /* Bit  [15:8]  - Rsp flags */
+#define RSP5_FLAGS_S		8
+#define RSP5_STUFF_M		BITFIELD_MASK(16) /* Bits [31:16] - Stuff bits */
+#define RSP5_STUFF_S		16
+
+/* ----------------------------------------------
+ * SDIO Command Response structures for SPI mode
+ * ----------------------------------------------
+ */
+#define SPIRSP4_IO_OCR_M	BITFIELD_MASK(16) /* Bits [15:0]    - Card's OCR Bits [23:8] */
+#define SPIRSP4_IO_OCR_S	0
+#define SPIRSP4_STUFF_M		BITFIELD_MASK(3)  /* Bits [18:16]   - Stuff bits */
+#define SPIRSP4_STUFF_S		16
+#define SPIRSP4_MEM_PRESENT_M	BITFIELD_MASK(1)  /* Bit  19        - Memory present */
+#define SPIRSP4_MEM_PRESENT_S	19
+#define SPIRSP4_NUM_FUNCS_M	BITFIELD_MASK(3)  /* Bits [22:20]   - Number of I/O funcs */
+#define SPIRSP4_NUM_FUNCS_S	20
+#define SPIRSP4_CARD_READY_M	BITFIELD_MASK(1)  /* Bit  23        - SDIO card ready */
+#define SPIRSP4_CARD_READY_S	23
+#define SPIRSP4_IDLE_STATE_M	BITFIELD_MASK(1)  /* Bit  24        - idle state */
+#define SPIRSP4_IDLE_STATE_S	24
+#define SPIRSP4_ILLEGAL_CMD_M	BITFIELD_MASK(1)  /* Bit  26        - Illegal Cmd error */
+#define SPIRSP4_ILLEGAL_CMD_S	26
+#define SPIRSP4_COM_CRC_ERROR_M	BITFIELD_MASK(1)  /* Bit  27        - COM CRC error */
+#define SPIRSP4_COM_CRC_ERROR_S	27
+#define SPIRSP4_FUNC_NUM_ERROR_M	BITFIELD_MASK(1)  /* Bit  28        - Function number error
+							   */
+#define SPIRSP4_FUNC_NUM_ERROR_S	28
+#define SPIRSP4_PARAM_ERROR_M	BITFIELD_MASK(1)  /* Bit  30        - Parameter Error Bit */
+#define SPIRSP4_PARAM_ERROR_S	30
+#define SPIRSP4_START_BIT_M	BITFIELD_MASK(1)  /* Bit  31        - Start Bit */
+#define SPIRSP4_START_BIT_S	31
+
+#define SPIRSP5_DATA_M			BITFIELD_MASK(8)  /* Bits [23:16]   - R/W Data */
+#define SPIRSP5_DATA_S			16
+#define SPIRSP5_IDLE_STATE_M		BITFIELD_MASK(1)  /* Bit  24        - Idle state */
+#define SPIRSP5_IDLE_STATE_S		24
+#define SPIRSP5_ILLEGAL_CMD_M		BITFIELD_MASK(1)  /* Bit  26        - Illegal Cmd error */
+#define SPIRSP5_ILLEGAL_CMD_S		26
+#define SPIRSP5_COM_CRC_ERROR_M		BITFIELD_MASK(1)  /* Bit  27        - COM CRC error */
+#define SPIRSP5_COM_CRC_ERROR_S		27
+#define SPIRSP5_FUNC_NUM_ERROR_M	BITFIELD_MASK(1)  /* Bit  28        - Function number error
+							   */
+#define SPIRSP5_FUNC_NUM_ERROR_S	28
+#define SPIRSP5_PARAM_ERROR_M		BITFIELD_MASK(1)  /* Bit  30        - Parameter Error Bit */
+#define SPIRSP5_PARAM_ERROR_S		30
+#define SPIRSP5_START_BIT_M		BITFIELD_MASK(1)  /* Bit  31        - Start Bit */
+#define SPIRSP5_START_BIT_S		31
+
+/* RSP6 card status format; Pg 68 Physical Layer spec v 1.10 */
+#define RSP6STAT_AKE_SEQ_ERROR_M	BITFIELD_MASK(1)  /* Bit 3	- Authentication seq error
+							   */
+#define RSP6STAT_AKE_SEQ_ERROR_S	3
+#define RSP6STAT_APP_CMD_M		BITFIELD_MASK(1)  /* Bit 5	- Card expects ACMD */
+#define RSP6STAT_APP_CMD_S		5
+#define RSP6STAT_READY_FOR_DATA_M	BITFIELD_MASK(1)  /* Bit 8	- Ready for data
+							   * (buff empty)
+							   */
+#define RSP6STAT_READY_FOR_DATA_S	8
+#define RSP6STAT_CURR_STATE_M		BITFIELD_MASK(4)  /* Bits [12:9] - Card state at
+							   * Cmd reception
+							   */
+#define RSP6STAT_CURR_STATE_S		9
+#define RSP6STAT_ERROR_M		BITFIELD_MASK(1)  /* Bit 13  - General/Unknown error Bit 19
+							   */
+#define RSP6STAT_ERROR_S		13
+#define RSP6STAT_ILLEGAL_CMD_M		BITFIELD_MASK(1)  /* Bit 14  - Illegal cmd for
+							   * card state Bit 22
+							   */
+#define RSP6STAT_ILLEGAL_CMD_S		14
+#define RSP6STAT_COM_CRC_ERROR_M	BITFIELD_MASK(1)  /* Bit 15  - CRC previous command
+							   * failed Bit 23
+							   */
+#define RSP6STAT_COM_CRC_ERROR_S	15
+
+#define SDIOH_XFER_TYPE_READ    SD_IO_OP_READ
+#define SDIOH_XFER_TYPE_WRITE   SD_IO_OP_WRITE
+
+#endif /* _SDIO_H */
diff --git a/drivers/net/wireless/bcm4329/include/sdioh.h b/drivers/net/wireless/bcm4329/include/sdioh.h
new file mode 100644
index 0000000..8123452
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/sdioh.h
@@ -0,0 +1,299 @@
+/*
+ * SDIO Host Controller Spec header file
+ * Register map and definitions for the Standard Host Controller
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: sdioh.h,v 13.13.18.1.16.3 2009/12/08 22:34:21 Exp $
+ */
+
+#ifndef	_SDIOH_H
+#define	_SDIOH_H
+
+#define SD_SysAddr			0x000
+#define SD_BlockSize			0x004
+#define SD_BlockCount 			0x006
+#define SD_Arg0				0x008
+#define SD_Arg1 			0x00A
+#define SD_TransferMode			0x00C
+#define SD_Command 			0x00E
+#define SD_Response0			0x010
+#define SD_Response1 			0x012
+#define SD_Response2			0x014
+#define SD_Response3 			0x016
+#define SD_Response4			0x018
+#define SD_Response5 			0x01A
+#define SD_Response6			0x01C
+#define SD_Response7 			0x01E
+#define SD_BufferDataPort0		0x020
+#define SD_BufferDataPort1 		0x022
+#define SD_PresentState			0x024
+#define SD_HostCntrl			0x028
+#define SD_PwrCntrl			0x029
+#define SD_BlockGapCntrl 		0x02A
+#define SD_WakeupCntrl 			0x02B
+#define SD_ClockCntrl			0x02C
+#define SD_TimeoutCntrl 		0x02E
+#define SD_SoftwareReset		0x02F
+#define SD_IntrStatus			0x030
+#define SD_ErrorIntrStatus 		0x032
+#define SD_IntrStatusEnable		0x034
+#define SD_ErrorIntrStatusEnable 	0x036
+#define SD_IntrSignalEnable		0x038
+#define SD_ErrorIntrSignalEnable 	0x03A
+#define SD_CMD12ErrorStatus		0x03C
+#define SD_Capabilities			0x040
+#define SD_Capabilities_Reserved	0x044
+#define SD_MaxCurCap			0x048
+#define SD_MaxCurCap_Reserved		0x04C
+#define SD_ADMA_SysAddr			0x58
+#define SD_SlotInterruptStatus		0x0FC
+#define SD_HostControllerVersion 	0x0FE
+
+/* SD specific registers in PCI config space */
+#define SD_SlotInfo	0x40
+
+/* SD_Capabilities reg (0x040) */
+#define CAP_TO_CLKFREQ_M 	BITFIELD_MASK(6)
+#define CAP_TO_CLKFREQ_S 	0
+#define CAP_TO_CLKUNIT_M  	BITFIELD_MASK(1)
+#define CAP_TO_CLKUNIT_S 	7
+#define CAP_BASECLK_M 		BITFIELD_MASK(6)
+#define CAP_BASECLK_S 		8
+#define CAP_MAXBLOCK_M 		BITFIELD_MASK(2)
+#define CAP_MAXBLOCK_S		16
+#define CAP_ADMA2_M		BITFIELD_MASK(1)
+#define CAP_ADMA2_S		19
+#define CAP_ADMA1_M		BITFIELD_MASK(1)
+#define CAP_ADMA1_S		20
+#define CAP_HIGHSPEED_M		BITFIELD_MASK(1)
+#define CAP_HIGHSPEED_S		21
+#define CAP_DMA_M		BITFIELD_MASK(1)
+#define CAP_DMA_S		22
+#define CAP_SUSPEND_M		BITFIELD_MASK(1)
+#define CAP_SUSPEND_S		23
+#define CAP_VOLT_3_3_M		BITFIELD_MASK(1)
+#define CAP_VOLT_3_3_S		24
+#define CAP_VOLT_3_0_M		BITFIELD_MASK(1)
+#define CAP_VOLT_3_0_S		25
+#define CAP_VOLT_1_8_M		BITFIELD_MASK(1)
+#define CAP_VOLT_1_8_S		26
+#define CAP_64BIT_HOST_M	BITFIELD_MASK(1)
+#define CAP_64BIT_HOST_S	28
+
+/* SD_MaxCurCap reg (0x048) */
+#define CAP_CURR_3_3_M		BITFIELD_MASK(8)
+#define CAP_CURR_3_3_S		0
+#define CAP_CURR_3_0_M		BITFIELD_MASK(8)
+#define CAP_CURR_3_0_S		8
+#define CAP_CURR_1_8_M		BITFIELD_MASK(8)
+#define CAP_CURR_1_8_S		16
+
+/* SD_SysAddr: Offset 0x0000, Size 4 bytes */
+
+/* SD_BlockSize: Offset 0x004, Size 2 bytes */
+#define BLKSZ_BLKSZ_M		BITFIELD_MASK(12)
+#define BLKSZ_BLKSZ_S		0
+#define BLKSZ_BNDRY_M		BITFIELD_MASK(3)
+#define BLKSZ_BNDRY_S		12
+
+/* SD_BlockCount: Offset 0x006, size 2 bytes */
+
+/* SD_Arg0: Offset 0x008, size = 4 bytes  */
+/* SD_TransferMode Offset 0x00C, size = 2 bytes */
+#define XFER_DMA_ENABLE_M   	BITFIELD_MASK(1)
+#define XFER_DMA_ENABLE_S	0
+#define XFER_BLK_COUNT_EN_M 	BITFIELD_MASK(1)
+#define XFER_BLK_COUNT_EN_S	1
+#define XFER_CMD_12_EN_M    	BITFIELD_MASK(1)
+#define XFER_CMD_12_EN_S 	2
+#define XFER_DATA_DIRECTION_M	BITFIELD_MASK(1)
+#define XFER_DATA_DIRECTION_S	4
+#define XFER_MULTI_BLOCK_M	BITFIELD_MASK(1)
+#define XFER_MULTI_BLOCK_S	5
+
+/* SD_Command: Offset 0x00E, size = 2 bytes */
+/* resp_type field */
+#define RESP_TYPE_NONE 		0
+#define RESP_TYPE_136  		1
+#define RESP_TYPE_48   		2
+#define RESP_TYPE_48_BUSY	3
+/* type field */
+#define CMD_TYPE_NORMAL		0
+#define CMD_TYPE_SUSPEND	1
+#define CMD_TYPE_RESUME		2
+#define CMD_TYPE_ABORT		3
+
+#define CMD_RESP_TYPE_M		BITFIELD_MASK(2)	/* Bits [0-1] 	- Response type */
+#define CMD_RESP_TYPE_S		0
+#define CMD_CRC_EN_M		BITFIELD_MASK(1)	/* Bit 3 	- CRC enable */
+#define CMD_CRC_EN_S		3
+#define CMD_INDEX_EN_M		BITFIELD_MASK(1)	/* Bit 4 	- Enable index checking */
+#define CMD_INDEX_EN_S		4
+#define CMD_DATA_EN_M		BITFIELD_MASK(1)	/* Bit 5 	- Using DAT line */
+#define CMD_DATA_EN_S		5
+#define CMD_TYPE_M		BITFIELD_MASK(2)	/* Bit [6-7] 	- Normal, abort, resume, etc
+							 */
+#define CMD_TYPE_S		6
+#define CMD_INDEX_M		BITFIELD_MASK(6)	/* Bits [8-13] 	- Command number */
+#define CMD_INDEX_S		8
+
+/* SD_BufferDataPort0	: Offset 0x020, size = 2 or 4 bytes */
+/* SD_BufferDataPort1 	: Offset 0x022, size = 2 bytes */
+/* SD_PresentState	: Offset 0x024, size = 4 bytes */
+#define PRES_CMD_INHIBIT_M	BITFIELD_MASK(1)	/* Bit 0	May use CMD */
+#define PRES_CMD_INHIBIT_S	0
+#define PRES_DAT_INHIBIT_M	BITFIELD_MASK(1)	/* Bit 1	May use DAT */
+#define PRES_DAT_INHIBIT_S	1
+#define PRES_DAT_BUSY_M		BITFIELD_MASK(1)	/* Bit 2	DAT is busy */
+#define PRES_DAT_BUSY_S		2
+#define PRES_PRESENT_RSVD_M	BITFIELD_MASK(5)	/* Bit [3-7]	rsvd */
+#define PRES_PRESENT_RSVD_S	3
+#define PRES_WRITE_ACTIVE_M	BITFIELD_MASK(1)	/* Bit 8	Write is active */
+#define PRES_WRITE_ACTIVE_S	8
+#define PRES_READ_ACTIVE_M	BITFIELD_MASK(1)	/* Bit 9	Read is active */
+#define PRES_READ_ACTIVE_S	9
+#define PRES_WRITE_DATA_RDY_M	BITFIELD_MASK(1)	/* Bit 10	Write buf is avail */
+#define PRES_WRITE_DATA_RDY_S	10
+#define PRES_READ_DATA_RDY_M	BITFIELD_MASK(1)	/* Bit 11	Read buf data avail */
+#define PRES_READ_DATA_RDY_S	11
+#define PRES_CARD_PRESENT_M	BITFIELD_MASK(1)	/* Bit 16	Card present - debounced */
+#define PRES_CARD_PRESENT_S	16
+#define PRES_CARD_STABLE_M	BITFIELD_MASK(1)	/* Bit 17	Debugging */
+#define PRES_CARD_STABLE_S	17
+#define PRES_CARD_PRESENT_RAW_M	BITFIELD_MASK(1)	/* Bit 18	Not debounced */
+#define PRES_CARD_PRESENT_RAW_S	18
+#define PRES_WRITE_ENABLED_M	BITFIELD_MASK(1)	/* Bit 19	Write protected? */
+#define PRES_WRITE_ENABLED_S	19
+#define PRES_DAT_SIGNAL_M	BITFIELD_MASK(4)	/* Bit [20-23]	Debugging */
+#define PRES_DAT_SIGNAL_S	20
+#define PRES_CMD_SIGNAL_M	BITFIELD_MASK(1)	/* Bit 24	Debugging */
+#define PRES_CMD_SIGNAL_S	24
+
+/* SD_HostCntrl: Offset 0x028, size = 1 bytes */
+#define HOST_LED_M		BITFIELD_MASK(1)	/* Bit 0	LED On/Off */
+#define HOST_LED_S		0
+#define HOST_DATA_WIDTH_M	BITFIELD_MASK(1)	/* Bit 1	4 bit enable */
+#define HOST_DATA_WIDTH_S	1
+#define HOST_HI_SPEED_EN_M	BITFIELD_MASK(1)	/* Bit 2	High speed vs low speed */
+#define HOST_DMA_SEL_S		3
+#define HOST_DMA_SEL_M		BITFIELD_MASK(2)	/* Bit 4:3	DMA Select */
+#define HOST_HI_SPEED_EN_S	2
+
+/* misc defines */
+#define SD1_MODE 		0x1	/* SD Host Cntrlr Spec */
+#define SD4_MODE 		0x2	/* SD Host Cntrlr Spec */
+
+/* SD_PwrCntrl: Offset 0x029, size = 1 bytes */
+#define PWR_BUS_EN_M		BITFIELD_MASK(1)	/* Bit 0	Power the bus */
+#define PWR_BUS_EN_S		0
+#define PWR_VOLTS_M		BITFIELD_MASK(3)	/* Bit [1-3]	Voltage Select */
+#define PWR_VOLTS_S		1
+
+/* SD_SoftwareReset: Offset 0x02F, size = 1 byte */
+#define SW_RESET_ALL_M		BITFIELD_MASK(1)	/* Bit 0	Reset All */
+#define SW_RESET_ALL_S		0
+#define SW_RESET_CMD_M		BITFIELD_MASK(1)	/* Bit 1	CMD Line Reset */
+#define SW_RESET_CMD_S		1
+#define SW_RESET_DAT_M		BITFIELD_MASK(1)	/* Bit 2	DAT Line Reset */
+#define SW_RESET_DAT_S		2
+
+/* SD_IntrStatus: Offset 0x030, size = 2 bytes */
+/* Defs also serve SD_IntrStatusEnable and SD_IntrSignalEnable */
+#define INTSTAT_CMD_COMPLETE_M		BITFIELD_MASK(1)	/* Bit 0 */
+#define INTSTAT_CMD_COMPLETE_S		0
+#define INTSTAT_XFER_COMPLETE_M		BITFIELD_MASK(1)
+#define INTSTAT_XFER_COMPLETE_S		1
+#define INTSTAT_BLOCK_GAP_EVENT_M	BITFIELD_MASK(1)
+#define INTSTAT_BLOCK_GAP_EVENT_S	2
+#define INTSTAT_DMA_INT_M		BITFIELD_MASK(1)
+#define INTSTAT_DMA_INT_S		3
+#define INTSTAT_BUF_WRITE_READY_M	BITFIELD_MASK(1)
+#define INTSTAT_BUF_WRITE_READY_S	4
+#define INTSTAT_BUF_READ_READY_M	BITFIELD_MASK(1)
+#define INTSTAT_BUF_READ_READY_S	5
+#define INTSTAT_CARD_INSERTION_M	BITFIELD_MASK(1)
+#define INTSTAT_CARD_INSERTION_S	6
+#define INTSTAT_CARD_REMOVAL_M		BITFIELD_MASK(1)
+#define INTSTAT_CARD_REMOVAL_S		7
+#define INTSTAT_CARD_INT_M		BITFIELD_MASK(1)
+#define INTSTAT_CARD_INT_S		8
+#define INTSTAT_ERROR_INT_M		BITFIELD_MASK(1)	/* Bit 15 */
+#define INTSTAT_ERROR_INT_S		15
+
+/* SD_ErrorIntrStatus: Offset 0x032, size = 2 bytes */
+/* Defs also serve SD_ErrorIntrStatusEnable and SD_ErrorIntrSignalEnable */
+#define ERRINT_CMD_TIMEOUT_M		BITFIELD_MASK(1)
+#define ERRINT_CMD_TIMEOUT_S		0
+#define ERRINT_CMD_CRC_M		BITFIELD_MASK(1)
+#define ERRINT_CMD_CRC_S		1
+#define ERRINT_CMD_ENDBIT_M		BITFIELD_MASK(1)
+#define ERRINT_CMD_ENDBIT_S		2
+#define ERRINT_CMD_INDEX_M		BITFIELD_MASK(1)
+#define ERRINT_CMD_INDEX_S		3
+#define ERRINT_DATA_TIMEOUT_M		BITFIELD_MASK(1)
+#define ERRINT_DATA_TIMEOUT_S		4
+#define ERRINT_DATA_CRC_M		BITFIELD_MASK(1)
+#define ERRINT_DATA_CRC_S		5
+#define ERRINT_DATA_ENDBIT_M		BITFIELD_MASK(1)
+#define ERRINT_DATA_ENDBIT_S		6
+#define ERRINT_CURRENT_LIMIT_M		BITFIELD_MASK(1)
+#define ERRINT_CURRENT_LIMIT_S		7
+#define ERRINT_AUTO_CMD12_M		BITFIELD_MASK(1)
+#define ERRINT_AUTO_CMD12_S		8
+#define ERRINT_VENDOR_M			BITFIELD_MASK(4)
+#define ERRINT_VENDOR_S			12
+
+/* Also provide definitions in "normal" form to allow combined masks */
+#define ERRINT_CMD_TIMEOUT_BIT		0x0001
+#define ERRINT_CMD_CRC_BIT		0x0002
+#define ERRINT_CMD_ENDBIT_BIT		0x0004
+#define ERRINT_CMD_INDEX_BIT		0x0008
+#define ERRINT_DATA_TIMEOUT_BIT		0x0010
+#define ERRINT_DATA_CRC_BIT		0x0020
+#define ERRINT_DATA_ENDBIT_BIT		0x0040
+#define ERRINT_CURRENT_LIMIT_BIT	0x0080
+#define ERRINT_AUTO_CMD12_BIT		0x0100
+
+/* Masks to select CMD vs. DATA errors */
+#define ERRINT_CMD_ERRS		(ERRINT_CMD_TIMEOUT_BIT | ERRINT_CMD_CRC_BIT |\
+				 ERRINT_CMD_ENDBIT_BIT | ERRINT_CMD_INDEX_BIT)
+#define ERRINT_DATA_ERRS	(ERRINT_DATA_TIMEOUT_BIT | ERRINT_DATA_CRC_BIT |\
+				 ERRINT_DATA_ENDBIT_BIT)
+#define ERRINT_TRANSFER_ERRS	(ERRINT_CMD_ERRS | ERRINT_DATA_ERRS)
+
+/* SD_WakeupCntr_BlockGapCntrl : Offset 0x02A , size = bytes */
+/* SD_ClockCntrl	: Offset 0x02C , size = bytes */
+/* SD_SoftwareReset_TimeoutCntrl 	: Offset 0x02E , size = bytes */
+/* SD_IntrStatus	: Offset 0x030 , size = bytes */
+/* SD_ErrorIntrStatus 	: Offset 0x032 , size = bytes */
+/* SD_IntrStatusEnable	: Offset 0x034 , size = bytes */
+/* SD_ErrorIntrStatusEnable : Offset 0x036 , size = bytes */
+/* SD_IntrSignalEnable	: Offset 0x038 , size = bytes */
+/* SD_ErrorIntrSignalEnable : Offset 0x03A , size = bytes */
+/* SD_CMD12ErrorStatus	: Offset 0x03C , size = bytes */
+/* SD_Capabilities	: Offset 0x040 , size = bytes */
+/* SD_MaxCurCap		: Offset 0x048 , size = bytes */
+/* SD_MaxCurCap_Reserved: Offset 0x04C , size = bytes */
+/* SD_SlotInterruptStatus: Offset 0x0FC , size = bytes */
+/* SD_HostControllerVersion : Offset 0x0FE , size = bytes */
+
+#endif /* _SDIOH_H */
diff --git a/drivers/net/wireless/bcm4329/include/sdiovar.h b/drivers/net/wireless/bcm4329/include/sdiovar.h
new file mode 100644
index 0000000..0179d4c
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/sdiovar.h
@@ -0,0 +1,58 @@
+/*
+ * Structure used by apps whose drivers access SDIO drivers.
+ * Pulled out separately so dhdu and wlu can both use it.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: sdiovar.h,v 13.5.14.2.16.2 2009/12/08 22:34:21 Exp $
+ */
+
+#ifndef _sdiovar_h_
+#define _sdiovar_h_
+
+#include <typedefs.h>
+
+/* require default structure packing */
+#define BWL_DEFAULT_PACKING
+#include <packed_section_start.h>
+
+typedef struct sdreg {
+	int func;
+	int offset;
+	int value;
+} sdreg_t;
+
+/* Common msglevel constants */
+#define SDH_ERROR_VAL		0x0001	/* Error */
+#define SDH_TRACE_VAL		0x0002	/* Trace */
+#define SDH_INFO_VAL		0x0004	/* Info */
+#define SDH_DEBUG_VAL		0x0008	/* Debug */
+#define SDH_DATA_VAL		0x0010	/* Data */
+#define SDH_CTRL_VAL		0x0020	/* Control Regs */
+#define SDH_LOG_VAL		0x0040	/* Enable bcmlog */
+#define SDH_DMA_VAL		0x0080	/* DMA */
+
+#define NUM_PREV_TRANSACTIONS	16
+
+
+#include <packed_section_end.h>
+
+#endif /* _sdiovar_h_ */
diff --git a/drivers/net/wireless/bcm4329/include/siutils.h b/drivers/net/wireless/bcm4329/include/siutils.h
new file mode 100644
index 0000000..cb9f140
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/siutils.h
@@ -0,0 +1,235 @@
+/*
+ * Misc utility routines for accessing the SOC Interconnects
+ * of Broadcom HNBU chips.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: siutils.h,v 13.197.4.2.4.3.8.16 2010/06/23 21:36:05 Exp $
+ */
+
+
+#ifndef	_siutils_h_
+#define	_siutils_h_
+
+
+struct si_pub {
+	uint	socitype;		
+
+	uint	bustype;		
+	uint	buscoretype;		
+	uint	buscorerev;		
+	uint	buscoreidx;		
+	int	ccrev;			
+	uint32	cccaps;			
+	int	pmurev;			
+	uint32	pmucaps;		
+	uint	boardtype;		
+	uint	boardvendor;		
+	uint	boardflags;		
+	uint	chip;			
+	uint	chiprev;		
+	uint	chippkg;		
+	uint32	chipst;			
+	bool	issim;			
+	uint    socirev;		
+	bool	pci_pr32414;
+};
+
+#if defined(WLC_HIGH) && !defined(WLC_LOW)
+typedef struct si_pub si_t;
+#else
+typedef const struct si_pub si_t;
+#endif
+
+
+#define	SI_OSH		NULL	
+
+
+#define	XTAL			0x1	
+#define	PLL			0x2	
+
+
+#define	CLK_FAST		0	
+#define	CLK_DYNAMIC		2	
+
+
+#define GPIO_DRV_PRIORITY	0	
+#define GPIO_APP_PRIORITY	1	
+#define GPIO_HI_PRIORITY	2	
+
+
+#define GPIO_PULLUP		0
+#define GPIO_PULLDN		1
+
+
+#define GPIO_REGEVT		0	
+#define GPIO_REGEVT_INTMSK	1	
+#define GPIO_REGEVT_INTPOL	2	
+
+
+#define SI_DEVPATH_BUFSZ	16	
+
+
+#define	SI_DOATTACH	1
+#define SI_PCIDOWN	2
+#define SI_PCIUP	3
+
+#define	ISSIM_ENAB(sih)	0
+
+
+#if defined(BCMPMUCTL)
+#define PMUCTL_ENAB(sih)	(BCMPMUCTL)
+#else
+#define PMUCTL_ENAB(sih)	((sih)->cccaps & CC_CAP_PMU)
+#endif
+
+
+#if defined(BCMPMUCTL) && BCMPMUCTL
+#define CCCTL_ENAB(sih)		(0)
+#define CCPLL_ENAB(sih)		(0)
+#else
+#define CCCTL_ENAB(sih)		((sih)->cccaps & CC_CAP_PWR_CTL)
+#define CCPLL_ENAB(sih)		((sih)->cccaps & CC_CAP_PLL_MASK)
+#endif
+
+typedef void (*gpio_handler_t)(uint32 stat, void *arg);
+
+
+
+extern si_t *si_attach(uint pcidev, osl_t *osh, void *regs, uint bustype,
+                       void *sdh, char **vars, uint *varsz);
+extern si_t *si_kattach(osl_t *osh);
+extern void si_detach(si_t *sih);
+extern bool si_pci_war16165(si_t *sih);
+
+extern uint si_corelist(si_t *sih, uint coreid[]);
+extern uint si_coreid(si_t *sih);
+extern uint si_flag(si_t *sih);
+extern uint si_intflag(si_t *sih);
+extern uint si_coreidx(si_t *sih);
+extern uint si_coreunit(si_t *sih);
+extern uint si_corevendor(si_t *sih);
+extern uint si_corerev(si_t *sih);
+extern void *si_osh(si_t *sih);
+extern void si_setosh(si_t *sih, osl_t *osh);
+extern uint si_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val);
+extern void *si_coreregs(si_t *sih);
+extern void si_write_wrapperreg(si_t *sih, uint32 offset, uint32 val);
+extern uint32 si_core_cflags(si_t *sih, uint32 mask, uint32 val);
+extern void si_core_cflags_wo(si_t *sih, uint32 mask, uint32 val);
+extern uint32 si_core_sflags(si_t *sih, uint32 mask, uint32 val);
+extern bool si_iscoreup(si_t *sih);
+extern uint si_findcoreidx(si_t *sih, uint coreid, uint coreunit);
+extern void *si_setcoreidx(si_t *sih, uint coreidx);
+extern void *si_setcore(si_t *sih, uint coreid, uint coreunit);
+extern void *si_switch_core(si_t *sih, uint coreid, uint *origidx, uint *intr_val);
+extern void si_restore_core(si_t *sih, uint coreid, uint intr_val);
+extern int si_numaddrspaces(si_t *sih);
+extern uint32 si_addrspace(si_t *sih, uint asidx);
+extern uint32 si_addrspacesize(si_t *sih, uint asidx);
+extern int si_corebist(si_t *sih);
+extern void si_core_reset(si_t *sih, uint32 bits, uint32 resetbits);
+extern void si_core_tofixup(si_t *sih);
+extern void si_core_disable(si_t *sih, uint32 bits);
+extern uint32 si_clock_rate(uint32 pll_type, uint32 n, uint32 m);
+extern uint32 si_clock(si_t *sih);
+extern void si_clock_pmu_spuravoid(si_t *sih, bool spuravoid);
+extern uint32 si_alp_clock(si_t *sih);
+extern uint32 si_ilp_clock(si_t *sih);
+extern void si_pci_setup(si_t *sih, uint coremask);
+extern void si_pcmcia_init(si_t *sih);
+extern void si_setint(si_t *sih, int siflag);
+extern bool si_backplane64(si_t *sih);
+extern void si_register_intr_callback(si_t *sih, void *intrsoff_fn, void *intrsrestore_fn,
+	void *intrsenabled_fn, void *intr_arg);
+extern void si_deregister_intr_callback(si_t *sih);
+extern void si_clkctl_init(si_t *sih);
+extern uint16 si_clkctl_fast_pwrup_delay(si_t *sih);
+extern bool si_clkctl_cc(si_t *sih, uint mode);
+extern int si_clkctl_xtal(si_t *sih, uint what, bool on);
+extern uint32 si_gpiotimerval(si_t *sih, uint32 mask, uint32 val);
+extern bool si_backplane64(si_t *sih);
+extern void si_btcgpiowar(si_t *sih);
+extern bool si_deviceremoved(si_t *sih);
+extern uint32 si_socram_size(si_t *sih);
+
+extern void si_watchdog(si_t *sih, uint ticks);
+extern void si_watchdog_ms(si_t *sih, uint32 ms);
+extern void *si_gpiosetcore(si_t *sih);
+extern uint32 si_gpiocontrol(si_t *sih, uint32 mask, uint32 val, uint8 priority);
+extern uint32 si_gpioouten(si_t *sih, uint32 mask, uint32 val, uint8 priority);
+extern uint32 si_gpioout(si_t *sih, uint32 mask, uint32 val, uint8 priority);
+extern uint32 si_gpioin(si_t *sih);
+extern uint32 si_gpiointpolarity(si_t *sih, uint32 mask, uint32 val, uint8 priority);
+extern uint32 si_gpiointmask(si_t *sih, uint32 mask, uint32 val, uint8 priority);
+extern uint32 si_gpioled(si_t *sih, uint32 mask, uint32 val);
+extern uint32 si_gpioreserve(si_t *sih, uint32 gpio_num, uint8 priority);
+extern uint32 si_gpiorelease(si_t *sih, uint32 gpio_num, uint8 priority);
+extern uint32 si_gpiopull(si_t *sih, bool updown, uint32 mask, uint32 val);
+extern uint32 si_gpioevent(si_t *sih, uint regtype, uint32 mask, uint32 val);
+extern uint32 si_gpio_int_enable(si_t *sih, bool enable);
+
+
+extern void *si_gpio_handler_register(si_t *sih, uint32 e, bool lev, gpio_handler_t cb, void *arg);
+extern void si_gpio_handler_unregister(si_t *sih, void* gpioh);
+extern void si_gpio_handler_process(si_t *sih);
+
+
+extern bool si_pci_pmecap(si_t *sih);
+struct osl_info;
+extern bool si_pci_fastpmecap(struct osl_info *osh);
+extern bool si_pci_pmeclr(si_t *sih);
+extern void si_pci_pmeen(si_t *sih);
+extern uint si_pcie_readreg(void *sih, uint addrtype, uint offset);
+
+extern void si_sdio_init(si_t *sih);
+
+extern uint16 si_d11_devid(si_t *sih);
+extern int si_corepciid(si_t *sih, uint func, uint16 *pcivendor, uint16 *pcidevice,
+	uint8 *pciclass, uint8 *pcisubclass, uint8 *pciprogif, uint8 *pciheader);
+
+#define si_eci_init(sih) (0)
+#define si_eci_notify_bt(sih, type, val, interrupt)  (0)
+
+
+
+extern int si_devpath(si_t *sih, char *path, int size);
+
+extern char *si_getdevpathvar(si_t *sih, const char *name);
+extern int si_getdevpathintvar(si_t *sih, const char *name);
+
+
+extern uint8 si_pcieclkreq(si_t *sih, uint32 mask, uint32 val);
+extern void si_war42780_clkreq(si_t *sih, bool clkreq);
+extern void si_pci_sleep(si_t *sih);
+extern void si_pci_down(si_t *sih);
+extern void si_pci_up(si_t *sih);
+extern void si_pcie_war_ovr_disable(si_t *sih);
+extern void si_pcie_extendL1timer(si_t *sih, bool extend);
+extern int si_pci_fixcfg(si_t *sih);
+
+
+
+
+
+
+
+#endif	
diff --git a/drivers/net/wireless/bcm4329/include/spid.h b/drivers/net/wireless/bcm4329/include/spid.h
new file mode 100644
index 0000000..c740296
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/spid.h
@@ -0,0 +1,153 @@
+/*
+ * SPI device spec header file
+ *
+ * Copyright (C) 2010, Broadcom Corporation
+ * All Rights Reserved.
+ * 
+ * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation;
+ * the contents of this file may not be disclosed to third parties, copied
+ * or duplicated in any form, in whole or in part, without the prior
+ * written permission of Broadcom Corporation.
+ *
+ * $Id: spid.h,v 1.7.10.1.16.3 2009/04/09 19:23:14 Exp $
+ */
+
+#ifndef	_SPI_H
+#define	_SPI_H
+
+/*
+ * Brcm SPI Device Register Map.
+ *
+ */
+
+typedef volatile struct {
+	uint8	config;			/* 0x00, len, endian, clock, speed, polarity, wakeup */
+	uint8	response_delay;		/* 0x01, read response delay in bytes (corerev < 3) */
+	uint8	status_enable;		/* 0x02, status-enable, intr with status, response_delay
+					 * function selection, command/data error check
+					 */
+	uint8	reset_bp;		/* 0x03, reset on wlan/bt backplane reset (corerev >= 1) */
+	uint16	intr_reg;		/* 0x04, Intr status register */
+	uint16	intr_en_reg;		/* 0x06, Intr mask register */
+	uint32	status_reg;		/* 0x08, RO, Status bits of last spi transfer */
+	uint16	f1_info_reg;		/* 0x0c, RO, enabled, ready for data transfer, blocksize */
+	uint16	f2_info_reg;		/* 0x0e, RO, enabled, ready for data transfer, blocksize */
+	uint16	f3_info_reg;		/* 0x10, RO, enabled, ready for data transfer, blocksize */
+	uint32	test_read;		/* 0x14, RO 0xfeedbead signature */
+	uint32	test_rw;		/* 0x18, RW */
+	uint8	resp_delay_f0;		/* 0x1c, read resp delay bytes for F0 (corerev >= 3) */
+	uint8	resp_delay_f1;		/* 0x1d, read resp delay bytes for F1 (corerev >= 3) */
+	uint8	resp_delay_f2;		/* 0x1e, read resp delay bytes for F2 (corerev >= 3) */
+	uint8	resp_delay_f3;		/* 0x1f, read resp delay bytes for F3 (corerev >= 3) */
+} spi_regs_t;
+
+/* SPI device register offsets */
+#define SPID_CONFIG			0x00
+#define SPID_RESPONSE_DELAY		0x01
+#define SPID_STATUS_ENABLE		0x02
+#define SPID_RESET_BP			0x03	/* (corerev >= 1) */
+#define SPID_INTR_REG			0x04	/* 16 bits - Interrupt status */
+#define SPID_INTR_EN_REG		0x06	/* 16 bits - Interrupt mask */
+#define SPID_STATUS_REG			0x08	/* 32 bits */
+#define SPID_F1_INFO_REG		0x0C	/* 16 bits */
+#define SPID_F2_INFO_REG		0x0E	/* 16 bits */
+#define SPID_F3_INFO_REG		0x10	/* 16 bits */
+#define SPID_TEST_READ			0x14	/* 32 bits */
+#define SPID_TEST_RW			0x18	/* 32 bits */
+#define SPID_RESP_DELAY_F0		0x1c	/* 8 bits (corerev >= 3) */
+#define SPID_RESP_DELAY_F1		0x1d	/* 8 bits (corerev >= 3) */
+#define SPID_RESP_DELAY_F2		0x1e	/* 8 bits (corerev >= 3) */
+#define SPID_RESP_DELAY_F3		0x1f	/* 8 bits (corerev >= 3) */
+
+/* Bit masks for SPID_CONFIG device register */
+#define WORD_LENGTH_32	0x1	/* 0/1 16/32 bit word length */
+#define ENDIAN_BIG	0x2	/* 0/1 Little/Big Endian */
+#define CLOCK_PHASE	0x4	/* 0/1 clock phase delay */
+#define CLOCK_POLARITY	0x8	/* 0/1 Idle state clock polarity is low/high */
+#define HIGH_SPEED_MODE	0x10	/* 1/0 High Speed mode / Normal mode */
+#define INTR_POLARITY	0x20	/* 1/0 Interrupt active polarity is high/low */
+#define WAKE_UP		0x80	/* 0/1 Wake-up command from Host to WLAN */
+
+/* Bit mask for SPID_RESPONSE_DELAY device register */
+#define RESPONSE_DELAY_MASK	0xFF	/* Configurable rd response delay in multiples of 8 bits */
+
+/* Bit mask for SPID_STATUS_ENABLE device register */
+#define STATUS_ENABLE		0x1	/* 1/0 Status sent/not sent to host after read/write */
+#define INTR_WITH_STATUS	0x2	/* 0/1 Do-not / do-interrupt if status is sent */
+#define RESP_DELAY_ALL		0x4	/* Applicability of resp delay to F1 or all func's read */
+#define DWORD_PKT_LEN_EN	0x8	/* Packet len denoted in dwords instead of bytes */
+#define CMD_ERR_CHK_EN		0x20	/* Command error check enable */
+#define DATA_ERR_CHK_EN		0x40	/* Data error check enable */
+
+/* Bit mask for SPID_RESET_BP device register */
+#define RESET_ON_WLAN_BP_RESET	0x4	/* enable reset for WLAN backplane */
+#define RESET_ON_BT_BP_RESET	0x8	/* enable reset for BT backplane */
+#define RESET_SPI		0x80	/* reset the above enabled logic */
+
+/* Bit mask for SPID_INTR_REG device register */
+#define DATA_UNAVAILABLE	0x0001	/* Requested data not available; Clear by writing a "1" */
+#define F2_F3_FIFO_RD_UNDERFLOW	0x0002
+#define F2_F3_FIFO_WR_OVERFLOW	0x0004
+#define COMMAND_ERROR		0x0008	/* Cleared by writing 1 */
+#define DATA_ERROR		0x0010	/* Cleared by writing 1 */
+#define F2_PACKET_AVAILABLE	0x0020
+#define F3_PACKET_AVAILABLE	0x0040
+#define F1_OVERFLOW		0x0080	/* Due to last write. Bkplane has pending write requests */
+#define MISC_INTR0		0x0100
+#define MISC_INTR1		0x0200
+#define MISC_INTR2		0x0400
+#define MISC_INTR3		0x0800
+#define MISC_INTR4		0x1000
+#define F1_INTR			0x2000
+#define F2_INTR			0x4000
+#define F3_INTR			0x8000
+
+/* Bit mask for 32bit SPID_STATUS_REG device register */
+#define STATUS_DATA_NOT_AVAILABLE	0x00000001
+#define STATUS_UNDERFLOW		0x00000002
+#define STATUS_OVERFLOW			0x00000004
+#define STATUS_F2_INTR			0x00000008
+#define STATUS_F3_INTR			0x00000010
+#define STATUS_F2_RX_READY		0x00000020
+#define STATUS_F3_RX_READY		0x00000040
+#define STATUS_HOST_CMD_DATA_ERR	0x00000080
+#define STATUS_F2_PKT_AVAILABLE		0x00000100
+#define STATUS_F2_PKT_LEN_MASK		0x000FFE00
+#define STATUS_F2_PKT_LEN_SHIFT		9
+#define STATUS_F3_PKT_AVAILABLE		0x00100000
+#define STATUS_F3_PKT_LEN_MASK		0xFFE00000
+#define STATUS_F3_PKT_LEN_SHIFT		21
+
+/* Bit mask for 16 bits SPID_F1_INFO_REG device register */
+#define F1_ENABLED 			0x0001
+#define F1_RDY_FOR_DATA_TRANSFER	0x0002
+#define F1_MAX_PKT_SIZE			0x01FC
+
+/* Bit mask for 16 bits SPID_F2_INFO_REG device register */
+#define F2_ENABLED 			0x0001
+#define F2_RDY_FOR_DATA_TRANSFER	0x0002
+#define F2_MAX_PKT_SIZE			0x3FFC
+
+/* Bit mask for 16 bits SPID_F3_INFO_REG device register */
+#define F3_ENABLED 			0x0001
+#define F3_RDY_FOR_DATA_TRANSFER	0x0002
+#define F3_MAX_PKT_SIZE			0x3FFC
+
+/* Bit mask for 32 bits SPID_TEST_READ device register read in 16bit LE mode */
+#define TEST_RO_DATA_32BIT_LE		0xFEEDBEAD
+
+/* Maximum number of I/O funcs */
+#define SPI_MAX_IOFUNCS		4
+
+#define SPI_MAX_PKT_LEN		(2048*4)
+
+/* Misc defines */
+#define SPI_FUNC_0		0
+#define SPI_FUNC_1		1
+#define SPI_FUNC_2		2
+#define SPI_FUNC_3		3
+
+#define WAIT_F2RXFIFORDY	100
+#define WAIT_F2RXFIFORDY_DELAY	20
+
+#endif /* _SPI_H */
diff --git a/drivers/net/wireless/bcm4329/include/trxhdr.h b/drivers/net/wireless/bcm4329/include/trxhdr.h
new file mode 100644
index 0000000..8f5eed9
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/trxhdr.h
@@ -0,0 +1,46 @@
+/*
+ * TRX image file header format.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: trxhdr.h,v 13.11.310.1 2008/08/17 12:58:58 Exp $
+ */
+
+#include <typedefs.h>
+
+#define TRX_MAGIC	0x30524448	/* "HDR0" */
+#define TRX_VERSION	1		/* Version 1 */
+#define TRX_MAX_LEN	0x3A0000	/* Max length */
+#define TRX_NO_HEADER	1		/* Do not write TRX header */
+#define TRX_GZ_FILES	0x2     /* Contains up to TRX_MAX_OFFSET individual gzip files */
+#define TRX_MAX_OFFSET	3		/* Max number of individual files */
+#define TRX_UNCOMP_IMAGE	0x20	/* Trx contains uncompressed rtecdc.bin image */
+
+struct trx_header {
+	uint32 magic;		/* "HDR0" */
+	uint32 len;		/* Length of file including header */
+	uint32 crc32;		/* 32-bit CRC from flag_version to end of file */
+	uint32 flag_version;	/* 0:15 flags, 16:31 version */
+	uint32 offsets[TRX_MAX_OFFSET];	/* Offsets of partitions from start of header */
+};
+
+/* Compatibility */
+typedef struct trx_header TRXHDR, *PTRXHDR;
diff --git a/drivers/net/wireless/bcm4329/include/typedefs.h b/drivers/net/wireless/bcm4329/include/typedefs.h
new file mode 100644
index 0000000..4d9dd76
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/typedefs.h
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ * $Id: typedefs.h,v 1.85.34.1.2.5 2009/01/27 04:09:40 Exp $
+ */
+
+
+#ifndef _TYPEDEFS_H_
+#define _TYPEDEFS_H_
+
+#ifdef SITE_TYPEDEFS
+
+
+
+#include "site_typedefs.h"
+
+#else
+
+
+
+#ifdef __cplusplus
+
+#define TYPEDEF_BOOL
+#ifndef FALSE
+#define FALSE	false
+#endif
+#ifndef TRUE
+#define TRUE	true
+#endif
+
+#else	
+
+
+#endif	
+
+#if defined(__x86_64__)
+#define TYPEDEF_UINTPTR
+typedef unsigned long long int uintptr;
+#endif
+
+
+
+
+#if defined(TARGETOS_nucleus)
+
+#include <stddef.h>
+
+
+#define TYPEDEF_FLOAT_T
+#endif   
+
+#if defined(_NEED_SIZE_T_)
+typedef long unsigned int size_t;
+#endif
+
+#ifdef __DJGPP__
+typedef long unsigned int size_t;
+#endif 
+
+
+
+
+
+#define TYPEDEF_UINT
+#ifndef TARGETENV_android
+#define TYPEDEF_USHORT
+#define TYPEDEF_ULONG
+#endif 
+#ifdef __KERNEL__
+#include <linux/version.h>
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19))
+#define TYPEDEF_BOOL
+#endif	
+#endif	
+
+
+
+
+
+#if defined(__GNUC__) && defined(__STRICT_ANSI__)
+#define TYPEDEF_INT64
+#define TYPEDEF_UINT64
+#endif
+
+
+#if defined(__ICL)
+
+#define TYPEDEF_INT64
+
+#if defined(__STDC__)
+#define TYPEDEF_UINT64
+#endif
+
+#endif 
+
+#if !defined(__DJGPP__) && !defined(TARGETOS_nucleus)
+
+
+#if defined(__KERNEL__)
+
+#include <linux/types.h>	
+
+#else
+
+
+#include <sys/types.h>
+
+#endif 
+
+#endif 
+
+
+
+
+#define USE_TYPEDEF_DEFAULTS
+
+#endif 
+
+
+
+
+#ifdef USE_TYPEDEF_DEFAULTS
+#undef USE_TYPEDEF_DEFAULTS
+
+#ifndef TYPEDEF_BOOL
+typedef	 unsigned char	bool;
+#endif
+
+
+
+#ifndef TYPEDEF_UCHAR
+typedef unsigned char	uchar;
+#endif
+
+#ifndef TYPEDEF_USHORT
+typedef unsigned short	ushort;
+#endif
+
+#ifndef TYPEDEF_UINT
+typedef unsigned int	uint;
+#endif
+
+#ifndef TYPEDEF_ULONG
+typedef unsigned long	ulong;
+#endif
+
+
+
+#ifndef TYPEDEF_UINT8
+typedef unsigned char	uint8;
+#endif
+
+#ifndef TYPEDEF_UINT16
+typedef unsigned short	uint16;
+#endif
+
+#ifndef TYPEDEF_UINT32
+typedef unsigned int	uint32;
+#endif
+
+#ifndef TYPEDEF_UINT64
+typedef unsigned long long uint64;
+#endif
+
+#ifndef TYPEDEF_UINTPTR
+typedef unsigned int	uintptr;
+#endif
+
+#ifndef TYPEDEF_INT8
+typedef signed char	int8;
+#endif
+
+#ifndef TYPEDEF_INT16
+typedef signed short	int16;
+#endif
+
+#ifndef TYPEDEF_INT32
+typedef signed int	int32;
+#endif
+
+#ifndef TYPEDEF_INT64
+typedef signed long long int64;
+#endif
+
+
+
+#ifndef TYPEDEF_FLOAT32
+typedef float		float32;
+#endif
+
+#ifndef TYPEDEF_FLOAT64
+typedef double		float64;
+#endif
+
+
+
+#ifndef TYPEDEF_FLOAT_T
+
+#if defined(FLOAT32)
+typedef float32 float_t;
+#else 
+typedef float64 float_t;
+#endif
+
+#endif 
+
+
+
+#ifndef FALSE
+#define FALSE	0
+#endif
+
+#ifndef TRUE
+#define TRUE	1  
+#endif
+
+#ifndef NULL
+#define	NULL	0
+#endif
+
+#ifndef OFF
+#define	OFF	0
+#endif
+
+#ifndef ON
+#define	ON	1  
+#endif
+
+#define	AUTO	(-1) 
+
+
+
+#ifndef PTRSZ
+#define	PTRSZ	sizeof(char*)
+#endif
+
+
+
+#if defined(__GNUC__)
+	#define BWL_COMPILER_GNU
+#elif defined(__CC_ARM)
+	#define BWL_COMPILER_ARMCC
+#else
+	#error "Unknown compiler!"
+#endif 
+
+
+#ifndef INLINE
+	#if defined(BWL_COMPILER_MICROSOFT)
+		#define INLINE __inline
+	#elif defined(BWL_COMPILER_GNU)
+		#define INLINE __inline__
+	#elif defined(BWL_COMPILER_ARMCC)
+		#define INLINE	__inline
+	#else
+		#define INLINE
+	#endif 
+#endif 
+
+#undef TYPEDEF_BOOL
+#undef TYPEDEF_UCHAR
+#undef TYPEDEF_USHORT
+#undef TYPEDEF_UINT
+#undef TYPEDEF_ULONG
+#undef TYPEDEF_UINT8
+#undef TYPEDEF_UINT16
+#undef TYPEDEF_UINT32
+#undef TYPEDEF_UINT64
+#undef TYPEDEF_UINTPTR
+#undef TYPEDEF_INT8
+#undef TYPEDEF_INT16
+#undef TYPEDEF_INT32
+#undef TYPEDEF_INT64
+#undef TYPEDEF_FLOAT32
+#undef TYPEDEF_FLOAT64
+#undef TYPEDEF_FLOAT_T
+
+#endif 
+
+
+#define UNUSED_PARAMETER(x) (void)(x)
+
+
+#include <bcmdefs.h>
+
+#endif 
diff --git a/drivers/net/wireless/bcm4329/include/wlioctl.h b/drivers/net/wireless/bcm4329/include/wlioctl.h
new file mode 100644
index 0000000..00c61f1
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/wlioctl.h
@@ -0,0 +1,1673 @@
+/*
+ * Custom OID/ioctl definitions for
+ * Broadcom 802.11abg Networking Device Driver
+ *
+ * Definitions subject to change without notice.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: wlioctl.h,v 1.601.4.15.2.14.2.62.4.3 2011/02/09 23:31:02 Exp $
+ */
+
+
+#ifndef _wlioctl_h_
+#define	_wlioctl_h_
+
+#include <typedefs.h>
+#include <proto/ethernet.h>
+#include <proto/bcmeth.h>
+#include <proto/bcmevent.h>
+#include <proto/802.11.h>
+#include <bcmwifi.h>
+
+
+
+#define ACTION_FRAME_SIZE 1040
+
+typedef struct wl_action_frame {
+	struct ether_addr	da;
+	uint16				len;
+	uint32				packetId;
+	uint8				data[ACTION_FRAME_SIZE];
+} wl_action_frame_t;
+
+#define WL_WIFI_ACTION_FRAME_SIZE sizeof(struct wl_action_frame)
+
+
+#define BWL_DEFAULT_PACKING
+#include <packed_section_start.h>
+
+#define RWL_ACTION_WIFI_CATEGORY	127  
+#define RWL_WIFI_OUI_BYTE1		0x90 
+#define RWL_WIFI_OUI_BYTE2		0x4C
+#define RWL_WIFI_OUI_BYTE3		0x0F
+#define RWL_WIFI_ACTION_FRAME_SIZE	sizeof(struct dot11_action_wifi_vendor_specific)
+#define RWL_WIFI_DEFAULT                0x00
+#define RWL_WIFI_FIND_MY_PEER		0x09 
+#define RWL_WIFI_FOUND_PEER		0x0A 
+#define RWL_ACTION_WIFI_FRAG_TYPE	0x55 
+
+typedef struct ssid_info
+{
+	uint8		ssid_len;		
+	uint8		ssid[32];		
+} ssid_info_t;
+
+typedef struct cnt_rx
+{
+	uint32 cnt_rxundec;
+	uint32 cnt_rxframe;
+} cnt_rx_t;
+
+
+
+#define RWL_REF_MAC_ADDRESS_OFFSET	17
+#define RWL_DUT_MAC_ADDRESS_OFFSET	23
+#define RWL_WIFI_CLIENT_CHANNEL_OFFSET	50
+#define RWL_WIFI_SERVER_CHANNEL_OFFSET	51
+
+
+
+
+
+#define	WL_BSS_INFO_VERSION	108		
+
+
+typedef struct wl_bss_info {
+	uint32		version;		
+	uint32		length;			
+	struct ether_addr BSSID;
+	uint16		beacon_period;		
+	uint16		capability;		
+	uint8		SSID_len;
+	uint8		SSID[32];
+	struct {
+		uint	count;			
+		uint8	rates[16];		
+	} rateset;				
+	chanspec_t	chanspec;		
+	uint16		atim_window;		
+	uint8		dtim_period;		
+	int16		RSSI;			
+	int8		phy_noise;		
+
+	uint8		n_cap;			
+	uint32		nbss_cap;		
+	uint8		ctl_ch;			
+	uint32		reserved32[1];		
+	uint8		flags;			
+	uint8		reserved[3];		
+	uint8		basic_mcs[MCSSET_LEN];	
+
+	uint16		ie_offset;		
+	uint32		ie_length;		
+	
+	
+} wl_bss_info_t;
+
+typedef struct wlc_ssid {
+	uint32		SSID_len;
+	uchar		SSID[32];
+} wlc_ssid_t;
+
+
+#define WL_BSSTYPE_INFRA 1
+#define WL_BSSTYPE_INDEP 0
+#define WL_BSSTYPE_ANY   2
+
+
+#define WL_SCANFLAGS_PASSIVE 0x01
+#define WL_SCANFLAGS_PROHIBITED	0x04
+
+typedef struct wl_scan_params {
+	wlc_ssid_t ssid;		
+	struct ether_addr bssid;	
+	int8 bss_type;			
+	int8 scan_type;		
+	int32 nprobes;			
+	int32 active_time;		
+	int32 passive_time;		
+	int32 home_time;		
+	int32 channel_num;		
+	uint16 channel_list[1];		
+} wl_scan_params_t;
+
+#define WL_SCAN_PARAMS_FIXED_SIZE 64
+
+
+#define WL_SCAN_PARAMS_COUNT_MASK 0x0000ffff
+#define WL_SCAN_PARAMS_NSSID_SHIFT 16
+
+#define WL_SCAN_ACTION_START      1
+#define WL_SCAN_ACTION_CONTINUE   2
+#define WL_SCAN_ACTION_ABORT      3
+
+#define ISCAN_REQ_VERSION 1
+
+
+typedef struct wl_iscan_params {
+	uint32 version;
+	uint16 action;
+	uint16 scan_duration;
+	wl_scan_params_t params;
+} wl_iscan_params_t;
+
+#define WL_ISCAN_PARAMS_FIXED_SIZE (OFFSETOF(wl_iscan_params_t, params) + sizeof(wlc_ssid_t))
+
+typedef struct wl_scan_results {
+	uint32 buflen;
+	uint32 version;
+	uint32 count;
+	wl_bss_info_t bss_info[1];
+} wl_scan_results_t;
+
+#define WL_SCAN_RESULTS_FIXED_SIZE 12
+
+
+#define WL_SCAN_RESULTS_SUCCESS	0
+#define WL_SCAN_RESULTS_PARTIAL	1
+#define WL_SCAN_RESULTS_PENDING	2
+#define WL_SCAN_RESULTS_ABORTED	3
+#define WL_SCAN_RESULTS_NO_MEM	4
+
+#define ESCAN_REQ_VERSION 1
+
+typedef struct wl_escan_params {
+	uint32 version;
+	uint16 action;
+	uint16 sync_id;
+	wl_scan_params_t params;
+} wl_escan_params_t;
+
+#define WL_ESCAN_PARAMS_FIXED_SIZE (OFFSETOF(wl_escan_params_t, params) + sizeof(wlc_ssid_t))
+
+typedef struct wl_escan_result {
+	uint32 buflen;
+	uint32 version;
+	uint16 sync_id;
+	uint16 bss_count;
+	wl_bss_info_t bss_info[1];
+} wl_escan_result_t;
+
+#define WL_ESCAN_RESULTS_FIXED_SIZE (sizeof(wl_escan_result_t) - sizeof(wl_bss_info_t))
+
+
+typedef struct wl_iscan_results {
+	uint32 status;
+	wl_scan_results_t results;
+} wl_iscan_results_t;
+
+#define WL_ISCAN_RESULTS_FIXED_SIZE \
+	(WL_SCAN_RESULTS_FIXED_SIZE + OFFSETOF(wl_iscan_results_t, results))
+
+#define WL_NUMRATES		16	
+typedef struct wl_rateset {
+	uint32	count;			
+	uint8	rates[WL_NUMRATES];	
+} wl_rateset_t;
+
+
+typedef struct wl_uint32_list {
+	
+	uint32 count;
+	
+	uint32 element[1];
+} wl_uint32_list_t;
+
+
+typedef struct wl_assoc_params {
+	struct ether_addr bssid;	
+	uint16 bssid_cnt;		
+	int32 chanspec_num;		
+	chanspec_t chanspec_list[1];	
+} wl_assoc_params_t;
+#define WL_ASSOC_PARAMS_FIXED_SIZE 	(sizeof(wl_assoc_params_t) - sizeof(chanspec_t))
+
+
+typedef wl_assoc_params_t wl_reassoc_params_t;
+#define WL_REASSOC_PARAMS_FIXED_SIZE	WL_ASSOC_PARAMS_FIXED_SIZE
+
+
+typedef struct wl_join_params {
+	wlc_ssid_t ssid;
+	wl_assoc_params_t params;	
+} wl_join_params_t;
+#define WL_JOIN_PARAMS_FIXED_SIZE 	(sizeof(wl_join_params_t) - sizeof(chanspec_t))
+
+#define WLC_CNTRY_BUF_SZ	4		
+
+typedef struct wl_country {
+	char country_abbrev[WLC_CNTRY_BUF_SZ];
+	int32 rev;
+	char ccode[WLC_CNTRY_BUF_SZ];
+} wl_country_t;
+
+typedef enum sup_auth_status {
+	
+	WLC_SUP_DISCONNECTED = 0,
+	WLC_SUP_CONNECTING,
+	WLC_SUP_IDREQUIRED,
+	WLC_SUP_AUTHENTICATING,
+	WLC_SUP_AUTHENTICATED,
+	WLC_SUP_KEYXCHANGE,
+	WLC_SUP_KEYED,
+	WLC_SUP_TIMEOUT,
+	WLC_SUP_LAST_BASIC_STATE,
+
+	
+	WLC_SUP_KEYXCHANGE_WAIT_M1 = WLC_SUP_AUTHENTICATED,
+	                                
+	WLC_SUP_KEYXCHANGE_PREP_M2 = WLC_SUP_KEYXCHANGE,
+	                                
+	WLC_SUP_KEYXCHANGE_WAIT_M3 = WLC_SUP_LAST_BASIC_STATE,
+	                                
+	WLC_SUP_KEYXCHANGE_PREP_M4,		
+	WLC_SUP_KEYXCHANGE_WAIT_G1,		
+	WLC_SUP_KEYXCHANGE_PREP_G2		
+} sup_auth_status_t;
+
+
+#define	CRYPTO_ALGO_OFF			0
+#define	CRYPTO_ALGO_WEP1		1
+#define	CRYPTO_ALGO_TKIP		2
+#define	CRYPTO_ALGO_WEP128		3
+#define CRYPTO_ALGO_AES_CCM		4
+#define CRYPTO_ALGO_AES_OCB_MSDU	5
+#define CRYPTO_ALGO_AES_OCB_MPDU	6
+#define CRYPTO_ALGO_NALG		7
+#define CRYPTO_ALGO_SMS4		11
+
+#define WSEC_GEN_MIC_ERROR	0x0001
+#define WSEC_GEN_REPLAY		0x0002
+#define WSEC_GEN_ICV_ERROR	0x0004
+
+#define WL_SOFT_KEY	(1 << 0)	
+#define WL_PRIMARY_KEY	(1 << 1)	
+#define WL_KF_RES_4	(1 << 4)	
+#define WL_KF_RES_5	(1 << 5)	
+#define WL_IBSS_PEER_GROUP_KEY	(1 << 6)	
+
+typedef struct wl_wsec_key {
+	uint32		index;		
+	uint32		len;		
+	uint8		data[DOT11_MAX_KEY_SIZE];	
+	uint32		pad_1[18];
+	uint32		algo;		
+	uint32		flags;		
+	uint32		pad_2[2];
+	int		pad_3;
+	int		iv_initialized;	
+	int		pad_4;
+	
+	struct {
+		uint32	hi;		
+		uint16	lo;		
+	} rxiv;
+	uint32		pad_5[2];
+	struct ether_addr ea;		
+} wl_wsec_key_t;
+
+#define WSEC_MIN_PSK_LEN	8
+#define WSEC_MAX_PSK_LEN	64
+
+
+#define WSEC_PASSPHRASE		(1<<0)
+
+
+typedef struct {
+	ushort	key_len;		
+	ushort	flags;			
+	uint8	key[WSEC_MAX_PSK_LEN];	
+} wsec_pmk_t;
+
+
+#define WEP_ENABLED		0x0001
+#define TKIP_ENABLED		0x0002
+#define AES_ENABLED		0x0004
+#define WSEC_SWFLAG		0x0008
+#define SES_OW_ENABLED		0x0040	
+#define SMS4_ENABLED		0x0100
+
+
+#define WPA_AUTH_DISABLED	0x0000	
+#define WPA_AUTH_NONE		0x0001	
+#define WPA_AUTH_UNSPECIFIED	0x0002	
+#define WPA_AUTH_PSK		0x0004	
+	
+#define WPA2_AUTH_UNSPECIFIED	0x0040	
+#define WPA2_AUTH_PSK		0x0080	
+#define BRCM_AUTH_PSK           0x0100  
+#define BRCM_AUTH_DPT		0x0200	
+#define WPA_AUTH_WAPI		0x0400	
+
+#define WPA_AUTH_PFN_ANY	0xffffffff	
+
+
+#define	MAXPMKID		16
+
+typedef struct _pmkid {
+	struct ether_addr	BSSID;
+	uint8			PMKID[WPA2_PMKID_LEN];
+} pmkid_t;
+
+typedef struct _pmkid_list {
+	uint32	npmkid;
+	pmkid_t	pmkid[1];
+} pmkid_list_t;
+
+typedef struct _pmkid_cand {
+	struct ether_addr	BSSID;
+	uint8			preauth;
+} pmkid_cand_t;
+
+typedef struct _pmkid_cand_list {
+	uint32	npmkid_cand;
+	pmkid_cand_t	pmkid_cand[1];
+} pmkid_cand_list_t;
+
+
+
+
+typedef struct {
+	uint32	val;
+	struct ether_addr ea;
+} scb_val_t;
+
+
+
+typedef struct channel_info {
+	int hw_channel;
+	int target_channel;
+	int scan_channel;
+} channel_info_t;
+
+
+struct maclist {
+	uint count;			
+	struct ether_addr ea[1];	
+};
+
+
+typedef struct get_pktcnt {
+	uint rx_good_pkt;
+	uint rx_bad_pkt;
+	uint tx_good_pkt;
+	uint tx_bad_pkt;
+	uint rx_ocast_good_pkt; 
+} get_pktcnt_t;
+
+
+typedef struct wl_ioctl {
+	uint cmd;	
+	void *buf;	
+	uint len;	
+	uint8 set;	
+	uint used;	
+	uint needed;	
+} wl_ioctl_t;
+
+
+
+#define WLC_IOCTL_MAGIC		0x14e46c77
+
+
+#define WLC_IOCTL_VERSION	1
+
+#define	WLC_IOCTL_MAXLEN	8192	
+#define	WLC_IOCTL_SMLEN		256		
+#define	WLC_IOCTL_MEDLEN	1536	
+
+
+
+#define WLC_GET_MAGIC				0
+#define WLC_GET_VERSION				1
+#define WLC_UP					2
+#define WLC_DOWN				3
+#define WLC_GET_LOOP				4
+#define WLC_SET_LOOP				5
+#define WLC_DUMP				6
+#define WLC_GET_MSGLEVEL			7
+#define WLC_SET_MSGLEVEL			8
+#define WLC_GET_PROMISC				9
+#define WLC_SET_PROMISC				10
+ 
+#define WLC_GET_RATE				12
+ 
+#define WLC_GET_INSTANCE			14
+ 
+ 
+ 
+ 
+#define WLC_GET_INFRA				19
+#define WLC_SET_INFRA				20
+#define WLC_GET_AUTH				21
+#define WLC_SET_AUTH				22
+#define WLC_GET_BSSID				23
+#define WLC_SET_BSSID				24
+#define WLC_GET_SSID				25
+#define WLC_SET_SSID				26
+#define WLC_RESTART				27
+ 
+#define WLC_GET_CHANNEL				29
+#define WLC_SET_CHANNEL				30
+#define WLC_GET_SRL				31
+#define WLC_SET_SRL				32
+#define WLC_GET_LRL				33
+#define WLC_SET_LRL				34
+#define WLC_GET_PLCPHDR				35
+#define WLC_SET_PLCPHDR				36
+#define WLC_GET_RADIO				37
+#define WLC_SET_RADIO				38
+#define WLC_GET_PHYTYPE				39
+#define WLC_DUMP_RATE				40
+#define WLC_SET_RATE_PARAMS			41
+ 
+ 
+#define WLC_GET_KEY				44
+#define WLC_SET_KEY				45
+#define WLC_GET_REGULATORY			46
+#define WLC_SET_REGULATORY			47
+#define WLC_GET_PASSIVE_SCAN			48
+#define WLC_SET_PASSIVE_SCAN			49
+#define WLC_SCAN				50
+#define WLC_SCAN_RESULTS			51
+#define WLC_DISASSOC				52
+#define WLC_REASSOC				53
+#define WLC_GET_ROAM_TRIGGER			54
+#define WLC_SET_ROAM_TRIGGER			55
+#define WLC_GET_ROAM_DELTA			56
+#define WLC_SET_ROAM_DELTA			57
+#define WLC_GET_ROAM_SCAN_PERIOD		58
+#define WLC_SET_ROAM_SCAN_PERIOD		59
+#define WLC_EVM					60	
+#define WLC_GET_TXANT				61
+#define WLC_SET_TXANT				62
+#define WLC_GET_ANTDIV				63
+#define WLC_SET_ANTDIV				64
+ 
+ 
+#define WLC_GET_CLOSED				67
+#define WLC_SET_CLOSED				68
+#define WLC_GET_MACLIST				69
+#define WLC_SET_MACLIST				70
+#define WLC_GET_RATESET				71
+#define WLC_SET_RATESET				72
+ 
+#define WLC_LONGTRAIN				74
+#define WLC_GET_BCNPRD				75
+#define WLC_SET_BCNPRD				76
+#define WLC_GET_DTIMPRD				77
+#define WLC_SET_DTIMPRD				78
+#define WLC_GET_SROM				79
+#define WLC_SET_SROM				80
+#define WLC_GET_WEP_RESTRICT			81
+#define WLC_SET_WEP_RESTRICT			82
+#define WLC_GET_COUNTRY				83
+#define WLC_SET_COUNTRY				84
+#define WLC_GET_PM				85
+#define WLC_SET_PM				86
+#define WLC_GET_WAKE				87
+#define WLC_SET_WAKE				88
+ 
+#define WLC_GET_FORCELINK			90	
+#define WLC_SET_FORCELINK			91	
+#define WLC_FREQ_ACCURACY			92	
+#define WLC_CARRIER_SUPPRESS			93	
+#define WLC_GET_PHYREG				94
+#define WLC_SET_PHYREG				95
+#define WLC_GET_RADIOREG			96
+#define WLC_SET_RADIOREG			97
+#define WLC_GET_REVINFO				98
+#define WLC_GET_UCANTDIV			99
+#define WLC_SET_UCANTDIV			100
+#define WLC_R_REG				101
+#define WLC_W_REG				102
+
+ 
+#define WLC_GET_MACMODE				105
+#define WLC_SET_MACMODE				106
+#define WLC_GET_MONITOR				107
+#define WLC_SET_MONITOR				108
+#define WLC_GET_GMODE				109
+#define WLC_SET_GMODE				110
+#define WLC_GET_LEGACY_ERP			111
+#define WLC_SET_LEGACY_ERP			112
+#define WLC_GET_RX_ANT				113
+#define WLC_GET_CURR_RATESET			114	
+#define WLC_GET_SCANSUPPRESS			115
+#define WLC_SET_SCANSUPPRESS			116
+#define WLC_GET_AP				117
+#define WLC_SET_AP				118
+#define WLC_GET_EAP_RESTRICT			119
+#define WLC_SET_EAP_RESTRICT			120
+#define WLC_SCB_AUTHORIZE			121
+#define WLC_SCB_DEAUTHORIZE			122
+#define WLC_GET_WDSLIST				123
+#define WLC_SET_WDSLIST				124
+#define WLC_GET_ATIM				125
+#define WLC_SET_ATIM				126
+#define WLC_GET_RSSI				127
+#define WLC_GET_PHYANTDIV			128
+#define WLC_SET_PHYANTDIV			129
+#define WLC_AP_RX_ONLY				130
+#define WLC_GET_TX_PATH_PWR			131
+#define WLC_SET_TX_PATH_PWR			132
+#define WLC_GET_WSEC				133
+#define WLC_SET_WSEC				134
+#define WLC_GET_PHY_NOISE			135
+#define WLC_GET_BSS_INFO			136
+#define WLC_GET_PKTCNTS				137
+#define WLC_GET_LAZYWDS				138
+#define WLC_SET_LAZYWDS				139
+#define WLC_GET_BANDLIST			140
+#define WLC_GET_BAND				141
+#define WLC_SET_BAND				142
+#define WLC_SCB_DEAUTHENTICATE			143
+#define WLC_GET_SHORTSLOT			144
+#define WLC_GET_SHORTSLOT_OVERRIDE		145
+#define WLC_SET_SHORTSLOT_OVERRIDE		146
+#define WLC_GET_SHORTSLOT_RESTRICT		147
+#define WLC_SET_SHORTSLOT_RESTRICT		148
+#define WLC_GET_GMODE_PROTECTION		149
+#define WLC_GET_GMODE_PROTECTION_OVERRIDE	150
+#define WLC_SET_GMODE_PROTECTION_OVERRIDE	151
+#define WLC_UPGRADE				152
+ 
+ 
+#define WLC_GET_IGNORE_BCNS			155
+#define WLC_SET_IGNORE_BCNS			156
+#define WLC_GET_SCB_TIMEOUT			157
+#define WLC_SET_SCB_TIMEOUT			158
+#define WLC_GET_ASSOCLIST			159
+#define WLC_GET_CLK				160
+#define WLC_SET_CLK				161
+#define WLC_GET_UP				162
+#define WLC_OUT					163
+#define WLC_GET_WPA_AUTH			164
+#define WLC_SET_WPA_AUTH			165
+#define WLC_GET_UCFLAGS				166
+#define WLC_SET_UCFLAGS				167
+#define WLC_GET_PWRIDX				168
+#define WLC_SET_PWRIDX				169
+#define WLC_GET_TSSI				170
+#define WLC_GET_SUP_RATESET_OVERRIDE		171
+#define WLC_SET_SUP_RATESET_OVERRIDE		172
+ 
+ 
+ 
+ 
+ 
+#define WLC_GET_PROTECTION_CONTROL		178
+#define WLC_SET_PROTECTION_CONTROL		179
+#define WLC_GET_PHYLIST				180
+#define WLC_ENCRYPT_STRENGTH			181	
+#define WLC_DECRYPT_STATUS			182	
+#define WLC_GET_KEY_SEQ				183
+#define WLC_GET_SCAN_CHANNEL_TIME		184
+#define WLC_SET_SCAN_CHANNEL_TIME		185
+#define WLC_GET_SCAN_UNASSOC_TIME		186
+#define WLC_SET_SCAN_UNASSOC_TIME		187
+#define WLC_GET_SCAN_HOME_TIME			188
+#define WLC_SET_SCAN_HOME_TIME			189
+#define WLC_GET_SCAN_NPROBES			190
+#define WLC_SET_SCAN_NPROBES			191
+#define WLC_GET_PRB_RESP_TIMEOUT		192
+#define WLC_SET_PRB_RESP_TIMEOUT		193
+#define WLC_GET_ATTEN				194
+#define WLC_SET_ATTEN				195
+#define WLC_GET_SHMEM				196	
+#define WLC_SET_SHMEM				197	
+ 
+ 
+#define WLC_SET_WSEC_TEST			200
+#define WLC_SCB_DEAUTHENTICATE_FOR_REASON	201
+#define WLC_TKIP_COUNTERMEASURES		202
+#define WLC_GET_PIOMODE				203
+#define WLC_SET_PIOMODE				204
+#define WLC_SET_ASSOC_PREFER			205
+#define WLC_GET_ASSOC_PREFER			206
+#define WLC_SET_ROAM_PREFER			207
+#define WLC_GET_ROAM_PREFER			208
+#define WLC_SET_LED				209
+#define WLC_GET_LED				210
+#define WLC_GET_INTERFERENCE_MODE		211
+#define WLC_SET_INTERFERENCE_MODE		212
+#define WLC_GET_CHANNEL_QA			213
+#define WLC_START_CHANNEL_QA			214
+#define WLC_GET_CHANNEL_SEL			215
+#define WLC_START_CHANNEL_SEL			216
+#define WLC_GET_VALID_CHANNELS			217
+#define WLC_GET_FAKEFRAG			218
+#define WLC_SET_FAKEFRAG			219
+#define WLC_GET_PWROUT_PERCENTAGE		220
+#define WLC_SET_PWROUT_PERCENTAGE		221
+#define WLC_SET_BAD_FRAME_PREEMPT		222
+#define WLC_GET_BAD_FRAME_PREEMPT		223
+#define WLC_SET_LEAP_LIST			224
+#define WLC_GET_LEAP_LIST			225
+#define WLC_GET_CWMIN				226
+#define WLC_SET_CWMIN				227
+#define WLC_GET_CWMAX				228
+#define WLC_SET_CWMAX				229
+#define WLC_GET_WET				230
+#define WLC_SET_WET				231
+#define WLC_GET_PUB				232
+ 
+ 
+#define WLC_GET_KEY_PRIMARY			235
+#define WLC_SET_KEY_PRIMARY			236
+ 
+#define WLC_GET_ACI_ARGS			238
+#define WLC_SET_ACI_ARGS			239
+#define WLC_UNSET_CALLBACK			240
+#define WLC_SET_CALLBACK			241
+#define WLC_GET_RADAR				242
+#define WLC_SET_RADAR				243
+#define WLC_SET_SPECT_MANAGMENT			244
+#define WLC_GET_SPECT_MANAGMENT			245
+#define WLC_WDS_GET_REMOTE_HWADDR		246	
+#define WLC_WDS_GET_WPA_SUP			247
+#define WLC_SET_CS_SCAN_TIMER			248
+#define WLC_GET_CS_SCAN_TIMER			249
+#define WLC_MEASURE_REQUEST			250
+#define WLC_INIT				251
+#define WLC_SEND_QUIET				252
+#define WLC_KEEPALIVE			253
+#define WLC_SEND_PWR_CONSTRAINT			254
+#define WLC_UPGRADE_STATUS			255
+#define WLC_CURRENT_PWR				256
+#define WLC_GET_SCAN_PASSIVE_TIME		257
+#define WLC_SET_SCAN_PASSIVE_TIME		258
+#define WLC_LEGACY_LINK_BEHAVIOR		259
+#define WLC_GET_CHANNELS_IN_COUNTRY		260
+#define WLC_GET_COUNTRY_LIST			261
+#define WLC_GET_VAR				262	
+#define WLC_SET_VAR				263	
+#define WLC_NVRAM_GET				264	
+#define WLC_NVRAM_SET				265
+#define WLC_NVRAM_DUMP				266
+#define WLC_REBOOT				267
+#define WLC_SET_WSEC_PMK			268
+#define WLC_GET_AUTH_MODE			269
+#define WLC_SET_AUTH_MODE			270
+#define WLC_GET_WAKEENTRY			271
+#define WLC_SET_WAKEENTRY			272
+#define WLC_NDCONFIG_ITEM			273	
+#define WLC_NVOTPW				274
+#define WLC_OTPW				275
+#define WLC_IOV_BLOCK_GET			276
+#define WLC_IOV_MODULES_GET			277
+#define WLC_SOFT_RESET				278
+#define WLC_GET_ALLOW_MODE			279
+#define WLC_SET_ALLOW_MODE			280
+#define WLC_GET_DESIRED_BSSID			281
+#define WLC_SET_DESIRED_BSSID			282
+#define	WLC_DISASSOC_MYAP			283
+#define WLC_GET_NBANDS				284	
+#define WLC_GET_BANDSTATES			285	
+#define WLC_GET_WLC_BSS_INFO			286	
+#define WLC_GET_ASSOC_INFO			287	
+#define WLC_GET_OID_PHY				288	
+#define WLC_SET_OID_PHY				289	
+#define WLC_SET_ASSOC_TIME			290	
+#define WLC_GET_DESIRED_SSID			291	
+#define WLC_GET_CHANSPEC			292	
+#define WLC_GET_ASSOC_STATE			293	
+#define WLC_SET_PHY_STATE			294	
+#define WLC_GET_SCAN_PENDING			295	
+#define WLC_GET_SCANREQ_PENDING			296	
+#define WLC_GET_PREV_ROAM_REASON		297	
+#define WLC_SET_PREV_ROAM_REASON		298	
+#define WLC_GET_BANDSTATES_PI			299	
+#define WLC_GET_PHY_STATE			300	
+#define WLC_GET_BSS_WPA_RSN			301	
+#define WLC_GET_BSS_WPA2_RSN			302	
+#define WLC_GET_BSS_BCN_TS			303	
+#define WLC_GET_INT_DISASSOC			304	
+#define WLC_SET_NUM_PEERS			305     
+#define WLC_GET_NUM_BSS				306	
+#define WLC_LAST				307	
+
+
+
+#define WL_RADIO_SW_DISABLE		(1<<0)
+#define WL_RADIO_HW_DISABLE		(1<<1)
+#define WL_RADIO_MPC_DISABLE		(1<<2)
+#define WL_RADIO_COUNTRY_DISABLE	(1<<3)	
+
+
+#define WL_TXPWR_OVERRIDE	(1U<<31)
+
+#define WL_PHY_PAVARS_LEN	6	
+
+
+#define WL_DIAG_INTERRUPT			1	
+#define WL_DIAG_LOOPBACK			2	
+#define WL_DIAG_MEMORY				3	
+#define WL_DIAG_LED				4	
+#define WL_DIAG_REG				5	
+#define WL_DIAG_SROM				6	
+#define WL_DIAG_DMA				7	
+
+#define WL_DIAGERR_SUCCESS			0
+#define WL_DIAGERR_FAIL_TO_RUN			1	
+#define WL_DIAGERR_NOT_SUPPORTED		2	
+#define WL_DIAGERR_INTERRUPT_FAIL		3	
+#define WL_DIAGERR_LOOPBACK_FAIL		4	
+#define WL_DIAGERR_SROM_FAIL			5	
+#define WL_DIAGERR_SROM_BADCRC			6	
+#define WL_DIAGERR_REG_FAIL			7	
+#define WL_DIAGERR_MEMORY_FAIL			8	
+#define WL_DIAGERR_NOMEM			9	
+#define WL_DIAGERR_DMA_FAIL			10	
+
+#define WL_DIAGERR_MEMORY_TIMEOUT		11	
+#define WL_DIAGERR_MEMORY_BADPATTERN		12	
+
+
+#define	WLC_BAND_AUTO		0	
+#define	WLC_BAND_5G		1	
+#define	WLC_BAND_2G		2	
+#define	WLC_BAND_ALL		3	
+
+
+#define WL_CHAN_FREQ_RANGE_2G      0
+#define WL_CHAN_FREQ_RANGE_5GL     1
+#define WL_CHAN_FREQ_RANGE_5GM     2
+#define WL_CHAN_FREQ_RANGE_5GH     3
+
+
+#define	WLC_PHY_TYPE_A		0
+#define	WLC_PHY_TYPE_B		1
+#define	WLC_PHY_TYPE_G		2
+#define	WLC_PHY_TYPE_N		4
+#define	WLC_PHY_TYPE_LP		5
+#define	WLC_PHY_TYPE_SSN	6
+#define	WLC_PHY_TYPE_NULL	0xf
+
+
+#define WLC_MACMODE_DISABLED	0	
+#define WLC_MACMODE_DENY	1	
+#define WLC_MACMODE_ALLOW	2	
+
+
+#define GMODE_LEGACY_B		0
+#define GMODE_AUTO		1
+#define GMODE_ONLY		2
+#define GMODE_B_DEFERRED	3
+#define GMODE_PERFORMANCE	4
+#define GMODE_LRS		5
+#define GMODE_MAX		6
+
+
+#define WLC_PLCP_AUTO	-1
+#define WLC_PLCP_SHORT	0
+#define WLC_PLCP_LONG	1
+
+
+#define WLC_PROTECTION_AUTO		-1
+#define WLC_PROTECTION_OFF		0
+#define WLC_PROTECTION_ON		1
+#define WLC_PROTECTION_MMHDR_ONLY	2
+#define WLC_PROTECTION_CTS_ONLY		3
+
+
+#define WLC_PROTECTION_CTL_OFF		0
+#define WLC_PROTECTION_CTL_LOCAL	1
+#define WLC_PROTECTION_CTL_OVERLAP	2
+
+
+#define WLC_N_PROTECTION_OFF		0
+#define WLC_N_PROTECTION_OPTIONAL	1
+#define WLC_N_PROTECTION_20IN40		2
+#define WLC_N_PROTECTION_MIXEDMODE	3
+
+
+#define WLC_N_PREAMBLE_MIXEDMODE	0
+#define WLC_N_PREAMBLE_GF		1
+
+
+#define WLC_N_BW_20ALL			0
+#define WLC_N_BW_40ALL			1
+#define WLC_N_BW_20IN2G_40IN5G		2
+
+
+#define WLC_N_TXRX_CHAIN0		0
+#define WLC_N_TXRX_CHAIN1		1
+
+
+#define WLC_N_SGI_20			0x01
+#define WLC_N_SGI_40			0x02
+
+
+#define PM_OFF	0
+#define PM_MAX	1
+#define PM_FAST 2
+
+#define LISTEN_INTERVAL			10
+
+#define	INTERFERE_NONE	0	
+#define	NON_WLAN	1	
+#define	WLAN_MANUAL	2	
+#define	WLAN_AUTO	3	
+#define AUTO_ACTIVE	(1 << 7) 
+
+typedef struct wl_aci_args {
+	int enter_aci_thresh; 
+	int exit_aci_thresh; 
+	int usec_spin; 
+	int glitch_delay; 
+	uint16 nphy_adcpwr_enter_thresh;	
+	uint16 nphy_adcpwr_exit_thresh;	
+	uint16 nphy_repeat_ctr;		
+	uint16 nphy_num_samples;	
+	uint16 nphy_undetect_window_sz;	
+	uint16 nphy_b_energy_lo_aci;	
+	uint16 nphy_b_energy_md_aci;	
+	uint16 nphy_b_energy_hi_aci;	
+} wl_aci_args_t;
+
+#define WL_ACI_ARGS_LEGACY_LENGTH	16	
+
+
+
+#define WL_ERROR_VAL		0x00000001
+#define WL_TRACE_VAL		0x00000002
+#define WL_PRHDRS_VAL		0x00000004
+#define WL_PRPKT_VAL		0x00000008
+#define WL_INFORM_VAL		0x00000010
+#define WL_TMP_VAL		0x00000020
+#define WL_OID_VAL		0x00000040
+#define WL_RATE_VAL		0x00000080
+#define WL_ASSOC_VAL		0x00000100
+#define WL_PRUSR_VAL		0x00000200
+#define WL_PS_VAL		0x00000400
+#define WL_TXPWR_VAL		0x00000800
+#define WL_PORT_VAL		0x00001000
+#define WL_DUAL_VAL		0x00002000
+#define WL_WSEC_VAL		0x00004000
+#define WL_WSEC_DUMP_VAL	0x00008000
+#define WL_LOG_VAL		0x00010000
+#define WL_NRSSI_VAL		0x00020000
+#define WL_LOFT_VAL		0x00040000
+#define WL_REGULATORY_VAL	0x00080000
+#define WL_PHYCAL_VAL		0x00100000
+#define WL_RADAR_VAL		0x00200000
+#define WL_MPC_VAL		0x00400000
+#define WL_APSTA_VAL		0x00800000
+#define WL_DFS_VAL		0x01000000
+#define WL_BA_VAL		0x02000000
+#define WL_MBSS_VAL		0x04000000
+#define WL_CAC_VAL		0x08000000
+#define WL_AMSDU_VAL		0x10000000
+#define WL_AMPDU_VAL		0x20000000
+#define WL_FFPLD_VAL		0x40000000
+
+
+#define WL_DPT_VAL 		0x00000001
+#define WL_SCAN_VAL		0x00000002
+#define WL_WOWL_VAL		0x00000004
+#define WL_COEX_VAL		0x00000008
+#define WL_RTDC_VAL		0x00000010
+#define WL_BTA_VAL		0x00000040
+
+
+#define	WL_LED_NUMGPIO		16	
+
+
+#define	WL_LED_OFF		0		
+#define	WL_LED_ON		1		
+#define	WL_LED_ACTIVITY		2		
+#define	WL_LED_RADIO		3		
+#define	WL_LED_ARADIO		4		
+#define	WL_LED_BRADIO		5		
+#define	WL_LED_BGMODE		6		
+#define	WL_LED_WI1		7
+#define	WL_LED_WI2		8
+#define	WL_LED_WI3		9
+#define	WL_LED_ASSOC		10		
+#define	WL_LED_INACTIVE		11		
+#define	WL_LED_ASSOCACT		12		
+#define	WL_LED_NUMBEHAVIOR	13
+
+
+#define	WL_LED_BEH_MASK		0x7f		
+#define	WL_LED_AL_MASK		0x80		
+
+
+#define WL_NUMCHANNELS		64
+#define WL_NUMCHANSPECS		100
+
+
+#define WL_WDS_WPA_ROLE_AUTH	0	
+#define WL_WDS_WPA_ROLE_SUP	1	
+#define WL_WDS_WPA_ROLE_AUTO	255	
+
+
+#define WL_EVENTING_MASK_LEN	16
+
+
+#define VNDR_IE_CMD_LEN		4	
+
+
+#define VNDR_IE_BEACON_FLAG	0x1
+#define VNDR_IE_PRBRSP_FLAG	0x2
+#define VNDR_IE_ASSOCRSP_FLAG	0x4
+#define VNDR_IE_AUTHRSP_FLAG	0x8
+#define VNDR_IE_PRBREQ_FLAG	0x10
+#define VNDR_IE_ASSOCREQ_FLAG	0x20
+#define VNDR_IE_CUSTOM_FLAG		0x100 
+
+#define VNDR_IE_INFO_HDR_LEN	(sizeof(uint32))
+
+typedef struct {
+	uint32 pktflag;			
+	vndr_ie_t vndr_ie_data;		
+} vndr_ie_info_t;
+
+typedef struct {
+	int iecount;			
+	vndr_ie_info_t vndr_ie_list[1];	
+} vndr_ie_buf_t;
+
+typedef struct {
+	char cmd[VNDR_IE_CMD_LEN];	
+	vndr_ie_buf_t vndr_ie_buffer;	
+} vndr_ie_setbuf_t;
+
+
+
+
+#define WL_JOIN_PREF_RSSI	1	
+#define WL_JOIN_PREF_WPA	2	
+#define WL_JOIN_PREF_BAND	3	
+
+
+#define WLJP_BAND_ASSOC_PREF	255	
+
+
+#define WL_WPA_ACP_MCS_ANY	"\x00\x00\x00\x00"
+
+struct tsinfo_arg {
+	uint8 octets[3];
+};
+
+
+#define	NFIFO			6	
+
+#define	WL_CNT_T_VERSION	5	
+#define	WL_CNT_EXT_T_VERSION	1
+
+typedef struct {
+	uint16	version;	
+	uint16	length;		
+
+	
+	uint32	txframe;	
+	uint32	txbyte;		
+	uint32	txretrans;	
+	uint32	txerror;	
+	uint32	txctl;		
+	uint32	txprshort;	
+	uint32	txserr;		
+	uint32	txnobuf;	
+	uint32	txnoassoc;	
+	uint32	txrunt;		
+	uint32	txchit;		
+	uint32	txcmiss;	
+
+	
+	uint32	txuflo;		
+	uint32	txphyerr;	
+	uint32	txphycrs;
+
+	
+	uint32	rxframe;	
+	uint32	rxbyte;		
+	uint32	rxerror;	
+	uint32	rxctl;		
+	uint32	rxnobuf;	
+	uint32	rxnondata;	
+	uint32	rxbadds;	
+	uint32	rxbadcm;	
+	uint32	rxfragerr;	
+	uint32	rxrunt;		
+	uint32	rxgiant;	
+	uint32	rxnoscb;	
+	uint32	rxbadproto;	
+	uint32	rxbadsrcmac;	
+	uint32	rxbadda;	
+	uint32	rxfilter;	
+
+	
+	uint32	rxoflo;		
+	uint32	rxuflo[NFIFO];	
+
+	uint32	d11cnt_txrts_off;	
+	uint32	d11cnt_rxcrc_off;	
+	uint32	d11cnt_txnocts_off;	
+
+	
+	uint32	dmade;		
+	uint32	dmada;		
+	uint32	dmape;		
+	uint32	reset;		
+	uint32	tbtt;		
+	uint32	txdmawar;
+	uint32	pkt_callback_reg_fail;	
+
+	
+	uint32	txallfrm;	
+	uint32	txrtsfrm;	
+	uint32	txctsfrm;	
+	uint32	txackfrm;	
+	uint32	txdnlfrm;	
+	uint32	txbcnfrm;	
+	uint32	txfunfl[8];	
+	uint32	txtplunfl;	
+	uint32	txphyerror;	
+	uint32	rxfrmtoolong;	
+	uint32	rxfrmtooshrt;	
+	uint32	rxinvmachdr;	
+	uint32	rxbadfcs;	
+	uint32	rxbadplcp;	
+	uint32	rxcrsglitch;	
+	uint32	rxstrt;		
+	uint32	rxdfrmucastmbss; 
+	uint32	rxmfrmucastmbss; 
+	uint32	rxcfrmucast;	
+	uint32	rxrtsucast;	
+	uint32	rxctsucast;	
+	uint32	rxackucast;	
+	uint32	rxdfrmocast;	
+	uint32	rxmfrmocast;	
+	uint32	rxcfrmocast;	
+	uint32	rxrtsocast;	
+	uint32	rxctsocast;	
+	uint32	rxdfrmmcast;	
+	uint32	rxmfrmmcast;	
+	uint32	rxcfrmmcast;	
+	uint32	rxbeaconmbss;	
+	uint32	rxdfrmucastobss; 
+	uint32	rxbeaconobss;	
+	uint32	rxrsptmout;	
+	uint32	bcntxcancl;	
+	uint32	rxf0ovfl;	
+	uint32	rxf1ovfl;	
+	uint32	rxf2ovfl;	
+	uint32	txsfovfl;	
+	uint32	pmqovfl;	
+	uint32	rxcgprqfrm;	
+	uint32	rxcgprsqovfl;	
+	uint32	txcgprsfail;	
+	uint32	txcgprssuc;	
+	uint32	prs_timeout;	
+	uint32	rxnack;
+	uint32	frmscons;
+	uint32	txnack;
+	uint32	txglitch_nack;	
+	uint32	txburst;	
+
+	
+	uint32	txfrag;		
+	uint32	txmulti;	
+	uint32	txfail;		
+	uint32	txretry;	
+	uint32	txretrie;	
+	uint32	rxdup;		
+	uint32	txrts;		
+	uint32	txnocts;	
+	uint32	txnoack;	
+	uint32	rxfrag;		
+	uint32	rxmulti;	
+	uint32	rxcrc;		
+	uint32	txfrmsnt;	
+	uint32	rxundec;	
+
+	
+	uint32	tkipmicfaill;	
+	uint32	tkipcntrmsr;	
+	uint32	tkipreplay;	
+	uint32	ccmpfmterr;	
+	uint32	ccmpreplay;	
+	uint32	ccmpundec;	
+	uint32	fourwayfail;	
+	uint32	wepundec;	
+	uint32	wepicverr;	
+	uint32	decsuccess;	
+	uint32	tkipicverr;	
+	uint32	wepexcluded;	
+
+	uint32	txchanrej;	
+	uint32	psmwds;		
+	uint32	phywatchdog;	
+
+	
+	uint32	prq_entries_handled;	
+	uint32	prq_undirected_entries;	
+	uint32	prq_bad_entries;	
+	uint32	atim_suppress_count;	
+	uint32	bcn_template_not_ready;	
+	uint32	bcn_template_not_ready_done; 
+	uint32	late_tbtt_dpc;	
+
+	
+	uint32  rx1mbps;	
+	uint32  rx2mbps;	
+	uint32  rx5mbps5;	
+	uint32  rx6mbps;	
+	uint32  rx9mbps;	
+	uint32  rx11mbps;	
+	uint32  rx12mbps;	
+	uint32  rx18mbps;	
+	uint32  rx24mbps;	
+	uint32  rx36mbps;	
+	uint32  rx48mbps;	
+	uint32  rx54mbps;	
+	uint32  rx108mbps; 	
+	uint32  rx162mbps;	
+	uint32  rx216mbps;	
+	uint32  rx270mbps;	
+	uint32  rx324mbps;	
+	uint32  rx378mbps;	
+	uint32  rx432mbps;	
+	uint32  rx486mbps;	
+	uint32  rx540mbps;	
+	
+	uint32	pktengrxducast; 
+	uint32	pktengrxdmcast; 
+} wl_cnt_t;
+
+typedef struct {
+	uint16	version;	
+	uint16	length;		
+
+	uint32 rxampdu_sgi;	
+	uint32 rxampdu_stbc; 
+	uint32 rxmpdu_sgi;	
+	uint32 rxmpdu_stbc;  
+	uint32	rxmcs0_40M;  
+	uint32	rxmcs1_40M;  
+	uint32	rxmcs2_40M;  
+	uint32	rxmcs3_40M;  
+	uint32	rxmcs4_40M;  
+	uint32	rxmcs5_40M;  
+	uint32	rxmcs6_40M;  
+	uint32	rxmcs7_40M;  
+	uint32	rxmcs32_40M;  
+
+	uint32	txfrmsnt_20Mlo;  
+	uint32	txfrmsnt_20Mup;  
+	uint32	txfrmsnt_40M;   
+
+	uint32 rx_20ul;
+} wl_cnt_ext_t;
+
+#define	WL_RXDIV_STATS_T_VERSION	1	
+typedef struct {
+	uint16	version;	
+	uint16	length;		
+
+	uint32 rxant[4];	
+} wl_rxdiv_stats_t;
+
+#define	WL_DELTA_STATS_T_VERSION	1	
+
+typedef struct {
+	uint16 version;     
+	uint16 length;      
+
+	
+	uint32 txframe;     
+	uint32 txbyte;      
+	uint32 txretrans;   
+	uint32 txfail;      
+
+	
+	uint32 rxframe;     
+	uint32 rxbyte;      
+
+	
+	uint32  rx1mbps;	
+	uint32  rx2mbps;	
+	uint32  rx5mbps5;	
+	uint32  rx6mbps;	
+	uint32  rx9mbps;	
+	uint32  rx11mbps;	
+	uint32  rx12mbps;	
+	uint32  rx18mbps;	
+	uint32  rx24mbps;	
+	uint32  rx36mbps;	
+	uint32  rx48mbps;	
+	uint32  rx54mbps;	
+	uint32  rx108mbps; 	
+	uint32  rx162mbps;	
+	uint32  rx216mbps;	
+	uint32  rx270mbps;	
+	uint32  rx324mbps;	
+	uint32  rx378mbps;	
+	uint32  rx432mbps;	
+	uint32  rx486mbps;	
+	uint32  rx540mbps;	
+} wl_delta_stats_t;
+
+#define WL_WME_CNT_VERSION	1	
+
+typedef struct {
+	uint32 packets;
+	uint32 bytes;
+} wl_traffic_stats_t;
+
+typedef struct {
+	uint16	version;	
+	uint16	length;		
+
+	wl_traffic_stats_t tx[AC_COUNT];	
+	wl_traffic_stats_t tx_failed[AC_COUNT];	
+	wl_traffic_stats_t rx[AC_COUNT];	
+	wl_traffic_stats_t rx_failed[AC_COUNT];	
+
+	wl_traffic_stats_t forward[AC_COUNT];	
+
+	wl_traffic_stats_t tx_expired[AC_COUNT];	
+
+} wl_wme_cnt_t;
+
+
+
+#define WLC_ROAM_TRIGGER_DEFAULT	0 
+#define WLC_ROAM_TRIGGER_BANDWIDTH	1 
+#define WLC_ROAM_TRIGGER_DISTANCE	2 
+#define WLC_ROAM_TRIGGER_MAX_VALUE	2 
+
+
+enum {
+	PFN_LIST_ORDER,
+	PFN_RSSI
+};
+
+enum {
+	DISABLE,
+	ENABLE
+};
+
+#define SORT_CRITERIA_BIT		0
+#define AUTO_NET_SWITCH_BIT		1
+#define ENABLE_BKGRD_SCAN_BIT	2
+#define IMMEDIATE_SCAN_BIT		3
+#define	AUTO_CONNECT_BIT		4
+#define	ENABLE_BD_SCAN_BIT		5
+#define ENABLE_ADAPTSCAN_BIT	6
+
+#define SORT_CRITERIA_MASK		0x01
+#define AUTO_NET_SWITCH_MASK	0x02
+#define ENABLE_BKGRD_SCAN_MASK	0x04
+#define IMMEDIATE_SCAN_MASK		0x08
+#define	AUTO_CONNECT_MASK		0x10
+#define ENABLE_BD_SCAN_MASK		0x20
+#define ENABLE_ADAPTSCAN_MASK	0x40
+
+#define PFN_VERSION			1
+
+#define MAX_PFN_LIST_COUNT	16
+
+
+typedef struct wl_pfn_param {
+	int32 version;			
+	int32 scan_freq;		
+	int32 lost_network_timeout;	
+	int16 flags;			
+	int16 rssi_margin;		
+	int32  repeat_scan;
+	int32  max_freq_adjust;
+} wl_pfn_param_t;
+
+typedef struct wl_pfn {
+	wlc_ssid_t		ssid;			
+	int32			bss_type;		
+	int32			infra;			
+	int32			auth;			
+	uint32			wpa_auth;		
+	int32			wsec;			
+} wl_pfn_t;
+
+#define PNO_SCAN_MAX_FW		508*1000
+#define PNO_SCAN_MAX_FW_SEC	PNO_SCAN_MAX_FW/1000
+#define PNO_SCAN_MIN_FW_SEC	10
+
+
+#define TOE_TX_CSUM_OL		0x00000001
+#define TOE_RX_CSUM_OL		0x00000002
+
+
+#define TOE_ERRTEST_TX_CSUM	0x00000001
+#define TOE_ERRTEST_RX_CSUM	0x00000002
+#define TOE_ERRTEST_RX_CSUM2	0x00000004
+
+struct toe_ol_stats_t {
+	
+	uint32 tx_summed;
+
+	
+	uint32 tx_iph_fill;
+	uint32 tx_tcp_fill;
+	uint32 tx_udp_fill;
+	uint32 tx_icmp_fill;
+
+	
+	uint32 rx_iph_good;
+	uint32 rx_iph_bad;
+	uint32 rx_tcp_good;
+	uint32 rx_tcp_bad;
+	uint32 rx_udp_good;
+	uint32 rx_udp_bad;
+	uint32 rx_icmp_good;
+	uint32 rx_icmp_bad;
+
+	
+	uint32 tx_tcp_errinj;
+	uint32 tx_udp_errinj;
+	uint32 tx_icmp_errinj;
+
+	
+	uint32 rx_tcp_errinj;
+	uint32 rx_udp_errinj;
+	uint32 rx_icmp_errinj;
+};
+
+
+#define ARP_OL_AGENT		0x00000001
+#define ARP_OL_SNOOP		0x00000002
+#define ARP_OL_HOST_AUTO_REPLY	0x00000004
+#define ARP_OL_PEER_AUTO_REPLY	0x00000008
+
+
+#define ARP_ERRTEST_REPLY_PEER	0x1
+#define ARP_ERRTEST_REPLY_HOST	0x2
+
+#define ARP_MULTIHOMING_MAX	8	
+
+
+struct arp_ol_stats_t {
+	uint32  host_ip_entries;	
+	uint32  host_ip_overflow;	
+
+	uint32  arp_table_entries;	
+	uint32  arp_table_overflow;	
+
+	uint32  host_request;		
+	uint32  host_reply;		
+	uint32  host_service;		
+
+	uint32  peer_request;		
+	uint32  peer_request_drop;	
+	uint32  peer_reply;		
+	uint32  peer_reply_drop;	
+	uint32  peer_service;		
+};
+
+
+
+
+
+typedef struct wl_keep_alive_pkt {
+	uint32	period_msec;	
+	uint16	len_bytes;	
+	uint8	data[1];	
+} wl_keep_alive_pkt_t;
+
+#define WL_KEEP_ALIVE_FIXED_LEN		OFFSETOF(wl_keep_alive_pkt_t, data)
+
+
+
+
+
+typedef enum wl_pkt_filter_type {
+	WL_PKT_FILTER_TYPE_PATTERN_MATCH	
+} wl_pkt_filter_type_t;
+
+#define WL_PKT_FILTER_TYPE wl_pkt_filter_type_t
+
+
+typedef struct wl_pkt_filter_pattern {
+	uint32	offset;		
+	uint32	size_bytes;	
+	uint8   mask_and_pattern[1]; 
+} wl_pkt_filter_pattern_t;
+
+
+typedef struct wl_pkt_filter {
+	uint32	id;		
+	uint32	type;		
+	uint32	negate_match;	
+	union {			
+		wl_pkt_filter_pattern_t pattern;	
+	} u;
+} wl_pkt_filter_t;
+
+#define WL_PKT_FILTER_FIXED_LEN		  OFFSETOF(wl_pkt_filter_t, u)
+#define WL_PKT_FILTER_PATTERN_FIXED_LEN	  OFFSETOF(wl_pkt_filter_pattern_t, mask_and_pattern)
+
+
+typedef struct wl_pkt_filter_enable {
+	uint32	id;		
+	uint32	enable;		
+} wl_pkt_filter_enable_t;
+
+
+typedef struct wl_pkt_filter_list {
+	uint32	num;		
+	wl_pkt_filter_t	filter[1];	
+} wl_pkt_filter_list_t;
+
+#define WL_PKT_FILTER_LIST_FIXED_LEN	  OFFSETOF(wl_pkt_filter_list_t, filter)
+
+
+typedef struct wl_pkt_filter_stats {
+	uint32	num_pkts_matched;	
+	uint32	num_pkts_forwarded;	
+	uint32	num_pkts_discarded;	
+} wl_pkt_filter_stats_t;
+
+
+typedef struct wl_seq_cmd_ioctl {
+	uint32 cmd;		
+	uint32 len;		
+} wl_seq_cmd_ioctl_t;
+
+#define WL_SEQ_CMD_ALIGN_BYTES	4
+
+
+#define WL_SEQ_CMDS_GET_IOCTL_FILTER(cmd) \
+	(((cmd) == WLC_GET_MAGIC)		|| \
+	 ((cmd) == WLC_GET_VERSION)		|| \
+	 ((cmd) == WLC_GET_AP)			|| \
+	 ((cmd) == WLC_GET_INSTANCE))
+
+
+
+#define WL_PKTENG_PER_TX_START			0x01
+#define WL_PKTENG_PER_TX_STOP			0x02
+#define WL_PKTENG_PER_RX_START			0x04
+#define WL_PKTENG_PER_RX_WITH_ACK_START 	0x05
+#define WL_PKTENG_PER_TX_WITH_ACK_START 	0x06
+#define WL_PKTENG_PER_RX_STOP			0x08
+#define WL_PKTENG_PER_MASK			0xff
+
+#define WL_PKTENG_SYNCHRONOUS			0x100	
+
+typedef struct wl_pkteng {
+	uint32 flags;
+	uint32 delay;			
+	uint32 nframes;			
+	uint32 length;			
+	uint8  seqno;			
+	struct ether_addr dest;		
+	struct ether_addr src;		
+} wl_pkteng_t;
+
+#define NUM_80211b_RATES	4
+#define NUM_80211ag_RATES	8
+#define NUM_80211n_RATES	32
+#define NUM_80211_RATES		(NUM_80211b_RATES+NUM_80211ag_RATES+NUM_80211n_RATES)
+typedef struct wl_pkteng_stats {
+	uint32 lostfrmcnt;		
+	int32 rssi;			
+	int32 snr;			
+	uint16 rxpktcnt[NUM_80211_RATES+1];
+} wl_pkteng_stats_t;
+
+#define WL_WOWL_MAGIC	(1 << 0)	
+#define WL_WOWL_NET	(1 << 1)	
+#define WL_WOWL_DIS	(1 << 2)	
+#define WL_WOWL_RETR	(1 << 3)	
+#define WL_WOWL_BCN	(1 << 4)	
+#define WL_WOWL_TST	(1 << 5)	
+#define WL_WOWL_BCAST	(1 << 15)	
+
+#define MAGIC_PKT_MINLEN 102	
+
+typedef struct {
+	uint masksize;		
+	uint offset;		
+	uint patternoffset;	
+	uint patternsize;	
+	
+	
+} wl_wowl_pattern_t;
+
+typedef struct {
+	uint			count;
+	wl_wowl_pattern_t	pattern[1];
+} wl_wowl_pattern_list_t;
+
+typedef struct {
+	uint8	pci_wakeind;	
+	uint16	ucode_wakeind;	
+} wl_wowl_wakeind_t;
+
+
+typedef struct wl_txrate_class {
+	uint8		init_rate;
+	uint8		min_rate;
+	uint8		max_rate;
+} wl_txrate_class_t;
+
+
+
+
+#define WLC_OBSS_SCAN_PASSIVE_DWELL_DEFAULT		100	
+#define WLC_OBSS_SCAN_PASSIVE_DWELL_MIN			5	
+#define WLC_OBSS_SCAN_PASSIVE_DWELL_MAX			1000	
+#define WLC_OBSS_SCAN_ACTIVE_DWELL_DEFAULT		20	
+#define WLC_OBSS_SCAN_ACTIVE_DWELL_MIN			10	
+#define WLC_OBSS_SCAN_ACTIVE_DWELL_MAX			1000	
+#define WLC_OBSS_SCAN_WIDTHSCAN_INTERVAL_DEFAULT	300	
+#define WLC_OBSS_SCAN_WIDTHSCAN_INTERVAL_MIN		10	
+#define WLC_OBSS_SCAN_WIDTHSCAN_INTERVAL_MAX		900	
+#define WLC_OBSS_SCAN_CHANWIDTH_TRANSITION_DLY_DEFAULT	5
+#define WLC_OBSS_SCAN_CHANWIDTH_TRANSITION_DLY_MIN	5
+#define WLC_OBSS_SCAN_CHANWIDTH_TRANSITION_DLY_MAX	100
+#define WLC_OBSS_SCAN_PASSIVE_TOTAL_PER_CHANNEL_DEFAULT	200	
+#define WLC_OBSS_SCAN_PASSIVE_TOTAL_PER_CHANNEL_MIN	200	
+#define WLC_OBSS_SCAN_PASSIVE_TOTAL_PER_CHANNEL_MAX	10000	
+#define WLC_OBSS_SCAN_ACTIVE_TOTAL_PER_CHANNEL_DEFAULT	20	
+#define WLC_OBSS_SCAN_ACTIVE_TOTAL_PER_CHANNEL_MIN	20	
+#define WLC_OBSS_SCAN_ACTIVE_TOTAL_PER_CHANNEL_MAX	10000	
+#define WLC_OBSS_SCAN_ACTIVITY_THRESHOLD_DEFAULT	25	
+#define WLC_OBSS_SCAN_ACTIVITY_THRESHOLD_MIN		0	
+#define WLC_OBSS_SCAN_ACTIVITY_THRESHOLD_MAX		100	
+
+
+typedef struct wl_obss_scan_arg {
+	int16	passive_dwell;
+	int16	active_dwell;
+	int16	bss_widthscan_interval;
+	int16	passive_total;
+	int16	active_total;
+	int16	chanwidth_transition_delay;
+	int16	activity_threshold;
+} wl_obss_scan_arg_t;
+#define WL_OBSS_SCAN_PARAM_LEN	sizeof(wl_obss_scan_arg_t)
+#define WL_MIN_NUM_OBSS_SCAN_ARG 7	
+
+#define WL_COEX_INFO_MASK		0x07
+#define WL_COEX_INFO_REQ		0x01
+#define	WL_COEX_40MHZ_INTOLERANT	0x02
+#define	WL_COEX_WIDTH20			0x04
+
+typedef struct wl_action_obss_coex_req {
+	uint8 info;
+	uint8 num;
+	uint8 ch_list[1];
+} wl_action_obss_coex_req_t;
+
+
+#define MAX_RSSI_LEVELS 8
+
+
+typedef struct wl_rssi_event {
+	
+	uint32 rate_limit_msec;
+	
+	uint8 num_rssi_levels;
+	
+	int8 rssi_levels[MAX_RSSI_LEVELS];
+} wl_rssi_event_t;
+
+
+
+#define WLFEATURE_DISABLE_11N		0x00000001
+#define WLFEATURE_DISABLE_11N_STBC_TX	0x00000002
+#define WLFEATURE_DISABLE_11N_STBC_RX	0x00000004
+#define WLFEATURE_DISABLE_11N_SGI_TX	0x00000008
+#define WLFEATURE_DISABLE_11N_SGI_RX	0x00000010
+#define WLFEATURE_DISABLE_11N_AMPDU_TX	0x00000020
+#define WLFEATURE_DISABLE_11N_AMPDU_RX	0x00000040
+#define WLFEATURE_DISABLE_11N_GF	0x00000080
+
+
+
+#include <packed_section_end.h>
+
+
+#include <packed_section_start.h>
+
+
+typedef BWL_PRE_PACKED_STRUCT struct sta_prbreq_wps_ie_hdr {
+	struct ether_addr staAddr;
+	uint16 ieLen;
+} BWL_POST_PACKED_STRUCT sta_prbreq_wps_ie_hdr_t;
+
+typedef BWL_PRE_PACKED_STRUCT struct sta_prbreq_wps_ie_data {
+	sta_prbreq_wps_ie_hdr_t hdr;
+	uint8 ieData[1];
+} BWL_POST_PACKED_STRUCT sta_prbreq_wps_ie_data_t;
+
+typedef BWL_PRE_PACKED_STRUCT struct sta_prbreq_wps_ie_list {
+	uint32 totLen;
+	uint8 ieDataList[1];
+} BWL_POST_PACKED_STRUCT sta_prbreq_wps_ie_list_t;
+
+
+#include <packed_section_end.h>
+
+#endif 
diff --git a/drivers/net/wireless/bcm4329/linux_osl.c b/drivers/net/wireless/bcm4329/linux_osl.c
new file mode 100644
index 0000000..cf72a07
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/linux_osl.c
@@ -0,0 +1,625 @@
+/*
+ * Linux OS Independent Layer
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: linux_osl.c,v 1.125.12.3.8.7 2010/05/04 21:10:04 Exp $
+ */
+
+
+#define LINUX_OSL
+
+#include <typedefs.h>
+#include <bcmendian.h>
+#include <linuxver.h>
+#include <bcmdefs.h>
+#include <osl.h>
+#include <bcmutils.h>
+#include <linux/delay.h>
+#include <pcicfg.h>
+#include <linux/mutex.h>
+
+#define PCI_CFG_RETRY 		10
+
+#define OS_HANDLE_MAGIC		0x1234abcd	
+#define BCM_MEM_FILENAME_LEN 	24		
+
+#ifdef DHD_USE_STATIC_BUF
+#define MAX_STATIC_BUF_NUM 16
+#define STATIC_BUF_SIZE	(PAGE_SIZE*2)
+#define STATIC_BUF_TOTAL_LEN (MAX_STATIC_BUF_NUM*STATIC_BUF_SIZE)
+typedef struct bcm_static_buf {
+	struct mutex static_sem;
+	unsigned char *buf_ptr;
+	unsigned char buf_use[MAX_STATIC_BUF_NUM];
+} bcm_static_buf_t;
+
+static bcm_static_buf_t *bcm_static_buf = 0;
+
+#define MAX_STATIC_PKT_NUM 8
+typedef struct bcm_static_pkt {
+	struct sk_buff *skb_4k[MAX_STATIC_PKT_NUM];
+	struct sk_buff *skb_8k[MAX_STATIC_PKT_NUM];
+	struct mutex osl_pkt_sem;
+	unsigned char pkt_use[MAX_STATIC_PKT_NUM*2];
+} bcm_static_pkt_t;
+static bcm_static_pkt_t *bcm_static_skb = 0;
+
+#endif 
+typedef struct bcm_mem_link {
+	struct bcm_mem_link *prev;
+	struct bcm_mem_link *next;
+	uint	size;
+	int	line;
+	char	file[BCM_MEM_FILENAME_LEN];
+} bcm_mem_link_t;
+
+struct osl_info {
+	osl_pubinfo_t pub;
+	uint magic;
+	void *pdev;
+	uint malloced;
+	uint failed;
+	uint bustype;
+	bcm_mem_link_t *dbgmem_list;
+};
+
+static int16 linuxbcmerrormap[] =
+{	0, 			
+	-EINVAL,		
+	-EINVAL,		
+	-EINVAL,		
+	-EINVAL,		
+	-EINVAL,		
+	-EINVAL,		
+	-EINVAL,		
+	-EINVAL,		
+	-EINVAL,		
+	-EINVAL,		
+	-EINVAL, 		
+	-EINVAL, 		
+	-EINVAL, 		
+	-E2BIG,			
+	-E2BIG,			
+	-EBUSY, 		
+	-EINVAL, 		
+	-EINVAL, 		
+	-EINVAL, 		
+	-EINVAL, 		
+	-EFAULT, 		
+	-ENOMEM, 		
+	-EOPNOTSUPP,		
+	-EMSGSIZE,		
+	-EINVAL,		
+	-EPERM,			
+	-ENOMEM, 		
+	-EINVAL, 		
+	-ERANGE, 		
+	-EINVAL, 		
+	-EINVAL, 		
+	-EINVAL, 		
+	-EINVAL, 		
+	-EINVAL,		
+	-EIO,			
+	-ENODEV,		
+	-EINVAL,		
+	-EIO,			
+	-EIO,			
+	-EINVAL,		
+	-EINVAL,		
+
+
+
+#if BCME_LAST != -41
+#error "You need to add a OS error translation in the linuxbcmerrormap \
+	for new error code defined in bcmutils.h"
+#endif 
+};
+
+
+int
+osl_error(int bcmerror)
+{
+	if (bcmerror > 0)
+		bcmerror = 0;
+	else if (bcmerror < BCME_LAST)
+		bcmerror = BCME_ERROR;
+
+	
+	return linuxbcmerrormap[-bcmerror];
+}
+
+void * dhd_os_prealloc(int section, unsigned long size);
+osl_t *
+osl_attach(void *pdev, uint bustype, bool pkttag)
+{
+	osl_t *osh;
+	gfp_t flags;
+
+	flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL;
+	osh = kmalloc(sizeof(osl_t), flags);
+	ASSERT(osh);
+
+	bzero(osh, sizeof(osl_t));
+
+	
+	ASSERT(ABS(BCME_LAST) == (ARRAYSIZE(linuxbcmerrormap) - 1));
+
+	osh->magic = OS_HANDLE_MAGIC;
+	osh->malloced = 0;
+	osh->failed = 0;
+	osh->dbgmem_list = NULL;
+	osh->pdev = pdev;
+	osh->pub.pkttag = pkttag;
+	osh->bustype = bustype;
+
+	switch (bustype) {
+		case PCI_BUS:
+		case SI_BUS:
+		case PCMCIA_BUS:
+			osh->pub.mmbus = TRUE;
+			break;
+		case JTAG_BUS:
+		case SDIO_BUS:
+		case USB_BUS:
+		case SPI_BUS:
+			osh->pub.mmbus = FALSE;
+			break;
+		default:
+			ASSERT(FALSE);
+			break;
+	}
+
+#ifdef DHD_USE_STATIC_BUF
+
+
+	if (!bcm_static_buf) {
+		if (!(bcm_static_buf = (bcm_static_buf_t *)dhd_os_prealloc(3, STATIC_BUF_SIZE+
+			STATIC_BUF_TOTAL_LEN))) {
+			printk("can not alloc static buf!\n");
+		}
+		else {
+			/* printk("alloc static buf at %x!\n", (unsigned int)bcm_static_buf); */
+		}
+		
+		mutex_init(&bcm_static_buf->static_sem);
+
+		
+		bcm_static_buf->buf_ptr = (unsigned char *)bcm_static_buf + STATIC_BUF_SIZE;
+
+	}
+	
+	if (!bcm_static_skb)
+	{
+		int i;
+		void *skb_buff_ptr = 0;
+		bcm_static_skb = (bcm_static_pkt_t *)((char *)bcm_static_buf + 2048);
+		skb_buff_ptr = dhd_os_prealloc(4, 0);
+
+		bcopy(skb_buff_ptr, bcm_static_skb, sizeof(struct sk_buff *)*16);
+		for (i = 0; i < MAX_STATIC_PKT_NUM*2; i++)
+			bcm_static_skb->pkt_use[i] = 0;
+
+		mutex_init(&bcm_static_skb->osl_pkt_sem);
+	}
+#endif 
+	return osh;
+}
+
+void
+osl_detach(osl_t *osh)
+{
+	if (osh == NULL)
+		return;
+
+#ifdef DHD_USE_STATIC_BUF
+	if (bcm_static_buf) {
+		bcm_static_buf = 0;
+	}
+	if (bcm_static_skb) {
+		bcm_static_skb = 0;
+	}
+#endif 
+	ASSERT(osh->magic == OS_HANDLE_MAGIC);
+	kfree(osh);
+}
+
+
+void*
+osl_pktget(osl_t *osh, uint len)
+{
+	struct sk_buff *skb;
+	gfp_t flags;
+
+	flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL;
+	if ((skb = __dev_alloc_skb(len, flags))) {
+		skb_put(skb, len);
+		skb->priority = 0;
+
+
+		osh->pub.pktalloced++;
+	}
+
+	return ((void*) skb);
+}
+
+
+void
+osl_pktfree(osl_t *osh, void *p, bool send)
+{
+	struct sk_buff *skb, *nskb;
+
+	skb = (struct sk_buff*) p;
+
+	if (send && osh->pub.tx_fn)
+		osh->pub.tx_fn(osh->pub.tx_ctx, p, 0);
+
+	
+	while (skb) {
+		nskb = skb->next;
+		skb->next = NULL;
+
+
+		if (skb->destructor) {
+			
+			dev_kfree_skb_any(skb);
+		} else {
+			
+			dev_kfree_skb(skb);
+		}
+
+		osh->pub.pktalloced--;
+
+		skb = nskb;
+	}
+}
+
+#ifdef DHD_USE_STATIC_BUF
+void*
+osl_pktget_static(osl_t *osh, uint len)
+{
+	int i = 0;
+	struct sk_buff *skb;
+
+	
+	if (len > (PAGE_SIZE*2))
+	{
+		printk("Do we really need this big skb??\n");
+		return osl_pktget(osh, len);
+	}
+
+	
+	mutex_lock(&bcm_static_skb->osl_pkt_sem);
+	if (len <= PAGE_SIZE)
+	{
+		
+		for (i = 0; i < MAX_STATIC_PKT_NUM; i++)
+		{
+			if (bcm_static_skb->pkt_use[i] == 0)
+				break;
+		}
+
+		if (i != MAX_STATIC_PKT_NUM)
+		{
+			bcm_static_skb->pkt_use[i] = 1;
+			mutex_unlock(&bcm_static_skb->osl_pkt_sem);
+
+			skb = bcm_static_skb->skb_4k[i];
+			skb->tail = skb->data + len;
+			skb->len = len;
+			
+			return skb;
+		}
+	}
+
+	
+	for (i = 0; i < MAX_STATIC_PKT_NUM; i++)
+	{
+		if (bcm_static_skb->pkt_use[i+MAX_STATIC_PKT_NUM] == 0)
+			break;
+	}
+
+	if (i != MAX_STATIC_PKT_NUM)
+	{
+		bcm_static_skb->pkt_use[i+MAX_STATIC_PKT_NUM] = 1;
+		mutex_unlock(&bcm_static_skb->osl_pkt_sem);
+		skb = bcm_static_skb->skb_8k[i];
+		skb->tail = skb->data + len;
+		skb->len = len;
+		
+		return skb;
+	}
+
+
+	
+	mutex_unlock(&bcm_static_skb->osl_pkt_sem);
+	printk("all static pkt in use!\n");
+	return osl_pktget(osh, len);
+}
+
+
+void
+osl_pktfree_static(osl_t *osh, void *p, bool send)
+{
+	int i;
+	
+	for (i = 0; i < MAX_STATIC_PKT_NUM*2; i++)
+	{
+		if (p == bcm_static_skb->skb_4k[i])
+		{
+			mutex_lock(&bcm_static_skb->osl_pkt_sem);
+			bcm_static_skb->pkt_use[i] = 0;
+			mutex_unlock(&bcm_static_skb->osl_pkt_sem);
+
+			
+			return;
+		}
+	}
+	return osl_pktfree(osh, p, send);
+}
+#endif 
+uint32
+osl_pci_read_config(osl_t *osh, uint offset, uint size)
+{
+	uint val = 0;
+	uint retry = PCI_CFG_RETRY;
+
+	ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
+
+	
+	ASSERT(size == 4);
+
+	do {
+		pci_read_config_dword(osh->pdev, offset, &val);
+		if (val != 0xffffffff)
+			break;
+	} while (retry--);
+
+
+	return (val);
+}
+
+void
+osl_pci_write_config(osl_t *osh, uint offset, uint size, uint val)
+{
+	uint retry = PCI_CFG_RETRY;
+
+	ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
+
+	
+	ASSERT(size == 4);
+
+	do {
+		pci_write_config_dword(osh->pdev, offset, val);
+		if (offset != PCI_BAR0_WIN)
+			break;
+		if (osl_pci_read_config(osh, offset, size) == val)
+			break;
+	} while (retry--);
+
+}
+
+
+uint
+osl_pci_bus(osl_t *osh)
+{
+	ASSERT(osh && (osh->magic == OS_HANDLE_MAGIC) && osh->pdev);
+
+	return ((struct pci_dev *)osh->pdev)->bus->number;
+}
+
+
+uint
+osl_pci_slot(osl_t *osh)
+{
+	ASSERT(osh && (osh->magic == OS_HANDLE_MAGIC) && osh->pdev);
+
+	return PCI_SLOT(((struct pci_dev *)osh->pdev)->devfn);
+}
+
+static void
+osl_pcmcia_attr(osl_t *osh, uint offset, char *buf, int size, bool write)
+{
+}
+
+void
+osl_pcmcia_read_attr(osl_t *osh, uint offset, void *buf, int size)
+{
+	osl_pcmcia_attr(osh, offset, (char *) buf, size, FALSE);
+}
+
+void
+osl_pcmcia_write_attr(osl_t *osh, uint offset, void *buf, int size)
+{
+	osl_pcmcia_attr(osh, offset, (char *) buf, size, TRUE);
+}
+
+
+
+void*
+osl_malloc(osl_t *osh, uint size)
+{
+	void *addr;
+	gfp_t flags;
+
+	if (osh)
+		ASSERT(osh->magic == OS_HANDLE_MAGIC);
+
+#ifdef DHD_USE_STATIC_BUF
+	if (bcm_static_buf)
+	{
+		int i = 0;
+		if ((size >= PAGE_SIZE)&&(size <= STATIC_BUF_SIZE))
+		{
+			mutex_lock(&bcm_static_buf->static_sem);
+			
+			for (i = 0; i < MAX_STATIC_BUF_NUM; i++)
+			{
+				if (bcm_static_buf->buf_use[i] == 0)
+					break;
+			}
+			
+			if (i == MAX_STATIC_BUF_NUM)
+			{
+				mutex_unlock(&bcm_static_buf->static_sem);
+				printk("all static buff in use!\n");
+				goto original;
+			}
+			
+			bcm_static_buf->buf_use[i] = 1;
+			mutex_unlock(&bcm_static_buf->static_sem);
+
+			bzero(bcm_static_buf->buf_ptr+STATIC_BUF_SIZE*i, size);
+			if (osh)
+				osh->malloced += size;
+
+			return ((void *)(bcm_static_buf->buf_ptr+STATIC_BUF_SIZE*i));
+		}
+	}
+original:
+#endif 
+	flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL;
+	if ((addr = kmalloc(size, flags)) == NULL) {
+		if (osh)
+			osh->failed++;
+		return (NULL);
+	}
+	if (osh)
+		osh->malloced += size;
+
+	return (addr);
+}
+
+void
+osl_mfree(osl_t *osh, void *addr, uint size)
+{
+#ifdef DHD_USE_STATIC_BUF
+	if (bcm_static_buf)
+	{
+		if ((addr > (void *)bcm_static_buf) && ((unsigned char *)addr
+			<= ((unsigned char *)bcm_static_buf + STATIC_BUF_TOTAL_LEN)))
+		{
+			int buf_idx = 0;
+			
+			buf_idx = ((unsigned char *)addr - bcm_static_buf->buf_ptr)/STATIC_BUF_SIZE;
+			
+			mutex_lock(&bcm_static_buf->static_sem);
+			bcm_static_buf->buf_use[buf_idx] = 0;
+			mutex_unlock(&bcm_static_buf->static_sem);
+
+			if (osh) {
+				ASSERT(osh->magic == OS_HANDLE_MAGIC);
+				osh->malloced -= size;
+			}
+			return;
+		}
+	}
+#endif 
+	if (osh) {
+		ASSERT(osh->magic == OS_HANDLE_MAGIC);
+		osh->malloced -= size;
+	}
+	kfree(addr);
+}
+
+uint
+osl_malloced(osl_t *osh)
+{
+	ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
+	return (osh->malloced);
+}
+
+uint
+osl_malloc_failed(osl_t *osh)
+{
+	ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
+	return (osh->failed);
+}
+
+void*
+osl_dma_alloc_consistent(osl_t *osh, uint size, ulong *pap)
+{
+	ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
+
+	return (pci_alloc_consistent(osh->pdev, size, (dma_addr_t*)pap));
+}
+
+void
+osl_dma_free_consistent(osl_t *osh, void *va, uint size, ulong pa)
+{
+	ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
+
+	pci_free_consistent(osh->pdev, size, va, (dma_addr_t)pa);
+}
+
+uint
+osl_dma_map(osl_t *osh, void *va, uint size, int direction)
+{
+	int dir;
+
+	ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
+	dir = (direction == DMA_TX)? PCI_DMA_TODEVICE: PCI_DMA_FROMDEVICE;
+	return (pci_map_single(osh->pdev, va, size, dir));
+}
+
+void
+osl_dma_unmap(osl_t *osh, uint pa, uint size, int direction)
+{
+	int dir;
+
+	ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
+	dir = (direction == DMA_TX)? PCI_DMA_TODEVICE: PCI_DMA_FROMDEVICE;
+	pci_unmap_single(osh->pdev, (uint32)pa, size, dir);
+}
+
+
+void
+osl_delay(uint usec)
+{
+	uint d;
+
+	while (usec > 0) {
+		d = MIN(usec, 1000);
+		udelay(d);
+		usec -= d;
+	}
+}
+
+
+
+void *
+osl_pktdup(osl_t *osh, void *skb)
+{
+	void * p;
+	gfp_t flags;
+
+	flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL;
+	if ((p = skb_clone((struct sk_buff*)skb, flags)) == NULL)
+		return NULL;
+
+	
+	if (osh->pub.pkttag)
+		bzero((void*)((struct sk_buff *)p)->cb, OSL_PKTTAG_SZ);
+
+	
+	osh->pub.pktalloced++;
+	return (p);
+}
diff --git a/drivers/net/wireless/bcm4329/miniopt.c b/drivers/net/wireless/bcm4329/miniopt.c
new file mode 100644
index 0000000..6a184a7
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/miniopt.c
@@ -0,0 +1,163 @@
+/*
+ * Description.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ * $Id: miniopt.c,v 1.1.6.4 2009/09/25 00:32:01 Exp $
+ */
+
+/* ---- Include Files ---------------------------------------------------- */
+
+#include <typedefs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "miniopt.h"
+
+
+/* ---- Public Variables ------------------------------------------------- */
+/* ---- Private Constants and Types -------------------------------------- */
+
+
+
+/* ---- Private Variables ------------------------------------------------ */
+/* ---- Private Function Prototypes -------------------------------------- */
+/* ---- Functions -------------------------------------------------------- */
+
+/* ----------------------------------------------------------------------- */
+void
+miniopt_init(miniopt_t *t, const char* name, const char* flags, bool longflags)
+{
+	static const char *null_flags = "";
+
+	memset(t, 0, sizeof(miniopt_t));
+	t->name = name;
+	if (flags == NULL)
+		t->flags = null_flags;
+	else
+		t->flags = flags;
+	t->longflags = longflags;
+}
+
+
+/* ----------------------------------------------------------------------- */
+int
+miniopt(miniopt_t *t, char **argv)
+{
+	int keylen;
+	char *p, *eq, *valstr, *endptr = NULL;
+	int err = 0;
+
+	t->consumed = 0;
+	t->positional = FALSE;
+	memset(t->key, 0, MINIOPT_MAXKEY);
+	t->opt = '\0';
+	t->valstr = NULL;
+	t->good_int = FALSE;
+	valstr = NULL;
+
+	if (*argv == NULL) {
+		err = -1;
+		goto exit;
+	}
+
+	p = *argv++;
+	t->consumed++;
+
+	if (!t->opt_end && !strcmp(p, "--")) {
+		t->opt_end = TRUE;
+		if (*argv == NULL) {
+			err = -1;
+			goto exit;
+		}
+		p = *argv++;
+		t->consumed++;
+	}
+
+	if (t->opt_end) {
+		t->positional = TRUE;
+		valstr = p;
+	}
+	else if (!strncmp(p, "--", 2)) {
+		eq = strchr(p, '=');
+		if (eq == NULL && !t->longflags) {
+			fprintf(stderr,
+				"%s: missing \" = \" in long param \"%s\"\n", t->name, p);
+			err = 1;
+			goto exit;
+		}
+		keylen = eq ? (eq - (p + 2)) : (int)strlen(p) - 2;
+		if (keylen > 63) keylen = 63;
+		memcpy(t->key, p + 2, keylen);
+
+		if (eq) {
+			valstr = eq + 1;
+			if (*valstr == '\0') {
+				fprintf(stderr,
+				        "%s: missing value after \" = \" in long param \"%s\"\n",
+				        t->name, p);
+				err = 1;
+				goto exit;
+			}
+		}
+	}
+	else if (!strncmp(p, "-", 1)) {
+		t->opt = p[1];
+		if (strlen(p) > 2) {
+			fprintf(stderr,
+				"%s: only single char options, error on param \"%s\"\n",
+				t->name, p);
+			err = 1;
+			goto exit;
+		}
+		if (strchr(t->flags, t->opt)) {
+			/* this is a flag option, no value expected */
+			valstr = NULL;
+		} else {
+			if (*argv == NULL) {
+				fprintf(stderr,
+				"%s: missing value parameter after \"%s\"\n", t->name, p);
+				err = 1;
+				goto exit;
+			}
+			valstr = *argv;
+			argv++;
+			t->consumed++;
+		}
+	} else {
+		t->positional = TRUE;
+		valstr = p;
+	}
+
+	/* parse valstr as int just in case */
+	if (valstr) {
+		t->uval = (uint)strtoul(valstr, &endptr, 0);
+		t->val = (int)t->uval;
+		t->good_int = (*endptr == '\0');
+	}
+
+	t->valstr = valstr;
+
+exit:
+	if (err == 1)
+		t->opt = '?';
+
+	return err;
+}
diff --git a/drivers/net/wireless/bcm4329/sbutils.c b/drivers/net/wireless/bcm4329/sbutils.c
new file mode 100644
index 0000000..46cd510
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/sbutils.c
@@ -0,0 +1,1004 @@
+/*
+ * Misc utility routines for accessing chip-specific features
+ * of the SiliconBackplane-based Broadcom chips.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: sbutils.c,v 1.662.4.10.2.7.4.2 2010/04/19 05:48:48 Exp $
+ */
+
+#include <typedefs.h>
+#include <bcmdefs.h>
+#include <osl.h>
+#include <bcmutils.h>
+#include <siutils.h>
+#include <bcmdevs.h>
+#include <hndsoc.h>
+#include <sbchipc.h>
+#include <pcicfg.h>
+#include <sbpcmcia.h>
+
+#include "siutils_priv.h"
+
+/* local prototypes */
+static uint _sb_coreidx(si_info_t *sii, uint32 sba);
+static uint _sb_scan(si_info_t *sii, uint32 sba, void *regs, uint bus, uint32 sbba,
+                     uint ncores);
+static uint32 _sb_coresba(si_info_t *sii);
+static void *_sb_setcoreidx(si_info_t *sii, uint coreidx);
+
+#define	SET_SBREG(sii, r, mask, val)	\
+		W_SBREG((sii), (r), ((R_SBREG((sii), (r)) & ~(mask)) | (val)))
+#define	REGS2SB(va)	(sbconfig_t*) ((int8*)(va) + SBCONFIGOFF)
+
+/* sonicsrev */
+#define	SONICS_2_2	(SBIDL_RV_2_2 >> SBIDL_RV_SHIFT)
+#define	SONICS_2_3	(SBIDL_RV_2_3 >> SBIDL_RV_SHIFT)
+
+#define	R_SBREG(sii, sbr)	sb_read_sbreg((sii), (sbr))
+#define	W_SBREG(sii, sbr, v)	sb_write_sbreg((sii), (sbr), (v))
+#define	AND_SBREG(sii, sbr, v)	W_SBREG((sii), (sbr), (R_SBREG((sii), (sbr)) & (v)))
+#define	OR_SBREG(sii, sbr, v)	W_SBREG((sii), (sbr), (R_SBREG((sii), (sbr)) | (v)))
+
+static uint32
+sb_read_sbreg(si_info_t *sii, volatile uint32 *sbr)
+{
+	uint8 tmp;
+	uint32 val, intr_val = 0;
+
+
+	/*
+	 * compact flash only has 11 bits address, while we needs 12 bits address.
+	 * MEM_SEG will be OR'd with other 11 bits address in hardware,
+	 * so we program MEM_SEG with 12th bit when necessary(access sb regsiters).
+	 * For normal PCMCIA bus(CFTable_regwinsz > 2k), do nothing special
+	 */
+	if (PCMCIA(sii)) {
+		INTR_OFF(sii, intr_val);
+		tmp = 1;
+		OSL_PCMCIA_WRITE_ATTR(sii->osh, MEM_SEG, &tmp, 1);
+		sbr = (volatile uint32 *)((uintptr)sbr & ~(1 << 11)); /* mask out bit 11 */
+	}
+
+	val = R_REG(sii->osh, sbr);
+
+	if (PCMCIA(sii)) {
+		tmp = 0;
+		OSL_PCMCIA_WRITE_ATTR(sii->osh, MEM_SEG, &tmp, 1);
+		INTR_RESTORE(sii, intr_val);
+	}
+
+	return (val);
+}
+
+static void
+sb_write_sbreg(si_info_t *sii, volatile uint32 *sbr, uint32 v)
+{
+	uint8 tmp;
+	volatile uint32 dummy;
+	uint32 intr_val = 0;
+
+
+	/*
+	 * compact flash only has 11 bits address, while we needs 12 bits address.
+	 * MEM_SEG will be OR'd with other 11 bits address in hardware,
+	 * so we program MEM_SEG with 12th bit when necessary(access sb regsiters).
+	 * For normal PCMCIA bus(CFTable_regwinsz > 2k), do nothing special
+	 */
+	if (PCMCIA(sii)) {
+		INTR_OFF(sii, intr_val);
+		tmp = 1;
+		OSL_PCMCIA_WRITE_ATTR(sii->osh, MEM_SEG, &tmp, 1);
+		sbr = (volatile uint32 *)((uintptr)sbr & ~(1 << 11)); /* mask out bit 11 */
+	}
+
+	if (BUSTYPE(sii->pub.bustype) == PCMCIA_BUS) {
+#ifdef IL_BIGENDIAN
+		dummy = R_REG(sii->osh, sbr);
+		W_REG(sii->osh, ((volatile uint16 *)sbr + 1), (uint16)((v >> 16) & 0xffff));
+		dummy = R_REG(sii->osh, sbr);
+		W_REG(sii->osh, (volatile uint16 *)sbr, (uint16)(v & 0xffff));
+#else
+		dummy = R_REG(sii->osh, sbr);
+		W_REG(sii->osh, (volatile uint16 *)sbr, (uint16)(v & 0xffff));
+		dummy = R_REG(sii->osh, sbr);
+		W_REG(sii->osh, ((volatile uint16 *)sbr + 1), (uint16)((v >> 16) & 0xffff));
+#endif	/* IL_BIGENDIAN */
+	} else
+		W_REG(sii->osh, sbr, v);
+
+	if (PCMCIA(sii)) {
+		tmp = 0;
+		OSL_PCMCIA_WRITE_ATTR(sii->osh, MEM_SEG, &tmp, 1);
+		INTR_RESTORE(sii, intr_val);
+	}
+}
+
+uint
+sb_coreid(si_t *sih)
+{
+	si_info_t *sii;
+	sbconfig_t *sb;
+
+	sii = SI_INFO(sih);
+	sb = REGS2SB(sii->curmap);
+
+	return ((R_SBREG(sii, &sb->sbidhigh) & SBIDH_CC_MASK) >> SBIDH_CC_SHIFT);
+}
+
+uint
+sb_flag(si_t *sih)
+{
+	si_info_t *sii;
+	sbconfig_t *sb;
+
+	sii = SI_INFO(sih);
+	sb = REGS2SB(sii->curmap);
+
+	return R_SBREG(sii, &sb->sbtpsflag) & SBTPS_NUM0_MASK;
+}
+
+void
+sb_setint(si_t *sih, int siflag)
+{
+	si_info_t *sii;
+	sbconfig_t *sb;
+	uint32 vec;
+
+	sii = SI_INFO(sih);
+	sb = REGS2SB(sii->curmap);
+
+	if (siflag == -1)
+		vec = 0;
+	else
+		vec = 1 << siflag;
+	W_SBREG(sii, &sb->sbintvec, vec);
+}
+
+/* return core index of the core with address 'sba' */
+static uint
+_sb_coreidx(si_info_t *sii, uint32 sba)
+{
+	uint i;
+
+	for (i = 0; i < sii->numcores; i ++)
+		if (sba == sii->common_info->coresba[i])
+			return i;
+	return BADIDX;
+}
+
+/* return core address of the current core */
+static uint32
+_sb_coresba(si_info_t *sii)
+{
+	uint32 sbaddr;
+
+
+	switch (BUSTYPE(sii->pub.bustype)) {
+	case SI_BUS: {
+		sbconfig_t *sb = REGS2SB(sii->curmap);
+		sbaddr = sb_base(R_SBREG(sii, &sb->sbadmatch0));
+		break;
+	}
+
+	case PCI_BUS:
+		sbaddr = OSL_PCI_READ_CONFIG(sii->osh, PCI_BAR0_WIN, sizeof(uint32));
+		break;
+
+	case PCMCIA_BUS: {
+		uint8 tmp = 0;
+		OSL_PCMCIA_READ_ATTR(sii->osh, PCMCIA_ADDR0, &tmp, 1);
+		sbaddr  = (uint32)tmp << 12;
+		OSL_PCMCIA_READ_ATTR(sii->osh, PCMCIA_ADDR1, &tmp, 1);
+		sbaddr |= (uint32)tmp << 16;
+		OSL_PCMCIA_READ_ATTR(sii->osh, PCMCIA_ADDR2, &tmp, 1);
+		sbaddr |= (uint32)tmp << 24;
+		break;
+	}
+
+	case SPI_BUS:
+	case SDIO_BUS:
+		sbaddr = (uint32)(uintptr)sii->curmap;
+		break;
+
+
+	default:
+		sbaddr = BADCOREADDR;
+		break;
+	}
+
+	return sbaddr;
+}
+
+uint
+sb_corevendor(si_t *sih)
+{
+	si_info_t *sii;
+	sbconfig_t *sb;
+
+	sii = SI_INFO(sih);
+	sb = REGS2SB(sii->curmap);
+
+	return ((R_SBREG(sii, &sb->sbidhigh) & SBIDH_VC_MASK) >> SBIDH_VC_SHIFT);
+}
+
+uint
+sb_corerev(si_t *sih)
+{
+	si_info_t *sii;
+	sbconfig_t *sb;
+	uint sbidh;
+
+	sii = SI_INFO(sih);
+	sb = REGS2SB(sii->curmap);
+	sbidh = R_SBREG(sii, &sb->sbidhigh);
+
+	return (SBCOREREV(sbidh));
+}
+
+/* set core-specific control flags */
+void
+sb_core_cflags_wo(si_t *sih, uint32 mask, uint32 val)
+{
+	si_info_t *sii;
+	sbconfig_t *sb;
+	uint32 w;
+
+	sii = SI_INFO(sih);
+	sb = REGS2SB(sii->curmap);
+
+	ASSERT((val & ~mask) == 0);
+
+	/* mask and set */
+	w = (R_SBREG(sii, &sb->sbtmstatelow) & ~(mask << SBTML_SICF_SHIFT)) |
+	        (val << SBTML_SICF_SHIFT);
+	W_SBREG(sii, &sb->sbtmstatelow, w);
+}
+
+/* set/clear core-specific control flags */
+uint32
+sb_core_cflags(si_t *sih, uint32 mask, uint32 val)
+{
+	si_info_t *sii;
+	sbconfig_t *sb;
+	uint32 w;
+
+	sii = SI_INFO(sih);
+	sb = REGS2SB(sii->curmap);
+
+	ASSERT((val & ~mask) == 0);
+
+	/* mask and set */
+	if (mask || val) {
+		w = (R_SBREG(sii, &sb->sbtmstatelow) & ~(mask << SBTML_SICF_SHIFT)) |
+		        (val << SBTML_SICF_SHIFT);
+		W_SBREG(sii, &sb->sbtmstatelow, w);
+	}
+
+	/* return the new value
+	 * for write operation, the following readback ensures the completion of write opration.
+	 */
+	return (R_SBREG(sii, &sb->sbtmstatelow) >> SBTML_SICF_SHIFT);
+}
+
+/* set/clear core-specific status flags */
+uint32
+sb_core_sflags(si_t *sih, uint32 mask, uint32 val)
+{
+	si_info_t *sii;
+	sbconfig_t *sb;
+	uint32 w;
+
+	sii = SI_INFO(sih);
+	sb = REGS2SB(sii->curmap);
+
+	ASSERT((val & ~mask) == 0);
+	ASSERT((mask & ~SISF_CORE_BITS) == 0);
+
+	/* mask and set */
+	if (mask || val) {
+		w = (R_SBREG(sii, &sb->sbtmstatehigh) & ~(mask << SBTMH_SISF_SHIFT)) |
+		        (val << SBTMH_SISF_SHIFT);
+		W_SBREG(sii, &sb->sbtmstatehigh, w);
+	}
+
+	/* return the new value */
+	return (R_SBREG(sii, &sb->sbtmstatehigh) >> SBTMH_SISF_SHIFT);
+}
+
+bool
+sb_iscoreup(si_t *sih)
+{
+	si_info_t *sii;
+	sbconfig_t *sb;
+
+	sii = SI_INFO(sih);
+	sb = REGS2SB(sii->curmap);
+
+	return ((R_SBREG(sii, &sb->sbtmstatelow) &
+	         (SBTML_RESET | SBTML_REJ_MASK | (SICF_CLOCK_EN << SBTML_SICF_SHIFT))) ==
+	        (SICF_CLOCK_EN << SBTML_SICF_SHIFT));
+}
+
+/*
+ * Switch to 'coreidx', issue a single arbitrary 32bit register mask&set operation,
+ * switch back to the original core, and return the new value.
+ *
+ * When using the silicon backplane, no fidleing with interrupts or core switches are needed.
+ *
+ * Also, when using pci/pcie, we can optimize away the core switching for pci registers
+ * and (on newer pci cores) chipcommon registers.
+ */
+uint
+sb_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val)
+{
+	uint origidx = 0;
+	uint32 *r = NULL;
+	uint w;
+	uint intr_val = 0;
+	bool fast = FALSE;
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+
+	ASSERT(GOODIDX(coreidx));
+	ASSERT(regoff < SI_CORE_SIZE);
+	ASSERT((val & ~mask) == 0);
+
+	if (coreidx >= SI_MAXCORES)
+		return 0;
+
+	if (BUSTYPE(sii->pub.bustype) == SI_BUS) {
+		/* If internal bus, we can always get at everything */
+		fast = TRUE;
+		/* map if does not exist */
+		if (!sii->common_info->regs[coreidx]) {
+			sii->common_info->regs[coreidx] =
+			    REG_MAP(sii->common_info->coresba[coreidx], SI_CORE_SIZE);
+			ASSERT(GOODREGS(sii->common_info->regs[coreidx]));
+		}
+		r = (uint32 *)((uchar *)sii->common_info->regs[coreidx] + regoff);
+	} else if (BUSTYPE(sii->pub.bustype) == PCI_BUS) {
+		/* If pci/pcie, we can get at pci/pcie regs and on newer cores to chipc */
+
+		if ((sii->common_info->coreid[coreidx] == CC_CORE_ID) && SI_FAST(sii)) {
+			/* Chipc registers are mapped at 12KB */
+
+			fast = TRUE;
+			r = (uint32 *)((char *)sii->curmap + PCI_16KB0_CCREGS_OFFSET + regoff);
+		} else if (sii->pub.buscoreidx == coreidx) {
+			/* pci registers are at either in the last 2KB of an 8KB window
+			 * or, in pcie and pci rev 13 at 8KB
+			 */
+			fast = TRUE;
+			if (SI_FAST(sii))
+				r = (uint32 *)((char *)sii->curmap +
+				               PCI_16KB0_PCIREGS_OFFSET + regoff);
+			else
+				r = (uint32 *)((char *)sii->curmap +
+				               ((regoff >= SBCONFIGOFF) ?
+				                PCI_BAR0_PCISBR_OFFSET : PCI_BAR0_PCIREGS_OFFSET) +
+				               regoff);
+		}
+	}
+
+	if (!fast) {
+		INTR_OFF(sii, intr_val);
+
+		/* save current core index */
+		origidx = si_coreidx(&sii->pub);
+
+		/* switch core */
+		r = (uint32*) ((uchar*)sb_setcoreidx(&sii->pub, coreidx) + regoff);
+	}
+	ASSERT(r != NULL);
+
+	/* mask and set */
+	if (mask || val) {
+		if (regoff >= SBCONFIGOFF) {
+			w = (R_SBREG(sii, r) & ~mask) | val;
+			W_SBREG(sii, r, w);
+		} else {
+			w = (R_REG(sii->osh, r) & ~mask) | val;
+			W_REG(sii->osh, r, w);
+		}
+	}
+
+	/* readback */
+	if (regoff >= SBCONFIGOFF)
+		w = R_SBREG(sii, r);
+	else {
+		if ((CHIPID(sii->pub.chip) == BCM5354_CHIP_ID) &&
+		    (coreidx == SI_CC_IDX) &&
+		    (regoff == OFFSETOF(chipcregs_t, watchdog))) {
+			w = val;
+		} else
+			w = R_REG(sii->osh, r);
+	}
+
+	if (!fast) {
+		/* restore core index */
+		if (origidx != coreidx)
+			sb_setcoreidx(&sii->pub, origidx);
+
+		INTR_RESTORE(sii, intr_val);
+	}
+
+	return (w);
+}
+
+/* Scan the enumeration space to find all cores starting from the given
+ * bus 'sbba'. Append coreid and other info to the lists in 'si'. 'sba'
+ * is the default core address at chip POR time and 'regs' is the virtual
+ * address that the default core is mapped at. 'ncores' is the number of
+ * cores expected on bus 'sbba'. It returns the total number of cores
+ * starting from bus 'sbba', inclusive.
+ */
+#define SB_MAXBUSES	2
+static uint
+_sb_scan(si_info_t *sii, uint32 sba, void *regs, uint bus, uint32 sbba, uint numcores)
+{
+	uint next;
+	uint ncc = 0;
+	uint i;
+
+	if (bus >= SB_MAXBUSES) {
+		SI_ERROR(("_sb_scan: bus 0x%08x at level %d is too deep to scan\n", sbba, bus));
+		return 0;
+	}
+	SI_MSG(("_sb_scan: scan bus 0x%08x assume %u cores\n", sbba, numcores));
+
+	/* Scan all cores on the bus starting from core 0.
+	 * Core addresses must be contiguous on each bus.
+	 */
+	for (i = 0, next = sii->numcores; i < numcores && next < SB_BUS_MAXCORES; i++, next++) {
+		sii->common_info->coresba[next] = sbba + (i * SI_CORE_SIZE);
+
+		/* keep and reuse the initial register mapping */
+		if ((BUSTYPE(sii->pub.bustype) == SI_BUS) &&
+			(sii->common_info->coresba[next] == sba)) {
+			SI_MSG(("_sb_scan: reuse mapped regs %p for core %u\n", regs, next));
+			sii->common_info->regs[next] = regs;
+		}
+
+		/* change core to 'next' and read its coreid */
+		sii->curmap = _sb_setcoreidx(sii, next);
+		sii->curidx = next;
+
+		sii->common_info->coreid[next] = sb_coreid(&sii->pub);
+
+		/* core specific processing... */
+		/* chipc provides # cores */
+		if (sii->common_info->coreid[next] == CC_CORE_ID) {
+			chipcregs_t *cc = (chipcregs_t *)sii->curmap;
+			uint32 ccrev = sb_corerev(&sii->pub);
+
+			/* determine numcores - this is the total # cores in the chip */
+			if (((ccrev == 4) || (ccrev >= 6)))
+				numcores = (R_REG(sii->osh, &cc->chipid) & CID_CC_MASK) >>
+				        CID_CC_SHIFT;
+			else {
+				/* Older chips */
+				uint chip = sii->pub.chip;
+
+				if (chip == BCM4306_CHIP_ID)	/* < 4306c0 */
+					numcores = 6;
+				else if (chip == BCM4704_CHIP_ID)
+					numcores = 9;
+				else if (chip == BCM5365_CHIP_ID)
+					numcores = 7;
+				else {
+					SI_ERROR(("sb_chip2numcores: unsupported chip 0x%x\n",
+					          chip));
+					ASSERT(0);
+					numcores = 1;
+				}
+			}
+			SI_MSG(("_sb_scan: there are %u cores in the chip %s\n", numcores,
+				sii->pub.issim ? "QT" : ""));
+		}
+		/* scan bridged SB(s) and add results to the end of the list */
+		else if (sii->common_info->coreid[next] == OCP_CORE_ID) {
+			sbconfig_t *sb = REGS2SB(sii->curmap);
+			uint32 nsbba = R_SBREG(sii, &sb->sbadmatch1);
+			uint nsbcc;
+
+			sii->numcores = next + 1;
+
+			if ((nsbba & 0xfff00000) != SI_ENUM_BASE)
+				continue;
+			nsbba &= 0xfffff000;
+			if (_sb_coreidx(sii, nsbba) != BADIDX)
+				continue;
+
+			nsbcc = (R_SBREG(sii, &sb->sbtmstatehigh) & 0x000f0000) >> 16;
+			nsbcc = _sb_scan(sii, sba, regs, bus + 1, nsbba, nsbcc);
+			if (sbba == SI_ENUM_BASE)
+				numcores -= nsbcc;
+			ncc += nsbcc;
+		}
+	}
+
+	SI_MSG(("_sb_scan: found %u cores on bus 0x%08x\n", i, sbba));
+
+	sii->numcores = i + ncc;
+	return sii->numcores;
+}
+
+/* scan the sb enumerated space to identify all cores */
+void
+sb_scan(si_t *sih, void *regs, uint devid)
+{
+	si_info_t *sii;
+	uint32 origsba;
+
+	sii = SI_INFO(sih);
+
+	/* Save the current core info and validate it later till we know
+	 * for sure what is good and what is bad.
+	 */
+	origsba = _sb_coresba(sii);
+
+	/* scan all SB(s) starting from SI_ENUM_BASE */
+	sii->numcores = _sb_scan(sii, origsba, regs, 0, SI_ENUM_BASE, 1);
+}
+
+/*
+ * This function changes logical "focus" to the indicated core;
+ * must be called with interrupts off.
+ * Moreover, callers should keep interrupts off during switching out of and back to d11 core
+ */
+void *
+sb_setcoreidx(si_t *sih, uint coreidx)
+{
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+
+	if (coreidx >= sii->numcores)
+		return (NULL);
+
+	/*
+	 * If the user has provided an interrupt mask enabled function,
+	 * then assert interrupts are disabled before switching the core.
+	 */
+	ASSERT((sii->intrsenabled_fn == NULL) || !(*(sii)->intrsenabled_fn)((sii)->intr_arg));
+
+	sii->curmap = _sb_setcoreidx(sii, coreidx);
+	sii->curidx = coreidx;
+
+	return (sii->curmap);
+}
+
+/* This function changes the logical "focus" to the indicated core.
+ * Return the current core's virtual address.
+ */
+static void *
+_sb_setcoreidx(si_info_t *sii, uint coreidx)
+{
+	uint32 sbaddr = sii->common_info->coresba[coreidx];
+	void *regs;
+
+	switch (BUSTYPE(sii->pub.bustype)) {
+	case SI_BUS:
+		/* map new one */
+		if (!sii->common_info->regs[coreidx]) {
+			sii->common_info->regs[coreidx] = REG_MAP(sbaddr, SI_CORE_SIZE);
+			ASSERT(GOODREGS(sii->common_info->regs[coreidx]));
+		}
+		regs = sii->common_info->regs[coreidx];
+		break;
+
+	case PCI_BUS:
+		/* point bar0 window */
+		OSL_PCI_WRITE_CONFIG(sii->osh, PCI_BAR0_WIN, 4, sbaddr);
+		regs = sii->curmap;
+		break;
+
+	case PCMCIA_BUS: {
+		uint8 tmp = (sbaddr >> 12) & 0x0f;
+		OSL_PCMCIA_WRITE_ATTR(sii->osh, PCMCIA_ADDR0, &tmp, 1);
+		tmp = (sbaddr >> 16) & 0xff;
+		OSL_PCMCIA_WRITE_ATTR(sii->osh, PCMCIA_ADDR1, &tmp, 1);
+		tmp = (sbaddr >> 24) & 0xff;
+		OSL_PCMCIA_WRITE_ATTR(sii->osh, PCMCIA_ADDR2, &tmp, 1);
+		regs = sii->curmap;
+		break;
+	}
+	case SPI_BUS:
+	case SDIO_BUS:
+		/* map new one */
+		if (!sii->common_info->regs[coreidx]) {
+			sii->common_info->regs[coreidx] = (void *)(uintptr)sbaddr;
+			ASSERT(GOODREGS(sii->common_info->regs[coreidx]));
+		}
+		regs = sii->common_info->regs[coreidx];
+		break;
+
+
+	default:
+		ASSERT(0);
+		regs = NULL;
+		break;
+	}
+
+	return regs;
+}
+
+/* Return the address of sbadmatch0/1/2/3 register */
+static volatile uint32 *
+sb_admatch(si_info_t *sii, uint asidx)
+{
+	sbconfig_t *sb;
+	volatile uint32 *addrm;
+
+	sb = REGS2SB(sii->curmap);
+
+	switch (asidx) {
+	case 0:
+		addrm =  &sb->sbadmatch0;
+		break;
+
+	case 1:
+		addrm =  &sb->sbadmatch1;
+		break;
+
+	case 2:
+		addrm =  &sb->sbadmatch2;
+		break;
+
+	case 3:
+		addrm =  &sb->sbadmatch3;
+		break;
+
+	default:
+		SI_ERROR(("%s: Address space index (%d) out of range\n", __FUNCTION__, asidx));
+		return 0;
+	}
+
+	return (addrm);
+}
+
+/* Return the number of address spaces in current core */
+int
+sb_numaddrspaces(si_t *sih)
+{
+	si_info_t *sii;
+	sbconfig_t *sb;
+
+	sii = SI_INFO(sih);
+	sb = REGS2SB(sii->curmap);
+
+	/* + 1 because of enumeration space */
+	return ((R_SBREG(sii, &sb->sbidlow) & SBIDL_AR_MASK) >> SBIDL_AR_SHIFT) + 1;
+}
+
+/* Return the address of the nth address space in the current core */
+uint32
+sb_addrspace(si_t *sih, uint asidx)
+{
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+
+	return (sb_base(R_SBREG(sii, sb_admatch(sii, asidx))));
+}
+
+/* Return the size of the nth address space in the current core */
+uint32
+sb_addrspacesize(si_t *sih, uint asidx)
+{
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+
+	return (sb_size(R_SBREG(sii, sb_admatch(sii, asidx))));
+}
+
+
+/* do buffered registers update */
+void
+sb_commit(si_t *sih)
+{
+	si_info_t *sii;
+	uint origidx;
+	uint intr_val = 0;
+
+	sii = SI_INFO(sih);
+
+	origidx = sii->curidx;
+	ASSERT(GOODIDX(origidx));
+
+	INTR_OFF(sii, intr_val);
+
+	/* switch over to chipcommon core if there is one, else use pci */
+	if (sii->pub.ccrev != NOREV) {
+		chipcregs_t *ccregs = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0);
+
+		/* do the buffer registers update */
+		W_REG(sii->osh, &ccregs->broadcastaddress, SB_COMMIT);
+		W_REG(sii->osh, &ccregs->broadcastdata, 0x0);
+	} else
+		ASSERT(0);
+
+	/* restore core index */
+	sb_setcoreidx(sih, origidx);
+	INTR_RESTORE(sii, intr_val);
+}
+
+void
+sb_core_disable(si_t *sih, uint32 bits)
+{
+	si_info_t *sii;
+	volatile uint32 dummy;
+	sbconfig_t *sb;
+
+	sii = SI_INFO(sih);
+
+	ASSERT(GOODREGS(sii->curmap));
+	sb = REGS2SB(sii->curmap);
+
+	/* if core is already in reset, just return */
+	if (R_SBREG(sii, &sb->sbtmstatelow) & SBTML_RESET)
+		return;
+
+	/* if clocks are not enabled, put into reset and return */
+	if ((R_SBREG(sii, &sb->sbtmstatelow) & (SICF_CLOCK_EN << SBTML_SICF_SHIFT)) == 0)
+		goto disable;
+
+	/* set target reject and spin until busy is clear (preserve core-specific bits) */
+	OR_SBREG(sii, &sb->sbtmstatelow, SBTML_REJ);
+	dummy = R_SBREG(sii, &sb->sbtmstatelow);
+	OSL_DELAY(1);
+	SPINWAIT((R_SBREG(sii, &sb->sbtmstatehigh) & SBTMH_BUSY), 100000);
+	if (R_SBREG(sii, &sb->sbtmstatehigh) & SBTMH_BUSY)
+		SI_ERROR(("%s: target state still busy\n", __FUNCTION__));
+
+	if (R_SBREG(sii, &sb->sbidlow) & SBIDL_INIT) {
+		OR_SBREG(sii, &sb->sbimstate, SBIM_RJ);
+		dummy = R_SBREG(sii, &sb->sbimstate);
+		OSL_DELAY(1);
+		SPINWAIT((R_SBREG(sii, &sb->sbimstate) & SBIM_BY), 100000);
+	}
+
+	/* set reset and reject while enabling the clocks */
+	W_SBREG(sii, &sb->sbtmstatelow,
+	        (((bits | SICF_FGC | SICF_CLOCK_EN) << SBTML_SICF_SHIFT) |
+	         SBTML_REJ | SBTML_RESET));
+	dummy = R_SBREG(sii, &sb->sbtmstatelow);
+	OSL_DELAY(10);
+
+	/* don't forget to clear the initiator reject bit */
+	if (R_SBREG(sii, &sb->sbidlow) & SBIDL_INIT)
+		AND_SBREG(sii, &sb->sbimstate, ~SBIM_RJ);
+
+disable:
+	/* leave reset and reject asserted */
+	W_SBREG(sii, &sb->sbtmstatelow, ((bits << SBTML_SICF_SHIFT) | SBTML_REJ | SBTML_RESET));
+	OSL_DELAY(1);
+}
+
+/* reset and re-enable a core
+ * inputs:
+ * bits - core specific bits that are set during and after reset sequence
+ * resetbits - core specific bits that are set only during reset sequence
+ */
+void
+sb_core_reset(si_t *sih, uint32 bits, uint32 resetbits)
+{
+	si_info_t *sii;
+	sbconfig_t *sb;
+	volatile uint32 dummy;
+
+	sii = SI_INFO(sih);
+	ASSERT(GOODREGS(sii->curmap));
+	sb = REGS2SB(sii->curmap);
+
+	/*
+	 * Must do the disable sequence first to work for arbitrary current core state.
+	 */
+	sb_core_disable(sih, (bits | resetbits));
+
+	/*
+	 * Now do the initialization sequence.
+	 */
+
+	/* set reset while enabling the clock and forcing them on throughout the core */
+	W_SBREG(sii, &sb->sbtmstatelow,
+	        (((bits | resetbits | SICF_FGC | SICF_CLOCK_EN) << SBTML_SICF_SHIFT) |
+	         SBTML_RESET));
+	dummy = R_SBREG(sii, &sb->sbtmstatelow);
+	OSL_DELAY(1);
+
+	if (R_SBREG(sii, &sb->sbtmstatehigh) & SBTMH_SERR) {
+		W_SBREG(sii, &sb->sbtmstatehigh, 0);
+	}
+	if ((dummy = R_SBREG(sii, &sb->sbimstate)) & (SBIM_IBE | SBIM_TO)) {
+		AND_SBREG(sii, &sb->sbimstate, ~(SBIM_IBE | SBIM_TO));
+	}
+
+	/* clear reset and allow it to propagate throughout the core */
+	W_SBREG(sii, &sb->sbtmstatelow,
+	        ((bits | resetbits | SICF_FGC | SICF_CLOCK_EN) << SBTML_SICF_SHIFT));
+	dummy = R_SBREG(sii, &sb->sbtmstatelow);
+	OSL_DELAY(1);
+
+	/* leave clock enabled */
+	W_SBREG(sii, &sb->sbtmstatelow, ((bits | SICF_CLOCK_EN) << SBTML_SICF_SHIFT));
+	dummy = R_SBREG(sii, &sb->sbtmstatelow);
+	OSL_DELAY(1);
+}
+
+void
+sb_core_tofixup(si_t *sih)
+{
+	si_info_t *sii;
+	sbconfig_t *sb;
+
+	sii = SI_INFO(sih);
+
+	if ((BUSTYPE(sii->pub.bustype) != PCI_BUS) || PCIE(sii) ||
+	    (PCI(sii) && (sii->pub.buscorerev >= 5)))
+		return;
+
+	ASSERT(GOODREGS(sii->curmap));
+	sb = REGS2SB(sii->curmap);
+
+	if (BUSTYPE(sii->pub.bustype) == SI_BUS) {
+		SET_SBREG(sii, &sb->sbimconfiglow,
+		          SBIMCL_RTO_MASK | SBIMCL_STO_MASK,
+		          (0x5 << SBIMCL_RTO_SHIFT) | 0x3);
+	} else {
+		if (sb_coreid(sih) == PCI_CORE_ID) {
+			SET_SBREG(sii, &sb->sbimconfiglow,
+			          SBIMCL_RTO_MASK | SBIMCL_STO_MASK,
+			          (0x3 << SBIMCL_RTO_SHIFT) | 0x2);
+		} else {
+			SET_SBREG(sii, &sb->sbimconfiglow, (SBIMCL_RTO_MASK | SBIMCL_STO_MASK), 0);
+		}
+	}
+
+	sb_commit(sih);
+}
+
+/*
+ * Set the initiator timeout for the "master core".
+ * The master core is defined to be the core in control
+ * of the chip and so it issues accesses to non-memory
+ * locations (Because of dma *any* core can access memeory).
+ *
+ * The routine uses the bus to decide who is the master:
+ *	SI_BUS => mips
+ *	JTAG_BUS => chipc
+ *	PCI_BUS => pci or pcie
+ *	PCMCIA_BUS => pcmcia
+ *	SDIO_BUS => pcmcia
+ *
+ * This routine exists so callers can disable initiator
+ * timeouts so accesses to very slow devices like otp
+ * won't cause an abort. The routine allows arbitrary
+ * settings of the service and request timeouts, though.
+ *
+ * Returns the timeout state before changing it or -1
+ * on error.
+ */
+
+#define	TO_MASK	(SBIMCL_RTO_MASK | SBIMCL_STO_MASK)
+
+uint32
+sb_set_initiator_to(si_t *sih, uint32 to, uint idx)
+{
+	si_info_t *sii;
+	uint origidx;
+	uint intr_val = 0;
+	uint32 tmp, ret = 0xffffffff;
+	sbconfig_t *sb;
+
+	sii = SI_INFO(sih);
+
+	if ((to & ~TO_MASK) != 0)
+		return ret;
+
+	/* Figure out the master core */
+	if (idx == BADIDX) {
+		switch (BUSTYPE(sii->pub.bustype)) {
+		case PCI_BUS:
+			idx = sii->pub.buscoreidx;
+			break;
+		case JTAG_BUS:
+			idx = SI_CC_IDX;
+			break;
+		case PCMCIA_BUS:
+		case SDIO_BUS:
+			idx = si_findcoreidx(sih, PCMCIA_CORE_ID, 0);
+			break;
+		case SI_BUS:
+			idx = si_findcoreidx(sih, MIPS33_CORE_ID, 0);
+			break;
+		default:
+			ASSERT(0);
+		}
+		if (idx == BADIDX)
+			return ret;
+	}
+
+	INTR_OFF(sii, intr_val);
+	origidx = si_coreidx(sih);
+
+	sb = REGS2SB(sb_setcoreidx(sih, idx));
+
+	tmp = R_SBREG(sii, &sb->sbimconfiglow);
+	ret = tmp & TO_MASK;
+	W_SBREG(sii, &sb->sbimconfiglow, (tmp & ~TO_MASK) | to);
+
+	sb_commit(sih);
+	sb_setcoreidx(sih, origidx);
+	INTR_RESTORE(sii, intr_val);
+	return ret;
+}
+
+uint32
+sb_base(uint32 admatch)
+{
+	uint32 base;
+	uint type;
+
+	type = admatch & SBAM_TYPE_MASK;
+	ASSERT(type < 3);
+
+	base = 0;
+
+	if (type == 0) {
+		base = admatch & SBAM_BASE0_MASK;
+	} else if (type == 1) {
+		ASSERT(!(admatch & SBAM_ADNEG));	/* neg not supported */
+		base = admatch & SBAM_BASE1_MASK;
+	} else if (type == 2) {
+		ASSERT(!(admatch & SBAM_ADNEG));	/* neg not supported */
+		base = admatch & SBAM_BASE2_MASK;
+	}
+
+	return (base);
+}
+
+uint32
+sb_size(uint32 admatch)
+{
+	uint32 size;
+	uint type;
+
+	type = admatch & SBAM_TYPE_MASK;
+	ASSERT(type < 3);
+
+	size = 0;
+
+	if (type == 0) {
+		size = 1 << (((admatch & SBAM_ADINT0_MASK) >> SBAM_ADINT0_SHIFT) + 1);
+	} else if (type == 1) {
+		ASSERT(!(admatch & SBAM_ADNEG));	/* neg not supported */
+		size = 1 << (((admatch & SBAM_ADINT1_MASK) >> SBAM_ADINT1_SHIFT) + 1);
+	} else if (type == 2) {
+		ASSERT(!(admatch & SBAM_ADNEG));	/* neg not supported */
+		size = 1 << (((admatch & SBAM_ADINT2_MASK) >> SBAM_ADINT2_SHIFT) + 1);
+	}
+
+	return (size);
+}
diff --git a/drivers/net/wireless/bcm4329/siutils.c b/drivers/net/wireless/bcm4329/siutils.c
new file mode 100644
index 0000000..1814db0
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/siutils.c
@@ -0,0 +1,1527 @@
+/*
+ * Misc utility routines for accessing chip-specific features
+ * of the SiliconBackplane-based Broadcom chips.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: siutils.c,v 1.662.4.4.4.16.4.28 2010/06/23 21:37:54 Exp $
+ */
+
+#include <typedefs.h>
+#include <bcmdefs.h>
+#include <osl.h>
+#include <bcmutils.h>
+#include <siutils.h>
+#include <bcmdevs.h>
+#include <hndsoc.h>
+#include <sbchipc.h>
+#include <pcicfg.h>
+#include <sbpcmcia.h>
+#include <sbsocram.h>
+#include <bcmsdh.h>
+#include <sdio.h>
+#include <sbsdio.h>
+#include <sbhnddma.h>
+#include <sbsdpcmdev.h>
+#include <bcmsdpcm.h>
+#include <hndpmu.h>
+
+#include "siutils_priv.h"
+
+/* local prototypes */
+static si_info_t *si_doattach(si_info_t *sii, uint devid, osl_t *osh, void *regs,
+                              uint bustype, void *sdh, char **vars, uint *varsz);
+static bool si_buscore_prep(si_info_t *sii, uint bustype, uint devid, void *sdh);
+static bool si_buscore_setup(si_info_t *sii, chipcregs_t *cc, uint bustype, uint32 savewin,
+	uint *origidx, void *regs);
+
+
+/* global variable to indicate reservation/release of gpio's */
+static uint32 si_gpioreservation = 0;
+static void *common_info_alloced = NULL;
+
+/* global flag to prevent shared resources from being initialized multiple times in si_attach() */
+
+/*
+ * Allocate a si handle.
+ * devid - pci device id (used to determine chip#)
+ * osh - opaque OS handle
+ * regs - virtual address of initial core registers
+ * bustype - pci/pcmcia/sb/sdio/etc
+ * vars - pointer to a pointer area for "environment" variables
+ * varsz - pointer to int to return the size of the vars
+ */
+si_t *
+si_attach(uint devid, osl_t *osh, void *regs,
+                       uint bustype, void *sdh, char **vars, uint *varsz)
+{
+	si_info_t *sii;
+
+	/* alloc si_info_t */
+	if ((sii = MALLOC(osh, sizeof (si_info_t))) == NULL) {
+		SI_ERROR(("si_attach: malloc failed! malloced %d bytes\n", MALLOCED(osh)));
+		return (NULL);
+	}
+
+	if (si_doattach(sii, devid, osh, regs, bustype, sdh, vars, varsz) == NULL) {
+		if (NULL != sii->common_info)
+			MFREE(osh, sii->common_info, sizeof(si_common_info_t));
+		MFREE(osh, sii, sizeof(si_info_t));
+		return (NULL);
+	}
+	sii->vars = vars ? *vars : NULL;
+	sii->varsz = varsz ? *varsz : 0;
+
+	return (si_t *)sii;
+}
+
+/* global kernel resource */
+static si_info_t ksii;
+
+static uint32	wd_msticks;		/* watchdog timer ticks normalized to ms */
+
+/* generic kernel variant of si_attach() */
+si_t *
+si_kattach(osl_t *osh)
+{
+	static bool ksii_attached = FALSE;
+
+	if (!ksii_attached) {
+		void *regs = REG_MAP(SI_ENUM_BASE, SI_CORE_SIZE);
+
+		if (si_doattach(&ksii, BCM4710_DEVICE_ID, osh, regs,
+		                SI_BUS, NULL,
+		                osh != SI_OSH ? &ksii.vars : NULL,
+		                osh != SI_OSH ? &ksii.varsz : NULL) == NULL) {
+			if (NULL != ksii.common_info)
+				MFREE(osh, ksii.common_info, sizeof(si_common_info_t));
+			SI_ERROR(("si_kattach: si_doattach failed\n"));
+			REG_UNMAP(regs);
+			return NULL;
+		}
+		REG_UNMAP(regs);
+
+		/* save ticks normalized to ms for si_watchdog_ms() */
+		if (PMUCTL_ENAB(&ksii.pub)) {
+			/* based on 32KHz ILP clock */
+			wd_msticks = 32;
+		} else {
+			wd_msticks = ALP_CLOCK / 1000;
+		}
+
+		ksii_attached = TRUE;
+		SI_MSG(("si_kattach done. ccrev = %d, wd_msticks = %d\n",
+		        ksii.pub.ccrev, wd_msticks));
+	}
+
+	return &ksii.pub;
+}
+
+
+static bool
+si_buscore_prep(si_info_t *sii, uint bustype, uint devid, void *sdh)
+{
+	/* need to set memseg flag for CF card first before any sb registers access */
+	if (BUSTYPE(bustype) == PCMCIA_BUS)
+		sii->memseg = TRUE;
+
+
+	if (BUSTYPE(bustype) == SDIO_BUS) {
+		int err;
+		uint8 clkset;
+
+		/* Try forcing SDIO core to do ALPAvail request only */
+		clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ;
+		bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
+		if (!err) {
+			uint8 clkval;
+
+			/* If register supported, wait for ALPAvail and then force ALP */
+			clkval = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, NULL);
+			if ((clkval & ~SBSDIO_AVBITS) == clkset) {
+				SPINWAIT(((clkval = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
+					SBSDIO_FUNC1_CHIPCLKCSR, NULL)), !SBSDIO_ALPAV(clkval)),
+					PMU_MAX_TRANSITION_DLY);
+				if (!SBSDIO_ALPAV(clkval)) {
+					SI_ERROR(("timeout on ALPAV wait, clkval 0x%02x\n",
+						clkval));
+					return FALSE;
+				}
+				clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP;
+				bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
+					clkset, &err);
+				OSL_DELAY(65);
+			}
+		}
+
+		/* Also, disable the extra SDIO pull-ups */
+		bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL);
+	}
+
+
+	return TRUE;
+}
+
+static bool
+si_buscore_setup(si_info_t *sii, chipcregs_t *cc, uint bustype, uint32 savewin,
+	uint *origidx, void *regs)
+{
+	bool pci, pcie;
+	uint i;
+	uint pciidx, pcieidx, pcirev, pcierev;
+
+	cc = si_setcoreidx(&sii->pub, SI_CC_IDX);
+	ASSERT((uintptr)cc);
+
+	/* get chipcommon rev */
+	sii->pub.ccrev = (int)si_corerev(&sii->pub);
+
+	/* get chipcommon chipstatus */
+	if (sii->pub.ccrev >= 11)
+		sii->pub.chipst = R_REG(sii->osh, &cc->chipstatus);
+
+	/* get chipcommon capabilites */
+	sii->pub.cccaps = R_REG(sii->osh, &cc->capabilities);
+
+	/* get pmu rev and caps */
+	if (sii->pub.cccaps & CC_CAP_PMU) {
+		sii->pub.pmucaps = R_REG(sii->osh, &cc->pmucapabilities);
+		sii->pub.pmurev = sii->pub.pmucaps & PCAP_REV_MASK;
+	}
+
+	SI_MSG(("Chipc: rev %d, caps 0x%x, chipst 0x%x pmurev %d, pmucaps 0x%x\n",
+		sii->pub.ccrev, sii->pub.cccaps, sii->pub.chipst, sii->pub.pmurev,
+		sii->pub.pmucaps));
+
+	/* figure out bus/orignal core idx */
+	sii->pub.buscoretype = NODEV_CORE_ID;
+	sii->pub.buscorerev = NOREV;
+	sii->pub.buscoreidx = BADIDX;
+
+	pci = pcie = FALSE;
+	pcirev = pcierev = NOREV;
+	pciidx = pcieidx = BADIDX;
+
+	for (i = 0; i < sii->numcores; i++) {
+		uint cid, crev;
+
+		si_setcoreidx(&sii->pub, i);
+		cid = si_coreid(&sii->pub);
+		crev = si_corerev(&sii->pub);
+
+		/* Display cores found */
+		SI_MSG(("CORE[%d]: id 0x%x rev %d base 0x%x regs 0x%p\n",
+		        i, cid, crev, sii->common_info->coresba[i], sii->common_info->regs[i]));
+
+		if (BUSTYPE(bustype) == PCI_BUS) {
+			if (cid == PCI_CORE_ID) {
+				pciidx = i;
+				pcirev = crev;
+				pci = TRUE;
+			} else if (cid == PCIE_CORE_ID) {
+				pcieidx = i;
+				pcierev = crev;
+				pcie = TRUE;
+			}
+		} else if ((BUSTYPE(bustype) == PCMCIA_BUS) &&
+		           (cid == PCMCIA_CORE_ID)) {
+			sii->pub.buscorerev = crev;
+			sii->pub.buscoretype = cid;
+			sii->pub.buscoreidx = i;
+		}
+		else if (((BUSTYPE(bustype) == SDIO_BUS) ||
+		          (BUSTYPE(bustype) == SPI_BUS)) &&
+		         ((cid == PCMCIA_CORE_ID) ||
+		          (cid == SDIOD_CORE_ID))) {
+			sii->pub.buscorerev = crev;
+			sii->pub.buscoretype = cid;
+			sii->pub.buscoreidx = i;
+		}
+
+		/* find the core idx before entering this func. */
+		if ((savewin && (savewin == sii->common_info->coresba[i])) ||
+		    (regs == sii->common_info->regs[i]))
+			*origidx = i;
+	}
+
+
+	SI_MSG(("Buscore id/type/rev %d/0x%x/%d\n", sii->pub.buscoreidx, sii->pub.buscoretype,
+		sii->pub.buscorerev));
+
+	if (BUSTYPE(sii->pub.bustype) == SI_BUS && (CHIPID(sii->pub.chip) == BCM4712_CHIP_ID) &&
+	    (sii->pub.chippkg != BCM4712LARGE_PKG_ID) && (sii->pub.chiprev <= 3))
+		OR_REG(sii->osh, &cc->slow_clk_ctl, SCC_SS_XTAL);
+
+
+	/* Make sure any on-chip ARM is off (in case strapping is wrong), or downloaded code was
+	 * already running.
+	 */
+	if ((BUSTYPE(bustype) == SDIO_BUS) || (BUSTYPE(bustype) == SPI_BUS)) {
+		if (si_setcore(&sii->pub, ARM7S_CORE_ID, 0) ||
+		    si_setcore(&sii->pub, ARMCM3_CORE_ID, 0))
+			si_core_disable(&sii->pub, 0);
+	}
+
+	/* return to the original core */
+	si_setcoreidx(&sii->pub, *origidx);
+
+	return TRUE;
+}
+
+
+
+static si_info_t *
+si_doattach(si_info_t *sii, uint devid, osl_t *osh, void *regs,
+                       uint bustype, void *sdh, char **vars, uint *varsz)
+{
+	struct si_pub *sih = &sii->pub;
+	uint32 w, savewin;
+	chipcregs_t *cc;
+	char *pvars = NULL;
+	uint origidx;
+
+	ASSERT(GOODREGS(regs));
+
+	bzero((uchar*)sii, sizeof(si_info_t));
+
+
+	{
+		if (NULL == (common_info_alloced = (void *)MALLOC(osh, sizeof(si_common_info_t)))) {
+			SI_ERROR(("si_doattach: malloc failed! malloced %dbytes\n", MALLOCED(osh)));
+			return (NULL);
+		}
+		bzero((uchar*)(common_info_alloced), sizeof(si_common_info_t));
+	}
+	sii->common_info = (si_common_info_t *)common_info_alloced;
+	sii->common_info->attach_count++;
+
+	savewin = 0;
+
+	sih->buscoreidx = BADIDX;
+
+	sii->curmap = regs;
+	sii->sdh = sdh;
+	sii->osh = osh;
+
+
+	/* find Chipcommon address */
+	if (bustype == PCI_BUS) {
+		savewin = OSL_PCI_READ_CONFIG(sii->osh, PCI_BAR0_WIN, sizeof(uint32));
+		if (!GOODCOREADDR(savewin, SI_ENUM_BASE))
+			savewin = SI_ENUM_BASE;
+		OSL_PCI_WRITE_CONFIG(sii->osh, PCI_BAR0_WIN, 4, SI_ENUM_BASE);
+		cc = (chipcregs_t *)regs;
+	} else
+	if ((bustype == SDIO_BUS) || (bustype == SPI_BUS)) {
+		cc = (chipcregs_t *)sii->curmap;
+	} else {
+		cc = (chipcregs_t *)REG_MAP(SI_ENUM_BASE, SI_CORE_SIZE);
+	}
+
+	sih->bustype = bustype;
+	if (bustype != BUSTYPE(bustype)) {
+		SI_ERROR(("si_doattach: bus type %d does not match configured bus type %d\n",
+			bustype, BUSTYPE(bustype)));
+		return NULL;
+	}
+
+	/* bus/core/clk setup for register access */
+	if (!si_buscore_prep(sii, bustype, devid, sdh)) {
+		SI_ERROR(("si_doattach: si_core_clk_prep failed %d\n", bustype));
+		return NULL;
+	}
+
+	/* ChipID recognition.
+	 *   We assume we can read chipid at offset 0 from the regs arg.
+	 *   If we add other chiptypes (or if we need to support old sdio hosts w/o chipcommon),
+	 *   some way of recognizing them needs to be added here.
+	 */
+	w = R_REG(osh, &cc->chipid);
+	sih->socitype = (w & CID_TYPE_MASK) >> CID_TYPE_SHIFT;
+	/* Might as wll fill in chip id rev & pkg */
+	sih->chip = w & CID_ID_MASK;
+	sih->chiprev = (w & CID_REV_MASK) >> CID_REV_SHIFT;
+	sih->chippkg = (w & CID_PKG_MASK) >> CID_PKG_SHIFT;
+	if ((CHIPID(sih->chip) == BCM4329_CHIP_ID) && (sih->chippkg != BCM4329_289PIN_PKG_ID))
+		sih->chippkg = BCM4329_182PIN_PKG_ID;
+	sih->issim = IS_SIM(sih->chippkg);
+
+	/* scan for cores */
+	if (CHIPTYPE(sii->pub.socitype) == SOCI_SB) {
+		SI_MSG(("Found chip type SB (0x%08x)\n", w));
+		sb_scan(&sii->pub, regs, devid);
+	} else if (CHIPTYPE(sii->pub.socitype) == SOCI_AI) {
+		SI_MSG(("Found chip type AI (0x%08x)\n", w));
+		/* pass chipc address instead of original core base */
+		ai_scan(&sii->pub, (void *)cc, devid);
+	} else {
+		SI_ERROR(("Found chip of unkown type (0x%08x)\n", w));
+		return NULL;
+	}
+	/* no cores found, bail out */
+	if (sii->numcores == 0) {
+		SI_ERROR(("si_doattach: could not find any cores\n"));
+		return NULL;
+	}
+	/* bus/core/clk setup */
+	origidx = SI_CC_IDX;
+	if (!si_buscore_setup(sii, cc, bustype, savewin, &origidx, regs)) {
+		SI_ERROR(("si_doattach: si_buscore_setup failed\n"));
+		return NULL;
+	}
+
+	pvars = NULL;
+
+
+
+		if (sii->pub.ccrev >= 20) {
+			cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0);
+			W_REG(osh, &cc->gpiopullup, 0);
+			W_REG(osh, &cc->gpiopulldown, 0);
+			si_setcoreidx(sih, origidx);
+		}
+
+		/* Skip PMU initialization from the Dongle Host.
+		 * Firmware will take care of it when it comes up.
+		 */
+
+
+
+	return (sii);
+}
+
+/* may be called with core in reset */
+void
+si_detach(si_t *sih)
+{
+	si_info_t *sii;
+	uint idx;
+
+	sii = SI_INFO(sih);
+
+	if (sii == NULL)
+		return;
+
+	if (BUSTYPE(sih->bustype) == SI_BUS)
+		for (idx = 0; idx < SI_MAXCORES; idx++)
+			if (sii->common_info->regs[idx]) {
+				REG_UNMAP(sii->common_info->regs[idx]);
+				sii->common_info->regs[idx] = NULL;
+			}
+
+
+	if (1 == sii->common_info->attach_count--) {
+		MFREE(sii->osh, sii->common_info, sizeof(si_common_info_t));
+		common_info_alloced = NULL;
+	}
+
+#if !defined(BCMBUSTYPE) || (BCMBUSTYPE == SI_BUS)
+	if (sii != &ksii)
+#endif	/* !BCMBUSTYPE || (BCMBUSTYPE == SI_BUS) */
+		MFREE(sii->osh, sii, sizeof(si_info_t));
+}
+
+void *
+si_osh(si_t *sih)
+{
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+	return sii->osh;
+}
+
+void
+si_setosh(si_t *sih, osl_t *osh)
+{
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+	if (sii->osh != NULL) {
+		SI_ERROR(("osh is already set....\n"));
+		ASSERT(!sii->osh);
+	}
+	sii->osh = osh;
+}
+
+/* register driver interrupt disabling and restoring callback functions */
+void
+si_register_intr_callback(si_t *sih, void *intrsoff_fn, void *intrsrestore_fn,
+                          void *intrsenabled_fn, void *intr_arg)
+{
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+	sii->intr_arg = intr_arg;
+	sii->intrsoff_fn = (si_intrsoff_t)intrsoff_fn;
+	sii->intrsrestore_fn = (si_intrsrestore_t)intrsrestore_fn;
+	sii->intrsenabled_fn = (si_intrsenabled_t)intrsenabled_fn;
+	/* save current core id.  when this function called, the current core
+	 * must be the core which provides driver functions(il, et, wl, etc.)
+	 */
+	sii->dev_coreid = sii->common_info->coreid[sii->curidx];
+}
+
+void
+si_deregister_intr_callback(si_t *sih)
+{
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+	sii->intrsoff_fn = NULL;
+}
+
+uint
+si_intflag(si_t *sih)
+{
+	si_info_t *sii = SI_INFO(sih);
+	if (CHIPTYPE(sih->socitype) == SOCI_SB) {
+		sbconfig_t *ccsbr = (sbconfig_t *)((uintptr)((ulong)
+			    (sii->common_info->coresba[SI_CC_IDX]) + SBCONFIGOFF));
+		return R_REG(sii->osh, &ccsbr->sbflagst);
+	} else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		return R_REG(sii->osh, ((uint32 *)(uintptr)
+			    (sii->common_info->oob_router + OOB_STATUSA)));
+	else {
+		ASSERT(0);
+		return 0;
+	}
+}
+
+uint
+si_flag(si_t *sih)
+{
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		return sb_flag(sih);
+	else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		return ai_flag(sih);
+	else {
+		ASSERT(0);
+		return 0;
+	}
+}
+
+void
+si_setint(si_t *sih, int siflag)
+{
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		sb_setint(sih, siflag);
+	else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		ai_setint(sih, siflag);
+	else
+		ASSERT(0);
+}
+
+uint
+si_coreid(si_t *sih)
+{
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+	return sii->common_info->coreid[sii->curidx];
+}
+
+uint
+si_coreidx(si_t *sih)
+{
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+	return sii->curidx;
+}
+
+/* return the core-type instantiation # of the current core */
+uint
+si_coreunit(si_t *sih)
+{
+	si_info_t *sii;
+	uint idx;
+	uint coreid;
+	uint coreunit;
+	uint i;
+
+	sii = SI_INFO(sih);
+	coreunit = 0;
+
+	idx = sii->curidx;
+
+	ASSERT(GOODREGS(sii->curmap));
+	coreid = si_coreid(sih);
+
+	/* count the cores of our type */
+	for (i = 0; i < idx; i++)
+		if (sii->common_info->coreid[i] == coreid)
+			coreunit++;
+
+	return (coreunit);
+}
+
+uint
+si_corevendor(si_t *sih)
+{
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		return sb_corevendor(sih);
+	else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		return ai_corevendor(sih);
+	else {
+		ASSERT(0);
+		return 0;
+	}
+}
+
+bool
+si_backplane64(si_t *sih)
+{
+	return ((sih->cccaps & CC_CAP_BKPLN64) != 0);
+}
+
+uint
+si_corerev(si_t *sih)
+{
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		return sb_corerev(sih);
+	else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		return ai_corerev(sih);
+	else {
+		ASSERT(0);
+		return 0;
+	}
+}
+
+/* return index of coreid or BADIDX if not found */
+uint
+si_findcoreidx(si_t *sih, uint coreid, uint coreunit)
+{
+	si_info_t *sii;
+	uint found;
+	uint i;
+
+	sii = SI_INFO(sih);
+
+	found = 0;
+
+	for (i = 0; i < sii->numcores; i++)
+		if (sii->common_info->coreid[i] == coreid) {
+			if (found == coreunit)
+				return (i);
+			found++;
+		}
+
+	return (BADIDX);
+}
+
+/* return list of found cores */
+uint
+si_corelist(si_t *sih, uint coreid[])
+{
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+
+	bcopy((uchar*)sii->common_info->coreid, (uchar*)coreid, (sii->numcores * sizeof(uint)));
+	return (sii->numcores);
+}
+
+/* return current register mapping */
+void *
+si_coreregs(si_t *sih)
+{
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+	ASSERT(GOODREGS(sii->curmap));
+
+	return (sii->curmap);
+}
+
+/*
+ * This function changes logical "focus" to the indicated core;
+ * must be called with interrupts off.
+ * Moreover, callers should keep interrupts off during switching out of and back to d11 core
+ */
+void *
+si_setcore(si_t *sih, uint coreid, uint coreunit)
+{
+	uint idx;
+
+	idx = si_findcoreidx(sih, coreid, coreunit);
+	if (!GOODIDX(idx))
+		return (NULL);
+
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		return sb_setcoreidx(sih, idx);
+	else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		return ai_setcoreidx(sih, idx);
+	else {
+		ASSERT(0);
+		return NULL;
+	}
+}
+
+void *
+si_setcoreidx(si_t *sih, uint coreidx)
+{
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		return sb_setcoreidx(sih, coreidx);
+	else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		return ai_setcoreidx(sih, coreidx);
+	else {
+		ASSERT(0);
+		return NULL;
+	}
+}
+
+/* Turn off interrupt as required by sb_setcore, before switch core */
+void *si_switch_core(si_t *sih, uint coreid, uint *origidx, uint *intr_val)
+{
+	void *cc;
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+
+	INTR_OFF(sii, *intr_val);
+	*origidx = sii->curidx;
+	cc = si_setcore(sih, coreid, 0);
+	ASSERT(cc != NULL);
+
+	return cc;
+}
+
+/* restore coreidx and restore interrupt */
+void si_restore_core(si_t *sih, uint coreid, uint intr_val)
+{
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+
+	si_setcoreidx(sih, coreid);
+	INTR_RESTORE(sii, intr_val);
+}
+
+int
+si_numaddrspaces(si_t *sih)
+{
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		return sb_numaddrspaces(sih);
+	else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		return ai_numaddrspaces(sih);
+	else {
+		ASSERT(0);
+		return 0;
+	}
+}
+
+uint32
+si_addrspace(si_t *sih, uint asidx)
+{
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		return sb_addrspace(sih, asidx);
+	else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		return ai_addrspace(sih, asidx);
+	else {
+		ASSERT(0);
+		return 0;
+	}
+}
+
+uint32
+si_addrspacesize(si_t *sih, uint asidx)
+{
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		return sb_addrspacesize(sih, asidx);
+	else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		return ai_addrspacesize(sih, asidx);
+	else {
+		ASSERT(0);
+		return 0;
+	}
+}
+
+uint32
+si_core_cflags(si_t *sih, uint32 mask, uint32 val)
+{
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		return sb_core_cflags(sih, mask, val);
+	else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		return ai_core_cflags(sih, mask, val);
+	else {
+		ASSERT(0);
+		return 0;
+	}
+}
+
+void
+si_core_cflags_wo(si_t *sih, uint32 mask, uint32 val)
+{
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		sb_core_cflags_wo(sih, mask, val);
+	else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		ai_core_cflags_wo(sih, mask, val);
+	else
+		ASSERT(0);
+}
+
+uint32
+si_core_sflags(si_t *sih, uint32 mask, uint32 val)
+{
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		return sb_core_sflags(sih, mask, val);
+	else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		return ai_core_sflags(sih, mask, val);
+	else {
+		ASSERT(0);
+		return 0;
+	}
+}
+
+bool
+si_iscoreup(si_t *sih)
+{
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		return sb_iscoreup(sih);
+	else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		return ai_iscoreup(sih);
+	else {
+		ASSERT(0);
+		return FALSE;
+	}
+}
+
+void
+si_write_wrapperreg(si_t *sih, uint32 offset, uint32 val)
+{
+	/* only for 4319, no requirement for SOCI_SB */
+	if (CHIPTYPE(sih->socitype) == SOCI_AI) {
+		ai_write_wrap_reg(sih, offset, val);
+	}
+	else
+		return;
+
+	return;
+}
+
+uint
+si_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val)
+{
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		return sb_corereg(sih, coreidx, regoff, mask, val);
+	else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		return ai_corereg(sih, coreidx, regoff, mask, val);
+	else {
+		ASSERT(0);
+		return 0;
+	}
+}
+
+void
+si_core_disable(si_t *sih, uint32 bits)
+{
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		sb_core_disable(sih, bits);
+	else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		ai_core_disable(sih, bits);
+}
+
+void
+si_core_reset(si_t *sih, uint32 bits, uint32 resetbits)
+{
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		sb_core_reset(sih, bits, resetbits);
+	else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		ai_core_reset(sih, bits, resetbits);
+}
+
+void
+si_core_tofixup(si_t *sih)
+{
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		sb_core_tofixup(sih);
+}
+
+/* Run bist on current core. Caller needs to take care of core-specific bist hazards */
+int
+si_corebist(si_t *sih)
+{
+	uint32 cflags;
+	int result = 0;
+
+	/* Read core control flags */
+	cflags = si_core_cflags(sih, 0, 0);
+
+	/* Set bist & fgc */
+	si_core_cflags(sih, 0, (SICF_BIST_EN | SICF_FGC));
+
+	/* Wait for bist done */
+	SPINWAIT(((si_core_sflags(sih, 0, 0) & SISF_BIST_DONE) == 0), 100000);
+
+	if (si_core_sflags(sih, 0, 0) & SISF_BIST_ERROR)
+		result = BCME_ERROR;
+
+	/* Reset core control flags */
+	si_core_cflags(sih, 0xffff, cflags);
+
+	return result;
+}
+
+static uint32
+factor6(uint32 x)
+{
+	switch (x) {
+	case CC_F6_2:	return 2;
+	case CC_F6_3:	return 3;
+	case CC_F6_4:	return 4;
+	case CC_F6_5:	return 5;
+	case CC_F6_6:	return 6;
+	case CC_F6_7:	return 7;
+	default:	return 0;
+	}
+}
+
+/* calculate the speed the SI would run at given a set of clockcontrol values */
+uint32
+si_clock_rate(uint32 pll_type, uint32 n, uint32 m)
+{
+	uint32 n1, n2, clock, m1, m2, m3, mc;
+
+	n1 = n & CN_N1_MASK;
+	n2 = (n & CN_N2_MASK) >> CN_N2_SHIFT;
+
+	if (pll_type == PLL_TYPE6) {
+		if (m & CC_T6_MMASK)
+			return CC_T6_M1;
+		else
+			return CC_T6_M0;
+	} else if ((pll_type == PLL_TYPE1) ||
+	           (pll_type == PLL_TYPE3) ||
+	           (pll_type == PLL_TYPE4) ||
+	           (pll_type == PLL_TYPE7)) {
+		n1 = factor6(n1);
+		n2 += CC_F5_BIAS;
+	} else if (pll_type == PLL_TYPE2) {
+		n1 += CC_T2_BIAS;
+		n2 += CC_T2_BIAS;
+		ASSERT((n1 >= 2) && (n1 <= 7));
+		ASSERT((n2 >= 5) && (n2 <= 23));
+	} else if (pll_type == PLL_TYPE5) {
+		return (100000000);
+	} else
+		ASSERT(0);
+	/* PLL types 3 and 7 use BASE2 (25Mhz) */
+	if ((pll_type == PLL_TYPE3) ||
+	    (pll_type == PLL_TYPE7)) {
+		clock = CC_CLOCK_BASE2 * n1 * n2;
+	} else
+		clock = CC_CLOCK_BASE1 * n1 * n2;
+
+	if (clock == 0)
+		return 0;
+
+	m1 = m & CC_M1_MASK;
+	m2 = (m & CC_M2_MASK) >> CC_M2_SHIFT;
+	m3 = (m & CC_M3_MASK) >> CC_M3_SHIFT;
+	mc = (m & CC_MC_MASK) >> CC_MC_SHIFT;
+
+	if ((pll_type == PLL_TYPE1) ||
+	    (pll_type == PLL_TYPE3) ||
+	    (pll_type == PLL_TYPE4) ||
+	    (pll_type == PLL_TYPE7)) {
+		m1 = factor6(m1);
+		if ((pll_type == PLL_TYPE1) || (pll_type == PLL_TYPE3))
+			m2 += CC_F5_BIAS;
+		else
+			m2 = factor6(m2);
+		m3 = factor6(m3);
+
+		switch (mc) {
+		case CC_MC_BYPASS:	return (clock);
+		case CC_MC_M1:		return (clock / m1);
+		case CC_MC_M1M2:	return (clock / (m1 * m2));
+		case CC_MC_M1M2M3:	return (clock / (m1 * m2 * m3));
+		case CC_MC_M1M3:	return (clock / (m1 * m3));
+		default:		return (0);
+		}
+	} else {
+		ASSERT(pll_type == PLL_TYPE2);
+
+		m1 += CC_T2_BIAS;
+		m2 += CC_T2M2_BIAS;
+		m3 += CC_T2_BIAS;
+		ASSERT((m1 >= 2) && (m1 <= 7));
+		ASSERT((m2 >= 3) && (m2 <= 10));
+		ASSERT((m3 >= 2) && (m3 <= 7));
+
+		if ((mc & CC_T2MC_M1BYP) == 0)
+			clock /= m1;
+		if ((mc & CC_T2MC_M2BYP) == 0)
+			clock /= m2;
+		if ((mc & CC_T2MC_M3BYP) == 0)
+			clock /= m3;
+
+		return (clock);
+	}
+}
+
+
+/* set chip watchdog reset timer to fire in 'ticks' */
+void
+si_watchdog(si_t *sih, uint ticks)
+{
+	if (PMUCTL_ENAB(sih)) {
+
+		if ((sih->chip == BCM4319_CHIP_ID) && (sih->chiprev == 0) && (ticks != 0)) {
+			si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, clk_ctl_st), ~0, 0x2);
+			si_setcore(sih, USB20D_CORE_ID, 0);
+			si_core_disable(sih, 1);
+			si_setcore(sih, CC_CORE_ID, 0);
+		}
+
+		if (ticks == 1)
+			ticks = 2;
+		si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, pmuwatchdog), ~0, ticks);
+	} else {
+		/* instant NMI */
+		si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, watchdog), ~0, ticks);
+	}
+}
+
+#if !defined(BCMBUSTYPE) || (BCMBUSTYPE == SI_BUS)
+/* trigger watchdog reset after ms milliseconds */
+void
+si_watchdog_ms(si_t *sih, uint32 ms)
+{
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+
+	si_watchdog(sih, wd_msticks * ms);
+}
+#endif
+
+
+
+/* initialize the sdio core */
+void
+si_sdio_init(si_t *sih)
+{
+	si_info_t *sii = SI_INFO(sih);
+
+	if (((sih->buscoretype == PCMCIA_CORE_ID) && (sih->buscorerev >= 8)) ||
+	    (sih->buscoretype == SDIOD_CORE_ID)) {
+		uint idx;
+		sdpcmd_regs_t *sdpregs;
+
+		/* get the current core index */
+		idx = sii->curidx;
+		ASSERT(idx == si_findcoreidx(sih, D11_CORE_ID, 0));
+
+		/* switch to sdio core */
+		if (!(sdpregs = (sdpcmd_regs_t *)si_setcore(sih, PCMCIA_CORE_ID, 0)))
+			sdpregs = (sdpcmd_regs_t *)si_setcore(sih, SDIOD_CORE_ID, 0);
+		ASSERT(sdpregs);
+
+		SI_MSG(("si_sdio_init: For PCMCIA/SDIO Corerev %d, enable ints from core %d "
+		        "through SD core %d (%p)\n",
+		        sih->buscorerev, idx, sii->curidx, sdpregs));
+
+		/* enable backplane error and core interrupts */
+		W_REG(sii->osh, &sdpregs->hostintmask, I_SBINT);
+		W_REG(sii->osh, &sdpregs->sbintmask, (I_SB_SERR | I_SB_RESPERR | (1 << idx)));
+
+		/* switch back to previous core */
+		si_setcoreidx(sih, idx);
+	}
+
+	/* enable interrupts */
+	bcmsdh_intr_enable(sii->sdh);
+
+}
+
+
+/* change logical "focus" to the gpio core for optimized access */
+void *
+si_gpiosetcore(si_t *sih)
+{
+	return (si_setcoreidx(sih, SI_CC_IDX));
+}
+
+/* mask&set gpiocontrol bits */
+uint32
+si_gpiocontrol(si_t *sih, uint32 mask, uint32 val, uint8 priority)
+{
+	uint regoff;
+
+	regoff = 0;
+
+	/* gpios could be shared on router platforms
+	 * ignore reservation if it's high priority (e.g., test apps)
+	 */
+	if ((priority != GPIO_HI_PRIORITY) &&
+	    (BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) {
+		mask = priority ? (si_gpioreservation & mask) :
+			((si_gpioreservation | mask) & ~(si_gpioreservation));
+		val &= mask;
+	}
+
+	regoff = OFFSETOF(chipcregs_t, gpiocontrol);
+	return (si_corereg(sih, SI_CC_IDX, regoff, mask, val));
+}
+
+/* mask&set gpio output enable bits */
+uint32
+si_gpioouten(si_t *sih, uint32 mask, uint32 val, uint8 priority)
+{
+	uint regoff;
+
+	regoff = 0;
+
+	/* gpios could be shared on router platforms
+	 * ignore reservation if it's high priority (e.g., test apps)
+	 */
+	if ((priority != GPIO_HI_PRIORITY) &&
+	    (BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) {
+		mask = priority ? (si_gpioreservation & mask) :
+			((si_gpioreservation | mask) & ~(si_gpioreservation));
+		val &= mask;
+	}
+
+	regoff = OFFSETOF(chipcregs_t, gpioouten);
+	return (si_corereg(sih, SI_CC_IDX, regoff, mask, val));
+}
+
+/* mask&set gpio output bits */
+uint32
+si_gpioout(si_t *sih, uint32 mask, uint32 val, uint8 priority)
+{
+	uint regoff;
+
+	regoff = 0;
+
+	/* gpios could be shared on router platforms
+	 * ignore reservation if it's high priority (e.g., test apps)
+	 */
+	if ((priority != GPIO_HI_PRIORITY) &&
+	    (BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) {
+		mask = priority ? (si_gpioreservation & mask) :
+			((si_gpioreservation | mask) & ~(si_gpioreservation));
+		val &= mask;
+	}
+
+	regoff = OFFSETOF(chipcregs_t, gpioout);
+	return (si_corereg(sih, SI_CC_IDX, regoff, mask, val));
+}
+
+/* reserve one gpio */
+uint32
+si_gpioreserve(si_t *sih, uint32 gpio_bitmask, uint8 priority)
+{
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+
+	/* only cores on SI_BUS share GPIO's and only applcation users need to
+	 * reserve/release GPIO
+	 */
+	if ((BUSTYPE(sih->bustype) != SI_BUS) || (!priority)) {
+		ASSERT((BUSTYPE(sih->bustype) == SI_BUS) && (priority));
+		return -1;
+	}
+	/* make sure only one bit is set */
+	if ((!gpio_bitmask) || ((gpio_bitmask) & (gpio_bitmask - 1))) {
+		ASSERT((gpio_bitmask) && !((gpio_bitmask) & (gpio_bitmask - 1)));
+		return -1;
+	}
+
+	/* already reserved */
+	if (si_gpioreservation & gpio_bitmask)
+		return -1;
+	/* set reservation */
+	si_gpioreservation |= gpio_bitmask;
+
+	return si_gpioreservation;
+}
+
+/* release one gpio */
+/*
+ * releasing the gpio doesn't change the current value on the GPIO last write value
+ * persists till some one overwrites it
+ */
+
+uint32
+si_gpiorelease(si_t *sih, uint32 gpio_bitmask, uint8 priority)
+{
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+
+	/* only cores on SI_BUS share GPIO's and only applcation users need to
+	 * reserve/release GPIO
+	 */
+	if ((BUSTYPE(sih->bustype) != SI_BUS) || (!priority)) {
+		ASSERT((BUSTYPE(sih->bustype) == SI_BUS) && (priority));
+		return -1;
+	}
+	/* make sure only one bit is set */
+	if ((!gpio_bitmask) || ((gpio_bitmask) & (gpio_bitmask - 1))) {
+		ASSERT((gpio_bitmask) && !((gpio_bitmask) & (gpio_bitmask - 1)));
+		return -1;
+	}
+
+	/* already released */
+	if (!(si_gpioreservation & gpio_bitmask))
+		return -1;
+
+	/* clear reservation */
+	si_gpioreservation &= ~gpio_bitmask;
+
+	return si_gpioreservation;
+}
+
+/* return the current gpioin register value */
+uint32
+si_gpioin(si_t *sih)
+{
+	si_info_t *sii;
+	uint regoff;
+
+	sii = SI_INFO(sih);
+	regoff = 0;
+
+	regoff = OFFSETOF(chipcregs_t, gpioin);
+	return (si_corereg(sih, SI_CC_IDX, regoff, 0, 0));
+}
+
+/* mask&set gpio interrupt polarity bits */
+uint32
+si_gpiointpolarity(si_t *sih, uint32 mask, uint32 val, uint8 priority)
+{
+	si_info_t *sii;
+	uint regoff;
+
+	sii = SI_INFO(sih);
+	regoff = 0;
+
+	/* gpios could be shared on router platforms */
+	if ((BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) {
+		mask = priority ? (si_gpioreservation & mask) :
+			((si_gpioreservation | mask) & ~(si_gpioreservation));
+		val &= mask;
+	}
+
+	regoff = OFFSETOF(chipcregs_t, gpiointpolarity);
+	return (si_corereg(sih, SI_CC_IDX, regoff, mask, val));
+}
+
+/* mask&set gpio interrupt mask bits */
+uint32
+si_gpiointmask(si_t *sih, uint32 mask, uint32 val, uint8 priority)
+{
+	si_info_t *sii;
+	uint regoff;
+
+	sii = SI_INFO(sih);
+	regoff = 0;
+
+	/* gpios could be shared on router platforms */
+	if ((BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) {
+		mask = priority ? (si_gpioreservation & mask) :
+			((si_gpioreservation | mask) & ~(si_gpioreservation));
+		val &= mask;
+	}
+
+	regoff = OFFSETOF(chipcregs_t, gpiointmask);
+	return (si_corereg(sih, SI_CC_IDX, regoff, mask, val));
+}
+
+/* assign the gpio to an led */
+uint32
+si_gpioled(si_t *sih, uint32 mask, uint32 val)
+{
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+	if (sih->ccrev < 16)
+		return -1;
+
+	/* gpio led powersave reg */
+	return (si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, gpiotimeroutmask), mask, val));
+}
+
+/* mask&set gpio timer val */
+uint32
+si_gpiotimerval(si_t *sih, uint32 mask, uint32 gpiotimerval)
+{
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+
+	if (sih->ccrev < 16)
+		return -1;
+
+	return (si_corereg(sih, SI_CC_IDX,
+		OFFSETOF(chipcregs_t, gpiotimerval), mask, gpiotimerval));
+}
+
+uint32
+si_gpiopull(si_t *sih, bool updown, uint32 mask, uint32 val)
+{
+	si_info_t *sii;
+	uint offs;
+
+	sii = SI_INFO(sih);
+	if (sih->ccrev < 20)
+		return -1;
+
+	offs = (updown ? OFFSETOF(chipcregs_t, gpiopulldown) : OFFSETOF(chipcregs_t, gpiopullup));
+	return (si_corereg(sih, SI_CC_IDX, offs, mask, val));
+}
+
+uint32
+si_gpioevent(si_t *sih, uint regtype, uint32 mask, uint32 val)
+{
+	si_info_t *sii;
+	uint offs;
+
+	sii = SI_INFO(sih);
+	if (sih->ccrev < 11)
+		return -1;
+
+	if (regtype == GPIO_REGEVT)
+		offs = OFFSETOF(chipcregs_t, gpioevent);
+	else if (regtype == GPIO_REGEVT_INTMSK)
+		offs = OFFSETOF(chipcregs_t, gpioeventintmask);
+	else if (regtype == GPIO_REGEVT_INTPOL)
+		offs = OFFSETOF(chipcregs_t, gpioeventintpolarity);
+	else
+		return -1;
+
+	return (si_corereg(sih, SI_CC_IDX, offs, mask, val));
+}
+
+void *
+si_gpio_handler_register(si_t *sih, uint32 event,
+	bool level, gpio_handler_t cb, void *arg)
+{
+	si_info_t *sii;
+	gpioh_item_t *gi;
+
+	ASSERT(event);
+	ASSERT(cb != NULL);
+
+	sii = SI_INFO(sih);
+	if (sih->ccrev < 11)
+		return NULL;
+
+	if ((gi = MALLOC(sii->osh, sizeof(gpioh_item_t))) == NULL)
+		return NULL;
+
+	bzero(gi, sizeof(gpioh_item_t));
+	gi->event = event;
+	gi->handler = cb;
+	gi->arg = arg;
+	gi->level = level;
+
+	gi->next = sii->gpioh_head;
+	sii->gpioh_head = gi;
+
+	return (void *)(gi);
+}
+
+void
+si_gpio_handler_unregister(si_t *sih, void *gpioh)
+{
+	si_info_t *sii;
+	gpioh_item_t *p, *n;
+
+	sii = SI_INFO(sih);
+	if (sih->ccrev < 11)
+		return;
+
+	ASSERT(sii->gpioh_head != NULL);
+	if ((void*)sii->gpioh_head == gpioh) {
+		sii->gpioh_head = sii->gpioh_head->next;
+		MFREE(sii->osh, gpioh, sizeof(gpioh_item_t));
+		return;
+	} else {
+		p = sii->gpioh_head;
+		n = p->next;
+		while (n) {
+			if ((void*)n == gpioh) {
+				p->next = n->next;
+				MFREE(sii->osh, gpioh, sizeof(gpioh_item_t));
+				return;
+			}
+			p = n;
+			n = n->next;
+		}
+	}
+
+	ASSERT(0); /* Not found in list */
+}
+
+void
+si_gpio_handler_process(si_t *sih)
+{
+	si_info_t *sii;
+	gpioh_item_t *h;
+	uint32 status;
+	uint32 level = si_gpioin(sih);
+	uint32 edge = si_gpioevent(sih, GPIO_REGEVT, 0, 0);
+
+	sii = SI_INFO(sih);
+	for (h = sii->gpioh_head; h != NULL; h = h->next) {
+		if (h->handler) {
+			status = (h->level ? level : edge);
+
+			if (status & h->event)
+				h->handler(status, h->arg);
+		}
+	}
+
+	si_gpioevent(sih, GPIO_REGEVT, edge, edge); /* clear edge-trigger status */
+}
+
+uint32
+si_gpio_int_enable(si_t *sih, bool enable)
+{
+	si_info_t *sii;
+	uint offs;
+
+	sii = SI_INFO(sih);
+	if (sih->ccrev < 11)
+		return -1;
+
+	offs = OFFSETOF(chipcregs_t, intmask);
+	return (si_corereg(sih, SI_CC_IDX, offs, CI_GPIO, (enable ? CI_GPIO : 0)));
+}
+
+
+/* Return the RAM size of the SOCRAM core */
+uint32
+si_socram_size(si_t *sih)
+{
+	si_info_t *sii;
+	uint origidx;
+	uint intr_val = 0;
+
+	sbsocramregs_t *regs;
+	bool wasup;
+	uint corerev;
+	uint32 coreinfo;
+	uint memsize = 0;
+
+	sii = SI_INFO(sih);
+
+	/* Block ints and save current core */
+	INTR_OFF(sii, intr_val);
+	origidx = si_coreidx(sih);
+
+	/* Switch to SOCRAM core */
+	if (!(regs = si_setcore(sih, SOCRAM_CORE_ID, 0)))
+		goto done;
+
+	/* Get info for determining size */
+	if (!(wasup = si_iscoreup(sih)))
+		si_core_reset(sih, 0, 0);
+	corerev = si_corerev(sih);
+	coreinfo = R_REG(sii->osh, &regs->coreinfo);
+
+	/* Calculate size from coreinfo based on rev */
+	if (corerev == 0)
+		memsize = 1 << (16 + (coreinfo & SRCI_MS0_MASK));
+	else if (corerev < 3) {
+		memsize = 1 << (SR_BSZ_BASE + (coreinfo & SRCI_SRBSZ_MASK));
+		memsize *= (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT;
+	} else {
+		uint nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT;
+		uint bsz = (coreinfo & SRCI_SRBSZ_MASK);
+		uint lss = (coreinfo & SRCI_LSS_MASK) >> SRCI_LSS_SHIFT;
+		if (lss != 0)
+			nb --;
+		memsize = nb * (1 << (bsz + SR_BSZ_BASE));
+		if (lss != 0)
+			memsize += (1 << ((lss - 1) + SR_BSZ_BASE));
+	}
+
+	/* Return to previous state and core */
+	if (!wasup)
+		si_core_disable(sih, 0);
+	si_setcoreidx(sih, origidx);
+
+done:
+	INTR_RESTORE(sii, intr_val);
+
+	return memsize;
+}
+
+
+void
+si_btcgpiowar(si_t *sih)
+{
+	si_info_t *sii;
+	uint origidx;
+	uint intr_val = 0;
+	chipcregs_t *cc;
+
+	sii = SI_INFO(sih);
+
+	/* Make sure that there is ChipCommon core present &&
+	 * UART_TX is strapped to 1
+	 */
+	if (!(sih->cccaps & CC_CAP_UARTGPIO))
+		return;
+
+	/* si_corereg cannot be used as we have to guarantee 8-bit read/writes */
+	INTR_OFF(sii, intr_val);
+
+	origidx = si_coreidx(sih);
+
+	cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0);
+	ASSERT(cc != NULL);
+
+	W_REG(sii->osh, &cc->uart0mcr, R_REG(sii->osh, &cc->uart0mcr) | 0x04);
+
+	/* restore the original index */
+	si_setcoreidx(sih, origidx);
+
+	INTR_RESTORE(sii, intr_val);
+}
+
+/* check if the device is removed */
+bool
+si_deviceremoved(si_t *sih)
+{
+	uint32 w;
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+
+	switch (BUSTYPE(sih->bustype)) {
+	case PCI_BUS:
+		ASSERT(sii->osh != NULL);
+		w = OSL_PCI_READ_CONFIG(sii->osh, PCI_CFG_VID, sizeof(uint32));
+		if ((w & 0xFFFF) != VENDOR_BROADCOM)
+			return TRUE;
+		else
+			return FALSE;
+	default:
+		return FALSE;
+	}
+	return FALSE;
+}
diff --git a/drivers/net/wireless/bcm4329/siutils_priv.h b/drivers/net/wireless/bcm4329/siutils_priv.h
new file mode 100644
index 0000000..e8ad7e5
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/siutils_priv.h
@@ -0,0 +1,213 @@
+/*
+ * Include file private to the SOC Interconnect support files.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: siutils_priv.h,v 1.3.10.5.4.2 2009/09/22 13:28:16 Exp $
+ */
+
+#ifndef	_siutils_priv_h_
+#define	_siutils_priv_h_
+
+/* debug/trace */
+#define	SI_ERROR(args)
+
+#define	SI_MSG(args)
+
+#define	IS_SIM(chippkg)	((chippkg == HDLSIM_PKG_ID) || (chippkg == HWSIM_PKG_ID))
+
+typedef uint32 (*si_intrsoff_t)(void *intr_arg);
+typedef void (*si_intrsrestore_t)(void *intr_arg, uint32 arg);
+typedef bool (*si_intrsenabled_t)(void *intr_arg);
+
+typedef struct gpioh_item {
+	void			*arg;
+	bool			level;
+	gpio_handler_t		handler;
+	uint32			event;
+	struct gpioh_item	*next;
+} gpioh_item_t;
+
+/* misc si info needed by some of the routines */
+typedef struct si_common_info {
+	void	*regs[SI_MAXCORES];	/* other regs va */
+	void	*regs2[SI_MAXCORES];	/* va of each core second register set (usbh20) */
+	uint	coreid[SI_MAXCORES];	/* id of each core */
+	uint32	cia[SI_MAXCORES];	/* erom cia entry for each core */
+	uint32	cib[SI_MAXCORES];	/* erom cia entry for each core */
+	uint32	coresba_size[SI_MAXCORES]; /* backplane address space size */
+	uint32	coresba2_size[SI_MAXCORES]; /* second address space size */
+	uint32	coresba[SI_MAXCORES];	/* backplane address of each core */
+	uint32	coresba2[SI_MAXCORES];	/* address of each core second register set (usbh20) */
+	void	*wrappers[SI_MAXCORES];	/* other cores wrapper va */
+	uint32	wrapba[SI_MAXCORES];	/* address of controlling wrapper */
+	uint32	oob_router;		/* oob router registers for axi */
+	uint8	attach_count;
+} si_common_info_t;
+
+typedef struct si_info {
+	struct si_pub pub;		/* back plane public state (must be first field) */
+
+	void	*osh;			/* osl os handle */
+	void	*sdh;			/* bcmsdh handle */
+	void *pch;			/* PCI/E core handle */
+	uint	dev_coreid;		/* the core provides driver functions */
+	void	*intr_arg;		/* interrupt callback function arg */
+	si_intrsoff_t intrsoff_fn;	/* turns chip interrupts off */
+	si_intrsrestore_t intrsrestore_fn; /* restore chip interrupts */
+	si_intrsenabled_t intrsenabled_fn; /* check if interrupts are enabled */
+
+
+	gpioh_item_t *gpioh_head; 	/* GPIO event handlers list */
+
+	bool	memseg;			/* flag to toggle MEM_SEG register */
+
+	char *vars;
+	uint varsz;
+
+	void	*curmap;		/* current regs va */
+
+	uint	curidx;			/* current core index */
+	uint	numcores;		/* # discovered cores */
+	void	*curwrap;		/* current wrapper va */
+	si_common_info_t	*common_info;	/* Common information for all the cores in a chip */
+} si_info_t;
+
+#define	SI_INFO(sih)	(si_info_t *)(uintptr)sih
+
+#define	GOODCOREADDR(x, b) (((x) >= (b)) && ((x) < ((b) + SI_MAXCORES * SI_CORE_SIZE)) && \
+		ISALIGNED((x), SI_CORE_SIZE))
+#define	GOODREGS(regs)	((regs) != NULL && ISALIGNED((uintptr)(regs), SI_CORE_SIZE))
+#define BADCOREADDR	0
+#define	GOODIDX(idx)	(((uint)idx) < SI_MAXCORES)
+#define	BADIDX		(SI_MAXCORES + 1)
+#define	NOREV		-1		/* Invalid rev */
+
+#define PCI(si)		((BUSTYPE((si)->pub.bustype) == PCI_BUS) &&	\
+			 ((si)->pub.buscoretype == PCI_CORE_ID))
+#define PCIE(si)	((BUSTYPE((si)->pub.bustype) == PCI_BUS) &&	\
+			 ((si)->pub.buscoretype == PCIE_CORE_ID))
+#define PCMCIA(si)	((BUSTYPE((si)->pub.bustype) == PCMCIA_BUS) && ((si)->memseg == TRUE))
+
+/* Newer chips can access PCI/PCIE and CC core without requiring to change
+ * PCI BAR0 WIN
+ */
+#define SI_FAST(si) (((si)->pub.buscoretype == PCIE_CORE_ID) ||	\
+		     (((si)->pub.buscoretype == PCI_CORE_ID) && (si)->pub.buscorerev >= 13))
+
+#define PCIEREGS(si) (((char *)((si)->curmap) + PCI_16KB0_PCIREGS_OFFSET))
+#define CCREGS_FAST(si) (((char *)((si)->curmap) + PCI_16KB0_CCREGS_OFFSET))
+
+/*
+ * Macros to disable/restore function core(D11, ENET, ILINE20, etc) interrupts before/
+ * after core switching to avoid invalid register accesss inside ISR.
+ */
+#define INTR_OFF(si, intr_val) \
+	if ((si)->intrsoff_fn && (si)->common_info->coreid[(si)->curidx] == (si)->dev_coreid) {	\
+		intr_val = (*(si)->intrsoff_fn)((si)->intr_arg); }
+#define INTR_RESTORE(si, intr_val) \
+	if ((si)->intrsrestore_fn && (si)->common_info->coreid[(si)->curidx] == (si)->dev_coreid) {\
+		(*(si)->intrsrestore_fn)((si)->intr_arg, intr_val); }
+
+/* dynamic clock control defines */
+#define	LPOMINFREQ		25000		/* low power oscillator min */
+#define	LPOMAXFREQ		43000		/* low power oscillator max */
+#define	XTALMINFREQ		19800000	/* 20 MHz - 1% */
+#define	XTALMAXFREQ		20200000	/* 20 MHz + 1% */
+#define	PCIMINFREQ		25000000	/* 25 MHz */
+#define	PCIMAXFREQ		34000000	/* 33 MHz + fudge */
+
+#define	ILP_DIV_5MHZ		0		/* ILP = 5 MHz */
+#define	ILP_DIV_1MHZ		4		/* ILP = 1 MHz */
+
+#define PCI_FORCEHT(si)	\
+	(((PCIE(si)) && (si->pub.chip == BCM4311_CHIP_ID) && ((si->pub.chiprev <= 1))) || \
+	((PCI(si) || PCIE(si)) && (si->pub.chip == BCM4321_CHIP_ID)))
+
+/* GPIO Based LED powersave defines */
+#define DEFAULT_GPIO_ONTIME	10		/* Default: 10% on */
+#define DEFAULT_GPIO_OFFTIME	90		/* Default: 10% on */
+
+#ifndef DEFAULT_GPIOTIMERVAL
+#define DEFAULT_GPIOTIMERVAL  ((DEFAULT_GPIO_ONTIME << GPIO_ONTIME_SHIFT) | DEFAULT_GPIO_OFFTIME)
+#endif
+
+/* Silicon Backplane externs */
+extern void sb_scan(si_t *sih, void *regs, uint devid);
+extern uint sb_coreid(si_t *sih);
+extern uint sb_flag(si_t *sih);
+extern void sb_setint(si_t *sih, int siflag);
+extern uint sb_corevendor(si_t *sih);
+extern uint sb_corerev(si_t *sih);
+extern uint sb_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val);
+extern bool sb_iscoreup(si_t *sih);
+extern void *sb_setcoreidx(si_t *sih, uint coreidx);
+extern uint32 sb_core_cflags(si_t *sih, uint32 mask, uint32 val);
+extern void sb_core_cflags_wo(si_t *sih, uint32 mask, uint32 val);
+extern uint32 sb_core_sflags(si_t *sih, uint32 mask, uint32 val);
+extern void sb_commit(si_t *sih);
+extern uint32 sb_base(uint32 admatch);
+extern uint32 sb_size(uint32 admatch);
+extern void sb_core_reset(si_t *sih, uint32 bits, uint32 resetbits);
+extern void sb_core_tofixup(si_t *sih);
+extern void sb_core_disable(si_t *sih, uint32 bits);
+extern uint32 sb_addrspace(si_t *sih, uint asidx);
+extern uint32 sb_addrspacesize(si_t *sih, uint asidx);
+extern int sb_numaddrspaces(si_t *sih);
+
+extern uint32 sb_set_initiator_to(si_t *sih, uint32 to, uint idx);
+
+
+
+/* Wake-on-wireless-LAN (WOWL) */
+extern bool sb_pci_pmecap(si_t *sih);
+struct osl_info;
+extern bool sb_pci_fastpmecap(struct osl_info *osh);
+extern bool sb_pci_pmeclr(si_t *sih);
+extern void sb_pci_pmeen(si_t *sih);
+extern uint sb_pcie_readreg(void *sih, uint addrtype, uint offset);
+
+/* AMBA Interconnect exported externs */
+extern si_t *ai_attach(uint pcidev, osl_t *osh, void *regs, uint bustype,
+                       void *sdh, char **vars, uint *varsz);
+extern si_t *ai_kattach(osl_t *osh);
+extern void ai_scan(si_t *sih, void *regs, uint devid);
+
+extern uint ai_flag(si_t *sih);
+extern void ai_setint(si_t *sih, int siflag);
+extern uint ai_coreidx(si_t *sih);
+extern uint ai_corevendor(si_t *sih);
+extern uint ai_corerev(si_t *sih);
+extern bool ai_iscoreup(si_t *sih);
+extern void *ai_setcoreidx(si_t *sih, uint coreidx);
+extern uint32 ai_core_cflags(si_t *sih, uint32 mask, uint32 val);
+extern void ai_core_cflags_wo(si_t *sih, uint32 mask, uint32 val);
+extern uint32 ai_core_sflags(si_t *sih, uint32 mask, uint32 val);
+extern uint ai_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val);
+extern void ai_core_reset(si_t *sih, uint32 bits, uint32 resetbits);
+extern void ai_core_disable(si_t *sih, uint32 bits);
+extern int ai_numaddrspaces(si_t *sih);
+extern uint32 ai_addrspace(si_t *sih, uint asidx);
+extern uint32 ai_addrspacesize(si_t *sih, uint asidx);
+extern void ai_write_wrap_reg(si_t *sih, uint32 offset, uint32 val);
+
+
+#endif	/* _siutils_priv_h_ */
diff --git a/drivers/net/wireless/bcm4329/wl_iw.c b/drivers/net/wireless/bcm4329/wl_iw.c
new file mode 100644
index 0000000..434e584
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/wl_iw.c
@@ -0,0 +1,8455 @@
+/*
+ * Linux Wireless Extensions support
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: wl_iw.c,v 1.51.4.9.2.6.4.142.4.78 2011/02/11 21:27:52 Exp $
+ */
+
+
+#include <typedefs.h>
+#include <linuxver.h>
+#include <osl.h>
+
+#include <bcmutils.h>
+#include <bcmendian.h>
+#include <proto/ethernet.h>
+
+#include <linux/if_arp.h>
+#include <asm/uaccess.h>
+
+#include <dngl_stats.h>
+#include <dhd.h>
+#include <dhdioctl.h>
+
+typedef void wlc_info_t;
+typedef void wl_info_t;
+typedef const struct si_pub  si_t;
+#include <wlioctl.h>
+
+#include <proto/ethernet.h>
+#include <dngl_stats.h>
+#include <dhd.h>
+#define WL_ERROR(x) printf x
+#define WL_TRACE(x)
+#define WL_ASSOC(x)
+#define WL_INFORM(x)
+#define WL_WSEC(x)
+#define WL_SCAN(x)
+#define WL_PNO(x)
+#define WL_TRACE_COEX(x)
+
+#include <wl_iw.h>
+
+
+
+#ifndef IW_ENCODE_ALG_SM4
+#define IW_ENCODE_ALG_SM4 0x20
+#endif
+
+#ifndef IW_AUTH_WAPI_ENABLED
+#define IW_AUTH_WAPI_ENABLED 0x20
+#endif
+
+#ifndef IW_AUTH_WAPI_VERSION_1
+#define IW_AUTH_WAPI_VERSION_1	0x00000008
+#endif
+
+#ifndef IW_AUTH_CIPHER_SMS4
+#define IW_AUTH_CIPHER_SMS4	0x00000020
+#endif
+
+#ifndef IW_AUTH_KEY_MGMT_WAPI_PSK
+#define IW_AUTH_KEY_MGMT_WAPI_PSK 4
+#endif
+
+#ifndef IW_AUTH_KEY_MGMT_WAPI_CERT
+#define IW_AUTH_KEY_MGMT_WAPI_CERT 8
+#endif
+
+
+#define IW_WSEC_ENABLED(wsec)	((wsec) & (WEP_ENABLED | TKIP_ENABLED | AES_ENABLED | SMS4_ENABLED))
+
+#include <linux/rtnetlink.h>
+#include <linux/mutex.h>
+
+#define WL_IW_USE_ISCAN  1
+#define ENABLE_ACTIVE_PASSIVE_SCAN_SUPPRESS  1
+
+#if defined(SOFTAP)
+#define WL_SOFTAP(x) printk x
+static struct net_device *priv_dev;
+static bool ap_cfg_running = FALSE;
+bool ap_fw_loaded = FALSE;
+static long ap_cfg_pid = -1;
+struct net_device *ap_net_dev = NULL;
+struct semaphore  ap_eth_sema;
+static struct completion ap_cfg_exited;
+static int wl_iw_set_ap_security(struct net_device *dev, struct ap_profile *ap);
+static int wl_iw_softap_deassoc_stations(struct net_device *dev, u8 *mac);
+#endif
+
+#define WL_IW_IOCTL_CALL(func_call) \
+	do {				\
+		func_call;		\
+	} while (0)
+
+static int		g_onoff = G_WLAN_SET_ON;
+wl_iw_extra_params_t	g_wl_iw_params;
+static struct mutex	wl_cache_lock;
+
+extern bool wl_iw_conn_status_str(uint32 event_type, uint32 status,
+	uint32 reason, char* stringBuf, uint buflen);
+#include <bcmsdbus.h>
+extern void dhd_customer_gpio_wlan_ctrl(int onoff);
+extern uint dhd_dev_reset(struct net_device *dev, uint8 flag);
+extern void dhd_dev_init_ioctl(struct net_device *dev);
+int dev_iw_write_cfg1_bss_var(struct net_device *dev, int val);
+
+uint wl_msg_level = WL_ERROR_VAL;
+
+#define MAX_WLIW_IOCTL_LEN 1024
+
+
+#if defined(IL_BIGENDIAN)
+#include <bcmendian.h>
+#define htod32(i) (bcmswap32(i))
+#define htod16(i) (bcmswap16(i))
+#define dtoh32(i) (bcmswap32(i))
+#define dtoh16(i) (bcmswap16(i))
+#define htodchanspec(i) htod16(i)
+#define dtohchanspec(i) dtoh16(i)
+#else
+#define htod32(i) i
+#define htod16(i) i
+#define dtoh32(i) i
+#define dtoh16(i) i
+#define htodchanspec(i) i
+#define dtohchanspec(i) i
+#endif
+
+#ifdef CONFIG_WIRELESS_EXT
+
+extern struct iw_statistics *dhd_get_wireless_stats(struct net_device *dev);
+extern int dhd_wait_pend8021x(struct net_device *dev);
+#endif 
+
+#if WIRELESS_EXT < 19
+#define IW_IOCTL_IDX(cmd)	((cmd) - SIOCIWFIRST)
+#define IW_EVENT_IDX(cmd)	((cmd) - IWEVFIRST)
+#endif 
+
+static void *g_scan = NULL;
+static volatile uint g_scan_specified_ssid;
+static wlc_ssid_t g_specific_ssid;
+
+static wlc_ssid_t g_ssid;
+
+bool btcoex_is_sco_active(struct net_device *dev);
+static wl_iw_ss_cache_ctrl_t g_ss_cache_ctrl;
+#if defined(CONFIG_FIRST_SCAN)
+static volatile uint g_first_broadcast_scan;
+static volatile uint g_first_counter_scans;
+#define MAX_ALLOWED_BLOCK_SCAN_FROM_FIRST_SCAN 3
+#endif
+
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+#define DAEMONIZE(a) daemonize(a); \
+	allow_signal(SIGKILL); \
+	allow_signal(SIGTERM);
+#else 
+#define RAISE_RX_SOFTIRQ() \
+	cpu_raise_softirq(smp_processor_id(), NET_RX_SOFTIRQ)
+#define DAEMONIZE(a) daemonize(); \
+	do { if (a) \
+		strncpy(current->comm, a, MIN(sizeof(current->comm), (strlen(a) + 1))); \
+	} while (0);
+#endif 
+
+#if defined(WL_IW_USE_ISCAN)
+#if !defined(CSCAN)
+static void wl_iw_free_ss_cache(void);
+static int   wl_iw_run_ss_cache_timer(int kick_off);
+#endif
+#if defined(CONFIG_FIRST_SCAN)
+int  wl_iw_iscan_set_scan_broadcast_prep(struct net_device *dev, uint flag);
+#endif
+static int dev_wlc_bufvar_set(struct net_device *dev, char *name, char *buf, int len);
+#define ISCAN_STATE_IDLE   0
+#define ISCAN_STATE_SCANING 1
+
+#define WLC_IW_ISCAN_MAXLEN   2048
+typedef struct iscan_buf {
+	struct iscan_buf * next;
+	char   iscan_buf[WLC_IW_ISCAN_MAXLEN];
+} iscan_buf_t;
+
+typedef struct iscan_info {
+	struct net_device *dev;
+	struct timer_list timer;
+	uint32 timer_ms;
+	uint32 timer_on;
+	int    iscan_state;
+	iscan_buf_t * list_hdr;
+	iscan_buf_t * list_cur;
+
+	
+	long sysioc_pid;
+	struct semaphore sysioc_sem;
+	struct completion sysioc_exited;
+
+	uint32 scan_flag;	
+#if defined CSCAN
+	char ioctlbuf[WLC_IOCTL_MEDLEN];
+#else
+	char ioctlbuf[WLC_IOCTL_SMLEN];
+#endif
+	wl_iscan_params_t *iscan_ex_params_p;
+	int iscan_ex_param_size;
+} iscan_info_t;
+#define COEX_DHCP 1
+
+#define BT_DHCP_eSCO_FIX
+#define BT_DHCP_USE_FLAGS
+#define BT_DHCP_OPPORTUNITY_WINDOW_TIME	 2500
+#define BT_DHCP_FLAG_FORCE_TIME 5500
+static void wl_iw_bt_flag_set(struct net_device *dev, bool set);
+static void wl_iw_bt_release(void);
+
+typedef enum bt_coex_status {
+	BT_DHCP_IDLE = 0,
+	BT_DHCP_START,
+	BT_DHCP_OPPORTUNITY_WINDOW,
+	BT_DHCP_FLAG_FORCE_TIMEOUT
+} coex_status_t;
+
+typedef struct bt_info {
+	struct net_device *dev;
+	struct timer_list timer;
+	uint32 timer_ms;
+	uint32 timer_on;
+	bool   dhcp_done;
+	int    bt_state;
+
+	long   bt_pid;
+	struct semaphore bt_sem;
+	struct completion bt_exited;
+} bt_info_t;
+
+bt_info_t *g_bt = NULL;
+static void wl_iw_bt_timerfunc(ulong data);
+iscan_info_t *g_iscan = NULL;
+static void wl_iw_timerfunc(ulong data);
+static void wl_iw_set_event_mask(struct net_device *dev);
+static int
+wl_iw_iscan(iscan_info_t *iscan, wlc_ssid_t *ssid, uint16 action);
+#endif 
+static int
+wl_iw_set_scan(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	union iwreq_data *wrqu,
+	char *extra
+);
+
+#ifndef CSCAN
+static int
+wl_iw_get_scan(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_point *dwrq,
+	char *extra
+);
+
+static uint
+wl_iw_get_scan_prep(
+	wl_scan_results_t *list,
+	struct iw_request_info *info,
+	char *extra,
+	short max_size
+);
+#endif
+
+static void swap_key_from_BE(
+	        wl_wsec_key_t *key
+)
+{
+	key->index = htod32(key->index);
+	key->len = htod32(key->len);
+	key->algo = htod32(key->algo);
+	key->flags = htod32(key->flags);
+	key->rxiv.hi = htod32(key->rxiv.hi);
+	key->rxiv.lo = htod16(key->rxiv.lo);
+	key->iv_initialized = htod32(key->iv_initialized);
+}
+
+static void swap_key_to_BE(
+	        wl_wsec_key_t *key
+)
+{
+	key->index = dtoh32(key->index);
+	key->len = dtoh32(key->len);
+	key->algo = dtoh32(key->algo);
+	key->flags = dtoh32(key->flags);
+	key->rxiv.hi = dtoh32(key->rxiv.hi);
+	key->rxiv.lo = dtoh16(key->rxiv.lo);
+	key->iv_initialized = dtoh32(key->iv_initialized);
+}
+
+static int
+dev_wlc_ioctl(
+	struct net_device *dev,
+	int cmd,
+	void *arg,
+	int len
+)
+{
+	struct ifreq ifr;
+	wl_ioctl_t ioc;
+	mm_segment_t fs;
+	int ret = -EINVAL;
+
+	if (!dev) {
+		WL_ERROR(("%s: dev is null\n", __FUNCTION__));
+		return ret;
+	}
+
+	net_os_wake_lock(dev);
+
+	WL_INFORM(("\n%s, PID:%x: send Local IOCTL -> dhd: cmd:0x%x, buf:%p, len:%d ,\n",
+		__FUNCTION__, current->pid, cmd, arg, len));
+
+	if (g_onoff == G_WLAN_SET_ON) {
+		memset(&ioc, 0, sizeof(ioc));
+		ioc.cmd = cmd;
+		ioc.buf = arg;
+		ioc.len = len;
+
+		strcpy(ifr.ifr_name, dev->name);
+		ifr.ifr_data = (caddr_t) &ioc;
+
+		ret = dev_open(dev);
+		if (ret) {
+			WL_ERROR(("%s: Error dev_open: %d\n", __func__, ret));
+			net_os_wake_unlock(dev);
+			return ret;
+		}
+
+		fs = get_fs();
+		set_fs(get_ds());
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 31))
+		ret = dev->do_ioctl(dev, &ifr, SIOCDEVPRIVATE);
+#else
+		ret = dev->netdev_ops->ndo_do_ioctl(dev, &ifr, SIOCDEVPRIVATE);
+#endif
+		set_fs(fs);
+	}
+	else {
+		WL_TRACE(("%s: call after driver stop : ignored\n", __FUNCTION__));
+	}
+
+	net_os_wake_unlock(dev);
+
+	return ret;
+}
+
+
+static int
+dev_wlc_intvar_get_reg(
+	struct net_device *dev,
+	char *name,
+	uint  reg,
+	int *retval)
+{
+	union {
+		char buf[WLC_IOCTL_SMLEN];
+		int val;
+	} var;
+	int error;
+
+	uint len;
+	len = bcm_mkiovar(name, (char *)(&reg), sizeof(reg), (char *)(&var), sizeof(var.buf));
+	ASSERT(len);
+	error = dev_wlc_ioctl(dev, WLC_GET_VAR, (void *)&var, len);
+
+	*retval = dtoh32(var.val);
+	return (error);
+}
+
+
+static int
+dev_wlc_intvar_set_reg(
+	struct net_device *dev,
+	char *name,
+	char *addr,
+	char * val)
+{
+	char reg_addr[8];
+
+	memset(reg_addr, 0, sizeof(reg_addr));
+	memcpy((char *)&reg_addr[0], (char *)addr, 4);
+	memcpy((char *)&reg_addr[4], (char *)val, 4);
+
+	return (dev_wlc_bufvar_set(dev, name,  (char *)&reg_addr[0], sizeof(reg_addr)));
+}
+
+
+static int
+dev_wlc_intvar_set(
+	struct net_device *dev,
+	char *name,
+	int val)
+{
+	char buf[WLC_IOCTL_SMLEN];
+	uint len;
+
+	val = htod32(val);
+	len = bcm_mkiovar(name, (char *)(&val), sizeof(val), buf, sizeof(buf));
+	ASSERT(len);
+
+	return (dev_wlc_ioctl(dev, WLC_SET_VAR, buf, len));
+}
+
+#if defined(WL_IW_USE_ISCAN)
+static int
+dev_iw_iovar_setbuf(
+	struct net_device *dev,
+	char *iovar,
+	void *param,
+	int paramlen,
+	void *bufptr,
+	int buflen)
+{
+	int iolen;
+
+	iolen = bcm_mkiovar(iovar, param, paramlen, bufptr, buflen);
+	ASSERT(iolen);
+
+	if (iolen == 0)
+		return 0;
+
+	return (dev_wlc_ioctl(dev, WLC_SET_VAR, bufptr, iolen));
+}
+
+static int
+dev_iw_iovar_getbuf(
+	struct net_device *dev,
+	char *iovar,
+	void *param,
+	int paramlen,
+	void *bufptr,
+	int buflen)
+{
+	int iolen;
+
+	iolen = bcm_mkiovar(iovar, param, paramlen, bufptr, buflen);
+	ASSERT(iolen);
+
+	return (dev_wlc_ioctl(dev, WLC_GET_VAR, bufptr, buflen));
+}
+#endif 
+
+
+#if WIRELESS_EXT > 17
+static int
+dev_wlc_bufvar_set(
+	struct net_device *dev,
+	char *name,
+	char *buf, int len)
+{
+	static char ioctlbuf[MAX_WLIW_IOCTL_LEN];
+	uint buflen;
+
+	buflen = bcm_mkiovar(name, buf, len, ioctlbuf, sizeof(ioctlbuf));
+	ASSERT(buflen);
+
+	return (dev_wlc_ioctl(dev, WLC_SET_VAR, ioctlbuf, buflen));
+}
+#endif
+
+
+static int
+dev_wlc_bufvar_get(
+	struct net_device *dev,
+	char *name,
+	char *buf, int buflen)
+{
+	static char ioctlbuf[MAX_WLIW_IOCTL_LEN];
+	int error;
+	uint len;
+
+	len = bcm_mkiovar(name, NULL, 0, ioctlbuf, sizeof(ioctlbuf));
+	ASSERT(len);
+	error = dev_wlc_ioctl(dev, WLC_GET_VAR, (void *)ioctlbuf, MAX_WLIW_IOCTL_LEN);
+	if (!error)
+		bcopy(ioctlbuf, buf, buflen);
+
+	return (error);
+}
+
+
+
+static int
+dev_wlc_intvar_get(
+	struct net_device *dev,
+	char *name,
+	int *retval)
+{
+	union {
+		char buf[WLC_IOCTL_SMLEN];
+		int val;
+	} var;
+	int error;
+
+	uint len;
+	uint data_null;
+
+	len = bcm_mkiovar(name, (char *)(&data_null), 0, (char *)(&var), sizeof(var.buf));
+	ASSERT(len);
+	error = dev_wlc_ioctl(dev, WLC_GET_VAR, (void *)&var, len);
+
+	*retval = dtoh32(var.val);
+
+	return (error);
+}
+
+
+#if WIRELESS_EXT > 12
+static int
+wl_iw_set_active_scan(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	union iwreq_data *wrqu,
+	char *extra
+)
+{
+	int as = 0;
+	int error = 0;
+	char *p = extra;
+
+#if defined(WL_IW_USE_ISCAN)
+	if (g_iscan->iscan_state == ISCAN_STATE_IDLE)
+#endif 
+		error = dev_wlc_ioctl(dev, WLC_SET_PASSIVE_SCAN, &as, sizeof(as));
+#if defined(WL_IW_USE_ISCAN)
+	else
+		g_iscan->scan_flag = as;
+#endif 
+	p += snprintf(p, MAX_WX_STRING, "OK");
+
+	wrqu->data.length = p - extra + 1;
+	return error;
+}
+
+static int
+wl_iw_set_passive_scan(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	union iwreq_data *wrqu,
+	char *extra
+)
+{
+	int ps = 1;
+	int error = 0;
+	char *p = extra;
+
+#if defined(WL_IW_USE_ISCAN)
+	if (g_iscan->iscan_state == ISCAN_STATE_IDLE) {
+#endif 
+
+		 
+		if (g_scan_specified_ssid == 0) {
+			error = dev_wlc_ioctl(dev, WLC_SET_PASSIVE_SCAN, &ps, sizeof(ps));
+		}
+#if defined(WL_IW_USE_ISCAN)
+	}
+	else
+		g_iscan->scan_flag = ps;
+#endif 
+
+	p += snprintf(p, MAX_WX_STRING, "OK");
+
+	wrqu->data.length = p - extra + 1;
+	return error;
+}
+
+
+static int
+wl_iw_set_txpower(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	union iwreq_data *wrqu,
+	char *extra
+)
+{
+	int error = 0;
+	char *p = extra;
+	int txpower = -1;
+
+	txpower = bcm_atoi(extra + strlen(TXPOWER_SET_CMD) + 1);
+	if ((txpower >= 0) && (txpower <= 127)) {
+		txpower |= WL_TXPWR_OVERRIDE;
+		txpower = htod32(txpower);
+
+		error = dev_wlc_intvar_set(dev, "qtxpower", txpower);
+		p += snprintf(p, MAX_WX_STRING, "OK");
+		WL_TRACE(("%s: set TXpower 0x%X is OK\n", __FUNCTION__, txpower));
+	} else {
+		WL_ERROR(("%s: set tx power failed\n", __FUNCTION__));
+		p += snprintf(p, MAX_WX_STRING, "FAIL");
+	}
+
+	wrqu->data.length = p - extra + 1;
+	return error;
+}
+
+static int
+wl_iw_get_macaddr(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	union iwreq_data *wrqu,
+	char *extra
+)
+{
+	int error;
+	char buf[128];
+	struct ether_addr *id;
+	char *p = extra;
+
+	
+	strcpy(buf, "cur_etheraddr");
+	error = dev_wlc_ioctl(dev, WLC_GET_VAR, buf, sizeof(buf));
+	id = (struct ether_addr *) buf;
+	p += snprintf(p, MAX_WX_STRING, "Macaddr = %02X:%02X:%02X:%02X:%02X:%02X\n",
+		id->octet[0], id->octet[1], id->octet[2],
+		id->octet[3], id->octet[4], id->octet[5]);
+	wrqu->data.length = p - extra + 1;
+
+	return error;
+}
+
+
+static int
+wl_iw_set_country(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	union iwreq_data *wrqu,
+	char *extra
+)
+{
+	char country_code[WLC_CNTRY_BUF_SZ];
+	int error = 0;
+	char *p = extra;
+	int country_offset;
+	int country_code_size;
+	wl_country_t cspec = {{0}, 0, {0}};
+	char smbuf[WLC_IOCTL_SMLEN];
+
+	cspec.rev = -1;
+	memset(country_code, 0, sizeof(country_code));
+	memset(smbuf, 0, sizeof(smbuf));
+
+	country_offset = strcspn(extra, " ");
+	country_code_size = strlen(extra) - country_offset;
+
+	if (country_offset != 0) {
+		strncpy(country_code, extra + country_offset +1,
+			MIN(country_code_size, sizeof(country_code)));
+
+
+		memcpy(cspec.country_abbrev, country_code, WLC_CNTRY_BUF_SZ);
+		memcpy(cspec.ccode, country_code, WLC_CNTRY_BUF_SZ);
+
+		get_customized_country_code((char *)&cspec.country_abbrev, &cspec);
+
+		if ((error = dev_iw_iovar_setbuf(dev, "country", &cspec, \
+			sizeof(cspec), smbuf, sizeof(smbuf))) >= 0) {
+			p += snprintf(p, MAX_WX_STRING, "OK");
+			WL_ERROR(("%s: set country for %s as %s rev %d is OK\n", \
+				__FUNCTION__, country_code, cspec.ccode, cspec.rev));
+			dhd_bus_country_set(dev, &cspec);
+			goto exit;
+		}
+	}
+
+	WL_ERROR(("%s: set country for %s as %s rev %d failed\n", \
+			__FUNCTION__, country_code, cspec.ccode, cspec.rev));
+
+	p += snprintf(p, MAX_WX_STRING, "FAIL");
+
+exit:
+	wrqu->data.length = p - extra + 1;
+	return error;
+}
+
+#ifdef CUSTOMER_HW2
+static int
+wl_iw_set_power_mode(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	union iwreq_data *wrqu,
+	char *extra
+)
+{
+	int error = 0;
+	char *p = extra;
+	static int pm = PM_FAST;
+	int pm_local = PM_OFF;
+	char powermode_val = 0;
+
+	WL_TRACE_COEX(("%s: DHCP session cmd:%s\n", __FUNCTION__, extra));
+
+	strncpy((char *)&powermode_val, extra + strlen("POWERMODE") + 1, 1);
+
+	if (strnicmp((char *)&powermode_val, "1", strlen("1")) == 0) {
+
+		dev_wlc_ioctl(dev, WLC_GET_PM, &pm, sizeof(pm));
+		dev_wlc_ioctl(dev, WLC_SET_PM, &pm_local, sizeof(pm_local));
+
+		/* Disable packet filtering if necessary */
+		net_os_set_packet_filter(dev, 0);
+
+		g_bt->dhcp_done = false;
+		WL_TRACE_COEX(("%s: DHCP start, pm:%d changed to pm:%d\n",
+			__FUNCTION__, pm, pm_local));
+
+	} else if (strnicmp((char *)&powermode_val, "0", strlen("0")) == 0) {
+
+		dev_wlc_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm));
+
+		/* Enable packet filtering if was turned off */
+		net_os_set_packet_filter(dev, 1);
+
+		g_bt->dhcp_done = true;
+
+	} else {
+		WL_ERROR(("%s Unkwown yet power setting, ignored\n",
+			__FUNCTION__));
+	}
+
+	p += snprintf(p, MAX_WX_STRING, "OK");
+
+	wrqu->data.length = p - extra + 1;
+
+	return error;
+}
+#endif
+
+
+bool btcoex_is_sco_active(struct net_device *dev)
+{
+	int ioc_res = 0;
+	bool res = false;
+	int sco_id_cnt = 0;
+	int param27;
+	int i;
+
+	for (i = 0; i < 12; i++) {
+
+		ioc_res = dev_wlc_intvar_get_reg(dev, "btc_params", 27, &param27);
+
+		WL_TRACE_COEX(("%s, sample[%d], btc params: 27:%x\n",
+			__FUNCTION__, i, param27));
+
+		if (ioc_res < 0) {
+			WL_ERROR(("%s ioc read btc params error\n", __FUNCTION__));
+			break;
+		}
+
+		if ((param27 & 0x6) == 2) {
+			sco_id_cnt++;
+		}
+
+		if (sco_id_cnt > 2) {
+			WL_TRACE_COEX(("%s, sco/esco detected, pkt id_cnt:%d  samples:%d\n",
+				__FUNCTION__, sco_id_cnt, i));
+			res = true;
+			break;
+		}
+
+		msleep(5);
+	}
+
+	return res;
+}
+
+#if defined(BT_DHCP_eSCO_FIX)
+
+static int set_btc_esco_params(struct net_device *dev, bool trump_sco)
+{
+	static bool saved_status = false;
+
+	char buf_reg50va_dhcp_on[8] = { 50, 00, 00, 00, 0x22, 0x80, 0x00, 0x00 };
+	char buf_reg51va_dhcp_on[8] = { 51, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 };
+	char buf_reg64va_dhcp_on[8] = { 64, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 };
+	char buf_reg65va_dhcp_on[8] = { 65, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 };
+	char buf_reg71va_dhcp_on[8] = { 71, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 };
+
+	uint32 regaddr;
+	static uint32 saved_reg50;
+	static uint32 saved_reg51;
+	static uint32 saved_reg64;
+	static uint32 saved_reg65;
+	static uint32 saved_reg71;
+
+	if (trump_sco) {
+
+		WL_TRACE_COEX(("Do new SCO/eSCO coex algo {save & override} \n"));
+
+		if  ((!dev_wlc_intvar_get_reg(dev, "btc_params", 50,  &saved_reg50)) &&
+			(!dev_wlc_intvar_get_reg(dev, "btc_params", 51,  &saved_reg51)) &&
+			(!dev_wlc_intvar_get_reg(dev, "btc_params", 64,  &saved_reg64)) &&
+			(!dev_wlc_intvar_get_reg(dev, "btc_params", 65,  &saved_reg65)) &&
+			(!dev_wlc_intvar_get_reg(dev, "btc_params", 71,  &saved_reg71))) {
+
+			saved_status = TRUE;
+			WL_TRACE_COEX(("%s saved bt_params[50,51,64,65,71]:"
+				" 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+				__FUNCTION__, saved_reg50, saved_reg51,
+				saved_reg64, saved_reg65, saved_reg71));
+
+		} else {
+			WL_ERROR((":%s: save btc_params failed\n",
+				__FUNCTION__));
+			saved_status = false;
+			return -1;
+		}
+
+		WL_TRACE_COEX(("override with [50,51,64,65,71]:"
+			" 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+			*(u32 *)(buf_reg50va_dhcp_on+4),
+			*(u32 *)(buf_reg51va_dhcp_on+4),
+			*(u32 *)(buf_reg64va_dhcp_on+4),
+			*(u32 *)(buf_reg65va_dhcp_on+4),
+			*(u32 *)(buf_reg71va_dhcp_on+4)));
+
+		dev_wlc_bufvar_set(dev, "btc_params", (char *)&buf_reg50va_dhcp_on[0], 8);
+		dev_wlc_bufvar_set(dev, "btc_params", (char *)&buf_reg51va_dhcp_on[0], 8);
+		dev_wlc_bufvar_set(dev, "btc_params", (char *)&buf_reg64va_dhcp_on[0], 8);
+		dev_wlc_bufvar_set(dev, "btc_params", (char *)&buf_reg65va_dhcp_on[0], 8);
+		dev_wlc_bufvar_set(dev, "btc_params", (char *)&buf_reg71va_dhcp_on[0], 8);
+
+		saved_status = true;
+
+	} else if (saved_status) {
+
+		WL_TRACE_COEX(("Do new SCO/eSCO coex algo {save & override} \n"));
+
+		regaddr = 50;
+		dev_wlc_intvar_set_reg(dev, "btc_params",
+			(char *)&regaddr, (char *)&saved_reg50);
+		regaddr = 51;
+		dev_wlc_intvar_set_reg(dev, "btc_params",
+			(char *)&regaddr, (char *)&saved_reg51);
+		regaddr = 64;
+		dev_wlc_intvar_set_reg(dev, "btc_params",
+			(char *)&regaddr, (char *)&saved_reg64);
+		regaddr = 65;
+		dev_wlc_intvar_set_reg(dev, "btc_params",
+			(char *)&regaddr, (char *)&saved_reg65);
+		regaddr = 71;
+		dev_wlc_intvar_set_reg(dev, "btc_params",
+			(char *)&regaddr, (char *)&saved_reg71);
+
+		WL_TRACE_COEX(("restore bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+			saved_reg50, saved_reg51, saved_reg64,
+			saved_reg65, saved_reg71));
+
+		saved_status = false;
+	} else {
+		WL_ERROR((":%s att to restore not saved BTCOEX params\n",
+			__FUNCTION__));
+		return -1;
+	}
+	return 0;
+}
+#endif
+
+static int
+wl_iw_get_power_mode(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	union iwreq_data *wrqu,
+	char *extra
+)
+{
+	int error;
+	char *p = extra;
+	int pm_local = PM_FAST;
+
+	error = dev_wlc_ioctl(dev, WLC_GET_PM, &pm_local, sizeof(pm_local));
+	if (!error) {
+		WL_TRACE(("%s: Powermode = %d\n", __func__, pm_local));
+		if (pm_local == PM_OFF)
+			pm_local = 1; /* Active */
+		else
+			pm_local = 0; /* Auto */
+		p += snprintf(p, MAX_WX_STRING, "powermode = %d", pm_local);
+	}
+	else {
+		WL_TRACE(("%s: Error = %d\n", __func__, error));
+		p += snprintf(p, MAX_WX_STRING, "FAIL");
+	}
+	wrqu->data.length = p - extra + 1;
+	return error;
+}
+
+static int
+wl_iw_set_btcoex_dhcp(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	union iwreq_data *wrqu,
+	char *extra
+)
+{
+	int error = 0;
+	char *p = extra;
+#ifndef CUSTOMER_HW2
+	static int  pm = PM_FAST;
+	int  pm_local = PM_OFF;
+#endif
+	char powermode_val = 0;
+	char buf_reg66va_dhcp_on[8] = { 66, 00, 00, 00, 0x10, 0x27, 0x00, 0x00 };
+	char buf_reg41va_dhcp_on[8] = { 41, 00, 00, 00, 0x33, 0x00, 0x00, 0x00 };
+	char buf_reg68va_dhcp_on[8] = { 68, 00, 00, 00, 0x90, 0x01, 0x00, 0x00 };
+
+	uint32 regaddr;
+	static uint32 saved_reg66;
+	static uint32 saved_reg41;
+	static uint32 saved_reg68;
+	static bool saved_status = FALSE;
+
+	char buf_flag7_default[8] =   { 7, 00, 00, 00, 0x0, 0x00, 0x00, 0x00};
+
+#ifdef CUSTOMER_HW2
+	strncpy((char *)&powermode_val, extra + strlen("BTCOEXMODE") + 1, 1);
+#else
+	strncpy((char *)&powermode_val, extra + strlen("POWERMODE") + 1, 1);
+#endif
+
+	if (strnicmp((char *)&powermode_val, "1", strlen("1")) == 0) {
+
+		WL_TRACE_COEX(("%s: DHCP session start, cmd:%s\n", __FUNCTION__, extra));
+
+		if ((saved_status == FALSE) &&
+#ifndef CUSTOMER_HW2
+		   (!dev_wlc_ioctl(dev, WLC_GET_PM, &pm, sizeof(pm))) &&
+#endif
+		   (!dev_wlc_intvar_get_reg(dev, "btc_params", 66,  &saved_reg66)) &&
+		   (!dev_wlc_intvar_get_reg(dev, "btc_params", 41,  &saved_reg41)) &&
+		   (!dev_wlc_intvar_get_reg(dev, "btc_params", 68,  &saved_reg68))) {
+			WL_TRACE_COEX(("save regs {66,41,68} ->: 0x%x 0x%x 0x%x\n", \
+				saved_reg66, saved_reg41, saved_reg68));
+
+#ifndef CUSTOMER_HW2
+			dev_wlc_ioctl(dev, WLC_SET_PM, &pm_local, sizeof(pm_local));
+#endif
+
+				if (btcoex_is_sco_active(dev)) {
+
+					dev_wlc_bufvar_set(dev, "btc_params", \
+						(char *)&buf_reg66va_dhcp_on[0], \
+						 sizeof(buf_reg66va_dhcp_on));
+
+					dev_wlc_bufvar_set(dev, "btc_params", \
+						(char *)&buf_reg41va_dhcp_on[0], \
+						 sizeof(buf_reg41va_dhcp_on));
+
+					dev_wlc_bufvar_set(dev, "btc_params", \
+						(char *)&buf_reg68va_dhcp_on[0], \
+						 sizeof(buf_reg68va_dhcp_on));
+					saved_status = TRUE;
+
+					g_bt->bt_state = BT_DHCP_START;
+					g_bt->timer_on = 1;
+					mod_timer(&g_bt->timer, g_bt->timer.expires);
+					WL_TRACE_COEX(("%s enable BT DHCP Timer\n", \
+					__FUNCTION__));
+			}
+		}
+		else if (saved_status == TRUE) {
+			WL_ERROR(("%s was called w/o DHCP OFF. Continue\n", __FUNCTION__));
+		}
+	}
+#ifdef CUSTOMER_HW2
+	else if (strnicmp((char *)&powermode_val, "2", strlen("2")) == 0) {
+#else
+	else if (strnicmp((char *)&powermode_val, "0", strlen("0")) == 0) {
+#endif
+
+#ifndef CUSTOMER_HW2
+		dev_wlc_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm));
+#endif
+
+		WL_TRACE_COEX(("%s disable BT DHCP Timer\n", __FUNCTION__));
+		if (g_bt->timer_on) {
+			g_bt->timer_on = 0;
+			del_timer_sync(&g_bt->timer);
+
+			if (g_bt->bt_state != BT_DHCP_IDLE) {
+				WL_TRACE_COEX(("%s bt->bt_state:%d\n",
+					__FUNCTION__, g_bt->bt_state));
+
+				up(&g_bt->bt_sem);
+			}
+		}
+
+		if (saved_status == TRUE) {
+			dev_wlc_bufvar_set(dev, "btc_flags", \
+				(char *)&buf_flag7_default[0], sizeof(buf_flag7_default));
+
+			regaddr = 66;
+			dev_wlc_intvar_set_reg(dev, "btc_params", \
+				(char *)&regaddr, (char *)&saved_reg66);
+			regaddr = 41;
+			dev_wlc_intvar_set_reg(dev, "btc_params", \
+				(char *)&regaddr, (char *)&saved_reg41);
+			regaddr = 68;
+			dev_wlc_intvar_set_reg(dev, "btc_params", \
+				(char *)&regaddr, (char *)&saved_reg68);
+
+			WL_TRACE_COEX(("restore regs {66,41,68} <- 0x%x 0x%x 0x%x\n", \
+					saved_reg66, saved_reg41, saved_reg68));
+		}
+		saved_status = FALSE;
+	}
+	else {
+		WL_ERROR(("%s Unkwown yet power setting, ignored\n",
+			__FUNCTION__));
+	}
+
+	p += snprintf(p, MAX_WX_STRING, "OK");
+
+	wrqu->data.length = p - extra + 1;
+
+	return error;
+}
+
+static int
+wl_iw_set_suspend(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	union iwreq_data *wrqu,
+	char *extra
+)
+{
+	int suspend_flag;
+	int ret_now;
+	int ret = 0;
+
+	suspend_flag = *(extra + strlen(SETSUSPEND_CMD) + 1) - '0';
+
+	if (suspend_flag != 0)
+		suspend_flag = 1;
+
+	ret_now = net_os_set_suspend_disable(dev, suspend_flag);
+
+	if (ret_now != suspend_flag) {
+		if (!(ret = net_os_set_suspend(dev, ret_now)))
+			WL_ERROR(("%s: Suspend Flag %d -> %d\n", \
+					__FUNCTION__, ret_now, suspend_flag));
+		else
+			WL_ERROR(("%s: failed %d\n", __FUNCTION__, ret));
+	}
+
+	return ret;
+}
+
+
+int
+wl_format_ssid(char* ssid_buf, uint8* ssid, int ssid_len)
+{
+	int i, c;
+	char *p = ssid_buf;
+
+	if (ssid_len > 32) ssid_len = 32;
+
+	for (i = 0; i < ssid_len; i++) {
+		c = (int)ssid[i];
+		if (c == '\\') {
+			*p++ = '\\';
+			*p++ = '\\';
+		} else if (isprint((uchar)c)) {
+			*p++ = (char)c;
+		} else {
+			p += sprintf(p, "\\x%02X", c);
+		}
+	}
+	*p = '\0';
+
+	return p - ssid_buf;
+}
+
+static int
+wl_iw_get_link_speed(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	union iwreq_data *wrqu,
+	char *extra
+)
+{
+	int error = 0;
+	char *p = extra;
+	static int link_speed;
+
+	net_os_wake_lock(dev);
+	if (g_onoff == G_WLAN_SET_ON) {
+		error = dev_wlc_ioctl(dev, WLC_GET_RATE, &link_speed, sizeof(link_speed));
+		link_speed *= 500000;
+	}
+
+	p += snprintf(p, MAX_WX_STRING, "LinkSpeed %d", link_speed/1000000);
+
+	wrqu->data.length = p - extra + 1;
+
+	net_os_wake_unlock(dev);
+	return error;
+}
+
+
+static int
+wl_iw_get_dtim_skip(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	union iwreq_data *wrqu,
+	char *extra
+)
+{
+	int error = -1;
+	char *p = extra;
+	char iovbuf[32];
+
+	net_os_wake_lock(dev);
+	if (g_onoff == G_WLAN_SET_ON) {
+
+			memset(iovbuf, 0, sizeof(iovbuf));
+			strcpy(iovbuf, "bcn_li_dtim");
+
+			if ((error = dev_wlc_ioctl(dev, WLC_GET_VAR,
+				&iovbuf, sizeof(iovbuf))) >= 0) {
+
+				p += snprintf(p, MAX_WX_STRING, "Dtim_skip %d", iovbuf[0]);
+				WL_TRACE(("%s: get dtim_skip = %d\n", __FUNCTION__, iovbuf[0]));
+				wrqu->data.length = p - extra + 1;
+			}
+			else
+				WL_ERROR(("%s: get dtim_skip failed code %d\n", \
+					__FUNCTION__, error));
+	}
+	net_os_wake_unlock(dev);
+	return error;
+}
+
+
+static int
+wl_iw_set_dtim_skip(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	union iwreq_data *wrqu,
+	char *extra
+)
+{
+	int error = -1;
+	char *p = extra;
+	int bcn_li_dtim;
+	char iovbuf[32];
+
+	net_os_wake_lock(dev);
+	if (g_onoff == G_WLAN_SET_ON) {
+
+		bcn_li_dtim = htod32((uint)*(extra + strlen(DTIM_SKIP_SET_CMD) + 1) - '0');
+
+		if ((bcn_li_dtim >= 0) || ((bcn_li_dtim <= 5))) {
+
+			memset(iovbuf, 0, sizeof(iovbuf));
+			bcm_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim,
+				4, iovbuf, sizeof(iovbuf));
+
+			if ((error = dev_wlc_ioctl(dev, WLC_SET_VAR,
+				&iovbuf, sizeof(iovbuf))) >= 0) {
+				p += snprintf(p, MAX_WX_STRING, "OK");
+
+				net_os_set_dtim_skip(dev, bcn_li_dtim);
+
+				WL_TRACE(("%s: set dtim_skip %d OK\n", __FUNCTION__, \
+					bcn_li_dtim));
+				goto exit;
+			}
+			else  WL_ERROR(("%s: set dtim_skip %d failed code %d\n", \
+				__FUNCTION__, bcn_li_dtim, error));
+		}
+		else  WL_ERROR(("%s Incorrect dtim_skip setting %d, ignored\n", \
+			__FUNCTION__, bcn_li_dtim));
+	}
+
+	p += snprintf(p, MAX_WX_STRING, "FAIL");
+
+exit:
+	wrqu->data.length = p - extra + 1;
+	net_os_wake_unlock(dev);
+	return error;
+}
+
+
+static int
+wl_iw_get_band(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	union iwreq_data *wrqu,
+	char *extra
+)
+{
+	int error = -1;
+	char *p = extra;
+	static int band;
+
+	net_os_wake_lock(dev);
+
+	if (g_onoff == G_WLAN_SET_ON) {
+		error = dev_wlc_ioctl(dev, WLC_GET_BAND, &band, sizeof(band));
+
+		p += snprintf(p, MAX_WX_STRING, "Band %d", band);
+
+		wrqu->data.length = p - extra + 1;
+	}
+
+	net_os_wake_unlock(dev);
+	return error;
+}
+
+
+static int
+wl_iw_set_band(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	union iwreq_data *wrqu,
+	char *extra
+)
+{
+	int error = -1;
+	char *p = extra;
+	uint band;
+
+	net_os_wake_lock(dev);
+
+	if (g_onoff == G_WLAN_SET_ON) {
+
+		band = htod32((uint)*(extra + strlen(BAND_SET_CMD) + 1) - '0');
+
+		if ((band == WLC_BAND_AUTO) || (band == WLC_BAND_5G) || (band == WLC_BAND_2G)) {
+
+			if ((error = dev_wlc_ioctl(dev, WLC_SET_BAND,
+				&band, sizeof(band))) >= 0) {
+				p += snprintf(p, MAX_WX_STRING, "OK");
+				WL_TRACE(("%s: set band %d OK\n", __FUNCTION__, band));
+				goto exit;
+			}
+			else WL_ERROR(("%s: set band %d failed code %d\n", __FUNCTION__, \
+					band, error));
+		}
+		else WL_ERROR(("%s Incorrect band setting %d, ignored\n", __FUNCTION__, band));
+	}
+
+	p += snprintf(p, MAX_WX_STRING, "FAIL");
+
+exit:
+	wrqu->data.length = p - extra + 1;
+	net_os_wake_unlock(dev);
+	return error;
+}
+
+#ifdef PNO_SUPPORT
+
+static int
+wl_iw_set_pno_reset(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	union iwreq_data *wrqu,
+	char *extra
+)
+{
+	int error = -1;
+	char *p = extra;
+
+	net_os_wake_lock(dev);
+	if ((g_onoff == G_WLAN_SET_ON) && (dev != NULL)) {
+
+		if ((error = dhd_dev_pno_reset(dev)) >= 0) {
+				p += snprintf(p, MAX_WX_STRING, "OK");
+				WL_TRACE(("%s: set OK\n", __FUNCTION__));
+				goto exit;
+		}
+		else  WL_ERROR(("%s: failed code %d\n", __FUNCTION__, error));
+	}
+
+	p += snprintf(p, MAX_WX_STRING, "FAIL");
+
+exit:
+	wrqu->data.length = p - extra + 1;
+	net_os_wake_unlock(dev);
+	return error;
+}
+
+
+
+static int
+wl_iw_set_pno_enable(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	union iwreq_data *wrqu,
+	char *extra
+)
+{
+	int error = -1;
+	char *p = extra;
+	int pfn_enabled;
+
+	net_os_wake_lock(dev);
+	pfn_enabled = htod32((uint)*(extra + strlen(PNOENABLE_SET_CMD) + 1) - '0');
+
+	if ((g_onoff == G_WLAN_SET_ON) && (dev != NULL)) {
+
+		if ((error = dhd_dev_pno_enable(dev, pfn_enabled)) >= 0) {
+				p += snprintf(p, MAX_WX_STRING, "OK");
+				WL_TRACE(("%s: set OK\n", __FUNCTION__));
+				goto exit;
+		}
+		else  WL_ERROR(("%s: failed code %d\n", __FUNCTION__, error));
+	}
+
+	p += snprintf(p, MAX_WX_STRING, "FAIL");
+
+exit:
+	wrqu->data.length = p - extra + 1;
+	net_os_wake_unlock(dev);
+	return error;
+}
+
+
+
+static int
+wl_iw_set_pno_set(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	union iwreq_data *wrqu,
+	char *extra
+)
+{
+	int res = -1;
+	wlc_ssid_t ssids_local[MAX_PFN_LIST_COUNT];
+	int nssid = 0;
+	cmd_tlv_t *cmd_tlv_temp;
+	char *str_ptr;
+	int tlv_size_left;
+	int pno_time;
+	int pno_repeat;
+	int pno_freq_expo_max;
+
+#ifdef PNO_SET_DEBUG
+	int i;
+	char pno_in_example[] = {'P', 'N', 'O', 'S', 'E', 'T', 'U', 'P', ' ', \
+							'S', '1', '2', '0',
+							'S',
+							0x04,
+							'B', 'R', 'C', 'M',
+							'S',
+							0x04,
+							'G', 'O', 'O', 'G',
+							'T',
+							'1','E',
+							'R',
+							'2',
+							'M',
+							'2',
+							0x00
+							};
+#endif
+
+	net_os_wake_lock(dev);
+	WL_ERROR(("\n### %s: info->cmd:%x, info->flags:%x, u.data=0x%p, u.len=%d\n",
+		__FUNCTION__, info->cmd, info->flags,
+		wrqu->data.pointer, wrqu->data.length));
+
+	if (g_onoff == G_WLAN_SET_OFF) {
+		WL_TRACE(("%s: driver is not up yet after START\n", __FUNCTION__));
+		goto exit_proc;
+	}
+
+	if (wrqu->data.length < (strlen(PNOSETUP_SET_CMD) + sizeof(cmd_tlv_t))) {
+		WL_ERROR(("%s aggument=%d  less %d\n", __FUNCTION__, \
+			wrqu->data.length, strlen(PNOSETUP_SET_CMD) + sizeof(cmd_tlv_t)));
+		goto exit_proc;
+	}
+
+#ifdef PNO_SET_DEBUG
+	if (!(extra = kmalloc(sizeof(pno_in_example) +100, GFP_KERNEL))) {
+		res = -ENOMEM;
+		goto exit_proc;
+	}
+	memcpy(extra, pno_in_example, sizeof(pno_in_example));
+	wrqu->data.length = sizeof(pno_in_example);
+	for (i = 0; i < wrqu->data.length; i++)
+		printf("%02X ", extra[i]);
+	printf("\n");
+#endif
+
+	str_ptr = extra;
+#ifdef PNO_SET_DEBUG
+	str_ptr +=  strlen("PNOSETUP ");
+	tlv_size_left = wrqu->data.length - strlen("PNOSETUP ");
+#else
+	str_ptr +=  strlen(PNOSETUP_SET_CMD);
+	tlv_size_left = wrqu->data.length - strlen(PNOSETUP_SET_CMD);
+#endif
+
+	cmd_tlv_temp = (cmd_tlv_t *)str_ptr;
+	memset(ssids_local, 0, sizeof(ssids_local));
+	pno_repeat = pno_freq_expo_max = 0;
+
+	if ((cmd_tlv_temp->prefix == PNO_TLV_PREFIX) && \
+		(cmd_tlv_temp->version == PNO_TLV_VERSION) && \
+		(cmd_tlv_temp->subver == PNO_TLV_SUBVERSION))
+	{
+		str_ptr += sizeof(cmd_tlv_t);
+		tlv_size_left  -= sizeof(cmd_tlv_t);
+
+		if ((nssid = wl_iw_parse_ssid_list_tlv(&str_ptr, ssids_local, \
+				MAX_PFN_LIST_COUNT, &tlv_size_left)) <= 0) {
+			WL_ERROR(("SSID is not presented or corrupted ret=%d\n", nssid));
+			goto exit_proc;
+		}
+		else {
+			if ((str_ptr[0] != PNO_TLV_TYPE_TIME) || (tlv_size_left <= 1)) {
+				WL_ERROR(("%s scan duration corrupted field size %d\n", \
+						__FUNCTION__, tlv_size_left));
+				goto exit_proc;
+			}
+			str_ptr++;
+			pno_time = simple_strtoul(str_ptr, &str_ptr, 16);
+			WL_PNO(("%s: pno_time=%d\n", __FUNCTION__, pno_time));
+
+			if (str_ptr[0] != 0) {
+				if ((str_ptr[0] != PNO_TLV_FREQ_REPEAT)) {
+					WL_ERROR(("%s pno repeat : corrupted field\n", \
+						__FUNCTION__));
+					goto exit_proc;
+				}
+				str_ptr++;
+				pno_repeat = simple_strtoul(str_ptr, &str_ptr, 16);
+				WL_PNO(("%s :got pno_repeat=%d\n", __FUNCTION__, pno_repeat));
+				if (str_ptr[0] != PNO_TLV_FREQ_EXPO_MAX) {
+					WL_ERROR(("%s FREQ_EXPO_MAX corrupted field size\n", \
+							__FUNCTION__));
+					goto exit_proc;
+				}
+				str_ptr++;
+				pno_freq_expo_max = simple_strtoul(str_ptr, &str_ptr, 16);
+				WL_PNO(("%s: pno_freq_expo_max=%d\n", \
+							__FUNCTION__, pno_freq_expo_max));
+			}
+		}
+	}
+	else {
+		WL_ERROR(("%s get wrong TLV command\n", __FUNCTION__));
+		goto exit_proc;
+	}
+
+	res = dhd_dev_pno_set(dev, ssids_local, nssid, pno_time, pno_repeat, pno_freq_expo_max);
+
+exit_proc:
+	net_os_wake_unlock(dev);
+	return res;
+}
+#endif
+
+static int
+wl_iw_get_rssi(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	union iwreq_data *wrqu,
+	char *extra
+)
+{
+	static int rssi = 0;
+	static wlc_ssid_t ssid = {0};
+	int error = 0;
+	char *p = extra;
+	static char ssidbuf[SSID_FMT_BUF_LEN];
+	scb_val_t scb_val;
+
+	net_os_wake_lock(dev);
+
+	bzero(&scb_val, sizeof(scb_val_t));
+
+	if (g_onoff == G_WLAN_SET_ON) {
+		error = dev_wlc_ioctl(dev, WLC_GET_RSSI, &scb_val, sizeof(scb_val_t));
+		if (error) {
+			WL_ERROR(("%s: Fails %d\n", __FUNCTION__, error));
+		} else {
+			rssi = dtoh32(scb_val.val);
+
+			error = dev_wlc_ioctl(dev, WLC_GET_SSID, &ssid, sizeof(ssid));
+			if (!error) {
+				ssid.SSID_len = dtoh32(ssid.SSID_len);
+				wl_format_ssid(ssidbuf, ssid.SSID, dtoh32(ssid.SSID_len));
+			}
+		}
+	}
+
+	WL_ASSOC(("%s ssid_len:%d, rssi:%d\n", __FUNCTION__, ssid.SSID_len, rssi));
+
+	if (error || (ssid.SSID_len == 0)) {
+		p += snprintf(p, MAX_WX_STRING, "FAIL");
+	} else {
+		p += snprintf(p, MAX_WX_STRING, "%s rssi %d ", ssidbuf, rssi);
+	}
+	wrqu->data.length = p - extra + 1;
+
+	net_os_wake_unlock(dev);
+	return error;
+}
+
+int
+wl_iw_send_priv_event(
+	struct net_device *dev,
+	char *flag
+)
+{
+	union iwreq_data wrqu;
+	char extra[IW_CUSTOM_MAX + 1];
+	int cmd;
+
+	cmd = IWEVCUSTOM;
+	memset(&wrqu, 0, sizeof(wrqu));
+	if (strlen(flag) > sizeof(extra))
+		return -1;
+
+	strcpy(extra, flag);
+	wrqu.data.length = strlen(extra);
+	wireless_send_event(dev, cmd, &wrqu, extra);
+	net_os_wake_lock_timeout_enable(dev);
+	WL_TRACE(("Send IWEVCUSTOM Event as %s\n", extra));
+
+	return 0;
+}
+
+
+int
+wl_control_wl_start(struct net_device *dev)
+{
+	int ret = 0;
+	wl_iw_t *iw;
+
+	WL_TRACE(("Enter %s \n", __FUNCTION__));
+
+	if (!dev) {
+		WL_ERROR(("%s: dev is null\n", __FUNCTION__));
+		return -1;
+	}
+
+	iw = *(wl_iw_t **)netdev_priv(dev);
+
+	if (!iw) {
+		WL_ERROR(("%s: wl is null\n", __FUNCTION__));
+		return -1;
+	}
+	dhd_os_start_lock(iw->pub);
+
+	if (g_onoff == G_WLAN_SET_OFF) {
+		dhd_customer_gpio_wlan_ctrl(WLAN_RESET_ON);
+
+#if defined(BCMLXSDMMC)
+		sdioh_start(NULL, 0);
+#endif
+
+		ret = dhd_dev_reset(dev, 0);
+
+		if (ret == BCME_OK) {
+#if defined(BCMLXSDMMC)
+			sdioh_start(NULL, 1);
+#endif
+			dhd_dev_init_ioctl(dev);
+			g_onoff = G_WLAN_SET_ON;
+		}
+	}
+	WL_TRACE(("Exited %s \n", __FUNCTION__));
+
+	dhd_os_start_unlock(iw->pub);
+	return ret;
+}
+
+
+static int
+wl_iw_control_wl_off(
+	struct net_device *dev,
+	struct iw_request_info *info
+)
+{
+	int ret = 0;
+	wl_iw_t *iw;
+
+	WL_TRACE(("Enter %s\n", __FUNCTION__));
+
+	if (!dev) {
+		WL_ERROR(("%s: dev is null\n", __FUNCTION__));
+		return -1;
+	}
+
+	iw = *(wl_iw_t **)netdev_priv(dev);
+	if (!iw) {
+		WL_ERROR(("%s: dev is null\n", __FUNCTION__));
+		return -1;
+	}
+	dhd_os_start_lock(iw->pub);
+
+#ifdef SOFTAP
+	ap_cfg_running = FALSE;
+#endif
+
+	if (g_onoff == G_WLAN_SET_ON) {
+		g_onoff = G_WLAN_SET_OFF;
+#if defined(WL_IW_USE_ISCAN)
+		g_iscan->iscan_state = ISCAN_STATE_IDLE;
+#endif
+
+		dhd_dev_reset(dev, 1);
+
+#if defined(WL_IW_USE_ISCAN)
+#if !defined(CSCAN)
+		wl_iw_free_ss_cache();
+		wl_iw_run_ss_cache_timer(0);
+
+		g_ss_cache_ctrl.m_link_down = 1;
+#endif
+		memset(g_scan, 0, G_SCAN_RESULTS);
+		g_scan_specified_ssid = 0;
+#if defined(CONFIG_FIRST_SCAN)
+		g_first_broadcast_scan = BROADCAST_SCAN_FIRST_IDLE;
+		g_first_counter_scans = 0;
+#endif
+#endif
+
+#if defined(BCMLXSDMMC)
+		sdioh_stop(NULL);
+#endif
+
+		net_os_set_dtim_skip(dev, 0);
+
+		dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF);
+
+		wl_iw_send_priv_event(dev, "STOP");
+	}
+
+	dhd_os_start_unlock(iw->pub);
+
+	WL_TRACE(("Exited %s\n", __FUNCTION__));
+
+	return ret;
+}
+
+static int
+wl_iw_control_wl_on(
+	struct net_device *dev,
+	struct iw_request_info *info
+)
+{
+	int ret = 0;
+
+	WL_TRACE(("Enter %s \n", __FUNCTION__));
+
+	if ((ret = wl_control_wl_start(dev)) != BCME_OK) {
+		WL_ERROR(("%s failed first attemp\n", __FUNCTION__));
+		dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF);
+		if ((ret = wl_control_wl_start(dev)) != BCME_OK) {
+			WL_ERROR(("%s failed second attemp\n", __FUNCTION__));
+			net_os_send_hang_message(dev);
+			return ret;
+		}
+	}
+
+	wl_iw_send_priv_event(dev, "START");
+
+#ifdef SOFTAP
+	if (!ap_fw_loaded) {
+		wl_iw_iscan_set_scan_broadcast_prep(dev, 0);
+	}
+#else
+	wl_iw_iscan_set_scan_broadcast_prep(dev, 0);
+#endif
+
+	WL_TRACE(("Exited %s \n", __FUNCTION__));
+
+	return ret;
+}
+
+#ifdef SOFTAP
+static struct ap_profile my_ap;
+static int set_ap_cfg(struct net_device *dev, struct ap_profile *ap);
+static int get_assoc_sta_list(struct net_device *dev, char *buf, int len);
+static int set_ap_mac_list(struct net_device *dev, void *buf);
+
+#define PTYPE_STRING	0
+#define PTYPE_INTDEC	1
+#define PTYPE_INTHEX	2
+#define PTYPE_STR_HEX	3
+int get_parmeter_from_string(
+	char **str_ptr, const char *token, int param_type, void  *dst, int param_max_len);
+
+#endif
+
+int hex2num(char c)
+{
+	if (c >= '0' && c <= '9')
+		return c - '0';
+	if (c >= 'a' && c <= 'f')
+		return c - 'a' + 10;
+	if (c >= 'A' && c <= 'F')
+		return c - 'A' + 10;
+	return -1;
+}
+
+int hex2byte(const char *hex)
+{
+	int a, b;
+	a = hex2num(*hex++);
+	if (a < 0)
+		return -1;
+	b = hex2num(*hex++);
+	if (b < 0)
+		return -1;
+	return (a << 4) | b;
+}
+
+
+
+int hstr_2_buf(const char *txt, u8 *buf, int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++) {
+		int a, b;
+
+		a = hex2num(*txt++);
+		if (a < 0)
+			return -1;
+		b = hex2num(*txt++);
+		if (b < 0)
+			return -1;
+		*buf++ = (a << 4) | b;
+	}
+
+	return 0;
+}
+
+#if defined(SOFTAP) && defined(SOFTAP_TLV_CFG)
+
+static int wl_iw_softap_cfg_tlv(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	union iwreq_data *wrqu,
+	char *extra
+)
+{
+	int res = -1;
+	char *str_ptr;
+	int tlv_size_left;
+
+
+#define SOFTAP_TLV_DEBUG  1
+#ifdef SOFTAP_TLV_DEBUG
+char softap_cmd_example[] = {
+
+	'S', 'O', 'F', 'T', 'A', 'P', 'S', 'E', 'T', ' ',
+
+	SOFTAP_TLV_PREFIX, SOFTAP_TLV_VERSION,
+	SOFTAP_TLV_SUBVERSION, SOFTAP_TLV_RESERVED,
+
+	TLV_TYPE_SSID,		9, 'B', 'R', 'C', 'M', ',', 'G', 'O', 'O', 'G',
+
+	TLV_TYPE_SECUR,		4, 'O', 'P', 'E', 'N',
+
+	TLV_TYPE_KEY,		4, 0x31, 0x32, 0x33, 0x34,
+
+	TLV_TYPE_CHANNEL,	4, 0x06, 0x00, 0x00, 0x00
+};
+#endif
+
+
+#ifdef SOFTAP_TLV_DEBUG
+	{
+	int i;
+	if (!(extra = kmalloc(sizeof(softap_cmd_example) +10, GFP_KERNEL)))
+		return -ENOMEM;
+	memcpy(extra, softap_cmd_example, sizeof(softap_cmd_example));
+	wrqu->data.length = sizeof(softap_cmd_example);
+	print_buf(extra, wrqu->data.length, 16);
+	for (i = 0; i < wrqu->data.length; i++)
+		printf("%c ", extra[i]);
+	printf("\n");
+	}
+#endif
+
+	WL_ERROR(("\n### %s: info->cmd:%x, info->flags:%x, u.data=0x%p, u.len=%d\n",
+		__FUNCTION__, info->cmd, info->flags,
+		wrqu->data.pointer, wrqu->data.length));
+
+	if (g_onoff == G_WLAN_SET_OFF) {
+		WL_TRACE(("%s: driver is not up yet after START\n", __FUNCTION__));
+		return -1;
+	}
+
+	if (wrqu->data.length < (strlen(SOFTAP_SET_CMD) + sizeof(cmd_tlv_t))) {
+		WL_ERROR(("%s argument=%d  less %d\n", __FUNCTION__,
+			wrqu->data.length, strlen(SOFTAP_SET_CMD) + sizeof(cmd_tlv_t)));
+		return -1;
+	}
+
+	str_ptr =  extra + strlen(SOFTAP_SET_CMD)+1; 
+	tlv_size_left = wrqu->data.length - (strlen(SOFTAP_SET_CMD)+1);
+
+	memset(&my_ap, 0, sizeof(my_ap));
+
+	return res;
+}
+#endif
+
+
+#ifdef SOFTAP
+int init_ap_profile_from_string(char *param_str, struct ap_profile *ap_cfg)
+{
+	char *str_ptr = param_str;
+	char sub_cmd[16];
+	int ret = 0;
+
+	memset(sub_cmd, 0, sizeof(sub_cmd));
+	memset(ap_cfg, 0, sizeof(struct ap_profile));
+
+	if (get_parmeter_from_string(&str_ptr, "ASCII_CMD=",
+		PTYPE_STRING, sub_cmd, SSID_LEN) != 0) {
+		return -1;
+	}
+	if (strncmp(sub_cmd, "AP_CFG", 6)) {
+		WL_ERROR(("ERROR: sub_cmd:%s != 'AP_CFG'!\n", sub_cmd));
+		return -1;
+	}
+
+	ret = get_parmeter_from_string(&str_ptr, "SSID=", PTYPE_STRING, ap_cfg->ssid, SSID_LEN);
+
+	ret |= get_parmeter_from_string(&str_ptr, "SEC=", PTYPE_STRING,  ap_cfg->sec, SEC_LEN);
+
+	ret |= get_parmeter_from_string(&str_ptr, "KEY=", PTYPE_STRING,  ap_cfg->key, KEY_LEN);
+
+	ret |= get_parmeter_from_string(&str_ptr, "CHANNEL=", PTYPE_INTDEC, &ap_cfg->channel, 5);
+
+	get_parmeter_from_string(&str_ptr, "PREAMBLE=", PTYPE_INTDEC, &ap_cfg->preamble, 5);
+
+	get_parmeter_from_string(&str_ptr, "MAX_SCB=", PTYPE_INTDEC,  &ap_cfg->max_scb, 5);
+
+	get_parmeter_from_string(&str_ptr, "HIDDEN=", PTYPE_INTDEC, &ap_cfg->closednet, 5);
+
+	get_parmeter_from_string(&str_ptr, "COUNTRY=", PTYPE_STRING, &ap_cfg->country_code, 3);
+
+	return ret;
+}
+#endif
+
+
+#ifdef SOFTAP
+static int iwpriv_set_ap_config(struct net_device *dev,
+		struct iw_request_info *info,
+		union iwreq_data *wrqu,
+		char *ext)
+{
+	int res = 0;
+	char  *extra = NULL;
+	struct ap_profile *ap_cfg = &my_ap;
+
+	WL_TRACE(("%s: info->cmd:%x, info->flags:%x, u.data:%p, u.len:%d\n",
+		__FUNCTION__,
+		info->cmd, info->flags,
+		wrqu->data.pointer, wrqu->data.length));
+
+	if (wrqu->data.length != 0) {
+
+		char *str_ptr;
+
+		if (!(extra = kmalloc(wrqu->data.length+1, GFP_KERNEL)))
+			return -ENOMEM;
+
+		if (copy_from_user(extra, wrqu->data.pointer, wrqu->data.length)) {
+			kfree(extra);
+			return -EFAULT;
+		}
+
+		extra[wrqu->data.length] = 0;
+		WL_SOFTAP((" Got str param in iw_point:\n %s\n", extra));
+
+		memset(ap_cfg, 0, sizeof(struct ap_profile));
+
+		str_ptr = extra;
+
+		if ((res = init_ap_profile_from_string(extra, ap_cfg)) < 0) {
+			WL_ERROR(("%s failed to parse %d\n", __FUNCTION__, res));
+			kfree(extra);
+			return -1;
+		}
+
+	} else {
+		WL_ERROR(("IWPRIV argument len = 0 \n"));
+		return -1;
+	}
+
+	if ((res = set_ap_cfg(dev, ap_cfg)) < 0)
+		WL_ERROR(("%s failed to set_ap_cfg %d\n", __FUNCTION__, res));
+
+	kfree(extra);
+
+	return res;
+}
+#endif
+
+
+#ifdef SOFTAP
+static int iwpriv_get_assoc_list(struct net_device *dev,
+		struct iw_request_info *info,
+		union iwreq_data *p_iwrq,
+		char *extra)
+{
+	int i, ret = 0;
+	char mac_buf[256];
+	struct maclist *sta_maclist = (struct maclist *)mac_buf;
+
+	char mac_lst[384];
+	char *p_mac_str;
+	char *p_mac_str_end;
+
+	if ((!dev) || (!extra)) {
+		return -EINVAL;
+	}
+
+	net_os_wake_lock(dev);
+
+	WL_TRACE(("\n %s: IWPRIV IOCTL: cmd:%hx, flags:%hx, extra:%p, iwp.len:%d, \
+		iwp.len:%p, iwp.flags:%x  \n", __FUNCTION__, info->cmd, info->flags, \
+		extra, p_iwrq->data.length, p_iwrq->data.pointer, p_iwrq->data.flags));
+
+	memset(sta_maclist, 0, sizeof(mac_buf));
+
+	sta_maclist->count = 8;
+
+	WL_SOFTAP(("%s: net device:%s, buf_sz:%d\n",
+		__FUNCTION__, dev->name, sizeof(mac_buf)));
+
+	if ((ret = get_assoc_sta_list(dev, mac_buf, sizeof(mac_buf))) < 0) {
+		WL_ERROR(("%s: sta list ioctl error:%d\n",
+			__FUNCTION__, ret));
+		goto func_exit;
+	}
+
+	WL_SOFTAP(("%s: got %d stations\n", __FUNCTION__,
+		sta_maclist->count));
+
+	memset(mac_lst, 0, sizeof(mac_lst));
+	p_mac_str = mac_lst;
+	p_mac_str_end = &mac_lst[sizeof(mac_lst)-1];
+
+	for (i = 0; i < 8; i++) {
+		struct ether_addr *id = &sta_maclist->ea[i];
+		if (!ETHER_ISNULLADDR(id->octet)) {
+			scb_val_t scb_val;
+			int rssi = 0;
+
+			bzero(&scb_val, sizeof(scb_val_t));
+
+			if ((p_mac_str_end - p_mac_str) <= 36) {
+				WL_ERROR(("%s: mac list buf is < 36 for item[%i] item\n",
+					__FUNCTION__, i));
+				break;
+			}
+
+			p_mac_str += snprintf(p_mac_str, MAX_WX_STRING,
+			"\nMac[%d]=%02X:%02X:%02X:%02X:%02X:%02X,", i,
+			id->octet[0], id->octet[1], id->octet[2],
+			id->octet[3], id->octet[4], id->octet[5]);
+
+			bcopy(id->octet, &scb_val.ea, 6);
+			ret = dev_wlc_ioctl(dev, WLC_GET_RSSI, &scb_val, sizeof(scb_val_t));
+			if (ret  < 0) {
+				snprintf(p_mac_str, MAX_WX_STRING, "RSSI:ERR");
+				WL_ERROR(("%s: RSSI ioctl error:%d\n",
+					__FUNCTION__, ret));
+				break;
+			}
+
+			rssi = dtoh32(scb_val.val);
+			p_mac_str += snprintf(p_mac_str, MAX_WX_STRING,
+			"RSSI:%d", rssi);
+		}
+	}
+
+	p_iwrq->data.length = strlen(mac_lst) + 1;
+
+	WL_SOFTAP(("%s: data to user:\n%s\n usr_ptr:%p\n", __FUNCTION__,
+		mac_lst, p_iwrq->data.pointer));
+
+	if (p_iwrq->data.length) {
+		bcopy(mac_lst, extra, p_iwrq->data.length);
+	}
+
+func_exit:
+	net_os_wake_unlock(dev);
+
+	WL_TRACE(("Exited %s \n", __FUNCTION__));
+	return ret;
+}
+#endif
+
+
+#ifdef SOFTAP
+#define MAC_FILT_MAX 8
+static int iwpriv_set_mac_filters(struct net_device *dev,
+		struct iw_request_info *info,
+		union iwreq_data *wrqu,
+		char *ext)
+{
+	int i, ret = -1;
+	char  * extra = NULL;
+	int mac_cnt = 0;
+	int mac_mode = 0;
+	struct ether_addr *p_ea;
+	struct mac_list_set mflist_set;
+
+	WL_SOFTAP((">>> Got IWPRIV SET_MAC_FILTER IOCTL:  info->cmd:%x, \
+			info->flags:%x, u.data:%p, u.len:%d\n",
+			info->cmd, info->flags,
+			wrqu->data.pointer, wrqu->data.length));
+
+	if (wrqu->data.length != 0) {
+
+		char *str_ptr;
+
+		if (!(extra = kmalloc(wrqu->data.length+1, GFP_KERNEL)))
+			return -ENOMEM;
+
+		if (copy_from_user(extra, wrqu->data.pointer, wrqu->data.length)) {
+			kfree(extra);
+			return -EFAULT;
+		}
+
+		extra[wrqu->data.length] = 0;
+		WL_SOFTAP((" Got parameter string in iw_point:\n %s \n", extra));
+
+		memset(&mflist_set, 0, sizeof(mflist_set));
+
+		str_ptr = extra;
+
+		if (get_parmeter_from_string(&str_ptr, "MAC_MODE=",
+			PTYPE_INTDEC, &mac_mode, 4) != 0) {
+			WL_ERROR(("ERROR: 'MAC_MODE=' token is missing\n"));
+			goto exit_proc;
+		}
+
+		p_ea = &mflist_set.mac_list.ea[0];
+
+		if (get_parmeter_from_string(&str_ptr, "MAC_CNT=",
+			PTYPE_INTDEC, &mac_cnt, 4) != 0) {
+			WL_ERROR(("ERROR: 'MAC_CNT=' token param is missing \n"));
+			goto exit_proc;
+		}
+
+		if (mac_cnt > MAC_FILT_MAX) {
+			WL_ERROR(("ERROR: number of MAC filters > MAX\n"));
+			goto exit_proc;
+		}
+
+		for (i=0; i < mac_cnt; i++)
+			if (get_parmeter_from_string(&str_ptr, "MAC=",
+				PTYPE_STR_HEX, &p_ea[i], 12) != 0) {
+				WL_ERROR(("ERROR: MAC_filter[%d] is missing !\n", i));
+				goto exit_proc;
+			}
+
+		WL_SOFTAP(("MAC_MODE=:%d, MAC_CNT=%d, MACs:..\n", mac_mode, mac_cnt));
+		for (i = 0; i < mac_cnt; i++) {
+		   WL_SOFTAP(("mac_filt[%d]:", i));
+		   print_buf(&p_ea[i], 6, 0);
+		}
+
+		mflist_set.mode = mac_mode;
+		mflist_set.mac_list.count = mac_cnt;
+		set_ap_mac_list(dev, &mflist_set);
+
+		wrqu->data.pointer = NULL;
+		wrqu->data.length = 0;
+		ret = 0;
+
+	} else {
+		WL_ERROR(("IWPRIV argument len is 0\n"));
+		return -1;
+	}
+
+	exit_proc:
+	kfree(extra);
+	return ret;
+}
+#endif
+
+
+#ifdef SOFTAP
+static int iwpriv_set_ap_sta_disassoc(struct net_device *dev,
+        struct iw_request_info *info,
+        union iwreq_data *wrqu,
+        char *ext)
+{
+	int res = 0;
+	char sta_mac[6] = {0, 0, 0, 0, 0, 0};
+	char cmd_buf[256];
+	char *str_ptr = cmd_buf;
+
+	WL_SOFTAP((">>%s called\n args: info->cmd:%x,"
+		" info->flags:%x, u.data.p:%p, u.data.len:%d\n",
+		__FUNCTION__, info->cmd, info->flags,
+		wrqu->data.pointer, wrqu->data.length));
+
+	if (wrqu->data.length != 0) {
+
+		if (copy_from_user(cmd_buf, wrqu->data.pointer, wrqu->data.length)) {
+			return -EFAULT;
+		}
+
+		if (get_parmeter_from_string(&str_ptr,
+			"MAC=", PTYPE_STR_HEX, sta_mac, 12) == 0) {
+			res = wl_iw_softap_deassoc_stations(dev, sta_mac);
+		} else  {
+			WL_ERROR(("ERROR: STA_MAC= token not found\n"));
+		}
+	}
+
+	return res;
+}
+#endif
+
+#endif
+
+
+#if WIRELESS_EXT < 13
+struct iw_request_info
+{
+	__u16		cmd;		
+	__u16		flags;		
+};
+
+typedef int (*iw_handler)(struct net_device *dev,
+		struct iw_request_info *info,
+		void *wrqu,
+		char *extra);
+#endif 
+
+static int
+wl_iw_config_commit(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	void *zwrq,
+	char *extra
+)
+{
+	wlc_ssid_t ssid;
+	int error;
+	struct sockaddr bssid;
+
+	WL_TRACE(("%s: SIOCSIWCOMMIT\n", dev->name));
+
+	if ((error = dev_wlc_ioctl(dev, WLC_GET_SSID, &ssid, sizeof(ssid))))
+		return error;
+
+	ssid.SSID_len = dtoh32(ssid.SSID_len);
+
+	if (!ssid.SSID_len)
+		return 0;
+
+	bzero(&bssid, sizeof(struct sockaddr));
+	if ((error = dev_wlc_ioctl(dev, WLC_REASSOC, &bssid, ETHER_ADDR_LEN))) {
+		WL_ERROR(("%s: WLC_REASSOC to %s failed \n", __FUNCTION__, ssid.SSID));
+		return error;
+	}
+
+	return 0;
+}
+
+static int
+wl_iw_get_name(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	char *cwrq,
+	char *extra
+)
+{
+	WL_TRACE(("%s: SIOCGIWNAME\n", dev->name));
+
+	strcpy(cwrq, "IEEE 802.11-DS");
+
+	return 0;
+}
+
+static int
+wl_iw_set_freq(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_freq *fwrq,
+	char *extra
+)
+{
+	int error, chan;
+	uint sf = 0;
+
+	WL_TRACE(("%s %s: SIOCSIWFREQ\n", __FUNCTION__, dev->name));
+
+#if defined(SOFTAP)
+	if (ap_cfg_running) {
+		WL_TRACE(("%s:>> not executed, 'SOFT_AP is active' \n", __FUNCTION__));
+		return 0;
+	}
+#endif
+
+	
+	if (fwrq->e == 0 && fwrq->m < MAXCHANNEL) {
+		chan = fwrq->m;
+	}
+
+	
+	else {
+		
+		if (fwrq->e >= 6) {
+			fwrq->e -= 6;
+			while (fwrq->e--)
+				fwrq->m *= 10;
+		} else if (fwrq->e < 6) {
+			while (fwrq->e++ < 6)
+				fwrq->m /= 10;
+		}
+	
+	if (fwrq->m > 4000 && fwrq->m < 5000)
+		sf = WF_CHAN_FACTOR_4_G; 
+
+		chan = wf_mhz2channel(fwrq->m, sf);
+	}
+	chan = htod32(chan);
+	if ((error = dev_wlc_ioctl(dev, WLC_SET_CHANNEL, &chan, sizeof(chan))))
+		return error;
+
+	g_wl_iw_params.target_channel = chan;
+	
+	return -EINPROGRESS;
+}
+
+static int
+wl_iw_get_freq(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_freq *fwrq,
+	char *extra
+)
+{
+	channel_info_t ci;
+	int error;
+
+	WL_TRACE(("%s: SIOCGIWFREQ\n", dev->name));
+
+	if ((error = dev_wlc_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(ci))))
+		return error;
+
+	fwrq->m = dtoh32(ci.hw_channel);
+	fwrq->e = dtoh32(0);
+	return 0;
+}
+
+static int
+wl_iw_set_mode(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	__u32 *uwrq,
+	char *extra
+)
+{
+	int infra = 0, ap = 0, error = 0;
+
+	WL_TRACE(("%s: SIOCSIWMODE\n", dev->name));
+
+	switch (*uwrq) {
+	case IW_MODE_MASTER:
+		infra = ap = 1;
+		break;
+	case IW_MODE_ADHOC:
+	case IW_MODE_AUTO:
+		break;
+	case IW_MODE_INFRA:
+		infra = 1;
+		break;
+	default:
+		return -EINVAL;
+	}
+	infra = htod32(infra);
+	ap = htod32(ap);
+
+	if ((error = dev_wlc_ioctl(dev, WLC_SET_INFRA, &infra, sizeof(infra))) ||
+	    (error = dev_wlc_ioctl(dev, WLC_SET_AP, &ap, sizeof(ap))))
+		return error;
+
+	
+	return -EINPROGRESS;
+}
+
+static int
+wl_iw_get_mode(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	__u32 *uwrq,
+	char *extra
+)
+{
+	int error, infra = 0, ap = 0;
+
+	WL_TRACE(("%s: SIOCGIWMODE\n", dev->name));
+
+	if ((error = dev_wlc_ioctl(dev, WLC_GET_INFRA, &infra, sizeof(infra))) ||
+	    (error = dev_wlc_ioctl(dev, WLC_GET_AP, &ap, sizeof(ap))))
+		return error;
+
+	infra = dtoh32(infra);
+	ap = dtoh32(ap);
+	*uwrq = infra ? ap ? IW_MODE_MASTER : IW_MODE_INFRA : IW_MODE_ADHOC;
+
+	return 0;
+}
+
+static int
+wl_iw_get_range(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_point *dwrq,
+	char *extra
+)
+{
+	struct iw_range *range = (struct iw_range *) extra;
+	wl_uint32_list_t *list;
+	wl_rateset_t rateset;
+	int8 *channels;
+	int error, i, k;
+	uint sf, ch;
+
+	int phytype;
+	int bw_cap = 0, sgi_tx = 0, nmode = 0;
+	channel_info_t ci;
+	uint8 nrate_list2copy = 0;
+	uint16 nrate_list[4][8] = { {13, 26, 39, 52, 78, 104, 117, 130},
+		{14, 29, 43, 58, 87, 116, 130, 144},
+		{27, 54, 81, 108, 162, 216, 243, 270},
+		{30, 60, 90, 120, 180, 240, 270, 300}};
+
+	WL_TRACE(("%s: SIOCGIWRANGE\n", dev->name));
+
+	if (!extra)
+		return -EINVAL;
+
+	channels = kmalloc((MAXCHANNEL+1)*4, GFP_KERNEL);
+	if (!channels) {
+		WL_ERROR(("Could not alloc channels\n"));
+		return -ENOMEM;
+	}
+	list = (wl_uint32_list_t *)channels;
+
+	dwrq->length = sizeof(struct iw_range);
+	memset(range, 0, sizeof(range));
+
+	range->min_nwid = range->max_nwid = 0;
+
+	list->count = htod32(MAXCHANNEL);
+	if ((error = dev_wlc_ioctl(dev, WLC_GET_VALID_CHANNELS, channels, (MAXCHANNEL+1)*4))) {
+		kfree(channels);
+		return error;
+	}
+	for (i = 0; i < dtoh32(list->count) && i < IW_MAX_FREQUENCIES; i++) {
+		range->freq[i].i = dtoh32(list->element[i]);
+
+		ch = dtoh32(list->element[i]);
+		if (ch <= CH_MAX_2G_CHANNEL)
+			sf = WF_CHAN_FACTOR_2_4_G;
+		else
+			sf = WF_CHAN_FACTOR_5_G;
+
+		range->freq[i].m = wf_channel2mhz(ch, sf);
+		range->freq[i].e = 6;
+	}
+	range->num_frequency = range->num_channels = i;
+
+	range->max_qual.qual = 5;
+	
+	range->max_qual.level = 0x100 - 200;	
+	
+	range->max_qual.noise = 0x100 - 200;	
+	
+	range->sensitivity = 65535;
+
+#if WIRELESS_EXT > 11
+	
+	range->avg_qual.qual = 3;
+	
+	range->avg_qual.level = 0x100 + WL_IW_RSSI_GOOD;
+	
+	range->avg_qual.noise = 0x100 - 75;	
+#endif 
+
+	if ((error = dev_wlc_ioctl(dev, WLC_GET_CURR_RATESET, &rateset, sizeof(rateset)))) {
+		kfree(channels);
+		return error;
+	}
+	rateset.count = dtoh32(rateset.count);
+	range->num_bitrates = rateset.count;
+	for (i = 0; i < rateset.count && i < IW_MAX_BITRATES; i++)
+		range->bitrate[i] = (rateset.rates[i]& 0x7f) * 500000; 
+	dev_wlc_intvar_get(dev, "nmode", &nmode);
+	dev_wlc_ioctl(dev, WLC_GET_PHYTYPE, &phytype, sizeof(phytype));
+
+	if (nmode == 1 && phytype == WLC_PHY_TYPE_SSN) {
+		dev_wlc_intvar_get(dev, "mimo_bw_cap", &bw_cap);
+		dev_wlc_intvar_get(dev, "sgi_tx", &sgi_tx);
+		dev_wlc_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(channel_info_t));
+		ci.hw_channel = dtoh32(ci.hw_channel);
+
+		if (bw_cap == 0 ||
+			(bw_cap == 2 && ci.hw_channel <= 14)) {
+			if (sgi_tx == 0)
+				nrate_list2copy = 0;
+			else
+				nrate_list2copy = 1;
+		}
+		if (bw_cap == 1 ||
+			(bw_cap == 2 && ci.hw_channel >= 36)) {
+			if (sgi_tx == 0)
+				nrate_list2copy = 2;
+			else
+				nrate_list2copy = 3;
+		}
+		range->num_bitrates += 8;
+		for (k = 0; i < range->num_bitrates; k++, i++) {
+			
+			range->bitrate[i] = (nrate_list[nrate_list2copy][k]) * 500000;
+		}
+	}
+
+	if ((error = dev_wlc_ioctl(dev, WLC_GET_PHYTYPE, &i, sizeof(i)))) {
+		kfree(channels);
+		return error;
+	}
+	i = dtoh32(i);
+	if (i == WLC_PHY_TYPE_A)
+		range->throughput = 24000000;	
+	else
+		range->throughput = 1500000;	
+
+	range->min_rts = 0;
+	range->max_rts = 2347;
+	range->min_frag = 256;
+	range->max_frag = 2346;
+
+	range->max_encoding_tokens = DOT11_MAX_DEFAULT_KEYS;
+	range->num_encoding_sizes = 4;
+	range->encoding_size[0] = WEP1_KEY_SIZE;
+	range->encoding_size[1] = WEP128_KEY_SIZE;
+#if WIRELESS_EXT > 17
+	range->encoding_size[2] = TKIP_KEY_SIZE;
+#else
+	range->encoding_size[2] = 0;
+#endif
+	range->encoding_size[3] = AES_KEY_SIZE;
+
+	range->min_pmp = 0;
+	range->max_pmp = 0;
+	range->min_pmt = 0;
+	range->max_pmt = 0;
+	range->pmp_flags = 0;
+	range->pm_capa = 0;
+
+	range->num_txpower = 2;
+	range->txpower[0] = 1;
+	range->txpower[1] = 255;
+	range->txpower_capa = IW_TXPOW_MWATT;
+
+#if WIRELESS_EXT > 10
+	range->we_version_compiled = WIRELESS_EXT;
+	range->we_version_source = 19;
+
+	range->retry_capa = IW_RETRY_LIMIT;
+	range->retry_flags = IW_RETRY_LIMIT;
+	range->r_time_flags = 0;
+	
+	range->min_retry = 1;
+	range->max_retry = 255;
+	
+	range->min_r_time = 0;
+	range->max_r_time = 0;
+#endif 
+
+#if WIRELESS_EXT > 17
+	range->enc_capa = IW_ENC_CAPA_WPA;
+	range->enc_capa |= IW_ENC_CAPA_CIPHER_TKIP;
+	range->enc_capa |= IW_ENC_CAPA_CIPHER_CCMP;
+#ifdef BCMWPA2
+	range->enc_capa |= IW_ENC_CAPA_WPA2;
+#endif
+
+	IW_EVENT_CAPA_SET_KERNEL(range->event_capa);
+
+	IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP);
+	IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN);
+	IW_EVENT_CAPA_SET(range->event_capa, IWEVTXDROP);
+	IW_EVENT_CAPA_SET(range->event_capa, IWEVMICHAELMICFAILURE);
+#ifdef BCMWPA2
+	IW_EVENT_CAPA_SET(range->event_capa, IWEVPMKIDCAND);
+#endif
+#endif 
+
+	kfree(channels);
+
+	return 0;
+}
+
+static int
+rssi_to_qual(int rssi)
+{
+	if (rssi <= WL_IW_RSSI_NO_SIGNAL)
+		return 0;
+	else if (rssi <= WL_IW_RSSI_VERY_LOW)
+		return 1;
+	else if (rssi <= WL_IW_RSSI_LOW)
+		return 2;
+	else if (rssi <= WL_IW_RSSI_GOOD)
+		return 3;
+	else if (rssi <= WL_IW_RSSI_VERY_GOOD)
+		return 4;
+	else
+		return 5;
+}
+
+static int
+wl_iw_set_spy(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_point *dwrq,
+	char *extra
+)
+{
+	wl_iw_t *iw = *(wl_iw_t **)netdev_priv(dev);
+	struct sockaddr *addr = (struct sockaddr *) extra;
+	int i;
+
+	WL_TRACE(("%s: SIOCSIWSPY\n", dev->name));
+
+	if (!extra)
+		return -EINVAL;
+
+	iw->spy_num = MIN(ARRAYSIZE(iw->spy_addr), dwrq->length);
+	for (i = 0; i < iw->spy_num; i++)
+		memcpy(&iw->spy_addr[i], addr[i].sa_data, ETHER_ADDR_LEN);
+	memset(iw->spy_qual, 0, sizeof(iw->spy_qual));
+
+	return 0;
+}
+
+static int
+wl_iw_get_spy(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_point *dwrq,
+	char *extra
+)
+{
+	wl_iw_t *iw = *(wl_iw_t **)netdev_priv(dev);
+	struct sockaddr *addr = (struct sockaddr *) extra;
+	struct iw_quality *qual = (struct iw_quality *) &addr[iw->spy_num];
+	int i;
+
+	WL_TRACE(("%s: SIOCGIWSPY\n", dev->name));
+
+	if (!extra)
+		return -EINVAL;
+
+	dwrq->length = iw->spy_num;
+	for (i = 0; i < iw->spy_num; i++) {
+		memcpy(addr[i].sa_data, &iw->spy_addr[i], ETHER_ADDR_LEN);
+		addr[i].sa_family = AF_UNIX;
+		memcpy(&qual[i], &iw->spy_qual[i], sizeof(struct iw_quality));
+		iw->spy_qual[i].updated = 0;
+	}
+
+	return 0;
+}
+
+
+static int
+wl_iw_ch_to_chanspec(int ch, wl_join_params_t *join_params, int *join_params_size)
+{
+	chanspec_t chanspec = 0;
+
+	if (ch != 0) {
+
+		join_params->params.chanspec_num = 1;
+		join_params->params.chanspec_list[0] = ch;
+
+		if (join_params->params.chanspec_list[0])
+			chanspec |= WL_CHANSPEC_BAND_2G;
+		else
+			chanspec |= WL_CHANSPEC_BAND_5G;
+
+		chanspec |= WL_CHANSPEC_BW_20;
+		chanspec |= WL_CHANSPEC_CTL_SB_NONE;
+
+		*join_params_size += WL_ASSOC_PARAMS_FIXED_SIZE +
+			join_params->params.chanspec_num * sizeof(chanspec_t);
+
+		join_params->params.chanspec_list[0]  &= WL_CHANSPEC_CHAN_MASK;
+		join_params->params.chanspec_list[0] |= chanspec;
+		join_params->params.chanspec_list[0] =
+		        htodchanspec(join_params->params.chanspec_list[0]);
+
+		join_params->params.chanspec_num = htod32(join_params->params.chanspec_num);
+
+		WL_TRACE(("%s  join_params->params.chanspec_list[0]= %X\n", \
+			__FUNCTION__, join_params->params.chanspec_list[0]));
+	}
+	return 1;
+}
+
+static int
+wl_iw_set_wap(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct sockaddr *awrq,
+	char *extra
+)
+{
+	int error = -EINVAL;
+	wl_join_params_t join_params;
+	int join_params_size;
+
+	WL_TRACE(("%s: SIOCSIWAP\n", dev->name));
+
+	if (awrq->sa_family != ARPHRD_ETHER) {
+		WL_ERROR(("Invalid Header...sa_family\n"));
+		return -EINVAL;
+	}
+
+	
+	if (ETHER_ISBCAST(awrq->sa_data) || ETHER_ISNULLADDR(awrq->sa_data)) {
+		scb_val_t scbval;
+		
+		bzero(&scbval, sizeof(scb_val_t));
+		
+		(void) dev_wlc_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t));
+		return 0;
+	}
+
+
+	
+	memset(&join_params, 0, sizeof(join_params));
+	join_params_size = sizeof(join_params.ssid);
+
+	memcpy(join_params.ssid.SSID, g_ssid.SSID, g_ssid.SSID_len);
+	join_params.ssid.SSID_len = htod32(g_ssid.SSID_len);
+	memcpy(&join_params.params.bssid, awrq->sa_data, ETHER_ADDR_LEN);
+
+	WL_ASSOC(("%s  target_channel=%d\n", __FUNCTION__, g_wl_iw_params.target_channel));
+	wl_iw_ch_to_chanspec(g_wl_iw_params.target_channel, &join_params, &join_params_size);
+
+	if ((error = dev_wlc_ioctl(dev, WLC_SET_SSID, &join_params, join_params_size))) {
+		WL_ERROR(("%s Invalid ioctl data=%d\n", __FUNCTION__, error));
+		return error;
+	}
+
+	if (g_ssid.SSID_len) {
+		WL_ASSOC(("%s: join SSID=%s BSSID="MACSTR" ch=%d\n", __FUNCTION__,  \
+			g_ssid.SSID, MAC2STR((u8 *)awrq->sa_data), \
+			g_wl_iw_params.target_channel));
+	}
+
+	
+	memset(&g_ssid, 0, sizeof(g_ssid));
+	return 0;
+}
+
+static int
+wl_iw_get_wap(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct sockaddr *awrq,
+	char *extra
+)
+{
+	WL_TRACE(("%s: SIOCGIWAP\n", dev->name));
+
+	awrq->sa_family = ARPHRD_ETHER;
+	memset(awrq->sa_data, 0, ETHER_ADDR_LEN);
+
+	
+	(void) dev_wlc_ioctl(dev, WLC_GET_BSSID, awrq->sa_data, ETHER_ADDR_LEN);
+
+	return 0;
+}
+
+#if WIRELESS_EXT > 17
+static int
+wl_iw_mlme(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct sockaddr *awrq,
+	char *extra
+)
+{
+	struct iw_mlme *mlme;
+	scb_val_t scbval;
+	int error  = -EINVAL;
+
+	WL_TRACE(("%s: SIOCSIWMLME DISASSOC/DEAUTH\n", dev->name));
+
+	mlme = (struct iw_mlme *)extra;
+	if (mlme == NULL) {
+		WL_ERROR(("Invalid ioctl data.\n"));
+		return error;
+	}
+
+	scbval.val = mlme->reason_code;
+	bcopy(&mlme->addr.sa_data, &scbval.ea, ETHER_ADDR_LEN);
+
+	if (mlme->cmd == IW_MLME_DISASSOC) {
+		scbval.val = htod32(scbval.val);
+		error = dev_wlc_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t));
+	}
+	else if (mlme->cmd == IW_MLME_DEAUTH) {
+		scbval.val = htod32(scbval.val);
+		error = dev_wlc_ioctl(dev, WLC_SCB_DEAUTHENTICATE_FOR_REASON, &scbval,
+			sizeof(scb_val_t));
+	}
+	else {
+		WL_ERROR(("Invalid ioctl data.\n"));
+		return error;
+	}
+
+	return error;
+}
+#endif 
+
+#ifndef WL_IW_USE_ISCAN
+static int
+wl_iw_get_aplist(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_point *dwrq,
+	char *extra
+)
+{
+	wl_scan_results_t *list;
+	struct sockaddr *addr = (struct sockaddr *) extra;
+	struct iw_quality qual[IW_MAX_AP];
+	wl_bss_info_t *bi = NULL;
+	int error, i;
+	uint buflen = dwrq->length;
+
+	WL_TRACE(("%s: SIOCGIWAPLIST\n", dev->name));
+
+	if (!extra)
+		return -EINVAL;
+
+	list = kmalloc(buflen, GFP_KERNEL);
+	if (!list)
+		return -ENOMEM;
+	memset(list, 0, buflen);
+	list->buflen = htod32(buflen);
+	if ((error = dev_wlc_ioctl(dev, WLC_SCAN_RESULTS, list, buflen))) {
+		WL_ERROR(("%d: Scan results error %d\n", __LINE__, error));
+		kfree(list);
+		return error;
+	}
+	list->buflen = dtoh32(list->buflen);
+	list->version = dtoh32(list->version);
+	list->count = dtoh32(list->count);
+	if (list->version != WL_BSS_INFO_VERSION) {
+		WL_ERROR(("%s: list->version %d != WL_BSS_INFO_VERSION\n", \
+			 __FUNCTION__, list->version));
+		kfree(list);
+		return -EINVAL;
+	}
+
+	for (i = 0, dwrq->length = 0; i < list->count && dwrq->length < IW_MAX_AP; i++) {
+		bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : list->bss_info;
+
+		if ((dtoh32(bi->length) > buflen) ||
+		    (((uintptr)bi + dtoh32(bi->length)) > ((uintptr)list + buflen))) {
+			WL_ERROR(("%s: Scan results out of bounds: %u\n",__FUNCTION__,dtoh32(bi->length)));
+			kfree(list);
+			return -E2BIG;
+		}
+
+		if (!(dtoh16(bi->capability) & DOT11_CAP_ESS))
+			continue;
+
+		memcpy(addr[dwrq->length].sa_data, &bi->BSSID, ETHER_ADDR_LEN);
+		addr[dwrq->length].sa_family = ARPHRD_ETHER;
+		qual[dwrq->length].qual = rssi_to_qual(dtoh16(bi->RSSI));
+		qual[dwrq->length].level = 0x100 + dtoh16(bi->RSSI);
+		qual[dwrq->length].noise = 0x100 + bi->phy_noise;
+
+#if WIRELESS_EXT > 18
+		qual[dwrq->length].updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
+#else
+		qual[dwrq->length].updated = 7;
+#endif 
+
+		dwrq->length++;
+	}
+
+	kfree(list);
+
+	if (dwrq->length) {
+		memcpy(&addr[dwrq->length], qual, sizeof(struct iw_quality) * dwrq->length);
+		
+		dwrq->flags = 1;
+	}
+	return 0;
+}
+#endif
+
+#ifdef WL_IW_USE_ISCAN
+static int
+wl_iw_iscan_get_aplist(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_point *dwrq,
+	char *extra
+)
+{
+	wl_scan_results_t *list;
+	iscan_buf_t * buf;
+	iscan_info_t *iscan = g_iscan;
+
+	struct sockaddr *addr = (struct sockaddr *) extra;
+	struct iw_quality qual[IW_MAX_AP];
+	wl_bss_info_t *bi = NULL;
+	int i;
+
+	WL_TRACE(("%s: SIOCGIWAPLIST\n", dev->name));
+
+	if (!extra)
+		return -EINVAL;
+
+	if ((!iscan) || (iscan->sysioc_pid < 0)) {
+		WL_ERROR(("%s error\n", __FUNCTION__));
+		return 0;
+	}
+
+	buf = iscan->list_hdr;
+	
+	while (buf) {
+		list = &((wl_iscan_results_t*)buf->iscan_buf)->results;
+		if (list->version != WL_BSS_INFO_VERSION) {
+			WL_ERROR(("%s : list->version %d != WL_BSS_INFO_VERSION\n", \
+				__FUNCTION__, list->version));
+			return -EINVAL;
+		}
+
+		bi = NULL;
+		for (i = 0, dwrq->length = 0; i < list->count && dwrq->length < IW_MAX_AP; i++) {
+			bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length))
+			          : list->bss_info;
+
+			if ((dtoh32(bi->length) > WLC_IW_ISCAN_MAXLEN) ||
+			    (((uintptr)bi + dtoh32(bi->length)) > ((uintptr)list + WLC_IW_ISCAN_MAXLEN))) {
+				WL_ERROR(("%s: Scan results out of bounds: %u\n",__FUNCTION__,dtoh32(bi->length)));
+				return -E2BIG;
+			}
+
+			if (!(dtoh16(bi->capability) & DOT11_CAP_ESS))
+				continue;
+
+			memcpy(addr[dwrq->length].sa_data, &bi->BSSID, ETHER_ADDR_LEN);
+			addr[dwrq->length].sa_family = ARPHRD_ETHER;
+			qual[dwrq->length].qual = rssi_to_qual(dtoh16(bi->RSSI));
+			qual[dwrq->length].level = 0x100 + dtoh16(bi->RSSI);
+			qual[dwrq->length].noise = 0x100 + bi->phy_noise;
+
+#if WIRELESS_EXT > 18
+			qual[dwrq->length].updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
+#else
+			qual[dwrq->length].updated = 7;
+#endif 
+
+			dwrq->length++;
+		}
+		buf = buf->next;
+	}
+	if (dwrq->length) {
+		memcpy(&addr[dwrq->length], qual, sizeof(struct iw_quality) * dwrq->length);
+		
+		dwrq->flags = 1;
+	}
+	return 0;
+}
+
+static int
+wl_iw_iscan_prep(wl_scan_params_t *params, wlc_ssid_t *ssid)
+{
+	int err = 0;
+
+	memcpy(&params->bssid, &ether_bcast, ETHER_ADDR_LEN);
+	params->bss_type = DOT11_BSSTYPE_ANY;
+	params->scan_type = 0;
+	params->nprobes = -1;
+	params->active_time = -1;
+	params->passive_time = -1;
+	params->home_time = -1;
+	params->channel_num = 0;
+#if defined(CONFIG_FIRST_SCAN)
+	if (g_first_broadcast_scan == BROADCAST_SCAN_FIRST_STARTED)
+		params->passive_time = 30;
+#endif
+	params->nprobes = htod32(params->nprobes);
+	params->active_time = htod32(params->active_time);
+	params->passive_time = htod32(params->passive_time);
+	params->home_time = htod32(params->home_time);
+	if (ssid && ssid->SSID_len)
+		memcpy(&params->ssid, ssid, sizeof(wlc_ssid_t));
+
+	return err;
+}
+
+static int
+wl_iw_iscan(iscan_info_t *iscan, wlc_ssid_t *ssid, uint16 action)
+{
+	int err = 0;
+
+	iscan->iscan_ex_params_p->version = htod32(ISCAN_REQ_VERSION);
+	iscan->iscan_ex_params_p->action = htod16(action);
+	iscan->iscan_ex_params_p->scan_duration = htod16(0);
+
+	WL_SCAN(("%s : nprobes=%d\n", __FUNCTION__, iscan->iscan_ex_params_p->params.nprobes));
+	WL_SCAN(("active_time=%d\n", iscan->iscan_ex_params_p->params.active_time));
+	WL_SCAN(("passive_time=%d\n", iscan->iscan_ex_params_p->params.passive_time));
+	WL_SCAN(("home_time=%d\n", iscan->iscan_ex_params_p->params.home_time));
+	WL_SCAN(("scan_type=%d\n", iscan->iscan_ex_params_p->params.scan_type));
+	WL_SCAN(("bss_type=%d\n", iscan->iscan_ex_params_p->params.bss_type));
+
+	if ((err = dev_iw_iovar_setbuf(iscan->dev, "iscan", iscan->iscan_ex_params_p, \
+		iscan->iscan_ex_param_size, iscan->ioctlbuf, sizeof(iscan->ioctlbuf)))) {
+			WL_ERROR(("Set ISCAN for %s failed with %d\n", __FUNCTION__, err));
+			err = -1;
+	}
+
+	return err;
+}
+
+static void
+wl_iw_timerfunc(ulong data)
+{
+	iscan_info_t *iscan = (iscan_info_t *)data;
+	if (iscan) {
+		iscan->timer_on = 0;
+		if (iscan->iscan_state != ISCAN_STATE_IDLE) {
+			WL_SCAN(("timer trigger\n"));
+			up(&iscan->sysioc_sem);
+		}
+	}
+}
+static void wl_iw_set_event_mask(struct net_device *dev)
+{
+	char eventmask[WL_EVENTING_MASK_LEN];
+	char iovbuf[WL_EVENTING_MASK_LEN + 12];	
+
+	dev_iw_iovar_getbuf(dev, "event_msgs", "", 0, iovbuf, sizeof(iovbuf));
+	bcopy(iovbuf, eventmask, WL_EVENTING_MASK_LEN);
+	setbit(eventmask, WLC_E_SCAN_COMPLETE);
+	dev_iw_iovar_setbuf(dev, "event_msgs", eventmask, WL_EVENTING_MASK_LEN,
+		iovbuf, sizeof(iovbuf));
+}
+
+static uint32
+wl_iw_iscan_get(iscan_info_t *iscan)
+{
+	iscan_buf_t * buf;
+	iscan_buf_t * ptr;
+	wl_iscan_results_t * list_buf;
+	wl_iscan_results_t list;
+	wl_scan_results_t *results;
+	uint32 status;
+	int res;
+
+	mutex_lock(&wl_cache_lock);
+	if (iscan->list_cur) {
+		buf = iscan->list_cur;
+		iscan->list_cur = buf->next;
+	}
+	else {
+		buf = kmalloc(sizeof(iscan_buf_t), GFP_KERNEL);
+		if (!buf) {
+			WL_ERROR(("%s can't alloc iscan_buf_t : going to abort currect iscan\n", \
+						__FUNCTION__));
+			mutex_unlock(&wl_cache_lock);
+			return WL_SCAN_RESULTS_NO_MEM;
+		}
+		buf->next = NULL;
+		if (!iscan->list_hdr)
+			iscan->list_hdr = buf;
+		else {
+			ptr = iscan->list_hdr;
+			while (ptr->next) {
+				ptr = ptr->next;
+			}
+			ptr->next = buf;
+		}
+	}
+	memset(buf->iscan_buf, 0, WLC_IW_ISCAN_MAXLEN);
+	list_buf = (wl_iscan_results_t*)buf->iscan_buf;
+	results = &list_buf->results;
+	results->buflen = WL_ISCAN_RESULTS_FIXED_SIZE;
+	results->version = 0;
+	results->count = 0;
+
+	memset(&list, 0, sizeof(list));
+	list.results.buflen = htod32(WLC_IW_ISCAN_MAXLEN);
+	res = dev_iw_iovar_getbuf(
+		iscan->dev,
+		"iscanresults",
+		&list,
+		WL_ISCAN_RESULTS_FIXED_SIZE,
+		buf->iscan_buf,
+		WLC_IW_ISCAN_MAXLEN);
+	if (res == 0) {
+		results->buflen = dtoh32(results->buflen);
+		results->version = dtoh32(results->version);
+		results->count = dtoh32(results->count);
+		WL_SCAN(("results->count = %d\n", results->count));
+
+		WL_SCAN(("results->buflen = %d\n", results->buflen));
+		status = dtoh32(list_buf->status);
+	} else {
+		WL_ERROR(("%s returns error %d\n", __FUNCTION__, res));
+		status = WL_SCAN_RESULTS_NO_MEM;
+	}
+	mutex_unlock(&wl_cache_lock);
+	return status;
+}
+
+static void wl_iw_force_specific_scan(iscan_info_t *iscan)
+{
+	WL_SCAN(("%s force Specific SCAN for %s\n", __FUNCTION__, g_specific_ssid.SSID));
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+	rtnl_lock();
+#endif
+	(void) dev_wlc_ioctl(iscan->dev, WLC_SCAN, &g_specific_ssid, sizeof(g_specific_ssid));
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+	rtnl_unlock();
+#endif
+}
+
+static void wl_iw_send_scan_complete(iscan_info_t *iscan)
+{
+#ifndef SANDGATE2G
+	union iwreq_data wrqu;
+
+	memset(&wrqu, 0, sizeof(wrqu));
+
+	wireless_send_event(iscan->dev, SIOCGIWSCAN, &wrqu, NULL);
+#if defined(CONFIG_FIRST_SCAN)
+	if (g_first_broadcast_scan == BROADCAST_SCAN_FIRST_STARTED)
+		g_first_broadcast_scan = BROADCAST_SCAN_FIRST_RESULT_READY;
+#endif
+	WL_SCAN(("Send Event ISCAN complete\n"));
+#endif 
+}
+
+static int
+_iscan_sysioc_thread(void *data)
+{
+	uint32 status;
+	iscan_info_t *iscan = (iscan_info_t *)data;
+	static bool iscan_pass_abort = FALSE;
+
+	DAEMONIZE("iscan_sysioc");
+
+	status = WL_SCAN_RESULTS_PARTIAL;
+	while (down_interruptible(&iscan->sysioc_sem) == 0) {
+
+		net_os_wake_lock(iscan->dev);
+
+#if defined(SOFTAP)
+		if (ap_cfg_running) {
+			WL_SCAN(("%s skipping SCAN ops in AP mode !!!\n", __FUNCTION__));
+			net_os_wake_unlock(iscan->dev);
+			continue;
+		}
+#endif
+
+		if (iscan->timer_on) {
+			iscan->timer_on = 0;
+			del_timer_sync(&iscan->timer);
+		}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+		rtnl_lock();
+#endif
+		status = wl_iw_iscan_get(iscan);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+		rtnl_unlock();
+#endif
+
+		if (g_scan_specified_ssid && (iscan_pass_abort == TRUE)) {
+			WL_SCAN(("%s Get results from specific scan status=%d\n", __FUNCTION__, status));
+			wl_iw_send_scan_complete(iscan);
+			iscan_pass_abort = FALSE;
+			status  = -1;
+		}
+
+		switch (status) {
+			case WL_SCAN_RESULTS_PARTIAL:
+				WL_SCAN(("iscanresults incomplete\n"));
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+				rtnl_lock();
+#endif
+
+				wl_iw_iscan(iscan, NULL, WL_SCAN_ACTION_CONTINUE);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+				rtnl_unlock();
+#endif
+
+				mod_timer(&iscan->timer, jiffies + iscan->timer_ms*HZ/1000);
+				iscan->timer_on = 1;
+				break;
+			case WL_SCAN_RESULTS_SUCCESS:
+				WL_SCAN(("iscanresults complete\n"));
+				iscan->iscan_state = ISCAN_STATE_IDLE;
+				wl_iw_send_scan_complete(iscan);
+				break;
+			case WL_SCAN_RESULTS_PENDING:
+				WL_SCAN(("iscanresults pending\n"));
+
+				mod_timer(&iscan->timer, jiffies + iscan->timer_ms*HZ/1000);
+				iscan->timer_on = 1;
+				break;
+			case WL_SCAN_RESULTS_ABORTED:
+				WL_SCAN(("iscanresults aborted\n"));
+				iscan->iscan_state = ISCAN_STATE_IDLE;
+				if (g_scan_specified_ssid == 0)
+					wl_iw_send_scan_complete(iscan);
+				else {
+					iscan_pass_abort = TRUE;
+					wl_iw_force_specific_scan(iscan);
+				}
+				break;
+			case WL_SCAN_RESULTS_NO_MEM:
+				WL_SCAN(("iscanresults can't alloc memory: skip\n"));
+				iscan->iscan_state = ISCAN_STATE_IDLE;
+				break;
+			default:
+				WL_SCAN(("iscanresults returned unknown status %d\n", status));
+				break;
+		}
+
+		net_os_wake_unlock(iscan->dev);
+	}
+
+	if (iscan->timer_on) {
+		iscan->timer_on = 0;
+		del_timer_sync(&iscan->timer);
+	}
+
+	complete_and_exit(&iscan->sysioc_exited, 0);
+}
+#endif 
+
+#if !defined(CSCAN)
+
+static void
+wl_iw_set_ss_cache_timer_flag(void)
+{
+	g_ss_cache_ctrl.m_timer_expired = 1;
+	WL_TRACE(("%s called\n", __FUNCTION__));
+}
+
+static int
+wl_iw_init_ss_cache_ctrl(void)
+{
+	WL_TRACE(("%s :\n", __FUNCTION__));
+	g_ss_cache_ctrl.m_prev_scan_mode = 0;
+	g_ss_cache_ctrl.m_cons_br_scan_cnt = 0;
+	g_ss_cache_ctrl.m_cache_head = NULL;
+	g_ss_cache_ctrl.m_link_down = 0;
+	g_ss_cache_ctrl.m_timer_expired = 0;
+	memset(g_ss_cache_ctrl.m_active_bssid, 0, ETHER_ADDR_LEN);
+
+	g_ss_cache_ctrl.m_timer = kmalloc(sizeof(struct timer_list), GFP_KERNEL);
+	if (!g_ss_cache_ctrl.m_timer) {
+		return -ENOMEM;
+	}
+	g_ss_cache_ctrl.m_timer->function = (void *)wl_iw_set_ss_cache_timer_flag;
+	init_timer(g_ss_cache_ctrl.m_timer);
+
+	return 0;
+}
+
+
+
+static void
+wl_iw_free_ss_cache(void)
+{
+	wl_iw_ss_cache_t *node, *cur;
+	wl_iw_ss_cache_t **spec_scan_head;
+
+	WL_TRACE(("%s called\n", __FUNCTION__));
+
+	mutex_lock(&wl_cache_lock);
+	spec_scan_head = &g_ss_cache_ctrl.m_cache_head;
+	node = *spec_scan_head;
+
+	for (;node;) {
+		WL_TRACE(("%s : SSID - %s\n", __FUNCTION__, node->bss_info->SSID));
+		cur = node;
+		node = cur->next;
+		kfree(cur);
+	}
+	*spec_scan_head = NULL;
+	mutex_unlock(&wl_cache_lock);
+}
+
+
+
+static int
+wl_iw_run_ss_cache_timer(int kick_off)
+{
+	struct timer_list **timer;
+
+	timer = &g_ss_cache_ctrl.m_timer;
+
+	if (*timer) {
+		if (kick_off) {
+			(*timer)->expires = jiffies + 30000 * HZ / 1000;	
+			add_timer(*timer);
+			WL_TRACE(("%s : timer starts \n", __FUNCTION__));
+		} else {
+			del_timer_sync(*timer);
+			WL_TRACE(("%s : timer stops \n", __FUNCTION__));
+		}
+	}
+
+	return 0;
+}
+
+
+void
+wl_iw_release_ss_cache_ctrl(void)
+{
+	WL_TRACE(("%s :\n", __FUNCTION__));
+	wl_iw_free_ss_cache();
+	wl_iw_run_ss_cache_timer(0);
+	if (g_ss_cache_ctrl.m_timer) {
+		kfree(g_ss_cache_ctrl.m_timer);
+	}
+}
+
+
+
+static void
+wl_iw_reset_ss_cache(void)
+{
+	wl_iw_ss_cache_t *node, *prev, *cur;
+	wl_iw_ss_cache_t **spec_scan_head;
+
+	mutex_lock(&wl_cache_lock);
+	spec_scan_head = &g_ss_cache_ctrl.m_cache_head;
+	node = *spec_scan_head;
+	prev = node;
+
+	for (;node;) {
+		WL_TRACE(("%s : node SSID %s \n", __FUNCTION__, node->bss_info->SSID));
+		if (!node->dirty) {
+			cur = node;
+			if (cur == *spec_scan_head) {
+				*spec_scan_head = cur->next;
+				prev = *spec_scan_head;
+			}
+			else {
+				prev->next = cur->next;
+			}
+			node = cur->next;
+
+			WL_TRACE(("%s : Del node : SSID %s\n", __FUNCTION__, cur->bss_info->SSID));
+			kfree(cur);
+			continue;
+		}
+
+		node->dirty = 0;
+		prev = node;
+		node = node->next;
+	}
+	mutex_unlock(&wl_cache_lock);
+}
+
+
+static int
+wl_iw_add_bss_to_ss_cache(wl_scan_results_t *ss_list)
+{
+
+	wl_iw_ss_cache_t *node, *prev, *leaf;
+	wl_iw_ss_cache_t **spec_scan_head;
+	wl_bss_info_t *bi = NULL;
+	int i;
+
+	if (!ss_list->count) {
+		return 0;
+	}
+
+	mutex_lock(&wl_cache_lock);
+	spec_scan_head = &g_ss_cache_ctrl.m_cache_head;
+
+	for (i = 0; i < ss_list->count; i++) {
+
+		node = *spec_scan_head;
+		prev = node;
+
+		bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : ss_list->bss_info;
+
+		WL_TRACE(("%s : find %d with specific SSID %s\n", __FUNCTION__, i, bi->SSID));
+		for (;node;) {
+			if (!memcmp(&node->bss_info->BSSID, &bi->BSSID, ETHER_ADDR_LEN)) {
+				
+				WL_TRACE(("dirty marked : SSID %s\n", bi->SSID));
+				node->dirty = 1;
+				break;
+			}
+			prev = node;
+			node = node->next;
+		}
+
+		if (node) {
+			continue;
+		}
+		leaf = kmalloc(bi->length + WLC_IW_SS_CACHE_CTRL_FIELD_MAXLEN, GFP_KERNEL);
+		if (!leaf) {
+			WL_ERROR(("Memory alloc failure %d\n", \
+				bi->length + WLC_IW_SS_CACHE_CTRL_FIELD_MAXLEN));
+			mutex_unlock(&wl_cache_lock);
+			return -ENOMEM;
+		}
+
+		memcpy(leaf->bss_info, bi, bi->length);
+		leaf->next = NULL;
+		leaf->dirty = 1;
+		leaf->count = 1;
+		leaf->version = ss_list->version;
+
+		if (!prev) {
+			*spec_scan_head = leaf;
+		}
+		else {
+			prev->next = leaf;
+		}
+	}
+	mutex_unlock(&wl_cache_lock);
+	return 0;
+}
+
+
+static int
+wl_iw_merge_scan_cache(struct iw_request_info *info, char *extra, uint buflen_from_user,
+__u16 *merged_len)
+{
+	wl_iw_ss_cache_t *node;
+	wl_scan_results_t *list_merge;
+
+	mutex_lock(&wl_cache_lock);
+	node = g_ss_cache_ctrl.m_cache_head;
+	for (;node;) {
+		list_merge = (wl_scan_results_t *)&node->buflen;
+		WL_TRACE(("%s: Cached Specific APs list=%d\n", __FUNCTION__, list_merge->count));
+		if (buflen_from_user - *merged_len > 0) {
+			*merged_len += (__u16) wl_iw_get_scan_prep(list_merge, info,
+				extra + *merged_len, buflen_from_user - *merged_len);
+		}
+		else {
+			WL_TRACE(("%s: exit with break\n", __FUNCTION__));
+			break;
+		}
+		node = node->next;
+	}
+	mutex_unlock(&wl_cache_lock);
+	return 0;
+}
+
+
+static int
+wl_iw_delete_bss_from_ss_cache(void *addr)
+{
+
+	wl_iw_ss_cache_t *node, *prev;
+	wl_iw_ss_cache_t **spec_scan_head;
+
+	mutex_lock(&wl_cache_lock);
+	spec_scan_head = &g_ss_cache_ctrl.m_cache_head;
+	node = *spec_scan_head;
+	prev = node;
+	for (;node;) {
+		if (!memcmp(&node->bss_info->BSSID, addr, ETHER_ADDR_LEN)) {
+			if (node == *spec_scan_head) {
+				*spec_scan_head = node->next;
+			}
+			else {
+				prev->next = node->next;
+			}
+
+			WL_TRACE(("%s : Del node : %s\n", __FUNCTION__, node->bss_info->SSID));
+			kfree(node);
+			break;
+		}
+
+		prev = node;
+		node = node->next;
+	}
+
+	memset(addr, 0, ETHER_ADDR_LEN);
+	mutex_unlock(&wl_cache_lock);
+	return 0;
+}
+
+#endif
+
+
+static int
+wl_iw_set_scan(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	union iwreq_data *wrqu,
+	char *extra
+)
+{
+	int error;
+	WL_TRACE(("%s dev:%s: SIOCSIWSCAN : SCAN\n", __FUNCTION__, dev->name));
+
+#if defined(CSCAN)
+	WL_ERROR(("%s: Scan from SIOCGIWSCAN not supported\n", __FUNCTION__));
+	return -EINVAL;
+#endif 
+
+#if defined(SOFTAP)
+	if (ap_cfg_running) {
+		WL_TRACE(("\n>%s: Not executed, reason -'SOFTAP is active'\n", __FUNCTION__));
+		return 0;
+	}
+#endif
+
+	if (g_onoff == G_WLAN_SET_OFF)
+		return 0;
+
+	memset(&g_specific_ssid, 0, sizeof(g_specific_ssid));
+#ifndef WL_IW_USE_ISCAN
+	g_scan_specified_ssid = 0;
+#endif 
+
+#if WIRELESS_EXT > 17
+	
+	if (wrqu->data.length == sizeof(struct iw_scan_req)) {
+		if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
+			struct iw_scan_req *req = (struct iw_scan_req *)extra;
+#if defined(CONFIG_FIRST_SCAN)
+			if (g_first_broadcast_scan != BROADCAST_SCAN_FIRST_RESULT_CONSUMED) {
+				WL_ERROR(("%s Ignoring SC %s first BC is not done = %d\n", \
+						__FUNCTION__, req->essid, \
+						g_first_broadcast_scan));
+				return -EBUSY;
+			}
+#endif
+			if (g_scan_specified_ssid) {
+				WL_SCAN(("%s Specific SCAN is not done ignore scan for = %s \n", \
+					__FUNCTION__, req->essid));
+				return -EBUSY;
+			}
+			else {
+				g_specific_ssid.SSID_len = MIN(sizeof(g_specific_ssid.SSID), \
+										req->essid_len);
+				memcpy(g_specific_ssid.SSID, req->essid, g_specific_ssid.SSID_len);
+				g_specific_ssid.SSID_len = htod32(g_specific_ssid.SSID_len);
+				g_scan_specified_ssid = 1;
+				WL_TRACE(("### Specific scan ssid=%s len=%d\n", \
+						g_specific_ssid.SSID, g_specific_ssid.SSID_len));
+			}
+		}
+	}
+#endif 
+	
+	if ((error = dev_wlc_ioctl(dev, WLC_SCAN, &g_specific_ssid, sizeof(g_specific_ssid)))) {
+		WL_SCAN(("Set SCAN for %s failed with %d\n", g_specific_ssid.SSID, error));
+		g_scan_specified_ssid = 0;
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+#ifdef WL_IW_USE_ISCAN
+int
+wl_iw_iscan_set_scan_broadcast_prep(struct net_device *dev, uint flag)
+{
+	wlc_ssid_t ssid;
+	iscan_info_t *iscan = g_iscan;
+
+#if defined(CONFIG_FIRST_SCAN)
+	if (g_first_broadcast_scan == BROADCAST_SCAN_FIRST_IDLE) {
+		g_first_broadcast_scan = BROADCAST_SCAN_FIRST_STARTED;
+		WL_SCAN(("%s: First Brodcast scan was forced\n", __FUNCTION__));
+	}
+	else if (g_first_broadcast_scan == BROADCAST_SCAN_FIRST_STARTED) {
+		WL_SCAN(("%s: ignore ISCAN request first BS is not done yet\n", __FUNCTION__));
+		return 0;
+	}
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+	if (flag)
+		rtnl_lock();
+#endif
+
+	dev_wlc_ioctl(dev, WLC_SET_PASSIVE_SCAN, &iscan->scan_flag, sizeof(iscan->scan_flag));
+	wl_iw_set_event_mask(dev);
+
+	WL_SCAN(("+++: Set Broadcast ISCAN\n"));
+	
+	memset(&ssid, 0, sizeof(ssid));
+
+	iscan->list_cur = iscan->list_hdr;
+	iscan->iscan_state = ISCAN_STATE_SCANING;
+
+	memset(&iscan->iscan_ex_params_p->params, 0, iscan->iscan_ex_param_size);
+	wl_iw_iscan_prep(&iscan->iscan_ex_params_p->params, &ssid);
+	wl_iw_iscan(iscan, &ssid, WL_SCAN_ACTION_START);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+	if (flag)
+		rtnl_unlock();
+#endif
+
+	mod_timer(&iscan->timer, jiffies + iscan->timer_ms*HZ/1000);
+
+	iscan->timer_on = 1;
+
+	return 0;
+}
+
+static int
+wl_iw_iscan_set_scan(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	union iwreq_data *wrqu,
+	char *extra
+)
+{
+	wlc_ssid_t ssid;
+	iscan_info_t *iscan = g_iscan;
+	int ret = 0;
+
+	WL_SCAN(("%s: SIOCSIWSCAN : ISCAN\n", dev->name));
+
+#if defined(CSCAN)
+	WL_ERROR(("%s: Scan from SIOCGIWSCAN not supported\n", __FUNCTION__));
+	return -EINVAL;
+#endif
+
+	net_os_wake_lock(dev);
+
+#if defined(SOFTAP)
+	if (ap_cfg_running) {
+		WL_SCAN(("\n>%s: Not executed, reason -'SOFTAP is active'\n", __FUNCTION__));
+		goto set_scan_end;
+	}
+#endif
+
+	if (g_onoff == G_WLAN_SET_OFF) {
+		WL_SCAN(("%s: driver is not up yet after START\n", __FUNCTION__));
+		goto set_scan_end;
+	}
+
+#ifdef PNO_SUPPORT
+	if  (dhd_dev_get_pno_status(dev)) {
+		WL_SCAN(("%s: Scan called when PNO is active\n", __FUNCTION__));
+	}
+#endif
+
+	if ((!iscan) || (iscan->sysioc_pid < 0)) {
+		WL_ERROR(("%s error\n", __FUNCTION__));
+		goto set_scan_end;
+	}
+
+	if (g_scan_specified_ssid) {
+		WL_SCAN(("%s Specific SCAN already running ignoring BC scan\n", \
+				__FUNCTION__));
+		ret = EBUSY;
+		goto set_scan_end;
+	}
+
+	memset(&ssid, 0, sizeof(ssid));
+
+#if WIRELESS_EXT > 17
+	
+	if (wrqu->data.length == sizeof(struct iw_scan_req)) {
+		if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
+			int as = 0;
+			struct iw_scan_req *req = (struct iw_scan_req *)extra;
+			ssid.SSID_len = MIN(sizeof(ssid.SSID), req->essid_len);
+			memcpy(ssid.SSID, req->essid, ssid.SSID_len);
+			ssid.SSID_len = htod32(ssid.SSID_len);
+			dev_wlc_ioctl(dev, WLC_SET_PASSIVE_SCAN, &as, sizeof(as));
+			wl_iw_set_event_mask(dev);
+			ret = wl_iw_set_scan(dev, info, wrqu, extra);
+			goto set_scan_end;
+		}
+		else {
+			g_scan_specified_ssid = 0;
+
+			if (iscan->iscan_state == ISCAN_STATE_SCANING) {
+				WL_SCAN(("%s ISCAN already in progress \n", __FUNCTION__));
+				goto set_scan_end;
+			}
+		}
+	}
+#endif 
+
+#if defined(CONFIG_FIRST_SCAN) && !defined(CSCAN)
+	if (g_first_broadcast_scan < BROADCAST_SCAN_FIRST_RESULT_CONSUMED) {
+		if (++g_first_counter_scans == MAX_ALLOWED_BLOCK_SCAN_FROM_FIRST_SCAN) {
+
+			WL_ERROR(("%s Clean up First scan flag which is %d\n", \
+				 __FUNCTION__, g_first_broadcast_scan));
+			g_first_broadcast_scan = BROADCAST_SCAN_FIRST_RESULT_CONSUMED;
+		}
+		else {
+			WL_ERROR(("%s Ignoring Broadcast Scan:First Scan is not done yet %d\n", \
+					__FUNCTION__, g_first_counter_scans));
+			ret = -EBUSY;
+			goto set_scan_end;
+		}
+	}
+#endif
+
+	wl_iw_iscan_set_scan_broadcast_prep(dev, 0);
+
+set_scan_end:
+	net_os_wake_unlock(dev);
+	return ret;
+}
+#endif 
+
+#if WIRELESS_EXT > 17
+static bool
+ie_is_wpa_ie(uint8 **wpaie, uint8 **tlvs, int *tlvs_len)
+{
+	uint8 *ie = *wpaie;
+
+	if ((ie[1] >= 6) &&
+		!bcmp((const void *)&ie[2], (const void *)(WPA_OUI "\x01"), 4)) {
+		return TRUE;
+	}
+
+	ie += ie[1] + 2;
+	
+	*tlvs_len -= (int)(ie - *tlvs);
+	
+	*tlvs = ie;
+	return FALSE;
+}
+
+static bool
+ie_is_wps_ie(uint8 **wpsie, uint8 **tlvs, int *tlvs_len)
+{
+	uint8 *ie = *wpsie;
+
+	if ((ie[1] >= 4) &&
+		!bcmp((const void *)&ie[2], (const void *)(WPA_OUI "\x04"), 4)) {
+		return TRUE;
+	}
+
+	ie += ie[1] + 2;
+	
+	*tlvs_len -= (int)(ie - *tlvs);
+	
+	*tlvs = ie;
+	return FALSE;
+}
+#endif 
+
+static inline int _wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data,
+	size_t len, int uppercase)
+{
+	size_t i;
+	char *pos = buf, *end = buf + buf_size;
+	int ret;
+	if (buf_size == 0)
+		return 0;
+	for (i = 0; i < len; i++) {
+		ret = snprintf(pos, end - pos, uppercase ? "%02X" : "%02x",
+			data[i]);
+		if (ret < 0 || ret >= end - pos) {
+			end[-1] = '\0';
+			return pos - buf;
+		}
+		pos += ret;
+	}
+	end[-1] = '\0';
+	return pos - buf;
+}
+
+
+int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len)
+{
+	return _wpa_snprintf_hex(buf, buf_size, data, len, 0);
+}
+
+static int
+wl_iw_handle_scanresults_ies(char **event_p, char *end,
+	struct iw_request_info *info, wl_bss_info_t *bi)
+{
+#if WIRELESS_EXT > 17
+	struct iw_event	iwe;
+	char *event;
+	char *buf;
+	int custom_event_len;
+
+	event = *event_p;
+	if (bi->ie_length) {
+		
+		bcm_tlv_t *ie;
+		uint8 *ptr = ((uint8 *)bi) + sizeof(wl_bss_info_t);
+		int ptr_len = bi->ie_length;
+
+#ifdef BCMWPA2
+		if ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_RSN_ID))) {
+			iwe.cmd = IWEVGENIE;
+			iwe.u.data.length = ie->len + 2;
+			event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie);
+		}
+		ptr = ((uint8 *)bi) + sizeof(wl_bss_info_t);
+#endif 
+
+		while ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_WPA_ID))) {
+			
+			if (ie_is_wps_ie(((uint8 **)&ie), &ptr, &ptr_len)) {
+				iwe.cmd = IWEVGENIE;
+				iwe.u.data.length = ie->len + 2;
+				event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie);
+				break;
+			}
+		}
+
+		ptr = ((uint8 *)bi) + sizeof(wl_bss_info_t);
+		ptr_len = bi->ie_length;
+		while ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_WPA_ID))) {
+			if (ie_is_wpa_ie(((uint8 **)&ie), &ptr, &ptr_len)) {
+				iwe.cmd = IWEVGENIE;
+				iwe.u.data.length = ie->len + 2;
+				event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie);
+				break;
+			}
+		}
+
+		ptr = ((uint8 *)bi) + sizeof(wl_bss_info_t);
+		ptr_len = bi->ie_length;
+
+		while ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_WAPI_ID))) {
+			WL_TRACE(("%s: found a WAPI IE...\n", __FUNCTION__));
+#ifdef WAPI_IE_USE_GENIE
+			iwe.cmd = IWEVGENIE;
+			iwe.u.data.length = ie->len + 2;
+			event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie);
+#else 
+			iwe.cmd = IWEVCUSTOM;
+			custom_event_len = strlen("wapi_ie=") + 2*(ie->len + 2);
+			iwe.u.data.length = custom_event_len;
+
+			buf = kmalloc(custom_event_len+1, GFP_KERNEL);
+			if (buf == NULL)
+			{
+				WL_ERROR(("malloc(%d) returned NULL...\n", custom_event_len));
+				break;
+			}
+
+			memcpy(buf, "wapi_ie=", 8);
+			wpa_snprintf_hex(buf + 8, 2+1, &(ie->id), 1);
+			wpa_snprintf_hex(buf + 10, 2+1, &(ie->len), 1);
+			wpa_snprintf_hex(buf + 12, 2*ie->len+1, ie->data, ie->len);
+			event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, buf);
+			kfree(buf);
+#endif 
+			break;
+		}
+	*event_p = event;
+	}
+#endif 
+
+	return 0;
+}
+
+#ifndef CSCAN
+static uint
+wl_iw_get_scan_prep(
+	wl_scan_results_t *list,
+	struct iw_request_info *info,
+	char *extra,
+	short max_size)
+{
+	int  i, j;
+	struct iw_event  iwe;
+	wl_bss_info_t *bi = NULL;
+	char *event = extra, *end = extra + max_size - WE_ADD_EVENT_FIX, *value;
+	int	ret = 0;
+	int channel;
+
+	if (!list) {
+		WL_ERROR(("%s: Null list pointer",__FUNCTION__));
+		return ret;
+	}
+
+	for (i = 0; i < list->count && i < IW_MAX_AP; i++)
+	{
+		if (list->version != WL_BSS_INFO_VERSION) {
+			WL_ERROR(("%s : list->version %d != WL_BSS_INFO_VERSION\n", \
+				__FUNCTION__, list->version));
+			return ret;
+		}
+
+		bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : list->bss_info;
+
+		WL_TRACE(("%s : %s\n", __FUNCTION__, bi->SSID));
+
+		iwe.cmd = SIOCGIWAP;
+		iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+		memcpy(iwe.u.ap_addr.sa_data, &bi->BSSID, ETHER_ADDR_LEN);
+		event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_ADDR_LEN);
+		
+		iwe.u.data.length = dtoh32(bi->SSID_len);
+		iwe.cmd = SIOCGIWESSID;
+		iwe.u.data.flags = 1;
+		event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, bi->SSID);
+
+		if (dtoh16(bi->capability) & (DOT11_CAP_ESS | DOT11_CAP_IBSS)) {
+			iwe.cmd = SIOCGIWMODE;
+			if (dtoh16(bi->capability) & DOT11_CAP_ESS)
+				iwe.u.mode = IW_MODE_INFRA;
+			else
+				iwe.u.mode = IW_MODE_ADHOC;
+			event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_UINT_LEN);
+		}
+
+		iwe.cmd = SIOCGIWFREQ;
+		channel = (bi->ctl_ch == 0) ? CHSPEC_CHANNEL(bi->chanspec) : bi->ctl_ch;
+		iwe.u.freq.m = wf_channel2mhz(channel,
+			channel <= CH_MAX_2G_CHANNEL ?
+			WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G);
+		iwe.u.freq.e = 6;
+		event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_FREQ_LEN);
+
+		iwe.cmd = IWEVQUAL;
+		iwe.u.qual.qual = rssi_to_qual(dtoh16(bi->RSSI));
+		iwe.u.qual.level = 0x100 + dtoh16(bi->RSSI);
+		iwe.u.qual.noise = 0x100 + bi->phy_noise;
+		event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_QUAL_LEN);
+
+		wl_iw_handle_scanresults_ies(&event, end, info, bi);
+
+		iwe.cmd = SIOCGIWENCODE;
+		if (dtoh16(bi->capability) & DOT11_CAP_PRIVACY)
+			iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+		else
+			iwe.u.data.flags = IW_ENCODE_DISABLED;
+		iwe.u.data.length = 0;
+		event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)event);
+
+		if (bi->rateset.count) {
+			if (((event -extra) + IW_EV_LCP_LEN) <= (uintptr)end) {
+				value = event + IW_EV_LCP_LEN;
+				iwe.cmd = SIOCGIWRATE;
+
+				iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
+				for (j = 0; j < bi->rateset.count && j < IW_MAX_BITRATES; j++) {
+					iwe.u.bitrate.value = (bi->rateset.rates[j] & 0x7f) * 500000;
+					value = IWE_STREAM_ADD_VALUE(info, event, value, end, &iwe,
+						IW_EV_PARAM_LEN);
+				}
+				event = value;
+			}
+		}
+	} 
+
+	if ((ret = (event - extra)) < 0) {
+		WL_ERROR(("==> Wrong size\n"));
+		ret = 0;
+	}
+	WL_TRACE(("%s: size=%d bytes prepared \n", __FUNCTION__, (unsigned int)(event - extra)));
+	return (uint)ret;
+}
+
+static int
+wl_iw_get_scan(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_point *dwrq,
+	char *extra
+)
+{
+	channel_info_t ci;
+	wl_scan_results_t *list_merge;
+	wl_scan_results_t *list = (wl_scan_results_t *) g_scan;
+	int error;
+	uint buflen_from_user = dwrq->length;
+	uint len =  G_SCAN_RESULTS;
+	__u16 len_ret = 0;
+#if !defined(CSCAN)
+	__u16 merged_len = 0;
+#endif
+#if defined(WL_IW_USE_ISCAN)
+	iscan_info_t *iscan = g_iscan;
+	iscan_buf_t * p_buf;
+#if  !defined(CSCAN)
+	uint32 counter = 0;
+#endif
+#endif
+	WL_TRACE(("%s: buflen_from_user %d: \n", dev->name, buflen_from_user));
+
+	if (!extra) {
+		WL_TRACE(("%s: wl_iw_get_scan return -EINVAL\n", dev->name));
+		return -EINVAL;
+	}
+
+	if ((error = dev_wlc_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(ci))))
+		return error;
+	ci.scan_channel = dtoh32(ci.scan_channel);
+	if (ci.scan_channel)
+		return -EAGAIN;
+
+#if !defined(CSCAN)
+	if (g_ss_cache_ctrl.m_timer_expired) {
+		wl_iw_free_ss_cache();
+		g_ss_cache_ctrl.m_timer_expired ^= 1;
+	}
+	if ((!g_scan_specified_ssid && g_ss_cache_ctrl.m_prev_scan_mode) ||
+		g_ss_cache_ctrl.m_cons_br_scan_cnt > 4) {
+		g_ss_cache_ctrl.m_cons_br_scan_cnt = 0;
+		
+		wl_iw_reset_ss_cache();
+	}
+	g_ss_cache_ctrl.m_prev_scan_mode = g_scan_specified_ssid;
+	if (g_scan_specified_ssid) {
+		g_ss_cache_ctrl.m_cons_br_scan_cnt = 0;
+	}
+	else {
+		g_ss_cache_ctrl.m_cons_br_scan_cnt++;
+	}
+#endif
+
+	if (g_scan_specified_ssid) {
+		
+		list = kmalloc(len, GFP_KERNEL);
+		if (!list) {
+			WL_TRACE(("%s: wl_iw_get_scan return -ENOMEM\n", dev->name));
+			g_scan_specified_ssid = 0;
+			return -ENOMEM;
+		}
+	}
+
+	memset(list, 0, len);
+	list->buflen = htod32(len);
+	if ((error = dev_wlc_ioctl(dev, WLC_SCAN_RESULTS, list, len))) {
+		WL_ERROR(("%s: %s : Scan_results ERROR %d\n", dev->name, __FUNCTION__, error));
+		dwrq->length = len;
+		if (g_scan_specified_ssid) {
+			g_scan_specified_ssid = 0;
+			kfree(list);
+		}
+		return 0;
+	}
+	list->buflen = dtoh32(list->buflen);
+	list->version = dtoh32(list->version);
+	list->count = dtoh32(list->count);
+
+	if (list->version != WL_BSS_INFO_VERSION) {
+		WL_ERROR(("%s : list->version %d != WL_BSS_INFO_VERSION\n",
+				__FUNCTION__, list->version));
+		if (g_scan_specified_ssid) {
+			g_scan_specified_ssid = 0;
+			kfree(list);
+		}
+		return -EINVAL;
+	}
+
+#if !defined(CSCAN)
+	if (g_scan_specified_ssid) {
+		
+		wl_iw_add_bss_to_ss_cache(list);
+		kfree(list);
+	}
+
+	mutex_lock(&wl_cache_lock);
+#if defined(WL_IW_USE_ISCAN)
+	if (g_scan_specified_ssid)
+		WL_TRACE(("%s: Specified scan APs from scan=%d\n", __FUNCTION__, list->count));
+	p_buf = iscan->list_hdr;
+	
+	while (p_buf != iscan->list_cur) {
+		list_merge = &((wl_iscan_results_t*)p_buf->iscan_buf)->results;
+		WL_TRACE(("%s: Bcast APs list=%d\n", __FUNCTION__, list_merge->count));
+		counter += list_merge->count;
+		if (list_merge->count > 0)
+			len_ret += (__u16) wl_iw_get_scan_prep(list_merge, info,
+				extra+len_ret, buflen_from_user -len_ret);
+		p_buf = p_buf->next;
+	}
+	WL_TRACE(("%s merged with total Bcast APs=%d\n", __FUNCTION__, counter));
+#else
+	list_merge = (wl_scan_results_t *) g_scan;
+	len_ret = (__u16) wl_iw_get_scan_prep(list_merge, info, extra, buflen_from_user);
+#endif
+	mutex_unlock(&wl_cache_lock);
+	if (g_ss_cache_ctrl.m_link_down) {
+		wl_iw_delete_bss_from_ss_cache(g_ss_cache_ctrl.m_active_bssid);
+	}
+
+	wl_iw_merge_scan_cache(info, extra+len_ret, buflen_from_user-len_ret, &merged_len);
+	len_ret += merged_len;
+	wl_iw_run_ss_cache_timer(0);
+	wl_iw_run_ss_cache_timer(1);
+#else
+
+	if (g_scan_specified_ssid) {
+		WL_TRACE(("%s: Specified scan APs in the list =%d\n", __FUNCTION__, list->count));
+		len_ret = (__u16) wl_iw_get_scan_prep(list, info, extra, buflen_from_user);
+		kfree(list);
+
+#if defined(WL_IW_USE_ISCAN)
+		p_buf = iscan->list_hdr;
+		
+		while (p_buf != iscan->list_cur) {
+			list_merge = &((wl_iscan_results_t*)p_buf->iscan_buf)->results;
+			WL_TRACE(("%s: Bcast APs list=%d\n", __FUNCTION__, list_merge->count));
+			if (list_merge->count > 0)
+				len_ret += (__u16) wl_iw_get_scan_prep(list_merge, info,
+				    extra+len_ret, buflen_from_user -len_ret);
+			p_buf = p_buf->next;
+		}
+#else
+		list_merge = (wl_scan_results_t *) g_scan;
+		WL_TRACE(("%s: Bcast APs list=%d\n", __FUNCTION__, list_merge->count));
+		if (list_merge->count > 0)
+			len_ret += (__u16) wl_iw_get_scan_prep(list_merge, info, extra+len_ret,
+				buflen_from_user -len_ret);
+#endif
+	}
+	else {
+		list = (wl_scan_results_t *) g_scan;
+		len_ret = (__u16) wl_iw_get_scan_prep(list, info, extra, buflen_from_user);
+	}
+#endif
+
+#if defined(WL_IW_USE_ISCAN)
+	
+	g_scan_specified_ssid = 0;
+#endif 
+	
+	if ((len_ret + WE_ADD_EVENT_FIX) < buflen_from_user)
+		len = len_ret;
+
+	dwrq->length = len;
+	dwrq->flags = 0;	
+
+	WL_TRACE(("%s return to WE %d bytes APs=%d\n", __FUNCTION__, dwrq->length, list->count));
+	return 0;
+}
+#endif
+
+#if defined(WL_IW_USE_ISCAN)
+static int
+wl_iw_iscan_get_scan(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_point *dwrq,
+	char *extra
+)
+{
+	wl_scan_results_t *list;
+	struct iw_event	iwe;
+	wl_bss_info_t *bi = NULL;
+	int ii, j;
+	int apcnt;
+	char *event = extra, *end = extra + dwrq->length, *value;
+	iscan_info_t *iscan = g_iscan;
+	iscan_buf_t * p_buf;
+	uint32  counter = 0;
+	uint8   channel;
+#if !defined(CSCAN)
+	__u16 merged_len = 0;
+	uint buflen_from_user = dwrq->length;
+#endif
+
+	WL_SCAN(("%s %s buflen_from_user %d:\n", dev->name, __FUNCTION__, dwrq->length));
+
+#if defined(SOFTAP)
+	if (ap_cfg_running) {
+		WL_TRACE(("%s: Not executed, reason -'SOFTAP is active'\n", __FUNCTION__));
+		return -EINVAL;
+	}
+#endif
+
+	if (!extra) {
+		WL_TRACE(("%s: INVALID SIOCGIWSCAN GET bad parameter\n", dev->name));
+		return -EINVAL;
+	}
+
+#if defined(CONFIG_FIRST_SCAN)
+	if (g_first_broadcast_scan < BROADCAST_SCAN_FIRST_RESULT_READY) {
+		WL_TRACE(("%s %s: first ISCAN results are NOT ready yet \n", \
+			 dev->name, __FUNCTION__));
+		return -EAGAIN;
+	}
+#endif
+
+	if ((!iscan) || (iscan->sysioc_pid < 0)) {
+		WL_ERROR(("%ssysioc_pid\n", __FUNCTION__));
+		return -EAGAIN;
+	}
+
+#if !defined(CSCAN)
+	if (g_ss_cache_ctrl.m_timer_expired) {
+		wl_iw_free_ss_cache();
+		g_ss_cache_ctrl.m_timer_expired ^= 1;
+	}
+	if (g_scan_specified_ssid) {
+		return wl_iw_get_scan(dev, info, dwrq, extra);
+	}
+	else {
+		if (g_ss_cache_ctrl.m_link_down) {
+			wl_iw_delete_bss_from_ss_cache(g_ss_cache_ctrl.m_active_bssid);
+		}
+		if (g_ss_cache_ctrl.m_prev_scan_mode || g_ss_cache_ctrl.m_cons_br_scan_cnt > 4) {
+			g_ss_cache_ctrl.m_cons_br_scan_cnt = 0;
+
+			wl_iw_reset_ss_cache();
+		}
+		g_ss_cache_ctrl.m_prev_scan_mode = g_scan_specified_ssid;
+		g_ss_cache_ctrl.m_cons_br_scan_cnt++;
+	}
+#endif
+
+	WL_TRACE(("%s: SIOCGIWSCAN GET broadcast results\n", dev->name));
+	apcnt = 0;
+	p_buf = iscan->list_hdr;
+	
+	while (p_buf != iscan->list_cur) {
+	    list = &((wl_iscan_results_t*)p_buf->iscan_buf)->results;
+
+	    counter += list->count;
+
+	    if (list->version != WL_BSS_INFO_VERSION) {
+		WL_ERROR(("%s : list->version %d != WL_BSS_INFO_VERSION\n",
+			 __FUNCTION__, list->version));
+		return -EINVAL;
+	    }
+
+	    bi = NULL;
+	    for (ii = 0; ii < list->count && apcnt < IW_MAX_AP; apcnt++, ii++) {
+		bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : list->bss_info;
+
+		if ((dtoh32(bi->length) > WLC_IW_ISCAN_MAXLEN) ||
+		    (((uintptr)bi + dtoh32(bi->length)) > ((uintptr)list + WLC_IW_ISCAN_MAXLEN))) {
+			WL_ERROR(("%s: Scan results out of bounds: %u\n",__FUNCTION__,dtoh32(bi->length)));
+			return -E2BIG;
+		}
+
+		if (event + ETHER_ADDR_LEN + bi->SSID_len + IW_EV_UINT_LEN + IW_EV_FREQ_LEN +
+			IW_EV_QUAL_LEN >= end)
+			return -E2BIG;
+
+		iwe.cmd = SIOCGIWAP;
+		iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+		memcpy(iwe.u.ap_addr.sa_data, &bi->BSSID, ETHER_ADDR_LEN);
+		event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_ADDR_LEN);
+
+		iwe.u.data.length = dtoh32(bi->SSID_len);
+		iwe.cmd = SIOCGIWESSID;
+		iwe.u.data.flags = 1;
+		event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, bi->SSID);
+
+		if (dtoh16(bi->capability) & (DOT11_CAP_ESS | DOT11_CAP_IBSS)) {
+			iwe.cmd = SIOCGIWMODE;
+			if (dtoh16(bi->capability) & DOT11_CAP_ESS)
+				iwe.u.mode = IW_MODE_INFRA;
+			else
+				iwe.u.mode = IW_MODE_ADHOC;
+			event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_UINT_LEN);
+		}
+
+		iwe.cmd = SIOCGIWFREQ;
+		channel = (bi->ctl_ch == 0) ? CHSPEC_CHANNEL(bi->chanspec) : bi->ctl_ch;
+		iwe.u.freq.m = wf_channel2mhz(channel,
+			channel <= CH_MAX_2G_CHANNEL ?
+			WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G);
+		iwe.u.freq.e = 6;
+		event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_FREQ_LEN);
+
+		iwe.cmd = IWEVQUAL;
+		iwe.u.qual.qual = rssi_to_qual(dtoh16(bi->RSSI));
+		iwe.u.qual.level = 0x100 + dtoh16(bi->RSSI);
+		iwe.u.qual.noise = 0x100 + bi->phy_noise;
+		event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_QUAL_LEN);
+
+		wl_iw_handle_scanresults_ies(&event, end, info, bi);
+
+		iwe.cmd = SIOCGIWENCODE;
+		if (dtoh16(bi->capability) & DOT11_CAP_PRIVACY)
+			iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+		else
+			iwe.u.data.flags = IW_ENCODE_DISABLED;
+		iwe.u.data.length = 0;
+		event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)event);
+
+		if (bi->rateset.count) {
+			if (event + IW_MAX_BITRATES*IW_EV_PARAM_LEN >= end)
+				return -E2BIG;
+
+			value = event + IW_EV_LCP_LEN;
+			iwe.cmd = SIOCGIWRATE;
+			
+			iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
+			for (j = 0; j < bi->rateset.count && j < IW_MAX_BITRATES; j++) {
+				iwe.u.bitrate.value = (bi->rateset.rates[j] & 0x7f) * 500000;
+				value = IWE_STREAM_ADD_VALUE(info, event, value, end, &iwe,
+					IW_EV_PARAM_LEN);
+			}
+			event = value;
+		}
+	    }
+	    p_buf = p_buf->next;
+	} 
+
+	dwrq->length = event - extra;
+	dwrq->flags = 0;	
+
+#if !defined(CSCAN)
+	wl_iw_merge_scan_cache(info, event, buflen_from_user - dwrq->length, &merged_len);
+	dwrq->length += merged_len;
+	wl_iw_run_ss_cache_timer(0);
+	wl_iw_run_ss_cache_timer(1);
+#endif /* CSCAN */
+#if defined(CONFIG_FIRST_SCAN)
+	g_first_broadcast_scan = BROADCAST_SCAN_FIRST_RESULT_CONSUMED;
+#endif
+
+	WL_TRACE(("%s return to WE %d bytes APs=%d\n", __FUNCTION__, dwrq->length, counter));
+
+	return 0;
+}
+#endif 
+
+static int
+wl_iw_set_essid(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_point *dwrq,
+	char *extra
+)
+{
+	int error;
+	wl_join_params_t join_params;
+	int join_params_size;
+
+	WL_TRACE(("%s: SIOCSIWESSID\n", dev->name));
+
+	
+	memset(&g_ssid, 0, sizeof(g_ssid));
+
+	CHECK_EXTRA_FOR_NULL(extra);
+
+	if (dwrq->length && extra) {
+#if WIRELESS_EXT > 20
+		g_ssid.SSID_len = MIN(sizeof(g_ssid.SSID), dwrq->length);
+#else
+		g_ssid.SSID_len = MIN(sizeof(g_ssid.SSID), dwrq->length-1);
+#endif
+		memcpy(g_ssid.SSID, extra, g_ssid.SSID_len);
+	} else {
+		
+		g_ssid.SSID_len = 0;
+	}
+	g_ssid.SSID_len = htod32(g_ssid.SSID_len);
+
+	memset(&join_params, 0, sizeof(join_params));
+	join_params_size = sizeof(join_params.ssid);
+
+	memcpy(&join_params.ssid.SSID, g_ssid.SSID, g_ssid.SSID_len);
+	join_params.ssid.SSID_len = htod32(g_ssid.SSID_len);
+	memcpy(&join_params.params.bssid, &ether_bcast, ETHER_ADDR_LEN);
+
+	wl_iw_ch_to_chanspec(g_wl_iw_params.target_channel, &join_params, &join_params_size);
+
+	if ((error = dev_wlc_ioctl(dev, WLC_SET_SSID, &join_params, join_params_size))) {
+		WL_ERROR(("Invalid ioctl data=%d\n", error));
+		return error;
+	}
+
+	if (g_ssid.SSID_len) {
+		WL_TRACE(("%s: join SSID=%s ch=%d\n", __FUNCTION__, \
+			g_ssid.SSID,  g_wl_iw_params.target_channel));
+	}
+	return 0;
+}
+
+static int
+wl_iw_get_essid(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_point *dwrq,
+	char *extra
+)
+{
+	wlc_ssid_t ssid;
+	int error;
+
+	WL_TRACE(("%s: SIOCGIWESSID\n", dev->name));
+
+	if (!extra)
+		return -EINVAL;
+
+	if ((error = dev_wlc_ioctl(dev, WLC_GET_SSID, &ssid, sizeof(ssid)))) {
+		WL_ERROR(("Error getting the SSID\n"));
+		return error;
+	}
+
+	ssid.SSID_len = dtoh32(ssid.SSID_len);
+
+	memcpy(extra, ssid.SSID, ssid.SSID_len);
+
+	dwrq->length = ssid.SSID_len;
+
+	dwrq->flags = 1; 
+
+	return 0;
+}
+
+static int
+wl_iw_set_nick(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_point *dwrq,
+	char *extra
+)
+{
+	wl_iw_t *iw = *(wl_iw_t **)netdev_priv(dev);
+
+	WL_TRACE(("%s: SIOCSIWNICKN\n", dev->name));
+
+	if (!extra)
+		return -EINVAL;
+
+	if (dwrq->length > sizeof(iw->nickname))
+		return -E2BIG;
+
+	memcpy(iw->nickname, extra, dwrq->length);
+	iw->nickname[dwrq->length - 1] = '\0';
+
+	return 0;
+}
+
+static int
+wl_iw_get_nick(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_point *dwrq,
+	char *extra
+)
+{
+	wl_iw_t *iw = *(wl_iw_t **)netdev_priv(dev);
+
+	WL_TRACE(("%s: SIOCGIWNICKN\n", dev->name));
+
+	if (!extra)
+		return -EINVAL;
+
+	strcpy(extra, iw->nickname);
+	dwrq->length = strlen(extra) + 1;
+
+	return 0;
+}
+
+static int wl_iw_set_rate(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra
+)
+{
+	wl_rateset_t rateset;
+	int error, rate, i, error_bg, error_a;
+
+	WL_TRACE(("%s: SIOCSIWRATE\n", dev->name));
+
+	
+	if ((error = dev_wlc_ioctl(dev, WLC_GET_CURR_RATESET, &rateset, sizeof(rateset))))
+		return error;
+
+	rateset.count = dtoh32(rateset.count);
+
+	if (vwrq->value < 0) {
+		
+		rate = rateset.rates[rateset.count - 1] & 0x7f;
+	} else if (vwrq->value < rateset.count) {
+		
+		rate = rateset.rates[vwrq->value] & 0x7f;
+	} else {
+		
+		rate = vwrq->value / 500000;
+	}
+
+	if (vwrq->fixed) {
+		
+		error_bg = dev_wlc_intvar_set(dev, "bg_rate", rate);
+		error_a = dev_wlc_intvar_set(dev, "a_rate", rate);
+
+		if (error_bg && error_a)
+			return (error_bg | error_a);
+	} else {
+		
+		
+		error_bg = dev_wlc_intvar_set(dev, "bg_rate", 0);
+		
+		error_a = dev_wlc_intvar_set(dev, "a_rate", 0);
+
+		if (error_bg && error_a)
+			return (error_bg | error_a);
+
+		
+		for (i = 0; i < rateset.count; i++)
+			if ((rateset.rates[i] & 0x7f) > rate)
+				break;
+		rateset.count = htod32(i);
+
+		
+		if ((error = dev_wlc_ioctl(dev, WLC_SET_RATESET, &rateset, sizeof(rateset))))
+			return error;
+	}
+
+	return 0;
+}
+
+static int wl_iw_get_rate(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra
+)
+{
+	int error, rate;
+
+	WL_TRACE(("%s: SIOCGIWRATE\n", dev->name));
+
+	
+	if ((error = dev_wlc_ioctl(dev, WLC_GET_RATE, &rate, sizeof(rate))))
+		return error;
+	rate = dtoh32(rate);
+	vwrq->value = rate * 500000;
+
+	return 0;
+}
+
+static int
+wl_iw_set_rts(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra
+)
+{
+	int error, rts;
+
+	WL_TRACE(("%s: SIOCSIWRTS\n", dev->name));
+
+	if (vwrq->disabled)
+		rts = DOT11_DEFAULT_RTS_LEN;
+	else if (vwrq->value < 0 || vwrq->value > DOT11_DEFAULT_RTS_LEN)
+		return -EINVAL;
+	else
+		rts = vwrq->value;
+
+	if ((error = dev_wlc_intvar_set(dev, "rtsthresh", rts)))
+		return error;
+
+	return 0;
+}
+
+static int
+wl_iw_get_rts(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra
+)
+{
+	int error, rts;
+
+	WL_TRACE(("%s: SIOCGIWRTS\n", dev->name));
+
+	if ((error = dev_wlc_intvar_get(dev, "rtsthresh", &rts)))
+		return error;
+
+	vwrq->value = rts;
+	vwrq->disabled = (rts >= DOT11_DEFAULT_RTS_LEN);
+	vwrq->fixed = 1;
+
+	return 0;
+}
+
+static int
+wl_iw_set_frag(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra
+)
+{
+	int error, frag;
+
+	WL_TRACE(("%s: SIOCSIWFRAG\n", dev->name));
+
+	if (vwrq->disabled)
+		frag = DOT11_DEFAULT_FRAG_LEN;
+	else if (vwrq->value < 0 || vwrq->value > DOT11_DEFAULT_FRAG_LEN)
+		return -EINVAL;
+	else
+		frag = vwrq->value;
+
+	if ((error = dev_wlc_intvar_set(dev, "fragthresh", frag)))
+		return error;
+
+	return 0;
+}
+
+static int
+wl_iw_get_frag(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra
+)
+{
+	int error, fragthreshold;
+
+	WL_TRACE(("%s: SIOCGIWFRAG\n", dev->name));
+
+	if ((error = dev_wlc_intvar_get(dev, "fragthresh", &fragthreshold)))
+		return error;
+
+	vwrq->value = fragthreshold;
+	vwrq->disabled = (fragthreshold >= DOT11_DEFAULT_FRAG_LEN);
+	vwrq->fixed = 1;
+
+	return 0;
+}
+
+static int
+wl_iw_set_txpow(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra
+)
+{
+	int error, disable;
+	uint16 txpwrmw;
+	WL_TRACE(("%s: SIOCSIWTXPOW\n", dev->name));
+
+	
+	disable = vwrq->disabled ? WL_RADIO_SW_DISABLE : 0;
+	disable += WL_RADIO_SW_DISABLE << 16;
+
+	disable = htod32(disable);
+	if ((error = dev_wlc_ioctl(dev, WLC_SET_RADIO, &disable, sizeof(disable))))
+		return error;
+
+	
+	if (disable & WL_RADIO_SW_DISABLE)
+		return 0;
+
+	
+	if (!(vwrq->flags & IW_TXPOW_MWATT))
+		return -EINVAL;
+
+	
+	if (vwrq->value < 0)
+		return 0;
+
+	if (vwrq->value > 0xffff) txpwrmw = 0xffff;
+	else txpwrmw = (uint16)vwrq->value;
+
+
+	error = dev_wlc_intvar_set(dev, "qtxpower", (int)(bcm_mw_to_qdbm(txpwrmw)));
+	return error;
+}
+
+static int
+wl_iw_get_txpow(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra
+)
+{
+	int error, disable, txpwrdbm;
+	uint8 result;
+
+	WL_TRACE(("%s: SIOCGIWTXPOW\n", dev->name));
+
+	if ((error = dev_wlc_ioctl(dev, WLC_GET_RADIO, &disable, sizeof(disable))) ||
+	    (error = dev_wlc_intvar_get(dev, "qtxpower", &txpwrdbm)))
+		return error;
+
+	disable = dtoh32(disable);
+	result = (uint8)(txpwrdbm & ~WL_TXPWR_OVERRIDE);
+	vwrq->value = (int32)bcm_qdbm_to_mw(result);
+	vwrq->fixed = 0;
+	vwrq->disabled = (disable & (WL_RADIO_SW_DISABLE | WL_RADIO_HW_DISABLE)) ? 1 : 0;
+	vwrq->flags = IW_TXPOW_MWATT;
+
+	return 0;
+}
+
+#if WIRELESS_EXT > 10
+static int
+wl_iw_set_retry(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra
+)
+{
+	int error, lrl, srl;
+
+	WL_TRACE(("%s: SIOCSIWRETRY\n", dev->name));
+
+	
+	if (vwrq->disabled || (vwrq->flags & IW_RETRY_LIFETIME))
+		return -EINVAL;
+
+	
+	if (vwrq->flags & IW_RETRY_LIMIT) {
+
+		
+#if WIRELESS_EXT > 20
+	if ((vwrq->flags & IW_RETRY_LONG) ||(vwrq->flags & IW_RETRY_MAX) ||
+		!((vwrq->flags & IW_RETRY_SHORT) || (vwrq->flags & IW_RETRY_MIN))) {
+#else
+	if ((vwrq->flags & IW_RETRY_MAX) || !(vwrq->flags & IW_RETRY_MIN)) {
+#endif 
+			lrl = htod32(vwrq->value);
+			if ((error = dev_wlc_ioctl(dev, WLC_SET_LRL, &lrl, sizeof(lrl))))
+				return error;
+		}
+
+		
+#if WIRELESS_EXT > 20
+	if ((vwrq->flags & IW_RETRY_SHORT) ||(vwrq->flags & IW_RETRY_MIN) ||
+		!((vwrq->flags & IW_RETRY_LONG) || (vwrq->flags & IW_RETRY_MAX))) {
+#else
+		if ((vwrq->flags & IW_RETRY_MIN) || !(vwrq->flags & IW_RETRY_MAX)) {
+#endif 
+			srl = htod32(vwrq->value);
+			if ((error = dev_wlc_ioctl(dev, WLC_SET_SRL, &srl, sizeof(srl))))
+				return error;
+		}
+	}
+	return 0;
+}
+
+static int
+wl_iw_get_retry(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra
+)
+{
+	int error, lrl, srl;
+
+	WL_TRACE(("%s: SIOCGIWRETRY\n", dev->name));
+
+	vwrq->disabled = 0;      
+
+	
+	if ((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME)
+		return -EINVAL;
+
+	
+	if ((error = dev_wlc_ioctl(dev, WLC_GET_LRL, &lrl, sizeof(lrl))) ||
+	    (error = dev_wlc_ioctl(dev, WLC_GET_SRL, &srl, sizeof(srl))))
+		return error;
+
+	lrl = dtoh32(lrl);
+	srl = dtoh32(srl);
+
+	
+	if (vwrq->flags & IW_RETRY_MAX) {
+		vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
+		vwrq->value = lrl;
+	} else {
+		vwrq->flags = IW_RETRY_LIMIT;
+		vwrq->value = srl;
+		if (srl != lrl)
+			vwrq->flags |= IW_RETRY_MIN;
+	}
+
+	return 0;
+}
+#endif 
+
+static int
+wl_iw_set_encode(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_point *dwrq,
+	char *extra
+)
+{
+	wl_wsec_key_t key;
+	int error, val, wsec;
+
+	WL_TRACE(("%s: SIOCSIWENCODE\n", dev->name));
+
+	memset(&key, 0, sizeof(key));
+
+	if ((dwrq->flags & IW_ENCODE_INDEX) == 0) {
+		
+		for (key.index = 0; key.index < DOT11_MAX_DEFAULT_KEYS; key.index++) {
+			val = htod32(key.index);
+			if ((error = dev_wlc_ioctl(dev, WLC_GET_KEY_PRIMARY, &val, sizeof(val))))
+				return error;
+			val = dtoh32(val);
+			if (val)
+				break;
+		}
+		
+		if (key.index == DOT11_MAX_DEFAULT_KEYS)
+			key.index = 0;
+	} else {
+		key.index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+		if (key.index >= DOT11_MAX_DEFAULT_KEYS)
+			return -EINVAL;
+	}
+
+	
+	if (!extra || !dwrq->length || (dwrq->flags & IW_ENCODE_NOKEY)) {
+		
+		val = htod32(key.index);
+		if ((error = dev_wlc_ioctl(dev, WLC_SET_KEY_PRIMARY, &val, sizeof(val))))
+			return error;
+	} else {
+		key.len = dwrq->length;
+
+		if (dwrq->length > sizeof(key.data))
+			return -EINVAL;
+
+		memcpy(key.data, extra, dwrq->length);
+
+		key.flags = WL_PRIMARY_KEY;
+		switch (key.len) {
+		case WEP1_KEY_SIZE:
+			key.algo = CRYPTO_ALGO_WEP1;
+			break;
+		case WEP128_KEY_SIZE:
+			key.algo = CRYPTO_ALGO_WEP128;
+			break;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14)
+		case TKIP_KEY_SIZE:
+			key.algo = CRYPTO_ALGO_TKIP;
+			break;
+#endif
+		case AES_KEY_SIZE:
+			key.algo = CRYPTO_ALGO_AES_CCM;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		
+		swap_key_from_BE(&key);
+		if ((error = dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key))))
+			return error;
+	}
+
+	
+	val = (dwrq->flags & IW_ENCODE_DISABLED) ? 0 : WEP_ENABLED;
+
+	if ((error = dev_wlc_intvar_get(dev, "wsec", &wsec)))
+		return error;
+
+	wsec  &= ~(WEP_ENABLED);
+	wsec |= val;
+
+	if ((error = dev_wlc_intvar_set(dev, "wsec", wsec)))
+		return error;
+
+	
+	val = (dwrq->flags & IW_ENCODE_RESTRICTED) ? 1 : 0;
+	val = htod32(val);
+	if ((error = dev_wlc_ioctl(dev, WLC_SET_AUTH, &val, sizeof(val))))
+		return error;
+
+	return 0;
+}
+
+static int
+wl_iw_get_encode(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_point *dwrq,
+	char *extra
+)
+{
+	wl_wsec_key_t key;
+	int error, val, wsec, auth;
+
+	WL_TRACE(("%s: SIOCGIWENCODE\n", dev->name));
+
+	
+	bzero(&key, sizeof(wl_wsec_key_t));
+
+	if ((dwrq->flags & IW_ENCODE_INDEX) == 0) {
+		
+		for (key.index = 0; key.index < DOT11_MAX_DEFAULT_KEYS; key.index++) {
+			val = key.index;
+			if ((error = dev_wlc_ioctl(dev, WLC_GET_KEY_PRIMARY, &val, sizeof(val))))
+				return error;
+			val = dtoh32(val);
+			if (val)
+				break;
+		}
+	} else
+		key.index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+
+	if (key.index >= DOT11_MAX_DEFAULT_KEYS)
+		key.index = 0;
+
+	
+
+	if ((error = dev_wlc_ioctl(dev, WLC_GET_WSEC, &wsec, sizeof(wsec))) ||
+	    (error = dev_wlc_ioctl(dev, WLC_GET_AUTH, &auth, sizeof(auth))))
+		return error;
+
+	swap_key_to_BE(&key);
+
+	wsec = dtoh32(wsec);
+	auth = dtoh32(auth);
+	
+	dwrq->length = MIN(DOT11_MAX_KEY_SIZE, key.len);
+
+	
+	dwrq->flags = key.index + 1;
+	if (!(wsec & (WEP_ENABLED | TKIP_ENABLED | AES_ENABLED))) {
+		
+		dwrq->flags |= IW_ENCODE_DISABLED;
+	}
+	if (auth) {
+		
+		dwrq->flags |= IW_ENCODE_RESTRICTED;
+	}
+
+	
+	if (dwrq->length && extra)
+		memcpy(extra, key.data, dwrq->length);
+
+	return 0;
+}
+
+static int
+wl_iw_set_power(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra
+)
+{
+	int error, pm;
+
+	WL_TRACE(("%s: SIOCSIWPOWER\n", dev->name));
+
+	pm = vwrq->disabled ? PM_OFF : PM_MAX;
+
+	pm = htod32(pm);
+	if ((error = dev_wlc_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm))))
+		return error;
+
+	return 0;
+}
+
+static int
+wl_iw_get_power(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra
+)
+{
+	int error, pm;
+
+	WL_TRACE(("%s: SIOCGIWPOWER\n", dev->name));
+
+	if ((error = dev_wlc_ioctl(dev, WLC_GET_PM, &pm, sizeof(pm))))
+		return error;
+
+	pm = dtoh32(pm);
+	vwrq->disabled = pm ? 0 : 1;
+	vwrq->flags = IW_POWER_ALL_R;
+
+	return 0;
+}
+
+#if WIRELESS_EXT > 17
+static int
+wl_iw_set_wpaie(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_point *iwp,
+	char *extra
+)
+{
+	uchar buf[WLC_IOCTL_SMLEN] = {0};
+	uchar *p = buf;
+	int wapi_ie_size;
+
+	WL_TRACE(("%s: SIOCSIWGENIE\n", dev->name));
+
+	CHECK_EXTRA_FOR_NULL(extra);
+
+	if (extra[0] == DOT11_MNG_WAPI_ID)
+	{
+		wapi_ie_size = iwp->length;
+		memcpy(p, extra, iwp->length);
+		dev_wlc_bufvar_set(dev, "wapiie", buf, wapi_ie_size);
+	}
+	else
+		dev_wlc_bufvar_set(dev, "wpaie", extra, iwp->length);
+
+	return 0;
+}
+
+static int
+wl_iw_get_wpaie(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_point *iwp,
+	char *extra
+)
+{
+	WL_TRACE(("%s: SIOCGIWGENIE\n", dev->name));
+	iwp->length = 64;
+	dev_wlc_bufvar_get(dev, "wpaie", extra, iwp->length);
+	return 0;
+}
+
+static int
+wl_iw_set_encodeext(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_point *dwrq,
+	char *extra
+)
+{
+	wl_wsec_key_t key;
+	int error;
+	struct iw_encode_ext *iwe;
+
+	WL_WSEC(("%s: SIOCSIWENCODEEXT\n", dev->name));
+
+	CHECK_EXTRA_FOR_NULL(extra);
+
+	memset(&key, 0, sizeof(key));
+	iwe = (struct iw_encode_ext *)extra;
+
+	
+	if (dwrq->flags & IW_ENCODE_DISABLED) {
+
+	}
+
+	
+	key.index = 0;
+	if (dwrq->flags & IW_ENCODE_INDEX)
+		key.index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+
+	key.len = iwe->key_len;
+
+	
+	if (!ETHER_ISMULTI(iwe->addr.sa_data))
+		bcopy((void *)&iwe->addr.sa_data, (char *)&key.ea, ETHER_ADDR_LEN);
+
+	
+	if (key.len == 0) {
+		if (iwe->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
+			WL_WSEC(("Changing the the primary Key to %d\n", key.index));
+			
+			key.index = htod32(key.index);
+			error = dev_wlc_ioctl(dev, WLC_SET_KEY_PRIMARY,
+				&key.index, sizeof(key.index));
+			if (error)
+				return error;
+		}
+		
+		else {
+			swap_key_from_BE(&key);
+			dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key));
+		}
+	}
+	else {
+		if (iwe->key_len > sizeof(key.data))
+			return -EINVAL;
+
+		WL_WSEC(("Setting the key index %d\n", key.index));
+		if (iwe->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
+			WL_WSEC(("key is a Primary Key\n"));
+			key.flags = WL_PRIMARY_KEY;
+		}
+
+		bcopy((void *)iwe->key, key.data, iwe->key_len);
+
+		if (iwe->alg == IW_ENCODE_ALG_TKIP) {
+			uint8 keybuf[8];
+			bcopy(&key.data[24], keybuf, sizeof(keybuf));
+			bcopy(&key.data[16], &key.data[24], sizeof(keybuf));
+			bcopy(keybuf, &key.data[16], sizeof(keybuf));
+		}
+
+		
+		if (iwe->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) {
+			uchar *ivptr;
+			ivptr = (uchar *)iwe->rx_seq;
+			key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
+				(ivptr[3] << 8) | ivptr[2];
+			key.rxiv.lo = (ivptr[1] << 8) | ivptr[0];
+			key.iv_initialized = TRUE;
+		}
+
+		switch (iwe->alg) {
+			case IW_ENCODE_ALG_NONE:
+				key.algo = CRYPTO_ALGO_OFF;
+				break;
+			case IW_ENCODE_ALG_WEP:
+				if (iwe->key_len == WEP1_KEY_SIZE)
+					key.algo = CRYPTO_ALGO_WEP1;
+				else
+					key.algo = CRYPTO_ALGO_WEP128;
+				break;
+			case IW_ENCODE_ALG_TKIP:
+				key.algo = CRYPTO_ALGO_TKIP;
+				break;
+			case IW_ENCODE_ALG_CCMP:
+				key.algo = CRYPTO_ALGO_AES_CCM;
+				break;
+			case IW_ENCODE_ALG_SM4:
+				key.algo = CRYPTO_ALGO_SMS4;
+				if (iwe->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
+					key.flags &= ~WL_PRIMARY_KEY;
+				}
+				break;
+			default:
+				break;
+		}
+		swap_key_from_BE(&key);
+
+		dhd_wait_pend8021x(dev);
+
+		error = dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key));
+		if (error)
+			return error;
+	}
+	return 0;
+}
+
+#if WIRELESS_EXT > 17
+#ifdef BCMWPA2
+struct {
+	pmkid_list_t pmkids;
+	pmkid_t foo[MAXPMKID-1];
+} pmkid_list;
+
+static int
+wl_iw_set_pmksa(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra
+)
+{
+	struct iw_pmksa *iwpmksa;
+	uint i;
+	int ret = 0;
+	char eabuf[ETHER_ADDR_STR_LEN];
+
+	WL_WSEC(("%s: SIOCSIWPMKSA\n", dev->name));
+	CHECK_EXTRA_FOR_NULL(extra);
+
+	iwpmksa = (struct iw_pmksa *)extra;
+	bzero((char *)eabuf, ETHER_ADDR_STR_LEN);
+
+	if (iwpmksa->cmd == IW_PMKSA_FLUSH) {
+		WL_WSEC(("wl_iw_set_pmksa - IW_PMKSA_FLUSH\n"));
+		bzero((char *)&pmkid_list, sizeof(pmkid_list));
+	}
+
+	else if (iwpmksa->cmd == IW_PMKSA_REMOVE) {
+		{
+			pmkid_list_t pmkid, *pmkidptr;
+			uint j;
+			pmkidptr = &pmkid;
+
+			bcopy(&iwpmksa->bssid.sa_data[0], &pmkidptr->pmkid[0].BSSID, ETHER_ADDR_LEN);
+			bcopy(&iwpmksa->pmkid[0], &pmkidptr->pmkid[0].PMKID, WPA2_PMKID_LEN);
+
+			WL_WSEC(("wl_iw_set_pmksa,IW_PMKSA_REMOVE - PMKID: %s = ",
+				bcm_ether_ntoa(&pmkidptr->pmkid[0].BSSID,
+				eabuf)));
+			for (j = 0; j < WPA2_PMKID_LEN; j++)
+				WL_WSEC(("%02x ", pmkidptr->pmkid[0].PMKID[j]));
+			WL_WSEC(("\n"));
+		}
+
+		for (i = 0; i < pmkid_list.pmkids.npmkid; i++)
+			if (!bcmp(&iwpmksa->bssid.sa_data[0], &pmkid_list.pmkids.pmkid[i].BSSID,
+				ETHER_ADDR_LEN))
+				break;
+
+		if ((pmkid_list.pmkids.npmkid > 0) && (i < pmkid_list.pmkids.npmkid)) {
+			bzero(&pmkid_list.pmkids.pmkid[i], sizeof(pmkid_t));
+			for (; i < (pmkid_list.pmkids.npmkid - 1); i++) {
+				bcopy(&pmkid_list.pmkids.pmkid[i+1].BSSID,
+					&pmkid_list.pmkids.pmkid[i].BSSID,
+					ETHER_ADDR_LEN);
+				bcopy(&pmkid_list.pmkids.pmkid[i+1].PMKID,
+					&pmkid_list.pmkids.pmkid[i].PMKID,
+					WPA2_PMKID_LEN);
+			}
+			pmkid_list.pmkids.npmkid--;
+		}
+		else
+			ret = -EINVAL;
+	}
+
+	else if (iwpmksa->cmd == IW_PMKSA_ADD) {
+		for (i = 0; i < pmkid_list.pmkids.npmkid; i++)
+			if (!bcmp(&iwpmksa->bssid.sa_data[0], &pmkid_list.pmkids.pmkid[i].BSSID,
+				ETHER_ADDR_LEN))
+				break;
+		if (i < MAXPMKID) {
+			bcopy(&iwpmksa->bssid.sa_data[0],
+				&pmkid_list.pmkids.pmkid[i].BSSID,
+				ETHER_ADDR_LEN);
+			bcopy(&iwpmksa->pmkid[0], &pmkid_list.pmkids.pmkid[i].PMKID,
+				WPA2_PMKID_LEN);
+			if (i == pmkid_list.pmkids.npmkid)
+				pmkid_list.pmkids.npmkid++;
+		}
+		else
+			ret = -EINVAL;
+
+		{
+			uint j;
+			uint k;
+			k = pmkid_list.pmkids.npmkid;
+			WL_WSEC(("wl_iw_set_pmksa,IW_PMKSA_ADD - PMKID: %s = ",
+				bcm_ether_ntoa(&pmkid_list.pmkids.pmkid[k].BSSID,
+				eabuf)));
+			for (j = 0; j < WPA2_PMKID_LEN; j++)
+				WL_WSEC(("%02x ", pmkid_list.pmkids.pmkid[k].PMKID[j]));
+			WL_WSEC(("\n"));
+		}
+	}
+	WL_WSEC(("PRINTING pmkid LIST - No of elements %d, ret = %d\n", pmkid_list.pmkids.npmkid, ret));
+	for (i = 0; i < pmkid_list.pmkids.npmkid; i++) {
+		uint j;
+		WL_WSEC(("PMKID[%d]: %s = ", i,
+			bcm_ether_ntoa(&pmkid_list.pmkids.pmkid[i].BSSID,
+			eabuf)));
+		for (j = 0; j < WPA2_PMKID_LEN; j++)
+			WL_WSEC(("%02x ", pmkid_list.pmkids.pmkid[i].PMKID[j]));
+		WL_WSEC(("\n"));
+	}
+	WL_WSEC(("\n"));
+
+	if (!ret)
+		ret = dev_wlc_bufvar_set(dev, "pmkid_info", (char *)&pmkid_list, sizeof(pmkid_list));
+	return ret;
+}
+#endif 
+#endif 
+
+static int
+wl_iw_get_encodeext(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra
+)
+{
+	WL_WSEC(("%s: SIOCGIWENCODEEXT\n", dev->name));
+	return 0;
+}
+
+static int
+wl_iw_set_wpaauth(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra
+)
+{
+	int error = 0;
+	int paramid;
+	int paramval;
+	int val = 0;
+	wl_iw_t *iw = *(wl_iw_t **)netdev_priv(dev);
+
+	WL_WSEC(("%s: SIOCSIWAUTH\n", dev->name));
+
+#if defined(SOFTAP)
+	if (ap_cfg_running) {
+		WL_TRACE(("%s: Not executed, reason -'SOFTAP is active'\n", __FUNCTION__));
+		return 0;
+	}
+#endif
+
+	paramid = vwrq->flags & IW_AUTH_INDEX;
+	paramval = vwrq->value;
+
+	WL_WSEC(("%s: SIOCSIWAUTH, paramid = 0x%0x, paramval = 0x%0x\n",
+		dev->name, paramid, paramval));
+
+	switch (paramid) {
+	case IW_AUTH_WPA_VERSION:
+		
+		if (paramval & IW_AUTH_WPA_VERSION_DISABLED)
+			val = WPA_AUTH_DISABLED;
+		else if (paramval & (IW_AUTH_WPA_VERSION_WPA))
+			val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
+#ifdef BCMWPA2
+		else if (paramval & IW_AUTH_WPA_VERSION_WPA2)
+			val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
+#endif 
+		else if (paramval & IW_AUTH_WAPI_VERSION_1)
+			val = WPA_AUTH_WAPI;
+		WL_WSEC(("%s: %d: setting wpa_auth to 0x%0x\n", __FUNCTION__, __LINE__, val));
+		if ((error = dev_wlc_intvar_set(dev, "wpa_auth", val)))
+			return error;
+		break;
+	case IW_AUTH_CIPHER_PAIRWISE:
+	case IW_AUTH_CIPHER_GROUP:
+		
+		
+		if (paramval & (IW_AUTH_CIPHER_WEP40 | IW_AUTH_CIPHER_WEP104))
+			val = 	WEP_ENABLED;
+		if (paramval & IW_AUTH_CIPHER_TKIP)
+			val = TKIP_ENABLED;
+		if (paramval & IW_AUTH_CIPHER_CCMP)
+			val = AES_ENABLED;
+		if (paramval & IW_AUTH_CIPHER_SMS4)
+			val = SMS4_ENABLED;
+
+		if (paramid == IW_AUTH_CIPHER_PAIRWISE) {
+			iw->pwsec = val;
+			val |= iw->gwsec;
+		}
+		else {
+			iw->gwsec = val;
+			val |= iw->pwsec;
+		}
+
+		if (iw->privacy_invoked && !val) {
+			WL_WSEC(("%s: %s: 'Privacy invoked' TRUE but clearing wsec, assuming "
+				"we're a WPS enrollee\n", dev->name, __FUNCTION__));
+			if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", TRUE))) {
+				WL_ERROR(("Failed to set iovar is_WPS_enrollee\n"));
+				return error;
+			}
+		} else if (val) {
+			if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", FALSE))) {
+				WL_ERROR(("Failed to clear iovar is_WPS_enrollee\n"));
+				return error;
+			}
+		}
+
+		if ((error = dev_wlc_intvar_set(dev, "wsec", val))) {
+			WL_ERROR(("Failed to set 'wsec'iovar\n"));
+			return error;
+		}
+
+		break;
+
+	case IW_AUTH_KEY_MGMT:
+		if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val))) {
+			WL_ERROR(("Failed to get 'wpa_auth'iovar\n"));
+			return error;
+		}
+
+		if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
+			if (paramval & IW_AUTH_KEY_MGMT_PSK)
+				val = WPA_AUTH_PSK;
+			else
+				val = WPA_AUTH_UNSPECIFIED;
+		}
+#ifdef BCMWPA2
+		else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
+			if (paramval & IW_AUTH_KEY_MGMT_PSK)
+				val = WPA2_AUTH_PSK;
+			else
+				val = WPA2_AUTH_UNSPECIFIED;
+		}
+#endif 
+		if (paramval & (IW_AUTH_KEY_MGMT_WAPI_PSK | IW_AUTH_KEY_MGMT_WAPI_CERT))
+			val = WPA_AUTH_WAPI;
+		WL_WSEC(("%s: %d: setting wpa_auth to %d\n", __FUNCTION__, __LINE__, val));
+		if ((error = dev_wlc_intvar_set(dev, "wpa_auth", val))) {
+			WL_ERROR(("Failed to set 'wpa_auth'iovar\n"));
+			return error;
+		}
+
+		break;
+	case IW_AUTH_TKIP_COUNTERMEASURES:
+		if ((error = dev_wlc_bufvar_set(dev, "tkip_countermeasures", \
+						(char *)&paramval, sizeof(paramval))))
+			WL_WSEC(("%s: tkip_countermeasures failed %d\n", __FUNCTION__, error));
+		break;
+
+	case IW_AUTH_80211_AUTH_ALG:
+		
+		WL_WSEC(("Setting the D11auth %d\n", paramval));
+		if (paramval == IW_AUTH_ALG_OPEN_SYSTEM)
+			val = 0;
+		else if (paramval == IW_AUTH_ALG_SHARED_KEY)
+			val = 1;
+		else if (paramval == (IW_AUTH_ALG_OPEN_SYSTEM | IW_AUTH_ALG_SHARED_KEY))
+			val = 2;
+		else
+			error = 1;
+		if (!error && (error = dev_wlc_intvar_set(dev, "auth", val)))
+			return error;
+		break;
+
+	case IW_AUTH_WPA_ENABLED:
+		if (paramval == 0) {
+			iw->pwsec = 0;
+			iw->gwsec = 0;
+			if ((error = dev_wlc_intvar_get(dev, "wsec", &val))) {
+				WL_ERROR(("Failed to get 'wsec'iovar\n"));
+				return error;
+			}
+			if (val & (TKIP_ENABLED | AES_ENABLED)) {
+				val &= ~(TKIP_ENABLED | AES_ENABLED);
+				dev_wlc_intvar_set(dev, "wsec", val);
+			}
+			val = 0;
+
+			WL_INFORM(("%s: %d: setting wpa_auth to %d\n",
+				__FUNCTION__, __LINE__, val));
+			error = dev_wlc_intvar_set(dev, "wpa_auth", 0);
+			if (error)
+				WL_ERROR(("Failed to set 'wpa_auth'iovar\n"));
+			return error;
+		}
+
+		
+		break;
+
+	case IW_AUTH_DROP_UNENCRYPTED:
+		error = dev_wlc_bufvar_set(dev, "wsec_restrict", \
+				   (char *)&paramval, sizeof(paramval));
+		if (error)
+			WL_ERROR(("%s: wsec_restrict %d\n", __FUNCTION__, error));
+		break;
+
+	case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+		error = dev_wlc_bufvar_set(dev, "rx_unencrypted_eapol", \
+				   (char *)&paramval, sizeof(paramval));
+		if (error)
+			WL_WSEC(("%s: rx_unencrypted_eapol %d\n", __FUNCTION__, error));
+		break;
+
+#if WIRELESS_EXT > 17
+	case IW_AUTH_ROAMING_CONTROL:
+		WL_INFORM(("%s: IW_AUTH_ROAMING_CONTROL\n", __FUNCTION__));
+		
+		break;
+	case IW_AUTH_PRIVACY_INVOKED: {
+		int wsec;
+
+		if (paramval == 0) {
+			iw->privacy_invoked = FALSE;
+			if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", FALSE))) {
+				WL_WSEC(("Failed to clear iovar is_WPS_enrollee\n"));
+				return error;
+			}
+		} else {
+			iw->privacy_invoked = TRUE;
+			if ((error = dev_wlc_intvar_get(dev, "wsec", &wsec)))
+				return error;
+
+			if (!(IW_WSEC_ENABLED(wsec))) {
+
+				if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", TRUE))) {
+					WL_WSEC(("Failed to set iovar is_WPS_enrollee\n"));
+					return error;
+				}
+			} else {
+				if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", FALSE))) {
+					WL_WSEC(("Failed to clear iovar is_WPS_enrollee\n"));
+					return error;
+				}
+			}
+		}
+		break;
+	}
+#endif
+	case IW_AUTH_WAPI_ENABLED:
+		if ((error = dev_wlc_intvar_get(dev, "wsec", &val)))
+			return error;
+		if (paramval) {
+			val |= SMS4_ENABLED;
+			if ((error = dev_wlc_intvar_set(dev, "wsec", val))) {
+				WL_ERROR(("%s: setting wsec to 0x%0x returned error %d\n",
+					__FUNCTION__, val, error));
+				return error;
+			}
+			if ((error = dev_wlc_intvar_set(dev, "wpa_auth", WPA_AUTH_WAPI))) {
+				WL_ERROR(("%s: setting wpa_auth(WPA_AUTH_WAPI) returned %d\n",
+					__FUNCTION__, error));
+				return error;
+			}
+		}
+
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+#ifdef BCMWPA2
+#define VAL_PSK(_val) (((_val) & WPA_AUTH_PSK) || ((_val) & WPA2_AUTH_PSK))
+#else
+#define VAL_PSK(_val) (((_val) & WPA_AUTH_PSK))
+#endif 
+
+static int
+wl_iw_get_wpaauth(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra
+)
+{
+	int error;
+	int paramid;
+	int paramval = 0;
+	int val;
+	wl_iw_t *iw = *(wl_iw_t **)netdev_priv(dev);
+
+	WL_TRACE(("%s: SIOCGIWAUTH\n", dev->name));
+
+	paramid = vwrq->flags & IW_AUTH_INDEX;
+
+	switch (paramid) {
+	case IW_AUTH_WPA_VERSION:
+		
+		if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val)))
+			return error;
+		if (val & (WPA_AUTH_NONE | WPA_AUTH_DISABLED))
+			paramval = IW_AUTH_WPA_VERSION_DISABLED;
+		else if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED))
+			paramval = IW_AUTH_WPA_VERSION_WPA;
+#ifdef BCMWPA2
+		else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED))
+			paramval = IW_AUTH_WPA_VERSION_WPA2;
+#endif 
+		break;
+	case IW_AUTH_CIPHER_PAIRWISE:
+	case IW_AUTH_CIPHER_GROUP:
+		if (paramid == IW_AUTH_CIPHER_PAIRWISE)
+			val = iw->pwsec;
+		else
+			val = iw->gwsec;
+
+		paramval = 0;
+		if (val) {
+			if (val & WEP_ENABLED)
+				paramval |= (IW_AUTH_CIPHER_WEP40 | IW_AUTH_CIPHER_WEP104);
+			if (val & TKIP_ENABLED)
+				paramval |= (IW_AUTH_CIPHER_TKIP);
+			if (val & AES_ENABLED)
+				paramval |= (IW_AUTH_CIPHER_CCMP);
+		}
+		else
+			paramval = IW_AUTH_CIPHER_NONE;
+		break;
+	case IW_AUTH_KEY_MGMT:
+		
+		if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val)))
+			return error;
+		if (VAL_PSK(val))
+			paramval = IW_AUTH_KEY_MGMT_PSK;
+		else
+			paramval = IW_AUTH_KEY_MGMT_802_1X;
+
+		break;
+	case IW_AUTH_TKIP_COUNTERMEASURES:
+		error = dev_wlc_bufvar_get(dev, "tkip_countermeasures", \
+							(char *)&paramval, sizeof(paramval));
+		if (error)
+			WL_ERROR(("%s get tkip_countermeasures %d\n", __FUNCTION__, error));
+		break;
+
+	case IW_AUTH_DROP_UNENCRYPTED:
+		error = dev_wlc_bufvar_get(dev, "wsec_restrict", \
+					   (char *)&paramval, sizeof(paramval));
+		if (error)
+			WL_ERROR(("%s get wsec_restrict %d\n", __FUNCTION__, error));
+		break;
+
+	case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+		error = dev_wlc_bufvar_get(dev, "rx_unencrypted_eapol", \
+						   (char *)&paramval, sizeof(paramval));
+		if (error)
+			WL_ERROR(("%s get rx_unencrypted_eapol %d\n", __FUNCTION__, error));
+		break;
+
+	case IW_AUTH_80211_AUTH_ALG:
+		
+		if ((error = dev_wlc_intvar_get(dev, "auth", &val)))
+			return error;
+		if (!val)
+			paramval = IW_AUTH_ALG_OPEN_SYSTEM;
+		else
+			paramval = IW_AUTH_ALG_SHARED_KEY;
+		break;
+	case IW_AUTH_WPA_ENABLED:
+		if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val)))
+			return error;
+		if (val)
+			paramval = TRUE;
+		else
+			paramval = FALSE;
+		break;
+#if WIRELESS_EXT > 17
+	case IW_AUTH_ROAMING_CONTROL:
+		WL_ERROR(("%s: IW_AUTH_ROAMING_CONTROL\n", __FUNCTION__));
+		
+		break;
+	case IW_AUTH_PRIVACY_INVOKED:
+		paramval = iw->privacy_invoked;
+		break;
+#endif 
+	}
+	vwrq->value = paramval;
+	return 0;
+}
+#endif
+
+
+#ifdef SOFTAP
+
+static int ap_macmode = MACLIST_MODE_DISABLED;
+static struct mflist ap_black_list;
+static int
+wl_iw_parse_wep(char *keystr, wl_wsec_key_t *key)
+{
+	char hex[] = "XX";
+	unsigned char *data = key->data;
+
+	switch (strlen(keystr)) {
+	case 5:
+	case 13:
+	case 16:
+		key->len = strlen(keystr);
+		memcpy(data, keystr, key->len + 1);
+		break;
+	case 12:
+	case 28:
+	case 34:
+	case 66:
+		if (!strnicmp(keystr, "0x", 2))
+			keystr += 2;
+		else
+			return -1;
+	case 10:
+	case 26:
+	case 32:
+	case 64:
+		key->len = strlen(keystr) / 2;
+		while (*keystr) {
+			strncpy(hex, keystr, 2);
+			*data++ = (char) bcm_strtoul(hex, NULL, 16);
+			keystr += 2;
+		}
+		break;
+	default:
+		return -1;
+	}
+
+	switch (key->len) {
+	case 5:
+		key->algo = CRYPTO_ALGO_WEP1;
+		break;
+	case 13:
+		key->algo = CRYPTO_ALGO_WEP128;
+		break;
+	case 16:
+		key->algo = CRYPTO_ALGO_AES_CCM;
+		break;
+	case 32:
+		key->algo = CRYPTO_ALGO_TKIP;
+		break;
+	default:
+		return -1;
+	}
+
+	key->flags |= WL_PRIMARY_KEY;
+
+	return 0;
+}
+
+#ifdef EXT_WPA_CRYPTO
+#define SHA1HashSize 20
+extern void pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len,
+				int iterations, u8 *buf, size_t buflen);
+
+#else
+
+#define SHA1HashSize 20
+int pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len,
+				int iterations, u8 *buf, size_t buflen)
+{
+	WL_ERROR(("WARNING: %s is not implemented !!!\n", __FUNCTION__));
+	return -1;
+}
+
+#endif 
+
+
+int dev_iw_write_cfg1_bss_var(struct net_device *dev, int val)
+{
+	struct {
+		int cfg;
+		int val;
+	} bss_setbuf;
+
+	int bss_set_res;
+	char smbuf[WLC_IOCTL_SMLEN];
+	memset(smbuf, 0, sizeof(smbuf));
+
+	bss_setbuf.cfg = 1;
+	bss_setbuf.val = val;
+
+	bss_set_res = dev_iw_iovar_setbuf(dev, "bss",
+		&bss_setbuf, sizeof(bss_setbuf), smbuf, sizeof(smbuf));
+	WL_TRACE(("%s: bss_set_result:%d set with %d\n", __FUNCTION__, bss_set_res, val));
+
+	return bss_set_res;
+}
+
+
+int dev_iw_read_cfg1_bss_var(struct net_device *dev, int *val)
+{
+	int bsscfg_idx = 1;
+	int bss_set_res;
+	char smbuf[WLC_IOCTL_SMLEN];
+	memset(smbuf, 0, sizeof(smbuf));
+
+	bss_set_res = dev_iw_iovar_getbuf(dev, "bss", \
+		 &bsscfg_idx, sizeof(bsscfg_idx), smbuf, sizeof(smbuf));
+	*val = *(int*)smbuf;
+	*val = dtoh32(*val);
+	WL_TRACE(("%s: status=%d bss_get_result=%d\n", __FUNCTION__, bss_set_res, *val));
+	return bss_set_res;
+}
+
+
+#ifndef AP_ONLY
+static int wl_bssiovar_mkbuf(
+			const char *iovar,
+			int bssidx,
+			void *param,
+			int paramlen,
+			void *bufptr,
+			int buflen,
+			int *perr)
+{
+	const char *prefix = "bsscfg:";
+	int8 *p;
+	uint prefixlen;
+	uint namelen;
+	uint iolen;
+
+	prefixlen = strlen(prefix);
+	namelen = strlen(iovar) + 1;
+	iolen = prefixlen + namelen + sizeof(int) + paramlen;
+
+	if (buflen < 0 || iolen > (uint)buflen) {
+		*perr = BCME_BUFTOOSHORT;
+		return 0;
+	}
+
+	p = (int8 *)bufptr;
+
+	memcpy(p, prefix, prefixlen);
+	p += prefixlen;
+
+	memcpy(p, iovar, namelen);
+	p += namelen;
+
+	bssidx = htod32(bssidx);
+	memcpy(p, &bssidx, sizeof(int32));
+	p += sizeof(int32);
+
+	if (paramlen)
+		memcpy(p, param, paramlen);
+
+	*perr = 0;
+	return iolen;
+}
+#endif 
+
+
+int get_user_params(char *user_params, struct iw_point *dwrq)
+{
+	int ret = 0;
+
+	if (copy_from_user(user_params, dwrq->pointer, dwrq->length)) {
+		WL_ERROR(("\n%s: no user params: uptr:%p, ulen:%d\n",
+			__FUNCTION__, dwrq->pointer, dwrq->length));
+		return -EFAULT;
+	}
+
+	WL_TRACE(("\n%s: iwpriv user params:%s\n", __FUNCTION__, user_params));
+
+	return ret;
+}
+
+
+#define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base))
+
+#if defined(CSCAN)
+
+static int
+wl_iw_combined_scan_set(struct net_device *dev, wlc_ssid_t* ssids_local, int nssid, int nchan)
+{
+	int params_size = WL_SCAN_PARAMS_FIXED_SIZE + WL_NUMCHANNELS * sizeof(uint16);
+	int err = 0;
+	char *p;
+	int i;
+	iscan_info_t *iscan = g_iscan;
+
+	WL_SCAN(("%s nssid=%d nchan=%d\n", __FUNCTION__, nssid, nchan));
+
+	if ((!dev) && (!g_iscan) && (!iscan->iscan_ex_params_p)) {
+		WL_ERROR(("%s error exit\n", __FUNCTION__));
+		err = -1;
+		goto exit;
+	}
+
+#ifdef PNO_SUPPORT
+	if  (dhd_dev_get_pno_status(dev)) {
+		WL_ERROR(("%s: Scan called when PNO is active\n", __FUNCTION__));
+	}
+#endif
+
+	params_size += WL_SCAN_PARAMS_SSID_MAX * sizeof(wlc_ssid_t);
+
+	if (nssid > 0) {
+		i = OFFSETOF(wl_scan_params_t, channel_list) + nchan * sizeof(uint16);
+		i = ROUNDUP(i, sizeof(uint32));
+		if (i + nssid * sizeof(wlc_ssid_t) > params_size) {
+			printf("additional ssids exceed params_size\n");
+			err = -1;
+			goto exit;
+		}
+
+		p = ((char*)&iscan->iscan_ex_params_p->params) + i;
+		memcpy(p, ssids_local, nssid * sizeof(wlc_ssid_t));
+		p += nssid * sizeof(wlc_ssid_t);
+	} else {
+		p = (char*)iscan->iscan_ex_params_p->params.channel_list + nchan * sizeof(uint16);
+	}
+
+	iscan->iscan_ex_params_p->params.channel_num = \
+		htod32((nssid << WL_SCAN_PARAMS_NSSID_SHIFT) | \
+					(nchan & WL_SCAN_PARAMS_COUNT_MASK));
+
+	nssid = \
+	(uint)((iscan->iscan_ex_params_p->params.channel_num >> WL_SCAN_PARAMS_NSSID_SHIFT) & \
+		               WL_SCAN_PARAMS_COUNT_MASK);
+
+	params_size = (int) (p - (char*)iscan->iscan_ex_params_p + nssid * sizeof(wlc_ssid_t));
+	iscan->iscan_ex_param_size = params_size;
+
+	iscan->list_cur = iscan->list_hdr;
+	iscan->iscan_state = ISCAN_STATE_SCANING;
+	wl_iw_set_event_mask(dev);
+	mod_timer(&iscan->timer, jiffies + iscan->timer_ms*HZ/1000);
+
+	iscan->timer_on = 1;
+
+#ifdef SCAN_DUMP
+	{
+		int i;
+		WL_SCAN(("\n### List of SSIDs to scan ###\n"));
+		for (i = 0; i < nssid; i++) {
+			if (!ssids_local[i].SSID_len)
+				WL_SCAN(("%d: Broadcast scan\n", i));
+			else
+			WL_SCAN(("%d: scan  for  %s size =%d\n", i, \
+				ssids_local[i].SSID, ssids_local[i].SSID_len));
+		}
+		WL_SCAN(("### List of channels to scan ###\n"));
+		for (i = 0; i < nchan; i++)
+		{
+			WL_SCAN(("%d ", iscan->iscan_ex_params_p->params.channel_list[i]));
+		}
+		WL_SCAN(("\nnprobes=%d\n", iscan->iscan_ex_params_p->params.nprobes));
+		WL_SCAN(("active_time=%d\n", iscan->iscan_ex_params_p->params.active_time));
+		WL_SCAN(("passive_time=%d\n", iscan->iscan_ex_params_p->params.passive_time));
+		WL_SCAN(("home_time=%d\n", iscan->iscan_ex_params_p->params.home_time));
+		WL_SCAN(("scan_type=%d\n", iscan->iscan_ex_params_p->params.scan_type));
+		WL_SCAN(("\n###################\n"));
+	}
+#endif
+
+	if (params_size > WLC_IOCTL_MEDLEN) {
+			WL_ERROR(("Set ISCAN for %s due to params_size=%d  \n", \
+				__FUNCTION__, params_size));
+			err = -1;
+	}
+
+	if ((err = dev_iw_iovar_setbuf(dev, "iscan", iscan->iscan_ex_params_p, \
+			iscan->iscan_ex_param_size, \
+			iscan->ioctlbuf, sizeof(iscan->ioctlbuf)))) {
+			WL_ERROR(("Set ISCAN for %s failed with %d\n", __FUNCTION__, err));
+			err = -1;
+	}
+
+exit:
+
+	return err;
+}
+
+
+static int iwpriv_set_cscan(struct net_device *dev, struct iw_request_info *info, \
+				union iwreq_data *wrqu, char *ext)
+{
+	int res = 0;
+	char  *extra = NULL;
+	iscan_info_t *iscan = g_iscan;
+	wlc_ssid_t ssids_local[WL_SCAN_PARAMS_SSID_MAX];
+	int nssid = 0;
+	int nchan = 0;
+
+	WL_TRACE(("\%s: info->cmd:%x, info->flags:%x, u.data=0x%p, u.len=%d\n",
+		__FUNCTION__, info->cmd, info->flags,
+		wrqu->data.pointer, wrqu->data.length));
+
+	if (g_onoff == G_WLAN_SET_OFF) {
+		WL_TRACE(("%s: driver is not up yet after START\n", __FUNCTION__));
+		return -1;
+	}
+
+#ifdef PNO_SET_DEBUG
+	wl_iw_set_pno_set(dev, info, wrqu, extra);
+	return 0;
+#endif
+
+	if (wrqu->data.length != 0) {
+
+		char *str_ptr;
+
+		if (!iscan->iscan_ex_params_p) {
+			return -EFAULT;
+		}
+
+		if (!(extra = kmalloc(wrqu->data.length+1, GFP_KERNEL)))
+			return -ENOMEM;
+
+		if (copy_from_user(extra, wrqu->data.pointer, wrqu->data.length)) {
+			kfree(extra);
+			return -EFAULT;
+		}
+
+		extra[wrqu->data.length] = 0;
+		WL_ERROR(("Got str param in iw_point:\n %s\n", extra));
+
+		str_ptr = extra;
+
+		if (strncmp(str_ptr, GET_SSID, strlen(GET_SSID))) {
+			WL_ERROR(("%s Error: extracting SSID='' string\n", __FUNCTION__));
+			goto exit_proc;
+		}
+		str_ptr += strlen(GET_SSID);
+		nssid = wl_iw_parse_ssid_list(&str_ptr, ssids_local, nssid, \
+						WL_SCAN_PARAMS_SSID_MAX);
+		if (nssid == -1) {
+			WL_ERROR(("%s wrong ssid list", __FUNCTION__));
+			return -1;
+		}
+
+		if (iscan->iscan_ex_param_size > WLC_IOCTL_MAXLEN) {
+			WL_ERROR(("%s wrong ex_param_size %d", \
+				__FUNCTION__, iscan->iscan_ex_param_size));
+			return -1;
+		}
+		memset(iscan->iscan_ex_params_p, 0, iscan->iscan_ex_param_size);
+
+		
+		wl_iw_iscan_prep(&iscan->iscan_ex_params_p->params, NULL);
+		iscan->iscan_ex_params_p->version = htod32(ISCAN_REQ_VERSION);
+		iscan->iscan_ex_params_p->action = htod16(WL_SCAN_ACTION_START);
+		iscan->iscan_ex_params_p->scan_duration = htod16(0);
+
+		
+		if ((nchan = wl_iw_parse_channel_list(&str_ptr, \
+					&iscan->iscan_ex_params_p->params.channel_list[0], \
+					WL_NUMCHANNELS)) == -1) {
+			WL_ERROR(("%s missing channel list\n", __FUNCTION__));
+			return -1;
+		}
+
+		
+		get_parmeter_from_string(&str_ptr, \
+				GET_NPROBE, PTYPE_INTDEC, \
+				&iscan->iscan_ex_params_p->params.nprobes, 2);
+
+		get_parmeter_from_string(&str_ptr, GET_ACTIVE_ASSOC_DWELL, PTYPE_INTDEC, \
+						&iscan->iscan_ex_params_p->params.active_time, 4);
+
+		get_parmeter_from_string(&str_ptr, GET_PASSIVE_ASSOC_DWELL, PTYPE_INTDEC, \
+						&iscan->iscan_ex_params_p->params.passive_time, 4);
+
+		get_parmeter_from_string(&str_ptr, GET_HOME_DWELL, PTYPE_INTDEC, \
+					&iscan->iscan_ex_params_p->params.home_time, 4);
+
+		get_parmeter_from_string(&str_ptr, GET_SCAN_TYPE, PTYPE_INTDEC, \
+					&iscan->iscan_ex_params_p->params.scan_type, 1);
+
+		res = wl_iw_combined_scan_set(dev, ssids_local, nssid, nchan);
+
+	} else {
+		  WL_ERROR(("IWPRIV argument len = 0 \n"));
+		  return -1;
+	}
+
+exit_proc:
+
+	kfree(extra);
+
+	return res;
+}
+
+
+static int
+wl_iw_set_cscan(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	union iwreq_data *wrqu,
+	char *extra
+)
+{
+	int res = -1;
+	iscan_info_t *iscan = g_iscan;
+	wlc_ssid_t ssids_local[WL_SCAN_PARAMS_SSID_MAX];
+	int nssid = 0;
+	int nchan = 0;
+	cscan_tlv_t *cscan_tlv_temp;
+	char type;
+	char *str_ptr;
+	int tlv_size_left;
+#ifdef TLV_DEBUG
+	int i;
+	char tlv_in_example[] = {			'C', 'S', 'C', 'A', 'N', ' ', \
+							0x53, 0x01, 0x00, 0x00,
+							'S',	  
+							0x00, 
+							'S',    
+							0x04, 
+							'B', 'R', 'C', 'M',
+							'C',
+							0x06, 
+							'P', 
+							0x94,
+							0x11,
+							'T',     
+							0x01  
+							};
+#endif 
+
+	WL_TRACE(("\n### %s: info->cmd:%x, info->flags:%x, u.data=0x%p, u.len=%d\n",
+		__FUNCTION__, info->cmd, info->flags,
+		wrqu->data.pointer, wrqu->data.length));
+
+	net_os_wake_lock(dev);
+
+	if (g_onoff == G_WLAN_SET_OFF) {
+		WL_TRACE(("%s: driver is not up yet after START\n", __FUNCTION__));
+		goto exit_proc;
+	}
+
+
+	if (wrqu->data.length < (strlen(CSCAN_COMMAND) + sizeof(cscan_tlv_t))) {
+		WL_ERROR(("%s aggument=%d  less %d\n", __FUNCTION__, \
+			wrqu->data.length, strlen(CSCAN_COMMAND) + sizeof(cscan_tlv_t)));
+		goto exit_proc;
+	}
+
+#ifdef TLV_DEBUG
+	memcpy(extra, tlv_in_example, sizeof(tlv_in_example));
+	wrqu->data.length = sizeof(tlv_in_example);
+	for (i = 0; i < wrqu->data.length; i++)
+		printf("%02X ", extra[i]);
+	printf("\n");
+#endif 
+
+	str_ptr = extra;
+	str_ptr +=  strlen(CSCAN_COMMAND);
+	tlv_size_left = wrqu->data.length - strlen(CSCAN_COMMAND);
+
+	cscan_tlv_temp = (cscan_tlv_t *)str_ptr;
+	memset(ssids_local, 0, sizeof(ssids_local));
+	
+	if ((cscan_tlv_temp->prefix == CSCAN_TLV_PREFIX) && \
+		(cscan_tlv_temp->version == CSCAN_TLV_VERSION) && \
+		(cscan_tlv_temp->subver == CSCAN_TLV_SUBVERSION))
+	{
+		str_ptr += sizeof(cscan_tlv_t);
+		tlv_size_left  -= sizeof(cscan_tlv_t);
+
+		
+		if ((nssid = wl_iw_parse_ssid_list_tlv(&str_ptr, ssids_local, \
+				WL_SCAN_PARAMS_SSID_MAX, &tlv_size_left)) <= 0) {
+			WL_ERROR(("SSID is not presented or corrupted ret=%d\n", nssid));
+			goto exit_proc;
+		}
+		else {
+			
+			memset(iscan->iscan_ex_params_p, 0, iscan->iscan_ex_param_size);
+
+			
+			wl_iw_iscan_prep(&iscan->iscan_ex_params_p->params, NULL);
+			iscan->iscan_ex_params_p->version = htod32(ISCAN_REQ_VERSION);
+			iscan->iscan_ex_params_p->action = htod16(WL_SCAN_ACTION_START);
+			iscan->iscan_ex_params_p->scan_duration = htod16(0);
+
+			
+			while (tlv_size_left > 0)
+			{
+			type = str_ptr[0];
+			switch (type) {
+				case CSCAN_TLV_TYPE_CHANNEL_IE:
+					
+					if ((nchan = wl_iw_parse_channel_list_tlv(&str_ptr, \
+					&iscan->iscan_ex_params_p->params.channel_list[0], \
+					WL_NUMCHANNELS, &tlv_size_left)) == -1) {
+					WL_ERROR(("%s missing channel list\n", \
+						 __FUNCTION__));
+						goto exit_proc;
+					}
+				break;
+				case CSCAN_TLV_TYPE_NPROBE_IE:
+					if ((res = wl_iw_parse_data_tlv(&str_ptr, \
+						&iscan->iscan_ex_params_p->params.nprobes, \
+						sizeof(iscan->iscan_ex_params_p->params.nprobes), \
+						type, sizeof(char), &tlv_size_left)) == -1) {
+						WL_ERROR(("%s return %d\n", \
+							__FUNCTION__, res));
+							goto exit_proc;
+					}
+				break;
+				case CSCAN_TLV_TYPE_ACTIVE_IE:
+					if ((res = wl_iw_parse_data_tlv(&str_ptr, \
+					&iscan->iscan_ex_params_p->params.active_time, \
+					sizeof(iscan->iscan_ex_params_p->params.active_time), \
+					type, sizeof(short), &tlv_size_left)) == -1) {
+						WL_ERROR(("%s return %d\n", \
+						__FUNCTION__, res));
+						goto exit_proc;
+					}
+				break;
+				case CSCAN_TLV_TYPE_PASSIVE_IE:
+					if ((res = wl_iw_parse_data_tlv(&str_ptr, \
+					&iscan->iscan_ex_params_p->params.passive_time, \
+					sizeof(iscan->iscan_ex_params_p->params.passive_time), \
+					type, sizeof(short), &tlv_size_left)) == -1) {
+						WL_ERROR(("%s return %d\n", \
+						__FUNCTION__, res));
+						goto exit_proc;
+					}
+				break;
+				case CSCAN_TLV_TYPE_HOME_IE:
+					if ((res = wl_iw_parse_data_tlv(&str_ptr, \
+					&iscan->iscan_ex_params_p->params.home_time, \
+					sizeof(iscan->iscan_ex_params_p->params.home_time), \
+					type, sizeof(short), &tlv_size_left)) == -1) {
+						WL_ERROR(("%s return %d\n", \
+						__FUNCTION__, res));
+						goto exit_proc;
+					}
+				break;
+				case CSCAN_TLV_TYPE_STYPE_IE:
+					if ((res = wl_iw_parse_data_tlv(&str_ptr, \
+					&iscan->iscan_ex_params_p->params.scan_type, \
+					sizeof(iscan->iscan_ex_params_p->params.scan_type), \
+					type, sizeof(char), &tlv_size_left)) == -1) {
+					WL_ERROR(("%s return %d\n", \
+						__FUNCTION__, res));
+						goto exit_proc;
+					}
+				break;
+
+				default :
+					WL_ERROR(("%s get unkwown type %X\n", \
+						__FUNCTION__, type));
+					goto exit_proc;
+				break;
+				}
+			} 
+			}
+		}
+		else {
+			WL_ERROR(("%s get wrong TLV command\n", __FUNCTION__));
+			goto exit_proc;
+		}
+
+#if defined(CONFIG_FIRST_SCAN)
+		if (g_first_broadcast_scan < BROADCAST_SCAN_FIRST_RESULT_CONSUMED) {
+			if (++g_first_counter_scans == MAX_ALLOWED_BLOCK_SCAN_FROM_FIRST_SCAN) {
+
+				WL_ERROR(("%s Clean up First scan flag which is %d\n", \
+						 __FUNCTION__, g_first_broadcast_scan));
+				g_first_broadcast_scan = BROADCAST_SCAN_FIRST_RESULT_CONSUMED;
+			}
+			else {
+				WL_ERROR(("%s Ignoring CSCAN : First Scan is not done yet %d\n", \
+						__FUNCTION__, g_first_counter_scans));
+				res = -EBUSY;
+				goto exit_proc;
+			}
+		}
+#endif
+
+		res = wl_iw_combined_scan_set(dev, ssids_local, nssid, nchan);
+
+exit_proc:
+	net_os_wake_unlock(dev);
+	return res;
+}
+
+#endif 
+
+#ifdef SOFTAP
+#ifndef AP_ONLY
+
+static int thr_wait_for_2nd_eth_dev(void *data)
+{
+	struct net_device *dev = (struct net_device *)data;
+	wl_iw_t *iw;
+	int ret = 0;
+	unsigned long flags;
+
+	net_os_wake_lock(dev);
+
+	DAEMONIZE("wl0_eth_wthread");
+
+	WL_TRACE(("\n>%s thread started:, PID:%x\n", __FUNCTION__, current->pid));
+	iw = *(wl_iw_t **)netdev_priv(dev);
+	if (!iw) {
+		WL_ERROR(("%s: dev is null\n", __FUNCTION__));
+		ret = -1;
+		goto fail;
+	}
+
+#ifndef BCMSDIOH_STD
+	if (down_timeout(&ap_eth_sema,  msecs_to_jiffies(5000)) != 0) {
+		WL_ERROR(("\n%s: sap_eth_sema timeout \n", __FUNCTION__));
+		ret = -1;
+		goto fail;
+	}
+#endif
+
+	flags = dhd_os_spin_lock(iw->pub);
+	if (!ap_net_dev) {
+		WL_ERROR((" ap_net_dev is null !!!"));
+		ret = -1;
+		dhd_os_spin_unlock(iw->pub, flags);
+		goto fail;
+	}
+
+	WL_TRACE(("\n>%s: Thread:'softap ethdev IF:%s is detected !!!'\n\n",
+		__FUNCTION__, ap_net_dev->name));
+
+	ap_cfg_running = TRUE;
+
+	dhd_os_spin_unlock(iw->pub, flags);
+
+	bcm_mdelay(500);
+
+	wl_iw_send_priv_event(priv_dev, "AP_SET_CFG_OK");
+
+fail:
+	WL_TRACE(("\n>%s, thread completed\n", __FUNCTION__));
+
+	net_os_wake_unlock(dev);
+
+	complete_and_exit(&ap_cfg_exited, 0);
+	return ret;
+}
+#endif 
+#ifndef AP_ONLY
+static int last_auto_channel = 6;
+#endif
+static int get_softap_auto_channel(struct net_device *dev, struct ap_profile *ap)
+{
+	int chosen = 0;
+	wl_uint32_list_t request;
+	int rescan = 0;
+	int retry = 0;
+	int updown = 0;
+	int ret = 0;
+	wlc_ssid_t null_ssid;
+	int res = 0;
+#ifndef AP_ONLY
+	int iolen = 0;
+	int mkvar_err = 0;
+	int bsscfg_index = 1;
+	char buf[WLC_IOCTL_SMLEN];
+#endif
+	WL_SOFTAP(("Enter %s\n", __FUNCTION__));
+
+#ifndef AP_ONLY
+	if (ap_cfg_running) {
+		ap->channel = last_auto_channel;
+		return res;
+	}
+#endif
+	memset(&null_ssid, 0, sizeof(wlc_ssid_t));
+	res |= dev_wlc_ioctl(dev, WLC_UP, &updown, sizeof(updown));
+#ifdef AP_ONLY
+	res |= dev_wlc_ioctl(dev, WLC_SET_SSID, &null_ssid, sizeof(null_ssid));
+#else
+	iolen = wl_bssiovar_mkbuf("ssid", bsscfg_index, (char *)(&null_ssid), \
+		null_ssid.SSID_len+4, buf, sizeof(buf), &mkvar_err);
+	ASSERT(iolen);
+	res |= dev_wlc_ioctl(dev, WLC_SET_VAR, buf, iolen);
+#endif
+	auto_channel_retry:
+			request.count = htod32(0);
+			ret = dev_wlc_ioctl(dev, WLC_START_CHANNEL_SEL, &request, sizeof(request));
+			if (ret < 0) {
+				WL_ERROR(("can't start auto channel scan\n"));
+				goto fail;
+			}
+
+	get_channel_retry:
+			bcm_mdelay(500);
+
+			ret = dev_wlc_ioctl(dev, WLC_GET_CHANNEL_SEL, &chosen, sizeof(chosen));
+			if (ret < 0 || dtoh32(chosen) == 0) {
+				if (retry++ < 3)
+					goto get_channel_retry;
+				else {
+					WL_ERROR(("can't get auto channel sel, err = %d, \
+						chosen = %d\n", ret, chosen));
+					goto fail;
+				}
+			}
+			if ((chosen == 1) && (!rescan++))
+				goto auto_channel_retry;
+			WL_SOFTAP(("Set auto channel = %d\n", chosen));
+			ap->channel = chosen;
+			if ((res = dev_wlc_ioctl(dev, WLC_DOWN, &updown, sizeof(updown))) < 0) {
+				WL_ERROR(("%s fail to set up err =%d\n", __FUNCTION__, res));
+				goto fail;
+			}
+#ifndef AP_ONLY
+	if (!res)
+		last_auto_channel = ap->channel;
+#endif
+
+fail :
+	return res;
+}
+
+
+static int set_ap_cfg(struct net_device *dev, struct ap_profile *ap)
+{
+	int updown = 0;
+	int channel = 0;
+
+	wlc_ssid_t ap_ssid;
+	int max_assoc = 8;
+
+	int res = 0;
+	int apsta_var = 0;
+#ifndef AP_ONLY
+	int mpc = 0;
+	int iolen = 0;
+	int mkvar_err = 0;
+	int bsscfg_index = 1;
+	char buf[WLC_IOCTL_SMLEN];
+#endif
+
+	if (!dev) {
+		WL_ERROR(("%s: dev is null\n", __FUNCTION__));
+		return -1;
+	}
+
+	net_os_wake_lock(dev);
+
+	WL_SOFTAP(("wl_iw: set ap profile:\n"));
+	WL_SOFTAP(("	ssid = '%s'\n", ap->ssid));
+	WL_SOFTAP(("	security = '%s'\n", ap->sec));
+	if (ap->key[0] != '\0')
+		WL_SOFTAP(("	key = '%s'\n", ap->key));
+	WL_SOFTAP(("	channel = %d\n", ap->channel));
+	WL_SOFTAP(("	max scb = %d\n", ap->max_scb));
+
+#ifdef AP_ONLY
+	if (ap_cfg_running) {
+		wl_iw_softap_deassoc_stations(dev, NULL);
+		ap_cfg_running = FALSE;
+	}
+#endif
+
+	if (ap_cfg_running == FALSE) {
+
+#ifndef AP_ONLY
+		sema_init(&ap_eth_sema, 0);
+
+		mpc = 0;
+		if ((res = dev_wlc_intvar_set(dev, "mpc", mpc))) {
+			WL_ERROR(("%s fail to set mpc\n", __FUNCTION__));
+			goto fail;
+		}
+#endif
+
+		updown = 0;
+		if ((res = dev_wlc_ioctl(dev, WLC_DOWN, &updown, sizeof(updown)))) {
+			WL_ERROR(("%s fail to set updown\n", __FUNCTION__));
+			goto fail;
+		}
+
+#ifdef AP_ONLY
+		apsta_var = 0;
+		if ((res = dev_wlc_ioctl(dev, WLC_SET_AP, &apsta_var, sizeof(apsta_var)))) {
+			WL_ERROR(("%s fail to set apsta_var 0\n", __FUNCTION__));
+			goto fail;
+		}
+		apsta_var = 1;
+		if ((res = dev_wlc_ioctl(dev, WLC_SET_AP, &apsta_var, sizeof(apsta_var)))) {
+			WL_ERROR(("%s fail to set apsta_var 1\n", __FUNCTION__));
+			goto fail;
+		}
+		res = dev_wlc_ioctl(dev, WLC_GET_AP, &apsta_var, sizeof(apsta_var));
+#else
+		apsta_var = 1;
+		iolen = wl_bssiovar_mkbuf("apsta",
+			bsscfg_index,  &apsta_var, sizeof(apsta_var)+4,
+			buf, sizeof(buf), &mkvar_err);
+
+		if (iolen <= 0)
+			goto fail;
+
+		if ((res = dev_wlc_ioctl(dev, WLC_SET_VAR, buf, iolen)) < 0) {
+			WL_ERROR(("%s fail to set apsta \n", __FUNCTION__));
+			goto fail;
+		}
+		WL_TRACE(("\n>in %s: apsta set result: %d \n", __FUNCTION__, res));
+#endif
+
+		updown = 1;
+		if ((res = dev_wlc_ioctl(dev, WLC_UP, &updown, sizeof(updown))) < 0) {
+			WL_ERROR(("%s fail to set apsta \n", __FUNCTION__));
+			goto fail;
+		}
+
+	} else {
+		
+		if (!ap_net_dev) {
+			WL_ERROR(("%s: ap_net_dev is null\n", __FUNCTION__));
+			goto fail;
+		}
+
+		res = wl_iw_softap_deassoc_stations(ap_net_dev, NULL);
+
+		
+		if ((res = dev_iw_write_cfg1_bss_var(dev, 0)) < 0) {
+			WL_ERROR(("%s fail to set bss down\n", __FUNCTION__));
+			goto fail;
+		}
+	}
+
+	if (strlen(ap->country_code)) {
+		WL_ERROR(("%s: Igonored: Country MUST be specified \
+				  COUNTRY command with \n",	__FUNCTION__));
+	} else {
+		WL_SOFTAP(("%s: Country code is not specified,"
+			" will use Radio's default\n",
+			__FUNCTION__));
+	}
+
+	iolen = wl_bssiovar_mkbuf("closednet",
+		bsscfg_index,  &ap->closednet, sizeof(ap->closednet)+4,
+		buf, sizeof(buf), &mkvar_err);
+	ASSERT(iolen);
+	if ((res = dev_wlc_ioctl(dev, WLC_SET_VAR, buf, iolen)) < 0) {
+		WL_ERROR(("%s failed to set 'closednet'for apsta \n", __FUNCTION__));
+		goto fail;
+	}
+
+	
+	if ((ap->channel == 0) && (get_softap_auto_channel(dev, ap) < 0)) {
+		ap->channel = 1;
+		WL_ERROR(("%s auto channel failed, pick up channel=%d\n", \
+			__FUNCTION__, ap->channel));
+	}
+
+	channel = ap->channel;
+	if ((res = dev_wlc_ioctl(dev, WLC_SET_CHANNEL, &channel, sizeof(channel)))) {
+			WL_ERROR(("%s fail to set channel\n", __FUNCTION__));
+			goto fail;
+	}
+
+	if (ap_cfg_running == FALSE) {
+		updown = 0;
+		if ((res = dev_wlc_ioctl(dev, WLC_UP, &updown, sizeof(updown)))) {
+			WL_ERROR(("%s fail to set up\n", __FUNCTION__));
+			goto fail;
+		}
+	}
+
+	max_assoc = ap->max_scb;
+	if ((res = dev_wlc_intvar_set(dev, "maxassoc", max_assoc))) {
+			WL_ERROR(("%s fail to set maxassoc\n", __FUNCTION__));
+			goto fail;
+	}
+
+	ap_ssid.SSID_len = strlen(ap->ssid);
+	strncpy(ap_ssid.SSID, ap->ssid, ap_ssid.SSID_len);
+
+#ifdef AP_ONLY
+	if ((res = wl_iw_set_ap_security(dev, &my_ap)) != 0) {
+		WL_ERROR(("ERROR:%d in:%s, wl_iw_set_ap_security is skipped\n", \
+		res, __FUNCTION__));
+		goto fail;
+	}
+	wl_iw_send_priv_event(dev, "ASCII_CMD=AP_BSS_START");
+	ap_cfg_running = TRUE;
+#else
+	iolen = wl_bssiovar_mkbuf("ssid", bsscfg_index, (char *)(&ap_ssid),
+		ap_ssid.SSID_len+4, buf, sizeof(buf), &mkvar_err);
+	ASSERT(iolen);
+	if ((res = dev_wlc_ioctl(dev, WLC_SET_VAR, buf, iolen)) != 0) {
+		WL_ERROR(("ERROR:%d in:%s, Security & BSS reconfiguration is skipped\n", \
+		res, __FUNCTION__));
+		goto fail;
+	}
+	if (ap_cfg_running == FALSE) {
+		init_completion(&ap_cfg_exited);
+		ap_cfg_pid = kernel_thread(thr_wait_for_2nd_eth_dev, dev, 0);
+	} else {
+		ap_cfg_pid = -1;
+		if (ap_net_dev == NULL) {
+			WL_ERROR(("%s ERROR: ap_net_dev is NULL !!!\n", __FUNCTION__));
+			goto fail;
+		}
+
+		WL_ERROR(("%s: %s Configure security & restart AP bss \n", \
+			 __FUNCTION__, ap_net_dev->name));
+
+		if ((res = wl_iw_set_ap_security(ap_net_dev, &my_ap)) < 0) {
+			WL_ERROR(("%s fail to set security : %d\n", __FUNCTION__, res));
+			goto fail;
+		}
+
+		if ((res = dev_iw_write_cfg1_bss_var(dev, 1)) < 0) {
+			WL_ERROR(("%s fail to set bss up\n", __FUNCTION__));
+			goto fail;
+		}
+	}
+#endif 
+fail:
+	WL_SOFTAP(("%s exit with %d\n", __FUNCTION__, res));
+
+	net_os_wake_unlock(dev);
+
+	return res;
+}
+
+
+static int wl_iw_set_ap_security(struct net_device *dev, struct ap_profile *ap)
+{
+	int wsec = 0;
+	int wpa_auth = 0;
+	int res = 0;
+	int i;
+	char *ptr;
+#ifdef AP_ONLY
+	int mpc = 0;
+	wlc_ssid_t ap_ssid;
+#endif
+	wl_wsec_key_t key;
+
+	WL_SOFTAP(("\nsetting SOFTAP security mode:\n"));
+	WL_SOFTAP(("wl_iw: set ap profile:\n"));
+	WL_SOFTAP(("	ssid = '%s'\n", ap->ssid));
+	WL_SOFTAP(("	security = '%s'\n", ap->sec));
+	if (ap->key[0] != '\0') {
+		WL_SOFTAP(("	key = '%s'\n", ap->key));
+	}
+	WL_SOFTAP(("	channel = %d\n", ap->channel));
+	WL_SOFTAP(("	max scb = %d\n", ap->max_scb));
+
+	if (strnicmp(ap->sec, "open", strlen("open")) == 0) {
+		wsec = 0;
+		res = dev_wlc_intvar_set(dev, "wsec", wsec);
+		wpa_auth = WPA_AUTH_DISABLED;
+		res |= dev_wlc_intvar_set(dev, "wpa_auth", wpa_auth);
+
+		WL_SOFTAP(("=====================\n"));
+		WL_SOFTAP((" wsec & wpa_auth set 'OPEN', result:&d %d\n", res));
+		WL_SOFTAP(("=====================\n"));
+
+	} else if (strnicmp(ap->sec, "wep", strlen("wep")) == 0) {
+
+		memset(&key, 0, sizeof(key));
+
+		wsec = WEP_ENABLED;
+		res = dev_wlc_intvar_set(dev, "wsec", wsec);
+
+		key.index = 0;
+		if (wl_iw_parse_wep(ap->key, &key)) {
+			WL_SOFTAP(("wep key parse err!\n"));
+			return -1;
+		}
+
+		key.index = htod32(key.index);
+		key.len = htod32(key.len);
+		key.algo = htod32(key.algo);
+		key.flags = htod32(key.flags);
+
+		res |= dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key));
+
+		wpa_auth = WPA_AUTH_DISABLED;
+		res |= dev_wlc_intvar_set(dev, "wpa_auth", wpa_auth);
+
+		WL_SOFTAP(("=====================\n"));
+		WL_SOFTAP((" wsec & auth set 'WEP', result:&d %d\n", res));
+		WL_SOFTAP(("=====================\n"));
+
+	} else if (strnicmp(ap->sec, "wpa2-psk", strlen("wpa2-psk")) == 0) {
+		wsec_pmk_t psk;
+		size_t key_len;
+
+		wsec = AES_ENABLED;
+		dev_wlc_intvar_set(dev, "wsec", wsec);
+
+		key_len = strlen(ap->key);
+		if (key_len < WSEC_MIN_PSK_LEN || key_len > WSEC_MAX_PSK_LEN) {
+			WL_SOFTAP(("passphrase must be between %d and %d characters long\n",
+			WSEC_MIN_PSK_LEN, WSEC_MAX_PSK_LEN));
+			return -1;
+		}
+
+		if (key_len < WSEC_MAX_PSK_LEN) {
+			unsigned char output[2*SHA1HashSize];
+			char key_str_buf[WSEC_MAX_PSK_LEN+1];
+
+			memset(output, 0, sizeof(output));
+			pbkdf2_sha1(ap->key, ap->ssid, strlen(ap->ssid), 4096, output, 32);
+
+			ptr = key_str_buf;
+			for (i = 0; i < (WSEC_MAX_PSK_LEN/8); i++) {
+				sprintf(ptr, "%02x%02x%02x%02x", (uint)output[i*4], \
+					 (uint)output[i*4+1], (uint)output[i*4+2], \
+					 (uint)output[i*4+3]);
+				ptr += 8;
+			}
+			WL_SOFTAP(("%s: passphase = %s\n", __FUNCTION__, key_str_buf));
+
+			psk.key_len = htod16((ushort)WSEC_MAX_PSK_LEN);
+			memcpy(psk.key, key_str_buf, psk.key_len);
+		} else {
+			psk.key_len = htod16((ushort) key_len);
+			memcpy(psk.key, ap->key, key_len);
+		}
+		psk.flags = htod16(WSEC_PASSPHRASE);
+		dev_wlc_ioctl(dev, WLC_SET_WSEC_PMK, &psk, sizeof(psk));
+
+		wpa_auth = WPA2_AUTH_PSK;
+		dev_wlc_intvar_set(dev, "wpa_auth", wpa_auth);
+
+	} else if (strnicmp(ap->sec, "wpa-psk", strlen("wpa-psk")) == 0) {
+
+		wsec_pmk_t psk;
+		size_t key_len;
+
+		wsec = TKIP_ENABLED;
+		res = dev_wlc_intvar_set(dev, "wsec", wsec);
+
+		key_len = strlen(ap->key);
+		if (key_len < WSEC_MIN_PSK_LEN || key_len > WSEC_MAX_PSK_LEN) {
+			WL_SOFTAP(("passphrase must be between %d and %d characters long\n",
+			WSEC_MIN_PSK_LEN, WSEC_MAX_PSK_LEN));
+			return -1;
+		}
+
+		if (key_len < WSEC_MAX_PSK_LEN) {
+			unsigned char output[2*SHA1HashSize];
+			char key_str_buf[WSEC_MAX_PSK_LEN+1];
+			bzero(output, 2*SHA1HashSize);
+
+			WL_SOFTAP(("%s: do passhash...\n", __FUNCTION__));
+
+			pbkdf2_sha1(ap->key, ap->ssid, strlen(ap->ssid), 4096, output, 32);
+
+			ptr = key_str_buf;
+			for (i = 0; i < (WSEC_MAX_PSK_LEN/8); i++) {
+				WL_SOFTAP(("[%02d]: %08x\n", i, *((unsigned int *)&output[i*4])));
+
+				sprintf(ptr, "%02x%02x%02x%02x", (uint)output[i*4],
+					(uint)output[i*4+1], (uint)output[i*4+2],
+					(uint)output[i*4+3]);
+				ptr += 8;
+			}
+			WL_SOFTAP(("%s: passphase = %s\n", __FUNCTION__, key_str_buf));
+
+			psk.key_len = htod16((ushort)WSEC_MAX_PSK_LEN);
+			memcpy(psk.key, key_str_buf, psk.key_len);
+		} else {
+			psk.key_len = htod16((ushort) key_len);
+			memcpy(psk.key, ap->key, key_len);
+		}
+
+		psk.flags = htod16(WSEC_PASSPHRASE);
+		res |= dev_wlc_ioctl(dev, WLC_SET_WSEC_PMK, &psk, sizeof(psk));
+
+		wpa_auth = WPA_AUTH_PSK;
+		res |= dev_wlc_intvar_set(dev, "wpa_auth", wpa_auth);
+
+		WL_SOFTAP((" wsec & auth set 'wpa-psk' (TKIP), result:&d %d\n", res));
+	}
+
+#ifdef AP_ONLY
+		ap_ssid.SSID_len = strlen(ap->ssid);
+		strncpy(ap_ssid.SSID, ap->ssid, ap_ssid.SSID_len);
+		res |= dev_wlc_ioctl(dev, WLC_SET_SSID, &ap_ssid, sizeof(ap_ssid));
+		mpc = 0;
+		res |= dev_wlc_intvar_set(dev, "mpc", mpc);
+		if (strnicmp(ap->sec, "wep", strlen("wep")) == 0) {
+			res |= dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key));
+		}
+#endif
+	return res;
+}
+
+
+
+int get_parmeter_from_string(
+			char **str_ptr, const char *token,
+			int param_type, void  *dst, int param_max_len)
+{
+	char int_str[7] = "0";
+	int parm_str_len;
+	char  *param_str_begin;
+	char  *param_str_end;
+
+	if ((*str_ptr) && !strncmp(*str_ptr, token, strlen(token))) {
+
+		strsep(str_ptr, "=,");
+		param_str_begin = *str_ptr;
+		strsep(str_ptr, "=,");
+
+		if (*str_ptr == NULL) {
+			parm_str_len = strlen(param_str_begin);
+		} else {
+			param_str_end = *str_ptr-1;
+			parm_str_len = param_str_end - param_str_begin;
+		}
+
+		WL_TRACE((" 'token:%s', len:%d, ", token, parm_str_len));
+
+		if (parm_str_len > param_max_len) {
+			WL_TRACE((" WARNING: extracted param len:%d is > MAX:%d\n",
+				parm_str_len, param_max_len));
+
+			parm_str_len = param_max_len;
+		}
+
+		switch (param_type) {
+
+		case PTYPE_INTDEC: {
+			int *pdst_int = dst;
+			char *eptr;
+
+			if (parm_str_len > sizeof(int_str))
+				 parm_str_len = sizeof(int_str);
+
+			memcpy(int_str, param_str_begin, parm_str_len);
+
+			*pdst_int = simple_strtoul(int_str, &eptr, 10);
+
+			WL_TRACE((" written as integer:%d\n",  *pdst_int));
+			}
+			break;
+		case PTYPE_STR_HEX: {
+			u8 *buf = dst;
+
+			param_max_len = param_max_len >> 1;
+			hstr_2_buf(param_str_begin, buf, param_max_len);
+			print_buf(buf, param_max_len, 0);
+			}
+			break;
+		default:
+			memcpy(dst, param_str_begin, parm_str_len);
+			*((char *)dst + parm_str_len) = 0;
+			WL_TRACE((" written as a string:%s\n", (char *)dst));
+			break;
+		}
+
+		return 0;
+	} else {
+		WL_ERROR(("\n %s: No token:%s in str:%s\n",
+			__FUNCTION__, token, *str_ptr));
+
+		return -1;
+	}
+}
+
+static int wl_iw_softap_deassoc_stations(struct net_device *dev, u8 *mac)
+{
+	int i;
+	int res = 0;
+	char mac_buf[128] = {0};
+	char z_mac[6] = {0, 0, 0, 0, 0, 0};
+	char *sta_mac;
+	struct maclist *assoc_maclist = (struct maclist *) mac_buf;
+	bool deauth_all = false;
+
+	if (mac == NULL) {
+		deauth_all = true;
+		sta_mac = z_mac;
+	} else {
+		sta_mac = mac;
+	}
+
+	memset(assoc_maclist, 0, sizeof(mac_buf));
+	assoc_maclist->count = 8;
+
+	res = dev_wlc_ioctl(dev, WLC_GET_ASSOCLIST, assoc_maclist, 128);
+	if (res != 0) {
+		WL_SOFTAP(("%s: Error:%d Couldn't get ASSOC List\n", __FUNCTION__, res));
+		return res;
+	}
+
+	if (assoc_maclist->count) {
+		for (i = 0; i < assoc_maclist->count; i++) {
+			scb_val_t scbval;
+
+			scbval.val = htod32(1);
+			bcopy(&assoc_maclist->ea[i], &scbval.ea, ETHER_ADDR_LEN);
+
+			if (deauth_all || (memcmp(&scbval.ea, sta_mac, ETHER_ADDR_LEN) == 0)) {
+				WL_SOFTAP(("%s, deauth STA:%d \n", __FUNCTION__, i));
+				res |= dev_wlc_ioctl(dev, WLC_SCB_DEAUTHENTICATE_FOR_REASON,
+					&scbval, sizeof(scb_val_t));
+			}
+		}
+	} else {
+		WL_SOFTAP((" STA ASSOC list is empty\n"));
+	}
+
+	if (res != 0) {
+		WL_ERROR(("%s: Error:%d\n", __FUNCTION__, res));
+	} else if (assoc_maclist->count) {
+		bcm_mdelay(200);
+	}
+	return res;
+}
+
+
+static int iwpriv_softap_stop(struct net_device *dev,
+	struct iw_request_info *info,
+	union iwreq_data *wrqu,
+	char *ext)
+{
+	int res = 0;
+
+	WL_SOFTAP(("got iwpriv AP_BSS_STOP\n"));
+
+	if ((!dev) && (!ap_net_dev)) {
+		WL_ERROR(("%s: dev is null\n", __FUNCTION__));
+		return res;
+	}
+
+	net_os_wake_lock(dev);
+
+	if ((ap_cfg_running == TRUE)) {
+#ifdef AP_ONLY
+		wl_iw_softap_deassoc_stations(dev, NULL);
+#else
+		wl_iw_softap_deassoc_stations(ap_net_dev, NULL);
+
+		if ((res = dev_iw_write_cfg1_bss_var(dev, 2)) < 0)
+			WL_ERROR(("%s failed to del BSS err = %d", __FUNCTION__, res));
+#endif
+
+		bcm_mdelay(100);
+
+		wrqu->data.length = 0;
+		ap_cfg_running = FALSE;
+	}
+	else
+		WL_ERROR(("%s: was called when SoftAP is OFF : move on\n", __FUNCTION__));
+
+	WL_SOFTAP(("%s Done with %d\n", __FUNCTION__, res));
+
+	net_os_wake_unlock(dev);
+
+	return res;
+}
+
+
+static int iwpriv_fw_reload(struct net_device *dev,
+		struct iw_request_info *info,
+		union iwreq_data *wrqu,
+		char *ext)
+{
+	int ret = -1;
+	char extra[256];
+	char *fwstr = fw_path;
+
+	WL_SOFTAP(("current firmware_path[]=%s\n", fwstr));
+
+	WL_TRACE((">Got FW_RELOAD cmd:"
+				"info->cmd:%x, info->flags:%x, u.data:%p, u.len:%d, \
+				fw_path:%p, len:%d \n",
+				info->cmd, info->flags,
+				wrqu->data.pointer, wrqu->data.length, fwstr, strlen(fwstr)));
+
+	if ((wrqu->data.length > 4) && (wrqu->data.length < sizeof(extra))) {
+
+		char *str_ptr;
+
+		if (copy_from_user(extra, wrqu->data.pointer, wrqu->data.length)) {
+			ret = -EFAULT;
+			goto exit_proc;
+		}
+
+		extra[wrqu->data.length] = 8;
+		str_ptr = extra;
+
+		if (get_parmeter_from_string(&str_ptr, "FW_PATH=", PTYPE_STRING, fwstr, 255) != 0) {
+			WL_ERROR(("Error: extracting FW_PATH='' string\n"));
+			goto exit_proc;
+		}
+
+		if (strstr(fwstr, "apsta") != NULL) {
+			WL_SOFTAP(("GOT APSTA FIRMWARE\n"));
+			ap_fw_loaded = TRUE;
+		} else {
+			WL_SOFTAP(("GOT STA FIRMWARE\n"));
+			ap_fw_loaded = FALSE;
+		}
+
+		WL_SOFTAP(("SET firmware_path[]=%s , str_p:%p\n", fwstr, fwstr));
+		ret = 0;
+	} else {
+		WL_ERROR(("Error: ivalid param len:%d\n", wrqu->data.length));
+	}
+
+exit_proc:
+	return ret;
+}
+#endif
+
+#ifdef SOFTAP
+static int iwpriv_wpasupp_loop_tst(struct net_device *dev,
+		struct iw_request_info *info,
+		union iwreq_data *wrqu,
+		char *ext)
+{
+	int res = 0;
+	char  *params = NULL;
+
+	WL_TRACE((">Got IWPRIV  wp_supp loopback cmd test:"
+				"info->cmd:%x, info->flags:%x, u.data:%p, u.len:%d\n",
+				info->cmd, info->flags,
+				wrqu->data.pointer, wrqu->data.length));
+
+	if (wrqu->data.length != 0) {
+
+		if (!(params = kmalloc(wrqu->data.length+1, GFP_KERNEL)))
+			return -ENOMEM;
+
+		if (copy_from_user(params, wrqu->data.pointer, wrqu->data.length)) {
+			kfree(params);
+			return -EFAULT;
+		}
+
+		params[wrqu->data.length] = 0;
+		WL_SOFTAP(("\n>> copied from user:\n %s\n", params));
+	} else {
+		WL_ERROR(("ERROR param length is 0\n"));
+		return -EFAULT;
+	}
+
+	res = wl_iw_send_priv_event(dev, params);
+	kfree(params);
+
+	return res;
+}
+#endif
+
+
+static int
+iwpriv_en_ap_bss(
+		struct net_device *dev,
+		struct iw_request_info *info,
+		void *wrqu,
+		char *extra)
+{
+	int res = 0;
+
+	if (!dev) {
+		WL_ERROR(("%s: dev is null\n", __FUNCTION__));
+		return -1;
+	}
+
+	net_os_wake_lock(dev);
+
+	WL_SOFTAP(("%s: rcvd IWPRIV IOCTL:  for dev:%s\n", __FUNCTION__, dev->name));
+
+#ifndef AP_ONLY
+	if (ap_cfg_pid >= 0) {
+		wait_for_completion(&ap_cfg_exited);
+		ap_cfg_pid = -1;
+	}
+
+	if ((res = wl_iw_set_ap_security(dev, &my_ap)) != 0) {
+		WL_ERROR((" %s ERROR setting SOFTAP security in :%d\n", __FUNCTION__, res));
+	}
+	else {
+		if ((res = dev_iw_write_cfg1_bss_var(dev, 1)) < 0)
+			WL_ERROR(("%s fail to set bss up err=%d\n", __FUNCTION__, res));
+		else
+			bcm_mdelay(100);
+	}
+
+#endif 
+	WL_SOFTAP(("%s done with res %d \n", __FUNCTION__, res));
+
+	net_os_wake_unlock(dev);
+
+	return res;
+}
+
+static int
+get_assoc_sta_list(struct net_device *dev, char *buf, int len)
+{
+	WL_TRACE(("%s: dev_wlc_ioctl(dev:%p, cmd:%d, buf:%p, len:%d)\n",
+		__FUNCTION__, dev, WLC_GET_ASSOCLIST, buf, len));
+
+	return dev_wlc_ioctl(dev, WLC_GET_ASSOCLIST, buf, len);
+
+}
+
+
+void check_error(int res, const char *msg, const char *func, int line)
+{
+	if (res != 0)
+		WL_ERROR(("%s, %d function:%s, line:%d\n", msg, res, func, line));
+}
+
+static int
+set_ap_mac_list(struct net_device *dev, void *buf)
+{
+	struct mac_list_set *mac_list_set = (struct mac_list_set *)buf;
+	struct maclist *maclist = (struct maclist *)&mac_list_set->mac_list;
+	int length;
+	int i;
+	int mac_mode = mac_list_set->mode;
+	int ioc_res = 0;
+	ap_macmode = mac_list_set->mode;
+
+	bzero(&ap_black_list, sizeof(struct mflist));
+
+	if (mac_mode == MACLIST_MODE_DISABLED) {
+
+		ioc_res = dev_wlc_ioctl(dev, WLC_SET_MACMODE, &mac_mode, sizeof(mac_mode));
+		check_error(ioc_res, "ioctl ERROR:", __FUNCTION__, __LINE__);
+		WL_SOFTAP(("%s: MAC filtering disabled\n", __FUNCTION__));
+	} else {
+
+		scb_val_t scbval;
+		char mac_buf[256] = {0};
+		struct maclist *assoc_maclist = (struct maclist *) mac_buf;
+
+		bcopy(maclist, &ap_black_list, sizeof(ap_black_list));
+
+		ioc_res = dev_wlc_ioctl(dev, WLC_SET_MACMODE, &mac_mode, sizeof(mac_mode));
+		check_error(ioc_res, "ioctl ERROR:", __FUNCTION__, __LINE__);
+
+		length = sizeof(maclist->count) + maclist->count*ETHER_ADDR_LEN;
+		dev_wlc_ioctl(dev, WLC_SET_MACLIST, maclist, length);
+
+		WL_SOFTAP(("%s: applied MAC List, mode:%d, length %d:\n",
+			__FUNCTION__, mac_mode, length));
+		for (i = 0; i < maclist->count; i++)
+			WL_SOFTAP(("mac %d: %02X:%02X:%02X:%02X:%02X:%02X\n",
+				i, maclist->ea[i].octet[0], maclist->ea[i].octet[1], \
+				maclist->ea[i].octet[2], \
+				maclist->ea[i].octet[3], maclist->ea[i].octet[4], \
+				maclist->ea[i].octet[5]));
+
+		assoc_maclist->count = 8;
+		ioc_res = dev_wlc_ioctl(dev, WLC_GET_ASSOCLIST, assoc_maclist, 256);
+		check_error(ioc_res, "ioctl ERROR:", __FUNCTION__, __LINE__);
+		WL_SOFTAP((" Cur assoc clients:%d\n", assoc_maclist->count));
+
+		if (assoc_maclist->count)
+			for (i = 0; i < assoc_maclist->count; i++) {
+				int j;
+				bool assoc_mac_matched = false;
+
+				WL_SOFTAP(("\n Cheking assoc STA: "));
+				print_buf(&assoc_maclist->ea[i], 6, 7);
+				WL_SOFTAP(("with the b/w list:"));
+
+				for (j = 0; j < maclist->count; j++)
+					if (!bcmp(&assoc_maclist->ea[i], &maclist->ea[j],
+						ETHER_ADDR_LEN)) {
+
+						assoc_mac_matched = true;
+						break;
+					}
+
+				if (((mac_mode == MACLIST_MODE_ALLOW) && !assoc_mac_matched) ||
+					((mac_mode == MACLIST_MODE_DENY) && assoc_mac_matched)) {
+
+					WL_SOFTAP(("b-match or w-mismatch,"
+								" do deauth/disassoc \n"));
+							scbval.val = htod32(1);
+							bcopy(&assoc_maclist->ea[i], &scbval.ea, \
+							ETHER_ADDR_LEN);
+							ioc_res = dev_wlc_ioctl(dev,
+								WLC_SCB_DEAUTHENTICATE_FOR_REASON,
+								&scbval, sizeof(scb_val_t));
+							check_error(ioc_res,
+								"ioctl ERROR:",
+								__FUNCTION__, __LINE__);
+
+				} else {
+					WL_SOFTAP((" no b/w list hits, let it be\n"));
+				}
+		} else {
+			WL_SOFTAP(("No ASSOC CLIENTS\n"));
+		}
+	} 
+
+	WL_SOFTAP(("%s iocres:%d\n", __FUNCTION__, ioc_res));
+	return ioc_res;
+}
+#endif
+
+
+#ifdef SOFTAP
+int set_macfilt_from_string(struct mflist *pmflist, char **param_str)
+{
+	return 0;
+}
+#endif
+
+
+#ifdef SOFTAP
+#define PARAM_OFFSET PROFILE_OFFSET
+
+int wl_iw_process_private_ascii_cmd(
+			struct net_device *dev,
+			struct iw_request_info *info,
+			union iwreq_data *dwrq,
+			char *cmd_str)
+{
+	int ret = 0;
+	char *sub_cmd = cmd_str + PROFILE_OFFSET + strlen("ASCII_CMD=");
+
+	WL_SOFTAP(("\n %s: ASCII_CMD: offs_0:%s, offset_32:\n'%s'\n",
+		__FUNCTION__, cmd_str, cmd_str + PROFILE_OFFSET));
+
+	if (strnicmp(sub_cmd, "AP_CFG", strlen("AP_CFG")) == 0) {
+
+		WL_SOFTAP((" AP_CFG \n"));
+
+
+		if (init_ap_profile_from_string(cmd_str+PROFILE_OFFSET, &my_ap) != 0) {
+			WL_ERROR(("ERROR: SoftAP CFG prams !\n"));
+			ret = -1;
+		} else {
+			ret = set_ap_cfg(dev, &my_ap);
+		}
+
+	} else if (strnicmp(sub_cmd, "AP_BSS_START", strlen("AP_BSS_START")) == 0) {
+
+		WL_SOFTAP(("\n SOFTAP - ENABLE BSS \n"));
+
+		WL_SOFTAP(("\n!!! got 'WL_AP_EN_BSS' from WPA supplicant, dev:%s\n", dev->name));
+
+#ifndef AP_ONLY
+		if (ap_net_dev == NULL) {
+			printf("\n ERROR: SOFTAP net_dev* is NULL !!!\n");
+		} else {
+			if ((ret = iwpriv_en_ap_bss(ap_net_dev, info, dwrq, cmd_str)) < 0)
+				WL_ERROR(("%s line %d fail to set bss up\n", \
+					__FUNCTION__, __LINE__));
+		}
+#else
+		if ((ret = iwpriv_en_ap_bss(dev, info, dwrq, cmd_str)) < 0)
+				WL_ERROR(("%s line %d fail to set bss up\n", \
+					__FUNCTION__, __LINE__));
+#endif
+	} else if (strnicmp(sub_cmd, "ASSOC_LST", strlen("ASSOC_LST")) == 0) {
+		/* no code yet */
+	} else if (strnicmp(sub_cmd, "AP_BSS_STOP", strlen("AP_BSS_STOP")) == 0) {
+		WL_SOFTAP((" \n temp DOWN SOFTAP\n"));
+#ifndef AP_ONLY
+		if ((ret = dev_iw_write_cfg1_bss_var(dev, 0)) < 0) {
+				WL_ERROR(("%s line %d fail to set bss down\n", \
+					__FUNCTION__, __LINE__));
+		}
+#endif
+	}
+
+	return ret;
+}
+#endif
+
+static int wl_iw_set_priv(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_point *dwrq,
+	char *ext
+)
+{
+	int ret = 0;
+	char * extra;
+
+	if (!(extra = kmalloc(dwrq->length, GFP_KERNEL)))
+	    return -ENOMEM;
+
+	if (copy_from_user(extra, dwrq->pointer, dwrq->length)) {
+	    kfree(extra);
+	    return -EFAULT;
+	}
+
+	WL_TRACE(("%s: SIOCSIWPRIV request %s, info->cmd:%x, info->flags:%d\n dwrq->length:%d",
+		dev->name, extra, info->cmd, info->flags, dwrq->length));
+
+	net_os_wake_lock(dev);
+	
+	if (dwrq->length && extra) {
+		if (strnicmp(extra, "START", strlen("START")) == 0) {
+			wl_iw_control_wl_on(dev, info);
+			WL_TRACE(("%s, Received regular START command\n", __FUNCTION__));
+		}
+
+		if (g_onoff == G_WLAN_SET_OFF) {
+			WL_TRACE(("%s, missing START, Fail\n", __FUNCTION__));
+			kfree(extra);
+			net_os_wake_unlock(dev);
+			return -EFAULT;
+		}
+
+		if (strnicmp(extra, "SCAN-ACTIVE", strlen("SCAN-ACTIVE")) == 0) {
+#ifdef ENABLE_ACTIVE_PASSIVE_SCAN_SUPPRESS
+			WL_TRACE(("%s: active scan setting suppressed\n", dev->name));
+#else
+			ret = wl_iw_set_active_scan(dev, info, (union iwreq_data *)dwrq, extra);
+#endif
+		} else if (strnicmp(extra, "SCAN-PASSIVE", strlen("SCAN-PASSIVE")) == 0)
+#ifdef ENABLE_ACTIVE_PASSIVE_SCAN_SUPPRESS
+			WL_TRACE(("%s: passive scan setting suppressed\n", dev->name));
+#else
+			ret = wl_iw_set_passive_scan(dev, info, (union iwreq_data *)dwrq, extra);
+#endif
+		else if (strnicmp(extra, "RSSI", strlen("RSSI")) == 0)
+			ret = wl_iw_get_rssi(dev, info, (union iwreq_data *)dwrq, extra);
+		else if (strnicmp(extra, "LINKSPEED", strlen("LINKSPEED")) == 0)
+			ret = wl_iw_get_link_speed(dev, info, (union iwreq_data *)dwrq, extra);
+		else if (strnicmp(extra, "MACADDR", strlen("MACADDR")) == 0)
+			ret = wl_iw_get_macaddr(dev, info, (union iwreq_data *)dwrq, extra);
+		else if (strnicmp(extra, "COUNTRY", strlen("COUNTRY")) == 0)
+			ret = wl_iw_set_country(dev, info, (union iwreq_data *)dwrq, extra);
+		else if (strnicmp(extra, "STOP", strlen("STOP")) == 0)
+			ret = wl_iw_control_wl_off(dev, info);
+		else if (strnicmp(extra, BAND_GET_CMD, strlen(BAND_GET_CMD)) == 0)
+			ret = wl_iw_get_band(dev, info, (union iwreq_data *)dwrq, extra);
+		else if (strnicmp(extra, BAND_SET_CMD, strlen(BAND_SET_CMD)) == 0)
+			ret = wl_iw_set_band(dev, info, (union iwreq_data *)dwrq, extra);
+		else if (strnicmp(extra, DTIM_SKIP_GET_CMD, strlen(DTIM_SKIP_GET_CMD)) == 0)
+			ret = wl_iw_get_dtim_skip(dev, info, (union iwreq_data *)dwrq, extra);
+		else if (strnicmp(extra, DTIM_SKIP_SET_CMD, strlen(DTIM_SKIP_SET_CMD)) == 0)
+			ret = wl_iw_set_dtim_skip(dev, info, (union iwreq_data *)dwrq, extra);
+		else if (strnicmp(extra, SETSUSPEND_CMD, strlen(SETSUSPEND_CMD)) == 0)
+			ret = wl_iw_set_suspend(dev, info, (union iwreq_data *)dwrq, extra);
+	    else if (strnicmp(extra, TXPOWER_SET_CMD, strlen(TXPOWER_SET_CMD)) == 0)
+			ret = wl_iw_set_txpower(dev, info, (union iwreq_data *)dwrq, extra);
+#if defined(PNO_SUPPORT)
+		else if (strnicmp(extra, PNOSSIDCLR_SET_CMD, strlen(PNOSSIDCLR_SET_CMD)) == 0)
+			ret = wl_iw_set_pno_reset(dev, info, (union iwreq_data *)dwrq, extra);
+		else if (strnicmp(extra, PNOSETUP_SET_CMD, strlen(PNOSETUP_SET_CMD)) == 0)
+			ret = wl_iw_set_pno_set(dev, info, (union iwreq_data *)dwrq, extra);
+		else if (strnicmp(extra, PNOENABLE_SET_CMD, strlen(PNOENABLE_SET_CMD)) == 0)
+			ret = wl_iw_set_pno_enable(dev, info, (union iwreq_data *)dwrq, extra);
+#endif
+#if defined(CSCAN)
+		else if (strnicmp(extra, CSCAN_COMMAND, strlen(CSCAN_COMMAND)) == 0)
+			ret = wl_iw_set_cscan(dev, info, (union iwreq_data *)dwrq, extra);
+#endif
+#ifdef CUSTOMER_HW2
+		else if (strnicmp(extra, "POWERMODE", strlen("POWERMODE")) == 0)
+			ret = wl_iw_set_power_mode(dev, info, (union iwreq_data *)dwrq, extra);
+		else if (strnicmp(extra, "BTCOEXMODE", strlen("BTCOEXMODE")) == 0) {
+			WL_TRACE_COEX(("%s:got Framwrork cmd: 'BTCOEXMODE'\n", __FUNCTION__));
+			ret = wl_iw_set_btcoex_dhcp(dev, info, (union iwreq_data *)dwrq, extra);
+		}
+#else
+		else if (strnicmp(extra, "POWERMODE", strlen("POWERMODE")) == 0)
+			ret = wl_iw_set_btcoex_dhcp(dev, info, (union iwreq_data *)dwrq, extra);
+#endif
+		else if (strnicmp(extra, "GETPOWER", strlen("GETPOWER")) == 0)
+			ret = wl_iw_get_power_mode(dev, info, (union iwreq_data *)dwrq, extra);
+		else if (strnicmp(extra, RXFILTER_START_CMD, strlen(RXFILTER_START_CMD)) == 0)
+			ret = net_os_set_packet_filter(dev, 1);
+		else if (strnicmp(extra, RXFILTER_STOP_CMD, strlen(RXFILTER_STOP_CMD)) == 0)
+			ret = net_os_set_packet_filter(dev, 0);
+		else if (strnicmp(extra, RXFILTER_ADD_CMD, strlen(RXFILTER_ADD_CMD)) == 0) {
+			int filter_num = *(extra + strlen(RXFILTER_ADD_CMD) + 1) - '0';
+			ret = net_os_rxfilter_add_remove(dev, TRUE, filter_num);
+		}
+		else if (strnicmp(extra, RXFILTER_REMOVE_CMD, strlen(RXFILTER_REMOVE_CMD)) == 0) {
+			int filter_num = *(extra + strlen(RXFILTER_REMOVE_CMD) + 1) - '0';
+			ret = net_os_rxfilter_add_remove(dev, FALSE, filter_num);
+		}
+#ifdef SOFTAP
+#ifdef SOFTAP_TLV_CFG
+		else if (strnicmp(extra, SOFTAP_SET_CMD, strlen(SOFTAP_SET_CMD)) == 0) {
+		    wl_iw_softap_cfg_tlv(dev, info, (union iwreq_data *)dwrq, extra);
+		}
+#endif
+		else if (strnicmp(extra, "ASCII_CMD", strlen("ASCII_CMD")) == 0) {
+			wl_iw_process_private_ascii_cmd(dev, info, (union iwreq_data *)dwrq, extra);
+		} else if (strnicmp(extra, "AP_MAC_LIST_SET", strlen("AP_MAC_LIST_SET")) == 0) {
+			WL_SOFTAP(("penguin, set AP_MAC_LIST_SET\n"));
+			set_ap_mac_list(dev, (extra + PROFILE_OFFSET));
+		}
+#endif
+		else {
+			WL_TRACE(("Unknown PRIVATE command: %s: ignored\n", extra));
+			snprintf(extra, MAX_WX_STRING, "OK");
+			dwrq->length = strlen("OK") + 1;
+		}
+	}
+
+	net_os_wake_unlock(dev);
+
+	if (extra) {
+		if (copy_to_user(dwrq->pointer, extra, dwrq->length)) {
+			kfree(extra);
+			return -EFAULT;
+		}
+
+		kfree(extra);
+	}
+
+	return ret;
+}
+
+static const iw_handler wl_iw_handler[] =
+{
+	(iw_handler) wl_iw_config_commit,	
+	(iw_handler) wl_iw_get_name,		
+	(iw_handler) NULL,			
+	(iw_handler) NULL,			
+	(iw_handler) wl_iw_set_freq,		
+	(iw_handler) wl_iw_get_freq,		
+	(iw_handler) wl_iw_set_mode,		
+	(iw_handler) wl_iw_get_mode,		
+	(iw_handler) NULL,
+	(iw_handler) NULL,
+	(iw_handler) NULL,
+	(iw_handler) wl_iw_get_range,
+	(iw_handler) wl_iw_set_priv,
+	(iw_handler) NULL,
+	(iw_handler) NULL,			
+	(iw_handler) NULL,			
+	(iw_handler) wl_iw_set_spy,		
+	(iw_handler) wl_iw_get_spy,		
+	(iw_handler) NULL,			
+	(iw_handler) NULL,			
+	(iw_handler) wl_iw_set_wap,		
+	(iw_handler) wl_iw_get_wap,		
+#if WIRELESS_EXT > 17
+	(iw_handler) wl_iw_mlme,		
+#else
+	(iw_handler) NULL,			
+#endif
+#if defined(WL_IW_USE_ISCAN)
+	(iw_handler) wl_iw_iscan_get_aplist,	
+#else
+	(iw_handler) wl_iw_get_aplist,		
+#endif 
+#if WIRELESS_EXT > 13
+#if defined(WL_IW_USE_ISCAN)
+	(iw_handler) wl_iw_iscan_set_scan,	
+	(iw_handler) wl_iw_iscan_get_scan,	
+#else
+	(iw_handler) wl_iw_set_scan,		
+	(iw_handler) wl_iw_get_scan,		
+#endif
+#else	
+	(iw_handler) NULL,			
+	(iw_handler) NULL,			
+#endif	
+	(iw_handler) wl_iw_set_essid,		
+	(iw_handler) wl_iw_get_essid,		
+	(iw_handler) wl_iw_set_nick,
+	(iw_handler) wl_iw_get_nick,		
+	(iw_handler) NULL,			
+	(iw_handler) NULL,			
+	(iw_handler) wl_iw_set_rate,		
+	(iw_handler) wl_iw_get_rate,		
+	(iw_handler) wl_iw_set_rts,		
+	(iw_handler) wl_iw_get_rts,		
+	(iw_handler) wl_iw_set_frag,		
+	(iw_handler) wl_iw_get_frag,		
+	(iw_handler) wl_iw_set_txpow,		
+	(iw_handler) wl_iw_get_txpow,		
+#if WIRELESS_EXT > 10
+	(iw_handler) wl_iw_set_retry,		
+	(iw_handler) wl_iw_get_retry,		
+#endif 
+	(iw_handler) wl_iw_set_encode,		
+	(iw_handler) wl_iw_get_encode,		
+	(iw_handler) wl_iw_set_power,		
+	(iw_handler) wl_iw_get_power,		
+#if WIRELESS_EXT > 17
+	(iw_handler) NULL,			
+	(iw_handler) NULL,			
+	(iw_handler) wl_iw_set_wpaie,		
+	(iw_handler) wl_iw_get_wpaie,		
+	(iw_handler) wl_iw_set_wpaauth,		
+	(iw_handler) wl_iw_get_wpaauth,		
+	(iw_handler) wl_iw_set_encodeext,	
+	(iw_handler) wl_iw_get_encodeext,	
+#ifdef BCMWPA2
+	(iw_handler) wl_iw_set_pmksa,			
+#endif
+#endif 
+};
+
+#if WIRELESS_EXT > 12
+static const iw_handler wl_iw_priv_handler[] = {
+	NULL,
+	(iw_handler)wl_iw_set_active_scan,
+	NULL,
+	(iw_handler)wl_iw_get_rssi,
+	NULL,
+	(iw_handler)wl_iw_set_passive_scan,
+	NULL,
+	(iw_handler)wl_iw_get_link_speed,
+	NULL,
+	(iw_handler)wl_iw_get_macaddr,
+	NULL,
+	(iw_handler)wl_iw_control_wl_off,
+	NULL,
+	(iw_handler)wl_iw_control_wl_on,
+#ifdef SOFTAP
+	NULL,
+	(iw_handler)iwpriv_set_ap_config,
+
+	NULL,
+	(iw_handler)iwpriv_get_assoc_list,
+
+	NULL,
+	(iw_handler)iwpriv_set_mac_filters,
+
+	NULL,
+	(iw_handler)iwpriv_en_ap_bss,
+
+	NULL,
+	(iw_handler)iwpriv_wpasupp_loop_tst,
+
+	NULL,
+	(iw_handler)iwpriv_softap_stop,
+
+	NULL,
+	(iw_handler)iwpriv_fw_reload,
+
+	NULL,
+	(iw_handler)iwpriv_set_ap_sta_disassoc,
+#endif
+#if defined(CSCAN)
+
+	NULL,
+	(iw_handler)iwpriv_set_cscan
+#endif 	
+};
+
+static const struct iw_priv_args wl_iw_priv_args[] = {
+	{
+		WL_IW_SET_ACTIVE_SCAN,
+		0,
+		IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+		"SCAN-ACTIVE"
+	},
+	{
+		WL_IW_GET_RSSI,
+		0,
+		IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+		"RSSI"
+	},
+	{
+		WL_IW_SET_PASSIVE_SCAN,
+		0,
+		IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+		"SCAN-PASSIVE"
+	},
+	{
+		WL_IW_GET_LINK_SPEED,
+		0,
+		IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+		"LINKSPEED"
+	},
+	{
+		WL_IW_GET_CURR_MACADDR,
+		0,
+		IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+		"Macaddr"
+	},
+	{
+		WL_IW_SET_STOP,
+		0,
+		IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+		"STOP"
+	},
+	{
+		WL_IW_SET_START,
+		0,
+		IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+		"START"
+	},
+
+#ifdef SOFTAP
+	{
+		WL_SET_AP_CFG,
+		IW_PRIV_TYPE_CHAR |  256,
+		0,
+		"AP_SET_CFG"
+	},
+
+	{
+		WL_AP_STA_LIST,
+		IW_PRIV_TYPE_CHAR | 0,
+		IW_PRIV_TYPE_CHAR | 1024,
+		"AP_GET_STA_LIST"
+	},
+
+	{
+		WL_AP_MAC_FLTR,
+		IW_PRIV_TYPE_CHAR | 256,
+		IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 0,
+		"AP_SET_MAC_FLTR"
+	},
+
+	{
+		WL_AP_BSS_START,
+		0,
+		IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+		"AP_BSS_START"
+	},
+
+	{
+		AP_LPB_CMD,
+		IW_PRIV_TYPE_CHAR | 256,
+		IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 0,
+		"AP_LPB_CMD"
+	},
+
+	{
+		WL_AP_STOP,
+		IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 0,
+		IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 0,
+		"AP_BSS_STOP"
+	},
+
+	{
+		WL_FW_RELOAD,
+		IW_PRIV_TYPE_CHAR | 256,
+		IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 0,
+		"WL_FW_RELOAD"
+	},
+
+	{
+		WL_AP_STA_DISASSOC,
+		IW_PRIV_TYPE_CHAR | 256,
+		IW_PRIV_TYPE_CHAR | 0,
+		"AP_STA_DISASSOC"
+	},
+#endif
+#if defined(CSCAN)
+	{
+		WL_COMBO_SCAN,
+		IW_PRIV_TYPE_CHAR | 1024,
+		0,
+		"CSCAN"
+	},
+#endif
+};
+
+const struct iw_handler_def wl_iw_handler_def =
+{
+	.num_standard = ARRAYSIZE(wl_iw_handler),
+	.standard = (iw_handler *) wl_iw_handler,
+	.num_private = ARRAYSIZE(wl_iw_priv_handler),
+	.num_private_args = ARRAY_SIZE(wl_iw_priv_args),
+	.private = (iw_handler *)wl_iw_priv_handler,
+	.private_args = (void *) wl_iw_priv_args,
+
+#if WIRELESS_EXT >= 19
+	get_wireless_stats: dhd_get_wireless_stats,
+#endif 
+};
+#endif 
+
+
+int wl_iw_ioctl(
+	struct net_device *dev,
+	struct ifreq *rq,
+	int cmd
+)
+{
+	struct iwreq *wrq = (struct iwreq *) rq;
+	struct iw_request_info info;
+	iw_handler handler;
+	char *extra = NULL;
+	int token_size = 1, max_tokens = 0, ret = 0;
+
+	net_os_wake_lock(dev);
+
+	WL_TRACE(("%s: cmd:%x alled via dhd->do_ioctl()entry point\n", __FUNCTION__, cmd));
+	if (cmd < SIOCIWFIRST ||
+		IW_IOCTL_IDX(cmd) >= ARRAYSIZE(wl_iw_handler) ||
+		!(handler = wl_iw_handler[IW_IOCTL_IDX(cmd)])) {
+			WL_ERROR(("%s: error in cmd=%x : not supported\n", __FUNCTION__, cmd));
+			net_os_wake_unlock(dev);
+			return -EOPNOTSUPP;
+	}
+
+	switch (cmd) {
+
+	case SIOCSIWESSID:
+	case SIOCGIWESSID:
+	case SIOCSIWNICKN:
+	case SIOCGIWNICKN:
+		max_tokens = IW_ESSID_MAX_SIZE + 1;
+		break;
+
+	case SIOCSIWENCODE:
+	case SIOCGIWENCODE:
+#if WIRELESS_EXT > 17
+	case SIOCSIWENCODEEXT:
+	case SIOCGIWENCODEEXT:
+#endif
+		max_tokens = wrq->u.data.length;
+		break;
+
+	case SIOCGIWRANGE:
+		max_tokens = sizeof(struct iw_range) + 500;
+		break;
+
+	case SIOCGIWAPLIST:
+		token_size = sizeof(struct sockaddr) + sizeof(struct iw_quality);
+		max_tokens = IW_MAX_AP;
+		break;
+
+#if WIRELESS_EXT > 13
+	case SIOCGIWSCAN:
+#if defined(WL_IW_USE_ISCAN)
+	if (g_iscan)
+		max_tokens = wrq->u.data.length;
+	else
+#endif
+		max_tokens = IW_SCAN_MAX_DATA;
+		break;
+#endif 
+
+	case SIOCSIWSPY:
+		token_size = sizeof(struct sockaddr);
+		max_tokens = IW_MAX_SPY;
+		break;
+
+	case SIOCGIWSPY:
+		token_size = sizeof(struct sockaddr) + sizeof(struct iw_quality);
+		max_tokens = IW_MAX_SPY;
+		break;
+
+#if WIRELESS_EXT > 17
+	case SIOCSIWPMKSA:
+	case SIOCSIWGENIE:
+#endif 
+	case SIOCSIWPRIV:
+		max_tokens = wrq->u.data.length;
+		break;
+	}
+
+	if (max_tokens && wrq->u.data.pointer) {
+		if (wrq->u.data.length > max_tokens) {
+			WL_ERROR(("%s: error in cmd=%x wrq->u.data.length=%d  > max_tokens=%d\n", \
+				__FUNCTION__, cmd, wrq->u.data.length, max_tokens));
+			ret = -E2BIG;
+			goto wl_iw_ioctl_done;
+		}
+		if (!(extra = kmalloc(max_tokens * token_size, GFP_KERNEL))) {
+			ret = -ENOMEM;
+			goto wl_iw_ioctl_done;
+		}
+
+		if (copy_from_user(extra, wrq->u.data.pointer, wrq->u.data.length * token_size)) {
+			kfree(extra);
+			ret = -EFAULT;
+			goto wl_iw_ioctl_done;
+		}
+	}
+
+	info.cmd = cmd;
+	info.flags = 0;
+
+	ret = handler(dev, &info, &wrq->u, extra);
+
+	if (extra) {
+		if (copy_to_user(wrq->u.data.pointer, extra, wrq->u.data.length * token_size)) {
+			kfree(extra);
+			ret = -EFAULT;
+			goto wl_iw_ioctl_done;
+		}
+
+		kfree(extra);
+	}
+
+wl_iw_ioctl_done:
+
+	net_os_wake_unlock(dev);
+
+	return ret;
+}
+
+
+bool
+wl_iw_conn_status_str(uint32 event_type, uint32 status, uint32 reason,
+	char* stringBuf, uint buflen)
+{
+	typedef struct conn_fail_event_map_t {
+		uint32 inEvent;			
+		uint32 inStatus;		
+		uint32 inReason;		
+		const char* outName;	
+		const char* outCause;	
+	} conn_fail_event_map_t;
+
+	
+#	define WL_IW_DONT_CARE	9999
+	const conn_fail_event_map_t event_map [] = {
+		
+		
+		{WLC_E_SET_SSID,     WLC_E_STATUS_SUCCESS,   WL_IW_DONT_CARE,
+		"Conn", "Success"},
+		{WLC_E_SET_SSID,     WLC_E_STATUS_NO_NETWORKS, WL_IW_DONT_CARE,
+		"Conn", "NoNetworks"},
+		{WLC_E_SET_SSID,     WLC_E_STATUS_FAIL,      WL_IW_DONT_CARE,
+		"Conn", "ConfigMismatch"},
+		{WLC_E_PRUNE,        WL_IW_DONT_CARE,        WLC_E_PRUNE_ENCR_MISMATCH,
+		"Conn", "EncrypMismatch"},
+		{WLC_E_PRUNE,        WL_IW_DONT_CARE,        WLC_E_RSN_MISMATCH,
+		"Conn", "RsnMismatch"},
+		{WLC_E_AUTH,         WLC_E_STATUS_TIMEOUT,   WL_IW_DONT_CARE,
+		"Conn", "AuthTimeout"},
+		{WLC_E_AUTH,         WLC_E_STATUS_FAIL,      WL_IW_DONT_CARE,
+		"Conn", "AuthFail"},
+		{WLC_E_AUTH,         WLC_E_STATUS_NO_ACK,    WL_IW_DONT_CARE,
+		"Conn", "AuthNoAck"},
+		{WLC_E_REASSOC,      WLC_E_STATUS_FAIL,      WL_IW_DONT_CARE,
+		"Conn", "ReassocFail"},
+		{WLC_E_REASSOC,      WLC_E_STATUS_TIMEOUT,   WL_IW_DONT_CARE,
+		"Conn", "ReassocTimeout"},
+		{WLC_E_REASSOC,      WLC_E_STATUS_ABORT,     WL_IW_DONT_CARE,
+		"Conn", "ReassocAbort"},
+		{WLC_E_PSK_SUP,      WLC_SUP_KEYED,          WL_IW_DONT_CARE,
+		"Sup", "ConnSuccess"},
+		{WLC_E_PSK_SUP,      WL_IW_DONT_CARE,        WL_IW_DONT_CARE,
+		"Sup", "WpaHandshakeFail"},
+		{WLC_E_DEAUTH_IND,   WL_IW_DONT_CARE,        WL_IW_DONT_CARE,
+		"Conn", "Deauth"},
+		{WLC_E_DISASSOC_IND, WL_IW_DONT_CARE,        WL_IW_DONT_CARE,
+		"Conn", "DisassocInd"},
+		{WLC_E_DISASSOC,     WL_IW_DONT_CARE,        WL_IW_DONT_CARE,
+		"Conn", "Disassoc"}
+	};
+
+	const char* name = "";
+	const char* cause = NULL;
+	int i;
+
+	
+	for (i = 0;  i < sizeof(event_map)/sizeof(event_map[0]);  i++) {
+		const conn_fail_event_map_t* row = &event_map[i];
+		if (row->inEvent == event_type &&
+		    (row->inStatus == status || row->inStatus == WL_IW_DONT_CARE) &&
+		    (row->inReason == reason || row->inReason == WL_IW_DONT_CARE)) {
+			name = row->outName;
+			cause = row->outCause;
+			break;
+		}
+	}
+
+	
+	if (cause) {
+		memset(stringBuf, 0, buflen);
+		snprintf(stringBuf, buflen, "%s %s %02d %02d",
+			name, cause, status, reason);
+		WL_INFORM(("Connection status: %s\n", stringBuf));
+		return TRUE;
+	} else {
+		return FALSE;
+	}
+}
+
+#if WIRELESS_EXT > 14
+
+static bool
+wl_iw_check_conn_fail(wl_event_msg_t *e, char* stringBuf, uint buflen)
+{
+	uint32 event = ntoh32(e->event_type);
+	uint32 status =  ntoh32(e->status);
+	uint32 reason =  ntoh32(e->reason);
+
+	if (wl_iw_conn_status_str(event, status, reason, stringBuf, buflen)) {
+		return TRUE;
+	}
+	else
+		return FALSE;
+}
+#endif
+
+#ifndef IW_CUSTOM_MAX
+#define IW_CUSTOM_MAX 256 
+#endif
+
+void
+wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data)
+{
+#if WIRELESS_EXT > 13
+	union iwreq_data wrqu;
+	char extra[IW_CUSTOM_MAX + 1];
+	int cmd = 0;
+	uint32 event_type = ntoh32(e->event_type);
+	uint16 flags =  ntoh16(e->flags);
+	uint32 datalen = ntoh32(e->datalen);
+	uint32 status =  ntoh32(e->status);
+	uint32 toto;
+#if defined(ROAM_NOT_USED)
+	static uint32 roam_no_success = 0;
+	static bool roam_no_success_send = FALSE;
+#endif
+	memset(&wrqu, 0, sizeof(wrqu));
+	memset(extra, 0, sizeof(extra));
+
+	if (!dev) {
+		WL_ERROR(("%s: dev is null\n", __FUNCTION__));
+		return;
+	}
+
+	net_os_wake_lock(dev);
+
+	WL_TRACE(("%s: dev=%s event=%d \n", __FUNCTION__, dev->name, event_type));
+
+	switch (event_type) {
+
+	case WLC_E_RELOAD:
+		WL_ERROR(("%s: Firmware ERROR %d\n", __FUNCTION__, status));
+		net_os_send_hang_message(dev);
+		goto wl_iw_event_end;
+
+#if defined(SOFTAP)
+	case WLC_E_PRUNE:
+		if (ap_cfg_running) {
+			char *macaddr = (char *)&e->addr;
+			WL_SOFTAP(("PRUNE received, %02X:%02X:%02X:%02X:%02X:%02X!\n",
+				macaddr[0], macaddr[1], macaddr[2], macaddr[3], \
+				macaddr[4], macaddr[5]));
+
+			if (ap_macmode) {
+				int i;
+				for (i = 0; i < ap_black_list.count; i++) {
+					if (!bcmp(macaddr, &ap_black_list.ea[i], \
+						sizeof(struct ether_addr))) {
+						WL_SOFTAP(("mac in black list, ignore it\n"));
+						break;
+					}
+				}
+
+				if (i == ap_black_list.count) {
+					char mac_buf[32] = {0};
+					sprintf(mac_buf, "STA_BLOCK %02X:%02X:%02X:%02X:%02X:%02X",
+						macaddr[0], macaddr[1], macaddr[2],
+						macaddr[3], macaddr[4], macaddr[5]);
+					wl_iw_send_priv_event(priv_dev, mac_buf);
+				}
+			}
+		}
+		break;
+#endif
+	case WLC_E_TXFAIL:
+		cmd = IWEVTXDROP;
+		memcpy(wrqu.addr.sa_data, &e->addr, ETHER_ADDR_LEN);
+		wrqu.addr.sa_family = ARPHRD_ETHER;
+		break;
+#if WIRELESS_EXT > 14
+	case WLC_E_JOIN:
+	case WLC_E_ASSOC_IND:
+	case WLC_E_REASSOC_IND:
+#if defined(SOFTAP)
+		WL_SOFTAP(("STA connect received %d\n", event_type));
+		if (ap_cfg_running) {
+			wl_iw_send_priv_event(priv_dev, "STA_JOIN");
+			goto wl_iw_event_end;
+		}
+#endif
+		memcpy(wrqu.addr.sa_data, &e->addr, ETHER_ADDR_LEN);
+		wrqu.addr.sa_family = ARPHRD_ETHER;
+		cmd = IWEVREGISTERED;
+		break;
+	case WLC_E_ROAM:
+		if (status == WLC_E_STATUS_SUCCESS) {
+			WL_ASSOC(("%s: WLC_E_ROAM: success\n", __FUNCTION__));
+#if defined(ROAM_NOT_USED)
+			roam_no_success_send = FALSE;
+			roam_no_success = 0;
+#endif
+			goto wl_iw_event_end;
+		}
+#if defined(ROAM_NOT_USED)
+		else if (status == WLC_E_STATUS_NO_NETWORKS) {
+			roam_no_success++;
+			if ((roam_no_success == 5) && (roam_no_success_send == FALSE)) {
+				roam_no_success_send = TRUE;
+				bzero(wrqu.addr.sa_data, ETHER_ADDR_LEN);
+				bzero(&extra, ETHER_ADDR_LEN);
+				cmd = SIOCGIWAP;
+				WL_ERROR(("%s  ROAMING did not succeeded , send Link Down\n", \
+					__FUNCTION__));
+			} else {
+				WL_TRACE(("##### ROAMING did not succeeded %d\n", roam_no_success));
+				goto wl_iw_event_end;
+			}
+		}
+#endif
+		break;
+	case WLC_E_DEAUTH_IND:
+	case WLC_E_DISASSOC_IND:
+#if defined(SOFTAP)
+		WL_SOFTAP(("STA disconnect received %d\n", event_type));
+		if (ap_cfg_running) {
+			wl_iw_send_priv_event(priv_dev, "STA_LEAVE");
+			goto wl_iw_event_end;
+		}
+#endif
+		cmd = SIOCGIWAP;
+		bzero(wrqu.addr.sa_data, ETHER_ADDR_LEN);
+		wrqu.addr.sa_family = ARPHRD_ETHER;
+		bzero(&extra, ETHER_ADDR_LEN);
+		break;
+	case WLC_E_LINK:
+	case WLC_E_NDIS_LINK:
+		cmd = SIOCGIWAP;
+		if (!(flags & WLC_EVENT_MSG_LINK)) {
+#ifdef SOFTAP
+#ifdef AP_ONLY
+		if (ap_cfg_running) {
+#else
+		if (ap_cfg_running && !strncmp(dev->name, "wl0.1", 5)) {
+#endif	
+				WL_SOFTAP(("AP DOWN %d\n", event_type));
+				wl_iw_send_priv_event(priv_dev, "AP_DOWN");
+			} else {
+				WL_TRACE(("STA_Link Down\n"));
+				g_ss_cache_ctrl.m_link_down = 1;
+			}
+#else
+			g_ss_cache_ctrl.m_link_down = 1;
+#endif
+			WL_TRACE(("Link Down\n"));
+
+			bzero(wrqu.addr.sa_data, ETHER_ADDR_LEN);
+			bzero(&extra, ETHER_ADDR_LEN);
+		}
+		else {
+			memcpy(wrqu.addr.sa_data, &e->addr, ETHER_ADDR_LEN);
+			g_ss_cache_ctrl.m_link_down = 0;
+
+			memcpy(g_ss_cache_ctrl.m_active_bssid, &e->addr, ETHER_ADDR_LEN);
+
+#ifdef SOFTAP
+#ifdef AP_ONLY
+			if (ap_cfg_running) {
+#else
+			if (ap_cfg_running && !strncmp(dev->name, "wl0.1", 5)) {
+#endif
+				WL_SOFTAP(("AP UP %d\n", event_type));
+				wl_iw_send_priv_event(priv_dev, "AP_UP");
+			} else {
+				WL_TRACE(("STA_LINK_UP\n"));
+#if defined(ROAM_NOT_USED)
+				roam_no_success_send = FALSE;
+				roam_no_success = 0;
+#endif
+			}
+#endif
+			WL_TRACE(("Link UP\n"));
+
+		}
+		net_os_wake_lock_timeout_enable(dev);
+		wrqu.addr.sa_family = ARPHRD_ETHER;
+		break;
+	case WLC_E_ACTION_FRAME:
+		cmd = IWEVCUSTOM;
+		if (datalen + 1 <= sizeof(extra)) {
+			wrqu.data.length = datalen + 1;
+			extra[0] = WLC_E_ACTION_FRAME;
+			memcpy(&extra[1], data, datalen);
+			WL_TRACE(("WLC_E_ACTION_FRAME len %d \n", wrqu.data.length));
+		}
+		break;
+
+	case WLC_E_ACTION_FRAME_COMPLETE:
+		cmd = IWEVCUSTOM;
+		memcpy(&toto, data, 4);
+		if (sizeof(status) + 1 <= sizeof(extra)) {
+			wrqu.data.length = sizeof(status) + 1;
+			extra[0] = WLC_E_ACTION_FRAME_COMPLETE;
+			memcpy(&extra[1], &status, sizeof(status));
+			printf("wl_iw_event status %d PacketId %d \n", status, toto);
+			printf("WLC_E_ACTION_FRAME_COMPLETE len %d \n", wrqu.data.length);
+		}
+		break;
+#endif 
+#if WIRELESS_EXT > 17
+	case WLC_E_MIC_ERROR: {
+		struct	iw_michaelmicfailure  *micerrevt = (struct  iw_michaelmicfailure  *)&extra;
+		cmd = IWEVMICHAELMICFAILURE;
+		wrqu.data.length = sizeof(struct iw_michaelmicfailure);
+		if (flags & WLC_EVENT_MSG_GROUP)
+			micerrevt->flags |= IW_MICFAILURE_GROUP;
+		else
+			micerrevt->flags |= IW_MICFAILURE_PAIRWISE;
+		memcpy(micerrevt->src_addr.sa_data, &e->addr, ETHER_ADDR_LEN);
+		micerrevt->src_addr.sa_family = ARPHRD_ETHER;
+
+		break;
+	}
+#ifdef BCMWPA2
+	case WLC_E_PMKID_CACHE: {
+		if (data)
+		{
+			struct iw_pmkid_cand *iwpmkidcand = (struct iw_pmkid_cand *)&extra;
+			pmkid_cand_list_t *pmkcandlist;
+			pmkid_cand_t	*pmkidcand;
+			int count;
+
+			cmd = IWEVPMKIDCAND;
+			pmkcandlist = data;
+			count = ntoh32_ua((uint8 *)&pmkcandlist->npmkid_cand);
+			ASSERT(count >= 0);
+			wrqu.data.length = sizeof(struct iw_pmkid_cand);
+			pmkidcand = pmkcandlist->pmkid_cand;
+			while (count) {
+				bzero(iwpmkidcand, sizeof(struct iw_pmkid_cand));
+				if (pmkidcand->preauth)
+					iwpmkidcand->flags |= IW_PMKID_CAND_PREAUTH;
+				bcopy(&pmkidcand->BSSID, &iwpmkidcand->bssid.sa_data,
+					ETHER_ADDR_LEN);
+#ifndef SANDGATE2G
+				wireless_send_event(dev, cmd, &wrqu, extra);
+#endif
+				pmkidcand++;
+				count--;
+			}
+		}
+		goto wl_iw_event_end;
+	}
+#endif 
+#endif 
+
+	case WLC_E_SCAN_COMPLETE:
+#if defined(WL_IW_USE_ISCAN)
+		if ((g_iscan) && (g_iscan->sysioc_pid >= 0) &&
+			(g_iscan->iscan_state != ISCAN_STATE_IDLE))
+		{
+			up(&g_iscan->sysioc_sem);
+		} else {
+			cmd = SIOCGIWSCAN;
+			wrqu.data.length = strlen(extra);
+			WL_TRACE(("Event WLC_E_SCAN_COMPLETE from specific scan %d\n", \
+				g_iscan->iscan_state));
+		}
+#else
+		cmd = SIOCGIWSCAN;
+		wrqu.data.length = strlen(extra);
+		WL_TRACE(("Event WLC_E_SCAN_COMPLETE\n"));
+#endif
+		break;
+
+	case WLC_E_PFN_NET_FOUND:
+	{
+		wlc_ssid_t	* ssid;
+		ssid = (wlc_ssid_t *)data;
+		WL_TRACE(("%s Event WLC_E_PFN_NET_FOUND, send %s up : find %s len=%d\n", \
+			__FUNCTION__, PNO_EVENT_UP, ssid->SSID, ssid->SSID_len));
+		net_os_wake_lock_timeout_enable(dev);
+		cmd = IWEVCUSTOM;
+		memset(&wrqu, 0, sizeof(wrqu));
+		strcpy(extra, PNO_EVENT_UP);
+		wrqu.data.length = strlen(extra);
+	}
+	break;
+
+	default:
+		
+		WL_TRACE(("Unknown Event %d: ignoring\n", event_type));
+		break;
+	}
+#ifndef SANDGATE2G
+	if (cmd) {
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 31))
+		if (cmd == SIOCGIWSCAN)
+			wireless_send_event(dev, cmd, &wrqu, NULL);
+		else
+#endif
+		wireless_send_event(dev, cmd, &wrqu, extra);
+	}
+#endif
+
+#if WIRELESS_EXT > 14
+	memset(extra, 0, sizeof(extra));
+	if (wl_iw_check_conn_fail(e, extra, sizeof(extra))) {
+		cmd = IWEVCUSTOM;
+		wrqu.data.length = strlen(extra);
+#ifndef SANDGATE2G
+		wireless_send_event(dev, cmd, &wrqu, extra);
+#endif
+	}
+#endif
+wl_iw_event_end:
+	net_os_wake_unlock(dev);
+#endif
+}
+
+int wl_iw_get_wireless_stats(struct net_device *dev, struct iw_statistics *wstats)
+{
+	int res = 0;
+	wl_cnt_t cnt;
+	int phy_noise;
+	int rssi;
+	scb_val_t scb_val;
+
+	phy_noise = 0;
+	if ((res = dev_wlc_ioctl(dev, WLC_GET_PHY_NOISE, &phy_noise, sizeof(phy_noise))))
+		goto done;
+
+	phy_noise = dtoh32(phy_noise);
+	WL_TRACE(("wl_iw_get_wireless_stats phy noise=%d\n", phy_noise));
+
+	bzero(&scb_val, sizeof(scb_val_t));
+	if ((res = dev_wlc_ioctl(dev, WLC_GET_RSSI, &scb_val, sizeof(scb_val_t))))
+		goto done;
+
+	rssi = dtoh32(scb_val.val);
+	WL_TRACE(("wl_iw_get_wireless_stats rssi=%d\n", rssi));
+	if (rssi <= WL_IW_RSSI_NO_SIGNAL)
+		wstats->qual.qual = 0;
+	else if (rssi <= WL_IW_RSSI_VERY_LOW)
+		wstats->qual.qual = 1;
+	else if (rssi <= WL_IW_RSSI_LOW)
+		wstats->qual.qual = 2;
+	else if (rssi <= WL_IW_RSSI_GOOD)
+		wstats->qual.qual = 3;
+	else if (rssi <= WL_IW_RSSI_VERY_GOOD)
+		wstats->qual.qual = 4;
+	else
+		wstats->qual.qual = 5;
+
+	
+	wstats->qual.level = 0x100 + rssi;
+	wstats->qual.noise = 0x100 + phy_noise;
+#if WIRELESS_EXT > 18
+	wstats->qual.updated |= (IW_QUAL_ALL_UPDATED | IW_QUAL_DBM);
+#else
+	wstats->qual.updated |= 7;
+#endif 
+
+#if WIRELESS_EXT > 11
+	WL_TRACE(("wl_iw_get_wireless_stats counters=%d\n", (int)sizeof(wl_cnt_t)));
+
+	memset(&cnt, 0, sizeof(wl_cnt_t));
+	res = dev_wlc_bufvar_get(dev, "counters", (char *)&cnt, sizeof(wl_cnt_t));
+	if (res)
+	{
+		WL_ERROR(("wl_iw_get_wireless_stats counters failed error=%d\n", res));
+		goto done;
+	}
+
+	cnt.version = dtoh16(cnt.version);
+	if (cnt.version != WL_CNT_T_VERSION) {
+		WL_TRACE(("\tIncorrect version of counters struct: expected %d; got %d\n",
+			WL_CNT_T_VERSION, cnt.version));
+		goto done;
+	}
+
+	wstats->discard.nwid = 0;
+	wstats->discard.code = dtoh32(cnt.rxundec);
+	wstats->discard.fragment = dtoh32(cnt.rxfragerr);
+	wstats->discard.retries = dtoh32(cnt.txfail);
+	wstats->discard.misc = dtoh32(cnt.rxrunt) + dtoh32(cnt.rxgiant);
+	wstats->miss.beacon = 0;
+
+	WL_TRACE(("wl_iw_get_wireless_stats counters txframe=%d txbyte=%d\n",
+		dtoh32(cnt.txframe), dtoh32(cnt.txbyte)));
+	WL_TRACE(("wl_iw_get_wireless_stats counters rxfrmtoolong=%d\n", dtoh32(cnt.rxfrmtoolong)));
+	WL_TRACE(("wl_iw_get_wireless_stats counters rxbadplcp=%d\n", dtoh32(cnt.rxbadplcp)));
+	WL_TRACE(("wl_iw_get_wireless_stats counters rxundec=%d\n", dtoh32(cnt.rxundec)));
+	WL_TRACE(("wl_iw_get_wireless_stats counters rxfragerr=%d\n", dtoh32(cnt.rxfragerr)));
+	WL_TRACE(("wl_iw_get_wireless_stats counters txfail=%d\n", dtoh32(cnt.txfail)));
+	WL_TRACE(("wl_iw_get_wireless_stats counters rxrunt=%d\n", dtoh32(cnt.rxrunt)));
+	WL_TRACE(("wl_iw_get_wireless_stats counters rxgiant=%d\n", dtoh32(cnt.rxgiant)));
+
+#endif 
+
+done:
+	return res;
+}
+static void
+wl_iw_bt_flag_set(
+	struct net_device *dev,
+	bool set)
+{
+#if defined(BT_DHCP_USE_FLAGS)
+	char buf_flag7_dhcp_on[8] = { 7, 00, 00, 00, 0x1, 0x0, 0x00, 0x00 };
+	char buf_flag7_default[8]   = { 7, 00, 00, 00, 0x0, 0x00, 0x00, 0x00};
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+	rtnl_lock();
+#endif
+
+#if defined(BT_DHCP_eSCO_FIX)
+	set_btc_esco_params(dev, set);
+#endif
+
+#if defined(BT_DHCP_USE_FLAGS)
+	WL_TRACE_COEX(("WI-FI priority boost via bt flags, set:%d\n", set));
+	if (set == TRUE) {
+		dev_wlc_bufvar_set(dev, "btc_flags",
+					(char *)&buf_flag7_dhcp_on[0], sizeof(buf_flag7_dhcp_on));
+	}
+	else  {
+		dev_wlc_bufvar_set(dev, "btc_flags",
+					(char *)&buf_flag7_default[0], sizeof(buf_flag7_default));
+	}
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+	rtnl_unlock();
+#endif
+}
+
+static void
+wl_iw_bt_timerfunc(ulong data)
+{
+	bt_info_t  *bt_local = (bt_info_t *)data;
+	bt_local->timer_on = 0;
+	WL_TRACE(("%s\n", __FUNCTION__));
+
+	up(&bt_local->bt_sem);
+}
+
+static int
+_bt_dhcp_sysioc_thread(void *data)
+{
+	DAEMONIZE("dhcp_sysioc");
+
+	while (down_interruptible(&g_bt->bt_sem) == 0) {
+
+		net_os_wake_lock(g_bt->dev);
+
+		if (g_bt->timer_on) {
+			g_bt->timer_on = 0;
+			del_timer_sync(&g_bt->timer);
+		}
+
+		switch (g_bt->bt_state) {
+			case BT_DHCP_START:
+				WL_TRACE_COEX(("%s bt_dhcp stm: started \n", __FUNCTION__));
+				g_bt->bt_state = BT_DHCP_OPPORTUNITY_WINDOW;
+				mod_timer(&g_bt->timer, jiffies + BT_DHCP_OPPORTUNITY_WINDOW_TIME*HZ/1000);
+				g_bt->timer_on = 1;
+				break;
+
+			case BT_DHCP_OPPORTUNITY_WINDOW:
+				if (g_bt->dhcp_done) {
+					WL_TRACE_COEX(("%s DHCP Done before T1 expiration\n", \
+						__FUNCTION__));
+					g_bt->bt_state = BT_DHCP_IDLE;
+					g_bt->timer_on = 0;
+					break;
+				}
+
+				WL_TRACE_COEX(("%s DHCP T1:%d expired\n", \
+						__FUNCTION__, BT_DHCP_OPPORTUNITY_WINDOW_TIME));
+				if (g_bt->dev) wl_iw_bt_flag_set(g_bt->dev, TRUE);
+				g_bt->bt_state = BT_DHCP_FLAG_FORCE_TIMEOUT;
+				mod_timer(&g_bt->timer, jiffies + BT_DHCP_FLAG_FORCE_TIME*HZ/1000);
+				g_bt->timer_on = 1;
+				break;
+
+			case BT_DHCP_FLAG_FORCE_TIMEOUT:
+				if (g_bt->dhcp_done) {
+					WL_TRACE_COEX(("%s DHCP Done before T2 expiration\n", \
+						__FUNCTION__));
+				} else {
+					WL_TRACE_COEX(("%s DHCP wait interval T2:%d msec expired\n",
+						__FUNCTION__, BT_DHCP_FLAG_FORCE_TIME));
+				}
+
+				if (g_bt->dev)  wl_iw_bt_flag_set(g_bt->dev, FALSE);
+				g_bt->bt_state = BT_DHCP_IDLE;
+				g_bt->timer_on = 0;
+				break;
+
+			default:
+				WL_ERROR(("%s error g_status=%d !!!\n", __FUNCTION__, \
+				          g_bt->bt_state));
+				if (g_bt->dev) wl_iw_bt_flag_set(g_bt->dev, FALSE);
+				g_bt->bt_state = BT_DHCP_IDLE;
+				g_bt->timer_on = 0;
+				break;
+		}
+
+		net_os_wake_unlock(g_bt->dev);
+	}
+
+	if (g_bt->timer_on) {
+		g_bt->timer_on = 0;
+		del_timer_sync(&g_bt->timer);
+	}
+
+	complete_and_exit(&g_bt->bt_exited, 0);
+}
+
+static void
+wl_iw_bt_release(void)
+{
+	bt_info_t *bt_local = g_bt;
+
+	if (!bt_local) {
+		return;
+	}
+
+	if (bt_local->bt_pid >= 0) {
+		KILL_PROC(bt_local->bt_pid, SIGTERM);
+		wait_for_completion(&bt_local->bt_exited);
+	}
+	kfree(bt_local);
+	g_bt = NULL;
+}
+
+static int
+wl_iw_bt_init(struct net_device *dev)
+{
+	bt_info_t *bt_dhcp = NULL;
+
+	bt_dhcp = kmalloc(sizeof(bt_info_t), GFP_KERNEL);
+	if (!bt_dhcp)
+		return -ENOMEM;
+
+	memset(bt_dhcp, 0, sizeof(bt_info_t));
+	bt_dhcp->bt_pid = -1;
+	g_bt = bt_dhcp;
+	bt_dhcp->dev = dev;
+	bt_dhcp->bt_state = BT_DHCP_IDLE;
+
+	
+	bt_dhcp->timer_ms    = 10;
+	init_timer(&bt_dhcp->timer);
+	bt_dhcp->timer.data = (ulong)bt_dhcp;
+	bt_dhcp->timer.function = wl_iw_bt_timerfunc;
+
+	sema_init(&bt_dhcp->bt_sem, 0);
+	init_completion(&bt_dhcp->bt_exited);
+	bt_dhcp->bt_pid = kernel_thread(_bt_dhcp_sysioc_thread, bt_dhcp, 0);
+	if (bt_dhcp->bt_pid < 0) {
+		WL_ERROR(("Failed in %s\n", __FUNCTION__));
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+int wl_iw_attach(struct net_device *dev, void *dhdp)
+{
+	int params_size;
+	wl_iw_t *iw;
+#if defined(WL_IW_USE_ISCAN)
+	iscan_info_t *iscan = NULL;
+#endif
+
+	mutex_init(&wl_cache_lock);
+
+#if defined(WL_IW_USE_ISCAN)
+	if (!dev)
+		return 0;
+
+	memset(&g_wl_iw_params, 0, sizeof(wl_iw_extra_params_t));
+
+#ifdef CSCAN
+	params_size = (WL_SCAN_PARAMS_FIXED_SIZE + OFFSETOF(wl_iscan_params_t, params)) +
+	    (WL_NUMCHANNELS * sizeof(uint16)) + WL_SCAN_PARAMS_SSID_MAX * sizeof(wlc_ssid_t);
+#else
+	params_size = (WL_SCAN_PARAMS_FIXED_SIZE + OFFSETOF(wl_iscan_params_t, params));
+#endif 
+	iscan = kmalloc(sizeof(iscan_info_t), GFP_KERNEL);
+	if (!iscan)
+		return -ENOMEM;
+	memset(iscan, 0, sizeof(iscan_info_t));
+	
+	iscan->iscan_ex_params_p = (wl_iscan_params_t*)kmalloc(params_size, GFP_KERNEL);
+	if (!iscan->iscan_ex_params_p)
+		return -ENOMEM;
+	iscan->iscan_ex_param_size = params_size;
+	iscan->sysioc_pid = -1;
+	
+	g_iscan = iscan;
+	iscan->dev = dev;
+	iscan->iscan_state = ISCAN_STATE_IDLE;
+#if defined(CONFIG_FIRST_SCAN)
+	g_first_broadcast_scan = BROADCAST_SCAN_FIRST_IDLE;
+	g_first_counter_scans = 0;
+	g_iscan->scan_flag = 0;
+#endif
+
+	iscan->timer_ms    = 8000;
+	init_timer(&iscan->timer);
+	iscan->timer.data = (ulong)iscan;
+	iscan->timer.function = wl_iw_timerfunc;
+
+	sema_init(&iscan->sysioc_sem, 0);
+	init_completion(&iscan->sysioc_exited);
+	iscan->sysioc_pid = kernel_thread(_iscan_sysioc_thread, iscan, 0);
+	if (iscan->sysioc_pid < 0)
+		return -ENOMEM;
+#endif
+
+	iw = *(wl_iw_t **)netdev_priv(dev);
+	iw->pub = (dhd_pub_t *)dhdp;
+#ifdef SOFTAP
+	priv_dev = dev;
+#endif 
+	g_scan = NULL;
+
+	g_scan = (void *)kmalloc(G_SCAN_RESULTS, GFP_KERNEL);
+	if (!g_scan)
+		return -ENOMEM;
+
+	memset(g_scan, 0, G_SCAN_RESULTS);
+	g_scan_specified_ssid = 0;
+
+#if !defined(CSCAN)
+	wl_iw_init_ss_cache_ctrl();
+#endif
+
+	wl_iw_bt_init(dev);
+
+	return 0;
+}
+
+void wl_iw_detach(void)
+{
+#if defined(WL_IW_USE_ISCAN)
+	iscan_buf_t  *buf;
+	iscan_info_t *iscan = g_iscan;
+
+	if (!iscan)
+		return;
+	if (iscan->sysioc_pid >= 0) {
+		KILL_PROC(iscan->sysioc_pid, SIGTERM);
+		wait_for_completion(&iscan->sysioc_exited);
+	}
+	mutex_lock(&wl_cache_lock);
+	while (iscan->list_hdr) {
+		buf = iscan->list_hdr->next;
+		kfree(iscan->list_hdr);
+		iscan->list_hdr = buf;
+	}
+	kfree(iscan->iscan_ex_params_p);
+	kfree(iscan);
+	g_iscan = NULL;
+	mutex_unlock(&wl_cache_lock);
+#endif
+
+	if (g_scan)
+		kfree(g_scan);
+
+	g_scan = NULL;
+#if !defined(CSCAN)
+	wl_iw_release_ss_cache_ctrl();
+#endif
+	wl_iw_bt_release();
+#ifdef SOFTAP
+	if (ap_cfg_running) {
+		WL_TRACE(("\n%s AP is going down\n", __FUNCTION__));
+		wl_iw_send_priv_event(priv_dev, "AP_DOWN");
+	}
+#endif
+}
diff --git a/drivers/net/wireless/bcm4329/wl_iw.h b/drivers/net/wireless/bcm4329/wl_iw.h
new file mode 100644
index 0000000..ee6c699
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/wl_iw.h
@@ -0,0 +1,309 @@
+/*
+ * Linux Wireless Extensions support
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: wl_iw.h,v 1.5.34.1.6.36.4.18 2011/02/10 19:33:12 Exp $
+ */
+
+
+#ifndef _wl_iw_h_
+#define _wl_iw_h_
+
+#include <linux/wireless.h>
+
+#include <typedefs.h>
+#include <proto/ethernet.h>
+#include <wlioctl.h>
+
+#define WL_SCAN_PARAMS_SSID_MAX 	10
+#define GET_SSID			"SSID="
+#define GET_CHANNEL			"CH="
+#define GET_NPROBE 			"NPROBE="
+#define GET_ACTIVE_ASSOC_DWELL  	"ACTIVE="
+#define GET_PASSIVE_ASSOC_DWELL  	"PASSIVE="
+#define GET_HOME_DWELL  		"HOME="
+#define GET_SCAN_TYPE			"TYPE="
+
+#define BAND_GET_CMD				"GETBAND"
+#define BAND_SET_CMD				"SETBAND"
+#define DTIM_SKIP_GET_CMD			"DTIMSKIPGET"
+#define DTIM_SKIP_SET_CMD			"DTIMSKIPSET"
+#define SETSUSPEND_CMD				"SETSUSPENDOPT"
+#define PNOSSIDCLR_SET_CMD			"PNOSSIDCLR"
+#define PNOSETUP_SET_CMD			"PNOSETUP "
+#define PNOENABLE_SET_CMD			"PNOFORCE"
+#define PNODEBUG_SET_CMD			"PNODEBUG"
+#define TXPOWER_SET_CMD				"TXPOWER"
+#define RXFILTER_START_CMD			"RXFILTER-START"
+#define RXFILTER_STOP_CMD			"RXFILTER-STOP"
+#define RXFILTER_ADD_CMD			"RXFILTER-ADD"
+#define RXFILTER_REMOVE_CMD			"RXFILTER-REMOVE"
+
+#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
+#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
+
+
+typedef struct wl_iw_extra_params {
+	int 	target_channel;
+} wl_iw_extra_params_t;
+
+struct cntry_locales_custom {
+	char iso_abbrev[WLC_CNTRY_BUF_SZ];
+	char custom_locale[WLC_CNTRY_BUF_SZ];
+	int32 custom_locale_rev;
+};
+
+#define	WL_IW_RSSI_MINVAL	-200
+#define	WL_IW_RSSI_NO_SIGNAL	-91
+#define	WL_IW_RSSI_VERY_LOW	-80
+#define	WL_IW_RSSI_LOW		-70
+#define	WL_IW_RSSI_GOOD		-68
+#define	WL_IW_RSSI_VERY_GOOD	-58
+#define	WL_IW_RSSI_EXCELLENT	-57
+#define	WL_IW_RSSI_INVALID	 0
+#define MAX_WX_STRING		80
+#define isprint(c)		bcm_isprint(c)
+#define WL_IW_SET_ACTIVE_SCAN	(SIOCIWFIRSTPRIV+1)
+#define WL_IW_GET_RSSI		(SIOCIWFIRSTPRIV+3)
+#define WL_IW_SET_PASSIVE_SCAN	(SIOCIWFIRSTPRIV+5)
+#define WL_IW_GET_LINK_SPEED	(SIOCIWFIRSTPRIV+7)
+#define WL_IW_GET_CURR_MACADDR	(SIOCIWFIRSTPRIV+9)
+#define WL_IW_SET_STOP		(SIOCIWFIRSTPRIV+11)
+#define WL_IW_SET_START		(SIOCIWFIRSTPRIV+13)
+
+
+#define WL_SET_AP_CFG           (SIOCIWFIRSTPRIV+15)
+#define WL_AP_STA_LIST          (SIOCIWFIRSTPRIV+17)
+#define WL_AP_MAC_FLTR	        (SIOCIWFIRSTPRIV+19)
+#define WL_AP_BSS_START         (SIOCIWFIRSTPRIV+21)
+#define AP_LPB_CMD              (SIOCIWFIRSTPRIV+23)
+#define WL_AP_STOP              (SIOCIWFIRSTPRIV+25)
+#define WL_FW_RELOAD            (SIOCIWFIRSTPRIV+27)
+#define WL_AP_STA_DISASSOC		(SIOCIWFIRSTPRIV+29)
+#define WL_COMBO_SCAN           (SIOCIWFIRSTPRIV+31)
+
+#define G_SCAN_RESULTS		(8*1024)
+#define WE_ADD_EVENT_FIX	0x80
+#define G_WLAN_SET_ON		0
+#define G_WLAN_SET_OFF		1
+
+#define CHECK_EXTRA_FOR_NULL(extra) \
+if (!extra) { \
+	WL_ERROR(("%s: error : extra is null pointer\n", __FUNCTION__)); \
+	return -EINVAL; \
+}
+
+typedef struct wl_iw {
+	char nickname[IW_ESSID_MAX_SIZE];
+
+	struct iw_statistics wstats;
+
+	int spy_num;
+	uint32 pwsec;
+	uint32 gwsec;
+	bool privacy_invoked;
+
+	struct ether_addr spy_addr[IW_MAX_SPY];
+	struct iw_quality spy_qual[IW_MAX_SPY];
+	void  *wlinfo;
+	dhd_pub_t * pub;
+} wl_iw_t;
+
+#define WLC_IW_SS_CACHE_MAXLEN				2048
+#define WLC_IW_SS_CACHE_CTRL_FIELD_MAXLEN	32
+#define WLC_IW_BSS_INFO_MAXLEN 				\
+	(WLC_IW_SS_CACHE_MAXLEN - WLC_IW_SS_CACHE_CTRL_FIELD_MAXLEN)
+
+typedef struct wl_iw_ss_cache {
+	struct wl_iw_ss_cache *next;
+	int dirty;
+	uint32 buflen;
+	uint32 version;
+	uint32 count;
+	wl_bss_info_t bss_info[1];
+} wl_iw_ss_cache_t;
+
+typedef struct wl_iw_ss_cache_ctrl {
+	wl_iw_ss_cache_t *m_cache_head;
+	int m_link_down;
+	int m_timer_expired;
+	char m_active_bssid[ETHER_ADDR_LEN];
+	uint m_prev_scan_mode;
+	uint m_cons_br_scan_cnt;
+	struct timer_list *m_timer;
+} wl_iw_ss_cache_ctrl_t;
+
+typedef enum broadcast_first_scan {
+	BROADCAST_SCAN_FIRST_IDLE = 0,
+	BROADCAST_SCAN_FIRST_STARTED,
+	BROADCAST_SCAN_FIRST_RESULT_READY,
+	BROADCAST_SCAN_FIRST_RESULT_CONSUMED
+} broadcast_first_scan_t;
+#ifdef SOFTAP
+#define SSID_LEN	33
+#define SEC_LEN		16
+#define KEY_LEN		65
+#define PROFILE_OFFSET	32
+struct ap_profile {
+	uint8	ssid[SSID_LEN];
+	uint8	sec[SEC_LEN];
+	uint8	key[KEY_LEN];
+	uint32	channel;
+	uint32	preamble;
+	uint32	max_scb;
+	uint32  closednet;
+	char country_code[WLC_CNTRY_BUF_SZ];
+};
+
+
+#define MACLIST_MODE_DISABLED		0
+#define MACLIST_MODE_DENY		1
+#define MACLIST_MODE_ALLOW		2
+struct mflist {
+	uint count;
+	struct ether_addr ea[16];
+};
+
+struct mac_list_set {
+	uint32	mode;
+	struct mflist mac_list;
+};
+#endif
+
+#if WIRELESS_EXT > 12
+#include <net/iw_handler.h>
+extern const struct iw_handler_def wl_iw_handler_def;
+#endif
+
+extern int wl_iw_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+extern void wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data);
+extern int wl_iw_get_wireless_stats(struct net_device *dev, struct iw_statistics *wstats);
+int wl_iw_attach(struct net_device *dev, void * dhdp);
+void wl_iw_detach(void);
+int wl_control_wl_start(struct net_device *dev);
+
+extern int net_os_wake_lock(struct net_device *dev);
+extern int net_os_wake_unlock(struct net_device *dev);
+extern int net_os_wake_lock_timeout(struct net_device *dev);
+extern int net_os_wake_lock_timeout_enable(struct net_device *dev);
+extern int net_os_set_suspend_disable(struct net_device *dev, int val);
+extern int net_os_set_suspend(struct net_device *dev, int val);
+extern int net_os_set_dtim_skip(struct net_device *dev, int val);
+extern void get_customized_country_code(char *country_iso_code, wl_country_t *cspec);
+extern char *dhd_bus_country_get(struct net_device *dev);
+extern int dhd_get_dtim_skip(dhd_pub_t *dhd);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
+#define IWE_STREAM_ADD_EVENT(info, stream, ends, iwe, extra) \
+	iwe_stream_add_event(info, stream, ends, iwe, extra)
+#define IWE_STREAM_ADD_VALUE(info, event, value, ends, iwe, event_len) \
+	iwe_stream_add_value(info, event, value, ends, iwe, event_len)
+#define IWE_STREAM_ADD_POINT(info, stream, ends, iwe, extra) \
+	iwe_stream_add_point(info, stream, ends, iwe, extra)
+#else
+#define IWE_STREAM_ADD_EVENT(info, stream, ends, iwe, extra) \
+	iwe_stream_add_event(stream, ends, iwe, extra)
+#define IWE_STREAM_ADD_VALUE(info, event, value, ends, iwe, event_len) \
+	iwe_stream_add_value(event, value, ends, iwe, event_len)
+#define IWE_STREAM_ADD_POINT(info, stream, ends, iwe, extra) \
+	iwe_stream_add_point(stream, ends, iwe, extra)
+#endif
+
+extern int dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled);
+extern int dhd_pno_clean(dhd_pub_t *dhd);
+extern int dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, \
+					    ushort  scan_fr, int pno_repeat, int pno_freq_expo_max);
+extern int dhd_pno_get_status(dhd_pub_t *dhd);
+extern int dhd_dev_pno_reset(struct net_device *dev);
+extern int dhd_dev_pno_set(struct net_device *dev, wlc_ssid_t* ssids_local, \
+				 int nssid, ushort  scan_fr, int pno_repeat, int pno_freq_expo_max);
+extern int dhd_dev_pno_enable(struct net_device *dev,  int pfn_enabled);
+extern int dhd_dev_get_pno_status(struct net_device *dev);
+extern void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec);
+
+#define PNO_TLV_PREFIX			'S'
+#define PNO_TLV_VERSION			'1'
+#define PNO_TLV_SUBVERSION 		'2'
+#define PNO_TLV_RESERVED		'0'
+#define PNO_TLV_TYPE_SSID_IE		'S'
+#define PNO_TLV_TYPE_TIME		'T'
+#define PNO_TLV_FREQ_REPEAT		'R'
+#define PNO_TLV_FREQ_EXPO_MAX	'M'
+#define PNO_EVENT_UP			"PNO_EVENT"
+
+typedef struct cmd_tlv {
+	char prefix;
+	char version;
+	char subver;
+	char reserved;
+} cmd_tlv_t;
+
+#ifdef SOFTAP_TLV_CFG
+#define SOFTAP_SET_CMD				"SOFTAPSET "
+#define SOFTAP_TLV_PREFIX			'A'
+#define SOFTAP_TLV_VERSION			'1'
+#define SOFTAP_TLV_SUBVERSION			'0'
+#define SOFTAP_TLV_RESERVED			'0'
+
+#define TLV_TYPE_SSID				'S'
+#define TLV_TYPE_SECUR				'E'
+#define TLV_TYPE_KEY				'K'
+#define TLV_TYPE_CHANNEL			'C'
+#endif
+
+#if defined(CSCAN)
+
+typedef struct cscan_tlv {
+	char prefix;
+	char version;
+	char subver;
+	char reserved;
+} cscan_tlv_t;
+
+#define CSCAN_COMMAND				"CSCAN "
+#define CSCAN_TLV_PREFIX 			'S'
+#define CSCAN_TLV_VERSION			1
+#define CSCAN_TLV_SUBVERSION			0
+#define CSCAN_TLV_TYPE_SSID_IE			'S'
+#define CSCAN_TLV_TYPE_CHANNEL_IE		'C'
+#define CSCAN_TLV_TYPE_NPROBE_IE		'N'
+#define CSCAN_TLV_TYPE_ACTIVE_IE		'A'
+#define CSCAN_TLV_TYPE_PASSIVE_IE		'P'
+#define CSCAN_TLV_TYPE_HOME_IE			'H'
+#define CSCAN_TLV_TYPE_STYPE_IE			'T'
+
+extern int wl_iw_parse_channel_list_tlv(char** list_str, uint16* channel_list, \
+					int channel_num, int *bytes_left);
+
+extern int wl_iw_parse_data_tlv(char** list_str, void  *dst, int dst_size, \
+					const char token, int input_size, int *bytes_left);
+
+extern int wl_iw_parse_ssid_list_tlv(char** list_str, wlc_ssid_t* ssid, \
+					int max, int *bytes_left);
+
+extern int wl_iw_parse_ssid_list(char** list_str, wlc_ssid_t* ssid, int idx, int max);
+
+extern int wl_iw_parse_channel_list(char** list_str, uint16* channel_list, int channel_num);
+
+#endif
+
+#endif
diff --git a/drivers/net/wireless/bcmdhd/bcmwifi.c b/drivers/net/wireless/bcmdhd/bcmwifi.c
new file mode 100644
index 0000000..bc975e8
--- /dev/null
+++ b/drivers/net/wireless/bcmdhd/bcmwifi.c
@@ -0,0 +1,930 @@
+/*
+ * Misc utility routines used by kernel or app-level.
+ * Contents are wifi-specific, used by any kernel or app-level
+ * software that might want wifi things as it grows.
+ *
+ * Copyright (C) 1999-2012, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ * $Id: bcmwifi.c 309193 2012-01-19 00:03:57Z $
+ */
+
+#include <bcm_cfg.h>
+#include <typedefs.h>
+
+#ifdef BCMDRIVER
+#include <osl.h>
+#include <bcmutils.h>
+#define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base))
+#define tolower(c) (bcm_isupper((c)) ? ((c) + 'a' - 'A') : (c))
+#else
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#ifndef ASSERT
+#define ASSERT(exp)
+#endif
+#endif 
+#include <bcmwifi.h>
+
+#if defined(WIN32) && (defined(BCMDLL) || defined(WLMDLL))
+#include <bcmstdlib.h> 	
+#endif
+
+#ifndef D11AC_IOTYPES
+
+
+
+
+
+
+
+char *
+wf_chspec_ntoa(chanspec_t chspec, char *buf)
+{
+	const char *band, *bw, *sb;
+	uint channel;
+
+	band = "";
+	bw = "";
+	sb = "";
+	channel = CHSPEC_CHANNEL(chspec);
+	
+	if ((CHSPEC_IS2G(chspec) && channel > CH_MAX_2G_CHANNEL) ||
+	    (CHSPEC_IS5G(chspec) && channel <= CH_MAX_2G_CHANNEL))
+		band = (CHSPEC_IS2G(chspec)) ? "b" : "a";
+	if (CHSPEC_IS40(chspec)) {
+		if (CHSPEC_SB_UPPER(chspec)) {
+			sb = "u";
+			channel += CH_10MHZ_APART;
+		} else {
+			sb = "l";
+			channel -= CH_10MHZ_APART;
+		}
+	} else if (CHSPEC_IS10(chspec)) {
+		bw = "n";
+	}
+
+	
+	snprintf(buf, 6, "%d%s%s%s", channel, band, bw, sb);
+	return (buf);
+}
+
+
+chanspec_t
+wf_chspec_aton(const char *a)
+{
+	char *endp = NULL;
+	uint channel, band, bw, ctl_sb;
+	char c;
+
+	channel = strtoul(a, &endp, 10);
+
+	
+	if (endp == a)
+		return 0;
+
+	if (channel > MAXCHANNEL)
+		return 0;
+
+	band = ((channel <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G);
+	bw = WL_CHANSPEC_BW_20;
+	ctl_sb = WL_CHANSPEC_CTL_SB_NONE;
+
+	a = endp;
+
+	c = tolower(a[0]);
+	if (c == '\0')
+		goto done;
+
+	
+	if (c == 'a' || c == 'b') {
+		band = (c == 'a') ? WL_CHANSPEC_BAND_5G : WL_CHANSPEC_BAND_2G;
+		a++;
+		c = tolower(a[0]);
+		if (c == '\0')
+			goto done;
+	}
+
+	
+	if (c == 'n') {
+		bw = WL_CHANSPEC_BW_10;
+	} else if (c == 'l') {
+		bw = WL_CHANSPEC_BW_40;
+		ctl_sb = WL_CHANSPEC_CTL_SB_LOWER;
+		
+		if (channel <= (MAXCHANNEL - CH_20MHZ_APART))
+			channel += CH_10MHZ_APART;
+		else
+			return 0;
+	} else if (c == 'u') {
+		bw = WL_CHANSPEC_BW_40;
+		ctl_sb = WL_CHANSPEC_CTL_SB_UPPER;
+		
+		if (channel > CH_20MHZ_APART)
+			channel -= CH_10MHZ_APART;
+		else
+			return 0;
+	} else {
+		return 0;
+	}
+
+done:
+	return (channel | band | bw | ctl_sb);
+}
+
+
+bool
+wf_chspec_malformed(chanspec_t chanspec)
+{
+	
+	if (!CHSPEC_IS5G(chanspec) && !CHSPEC_IS2G(chanspec))
+		return TRUE;
+	
+	if (!CHSPEC_IS40(chanspec) && !CHSPEC_IS20(chanspec))
+		return TRUE;
+
+	
+	if (CHSPEC_IS20(chanspec)) {
+		if (!CHSPEC_SB_NONE(chanspec))
+			return TRUE;
+	} else {
+		if (!CHSPEC_SB_UPPER(chanspec) && !CHSPEC_SB_LOWER(chanspec))
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+uint8
+wf_chspec_ctlchan(chanspec_t chspec)
+{
+	uint8 ctl_chan;
+
+	
+	if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_NONE) {
+		return CHSPEC_CHANNEL(chspec);
+	} else {
+		
+		ASSERT(CHSPEC_BW(chspec) == WL_CHANSPEC_BW_40);
+		
+		if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_UPPER) {
+			
+			ctl_chan = UPPER_20_SB(CHSPEC_CHANNEL(chspec));
+		} else {
+			ASSERT(CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_LOWER);
+			
+			ctl_chan = LOWER_20_SB(CHSPEC_CHANNEL(chspec));
+		}
+	}
+
+	return ctl_chan;
+}
+
+chanspec_t
+wf_chspec_ctlchspec(chanspec_t chspec)
+{
+	chanspec_t ctl_chspec = 0;
+	uint8 channel;
+
+	ASSERT(!wf_chspec_malformed(chspec));
+
+	
+	if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_NONE) {
+		return chspec;
+	} else {
+		if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_UPPER) {
+			channel = UPPER_20_SB(CHSPEC_CHANNEL(chspec));
+		} else {
+			channel = LOWER_20_SB(CHSPEC_CHANNEL(chspec));
+		}
+		ctl_chspec = channel | WL_CHANSPEC_BW_20 | WL_CHANSPEC_CTL_SB_NONE;
+		ctl_chspec |= CHSPEC_BAND(chspec);
+	}
+	return ctl_chspec;
+}
+
+#else 
+
+
+
+
+
+
+static const char *wf_chspec_bw_str[] =
+{
+	"5",
+	"10",
+	"20",
+	"40",
+	"80",
+	"160",
+	"80+80",
+	"na"
+};
+
+static const uint8 wf_chspec_bw_mhz[] =
+{5, 10, 20, 40, 80, 160, 160};
+
+#define WF_NUM_BW \
+	(sizeof(wf_chspec_bw_mhz)/sizeof(uint8))
+
+
+static const uint8 wf_5g_40m_chans[] =
+{38, 46, 54, 62, 102, 110, 118, 126, 134, 142, 151, 159};
+#define WF_NUM_5G_40M_CHANS \
+	(sizeof(wf_5g_40m_chans)/sizeof(uint8))
+
+
+static const uint8 wf_5g_80m_chans[] =
+{42, 58, 106, 122, 138, 155};
+#define WF_NUM_5G_80M_CHANS \
+	(sizeof(wf_5g_80m_chans)/sizeof(uint8))
+
+
+static const uint8 wf_5g_160m_chans[] =
+{50, 114};
+#define WF_NUM_5G_160M_CHANS \
+	(sizeof(wf_5g_160m_chans)/sizeof(uint8))
+
+
+
+static uint
+bw_chspec_to_mhz(chanspec_t chspec)
+{
+	uint bw;
+
+	bw = (chspec & WL_CHANSPEC_BW_MASK) >> WL_CHANSPEC_BW_SHIFT;
+	return (bw >= WF_NUM_BW ? 0 : wf_chspec_bw_mhz[bw]);
+}
+
+
+static uint8
+center_chan_to_edge(uint bw)
+{
+	
+	return (uint8)(((bw - 20) / 2) / 5);
+}
+
+
+static uint8
+channel_low_edge(uint center_ch, uint bw)
+{
+	return (uint8)(center_ch - center_chan_to_edge(bw));
+}
+
+
+static int
+channel_to_sb(uint center_ch, uint ctl_ch, uint bw)
+{
+	uint lowest = channel_low_edge(center_ch, bw);
+	uint sb;
+
+	if ((ctl_ch - lowest) % 4) {
+		
+		return -1;
+	}
+
+	sb = ((ctl_ch - lowest) / 4);
+
+	
+	if (sb >= (bw / 20)) {
+		
+		return -1;
+	}
+
+	return sb;
+}
+
+
+static uint8
+channel_to_ctl_chan(uint center_ch, uint bw, uint sb)
+{
+	return (uint8)(channel_low_edge(center_ch, bw) + sb * 4);
+}
+
+
+static int
+channel_80mhz_to_id(uint ch)
+{
+	uint i;
+	for (i = 0; i < WF_NUM_5G_80M_CHANS; i ++) {
+		if (ch == wf_5g_80m_chans[i])
+			return i;
+	}
+
+	return -1;
+}
+
+
+char *
+wf_chspec_ntoa(chanspec_t chspec, char *buf)
+{
+	const char *band;
+	uint ctl_chan;
+
+	if (wf_chspec_malformed(chspec))
+		return NULL;
+
+	band = "";
+
+	
+	if ((CHSPEC_IS2G(chspec) && CHSPEC_CHANNEL(chspec) > CH_MAX_2G_CHANNEL) ||
+	    (CHSPEC_IS5G(chspec) && CHSPEC_CHANNEL(chspec) <= CH_MAX_2G_CHANNEL))
+		band = (CHSPEC_IS2G(chspec)) ? "2g" : "5g";
+
+	
+	ctl_chan = wf_chspec_ctlchan(chspec);
+
+	
+	if (CHSPEC_IS20(chspec)) {
+		snprintf(buf, CHANSPEC_STR_LEN, "%s%d", band, ctl_chan);
+	} else if (!CHSPEC_IS8080(chspec)) {
+		const char *bw;
+		const char *sb = "";
+
+		bw = wf_chspec_bw_str[(chspec & WL_CHANSPEC_BW_MASK) >> WL_CHANSPEC_BW_SHIFT];
+
+#ifdef CHANSPEC_NEW_40MHZ_FORMAT
+		
+		if (CHSPEC_IS40(chspec) && CHSPEC_IS2G(chspec)) {
+			sb = CHSPEC_SB_UPPER(chspec) ? "u" : "l";
+		}
+
+		snprintf(buf, CHANSPEC_STR_LEN, "%s%d/%s%s", band, ctl_chan, bw, sb);
+#else
+		
+		if (CHSPEC_IS40(chspec)) {
+			sb = CHSPEC_SB_UPPER(chspec) ? "u" : "l";
+			snprintf(buf, CHANSPEC_STR_LEN, "%s%d%s", band, ctl_chan, sb);
+		} else {
+			snprintf(buf, CHANSPEC_STR_LEN, "%s%d/%s", band, ctl_chan, bw);
+		}
+#endif 
+
+	} else {
+		
+		uint chan1 = (chspec & WL_CHANSPEC_CHAN1_MASK) >> WL_CHANSPEC_CHAN1_SHIFT;
+		uint chan2 = (chspec & WL_CHANSPEC_CHAN2_MASK) >> WL_CHANSPEC_CHAN2_SHIFT;
+
+		
+		chan1 = (chan1 < WF_NUM_5G_80M_CHANS) ? wf_5g_80m_chans[chan1] : 0;
+		chan2 = (chan2 < WF_NUM_5G_80M_CHANS) ? wf_5g_80m_chans[chan2] : 0;
+
+		
+		snprintf(buf, CHANSPEC_STR_LEN, "%d/80+80/%d-%d", ctl_chan, chan1, chan2);
+	}
+
+	return (buf);
+}
+
+static int
+read_uint(const char **p, unsigned int *num)
+{
+	unsigned long val;
+	char *endp = NULL;
+
+	val = strtoul(*p, &endp, 10);
+	
+	if (endp == *p)
+		return 0;
+
+	
+	*p = endp;
+	
+	*num = (unsigned int)val;
+
+	return 1;
+}
+
+
+chanspec_t
+wf_chspec_aton(const char *a)
+{
+	chanspec_t chspec;
+	uint chspec_ch, chspec_band, bw, chspec_bw, chspec_sb;
+	uint num, ctl_ch;
+	uint ch1, ch2;
+	char c, sb_ul = '\0';
+	int i;
+
+	bw = 20;
+	chspec_sb = 0;
+	chspec_ch = ch1 = ch2 = 0;
+
+	
+	if (!read_uint(&a, &num))
+		return 0;
+
+	
+	c = tolower(a[0]);
+	if (c == 'g') {
+		a ++; 
+
+		
+		if (num == 2)
+			chspec_band = WL_CHANSPEC_BAND_2G;
+		else if (num == 5)
+			chspec_band = WL_CHANSPEC_BAND_5G;
+		else
+			return 0;
+
+		
+		if (!read_uint(&a, &ctl_ch))
+			return 0;
+
+		c = tolower(a[0]);
+	}
+	else {
+		
+		ctl_ch = num;
+		chspec_band = ((ctl_ch <= CH_MAX_2G_CHANNEL) ?
+		               WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G);
+	}
+
+	if (c == '\0') {
+		
+		chspec_bw = WL_CHANSPEC_BW_20;
+		goto done_read;
+	}
+
+	a ++; 
+
+	
+	if (c == 'u' || c == 'l') {
+		sb_ul = c;
+		chspec_bw = WL_CHANSPEC_BW_40;
+		goto done_read;
+	}
+
+	
+	if (c != '/')
+		return 0;
+
+	
+	if (!read_uint(&a, &bw))
+		return 0;
+
+	
+	if (bw == 20) {
+		chspec_bw = WL_CHANSPEC_BW_20;
+	} else if (bw == 40) {
+		chspec_bw = WL_CHANSPEC_BW_40;
+	} else if (bw == 80) {
+		chspec_bw = WL_CHANSPEC_BW_80;
+	} else if (bw == 160) {
+		chspec_bw = WL_CHANSPEC_BW_160;
+	} else {
+		return 0;
+	}
+
+	
+
+	c = tolower(a[0]);
+
+	
+	if (chspec_band == WL_CHANSPEC_BAND_2G && bw == 40) {
+		if (c == 'u' || c == 'l') {
+			a ++; 
+			sb_ul = c;
+			goto done_read;
+		}
+	}
+
+	
+	if (c == '+') {
+		
+		static const char *plus80 = "80/";
+
+		
+		chspec_bw = WL_CHANSPEC_BW_8080;
+
+		a ++; 
+
+		
+		for (i = 0; i < 3; i++) {
+			if (*a++ != *plus80++) {
+				return 0;
+			}
+		}
+
+		
+		if (!read_uint(&a, &ch1))
+			return 0;
+
+		
+		if (a[0] != '-')
+			return 0;
+		a ++; 
+
+		
+		if (!read_uint(&a, &ch2))
+			return 0;
+	}
+
+done_read:
+	
+	while (a[0] == ' ') {
+		a ++;
+	}
+
+	
+	if (a[0] != '\0')
+		return 0;
+
+	
+
+	
+	if (sb_ul != '\0') {
+		if (sb_ul == 'l') {
+			chspec_ch = UPPER_20_SB(ctl_ch);
+			chspec_sb = WL_CHANSPEC_CTL_SB_LLL;
+		} else if (sb_ul == 'u') {
+			chspec_ch = LOWER_20_SB(ctl_ch);
+			chspec_sb = WL_CHANSPEC_CTL_SB_LLU;
+		}
+	}
+	
+	else if (chspec_bw == WL_CHANSPEC_BW_20) {
+		chspec_ch = ctl_ch;
+		chspec_sb = 0;
+	}
+	
+	else if (chspec_bw != WL_CHANSPEC_BW_8080) {
+		
+		const uint8 *center_ch = NULL;
+		int num_ch = 0;
+		int sb = -1;
+
+		if (chspec_bw == WL_CHANSPEC_BW_40) {
+			center_ch = wf_5g_40m_chans;
+			num_ch = WF_NUM_5G_40M_CHANS;
+		} else if (chspec_bw == WL_CHANSPEC_BW_80) {
+			center_ch = wf_5g_80m_chans;
+			num_ch = WF_NUM_5G_80M_CHANS;
+		} else if (chspec_bw == WL_CHANSPEC_BW_160) {
+			center_ch = wf_5g_160m_chans;
+			num_ch = WF_NUM_5G_160M_CHANS;
+		} else {
+			return 0;
+		}
+
+		for (i = 0; i < num_ch; i ++) {
+			sb = channel_to_sb(center_ch[i], ctl_ch, bw);
+			if (sb >= 0) {
+				chspec_ch = center_ch[i];
+				chspec_sb = sb << WL_CHANSPEC_CTL_SB_SHIFT;
+				break;
+			}
+		}
+
+		
+		if (sb < 0) {
+			return 0;
+		}
+	}
+	
+	else {
+		int ch1_id = 0, ch2_id = 0;
+		int sb;
+
+		ch1_id = channel_80mhz_to_id(ch1);
+		ch2_id = channel_80mhz_to_id(ch2);
+
+		
+		if (ch1 >= ch2 || ch1_id < 0 || ch2_id < 0)
+			return 0;
+
+		
+		chspec_ch = (((uint16)ch1_id << WL_CHANSPEC_CHAN1_SHIFT) |
+			((uint16)ch2_id << WL_CHANSPEC_CHAN2_SHIFT));
+
+		
+
+		
+		sb = channel_to_sb(ch1, ctl_ch, bw);
+		if (sb < 0) {
+			
+			sb = channel_to_sb(ch2, ctl_ch, bw);
+			if (sb < 0) {
+				
+				return 0;
+			}
+			
+			sb += 4;
+		}
+
+		chspec_sb = sb << WL_CHANSPEC_CTL_SB_SHIFT;
+	}
+
+	chspec = (chspec_ch | chspec_band | chspec_bw | chspec_sb);
+
+	if (wf_chspec_malformed(chspec))
+		return 0;
+
+	return chspec;
+}
+
+
+bool
+wf_chspec_malformed(chanspec_t chanspec)
+{
+	uint chspec_bw = CHSPEC_BW(chanspec);
+	uint chspec_ch = CHSPEC_CHANNEL(chanspec);
+
+	
+	if (CHSPEC_IS2G(chanspec)) {
+		
+		if (chspec_bw != WL_CHANSPEC_BW_20 &&
+		    chspec_bw != WL_CHANSPEC_BW_40) {
+			return TRUE;
+		}
+	} else if (CHSPEC_IS5G(chanspec)) {
+		if (chspec_bw == WL_CHANSPEC_BW_8080) {
+			uint ch1_id, ch2_id;
+
+			
+			ch1_id = CHSPEC_CHAN1(chanspec);
+			ch2_id = CHSPEC_CHAN2(chanspec);
+			if (ch1_id >= WF_NUM_5G_80M_CHANS || ch2_id >= WF_NUM_5G_80M_CHANS)
+				return TRUE;
+
+			
+			if (ch2_id <= ch1_id)
+				return TRUE;
+		} else if (chspec_bw == WL_CHANSPEC_BW_20 || chspec_bw == WL_CHANSPEC_BW_40 ||
+		           chspec_bw == WL_CHANSPEC_BW_80 || chspec_bw == WL_CHANSPEC_BW_160) {
+
+			if (chspec_ch > MAXCHANNEL) {
+				return TRUE;
+			}
+		} else {
+			
+			return TRUE;
+		}
+	} else {
+		
+		return TRUE;
+	}
+
+	
+	if (chspec_bw == WL_CHANSPEC_BW_20) {
+		if (CHSPEC_CTL_SB(chanspec) != WL_CHANSPEC_CTL_SB_LLL)
+			return TRUE;
+	} else if (chspec_bw == WL_CHANSPEC_BW_40) {
+		if (CHSPEC_CTL_SB(chanspec) > WL_CHANSPEC_CTL_SB_LLU)
+			return TRUE;
+	} else if (chspec_bw == WL_CHANSPEC_BW_80) {
+		if (CHSPEC_CTL_SB(chanspec) > WL_CHANSPEC_CTL_SB_LUU)
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+bool
+wf_chspec_valid(chanspec_t chanspec)
+{
+	uint chspec_bw = CHSPEC_BW(chanspec);
+	uint chspec_ch = CHSPEC_CHANNEL(chanspec);
+
+	if (wf_chspec_malformed(chanspec))
+		return FALSE;
+
+	if (CHSPEC_IS2G(chanspec)) {
+		
+		if (chspec_bw == WL_CHANSPEC_BW_20) {
+			if (chspec_ch >= 1 && chspec_ch <= 14)
+				return TRUE;
+		} else if (chspec_bw == WL_CHANSPEC_BW_40) {
+			if (chspec_ch >= 3 && chspec_ch <= 11)
+				return TRUE;
+		}
+	} else if (CHSPEC_IS5G(chanspec)) {
+		if (chspec_bw == WL_CHANSPEC_BW_8080) {
+			uint16 ch1, ch2;
+
+			ch1 = wf_5g_80m_chans[CHSPEC_CHAN1(chanspec)];
+			ch2 = wf_5g_80m_chans[CHSPEC_CHAN2(chanspec)];
+
+			
+			if (ch2 > ch1 + CH_80MHZ_APART)
+				return TRUE;
+		} else {
+			const uint8 *center_ch;
+			uint num_ch, i;
+
+			if (chspec_bw == WL_CHANSPEC_BW_20 || chspec_bw == WL_CHANSPEC_BW_40) {
+				center_ch = wf_5g_40m_chans;
+				num_ch = WF_NUM_5G_40M_CHANS;
+			} else if (chspec_bw == WL_CHANSPEC_BW_80) {
+				center_ch = wf_5g_80m_chans;
+				num_ch = WF_NUM_5G_80M_CHANS;
+			} else if (chspec_bw == WL_CHANSPEC_BW_160) {
+				center_ch = wf_5g_160m_chans;
+				num_ch = WF_NUM_5G_160M_CHANS;
+			} else {
+				
+				return FALSE;
+			}
+
+			
+			if (chspec_bw == WL_CHANSPEC_BW_20) {
+				
+				for (i = 0; i < num_ch; i ++) {
+					if (chspec_ch == (uint)LOWER_20_SB(center_ch[i]) ||
+					    chspec_ch == (uint)UPPER_20_SB(center_ch[i]))
+						break; 
+				}
+
+				if (i == num_ch) {
+					
+					if (chspec_ch == 34 || chspec_ch == 38 ||
+					    chspec_ch == 42 || chspec_ch == 46)
+						i = 0;
+				}
+			} else {
+				
+				for (i = 0; i < num_ch; i ++) {
+					if (chspec_ch == center_ch[i])
+						break; 
+				}
+			}
+
+			if (i < num_ch) {
+				
+				return TRUE;
+			}
+		}
+	}
+
+	return FALSE;
+}
+
+
+uint8
+wf_chspec_ctlchan(chanspec_t chspec)
+{
+	uint center_chan;
+	uint bw_mhz;
+	uint sb;
+
+	ASSERT(!wf_chspec_malformed(chspec));
+
+	
+	if (CHSPEC_IS20(chspec)) {
+		return CHSPEC_CHANNEL(chspec);
+	} else {
+		sb = CHSPEC_CTL_SB(chspec) >> WL_CHANSPEC_CTL_SB_SHIFT;
+
+		if (CHSPEC_IS8080(chspec)) {
+			bw_mhz = 80;
+
+			if (sb < 4) {
+				center_chan = CHSPEC_CHAN1(chspec);
+			}
+			else {
+				center_chan = CHSPEC_CHAN2(chspec);
+				sb -= 4;
+			}
+
+			
+			center_chan = wf_5g_80m_chans[center_chan];
+		}
+		else {
+			bw_mhz = bw_chspec_to_mhz(chspec);
+			center_chan = CHSPEC_CHANNEL(chspec) >> WL_CHANSPEC_CHAN_SHIFT;
+		}
+
+		return (channel_to_ctl_chan(center_chan, bw_mhz, sb));
+	}
+}
+
+
+chanspec_t
+wf_chspec_ctlchspec(chanspec_t chspec)
+{
+	chanspec_t ctl_chspec = chspec;
+	uint8 ctl_chan;
+
+	ASSERT(!wf_chspec_malformed(chspec));
+
+	
+	if (!CHSPEC_IS20(chspec)) {
+		ctl_chan = wf_chspec_ctlchan(chspec);
+		ctl_chspec = ctl_chan | WL_CHANSPEC_BW_20;
+		ctl_chspec |= CHSPEC_BAND(chspec);
+	}
+	return ctl_chspec;
+}
+
+#endif 
+
+
+extern chanspec_t wf_chspec_primary40_chspec(chanspec_t chspec)
+{
+	chanspec_t chspec40 = chspec;
+	uint center_chan;
+	uint sb;
+
+	ASSERT(!wf_chspec_malformed(chspec));
+
+	if (CHSPEC_IS80(chspec)) {
+		center_chan = CHSPEC_CHANNEL(chspec);
+		sb = CHSPEC_CTL_SB(chspec);
+
+		if (sb == WL_CHANSPEC_CTL_SB_UL) {
+			
+			sb = WL_CHANSPEC_CTL_SB_L;
+			center_chan += CH_20MHZ_APART;
+		} else if (sb == WL_CHANSPEC_CTL_SB_UU) {
+			
+			sb = WL_CHANSPEC_CTL_SB_U;
+			center_chan += CH_20MHZ_APART;
+		} else {
+			
+			
+			center_chan -= CH_20MHZ_APART;
+		}
+
+		
+		chspec40 = (WL_CHANSPEC_BAND_5G | WL_CHANSPEC_BW_40 |
+		            sb | center_chan);
+	}
+
+	return chspec40;
+}
+
+
+int
+wf_mhz2channel(uint freq, uint start_factor)
+{
+	int ch = -1;
+	uint base;
+	int offset;
+
+	
+	if (start_factor == 0) {
+		if (freq >= 2400 && freq <= 2500)
+			start_factor = WF_CHAN_FACTOR_2_4_G;
+		else if (freq >= 5000 && freq <= 6000)
+			start_factor = WF_CHAN_FACTOR_5_G;
+	}
+
+	if (freq == 2484 && start_factor == WF_CHAN_FACTOR_2_4_G)
+		return 14;
+
+	base = start_factor / 2;
+
+	
+	if ((freq < base) || (freq > base + 1000))
+		return -1;
+
+	offset = freq - base;
+	ch = offset / 5;
+
+	
+	if (offset != (ch * 5))
+		return -1;
+
+	
+	if (start_factor == WF_CHAN_FACTOR_2_4_G && (ch < 1 || ch > 13))
+		return -1;
+
+	return ch;
+}
+
+
+int
+wf_channel2mhz(uint ch, uint start_factor)
+{
+	int freq;
+
+	if ((start_factor == WF_CHAN_FACTOR_2_4_G && (ch < 1 || ch > 14)) ||
+	    (ch > 200))
+		freq = -1;
+	else if ((start_factor == WF_CHAN_FACTOR_2_4_G) && (ch == 14))
+		freq = 2484;
+	else
+		freq = ch * 5 + start_factor / 2;
+
+	return freq;
+}
diff --git a/drivers/net/wireless/bcmdhd/include/bcmwifi.h b/drivers/net/wireless/bcmdhd/include/bcmwifi.h
new file mode 100644
index 0000000..f3f593f4
--- /dev/null
+++ b/drivers/net/wireless/bcmdhd/include/bcmwifi.h
@@ -0,0 +1,345 @@
+/*
+ * Misc utility routines for WL and Apps
+ * This header file housing the define and function prototype use by
+ * both the wl driver, tools & Apps.
+ *
+ * Copyright (C) 1999-2012, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmwifi.h 309193 2012-01-19 00:03:57Z $
+ */
+
+#ifndef	_bcmwifi_h_
+#define	_bcmwifi_h_
+
+
+
+typedef uint16 chanspec_t;
+
+
+#define CH_UPPER_SB			0x01
+#define CH_LOWER_SB			0x02
+#define CH_EWA_VALID			0x04
+#define CH_80MHZ_APART			16
+#define CH_40MHZ_APART			8
+#define CH_20MHZ_APART			4
+#define CH_10MHZ_APART			2
+#define CH_5MHZ_APART			1	
+#define CH_MAX_2G_CHANNEL		14	
+#define	MAXCHANNEL		224	
+#define CHSPEC_CTLOVLP(sp1, sp2, sep)	ABS(wf_chspec_ctlchan(sp1) - wf_chspec_ctlchan(sp2)) < (sep)
+
+
+#undef  D11AC_IOTYPES
+#define D11AC_IOTYPES
+
+#ifndef D11AC_IOTYPES
+
+#define WL_CHANSPEC_CHAN_MASK		0x00ff
+#define WL_CHANSPEC_CHAN_SHIFT		0
+
+#define WL_CHANSPEC_CTL_SB_MASK		0x0300
+#define WL_CHANSPEC_CTL_SB_SHIFT	     8
+#define WL_CHANSPEC_CTL_SB_LOWER	0x0100
+#define WL_CHANSPEC_CTL_SB_UPPER	0x0200
+#define WL_CHANSPEC_CTL_SB_NONE		0x0300
+
+#define WL_CHANSPEC_BW_MASK		0x0C00
+#define WL_CHANSPEC_BW_SHIFT		    10
+#define WL_CHANSPEC_BW_10		0x0400
+#define WL_CHANSPEC_BW_20		0x0800
+#define WL_CHANSPEC_BW_40		0x0C00
+
+#define WL_CHANSPEC_BAND_MASK		0xf000
+#define WL_CHANSPEC_BAND_SHIFT		12
+#define WL_CHANSPEC_BAND_5G		0x1000
+#define WL_CHANSPEC_BAND_2G		0x2000
+#define INVCHANSPEC			255
+
+
+#define LOWER_20_SB(channel)	(((channel) > CH_10MHZ_APART) ? ((channel) - CH_10MHZ_APART) : 0)
+#define UPPER_20_SB(channel)	(((channel) < (MAXCHANNEL - CH_10MHZ_APART)) ? \
+				((channel) + CH_10MHZ_APART) : 0)
+#define CHSPEC_WLCBANDUNIT(chspec)	(CHSPEC_IS5G(chspec) ? BAND_5G_INDEX : BAND_2G_INDEX)
+#define CH20MHZ_CHSPEC(channel)	(chanspec_t)((chanspec_t)(channel) | WL_CHANSPEC_BW_20 | \
+				WL_CHANSPEC_CTL_SB_NONE | (((channel) <= CH_MAX_2G_CHANNEL) ? \
+				WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G))
+#define NEXT_20MHZ_CHAN(channel)	(((channel) < (MAXCHANNEL - CH_20MHZ_APART)) ? \
+					((channel) + CH_20MHZ_APART) : 0)
+#define CH40MHZ_CHSPEC(channel, ctlsb)	(chanspec_t) \
+					((channel) | (ctlsb) | WL_CHANSPEC_BW_40 | \
+					((channel) <= CH_MAX_2G_CHANNEL ? WL_CHANSPEC_BAND_2G : \
+					WL_CHANSPEC_BAND_5G))
+#define CHSPEC_CHANNEL(chspec)	((uint8)((chspec) & WL_CHANSPEC_CHAN_MASK))
+#define CHSPEC_BAND(chspec)	((chspec) & WL_CHANSPEC_BAND_MASK)
+
+
+#define CHSPEC_CTL_SB(chspec)	((chspec) & WL_CHANSPEC_CTL_SB_MASK)
+#define CHSPEC_BW(chspec)	((chspec) & WL_CHANSPEC_BW_MASK)
+
+#ifdef WL11N_20MHZONLY
+
+#define CHSPEC_IS10(chspec)	0
+#define CHSPEC_IS20(chspec)	1
+#ifndef CHSPEC_IS40
+#define CHSPEC_IS40(chspec)	0
+#endif
+
+#else 
+
+#define CHSPEC_IS10(chspec)	(((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_10)
+#define CHSPEC_IS20(chspec)	(((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_20)
+#ifndef CHSPEC_IS40
+#define CHSPEC_IS40(chspec)	(((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_40)
+#endif
+
+#endif 
+
+#define CHSPEC_IS5G(chspec)	(((chspec) & WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_5G)
+#define CHSPEC_IS2G(chspec)	(((chspec) & WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_2G)
+#define CHSPEC_SB_NONE(chspec)	(((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_NONE)
+#define CHSPEC_SB_UPPER(chspec)	(((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_UPPER)
+#define CHSPEC_SB_LOWER(chspec)	(((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_LOWER)
+#define CHSPEC_CTL_CHAN(chspec)  ((CHSPEC_SB_LOWER(chspec)) ? \
+				  (LOWER_20_SB(((chspec) & WL_CHANSPEC_CHAN_MASK))) : \
+				  (UPPER_20_SB(((chspec) & WL_CHANSPEC_CHAN_MASK))))
+#define CHSPEC2WLC_BAND(chspec) (CHSPEC_IS5G(chspec) ? WLC_BAND_5G : WLC_BAND_2G)
+
+#define CHANSPEC_STR_LEN    8
+
+#else 
+
+#define WL_CHANSPEC_CHAN_MASK		0x00ff
+#define WL_CHANSPEC_CHAN_SHIFT		0
+#define WL_CHANSPEC_CHAN1_MASK		0x000f
+#define WL_CHANSPEC_CHAN1_SHIFT		0
+#define WL_CHANSPEC_CHAN2_MASK		0x00f0
+#define WL_CHANSPEC_CHAN2_SHIFT		4
+
+#define WL_CHANSPEC_CTL_SB_MASK		0x0700
+#define WL_CHANSPEC_CTL_SB_SHIFT	8
+#define WL_CHANSPEC_CTL_SB_LLL		0x0000
+#define WL_CHANSPEC_CTL_SB_LLU		0x0100
+#define WL_CHANSPEC_CTL_SB_LUL		0x0200
+#define WL_CHANSPEC_CTL_SB_LUU		0x0300
+#define WL_CHANSPEC_CTL_SB_ULL		0x0400
+#define WL_CHANSPEC_CTL_SB_ULU		0x0500
+#define WL_CHANSPEC_CTL_SB_UUL		0x0600
+#define WL_CHANSPEC_CTL_SB_UUU		0x0700
+#define WL_CHANSPEC_CTL_SB_LL		WL_CHANSPEC_CTL_SB_LLL
+#define WL_CHANSPEC_CTL_SB_LU		WL_CHANSPEC_CTL_SB_LLU
+#define WL_CHANSPEC_CTL_SB_UL		WL_CHANSPEC_CTL_SB_LUL
+#define WL_CHANSPEC_CTL_SB_UU		WL_CHANSPEC_CTL_SB_LUU
+#define WL_CHANSPEC_CTL_SB_L		WL_CHANSPEC_CTL_SB_LLL
+#define WL_CHANSPEC_CTL_SB_U		WL_CHANSPEC_CTL_SB_LLU
+#define WL_CHANSPEC_CTL_SB_LOWER 	WL_CHANSPEC_CTL_SB_LLL
+#define WL_CHANSPEC_CTL_SB_UPPER	WL_CHANSPEC_CTL_SB_LLU
+
+#define WL_CHANSPEC_BW_MASK		0x3800
+#define WL_CHANSPEC_BW_SHIFT		11
+#define WL_CHANSPEC_BW_5		0x0000
+#define WL_CHANSPEC_BW_10		0x0800
+#define WL_CHANSPEC_BW_20		0x1000
+#define WL_CHANSPEC_BW_40		0x1800
+#define WL_CHANSPEC_BW_80		0x2000
+#define WL_CHANSPEC_BW_160		0x2800
+#define WL_CHANSPEC_BW_8080		0x3000
+
+#define WL_CHANSPEC_BAND_MASK		0xc000
+#define WL_CHANSPEC_BAND_SHIFT		14
+#define WL_CHANSPEC_BAND_2G		0x0000
+#define WL_CHANSPEC_BAND_3G		0x4000
+#define WL_CHANSPEC_BAND_4G		0x8000
+#define WL_CHANSPEC_BAND_5G		0xc000
+#define INVCHANSPEC			255
+
+
+#define LOWER_20_SB(channel)		(((channel) > CH_10MHZ_APART) ? \
+					((channel) - CH_10MHZ_APART) : 0)
+#define UPPER_20_SB(channel)		(((channel) < (MAXCHANNEL - CH_10MHZ_APART)) ? \
+					((channel) + CH_10MHZ_APART) : 0)
+#define LOWER_40_SB(channel)		((channel) - CH_20MHZ_APART)
+#define UPPER_40_SB(channel)		((channel) + CH_20MHZ_APART)
+#define CHSPEC_WLCBANDUNIT(chspec)	(CHSPEC_IS5G(chspec) ? BAND_5G_INDEX : BAND_2G_INDEX)
+#define CH20MHZ_CHSPEC(channel)		(chanspec_t)((chanspec_t)(channel) | WL_CHANSPEC_BW_20 | \
+					(((channel) <= CH_MAX_2G_CHANNEL) ? \
+					WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G))
+#define NEXT_20MHZ_CHAN(channel)	(((channel) < (MAXCHANNEL - CH_20MHZ_APART)) ? \
+					((channel) + CH_20MHZ_APART) : 0)
+#define CH40MHZ_CHSPEC(channel, ctlsb)	(chanspec_t) \
+					((channel) | (ctlsb) | WL_CHANSPEC_BW_40 | \
+					((channel) <= CH_MAX_2G_CHANNEL ? WL_CHANSPEC_BAND_2G : \
+					WL_CHANSPEC_BAND_5G))
+#define CH80MHZ_CHSPEC(channel, ctlsb)	(chanspec_t) \
+					((channel) | (ctlsb) | \
+					 WL_CHANSPEC_BW_80 | WL_CHANSPEC_BAND_5G)
+#define CH160MHZ_CHSPEC(channel, ctlsb)	(chanspec_t) \
+					((channel) | (ctlsb) | \
+					 WL_CHANSPEC_BW_160 | WL_CHANSPEC_BAND_5G)
+
+
+#define CHSPEC_CHANNEL(chspec)		((uint8)((chspec) & WL_CHANSPEC_CHAN_MASK))
+#define CHSPEC_CHAN1(chspec)		((chspec) & WL_CHANSPEC_CHAN1_MASK)
+#define CHSPEC_CHAN2(chspec)		((chspec) & WL_CHANSPEC_CHAN2_MASK)
+#define CHSPEC_BAND(chspec)		((chspec) & WL_CHANSPEC_BAND_MASK)
+#define CHSPEC_CTL_SB(chspec)		((chspec) & WL_CHANSPEC_CTL_SB_MASK)
+#define CHSPEC_BW(chspec)		((chspec) & WL_CHANSPEC_BW_MASK)
+
+#ifdef WL11N_20MHZONLY
+
+#define CHSPEC_IS10(chspec)	0
+#define CHSPEC_IS20(chspec)	1
+#ifndef CHSPEC_IS40
+#define CHSPEC_IS40(chspec)	0
+#endif
+#ifndef CHSPEC_IS80
+#define CHSPEC_IS80(chspec)	0
+#endif
+#ifndef CHSPEC_IS160
+#define CHSPEC_IS160(chspec)	0
+#endif
+#ifndef CHSPEC_IS8080
+#define CHSPEC_IS8080(chspec)	0
+#endif
+
+#else 
+
+#define CHSPEC_IS10(chspec)	(((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_10)
+#define CHSPEC_IS20(chspec)	(((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_20)
+#ifndef CHSPEC_IS40
+#define CHSPEC_IS40(chspec)	(((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_40)
+#endif
+#ifndef CHSPEC_IS80
+#define CHSPEC_IS80(chspec)	(((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_80)
+#endif
+#ifndef CHSPEC_IS160
+#define CHSPEC_IS160(chspec)	(((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_160)
+#endif
+#ifndef CHSPEC_IS8080
+#define CHSPEC_IS8080(chspec)	(((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_8080)
+#endif
+
+#endif 
+
+#define CHSPEC_IS5G(chspec)	(((chspec) & WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_5G)
+#define CHSPEC_IS2G(chspec)	(((chspec) & WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_2G)
+#define CHSPEC_SB_UPPER(chspec)	\
+	((((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_UPPER) && \
+	(((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_40))
+#define CHSPEC_SB_LOWER(chspec)	\
+	((((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_LOWER) && \
+	(((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_40))
+#define CHSPEC2WLC_BAND(chspec) (CHSPEC_IS5G(chspec) ? WLC_BAND_5G : WLC_BAND_2G)
+
+
+#define CHANSPEC_STR_LEN    20
+
+
+
+#define WL_LCHANSPEC_CHAN_MASK		0x00ff
+#define WL_LCHANSPEC_CHAN_SHIFT		     0
+
+#define WL_LCHANSPEC_CTL_SB_MASK	0x0300
+#define WL_LCHANSPEC_CTL_SB_SHIFT	     8
+#define WL_LCHANSPEC_CTL_SB_LOWER	0x0100
+#define WL_LCHANSPEC_CTL_SB_UPPER	0x0200
+#define WL_LCHANSPEC_CTL_SB_NONE	0x0300
+
+#define WL_LCHANSPEC_BW_MASK		0x0C00
+#define WL_LCHANSPEC_BW_SHIFT		    10
+#define WL_LCHANSPEC_BW_10		0x0400
+#define WL_LCHANSPEC_BW_20		0x0800
+#define WL_LCHANSPEC_BW_40		0x0C00
+
+#define WL_LCHANSPEC_BAND_MASK		0xf000
+#define WL_LCHANSPEC_BAND_SHIFT		    12
+#define WL_LCHANSPEC_BAND_5G		0x1000
+#define WL_LCHANSPEC_BAND_2G		0x2000
+
+#define LCHSPEC_CHANNEL(chspec)	((uint8)((chspec) & WL_LCHANSPEC_CHAN_MASK))
+#define LCHSPEC_BAND(chspec)	((chspec) & WL_LCHANSPEC_BAND_MASK)
+#define LCHSPEC_CTL_SB(chspec)	((chspec) & WL_LCHANSPEC_CTL_SB_MASK)
+#define LCHSPEC_BW(chspec)	((chspec) & WL_LCHANSPEC_BW_MASK)
+#define LCHSPEC_IS10(chspec)	(((chspec) & WL_LCHANSPEC_BW_MASK) == WL_LCHANSPEC_BW_10)
+#define LCHSPEC_IS20(chspec)	(((chspec) & WL_LCHANSPEC_BW_MASK) == WL_LCHANSPEC_BW_20)
+#define LCHSPEC_IS40(chspec)	(((chspec) & WL_LCHANSPEC_BW_MASK) == WL_LCHANSPEC_BW_40)
+#define LCHSPEC_IS5G(chspec)	(((chspec) & WL_LCHANSPEC_BAND_MASK) == WL_LCHANSPEC_BAND_5G)
+#define LCHSPEC_IS2G(chspec)	(((chspec) & WL_LCHANSPEC_BAND_MASK) == WL_LCHANSPEC_BAND_2G)
+
+#define LCHSPEC_CREATE(chan, band, bw, sb)  ((uint16)((chan) | (sb) | (bw) | (band)))
+
+#endif 
+
+
+
+
+#define WF_CHAN_FACTOR_2_4_G		4814	
+
+
+#define WF_CHAN_FACTOR_5_G		10000	
+
+
+#define WF_CHAN_FACTOR_4_G		8000	
+
+
+#define WLC_MAXRATE	108	
+#define WLC_RATE_1M	2	
+#define WLC_RATE_2M	4	
+#define WLC_RATE_5M5	11	
+#define WLC_RATE_11M	22	
+#define WLC_RATE_6M	12	
+#define WLC_RATE_9M	18	
+#define WLC_RATE_12M	24	
+#define WLC_RATE_18M	36	
+#define WLC_RATE_24M	48	
+#define WLC_RATE_36M	72	
+#define WLC_RATE_48M	96	
+#define WLC_RATE_54M	108	
+
+#define WLC_2G_25MHZ_OFFSET		5	
+
+
+extern char * wf_chspec_ntoa(chanspec_t chspec, char *buf);
+
+
+extern chanspec_t wf_chspec_aton(const char *a);
+
+
+extern bool wf_chspec_malformed(chanspec_t chanspec);
+
+
+extern bool wf_chspec_valid(chanspec_t chanspec);
+
+
+extern uint8 wf_chspec_ctlchan(chanspec_t chspec);
+
+
+extern chanspec_t wf_chspec_ctlchspec(chanspec_t chspec);
+
+
+extern chanspec_t wf_chspec_primary40_chspec(chanspec_t chspec);
+
+
+extern int wf_mhz2channel(uint freq, uint start_factor);
+
+
+extern int wf_channel2mhz(uint channel, uint start_factor);
+
+#endif	
diff --git a/drivers/net/wireless/bcmdhd/include/htsf.h b/drivers/net/wireless/bcmdhd/include/htsf.h
new file mode 100644
index 0000000..d875edb
--- /dev/null
+++ b/drivers/net/wireless/bcmdhd/include/htsf.h
@@ -0,0 +1,74 @@
+/*
+ * Time stamps for latency measurements 
+ *
+ * Copyright (C) 1999-2011, Broadcom Corporation
+ * 
+ *         Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: htsf.h 277737 2011-08-16 17:54:59Z $
+ */
+#ifndef _HTSF_H_
+#define _HTSF_H_
+
+#define HTSFMAGIC       	0xCDCDABAB  /* in network order for tcpdump  */
+#define HTSFENDMAGIC    	0xEFEFABAB  /* to distinguish from RT2 magic */
+#define HTSF_HOSTOFFSET		102
+#define HTSF_DNGLOFFSET		HTSF_HOSTOFFSET	- 4
+#define HTSF_DNGLOFFSET2	HTSF_HOSTOFFSET	+ 106
+#define HTSF_MIN_PKTLEN 	200
+#define ETHER_TYPE_BRCM_PKTDLYSTATS     0x886d
+
+typedef enum htsfts_type {
+	T10,
+	T20,
+	T30,
+	T40,
+	T50,
+	T60,
+	T70,
+	T80,
+	T90,
+	TA0,
+	TE0
+} htsf_timestamp_t;
+
+typedef struct {
+	uint32 magic;
+	uint32 prio;
+	uint32 seqnum;
+	uint32 misc;
+	uint32 c10;
+	uint32 t10;
+	uint32 c20;
+	uint32 t20;
+	uint32 t30;
+	uint32 t40;
+	uint32 t50;
+	uint32 t60;
+	uint32 t70;
+	uint32 t80;
+	uint32 t90;
+	uint32 cA0;
+	uint32 tA0;
+	uint32 cE0;
+	uint32 tE0;
+	uint32 endmagic;
+} htsfts_t;
+
+#endif /* _HTSF_H_ */
diff --git a/drivers/net/wireless/bcmdhd/include/wlc_clm_rates.h b/drivers/net/wireless/bcmdhd/include/wlc_clm_rates.h
new file mode 100644
index 0000000..d9061bb
--- /dev/null
+++ b/drivers/net/wireless/bcmdhd/include/wlc_clm_rates.h
@@ -0,0 +1,255 @@
+/*
+ * Indices for 802.11 a/b/g/n/ac 1-3 chain symmetric transmit rates
+ * Copyright (C) 2012, Broadcom Corporation
+ * All Rights Reserved.
+ * 
+ * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation;
+ * the contents of this file may not be disclosed to third parties, copied
+ * or duplicated in any form, in whole or in part, without the prior
+ * written permission of Broadcom Corporation.
+ *
+ * $Id: wlc_clm_rates.h 252708 2011-04-12 06:45:56Z $
+ */
+
+#ifndef _WLC_CLM_RATES_H_
+#define _WLC_CLM_RATES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef enum clm_rates {
+	/************
+	 * 1 chain  *
+	 ************
+	 */
+
+	/* 1 Stream */
+	CLM_RATE_1X1_DSSS_1         = 0,
+	CLM_RATE_1X1_DSSS_2         = 1,
+	CLM_RATE_1X1_DSSS_5_5       = 2,
+	CLM_RATE_1X1_DSSS_11        = 3,
+
+	CLM_RATE_1X1_OFDM_6         = 4,
+	CLM_RATE_1X1_OFDM_9         = 5,
+	CLM_RATE_1X1_OFDM_12        = 6,
+	CLM_RATE_1X1_OFDM_18        = 7,
+	CLM_RATE_1X1_OFDM_24        = 8,
+	CLM_RATE_1X1_OFDM_36        = 9,
+	CLM_RATE_1X1_OFDM_48        = 10,
+	CLM_RATE_1X1_OFDM_54        = 11,
+
+	CLM_RATE_1X1_MCS0           = 12,
+	CLM_RATE_1X1_MCS1           = 13,
+	CLM_RATE_1X1_MCS2           = 14,
+	CLM_RATE_1X1_MCS3           = 15,
+	CLM_RATE_1X1_MCS4           = 16,
+	CLM_RATE_1X1_MCS5           = 17,
+	CLM_RATE_1X1_MCS6           = 18,
+	CLM_RATE_1X1_MCS7           = 19,
+
+	CLM_RATE_1X1_VHT0SS1        = 12,
+	CLM_RATE_1X1_VHT1SS1        = 13,
+	CLM_RATE_1X1_VHT2SS1        = 14,
+	CLM_RATE_1X1_VHT3SS1        = 15,
+	CLM_RATE_1X1_VHT4SS1        = 16,
+	CLM_RATE_1X1_VHT5SS1        = 17,
+	CLM_RATE_1X1_VHT6SS1        = 18,
+	CLM_RATE_1X1_VHT7SS1        = 19,
+	CLM_RATE_1X1_VHT8SS1        = 20,
+	CLM_RATE_1X1_VHT9SS1        = 21,
+
+
+	/************
+	 * 2 chains *
+	 ************
+	 */
+
+	/* 1 Stream expanded + 1 */
+	CLM_RATE_1X2_DSSS_1         = 22,
+	CLM_RATE_1X2_DSSS_2         = 23,
+	CLM_RATE_1X2_DSSS_5_5       = 24,
+	CLM_RATE_1X2_DSSS_11        = 25,
+
+	CLM_RATE_1X2_CDD_OFDM_6     = 26,
+	CLM_RATE_1X2_CDD_OFDM_9     = 27,
+	CLM_RATE_1X2_CDD_OFDM_12    = 28,
+	CLM_RATE_1X2_CDD_OFDM_18    = 29,
+	CLM_RATE_1X2_CDD_OFDM_24    = 30,
+	CLM_RATE_1X2_CDD_OFDM_36    = 31,
+	CLM_RATE_1X2_CDD_OFDM_48    = 32,
+	CLM_RATE_1X2_CDD_OFDM_54    = 33,
+
+	CLM_RATE_1X2_CDD_MCS0       = 34,
+	CLM_RATE_1X2_CDD_MCS1       = 35,
+	CLM_RATE_1X2_CDD_MCS2       = 36,
+	CLM_RATE_1X2_CDD_MCS3       = 37,
+	CLM_RATE_1X2_CDD_MCS4       = 38,
+	CLM_RATE_1X2_CDD_MCS5       = 39,
+	CLM_RATE_1X2_CDD_MCS6       = 40,
+	CLM_RATE_1X2_CDD_MCS7       = 41,
+
+	CLM_RATE_1X2_VHT0SS1        = 34,
+	CLM_RATE_1X2_VHT1SS1        = 35,
+	CLM_RATE_1X2_VHT2SS1        = 36,
+	CLM_RATE_1X2_VHT3SS1        = 37,
+	CLM_RATE_1X2_VHT4SS1        = 38,
+	CLM_RATE_1X2_VHT5SS1        = 39,
+	CLM_RATE_1X2_VHT6SS1        = 40,
+	CLM_RATE_1X2_VHT7SS1        = 41,
+	CLM_RATE_1X2_VHT8SS1        = 42,
+	CLM_RATE_1X2_VHT9SS1        = 43,
+
+	/* 2 Streams */
+	CLM_RATE_2X2_STBC_MCS0      = 44,
+	CLM_RATE_2X2_STBC_MCS1      = 45,
+	CLM_RATE_2X2_STBC_MCS2      = 46,
+	CLM_RATE_2X2_STBC_MCS3      = 47,
+	CLM_RATE_2X2_STBC_MCS4      = 48,
+	CLM_RATE_2X2_STBC_MCS5      = 49,
+	CLM_RATE_2X2_STBC_MCS6      = 50,
+	CLM_RATE_2X2_STBC_MCS7      = 51,
+
+	CLM_RATE_2X2_STBC_VHT0SS1   = 44,
+	CLM_RATE_2X2_STBC_VHT1SS1   = 45,
+	CLM_RATE_2X2_STBC_VHT2SS1   = 46,
+	CLM_RATE_2X2_STBC_VHT3SS1   = 47,
+	CLM_RATE_2X2_STBC_VHT4SS1   = 48,
+	CLM_RATE_2X2_STBC_VHT5SS1   = 49,
+	CLM_RATE_2X2_STBC_VHT6SS1   = 50,
+	CLM_RATE_2X2_STBC_VHT7SS1   = 51,
+	CLM_RATE_2X2_STBC_VHT8SS1   = 52,
+	CLM_RATE_2X2_STBC_VHT9SS1   = 53,
+
+	CLM_RATE_2X2_SDM_MCS8       = 54,
+	CLM_RATE_2X2_SDM_MCS9       = 55,
+	CLM_RATE_2X2_SDM_MCS10      = 56,
+	CLM_RATE_2X2_SDM_MCS11      = 57,
+	CLM_RATE_2X2_SDM_MCS12      = 58,
+	CLM_RATE_2X2_SDM_MCS13      = 59,
+	CLM_RATE_2X2_SDM_MCS14      = 60,
+	CLM_RATE_2X2_SDM_MCS15      = 61,
+
+	CLM_RATE_2X2_VHT0SS2        = 54,
+	CLM_RATE_2X2_VHT1SS2        = 55,
+	CLM_RATE_2X2_VHT2SS2        = 56,
+	CLM_RATE_2X2_VHT3SS2        = 57,
+	CLM_RATE_2X2_VHT4SS2        = 58,
+	CLM_RATE_2X2_VHT5SS2        = 59,
+	CLM_RATE_2X2_VHT6SS2        = 60,
+	CLM_RATE_2X2_VHT7SS2        = 61,
+	CLM_RATE_2X2_VHT8SS2        = 62,
+	CLM_RATE_2X2_VHT9SS2        = 63,
+
+
+	/************
+	 * 3 chains *
+	 ************
+	 */
+
+	/* 1 Stream expanded + 2 */
+	CLM_RATE_1X3_DSSS_1         = 64,
+	CLM_RATE_1X3_DSSS_2         = 65,
+	CLM_RATE_1X3_DSSS_5_5       = 66,
+	CLM_RATE_1X3_DSSS_11        = 67,
+
+	CLM_RATE_1X3_CDD_OFDM_6     = 68,
+	CLM_RATE_1X3_CDD_OFDM_9     = 69,
+	CLM_RATE_1X3_CDD_OFDM_12    = 70,
+	CLM_RATE_1X3_CDD_OFDM_18    = 71,
+	CLM_RATE_1X3_CDD_OFDM_24    = 72,
+	CLM_RATE_1X3_CDD_OFDM_36    = 73,
+	CLM_RATE_1X3_CDD_OFDM_48    = 74,
+	CLM_RATE_1X3_CDD_OFDM_54    = 75,
+
+	CLM_RATE_1X3_CDD_MCS0       = 76,
+	CLM_RATE_1X3_CDD_MCS1       = 77,
+	CLM_RATE_1X3_CDD_MCS2       = 78,
+	CLM_RATE_1X3_CDD_MCS3       = 79,
+	CLM_RATE_1X3_CDD_MCS4       = 80,
+	CLM_RATE_1X3_CDD_MCS5       = 81,
+	CLM_RATE_1X3_CDD_MCS6       = 82,
+	CLM_RATE_1X3_CDD_MCS7       = 83,
+
+	CLM_RATE_1X3_VHT0SS1        = 76,
+	CLM_RATE_1X3_VHT1SS1        = 77,
+	CLM_RATE_1X3_VHT2SS1        = 78,
+	CLM_RATE_1X3_VHT3SS1        = 79,
+	CLM_RATE_1X3_VHT4SS1        = 80,
+	CLM_RATE_1X3_VHT5SS1        = 81,
+	CLM_RATE_1X3_VHT6SS1        = 82,
+	CLM_RATE_1X3_VHT7SS1        = 83,
+	CLM_RATE_1X3_VHT8SS1        = 84,
+	CLM_RATE_1X3_VHT9SS1        = 85,
+
+	/* 2 Streams expanded + 1 */
+	CLM_RATE_2X3_STBC_MCS0      = 86,
+	CLM_RATE_2X3_STBC_MCS1      = 87,
+	CLM_RATE_2X3_STBC_MCS2      = 88,
+	CLM_RATE_2X3_STBC_MCS3      = 89,
+	CLM_RATE_2X3_STBC_MCS4      = 90,
+	CLM_RATE_2X3_STBC_MCS5      = 91,
+	CLM_RATE_2X3_STBC_MCS6      = 92,
+	CLM_RATE_2X3_STBC_MCS7      = 93,
+
+	CLM_RATE_2X3_STBC_VHT0SS1   = 86,
+	CLM_RATE_2X3_STBC_VHT1SS1   = 87,
+	CLM_RATE_2X3_STBC_VHT2SS1   = 88,
+	CLM_RATE_2X3_STBC_VHT3SS1   = 89,
+	CLM_RATE_2X3_STBC_VHT4SS1   = 90,
+	CLM_RATE_2X3_STBC_VHT5SS1   = 91,
+	CLM_RATE_2X3_STBC_VHT6SS1   = 92,
+	CLM_RATE_2X3_STBC_VHT7SS1   = 93,
+	CLM_RATE_2X3_STBC_VHT8SS1   = 94,
+	CLM_RATE_2X3_STBC_VHT9SS1   = 95,
+
+	CLM_RATE_2X3_SDM_MCS8       = 96,
+	CLM_RATE_2X3_SDM_MCS9       = 97,
+	CLM_RATE_2X3_SDM_MCS10      = 98,
+	CLM_RATE_2X3_SDM_MCS11      = 99,
+	CLM_RATE_2X3_SDM_MCS12      = 100,
+	CLM_RATE_2X3_SDM_MCS13      = 101,
+	CLM_RATE_2X3_SDM_MCS14      = 102,
+	CLM_RATE_2X3_SDM_MCS15      = 103,
+
+	CLM_RATE_2X3_VHT0SS2        = 96,
+	CLM_RATE_2X3_VHT1SS2        = 97,
+	CLM_RATE_2X3_VHT2SS2        = 98,
+	CLM_RATE_2X3_VHT3SS2        = 99,
+	CLM_RATE_2X3_VHT4SS2        = 100,
+	CLM_RATE_2X3_VHT5SS2        = 101,
+	CLM_RATE_2X3_VHT6SS2        = 102,
+	CLM_RATE_2X3_VHT7SS2        = 103,
+	CLM_RATE_2X3_VHT8SS2        = 104,
+	CLM_RATE_2X3_VHT9SS2        = 105,
+
+	/* 3 Streams */
+	CLM_RATE_3X3_SDM_MCS16      = 106,
+	CLM_RATE_3X3_SDM_MCS17      = 107,
+	CLM_RATE_3X3_SDM_MCS18      = 108,
+	CLM_RATE_3X3_SDM_MCS19      = 109,
+	CLM_RATE_3X3_SDM_MCS20      = 110,
+	CLM_RATE_3X3_SDM_MCS21      = 111,
+	CLM_RATE_3X3_SDM_MCS22      = 112,
+	CLM_RATE_3X3_SDM_MCS23      = 113,
+
+	CLM_RATE_3X3_VHT0SS3        = 106,
+	CLM_RATE_3X3_VHT1SS3        = 107,
+	CLM_RATE_3X3_VHT2SS3        = 108,
+	CLM_RATE_3X3_VHT3SS3        = 109,
+	CLM_RATE_3X3_VHT4SS3        = 110,
+	CLM_RATE_3X3_VHT5SS3        = 111,
+	CLM_RATE_3X3_VHT6SS3        = 112,
+	CLM_RATE_3X3_VHT7SS3        = 113,
+	CLM_RATE_3X3_VHT8SS3        = 114,
+	CLM_RATE_3X3_VHT9SS3        = 115,
+
+	/* Number of rate codes */
+	CLM_NUMRATES				= 116,
+	} clm_rates_t;
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _WLC_CLM_RATES_H_ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c
index 2e1a317..826dd3e 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c
@@ -551,6 +551,9 @@
 
 	mutex_lock(&priv->mutex);
 
+	if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+		goto out;
+
 	if (unlikely(test_bit(STATUS_SCANNING, &priv->status))) {
 		IWL_DEBUG_MAC80211(priv, "leave - scanning\n");
 		goto out;
diff --git a/drivers/net/wireless/libra/Makefile b/drivers/net/wireless/libra/Makefile
new file mode 100644
index 0000000..849de05
--- /dev/null
+++ b/drivers/net/wireless/libra/Makefile
@@ -0,0 +1,14 @@
+
+# Makefile for wlan sdio if driver
+
+librasdioif-objs += libra_sdioif.o ../wcnss/qcomwlan_secif.o
+
+ifdef CONFIG_ARCH_MSM8X60
+	librasdioif-objs += qcomwlan_pwrif.o
+endif
+
+ifdef CONFIG_ARCH_MSM7X27A
+	librasdioif-objs += qcomwlan7x27a_pwrif.o
+endif
+
+obj-$(CONFIG_LIBRA_SDIOIF) += librasdioif.o
diff --git a/drivers/net/wireless/libra/libra_sdioif.c b/drivers/net/wireless/libra/libra_sdioif.c
new file mode 100644
index 0000000..aa7970a
--- /dev/null
+++ b/drivers/net/wireless/libra/libra_sdioif.c
@@ -0,0 +1,547 @@
+/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/libra_sdioif.h>
+#include <linux/delay.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/module.h>
+
+/* Libra SDIO function device */
+static struct sdio_func *libra_sdio_func;
+static struct mmc_host *libra_mmc_host;
+static int libra_mmc_host_index;
+
+/* SDIO Card ID / Device ID */
+static unsigned short  libra_sdio_card_id;
+
+static suspend_handler_t *libra_suspend_hldr;
+static resume_handler_t *libra_resume_hldr;
+static notify_card_removal_t *libra_notify_card_removal_hdlr;
+static shutdown_handler_t *libra_sdio_shutdown_hdlr;
+
+int libra_enable_sdio_irq_in_chip(struct sdio_func *func, u8 enable)
+{
+	unsigned char reg = 0;
+	int err = 0;
+
+	sdio_claim_host(func);
+
+	/* Read the value into reg */
+	libra_sdiocmd52(func, SDIO_CCCR_IENx, &reg, 0, &err);
+	if (err)
+		printk(KERN_ERR "%s: Could not read  SDIO_CCCR_IENx register "
+				"err=%d\n", __func__, err);
+
+	if (libra_mmc_host) {
+		if (enable) {
+			reg |= 1 << func->num;
+			reg |= 1;
+		} else {
+			reg &= ~(1 << func->num);
+		}
+		libra_sdiocmd52(func, SDIO_CCCR_IENx, &reg, 1, &err);
+		if (err)
+			printk(KERN_ERR "%s: Could not enable/disable irq "
+					 "err=%d\n", __func__, err);
+	 }
+	sdio_release_host(func);
+
+	return err;
+}
+EXPORT_SYMBOL(libra_enable_sdio_irq_in_chip);
+
+/**
+ * libra_sdio_configure() - Function to configure the SDIO device param
+ * @libra_sdio_rxhandler    Rx handler
+ * @func_drv_fn             Function driver function for special setup
+ * @funcdrv_timeout         Function Enable timeout
+ * @blksize                 Block size
+ *
+ * Configure SDIO device, enable function and set block size
+ */
+int libra_sdio_configure(sdio_irq_handler_t libra_sdio_rxhandler,
+	void  (*func_drv_fn)(int *status),
+	unsigned int funcdrv_timeout, unsigned int blksize)
+{
+	int err_ret = 0;
+	struct sdio_func *func = libra_sdio_func;
+
+	if (libra_sdio_func == NULL) {
+		printk(KERN_ERR "%s: Error SDIO card not detected\n", __func__);
+		goto cfg_error;
+	}
+
+	sdio_claim_host(func);
+
+	/* Currently block sizes are set here. */
+	func->max_blksize = blksize;
+	if (sdio_set_block_size(func, blksize)) {
+		printk(KERN_ERR "%s: Unable to set the block size.\n",
+				__func__);
+		sdio_release_host(func);
+		goto cfg_error;
+	}
+
+	/* Function driver specific configuration. */
+	if (func_drv_fn) {
+		(*func_drv_fn)(&err_ret);
+		if (err_ret) {
+			printk(KERN_ERR "%s: function driver provided configure function error=%d\n",
+				__func__, err_ret);
+			sdio_release_host(func);
+			goto cfg_error;
+		}
+	}
+
+	/* We set this based on the function card. */
+	func->enable_timeout = funcdrv_timeout;
+	err_ret = sdio_enable_func(func);
+	if (err_ret != 0) {
+		printk(KERN_ERR "%s: Unable to enable function %d\n",
+				__func__, err_ret);
+		sdio_release_host(func);
+		goto cfg_error;
+	}
+
+	if (sdio_claim_irq(func, libra_sdio_rxhandler)) {
+		sdio_disable_func(func);
+		printk(KERN_ERR "%s: Unable to claim irq.\n", __func__);
+		sdio_release_host(func);
+		goto cfg_error;
+	}
+
+	libra_enable_sdio_irq_in_chip(func, 0);
+
+	sdio_release_host(func);
+
+	return 0;
+
+cfg_error:
+	return -1;
+
+}
+EXPORT_SYMBOL(libra_sdio_configure);
+
+int libra_sdio_configure_suspend_resume(
+		suspend_handler_t *libra_sdio_suspend_hdlr,
+		resume_handler_t *libra_sdio_resume_hdlr)
+{
+	libra_suspend_hldr = libra_sdio_suspend_hdlr;
+	libra_resume_hldr = libra_sdio_resume_hdlr;
+	return 0;
+}
+EXPORT_SYMBOL(libra_sdio_configure_suspend_resume);
+
+/*
+ * libra_sdio_deconfigure() - Function to reset the SDIO device param
+ */
+void libra_sdio_deconfigure(struct sdio_func *func)
+{
+	if (NULL == libra_sdio_func)
+		return;
+
+	sdio_claim_host(func);
+	sdio_release_irq(func);
+	sdio_disable_func(func);
+	sdio_release_host(func);
+}
+EXPORT_SYMBOL(libra_sdio_deconfigure);
+
+int libra_enable_sdio_irq(struct sdio_func *func, u8 enable)
+{
+	if (libra_mmc_host && libra_mmc_host->ops &&
+			libra_mmc_host->ops->enable_sdio_irq) {
+		libra_mmc_host->ops->enable_sdio_irq(libra_mmc_host, enable);
+		return 0;
+	}
+
+	printk(KERN_ERR "%s: Could not enable disable irq\n", __func__);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(libra_enable_sdio_irq);
+
+int libra_disable_sdio_irq_capability(struct sdio_func *func, u8 disable)
+{
+	if (libra_mmc_host) {
+		if (disable)
+			libra_mmc_host->caps &= ~MMC_CAP_SDIO_IRQ;
+		else
+			libra_mmc_host->caps |= MMC_CAP_SDIO_IRQ;
+		return 0;
+	}
+	printk(KERN_ERR "%s: Could not change sdio capabilities to polling\n",
+			__func__);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(libra_disable_sdio_irq_capability);
+
+/*
+ * libra_sdio_release_irq() - Function to release IRQ
+ */
+void libra_sdio_release_irq(struct sdio_func *func)
+{
+	if (NULL == libra_sdio_func)
+		return;
+
+	sdio_release_irq(func);
+}
+EXPORT_SYMBOL(libra_sdio_release_irq);
+
+/*
+ * libra_sdio_disable_func() - Function to disable sdio func
+ */
+void libra_sdio_disable_func(struct sdio_func *func)
+{
+	if (NULL == libra_sdio_func)
+		return;
+
+	sdio_disable_func(func);
+}
+EXPORT_SYMBOL(libra_sdio_disable_func);
+
+/*
+ * Return the SDIO Function device
+ */
+struct sdio_func *libra_getsdio_funcdev(void)
+{
+	return libra_sdio_func;
+}
+EXPORT_SYMBOL(libra_getsdio_funcdev);
+
+/*
+ * Set function driver as the private data for the function device
+ */
+void libra_sdio_setprivdata(struct sdio_func *sdio_func_dev,
+		void *padapter)
+{
+	if (NULL == libra_sdio_func)
+		return;
+
+	sdio_set_drvdata(sdio_func_dev, padapter);
+}
+EXPORT_SYMBOL(libra_sdio_setprivdata);
+
+/*
+ * Return private data of the function device.
+ */
+void *libra_sdio_getprivdata(struct sdio_func *sdio_func_dev)
+{
+	return sdio_get_drvdata(sdio_func_dev);
+}
+EXPORT_SYMBOL(libra_sdio_getprivdata);
+
+/*
+ * Function driver claims the SDIO device
+ */
+void libra_claim_host(struct sdio_func *sdio_func_dev,
+		pid_t *curr_claimed, pid_t current_pid, atomic_t *claim_count)
+{
+	if (NULL == libra_sdio_func)
+		return;
+
+	if (*curr_claimed == current_pid) {
+		atomic_inc(claim_count);
+		return;
+	}
+
+	/* Go ahead and claim the host if not locked by anybody. */
+	sdio_claim_host(sdio_func_dev);
+
+	*curr_claimed = current_pid;
+	atomic_inc(claim_count);
+
+}
+EXPORT_SYMBOL(libra_claim_host);
+
+/*
+ * Function driver releases the SDIO device
+ */
+void libra_release_host(struct sdio_func *sdio_func_dev,
+		pid_t *curr_claimed, pid_t current_pid, atomic_t *claim_count)
+{
+
+	if (NULL == libra_sdio_func)
+		return;
+
+	if (*curr_claimed != current_pid) {
+		/* Dont release  */
+		return;
+	}
+
+	atomic_dec(claim_count);
+	if (atomic_read(claim_count) == 0) {
+		*curr_claimed = 0;
+		sdio_release_host(sdio_func_dev);
+	}
+}
+EXPORT_SYMBOL(libra_release_host);
+
+void libra_sdiocmd52(struct sdio_func *sdio_func_dev, unsigned int addr,
+	u8 *byte_var, int write, int *err_ret)
+{
+	if (write)
+		sdio_writeb(sdio_func_dev, byte_var[0], addr, err_ret);
+	else
+		byte_var[0] = sdio_readb(sdio_func_dev, addr, err_ret);
+}
+EXPORT_SYMBOL(libra_sdiocmd52);
+
+u8 libra_sdio_readsb(struct sdio_func *func, void *dst,
+	unsigned int addr, int count)
+{
+	return sdio_readsb(func, dst, addr, count);
+}
+EXPORT_SYMBOL(libra_sdio_readsb);
+
+int libra_sdio_memcpy_fromio(struct sdio_func *func,
+		void *dst, unsigned int addr, int count)
+{
+	return sdio_memcpy_fromio(func, dst, addr, count);
+}
+EXPORT_SYMBOL(libra_sdio_memcpy_fromio);
+
+int libra_sdio_writesb(struct sdio_func *func,
+		unsigned int addr, void *src, int count)
+{
+	return sdio_writesb(func, addr, src, count);
+}
+EXPORT_SYMBOL(libra_sdio_writesb);
+
+int libra_sdio_memcpy_toio(struct sdio_func *func,
+	unsigned int addr, void *src, int count)
+{
+	return sdio_memcpy_toio(func, addr, src, count);
+}
+EXPORT_SYMBOL(libra_sdio_memcpy_toio);
+
+int libra_detect_card_change(void)
+{
+	if (libra_mmc_host) {
+		if (!strcmp(libra_mmc_host->class_dev.class->name, "mmc_host")
+			&& (libra_mmc_host_index == libra_mmc_host->index)) {
+			mmc_detect_change(libra_mmc_host, 0);
+			return 0;
+		}
+	}
+
+	printk(KERN_ERR "%s: Could not trigger card change\n", __func__);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(libra_detect_card_change);
+
+int libra_sdio_enable_polling(void)
+{
+	if (libra_mmc_host) {
+		if (!strcmp(libra_mmc_host->class_dev.class->name, "mmc_host")
+			&& (libra_mmc_host_index == libra_mmc_host->index)) {
+			libra_mmc_host->caps |= MMC_CAP_NEEDS_POLL;
+			mmc_detect_change(libra_mmc_host, 0);
+			return 0;
+		}
+	}
+
+	printk(KERN_ERR "%s: Could not trigger SDIO scan\n", __func__);
+	return -1;
+}
+EXPORT_SYMBOL(libra_sdio_enable_polling);
+
+void libra_sdio_set_clock(struct sdio_func *func, unsigned int clk_freq)
+{
+    struct mmc_host *host = func->card->host;
+    host->ios.clock = clk_freq;
+    host->ops->set_ios(host, &host->ios);
+
+}
+EXPORT_SYMBOL(libra_sdio_set_clock);
+
+/*
+ * API to get SDIO Device Card ID
+ */
+void libra_sdio_get_card_id(struct sdio_func *func, unsigned short *card_id)
+{
+	if (card_id)
+		*card_id = libra_sdio_card_id;
+}
+EXPORT_SYMBOL(libra_sdio_get_card_id);
+
+/*
+ * SDIO Probe
+ */
+static int libra_sdio_probe(struct sdio_func *func,
+		const struct sdio_device_id *sdio_dev_id)
+{
+	libra_mmc_host = func->card->host;
+	libra_mmc_host_index = libra_mmc_host->index;
+	libra_sdio_func = func;
+	libra_sdio_card_id = sdio_dev_id->device;
+
+	printk(KERN_INFO "%s: success with block size of %d device_id=0x%x\n",
+		__func__,
+		func->cur_blksize,
+		sdio_dev_id->device);
+
+	/* Turn off SDIO polling from now on */
+	libra_mmc_host->caps &= ~MMC_CAP_NEEDS_POLL;
+	return 0;
+}
+
+static void libra_sdio_remove(struct sdio_func *func)
+{
+	if (libra_notify_card_removal_hdlr)
+		libra_notify_card_removal_hdlr();
+	libra_sdio_func = NULL;
+
+	printk(KERN_INFO "%s : Module removed.\n", __func__);
+}
+
+#ifdef CONFIG_PM
+static int libra_sdio_suspend(struct device *dev)
+{
+	struct sdio_func *func = dev_to_sdio_func(dev);
+	int ret = 0;
+
+	ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
+
+	if (ret) {
+		printk(KERN_ERR "%s: Error Host doesn't support the keep power capability\n" ,
+			__func__);
+		return ret;
+	}
+	if (libra_suspend_hldr) {
+		/* Disable SDIO IRQ when driver is being suspended */
+		libra_enable_sdio_irq(func, 0);
+		ret = libra_suspend_hldr(func);
+		if (ret) {
+			printk(KERN_ERR
+			"%s: Libra driver is not able to suspend\n" , __func__);
+			/* Error - Restore SDIO IRQ */
+			libra_enable_sdio_irq(func, 1);
+			return ret;
+		}
+	}
+
+
+	return sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ);
+}
+
+static int libra_sdio_resume(struct device *dev)
+{
+	struct sdio_func *func = dev_to_sdio_func(dev);
+
+	if (libra_resume_hldr) {
+		libra_resume_hldr(func);
+		/* Restore SDIO IRQ */
+		libra_enable_sdio_irq(func, 1);
+	}
+
+	return 0;
+}
+#else
+#define libra_sdio_suspend 0
+#define libra_sdio_resume 0
+#endif
+
+static void libra_sdio_shutdown(struct device *dev)
+{
+	if (libra_sdio_shutdown_hdlr) {
+		libra_sdio_shutdown_hdlr();
+		printk(KERN_INFO "%s : Notified shutdown event to Libra driver.\n",
+			 __func__);
+	}
+}
+
+int libra_sdio_register_shutdown_hdlr(
+		shutdown_handler_t *libra_shutdown_hdlr)
+{
+	libra_sdio_shutdown_hdlr = libra_shutdown_hdlr;
+	return 0;
+}
+EXPORT_SYMBOL(libra_sdio_register_shutdown_hdlr);
+
+int libra_sdio_notify_card_removal(
+		notify_card_removal_t *libra_sdio_notify_card_removal_hdlr)
+{
+	libra_notify_card_removal_hdlr = libra_sdio_notify_card_removal_hdlr;
+	return 0;
+}
+EXPORT_SYMBOL(libra_sdio_notify_card_removal);
+
+static struct sdio_device_id libra_sdioid[] = {
+    {.class = 0, .vendor = LIBRA_MAN_ID,  .device = LIBRA_REV_1_0_CARD_ID},
+    {.class = 0, .vendor = VOLANS_MAN_ID, .device = VOLANS_REV_2_0_CARD_ID},
+    {}
+};
+
+static const struct dev_pm_ops libra_sdio_pm_ops = {
+    .suspend = libra_sdio_suspend,
+    .resume = libra_sdio_resume,
+};
+
+static struct sdio_driver libra_sdiofn_driver = {
+	.name      = "libra_sdiofn",
+	.id_table  = libra_sdioid,
+	.probe     = libra_sdio_probe,
+	.remove    = libra_sdio_remove,
+	.drv.pm    = &libra_sdio_pm_ops,
+	.drv.shutdown    = libra_sdio_shutdown,
+};
+
+static int __init libra_sdioif_init(void)
+{
+	libra_sdio_func = NULL;
+	libra_mmc_host = NULL;
+	libra_mmc_host_index = -1;
+	libra_suspend_hldr = NULL;
+	libra_resume_hldr = NULL;
+	libra_notify_card_removal_hdlr = NULL;
+	libra_sdio_shutdown_hdlr = NULL;
+
+	sdio_register_driver(&libra_sdiofn_driver);
+
+	printk(KERN_INFO "%s: Loaded Successfully\n", __func__);
+
+	return 0;
+}
+
+static void __exit libra_sdioif_exit(void)
+{
+	unsigned int attempts = 0;
+
+	if (!libra_detect_card_change()) {
+		do {
+			++attempts;
+			msleep(500);
+		} while (libra_sdio_func != NULL && attempts < 3);
+	}
+
+	if (libra_sdio_func != NULL)
+		printk(KERN_ERR "%s: Card removal not detected\n", __func__);
+
+	sdio_unregister_driver(&libra_sdiofn_driver);
+
+	libra_sdio_func = NULL;
+	libra_mmc_host = NULL;
+	libra_mmc_host_index = -1;
+
+	printk(KERN_INFO "%s: Unloaded Successfully\n", __func__);
+}
+
+module_init(libra_sdioif_init);
+module_exit(libra_sdioif_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("1.0");
+MODULE_DESCRIPTION("WLAN SDIODriver");
diff --git a/drivers/net/wireless/libra/qcomwlan7x27a_pwrif.c b/drivers/net/wireless/libra/qcomwlan7x27a_pwrif.c
new file mode 100644
index 0000000..ef0111a
--- /dev/null
+++ b/drivers/net/wireless/libra/qcomwlan7x27a_pwrif.c
@@ -0,0 +1,225 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio.h>
+#include <mach/rpc_pmapp.h>
+#include <linux/err.h>
+#include <linux/qcomwlan7x27a_pwrif.h>
+#include <linux/module.h>
+
+#define WLAN_GPIO_EXT_POR_N     134
+
+static const char *id = "WLAN";
+
+enum {
+	WLAN_VREG_L17 = 0,
+	WLAN_VREG_S3,
+	WLAN_VREG_TCXO_L11,
+	WLAN_VREG_L19,
+	WLAN_VREG_L5,
+	WLAN_VREG_L6
+};
+
+struct wlan_vreg_info {
+	const char *vreg_id;
+	unsigned int level_min;
+	unsigned int level_max;
+	unsigned int pmapp_id;
+	unsigned int is_vreg_pin_controlled;
+	struct regulator *reg;
+};
+
+
+static struct wlan_vreg_info vreg_info[] = {
+	{"bt",        3050000, 3050000, 21, 1, NULL},
+	{"msme1",     1800000, 1800000, 2,  0, NULL},
+	{"wlan_tcx0", 1800000, 1800000, 53, 0, NULL},
+	{"wlan4",     1200000, 1200000, 23, 0, NULL},
+	{"wlan2",     1350000, 1350000, 9,  1, NULL},
+	{"wlan3",     1200000, 1200000, 10, 1, NULL},
+};
+
+static int qrf6285_init_regs(void)
+{
+	struct regulator_bulk_data regs[ARRAY_SIZE(vreg_info)];
+	int i, rc;
+
+	for (i = 0; i < ARRAY_SIZE(regs); i++) {
+		regs[i].supply = vreg_info[i].vreg_id;
+		regs[i].min_uV = vreg_info[i].level_min;
+		regs[i].max_uV = vreg_info[i].level_max;
+	}
+
+	rc = regulator_bulk_get(NULL, ARRAY_SIZE(regs), regs);
+	if (rc) {
+		pr_err("%s: could not get regulators: %d\n", __func__, rc);
+		goto out;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(regs); i++)
+		vreg_info[i].reg = regs[i].consumer;
+
+	return 0;
+
+out:
+	return rc;
+}
+
+int chip_power_qrf6285(bool on)
+{
+	static bool init_done;
+	int rc = 0, index = 0;
+
+	if (unlikely(!init_done)) {
+		rc = qrf6285_init_regs();
+		if (rc)
+			return rc;
+		else
+			init_done = true;
+	}
+
+	if (on) {
+		rc = gpio_request(WLAN_GPIO_EXT_POR_N, "WLAN_DEEP_SLEEP_N");
+
+		if (rc) {
+			pr_err("WLAN reset GPIO %d request failed %d\n",
+			WLAN_GPIO_EXT_POR_N, rc);
+			goto fail;
+		}
+		rc = gpio_direction_output(WLAN_GPIO_EXT_POR_N, 1);
+		if (rc < 0) {
+			pr_err("WLAN reset GPIO %d set direction failed %d\n",
+			WLAN_GPIO_EXT_POR_N, rc);
+			goto fail_gpio_dir_out;
+		}
+		rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_A0,
+					PMAPP_CLOCK_VOTE_ON);
+		if (rc) {
+			pr_err("%s: Configuring A0 to always"
+			" on failed %d\n", __func__, rc);
+			goto clock_vote_fail;
+		}
+	} else {
+		gpio_set_value_cansleep(WLAN_GPIO_EXT_POR_N, 0);
+		rc = gpio_direction_input(WLAN_GPIO_EXT_POR_N);
+		if (rc) {
+			pr_err("WLAN reset GPIO %d set direction failed %d\n",
+			WLAN_GPIO_EXT_POR_N, rc);
+		}
+		gpio_free(WLAN_GPIO_EXT_POR_N);
+		rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_A0,
+					PMAPP_CLOCK_VOTE_OFF);
+		if (rc) {
+			pr_err("%s: Configuring A0 to turn OFF"
+			" failed %d\n", __func__, rc);
+		}
+	}
+
+	for (index = 0; index < ARRAY_SIZE(vreg_info); index++) {
+		if (on) {
+
+			rc = regulator_set_voltage(vreg_info[index].reg,
+						vreg_info[index].level_min,
+						vreg_info[index].level_max);
+			if (rc) {
+				pr_err("%s:%s set voltage failed %d\n",
+					__func__, vreg_info[index].vreg_id, rc);
+
+				goto vreg_fail;
+			}
+
+			rc = regulator_enable(vreg_info[index].reg);
+			if (rc) {
+				pr_err("%s:%s vreg enable failed %d\n",
+					__func__, vreg_info[index].vreg_id, rc);
+
+				goto vreg_fail;
+			}
+
+			if (vreg_info[index].is_vreg_pin_controlled) {
+				rc = pmapp_vreg_lpm_pincntrl_vote(id,
+					 vreg_info[index].pmapp_id,
+					 PMAPP_CLOCK_ID_A0, 1);
+				if (rc) {
+					pr_err("%s:%s pmapp_vreg_lpm_pincntrl"
+						" for enable failed %d\n",
+						__func__,
+						vreg_info[index].vreg_id, rc);
+					goto vreg_clock_vote_fail;
+				}
+			}
+
+			/*At this point CLK_PWR_REQ is high*/
+			if (WLAN_VREG_L6 == index) {
+				/*
+				 * Configure A0 clock to be slave to
+				 * WLAN_CLK_PWR_REQ
+`				 */
+				rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_A0,
+						PMAPP_CLOCK_VOTE_PIN_CTRL);
+				if (rc) {
+					pr_err("%s: Configuring A0 to Pin"
+					" controllable failed %d\n",
+							 __func__, rc);
+					goto vreg_clock_vote_fail;
+				}
+			}
+
+		} else {
+
+			if (vreg_info[index].is_vreg_pin_controlled) {
+				rc = pmapp_vreg_lpm_pincntrl_vote(id,
+						 vreg_info[index].pmapp_id,
+						 PMAPP_CLOCK_ID_A0, 0);
+				if (rc) {
+					pr_err("%s:%s pmapp_vreg_lpm_pincntrl"
+						" for disable failed %d\n",
+						__func__,
+						vreg_info[index].vreg_id, rc);
+				}
+			}
+			rc = regulator_disable(vreg_info[index].reg);
+			if (rc) {
+				pr_err("%s:%s vreg disable failed %d\n",
+					__func__, vreg_info[index].vreg_id, rc);
+			}
+		}
+	}
+	return 0;
+vreg_fail:
+	index--;
+vreg_clock_vote_fail:
+	while (index >= 0) {
+		rc = regulator_disable(vreg_info[index].reg);
+		if (rc) {
+			pr_err("%s:%s vreg disable failed %d\n",
+				__func__, vreg_info[index].vreg_id, rc);
+		}
+		index--;
+	}
+	if (!on)
+		goto fail;
+clock_vote_fail:
+	gpio_set_value_cansleep(WLAN_GPIO_EXT_POR_N, 0);
+	rc = gpio_direction_input(WLAN_GPIO_EXT_POR_N);
+	if (rc) {
+		pr_err("WLAN reset GPIO %d set direction failed %d\n",
+			WLAN_GPIO_EXT_POR_N, rc);
+	}
+fail_gpio_dir_out:
+	gpio_free(WLAN_GPIO_EXT_POR_N);
+fail:
+	return rc;
+}
+EXPORT_SYMBOL(chip_power_qrf6285);
diff --git a/drivers/net/wireless/libra/qcomwlan_pwrif.c b/drivers/net/wireless/libra/qcomwlan_pwrif.c
new file mode 100644
index 0000000..52b1a51
--- /dev/null
+++ b/drivers/net/wireless/libra/qcomwlan_pwrif.c
@@ -0,0 +1,359 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/qcomwlan_pwrif.h>
+#include <linux/export.h>
+
+#define GPIO_WLAN_DEEP_SLEEP_N  230
+#define GPIO_WLAN_DEEP_SLEEP_N_DRAGON  82
+#define WLAN_RESET_OUT          1
+#define WLAN_RESET              0
+
+static const char *id = "WLAN";
+
+/**
+ * vos_chip_power_qrf8615() - WLAN Power Up Seq for WCN1314 rev 2.0 on QRF 8615
+ * @on - Turn WLAN ON/OFF (1 or 0)
+ *
+ * Power up/down WLAN by turning on/off various regs and asserting/deasserting
+ * Power-on-reset pin. Also, put XO A0 buffer as slave to wlan_clk_pwr_req while
+ * turning ON WLAN and vice-versa.
+ *
+ * This function returns 0 on success or a non-zero value on failure.
+ */
+int vos_chip_power_qrf8615(int on)
+{
+	static char wlan_on;
+	static const char *vregs_qwlan_name[] = {
+		"8058_l20",
+		"8058_l8",
+		"8901_s4",
+		"8901_lvs1",
+		"8901_l0",
+		"8058_s2",
+		"8058_s1",
+	};
+	static const char *vregs_qwlan_pc_name[] = {
+		"8058_l20_pc",
+		"8058_l8_pc",
+		NULL,
+		NULL,
+		"8901_l0_pc",
+		"8058_s2_pc",
+		NULL,
+	};
+	static const int vregs_qwlan_val_min[] = {
+		1800000,
+		3050000,
+		1225000,
+		0,
+		1200000,
+		1300000,
+		500000,
+	};
+	static const int vregs_qwlan_val_max[] = {
+		1800000,
+		3050000,
+		1225000,
+		0,
+		1200000,
+		1300000,
+		1250000,
+	};
+	static const int vregs_qwlan_peek_current[] = {
+		4000,
+		150000,
+		60000,
+		0,
+		32000,
+		130000,
+		0,
+	};
+	static const bool vregs_is_pin_controlled_default[] = {
+		1,
+		1,
+		0,
+		0,
+		1,
+		1,
+		0,
+	};
+	static const bool vregs_is_pin_controlled_dragon[] = {
+		0,
+		0,
+		0,
+		0,
+		0,
+		1,
+		0,
+	};
+	bool const *vregs_is_pin_controlled;
+	static struct regulator *vregs_qwlan[ARRAY_SIZE(vregs_qwlan_name)];
+	static struct regulator *vregs_pc_qwlan[ARRAY_SIZE(vregs_qwlan_name)];
+	static struct msm_xo_voter *wlan_clock;
+	int ret, i, rc = 0;
+	unsigned wlan_gpio_deep_sleep = GPIO_WLAN_DEEP_SLEEP_N;
+
+	vregs_is_pin_controlled = vregs_is_pin_controlled_default;
+
+	if (machine_is_msm8x60_dragon()) {
+		wlan_gpio_deep_sleep = GPIO_WLAN_DEEP_SLEEP_N_DRAGON;
+		vregs_is_pin_controlled = vregs_is_pin_controlled_dragon;
+	}
+	/* WLAN RESET and CLK settings */
+	if (on && !wlan_on) {
+		/*
+		 * Program U12 GPIO expander pin IO1 to de-assert (drive 0)
+		 * WLAN_EXT_POR_N to put WLAN in reset
+		 */
+		rc = gpio_request(wlan_gpio_deep_sleep, "WLAN_DEEP_SLEEP_N");
+		if (rc) {
+			pr_err("WLAN reset GPIO %d request failed\n",
+					wlan_gpio_deep_sleep);
+			goto fail;
+		}
+		rc = gpio_direction_output(wlan_gpio_deep_sleep,
+				WLAN_RESET);
+		if (rc < 0) {
+			pr_err("WLAN reset GPIO %d set output direction failed",
+					wlan_gpio_deep_sleep);
+			goto fail_gpio_dir_out;
+		}
+
+		/* Configure TCXO to be slave to WLAN_CLK_PWR_REQ */
+		if (wlan_clock == NULL) {
+			wlan_clock = msm_xo_get(MSM_XO_TCXO_A0, id);
+			if (IS_ERR(wlan_clock)) {
+				pr_err("Failed to get TCXO_A0 voter (%ld)\n",
+						PTR_ERR(wlan_clock));
+				goto fail_gpio_dir_out;
+			}
+		}
+
+		rc = msm_xo_mode_vote(wlan_clock, MSM_XO_MODE_PIN_CTRL);
+		if (rc < 0) {
+			pr_err("Configuring TCXO to Pin controllable failed"
+					"(%d)\n", rc);
+			goto fail_xo_mode_vote;
+		}
+	} else if (!on && wlan_on) {
+		if (wlan_clock != NULL)
+			msm_xo_mode_vote(wlan_clock, MSM_XO_MODE_OFF);
+		gpio_set_value_cansleep(wlan_gpio_deep_sleep, WLAN_RESET);
+		gpio_free(wlan_gpio_deep_sleep);
+	}
+
+	/* WLAN VREG settings */
+	for (i = 0; i < ARRAY_SIZE(vregs_qwlan_name); i++) {
+		if (on && !wlan_on)	{
+			vregs_qwlan[i] = regulator_get(NULL,
+					vregs_qwlan_name[i]);
+			if (IS_ERR(vregs_qwlan[i])) {
+				pr_err("regulator get of %s failed (%ld)\n",
+						vregs_qwlan_name[i],
+						PTR_ERR(vregs_qwlan[i]));
+				rc = PTR_ERR(vregs_qwlan[i]);
+				goto vreg_get_fail;
+			}
+			if (vregs_qwlan_val_min[i] || vregs_qwlan_val_max[i]) {
+				rc = regulator_set_voltage(vregs_qwlan[i],
+						vregs_qwlan_val_min[i],
+						vregs_qwlan_val_max[i]);
+				if (rc) {
+					pr_err("regulator_set_voltage(%s) failed\n",
+							vregs_qwlan_name[i]);
+					goto vreg_fail;
+				}
+			}
+			/* vote for pin control (if needed) */
+			if (vregs_is_pin_controlled[i]) {
+				vregs_pc_qwlan[i] = regulator_get(NULL,
+							vregs_qwlan_pc_name[i]);
+				if (IS_ERR(vregs_pc_qwlan[i])) {
+					pr_err("regulator get of %s failed "
+						"(%ld)\n",
+						vregs_qwlan_pc_name[i],
+						PTR_ERR(vregs_pc_qwlan[i]));
+					rc = PTR_ERR(vregs_pc_qwlan[i]);
+					goto vreg_fail;
+				}
+			}
+
+			if (vregs_qwlan_peek_current[i]) {
+				rc = regulator_set_optimum_mode(vregs_qwlan[i],
+						vregs_qwlan_peek_current[i]);
+				if (rc < 0)
+					pr_err("vreg %s set optimum mode"
+						" failed to %d (%d)\n",
+						vregs_qwlan_name[i], rc,
+						 vregs_qwlan_peek_current[i]);
+			}
+			rc = regulator_enable(vregs_qwlan[i]);
+			if (rc < 0) {
+				pr_err("vreg %s enable failed (%d)\n",
+						vregs_qwlan_name[i], rc);
+				goto vreg_fail;
+			}
+			if (vregs_is_pin_controlled[i]) {
+				rc = regulator_enable(vregs_pc_qwlan[i]);
+				if (rc < 0) {
+					pr_err("vreg %s enable failed (%d)\n",
+						vregs_qwlan_pc_name[i], rc);
+					goto vreg_fail;
+				}
+			}
+		} else if (!on && wlan_on) {
+
+			if (vregs_qwlan_peek_current[i]) {
+				/* For legacy reasons we pass 1mA current to
+				 * put regulator in LPM mode.
+				 */
+				rc = regulator_set_optimum_mode(vregs_qwlan[i],
+									 1000);
+				if (rc < 0)
+					pr_info("vreg %s set optimum mode"
+								"failed (%d)\n",
+						vregs_qwlan_name[i], rc);
+				rc = regulator_set_voltage(vregs_qwlan[i], 0 ,
+							vregs_qwlan_val_max[i]);
+				if (rc)
+					pr_err("regulator_set_voltage(%s)"
+								"failed (%d)\n",
+						vregs_qwlan_name[i], rc);
+
+			}
+
+			if (vregs_is_pin_controlled[i]) {
+				rc = regulator_disable(vregs_pc_qwlan[i]);
+				if (rc < 0) {
+					pr_err("vreg %s disable failed (%d)\n",
+						vregs_qwlan_pc_name[i], rc);
+					goto vreg_fail;
+				}
+				regulator_put(vregs_pc_qwlan[i]);
+			}
+
+			rc = regulator_disable(vregs_qwlan[i]);
+			if (rc < 0) {
+				pr_err("vreg %s disable failed (%d)\n",
+						vregs_qwlan_name[i], rc);
+				goto vreg_fail;
+			}
+			regulator_put(vregs_qwlan[i]);
+		}
+	}
+	if (on) {
+		gpio_set_value_cansleep(wlan_gpio_deep_sleep, WLAN_RESET_OUT);
+		wlan_on = true;
+	}
+	else
+		wlan_on = false;
+	return 0;
+
+vreg_fail:
+	regulator_put(vregs_qwlan[i]);
+	if (vregs_is_pin_controlled[i])
+		regulator_put(vregs_pc_qwlan[i]);
+vreg_get_fail:
+	i--;
+	while (i >= 0) {
+		ret = !on ? regulator_enable(vregs_qwlan[i]) :
+			regulator_disable(vregs_qwlan[i]);
+		if (ret < 0) {
+			pr_err("vreg %s %s failed (%d) in err path\n",
+					vregs_qwlan_name[i],
+					!on ? "enable" : "disable", ret);
+		}
+		if (vregs_is_pin_controlled[i]) {
+			ret = !on ? regulator_enable(vregs_pc_qwlan[i]) :
+				regulator_disable(vregs_pc_qwlan[i]);
+			if (ret < 0) {
+				pr_err("vreg %s %s failed (%d) in err path\n",
+					vregs_qwlan_pc_name[i],
+					!on ? "enable" : "disable", ret);
+			}
+		}
+		regulator_put(vregs_qwlan[i]);
+		if (vregs_is_pin_controlled[i])
+			regulator_put(vregs_pc_qwlan[i]);
+		i--;
+	}
+	if (!on)
+		goto fail;
+fail_xo_mode_vote:
+	msm_xo_put(wlan_clock);
+fail_gpio_dir_out:
+	gpio_free(wlan_gpio_deep_sleep);
+fail:
+	return rc;
+}
+EXPORT_SYMBOL(vos_chip_power_qrf8615);
+
+/**
+ * qcomwlan_pmic_xo_core_force_enable() - Force XO Core of PMIC to be ALWAYS ON
+ * @on - Force XO Core  ON/OFF (1 or 0)
+ *
+ * The XO_CORE controls the XO feeding the TCXO buffers (A0, A1, etc.). WLAN
+ * wants to keep the XO core on even though our buffer A0 is in pin control
+ * because it can take a long time turn the XO back on and warm up the buffers.
+ * This helps in optimizing power in BMPS (power save) mode of WLAN.
+ * The WLAN driver wrapper function takes care that this API is not called
+ * consecutively.
+ *
+ * This function returns 0 on success or a non-zero value on failure.
+ */
+int qcomwlan_pmic_xo_core_force_enable(int on)
+{
+	static struct msm_xo_voter *wlan_ps;
+	int rc = 0;
+
+	if (wlan_ps == NULL) {
+		wlan_ps = msm_xo_get(MSM_XO_CORE, id);
+		if (IS_ERR(wlan_ps)) {
+			pr_err("Failed to get XO CORE voter (%ld)\n",
+					PTR_ERR(wlan_ps));
+			goto fail;
+		}
+	}
+
+	if (on)
+		rc = msm_xo_mode_vote(wlan_ps, MSM_XO_MODE_ON);
+	else
+		rc = msm_xo_mode_vote(wlan_ps, MSM_XO_MODE_OFF);
+
+	if (rc < 0) {
+		pr_err("XO Core %s failed (%d)\n",
+			on ? "enable" : "disable", rc);
+		goto fail_xo_mode_vote;
+	}
+	return 0;
+fail_xo_mode_vote:
+	msm_xo_put(wlan_ps);
+fail:
+	return rc;
+}
+EXPORT_SYMBOL(qcomwlan_pmic_xo_core_force_enable);
+
+
+/**
+ * qcomwlan_freq_change_1p3v_supply() - function to change the freq for 1.3V RF supply.
+ * @freq - freq of the 1.3V Supply
+ *
+ * This function returns 0 on success or a non-zero value on failure.
+ */
+
+int qcomwlan_freq_change_1p3v_supply(enum rpm_vreg_freq freq)
+{
+	return rpm_vreg_set_frequency(RPM_VREG_ID_PM8058_S2, freq);
+}
+EXPORT_SYMBOL(qcomwlan_freq_change_1p3v_supply);
diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/rf.c b/drivers/net/wireless/rtlwifi/rtl8192cu/rf.c
index 506b9a0..4763426 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192cu/rf.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192cu/rf.c
@@ -104,7 +104,7 @@
 			tx_agc[RF90_PATH_A] = 0x10101010;
 			tx_agc[RF90_PATH_B] = 0x10101010;
 		} else if (rtlpriv->dm.dynamic_txhighpower_lvl ==
-			   TXHIGHPWRLEVEL_LEVEL1) {
+			   TXHIGHPWRLEVEL_LEVEL2) {
 			tx_agc[RF90_PATH_A] = 0x00000000;
 			tx_agc[RF90_PATH_B] = 0x00000000;
 		} else{
diff --git a/drivers/net/wireless/wcnss/Makefile b/drivers/net/wireless/wcnss/Makefile
new file mode 100644
index 0000000..c077848
--- /dev/null
+++ b/drivers/net/wireless/wcnss/Makefile
@@ -0,0 +1,6 @@
+
+# Makefile for WCNSS triple-play driver
+
+wcnsscore-objs += wcnss_wlan.o wcnss_riva.o qcomwlan_secif.o
+
+obj-$(CONFIG_WCNSS_CORE) += wcnsscore.o
diff --git a/drivers/net/wireless/wcnss/qcomwlan_secif.c b/drivers/net/wireless/wcnss/qcomwlan_secif.c
new file mode 100644
index 0000000..e2be75c
--- /dev/null
+++ b/drivers/net/wireless/wcnss/qcomwlan_secif.c
@@ -0,0 +1,63 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/export.h>
+#include <linux/qcomwlan_secif.h>
+
+/*
+ * APIs for calling crypto routines from kernel
+ */
+struct crypto_ahash *wcnss_wlan_crypto_alloc_ahash(const char *alg_name,
+							 u32 type, u32 mask)
+{
+	return crypto_alloc_ahash(alg_name, type, mask);
+}
+EXPORT_SYMBOL(wcnss_wlan_crypto_alloc_ahash);
+
+int wcnss_wlan_crypto_ahash_digest(struct ahash_request *req)
+{
+	return crypto_ahash_digest(req);
+}
+EXPORT_SYMBOL(wcnss_wlan_crypto_ahash_digest);
+
+void wcnss_wlan_crypto_free_ahash(struct crypto_ahash *tfm)
+{
+	crypto_free_ahash(tfm);
+}
+EXPORT_SYMBOL(wcnss_wlan_crypto_free_ahash);
+
+int wcnss_wlan_crypto_ahash_setkey(struct crypto_ahash *tfm, const u8 *key,
+			unsigned int keylen)
+{
+	return crypto_ahash_setkey(tfm, key, keylen);
+}
+EXPORT_SYMBOL(wcnss_wlan_crypto_ahash_setkey);
+
+struct crypto_ablkcipher *
+wcnss_wlan_crypto_alloc_ablkcipher(const char *alg_name, u32 type, u32 mask)
+{
+	return crypto_alloc_ablkcipher(alg_name, type, mask);
+}
+EXPORT_SYMBOL(wcnss_wlan_crypto_alloc_ablkcipher);
+
+void wcnss_wlan_ablkcipher_request_free(struct ablkcipher_request *req)
+{
+	ablkcipher_request_free(req);
+}
+EXPORT_SYMBOL(wcnss_wlan_ablkcipher_request_free);
+
+void wcnss_wlan_crypto_free_ablkcipher(struct crypto_ablkcipher *tfm)
+{
+	crypto_free_ablkcipher(tfm);
+}
+EXPORT_SYMBOL(wcnss_wlan_crypto_free_ablkcipher);
+
diff --git a/drivers/net/wireless/wcnss/wcnss_riva.c b/drivers/net/wireless/wcnss/wcnss_riva.c
new file mode 100644
index 0000000..23365ff
--- /dev/null
+++ b/drivers/net/wireless/wcnss/wcnss_riva.c
@@ -0,0 +1,399 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mfd/pm8xxx/pm8921.h>
+#include <linux/mfd/pm8xxx/gpio.h>
+#include <linux/wcnss_wlan.h>
+#include <linux/semaphore.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <mach/msm_xo.h>
+#include <mach/msm_iomap.h>
+
+
+static void __iomem *msm_riva_base;
+static struct msm_xo_voter *wlan_clock;
+static const char *id = "WLAN";
+static LIST_HEAD(power_on_lock_list);
+static DEFINE_MUTEX(list_lock);
+static DEFINE_SEMAPHORE(riva_power_on_lock);
+
+#define MSM_RIVA_PHYS                     0x03204000
+#define RIVA_PMU_CFG                      (msm_riva_base + 0x28)
+#define RIVA_PMU_CFG_IRIS_XO_CFG          BIT(3)
+#define RIVA_PMU_CFG_IRIS_XO_EN           BIT(4)
+#define RIVA_PMU_CFG_GC_BUS_MUX_SEL_TOP   BIT(5)
+#define RIVA_PMU_CFG_IRIS_XO_CFG_STS      BIT(6) /* 1: in progress, 0: done */
+
+#define RIVA_PMU_CFG_IRIS_XO_MODE         0x6
+#define RIVA_PMU_CFG_IRIS_XO_MODE_48      (3 << 1)
+
+#define VREG_NULL_CONFIG            0x0000
+#define VREG_GET_REGULATOR_MASK     0x0001
+#define VREG_SET_VOLTAGE_MASK       0x0002
+#define VREG_OPTIMUM_MODE_MASK      0x0004
+#define VREG_ENABLE_MASK            0x0008
+
+struct vregs_info {
+	const char * const name;
+	int state;
+	const int nominal_min;
+	const int low_power_min;
+	const int max_voltage;
+	const int uA_load;
+	struct regulator *regulator;
+};
+
+static struct vregs_info iris_vregs[] = {
+	{"iris_vddxo",  VREG_NULL_CONFIG, 1800000, 0, 1800000, 10000,  NULL},
+	{"iris_vddrfa", VREG_NULL_CONFIG, 1300000, 0, 1300000, 100000, NULL},
+	{"iris_vddpa",  VREG_NULL_CONFIG, 2900000, 0, 3000000, 515000, NULL},
+	{"iris_vdddig", VREG_NULL_CONFIG, 1200000, 0, 1200000, 10000,  NULL},
+};
+
+static struct vregs_info riva_vregs[] = {
+	{"riva_vddmx",  VREG_NULL_CONFIG, 1050000, 0, 1150000, 0,      NULL},
+	{"riva_vddcx",  VREG_NULL_CONFIG, 1050000, 0, 1150000, 0,      NULL},
+	{"riva_vddpx",  VREG_NULL_CONFIG, 1800000, 0, 1800000, 0,      NULL},
+};
+
+struct host_driver {
+	char name[20];
+	struct list_head list;
+};
+
+
+static int configure_iris_xo(struct device *dev, bool use_48mhz_xo, int on)
+{
+	u32 reg = 0;
+	int rc = 0;
+	struct clk *cxo = clk_get(dev, "cxo");
+	if (IS_ERR(cxo)) {
+		pr_err("Couldn't get cxo clock\n");
+		return PTR_ERR(cxo);
+	}
+
+	if (on) {
+		msm_riva_base = ioremap(MSM_RIVA_PHYS, SZ_256);
+		if (!msm_riva_base) {
+			pr_err("ioremap MSM_RIVA_PHYS failed\n");
+			goto fail;
+		}
+
+		/* Enable IRIS XO */
+		rc = clk_prepare_enable(cxo);
+		if (rc) {
+			pr_err("cxo enable failed\n");
+			goto fail;
+		}
+		writel_relaxed(0, RIVA_PMU_CFG);
+		reg = readl_relaxed(RIVA_PMU_CFG);
+		reg |= RIVA_PMU_CFG_GC_BUS_MUX_SEL_TOP |
+				RIVA_PMU_CFG_IRIS_XO_EN;
+		writel_relaxed(reg, RIVA_PMU_CFG);
+
+		/* Clear XO_MODE[b2:b1] bits. Clear implies 19.2 MHz TCXO */
+		reg &= ~(RIVA_PMU_CFG_IRIS_XO_MODE);
+
+		if (use_48mhz_xo)
+			reg |= RIVA_PMU_CFG_IRIS_XO_MODE_48;
+
+		writel_relaxed(reg, RIVA_PMU_CFG);
+
+		/* Start IRIS XO configuration */
+		reg |= RIVA_PMU_CFG_IRIS_XO_CFG;
+		writel_relaxed(reg, RIVA_PMU_CFG);
+
+		/* Wait for XO configuration to finish */
+		while (readl_relaxed(RIVA_PMU_CFG) &
+						RIVA_PMU_CFG_IRIS_XO_CFG_STS)
+			cpu_relax();
+
+		/* Stop IRIS XO configuration */
+		reg &= ~(RIVA_PMU_CFG_GC_BUS_MUX_SEL_TOP |
+				RIVA_PMU_CFG_IRIS_XO_CFG);
+		writel_relaxed(reg, RIVA_PMU_CFG);
+		clk_disable_unprepare(cxo);
+
+		if (!use_48mhz_xo) {
+			wlan_clock = msm_xo_get(MSM_XO_TCXO_A2, id);
+			if (IS_ERR(wlan_clock)) {
+				rc = PTR_ERR(wlan_clock);
+				pr_err("Failed to get MSM_XO_TCXO_A2 voter"
+							" (%d)\n", rc);
+				goto fail;
+			}
+
+			rc = msm_xo_mode_vote(wlan_clock, MSM_XO_MODE_ON);
+			if (rc < 0) {
+				pr_err("Configuring MSM_XO_MODE_ON failed"
+							" (%d)\n", rc);
+				goto msm_xo_vote_fail;
+			}
+		}
+	}  else {
+		if (wlan_clock != NULL && !use_48mhz_xo) {
+			rc = msm_xo_mode_vote(wlan_clock, MSM_XO_MODE_OFF);
+			if (rc < 0)
+				pr_err("Configuring MSM_XO_MODE_OFF failed"
+							" (%d)\n", rc);
+		}
+	}
+
+	/* Add some delay for XO to settle */
+	msleep(20);
+
+	clk_put(cxo);
+	return rc;
+
+msm_xo_vote_fail:
+	msm_xo_put(wlan_clock);
+
+fail:
+	clk_put(cxo);
+	return rc;
+}
+
+/* Helper routine to turn off all WCNSS vregs e.g. IRIS, Riva */
+static void wcnss_vregs_off(struct vregs_info regulators[], uint size)
+{
+	int i, rc = 0;
+
+	/* Regulators need to be turned off in the reverse order */
+	for (i = (size-1); i >= 0; i--) {
+		if (regulators[i].state == VREG_NULL_CONFIG)
+			continue;
+
+		/* Remove PWM mode */
+		if (regulators[i].state & VREG_OPTIMUM_MODE_MASK) {
+			rc = regulator_set_optimum_mode(
+					regulators[i].regulator, 0);
+			if (rc < 0)
+				pr_err("regulator_set_optimum_mode(%s) failed (%d)\n",
+						regulators[i].name, rc);
+		}
+
+		/* Set voltage to lowest level */
+		if (regulators[i].state & VREG_SET_VOLTAGE_MASK) {
+			rc = regulator_set_voltage(regulators[i].regulator,
+					regulators[i].low_power_min,
+					regulators[i].max_voltage);
+			if (rc)
+				pr_err("regulator_set_voltage(%s) failed (%d)\n",
+						regulators[i].name, rc);
+		}
+
+		/* Disable regulator */
+		if (regulators[i].state & VREG_ENABLE_MASK) {
+			rc = regulator_disable(regulators[i].regulator);
+			if (rc < 0)
+				pr_err("vreg %s disable failed (%d)\n",
+						regulators[i].name, rc);
+		}
+
+		/* Free the regulator source */
+		if (regulators[i].state & VREG_GET_REGULATOR_MASK)
+			regulator_put(regulators[i].regulator);
+
+		regulators[i].state = VREG_NULL_CONFIG;
+	}
+}
+
+/* Common helper routine to turn on all WCNSS vregs e.g. IRIS, Riva */
+static int wcnss_vregs_on(struct device *dev,
+		struct vregs_info regulators[], uint size)
+{
+	int i, rc = 0, reg_cnt;
+
+	for (i = 0; i < size; i++) {
+			/* Get regulator source */
+		regulators[i].regulator =
+			regulator_get(dev, regulators[i].name);
+		if (IS_ERR(regulators[i].regulator)) {
+			rc = PTR_ERR(regulators[i].regulator);
+				pr_err("regulator get of %s failed (%d)\n",
+					regulators[i].name, rc);
+				goto fail;
+		}
+		regulators[i].state |= VREG_GET_REGULATOR_MASK;
+		reg_cnt = regulator_count_voltages(regulators[i].regulator);
+		/* Set voltage to nominal. Exclude swtiches e.g. LVS */
+		if ((regulators[i].nominal_min || regulators[i].max_voltage)
+				&& (reg_cnt > 0)) {
+			rc = regulator_set_voltage(regulators[i].regulator,
+					regulators[i].nominal_min,
+					regulators[i].max_voltage);
+			if (rc) {
+				pr_err("regulator_set_voltage(%s) failed (%d)\n",
+						regulators[i].name, rc);
+				goto fail;
+			}
+			regulators[i].state |= VREG_SET_VOLTAGE_MASK;
+		}
+
+		/* Vote for PWM/PFM mode if needed */
+		if (regulators[i].uA_load && (reg_cnt > 0)) {
+			rc = regulator_set_optimum_mode(regulators[i].regulator,
+					regulators[i].uA_load);
+			if (rc < 0) {
+				pr_err("regulator_set_optimum_mode(%s) failed (%d)\n",
+						regulators[i].name, rc);
+				goto fail;
+			}
+			regulators[i].state |= VREG_OPTIMUM_MODE_MASK;
+		}
+
+		/* Enable the regulator */
+		rc = regulator_enable(regulators[i].regulator);
+		if (rc) {
+			pr_err("vreg %s enable failed (%d)\n",
+				regulators[i].name, rc);
+			goto fail;
+		}
+		regulators[i].state |= VREG_ENABLE_MASK;
+	}
+
+	return rc;
+
+fail:
+	wcnss_vregs_off(regulators, size);
+	return rc;
+
+}
+
+static void wcnss_iris_vregs_off(void)
+{
+	wcnss_vregs_off(iris_vregs, ARRAY_SIZE(iris_vregs));
+}
+
+static int wcnss_iris_vregs_on(struct device *dev)
+{
+	return wcnss_vregs_on(dev, iris_vregs, ARRAY_SIZE(iris_vregs));
+}
+
+static void wcnss_riva_vregs_off(void)
+{
+	wcnss_vregs_off(riva_vregs, ARRAY_SIZE(riva_vregs));
+}
+
+static int wcnss_riva_vregs_on(struct device *dev)
+{
+	return wcnss_vregs_on(dev, riva_vregs, ARRAY_SIZE(riva_vregs));
+}
+
+int wcnss_wlan_power(struct device *dev,
+		struct wcnss_wlan_config *cfg,
+		enum wcnss_opcode on)
+{
+	int rc = 0;
+
+	if (on) {
+		down(&riva_power_on_lock);
+		/* RIVA regulator settings */
+		rc = wcnss_riva_vregs_on(dev);
+		if (rc)
+			goto fail_riva_on;
+
+		/* IRIS regulator settings */
+		rc = wcnss_iris_vregs_on(dev);
+		if (rc)
+			goto fail_iris_on;
+
+		/* Configure IRIS XO */
+		rc = configure_iris_xo(dev, cfg->use_48mhz_xo,
+				WCNSS_WLAN_SWITCH_ON);
+		if (rc)
+			goto fail_iris_xo;
+		up(&riva_power_on_lock);
+
+	} else {
+		configure_iris_xo(dev, cfg->use_48mhz_xo,
+				WCNSS_WLAN_SWITCH_OFF);
+		wcnss_iris_vregs_off();
+		wcnss_riva_vregs_off();
+	}
+
+	return rc;
+
+fail_iris_xo:
+	wcnss_iris_vregs_off();
+
+fail_iris_on:
+	wcnss_riva_vregs_off();
+
+fail_riva_on:
+	up(&riva_power_on_lock);
+	return rc;
+}
+EXPORT_SYMBOL(wcnss_wlan_power);
+
+/*
+ * During SSR Riva should not be 'powered on' until all the host drivers
+ * finish their shutdown routines.  Host drivers use below APIs to
+ * synchronize power-on. Riva will not be 'powered on' until all the
+ * requests(to lock power-on) are freed.
+ */
+int req_riva_power_on_lock(char *driver_name)
+{
+	struct host_driver *node;
+
+	if (!driver_name)
+		goto err;
+
+	node = kmalloc(sizeof(struct host_driver), GFP_KERNEL);
+	if (!node)
+		goto err;
+	strncpy(node->name, driver_name, sizeof(node->name));
+
+	mutex_lock(&list_lock);
+	/* Lock when the first request is added */
+	if (list_empty(&power_on_lock_list))
+		down(&riva_power_on_lock);
+	list_add(&node->list, &power_on_lock_list);
+	mutex_unlock(&list_lock);
+
+	return 0;
+
+err:
+	return -EINVAL;
+}
+EXPORT_SYMBOL(req_riva_power_on_lock);
+
+int free_riva_power_on_lock(char *driver_name)
+{
+	int ret = -1;
+	struct host_driver *node;
+
+	mutex_lock(&list_lock);
+	list_for_each_entry(node, &power_on_lock_list, list) {
+		if (!strncmp(node->name, driver_name, sizeof(node->name))) {
+			list_del(&node->list);
+			kfree(node);
+			ret = 0;
+			break;
+		}
+	}
+	/* unlock when the last host driver frees the lock */
+	if (list_empty(&power_on_lock_list))
+		up(&riva_power_on_lock);
+	mutex_unlock(&list_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(free_riva_power_on_lock);
diff --git a/drivers/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c
new file mode 100644
index 0000000..ad9dc7d
--- /dev/null
+++ b/drivers/net/wireless/wcnss/wcnss_wlan.c
@@ -0,0 +1,532 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/wcnss_wlan.h>
+#include <linux/platform_data/qcom_wcnss_device.h>
+#include <linux/workqueue.h>
+#include <linux/jiffies.h>
+#include <linux/gpio.h>
+#include <mach/peripheral-loader.h>
+
+#define DEVICE "wcnss_wlan"
+#define VERSION "1.01"
+#define WCNSS_PIL_DEVICE "wcnss"
+
+/* module params */
+#define WCNSS_CONFIG_UNSPECIFIED (-1)
+static int has_48mhz_xo = WCNSS_CONFIG_UNSPECIFIED;
+module_param(has_48mhz_xo, int, S_IWUSR | S_IRUGO);
+MODULE_PARM_DESC(has_48mhz_xo, "Is an external 48 MHz XO present");
+
+static struct {
+	struct platform_device *pdev;
+	void		*pil;
+	struct resource	*mmio_res;
+	struct resource	*tx_irq_res;
+	struct resource	*rx_irq_res;
+	struct resource	*gpios_5wire;
+	const struct dev_pm_ops *pm_ops;
+	int		triggered;
+	int		smd_channel_ready;
+	unsigned int	serial_number;
+	int		thermal_mitigation;
+	void		(*tm_notify)(struct device *, int);
+	struct wcnss_wlan_config wlan_config;
+	struct delayed_work wcnss_work;
+} *penv = NULL;
+
+static ssize_t wcnss_serial_number_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	if (!penv)
+		return -ENODEV;
+
+	return scnprintf(buf, PAGE_SIZE, "%08X\n", penv->serial_number);
+}
+
+static ssize_t wcnss_serial_number_store(struct device *dev,
+		struct device_attribute *attr, const char * buf, size_t count)
+{
+	unsigned int value;
+
+	if (!penv)
+		return -ENODEV;
+
+	if (sscanf(buf, "%08X", &value) != 1)
+		return -EINVAL;
+
+	penv->serial_number = value;
+	return count;
+}
+
+static DEVICE_ATTR(serial_number, S_IRUSR | S_IWUSR,
+	wcnss_serial_number_show, wcnss_serial_number_store);
+
+
+static ssize_t wcnss_thermal_mitigation_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	if (!penv)
+		return -ENODEV;
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", penv->thermal_mitigation);
+}
+
+static ssize_t wcnss_thermal_mitigation_store(struct device *dev,
+		struct device_attribute *attr, const char * buf, size_t count)
+{
+	int value;
+
+	if (!penv)
+		return -ENODEV;
+
+	if (sscanf(buf, "%d", &value) != 1)
+		return -EINVAL;
+	penv->thermal_mitigation = value;
+	if (penv->tm_notify)
+		(penv->tm_notify)(dev, value);
+	return count;
+}
+
+static DEVICE_ATTR(thermal_mitigation, S_IRUSR | S_IWUSR,
+	wcnss_thermal_mitigation_show, wcnss_thermal_mitigation_store);
+
+static int wcnss_create_sysfs(struct device *dev)
+{
+	int ret;
+
+	if (!dev)
+		return -ENODEV;
+
+	ret = device_create_file(dev, &dev_attr_serial_number);
+	if (ret)
+		return ret;
+
+	ret = device_create_file(dev, &dev_attr_thermal_mitigation);
+	if (ret) {
+		device_remove_file(dev, &dev_attr_serial_number);
+		return ret;
+	}
+	return 0;
+}
+
+static void wcnss_remove_sysfs(struct device *dev)
+{
+	if (dev) {
+		device_remove_file(dev, &dev_attr_serial_number);
+		device_remove_file(dev, &dev_attr_thermal_mitigation);
+	}
+}
+
+static void wcnss_post_bootup(struct work_struct *work)
+{
+	pr_info("%s: Cancel APPS vote for Iris & Riva\n", __func__);
+
+	/* Since Riva is up, cancel any APPS vote for Iris & Riva VREGs  */
+	wcnss_wlan_power(&penv->pdev->dev, &penv->wlan_config,
+		WCNSS_WLAN_SWITCH_OFF);
+}
+
+static int
+wcnss_gpios_config(struct resource *gpios_5wire, bool enable)
+{
+	int i, j;
+	int rc = 0;
+
+	for (i = gpios_5wire->start; i <= gpios_5wire->end; i++) {
+		if (enable) {
+			rc = gpio_request(i, gpios_5wire->name);
+			if (rc) {
+				pr_err("WCNSS gpio_request %d err %d\n", i, rc);
+				goto fail;
+			}
+		} else
+			gpio_free(i);
+	}
+
+	return rc;
+
+fail:
+	for (j = i-1; j >= gpios_5wire->start; j--)
+		gpio_free(j);
+	return rc;
+}
+
+static int __devinit
+wcnss_wlan_ctrl_probe(struct platform_device *pdev)
+{
+	if (!penv)
+		return -ENODEV;
+
+	penv->smd_channel_ready = 1;
+
+	pr_info("%s: SMD ctrl channel up\n", __func__);
+
+	/* Schedule a work to do any post boot up activity */
+	INIT_DELAYED_WORK(&penv->wcnss_work, wcnss_post_bootup);
+	schedule_delayed_work(&penv->wcnss_work, msecs_to_jiffies(10000));
+
+	return 0;
+}
+
+static int __devexit
+wcnss_wlan_ctrl_remove(struct platform_device *pdev)
+{
+	if (penv)
+		penv->smd_channel_ready = 0;
+
+	pr_info("%s: SMD ctrl channel down\n", __func__);
+
+	return 0;
+}
+
+
+static struct platform_driver wcnss_wlan_ctrl_driver = {
+	.driver = {
+		.name	= "WLAN_CTRL",
+		.owner	= THIS_MODULE,
+	},
+	.probe	= wcnss_wlan_ctrl_probe,
+	.remove	= __devexit_p(wcnss_wlan_ctrl_remove),
+};
+
+struct device *wcnss_wlan_get_device(void)
+{
+	if (penv && penv->pdev && penv->smd_channel_ready)
+		return &penv->pdev->dev;
+	return NULL;
+}
+EXPORT_SYMBOL(wcnss_wlan_get_device);
+
+struct platform_device *wcnss_get_platform_device(void)
+{
+	if (penv && penv->pdev)
+		return penv->pdev;
+	return NULL;
+}
+EXPORT_SYMBOL(wcnss_get_platform_device);
+
+struct wcnss_wlan_config *wcnss_get_wlan_config(void)
+{
+	if (penv && penv->pdev)
+		return &penv->wlan_config;
+	return NULL;
+}
+EXPORT_SYMBOL(wcnss_get_wlan_config);
+
+struct resource *wcnss_wlan_get_memory_map(struct device *dev)
+{
+	if (penv && dev && (dev == &penv->pdev->dev) && penv->smd_channel_ready)
+		return penv->mmio_res;
+	return NULL;
+}
+EXPORT_SYMBOL(wcnss_wlan_get_memory_map);
+
+int wcnss_wlan_get_dxe_tx_irq(struct device *dev)
+{
+	if (penv && dev && (dev == &penv->pdev->dev) &&
+				penv->tx_irq_res && penv->smd_channel_ready)
+		return penv->tx_irq_res->start;
+	return WCNSS_WLAN_IRQ_INVALID;
+}
+EXPORT_SYMBOL(wcnss_wlan_get_dxe_tx_irq);
+
+int wcnss_wlan_get_dxe_rx_irq(struct device *dev)
+{
+	if (penv && dev && (dev == &penv->pdev->dev) &&
+				penv->rx_irq_res && penv->smd_channel_ready)
+		return penv->rx_irq_res->start;
+	return WCNSS_WLAN_IRQ_INVALID;
+}
+EXPORT_SYMBOL(wcnss_wlan_get_dxe_rx_irq);
+
+void wcnss_wlan_register_pm_ops(struct device *dev,
+				const struct dev_pm_ops *pm_ops)
+{
+	if (penv && dev && (dev == &penv->pdev->dev) && pm_ops)
+		penv->pm_ops = pm_ops;
+}
+EXPORT_SYMBOL(wcnss_wlan_register_pm_ops);
+
+void wcnss_wlan_unregister_pm_ops(struct device *dev,
+				const struct dev_pm_ops *pm_ops)
+{
+	if (penv && dev && (dev == &penv->pdev->dev) && pm_ops) {
+		if (pm_ops->suspend != penv->pm_ops->suspend ||
+				pm_ops->resume != penv->pm_ops->resume)
+			pr_err("PM APIs dont match with registered APIs\n");
+		penv->pm_ops = NULL;
+	}
+}
+EXPORT_SYMBOL(wcnss_wlan_unregister_pm_ops);
+
+void wcnss_register_thermal_mitigation(struct device *dev,
+				void (*tm_notify)(struct device *, int))
+{
+	if (penv && dev && tm_notify)
+		penv->tm_notify = tm_notify;
+}
+EXPORT_SYMBOL(wcnss_register_thermal_mitigation);
+
+void wcnss_unregister_thermal_mitigation(
+				void (*tm_notify)(struct device *, int))
+{
+	if (penv && tm_notify) {
+		if (tm_notify != penv->tm_notify)
+			pr_err("tm_notify doesn't match registered\n");
+		penv->tm_notify = NULL;
+	}
+}
+EXPORT_SYMBOL(wcnss_unregister_thermal_mitigation);
+
+unsigned int wcnss_get_serial_number(void)
+{
+	if (penv)
+		return penv->serial_number;
+	return 0;
+}
+EXPORT_SYMBOL(wcnss_get_serial_number);
+
+static int wcnss_wlan_suspend(struct device *dev)
+{
+	if (penv && dev && (dev == &penv->pdev->dev) &&
+	    penv->smd_channel_ready &&
+	    penv->pm_ops && penv->pm_ops->suspend)
+		return penv->pm_ops->suspend(dev);
+	return 0;
+}
+
+static int wcnss_wlan_resume(struct device *dev)
+{
+	if (penv && dev && (dev == &penv->pdev->dev) &&
+	    penv->smd_channel_ready &&
+	    penv->pm_ops && penv->pm_ops->resume)
+		return penv->pm_ops->resume(dev);
+	return 0;
+}
+
+static int
+wcnss_trigger_config(struct platform_device *pdev)
+{
+	int ret;
+	struct qcom_wcnss_opts *pdata;
+
+	/* make sure we are only triggered once */
+	if (penv->triggered)
+		return 0;
+	penv->triggered = 1;
+
+	/* initialize the WCNSS device configuration */
+	pdata = pdev->dev.platform_data;
+	if (WCNSS_CONFIG_UNSPECIFIED == has_48mhz_xo)
+		has_48mhz_xo = pdata->has_48mhz_xo;
+	penv->wlan_config.use_48mhz_xo = has_48mhz_xo;
+
+	penv->thermal_mitigation = 0;
+
+	penv->gpios_5wire = platform_get_resource_byname(pdev, IORESOURCE_IO,
+							"wcnss_gpios_5wire");
+
+	/* allocate 5-wire GPIO resources */
+	if (!penv->gpios_5wire) {
+		dev_err(&pdev->dev, "insufficient IO resources\n");
+		ret = -ENOENT;
+		goto fail_gpio_res;
+	}
+
+	/* Configure 5 wire GPIOs */
+	ret = wcnss_gpios_config(penv->gpios_5wire, true);
+	if (ret) {
+		dev_err(&pdev->dev, "WCNSS gpios config failed.\n");
+		goto fail_gpio_res;
+	}
+
+	/* power up the WCNSS */
+	ret = wcnss_wlan_power(&pdev->dev, &penv->wlan_config,
+					WCNSS_WLAN_SWITCH_ON);
+	if (ret) {
+		dev_err(&pdev->dev, "WCNSS Power-up failed.\n");
+		goto fail_power;
+	}
+
+	/* trigger initialization of the WCNSS */
+	penv->pil = pil_get(WCNSS_PIL_DEVICE);
+	if (IS_ERR(penv->pil)) {
+		dev_err(&pdev->dev, "Peripheral Loader failed on WCNSS.\n");
+		ret = PTR_ERR(penv->pil);
+		penv->pil = NULL;
+		goto fail_pil;
+	}
+
+	/* allocate resources */
+	penv->mmio_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+							"wcnss_mmio");
+	penv->tx_irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+							"wcnss_wlantx_irq");
+	penv->rx_irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+							"wcnss_wlanrx_irq");
+
+	if (!(penv->mmio_res && penv->tx_irq_res && penv->rx_irq_res)) {
+		dev_err(&pdev->dev, "insufficient resources\n");
+		ret = -ENOENT;
+		goto fail_res;
+	}
+
+	/* register sysfs entries */
+	ret = wcnss_create_sysfs(&pdev->dev);
+	if (ret)
+		goto fail_sysfs;
+
+	return 0;
+
+fail_sysfs:
+fail_res:
+	if (penv->pil)
+		pil_put(penv->pil);
+fail_pil:
+	wcnss_wlan_power(&pdev->dev, &penv->wlan_config,
+				WCNSS_WLAN_SWITCH_OFF);
+fail_power:
+	wcnss_gpios_config(penv->gpios_5wire, false);
+fail_gpio_res:
+	kfree(penv);
+	penv = NULL;
+	return ret;
+}
+
+#ifndef MODULE
+static int wcnss_node_open(struct inode *inode, struct file *file)
+{
+	struct platform_device *pdev;
+
+	pr_info(DEVICE " triggered by userspace\n");
+
+	pdev = penv->pdev;
+	return wcnss_trigger_config(pdev);
+}
+
+static const struct file_operations wcnss_node_fops = {
+	.owner = THIS_MODULE,
+	.open = wcnss_node_open,
+};
+
+static struct miscdevice wcnss_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = DEVICE,
+	.fops = &wcnss_node_fops,
+};
+#endif /* ifndef MODULE */
+
+
+static int __devinit
+wcnss_wlan_probe(struct platform_device *pdev)
+{
+	/* verify we haven't been called more than once */
+	if (penv) {
+		dev_err(&pdev->dev, "cannot handle multiple devices.\n");
+		return -ENODEV;
+	}
+
+	/* create an environment to track the device */
+	penv = kzalloc(sizeof(*penv), GFP_KERNEL);
+	if (!penv) {
+		dev_err(&pdev->dev, "cannot allocate device memory.\n");
+		return -ENOMEM;
+	}
+	penv->pdev = pdev;
+
+#ifdef MODULE
+
+	/*
+	 * Since we were built as a module, we are running because
+	 * the module was loaded, therefore we assume userspace
+	 * applications are available to service PIL, so we can
+	 * trigger the WCNSS configuration now
+	 */
+	pr_info(DEVICE " probed in MODULE mode\n");
+	return wcnss_trigger_config(pdev);
+
+#else
+
+	/*
+	 * Since we were built into the kernel we'll be called as part
+	 * of kernel initialization.  We don't know if userspace
+	 * applications are available to service PIL at this time
+	 * (they probably are not), so we simply create a device node
+	 * here.  When userspace is available it should touch the
+	 * device so that we know that WCNSS configuration can take
+	 * place
+	 */
+	pr_info(DEVICE " probed in built-in mode\n");
+	return misc_register(&wcnss_misc);
+
+#endif
+}
+
+static int __devexit
+wcnss_wlan_remove(struct platform_device *pdev)
+{
+	wcnss_remove_sysfs(&pdev->dev);
+	return 0;
+}
+
+
+static const struct dev_pm_ops wcnss_wlan_pm_ops = {
+	.suspend	= wcnss_wlan_suspend,
+	.resume		= wcnss_wlan_resume,
+};
+
+static struct platform_driver wcnss_wlan_driver = {
+	.driver = {
+		.name	= DEVICE,
+		.owner	= THIS_MODULE,
+		.pm	= &wcnss_wlan_pm_ops,
+	},
+	.probe	= wcnss_wlan_probe,
+	.remove	= __devexit_p(wcnss_wlan_remove),
+};
+
+static int __init wcnss_wlan_init(void)
+{
+	platform_driver_register(&wcnss_wlan_driver);
+	platform_driver_register(&wcnss_wlan_ctrl_driver);
+
+	return 0;
+}
+
+static void __exit wcnss_wlan_exit(void)
+{
+	if (penv) {
+		if (penv->pil)
+			pil_put(penv->pil);
+
+
+		kfree(penv);
+		penv = NULL;
+	}
+
+	platform_driver_unregister(&wcnss_wlan_ctrl_driver);
+	platform_driver_unregister(&wcnss_wlan_driver);
+}
+
+module_init(wcnss_wlan_init);
+module_exit(wcnss_wlan_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(VERSION);
+MODULE_DESCRIPTION(DEVICE "Driver");
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index 8e84ce9..fe151b5 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -91,8 +91,20 @@
 	help
 	  OpenFirmware PCI IRQ routing helpers
 
+config OF_SPMI
+	def_tristate SPMI
+	depends on SPMI
+	help
+	  OpenFirmware SPMI bus accessors
+
 config OF_MTD
 	depends on MTD
 	def_bool y
 
+config OF_SLIMBUS
+	def_tristate SLIMBUS
+	depends on SLIMBUS
+	help
+	  OpenFirmware SLIMBUS accessors
+
 endmenu # OF
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index aa90e60..c3a31c8 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -12,4 +12,6 @@
 obj-$(CONFIG_OF_MDIO)	+= of_mdio.o
 obj-$(CONFIG_OF_PCI)	+= of_pci.o
 obj-$(CONFIG_OF_PCI_IRQ)  += of_pci_irq.o
+obj-$(CONFIG_OF_SPMI)	+= of_spmi.o
 obj-$(CONFIG_OF_MTD)	+= of_mtd.o
+obj-$(CONFIG_OF_SLIMBUS)	+= of_slimbus.o
diff --git a/drivers/of/of_slimbus.c b/drivers/of/of_slimbus.c
new file mode 100644
index 0000000..512ca73
--- /dev/null
+++ b/drivers/of/of_slimbus.c
@@ -0,0 +1,83 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/* OF helpers for SLIMbus */
+#include <linux/slimbus/slimbus.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_slimbus.h>
+
+int of_register_slim_devices(struct slim_controller *ctrl)
+{
+	struct device_node *node;
+	struct slim_boardinfo *binfo = NULL;
+	int n = 0;
+	int ret = 0;
+
+	if (!ctrl->dev.of_node)
+		return -EINVAL;
+
+	for_each_child_of_node(ctrl->dev.of_node, node) {
+		struct property *prop;
+		struct slim_device *slim;
+		char *name;
+		prop = of_find_property(node, "elemental-addr", NULL);
+		if (!prop || prop->length != 6) {
+			dev_err(&ctrl->dev, "of_slim: invalid E-addr");
+			continue;
+		}
+		name = kzalloc(SLIMBUS_NAME_SIZE, GFP_KERNEL);
+		if (!name) {
+			dev_err(&ctrl->dev, "of_slim: out of memory");
+			ret = -ENOMEM;
+			goto of_slim_err;
+		}
+		if (of_modalias_node(node, name, SLIMBUS_NAME_SIZE) < 0) {
+			dev_err(&ctrl->dev, "of_slim: modalias failure on %s\n",
+				node->full_name);
+			kfree(name);
+			continue;
+		}
+		slim = kzalloc(sizeof(struct slim_device), GFP_KERNEL);
+		if (!slim) {
+			dev_err(&ctrl->dev, "of_slim: out of memory");
+			ret = -ENOMEM;
+			kfree(name);
+			goto of_slim_err;
+		}
+		memcpy(slim->e_addr, prop->value, 6);
+
+		binfo = krealloc(binfo, (n + 1) * sizeof(struct slim_boardinfo),
+					GFP_KERNEL);
+		if (!binfo) {
+			dev_err(&ctrl->dev, "out of memory");
+			kfree(name);
+			kfree(slim);
+			return -ENOMEM;
+		}
+		slim->name = (const char *)name;
+		binfo[n].bus_num = ctrl->nr;
+		binfo[n].slim_slave = slim;
+		n++;
+	}
+	return slim_register_board_info(binfo, n);
+of_slim_err:
+	n--;
+	while (n >= 0) {
+		kfree(binfo[n].slim_slave->name);
+		kfree(binfo[n].slim_slave);
+	}
+	kfree(binfo);
+	return ret;
+}
diff --git a/drivers/of/of_spmi.c b/drivers/of/of_spmi.c
new file mode 100644
index 0000000..61085c9
--- /dev/null
+++ b/drivers/of/of_spmi.c
@@ -0,0 +1,407 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/spmi.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_spmi.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/types.h>
+
+struct of_spmi_dev_info {
+	struct spmi_controller *ctrl;
+	struct spmi_boardinfo b_info;
+};
+
+struct of_spmi_res_info {
+	struct device_node *node;
+	uint32_t num_reg;
+	uint32_t num_irq;
+};
+
+/*
+ * Initialize r_info structure for safe usage
+ */
+static inline void of_spmi_init_resource(struct of_spmi_res_info *r_info,
+					 struct device_node *node)
+{
+	r_info->node = node;
+	r_info->num_reg = 0;
+	r_info->num_irq = 0;
+}
+
+/*
+ * Allocate dev_node array for spmi_device
+ */
+static inline int of_spmi_alloc_device_store(struct of_spmi_dev_info *d_info,
+					     uint32_t num_dev_node)
+{
+	d_info->b_info.num_dev_node = num_dev_node;
+	d_info->b_info.dev_node = kzalloc(sizeof(struct spmi_resource) *
+						num_dev_node, GFP_KERNEL);
+	if (!d_info->b_info.dev_node)
+		return -ENOMEM;
+
+	return 0;
+}
+
+/*
+ * Calculate the number of resources to allocate
+ *
+ * The caller is responsible for initializing the of_spmi_res_info structure.
+ */
+static void of_spmi_sum_node_resources(struct of_spmi_res_info *r_info,
+				       bool has_reg)
+{
+	struct of_irq oirq;
+	uint64_t size;
+	uint32_t flags;
+	int i = 0;
+
+	while (of_irq_map_one(r_info->node, i, &oirq) == 0)
+		i++;
+
+	r_info->num_irq += i;
+
+	if (!has_reg)
+		return;
+
+	/*
+	 * We can't use of_address_to_resource here since it includes
+	 * address translation; and address translation assumes that no
+	 * parent buses have a size-cell of 0. But SPMI does have a
+	 * size-cell of 0.
+	 */
+	i = 0;
+	while (of_get_address(r_info->node, i, &size, &flags) != NULL)
+		i++;
+
+	r_info->num_reg += i;
+}
+
+/*
+ * free spmi_resource for the spmi_device
+ */
+static void of_spmi_free_device_resources(struct of_spmi_dev_info *d_info)
+{
+	int i;
+
+	for (i = 0; i < d_info->b_info.num_dev_node; i++)
+		kfree(d_info->b_info.dev_node[i].resource);
+
+	kfree(d_info->b_info.dev_node);
+}
+
+/*
+ * Gather node resources and populate
+ */
+static void of_spmi_populate_node_resources(struct of_spmi_dev_info *d_info,
+					    struct of_spmi_res_info *r_info,
+					    int idx)
+
+{
+	uint32_t num_irq = r_info->num_irq, num_reg = r_info->num_reg;
+	int i;
+	struct resource *res;
+	const  __be32 *addrp;
+	uint64_t size;
+	uint32_t flags;
+
+	res = d_info->b_info.dev_node[idx].resource;
+	d_info->b_info.dev_node[idx].of_node = r_info->node;
+
+	if ((num_irq || num_reg) && (res != NULL)) {
+		for (i = 0; i < num_reg; i++, res++) {
+			/* Addresses are always 16 bits */
+			addrp = of_get_address(r_info->node, i, &size, &flags);
+			BUG_ON(!addrp);
+			res->start = be32_to_cpup(addrp);
+			res->end = res->start + size - 1;
+			res->flags = flags;
+		}
+		WARN_ON(of_irq_to_resource_table(r_info->node, res, num_irq) !=
+								num_irq);
+	}
+}
+
+/*
+ * Allocate enough memory to handle the resources associated with the
+ * device_node. The number of device nodes included in this allocation
+ * depends on whether the spmi-dev-container flag is specified or not.
+ */
+static int of_spmi_allocate_node_resources(struct of_spmi_dev_info *d_info,
+					   struct of_spmi_res_info *r_info,
+					   uint32_t idx)
+{
+	uint32_t num_irq = r_info->num_irq, num_reg = r_info->num_reg;
+	struct resource *res = NULL;
+
+	if (num_irq || num_reg) {
+		res = kzalloc(sizeof(*res) * (num_irq + num_reg), GFP_KERNEL);
+		if (!res)
+			return -ENOMEM;
+	}
+	d_info->b_info.dev_node[idx].num_resources = num_reg + num_irq;
+	d_info->b_info.dev_node[idx].resource = res;
+
+	return 0;
+}
+
+/*
+ * create a single spmi_device
+ */
+static int of_spmi_create_device(struct of_spmi_dev_info *d_info,
+				 struct device_node *node)
+{
+	struct spmi_controller *ctrl = d_info->ctrl;
+	struct spmi_boardinfo *b_info = &d_info->b_info;
+	void *result;
+	int rc;
+
+	rc = of_modalias_node(node, b_info->name, sizeof(b_info->name));
+	if (rc < 0) {
+		dev_err(&ctrl->dev, "of_spmi modalias failure on %s\n",
+				node->full_name);
+		return rc;
+	}
+
+	b_info->of_node = of_node_get(node);
+	result = spmi_new_device(ctrl, b_info);
+
+	if (result == NULL) {
+		dev_err(&ctrl->dev, "of_spmi: Failure registering %s\n",
+				node->full_name);
+		of_node_put(node);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+/*
+ * Walks all children of a node containing the spmi-dev-container
+ * binding. This special type of spmi_device can include resources
+ * from more than one device node.
+ */
+static void of_spmi_walk_dev_container(struct of_spmi_dev_info *d_info,
+					struct device_node *container)
+{
+	struct of_spmi_res_info r_info = {};
+	struct spmi_controller *ctrl = d_info->ctrl;
+	struct device_node *node;
+	int rc, i, num_dev_node = 0;
+
+	if (!of_device_is_available(container))
+		return;
+
+	/*
+	 * Count the total number of device_nodes so we know how much
+	 * device_store to allocate.
+	 */
+	for_each_child_of_node(container, node) {
+		if (!of_device_is_available(node))
+			continue;
+		num_dev_node++;
+	}
+
+	rc = of_spmi_alloc_device_store(d_info, num_dev_node);
+	if (rc) {
+		dev_err(&ctrl->dev, "%s: unable to allocate"
+				" device resources\n", __func__);
+		return;
+	}
+
+	i = 0;
+	for_each_child_of_node(container, node) {
+		if (!of_device_is_available(node))
+			continue;
+		of_spmi_init_resource(&r_info, node);
+		of_spmi_sum_node_resources(&r_info, 1);
+		rc = of_spmi_allocate_node_resources(d_info, &r_info, i);
+		if (rc) {
+			dev_err(&ctrl->dev, "%s: unable to allocate"
+					" resources\n", __func__);
+			of_spmi_free_device_resources(d_info);
+			return;
+		}
+		of_spmi_populate_node_resources(d_info, &r_info, i);
+		i++;
+	}
+
+	rc = of_spmi_create_device(d_info, container);
+	if (rc) {
+		dev_err(&ctrl->dev, "%s: unable to create device for"
+				" node %s\n", __func__, container->full_name);
+		of_spmi_free_device_resources(d_info);
+		return;
+	}
+}
+
+/*
+ * Walks all children of a node containing the spmi-slave-container
+ * binding. This indicates that all spmi_devices created from this
+ * point all share the same slave_id.
+ */
+static void of_spmi_walk_slave_container(struct of_spmi_dev_info *d_info,
+					struct device_node *container)
+{
+	struct spmi_controller *ctrl = d_info->ctrl;
+	struct device_node *node;
+	int rc;
+
+	for_each_child_of_node(container, node) {
+		struct of_spmi_res_info r_info;
+
+		if (!of_device_is_available(node))
+			continue;
+
+		/**
+		 * Check to see if this node contains children which
+		 * should be all created as the same spmi_device.
+		 */
+		if (of_get_property(node, "spmi-dev-container", NULL)) {
+			of_spmi_walk_dev_container(d_info, node);
+			continue;
+		}
+
+		rc = of_spmi_alloc_device_store(d_info, 1);
+		if (rc) {
+			dev_err(&ctrl->dev, "%s: unable to allocate"
+					" device resources\n", __func__);
+			goto slave_err;
+		}
+
+		of_spmi_init_resource(&r_info, node);
+		of_spmi_sum_node_resources(&r_info, 1);
+
+		rc = of_spmi_allocate_node_resources(d_info, &r_info, 0);
+		if (rc) {
+			dev_err(&ctrl->dev, "%s: unable to allocate"
+						" resources\n", __func__);
+			goto slave_err;
+		}
+
+		of_spmi_populate_node_resources(d_info, &r_info, 0);
+
+		rc = of_spmi_create_device(d_info, node);
+		if (rc) {
+			dev_err(&ctrl->dev, "%s: unable to create device for"
+				     " node %s\n", __func__, node->full_name);
+			goto slave_err;
+		}
+	}
+	return;
+
+slave_err:
+	of_spmi_free_device_resources(d_info);
+}
+
+int of_spmi_register_devices(struct spmi_controller *ctrl)
+{
+	struct device_node *node = ctrl->dev.of_node;
+
+	/* Only register child devices if the ctrl has a node pointer set */
+	if (!node)
+		return -ENODEV;
+
+	if (of_get_property(node, "spmi-slave-container", NULL)) {
+		dev_err(&ctrl->dev, "%s: structural error: spmi-slave-container"
+			" is prohibited at the root level\n", __func__);
+		return -EINVAL;
+	} else if (of_get_property(node, "spmi-dev-container", NULL)) {
+		dev_err(&ctrl->dev, "%s: structural error: spmi-dev-container"
+			" is prohibited at the root level\n", __func__);
+		return -EINVAL;
+	}
+
+	/**
+	 * Make best effort to launch as many nodes as possible. If there are
+	 * syntax errors, we will simply ignore that subtree and keep going.
+	 */
+	for_each_child_of_node(ctrl->dev.of_node, node) {
+		struct of_spmi_dev_info d_info = {};
+		const __be32 *slave_id;
+		int len, rc, have_dev_container = 0;
+
+		slave_id = of_get_property(node, "reg", &len);
+		if (!slave_id) {
+			dev_err(&ctrl->dev, "%s: invalid sid "
+					"on %s\n", __func__, node->full_name);
+			continue;
+		}
+
+		d_info.b_info.slave_id = be32_to_cpup(slave_id);
+		d_info.ctrl = ctrl;
+
+		if (of_get_property(node, "spmi-dev-container", NULL))
+			have_dev_container = 1;
+		if (of_get_property(node, "spmi-slave-container", NULL)) {
+			if (have_dev_container)
+				of_spmi_walk_dev_container(&d_info, node);
+			else
+				of_spmi_walk_slave_container(&d_info, node);
+		} else {
+			struct of_spmi_res_info r_info;
+
+			/**
+			 * A dev container at the second level without a slave
+			 * container is considered an error.
+			 */
+			if (have_dev_container) {
+				dev_err(&ctrl->dev, "%s: structural error,"
+				     " node %s has spmi-dev-container without"
+				     " specifying spmi-slave-container\n",
+				     __func__, node->full_name);
+				continue;
+			}
+
+			if (!of_device_is_available(node))
+				continue;
+
+			rc = of_spmi_alloc_device_store(&d_info, 1);
+			if (rc) {
+				dev_err(&ctrl->dev, "%s: unable to allocate"
+					" device resources\n", __func__);
+				continue;
+			}
+
+			of_spmi_init_resource(&r_info, node);
+			of_spmi_sum_node_resources(&r_info, 0);
+			rc = of_spmi_allocate_node_resources(&d_info,
+								&r_info, 0);
+			if (rc) {
+				dev_err(&ctrl->dev, "%s: unable to allocate"
+						" resources\n", __func__);
+				of_spmi_free_device_resources(&d_info);
+				continue;
+			}
+
+			of_spmi_populate_node_resources(&d_info, &r_info, 0);
+
+			rc = of_spmi_create_device(&d_info, node);
+			if (rc) {
+				dev_err(&ctrl->dev, "%s: unable to create"
+						" device\n", __func__);
+				of_spmi_free_device_resources(&d_info);
+				continue;
+			}
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(of_spmi_register_devices);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig
index 8390dca..17b5df5 100644
--- a/drivers/platform/Kconfig
+++ b/drivers/platform/Kconfig
@@ -1,3 +1,6 @@
 if X86
 source "drivers/platform/x86/Kconfig"
 endif
+if ARCH_MSM
+source "drivers/platform/msm/Kconfig"
+endif
diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile
index 782953a..58c62bd 100644
--- a/drivers/platform/Makefile
+++ b/drivers/platform/Makefile
@@ -3,3 +3,4 @@
 #
 
 obj-$(CONFIG_X86)		+= x86/
+obj-$(CONFIG_ARCH_MSM)		+= msm/
diff --git a/drivers/platform/msm/Kconfig b/drivers/platform/msm/Kconfig
new file mode 100644
index 0000000..23efb00
--- /dev/null
+++ b/drivers/platform/msm/Kconfig
@@ -0,0 +1,52 @@
+menu "Qualcomm MSM specific device drivers"
+	depends on ARCH_MSM
+
+config MSM_SSBI
+	bool "Qualcomm Single-wire Serial Bus Interface (SSBI)"
+	help
+	  If you say yes to this option, support will be included for the
+	  built-in SSBI interface on Qualcomm MSM family processors.
+
+	  This is required for communicating with Qualcomm PMICs and
+	  other devices that have the SSBI interface.
+
+config SPS
+	bool "SPS support"
+	depends on (HAS_IOMEM && (ARCH_MSM8960 || ARCH_MSM8X60 \
+			|| ARCH_APQ8064 || ARCH_MSM9615 || ARCH_MSMCOPPER))
+	select GENERIC_ALLOCATOR
+	default n
+	help
+	  The SPS (Smart Peripheral Switch) is a DMA engine.
+	  It can move data in the following modes:
+		1. Peripheral-to-Peripheral.
+		2. Peripheral-to-Memory.
+		3. Memory-to-Memory.
+
+config USB_BAM
+	boolean "USB BAM Driver"
+	depends on SPS && USB_GADGET
+	help
+	  Enabling this option adds USB BAM Driver.
+	  USB BAM driver was added to supports SPS Peripheral-to-Peripheral
+	  transfers between the USB and other peripheral.
+
+config SPS_SUPPORT_BAMDMA
+	bool "SPS support BAM DMA"
+	depends on SPS
+	default n
+	help
+	  The BAM-DMA is used for Memory-to-Memory transfers.
+	  The main use cases is RPC between processors.
+	  The BAM-DMA hardware has 2 registers sets:
+		1. A BAM HW like all the peripherals.
+		2. A DMA channel configuration (i.e. channel priority).
+
+config SPS_SUPPORT_NDP_BAM
+	bool "SPS support NDP BAM"
+	depends on SPS
+	default n
+	help
+	  No-Data-Path BAM is used to improve BAM performance.
+
+endmenu
diff --git a/drivers/platform/msm/Makefile b/drivers/platform/msm/Makefile
new file mode 100644
index 0000000..92eb492
--- /dev/null
+++ b/drivers/platform/msm/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the MSM specific device drivers.
+#
+obj-$(CONFIG_MSM_SSBI) += ssbi.o
+obj-$(CONFIG_USB_BAM) += usb_bam.o
+obj-$(CONFIG_SPS) += sps/
diff --git a/drivers/platform/msm/sps/Makefile b/drivers/platform/msm/sps/Makefile
new file mode 100644
index 0000000..f19e162
--- /dev/null
+++ b/drivers/platform/msm/sps/Makefile
@@ -0,0 +1,2 @@
+obj-y += bam.o sps_bam.o sps.o sps_dma.o sps_map.o sps_mem.o sps_rm.o
+
diff --git a/drivers/platform/msm/sps/bam.c b/drivers/platform/msm/sps/bam.c
new file mode 100644
index 0000000..cf98f68
--- /dev/null
+++ b/drivers/platform/msm/sps/bam.c
@@ -0,0 +1,1402 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/* Bus-Access-Manager (BAM) Hardware manager. */
+
+#include <linux/types.h>	/* u32 */
+#include <linux/kernel.h>	/* pr_info() */
+#include <linux/io.h>		/* ioread32() */
+#include <linux/bitops.h>	/* find_first_bit() */
+#include <linux/errno.h>	/* ENODEV */
+
+#include "bam.h"
+#include "sps_bam.h"
+
+/**
+ *  Valid BAM Hardware version.
+ *
+ */
+#define BAM_MIN_VERSION 2
+#define BAM_MAX_VERSION 0x2f
+
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+
+/* Maximum number of execution environment */
+#define BAM_MAX_EES 8
+
+/**
+ *  BAM Hardware registers.
+ *
+ */
+#define CTRL                        (0x0)
+#define REVISION                    (0x4)
+#define SW_REVISION                 (0x80)
+#define NUM_PIPES                   (0x3c)
+#define TIMER                       (0x40)
+#define TIMER_CTRL                  (0x44)
+#define DESC_CNT_TRSHLD             (0x8)
+#define IRQ_SRCS                    (0xc)
+#define IRQ_SRCS_MSK                (0x10)
+#define IRQ_SRCS_UNMASKED           (0x30)
+#define IRQ_STTS                    (0x14)
+#define IRQ_CLR                     (0x18)
+#define IRQ_EN                      (0x1c)
+#define AHB_MASTER_ERR_CTRLS        (0x24)
+#define AHB_MASTER_ERR_ADDR         (0x28)
+#define AHB_MASTER_ERR_DATA         (0x2c)
+#define TRUST_REG                   (0x70)
+#define TEST_BUS_SEL                (0x74)
+#define TEST_BUS_REG                (0x78)
+#define CNFG_BITS                   (0x7c)
+#define IRQ_SRCS_EE(n)             (0x800 + 128 * (n))
+#define IRQ_SRCS_MSK_EE(n)         (0x804 + 128 * (n))
+#define IRQ_SRCS_UNMASKED_EE(n)    (0x808 + 128 * (n))
+
+#define P_CTRL(n)                  (0x1000 + 4096 * (n))
+#define P_RST(n)                   (0x1004 + 4096 * (n))
+#define P_HALT(n)                  (0x1008 + 4096 * (n))
+#define P_IRQ_STTS(n)              (0x1010 + 4096 * (n))
+#define P_IRQ_CLR(n)               (0x1014 + 4096 * (n))
+#define P_IRQ_EN(n)                (0x1018 + 4096 * (n))
+#define P_TIMER(n)                 (0x101c + 4096 * (n))
+#define P_TIMER_CTRL(n)            (0x1020 + 4096 * (n))
+#define P_PRDCR_SDBND(n)           (0x1024 + 4096 * (n))
+#define P_CNSMR_SDBND(n)           (0x1028 + 4096 * (n))
+#define P_TRUST_REG(n)             (0x1030 + 4096 * (n))
+#define P_EVNT_DEST_ADDR(n)        (0x182c + 4096 * (n))
+#define P_EVNT_REG(n)              (0x1818 + 4096 * (n))
+#define P_SW_OFSTS(n)              (0x1800 + 4096 * (n))
+#define P_DATA_FIFO_ADDR(n)        (0x1824 + 4096 * (n))
+#define P_DESC_FIFO_ADDR(n)        (0x181c + 4096 * (n))
+#define P_EVNT_GEN_TRSHLD(n)       (0x1828 + 4096 * (n))
+#define P_FIFO_SIZES(n)            (0x1820 + 4096 * (n))
+#define P_RETR_CNTXT(n)            (0x1834 + 4096 * (n))
+#define P_SI_CNTXT(n)              (0x1838 + 4096 * (n))
+#define P_DF_CNTXT(n)              (0x1830 + 4096 * (n))
+#define P_AU_PSM_CNTXT_1(n)        (0x1804 + 4096 * (n))
+#define P_PSM_CNTXT_2(n)           (0x1808 + 4096 * (n))
+#define P_PSM_CNTXT_3(n)           (0x180c + 4096 * (n))
+#define P_PSM_CNTXT_4(n)           (0x1810 + 4096 * (n))
+#define P_PSM_CNTXT_5(n)           (0x1814 + 4096 * (n))
+
+/**
+ *  BAM Hardware registers bitmask.
+ *  format: <register>_<field>
+ *
+ */
+/* CTRL */
+#define CACHE_MISS_ERR_RESP_EN                 0x80000
+#define LOCAL_CLK_GATING                       0x60000
+#define IBC_DISABLE                            0x10000
+#define BAM_CACHED_DESC_STORE                   0x8000
+#define BAM_DESC_CACHE_SEL                      0x6000
+#define BAM_EN_ACCUM                              0x10
+#define BAM_EN                                     0x2
+#define BAM_SW_RST                                 0x1
+
+/* REVISION */
+#define BAM_INACTIV_TMR_BASE                0xff000000
+#define BAM_CMD_DESC_EN                       0x800000
+#define BAM_DESC_CACHE_DEPTH                  0x600000
+#define BAM_NUM_INACTIV_TMRS                  0x100000
+#define BAM_INACTIV_TMRS_EXST                  0x80000
+#define BAM_HIGH_FREQUENCY_BAM                 0x40000
+#define BAM_HAS_NO_BYPASS                      0x20000
+#define BAM_SECURED                            0x10000
+#define BAM_USE_VMIDMT                          0x8000
+#define BAM_AXI_ACTIVE                          0x4000
+#define BAM_CE_BUFFER_SIZE                      0x2000
+#define BAM_NUM_EES                              0xf00
+#define BAM_REVISION                              0xff
+
+/* SW_REVISION */
+#define BAM_MAJOR                           0xf0000000
+#define BAM_MINOR                            0xfff0000
+#define BAM_STEP                                0xffff
+
+/* NUM_PIPES */
+#define BAM_NON_PIPE_GRP                    0xff000000
+#define BAM_PERIPH_NON_PIPE_GRP               0xff0000
+#define BAM_NUM_PIPES                             0xff
+
+/* TIMER */
+#define BAM_TIMER                               0xffff
+
+/* TIMER_CTRL */
+#define TIMER_RST                           0x80000000
+#define TIMER_RUN                           0x40000000
+#define TIMER_MODE                          0x20000000
+#define TIMER_TRSHLD                            0xffff
+
+/* DESC_CNT_TRSHLD */
+#define BAM_DESC_CNT_TRSHLD                     0xffff
+
+/* IRQ_SRCS */
+#define BAM_IRQ                         0x80000000
+#define P_IRQ                           0x7fffffff
+
+/* IRQ_STTS */
+#define IRQ_STTS_BAM_TIMER_IRQ                         0x10
+#define IRQ_STTS_BAM_EMPTY_IRQ                          0x8
+#define IRQ_STTS_BAM_ERROR_IRQ                          0x4
+#define IRQ_STTS_BAM_HRESP_ERR_IRQ                      0x2
+
+/* IRQ_CLR */
+#define IRQ_CLR_BAM_TIMER_IRQ                          0x10
+#define IRQ_CLR_BAM_EMPTY_CLR                           0x8
+#define IRQ_CLR_BAM_ERROR_CLR                           0x4
+#define IRQ_CLR_BAM_HRESP_ERR_CLR                       0x2
+
+/* IRQ_EN */
+#define IRQ_EN_BAM_TIMER_IRQ                           0x10
+#define IRQ_EN_BAM_EMPTY_EN                             0x8
+#define IRQ_EN_BAM_ERROR_EN                             0x4
+#define IRQ_EN_BAM_HRESP_ERR_EN                         0x2
+
+/* AHB_MASTER_ERR_CTRLS */
+#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HVMID         0x7c0000
+#define AHB_MASTER_ERR_CTRLS_BAM_ERR_DIRECT_MODE    0x20000
+#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HCID           0x1f000
+#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HPROT            0xf00
+#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HBURST            0xe0
+#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HSIZE             0x18
+#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HWRITE             0x4
+#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HTRANS             0x3
+
+/* TRUST_REG  */
+#define LOCK_EE_CTRL                            0x2000
+#define BAM_VMID                                0x1f00
+#define BAM_RST_BLOCK                             0x80
+#define BAM_EE                                     0x7
+
+/* TEST_BUS_SEL */
+#define BAM_SW_EVENTS_ZERO                    0x200000
+#define BAM_SW_EVENTS_SEL                     0x180000
+#define BAM_DATA_ERASE                         0x40000
+#define BAM_DATA_FLUSH                         0x20000
+#define BAM_CLK_ALWAYS_ON                      0x10000
+#define BAM_TESTBUS_SEL                           0x7f
+
+/* CNFG_BITS */
+#define CNFG_BITS_MULTIPLE_EVENTS_DESC_AVAIL_EN  0x40000000
+#define CNFG_BITS_MULTIPLE_EVENTS_SIZE_EN        0x20000000
+#define CNFG_BITS_BAM_ZLT_W_CD_SUPPORT           0x10000000
+#define CNFG_BITS_BAM_CD_ENABLE                   0x8000000
+#define CNFG_BITS_BAM_AU_ACCUMED                  0x4000000
+#define CNFG_BITS_BAM_PSM_P_HD_DATA               0x2000000
+#define CNFG_BITS_BAM_REG_P_EN                    0x1000000
+#define CNFG_BITS_BAM_WB_DSC_AVL_P_RST             0x800000
+#define CNFG_BITS_BAM_WB_RETR_SVPNT                0x400000
+#define CNFG_BITS_BAM_WB_CSW_ACK_IDL               0x200000
+#define CNFG_BITS_BAM_WB_BLK_CSW                   0x100000
+#define CNFG_BITS_BAM_WB_P_RES                      0x80000
+#define CNFG_BITS_BAM_SI_P_RES                      0x40000
+#define CNFG_BITS_BAM_AU_P_RES                      0x20000
+#define CNFG_BITS_BAM_PSM_P_RES                     0x10000
+#define CNFG_BITS_BAM_PSM_CSW_REQ                    0x8000
+#define CNFG_BITS_BAM_SB_CLK_REQ                     0x4000
+#define CNFG_BITS_BAM_IBC_DISABLE                    0x2000
+#define CNFG_BITS_BAM_NO_EXT_P_RST                   0x1000
+#define CNFG_BITS_BAM_FULL_PIPE                       0x800
+#define CNFG_BITS_BAM_PIPE_CNFG                         0x4
+
+/* P_ctrln */
+#define P_LOCK_GROUP                          0x1f0000
+#define P_WRITE_NWD                              0x800
+#define P_PREFETCH_LIMIT                         0x600
+#define P_AUTO_EOB_SEL                           0x180
+#define P_AUTO_EOB                                0x40
+#define P_SYS_MODE                                0x20
+#define P_SYS_STRM                                0x10
+#define P_DIRECTION                                0x8
+#define P_EN                                       0x2
+
+/* P_RSTn */
+#define P_RST_P_SW_RST                             0x1
+
+/* P_HALTn */
+#define P_HALT_P_PROD_HALTED                       0x2
+#define P_HALT_P_HALT                              0x1
+
+/* P_TRUST_REGn */
+#define BAM_P_VMID                              0x1f00
+#define BAM_P_SUP_GROUP                           0xf8
+#define BAM_P_EE                                   0x7
+
+/* P_IRQ_STTSn */
+#define P_IRQ_STTS_P_TRNSFR_END_IRQ               0x20
+#define P_IRQ_STTS_P_ERR_IRQ                      0x10
+#define P_IRQ_STTS_P_OUT_OF_DESC_IRQ               0x8
+#define P_IRQ_STTS_P_WAKE_IRQ                      0x4
+#define P_IRQ_STTS_P_TIMER_IRQ                     0x2
+#define P_IRQ_STTS_P_PRCSD_DESC_IRQ                0x1
+
+/* P_IRQ_CLRn */
+#define P_IRQ_CLR_P_TRNSFR_END_CLR                0x20
+#define P_IRQ_CLR_P_ERR_CLR                       0x10
+#define P_IRQ_CLR_P_OUT_OF_DESC_CLR                0x8
+#define P_IRQ_CLR_P_WAKE_CLR                       0x4
+#define P_IRQ_CLR_P_TIMER_CLR                      0x2
+#define P_IRQ_CLR_P_PRCSD_DESC_CLR                 0x1
+
+/* P_IRQ_ENn */
+#define P_IRQ_EN_P_TRNSFR_END_EN                  0x20
+#define P_IRQ_EN_P_ERR_EN                         0x10
+#define P_IRQ_EN_P_OUT_OF_DESC_EN                  0x8
+#define P_IRQ_EN_P_WAKE_EN                         0x4
+#define P_IRQ_EN_P_TIMER_EN                        0x2
+#define P_IRQ_EN_P_PRCSD_DESC_EN                   0x1
+
+/* P_TIMERn */
+#define P_TIMER_P_TIMER                         0xffff
+
+/* P_TIMER_ctrln */
+#define P_TIMER_RST                         0x80000000
+#define P_TIMER_RUN                         0x40000000
+#define P_TIMER_MODE                        0x20000000
+#define P_TIMER_TRSHLD                          0xffff
+
+/* P_PRDCR_SDBNDn */
+#define P_PRDCR_SDBNDn_BAM_P_SB_UPDATED      0x1000000
+#define P_PRDCR_SDBNDn_BAM_P_TOGGLE           0x100000
+#define P_PRDCR_SDBNDn_BAM_P_CTRL              0xf0000
+#define P_PRDCR_SDBNDn_BAM_P_BYTES_FREE         0xffff
+
+/* P_CNSMR_SDBNDn */
+#define P_CNSMR_SDBNDn_BAM_P_SB_UPDATED      0x1000000
+#define P_CNSMR_SDBNDn_BAM_P_WAIT_4_ACK       0x800000
+#define P_CNSMR_SDBNDn_BAM_P_ACK_TOGGLE       0x400000
+#define P_CNSMR_SDBNDn_BAM_P_ACK_TOGGLE_R     0x200000
+#define P_CNSMR_SDBNDn_BAM_P_TOGGLE           0x100000
+#define P_CNSMR_SDBNDn_BAM_P_CTRL              0xf0000
+#define P_CNSMR_SDBNDn_BAM_P_BYTES_AVAIL        0xffff
+
+/* P_EVNT_regn */
+#define P_BYTES_CONSUMED                    0xffff0000
+#define P_DESC_FIFO_PEER_OFST                   0xffff
+
+/* P_SW_ofstsn */
+#define SW_OFST_IN_DESC                     0xffff0000
+#define SW_DESC_OFST                            0xffff
+
+/* P_EVNT_GEN_TRSHLDn */
+#define P_EVNT_GEN_TRSHLD_P_TRSHLD              0xffff
+
+/* P_FIFO_sizesn */
+#define P_DATA_FIFO_SIZE                    0xffff0000
+#define P_DESC_FIFO_SIZE                        0xffff
+
+#define P_RETR_CNTXT_RETR_DESC_OFST            0xffff0000
+#define P_RETR_CNTXT_RETR_OFST_IN_DESC             0xffff
+#define P_SI_CNTXT_SI_DESC_OFST                    0xffff
+#define P_DF_CNTXT_WB_ACCUMULATED              0xffff0000
+#define P_DF_CNTXT_DF_DESC_OFST                    0xffff
+#define P_AU_PSM_CNTXT_1_AU_PSM_ACCUMED        0xffff0000
+#define P_AU_PSM_CNTXT_1_AU_ACKED                  0xffff
+#define P_PSM_CNTXT_2_PSM_DESC_VALID           0x80000000
+#define P_PSM_CNTXT_2_PSM_DESC_IRQ             0x40000000
+#define P_PSM_CNTXT_2_PSM_DESC_IRQ_DONE        0x20000000
+#define P_PSM_CNTXT_2_PSM_GENERAL_BITS         0x1e000000
+#define P_PSM_CNTXT_2_PSM_CONS_STATE            0x1c00000
+#define P_PSM_CNTXT_2_PSM_PROD_SYS_STATE         0x380000
+#define P_PSM_CNTXT_2_PSM_PROD_B2B_STATE          0x70000
+#define P_PSM_CNTXT_2_PSM_DESC_SIZE                0xffff
+#define P_PSM_CNTXT_4_PSM_DESC_OFST            0xffff0000
+#define P_PSM_CNTXT_4_PSM_SAVED_ACCUMED_SIZE       0xffff
+#define P_PSM_CNTXT_5_PSM_BLOCK_BYTE_CNT       0xffff0000
+#define P_PSM_CNTXT_5_PSM_OFST_IN_DESC             0xffff
+
+#else
+
+/* Maximum number of execution environment */
+#define BAM_MAX_EES 4
+
+/**
+ *  BAM Hardware registers.
+ *
+ */
+#define CTRL                        (0xf80)
+#define REVISION                    (0xf84)
+#define NUM_PIPES                   (0xfbc)
+#define DESC_CNT_TRSHLD             (0xf88)
+#define IRQ_SRCS                    (0xf8c)
+#define IRQ_SRCS_MSK                (0xf90)
+#define IRQ_SRCS_UNMASKED           (0xfb0)
+#define IRQ_STTS                    (0xf94)
+#define IRQ_CLR                     (0xf98)
+#define IRQ_EN                      (0xf9c)
+#define IRQ_SIC_SEL                 (0xfa0)
+#define AHB_MASTER_ERR_CTRLS        (0xfa4)
+#define AHB_MASTER_ERR_ADDR         (0xfa8)
+#define AHB_MASTER_ERR_DATA         (0xfac)
+/* The addresses for IRQ_DEST and PERIPH_IRQ_DEST become reserved */
+#define IRQ_DEST                    (0xfb4)
+#define PERIPH_IRQ_DEST             (0xfb8)
+#define TEST_BUS_REG                (0xff8)
+#define CNFG_BITS                   (0xffc)
+#define TEST_BUS_SEL                (0xff4)
+#define TRUST_REG                   (0xff0)
+#define IRQ_SRCS_EE(n)             (0x1800 + 128 * (n))
+#define IRQ_SRCS_MSK_EE(n)         (0x1804 + 128 * (n))
+#define IRQ_SRCS_UNMASKED_EE(n)    (0x1808 + 128 * (n))
+
+#define P_CTRL(n)                  (0x0000 + 128 * (n))
+#define P_RST(n)                   (0x0004 + 128 * (n))
+#define P_HALT(n)                  (0x0008 + 128 * (n))
+#define P_IRQ_STTS(n)              (0x0010 + 128 * (n))
+#define P_IRQ_CLR(n)               (0x0014 + 128 * (n))
+#define P_IRQ_EN(n)                (0x0018 + 128 * (n))
+#define P_TIMER(n)                 (0x001c + 128 * (n))
+#define P_TIMER_CTRL(n)            (0x0020 + 128 * (n))
+#define P_PRDCR_SDBND(n)            (0x0024 + 128 * (n))
+#define P_CNSMR_SDBND(n)            (0x0028 + 128 * (n))
+#define P_TRUST_REG(n)             (0x0030 + 128 * (n))
+#define P_EVNT_DEST_ADDR(n)        (0x102c + 64 * (n))
+#define P_EVNT_REG(n)              (0x1018 + 64 * (n))
+#define P_SW_OFSTS(n)              (0x1000 + 64 * (n))
+#define P_DATA_FIFO_ADDR(n)        (0x1024 + 64 * (n))
+#define P_DESC_FIFO_ADDR(n)        (0x101c + 64 * (n))
+#define P_EVNT_GEN_TRSHLD(n)       (0x1028 + 64 * (n))
+#define P_FIFO_SIZES(n)            (0x1020 + 64 * (n))
+#define P_IRQ_DEST_ADDR(n)         (0x103c + 64 * (n))
+#define P_RETR_CNTXT(n)           (0x1034 + 64 * (n))
+#define P_SI_CNTXT(n)             (0x1038 + 64 * (n))
+#define P_AU_PSM_CNTXT_1(n)       (0x1004 + 64 * (n))
+#define P_PSM_CNTXT_2(n)          (0x1008 + 64 * (n))
+#define P_PSM_CNTXT_3(n)          (0x100c + 64 * (n))
+#define P_PSM_CNTXT_4(n)          (0x1010 + 64 * (n))
+#define P_PSM_CNTXT_5(n)          (0x1014 + 64 * (n))
+
+/**
+ *  BAM Hardware registers bitmask.
+ *  format: <register>_<field>
+ *
+ */
+/* CTRL */
+#define IBC_DISABLE                            0x10000
+#define BAM_CACHED_DESC_STORE                   0x8000
+#define BAM_DESC_CACHE_SEL                      0x6000
+/* BAM_PERIPH_IRQ_SIC_SEL is an obsolete field; This bit is reserved now */
+#define BAM_PERIPH_IRQ_SIC_SEL                  0x1000
+#define BAM_EN_ACCUM                              0x10
+#define BAM_EN                                     0x2
+#define BAM_SW_RST                                 0x1
+
+/* REVISION */
+#define BAM_INACTIV_TMR_BASE                0xff000000
+#define BAM_INACTIV_TMRS_EXST                  0x80000
+#define BAM_HIGH_FREQUENCY_BAM                 0x40000
+#define BAM_HAS_NO_BYPASS                      0x20000
+#define BAM_SECURED                            0x10000
+#define BAM_NUM_EES                              0xf00
+#define BAM_REVISION                              0xff
+
+/* NUM_PIPES */
+#define BAM_NON_PIPE_GRP                    0xff000000
+#define BAM_PERIPH_NON_PIPE_GRP               0xff0000
+#define BAM_NUM_PIPES                             0xff
+
+/* DESC_CNT_TRSHLD */
+#define BAM_DESC_CNT_TRSHLD                     0xffff
+
+/* IRQ_SRCS */
+#define BAM_IRQ                         0x80000000
+#define P_IRQ                           0x7fffffff
+
+#define IRQ_STTS_BAM_EMPTY_IRQ                          0x8
+#define IRQ_STTS_BAM_ERROR_IRQ                          0x4
+#define IRQ_STTS_BAM_HRESP_ERR_IRQ                      0x2
+#define IRQ_CLR_BAM_EMPTY_CLR                           0x8
+#define IRQ_CLR_BAM_ERROR_CLR                           0x4
+#define IRQ_CLR_BAM_HRESP_ERR_CLR                       0x2
+#define IRQ_EN_BAM_EMPTY_EN                             0x8
+#define IRQ_EN_BAM_ERROR_EN                             0x4
+#define IRQ_EN_BAM_HRESP_ERR_EN                         0x2
+#define IRQ_SIC_SEL_BAM_IRQ_SIC_SEL              0x80000000
+#define IRQ_SIC_SEL_P_IRQ_SIC_SEL                0x7fffffff
+#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HVMID         0x7c0000
+#define AHB_MASTER_ERR_CTRLS_BAM_ERR_DIRECT_MODE    0x20000
+#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HCID           0x1f000
+#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HPROT            0xf00
+#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HBURST            0xe0
+#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HSIZE             0x18
+#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HWRITE             0x4
+#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HTRANS             0x3
+#define CNFG_BITS_BAM_AU_ACCUMED                  0x4000000
+#define CNFG_BITS_BAM_PSM_P_HD_DATA               0x2000000
+#define CNFG_BITS_BAM_REG_P_EN                    0x1000000
+#define CNFG_BITS_BAM_WB_DSC_AVL_P_RST             0x800000
+#define CNFG_BITS_BAM_WB_RETR_SVPNT                0x400000
+#define CNFG_BITS_BAM_WB_CSW_ACK_IDL               0x200000
+#define CNFG_BITS_BAM_WB_BLK_CSW                   0x100000
+#define CNFG_BITS_BAM_WB_P_RES                      0x80000
+#define CNFG_BITS_BAM_SI_P_RES                      0x40000
+#define CNFG_BITS_BAM_AU_P_RES                      0x20000
+#define CNFG_BITS_BAM_PSM_P_RES                     0x10000
+#define CNFG_BITS_BAM_PSM_CSW_REQ                    0x8000
+#define CNFG_BITS_BAM_SB_CLK_REQ                     0x4000
+#define CNFG_BITS_BAM_IBC_DISABLE                    0x2000
+#define CNFG_BITS_BAM_NO_EXT_P_RST                   0x1000
+#define CNFG_BITS_BAM_FULL_PIPE                       0x800
+#define CNFG_BITS_BAM_PIPE_CNFG                         0x4
+
+/* TEST_BUS_SEL */
+#define BAM_DATA_ERASE                         0x40000
+#define BAM_DATA_FLUSH                         0x20000
+#define BAM_CLK_ALWAYS_ON                      0x10000
+#define BAM_TESTBUS_SEL                           0x7f
+
+/* TRUST_REG  */
+#define BAM_VMID                                0x1f00
+#define BAM_RST_BLOCK                             0x80
+#define BAM_EE                                     0x3
+
+/* P_TRUST_REGn */
+#define BAM_P_VMID                              0x1f00
+#define BAM_P_EE                                   0x3
+
+/* P_PRDCR_SDBNDn */
+#define P_PRDCR_SDBNDn_BAM_P_SB_UPDATED      0x1000000
+#define P_PRDCR_SDBNDn_BAM_P_TOGGLE           0x100000
+#define P_PRDCR_SDBNDn_BAM_P_CTRL              0xf0000
+#define P_PRDCR_SDBNDn_BAM_P_BYTES_FREE         0xffff
+/* P_CNSMR_SDBNDn */
+#define P_CNSMR_SDBNDn_BAM_P_SB_UPDATED      0x1000000
+#define P_CNSMR_SDBNDn_BAM_P_WAIT_4_ACK       0x800000
+#define P_CNSMR_SDBNDn_BAM_P_ACK_TOGGLE       0x400000
+#define P_CNSMR_SDBNDn_BAM_P_ACK_TOGGLE_R     0x200000
+#define P_CNSMR_SDBNDn_BAM_P_TOGGLE           0x100000
+#define P_CNSMR_SDBNDn_BAM_P_CTRL              0xf0000
+#define P_CNSMR_SDBNDn_BAM_P_BYTES_AVAIL        0xffff
+
+/* P_ctrln */
+#define P_PREFETCH_LIMIT                         0x600
+#define P_AUTO_EOB_SEL                           0x180
+#define P_AUTO_EOB                                0x40
+#define P_SYS_MODE                             0x20
+#define P_SYS_STRM                             0x10
+#define P_DIRECTION                             0x8
+#define P_EN                                    0x2
+
+#define P_RST_P_SW_RST                                 0x1
+
+#define P_HALT_P_PROD_HALTED                           0x2
+#define P_HALT_P_HALT                                  0x1
+
+#define P_IRQ_STTS_P_TRNSFR_END_IRQ                   0x20
+#define P_IRQ_STTS_P_ERR_IRQ                          0x10
+#define P_IRQ_STTS_P_OUT_OF_DESC_IRQ                   0x8
+#define P_IRQ_STTS_P_WAKE_IRQ                          0x4
+#define P_IRQ_STTS_P_TIMER_IRQ                         0x2
+#define P_IRQ_STTS_P_PRCSD_DESC_IRQ                    0x1
+
+#define P_IRQ_CLR_P_TRNSFR_END_CLR                    0x20
+#define P_IRQ_CLR_P_ERR_CLR                           0x10
+#define P_IRQ_CLR_P_OUT_OF_DESC_CLR                    0x8
+#define P_IRQ_CLR_P_WAKE_CLR                           0x4
+#define P_IRQ_CLR_P_TIMER_CLR                          0x2
+#define P_IRQ_CLR_P_PRCSD_DESC_CLR                     0x1
+
+#define P_IRQ_EN_P_TRNSFR_END_EN                      0x20
+#define P_IRQ_EN_P_ERR_EN                             0x10
+#define P_IRQ_EN_P_OUT_OF_DESC_EN                      0x8
+#define P_IRQ_EN_P_WAKE_EN                             0x4
+#define P_IRQ_EN_P_TIMER_EN                            0x2
+#define P_IRQ_EN_P_PRCSD_DESC_EN                       0x1
+
+#define P_TIMER_P_TIMER                             0xffff
+
+/* P_TIMER_ctrln */
+#define P_TIMER_RST                0x80000000
+#define P_TIMER_RUN                0x40000000
+#define P_TIMER_MODE               0x20000000
+#define P_TIMER_TRSHLD                 0xffff
+
+/* P_EVNT_regn */
+#define P_BYTES_CONSUMED             0xffff0000
+#define P_DESC_FIFO_PEER_OFST            0xffff
+
+/* P_SW_ofstsn */
+#define SW_OFST_IN_DESC              0xffff0000
+#define SW_DESC_OFST                     0xffff
+
+#define P_EVNT_GEN_TRSHLD_P_TRSHLD                  0xffff
+
+/* P_FIFO_sizesn */
+#define P_DATA_FIFO_SIZE           0xffff0000
+#define P_DESC_FIFO_SIZE               0xffff
+
+#define P_RETR_CNTXT_RETR_DESC_OFST            0xffff0000
+#define P_RETR_CNTXT_RETR_OFST_IN_DESC             0xffff
+#define P_SI_CNTXT_SI_DESC_OFST                    0xffff
+#define P_AU_PSM_CNTXT_1_AU_PSM_ACCUMED        0xffff0000
+#define P_AU_PSM_CNTXT_1_AU_ACKED                  0xffff
+#define P_PSM_CNTXT_2_PSM_DESC_VALID           0x80000000
+#define P_PSM_CNTXT_2_PSM_DESC_IRQ             0x40000000
+#define P_PSM_CNTXT_2_PSM_DESC_IRQ_DONE        0x20000000
+#define P_PSM_CNTXT_2_PSM_GENERAL_BITS         0x1e000000
+#define P_PSM_CNTXT_2_PSM_CONS_STATE            0x1c00000
+#define P_PSM_CNTXT_2_PSM_PROD_SYS_STATE         0x380000
+#define P_PSM_CNTXT_2_PSM_PROD_B2B_STATE          0x70000
+#define P_PSM_CNTXT_2_PSM_DESC_SIZE                0xffff
+#define P_PSM_CNTXT_4_PSM_DESC_OFST            0xffff0000
+#define P_PSM_CNTXT_4_PSM_SAVED_ACCUMED_SIZE       0xffff
+#define P_PSM_CNTXT_5_PSM_BLOCK_BYTE_CNT       0xffff0000
+#define P_PSM_CNTXT_5_PSM_OFST_IN_DESC             0xffff
+#endif
+
+#define BAM_ERROR   (-1)
+
+/* AHB buffer error control */
+enum bam_nonsecure_reset {
+	BAM_NONSECURE_RESET_ENABLE  = 0,
+	BAM_NONSECURE_RESET_DISABLE = 1,
+};
+
+/**
+ *
+ * Read register with debug info.
+ *
+ * @base - bam base virtual address.
+ * @offset - register offset.
+ *
+ * @return u32
+ */
+static inline u32 bam_read_reg(void *base, u32 offset)
+{
+	u32 val = ioread32(base + offset);
+	SPS_DBG("sps:bam 0x%x(va) read reg 0x%x r_val 0x%x.\n",
+			(u32) base, offset, val);
+	return val;
+}
+
+/**
+ * Read register masked field with debug info.
+ *
+ * @base - bam base virtual address.
+ * @offset - register offset.
+ * @mask - register bitmask.
+ *
+ * @return u32
+ */
+static inline u32 bam_read_reg_field(void *base, u32 offset, const u32 mask)
+{
+	u32 shift = find_first_bit((void *)&mask, 32);
+	u32 val = ioread32(base + offset);
+	val &= mask;		/* clear other bits */
+	val >>= shift;
+	SPS_DBG("sps:bam 0x%x(va) read reg 0x%x mask 0x%x r_val 0x%x.\n",
+			(u32) base, offset, mask, val);
+	return val;
+}
+
+/**
+ *
+ * Write register with debug info.
+ *
+ * @base - bam base virtual address.
+ * @offset - register offset.
+ * @val - value to write.
+ *
+ */
+static inline void bam_write_reg(void *base, u32 offset, u32 val)
+{
+	iowrite32(val, base + offset);
+	SPS_DBG("sps:bam 0x%x(va) write reg 0x%x w_val 0x%x.\n",
+			(u32) base, offset, val);
+}
+
+/**
+ * Write register masked field with debug info.
+ *
+ * @base - bam base virtual address.
+ * @offset - register offset.
+ * @mask - register bitmask.
+ * @val - value to write.
+ *
+ */
+static inline void bam_write_reg_field(void *base, u32 offset,
+				       const u32 mask, u32 val)
+{
+	u32 shift = find_first_bit((void *)&mask, 32);
+	u32 tmp = ioread32(base + offset);
+
+	tmp &= ~mask;		/* clear written bits */
+	val = tmp | (val << shift);
+	iowrite32(val, base + offset);
+	SPS_DBG("sps:bam 0x%x(va) write reg 0x%x w_val 0x%x.\n",
+			(u32) base, offset, val);
+}
+
+/**
+ * Initialize a BAM device
+ *
+ */
+int bam_init(void *base, u32 ee,
+		u16 summing_threshold,
+		u32 irq_mask, u32 *version, u32 *num_pipes)
+{
+	/* disable bit#11 because of HW bug */
+	u32 cfg_bits = 0xffffffff & ~(1 << 11);
+	u32 ver = 0;
+
+	SPS_DBG2("sps:%s:bam=0x%x(va).ee=%d.", __func__, (u32) base, ee);
+
+	ver = bam_read_reg_field(base, REVISION, BAM_REVISION);
+
+	if ((ver < BAM_MIN_VERSION) || (ver > BAM_MAX_VERSION)) {
+		SPS_ERR("sps:bam 0x%x(va) Invalid BAM REVISION 0x%x.\n",
+				(u32) base, ver);
+		return -ENODEV;
+	} else
+		SPS_INFO("sps:REVISION of BAM 0x%x is 0x%x.\n",
+				(u32) base, ver);
+
+	if (summing_threshold == 0) {
+		summing_threshold = 4;
+		SPS_ERR("sps:bam 0x%x(va) summing_threshold is zero , "
+				"use default 4.\n", (u32) base);
+	}
+
+	bam_write_reg_field(base, CTRL, BAM_SW_RST, 1);
+	/* No delay needed */
+	bam_write_reg_field(base, CTRL, BAM_SW_RST, 0);
+
+	bam_write_reg_field(base, CTRL, BAM_EN, 1);
+
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+	bam_write_reg_field(base, CTRL, CACHE_MISS_ERR_RESP_EN, 1);
+
+	bam_write_reg_field(base, CTRL, LOCAL_CLK_GATING, 1);
+#endif
+
+	bam_write_reg(base, DESC_CNT_TRSHLD, summing_threshold);
+
+	bam_write_reg(base, CNFG_BITS, cfg_bits);
+
+	/*
+	 *  Enable Global BAM Interrupt - for error reasons ,
+	 *  filter with mask.
+	 *  Note: Pipes interrupts are disabled until BAM_P_IRQ_enn is set
+	 */
+	bam_write_reg_field(base, IRQ_SRCS_MSK_EE(ee), BAM_IRQ, 1);
+
+	bam_write_reg(base, IRQ_EN, irq_mask);
+
+	*num_pipes = bam_read_reg_field(base, NUM_PIPES, BAM_NUM_PIPES);
+
+	*version = ver;
+
+	return 0;
+}
+
+/**
+ * Set BAM global execution environment
+ *
+ * @base - BAM virtual base address
+ *
+ * @ee - BAM execution environment index
+ *
+ * @vmid - virtual master identifier
+ *
+ * @reset - enable/disable BAM global software reset
+ */
+static void bam_set_ee(void *base, u32 ee, u32 vmid,
+			enum bam_nonsecure_reset reset)
+{
+	bam_write_reg_field(base, TRUST_REG, BAM_EE, ee);
+	bam_write_reg_field(base, TRUST_REG, BAM_VMID, vmid);
+	bam_write_reg_field(base, TRUST_REG, BAM_RST_BLOCK, reset);
+}
+
+/**
+ * Set the pipe execution environment
+ *
+ * @base - BAM virtual base address
+ *
+ * @pipe - pipe index
+ *
+ * @ee - BAM execution environment index
+ *
+ * @vmid - virtual master identifier
+ */
+static void bam_pipe_set_ee(void *base, u32 pipe, u32 ee, u32 vmid)
+{
+	bam_write_reg_field(base, P_TRUST_REG(pipe), BAM_P_EE, ee);
+	bam_write_reg_field(base, P_TRUST_REG(pipe), BAM_P_VMID, vmid);
+}
+
+/**
+ * Initialize BAM device security execution environment
+ */
+int bam_security_init(void *base, u32 ee, u32 vmid, u32 pipe_mask)
+{
+	u32 version;
+	u32 num_pipes;
+	u32 mask;
+	u32 pipe;
+
+	SPS_DBG2("sps:%s:bam=0x%x(va).", __func__, (u32) base);
+
+	/*
+	 * Discover the hardware version number and the number of pipes
+	 * supported by this BAM
+	 */
+	version = bam_read_reg_field(base, REVISION, BAM_REVISION);
+	num_pipes = bam_read_reg_field(base, NUM_PIPES, BAM_NUM_PIPES);
+	if (version < 3 || version > 0x1F) {
+		SPS_ERR("sps:bam 0x%x(va) security is not supported for this"
+			"BAM version 0x%x.\n", (u32) base, version);
+		return -ENODEV;
+	}
+
+	if (num_pipes > BAM_MAX_PIPES) {
+		SPS_ERR("sps:bam 0x%x(va) the number of pipes is more than "
+			"the maximum number allowed.", (u32) base);
+		return -ENODEV;
+	}
+
+	for (pipe = 0, mask = 1; pipe < num_pipes; pipe++, mask <<= 1)
+		if ((mask & pipe_mask) != 0)
+			bam_pipe_set_ee(base, pipe, ee, vmid);
+
+	/* If MSbit is set, assign top-level interrupt to this EE */
+	mask = 1UL << 31;
+	if ((mask & pipe_mask) != 0)
+		bam_set_ee(base, ee, vmid, BAM_NONSECURE_RESET_ENABLE);
+
+	return 0;
+}
+
+/**
+ * Verify that a BAM device is enabled and gathers the hardware
+ * configuration.
+ *
+ */
+int bam_check(void *base, u32 *version, u32 *num_pipes)
+{
+	u32 ver = 0;
+
+	SPS_DBG2("sps:%s:bam=0x%x(va).", __func__, (u32) base);
+
+	if (!bam_read_reg_field(base, CTRL, BAM_EN)) {
+		SPS_ERR("sps:%s:bam 0x%x(va) is not enabled.\n",
+				__func__, (u32) base);
+		return -ENODEV;
+	}
+
+	ver = bam_read_reg(base, REVISION) & BAM_REVISION;
+
+	/*
+	 *  Discover the hardware version number and the number of pipes
+	 *  supported by this BAM
+	 */
+	*num_pipes = bam_read_reg(base, NUM_PIPES);
+	*version = ver;
+
+	/* Check BAM version */
+	if ((ver < BAM_MIN_VERSION) || (ver > BAM_MAX_VERSION)) {
+		SPS_ERR("sps:%s:bam 0x%x(va) Invalid BAM version 0x%x.\n",
+				__func__, (u32) base, ver);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+/**
+ * Disable a BAM device
+ *
+ */
+void bam_exit(void *base, u32 ee)
+{
+	SPS_DBG2("sps:%s:bam=0x%x(va).ee=%d.", __func__, (u32) base, ee);
+
+	bam_write_reg_field(base, IRQ_SRCS_MSK_EE(ee), BAM_IRQ, 0);
+
+	bam_write_reg(base, IRQ_EN, 0);
+
+	/* Disable the BAM */
+	bam_write_reg_field(base, CTRL, BAM_EN, 0);
+}
+
+/**
+ * Output BAM register content
+ * including the TEST_BUS register content under
+ * different TEST_BUS_SEL values.
+ */
+static void bam_output_register_content(void *base)
+{
+	u32 num_pipes;
+	u32 test_bus_selection[] = {0x1, 0x2, 0x3, 0x4, 0xD, 0x10,
+			0x41, 0x42, 0x43, 0x44, 0x45, 0x46};
+	u32 i;
+	u32 size = sizeof(test_bus_selection) / sizeof(u32);
+
+	for (i = 0; i < size; i++) {
+		bam_write_reg_field(base, TEST_BUS_SEL, BAM_TESTBUS_SEL,
+					test_bus_selection[i]);
+
+		SPS_INFO("sps:bam 0x%x(va);BAM_TEST_BUS_REG is"
+			"0x%x when BAM_TEST_BUS_SEL is 0x%x.",
+			(u32) base, bam_read_reg(base, TEST_BUS_REG),
+			bam_read_reg_field(base, TEST_BUS_SEL,
+					BAM_TESTBUS_SEL));
+	}
+
+	print_bam_reg(base);
+
+	num_pipes = bam_read_reg_field(base, NUM_PIPES,
+					BAM_NUM_PIPES);
+	SPS_INFO("sps:bam 0x%x(va) has %d pipes.",
+			(u32) base, num_pipes);
+
+	for (i = 0; i < num_pipes; i++)
+		print_bam_pipe_reg(base, i);
+
+}
+
+/**
+ * Get BAM IRQ source and clear global IRQ status
+ */
+u32 bam_check_irq_source(void *base, u32 ee, u32 mask,
+				enum sps_callback_case *cb_case)
+{
+	u32 source = bam_read_reg(base, IRQ_SRCS_EE(ee));
+	u32 clr = source & (1UL << 31);
+
+	if (clr) {
+		u32 status = 0;
+		status = bam_read_reg(base, IRQ_STTS);
+
+		if (status & IRQ_STTS_BAM_ERROR_IRQ) {
+			SPS_ERR("sps:bam 0x%x(va);bam irq status="
+				"0x%x.\nsps: BAM_ERROR_IRQ\n",
+				(u32) base, status);
+			bam_output_register_content(base);
+			*cb_case = SPS_CALLBACK_BAM_ERROR_IRQ;
+		} else if (status & IRQ_STTS_BAM_HRESP_ERR_IRQ) {
+			SPS_ERR("sps:bam 0x%x(va);bam irq status="
+				"0x%x.\nsps: BAM_HRESP_ERR_IRQ\n",
+				(u32) base, status);
+			bam_output_register_content(base);
+			*cb_case = SPS_CALLBACK_BAM_HRESP_ERR_IRQ;
+		} else
+			SPS_INFO("sps:bam 0x%x(va);bam irq status="
+				"0x%x.", (u32) base, status);
+
+		bam_write_reg(base, IRQ_CLR, status);
+	}
+
+	source &= (mask|(1UL << 31));
+	return source;
+}
+
+/**
+ * Initialize a BAM pipe
+ */
+int bam_pipe_init(void *base, u32 pipe,	struct bam_pipe_parameters *param,
+					u32 ee)
+{
+	SPS_DBG2("sps:%s:bam=0x%x(va).pipe=%d.", __func__, (u32) base, pipe);
+
+	/* Reset the BAM pipe */
+	bam_write_reg(base, P_RST(pipe), 1);
+	/* No delay needed */
+	bam_write_reg(base, P_RST(pipe), 0);
+
+	/* Enable the Pipe Interrupt at the BAM level */
+	bam_write_reg_field(base, IRQ_SRCS_MSK_EE(ee), (1 << pipe), 1);
+
+	bam_write_reg(base, P_IRQ_EN(pipe), param->pipe_irq_mask);
+
+	bam_write_reg_field(base, P_CTRL(pipe), P_DIRECTION, param->dir);
+	bam_write_reg_field(base, P_CTRL(pipe), P_SYS_MODE, param->mode);
+
+	bam_write_reg(base, P_EVNT_GEN_TRSHLD(pipe), param->event_threshold);
+
+	bam_write_reg(base, P_DESC_FIFO_ADDR(pipe), param->desc_base);
+	bam_write_reg_field(base, P_FIFO_SIZES(pipe), P_DESC_FIFO_SIZE,
+			    param->desc_size);
+
+	bam_write_reg_field(base, P_CTRL(pipe), P_SYS_STRM,
+			    param->stream_mode);
+
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+	bam_write_reg_field(base, P_CTRL(pipe), P_LOCK_GROUP,
+				param->lock_group);
+
+	SPS_DBG("sps:bam=0x%x(va).pipe=%d.lock_group=%d.\n",
+			(u32) base, pipe, param->lock_group);
+#endif
+
+	if (param->mode == BAM_PIPE_MODE_BAM2BAM) {
+		u32 peer_dest_addr = param->peer_phys_addr +
+				      P_EVNT_REG(param->peer_pipe);
+
+		bam_write_reg(base, P_DATA_FIFO_ADDR(pipe),
+			      param->data_base);
+		bam_write_reg_field(base, P_FIFO_SIZES(pipe),
+				    P_DATA_FIFO_SIZE, param->data_size);
+
+		bam_write_reg(base, P_EVNT_DEST_ADDR(pipe), peer_dest_addr);
+
+		SPS_DBG2("sps:bam=0x%x(va).pipe=%d.peer_bam=0x%x."
+			"peer_pipe=%d.\n",
+			(u32) base, pipe,
+			(u32) param->peer_phys_addr,
+			param->peer_pipe);
+
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+		bam_write_reg_field(base, P_CTRL(pipe), P_WRITE_NWD,
+					param->write_nwd);
+
+		SPS_DBG("sps:%s WRITE_NWD bit for this bam2bam pipe.",
+			param->write_nwd ? "Set" : "Do not set");
+#endif
+	}
+
+	/* Pipe Enable - at last */
+	bam_write_reg_field(base, P_CTRL(pipe), P_EN, 1);
+
+	return 0;
+}
+
+/**
+ * Reset the BAM pipe
+ *
+ */
+void bam_pipe_exit(void *base, u32 pipe, u32 ee)
+{
+	SPS_DBG2("sps:%s:bam=0x%x(va).pipe=%d.", __func__, (u32) base, pipe);
+
+	bam_write_reg(base, P_IRQ_EN(pipe), 0);
+
+	/* Disable the Pipe Interrupt at the BAM level */
+	bam_write_reg_field(base, IRQ_SRCS_MSK_EE(ee), (1 << pipe), 0);
+
+	/* Pipe Disable */
+	bam_write_reg_field(base, P_CTRL(pipe), P_EN, 0);
+}
+
+/**
+ * Enable a BAM pipe
+ *
+ */
+void bam_pipe_enable(void *base, u32 pipe)
+{
+	SPS_DBG2("sps:%s:bam=0x%x(va).pipe=%d.", __func__, (u32) base, pipe);
+
+	bam_write_reg_field(base, P_CTRL(pipe), P_EN, 1);
+}
+
+/**
+ * Diasble a BAM pipe
+ *
+ */
+void bam_pipe_disable(void *base, u32 pipe)
+{
+	SPS_DBG2("sps:%s:bam=0x%x(va).pipe=%d.", __func__, (u32) base, pipe);
+
+	bam_write_reg_field(base, P_CTRL(pipe), P_EN, 0);
+}
+
+/**
+ * Check if a BAM pipe is enabled.
+ *
+ */
+int bam_pipe_is_enabled(void *base, u32 pipe)
+{
+	return bam_read_reg_field(base, P_CTRL(pipe), P_EN);
+}
+
+/**
+ * Configure interrupt for a BAM pipe
+ *
+ */
+void bam_pipe_set_irq(void *base, u32 pipe, enum bam_enable irq_en,
+		      u32 src_mask, u32 ee)
+{
+	SPS_DBG2("sps:%s:bam=0x%x(va).pipe=%d.", __func__, (u32) base, pipe);
+
+	bam_write_reg(base, P_IRQ_EN(pipe), src_mask);
+	bam_write_reg_field(base, IRQ_SRCS_MSK_EE(ee), (1 << pipe), irq_en);
+}
+
+/**
+ * Configure a BAM pipe for satellite MTI use
+ *
+ */
+void bam_pipe_satellite_mti(void *base, u32 pipe, u32 irq_gen_addr, u32 ee)
+{
+	bam_write_reg(base, P_IRQ_EN(pipe), 0);
+#ifndef CONFIG_SPS_SUPPORT_NDP_BAM
+	bam_write_reg(base, P_IRQ_DEST_ADDR(pipe), irq_gen_addr);
+	bam_write_reg_field(base, IRQ_SIC_SEL, (1 << pipe), 1);
+#endif
+	bam_write_reg_field(base, IRQ_SRCS_MSK, (1 << pipe), 1);
+}
+
+/**
+ * Configure MTI for a BAM pipe
+ *
+ */
+void bam_pipe_set_mti(void *base, u32 pipe, enum bam_enable irq_en,
+		      u32 src_mask, u32 irq_gen_addr)
+{
+	/*
+	 * MTI use is only supported on BAMs when global config is controlled
+	 * by a remote processor.
+	 * Consequently, the global configuration register to enable SIC (MTI)
+	 * support cannot be accessed.
+	 * The remote processor must be relied upon to enable the SIC and the
+	 * interrupt. Since the remote processor enable both SIC and interrupt,
+	 * the interrupt enable mask must be set to zero for polling mode.
+	 */
+#ifndef CONFIG_SPS_SUPPORT_NDP_BAM
+	bam_write_reg(base, P_IRQ_DEST_ADDR(pipe), irq_gen_addr);
+#endif
+	if (!irq_en)
+		src_mask = 0;
+
+	bam_write_reg(base, P_IRQ_EN(pipe), src_mask);
+}
+
+/**
+ * Get and Clear BAM pipe IRQ status
+ *
+ */
+u32 bam_pipe_get_and_clear_irq_status(void *base, u32 pipe)
+{
+	u32 status = 0;
+
+	status = bam_read_reg(base, P_IRQ_STTS(pipe));
+	bam_write_reg(base, P_IRQ_CLR(pipe), status);
+
+	return status;
+}
+
+/**
+ * Set write offset for a BAM pipe
+ *
+ */
+void bam_pipe_set_desc_write_offset(void *base, u32 pipe, u32 next_write)
+{
+	/*
+	 * It is not necessary to perform a read-modify-write masking to write
+	 * the P_DESC_FIFO_PEER_OFST value, since the other field in the
+	 * register (P_BYTES_CONSUMED) is read-only.
+	 */
+	bam_write_reg_field(base, P_EVNT_REG(pipe), P_DESC_FIFO_PEER_OFST,
+			    next_write);
+}
+
+/**
+ * Get write offset for a BAM pipe
+ *
+ */
+u32 bam_pipe_get_desc_write_offset(void *base, u32 pipe)
+{
+	return bam_read_reg_field(base, P_EVNT_REG(pipe),
+				  P_DESC_FIFO_PEER_OFST);
+}
+
+/**
+ * Get read offset for a BAM pipe
+ *
+ */
+u32 bam_pipe_get_desc_read_offset(void *base, u32 pipe)
+{
+	return bam_read_reg_field(base, P_SW_OFSTS(pipe), SW_DESC_OFST);
+}
+
+/**
+ * Configure inactivity timer count for a BAM pipe
+ *
+ */
+void bam_pipe_timer_config(void *base, u32 pipe, enum bam_pipe_timer_mode mode,
+			 u32 timeout_count)
+{
+	bam_write_reg_field(base, P_TIMER_CTRL(pipe), P_TIMER_MODE, mode);
+	bam_write_reg_field(base, P_TIMER_CTRL(pipe), P_TIMER_TRSHLD,
+			    timeout_count);
+}
+
+/**
+ * Reset inactivity timer for a BAM pipe
+ *
+ */
+void bam_pipe_timer_reset(void *base, u32 pipe)
+{
+	/* reset */
+	bam_write_reg_field(base, P_TIMER_CTRL(pipe), P_TIMER_RST, 0);
+	/* active */
+	bam_write_reg_field(base, P_TIMER_CTRL(pipe), P_TIMER_RST, 1);
+}
+
+/**
+ * Get inactivity timer count for a BAM pipe
+ *
+ */
+u32 bam_pipe_timer_get_count(void *base, u32 pipe)
+{
+	return bam_read_reg(base, P_TIMER(pipe));
+}
+
+#ifdef CONFIG_DEBUG_FS
+/* output the content of BAM-level registers */
+void print_bam_reg(void *virt_addr)
+{
+	int i, n;
+	u32 *bam = (u32 *) virt_addr;
+	u32 ctrl;
+	u32 ver;
+	u32 pipes;
+
+	if (bam == NULL)
+		return;
+
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+	ctrl = bam[0x0 / 4];
+	ver = bam[0x4 / 4];
+	pipes = bam[0x3c / 4];
+#else
+	ctrl = bam[0xf80 / 4];
+	ver = bam[0xf84 / 4];
+	pipes = bam[0xfbc / 4];
+#endif
+
+	SPS_INFO("\nsps:----- Content of BAM-level registers <begin> -----\n");
+
+	SPS_INFO("BAM_CTRL: 0x%x.\n", ctrl);
+	SPS_INFO("BAM_REVISION: 0x%x.\n", ver);
+	SPS_INFO("NUM_PIPES: 0x%x.\n", pipes);
+
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+	for (i = 0x0; i < 0x80; i += 0x10)
+#else
+	for (i = 0xf80; i < 0x1000; i += 0x10)
+#endif
+		SPS_INFO("bam addr 0x%x: 0x%x,0x%x,0x%x,0x%x.\n", i,
+			bam[i / 4], bam[(i / 4) + 1],
+			bam[(i / 4) + 2], bam[(i / 4) + 3]);
+
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+	for (i = 0x800, n = 0; n++ < 8; i += 0x80)
+#else
+	for (i = 0x1800, n = 0; n++ < 4; i += 0x80)
+#endif
+		SPS_INFO("bam addr 0x%x: 0x%x,0x%x,0x%x,0x%x.\n", i,
+			bam[i / 4], bam[(i / 4) + 1],
+			bam[(i / 4) + 2], bam[(i / 4) + 3]);
+
+	SPS_INFO("\nsps:----- Content of BAM-level registers <end> -----\n");
+}
+
+/* output the content of BAM pipe registers */
+void print_bam_pipe_reg(void *virt_addr, u32 pipe_index)
+{
+	int i;
+	u32 *bam = (u32 *) virt_addr;
+	u32 pipe = pipe_index;
+
+	if (bam == NULL)
+		return;
+
+	SPS_INFO("\nsps:----- Content of Pipe %d registers <begin> -----\n",
+			pipe);
+
+	SPS_INFO("-- Pipe Management Registers --\n");
+
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+	for (i = 0x1000 + 0x1000 * pipe; i < 0x1000 + 0x1000 * pipe + 0x80;
+	    i += 0x10)
+#else
+	for (i = 0x0000 + 0x80 * pipe; i < 0x0000 + 0x80 * (pipe + 1);
+	    i += 0x10)
+#endif
+		SPS_INFO("bam addr 0x%x: 0x%x,0x%x,0x%x,0x%x.\n", i,
+			bam[i / 4], bam[(i / 4) + 1],
+			bam[(i / 4) + 2], bam[(i / 4) + 3]);
+
+	SPS_INFO("-- Pipe Configuration and Internal State Registers --\n");
+
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+	for (i = 0x1800 + 0x1000 * pipe; i < 0x1800 + 0x1000 * pipe + 0x40;
+	    i += 0x10)
+#else
+	for (i = 0x1000 + 0x40 * pipe; i < 0x1000 + 0x40 * (pipe + 1);
+	    i += 0x10)
+#endif
+		SPS_INFO("bam addr 0x%x: 0x%x,0x%x,0x%x,0x%x.\n", i,
+			bam[i / 4], bam[(i / 4) + 1],
+			bam[(i / 4) + 2], bam[(i / 4) + 3]);
+
+	SPS_INFO("\nsps:----- Content of Pipe %d registers <end> -----\n",
+			pipe);
+}
+
+/* output the content of selected BAM-level registers */
+void print_bam_selected_reg(void *virt_addr)
+{
+	void *base = virt_addr;
+
+	if (base == NULL)
+		return;
+
+	SPS_INFO("\nsps:----- Content of BAM-level registers <begin> -----\n");
+
+	SPS_INFO("BAM_CTRL: 0x%x\n"
+		"BAM_REVISION: 0x%x\n"
+		"BAM_NUM_EES: %d\n"
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+		"BAM_CMD_DESC_EN: 0x%x\n"
+#endif
+		"BAM_NUM_PIPES: %d\n"
+		"BAM_DESC_CNT_TRSHLD: 0x%x (%d)\n"
+		"BAM_IRQ_SRCS: 0x%x\n"
+		"BAM_IRQ_SRCS_MSK: 0x%x\n"
+		"BAM_EE: %d\n"
+		"BAM_CNFG_BITS: 0x%x\n",
+		bam_read_reg(base, CTRL),
+		bam_read_reg_field(base, REVISION, BAM_REVISION),
+		bam_read_reg_field(base, REVISION, BAM_NUM_EES),
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+		bam_read_reg_field(base, REVISION, BAM_CMD_DESC_EN),
+#endif
+		bam_read_reg_field(base, NUM_PIPES, BAM_NUM_PIPES),
+		bam_read_reg_field(base, DESC_CNT_TRSHLD, BAM_DESC_CNT_TRSHLD),
+		bam_read_reg_field(base, DESC_CNT_TRSHLD, BAM_DESC_CNT_TRSHLD),
+		bam_read_reg(base, IRQ_SRCS),
+		bam_read_reg(base, IRQ_SRCS_MSK),
+		bam_read_reg_field(base, TRUST_REG, BAM_EE),
+		bam_read_reg(base, CNFG_BITS));
+
+	SPS_INFO("\nsps:----- Content of BAM-level registers <end> -----\n");
+}
+
+/* output the content of selected BAM pipe registers */
+void print_bam_pipe_selected_reg(void *virt_addr, u32 pipe_index)
+{
+	void *base = virt_addr;
+	u32 pipe = pipe_index;
+
+	if (base == NULL)
+		return;
+
+	SPS_INFO("\nsps:----- Registers of Pipe %d -----\n", pipe);
+
+	SPS_INFO("BAM_P_CTRL: 0x%x\n"
+		"BAM_P_SYS_MODE: %d\n"
+		"BAM_P_DIRECTION: %d\n"
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+		"BAM_P_LOCK_GROUP: 0x%x (%d)\n"
+#endif
+		"BAM_P_EE: %d\n"
+		"BAM_P_IRQ_STTS: 0x%x\n"
+		"BAM_P_IRQ_STTS_P_TRNSFR_END_IRQ: 0x%x\n"
+		"BAM_P_IRQ_STTS_P_PRCSD_DESC_IRQ: 0x%x\n"
+		"BAM_P_IRQ_EN: 0x%x\n"
+		"BAM_P_PRDCR_SDBNDn_BAM_P_BYTES_FREE: 0x%x (%d)\n"
+		"BAM_P_CNSMR_SDBNDn_BAM_P_BYTES_AVAIL: 0x%x (%d)\n"
+		"BAM_P_SW_DESC_OFST: 0x%x\n"
+		"BAM_P_DESC_FIFO_PEER_OFST: 0x%x\n"
+		"BAM_P_EVNT_DEST_ADDR: 0x%x\n"
+		"BAM_P_DESC_FIFO_ADDR: 0x%x\n"
+		"BAM_P_DESC_FIFO_SIZE: 0x%x (%d)\n"
+		"BAM_P_DATA_FIFO_ADDR: 0x%x\n"
+		"BAM_P_DATA_FIFO_SIZE: 0x%x (%d)\n"
+		"BAM_P_EVNT_GEN_TRSHLD: 0x%x (%d)\n",
+		bam_read_reg(base, P_CTRL(pipe)),
+		bam_read_reg_field(base, P_CTRL(pipe), P_SYS_MODE),
+		bam_read_reg_field(base, P_CTRL(pipe), P_DIRECTION),
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+		bam_read_reg_field(base, P_CTRL(pipe), P_LOCK_GROUP),
+		bam_read_reg_field(base, P_CTRL(pipe), P_LOCK_GROUP),
+#endif
+		bam_read_reg_field(base, P_TRUST_REG(pipe), BAM_P_EE),
+		bam_read_reg(base, P_IRQ_STTS(pipe)),
+		bam_read_reg_field(base, P_IRQ_STTS(pipe),
+					P_IRQ_STTS_P_TRNSFR_END_IRQ),
+		bam_read_reg_field(base, P_IRQ_STTS(pipe),
+					P_IRQ_STTS_P_PRCSD_DESC_IRQ),
+		bam_read_reg(base, P_IRQ_EN(pipe)),
+		bam_read_reg_field(base, P_PRDCR_SDBND(pipe),
+					P_PRDCR_SDBNDn_BAM_P_BYTES_FREE),
+		bam_read_reg_field(base, P_PRDCR_SDBND(pipe),
+					P_PRDCR_SDBNDn_BAM_P_BYTES_FREE),
+		bam_read_reg_field(base, P_CNSMR_SDBND(pipe),
+					P_CNSMR_SDBNDn_BAM_P_BYTES_AVAIL),
+		bam_read_reg_field(base, P_CNSMR_SDBND(pipe),
+					P_CNSMR_SDBNDn_BAM_P_BYTES_AVAIL),
+		bam_read_reg_field(base, P_SW_OFSTS(pipe), SW_DESC_OFST),
+		bam_read_reg_field(base, P_EVNT_REG(pipe),
+					P_DESC_FIFO_PEER_OFST),
+		bam_read_reg(base, P_EVNT_DEST_ADDR(pipe)),
+		bam_read_reg(base, P_DESC_FIFO_ADDR(pipe)),
+		bam_read_reg_field(base, P_FIFO_SIZES(pipe), P_DESC_FIFO_SIZE),
+		bam_read_reg_field(base, P_FIFO_SIZES(pipe), P_DESC_FIFO_SIZE),
+		bam_read_reg(base, P_DATA_FIFO_ADDR(pipe)),
+		bam_read_reg_field(base, P_FIFO_SIZES(pipe), P_DATA_FIFO_SIZE),
+		bam_read_reg_field(base, P_FIFO_SIZES(pipe), P_DATA_FIFO_SIZE),
+		bam_read_reg_field(base, P_EVNT_GEN_TRSHLD(pipe),
+					P_EVNT_GEN_TRSHLD_P_TRSHLD),
+		bam_read_reg_field(base, P_EVNT_GEN_TRSHLD(pipe),
+					P_EVNT_GEN_TRSHLD_P_TRSHLD));
+}
+
+/* output descriptor FIFO of a pipe */
+void print_bam_pipe_desc_fifo(void *virt_addr, u32 pipe_index)
+{
+	void *base = virt_addr;
+	u32 pipe = pipe_index;
+	u32 desc_fifo_addr;
+	u32 desc_fifo_size;
+	u32 *desc_fifo;
+	int i;
+
+	if (base == NULL)
+		return;
+
+	desc_fifo_addr = bam_read_reg(base, P_DESC_FIFO_ADDR(pipe));
+	desc_fifo_size = bam_read_reg_field(base, P_FIFO_SIZES(pipe),
+						P_DESC_FIFO_SIZE);
+
+	if (desc_fifo_addr == 0) {
+		SPS_ERR("sps:%s:desc FIFO address of Pipe %d is NULL.\n",
+			__func__, pipe);
+		return;
+	} else if (desc_fifo_size == 0) {
+		SPS_ERR("sps:%s:desc FIFO size of Pipe %d is 0.\n",
+			__func__, pipe);
+		return;
+	}
+
+	SPS_INFO("\nsps:----- descriptor FIFO of Pipe %d -----\n", pipe);
+
+	SPS_INFO("BAM_P_DESC_FIFO_ADDR: 0x%x\n"
+		"BAM_P_DESC_FIFO_SIZE: 0x%x (%d)\n\n",
+		desc_fifo_addr, desc_fifo_size, desc_fifo_size);
+
+	desc_fifo = (u32 *) phys_to_virt(desc_fifo_addr);
+
+	SPS_INFO("-------------------- begin of FIFO --------------------\n");
+
+	for (i = 0; i < desc_fifo_size; i += 0x10)
+		SPS_INFO("addr 0x%x: 0x%x, 0x%x, 0x%x, 0x%x.\n",
+			desc_fifo_addr + i,
+			desc_fifo[i / 4], desc_fifo[(i / 4) + 1],
+			desc_fifo[(i / 4) + 2], desc_fifo[(i / 4) + 3]);
+
+	SPS_INFO("--------------------  end of FIFO  --------------------\n");
+}
+#endif
diff --git a/drivers/platform/msm/sps/bam.h b/drivers/platform/msm/sps/bam.h
new file mode 100644
index 0000000..3521ffa
--- /dev/null
+++ b/drivers/platform/msm/sps/bam.h
@@ -0,0 +1,408 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/* Bus-Access-Manager (BAM) Hardware manager functions API. */
+
+#ifndef _BAM_H_
+#define _BAM_H_
+
+#include <linux/types.h>	/* u32 */
+#include <linux/io.h>		/* ioread32() */
+#include <linux/bitops.h>	/* find_first_bit() */
+#include "spsi.h"
+
+/* Pipe mode */
+enum bam_pipe_mode {
+	BAM_PIPE_MODE_BAM2BAM = 0,	/* BAM to BAM */
+	BAM_PIPE_MODE_SYSTEM = 1,	/* BAM to/from System Memory */
+};
+
+/* Pipe direction */
+enum bam_pipe_dir {
+	/* The Pipe Reads data from data-fifo or system-memory */
+	BAM_PIPE_CONSUMER = 0,
+	/* The Pipe Writes data to data-fifo or system-memory */
+	BAM_PIPE_PRODUCER = 1,
+};
+
+/* Stream mode Type */
+enum bam_stream_mode {
+	BAM_STREAM_MODE_DISABLE = 0,
+	BAM_STREAM_MODE_ENABLE = 1,
+};
+
+/* NWD written Type */
+enum bam_write_nwd {
+	BAM_WRITE_NWD_DISABLE = 0,
+	BAM_WRITE_NWD_ENABLE = 1,
+};
+
+
+/* Enable Type */
+enum bam_enable {
+	BAM_DISABLE = 0,
+	BAM_ENABLE = 1,
+};
+
+/* Pipe timer mode */
+enum bam_pipe_timer_mode {
+	BAM_PIPE_TIMER_ONESHOT = 0,
+	BAM_PIPE_TIMER_PERIODIC = 1,
+};
+
+struct transfer_descriptor {
+	u32 addr;	/* Buffer physical address */
+	u32 size:16;	/* Buffer size in bytes */
+	u32 flags:16;	/* Flag bitmask (see SPS_IOVEC_FLAG_ #defines) */
+}  __packed;
+
+/* BAM pipe initialization parameters */
+struct bam_pipe_parameters {
+	u16 event_threshold;
+	u32 pipe_irq_mask;
+	enum bam_pipe_dir dir;
+	enum bam_pipe_mode mode;
+	enum bam_write_nwd write_nwd;
+	u32 desc_base;	/* Physical address of descriptor FIFO */
+	u32 desc_size;	/* Size (bytes) of descriptor FIFO */
+	u32 lock_group;	/* The lock group this pipe belongs to */
+	enum bam_stream_mode stream_mode;
+	u32 ee;		/* BAM execution environment index */
+
+	/* The following are only valid if mode is BAM2BAM */
+	u32 peer_phys_addr;
+	u32 peer_pipe;
+	u32 data_base;	/* Physical address of data FIFO */
+	u32 data_size;	/* Size (bytes) of data FIFO */
+};
+
+/**
+ * Initialize a BAM device
+ *
+ * This function initializes a BAM device.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @ee - BAM execution environment index
+ *
+ * @summing_threshold - summing threshold (global for all pipes)
+ *
+ * @irq_mask - error interrupts mask
+ *
+ * @version - return BAM hardware version
+ *
+ * @num_pipes - return number of pipes
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int bam_init(void *base,
+		u32 ee,
+		u16 summing_threshold,
+		u32 irq_mask, u32 *version, u32 *num_pipes);
+
+/**
+ * Initialize BAM device security execution environment
+ *
+ * @base - BAM virtual base address.
+ *
+ * @ee - BAM execution environment index
+ *
+ * @vmid - virtual master identifier
+ *
+ * @pipe_mask - bit mask of pipes to assign to EE
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int bam_security_init(void *base, u32 ee, u32 vmid, u32 pipe_mask);
+
+/**
+ * Check a BAM device
+ *
+ * This function verifies that a BAM device is enabled and gathers
+ *    the hardware configuration.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @version - return BAM hardware version
+ *
+ * @num_pipes - return number of pipes
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int bam_check(void *base, u32 *version, u32 *num_pipes);
+
+/**
+ * Disable a BAM device
+ *
+ * This function disables a BAM device.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @ee - BAM execution environment index
+ *
+ */
+void bam_exit(void *base, u32 ee);
+
+/**
+ * Get BAM IRQ source and clear global IRQ status
+ *
+ * This function gets BAM IRQ source.
+ * Clear global IRQ status if it is non-zero.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @ee - BAM execution environment index
+ *
+ * @mask - active pipes mask.
+ *
+ * @case - callback case.
+ *
+ * @return IRQ status
+ *
+ */
+u32 bam_check_irq_source(void *base, u32 ee, u32 mask,
+				enum sps_callback_case *cb_case);
+
+/**
+ * Initialize a BAM pipe
+ *
+ * This function initializes a BAM pipe.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @pipe - pipe index
+ *
+ * @param - bam pipe parameters.
+ *
+ * @ee - BAM execution environment index
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int bam_pipe_init(void *base, u32 pipe, struct bam_pipe_parameters *param,
+					u32 ee);
+
+/**
+ * Reset the BAM pipe
+ *
+ * This function resets the BAM pipe.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @pipe - pipe index
+ *
+ * @ee - BAM execution environment index
+ *
+ */
+void bam_pipe_exit(void *base, u32 pipe, u32 ee);
+
+/**
+ * Enable a BAM pipe
+ *
+ * This function enables a BAM pipe.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @pipe - pipe index
+ *
+ */
+void bam_pipe_enable(void *base, u32 pipe);
+
+/**
+ * Disable a BAM pipe
+ *
+ * This function disables a BAM pipe.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @pipe - pipe index
+ *
+ */
+void bam_pipe_disable(void *base, u32 pipe);
+
+/**
+ * Get a BAM pipe enable state
+ *
+ * This function determines if a BAM pipe is enabled.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @pipe - pipe index
+ *
+ * @return true if enabled, false if disabled
+ *
+ */
+int bam_pipe_is_enabled(void *base, u32 pipe);
+
+/**
+ * Configure interrupt for a BAM pipe
+ *
+ * This function configures the interrupt for a BAM pipe.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @pipe - pipe index
+ *
+ * @irq_en - enable or disable interrupt
+ *
+ * @src_mask - interrupt source mask, set regardless of whether
+ *    interrupt is disabled
+ *
+ * @ee - BAM execution environment index
+ *
+ */
+void bam_pipe_set_irq(void *base, u32 pipe, enum bam_enable irq_en,
+		      u32 src_mask, u32 ee);
+
+/**
+ * Configure a BAM pipe for satellite MTI use
+ *
+ * This function configures a BAM pipe for satellite MTI use.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @pipe - pipe index
+ *
+ * @irq_gen_addr - physical address written to generate MTI
+ *
+ * @ee - BAM execution environment index
+ *
+ */
+void bam_pipe_satellite_mti(void *base, u32 pipe, u32 irq_gen_addr, u32 ee);
+
+/**
+ * Configure MTI for a BAM pipe
+ *
+ * This function configures the interrupt for a BAM pipe.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @pipe - pipe index
+ *
+ * @irq_en - enable or disable interrupt
+ *
+ * @src_mask - interrupt source mask, set regardless of whether
+ *    interrupt is disabled
+ *
+ * @irq_gen_addr - physical address written to generate MTI
+ *
+ */
+void bam_pipe_set_mti(void *base, u32 pipe, enum bam_enable irq_en,
+		      u32 src_mask, u32 irq_gen_addr);
+
+/**
+ * Get and Clear BAM pipe IRQ status
+ *
+ * This function gets and clears BAM pipe IRQ status.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @pipe - pipe index
+ *
+ * @return IRQ status
+ *
+ */
+u32 bam_pipe_get_and_clear_irq_status(void *base, u32 pipe);
+
+/**
+ * Set write offset for a BAM pipe
+ *
+ * This function sets the write offset for a BAM pipe.  This is
+ *    the offset that is maintained by software in system mode.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @pipe - pipe index
+ *
+ * @next_write - descriptor FIFO write offset
+ *
+ */
+void bam_pipe_set_desc_write_offset(void *base, u32 pipe, u32 next_write);
+
+/**
+ * Get write offset for a BAM pipe
+ *
+ * This function gets the write offset for a BAM pipe.  This is
+ *    the offset that is maintained by the pipe's peer pipe or by software.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @pipe - pipe index
+ *
+ * @return descriptor FIFO write offset
+ *
+ */
+u32 bam_pipe_get_desc_write_offset(void *base, u32 pipe);
+
+/**
+ * Get read offset for a BAM pipe
+ *
+ * This function gets the read offset for a BAM pipe.  This is
+ *    the offset that is maintained by the pipe in system mode.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @pipe - pipe index
+ *
+ * @return descriptor FIFO read offset
+ *
+ */
+u32 bam_pipe_get_desc_read_offset(void *base, u32 pipe);
+
+/**
+ * Configure inactivity timer count for a BAM pipe
+ *
+ * This function configures the inactivity timer count for a BAM pipe.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @pipe - pipe index
+ *
+ * @mode - timer operating mode
+ *
+ * @timeout_count - timeout count
+ *
+ */
+void bam_pipe_timer_config(void *base, u32 pipe,
+			   enum bam_pipe_timer_mode mode,
+			   u32 timeout_count);
+
+/**
+ * Reset inactivity timer for a BAM pipe
+ *
+ * This function resets the inactivity timer count for a BAM pipe.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @pipe - pipe index
+ *
+ */
+void bam_pipe_timer_reset(void *base, u32 pipe);
+
+/**
+ * Get inactivity timer count for a BAM pipe
+ *
+ * This function gets the inactivity timer count for a BAM pipe.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @pipe - pipe index
+ *
+ * @return inactivity timer count
+ *
+ */
+u32 bam_pipe_timer_get_count(void *base, u32 pipe);
+
+#endif				/* _BAM_H_ */
diff --git a/drivers/platform/msm/sps/sps.c b/drivers/platform/msm/sps/sps.c
new file mode 100644
index 0000000..fbaea09
--- /dev/null
+++ b/drivers/platform/msm/sps/sps.c
@@ -0,0 +1,2164 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/* Smart-Peripheral-Switch (SPS) Module. */
+
+#include <linux/types.h>	/* u32 */
+#include <linux/kernel.h>	/* pr_info() */
+#include <linux/module.h>	/* module_init() */
+#include <linux/slab.h>		/* kzalloc() */
+#include <linux/mutex.h>	/* mutex */
+#include <linux/device.h>	/* device */
+#include <linux/fs.h>		/* alloc_chrdev_region() */
+#include <linux/list.h>		/* list_head */
+#include <linux/memory.h>	/* memset */
+#include <linux/io.h>		/* ioremap() */
+#include <linux/clk.h>		/* clk_enable() */
+#include <linux/platform_device.h>	/* platform_get_resource_byname() */
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/of.h>
+#include <mach/msm_sps.h>	/* msm_sps_platform_data */
+
+#include "sps_bam.h"
+#include "spsi.h"
+#include "sps_core.h"
+
+#define SPS_DRV_NAME "msm_sps"	/* must match the platform_device name */
+
+/**
+ *  SPS Driver state struct
+ */
+struct sps_drv {
+	struct class *dev_class;
+	dev_t dev_num;
+	struct device *dev;
+	struct clk *pmem_clk;
+	struct clk *bamdma_clk;
+	struct clk *dfab_clk;
+
+	int is_ready;
+
+	/* Platform data */
+	u32 pipemem_phys_base;
+	u32 pipemem_size;
+	u32 bamdma_bam_phys_base;
+	u32 bamdma_bam_size;
+	u32 bamdma_dma_phys_base;
+	u32 bamdma_dma_size;
+	u32 bamdma_irq;
+	u32 bamdma_restricted_pipes;
+
+	/* Driver options bitflags (see SPS_OPT_*) */
+	u32 options;
+
+	/* Mutex to protect BAM and connection queues */
+	struct mutex lock;
+
+	/* BAM devices */
+	struct list_head bams_q;
+
+	char *hal_bam_version;
+
+	/* Connection control state */
+	struct sps_rm connection_ctrl;
+};
+
+
+/**
+ *  SPS driver state
+ */
+static struct sps_drv *sps;
+
+static void sps_device_de_init(void);
+
+#ifdef CONFIG_DEBUG_FS
+u8 debugfs_record_enabled;
+u8 logging_option;
+u8 debug_level_option;
+u8 print_limit_option;
+u8 reg_dump_option;
+
+static char *debugfs_buf;
+static u32 debugfs_buf_size;
+static u32 debugfs_buf_used;
+static int wraparound;
+
+struct dentry *dent;
+struct dentry *dfile_info;
+struct dentry *dfile_logging_option;
+struct dentry *dfile_debug_level_option;
+struct dentry *dfile_print_limit_option;
+struct dentry *dfile_reg_dump_option;
+struct dentry *dfile_bam_addr;
+
+static struct sps_bam *phy2bam(u32 phys_addr);
+
+/* record debug info for debugfs */
+void sps_debugfs_record(const char *msg)
+{
+	if (debugfs_record_enabled) {
+		if (debugfs_buf_used + MAX_MSG_LEN >= debugfs_buf_size) {
+			debugfs_buf_used = 0;
+			wraparound = true;
+		}
+		debugfs_buf_used += scnprintf(debugfs_buf + debugfs_buf_used,
+				debugfs_buf_size - debugfs_buf_used, msg);
+
+		if (wraparound)
+			scnprintf(debugfs_buf + debugfs_buf_used,
+					debugfs_buf_size - debugfs_buf_used,
+					"\n**** end line of sps log ****\n\n");
+	}
+}
+
+/* read the recorded debug info to userspace */
+static ssize_t sps_read_info(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	int ret = 0;
+	int size;
+
+	if (debugfs_record_enabled) {
+		if (wraparound)
+			size = debugfs_buf_size - MAX_MSG_LEN;
+		else
+			size = debugfs_buf_used;
+
+		ret = simple_read_from_buffer(ubuf, count, ppos,
+				debugfs_buf, size);
+	}
+
+	return ret;
+}
+
+/*
+ * set the buffer size (in KB) for debug info
+ */
+static ssize_t sps_set_info(struct file *file, const char __user *buf,
+				 size_t count, loff_t *ppos)
+{
+	unsigned long missing;
+	char str[MAX_MSG_LEN];
+	int i;
+	u32 buf_size_kb = 0;
+	u32 new_buf_size;
+
+	memset(str, 0, sizeof(str));
+	missing = copy_from_user(str, buf, sizeof(str));
+	if (missing)
+		return -EFAULT;
+
+	for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
+		buf_size_kb = (buf_size_kb * 10) + (str[i] - '0');
+
+	pr_info("sps:debugfs: input buffer size is %dKB\n", buf_size_kb);
+
+	if ((logging_option == 0) || (logging_option == 2)) {
+		pr_info("sps:debugfs: need to first turn on recording.\n");
+		return -EFAULT;
+	}
+
+	if (buf_size_kb < 1) {
+		pr_info("sps:debugfs: buffer size should be "
+			"no less than 1KB.\n");
+		return -EFAULT;
+	}
+
+	new_buf_size = buf_size_kb * SZ_1K;
+
+	if (debugfs_record_enabled) {
+		if (debugfs_buf_size == new_buf_size) {
+			/* need do nothing */
+			pr_info("sps:debugfs: input buffer size "
+				"is the same as before.\n");
+			return count;
+		} else {
+			/* release the current buffer */
+			debugfs_record_enabled = false;
+			debugfs_buf_used = 0;
+			wraparound = false;
+			kfree(debugfs_buf);
+			debugfs_buf = NULL;
+		}
+	}
+
+	/* allocate new buffer */
+	debugfs_buf_size = new_buf_size;
+
+	debugfs_buf = kzalloc(sizeof(char) * debugfs_buf_size,
+			GFP_KERNEL);
+	if (!debugfs_buf) {
+		debugfs_buf_size = 0;
+		pr_err("sps:fail to allocate memory for debug_fs.\n");
+		return -ENOMEM;
+	}
+
+	debugfs_buf_used = 0;
+	wraparound = false;
+	debugfs_record_enabled = true;
+
+	return count;
+}
+
+const struct file_operations sps_info_ops = {
+	.read = sps_read_info,
+	.write = sps_set_info,
+};
+
+/* return the current logging option to userspace */
+static ssize_t sps_read_logging_option(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	char value[MAX_MSG_LEN];
+	int nbytes;
+
+	nbytes = snprintf(value, MAX_MSG_LEN, "%d\n", logging_option);
+
+	return simple_read_from_buffer(ubuf, count, ppos, value, nbytes);
+}
+
+/*
+ * set the logging option
+ */
+static ssize_t sps_set_logging_option(struct file *file, const char __user *buf,
+				 size_t count, loff_t *ppos)
+{
+	unsigned long missing;
+	char str[MAX_MSG_LEN];
+	int i;
+	u8 option = 0;
+
+	memset(str, 0, sizeof(str));
+	missing = copy_from_user(str, buf, sizeof(str));
+	if (missing)
+		return -EFAULT;
+
+	for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
+		option = (option * 10) + (str[i] - '0');
+
+	pr_info("sps:debugfs: try to change logging option to %d\n", option);
+
+	if (option > 3) {
+		pr_err("sps:debugfs: invalid logging option:%d\n", option);
+		return count;
+	}
+
+	if (((option == 0) || (option == 2)) &&
+		((logging_option == 1) || (logging_option == 3))) {
+		debugfs_record_enabled = false;
+		kfree(debugfs_buf);
+		debugfs_buf = NULL;
+		debugfs_buf_used = 0;
+		debugfs_buf_size = 0;
+		wraparound = false;
+	}
+
+	logging_option = option;
+
+	return count;
+}
+
+const struct file_operations sps_logging_option_ops = {
+	.read = sps_read_logging_option,
+	.write = sps_set_logging_option,
+};
+
+/*
+ * input the bam physical address
+ */
+static ssize_t sps_set_bam_addr(struct file *file, const char __user *buf,
+				 size_t count, loff_t *ppos)
+{
+	unsigned long missing;
+	char str[MAX_MSG_LEN];
+	u32 i;
+	u32 bam_addr = 0;
+	struct sps_bam *bam;
+	u32 num_pipes = 0;
+	void *vir_addr;
+
+	memset(str, 0, sizeof(str));
+	missing = copy_from_user(str, buf, sizeof(str));
+	if (missing)
+		return -EFAULT;
+
+	for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
+		bam_addr = (bam_addr * 10) + (str[i] - '0');
+
+	pr_info("sps:debugfs:input BAM physical address:0x%x\n", bam_addr);
+
+	bam = phy2bam(bam_addr);
+
+	if (bam == NULL) {
+		pr_err("sps:debugfs:BAM 0x%x is not registered.", bam_addr);
+		return count;
+	} else {
+		vir_addr = bam->base;
+		num_pipes = bam->props.num_pipes;
+	}
+
+	switch (reg_dump_option) {
+	case 1: /* output all registers of this BAM */
+		print_bam_reg(vir_addr);
+		for (i = 0; i < num_pipes; i++)
+			print_bam_pipe_reg(vir_addr, i);
+		break;
+	case 2: /* output BAM-level registers */
+		print_bam_reg(vir_addr);
+		break;
+	case 3: /* output selected BAM-level registers */
+		print_bam_selected_reg(vir_addr);
+		break;
+	case 4: /* output selected registers of all pipes */
+		for (i = 0; i < num_pipes; i++)
+			print_bam_pipe_selected_reg(vir_addr, i);
+		break;
+	case 5: /* output selected registers of some pipes */
+		print_bam_pipe_selected_reg(vir_addr, 4);
+		print_bam_pipe_selected_reg(vir_addr, 5);
+		break;
+	case 6: /* output desc FIFO of all active pipes */
+		for (i = 0; i < num_pipes; i++)
+			print_bam_pipe_desc_fifo(vir_addr, i);
+		break;
+	case 7: /* output desc FIFO of some pipes */
+		print_bam_pipe_desc_fifo(vir_addr, 4);
+		print_bam_pipe_desc_fifo(vir_addr, 5);
+		break;
+	case 8: /* output selected registers and valid desc FIFO of all pipes */
+		for (i = 0; i < num_pipes; i++) {
+			print_bam_pipe_selected_reg(vir_addr, i);
+			print_bam_pipe_desc_fifo(vir_addr, i);
+		}
+		break;
+	case 9: /* output selected registers and desc FIFO of some pipes */
+		print_bam_pipe_selected_reg(vir_addr, 4);
+		print_bam_pipe_desc_fifo(vir_addr, 4);
+		print_bam_pipe_selected_reg(vir_addr, 5);
+		print_bam_pipe_desc_fifo(vir_addr, 5);
+		break;
+	default:
+		pr_info("sps:no dump option is chosen yet.");
+	}
+
+	return count;
+}
+
+const struct file_operations sps_bam_addr_ops = {
+	.write = sps_set_bam_addr,
+};
+
+static void sps_debugfs_init(void)
+{
+	debugfs_record_enabled = false;
+	logging_option = 0;
+	debug_level_option = 0;
+	print_limit_option = 0;
+	reg_dump_option = 0;
+	debugfs_buf_size = 0;
+	debugfs_buf_used = 0;
+	wraparound = false;
+
+	dent = debugfs_create_dir("sps", 0);
+	if (IS_ERR(dent)) {
+		pr_err("sps:fail to create the folder for debug_fs.\n");
+		return;
+	}
+
+	dfile_info = debugfs_create_file("info", 0666, dent, 0,
+			&sps_info_ops);
+	if (!dfile_info || IS_ERR(dfile_info)) {
+		pr_err("sps:fail to create the file for debug_fs info.\n");
+		goto info_err;
+	}
+
+	dfile_logging_option = debugfs_create_file("logging_option", 0666,
+			dent, 0, &sps_logging_option_ops);
+	if (!dfile_logging_option || IS_ERR(dfile_logging_option)) {
+		pr_err("sps:fail to create the file for debug_fs "
+			"logging_option.\n");
+		goto logging_option_err;
+	}
+
+	dfile_debug_level_option = debugfs_create_u8("debug_level_option",
+					0666, dent, &debug_level_option);
+	if (!dfile_debug_level_option || IS_ERR(dfile_debug_level_option)) {
+		pr_err("sps:fail to create the file for debug_fs "
+			"debug_level_option.\n");
+		goto debug_level_option_err;
+	}
+
+	dfile_print_limit_option = debugfs_create_u8("print_limit_option",
+					0666, dent, &print_limit_option);
+	if (!dfile_print_limit_option || IS_ERR(dfile_print_limit_option)) {
+		pr_err("sps:fail to create the file for debug_fs "
+			"print_limit_option.\n");
+		goto print_limit_option_err;
+	}
+
+	dfile_reg_dump_option = debugfs_create_u8("reg_dump_option", 0666,
+						dent, &reg_dump_option);
+	if (!dfile_reg_dump_option || IS_ERR(dfile_reg_dump_option)) {
+		pr_err("sps:fail to create the file for debug_fs "
+			"reg_dump_option.\n");
+		goto reg_dump_option_err;
+	}
+
+	dfile_bam_addr = debugfs_create_file("bam_addr", 0666,
+			dent, 0, &sps_bam_addr_ops);
+	if (!dfile_bam_addr || IS_ERR(dfile_bam_addr)) {
+		pr_err("sps:fail to create the file for debug_fs "
+			"bam_addr.\n");
+		goto bam_addr_err;
+	}
+
+	return;
+
+bam_addr_err:
+	debugfs_remove(dfile_reg_dump_option);
+reg_dump_option_err:
+	debugfs_remove(dfile_print_limit_option);
+print_limit_option_err:
+	debugfs_remove(dfile_debug_level_option);
+debug_level_option_err:
+	debugfs_remove(dfile_logging_option);
+logging_option_err:
+	debugfs_remove(dfile_info);
+info_err:
+	debugfs_remove(dent);
+}
+
+static void sps_debugfs_exit(void)
+{
+	if (dfile_info)
+		debugfs_remove(dfile_info);
+	if (dfile_logging_option)
+		debugfs_remove(dfile_logging_option);
+	if (dfile_debug_level_option)
+		debugfs_remove(dfile_debug_level_option);
+	if (dfile_print_limit_option)
+		debugfs_remove(dfile_print_limit_option);
+	if (dfile_reg_dump_option)
+		debugfs_remove(dfile_reg_dump_option);
+	if (dfile_bam_addr)
+		debugfs_remove(dfile_bam_addr);
+	if (dent)
+		debugfs_remove(dent);
+	kfree(debugfs_buf);
+	debugfs_buf = NULL;
+}
+#endif
+
+/**
+ * Initialize SPS device
+ *
+ * This function initializes the SPS device.
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+static int sps_device_init(void)
+{
+	int result;
+	int success;
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+	struct sps_bam_props bamdma_props = {0};
+#endif
+
+	SPS_DBG2("sps:%s.", __func__);
+
+	success = false;
+
+	result = sps_mem_init(sps->pipemem_phys_base, sps->pipemem_size);
+	if (result) {
+		SPS_ERR("sps:SPS memory init failed");
+		goto exit_err;
+	}
+
+	INIT_LIST_HEAD(&sps->bams_q);
+	mutex_init(&sps->lock);
+
+	if (sps_rm_init(&sps->connection_ctrl, sps->options)) {
+		SPS_ERR("sps:Fail to init SPS resource manager");
+		goto exit_err;
+	}
+
+	result = sps_bam_driver_init(sps->options);
+	if (result) {
+		SPS_ERR("sps:SPS BAM driver init failed");
+		goto exit_err;
+	}
+
+	/* Initialize the BAM DMA device */
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+	bamdma_props.phys_addr = sps->bamdma_bam_phys_base;
+	bamdma_props.virt_addr = ioremap(sps->bamdma_bam_phys_base,
+					 sps->bamdma_bam_size);
+
+	if (!bamdma_props.virt_addr) {
+		SPS_ERR("sps:Fail to IO map BAM-DMA BAM registers.\n");
+		goto exit_err;
+	}
+
+	SPS_DBG2("sps:bamdma_bam.phys=0x%x.virt=0x%x.",
+		bamdma_props.phys_addr,
+		(u32) bamdma_props.virt_addr);
+
+	bamdma_props.periph_phys_addr =	sps->bamdma_dma_phys_base;
+	bamdma_props.periph_virt_size = sps->bamdma_dma_size;
+	bamdma_props.periph_virt_addr = ioremap(sps->bamdma_dma_phys_base,
+						sps->bamdma_dma_size);
+
+	if (!bamdma_props.periph_virt_addr) {
+		SPS_ERR("sps:Fail to IO map BAM-DMA peripheral reg.\n");
+		goto exit_err;
+	}
+
+	SPS_DBG2("sps:bamdma_dma.phys=0x%x.virt=0x%x.",
+		bamdma_props.periph_phys_addr,
+		(u32) bamdma_props.periph_virt_addr);
+
+	bamdma_props.irq = sps->bamdma_irq;
+
+	bamdma_props.event_threshold = 0x10;	/* Pipe event threshold */
+	bamdma_props.summing_threshold = 0x10;	/* BAM event threshold */
+
+	bamdma_props.options = SPS_BAM_OPT_BAMDMA;
+	bamdma_props.restricted_pipes =	sps->bamdma_restricted_pipes;
+
+	result = sps_dma_init(&bamdma_props);
+	if (result) {
+		SPS_ERR("sps:SPS BAM DMA driver init failed");
+		goto exit_err;
+	}
+#endif /* CONFIG_SPS_SUPPORT_BAMDMA */
+
+	result = sps_map_init(NULL, sps->options);
+	if (result) {
+		SPS_ERR("sps:SPS connection mapping init failed");
+		goto exit_err;
+	}
+
+	success = true;
+exit_err:
+	if (!success) {
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+		sps_device_de_init();
+#endif
+		return SPS_ERROR;
+	}
+
+	return 0;
+}
+
+/**
+ * De-initialize SPS device
+ *
+ * This function de-initializes the SPS device.
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+static void sps_device_de_init(void)
+{
+	SPS_DBG2("sps:%s.", __func__);
+
+	if (sps != NULL) {
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+		sps_dma_de_init();
+#endif
+		/* Are there any remaining BAM registrations? */
+		if (!list_empty(&sps->bams_q))
+			SPS_ERR("sps:SPS de-init: BAMs are still registered");
+
+		sps_map_de_init();
+
+		kfree(sps);
+	}
+
+	sps_mem_de_init();
+}
+
+/**
+ * Initialize client state context
+ *
+ * This function initializes a client state context struct.
+ *
+ * @client - Pointer to client state context
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+static int sps_client_init(struct sps_pipe *client)
+{
+	SPS_DBG("sps:%s.", __func__);
+
+	if (client == NULL)
+		return -EINVAL;
+
+	/*
+	 * NOTE: Cannot store any state within the SPS driver because
+	 * the driver init function may not have been called yet.
+	 */
+	memset(client, 0, sizeof(*client));
+	sps_rm_config_init(&client->connect);
+
+	client->client_state = SPS_STATE_DISCONNECT;
+	client->bam = NULL;
+
+	return 0;
+}
+
+/**
+ * De-initialize client state context
+ *
+ * This function de-initializes a client state context struct.
+ *
+ * @client - Pointer to client state context
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+static int sps_client_de_init(struct sps_pipe *client)
+{
+	SPS_DBG("sps:%s.", __func__);
+
+	if (client->client_state != SPS_STATE_DISCONNECT) {
+		SPS_ERR("sps:De-init client in connected state: 0x%x",
+				   client->client_state);
+		return SPS_ERROR;
+	}
+
+	client->bam = NULL;
+	client->map = NULL;
+	memset(&client->connect, 0, sizeof(client->connect));
+
+	return 0;
+}
+
+/**
+ * Find the BAM device from the physical address
+ *
+ * This function finds a BAM device in the BAM registration list that
+ * matches the specified physical address.
+ *
+ * @phys_addr - physical address of the BAM
+ *
+ * @return - pointer to the BAM device struct, or NULL on error
+ *
+ */
+static struct sps_bam *phy2bam(u32 phys_addr)
+{
+	struct sps_bam *bam;
+
+	SPS_DBG("sps:%s.", __func__);
+
+	list_for_each_entry(bam, &sps->bams_q, list) {
+		if (bam->props.phys_addr == phys_addr)
+			return bam;
+	}
+
+	return NULL;
+}
+
+/**
+ * Find the handle of a BAM device based on the physical address
+ *
+ * This function finds a BAM device in the BAM registration list that
+ * matches the specified physical address, and returns its handle.
+ *
+ * @phys_addr - physical address of the BAM
+ *
+ * @h - device handle of the BAM
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_phy2h(u32 phys_addr, u32 *handle)
+{
+	struct sps_bam *bam;
+
+	SPS_DBG("sps:%s.", __func__);
+
+	if (handle == NULL) {
+		SPS_ERR("sps:%s:handle is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	list_for_each_entry(bam, &sps->bams_q, list) {
+		if (bam->props.phys_addr == phys_addr) {
+			*handle = (u32) bam;
+			return 0;
+		}
+	}
+
+	SPS_ERR("sps: BAM device 0x%x is not registered yet.\n", phys_addr);
+
+	return -ENODEV;
+}
+EXPORT_SYMBOL(sps_phy2h);
+
+/**
+ * Setup desc/data FIFO for bam-to-bam connection
+ *
+ * @mem_buffer - Pointer to struct for allocated memory properties.
+ *
+ * @addr - address of FIFO
+ *
+ * @size - FIFO size
+ *
+ * @use_offset - use address offset instead of absolute address
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_setup_bam2bam_fifo(struct sps_mem_buffer *mem_buffer,
+		  u32 addr, u32 size, int use_offset)
+{
+	SPS_DBG("sps:%s.", __func__);
+
+	if ((mem_buffer == NULL) || (size == 0)) {
+		SPS_ERR("sps:invalid buffer address or size.");
+		return SPS_ERROR;
+	}
+
+	if (use_offset) {
+		if ((addr + size) <= sps->pipemem_size)
+			mem_buffer->phys_base = sps->pipemem_phys_base + addr;
+		else {
+			SPS_ERR("sps:requested mem is out of "
+					"pipe mem range.\n");
+			return SPS_ERROR;
+		}
+	} else {
+		if (addr >= sps->pipemem_phys_base &&
+			(addr + size) <= (sps->pipemem_phys_base
+						+ sps->pipemem_size))
+			mem_buffer->phys_base = addr;
+		else {
+			SPS_ERR("sps:requested mem is out of "
+					"pipe mem range.\n");
+			return SPS_ERROR;
+		}
+	}
+
+	mem_buffer->base = spsi_get_mem_ptr(mem_buffer->phys_base);
+	mem_buffer->size = size;
+
+	memset(mem_buffer->base, 0, mem_buffer->size);
+
+	return 0;
+}
+EXPORT_SYMBOL(sps_setup_bam2bam_fifo);
+
+/**
+ * Find the BAM device from the handle
+ *
+ * This function finds a BAM device in the BAM registration list that
+ * matches the specified device handle.
+ *
+ * @h - device handle of the BAM
+ *
+ * @return - pointer to the BAM device struct, or NULL on error
+ *
+ */
+struct sps_bam *sps_h2bam(u32 h)
+{
+	struct sps_bam *bam;
+
+	SPS_DBG("sps:%s.", __func__);
+
+	if (h == SPS_DEV_HANDLE_MEM || h == SPS_DEV_HANDLE_INVALID)
+		return NULL;
+
+	list_for_each_entry(bam, &sps->bams_q, list) {
+		if ((u32) bam == (u32) h)
+			return bam;
+	}
+
+	SPS_ERR("sps:Can't find BAM device for handle 0x%x.", h);
+
+	return NULL;
+}
+
+/**
+ * Lock BAM device
+ *
+ * This function obtains the BAM spinlock on the client's connection.
+ *
+ * @pipe - pointer to client pipe state
+ *
+ * @return pointer to BAM device struct, or NULL on error
+ *
+ */
+static struct sps_bam *sps_bam_lock(struct sps_pipe *pipe)
+{
+	struct sps_bam *bam;
+	u32 pipe_index;
+
+	bam = pipe->bam;
+	if (bam == NULL) {
+		SPS_ERR("sps:Connection is not in connected state.");
+		return NULL;
+	}
+
+	spin_lock_irqsave(&bam->connection_lock, bam->irqsave_flags);
+
+	/* Verify client owns this pipe */
+	pipe_index = pipe->pipe_index;
+	if (pipe_index >= bam->props.num_pipes ||
+	    pipe != bam->pipes[pipe_index]) {
+		SPS_ERR("sps:Client not owner of BAM 0x%x pipe: %d (max %d)",
+			bam->props.phys_addr, pipe_index,
+			bam->props.num_pipes);
+		spin_unlock_irqrestore(&bam->connection_lock,
+						bam->irqsave_flags);
+		return NULL;
+	}
+
+	return bam;
+}
+
+/**
+ * Unlock BAM device
+ *
+ * This function releases the BAM spinlock on the client's connection.
+ *
+ * @bam - pointer to BAM device struct
+ *
+ */
+static inline void sps_bam_unlock(struct sps_bam *bam)
+{
+	spin_unlock_irqrestore(&bam->connection_lock, bam->irqsave_flags);
+}
+
+/**
+ * Connect an SPS connection end point
+ *
+ */
+int sps_connect(struct sps_pipe *h, struct sps_connect *connect)
+{
+	struct sps_pipe *pipe = h;
+	u32 dev;
+	struct sps_bam *bam;
+	int result;
+
+	SPS_DBG2("sps:%s.", __func__);
+
+	if (h == NULL) {
+		SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	} else if (connect == NULL) {
+		SPS_ERR("sps:%s:connection is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	if (sps == NULL)
+		return -ENODEV;
+
+	if (!sps->is_ready) {
+		SPS_ERR("sps:sps_connect:sps driver is not ready.\n");
+		return -EAGAIN;
+	}
+
+	if ((connect->lock_group != SPSRM_CLEAR)
+		&& (connect->lock_group > BAM_MAX_P_LOCK_GROUP_NUM)) {
+		SPS_ERR("sps:The value of pipe lock group is invalid.\n");
+		return SPS_ERROR;
+	}
+
+	mutex_lock(&sps->lock);
+	/*
+	 * Must lock the BAM device at the top level function, so must
+	 * determine which BAM is the target for the connection
+	 */
+	if (connect->mode == SPS_MODE_SRC)
+		dev = connect->source;
+	else
+		dev = connect->destination;
+
+	bam = sps_h2bam(dev);
+	if (bam == NULL) {
+		SPS_ERR("sps:Invalid BAM device handle: 0x%x", dev);
+		result = SPS_ERROR;
+		goto exit_err;
+	}
+
+	SPS_DBG2("sps:sps_connect: bam 0x%x src 0x%x dest 0x%x mode %s",
+			BAM_ID(bam),
+			connect->source,
+			connect->destination,
+			connect->mode == SPS_MODE_SRC ? "SRC" : "DEST");
+
+	/* Allocate resources for the specified connection */
+	pipe->connect = *connect;
+	mutex_lock(&bam->lock);
+	result = sps_rm_state_change(pipe, SPS_STATE_ALLOCATE);
+	mutex_unlock(&bam->lock);
+	if (result)
+		goto exit_err;
+
+	/* Configure the connection */
+	mutex_lock(&bam->lock);
+	result = sps_rm_state_change(pipe, SPS_STATE_CONNECT);
+	mutex_unlock(&bam->lock);
+	if (result) {
+		sps_disconnect(h);
+		goto exit_err;
+	}
+
+exit_err:
+	mutex_unlock(&sps->lock);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_connect);
+
+/**
+ * Disconnect an SPS connection end point
+ *
+ * This function disconnects an SPS connection end point.
+ * The SPS hardware associated with that end point will be disabled.
+ * For a connection involving system memory (SPS_DEV_HANDLE_MEM), all
+ * connection resources are deallocated.  For a peripheral-to-peripheral
+ * connection, the resources associated with the connection will not be
+ * deallocated until both end points are closed.
+ *
+ * The client must call sps_connect() for the handle before calling
+ * this function.
+ *
+ * @h - client context for SPS connection end point
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_disconnect(struct sps_pipe *h)
+{
+	struct sps_pipe *pipe = h;
+	struct sps_pipe *check;
+	struct sps_bam *bam;
+	int result;
+
+	SPS_DBG2("sps:%s.", __func__);
+
+	if (pipe == NULL) {
+		SPS_ERR("sps:Invalid pipe.");
+		return SPS_ERROR;
+	}
+
+	bam = pipe->bam;
+	if (bam == NULL) {
+		SPS_ERR("sps:BAM device of this pipe is NULL.");
+		return SPS_ERROR;
+	}
+
+	SPS_DBG2("sps:sps_disconnect: bam 0x%x src 0x%x dest 0x%x mode %s",
+			BAM_ID(bam),
+			pipe->connect.source,
+			pipe->connect.destination,
+			pipe->connect.mode == SPS_MODE_SRC ? "SRC" : "DEST");
+
+	result = SPS_ERROR;
+	/* Cross-check client with map table */
+	if (pipe->connect.mode == SPS_MODE_SRC)
+		check = pipe->map->client_src;
+	else
+		check = pipe->map->client_dest;
+
+	if (check != pipe) {
+		SPS_ERR("sps:Client context is corrupt");
+		goto exit_err;
+	}
+
+	/* Disconnect the BAM pipe */
+	mutex_lock(&bam->lock);
+	result = sps_rm_state_change(pipe, SPS_STATE_DISCONNECT);
+	mutex_unlock(&bam->lock);
+	if (result)
+		goto exit_err;
+
+	sps_rm_config_init(&pipe->connect);
+	result = 0;
+
+exit_err:
+
+	return result;
+}
+EXPORT_SYMBOL(sps_disconnect);
+
+/**
+ * Register an event object for an SPS connection end point
+ *
+ */
+int sps_register_event(struct sps_pipe *h, struct sps_register_event *reg)
+{
+	struct sps_pipe *pipe = h;
+	struct sps_bam *bam;
+	int result;
+
+	SPS_DBG2("sps:%s.", __func__);
+
+	if (h == NULL) {
+		SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	} else if (reg == NULL) {
+		SPS_ERR("sps:%s:registered event is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	if (sps == NULL)
+		return -ENODEV;
+
+	if (!sps->is_ready) {
+		SPS_ERR("sps:sps_connect:sps driver not ready.\n");
+		return -EAGAIN;
+	}
+
+	bam = sps_bam_lock(pipe);
+	if (bam == NULL)
+		return SPS_ERROR;
+
+	result = sps_bam_pipe_reg_event(bam, pipe->pipe_index, reg);
+	sps_bam_unlock(bam);
+	if (result)
+		SPS_ERR("sps:Fail to register event for BAM 0x%x pipe %d",
+			pipe->bam->props.phys_addr, pipe->pipe_index);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_register_event);
+
+/**
+ * Enable an SPS connection end point
+ *
+ */
+int sps_flow_on(struct sps_pipe *h)
+{
+	struct sps_pipe *pipe = h;
+	struct sps_bam *bam;
+	int result;
+
+	SPS_DBG2("sps:%s.", __func__);
+
+	if (h == NULL) {
+		SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	bam = sps_bam_lock(pipe);
+	if (bam == NULL)
+		return SPS_ERROR;
+
+	/* Enable the pipe data flow */
+	result = sps_rm_state_change(pipe, SPS_STATE_ENABLE);
+	sps_bam_unlock(bam);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_flow_on);
+
+/**
+ * Disable an SPS connection end point
+ *
+ */
+int sps_flow_off(struct sps_pipe *h, enum sps_flow_off mode)
+{
+	struct sps_pipe *pipe = h;
+	struct sps_bam *bam;
+	int result;
+
+	SPS_DBG2("sps:%s.", __func__);
+
+	if (h == NULL) {
+		SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	bam = sps_bam_lock(pipe);
+	if (bam == NULL)
+		return SPS_ERROR;
+
+	/* Disable the pipe data flow */
+	result = sps_rm_state_change(pipe, SPS_STATE_DISABLE);
+	sps_bam_unlock(bam);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_flow_off);
+
+/**
+ * Perform a DMA transfer on an SPS connection end point
+ *
+ */
+int sps_transfer(struct sps_pipe *h, struct sps_transfer *transfer)
+{
+	struct sps_pipe *pipe = h;
+	struct sps_bam *bam;
+	int result;
+
+	SPS_DBG("sps:%s.", __func__);
+
+	if (h == NULL) {
+		SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	} else if (transfer == NULL) {
+		SPS_ERR("sps:%s:transfer is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	bam = sps_bam_lock(pipe);
+	if (bam == NULL)
+		return SPS_ERROR;
+
+	result = sps_bam_pipe_transfer(bam, pipe->pipe_index, transfer);
+
+	sps_bam_unlock(bam);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_transfer);
+
+/**
+ * Perform a single DMA transfer on an SPS connection end point
+ *
+ */
+int sps_transfer_one(struct sps_pipe *h, u32 addr, u32 size,
+		     void *user, u32 flags)
+{
+	struct sps_pipe *pipe = h;
+	struct sps_bam *bam;
+	int result;
+
+	SPS_DBG("sps:%s.", __func__);
+
+	if (h == NULL) {
+		SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	if ((flags & SPS_IOVEC_FLAG_NWD) &&
+		!(flags & (SPS_IOVEC_FLAG_EOT | SPS_IOVEC_FLAG_CMD))) {
+		SPS_ERR("sps:NWD is only valid with EOT or CMD.\n");
+		return SPS_ERROR;
+	} else if ((flags & SPS_IOVEC_FLAG_EOT) &&
+		(flags & SPS_IOVEC_FLAG_CMD)) {
+		SPS_ERR("sps:EOT and CMD are not allowed to coexist.\n");
+		return SPS_ERROR;
+	} else if (!(flags & SPS_IOVEC_FLAG_CMD) &&
+		(flags & (SPS_IOVEC_FLAG_LOCK | SPS_IOVEC_FLAG_UNLOCK))) {
+		SPS_ERR("sps:pipe lock and unlock flags are only valid with "
+			"Command Descriptor.\n");
+		return SPS_ERROR;
+	} else if ((flags & SPS_IOVEC_FLAG_LOCK) &&
+		(flags & SPS_IOVEC_FLAG_UNLOCK)) {
+		SPS_ERR("sps:Can't lock and unlock a pipe by the same"
+			"Command Descriptor.\n");
+		return SPS_ERROR;
+	} else if ((flags & SPS_IOVEC_FLAG_IMME) &&
+		(flags & SPS_IOVEC_FLAG_CMD)) {
+		SPS_ERR("sps:Immediate and CMD are not allowed to coexist.\n");
+		return SPS_ERROR;
+	} else if ((flags & SPS_IOVEC_FLAG_IMME) &&
+		(flags & SPS_IOVEC_FLAG_NWD)) {
+		SPS_ERR("sps:Immediate and NWD are not allowed to coexist.\n");
+		return SPS_ERROR;
+	}
+
+	bam = sps_bam_lock(pipe);
+	if (bam == NULL)
+		return SPS_ERROR;
+
+	result = sps_bam_pipe_transfer_one(bam, pipe->pipe_index,
+					   addr, size, user, flags);
+
+	sps_bam_unlock(bam);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_transfer_one);
+
+/**
+ * Read event queue for an SPS connection end point
+ *
+ */
+int sps_get_event(struct sps_pipe *h, struct sps_event_notify *notify)
+{
+	struct sps_pipe *pipe = h;
+	struct sps_bam *bam;
+	int result;
+
+	SPS_DBG("sps:%s.", __func__);
+
+	if (h == NULL) {
+		SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	} else if (notify == NULL) {
+		SPS_ERR("sps:%s:event_notify is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	bam = sps_bam_lock(pipe);
+	if (bam == NULL)
+		return SPS_ERROR;
+
+	result = sps_bam_pipe_get_event(bam, pipe->pipe_index, notify);
+	sps_bam_unlock(bam);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_get_event);
+
+/**
+ * Determine whether an SPS connection end point FIFO is empty
+ *
+ */
+int sps_is_pipe_empty(struct sps_pipe *h, u32 *empty)
+{
+	struct sps_pipe *pipe = h;
+	struct sps_bam *bam;
+	int result;
+
+	SPS_DBG("sps:%s.", __func__);
+
+	if (h == NULL) {
+		SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	} else if (empty == NULL) {
+		SPS_ERR("sps:%s:result pointer is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	bam = sps_bam_lock(pipe);
+	if (bam == NULL)
+		return SPS_ERROR;
+
+	result = sps_bam_pipe_is_empty(bam, pipe->pipe_index, empty);
+	sps_bam_unlock(bam);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_is_pipe_empty);
+
+/**
+ * Get number of free transfer entries for an SPS connection end point
+ *
+ */
+int sps_get_free_count(struct sps_pipe *h, u32 *count)
+{
+	struct sps_pipe *pipe = h;
+	struct sps_bam *bam;
+	int result;
+
+	SPS_DBG("sps:%s.", __func__);
+
+	if (h == NULL) {
+		SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	} else if (count == NULL) {
+		SPS_ERR("sps:%s:result pointer is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	bam = sps_bam_lock(pipe);
+	if (bam == NULL)
+		return SPS_ERROR;
+
+	result = sps_bam_get_free_count(bam, pipe->pipe_index, count);
+	sps_bam_unlock(bam);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_get_free_count);
+
+/**
+ * Reset an SPS BAM device
+ *
+ */
+int sps_device_reset(u32 dev)
+{
+	struct sps_bam *bam;
+	int result;
+
+	SPS_DBG2("sps:%s: dev = 0x%x", __func__, dev);
+
+	if (dev == 0) {
+		SPS_ERR("sps:%s:device handle should not be 0.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	mutex_lock(&sps->lock);
+	/* Search for the target BAM device */
+	bam = sps_h2bam(dev);
+	if (bam == NULL) {
+		SPS_ERR("sps:Invalid BAM device handle: 0x%x", dev);
+		result = SPS_ERROR;
+		goto exit_err;
+	}
+
+	mutex_lock(&bam->lock);
+	result = sps_bam_reset(bam);
+	mutex_unlock(&bam->lock);
+	if (result) {
+		SPS_ERR("sps:Fail to reset BAM device: 0x%x", dev);
+		goto exit_err;
+	}
+
+exit_err:
+	mutex_unlock(&sps->lock);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_device_reset);
+
+/**
+ * Get the configuration parameters for an SPS connection end point
+ *
+ */
+int sps_get_config(struct sps_pipe *h, struct sps_connect *config)
+{
+	struct sps_pipe *pipe = h;
+
+	SPS_DBG("sps:%s.", __func__);
+
+	if (h == NULL) {
+		SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	} else if (config == NULL) {
+		SPS_ERR("sps:%s:config pointer is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	/* Copy current client connection state */
+	*config = pipe->connect;
+
+	return 0;
+}
+EXPORT_SYMBOL(sps_get_config);
+
+/**
+ * Set the configuration parameters for an SPS connection end point
+ *
+ */
+int sps_set_config(struct sps_pipe *h, struct sps_connect *config)
+{
+	struct sps_pipe *pipe = h;
+	struct sps_bam *bam;
+	int result;
+
+	SPS_DBG("sps:%s.", __func__);
+
+	if (h == NULL) {
+		SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	} else if (config == NULL) {
+		SPS_ERR("sps:%s:config pointer is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	bam = sps_bam_lock(pipe);
+	if (bam == NULL)
+		return SPS_ERROR;
+
+	result = sps_bam_pipe_set_params(bam, pipe->pipe_index,
+					 config->options);
+	if (result == 0)
+		pipe->connect.options = config->options;
+	sps_bam_unlock(bam);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_set_config);
+
+/**
+ * Set ownership of an SPS connection end point
+ *
+ */
+int sps_set_owner(struct sps_pipe *h, enum sps_owner owner,
+		  struct sps_satellite *connect)
+{
+	struct sps_pipe *pipe = h;
+	struct sps_bam *bam;
+	int result;
+
+	SPS_DBG("sps:%s.", __func__);
+
+	if (h == NULL) {
+		SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	} else if (connect == NULL) {
+		SPS_ERR("sps:%s:connection is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	if (owner != SPS_OWNER_REMOTE) {
+		SPS_ERR("sps:Unsupported ownership state: %d", owner);
+		return SPS_ERROR;
+	}
+
+	bam = sps_bam_lock(pipe);
+	if (bam == NULL)
+		return SPS_ERROR;
+
+	result = sps_bam_set_satellite(bam, pipe->pipe_index);
+	if (result)
+		goto exit_err;
+
+	/* Return satellite connect info */
+	if (connect == NULL)
+		goto exit_err;
+
+	if (pipe->connect.mode == SPS_MODE_SRC) {
+		connect->dev = pipe->map->src.bam_phys;
+		connect->pipe_index = pipe->map->src.pipe_index;
+	} else {
+		connect->dev = pipe->map->dest.bam_phys;
+		connect->pipe_index = pipe->map->dest.pipe_index;
+	}
+	connect->config = SPS_CONFIG_SATELLITE;
+	connect->options = (enum sps_option) 0;
+
+exit_err:
+	sps_bam_unlock(bam);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_set_owner);
+
+/**
+ * Allocate memory from the SPS Pipe-Memory.
+ *
+ */
+int sps_alloc_mem(struct sps_pipe *h, enum sps_mem mem,
+		  struct sps_mem_buffer *mem_buffer)
+{
+	SPS_DBG("sps:%s.", __func__);
+
+	if (h == NULL) {
+		SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	if (sps == NULL)
+		return -ENODEV;
+
+	if (!sps->is_ready) {
+		SPS_ERR("sps:sps_alloc_mem:sps driver is not ready.");
+		return -EAGAIN;
+	}
+
+	if (mem_buffer == NULL || mem_buffer->size == 0) {
+		SPS_ERR("sps:invalid memory buffer address or size");
+		return SPS_ERROR;
+	}
+
+	mem_buffer->phys_base = sps_mem_alloc_io(mem_buffer->size);
+	if (mem_buffer->phys_base == SPS_ADDR_INVALID) {
+		SPS_ERR("sps:invalid address of allocated memory");
+		return SPS_ERROR;
+	}
+
+	mem_buffer->base = spsi_get_mem_ptr(mem_buffer->phys_base);
+
+	return 0;
+}
+EXPORT_SYMBOL(sps_alloc_mem);
+
+/**
+ * Free memory from the SPS Pipe-Memory.
+ *
+ */
+int sps_free_mem(struct sps_pipe *h, struct sps_mem_buffer *mem_buffer)
+{
+	SPS_DBG("sps:%s.", __func__);
+
+	if (h == NULL) {
+		SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	if (mem_buffer == NULL || mem_buffer->phys_base == SPS_ADDR_INVALID) {
+		SPS_ERR("sps:invalid memory to free");
+		return SPS_ERROR;
+	}
+
+	sps_mem_free_io(mem_buffer->phys_base, mem_buffer->size);
+
+	return 0;
+}
+EXPORT_SYMBOL(sps_free_mem);
+
+/**
+ * Get the number of unused descriptors in the descriptor FIFO
+ * of a pipe
+ *
+ */
+int sps_get_unused_desc_num(struct sps_pipe *h, u32 *desc_num)
+{
+	struct sps_pipe *pipe = h;
+	struct sps_bam *bam;
+	int result;
+
+	SPS_DBG("sps:%s.", __func__);
+
+	if (h == NULL) {
+		SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	} else if (desc_num == NULL) {
+		SPS_ERR("sps:%s:result pointer is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	bam = sps_bam_lock(pipe);
+	if (bam == NULL)
+		return SPS_ERROR;
+
+	result = sps_bam_pipe_get_unused_desc_num(bam, pipe->pipe_index,
+						desc_num);
+
+	sps_bam_unlock(bam);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_get_unused_desc_num);
+
+/**
+ * Register a BAM device
+ *
+ */
+int sps_register_bam_device(const struct sps_bam_props *bam_props,
+				u32 *dev_handle)
+{
+	struct sps_bam *bam = NULL;
+	void *virt_addr = NULL;
+	u32 manage;
+	int ok;
+	int result;
+
+	SPS_DBG2("sps:%s.", __func__);
+
+	if (bam_props == NULL) {
+		SPS_ERR("sps:%s:bam_props is NULL.\n", __func__);
+		return SPS_ERROR;
+	} else if (dev_handle == NULL) {
+		SPS_ERR("sps:%s:device handle is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	if (sps == NULL)
+		return SPS_ERROR;
+
+	/* BAM-DMA is registered internally during power-up */
+	if ((!sps->is_ready) && !(bam_props->options & SPS_BAM_OPT_BAMDMA)) {
+		SPS_ERR("sps:sps_register_bam_device:sps driver not ready.\n");
+		return -EAGAIN;
+	}
+
+	/* Check BAM parameters */
+	manage = bam_props->manage & SPS_BAM_MGR_ACCESS_MASK;
+	if (manage != SPS_BAM_MGR_NONE) {
+		if (bam_props->virt_addr == NULL && bam_props->virt_size == 0) {
+			SPS_ERR("sps:Invalid properties for BAM: %x",
+					   bam_props->phys_addr);
+			return SPS_ERROR;
+		}
+	}
+	if ((bam_props->manage & SPS_BAM_MGR_DEVICE_REMOTE) == 0) {
+		/* BAM global is configured by local processor */
+		if (bam_props->summing_threshold == 0) {
+			SPS_ERR("sps:Invalid device ctrl properties for "
+				"BAM: %x", bam_props->phys_addr);
+			return SPS_ERROR;
+		}
+	}
+	manage = bam_props->manage &
+		  (SPS_BAM_MGR_PIPE_NO_CONFIG | SPS_BAM_MGR_PIPE_NO_CTRL);
+
+	/* In case of error */
+	*dev_handle = SPS_DEV_HANDLE_INVALID;
+	result = SPS_ERROR;
+
+	mutex_lock(&sps->lock);
+	/* Is this BAM already registered? */
+	bam = phy2bam(bam_props->phys_addr);
+	if (bam != NULL) {
+		mutex_unlock(&sps->lock);
+		SPS_ERR("sps:BAM is already registered: %x",
+				bam->props.phys_addr);
+		result = -EEXIST;
+		bam = NULL;   /* Avoid error clean-up kfree(bam) */
+		goto exit_err;
+	}
+
+	/* Perform virtual mapping if required */
+	if ((bam_props->manage & SPS_BAM_MGR_ACCESS_MASK) !=
+	    SPS_BAM_MGR_NONE && bam_props->virt_addr == NULL) {
+		/* Map the memory region */
+		virt_addr = ioremap(bam_props->phys_addr, bam_props->virt_size);
+		if (virt_addr == NULL) {
+			SPS_ERR("sps:Unable to map BAM IO mem:0x%x size:0x%x",
+				bam_props->phys_addr, bam_props->virt_size);
+			goto exit_err;
+		}
+	}
+
+	bam = kzalloc(sizeof(*bam), GFP_KERNEL);
+	if (bam == NULL) {
+		SPS_ERR("sps:Unable to allocate BAM device state: size 0x%x",
+			sizeof(*bam));
+		goto exit_err;
+	}
+	memset(bam, 0, sizeof(*bam));
+
+	mutex_init(&bam->lock);
+	mutex_lock(&bam->lock);
+
+	/* Copy configuration to BAM device descriptor */
+	bam->props = *bam_props;
+	if (virt_addr != NULL)
+		bam->props.virt_addr = virt_addr;
+
+	if ((bam_props->manage & SPS_BAM_MGR_DEVICE_REMOTE) != 0 &&
+	    (bam_props->manage & SPS_BAM_MGR_MULTI_EE) != 0 &&
+	    bam_props->ee == 0) {
+		/*
+		 * BAM global is owned by a remote processor, so force EE index
+		 * to a non-zero value to insure EE zero globals are not
+		 * modified.
+		 */
+		SPS_DBG2("sps:Setting EE for BAM %x to non-zero",
+				  bam_props->phys_addr);
+		bam->props.ee = 1;
+	}
+
+	ok = sps_bam_device_init(bam);
+	mutex_unlock(&bam->lock);
+	if (ok) {
+		SPS_ERR("sps:Fail to init BAM device: phys 0x%0x",
+			bam->props.phys_addr);
+		goto exit_err;
+	}
+
+	/* Add BAM to the list */
+	list_add_tail(&bam->list, &sps->bams_q);
+	*dev_handle = (u32) bam;
+
+	result = 0;
+exit_err:
+	mutex_unlock(&sps->lock);
+
+	if (result) {
+		if (bam != NULL) {
+			if (virt_addr != NULL)
+				iounmap(bam->props.virt_addr);
+			kfree(bam);
+		}
+
+		return result;
+	}
+
+	/* If this BAM is attached to a BAM-DMA, init the BAM-DMA device */
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+	if ((bam->props.options & SPS_BAM_OPT_BAMDMA)) {
+		if (sps_dma_device_init((u32) bam)) {
+			bam->props.options &= ~SPS_BAM_OPT_BAMDMA;
+			sps_deregister_bam_device((u32) bam);
+			SPS_ERR("sps:Fail to init BAM-DMA BAM: phys 0x%0x",
+				bam->props.phys_addr);
+			return SPS_ERROR;
+		}
+	}
+#endif /* CONFIG_SPS_SUPPORT_BAMDMA */
+
+	SPS_INFO("sps:BAM 0x%x is registered.", bam->props.phys_addr);
+
+	return 0;
+}
+EXPORT_SYMBOL(sps_register_bam_device);
+
+/**
+ * Deregister a BAM device
+ *
+ */
+int sps_deregister_bam_device(u32 dev_handle)
+{
+	struct sps_bam *bam;
+
+	SPS_DBG2("sps:%s.", __func__);
+
+	if (dev_handle == 0) {
+		SPS_ERR("sps:%s:device handle should not be 0.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	bam = sps_h2bam(dev_handle);
+	if (bam == NULL) {
+		SPS_ERR("sps:did not find a BAM for this handle");
+		return SPS_ERROR;
+	}
+
+	SPS_DBG2("sps:SPS deregister BAM: phys 0x%x.", bam->props.phys_addr);
+
+	/* If this BAM is attached to a BAM-DMA, init the BAM-DMA device */
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+	if ((bam->props.options & SPS_BAM_OPT_BAMDMA)) {
+		mutex_lock(&bam->lock);
+		(void)sps_dma_device_de_init((u32) bam);
+		bam->props.options &= ~SPS_BAM_OPT_BAMDMA;
+		mutex_unlock(&bam->lock);
+	}
+#endif
+
+	/* Remove the BAM from the registration list */
+	mutex_lock(&sps->lock);
+	list_del(&bam->list);
+	mutex_unlock(&sps->lock);
+
+	/* De-init the BAM and free resources */
+	mutex_lock(&bam->lock);
+	sps_bam_device_de_init(bam);
+	mutex_unlock(&bam->lock);
+	if (bam->props.virt_size)
+		(void)iounmap(bam->props.virt_addr);
+
+	kfree(bam);
+
+	return 0;
+}
+EXPORT_SYMBOL(sps_deregister_bam_device);
+
+/**
+ * Get processed I/O vector (completed transfers)
+ *
+ */
+int sps_get_iovec(struct sps_pipe *h, struct sps_iovec *iovec)
+{
+	struct sps_pipe *pipe = h;
+	struct sps_bam *bam;
+	int result;
+
+	SPS_DBG("sps:%s.", __func__);
+
+	if (h == NULL) {
+		SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	} else if (iovec == NULL) {
+		SPS_ERR("sps:%s:iovec pointer is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	bam = sps_bam_lock(pipe);
+	if (bam == NULL)
+		return SPS_ERROR;
+
+	/* Get an iovec from the BAM pipe descriptor FIFO */
+	result = sps_bam_pipe_get_iovec(bam, pipe->pipe_index, iovec);
+	sps_bam_unlock(bam);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_get_iovec);
+
+/**
+ * Perform timer control
+ *
+ */
+int sps_timer_ctrl(struct sps_pipe *h,
+			struct sps_timer_ctrl *timer_ctrl,
+			struct sps_timer_result *timer_result)
+{
+	struct sps_pipe *pipe = h;
+	struct sps_bam *bam;
+	int result;
+
+	SPS_DBG("sps:%s.", __func__);
+
+	if (h == NULL) {
+		SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	} else if (timer_ctrl == NULL) {
+		SPS_ERR("sps:%s:timer_ctrl pointer is NULL.\n", __func__);
+		return SPS_ERROR;
+	} else if (timer_result == NULL) {
+		SPS_ERR("sps:%s:result pointer is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	bam = sps_bam_lock(pipe);
+	if (bam == NULL)
+		return SPS_ERROR;
+
+	/* Perform the BAM pipe timer control operation */
+	result = sps_bam_pipe_timer_ctrl(bam, pipe->pipe_index, timer_ctrl,
+					 timer_result);
+	sps_bam_unlock(bam);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_timer_ctrl);
+
+/**
+ * Allocate client state context
+ *
+ */
+struct sps_pipe *sps_alloc_endpoint(void)
+{
+	struct sps_pipe *ctx = NULL;
+
+	SPS_DBG("sps:%s.", __func__);
+
+	ctx = kzalloc(sizeof(struct sps_pipe), GFP_KERNEL);
+	if (ctx == NULL) {
+		SPS_ERR("sps:Fail to allocate pipe context.");
+		return NULL;
+	}
+
+	sps_client_init(ctx);
+
+	return ctx;
+}
+EXPORT_SYMBOL(sps_alloc_endpoint);
+
+/**
+ * Free client state context
+ *
+ */
+int sps_free_endpoint(struct sps_pipe *ctx)
+{
+	int res;
+
+	SPS_DBG("sps:%s.", __func__);
+
+	if (ctx == NULL) {
+		SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	res = sps_client_de_init(ctx);
+
+	if (res == 0)
+		kfree(ctx);
+
+	return res;
+}
+EXPORT_SYMBOL(sps_free_endpoint);
+
+/**
+ * Platform Driver.
+ */
+static int get_platform_data(struct platform_device *pdev)
+{
+	struct resource *resource;
+	struct msm_sps_platform_data *pdata;
+
+	SPS_DBG("sps:%s.", __func__);
+
+	pdata = pdev->dev.platform_data;
+
+	if (pdata == NULL) {
+		SPS_ERR("sps:inavlid platform data.\n");
+		sps->bamdma_restricted_pipes = 0;
+		return -EINVAL;
+	} else {
+		sps->bamdma_restricted_pipes = pdata->bamdma_restricted_pipes;
+		SPS_DBG("sps:bamdma_restricted_pipes=0x%x.",
+			sps->bamdma_restricted_pipes);
+	}
+
+	resource  = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						 "pipe_mem");
+	if (resource) {
+		sps->pipemem_phys_base = resource->start;
+		sps->pipemem_size = resource_size(resource);
+		SPS_DBG("sps:pipemem.base=0x%x,size=0x%x.",
+			sps->pipemem_phys_base,
+			sps->pipemem_size);
+	}
+
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+	resource  = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						 "bamdma_bam");
+	if (resource) {
+		sps->bamdma_bam_phys_base = resource->start;
+		sps->bamdma_bam_size = resource_size(resource);
+		SPS_DBG("sps:bamdma_bam.base=0x%x,size=0x%x.",
+			sps->bamdma_bam_phys_base,
+			sps->bamdma_bam_size);
+	}
+
+	resource  = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						 "bamdma_dma");
+	if (resource) {
+		sps->bamdma_dma_phys_base = resource->start;
+		sps->bamdma_dma_size = resource_size(resource);
+		SPS_DBG("sps:bamdma_dma.base=0x%x,size=0x%x.",
+			sps->bamdma_dma_phys_base,
+			sps->bamdma_dma_size);
+	}
+
+	resource  = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+						 "bamdma_irq");
+	if (resource) {
+		sps->bamdma_irq = resource->start;
+		SPS_DBG("sps:bamdma_irq=%d.", sps->bamdma_irq);
+	}
+#endif
+
+	return 0;
+}
+
+/**
+ * Read data from device tree
+ */
+static int get_device_tree_data(struct platform_device *pdev)
+{
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+	struct resource *resource;
+
+	SPS_DBG("sps:%s.", __func__);
+
+	if (of_property_read_u32((&pdev->dev)->of_node,
+				"qcom,bam-dma-res-pipes",
+				&sps->bamdma_restricted_pipes)) {
+		SPS_ERR("sps:Fail to get restricted bamdma pipes.\n");
+		return -EINVAL;
+	} else
+		SPS_DBG("sps:bamdma_restricted_pipes=0x%x.",
+			sps->bamdma_restricted_pipes);
+
+	resource  = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (resource) {
+		sps->bamdma_bam_phys_base = resource->start;
+		sps->bamdma_bam_size = resource_size(resource);
+		SPS_DBG("sps:bamdma_bam.base=0x%x,size=0x%x.",
+			sps->bamdma_bam_phys_base,
+			sps->bamdma_bam_size);
+	} else {
+		SPS_ERR("sps:BAM DMA BAM mem unavailable.");
+		return -ENODEV;
+	}
+
+	resource  = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (resource) {
+		sps->bamdma_dma_phys_base = resource->start;
+		sps->bamdma_dma_size = resource_size(resource);
+		SPS_DBG("sps:bamdma_dma.base=0x%x,size=0x%x.",
+			sps->bamdma_dma_phys_base,
+			sps->bamdma_dma_size);
+	} else {
+		SPS_ERR("sps:BAM DMA mem unavailable.");
+		return -ENODEV;
+	}
+
+	resource  = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (resource) {
+		sps->bamdma_irq = resource->start;
+		SPS_DBG("sps:bamdma_irq=%d.", sps->bamdma_irq);
+	} else {
+		SPS_ERR("sps:BAM DMA IRQ unavailable.");
+		return -ENODEV;
+	}
+#endif
+
+	return 0;
+}
+
+static int __devinit msm_sps_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	SPS_DBG2("sps:%s.", __func__);
+
+	if (pdev->dev.of_node) {
+		if (get_device_tree_data(pdev)) {
+			SPS_ERR("sps:Fail to get data from device tree.");
+			return -ENODEV;
+		} else
+			SPS_DBG("sps:get data from device tree.");
+	} else {
+		if (get_platform_data(pdev)) {
+			SPS_ERR("sps:Fail to get platform data.");
+			return -ENODEV;
+		} else
+			SPS_DBG("sps:get platform data.");
+	}
+
+	/* Create Device */
+	sps->dev_class = class_create(THIS_MODULE, SPS_DRV_NAME);
+
+	ret = alloc_chrdev_region(&sps->dev_num, 0, 1, SPS_DRV_NAME);
+	if (ret) {
+		SPS_ERR("sps:alloc_chrdev_region err.");
+		goto alloc_chrdev_region_err;
+	}
+
+	sps->dev = device_create(sps->dev_class, NULL, sps->dev_num, sps,
+				SPS_DRV_NAME);
+	if (IS_ERR(sps->dev)) {
+		SPS_ERR("sps:device_create err.");
+		goto device_create_err;
+	}
+
+	sps->dfab_clk = clk_get(sps->dev, "dfab_clk");
+	if (IS_ERR(sps->dfab_clk)) {
+		SPS_ERR("sps:fail to get dfab_clk.");
+		goto clk_err;
+	} else {
+		ret = clk_set_rate(sps->dfab_clk, 64000000);
+		if (ret) {
+			SPS_ERR("sps:failed to set dfab_clk rate. "
+					"ret=%d", ret);
+			clk_put(sps->dfab_clk);
+			goto clk_err;
+		}
+	}
+
+	sps->pmem_clk = clk_get(sps->dev, "mem_clk");
+	if (IS_ERR(sps->pmem_clk)) {
+		SPS_ERR("sps:fail to get pmem_clk.");
+		goto clk_err;
+	} else {
+		ret = clk_prepare_enable(sps->pmem_clk);
+		if (ret) {
+			SPS_ERR("sps:failed to enable pmem_clk. ret=%d", ret);
+			goto clk_err;
+		}
+	}
+
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+	sps->bamdma_clk = clk_get(sps->dev, "dma_bam_pclk");
+	if (IS_ERR(sps->bamdma_clk)) {
+		SPS_ERR("sps:fail to get bamdma_clk.");
+		goto clk_err;
+	} else {
+		ret = clk_prepare_enable(sps->bamdma_clk);
+		if (ret) {
+			SPS_ERR("sps:failed to enable bamdma_clk. ret=%d", ret);
+			goto clk_err;
+		}
+	}
+
+	ret = clk_prepare_enable(sps->dfab_clk);
+	if (ret) {
+		SPS_ERR("sps:failed to enable dfab_clk. ret=%d", ret);
+		goto clk_err;
+	}
+#endif
+	ret = sps_device_init();
+	if (ret) {
+		SPS_ERR("sps:sps_device_init err.");
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+		clk_disable_unprepare(sps->dfab_clk);
+#endif
+		goto sps_device_init_err;
+	}
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+	clk_disable_unprepare(sps->dfab_clk);
+#endif
+	sps->is_ready = true;
+
+	SPS_INFO("sps:sps is ready.");
+
+	return 0;
+clk_err:
+sps_device_init_err:
+	device_destroy(sps->dev_class, sps->dev_num);
+device_create_err:
+	unregister_chrdev_region(sps->dev_num, 1);
+alloc_chrdev_region_err:
+	class_destroy(sps->dev_class);
+
+	return -ENODEV;
+}
+
+static int __devexit msm_sps_remove(struct platform_device *pdev)
+{
+	SPS_DBG("sps:%s.", __func__);
+
+	device_destroy(sps->dev_class, sps->dev_num);
+	unregister_chrdev_region(sps->dev_num, 1);
+	class_destroy(sps->dev_class);
+	sps_device_de_init();
+
+	clk_put(sps->dfab_clk);
+	clk_put(sps->pmem_clk);
+	clk_put(sps->bamdma_clk);
+
+	return 0;
+}
+
+static struct of_device_id msm_sps_match[] = {
+	{	.compatible = "qcom,msm_sps",
+	},
+	{}
+};
+
+static struct platform_driver msm_sps_driver = {
+	.probe          = msm_sps_probe,
+	.driver		= {
+		.name	= SPS_DRV_NAME,
+		.owner	= THIS_MODULE,
+		.of_match_table = msm_sps_match,
+	},
+	.remove		= __exit_p(msm_sps_remove),
+};
+
+/**
+ * Module Init.
+ */
+static int __init sps_init(void)
+{
+	int ret;
+
+#ifdef CONFIG_DEBUG_FS
+	sps_debugfs_init();
+#endif
+
+	SPS_DBG("sps:%s.", __func__);
+
+	/* Allocate the SPS driver state struct */
+	sps = kzalloc(sizeof(*sps), GFP_KERNEL);
+	if (sps == NULL) {
+		SPS_ERR("sps:Unable to allocate driver state context.");
+		return -ENOMEM;
+	}
+
+	ret = platform_driver_register(&msm_sps_driver);
+
+	return ret;
+}
+
+/**
+ * Module Exit.
+ */
+static void __exit sps_exit(void)
+{
+	SPS_DBG("sps:%s.", __func__);
+
+	platform_driver_unregister(&msm_sps_driver);
+
+	if (sps != NULL) {
+		kfree(sps);
+		sps = NULL;
+	}
+
+#ifdef CONFIG_DEBUG_FS
+	sps_debugfs_exit();
+#endif
+}
+
+arch_initcall(sps_init);
+module_exit(sps_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Smart Peripheral Switch (SPS)");
+
diff --git a/drivers/platform/msm/sps/sps_bam.c b/drivers/platform/msm/sps/sps_bam.c
new file mode 100644
index 0000000..245ccd2
--- /dev/null
+++ b/drivers/platform/msm/sps/sps_bam.c
@@ -0,0 +1,2007 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/types.h>	/* u32 */
+#include <linux/kernel.h>	/* pr_info() */
+#include <linux/mutex.h>	/* mutex */
+#include <linux/list.h>		/* list_head */
+#include <linux/slab.h>		/* kzalloc() */
+#include <linux/interrupt.h>	/* request_irq() */
+#include <linux/memory.h>	/* memset */
+#include <linux/vmalloc.h>
+
+#include "sps_bam.h"
+#include "bam.h"
+#include "spsi.h"
+
+/* All BAM global IRQ sources */
+#define BAM_IRQ_ALL (BAM_DEV_IRQ_HRESP_ERROR | BAM_DEV_IRQ_ERROR)
+
+/* BAM device state flags */
+#define BAM_STATE_INIT     (1UL << 1)
+#define BAM_STATE_IRQ      (1UL << 2)
+#define BAM_STATE_ENABLED  (1UL << 3)
+#define BAM_STATE_BAM2BAM  (1UL << 4)
+#define BAM_STATE_MTI      (1UL << 5)
+#define BAM_STATE_REMOTE   (1UL << 6)
+
+/* Mask for valid hardware descriptor flags */
+#define BAM_IOVEC_FLAG_MASK   \
+	(SPS_IOVEC_FLAG_INT | SPS_IOVEC_FLAG_EOT | SPS_IOVEC_FLAG_EOB |   \
+	SPS_IOVEC_FLAG_NWD | SPS_IOVEC_FLAG_CMD | SPS_IOVEC_FLAG_LOCK |   \
+	SPS_IOVEC_FLAG_UNLOCK | SPS_IOVEC_FLAG_IMME)
+
+/* Mask for invalid BAM-to-BAM pipe options */
+#define BAM2BAM_O_INVALID   \
+	(SPS_O_DESC_DONE | \
+	 SPS_O_EOT | \
+	 SPS_O_POLL | \
+	 SPS_O_NO_Q | \
+	 SPS_O_ACK_TRANSFERS)
+
+/**
+ * Pipe/client pointer value indicating pipe is allocated, but no client has
+ * been assigned
+ */
+#define BAM_PIPE_UNASSIGNED   ((struct sps_pipe *)0x77777777)
+
+/* Check whether pipe has been assigned */
+#define BAM_PIPE_IS_ASSIGNED(p)  \
+	(((p) != NULL) && ((p) != BAM_PIPE_UNASSIGNED))
+
+/* Is MTI use supported for a specific BAM version? */
+#define BAM_VERSION_MTI_SUPPORT(ver)   (ver <= 2)
+
+/* Event option<->event translation table entry */
+struct sps_bam_opt_event_table {
+	enum sps_event event_id;
+	enum sps_option option;
+	enum bam_pipe_irq pipe_irq;
+};
+
+static const struct sps_bam_opt_event_table opt_event_table[] = {
+	{SPS_EVENT_EOT, SPS_O_EOT, BAM_PIPE_IRQ_EOT},
+	{SPS_EVENT_DESC_DONE, SPS_O_DESC_DONE, BAM_PIPE_IRQ_DESC_INT},
+	{SPS_EVENT_WAKEUP, SPS_O_WAKEUP, BAM_PIPE_IRQ_WAKE},
+	{SPS_EVENT_INACTIVE, SPS_O_INACTIVE, BAM_PIPE_IRQ_TIMER},
+	{SPS_EVENT_OUT_OF_DESC, SPS_O_OUT_OF_DESC,
+		BAM_PIPE_IRQ_OUT_OF_DESC},
+	{SPS_EVENT_ERROR, SPS_O_ERROR, BAM_PIPE_IRQ_ERROR}
+};
+
+/* Pipe event source handler */
+static void pipe_handler(struct sps_bam *dev,
+			struct sps_pipe *pipe);
+
+/**
+ * Pipe transfer event (EOT, DESC_DONE) source handler.
+ * This function is called by pipe_handler() and other functions to process the
+ * descriptor FIFO.
+ */
+static void pipe_handler_eot(struct sps_bam *dev,
+			   struct sps_pipe *pipe);
+
+/**
+ * BAM driver initialization
+ */
+int sps_bam_driver_init(u32 options)
+{
+	int n;
+
+	/*
+	 * Check that SPS_O_ and BAM_PIPE_IRQ_ values are identical.
+	 * This is required so that the raw pipe IRQ status can be passed
+	 * to the client in the SPS_EVENT_IRQ.
+	 */
+	for (n = 0; n < ARRAY_SIZE(opt_event_table); n++) {
+		if ((u32)opt_event_table[n].option !=
+			(u32)opt_event_table[n].pipe_irq) {
+			SPS_ERR("sps:SPS_O 0x%x != HAL IRQ 0x%x",
+				opt_event_table[n].option,
+				opt_event_table[n].pipe_irq);
+			return SPS_ERROR;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * BAM interrupt service routine
+ *
+ * This function is the BAM interrupt service routine.
+ *
+ * @ctxt - pointer to ISR's registered argument
+ *
+ * @return void
+ */
+static irqreturn_t bam_isr(int irq, void *ctxt)
+{
+	struct sps_bam *dev = ctxt;
+	struct sps_pipe *pipe;
+	u32 source;
+	unsigned long flags = 0;
+
+
+	spin_lock_irqsave(&dev->isr_lock, flags);
+
+	/* Get BAM interrupt source(s) */
+	if ((dev->state & BAM_STATE_MTI) == 0) {
+		u32 mask = dev->pipe_active_mask;
+		enum sps_callback_case cb_case;
+		source = bam_check_irq_source(dev->base, dev->props.ee,
+						mask, &cb_case);
+
+		SPS_DBG1("sps:bam_isr:bam=0x%x;source=0x%x;mask=0x%x.",
+				BAM_ID(dev), source, mask);
+
+		if ((source & (1UL << 31)) && (dev->props.callback)) {
+			SPS_INFO("sps:bam_isr:bam=0x%x;callback for case %d.",
+				BAM_ID(dev), cb_case);
+			dev->props.callback(cb_case, dev->props.user);
+		}
+
+		/* Mask any non-local source */
+		source &= dev->pipe_active_mask;
+	} else {
+		/* If MTIs are used, must poll each active pipe */
+		source = dev->pipe_active_mask;
+
+		SPS_DBG1("sps:bam_isr for MTI:bam=0x%x;source=0x%x.",
+				BAM_ID(dev), source);
+	}
+
+	/* Process active pipe sources */
+	pipe = list_first_entry(&dev->pipes_q, struct sps_pipe, list);
+
+	list_for_each_entry(pipe, &dev->pipes_q, list) {
+		/* Check this pipe's bit in the source mask */
+		if ((source & pipe->pipe_index_mask)) {
+			/* This pipe has an interrupt pending */
+			pipe_handler(dev, pipe);
+			source &= ~pipe->pipe_index_mask;
+		}
+		if (source == 0)
+			break;
+	}
+
+	/* Process any inactive pipe sources */
+	if (source) {
+		SPS_ERR("sps:IRQ from BAM 0x%x inactive pipe(s) 0x%x",
+			BAM_ID(dev), source);
+		dev->irq_from_disabled_pipe++;
+	}
+
+	spin_unlock_irqrestore(&dev->isr_lock, flags);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * BAM device enable
+ */
+int sps_bam_enable(struct sps_bam *dev)
+{
+	u32 num_pipes;
+	u32 irq_mask;
+	int result;
+	int rc;
+	int MTIenabled;
+
+	/* Is this BAM enabled? */
+	if ((dev->state & BAM_STATE_ENABLED))
+		return 0;	/* Yes, so no work to do */
+
+	/* Is there any access to this BAM? */
+	if ((dev->props.manage & SPS_BAM_MGR_ACCESS_MASK) == SPS_BAM_MGR_NONE) {
+		SPS_ERR("sps:No local access to BAM 0x%x", BAM_ID(dev));
+		return SPS_ERROR;
+	}
+
+	/* Set interrupt handling */
+	if ((dev->props.options & SPS_BAM_OPT_IRQ_DISABLED) != 0 ||
+	    dev->props.irq == SPS_IRQ_INVALID) {
+		/* Disable the BAM interrupt */
+		irq_mask = 0;
+		dev->state &= ~BAM_STATE_IRQ;
+	} else {
+		/* Register BAM ISR */
+		if (dev->props.irq > 0)
+			result = request_irq(dev->props.irq,
+				    (irq_handler_t) bam_isr,
+				    IRQF_TRIGGER_HIGH, "sps", dev);
+
+		if (result) {
+			SPS_ERR("sps:Failed to enable BAM 0x%x IRQ %d",
+				BAM_ID(dev), dev->props.irq);
+			return SPS_ERROR;
+		}
+
+		/* Enable the BAM interrupt */
+		irq_mask = BAM_IRQ_ALL;
+		dev->state |= BAM_STATE_IRQ;
+
+		/* Register BAM IRQ for apps wakeup */
+		if (dev->props.options & SPS_BAM_OPT_IRQ_WAKEUP) {
+			result = enable_irq_wake(dev->props.irq);
+
+			if (result) {
+				SPS_ERR("sps:Fail to enable wakeup irq "
+					"BAM 0x%x IRQ %d",
+					BAM_ID(dev), dev->props.irq);
+				return SPS_ERROR;
+			} else
+				SPS_DBG2("sps:Enable wakeup irq for "
+					"BAM 0x%x IRQ %d",
+					BAM_ID(dev), dev->props.irq);
+		}
+	}
+
+	/* Is global BAM control managed by the local processor? */
+	num_pipes = 0;
+	if ((dev->props.manage & SPS_BAM_MGR_DEVICE_REMOTE) == 0)
+		/* Yes, so initialize the BAM device */
+		rc = bam_init(dev->base,
+				  dev->props.ee,
+				  (u16) dev->props.summing_threshold,
+				  irq_mask,
+				  &dev->version, &num_pipes);
+	else
+		/* No, so just verify that it is enabled */
+		rc = bam_check(dev->base, &dev->version, &num_pipes);
+
+	if (rc) {
+		SPS_ERR("sps:Fail to init BAM 0x%x IRQ %d",
+			BAM_ID(dev), dev->props.irq);
+		return SPS_ERROR;
+	}
+
+	/* Check if this BAM supports MTIs (Message Triggered Interrupts) or
+	 * multiple EEs (Execution Environments).
+	 * MTI and EE support are mutually exclusive.
+	 */
+	MTIenabled = BAM_VERSION_MTI_SUPPORT(dev->version);
+
+	if ((dev->props.manage & SPS_BAM_MGR_DEVICE_REMOTE) != 0 &&
+			(dev->props.manage & SPS_BAM_MGR_MULTI_EE) != 0 &&
+			dev->props.ee == 0 && MTIenabled) {
+		/*
+		 * BAM global is owned by remote processor and local processor
+		 * must use MTI. Thus, force EE index to a non-zero value to
+		 * insure that EE zero globals can't be modified.
+		 */
+		SPS_ERR("sps:EE for satellite BAM must be set to non-zero.");
+		return SPS_ERROR;
+	}
+
+	/*
+	 * Enable MTI use (message triggered interrupt)
+	 * if local processor does not control the global BAM config
+	 * and this BAM supports MTIs.
+	 */
+	if ((dev->state & BAM_STATE_IRQ) != 0 &&
+		(dev->props.manage & SPS_BAM_MGR_DEVICE_REMOTE) != 0 &&
+		MTIenabled) {
+		if (dev->props.irq_gen_addr == 0 ||
+		    dev->props.irq_gen_addr == SPS_ADDR_INVALID) {
+			SPS_ERR("sps:MTI destination address not specified "
+				"for BAM 0x%x",	BAM_ID(dev));
+			return SPS_ERROR;
+		}
+		dev->state |= BAM_STATE_MTI;
+	}
+
+	if (num_pipes) {
+		dev->props.num_pipes = num_pipes;
+		SPS_DBG1("sps:BAM 0x%x number of pipes reported by hw: %d",
+				 BAM_ID(dev), dev->props.num_pipes);
+	}
+
+	/* Check EE index */
+	if (!MTIenabled && dev->props.ee >= SPS_BAM_NUM_EES) {
+		SPS_ERR("sps:Invalid EE BAM 0x%x: %d", BAM_ID(dev),
+				dev->props.ee);
+		return SPS_ERROR;
+	}
+
+	/*
+	 * Process EE configuration parameters,
+	 * if specified in the properties
+	 */
+	if (!MTIenabled && dev->props.sec_config == SPS_BAM_SEC_DO_CONFIG) {
+		struct sps_bam_sec_config_props *p_sec =
+						dev->props.p_sec_config_props;
+		if (p_sec == NULL) {
+			SPS_ERR("sps:EE config table is not specified for "
+				"BAM 0x%x", BAM_ID(dev));
+			return SPS_ERROR;
+		}
+
+		/*
+		 * Set restricted pipes based on the pipes assigned to local EE
+		 */
+		dev->props.restricted_pipes =
+					~p_sec->ees[dev->props.ee].pipe_mask;
+
+		/*
+		 * If local processor manages the BAM, perform the EE
+		 * configuration
+		 */
+		if ((dev->props.manage & SPS_BAM_MGR_DEVICE_REMOTE) == 0) {
+			u32 ee;
+			u32 pipe_mask;
+			int n, i;
+
+			/*
+			 * Verify that there are no overlapping pipe
+			 * assignments
+			 */
+			for (n = 0; n < SPS_BAM_NUM_EES - 1; n++) {
+				for (i = n + 1; i < SPS_BAM_NUM_EES; i++) {
+					if ((p_sec->ees[n].pipe_mask &
+						p_sec->ees[i].pipe_mask) != 0) {
+						SPS_ERR("sps:Overlapping pipe "
+							"assignments for BAM "
+							"0x%x: EEs %d and %d",
+							BAM_ID(dev), n, i);
+						return SPS_ERROR;
+					}
+				}
+			}
+
+			for (ee = 0; ee < SPS_BAM_NUM_EES; ee++) {
+				/*
+				 * MSbit specifies EE for the global (top-level)
+				 * BAM interrupt
+				 */
+				pipe_mask = p_sec->ees[ee].pipe_mask;
+				if (ee == dev->props.ee)
+					pipe_mask |= (1UL << 31);
+				else
+					pipe_mask &= ~(1UL << 31);
+
+				bam_security_init(dev->base, ee,
+						p_sec->ees[ee].vmid, pipe_mask);
+			}
+		}
+	}
+
+	/*
+	 * If local processor manages the BAM and the BAM supports MTIs
+	 * but does not support multiple EEs, set all restricted pipes
+	 * to MTI mode.
+	 */
+	if ((dev->props.manage & SPS_BAM_MGR_DEVICE_REMOTE) == 0
+			&& MTIenabled) {
+		u32 pipe_index;
+		u32 pipe_mask;
+		for (pipe_index = 0, pipe_mask = 1;
+		    pipe_index < dev->props.num_pipes;
+		    pipe_index++, pipe_mask <<= 1) {
+			if ((pipe_mask & dev->props.restricted_pipes) == 0)
+				continue;	/* This is a local pipe */
+
+			/*
+			 * Enable MTI with destination address of zero
+			 * (and source mask zero). Pipe is in reset,
+			 * so no interrupt will be generated.
+			 */
+			bam_pipe_satellite_mti(dev->base, pipe_index, 0,
+						       dev->props.ee);
+		}
+	}
+
+	dev->state |= BAM_STATE_ENABLED;
+	SPS_DBG2("sps:BAM 0x%x enabled: ver: %d, number of pipes: %d",
+		BAM_ID(dev), dev->version, dev->props.num_pipes);
+	return 0;
+}
+
+/**
+ * BAM device disable
+ *
+ */
+int sps_bam_disable(struct sps_bam *dev)
+{
+	if ((dev->state & BAM_STATE_ENABLED) == 0)
+		return 0;
+
+	/* Is there any access to this BAM? */
+	if ((dev->props.manage & SPS_BAM_MGR_ACCESS_MASK) == SPS_BAM_MGR_NONE) {
+		SPS_ERR("sps:No local access to BAM 0x%x", BAM_ID(dev));
+		return SPS_ERROR;
+	}
+
+	/* Is this BAM controlled by the local processor? */
+	if ((dev->props.manage & SPS_BAM_MGR_DEVICE_REMOTE)) {
+		/* No, so just mark it disabled */
+		dev->state &= ~BAM_STATE_ENABLED;
+		return 0;
+	}
+
+	/* Disable BAM (interrupts) */
+	if ((dev->state & BAM_STATE_IRQ)) {
+		bam_exit(dev->base, dev->props.ee);
+
+		/* Deregister BAM ISR */
+		if ((dev->state & BAM_STATE_IRQ))
+			if (dev->props.irq > 0)
+				free_irq(dev->props.irq, dev);
+		dev->state &= ~BAM_STATE_IRQ;
+	}
+
+	dev->state &= ~BAM_STATE_ENABLED;
+
+	SPS_DBG2("sps:BAM 0x%x disabled", BAM_ID(dev));
+
+	return 0;
+}
+
+/**
+ * BAM device initialization
+ */
+int sps_bam_device_init(struct sps_bam *dev)
+{
+	if (dev->props.virt_addr == NULL) {
+		SPS_ERR("sps:NULL BAM virtual address");
+		return SPS_ERROR;
+	}
+	dev->base = (void *) dev->props.virt_addr;
+
+	if (dev->props.num_pipes == 0) {
+		/* Assume max number of pipes until BAM registers can be read */
+		dev->props.num_pipes = BAM_MAX_PIPES;
+		SPS_DBG2("sps:BAM 0x%x: assuming max number of pipes: %d",
+			BAM_ID(dev), dev->props.num_pipes);
+	}
+
+	/* Init BAM state data */
+	dev->state = 0;
+	dev->pipe_active_mask = 0;
+	dev->pipe_remote_mask = 0;
+	INIT_LIST_HEAD(&dev->pipes_q);
+
+	spin_lock_init(&dev->isr_lock);
+
+	spin_lock_init(&dev->connection_lock);
+
+	if ((dev->props.options & SPS_BAM_OPT_ENABLE_AT_BOOT))
+		if (sps_bam_enable(dev)) {
+			SPS_ERR("sps:Fail to enable bam device");
+			return SPS_ERROR;
+		}
+
+	SPS_DBG2("sps:BAM device: phys 0x%x IRQ %d",
+			BAM_ID(dev), dev->props.irq);
+
+	return 0;
+}
+
+/**
+ * BAM device de-initialization
+ *
+ */
+int sps_bam_device_de_init(struct sps_bam *dev)
+{
+	int result;
+
+	SPS_DBG2("sps:BAM device DEINIT: phys 0x%x IRQ %d",
+		BAM_ID(dev), dev->props.irq);
+
+	result = sps_bam_disable(dev);
+
+	return result;
+}
+
+/**
+ * BAM device reset
+ *
+ */
+int sps_bam_reset(struct sps_bam *dev)
+{
+	struct sps_pipe *pipe;
+	u32 pipe_index;
+	int result;
+
+	SPS_DBG2("sps:BAM device RESET: phys 0x%x IRQ %d",
+		BAM_ID(dev), dev->props.irq);
+
+	/* If BAM is enabled, then disable */
+	result = 0;
+	if ((dev->state & BAM_STATE_ENABLED)) {
+		/* Verify that no pipes are currently allocated */
+		for (pipe_index = 0; pipe_index < dev->props.num_pipes;
+		      pipe_index++) {
+			pipe = dev->pipes[pipe_index];
+			if (BAM_PIPE_IS_ASSIGNED(pipe)) {
+				SPS_ERR("sps:BAM device 0x%x RESET failed: "
+					"pipe %d in use",
+					BAM_ID(dev), pipe_index);
+				result = SPS_ERROR;
+				break;
+			}
+		}
+
+		if (result == 0)
+			result = sps_bam_disable(dev);
+	}
+
+	/* BAM will be reset as part of the enable process */
+	if (result == 0)
+		result = sps_bam_enable(dev);
+
+	return result;
+}
+
+/**
+ * Clear the BAM pipe state struct
+ *
+ * This function clears the BAM pipe state struct.
+ *
+ * @pipe - pointer to client pipe struct
+ *
+ */
+static void pipe_clear(struct sps_pipe *pipe)
+{
+	INIT_LIST_HEAD(&pipe->list);
+
+	pipe->state = 0;
+	pipe->pipe_index = SPS_BAM_PIPE_INVALID;
+	pipe->pipe_index_mask = 0;
+	pipe->irq_mask = 0;
+	pipe->mode = -1;
+	pipe->num_descs = 0;
+	pipe->desc_size = 0;
+	memset(&pipe->sys, 0, sizeof(pipe->sys));
+	INIT_LIST_HEAD(&pipe->sys.events_q);
+}
+
+/**
+ * Allocate a BAM pipe
+ *
+ */
+u32 sps_bam_pipe_alloc(struct sps_bam *dev, u32 pipe_index)
+{
+	u32 pipe_mask;
+
+	if (pipe_index == SPS_BAM_PIPE_INVALID) {
+		/* Allocate a pipe from the BAM */
+		if ((dev->props.manage & SPS_BAM_MGR_PIPE_NO_ALLOC)) {
+			SPS_ERR("sps:Restricted from allocating pipes "
+				"on BAM 0x%x", BAM_ID(dev));
+			return SPS_BAM_PIPE_INVALID;
+		}
+		for (pipe_index = 0, pipe_mask = 1;
+		    pipe_index < dev->props.num_pipes;
+		    pipe_index++, pipe_mask <<= 1) {
+			if ((pipe_mask & dev->props.restricted_pipes))
+				continue;	/* This is a restricted pipe */
+
+			if (dev->pipes[pipe_index] == NULL)
+				break;	/* Found an available pipe */
+		}
+		if (pipe_index >= dev->props.num_pipes) {
+			SPS_ERR("sps:Fail to allocate pipe on BAM 0x%x",
+				BAM_ID(dev));
+			return SPS_BAM_PIPE_INVALID;
+		}
+	} else {
+		/* Check that client-specified pipe is available */
+		if (pipe_index >= dev->props.num_pipes) {
+			SPS_ERR("sps:Invalid pipe %d for allocate on BAM 0x%x",
+				pipe_index, BAM_ID(dev));
+			return SPS_BAM_PIPE_INVALID;
+		}
+		if ((dev->props.restricted_pipes & (1UL << pipe_index))) {
+			SPS_ERR("sps:BAM 0x%x pipe %d is not local",
+				BAM_ID(dev), pipe_index);
+			return SPS_BAM_PIPE_INVALID;
+		}
+		if (dev->pipes[pipe_index] != NULL) {
+			SPS_ERR("sps:Pipe %d already allocated on BAM 0x%x",
+				pipe_index, BAM_ID(dev));
+			return SPS_BAM_PIPE_INVALID;
+		}
+	}
+
+	/* Mark pipe as allocated */
+	dev->pipes[pipe_index] = BAM_PIPE_UNASSIGNED;
+
+	return pipe_index;
+}
+
+/**
+ * Free a BAM pipe
+ *
+ */
+void sps_bam_pipe_free(struct sps_bam *dev, u32 pipe_index)
+{
+	struct sps_pipe *pipe;
+
+	if (pipe_index >= dev->props.num_pipes) {
+		SPS_ERR("sps:Invalid BAM 0x%x pipe: %d", BAM_ID(dev),
+				pipe_index);
+		return;
+	}
+
+	/* Get the client pipe struct and mark the pipe free */
+	pipe = dev->pipes[pipe_index];
+	dev->pipes[pipe_index] = NULL;
+
+	/* Is the pipe currently allocated? */
+	if (pipe == NULL) {
+		SPS_ERR("sps:Attempt to free unallocated pipe %d on "
+			"BAM 0x%x", pipe_index, BAM_ID(dev));
+		return;
+	}
+
+	if (pipe == BAM_PIPE_UNASSIGNED)
+		return;		/* Never assigned, so no work to do */
+
+	/* Return pending items to appropriate pools */
+	if (!list_empty(&pipe->sys.events_q)) {
+		struct sps_q_event *sps_event;
+
+		SPS_ERR("sps:Disconnect BAM 0x%x pipe %d with events pending",
+			BAM_ID(dev), pipe_index);
+
+		sps_event = list_entry((&pipe->sys.events_q)->next,
+				typeof(*sps_event), list);
+
+		while (&sps_event->list != (&pipe->sys.events_q)) {
+			struct sps_q_event *sps_event_delete = sps_event;
+
+			list_del(&sps_event->list);
+			sps_event = list_entry(sps_event->list.next,
+					typeof(*sps_event), list);
+			kfree(sps_event_delete);
+		}
+	}
+
+	/* Clear the BAM pipe state struct */
+	pipe_clear(pipe);
+}
+
+/**
+ * Establish BAM pipe connection
+ *
+ */
+int sps_bam_pipe_connect(struct sps_pipe *bam_pipe,
+			 const struct sps_bam_connect_param *params)
+{
+	struct bam_pipe_parameters hw_params;
+	struct sps_bam *dev;
+	const struct sps_connection *map = bam_pipe->map;
+	const struct sps_conn_end_pt *map_pipe;
+	const struct sps_conn_end_pt *other_pipe;
+	void *desc_buf = NULL;
+	u32 pipe_index;
+	int result;
+
+	/* Clear the client pipe state and hw init struct */
+	pipe_clear(bam_pipe);
+	memset(&hw_params, 0, sizeof(hw_params));
+
+	/* Initialize the BAM state struct */
+	bam_pipe->mode = params->mode;
+
+	/* Set pipe streaming mode */
+	if ((params->options & SPS_O_STREAMING) == 0)
+		hw_params.stream_mode = BAM_STREAM_MODE_DISABLE;
+	else
+		hw_params.stream_mode = BAM_STREAM_MODE_ENABLE;
+
+	/* Determine which end point to connect */
+	if (bam_pipe->mode == SPS_MODE_SRC) {
+		map_pipe = &map->src;
+		other_pipe = &map->dest;
+		hw_params.dir = BAM_PIPE_PRODUCER;
+	} else {
+		map_pipe = &map->dest;
+		other_pipe = &map->src;
+		hw_params.dir = BAM_PIPE_CONSUMER;
+	}
+
+	/* Process map parameters */
+	dev = map_pipe->bam;
+	pipe_index = map_pipe->pipe_index;
+	if (pipe_index >= dev->props.num_pipes) {
+		SPS_ERR("sps:Invalid BAM 0x%x pipe: %d", BAM_ID(dev),
+				pipe_index);
+		return SPS_ERROR;
+	}
+	hw_params.event_threshold = (u16) map_pipe->event_threshold;
+	hw_params.ee = dev->props.ee;
+	hw_params.lock_group = map_pipe->lock_group;
+
+	/* Verify that control of this pipe is allowed */
+	if ((dev->props.manage & SPS_BAM_MGR_PIPE_NO_CTRL) ||
+	    (dev->props.restricted_pipes & (1UL << pipe_index))) {
+		SPS_ERR("sps:BAM 0x%x pipe %d is not local",
+			BAM_ID(dev), pipe_index);
+		return SPS_ERROR;
+	}
+
+	/* Control without configuration permission is not supported yet */
+	if ((dev->props.manage & SPS_BAM_MGR_PIPE_NO_CONFIG)) {
+		SPS_ERR("sps:BAM 0x%x pipe %d remote config is not supported",
+			BAM_ID(dev), pipe_index);
+		return SPS_ERROR;
+	}
+
+	/* Determine operational mode */
+	if (other_pipe->bam != NULL) {
+		/* BAM-to-BAM mode */
+		bam_pipe->state |= BAM_STATE_BAM2BAM;
+		hw_params.mode = BAM_PIPE_MODE_BAM2BAM;
+		hw_params.peer_phys_addr =
+			((struct sps_bam *) (other_pipe->bam))->props.phys_addr;
+		hw_params.peer_pipe = other_pipe->pipe_index;
+
+		/* Verify FIFO buffers are allocated for BAM-to-BAM pipes */
+		if (map->desc.phys_base == SPS_ADDR_INVALID ||
+		    map->data.phys_base == SPS_ADDR_INVALID ||
+		    map->desc.size == 0 || map->data.size == 0) {
+			SPS_ERR("sps:FIFO buffers are not allocated for BAM "
+				"0x%x pipe %d.", BAM_ID(dev), pipe_index);
+			return SPS_ERROR;
+		}
+		hw_params.data_base = map->data.phys_base;
+		hw_params.data_size = map->data.size;
+
+		/* Clear the data FIFO for debug */
+		if (map->data.base != NULL && bam_pipe->mode == SPS_MODE_SRC)
+			memset(map->data.base, 0, hw_params.data_size);
+
+		/* set NWD bit for BAM2BAM producer pipe */
+		if (bam_pipe->mode == SPS_MODE_SRC) {
+			if ((params->options & SPS_O_WRITE_NWD) == 0)
+				hw_params.write_nwd = BAM_WRITE_NWD_DISABLE;
+			else
+				hw_params.write_nwd = BAM_WRITE_NWD_ENABLE;
+		}
+	} else {
+		/* System mode */
+		hw_params.mode = BAM_PIPE_MODE_SYSTEM;
+		bam_pipe->sys.desc_buf = map->desc.base;
+		bam_pipe->sys.desc_offset = 0;
+		bam_pipe->sys.acked_offset = 0;
+	}
+
+	/* Initialize the client pipe state */
+	bam_pipe->pipe_index = pipe_index;
+	bam_pipe->pipe_index_mask = 1UL << pipe_index;
+
+	/* Get virtual address for descriptor FIFO */
+	if (map->desc.phys_base != SPS_ADDR_INVALID) {
+		if (map->desc.size < (2 * sizeof(struct sps_iovec))) {
+			SPS_ERR("sps:Invalid descriptor FIFO size "
+				"for BAM 0x%x pipe %d: %d",
+				BAM_ID(dev), pipe_index, map->desc.size);
+			return SPS_ERROR;
+		}
+		desc_buf = map->desc.base;
+
+		/*
+		 * Note that descriptor base and size will be left zero from
+		 * the memset() above if the physical address was invalid.
+		 * This allows a satellite driver to set the FIFO as
+		 * local memory	for system mode.
+		 */
+		hw_params.desc_base = map->desc.phys_base;
+		hw_params.desc_size = map->desc.size;
+	}
+
+	/* Configure the descriptor FIFO for both operational modes */
+	if (desc_buf != NULL)
+		if (bam_pipe->mode == SPS_MODE_SRC ||
+		    hw_params.mode == BAM_PIPE_MODE_SYSTEM)
+			memset(desc_buf, 0, hw_params.desc_size);
+
+	bam_pipe->desc_size = hw_params.desc_size;
+	bam_pipe->num_descs = bam_pipe->desc_size / sizeof(struct sps_iovec);
+
+	result = SPS_ERROR;
+	/* Insure that the BAM is enabled */
+	if ((dev->state & BAM_STATE_ENABLED) == 0)
+		if (sps_bam_enable(dev))
+			goto exit_init_err;
+
+	/* Check pipe allocation */
+	if (dev->pipes[pipe_index] != BAM_PIPE_UNASSIGNED) {
+		SPS_ERR("sps:Invalid pipe %d on BAM 0x%x for connect",
+			pipe_index, BAM_ID(dev));
+		return SPS_ERROR;
+	}
+
+	if (bam_pipe_is_enabled(dev->base, pipe_index)) {
+		SPS_ERR("sps:BAM 0x%x pipe %d sharing violation",
+			BAM_ID(dev), pipe_index);
+		return SPS_ERROR;
+	}
+
+	if (bam_pipe_init(dev->base, pipe_index, &hw_params, dev->props.ee)) {
+		SPS_ERR("sps:BAM 0x%x pipe %d init error",
+			BAM_ID(dev), pipe_index);
+		goto exit_err;
+	}
+
+	/* Assign pipe to client */
+	dev->pipes[pipe_index] = bam_pipe;
+
+	/* Process configuration parameters */
+	if (params->options != 0 ||
+	    (bam_pipe->state & BAM_STATE_BAM2BAM) == 0) {
+		/* Process init-time only parameters */
+		u32 irq_gen_addr;
+
+		/* Set interrupt mode */
+		irq_gen_addr = SPS_ADDR_INVALID;
+		if ((params->options & SPS_O_IRQ_MTI))
+			/* Client has directly specified the MTI address */
+			irq_gen_addr = params->irq_gen_addr;
+		else if ((dev->state & BAM_STATE_MTI))
+			/* This BAM has MTI use enabled */
+			irq_gen_addr = dev->props.irq_gen_addr;
+
+		if (irq_gen_addr != SPS_ADDR_INVALID) {
+			/*
+			 * No checks - assume BAM is already setup for
+			 * MTI generation,
+			 * or the pipe will be set to satellite control.
+			 */
+			bam_pipe->state |= BAM_STATE_MTI;
+			bam_pipe->irq_gen_addr = irq_gen_addr;
+		}
+
+		/* Process runtime parameters */
+		if (sps_bam_pipe_set_params(dev, pipe_index,
+					  params->options)) {
+			dev->pipes[pipe_index] = BAM_PIPE_UNASSIGNED;
+			goto exit_err;
+		}
+	}
+
+	/* Indicate initialization is complete */
+	dev->pipes[pipe_index] = bam_pipe;
+	dev->pipe_active_mask |= 1UL << pipe_index;
+	list_add_tail(&bam_pipe->list, &dev->pipes_q);
+
+	bam_pipe->state |= BAM_STATE_INIT;
+	result = 0;
+exit_err:
+	if (result)
+		bam_pipe_exit(dev->base, pipe_index, dev->props.ee);
+exit_init_err:
+	if (result) {
+		/* Clear the client pipe state */
+		pipe_clear(bam_pipe);
+	}
+
+	return result;
+}
+
+/**
+ * Disconnect a BAM pipe connection
+ *
+ */
+int sps_bam_pipe_disconnect(struct sps_bam *dev, u32 pipe_index)
+{
+	struct sps_pipe *pipe;
+	int result;
+
+	if (pipe_index >= dev->props.num_pipes) {
+		SPS_ERR("sps:Invalid BAM 0x%x pipe: %d", BAM_ID(dev),
+				pipe_index);
+		return SPS_ERROR;
+	}
+
+	/* Deallocate and reset the BAM pipe */
+	pipe = dev->pipes[pipe_index];
+	if (BAM_PIPE_IS_ASSIGNED(pipe)) {
+		if ((dev->pipe_active_mask & (1UL << pipe_index))) {
+			list_del(&pipe->list);
+			dev->pipe_active_mask &= ~(1UL << pipe_index);
+		}
+		dev->pipe_remote_mask &= ~(1UL << pipe_index);
+		bam_pipe_exit(dev->base, pipe_index, dev->props.ee);
+		if (pipe->sys.desc_cache != NULL) {
+			u32 size = pipe->num_descs * sizeof(void *);
+			if (pipe->desc_size + size <= PAGE_SIZE)
+				kfree(pipe->sys.desc_cache);
+			else
+				vfree(pipe->sys.desc_cache);
+			pipe->sys.desc_cache = NULL;
+		}
+		dev->pipes[pipe_index] = BAM_PIPE_UNASSIGNED;
+		pipe_clear(pipe);
+		result = 0;
+	} else {
+		result = SPS_ERROR;
+	}
+
+	if (result)
+		SPS_ERR("sps:BAM 0x%x pipe %d already disconnected",
+			BAM_ID(dev), pipe_index);
+
+	return result;
+}
+
+/**
+ * Set BAM pipe interrupt enable state
+ *
+ * This function sets the interrupt enable state for a BAM pipe.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @poll - true if SPS_O_POLL is set, false otherwise
+ *
+ */
+static void pipe_set_irq(struct sps_bam *dev, u32 pipe_index,
+				 u32 poll)
+{
+	struct sps_pipe *pipe = dev->pipes[pipe_index];
+	enum bam_enable irq_enable;
+
+	if (poll == 0 && pipe->irq_mask != 0 &&
+	    (dev->state & BAM_STATE_IRQ)) {
+		if ((pipe->state & BAM_STATE_BAM2BAM) != 0 &&
+		    (pipe->state & BAM_STATE_IRQ) == 0) {
+			/*
+			 * If enabling the interrupt for a BAM-to-BAM pipe,
+			 * clear the existing interrupt status
+			 */
+			(void)bam_pipe_get_and_clear_irq_status(dev->base,
+							   pipe_index);
+		}
+		pipe->state |= BAM_STATE_IRQ;
+		irq_enable = BAM_ENABLE;
+		pipe->polled = false;
+	} else {
+		pipe->state &= ~BAM_STATE_IRQ;
+		irq_enable = BAM_DISABLE;
+		pipe->polled = true;
+		if (poll == 0 && pipe->irq_mask)
+			SPS_DBG2("sps:BAM 0x%x pipe %d forced to use polling",
+				 BAM_ID(dev), pipe_index);
+	}
+	if ((pipe->state & BAM_STATE_MTI) == 0)
+		bam_pipe_set_irq(dev->base, pipe_index, irq_enable,
+					 pipe->irq_mask, dev->props.ee);
+	else
+		bam_pipe_set_mti(dev->base, pipe_index, irq_enable,
+					 pipe->irq_mask, pipe->irq_gen_addr);
+
+}
+
+/**
+ * Set BAM pipe parameters
+ *
+ */
+int sps_bam_pipe_set_params(struct sps_bam *dev, u32 pipe_index, u32 options)
+{
+	struct sps_pipe *pipe = dev->pipes[pipe_index];
+	u32 mask;
+	int wake_up_is_one_shot;
+	int no_queue;
+	int ack_xfers;
+	u32 size;
+	int n;
+
+	/* Capture some options */
+	wake_up_is_one_shot = ((options & SPS_O_WAKEUP_IS_ONESHOT));
+	no_queue = ((options & SPS_O_NO_Q));
+	ack_xfers = ((options & SPS_O_ACK_TRANSFERS));
+
+	/* Create interrupt source mask */
+	mask = 0;
+	for (n = 0; n < ARRAY_SIZE(opt_event_table); n++) {
+		/* Is client registering for this event? */
+		if ((options & opt_event_table[n].option) == 0)
+			continue;	/* No */
+
+		mask |= opt_event_table[n].pipe_irq;
+	}
+
+#ifdef SPS_BAM_STATISTICS
+	/* Is an illegal mode change specified? */
+	if (pipe->sys.desc_wr_count > 0 &&
+	    (no_queue != pipe->sys.no_queue
+	     || ack_xfers != pipe->sys.ack_xfers)) {
+		SPS_ERR("sps:Queue/ack mode change after transfer: "
+			"BAM 0x%x pipe %d opt 0x%x",
+			BAM_ID(dev), pipe_index, options);
+		return SPS_ERROR;
+	}
+#endif /* SPS_BAM_STATISTICS */
+
+	/* Is client setting invalid options for a BAM-to-BAM connection? */
+	if ((pipe->state & BAM_STATE_BAM2BAM) &&
+	    (options & BAM2BAM_O_INVALID)) {
+		SPS_ERR("sps:Invalid option for BAM-to-BAM: BAM 0x%x pipe %d "
+			"opt 0x%x", BAM_ID(dev), pipe_index, options);
+		return SPS_ERROR;
+	}
+
+	/* Allocate descriptor FIFO cache if NO_Q option is disabled */
+	if (!no_queue && pipe->sys.desc_cache == NULL && pipe->num_descs > 0
+	    && (pipe->state & BAM_STATE_BAM2BAM) == 0) {
+		/* Allocate both descriptor cache and user pointer array */
+		size = pipe->num_descs * sizeof(void *);
+
+		if (pipe->desc_size + size <= PAGE_SIZE)
+			pipe->sys.desc_cache =
+				kzalloc(pipe->desc_size + size, GFP_KERNEL);
+		else {
+			pipe->sys.desc_cache =
+				vmalloc(pipe->desc_size + size);
+			memset(pipe->sys.desc_cache, 0, pipe->desc_size + size);
+		}
+
+		if (pipe->sys.desc_cache == NULL) {
+			/*** MUST BE LAST POINT OF FAILURE (see below) *****/
+			SPS_ERR("sps:Desc cache error: BAM 0x%x pipe %d: %d",
+				BAM_ID(dev), pipe_index,
+				pipe->desc_size + size);
+			return SPS_ERROR;
+		}
+		pipe->sys.user_ptrs = (void **)(pipe->sys.desc_cache +
+						 pipe->desc_size);
+		pipe->sys.cache_offset = pipe->sys.acked_offset;
+	}
+
+	/*
+	 * No failures beyond this point. Note that malloc() is last point of
+	 * failure, so no free() handling is needed.
+	 */
+
+	/* Enable/disable the pipe's interrupt sources */
+	pipe->irq_mask = mask;
+	pipe_set_irq(dev, pipe_index, (options & SPS_O_POLL));
+
+	/* Store software feature enables */
+	pipe->wake_up_is_one_shot = wake_up_is_one_shot;
+	pipe->sys.no_queue = no_queue;
+	pipe->sys.ack_xfers = ack_xfers;
+
+	return 0;
+}
+
+/**
+ * Enable a BAM pipe
+ *
+ */
+int sps_bam_pipe_enable(struct sps_bam *dev, u32 pipe_index)
+{
+	struct sps_pipe *pipe = dev->pipes[pipe_index];
+
+	/* Enable the BAM pipe */
+	bam_pipe_enable(dev->base, pipe_index);
+	pipe->state |= BAM_STATE_ENABLED;
+
+	return 0;
+}
+
+/**
+ * Disable a BAM pipe
+ *
+ */
+int sps_bam_pipe_disable(struct sps_bam *dev, u32 pipe_index)
+{
+	struct sps_pipe *pipe = dev->pipes[pipe_index];
+
+	/* Disable the BAM pipe */
+	bam_pipe_disable(dev->base, pipe_index);
+	pipe->state &= ~BAM_STATE_ENABLED;
+
+	return 0;
+}
+
+/**
+ * Register an event for a BAM pipe
+ *
+ */
+int sps_bam_pipe_reg_event(struct sps_bam *dev,
+			   u32 pipe_index,
+			   struct sps_register_event *reg)
+{
+	struct sps_pipe *pipe = dev->pipes[pipe_index];
+	struct sps_bam_event_reg *event_reg;
+	int n;
+
+	if (pipe->sys.no_queue && reg->xfer_done != NULL &&
+	    reg->mode != SPS_TRIGGER_CALLBACK) {
+		SPS_ERR("sps:Only callback events support for NO_Q: "
+			"BAM 0x%x pipe %d mode %d",
+			BAM_ID(dev), pipe_index, reg->mode);
+		return SPS_ERROR;
+	}
+
+	for (n = 0; n < ARRAY_SIZE(opt_event_table); n++) {
+		int index;
+
+		/* Is client registering for this event? */
+		if ((reg->options & opt_event_table[n].option) == 0)
+			continue;	/* No */
+
+		index = SPS_EVENT_INDEX(opt_event_table[n].event_id);
+		if (index < 0)
+			SPS_ERR("sps:Negative event index: "
+			"BAM 0x%x pipe %d mode %d",
+			BAM_ID(dev), pipe_index, reg->mode);
+		else {
+			event_reg = &pipe->sys.event_regs[index];
+			event_reg->xfer_done = reg->xfer_done;
+			event_reg->callback = reg->callback;
+			event_reg->mode = reg->mode;
+			event_reg->user = reg->user;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * Submit a transfer of a single buffer to a BAM pipe
+ *
+ */
+int sps_bam_pipe_transfer_one(struct sps_bam *dev,
+				    u32 pipe_index, u32 addr, u32 size,
+				    void *user, u32 flags)
+{
+	struct sps_pipe *pipe = dev->pipes[pipe_index];
+	struct sps_iovec *desc;
+	struct sps_iovec iovec;
+	u32 next_write;
+
+	/* Is this a BAM-to-BAM or satellite connection? */
+	if ((pipe->state & (BAM_STATE_BAM2BAM | BAM_STATE_REMOTE))) {
+		SPS_ERR("sps:Transfer on BAM-to-BAM: BAM 0x%x pipe %d",
+			BAM_ID(dev), pipe_index);
+		return SPS_ERROR;
+	}
+
+	/*
+	 * Client identifier (user pointer) is not supported for
+	 * SPS_O_NO_Q option.
+	 */
+	if (pipe->sys.no_queue && user != NULL) {
+		SPS_ERR("sps:User pointer arg non-NULL: BAM 0x%x pipe %d",
+			BAM_ID(dev), pipe_index);
+		return SPS_ERROR;
+	}
+
+	/* Determine if descriptor can be queued */
+	next_write = pipe->sys.desc_offset + sizeof(struct sps_iovec);
+	if (next_write >= pipe->desc_size)
+		next_write = 0;
+
+	if (next_write == pipe->sys.acked_offset) {
+		/*
+		 * If pipe is polled and client is not ACK'ing descriptors,
+		 * perform polling operation so that any outstanding ACKs
+		 * can occur.
+		 */
+		if (!pipe->sys.ack_xfers && pipe->polled) {
+			pipe_handler_eot(dev, pipe);
+			if (next_write == pipe->sys.acked_offset) {
+				SPS_DBG2("sps:Descriptor FIFO is full for BAM "
+					"0x%x pipe %d after pipe_handler_eot",
+					BAM_ID(dev), pipe_index);
+				return SPS_ERROR;
+			}
+		} else {
+			SPS_DBG2("sps:Descriptor FIFO is full for "
+				"BAM 0x%x pipe %d", BAM_ID(dev), pipe_index);
+			return SPS_ERROR;
+		}
+	}
+
+	/* Create descriptor */
+	if (!pipe->sys.no_queue)
+		desc = (struct sps_iovec *) (pipe->sys.desc_cache +
+					      pipe->sys.desc_offset);
+	else
+		desc = &iovec;
+
+	desc->addr = addr;
+	desc->size = size;
+	if ((flags & SPS_IOVEC_FLAG_DEFAULT) == 0) {
+		desc->flags = flags & BAM_IOVEC_FLAG_MASK;
+	} else {
+		if (pipe->mode == SPS_MODE_SRC)
+			desc->flags = SPS_IOVEC_FLAG_INT;
+		else
+			desc->flags = SPS_IOVEC_FLAG_INT | SPS_IOVEC_FLAG_EOT;
+	}
+#ifdef SPS_BAM_STATISTICS
+	if ((flags & SPS_IOVEC_FLAG_INT))
+		pipe->sys.int_flags++;
+	if ((flags & SPS_IOVEC_FLAG_EOT))
+		pipe->sys.eot_flags++;
+#endif /* SPS_BAM_STATISTICS */
+
+	/* Update hardware descriptor FIFO - should result in burst */
+	*((struct sps_iovec *) (pipe->sys.desc_buf + pipe->sys.desc_offset))
+	= *desc;
+
+	/* Record user pointer value */
+	if (!pipe->sys.no_queue) {
+		u32 index = pipe->sys.desc_offset / sizeof(struct sps_iovec);
+		pipe->sys.user_ptrs[index] = user;
+#ifdef SPS_BAM_STATISTICS
+		if (user != NULL)
+			pipe->sys.user_ptrs_count++;
+#endif /* SPS_BAM_STATISTICS */
+	}
+
+	/* Update descriptor ACK offset */
+	pipe->sys.desc_offset = next_write;
+
+#ifdef SPS_BAM_STATISTICS
+	/* Update statistics */
+	pipe->sys.desc_wr_count++;
+#endif /* SPS_BAM_STATISTICS */
+
+	/* Notify pipe */
+	if ((flags & SPS_IOVEC_FLAG_NO_SUBMIT) == 0) {
+		wmb(); /* Memory Barrier */
+		bam_pipe_set_desc_write_offset(dev->base, pipe_index,
+					       next_write);
+	}
+
+	return 0;
+}
+
+/**
+ * Submit a transfer to a BAM pipe
+ *
+ */
+int sps_bam_pipe_transfer(struct sps_bam *dev,
+			 u32 pipe_index, struct sps_transfer *transfer)
+{
+	struct sps_iovec *iovec;
+	u32 count;
+	u32 flags;
+	void *user;
+	int n;
+	int result;
+
+	if (transfer->iovec_count == 0) {
+		SPS_ERR("sps:iovec count zero: BAM 0x%x pipe %d",
+			BAM_ID(dev), pipe_index);
+		return SPS_ERROR;
+	}
+
+	sps_bam_get_free_count(dev, pipe_index, &count);
+	if (count < transfer->iovec_count) {
+		SPS_ERR("sps:Insufficient free desc: BAM 0x%x pipe %d: %d",
+			BAM_ID(dev), pipe_index, count);
+		return SPS_ERROR;
+	}
+
+	user = NULL;		/* NULL for all except last descriptor */
+	for (n = (int)transfer->iovec_count - 1, iovec = transfer->iovec;
+	    n >= 0; n--, iovec++) {
+		if (n > 0) {
+			/* This is *not* the last descriptor */
+			flags = iovec->flags | SPS_IOVEC_FLAG_NO_SUBMIT;
+		} else {
+			/* This *is* the last descriptor */
+			flags = iovec->flags;
+			user = transfer->user;
+		}
+		result = sps_bam_pipe_transfer_one(dev, pipe_index,
+						 iovec->addr,
+						 iovec->size, user,
+						 flags);
+		if (result)
+			return SPS_ERROR;
+	}
+
+	return 0;
+}
+
+/**
+ * Allocate an event tracking struct
+ *
+ * This function allocates an event tracking struct.
+ *
+ * @pipe - pointer to pipe state
+ *
+ * @event_reg - pointer to event registration
+ *
+ * @return - pointer to event notification struct, or NULL
+ *
+ */
+static struct sps_q_event *alloc_event(struct sps_pipe *pipe,
+					struct sps_bam_event_reg *event_reg)
+{
+	struct sps_q_event *event;
+
+	/* A callback event object is registered, so trigger with payload */
+	event = &pipe->sys.event;
+	memset(event, 0, sizeof(*event));
+
+	return event;
+}
+
+/**
+ * Trigger an event notification
+ *
+ * This function triggers an event notification.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe - pointer to pipe state
+ *
+ * @event_reg - pointer to event registration
+ *
+ * @sps_event - pointer to event struct
+ *
+ */
+static void trigger_event(struct sps_bam *dev,
+			  struct sps_pipe *pipe,
+			  struct sps_bam_event_reg *event_reg,
+			  struct sps_q_event *sps_event)
+{
+	if (sps_event == NULL) {
+		SPS_DBG("sps:trigger_event.sps_event is NULL.");
+		return;
+	}
+
+	if (event_reg->xfer_done) {
+		complete(event_reg->xfer_done);
+		SPS_DBG("sps:trigger_event.done=%d.",
+			event_reg->xfer_done->done);
+	}
+
+	if (event_reg->callback) {
+		event_reg->callback(&sps_event->notify);
+		SPS_DBG("sps:trigger_event.using callback.");
+	}
+
+}
+
+/**
+ * Handle a BAM pipe's generic interrupt sources
+ *
+ * This function creates the event notification for a BAM pipe's
+ *    generic interrupt sources.  The caller of this function must lock the BAM
+ *    device's mutex.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe - pointer to pipe state
+ *
+ * @event_id - event identifier enum
+ *
+ */
+static void pipe_handler_generic(struct sps_bam *dev,
+			       struct sps_pipe *pipe,
+			       enum sps_event event_id)
+{
+	struct sps_bam_event_reg *event_reg;
+	struct sps_q_event *sps_event;
+	int index;
+
+	index = SPS_EVENT_INDEX(event_id);
+	if (index < 0 || index >= SPS_EVENT_INDEX(SPS_EVENT_MAX))
+		return;
+
+	event_reg = &pipe->sys.event_regs[index];
+	sps_event = alloc_event(pipe, event_reg);
+	if (sps_event != NULL) {
+		sps_event->notify.event_id = event_id;
+		sps_event->notify.user = event_reg->user;
+		trigger_event(dev, pipe, event_reg, sps_event);
+	}
+}
+
+/**
+ * Handle a BAM pipe's WAKEUP interrupt sources
+ *
+ * This function creates the event notification for a BAM pipe's
+ *    WAKEUP interrupt source.  The caller of this function must lock the BAM
+ *    device's mutex.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe - pointer to pipe state
+ *
+ */
+static void pipe_handler_wakeup(struct sps_bam *dev, struct sps_pipe *pipe)
+{
+	struct sps_bam_event_reg *event_reg;
+	struct sps_q_event *event;
+	u32 pipe_index = pipe->pipe_index;
+
+	if (pipe->wake_up_is_one_shot) {
+		/* Disable the pipe WAKEUP interrupt source */
+		pipe->irq_mask &= ~BAM_PIPE_IRQ_WAKE;
+		pipe_set_irq(dev, pipe_index, pipe->polled);
+	}
+
+	event_reg = &pipe->sys.event_regs[SPS_EVENT_INDEX(SPS_EVENT_WAKEUP)];
+	event = alloc_event(pipe, event_reg);
+	if (event != NULL) {
+		event->notify.event_id = SPS_EVENT_WAKEUP;
+		event->notify.user = event_reg->user;
+		trigger_event(dev, pipe, event_reg, event);
+	}
+}
+
+/**
+ * Handle a BAM pipe's EOT/INT interrupt sources
+ *
+ * This function creates the event notification for a BAM pipe's EOT interrupt
+ *    source.  The caller of this function must lock the BAM device's mutex.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe - pointer to pipe state
+ *
+ */
+static void pipe_handler_eot(struct sps_bam *dev, struct sps_pipe *pipe)
+{
+	struct sps_bam_event_reg *event_reg;
+	struct sps_q_event *event;
+	struct sps_iovec *desc;
+	struct sps_iovec *cache;
+	void **user;
+	u32 *update_offset;
+	u32 pipe_index = pipe->pipe_index;
+	u32 offset;
+	u32 end_offset;
+	enum sps_event event_id;
+	u32 flags;
+	u32 enabled;
+	int producer = (pipe->mode == SPS_MODE_SRC);
+
+	if (pipe->sys.handler_eot)
+		/*
+		 * This can happen if the pipe is configured for polling
+		 * (IRQ disabled) and callback event generation.
+		 * The client may perform a get_iovec() inside the callback.
+		 */
+		return;
+
+	pipe->sys.handler_eot = true;
+
+	/* Get offset of last descriptor completed by the pipe */
+	end_offset = bam_pipe_get_desc_read_offset(dev->base, pipe_index);
+
+	/* If no queue, then do not generate any events */
+	if (pipe->sys.no_queue) {
+		if (!pipe->sys.ack_xfers) {
+			/* Client is not ACK'ing transfers, so do it now */
+			pipe->sys.acked_offset = end_offset;
+		}
+		pipe->sys.handler_eot = false;
+		return;
+	}
+
+	/*
+	 * Get offset of last descriptor processed by software,
+	 * and update to the last descriptor completed by the pipe
+	 */
+	if (!pipe->sys.ack_xfers) {
+		update_offset = &pipe->sys.acked_offset;
+		offset = *update_offset;
+	} else {
+		update_offset = &pipe->sys.cache_offset;
+		offset = *update_offset;
+	}
+
+	/* Are there any completed descriptors to process? */
+	if (offset == end_offset) {
+		pipe->sys.handler_eot = false;
+		return;
+	}
+
+	/* Determine enabled events */
+	enabled = 0;
+	if ((pipe->irq_mask & SPS_O_EOT))
+		enabled |= SPS_IOVEC_FLAG_EOT;
+
+	if ((pipe->irq_mask & SPS_O_DESC_DONE))
+		enabled |= SPS_IOVEC_FLAG_INT;
+
+	/*
+	 * For producer pipe, update the cached descriptor byte count and flags.
+	 * For consumer pipe, the BAM does not update the descriptors, so just
+	 * use the cached copies.
+	 */
+	if (producer) {
+		/*
+		 * Do copies in a tight loop to increase chance of
+		 * multi-descriptor burst accesses on the bus
+		 */
+		struct sps_iovec *desc_end;
+
+		/* Set starting point for copy */
+		desc = (struct sps_iovec *) (pipe->sys.desc_buf + offset);
+		cache =	(struct sps_iovec *) (pipe->sys.desc_cache + offset);
+
+		/* Fetch all completed descriptors to end of FIFO (wrap) */
+		if (end_offset < offset) {
+			desc_end = (struct sps_iovec *)
+				   (pipe->sys.desc_buf + pipe->desc_size);
+			while (desc < desc_end)
+				*cache++ = *desc++;
+
+			desc = (void *)pipe->sys.desc_buf;
+			cache = (void *)pipe->sys.desc_cache;
+		}
+
+		/* Fetch all remaining completed descriptors (no wrap) */
+		desc_end = (struct sps_iovec *)	(pipe->sys.desc_buf +
+						 end_offset);
+		while (desc < desc_end)
+			*cache++ = *desc++;
+	}
+
+	/* Process all completed descriptors */
+	cache = (struct sps_iovec *) (pipe->sys.desc_cache + offset);
+	user = &pipe->sys.user_ptrs[offset / sizeof(struct sps_iovec)];
+	for (;;) {
+		/*
+		 * Increment offset to next descriptor and update pipe offset
+		 * so a client callback can fetch the I/O vector.
+		 */
+		offset += sizeof(struct sps_iovec);
+		if (offset >= pipe->desc_size)
+			/* Roll to start of descriptor FIFO */
+			offset = 0;
+
+		*update_offset = offset;
+#ifdef SPS_BAM_STATISTICS
+		pipe->sys.desc_rd_count++;
+#endif /* SPS_BAM_STATISTICS */
+
+		/* Did client request notification for this descriptor? */
+		flags = cache->flags & enabled;
+		if (*user != NULL || flags) {
+			int index;
+
+			if ((flags & SPS_IOVEC_FLAG_EOT))
+				event_id = SPS_EVENT_EOT;
+			else
+				event_id = SPS_EVENT_DESC_DONE;
+
+			index = SPS_EVENT_INDEX(event_id);
+			event_reg = &pipe->sys.event_regs[index];
+			event = alloc_event(pipe, event_reg);
+			if (event != NULL) {
+				/*
+				 * Store the descriptor and user pointer
+				 * in the notification
+				 */
+				event->notify.data.transfer.iovec = *cache;
+				event->notify.data.transfer.user = *user;
+
+				event->notify.event_id = event_id;
+				event->notify.user = event_reg->user;
+				trigger_event(dev, pipe, event_reg, event);
+			}
+#ifdef SPS_BAM_STATISTICS
+			if (*user != NULL)
+				pipe->sys.user_found++;
+#endif /* SPS_BAM_STATISTICS */
+		}
+
+		/* Increment to next descriptor */
+		if (offset == end_offset)
+			break;	/* No more descriptors */
+
+		if (offset) {
+			cache++;
+			user++;
+		} else {
+			cache = (void *)pipe->sys.desc_cache;
+			user = pipe->sys.user_ptrs;
+		}
+	}
+
+	pipe->sys.handler_eot = false;
+}
+
+/**
+ * Handle a BAM pipe's interrupt sources
+ *
+ * This function handles a BAM pipe's interrupt sources.
+ *    The caller of this function must lock the BAM device's mutex.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @return void
+ *
+ */
+static void pipe_handler(struct sps_bam *dev, struct sps_pipe *pipe)
+{
+	u32 pipe_index;
+	u32 status;
+	enum sps_event event_id;
+
+	/* Get interrupt sources and ack all */
+	pipe_index = pipe->pipe_index;
+	status = bam_pipe_get_and_clear_irq_status(dev->base, pipe_index);
+
+	SPS_DBG("sps:pipe_handler.bam 0x%x.pipe %d.status=0x%x.",
+			BAM_ID(dev), pipe_index, status);
+
+	/* Check for enabled interrupt sources */
+	status &= pipe->irq_mask;
+	if (status == 0)
+		/* No enabled interrupt sources are active */
+		return;
+
+	/*
+	 * Process the interrupt sources in order of frequency of occurrance.
+	 * Check for early exit opportunities.
+	 */
+
+	if ((status & (SPS_O_EOT | SPS_O_DESC_DONE)) &&
+	    (pipe->state & BAM_STATE_BAM2BAM) == 0) {
+		pipe_handler_eot(dev, pipe);
+		if (pipe->sys.no_queue) {
+			/*
+			 * EOT handler will not generate any event if there
+			 * is no queue,
+			 * so generate "empty" (no descriptor) event
+			 */
+			if ((status & SPS_O_EOT))
+				event_id = SPS_EVENT_EOT;
+			else
+				event_id = SPS_EVENT_DESC_DONE;
+
+			pipe_handler_generic(dev, pipe, event_id);
+		}
+		status &= ~(SPS_O_EOT | SPS_O_DESC_DONE);
+		if (status == 0)
+			return;
+	}
+
+	if ((status & SPS_O_WAKEUP)) {
+		pipe_handler_wakeup(dev, pipe);
+		status &= ~SPS_O_WAKEUP;
+		if (status == 0)
+			return;
+	}
+
+	if ((status & SPS_O_INACTIVE)) {
+		pipe_handler_generic(dev, pipe, SPS_EVENT_INACTIVE);
+		status &= ~SPS_O_INACTIVE;
+		if (status == 0)
+			return;
+	}
+
+	if ((status & SPS_O_OUT_OF_DESC)) {
+		pipe_handler_generic(dev, pipe,
+					     SPS_EVENT_OUT_OF_DESC);
+		status &= ~SPS_O_OUT_OF_DESC;
+		if (status == 0)
+			return;
+	}
+
+	if ((status & SPS_EVENT_ERROR))
+		pipe_handler_generic(dev, pipe, SPS_EVENT_ERROR);
+}
+
+/**
+ * Get a BAM pipe event
+ *
+ */
+int sps_bam_pipe_get_event(struct sps_bam *dev,
+			   u32 pipe_index, struct sps_event_notify *notify)
+{
+	struct sps_pipe *pipe = dev->pipes[pipe_index];
+	struct sps_q_event *event_queue;
+
+	if (pipe->sys.no_queue) {
+		SPS_ERR("sps:Invalid connection for event: "
+			"BAM 0x%x pipe %d context 0x%x",
+			BAM_ID(dev), pipe_index, (u32) pipe);
+		notify->event_id = SPS_EVENT_INVALID;
+		return SPS_ERROR;
+	}
+
+	/* If pipe is polled, perform polling operation */
+	if (pipe->polled && (pipe->state & BAM_STATE_BAM2BAM) == 0)
+		pipe_handler_eot(dev, pipe);
+
+	/* Pull an event off the synchronous event queue */
+	if (list_empty(&pipe->sys.events_q)) {
+		event_queue = NULL;
+		SPS_DBG("sps:events_q of bam 0x%x is empty.", BAM_ID(dev));
+	} else {
+		SPS_DBG("sps:events_q of bam 0x%x is not empty.", BAM_ID(dev));
+		event_queue =
+		list_first_entry(&pipe->sys.events_q, struct sps_q_event,
+				 list);
+		list_del(&event_queue->list);
+	}
+
+	/* Update client's event buffer */
+	if (event_queue == NULL) {
+		/* No event queued, so set client's event to "invalid" */
+		notify->event_id = SPS_EVENT_INVALID;
+	} else {
+		/*
+		 * Copy event into client's buffer and return the event
+		 * to the pool
+		 */
+		*notify = event_queue->notify;
+		kfree(event_queue);
+#ifdef SPS_BAM_STATISTICS
+		pipe->sys.get_events++;
+#endif /* SPS_BAM_STATISTICS */
+	}
+
+	return 0;
+}
+
+/**
+ * Get processed I/O vector
+ */
+int sps_bam_pipe_get_iovec(struct sps_bam *dev, u32 pipe_index,
+			   struct sps_iovec *iovec)
+{
+	struct sps_pipe *pipe = dev->pipes[pipe_index];
+	struct sps_iovec *desc;
+	u32 read_offset;
+
+	/* Is this a valid pipe configured for get_iovec use? */
+	if (!pipe->sys.ack_xfers ||
+	    (pipe->state & BAM_STATE_BAM2BAM) != 0 ||
+	    (pipe->state & BAM_STATE_REMOTE)) {
+		return SPS_ERROR;
+	}
+
+	/* If pipe is polled and queue is enabled, perform polling operation */
+	if (pipe->polled && !pipe->sys.no_queue)
+		pipe_handler_eot(dev, pipe);
+
+	/* Is there a completed descriptor? */
+	if (pipe->sys.no_queue)
+		read_offset =
+		bam_pipe_get_desc_read_offset(dev->base, pipe_index);
+	else
+		read_offset = pipe->sys.cache_offset;
+
+	if (read_offset == pipe->sys.acked_offset) {
+		/* No, so clear the iovec to indicate FIFO is empty */
+		memset(iovec, 0, sizeof(*iovec));
+		return 0;
+	}
+
+	/* Fetch next descriptor */
+	desc = (struct sps_iovec *) (pipe->sys.desc_buf +
+				     pipe->sys.acked_offset);
+	*iovec = *desc;
+#ifdef SPS_BAM_STATISTICS
+	pipe->sys.get_iovecs++;
+#endif /* SPS_BAM_STATISTICS */
+
+	/* Update read/ACK offset */
+	pipe->sys.acked_offset += sizeof(struct sps_iovec);
+	if (pipe->sys.acked_offset >= pipe->desc_size)
+		pipe->sys.acked_offset = 0;
+
+	return 0;
+}
+
+/**
+ * Determine whether a BAM pipe descriptor FIFO is empty
+ *
+ */
+int sps_bam_pipe_is_empty(struct sps_bam *dev, u32 pipe_index,
+				u32 *empty)
+{
+	struct sps_pipe *pipe = dev->pipes[pipe_index];
+	u32 end_offset;
+	u32 acked_offset;
+
+	/* Is this a satellite connection? */
+	if ((pipe->state & BAM_STATE_REMOTE)) {
+		SPS_ERR("sps:Is empty on remote: BAM 0x%x pipe %d",
+			BAM_ID(dev), pipe_index);
+		return SPS_ERROR;
+	}
+
+	/* Get offset of last descriptor completed by the pipe */
+	end_offset = bam_pipe_get_desc_read_offset(dev->base, pipe_index);
+
+	if ((pipe->state & BAM_STATE_BAM2BAM) == 0)
+		/* System mode */
+		acked_offset = pipe->sys.acked_offset;
+	else
+		/* BAM-to-BAM */
+		acked_offset = bam_pipe_get_desc_write_offset(dev->base,
+							  pipe_index);
+
+
+	/* Determine descriptor FIFO state */
+	if (end_offset == acked_offset)
+		*empty = true;
+	else
+		*empty = false;
+
+	return 0;
+}
+
+/**
+ * Get number of free slots in a BAM pipe descriptor FIFO
+ *
+ */
+int sps_bam_get_free_count(struct sps_bam *dev, u32 pipe_index,
+				 u32 *count)
+{
+	struct sps_pipe *pipe = dev->pipes[pipe_index];
+	u32 next_write;
+	u32 free;
+
+	/* Is this a BAM-to-BAM or satellite connection? */
+	if ((pipe->state & (BAM_STATE_BAM2BAM | BAM_STATE_REMOTE))) {
+		SPS_ERR("sps:Free count on BAM-to-BAM or remote: BAM "
+			"0x%x pipe %d",	BAM_ID(dev), pipe_index);
+		*count = 0;
+		return SPS_ERROR;
+	}
+
+	/* Determine descriptor FIFO state */
+	next_write = pipe->sys.desc_offset + sizeof(struct sps_iovec);
+	if (next_write >= pipe->desc_size)
+		next_write = 0;
+
+	if (pipe->sys.acked_offset >= next_write)
+		free = pipe->sys.acked_offset - next_write;
+	else
+		free = pipe->desc_size - next_write + pipe->sys.acked_offset;
+
+	free /= sizeof(struct sps_iovec);
+	*count = free;
+
+	return 0;
+}
+
+/**
+ * Set BAM pipe to satellite ownership
+ *
+ */
+int sps_bam_set_satellite(struct sps_bam *dev, u32 pipe_index)
+{
+	struct sps_pipe *pipe = dev->pipes[pipe_index];
+
+	/*
+	 * Switch to satellite control is only supported on processor
+	 * that controls the BAM global config on multi-EE BAMs
+	 */
+	if ((dev->props.manage & SPS_BAM_MGR_MULTI_EE) == 0 ||
+	    (dev->props.manage & SPS_BAM_MGR_DEVICE_REMOTE)) {
+		SPS_ERR("sps:Cannot grant satellite control to BAM 0x%x "
+			"pipe %d", BAM_ID(dev), pipe_index);
+		return SPS_ERROR;
+	}
+
+	/* Is this pipe locally controlled? */
+	if ((dev->pipe_active_mask & (1UL << pipe_index)) == 0) {
+		SPS_ERR("sps:BAM 0x%x pipe %d not local and active",
+			BAM_ID(dev), pipe_index);
+		return SPS_ERROR;
+	}
+
+	/* Disable local interrupts for this pipe */
+	if (!pipe->polled)
+		bam_pipe_set_irq(dev->base, pipe_index, BAM_DISABLE,
+					 pipe->irq_mask, dev->props.ee);
+
+	if (BAM_VERSION_MTI_SUPPORT(dev->version)) {
+		/*
+		 * Set pipe to MTI interrupt mode.
+		 * Must be performed after IRQ disable,
+		 * because it is necessary to re-enable the IRQ to enable
+		 * MTI generation.
+		 * Set both pipe IRQ mask and MTI dest address to zero.
+		 */
+		if ((pipe->state & BAM_STATE_MTI) == 0 || pipe->polled) {
+			bam_pipe_satellite_mti(dev->base, pipe_index, 0,
+						       dev->props.ee);
+			pipe->state |= BAM_STATE_MTI;
+		}
+	}
+
+	/* Indicate satellite control */
+	list_del(&pipe->list);
+	dev->pipe_active_mask &= ~(1UL << pipe_index);
+	dev->pipe_remote_mask |= pipe->pipe_index_mask;
+	pipe->state |= BAM_STATE_REMOTE;
+
+	return 0;
+}
+
+/**
+ * Perform BAM pipe timer control
+ *
+ */
+int sps_bam_pipe_timer_ctrl(struct sps_bam *dev,
+			    u32 pipe_index,
+			    struct sps_timer_ctrl *timer_ctrl,
+			    struct sps_timer_result *timer_result)
+{
+	enum bam_pipe_timer_mode mode;
+	int result = 0;
+
+	/* Is this pipe locally controlled? */
+	if ((dev->pipe_active_mask & (1UL << pipe_index)) == 0) {
+		SPS_ERR("sps:BAM 0x%x pipe %d not local and active",
+			BAM_ID(dev), pipe_index);
+		return SPS_ERROR;
+	}
+
+	/* Perform the timer operation */
+	switch (timer_ctrl->op) {
+	case SPS_TIMER_OP_CONFIG:
+		mode = (timer_ctrl->mode == SPS_TIMER_MODE_ONESHOT) ?
+			BAM_PIPE_TIMER_ONESHOT :
+			BAM_PIPE_TIMER_PERIODIC;
+		bam_pipe_timer_config(dev->base, pipe_index, mode,
+				    timer_ctrl->timeout_msec * 10);
+		break;
+	case SPS_TIMER_OP_RESET:
+		bam_pipe_timer_reset(dev->base, pipe_index);
+		break;
+	case SPS_TIMER_OP_READ:
+		break;
+	default:
+		result = SPS_ERROR;
+		break;
+	}
+
+	/* Provide the current timer value */
+	if (timer_result != NULL)
+		timer_result->current_timer =
+			bam_pipe_timer_get_count(dev->base, pipe_index);
+
+	return result;
+}
+
+/**
+ * Get the number of unused descriptors in the descriptor FIFO
+ * of a pipe
+ */
+int sps_bam_pipe_get_unused_desc_num(struct sps_bam *dev, u32 pipe_index,
+					u32 *desc_num)
+{
+	u32 sw_offset, peer_offset, fifo_size;
+	u32 desc_size = sizeof(struct sps_iovec);
+	struct sps_pipe *pipe = dev->pipes[pipe_index];
+
+	if (pipe == NULL)
+		return SPS_ERROR;
+
+	fifo_size = pipe->desc_size;
+
+	sw_offset = bam_pipe_get_desc_read_offset(dev->base, pipe_index);
+	peer_offset = bam_pipe_get_desc_write_offset(dev->base, pipe_index);
+
+	if (sw_offset <= peer_offset)
+		*desc_num = (peer_offset - sw_offset) / desc_size;
+	else
+		*desc_num = (peer_offset + fifo_size - sw_offset) / desc_size;
+
+	return 0;
+}
diff --git a/drivers/platform/msm/sps/sps_bam.h b/drivers/platform/msm/sps/sps_bam.h
new file mode 100644
index 0000000..6004b75
--- /dev/null
+++ b/drivers/platform/msm/sps/sps_bam.h
@@ -0,0 +1,564 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Function and data structure declarations for SPS BAM handling.
+ */
+
+
+#ifndef _SPSBAM_H_
+#define _SPSBAM_H_
+
+#include <linux/types.h>
+#include <linux/completion.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+
+#include "spsi.h"
+
+#define BAM_HANDLE_INVALID         0
+
+enum bam_irq {
+	BAM_DEV_IRQ_RDY_TO_SLEEP = 0x00000001,
+	BAM_DEV_IRQ_HRESP_ERROR = 0x00000002,
+	BAM_DEV_IRQ_ERROR = 0x00000004,
+};
+
+/* Pipe interrupt mask */
+enum bam_pipe_irq {
+	/* BAM finishes descriptor which has INT bit selected */
+	BAM_PIPE_IRQ_DESC_INT = 0x00000001,
+	/* Inactivity timer Expires */
+	BAM_PIPE_IRQ_TIMER = 0x00000002,
+	/* Wakeup peripheral (i.e. USB) */
+	BAM_PIPE_IRQ_WAKE = 0x00000004,
+	/* Producer - no free space for adding a descriptor */
+	/* Consumer - no descriptors for processing */
+	BAM_PIPE_IRQ_OUT_OF_DESC = 0x00000008,
+	/* Pipe Error interrupt */
+	BAM_PIPE_IRQ_ERROR = 0x00000010,
+	/* End-Of-Transfer */
+	BAM_PIPE_IRQ_EOT = 0x00000020,
+};
+
+/* Halt Type */
+enum bam_halt {
+	BAM_HALT_OFF = 0,
+	BAM_HALT_ON = 1,
+};
+
+/* Threshold values of the DMA channels */
+enum bam_dma_thresh_dma {
+	BAM_DMA_THRESH_512 = 0x3,
+	BAM_DMA_THRESH_256 = 0x2,
+	BAM_DMA_THRESH_128 = 0x1,
+	BAM_DMA_THRESH_64 = 0x0,
+};
+
+/* Weight values of the DMA channels */
+enum bam_dma_weight_dma {
+	BAM_DMA_WEIGHT_HIGH = 7,
+	BAM_DMA_WEIGHT_MED = 3,
+	BAM_DMA_WEIGHT_LOW = 1,
+	BAM_DMA_WEIGHT_DEFAULT = BAM_DMA_WEIGHT_LOW,
+	BAM_DMA_WEIGHT_DISABLE = 0,
+};
+
+
+/* Invalid pipe index value */
+#define SPS_BAM_PIPE_INVALID  ((u32)(-1))
+
+/* Parameters for sps_bam_pipe_connect() */
+struct sps_bam_connect_param {
+	/* which end point must be initialized */
+	enum sps_mode mode;
+
+	/* OR'd connection end point options (see SPS_O defines) */
+	u32 options;
+
+	/* SETPEND/MTI interrupt generation parameters */
+	u32 irq_gen_addr;
+	u32 irq_gen_data;
+
+};
+
+/* Event registration struct */
+struct sps_bam_event_reg {
+	/* Client's event object handle */
+	struct completion *xfer_done;
+	void (*callback)(struct sps_event_notify *notify);
+
+	/* Event trigger mode */
+	enum sps_trigger mode;
+
+	/* User pointer that will be provided in event payload data */
+	void *user;
+
+};
+
+/* Descriptor FIFO cache entry */
+struct sps_bam_desc_cache {
+	struct sps_iovec iovec;
+	void *user; /* User pointer registered with this transfer */
+};
+
+/* Forward declaration */
+struct sps_bam;
+
+/* System mode control */
+struct sps_bam_sys_mode {
+	/* Descriptor FIFO control */
+	u8 *desc_buf; /* Descriptor FIFO for BAM pipe */
+	u32 desc_offset; /* Next new descriptor to be written to hardware */
+	u32 acked_offset; /* Next descriptor to be retired by software */
+
+	/* Descriptor cache control (!no_queue only) */
+	u8 *desc_cache; /* Software cache of descriptor FIFO contents */
+	u32 cache_offset; /* Next descriptor to be cached (ack_xfers only) */
+
+	/* User pointers associated with cached descriptors */
+	void **user_ptrs;
+
+	/* Event handling */
+	struct sps_bam_event_reg event_regs[SPS_EVENT_INDEX(SPS_EVENT_MAX)];
+	struct list_head events_q;
+
+	struct sps_q_event event;	/* Temp storage for event creation */
+	int no_queue;	/* Whether events are queued */
+	int ack_xfers;	/* Whether client must ACK all descriptors */
+	int handler_eot; /* Whether EOT handling is in progress (debug) */
+
+	/* Statistics */
+#ifdef SPS_BAM_STATISTICS
+	u32 desc_wr_count;
+	u32 desc_rd_count;
+	u32 user_ptrs_count;
+	u32 user_found;
+	u32 int_flags;
+	u32 eot_flags;
+	u32 callback_events;
+	u32 wait_events;
+	u32 queued_events;
+	u32 get_events;
+	u32 get_iovecs;
+#endif /* SPS_BAM_STATISTICS */
+};
+
+/* BAM pipe descriptor */
+struct sps_pipe {
+	struct list_head list;
+
+	/* Client state */
+	u32 client_state;
+	struct sps_bam *bam;
+	struct sps_connect connect;
+	const struct sps_connection *map;
+
+	/* Pipe parameters */
+	u32 state;
+	u32 pipe_index;
+	u32 pipe_index_mask;
+	u32 irq_mask;
+	int polled;
+	u32 irq_gen_addr;
+	enum sps_mode mode;
+	u32 num_descs; /* Size (number of elements) of descriptor FIFO */
+	u32 desc_size; /* Size (bytes) of descriptor FIFO */
+	int wake_up_is_one_shot; /* Whether WAKEUP event is a one-shot or not */
+
+	/* System mode control */
+	struct sps_bam_sys_mode sys;
+
+};
+
+/* BAM device descriptor */
+struct sps_bam {
+	struct list_head list;
+
+	/* BAM device properties, including connection defaults */
+	struct sps_bam_props props;
+
+	/* BAM device state */
+	u32 state;
+	struct mutex lock;
+	void *base; /* BAM virtual base address */
+	u32 version;
+	spinlock_t isr_lock;
+	spinlock_t connection_lock;
+	unsigned long irqsave_flags;
+
+	/* Pipe state */
+	u32 pipe_active_mask;
+	u32 pipe_remote_mask;
+	struct sps_pipe *pipes[BAM_MAX_PIPES];
+	struct list_head pipes_q;
+
+	/* Statistics */
+	u32 irq_from_disabled_pipe;
+	u32 event_trigger_failures;
+
+};
+
+/**
+ * BAM driver initialization
+ *
+ * This function initializes the BAM driver.
+ *
+ * @options - driver options bitflags (see SPS_OPT_*)
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_driver_init(u32 options);
+
+/**
+ * BAM device initialization
+ *
+ * This function initializes a BAM device.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_device_init(struct sps_bam *dev);
+
+/**
+ * BAM device de-initialization
+ *
+ * This function de-initializes a BAM device.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_device_de_init(struct sps_bam *dev);
+
+/**
+ * BAM device reset
+ *
+ * This Function resets a BAM device.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_reset(struct sps_bam *dev);
+
+/**
+ * BAM device enable
+ *
+ * This function enables a BAM device.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_enable(struct sps_bam *dev);
+
+/**
+ * BAM device disable
+ *
+ * This Function disables a BAM device.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_disable(struct sps_bam *dev);
+
+/**
+ * Allocate a BAM pipe
+ *
+ * This function allocates a BAM pipe.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - client-specified pipe index, or SPS_BAM_PIPE_INVALID if
+ *    any available pipe is acceptable
+ *
+ * @return - allocated pipe index, or SPS_BAM_PIPE_INVALID on error
+ *
+ */
+u32 sps_bam_pipe_alloc(struct sps_bam *dev, u32 pipe_index);
+
+/**
+ * Free a BAM pipe
+ *
+ * This function frees a BAM pipe.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ */
+void sps_bam_pipe_free(struct sps_bam *dev, u32 pipe_index);
+
+/**
+ * Establish BAM pipe connection
+ *
+ * This function establishes a connection for a BAM pipe (end point).
+ *
+ * @client - pointer to client pipe state struct
+ *
+ * @params - connection parameters
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_pipe_connect(struct sps_pipe *client,
+			const struct sps_bam_connect_param *params);
+
+/**
+ * Disconnect a BAM pipe connection
+ *
+ * This function disconnects a connection for a BAM pipe (end point).
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_pipe_disconnect(struct sps_bam *dev, u32 pipe_index);
+
+/**
+ * Set BAM pipe parameters
+ *
+ * This function sets parameters for a BAM pipe.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @options - bitflag options (see SPS_O_*)
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_pipe_set_params(struct sps_bam *dev, u32 pipe_index, u32 options);
+
+/**
+ * Enable a BAM pipe
+ *
+ * This function enables a BAM pipe.  Note that this function
+ *    is separate from the pipe connect function to allow proper
+ *    sequencing of consumer enable followed by producer enable.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_pipe_enable(struct sps_bam *dev, u32 pipe_index);
+
+/**
+ * Disable a BAM pipe
+ *
+ * This function disables a BAM pipe.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_pipe_disable(struct sps_bam *dev, u32 pipe_index);
+
+/**
+ * Register an event for a BAM pipe
+ *
+ * This function registers an event for a BAM pipe.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @reg - pointer to event registration struct
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_pipe_reg_event(struct sps_bam *dev, u32 pipe_index,
+			   struct sps_register_event *reg);
+
+/**
+ * Submit a transfer of a single buffer to a BAM pipe
+ *
+ * This function submits a transfer of a single buffer to a BAM pipe.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @addr - physical address of buffer to transfer
+ *
+ * @size - number of bytes to transfer
+ *
+ * @user - user pointer to register for event
+ *
+ * @flags - descriptor flags (see SPS_IOVEC_FLAG defines)
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_pipe_transfer_one(struct sps_bam *dev, u32 pipe_index, u32 addr,
+			      u32 size, void *user, u32 flags);
+
+/**
+ * Submit a transfer to a BAM pipe
+ *
+ * This function submits a transfer to a BAM pipe.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @transfer - pointer to transfer struct
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_pipe_transfer(struct sps_bam *dev, u32 pipe_index,
+			 struct sps_transfer *transfer);
+
+/**
+ * Get a BAM pipe event
+ *
+ * This function polls for a BAM pipe event.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @notify - pointer to event notification struct
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_pipe_get_event(struct sps_bam *dev, u32 pipe_index,
+			   struct sps_event_notify *notify);
+
+/**
+ * Get processed I/O vector
+ *
+ * This function fetches the next processed I/O vector.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @iovec - Pointer to I/O vector struct (output).
+ *   This struct will be zeroed if there are no more processed I/O vectors.
+ *
+ * @return 0 on success, negative value on error
+ */
+int sps_bam_pipe_get_iovec(struct sps_bam *dev, u32 pipe_index,
+			   struct sps_iovec *iovec);
+
+/**
+ * Determine whether a BAM pipe descriptor FIFO is empty
+ *
+ * This function returns the empty state of a BAM pipe descriptor FIFO.
+ *
+ * The pipe mutex must be locked before calling this function.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @empty - pointer to client's empty status word (boolean)
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_pipe_is_empty(struct sps_bam *dev, u32 pipe_index, u32 *empty);
+
+/**
+ * Get number of free slots in a BAM pipe descriptor FIFO
+ *
+ * This function returns the number of free slots in a BAM pipe descriptor FIFO.
+ *
+ * The pipe mutex must be locked before calling this function.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @count - pointer to count status
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_get_free_count(struct sps_bam *dev, u32 pipe_index, u32 *count);
+
+/**
+ * Set BAM pipe to satellite ownership
+ *
+ * This function sets the BAM pipe to satellite ownership.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_set_satellite(struct sps_bam *dev, u32 pipe_index);
+
+/**
+ * Perform BAM pipe timer control
+ *
+ * This function performs BAM pipe timer control operations.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @timer_ctrl - Pointer to timer control specification
+ *
+ * @timer_result - Pointer to buffer for timer operation result.
+ *    This argument can be NULL if no result is expected for the operation.
+ *    If non-NULL, the current timer value will always provided.
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_pipe_timer_ctrl(struct sps_bam *dev, u32 pipe_index,
+			    struct sps_timer_ctrl *timer_ctrl,
+			    struct sps_timer_result *timer_result);
+
+
+/**
+ * Get the number of unused descriptors in the descriptor FIFO
+ * of a pipe
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @desc_num - number of unused descriptors
+ *
+ */
+int sps_bam_pipe_get_unused_desc_num(struct sps_bam *dev, u32 pipe_index,
+					u32 *desc_num);
+
+#endif	/* _SPSBAM_H_ */
diff --git a/drivers/platform/msm/sps/sps_core.h b/drivers/platform/msm/sps/sps_core.h
new file mode 100644
index 0000000..5bd7c65
--- /dev/null
+++ b/drivers/platform/msm/sps/sps_core.h
@@ -0,0 +1,107 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Function and data structure declarations.
+ */
+
+#ifndef _SPS_CORE_H_
+#define _SPS_CORE_H_
+
+#include <linux/types.h>	/* u32 */
+#include <linux/mutex.h>	/* mutex */
+#include <linux/list.h>		/* list_head */
+
+#include "spsi.h"
+#include "sps_bam.h"
+
+/* Connection state definitions */
+#define SPS_STATE_DEF(x)   ('S' | ('P' << 8) | ('S' << 16) | ((x) << 24))
+#define IS_SPS_STATE_OK(x) \
+	(((x)->client_state & 0x00ffffff) == SPS_STATE_DEF(0))
+
+/* Configuration indicating satellite connection */
+#define SPS_CONFIG_SATELLITE  0x11111111
+
+/* Client connection state */
+#define SPS_STATE_DISCONNECT  0
+#define SPS_STATE_ALLOCATE    SPS_STATE_DEF(1)
+#define SPS_STATE_CONNECT     SPS_STATE_DEF(2)
+#define SPS_STATE_ENABLE      SPS_STATE_DEF(3)
+#define SPS_STATE_DISABLE     SPS_STATE_DEF(4)
+
+/* Connection mapping control struct */
+struct sps_rm {
+	struct list_head connections_q;
+	struct mutex lock;
+};
+
+/**
+ * Find the BAM device from the handle
+ *
+ * This function finds a BAM device in the BAM registration list that
+ * matches the specified device handle.
+ *
+ * @h - device handle of the BAM
+ *
+ * @return - pointer to the BAM device struct, or NULL on error
+ *
+ */
+struct sps_bam *sps_h2bam(u32 h);
+
+/**
+ * Initialize resource manager module
+ *
+ * This function initializes the resource manager module.
+ *
+ * @rm - pointer to resource manager struct
+ *
+ * @options - driver options bitflags (see SPS_OPT_*)
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_rm_init(struct sps_rm *rm, u32 options);
+
+/**
+ * De-initialize resource manager module
+ *
+ * This function de-initializes the resource manager module.
+ *
+ */
+void sps_rm_de_init(void);
+
+/**
+ * Initialize client state context
+ *
+ * This function initializes a client state context struct.
+ *
+ * @connect - pointer to client connection state struct
+ *
+ */
+void sps_rm_config_init(struct sps_connect *connect);
+
+/**
+ * Process connection state change
+ *
+ * This function processes a connection state change.
+ *
+ * @pipe - pointer to pipe context
+ *
+ * @state - new state for connection
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_rm_state_change(struct sps_pipe *pipe, u32 state);
+
+#endif				/* _SPS_CORE_H_ */
diff --git a/drivers/platform/msm/sps/sps_dma.c b/drivers/platform/msm/sps/sps_dma.c
new file mode 100644
index 0000000..f8b4f51d
--- /dev/null
+++ b/drivers/platform/msm/sps/sps_dma.c
@@ -0,0 +1,917 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/* BAM-DMA Manager. */
+
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+
+#include <linux/export.h>
+#include <linux/memory.h>	/* memset */
+
+#include "spsi.h"
+#include "bam.h"
+#include "sps_bam.h"		/* bam_dma_thresh_dma */
+#include "sps_core.h"		/* sps_h2bam() */
+
+/**
+ * registers
+ */
+
+#define DMA_ENBL			(0x00000000)
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+#define DMA_REVISION			(0x00000004)
+#define DMA_CONFIG			(0x00000008)
+#define DMA_CHNL_CONFIG(n)		(0x00001000 + 4096 * (n))
+#else
+#define DMA_CHNL_CONFIG(n)		(0x00000004 + 4 * (n))
+#define DMA_CONFIG			(0x00000040)
+#endif
+
+/**
+ * masks
+ */
+
+/* DMA_CHNL_confign */
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+#define DMA_CHNL_PRODUCER_PIPE_ENABLED	0x40000
+#define DMA_CHNL_CONSUMER_PIPE_ENABLED	0x20000
+#endif
+#define DMA_CHNL_HALT_DONE		0x10000
+#define DMA_CHNL_HALT			0x1000
+#define DMA_CHNL_ENABLE                 0x100
+#define DMA_CHNL_ACT_THRESH             0x30
+#define DMA_CHNL_WEIGHT                 0x7
+
+/* DMA_CONFIG */
+#define TESTBUS_SELECT                  0x3
+
+/**
+ *
+ * Write register with debug info.
+ *
+ * @base - bam base virtual address.
+ * @offset - register offset.
+ * @val - value to write.
+ *
+ */
+static inline void dma_write_reg(void *base, u32 offset, u32 val)
+{
+	iowrite32(val, base + offset);
+	SPS_DBG("sps:bamdma: write reg 0x%x w_val 0x%x.", offset, val);
+}
+
+/**
+ * Write register masked field with debug info.
+ *
+ * @base - bam base virtual address.
+ * @offset - register offset.
+ * @mask - register bitmask.
+ * @val - value to write.
+ *
+ */
+static inline void dma_write_reg_field(void *base, u32 offset,
+				       const u32 mask, u32 val)
+{
+	u32 shift = find_first_bit((void *)&mask, 32);
+	u32 tmp = ioread32(base + offset);
+
+	tmp &= ~mask;		/* clear written bits */
+	val = tmp | (val << shift);
+	iowrite32(val, base + offset);
+	SPS_DBG("sps:bamdma: write reg 0x%x w_val 0x%x.", offset, val);
+}
+
+/* Round max number of pipes to nearest multiple of 2 */
+#define DMA_MAX_PIPES         ((BAM_MAX_PIPES / 2) * 2)
+
+/* Maximum number of BAM-DMAs supported */
+#define MAX_BAM_DMA_DEVICES   1
+
+/* Maximum number of BAMs that will be registered */
+#define MAX_BAM_DMA_BAMS      1
+
+/* Pipe enable check values */
+#define DMA_PIPES_STATE_DIFF     0
+#define DMA_PIPES_BOTH_DISABLED  1
+#define DMA_PIPES_BOTH_ENABLED   2
+
+/* Even pipe is tx/dest/input/write, odd pipe is rx/src/output/read */
+#define DMA_PIPE_IS_DEST(p)   (((p) & 1) == 0)
+#define DMA_PIPE_IS_SRC(p)    (((p) & 1) != 0)
+
+/* BAM DMA pipe state */
+enum bamdma_pipe_state {
+	PIPE_INACTIVE = 0,
+	PIPE_ACTIVE
+};
+
+/* BAM DMA channel state */
+enum bamdma_chan_state {
+	DMA_CHAN_STATE_FREE = 0,
+	DMA_CHAN_STATE_ALLOC_EXT,	/* Client allocation */
+	DMA_CHAN_STATE_ALLOC_INT	/* Internal (resource mgr) allocation */
+};
+
+struct bamdma_chan {
+	/* Allocation state */
+	enum bamdma_chan_state state;
+
+	/* BAM DMA channel configuration parameters */
+	u32 threshold;
+	enum sps_dma_priority priority;
+
+	/* HWIO channel configuration parameters */
+	enum bam_dma_thresh_dma thresh;
+	enum bam_dma_weight_dma weight;
+
+};
+
+/* BAM DMA device state */
+struct bamdma_device {
+	/* BAM-DMA device state */
+	int enabled;
+	int local;
+
+	/* BAM device state */
+	struct sps_bam *bam;
+
+	/* BAM handle, for deregistration */
+	u32 h;
+
+	/* BAM DMA device virtual mapping */
+	void *virt_addr;
+	int virtual_mapped;
+	u32 phys_addr;
+	void *hwio;
+
+	/* BAM DMA pipe/channel state */
+	u32 num_pipes;
+	enum bamdma_pipe_state pipes[DMA_MAX_PIPES];
+	struct bamdma_chan chans[DMA_MAX_PIPES / 2];
+
+};
+
+/* BAM-DMA devices */
+static struct bamdma_device bam_dma_dev[MAX_BAM_DMA_DEVICES];
+static struct mutex bam_dma_lock;
+
+/*
+ * The BAM DMA module registers all BAMs in the BSP properties, but only
+ * uses the first BAM-DMA device for allocations.  References to the others
+ * are stored in the following data array.
+ */
+static int num_bams;
+static u32 bam_handles[MAX_BAM_DMA_BAMS];
+
+/**
+ * Find BAM-DMA device
+ *
+ * This function finds the BAM-DMA device associated with the BAM handle.
+ *
+ * @h - BAM handle
+ *
+ * @return - pointer to BAM-DMA device, or NULL on error
+ *
+ */
+static struct bamdma_device *sps_dma_find_device(u32 h)
+{
+	return &bam_dma_dev[0];
+}
+
+/**
+ * BAM DMA device enable
+ *
+ * This function enables a BAM DMA device and the associated BAM.
+ *
+ * @dev - pointer to BAM DMA device context
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+static int sps_dma_device_enable(struct bamdma_device *dev)
+{
+	if (dev->enabled)
+		return 0;
+
+	/*
+	 *  If the BAM-DMA device is locally controlled then enable BAM-DMA
+	 *  device
+	 */
+	if (dev->local)
+		dma_write_reg(dev->virt_addr, DMA_ENBL, 1);
+
+	/* Enable BAM device */
+	if (sps_bam_enable(dev->bam)) {
+		SPS_ERR("sps:Failed to enable BAM DMA's BAM: %x",
+			dev->phys_addr);
+		return SPS_ERROR;
+	}
+
+	dev->enabled = true;
+
+	return 0;
+}
+
+/**
+ * BAM DMA device enable
+ *
+ * This function initializes a BAM DMA device.
+ *
+ * @dev - pointer to BAM DMA device context
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+static int sps_dma_device_disable(struct bamdma_device *dev)
+{
+	u32 pipe_index;
+
+	if (!dev->enabled)
+		return 0;
+
+	/* Do not disable if channels active */
+	for (pipe_index = 0; pipe_index < dev->num_pipes; pipe_index++) {
+		if (dev->pipes[pipe_index] != PIPE_INACTIVE)
+			break;
+	}
+
+	if (pipe_index < dev->num_pipes) {
+		SPS_ERR("sps:Fail to disable BAM-DMA %x:channels are active",
+			dev->phys_addr);
+		return SPS_ERROR;
+	}
+
+	dev->enabled = false;
+
+	/* Disable BAM device */
+	if (sps_bam_disable(dev->bam)) {
+		SPS_ERR("sps:Fail to disable BAM-DMA BAM:%x", dev->phys_addr);
+		return SPS_ERROR;
+	}
+
+	/* Is the BAM-DMA device locally controlled? */
+	if (dev->local)
+		/* Disable BAM-DMA device */
+		dma_write_reg(dev->virt_addr, DMA_ENBL, 0);
+
+	return 0;
+}
+
+/**
+ * Initialize BAM DMA device
+ *
+ */
+int sps_dma_device_init(u32 h)
+{
+	struct bamdma_device *dev;
+	struct sps_bam_props *props;
+	u32 chan;
+	int result = SPS_ERROR;
+
+	mutex_lock(&bam_dma_lock);
+
+	/* Find a free BAM-DMA device slot */
+	dev = NULL;
+	if (bam_dma_dev[0].bam != NULL) {
+		SPS_ERR("sps:BAM-DMA BAM device is already initialized.");
+		goto exit_err;
+	} else {
+		dev = &bam_dma_dev[0];
+	}
+
+	/* Record BAM */
+	memset(dev, 0, sizeof(*dev));
+	dev->h = h;
+	dev->bam = sps_h2bam(h);
+
+	if (dev->bam == NULL) {
+		SPS_ERR("sps:BAM-DMA BAM device is not found "
+				"from the handle.");
+		goto exit_err;
+	}
+
+	/* Map the BAM DMA device into virtual space, if necessary */
+	props = &dev->bam->props;
+	dev->phys_addr = props->periph_phys_addr;
+	if (props->periph_virt_addr != NULL) {
+		dev->virt_addr = props->periph_virt_addr;
+		dev->virtual_mapped = false;
+	} else {
+		if (props->periph_virt_size == 0) {
+			SPS_ERR("sps:Unable to map BAM DMA IO memory: %x %x",
+			 dev->phys_addr, props->periph_virt_size);
+			goto exit_err;
+		}
+
+		dev->virt_addr = ioremap(dev->phys_addr,
+					  props->periph_virt_size);
+		if (dev->virt_addr == NULL) {
+			SPS_ERR("sps:Unable to map BAM DMA IO memory: %x %x",
+				dev->phys_addr, props->periph_virt_size);
+			goto exit_err;
+		}
+		dev->virtual_mapped = true;
+	}
+	dev->hwio = (void *) dev->virt_addr;
+
+	/* Is the BAM-DMA device locally controlled? */
+	if ((props->manage & SPS_BAM_MGR_DEVICE_REMOTE) == 0) {
+		SPS_DBG2("sps:BAM-DMA is controlled locally: %x",
+			dev->phys_addr);
+		dev->local = true;
+	} else {
+		SPS_DBG2("sps:BAM-DMA is controlled remotely: %x",
+			dev->phys_addr);
+		dev->local = false;
+	}
+
+	/*
+	 * Enable the BAM DMA and determine the number of pipes/channels.
+	 * Leave the BAM-DMA enabled, since it is always a shared device.
+	 */
+	if (sps_dma_device_enable(dev))
+		goto exit_err;
+
+	dev->num_pipes = dev->bam->props.num_pipes;
+
+	/* Disable all channels */
+	if (dev->local)
+		for (chan = 0; chan < (dev->num_pipes / 2); chan++) {
+			dma_write_reg_field(dev->virt_addr,
+					    DMA_CHNL_CONFIG(chan),
+					    DMA_CHNL_ENABLE, 0);
+		}
+
+	result = 0;
+exit_err:
+	if (result) {
+		if (dev != NULL) {
+			if (dev->virtual_mapped)
+				iounmap(dev->virt_addr);
+
+			dev->bam = NULL;
+		}
+	}
+
+	mutex_unlock(&bam_dma_lock);
+
+	return result;
+}
+
+/**
+ * De-initialize BAM DMA device
+ *
+ */
+int sps_dma_device_de_init(u32 h)
+{
+	struct bamdma_device *dev;
+	u32 pipe_index;
+	u32 chan;
+	int result = 0;
+
+	mutex_lock(&bam_dma_lock);
+
+	dev = sps_dma_find_device(h);
+	if (dev == NULL) {
+		SPS_ERR("sps:BAM-DMA: not registered: %x", h);
+		result = SPS_ERROR;
+		goto exit_err;
+	}
+
+	/* Check for channel leaks */
+	for (chan = 0; chan < dev->num_pipes / 2; chan++) {
+		if (dev->chans[chan].state != DMA_CHAN_STATE_FREE) {
+			SPS_ERR("sps:BAM-DMA: channel not free: %d", chan);
+			result = SPS_ERROR;
+			dev->chans[chan].state = DMA_CHAN_STATE_FREE;
+		}
+	}
+	for (pipe_index = 0; pipe_index < dev->num_pipes; pipe_index++) {
+		if (dev->pipes[pipe_index] != PIPE_INACTIVE) {
+			SPS_ERR("sps:BAM-DMA: pipe not inactive: %d",
+					pipe_index);
+			result = SPS_ERROR;
+			dev->pipes[pipe_index] = PIPE_INACTIVE;
+		}
+	}
+
+	/* Disable BAM and BAM-DMA */
+	if (sps_dma_device_disable(dev))
+		result = SPS_ERROR;
+
+	dev->h = BAM_HANDLE_INVALID;
+	dev->bam = NULL;
+	if (dev->virtual_mapped)
+		iounmap(dev->virt_addr);
+
+exit_err:
+	mutex_unlock(&bam_dma_lock);
+
+	return result;
+}
+
+/**
+ * Initialize BAM DMA module
+ *
+ */
+int sps_dma_init(const struct sps_bam_props *bam_props)
+{
+	struct sps_bam_props props;
+	const struct sps_bam_props *bam_reg;
+	u32 h;
+
+	/* Init local data */
+	memset(&bam_dma_dev, 0, sizeof(bam_dma_dev));
+	num_bams = 0;
+	memset(bam_handles, 0, sizeof(bam_handles));
+
+	/* Create a mutex to control access to the BAM-DMA devices */
+	mutex_init(&bam_dma_lock);
+
+	/* Are there any BAM DMA devices? */
+	if (bam_props == NULL)
+		return 0;
+
+	/*
+	 * Registers all BAMs in the BSP properties, but only uses the first
+	 * BAM-DMA device for allocations.
+	 */
+	if (bam_props->phys_addr) {
+		/* Force multi-EE option for all BAM-DMAs */
+		bam_reg = bam_props;
+		if ((bam_props->options & SPS_BAM_OPT_BAMDMA) &&
+		    (bam_props->manage & SPS_BAM_MGR_MULTI_EE) == 0) {
+			SPS_DBG("sps:Setting multi-EE options for BAM-DMA: %x",
+				bam_props->phys_addr);
+			props = *bam_props;
+			props.manage |= SPS_BAM_MGR_MULTI_EE;
+			bam_reg = &props;
+		}
+
+		/* Register the BAM */
+		if (sps_register_bam_device(bam_reg, &h)) {
+			SPS_ERR("sps:Fail to register BAM-DMA BAM device: "
+				"phys 0x%0x", bam_props->phys_addr);
+			return SPS_ERROR;
+		}
+
+		/* Record the BAM so that it may be deregistered later */
+		if (num_bams < MAX_BAM_DMA_BAMS) {
+			bam_handles[num_bams] = h;
+			num_bams++;
+		} else {
+			SPS_ERR("sps:BAM-DMA: BAM limit exceeded: %d",
+					num_bams);
+			return SPS_ERROR;
+		}
+	} else {
+		SPS_ERR("sps:BAM-DMA phys_addr is zero.");
+		return SPS_ERROR;
+	}
+
+
+	return 0;
+}
+
+/**
+ * De-initialize BAM DMA module
+ *
+ */
+void sps_dma_de_init(void)
+{
+	int n;
+
+	/* De-initialize the BAM devices */
+	for (n = 0; n < num_bams; n++)
+		sps_deregister_bam_device(bam_handles[n]);
+
+	/* Clear local data */
+	memset(&bam_dma_dev, 0, sizeof(bam_dma_dev));
+	num_bams = 0;
+	memset(bam_handles, 0, sizeof(bam_handles));
+}
+
+/**
+ * Allocate a BAM DMA channel
+ *
+ */
+int sps_alloc_dma_chan(const struct sps_alloc_dma_chan *alloc,
+		       struct sps_dma_chan *chan_info)
+{
+	struct bamdma_device *dev;
+	struct bamdma_chan *chan;
+	u32 pipe_index;
+	enum bam_dma_thresh_dma thresh = (enum bam_dma_thresh_dma) 0;
+	enum bam_dma_weight_dma weight = (enum bam_dma_weight_dma) 0;
+	int result = SPS_ERROR;
+
+	if (alloc == NULL || chan_info == NULL) {
+		SPS_ERR("sps:sps_alloc_dma_chan. invalid parameters");
+		return SPS_ERROR;
+	}
+
+	/* Translate threshold and priority to hwio values */
+	if (alloc->threshold != SPS_DMA_THRESHOLD_DEFAULT) {
+		if (alloc->threshold >= 512)
+			thresh = BAM_DMA_THRESH_512;
+		else if (alloc->threshold >= 256)
+			thresh = BAM_DMA_THRESH_256;
+		else if (alloc->threshold >= 128)
+			thresh = BAM_DMA_THRESH_128;
+		else
+			thresh = BAM_DMA_THRESH_64;
+	}
+
+	weight = alloc->priority;
+
+	if ((u32)alloc->priority > (u32)BAM_DMA_WEIGHT_HIGH) {
+		SPS_ERR("sps:BAM-DMA: invalid priority: %x", alloc->priority);
+		return SPS_ERROR;
+	}
+
+	mutex_lock(&bam_dma_lock);
+
+	dev = sps_dma_find_device(alloc->dev);
+	if (dev == NULL) {
+		SPS_ERR("sps:BAM-DMA: invalid BAM handle: %x", alloc->dev);
+		goto exit_err;
+	}
+
+	/* Search for a free set of pipes */
+	for (pipe_index = 0, chan = dev->chans;
+	      pipe_index < dev->num_pipes; pipe_index += 2, chan++) {
+		if (chan->state == DMA_CHAN_STATE_FREE) {
+			/* Just check pipes for safety */
+			if (dev->pipes[pipe_index] != PIPE_INACTIVE ||
+			    dev->pipes[pipe_index + 1] != PIPE_INACTIVE) {
+				SPS_ERR("sps:BAM-DMA: channel %d state "
+					"error:%d %d",
+					pipe_index / 2, dev->pipes[pipe_index],
+				 dev->pipes[pipe_index + 1]);
+				goto exit_err;
+			}
+			break; /* Found free pipe */
+		}
+	}
+
+	if (pipe_index >= dev->num_pipes) {
+		SPS_ERR("sps:BAM-DMA: no free channel. num_pipes = %d",
+			dev->num_pipes);
+		goto exit_err;
+	}
+
+	chan->state = DMA_CHAN_STATE_ALLOC_EXT;
+
+	/* Store config values for use when pipes are activated */
+	chan = &dev->chans[pipe_index / 2];
+	chan->threshold = alloc->threshold;
+	chan->thresh = thresh;
+	chan->priority = alloc->priority;
+	chan->weight = weight;
+
+	SPS_DBG2("sps:sps_alloc_dma_chan. pipe %d.\n", pipe_index);
+
+	/* Report allocated pipes to client */
+	chan_info->dev = dev->h;
+	/* Dest/input/write pipex */
+	chan_info->dest_pipe_index = pipe_index;
+	/* Source/output/read pipe */
+	chan_info->src_pipe_index = pipe_index + 1;
+
+	result = 0;
+exit_err:
+	mutex_unlock(&bam_dma_lock);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_alloc_dma_chan);
+
+/**
+ * Free a BAM DMA channel
+ *
+ */
+int sps_free_dma_chan(struct sps_dma_chan *chan)
+{
+	struct bamdma_device *dev;
+	u32 pipe_index;
+	int result = 0;
+
+	if (chan == NULL) {
+		SPS_ERR("sps:sps_free_dma_chan. chan is NULL");
+		return SPS_ERROR;
+	}
+
+	mutex_lock(&bam_dma_lock);
+
+	dev = sps_dma_find_device(chan->dev);
+	if (dev == NULL) {
+		SPS_ERR("sps:BAM-DMA: invalid BAM handle: %x", chan->dev);
+		result = SPS_ERROR;
+		goto exit_err;
+	}
+
+	/* Verify the pipe indices */
+	pipe_index = chan->dest_pipe_index;
+	if (pipe_index >= dev->num_pipes || ((pipe_index & 1)) ||
+	    (pipe_index + 1) != chan->src_pipe_index) {
+		SPS_ERR("sps:sps_free_dma_chan. Invalid pipe indices."
+			"num_pipes=%d.dest=%d.src=%d.",
+			dev->num_pipes,
+			chan->dest_pipe_index,
+			chan->src_pipe_index);
+		result = SPS_ERROR;
+		goto exit_err;
+	}
+
+	/* Are both pipes inactive? */
+	if (dev->chans[pipe_index / 2].state != DMA_CHAN_STATE_ALLOC_EXT ||
+	    dev->pipes[pipe_index] != PIPE_INACTIVE ||
+	    dev->pipes[pipe_index + 1] != PIPE_INACTIVE) {
+		SPS_ERR("sps:BAM-DMA: attempt to free active chan %d: %d %d",
+			pipe_index / 2, dev->pipes[pipe_index],
+			dev->pipes[pipe_index + 1]);
+		result = SPS_ERROR;
+		goto exit_err;
+	}
+
+	/* Free the channel */
+	dev->chans[pipe_index / 2].state = DMA_CHAN_STATE_FREE;
+
+exit_err:
+	mutex_unlock(&bam_dma_lock);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_free_dma_chan);
+
+/**
+ * Activate a BAM DMA pipe
+ *
+ * This function activates a BAM DMA pipe.
+ *
+ * @dev - pointer to BAM-DMA device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+static u32 sps_dma_check_pipes(struct bamdma_device *dev, u32 pipe_index)
+{
+	u32 pipe_in;
+	u32 pipe_out;
+	int enabled_in;
+	int enabled_out;
+	u32 check;
+
+	pipe_in = pipe_index & ~1;
+	pipe_out = pipe_in + 1;
+	enabled_in = bam_pipe_is_enabled(dev->bam->base, pipe_in);
+	enabled_out = bam_pipe_is_enabled(dev->bam->base, pipe_out);
+
+	if (!enabled_in && !enabled_out)
+		check = DMA_PIPES_BOTH_DISABLED;
+	else if (enabled_in && enabled_out)
+		check = DMA_PIPES_BOTH_ENABLED;
+	else
+		check = DMA_PIPES_STATE_DIFF;
+
+	return check;
+}
+
+/**
+ * Allocate a BAM DMA pipe
+ *
+ */
+int sps_dma_pipe_alloc(void *bam_arg, u32 pipe_index, enum sps_mode dir)
+{
+	struct sps_bam *bam = bam_arg;
+	struct bamdma_device *dev;
+	struct bamdma_chan *chan;
+	u32 channel;
+	int result = SPS_ERROR;
+
+	if (bam == NULL) {
+		SPS_ERR("sps:BAM context is NULL");
+		return SPS_ERROR;
+	}
+
+	/* Check pipe direction */
+	if ((DMA_PIPE_IS_DEST(pipe_index) && dir != SPS_MODE_DEST) ||
+	    (DMA_PIPE_IS_SRC(pipe_index) && dir != SPS_MODE_SRC)) {
+		SPS_ERR("sps:BAM-DMA: wrong direction for BAM %x pipe %d",
+			bam->props.phys_addr, pipe_index);
+		return SPS_ERROR;
+	}
+
+	mutex_lock(&bam_dma_lock);
+
+	dev = sps_dma_find_device((u32) bam);
+	if (dev == NULL) {
+		SPS_ERR("sps:BAM-DMA: invalid BAM: %x",
+			bam->props.phys_addr);
+		goto exit_err;
+	}
+	if (pipe_index >= dev->num_pipes) {
+		SPS_ERR("sps:BAM-DMA: BAM %x invalid pipe: %d",
+			bam->props.phys_addr, pipe_index);
+		goto exit_err;
+	}
+	if (dev->pipes[pipe_index] != PIPE_INACTIVE) {
+		SPS_ERR("sps:BAM-DMA: BAM %x pipe %d already active",
+			bam->props.phys_addr, pipe_index);
+		goto exit_err;
+	}
+
+	/* Mark pipe active */
+	dev->pipes[pipe_index] = PIPE_ACTIVE;
+
+	/* If channel is not allocated, make an internal allocation */
+	channel = pipe_index / 2;
+	chan = &dev->chans[channel];
+	if (chan->state != DMA_CHAN_STATE_ALLOC_EXT &&
+	    chan->state != DMA_CHAN_STATE_ALLOC_INT) {
+		chan->state = DMA_CHAN_STATE_ALLOC_INT;
+	}
+
+	result = 0;
+exit_err:
+	mutex_unlock(&bam_dma_lock);
+
+	return result;
+}
+
+/**
+ * Enable a BAM DMA pipe
+ *
+ */
+int sps_dma_pipe_enable(void *bam_arg, u32 pipe_index)
+{
+	struct sps_bam *bam = bam_arg;
+	struct bamdma_device *dev;
+	struct bamdma_chan *chan;
+	u32 channel;
+	int result = SPS_ERROR;
+
+	SPS_DBG2("sps:sps_dma_pipe_enable.pipe %d", pipe_index);
+
+	mutex_lock(&bam_dma_lock);
+
+	dev = sps_dma_find_device((u32) bam);
+	if (dev == NULL) {
+		SPS_ERR("sps:BAM-DMA: invalid BAM");
+		goto exit_err;
+	}
+	if (pipe_index >= dev->num_pipes) {
+		SPS_ERR("sps:BAM-DMA: BAM %x invalid pipe: %d",
+			bam->props.phys_addr, pipe_index);
+		goto exit_err;
+	}
+	if (dev->pipes[pipe_index] != PIPE_ACTIVE) {
+		SPS_ERR("sps:BAM-DMA: BAM %x pipe %d not active",
+			bam->props.phys_addr, pipe_index);
+		goto exit_err;
+	}
+
+      /*
+       * The channel must be enabled when the dest/input/write pipe
+       * is enabled
+       */
+	if (DMA_PIPE_IS_DEST(pipe_index)) {
+		/* Configure and enable the channel */
+		channel = pipe_index / 2;
+		chan = &dev->chans[channel];
+
+		if (chan->threshold != SPS_DMA_THRESHOLD_DEFAULT)
+			dma_write_reg_field(dev->virt_addr,
+					    DMA_CHNL_CONFIG(channel),
+					    DMA_CHNL_ACT_THRESH,
+					    chan->thresh);
+
+		if (chan->priority != SPS_DMA_PRI_DEFAULT)
+			dma_write_reg_field(dev->virt_addr,
+					    DMA_CHNL_CONFIG(channel),
+					    DMA_CHNL_WEIGHT,
+					    chan->weight);
+
+		dma_write_reg_field(dev->virt_addr,
+				    DMA_CHNL_CONFIG(channel),
+				    DMA_CHNL_ENABLE, 1);
+	}
+
+	result = 0;
+exit_err:
+	mutex_unlock(&bam_dma_lock);
+
+	return result;
+}
+
+/**
+ * Deactivate a BAM DMA pipe
+ *
+ * This function deactivates a BAM DMA pipe.
+ *
+ * @dev - pointer to BAM-DMA device descriptor
+ *
+ * @bam - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+static int sps_dma_deactivate_pipe_atomic(struct bamdma_device *dev,
+					  struct sps_bam *bam,
+					  u32 pipe_index)
+{
+	u32 channel;
+
+	if (dev->bam != bam)
+		return SPS_ERROR;
+	if (pipe_index >= dev->num_pipes)
+		return SPS_ERROR;
+	if (dev->pipes[pipe_index] != PIPE_ACTIVE)
+		return SPS_ERROR;	/* Pipe is not active */
+
+	SPS_DBG2("sps:BAM-DMA: deactivate pipe %d", pipe_index);
+
+	/* Mark pipe inactive */
+	dev->pipes[pipe_index] = PIPE_INACTIVE;
+
+	/*
+	 * Channel must be reset when either pipe is disabled, so just always
+	 * reset regardless of other pipe's state
+	 */
+	channel = pipe_index / 2;
+	dma_write_reg_field(dev->virt_addr, DMA_CHNL_CONFIG(channel),
+			    DMA_CHNL_ENABLE, 0);
+
+	/* If the peer pipe is also inactive, reset the channel */
+	if (sps_dma_check_pipes(dev, pipe_index) == DMA_PIPES_BOTH_DISABLED) {
+		/* Free channel if allocated internally */
+		if (dev->chans[channel].state == DMA_CHAN_STATE_ALLOC_INT)
+			dev->chans[channel].state = DMA_CHAN_STATE_FREE;
+	}
+
+	return 0;
+}
+
+/**
+ * Free a BAM DMA pipe
+ *
+ */
+int sps_dma_pipe_free(void *bam_arg, u32 pipe_index)
+{
+	struct bamdma_device *dev;
+	struct sps_bam *bam = bam_arg;
+	int result;
+
+	mutex_lock(&bam_dma_lock);
+
+	dev = sps_dma_find_device((u32) bam);
+	if (dev == NULL) {
+		SPS_ERR("sps:BAM-DMA: invalid BAM");
+		result = SPS_ERROR;
+		goto exit_err;
+	}
+
+	result = sps_dma_deactivate_pipe_atomic(dev, bam, pipe_index);
+
+exit_err:
+	mutex_unlock(&bam_dma_lock);
+
+	return result;
+}
+
+/**
+ * Get the BAM handle for BAM-DMA.
+ *
+ * The BAM handle should be use as source/destination in the sps_connect().
+ *
+ * @return bam handle on success, zero on error
+ */
+u32 sps_dma_get_bam_handle(void)
+{
+	return (u32) bam_dma_dev[0].bam;
+}
+EXPORT_SYMBOL(sps_dma_get_bam_handle);
+
+/**
+ * Free the BAM handle for BAM-DMA.
+ *
+ */
+void sps_dma_free_bam_handle(u32 h)
+{
+}
+EXPORT_SYMBOL(sps_dma_free_bam_handle);
+
+#endif /* CONFIG_SPS_SUPPORT_BAMDMA */
diff --git a/drivers/platform/msm/sps/sps_map.c b/drivers/platform/msm/sps/sps_map.c
new file mode 100644
index 0000000..d98a8b1
--- /dev/null
+++ b/drivers/platform/msm/sps/sps_map.c
@@ -0,0 +1,137 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/**
+ * Connection mapping table managment for SPS device driver.
+ */
+
+#include <linux/types.h>	/* u32 */
+#include <linux/kernel.h>	/* pr_info() */
+#include <linux/memory.h>	/* memset */
+
+#include "spsi.h"
+
+/* Module state */
+struct sps_map_state {
+	const struct sps_map *maps;
+	u32 num_maps;
+	u32 options;
+};
+
+static struct sps_map_state sps_maps;
+
+/**
+ * Initialize connection mapping module
+ *
+ */
+int sps_map_init(const struct sps_map *map_props, u32 options)
+{
+	const struct sps_map *maps;
+
+	/* Are there any connection mappings? */
+	memset(&sps_maps, 0, sizeof(sps_maps));
+	if (map_props == NULL)
+		return 0;
+
+	/* Init the module state */
+	sps_maps.maps = map_props;
+	sps_maps.options = options;
+	for (maps = sps_maps.maps;; maps++, sps_maps.num_maps++)
+		if (maps->src.periph_class == SPS_CLASS_INVALID &&
+		    maps->src.periph_phy_addr == SPS_ADDR_INVALID)
+			break;
+
+	SPS_DBG("sps: %d mappings", sps_maps.num_maps);
+
+	return 0;
+}
+
+/**
+ * De-initialize connection mapping module
+ *
+ */
+void sps_map_de_init(void)
+{
+	memset(&sps_maps, 0, sizeof(sps_maps));
+}
+
+/**
+ * Find matching connection mapping
+ *
+ */
+int sps_map_find(struct sps_connect *connect)
+{
+	const struct sps_map *map;
+	u32 i;
+	void *desc;
+	void *data;
+
+	/* Are there any connection mappings? */
+	if (sps_maps.num_maps == 0)
+		return SPS_ERROR;
+
+	/* Search the mapping table for a match to the specified connection */
+	for (i = sps_maps.num_maps, map = sps_maps.maps;
+	    i > 0; i--, map++)
+		if (map->src.periph_class == (u32) connect->source &&
+		    map->dest.periph_class == (u32) connect->destination
+		    && map->config == (u32) connect->config)
+			break;
+
+	if (i == 0)
+		return SPS_ERROR;
+
+	/*
+	 * Before modifying client parameter struct, perform all
+	 * operations that might fail
+	 */
+	desc = spsi_get_mem_ptr(map->desc_base);
+	if (desc == NULL) {
+		SPS_ERR("sps:Cannot get virt addr for I/O buffer: 0x%x",
+			map->desc_base);
+		return SPS_ERROR;
+	}
+
+	if (map->data_size > 0 && map->data_base != SPS_ADDR_INVALID) {
+		data = spsi_get_mem_ptr(map->data_base);
+		if (data == NULL) {
+			SPS_ERR("sps:Can't get virt addr for I/O buffer: 0x%x",
+				map->data_base);
+			return SPS_ERROR;
+		}
+	} else {
+		data = NULL;
+	}
+
+	/* Copy mapping values to client parameter struct */
+	if (connect->source != SPS_DEV_HANDLE_MEM)
+		connect->src_pipe_index = map->src.pipe_index;
+
+	if (connect->destination != SPS_DEV_HANDLE_MEM)
+		connect->dest_pipe_index = map->dest.pipe_index;
+
+	if (connect->mode == SPS_MODE_SRC)
+		connect->event_thresh = map->src.event_thresh;
+	else
+		connect->event_thresh = map->dest.event_thresh;
+
+	connect->desc.size = map->desc_size;
+	connect->desc.phys_base = map->desc_base;
+	connect->desc.base = desc;
+	if (map->data_size > 0 && map->data_base != SPS_ADDR_INVALID) {
+		connect->data.size = map->data_size;
+		connect->data.phys_base = map->data_base;
+		connect->data.base = data;
+	}
+
+	return 0;
+}
diff --git a/drivers/platform/msm/sps/sps_map.h b/drivers/platform/msm/sps/sps_map.h
new file mode 100644
index 0000000..692e47c
--- /dev/null
+++ b/drivers/platform/msm/sps/sps_map.h
@@ -0,0 +1,46 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/* SPS driver mapping table data declarations. */
+
+
+#ifndef _SPS_MAP_H_
+#define _SPS_MAP_H_
+
+#include <linux/types.h>	/* u32 */
+
+/* End point parameters */
+struct sps_map_end_point {
+	u32 periph_class;	/* Peripheral device enumeration class */
+	u32 periph_phy_addr;	/* Peripheral base address */
+	u32 pipe_index;		/* Pipe index */
+	u32 event_thresh;	/* Pipe event threshold */
+};
+
+/* Mapping connection descriptor */
+struct sps_map {
+	/* Source end point parameters */
+	struct sps_map_end_point src;
+
+	/* Destination end point parameters */
+	struct sps_map_end_point dest;
+
+	/* Resource parameters */
+	u32 config;	 /* Configuration (stream) identifier */
+	u32 desc_base;	 /* Physical address of descriptor FIFO */
+	u32 desc_size;	 /* Size (bytes) of descriptor FIFO */
+	u32 data_base;	 /* Physical address of data FIFO */
+	u32 data_size;	 /* Size (bytes) of data FIFO */
+
+};
+
+#endif /* _SPS_MAP_H_ */
diff --git a/drivers/platform/msm/sps/sps_mem.c b/drivers/platform/msm/sps/sps_mem.c
new file mode 100644
index 0000000..1b19b12
--- /dev/null
+++ b/drivers/platform/msm/sps/sps_mem.c
@@ -0,0 +1,168 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/**
+ * Pipe-Memory allocation/free management.
+ */
+
+#include <linux/types.h>	/* u32 */
+#include <linux/kernel.h>	/* pr_info() */
+#include <linux/io.h>		/* ioremap() */
+#include <linux/mutex.h>	/* mutex */
+#include <linux/list.h>		/* list_head */
+#include <linux/genalloc.h>	/* gen_pool_alloc() */
+#include <linux/errno.h>	/* ENOMEM */
+
+#include "sps_bam.h"
+#include "spsi.h"
+
+static u32 iomem_phys;
+static void *iomem_virt;
+static u32 iomem_size;
+static u32 iomem_offset;
+static struct gen_pool *pool;
+static u32 nid = 0xaa;
+
+/* Debug */
+static u32 total_alloc;
+static u32 total_free;
+
+/**
+ * Translate physical to virtual address
+ *
+ */
+void *spsi_get_mem_ptr(u32 phys_addr)
+{
+	void *virt = NULL;
+
+	if ((phys_addr >= iomem_phys) &&
+	    (phys_addr < (iomem_phys + iomem_size))) {
+		virt = (u8 *) iomem_virt + (phys_addr - iomem_phys);
+	} else {
+		virt = phys_to_virt(phys_addr);
+		SPS_ERR("sps:spsi_get_mem_ptr.invalid phys addr=0x%x.",
+			phys_addr);
+	}
+	return virt;
+}
+
+/**
+ * Allocate I/O (pipe) memory
+ *
+ */
+u32 sps_mem_alloc_io(u32 bytes)
+{
+	u32 phys_addr = SPS_ADDR_INVALID;
+	u32 virt_addr = 0;
+
+	virt_addr = gen_pool_alloc(pool, bytes);
+	if (virt_addr) {
+		iomem_offset = virt_addr - (u32) iomem_virt;
+		phys_addr = iomem_phys + iomem_offset;
+		total_alloc += bytes;
+	} else {
+		SPS_ERR("sps:gen_pool_alloc %d bytes fail.", bytes);
+		return SPS_ADDR_INVALID;
+	}
+
+	SPS_DBG2("sps:sps_mem_alloc_io.phys=0x%x.virt=0x%x.size=0x%x.",
+		phys_addr, virt_addr, bytes);
+
+	return phys_addr;
+}
+
+/**
+ * Free I/O memory
+ *
+ */
+void sps_mem_free_io(u32 phys_addr, u32 bytes)
+{
+	u32 virt_addr = 0;
+
+	iomem_offset = phys_addr - iomem_phys;
+	virt_addr = (u32) iomem_virt + iomem_offset;
+
+	SPS_DBG2("sps:sps_mem_free_io.phys=0x%x.virt=0x%x.size=0x%x.",
+		phys_addr, virt_addr, bytes);
+
+	gen_pool_free(pool, virt_addr, bytes);
+	total_free += bytes;
+}
+
+/**
+ * Initialize driver memory module
+ *
+ */
+int sps_mem_init(u32 pipemem_phys_base, u32 pipemem_size)
+{
+#ifndef CONFIG_SPS_SUPPORT_NDP_BAM
+	int res;
+#endif
+	/* 2^8=128. The desc-fifo and data-fifo minimal allocation. */
+	int min_alloc_order = 8;
+
+#ifndef CONFIG_SPS_SUPPORT_NDP_BAM
+	iomem_phys = pipemem_phys_base;
+	iomem_size = pipemem_size;
+
+	if (iomem_phys == 0) {
+		SPS_ERR("sps:Invalid Pipe-Mem address");
+		return SPS_ERROR;
+	} else {
+		iomem_virt = ioremap(iomem_phys, iomem_size);
+		if (!iomem_virt) {
+			SPS_ERR("sps:Failed to IO map pipe memory.\n");
+			return -ENOMEM;
+		}
+	}
+
+	iomem_offset = 0;
+	SPS_DBG("sps:sps_mem_init.iomem_phys=0x%x,iomem_virt=0x%x.",
+		iomem_phys, (u32) iomem_virt);
+#endif
+
+	pool = gen_pool_create(min_alloc_order, nid);
+
+	if (!pool) {
+		SPS_ERR("sps:Failed to create a new memory pool.\n");
+		return -ENOMEM;
+	}
+
+#ifndef CONFIG_SPS_SUPPORT_NDP_BAM
+	res = gen_pool_add(pool, (u32) iomem_virt, iomem_size, nid);
+	if (res)
+		return res;
+#endif
+
+	return 0;
+}
+
+/**
+ * De-initialize driver memory module
+ *
+ */
+int sps_mem_de_init(void)
+{
+	if (iomem_virt != NULL) {
+		gen_pool_destroy(pool);
+		pool = NULL;
+		iounmap(iomem_virt);
+		iomem_virt = NULL;
+	}
+
+	if (total_alloc == total_free)
+		return 0;
+	else {
+		SPS_ERR("sps:sps_mem_de_init:some memory not free");
+		return SPS_ERROR;
+	}
+}
diff --git a/drivers/platform/msm/sps/sps_rm.c b/drivers/platform/msm/sps/sps_rm.c
new file mode 100644
index 0000000..c980eb0
--- /dev/null
+++ b/drivers/platform/msm/sps/sps_rm.c
@@ -0,0 +1,817 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/* Resource management for the SPS device driver. */
+
+#include <linux/types.h>	/* u32 */
+#include <linux/kernel.h>	/* pr_info() */
+#include <linux/mutex.h>	/* mutex */
+#include <linux/list.h>		/* list_head */
+#include <linux/slab.h>		/* kzalloc() */
+#include <linux/memory.h>	/* memset */
+
+#include "spsi.h"
+#include "sps_core.h"
+
+/* Max BAM FIFO sizes */
+#define SPSRM_MAX_DESC_FIFO_SIZE    0xffff
+#define SPSRM_MAX_DATA_FIFO_SIZE    0xffff
+
+/* Connection control struct pointer */
+static struct sps_rm *sps_rm;
+
+/**
+ * Initialize resource manager module
+ */
+int sps_rm_init(struct sps_rm *rm, u32 options)
+{
+	/* Set the resource manager state struct pointer */
+	sps_rm = rm;
+
+	/* Initialize the state struct */
+	INIT_LIST_HEAD(&sps_rm->connections_q);
+	mutex_init(&sps_rm->lock);
+
+	return 0;
+}
+
+/**
+ * Initialize client state context
+ *
+ */
+void sps_rm_config_init(struct sps_connect *connect)
+{
+	memset(connect, SPSRM_CLEAR, sizeof(*connect));
+}
+
+/**
+ * Remove reference to connection mapping
+ *
+ * This function removes a reference from a connection mapping struct.
+ *
+ * @map - pointer to connection mapping struct
+ *
+ */
+static void sps_rm_remove_ref(struct sps_connection *map)
+{
+	/* Free this connection */
+	map->refs--;
+	if (map->refs <= 0) {
+		if (map->client_src != NULL || map->client_dest != NULL)
+			SPS_ERR("sps:Failed to allocate connection struct");
+
+		list_del(&map->list);
+		kfree(map);
+	}
+}
+
+/**
+ * Compare map to connect parameters
+ *
+ * This function compares client connect parameters to an allocated
+ * connection mapping.
+ *
+ * @pipe - client context for SPS connection end point
+ *
+ * @return - true if match, false otherwise
+ *
+ */
+static int sps_rm_map_match(const struct sps_connect *cfg,
+			    const struct sps_connection *map)
+{
+	if (cfg->source != map->src.dev ||
+	    cfg->destination != map->dest.dev)
+		return false;
+
+	if (cfg->src_pipe_index != SPSRM_CLEAR &&
+	    cfg->src_pipe_index != map->src.pipe_index)
+		return false;
+
+	if (cfg->dest_pipe_index != SPSRM_CLEAR &&
+	    cfg->dest_pipe_index != map->dest.pipe_index)
+		return false;
+
+	if (cfg->config != map->config)
+		return false;
+
+	if (cfg->desc.size != SPSRM_CLEAR) {
+		if (cfg->desc.size != map->desc.size)
+			return false;
+
+		if (cfg->desc.phys_base != SPSRM_CLEAR &&
+		    cfg->desc.base != (void *)SPSRM_CLEAR &&
+		    (cfg->desc.phys_base != map->desc.phys_base ||
+		     cfg->desc.base != map->desc.base)) {
+			return false;
+		}
+	}
+
+	if (cfg->data.size != SPSRM_CLEAR) {
+		if (cfg->data.size != map->data.size)
+			return false;
+
+		if (cfg->data.phys_base != SPSRM_CLEAR &&
+		    cfg->data.base != (void *)SPSRM_CLEAR &&
+		    (cfg->data.phys_base != map->data.phys_base ||
+		     cfg->data.base != map->data.base))
+			return false;
+	}
+
+	return true;
+}
+
+/**
+ * Find unconnected mapping
+ *
+ * This function finds an allocated a connection mapping.
+ *
+ * @pipe - client context for SPS connection end point
+ *
+ * @return - pointer to allocated connection mapping, or NULL if not found
+ *
+ */
+static struct sps_connection *find_unconnected(struct sps_pipe *pipe)
+{
+	struct sps_connect *cfg = &pipe->connect;
+	struct sps_connection *map;
+
+	/* Has this connection already been allocated? */
+	list_for_each_entry(map, &sps_rm->connections_q, list) {
+		if (sps_rm_map_match(cfg, map))
+			if ((cfg->mode == SPS_MODE_SRC
+			     && map->client_src == NULL)
+			    || (cfg->mode != SPS_MODE_SRC
+				&& map->client_dest == NULL))
+				return map;	/* Found */
+	}
+
+	return NULL;		/* Not Found */
+}
+
+/**
+ * Assign connection to client
+ *
+ * This function assigns a connection to a client.
+ *
+ * @pipe - client context for SPS connection end point
+ *
+ * @map - connection mapping
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+static int sps_rm_assign(struct sps_pipe *pipe,
+			 struct sps_connection *map)
+{
+	struct sps_connect *cfg = &pipe->connect;
+
+	/* Check ownership and BAM */
+	if ((cfg->mode == SPS_MODE_SRC && map->client_src != NULL) ||
+	    (cfg->mode != SPS_MODE_SRC && map->client_dest != NULL)) {
+		SPS_ERR("sps:The end point is already connected.\n");
+		return SPS_ERROR;
+	}
+
+	/* Check whether this end point is a BAM (not memory) */
+	if ((cfg->mode == SPS_MODE_SRC && map->src.bam == NULL) ||
+	    (cfg->mode != SPS_MODE_SRC && map->dest.bam == NULL)) {
+		SPS_ERR("sps:The end point is empty.\n");
+		return SPS_ERROR;
+	}
+
+	/* Record the connection assignment */
+	if (cfg->mode == SPS_MODE_SRC) {
+		map->client_src = pipe;
+		pipe->bam = map->src.bam;
+		pipe->pipe_index = map->src.pipe_index;
+		if (pipe->connect.event_thresh != SPSRM_CLEAR)
+			map->src.event_threshold = pipe->connect.event_thresh;
+		if (pipe->connect.lock_group != SPSRM_CLEAR)
+			map->src.lock_group = pipe->connect.lock_group;
+	} else {
+		map->client_dest = pipe;
+		pipe->bam = map->dest.bam;
+		pipe->pipe_index = map->dest.pipe_index;
+		if (pipe->connect.event_thresh != SPSRM_CLEAR)
+			map->dest.event_threshold =
+			pipe->connect.event_thresh;
+		if (pipe->connect.lock_group != SPSRM_CLEAR)
+			map->dest.lock_group = pipe->connect.lock_group;
+	}
+	pipe->map = map;
+
+	SPS_DBG("sps:sps_rm_assign.bam 0x%x.pipe_index=%d\n",
+			BAM_ID(pipe->bam), pipe->pipe_index);
+
+	/* Copy parameters to client connect state */
+	pipe->connect.src_pipe_index = map->src.pipe_index;
+	pipe->connect.dest_pipe_index = map->dest.pipe_index;
+	pipe->connect.desc = map->desc;
+	pipe->connect.data = map->data;
+
+	pipe->client_state = SPS_STATE_ALLOCATE;
+
+	return 0;
+}
+
+/**
+ * Free connection mapping resources
+ *
+ * This function frees a connection mapping resources.
+ *
+ * @pipe - client context for SPS connection end point
+ *
+ */
+static void sps_rm_free_map_rsrc(struct sps_connection *map)
+{
+	struct sps_bam *bam;
+
+	if (map->client_src != NULL || map->client_dest != NULL)
+		return;
+
+	if (map->alloc_src_pipe != SPS_BAM_PIPE_INVALID) {
+		bam = map->src.bam;
+		sps_bam_pipe_free(bam, map->src.pipe_index);
+
+		/* Is this a BAM-DMA pipe? */
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+		if ((bam->props.options & SPS_BAM_OPT_BAMDMA))
+			/* Deallocate and free the BAM-DMA channel */
+			sps_dma_pipe_free(bam, map->src.pipe_index);
+#endif
+		map->alloc_src_pipe = SPS_BAM_PIPE_INVALID;
+		map->src.pipe_index = SPS_BAM_PIPE_INVALID;
+	}
+	if (map->alloc_dest_pipe != SPS_BAM_PIPE_INVALID) {
+		bam = map->dest.bam;
+		sps_bam_pipe_free(bam, map->dest.pipe_index);
+
+		/* Is this a BAM-DMA pipe? */
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+		if ((bam->props.options & SPS_BAM_OPT_BAMDMA)) {
+			/* Deallocate the BAM-DMA channel */
+			sps_dma_pipe_free(bam, map->dest.pipe_index);
+		}
+#endif
+		map->alloc_dest_pipe = SPS_BAM_PIPE_INVALID;
+		map->dest.pipe_index = SPS_BAM_PIPE_INVALID;
+	}
+	if (map->alloc_desc_base != SPS_ADDR_INVALID) {
+		sps_mem_free_io(map->alloc_desc_base, map->desc.size);
+
+		map->alloc_desc_base = SPS_ADDR_INVALID;
+		map->desc.phys_base = SPS_ADDR_INVALID;
+	}
+	if (map->alloc_data_base != SPS_ADDR_INVALID) {
+		sps_mem_free_io(map->alloc_data_base, map->data.size);
+
+		map->alloc_data_base = SPS_ADDR_INVALID;
+		map->data.phys_base = SPS_ADDR_INVALID;
+	}
+}
+
+/**
+ * Init connection mapping from client connect
+ *
+ * This function initializes a connection mapping from the client's
+ * connect parameters.
+ *
+ * @map - connection mapping struct
+ *
+ * @cfg - client connect parameters
+ *
+ * @return - pointer to allocated connection mapping, or NULL on error
+ *
+ */
+static void sps_rm_init_map(struct sps_connection *map,
+			    const struct sps_connect *cfg)
+{
+	/* Clear the connection mapping struct */
+	memset(map, 0, sizeof(*map));
+	map->desc.phys_base = SPS_ADDR_INVALID;
+	map->data.phys_base = SPS_ADDR_INVALID;
+	map->alloc_desc_base = SPS_ADDR_INVALID;
+	map->alloc_data_base = SPS_ADDR_INVALID;
+	map->alloc_src_pipe = SPS_BAM_PIPE_INVALID;
+	map->alloc_dest_pipe = SPS_BAM_PIPE_INVALID;
+
+	/* Copy client required parameters */
+	map->src.dev = cfg->source;
+	map->dest.dev = cfg->destination;
+	map->desc.size = cfg->desc.size;
+	map->data.size = cfg->data.size;
+	map->config = cfg->config;
+
+	/* Did client specify descriptor FIFO? */
+	if (map->desc.size != SPSRM_CLEAR &&
+	    cfg->desc.phys_base != SPSRM_CLEAR &&
+	    cfg->desc.base != (void *)SPSRM_CLEAR)
+		map->desc = cfg->desc;
+
+	/* Did client specify data FIFO? */
+	if (map->data.size != SPSRM_CLEAR &&
+	    cfg->data.phys_base != SPSRM_CLEAR &&
+	    cfg->data.base != (void *)SPSRM_CLEAR)
+		map->data = cfg->data;
+
+	/* Did client specify source pipe? */
+	if (cfg->src_pipe_index != SPSRM_CLEAR)
+		map->src.pipe_index = cfg->src_pipe_index;
+	else
+		map->src.pipe_index = SPS_BAM_PIPE_INVALID;
+
+
+	/* Did client specify destination pipe? */
+	if (cfg->dest_pipe_index != SPSRM_CLEAR)
+		map->dest.pipe_index = cfg->dest_pipe_index;
+	else
+		map->dest.pipe_index = SPS_BAM_PIPE_INVALID;
+}
+
+/**
+ * Create a new connection mapping
+ *
+ * This function creates a new connection mapping.
+ *
+ * @pipe - client context for SPS connection end point
+ *
+ * @return - pointer to allocated connection mapping, or NULL on error
+ *
+ */
+static struct sps_connection *sps_rm_create(struct sps_pipe *pipe)
+{
+	struct sps_connection *map;
+	struct sps_bam *bam;
+	u32 desc_size;
+	u32 data_size;
+	enum sps_mode dir;
+	int success = false;
+
+	/* Allocate new connection */
+	map = kzalloc(sizeof(*map), GFP_KERNEL);
+	if (map == NULL) {
+		SPS_ERR("sps:Failed to allocate connection struct");
+		return NULL;
+	}
+
+	/* Initialize connection struct */
+	sps_rm_init_map(map, &pipe->connect);
+	dir = pipe->connect.mode;
+
+	/* Use a do/while() loop to avoid a "goto" */
+	success = false;
+	/* Get BAMs */
+	map->src.bam = sps_h2bam(map->src.dev);
+	if (map->src.bam == NULL) {
+		if (map->src.dev != SPS_DEV_HANDLE_MEM) {
+			SPS_ERR("sps:Invalid BAM handle: 0x%x", map->src.dev);
+			goto exit_err;
+		}
+		map->src.pipe_index = SPS_BAM_PIPE_INVALID;
+	}
+	map->dest.bam = sps_h2bam(map->dest.dev);
+	if (map->dest.bam == NULL) {
+		if (map->dest.dev != SPS_DEV_HANDLE_MEM) {
+			SPS_ERR("sps:Invalid BAM handle: 0x%x", map->dest.dev);
+			goto exit_err;
+		}
+		map->dest.pipe_index = SPS_BAM_PIPE_INVALID;
+	}
+
+	/* Check the BAM device for the pipe */
+	if ((dir == SPS_MODE_SRC && map->src.bam == NULL) ||
+	    (dir != SPS_MODE_SRC && map->dest.bam == NULL)) {
+		SPS_ERR("sps:Invalid BAM endpt: dir %d src 0x%x dest 0x%x",
+			dir, map->src.dev, map->dest.dev);
+		goto exit_err;
+	}
+
+	/* Allocate pipes and copy BAM parameters */
+	if (map->src.bam != NULL) {
+		/* Allocate the pipe */
+		bam = map->src.bam;
+		map->alloc_src_pipe = sps_bam_pipe_alloc(bam,
+							map->src.pipe_index);
+		if (map->alloc_src_pipe == SPS_BAM_PIPE_INVALID)
+			goto exit_err;
+		map->src.pipe_index = map->alloc_src_pipe;
+
+		/* Is this a BAM-DMA pipe? */
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+		if ((bam->props.options & SPS_BAM_OPT_BAMDMA)) {
+			int rc;
+			/* Allocate the BAM-DMA channel */
+			rc = sps_dma_pipe_alloc(bam, map->src.pipe_index,
+						 SPS_MODE_SRC);
+			if (rc) {
+				SPS_ERR("sps:Failed to alloc BAM-DMA pipe: %d",
+					map->src.pipe_index);
+				goto exit_err;
+			}
+		}
+#endif
+		map->src.bam_phys = bam->props.phys_addr;
+		map->src.event_threshold = bam->props.event_threshold;
+	}
+	if (map->dest.bam != NULL) {
+		/* Allocate the pipe */
+		bam = map->dest.bam;
+		map->alloc_dest_pipe = sps_bam_pipe_alloc(bam,
+							 map->dest.pipe_index);
+		if (map->alloc_dest_pipe == SPS_BAM_PIPE_INVALID)
+			goto exit_err;
+
+		map->dest.pipe_index = map->alloc_dest_pipe;
+
+		/* Is this a BAM-DMA pipe? */
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+		if ((bam->props.options & SPS_BAM_OPT_BAMDMA)) {
+			int rc;
+			/* Allocate the BAM-DMA channel */
+			rc = sps_dma_pipe_alloc(bam, map->dest.pipe_index,
+					       SPS_MODE_DEST);
+			if (rc) {
+				SPS_ERR("sps:Failed to alloc BAM-DMA pipe: %d",
+					map->dest.pipe_index);
+				goto exit_err;
+			}
+		}
+#endif
+		map->dest.bam_phys = bam->props.phys_addr;
+		map->dest.event_threshold =
+		bam->props.event_threshold;
+	}
+
+	/* Get default FIFO sizes */
+	desc_size = 0;
+	data_size = 0;
+	if (map->src.bam != NULL) {
+		bam = map->src.bam;
+		desc_size = bam->props.desc_size;
+		data_size = bam->props.data_size;
+	}
+	if (map->dest.bam != NULL) {
+		bam = map->dest.bam;
+		if (bam->props.desc_size > desc_size)
+			desc_size = bam->props.desc_size;
+		if (bam->props.data_size > data_size)
+			data_size = bam->props.data_size;
+	}
+
+	/* Set FIFO sizes */
+	if (map->desc.size == SPSRM_CLEAR)
+		map->desc.size = desc_size;
+	if (map->src.bam != NULL && map->dest.bam != NULL) {
+		/* BAM-to-BAM requires data FIFO */
+		if (map->data.size == SPSRM_CLEAR)
+			map->data.size = data_size;
+	} else {
+		map->data.size = 0;
+	}
+	if (map->desc.size > SPSRM_MAX_DESC_FIFO_SIZE) {
+		SPS_ERR("sps:Invalid desc FIFO size: 0x%x", map->desc.size);
+		goto exit_err;
+	}
+	if (map->src.bam != NULL && map->dest.bam != NULL &&
+	    map->data.size > SPSRM_MAX_DATA_FIFO_SIZE) {
+		SPS_ERR("sps:Invalid data FIFO size: 0x%x", map->data.size);
+		goto exit_err;
+	}
+
+	/* Allocate descriptor FIFO if necessary */
+	if (map->desc.size && map->desc.phys_base == SPS_ADDR_INVALID) {
+		map->alloc_desc_base = sps_mem_alloc_io(map->desc.size);
+		if (map->alloc_desc_base == SPS_ADDR_INVALID) {
+			SPS_ERR("sps:I/O memory allocation failure:0x%x",
+				map->desc.size);
+			goto exit_err;
+		}
+		map->desc.phys_base = map->alloc_desc_base;
+		map->desc.base = spsi_get_mem_ptr(map->desc.phys_base);
+		if (map->desc.base == NULL) {
+			SPS_ERR("sps:Cannot get virt addr for I/O buffer:0x%x",
+				map->desc.phys_base);
+			goto exit_err;
+		}
+	}
+
+	/* Allocate data FIFO if necessary */
+	if (map->data.size && map->data.phys_base == SPS_ADDR_INVALID) {
+		map->alloc_data_base = sps_mem_alloc_io(map->data.size);
+		if (map->alloc_data_base == SPS_ADDR_INVALID) {
+			SPS_ERR("sps:I/O memory allocation failure:0x%x",
+				map->data.size);
+			goto exit_err;
+		}
+		map->data.phys_base = map->alloc_data_base;
+		map->data.base = spsi_get_mem_ptr(map->data.phys_base);
+		if (map->data.base == NULL) {
+			SPS_ERR("sps:Cannot get virt addr for I/O buffer:0x%x",
+				map->data.phys_base);
+			goto exit_err;
+		}
+	}
+
+	/* Attempt to assign this connection to the client */
+	if (sps_rm_assign(pipe, map)) {
+		SPS_ERR("sps:failed to assign a connection to the client.\n");
+		goto exit_err;
+	}
+
+	/* Initialization was successful */
+	success = true;
+exit_err:
+
+	/* If initialization failed, free resources */
+	if (!success) {
+		sps_rm_free_map_rsrc(map);
+		kfree(map);
+		return NULL;
+	}
+
+	return map;
+}
+
+/**
+ * Free connection mapping
+ *
+ * This function frees a connection mapping.
+ *
+ * @pipe - client context for SPS connection end point
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+static int sps_rm_free(struct sps_pipe *pipe)
+{
+	struct sps_connection *map = (void *)pipe->map;
+	struct sps_connect *cfg = &pipe->connect;
+
+	mutex_lock(&sps_rm->lock);
+
+	/* Free this connection */
+	if (cfg->mode == SPS_MODE_SRC)
+		map->client_src = NULL;
+	else
+		map->client_dest = NULL;
+
+	pipe->map = NULL;
+	pipe->client_state = SPS_STATE_DISCONNECT;
+	sps_rm_free_map_rsrc(map);
+
+	sps_rm_remove_ref(map);
+
+	mutex_unlock(&sps_rm->lock);
+
+	return 0;
+}
+
+/**
+ * Allocate an SPS connection end point
+ *
+ * This function allocates resources and initializes a BAM connection.
+ *
+ * @pipe - client context for SPS connection end point
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+static int sps_rm_alloc(struct sps_pipe *pipe)
+{
+	struct sps_connection *map;
+	int result = SPS_ERROR;
+
+	if (pipe->connect.sps_reserved != SPSRM_CLEAR) {
+		/*
+		 * Client did not call sps_get_config()	to init
+		 * struct sps_connect, so only use legacy members.
+		 */
+		u32 source = pipe->connect.source;
+		u32 destination = pipe->connect.destination;
+		enum sps_mode mode = pipe->connect.mode;
+		u32 config = pipe->connect.config;
+		memset(&pipe->connect, SPSRM_CLEAR,
+			      sizeof(pipe->connect));
+		pipe->connect.source = source;
+		pipe->connect.destination = destination;
+		pipe->connect.mode = mode;
+		pipe->connect.config = config;
+	}
+	if (pipe->connect.config == SPSRM_CLEAR)
+		pipe->connect.config = SPS_CONFIG_DEFAULT;
+
+	/*
+	 *  If configuration is not default, then client is specifying a
+	 * connection mapping.  Find a matching mapping, or fail.
+	 * If a match is found, the client's Connect struct will be updated
+	 * with all the mapping's values.
+	 */
+	if (pipe->connect.config != SPS_CONFIG_DEFAULT) {
+		if (sps_map_find(&pipe->connect)) {
+			SPS_ERR("sps:Failed to find connection mapping");
+			return SPS_ERROR;
+		}
+	}
+
+	mutex_lock(&sps_rm->lock);
+	/* Check client state */
+	if (IS_SPS_STATE_OK(pipe)) {
+		SPS_ERR("sps:Client connection already allocated");
+		goto exit_err;
+	}
+
+	/* Are the connection resources already allocated? */
+	map = find_unconnected(pipe);
+	if (map != NULL) {
+		/* Attempt to assign this connection to the client */
+		if (sps_rm_assign(pipe, map))
+			/* Assignment failed, so must allocate new */
+			map = NULL;
+	}
+
+	/* Allocate a new connection if necessary */
+	if (map == NULL) {
+		map = sps_rm_create(pipe);
+		if (map == NULL) {
+			SPS_ERR("sps:Failed to allocate connection");
+			goto exit_err;
+		}
+		list_add_tail(&map->list, &sps_rm->connections_q);
+	}
+
+	/* Add the connection to the allocated queue */
+	map->refs++;
+
+	/* Initialization was successful */
+	result = 0;
+exit_err:
+	mutex_unlock(&sps_rm->lock);
+
+	if (result)
+		return SPS_ERROR;
+
+	return 0;
+}
+
+/**
+ * Disconnect an SPS connection end point
+ *
+ * This function frees resources and de-initializes a BAM connection.
+ *
+ * @pipe - client context for SPS connection end point
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+static int sps_rm_disconnect(struct sps_pipe *pipe)
+{
+	sps_rm_free(pipe);
+	return 0;
+}
+
+/**
+ * Process connection state change
+ *
+ * This function processes a connection state change.
+ *
+ * @pipe - pointer to client context
+ *
+ * @state - new state for connection
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_rm_state_change(struct sps_pipe *pipe, u32 state)
+{
+	int auto_enable = false;
+	int result;
+
+	/* Allocate the pipe */
+	if (pipe->client_state == SPS_STATE_DISCONNECT &&
+	    state == SPS_STATE_ALLOCATE) {
+		if (sps_rm_alloc(pipe)) {
+			SPS_ERR("sps:Fail to allocate resource for"
+				" BAM 0x%x pipe %d",
+				(u32) pipe->bam, pipe->pipe_index);
+			return SPS_ERROR;
+		}
+	}
+
+	/* Configure the pipe */
+	if (pipe->client_state == SPS_STATE_ALLOCATE &&
+	    state == SPS_STATE_CONNECT) {
+		/* Connect the BAM pipe */
+		struct sps_bam_connect_param params;
+		memset(&params, 0, sizeof(params));
+		params.mode = pipe->connect.mode;
+		if (pipe->connect.options != SPSRM_CLEAR) {
+			params.options = pipe->connect.options;
+			params.irq_gen_addr = pipe->connect.irq_gen_addr;
+			params.irq_gen_data = pipe->connect.irq_gen_data;
+		}
+		result = sps_bam_pipe_connect(pipe, &params);
+		if (result) {
+			SPS_ERR("sps:Failed to connect BAM 0x%x pipe %d",
+				(u32) pipe->bam, pipe->pipe_index);
+			return SPS_ERROR;
+		}
+		pipe->client_state = SPS_STATE_CONNECT;
+
+		/* Set auto-enable for system-mode connections */
+		if (pipe->connect.source == SPS_DEV_HANDLE_MEM ||
+		    pipe->connect.destination == SPS_DEV_HANDLE_MEM) {
+			if (pipe->map->desc.size != 0 &&
+			    pipe->map->desc.phys_base != SPS_ADDR_INVALID)
+				auto_enable = true;
+		}
+	}
+
+	/* Enable the pipe data flow */
+	if (pipe->client_state == SPS_STATE_CONNECT &&
+	    !(state == SPS_STATE_DISABLE
+	      || state == SPS_STATE_DISCONNECT)
+	    && (state == SPS_STATE_ENABLE || auto_enable
+		|| (pipe->connect.options & SPS_O_AUTO_ENABLE))) {
+		result = sps_bam_pipe_enable(pipe->bam, pipe->pipe_index);
+		if (result) {
+			SPS_ERR("sps:Failed to set BAM 0x%x pipe %d flow on",
+				pipe->bam->props.phys_addr,
+				pipe->pipe_index);
+			return SPS_ERROR;
+		}
+
+		/* Is this a BAM-DMA pipe? */
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+		if ((pipe->bam->props.options & SPS_BAM_OPT_BAMDMA)) {
+			/* Activate the BAM-DMA channel */
+			result = sps_dma_pipe_enable(pipe->bam,
+						     pipe->pipe_index);
+			if (result) {
+				SPS_ERR("sps:Failed to activate BAM-DMA"
+					" pipe: %d", pipe->pipe_index);
+				return SPS_ERROR;
+			}
+		}
+#endif
+		pipe->client_state = SPS_STATE_ENABLE;
+	}
+
+	/* Disable the pipe data flow */
+	if (pipe->client_state == SPS_STATE_ENABLE &&
+	    (state == SPS_STATE_DISABLE	|| state == SPS_STATE_DISCONNECT)) {
+		result = sps_bam_pipe_disable(pipe->bam, pipe->pipe_index);
+		if (result) {
+			SPS_ERR("sps:Failed to set BAM 0x%x pipe %d flow off",
+				pipe->bam->props.phys_addr,
+				pipe->pipe_index);
+			return SPS_ERROR;
+		}
+		pipe->client_state = SPS_STATE_CONNECT;
+	}
+
+	/* Disconnect the BAM pipe */
+	if (pipe->client_state == SPS_STATE_CONNECT &&
+	    state == SPS_STATE_DISCONNECT) {
+		struct sps_connection *map;
+		u32 pipe_index;
+
+		if (pipe->connect.mode == SPS_MODE_SRC)
+			pipe_index = pipe->map->src.pipe_index;
+		else
+			pipe_index = pipe->map->dest.pipe_index;
+
+
+		result = sps_bam_pipe_disconnect(pipe->bam, pipe_index);
+		if (result) {
+			SPS_ERR("sps:Failed to disconnect BAM 0x%x pipe %d",
+				pipe->bam->props.phys_addr,
+				pipe->pipe_index);
+			return SPS_ERROR;
+		}
+
+		/* Clear map state */
+		map = (void *)pipe->map;
+		if (pipe->connect.mode == SPS_MODE_SRC)
+			map->client_src = NULL;
+		else if (pipe->connect.mode == SPS_MODE_DEST)
+			map->client_dest = NULL;
+
+		sps_rm_disconnect(pipe);
+
+		/* Clear the client state */
+		pipe->map = NULL;
+		pipe->bam = NULL;
+		pipe->client_state = SPS_STATE_DISCONNECT;
+	}
+
+	return 0;
+}
diff --git a/drivers/platform/msm/sps/spsi.h b/drivers/platform/msm/sps/spsi.h
new file mode 100644
index 0000000..e8ab832
--- /dev/null
+++ b/drivers/platform/msm/sps/spsi.h
@@ -0,0 +1,396 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/**
+ * Smart-Peripheral-Switch (SPS) internal API.
+ */
+
+#ifndef _SPSI_H_
+#define _SPSI_H_
+
+#include <linux/types.h>	/* u32 */
+#include <linux/list.h>		/* list_head */
+#include <linux/kernel.h>	/* pr_info() */
+#include <linux/compiler.h>
+#include <linux/ratelimit.h>
+
+#include <mach/sps.h>
+
+#include "sps_map.h"
+
+#define BAM_MAX_PIPES              31
+#define BAM_MAX_P_LOCK_GROUP_NUM   31
+
+/* Adjust for offset of struct sps_q_event */
+#define SPS_EVENT_INDEX(e)    ((e) - 1)
+#define SPS_ERROR -1
+
+/* BAM identifier used in log messages */
+#define BAM_ID(dev)       ((dev)->props.phys_addr)
+
+/* "Clear" value for the connection parameter struct */
+#define SPSRM_CLEAR     0xcccccccc
+
+#ifdef CONFIG_DEBUG_FS
+extern u8 debugfs_record_enabled;
+extern u8 logging_option;
+extern u8 debug_level_option;
+extern u8 print_limit_option;
+
+#define MAX_MSG_LEN 80
+#define SPS_DEBUGFS(msg, args...) do {					\
+		char buf[MAX_MSG_LEN];		\
+		snprintf(buf, MAX_MSG_LEN, msg"\n", ##args);	\
+		sps_debugfs_record(buf);	\
+	} while (0)
+#define SPS_ERR(msg, args...) do {					\
+		if (unlikely(print_limit_option > 2))	\
+			pr_err_ratelimited(msg, ##args);	\
+		else	\
+			pr_err(msg, ##args);	\
+		if (unlikely(debugfs_record_enabled))	\
+			SPS_DEBUGFS(msg, ##args);	\
+	} while (0)
+#define SPS_INFO(msg, args...) do {					\
+		if (unlikely(print_limit_option > 1))	\
+			pr_info_ratelimited(msg, ##args);	\
+		else	\
+			pr_info(msg, ##args);	\
+		if (unlikely(debugfs_record_enabled))	\
+			SPS_DEBUGFS(msg, ##args);	\
+	} while (0)
+#define SPS_DBG(msg, args...) do {					\
+		if ((unlikely(logging_option > 1))	\
+			&& (unlikely(debug_level_option > 3))) {\
+			if (unlikely(print_limit_option > 0))	\
+				pr_info_ratelimited(msg, ##args);	\
+			else	\
+				pr_info(msg, ##args);	\
+		} else	\
+			pr_debug(msg, ##args);	\
+		if (unlikely(debugfs_record_enabled))	\
+			SPS_DEBUGFS(msg, ##args);	\
+	} while (0)
+#define SPS_DBG1(msg, args...) do {					\
+		if ((unlikely(logging_option > 1))	\
+			&& (unlikely(debug_level_option > 2))) {\
+			if (unlikely(print_limit_option > 0))	\
+				pr_info_ratelimited(msg, ##args);	\
+			else	\
+				pr_info(msg, ##args);	\
+		} else	\
+			pr_debug(msg, ##args);	\
+		if (unlikely(debugfs_record_enabled))	\
+			SPS_DEBUGFS(msg, ##args);	\
+	} while (0)
+#define SPS_DBG2(msg, args...) do {					\
+		if ((unlikely(logging_option > 1))	\
+			&& (unlikely(debug_level_option > 1))) {\
+			if (unlikely(print_limit_option > 0))	\
+				pr_info_ratelimited(msg, ##args);	\
+			else	\
+				pr_info(msg, ##args);	\
+		} else	\
+			pr_debug(msg, ##args);	\
+		if (unlikely(debugfs_record_enabled))	\
+			SPS_DEBUGFS(msg, ##args);	\
+	} while (0)
+#define SPS_DBG3(msg, args...) do {					\
+		if ((unlikely(logging_option > 1))	\
+			&& (unlikely(debug_level_option > 0))) {\
+			if (unlikely(print_limit_option > 0))	\
+				pr_info_ratelimited(msg, ##args);	\
+			else	\
+				pr_info(msg, ##args);	\
+		} else	\
+			pr_debug(msg, ##args);	\
+		if (unlikely(debugfs_record_enabled))	\
+			SPS_DEBUGFS(msg, ##args);	\
+	} while (0)
+#else
+#define	SPS_DBG3(x...)		pr_debug(x)
+#define	SPS_DBG2(x...)		pr_debug(x)
+#define	SPS_DBG1(x...)		pr_debug(x)
+#define	SPS_DBG(x...)		pr_debug(x)
+#define	SPS_INFO(x...)		pr_info(x)
+#define	SPS_ERR(x...)		pr_err(x)
+#endif
+
+/* End point parameters */
+struct sps_conn_end_pt {
+	u32 dev;		/* Device handle of BAM */
+	u32 bam_phys;		/* Physical address of BAM. */
+	u32 pipe_index;		/* Pipe index */
+	u32 event_threshold;	/* Pipe event threshold */
+	u32 lock_group;	/* The lock group this pipe belongs to */
+	void *bam;
+};
+
+/* Connection bookkeeping descriptor struct */
+struct sps_connection {
+	struct list_head list;
+
+	/* Source end point parameters */
+	struct sps_conn_end_pt src;
+
+	/* Destination end point parameters */
+	struct sps_conn_end_pt dest;
+
+	/* Resource parameters */
+	struct sps_mem_buffer desc;	/* Descriptor FIFO */
+	struct sps_mem_buffer data;	/* Data FIFO (BAM-to-BAM mode only) */
+	u32 config;		/* Client specified connection configuration */
+
+	/* Connection state */
+	void *client_src;
+	void *client_dest;
+	int refs;		/* Reference counter */
+
+	/* Dynamically allocated resouces, if required */
+	u32 alloc_src_pipe;	/* Source pipe index */
+	u32 alloc_dest_pipe;	/* Destination pipe index */
+	u32 alloc_desc_base;	/* Physical address of descriptor FIFO */
+	u32 alloc_data_base;	/* Physical address of data FIFO */
+};
+
+/* Event bookkeeping descriptor struct */
+struct sps_q_event {
+	struct list_head list;
+	/* Event payload data */
+	struct sps_event_notify notify;
+};
+
+/* Memory heap statistics */
+struct sps_mem_stats {
+	u32 base_addr;
+	u32 size;
+	u32 blocks_used;
+	u32 bytes_used;
+	u32 max_bytes_used;
+};
+
+#ifdef CONFIG_DEBUG_FS
+/* record debug info for debugfs */
+void sps_debugfs_record(const char *);
+
+/* output the content of BAM-level registers */
+void print_bam_reg(void *);
+
+/* output the content of BAM pipe registers */
+void print_bam_pipe_reg(void *, u32);
+
+/* output the content of selected BAM-level registers */
+void print_bam_selected_reg(void *);
+
+/* output the content of selected BAM pipe registers */
+void print_bam_pipe_selected_reg(void *, u32);
+
+/* output descriptor FIFO of a pipe */
+void print_bam_pipe_desc_fifo(void *, u32);
+#endif
+
+/**
+ * Translate physical to virtual address
+ *
+ * This Function translates physical to virtual address.
+ *
+ * @phys_addr - physical address to translate
+ *
+ * @return virtual memory pointer
+ *
+ */
+void *spsi_get_mem_ptr(u32 phys_addr);
+
+/**
+ * Allocate I/O (pipe) memory
+ *
+ * This function allocates target I/O (pipe) memory.
+ *
+ * @bytes - number of bytes to allocate
+ *
+ * @return physical address of allocated memory, or SPS_ADDR_INVALID on error
+ */
+u32 sps_mem_alloc_io(u32 bytes);
+
+/**
+ * Free I/O (pipe) memory
+ *
+ * This function frees target I/O (pipe) memory.
+ *
+ * @phys_addr - physical address of memory to free
+ *
+ * @bytes - number of bytes to free.
+ */
+void sps_mem_free_io(u32 phys_addr, u32 bytes);
+
+/**
+ * Find matching connection mapping
+ *
+ * This function searches for a connection mapping that matches the
+ * parameters supplied by the client.  If a match is found, the client's
+ * parameter struct is updated with the values specified in the mapping.
+ *
+ * @connect - pointer to client connection parameters
+ *
+ * @return 0 if match is found, negative value otherwise
+ *
+ */
+int sps_map_find(struct sps_connect *connect);
+
+/**
+ * Allocate a BAM DMA pipe
+ *
+ * This function allocates a BAM DMA pipe, and is intended to be called
+ * internally from the BAM resource manager.  Allocation implies that
+ * the pipe has been referenced by a client Connect() and is in use.
+ *
+ * BAM DMA is permissive with activations, and allows a pipe to be allocated
+ * with or without a client-initiated allocation.  This allows the client to
+ * specify exactly which pipe should be used directly through the Connect() API.
+ * sps_dma_alloc_chan() does not allow the client to specify the pipes/channel.
+ *
+ * @bam - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @dir - pipe direction
+ *
+ * @return 0 on success, negative value on error
+ */
+int sps_dma_pipe_alloc(void *bam, u32 pipe_index, enum sps_mode dir);
+
+/**
+ * Enable a BAM DMA pipe
+ *
+ * This function enables the channel associated with a BAM DMA pipe, and
+ * is intended to be called internally from the BAM resource manager.
+ * Enable must occur *after* the pipe has been enabled so that proper
+ * sequencing between pipe and DMA channel enables can be enforced.
+ *
+ * @bam - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_dma_pipe_enable(void *bam, u32 pipe_index);
+
+/**
+ * Free a BAM DMA pipe
+ *
+ * This function disables and frees a BAM DMA pipe, and is intended to be
+ * called internally from the BAM resource manager.  This must occur *after*
+ * the pipe has been disabled/reset so that proper sequencing between pipe and
+ * DMA channel resets can be enforced.
+ *
+ * @bam_arg - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_dma_pipe_free(void *bam, u32 pipe_index);
+
+/**
+ * Initialize driver memory module
+ *
+ * This function initializes the driver memory module.
+ *
+ * @pipemem_phys_base - Pipe-Memory physical base.
+ *
+ * @pipemem_size - Pipe-Memory size.
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_mem_init(u32 pipemem_phys_base, u32 pipemem_size);
+
+/**
+ * De-initialize driver memory module
+ *
+ * This function de-initializes the driver memory module.
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_mem_de_init(void);
+
+/**
+ * Initialize BAM DMA module
+ *
+ * This function initializes the BAM DMA module.
+ *
+ * @bam_props - pointer to BAM DMA devices BSP configuration properties
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_dma_init(const struct sps_bam_props *bam_props);
+
+/**
+ * De-initialize BAM DMA module
+ *
+ * This function de-initializes the SPS BAM DMA module.
+ *
+ */
+void sps_dma_de_init(void);
+
+/**
+ * Initialize BAM DMA device
+ *
+ * This function initializes a BAM DMA device.
+ *
+ * @h - BAM handle
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_dma_device_init(u32 h);
+
+/**
+ * De-initialize BAM DMA device
+ *
+ * This function de-initializes a BAM DMA device.
+ *
+ * @h - BAM handle
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_dma_device_de_init(u32 h);
+
+/**
+ * Initialize connection mapping module
+ *
+ * This function initializes the SPS connection mapping module.
+ *
+ * @map_props - pointer to connection mapping BSP configuration properties
+ *
+ * @options - driver options bitflags (see SPS_OPT_*)
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+
+int sps_map_init(const struct sps_map *map_props, u32 options);
+
+/**
+ * De-initialize connection mapping module
+ *
+ * This function de-initializes the SPS connection mapping module.
+ *
+ */
+void sps_map_de_init(void);
+
+#endif	/* _SPSI_H_ */
diff --git a/drivers/platform/msm/ssbi.c b/drivers/platform/msm/ssbi.c
new file mode 100644
index 0000000..265d56f
--- /dev/null
+++ b/drivers/platform/msm/ssbi.c
@@ -0,0 +1,441 @@
+/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2010, Google Inc.
+ *
+ * Original authors: Code Aurora Forum
+ *
+ * Author: Dima Zavin <dima@android.com>
+ *  - Largely rewritten from original to not be an i2c driver.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/msm_ssbi.h>
+#include <linux/remote_spinlock.h>
+
+/* SSBI 2.0 controller registers */
+#define SSBI2_CMD			0x0008
+#define SSBI2_RD			0x0010
+#define SSBI2_STATUS			0x0014
+#define SSBI2_MODE2			0x001C
+
+/* SSBI_CMD fields */
+#define SSBI_CMD_RDWRN			(1 << 24)
+
+/* SSBI_STATUS fields */
+#define SSBI_STATUS_RD_READY		(1 << 2)
+#define SSBI_STATUS_READY		(1 << 1)
+#define SSBI_STATUS_MCHN_BUSY		(1 << 0)
+
+/* SSBI_MODE2 fields */
+#define SSBI_MODE2_REG_ADDR_15_8_SHFT	0x04
+#define SSBI_MODE2_REG_ADDR_15_8_MASK	(0x7f << SSBI_MODE2_REG_ADDR_15_8_SHFT)
+
+#define SET_SSBI_MODE2_REG_ADDR_15_8(MD, AD) \
+	(((MD) & 0x0F) | ((((AD) >> 8) << SSBI_MODE2_REG_ADDR_15_8_SHFT) & \
+	SSBI_MODE2_REG_ADDR_15_8_MASK))
+
+/* SSBI PMIC Arbiter command registers */
+#define SSBI_PA_CMD			0x0000
+#define SSBI_PA_RD_STATUS		0x0004
+
+/* SSBI_PA_CMD fields */
+#define SSBI_PA_CMD_RDWRN		(1 << 24)
+#define SSBI_PA_CMD_ADDR_MASK		0x7fff /* REG_ADDR_7_0, REG_ADDR_8_14*/
+
+/* SSBI_PA_RD_STATUS fields */
+#define SSBI_PA_RD_STATUS_TRANS_DONE	(1 << 27)
+#define SSBI_PA_RD_STATUS_TRANS_DENIED	(1 << 26)
+
+#define SSBI_TIMEOUT_US			100
+
+/* SSBI_FSM Read and Write commands for the FSM9xxx SSBI implementation */
+#define SSBI_FSM_CMD_REG_ADDR_SHFT  (0x08)
+
+#define SSBI_FSM_CMD_READ(AD) \
+	(SSBI_CMD_RDWRN | (((AD) & 0xFFFF) << SSBI_FSM_CMD_REG_ADDR_SHFT))
+
+#define SSBI_FSM_CMD_WRITE(AD, DT) \
+	((((AD) & 0xFFFF) << SSBI_FSM_CMD_REG_ADDR_SHFT) | ((DT) & 0xFF))
+
+struct msm_ssbi {
+	struct device		*dev;
+	struct device		*slave;
+	void __iomem		*base;
+	bool			 use_rlock;
+	spinlock_t		lock;
+	remote_spinlock_t	 rspin_lock;
+	enum msm_ssbi_controller_type controller_type;
+	int (*read)(struct msm_ssbi *, u16 addr, u8 *buf, int len);
+	int (*write)(struct msm_ssbi *, u16 addr, u8 *buf, int len);
+};
+
+#define to_msm_ssbi(dev)	platform_get_drvdata(to_platform_device(dev))
+
+static inline u32 ssbi_readl(struct msm_ssbi *ssbi, u32 reg)
+{
+	return readl(ssbi->base + reg);
+}
+
+static inline void ssbi_writel(struct msm_ssbi *ssbi, u32 val, u32 reg)
+{
+	writel(val, ssbi->base + reg);
+}
+
+static int ssbi_wait_mask(struct msm_ssbi *ssbi, u32 set_mask, u32 clr_mask)
+{
+	u32 timeout = SSBI_TIMEOUT_US;
+	u32 val;
+
+	while (timeout--) {
+		val = ssbi_readl(ssbi, SSBI2_STATUS);
+		if (((val & set_mask) == set_mask) && ((val & clr_mask) == 0))
+			return 0;
+		udelay(1);
+	}
+
+	dev_err(ssbi->dev, "%s: timeout (status %x set_mask %x clr_mask %x)\n",
+		__func__, ssbi_readl(ssbi, SSBI2_STATUS), set_mask, clr_mask);
+	return -ETIMEDOUT;
+}
+
+static int
+msm_ssbi_read_bytes(struct msm_ssbi *ssbi, u16 addr, u8 *buf, int len)
+{
+	u32 cmd = SSBI_CMD_RDWRN | ((addr & 0xff) << 16);
+	int ret = 0;
+
+	if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) {
+		u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2);
+		mode2 = SET_SSBI_MODE2_REG_ADDR_15_8(mode2, addr);
+		ssbi_writel(ssbi, mode2, SSBI2_MODE2);
+	}
+
+	if (ssbi->controller_type == FSM_SBI_CTRL_SSBI)
+		cmd = SSBI_FSM_CMD_READ(addr);
+	else
+		cmd = SSBI_CMD_RDWRN | ((addr & 0xff) << 16);
+
+	while (len) {
+		ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0);
+		if (ret)
+			goto err;
+
+		ssbi_writel(ssbi, cmd, SSBI2_CMD);
+		ret = ssbi_wait_mask(ssbi, SSBI_STATUS_RD_READY, 0);
+		if (ret)
+			goto err;
+		*buf++ = ssbi_readl(ssbi, SSBI2_RD) & 0xff;
+		len--;
+	}
+
+err:
+	return ret;
+}
+
+static int
+msm_ssbi_write_bytes(struct msm_ssbi *ssbi, u16 addr, u8 *buf, int len)
+{
+	int ret = 0;
+
+	if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) {
+		u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2);
+		mode2 = SET_SSBI_MODE2_REG_ADDR_15_8(mode2, addr);
+		ssbi_writel(ssbi, mode2, SSBI2_MODE2);
+	}
+
+	while (len) {
+		ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0);
+		if (ret)
+			goto err;
+
+		if (ssbi->controller_type == FSM_SBI_CTRL_SSBI)
+			ssbi_writel(ssbi, SSBI_FSM_CMD_WRITE(addr, *buf),
+				SSBI2_CMD);
+		else
+			ssbi_writel(ssbi, ((addr & 0xff) << 16) | *buf,
+				SSBI2_CMD);
+
+		ret = ssbi_wait_mask(ssbi, 0, SSBI_STATUS_MCHN_BUSY);
+		if (ret)
+			goto err;
+		buf++;
+		len--;
+	}
+
+err:
+	return ret;
+}
+
+static inline int
+msm_ssbi_pa_transfer(struct msm_ssbi *ssbi, u32 cmd, u8 *data)
+{
+	u32 timeout = SSBI_TIMEOUT_US;
+	u32 rd_status = 0;
+
+	ssbi_writel(ssbi, cmd, SSBI_PA_CMD);
+
+	while (timeout--) {
+		rd_status = ssbi_readl(ssbi, SSBI_PA_RD_STATUS);
+
+		if (rd_status & SSBI_PA_RD_STATUS_TRANS_DENIED) {
+			dev_err(ssbi->dev, "%s: transaction denied (0x%x)\n",
+					__func__, rd_status);
+			return -EPERM;
+		}
+
+		if (rd_status & SSBI_PA_RD_STATUS_TRANS_DONE) {
+			if (data)
+				*data = rd_status & 0xff;
+			return 0;
+		}
+		udelay(1);
+	}
+
+	dev_err(ssbi->dev, "%s: timeout, status 0x%x\n", __func__, rd_status);
+	return -ETIMEDOUT;
+}
+
+static int
+msm_ssbi_pa_read_bytes(struct msm_ssbi *ssbi, u16 addr, u8 *buf, int len)
+{
+	u32 cmd;
+	int ret = 0;
+
+	cmd = SSBI_PA_CMD_RDWRN | (addr & SSBI_PA_CMD_ADDR_MASK) << 8;
+
+	while (len) {
+		ret = msm_ssbi_pa_transfer(ssbi, cmd, buf);
+		if (ret)
+			goto err;
+		buf++;
+		len--;
+	}
+
+err:
+	return ret;
+}
+
+static int
+msm_ssbi_pa_write_bytes(struct msm_ssbi *ssbi, u16 addr, u8 *buf, int len)
+{
+	u32 cmd;
+	int ret = 0;
+
+	while (len) {
+		cmd = (addr & SSBI_PA_CMD_ADDR_MASK) << 8 | *buf;
+		ret = msm_ssbi_pa_transfer(ssbi, cmd, NULL);
+		if (ret)
+			goto err;
+		buf++;
+		len--;
+	}
+
+err:
+	return ret;
+}
+
+int msm_ssbi_read(struct device *dev, u16 addr, u8 *buf, int len)
+{
+	struct msm_ssbi *ssbi = to_msm_ssbi(dev);
+	unsigned long flags;
+	int ret;
+
+	if (ssbi->dev != dev)
+		return -ENXIO;
+
+	if (ssbi->use_rlock) {
+		remote_spin_lock_irqsave(&ssbi->rspin_lock, flags);
+		ret = ssbi->read(ssbi, addr, buf, len);
+		remote_spin_unlock_irqrestore(&ssbi->rspin_lock, flags);
+	} else {
+		spin_lock_irqsave(&ssbi->lock, flags);
+		ret = ssbi->read(ssbi, addr, buf, len);
+		spin_unlock_irqrestore(&ssbi->lock, flags);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(msm_ssbi_read);
+
+int msm_ssbi_write(struct device *dev, u16 addr, u8 *buf, int len)
+{
+	struct msm_ssbi *ssbi = to_msm_ssbi(dev);
+	unsigned long flags;
+	int ret;
+
+	if (ssbi->dev != dev)
+		return -ENXIO;
+
+	if (ssbi->use_rlock) {
+		remote_spin_lock_irqsave(&ssbi->rspin_lock, flags);
+		ret = ssbi->write(ssbi, addr, buf, len);
+		remote_spin_unlock_irqrestore(&ssbi->rspin_lock, flags);
+	} else {
+		spin_lock_irqsave(&ssbi->lock, flags);
+		ret = ssbi->write(ssbi, addr, buf, len);
+		spin_unlock_irqrestore(&ssbi->lock, flags);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(msm_ssbi_write);
+
+static int __devinit msm_ssbi_add_slave(struct msm_ssbi *ssbi,
+				const struct msm_ssbi_slave_info *slave)
+{
+	struct platform_device *slave_pdev;
+	int ret;
+
+	if (ssbi->slave) {
+		pr_err("slave already attached??\n");
+		return -EBUSY;
+	}
+
+	slave_pdev = platform_device_alloc(slave->name, -1);
+	if (!slave_pdev) {
+		pr_err("cannot allocate pdev for slave '%s'", slave->name);
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	slave_pdev->dev.parent = ssbi->dev;
+	slave_pdev->dev.platform_data = slave->platform_data;
+
+	ret = platform_device_add(slave_pdev);
+	if (ret) {
+		pr_err("cannot add slave platform device for '%s'\n",
+				slave->name);
+		goto err;
+	}
+
+	ssbi->slave = &slave_pdev->dev;
+	return 0;
+
+err:
+	if (slave_pdev)
+		platform_device_put(slave_pdev);
+	return ret;
+}
+
+static int __devinit msm_ssbi_probe(struct platform_device *pdev)
+{
+	const struct msm_ssbi_platform_data *pdata = pdev->dev.platform_data;
+	struct resource *mem_res;
+	struct msm_ssbi *ssbi;
+	int ret = 0;
+
+	if (!pdata) {
+		pr_err("missing platform data\n");
+		return -EINVAL;
+	}
+
+	pr_debug("%s\n", pdata->slave.name);
+
+	ssbi = kzalloc(sizeof(struct msm_ssbi), GFP_KERNEL);
+	if (!ssbi) {
+		pr_err("can not allocate ssbi_data\n");
+		return -ENOMEM;
+	}
+
+	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem_res) {
+		pr_err("missing mem resource\n");
+		ret = -EINVAL;
+		goto err_get_mem_res;
+	}
+
+	ssbi->base = ioremap(mem_res->start, resource_size(mem_res));
+	if (!ssbi->base) {
+		pr_err("ioremap of 0x%p failed\n", (void *)mem_res->start);
+		ret = -EINVAL;
+		goto err_ioremap;
+	}
+	ssbi->dev = &pdev->dev;
+	platform_set_drvdata(pdev, ssbi);
+
+	ssbi->controller_type = pdata->controller_type;
+	if (ssbi->controller_type == MSM_SBI_CTRL_PMIC_ARBITER) {
+		ssbi->read = msm_ssbi_pa_read_bytes;
+		ssbi->write = msm_ssbi_pa_write_bytes;
+	} else {
+		ssbi->read = msm_ssbi_read_bytes;
+		ssbi->write = msm_ssbi_write_bytes;
+	}
+
+	if (pdata->rsl_id) {
+		ret = remote_spin_lock_init(&ssbi->rspin_lock, pdata->rsl_id);
+		if (ret) {
+			dev_err(&pdev->dev, "remote spinlock init failed\n");
+			goto err_ssbi_add_slave;
+		}
+		ssbi->use_rlock = 1;
+	}
+
+	spin_lock_init(&ssbi->lock);
+
+	ret = msm_ssbi_add_slave(ssbi, &pdata->slave);
+	if (ret)
+		goto err_ssbi_add_slave;
+
+	return 0;
+
+err_ssbi_add_slave:
+	platform_set_drvdata(pdev, NULL);
+	iounmap(ssbi->base);
+err_ioremap:
+err_get_mem_res:
+	kfree(ssbi);
+	return ret;
+}
+
+static int __devexit msm_ssbi_remove(struct platform_device *pdev)
+{
+	struct msm_ssbi *ssbi = platform_get_drvdata(pdev);
+
+	platform_set_drvdata(pdev, NULL);
+	iounmap(ssbi->base);
+	kfree(ssbi);
+	return 0;
+}
+
+static struct platform_driver msm_ssbi_driver = {
+	.probe		= msm_ssbi_probe,
+	.remove		= __exit_p(msm_ssbi_remove),
+	.driver		= {
+		.name	= "msm_ssbi",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init msm_ssbi_init(void)
+{
+	return platform_driver_register(&msm_ssbi_driver);
+}
+postcore_initcall(msm_ssbi_init);
+
+static void __exit msm_ssbi_exit(void)
+{
+	platform_driver_unregister(&msm_ssbi_driver);
+}
+module_exit(msm_ssbi_exit)
+
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:msm_ssbi");
+MODULE_AUTHOR("Dima Zavin <dima@android.com>");
diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c
new file mode 100644
index 0000000..1df6d34
--- /dev/null
+++ b/drivers/platform/msm/usb_bam.c
@@ -0,0 +1,394 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/stat.h>
+#include <linux/module.h>
+#include <linux/usb/msm_hsusb.h>
+#include <mach/usb_bam.h>
+#include <mach/sps.h>
+#include <linux/workqueue.h>
+
+#define USB_SUMMING_THRESHOLD 512
+#define CONNECTIONS_NUM		4
+
+static struct sps_bam_props usb_props;
+static struct sps_pipe *sps_pipes[CONNECTIONS_NUM][2];
+static struct sps_connect sps_connections[CONNECTIONS_NUM][2];
+static struct sps_mem_buffer data_mem_buf[CONNECTIONS_NUM][2];
+static struct sps_mem_buffer desc_mem_buf[CONNECTIONS_NUM][2];
+static struct platform_device *usb_bam_pdev;
+static struct workqueue_struct *usb_bam_wq;
+
+struct usb_bam_wake_event_info {
+	struct sps_register_event event;
+	int (*callback)(void *);
+	void *param;
+	struct work_struct wake_w;
+};
+
+struct usb_bam_connect_info {
+	u8 idx;
+	u8 *src_pipe;
+	u8 *dst_pipe;
+	struct usb_bam_wake_event_info peer_event;
+	bool enabled;
+};
+
+static struct usb_bam_connect_info usb_bam_connections[CONNECTIONS_NUM];
+
+static inline int bam_offset(struct msm_usb_bam_platform_data *pdata)
+{
+	return pdata->usb_active_bam * CONNECTIONS_NUM * 2;
+}
+
+static int connect_pipe(u8 connection_idx, enum usb_bam_pipe_dir pipe_dir,
+						u8 *usb_pipe_idx)
+{
+	int ret;
+	struct sps_pipe **pipe = &sps_pipes[connection_idx][pipe_dir];
+	struct sps_connect *connection =
+		&sps_connections[connection_idx][pipe_dir];
+	struct msm_usb_bam_platform_data *pdata =
+		(struct msm_usb_bam_platform_data *)
+			(usb_bam_pdev->dev.platform_data);
+	struct usb_bam_pipe_connect *pipe_connection =
+			(struct usb_bam_pipe_connect *)(pdata->connections +
+			 bam_offset(pdata) + (2*connection_idx+pipe_dir));
+
+	*pipe = sps_alloc_endpoint();
+	if (*pipe == NULL) {
+		pr_err("%s: sps_alloc_endpoint failed\n", __func__);
+		return -ENOMEM;
+	}
+
+	ret = sps_get_config(*pipe, connection);
+	if (ret) {
+		pr_err("%s: tx get config failed %d\n", __func__, ret);
+		goto get_config_failed;
+	}
+
+	ret = sps_phy2h(pipe_connection->src_phy_addr, &(connection->source));
+	if (ret) {
+		pr_err("%s: sps_phy2h failed (src BAM) %d\n", __func__, ret);
+		goto get_config_failed;
+	}
+
+	connection->src_pipe_index = pipe_connection->src_pipe_index;
+	ret = sps_phy2h(pipe_connection->dst_phy_addr,
+					&(connection->destination));
+	if (ret) {
+		pr_err("%s: sps_phy2h failed (dst BAM) %d\n", __func__, ret);
+		goto get_config_failed;
+	}
+	connection->dest_pipe_index = pipe_connection->dst_pipe_index;
+
+	if (pipe_dir == USB_TO_PEER_PERIPHERAL) {
+		connection->mode = SPS_MODE_SRC;
+		*usb_pipe_idx = connection->src_pipe_index;
+	} else {
+		connection->mode = SPS_MODE_DEST;
+		*usb_pipe_idx = connection->dest_pipe_index;
+	}
+
+	ret = sps_setup_bam2bam_fifo(
+				&data_mem_buf[connection_idx][pipe_dir],
+				pipe_connection->data_fifo_base_offset,
+				pipe_connection->data_fifo_size, 1);
+	if (ret) {
+		pr_err("%s: data fifo setup failure %d\n", __func__, ret);
+		goto fifo_setup_error;
+	}
+	connection->data = data_mem_buf[connection_idx][pipe_dir];
+
+	ret = sps_setup_bam2bam_fifo(
+				&desc_mem_buf[connection_idx][pipe_dir],
+				pipe_connection->desc_fifo_base_offset,
+				pipe_connection->desc_fifo_size, 1);
+	if (ret) {
+		pr_err("%s: desc. fifo setup failure %d\n", __func__, ret);
+		goto fifo_setup_error;
+	}
+	connection->desc = desc_mem_buf[connection_idx][pipe_dir];
+	connection->event_thresh = 512;
+
+	ret = sps_connect(*pipe, connection);
+	if (ret < 0) {
+		pr_err("%s: tx connect error %d\n", __func__, ret);
+		goto error;
+	}
+	return 0;
+
+error:
+	sps_disconnect(*pipe);
+fifo_setup_error:
+get_config_failed:
+	sps_free_endpoint(*pipe);
+	return ret;
+}
+
+int usb_bam_connect(u8 idx, u8 *src_pipe_idx, u8 *dst_pipe_idx)
+{
+	struct usb_bam_connect_info *connection = &usb_bam_connections[idx];
+	int ret;
+
+	if (idx >= CONNECTIONS_NUM) {
+		pr_err("%s: Invalid connection index\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	if (connection->enabled) {
+		pr_info("%s: connection %d was already established\n",
+			__func__, idx);
+		return 0;
+	}
+	connection->src_pipe = src_pipe_idx;
+	connection->dst_pipe = dst_pipe_idx;
+	connection->idx = idx;
+
+	/* open USB -> Peripheral pipe */
+	ret = connect_pipe(connection->idx, USB_TO_PEER_PERIPHERAL,
+					   connection->src_pipe);
+	if (ret) {
+		pr_err("%s: src pipe connection failure\n", __func__);
+		return ret;
+	}
+	/* open Peripheral -> USB pipe */
+	ret = connect_pipe(connection->idx, PEER_PERIPHERAL_TO_USB,
+				 connection->dst_pipe);
+	if (ret) {
+		pr_err("%s: dst pipe connection failure\n", __func__);
+		return ret;
+	}
+	connection->enabled = 1;
+
+	return 0;
+}
+
+static void usb_bam_wake_work(struct work_struct *w)
+{
+	struct usb_bam_wake_event_info *wake_event_info =
+		container_of(w, struct usb_bam_wake_event_info, wake_w);
+
+	wake_event_info->callback(wake_event_info->param);
+}
+
+static void usb_bam_wake_cb(struct sps_event_notify *notify)
+{
+	struct usb_bam_wake_event_info *wake_event_info =
+		(struct usb_bam_wake_event_info *)notify->user;
+
+	queue_work(usb_bam_wq, &wake_event_info->wake_w);
+}
+
+int usb_bam_register_wake_cb(u8 idx,
+	int (*callback)(void *user), void* param)
+{
+	struct sps_pipe *pipe = sps_pipes[idx][PEER_PERIPHERAL_TO_USB];
+	struct sps_connect *sps_connection =
+		&sps_connections[idx][PEER_PERIPHERAL_TO_USB];
+	struct usb_bam_connect_info *connection = &usb_bam_connections[idx];
+	struct usb_bam_wake_event_info *wake_event_info =
+		&connection->peer_event;
+	int ret;
+
+	wake_event_info->param = param;
+	wake_event_info->callback = callback;
+	wake_event_info->event.mode = SPS_TRIGGER_CALLBACK;
+	wake_event_info->event.xfer_done = NULL;
+	wake_event_info->event.callback = callback ? usb_bam_wake_cb : NULL;
+	wake_event_info->event.user = wake_event_info;
+	wake_event_info->event.options = SPS_O_WAKEUP;
+	ret = sps_register_event(pipe, &wake_event_info->event);
+	if (ret) {
+		pr_err("%s: sps_register_event() failed %d\n", __func__, ret);
+		return ret;
+	}
+
+	sps_connection->options = callback ?
+		(SPS_O_AUTO_ENABLE | SPS_O_WAKEUP | SPS_O_WAKEUP_IS_ONESHOT) :
+			SPS_O_AUTO_ENABLE;
+	ret = sps_set_config(pipe, sps_connection);
+	if (ret) {
+		pr_err("%s: sps_set_config() failed %d\n", __func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int usb_bam_init(void)
+{
+	u32 h_usb;
+	int ret;
+	void *usb_virt_addr;
+	struct msm_usb_bam_platform_data *pdata =
+		(struct msm_usb_bam_platform_data *)
+			(usb_bam_pdev->dev.platform_data);
+	struct resource *res;
+	int irq;
+
+	res = platform_get_resource(usb_bam_pdev, IORESOURCE_MEM,
+						pdata->usb_active_bam);
+	if (!res) {
+		dev_err(&usb_bam_pdev->dev, "Unable to get memory resource\n");
+		return -ENODEV;
+	}
+
+	irq = platform_get_irq(usb_bam_pdev, pdata->usb_active_bam);
+	if (irq < 0) {
+		dev_err(&usb_bam_pdev->dev, "Unable to get IRQ resource\n");
+		return irq;
+	}
+
+	usb_virt_addr = ioremap(res->start, resource_size(res));
+	if (!usb_virt_addr) {
+		pr_err("%s: ioremap failed\n", __func__);
+		return -ENOMEM;
+	}
+	usb_props.phys_addr = res->start;
+	usb_props.virt_addr = usb_virt_addr;
+	usb_props.virt_size = resource_size(res);
+	usb_props.irq = irq;
+	usb_props.summing_threshold = USB_SUMMING_THRESHOLD;
+	usb_props.num_pipes = pdata->usb_bam_num_pipes;
+
+	ret = sps_register_bam_device(&usb_props, &h_usb);
+	if (ret < 0) {
+		pr_err("%s: register bam error %d\n", __func__, ret);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static char *bam_enable_strings[2] = {
+	[HSUSB_BAM] = "hsusb",
+	[HSIC_BAM]  = "hsic",
+};
+
+static ssize_t
+usb_bam_show_enable(struct device *dev, struct device_attribute *attr,
+		    char *buf)
+{
+	struct platform_device *pdev = container_of(dev, struct platform_device,
+						    dev);
+	struct msm_usb_bam_platform_data *pdata =
+		(struct msm_usb_bam_platform_data *)
+			(usb_bam_pdev->dev.platform_data);
+
+	if (!pdev || !pdata)
+		return 0;
+	return scnprintf(buf, PAGE_SIZE, "%s\n",
+			 bam_enable_strings[pdata->usb_active_bam]);
+}
+
+static ssize_t usb_bam_store_enable(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct platform_device *pdev = container_of(dev, struct platform_device,
+						    dev);
+	struct msm_usb_bam_platform_data *pdata =
+		(struct msm_usb_bam_platform_data *)
+			(usb_bam_pdev->dev.platform_data);
+	char str[10], *pstr;
+	int ret, i;
+
+	strlcpy(str, buf, sizeof(str));
+	pstr = strim(str);
+
+	for (i = 0; i < ARRAY_SIZE(bam_enable_strings); i++) {
+		if (!strncmp(pstr, bam_enable_strings[i], sizeof(str)))
+			pdata->usb_active_bam = i;
+	}
+
+	dev_dbg(&pdev->dev, "active_bam=%s\n",
+		bam_enable_strings[pdata->usb_active_bam]);
+
+	ret = usb_bam_init();
+	if (ret) {
+		dev_err(&pdev->dev, "failed to initialize usb bam\n");
+		return ret;
+	}
+
+	return count;
+}
+
+static DEVICE_ATTR(enable, S_IWUSR | S_IRUSR, usb_bam_show_enable,
+		   usb_bam_store_enable);
+
+static int usb_bam_probe(struct platform_device *pdev)
+{
+	int ret, i;
+
+	dev_dbg(&pdev->dev, "usb_bam_probe\n");
+
+	for (i = 0; i < CONNECTIONS_NUM; i++) {
+		usb_bam_connections[i].enabled = 0;
+		INIT_WORK(&usb_bam_connections[i].peer_event.wake_w,
+			usb_bam_wake_work);
+	}
+
+	if (!pdev->dev.platform_data) {
+		dev_err(&pdev->dev, "missing platform_data\n");
+		return -ENODEV;
+	}
+	usb_bam_pdev = pdev;
+
+	ret = device_create_file(&pdev->dev, &dev_attr_enable);
+	if (ret)
+		dev_err(&pdev->dev, "failed to create device file\n");
+
+	usb_bam_wq = alloc_workqueue("usb_bam_wq",
+		WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
+	if (!usb_bam_wq) {
+		pr_err("unable to create workqueue usb_bam_wq\n");
+		return -ENOMEM;
+	}
+
+	return ret;
+}
+
+static int usb_bam_remove(struct platform_device *pdev)
+{
+	destroy_workqueue(usb_bam_wq);
+
+	return 0;
+}
+
+static struct platform_driver usb_bam_driver = {
+	.probe = usb_bam_probe,
+	.remove = usb_bam_remove,
+	.driver = { .name = "usb_bam", },
+};
+
+static int __init init(void)
+{
+	return platform_driver_register(&usb_bam_driver);
+}
+module_init(init);
+
+static void __exit cleanup(void)
+{
+	platform_driver_unregister(&usb_bam_driver);
+}
+module_exit(cleanup);
+
+MODULE_DESCRIPTION("MSM USB BAM DRIVER");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 99dc29f..a263750 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -288,6 +288,133 @@
 	  Say Y to enable support for the battery charger control sysfs and
 	  platform data of MAX8998/LP3974 PMICs.
 
+config BATTERY_MSM
+	tristate "MSM battery"
+	depends on ARCH_MSM
+	default m
+	help
+	  Say Y to enable support for the battery in Qualcomm MSM.
+
+config BATTERY_MSM8X60
+	tristate "MSM8X60 battery"
+	select PMIC8XXX_BATTALARM
+	help
+	  Some MSM boards have dual charging paths to charge the battery.
+	  Say Y to enable support for the battery charging in
+	  such devices.
+
+config PM8058_CHARGER
+	tristate "pmic8058 charger"
+	depends on BATTERY_MSM8X60
+	depends on PMIC8058
+	help
+	  Say Y to enable support for battery charging from the pmic8058.
+	  pmic8058 provides a linear charging circuit connected to the usb
+	  cable on Qualcomm's msm8x60 surf board.
+
+config ISL9519_CHARGER
+	tristate "isl9519 charger"
+	depends on (BATTERY_MSM8X60 || PM8921_CHARGER)
+	depends on I2C
+	default n
+	help
+	  The isl9519q charger chip from intersil is connected to an external
+	  charger cable and is preferred way of charging the battery because
+	  of its high current rating.
+	  Choose Y if you are compiling for Qualcomm's msm8x60 surf/ffa board.
+
+config SMB137B_CHARGER
+	tristate "smb137b charger"
+	default n
+	depends on I2C
+	help
+	  The smb137b charger chip from summit is a switching mode based
+	  charging solution.
+	  Choose Y if you are compiling for Qualcomm's msm8x60 fluid board.
+	  To compile this driver as a module, choose M here: the module will
+	  be called smb137b.
+
+config SMB349_CHARGER
+	tristate "smb349 charger"
+	depends on I2C
+	help
+	  Say Y to enable support for the SMB349 switching mode based charger
+	  and sysfs. The driver supports controlling charger-enable and
+	  current limiting capabilities. The driver also lets the
+	  SMB349 be operated as a slave device via the power supply
+	  framework.
+
+config BATTERY_MSM_FAKE
+	tristate "Fake MSM battery"
+	depends on ARCH_MSM && BATTERY_MSM
+	default n
+	help
+	  Say Y to bypass actual battery queries.
+
+config PM8058_FIX_USB
+	tristate "pmic8058 software workaround for usb removal"
+	depends on PMIC8058
+	depends on !PM8058_CHARGER
+	help
+	  Say Y to enable the software workaround to USB Vbus line
+	  staying high even when USB cable is removed. This option
+	  is in lieu of a complete pm8058 charging driver.
+
+config BATTERY_QCIBAT
+	tristate "Quanta Computer Inc. Battery"
+	depends on SENSORS_WPCE775X
+	default n
+	help
+	  Say Y here if you want to use the Quanta battery driver for ST15
+	  platform.
+
+config BATTERY_BQ27520
+	tristate "BQ27520 battery driver"
+	depends on I2C
+	default n
+	help
+	  Say Y here to enable support for batteries with BQ27520 (I2C) chips.
+
+config BATTERY_BQ27541
+	tristate "BQ27541 battery driver"
+	depends on I2C
+	default n
+	help
+	  Say Y here to enable support for batteries with BQ27541 (I2C) chips.
+
+config BQ27520_TEST_ENABLE
+	bool "Enable BQ27520 Fuel Gauge Chip Test"
+	depends on BATTERY_BQ27520
+	default n
+	help
+	  Say Y here to enable Test sysfs Interface for BQ27520 Drivers.
+
+config PM8921_CHARGER
+	tristate "PM8921 Charger driver"
+	depends on MFD_PM8921_CORE
+	help
+	  Say Y here to enable support for pm8921 chip charger subdevice
+
+config PM8XXX_CCADC
+	tristate "PM8XXX battery current adc driver"
+	depends on MFD_PM8921_CORE
+	help
+	  Say Y here to enable support for pm8921 chip bms subdevice
+
+config LTC4088_CHARGER
+	tristate "LTC4088 Charger driver"
+	depends on GPIOLIB
+	help
+	  Say Y here to enable support for ltc4088 chip charger. It controls the
+	  operations through GPIO pins.
+
+config PM8921_BMS
+	select PM8XXX_CCADC
+	tristate "PM8921 Battery Monitoring System driver"
+	depends on MFD_PM8921_CORE
+	help
+	  Say Y here to enable support for pm8921 chip bms subdevice
+
 config CHARGER_SMB347
 	tristate "Summit Microelectronics SMB347 Battery Charger"
 	depends on I2C
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index b6b2434..007d75b 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -43,4 +43,18 @@
 obj-$(CONFIG_CHARGER_MANAGER)	+= charger-manager.o
 obj-$(CONFIG_CHARGER_MAX8997)	+= max8997_charger.o
 obj-$(CONFIG_CHARGER_MAX8998)	+= max8998_charger.o
+obj-$(CONFIG_BATTERY_MSM)       += msm_battery.o
+obj-$(CONFIG_BATTERY_MSM8X60)   += msm_charger.o
+obj-$(CONFIG_PM8058_CHARGER)    += pmic8058-charger.o
+obj-$(CONFIG_ISL9519_CHARGER)   += isl9519q.o
+obj-$(CONFIG_SMB349_CHARGER)   += smb349.o
+obj-$(CONFIG_PM8058_FIX_USB)    += pm8058_usb_fix.o
+obj-$(CONFIG_BATTERY_QCIBAT)    += qci_battery.o
+obj-$(CONFIG_BATTERY_BQ27520)	+= bq27520_fuelgauger.o
+obj-$(CONFIG_BATTERY_BQ27541)	+= bq27541_fuelgauger.o
+obj-$(CONFIG_SMB137B_CHARGER)   += smb137b.o
+obj-$(CONFIG_PM8XXX_CCADC)	+= pm8xxx-ccadc.o
+obj-$(CONFIG_PM8921_BMS)	+= pm8921-bms.o
+obj-$(CONFIG_PM8921_CHARGER)	+= pm8921-charger.o
+obj-$(CONFIG_LTC4088_CHARGER)	+= ltc4088-charger.o
 obj-$(CONFIG_CHARGER_SMB347)	+= smb347-charger.o
diff --git a/drivers/power/bq27520_fuelgauger.c b/drivers/power/bq27520_fuelgauger.c
new file mode 100644
index 0000000..3c191cd
--- /dev/null
+++ b/drivers/power/bq27520_fuelgauger.c
@@ -0,0 +1,960 @@
+/* Copyright (C) 2008 Rodolfo Giometti <giometti@linux.it>
+ * Copyright (C) 2008 Eurotech S.p.A. <info@eurotech.it>
+ * Based on a previous work by Copyright (C) 2008 Texas Instruments, Inc.
+ *
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/param.h>
+#include <linux/jiffies.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/idr.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+#include <linux/time.h>
+#include <linux/i2c/bq27520.h>
+#include <linux/mfd/pmic8058.h>
+#include <linux/regulator/pmic8058-regulator.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/machine.h>
+#include <linux/err.h>
+#include <linux/msm-charger.h>
+
+#define DRIVER_VERSION			"1.1.0"
+/* Bq27520 standard data commands */
+#define BQ27520_REG_CNTL		0x00
+#define BQ27520_REG_AR			0x02
+#define BQ27520_REG_ARTTE		0x04
+#define BQ27520_REG_TEMP		0x06
+#define BQ27520_REG_VOLT		0x08
+#define BQ27520_REG_FLAGS		0x0A
+#define BQ27520_REG_NAC			0x0C
+#define BQ27520_REG_FAC			0x0e
+#define BQ27520_REG_RM			0x10
+#define BQ27520_REG_FCC			0x12
+#define BQ27520_REG_AI			0x14
+#define BQ27520_REG_TTE			0x16
+#define BQ27520_REG_TTF			0x18
+#define BQ27520_REG_SI			0x1a
+#define BQ27520_REG_STTE		0x1c
+#define BQ27520_REG_MLI			0x1e
+#define BQ27520_REG_MLTTE		0x20
+#define BQ27520_REG_AE			0x22
+#define BQ27520_REG_AP			0x24
+#define BQ27520_REG_TTECP		0x26
+#define BQ27520_REG_SOH			0x28
+#define BQ27520_REG_SOC			0x2c
+#define BQ27520_REG_NIC			0x2e
+#define BQ27520_REG_ICR			0x30
+#define BQ27520_REG_LOGIDX		0x32
+#define BQ27520_REG_LOGBUF		0x34
+#define BQ27520_FLAG_DSC		BIT(0)
+#define BQ27520_FLAG_FC			BIT(9)
+#define BQ27520_FLAG_BAT_DET		BIT(3)
+#define BQ27520_CS_DLOGEN		BIT(15)
+#define BQ27520_CS_SS		    BIT(13)
+/* Control subcommands */
+#define BQ27520_SUBCMD_CTNL_STATUS  0x0000
+#define BQ27520_SUBCMD_DEVCIE_TYPE  0x0001
+#define BQ27520_SUBCMD_FW_VER  0x0002
+#define BQ27520_SUBCMD_HW_VER  0x0003
+#define BQ27520_SUBCMD_DF_CSUM  0x0004
+#define BQ27520_SUBCMD_PREV_MACW   0x0007
+#define BQ27520_SUBCMD_CHEM_ID   0x0008
+#define BQ27520_SUBCMD_BD_OFFSET   0x0009
+#define BQ27520_SUBCMD_INT_OFFSET  0x000a
+#define BQ27520_SUBCMD_CC_VER   0x000b
+#define BQ27520_SUBCMD_OCV  0x000c
+#define BQ27520_SUBCMD_BAT_INS   0x000d
+#define BQ27520_SUBCMD_BAT_REM   0x000e
+#define BQ27520_SUBCMD_SET_HIB   0x0011
+#define BQ27520_SUBCMD_CLR_HIB   0x0012
+#define BQ27520_SUBCMD_SET_SLP   0x0013
+#define BQ27520_SUBCMD_CLR_SLP   0x0014
+#define BQ27520_SUBCMD_FCT_RES   0x0015
+#define BQ27520_SUBCMD_ENABLE_DLOG  0x0018
+#define BQ27520_SUBCMD_DISABLE_DLOG 0x0019
+#define BQ27520_SUBCMD_SEALED   0x0020
+#define BQ27520_SUBCMD_ENABLE_IT    0x0021
+#define BQ27520_SUBCMD_DISABLE_IT   0x0023
+#define BQ27520_SUBCMD_CAL_MODE  0x0040
+#define BQ27520_SUBCMD_RESET   0x0041
+
+#define ZERO_DEGREE_CELSIUS_IN_TENTH_KELVIN   (-2731)
+#define BQ27520_INIT_DELAY ((HZ)*1)
+#define BQ27520_POLLING_STATUS ((HZ)*3)
+#define BQ27520_COULOMB_POLL ((HZ)*30)
+
+/* If the system has several batteries we need a different name for each
+ * of them...
+ */
+static DEFINE_IDR(battery_id);
+static DEFINE_MUTEX(battery_mutex);
+
+struct bq27520_device_info;
+struct bq27520_access_methods {
+	int (*read)(u8 reg, int *rt_value, int b_single,
+		struct bq27520_device_info *di);
+};
+
+struct bq27520_device_info {
+	struct device				*dev;
+	int					id;
+	struct bq27520_access_methods		*bus;
+	struct i2c_client			*client;
+	const struct bq27520_platform_data	*pdata;
+	struct work_struct			counter;
+	/* 300ms delay is needed after bq27520 is powered up
+	 * and before any successful I2C transaction
+	 */
+	struct  delayed_work			hw_config;
+	uint32_t				irq;
+};
+
+enum {
+	GET_BATTERY_STATUS,
+	GET_BATTERY_TEMPERATURE,
+	GET_BATTERY_VOLTAGE,
+	GET_BATTERY_CAPACITY,
+	NUM_OF_STATUS,
+};
+
+struct bq27520_status {
+	/* Informations owned and maintained by Bq27520 driver, updated
+	 * by poller or SOC_INT interrupt, decoupling from I/Oing
+	 * hardware directly
+	 */
+	int			status[NUM_OF_STATUS];
+	spinlock_t		lock;
+	struct delayed_work	poller;
+};
+
+static struct bq27520_status current_battery_status;
+static struct bq27520_device_info *bq27520_di;
+static int coulomb_counter;
+static spinlock_t lock; /* protect access to coulomb_counter */
+static struct timer_list timer; /* charge counter timer every 30 secs */
+
+static int bq27520_i2c_txsubcmd(u8 reg, unsigned short subcmd,
+		struct bq27520_device_info *di);
+
+static int bq27520_read(u8 reg, int *rt_value, int b_single,
+			struct bq27520_device_info *di)
+{
+	return di->bus->read(reg, rt_value, b_single, di);
+}
+
+/*
+ * Return the battery temperature in tenths of degree Celsius
+ * Or < 0 if something fails.
+ */
+static int bq27520_battery_temperature(struct bq27520_device_info *di)
+{
+	int ret, temp = 0;
+
+	ret = bq27520_read(BQ27520_REG_TEMP, &temp, 0, di);
+	if (ret) {
+		dev_err(di->dev, "error %d reading temperature\n", ret);
+		return ret;
+	}
+
+	return temp + ZERO_DEGREE_CELSIUS_IN_TENTH_KELVIN;
+}
+
+/*
+ * Return the battery Voltage in milivolts
+ * Or < 0 if something fails.
+ */
+static int bq27520_battery_voltage(struct bq27520_device_info *di)
+{
+	int ret, volt = 0;
+
+	ret = bq27520_read(BQ27520_REG_VOLT, &volt, 0, di);
+	if (ret) {
+		dev_err(di->dev, "error %d reading voltage\n", ret);
+		return ret;
+	}
+
+	return volt;
+}
+
+/*
+ * Return the battery Relative State-of-Charge
+ * Or < 0 if something fails.
+ */
+static int bq27520_battery_rsoc(struct bq27520_device_info *di)
+{
+	int ret, rsoc = 0;
+
+	ret = bq27520_read(BQ27520_REG_SOC, &rsoc, 0, di);
+
+	if (ret) {
+		dev_err(di->dev,
+			"error %d reading relative State-of-Charge\n", ret);
+		return ret;
+	}
+
+	return rsoc;
+}
+
+static void bq27520_cntl_cmd(struct bq27520_device_info *di,
+				int subcmd)
+{
+	bq27520_i2c_txsubcmd(BQ27520_REG_CNTL, subcmd, di);
+}
+
+/*
+ * i2c specific code
+ */
+static int bq27520_i2c_txsubcmd(u8 reg, unsigned short subcmd,
+		struct bq27520_device_info *di)
+{
+	struct i2c_msg msg;
+	unsigned char data[3];
+
+	if (!di->client)
+		return -ENODEV;
+
+	memset(data, 0, sizeof(data));
+	data[0] = reg;
+	data[1] = subcmd & 0x00FF;
+	data[2] = (subcmd & 0xFF00) >> 8;
+
+	msg.addr = di->client->addr;
+	msg.flags = 0;
+	msg.len = 3;
+	msg.buf = data;
+
+	if (i2c_transfer(di->client->adapter, &msg, 1) < 0)
+		return -EIO;
+
+	return 0;
+}
+
+static int bq27520_chip_config(struct bq27520_device_info *di)
+{
+	int flags = 0, ret = 0;
+
+	bq27520_cntl_cmd(di, BQ27520_SUBCMD_CTNL_STATUS);
+	udelay(66);
+	ret = bq27520_read(BQ27520_REG_CNTL, &flags, 0, di);
+	if (ret < 0) {
+		dev_err(di->dev, "error %d reading register %02x\n",
+			 ret, BQ27520_REG_CNTL);
+		return ret;
+	}
+	udelay(66);
+
+	bq27520_cntl_cmd(di, BQ27520_SUBCMD_ENABLE_IT);
+	udelay(66);
+
+	if (di->pdata->enable_dlog && !(flags & BQ27520_CS_DLOGEN)) {
+		bq27520_cntl_cmd(di, BQ27520_SUBCMD_ENABLE_DLOG);
+		udelay(66);
+	}
+
+	return 0;
+}
+
+static void bq27520_every_30secs(unsigned long data)
+{
+	struct bq27520_device_info *di = (struct bq27520_device_info *)data;
+
+	schedule_work(&di->counter);
+	mod_timer(&timer, jiffies + BQ27520_COULOMB_POLL);
+}
+
+static void bq27520_coulomb_counter_work(struct work_struct *work)
+{
+	int value = 0, temp = 0, index = 0, ret = 0, count = 0;
+	struct bq27520_device_info *di;
+	unsigned long flags;
+
+	di = container_of(work, struct bq27520_device_info, counter);
+
+	/* retrieve 30 values from FIFO of coulomb data logging buffer
+	 * and average over time
+	 */
+	do {
+		ret = bq27520_read(BQ27520_REG_LOGBUF, &temp, 0, di);
+		if (ret < 0)
+			break;
+		if (temp != 0x7FFF) {
+			++count;
+			value += temp;
+		}
+		udelay(66);
+		ret = bq27520_read(BQ27520_REG_LOGIDX, &index, 0, di);
+		if (ret < 0)
+			break;
+		udelay(66);
+	} while (index != 0 || temp != 0x7FFF);
+
+	if (ret < 0) {
+		dev_err(di->dev, "Error %d reading datalog register\n", ret);
+		return;
+	}
+
+	if (count) {
+		spin_lock_irqsave(&lock, flags);
+		coulomb_counter = value/count;
+		spin_unlock_irqrestore(&lock, flags);
+	}
+}
+
+static int bq27520_is_battery_present(void)
+{
+	return 1;
+}
+
+static int bq27520_is_battery_temp_within_range(void)
+{
+	return 1;
+}
+
+static int bq27520_is_battery_id_valid(void)
+{
+	return 1;
+}
+
+static int bq27520_status_getter(int function)
+{
+	int status = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&current_battery_status.lock, flags);
+	status = current_battery_status.status[function];
+	spin_unlock_irqrestore(&current_battery_status.lock, flags);
+
+	return status;
+}
+
+static int bq27520_get_battery_mvolts(void)
+{
+	return bq27520_status_getter(GET_BATTERY_VOLTAGE);
+}
+
+static int bq27520_get_battery_temperature(void)
+{
+	return bq27520_status_getter(GET_BATTERY_TEMPERATURE);
+}
+
+static int bq27520_get_battery_status(void)
+{
+	return bq27520_status_getter(GET_BATTERY_STATUS);
+}
+
+static int bq27520_get_remaining_capacity(void)
+{
+	return bq27520_status_getter(GET_BATTERY_CAPACITY);
+}
+
+static struct msm_battery_gauge bq27520_batt_gauge = {
+	.get_battery_mvolts		= bq27520_get_battery_mvolts,
+	.get_battery_temperature	= bq27520_get_battery_temperature,
+	.is_battery_present		= bq27520_is_battery_present,
+	.is_battery_temp_within_range	= bq27520_is_battery_temp_within_range,
+	.is_battery_id_valid		= bq27520_is_battery_id_valid,
+	.get_battery_status		= bq27520_get_battery_status,
+	.get_batt_remaining_capacity	= bq27520_get_remaining_capacity,
+};
+
+static void update_current_battery_status(int data)
+{
+	int status[4], ret = 0;
+	unsigned long flag;
+
+	memset(status, 0, sizeof status);
+	ret = bq27520_battery_rsoc(bq27520_di);
+	status[GET_BATTERY_CAPACITY] = (ret < 0) ? 0 : ret;
+
+	status[GET_BATTERY_VOLTAGE] = bq27520_battery_voltage(bq27520_di);
+	status[GET_BATTERY_TEMPERATURE] =
+				bq27520_battery_temperature(bq27520_di);
+
+	spin_lock_irqsave(&current_battery_status.lock, flag);
+	current_battery_status.status[GET_BATTERY_STATUS] = data;
+	current_battery_status.status[GET_BATTERY_VOLTAGE] =
+						status[GET_BATTERY_VOLTAGE];
+	current_battery_status.status[GET_BATTERY_TEMPERATURE] =
+						status[GET_BATTERY_TEMPERATURE];
+	current_battery_status.status[GET_BATTERY_CAPACITY] =
+						status[GET_BATTERY_CAPACITY];
+	spin_unlock_irqrestore(&current_battery_status.lock, flag);
+}
+
+/* only if battery charging satus changes then notify msm_charger. otherwise
+ * only refresh current_batter_status
+ */
+static int if_notify_msm_charger(int *data)
+{
+	int ret = 0, flags = 0, status = 0;
+	unsigned long flag;
+
+	ret = bq27520_read(BQ27520_REG_FLAGS, &flags, 0, bq27520_di);
+	if (ret < 0) {
+		dev_err(bq27520_di->dev, "error %d reading register %02x\n",
+			ret, BQ27520_REG_FLAGS);
+		status = POWER_SUPPLY_STATUS_UNKNOWN;
+	} else {
+		if (flags & BQ27520_FLAG_FC)
+			status = POWER_SUPPLY_STATUS_FULL;
+		else if (flags & BQ27520_FLAG_DSC)
+			status = POWER_SUPPLY_STATUS_DISCHARGING;
+		else
+			status = POWER_SUPPLY_STATUS_CHARGING;
+	}
+
+	*data = status;
+	spin_lock_irqsave(&current_battery_status.lock, flag);
+	ret = (status != current_battery_status.status[GET_BATTERY_STATUS]);
+	spin_unlock_irqrestore(&current_battery_status.lock, flag);
+	return ret;
+}
+
+static void battery_status_poller(struct work_struct *work)
+{
+	int status = 0, temp = 0;
+
+	temp = if_notify_msm_charger(&status);
+	update_current_battery_status(status);
+	if (temp)
+		msm_charger_notify_event(NULL, CHG_BATT_STATUS_CHANGE);
+
+	schedule_delayed_work(&current_battery_status.poller,
+				BQ27520_POLLING_STATUS);
+}
+
+static void bq27520_hw_config(struct work_struct *work)
+{
+	int ret = 0, flags = 0, type = 0, fw_ver = 0, status = 0;
+	struct bq27520_device_info *di;
+
+	di  = container_of(work, struct bq27520_device_info, hw_config.work);
+
+	pr_debug(KERN_INFO "Enter bq27520_hw_config\n");
+	ret = bq27520_chip_config(di);
+	if (ret) {
+		dev_err(di->dev, "Failed to config Bq27520 ret = %d\n", ret);
+		return;
+	}
+	/* bq27520 is ready for access, update current_battery_status by reading
+	 * from hardware
+	 */
+	if_notify_msm_charger(&status);
+	update_current_battery_status(status);
+	msm_battery_gauge_register(&bq27520_batt_gauge);
+	msm_charger_notify_event(NULL, CHG_BATT_STATUS_CHANGE);
+
+	enable_irq(di->irq);
+
+	/* poll battery status every 3 seconds, if charging status changes,
+	 * notify msm_charger
+	 */
+	schedule_delayed_work(&current_battery_status.poller,
+				BQ27520_POLLING_STATUS);
+
+	if (di->pdata->enable_dlog) {
+		schedule_work(&di->counter);
+		init_timer(&timer);
+		timer.function = &bq27520_every_30secs;
+		timer.data = (unsigned long)di;
+		timer.expires = jiffies + BQ27520_COULOMB_POLL;
+		add_timer(&timer);
+	}
+
+	bq27520_cntl_cmd(di, BQ27520_SUBCMD_CTNL_STATUS);
+	udelay(66);
+	bq27520_read(BQ27520_REG_CNTL, &flags, 0, di);
+	bq27520_cntl_cmd(di, BQ27520_SUBCMD_DEVCIE_TYPE);
+	udelay(66);
+	bq27520_read(BQ27520_REG_CNTL, &type, 0, di);
+	bq27520_cntl_cmd(di, BQ27520_SUBCMD_FW_VER);
+	udelay(66);
+	bq27520_read(BQ27520_REG_CNTL, &fw_ver, 0, di);
+
+	dev_info(di->dev, "DEVICE_TYPE is 0x%02X, FIRMWARE_VERSION\
+		is 0x%02X\n", type, fw_ver);
+	dev_info(di->dev, "Complete bq27520 configuration 0x%02X\n", flags);
+}
+
+static int bq27520_read_i2c(u8 reg, int *rt_value, int b_single,
+			struct bq27520_device_info *di)
+{
+	struct i2c_client *client = di->client;
+	struct i2c_msg msg[1];
+	unsigned char data[2];
+	int err;
+
+	if (!client->adapter)
+		return -ENODEV;
+
+	msg->addr = client->addr;
+	msg->flags = 0;
+	msg->len = 1;
+	msg->buf = data;
+
+	data[0] = reg;
+	err = i2c_transfer(client->adapter, msg, 1);
+
+	if (err >= 0) {
+		if (!b_single)
+			msg->len = 2;
+		else
+			msg->len = 1;
+
+		msg->flags = I2C_M_RD;
+		err = i2c_transfer(client->adapter, msg, 1);
+		if (err >= 0) {
+			if (!b_single)
+				*rt_value = get_unaligned_le16(data);
+			else
+				*rt_value = data[0];
+
+			return 0;
+		}
+	}
+	return err;
+}
+
+#ifdef CONFIG_BQ27520_TEST_ENABLE
+static int reg;
+static int subcmd;
+static ssize_t bq27520_read_stdcmd(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int ret;
+	int temp = 0;
+	struct platform_device *client;
+	struct bq27520_device_info *di;
+
+	client = to_platform_device(dev);
+	di = platform_get_drvdata(client);
+
+	if (reg <= BQ27520_REG_ICR && reg > 0x00) {
+		ret = bq27520_read(reg, &temp, 0, di);
+		if (ret)
+			ret = snprintf(buf, PAGE_SIZE, "Read Error!\n");
+		else
+			ret = snprintf(buf, PAGE_SIZE, "0x%02x\n", temp);
+	} else
+		ret = snprintf(buf, PAGE_SIZE, "Register Error!\n");
+
+	return ret;
+}
+
+static ssize_t bq27520_write_stdcmd(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	ssize_t ret = strnlen(buf, PAGE_SIZE);
+	int cmd;
+
+	sscanf(buf, "%x", &cmd);
+	reg = cmd;
+	dev_info(dev, "recv'd cmd is 0x%02X\n", reg);
+	return ret;
+}
+
+static ssize_t bq27520_read_subcmd(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int ret, temp = 0;
+	struct platform_device *client;
+	struct bq27520_device_info *di;
+
+	client = to_platform_device(dev);
+	di = platform_get_drvdata(client);
+
+	if (subcmd == BQ27520_SUBCMD_DEVCIE_TYPE ||
+		 subcmd == BQ27520_SUBCMD_FW_VER ||
+		 subcmd == BQ27520_SUBCMD_HW_VER ||
+		 subcmd == BQ27520_SUBCMD_CHEM_ID) {
+
+		bq27520_cntl_cmd(di, subcmd);/* Retrieve Chip status */
+		udelay(66);
+		ret = bq27520_read(BQ27520_REG_CNTL, &temp, 0, di);
+
+		if (ret)
+			ret = snprintf(buf, PAGE_SIZE, "Read Error!\n");
+		else
+			ret = snprintf(buf, PAGE_SIZE, "0x%02x\n", temp);
+	} else
+		ret = snprintf(buf, PAGE_SIZE, "Register Error!\n");
+
+	return ret;
+}
+
+static ssize_t bq27520_write_subcmd(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	ssize_t ret = strnlen(buf, PAGE_SIZE);
+	int cmd;
+
+	sscanf(buf, "%x", &cmd);
+	subcmd = cmd;
+	return ret;
+}
+
+static DEVICE_ATTR(std_cmd, S_IRUGO|S_IWUGO, bq27520_read_stdcmd,
+	bq27520_write_stdcmd);
+static DEVICE_ATTR(sub_cmd, S_IRUGO|S_IWUGO, bq27520_read_subcmd,
+	bq27520_write_subcmd);
+static struct attribute *fs_attrs[] = {
+	&dev_attr_std_cmd.attr,
+	&dev_attr_sub_cmd.attr,
+	NULL,
+};
+static struct attribute_group fs_attr_group = {
+	.attrs = fs_attrs,
+};
+
+static struct platform_device this_device = {
+	.name = "bq27520-test",
+	.id = -1,
+	.dev.platform_data = NULL,
+};
+#endif
+
+static irqreturn_t soc_irqhandler(int irq, void *dev_id)
+{
+	int status = 0, temp = 0;
+
+	temp = if_notify_msm_charger(&status);
+	update_current_battery_status(status);
+	if (temp)
+		msm_charger_notify_event(NULL, CHG_BATT_STATUS_CHANGE);
+	return IRQ_HANDLED;
+}
+
+static struct regulator *vreg_bq27520;
+static int bq27520_power(bool enable, struct bq27520_device_info *di)
+{
+	int rc = 0, ret;
+	const struct bq27520_platform_data *platdata;
+
+	platdata = di->pdata;
+	if (enable) {
+		/* switch on Vreg_S3 */
+		rc = regulator_enable(vreg_bq27520);
+		if (rc < 0) {
+			dev_err(di->dev, "%s: vreg %s %s failed (%d)\n",
+				__func__, platdata->vreg_name, "enable", rc);
+			goto vreg_fail;
+		}
+
+		/* Battery gauge enable and switch on onchip 2.5V LDO */
+		rc = gpio_request(platdata->chip_en, "GAUGE_EN");
+		if (rc) {
+			dev_err(di->dev, "%s: fail to request gpio %d (%d)\n",
+				__func__, platdata->chip_en, rc);
+			goto vreg_fail;
+		}
+
+		gpio_direction_output(platdata->chip_en, 0);
+		gpio_set_value(platdata->chip_en, 1);
+		rc = gpio_request(platdata->soc_int, "GAUGE_SOC_INT");
+		if (rc) {
+			dev_err(di->dev, "%s: fail to request gpio %d (%d)\n",
+				__func__, platdata->soc_int, rc);
+			goto gpio_fail;
+		}
+		gpio_direction_input(platdata->soc_int);
+		di->irq = gpio_to_irq(platdata->soc_int);
+		rc = request_threaded_irq(di->irq, NULL, soc_irqhandler,
+				IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
+				"BQ27520_IRQ", di);
+		if (rc) {
+			dev_err(di->dev, "%s: fail to request irq %d (%d)\n",
+				__func__, platdata->soc_int, rc);
+			goto irqreq_fail;
+		} else {
+			disable_irq_nosync(di->irq);
+		}
+	} else {
+		free_irq(di->irq, di);
+		gpio_free(platdata->soc_int);
+		/* switch off on-chip 2.5V LDO and disable Battery gauge */
+		gpio_set_value(platdata->chip_en, 0);
+		gpio_free(platdata->chip_en);
+		/* switch off Vreg_S3 */
+		rc = regulator_disable(vreg_bq27520);
+		if (rc < 0) {
+			dev_err(di->dev, "%s: vreg %s %s failed (%d)\n",
+				__func__, platdata->vreg_name, "disable", rc);
+			goto vreg_fail;
+		}
+	}
+	return rc;
+
+irqreq_fail:
+	gpio_free(platdata->soc_int);
+gpio_fail:
+	gpio_set_value(platdata->chip_en, 0);
+	gpio_free(platdata->chip_en);
+vreg_fail:
+	ret = !enable ? regulator_enable(vreg_bq27520) :
+		regulator_disable(vreg_bq27520);
+	if (ret < 0) {
+		dev_err(di->dev, "%s: vreg %s %s failed (%d) in err path\n",
+			__func__, platdata->vreg_name,
+			!enable ? "enable" : "disable", ret);
+	}
+	return rc;
+}
+
+static int bq27520_dev_setup(bool enable, struct bq27520_device_info *di)
+{
+	int rc;
+	const struct bq27520_platform_data *platdata;
+
+	platdata = di->pdata;
+	if (enable) {
+		/* enable and set voltage Vreg_S3 */
+		vreg_bq27520 = regulator_get(NULL,
+				platdata->vreg_name);
+		if (IS_ERR(vreg_bq27520)) {
+			dev_err(di->dev, "%s: regulator get of %s\
+				failed (%ld)\n", __func__, platdata->vreg_name,
+				PTR_ERR(vreg_bq27520));
+			rc = PTR_ERR(vreg_bq27520);
+			goto vreg_get_fail;
+		}
+		rc = regulator_set_voltage(vreg_bq27520,
+			platdata->vreg_value, platdata->vreg_value);
+		if (rc) {
+			dev_err(di->dev, "%s: regulator_set_voltage(%s) failed\
+				 (%d)\n", __func__, platdata->vreg_name, rc);
+			goto vreg_get_fail;
+		}
+	} else {
+		regulator_put(vreg_bq27520);
+	}
+	return 0;
+
+vreg_get_fail:
+	regulator_put(vreg_bq27520);
+	return rc;
+}
+
+static int bq27520_battery_probe(struct i2c_client *client,
+				 const struct i2c_device_id *id)
+{
+	struct bq27520_device_info *di;
+	struct bq27520_access_methods *bus;
+	const struct bq27520_platform_data  *pdata;
+	int num, retval = 0;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+		return -ENODEV;
+
+	pdata = client->dev.platform_data;
+
+	/* Get new ID for the new battery device */
+	retval = idr_pre_get(&battery_id, GFP_KERNEL);
+	if (retval == 0)
+		return -ENOMEM;
+	mutex_lock(&battery_mutex);
+	retval = idr_get_new(&battery_id, client, &num);
+	mutex_unlock(&battery_mutex);
+	if (retval < 0)
+		return retval;
+
+	di = kzalloc(sizeof(*di), GFP_KERNEL);
+	if (!di) {
+		dev_err(&client->dev, "failed to allocate device info data\n");
+		retval = -ENOMEM;
+		goto batt_failed_1;
+	}
+	di->id = num;
+	di->pdata = pdata;
+
+	bus = kzalloc(sizeof(*bus), GFP_KERNEL);
+	if (!bus) {
+		dev_err(&client->dev, "failed to allocate data\n");
+		retval = -ENOMEM;
+		goto batt_failed_2;
+	}
+
+	i2c_set_clientdata(client, di);
+	di->dev = &client->dev;
+	bus->read = &bq27520_read_i2c;
+	di->bus = bus;
+	di->client = client;
+
+#ifdef CONFIG_BQ27520_TEST_ENABLE
+	platform_set_drvdata(&this_device, di);
+	retval = platform_device_register(&this_device);
+	if (!retval) {
+		retval = sysfs_create_group(&this_device.dev.kobj,
+			 &fs_attr_group);
+		if (retval)
+			goto batt_failed_3;
+	} else
+		goto batt_failed_3;
+#endif
+
+	retval = bq27520_dev_setup(true, di);
+	if (retval) {
+		dev_err(&client->dev, "failed to setup ret = %d\n", retval);
+		goto batt_failed_3;
+	}
+
+	retval = bq27520_power(true, di);
+	if (retval) {
+		dev_err(&client->dev, "failed to powerup ret = %d\n", retval);
+		goto batt_failed_3;
+	}
+
+	spin_lock_init(&lock);
+
+	bq27520_di = di;
+	if (pdata->enable_dlog)
+		INIT_WORK(&di->counter, bq27520_coulomb_counter_work);
+
+	INIT_DELAYED_WORK(&current_battery_status.poller,
+			battery_status_poller);
+	INIT_DELAYED_WORK(&di->hw_config, bq27520_hw_config);
+	schedule_delayed_work(&di->hw_config, BQ27520_INIT_DELAY);
+
+	return 0;
+
+batt_failed_3:
+	kfree(bus);
+batt_failed_2:
+	kfree(di);
+batt_failed_1:
+	mutex_lock(&battery_mutex);
+	idr_remove(&battery_id, num);
+	mutex_unlock(&battery_mutex);
+
+	return retval;
+}
+
+static int bq27520_battery_remove(struct i2c_client *client)
+{
+	struct bq27520_device_info *di = i2c_get_clientdata(client);
+
+	if (di->pdata->enable_dlog) {
+		del_timer_sync(&timer);
+		cancel_work_sync(&di->counter);
+		bq27520_cntl_cmd(di, BQ27520_SUBCMD_DISABLE_DLOG);
+		udelay(66);
+	}
+
+	bq27520_cntl_cmd(di, BQ27520_SUBCMD_DISABLE_IT);
+	cancel_delayed_work_sync(&di->hw_config);
+	cancel_delayed_work_sync(&current_battery_status.poller);
+
+	bq27520_dev_setup(false, di);
+	bq27520_power(false, di);
+
+	kfree(di->bus);
+
+	mutex_lock(&battery_mutex);
+	idr_remove(&battery_id, di->id);
+	mutex_unlock(&battery_mutex);
+
+	kfree(di);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int bq27520_suspend(struct device *dev)
+{
+	struct bq27520_device_info *di = dev_get_drvdata(dev);
+
+	disable_irq_nosync(di->irq);
+	if (di->pdata->enable_dlog) {
+		del_timer_sync(&timer);
+		cancel_work_sync(&di->counter);
+	}
+
+	cancel_delayed_work_sync(&current_battery_status.poller);
+	return 0;
+}
+
+static int bq27520_resume(struct device *dev)
+{
+	struct bq27520_device_info *di = dev_get_drvdata(dev);
+
+	enable_irq(di->irq);
+	if (di->pdata->enable_dlog)
+		add_timer(&timer);
+
+	schedule_delayed_work(&current_battery_status.poller,
+				BQ27520_POLLING_STATUS);
+	return 0;
+}
+
+static const struct dev_pm_ops bq27520_pm_ops = {
+	.suspend = bq27520_suspend,
+	.resume = bq27520_resume,
+};
+#endif
+
+static const struct i2c_device_id bq27520_id[] = {
+	{ "bq27520", 1 },
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, BQ27520_id);
+
+static struct i2c_driver bq27520_battery_driver = {
+	.driver = {
+		.name = "bq27520-battery",
+		.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm = &bq27520_pm_ops,
+#endif
+	},
+	.probe = bq27520_battery_probe,
+	.remove = bq27520_battery_remove,
+	.id_table = bq27520_id,
+};
+
+static void init_battery_status(void)
+{
+	spin_lock_init(&current_battery_status.lock);
+	current_battery_status.status[GET_BATTERY_STATUS] =
+			POWER_SUPPLY_STATUS_UNKNOWN;
+}
+
+static int __init bq27520_battery_init(void)
+{
+	int ret;
+
+	/* initialize current_battery_status, and register with msm-charger */
+	init_battery_status();
+
+	ret = i2c_add_driver(&bq27520_battery_driver);
+	if (ret)
+		printk(KERN_ERR "Unable to register driver ret = %d\n", ret);
+
+	return ret;
+}
+module_init(bq27520_battery_init);
+
+static void __exit bq27520_battery_exit(void)
+{
+	i2c_del_driver(&bq27520_battery_driver);
+	msm_battery_gauge_unregister(&bq27520_batt_gauge);
+}
+module_exit(bq27520_battery_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Qualcomm Innovation Center, Inc.");
+MODULE_DESCRIPTION("BQ27520 battery monitor driver");
diff --git a/drivers/power/bq27541_fuelgauger.c b/drivers/power/bq27541_fuelgauger.c
new file mode 100644
index 0000000..516a861
--- /dev/null
+++ b/drivers/power/bq27541_fuelgauger.c
@@ -0,0 +1,623 @@
+/* Copyright (C) 2008 Rodolfo Giometti <giometti@linux.it>
+ * Copyright (C) 2008 Eurotech S.p.A. <info@eurotech.it>
+ * Based on a previous work by Copyright (C) 2008 Texas Instruments, Inc.
+ *
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/param.h>
+#include <linux/jiffies.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/idr.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+#include <linux/time.h>
+#include <linux/mfd/pmic8058.h>
+#include <linux/regulator/pmic8058-regulator.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/machine.h>
+#include <linux/err.h>
+#include <linux/msm-charger.h>
+#include <linux/i2c/bq27520.h> /* use the same platform data as bq27520 */
+
+#define DRIVER_VERSION			"1.1.0"
+/* Bq27541 standard data commands */
+#define BQ27541_REG_CNTL		0x00
+#define BQ27541_REG_AR			0x02
+#define BQ27541_REG_ARTTE		0x04
+#define BQ27541_REG_TEMP		0x06
+#define BQ27541_REG_VOLT		0x08
+#define BQ27541_REG_FLAGS		0x0A
+#define BQ27541_REG_NAC			0x0C
+#define BQ27541_REG_FAC			0x0e
+#define BQ27541_REG_RM			0x10
+#define BQ27541_REG_FCC			0x12
+#define BQ27541_REG_AI			0x14
+#define BQ27541_REG_TTE			0x16
+#define BQ27541_REG_TTF			0x18
+#define BQ27541_REG_SI			0x1a
+#define BQ27541_REG_STTE		0x1c
+#define BQ27541_REG_MLI			0x1e
+#define BQ27541_REG_MLTTE		0x20
+#define BQ27541_REG_AE			0x22
+#define BQ27541_REG_AP			0x24
+#define BQ27541_REG_TTECP		0x26
+#define BQ27541_REG_SOH			0x28
+#define BQ27541_REG_SOC			0x2c
+#define BQ27541_REG_NIC			0x2e
+#define BQ27541_REG_ICR			0x30
+#define BQ27541_REG_LOGIDX		0x32
+#define BQ27541_REG_LOGBUF		0x34
+
+#define BQ27541_FLAG_DSC		BIT(0)
+#define BQ27541_FLAG_FC			BIT(9)
+
+#define BQ27541_CS_DLOGEN		BIT(15)
+#define BQ27541_CS_SS		    BIT(13)
+
+/* Control subcommands */
+#define BQ27541_SUBCMD_CTNL_STATUS  0x0000
+#define BQ27541_SUBCMD_DEVCIE_TYPE  0x0001
+#define BQ27541_SUBCMD_FW_VER  0x0002
+#define BQ27541_SUBCMD_HW_VER  0x0003
+#define BQ27541_SUBCMD_DF_CSUM  0x0004
+#define BQ27541_SUBCMD_PREV_MACW   0x0007
+#define BQ27541_SUBCMD_CHEM_ID   0x0008
+#define BQ27541_SUBCMD_BD_OFFSET   0x0009
+#define BQ27541_SUBCMD_INT_OFFSET  0x000a
+#define BQ27541_SUBCMD_CC_VER   0x000b
+#define BQ27541_SUBCMD_OCV  0x000c
+#define BQ27541_SUBCMD_BAT_INS   0x000d
+#define BQ27541_SUBCMD_BAT_REM   0x000e
+#define BQ27541_SUBCMD_SET_HIB   0x0011
+#define BQ27541_SUBCMD_CLR_HIB   0x0012
+#define BQ27541_SUBCMD_SET_SLP   0x0013
+#define BQ27541_SUBCMD_CLR_SLP   0x0014
+#define BQ27541_SUBCMD_FCT_RES   0x0015
+#define BQ27541_SUBCMD_ENABLE_DLOG  0x0018
+#define BQ27541_SUBCMD_DISABLE_DLOG 0x0019
+#define BQ27541_SUBCMD_SEALED   0x0020
+#define BQ27541_SUBCMD_ENABLE_IT    0x0021
+#define BQ27541_SUBCMD_DISABLE_IT   0x0023
+#define BQ27541_SUBCMD_CAL_MODE  0x0040
+#define BQ27541_SUBCMD_RESET   0x0041
+#define ZERO_DEGREE_CELSIUS_IN_TENTH_KELVIN   (-2731)
+#define BQ27541_INIT_DELAY   ((HZ)*1)
+
+/* If the system has several batteries we need a different name for each
+ * of them...
+ */
+static DEFINE_IDR(battery_id);
+static DEFINE_MUTEX(battery_mutex);
+
+struct bq27541_device_info;
+struct bq27541_access_methods {
+	int (*read)(u8 reg, int *rt_value, int b_single,
+		struct bq27541_device_info *di);
+};
+
+struct bq27541_device_info {
+	struct device			*dev;
+	int				id;
+	struct bq27541_access_methods	*bus;
+	struct i2c_client		*client;
+	struct work_struct		counter;
+	/* 300ms delay is needed after bq27541 is powered up
+	 * and before any successful I2C transaction
+	 */
+	struct  delayed_work		hw_config;
+};
+
+static int coulomb_counter;
+static spinlock_t lock; /* protect access to coulomb_counter */
+
+static int bq27541_i2c_txsubcmd(u8 reg, unsigned short subcmd,
+		struct bq27541_device_info *di);
+
+static int bq27541_read(u8 reg, int *rt_value, int b_single,
+			struct bq27541_device_info *di)
+{
+	return di->bus->read(reg, rt_value, b_single, di);
+}
+
+/*
+ * Return the battery temperature in tenths of degree Celsius
+ * Or < 0 if something fails.
+ */
+static int bq27541_battery_temperature(struct bq27541_device_info *di)
+{
+	int ret;
+	int temp = 0;
+
+	ret = bq27541_read(BQ27541_REG_TEMP, &temp, 0, di);
+	if (ret) {
+		dev_err(di->dev, "error reading temperature\n");
+		return ret;
+	}
+
+	return temp + ZERO_DEGREE_CELSIUS_IN_TENTH_KELVIN;
+}
+
+/*
+ * Return the battery Voltage in milivolts
+ * Or < 0 if something fails.
+ */
+static int bq27541_battery_voltage(struct bq27541_device_info *di)
+{
+	int ret;
+	int volt = 0;
+
+	ret = bq27541_read(BQ27541_REG_VOLT, &volt, 0, di);
+	if (ret) {
+		dev_err(di->dev, "error reading voltage\n");
+		return ret;
+	}
+
+	return volt * 1000;
+}
+
+static void bq27541_cntl_cmd(struct bq27541_device_info *di,
+				int subcmd)
+{
+	bq27541_i2c_txsubcmd(BQ27541_REG_CNTL, subcmd, di);
+}
+
+/*
+ * i2c specific code
+ */
+static int bq27541_i2c_txsubcmd(u8 reg, unsigned short subcmd,
+		struct bq27541_device_info *di)
+{
+	struct i2c_msg msg;
+	unsigned char data[3];
+	int ret;
+
+	if (!di->client)
+		return -ENODEV;
+
+	memset(data, 0, sizeof(data));
+	data[0] = reg;
+	data[1] = subcmd & 0x00FF;
+	data[2] = (subcmd & 0xFF00) >> 8;
+
+	msg.addr = di->client->addr;
+	msg.flags = 0;
+	msg.len = 3;
+	msg.buf = data;
+
+	ret = i2c_transfer(di->client->adapter, &msg, 1);
+	if (ret < 0)
+		return -EIO;
+
+	return 0;
+}
+
+static int bq27541_chip_config(struct bq27541_device_info *di)
+{
+	int flags = 0, ret = 0;
+
+	bq27541_cntl_cmd(di, BQ27541_SUBCMD_CTNL_STATUS);
+	udelay(66);
+	ret = bq27541_read(BQ27541_REG_CNTL, &flags, 0, di);
+	if (ret < 0) {
+		dev_err(di->dev, "error reading register %02x ret = %d\n",
+			 BQ27541_REG_CNTL, ret);
+		return ret;
+	}
+	udelay(66);
+
+	bq27541_cntl_cmd(di, BQ27541_SUBCMD_ENABLE_IT);
+	udelay(66);
+
+	if (!(flags & BQ27541_CS_DLOGEN)) {
+		bq27541_cntl_cmd(di, BQ27541_SUBCMD_ENABLE_DLOG);
+		udelay(66);
+	}
+
+	return 0;
+}
+
+static void bq27541_coulomb_counter_work(struct work_struct *work)
+{
+	int value = 0, temp = 0, index = 0, ret = 0;
+	struct bq27541_device_info *di;
+	unsigned long flags;
+	int count = 0;
+
+	di = container_of(work, struct bq27541_device_info, counter);
+
+	/* retrieve 30 values from FIFO of coulomb data logging buffer
+	 * and average over time
+	 */
+	do {
+		ret = bq27541_read(BQ27541_REG_LOGBUF, &temp, 0, di);
+		if (ret < 0)
+			break;
+		if (temp != 0x7FFF) {
+			++count;
+			value += temp;
+		}
+		/* delay 66uS, waiting time between continuous reading
+		 * results
+		 */
+		udelay(66);
+		ret = bq27541_read(BQ27541_REG_LOGIDX, &index, 0, di);
+		if (ret < 0)
+			break;
+		udelay(66);
+	} while (index != 0 || temp != 0x7FFF);
+
+	if (ret < 0) {
+		dev_err(di->dev, "Error reading datalog register\n");
+		return;
+	}
+
+	if (count) {
+		spin_lock_irqsave(&lock, flags);
+		coulomb_counter = value/count;
+		spin_unlock_irqrestore(&lock, flags);
+	}
+}
+
+struct bq27541_device_info *bq27541_di;
+
+static int bq27541_get_battery_mvolts(void)
+{
+	return bq27541_battery_voltage(bq27541_di);
+}
+
+static int bq27541_get_battery_temperature(void)
+{
+	return bq27541_battery_temperature(bq27541_di);
+}
+static int bq27541_is_battery_present(void)
+{
+	return 1;
+}
+static int bq27541_is_battery_temp_within_range(void)
+{
+	return 1;
+}
+static int bq27541_is_battery_id_valid(void)
+{
+	return 1;
+}
+
+static struct msm_battery_gauge bq27541_batt_gauge = {
+	.get_battery_mvolts		= bq27541_get_battery_mvolts,
+	.get_battery_temperature	= bq27541_get_battery_temperature,
+	.is_battery_present		= bq27541_is_battery_present,
+	.is_battery_temp_within_range	= bq27541_is_battery_temp_within_range,
+	.is_battery_id_valid		= bq27541_is_battery_id_valid,
+};
+static void bq27541_hw_config(struct work_struct *work)
+{
+	int ret = 0, flags = 0, type = 0, fw_ver = 0;
+	struct bq27541_device_info *di;
+
+	di  = container_of(work, struct bq27541_device_info, hw_config.work);
+	ret = bq27541_chip_config(di);
+	if (ret) {
+		dev_err(di->dev, "Failed to config Bq27541\n");
+		return;
+	}
+	msm_battery_gauge_register(&bq27541_batt_gauge);
+
+	bq27541_cntl_cmd(di, BQ27541_SUBCMD_CTNL_STATUS);
+	udelay(66);
+	bq27541_read(BQ27541_REG_CNTL, &flags, 0, di);
+	bq27541_cntl_cmd(di, BQ27541_SUBCMD_DEVCIE_TYPE);
+	udelay(66);
+	bq27541_read(BQ27541_REG_CNTL, &type, 0, di);
+	bq27541_cntl_cmd(di, BQ27541_SUBCMD_FW_VER);
+	udelay(66);
+	bq27541_read(BQ27541_REG_CNTL, &fw_ver, 0, di);
+
+	dev_info(di->dev, "DEVICE_TYPE is 0x%02X, FIRMWARE_VERSION is 0x%02X\n",
+			type, fw_ver);
+	dev_info(di->dev, "Complete bq27541 configuration 0x%02X\n", flags);
+}
+
+static int bq27541_read_i2c(u8 reg, int *rt_value, int b_single,
+			struct bq27541_device_info *di)
+{
+	struct i2c_client *client = di->client;
+	struct i2c_msg msg[1];
+	unsigned char data[2];
+	int err;
+
+	if (!client->adapter)
+		return -ENODEV;
+
+	msg->addr = client->addr;
+	msg->flags = 0;
+	msg->len = 1;
+	msg->buf = data;
+
+	data[0] = reg;
+	err = i2c_transfer(client->adapter, msg, 1);
+
+	if (err >= 0) {
+		if (!b_single)
+			msg->len = 2;
+		else
+			msg->len = 1;
+
+		msg->flags = I2C_M_RD;
+		err = i2c_transfer(client->adapter, msg, 1);
+		if (err >= 0) {
+			if (!b_single)
+				*rt_value = get_unaligned_le16(data);
+			else
+				*rt_value = data[0];
+
+			return 0;
+		}
+	}
+	return err;
+}
+
+#ifdef CONFIG_BQ27541_TEST_ENABLE
+static int reg;
+static int subcmd;
+static ssize_t bq27541_read_stdcmd(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int ret;
+	int temp = 0;
+	struct platform_device *client;
+	struct bq27541_device_info *di;
+
+	client = to_platform_device(dev);
+	di = platform_get_drvdata(client);
+
+	if (reg <= BQ27541_REG_ICR && reg > 0x00) {
+		ret = bq27541_read(reg, &temp, 0, di);
+		if (ret)
+			ret = snprintf(buf, PAGE_SIZE, "Read Error!\n");
+		else
+			ret = snprintf(buf, PAGE_SIZE, "0x%02x\n", temp);
+	} else
+		ret = snprintf(buf, PAGE_SIZE, "Register Error!\n");
+
+	return ret;
+}
+
+static ssize_t bq27541_write_stdcmd(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	ssize_t ret = strnlen(buf, PAGE_SIZE);
+	int cmd;
+
+	sscanf(buf, "%x", &cmd);
+	reg = cmd;
+	return ret;
+}
+
+static ssize_t bq27541_read_subcmd(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int ret;
+	int temp = 0;
+	struct platform_device *client;
+	struct bq27541_device_info *di;
+
+	client = to_platform_device(dev);
+	di = platform_get_drvdata(client);
+
+	if (subcmd == BQ27541_SUBCMD_DEVCIE_TYPE ||
+		 subcmd == BQ27541_SUBCMD_FW_VER ||
+		 subcmd == BQ27541_SUBCMD_HW_VER ||
+		 subcmd == BQ27541_SUBCMD_CHEM_ID) {
+
+		bq27541_cntl_cmd(di, subcmd); /* Retrieve Chip status */
+		udelay(66);
+		ret = bq27541_read(BQ27541_REG_CNTL, &temp, 0, di);
+
+		if (ret)
+			ret = snprintf(buf, PAGE_SIZE, "Read Error!\n");
+		else
+			ret = snprintf(buf, PAGE_SIZE, "0x%02x\n", temp);
+	} else
+		ret = snprintf(buf, PAGE_SIZE, "Register Error!\n");
+
+	return ret;
+}
+
+static ssize_t bq27541_write_subcmd(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	ssize_t ret = strnlen(buf, PAGE_SIZE);
+	int cmd;
+
+	sscanf(buf, "%x", &cmd);
+	subcmd = cmd;
+	return ret;
+}
+
+static DEVICE_ATTR(std_cmd, S_IRUGO|S_IWUGO, bq27541_read_stdcmd,
+	bq27541_write_stdcmd);
+static DEVICE_ATTR(sub_cmd, S_IRUGO|S_IWUGO, bq27541_read_subcmd,
+	bq27541_write_subcmd);
+static struct attribute *fs_attrs[] = {
+	&dev_attr_std_cmd.attr,
+	&dev_attr_sub_cmd.attr,
+	NULL,
+};
+static struct attribute_group fs_attr_group = {
+	.attrs = fs_attrs,
+};
+
+static struct platform_device this_device = {
+	.name			= "bq27541-test",
+	.id			= -1,
+	.dev.platform_data	= NULL,
+};
+#endif
+
+static int bq27541_battery_probe(struct i2c_client *client,
+				 const struct i2c_device_id *id)
+{
+	char *name;
+	struct bq27541_device_info *di;
+	struct bq27541_access_methods *bus;
+	int num;
+	int retval = 0;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+		return -ENODEV;
+
+	/* Get new ID for the new battery device */
+	retval = idr_pre_get(&battery_id, GFP_KERNEL);
+	if (retval == 0)
+		return -ENOMEM;
+	mutex_lock(&battery_mutex);
+	retval = idr_get_new(&battery_id, client, &num);
+	mutex_unlock(&battery_mutex);
+	if (retval < 0)
+		return retval;
+
+	name = kasprintf(GFP_KERNEL, "%s-%d", id->name, num);
+	if (!name) {
+		dev_err(&client->dev, "failed to allocate device name\n");
+		retval = -ENOMEM;
+		goto batt_failed_1;
+	}
+
+	di = kzalloc(sizeof(*di), GFP_KERNEL);
+	if (!di) {
+		dev_err(&client->dev, "failed to allocate device info data\n");
+		retval = -ENOMEM;
+		goto batt_failed_2;
+	}
+	di->id = num;
+
+	bus = kzalloc(sizeof(*bus), GFP_KERNEL);
+	if (!bus) {
+		dev_err(&client->dev, "failed to allocate access method "
+					"data\n");
+		retval = -ENOMEM;
+		goto batt_failed_3;
+	}
+
+	i2c_set_clientdata(client, di);
+	di->dev = &client->dev;
+	bus->read = &bq27541_read_i2c;
+	di->bus = bus;
+	di->client = client;
+
+#ifdef CONFIG_BQ27541_TEST_ENABLE
+	platform_set_drvdata(&this_device, di);
+	retval = platform_device_register(&this_device);
+	if (!retval) {
+		retval = sysfs_create_group(&this_device.dev.kobj,
+			 &fs_attr_group);
+		if (retval)
+			goto batt_failed_4;
+	} else
+		goto batt_failed_4;
+#endif
+
+	if (retval) {
+		dev_err(&client->dev, "failed to setup bq27541\n");
+		goto batt_failed_4;
+	}
+
+	if (retval) {
+		dev_err(&client->dev, "failed to powerup bq27541\n");
+		goto batt_failed_4;
+	}
+
+	spin_lock_init(&lock);
+
+	bq27541_di = di;
+	INIT_WORK(&di->counter, bq27541_coulomb_counter_work);
+	INIT_DELAYED_WORK(&di->hw_config, bq27541_hw_config);
+	schedule_delayed_work(&di->hw_config, BQ27541_INIT_DELAY);
+	return 0;
+
+batt_failed_4:
+	kfree(bus);
+batt_failed_3:
+	kfree(di);
+batt_failed_2:
+	kfree(name);
+batt_failed_1:
+	mutex_lock(&battery_mutex);
+	idr_remove(&battery_id, num);
+	mutex_unlock(&battery_mutex);
+
+	return retval;
+}
+
+static int bq27541_battery_remove(struct i2c_client *client)
+{
+	struct bq27541_device_info *di = i2c_get_clientdata(client);
+
+	msm_battery_gauge_unregister(&bq27541_batt_gauge);
+	bq27541_cntl_cmd(di, BQ27541_SUBCMD_DISABLE_DLOG);
+	udelay(66);
+	bq27541_cntl_cmd(di, BQ27541_SUBCMD_DISABLE_IT);
+	cancel_delayed_work_sync(&di->hw_config);
+
+	kfree(di->bus);
+
+	mutex_lock(&battery_mutex);
+	idr_remove(&battery_id, di->id);
+	mutex_unlock(&battery_mutex);
+
+	kfree(di);
+	return 0;
+}
+
+static const struct i2c_device_id bq27541_id[] = {
+	{ "bq27541", 1 },
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, BQ27541_id);
+
+static struct i2c_driver bq27541_battery_driver = {
+	.driver		= {
+			.name = "bq27541-battery",
+	},
+	.probe		= bq27541_battery_probe,
+	.remove		= bq27541_battery_remove,
+	.id_table	= bq27541_id,
+};
+
+static int __init bq27541_battery_init(void)
+{
+	int ret;
+
+	ret = i2c_add_driver(&bq27541_battery_driver);
+	if (ret)
+		printk(KERN_ERR "Unable to register BQ27541 driver\n");
+
+	return ret;
+}
+module_init(bq27541_battery_init);
+
+static void __exit bq27541_battery_exit(void)
+{
+	i2c_del_driver(&bq27541_battery_driver);
+}
+module_exit(bq27541_battery_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Qualcomm Innovation Center, Inc.");
+MODULE_DESCRIPTION("BQ27541 battery monitor driver");
diff --git a/drivers/power/isl9519q.c b/drivers/power/isl9519q.c
new file mode 100644
index 0000000..7ebbf46
--- /dev/null
+++ b/drivers/power/isl9519q.c
@@ -0,0 +1,849 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#define pr_fmt(fmt)	"%s: " fmt, __func__
+
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/msm-charger.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+#include <linux/i2c/isl9519.h>
+#include <linux/msm_adc.h>
+#include <linux/spinlock.h>
+
+#define CHG_CURRENT_REG		0x14
+#define MAX_SYS_VOLTAGE_REG	0x15
+#define CONTROL_REG		0x3D
+#define MIN_SYS_VOLTAGE_REG	0x3E
+#define INPUT_CURRENT_REG	0x3F
+#define MANUFACTURER_ID_REG	0xFE
+#define DEVICE_ID_REG		0xFF
+
+#define TRCKL_CHG_STATUS_BIT	0x80
+
+#define ISL9519_CHG_PERIOD_SEC	150
+
+struct isl9519q_struct {
+	struct i2c_client		*client;
+	struct delayed_work		charge_work;
+	int				present;
+	int				batt_present;
+	bool				charging;
+	int				chgcurrent;
+	int				term_current;
+	int				input_current;
+	int				max_system_voltage;
+	int				min_system_voltage;
+	int				valid_n_gpio;
+	struct dentry			*dent;
+	struct msm_hardware_charger	adapter_hw_chg;
+	int				suspended;
+	int				charge_at_resume;
+	struct power_supply		dc_psy;
+	spinlock_t			lock;
+	bool				notify_by_pmic;
+	bool				trickle;
+};
+
+static struct isl9519q_struct *the_isl_chg;
+
+static int isl9519q_read_reg(struct i2c_client *client, int reg,
+	u16 *val)
+{
+	int ret;
+	struct isl9519q_struct *isl_chg;
+
+	isl_chg = i2c_get_clientdata(client);
+	ret = i2c_smbus_read_word_data(isl_chg->client, reg);
+
+	if (ret < 0) {
+		dev_err(&isl_chg->client->dev,
+			"i2c read fail: can't read from %02x: %d\n", reg, ret);
+		return -EAGAIN;
+	} else {
+		*val = ret;
+	}
+
+	pr_debug("reg=0x%x.val=0x%x.\n", reg, *val);
+
+	return 0;
+}
+
+static int isl9519q_write_reg(struct i2c_client *client, int reg,
+	u16 val)
+{
+	int ret;
+	struct isl9519q_struct *isl_chg;
+
+	pr_debug("reg=0x%x.val=0x%x.\n", reg, val);
+
+	isl_chg = i2c_get_clientdata(client);
+	ret = i2c_smbus_write_word_data(isl_chg->client, reg, val);
+
+	if (ret < 0) {
+		dev_err(&isl_chg->client->dev,
+			"i2c write fail: can't write %02x to %02x: %d\n",
+			val, reg, ret);
+		return -EAGAIN;
+	}
+	return 0;
+}
+
+/**
+ * Read charge-current via ADC.
+ *
+ * The ISL CCMON (charge-current-monitor) pin is connected to
+ * the PMIC MPP#X pin.
+ * This not required when notify_by_pmic is used where the PMIC
+ * uses BMS to notify the ISL on charging-done / charge-resume.
+ */
+static int isl_read_adc(int channel, int *mv_reading)
+{
+	int ret;
+	void *h;
+	struct adc_chan_result adc_chan_result;
+	struct completion  conv_complete_evt;
+
+	pr_debug("called for %d\n", channel);
+	ret = adc_channel_open(channel, &h);
+	if (ret) {
+		pr_err("couldnt open channel %d ret=%d\n", channel, ret);
+		goto out;
+	}
+	init_completion(&conv_complete_evt);
+	ret = adc_channel_request_conv(h, &conv_complete_evt);
+	if (ret) {
+		pr_err("couldnt request conv channel %d ret=%d\n",
+						channel, ret);
+		goto out;
+	}
+	ret = wait_for_completion_interruptible(&conv_complete_evt);
+	if (ret) {
+		pr_err("wait interrupted channel %d ret=%d\n", channel, ret);
+		goto out;
+	}
+	ret = adc_channel_read_result(h, &adc_chan_result);
+	if (ret) {
+		pr_err("couldnt read result channel %d ret=%d\n",
+						channel, ret);
+		goto out;
+	}
+	ret = adc_channel_close(h);
+	if (ret)
+		pr_err("couldnt close channel %d ret=%d\n", channel, ret);
+	if (mv_reading)
+		*mv_reading = (int)adc_chan_result.measurement;
+
+	pr_debug("done for %d\n", channel);
+	return adc_chan_result.physical;
+out:
+	*mv_reading = 0;
+	pr_debug("done with error for %d\n", channel);
+
+	return -EINVAL;
+}
+
+static bool is_trickle_charging(struct isl9519q_struct *isl_chg)
+{
+	u16 ctrl = 0;
+	int ret;
+
+	ret = isl9519q_read_reg(isl_chg->client, CONTROL_REG, &ctrl);
+
+	if (!ret) {
+		pr_debug("control_reg=0x%x.\n", ctrl);
+	} else {
+		dev_err(&isl_chg->client->dev,
+			"%s couldnt read cntrl reg\n", __func__);
+	}
+
+	if (ctrl & TRCKL_CHG_STATUS_BIT)
+		return true;
+
+	return false;
+}
+
+static void isl_adapter_check_ichg(struct isl9519q_struct *isl_chg)
+{
+	int ichg; /* isl charger current */
+	int mv_reading = 0;
+
+	ichg = isl_read_adc(CHANNEL_ADC_BATT_AMON, &mv_reading);
+
+	dev_dbg(&isl_chg->client->dev, "%s mv_reading=%d\n",
+		__func__, mv_reading);
+	dev_dbg(&isl_chg->client->dev, "%s isl_charger_current=%d\n",
+		__func__, ichg);
+
+	if (ichg >= 0 && ichg <= isl_chg->term_current)
+		msm_charger_notify_event(&isl_chg->adapter_hw_chg,
+					 CHG_DONE_EVENT);
+
+	isl_chg->trickle = is_trickle_charging(isl_chg);
+	if (isl_chg->trickle)
+		msm_charger_notify_event(&isl_chg->adapter_hw_chg,
+					 CHG_BATT_BEGIN_FAST_CHARGING);
+}
+
+/**
+ * isl9519q_worker
+ *
+ * Periodic task required to kick the ISL HW watchdog to keep
+ * charging.
+ *
+ * @isl9519_work: work context.
+ */
+static void isl9519q_worker(struct work_struct *isl9519_work)
+{
+	struct isl9519q_struct *isl_chg;
+
+	isl_chg = container_of(isl9519_work, struct isl9519q_struct,
+			charge_work.work);
+
+	dev_dbg(&isl_chg->client->dev, "%s\n", __func__);
+
+	if (!isl_chg->charging) {
+		pr_debug("stop charging.\n");
+		isl9519q_write_reg(isl_chg->client, CHG_CURRENT_REG, 0);
+		return; /* Stop periodic worker */
+	}
+
+	/* Kick the dog by writting to CHG_CURRENT_REG */
+	isl9519q_write_reg(isl_chg->client, CHG_CURRENT_REG,
+			   isl_chg->chgcurrent);
+
+	if (isl_chg->notify_by_pmic)
+		isl_chg->trickle = is_trickle_charging(isl_chg);
+	else
+		isl_adapter_check_ichg(isl_chg);
+
+	schedule_delayed_work(&isl_chg->charge_work,
+			      (ISL9519_CHG_PERIOD_SEC * HZ));
+}
+
+static int isl9519q_start_charging(struct isl9519q_struct *isl_chg,
+				   int chg_voltage, int chg_current)
+{
+	pr_debug("\n");
+
+	if (isl_chg->charging) {
+		pr_warn("already charging.\n");
+		return 0;
+	}
+
+	if (isl_chg->suspended) {
+		pr_warn("suspended - can't start charging.\n");
+		isl_chg->charge_at_resume = 1;
+		return 0;
+	}
+
+	dev_dbg(&isl_chg->client->dev,
+		"%s starting timed work.period=%d seconds.\n",
+		__func__, (int) ISL9519_CHG_PERIOD_SEC);
+
+	/*
+	 * The ISL will start charging from the worker context.
+	 * This API might be called from interrupt context.
+	 */
+	schedule_delayed_work(&isl_chg->charge_work, 1);
+
+	isl_chg->charging = true;
+
+	return 0;
+}
+
+static int isl9519q_stop_charging(struct isl9519q_struct *isl_chg)
+{
+	pr_debug("\n");
+
+	if (!(isl_chg->charging)) {
+		pr_warn("already not charging.\n");
+		return 0;
+	}
+
+	if (isl_chg->suspended) {
+		isl_chg->charge_at_resume = 0;
+		return 0;
+	}
+
+	dev_dbg(&isl_chg->client->dev, "%s\n", __func__);
+
+	isl_chg->charging = false;
+	isl_chg->trickle = false;
+	/*
+	 * The ISL will stop charging from the worker context.
+	 * This API might be called from interrupt context.
+	 */
+	schedule_delayed_work(&isl_chg->charge_work, 1);
+
+	return 0;
+}
+
+static int isl_adapter_start_charging(struct msm_hardware_charger *hw_chg,
+				      int chg_voltage, int chg_current)
+{
+	int rc;
+	struct isl9519q_struct *isl_chg;
+
+	isl_chg = container_of(hw_chg, struct isl9519q_struct, adapter_hw_chg);
+	rc = isl9519q_start_charging(isl_chg, chg_voltage, chg_current);
+
+	return rc;
+}
+
+static int isl_adapter_stop_charging(struct msm_hardware_charger *hw_chg)
+{
+	int rc;
+	struct isl9519q_struct *isl_chg;
+
+	isl_chg = container_of(hw_chg, struct isl9519q_struct, adapter_hw_chg);
+	rc = isl9519q_stop_charging(isl_chg);
+
+	return rc;
+}
+
+static int isl9519q_charging_switched(struct msm_hardware_charger *hw_chg)
+{
+	struct isl9519q_struct *isl_chg;
+
+	isl_chg = container_of(hw_chg, struct isl9519q_struct, adapter_hw_chg);
+	dev_dbg(&isl_chg->client->dev, "%s\n", __func__);
+	return 0;
+}
+
+static irqreturn_t isl_valid_handler(int irq, void *dev_id)
+{
+	int val;
+	struct isl9519q_struct *isl_chg;
+	struct i2c_client *client = dev_id;
+
+	isl_chg = i2c_get_clientdata(client);
+	val = gpio_get_value_cansleep(isl_chg->valid_n_gpio);
+	if (val < 0) {
+		dev_err(&isl_chg->client->dev,
+			"%s gpio_get_value failed for %d ret=%d\n", __func__,
+			isl_chg->valid_n_gpio, val);
+		goto err;
+	}
+	dev_dbg(&isl_chg->client->dev, "%s val=%d\n", __func__, val);
+
+	if (val) {
+		if (isl_chg->present == 1) {
+			msm_charger_notify_event(&isl_chg->adapter_hw_chg,
+						 CHG_REMOVED_EVENT);
+			isl_chg->present = 0;
+		}
+	} else {
+		if (isl_chg->present == 0) {
+			msm_charger_notify_event(&isl_chg->adapter_hw_chg,
+						 CHG_INSERTED_EVENT);
+			isl_chg->present = 1;
+		}
+	}
+err:
+	return IRQ_HANDLED;
+}
+
+static enum power_supply_property pm_power_props[] = {
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_CURRENT_MAX,
+	POWER_SUPPLY_PROP_CHARGE_TYPE,
+};
+
+static char *pm_power_supplied_to[] = {
+	"battery",
+};
+
+static int get_prop_charge_type(struct isl9519q_struct *isl_chg)
+{
+	if (!isl_chg->present)
+		return POWER_SUPPLY_CHARGE_TYPE_NONE;
+
+	if (isl_chg->trickle)
+		return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+
+	if (isl_chg->charging)
+		return POWER_SUPPLY_CHARGE_TYPE_FAST;
+
+	return POWER_SUPPLY_CHARGE_TYPE_NONE;
+}
+static int pm_power_get_property(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  union power_supply_propval *val)
+{
+	struct isl9519q_struct *isl_chg = container_of(psy,
+						struct isl9519q_struct,
+						dc_psy);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		val->intval = isl_chg->chgcurrent;
+		break;
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = (int)isl_chg->present;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_TYPE:
+		val->intval = get_prop_charge_type(isl_chg);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int pm_power_set_property(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  const union power_supply_propval *val)
+{
+	struct isl9519q_struct *isl_chg = container_of(psy,
+						struct isl9519q_struct,
+						dc_psy);
+	unsigned long flags;
+	int rc;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		if (val->intval) {
+			isl_chg->present = val->intval;
+		} else {
+			isl_chg->present = 0;
+			if (isl_chg->charging)
+				goto stop_charging;
+		}
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		if (val->intval) {
+			if (isl_chg->chgcurrent != val->intval)
+				return -EINVAL;
+		}
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_TYPE:
+		if (val->intval && isl_chg->present) {
+			if (val->intval == POWER_SUPPLY_CHARGE_TYPE_FAST)
+				goto start_charging;
+			if (val->intval == POWER_SUPPLY_CHARGE_TYPE_NONE)
+				goto stop_charging;
+		} else {
+			return -EINVAL;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+	power_supply_changed(&isl_chg->dc_psy);
+	return 0;
+
+start_charging:
+	spin_lock_irqsave(&isl_chg->lock, flags);
+	rc = isl9519q_start_charging(isl_chg, 0, isl_chg->chgcurrent);
+	if (rc)
+		pr_err("Failed to start charging rc=%d\n", rc);
+	spin_unlock_irqrestore(&isl_chg->lock, flags);
+	power_supply_changed(&isl_chg->dc_psy);
+	return rc;
+
+stop_charging:
+	spin_lock_irqsave(&isl_chg->lock, flags);
+	rc = isl9519q_stop_charging(isl_chg);
+	if (rc)
+		pr_err("Failed to start charging rc=%d\n", rc);
+	spin_unlock_irqrestore(&isl_chg->lock, flags);
+	power_supply_changed(&isl_chg->dc_psy);
+	return rc;
+}
+
+#define MAX_VOLTAGE_REG_MASK  0x3FF0
+#define MIN_VOLTAGE_REG_MASK  0x3F00
+#define DEFAULT_MAX_VOLTAGE_REG_VALUE	0x1070
+#define DEFAULT_MIN_VOLTAGE_REG_VALUE	0x0D00
+
+static int __devinit isl9519q_init_adapter(struct isl9519q_struct *isl_chg)
+{
+	int ret;
+	struct i2c_client *client = isl_chg->client;
+	struct isl_platform_data *pdata = client->dev.platform_data;
+
+	isl_chg->adapter_hw_chg.type = CHG_TYPE_AC;
+	isl_chg->adapter_hw_chg.rating = 2;
+	isl_chg->adapter_hw_chg.name = "isl-adapter";
+	isl_chg->adapter_hw_chg.start_charging = isl_adapter_start_charging;
+	isl_chg->adapter_hw_chg.stop_charging = isl_adapter_stop_charging;
+	isl_chg->adapter_hw_chg.charging_switched = isl9519q_charging_switched;
+
+	ret = gpio_request(pdata->valid_n_gpio, "isl_charger_valid");
+	if (ret) {
+		dev_err(&client->dev, "%s gpio_request failed "
+				      "for %d ret=%d\n",
+			__func__, pdata->valid_n_gpio, ret);
+		goto out;
+	}
+
+	ret = msm_charger_register(&isl_chg->adapter_hw_chg);
+	if (ret) {
+		dev_err(&client->dev,
+			"%s msm_charger_register failed for ret =%d\n",
+			__func__, ret);
+		goto free_gpio;
+	}
+
+	ret = request_threaded_irq(client->irq, NULL,
+				   isl_valid_handler,
+				   IRQF_TRIGGER_FALLING |
+				   IRQF_TRIGGER_RISING,
+				   "isl_charger_valid", client);
+	if (ret) {
+		dev_err(&client->dev,
+			"%s request_threaded_irq failed "
+			"for %d ret =%d\n",
+			__func__, client->irq, ret);
+		goto unregister;
+	}
+	irq_set_irq_wake(client->irq, 1);
+
+	ret = gpio_get_value_cansleep(isl_chg->valid_n_gpio);
+	if (ret < 0) {
+		dev_err(&client->dev,
+			"%s gpio_get_value failed for %d ret=%d\n",
+			__func__, pdata->valid_n_gpio, ret);
+		/* assume absent */
+		ret = 1;
+	}
+	if (!ret) {
+		msm_charger_notify_event(&isl_chg->adapter_hw_chg,
+				CHG_INSERTED_EVENT);
+		isl_chg->present = 1;
+	}
+
+	return 0;
+
+unregister:
+	msm_charger_unregister(&isl_chg->adapter_hw_chg);
+free_gpio:
+	gpio_free(pdata->valid_n_gpio);
+out:
+	return ret;
+
+}
+
+static int __devinit isl9519q_init_ext_chg(struct isl9519q_struct *isl_chg)
+{
+	int ret;
+
+	isl_chg->dc_psy.name = "dc";
+	isl_chg->dc_psy.type = POWER_SUPPLY_TYPE_MAINS;
+	isl_chg->dc_psy.supplied_to = pm_power_supplied_to;
+	isl_chg->dc_psy.num_supplicants = ARRAY_SIZE(pm_power_supplied_to);
+	isl_chg->dc_psy.properties = pm_power_props;
+	isl_chg->dc_psy.num_properties = ARRAY_SIZE(pm_power_props);
+	isl_chg->dc_psy.get_property = pm_power_get_property;
+	isl_chg->dc_psy.set_property = pm_power_set_property;
+
+	ret = power_supply_register(&isl_chg->client->dev, &isl_chg->dc_psy);
+	if (ret) {
+		pr_err("failed to register dc charger.ret=%d.\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+static int set_reg(void *data, u64 val)
+{
+	int addr = (int)data;
+	int ret;
+	u16 temp;
+
+	temp = (u16) val;
+	ret = isl9519q_write_reg(the_isl_chg->client, addr, temp);
+
+	if (ret) {
+		pr_err("isl9519q_write_reg to %x value =%d errored = %d\n",
+			addr, temp, ret);
+		return -EAGAIN;
+	}
+	return 0;
+}
+static int get_reg(void *data, u64 *val)
+{
+	int addr = (int)data;
+	int ret;
+	u16 temp;
+
+	ret = isl9519q_read_reg(the_isl_chg->client, addr, &temp);
+	if (ret) {
+		pr_err("isl9519q_read_reg to %x value =%d errored = %d\n",
+			addr, temp, ret);
+		return -EAGAIN;
+	}
+
+	*val = temp;
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_reg, set_reg, "0x%02llx\n");
+
+static void create_debugfs_entries(struct isl9519q_struct *isl_chg)
+{
+	isl_chg->dent = debugfs_create_dir("isl9519q", NULL);
+
+	if (IS_ERR(isl_chg->dent)) {
+		pr_err("isl9519q driver couldn't create debugfs dir\n");
+		return;
+	}
+
+	debugfs_create_file("CHG_CURRENT_REG", 0644, isl_chg->dent,
+				(void *) CHG_CURRENT_REG, &reg_fops);
+	debugfs_create_file("MAX_SYS_VOLTAGE_REG", 0644, isl_chg->dent,
+				(void *) MAX_SYS_VOLTAGE_REG, &reg_fops);
+	debugfs_create_file("CONTROL_REG", 0644, isl_chg->dent,
+				(void *) CONTROL_REG, &reg_fops);
+	debugfs_create_file("MIN_SYS_VOLTAGE_REG", 0644, isl_chg->dent,
+				(void *) MIN_SYS_VOLTAGE_REG, &reg_fops);
+	debugfs_create_file("INPUT_CURRENT_REG", 0644, isl_chg->dent,
+				(void *) INPUT_CURRENT_REG, &reg_fops);
+	debugfs_create_file("MANUFACTURER_ID_REG", 0644, isl_chg->dent,
+				(void *) MANUFACTURER_ID_REG, &reg_fops);
+	debugfs_create_file("DEVICE_ID_REG", 0644, isl_chg->dent,
+				(void *) DEVICE_ID_REG, &reg_fops);
+}
+
+static void remove_debugfs_entries(struct isl9519q_struct *isl_chg)
+{
+	if (isl_chg->dent)
+		debugfs_remove_recursive(isl_chg->dent);
+}
+
+static int __devinit isl9519q_hwinit(struct isl9519q_struct *isl_chg)
+{
+	int ret;
+
+	ret = isl9519q_write_reg(isl_chg->client, MAX_SYS_VOLTAGE_REG,
+			isl_chg->max_system_voltage);
+	if (ret) {
+		pr_err("Failed to set MAX_SYS_VOLTAGE rc=%d\n", ret);
+		return ret;
+	}
+
+	ret = isl9519q_write_reg(isl_chg->client, MIN_SYS_VOLTAGE_REG,
+			isl_chg->min_system_voltage);
+	if (ret) {
+		pr_err("Failed to set MIN_SYS_VOLTAGE rc=%d\n", ret);
+		return ret;
+	}
+
+	if (isl_chg->input_current) {
+		ret = isl9519q_write_reg(isl_chg->client,
+				INPUT_CURRENT_REG,
+				isl_chg->input_current);
+		if (ret) {
+			pr_err("Failed to set INPUT_CURRENT rc=%d\n", ret);
+			return ret;
+		}
+	}
+	return 0;
+}
+
+static int __devinit isl9519q_probe(struct i2c_client *client,
+				    const struct i2c_device_id *id)
+{
+	struct isl_platform_data *pdata;
+	struct isl9519q_struct *isl_chg;
+	int ret;
+
+	ret = 0;
+	pdata = client->dev.platform_data;
+
+	pr_debug("\n");
+
+	if (pdata == NULL) {
+		dev_err(&client->dev, "%s no platform data\n", __func__);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (!i2c_check_functionality(client->adapter,
+				I2C_FUNC_SMBUS_WORD_DATA)) {
+		ret = -EIO;
+		goto out;
+	}
+
+	isl_chg = kzalloc(sizeof(*isl_chg), GFP_KERNEL);
+	if (!isl_chg) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	spin_lock_init(&isl_chg->lock);
+
+	INIT_DELAYED_WORK(&isl_chg->charge_work, isl9519q_worker);
+	isl_chg->client = client;
+	isl_chg->chgcurrent = pdata->chgcurrent;
+	isl_chg->term_current = pdata->term_current;
+	isl_chg->input_current = pdata->input_current;
+	isl_chg->max_system_voltage = pdata->max_system_voltage;
+	isl_chg->min_system_voltage = pdata->min_system_voltage;
+	isl_chg->valid_n_gpio = pdata->valid_n_gpio;
+
+	/* h/w ignores lower 7 bits of charging current and input current */
+	isl_chg->chgcurrent &= ~0x7F;
+	isl_chg->input_current &= ~0x7F;
+
+	/**
+	 * ISL is Notified by PMIC to start/stop charging, rather than
+	 * handling interrupt from ISL for End-Of-Chargring, and
+	 * monitoring the charge-current periodically. The valid_n_gpio
+	 * is also not used, dc-present is detected by PMIC.
+	 */
+	isl_chg->notify_by_pmic = (client->irq == 0);
+	i2c_set_clientdata(client, isl_chg);
+
+	if (pdata->chg_detection_config) {
+		ret = pdata->chg_detection_config();
+		if (ret) {
+			dev_err(&client->dev, "%s valid config failed ret=%d\n",
+				__func__, ret);
+			goto free_isl_chg;
+		}
+	}
+
+	isl_chg->max_system_voltage &= MAX_VOLTAGE_REG_MASK;
+	isl_chg->min_system_voltage &= MIN_VOLTAGE_REG_MASK;
+	if (isl_chg->max_system_voltage == 0)
+		isl_chg->max_system_voltage = DEFAULT_MAX_VOLTAGE_REG_VALUE;
+	if (isl_chg->min_system_voltage == 0)
+		isl_chg->min_system_voltage = DEFAULT_MIN_VOLTAGE_REG_VALUE;
+
+	ret = isl9519q_hwinit(isl_chg);
+	if (ret)
+		goto free_isl_chg;
+
+	if (isl_chg->notify_by_pmic)
+		ret = isl9519q_init_ext_chg(isl_chg);
+	else
+		ret = isl9519q_init_adapter(isl_chg);
+
+	if (ret)
+		goto free_isl_chg;
+
+	the_isl_chg = isl_chg;
+	create_debugfs_entries(isl_chg);
+
+	pr_info("OK.\n");
+
+	return 0;
+
+free_isl_chg:
+	kfree(isl_chg);
+out:
+	return ret;
+}
+
+static int __devexit isl9519q_remove(struct i2c_client *client)
+{
+	struct isl_platform_data *pdata;
+	struct isl9519q_struct *isl_chg = i2c_get_clientdata(client);
+
+	pdata = client->dev.platform_data;
+	gpio_free(pdata->valid_n_gpio);
+	free_irq(client->irq, client);
+	cancel_delayed_work_sync(&isl_chg->charge_work);
+	if (isl_chg->notify_by_pmic) {
+		power_supply_unregister(&isl_chg->dc_psy);
+	} else {
+		msm_charger_notify_event(&isl_chg->adapter_hw_chg,
+							CHG_REMOVED_EVENT);
+		msm_charger_unregister(&isl_chg->adapter_hw_chg);
+	}
+	remove_debugfs_entries(isl_chg);
+	the_isl_chg = NULL;
+	kfree(isl_chg);
+	return 0;
+}
+
+static const struct i2c_device_id isl9519q_id[] = {
+	{"isl9519q", 0},
+	{},
+};
+
+#ifdef CONFIG_PM
+static int isl9519q_suspend(struct device *dev)
+{
+	struct isl9519q_struct *isl_chg = dev_get_drvdata(dev);
+
+	dev_dbg(&isl_chg->client->dev, "%s\n", __func__);
+	/*
+	 * do not suspend while we are charging
+	 * because we need to periodically update the register
+	 * for charging to proceed
+	 */
+	if (isl_chg->charging)
+		return -EBUSY;
+
+	isl_chg->suspended  = 1;
+	return 0;
+}
+
+static int isl9519q_resume(struct device *dev)
+{
+	struct isl9519q_struct *isl_chg = dev_get_drvdata(dev);
+
+	dev_dbg(&isl_chg->client->dev, "%s\n", __func__);
+	isl_chg->suspended  = 0;
+	if (isl_chg->charge_at_resume) {
+		isl_chg->charge_at_resume = 0;
+		isl9519q_start_charging(isl_chg, 0, 0);
+	}
+	return 0;
+}
+
+static const struct dev_pm_ops isl9519q_pm_ops = {
+	.suspend = isl9519q_suspend,
+	.resume = isl9519q_resume,
+};
+#endif
+
+static struct i2c_driver isl9519q_driver = {
+	.driver = {
+		   .name = "isl9519q",
+		   .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+		   .pm = &isl9519q_pm_ops,
+#endif
+	},
+	.probe = isl9519q_probe,
+	.remove = __devexit_p(isl9519q_remove),
+	.id_table = isl9519q_id,
+};
+
+static int __init isl9519q_init(void)
+{
+	return i2c_add_driver(&isl9519q_driver);
+}
+
+late_initcall_sync(isl9519q_init);
+
+static void __exit isl9519q_exit(void)
+{
+	return i2c_del_driver(&isl9519q_driver);
+}
+
+module_exit(isl9519q_exit);
+
+MODULE_AUTHOR("Abhijeet Dharmapurikar <adharmap@codeaurora.org>");
+MODULE_DESCRIPTION("Driver for ISL9519Q Charger chip");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/ltc4088-charger.c b/drivers/power/ltc4088-charger.c
new file mode 100644
index 0000000..dbc75cd
--- /dev/null
+++ b/drivers/power/ltc4088-charger.c
@@ -0,0 +1,338 @@
+/*
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#define pr_fmt(fmt)	"%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/power/ltc4088-charger.h>
+
+#define MAX_CURRENT_UA(n)	(n)
+#define MAX_CURRENT_MA(n)	(n * MAX_CURRENT_UA(1000))
+
+/**
+ * ltc4088_max_current - A typical current values supported by the charger
+ * @LTC4088_MAX_CURRENT_100mA:  100mA current
+ * @LTC4088_MAX_CURRENT_500mA:  500mA current
+ * @LTC4088_MAX_CURRENT_1A:     1A current
+ */
+enum ltc4088_max_current {
+	LTC4088_MAX_CURRENT_100mA = 100,
+	LTC4088_MAX_CURRENT_500mA = 500,
+	LTC4088_MAX_CURRENT_1A = 1000,
+};
+
+/**
+ * struct ltc4088_chg_chip - Device information
+ * @dev:			Device pointer to access the parent
+ * @lock:			Enable mutual exclusion
+ * @usb_psy:			USB device information
+ * @gpio_mode_select_d0:	GPIO #pin for D0 charger line
+ * @gpio_mode_select_d1:	GPIO #pin for D1 charger line
+ * @gpio_mode_select_d2:	GPIO #pin for D2 charger line
+ * @max_current:		Maximum current that is supplied at this time
+ */
+struct ltc4088_chg_chip {
+	struct device		*dev;
+	struct mutex		lock;
+	struct power_supply	usb_psy;
+	unsigned int		gpio_mode_select_d0;
+	unsigned int		gpio_mode_select_d1;
+	unsigned int		gpio_mode_select_d2;
+	unsigned int		max_current;
+};
+
+static enum power_supply_property pm_power_props[] = {
+	POWER_SUPPLY_PROP_CURRENT_MAX,
+	POWER_SUPPLY_PROP_ONLINE,
+};
+
+static char *pm_power_supplied_to[] = {
+	"battery",
+};
+
+static int ltc4088_set_charging(struct ltc4088_chg_chip *chip, bool enable)
+{
+	mutex_lock(&chip->lock);
+
+	if (enable) {
+		gpio_set_value_cansleep(chip->gpio_mode_select_d2, 0);
+	} else {
+		/* When disabling charger, set the max current to 0 also */
+		chip->max_current = 0;
+		gpio_set_value_cansleep(chip->gpio_mode_select_d0, 1);
+		gpio_set_value_cansleep(chip->gpio_mode_select_d1, 1);
+		gpio_set_value_cansleep(chip->gpio_mode_select_d2, 1);
+	}
+
+	mutex_unlock(&chip->lock);
+
+	return 0;
+}
+
+static void ltc4088_set_max_current(struct ltc4088_chg_chip *chip, int value)
+{
+	mutex_lock(&chip->lock);
+
+	/* If current is less than 100mA, we can not support that granularity */
+	if (value <  MAX_CURRENT_MA(LTC4088_MAX_CURRENT_100mA)) {
+		chip->max_current = 0;
+		gpio_set_value_cansleep(chip->gpio_mode_select_d0, 1);
+		gpio_set_value_cansleep(chip->gpio_mode_select_d1, 1);
+	} else if (value <  MAX_CURRENT_MA(LTC4088_MAX_CURRENT_500mA)) {
+		chip->max_current = MAX_CURRENT_MA(LTC4088_MAX_CURRENT_100mA);
+		gpio_set_value_cansleep(chip->gpio_mode_select_d0, 0);
+		gpio_set_value_cansleep(chip->gpio_mode_select_d1, 0);
+	} else if (value <  MAX_CURRENT_MA(LTC4088_MAX_CURRENT_1A)) {
+		chip->max_current = MAX_CURRENT_MA(LTC4088_MAX_CURRENT_500mA);
+		gpio_set_value_cansleep(chip->gpio_mode_select_d0, 0);
+		gpio_set_value_cansleep(chip->gpio_mode_select_d1, 1);
+	} else {
+		chip->max_current = MAX_CURRENT_MA(LTC4088_MAX_CURRENT_1A);
+		gpio_set_value_cansleep(chip->gpio_mode_select_d0, 1);
+		gpio_set_value_cansleep(chip->gpio_mode_select_d1, 0);
+	}
+
+	mutex_unlock(&chip->lock);
+}
+
+static void ltc4088_set_charging_off(struct ltc4088_chg_chip *chip)
+{
+	gpio_set_value_cansleep(chip->gpio_mode_select_d0, 1);
+	gpio_set_value_cansleep(chip->gpio_mode_select_d1, 1);
+}
+
+static int ltc4088_set_initial_state(struct ltc4088_chg_chip *chip)
+{
+	int rc;
+
+	rc = gpio_request(chip->gpio_mode_select_d0, "ltc4088_D0");
+	if (rc) {
+		pr_err("gpio request failed for GPIO %d\n",
+				chip->gpio_mode_select_d0);
+		return rc;
+	}
+
+	rc = gpio_request(chip->gpio_mode_select_d1, "ltc4088_D1");
+	if (rc) {
+		pr_err("gpio request failed for GPIO %d\n",
+				chip->gpio_mode_select_d1);
+		goto gpio_err_d0;
+	}
+
+	rc = gpio_request(chip->gpio_mode_select_d2, "ltc4088_D2");
+	if (rc) {
+		pr_err("gpio request failed for GPIO %d\n",
+				chip->gpio_mode_select_d2);
+		goto gpio_err_d1;
+	}
+
+	rc = gpio_direction_output(chip->gpio_mode_select_d0, 0);
+	if (rc) {
+		pr_err("failed to set direction for GPIO %d\n",
+				chip->gpio_mode_select_d0);
+		goto gpio_err_d2;
+	}
+
+	rc = gpio_direction_output(chip->gpio_mode_select_d1, 0);
+	if (rc) {
+		pr_err("failed to set direction for GPIO %d\n",
+				chip->gpio_mode_select_d1);
+		goto gpio_err_d2;
+	}
+
+	rc = gpio_direction_output(chip->gpio_mode_select_d2, 1);
+	if (rc) {
+		pr_err("failed to set direction for GPIO %d\n",
+				chip->gpio_mode_select_d2);
+		goto gpio_err_d2;
+	}
+
+	return 0;
+
+gpio_err_d2:
+	gpio_free(chip->gpio_mode_select_d2);
+gpio_err_d1:
+	gpio_free(chip->gpio_mode_select_d1);
+gpio_err_d0:
+	gpio_free(chip->gpio_mode_select_d0);
+	return rc;
+}
+
+static int pm_power_get_property(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  union power_supply_propval *val)
+{
+	struct ltc4088_chg_chip *chip;
+
+	if (psy->type == POWER_SUPPLY_TYPE_USB) {
+		chip = container_of(psy, struct ltc4088_chg_chip,
+						usb_psy);
+		switch (psp) {
+		case POWER_SUPPLY_PROP_ONLINE:
+			if (chip->max_current)
+				val->intval = 1;
+			else
+				val->intval = 0;
+			break;
+		case POWER_SUPPLY_PROP_CURRENT_MAX:
+			val->intval = chip->max_current;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+static int pm_power_set_property(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  const union power_supply_propval *val)
+{
+	struct ltc4088_chg_chip *chip;
+
+	if (psy->type == POWER_SUPPLY_TYPE_USB) {
+		chip = container_of(psy, struct ltc4088_chg_chip,
+						usb_psy);
+		switch (psp) {
+		case POWER_SUPPLY_PROP_ONLINE:
+			ltc4088_set_charging(chip, val->intval);
+			break;
+		case POWER_SUPPLY_PROP_CURRENT_MAX:
+			ltc4088_set_max_current(chip, val->intval);
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+static int __devinit ltc4088_charger_probe(struct platform_device *pdev)
+{
+	int rc;
+	struct ltc4088_chg_chip *chip;
+	const struct ltc4088_charger_platform_data *pdata
+			= pdev->dev.platform_data;
+
+	if (!pdata) {
+		pr_err("missing platform data\n");
+		return -EINVAL;
+	}
+
+	chip = kzalloc(sizeof(struct ltc4088_chg_chip),
+					GFP_KERNEL);
+	if (!chip) {
+		pr_err("Cannot allocate pm_chg_chip\n");
+		return -ENOMEM;
+	}
+
+	chip->dev = &pdev->dev;
+
+	if (pdata->gpio_mode_select_d0 < 0 ||
+		 pdata->gpio_mode_select_d1 < 0 ||
+		 pdata->gpio_mode_select_d2 < 0) {
+		pr_err("Invalid platform data supplied\n");
+		rc = -EINVAL;
+		goto free_chip;
+	}
+
+	mutex_init(&chip->lock);
+
+	chip->gpio_mode_select_d0 = pdata->gpio_mode_select_d0;
+	chip->gpio_mode_select_d1 = pdata->gpio_mode_select_d1;
+	chip->gpio_mode_select_d2 = pdata->gpio_mode_select_d2;
+
+	chip->usb_psy.name = "usb",
+	chip->usb_psy.type = POWER_SUPPLY_TYPE_USB,
+	chip->usb_psy.supplied_to = pm_power_supplied_to,
+	chip->usb_psy.num_supplicants = ARRAY_SIZE(pm_power_supplied_to),
+	chip->usb_psy.properties = pm_power_props,
+	chip->usb_psy.num_properties = ARRAY_SIZE(pm_power_props),
+	chip->usb_psy.get_property = pm_power_get_property,
+	chip->usb_psy.set_property = pm_power_set_property,
+
+	rc = power_supply_register(chip->dev, &chip->usb_psy);
+	if (rc < 0) {
+		pr_err("power_supply_register usb failed rc = %d\n", rc);
+		goto free_chip;
+	}
+
+	platform_set_drvdata(pdev, chip);
+
+	rc = ltc4088_set_initial_state(chip);
+	if (rc < 0) {
+		pr_err("setting initial state failed rc = %d\n", rc);
+		goto unregister_usb;
+	}
+
+	return 0;
+
+unregister_usb:
+	platform_set_drvdata(pdev, NULL);
+	power_supply_unregister(&chip->usb_psy);
+free_chip:
+	kfree(chip);
+
+	return rc;
+}
+
+static int __devexit ltc4088_charger_remove(struct platform_device *pdev)
+{
+	struct ltc4088_chg_chip *chip = platform_get_drvdata(pdev);
+
+	ltc4088_set_charging_off(chip);
+
+	gpio_free(chip->gpio_mode_select_d2);
+	gpio_free(chip->gpio_mode_select_d1);
+	gpio_free(chip->gpio_mode_select_d0);
+
+	power_supply_unregister(&chip->usb_psy);
+
+	platform_set_drvdata(pdev, NULL);
+	mutex_destroy(&chip->lock);
+	kfree(chip);
+
+	return 0;
+}
+
+static struct platform_driver ltc4088_charger_driver = {
+	.probe	= ltc4088_charger_probe,
+	.remove	= __devexit_p(ltc4088_charger_remove),
+	.driver	= {
+		.name	= LTC4088_CHARGER_DEV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init ltc4088_charger_init(void)
+{
+	return platform_driver_register(&ltc4088_charger_driver);
+}
+
+static void __exit ltc4088_charger_exit(void)
+{
+	platform_driver_unregister(&ltc4088_charger_driver);
+}
+
+subsys_initcall(ltc4088_charger_init);
+module_exit(ltc4088_charger_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("LTC4088 charger/battery driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" LTC4088_CHARGER_DEV_NAME);
diff --git a/drivers/power/msm_battery.c b/drivers/power/msm_battery.c
new file mode 100644
index 0000000..0555399
--- /dev/null
+++ b/drivers/power/msm_battery.c
@@ -0,0 +1,1592 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*
+ * this needs to be before <linux/kernel.h> is loaded,
+ * and <linux/sched.h> loads <linux/kernel.h>
+ */
+#define DEBUG  0
+
+#include <linux/slab.h>
+#include <linux/earlysuspend.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+
+#include <asm/atomic.h>
+
+#include <mach/msm_rpcrouter.h>
+#include <mach/msm_battery.h>
+
+#define BATTERY_RPC_PROG	0x30000089
+#define BATTERY_RPC_VER_1_1	0x00010001
+#define BATTERY_RPC_VER_2_1	0x00020001
+#define BATTERY_RPC_VER_4_1     0x00040001
+#define BATTERY_RPC_VER_5_1     0x00050001
+
+#define BATTERY_RPC_CB_PROG	(BATTERY_RPC_PROG | 0x01000000)
+
+#define CHG_RPC_PROG		0x3000001a
+#define CHG_RPC_VER_1_1		0x00010001
+#define CHG_RPC_VER_1_3		0x00010003
+#define CHG_RPC_VER_2_2		0x00020002
+#define CHG_RPC_VER_3_1         0x00030001
+#define CHG_RPC_VER_4_1         0x00040001
+
+#define BATTERY_REGISTER_PROC				2
+#define BATTERY_MODIFY_CLIENT_PROC			4
+#define BATTERY_DEREGISTER_CLIENT_PROC			5
+#define BATTERY_READ_MV_PROC				12
+#define BATTERY_ENABLE_DISABLE_FILTER_PROC		14
+
+#define VBATT_FILTER			2
+
+#define BATTERY_CB_TYPE_PROC		1
+#define BATTERY_CB_ID_ALL_ACTIV		1
+#define BATTERY_CB_ID_LOW_VOL		2
+
+#define BATTERY_LOW		3200
+#define BATTERY_HIGH		4300
+
+#define ONCRPC_CHG_GET_GENERAL_STATUS_PROC	12
+#define ONCRPC_CHARGER_API_VERSIONS_PROC	0xffffffff
+
+#define BATT_RPC_TIMEOUT    5000	/* 5 sec */
+
+#define INVALID_BATT_HANDLE    -1
+
+#define RPC_TYPE_REQ     0
+#define RPC_TYPE_REPLY   1
+#define RPC_REQ_REPLY_COMMON_HEADER_SIZE   (3 * sizeof(uint32_t))
+
+
+#if DEBUG
+#define DBG_LIMIT(x...) do {if (printk_ratelimit()) pr_debug(x); } while (0)
+#else
+#define DBG_LIMIT(x...) do {} while (0)
+#endif
+
+enum {
+	BATTERY_REGISTRATION_SUCCESSFUL = 0,
+	BATTERY_DEREGISTRATION_SUCCESSFUL = BATTERY_REGISTRATION_SUCCESSFUL,
+	BATTERY_MODIFICATION_SUCCESSFUL = BATTERY_REGISTRATION_SUCCESSFUL,
+	BATTERY_INTERROGATION_SUCCESSFUL = BATTERY_REGISTRATION_SUCCESSFUL,
+	BATTERY_CLIENT_TABLE_FULL = 1,
+	BATTERY_REG_PARAMS_WRONG = 2,
+	BATTERY_DEREGISTRATION_FAILED = 4,
+	BATTERY_MODIFICATION_FAILED = 8,
+	BATTERY_INTERROGATION_FAILED = 16,
+	/* Client's filter could not be set because perhaps it does not exist */
+	BATTERY_SET_FILTER_FAILED         = 32,
+	/* Client's could not be found for enabling or disabling the individual
+	 * client */
+	BATTERY_ENABLE_DISABLE_INDIVIDUAL_CLIENT_FAILED  = 64,
+	BATTERY_LAST_ERROR = 128,
+};
+
+enum {
+	BATTERY_VOLTAGE_UP = 0,
+	BATTERY_VOLTAGE_DOWN,
+	BATTERY_VOLTAGE_ABOVE_THIS_LEVEL,
+	BATTERY_VOLTAGE_BELOW_THIS_LEVEL,
+	BATTERY_VOLTAGE_LEVEL,
+	BATTERY_ALL_ACTIVITY,
+	VBATT_CHG_EVENTS,
+	BATTERY_VOLTAGE_UNKNOWN,
+};
+
+/*
+ * This enum contains defintions of the charger hardware status
+ */
+enum chg_charger_status_type {
+	/* The charger is good      */
+	CHARGER_STATUS_GOOD,
+	/* The charger is bad       */
+	CHARGER_STATUS_BAD,
+	/* The charger is weak      */
+	CHARGER_STATUS_WEAK,
+	/* Invalid charger status.  */
+	CHARGER_STATUS_INVALID
+};
+
+/*
+ *This enum contains defintions of the charger hardware type
+ */
+enum chg_charger_hardware_type {
+	/* The charger is removed                 */
+	CHARGER_TYPE_NONE,
+	/* The charger is a regular wall charger   */
+	CHARGER_TYPE_WALL,
+	/* The charger is a PC USB                 */
+	CHARGER_TYPE_USB_PC,
+	/* The charger is a wall USB charger       */
+	CHARGER_TYPE_USB_WALL,
+	/* The charger is a USB carkit             */
+	CHARGER_TYPE_USB_CARKIT,
+	/* Invalid charger hardware status.        */
+	CHARGER_TYPE_INVALID
+};
+
+/*
+ *  This enum contains defintions of the battery status
+ */
+enum chg_battery_status_type {
+	/* The battery is good        */
+	BATTERY_STATUS_GOOD,
+	/* The battery is cold/hot    */
+	BATTERY_STATUS_BAD_TEMP,
+	/* The battery is bad         */
+	BATTERY_STATUS_BAD,
+	/* The battery is removed     */
+	BATTERY_STATUS_REMOVED,		/* on v2.2 only */
+	BATTERY_STATUS_INVALID_v1 = BATTERY_STATUS_REMOVED,
+	/* Invalid battery status.    */
+	BATTERY_STATUS_INVALID
+};
+
+/*
+ *This enum contains defintions of the battery voltage level
+ */
+enum chg_battery_level_type {
+	/* The battery voltage is dead/very low (less than 3.2V) */
+	BATTERY_LEVEL_DEAD,
+	/* The battery voltage is weak/low (between 3.2V and 3.4V) */
+	BATTERY_LEVEL_WEAK,
+	/* The battery voltage is good/normal(between 3.4V and 4.2V) */
+	BATTERY_LEVEL_GOOD,
+	/* The battery voltage is up to full (close to 4.2V) */
+	BATTERY_LEVEL_FULL,
+	/* Invalid battery voltage level. */
+	BATTERY_LEVEL_INVALID
+};
+
+#ifndef CONFIG_BATTERY_MSM_FAKE
+struct rpc_reply_batt_chg_v1 {
+	struct rpc_reply_hdr hdr;
+	u32 	more_data;
+
+	u32	charger_status;
+	u32	charger_type;
+	u32	battery_status;
+	u32	battery_level;
+	u32     battery_voltage;
+	u32	battery_temp;
+};
+
+struct rpc_reply_batt_chg_v2 {
+	struct rpc_reply_batt_chg_v1	v1;
+
+	u32	is_charger_valid;
+	u32	is_charging;
+	u32	is_battery_valid;
+	u32	ui_event;
+};
+
+union rpc_reply_batt_chg {
+	struct rpc_reply_batt_chg_v1	v1;
+	struct rpc_reply_batt_chg_v2	v2;
+};
+
+static union rpc_reply_batt_chg rep_batt_chg;
+#endif
+
+struct msm_battery_info {
+	u32 voltage_max_design;
+	u32 voltage_min_design;
+	u32 chg_api_version;
+	u32 batt_technology;
+	u32 batt_api_version;
+
+	u32 avail_chg_sources;
+	u32 current_chg_source;
+
+	u32 batt_status;
+	u32 batt_health;
+	u32 charger_valid;
+	u32 batt_valid;
+	u32 batt_capacity; /* in percentage */
+
+	u32 charger_status;
+	u32 charger_type;
+	u32 battery_status;
+	u32 battery_level;
+	u32 battery_voltage; /* in millie volts */
+	u32 battery_temp;  /* in celsius */
+
+	u32(*calculate_capacity) (u32 voltage);
+
+	s32 batt_handle;
+
+	struct power_supply *msm_psy_ac;
+	struct power_supply *msm_psy_usb;
+	struct power_supply *msm_psy_batt;
+	struct power_supply *current_ps;
+
+	struct msm_rpc_client *batt_client;
+	struct msm_rpc_endpoint *chg_ep;
+
+	wait_queue_head_t wait_q;
+
+	u32 vbatt_modify_reply_avail;
+
+	struct early_suspend early_suspend;
+};
+
+static struct msm_battery_info msm_batt_info = {
+	.batt_handle = INVALID_BATT_HANDLE,
+	.charger_status = CHARGER_STATUS_BAD,
+	.charger_type = CHARGER_TYPE_INVALID,
+	.battery_status = BATTERY_STATUS_GOOD,
+	.battery_level = BATTERY_LEVEL_FULL,
+	.battery_voltage = BATTERY_HIGH,
+	.batt_capacity = 100,
+	.batt_status = POWER_SUPPLY_STATUS_DISCHARGING,
+	.batt_health = POWER_SUPPLY_HEALTH_GOOD,
+	.batt_valid  = 1,
+	.battery_temp = 23,
+	.vbatt_modify_reply_avail = 0,
+};
+
+static enum power_supply_property msm_power_props[] = {
+	POWER_SUPPLY_PROP_ONLINE,
+};
+
+static char *msm_power_supplied_to[] = {
+	"battery",
+};
+
+static int msm_power_get_property(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  union power_supply_propval *val)
+{
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		if (psy->type == POWER_SUPPLY_TYPE_MAINS) {
+			val->intval = msm_batt_info.current_chg_source & AC_CHG
+			    ? 1 : 0;
+		}
+		if (psy->type == POWER_SUPPLY_TYPE_USB) {
+			val->intval = msm_batt_info.current_chg_source & USB_CHG
+			    ? 1 : 0;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static struct power_supply msm_psy_ac = {
+	.name = "ac",
+	.type = POWER_SUPPLY_TYPE_MAINS,
+	.supplied_to = msm_power_supplied_to,
+	.num_supplicants = ARRAY_SIZE(msm_power_supplied_to),
+	.properties = msm_power_props,
+	.num_properties = ARRAY_SIZE(msm_power_props),
+	.get_property = msm_power_get_property,
+};
+
+static struct power_supply msm_psy_usb = {
+	.name = "usb",
+	.type = POWER_SUPPLY_TYPE_USB,
+	.supplied_to = msm_power_supplied_to,
+	.num_supplicants = ARRAY_SIZE(msm_power_supplied_to),
+	.properties = msm_power_props,
+	.num_properties = ARRAY_SIZE(msm_power_props),
+	.get_property = msm_power_get_property,
+};
+
+static enum power_supply_property msm_batt_power_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_CAPACITY,
+};
+
+static int msm_batt_power_get_property(struct power_supply *psy,
+				       enum power_supply_property psp,
+				       union power_supply_propval *val)
+{
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		val->intval = msm_batt_info.batt_status;
+		break;
+	case POWER_SUPPLY_PROP_HEALTH:
+		val->intval = msm_batt_info.batt_health;
+		break;
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = msm_batt_info.batt_valid;
+		break;
+	case POWER_SUPPLY_PROP_TECHNOLOGY:
+		val->intval = msm_batt_info.batt_technology;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+		val->intval = msm_batt_info.voltage_max_design;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+		val->intval = msm_batt_info.voltage_min_design;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		val->intval = msm_batt_info.battery_voltage;
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY:
+		val->intval = msm_batt_info.batt_capacity;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static struct power_supply msm_psy_batt = {
+	.name = "battery",
+	.type = POWER_SUPPLY_TYPE_BATTERY,
+	.properties = msm_batt_power_props,
+	.num_properties = ARRAY_SIZE(msm_batt_power_props),
+	.get_property = msm_batt_power_get_property,
+};
+
+#ifndef CONFIG_BATTERY_MSM_FAKE
+struct msm_batt_get_volt_ret_data {
+	u32 battery_voltage;
+};
+
+static int msm_batt_get_volt_ret_func(struct msm_rpc_client *batt_client,
+				       void *buf, void *data)
+{
+	struct msm_batt_get_volt_ret_data *data_ptr, *buf_ptr;
+
+	data_ptr = (struct msm_batt_get_volt_ret_data *)data;
+	buf_ptr = (struct msm_batt_get_volt_ret_data *)buf;
+
+	data_ptr->battery_voltage = be32_to_cpu(buf_ptr->battery_voltage);
+
+	return 0;
+}
+
+static u32 msm_batt_get_vbatt_voltage(void)
+{
+	int rc;
+
+	struct msm_batt_get_volt_ret_data rep;
+
+	rc = msm_rpc_client_req(msm_batt_info.batt_client,
+			BATTERY_READ_MV_PROC,
+			NULL, NULL,
+			msm_batt_get_volt_ret_func, &rep,
+			msecs_to_jiffies(BATT_RPC_TIMEOUT));
+
+	if (rc < 0) {
+		pr_err("%s: FAIL: vbatt get volt. rc=%d\n", __func__, rc);
+		return 0;
+	}
+
+	return rep.battery_voltage;
+}
+
+#define	be32_to_cpu_self(v)	(v = be32_to_cpu(v))
+
+static int msm_batt_get_batt_chg_status(void)
+{
+	int rc;
+
+	struct rpc_req_batt_chg {
+		struct rpc_request_hdr hdr;
+		u32 more_data;
+	} req_batt_chg;
+	struct rpc_reply_batt_chg_v1 *v1p;
+
+	req_batt_chg.more_data = cpu_to_be32(1);
+
+	memset(&rep_batt_chg, 0, sizeof(rep_batt_chg));
+
+	v1p = &rep_batt_chg.v1;
+	rc = msm_rpc_call_reply(msm_batt_info.chg_ep,
+				ONCRPC_CHG_GET_GENERAL_STATUS_PROC,
+				&req_batt_chg, sizeof(req_batt_chg),
+				&rep_batt_chg, sizeof(rep_batt_chg),
+				msecs_to_jiffies(BATT_RPC_TIMEOUT));
+	if (rc < 0) {
+		pr_err("%s: ERROR. msm_rpc_call_reply failed! proc=%d rc=%d\n",
+		       __func__, ONCRPC_CHG_GET_GENERAL_STATUS_PROC, rc);
+		return rc;
+	} else if (be32_to_cpu(v1p->more_data)) {
+		be32_to_cpu_self(v1p->charger_status);
+		be32_to_cpu_self(v1p->charger_type);
+		be32_to_cpu_self(v1p->battery_status);
+		be32_to_cpu_self(v1p->battery_level);
+		be32_to_cpu_self(v1p->battery_voltage);
+		be32_to_cpu_self(v1p->battery_temp);
+	} else {
+		pr_err("%s: No battery/charger data in RPC reply\n", __func__);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static void msm_batt_update_psy_status(void)
+{
+	static u32 unnecessary_event_count;
+	u32	charger_status;
+	u32	charger_type;
+	u32	battery_status;
+	u32	battery_level;
+	u32     battery_voltage;
+	u32	battery_temp;
+	struct	power_supply	*supp;
+
+	if (msm_batt_get_batt_chg_status())
+		return;
+
+	charger_status = rep_batt_chg.v1.charger_status;
+	charger_type = rep_batt_chg.v1.charger_type;
+	battery_status = rep_batt_chg.v1.battery_status;
+	battery_level = rep_batt_chg.v1.battery_level;
+	battery_voltage = rep_batt_chg.v1.battery_voltage;
+	battery_temp = rep_batt_chg.v1.battery_temp;
+
+	/* Make correction for battery status */
+	if (battery_status == BATTERY_STATUS_INVALID_v1) {
+		if (msm_batt_info.chg_api_version < CHG_RPC_VER_3_1)
+			battery_status = BATTERY_STATUS_INVALID;
+	}
+
+	if (charger_status == msm_batt_info.charger_status &&
+	    charger_type == msm_batt_info.charger_type &&
+	    battery_status == msm_batt_info.battery_status &&
+	    battery_level == msm_batt_info.battery_level &&
+	    battery_voltage == msm_batt_info.battery_voltage &&
+	    battery_temp == msm_batt_info.battery_temp) {
+		/* Got unnecessary event from Modem PMIC VBATT driver.
+		 * Nothing changed in Battery or charger status.
+		 */
+		unnecessary_event_count++;
+		if ((unnecessary_event_count % 20) == 1)
+			DBG_LIMIT("BATT: same event count = %u\n",
+				 unnecessary_event_count);
+		return;
+	}
+
+	unnecessary_event_count = 0;
+
+	DBG_LIMIT("BATT: rcvd: %d, %d, %d, %d; %d, %d\n",
+		 charger_status, charger_type, battery_status,
+		 battery_level, battery_voltage, battery_temp);
+
+	if (battery_status == BATTERY_STATUS_INVALID &&
+	    battery_level != BATTERY_LEVEL_INVALID) {
+		DBG_LIMIT("BATT: change status(%d) to (%d) for level=%d\n",
+			 battery_status, BATTERY_STATUS_GOOD, battery_level);
+		battery_status = BATTERY_STATUS_GOOD;
+	}
+
+	if (msm_batt_info.charger_type != charger_type) {
+		if (charger_type == CHARGER_TYPE_USB_WALL ||
+		    charger_type == CHARGER_TYPE_USB_PC ||
+		    charger_type == CHARGER_TYPE_USB_CARKIT) {
+			DBG_LIMIT("BATT: USB charger plugged in\n");
+			msm_batt_info.current_chg_source = USB_CHG;
+			supp = &msm_psy_usb;
+		} else if (charger_type == CHARGER_TYPE_WALL) {
+			DBG_LIMIT("BATT: AC Wall changer plugged in\n");
+			msm_batt_info.current_chg_source = AC_CHG;
+			supp = &msm_psy_ac;
+		} else {
+			if (msm_batt_info.current_chg_source & AC_CHG)
+				DBG_LIMIT("BATT: AC Wall charger removed\n");
+			else if (msm_batt_info.current_chg_source & USB_CHG)
+				DBG_LIMIT("BATT: USB charger removed\n");
+			else
+				DBG_LIMIT("BATT: No charger present\n");
+			msm_batt_info.current_chg_source = 0;
+			supp = &msm_psy_batt;
+
+			/* Correct charger status */
+			if (charger_status != CHARGER_STATUS_INVALID) {
+				DBG_LIMIT("BATT: No charging!\n");
+				charger_status = CHARGER_STATUS_INVALID;
+				msm_batt_info.batt_status =
+					POWER_SUPPLY_STATUS_NOT_CHARGING;
+			}
+		}
+	} else
+		supp = NULL;
+
+	if (msm_batt_info.charger_status != charger_status) {
+		if (charger_status == CHARGER_STATUS_GOOD ||
+		    charger_status == CHARGER_STATUS_WEAK) {
+			if (msm_batt_info.current_chg_source) {
+				DBG_LIMIT("BATT: Charging.\n");
+				msm_batt_info.batt_status =
+					POWER_SUPPLY_STATUS_CHARGING;
+
+				/* Correct when supp==NULL */
+				if (msm_batt_info.current_chg_source & AC_CHG)
+					supp = &msm_psy_ac;
+				else
+					supp = &msm_psy_usb;
+			}
+		} else {
+			DBG_LIMIT("BATT: No charging.\n");
+			msm_batt_info.batt_status =
+				POWER_SUPPLY_STATUS_NOT_CHARGING;
+			supp = &msm_psy_batt;
+		}
+	} else {
+		/* Correct charger status */
+		if (charger_type != CHARGER_TYPE_INVALID &&
+		    charger_status == CHARGER_STATUS_GOOD) {
+			DBG_LIMIT("BATT: In charging\n");
+			msm_batt_info.batt_status =
+				POWER_SUPPLY_STATUS_CHARGING;
+		}
+	}
+
+	/* Correct battery voltage and status */
+	if (!battery_voltage) {
+		if (charger_status == CHARGER_STATUS_INVALID) {
+			DBG_LIMIT("BATT: Read VBATT\n");
+			battery_voltage = msm_batt_get_vbatt_voltage();
+		} else
+			/* Use previous */
+			battery_voltage = msm_batt_info.battery_voltage;
+	}
+	if (battery_status == BATTERY_STATUS_INVALID) {
+		if (battery_voltage >= msm_batt_info.voltage_min_design &&
+		    battery_voltage <= msm_batt_info.voltage_max_design) {
+			DBG_LIMIT("BATT: Battery valid\n");
+			msm_batt_info.batt_valid = 1;
+			battery_status = BATTERY_STATUS_GOOD;
+		}
+	}
+
+	if (msm_batt_info.battery_status != battery_status) {
+		if (battery_status != BATTERY_STATUS_INVALID) {
+			msm_batt_info.batt_valid = 1;
+
+			if (battery_status == BATTERY_STATUS_BAD) {
+				DBG_LIMIT("BATT: Battery bad.\n");
+				msm_batt_info.batt_health =
+					POWER_SUPPLY_HEALTH_DEAD;
+			} else if (battery_status == BATTERY_STATUS_BAD_TEMP) {
+				DBG_LIMIT("BATT: Battery overheat.\n");
+				msm_batt_info.batt_health =
+					POWER_SUPPLY_HEALTH_OVERHEAT;
+			} else {
+				DBG_LIMIT("BATT: Battery good.\n");
+				msm_batt_info.batt_health =
+					POWER_SUPPLY_HEALTH_GOOD;
+			}
+		} else {
+			msm_batt_info.batt_valid = 0;
+			DBG_LIMIT("BATT: Battery invalid.\n");
+			msm_batt_info.batt_health = POWER_SUPPLY_HEALTH_UNKNOWN;
+		}
+
+		if (msm_batt_info.batt_status != POWER_SUPPLY_STATUS_CHARGING) {
+			if (battery_status == BATTERY_STATUS_INVALID) {
+				DBG_LIMIT("BATT: Battery -> unknown\n");
+				msm_batt_info.batt_status =
+					POWER_SUPPLY_STATUS_UNKNOWN;
+			} else {
+				DBG_LIMIT("BATT: Battery -> discharging\n");
+				msm_batt_info.batt_status =
+					POWER_SUPPLY_STATUS_DISCHARGING;
+			}
+		}
+
+		if (!supp) {
+			if (msm_batt_info.current_chg_source) {
+				if (msm_batt_info.current_chg_source & AC_CHG)
+					supp = &msm_psy_ac;
+				else
+					supp = &msm_psy_usb;
+			} else
+				supp = &msm_psy_batt;
+		}
+	}
+
+	msm_batt_info.charger_status 	= charger_status;
+	msm_batt_info.charger_type 	= charger_type;
+	msm_batt_info.battery_status 	= battery_status;
+	msm_batt_info.battery_level 	= battery_level;
+	msm_batt_info.battery_temp 	= battery_temp;
+
+	if (msm_batt_info.battery_voltage != battery_voltage) {
+		msm_batt_info.battery_voltage  	= battery_voltage;
+		msm_batt_info.batt_capacity =
+			msm_batt_info.calculate_capacity(battery_voltage);
+		DBG_LIMIT("BATT: voltage = %u mV [capacity = %d%%]\n",
+			 battery_voltage, msm_batt_info.batt_capacity);
+
+		if (!supp)
+			supp = msm_batt_info.current_ps;
+	}
+
+	if (supp) {
+		msm_batt_info.current_ps = supp;
+		DBG_LIMIT("BATT: Supply = %s\n", supp->name);
+		power_supply_changed(supp);
+	}
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+struct batt_modify_client_req {
+
+	u32 client_handle;
+
+	/* The voltage at which callback (CB) should be called. */
+	u32 desired_batt_voltage;
+
+	/* The direction when the CB should be called. */
+	u32 voltage_direction;
+
+	/* The registered callback to be called when voltage and
+	 * direction specs are met. */
+	u32 batt_cb_id;
+
+	/* The call back data */
+	u32 cb_data;
+};
+
+struct batt_modify_client_rep {
+	u32 result;
+};
+
+static int msm_batt_modify_client_arg_func(struct msm_rpc_client *batt_client,
+				       void *buf, void *data)
+{
+	struct batt_modify_client_req *batt_modify_client_req =
+		(struct batt_modify_client_req *)data;
+	u32 *req = (u32 *)buf;
+	int size = 0;
+
+	*req = cpu_to_be32(batt_modify_client_req->client_handle);
+	size += sizeof(u32);
+	req++;
+
+	*req = cpu_to_be32(batt_modify_client_req->desired_batt_voltage);
+	size += sizeof(u32);
+	req++;
+
+	*req = cpu_to_be32(batt_modify_client_req->voltage_direction);
+	size += sizeof(u32);
+	req++;
+
+	*req = cpu_to_be32(batt_modify_client_req->batt_cb_id);
+	size += sizeof(u32);
+	req++;
+
+	*req = cpu_to_be32(batt_modify_client_req->cb_data);
+	size += sizeof(u32);
+
+	return size;
+}
+
+static int msm_batt_modify_client_ret_func(struct msm_rpc_client *batt_client,
+				       void *buf, void *data)
+{
+	struct  batt_modify_client_rep *data_ptr, *buf_ptr;
+
+	data_ptr = (struct batt_modify_client_rep *)data;
+	buf_ptr = (struct batt_modify_client_rep *)buf;
+
+	data_ptr->result = be32_to_cpu(buf_ptr->result);
+
+	return 0;
+}
+
+static int msm_batt_modify_client(u32 client_handle, u32 desired_batt_voltage,
+	     u32 voltage_direction, u32 batt_cb_id, u32 cb_data)
+{
+	int rc;
+
+	struct batt_modify_client_req  req;
+	struct batt_modify_client_rep rep;
+
+	req.client_handle = client_handle;
+	req.desired_batt_voltage = desired_batt_voltage;
+	req.voltage_direction = voltage_direction;
+	req.batt_cb_id = batt_cb_id;
+	req.cb_data = cb_data;
+
+	rc = msm_rpc_client_req(msm_batt_info.batt_client,
+			BATTERY_MODIFY_CLIENT_PROC,
+			msm_batt_modify_client_arg_func, &req,
+			msm_batt_modify_client_ret_func, &rep,
+			msecs_to_jiffies(BATT_RPC_TIMEOUT));
+
+	if (rc < 0) {
+		pr_err("%s: ERROR. failed to modify  Vbatt client\n",
+		       __func__);
+		return rc;
+	}
+
+	if (rep.result != BATTERY_MODIFICATION_SUCCESSFUL) {
+		pr_err("%s: ERROR. modify client failed. result = %u\n",
+		       __func__, rep.result);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+void msm_batt_early_suspend(struct early_suspend *h)
+{
+	int rc;
+
+	pr_debug("%s: enter\n", __func__);
+
+	if (msm_batt_info.batt_handle != INVALID_BATT_HANDLE) {
+		rc = msm_batt_modify_client(msm_batt_info.batt_handle,
+				BATTERY_LOW, BATTERY_VOLTAGE_BELOW_THIS_LEVEL,
+				BATTERY_CB_ID_LOW_VOL, BATTERY_LOW);
+
+		if (rc < 0) {
+			pr_err("%s: msm_batt_modify_client. rc=%d\n",
+			       __func__, rc);
+			return;
+		}
+	} else {
+		pr_err("%s: ERROR. invalid batt_handle\n", __func__);
+		return;
+	}
+
+	pr_debug("%s: exit\n", __func__);
+}
+
+void msm_batt_late_resume(struct early_suspend *h)
+{
+	int rc;
+
+	pr_debug("%s: enter\n", __func__);
+
+	if (msm_batt_info.batt_handle != INVALID_BATT_HANDLE) {
+		rc = msm_batt_modify_client(msm_batt_info.batt_handle,
+				BATTERY_LOW, BATTERY_ALL_ACTIVITY,
+			       BATTERY_CB_ID_ALL_ACTIV, BATTERY_ALL_ACTIVITY);
+		if (rc < 0) {
+			pr_err("%s: msm_batt_modify_client FAIL rc=%d\n",
+			       __func__, rc);
+			return;
+		}
+	} else {
+		pr_err("%s: ERROR. invalid batt_handle\n", __func__);
+		return;
+	}
+
+	msm_batt_update_psy_status();
+	pr_debug("%s: exit\n", __func__);
+}
+#endif
+
+struct msm_batt_vbatt_filter_req {
+	u32 batt_handle;
+	u32 enable_filter;
+	u32 vbatt_filter;
+};
+
+struct msm_batt_vbatt_filter_rep {
+	u32 result;
+};
+
+static int msm_batt_filter_arg_func(struct msm_rpc_client *batt_client,
+
+		void *buf, void *data)
+{
+	struct msm_batt_vbatt_filter_req *vbatt_filter_req =
+		(struct msm_batt_vbatt_filter_req *)data;
+	u32 *req = (u32 *)buf;
+	int size = 0;
+
+	*req = cpu_to_be32(vbatt_filter_req->batt_handle);
+	size += sizeof(u32);
+	req++;
+
+	*req = cpu_to_be32(vbatt_filter_req->enable_filter);
+	size += sizeof(u32);
+	req++;
+
+	*req = cpu_to_be32(vbatt_filter_req->vbatt_filter);
+	size += sizeof(u32);
+	return size;
+}
+
+static int msm_batt_filter_ret_func(struct msm_rpc_client *batt_client,
+				       void *buf, void *data)
+{
+
+	struct msm_batt_vbatt_filter_rep *data_ptr, *buf_ptr;
+
+	data_ptr = (struct msm_batt_vbatt_filter_rep *)data;
+	buf_ptr = (struct msm_batt_vbatt_filter_rep *)buf;
+
+	data_ptr->result = be32_to_cpu(buf_ptr->result);
+	return 0;
+}
+
+static int msm_batt_enable_filter(u32 vbatt_filter)
+{
+	int rc;
+	struct  msm_batt_vbatt_filter_req  vbatt_filter_req;
+	struct  msm_batt_vbatt_filter_rep  vbatt_filter_rep;
+
+	vbatt_filter_req.batt_handle = msm_batt_info.batt_handle;
+	vbatt_filter_req.enable_filter = 1;
+	vbatt_filter_req.vbatt_filter = vbatt_filter;
+
+	rc = msm_rpc_client_req(msm_batt_info.batt_client,
+			BATTERY_ENABLE_DISABLE_FILTER_PROC,
+			msm_batt_filter_arg_func, &vbatt_filter_req,
+			msm_batt_filter_ret_func, &vbatt_filter_rep,
+			msecs_to_jiffies(BATT_RPC_TIMEOUT));
+
+	if (rc < 0) {
+		pr_err("%s: FAIL: enable vbatt filter. rc=%d\n",
+		       __func__, rc);
+		return rc;
+	}
+
+	if (vbatt_filter_rep.result != BATTERY_DEREGISTRATION_SUCCESSFUL) {
+		pr_err("%s: FAIL: enable vbatt filter: result=%d\n",
+		       __func__, vbatt_filter_rep.result);
+		return -EIO;
+	}
+
+	pr_debug("%s: enable vbatt filter: OK\n", __func__);
+	return rc;
+}
+
+struct batt_client_registration_req {
+	/* The voltage at which callback (CB) should be called. */
+	u32 desired_batt_voltage;
+
+	/* The direction when the CB should be called. */
+	u32 voltage_direction;
+
+	/* The registered callback to be called when voltage and
+	 * direction specs are met. */
+	u32 batt_cb_id;
+
+	/* The call back data */
+	u32 cb_data;
+	u32 more_data;
+	u32 batt_error;
+};
+
+struct batt_client_registration_req_4_1 {
+	/* The voltage at which callback (CB) should be called. */
+	u32 desired_batt_voltage;
+
+	/* The direction when the CB should be called. */
+	u32 voltage_direction;
+
+	/* The registered callback to be called when voltage and
+	 * direction specs are met. */
+	u32 batt_cb_id;
+
+	/* The call back data */
+	u32 cb_data;
+	u32 batt_error;
+};
+
+struct batt_client_registration_rep {
+	u32 batt_handle;
+};
+
+struct batt_client_registration_rep_4_1 {
+	u32 batt_handle;
+	u32 more_data;
+	u32 err;
+};
+
+static int msm_batt_register_arg_func(struct msm_rpc_client *batt_client,
+				       void *buf, void *data)
+{
+	struct batt_client_registration_req *batt_reg_req =
+		(struct batt_client_registration_req *)data;
+
+	u32 *req = (u32 *)buf;
+	int size = 0;
+
+
+	if (msm_batt_info.batt_api_version == BATTERY_RPC_VER_4_1) {
+		*req = cpu_to_be32(batt_reg_req->desired_batt_voltage);
+		size += sizeof(u32);
+		req++;
+
+		*req = cpu_to_be32(batt_reg_req->voltage_direction);
+		size += sizeof(u32);
+		req++;
+
+		*req = cpu_to_be32(batt_reg_req->batt_cb_id);
+		size += sizeof(u32);
+		req++;
+
+		*req = cpu_to_be32(batt_reg_req->cb_data);
+		size += sizeof(u32);
+		req++;
+
+		*req = cpu_to_be32(batt_reg_req->batt_error);
+		size += sizeof(u32);
+
+		return size;
+	} else {
+		*req = cpu_to_be32(batt_reg_req->desired_batt_voltage);
+		size += sizeof(u32);
+		req++;
+
+		*req = cpu_to_be32(batt_reg_req->voltage_direction);
+		size += sizeof(u32);
+		req++;
+
+		*req = cpu_to_be32(batt_reg_req->batt_cb_id);
+		size += sizeof(u32);
+		req++;
+
+		*req = cpu_to_be32(batt_reg_req->cb_data);
+		size += sizeof(u32);
+		req++;
+
+		*req = cpu_to_be32(batt_reg_req->more_data);
+		size += sizeof(u32);
+		req++;
+
+		*req = cpu_to_be32(batt_reg_req->batt_error);
+		size += sizeof(u32);
+
+		return size;
+	}
+
+}
+
+static int msm_batt_register_ret_func(struct msm_rpc_client *batt_client,
+				       void *buf, void *data)
+{
+	struct batt_client_registration_rep *data_ptr, *buf_ptr;
+	struct batt_client_registration_rep_4_1 *data_ptr_4_1, *buf_ptr_4_1;
+
+	if (msm_batt_info.batt_api_version == BATTERY_RPC_VER_4_1) {
+		data_ptr_4_1 = (struct batt_client_registration_rep_4_1 *)data;
+		buf_ptr_4_1 = (struct batt_client_registration_rep_4_1 *)buf;
+
+		data_ptr_4_1->batt_handle
+			= be32_to_cpu(buf_ptr_4_1->batt_handle);
+		data_ptr_4_1->more_data
+			= be32_to_cpu(buf_ptr_4_1->more_data);
+		data_ptr_4_1->err = be32_to_cpu(buf_ptr_4_1->err);
+		return 0;
+	} else {
+		data_ptr = (struct batt_client_registration_rep *)data;
+		buf_ptr = (struct batt_client_registration_rep *)buf;
+
+		data_ptr->batt_handle = be32_to_cpu(buf_ptr->batt_handle);
+		return 0;
+	}
+}
+
+static int msm_batt_register(u32 desired_batt_voltage,
+			     u32 voltage_direction, u32 batt_cb_id, u32 cb_data)
+{
+	struct batt_client_registration_req batt_reg_req;
+	struct batt_client_registration_req_4_1 batt_reg_req_4_1;
+	struct batt_client_registration_rep batt_reg_rep;
+	struct batt_client_registration_rep_4_1 batt_reg_rep_4_1;
+	void *request;
+	void *reply;
+	int rc;
+
+	if (msm_batt_info.batt_api_version == BATTERY_RPC_VER_4_1) {
+		batt_reg_req_4_1.desired_batt_voltage = desired_batt_voltage;
+		batt_reg_req_4_1.voltage_direction = voltage_direction;
+		batt_reg_req_4_1.batt_cb_id = batt_cb_id;
+		batt_reg_req_4_1.cb_data = cb_data;
+		batt_reg_req_4_1.batt_error = 1;
+		request = &batt_reg_req_4_1;
+	} else {
+		batt_reg_req.desired_batt_voltage = desired_batt_voltage;
+		batt_reg_req.voltage_direction = voltage_direction;
+		batt_reg_req.batt_cb_id = batt_cb_id;
+		batt_reg_req.cb_data = cb_data;
+		batt_reg_req.more_data = 1;
+		batt_reg_req.batt_error = 0;
+		request = &batt_reg_req;
+	}
+
+	if (msm_batt_info.batt_api_version == BATTERY_RPC_VER_4_1)
+		reply = &batt_reg_rep_4_1;
+	else
+		reply = &batt_reg_rep;
+
+	rc = msm_rpc_client_req(msm_batt_info.batt_client,
+			BATTERY_REGISTER_PROC,
+			msm_batt_register_arg_func, request,
+			msm_batt_register_ret_func, reply,
+			msecs_to_jiffies(BATT_RPC_TIMEOUT));
+
+	if (rc < 0) {
+		pr_err("%s: FAIL: vbatt register. rc=%d\n", __func__, rc);
+		return rc;
+	}
+
+	if (msm_batt_info.batt_api_version == BATTERY_RPC_VER_4_1) {
+		if (batt_reg_rep_4_1.more_data != 0
+			&& batt_reg_rep_4_1.err
+				!= BATTERY_REGISTRATION_SUCCESSFUL) {
+			pr_err("%s: vBatt Registration Failed proc_num=%d\n"
+					, __func__, BATTERY_REGISTER_PROC);
+			return -EIO;
+		}
+		msm_batt_info.batt_handle = batt_reg_rep_4_1.batt_handle;
+	} else
+		msm_batt_info.batt_handle = batt_reg_rep.batt_handle;
+
+	return 0;
+}
+
+struct batt_client_deregister_req {
+	u32 batt_handle;
+};
+
+struct batt_client_deregister_rep {
+	u32 batt_error;
+};
+
+static int msm_batt_deregister_arg_func(struct msm_rpc_client *batt_client,
+				       void *buf, void *data)
+{
+	struct batt_client_deregister_req *deregister_req =
+		(struct  batt_client_deregister_req *)data;
+	u32 *req = (u32 *)buf;
+	int size = 0;
+
+	*req = cpu_to_be32(deregister_req->batt_handle);
+	size += sizeof(u32);
+
+	return size;
+}
+
+static int msm_batt_deregister_ret_func(struct msm_rpc_client *batt_client,
+				       void *buf, void *data)
+{
+	struct batt_client_deregister_rep *data_ptr, *buf_ptr;
+
+	data_ptr = (struct batt_client_deregister_rep *)data;
+	buf_ptr = (struct batt_client_deregister_rep *)buf;
+
+	data_ptr->batt_error = be32_to_cpu(buf_ptr->batt_error);
+
+	return 0;
+}
+
+static int msm_batt_deregister(u32 batt_handle)
+{
+	int rc;
+	struct batt_client_deregister_req req;
+	struct batt_client_deregister_rep rep;
+
+	req.batt_handle = batt_handle;
+
+	rc = msm_rpc_client_req(msm_batt_info.batt_client,
+			BATTERY_DEREGISTER_CLIENT_PROC,
+			msm_batt_deregister_arg_func, &req,
+			msm_batt_deregister_ret_func, &rep,
+			msecs_to_jiffies(BATT_RPC_TIMEOUT));
+
+	if (rc < 0) {
+		pr_err("%s: FAIL: vbatt deregister. rc=%d\n", __func__, rc);
+		return rc;
+	}
+
+	if (rep.batt_error != BATTERY_DEREGISTRATION_SUCCESSFUL) {
+		pr_err("%s: vbatt deregistration FAIL. error=%d, handle=%d\n",
+		       __func__, rep.batt_error, batt_handle);
+		return -EIO;
+	}
+
+	return 0;
+}
+#endif  /* CONFIG_BATTERY_MSM_FAKE */
+
+static int msm_batt_cleanup(void)
+{
+	int rc = 0;
+
+#ifndef CONFIG_BATTERY_MSM_FAKE
+	if (msm_batt_info.batt_handle != INVALID_BATT_HANDLE) {
+
+		rc = msm_batt_deregister(msm_batt_info.batt_handle);
+		if (rc < 0)
+			pr_err("%s: FAIL: msm_batt_deregister. rc=%d\n",
+			       __func__, rc);
+	}
+
+	msm_batt_info.batt_handle = INVALID_BATT_HANDLE;
+
+	if (msm_batt_info.batt_client)
+		msm_rpc_unregister_client(msm_batt_info.batt_client);
+#endif  /* CONFIG_BATTERY_MSM_FAKE */
+
+	if (msm_batt_info.msm_psy_ac)
+		power_supply_unregister(msm_batt_info.msm_psy_ac);
+
+	if (msm_batt_info.msm_psy_usb)
+		power_supply_unregister(msm_batt_info.msm_psy_usb);
+	if (msm_batt_info.msm_psy_batt)
+		power_supply_unregister(msm_batt_info.msm_psy_batt);
+
+#ifndef CONFIG_BATTERY_MSM_FAKE
+	if (msm_batt_info.chg_ep) {
+		rc = msm_rpc_close(msm_batt_info.chg_ep);
+		if (rc < 0) {
+			pr_err("%s: FAIL. msm_rpc_close(chg_ep). rc=%d\n",
+			       __func__, rc);
+		}
+	}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	if (msm_batt_info.early_suspend.suspend == msm_batt_early_suspend)
+		unregister_early_suspend(&msm_batt_info.early_suspend);
+#endif
+#endif
+	return rc;
+}
+
+static u32 msm_batt_capacity(u32 current_voltage)
+{
+	u32 low_voltage = msm_batt_info.voltage_min_design;
+	u32 high_voltage = msm_batt_info.voltage_max_design;
+
+	if (current_voltage <= low_voltage)
+		return 0;
+	else if (current_voltage >= high_voltage)
+		return 100;
+	else
+		return (current_voltage - low_voltage) * 100
+			/ (high_voltage - low_voltage);
+}
+
+#ifndef CONFIG_BATTERY_MSM_FAKE
+int msm_batt_get_charger_api_version(void)
+{
+	int rc ;
+	struct rpc_reply_hdr *reply;
+
+	struct rpc_req_chg_api_ver {
+		struct rpc_request_hdr hdr;
+		u32 more_data;
+	} req_chg_api_ver;
+
+	struct rpc_rep_chg_api_ver {
+		struct rpc_reply_hdr hdr;
+		u32 num_of_chg_api_versions;
+		u32 *chg_api_versions;
+	};
+
+	u32 num_of_versions;
+
+	struct rpc_rep_chg_api_ver *rep_chg_api_ver;
+
+
+	req_chg_api_ver.more_data = cpu_to_be32(1);
+
+	msm_rpc_setup_req(&req_chg_api_ver.hdr, CHG_RPC_PROG, CHG_RPC_VER_1_1,
+			  ONCRPC_CHARGER_API_VERSIONS_PROC);
+
+	rc = msm_rpc_write(msm_batt_info.chg_ep, &req_chg_api_ver,
+			sizeof(req_chg_api_ver));
+	if (rc < 0) {
+		pr_err("%s: FAIL: msm_rpc_write. proc=0x%08x, rc=%d\n",
+		       __func__, ONCRPC_CHARGER_API_VERSIONS_PROC, rc);
+		return rc;
+	}
+
+	for (;;) {
+		rc = msm_rpc_read(msm_batt_info.chg_ep, (void *) &reply, -1,
+				BATT_RPC_TIMEOUT);
+		if (rc < 0)
+			return rc;
+		if (rc < RPC_REQ_REPLY_COMMON_HEADER_SIZE) {
+			pr_err("%s: LENGTH ERR: msm_rpc_read. rc=%d (<%d)\n",
+			       __func__, rc, RPC_REQ_REPLY_COMMON_HEADER_SIZE);
+
+			rc = -EIO;
+			break;
+		}
+		/* we should not get RPC REQ or call packets -- ignore them */
+		if (reply->type == RPC_TYPE_REQ) {
+			pr_err("%s: TYPE ERR: type=%d (!=%d)\n",
+			       __func__, reply->type, RPC_TYPE_REQ);
+			kfree(reply);
+			continue;
+		}
+
+		/* If an earlier call timed out, we could get the (no
+		 * longer wanted) reply for it.	 Ignore replies that
+		 * we don't expect
+		 */
+		if (reply->xid != req_chg_api_ver.hdr.xid) {
+			pr_err("%s: XID ERR: xid=%d (!=%d)\n", __func__,
+			       reply->xid, req_chg_api_ver.hdr.xid);
+			kfree(reply);
+			continue;
+		}
+		if (reply->reply_stat != RPCMSG_REPLYSTAT_ACCEPTED) {
+			rc = -EPERM;
+			break;
+		}
+		if (reply->data.acc_hdr.accept_stat !=
+				RPC_ACCEPTSTAT_SUCCESS) {
+			rc = -EINVAL;
+			break;
+		}
+
+		rep_chg_api_ver = (struct rpc_rep_chg_api_ver *)reply;
+
+		num_of_versions =
+			be32_to_cpu(rep_chg_api_ver->num_of_chg_api_versions);
+
+		rep_chg_api_ver->chg_api_versions =  (u32 *)
+			((u8 *) reply + sizeof(struct rpc_reply_hdr) +
+			sizeof(rep_chg_api_ver->num_of_chg_api_versions));
+
+		rc = be32_to_cpu(
+			rep_chg_api_ver->chg_api_versions[num_of_versions - 1]);
+
+		pr_debug("%s: num_of_chg_api_versions = %u. "
+			"The chg api version = 0x%08x\n", __func__,
+			num_of_versions, rc);
+		break;
+	}
+	kfree(reply);
+	return rc;
+}
+
+static int msm_batt_cb_func(struct msm_rpc_client *client,
+			    void *buffer, int in_size)
+{
+	int rc = 0;
+	struct rpc_request_hdr *req;
+	u32 procedure;
+	u32 accept_status;
+
+	req = (struct rpc_request_hdr *)buffer;
+	procedure = be32_to_cpu(req->procedure);
+
+	switch (procedure) {
+	case BATTERY_CB_TYPE_PROC:
+		accept_status = RPC_ACCEPTSTAT_SUCCESS;
+		break;
+
+	default:
+		accept_status = RPC_ACCEPTSTAT_PROC_UNAVAIL;
+		pr_err("%s: ERROR. procedure (%d) not supported\n",
+		       __func__, procedure);
+		break;
+	}
+
+	msm_rpc_start_accepted_reply(msm_batt_info.batt_client,
+			be32_to_cpu(req->xid), accept_status);
+
+	rc = msm_rpc_send_accepted_reply(msm_batt_info.batt_client, 0);
+	if (rc)
+		pr_err("%s: FAIL: sending reply. rc=%d\n", __func__, rc);
+
+	if (accept_status == RPC_ACCEPTSTAT_SUCCESS)
+		msm_batt_update_psy_status();
+
+	return rc;
+}
+#endif  /* CONFIG_BATTERY_MSM_FAKE */
+
+static int __devinit msm_batt_probe(struct platform_device *pdev)
+{
+	int rc;
+	struct msm_psy_batt_pdata *pdata = pdev->dev.platform_data;
+
+	if (pdev->id != -1) {
+		dev_err(&pdev->dev,
+			"%s: MSM chipsets Can only support one"
+			" battery ", __func__);
+		return -EINVAL;
+	}
+
+#ifndef CONFIG_BATTERY_MSM_FAKE
+	if (pdata->avail_chg_sources & AC_CHG) {
+#else
+	{
+#endif
+		rc = power_supply_register(&pdev->dev, &msm_psy_ac);
+		if (rc < 0) {
+			dev_err(&pdev->dev,
+				"%s: power_supply_register failed"
+				" rc = %d\n", __func__, rc);
+			msm_batt_cleanup();
+			return rc;
+		}
+		msm_batt_info.msm_psy_ac = &msm_psy_ac;
+		msm_batt_info.avail_chg_sources |= AC_CHG;
+	}
+
+	if (pdata->avail_chg_sources & USB_CHG) {
+		rc = power_supply_register(&pdev->dev, &msm_psy_usb);
+		if (rc < 0) {
+			dev_err(&pdev->dev,
+				"%s: power_supply_register failed"
+				" rc = %d\n", __func__, rc);
+			msm_batt_cleanup();
+			return rc;
+		}
+		msm_batt_info.msm_psy_usb = &msm_psy_usb;
+		msm_batt_info.avail_chg_sources |= USB_CHG;
+	}
+
+	if (!msm_batt_info.msm_psy_ac && !msm_batt_info.msm_psy_usb) {
+
+		dev_err(&pdev->dev,
+			"%s: No external Power supply(AC or USB)"
+			"is avilable\n", __func__);
+		msm_batt_cleanup();
+		return -ENODEV;
+	}
+
+	msm_batt_info.voltage_max_design = pdata->voltage_max_design;
+	msm_batt_info.voltage_min_design = pdata->voltage_min_design;
+	msm_batt_info.batt_technology = pdata->batt_technology;
+	msm_batt_info.calculate_capacity = pdata->calculate_capacity;
+
+	if (!msm_batt_info.voltage_min_design)
+		msm_batt_info.voltage_min_design = BATTERY_LOW;
+	if (!msm_batt_info.voltage_max_design)
+		msm_batt_info.voltage_max_design = BATTERY_HIGH;
+
+	if (msm_batt_info.batt_technology == POWER_SUPPLY_TECHNOLOGY_UNKNOWN)
+		msm_batt_info.batt_technology = POWER_SUPPLY_TECHNOLOGY_LION;
+
+	if (!msm_batt_info.calculate_capacity)
+		msm_batt_info.calculate_capacity = msm_batt_capacity;
+
+	rc = power_supply_register(&pdev->dev, &msm_psy_batt);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "%s: power_supply_register failed"
+			" rc=%d\n", __func__, rc);
+		msm_batt_cleanup();
+		return rc;
+	}
+	msm_batt_info.msm_psy_batt = &msm_psy_batt;
+
+#ifndef CONFIG_BATTERY_MSM_FAKE
+	rc = msm_batt_register(BATTERY_LOW, BATTERY_ALL_ACTIVITY,
+			       BATTERY_CB_ID_ALL_ACTIV, BATTERY_ALL_ACTIVITY);
+	if (rc < 0) {
+		dev_err(&pdev->dev,
+			"%s: msm_batt_register failed rc = %d\n", __func__, rc);
+		msm_batt_cleanup();
+		return rc;
+	}
+
+	rc =  msm_batt_enable_filter(VBATT_FILTER);
+
+	if (rc < 0) {
+		dev_err(&pdev->dev,
+			"%s: msm_batt_enable_filter failed rc = %d\n",
+			__func__, rc);
+		msm_batt_cleanup();
+		return rc;
+	}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	msm_batt_info.early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN;
+	msm_batt_info.early_suspend.suspend = msm_batt_early_suspend;
+	msm_batt_info.early_suspend.resume = msm_batt_late_resume;
+	register_early_suspend(&msm_batt_info.early_suspend);
+#endif
+	msm_batt_update_psy_status();
+
+#else
+	power_supply_changed(&msm_psy_ac);
+#endif  /* CONFIG_BATTERY_MSM_FAKE */
+
+	return 0;
+}
+
+static int __devexit msm_batt_remove(struct platform_device *pdev)
+{
+	int rc;
+	rc = msm_batt_cleanup();
+
+	if (rc < 0) {
+		dev_err(&pdev->dev,
+			"%s: msm_batt_cleanup  failed rc=%d\n", __func__, rc);
+		return rc;
+	}
+	return 0;
+}
+
+static struct platform_driver msm_batt_driver = {
+	.probe = msm_batt_probe,
+	.remove = __devexit_p(msm_batt_remove),
+	.driver = {
+		   .name = "msm-battery",
+		   .owner = THIS_MODULE,
+		   },
+};
+
+static int __devinit msm_batt_init_rpc(void)
+{
+	int rc;
+
+#ifdef CONFIG_BATTERY_MSM_FAKE
+	pr_info("Faking MSM battery\n");
+#else
+
+	msm_batt_info.chg_ep =
+		msm_rpc_connect_compatible(CHG_RPC_PROG, CHG_RPC_VER_4_1, 0);
+	msm_batt_info.chg_api_version =  CHG_RPC_VER_4_1;
+	if (msm_batt_info.chg_ep == NULL) {
+		pr_err("%s: rpc connect CHG_RPC_PROG = NULL\n", __func__);
+		return -ENODEV;
+	}
+
+	if (IS_ERR(msm_batt_info.chg_ep)) {
+		msm_batt_info.chg_ep = msm_rpc_connect_compatible(
+				CHG_RPC_PROG, CHG_RPC_VER_3_1, 0);
+		msm_batt_info.chg_api_version =  CHG_RPC_VER_3_1;
+	}
+	if (IS_ERR(msm_batt_info.chg_ep)) {
+		msm_batt_info.chg_ep = msm_rpc_connect_compatible(
+				CHG_RPC_PROG, CHG_RPC_VER_1_1, 0);
+		msm_batt_info.chg_api_version =  CHG_RPC_VER_1_1;
+	}
+	if (IS_ERR(msm_batt_info.chg_ep)) {
+		msm_batt_info.chg_ep = msm_rpc_connect_compatible(
+				CHG_RPC_PROG, CHG_RPC_VER_1_3, 0);
+		msm_batt_info.chg_api_version =  CHG_RPC_VER_1_3;
+	}
+	if (IS_ERR(msm_batt_info.chg_ep)) {
+		msm_batt_info.chg_ep = msm_rpc_connect_compatible(
+				CHG_RPC_PROG, CHG_RPC_VER_2_2, 0);
+		msm_batt_info.chg_api_version =  CHG_RPC_VER_2_2;
+	}
+	if (IS_ERR(msm_batt_info.chg_ep)) {
+		rc = PTR_ERR(msm_batt_info.chg_ep);
+		pr_err("%s: FAIL: rpc connect for CHG_RPC_PROG. rc=%d\n",
+		       __func__, rc);
+		msm_batt_info.chg_ep = NULL;
+		return rc;
+	}
+
+	/* Get the real 1.x version */
+	if (msm_batt_info.chg_api_version == CHG_RPC_VER_1_1)
+		msm_batt_info.chg_api_version =
+			msm_batt_get_charger_api_version();
+
+	/* Fall back to 1.1 for default */
+	if (msm_batt_info.chg_api_version < 0)
+		msm_batt_info.chg_api_version = CHG_RPC_VER_1_1;
+	msm_batt_info.batt_api_version =  BATTERY_RPC_VER_4_1;
+
+	msm_batt_info.batt_client =
+		msm_rpc_register_client("battery", BATTERY_RPC_PROG,
+					BATTERY_RPC_VER_4_1,
+					1, msm_batt_cb_func);
+
+	if (msm_batt_info.batt_client == NULL) {
+		pr_err("%s: FAIL: rpc_register_client. batt_client=NULL\n",
+		       __func__);
+		return -ENODEV;
+	}
+	if (IS_ERR(msm_batt_info.batt_client)) {
+		msm_batt_info.batt_client =
+			msm_rpc_register_client("battery", BATTERY_RPC_PROG,
+						BATTERY_RPC_VER_1_1,
+						1, msm_batt_cb_func);
+		msm_batt_info.batt_api_version =  BATTERY_RPC_VER_1_1;
+	}
+	if (IS_ERR(msm_batt_info.batt_client)) {
+		msm_batt_info.batt_client =
+			msm_rpc_register_client("battery", BATTERY_RPC_PROG,
+						BATTERY_RPC_VER_2_1,
+						1, msm_batt_cb_func);
+		msm_batt_info.batt_api_version =  BATTERY_RPC_VER_2_1;
+	}
+	if (IS_ERR(msm_batt_info.batt_client)) {
+		msm_batt_info.batt_client =
+			msm_rpc_register_client("battery", BATTERY_RPC_PROG,
+						BATTERY_RPC_VER_5_1,
+						1, msm_batt_cb_func);
+		msm_batt_info.batt_api_version =  BATTERY_RPC_VER_5_1;
+	}
+	if (IS_ERR(msm_batt_info.batt_client)) {
+		rc = PTR_ERR(msm_batt_info.batt_client);
+		pr_err("%s: ERROR: rpc_register_client: rc = %d\n ",
+		       __func__, rc);
+		msm_batt_info.batt_client = NULL;
+		return rc;
+	}
+#endif  /* CONFIG_BATTERY_MSM_FAKE */
+
+	rc = platform_driver_register(&msm_batt_driver);
+
+	if (rc < 0)
+		pr_err("%s: FAIL: platform_driver_register. rc = %d\n",
+		       __func__, rc);
+
+	return rc;
+}
+
+static int __init msm_batt_init(void)
+{
+	int rc;
+
+	pr_debug("%s: enter\n", __func__);
+
+	rc = msm_batt_init_rpc();
+
+	if (rc < 0) {
+		pr_err("%s: FAIL: msm_batt_init_rpc.  rc=%d\n", __func__, rc);
+		msm_batt_cleanup();
+		return rc;
+	}
+
+	pr_info("%s: Charger/Battery = 0x%08x/0x%08x (RPC version)\n",
+		__func__, msm_batt_info.chg_api_version,
+		msm_batt_info.batt_api_version);
+
+	return 0;
+}
+
+static void __exit msm_batt_exit(void)
+{
+	platform_driver_unregister(&msm_batt_driver);
+}
+
+module_init(msm_batt_init);
+module_exit(msm_batt_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Kiran Kandi, Qualcomm Innovation Center, Inc.");
+MODULE_DESCRIPTION("Battery driver for Qualcomm MSM chipsets.");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:msm_battery");
diff --git a/drivers/power/msm_charger.c b/drivers/power/msm_charger.c
new file mode 100644
index 0000000..8594ec2
--- /dev/null
+++ b/drivers/power/msm_charger.c
@@ -0,0 +1,1286 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/mfd/pmic8058.h>
+#include <linux/interrupt.h>
+#include <linux/power_supply.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/debugfs.h>
+#include <linux/msm-charger.h>
+#include <linux/time.h>
+#include <linux/slab.h>
+#include <linux/wakelock.h>
+
+#include <asm/atomic.h>
+
+#include <mach/msm_hsusb.h>
+
+#define MSM_CHG_MAX_EVENTS		16
+#define CHARGING_TEOC_MS		9000000
+#define UPDATE_TIME_MS			60000
+#define RESUME_CHECK_PERIOD_MS		60000
+
+#define DEFAULT_BATT_MAX_V		4200
+#define DEFAULT_BATT_MIN_V		3200
+
+#define MSM_CHARGER_GAUGE_MISSING_VOLTS 3500
+#define MSM_CHARGER_GAUGE_MISSING_TEMP  35
+/**
+ * enum msm_battery_status
+ * @BATT_STATUS_ABSENT: battery not present
+ * @BATT_STATUS_ID_INVALID: battery present but the id is invalid
+ * @BATT_STATUS_DISCHARGING: battery is present and is discharging
+ * @BATT_STATUS_TRKL_CHARGING: battery is being trickle charged
+ * @BATT_STATUS_FAST_CHARGING: battery is being fast charged
+ * @BATT_STATUS_JUST_FINISHED_CHARGING: just finished charging,
+ *		battery is fully charged. Do not begin charging untill the
+ *		voltage falls below a threshold to avoid overcharging
+ * @BATT_STATUS_TEMPERATURE_OUT_OF_RANGE: battery present,
+					no charging, temp is hot/cold
+ */
+enum msm_battery_status {
+	BATT_STATUS_ABSENT,
+	BATT_STATUS_ID_INVALID,
+	BATT_STATUS_DISCHARGING,
+	BATT_STATUS_TRKL_CHARGING,
+	BATT_STATUS_FAST_CHARGING,
+	BATT_STATUS_JUST_FINISHED_CHARGING,
+	BATT_STATUS_TEMPERATURE_OUT_OF_RANGE,
+};
+
+struct msm_hardware_charger_priv {
+	struct list_head list;
+	struct msm_hardware_charger *hw_chg;
+	enum msm_hardware_charger_state hw_chg_state;
+	unsigned int max_source_current;
+	struct power_supply psy;
+};
+
+struct msm_charger_event {
+	enum msm_hardware_charger_event event;
+	struct msm_hardware_charger *hw_chg;
+};
+
+struct msm_charger_mux {
+	int inited;
+	struct list_head msm_hardware_chargers;
+	int count_chargers;
+	struct mutex msm_hardware_chargers_lock;
+
+	struct device *dev;
+
+	unsigned int max_voltage;
+	unsigned int min_voltage;
+
+	unsigned int safety_time;
+	struct delayed_work teoc_work;
+
+	unsigned int update_time;
+	int stop_update;
+	struct delayed_work update_heartbeat_work;
+
+	struct mutex status_lock;
+	enum msm_battery_status batt_status;
+	struct msm_hardware_charger_priv *current_chg_priv;
+	struct msm_hardware_charger_priv *current_mon_priv;
+
+	unsigned int (*get_batt_capacity_percent) (void);
+
+	struct msm_charger_event *queue;
+	int tail;
+	int head;
+	spinlock_t queue_lock;
+	int queue_count;
+	struct work_struct queue_work;
+	struct workqueue_struct *event_wq_thread;
+	struct wake_lock wl;
+};
+
+static struct msm_charger_mux msm_chg;
+
+static struct msm_battery_gauge *msm_batt_gauge;
+
+static int is_chg_capable_of_charging(struct msm_hardware_charger_priv *priv)
+{
+	if (priv->hw_chg_state == CHG_READY_STATE
+	    || priv->hw_chg_state == CHG_CHARGING_STATE)
+		return 1;
+
+	return 0;
+}
+
+static int is_batt_status_capable_of_charging(void)
+{
+	if (msm_chg.batt_status == BATT_STATUS_ABSENT
+	    || msm_chg.batt_status == BATT_STATUS_TEMPERATURE_OUT_OF_RANGE
+	    || msm_chg.batt_status == BATT_STATUS_ID_INVALID
+	    || msm_chg.batt_status == BATT_STATUS_JUST_FINISHED_CHARGING)
+		return 0;
+	return 1;
+}
+
+static int is_batt_status_charging(void)
+{
+	if (msm_chg.batt_status == BATT_STATUS_TRKL_CHARGING
+	    || msm_chg.batt_status == BATT_STATUS_FAST_CHARGING)
+		return 1;
+	return 0;
+}
+
+static int is_battery_present(void)
+{
+	if (msm_batt_gauge && msm_batt_gauge->is_battery_present)
+		return msm_batt_gauge->is_battery_present();
+	else {
+		pr_err("msm-charger: no batt gauge batt=absent\n");
+		return 0;
+	}
+}
+
+static int is_battery_temp_within_range(void)
+{
+	if (msm_batt_gauge && msm_batt_gauge->is_battery_temp_within_range)
+		return msm_batt_gauge->is_battery_temp_within_range();
+	else {
+		pr_err("msm-charger no batt gauge batt=out_of_temperatur\n");
+		return 0;
+	}
+}
+
+static int is_battery_id_valid(void)
+{
+	if (msm_batt_gauge && msm_batt_gauge->is_battery_id_valid)
+		return msm_batt_gauge->is_battery_id_valid();
+	else {
+		pr_err("msm-charger no batt gauge batt=id_invalid\n");
+		return 0;
+	}
+}
+
+static int get_prop_battery_mvolts(void)
+{
+	if (msm_batt_gauge && msm_batt_gauge->get_battery_mvolts)
+		return msm_batt_gauge->get_battery_mvolts();
+	else {
+		pr_err("msm-charger no batt gauge assuming 3.5V\n");
+		return MSM_CHARGER_GAUGE_MISSING_VOLTS;
+	}
+}
+
+static int get_battery_temperature(void)
+{
+	if (msm_batt_gauge && msm_batt_gauge->get_battery_temperature)
+		return msm_batt_gauge->get_battery_temperature();
+	else {
+		pr_err("msm-charger no batt gauge assuming 35 deg G\n");
+		return MSM_CHARGER_GAUGE_MISSING_TEMP;
+	}
+}
+
+static int get_prop_batt_capacity(void)
+{
+	int capacity;
+
+	if (msm_batt_gauge && msm_batt_gauge->get_batt_remaining_capacity)
+		capacity = msm_batt_gauge->get_batt_remaining_capacity();
+	else
+		capacity = msm_chg.get_batt_capacity_percent();
+
+	if (capacity <= 10)
+		pr_err("battery capacity very low = %d\n", capacity);
+
+	return capacity;
+}
+
+static int get_prop_batt_health(void)
+{
+	int status = 0;
+
+	if (msm_chg.batt_status == BATT_STATUS_TEMPERATURE_OUT_OF_RANGE)
+		status = POWER_SUPPLY_HEALTH_OVERHEAT;
+	else
+		status = POWER_SUPPLY_HEALTH_GOOD;
+
+	return status;
+}
+
+static int get_prop_charge_type(void)
+{
+	int status = 0;
+
+	if (msm_chg.batt_status == BATT_STATUS_TRKL_CHARGING)
+		status = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+	else if (msm_chg.batt_status == BATT_STATUS_FAST_CHARGING)
+		status = POWER_SUPPLY_CHARGE_TYPE_FAST;
+	else
+		status = POWER_SUPPLY_CHARGE_TYPE_NONE;
+
+	return status;
+}
+
+static int get_prop_batt_status(void)
+{
+	int status = 0;
+
+	if (msm_batt_gauge && msm_batt_gauge->get_battery_status) {
+		status = msm_batt_gauge->get_battery_status();
+		if (status == POWER_SUPPLY_STATUS_CHARGING ||
+			status == POWER_SUPPLY_STATUS_FULL ||
+			status == POWER_SUPPLY_STATUS_DISCHARGING)
+			return status;
+	}
+
+	if (is_batt_status_charging())
+		status = POWER_SUPPLY_STATUS_CHARGING;
+	else if (msm_chg.batt_status ==
+		 BATT_STATUS_JUST_FINISHED_CHARGING
+			 && msm_chg.current_chg_priv != NULL)
+		status = POWER_SUPPLY_STATUS_FULL;
+	else
+		status = POWER_SUPPLY_STATUS_DISCHARGING;
+
+	return status;
+}
+
+ /* This function should only be called within handle_event or resume */
+static void update_batt_status(void)
+{
+	if (is_battery_present()) {
+		if (is_battery_id_valid()) {
+			if (msm_chg.batt_status == BATT_STATUS_ABSENT
+				|| msm_chg.batt_status
+					== BATT_STATUS_ID_INVALID) {
+				msm_chg.batt_status = BATT_STATUS_DISCHARGING;
+			}
+		} else
+			msm_chg.batt_status = BATT_STATUS_ID_INVALID;
+	 } else
+		msm_chg.batt_status = BATT_STATUS_ABSENT;
+}
+
+static enum power_supply_property msm_power_props[] = {
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_ONLINE,
+};
+
+static char *msm_power_supplied_to[] = {
+	"battery",
+};
+
+static int msm_power_get_property(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  union power_supply_propval *val)
+{
+	struct msm_hardware_charger_priv *priv;
+
+	priv = container_of(psy, struct msm_hardware_charger_priv, psy);
+	switch (psp) {
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = !(priv->hw_chg_state == CHG_ABSENT_STATE);
+		break;
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = (priv->hw_chg_state == CHG_READY_STATE)
+			|| (priv->hw_chg_state == CHG_CHARGING_STATE);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static enum power_supply_property msm_batt_power_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_CHARGE_TYPE,
+	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_CAPACITY,
+};
+
+static int msm_batt_power_get_property(struct power_supply *psy,
+				       enum power_supply_property psp,
+				       union power_supply_propval *val)
+{
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		val->intval = get_prop_batt_status();
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_TYPE:
+		val->intval = get_prop_charge_type();
+		break;
+	case POWER_SUPPLY_PROP_HEALTH:
+		val->intval = get_prop_batt_health();
+		break;
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = !(msm_chg.batt_status == BATT_STATUS_ABSENT);
+		break;
+	case POWER_SUPPLY_PROP_TECHNOLOGY:
+		val->intval = POWER_SUPPLY_TECHNOLOGY_NiMH;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+		val->intval = msm_chg.max_voltage * 1000;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+		val->intval = msm_chg.min_voltage * 1000;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		val->intval = get_prop_battery_mvolts();
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY:
+		val->intval = get_prop_batt_capacity();
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static struct power_supply msm_psy_batt = {
+	.name = "battery",
+	.type = POWER_SUPPLY_TYPE_BATTERY,
+	.properties = msm_batt_power_props,
+	.num_properties = ARRAY_SIZE(msm_batt_power_props),
+	.get_property = msm_batt_power_get_property,
+};
+
+static int usb_chg_current;
+static struct msm_hardware_charger_priv *usb_hw_chg_priv;
+static void (*notify_vbus_state_func_ptr)(int);
+static int usb_notified_of_insertion;
+
+/* this is passed to the hsusb via platform_data msm_otg_pdata */
+int msm_charger_register_vbus_sn(void (*callback)(int))
+{
+	pr_debug(KERN_INFO "%s\n", __func__);
+	notify_vbus_state_func_ptr = callback;
+	return 0;
+}
+
+/* this is passed to the hsusb via platform_data msm_otg_pdata */
+void msm_charger_unregister_vbus_sn(void (*callback)(int))
+{
+	pr_debug(KERN_INFO "%s\n", __func__);
+	notify_vbus_state_func_ptr = NULL;
+}
+
+static void notify_usb_of_the_plugin_event(struct msm_hardware_charger_priv
+					   *hw_chg, int plugin)
+{
+	plugin = !!plugin;
+	if (plugin == 1 && usb_notified_of_insertion == 0) {
+		usb_notified_of_insertion = 1;
+		if (notify_vbus_state_func_ptr) {
+			dev_dbg(msm_chg.dev, "%s notifying plugin\n", __func__);
+			(*notify_vbus_state_func_ptr) (plugin);
+		} else
+			dev_dbg(msm_chg.dev, "%s unable to notify plugin\n",
+				__func__);
+		usb_hw_chg_priv = hw_chg;
+	}
+	if (plugin == 0 && usb_notified_of_insertion == 1) {
+		if (notify_vbus_state_func_ptr) {
+			dev_dbg(msm_chg.dev, "%s notifying unplugin\n",
+				__func__);
+			(*notify_vbus_state_func_ptr) (plugin);
+		} else
+			dev_dbg(msm_chg.dev, "%s unable to notify unplugin\n",
+				__func__);
+		usb_notified_of_insertion = 0;
+		usb_hw_chg_priv = NULL;
+	}
+}
+
+static unsigned int msm_chg_get_batt_capacity_percent(void)
+{
+	unsigned int current_voltage = get_prop_battery_mvolts();
+	unsigned int low_voltage = msm_chg.min_voltage;
+	unsigned int high_voltage = msm_chg.max_voltage;
+
+	if (current_voltage <= low_voltage)
+		return 0;
+	else if (current_voltage >= high_voltage)
+		return 100;
+	else
+		return (current_voltage - low_voltage) * 100
+		    / (high_voltage - low_voltage);
+}
+
+#ifdef DEBUG
+static inline void debug_print(const char *func,
+			       struct msm_hardware_charger_priv *hw_chg_priv)
+{
+	dev_dbg(msm_chg.dev,
+		"%s current=(%s)(s=%d)(r=%d) new=(%s)(s=%d)(r=%d) batt=%d En\n",
+		func,
+		msm_chg.current_chg_priv ? msm_chg.current_chg_priv->
+		hw_chg->name : "none",
+		msm_chg.current_chg_priv ? msm_chg.
+		current_chg_priv->hw_chg_state : -1,
+		msm_chg.current_chg_priv ? msm_chg.current_chg_priv->
+		hw_chg->rating : -1,
+		hw_chg_priv ? hw_chg_priv->hw_chg->name : "none",
+		hw_chg_priv ? hw_chg_priv->hw_chg_state : -1,
+		hw_chg_priv ? hw_chg_priv->hw_chg->rating : -1,
+		msm_chg.batt_status);
+}
+#else
+static inline void debug_print(const char *func,
+			       struct msm_hardware_charger_priv *hw_chg_priv)
+{
+}
+#endif
+
+static struct msm_hardware_charger_priv *find_best_charger(void)
+{
+	struct msm_hardware_charger_priv *hw_chg_priv;
+	struct msm_hardware_charger_priv *better;
+	int rating;
+
+	better = NULL;
+	rating = 0;
+
+	list_for_each_entry(hw_chg_priv, &msm_chg.msm_hardware_chargers, list) {
+		if (is_chg_capable_of_charging(hw_chg_priv)) {
+			if (hw_chg_priv->hw_chg->rating > rating) {
+				rating = hw_chg_priv->hw_chg->rating;
+				better = hw_chg_priv;
+			}
+		}
+	}
+
+	return better;
+}
+
+static int msm_charging_switched(struct msm_hardware_charger_priv *priv)
+{
+	int ret = 0;
+
+	if (priv->hw_chg->charging_switched)
+		ret = priv->hw_chg->charging_switched(priv->hw_chg);
+	return ret;
+}
+
+static int msm_stop_charging(struct msm_hardware_charger_priv *priv)
+{
+	int ret;
+
+	ret = priv->hw_chg->stop_charging(priv->hw_chg);
+	if (!ret)
+		wake_unlock(&msm_chg.wl);
+	return ret;
+}
+
+static void msm_enable_system_current(struct msm_hardware_charger_priv *priv)
+{
+	if (priv->hw_chg->start_system_current)
+		priv->hw_chg->start_system_current(priv->hw_chg,
+					 priv->max_source_current);
+}
+
+static void msm_disable_system_current(struct msm_hardware_charger_priv *priv)
+{
+	if (priv->hw_chg->stop_system_current)
+		priv->hw_chg->stop_system_current(priv->hw_chg);
+}
+
+/* the best charger has been selected -start charging from current_chg_priv */
+static int msm_start_charging(void)
+{
+	int ret;
+	struct msm_hardware_charger_priv *priv;
+
+	priv = msm_chg.current_chg_priv;
+	wake_lock(&msm_chg.wl);
+	ret = priv->hw_chg->start_charging(priv->hw_chg, msm_chg.max_voltage,
+					 priv->max_source_current);
+	if (ret) {
+		wake_unlock(&msm_chg.wl);
+		dev_err(msm_chg.dev, "%s couldnt start chg error = %d\n",
+			priv->hw_chg->name, ret);
+	} else
+		priv->hw_chg_state = CHG_CHARGING_STATE;
+
+	return ret;
+}
+
+static void handle_charging_done(struct msm_hardware_charger_priv *priv)
+{
+	if (msm_chg.current_chg_priv == priv) {
+		if (msm_chg.current_chg_priv->hw_chg_state ==
+		    CHG_CHARGING_STATE)
+			if (msm_stop_charging(msm_chg.current_chg_priv)) {
+				dev_err(msm_chg.dev, "%s couldnt stop chg\n",
+					msm_chg.current_chg_priv->hw_chg->name);
+			}
+		msm_chg.current_chg_priv->hw_chg_state = CHG_READY_STATE;
+
+		msm_chg.batt_status = BATT_STATUS_JUST_FINISHED_CHARGING;
+		dev_info(msm_chg.dev, "%s: stopping safety timer work\n",
+				__func__);
+		cancel_delayed_work(&msm_chg.teoc_work);
+
+		if (msm_batt_gauge && msm_batt_gauge->monitor_for_recharging)
+			msm_batt_gauge->monitor_for_recharging();
+		else
+			dev_err(msm_chg.dev,
+			      "%s: no batt gauge recharge monitor\n", __func__);
+	}
+}
+
+static void teoc(struct work_struct *work)
+{
+	/* we have been charging too long - stop charging */
+	dev_info(msm_chg.dev, "%s: safety timer work expired\n", __func__);
+
+	mutex_lock(&msm_chg.status_lock);
+	if (msm_chg.current_chg_priv != NULL
+	    && msm_chg.current_chg_priv->hw_chg_state == CHG_CHARGING_STATE) {
+		handle_charging_done(msm_chg.current_chg_priv);
+	}
+	mutex_unlock(&msm_chg.status_lock);
+}
+
+static void handle_battery_inserted(void)
+{
+	/* if a charger is already present start charging */
+	if (msm_chg.current_chg_priv != NULL &&
+	    is_batt_status_capable_of_charging() &&
+	    !is_batt_status_charging()) {
+		if (msm_start_charging()) {
+			dev_err(msm_chg.dev, "%s couldnt start chg\n",
+				msm_chg.current_chg_priv->hw_chg->name);
+			return;
+		}
+		msm_chg.batt_status = BATT_STATUS_TRKL_CHARGING;
+
+		dev_info(msm_chg.dev, "%s: starting safety timer work\n",
+				__func__);
+		queue_delayed_work(msm_chg.event_wq_thread,
+					&msm_chg.teoc_work,
+				      round_jiffies_relative(msecs_to_jiffies
+							     (msm_chg.
+							      safety_time)));
+	}
+}
+
+static void handle_battery_removed(void)
+{
+	/* if a charger is charging the battery stop it */
+	if (msm_chg.current_chg_priv != NULL
+	    && msm_chg.current_chg_priv->hw_chg_state == CHG_CHARGING_STATE) {
+		if (msm_stop_charging(msm_chg.current_chg_priv)) {
+			dev_err(msm_chg.dev, "%s couldnt stop chg\n",
+				msm_chg.current_chg_priv->hw_chg->name);
+		}
+		msm_chg.current_chg_priv->hw_chg_state = CHG_READY_STATE;
+
+		dev_info(msm_chg.dev, "%s: stopping safety timer work\n",
+				__func__);
+		cancel_delayed_work(&msm_chg.teoc_work);
+	}
+}
+
+static void update_heartbeat(struct work_struct *work)
+{
+	int temperature;
+
+	if (msm_chg.batt_status == BATT_STATUS_ABSENT
+		|| msm_chg.batt_status == BATT_STATUS_ID_INVALID) {
+		if (is_battery_present())
+			if (is_battery_id_valid()) {
+				msm_chg.batt_status = BATT_STATUS_DISCHARGING;
+				handle_battery_inserted();
+			}
+	} else {
+		if (!is_battery_present()) {
+			msm_chg.batt_status = BATT_STATUS_ABSENT;
+			handle_battery_removed();
+		}
+		/*
+		 * check battery id because a good battery could be removed
+		 * and replaced with a invalid battery.
+		 */
+		if (!is_battery_id_valid()) {
+			msm_chg.batt_status = BATT_STATUS_ID_INVALID;
+			handle_battery_removed();
+		}
+	}
+	pr_debug("msm-charger %s batt_status= %d\n",
+				__func__, msm_chg.batt_status);
+
+	if (msm_chg.current_chg_priv
+		&& msm_chg.current_chg_priv->hw_chg_state
+			== CHG_CHARGING_STATE) {
+		temperature = get_battery_temperature();
+		/* TODO implement JEITA SPEC*/
+	}
+
+	/* notify that the voltage has changed
+	 * the read of the capacity will trigger a
+	 * voltage read*/
+	power_supply_changed(&msm_psy_batt);
+
+	if (msm_chg.stop_update) {
+		msm_chg.stop_update = 0;
+		return;
+	}
+	queue_delayed_work(msm_chg.event_wq_thread,
+				&msm_chg.update_heartbeat_work,
+			      round_jiffies_relative(msecs_to_jiffies
+						     (msm_chg.update_time)));
+}
+
+/* set the charger state to READY before calling this */
+static void handle_charger_ready(struct msm_hardware_charger_priv *hw_chg_priv)
+{
+	struct msm_hardware_charger_priv *old_chg_priv = NULL;
+
+	debug_print(__func__, hw_chg_priv);
+
+	if (msm_chg.current_chg_priv != NULL
+	    && hw_chg_priv->hw_chg->rating >
+	    msm_chg.current_chg_priv->hw_chg->rating) {
+		/*
+		 * a better charger was found, ask the current charger
+		 * to stop charging if it was charging
+		 */
+		if (msm_chg.current_chg_priv->hw_chg_state ==
+		    CHG_CHARGING_STATE) {
+			if (msm_stop_charging(msm_chg.current_chg_priv)) {
+				dev_err(msm_chg.dev, "%s couldnt stop chg\n",
+					msm_chg.current_chg_priv->hw_chg->name);
+				return;
+			}
+			if (msm_charging_switched(msm_chg.current_chg_priv)) {
+				dev_err(msm_chg.dev, "%s couldnt stop chg\n",
+					msm_chg.current_chg_priv->hw_chg->name);
+				return;
+			}
+		}
+		msm_chg.current_chg_priv->hw_chg_state = CHG_READY_STATE;
+		old_chg_priv = msm_chg.current_chg_priv;
+		msm_chg.current_chg_priv = NULL;
+	}
+
+	if (msm_chg.current_chg_priv == NULL) {
+		msm_chg.current_chg_priv = hw_chg_priv;
+		dev_info(msm_chg.dev,
+			 "%s: best charger = %s\n", __func__,
+			 msm_chg.current_chg_priv->hw_chg->name);
+
+		msm_enable_system_current(msm_chg.current_chg_priv);
+		/*
+		 * since a better charger was chosen, ask the old
+		 * charger to stop providing system current
+		 */
+		if (old_chg_priv != NULL)
+			msm_disable_system_current(old_chg_priv);
+
+		if (!is_batt_status_capable_of_charging())
+			return;
+
+		/* start charging from the new charger */
+		if (!msm_start_charging()) {
+			/* if we simply switched chg continue with teoc timer
+			 * else we update the batt state and set the teoc
+			 * timer */
+			if (!is_batt_status_charging()) {
+				dev_info(msm_chg.dev,
+				       "%s: starting safety timer\n", __func__);
+				queue_delayed_work(msm_chg.event_wq_thread,
+							&msm_chg.teoc_work,
+						      round_jiffies_relative
+						      (msecs_to_jiffies
+						       (msm_chg.safety_time)));
+				msm_chg.batt_status = BATT_STATUS_TRKL_CHARGING;
+			}
+		} else {
+			/* we couldnt start charging from the new readied
+			 * charger */
+			if (is_batt_status_charging())
+				msm_chg.batt_status = BATT_STATUS_DISCHARGING;
+		}
+	}
+}
+
+static void handle_charger_removed(struct msm_hardware_charger_priv
+				   *hw_chg_removed, int new_state)
+{
+	struct msm_hardware_charger_priv *hw_chg_priv;
+
+	debug_print(__func__, hw_chg_removed);
+
+	if (msm_chg.current_chg_priv == hw_chg_removed) {
+		msm_disable_system_current(hw_chg_removed);
+		if (msm_chg.current_chg_priv->hw_chg_state
+						== CHG_CHARGING_STATE) {
+			if (msm_stop_charging(hw_chg_removed)) {
+				dev_err(msm_chg.dev, "%s couldnt stop chg\n",
+					msm_chg.current_chg_priv->hw_chg->name);
+			}
+		}
+		msm_chg.current_chg_priv = NULL;
+	}
+
+	hw_chg_removed->hw_chg_state = new_state;
+
+	if (msm_chg.current_chg_priv == NULL) {
+		hw_chg_priv = find_best_charger();
+		if (hw_chg_priv == NULL) {
+			dev_info(msm_chg.dev, "%s: no chargers\n", __func__);
+			/* if the battery was Just finished charging
+			 * we keep that state as is so that we dont rush
+			 * in to charging the battery when a charger is
+			 * plugged in shortly. */
+			if (is_batt_status_charging())
+				msm_chg.batt_status = BATT_STATUS_DISCHARGING;
+		} else {
+			msm_chg.current_chg_priv = hw_chg_priv;
+			msm_enable_system_current(hw_chg_priv);
+			dev_info(msm_chg.dev,
+				 "%s: best charger = %s\n", __func__,
+				 msm_chg.current_chg_priv->hw_chg->name);
+
+			if (!is_batt_status_capable_of_charging())
+				return;
+
+			if (msm_start_charging()) {
+				/* we couldnt start charging for some reason */
+				msm_chg.batt_status = BATT_STATUS_DISCHARGING;
+			}
+		}
+	}
+
+	/* if we arent charging stop the safety timer */
+	if (!is_batt_status_charging()) {
+		dev_info(msm_chg.dev, "%s: stopping safety timer work\n",
+				__func__);
+		cancel_delayed_work(&msm_chg.teoc_work);
+	}
+}
+
+static void handle_event(struct msm_hardware_charger *hw_chg, int event)
+{
+	struct msm_hardware_charger_priv *priv = NULL;
+
+	/*
+	 * if hw_chg is NULL then this event comes from non-charger
+	 * parties like battery gauge
+	 */
+	if (hw_chg)
+		priv = hw_chg->charger_private;
+
+	mutex_lock(&msm_chg.status_lock);
+
+	switch (event) {
+	case CHG_INSERTED_EVENT:
+		if (priv->hw_chg_state != CHG_ABSENT_STATE) {
+			dev_info(msm_chg.dev,
+				 "%s insertion detected when cbl present",
+				 hw_chg->name);
+			break;
+		}
+		update_batt_status();
+		if (hw_chg->type == CHG_TYPE_USB) {
+			priv->hw_chg_state = CHG_PRESENT_STATE;
+			notify_usb_of_the_plugin_event(priv, 1);
+			if (usb_chg_current) {
+				priv->max_source_current = usb_chg_current;
+				usb_chg_current = 0;
+				/* usb has already indicated us to charge */
+				priv->hw_chg_state = CHG_READY_STATE;
+				handle_charger_ready(priv);
+			}
+		} else {
+			priv->hw_chg_state = CHG_READY_STATE;
+			handle_charger_ready(priv);
+		}
+		break;
+	case CHG_ENUMERATED_EVENT:	/* only in USB types */
+		if (priv->hw_chg_state == CHG_ABSENT_STATE) {
+			dev_info(msm_chg.dev, "%s enum withuot presence\n",
+				 hw_chg->name);
+			break;
+		}
+		update_batt_status();
+		dev_dbg(msm_chg.dev, "%s enum with %dmA to draw\n",
+			 hw_chg->name, priv->max_source_current);
+		if (priv->max_source_current == 0) {
+			/* usb subsystem doesnt want us to draw
+			 * charging current */
+			/* act as if the charge is removed */
+			if (priv->hw_chg_state != CHG_PRESENT_STATE)
+				handle_charger_removed(priv, CHG_PRESENT_STATE);
+		} else {
+			if (priv->hw_chg_state != CHG_READY_STATE) {
+				priv->hw_chg_state = CHG_READY_STATE;
+				handle_charger_ready(priv);
+			}
+		}
+		break;
+	case CHG_REMOVED_EVENT:
+		if (priv->hw_chg_state == CHG_ABSENT_STATE) {
+			dev_info(msm_chg.dev, "%s cable already removed\n",
+				 hw_chg->name);
+			break;
+		}
+		update_batt_status();
+		if (hw_chg->type == CHG_TYPE_USB) {
+			usb_chg_current = 0;
+			notify_usb_of_the_plugin_event(priv, 0);
+		}
+		handle_charger_removed(priv, CHG_ABSENT_STATE);
+		break;
+	case CHG_DONE_EVENT:
+		if (priv->hw_chg_state == CHG_CHARGING_STATE)
+			handle_charging_done(priv);
+		break;
+	case CHG_BATT_BEGIN_FAST_CHARGING:
+		/* only update if we are TRKL charging */
+		if (msm_chg.batt_status == BATT_STATUS_TRKL_CHARGING)
+			msm_chg.batt_status = BATT_STATUS_FAST_CHARGING;
+		break;
+	case CHG_BATT_NEEDS_RECHARGING:
+		msm_chg.batt_status = BATT_STATUS_DISCHARGING;
+		handle_battery_inserted();
+		priv = msm_chg.current_chg_priv;
+		break;
+	case CHG_BATT_TEMP_OUTOFRANGE:
+		/* the batt_temp out of range can trigger
+		 * when the battery is absent */
+		if (!is_battery_present()
+		    && msm_chg.batt_status != BATT_STATUS_ABSENT) {
+			msm_chg.batt_status = BATT_STATUS_ABSENT;
+			handle_battery_removed();
+			break;
+		}
+		if (msm_chg.batt_status == BATT_STATUS_TEMPERATURE_OUT_OF_RANGE)
+			break;
+		msm_chg.batt_status = BATT_STATUS_TEMPERATURE_OUT_OF_RANGE;
+		handle_battery_removed();
+		break;
+	case CHG_BATT_TEMP_INRANGE:
+		if (msm_chg.batt_status != BATT_STATUS_TEMPERATURE_OUT_OF_RANGE)
+			break;
+		msm_chg.batt_status = BATT_STATUS_ID_INVALID;
+		/* check id */
+		if (!is_battery_id_valid())
+			break;
+		/* assume that we are discharging from the battery
+		 * and act as if the battery was inserted
+		 * if a charger is present charging will be resumed */
+		msm_chg.batt_status = BATT_STATUS_DISCHARGING;
+		handle_battery_inserted();
+		break;
+	case CHG_BATT_INSERTED:
+		if (msm_chg.batt_status != BATT_STATUS_ABSENT)
+			break;
+		/* debounce */
+		if (!is_battery_present())
+			break;
+		msm_chg.batt_status = BATT_STATUS_ID_INVALID;
+		if (!is_battery_id_valid())
+			break;
+		/* assume that we are discharging from the battery */
+		msm_chg.batt_status = BATT_STATUS_DISCHARGING;
+		/* check if a charger is present */
+		handle_battery_inserted();
+		break;
+	case CHG_BATT_REMOVED:
+		if (msm_chg.batt_status == BATT_STATUS_ABSENT)
+			break;
+		/* debounce */
+		if (is_battery_present())
+			break;
+		msm_chg.batt_status = BATT_STATUS_ABSENT;
+		handle_battery_removed();
+		break;
+	case CHG_BATT_STATUS_CHANGE:
+		/* TODO  battery SOC like battery-alarm/charging-full features
+		can be added here for future improvement */
+		break;
+	}
+	dev_dbg(msm_chg.dev, "%s %d done batt_status=%d\n", __func__,
+		event, msm_chg.batt_status);
+
+	/* update userspace */
+	if (msm_batt_gauge)
+		power_supply_changed(&msm_psy_batt);
+	if (priv)
+		power_supply_changed(&priv->psy);
+
+	mutex_unlock(&msm_chg.status_lock);
+}
+
+static int msm_chg_dequeue_event(struct msm_charger_event **event)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&msm_chg.queue_lock, flags);
+	if (msm_chg.queue_count == 0) {
+		spin_unlock_irqrestore(&msm_chg.queue_lock, flags);
+		return -EINVAL;
+	}
+	*event = &msm_chg.queue[msm_chg.head];
+	msm_chg.head = (msm_chg.head + 1) % MSM_CHG_MAX_EVENTS;
+	pr_debug("%s dequeueing %d\n", __func__, (*event)->event);
+	msm_chg.queue_count--;
+	spin_unlock_irqrestore(&msm_chg.queue_lock, flags);
+	return 0;
+}
+
+static int msm_chg_enqueue_event(struct msm_hardware_charger *hw_chg,
+			enum msm_hardware_charger_event event)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&msm_chg.queue_lock, flags);
+	if (msm_chg.queue_count == MSM_CHG_MAX_EVENTS) {
+		spin_unlock_irqrestore(&msm_chg.queue_lock, flags);
+		pr_err("%s: queue full cannot enqueue %d\n",
+				__func__, event);
+		return -EAGAIN;
+	}
+	pr_debug("%s queueing %d\n", __func__, event);
+	msm_chg.queue[msm_chg.tail].event = event;
+	msm_chg.queue[msm_chg.tail].hw_chg = hw_chg;
+	msm_chg.tail = (msm_chg.tail + 1)%MSM_CHG_MAX_EVENTS;
+	msm_chg.queue_count++;
+	spin_unlock_irqrestore(&msm_chg.queue_lock, flags);
+	return 0;
+}
+
+static void process_events(struct work_struct *work)
+{
+	struct msm_charger_event *event;
+	int rc;
+
+	do {
+		rc = msm_chg_dequeue_event(&event);
+		if (!rc)
+			handle_event(event->hw_chg, event->event);
+	} while (!rc);
+}
+
+/* USB calls these to tell us how much charging current we should draw */
+void msm_charger_vbus_draw(unsigned int mA)
+{
+	if (usb_hw_chg_priv) {
+		usb_hw_chg_priv->max_source_current = mA;
+		msm_charger_notify_event(usb_hw_chg_priv->hw_chg,
+						CHG_ENUMERATED_EVENT);
+	} else
+		/* remember the current, to be used when charger is ready */
+		usb_chg_current = mA;
+}
+
+static int determine_initial_batt_status(void)
+{
+	if (is_battery_present())
+		if (is_battery_id_valid())
+			if (is_battery_temp_within_range())
+				msm_chg.batt_status = BATT_STATUS_DISCHARGING;
+			else
+				msm_chg.batt_status
+				    = BATT_STATUS_TEMPERATURE_OUT_OF_RANGE;
+		else
+			msm_chg.batt_status = BATT_STATUS_ID_INVALID;
+	else
+		msm_chg.batt_status = BATT_STATUS_ABSENT;
+
+	if (is_batt_status_capable_of_charging())
+		handle_battery_inserted();
+
+	/* start updaing the battery powersupply every msm_chg.update_time
+	 * milliseconds */
+	queue_delayed_work(msm_chg.event_wq_thread,
+				&msm_chg.update_heartbeat_work,
+			      round_jiffies_relative(msecs_to_jiffies
+						     (msm_chg.update_time)));
+
+	pr_debug("%s:OK batt_status=%d\n", __func__, msm_chg.batt_status);
+	return 0;
+}
+
+static int __devinit msm_charger_probe(struct platform_device *pdev)
+{
+	msm_chg.dev = &pdev->dev;
+	if (pdev->dev.platform_data) {
+		unsigned int milli_secs;
+
+		struct msm_charger_platform_data *pdata
+		    =
+		    (struct msm_charger_platform_data *)pdev->dev.platform_data;
+
+		milli_secs = pdata->safety_time * 60 * MSEC_PER_SEC;
+		if (milli_secs > jiffies_to_msecs(MAX_JIFFY_OFFSET)) {
+			dev_warn(&pdev->dev, "%s: safety time too large"
+				 "%dms\n", __func__, milli_secs);
+			milli_secs = jiffies_to_msecs(MAX_JIFFY_OFFSET);
+		}
+		msm_chg.safety_time = milli_secs;
+
+		milli_secs = pdata->update_time * 60 * MSEC_PER_SEC;
+		if (milli_secs > jiffies_to_msecs(MAX_JIFFY_OFFSET)) {
+			dev_warn(&pdev->dev, "%s: safety time too large"
+				 "%dms\n", __func__, milli_secs);
+			milli_secs = jiffies_to_msecs(MAX_JIFFY_OFFSET);
+		}
+		msm_chg.update_time = milli_secs;
+
+		msm_chg.max_voltage = pdata->max_voltage;
+		msm_chg.min_voltage = pdata->min_voltage;
+		msm_chg.get_batt_capacity_percent =
+		    pdata->get_batt_capacity_percent;
+	}
+	if (msm_chg.safety_time == 0)
+		msm_chg.safety_time = CHARGING_TEOC_MS;
+	if (msm_chg.update_time == 0)
+		msm_chg.update_time = UPDATE_TIME_MS;
+	if (msm_chg.max_voltage == 0)
+		msm_chg.max_voltage = DEFAULT_BATT_MAX_V;
+	if (msm_chg.min_voltage == 0)
+		msm_chg.min_voltage = DEFAULT_BATT_MIN_V;
+	if (msm_chg.get_batt_capacity_percent == NULL)
+		msm_chg.get_batt_capacity_percent =
+		    msm_chg_get_batt_capacity_percent;
+
+	mutex_init(&msm_chg.status_lock);
+	INIT_DELAYED_WORK(&msm_chg.teoc_work, teoc);
+	INIT_DELAYED_WORK(&msm_chg.update_heartbeat_work, update_heartbeat);
+
+	wake_lock_init(&msm_chg.wl, WAKE_LOCK_SUSPEND, "msm_charger");
+	return 0;
+}
+
+static int __devexit msm_charger_remove(struct platform_device *pdev)
+{
+	wake_lock_destroy(&msm_chg.wl);
+	mutex_destroy(&msm_chg.status_lock);
+	power_supply_unregister(&msm_psy_batt);
+	return 0;
+}
+
+int msm_charger_notify_event(struct msm_hardware_charger *hw_chg,
+			     enum msm_hardware_charger_event event)
+{
+	msm_chg_enqueue_event(hw_chg, event);
+	queue_work(msm_chg.event_wq_thread, &msm_chg.queue_work);
+	return 0;
+}
+EXPORT_SYMBOL(msm_charger_notify_event);
+
+int msm_charger_register(struct msm_hardware_charger *hw_chg)
+{
+	struct msm_hardware_charger_priv *priv;
+	int rc = 0;
+
+	if (!msm_chg.inited) {
+		pr_err("%s: msm_chg is NULL,Too early to register\n", __func__);
+		return -EAGAIN;
+	}
+
+	if (hw_chg->start_charging == NULL
+		|| hw_chg->stop_charging == NULL
+		|| hw_chg->name == NULL
+		|| hw_chg->rating == 0) {
+		pr_err("%s: invalid hw_chg\n", __func__);
+		return -EINVAL;
+	}
+
+	priv = kzalloc(sizeof *priv, GFP_KERNEL);
+	if (priv == NULL) {
+		dev_err(msm_chg.dev, "%s kzalloc failed\n", __func__);
+		return -ENOMEM;
+	}
+
+	priv->psy.name = hw_chg->name;
+	if (hw_chg->type == CHG_TYPE_USB)
+		priv->psy.type = POWER_SUPPLY_TYPE_USB;
+	else
+		priv->psy.type = POWER_SUPPLY_TYPE_MAINS;
+
+	priv->psy.supplied_to = msm_power_supplied_to;
+	priv->psy.num_supplicants = ARRAY_SIZE(msm_power_supplied_to);
+	priv->psy.properties = msm_power_props;
+	priv->psy.num_properties = ARRAY_SIZE(msm_power_props);
+	priv->psy.get_property = msm_power_get_property;
+
+	rc = power_supply_register(NULL, &priv->psy);
+	if (rc) {
+		dev_err(msm_chg.dev, "%s power_supply_register failed\n",
+			__func__);
+		goto out;
+	}
+
+	priv->hw_chg = hw_chg;
+	priv->hw_chg_state = CHG_ABSENT_STATE;
+	INIT_LIST_HEAD(&priv->list);
+	mutex_lock(&msm_chg.msm_hardware_chargers_lock);
+	list_add_tail(&priv->list, &msm_chg.msm_hardware_chargers);
+	mutex_unlock(&msm_chg.msm_hardware_chargers_lock);
+	hw_chg->charger_private = (void *)priv;
+	return 0;
+
+out:
+	kfree(priv);
+	return rc;
+}
+EXPORT_SYMBOL(msm_charger_register);
+
+void msm_battery_gauge_register(struct msm_battery_gauge *batt_gauge)
+{
+	int rc;
+
+	if (msm_batt_gauge) {
+		msm_batt_gauge = batt_gauge;
+		pr_err("msm-charger %s multiple battery gauge called\n",
+								__func__);
+	} else {
+		rc = power_supply_register(msm_chg.dev, &msm_psy_batt);
+		if (rc < 0) {
+			dev_err(msm_chg.dev, "%s: power_supply_register failed"
+					" rc=%d\n", __func__, rc);
+			return;
+		}
+
+		msm_batt_gauge = batt_gauge;
+		determine_initial_batt_status();
+	}
+}
+EXPORT_SYMBOL(msm_battery_gauge_register);
+
+void msm_battery_gauge_unregister(struct msm_battery_gauge *batt_gauge)
+{
+	msm_batt_gauge = NULL;
+}
+EXPORT_SYMBOL(msm_battery_gauge_unregister);
+
+int msm_charger_unregister(struct msm_hardware_charger *hw_chg)
+{
+	struct msm_hardware_charger_priv *priv;
+
+	priv = (struct msm_hardware_charger_priv *)(hw_chg->charger_private);
+	mutex_lock(&msm_chg.msm_hardware_chargers_lock);
+	list_del(&priv->list);
+	mutex_unlock(&msm_chg.msm_hardware_chargers_lock);
+	power_supply_unregister(&priv->psy);
+	kfree(priv);
+	return 0;
+}
+EXPORT_SYMBOL(msm_charger_unregister);
+
+static int msm_charger_suspend(struct device *dev)
+{
+	dev_dbg(msm_chg.dev, "%s suspended\n", __func__);
+	msm_chg.stop_update = 1;
+	cancel_delayed_work(&msm_chg.update_heartbeat_work);
+	mutex_lock(&msm_chg.status_lock);
+	handle_battery_removed();
+	mutex_unlock(&msm_chg.status_lock);
+	return 0;
+}
+
+static int msm_charger_resume(struct device *dev)
+{
+	dev_dbg(msm_chg.dev, "%s resumed\n", __func__);
+	msm_chg.stop_update = 0;
+	/* start updaing the battery powersupply every msm_chg.update_time
+	 * milliseconds */
+	queue_delayed_work(msm_chg.event_wq_thread,
+				&msm_chg.update_heartbeat_work,
+			      round_jiffies_relative(msecs_to_jiffies
+						     (msm_chg.update_time)));
+	mutex_lock(&msm_chg.status_lock);
+	handle_battery_inserted();
+	mutex_unlock(&msm_chg.status_lock);
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(msm_charger_pm_ops,
+		msm_charger_suspend, msm_charger_resume);
+
+static struct platform_driver msm_charger_driver = {
+	.probe = msm_charger_probe,
+	.remove = __devexit_p(msm_charger_remove),
+	.driver = {
+		   .name = "msm-charger",
+		   .owner = THIS_MODULE,
+		   .pm = &msm_charger_pm_ops,
+	},
+};
+
+static int __init msm_charger_init(void)
+{
+	int rc;
+
+	INIT_LIST_HEAD(&msm_chg.msm_hardware_chargers);
+	msm_chg.count_chargers = 0;
+	mutex_init(&msm_chg.msm_hardware_chargers_lock);
+
+	msm_chg.queue = kzalloc(sizeof(struct msm_charger_event)
+				* MSM_CHG_MAX_EVENTS,
+				GFP_KERNEL);
+	if (!msm_chg.queue) {
+		rc = -ENOMEM;
+		goto out;
+	}
+	msm_chg.tail = 0;
+	msm_chg.head = 0;
+	spin_lock_init(&msm_chg.queue_lock);
+	msm_chg.queue_count = 0;
+	INIT_WORK(&msm_chg.queue_work, process_events);
+	msm_chg.event_wq_thread = create_workqueue("msm_charger_eventd");
+	if (!msm_chg.event_wq_thread) {
+		rc = -ENOMEM;
+		goto free_queue;
+	}
+	rc = platform_driver_register(&msm_charger_driver);
+	if (rc < 0) {
+		pr_err("%s: FAIL: platform_driver_register. rc = %d\n",
+		       __func__, rc);
+		goto destroy_wq_thread;
+	}
+	msm_chg.inited = 1;
+	return 0;
+
+destroy_wq_thread:
+	destroy_workqueue(msm_chg.event_wq_thread);
+free_queue:
+	kfree(msm_chg.queue);
+out:
+	return rc;
+}
+
+static void __exit msm_charger_exit(void)
+{
+	flush_workqueue(msm_chg.event_wq_thread);
+	destroy_workqueue(msm_chg.event_wq_thread);
+	kfree(msm_chg.queue);
+	platform_driver_unregister(&msm_charger_driver);
+}
+
+module_init(msm_charger_init);
+module_exit(msm_charger_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Abhijeet Dharmapurikar <adharmap@codeaurora.org>");
+MODULE_DESCRIPTION("Battery driver for Qualcomm MSM chipsets.");
+MODULE_VERSION("1.0");
diff --git a/drivers/power/pm8058_usb_fix.c b/drivers/power/pm8058_usb_fix.c
new file mode 100644
index 0000000..80b1f87
--- /dev/null
+++ b/drivers/power/pm8058_usb_fix.c
@@ -0,0 +1,357 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/mfd/pmic8058.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/workqueue.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+
+#include <mach/msm_xo.h>
+#include <mach/msm_hsusb.h>
+
+/* Config Regs  and their bits*/
+#define PM8058_CHG_TEST			0x75
+#define IGNORE_LL                       2
+
+#define PM8058_CHG_TEST_2		0xEA
+#define PM8058_CHG_TEST_3		0xEB
+#define PM8058_OVP_TEST_REG		0xF6
+#define FORCE_OVP_OFF			3
+
+#define PM8058_CHG_CNTRL		0x1E
+#define CHG_TRICKLE_EN			7
+#define CHG_USB_SUSPEND			6
+#define CHG_IMON_CAL			5
+#define CHG_IMON_GAIN			4
+#define CHG_VBUS_FROM_BOOST_OVRD	2
+#define CHG_CHARGE_DIS			1
+#define CHG_VCP_EN			0
+
+#define PM8058_CHG_CNTRL_2		0xD8
+#define ATC_DIS				7	/* coincell backed */
+#define CHARGE_AUTO_DIS			6
+#define DUMB_CHG_OVRD			5	/* coincell backed */
+#define ENUM_DONE			4
+#define CHG_TEMP_MODE			3
+#define CHG_BATT_TEMP_DIS		1	/* coincell backed */
+#define CHG_FAILED_CLEAR		0
+
+#define PM8058_CHG_VMAX_SEL		0x21
+#define PM8058_CHG_VBAT_DET		0xD9
+#define PM8058_CHG_IMAX			0x1F
+#define PM8058_CHG_TRICKLE		0xDB
+#define PM8058_CHG_ITERM		0xDC
+#define PM8058_CHG_TTRKL_MAX		0xE1
+#define PM8058_CHG_TCHG_MAX		0xE4
+#define PM8058_CHG_TEMP_THRESH		0xE2
+#define PM8058_CHG_TEMP_REG		0xE3
+#define PM8058_CHG_PULSE		0x22
+
+/* IRQ STATUS and CLEAR */
+#define PM8058_CHG_STATUS_CLEAR_IRQ_1	0x31
+#define PM8058_CHG_STATUS_CLEAR_IRQ_3	0x33
+#define PM8058_CHG_STATUS_CLEAR_IRQ_10	0xB3
+#define PM8058_CHG_STATUS_CLEAR_IRQ_11	0xB4
+
+/* IRQ MASKS */
+#define PM8058_CHG_MASK_IRQ_1		0x38
+
+#define PM8058_CHG_MASK_IRQ_3		0x3A
+#define PM8058_CHG_MASK_IRQ_10		0xBA
+#define PM8058_CHG_MASK_IRQ_11		0xBB
+
+/* IRQ Real time status regs */
+#define PM8058_CHG_STATUS_RT_1		0x3F
+#define STATUS_RTCHGVAL			7
+#define STATUS_RTCHGINVAL		6
+#define STATUS_RTBATT_REPLACE		5
+#define STATUS_RTVBATDET_LOW		4
+#define STATUS_RTCHGILIM		3
+#define STATUS_RTPCTDONE		1
+#define STATUS_RTVCP			0
+#define PM8058_CHG_STATUS_RT_3		0x41
+#define PM8058_CHG_STATUS_RT_10		0xC1
+#define PM8058_CHG_STATUS_RT_11		0xC2
+
+/* VTRIM */
+#define PM8058_CHG_VTRIM		0x1D
+#define PM8058_CHG_VBATDET_TRIM		0x1E
+#define PM8058_CHG_ITRIM		0x1F
+#define PM8058_CHG_TTRIM		0x20
+
+#define AUTO_CHARGING_VMAXSEL				4200
+#define AUTO_CHARGING_FAST_TIME_MAX_MINUTES		512
+#define AUTO_CHARGING_TRICKLE_TIME_MINUTES		30
+#define AUTO_CHARGING_VEOC_ITERM			100
+#define AUTO_CHARGING_IEOC_ITERM			160
+
+#define AUTO_CHARGING_VBATDET				4150
+#define AUTO_CHARGING_VEOC_VBATDET			4100
+#define AUTO_CHARGING_VEOC_TCHG				16
+#define AUTO_CHARGING_VEOC_TCHG_FINAL_CYCLE		32
+#define AUTO_CHARGING_VEOC_BEGIN_TIME_MS		5400000
+
+#define AUTO_CHARGING_VEOC_VBAT_LOW_CHECK_TIME_MS	60000
+#define AUTO_CHARGING_RESUME_CHARGE_DETECTION_COUNTER	5
+
+#define PM8058_CHG_I_STEP_MA 50
+#define PM8058_CHG_I_MIN_MA 50
+#define PM8058_CHG_T_TCHG_SHIFT 2
+#define PM8058_CHG_I_TERM_STEP_MA 10
+#define PM8058_CHG_V_STEP_MV 25
+#define PM8058_CHG_V_MIN_MV  2400
+/*
+ * enum pmic_chg_interrupts: pmic interrupts
+ * @CHGVAL_IRQ: charger V between 3.3 and 7.9
+ * @CHGINVAL_IRQ: charger V outside 3.3 and 7.9
+ * @VBATDET_LOW_IRQ: VBAT < VBATDET
+ * @VCP_IRQ: VDD went below VBAT: BAT_FET is turned on
+ * @CHGILIM_IRQ: mA consumed>IMAXSEL: chgloop draws less mA
+ * @ATC_DONE_IRQ: Auto Trickle done
+ * @ATCFAIL_IRQ: Auto Trickle fail
+ * @AUTO_CHGDONE_IRQ: Auto chg done
+ * @AUTO_CHGFAIL_IRQ: time exceeded w/o reaching term current
+ * @CHGSTATE_IRQ: something happend causing a state change
+ * @FASTCHG_IRQ: trkl charging completed: moving to fastchg
+ * @CHG_END_IRQ: mA has dropped to termination current
+ * @BATTTEMP_IRQ: batt temp is out of range
+ * @CHGHOT_IRQ: the pass device is too hot
+ * @CHGTLIMIT_IRQ: unused
+ * @CHG_GONE_IRQ: charger was removed
+ * @VCPMAJOR_IRQ: vcp major
+ * @VBATDET_IRQ: VBAT >= VBATDET
+ * @BATFET_IRQ: BATFET closed
+ * @BATT_REPLACE_IRQ:
+ * @BATTCONNECT_IRQ:
+ */
+enum pmic_chg_interrupts {
+	CHGVAL_IRQ,
+	CHGINVAL_IRQ,
+	VBATDET_LOW_IRQ,
+	VCP_IRQ,
+	CHGILIM_IRQ,
+	ATC_DONE_IRQ,
+	ATCFAIL_IRQ,
+	AUTO_CHGDONE_IRQ,
+	AUTO_CHGFAIL_IRQ,
+	CHGSTATE_IRQ,
+	FASTCHG_IRQ,
+	CHG_END_IRQ,
+	BATTTEMP_IRQ,
+	CHGHOT_IRQ,
+	CHGTLIMIT_IRQ,
+	CHG_GONE_IRQ,
+	VCPMAJOR_IRQ,
+	VBATDET_IRQ,
+	BATFET_IRQ,
+	BATT_REPLACE_IRQ,
+	BATTCONNECT_IRQ,
+	PMIC_CHG_MAX_INTS
+};
+
+struct pm8058_charger {
+	struct pmic_charger_pdata *pdata;
+	struct pm8058_chip *pm_chip;
+	struct device *dev;
+
+	int pmic_chg_irq[PMIC_CHG_MAX_INTS];
+	DECLARE_BITMAP(enabled_irqs, PMIC_CHG_MAX_INTS);
+
+	struct delayed_work check_vbat_low_work;
+	struct delayed_work veoc_begin_work;
+	int waiting_for_topoff;
+	int waiting_for_veoc;
+	int current_charger_current;
+
+	struct msm_xo_voter *voter;
+	struct dentry *dent;
+};
+
+static struct pm8058_charger pm8058_chg;
+
+static int pm_chg_get_rt_status(int irq)
+{
+	int count = 3;
+	int ret;
+
+	while ((ret =
+		pm8058_irq_get_rt_status(pm8058_chg.pm_chip, irq)) == -EAGAIN
+	       && count--) {
+		dev_info(pm8058_chg.dev, "%s trycount=%d\n", __func__, count);
+		cpu_relax();
+	}
+	if (ret == -EAGAIN)
+		return 0;
+	else
+		return ret;
+}
+
+static int is_chg_plugged_in(void)
+{
+	return pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[CHGVAL_IRQ]);
+}
+
+static irqreturn_t pm8058_chg_chgval_handler(int irq, void *dev_id)
+{
+	u8 old, temp;
+	int ret;
+
+	if (!is_chg_plugged_in()) {	/*this debounces it */
+		ret = pm8058_read(pm8058_chg.pm_chip, PM8058_OVP_TEST_REG,
+					&old, 1);
+		temp = old | BIT(FORCE_OVP_OFF);
+		ret = pm8058_write(pm8058_chg.pm_chip, PM8058_OVP_TEST_REG,
+					&temp, 1);
+		temp = 0xFC;
+		ret = pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_TEST,
+					&temp, 1);
+		pr_debug("%s forced wrote 0xFC to test ret=%d\n",
+							__func__, ret);
+		/* 20 ms sleep is for the VCHG to discharge */
+		msleep(20);
+		temp = 0xF0;
+		ret = pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_TEST,
+					&temp, 1);
+		ret = pm8058_write(pm8058_chg.pm_chip, PM8058_OVP_TEST_REG,
+					&old, 1);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void free_irqs(void)
+{
+	int i;
+
+	for (i = 0; i < PMIC_CHG_MAX_INTS; i++)
+		if (pm8058_chg.pmic_chg_irq[i]) {
+			free_irq(pm8058_chg.pmic_chg_irq[i], NULL);
+			pm8058_chg.pmic_chg_irq[i] = 0;
+		}
+}
+
+static int __devinit request_irqs(struct platform_device *pdev)
+{
+	struct resource *res;
+	int ret;
+
+	ret = 0;
+	bitmap_fill(pm8058_chg.enabled_irqs, PMIC_CHG_MAX_INTS);
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "CHGVAL");
+	if (res == NULL) {
+		dev_err(pm8058_chg.dev,
+			"%s:couldnt find resource CHGVAL\n", __func__);
+		goto err_out;
+	} else {
+		ret = request_any_context_irq(res->start,
+				  pm8058_chg_chgval_handler,
+				  IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+				  res->name, NULL);
+		if (ret < 0) {
+			dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
+				__func__, res->start, ret);
+			goto err_out;
+		} else {
+			pm8058_chg.pmic_chg_irq[CHGVAL_IRQ] = res->start;
+		}
+	}
+
+	return 0;
+
+err_out:
+	free_irqs();
+	return -EINVAL;
+}
+
+static int pm8058_usb_voltage_lower_limit(void)
+{
+	u8 temp, old;
+	int ret = 0;
+
+	temp = 0x10;
+	ret |= pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_TEST, &temp, 1);
+	ret |= pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_TEST, &old, 1);
+	old = old & ~BIT(IGNORE_LL);
+	temp = 0x90  | (0xF & old);
+	pr_debug("%s writing 0x%x to test\n", __func__, temp);
+	ret |= pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_TEST, &temp, 1);
+
+	return ret;
+}
+
+static int __devinit pm8058_charger_probe(struct platform_device *pdev)
+{
+	struct pm8058_chip *pm_chip;
+
+	pm_chip = dev_get_drvdata(pdev->dev.parent);
+	if (pm_chip == NULL) {
+		pr_err("%s:no parent data passed in.\n", __func__);
+		return -EFAULT;
+	}
+
+	pm8058_chg.pm_chip = pm_chip;
+	pm8058_chg.pdata = pdev->dev.platform_data;
+	pm8058_chg.dev = &pdev->dev;
+
+	if (request_irqs(pdev)) {
+		pr_err("%s: couldnt register interrupts\n", __func__);
+		return -EINVAL;
+	}
+
+	if (pm8058_usb_voltage_lower_limit()) {
+		pr_err("%s: couldnt write to IGNORE_LL\n", __func__);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int __devexit pm8058_charger_remove(struct platform_device *pdev)
+{
+	free_irqs();
+	return 0;
+}
+
+static struct platform_driver pm8058_charger_driver = {
+	.probe = pm8058_charger_probe,
+	.remove = __devexit_p(pm8058_charger_remove),
+	.driver = {
+		   .name = "pm-usb-fix",
+		   .owner = THIS_MODULE,
+	},
+};
+
+static int __init pm8058_charger_init(void)
+{
+	return platform_driver_register(&pm8058_charger_driver);
+}
+
+static void __exit pm8058_charger_exit(void)
+{
+	platform_driver_unregister(&pm8058_charger_driver);
+}
+
+late_initcall(pm8058_charger_init);
+module_exit(pm8058_charger_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC8058 BATTERY driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pm8058_charger");
diff --git a/drivers/power/pm8921-bms.c b/drivers/power/pm8921-bms.c
new file mode 100644
index 0000000..b0439bc
--- /dev/null
+++ b/drivers/power/pm8921-bms.c
@@ -0,0 +1,2720 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#define pr_fmt(fmt)	"%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/power_supply.h>
+#include <linux/mfd/pm8xxx/pm8921-bms.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/pm8xxx-adc.h>
+#include <linux/mfd/pm8xxx/ccadc.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+
+#define BMS_CONTROL		0x224
+#define BMS_S1_DELAY		0x225
+#define BMS_OUTPUT0		0x230
+#define BMS_OUTPUT1		0x231
+#define BMS_TOLERANCES		0x232
+#define BMS_TEST1		0x237
+
+#define ADC_ARB_SECP_CNTRL	0x190
+#define ADC_ARB_SECP_AMUX_CNTRL	0x191
+#define ADC_ARB_SECP_ANA_PARAM	0x192
+#define ADC_ARB_SECP_DIG_PARAM	0x193
+#define ADC_ARB_SECP_RSV	0x194
+#define ADC_ARB_SECP_DATA1	0x195
+#define ADC_ARB_SECP_DATA0	0x196
+
+#define ADC_ARB_BMS_CNTRL	0x18D
+#define AMUX_TRIM_2		0x322
+#define TEST_PROGRAM_REV	0x339
+
+enum pmic_bms_interrupts {
+	PM8921_BMS_SBI_WRITE_OK,
+	PM8921_BMS_CC_THR,
+	PM8921_BMS_VSENSE_THR,
+	PM8921_BMS_VSENSE_FOR_R,
+	PM8921_BMS_OCV_FOR_R,
+	PM8921_BMS_GOOD_OCV,
+	PM8921_BMS_VSENSE_AVG,
+	PM_BMS_MAX_INTS,
+};
+
+struct pm8921_soc_params {
+	uint16_t	last_good_ocv_raw;
+	int		cc;
+
+	int		last_good_ocv_uv;
+};
+
+struct pm8921_rbatt_params {
+	uint16_t	ocv_for_rbatt_raw;
+	uint16_t	vsense_for_rbatt_raw;
+	uint16_t	vbatt_for_rbatt_raw;
+
+	int		ocv_for_rbatt_uv;
+	int		vsense_for_rbatt_uv;
+	int		vbatt_for_rbatt_uv;
+};
+
+/**
+ * struct pm8921_bms_chip -
+ * @bms_output_lock:	lock to prevent concurrent bms reads
+ *
+ * @last_ocv_uv_mutex:	mutex to protect simultaneous invocations of calculate
+ *			state of charge, note that last_ocv_uv could be
+ *			changed as soc is adjusted. This mutex protects
+ *			simultaneous updates of last_ocv_uv as well. This mutex
+ *			also protects changes to *_at_100 variables used in
+ *			faking 100% SOC.
+ */
+struct pm8921_bms_chip {
+	struct device		*dev;
+	struct dentry		*dent;
+	unsigned int		r_sense;
+	unsigned int		i_test;
+	unsigned int		v_failure;
+	unsigned int		fcc;
+	struct single_row_lut	*fcc_temp_lut;
+	struct single_row_lut	*fcc_sf_lut;
+	struct pc_temp_ocv_lut	*pc_temp_ocv_lut;
+	struct sf_lut		*pc_sf_lut;
+	struct sf_lut		*rbatt_sf_lut;
+	int			delta_rbatt_mohm;
+	struct work_struct	calib_hkadc_work;
+	struct delayed_work	calib_ccadc_work;
+	unsigned int		calib_delay_ms;
+	unsigned int		revision;
+	unsigned int		xoadc_v0625_usb_present;
+	unsigned int		xoadc_v0625_usb_absent;
+	unsigned int		xoadc_v0625;
+	unsigned int		xoadc_v125;
+	unsigned int		batt_temp_channel;
+	unsigned int		vbat_channel;
+	unsigned int		ref625mv_channel;
+	unsigned int		ref1p25v_channel;
+	unsigned int		batt_id_channel;
+	unsigned int		pmic_bms_irq[PM_BMS_MAX_INTS];
+	DECLARE_BITMAP(enabled_irqs, PM_BMS_MAX_INTS);
+	struct mutex		bms_output_lock;
+	struct single_row_lut	*adjusted_fcc_temp_lut;
+	unsigned int		charging_began;
+	unsigned int		start_percent;
+	unsigned int		end_percent;
+	enum battery_type	batt_type;
+	uint16_t		ocv_reading_at_100;
+	int			cc_reading_at_100;
+	int			max_voltage_uv;
+
+	int			batt_temp_suspend;
+	int			soc_rbatt_suspend;
+	int			default_rbatt_mohm;
+	int			amux_2_trim_delta;
+	uint16_t		prev_last_good_ocv_raw;
+	unsigned int		rconn_mohm;
+	struct mutex		last_ocv_uv_mutex;
+	int			last_ocv_uv;
+	int			last_cc_uah; /* used for Iavg calc for UUC */
+	struct timeval		t;
+	int			last_uuc_uah;
+	int			enable_fcc_learning;
+};
+
+static struct pm8921_bms_chip *the_chip;
+
+#define DEFAULT_RBATT_MOHMS		128
+#define DEFAULT_OCV_MICROVOLTS		3900000
+#define DEFAULT_CHARGE_CYCLES		0
+
+static int last_usb_cal_delta_uv = 1800;
+module_param(last_usb_cal_delta_uv, int, 0644);
+
+static int last_chargecycles = DEFAULT_CHARGE_CYCLES;
+static int last_charge_increase;
+module_param(last_chargecycles, int, 0644);
+module_param(last_charge_increase, int, 0644);
+
+static int last_rbatt = -EINVAL;
+static int last_soc = -EINVAL;
+static int last_real_fcc_mah = -EINVAL;
+static int last_real_fcc_batt_temp = -EINVAL;
+
+static int bms_ops_set(const char *val, const struct kernel_param *kp)
+{
+	if (*(int *)kp->arg == -EINVAL)
+		return param_set_int(val, kp);
+	else
+		return 0;
+}
+
+static struct kernel_param_ops bms_param_ops = {
+	.set = bms_ops_set,
+	.get = param_get_int,
+};
+
+module_param_cb(last_rbatt, &bms_param_ops, &last_rbatt, 0644);
+module_param_cb(last_soc, &bms_param_ops, &last_soc, 0644);
+
+/*
+ * bms_fake_battery is set in setups where a battery emulator is used instead
+ * of a real battery. This makes the bms driver report a different/fake value
+ * regardless of the calculated state of charge.
+ */
+static int bms_fake_battery = -EINVAL;
+module_param(bms_fake_battery, int, 0644);
+
+/* bms_start_XXX and bms_end_XXX are read only */
+static int bms_start_percent;
+static int bms_start_ocv_uv;
+static int bms_start_cc_uah;
+static int bms_end_percent;
+static int bms_end_ocv_uv;
+static int bms_end_cc_uah;
+
+static int bms_ro_ops_set(const char *val, const struct kernel_param *kp)
+{
+	return -EINVAL;
+}
+
+static struct kernel_param_ops bms_ro_param_ops = {
+	.set = bms_ro_ops_set,
+	.get = param_get_int,
+};
+module_param_cb(bms_start_percent, &bms_ro_param_ops, &bms_start_percent, 0644);
+module_param_cb(bms_start_ocv_uv, &bms_ro_param_ops, &bms_start_ocv_uv, 0644);
+module_param_cb(bms_start_cc_uah, &bms_ro_param_ops, &bms_start_cc_uah, 0644);
+
+module_param_cb(bms_end_percent, &bms_ro_param_ops, &bms_end_percent, 0644);
+module_param_cb(bms_end_ocv_uv, &bms_ro_param_ops, &bms_end_ocv_uv, 0644);
+module_param_cb(bms_end_cc_uah, &bms_ro_param_ops, &bms_end_cc_uah, 0644);
+
+static int interpolate_fcc(struct pm8921_bms_chip *chip, int batt_temp);
+static void readjust_fcc_table(void)
+{
+	struct single_row_lut *temp, *old;
+	int i, fcc, ratio;
+
+	if (!the_chip->fcc_temp_lut) {
+		pr_err("The static fcc lut table is NULL\n");
+		return;
+	}
+
+	temp = kzalloc(sizeof(struct single_row_lut), GFP_KERNEL);
+	if (!temp) {
+		pr_err("Cannot allocate memory for adjusted fcc table\n");
+		return;
+	}
+
+	fcc = interpolate_fcc(the_chip, last_real_fcc_batt_temp);
+
+	temp->cols = the_chip->fcc_temp_lut->cols;
+	for (i = 0; i < the_chip->fcc_temp_lut->cols; i++) {
+		temp->x[i] = the_chip->fcc_temp_lut->x[i];
+		ratio = div_u64(the_chip->fcc_temp_lut->y[i] * 1000, fcc);
+		temp->y[i] =  (ratio * last_real_fcc_mah);
+		temp->y[i] /= 1000;
+		pr_debug("temp=%d, staticfcc=%d, adjfcc=%d, ratio=%d\n",
+				temp->x[i], the_chip->fcc_temp_lut->y[i],
+				temp->y[i], ratio);
+	}
+
+	old = the_chip->adjusted_fcc_temp_lut;
+	the_chip->adjusted_fcc_temp_lut = temp;
+	kfree(old);
+}
+
+static int bms_last_real_fcc_set(const char *val,
+				const struct kernel_param *kp)
+{
+	int rc = 0;
+
+	if (last_real_fcc_mah == -EINVAL)
+		rc = param_set_int(val, kp);
+	if (rc) {
+		pr_err("Failed to set last_real_fcc_mah rc=%d\n", rc);
+		return rc;
+	}
+	if (last_real_fcc_batt_temp != -EINVAL)
+		readjust_fcc_table();
+	return rc;
+}
+static struct kernel_param_ops bms_last_real_fcc_param_ops = {
+	.set = bms_last_real_fcc_set,
+	.get = param_get_int,
+};
+module_param_cb(last_real_fcc_mah, &bms_last_real_fcc_param_ops,
+					&last_real_fcc_mah, 0644);
+
+static int bms_last_real_fcc_batt_temp_set(const char *val,
+				const struct kernel_param *kp)
+{
+	int rc = 0;
+
+	if (last_real_fcc_batt_temp == -EINVAL)
+		rc = param_set_int(val, kp);
+	if (rc) {
+		pr_err("Failed to set last_real_fcc_batt_temp rc=%d\n", rc);
+		return rc;
+	}
+	if (last_real_fcc_mah != -EINVAL)
+		readjust_fcc_table();
+	return rc;
+}
+
+static struct kernel_param_ops bms_last_real_fcc_batt_temp_param_ops = {
+	.set = bms_last_real_fcc_batt_temp_set,
+	.get = param_get_int,
+};
+module_param_cb(last_real_fcc_batt_temp, &bms_last_real_fcc_batt_temp_param_ops,
+					&last_real_fcc_batt_temp, 0644);
+
+static int pm_bms_get_rt_status(struct pm8921_bms_chip *chip, int irq_id)
+{
+	return pm8xxx_read_irq_stat(chip->dev->parent,
+					chip->pmic_bms_irq[irq_id]);
+}
+
+static void pm8921_bms_enable_irq(struct pm8921_bms_chip *chip, int interrupt)
+{
+	if (!__test_and_set_bit(interrupt, chip->enabled_irqs)) {
+		dev_dbg(chip->dev, "%s %d\n", __func__,
+						chip->pmic_bms_irq[interrupt]);
+		enable_irq(chip->pmic_bms_irq[interrupt]);
+	}
+}
+
+static void pm8921_bms_disable_irq(struct pm8921_bms_chip *chip, int interrupt)
+{
+	if (__test_and_clear_bit(interrupt, chip->enabled_irqs)) {
+		pr_debug("%d\n", chip->pmic_bms_irq[interrupt]);
+		disable_irq_nosync(chip->pmic_bms_irq[interrupt]);
+	}
+}
+
+static int pm_bms_masked_write(struct pm8921_bms_chip *chip, u16 addr,
+							u8 mask, u8 val)
+{
+	int rc;
+	u8 reg;
+
+	rc = pm8xxx_readb(chip->dev->parent, addr, &reg);
+	if (rc) {
+		pr_err("read failed addr = %03X, rc = %d\n", addr, rc);
+		return rc;
+	}
+	reg &= ~mask;
+	reg |= val & mask;
+	rc = pm8xxx_writeb(chip->dev->parent, addr, reg);
+	if (rc) {
+		pr_err("write failed addr = %03X, rc = %d\n", addr, rc);
+		return rc;
+	}
+	return 0;
+}
+
+static int usb_chg_plugged_in(void)
+{
+	union power_supply_propval ret = {0,};
+	static struct power_supply *psy;
+
+	if (psy == NULL) {
+		psy = power_supply_get_by_name("usb");
+		if (psy == NULL)
+			return 0;
+	}
+
+	if (psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &ret))
+		return 0;
+
+	return ret.intval;
+}
+
+#define HOLD_OREG_DATA		BIT(1)
+static int pm_bms_lock_output_data(struct pm8921_bms_chip *chip)
+{
+	int rc;
+
+	rc = pm_bms_masked_write(chip, BMS_CONTROL, HOLD_OREG_DATA,
+					HOLD_OREG_DATA);
+	if (rc) {
+		pr_err("couldnt lock bms output rc = %d\n", rc);
+		return rc;
+	}
+	return 0;
+}
+
+static int pm_bms_unlock_output_data(struct pm8921_bms_chip *chip)
+{
+	int rc;
+
+	rc = pm_bms_masked_write(chip, BMS_CONTROL, HOLD_OREG_DATA, 0);
+	if (rc) {
+		pr_err("fail to unlock BMS_CONTROL rc = %d\n", rc);
+		return rc;
+	}
+	return 0;
+}
+
+#define SELECT_OUTPUT_DATA	0x1C
+#define SELECT_OUTPUT_TYPE_SHIFT	2
+#define OCV_FOR_RBATT		0x0
+#define VSENSE_FOR_RBATT	0x1
+#define VBATT_FOR_RBATT		0x2
+#define CC_MSB			0x3
+#define CC_LSB			0x4
+#define LAST_GOOD_OCV_VALUE	0x5
+#define VSENSE_AVG		0x6
+#define VBATT_AVG		0x7
+
+static int pm_bms_read_output_data(struct pm8921_bms_chip *chip, int type,
+						int16_t *result)
+{
+	int rc;
+	u8 reg;
+
+	if (!result) {
+		pr_err("result pointer null\n");
+		return -EINVAL;
+	}
+	*result = 0;
+	if (type < OCV_FOR_RBATT || type > VBATT_AVG) {
+		pr_err("invalid type %d asked to read\n", type);
+		return -EINVAL;
+	}
+
+	rc = pm_bms_masked_write(chip, BMS_CONTROL, SELECT_OUTPUT_DATA,
+					type << SELECT_OUTPUT_TYPE_SHIFT);
+	if (rc) {
+		pr_err("fail to select %d type in BMS_CONTROL rc = %d\n",
+						type, rc);
+		return rc;
+	}
+
+	rc = pm8xxx_readb(chip->dev->parent, BMS_OUTPUT0, &reg);
+	if (rc) {
+		pr_err("fail to read BMS_OUTPUT0 for type %d rc = %d\n",
+			type, rc);
+		return rc;
+	}
+	*result = reg;
+	rc = pm8xxx_readb(chip->dev->parent, BMS_OUTPUT1, &reg);
+	if (rc) {
+		pr_err("fail to read BMS_OUTPUT1 for type %d rc = %d\n",
+			type, rc);
+		return rc;
+	}
+	*result |= reg << 8;
+	pr_debug("type %d result %x", type, *result);
+	return 0;
+}
+
+#define V_PER_BIT_MUL_FACTOR	97656
+#define V_PER_BIT_DIV_FACTOR	1000
+#define XOADC_INTRINSIC_OFFSET	0x6000
+static int xoadc_reading_to_microvolt(unsigned int a)
+{
+	if (a <= XOADC_INTRINSIC_OFFSET)
+		return 0;
+
+	return (a - XOADC_INTRINSIC_OFFSET)
+			* V_PER_BIT_MUL_FACTOR / V_PER_BIT_DIV_FACTOR;
+}
+
+#define XOADC_CALIB_UV		625000
+#define VBATT_MUL_FACTOR	3
+static int adjust_xo_vbatt_reading(struct pm8921_bms_chip *chip,
+					int usb_chg, unsigned int uv)
+{
+	s64 numerator, denominator;
+	int local_delta;
+
+	if (uv == 0)
+		return 0;
+
+	/* dont adjust if not calibrated */
+	if (chip->xoadc_v0625 == 0 || chip->xoadc_v125 == 0) {
+		pr_debug("No cal yet return %d\n", VBATT_MUL_FACTOR * uv);
+		return VBATT_MUL_FACTOR * uv;
+	}
+
+	if (usb_chg)
+		local_delta = last_usb_cal_delta_uv;
+	else
+		local_delta = 0;
+
+	pr_debug("using delta = %d\n", local_delta);
+	numerator = ((s64)uv - chip->xoadc_v0625 - local_delta)
+							* XOADC_CALIB_UV;
+	denominator =  (s64)chip->xoadc_v125 - chip->xoadc_v0625 - local_delta;
+	if (denominator == 0)
+		return uv * VBATT_MUL_FACTOR;
+	return (XOADC_CALIB_UV + local_delta + div_s64(numerator, denominator))
+						* VBATT_MUL_FACTOR;
+}
+
+#define CC_RESOLUTION_N_V1	1085069
+#define CC_RESOLUTION_D_V1	100000
+#define CC_RESOLUTION_N_V2	868056
+#define CC_RESOLUTION_D_V2	10000
+static s64 cc_to_microvolt_v1(s64 cc)
+{
+	return div_s64(cc * CC_RESOLUTION_N_V1, CC_RESOLUTION_D_V1);
+}
+
+static s64 cc_to_microvolt_v2(s64 cc)
+{
+	return div_s64(cc * CC_RESOLUTION_N_V2, CC_RESOLUTION_D_V2);
+}
+
+static s64 cc_to_microvolt(struct pm8921_bms_chip *chip, s64 cc)
+{
+	/*
+	 * resolution (the value of a single bit) was changed after revision 2.0
+	 * for more accurate readings
+	 */
+	return (chip->revision < PM8XXX_REVISION_8921_2p0) ?
+				cc_to_microvolt_v1((s64)cc) :
+				cc_to_microvolt_v2((s64)cc);
+}
+
+#define CC_READING_TICKS	55
+#define SLEEP_CLK_HZ		32768
+#define SECONDS_PER_HOUR	3600
+/**
+ * ccmicrovolt_to_nvh -
+ * @cc_uv:  coulumb counter converted to uV
+ *
+ * RETURNS:	coulumb counter based charge in nVh
+ *		(nano Volt Hour)
+ */
+static s64 ccmicrovolt_to_nvh(s64 cc_uv)
+{
+	return div_s64(cc_uv * CC_READING_TICKS * 1000,
+			SLEEP_CLK_HZ * SECONDS_PER_HOUR);
+}
+
+/* returns the signed value read from the hardware */
+static int read_cc(struct pm8921_bms_chip *chip, int *result)
+{
+	int rc;
+	uint16_t msw, lsw;
+
+	rc = pm_bms_read_output_data(chip, CC_LSB, &lsw);
+	if (rc) {
+		pr_err("fail to read CC_LSB rc = %d\n", rc);
+		return rc;
+	}
+	rc = pm_bms_read_output_data(chip, CC_MSB, &msw);
+	if (rc) {
+		pr_err("fail to read CC_MSB rc = %d\n", rc);
+		return rc;
+	}
+	*result = msw << 16 | lsw;
+	pr_debug("msw = %04x lsw = %04x cc = %d\n", msw, lsw, *result);
+	return 0;
+}
+
+static int adjust_xo_vbatt_reading_for_mbg(struct pm8921_bms_chip *chip,
+						int result)
+{
+	int64_t numerator;
+	int64_t denominator;
+
+	if (chip->amux_2_trim_delta == 0)
+		return result;
+
+	numerator = (s64)result * 1000000;
+	denominator = (1000000 + (410 * (s64)chip->amux_2_trim_delta));
+	return div_s64(numerator, denominator);
+}
+
+static int convert_vbatt_raw_to_uv(struct pm8921_bms_chip *chip,
+					int usb_chg,
+					uint16_t reading, int *result)
+{
+	*result = xoadc_reading_to_microvolt(reading);
+	pr_debug("raw = %04x vbatt = %u\n", reading, *result);
+	*result = adjust_xo_vbatt_reading(chip, usb_chg, *result);
+	pr_debug("after adj vbatt = %u\n", *result);
+	*result = adjust_xo_vbatt_reading_for_mbg(chip, *result);
+	return 0;
+}
+
+static int convert_vsense_to_uv(struct pm8921_bms_chip *chip,
+					int16_t reading, int *result)
+{
+	*result = pm8xxx_ccadc_reading_to_microvolt(chip->revision, reading);
+	pr_debug("raw = %04x vsense = %d\n", reading, *result);
+	*result = pm8xxx_cc_adjust_for_gain(*result);
+	pr_debug("after adj vsense = %d\n", *result);
+	return 0;
+}
+
+static int read_vsense_avg(struct pm8921_bms_chip *chip, int *result)
+{
+	int rc;
+	int16_t reading;
+
+	rc = pm_bms_read_output_data(chip, VSENSE_AVG, &reading);
+	if (rc) {
+		pr_err("fail to read VSENSE_AVG rc = %d\n", rc);
+		return rc;
+	}
+
+	convert_vsense_to_uv(chip, reading, result);
+	return 0;
+}
+
+static int linear_interpolate(int y0, int x0, int y1, int x1, int x)
+{
+	if (y0 == y1 || x == x0)
+		return y0;
+	if (x1 == x0 || x == x1)
+		return y1;
+
+	return y0 + ((y1 - y0) * (x - x0) / (x1 - x0));
+}
+
+static int interpolate_single_lut(struct single_row_lut *lut, int x)
+{
+	int i, result;
+
+	if (x < lut->x[0]) {
+		pr_debug("x %d less than known range return y = %d lut = %pS\n",
+							x, lut->y[0], lut);
+		return lut->y[0];
+	}
+	if (x > lut->x[lut->cols - 1]) {
+		pr_debug("x %d more than known range return y = %d lut = %pS\n",
+						x, lut->y[lut->cols - 1], lut);
+		return lut->y[lut->cols - 1];
+	}
+
+	for (i = 0; i < lut->cols; i++)
+		if (x <= lut->x[i])
+			break;
+	if (x == lut->x[i]) {
+		result = lut->y[i];
+	} else {
+		result = linear_interpolate(
+			lut->y[i - 1],
+			lut->x[i - 1],
+			lut->y[i],
+			lut->x[i],
+			x);
+	}
+	return result;
+}
+
+static int interpolate_fcc(struct pm8921_bms_chip *chip, int batt_temp)
+{
+	/* batt_temp is in tenths of degC - convert it to degC for lookups */
+	batt_temp = batt_temp/10;
+	return interpolate_single_lut(chip->fcc_temp_lut, batt_temp);
+}
+
+static int interpolate_fcc_adjusted(struct pm8921_bms_chip *chip, int batt_temp)
+{
+	/* batt_temp is in tenths of degC - convert it to degC for lookups */
+	batt_temp = batt_temp/10;
+	return interpolate_single_lut(chip->adjusted_fcc_temp_lut, batt_temp);
+}
+
+static int interpolate_scalingfactor_fcc(struct pm8921_bms_chip *chip,
+								int cycles)
+{
+	/*
+	 * sf table could be null when no battery aging data is available, in
+	 * that case return 100%
+	 */
+	if (chip->fcc_sf_lut)
+		return interpolate_single_lut(chip->fcc_sf_lut, cycles);
+	else
+		return 100;
+}
+
+static int interpolate_scalingfactor(struct pm8921_bms_chip *chip,
+				struct sf_lut *sf_lut,
+				int row_entry, int pc)
+{
+	int i, scalefactorrow1, scalefactorrow2, scalefactor;
+	int rows, cols;
+	int row1 = 0;
+	int row2 = 0;
+
+	/*
+	 * sf table could be null when no battery aging data is available, in
+	 * that case return 100%
+	 */
+	if (!sf_lut)
+		return 100;
+
+	rows = sf_lut->rows;
+	cols = sf_lut->cols;
+	if (pc > sf_lut->percent[0]) {
+		pr_debug("pc %d greater than known pc ranges for sfd\n", pc);
+		row1 = 0;
+		row2 = 0;
+	}
+	if (pc < sf_lut->percent[rows - 1]) {
+		pr_debug("pc %d less than known pc ranges for sf", pc);
+		row1 = rows - 1;
+		row2 = rows - 1;
+	}
+	for (i = 0; i < rows; i++) {
+		if (pc == sf_lut->percent[i]) {
+			row1 = i;
+			row2 = i;
+			break;
+		}
+		if (pc > sf_lut->percent[i]) {
+			row1 = i - 1;
+			row2 = i;
+			break;
+		}
+	}
+
+	if (row_entry < sf_lut->row_entries[0])
+		row_entry = sf_lut->row_entries[0];
+	if (row_entry > sf_lut->row_entries[cols - 1])
+		row_entry = sf_lut->row_entries[cols - 1];
+
+	for (i = 0; i < cols; i++)
+		if (row_entry <= sf_lut->row_entries[i])
+			break;
+	if (row_entry == sf_lut->row_entries[i]) {
+		scalefactor = linear_interpolate(
+				sf_lut->sf[row1][i],
+				sf_lut->percent[row1],
+				sf_lut->sf[row2][i],
+				sf_lut->percent[row2],
+				pc);
+		return scalefactor;
+	}
+
+	scalefactorrow1 = linear_interpolate(
+				sf_lut->sf[row1][i - 1],
+				sf_lut->row_entries[i - 1],
+				sf_lut->sf[row1][i],
+				sf_lut->row_entries[i],
+				row_entry);
+
+	scalefactorrow2 = linear_interpolate(
+				sf_lut->sf[row2][i - 1],
+				sf_lut->row_entries[i - 1],
+				sf_lut->sf[row2][i],
+				sf_lut->row_entries[i],
+				row_entry);
+
+	scalefactor = linear_interpolate(
+				scalefactorrow1,
+				sf_lut->percent[row1],
+				scalefactorrow2,
+				sf_lut->percent[row2],
+				pc);
+
+	return scalefactor;
+}
+
+static int is_between(int left, int right, int value)
+{
+	if (left >= right && left >= value && value >= right)
+		return 1;
+	if (left <= right && left <= value && value <= right)
+		return 1;
+
+	return 0;
+}
+
+static int interpolate_pc(struct pm8921_bms_chip *chip,
+				int batt_temp, int ocv)
+{
+	int i, j, pcj, pcj_minus_one, pc;
+	int rows = chip->pc_temp_ocv_lut->rows;
+	int cols = chip->pc_temp_ocv_lut->cols;
+
+	/* batt_temp is in tenths of degC - convert it to degC for lookups */
+	batt_temp = batt_temp/10;
+
+	if (batt_temp < chip->pc_temp_ocv_lut->temp[0]) {
+		pr_debug("batt_temp %d < known temp range for pc\n", batt_temp);
+		batt_temp = chip->pc_temp_ocv_lut->temp[0];
+	}
+	if (batt_temp > chip->pc_temp_ocv_lut->temp[cols - 1]) {
+		pr_debug("batt_temp %d > known temp range for pc\n", batt_temp);
+		batt_temp = chip->pc_temp_ocv_lut->temp[cols - 1];
+	}
+
+	for (j = 0; j < cols; j++)
+		if (batt_temp <= chip->pc_temp_ocv_lut->temp[j])
+			break;
+	if (batt_temp == chip->pc_temp_ocv_lut->temp[j]) {
+		/* found an exact match for temp in the table */
+		if (ocv >= chip->pc_temp_ocv_lut->ocv[0][j])
+			return chip->pc_temp_ocv_lut->percent[0];
+		if (ocv <= chip->pc_temp_ocv_lut->ocv[rows - 1][j])
+			return chip->pc_temp_ocv_lut->percent[rows - 1];
+		for (i = 0; i < rows; i++) {
+			if (ocv >= chip->pc_temp_ocv_lut->ocv[i][j]) {
+				if (ocv == chip->pc_temp_ocv_lut->ocv[i][j])
+					return
+					chip->pc_temp_ocv_lut->percent[i];
+				pc = linear_interpolate(
+					chip->pc_temp_ocv_lut->percent[i],
+					chip->pc_temp_ocv_lut->ocv[i][j],
+					chip->pc_temp_ocv_lut->percent[i - 1],
+					chip->pc_temp_ocv_lut->ocv[i - 1][j],
+					ocv);
+				return pc;
+			}
+		}
+	}
+
+	/*
+	 * batt_temp is within temperature for
+	 * column j-1 and j
+	 */
+	if (ocv >= chip->pc_temp_ocv_lut->ocv[0][j])
+		return chip->pc_temp_ocv_lut->percent[0];
+	if (ocv <= chip->pc_temp_ocv_lut->ocv[rows - 1][j - 1])
+		return chip->pc_temp_ocv_lut->percent[rows - 1];
+
+	pcj_minus_one = 0;
+	pcj = 0;
+	for (i = 0; i < rows-1; i++) {
+		if (pcj == 0
+			&& is_between(chip->pc_temp_ocv_lut->ocv[i][j],
+				chip->pc_temp_ocv_lut->ocv[i+1][j], ocv)) {
+			pcj = linear_interpolate(
+				chip->pc_temp_ocv_lut->percent[i],
+				chip->pc_temp_ocv_lut->ocv[i][j],
+				chip->pc_temp_ocv_lut->percent[i + 1],
+				chip->pc_temp_ocv_lut->ocv[i+1][j],
+				ocv);
+		}
+
+		if (pcj_minus_one == 0
+			&& is_between(chip->pc_temp_ocv_lut->ocv[i][j-1],
+				chip->pc_temp_ocv_lut->ocv[i+1][j-1], ocv)) {
+
+			pcj_minus_one = linear_interpolate(
+				chip->pc_temp_ocv_lut->percent[i],
+				chip->pc_temp_ocv_lut->ocv[i][j-1],
+				chip->pc_temp_ocv_lut->percent[i + 1],
+				chip->pc_temp_ocv_lut->ocv[i+1][j-1],
+				ocv);
+		}
+
+		if (pcj && pcj_minus_one) {
+			pc = linear_interpolate(
+				pcj_minus_one,
+				chip->pc_temp_ocv_lut->temp[j-1],
+				pcj,
+				chip->pc_temp_ocv_lut->temp[j],
+				batt_temp);
+			return pc;
+		}
+	}
+
+	if (pcj)
+		return pcj;
+
+	if (pcj_minus_one)
+		return pcj_minus_one;
+
+	pr_debug("%d ocv wasn't found for temp %d in the LUT returning 100%%",
+							ocv, batt_temp);
+	return 100;
+}
+
+#define BMS_MODE_BIT	BIT(6)
+#define EN_VBAT_BIT	BIT(5)
+#define OVERRIDE_MODE_DELAY_MS	20
+int pm8921_bms_get_simultaneous_battery_voltage_and_current(int *ibat_ua,
+								int *vbat_uv)
+{
+	int16_t vsense_raw;
+	int16_t vbat_raw;
+	int vsense_uv;
+	int usb_chg;
+
+	if (the_chip == NULL) {
+		pr_err("Called to early\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&the_chip->bms_output_lock);
+
+	pm8xxx_writeb(the_chip->dev->parent, BMS_S1_DELAY, 0x00);
+	pm_bms_masked_write(the_chip, BMS_CONTROL,
+			BMS_MODE_BIT | EN_VBAT_BIT, BMS_MODE_BIT | EN_VBAT_BIT);
+
+	msleep(OVERRIDE_MODE_DELAY_MS);
+
+	pm_bms_lock_output_data(the_chip);
+	pm_bms_read_output_data(the_chip, VSENSE_AVG, &vsense_raw);
+	pm_bms_read_output_data(the_chip, VBATT_AVG, &vbat_raw);
+	pm_bms_unlock_output_data(the_chip);
+	pm_bms_masked_write(the_chip, BMS_CONTROL,
+			BMS_MODE_BIT | EN_VBAT_BIT, 0);
+
+	pm8xxx_writeb(the_chip->dev->parent, BMS_S1_DELAY, 0x0B);
+
+	mutex_unlock(&the_chip->bms_output_lock);
+
+	usb_chg = usb_chg_plugged_in();
+
+	convert_vbatt_raw_to_uv(the_chip, usb_chg, vbat_raw, vbat_uv);
+	convert_vsense_to_uv(the_chip, vsense_raw, &vsense_uv);
+	*ibat_ua = vsense_uv * 1000 / (int)the_chip->r_sense;
+
+	pr_debug("vsense_raw = 0x%x vbat_raw = 0x%x"
+			" ibat_ua = %d vbat_uv = %d\n",
+			(uint16_t)vsense_raw, (uint16_t)vbat_raw,
+			*ibat_ua, *vbat_uv);
+	return 0;
+}
+EXPORT_SYMBOL(pm8921_bms_get_simultaneous_battery_voltage_and_current);
+
+static int read_rbatt_params_raw(struct pm8921_bms_chip *chip,
+				struct pm8921_rbatt_params *raw)
+{
+	int usb_chg;
+
+	mutex_lock(&chip->bms_output_lock);
+	pm_bms_lock_output_data(chip);
+
+	pm_bms_read_output_data(chip,
+			OCV_FOR_RBATT, &raw->ocv_for_rbatt_raw);
+	pm_bms_read_output_data(chip,
+			VBATT_FOR_RBATT, &raw->vbatt_for_rbatt_raw);
+	pm_bms_read_output_data(chip,
+			VSENSE_FOR_RBATT, &raw->vsense_for_rbatt_raw);
+
+	pm_bms_unlock_output_data(chip);
+	mutex_unlock(&chip->bms_output_lock);
+
+	usb_chg = usb_chg_plugged_in();
+	convert_vbatt_raw_to_uv(chip, usb_chg,
+			raw->vbatt_for_rbatt_raw, &raw->vbatt_for_rbatt_uv);
+	convert_vbatt_raw_to_uv(chip, usb_chg,
+			raw->ocv_for_rbatt_raw, &raw->ocv_for_rbatt_uv);
+	convert_vsense_to_uv(chip, raw->vsense_for_rbatt_raw,
+					&raw->vsense_for_rbatt_uv);
+
+	pr_debug("vbatt_for_rbatt_raw = 0x%x, vbatt_for_rbatt= %duV\n",
+			raw->vbatt_for_rbatt_raw, raw->vbatt_for_rbatt_uv);
+	pr_debug("ocv_for_rbatt_raw = 0x%x, ocv_for_rbatt= %duV\n",
+			raw->ocv_for_rbatt_raw, raw->ocv_for_rbatt_uv);
+	pr_debug("vsense_for_rbatt_raw = 0x%x, vsense_for_rbatt= %duV\n",
+			raw->vsense_for_rbatt_raw, raw->vsense_for_rbatt_uv);
+	return 0;
+}
+
+#define MBG_TRANSIENT_ERROR_RAW 51
+static void adjust_pon_ocv_raw(struct pm8921_bms_chip *chip,
+				struct pm8921_soc_params *raw)
+{
+	/* in 8921 parts the PON ocv is taken when the MBG is not settled.
+	 * decrease the pon ocv by 15mV raw value to account for it
+	 * Since a 1/3rd  of vbatt is supplied to the adc the raw value
+	 * needs to be adjusted by 5mV worth bits
+	 */
+	if (raw->last_good_ocv_raw >= MBG_TRANSIENT_ERROR_RAW)
+		raw->last_good_ocv_raw -= MBG_TRANSIENT_ERROR_RAW;
+}
+
+static int read_soc_params_raw(struct pm8921_bms_chip *chip,
+				struct pm8921_soc_params *raw)
+{
+	int usb_chg;
+
+	mutex_lock(&chip->bms_output_lock);
+	pm_bms_lock_output_data(chip);
+
+	pm_bms_read_output_data(chip,
+			LAST_GOOD_OCV_VALUE, &raw->last_good_ocv_raw);
+	read_cc(chip, &raw->cc);
+
+	pm_bms_unlock_output_data(chip);
+	mutex_unlock(&chip->bms_output_lock);
+
+	usb_chg =  usb_chg_plugged_in();
+
+	if (chip->prev_last_good_ocv_raw == 0) {
+		chip->prev_last_good_ocv_raw = raw->last_good_ocv_raw;
+		adjust_pon_ocv_raw(chip, raw);
+		convert_vbatt_raw_to_uv(chip, usb_chg,
+			raw->last_good_ocv_raw, &raw->last_good_ocv_uv);
+		chip->last_ocv_uv = raw->last_good_ocv_uv;
+	} else if (chip->prev_last_good_ocv_raw != raw->last_good_ocv_raw) {
+		chip->prev_last_good_ocv_raw = raw->last_good_ocv_raw;
+		convert_vbatt_raw_to_uv(chip, usb_chg,
+			raw->last_good_ocv_raw, &raw->last_good_ocv_uv);
+		chip->last_ocv_uv = raw->last_good_ocv_uv;
+		/* forget the old cc value upon ocv */
+		chip->last_cc_uah = 0;
+	} else {
+		raw->last_good_ocv_uv = chip->last_ocv_uv;
+	}
+
+	/* fake a high OCV if we are just done charging */
+	if (chip->ocv_reading_at_100 != raw->last_good_ocv_raw) {
+		chip->ocv_reading_at_100 = 0;
+		chip->cc_reading_at_100 = 0;
+	} else {
+		/*
+		 * force 100% ocv by selecting the highest voltage the
+		 * battery could ever reach
+		 */
+		raw->last_good_ocv_uv = chip->max_voltage_uv;
+		chip->last_ocv_uv = chip->max_voltage_uv;
+	}
+	pr_debug("0p625 = %duV\n", chip->xoadc_v0625);
+	pr_debug("1p25 = %duV\n", chip->xoadc_v125);
+	pr_debug("last_good_ocv_raw= 0x%x, last_good_ocv_uv= %duV\n",
+			raw->last_good_ocv_raw, raw->last_good_ocv_uv);
+	pr_debug("cc_raw= 0x%x\n", raw->cc);
+	return 0;
+}
+
+static int get_rbatt(struct pm8921_bms_chip *chip, int soc_rbatt, int batt_temp)
+{
+	int rbatt, scalefactor;
+
+	rbatt = (last_rbatt < 0) ? chip->default_rbatt_mohm : last_rbatt;
+	pr_debug("rbatt before scaling = %d\n", rbatt);
+	if (chip->rbatt_sf_lut == NULL)  {
+		pr_debug("RBATT = %d\n", rbatt);
+		return rbatt;
+	}
+	/* Convert the batt_temp to DegC from deciDegC */
+	batt_temp = batt_temp / 10;
+	scalefactor = interpolate_scalingfactor(chip, chip->rbatt_sf_lut,
+							batt_temp, soc_rbatt);
+	pr_debug("rbatt sf = %d for batt_temp = %d, soc_rbatt = %d\n",
+				scalefactor, batt_temp, soc_rbatt);
+	rbatt = (rbatt * scalefactor) / 100;
+
+	rbatt += the_chip->rconn_mohm;
+	pr_debug("adding rconn_mohm = %d rbatt = %d\n",
+				the_chip->rconn_mohm, rbatt);
+
+	if (is_between(20, 10, soc_rbatt))
+		rbatt = rbatt
+			+ ((20 - soc_rbatt) * chip->delta_rbatt_mohm) / 10;
+	else
+		if (is_between(10, 0, soc_rbatt))
+			rbatt = rbatt + chip->delta_rbatt_mohm;
+
+	pr_debug("RBATT = %d\n", rbatt);
+	return rbatt;
+}
+
+static int calculate_rbatt_resume(struct pm8921_bms_chip *chip,
+				struct pm8921_rbatt_params *raw)
+{
+	unsigned int  r_batt;
+
+	if (raw->ocv_for_rbatt_uv <= 0
+		|| raw->ocv_for_rbatt_uv <= raw->vbatt_for_rbatt_uv
+		|| raw->vsense_for_rbatt_raw <= 0) {
+		pr_debug("rbatt readings unavailable ocv = %d, vbatt = %d,"
+					"vsen = %d\n",
+					raw->ocv_for_rbatt_uv,
+					raw->vbatt_for_rbatt_uv,
+					raw->vsense_for_rbatt_raw);
+		return -EINVAL;
+	}
+	r_batt = ((raw->ocv_for_rbatt_uv - raw->vbatt_for_rbatt_uv)
+			* chip->r_sense) / raw->vsense_for_rbatt_uv;
+	pr_debug("r_batt = %umilliOhms", r_batt);
+	return r_batt;
+}
+
+static int calculate_fcc_uah(struct pm8921_bms_chip *chip, int batt_temp,
+							int chargecycles)
+{
+	int initfcc, result, scalefactor = 0;
+
+	if (chip->adjusted_fcc_temp_lut == NULL) {
+		initfcc = interpolate_fcc(chip, batt_temp);
+
+		scalefactor = interpolate_scalingfactor_fcc(chip, chargecycles);
+
+		/* Multiply the initial FCC value by the scale factor. */
+		result = (initfcc * scalefactor * 1000) / 100;
+		pr_debug("fcc = %d uAh\n", result);
+		return result;
+	} else {
+		return 1000 * interpolate_fcc_adjusted(chip, batt_temp);
+	}
+}
+
+static int get_battery_uvolts(struct pm8921_bms_chip *chip, int *uvolts)
+{
+	int rc;
+	struct pm8xxx_adc_chan_result result;
+
+	rc = pm8xxx_adc_read(chip->vbat_channel, &result);
+	if (rc) {
+		pr_err("error reading adc channel = %d, rc = %d\n",
+					chip->vbat_channel, rc);
+		return rc;
+	}
+	pr_debug("mvolts phy = %lld meas = 0x%llx", result.physical,
+						result.measurement);
+	*uvolts = (int)result.physical;
+	return 0;
+}
+
+static int adc_based_ocv(struct pm8921_bms_chip *chip, int *ocv)
+{
+	int vbatt, rbatt, ibatt_ua, rc;
+
+	rc = get_battery_uvolts(chip, &vbatt);
+	if (rc) {
+		pr_err("failed to read vbatt from adc rc = %d\n", rc);
+		return rc;
+	}
+
+	rc =  pm8921_bms_get_battery_current(&ibatt_ua);
+	if (rc) {
+		pr_err("failed to read batt current rc = %d\n", rc);
+		return rc;
+	}
+
+	rbatt = (last_rbatt < 0) ? chip->default_rbatt_mohm : last_rbatt;
+	*ocv = vbatt + (ibatt_ua * rbatt)/1000;
+	return 0;
+}
+
+static int calculate_pc(struct pm8921_bms_chip *chip, int ocv_uv, int batt_temp,
+							int chargecycles)
+{
+	int pc, scalefactor;
+
+	pc = interpolate_pc(chip, batt_temp, ocv_uv / 1000);
+	pr_debug("pc = %u for ocv = %dmicroVolts batt_temp = %d\n",
+					pc, ocv_uv, batt_temp);
+
+	scalefactor = interpolate_scalingfactor(chip,
+					chip->pc_sf_lut, chargecycles, pc);
+	pr_debug("scalefactor = %u batt_temp = %d\n", scalefactor, batt_temp);
+
+	/* Multiply the initial FCC value by the scale factor. */
+	pc = (pc * scalefactor) / 100;
+	return pc;
+}
+
+/**
+ * calculate_cc_uah -
+ * @chip:		the bms chip pointer
+ * @cc:			the cc reading from bms h/w
+ * @val:		return value
+ * @coulumb_counter:	adjusted coulumb counter for 100%
+ *
+ * RETURNS: in val pointer coulumb counter based charger in uAh
+ *          (micro Amp hour)
+ */
+static void calculate_cc_uah(struct pm8921_bms_chip *chip, int cc, int *val)
+{
+	int64_t cc_voltage_uv, cc_nvh, cc_uah;
+
+	cc_voltage_uv = cc;
+	cc_voltage_uv -= chip->cc_reading_at_100;
+	pr_debug("cc = %d. after subtracting 0x%x cc = %lld\n",
+					cc, chip->cc_reading_at_100,
+					cc_voltage_uv);
+	cc_voltage_uv = cc_to_microvolt(chip, cc_voltage_uv);
+	cc_voltage_uv = pm8xxx_cc_adjust_for_gain(cc_voltage_uv);
+	pr_debug("cc_voltage_uv = %lld microvolts\n", cc_voltage_uv);
+	cc_nvh = ccmicrovolt_to_nvh(cc_voltage_uv);
+	pr_debug("cc_nvh = %lld nano_volt_hour\n", cc_nvh);
+	cc_uah = div_s64(cc_nvh, chip->r_sense);
+	*val = cc_uah;
+}
+
+static int calculate_uuc_uah_at_given_current(struct pm8921_bms_chip *chip,
+				 int batt_temp, int chargecycles,
+				int rbatt, int fcc_uah, int i_ma)
+{
+	int unusable_uv, pc_unusable, uuc;
+
+	/* calculate unusable charge with itest */
+	unusable_uv = (rbatt * i_ma) + (chip->v_failure * 1000);
+	pc_unusable = calculate_pc(chip, unusable_uv, batt_temp, chargecycles);
+	uuc = (fcc_uah * pc_unusable) / 100;
+	pr_debug("For i_ma = %d, unusable_uv = %d unusable_pc = %d uuc = %d\n",
+					i_ma, unusable_uv, pc_unusable, uuc);
+	return uuc;
+}
+
+/* soc_rbatt when uuc_reported should be equal to uuc_now */
+#define SOC_RBATT_CHG		80
+#define SOC_RBATT_DISCHG	10
+static int calculate_unusable_charge_uah(struct pm8921_bms_chip *chip,
+				int rbatt, int fcc_uah, int cc_uah,
+				int soc_rbatt, int batt_temp, int chargecycles)
+{
+	struct timeval now;
+	int delta_time_s;
+	int delta_cc_uah;
+	int iavg_ua, iavg_ma;
+	int uuc_uah_itest, uuc_uah_iavg, uuc_now, uuc_reported;
+	s64 stepsize = 0;
+	int firsttime = 0;
+
+	delta_cc_uah = cc_uah - chip->last_cc_uah;
+	do_gettimeofday(&now);
+	if (chip->t.tv_sec != 0) {
+		delta_time_s = (now.tv_sec - chip->t.tv_sec);
+	} else {
+		/* uuc calculation for the first time */
+		delta_time_s = 0;
+		firsttime = 1;
+	}
+
+	if (delta_time_s != 0)
+		iavg_ua = div_s64((s64)delta_cc_uah * 3600, delta_time_s);
+	else
+		iavg_ua = 0;
+
+	iavg_ma = iavg_ua/1000;
+
+	pr_debug("t.tv_sec = %d, now.tv_sec = %d\n", (int)chip->t.tv_sec,
+				(int)now.tv_sec);
+
+	pr_debug("delta_time_s = %d iavg_ma = %d\n", delta_time_s, iavg_ma);
+
+	if (iavg_ma == 0) {
+		pr_debug("Iavg = 0 returning last uuc = %d\n",
+				chip->last_uuc_uah);
+		uuc_reported = chip->last_uuc_uah;
+		goto out;
+	}
+
+	/* calculate unusable charge with itest */
+	uuc_uah_itest = calculate_uuc_uah_at_given_current(chip,
+					batt_temp, chargecycles,
+					rbatt, fcc_uah, chip->i_test);
+
+	pr_debug("itest = %d uuc_itest = %d\n", chip->i_test, uuc_uah_itest);
+
+	/* calculate unusable charge with iavg */
+	iavg_ma = max(0, iavg_ma);
+	uuc_uah_iavg = calculate_uuc_uah_at_given_current(chip,
+					batt_temp, chargecycles,
+					rbatt, fcc_uah, iavg_ma);
+	pr_debug("iavg = %d uuc_iavg = %d\n", iavg_ma, uuc_uah_iavg);
+
+	if (firsttime) {
+		if (cc_uah < chip->last_cc_uah)
+			chip->last_uuc_uah = uuc_uah_itest;
+		else
+			chip->last_uuc_uah = uuc_uah_iavg;
+		pr_debug("firsttime uuc_prev = %d\n", chip->last_uuc_uah);
+	}
+
+	uuc_now = min(uuc_uah_itest, uuc_uah_iavg);
+
+	uuc_reported = -EINVAL;
+	if (cc_uah < chip->last_cc_uah) {
+		/* charging */
+		if (uuc_now < chip->last_uuc_uah) {
+			stepsize = max(1, (SOC_RBATT_CHG - soc_rbatt));
+			/* uuc_reported = uuc_prev + deltauuc / stepsize */
+			uuc_reported = div_s64 (stepsize * chip->last_uuc_uah
+					+ (uuc_now - chip->last_uuc_uah),
+					stepsize);
+			uuc_reported = max(0, uuc_reported);
+		}
+	} else {
+		if (uuc_now > chip->last_uuc_uah) {
+			stepsize = max(1, (soc_rbatt - SOC_RBATT_DISCHG));
+			/* uuc_reported = uuc_prev + deltauuc / stepsize */
+			uuc_reported = div_s64 (stepsize * chip->last_uuc_uah
+					+ (uuc_now - chip->last_uuc_uah),
+					stepsize);
+			uuc_reported = max(0, uuc_reported);
+		}
+	}
+	if (uuc_reported == -EINVAL)
+		uuc_reported = chip->last_uuc_uah;
+
+	pr_debug("uuc_now = %d uuc_prev = %d stepsize = %d uuc_reported = %d\n",
+			uuc_now, chip->last_uuc_uah, (int)stepsize,
+			uuc_reported);
+
+out:
+	/* remember the reported uuc */
+	chip->last_uuc_uah = uuc_reported;
+
+	/* remember cc_uah */
+	chip->last_cc_uah = cc_uah;
+
+	/* remember this time */
+	chip->t = now;
+
+	return uuc_reported;
+}
+
+/* calculate remainging charge at the time of ocv */
+static int calculate_remaining_charge_uah(struct pm8921_bms_chip *chip,
+						struct pm8921_soc_params *raw,
+						int fcc_uah, int batt_temp,
+						int chargecycles)
+{
+	int  ocv, pc;
+
+	ocv = raw->last_good_ocv_uv;
+	pc = calculate_pc(chip, ocv, batt_temp, chargecycles);
+	pr_debug("ocv = %d pc = %d\n", ocv, pc);
+	return (fcc_uah * pc) / 100;
+}
+
+static void calculate_soc_params(struct pm8921_bms_chip *chip,
+						struct pm8921_soc_params *raw,
+						int batt_temp, int chargecycles,
+						int *fcc_uah,
+						int *unusable_charge_uah,
+						int *remaining_charge_uah,
+						int *cc_uah,
+						int *rbatt)
+{
+	int soc_rbatt;
+
+	*fcc_uah = calculate_fcc_uah(chip, batt_temp, chargecycles);
+	pr_debug("FCC = %uuAh batt_temp = %d, cycles = %d\n",
+					*fcc_uah, batt_temp, chargecycles);
+
+
+	/* calculate remainging charge */
+	*remaining_charge_uah = calculate_remaining_charge_uah(chip, raw,
+					*fcc_uah, batt_temp, chargecycles);
+	pr_debug("RC = %uuAh\n", *remaining_charge_uah);
+
+	/* calculate cc micro_volt_hour */
+	calculate_cc_uah(chip, raw->cc, cc_uah);
+	pr_debug("cc_uah = %duAh raw->cc = %x cc = %lld after subtracting %x\n",
+				*cc_uah, raw->cc,
+				(int64_t)raw->cc - chip->cc_reading_at_100,
+				chip->cc_reading_at_100);
+
+	soc_rbatt = ((*remaining_charge_uah - *cc_uah) * 100) / *fcc_uah;
+	if (soc_rbatt < 0)
+		soc_rbatt = 0;
+	*rbatt = get_rbatt(chip, soc_rbatt, batt_temp);
+
+	*unusable_charge_uah = calculate_unusable_charge_uah(chip, *rbatt,
+					*fcc_uah, *cc_uah, soc_rbatt,
+					batt_temp,
+					chargecycles);
+	pr_debug("UUC = %uuAh\n", *unusable_charge_uah);
+}
+
+static int calculate_real_fcc_uah(struct pm8921_bms_chip *chip,
+				struct pm8921_soc_params *raw,
+				int batt_temp, int chargecycles,
+				int *ret_fcc_uah)
+{
+	int fcc_uah, unusable_charge_uah;
+	int remaining_charge_uah;
+	int cc_uah;
+	int real_fcc_uah;
+	int rbatt;
+
+	calculate_soc_params(chip, raw, batt_temp, chargecycles,
+						&fcc_uah,
+						&unusable_charge_uah,
+						&remaining_charge_uah,
+						&cc_uah,
+						&rbatt);
+
+	real_fcc_uah = remaining_charge_uah - cc_uah;
+	*ret_fcc_uah = fcc_uah;
+	pr_debug("real_fcc = %d, RC = %d CC = %d fcc = %d\n",
+			real_fcc_uah, remaining_charge_uah, cc_uah, fcc_uah);
+	return real_fcc_uah;
+}
+
+static int bound_soc(int soc)
+{
+	soc = max(0, soc);
+	soc = min(100, soc);
+	return soc;
+}
+
+static int last_soc_est = -EINVAL;
+static int adjust_soc(struct pm8921_bms_chip *chip, int soc, int batt_temp,
+		int rbatt , int fcc_uah, int uuc_uah, int cc_uah)
+{
+	int ibat_ua = 0, vbat_uv = 0;
+	int ocv_est_uv = 0, soc_est = 0, pc_est = 0, pc = 0;
+	int delta_ocv_uv = 0;
+	int n = 0;
+	int rc_new_uah = 0;
+	int pc_new = 0;
+	int soc_new = 0;
+	int m = 0;
+
+	pm8921_bms_get_simultaneous_battery_voltage_and_current(&ibat_ua,
+		&vbat_uv);
+
+	if (ibat_ua < 0)
+		goto out;
+	ocv_est_uv = vbat_uv + (ibat_ua * rbatt)/1000;
+	pc_est = calculate_pc(chip, ocv_est_uv, batt_temp, last_chargecycles);
+	soc_est = div_s64((s64)fcc_uah * pc_est - uuc_uah*100,
+						(s64)fcc_uah - uuc_uah);
+	soc_est = bound_soc(soc_est);
+
+	/*
+	 * do not adjust if soc_est is between 45 and 25 OR soc_est is
+	 * same as what bms calculated
+	 */
+	if (is_between(45, 25, soc_est) || soc_est == soc)
+		goto out;
+
+	if (last_soc_est == -EINVAL)
+		last_soc_est = soc;
+
+	n = min(200, max(1 , soc + soc_est + last_soc_est));
+	/* remember the last soc_est in last_soc_est */
+	last_soc_est = soc_est;
+
+	pc = calculate_pc(chip, chip->last_ocv_uv,
+				batt_temp, last_chargecycles);
+	if (pc > 0) {
+		pc_new = calculate_pc(chip, chip->last_ocv_uv - (++m * 1000),
+						batt_temp, last_chargecycles);
+		while (pc_new == pc) {
+			/* start taking 10mV steps */
+			m = m + 10;
+			pc_new = calculate_pc(chip,
+						chip->last_ocv_uv - (m * 1000),
+						batt_temp, last_chargecycles);
+		}
+	} else {
+		/*
+		 * pc is already at the lowest point,
+		 * assume 1 millivolt translates to 1% pc
+		 */
+		pc = 1;
+		pc_new = 0;
+		m = 1;
+	}
+
+	delta_ocv_uv = div_s64((soc - soc_est) * (s64)m * 1000,
+							n * (pc - pc_new));
+	chip->last_ocv_uv -= delta_ocv_uv;
+
+	if (chip->last_ocv_uv >= chip->max_voltage_uv)
+		chip->last_ocv_uv = chip->max_voltage_uv;
+
+	/* calculate the soc based on this new ocv */
+	pc_new = calculate_pc(chip, chip->last_ocv_uv,
+						batt_temp, last_chargecycles);
+	rc_new_uah = (fcc_uah * pc_new) / 100;
+	soc_new = (rc_new_uah - cc_uah - uuc_uah)*100 / (fcc_uah - uuc_uah);
+	soc_new = bound_soc(soc_new);
+
+	/*
+	 * if soc_new is ZERO force it higher so that phone doesnt report soc=0
+	 * soc = 0 should happen only when soc_est == 0
+	 */
+	if (soc_new == 0 && soc_est != 0)
+		soc_new = 1;
+
+	soc = soc_new;
+
+out:
+	pr_debug("ibat_ua = %d, vbat_uv = %d, ocv_est_uv = %d, pc_est = %d, "
+		"soc_est = %d, n = %d, delta_ocv_uv = %d, last_ocv_uv = %d, "
+		"pc_new = %d, soc_new = %d\n",
+		ibat_ua, vbat_uv, ocv_est_uv, pc_est,
+		soc_est, n, delta_ocv_uv, chip->last_ocv_uv,
+		pc_new, soc_new);
+
+	return soc;
+}
+
+/*
+ * Remaining Usable Charge = remaining_charge (charge at ocv instance)
+ *				- coloumb counter charge
+ *				- unusable charge (due to battery resistance)
+ * SOC% = (remaining usable charge/ fcc - usable_charge);
+ */
+static int calculate_state_of_charge(struct pm8921_bms_chip *chip,
+					struct pm8921_soc_params *raw,
+					int batt_temp, int chargecycles)
+{
+	int remaining_usable_charge_uah, fcc_uah, unusable_charge_uah;
+	int remaining_charge_uah, soc;
+	int cc_uah;
+	int rbatt;
+
+	calculate_soc_params(chip, raw, batt_temp, chargecycles,
+						&fcc_uah,
+						&unusable_charge_uah,
+						&remaining_charge_uah,
+						&cc_uah,
+						&rbatt);
+
+	/* calculate remaining usable charge */
+	remaining_usable_charge_uah = remaining_charge_uah
+					- cc_uah
+					- unusable_charge_uah;
+
+	pr_debug("RUC = %duAh\n", remaining_usable_charge_uah);
+	if (fcc_uah - unusable_charge_uah <= 0) {
+		pr_warn("FCC = %duAh, UUC = %duAh forcing soc = 0\n",
+						fcc_uah, unusable_charge_uah);
+		soc = 0;
+	} else {
+		soc = (remaining_usable_charge_uah * 100)
+			/ (fcc_uah - unusable_charge_uah);
+	}
+
+	if (soc > 100)
+		soc = 100;
+	pr_debug("SOC = %u%%\n", soc);
+
+	if (bms_fake_battery != -EINVAL) {
+		pr_debug("Returning Fake SOC = %d%%\n", bms_fake_battery);
+		return bms_fake_battery;
+	}
+
+	if (soc < 0) {
+		pr_err("bad rem_usb_chg = %d rem_chg %d,"
+				"cc_uah %d, unusb_chg %d\n",
+				remaining_usable_charge_uah,
+				remaining_charge_uah,
+				cc_uah, unusable_charge_uah);
+
+		pr_err("for bad rem_usb_chg last_ocv_uv = %d"
+				"chargecycles = %d, batt_temp = %d"
+				"fcc = %d soc =%d\n",
+				chip->last_ocv_uv, chargecycles, batt_temp,
+				fcc_uah, soc);
+		soc = 0;
+	}
+
+	soc = adjust_soc(chip, soc, batt_temp, rbatt,
+					fcc_uah, unusable_charge_uah, cc_uah);
+
+	if (last_soc == -EINVAL || soc <= last_soc) {
+		last_soc = soc;
+	} else {
+		/*
+		 * soc > last_soc
+		 * the device must be charging for reporting a higher soc, if
+		 * not ignore this soc and continue reporting the last_soc
+		 */
+		if (the_chip->start_percent != -EINVAL)
+			last_soc = soc;
+		else
+			pr_debug("soc = %d reporting last_soc = %d\n", soc,
+								last_soc);
+	}
+
+	pr_debug("Reported SOC = %u%%\n", last_soc);
+	return last_soc;
+}
+#define MIN_DELTA_625_UV	1000
+static void calib_hkadc(struct pm8921_bms_chip *chip)
+{
+	int voltage, rc;
+	struct pm8xxx_adc_chan_result result;
+	int usb_chg;
+	int this_delta;
+
+	rc = pm8xxx_adc_read(the_chip->ref1p25v_channel, &result);
+	if (rc) {
+		pr_err("ADC failed for 1.25volts rc = %d\n", rc);
+		return;
+	}
+	voltage = xoadc_reading_to_microvolt(result.adc_code);
+
+	pr_debug("result 1.25v = 0x%x, voltage = %duV adc_meas = %lld\n",
+				result.adc_code, voltage, result.measurement);
+
+	chip->xoadc_v125 = voltage;
+
+	rc = pm8xxx_adc_read(the_chip->ref625mv_channel, &result);
+	if (rc) {
+		pr_err("ADC failed for 1.25volts rc = %d\n", rc);
+		return;
+	}
+	voltage = xoadc_reading_to_microvolt(result.adc_code);
+
+	usb_chg = usb_chg_plugged_in();
+	pr_debug("result 0.625V = 0x%x, voltage = %duV adc_meas = %lld "
+				"usb_chg = %d\n",
+				result.adc_code, voltage, result.measurement,
+				usb_chg);
+
+	if (usb_chg)
+		chip->xoadc_v0625_usb_present = voltage;
+	else
+		chip->xoadc_v0625_usb_absent = voltage;
+
+	chip->xoadc_v0625 = voltage;
+	if (chip->xoadc_v0625_usb_present && chip->xoadc_v0625_usb_absent) {
+		this_delta = chip->xoadc_v0625_usb_present
+						- chip->xoadc_v0625_usb_absent;
+		pr_debug("this_delta= %duV\n", this_delta);
+		if (this_delta > MIN_DELTA_625_UV)
+			last_usb_cal_delta_uv = this_delta;
+		pr_debug("625V_present= %d, 625V_absent= %d, delta = %duV\n",
+			chip->xoadc_v0625_usb_present,
+			chip->xoadc_v0625_usb_absent,
+			last_usb_cal_delta_uv);
+	}
+}
+
+static void calibrate_hkadc_work(struct work_struct *work)
+{
+	struct pm8921_bms_chip *chip = container_of(work,
+				struct pm8921_bms_chip, calib_hkadc_work);
+
+	calib_hkadc(chip);
+}
+
+void pm8921_bms_calibrate_hkadc(void)
+{
+	schedule_work(&the_chip->calib_hkadc_work);
+}
+
+static void calibrate_ccadc_work(struct work_struct *work)
+{
+	struct pm8921_bms_chip *chip = container_of(work,
+				struct pm8921_bms_chip, calib_ccadc_work.work);
+
+	pm8xxx_calib_ccadc();
+	calib_hkadc(chip);
+	schedule_delayed_work(&chip->calib_ccadc_work,
+			round_jiffies_relative(msecs_to_jiffies
+			(chip->calib_delay_ms)));
+}
+
+int pm8921_bms_get_vsense_avg(int *result)
+{
+	int rc = -EINVAL;
+
+	if (the_chip) {
+		mutex_lock(&the_chip->bms_output_lock);
+		pm_bms_lock_output_data(the_chip);
+		rc = read_vsense_avg(the_chip, result);
+		pm_bms_unlock_output_data(the_chip);
+		mutex_unlock(&the_chip->bms_output_lock);
+	}
+
+	pr_err("called before initialization\n");
+	return rc;
+}
+EXPORT_SYMBOL(pm8921_bms_get_vsense_avg);
+
+int pm8921_bms_get_battery_current(int *result_ua)
+{
+	int vsense;
+
+	if (!the_chip) {
+		pr_err("called before initialization\n");
+		return -EINVAL;
+	}
+	if (the_chip->r_sense == 0) {
+		pr_err("r_sense is zero\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&the_chip->bms_output_lock);
+	pm_bms_lock_output_data(the_chip);
+	read_vsense_avg(the_chip, &vsense);
+	pm_bms_unlock_output_data(the_chip);
+	mutex_unlock(&the_chip->bms_output_lock);
+	pr_debug("vsense=%duV\n", vsense);
+	/* cast for signed division */
+	*result_ua = vsense * 1000 / (int)the_chip->r_sense;
+	pr_debug("ibat=%duA\n", *result_ua);
+	return 0;
+}
+EXPORT_SYMBOL(pm8921_bms_get_battery_current);
+
+int pm8921_bms_get_percent_charge(void)
+{
+	int batt_temp, rc;
+	struct pm8xxx_adc_chan_result result;
+	struct pm8921_soc_params raw;
+	int soc;
+
+	if (!the_chip) {
+		pr_err("called before initialization\n");
+		return -EINVAL;
+	}
+
+	rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
+	if (rc) {
+		pr_err("error reading adc channel = %d, rc = %d\n",
+					the_chip->batt_temp_channel, rc);
+		return rc;
+	}
+	pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical,
+						result.measurement);
+	batt_temp = (int)result.physical;
+
+	mutex_lock(&the_chip->last_ocv_uv_mutex);
+	read_soc_params_raw(the_chip, &raw);
+
+	soc = calculate_state_of_charge(the_chip, &raw,
+					batt_temp, last_chargecycles);
+	mutex_unlock(&the_chip->last_ocv_uv_mutex);
+	return soc;
+}
+EXPORT_SYMBOL_GPL(pm8921_bms_get_percent_charge);
+
+int pm8921_bms_get_rbatt(void)
+{
+	int batt_temp, rc;
+	struct pm8xxx_adc_chan_result result;
+	struct pm8921_soc_params raw;
+	int fcc_uah;
+	int unusable_charge_uah;
+	int remaining_charge_uah;
+	int cc_uah;
+	int rbatt;
+
+	if (!the_chip) {
+		pr_err("called before initialization\n");
+		return -EINVAL;
+	}
+
+	rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
+	if (rc) {
+		pr_err("error reading adc channel = %d, rc = %d\n",
+					the_chip->batt_temp_channel, rc);
+		return rc;
+	}
+	pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
+						result.measurement);
+	batt_temp = (int)result.physical;
+
+	mutex_lock(&the_chip->last_ocv_uv_mutex);
+
+	read_soc_params_raw(the_chip, &raw);
+
+	calculate_soc_params(the_chip, &raw, batt_temp, last_chargecycles,
+						&fcc_uah,
+						&unusable_charge_uah,
+						&remaining_charge_uah,
+						&cc_uah,
+						&rbatt);
+	mutex_unlock(&the_chip->last_ocv_uv_mutex);
+
+	return rbatt;
+}
+EXPORT_SYMBOL_GPL(pm8921_bms_get_rbatt);
+
+int pm8921_bms_get_fcc(void)
+{
+	int batt_temp, rc;
+	struct pm8xxx_adc_chan_result result;
+
+	if (!the_chip) {
+		pr_err("called before initialization\n");
+		return -EINVAL;
+	}
+
+	rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
+	if (rc) {
+		pr_err("error reading adc channel = %d, rc = %d\n",
+					the_chip->batt_temp_channel, rc);
+		return rc;
+	}
+	pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical,
+						result.measurement);
+	batt_temp = (int)result.physical;
+	return calculate_fcc_uah(the_chip, batt_temp, last_chargecycles);
+}
+EXPORT_SYMBOL_GPL(pm8921_bms_get_fcc);
+
+#define IBAT_TOL_MASK		0x0F
+#define OCV_TOL_MASK			0xF0
+#define IBAT_TOL_DEFAULT	0x03
+#define IBAT_TOL_NOCHG		0x0F
+#define OCV_TOL_DEFAULT		0x20
+#define OCV_TOL_NO_OCV		0x00
+void pm8921_bms_charging_began(void)
+{
+	int batt_temp, rc;
+	struct pm8xxx_adc_chan_result result;
+	struct pm8921_soc_params raw;
+
+	rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
+	if (rc) {
+		pr_err("error reading adc channel = %d, rc = %d\n",
+				the_chip->batt_temp_channel, rc);
+		return;
+	}
+	pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
+						result.measurement);
+	batt_temp = (int)result.physical;
+
+	mutex_lock(&the_chip->last_ocv_uv_mutex);
+	read_soc_params_raw(the_chip, &raw);
+
+	the_chip->start_percent = calculate_state_of_charge(the_chip, &raw,
+					batt_temp, last_chargecycles);
+	mutex_unlock(&the_chip->last_ocv_uv_mutex);
+
+	bms_start_percent = the_chip->start_percent;
+	bms_start_ocv_uv = raw.last_good_ocv_uv;
+	calculate_cc_uah(the_chip, raw.cc, &bms_start_cc_uah);
+	pm_bms_masked_write(the_chip, BMS_TOLERANCES,
+			IBAT_TOL_MASK, IBAT_TOL_DEFAULT);
+	pr_debug("start_percent = %u%%\n", the_chip->start_percent);
+}
+EXPORT_SYMBOL_GPL(pm8921_bms_charging_began);
+
+#define DELTA_FCC_PERCENT	3
+#define MIN_START_PERCENT_FOR_LEARNING	30
+void pm8921_bms_charging_end(int is_battery_full)
+{
+	int batt_temp, rc;
+	struct pm8xxx_adc_chan_result result;
+	struct pm8921_soc_params raw;
+
+	if (the_chip == NULL)
+		return;
+
+	rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
+	if (rc) {
+		pr_err("error reading adc channel = %d, rc = %d\n",
+				the_chip->batt_temp_channel, rc);
+		return;
+	}
+	pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
+						result.measurement);
+	batt_temp = (int)result.physical;
+
+	mutex_lock(&the_chip->last_ocv_uv_mutex);
+
+	read_soc_params_raw(the_chip, &raw);
+
+	calculate_cc_uah(the_chip, raw.cc, &bms_end_cc_uah);
+
+	bms_end_ocv_uv = raw.last_good_ocv_uv;
+
+	if (is_battery_full && the_chip->enable_fcc_learning
+		&& the_chip->start_percent <= MIN_START_PERCENT_FOR_LEARNING) {
+		int fcc_uah, new_fcc_uah, delta_fcc_uah;
+
+		new_fcc_uah = calculate_real_fcc_uah(the_chip, &raw,
+						batt_temp, last_chargecycles,
+						&fcc_uah);
+		delta_fcc_uah = new_fcc_uah - fcc_uah;
+		if (delta_fcc_uah < 0)
+			delta_fcc_uah = -delta_fcc_uah;
+
+		if (delta_fcc_uah * 100  > (DELTA_FCC_PERCENT * fcc_uah)) {
+			/* new_fcc_uah is outside the scope limit it */
+			if (new_fcc_uah > fcc_uah)
+				new_fcc_uah
+				= (fcc_uah +
+					(DELTA_FCC_PERCENT * fcc_uah) / 100);
+			else
+				new_fcc_uah
+				= (fcc_uah -
+					(DELTA_FCC_PERCENT * fcc_uah) / 100);
+
+			pr_debug("delta_fcc=%d > %d percent of fcc=%d"
+					"restring it to %d\n",
+					delta_fcc_uah, DELTA_FCC_PERCENT,
+					fcc_uah, new_fcc_uah);
+		}
+
+		last_real_fcc_mah = new_fcc_uah/1000;
+		last_real_fcc_batt_temp = batt_temp;
+		readjust_fcc_table();
+
+	}
+
+	if (is_battery_full) {
+		the_chip->ocv_reading_at_100 = raw.last_good_ocv_raw;
+		the_chip->cc_reading_at_100 = raw.cc;
+
+		the_chip->last_ocv_uv = the_chip->max_voltage_uv;
+		raw.last_good_ocv_uv = the_chip->max_voltage_uv;
+		/*
+		 * since we are treating this as an ocv event
+		 * forget the old cc value
+		 */
+		the_chip->last_cc_uah = 0;
+		pr_debug("EOC ocv_reading = 0x%x cc = 0x%x\n",
+				the_chip->ocv_reading_at_100,
+				the_chip->cc_reading_at_100);
+	}
+
+	the_chip->end_percent = calculate_state_of_charge(the_chip, &raw,
+					batt_temp, last_chargecycles);
+	mutex_unlock(&the_chip->last_ocv_uv_mutex);
+
+	bms_end_percent = the_chip->end_percent;
+
+	if (the_chip->end_percent > the_chip->start_percent) {
+		last_charge_increase +=
+			the_chip->end_percent - the_chip->start_percent;
+		if (last_charge_increase > 100) {
+			last_chargecycles++;
+			last_charge_increase = last_charge_increase % 100;
+		}
+	}
+	pr_debug("end_percent = %u%% last_charge_increase = %d"
+			"last_chargecycles = %d\n",
+			the_chip->end_percent,
+			last_charge_increase,
+			last_chargecycles);
+	the_chip->start_percent = -EINVAL;
+	the_chip->end_percent = -EINVAL;
+	pm_bms_masked_write(the_chip, BMS_TOLERANCES,
+				IBAT_TOL_MASK, IBAT_TOL_NOCHG);
+}
+EXPORT_SYMBOL_GPL(pm8921_bms_charging_end);
+
+int pm8921_bms_stop_ocv_updates(struct pm8921_bms_chip *chip)
+{
+	pr_debug("stopping ocv updates\n");
+	return pm_bms_masked_write(chip, BMS_TOLERANCES,
+			OCV_TOL_MASK, OCV_TOL_NO_OCV);
+}
+EXPORT_SYMBOL_GPL(pm8921_bms_stop_ocv_updates);
+
+int pm8921_bms_start_ocv_updates(struct pm8921_bms_chip *chip)
+{
+	pr_debug("stopping ocv updates\n");
+	return pm_bms_masked_write(chip, BMS_TOLERANCES,
+			OCV_TOL_MASK, OCV_TOL_DEFAULT);
+}
+EXPORT_SYMBOL_GPL(pm8921_bms_start_ocv_updates);
+
+static irqreturn_t pm8921_bms_sbi_write_ok_handler(int irq, void *data)
+{
+	pr_debug("irq = %d triggered", irq);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8921_bms_cc_thr_handler(int irq, void *data)
+{
+	pr_debug("irq = %d triggered", irq);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8921_bms_vsense_thr_handler(int irq, void *data)
+{
+	pr_debug("irq = %d triggered", irq);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8921_bms_vsense_for_r_handler(int irq, void *data)
+{
+	pr_debug("irq = %d triggered", irq);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8921_bms_ocv_for_r_handler(int irq, void *data)
+{
+	struct pm8921_bms_chip *chip = data;
+
+	pr_debug("irq = %d triggered", irq);
+	schedule_work(&chip->calib_hkadc_work);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8921_bms_good_ocv_handler(int irq, void *data)
+{
+	struct pm8921_bms_chip *chip = data;
+
+	pr_debug("irq = %d triggered", irq);
+	schedule_work(&chip->calib_hkadc_work);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8921_bms_vsense_avg_handler(int irq, void *data)
+{
+	pr_debug("irq = %d triggered", irq);
+	return IRQ_HANDLED;
+}
+
+struct pm_bms_irq_init_data {
+	unsigned int	irq_id;
+	char		*name;
+	unsigned long	flags;
+	irqreturn_t	(*handler)(int, void *);
+};
+
+#define BMS_IRQ(_id, _flags, _handler) \
+{ \
+	.irq_id		= _id, \
+	.name		= #_id, \
+	.flags		= _flags, \
+	.handler	= _handler, \
+}
+
+struct pm_bms_irq_init_data bms_irq_data[] = {
+	BMS_IRQ(PM8921_BMS_SBI_WRITE_OK, IRQF_TRIGGER_RISING,
+				pm8921_bms_sbi_write_ok_handler),
+	BMS_IRQ(PM8921_BMS_CC_THR, IRQF_TRIGGER_RISING,
+				pm8921_bms_cc_thr_handler),
+	BMS_IRQ(PM8921_BMS_VSENSE_THR, IRQF_TRIGGER_RISING,
+				pm8921_bms_vsense_thr_handler),
+	BMS_IRQ(PM8921_BMS_VSENSE_FOR_R, IRQF_TRIGGER_RISING,
+				pm8921_bms_vsense_for_r_handler),
+	BMS_IRQ(PM8921_BMS_OCV_FOR_R, IRQF_TRIGGER_RISING,
+				pm8921_bms_ocv_for_r_handler),
+	BMS_IRQ(PM8921_BMS_GOOD_OCV, IRQF_TRIGGER_RISING,
+				pm8921_bms_good_ocv_handler),
+	BMS_IRQ(PM8921_BMS_VSENSE_AVG, IRQF_TRIGGER_RISING,
+				pm8921_bms_vsense_avg_handler),
+};
+
+static void free_irqs(struct pm8921_bms_chip *chip)
+{
+	int i;
+
+	for (i = 0; i < PM_BMS_MAX_INTS; i++)
+		if (chip->pmic_bms_irq[i]) {
+			free_irq(chip->pmic_bms_irq[i], NULL);
+			chip->pmic_bms_irq[i] = 0;
+		}
+}
+
+static int __devinit request_irqs(struct pm8921_bms_chip *chip,
+					struct platform_device *pdev)
+{
+	struct resource *res;
+	int ret, i;
+
+	ret = 0;
+	bitmap_fill(chip->enabled_irqs, PM_BMS_MAX_INTS);
+
+	for (i = 0; i < ARRAY_SIZE(bms_irq_data); i++) {
+		res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+				bms_irq_data[i].name);
+		if (res == NULL) {
+			pr_err("couldn't find %s\n", bms_irq_data[i].name);
+			goto err_out;
+		}
+		ret = request_irq(res->start, bms_irq_data[i].handler,
+			bms_irq_data[i].flags,
+			bms_irq_data[i].name, chip);
+		if (ret < 0) {
+			pr_err("couldn't request %d (%s) %d\n", res->start,
+					bms_irq_data[i].name, ret);
+			goto err_out;
+		}
+		chip->pmic_bms_irq[bms_irq_data[i].irq_id] = res->start;
+		pm8921_bms_disable_irq(chip, bms_irq_data[i].irq_id);
+	}
+	return 0;
+
+err_out:
+	free_irqs(chip);
+	return -EINVAL;
+}
+
+static int pm8921_bms_suspend(struct device *dev)
+{
+	int rc;
+	struct pm8xxx_adc_chan_result result;
+	struct pm8921_bms_chip *chip = dev_get_drvdata(dev);
+	struct pm8921_soc_params raw;
+	int fcc_uah;
+	int remaining_charge_uah;
+	int cc_uah;
+
+	chip->batt_temp_suspend = 0;
+	rc = pm8xxx_adc_read(chip->batt_temp_channel, &result);
+	if (rc) {
+		pr_err("error reading adc channel = %d, rc = %d\n",
+					chip->batt_temp_channel, rc);
+	}
+	chip->batt_temp_suspend = (int)result.physical;
+
+	mutex_lock(&chip->last_ocv_uv_mutex);
+	read_soc_params_raw(chip, &raw);
+
+	fcc_uah = calculate_fcc_uah(chip,
+			chip->batt_temp_suspend, last_chargecycles);
+	pr_debug("FCC = %uuAh batt_temp = %d, cycles = %d\n",
+			fcc_uah, chip->batt_temp_suspend, last_chargecycles);
+	/* calculate remainging charge */
+	remaining_charge_uah = calculate_remaining_charge_uah(chip, &raw,
+					fcc_uah, chip->batt_temp_suspend,
+					last_chargecycles);
+	pr_debug("RC = %uuAh\n", remaining_charge_uah);
+
+	/* calculate cc micro_volt_hour */
+	calculate_cc_uah(chip, raw.cc, &cc_uah);
+	pr_debug("cc_uah = %duAh raw->cc = %x cc = %lld after subtracting %x\n",
+				cc_uah, raw.cc,
+				(int64_t)raw.cc - chip->cc_reading_at_100,
+				chip->cc_reading_at_100);
+	chip->soc_rbatt_suspend = ((remaining_charge_uah - cc_uah) * 100)
+						/ fcc_uah;
+	mutex_unlock(&chip->last_ocv_uv_mutex);
+
+	return 0;
+}
+
+#define DELTA_RBATT_PERCENT	10
+static int pm8921_bms_resume(struct device *dev)
+{
+	struct pm8921_rbatt_params raw;
+	struct pm8921_bms_chip *chip = dev_get_drvdata(dev);
+	int rbatt;
+	int expected_rbatt;
+	int scalefactor;
+	int delta_rbatt;
+
+	read_rbatt_params_raw(chip, &raw);
+	rbatt = calculate_rbatt_resume(chip, &raw);
+
+	if (rbatt < 0)
+		return 0;
+
+	expected_rbatt
+		= (last_rbatt < 0) ? chip->default_rbatt_mohm : last_rbatt;
+
+	if (chip->rbatt_sf_lut) {
+		scalefactor = interpolate_scalingfactor(chip,
+						chip->rbatt_sf_lut,
+						chip->batt_temp_suspend / 10,
+						chip->soc_rbatt_suspend);
+		rbatt = rbatt * 100 / scalefactor;
+	}
+
+	delta_rbatt = expected_rbatt - rbatt;
+	if (delta_rbatt)
+		delta_rbatt = -delta_rbatt;
+	/*
+	 * only update last_rbatt if rbatt is within some
+	 * percent of expected_rbatt
+	 */
+	if (delta_rbatt * 100 <= DELTA_RBATT_PERCENT * expected_rbatt)
+		last_rbatt = rbatt;
+
+	return 0;
+}
+
+static const struct dev_pm_ops pm8921_pm_ops = {
+	.suspend	= pm8921_bms_suspend,
+	.resume		= pm8921_bms_resume,
+};
+#define EN_BMS_BIT	BIT(7)
+#define EN_PON_HS_BIT	BIT(0)
+static int __devinit pm8921_bms_hw_init(struct pm8921_bms_chip *chip)
+{
+	int rc;
+
+	rc = pm_bms_masked_write(chip, BMS_CONTROL,
+			EN_BMS_BIT | EN_PON_HS_BIT, EN_BMS_BIT | EN_PON_HS_BIT);
+	if (rc) {
+		pr_err("failed to enable pon and bms addr = %d %d",
+				BMS_CONTROL, rc);
+	}
+
+	/* The charger will call start charge later if usb is present */
+	pm_bms_masked_write(chip, BMS_TOLERANCES,
+				IBAT_TOL_MASK, IBAT_TOL_NOCHG);
+	return 0;
+}
+
+static void check_initial_ocv(struct pm8921_bms_chip *chip)
+{
+	int ocv_uv, rc;
+	int16_t ocv_raw;
+	int usb_chg;
+
+	/*
+	 * Check if a ocv is available in bms hw,
+	 * if not compute it here at boot time and save it
+	 * in the last_ocv_uv.
+	 */
+	ocv_uv = 0;
+	pm_bms_read_output_data(chip, LAST_GOOD_OCV_VALUE, &ocv_raw);
+	usb_chg = usb_chg_plugged_in();
+	rc = convert_vbatt_raw_to_uv(chip, usb_chg, ocv_raw, &ocv_uv);
+	if (rc || ocv_uv == 0) {
+		rc = adc_based_ocv(chip, &ocv_uv);
+		if (rc) {
+			pr_err("failed to read adc based ocv_uv rc = %d\n", rc);
+			ocv_uv = DEFAULT_OCV_MICROVOLTS;
+		}
+	}
+	chip->last_ocv_uv = ocv_uv;
+	pr_debug("ocv_uv = %d last_ocv_uv = %d\n", ocv_uv, chip->last_ocv_uv);
+}
+
+static int64_t read_battery_id(struct pm8921_bms_chip *chip)
+{
+	int rc;
+	struct pm8xxx_adc_chan_result result;
+
+	rc = pm8xxx_adc_read(chip->batt_id_channel, &result);
+	if (rc) {
+		pr_err("error reading batt id channel = %d, rc = %d\n",
+					chip->vbat_channel, rc);
+		return rc;
+	}
+	pr_debug("batt_id phy = %lld meas = 0x%llx\n", result.physical,
+						result.measurement);
+	return result.adc_code;
+}
+
+#define PALLADIUM_ID_MIN	0x7F40
+#define PALLADIUM_ID_MAX	0x7F5A
+#define DESAY_5200_ID_MIN	0x7F7F
+#define DESAY_5200_ID_MAX	0x802F
+static int set_battery_data(struct pm8921_bms_chip *chip)
+{
+	int64_t battery_id;
+
+	if (chip->batt_type == BATT_DESAY)
+		goto desay;
+	else if (chip->batt_type == BATT_PALLADIUM)
+		goto palladium;
+
+	battery_id = read_battery_id(chip);
+	if (battery_id < 0) {
+		pr_err("cannot read battery id err = %lld\n", battery_id);
+		return battery_id;
+	}
+
+	if (is_between(PALLADIUM_ID_MIN, PALLADIUM_ID_MAX, battery_id)) {
+		goto palladium;
+	} else if (is_between(DESAY_5200_ID_MIN, DESAY_5200_ID_MAX,
+				battery_id)) {
+		goto desay;
+	} else {
+		pr_warn("invalid battid, palladium 1500 assumed batt_id %llx\n",
+				battery_id);
+		goto palladium;
+	}
+
+palladium:
+		chip->fcc = palladium_1500_data.fcc;
+		chip->fcc_temp_lut = palladium_1500_data.fcc_temp_lut;
+		chip->fcc_sf_lut = palladium_1500_data.fcc_sf_lut;
+		chip->pc_temp_ocv_lut = palladium_1500_data.pc_temp_ocv_lut;
+		chip->pc_sf_lut = palladium_1500_data.pc_sf_lut;
+		chip->rbatt_sf_lut = palladium_1500_data.rbatt_sf_lut;
+		chip->default_rbatt_mohm
+				= palladium_1500_data.default_rbatt_mohm;
+		chip->delta_rbatt_mohm = palladium_1500_data.delta_rbatt_mohm;
+		return 0;
+desay:
+		chip->fcc = desay_5200_data.fcc;
+		chip->fcc_temp_lut = desay_5200_data.fcc_temp_lut;
+		chip->pc_temp_ocv_lut = desay_5200_data.pc_temp_ocv_lut;
+		chip->pc_sf_lut = desay_5200_data.pc_sf_lut;
+		chip->rbatt_sf_lut = desay_5200_data.rbatt_sf_lut;
+		chip->default_rbatt_mohm = desay_5200_data.default_rbatt_mohm;
+		chip->delta_rbatt_mohm = desay_5200_data.delta_rbatt_mohm;
+		return 0;
+}
+
+enum bms_request_operation {
+	CALC_RBATT,
+	CALC_FCC,
+	CALC_PC,
+	CALC_SOC,
+	CALIB_HKADC,
+	CALIB_CCADC,
+	GET_VBAT_VSENSE_SIMULTANEOUS,
+	STOP_OCV,
+	START_OCV,
+};
+
+static int test_batt_temp = 5;
+static int test_chargecycle = 150;
+static int test_ocv = 3900000;
+enum {
+	TEST_BATT_TEMP,
+	TEST_CHARGE_CYCLE,
+	TEST_OCV,
+};
+static int get_test_param(void *data, u64 * val)
+{
+	switch ((int)data) {
+	case TEST_BATT_TEMP:
+		*val = test_batt_temp;
+		break;
+	case TEST_CHARGE_CYCLE:
+		*val = test_chargecycle;
+		break;
+	case TEST_OCV:
+		*val = test_ocv;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+static int set_test_param(void *data, u64  val)
+{
+	switch ((int)data) {
+	case TEST_BATT_TEMP:
+		test_batt_temp = (int)val;
+		break;
+	case TEST_CHARGE_CYCLE:
+		test_chargecycle = (int)val;
+		break;
+	case TEST_OCV:
+		test_ocv = (int)val;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(temp_fops, get_test_param, set_test_param, "%llu\n");
+
+static int get_calc(void *data, u64 * val)
+{
+	int param = (int)data;
+	int ret = 0;
+	int ibat_ua, vbat_uv;
+	struct pm8921_soc_params raw;
+	struct pm8921_rbatt_params rraw;
+
+	read_soc_params_raw(the_chip, &raw);
+	read_rbatt_params_raw(the_chip, &rraw);
+
+	*val = 0;
+
+	/* global irq number passed in via data */
+	switch (param) {
+	case CALC_RBATT:
+		*val = calculate_rbatt_resume(the_chip, &rraw);
+		break;
+	case CALC_FCC:
+		*val = calculate_fcc_uah(the_chip, test_batt_temp,
+							test_chargecycle);
+		break;
+	case CALC_PC:
+		*val = calculate_pc(the_chip, test_ocv, test_batt_temp,
+							test_chargecycle);
+		break;
+	case CALC_SOC:
+		*val = calculate_state_of_charge(the_chip, &raw,
+					test_batt_temp, test_chargecycle);
+		break;
+	case CALIB_HKADC:
+		/* reading this will trigger calibration */
+		*val = 0;
+		calib_hkadc(the_chip);
+		break;
+	case CALIB_CCADC:
+		/* reading this will trigger calibration */
+		*val = 0;
+		pm8xxx_calib_ccadc();
+		break;
+	case GET_VBAT_VSENSE_SIMULTANEOUS:
+		/* reading this will call simultaneous vbat and vsense */
+		*val =
+		pm8921_bms_get_simultaneous_battery_voltage_and_current(
+			&ibat_ua,
+			&vbat_uv);
+	default:
+		ret = -EINVAL;
+	}
+	return ret;
+}
+
+static int set_calc(void *data, u64 val)
+{
+	int param = (int)data;
+	int ret = 0;
+
+	switch (param) {
+	case STOP_OCV:
+		pm8921_bms_stop_ocv_updates(the_chip);
+		break;
+	case START_OCV:
+		pm8921_bms_start_ocv_updates(the_chip);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	return ret;
+}
+DEFINE_SIMPLE_ATTRIBUTE(calc_fops, get_calc, set_calc, "%llu\n");
+
+static int get_reading(void *data, u64 * val)
+{
+	int param = (int)data;
+	int ret = 0;
+	struct pm8921_soc_params raw;
+	struct pm8921_rbatt_params rraw;
+
+	read_soc_params_raw(the_chip, &raw);
+	read_rbatt_params_raw(the_chip, &rraw);
+
+	*val = 0;
+
+	switch (param) {
+	case CC_MSB:
+	case CC_LSB:
+		*val = raw.cc;
+		break;
+	case LAST_GOOD_OCV_VALUE:
+		*val = raw.last_good_ocv_uv;
+		break;
+	case VBATT_FOR_RBATT:
+		*val = rraw.vbatt_for_rbatt_uv;
+		break;
+	case VSENSE_FOR_RBATT:
+		*val = rraw.vsense_for_rbatt_uv;
+		break;
+	case OCV_FOR_RBATT:
+		*val = rraw.ocv_for_rbatt_uv;
+		break;
+	case VSENSE_AVG:
+		read_vsense_avg(the_chip, (uint *)val);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	return ret;
+}
+DEFINE_SIMPLE_ATTRIBUTE(reading_fops, get_reading, NULL, "%lld\n");
+
+static int get_rt_status(void *data, u64 * val)
+{
+	int i = (int)data;
+	int ret;
+
+	/* global irq number passed in via data */
+	ret = pm_bms_get_rt_status(the_chip, i);
+	*val = ret;
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(rt_fops, get_rt_status, NULL, "%llu\n");
+
+static int get_reg(void *data, u64 * val)
+{
+	int addr = (int)data;
+	int ret;
+	u8 temp;
+
+	ret = pm8xxx_readb(the_chip->dev->parent, addr, &temp);
+	if (ret) {
+		pr_err("pm8xxx_readb to %x value = %d errored = %d\n",
+			addr, temp, ret);
+		return -EAGAIN;
+	}
+	*val = temp;
+	return 0;
+}
+
+static int set_reg(void *data, u64 val)
+{
+	int addr = (int)data;
+	int ret;
+	u8 temp;
+
+	temp = (u8) val;
+	ret = pm8xxx_writeb(the_chip->dev->parent, addr, temp);
+	if (ret) {
+		pr_err("pm8xxx_writeb to %x value = %d errored = %d\n",
+			addr, temp, ret);
+		return -EAGAIN;
+	}
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_reg, set_reg, "0x%02llx\n");
+
+static void create_debugfs_entries(struct pm8921_bms_chip *chip)
+{
+	int i;
+
+	chip->dent = debugfs_create_dir("pm8921-bms", NULL);
+
+	if (IS_ERR(chip->dent)) {
+		pr_err("pmic bms couldnt create debugfs dir\n");
+		return;
+	}
+
+	debugfs_create_file("BMS_CONTROL", 0644, chip->dent,
+			(void *)BMS_CONTROL, &reg_fops);
+	debugfs_create_file("BMS_OUTPUT0", 0644, chip->dent,
+			(void *)BMS_OUTPUT0, &reg_fops);
+	debugfs_create_file("BMS_OUTPUT1", 0644, chip->dent,
+			(void *)BMS_OUTPUT1, &reg_fops);
+	debugfs_create_file("BMS_TEST1", 0644, chip->dent,
+			(void *)BMS_TEST1, &reg_fops);
+
+	debugfs_create_file("test_batt_temp", 0644, chip->dent,
+				(void *)TEST_BATT_TEMP, &temp_fops);
+	debugfs_create_file("test_chargecycle", 0644, chip->dent,
+				(void *)TEST_CHARGE_CYCLE, &temp_fops);
+	debugfs_create_file("test_ocv", 0644, chip->dent,
+				(void *)TEST_OCV, &temp_fops);
+
+	debugfs_create_file("read_cc", 0644, chip->dent,
+				(void *)CC_MSB, &reading_fops);
+	debugfs_create_file("read_last_good_ocv", 0644, chip->dent,
+				(void *)LAST_GOOD_OCV_VALUE, &reading_fops);
+	debugfs_create_file("read_vbatt_for_rbatt", 0644, chip->dent,
+				(void *)VBATT_FOR_RBATT, &reading_fops);
+	debugfs_create_file("read_vsense_for_rbatt", 0644, chip->dent,
+				(void *)VSENSE_FOR_RBATT, &reading_fops);
+	debugfs_create_file("read_ocv_for_rbatt", 0644, chip->dent,
+				(void *)OCV_FOR_RBATT, &reading_fops);
+	debugfs_create_file("read_vsense_avg", 0644, chip->dent,
+				(void *)VSENSE_AVG, &reading_fops);
+
+	debugfs_create_file("show_rbatt", 0644, chip->dent,
+				(void *)CALC_RBATT, &calc_fops);
+	debugfs_create_file("show_fcc", 0644, chip->dent,
+				(void *)CALC_FCC, &calc_fops);
+	debugfs_create_file("show_pc", 0644, chip->dent,
+				(void *)CALC_PC, &calc_fops);
+	debugfs_create_file("show_soc", 0644, chip->dent,
+				(void *)CALC_SOC, &calc_fops);
+	debugfs_create_file("calib_hkadc", 0644, chip->dent,
+				(void *)CALIB_HKADC, &calc_fops);
+	debugfs_create_file("calib_ccadc", 0644, chip->dent,
+				(void *)CALIB_CCADC, &calc_fops);
+	debugfs_create_file("stop_ocv", 0644, chip->dent,
+				(void *)STOP_OCV, &calc_fops);
+	debugfs_create_file("start_ocv", 0644, chip->dent,
+				(void *)START_OCV, &calc_fops);
+
+	debugfs_create_file("simultaneous", 0644, chip->dent,
+			(void *)GET_VBAT_VSENSE_SIMULTANEOUS, &calc_fops);
+
+	for (i = 0; i < ARRAY_SIZE(bms_irq_data); i++) {
+		if (chip->pmic_bms_irq[bms_irq_data[i].irq_id])
+			debugfs_create_file(bms_irq_data[i].name, 0444,
+				chip->dent,
+				(void *)bms_irq_data[i].irq_id,
+				&rt_fops);
+	}
+}
+
+#define REG_SBI_CONFIG		0x04F
+#define PAGE3_ENABLE_MASK	0x6
+#define PROGRAM_REV_MASK	0x0F
+#define PROGRAM_REV		0x9
+static int read_ocv_trim(struct pm8921_bms_chip *chip)
+{
+	int rc;
+	u8 reg, sbi_config;
+
+	rc = pm8xxx_readb(chip->dev->parent, REG_SBI_CONFIG, &sbi_config);
+	if (rc) {
+		pr_err("error = %d reading sbi config reg\n", rc);
+		return rc;
+	}
+
+	reg = sbi_config | PAGE3_ENABLE_MASK;
+	rc = pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, reg);
+	if (rc) {
+		pr_err("error = %d writing sbi config reg\n", rc);
+		return rc;
+	}
+
+	rc = pm8xxx_readb(chip->dev->parent, TEST_PROGRAM_REV, &reg);
+	if (rc)
+		pr_err("Error %d reading %d addr %d\n",
+			rc, reg, TEST_PROGRAM_REV);
+	pr_err("program rev reg is 0x%x\n", reg);
+	reg &= PROGRAM_REV_MASK;
+
+	/* If the revision is equal or higher do not adjust trim delta */
+	if (reg >= PROGRAM_REV) {
+		chip->amux_2_trim_delta = 0;
+		goto restore_sbi_config;
+	}
+
+	rc = pm8xxx_readb(chip->dev->parent, AMUX_TRIM_2, &reg);
+	if (rc) {
+		pr_err("error = %d reading trim reg\n", rc);
+		return rc;
+	}
+
+	pr_err("trim reg is 0x%x\n", reg);
+	chip->amux_2_trim_delta = abs(0x49 - reg);
+	pr_err("trim delta is %d\n", chip->amux_2_trim_delta);
+
+restore_sbi_config:
+	rc = pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, sbi_config);
+	if (rc) {
+		pr_err("error = %d writing sbi config reg\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int __devinit pm8921_bms_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	int vbatt;
+	struct pm8921_bms_chip *chip;
+	const struct pm8921_bms_platform_data *pdata
+				= pdev->dev.platform_data;
+
+	if (!pdata) {
+		pr_err("missing platform data\n");
+		return -EINVAL;
+	}
+
+	chip = kzalloc(sizeof(struct pm8921_bms_chip), GFP_KERNEL);
+	if (!chip) {
+		pr_err("Cannot allocate pm_bms_chip\n");
+		return -ENOMEM;
+	}
+
+	mutex_init(&chip->bms_output_lock);
+	mutex_init(&chip->last_ocv_uv_mutex);
+	chip->dev = &pdev->dev;
+	chip->r_sense = pdata->r_sense;
+	chip->i_test = pdata->i_test;
+	chip->v_failure = pdata->v_failure;
+	chip->calib_delay_ms = pdata->calib_delay_ms;
+	chip->max_voltage_uv = pdata->max_voltage_uv;
+	chip->batt_type = pdata->battery_type;
+	chip->rconn_mohm = pdata->rconn_mohm;
+	chip->start_percent = -EINVAL;
+	chip->end_percent = -EINVAL;
+	rc = set_battery_data(chip);
+	if (rc) {
+		pr_err("%s bad battery data %d\n", __func__, rc);
+		goto free_chip;
+	}
+
+	if (chip->pc_temp_ocv_lut == NULL) {
+		pr_err("temp ocv lut table is NULL\n");
+		rc = -EINVAL;
+		goto free_chip;
+	}
+
+	/* set defaults in the battery data */
+	if (chip->default_rbatt_mohm <= 0)
+		chip->default_rbatt_mohm = DEFAULT_RBATT_MOHMS;
+
+	chip->batt_temp_channel = pdata->bms_cdata.batt_temp_channel;
+	chip->vbat_channel = pdata->bms_cdata.vbat_channel;
+	chip->ref625mv_channel = pdata->bms_cdata.ref625mv_channel;
+	chip->ref1p25v_channel = pdata->bms_cdata.ref1p25v_channel;
+	chip->batt_id_channel = pdata->bms_cdata.batt_id_channel;
+	chip->revision = pm8xxx_get_revision(chip->dev->parent);
+	chip->enable_fcc_learning = pdata->enable_fcc_learning;
+	INIT_WORK(&chip->calib_hkadc_work, calibrate_hkadc_work);
+
+	rc = request_irqs(chip, pdev);
+	if (rc) {
+		pr_err("couldn't register interrupts rc = %d\n", rc);
+		goto free_chip;
+	}
+
+	rc = pm8921_bms_hw_init(chip);
+	if (rc) {
+		pr_err("couldn't init hardware rc = %d\n", rc);
+		goto free_irqs;
+	}
+
+	platform_set_drvdata(pdev, chip);
+	the_chip = chip;
+	create_debugfs_entries(chip);
+
+	rc = read_ocv_trim(chip);
+	if (rc) {
+		pr_err("couldn't adjust ocv_trim rc= %d\n", rc);
+		goto free_irqs;
+	}
+	check_initial_ocv(chip);
+
+	INIT_DELAYED_WORK(&chip->calib_ccadc_work, calibrate_ccadc_work);
+	/* begin calibration only on chips > 2.0 */
+	if (chip->revision >= PM8XXX_REVISION_8921_2p0)
+		schedule_delayed_work(&chip->calib_ccadc_work, 0);
+
+	/* initial hkadc calibration */
+	schedule_work(&chip->calib_hkadc_work);
+	/* enable the vbatt reading interrupts for scheduling hkadc calib */
+	pm8921_bms_enable_irq(chip, PM8921_BMS_GOOD_OCV);
+	pm8921_bms_enable_irq(chip, PM8921_BMS_OCV_FOR_R);
+
+	get_battery_uvolts(chip, &vbatt);
+	pr_info("OK battery_capacity_at_boot=%d volt = %d ocv = %d\n",
+				pm8921_bms_get_percent_charge(),
+				vbatt, chip->last_ocv_uv);
+	return 0;
+
+free_irqs:
+	free_irqs(chip);
+free_chip:
+	kfree(chip);
+	return rc;
+}
+
+static int __devexit pm8921_bms_remove(struct platform_device *pdev)
+{
+	struct pm8921_bms_chip *chip = platform_get_drvdata(pdev);
+
+	free_irqs(chip);
+	kfree(chip->adjusted_fcc_temp_lut);
+	platform_set_drvdata(pdev, NULL);
+	the_chip = NULL;
+	kfree(chip);
+	return 0;
+}
+
+static struct platform_driver pm8921_bms_driver = {
+	.probe	= pm8921_bms_probe,
+	.remove	= __devexit_p(pm8921_bms_remove),
+	.driver	= {
+		.name	= PM8921_BMS_DEV_NAME,
+		.owner	= THIS_MODULE,
+		.pm	= &pm8921_pm_ops
+	},
+};
+
+static int __init pm8921_bms_init(void)
+{
+	return platform_driver_register(&pm8921_bms_driver);
+}
+
+static void __exit pm8921_bms_exit(void)
+{
+	platform_driver_unregister(&pm8921_bms_driver);
+}
+
+late_initcall(pm8921_bms_init);
+module_exit(pm8921_bms_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC8921 bms driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8921_BMS_DEV_NAME);
diff --git a/drivers/power/pm8921-charger.c b/drivers/power/pm8921-charger.c
new file mode 100644
index 0000000..c983389
--- /dev/null
+++ b/drivers/power/pm8921-charger.c
@@ -0,0 +1,3973 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#define pr_fmt(fmt)	"%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/mfd/pm8xxx/pm8921-charger.h>
+#include <linux/mfd/pm8xxx/pm8921-bms.h>
+#include <linux/mfd/pm8xxx/pm8xxx-adc.h>
+#include <linux/mfd/pm8xxx/ccadc.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/workqueue.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+
+#include <mach/msm_xo.h>
+#include <mach/msm_hsusb.h>
+
+#define CHG_BUCK_CLOCK_CTRL	0x14
+
+#define PBL_ACCESS1		0x04
+#define PBL_ACCESS2		0x05
+#define SYS_CONFIG_1		0x06
+#define SYS_CONFIG_2		0x07
+#define CHG_CNTRL		0x204
+#define CHG_IBAT_MAX		0x205
+#define CHG_TEST		0x206
+#define CHG_BUCK_CTRL_TEST1	0x207
+#define CHG_BUCK_CTRL_TEST2	0x208
+#define CHG_BUCK_CTRL_TEST3	0x209
+#define COMPARATOR_OVERRIDE	0x20A
+#define PSI_TXRX_SAMPLE_DATA_0	0x20B
+#define PSI_TXRX_SAMPLE_DATA_1	0x20C
+#define PSI_TXRX_SAMPLE_DATA_2	0x20D
+#define PSI_TXRX_SAMPLE_DATA_3	0x20E
+#define PSI_CONFIG_STATUS	0x20F
+#define CHG_IBAT_SAFE		0x210
+#define CHG_ITRICKLE		0x211
+#define CHG_CNTRL_2		0x212
+#define CHG_VBAT_DET		0x213
+#define CHG_VTRICKLE		0x214
+#define CHG_ITERM		0x215
+#define CHG_CNTRL_3		0x216
+#define CHG_VIN_MIN		0x217
+#define CHG_TWDOG		0x218
+#define CHG_TTRKL_MAX		0x219
+#define CHG_TEMP_THRESH		0x21A
+#define CHG_TCHG_MAX		0x21B
+#define USB_OVP_CONTROL		0x21C
+#define DC_OVP_CONTROL		0x21D
+#define USB_OVP_TEST		0x21E
+#define DC_OVP_TEST		0x21F
+#define CHG_VDD_MAX		0x220
+#define CHG_VDD_SAFE		0x221
+#define CHG_VBAT_BOOT_THRESH	0x222
+#define USB_OVP_TRIM		0x355
+#define BUCK_CONTROL_TRIM1	0x356
+#define BUCK_CONTROL_TRIM2	0x357
+#define BUCK_CONTROL_TRIM3	0x358
+#define BUCK_CONTROL_TRIM4	0x359
+#define CHG_DEFAULTS_TRIM	0x35A
+#define CHG_ITRIM		0x35B
+#define CHG_TTRIM		0x35C
+#define CHG_COMP_OVR		0x20A
+#define IUSB_FINE_RES		0x2B6
+
+/* check EOC every 10 seconds */
+#define EOC_CHECK_PERIOD_MS	10000
+/* check for USB unplug every 200 msecs */
+#define UNPLUG_CHECK_WAIT_PERIOD_MS 200
+
+enum chg_fsm_state {
+	FSM_STATE_OFF_0 = 0,
+	FSM_STATE_BATFETDET_START_12 = 12,
+	FSM_STATE_BATFETDET_END_16 = 16,
+	FSM_STATE_ON_CHG_HIGHI_1 = 1,
+	FSM_STATE_ATC_2A = 2,
+	FSM_STATE_ATC_2B = 18,
+	FSM_STATE_ON_BAT_3 = 3,
+	FSM_STATE_ATC_FAIL_4 = 4 ,
+	FSM_STATE_DELAY_5 = 5,
+	FSM_STATE_ON_CHG_AND_BAT_6 = 6,
+	FSM_STATE_FAST_CHG_7 = 7,
+	FSM_STATE_TRKL_CHG_8 = 8,
+	FSM_STATE_CHG_FAIL_9 = 9,
+	FSM_STATE_EOC_10 = 10,
+	FSM_STATE_ON_CHG_VREGOK_11 = 11,
+	FSM_STATE_ATC_PAUSE_13 = 13,
+	FSM_STATE_FAST_CHG_PAUSE_14 = 14,
+	FSM_STATE_TRKL_CHG_PAUSE_15 = 15,
+	FSM_STATE_START_BOOT = 20,
+	FSM_STATE_FLCB_VREGOK = 21,
+	FSM_STATE_FLCB = 22,
+};
+
+struct fsm_state_to_batt_status {
+	enum chg_fsm_state	fsm_state;
+	int			batt_state;
+};
+
+static struct fsm_state_to_batt_status map[] = {
+	{FSM_STATE_OFF_0, POWER_SUPPLY_STATUS_UNKNOWN},
+	{FSM_STATE_BATFETDET_START_12, POWER_SUPPLY_STATUS_UNKNOWN},
+	{FSM_STATE_BATFETDET_END_16, POWER_SUPPLY_STATUS_UNKNOWN},
+	/*
+	 * for CHG_HIGHI_1 report NOT_CHARGING if battery missing,
+	 * too hot/cold, charger too hot
+	 */
+	{FSM_STATE_ON_CHG_HIGHI_1, POWER_SUPPLY_STATUS_FULL},
+	{FSM_STATE_ATC_2A, POWER_SUPPLY_STATUS_CHARGING},
+	{FSM_STATE_ATC_2B, POWER_SUPPLY_STATUS_CHARGING},
+	{FSM_STATE_ON_BAT_3, POWER_SUPPLY_STATUS_DISCHARGING},
+	{FSM_STATE_ATC_FAIL_4, POWER_SUPPLY_STATUS_DISCHARGING},
+	{FSM_STATE_DELAY_5, POWER_SUPPLY_STATUS_UNKNOWN },
+	{FSM_STATE_ON_CHG_AND_BAT_6, POWER_SUPPLY_STATUS_CHARGING},
+	{FSM_STATE_FAST_CHG_7, POWER_SUPPLY_STATUS_CHARGING},
+	{FSM_STATE_TRKL_CHG_8, POWER_SUPPLY_STATUS_CHARGING},
+	{FSM_STATE_CHG_FAIL_9, POWER_SUPPLY_STATUS_DISCHARGING},
+	{FSM_STATE_EOC_10, POWER_SUPPLY_STATUS_FULL},
+	{FSM_STATE_ON_CHG_VREGOK_11, POWER_SUPPLY_STATUS_NOT_CHARGING},
+	{FSM_STATE_ATC_PAUSE_13, POWER_SUPPLY_STATUS_NOT_CHARGING},
+	{FSM_STATE_FAST_CHG_PAUSE_14, POWER_SUPPLY_STATUS_NOT_CHARGING},
+	{FSM_STATE_TRKL_CHG_PAUSE_15, POWER_SUPPLY_STATUS_NOT_CHARGING},
+	{FSM_STATE_START_BOOT, POWER_SUPPLY_STATUS_NOT_CHARGING},
+	{FSM_STATE_FLCB_VREGOK, POWER_SUPPLY_STATUS_NOT_CHARGING},
+	{FSM_STATE_FLCB, POWER_SUPPLY_STATUS_NOT_CHARGING},
+};
+
+enum chg_regulation_loop {
+	VDD_LOOP = BIT(3),
+	BAT_CURRENT_LOOP = BIT(2),
+	INPUT_CURRENT_LOOP = BIT(1),
+	INPUT_VOLTAGE_LOOP = BIT(0),
+	CHG_ALL_LOOPS = VDD_LOOP | BAT_CURRENT_LOOP
+			| INPUT_CURRENT_LOOP | INPUT_VOLTAGE_LOOP,
+};
+
+enum pmic_chg_interrupts {
+	USBIN_VALID_IRQ = 0,
+	USBIN_OV_IRQ,
+	BATT_INSERTED_IRQ,
+	VBATDET_LOW_IRQ,
+	USBIN_UV_IRQ,
+	VBAT_OV_IRQ,
+	CHGWDOG_IRQ,
+	VCP_IRQ,
+	ATCDONE_IRQ,
+	ATCFAIL_IRQ,
+	CHGDONE_IRQ,
+	CHGFAIL_IRQ,
+	CHGSTATE_IRQ,
+	LOOP_CHANGE_IRQ,
+	FASTCHG_IRQ,
+	TRKLCHG_IRQ,
+	BATT_REMOVED_IRQ,
+	BATTTEMP_HOT_IRQ,
+	CHGHOT_IRQ,
+	BATTTEMP_COLD_IRQ,
+	CHG_GONE_IRQ,
+	BAT_TEMP_OK_IRQ,
+	COARSE_DET_LOW_IRQ,
+	VDD_LOOP_IRQ,
+	VREG_OV_IRQ,
+	VBATDET_IRQ,
+	BATFET_IRQ,
+	PSI_IRQ,
+	DCIN_VALID_IRQ,
+	DCIN_OV_IRQ,
+	DCIN_UV_IRQ,
+	PM_CHG_MAX_INTS,
+};
+
+struct bms_notify {
+	int			is_battery_full;
+	int			is_charging;
+	struct	work_struct	work;
+};
+
+/**
+ * struct pm8921_chg_chip -device information
+ * @dev:			device pointer to access the parent
+ * @usb_present:		present status of usb
+ * @dc_present:			present status of dc
+ * @usb_charger_current:	usb current to charge the battery with used when
+ *				the usb path is enabled or charging is resumed
+ * @safety_time:		max time for which charging will happen
+ * @update_time:		how frequently the userland needs to be updated
+ * @max_voltage_mv:		the max volts the batt should be charged up to
+ * @min_voltage_mv:		the min battery voltage before turning the FETon
+ * @cool_temp_dc:		the cool temp threshold in deciCelcius
+ * @warm_temp_dc:		the warm temp threshold in deciCelcius
+ * @resume_voltage_delta:	the voltage delta from vdd max at which the
+ *				battery should resume charging
+ * @term_current:		The charging based term current
+ *
+ */
+struct pm8921_chg_chip {
+	struct device			*dev;
+	unsigned int			usb_present;
+	unsigned int			dc_present;
+	unsigned int			usb_charger_current;
+	unsigned int			max_bat_chg_current;
+	unsigned int			pmic_chg_irq[PM_CHG_MAX_INTS];
+	unsigned int			safety_time;
+	unsigned int			ttrkl_time;
+	unsigned int			update_time;
+	unsigned int			max_voltage_mv;
+	unsigned int			min_voltage_mv;
+	int				cool_temp_dc;
+	int				warm_temp_dc;
+	unsigned int			temp_check_period;
+	unsigned int			cool_bat_chg_current;
+	unsigned int			warm_bat_chg_current;
+	unsigned int			cool_bat_voltage;
+	unsigned int			warm_bat_voltage;
+	unsigned int			is_bat_cool;
+	unsigned int			is_bat_warm;
+	unsigned int			resume_voltage_delta;
+	unsigned int			term_current;
+	unsigned int			vbat_channel;
+	unsigned int			batt_temp_channel;
+	unsigned int			batt_id_channel;
+	struct power_supply		usb_psy;
+	struct power_supply		dc_psy;
+	struct power_supply		*ext_psy;
+	struct power_supply		batt_psy;
+	struct dentry			*dent;
+	struct bms_notify		bms_notify;
+	bool				keep_btm_on_suspend;
+	bool				ext_charging;
+	bool				ext_charge_done;
+	bool				iusb_fine_res;
+	DECLARE_BITMAP(enabled_irqs, PM_CHG_MAX_INTS);
+	struct work_struct		battery_id_valid_work;
+	int64_t				batt_id_min;
+	int64_t				batt_id_max;
+	int				trkl_voltage;
+	int				weak_voltage;
+	int				trkl_current;
+	int				weak_current;
+	int				vin_min;
+	unsigned int			*thermal_mitigation;
+	int				thermal_levels;
+	struct delayed_work		update_heartbeat_work;
+	struct delayed_work		eoc_work;
+	struct delayed_work		unplug_check_work;
+	struct delayed_work		vin_collapse_check_work;
+	struct wake_lock		eoc_wake_lock;
+	enum pm8921_chg_cold_thr	cold_thr;
+	enum pm8921_chg_hot_thr		hot_thr;
+	int				rconn_mohm;
+	enum pm8921_chg_led_src_config	led_src_config;
+};
+
+/* user space parameter to limit usb current */
+static unsigned int usb_max_current;
+/*
+ * usb_target_ma is used for wall charger
+ * adaptive input current limiting only. Use
+ * pm_iusbmax_get() to get current maximum usb current setting.
+ */
+static int usb_target_ma;
+static int charging_disabled;
+static int thermal_mitigation;
+
+static struct pm8921_chg_chip *the_chip;
+
+static struct pm8xxx_adc_arb_btm_param btm_config;
+
+static int pm_chg_masked_write(struct pm8921_chg_chip *chip, u16 addr,
+							u8 mask, u8 val)
+{
+	int rc;
+	u8 reg;
+
+	rc = pm8xxx_readb(chip->dev->parent, addr, &reg);
+	if (rc) {
+		pr_err("pm8xxx_readb failed: addr=%03X, rc=%d\n", addr, rc);
+		return rc;
+	}
+	reg &= ~mask;
+	reg |= val & mask;
+	rc = pm8xxx_writeb(chip->dev->parent, addr, reg);
+	if (rc) {
+		pr_err("pm8xxx_writeb failed: addr=%03X, rc=%d\n", addr, rc);
+		return rc;
+	}
+	return 0;
+}
+
+static int pm_chg_get_rt_status(struct pm8921_chg_chip *chip, int irq_id)
+{
+	return pm8xxx_read_irq_stat(chip->dev->parent,
+					chip->pmic_chg_irq[irq_id]);
+}
+
+/* Treat OverVoltage/UnderVoltage as source missing */
+static int is_usb_chg_plugged_in(struct pm8921_chg_chip *chip)
+{
+	return pm_chg_get_rt_status(chip, USBIN_VALID_IRQ);
+}
+
+/* Treat OverVoltage/UnderVoltage as source missing */
+static int is_dc_chg_plugged_in(struct pm8921_chg_chip *chip)
+{
+	return pm_chg_get_rt_status(chip, DCIN_VALID_IRQ);
+}
+
+#define CAPTURE_FSM_STATE_CMD	0xC2
+#define READ_BANK_7		0x70
+#define READ_BANK_4		0x40
+static int pm_chg_get_fsm_state(struct pm8921_chg_chip *chip)
+{
+	u8 temp;
+	int err, ret = 0;
+
+	temp = CAPTURE_FSM_STATE_CMD;
+	err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
+	if (err) {
+		pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
+		return err;
+	}
+
+	temp = READ_BANK_7;
+	err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
+	if (err) {
+		pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
+		return err;
+	}
+
+	err = pm8xxx_readb(chip->dev->parent, CHG_TEST, &temp);
+	if (err) {
+		pr_err("pm8xxx_readb fail: addr=%03X, rc=%d\n", CHG_TEST, err);
+		return err;
+	}
+	/* get the lower 4 bits */
+	ret = temp & 0xF;
+
+	temp = READ_BANK_4;
+	err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
+	if (err) {
+		pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
+		return err;
+	}
+
+	err = pm8xxx_readb(chip->dev->parent, CHG_TEST, &temp);
+	if (err) {
+		pr_err("pm8xxx_readb fail: addr=%03X, rc=%d\n", CHG_TEST, err);
+		return err;
+	}
+	/* get the upper 1 bit */
+	ret |= (temp & 0x1) << 4;
+	return  ret;
+}
+
+#define READ_BANK_6		0x60
+static int pm_chg_get_regulation_loop(struct pm8921_chg_chip *chip)
+{
+	u8 temp;
+	int err;
+
+	temp = READ_BANK_6;
+	err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
+	if (err) {
+		pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
+		return err;
+	}
+
+	err = pm8xxx_readb(chip->dev->parent, CHG_TEST, &temp);
+	if (err) {
+		pr_err("pm8xxx_readb fail: addr=%03X, rc=%d\n", CHG_TEST, err);
+		return err;
+	}
+
+	/* return the lower 4 bits */
+	return temp & CHG_ALL_LOOPS;
+}
+
+#define CHG_USB_SUSPEND_BIT  BIT(2)
+static int pm_chg_usb_suspend_enable(struct pm8921_chg_chip *chip, int enable)
+{
+	return pm_chg_masked_write(chip, CHG_CNTRL_3, CHG_USB_SUSPEND_BIT,
+			enable ? CHG_USB_SUSPEND_BIT : 0);
+}
+
+#define CHG_EN_BIT	BIT(7)
+static int pm_chg_auto_enable(struct pm8921_chg_chip *chip, int enable)
+{
+	return pm_chg_masked_write(chip, CHG_CNTRL_3, CHG_EN_BIT,
+				enable ? CHG_EN_BIT : 0);
+}
+
+#define CHG_FAILED_CLEAR	BIT(0)
+#define ATC_FAILED_CLEAR	BIT(1)
+static int pm_chg_failed_clear(struct pm8921_chg_chip *chip, int clear)
+{
+	int rc;
+
+	rc = pm_chg_masked_write(chip, CHG_CNTRL_3, ATC_FAILED_CLEAR,
+				clear ? ATC_FAILED_CLEAR : 0);
+	rc |= pm_chg_masked_write(chip, CHG_CNTRL_3, CHG_FAILED_CLEAR,
+				clear ? CHG_FAILED_CLEAR : 0);
+	return rc;
+}
+
+#define CHG_CHARGE_DIS_BIT	BIT(1)
+static int pm_chg_charge_dis(struct pm8921_chg_chip *chip, int disable)
+{
+	return pm_chg_masked_write(chip, CHG_CNTRL, CHG_CHARGE_DIS_BIT,
+				disable ? CHG_CHARGE_DIS_BIT : 0);
+}
+
+static int pm_is_chg_charge_dis(struct pm8921_chg_chip *chip)
+{
+	u8 temp;
+
+	pm8xxx_readb(chip->dev->parent, CHG_CNTRL, &temp);
+	return  temp & CHG_CHARGE_DIS_BIT;
+}
+#define PM8921_CHG_V_MIN_MV	3240
+#define PM8921_CHG_V_STEP_MV	20
+#define PM8921_CHG_V_STEP_10MV_OFFSET_BIT	BIT(7)
+#define PM8921_CHG_VDDMAX_MAX	4500
+#define PM8921_CHG_VDDMAX_MIN	3400
+#define PM8921_CHG_V_MASK	0x7F
+static int __pm_chg_vddmax_set(struct pm8921_chg_chip *chip, int voltage)
+{
+	int remainder;
+	u8 temp = 0;
+
+	if (voltage < PM8921_CHG_VDDMAX_MIN
+			|| voltage > PM8921_CHG_VDDMAX_MAX) {
+		pr_err("bad mV=%d asked to set\n", voltage);
+		return -EINVAL;
+	}
+
+	temp = (voltage - PM8921_CHG_V_MIN_MV) / PM8921_CHG_V_STEP_MV;
+
+	remainder = voltage % 20;
+	if (remainder >= 10) {
+		temp |= PM8921_CHG_V_STEP_10MV_OFFSET_BIT;
+	}
+
+	pr_debug("voltage=%d setting %02x\n", voltage, temp);
+	return pm8xxx_writeb(chip->dev->parent, CHG_VDD_MAX, temp);
+}
+
+static int pm_chg_vddmax_get(struct pm8921_chg_chip *chip, int *voltage)
+{
+	u8 temp;
+	int rc;
+
+	rc = pm8xxx_readb(chip->dev->parent, CHG_VDD_MAX, &temp);
+	if (rc) {
+		pr_err("rc = %d while reading vdd max\n", rc);
+		*voltage = 0;
+		return rc;
+	}
+	*voltage = (int)(temp & PM8921_CHG_V_MASK) * PM8921_CHG_V_STEP_MV
+							+ PM8921_CHG_V_MIN_MV;
+	if (temp & PM8921_CHG_V_STEP_10MV_OFFSET_BIT)
+		*voltage =  *voltage + 10;
+	return 0;
+}
+
+static int pm_chg_vddmax_set(struct pm8921_chg_chip *chip, int voltage)
+{
+	int current_mv, ret, steps, i;
+	bool increase;
+
+	ret = 0;
+
+	if (voltage < PM8921_CHG_VDDMAX_MIN
+		|| voltage > PM8921_CHG_VDDMAX_MAX) {
+		pr_err("bad mV=%d asked to set\n", voltage);
+		return -EINVAL;
+	}
+
+	ret = pm_chg_vddmax_get(chip, &current_mv);
+	if (ret) {
+		pr_err("Failed to read vddmax rc=%d\n", ret);
+		return -EINVAL;
+	}
+	if (current_mv == voltage)
+		return 0;
+
+	/* Only change in increments when USB is present */
+	if (is_usb_chg_plugged_in(chip)) {
+		if (current_mv < voltage) {
+			steps = (voltage - current_mv) / PM8921_CHG_V_STEP_MV;
+			increase = true;
+		} else {
+			steps = (current_mv - voltage) / PM8921_CHG_V_STEP_MV;
+			increase = false;
+		}
+		for (i = 0; i < steps; i++) {
+			if (increase)
+				current_mv += PM8921_CHG_V_STEP_MV;
+			else
+				current_mv -= PM8921_CHG_V_STEP_MV;
+			ret |= __pm_chg_vddmax_set(chip, current_mv);
+		}
+	}
+	ret |= __pm_chg_vddmax_set(chip, voltage);
+	return ret;
+}
+
+#define PM8921_CHG_VDDSAFE_MIN	3400
+#define PM8921_CHG_VDDSAFE_MAX	4500
+static int pm_chg_vddsafe_set(struct pm8921_chg_chip *chip, int voltage)
+{
+	u8 temp;
+
+	if (voltage < PM8921_CHG_VDDSAFE_MIN
+			|| voltage > PM8921_CHG_VDDSAFE_MAX) {
+		pr_err("bad mV=%d asked to set\n", voltage);
+		return -EINVAL;
+	}
+	temp = (voltage - PM8921_CHG_V_MIN_MV) / PM8921_CHG_V_STEP_MV;
+	pr_debug("voltage=%d setting %02x\n", voltage, temp);
+	return pm_chg_masked_write(chip, CHG_VDD_SAFE, PM8921_CHG_V_MASK, temp);
+}
+
+#define PM8921_CHG_VBATDET_MIN	3240
+#define PM8921_CHG_VBATDET_MAX	5780
+static int pm_chg_vbatdet_set(struct pm8921_chg_chip *chip, int voltage)
+{
+	u8 temp;
+
+	if (voltage < PM8921_CHG_VBATDET_MIN
+			|| voltage > PM8921_CHG_VBATDET_MAX) {
+		pr_err("bad mV=%d asked to set\n", voltage);
+		return -EINVAL;
+	}
+	temp = (voltage - PM8921_CHG_V_MIN_MV) / PM8921_CHG_V_STEP_MV;
+	pr_debug("voltage=%d setting %02x\n", voltage, temp);
+	return pm_chg_masked_write(chip, CHG_VBAT_DET, PM8921_CHG_V_MASK, temp);
+}
+
+#define PM8921_CHG_VINMIN_MIN_MV	3800
+#define PM8921_CHG_VINMIN_STEP_MV	100
+#define PM8921_CHG_VINMIN_USABLE_MAX	6500
+#define PM8921_CHG_VINMIN_USABLE_MIN	4300
+#define PM8921_CHG_VINMIN_MASK		0x1F
+static int pm_chg_vinmin_set(struct pm8921_chg_chip *chip, int voltage)
+{
+	u8 temp;
+
+	if (voltage < PM8921_CHG_VINMIN_USABLE_MIN
+			|| voltage > PM8921_CHG_VINMIN_USABLE_MAX) {
+		pr_err("bad mV=%d asked to set\n", voltage);
+		return -EINVAL;
+	}
+	temp = (voltage - PM8921_CHG_VINMIN_MIN_MV) / PM8921_CHG_VINMIN_STEP_MV;
+	pr_debug("voltage=%d setting %02x\n", voltage, temp);
+	return pm_chg_masked_write(chip, CHG_VIN_MIN, PM8921_CHG_VINMIN_MASK,
+									temp);
+}
+
+static int pm_chg_vinmin_get(struct pm8921_chg_chip *chip)
+{
+	u8 temp;
+	int rc, voltage_mv;
+
+	rc = pm8xxx_readb(chip->dev->parent, CHG_VIN_MIN, &temp);
+	temp &= PM8921_CHG_VINMIN_MASK;
+
+	voltage_mv = PM8921_CHG_VINMIN_MIN_MV +
+			(int)temp * PM8921_CHG_VINMIN_STEP_MV;
+
+	return voltage_mv;
+}
+
+#define PM8921_CHG_IBATMAX_MIN	325
+#define PM8921_CHG_IBATMAX_MAX	2000
+#define PM8921_CHG_I_MIN_MA	225
+#define PM8921_CHG_I_STEP_MA	50
+#define PM8921_CHG_I_MASK	0x3F
+static int pm_chg_ibatmax_set(struct pm8921_chg_chip *chip, int chg_current)
+{
+	u8 temp;
+
+	if (chg_current < PM8921_CHG_IBATMAX_MIN
+			|| chg_current > PM8921_CHG_IBATMAX_MAX) {
+		pr_err("bad mA=%d asked to set\n", chg_current);
+		return -EINVAL;
+	}
+	temp = (chg_current - PM8921_CHG_I_MIN_MA) / PM8921_CHG_I_STEP_MA;
+	return pm_chg_masked_write(chip, CHG_IBAT_MAX, PM8921_CHG_I_MASK, temp);
+}
+
+#define PM8921_CHG_IBATSAFE_MIN	225
+#define PM8921_CHG_IBATSAFE_MAX	3375
+static int pm_chg_ibatsafe_set(struct pm8921_chg_chip *chip, int chg_current)
+{
+	u8 temp;
+
+	if (chg_current < PM8921_CHG_IBATSAFE_MIN
+			|| chg_current > PM8921_CHG_IBATSAFE_MAX) {
+		pr_err("bad mA=%d asked to set\n", chg_current);
+		return -EINVAL;
+	}
+	temp = (chg_current - PM8921_CHG_I_MIN_MA) / PM8921_CHG_I_STEP_MA;
+	return pm_chg_masked_write(chip, CHG_IBAT_SAFE,
+						PM8921_CHG_I_MASK, temp);
+}
+
+#define PM8921_CHG_ITERM_MIN_MA		50
+#define PM8921_CHG_ITERM_MAX_MA		200
+#define PM8921_CHG_ITERM_STEP_MA	10
+#define PM8921_CHG_ITERM_MASK		0xF
+static int pm_chg_iterm_set(struct pm8921_chg_chip *chip, int chg_current)
+{
+	u8 temp;
+
+	if (chg_current < PM8921_CHG_ITERM_MIN_MA
+			|| chg_current > PM8921_CHG_ITERM_MAX_MA) {
+		pr_err("bad mA=%d asked to set\n", chg_current);
+		return -EINVAL;
+	}
+
+	temp = (chg_current - PM8921_CHG_ITERM_MIN_MA)
+				/ PM8921_CHG_ITERM_STEP_MA;
+	return pm_chg_masked_write(chip, CHG_ITERM, PM8921_CHG_ITERM_MASK,
+					 temp);
+}
+
+static int pm_chg_iterm_get(struct pm8921_chg_chip *chip, int *chg_current)
+{
+	u8 temp;
+	int rc;
+
+	rc = pm8xxx_readb(chip->dev->parent, CHG_ITERM, &temp);
+	if (rc) {
+		pr_err("err=%d reading CHG_ITEM\n", rc);
+		*chg_current = 0;
+		return rc;
+	}
+	temp &= PM8921_CHG_ITERM_MASK;
+	*chg_current = (int)temp * PM8921_CHG_ITERM_STEP_MA
+					+ PM8921_CHG_ITERM_MIN_MA;
+	return 0;
+}
+
+struct usb_ma_limit_entry {
+	int	usb_ma;
+	u8	value;
+};
+
+static struct usb_ma_limit_entry usb_ma_table[] = {
+	{100, 0x0},
+	{200, 0x1},
+	{500, 0x2},
+	{600, 0x3},
+	{700, 0x4},
+	{800, 0x5},
+	{850, 0x6},
+	{900, 0x8},
+	{950, 0x7},
+	{1000, 0x9},
+	{1100, 0xA},
+	{1200, 0xB},
+	{1300, 0xC},
+	{1400, 0xD},
+	{1500, 0xE},
+	{1600, 0xF},
+};
+
+#define PM8921_CHG_IUSB_MASK 0x1C
+#define PM8921_CHG_IUSB_SHIFT 2
+#define PM8921_CHG_IUSB_MAX  7
+#define PM8921_CHG_IUSB_MIN  0
+#define PM8917_IUSB_FINE_RES BIT(0)
+static int pm_chg_iusbmax_set(struct pm8921_chg_chip *chip, int reg_val)
+{
+	u8 temp, fineres;
+	int rc;
+
+	fineres = PM8917_IUSB_FINE_RES & usb_ma_table[reg_val].value;
+	reg_val = usb_ma_table[reg_val].value >> 1;
+
+	if (reg_val < PM8921_CHG_IUSB_MIN || reg_val > PM8921_CHG_IUSB_MAX) {
+		pr_err("bad mA=%d asked to set\n", reg_val);
+		return -EINVAL;
+	}
+	temp = reg_val << PM8921_CHG_IUSB_SHIFT;
+
+	/* IUSB_FINE_RES */
+	if (chip->iusb_fine_res) {
+		/* Clear IUSB_FINE_RES bit to avoid overshoot */
+		rc = pm_chg_masked_write(chip, IUSB_FINE_RES,
+			PM8917_IUSB_FINE_RES, 0);
+
+		rc |= pm_chg_masked_write(chip, PBL_ACCESS2,
+			PM8921_CHG_IUSB_MASK, temp);
+
+		if (rc) {
+			pr_err("Failed to write PBL_ACCESS2 rc=%d\n", rc);
+			return rc;
+		}
+
+		if (fineres) {
+			rc = pm_chg_masked_write(chip, IUSB_FINE_RES,
+				PM8917_IUSB_FINE_RES, fineres);
+			if (rc)
+				pr_err("Failed to write ISUB_FINE_RES rc=%d\n",
+					rc);
+		}
+	} else {
+		rc = pm_chg_masked_write(chip, PBL_ACCESS2,
+			PM8921_CHG_IUSB_MASK, temp);
+		if (rc)
+			pr_err("Failed to write PBL_ACCESS2 rc=%d\n", rc);
+	}
+
+	return rc;
+}
+
+static int pm_chg_iusbmax_get(struct pm8921_chg_chip *chip, int *mA)
+{
+	u8 temp, fineres;
+	int rc, i;
+
+	fineres = 0;
+	*mA = 0;
+	rc = pm8xxx_readb(chip->dev->parent, PBL_ACCESS2, &temp);
+	if (rc) {
+		pr_err("err=%d reading PBL_ACCESS2\n", rc);
+		return rc;
+	}
+
+	if (chip->iusb_fine_res) {
+		rc = pm8xxx_readb(chip->dev->parent, IUSB_FINE_RES, &fineres);
+		if (rc) {
+			pr_err("err=%d reading IUSB_FINE_RES\n", rc);
+			return rc;
+		}
+	}
+	temp &= PM8921_CHG_IUSB_MASK;
+	temp = temp >> PM8921_CHG_IUSB_SHIFT;
+
+	temp = (temp << 1) | (fineres & PM8917_IUSB_FINE_RES);
+	for (i = ARRAY_SIZE(usb_ma_table) - 1; i >= 0; i--) {
+		if (usb_ma_table[i].value == temp)
+			break;
+	}
+
+	*mA = usb_ma_table[i].usb_ma;
+
+	return rc;
+}
+
+#define PM8921_CHG_WD_MASK 0x1F
+static int pm_chg_disable_wd(struct pm8921_chg_chip *chip)
+{
+	/* writing 0 to the wd timer disables it */
+	return pm_chg_masked_write(chip, CHG_TWDOG, PM8921_CHG_WD_MASK, 0);
+}
+
+#define PM8921_CHG_TCHG_MASK	0x7F
+#define PM8921_CHG_TCHG_MIN	4
+#define PM8921_CHG_TCHG_MAX	512
+#define PM8921_CHG_TCHG_STEP	4
+static int pm_chg_tchg_max_set(struct pm8921_chg_chip *chip, int minutes)
+{
+	u8 temp;
+
+	if (minutes < PM8921_CHG_TCHG_MIN || minutes > PM8921_CHG_TCHG_MAX) {
+		pr_err("bad max minutes =%d asked to set\n", minutes);
+		return -EINVAL;
+	}
+
+	temp = (minutes - 1)/PM8921_CHG_TCHG_STEP;
+	return pm_chg_masked_write(chip, CHG_TCHG_MAX, PM8921_CHG_TCHG_MASK,
+					 temp);
+}
+
+#define PM8921_CHG_TTRKL_MASK	0x1F
+#define PM8921_CHG_TTRKL_MIN	1
+#define PM8921_CHG_TTRKL_MAX	64
+static int pm_chg_ttrkl_max_set(struct pm8921_chg_chip *chip, int minutes)
+{
+	u8 temp;
+
+	if (minutes < PM8921_CHG_TTRKL_MIN || minutes > PM8921_CHG_TTRKL_MAX) {
+		pr_err("bad max minutes =%d asked to set\n", minutes);
+		return -EINVAL;
+	}
+
+	temp = minutes - 1;
+	return pm_chg_masked_write(chip, CHG_TTRKL_MAX, PM8921_CHG_TTRKL_MASK,
+					 temp);
+}
+
+#define PM8921_CHG_VTRKL_MIN_MV		2050
+#define PM8921_CHG_VTRKL_MAX_MV		2800
+#define PM8921_CHG_VTRKL_STEP_MV	50
+#define PM8921_CHG_VTRKL_SHIFT		4
+#define PM8921_CHG_VTRKL_MASK		0xF0
+static int pm_chg_vtrkl_low_set(struct pm8921_chg_chip *chip, int millivolts)
+{
+	u8 temp;
+
+	if (millivolts < PM8921_CHG_VTRKL_MIN_MV
+			|| millivolts > PM8921_CHG_VTRKL_MAX_MV) {
+		pr_err("bad voltage = %dmV asked to set\n", millivolts);
+		return -EINVAL;
+	}
+
+	temp = (millivolts - PM8921_CHG_VTRKL_MIN_MV)/PM8921_CHG_VTRKL_STEP_MV;
+	temp = temp << PM8921_CHG_VTRKL_SHIFT;
+	return pm_chg_masked_write(chip, CHG_VTRICKLE, PM8921_CHG_VTRKL_MASK,
+					 temp);
+}
+
+#define PM8921_CHG_VWEAK_MIN_MV		2100
+#define PM8921_CHG_VWEAK_MAX_MV		3600
+#define PM8921_CHG_VWEAK_STEP_MV	100
+#define PM8921_CHG_VWEAK_MASK		0x0F
+static int pm_chg_vweak_set(struct pm8921_chg_chip *chip, int millivolts)
+{
+	u8 temp;
+
+	if (millivolts < PM8921_CHG_VWEAK_MIN_MV
+			|| millivolts > PM8921_CHG_VWEAK_MAX_MV) {
+		pr_err("bad voltage = %dmV asked to set\n", millivolts);
+		return -EINVAL;
+	}
+
+	temp = (millivolts - PM8921_CHG_VWEAK_MIN_MV)/PM8921_CHG_VWEAK_STEP_MV;
+	return pm_chg_masked_write(chip, CHG_VTRICKLE, PM8921_CHG_VWEAK_MASK,
+					 temp);
+}
+
+#define PM8921_CHG_ITRKL_MIN_MA		50
+#define PM8921_CHG_ITRKL_MAX_MA		200
+#define PM8921_CHG_ITRKL_MASK		0x0F
+#define PM8921_CHG_ITRKL_STEP_MA	10
+static int pm_chg_itrkl_set(struct pm8921_chg_chip *chip, int milliamps)
+{
+	u8 temp;
+
+	if (milliamps < PM8921_CHG_ITRKL_MIN_MA
+		|| milliamps > PM8921_CHG_ITRKL_MAX_MA) {
+		pr_err("bad current = %dmA asked to set\n", milliamps);
+		return -EINVAL;
+	}
+
+	temp = (milliamps - PM8921_CHG_ITRKL_MIN_MA)/PM8921_CHG_ITRKL_STEP_MA;
+
+	return pm_chg_masked_write(chip, CHG_ITRICKLE, PM8921_CHG_ITRKL_MASK,
+					 temp);
+}
+
+#define PM8921_CHG_IWEAK_MIN_MA		325
+#define PM8921_CHG_IWEAK_MAX_MA		525
+#define PM8921_CHG_IWEAK_SHIFT		7
+#define PM8921_CHG_IWEAK_MASK		0x80
+static int pm_chg_iweak_set(struct pm8921_chg_chip *chip, int milliamps)
+{
+	u8 temp;
+
+	if (milliamps < PM8921_CHG_IWEAK_MIN_MA
+		|| milliamps > PM8921_CHG_IWEAK_MAX_MA) {
+		pr_err("bad current = %dmA asked to set\n", milliamps);
+		return -EINVAL;
+	}
+
+	if (milliamps < PM8921_CHG_IWEAK_MAX_MA)
+		temp = 0;
+	else
+		temp = 1;
+
+	temp = temp << PM8921_CHG_IWEAK_SHIFT;
+	return pm_chg_masked_write(chip, CHG_ITRICKLE, PM8921_CHG_IWEAK_MASK,
+					 temp);
+}
+
+#define PM8921_CHG_BATT_TEMP_THR_COLD	BIT(1)
+#define PM8921_CHG_BATT_TEMP_THR_COLD_SHIFT	1
+static int pm_chg_batt_cold_temp_config(struct pm8921_chg_chip *chip,
+					enum pm8921_chg_cold_thr cold_thr)
+{
+	u8 temp;
+
+	temp = cold_thr << PM8921_CHG_BATT_TEMP_THR_COLD_SHIFT;
+	temp = temp & PM8921_CHG_BATT_TEMP_THR_COLD;
+	return pm_chg_masked_write(chip, CHG_CNTRL_2,
+					PM8921_CHG_BATT_TEMP_THR_COLD,
+					 temp);
+}
+
+#define PM8921_CHG_BATT_TEMP_THR_HOT		BIT(0)
+#define PM8921_CHG_BATT_TEMP_THR_HOT_SHIFT	0
+static int pm_chg_batt_hot_temp_config(struct pm8921_chg_chip *chip,
+					enum pm8921_chg_hot_thr hot_thr)
+{
+	u8 temp;
+
+	temp = hot_thr << PM8921_CHG_BATT_TEMP_THR_HOT_SHIFT;
+	temp = temp & PM8921_CHG_BATT_TEMP_THR_HOT;
+	return pm_chg_masked_write(chip, CHG_CNTRL_2,
+					PM8921_CHG_BATT_TEMP_THR_HOT,
+					 temp);
+}
+
+#define PM8921_CHG_LED_SRC_CONFIG_SHIFT	4
+#define PM8921_CHG_LED_SRC_CONFIG_MASK	0x30
+static int pm_chg_led_src_config(struct pm8921_chg_chip *chip,
+				enum pm8921_chg_led_src_config led_src_config)
+{
+	u8 temp;
+
+	if (led_src_config < LED_SRC_GND ||
+			led_src_config > LED_SRC_BYPASS)
+		return -EINVAL;
+
+	if (led_src_config == LED_SRC_BYPASS)
+		return 0;
+
+	temp = led_src_config << PM8921_CHG_LED_SRC_CONFIG_SHIFT;
+
+	return pm_chg_masked_write(chip, CHG_CNTRL_3,
+					PM8921_CHG_LED_SRC_CONFIG_MASK, temp);
+}
+
+static void disable_input_voltage_regulation(struct pm8921_chg_chip *chip)
+{
+	u8 temp;
+	int rc;
+
+	rc = pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, 0x70);
+	if (rc) {
+		pr_err("Failed to write 0x70 to CTRL_TEST3 rc = %d\n", rc);
+		return;
+	}
+	rc = pm8xxx_readb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, &temp);
+	if (rc) {
+		pr_err("Failed to read CTRL_TEST3 rc = %d\n", rc);
+		return;
+	}
+	/* set the input voltage disable bit and the write bit */
+	temp |= 0x81;
+	rc = pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, temp);
+	if (rc) {
+		pr_err("Failed to write 0x%x to CTRL_TEST3 rc=%d\n", temp, rc);
+		return;
+	}
+}
+
+static void enable_input_voltage_regulation(struct pm8921_chg_chip *chip)
+{
+	u8 temp;
+	int rc;
+
+	rc = pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, 0x70);
+	if (rc) {
+		pr_err("Failed to write 0x70 to CTRL_TEST3 rc = %d\n", rc);
+		return;
+	}
+	rc = pm8xxx_readb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, &temp);
+	if (rc) {
+		pr_err("Failed to read CTRL_TEST3 rc = %d\n", rc);
+		return;
+	}
+	/* unset the input voltage disable bit */
+	temp &= 0xFE;
+	/* set the write bit */
+	temp |= 0x80;
+	rc = pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, temp);
+	if (rc) {
+		pr_err("Failed to write 0x%x to CTRL_TEST3 rc=%d\n", temp, rc);
+		return;
+	}
+}
+
+static int64_t read_battery_id(struct pm8921_chg_chip *chip)
+{
+	int rc;
+	struct pm8xxx_adc_chan_result result;
+
+	rc = pm8xxx_adc_read(chip->batt_id_channel, &result);
+	if (rc) {
+		pr_err("error reading batt id channel = %d, rc = %d\n",
+					chip->vbat_channel, rc);
+		return rc;
+	}
+	pr_debug("batt_id phy = %lld meas = 0x%llx\n", result.physical,
+						result.measurement);
+	return result.physical;
+}
+
+static int is_battery_valid(struct pm8921_chg_chip *chip)
+{
+	int64_t rc;
+
+	if (chip->batt_id_min == 0 && chip->batt_id_max == 0)
+		return 1;
+
+	rc = read_battery_id(chip);
+	if (rc < 0) {
+		pr_err("error reading batt id channel = %d, rc = %lld\n",
+					chip->vbat_channel, rc);
+		/* assume battery id is valid when adc error happens */
+		return 1;
+	}
+
+	if (rc < chip->batt_id_min || rc > chip->batt_id_max) {
+		pr_err("batt_id phy =%lld is not valid\n", rc);
+		return 0;
+	}
+	return 1;
+}
+
+static void check_battery_valid(struct pm8921_chg_chip *chip)
+{
+	if (is_battery_valid(chip) == 0) {
+		pr_err("batt_id not valid, disbling charging\n");
+		pm_chg_auto_enable(chip, 0);
+	} else {
+		pm_chg_auto_enable(chip, !charging_disabled);
+	}
+}
+
+static void battery_id_valid(struct work_struct *work)
+{
+	struct pm8921_chg_chip *chip = container_of(work,
+				struct pm8921_chg_chip, battery_id_valid_work);
+
+	check_battery_valid(chip);
+}
+
+static void pm8921_chg_enable_irq(struct pm8921_chg_chip *chip, int interrupt)
+{
+	if (!__test_and_set_bit(interrupt, chip->enabled_irqs)) {
+		dev_dbg(chip->dev, "%d\n", chip->pmic_chg_irq[interrupt]);
+		enable_irq(chip->pmic_chg_irq[interrupt]);
+	}
+}
+
+static void pm8921_chg_disable_irq(struct pm8921_chg_chip *chip, int interrupt)
+{
+	if (__test_and_clear_bit(interrupt, chip->enabled_irqs)) {
+		dev_dbg(chip->dev, "%d\n", chip->pmic_chg_irq[interrupt]);
+		disable_irq_nosync(chip->pmic_chg_irq[interrupt]);
+	}
+}
+
+static int pm8921_chg_is_enabled(struct pm8921_chg_chip *chip, int interrupt)
+{
+	return test_bit(interrupt, chip->enabled_irqs);
+}
+
+static bool is_ext_charging(struct pm8921_chg_chip *chip)
+{
+	union power_supply_propval ret = {0,};
+
+	if (!chip->ext_psy)
+		return false;
+	if (chip->ext_psy->get_property(chip->ext_psy,
+					POWER_SUPPLY_PROP_CHARGE_TYPE, &ret))
+		return false;
+	if (ret.intval > POWER_SUPPLY_CHARGE_TYPE_NONE)
+		return ret.intval;
+
+	return false;
+}
+
+static bool is_ext_trickle_charging(struct pm8921_chg_chip *chip)
+{
+	union power_supply_propval ret = {0,};
+
+	if (!chip->ext_psy)
+		return false;
+	if (chip->ext_psy->get_property(chip->ext_psy,
+					POWER_SUPPLY_PROP_CHARGE_TYPE, &ret))
+		return false;
+	if (ret.intval == POWER_SUPPLY_CHARGE_TYPE_TRICKLE)
+		return true;
+
+	return false;
+}
+
+static int is_battery_charging(int fsm_state)
+{
+	if (is_ext_charging(the_chip))
+		return 1;
+
+	switch (fsm_state) {
+	case FSM_STATE_ATC_2A:
+	case FSM_STATE_ATC_2B:
+	case FSM_STATE_ON_CHG_AND_BAT_6:
+	case FSM_STATE_FAST_CHG_7:
+	case FSM_STATE_TRKL_CHG_8:
+		return 1;
+	}
+	return 0;
+}
+
+static void bms_notify(struct work_struct *work)
+{
+	struct bms_notify *n = container_of(work, struct bms_notify, work);
+
+	if (n->is_charging) {
+		pm8921_bms_charging_began();
+	} else {
+		pm8921_bms_charging_end(n->is_battery_full);
+		n->is_battery_full = 0;
+	}
+}
+
+static void bms_notify_check(struct pm8921_chg_chip *chip)
+{
+	int fsm_state, new_is_charging;
+
+	fsm_state = pm_chg_get_fsm_state(chip);
+	new_is_charging = is_battery_charging(fsm_state);
+
+	if (chip->bms_notify.is_charging ^ new_is_charging) {
+		chip->bms_notify.is_charging = new_is_charging;
+		schedule_work(&(chip->bms_notify.work));
+	}
+}
+
+static enum power_supply_property pm_power_props_usb[] = {
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_CURRENT_MAX,
+};
+
+static enum power_supply_property pm_power_props_mains[] = {
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_ONLINE,
+};
+
+static char *pm_power_supplied_to[] = {
+	"battery",
+};
+
+#define USB_WALL_THRESHOLD_MA	500
+static int pm_power_get_property_mains(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  union power_supply_propval *val)
+{
+	/* Check if called before init */
+	if (!the_chip)
+		return -EINVAL;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_PRESENT:
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = 0;
+		if (charging_disabled)
+			return 0;
+
+		/* check external charger first before the dc path */
+		if (is_ext_charging(the_chip)) {
+			val->intval = 1;
+			return 0;
+		}
+
+		if (pm_is_chg_charge_dis(the_chip)) {
+			val->intval = 0;
+			return 0;
+		}
+
+		if (the_chip->dc_present) {
+			val->intval = 1;
+			return 0;
+		}
+
+		/* USB with max current greater than 500 mA connected */
+		if (usb_target_ma > USB_WALL_THRESHOLD_MA)
+			val->intval = is_usb_chg_plugged_in(the_chip);
+			return 0;
+
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int pm_power_get_property_usb(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  union power_supply_propval *val)
+{
+	int current_max;
+
+	/* Check if called before init */
+	if (!the_chip)
+		return -EINVAL;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		if (pm_is_chg_charge_dis(the_chip)) {
+			val->intval = 0;
+		} else {
+			pm_chg_iusbmax_get(the_chip, &current_max);
+			val->intval = current_max;
+		}
+		break;
+	case POWER_SUPPLY_PROP_PRESENT:
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = 0;
+		if (charging_disabled)
+			return 0;
+
+		/*
+		 * if drawing any current from usb is disabled behave
+		 * as if no usb cable is connected
+		 */
+		if (pm_is_chg_charge_dis(the_chip))
+			return 0;
+
+		/* USB charging */
+		if (usb_target_ma < USB_WALL_THRESHOLD_MA)
+			val->intval = is_usb_chg_plugged_in(the_chip);
+		else
+		    return 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static enum power_supply_property msm_batt_power_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_CHARGE_TYPE,
+	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_CAPACITY,
+	POWER_SUPPLY_PROP_CURRENT_NOW,
+	POWER_SUPPLY_PROP_TEMP,
+	POWER_SUPPLY_PROP_ENERGY_FULL,
+};
+
+static int get_prop_battery_uvolts(struct pm8921_chg_chip *chip)
+{
+	int rc;
+	struct pm8xxx_adc_chan_result result;
+
+	rc = pm8xxx_adc_read(chip->vbat_channel, &result);
+	if (rc) {
+		pr_err("error reading adc channel = %d, rc = %d\n",
+					chip->vbat_channel, rc);
+		return rc;
+	}
+	pr_debug("mvolts phy = %lld meas = 0x%llx\n", result.physical,
+						result.measurement);
+	return (int)result.physical;
+}
+
+static unsigned int voltage_based_capacity(struct pm8921_chg_chip *chip)
+{
+	unsigned int current_voltage_uv = get_prop_battery_uvolts(chip);
+	unsigned int current_voltage_mv = current_voltage_uv / 1000;
+	unsigned int low_voltage = chip->min_voltage_mv;
+	unsigned int high_voltage = chip->max_voltage_mv;
+
+	if (current_voltage_mv <= low_voltage)
+		return 0;
+	else if (current_voltage_mv >= high_voltage)
+		return 100;
+	else
+		return (current_voltage_mv - low_voltage) * 100
+		    / (high_voltage - low_voltage);
+}
+
+static int get_prop_batt_present(struct pm8921_chg_chip *chip)
+{
+	return pm_chg_get_rt_status(chip, BATT_INSERTED_IRQ);
+}
+
+static int get_prop_batt_capacity(struct pm8921_chg_chip *chip)
+{
+	int percent_soc;
+
+	if (!get_prop_batt_present(chip))
+		percent_soc = voltage_based_capacity(chip);
+	else
+		percent_soc = pm8921_bms_get_percent_charge();
+
+	if (percent_soc == -ENXIO)
+		percent_soc = voltage_based_capacity(chip);
+
+	if (percent_soc <= 10)
+		pr_warn("low battery charge = %d%%\n", percent_soc);
+
+	return percent_soc;
+}
+
+static int get_prop_batt_current(struct pm8921_chg_chip *chip)
+{
+	int result_ua, rc;
+
+	rc = pm8921_bms_get_battery_current(&result_ua);
+	if (rc == -ENXIO) {
+		rc = pm8xxx_ccadc_get_battery_current(&result_ua);
+	}
+
+	if (rc) {
+		pr_err("unable to get batt current rc = %d\n", rc);
+		return rc;
+	} else {
+		return result_ua;
+	}
+}
+
+static int get_prop_batt_fcc(struct pm8921_chg_chip *chip)
+{
+	int rc;
+
+	rc = pm8921_bms_get_fcc();
+	if (rc < 0)
+		pr_err("unable to get batt fcc rc = %d\n", rc);
+	return rc;
+}
+
+static int get_prop_batt_health(struct pm8921_chg_chip *chip)
+{
+	int temp;
+
+	temp = pm_chg_get_rt_status(chip, BATTTEMP_HOT_IRQ);
+	if (temp)
+		return POWER_SUPPLY_HEALTH_OVERHEAT;
+
+	temp = pm_chg_get_rt_status(chip, BATTTEMP_COLD_IRQ);
+	if (temp)
+		return POWER_SUPPLY_HEALTH_COLD;
+
+	return POWER_SUPPLY_HEALTH_GOOD;
+}
+
+static int get_prop_charge_type(struct pm8921_chg_chip *chip)
+{
+	int temp;
+
+	if (!get_prop_batt_present(chip))
+		return POWER_SUPPLY_CHARGE_TYPE_NONE;
+
+	if (is_ext_trickle_charging(chip))
+		return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+
+	if (is_ext_charging(chip))
+		return POWER_SUPPLY_CHARGE_TYPE_FAST;
+
+	temp = pm_chg_get_rt_status(chip, TRKLCHG_IRQ);
+	if (temp)
+		return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+
+	temp = pm_chg_get_rt_status(chip, FASTCHG_IRQ);
+	if (temp)
+		return POWER_SUPPLY_CHARGE_TYPE_FAST;
+
+	return POWER_SUPPLY_CHARGE_TYPE_NONE;
+}
+
+static int get_prop_batt_status(struct pm8921_chg_chip *chip)
+{
+	int batt_state = POWER_SUPPLY_STATUS_DISCHARGING;
+	int fsm_state = pm_chg_get_fsm_state(chip);
+	int i;
+
+	if (chip->ext_psy) {
+		if (chip->ext_charge_done)
+			return POWER_SUPPLY_STATUS_FULL;
+		if (chip->ext_charging)
+			return POWER_SUPPLY_STATUS_CHARGING;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(map); i++)
+		if (map[i].fsm_state == fsm_state)
+			batt_state = map[i].batt_state;
+
+	if (fsm_state == FSM_STATE_ON_CHG_HIGHI_1) {
+		if (!pm_chg_get_rt_status(chip, BATT_INSERTED_IRQ)
+			|| !pm_chg_get_rt_status(chip, BAT_TEMP_OK_IRQ)
+			|| pm_chg_get_rt_status(chip, CHGHOT_IRQ)
+			|| pm_chg_get_rt_status(chip, VBATDET_LOW_IRQ))
+
+			batt_state = POWER_SUPPLY_STATUS_NOT_CHARGING;
+	}
+	return batt_state;
+}
+
+#define MAX_TOLERABLE_BATT_TEMP_DDC	680
+static int get_prop_batt_temp(struct pm8921_chg_chip *chip)
+{
+	int rc;
+	struct pm8xxx_adc_chan_result result;
+
+	rc = pm8xxx_adc_read(chip->batt_temp_channel, &result);
+	if (rc) {
+		pr_err("error reading adc channel = %d, rc = %d\n",
+					chip->vbat_channel, rc);
+		return rc;
+	}
+	pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
+						result.measurement);
+	if (result.physical > MAX_TOLERABLE_BATT_TEMP_DDC)
+		pr_err("BATT_TEMP= %d > 68degC, device will be shutdown\n",
+							(int) result.physical);
+
+	return (int)result.physical;
+}
+
+static int pm_batt_power_get_property(struct power_supply *psy,
+				       enum power_supply_property psp,
+				       union power_supply_propval *val)
+{
+	struct pm8921_chg_chip *chip = container_of(psy, struct pm8921_chg_chip,
+								batt_psy);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		val->intval = get_prop_batt_status(chip);
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_TYPE:
+		val->intval = get_prop_charge_type(chip);
+		break;
+	case POWER_SUPPLY_PROP_HEALTH:
+		val->intval = get_prop_batt_health(chip);
+		break;
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = get_prop_batt_present(chip);
+		break;
+	case POWER_SUPPLY_PROP_TECHNOLOGY:
+		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+		val->intval = chip->max_voltage_mv * 1000;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+		val->intval = chip->min_voltage_mv * 1000;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		val->intval = get_prop_battery_uvolts(chip);
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY:
+		val->intval = get_prop_batt_capacity(chip);
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_NOW:
+		val->intval = get_prop_batt_current(chip);
+		break;
+	case POWER_SUPPLY_PROP_TEMP:
+		val->intval = get_prop_batt_temp(chip);
+		break;
+	case POWER_SUPPLY_PROP_ENERGY_FULL:
+		val->intval = get_prop_batt_fcc(chip) * 1000;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void (*notify_vbus_state_func_ptr)(int);
+static int usb_chg_current;
+static DEFINE_SPINLOCK(vbus_lock);
+
+int pm8921_charger_register_vbus_sn(void (*callback)(int))
+{
+	pr_debug("%p\n", callback);
+	notify_vbus_state_func_ptr = callback;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pm8921_charger_register_vbus_sn);
+
+/* this is passed to the hsusb via platform_data msm_otg_pdata */
+void pm8921_charger_unregister_vbus_sn(void (*callback)(int))
+{
+	pr_debug("%p\n", callback);
+	notify_vbus_state_func_ptr = NULL;
+}
+EXPORT_SYMBOL_GPL(pm8921_charger_unregister_vbus_sn);
+
+static void notify_usb_of_the_plugin_event(int plugin)
+{
+	plugin = !!plugin;
+	if (notify_vbus_state_func_ptr) {
+		pr_debug("notifying plugin\n");
+		(*notify_vbus_state_func_ptr) (plugin);
+	} else {
+		pr_debug("unable to notify plugin\n");
+	}
+}
+
+/* assumes vbus_lock is held */
+static void __pm8921_charger_vbus_draw(unsigned int mA)
+{
+	int i, rc;
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return;
+	}
+
+	if (mA >= 0 && mA <= 2) {
+		usb_chg_current = 0;
+		rc = pm_chg_iusbmax_set(the_chip, 0);
+		if (rc) {
+			pr_err("unable to set iusb to %d rc = %d\n", 0, rc);
+		}
+		rc = pm_chg_usb_suspend_enable(the_chip, 1);
+		if (rc)
+			pr_err("fail to set suspend bit rc=%d\n", rc);
+	} else {
+		rc = pm_chg_usb_suspend_enable(the_chip, 0);
+		if (rc)
+			pr_err("fail to reset suspend bit rc=%d\n", rc);
+		for (i = ARRAY_SIZE(usb_ma_table) - 1; i >= 0; i--) {
+			if (usb_ma_table[i].usb_ma <= mA)
+				break;
+		}
+
+		/* Check if IUSB_FINE_RES is available */
+		if ((usb_ma_table[i].value & PM8917_IUSB_FINE_RES)
+				&& !the_chip->iusb_fine_res)
+			i--;
+		if (i < 0)
+			i = 0;
+		rc = pm_chg_iusbmax_set(the_chip, i);
+		if (rc) {
+			pr_err("unable to set iusb to %d rc = %d\n", i, rc);
+		}
+	}
+}
+
+/* USB calls these to tell us how much max usb current the system can draw */
+void pm8921_charger_vbus_draw(unsigned int mA)
+{
+	unsigned long flags;
+
+	pr_debug("Enter charge=%d\n", mA);
+
+	if (!the_chip) {
+		pr_err("chip not yet initalized\n");
+		return;
+	}
+
+	/*
+	 * Reject VBUS requests if USB connection is the only available
+	 * power source. This makes sure that if booting without
+	 * battery the iusb_max value is not decreased avoiding potential
+	 * brown_outs.
+	 *
+	 * This would also apply when the battery has been
+	 * removed from the running system.
+	 */
+	if (!get_prop_batt_present(the_chip)
+		&& !is_dc_chg_plugged_in(the_chip)) {
+		pr_err("rejected: no other power source connected\n");
+		return;
+	}
+
+	if (usb_max_current && mA > usb_max_current) {
+		pr_warn("restricting usb current to %d instead of %d\n",
+					usb_max_current, mA);
+		mA = usb_max_current;
+	}
+	if (usb_target_ma == 0 && mA > USB_WALL_THRESHOLD_MA)
+		usb_target_ma = mA;
+
+	spin_lock_irqsave(&vbus_lock, flags);
+	if (the_chip) {
+		if (mA > USB_WALL_THRESHOLD_MA)
+			__pm8921_charger_vbus_draw(USB_WALL_THRESHOLD_MA);
+		else
+			__pm8921_charger_vbus_draw(mA);
+	} else {
+		/*
+		 * called before pmic initialized,
+		 * save this value and use it at probe
+		 */
+		if (mA > USB_WALL_THRESHOLD_MA)
+			usb_chg_current = USB_WALL_THRESHOLD_MA;
+		else
+			usb_chg_current = mA;
+	}
+	spin_unlock_irqrestore(&vbus_lock, flags);
+}
+EXPORT_SYMBOL_GPL(pm8921_charger_vbus_draw);
+
+int pm8921_charger_enable(bool enable)
+{
+	int rc;
+
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+	enable = !!enable;
+	rc = pm_chg_auto_enable(the_chip, enable);
+	if (rc)
+		pr_err("Failed rc=%d\n", rc);
+	return rc;
+}
+EXPORT_SYMBOL(pm8921_charger_enable);
+
+int pm8921_is_usb_chg_plugged_in(void)
+{
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+	return is_usb_chg_plugged_in(the_chip);
+}
+EXPORT_SYMBOL(pm8921_is_usb_chg_plugged_in);
+
+int pm8921_is_dc_chg_plugged_in(void)
+{
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+	return is_dc_chg_plugged_in(the_chip);
+}
+EXPORT_SYMBOL(pm8921_is_dc_chg_plugged_in);
+
+int pm8921_is_battery_present(void)
+{
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+	return get_prop_batt_present(the_chip);
+}
+EXPORT_SYMBOL(pm8921_is_battery_present);
+
+/*
+ * Disabling the charge current limit causes current
+ * current limits to have no monitoring. An adequate charger
+ * capable of supplying high current while sustaining VIN_MIN
+ * is required if the limiting is disabled.
+ */
+int pm8921_disable_input_current_limit(bool disable)
+{
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+	if (disable) {
+		pr_warn("Disabling input current limit!\n");
+
+		return pm8xxx_writeb(the_chip->dev->parent,
+			 CHG_BUCK_CTRL_TEST3, 0xF2);
+	}
+	return 0;
+}
+EXPORT_SYMBOL(pm8921_disable_input_current_limit);
+
+int pm8921_set_max_battery_charge_current(int ma)
+{
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+	return pm_chg_ibatmax_set(the_chip, ma);
+}
+EXPORT_SYMBOL(pm8921_set_max_battery_charge_current);
+
+int pm8921_disable_source_current(bool disable)
+{
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+	if (disable)
+		pr_warn("current drawn from chg=0, battery provides current\n");
+	return pm_chg_charge_dis(the_chip, disable);
+}
+EXPORT_SYMBOL(pm8921_disable_source_current);
+
+int pm8921_regulate_input_voltage(int voltage)
+{
+	int rc;
+
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+	rc = pm_chg_vinmin_set(the_chip, voltage);
+
+	if (rc == 0)
+		the_chip->vin_min = voltage;
+
+	return rc;
+}
+
+#define USB_OV_THRESHOLD_MASK  0x60
+#define USB_OV_THRESHOLD_SHIFT  5
+int pm8921_usb_ovp_set_threshold(enum pm8921_usb_ov_threshold ov)
+{
+	u8 temp;
+
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+
+	if (ov > PM_USB_OV_7V) {
+		pr_err("limiting to over voltage threshold to 7volts\n");
+		ov = PM_USB_OV_7V;
+	}
+
+	temp = USB_OV_THRESHOLD_MASK & (ov << USB_OV_THRESHOLD_SHIFT);
+
+	return pm_chg_masked_write(the_chip, USB_OVP_CONTROL,
+				USB_OV_THRESHOLD_MASK, temp);
+}
+EXPORT_SYMBOL(pm8921_usb_ovp_set_threshold);
+
+#define USB_DEBOUNCE_TIME_MASK	0x06
+#define USB_DEBOUNCE_TIME_SHIFT 1
+int pm8921_usb_ovp_set_hystersis(enum pm8921_usb_debounce_time ms)
+{
+	u8 temp;
+
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+
+	if (ms > PM_USB_DEBOUNCE_80P5MS) {
+		pr_err("limiting debounce to 80.5ms\n");
+		ms = PM_USB_DEBOUNCE_80P5MS;
+	}
+
+	temp = USB_DEBOUNCE_TIME_MASK & (ms << USB_DEBOUNCE_TIME_SHIFT);
+
+	return pm_chg_masked_write(the_chip, USB_OVP_CONTROL,
+				USB_DEBOUNCE_TIME_MASK, temp);
+}
+EXPORT_SYMBOL(pm8921_usb_ovp_set_hystersis);
+
+#define USB_OVP_DISABLE_MASK	0x80
+int pm8921_usb_ovp_disable(int disable)
+{
+	u8 temp = 0;
+
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+
+	if (disable)
+		temp = USB_OVP_DISABLE_MASK;
+
+	return pm_chg_masked_write(the_chip, USB_OVP_CONTROL,
+				USB_OVP_DISABLE_MASK, temp);
+}
+
+bool pm8921_is_battery_charging(int *source)
+{
+	int fsm_state, is_charging, dc_present, usb_present;
+
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+	fsm_state = pm_chg_get_fsm_state(the_chip);
+	is_charging = is_battery_charging(fsm_state);
+	if (is_charging == 0) {
+		*source = PM8921_CHG_SRC_NONE;
+		return is_charging;
+	}
+
+	if (source == NULL)
+		return is_charging;
+
+	/* the battery is charging, the source is requested, find it */
+	dc_present = is_dc_chg_plugged_in(the_chip);
+	usb_present = is_usb_chg_plugged_in(the_chip);
+
+	if (dc_present && !usb_present)
+		*source = PM8921_CHG_SRC_DC;
+
+	if (usb_present && !dc_present)
+		*source = PM8921_CHG_SRC_USB;
+
+	if (usb_present && dc_present)
+		/*
+		 * The system always chooses dc for charging since it has
+		 * higher priority.
+		 */
+		*source = PM8921_CHG_SRC_DC;
+
+	return is_charging;
+}
+EXPORT_SYMBOL(pm8921_is_battery_charging);
+
+int pm8921_set_usb_power_supply_type(enum power_supply_type type)
+{
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+
+	if (type < POWER_SUPPLY_TYPE_USB)
+		return -EINVAL;
+
+	the_chip->usb_psy.type = type;
+	power_supply_changed(&the_chip->usb_psy);
+	power_supply_changed(&the_chip->dc_psy);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pm8921_set_usb_power_supply_type);
+
+int pm8921_batt_temperature(void)
+{
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+	return get_prop_batt_temp(the_chip);
+}
+
+static void handle_usb_insertion_removal(struct pm8921_chg_chip *chip)
+{
+	int usb_present;
+
+	pm_chg_failed_clear(chip, 1);
+	usb_present = is_usb_chg_plugged_in(chip);
+	if (chip->usb_present ^ usb_present) {
+		notify_usb_of_the_plugin_event(usb_present);
+		chip->usb_present = usb_present;
+		power_supply_changed(&chip->usb_psy);
+		power_supply_changed(&chip->batt_psy);
+		pm8921_bms_calibrate_hkadc();
+	}
+	if (usb_present) {
+		schedule_delayed_work(&chip->unplug_check_work,
+			round_jiffies_relative(msecs_to_jiffies
+				(UNPLUG_CHECK_WAIT_PERIOD_MS)));
+		pm8921_chg_enable_irq(chip, CHG_GONE_IRQ);
+	} else {
+		/* USB unplugged reset target current */
+		usb_target_ma = 0;
+		pm8921_chg_disable_irq(chip, CHG_GONE_IRQ);
+	}
+	enable_input_voltage_regulation(chip);
+	bms_notify_check(chip);
+}
+
+static void handle_stop_ext_chg(struct pm8921_chg_chip *chip)
+{
+	if (!chip->ext_psy) {
+		pr_debug("external charger not registered.\n");
+		return;
+	}
+
+	if (!chip->ext_charging) {
+		pr_debug("already not charging.\n");
+		return;
+	}
+
+	power_supply_set_charge_type(chip->ext_psy,
+					POWER_SUPPLY_CHARGE_TYPE_NONE);
+	pm8921_disable_source_current(false); /* release BATFET */
+	power_supply_changed(&chip->dc_psy);
+	chip->ext_charging = false;
+	chip->ext_charge_done = false;
+	bms_notify_check(chip);
+	/* Update battery charging LEDs and user space battery info */
+	power_supply_changed(&chip->batt_psy);
+}
+
+static void handle_start_ext_chg(struct pm8921_chg_chip *chip)
+{
+	int dc_present;
+	int batt_present;
+	int batt_temp_ok;
+	int vbat_ov;
+	unsigned long delay =
+		round_jiffies_relative(msecs_to_jiffies(EOC_CHECK_PERIOD_MS));
+
+	if (!chip->ext_psy) {
+		pr_debug("external charger not registered.\n");
+		return;
+	}
+
+	if (chip->ext_charging) {
+		pr_debug("already charging.\n");
+		return;
+	}
+
+	dc_present = is_dc_chg_plugged_in(the_chip);
+	batt_present = pm_chg_get_rt_status(chip, BATT_INSERTED_IRQ);
+	batt_temp_ok = pm_chg_get_rt_status(chip, BAT_TEMP_OK_IRQ);
+
+	if (!dc_present) {
+		pr_warn("%s. dc not present.\n", __func__);
+		return;
+	}
+	if (!batt_present) {
+		pr_warn("%s. battery not present.\n", __func__);
+		return;
+	}
+	if (!batt_temp_ok) {
+		pr_warn("%s. battery temperature not ok.\n", __func__);
+		return;
+	}
+	pm8921_disable_source_current(true); /* Force BATFET=ON */
+	vbat_ov = pm_chg_get_rt_status(chip, VBAT_OV_IRQ);
+	if (vbat_ov) {
+		pr_warn("%s. battery over voltage.\n", __func__);
+		return;
+	}
+
+	power_supply_set_online(chip->ext_psy, dc_present);
+	power_supply_set_charge_type(chip->ext_psy,
+					POWER_SUPPLY_CHARGE_TYPE_FAST);
+	power_supply_changed(&chip->dc_psy);
+	chip->ext_charging = true;
+	chip->ext_charge_done = false;
+	bms_notify_check(chip);
+	/* Start BMS */
+	schedule_delayed_work(&chip->eoc_work, delay);
+	wake_lock(&chip->eoc_wake_lock);
+	/* Update battery charging LEDs and user space battery info */
+	power_supply_changed(&chip->batt_psy);
+}
+
+static void turn_off_usb_ovp_fet(struct pm8921_chg_chip *chip)
+{
+	u8 temp;
+	int rc;
+
+	rc = pm8xxx_writeb(chip->dev->parent, USB_OVP_TEST, 0x30);
+	if (rc) {
+		pr_err("Failed to write 0x30 to USB_OVP_TEST rc = %d\n", rc);
+		return;
+	}
+	rc = pm8xxx_readb(chip->dev->parent, USB_OVP_TEST, &temp);
+	if (rc) {
+		pr_err("Failed to read from USB_OVP_TEST rc = %d\n", rc);
+		return;
+	}
+	/* set ovp fet disable bit and the write bit */
+	temp |= 0x81;
+	rc = pm8xxx_writeb(chip->dev->parent, USB_OVP_TEST, temp);
+	if (rc) {
+		pr_err("Failed to write 0x%x USB_OVP_TEST rc=%d\n", temp, rc);
+		return;
+	}
+}
+
+static void turn_on_usb_ovp_fet(struct pm8921_chg_chip *chip)
+{
+	u8 temp;
+	int rc;
+
+	rc = pm8xxx_writeb(chip->dev->parent, USB_OVP_TEST, 0x30);
+	if (rc) {
+		pr_err("Failed to write 0x30 to USB_OVP_TEST rc = %d\n", rc);
+		return;
+	}
+	rc = pm8xxx_readb(chip->dev->parent, USB_OVP_TEST, &temp);
+	if (rc) {
+		pr_err("Failed to read from USB_OVP_TEST rc = %d\n", rc);
+		return;
+	}
+	/* unset ovp fet disable bit and set the write bit */
+	temp &= 0xFE;
+	temp |= 0x80;
+	rc = pm8xxx_writeb(chip->dev->parent, USB_OVP_TEST, temp);
+	if (rc) {
+		pr_err("Failed to write 0x%x to USB_OVP_TEST rc = %d\n",
+								temp, rc);
+		return;
+	}
+}
+
+static int param_open_ovp_counter = 10;
+module_param(param_open_ovp_counter, int, 0644);
+
+#define WRITE_BANK_4		0xC0
+#define USB_OVP_DEBOUNCE_TIME 0x06
+static void unplug_ovp_fet_open(struct pm8921_chg_chip *chip)
+{
+	int chg_gone = 0, usb_chg_plugged_in = 0;
+	int count = 0;
+
+	while (count++ < param_open_ovp_counter) {
+		pm_chg_masked_write(chip, USB_OVP_CONTROL,
+						USB_OVP_DEBOUNCE_TIME, 0x0);
+		usleep(10);
+		usb_chg_plugged_in = is_usb_chg_plugged_in(chip);
+		chg_gone = pm_chg_get_rt_status(chip, CHG_GONE_IRQ);
+		pr_debug("OVP FET count = %d chg_gone=%d, usb_valid = %d\n",
+					count, chg_gone, usb_chg_plugged_in);
+
+		/* note usb_chg_plugged_in=0 => chg_gone=1 */
+		if (chg_gone == 1 && usb_chg_plugged_in == 1) {
+			pr_debug("since chg_gone = 1 dis ovp_fet for 20msec\n");
+			turn_off_usb_ovp_fet(chip);
+
+			msleep(20);
+
+			turn_on_usb_ovp_fet(chip);
+		} else {
+			break;
+		}
+	}
+	pm_chg_masked_write(chip, USB_OVP_CONTROL,
+		USB_OVP_DEBOUNCE_TIME, 0x2);
+	pr_debug("Exit count=%d chg_gone=%d, usb_valid=%d\n",
+		count, chg_gone, usb_chg_plugged_in);
+	return;
+}
+
+static int find_usb_ma_value(int value)
+{
+	int i;
+
+	for (i = ARRAY_SIZE(usb_ma_table) - 1; i >= 0; i--) {
+		if (value >= usb_ma_table[i].usb_ma)
+			break;
+	}
+
+	return i;
+}
+
+static void decrease_usb_ma_value(int *value)
+{
+	int i;
+
+	if (value) {
+		i = find_usb_ma_value(*value);
+		if (i > 0)
+			i--;
+		*value = usb_ma_table[i].usb_ma;
+	}
+}
+
+static void increase_usb_ma_value(int *value)
+{
+	int i;
+
+	if (value) {
+		i = find_usb_ma_value(*value);
+
+		if (i < (ARRAY_SIZE(usb_ma_table) - 1))
+			i++;
+		/* Get next correct entry if IUSB_FINE_RES is not available */
+		while (!the_chip->iusb_fine_res
+			&& (usb_ma_table[i].value & PM8917_IUSB_FINE_RES)
+			&& i < (ARRAY_SIZE(usb_ma_table) - 1))
+			i++;
+
+		*value = usb_ma_table[i].usb_ma;
+	}
+}
+
+static void vin_collapse_check_worker(struct work_struct *work)
+{
+	struct delayed_work *dwork = to_delayed_work(work);
+	struct pm8921_chg_chip *chip = container_of(dwork,
+			struct pm8921_chg_chip, vin_collapse_check_work);
+
+	/* AICL only for wall-chargers */
+	if (is_usb_chg_plugged_in(chip) &&
+		usb_target_ma > USB_WALL_THRESHOLD_MA) {
+		/* decrease usb_target_ma */
+		decrease_usb_ma_value(&usb_target_ma);
+		/* reset here, increase in unplug_check_worker */
+		__pm8921_charger_vbus_draw(USB_WALL_THRESHOLD_MA);
+		pr_debug("usb_now=%d, usb_target = %d\n",
+				USB_WALL_THRESHOLD_MA, usb_target_ma);
+	} else {
+		handle_usb_insertion_removal(chip);
+	}
+}
+
+#define VIN_MIN_COLLAPSE_CHECK_MS	50
+static irqreturn_t usbin_valid_irq_handler(int irq, void *data)
+{
+	if (usb_target_ma)
+		schedule_delayed_work(&the_chip->vin_collapse_check_work,
+				      round_jiffies_relative(msecs_to_jiffies
+						(VIN_MIN_COLLAPSE_CHECK_MS)));
+	else
+	    handle_usb_insertion_removal(data);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t usbin_ov_irq_handler(int irq, void *data)
+{
+	pr_err("USB OverVoltage\n");
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t batt_inserted_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+	int status;
+
+	status = pm_chg_get_rt_status(chip, BATT_INSERTED_IRQ);
+	schedule_work(&chip->battery_id_valid_work);
+	handle_start_ext_chg(chip);
+	pr_debug("battery present=%d", status);
+	power_supply_changed(&chip->batt_psy);
+	return IRQ_HANDLED;
+}
+
+/*
+ * this interrupt used to restart charging a battery.
+ *
+ * Note: When DC-inserted the VBAT can't go low.
+ * VPH_PWR is provided by the ext-charger.
+ * After End-Of-Charging from DC, charging can be resumed only
+ * if DC is removed and then inserted after the battery was in use.
+ * Therefore the handle_start_ext_chg() is not called.
+ */
+static irqreturn_t vbatdet_low_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+	int high_transition;
+
+	high_transition = pm_chg_get_rt_status(chip, VBATDET_LOW_IRQ);
+
+	if (high_transition) {
+		/* enable auto charging */
+		pm_chg_auto_enable(chip, !charging_disabled);
+		pr_info("batt fell below resume voltage %s\n",
+			charging_disabled ? "" : "charger enabled");
+	}
+	pr_debug("fsm_state=%d\n", pm_chg_get_fsm_state(data));
+
+	power_supply_changed(&chip->batt_psy);
+	power_supply_changed(&chip->usb_psy);
+	power_supply_changed(&chip->dc_psy);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t usbin_uv_irq_handler(int irq, void *data)
+{
+	pr_err("USB UnderVoltage\n");
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t vbat_ov_irq_handler(int irq, void *data)
+{
+	pr_debug("fsm_state=%d\n", pm_chg_get_fsm_state(data));
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t chgwdog_irq_handler(int irq, void *data)
+{
+	pr_debug("fsm_state=%d\n", pm_chg_get_fsm_state(data));
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t vcp_irq_handler(int irq, void *data)
+{
+	pr_debug("fsm_state=%d\n", pm_chg_get_fsm_state(data));
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t atcdone_irq_handler(int irq, void *data)
+{
+	pr_debug("fsm_state=%d\n", pm_chg_get_fsm_state(data));
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t atcfail_irq_handler(int irq, void *data)
+{
+	pr_debug("fsm_state=%d\n", pm_chg_get_fsm_state(data));
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t chgdone_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+
+	pr_debug("state_changed_to=%d\n", pm_chg_get_fsm_state(data));
+
+	handle_stop_ext_chg(chip);
+
+	power_supply_changed(&chip->batt_psy);
+	power_supply_changed(&chip->usb_psy);
+	power_supply_changed(&chip->dc_psy);
+
+	bms_notify_check(chip);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t chgfail_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+	int ret;
+
+	ret = pm_chg_failed_clear(chip, 1);
+	if (ret)
+		pr_err("Failed to write CHG_FAILED_CLEAR bit\n");
+
+	pr_err("batt_present = %d, batt_temp_ok = %d, state_changed_to=%d\n",
+			get_prop_batt_present(chip),
+			pm_chg_get_rt_status(chip, BAT_TEMP_OK_IRQ),
+			pm_chg_get_fsm_state(data));
+
+	power_supply_changed(&chip->batt_psy);
+	power_supply_changed(&chip->usb_psy);
+	power_supply_changed(&chip->dc_psy);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t chgstate_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+
+	pr_debug("state_changed_to=%d\n", pm_chg_get_fsm_state(data));
+	power_supply_changed(&chip->batt_psy);
+	power_supply_changed(&chip->usb_psy);
+	power_supply_changed(&chip->dc_psy);
+
+	bms_notify_check(chip);
+
+	return IRQ_HANDLED;
+}
+
+static int param_vin_disable_counter = 5;
+module_param(param_vin_disable_counter, int, 0644);
+
+static void attempt_reverse_boost_fix(struct pm8921_chg_chip *chip,
+							int count, int usb_ma)
+{
+	__pm8921_charger_vbus_draw(500);
+	pr_debug("count = %d iusb=500mA\n", count);
+	disable_input_voltage_regulation(chip);
+	pr_debug("count = %d disable_input_regulation\n", count);
+
+	msleep(20);
+
+	pr_debug("count = %d end sleep 20ms chg_gone=%d, usb_valid = %d\n",
+				count,
+				pm_chg_get_rt_status(chip, CHG_GONE_IRQ),
+				is_usb_chg_plugged_in(chip));
+	pr_debug("count = %d restoring input regulation and usb_ma = %d\n",
+		 count, usb_ma);
+	enable_input_voltage_regulation(chip);
+	__pm8921_charger_vbus_draw(usb_ma);
+}
+
+#define VIN_ACTIVE_BIT BIT(0)
+#define UNPLUG_WRKARND_RESTORE_WAIT_PERIOD_US 200
+#define VIN_MIN_INCREASE_MV 100
+static void unplug_check_worker(struct work_struct *work)
+{
+	struct delayed_work *dwork = to_delayed_work(work);
+	struct pm8921_chg_chip *chip = container_of(dwork,
+				struct pm8921_chg_chip, unplug_check_work);
+	u8 reg_loop;
+	int ibat, usb_chg_plugged_in, usb_ma;
+	int chg_gone = 0;
+
+	reg_loop = 0;
+	usb_chg_plugged_in = is_usb_chg_plugged_in(chip);
+	if (!usb_chg_plugged_in) {
+		pr_debug("Stopping Unplug Check Worker since USB is removed"
+			"reg_loop = %d, fsm = %d ibat = %d\n",
+			pm_chg_get_regulation_loop(chip),
+			pm_chg_get_fsm_state(chip),
+			get_prop_batt_current(chip)
+			);
+		return;
+	}
+
+	pm_chg_iusbmax_get(chip, &usb_ma);
+	if (usb_ma == 500 && !usb_target_ma) {
+		pr_debug("Stopping Unplug Check Worker since USB == 500mA\n");
+		disable_input_voltage_regulation(chip);
+		return;
+	}
+
+	if (usb_ma <= 100) {
+		pr_debug(
+			"Unenumerated yet or suspended usb_ma = %d skipping\n",
+			usb_ma);
+		goto check_again_later;
+	}
+	if (pm8921_chg_is_enabled(chip, CHG_GONE_IRQ))
+		pr_debug("chg gone irq is enabled\n");
+
+	reg_loop = pm_chg_get_regulation_loop(chip);
+	pr_debug("reg_loop=0x%x usb_ma = %d\n", reg_loop, usb_ma);
+
+	if ((reg_loop & VIN_ACTIVE_BIT) && (usb_ma > USB_WALL_THRESHOLD_MA)) {
+		decrease_usb_ma_value(&usb_ma);
+		usb_target_ma = usb_ma;
+		/* end AICL here */
+		__pm8921_charger_vbus_draw(usb_ma);
+		pr_debug("usb_now=%d, usb_target = %d\n",
+			usb_ma, usb_target_ma);
+	}
+
+	reg_loop = pm_chg_get_regulation_loop(chip);
+	pr_debug("reg_loop=0x%x usb_ma = %d\n", reg_loop, usb_ma);
+
+	if (reg_loop & VIN_ACTIVE_BIT) {
+		ibat = get_prop_batt_current(chip);
+
+		pr_debug("ibat = %d fsm = %d reg_loop = 0x%x\n",
+				ibat, pm_chg_get_fsm_state(chip), reg_loop);
+		if (ibat > 0) {
+			int count = 0;
+
+			while (count++ < param_vin_disable_counter
+					&& usb_chg_plugged_in == 1) {
+				attempt_reverse_boost_fix(chip, count, usb_ma);
+				usb_chg_plugged_in
+					= is_usb_chg_plugged_in(chip);
+			}
+		}
+	}
+
+	usb_chg_plugged_in = is_usb_chg_plugged_in(chip);
+	chg_gone = pm_chg_get_rt_status(chip, CHG_GONE_IRQ);
+
+	if (chg_gone == 1  && usb_chg_plugged_in == 1) {
+		/* run the worker directly */
+		pr_debug(" ver5 step: chg_gone=%d, usb_valid = %d\n",
+						chg_gone, usb_chg_plugged_in);
+		unplug_ovp_fet_open(chip);
+	}
+
+	if (!(reg_loop & VIN_ACTIVE_BIT)) {
+		/* only increase iusb_max if vin loop not active */
+		if (usb_ma < usb_target_ma) {
+			increase_usb_ma_value(&usb_ma);
+			__pm8921_charger_vbus_draw(usb_ma);
+			pr_debug("usb_now=%d, usb_target = %d\n",
+					usb_ma, usb_target_ma);
+		} else {
+			usb_target_ma = usb_ma;
+		}
+	}
+check_again_later:
+	/* schedule to check again later */
+	schedule_delayed_work(&chip->unplug_check_work,
+		      round_jiffies_relative(msecs_to_jiffies
+				(UNPLUG_CHECK_WAIT_PERIOD_MS)));
+}
+
+static irqreturn_t loop_change_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+
+	pr_debug("fsm_state=%d reg_loop=0x%x\n",
+		pm_chg_get_fsm_state(data),
+		pm_chg_get_regulation_loop(data));
+	schedule_work(&chip->unplug_check_work.work);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t fastchg_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+	int high_transition;
+
+	high_transition = pm_chg_get_rt_status(chip, FASTCHG_IRQ);
+	if (high_transition && !delayed_work_pending(&chip->eoc_work)) {
+		wake_lock(&chip->eoc_wake_lock);
+		schedule_delayed_work(&chip->eoc_work,
+				      round_jiffies_relative(msecs_to_jiffies
+						     (EOC_CHECK_PERIOD_MS)));
+	}
+	power_supply_changed(&chip->batt_psy);
+	bms_notify_check(chip);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t trklchg_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+
+	power_supply_changed(&chip->batt_psy);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t batt_removed_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+	int status;
+
+	status = pm_chg_get_rt_status(chip, BATT_REMOVED_IRQ);
+	pr_debug("battery present=%d state=%d", !status,
+					 pm_chg_get_fsm_state(data));
+	handle_stop_ext_chg(chip);
+	power_supply_changed(&chip->batt_psy);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t batttemp_hot_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+
+	handle_stop_ext_chg(chip);
+	power_supply_changed(&chip->batt_psy);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t chghot_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+
+	pr_debug("Chg hot fsm_state=%d\n", pm_chg_get_fsm_state(data));
+	power_supply_changed(&chip->batt_psy);
+	power_supply_changed(&chip->usb_psy);
+	handle_stop_ext_chg(chip);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t batttemp_cold_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+
+	pr_debug("Batt cold fsm_state=%d\n", pm_chg_get_fsm_state(data));
+	handle_stop_ext_chg(chip);
+
+	power_supply_changed(&chip->batt_psy);
+	power_supply_changed(&chip->usb_psy);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t chg_gone_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+	int chg_gone, usb_chg_plugged_in;
+
+	usb_chg_plugged_in = is_usb_chg_plugged_in(chip);
+	chg_gone = pm_chg_get_rt_status(chip, CHG_GONE_IRQ);
+
+	pr_debug("chg_gone=%d, usb_valid = %d\n", chg_gone, usb_chg_plugged_in);
+	pr_debug("Chg gone fsm_state=%d\n", pm_chg_get_fsm_state(data));
+
+	power_supply_changed(&chip->batt_psy);
+	power_supply_changed(&chip->usb_psy);
+	return IRQ_HANDLED;
+}
+/*
+ *
+ * bat_temp_ok_irq_handler - is edge triggered, hence it will
+ * fire for two cases:
+ *
+ * If the interrupt line switches to high temperature is okay
+ * and thus charging begins.
+ * If bat_temp_ok is low this means the temperature is now
+ * too hot or cold, so charging is stopped.
+ *
+ */
+static irqreturn_t bat_temp_ok_irq_handler(int irq, void *data)
+{
+	int bat_temp_ok;
+	struct pm8921_chg_chip *chip = data;
+
+	bat_temp_ok = pm_chg_get_rt_status(chip, BAT_TEMP_OK_IRQ);
+
+	pr_debug("batt_temp_ok = %d fsm_state%d\n",
+			 bat_temp_ok, pm_chg_get_fsm_state(data));
+
+	if (bat_temp_ok)
+		handle_start_ext_chg(chip);
+	else
+		handle_stop_ext_chg(chip);
+
+	power_supply_changed(&chip->batt_psy);
+	power_supply_changed(&chip->usb_psy);
+	bms_notify_check(chip);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t coarse_det_low_irq_handler(int irq, void *data)
+{
+	pr_debug("fsm_state=%d\n", pm_chg_get_fsm_state(data));
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t vdd_loop_irq_handler(int irq, void *data)
+{
+	pr_debug("fsm_state=%d\n", pm_chg_get_fsm_state(data));
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t vreg_ov_irq_handler(int irq, void *data)
+{
+	pr_debug("fsm_state=%d\n", pm_chg_get_fsm_state(data));
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t vbatdet_irq_handler(int irq, void *data)
+{
+	pr_debug("fsm_state=%d\n", pm_chg_get_fsm_state(data));
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t batfet_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+
+	pr_debug("vreg ov\n");
+	power_supply_changed(&chip->batt_psy);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t dcin_valid_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+	int dc_present;
+
+	dc_present = pm_chg_get_rt_status(chip, DCIN_VALID_IRQ);
+	if (chip->ext_psy)
+		power_supply_set_online(chip->ext_psy, dc_present);
+	chip->dc_present = dc_present;
+	if (dc_present)
+		handle_start_ext_chg(chip);
+	else
+		handle_stop_ext_chg(chip);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t dcin_ov_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+
+	handle_stop_ext_chg(chip);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t dcin_uv_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+
+	handle_stop_ext_chg(chip);
+
+	return IRQ_HANDLED;
+}
+
+static int __pm_batt_external_power_changed_work(struct device *dev, void *data)
+{
+	struct power_supply *psy = &the_chip->batt_psy;
+	struct power_supply *epsy = dev_get_drvdata(dev);
+	int i, dcin_irq;
+
+	/* Only search for external supply if none is registered */
+	if (!the_chip->ext_psy) {
+		dcin_irq = the_chip->pmic_chg_irq[DCIN_VALID_IRQ];
+		for (i = 0; i < epsy->num_supplicants; i++) {
+			if (!strncmp(epsy->supplied_to[i], psy->name, 7)) {
+				if (!strncmp(epsy->name, "dc", 2)) {
+					the_chip->ext_psy = epsy;
+					dcin_valid_irq_handler(dcin_irq,
+							the_chip);
+				}
+			}
+		}
+	}
+	return 0;
+}
+
+static void pm_batt_external_power_changed(struct power_supply *psy)
+{
+	/* Only look for an external supply if it hasn't been registered */
+	if (!the_chip->ext_psy)
+		class_for_each_device(power_supply_class, NULL, psy,
+					 __pm_batt_external_power_changed_work);
+}
+
+/**
+ * update_heartbeat - internal function to update userspace
+ *		per update_time minutes
+ *
+ */
+static void update_heartbeat(struct work_struct *work)
+{
+	struct delayed_work *dwork = to_delayed_work(work);
+	struct pm8921_chg_chip *chip = container_of(dwork,
+				struct pm8921_chg_chip, update_heartbeat_work);
+
+	pm_chg_failed_clear(chip, 1);
+	power_supply_changed(&chip->batt_psy);
+	schedule_delayed_work(&chip->update_heartbeat_work,
+			      round_jiffies_relative(msecs_to_jiffies
+						     (chip->update_time)));
+}
+#define VDD_LOOP_ACTIVE_BIT	BIT(3)
+#define VDD_MAX_INCREASE_MV	400
+static int vdd_max_increase_mv = VDD_MAX_INCREASE_MV;
+module_param(vdd_max_increase_mv, int, 0644);
+
+static int ichg_threshold_ua = -400000;
+module_param(ichg_threshold_ua, int, 0644);
+static void adjust_vdd_max_for_fastchg(struct pm8921_chg_chip *chip)
+{
+	int ichg_meas_ua, vbat_uv;
+	int ichg_meas_ma;
+	int adj_vdd_max_mv, programmed_vdd_max;
+	int vbat_batt_terminal_uv;
+	int vbat_batt_terminal_mv;
+	int reg_loop;
+	int delta_mv = 0;
+
+	if (chip->rconn_mohm == 0) {
+		pr_debug("Exiting as rconn_mohm is 0\n");
+		return;
+	}
+	/* adjust vdd_max only in normal temperature zone */
+	if (chip->is_bat_cool || chip->is_bat_warm) {
+		pr_debug("Exiting is_bat_cool = %d is_batt_warm = %d\n",
+				chip->is_bat_cool, chip->is_bat_warm);
+		return;
+	}
+
+	reg_loop = pm_chg_get_regulation_loop(chip);
+	if (!(reg_loop & VDD_LOOP_ACTIVE_BIT)) {
+		pr_debug("Exiting Vdd loop is not active reg loop=0x%x\n",
+			reg_loop);
+		return;
+	}
+
+	pm8921_bms_get_simultaneous_battery_voltage_and_current(&ichg_meas_ua,
+								&vbat_uv);
+	if (ichg_meas_ua >= 0) {
+		pr_debug("Exiting ichg_meas_ua = %d > 0\n", ichg_meas_ua);
+		return;
+	}
+	if (ichg_meas_ua <= ichg_threshold_ua) {
+		pr_debug("Exiting ichg_meas_ua = %d < ichg_threshold_ua = %d\n",
+					ichg_meas_ua, ichg_threshold_ua);
+		return;
+	}
+	ichg_meas_ma = ichg_meas_ua / 1000;
+
+	/* rconn_mohm is in milliOhms */
+	vbat_batt_terminal_uv = vbat_uv + ichg_meas_ma * the_chip->rconn_mohm;
+	vbat_batt_terminal_mv = vbat_batt_terminal_uv/1000;
+	pm_chg_vddmax_get(the_chip, &programmed_vdd_max);
+
+	delta_mv =  chip->max_voltage_mv - vbat_batt_terminal_mv;
+
+	adj_vdd_max_mv = programmed_vdd_max + delta_mv;
+	pr_debug("vdd_max needs to be changed by %d mv from %d to %d\n",
+			delta_mv,
+			programmed_vdd_max,
+			adj_vdd_max_mv);
+
+	if (adj_vdd_max_mv < chip->max_voltage_mv) {
+		pr_debug("adj vdd_max lower than default max voltage\n");
+		return;
+	}
+
+	if (adj_vdd_max_mv > (chip->max_voltage_mv + vdd_max_increase_mv))
+		adj_vdd_max_mv = chip->max_voltage_mv + vdd_max_increase_mv;
+
+	pr_debug("adjusting vdd_max_mv to %d to make "
+		"vbat_batt_termial_uv = %d to %d\n",
+		adj_vdd_max_mv, vbat_batt_terminal_uv, chip->max_voltage_mv);
+	pm_chg_vddmax_set(chip, adj_vdd_max_mv);
+}
+
+enum {
+	CHG_IN_PROGRESS,
+	CHG_NOT_IN_PROGRESS,
+	CHG_FINISHED,
+};
+
+#define VBAT_TOLERANCE_MV	70
+#define CHG_DISABLE_MSLEEP	100
+static int is_charging_finished(struct pm8921_chg_chip *chip)
+{
+	int vbat_meas_uv, vbat_meas_mv, vbat_programmed, vbatdet_low;
+	int ichg_meas_ma, iterm_programmed;
+	int regulation_loop, fast_chg, vcp;
+	int rc;
+	static int last_vbat_programmed = -EINVAL;
+
+	if (!is_ext_charging(chip)) {
+		/* return if the battery is not being fastcharged */
+		fast_chg = pm_chg_get_rt_status(chip, FASTCHG_IRQ);
+		pr_debug("fast_chg = %d\n", fast_chg);
+		if (fast_chg == 0)
+			return CHG_NOT_IN_PROGRESS;
+
+		vcp = pm_chg_get_rt_status(chip, VCP_IRQ);
+		pr_debug("vcp = %d\n", vcp);
+		if (vcp == 1)
+			return CHG_IN_PROGRESS;
+
+		vbatdet_low = pm_chg_get_rt_status(chip, VBATDET_LOW_IRQ);
+		pr_debug("vbatdet_low = %d\n", vbatdet_low);
+		if (vbatdet_low == 1)
+			return CHG_IN_PROGRESS;
+
+		/* reset count if battery is hot/cold */
+		rc = pm_chg_get_rt_status(chip, BAT_TEMP_OK_IRQ);
+		pr_debug("batt_temp_ok = %d\n", rc);
+		if (rc == 0)
+			return CHG_IN_PROGRESS;
+
+		/* reset count if battery voltage is less than vddmax */
+		vbat_meas_uv = get_prop_battery_uvolts(chip);
+		if (vbat_meas_uv < 0)
+			return CHG_IN_PROGRESS;
+		vbat_meas_mv = vbat_meas_uv / 1000;
+
+		rc = pm_chg_vddmax_get(chip, &vbat_programmed);
+		if (rc) {
+			pr_err("couldnt read vddmax rc = %d\n", rc);
+			return CHG_IN_PROGRESS;
+		}
+		pr_debug("vddmax = %d vbat_meas_mv=%d\n",
+			 vbat_programmed, vbat_meas_mv);
+
+		if (last_vbat_programmed == -EINVAL)
+			last_vbat_programmed = vbat_programmed;
+		if (last_vbat_programmed !=  vbat_programmed) {
+			/* vddmax changed, reset and check again */
+			pr_debug("vddmax = %d last_vdd_max=%d\n",
+				 vbat_programmed, last_vbat_programmed);
+			last_vbat_programmed = vbat_programmed;
+			return CHG_IN_PROGRESS;
+		}
+
+		regulation_loop = pm_chg_get_regulation_loop(chip);
+		if (regulation_loop < 0) {
+			pr_err("couldnt read the regulation loop err=%d\n",
+				regulation_loop);
+			return CHG_IN_PROGRESS;
+		}
+		pr_debug("regulation_loop=%d\n", regulation_loop);
+
+		if (regulation_loop != 0 && regulation_loop != VDD_LOOP)
+			return CHG_IN_PROGRESS;
+	} /* !is_ext_charging */
+
+	/* reset count if battery chg current is more than iterm */
+	rc = pm_chg_iterm_get(chip, &iterm_programmed);
+	if (rc) {
+		pr_err("couldnt read iterm rc = %d\n", rc);
+		return CHG_IN_PROGRESS;
+	}
+
+	ichg_meas_ma = (get_prop_batt_current(chip)) / 1000;
+	pr_debug("iterm_programmed = %d ichg_meas_ma=%d\n",
+				iterm_programmed, ichg_meas_ma);
+	/*
+	 * ichg_meas_ma < 0 means battery is drawing current
+	 * ichg_meas_ma > 0 means battery is providing current
+	 */
+	if (ichg_meas_ma > 0)
+		return CHG_IN_PROGRESS;
+
+	if (ichg_meas_ma * -1 > iterm_programmed)
+		return CHG_IN_PROGRESS;
+
+	return CHG_FINISHED;
+}
+
+/**
+ * eoc_worker - internal function to check if battery EOC
+ *		has happened
+ *
+ * If all conditions favouring, if the charge current is
+ * less than the term current for three consecutive times
+ * an EOC has happened.
+ * The wakelock is released if there is no need to reshedule
+ * - this happens when the battery is removed or EOC has
+ * happened
+ */
+#define CONSECUTIVE_COUNT	3
+static void eoc_worker(struct work_struct *work)
+{
+	struct delayed_work *dwork = to_delayed_work(work);
+	struct pm8921_chg_chip *chip = container_of(dwork,
+				struct pm8921_chg_chip, eoc_work);
+	static int count;
+	int end;
+
+	pm_chg_failed_clear(chip, 1);
+	end = is_charging_finished(chip);
+
+	if (end == CHG_NOT_IN_PROGRESS) {
+		count = 0;
+		wake_unlock(&chip->eoc_wake_lock);
+		return;
+	}
+
+	if (end == CHG_FINISHED) {
+		count++;
+	} else {
+		count = 0;
+	}
+
+	if (count == CONSECUTIVE_COUNT) {
+		count = 0;
+		pr_info("End of Charging\n");
+
+		pm_chg_auto_enable(chip, 0);
+
+		if (is_ext_charging(chip))
+			chip->ext_charge_done = true;
+
+		if (chip->is_bat_warm || chip->is_bat_cool)
+			chip->bms_notify.is_battery_full = 0;
+		else
+			chip->bms_notify.is_battery_full = 1;
+		/* declare end of charging by invoking chgdone interrupt */
+		chgdone_irq_handler(chip->pmic_chg_irq[CHGDONE_IRQ], chip);
+		wake_unlock(&chip->eoc_wake_lock);
+	} else {
+		adjust_vdd_max_for_fastchg(chip);
+		pr_debug("EOC count = %d\n", count);
+		schedule_delayed_work(&chip->eoc_work,
+			      round_jiffies_relative(msecs_to_jiffies
+						     (EOC_CHECK_PERIOD_MS)));
+	}
+}
+
+static void btm_configure_work(struct work_struct *work)
+{
+	int rc;
+
+	rc = pm8xxx_adc_btm_configure(&btm_config);
+	if (rc)
+		pr_err("failed to configure btm rc=%d", rc);
+}
+
+DECLARE_WORK(btm_config_work, btm_configure_work);
+
+static void set_appropriate_battery_current(struct pm8921_chg_chip *chip)
+{
+	unsigned int chg_current = chip->max_bat_chg_current;
+
+	if (chip->is_bat_cool)
+		chg_current = min(chg_current, chip->cool_bat_chg_current);
+
+	if (chip->is_bat_warm)
+		chg_current = min(chg_current, chip->warm_bat_chg_current);
+
+	if (thermal_mitigation != 0 && chip->thermal_mitigation)
+		chg_current = min(chg_current,
+				chip->thermal_mitigation[thermal_mitigation]);
+
+	pm_chg_ibatmax_set(the_chip, chg_current);
+}
+
+#define TEMP_HYSTERISIS_DEGC 2
+static void battery_cool(bool enter)
+{
+	pr_debug("enter = %d\n", enter);
+	if (enter == the_chip->is_bat_cool)
+		return;
+	the_chip->is_bat_cool = enter;
+	if (enter) {
+		btm_config.low_thr_temp =
+			the_chip->cool_temp_dc + TEMP_HYSTERISIS_DEGC;
+		set_appropriate_battery_current(the_chip);
+		pm_chg_vddmax_set(the_chip, the_chip->cool_bat_voltage);
+		pm_chg_vbatdet_set(the_chip,
+			the_chip->cool_bat_voltage
+			- the_chip->resume_voltage_delta);
+	} else {
+		btm_config.low_thr_temp = the_chip->cool_temp_dc;
+		set_appropriate_battery_current(the_chip);
+		pm_chg_vddmax_set(the_chip, the_chip->max_voltage_mv);
+		pm_chg_vbatdet_set(the_chip,
+			the_chip->max_voltage_mv
+			- the_chip->resume_voltage_delta);
+	}
+	schedule_work(&btm_config_work);
+}
+
+static void battery_warm(bool enter)
+{
+	pr_debug("enter = %d\n", enter);
+	if (enter == the_chip->is_bat_warm)
+		return;
+	the_chip->is_bat_warm = enter;
+	if (enter) {
+		btm_config.high_thr_temp =
+			the_chip->warm_temp_dc - TEMP_HYSTERISIS_DEGC;
+		set_appropriate_battery_current(the_chip);
+		pm_chg_vddmax_set(the_chip, the_chip->warm_bat_voltage);
+		pm_chg_vbatdet_set(the_chip,
+			the_chip->warm_bat_voltage
+			- the_chip->resume_voltage_delta);
+	} else {
+		btm_config.high_thr_temp = the_chip->warm_temp_dc;
+		set_appropriate_battery_current(the_chip);
+		pm_chg_vddmax_set(the_chip, the_chip->max_voltage_mv);
+		pm_chg_vbatdet_set(the_chip,
+			the_chip->max_voltage_mv
+			- the_chip->resume_voltage_delta);
+	}
+	schedule_work(&btm_config_work);
+}
+
+static int configure_btm(struct pm8921_chg_chip *chip)
+{
+	int rc;
+
+	if (chip->warm_temp_dc != INT_MIN)
+		btm_config.btm_warm_fn = battery_warm;
+	else
+		btm_config.btm_warm_fn = NULL;
+
+	if (chip->cool_temp_dc != INT_MIN)
+		btm_config.btm_cool_fn = battery_cool;
+	else
+		btm_config.btm_cool_fn = NULL;
+
+	btm_config.low_thr_temp = chip->cool_temp_dc;
+	btm_config.high_thr_temp = chip->warm_temp_dc;
+	btm_config.interval = chip->temp_check_period;
+	rc = pm8xxx_adc_btm_configure(&btm_config);
+	if (rc)
+		pr_err("failed to configure btm rc = %d\n", rc);
+	rc = pm8xxx_adc_btm_start();
+	if (rc)
+		pr_err("failed to start btm rc = %d\n", rc);
+
+	return rc;
+}
+
+/**
+ * set_disable_status_param -
+ *
+ * Internal function to disable battery charging and also disable drawing
+ * any current from the source. The device is forced to run on a battery
+ * after this.
+ */
+static int set_disable_status_param(const char *val, struct kernel_param *kp)
+{
+	int ret;
+	struct pm8921_chg_chip *chip = the_chip;
+
+	ret = param_set_int(val, kp);
+	if (ret) {
+		pr_err("error setting value %d\n", ret);
+		return ret;
+	}
+	pr_info("factory set disable param to %d\n", charging_disabled);
+	if (chip) {
+		pm_chg_auto_enable(chip, !charging_disabled);
+		pm_chg_charge_dis(chip, charging_disabled);
+	}
+	return 0;
+}
+module_param_call(disabled, set_disable_status_param, param_get_uint,
+					&charging_disabled, 0644);
+
+static int rconn_mohm;
+static int set_rconn_mohm(const char *val, struct kernel_param *kp)
+{
+	int ret;
+	struct pm8921_chg_chip *chip = the_chip;
+
+	ret = param_set_int(val, kp);
+	if (ret) {
+		pr_err("error setting value %d\n", ret);
+		return ret;
+	}
+	if (chip)
+		chip->rconn_mohm = rconn_mohm;
+	return 0;
+}
+module_param_call(rconn_mohm, set_rconn_mohm, param_get_uint,
+					&rconn_mohm, 0644);
+/**
+ * set_thermal_mitigation_level -
+ *
+ * Internal function to control battery charging current to reduce
+ * temperature
+ */
+static int set_therm_mitigation_level(const char *val, struct kernel_param *kp)
+{
+	int ret;
+	struct pm8921_chg_chip *chip = the_chip;
+
+	ret = param_set_int(val, kp);
+	if (ret) {
+		pr_err("error setting value %d\n", ret);
+		return ret;
+	}
+
+	if (!chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+
+	if (!chip->thermal_mitigation) {
+		pr_err("no thermal mitigation\n");
+		return -EINVAL;
+	}
+
+	if (thermal_mitigation < 0
+		|| thermal_mitigation >= chip->thermal_levels) {
+		pr_err("out of bound level selected\n");
+		return -EINVAL;
+	}
+
+	set_appropriate_battery_current(chip);
+	return ret;
+}
+module_param_call(thermal_mitigation, set_therm_mitigation_level,
+					param_get_uint,
+					&thermal_mitigation, 0644);
+
+static int set_usb_max_current(const char *val, struct kernel_param *kp)
+{
+	int ret, mA;
+	struct pm8921_chg_chip *chip = the_chip;
+
+	ret = param_set_int(val, kp);
+	if (ret) {
+		pr_err("error setting value %d\n", ret);
+		return ret;
+	}
+	if (chip) {
+		pr_warn("setting current max to %d\n", usb_max_current);
+		pm_chg_iusbmax_get(chip, &mA);
+		if (mA > usb_max_current)
+			pm8921_charger_vbus_draw(usb_max_current);
+		return 0;
+	}
+	return -EINVAL;
+}
+module_param_call(usb_max_current, set_usb_max_current,
+	param_get_uint, &usb_max_current, 0644);
+
+static void free_irqs(struct pm8921_chg_chip *chip)
+{
+	int i;
+
+	for (i = 0; i < PM_CHG_MAX_INTS; i++)
+		if (chip->pmic_chg_irq[i]) {
+			free_irq(chip->pmic_chg_irq[i], chip);
+			chip->pmic_chg_irq[i] = 0;
+		}
+}
+
+/* determines the initial present states */
+static void __devinit determine_initial_state(struct pm8921_chg_chip *chip)
+{
+	unsigned long flags;
+	int fsm_state;
+
+	chip->dc_present = !!is_dc_chg_plugged_in(chip);
+	chip->usb_present = !!is_usb_chg_plugged_in(chip);
+
+	notify_usb_of_the_plugin_event(chip->usb_present);
+	if (chip->usb_present) {
+		schedule_delayed_work(&chip->unplug_check_work,
+			round_jiffies_relative(msecs_to_jiffies
+				(UNPLUG_CHECK_WAIT_PERIOD_MS)));
+		pm8921_chg_enable_irq(chip, CHG_GONE_IRQ);
+	}
+
+	pm8921_chg_enable_irq(chip, DCIN_VALID_IRQ);
+	pm8921_chg_enable_irq(chip, USBIN_VALID_IRQ);
+	pm8921_chg_enable_irq(chip, BATT_REMOVED_IRQ);
+	pm8921_chg_enable_irq(chip, BATT_INSERTED_IRQ);
+	pm8921_chg_enable_irq(chip, USBIN_OV_IRQ);
+	pm8921_chg_enable_irq(chip, USBIN_UV_IRQ);
+	pm8921_chg_enable_irq(chip, DCIN_OV_IRQ);
+	pm8921_chg_enable_irq(chip, DCIN_UV_IRQ);
+	pm8921_chg_enable_irq(chip, CHGFAIL_IRQ);
+	pm8921_chg_enable_irq(chip, FASTCHG_IRQ);
+	pm8921_chg_enable_irq(chip, VBATDET_LOW_IRQ);
+	pm8921_chg_enable_irq(chip, BAT_TEMP_OK_IRQ);
+
+	spin_lock_irqsave(&vbus_lock, flags);
+	if (usb_chg_current) {
+		/* reissue a vbus draw call */
+		__pm8921_charger_vbus_draw(usb_chg_current);
+		fastchg_irq_handler(chip->pmic_chg_irq[FASTCHG_IRQ], chip);
+	}
+	spin_unlock_irqrestore(&vbus_lock, flags);
+
+	fsm_state = pm_chg_get_fsm_state(chip);
+	if (is_battery_charging(fsm_state)) {
+		chip->bms_notify.is_charging = 1;
+		pm8921_bms_charging_began();
+	}
+
+	check_battery_valid(chip);
+
+	pr_debug("usb = %d, dc = %d  batt = %d state=%d\n",
+			chip->usb_present,
+			chip->dc_present,
+			get_prop_batt_present(chip),
+			fsm_state);
+}
+
+struct pm_chg_irq_init_data {
+	unsigned int	irq_id;
+	char		*name;
+	unsigned long	flags;
+	irqreturn_t	(*handler)(int, void *);
+};
+
+#define CHG_IRQ(_id, _flags, _handler) \
+{ \
+	.irq_id		= _id, \
+	.name		= #_id, \
+	.flags		= _flags, \
+	.handler	= _handler, \
+}
+struct pm_chg_irq_init_data chg_irq_data[] = {
+	CHG_IRQ(USBIN_VALID_IRQ, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+						usbin_valid_irq_handler),
+	CHG_IRQ(USBIN_OV_IRQ, IRQF_TRIGGER_RISING, usbin_ov_irq_handler),
+	CHG_IRQ(BATT_INSERTED_IRQ, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+						batt_inserted_irq_handler),
+	CHG_IRQ(VBATDET_LOW_IRQ, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+						vbatdet_low_irq_handler),
+	CHG_IRQ(USBIN_UV_IRQ, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+							usbin_uv_irq_handler),
+	CHG_IRQ(VBAT_OV_IRQ, IRQF_TRIGGER_RISING, vbat_ov_irq_handler),
+	CHG_IRQ(CHGWDOG_IRQ, IRQF_TRIGGER_RISING, chgwdog_irq_handler),
+	CHG_IRQ(VCP_IRQ, IRQF_TRIGGER_RISING, vcp_irq_handler),
+	CHG_IRQ(ATCDONE_IRQ, IRQF_TRIGGER_RISING, atcdone_irq_handler),
+	CHG_IRQ(ATCFAIL_IRQ, IRQF_TRIGGER_RISING, atcfail_irq_handler),
+	CHG_IRQ(CHGDONE_IRQ, IRQF_TRIGGER_RISING, chgdone_irq_handler),
+	CHG_IRQ(CHGFAIL_IRQ, IRQF_TRIGGER_RISING, chgfail_irq_handler),
+	CHG_IRQ(CHGSTATE_IRQ, IRQF_TRIGGER_RISING, chgstate_irq_handler),
+	CHG_IRQ(LOOP_CHANGE_IRQ, IRQF_TRIGGER_RISING, loop_change_irq_handler),
+	CHG_IRQ(FASTCHG_IRQ, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+						fastchg_irq_handler),
+	CHG_IRQ(TRKLCHG_IRQ, IRQF_TRIGGER_RISING, trklchg_irq_handler),
+	CHG_IRQ(BATT_REMOVED_IRQ, IRQF_TRIGGER_RISING,
+						batt_removed_irq_handler),
+	CHG_IRQ(BATTTEMP_HOT_IRQ, IRQF_TRIGGER_RISING,
+						batttemp_hot_irq_handler),
+	CHG_IRQ(CHGHOT_IRQ, IRQF_TRIGGER_RISING, chghot_irq_handler),
+	CHG_IRQ(BATTTEMP_COLD_IRQ, IRQF_TRIGGER_RISING,
+						batttemp_cold_irq_handler),
+	CHG_IRQ(CHG_GONE_IRQ, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+						chg_gone_irq_handler),
+	CHG_IRQ(BAT_TEMP_OK_IRQ, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+						bat_temp_ok_irq_handler),
+	CHG_IRQ(COARSE_DET_LOW_IRQ, IRQF_TRIGGER_RISING,
+						coarse_det_low_irq_handler),
+	CHG_IRQ(VDD_LOOP_IRQ, IRQF_TRIGGER_RISING, vdd_loop_irq_handler),
+	CHG_IRQ(VREG_OV_IRQ, IRQF_TRIGGER_RISING, vreg_ov_irq_handler),
+	CHG_IRQ(VBATDET_IRQ, IRQF_TRIGGER_RISING, vbatdet_irq_handler),
+	CHG_IRQ(BATFET_IRQ, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+						batfet_irq_handler),
+	CHG_IRQ(DCIN_VALID_IRQ, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+						dcin_valid_irq_handler),
+	CHG_IRQ(DCIN_OV_IRQ, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+						dcin_ov_irq_handler),
+	CHG_IRQ(DCIN_UV_IRQ, IRQF_TRIGGER_RISING, dcin_uv_irq_handler),
+};
+
+static int __devinit request_irqs(struct pm8921_chg_chip *chip,
+					struct platform_device *pdev)
+{
+	struct resource *res;
+	int ret, i;
+
+	ret = 0;
+	bitmap_fill(chip->enabled_irqs, PM_CHG_MAX_INTS);
+
+	for (i = 0; i < ARRAY_SIZE(chg_irq_data); i++) {
+		res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+				chg_irq_data[i].name);
+		if (res == NULL) {
+			pr_err("couldn't find %s\n", chg_irq_data[i].name);
+			goto err_out;
+		}
+		chip->pmic_chg_irq[chg_irq_data[i].irq_id] = res->start;
+		ret = request_irq(res->start, chg_irq_data[i].handler,
+			chg_irq_data[i].flags,
+			chg_irq_data[i].name, chip);
+		if (ret < 0) {
+			pr_err("couldn't request %d (%s) %d\n", res->start,
+					chg_irq_data[i].name, ret);
+			chip->pmic_chg_irq[chg_irq_data[i].irq_id] = 0;
+			goto err_out;
+		}
+		pm8921_chg_disable_irq(chip, chg_irq_data[i].irq_id);
+	}
+	return 0;
+
+err_out:
+	free_irqs(chip);
+	return -EINVAL;
+}
+
+static void pm8921_chg_force_19p2mhz_clk(struct pm8921_chg_chip *chip)
+{
+	int err;
+	u8 temp;
+
+	temp  = 0xD1;
+	err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
+	if (err) {
+		pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
+		return;
+	}
+
+	temp  = 0xD3;
+	err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
+	if (err) {
+		pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
+		return;
+	}
+
+	temp  = 0xD1;
+	err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
+	if (err) {
+		pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
+		return;
+	}
+
+	temp  = 0xD5;
+	err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
+	if (err) {
+		pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
+		return;
+	}
+
+	udelay(183);
+
+	temp  = 0xD1;
+	err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
+	if (err) {
+		pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
+		return;
+	}
+
+	temp  = 0xD0;
+	err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
+	if (err) {
+		pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
+		return;
+	}
+	udelay(32);
+
+	temp  = 0xD1;
+	err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
+	if (err) {
+		pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
+		return;
+	}
+
+	temp  = 0xD3;
+	err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
+	if (err) {
+		pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
+		return;
+	}
+}
+
+static void pm8921_chg_set_hw_clk_switching(struct pm8921_chg_chip *chip)
+{
+	int err;
+	u8 temp;
+
+	temp  = 0xD1;
+	err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
+	if (err) {
+		pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
+		return;
+	}
+
+	temp  = 0xD0;
+	err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
+	if (err) {
+		pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
+		return;
+	}
+}
+
+#define ENUM_TIMER_STOP_BIT	BIT(1)
+#define BOOT_DONE_BIT		BIT(6)
+#define CHG_BATFET_ON_BIT	BIT(3)
+#define CHG_VCP_EN		BIT(0)
+#define CHG_BAT_TEMP_DIS_BIT	BIT(2)
+#define SAFE_CURRENT_MA		1500
+#define VREF_BATT_THERM_FORCE_ON	BIT(7)
+static int __devinit pm8921_chg_hw_init(struct pm8921_chg_chip *chip)
+{
+	int rc;
+	int vdd_safe;
+
+	rc = pm_chg_masked_write(chip, SYS_CONFIG_2,
+					BOOT_DONE_BIT, BOOT_DONE_BIT);
+	if (rc) {
+		pr_err("Failed to set BOOT_DONE_BIT rc=%d\n", rc);
+		return rc;
+	}
+
+	vdd_safe = chip->max_voltage_mv + VDD_MAX_INCREASE_MV;
+
+	if (vdd_safe > PM8921_CHG_VDDSAFE_MAX)
+		vdd_safe = PM8921_CHG_VDDSAFE_MAX;
+
+	rc = pm_chg_vddsafe_set(chip, vdd_safe);
+
+	if (rc) {
+		pr_err("Failed to set safe voltage to %d rc=%d\n",
+						chip->max_voltage_mv, rc);
+		return rc;
+	}
+	rc = pm_chg_vbatdet_set(chip,
+				chip->max_voltage_mv
+				- chip->resume_voltage_delta);
+	if (rc) {
+		pr_err("Failed to set vbatdet comprator voltage to %d rc=%d\n",
+			chip->max_voltage_mv - chip->resume_voltage_delta, rc);
+		return rc;
+	}
+
+	rc = pm_chg_vddmax_set(chip, chip->max_voltage_mv);
+	if (rc) {
+		pr_err("Failed to set max voltage to %d rc=%d\n",
+						chip->max_voltage_mv, rc);
+		return rc;
+	}
+	rc = pm_chg_ibatsafe_set(chip, SAFE_CURRENT_MA);
+	if (rc) {
+		pr_err("Failed to set max voltage to %d rc=%d\n",
+						SAFE_CURRENT_MA, rc);
+		return rc;
+	}
+
+	rc = pm_chg_ibatmax_set(chip, chip->max_bat_chg_current);
+	if (rc) {
+		pr_err("Failed to set max current to 400 rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = pm_chg_iterm_set(chip, chip->term_current);
+	if (rc) {
+		pr_err("Failed to set term current to %d rc=%d\n",
+						chip->term_current, rc);
+		return rc;
+	}
+
+	/* Disable the ENUM TIMER */
+	rc = pm_chg_masked_write(chip, PBL_ACCESS2, ENUM_TIMER_STOP_BIT,
+			ENUM_TIMER_STOP_BIT);
+	if (rc) {
+		pr_err("Failed to set enum timer stop rc=%d\n", rc);
+		return rc;
+	}
+
+	if (chip->safety_time != 0) {
+		rc = pm_chg_tchg_max_set(chip, chip->safety_time);
+		if (rc) {
+			pr_err("Failed to set max time to %d minutes rc=%d\n",
+							chip->safety_time, rc);
+			return rc;
+		}
+	}
+
+	if (chip->ttrkl_time != 0) {
+		rc = pm_chg_ttrkl_max_set(chip, chip->ttrkl_time);
+		if (rc) {
+			pr_err("Failed to set trkl time to %d minutes rc=%d\n",
+							chip->safety_time, rc);
+			return rc;
+		}
+	}
+
+	if (chip->vin_min != 0) {
+		rc = pm_chg_vinmin_set(chip, chip->vin_min);
+		if (rc) {
+			pr_err("Failed to set vin min to %d mV rc=%d\n",
+							chip->vin_min, rc);
+			return rc;
+		}
+	} else {
+		chip->vin_min = pm_chg_vinmin_get(chip);
+	}
+
+	rc = pm_chg_disable_wd(chip);
+	if (rc) {
+		pr_err("Failed to disable wd rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = pm_chg_masked_write(chip, CHG_CNTRL_2,
+				CHG_BAT_TEMP_DIS_BIT, 0);
+	if (rc) {
+		pr_err("Failed to enable temp control chg rc=%d\n", rc);
+		return rc;
+	}
+	/* switch to a 3.2Mhz for the buck */
+	rc = pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CLOCK_CTRL, 0x15);
+	if (rc) {
+		pr_err("Failed to switch buck clk rc=%d\n", rc);
+		return rc;
+	}
+
+	if (chip->trkl_voltage != 0) {
+		rc = pm_chg_vtrkl_low_set(chip, chip->trkl_voltage);
+		if (rc) {
+			pr_err("Failed to set trkl voltage to %dmv  rc=%d\n",
+							chip->trkl_voltage, rc);
+			return rc;
+		}
+	}
+
+	if (chip->weak_voltage != 0) {
+		rc = pm_chg_vweak_set(chip, chip->weak_voltage);
+		if (rc) {
+			pr_err("Failed to set weak voltage to %dmv  rc=%d\n",
+							chip->weak_voltage, rc);
+			return rc;
+		}
+	}
+
+	if (chip->trkl_current != 0) {
+		rc = pm_chg_itrkl_set(chip, chip->trkl_current);
+		if (rc) {
+			pr_err("Failed to set trkl current to %dmA  rc=%d\n",
+							chip->trkl_voltage, rc);
+			return rc;
+		}
+	}
+
+	if (chip->weak_current != 0) {
+		rc = pm_chg_iweak_set(chip, chip->weak_current);
+		if (rc) {
+			pr_err("Failed to set weak current to %dmA  rc=%d\n",
+							chip->weak_current, rc);
+			return rc;
+		}
+	}
+
+	rc = pm_chg_batt_cold_temp_config(chip, chip->cold_thr);
+	if (rc) {
+		pr_err("Failed to set cold config %d  rc=%d\n",
+						chip->cold_thr, rc);
+	}
+
+	rc = pm_chg_batt_hot_temp_config(chip, chip->hot_thr);
+	if (rc) {
+		pr_err("Failed to set hot config %d  rc=%d\n",
+						chip->hot_thr, rc);
+	}
+
+	rc = pm_chg_led_src_config(chip, chip->led_src_config);
+	if (rc) {
+		pr_err("Failed to set charger LED src config %d  rc=%d\n",
+						chip->led_src_config, rc);
+	}
+
+	/* Workarounds for die 1.1 and 1.0 */
+	if (pm8xxx_get_revision(chip->dev->parent) < PM8XXX_REVISION_8921_2p0) {
+		pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST2, 0xF1);
+		pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, 0xCE);
+		pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, 0xD8);
+
+		/* software workaround for correct battery_id detection */
+		pm8xxx_writeb(chip->dev->parent, PSI_TXRX_SAMPLE_DATA_0, 0xFF);
+		pm8xxx_writeb(chip->dev->parent, PSI_TXRX_SAMPLE_DATA_1, 0xFF);
+		pm8xxx_writeb(chip->dev->parent, PSI_TXRX_SAMPLE_DATA_2, 0xFF);
+		pm8xxx_writeb(chip->dev->parent, PSI_TXRX_SAMPLE_DATA_3, 0xFF);
+		pm8xxx_writeb(chip->dev->parent, PSI_CONFIG_STATUS, 0x0D);
+		udelay(100);
+		pm8xxx_writeb(chip->dev->parent, PSI_CONFIG_STATUS, 0x0C);
+	}
+
+	/* Workarounds for die 3.0 */
+	if (pm8xxx_get_revision(chip->dev->parent) == PM8XXX_REVISION_8921_3p0)
+		pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, 0xAC);
+
+	/* Enable isub_fine resolution AICL for PM8917 */
+	if (pm8xxx_get_version(chip->dev->parent) == PM8XXX_VERSION_8917)
+		chip->iusb_fine_res = true;
+
+	pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, 0xD9);
+
+	/* Disable EOC FSM processing */
+	pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, 0x91);
+
+	pm8921_chg_force_19p2mhz_clk(chip);
+
+	rc = pm_chg_masked_write(chip, CHG_CNTRL, VREF_BATT_THERM_FORCE_ON,
+						VREF_BATT_THERM_FORCE_ON);
+	if (rc)
+		pr_err("Failed to Force Vref therm rc=%d\n", rc);
+
+	rc = pm_chg_charge_dis(chip, charging_disabled);
+	if (rc) {
+		pr_err("Failed to disable CHG_CHARGE_DIS bit rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = pm_chg_auto_enable(chip, !charging_disabled);
+	if (rc) {
+		pr_err("Failed to enable charging rc=%d\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int get_rt_status(void *data, u64 * val)
+{
+	int i = (int)data;
+	int ret;
+
+	/* global irq number is passed in via data */
+	ret = pm_chg_get_rt_status(the_chip, i);
+	*val = ret;
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(rt_fops, get_rt_status, NULL, "%llu\n");
+
+static int get_fsm_status(void *data, u64 * val)
+{
+	u8 temp;
+
+	temp = pm_chg_get_fsm_state(the_chip);
+	*val = temp;
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(fsm_fops, get_fsm_status, NULL, "%llu\n");
+
+static int get_reg_loop(void *data, u64 * val)
+{
+	u8 temp;
+
+	if (!the_chip) {
+		pr_err("%s called before init\n", __func__);
+		return -EINVAL;
+	}
+	temp = pm_chg_get_regulation_loop(the_chip);
+	*val = temp;
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(reg_loop_fops, get_reg_loop, NULL, "0x%02llx\n");
+
+static int get_reg(void *data, u64 * val)
+{
+	int addr = (int)data;
+	int ret;
+	u8 temp;
+
+	ret = pm8xxx_readb(the_chip->dev->parent, addr, &temp);
+	if (ret) {
+		pr_err("pm8xxx_readb to %x value =%d errored = %d\n",
+			addr, temp, ret);
+		return -EAGAIN;
+	}
+	*val = temp;
+	return 0;
+}
+
+static int set_reg(void *data, u64 val)
+{
+	int addr = (int)data;
+	int ret;
+	u8 temp;
+
+	temp = (u8) val;
+	ret = pm8xxx_writeb(the_chip->dev->parent, addr, temp);
+	if (ret) {
+		pr_err("pm8xxx_writeb to %x value =%d errored = %d\n",
+			addr, temp, ret);
+		return -EAGAIN;
+	}
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_reg, set_reg, "0x%02llx\n");
+
+enum {
+	BAT_WARM_ZONE,
+	BAT_COOL_ZONE,
+};
+static int get_warm_cool(void *data, u64 * val)
+{
+	if (!the_chip) {
+		pr_err("%s called before init\n", __func__);
+		return -EINVAL;
+	}
+	if ((int)data == BAT_WARM_ZONE)
+		*val = the_chip->is_bat_warm;
+	if ((int)data == BAT_COOL_ZONE)
+		*val = the_chip->is_bat_cool;
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(warm_cool_fops, get_warm_cool, NULL, "0x%lld\n");
+
+static void create_debugfs_entries(struct pm8921_chg_chip *chip)
+{
+	int i;
+
+	chip->dent = debugfs_create_dir("pm8921_chg", NULL);
+
+	if (IS_ERR(chip->dent)) {
+		pr_err("pmic charger couldnt create debugfs dir\n");
+		return;
+	}
+
+	debugfs_create_file("CHG_CNTRL", 0644, chip->dent,
+			    (void *)CHG_CNTRL, &reg_fops);
+	debugfs_create_file("CHG_CNTRL_2", 0644, chip->dent,
+			    (void *)CHG_CNTRL_2, &reg_fops);
+	debugfs_create_file("CHG_CNTRL_3", 0644, chip->dent,
+			    (void *)CHG_CNTRL_3, &reg_fops);
+	debugfs_create_file("PBL_ACCESS1", 0644, chip->dent,
+			    (void *)PBL_ACCESS1, &reg_fops);
+	debugfs_create_file("PBL_ACCESS2", 0644, chip->dent,
+			    (void *)PBL_ACCESS2, &reg_fops);
+	debugfs_create_file("SYS_CONFIG_1", 0644, chip->dent,
+			    (void *)SYS_CONFIG_1, &reg_fops);
+	debugfs_create_file("SYS_CONFIG_2", 0644, chip->dent,
+			    (void *)SYS_CONFIG_2, &reg_fops);
+	debugfs_create_file("CHG_VDD_MAX", 0644, chip->dent,
+			    (void *)CHG_VDD_MAX, &reg_fops);
+	debugfs_create_file("CHG_VDD_SAFE", 0644, chip->dent,
+			    (void *)CHG_VDD_SAFE, &reg_fops);
+	debugfs_create_file("CHG_VBAT_DET", 0644, chip->dent,
+			    (void *)CHG_VBAT_DET, &reg_fops);
+	debugfs_create_file("CHG_IBAT_MAX", 0644, chip->dent,
+			    (void *)CHG_IBAT_MAX, &reg_fops);
+	debugfs_create_file("CHG_IBAT_SAFE", 0644, chip->dent,
+			    (void *)CHG_IBAT_SAFE, &reg_fops);
+	debugfs_create_file("CHG_VIN_MIN", 0644, chip->dent,
+			    (void *)CHG_VIN_MIN, &reg_fops);
+	debugfs_create_file("CHG_VTRICKLE", 0644, chip->dent,
+			    (void *)CHG_VTRICKLE, &reg_fops);
+	debugfs_create_file("CHG_ITRICKLE", 0644, chip->dent,
+			    (void *)CHG_ITRICKLE, &reg_fops);
+	debugfs_create_file("CHG_ITERM", 0644, chip->dent,
+			    (void *)CHG_ITERM, &reg_fops);
+	debugfs_create_file("CHG_TCHG_MAX", 0644, chip->dent,
+			    (void *)CHG_TCHG_MAX, &reg_fops);
+	debugfs_create_file("CHG_TWDOG", 0644, chip->dent,
+			    (void *)CHG_TWDOG, &reg_fops);
+	debugfs_create_file("CHG_TEMP_THRESH", 0644, chip->dent,
+			    (void *)CHG_TEMP_THRESH, &reg_fops);
+	debugfs_create_file("CHG_COMP_OVR", 0644, chip->dent,
+			    (void *)CHG_COMP_OVR, &reg_fops);
+	debugfs_create_file("CHG_BUCK_CTRL_TEST1", 0644, chip->dent,
+			    (void *)CHG_BUCK_CTRL_TEST1, &reg_fops);
+	debugfs_create_file("CHG_BUCK_CTRL_TEST2", 0644, chip->dent,
+			    (void *)CHG_BUCK_CTRL_TEST2, &reg_fops);
+	debugfs_create_file("CHG_BUCK_CTRL_TEST3", 0644, chip->dent,
+			    (void *)CHG_BUCK_CTRL_TEST3, &reg_fops);
+	debugfs_create_file("CHG_TEST", 0644, chip->dent,
+			    (void *)CHG_TEST, &reg_fops);
+
+	debugfs_create_file("FSM_STATE", 0644, chip->dent, NULL,
+			    &fsm_fops);
+
+	debugfs_create_file("REGULATION_LOOP_CONTROL", 0644, chip->dent, NULL,
+			    &reg_loop_fops);
+
+	debugfs_create_file("BAT_WARM_ZONE", 0644, chip->dent,
+				(void *)BAT_WARM_ZONE, &warm_cool_fops);
+	debugfs_create_file("BAT_COOL_ZONE", 0644, chip->dent,
+				(void *)BAT_COOL_ZONE, &warm_cool_fops);
+
+	for (i = 0; i < ARRAY_SIZE(chg_irq_data); i++) {
+		if (chip->pmic_chg_irq[chg_irq_data[i].irq_id])
+			debugfs_create_file(chg_irq_data[i].name, 0444,
+				chip->dent,
+				(void *)chg_irq_data[i].irq_id,
+				&rt_fops);
+	}
+}
+
+static int pm8921_charger_suspend_noirq(struct device *dev)
+{
+	int rc;
+	struct pm8921_chg_chip *chip = dev_get_drvdata(dev);
+
+	rc = pm_chg_masked_write(chip, CHG_CNTRL, VREF_BATT_THERM_FORCE_ON, 0);
+	if (rc)
+		pr_err("Failed to Force Vref therm off rc=%d\n", rc);
+	pm8921_chg_set_hw_clk_switching(chip);
+	return 0;
+}
+
+static int pm8921_charger_resume_noirq(struct device *dev)
+{
+	int rc;
+	struct pm8921_chg_chip *chip = dev_get_drvdata(dev);
+
+	pm8921_chg_force_19p2mhz_clk(chip);
+
+	rc = pm_chg_masked_write(chip, CHG_CNTRL, VREF_BATT_THERM_FORCE_ON,
+						VREF_BATT_THERM_FORCE_ON);
+	if (rc)
+		pr_err("Failed to Force Vref therm on rc=%d\n", rc);
+	return 0;
+}
+
+static int pm8921_charger_resume(struct device *dev)
+{
+	int rc;
+	struct pm8921_chg_chip *chip = dev_get_drvdata(dev);
+
+	if (!(chip->cool_temp_dc == INT_MIN && chip->warm_temp_dc == INT_MIN)
+		&& !(chip->keep_btm_on_suspend)) {
+		rc = pm8xxx_adc_btm_configure(&btm_config);
+		if (rc)
+			pr_err("couldn't reconfigure btm rc=%d\n", rc);
+
+		rc = pm8xxx_adc_btm_start();
+		if (rc)
+			pr_err("couldn't restart btm rc=%d\n", rc);
+	}
+	if (pm8921_chg_is_enabled(chip, LOOP_CHANGE_IRQ)) {
+		disable_irq_wake(chip->pmic_chg_irq[LOOP_CHANGE_IRQ]);
+		pm8921_chg_disable_irq(chip, LOOP_CHANGE_IRQ);
+	}
+	return 0;
+}
+
+static int pm8921_charger_suspend(struct device *dev)
+{
+	int rc;
+	struct pm8921_chg_chip *chip = dev_get_drvdata(dev);
+
+	if (!(chip->cool_temp_dc == INT_MIN && chip->warm_temp_dc == INT_MIN)
+		&& !(chip->keep_btm_on_suspend)) {
+		rc = pm8xxx_adc_btm_end();
+		if (rc)
+			pr_err("Failed to disable BTM on suspend rc=%d\n", rc);
+	}
+
+	if (is_usb_chg_plugged_in(chip)) {
+		pm8921_chg_enable_irq(chip, LOOP_CHANGE_IRQ);
+		enable_irq_wake(chip->pmic_chg_irq[LOOP_CHANGE_IRQ]);
+	}
+
+	return 0;
+}
+static int __devinit pm8921_charger_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct pm8921_chg_chip *chip;
+	const struct pm8921_charger_platform_data *pdata
+				= pdev->dev.platform_data;
+
+	if (!pdata) {
+		pr_err("missing platform data\n");
+		return -EINVAL;
+	}
+
+	chip = kzalloc(sizeof(struct pm8921_chg_chip),
+					GFP_KERNEL);
+	if (!chip) {
+		pr_err("Cannot allocate pm_chg_chip\n");
+		return -ENOMEM;
+	}
+
+	chip->dev = &pdev->dev;
+	chip->safety_time = pdata->safety_time;
+	chip->ttrkl_time = pdata->ttrkl_time;
+	chip->update_time = pdata->update_time;
+	chip->max_voltage_mv = pdata->max_voltage;
+	chip->min_voltage_mv = pdata->min_voltage;
+	chip->resume_voltage_delta = pdata->resume_voltage_delta;
+	chip->term_current = pdata->term_current;
+	chip->vbat_channel = pdata->charger_cdata.vbat_channel;
+	chip->batt_temp_channel = pdata->charger_cdata.batt_temp_channel;
+	chip->batt_id_channel = pdata->charger_cdata.batt_id_channel;
+	chip->batt_id_min = pdata->batt_id_min;
+	chip->batt_id_max = pdata->batt_id_max;
+	if (pdata->cool_temp != INT_MIN)
+		chip->cool_temp_dc = pdata->cool_temp * 10;
+	else
+		chip->cool_temp_dc = INT_MIN;
+
+	if (pdata->warm_temp != INT_MIN)
+		chip->warm_temp_dc = pdata->warm_temp * 10;
+	else
+		chip->warm_temp_dc = INT_MIN;
+
+	chip->temp_check_period = pdata->temp_check_period;
+	chip->max_bat_chg_current = pdata->max_bat_chg_current;
+	chip->cool_bat_chg_current = pdata->cool_bat_chg_current;
+	chip->warm_bat_chg_current = pdata->warm_bat_chg_current;
+	chip->cool_bat_voltage = pdata->cool_bat_voltage;
+	chip->warm_bat_voltage = pdata->warm_bat_voltage;
+	chip->keep_btm_on_suspend = pdata->keep_btm_on_suspend;
+	chip->trkl_voltage = pdata->trkl_voltage;
+	chip->weak_voltage = pdata->weak_voltage;
+	chip->trkl_current = pdata->trkl_current;
+	chip->weak_current = pdata->weak_current;
+	chip->vin_min = pdata->vin_min;
+	chip->thermal_mitigation = pdata->thermal_mitigation;
+	chip->thermal_levels = pdata->thermal_levels;
+
+	chip->cold_thr = pdata->cold_thr;
+	chip->hot_thr = pdata->hot_thr;
+	chip->rconn_mohm = pdata->rconn_mohm;
+	chip->led_src_config = pdata->led_src_config;
+
+	rc = pm8921_chg_hw_init(chip);
+	if (rc) {
+		pr_err("couldn't init hardware rc=%d\n", rc);
+		goto free_chip;
+	}
+
+	chip->usb_psy.name = "usb",
+	chip->usb_psy.type = POWER_SUPPLY_TYPE_USB,
+	chip->usb_psy.supplied_to = pm_power_supplied_to,
+	chip->usb_psy.num_supplicants = ARRAY_SIZE(pm_power_supplied_to),
+	chip->usb_psy.properties = pm_power_props_usb,
+	chip->usb_psy.num_properties = ARRAY_SIZE(pm_power_props_usb),
+	chip->usb_psy.get_property = pm_power_get_property_usb,
+
+	chip->dc_psy.name = "pm8921-dc",
+	chip->dc_psy.type = POWER_SUPPLY_TYPE_MAINS,
+	chip->dc_psy.supplied_to = pm_power_supplied_to,
+	chip->dc_psy.num_supplicants = ARRAY_SIZE(pm_power_supplied_to),
+	chip->dc_psy.properties = pm_power_props_mains,
+	chip->dc_psy.num_properties = ARRAY_SIZE(pm_power_props_mains),
+	chip->dc_psy.get_property = pm_power_get_property_mains,
+
+	chip->batt_psy.name = "battery",
+	chip->batt_psy.type = POWER_SUPPLY_TYPE_BATTERY,
+	chip->batt_psy.properties = msm_batt_power_props,
+	chip->batt_psy.num_properties = ARRAY_SIZE(msm_batt_power_props),
+	chip->batt_psy.get_property = pm_batt_power_get_property,
+	chip->batt_psy.external_power_changed = pm_batt_external_power_changed,
+	rc = power_supply_register(chip->dev, &chip->usb_psy);
+	if (rc < 0) {
+		pr_err("power_supply_register usb failed rc = %d\n", rc);
+		goto free_chip;
+	}
+
+	rc = power_supply_register(chip->dev, &chip->dc_psy);
+	if (rc < 0) {
+		pr_err("power_supply_register usb failed rc = %d\n", rc);
+		goto unregister_usb;
+	}
+
+	rc = power_supply_register(chip->dev, &chip->batt_psy);
+	if (rc < 0) {
+		pr_err("power_supply_register batt failed rc = %d\n", rc);
+		goto unregister_dc;
+	}
+
+	platform_set_drvdata(pdev, chip);
+	the_chip = chip;
+
+	wake_lock_init(&chip->eoc_wake_lock, WAKE_LOCK_SUSPEND, "pm8921_eoc");
+	INIT_DELAYED_WORK(&chip->eoc_work, eoc_worker);
+	INIT_DELAYED_WORK(&chip->vin_collapse_check_work,
+						vin_collapse_check_worker);
+	INIT_DELAYED_WORK(&chip->unplug_check_work, unplug_check_worker);
+
+	rc = request_irqs(chip, pdev);
+	if (rc) {
+		pr_err("couldn't register interrupts rc=%d\n", rc);
+		goto unregister_batt;
+	}
+
+	enable_irq_wake(chip->pmic_chg_irq[USBIN_VALID_IRQ]);
+	enable_irq_wake(chip->pmic_chg_irq[USBIN_OV_IRQ]);
+	enable_irq_wake(chip->pmic_chg_irq[USBIN_UV_IRQ]);
+	enable_irq_wake(chip->pmic_chg_irq[BAT_TEMP_OK_IRQ]);
+	enable_irq_wake(chip->pmic_chg_irq[VBATDET_LOW_IRQ]);
+	enable_irq_wake(chip->pmic_chg_irq[FASTCHG_IRQ]);
+	/*
+	 * if both the cool_temp_dc and warm_temp_dc are invalid device doesnt
+	 * care for jeita compliance
+	 */
+	if (!(chip->cool_temp_dc == INT_MIN && chip->warm_temp_dc == INT_MIN)) {
+		rc = configure_btm(chip);
+		if (rc) {
+			pr_err("couldn't register with btm rc=%d\n", rc);
+			goto free_irq;
+		}
+	}
+
+	create_debugfs_entries(chip);
+
+	INIT_WORK(&chip->bms_notify.work, bms_notify);
+	INIT_WORK(&chip->battery_id_valid_work, battery_id_valid);
+
+	/* determine what state the charger is in */
+	determine_initial_state(chip);
+
+	if (chip->update_time) {
+		INIT_DELAYED_WORK(&chip->update_heartbeat_work,
+							update_heartbeat);
+		schedule_delayed_work(&chip->update_heartbeat_work,
+				      round_jiffies_relative(msecs_to_jiffies
+							(chip->update_time)));
+	}
+	return 0;
+
+free_irq:
+	free_irqs(chip);
+unregister_batt:
+	power_supply_unregister(&chip->batt_psy);
+unregister_dc:
+	power_supply_unregister(&chip->dc_psy);
+unregister_usb:
+	power_supply_unregister(&chip->usb_psy);
+free_chip:
+	kfree(chip);
+	return rc;
+}
+
+static int __devexit pm8921_charger_remove(struct platform_device *pdev)
+{
+	struct pm8921_chg_chip *chip = platform_get_drvdata(pdev);
+
+	free_irqs(chip);
+	platform_set_drvdata(pdev, NULL);
+	the_chip = NULL;
+	kfree(chip);
+	return 0;
+}
+static const struct dev_pm_ops pm8921_pm_ops = {
+	.suspend	= pm8921_charger_suspend,
+	.suspend_noirq  = pm8921_charger_suspend_noirq,
+	.resume_noirq   = pm8921_charger_resume_noirq,
+	.resume		= pm8921_charger_resume,
+};
+static struct platform_driver pm8921_charger_driver = {
+	.probe		= pm8921_charger_probe,
+	.remove		= __devexit_p(pm8921_charger_remove),
+	.driver		= {
+			.name	= PM8921_CHARGER_DEV_NAME,
+			.owner	= THIS_MODULE,
+			.pm	= &pm8921_pm_ops,
+	},
+};
+
+static int __init pm8921_charger_init(void)
+{
+	return platform_driver_register(&pm8921_charger_driver);
+}
+
+static void __exit pm8921_charger_exit(void)
+{
+	platform_driver_unregister(&pm8921_charger_driver);
+}
+
+late_initcall(pm8921_charger_init);
+module_exit(pm8921_charger_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC8921 charger/battery driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8921_CHARGER_DEV_NAME);
diff --git a/drivers/power/pm8xxx-ccadc.c b/drivers/power/pm8xxx-ccadc.c
new file mode 100644
index 0000000..ce72a5b
--- /dev/null
+++ b/drivers/power/pm8xxx-ccadc.c
@@ -0,0 +1,734 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#define pr_fmt(fmt)	"%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/ccadc.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#define CCADC_ANA_PARAM		0x240
+#define CCADC_DIG_PARAM		0x241
+#define CCADC_RSV		0x242
+#define CCADC_DATA0		0x244
+#define CCADC_DATA1		0x245
+#define CCADC_OFFSET_TRIM1	0x34A
+#define CCADC_OFFSET_TRIM0	0x34B
+#define CCADC_FULLSCALE_TRIM1	0x34C
+#define CCADC_FULLSCALE_TRIM0	0x34D
+
+/* note : TRIM1 is the msb and TRIM0 is the lsb */
+#define ADC_ARB_SECP_CNTRL	0x190
+#define ADC_ARB_SECP_AMUX_CNTRL	0x191
+#define ADC_ARB_SECP_ANA_PARAM	0x192
+#define ADC_ARB_SECP_DIG_PARAM	0x193
+#define ADC_ARB_SECP_RSV	0x194
+#define ADC_ARB_SECP_DATA1	0x195
+#define ADC_ARB_SECP_DATA0	0x196
+
+#define ADC_ARB_BMS_CNTRL	0x18D
+
+#define START_CONV_BIT	BIT(7)
+#define EOC_CONV_BIT	BIT(6)
+#define SEL_CCADC_BIT	BIT(1)
+#define EN_ARB_BIT	BIT(0)
+
+#define CCADC_CALIB_DIG_PARAM	0xE3
+#define CCADC_CALIB_RSV_GND	0x40
+#define CCADC_CALIB_RSV_25MV	0x80
+#define CCADC_CALIB_ANA_PARAM	0x1B
+#define SAMPLE_COUNT		16
+#define ADC_WAIT_COUNT		10
+
+#define CCADC_MAX_25MV		30000
+#define CCADC_MIN_25MV		20000
+#define CCADC_MAX_0UV		-4000
+#define CCADC_MIN_0UV		-7000
+
+#define CCADC_INTRINSIC_OFFSET  0xC000
+
+struct pm8xxx_ccadc_chip {
+	struct device		*dev;
+	struct dentry		*dent;
+	u16			ccadc_offset;
+	int			ccadc_gain_uv;
+	unsigned int		revision;
+	int			eoc_irq;
+	int			r_sense;
+};
+
+static struct pm8xxx_ccadc_chip *the_chip;
+
+#ifdef DEBUG
+static s64 microvolt_to_ccadc_reading_v1(s64 uv)
+{
+	return div_s64(uv * CCADC_READING_RESOLUTION_D_V1,
+				CCADC_READING_RESOLUTION_N_V1);
+}
+
+static s64 microvolt_to_ccadc_reading_v2(s64 uv)
+{
+	return div_s64(uv * CCADC_READING_RESOLUTION_D_V2,
+				CCADC_READING_RESOLUTION_N_V2);
+}
+
+static s64 microvolt_to_ccadc_reading(struct pm8xxx_ccadc_chip *chip, s64 cc)
+{
+	/*
+	 * resolution (the value of a single bit) was changed after revision 2.0
+	 * for more accurate readings
+	 */
+	return (the_chip->revision < PM8XXX_REVISION_8921_2p0) ?
+				microvolt_to_ccadc_reading_v1((s64)cc) :
+				microvolt_to_ccadc_reading_v2((s64)cc);
+}
+#endif
+
+static int cc_adjust_for_offset(u16 raw)
+{
+	/* this has the intrinsic offset */
+	return (int)raw - the_chip->ccadc_offset;
+}
+
+#define GAIN_REFERENCE_UV 25000
+/*
+ * gain compensation for ccadc readings - common for vsense based and
+ * couloumb counter based readings
+ */
+s64 pm8xxx_cc_adjust_for_gain(s64 uv)
+{
+	if (the_chip == NULL || the_chip->ccadc_gain_uv == 0)
+		return uv;
+
+	return div_s64(uv * GAIN_REFERENCE_UV, the_chip->ccadc_gain_uv);
+}
+EXPORT_SYMBOL(pm8xxx_cc_adjust_for_gain);
+
+static int pm_ccadc_masked_write(struct pm8xxx_ccadc_chip *chip, u16 addr,
+							u8 mask, u8 val)
+{
+	int rc;
+	u8 reg;
+
+	rc = pm8xxx_readb(chip->dev->parent, addr, &reg);
+	if (rc) {
+		pr_err("read failed addr = %03X, rc = %d\n", addr, rc);
+		return rc;
+	}
+	reg &= ~mask;
+	reg |= val & mask;
+	rc = pm8xxx_writeb(chip->dev->parent, addr, reg);
+	if (rc) {
+		pr_err("write failed addr = %03X, rc = %d\n", addr, rc);
+		return rc;
+	}
+	return 0;
+}
+
+#define REG_SBI_CONFIG		0x04F
+#define PAGE3_ENABLE_MASK	0x6
+static int calib_ccadc_enable_trim_access(struct pm8xxx_ccadc_chip *chip,
+								u8 *sbi_config)
+{
+	u8 reg;
+	int rc;
+
+	rc = pm8xxx_readb(chip->dev->parent, REG_SBI_CONFIG, sbi_config);
+	if (rc) {
+		pr_err("error = %d reading sbi config reg\n", rc);
+		return rc;
+	}
+
+	reg = *sbi_config | PAGE3_ENABLE_MASK;
+	return pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, reg);
+}
+
+static int calib_ccadc_restore_trim_access(struct pm8xxx_ccadc_chip *chip,
+							u8 sbi_config)
+{
+	return pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, sbi_config);
+}
+
+static int calib_ccadc_enable_arbiter(struct pm8xxx_ccadc_chip *chip)
+{
+	int rc;
+
+	/* enable Arbiter, must be sent twice */
+	rc = pm_ccadc_masked_write(chip, ADC_ARB_SECP_CNTRL,
+			SEL_CCADC_BIT | EN_ARB_BIT, SEL_CCADC_BIT | EN_ARB_BIT);
+	if (rc < 0) {
+		pr_err("error = %d enabling arbiter for offset\n", rc);
+		return rc;
+	}
+	rc = pm_ccadc_masked_write(chip, ADC_ARB_SECP_CNTRL,
+			SEL_CCADC_BIT | EN_ARB_BIT, SEL_CCADC_BIT | EN_ARB_BIT);
+	if (rc < 0) {
+		pr_err("error = %d writing ADC_ARB_SECP_CNTRL\n", rc);
+		return rc;
+	}
+	return 0;
+}
+
+static int calib_start_conv(struct pm8xxx_ccadc_chip *chip,
+					u16 *result)
+{
+	int rc, i;
+	u8 data_msb, data_lsb, reg;
+
+	/* Start conversion */
+	rc = pm_ccadc_masked_write(chip, ADC_ARB_SECP_CNTRL,
+					START_CONV_BIT, START_CONV_BIT);
+	if (rc < 0) {
+		pr_err("error = %d starting offset meas\n", rc);
+		return rc;
+	}
+
+	/* Wait for End of conversion */
+	for (i = 0; i < ADC_WAIT_COUNT; i++) {
+		rc = pm8xxx_readb(chip->dev->parent,
+					ADC_ARB_SECP_CNTRL, &reg);
+		if (rc < 0) {
+			pr_err("error = %d read eoc for offset\n", rc);
+			return rc;
+		}
+		if ((reg & (START_CONV_BIT | EOC_CONV_BIT)) != EOC_CONV_BIT)
+			msleep(20);
+		else
+			break;
+	}
+	if (i == ADC_WAIT_COUNT) {
+		pr_err("waited too long for offset eoc returning -EBUSY\n");
+		return -EBUSY;
+	}
+
+	rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_SECP_DATA0, &data_lsb);
+	if (rc < 0) {
+		pr_err("error = %d reading offset lsb\n", rc);
+		return rc;
+	}
+
+	rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_SECP_DATA1, &data_msb);
+	if (rc < 0) {
+		pr_err("error = %d reading offset msb\n", rc);
+		return rc;
+	}
+
+	*result = (data_msb << 8) | data_lsb;
+	return 0;
+}
+
+static int calib_ccadc_read_trim(struct pm8xxx_ccadc_chip *chip,
+					int addr, u8 *data_msb, u8 *data_lsb)
+{
+	int rc;
+	u8 sbi_config;
+
+	calib_ccadc_enable_trim_access(chip, &sbi_config);
+	rc = pm8xxx_readb(chip->dev->parent, addr, data_msb);
+	if (rc < 0) {
+		pr_err("error = %d read msb\n", rc);
+		return rc;
+	}
+	rc = pm8xxx_readb(chip->dev->parent, addr + 1, data_lsb);
+	if (rc < 0) {
+		pr_err("error = %d read lsb\n", rc);
+		return rc;
+	}
+	calib_ccadc_restore_trim_access(chip, sbi_config);
+	return 0;
+}
+
+static void calib_ccadc_read_offset_and_gain(struct pm8xxx_ccadc_chip *chip,
+						int *gain, u16 *offset)
+{
+	u8 data_msb;
+	u8 data_lsb;
+	int rc;
+
+	rc = calib_ccadc_read_trim(chip, CCADC_FULLSCALE_TRIM1,
+						&data_msb, &data_lsb);
+	*gain = (data_msb << 8) | data_lsb;
+
+	rc = calib_ccadc_read_trim(chip, CCADC_OFFSET_TRIM1,
+						&data_msb, &data_lsb);
+	*offset = (data_msb << 8) | data_lsb;
+
+	pr_debug("raw gain trim = 0x%x offset trim =0x%x\n", *gain, *offset);
+	*gain = pm8xxx_ccadc_reading_to_microvolt(chip->revision,
+							(s64)*gain - *offset);
+	pr_debug("gain uv = %duV offset=0x%x\n", *gain, *offset);
+}
+
+#define CCADC_PROGRAM_TRIM_COUNT	2
+#define ADC_ARB_BMS_CNTRL_CCADC_SHIFT	4
+#define ADC_ARB_BMS_CNTRL_CONV_MASK	0x03
+#define BMS_CONV_IN_PROGRESS		0x2
+
+static int calib_ccadc_program_trim(struct pm8xxx_ccadc_chip *chip,
+					int addr, u8 data_msb, u8 data_lsb,
+					int wait)
+{
+	int i, rc, loop;
+	u8 cntrl, sbi_config;
+	bool in_progress = 0;
+
+	loop = wait ? CCADC_PROGRAM_TRIM_COUNT : 0;
+
+	calib_ccadc_enable_trim_access(chip, &sbi_config);
+
+	for (i = 0; i < loop; i++) {
+		rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_BMS_CNTRL, &cntrl);
+		if (rc < 0) {
+			pr_err("error = %d reading ADC_ARB_BMS_CNTRL\n", rc);
+			return rc;
+		}
+
+		/* break if a ccadc conversion is not happening */
+		in_progress = (((cntrl >> ADC_ARB_BMS_CNTRL_CCADC_SHIFT)
+			& ADC_ARB_BMS_CNTRL_CONV_MASK) == BMS_CONV_IN_PROGRESS);
+
+		if (!in_progress)
+			break;
+	}
+
+	if (in_progress) {
+		pr_debug("conv in progress cannot write trim,returing EBUSY\n");
+		return -EBUSY;
+	}
+
+	rc = pm8xxx_writeb(chip->dev->parent, addr, data_msb);
+	if (rc < 0) {
+		pr_err("error = %d write msb = 0x%x\n", rc, data_msb);
+		return rc;
+	}
+	rc = pm8xxx_writeb(chip->dev->parent, addr + 1, data_lsb);
+	if (rc < 0) {
+		pr_err("error = %d write lsb = 0x%x\n", rc, data_lsb);
+		return rc;
+	}
+	calib_ccadc_restore_trim_access(chip, sbi_config);
+	return 0;
+}
+
+void pm8xxx_calib_ccadc(void)
+{
+	u8 data_msb, data_lsb, sec_cntrl;
+	int result_offset, result_gain;
+	u16 result;
+	int i, rc;
+
+	rc = pm8xxx_readb(the_chip->dev->parent,
+					ADC_ARB_SECP_CNTRL, &sec_cntrl);
+	if (rc < 0) {
+		pr_err("error = %d reading ADC_ARB_SECP_CNTRL\n", rc);
+		return;
+	}
+
+	rc = calib_ccadc_enable_arbiter(the_chip);
+	if (rc < 0) {
+		pr_err("error = %d enabling arbiter for offset\n", rc);
+		goto bail;
+	}
+
+	/*
+	 * Set decimation ratio to 4k, lower ratio may be used in order to speed
+	 * up, pending verification through bench
+	 */
+	rc = pm8xxx_writeb(the_chip->dev->parent, ADC_ARB_SECP_DIG_PARAM,
+							CCADC_CALIB_DIG_PARAM);
+	if (rc < 0) {
+		pr_err("error = %d writing ADC_ARB_SECP_DIG_PARAM\n", rc);
+		goto bail;
+	}
+
+	result_offset = 0;
+	for (i = 0; i < SAMPLE_COUNT; i++) {
+		/* Short analog inputs to CCADC internally to ground */
+		rc = pm8xxx_writeb(the_chip->dev->parent, ADC_ARB_SECP_RSV,
+							CCADC_CALIB_RSV_GND);
+		if (rc < 0) {
+			pr_err("error = %d selecting gnd voltage\n", rc);
+			goto bail;
+		}
+
+		/* Enable CCADC */
+		rc = pm8xxx_writeb(the_chip->dev->parent,
+				ADC_ARB_SECP_ANA_PARAM, CCADC_CALIB_ANA_PARAM);
+		if (rc < 0) {
+			pr_err("error = %d enabling ccadc\n", rc);
+			goto bail;
+		}
+
+		rc = calib_start_conv(the_chip, &result);
+		if (rc < 0) {
+			pr_err("error = %d for zero volt measurement\n", rc);
+			goto bail;
+		}
+
+		result_offset += result;
+	}
+
+	result_offset = result_offset / SAMPLE_COUNT;
+
+
+	pr_debug("offset result_offset = 0x%x, voltage = %llduV\n",
+			result_offset,
+			pm8xxx_ccadc_reading_to_microvolt(the_chip->revision,
+			((s64)result_offset - CCADC_INTRINSIC_OFFSET)));
+
+	the_chip->ccadc_offset = result_offset;
+	data_msb = the_chip->ccadc_offset >> 8;
+	data_lsb = the_chip->ccadc_offset;
+
+	rc = calib_ccadc_program_trim(the_chip, CCADC_OFFSET_TRIM1,
+						data_msb, data_lsb, 1);
+	if (rc) {
+		pr_debug("error = %d programming offset trim 0x%02x 0x%02x\n",
+					rc, data_msb, data_lsb);
+		/* enable the interrupt and write it when it fires */
+		enable_irq(the_chip->eoc_irq);
+	}
+
+	rc = calib_ccadc_enable_arbiter(the_chip);
+	if (rc < 0) {
+		pr_err("error = %d enabling arbiter for gain\n", rc);
+		goto bail;
+	}
+
+	/*
+	 * Set decimation ratio to 4k, lower ratio may be used in order to speed
+	 * up, pending verification through bench
+	 */
+	rc = pm8xxx_writeb(the_chip->dev->parent, ADC_ARB_SECP_DIG_PARAM,
+							CCADC_CALIB_DIG_PARAM);
+	if (rc < 0) {
+		pr_err("error = %d enabling decimation ration for gain\n", rc);
+		goto bail;
+	}
+
+	result_gain = 0;
+	for (i = 0; i < SAMPLE_COUNT; i++) {
+		rc = pm8xxx_writeb(the_chip->dev->parent,
+					ADC_ARB_SECP_RSV, CCADC_CALIB_RSV_25MV);
+		if (rc < 0) {
+			pr_err("error = %d selecting 25mV for gain\n", rc);
+			goto bail;
+		}
+
+		/* Enable CCADC */
+		rc = pm8xxx_writeb(the_chip->dev->parent,
+			ADC_ARB_SECP_ANA_PARAM, CCADC_CALIB_ANA_PARAM);
+		if (rc < 0) {
+			pr_err("error = %d enabling ccadc\n", rc);
+			goto bail;
+		}
+
+		rc = calib_start_conv(the_chip, &result);
+		if (rc < 0) {
+			pr_err("error = %d for adc reading 25mV\n", rc);
+			goto bail;
+		}
+
+		result_gain += result;
+	}
+	result_gain = result_gain / SAMPLE_COUNT;
+
+	/*
+	 * result_offset includes INTRINSIC OFFSET
+	 * the_chip->ccadc_gain_uv will be the actual voltage
+	 * measured for 25000UV
+	 */
+	the_chip->ccadc_gain_uv = pm8xxx_ccadc_reading_to_microvolt(
+				the_chip->revision,
+				((s64)result_gain - result_offset));
+
+	pr_debug("gain result_gain = 0x%x, voltage = %d microVolts\n",
+					result_gain, the_chip->ccadc_gain_uv);
+
+	data_msb = result_gain >> 8;
+	data_lsb = result_gain;
+	rc = calib_ccadc_program_trim(the_chip, CCADC_FULLSCALE_TRIM1,
+						data_msb, data_lsb, 0);
+	if (rc)
+		pr_debug("error = %d programming gain trim\n", rc);
+bail:
+	pm8xxx_writeb(the_chip->dev->parent, ADC_ARB_SECP_CNTRL, sec_cntrl);
+}
+EXPORT_SYMBOL(pm8xxx_calib_ccadc);
+
+static irqreturn_t pm8921_bms_ccadc_eoc_handler(int irq, void *data)
+{
+	u8 data_msb, data_lsb;
+	struct pm8xxx_ccadc_chip *chip = data;
+	int rc;
+
+	pr_debug("irq = %d triggered\n", irq);
+	data_msb = chip->ccadc_offset >> 8;
+	data_lsb = chip->ccadc_offset;
+
+	rc = calib_ccadc_program_trim(chip, CCADC_OFFSET_TRIM1,
+						data_msb, data_lsb, 0);
+	disable_irq_nosync(chip->eoc_irq);
+
+	return IRQ_HANDLED;
+}
+
+#define CCADC_IBAT_DIG_PARAM	0xA3
+#define CCADC_IBAT_RSV		0x10
+#define CCADC_IBAT_ANA_PARAM	0x1A
+static int ccadc_get_rsense_voltage(int *voltage_uv)
+{
+	u16 raw;
+	int result;
+	int rc = 0;
+
+	rc = calib_ccadc_enable_arbiter(the_chip);
+	if (rc < 0) {
+		pr_err("error = %d enabling arbiter for offset\n", rc);
+		return rc;
+	}
+
+	rc = pm8xxx_writeb(the_chip->dev->parent, ADC_ARB_SECP_DIG_PARAM,
+							CCADC_IBAT_DIG_PARAM);
+	if (rc < 0) {
+		pr_err("error = %d writing ADC_ARB_SECP_DIG_PARAM\n", rc);
+		return rc;
+	}
+
+	rc = pm8xxx_writeb(the_chip->dev->parent, ADC_ARB_SECP_RSV,
+						CCADC_IBAT_RSV);
+	if (rc < 0) {
+		pr_err("error = %d selecting rsense\n", rc);
+		return rc;
+	}
+
+	rc = pm8xxx_writeb(the_chip->dev->parent,
+			ADC_ARB_SECP_ANA_PARAM, CCADC_IBAT_ANA_PARAM);
+	if (rc < 0) {
+		pr_err("error = %d enabling ccadc\n", rc);
+		return rc;
+	}
+
+	rc = calib_start_conv(the_chip, &raw);
+	if (rc < 0) {
+		pr_err("error = %d for zero volt measurement\n", rc);
+		return rc;
+	}
+
+	pr_debug("Vsense raw = 0x%x\n", raw);
+	result = cc_adjust_for_offset(raw);
+	pr_debug("Vsense after offset raw = 0x%x offset=0x%x\n",
+					result,
+					the_chip->ccadc_offset);
+	*voltage_uv = pm8xxx_ccadc_reading_to_microvolt(the_chip->revision,
+			((s64)result));
+	pr_debug("Vsense before gain of %d = %d uV\n", the_chip->ccadc_gain_uv,
+					*voltage_uv);
+	*voltage_uv = pm8xxx_cc_adjust_for_gain(*voltage_uv);
+
+	pr_debug("Vsense = %d uV\n", *voltage_uv);
+	return 0;
+}
+
+int pm8xxx_ccadc_get_battery_current(int *bat_current_ua)
+{
+	int voltage_uv = 0, rc;
+
+	rc = ccadc_get_rsense_voltage(&voltage_uv);
+	if (rc) {
+		pr_err("cant get voltage across rsense rc = %d\n", rc);
+		return rc;
+	}
+
+	*bat_current_ua = voltage_uv * 1000/the_chip->r_sense;
+	/*
+	 * ccadc reads +ve current when the battery is charging
+	 * We need to return -ve if the battery is charging
+	 */
+	*bat_current_ua = -1 * (*bat_current_ua);
+	pr_debug("bat current = %d ma\n", *bat_current_ua);
+	return 0;
+}
+EXPORT_SYMBOL(pm8xxx_ccadc_get_battery_current);
+
+static int get_reg(void *data, u64 * val)
+{
+	int addr = (int)data;
+	int ret;
+	u8 temp;
+
+	ret = pm8xxx_readb(the_chip->dev->parent, addr, &temp);
+	if (ret) {
+		pr_err("pm8xxx_readb to %x value = %d errored = %d\n",
+			addr, temp, ret);
+		return -EAGAIN;
+	}
+	*val = temp;
+	return 0;
+}
+
+static int set_reg(void *data, u64 val)
+{
+	int addr = (int)data;
+	int ret;
+	u8 temp;
+
+	temp = (u8) val;
+	ret = pm8xxx_writeb(the_chip->dev->parent, addr, temp);
+	if (ret) {
+		pr_err("pm8xxx_writeb to %x value = %d errored = %d\n",
+			addr, temp, ret);
+		return -EAGAIN;
+	}
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_reg, set_reg, "0x%02llx\n");
+
+static int get_calc(void *data, u64 * val)
+{
+	int ibat, rc;
+
+	rc = pm8xxx_ccadc_get_battery_current(&ibat);
+	*val = ibat;
+	return rc;
+}
+DEFINE_SIMPLE_ATTRIBUTE(calc_fops, get_calc, NULL, "%lld\n");
+
+static void create_debugfs_entries(struct pm8xxx_ccadc_chip *chip)
+{
+	chip->dent = debugfs_create_dir("pm8xxx-ccadc", NULL);
+
+	if (IS_ERR(chip->dent)) {
+		pr_err("ccadc couldnt create debugfs dir\n");
+		return;
+	}
+
+	debugfs_create_file("CCADC_ANA_PARAM", 0644, chip->dent,
+			(void *)CCADC_ANA_PARAM, &reg_fops);
+	debugfs_create_file("CCADC_DIG_PARAM", 0644, chip->dent,
+			(void *)CCADC_DIG_PARAM, &reg_fops);
+	debugfs_create_file("CCADC_RSV", 0644, chip->dent,
+			(void *)CCADC_RSV, &reg_fops);
+	debugfs_create_file("CCADC_DATA0", 0644, chip->dent,
+			(void *)CCADC_DATA0, &reg_fops);
+	debugfs_create_file("CCADC_DATA1", 0644, chip->dent,
+			(void *)CCADC_DATA1, &reg_fops);
+	debugfs_create_file("CCADC_OFFSET_TRIM1", 0644, chip->dent,
+			(void *)CCADC_OFFSET_TRIM1, &reg_fops);
+	debugfs_create_file("CCADC_OFFSET_TRIM0", 0644, chip->dent,
+			(void *)CCADC_OFFSET_TRIM0, &reg_fops);
+	debugfs_create_file("CCADC_FULLSCALE_TRIM1", 0644, chip->dent,
+			(void *)CCADC_FULLSCALE_TRIM1, &reg_fops);
+	debugfs_create_file("CCADC_FULLSCALE_TRIM0", 0644, chip->dent,
+			(void *)CCADC_FULLSCALE_TRIM0, &reg_fops);
+
+	debugfs_create_file("show_ibatt", 0644, chip->dent,
+				(void *)0, &calc_fops);
+}
+
+static int __devinit pm8xxx_ccadc_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct pm8xxx_ccadc_chip *chip;
+	struct resource *res;
+	const struct pm8xxx_ccadc_platform_data *pdata
+				= pdev->dev.platform_data;
+
+	if (!pdata) {
+		pr_err("missing platform data\n");
+		return -EINVAL;
+	}
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+			"PM8921_BMS_CCADC_EOC");
+	if  (!res) {
+		pr_err("failed to get irq\n");
+		return -EINVAL;
+	}
+
+	chip = kzalloc(sizeof(struct pm8xxx_ccadc_chip), GFP_KERNEL);
+	if (!chip) {
+		pr_err("Cannot allocate pm_bms_chip\n");
+		return -ENOMEM;
+	}
+	chip->dev = &pdev->dev;
+	chip->revision = pm8xxx_get_revision(chip->dev->parent);
+	chip->eoc_irq = res->start;
+	chip->r_sense = pdata->r_sense;
+
+	calib_ccadc_read_offset_and_gain(chip,
+					&chip->ccadc_gain_uv,
+					&chip->ccadc_offset);
+	rc = request_irq(chip->eoc_irq,
+			pm8921_bms_ccadc_eoc_handler, IRQF_TRIGGER_RISING,
+			"bms_eoc_ccadc", chip);
+	if (rc) {
+		pr_err("failed to request %d irq rc= %d\n", chip->eoc_irq, rc);
+		goto free_chip;
+	}
+	disable_irq_nosync(chip->eoc_irq);
+
+	platform_set_drvdata(pdev, chip);
+	the_chip = chip;
+
+	create_debugfs_entries(chip);
+
+	return 0;
+
+free_chip:
+	kfree(chip);
+	return rc;
+}
+
+static int __devexit pm8xxx_ccadc_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_ccadc_chip *chip = platform_get_drvdata(pdev);
+
+	debugfs_remove_recursive(chip->dent);
+	the_chip = NULL;
+	kfree(chip);
+	return 0;
+}
+
+static struct platform_driver pm8xxx_ccadc_driver = {
+	.probe	= pm8xxx_ccadc_probe,
+	.remove	= __devexit_p(pm8xxx_ccadc_remove),
+	.driver	= {
+		.name	= PM8XXX_CCADC_DEV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init pm8xxx_ccadc_init(void)
+{
+	return platform_driver_register(&pm8xxx_ccadc_driver);
+}
+
+static void __exit pm8xxx_ccadc_exit(void)
+{
+	platform_driver_unregister(&pm8xxx_ccadc_driver);
+}
+
+module_init(pm8xxx_ccadc_init);
+module_exit(pm8xxx_ccadc_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC8XXX ccadc driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8XXX_CCADC_DEV_NAME);
diff --git a/drivers/power/pmic8058-charger.c b/drivers/power/pmic8058-charger.c
new file mode 100644
index 0000000..70b5d59
--- /dev/null
+++ b/drivers/power/pmic8058-charger.c
@@ -0,0 +1,2047 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/workqueue.h>
+#include <linux/msm-charger.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/msm_adc.h>
+#include <linux/notifier.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pmic8058.h>
+#include <linux/pmic8058-charger.h>
+#include <linux/mfd/pm8xxx/batt-alarm.h>
+
+#include <mach/msm_xo.h>
+#include <mach/msm_hsusb.h>
+
+/* Config Regs  and their bits*/
+#define PM8058_CHG_TEST			0x75
+#define IGNORE_LL			2
+#define PM8058_CHG_TEST_2		0xEA
+#define PM8058_CHG_TEST_3		0xEB
+#define PM8058_OVP_TEST_REG		0xF6
+#define FORCE_OVP_OFF			3
+
+#define PM8058_CHG_CNTRL		0x1E
+#define CHG_TRICKLE_EN			7
+#define CHG_USB_SUSPEND			6
+#define CHG_IMON_CAL			5
+#define CHG_IMON_GAIN			4
+#define CHG_CHARGE_BAT			3
+#define CHG_VBUS_FROM_BOOST_OVRD	2
+#define CHG_CHARGE_DIS			1
+#define CHG_VCP_EN			0
+
+#define PM8058_CHG_CNTRL_2		0xD8
+#define ATC_DIS				7	/* coincell backed */
+#define CHARGE_AUTO_DIS			6
+#define DUMB_CHG_OVRD			5	/* coincell backed */
+#define ENUM_DONE			4
+#define CHG_TEMP_MODE			3
+#define CHG_BATT_TEMP_DIS		1	/* coincell backed */
+#define CHG_FAILED_CLEAR		0
+
+#define PM8058_CHG_VMAX_SEL		0x21
+#define PM8058_CHG_VBAT_DET		0xD9
+#define PM8058_CHG_IMAX			0x1F
+#define PM8058_CHG_TRICKLE		0xDB
+#define PM8058_CHG_ITERM		0xDC
+#define PM8058_CHG_TTRKL_MAX		0xE1
+#define PM8058_CHG_TCHG_MAX		0xE4
+#define PM8058_CHG_TEMP_THRESH		0xE2
+#define PM8058_CHG_TEMP_REG		0xE3
+#define PM8058_CHG_PULSE		0x22
+
+/* IRQ STATUS and CLEAR */
+#define PM8058_CHG_STATUS_CLEAR_IRQ_1	0x31
+#define PM8058_CHG_STATUS_CLEAR_IRQ_3	0x33
+#define PM8058_CHG_STATUS_CLEAR_IRQ_10	0xB3
+#define PM8058_CHG_STATUS_CLEAR_IRQ_11	0xB4
+
+/* IRQ MASKS */
+#define PM8058_CHG_MASK_IRQ_1		0x38
+
+#define PM8058_CHG_MASK_IRQ_3		0x3A
+#define PM8058_CHG_MASK_IRQ_10		0xBA
+#define PM8058_CHG_MASK_IRQ_11		0xBB
+
+/* IRQ Real time status regs */
+#define PM8058_CHG_STATUS_RT_1		0x3F
+#define STATUS_RTCHGVAL			7
+#define STATUS_RTCHGINVAL		6
+#define STATUS_RTBATT_REPLACE		5
+#define STATUS_RTVBATDET_LOW		4
+#define STATUS_RTCHGILIM		3
+#define STATUS_RTPCTDONE		1
+#define STATUS_RTVCP			0
+#define PM8058_CHG_STATUS_RT_3		0x41
+#define PM8058_CHG_STATUS_RT_10		0xC1
+#define PM8058_CHG_STATUS_RT_11		0xC2
+
+/* VTRIM */
+#define PM8058_CHG_VTRIM		0x1D
+#define PM8058_CHG_VBATDET_TRIM		0x1E
+#define PM8058_CHG_ITRIM		0x1F
+#define PM8058_CHG_TTRIM		0x20
+
+#define AUTO_CHARGING_VMAXSEL				4200
+#define AUTO_CHARGING_FAST_TIME_MAX_MINUTES		512
+#define AUTO_CHARGING_TRICKLE_TIME_MINUTES		30
+#define AUTO_CHARGING_VEOC_ITERM			100
+#define AUTO_CHARGING_IEOC_ITERM			160
+#define AUTO_CHARGING_RESUME_MV				4100
+
+#define AUTO_CHARGING_VBATDET				4150
+#define AUTO_CHARGING_VBATDET_DEBOUNCE_TIME_MS		3000
+#define AUTO_CHARGING_VEOC_VBATDET			4100
+#define AUTO_CHARGING_VEOC_TCHG				16
+#define AUTO_CHARGING_VEOC_TCHG_FINAL_CYCLE		32
+#define AUTO_CHARGING_VEOC_BEGIN_TIME_MS		5400000
+
+#define AUTO_CHARGING_VEOC_VBAT_LOW_CHECK_TIME_MS	60000
+#define AUTO_CHARGING_RESUME_CHARGE_DETECTION_COUNTER	5
+
+#define AUTO_CHARGING_DONE_CHECK_TIME_MS		1000
+
+#define PM8058_CHG_I_STEP_MA 50
+#define PM8058_CHG_I_MIN_MA 50
+#define PM8058_CHG_T_TCHG_SHIFT 2
+#define PM8058_CHG_I_TERM_STEP_MA 10
+#define PM8058_CHG_V_STEP_MV 25
+#define PM8058_CHG_V_MIN_MV  2400
+/*
+ * enum pmic_chg_interrupts: pmic interrupts
+ * @CHGVAL_IRQ: charger V between 3.3 and 7.9
+ * @CHGINVAL_IRQ: charger V outside 3.3 and 7.9
+ * @VBATDET_LOW_IRQ: VBAT < VBATDET
+ * @VCP_IRQ: VDD went below VBAT: BAT_FET is turned on
+ * @CHGILIM_IRQ: mA consumed>IMAXSEL: chgloop draws less mA
+ * @ATC_DONE_IRQ: Auto Trickle done
+ * @ATCFAIL_IRQ: Auto Trickle fail
+ * @AUTO_CHGDONE_IRQ: Auto chg done
+ * @AUTO_CHGFAIL_IRQ: time exceeded w/o reaching term current
+ * @CHGSTATE_IRQ: something happend causing a state change
+ * @FASTCHG_IRQ: trkl charging completed: moving to fastchg
+ * @CHG_END_IRQ: mA has dropped to termination current
+ * @BATTTEMP_IRQ: batt temp is out of range
+ * @CHGHOT_IRQ: the pass device is too hot
+ * @CHGTLIMIT_IRQ: unused
+ * @CHG_GONE_IRQ: charger was removed
+ * @VCPMAJOR_IRQ: vcp major
+ * @VBATDET_IRQ: VBAT >= VBATDET
+ * @BATFET_IRQ: BATFET closed
+ * @BATT_REPLACE_IRQ:
+ * @BATTCONNECT_IRQ:
+ */
+enum pmic_chg_interrupts {
+	CHGVAL_IRQ,
+	CHGINVAL_IRQ,
+	VBATDET_LOW_IRQ,
+	VCP_IRQ,
+	CHGILIM_IRQ,
+	ATC_DONE_IRQ,
+	ATCFAIL_IRQ,
+	AUTO_CHGDONE_IRQ,
+	AUTO_CHGFAIL_IRQ,
+	CHGSTATE_IRQ,
+	FASTCHG_IRQ,
+	CHG_END_IRQ,
+	BATTTEMP_IRQ,
+	CHGHOT_IRQ,
+	CHGTLIMIT_IRQ,
+	CHG_GONE_IRQ,
+	VCPMAJOR_IRQ,
+	VBATDET_IRQ,
+	BATFET_IRQ,
+	BATT_REPLACE_IRQ,
+	BATTCONNECT_IRQ,
+	PMIC_CHG_MAX_INTS
+};
+
+struct pm8058_charger {
+	struct pmic_charger_pdata *pdata;
+	struct device *dev;
+
+	int pmic_chg_irq[PMIC_CHG_MAX_INTS];
+	DECLARE_BITMAP(enabled_irqs, PMIC_CHG_MAX_INTS);
+
+	struct delayed_work chg_done_check_work;
+	struct delayed_work check_vbat_low_work;
+	struct delayed_work veoc_begin_work;
+	struct delayed_work charging_check_work;
+	int waiting_for_topoff;
+	int waiting_for_veoc;
+	int vbatdet;
+	struct msm_hardware_charger hw_chg;
+	int current_charger_current;
+	int disabled;
+
+	struct msm_xo_voter *voter;
+	struct dentry *dent;
+
+	int inited;
+	int present;
+};
+
+static struct pm8058_charger pm8058_chg;
+static struct msm_hardware_charger usb_hw_chg;
+static struct pmic8058_charger_data chg_data;
+
+static int msm_battery_gauge_alarm_notify(struct notifier_block *nb,
+					  unsigned long status, void *unused);
+
+static struct notifier_block alarm_notifier = {
+	.notifier_call = msm_battery_gauge_alarm_notify,
+};
+
+static int resume_mv = AUTO_CHARGING_RESUME_MV;
+static DEFINE_MUTEX(batt_alarm_lock);
+static int resume_mv_set(const char *val, struct kernel_param *kp);
+module_param_call(resume_mv, resume_mv_set, param_get_int,
+				&resume_mv, S_IRUGO | S_IWUSR);
+
+static int resume_mv_set(const char *val, struct kernel_param *kp)
+{
+	int rc;
+
+	mutex_lock(&batt_alarm_lock);
+
+	rc = param_set_int(val, kp);
+	if (rc)
+		goto out;
+
+	rc = pm8xxx_batt_alarm_threshold_set(
+			PM8XXX_BATT_ALARM_LOWER_COMPARATOR, resume_mv);
+	if (!rc)
+		rc = pm8xxx_batt_alarm_threshold_set(
+			PM8XXX_BATT_ALARM_UPPER_COMPARATOR, 4300);
+
+out:
+	mutex_unlock(&batt_alarm_lock);
+	return rc;
+}
+
+static void pm8058_chg_enable_irq(int interrupt)
+{
+	if (!__test_and_set_bit(interrupt, pm8058_chg.enabled_irqs)) {
+		dev_dbg(pm8058_chg.dev, "%s %d\n", __func__,
+			pm8058_chg.pmic_chg_irq[interrupt]);
+		enable_irq(pm8058_chg.pmic_chg_irq[interrupt]);
+	}
+}
+
+static void pm8058_chg_disable_irq(int interrupt)
+{
+	if (__test_and_clear_bit(interrupt, pm8058_chg.enabled_irqs)) {
+		dev_dbg(pm8058_chg.dev, "%s %d\n", __func__,
+			pm8058_chg.pmic_chg_irq[interrupt]);
+		disable_irq_nosync(pm8058_chg.pmic_chg_irq[interrupt]);
+	}
+}
+
+static int pm_chg_get_rt_status(int irq)
+{
+	int ret;
+
+	ret = pm8xxx_read_irq_stat(pm8058_chg.dev->parent, irq);
+	if (ret == -EAGAIN)
+		return 0;
+	else
+		return ret;
+}
+
+static int is_chg_plugged_in(void)
+{
+	return pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[CHGVAL_IRQ]);
+}
+
+#ifdef DEBUG
+static void __dump_chg_regs(void)
+{
+	u8 temp;
+	int temp2;
+
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL, &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_CNTRL = 0x%x\n", temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL_2, &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_CNTRL_2 = 0x%x\n", temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_VMAX_SEL, &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_VMAX_SEL = 0x%x\n", temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_VBAT_DET, &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_VBAT_DET = 0x%x\n", temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_IMAX, &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_IMAX = 0x%x\n", temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_TRICKLE, &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_TRICKLE = 0x%x\n", temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_ITERM, &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_ITERM = 0x%x\n", temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_TTRKL_MAX, &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_TTRKL_MAX = 0x%x\n", temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_TCHG_MAX, &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_TCHG_MAX = 0x%x\n", temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_TEMP_THRESH, &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_TEMP_THRESH = 0x%x\n", temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_TEMP_REG, &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_TEMP_REG = 0x%x\n", temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_PULSE, &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_PULSE = 0x%x\n", temp);
+
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_STATUS_CLEAR_IRQ_1,
+		    &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_STATUS_CLEAR_IRQ_1 = 0x%x\n", temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_STATUS_CLEAR_IRQ_3,
+		    &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_STATUS_CLEAR_IRQ_3 = 0x%x\n", temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_STATUS_CLEAR_IRQ_10,
+		    &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_STATUS_CLEAR_IRQ_10 = 0x%x\n",
+		temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_STATUS_CLEAR_IRQ_11,
+		    &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_STATUS_CLEAR_IRQ_11 = 0x%x\n",
+		temp);
+
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_MASK_IRQ_1, &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_MASK_IRQ_1 = 0x%x\n", temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_MASK_IRQ_3, &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_MASK_IRQ_3 = 0x%x\n", temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_MASK_IRQ_10, &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_MASK_IRQ_10 = 0x%x\n", temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_MASK_IRQ_11, &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_MASK_IRQ_11 = 0x%x\n", temp);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[CHGVAL_IRQ]);
+	dev_dbg(pm8058_chg.dev, "CHGVAL_IRQ = %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[CHGINVAL_IRQ]);
+	dev_dbg(pm8058_chg.dev, "CHGINVAL_IRQ = %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[VBATDET_LOW_IRQ]);
+	dev_dbg(pm8058_chg.dev, "VBATDET_LOW_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[VCP_IRQ]);
+	dev_dbg(pm8058_chg.dev, "VCP_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[CHGILIM_IRQ]);
+	dev_dbg(pm8058_chg.dev, "CHGILIM_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[ATC_DONE_IRQ]);
+	dev_dbg(pm8058_chg.dev, "ATC_DONE_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[ATCFAIL_IRQ]);
+	dev_dbg(pm8058_chg.dev, "ATCFAIL_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[AUTO_CHGDONE_IRQ]);
+	dev_dbg(pm8058_chg.dev, "AUTO_CHGDONE_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[AUTO_CHGFAIL_IRQ]);
+	dev_dbg(pm8058_chg.dev, "AUTO_CHGFAIL_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[CHGSTATE_IRQ]);
+	dev_dbg(pm8058_chg.dev, "CHGSTATE_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[FASTCHG_IRQ]);
+	dev_dbg(pm8058_chg.dev, "FASTCHG_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[CHG_END_IRQ]);
+	dev_dbg(pm8058_chg.dev, "CHG_END_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[BATTTEMP_IRQ]);
+	dev_dbg(pm8058_chg.dev, "BATTTEMP_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[CHGHOT_IRQ]);
+	dev_dbg(pm8058_chg.dev, "CHGHOT_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[CHGTLIMIT_IRQ]);
+	dev_dbg(pm8058_chg.dev, "CHGTLIMIT_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[CHG_GONE_IRQ]);
+	dev_dbg(pm8058_chg.dev, "CHG_GONE_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[VCPMAJOR_IRQ]);
+	dev_dbg(pm8058_chg.dev, "VCPMAJOR_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[VBATDET_IRQ]);
+	dev_dbg(pm8058_chg.dev, "VBATDET_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[BATFET_IRQ]);
+	dev_dbg(pm8058_chg.dev, "BATFET_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[BATT_REPLACE_IRQ]);
+	dev_dbg(pm8058_chg.dev, "BATT_REPLACE_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[BATTCONNECT_IRQ]);
+	dev_dbg(pm8058_chg.dev, "BATTCONNECT_IRQ= %d\n", temp2);
+}
+#else
+static inline void __dump_chg_regs(void)
+{
+}
+#endif
+
+/* SSBI register access helper functions */
+static int pm_chg_suspend(int value)
+{
+	u8 temp;
+	int ret;
+
+	ret = pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL, &temp);
+	if (ret)
+		return ret;
+	if (value)
+		temp |= BIT(CHG_USB_SUSPEND);
+	else
+		temp &= ~BIT(CHG_USB_SUSPEND);
+
+	return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL, temp);
+}
+
+static int pm_chg_auto_disable(int value)
+{
+	u8 temp;
+	int ret;
+
+	ret = pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL_2, &temp);
+	if (ret)
+		return ret;
+	if (value)
+		temp |= BIT(CHARGE_AUTO_DIS);
+	else
+		temp &= ~BIT(CHARGE_AUTO_DIS);
+
+	return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL_2, temp);
+}
+
+static int pm_chg_batt_temp_disable(int value)
+{
+	u8 temp;
+	int ret;
+
+	ret = pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL_2, &temp);
+	if (ret)
+		return ret;
+	if (value)
+		temp |= BIT(CHG_BATT_TEMP_DIS);
+	else
+		temp &= ~BIT(CHG_BATT_TEMP_DIS);
+
+	return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL_2, temp);
+}
+
+static int pm_chg_vbatdet_set(int voltage)
+{
+	u8 temp;
+	int diff;
+
+	diff = (voltage - PM8058_CHG_V_MIN_MV);
+	if (diff < 0) {
+		dev_warn(pm8058_chg.dev, "%s bad mV=%d asked to set\n",
+			 __func__, voltage);
+		return -EINVAL;
+	}
+
+	temp = diff / PM8058_CHG_V_STEP_MV;
+	dev_dbg(pm8058_chg.dev, "%s voltage=%d setting %02x\n", __func__,
+		voltage, temp);
+	return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_VBAT_DET, temp);
+}
+
+static int pm_chg_imaxsel_set(int chg_current)
+{
+	u8 temp;
+	int diff;
+
+	diff = chg_current - PM8058_CHG_I_MIN_MA;
+	if (diff < 0) {
+		dev_warn(pm8058_chg.dev, "%s bad mA=%d asked to set\n",
+			 __func__, chg_current);
+		return -EINVAL;
+	}
+	temp = diff / PM8058_CHG_I_STEP_MA;
+	/* make sure we arent writing more than 5 bits of data */
+	if (temp > 31) {
+		dev_warn(pm8058_chg.dev, "%s max mA=1500 requested mA=%d\n",
+			__func__, chg_current);
+		temp = 31;
+	}
+	return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_IMAX, temp);
+}
+
+#define PM8058_CHG_VMAX_MIN  3300
+#define PM8058_CHG_VMAX_MAX  5500
+static int pm_chg_vmaxsel_set(int voltage)
+{
+	u8 temp;
+
+	if (voltage < PM8058_CHG_VMAX_MIN || voltage > PM8058_CHG_VMAX_MAX) {
+		dev_warn(pm8058_chg.dev, "%s bad mV=%d asked to set\n",
+			 __func__, voltage);
+		return -EINVAL;
+	}
+	temp = (voltage - PM8058_CHG_V_MIN_MV) / PM8058_CHG_V_STEP_MV;
+	dev_dbg(pm8058_chg.dev, "%s mV=%d setting %02x\n", __func__, voltage,
+		temp);
+	return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_VMAX_SEL, temp);
+}
+
+static int pm_chg_failed_clear(int value)
+{
+	u8 temp;
+	int ret;
+
+	ret = pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL_2, &temp);
+	if (ret)
+		return ret;
+	if (value)
+		temp |= BIT(CHG_FAILED_CLEAR);
+	else
+		temp &= ~BIT(CHG_FAILED_CLEAR);
+	return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL_2, temp);
+}
+
+static int pm_chg_iterm_set(int chg_current)
+{
+	u8 temp;
+
+	temp = (chg_current / PM8058_CHG_I_TERM_STEP_MA) - 1;
+	return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_ITERM, temp);
+}
+
+static int pm_chg_tchg_set(int minutes)
+{
+	u8 temp;
+
+	temp = (minutes >> PM8058_CHG_T_TCHG_SHIFT) - 1;
+	return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_TCHG_MAX, temp);
+}
+
+static int pm_chg_ttrkl_set(int minutes)
+{
+	u8 temp;
+
+	temp = minutes - 1;
+	return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_TTRKL_MAX,
+									temp);
+}
+
+static int pm_chg_enum_done_enable(int value)
+{
+	u8 temp;
+	int ret;
+
+	ret = pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL_2, &temp);
+	if (ret)
+		return ret;
+	if (value)
+		temp |= BIT(ENUM_DONE);
+	else
+		temp &= ~BIT(ENUM_DONE);
+
+	return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL_2, temp);
+}
+
+static uint32_t get_fsm_state(void)
+{
+	u8 temp;
+
+	temp = 0x00;
+	pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_TEST_3, temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_TEST_3, &temp);
+	return (uint32_t)temp;
+}
+
+static int get_fsm_status(void *data, u64 * val)
+{
+	*val = get_fsm_state();
+	return 0;
+}
+
+enum pmic8058_chg_state pmic8058_get_fsm_state(void)
+{
+	if (!pm8058_chg.inited) {
+		pr_err("%s: called when not inited\n", __func__);
+		return -EINVAL;
+	}
+
+	return get_fsm_state();
+}
+
+static int pm_chg_disable(int value)
+{
+	u8 temp;
+	int ret;
+
+	ret = pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL, &temp);
+	if (ret)
+		return ret;
+	if (value)
+		temp |= BIT(CHG_CHARGE_DIS);
+	else
+		temp &= ~BIT(CHG_CHARGE_DIS);
+
+	return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL, temp);
+}
+
+static void pm8058_start_system_current(struct msm_hardware_charger *hw_chg,
+								int max_current)
+{
+	int ret = 0;
+
+	if (pm8058_chg.disabled)
+		return;
+
+	ret = pm_chg_imaxsel_set(max_current);
+	ret |= pm_chg_enum_done_enable(1);
+	ret |= pm_chg_disable(0);
+	if (ret)
+		pr_err("%s: failed to turn on system power err=%d",
+							__func__, ret);
+}
+
+static void pm8058_stop_system_current(struct msm_hardware_charger *hw_chg)
+{
+	int ret = 0;
+
+	ret = pm_chg_enum_done_enable(0);
+	ret |= pm_chg_disable(1);
+	if (ret)
+		pr_err("%s: failed to turn off system power err=%d",
+							__func__, ret);
+}
+
+static int __pm8058_start_charging(int chg_current, int termination_current,
+				   int time)
+{
+	int ret = 0;
+
+	if (pm8058_chg.disabled)
+		goto out;
+
+	dev_info(pm8058_chg.dev, "%s %dmA %dmin\n",
+			__func__, chg_current, time);
+
+	ret = pm_chg_auto_disable(1);
+	if (ret)
+		goto out;
+
+	ret = pm_chg_suspend(0);
+	if (ret)
+		goto out;
+
+	ret = pm_chg_imaxsel_set(chg_current);
+	if (ret)
+		goto out;
+
+	ret = pm_chg_failed_clear(1);
+	if (ret)
+		goto out;
+
+	ret = pm_chg_iterm_set(termination_current);
+	if (ret)
+		goto out;
+
+	ret = pm_chg_tchg_set(time);
+	if (ret)
+		goto out;
+
+	ret = pm_chg_ttrkl_set(AUTO_CHARGING_TRICKLE_TIME_MINUTES);
+	if (ret)
+		goto out;
+
+	ret = pm_chg_batt_temp_disable(0);
+	if (ret)
+		goto out;
+
+	if (pm8058_chg.voter == NULL)
+		pm8058_chg.voter = msm_xo_get(MSM_XO_TCXO_D1, "pm8058_charger");
+	msm_xo_mode_vote(pm8058_chg.voter, MSM_XO_MODE_ON);
+
+	ret = pm_chg_enum_done_enable(1);
+	if (ret)
+		goto out;
+
+	wmb();
+
+	ret = pm_chg_auto_disable(0);
+	if (ret)
+		goto out;
+
+	/* wait for the enable to update interrupt status*/
+	msleep(20);
+
+	pm8058_chg_enable_irq(AUTO_CHGFAIL_IRQ);
+	pm8058_chg_enable_irq(CHGHOT_IRQ);
+	pm8058_chg_enable_irq(AUTO_CHGDONE_IRQ);
+	pm8058_chg_enable_irq(CHG_END_IRQ);
+	pm8058_chg_enable_irq(CHGSTATE_IRQ);
+
+out:
+	return ret;
+}
+
+static void chg_done_cleanup(void)
+{
+	dev_info(pm8058_chg.dev, "%s notify pm8058 charging completion"
+		"\n", __func__);
+
+	pm8058_chg_disable_irq(AUTO_CHGDONE_IRQ);
+	cancel_delayed_work_sync(&pm8058_chg.veoc_begin_work);
+	cancel_delayed_work_sync(&pm8058_chg.check_vbat_low_work);
+
+	pm8058_chg_disable_irq(CHG_END_IRQ);
+
+	pm8058_chg_disable_irq(VBATDET_LOW_IRQ);
+	pm8058_chg_disable_irq(VBATDET_IRQ);
+	pm8058_chg.waiting_for_veoc = 0;
+	pm8058_chg.waiting_for_topoff = 0;
+
+	pm_chg_auto_disable(1);
+
+	msm_charger_notify_event(&usb_hw_chg, CHG_DONE_EVENT);
+}
+
+static void chg_done_check_work(struct work_struct *work)
+{
+	chg_done_cleanup();
+}
+
+static void charging_check_work(struct work_struct *work)
+{
+	uint32_t fsm_state = get_fsm_state();
+	int rc;
+
+	switch (fsm_state) {
+	/* We're charging, so disarm alarm */
+	case PMIC8058_CHG_STATE_ATC:
+	case PMIC8058_CHG_STATE_FAST_CHG:
+	case PMIC8058_CHG_STATE_TRKL_CHG:
+		rc = pm8xxx_batt_alarm_disable(
+				PM8XXX_BATT_ALARM_UPPER_COMPARATOR);
+		if (!rc)
+			rc = pm8xxx_batt_alarm_disable(
+				PM8XXX_BATT_ALARM_LOWER_COMPARATOR);
+		if (rc)
+			dev_err(pm8058_chg.dev,
+				"%s: unable to set alarm state\n", __func__);
+		break;
+	default:
+		/* Still not charging, so update driver state */
+		chg_done_cleanup();
+		break;
+	};
+}
+
+static int pm8058_start_charging(struct msm_hardware_charger *hw_chg,
+				 int chg_voltage, int chg_current)
+{
+	int vbat_higher_than_vbatdet;
+	int ret = 0;
+
+	cancel_delayed_work_sync(&pm8058_chg.charging_check_work);
+
+	/*
+	 * adjust the max current for PC USB connection - set the higher limit
+	 * to 450 and make sure we never cross it
+	 */
+	if (chg_current == 500)
+		chg_current = 450;
+
+	if (hw_chg->type == CHG_TYPE_AC && chg_data.max_source_current)
+		chg_current = chg_data.max_source_current;
+
+	pm8058_chg.current_charger_current = chg_current;
+	pm8058_chg_enable_irq(FASTCHG_IRQ);
+
+	ret = pm_chg_vmaxsel_set(chg_voltage);
+	if (ret)
+		goto out;
+
+	/* set vbat to  CC to CV threshold */
+	ret = pm_chg_vbatdet_set(AUTO_CHARGING_VBATDET);
+	if (ret)
+		goto out;
+
+	pm8058_chg.vbatdet = AUTO_CHARGING_VBATDET;
+	/*
+	 * get the state of vbat and if it is higher than
+	 * AUTO_CHARGING_VBATDET we start the veoc start timer
+	 * else wait for the voltage to go to AUTO_CHARGING_VBATDET
+	 * and then start the 90 min timer
+	 */
+	vbat_higher_than_vbatdet =
+	    pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[VBATDET_IRQ]);
+	if (vbat_higher_than_vbatdet) {
+		/*
+		 * we are in constant voltage phase of charging
+		 * IEOC should happen withing 90 mins of this instant
+		 * else we enable VEOC
+		 */
+		dev_info(pm8058_chg.dev, "%s begin veoc timer\n", __func__);
+		schedule_delayed_work(&pm8058_chg.veoc_begin_work,
+				      round_jiffies_relative(msecs_to_jiffies
+				     (AUTO_CHARGING_VEOC_BEGIN_TIME_MS)));
+	} else
+		pm8058_chg_enable_irq(VBATDET_IRQ);
+
+	ret = __pm8058_start_charging(chg_current, AUTO_CHARGING_IEOC_ITERM,
+				AUTO_CHARGING_FAST_TIME_MAX_MINUTES);
+	pm8058_chg.current_charger_current = chg_current;
+
+	/*
+	 * We want to check the FSM state to verify we're charging. We must
+	 * wait before doing this to allow the VBATDET to settle. The worst
+	 * case for this is two seconds. The batt alarm does not have this
+	 * delay.
+	 */
+	schedule_delayed_work(&pm8058_chg.charging_check_work,
+				      round_jiffies_relative(msecs_to_jiffies
+			     (AUTO_CHARGING_VBATDET_DEBOUNCE_TIME_MS)));
+
+out:
+	return ret;
+}
+
+static void veoc_begin_work(struct work_struct *work)
+{
+	/* we have been doing CV for 90mins with no signs of IEOC
+	 * start checking for VEOC in addition with 16min charges*/
+	dev_info(pm8058_chg.dev, "%s begin veoc detection\n", __func__);
+	pm8058_chg.waiting_for_veoc = 1;
+	/*
+	 * disable VBATDET irq we dont need it unless we are at the end of
+	 * charge cycle
+	 */
+	pm8058_chg_disable_irq(VBATDET_IRQ);
+	__pm8058_start_charging(pm8058_chg.current_charger_current,
+				AUTO_CHARGING_VEOC_ITERM,
+				AUTO_CHARGING_VEOC_TCHG);
+}
+
+static void vbat_low_work(struct work_struct *work)
+{
+	/*
+	 * It has been one minute and the battery still holds voltage
+	 * start the final topoff - charging is almost done
+	 */
+	dev_info(pm8058_chg.dev, "%s vbatt maintains for a minute"
+		"starting topoff\n", __func__);
+	pm8058_chg.waiting_for_veoc = 0;
+	pm8058_chg.waiting_for_topoff = 1;
+	pm8058_chg_disable_irq(VBATDET_LOW_IRQ);
+	pm8058_chg_disable_irq(VBATDET_IRQ);
+	__pm8058_start_charging(pm8058_chg.current_charger_current,
+				AUTO_CHARGING_VEOC_ITERM,
+				AUTO_CHARGING_VEOC_TCHG_FINAL_CYCLE);
+}
+
+
+static irqreturn_t pm8058_chg_chgval_handler(int irq, void *dev_id)
+{
+	u8 old, temp;
+	int ret;
+
+	if (is_chg_plugged_in()) {	/* this debounces it */
+		if (!pm8058_chg.present) {
+			msm_charger_notify_event(&usb_hw_chg,
+						CHG_INSERTED_EVENT);
+			pm8058_chg.present = 1;
+		}
+	} else {
+		if (pm8058_chg.present) {
+			ret = pm8xxx_readb(pm8058_chg.dev->parent,
+						PM8058_OVP_TEST_REG,
+						&old);
+			temp = old | BIT(FORCE_OVP_OFF);
+			ret = pm8xxx_writeb(pm8058_chg.dev->parent,
+						PM8058_OVP_TEST_REG,
+						temp);
+			temp = 0xFC;
+			ret = pm8xxx_writeb(pm8058_chg.dev->parent,
+					PM8058_CHG_TEST, temp);
+			/* 10 ms sleep is for the VCHG to discharge */
+			msleep(10);
+			temp = 0xF0;
+			ret = pm8xxx_writeb(pm8058_chg.dev->parent,
+						PM8058_CHG_TEST,
+						temp);
+			ret = pm8xxx_writeb(pm8058_chg.dev->parent,
+						PM8058_OVP_TEST_REG,
+						old);
+
+			pm_chg_enum_done_enable(0);
+			pm_chg_auto_disable(1);
+			msm_charger_notify_event(&usb_hw_chg,
+						CHG_REMOVED_EVENT);
+			pm8058_chg.present = 0;
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8058_chg_chginval_handler(int irq, void *dev_id)
+{
+	u8 old, temp;
+	int ret;
+
+	if (pm8058_chg.present) {
+		pm8058_chg_disable_irq(CHGINVAL_IRQ);
+
+		pm_chg_enum_done_enable(0);
+		pm_chg_auto_disable(1);
+		ret = pm8xxx_readb(pm8058_chg.dev->parent,
+				PM8058_OVP_TEST_REG, &old);
+		temp = old | BIT(FORCE_OVP_OFF);
+		ret = pm8xxx_writeb(pm8058_chg.dev->parent,
+				PM8058_OVP_TEST_REG, temp);
+		temp = 0xFC;
+		ret = pm8xxx_writeb(pm8058_chg.dev->parent,
+				PM8058_CHG_TEST, temp);
+		/* 10 ms sleep is for the VCHG to discharge */
+		msleep(10);
+		temp = 0xF0;
+		ret = pm8xxx_writeb(pm8058_chg.dev->parent,
+				PM8058_CHG_TEST, temp);
+		ret = pm8xxx_writeb(pm8058_chg.dev->parent,
+				PM8058_OVP_TEST_REG, old);
+
+		if (!is_chg_plugged_in()) {
+			msm_charger_notify_event(&usb_hw_chg,
+					CHG_REMOVED_EVENT);
+			pm8058_chg.present = 0;
+		} else {
+			/* was a fake */
+			pm8058_chg_enable_irq(CHGINVAL_IRQ);
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8058_chg_auto_chgdone_handler(int irq, void *dev_id)
+{
+	dev_info(pm8058_chg.dev, "%s waiting a sec to confirm\n",
+		__func__);
+	pm8058_chg_disable_irq(AUTO_CHGDONE_IRQ);
+	pm8058_chg_disable_irq(VBATDET_IRQ);
+	if (!delayed_work_pending(&pm8058_chg.chg_done_check_work)) {
+		schedule_delayed_work(&pm8058_chg.chg_done_check_work,
+				      round_jiffies_relative(msecs_to_jiffies
+			     (AUTO_CHARGING_DONE_CHECK_TIME_MS)));
+	}
+	return IRQ_HANDLED;
+}
+
+/* can only happen with the pmic charger when it has been charing
+ * for either 16 mins wating for VEOC or 32 mins for topoff
+ * without a IEOC indication */
+static irqreturn_t pm8058_chg_auto_chgfail_handler(int irq, void *dev_id)
+{
+	pm8058_chg_disable_irq(AUTO_CHGFAIL_IRQ);
+
+	if (pm8058_chg.waiting_for_topoff == 1) {
+		dev_info(pm8058_chg.dev, "%s topoff done, charging done\n",
+			__func__);
+		pm8058_chg.waiting_for_topoff = 0;
+		/* notify we are done charging */
+		msm_charger_notify_event(&usb_hw_chg, CHG_DONE_EVENT);
+	} else {
+		/* start one minute timer and monitor VBATDET_LOW */
+		dev_info(pm8058_chg.dev, "%s monitoring vbat_low for a"
+			"minute\n", __func__);
+		schedule_delayed_work(&pm8058_chg.check_vbat_low_work,
+				      round_jiffies_relative(msecs_to_jiffies
+			     (AUTO_CHARGING_VEOC_VBAT_LOW_CHECK_TIME_MS)));
+
+		/* note we are waiting on veoc */
+		pm8058_chg.waiting_for_veoc = 1;
+
+		pm_chg_vbatdet_set(AUTO_CHARGING_VEOC_VBATDET);
+		pm8058_chg.vbatdet = AUTO_CHARGING_VEOC_VBATDET;
+		pm8058_chg_enable_irq(VBATDET_LOW_IRQ);
+	}
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8058_chg_chgstate_handler(int irq, void *dev_id)
+{
+	u8 temp;
+
+	temp = 0x00;
+	if (!pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_TEST_3, temp)) {
+		pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_TEST_3, &temp);
+		dev_dbg(pm8058_chg.dev, "%s state=%d\n", __func__, temp);
+	}
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8058_chg_fastchg_handler(int irq, void *dev_id)
+{
+	pm8058_chg_disable_irq(FASTCHG_IRQ);
+
+	/* we have begun the fast charging state */
+	dev_info(pm8058_chg.dev, "%s begin fast charging"
+		, __func__);
+	msm_charger_notify_event(&usb_hw_chg, CHG_BATT_BEGIN_FAST_CHARGING);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8058_chg_batttemp_handler(int irq, void *dev_id)
+{
+	int ret;
+
+	/* we could get temperature
+	 * interrupt when the battery is plugged out
+	 */
+	ret = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[BATTCONNECT_IRQ]);
+	if (ret) {
+		msm_charger_notify_event(&usb_hw_chg, CHG_BATT_REMOVED);
+	} else {
+		/* read status to determine we are inrange or outofrange */
+		ret =
+		    pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[BATTTEMP_IRQ]);
+		if (ret)
+			msm_charger_notify_event(&usb_hw_chg,
+						 CHG_BATT_TEMP_OUTOFRANGE);
+		else
+			msm_charger_notify_event(&usb_hw_chg,
+						 CHG_BATT_TEMP_INRANGE);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8058_chg_vbatdet_handler(int irq, void *dev_id)
+{
+	int ret;
+
+	/* settling time */
+	msleep(20);
+	ret = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[VBATDET_IRQ]);
+
+	if (ret) {
+		if (pm8058_chg.vbatdet == AUTO_CHARGING_VBATDET
+			&& !delayed_work_pending(&pm8058_chg.veoc_begin_work)) {
+			/*
+			 * we are in constant voltage phase of charging
+			 * IEOC should happen withing 90 mins of this instant
+			 * else we enable VEOC
+			 */
+			dev_info(pm8058_chg.dev, "%s entered constant voltage"
+				"begin veoc timer\n", __func__);
+			schedule_delayed_work(&pm8058_chg.veoc_begin_work,
+				      round_jiffies_relative
+				      (msecs_to_jiffies
+				      (AUTO_CHARGING_VEOC_BEGIN_TIME_MS)));
+		}
+	} else {
+		if (pm8058_chg.vbatdet == AUTO_CHARGING_VEOC_VBATDET) {
+			cancel_delayed_work_sync(
+				&pm8058_chg.check_vbat_low_work);
+
+			if (pm8058_chg.waiting_for_topoff
+			    || pm8058_chg.waiting_for_veoc) {
+				/*
+				 * the battery dropped its voltage below 4100
+				 * around a minute charge the battery for 16
+				 * mins and check vbat again for a minute
+				 */
+				dev_info(pm8058_chg.dev, "%s batt dropped vlt"
+					"within a minute\n", __func__);
+				pm8058_chg.waiting_for_topoff = 0;
+				pm8058_chg.waiting_for_veoc = 1;
+				pm8058_chg_disable_irq(VBATDET_IRQ);
+				__pm8058_start_charging(pm8058_chg.
+						current_charger_current,
+						AUTO_CHARGING_VEOC_ITERM,
+						AUTO_CHARGING_VEOC_TCHG);
+			}
+		}
+	}
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8058_chg_batt_replace_handler(int irq, void *dev_id)
+{
+	int ret;
+
+	pm8058_chg_disable_irq(BATT_REPLACE_IRQ);
+	ret = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[BATT_REPLACE_IRQ]);
+	if (ret) {
+		msm_charger_notify_event(&usb_hw_chg, CHG_BATT_INSERTED);
+		/*
+		 * battery is present enable batt removal
+		 * and batt temperatture interrupt
+		 */
+		pm8058_chg_enable_irq(BATTCONNECT_IRQ);
+	}
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8058_chg_battconnect_handler(int irq, void *dev_id)
+{
+	int ret;
+
+	ret = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[BATTCONNECT_IRQ]);
+	if (ret) {
+		msm_charger_notify_event(&usb_hw_chg, CHG_BATT_REMOVED);
+	} else {
+		msm_charger_notify_event(&usb_hw_chg, CHG_BATT_INSERTED);
+		pm8058_chg_enable_irq(BATTTEMP_IRQ);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int get_rt_status(void *data, u64 * val)
+{
+	int i = (int)data;
+	int ret;
+
+	ret = pm_chg_get_rt_status(i);
+	*val = ret;
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(rt_fops, get_rt_status, NULL, "%llu\n");
+DEFINE_SIMPLE_ATTRIBUTE(fsm_fops, get_fsm_status, NULL, "%llu\n");
+
+static void free_irqs(void)
+{
+	int i;
+
+	for (i = 0; i < PMIC_CHG_MAX_INTS; i++)
+		if (pm8058_chg.pmic_chg_irq[i]) {
+			free_irq(pm8058_chg.pmic_chg_irq[i], NULL);
+			pm8058_chg.pmic_chg_irq[i] = 0;
+		}
+}
+
+static int __devinit request_irqs(struct platform_device *pdev)
+{
+	struct resource *res;
+	int ret;
+
+	ret = 0;
+	bitmap_fill(pm8058_chg.enabled_irqs, PMIC_CHG_MAX_INTS);
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "CHGVAL");
+	if (res == NULL) {
+		dev_err(pm8058_chg.dev,
+			"%s:couldnt find resource CHGVAL\n", __func__);
+		goto err_out;
+	} else {
+		ret = request_threaded_irq(res->start, NULL,
+				  pm8058_chg_chgval_handler,
+				  IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+				  res->name, NULL);
+		if (ret < 0) {
+			dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
+				__func__, res->start, ret);
+			goto err_out;
+		} else {
+			pm8058_chg.pmic_chg_irq[CHGVAL_IRQ] = res->start;
+			pm8058_chg_disable_irq(CHGVAL_IRQ);
+			enable_irq_wake(pm8058_chg.pmic_chg_irq[CHGVAL_IRQ]);
+		}
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "CHGINVAL");
+	if (res == NULL) {
+		dev_err(pm8058_chg.dev,
+			"%s:couldnt find resource CHGINVAL\n", __func__);
+		goto err_out;
+	} else {
+		ret = request_threaded_irq(res->start, NULL,
+				  pm8058_chg_chginval_handler,
+				  IRQF_TRIGGER_RISING, res->name, NULL);
+		if (ret < 0) {
+			dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
+				__func__, res->start, ret);
+			goto err_out;
+		} else {
+			pm8058_chg.pmic_chg_irq[CHGINVAL_IRQ] = res->start;
+			pm8058_chg_disable_irq(CHGINVAL_IRQ);
+		}
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+					   "AUTO_CHGDONE");
+	if (res == NULL) {
+		dev_err(pm8058_chg.dev,
+			"%s:couldnt find resource AUTO_CHGDONE\n", __func__);
+		goto err_out;
+	} else {
+		ret = request_irq(res->start,
+				  pm8058_chg_auto_chgdone_handler,
+				  IRQF_TRIGGER_RISING,
+				  res->name, NULL);
+		if (ret < 0) {
+			dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
+				__func__, res->start, ret);
+			goto err_out;
+		} else {
+			pm8058_chg.pmic_chg_irq[AUTO_CHGDONE_IRQ] = res->start;
+			pm8058_chg_disable_irq(AUTO_CHGDONE_IRQ);
+		}
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+					   "AUTO_CHGFAIL");
+	if (res == NULL) {
+		dev_err(pm8058_chg.dev,
+			"%s:couldnt find resource AUTO_CHGFAIL\n", __func__);
+		goto err_out;
+	} else {
+		ret = request_irq(res->start,
+				  pm8058_chg_auto_chgfail_handler,
+				  IRQF_TRIGGER_RISING, res->name, NULL);
+		if (ret < 0) {
+			dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
+				__func__, res->start, ret);
+			goto err_out;
+		} else {
+			pm8058_chg.pmic_chg_irq[AUTO_CHGFAIL_IRQ] = res->start;
+			pm8058_chg_disable_irq(AUTO_CHGFAIL_IRQ);
+		}
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "CHGSTATE");
+	if (res == NULL) {
+		dev_err(pm8058_chg.dev,
+			"%s:couldnt find resource CHGSTATE\n", __func__);
+		goto err_out;
+	} else {
+		ret = request_irq(res->start,
+				  pm8058_chg_chgstate_handler,
+				  IRQF_TRIGGER_RISING, res->name, NULL);
+		if (ret < 0) {
+			dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
+				__func__, res->start, ret);
+			goto err_out;
+		} else {
+			pm8058_chg.pmic_chg_irq[CHGSTATE_IRQ] = res->start;
+			pm8058_chg_disable_irq(CHGSTATE_IRQ);
+		}
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "FASTCHG");
+	if (res == NULL) {
+		dev_err(pm8058_chg.dev,
+			"%s:couldnt find resource FASTCHG\n", __func__);
+		goto err_out;
+	} else {
+		ret = request_irq(res->start,
+				  pm8058_chg_fastchg_handler,
+				  IRQF_TRIGGER_RISING, res->name, NULL);
+		if (ret < 0) {
+			dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
+				__func__, res->start, ret);
+			goto err_out;
+		} else {
+			pm8058_chg.pmic_chg_irq[FASTCHG_IRQ] = res->start;
+			pm8058_chg_disable_irq(FASTCHG_IRQ);
+		}
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "BATTTEMP");
+	if (res == NULL) {
+		dev_err(pm8058_chg.dev,
+			"%s:couldnt find resource CHG_END\n", __func__);
+		goto err_out;
+	} else {
+		ret = request_irq(res->start,
+				  pm8058_chg_batttemp_handler,
+				  IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+				  res->name, NULL);
+		if (ret < 0) {
+			dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
+				__func__, res->start, ret);
+			goto err_out;
+		} else {
+			pm8058_chg.pmic_chg_irq[BATTTEMP_IRQ] = res->start;
+			pm8058_chg_disable_irq(BATTTEMP_IRQ);
+		}
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+					   "BATT_REPLACE");
+	if (res == NULL) {
+		dev_err(pm8058_chg.dev,
+			"%s:couldnt find resource BATT_REPLACE\n", __func__);
+		goto err_out;
+	} else {
+		ret = request_irq(res->start,
+				  pm8058_chg_batt_replace_handler,
+				  IRQF_TRIGGER_RISING, res->name, NULL);
+		if (ret < 0) {
+			dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
+				__func__, res->start, ret);
+			goto err_out;
+		} else {
+			pm8058_chg.pmic_chg_irq[BATT_REPLACE_IRQ] = res->start;
+			pm8058_chg_disable_irq(BATT_REPLACE_IRQ);
+		}
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "BATTCONNECT");
+	if (res == NULL) {
+		dev_err(pm8058_chg.dev,
+			"%s:couldnt find resource BATTCONNECT\n", __func__);
+		goto err_out;
+	} else {
+		ret = request_irq(res->start,
+				  pm8058_chg_battconnect_handler,
+				  IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+				  res->name, NULL);
+		if (ret < 0) {
+			dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
+				__func__, res->start, ret);
+			goto err_out;
+		} else {
+			pm8058_chg.pmic_chg_irq[BATTCONNECT_IRQ] = res->start;
+			pm8058_chg_disable_irq(BATTCONNECT_IRQ);
+		}
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "VBATDET");
+	if (res == NULL) {
+		dev_err(pm8058_chg.dev,
+			"%s:couldnt find resource VBATDET\n", __func__);
+		goto err_out;
+	} else {
+		ret = request_threaded_irq(res->start, NULL,
+				  pm8058_chg_vbatdet_handler,
+				  IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+				  res->name, NULL);
+		if (ret < 0) {
+			dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
+				__func__, res->start, ret);
+			goto err_out;
+		} else {
+			pm8058_chg.pmic_chg_irq[VBATDET_IRQ] = res->start;
+			pm8058_chg_disable_irq(VBATDET_IRQ);
+		}
+	}
+
+	return 0;
+
+err_out:
+	free_irqs();
+	return -EINVAL;
+}
+
+static int pm8058_get_charge_batt(void)
+{
+	u8 temp;
+	int rc;
+
+	rc = pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL, &temp);
+	if (rc)
+		return rc;
+
+	temp &= BIT(CHG_CHARGE_BAT);
+	if (temp)
+		temp = 1;
+	return temp;
+}
+EXPORT_SYMBOL(pm8058_get_charge_batt);
+
+static int pm8058_set_charge_batt(int on)
+{
+	u8 temp;
+	int rc;
+
+	rc = pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL, &temp);
+	if (rc)
+		return rc;
+	if (on)
+		temp |= BIT(CHG_CHARGE_BAT);
+	else
+		temp &= ~BIT(CHG_CHARGE_BAT);
+
+	return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL, temp);
+
+}
+EXPORT_SYMBOL(pm8058_set_charge_batt);
+
+static int get_charge_batt(void *data, u64 * val)
+{
+	int ret;
+
+	ret = pm8058_get_charge_batt();
+	if (ret < 0)
+		return ret;
+
+	*val = ret;
+	return 0;
+}
+
+static int set_charge_batt(void *data, u64 val)
+{
+	return pm8058_set_charge_batt(val);
+}
+DEFINE_SIMPLE_ATTRIBUTE(fet_fops, get_charge_batt, set_charge_batt, "%llu\n");
+
+static void pm8058_chg_determine_initial_state(void)
+{
+	if (is_chg_plugged_in()) {
+		pm8058_chg.present = 1;
+		msm_charger_notify_event(&usb_hw_chg, CHG_INSERTED_EVENT);
+		dev_info(pm8058_chg.dev, "%s charger present\n", __func__);
+	} else {
+		pm8058_chg.present = 0;
+		dev_info(pm8058_chg.dev, "%s charger absent\n", __func__);
+	}
+	pm8058_chg_enable_irq(CHGVAL_IRQ);
+}
+
+static int pm8058_stop_charging(struct msm_hardware_charger *hw_chg)
+{
+	int ret;
+
+	dev_info(pm8058_chg.dev, "%s stopping charging\n", __func__);
+
+	/* disable the irqs enabled while charging */
+	pm8058_chg_disable_irq(AUTO_CHGFAIL_IRQ);
+	pm8058_chg_disable_irq(CHGHOT_IRQ);
+	pm8058_chg_disable_irq(AUTO_CHGDONE_IRQ);
+	pm8058_chg_disable_irq(FASTCHG_IRQ);
+	pm8058_chg_disable_irq(CHG_END_IRQ);
+	pm8058_chg_disable_irq(VBATDET_IRQ);
+	pm8058_chg_disable_irq(VBATDET_LOW_IRQ);
+
+	cancel_delayed_work_sync(&pm8058_chg.veoc_begin_work);
+	cancel_delayed_work_sync(&pm8058_chg.check_vbat_low_work);
+	cancel_delayed_work_sync(&pm8058_chg.chg_done_check_work);
+	cancel_delayed_work_sync(&pm8058_chg.charging_check_work);
+
+	ret = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[FASTCHG_IRQ]);
+	if (ret == 1)
+		pm_chg_suspend(1);
+	else
+		dev_err(pm8058_chg.dev,
+			"%s called when not fast-charging\n", __func__);
+
+	pm_chg_failed_clear(1);
+
+	pm8058_chg.waiting_for_veoc = 0;
+	pm8058_chg.waiting_for_topoff = 0;
+
+	if (pm8058_chg.voter)
+		msm_xo_mode_vote(pm8058_chg.voter, MSM_XO_MODE_OFF);
+
+	return 0;
+}
+
+static int get_status(void *data, u64 * val)
+{
+	*val = pm8058_chg.current_charger_current;
+	return 0;
+}
+
+static int set_status(void *data, u64 val)
+{
+
+	pm8058_chg.current_charger_current = val;
+	if (pm8058_chg.current_charger_current)
+		pm8058_start_charging(NULL,
+			AUTO_CHARGING_VMAXSEL,
+			pm8058_chg.current_charger_current);
+	else
+		pm8058_stop_charging(NULL);
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(chg_fops, get_status, set_status, "%llu\n");
+
+static int set_disable_status_param(const char *val, struct kernel_param *kp)
+{
+	int ret;
+
+	ret = param_set_int(val, kp);
+	if (ret)
+		return ret;
+
+	if (pm8058_chg.inited && pm8058_chg.disabled) {
+		/*
+		 * stop_charging is called during usb suspend
+		 * act as the usb is removed by disabling auto and enum
+		 */
+		pm_chg_enum_done_enable(0);
+		pm_chg_auto_disable(1);
+		pm8058_stop_charging(NULL);
+	}
+	return 0;
+}
+module_param_call(disabled, set_disable_status_param, param_get_uint,
+					&(pm8058_chg.disabled), 0644);
+
+static int pm8058_charging_switched(struct msm_hardware_charger *hw_chg)
+{
+	u8 temp;
+
+	temp = 0xA3;
+	pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_TEST_2, temp);
+	temp = 0x84;
+	pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_TEST_2, temp);
+	msleep(2);
+	temp = 0x80;
+	pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_TEST_2, temp);
+	return 0;
+}
+
+static int get_reg(void *data, u64 * val)
+{
+	int i = (int)data;
+	int ret;
+	u8 temp;
+
+	ret = pm8xxx_readb(pm8058_chg.dev->parent, i, &temp);
+	if (ret)
+		return -EAGAIN;
+	*val = temp;
+	return 0;
+}
+
+static int set_reg(void *data, u64 val)
+{
+	int i = (int)data;
+	int ret;
+	u8 temp;
+
+	temp = (u8) val;
+	ret = pm8xxx_writeb(pm8058_chg.dev->parent, i, temp);
+	mb();
+	if (ret)
+		return -EAGAIN;
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_reg, set_reg, "%llu\n");
+
+#ifdef CONFIG_DEBUG_FS
+static void create_debugfs_entries(void)
+{
+	pm8058_chg.dent = debugfs_create_dir("pm8058_usb_chg", NULL);
+
+	if (IS_ERR(pm8058_chg.dent)) {
+		pr_err("pmic charger couldnt create debugfs dir\n");
+		return;
+	}
+
+	debugfs_create_file("CHG_CNTRL", 0644, pm8058_chg.dent,
+			    (void *)PM8058_CHG_CNTRL, &reg_fops);
+	debugfs_create_file("CHG_CNTRL_2", 0644, pm8058_chg.dent,
+			    (void *)PM8058_CHG_CNTRL_2, &reg_fops);
+	debugfs_create_file("CHG_VMAX_SEL", 0644, pm8058_chg.dent,
+			    (void *)PM8058_CHG_VMAX_SEL, &reg_fops);
+	debugfs_create_file("CHG_VBAT_DET", 0644, pm8058_chg.dent,
+			    (void *)PM8058_CHG_VBAT_DET, &reg_fops);
+	debugfs_create_file("CHG_IMAX", 0644, pm8058_chg.dent,
+			    (void *)PM8058_CHG_IMAX, &reg_fops);
+	debugfs_create_file("CHG_TRICKLE", 0644, pm8058_chg.dent,
+			    (void *)PM8058_CHG_TRICKLE, &reg_fops);
+	debugfs_create_file("CHG_ITERM", 0644, pm8058_chg.dent,
+			    (void *)PM8058_CHG_ITERM, &reg_fops);
+	debugfs_create_file("CHG_TTRKL_MAX", 0644, pm8058_chg.dent,
+			    (void *)PM8058_CHG_TTRKL_MAX, &reg_fops);
+	debugfs_create_file("CHG_TCHG_MAX", 0644, pm8058_chg.dent,
+			    (void *)PM8058_CHG_TCHG_MAX, &reg_fops);
+	debugfs_create_file("CHG_TEMP_THRESH", 0644, pm8058_chg.dent,
+			    (void *)PM8058_CHG_TEMP_THRESH, &reg_fops);
+	debugfs_create_file("CHG_TEMP_REG", 0644, pm8058_chg.dent,
+			    (void *)PM8058_CHG_TEMP_REG, &reg_fops);
+
+	debugfs_create_file("FSM_STATE", 0644, pm8058_chg.dent, NULL,
+			    &fsm_fops);
+
+	debugfs_create_file("stop", 0644, pm8058_chg.dent, NULL,
+			    &chg_fops);
+
+	if (pm8058_chg.pmic_chg_irq[CHGVAL_IRQ])
+		debugfs_create_file("CHGVAL", 0444, pm8058_chg.dent,
+				    (void *)pm8058_chg.pmic_chg_irq[CHGVAL_IRQ],
+				    &rt_fops);
+
+	if (pm8058_chg.pmic_chg_irq[CHGINVAL_IRQ])
+		debugfs_create_file("CHGINVAL", 0444, pm8058_chg.dent, (void *)
+				    pm8058_chg.pmic_chg_irq[CHGINVAL_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[CHGILIM_IRQ])
+		debugfs_create_file("CHGILIM", 0444, pm8058_chg.dent, (void *)
+				    pm8058_chg.pmic_chg_irq[CHGILIM_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[VCP_IRQ])
+		debugfs_create_file("VCP", 0444, pm8058_chg.dent,
+				    (void *)pm8058_chg.pmic_chg_irq[VCP_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[ATC_DONE_IRQ])
+		debugfs_create_file("ATC_DONE", 0444, pm8058_chg.dent, (void *)
+				    pm8058_chg.pmic_chg_irq[ATC_DONE_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[ATCFAIL_IRQ])
+		debugfs_create_file("ATCFAIL", 0444, pm8058_chg.dent, (void *)
+				    pm8058_chg.pmic_chg_irq[ATCFAIL_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[AUTO_CHGDONE_IRQ])
+		debugfs_create_file("AUTO_CHGDONE", 0444, pm8058_chg.dent,
+				    (void *)
+				    pm8058_chg.pmic_chg_irq[AUTO_CHGDONE_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[AUTO_CHGFAIL_IRQ])
+		debugfs_create_file("AUTO_CHGFAIL", 0444, pm8058_chg.dent,
+				    (void *)
+				    pm8058_chg.pmic_chg_irq[AUTO_CHGFAIL_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[CHGSTATE_IRQ])
+		debugfs_create_file("CHGSTATE", 0444, pm8058_chg.dent, (void *)
+				    pm8058_chg.pmic_chg_irq[CHGSTATE_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[FASTCHG_IRQ])
+		debugfs_create_file("FASTCHG", 0444, pm8058_chg.dent, (void *)
+				    pm8058_chg.pmic_chg_irq[FASTCHG_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[CHG_END_IRQ])
+		debugfs_create_file("CHG_END", 0444, pm8058_chg.dent, (void *)
+				    pm8058_chg.pmic_chg_irq[CHG_END_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[BATTTEMP_IRQ])
+		debugfs_create_file("BATTTEMP", 0444, pm8058_chg.dent, (void *)
+				    pm8058_chg.pmic_chg_irq[BATTTEMP_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[CHGHOT_IRQ])
+		debugfs_create_file("CHGHOT", 0444, pm8058_chg.dent,
+				    (void *)pm8058_chg.pmic_chg_irq[CHGHOT_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[CHGTLIMIT_IRQ])
+		debugfs_create_file("CHGTLIMIT", 0444, pm8058_chg.dent, (void *)
+				    pm8058_chg.pmic_chg_irq[CHGTLIMIT_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[CHG_GONE_IRQ])
+		debugfs_create_file("CHG_GONE", 0444, pm8058_chg.dent, (void *)
+				    pm8058_chg.pmic_chg_irq[CHG_GONE_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[VCPMAJOR_IRQ])
+		debugfs_create_file("VCPMAJOR", 0444, pm8058_chg.dent, (void *)
+				    pm8058_chg.pmic_chg_irq[VCPMAJOR_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[BATFET_IRQ])
+		debugfs_create_file("BATFET", 0444, pm8058_chg.dent,
+				    (void *)pm8058_chg.pmic_chg_irq[BATFET_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[BATT_REPLACE_IRQ])
+		debugfs_create_file("BATT_REPLACE", 0444, pm8058_chg.dent,
+				    (void *)
+				    pm8058_chg.pmic_chg_irq[BATT_REPLACE_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[BATTCONNECT_IRQ])
+		debugfs_create_file("BATTCONNECT", 0444, pm8058_chg.dent,
+				    (void *)
+				    pm8058_chg.pmic_chg_irq[BATTCONNECT_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[VBATDET_IRQ])
+		debugfs_create_file("VBATDET", 0444, pm8058_chg.dent, (void *)
+				    pm8058_chg.pmic_chg_irq[VBATDET_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[VBATDET_LOW_IRQ])
+		debugfs_create_file("VBATDET_LOW", 0444, pm8058_chg.dent,
+				    (void *)
+				    pm8058_chg.pmic_chg_irq[VBATDET_LOW_IRQ],
+				    &rt_fops);
+	debugfs_create_file("CHARGE_BATT", 0444, pm8058_chg.dent,
+				    NULL,
+				    &fet_fops);
+}
+#else
+static inline void create_debugfs_entries(void)
+{
+}
+#endif
+
+static void remove_debugfs_entries(void)
+{
+	debugfs_remove_recursive(pm8058_chg.dent);
+}
+
+static struct msm_hardware_charger usb_hw_chg = {
+	.type			= CHG_TYPE_USB,
+	.rating			= 1,
+	.name			= "pm8058-usb",
+	.start_charging		= pm8058_start_charging,
+	.stop_charging		= pm8058_stop_charging,
+	.charging_switched	= pm8058_charging_switched,
+	.start_system_current	= pm8058_start_system_current,
+	.stop_system_current	= pm8058_stop_system_current,
+};
+
+static int batt_read_adc(int channel, int *mv_reading)
+{
+	int ret;
+	void *h;
+	struct adc_chan_result adc_chan_result;
+	struct completion  conv_complete_evt;
+
+	pr_debug("%s: called for %d\n", __func__, channel);
+	ret = adc_channel_open(channel, &h);
+	if (ret) {
+		pr_err("%s: couldnt open channel %d ret=%d\n",
+					__func__, channel, ret);
+		goto out;
+	}
+	init_completion(&conv_complete_evt);
+	ret = adc_channel_request_conv(h, &conv_complete_evt);
+	if (ret) {
+		pr_err("%s: couldnt request conv channel %d ret=%d\n",
+						__func__, channel, ret);
+		goto out;
+	}
+	wait_for_completion(&conv_complete_evt);
+	ret = adc_channel_read_result(h, &adc_chan_result);
+	if (ret) {
+		pr_err("%s: couldnt read result channel %d ret=%d\n",
+						__func__, channel, ret);
+		goto out;
+	}
+	ret = adc_channel_close(h);
+	if (ret) {
+		pr_err("%s: couldnt close channel %d ret=%d\n",
+						__func__, channel, ret);
+	}
+	if (mv_reading)
+		*mv_reading = adc_chan_result.measurement;
+
+	pr_debug("%s: done for %d\n", __func__, channel);
+	return adc_chan_result.physical;
+out:
+	pr_debug("%s: done for %d\n", __func__, channel);
+	return -EINVAL;
+
+}
+
+#define BATT_THERM_OPEN_MV  2000
+static int pm8058_is_battery_present(void)
+{
+	int mv_reading;
+
+	mv_reading = 0;
+	batt_read_adc(CHANNEL_ADC_BATT_THERM, &mv_reading);
+	pr_debug("%s: therm_raw is %d\n", __func__, mv_reading);
+	if (mv_reading > 0 && mv_reading < BATT_THERM_OPEN_MV)
+		return 1;
+
+	return 0;
+}
+
+static int pm8058_get_battery_temperature(void)
+{
+	return batt_read_adc(CHANNEL_ADC_BATT_THERM, NULL);
+}
+
+#define BATT_THERM_OPERATIONAL_MAX_CELCIUS 40
+#define BATT_THERM_OPERATIONAL_MIN_CELCIUS 0
+static int pm8058_is_battery_temp_within_range(void)
+{
+	int therm_celcius;
+
+	therm_celcius = pm8058_get_battery_temperature();
+	pr_debug("%s: therm_celcius is %d\n", __func__, therm_celcius);
+	if (therm_celcius > 0
+		&& therm_celcius > BATT_THERM_OPERATIONAL_MIN_CELCIUS
+		&& therm_celcius < BATT_THERM_OPERATIONAL_MAX_CELCIUS)
+		return 1;
+
+	return 0;
+}
+
+#define BATT_ID_MAX_MV  800
+#define BATT_ID_MIN_MV  600
+static int pm8058_is_battery_id_valid(void)
+{
+	int batt_id_mv;
+
+	batt_id_mv = batt_read_adc(CHANNEL_ADC_BATT_ID, NULL);
+	pr_debug("%s: batt_id_mv is %d\n", __func__, batt_id_mv);
+
+	/*
+	 * The readings are not in range
+	 * assume battery is present for now
+	 */
+	return 1;
+
+	if (batt_id_mv > 0
+		&& batt_id_mv > BATT_ID_MIN_MV
+		&& batt_id_mv < BATT_ID_MAX_MV)
+		return 1;
+
+	return 0;
+}
+
+/* returns voltage in mV */
+static int pm8058_get_battery_mvolts(void)
+{
+	int vbatt_mv;
+
+	vbatt_mv = batt_read_adc(CHANNEL_ADC_VBATT, NULL);
+	pr_debug("%s: vbatt_mv is %d\n", __func__, vbatt_mv);
+	if (vbatt_mv > 0)
+		return vbatt_mv;
+	/*
+	 * return 0 to tell the upper layers
+	 * we couldnt read the battery voltage
+	 */
+	return 0;
+}
+
+static int msm_battery_gauge_alarm_notify(struct notifier_block *nb,
+		unsigned long status, void *unused)
+{
+	int rc;
+
+	pr_info("%s: status: %lu\n", __func__, status);
+
+	switch (status) {
+	case 0:
+		dev_err(pm8058_chg.dev,
+			"%s: spurious interrupt\n", __func__);
+		break;
+	/* expected case - trip of low threshold */
+	case 1:
+		rc = pm8xxx_batt_alarm_disable(
+				PM8XXX_BATT_ALARM_UPPER_COMPARATOR);
+		if (!rc)
+			rc = pm8xxx_batt_alarm_disable(
+				PM8XXX_BATT_ALARM_LOWER_COMPARATOR);
+		if (rc)
+			dev_err(pm8058_chg.dev,
+				"%s: unable to set alarm state\n", __func__);
+		msm_charger_notify_event(NULL, CHG_BATT_NEEDS_RECHARGING);
+		break;
+	case 2:
+		dev_err(pm8058_chg.dev,
+			"%s: trip of high threshold\n", __func__);
+		break;
+	default:
+		dev_err(pm8058_chg.dev,
+			"%s: error received\n", __func__);
+	};
+
+	return 0;
+}
+
+static int pm8058_monitor_for_recharging(void)
+{
+	int rc;
+	/* enable low comparator */
+	rc = pm8xxx_batt_alarm_disable(PM8XXX_BATT_ALARM_UPPER_COMPARATOR);
+	if (!rc)
+		return pm8xxx_batt_alarm_enable(
+				PM8XXX_BATT_ALARM_LOWER_COMPARATOR);
+
+	return rc;
+
+}
+
+static struct msm_battery_gauge pm8058_batt_gauge = {
+	.get_battery_mvolts = pm8058_get_battery_mvolts,
+	.get_battery_temperature = pm8058_get_battery_temperature,
+	.is_battery_present = pm8058_is_battery_present,
+	.is_battery_temp_within_range = pm8058_is_battery_temp_within_range,
+	.is_battery_id_valid = pm8058_is_battery_id_valid,
+	.monitor_for_recharging = pm8058_monitor_for_recharging,
+};
+
+static int pm8058_usb_voltage_lower_limit(void)
+{
+	u8 temp, old;
+	int ret = 0;
+
+	temp = 0x10;
+	ret |= pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_TEST, temp);
+	ret |= pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_TEST, &old);
+	old = old & ~BIT(IGNORE_LL);
+	temp = 0x90  | (0xF & old);
+	ret |= pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_TEST, temp);
+
+	return ret;
+}
+
+static int __devinit pm8058_charger_probe(struct platform_device *pdev)
+{
+	struct pmic8058_charger_data *pdata;
+	int rc = 0;
+
+	pm8058_chg.pdata = pdev->dev.platform_data;
+	pm8058_chg.dev = &pdev->dev;
+	pdata = (struct pmic8058_charger_data *) pm8058_chg.pdata;
+
+	if (pdata == NULL) {
+		pr_err("%s: pdata not present\n", __func__);
+		return -EINVAL;
+	}
+
+	if (pdata->charger_data_valid) {
+		usb_hw_chg.type = pdata->charger_type;
+		chg_data.charger_type = pdata->charger_type;
+		chg_data.max_source_current = pdata->max_source_current;
+	}
+
+	rc = request_irqs(pdev);
+	if (rc) {
+		pr_err("%s: couldnt register interrupts\n", __func__);
+		goto out;
+	}
+
+	rc = pm8058_usb_voltage_lower_limit();
+	if (rc) {
+		pr_err("%s: couldnt set ignore lower limit bit to 0\n",
+								__func__);
+		goto free_irq;
+	}
+
+	rc = msm_charger_register(&usb_hw_chg);
+	if (rc) {
+		pr_err("%s: msm_charger_register failed ret=%d\n",
+							__func__, rc);
+		goto free_irq;
+	}
+
+	pm_chg_batt_temp_disable(0);
+	msm_battery_gauge_register(&pm8058_batt_gauge);
+	__dump_chg_regs();
+
+	create_debugfs_entries();
+	INIT_DELAYED_WORK(&pm8058_chg.veoc_begin_work, veoc_begin_work);
+	INIT_DELAYED_WORK(&pm8058_chg.check_vbat_low_work, vbat_low_work);
+	INIT_DELAYED_WORK(&pm8058_chg.chg_done_check_work, chg_done_check_work);
+	INIT_DELAYED_WORK(&pm8058_chg.charging_check_work, charging_check_work);
+
+	/* determine what state the charger is in */
+	pm8058_chg_determine_initial_state();
+
+	pm8058_chg_enable_irq(BATTTEMP_IRQ);
+	pm8058_chg_enable_irq(BATTCONNECT_IRQ);
+
+	rc = pm8xxx_batt_alarm_disable(PM8XXX_BATT_ALARM_UPPER_COMPARATOR);
+	if (!rc)
+		rc = pm8xxx_batt_alarm_disable(
+			PM8XXX_BATT_ALARM_LOWER_COMPARATOR);
+	if (rc) {
+		pr_err("%s: unable to set batt alarm state\n", __func__);
+		goto free_irq;
+	}
+
+	/*
+	 * The batt-alarm driver requires sane values for both min / max,
+	 * regardless of whether they're both activated.
+	 */
+	rc = pm8xxx_batt_alarm_threshold_set(
+			PM8XXX_BATT_ALARM_LOWER_COMPARATOR, resume_mv);
+	if (!rc)
+		rc = pm8xxx_batt_alarm_threshold_set(
+			PM8XXX_BATT_ALARM_UPPER_COMPARATOR, 4300);
+	if (rc) {
+		pr_err("%s: unable to set batt alarm threshold\n", __func__);
+		goto free_irq;
+	}
+
+	rc = pm8xxx_batt_alarm_hold_time_set(
+				PM8XXX_BATT_ALARM_HOLD_TIME_16_MS);
+	if (rc) {
+		pr_err("%s: unable to set batt alarm hold time\n", __func__);
+		goto free_irq;
+	}
+
+	/* PWM enabled at 2Hz */
+	rc = pm8xxx_batt_alarm_pwm_rate_set(1, 7, 4);
+	if (rc) {
+		pr_err("%s: unable to set batt alarm pwm rate\n", __func__);
+		goto free_irq;
+	}
+
+	rc = pm8xxx_batt_alarm_register_notifier(&alarm_notifier);
+	if (rc) {
+		pr_err("%s: unable to register alarm notifier\n", __func__);
+		goto free_irq;
+	}
+
+	pm8058_chg.inited = 1;
+
+	return 0;
+
+free_irq:
+	free_irqs();
+out:
+	return rc;
+}
+
+static int __devexit pm8058_charger_remove(struct platform_device *pdev)
+{
+	struct pm8058_charger_chip *chip = platform_get_drvdata(pdev);
+	int rc;
+
+	msm_charger_notify_event(&usb_hw_chg, CHG_REMOVED_EVENT);
+	msm_charger_unregister(&usb_hw_chg);
+	cancel_delayed_work_sync(&pm8058_chg.veoc_begin_work);
+	cancel_delayed_work_sync(&pm8058_chg.check_vbat_low_work);
+	cancel_delayed_work_sync(&pm8058_chg.charging_check_work);
+	free_irqs();
+	remove_debugfs_entries();
+	kfree(chip);
+
+	rc = pm8xxx_batt_alarm_disable(PM8XXX_BATT_ALARM_UPPER_COMPARATOR);
+	if (!rc)
+		rc = pm8xxx_batt_alarm_disable(
+			PM8XXX_BATT_ALARM_LOWER_COMPARATOR);
+	if (rc)
+		pr_err("%s: unable to set batt alarm state\n", __func__);
+
+	rc |= pm8xxx_batt_alarm_unregister_notifier(&alarm_notifier);
+	if (rc)
+		pr_err("%s: unable to register alarm notifier\n", __func__);
+	return rc;
+}
+
+static struct platform_driver pm8058_charger_driver = {
+	.probe = pm8058_charger_probe,
+	.remove = __devexit_p(pm8058_charger_remove),
+	.driver = {
+		   .name = "pm8058-charger",
+		   .owner = THIS_MODULE,
+	},
+};
+
+static int __init pm8058_charger_init(void)
+{
+	return platform_driver_register(&pm8058_charger_driver);
+}
+
+static void __exit pm8058_charger_exit(void)
+{
+	platform_driver_unregister(&pm8058_charger_driver);
+}
+
+late_initcall(pm8058_charger_init);
+module_exit(pm8058_charger_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC8058 BATTERY driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pm8058_charger");
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c
index f58d1805..be6ba04 100644
--- a/drivers/power/power_supply_core.c
+++ b/drivers/power/power_supply_core.c
@@ -25,6 +25,61 @@
 
 static struct device_type power_supply_dev_type;
 
+/**
+ * power_supply_set_current_limit - set current limit
+ * @psy:	the power supply to control
+ * @limit:	current limit in uA from the power supply.
+ *		0 will disable the power supply.
+ *
+ * This function will set a maximum supply current from a source
+ * and it will disable the charger when limit is 0.
+ */
+int power_supply_set_current_limit(struct power_supply *psy, int limit)
+{
+	const union power_supply_propval ret = {limit,};
+
+	if (psy->set_property)
+		return psy->set_property(psy, POWER_SUPPLY_PROP_CURRENT_MAX,
+								&ret);
+
+	return -ENXIO;
+}
+EXPORT_SYMBOL_GPL(power_supply_set_current_limit);
+
+/**
+ * power_supply_set_online - set online state of the power supply
+ * @psy:	the power supply to control
+ * @enable:	sets online property of power supply
+ */
+int power_supply_set_online(struct power_supply *psy, bool enable)
+{
+	const union power_supply_propval ret = {enable,};
+
+	if (psy->set_property)
+		return psy->set_property(psy, POWER_SUPPLY_PROP_ONLINE,
+								&ret);
+
+	return -ENXIO;
+}
+EXPORT_SYMBOL_GPL(power_supply_set_online);
+
+/**
+ * power_supply_set_charge_type - set charge type of the power supply
+ * @psy:	the power supply to control
+ * @enable:	sets charge type property of power supply
+ */
+int power_supply_set_charge_type(struct power_supply *psy, int charge_type)
+{
+	const union power_supply_propval ret = {charge_type,};
+
+	if (psy->set_property)
+		return psy->set_property(psy, POWER_SUPPLY_PROP_CHARGE_TYPE,
+								&ret);
+
+	return -ENXIO;
+}
+EXPORT_SYMBOL_GPL(power_supply_set_charge_type);
+
 static int __power_supply_changed_work(struct device *dev, void *data)
 {
 	struct power_supply *psy = (struct power_supply *)data;
diff --git a/drivers/power/qci_battery.c b/drivers/power/qci_battery.c
new file mode 100644
index 0000000..724bcba
--- /dev/null
+++ b/drivers/power/qci_battery.c
@@ -0,0 +1,662 @@
+/* Quanta I2C Battery Driver
+ *
+ * Copyright (C) 2009 Quanta Computer 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.
+ *
+ */
+
+/*
+ *
+ *  The Driver with I/O communications via the I2C Interface for ST15 platform.
+ *  And it is only working on the nuvoTon WPCE775x Embedded Controller.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/sched.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/wpce775x.h>
+#include <linux/delay.h>
+
+#include "qci_battery.h"
+
+#define QCIBAT_DEFAULT_CHARGE_FULL_CAPACITY 2200 /* 2200 mAh */
+#define QCIBAT_DEFAULT_CHARGE_FULL_DESIGN   2200
+#define QCIBAT_DEFAULT_VOLTAGE_DESIGN      10800 /* 10.8 V */
+#define QCIBAT_STRING_SIZE 16
+
+/* General structure to hold the driver data */
+struct i2cbat_drv_data {
+	struct i2c_client *bi2c_client;
+	struct work_struct work;
+	unsigned int qcibat_irq;
+	unsigned int qcibat_gpio;
+	u8 battery_state;
+	u8 battery_dev_name[QCIBAT_STRING_SIZE];
+	u8 serial_number[QCIBAT_STRING_SIZE];
+	u8 manufacturer_name[QCIBAT_STRING_SIZE];
+	unsigned int charge_full;
+	unsigned int charge_full_design;
+	unsigned int voltage_full_design;
+	unsigned int energy_full;
+};
+
+static struct i2cbat_drv_data context;
+static struct mutex qci_i2c_lock;
+static struct mutex qci_transaction_lock;
+/*********************************************************************
+ *		Power
+ *********************************************************************/
+
+static int get_bat_info(u8 ec_data)
+{
+	u8 byte_read;
+
+	mutex_lock(&qci_i2c_lock);
+	i2c_smbus_write_byte(context.bi2c_client, ec_data);
+	byte_read = i2c_smbus_read_byte(context.bi2c_client);
+	mutex_unlock(&qci_i2c_lock);
+	return byte_read;
+}
+
+static int qci_ac_get_prop(struct power_supply *psy,
+			    enum power_supply_property psp,
+			    union power_supply_propval *val)
+{
+	int ret = 0;
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		if (get_bat_info(ECRAM_POWER_SOURCE) & EC_FLAG_ADAPTER_IN)
+			val->intval =  EC_ADAPTER_PRESENT;
+		else
+			val->intval =  EC_ADAPTER_NOT_PRESENT;
+	break;
+	default:
+		ret = -EINVAL;
+	break;
+	}
+	return ret;
+}
+
+static enum power_supply_property qci_ac_props[] = {
+	POWER_SUPPLY_PROP_ONLINE,
+};
+
+static enum power_supply_property qci_bat_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+	POWER_SUPPLY_PROP_VOLTAGE_AVG,
+	POWER_SUPPLY_PROP_CURRENT_AVG,
+	POWER_SUPPLY_PROP_CAPACITY,
+	POWER_SUPPLY_PROP_TEMP,
+	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+	POWER_SUPPLY_PROP_CHARGE_FULL,
+	POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
+	POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
+	POWER_SUPPLY_PROP_MODEL_NAME,
+	POWER_SUPPLY_PROP_MANUFACTURER,
+	POWER_SUPPLY_PROP_SERIAL_NUMBER,
+	POWER_SUPPLY_PROP_CHARGE_COUNTER,
+	POWER_SUPPLY_PROP_ENERGY_NOW,
+	POWER_SUPPLY_PROP_ENERGY_FULL,
+	POWER_SUPPLY_PROP_ENERGY_EMPTY,
+};
+
+static int read_data_from_battery(u8 smb_cmd, u8 smb_prtcl)
+{
+	if (context.battery_state & MAIN_BATTERY_STATUS_BAT_IN)	{
+		mutex_lock(&qci_i2c_lock);
+		i2c_smbus_write_byte_data(context.bi2c_client,
+					  ECRAM_SMB_STS, 0);
+		i2c_smbus_write_byte_data(context.bi2c_client, ECRAM_SMB_ADDR,
+					  BATTERY_SLAVE_ADDRESS);
+		i2c_smbus_write_byte_data(context.bi2c_client,
+					  ECRAM_SMB_CMD, smb_cmd);
+		i2c_smbus_write_byte_data(context.bi2c_client,
+					  ECRAM_SMB_PRTCL, smb_prtcl);
+		mutex_unlock(&qci_i2c_lock);
+		msleep(100);
+		return get_bat_info(ECRAM_SMB_STS);
+	} else
+		return SMBUS_DEVICE_NOACK;
+}
+
+static int qbat_get_status(union power_supply_propval *val)
+{
+	int status;
+
+	status = get_bat_info(ECRAM_BATTERY_STATUS);
+
+	if ((status & MAIN_BATTERY_STATUS_BAT_IN) == 0x0)
+		val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+	else if (status & MAIN_BATTERY_STATUS_BAT_CHARGING)
+		val->intval = POWER_SUPPLY_STATUS_CHARGING;
+	else if (status & MAIN_BATTERY_STATUS_BAT_FULL)
+		val->intval = POWER_SUPPLY_STATUS_FULL;
+	else if (status & MAIN_BATTERY_STATUS_BAT_DISCHRG)
+		val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+	else
+		val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+
+	return 0;
+}
+
+static int qbat_get_present(union power_supply_propval *val)
+{
+	if (context.battery_state & MAIN_BATTERY_STATUS_BAT_IN)
+		val->intval = EC_BAT_PRESENT;
+	else
+		val->intval = EC_BAT_NOT_PRESENT;
+	return 0;
+}
+
+static int qbat_get_health(union power_supply_propval *val)
+{
+	u8 health;
+
+	health = get_bat_info(ECRAM_CHARGER_ALARM);
+	if (!(context.battery_state & MAIN_BATTERY_STATUS_BAT_IN))
+		val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
+	else if (health & ALARM_OVER_TEMP)
+		val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+	else if (health & ALARM_REMAIN_CAPACITY)
+		val->intval = POWER_SUPPLY_HEALTH_DEAD;
+	else if (health & ALARM_OVER_CHARGE)
+		val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+	else
+		val->intval = POWER_SUPPLY_HEALTH_GOOD;
+	return 0;
+}
+
+static int qbat_get_voltage_avg(union power_supply_propval *val)
+{
+	val->intval = (get_bat_info(ECRAM_BATTERY_VOLTAGE_MSB) << 8 |
+		       get_bat_info(ECRAM_BATTERY_VOLTAGE_LSB)) * 1000;
+	return 0;
+}
+
+static int qbat_get_current_avg(union power_supply_propval *val)
+{
+	val->intval = (get_bat_info(ECRAM_BATTERY_CURRENT_MSB) << 8 |
+		       get_bat_info(ECRAM_BATTERY_CURRENT_LSB));
+	return 0;
+}
+
+static int qbat_get_capacity(union power_supply_propval *val)
+{
+	if (!(context.battery_state & MAIN_BATTERY_STATUS_BAT_IN))
+		val->intval = 0xFF;
+	else
+		val->intval = get_bat_info(ECRAM_BATTERY_CAPACITY);
+	return 0;
+}
+
+static int qbat_get_temp_avg(union power_supply_propval *val)
+{
+	int temp;
+	int rc = 0;
+
+	if (!(context.battery_state & MAIN_BATTERY_STATUS_BAT_IN)) {
+		val->intval = 0xFFFF;
+		rc = -ENODATA;
+	} else {
+		temp = (get_bat_info(ECRAM_BATTERY_TEMP_MSB) << 8) |
+			get_bat_info(ECRAM_BATTERY_TEMP_LSB);
+		val->intval = (temp - 2730) / 10;
+	}
+	return rc;
+}
+
+static int qbat_get_charge_full_design(union power_supply_propval *val)
+{
+	val->intval = context.charge_full_design;
+	return 0;
+}
+
+static int qbat_get_charge_full(union power_supply_propval *val)
+{
+	val->intval = context.charge_full;
+	return 0;
+}
+
+static int qbat_get_charge_counter(union power_supply_propval *val)
+{
+	u16 charge = 0;
+	int rc = 0;
+
+	mutex_lock(&qci_transaction_lock);
+	if (read_data_from_battery(BATTERY_CYCLE_COUNT,
+				   SMBUS_READ_WORD_PRTCL) == SMBUS_DONE) {
+		charge = get_bat_info(ECRAM_SMB_DATA1);
+		charge = charge << 8;
+		charge |= get_bat_info(ECRAM_SMB_DATA0);
+	} else
+		rc = -ENODATA;
+	mutex_unlock(&qci_transaction_lock);
+	val->intval = charge;
+	return rc;
+}
+
+static int qbat_get_time_empty_avg(union power_supply_propval *val)
+{
+	u16 avg = 0;
+	int rc = 0;
+
+	mutex_lock(&qci_transaction_lock);
+	if (read_data_from_battery(BATTERY_AVERAGE_TIME_TO_EMPTY,
+				   SMBUS_READ_WORD_PRTCL) == SMBUS_DONE) {
+		avg = get_bat_info(ECRAM_SMB_DATA1);
+		avg = avg << 8;
+		avg |= get_bat_info(ECRAM_SMB_DATA0);
+	} else
+		rc = -ENODATA;
+	mutex_unlock(&qci_transaction_lock);
+	val->intval = avg;
+	return rc;
+}
+
+static int qbat_get_time_full_avg(union power_supply_propval *val)
+{
+	u16 avg = 0;
+	int rc = 0;
+
+	mutex_lock(&qci_transaction_lock);
+	if (read_data_from_battery(BATTERY_AVERAGE_TIME_TO_FULL,
+				   SMBUS_READ_WORD_PRTCL) == SMBUS_DONE) {
+		avg = get_bat_info(ECRAM_SMB_DATA1);
+		avg = avg << 8;
+		avg |= get_bat_info(ECRAM_SMB_DATA0);
+	} else
+		rc = -ENODATA;
+	mutex_unlock(&qci_transaction_lock);
+	val->intval = avg;
+	return rc;
+}
+
+static int qbat_get_model_name(union power_supply_propval *val)
+{
+	unsigned char i, size;
+
+	mutex_lock(&qci_transaction_lock);
+	if (read_data_from_battery(BATTERY_DEVICE_NAME,
+				   SMBUS_READ_BLOCK_PRTCL) == SMBUS_DONE) {
+		size = min(get_bat_info(ECRAM_SMB_BCNT), QCIBAT_STRING_SIZE);
+		for (i = 0; i < size; i++) {
+			context.battery_dev_name[i] =
+				get_bat_info(ECRAM_SMB_DATA_START + i);
+		}
+		val->strval = context.battery_dev_name;
+	} else
+		val->strval = "Unknown";
+	mutex_unlock(&qci_transaction_lock);
+	return 0;
+}
+
+static int qbat_get_manufacturer_name(union power_supply_propval *val)
+{
+	val->strval = context.manufacturer_name;
+	return 0;
+}
+
+static int qbat_get_serial_number(union power_supply_propval *val)
+{
+	val->strval = context.serial_number;
+	return 0;
+}
+
+static int qbat_get_technology(union power_supply_propval *val)
+{
+	val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
+	return 0;
+}
+
+static int qbat_get_energy_now(union power_supply_propval *val)
+{
+	if (!(get_bat_info(ECRAM_BATTERY_STATUS) & MAIN_BATTERY_STATUS_BAT_IN))
+		val->intval = 0;
+	else
+		val->intval = (get_bat_info(ECRAM_BATTERY_CAPACITY) *
+			       context.energy_full) / 100;
+	return 0;
+}
+
+static int qbat_get_energy_full(union power_supply_propval *val)
+{
+	val->intval = context.energy_full;
+	return 0;
+}
+
+static int qbat_get_energy_empty(union power_supply_propval *val)
+{
+	val->intval = 0;
+	return 0;
+}
+
+static void qbat_init_get_charge_full(void)
+{
+	u16 charge = QCIBAT_DEFAULT_CHARGE_FULL_CAPACITY;
+
+	mutex_lock(&qci_transaction_lock);
+	if (read_data_from_battery(BATTERY_FULL_CAPACITY,
+				   SMBUS_READ_WORD_PRTCL) == SMBUS_DONE) {
+		charge = get_bat_info(ECRAM_SMB_DATA1);
+		charge = charge << 8;
+		charge |= get_bat_info(ECRAM_SMB_DATA0);
+	}
+	mutex_unlock(&qci_transaction_lock);
+	context.charge_full = charge;
+}
+
+static void qbat_init_get_charge_full_design(void)
+{
+	u16 charge = QCIBAT_DEFAULT_CHARGE_FULL_DESIGN;
+
+	mutex_lock(&qci_transaction_lock);
+	if (read_data_from_battery(BATTERY_DESIGN_CAPACITY,
+				   SMBUS_READ_WORD_PRTCL) == SMBUS_DONE) {
+		charge = get_bat_info(ECRAM_SMB_DATA1);
+		charge = charge << 8;
+		charge |= get_bat_info(ECRAM_SMB_DATA0);
+	}
+	mutex_unlock(&qci_transaction_lock);
+	context.charge_full_design = charge;
+}
+
+static void qbat_init_get_voltage_full_design(void)
+{
+	u16 voltage = QCIBAT_DEFAULT_VOLTAGE_DESIGN;
+
+	mutex_lock(&qci_transaction_lock);
+	if (read_data_from_battery(BATTERY_DESIGN_VOLTAGE,
+				   SMBUS_READ_WORD_PRTCL) == SMBUS_DONE) {
+		voltage = get_bat_info(ECRAM_SMB_DATA1);
+		voltage = voltage << 8;
+		voltage |= get_bat_info(ECRAM_SMB_DATA0);
+	}
+	mutex_unlock(&qci_transaction_lock);
+	context.voltage_full_design = voltage;
+}
+
+static void qbat_init_get_manufacturer_name(void)
+{
+	u8 size;
+	u8 i;
+	int rc;
+
+	mutex_lock(&qci_transaction_lock);
+	rc = read_data_from_battery(BATTERY_MANUFACTURE_NAME,
+				    SMBUS_READ_BLOCK_PRTCL);
+	if (rc == SMBUS_DONE) {
+		size = min(get_bat_info(ECRAM_SMB_BCNT), QCIBAT_STRING_SIZE);
+		for (i = 0; i < size; i++) {
+			context.manufacturer_name[i] =
+				get_bat_info(ECRAM_SMB_DATA_START + i);
+		}
+	} else
+		strcpy(context.manufacturer_name, "Unknown");
+	mutex_unlock(&qci_transaction_lock);
+}
+
+static void qbat_init_get_serial_number(void)
+{
+	u8 size;
+	u8 i;
+	int rc;
+
+	mutex_lock(&qci_transaction_lock);
+	rc = read_data_from_battery(BATTERY_SERIAL_NUMBER,
+				    SMBUS_READ_BLOCK_PRTCL);
+	if (rc == SMBUS_DONE) {
+		size = min(get_bat_info(ECRAM_SMB_BCNT), QCIBAT_STRING_SIZE);
+		for (i = 0; i < size; i++) {
+			context.serial_number[i] =
+				get_bat_info(ECRAM_SMB_DATA_START + i);
+		}
+	} else
+		strcpy(context.serial_number, "Unknown");
+	mutex_unlock(&qci_transaction_lock);
+}
+
+static void init_battery_stats(void)
+{
+	int i;
+
+	context.battery_state = get_bat_info(ECRAM_BATTERY_STATUS);
+	if (!(context.battery_state & MAIN_BATTERY_STATUS_BAT_IN))
+		return;
+	/* EC bug? needs some initial priming */
+	for (i = 0; i < 5; i++) {
+		read_data_from_battery(BATTERY_DESIGN_CAPACITY,
+				       SMBUS_READ_WORD_PRTCL);
+	}
+
+	qbat_init_get_charge_full_design();
+	qbat_init_get_charge_full();
+	qbat_init_get_voltage_full_design();
+
+	context.energy_full = context.voltage_full_design *
+		context.charge_full;
+
+	qbat_init_get_serial_number();
+	qbat_init_get_manufacturer_name();
+}
+
+/*********************************************************************
+ *		Battery properties
+ *********************************************************************/
+static int qbat_get_property(struct power_supply *psy,
+			     enum power_supply_property psp,
+			     union power_supply_propval *val)
+{
+	int ret = 0;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		ret = qbat_get_status(val);
+		break;
+	case POWER_SUPPLY_PROP_PRESENT:
+		ret = qbat_get_present(val);
+		break;
+	case POWER_SUPPLY_PROP_HEALTH:
+		ret = qbat_get_health(val);
+		break;
+	case POWER_SUPPLY_PROP_MANUFACTURER:
+		ret = qbat_get_manufacturer_name(val);
+		break;
+	case POWER_SUPPLY_PROP_TECHNOLOGY:
+		ret = qbat_get_technology(val);
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+		ret = qbat_get_voltage_avg(val);
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_AVG:
+		ret = qbat_get_current_avg(val);
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY:
+		ret = qbat_get_capacity(val);
+		break;
+	case POWER_SUPPLY_PROP_TEMP:
+		ret = qbat_get_temp_avg(val);
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+		ret = qbat_get_charge_full_design(val);
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_FULL:
+		ret = qbat_get_charge_full(val);
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_COUNTER:
+		ret = qbat_get_charge_counter(val);
+		break;
+	case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
+		ret = qbat_get_time_empty_avg(val);
+		break;
+	case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
+		ret = qbat_get_time_full_avg(val);
+		break;
+	case POWER_SUPPLY_PROP_MODEL_NAME:
+		ret = qbat_get_model_name(val);
+		break;
+	case POWER_SUPPLY_PROP_SERIAL_NUMBER:
+		ret = qbat_get_serial_number(val);
+		break;
+	case POWER_SUPPLY_PROP_ENERGY_NOW:
+		ret = qbat_get_energy_now(val);
+		break;
+	case POWER_SUPPLY_PROP_ENERGY_FULL:
+		ret = qbat_get_energy_full(val);
+		break;
+	case POWER_SUPPLY_PROP_ENERGY_EMPTY:
+		ret = qbat_get_energy_empty(val);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+/*********************************************************************
+ *		Initialisation
+ *********************************************************************/
+
+static struct power_supply qci_ac = {
+	.name = "ac",
+	.type = POWER_SUPPLY_TYPE_MAINS,
+	.properties = qci_ac_props,
+	.num_properties = ARRAY_SIZE(qci_ac_props),
+	.get_property = qci_ac_get_prop,
+};
+
+static struct power_supply qci_bat = {
+	.name = "battery",
+	.type = POWER_SUPPLY_TYPE_BATTERY,
+	.properties = qci_bat_props,
+	.num_properties = ARRAY_SIZE(qci_bat_props),
+	.get_property = qbat_get_property,
+	.use_for_apm = 1,
+};
+
+static irqreturn_t qbat_interrupt(int irq, void *dev_id)
+{
+	struct i2cbat_drv_data *ibat_drv_data = dev_id;
+	schedule_work(&ibat_drv_data->work);
+	return IRQ_HANDLED;
+}
+
+static void qbat_work(struct work_struct *_work)
+{
+	u8 status;
+
+	status = get_bat_info(ECRAM_BATTERY_EVENTS);
+	if (status & EC_EVENT_AC) {
+		context.battery_state = get_bat_info(ECRAM_BATTERY_STATUS);
+		power_supply_changed(&qci_ac);
+	}
+
+	if (status & (EC_EVENT_BATTERY | EC_EVENT_CHARGER | EC_EVENT_TIMER)) {
+		context.battery_state = get_bat_info(ECRAM_BATTERY_STATUS);
+		power_supply_changed(&qci_bat);
+		if (status & EC_EVENT_BATTERY)
+			init_battery_stats();
+	}
+}
+
+static struct platform_device *bat_pdev;
+
+static int __init qbat_init(void)
+{
+	int err = 0;
+
+	mutex_init(&qci_i2c_lock);
+	mutex_init(&qci_transaction_lock);
+
+	context.bi2c_client = wpce_get_i2c_client();
+	if (context.bi2c_client == NULL)
+		return -1;
+
+	i2c_set_clientdata(context.bi2c_client, &context);
+	context.qcibat_gpio = context.bi2c_client->irq;
+
+	/*battery device register*/
+	bat_pdev = platform_device_register_simple("battery", 0, NULL, 0);
+	if (IS_ERR(bat_pdev))
+		return PTR_ERR(bat_pdev);
+
+	err = power_supply_register(&bat_pdev->dev, &qci_ac);
+	if (err)
+		goto ac_failed;
+
+	qci_bat.name = bat_pdev->name;
+	err = power_supply_register(&bat_pdev->dev, &qci_bat);
+	if (err)
+		goto battery_failed;
+
+	/*battery irq configure*/
+	INIT_WORK(&context.work, qbat_work);
+	err = gpio_request(context.qcibat_gpio, "qci-bat");
+	if (err) {
+		dev_err(&context.bi2c_client->dev,
+			"[BAT] err gpio request\n");
+		goto gpio_request_fail;
+	}
+	context.qcibat_irq = gpio_to_irq(context.qcibat_gpio);
+	err = request_irq(context.qcibat_irq, qbat_interrupt,
+		IRQF_TRIGGER_FALLING, BATTERY_ID_NAME, &context);
+	if (err) {
+		dev_err(&context.bi2c_client->dev,
+			"[BAT] unable to get IRQ\n");
+		goto request_irq_fail;
+	}
+
+	init_battery_stats();
+	goto success;
+
+request_irq_fail:
+	gpio_free(context.qcibat_gpio);
+
+gpio_request_fail:
+	power_supply_unregister(&qci_bat);
+
+battery_failed:
+	power_supply_unregister(&qci_ac);
+
+ac_failed:
+	platform_device_unregister(bat_pdev);
+
+	i2c_set_clientdata(context.bi2c_client, NULL);
+success:
+	return err;
+}
+
+static void __exit qbat_exit(void)
+{
+	free_irq(context.qcibat_irq, &context);
+	gpio_free(context.qcibat_gpio);
+	power_supply_unregister(&qci_bat);
+	power_supply_unregister(&qci_ac);
+	platform_device_unregister(bat_pdev);
+	i2c_set_clientdata(context.bi2c_client, NULL);
+}
+
+late_initcall(qbat_init);
+module_exit(qbat_exit);
+
+MODULE_AUTHOR("Quanta Computer Inc.");
+MODULE_DESCRIPTION("Quanta Embedded Controller I2C Battery Driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/power/qci_battery.h b/drivers/power/qci_battery.h
new file mode 100644
index 0000000..dcbb62b
--- /dev/null
+++ b/drivers/power/qci_battery.h
@@ -0,0 +1,121 @@
+/* Header file for Quanta I2C Battery Driver
+ *
+ * Copyright (C) 2009 Quanta Computer 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.
+ *
+ */
+
+ /*
+ *
+ *  The Driver with I/O communications via the I2C Interface for ON2 of AP BU.
+ *  And it is only working on the nuvoTon WPCE775x Embedded Controller.
+ *
+ */
+
+#ifndef __QCI_BATTERY_H__
+#define __QCI_BATTERY_H__
+
+#define BAT_I2C_ADDRESS 0x1A
+#define BATTERY_ID_NAME          "qci-i2cbat"
+#define EC_FLAG_ADAPTER_IN		0x01
+#define EC_FLAG_POWER_ON		0x02
+#define EC_FLAG_ENTER_S3		0x04
+#define EC_FLAG_ENTER_S4		0x08
+#define EC_FLAG_IN_STANDBY		0x10
+#define EC_FLAG_SYSTEM_ON		0x20
+#define EC_FLAG_WAIT_HWPG		0x40
+#define EC_FLAG_S5_POWER_ON	0x80
+
+#define MAIN_BATTERY_STATUS_BAT_DISCHRG		0x01
+#define MAIN_BATTERY_STATUS_BAT_CHARGING	0x02
+#define MAIN_BATTERY_STATUS_BAT_ABNORMAL	0x04
+#define MAIN_BATTERY_STATUS_BAT_IN		0x08
+#define MAIN_BATTERY_STATUS_BAT_FULL		0x10
+#define MAIN_BATTERY_STATUS_BAT_LOW		0x20
+#define MAIN_BATTERY_STATUS_BAT_SMB_VALID	0x80
+
+#define CHG_STATUS_BAT_CHARGE			0x01
+#define CHG_STATUS_BAT_PRECHG			0x02
+#define CHG_STATUS_BAT_OVERTEMP			0x04
+#define CHG_STATUS_BAT_TYPE			0x08
+#define CHG_STATUS_BAT_GWROK			0x10
+#define CHG_STATUS_BAT_INCHARGE			0x20
+#define CHG_STATUS_BAT_WAKECHRG			0x40
+#define CHG_STATUS_BAT_CHGTIMEOUT		0x80
+
+#define EC_ADAPTER_PRESENT		0x1
+#define EC_BAT_PRESENT		        0x1
+#define EC_ADAPTER_NOT_PRESENT		0x0
+#define EC_BAT_NOT_PRESENT		0x0
+
+#define ECRAM_POWER_SOURCE              0x40
+#define ECRAM_CHARGER_ALARM		0x42
+#define ECRAM_BATTERY_STATUS            0x82
+#define ECRAM_BATTERY_CURRENT_LSB       0x83
+#define ECRAM_BATTERY_CURRENT_MSB       0x84
+#define ECRAM_BATTERY_VOLTAGE_LSB       0x87
+#define ECRAM_BATTERY_VOLTAGE_MSB       0x88
+#define ECRAM_BATTERY_CAPACITY          0x89
+#define ECRAM_BATTERY_TEMP_LSB          0x8C
+#define ECRAM_BATTERY_TEMP_MSB          0x8D
+#define ECRAM_BATTERY_EVENTS            0x99
+
+#define EC_EVENT_BATTERY                0x01
+#define EC_EVENT_CHARGER                0x02
+#define EC_EVENT_AC                     0x10
+#define EC_EVENT_TIMER                  0x40
+
+/* smbus access */
+#define SMBUS_READ_BYTE_PRTCL		0x07
+#define SMBUS_READ_WORD_PRTCL		0x09
+#define SMBUS_READ_BLOCK_PRTCL		0x0B
+
+/* smbus status code */
+#define SMBUS_OK			0x00
+#define SMBUS_DONE			0x80
+#define SMBUS_ALARM			0x40
+#define SMBUS_UNKNOW_FAILURE		0x07
+#define SMBUS_DEVICE_NOACK		0x10
+#define SMBUS_DEVICE_ERROR		0x11
+#define SMBUS_UNKNOW_ERROR		0x13
+#define SMBUS_TIME_OUT			0x18
+#define SMBUS_BUSY			0x1A
+
+/* ec ram mapping */
+#define ECRAM_SMB_PRTCL			0
+#define ECRAM_SMB_STS			1
+#define ECRAM_SMB_ADDR			2
+#define ECRAM_SMB_CMD			3
+#define ECRAM_SMB_DATA_START		4
+#define ECRAM_SMB_DATA0			4
+#define ECRAM_SMB_DATA1			5
+#define ECRAM_SMB_BCNT			36
+#define ECRAM_SMB_ALARM_ADDR		37
+#define ECRAM_SMB_ALARM_DATA0		38
+#define ECRAM_SMB_ALARM_DATA1		39
+
+/* smart battery commands */
+#define BATTERY_SLAVE_ADDRESS		0x16
+#define BATTERY_FULL_CAPACITY		0x10
+#define BATTERY_AVERAGE_TIME_TO_EMPTY	0x12
+#define BATTERY_AVERAGE_TIME_TO_FULL	0x13
+#define BATTERY_CYCLE_COUNT		0x17
+#define BATTERY_DESIGN_CAPACITY		0x18
+#define BATTERY_DESIGN_VOLTAGE		0x19
+#define BATTERY_SERIAL_NUMBER		0x1C
+#define BATTERY_MANUFACTURE_NAME        0x20
+#define BATTERY_DEVICE_NAME		0x21
+
+/* alarm bit */
+#define ALARM_REMAIN_CAPACITY           0x02
+#define ALARM_OVER_TEMP                 0x10
+#define ALARM_OVER_CHARGE               0x80
+#endif
diff --git a/drivers/power/smb137b.c b/drivers/power/smb137b.c
new file mode 100644
index 0000000..7ff8e28
--- /dev/null
+++ b/drivers/power/smb137b.c
@@ -0,0 +1,857 @@
+/* Copyright (c) 2010-2011 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/i2c/smb137b.h>
+#include <linux/power_supply.h>
+#include <linux/msm-charger.h>
+
+#define SMB137B_MASK(BITS, POS)  ((unsigned char)(((1 << BITS) - 1) << POS))
+
+#define CHG_CURRENT_REG		0x00
+#define FAST_CHG_CURRENT_MASK		SMB137B_MASK(3, 5)
+#define PRE_CHG_CURRENT_MASK		SMB137B_MASK(2, 3)
+#define TERM_CHG_CURRENT_MASK		SMB137B_MASK(2, 1)
+
+#define INPUT_CURRENT_LIMIT_REG	0x01
+#define IN_CURRENT_MASK			SMB137B_MASK(3, 5)
+#define IN_CURRENT_LIMIT_EN_BIT		BIT(2)
+#define IN_CURRENT_DET_THRESH_MASK	SMB137B_MASK(2, 0)
+
+#define FLOAT_VOLTAGE_REG	0x02
+#define STAT_OUT_POLARITY_BIT		BIT(7)
+#define FLOAT_VOLTAGE_MASK		SMB137B_MASK(7, 0)
+
+#define CONTROL_A_REG		0x03
+#define AUTO_RECHARGE_DIS_BIT		BIT(7)
+#define CURR_CYCLE_TERM_BIT		BIT(6)
+#define PRE_TO_FAST_V_MASK		SMB137B_MASK(3, 3)
+#define TEMP_BEHAV_BIT			BIT(2)
+#define THERM_NTC_CURR_MODE_BIT		BIT(1)
+#define THERM_NTC_47KOHM_BIT		BIT(0)
+
+#define CONTROL_B_REG		0x04
+#define STAT_OUTPUT_MODE_MASK		SMB137B_MASK(2, 6)
+#define BATT_OV_ENDS_CYCLE_BIT		BIT(5)
+#define AUTO_PRE_TO_FAST_DIS_BIT	BIT(4)
+#define SAFETY_TIMER_EN_BIT		BIT(3)
+#define OTG_LBR_WD_EN_BIT		BIT(2)
+#define CHG_WD_TIMER_EN_BIT		BIT(1)
+#define IRQ_OP_MASK			BIT(0)
+
+#define PIN_CTRL_REG		0x05
+#define AUTO_CHG_EN_BIT			BIT(7)
+#define AUTO_LBR_EN_BIT			BIT(6)
+#define OTG_LBR_BIT			BIT(5)
+#define I2C_PIN_BIT			BIT(4)
+#define PIN_EN_CTRL_MASK		SMB137B_MASK(2, 2)
+#define OTG_LBR_PIN_CTRL_MASK		SMB137B_MASK(2, 0)
+
+#define OTG_LBR_CTRL_REG	0x06
+#define BATT_MISSING_DET_EN_BIT		BIT(7)
+#define AUTO_RECHARGE_THRESH_MASK	BIT(6)
+#define USB_DP_DN_DET_EN_MASK		BIT(5)
+#define OTG_LBR_BATT_CURRENT_LIMIT_MASK	SMB137B_MASK(2, 3)
+#define OTG_LBR_UVLO_THRESH_MASK	SMB137B_MASK(3, 0)
+
+#define FAULT_INTR_REG		0x07
+#define SAFETY_TIMER_EXP_MASK		SMB137B_MASK(1, 7)
+#define BATT_TEMP_UNSAFE_MASK		SMB137B_MASK(1, 6)
+#define INPUT_OVLO_IVLO_MASK		SMB137B_MASK(1, 5)
+#define BATT_OVLO_MASK			SMB137B_MASK(1, 4)
+#define INTERNAL_OVER_TEMP_MASK		SMB137B_MASK(1, 2)
+#define ENTER_TAPER_CHG_MASK		SMB137B_MASK(1, 1)
+#define CHG_MASK			SMB137B_MASK(1, 0)
+
+#define CELL_TEMP_MON_REG	0x08
+#define THERMISTOR_CURR_MASK		SMB137B_MASK(2, 6)
+#define LOW_TEMP_CHG_INHIBIT_MASK	SMB137B_MASK(3, 3)
+#define HIGH_TEMP_CHG_INHIBIT_MASK	SMB137B_MASK(3, 0)
+
+#define	SAFETY_TIMER_THERMAL_SHUTDOWN_REG	0x09
+#define DCIN_OVLO_SEL_MASK		SMB137B_MASK(2, 7)
+#define RELOAD_EN_INPUT_VOLTAGE_MASK	SMB137B_MASK(1, 6)
+#define THERM_SHUTDN_EN_MASK		SMB137B_MASK(1, 5)
+#define STANDBY_WD_TIMER_EN_MASK		SMB137B_MASK(1, 4)
+#define COMPLETE_CHG_TMOUT_MASK		SMB137B_MASK(2, 2)
+#define PRE_CHG_TMOUT_MASK		SMB137B_MASK(2, 0)
+
+#define	VSYS_REG	0x0A
+#define	VSYS_MASK			SMB137B_MASK(3, 4)
+
+#define IRQ_RESET_REG	0x30
+
+#define COMMAND_A_REG	0x31
+#define	VOLATILE_REGS_WRITE_PERM_BIT	BIT(7)
+#define	POR_BIT				BIT(6)
+#define	FAST_CHG_SETTINGS_BIT		BIT(5)
+#define	BATT_CHG_EN_BIT			BIT(4)
+#define	USBIN_MODE_500_BIT		BIT(3)
+#define	USBIN_MODE_HCMODE_BIT		BIT(2)
+#define	OTG_LBR_EN_BIT			BIT(1)
+#define	STAT_OE_BIT			BIT(0)
+
+#define STATUS_A_REG	0x32
+#define INTERNAL_TEMP_IRQ_STAT		BIT(4)
+#define DCIN_OV_IRQ_STAT		BIT(3)
+#define DCIN_UV_IRQ_STAT		BIT(2)
+#define USBIN_OV_IRQ_STAT		BIT(1)
+#define USBIN_UV_IRQ_STAT		BIT(0)
+
+#define STATUS_B_REG	0x33
+#define USB_PIN_STAT			BIT(7)
+#define USB51_MODE_STAT			BIT(6)
+#define USB51_HC_MODE_STAT		BIT(5)
+#define INTERNAL_TEMP_LIMIT_B_STAT	BIT(4)
+#define DC_IN_OV_STAT			BIT(3)
+#define DC_IN_UV_STAT			BIT(2)
+#define USB_IN_OV_STAT			BIT(1)
+#define USB_IN_UV_STAT			BIT(0)
+
+#define	STATUS_C_REG	0x34
+#define AUTO_IN_CURR_LIMIT_MASK		SMB137B_MASK(4, 4)
+#define AUTO_IN_CURR_LIMIT_STAT		BIT(3)
+#define AUTO_SOURCE_DET_COMP_STAT_MASK	SMB137B_MASK(2, 1)
+#define AUTO_SOURCE_DET_RESULT_STAT	BIT(0)
+
+#define	STATUS_D_REG	0x35
+#define	VBATT_LESS_THAN_VSYS_STAT	BIT(7)
+#define	USB_FAIL_STAT			BIT(6)
+#define	BATT_TEMP_STAT_MASK		SMB137B_MASK(2, 4)
+#define	INTERNAL_TEMP_LIMIT_STAT	BIT(2)
+#define	OTG_LBR_MODE_EN_STAT		BIT(1)
+#define	OTG_LBR_VBATT_UVLO_STAT		BIT(0)
+
+#define	STATUS_E_REG	0x36
+#define	CHARGE_CYCLE_COUNT_STAT		BIT(7)
+#define	CHARGER_TERM_STAT		BIT(6)
+#define	SAFETY_TIMER_STAT_MASK		SMB137B_MASK(2, 4)
+#define	CHARGER_ERROR_STAT		BIT(3)
+#define	CHARGING_STAT_E			SMB137B_MASK(2, 1)
+#define	CHARGING_EN			BIT(0)
+
+#define	STATUS_F_REG	0x37
+#define	WD_IRQ_ACTIVE_STAT		BIT(7)
+#define	OTG_OVERCURRENT_STAT		BIT(6)
+#define	BATT_PRESENT_STAT		BIT(4)
+#define	BATT_OV_LATCHED_STAT		BIT(3)
+#define	CHARGER_OVLO_STAT		BIT(2)
+#define	CHARGER_UVLO_STAT		BIT(1)
+#define	BATT_LOW_STAT			BIT(0)
+
+#define	STATUS_G_REG	0x38
+#define	CHARGE_TIMEOUT_IRQ_STAT		BIT(7)
+#define	PRECHARGE_TIMEOUT_IRQ_STAT	BIT(6)
+#define	BATT_HOT_IRQ_STAT		BIT(5)
+#define	BATT_COLD_IRQ_STAT		BIT(4)
+#define	BATT_OV_IRQ_STAT		BIT(3)
+#define	TAPER_CHG_IRQ_STAT		BIT(2)
+#define	FAST_CHG_IRQ_STAT		BIT(1)
+#define	CHARGING_IRQ_STAT		BIT(0)
+
+#define	STATUS_H_REG	0x39
+#define	CHARGE_TIMEOUT_STAT		BIT(7)
+#define	PRECHARGE_TIMEOUT_STAT		BIT(6)
+#define	BATT_HOT_STAT			BIT(5)
+#define	BATT_COLD_STAT			BIT(4)
+#define	BATT_OV_STAT			BIT(3)
+#define	TAPER_CHG_STAT			BIT(2)
+#define	FAST_CHG_STAT			BIT(1)
+#define	CHARGING_STAT_H			BIT(0)
+
+#define DEV_ID_REG	0x3B
+
+#define COMMAND_B_REG	0x3C
+#define	THERM_NTC_CURR_VERRIDE		BIT(7)
+
+#define SMB137B_CHG_PERIOD	((HZ) * 150)
+
+#define INPUT_CURRENT_REG_DEFAULT	0xE1
+#define INPUT_CURRENT_REG_MIN		0x01
+#define	COMMAND_A_REG_DEFAULT		0xA0
+#define	COMMAND_A_REG_OTG_MODE		0xA2
+
+#define	PIN_CTRL_REG_DEFAULT		0x00
+#define	PIN_CTRL_REG_CHG_OFF		0x04
+
+#define	FAST_CHG_E_STATUS 0x2
+
+#define SMB137B_DEFAULT_BATT_RATING   950
+struct smb137b_data {
+	struct i2c_client *client;
+	struct delayed_work charge_work;
+
+	bool charging;
+	int chgcurrent;
+	int cur_charging_mode;
+	int max_system_voltage;
+	int min_system_voltage;
+
+	int valid_n_gpio;
+
+	int batt_status;
+	int batt_chg_type;
+	int batt_present;
+	int min_design;
+	int max_design;
+	int batt_mah_rating;
+
+	int usb_status;
+
+	u8 dev_id_reg;
+	struct msm_hardware_charger adapter_hw_chg;
+};
+
+static unsigned int disabled;
+static DEFINE_MUTEX(init_lock);
+static unsigned int init_otg_power;
+
+enum charger_stat {
+	SMB137B_ABSENT,
+	SMB137B_PRESENT,
+	SMB137B_ENUMERATED,
+};
+
+static struct smb137b_data *usb_smb137b_chg;
+
+static int smb137b_read_reg(struct i2c_client *client, int reg,
+	u8 *val)
+{
+	s32 ret;
+	struct smb137b_data *smb137b_chg;
+
+	smb137b_chg = i2c_get_clientdata(client);
+	ret = i2c_smbus_read_byte_data(smb137b_chg->client, reg);
+	if (ret < 0) {
+		dev_err(&smb137b_chg->client->dev,
+			"i2c read fail: can't read from %02x: %d\n", reg, ret);
+		return ret;
+	} else
+		*val = ret;
+
+	return 0;
+}
+
+static int smb137b_write_reg(struct i2c_client *client, int reg,
+	u8 val)
+{
+	s32 ret;
+	struct smb137b_data *smb137b_chg;
+
+	smb137b_chg = i2c_get_clientdata(client);
+	ret = i2c_smbus_write_byte_data(smb137b_chg->client, reg, val);
+	if (ret < 0) {
+		dev_err(&smb137b_chg->client->dev,
+			"i2c write fail: can't write %02x to %02x: %d\n",
+			val, reg, ret);
+		return ret;
+	}
+	return 0;
+}
+
+static ssize_t id_reg_show(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	struct smb137b_data *smb137b_chg;
+
+	smb137b_chg = i2c_get_clientdata(to_i2c_client(dev));
+
+	return sprintf(buf, "%02x\n", smb137b_chg->dev_id_reg);
+}
+static DEVICE_ATTR(id_reg, S_IRUGO | S_IWUSR, id_reg_show, NULL);
+
+#ifdef DEBUG
+static void smb137b_dbg_print_status_regs(struct smb137b_data *smb137b_chg)
+{
+	int ret;
+	u8 temp;
+
+	ret = smb137b_read_reg(smb137b_chg->client, STATUS_A_REG, &temp);
+	dev_dbg(&smb137b_chg->client->dev, "%s A=0x%x\n", __func__, temp);
+	ret = smb137b_read_reg(smb137b_chg->client, STATUS_B_REG, &temp);
+	dev_dbg(&smb137b_chg->client->dev, "%s B=0x%x\n", __func__, temp);
+	ret = smb137b_read_reg(smb137b_chg->client, STATUS_C_REG, &temp);
+	dev_dbg(&smb137b_chg->client->dev, "%s C=0x%x\n", __func__, temp);
+	ret = smb137b_read_reg(smb137b_chg->client, STATUS_D_REG, &temp);
+	dev_dbg(&smb137b_chg->client->dev, "%s D=0x%x\n", __func__, temp);
+	ret = smb137b_read_reg(smb137b_chg->client, STATUS_E_REG, &temp);
+	dev_dbg(&smb137b_chg->client->dev, "%s E=0x%x\n", __func__, temp);
+	ret = smb137b_read_reg(smb137b_chg->client, STATUS_F_REG, &temp);
+	dev_dbg(&smb137b_chg->client->dev, "%s F=0x%x\n", __func__, temp);
+	ret = smb137b_read_reg(smb137b_chg->client, STATUS_G_REG, &temp);
+	dev_dbg(&smb137b_chg->client->dev, "%s G=0x%x\n", __func__, temp);
+	ret = smb137b_read_reg(smb137b_chg->client, STATUS_H_REG, &temp);
+	dev_dbg(&smb137b_chg->client->dev, "%s H=0x%x\n", __func__, temp);
+}
+#else
+static void smb137b_dbg_print_status_regs(struct smb137b_data *smb137b_chg)
+{
+}
+#endif
+
+static int smb137b_start_charging(struct msm_hardware_charger *hw_chg,
+		int chg_voltage, int chg_current)
+{
+	int cmd_val = COMMAND_A_REG_DEFAULT;
+	u8 temp = 0;
+	int ret = 0;
+	struct smb137b_data *smb137b_chg;
+
+	smb137b_chg = container_of(hw_chg, struct smb137b_data, adapter_hw_chg);
+	if (disabled) {
+		dev_err(&smb137b_chg->client->dev,
+			"%s called when disabled\n", __func__);
+		goto out;
+	}
+
+	if (smb137b_chg->charging == true
+		&& smb137b_chg->chgcurrent == chg_current)
+		/* we are already charging with the same current*/
+		 dev_err(&smb137b_chg->client->dev,
+			 "%s charge with same current %d called again\n",
+			  __func__, chg_current);
+
+	dev_dbg(&smb137b_chg->client->dev, "%s\n", __func__);
+	if (chg_current < 500)
+		cmd_val &= ~USBIN_MODE_500_BIT;
+	else if (chg_current == 500)
+		cmd_val |= USBIN_MODE_500_BIT;
+	else
+		cmd_val |= USBIN_MODE_HCMODE_BIT;
+
+	smb137b_chg->chgcurrent = chg_current;
+	smb137b_chg->cur_charging_mode = cmd_val;
+
+	/* Due to non-volatile reload feature,always enable volatile
+	 * mirror writes before modifying any 00h~09h control register.
+	 * Current mode needs to be programmed according to what's detected
+	 * Otherwise default 100mA mode might cause VOUTL drop and fail
+	 * the system in case of dead battery.
+	 */
+	ret = smb137b_write_reg(smb137b_chg->client,
+					COMMAND_A_REG, cmd_val);
+	if (ret) {
+		dev_err(&smb137b_chg->client->dev,
+			"%s couldn't write to command_reg\n", __func__);
+		goto out;
+	}
+	ret = smb137b_write_reg(smb137b_chg->client,
+					PIN_CTRL_REG, PIN_CTRL_REG_DEFAULT);
+	if (ret) {
+		dev_err(&smb137b_chg->client->dev,
+			"%s couldn't write to pin ctrl reg\n", __func__);
+		goto out;
+	}
+	smb137b_chg->charging = true;
+	smb137b_chg->batt_status = POWER_SUPPLY_STATUS_CHARGING;
+	smb137b_chg->batt_chg_type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+
+	ret = smb137b_read_reg(smb137b_chg->client, STATUS_E_REG, &temp);
+	if (ret) {
+		dev_err(&smb137b_chg->client->dev,
+			"%s couldn't read status e reg %d\n", __func__, ret);
+	} else {
+		if (temp & CHARGER_ERROR_STAT) {
+			dev_err(&smb137b_chg->client->dev,
+				"%s chg error E=0x%x\n", __func__, temp);
+			smb137b_dbg_print_status_regs(smb137b_chg);
+		}
+		if (((temp & CHARGING_STAT_E) >> 1) >= FAST_CHG_E_STATUS)
+			smb137b_chg->batt_chg_type
+						= POWER_SUPPLY_CHARGE_TYPE_FAST;
+	}
+	/*schedule charge_work to keep track of battery charging state*/
+	schedule_delayed_work(&smb137b_chg->charge_work, SMB137B_CHG_PERIOD);
+
+out:
+	return ret;
+}
+
+static int smb137b_stop_charging(struct msm_hardware_charger *hw_chg)
+{
+	int ret = 0;
+	struct smb137b_data *smb137b_chg;
+
+	smb137b_chg = container_of(hw_chg, struct smb137b_data, adapter_hw_chg);
+
+	dev_dbg(&smb137b_chg->client->dev, "%s\n", __func__);
+	if (smb137b_chg->charging == false)
+		return 0;
+
+	smb137b_chg->charging = false;
+	smb137b_chg->batt_status = POWER_SUPPLY_STATUS_DISCHARGING;
+	smb137b_chg->batt_chg_type = POWER_SUPPLY_CHARGE_TYPE_NONE;
+
+	ret = smb137b_write_reg(smb137b_chg->client, COMMAND_A_REG,
+				smb137b_chg->cur_charging_mode);
+	if (ret) {
+		dev_err(&smb137b_chg->client->dev,
+			"%s couldn't write to command_reg\n", __func__);
+		goto out;
+	}
+
+	ret = smb137b_write_reg(smb137b_chg->client,
+					PIN_CTRL_REG, PIN_CTRL_REG_CHG_OFF);
+	if (ret)
+		dev_err(&smb137b_chg->client->dev,
+			"%s couldn't write to pin ctrl reg\n", __func__);
+
+out:
+	return ret;
+}
+
+static int smb137b_charger_switch(struct msm_hardware_charger *hw_chg)
+{
+	struct smb137b_data *smb137b_chg;
+
+	smb137b_chg = container_of(hw_chg, struct smb137b_data, adapter_hw_chg);
+	dev_dbg(&smb137b_chg->client->dev, "%s\n", __func__);
+	return 0;
+}
+
+static irqreturn_t smb137b_valid_handler(int irq, void *dev_id)
+{
+	int val;
+	struct smb137b_data *smb137b_chg;
+	struct i2c_client *client = dev_id;
+
+	smb137b_chg = i2c_get_clientdata(client);
+
+	pr_debug("%s Cable Detected USB inserted\n", __func__);
+	/*extra delay needed to allow CABLE_DET_N settling down and debounce
+	 before	trying to sample its correct value*/
+	usleep_range(1000, 1200);
+	val = gpio_get_value_cansleep(smb137b_chg->valid_n_gpio);
+	if (val < 0) {
+		dev_err(&smb137b_chg->client->dev,
+			"%s gpio_get_value failed for %d ret=%d\n", __func__,
+			smb137b_chg->valid_n_gpio, val);
+		goto err;
+	}
+	dev_dbg(&smb137b_chg->client->dev, "%s val=%d\n", __func__, val);
+
+	if (val) {
+		if (smb137b_chg->usb_status != SMB137B_ABSENT) {
+			smb137b_chg->usb_status = SMB137B_ABSENT;
+			msm_charger_notify_event(&smb137b_chg->adapter_hw_chg,
+					CHG_REMOVED_EVENT);
+		}
+	} else {
+		if (smb137b_chg->usb_status == SMB137B_ABSENT) {
+			smb137b_chg->usb_status = SMB137B_PRESENT;
+			msm_charger_notify_event(&smb137b_chg->adapter_hw_chg,
+					CHG_INSERTED_EVENT);
+		}
+	}
+err:
+	return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *dent;
+static int debug_fs_otg;
+static int otg_get(void *data, u64 *value)
+{
+	*value = debug_fs_otg;
+	return 0;
+}
+static int otg_set(void *data, u64 value)
+{
+	smb137b_otg_power(debug_fs_otg);
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(smb137b_otg_fops, otg_get, otg_set, "%llu\n");
+
+static void smb137b_create_debugfs_entries(struct smb137b_data *smb137b_chg)
+{
+	dent = debugfs_create_dir("smb137b", NULL);
+	if (dent) {
+		debugfs_create_file("otg", 0644, dent, NULL, &smb137b_otg_fops);
+	}
+}
+static void smb137b_destroy_debugfs_entries(void)
+{
+	if (dent)
+		debugfs_remove_recursive(dent);
+}
+#else
+static void smb137b_create_debugfs_entries(struct smb137b_data *smb137b_chg)
+{
+}
+static void smb137b_destroy_debugfs_entries(void)
+{
+}
+#endif
+
+static int set_disable_status_param(const char *val, struct kernel_param *kp)
+{
+	int ret;
+
+	ret = param_set_int(val, kp);
+	if (ret)
+		return ret;
+
+	if (usb_smb137b_chg && disabled)
+		msm_charger_notify_event(&usb_smb137b_chg->adapter_hw_chg,
+				CHG_DONE_EVENT);
+
+	pr_debug("%s disabled =%d\n", __func__, disabled);
+	return 0;
+}
+module_param_call(disabled, set_disable_status_param, param_get_uint,
+					&disabled, 0644);
+static void smb137b_charge_sm(struct work_struct *smb137b_work)
+{
+	int ret;
+	struct smb137b_data *smb137b_chg;
+	u8 temp = 0;
+	int notify_batt_changed = 0;
+
+	smb137b_chg = container_of(smb137b_work, struct smb137b_data,
+			charge_work.work);
+
+	/*if not charging, exit smb137b charging state transition*/
+	if (!smb137b_chg->charging)
+		return;
+
+	dev_dbg(&smb137b_chg->client->dev, "%s\n", __func__);
+
+	ret = smb137b_read_reg(smb137b_chg->client, STATUS_F_REG, &temp);
+	if (ret) {
+		dev_err(&smb137b_chg->client->dev,
+			"%s couldn't read status f reg %d\n", __func__, ret);
+		goto out;
+	}
+	if (smb137b_chg->batt_present != !(temp & BATT_PRESENT_STAT)) {
+		smb137b_chg->batt_present = !(temp & BATT_PRESENT_STAT);
+		notify_batt_changed = 1;
+	}
+
+	if (!smb137b_chg->batt_present)
+		smb137b_chg->batt_chg_type = POWER_SUPPLY_CHARGE_TYPE_NONE;
+
+	if (!smb137b_chg->batt_present && smb137b_chg->charging)
+		msm_charger_notify_event(&smb137b_chg->adapter_hw_chg,
+				CHG_DONE_EVENT);
+
+	if (smb137b_chg->batt_present
+		&& smb137b_chg->charging
+		&& smb137b_chg->batt_chg_type
+			!= POWER_SUPPLY_CHARGE_TYPE_FAST) {
+		ret = smb137b_read_reg(smb137b_chg->client,
+						STATUS_E_REG, &temp);
+		if (ret) {
+			dev_err(&smb137b_chg->client->dev,
+				"%s couldn't read cntrl reg\n", __func__);
+			goto out;
+
+		} else {
+			if (temp & CHARGER_ERROR_STAT) {
+				dev_err(&smb137b_chg->client->dev,
+					"%s error E=0x%x\n", __func__, temp);
+				smb137b_dbg_print_status_regs(smb137b_chg);
+			}
+			if (((temp & CHARGING_STAT_E) >> 1)
+					>= FAST_CHG_E_STATUS) {
+				smb137b_chg->batt_chg_type
+					= POWER_SUPPLY_CHARGE_TYPE_FAST;
+				notify_batt_changed = 1;
+				msm_charger_notify_event(
+					&smb137b_chg->adapter_hw_chg,
+					CHG_BATT_BEGIN_FAST_CHARGING);
+			} else {
+				smb137b_chg->batt_chg_type
+					= POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+			}
+		}
+	}
+
+out:
+	schedule_delayed_work(&smb137b_chg->charge_work,
+					SMB137B_CHG_PERIOD);
+}
+
+static void __smb137b_otg_power(int on)
+{
+	int ret;
+
+	if (on) {
+		ret = smb137b_write_reg(usb_smb137b_chg->client,
+					PIN_CTRL_REG, PIN_CTRL_REG_CHG_OFF);
+		if (ret) {
+			pr_err("%s turning off charging in pin_ctrl err=%d\n",
+								__func__, ret);
+			/*
+			 * don't change the command register if charging in
+			 * pin control cannot be turned off
+			 */
+			return;
+		}
+
+		ret = smb137b_write_reg(usb_smb137b_chg->client,
+			COMMAND_A_REG, COMMAND_A_REG_OTG_MODE);
+		if (ret)
+			pr_err("%s failed turning on OTG mode ret=%d\n",
+								__func__, ret);
+	} else {
+		ret = smb137b_write_reg(usb_smb137b_chg->client,
+			COMMAND_A_REG, COMMAND_A_REG_DEFAULT);
+		if (ret)
+			pr_err("%s failed turning off OTG mode ret=%d\n",
+								__func__, ret);
+		ret = smb137b_write_reg(usb_smb137b_chg->client,
+				PIN_CTRL_REG, PIN_CTRL_REG_DEFAULT);
+		if (ret)
+			pr_err("%s failed writing to pn_ctrl ret=%d\n",
+								__func__, ret);
+	}
+}
+static int __devinit smb137b_probe(struct i2c_client *client,
+				    const struct i2c_device_id *id)
+{
+	const struct smb137b_platform_data *pdata;
+	struct smb137b_data *smb137b_chg;
+	int ret = 0;
+
+	pdata = client->dev.platform_data;
+
+	if (pdata == NULL) {
+		dev_err(&client->dev, "%s no platform data\n", __func__);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (!i2c_check_functionality(client->adapter,
+				I2C_FUNC_SMBUS_BYTE_DATA)) {
+		ret = -EIO;
+		goto out;
+	}
+
+	smb137b_chg = kzalloc(sizeof(*smb137b_chg), GFP_KERNEL);
+	if (!smb137b_chg) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	INIT_DELAYED_WORK(&smb137b_chg->charge_work, smb137b_charge_sm);
+	smb137b_chg->client = client;
+	smb137b_chg->valid_n_gpio = pdata->valid_n_gpio;
+	smb137b_chg->batt_mah_rating = pdata->batt_mah_rating;
+	if (smb137b_chg->batt_mah_rating == 0)
+		smb137b_chg->batt_mah_rating = SMB137B_DEFAULT_BATT_RATING;
+
+	/*It supports USB-WALL charger and PC USB charger */
+	smb137b_chg->adapter_hw_chg.type = CHG_TYPE_USB;
+	smb137b_chg->adapter_hw_chg.rating = pdata->batt_mah_rating;
+	smb137b_chg->adapter_hw_chg.name = "smb137b-charger";
+	smb137b_chg->adapter_hw_chg.start_charging = smb137b_start_charging;
+	smb137b_chg->adapter_hw_chg.stop_charging = smb137b_stop_charging;
+	smb137b_chg->adapter_hw_chg.charging_switched = smb137b_charger_switch;
+
+	if (pdata->chg_detection_config)
+		ret = pdata->chg_detection_config();
+	if (ret) {
+		dev_err(&client->dev, "%s valid config failed ret=%d\n",
+			__func__, ret);
+		goto free_smb137b_chg;
+	}
+
+	ret = gpio_request(pdata->valid_n_gpio, "smb137b_charger_valid");
+	if (ret) {
+		dev_err(&client->dev, "%s gpio_request failed for %d ret=%d\n",
+			__func__, pdata->valid_n_gpio, ret);
+		goto free_smb137b_chg;
+	}
+
+	i2c_set_clientdata(client, smb137b_chg);
+
+	ret = msm_charger_register(&smb137b_chg->adapter_hw_chg);
+	if (ret) {
+		dev_err(&client->dev, "%s msm_charger_register\
+			failed for ret=%d\n", __func__, ret);
+		goto free_valid_gpio;
+	}
+
+	ret = irq_set_irq_wake(client->irq, 1);
+	if (ret) {
+		dev_err(&client->dev, "%s failed for irq_set_irq_wake %d ret =%d\n",
+			 __func__, client->irq, ret);
+		goto unregister_charger;
+	}
+
+	ret = request_threaded_irq(client->irq, NULL,
+				   smb137b_valid_handler,
+				   IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+				   "smb137b_charger_valid", client);
+	if (ret) {
+		dev_err(&client->dev,
+			"%s request_threaded_irq failed for %d ret =%d\n",
+			__func__, client->irq, ret);
+		goto disable_irq_wake;
+	}
+
+	ret = gpio_get_value_cansleep(smb137b_chg->valid_n_gpio);
+	if (ret < 0) {
+		dev_err(&client->dev,
+			"%s gpio_get_value failed for %d ret=%d\n", __func__,
+			pdata->valid_n_gpio, ret);
+		/* assume absent */
+		ret = 1;
+	}
+	if (!ret) {
+		msm_charger_notify_event(&smb137b_chg->adapter_hw_chg,
+			CHG_INSERTED_EVENT);
+		smb137b_chg->usb_status = SMB137B_PRESENT;
+	}
+
+	ret = smb137b_read_reg(smb137b_chg->client, DEV_ID_REG,
+			&smb137b_chg->dev_id_reg);
+
+	ret = device_create_file(&smb137b_chg->client->dev, &dev_attr_id_reg);
+
+	/* TODO read min_design and max_design from chip registers */
+	smb137b_chg->min_design = 3200;
+	smb137b_chg->max_design = 4200;
+
+	smb137b_chg->batt_status = POWER_SUPPLY_STATUS_DISCHARGING;
+	smb137b_chg->batt_chg_type = POWER_SUPPLY_CHARGE_TYPE_NONE;
+
+	device_init_wakeup(&client->dev, 1);
+
+	mutex_lock(&init_lock);
+	usb_smb137b_chg = smb137b_chg;
+	if (init_otg_power)
+		__smb137b_otg_power(init_otg_power);
+	mutex_unlock(&init_lock);
+
+	smb137b_create_debugfs_entries(smb137b_chg);
+	dev_dbg(&client->dev,
+		"%s OK device_id = %x chg_state=%d\n", __func__,
+		smb137b_chg->dev_id_reg, smb137b_chg->usb_status);
+	return 0;
+
+disable_irq_wake:
+	irq_set_irq_wake(client->irq, 0);
+unregister_charger:
+	msm_charger_unregister(&smb137b_chg->adapter_hw_chg);
+free_valid_gpio:
+	gpio_free(pdata->valid_n_gpio);
+free_smb137b_chg:
+	kfree(smb137b_chg);
+out:
+	return ret;
+}
+
+void smb137b_otg_power(int on)
+{
+	pr_debug("%s Enter on=%d\n", __func__, on);
+
+	mutex_lock(&init_lock);
+	if (!usb_smb137b_chg) {
+		init_otg_power = !!on;
+		pr_warning("%s called when not initialized\n", __func__);
+		mutex_unlock(&init_lock);
+		return;
+	}
+	__smb137b_otg_power(on);
+	mutex_unlock(&init_lock);
+}
+
+static int __devexit smb137b_remove(struct i2c_client *client)
+{
+	const struct smb137b_platform_data *pdata;
+	struct smb137b_data *smb137b_chg = i2c_get_clientdata(client);
+
+	pdata = client->dev.platform_data;
+	device_init_wakeup(&client->dev, 0);
+	irq_set_irq_wake(client->irq, 0);
+	free_irq(client->irq, client);
+	gpio_free(pdata->valid_n_gpio);
+	cancel_delayed_work_sync(&smb137b_chg->charge_work);
+
+	msm_charger_notify_event(&smb137b_chg->adapter_hw_chg,
+			 CHG_REMOVED_EVENT);
+	msm_charger_unregister(&smb137b_chg->adapter_hw_chg);
+	smb137b_destroy_debugfs_entries();
+	kfree(smb137b_chg);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int smb137b_suspend(struct device *dev)
+{
+	struct smb137b_data *smb137b_chg = dev_get_drvdata(dev);
+
+	dev_dbg(&smb137b_chg->client->dev, "%s\n", __func__);
+	if (smb137b_chg->charging)
+		return -EBUSY;
+	return 0;
+}
+
+static int smb137b_resume(struct device *dev)
+{
+	struct smb137b_data *smb137b_chg = dev_get_drvdata(dev);
+
+	dev_dbg(&smb137b_chg->client->dev, "%s\n", __func__);
+	return 0;
+}
+
+static const struct dev_pm_ops smb137b_pm_ops = {
+	.suspend = smb137b_suspend,
+	.resume = smb137b_resume,
+};
+#endif
+
+static const struct i2c_device_id smb137b_id[] = {
+	{"smb137b", 0},
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, smb137b_id);
+
+static struct i2c_driver smb137b_driver = {
+	.driver = {
+		   .name = "smb137b",
+		   .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+		   .pm = &smb137b_pm_ops,
+#endif
+	},
+	.probe = smb137b_probe,
+	.remove = __devexit_p(smb137b_remove),
+	.id_table = smb137b_id,
+};
+
+static int __init smb137b_init(void)
+{
+	return i2c_add_driver(&smb137b_driver);
+}
+module_init(smb137b_init);
+
+static void __exit smb137b_exit(void)
+{
+	return i2c_del_driver(&smb137b_driver);
+}
+module_exit(smb137b_exit);
+
+MODULE_AUTHOR("Abhijeet Dharmapurikar <adharmap@codeaurora.org>");
+MODULE_DESCRIPTION("Driver for SMB137B Charger chip");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("i2c:smb137b");
diff --git a/drivers/power/smb349.c b/drivers/power/smb349.c
new file mode 100644
index 0000000..4c07285
--- /dev/null
+++ b/drivers/power/smb349.c
@@ -0,0 +1,700 @@
+/* Copyright (c) 2012 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#define pr_fmt(fmt)	"%s: " fmt, __func__
+
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/i2c/smb349.h>
+#include <linux/power_supply.h>
+
+#define SMB349_MASK(BITS, POS)  ((unsigned char)(((1 << BITS) - 1) << POS))
+
+/* Register definitions */
+#define CHG_CURRENT_REG			0x00
+#define CHG_OTHER_CURRENT_REG	0x01
+#define VAR_FUNC_REG			0x02
+#define FLOAT_VOLTAGE_REG		0x03
+#define CHG_CTRL_REG			0x04
+#define STAT_TIMER_REG			0x05
+#define PIN_ENABLE_CTRL_REG		0x06
+#define THERM_CTRL_A_REG		0x07
+#define SYSOK_USB3_SELECT_REG	0x08
+#define CTRL_FUNCTIONS_REG		0x09
+#define OTG_TLIM_THERM_CNTRL_REG	0x0A
+#define HARD_SOFT_LIMIT_CELL_TEMP_MONITOR_REG 0x0B
+#define FAULT_IRQ_REG	0x0C
+#define STATUS_IRQ_REG	0x0D
+#define SYSOK_REG		0x0E
+#define CMD_A_REG		0x30
+#define CMD_B_REG		0x31
+#define CMD_C_REG		0x33
+#define IRQ_A_REG		0x35
+#define IRQ_B_REG		0x36
+#define IRQ_C_REG		0x37
+#define IRQ_D_REG		0x38
+#define IRQ_E_REG		0x39
+#define IRQ_F_REG		0x3A
+#define STATUS_A_REG	0x3B
+#define STATUS_B_REG	0x3C
+#define STATUS_C_REG	0x3D
+#define STATUS_D_REG	0x3E
+#define STATUS_E_REG	0x3F
+
+/* Status bits and masks */
+#define CHG_STATUS_MASK		SMB349_MASK(2, 1)
+#define CHG_ENABLE_STATUS_BIT		BIT(0)
+
+/* Control bits and masks */
+#define FAST_CHG_CURRENT_MASK			SMB349_MASK(4, 4)
+#define AC_INPUT_CURRENT_LIMIT_MASK		SMB349_MASK(4, 0)
+#define PRE_CHG_CURRENT_MASK			SMB349_MASK(3, 5)
+#define TERMINATION_CURRENT_MASK		SMB349_MASK(3, 2)
+#define PRE_CHG_TO_FAST_CHG_THRESH_MASK	SMB349_MASK(2, 6)
+#define FLOAT_VOLTAGE_MASK				SMB349_MASK(6, 0)
+#define CHG_ENABLE_BIT			BIT(1)
+#define VOLATILE_W_PERM_BIT		BIT(7)
+#define USB_SELECTION_BIT		BIT(1)
+#define SYSTEM_FET_ENABLE_BIT	BIT(7)
+#define AUTOMATIC_INPUT_CURR_LIMIT_BIT			BIT(4)
+#define AUTOMATIC_POWER_SOURCE_DETECTION_BIT	BIT(2)
+#define BATT_OV_END_CHG_BIT		BIT(1)
+#define VCHG_FUNCTION			BIT(0)
+#define CURR_TERM_END_CHG_BIT	BIT(6)
+
+struct smb349_struct {
+	struct			i2c_client *client;
+	bool			charging;
+	bool			present;
+	int			chg_current_ma;
+
+	int			en_n_gpio;
+	int			chg_susp_gpio;
+	struct dentry			*dent;
+	spinlock_t			lock;
+	struct work_struct			hwinit_work;
+
+	struct power_supply			dc_psy;
+};
+
+struct chg_ma_limit_entry {
+	int fast_chg_ma_limit;
+	int ac_input_ma_limit;
+	u8  chg_current_value;
+};
+
+static struct smb349_struct *the_smb349_chg;
+
+static int smb349_read_reg(struct i2c_client *client, int reg,
+				u8 *val)
+{
+	s32 ret;
+	struct smb349_struct *smb349_chg;
+
+	smb349_chg = i2c_get_clientdata(client);
+	ret = i2c_smbus_read_byte_data(smb349_chg->client, reg);
+	if (ret < 0) {
+		dev_err(&smb349_chg->client->dev,
+			"i2c read fail: can't read from %02x: %d\n", reg, ret);
+		return ret;
+	} else {
+		*val = ret;
+	}
+
+	return 0;
+}
+
+static int smb349_write_reg(struct i2c_client *client, int reg,
+						u8 val)
+{
+	s32 ret;
+	struct smb349_struct *smb349_chg;
+
+	smb349_chg = i2c_get_clientdata(client);
+	ret = i2c_smbus_write_byte_data(smb349_chg->client, reg, val);
+	if (ret < 0) {
+		dev_err(&smb349_chg->client->dev,
+			"i2c write fail: can't write %02x to %02x: %d\n",
+			val, reg, ret);
+		return ret;
+	}
+	return 0;
+}
+
+static int smb349_masked_write(struct i2c_client *client, int reg,
+		u8 mask, u8 val)
+{
+	s32 rc;
+	u8 temp;
+
+	rc = smb349_read_reg(client, reg, &temp);
+	if (rc) {
+		pr_err("smb349_read_reg failed: reg=%03X, rc=%d\n", reg, rc);
+		return rc;
+	}
+	temp &= ~mask;
+	temp |= val & mask;
+	rc = smb349_write_reg(client, reg, temp);
+	if (rc) {
+		pr_err("smb349_write failed: reg=%03X, rc=%d\n", reg, rc);
+		return rc;
+	}
+	return 0;
+}
+
+static enum power_supply_property pm_power_props[] = {
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_CURRENT_MAX,
+	POWER_SUPPLY_PROP_CHARGE_TYPE,
+};
+
+static char *pm_power_supplied_to[] = {
+	"battery",
+};
+
+static int get_prop_charge_type(struct smb349_struct *smb349_chg)
+{
+	if (smb349_chg->charging)
+		return POWER_SUPPLY_CHARGE_TYPE_FAST;
+
+	return POWER_SUPPLY_CHARGE_TYPE_NONE;
+}
+
+static int pm_power_get_property(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  union power_supply_propval *val)
+{
+	struct smb349_struct *smb349_chg = container_of(psy,
+						struct smb349_struct,
+						dc_psy);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		val->intval = smb349_chg->chg_current_ma;
+		break;
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = (int)smb349_chg->present;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_TYPE:
+		val->intval = get_prop_charge_type(smb349_chg);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+#define SMB349_FAST_CHG_MIN_MA	1000
+#define SMB349_FAST_CHG_STEP_MA	200
+#define SMB349_FAST_CHG_MAX_MA	4000
+#define SMB349_FAST_CHG_SHIFT	4
+static int chg_current_set(struct smb349_struct *smb349_chg)
+{
+	u8 temp;
+
+	if ((smb349_chg->chg_current_ma < SMB349_FAST_CHG_MIN_MA) ||
+		(smb349_chg->chg_current_ma >  SMB349_FAST_CHG_MAX_MA)) {
+		pr_err("bad mA=%d asked to set\n", smb349_chg->chg_current_ma);
+		return -EINVAL;
+	}
+
+	temp = (smb349_chg->chg_current_ma - SMB349_FAST_CHG_MIN_MA)
+			/ SMB349_FAST_CHG_STEP_MA;
+
+	temp = temp << SMB349_FAST_CHG_SHIFT;
+	pr_debug("fastchg limit=%d setting %02x\n",
+				smb349_chg->chg_current_ma, temp);
+	return smb349_masked_write(smb349_chg->client, CHG_CURRENT_REG,
+			FAST_CHG_CURRENT_MASK, temp);
+}
+
+static int set_reg(void *data, u64 val)
+{
+	int addr = (int)data;
+	int ret;
+	u8 temp;
+
+	temp = (u16) val;
+	ret = smb349_write_reg(the_smb349_chg->client, addr, temp);
+
+	if (ret) {
+		pr_err("smb349_write_reg to %x value =%d errored = %d\n",
+			addr, temp, ret);
+		return -EAGAIN;
+	}
+	return 0;
+}
+static int get_reg(void *data, u64 *val)
+{
+	int addr = (int)data;
+	int ret;
+	u8 temp;
+
+	ret = smb349_read_reg(the_smb349_chg->client, addr, &temp);
+	if (ret) {
+		pr_err("smb349_read_reg to %x value =%d errored = %d\n",
+			addr, temp, ret);
+		return -EAGAIN;
+	}
+
+	*val = temp;
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_reg, set_reg, "0x%02llx\n");
+
+static void create_debugfs_entries(struct smb349_struct *smb349_chg)
+{
+	struct dentry *file;
+	smb349_chg->dent = debugfs_create_dir(SMB349_NAME, NULL);
+	if (IS_ERR(smb349_chg->dent)) {
+		pr_err("smb349 driver couldn't create debugfs dir\n");
+		return;
+	}
+
+	file = debugfs_create_file("CHG_CURRENT_REG", 0644, smb349_chg->dent,
+				(void *) CHG_CURRENT_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("CHG_OTHER_CURRENT_REG", 0644,
+		smb349_chg->dent, (void *) CHG_OTHER_CURRENT_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("VAR_FUNC_REG", 0644, smb349_chg->dent,
+				(void *) VAR_FUNC_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("FLOAT_VOLTAGE_REG", 0644, smb349_chg->dent,
+				(void *) FLOAT_VOLTAGE_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("CHG_CTRL_REG", 0644, smb349_chg->dent,
+				(void *) CHG_CTRL_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("STAT_TIMER_REG", 0644, smb349_chg->dent,
+				(void *) STAT_TIMER_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("PIN_ENABLE_CTRL_REG", 0644,
+		smb349_chg->dent, (void *) PIN_ENABLE_CTRL_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("PIN_ENABLE_CTRL_REG", 0644,
+		smb349_chg->dent, (void *) PIN_ENABLE_CTRL_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("PIN_ENABLE_CTRL_REG", 0644,
+		smb349_chg->dent, (void *) PIN_ENABLE_CTRL_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("THERM_CTRL_A_REG", 0644, smb349_chg->dent,
+				(void *) THERM_CTRL_A_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("SYSOK_USB3_SELECT_REG", 0644,
+		smb349_chg->dent, (void *) SYSOK_USB3_SELECT_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("CTRL_FUNCTIONS_REG", 0644,
+		smb349_chg->dent, (void *) CTRL_FUNCTIONS_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("OTG_TLIM_THERM_CNTRL_REG", 0644,
+		smb349_chg->dent, (void *) OTG_TLIM_THERM_CNTRL_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("HARD_SOFT_LIMIT_CELL_TEMP_MONITOR_REG",
+		0644, smb349_chg->dent,
+		(void *) HARD_SOFT_LIMIT_CELL_TEMP_MONITOR_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("SYSOK_REG", 0644, smb349_chg->dent,
+				(void *) SYSOK_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("CMD_A_REG", 0644, smb349_chg->dent,
+				(void *) CMD_A_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("CMD_B_REG", 0644, smb349_chg->dent,
+				(void *) CMD_B_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("CMD_C_REG", 0644, smb349_chg->dent,
+				(void *) CMD_C_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("STATUS_A_REG", 0644, smb349_chg->dent,
+				(void *) STATUS_A_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("STATUS_B_REG", 0644, smb349_chg->dent,
+				(void *) STATUS_B_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("STATUS_C_REG", 0644, smb349_chg->dent,
+				(void *) STATUS_C_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("STATUS_D_REG", 0644, smb349_chg->dent,
+				(void *) STATUS_D_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("STATUS_E_REG", 0644, smb349_chg->dent,
+				(void *) STATUS_E_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+}
+
+static void remove_debugfs_entries(struct smb349_struct *smb349_chg)
+{
+	if (smb349_chg->dent)
+		debugfs_remove_recursive(smb349_chg->dent);
+}
+
+static int smb349_hwinit(struct smb349_struct *smb349_chg)
+{
+	int ret;
+
+	ret = smb349_write_reg(smb349_chg->client, CMD_A_REG,
+			VOLATILE_W_PERM_BIT);
+	if (ret) {
+		pr_err("Failed to set VOLATILE_W_PERM_BIT rc=%d\n", ret);
+		return ret;
+	}
+
+	ret = smb349_masked_write(smb349_chg->client, CHG_CTRL_REG,
+				CURR_TERM_END_CHG_BIT, CURR_TERM_END_CHG_BIT);
+	if (ret) {
+		pr_err("Failed to set CURR_TERM_END_CHG_BIT rc=%d\n", ret);
+		return ret;
+	}
+
+	ret = chg_current_set(smb349_chg);
+	if (ret) {
+		pr_err("Failed to set FAST_CHG_CURRENT rc=%d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int smb349_stop_charging(struct smb349_struct *smb349_chg)
+{
+	unsigned long flags;
+
+	if (smb349_chg->charging)
+		gpio_set_value_cansleep(smb349_chg->en_n_gpio, 0);
+
+	spin_lock_irqsave(&smb349_chg->lock, flags);
+	pr_debug("stop charging %d\n", smb349_chg->charging);
+	smb349_chg->charging = 0;
+	spin_unlock_irqrestore(&smb349_chg->lock, flags);
+	power_supply_changed(&smb349_chg->dc_psy);
+	return 0;
+}
+
+static int smb349_start_charging(struct smb349_struct *smb349_chg)
+{
+	unsigned long flags;
+	int rc;
+
+	rc = 0;
+	if (!smb349_chg->charging) {
+		gpio_set_value_cansleep(smb349_chg->en_n_gpio, 1);
+		/*
+		 * Write non-default values, charger chip reloads from
+		 * non-volatile memory if it was in suspend mode
+		 *
+		 */
+		rc = schedule_work(&smb349_chg->hwinit_work);
+	}
+
+	spin_lock_irqsave(&smb349_chg->lock, flags);
+	pr_debug("start charging %d\n", smb349_chg->charging);
+	smb349_chg->charging = 1;
+	spin_unlock_irqrestore(&smb349_chg->lock, flags);
+	power_supply_changed(&smb349_chg->dc_psy);
+	return rc;
+}
+
+static int pm_power_set_property(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  const union power_supply_propval *val)
+{
+	struct smb349_struct *smb349_chg = container_of(psy,
+						struct smb349_struct,
+						dc_psy);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		if (val->intval) {
+			smb349_chg->present = val->intval;
+		} else {
+			smb349_chg->present = 0;
+			if (smb349_chg->charging)
+				return smb349_stop_charging(smb349_chg);
+		}
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		if (val->intval) {
+			if (smb349_chg->chg_current_ma != val->intval)
+				return -EINVAL;
+		}
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_TYPE:
+		if (val->intval && smb349_chg->present) {
+			if (val->intval == POWER_SUPPLY_CHARGE_TYPE_FAST)
+				return smb349_start_charging(smb349_chg);
+			if (val->intval == POWER_SUPPLY_CHARGE_TYPE_NONE)
+				return smb349_stop_charging(smb349_chg);
+		} else {
+			return -EINVAL;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+	power_supply_changed(&smb349_chg->dc_psy);
+	return 0;
+}
+
+static void hwinit_worker(struct work_struct *work)
+{
+	int ret;
+	struct smb349_struct *smb349_chg = container_of(work,
+				struct smb349_struct, hwinit_work);
+
+	ret = smb349_hwinit(smb349_chg);
+	if (ret)
+		pr_err("Failed to re-initilaze registers\n");
+}
+
+static int __devinit smb349_init_ext_chg(struct smb349_struct *smb349_chg)
+{
+	int ret;
+
+	smb349_chg->dc_psy.name = "dc";
+	smb349_chg->dc_psy.type = POWER_SUPPLY_TYPE_MAINS;
+	smb349_chg->dc_psy.supplied_to = pm_power_supplied_to;
+	smb349_chg->dc_psy.num_supplicants = ARRAY_SIZE(pm_power_supplied_to);
+	smb349_chg->dc_psy.properties = pm_power_props;
+	smb349_chg->dc_psy.num_properties = ARRAY_SIZE(pm_power_props);
+	smb349_chg->dc_psy.get_property = pm_power_get_property;
+	smb349_chg->dc_psy.set_property = pm_power_set_property;
+
+	ret = power_supply_register(&smb349_chg->client->dev,
+				&smb349_chg->dc_psy);
+	if (ret) {
+		pr_err("failed to register power_supply. ret=%d.\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int __devinit smb349_probe(struct i2c_client *client,
+				    const struct i2c_device_id *id)
+{
+	const struct smb349_platform_data *pdata;
+	struct smb349_struct *smb349_chg;
+	int ret = 0;
+
+	pdata = client->dev.platform_data;
+
+	if (pdata == NULL) {
+		dev_err(&client->dev, "%s no platform data\n", __func__);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (!i2c_check_functionality(client->adapter,
+				I2C_FUNC_SMBUS_BYTE_DATA)) {
+		ret = -EIO;
+		goto out;
+	}
+
+	smb349_chg = kzalloc(sizeof(*smb349_chg), GFP_KERNEL);
+	if (!smb349_chg) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	smb349_chg->client = client;
+	smb349_chg->chg_current_ma = pdata->chg_current_ma;
+	ret = gpio_request(pdata->chg_susp_gpio, "smb349_suspend");
+	if (ret) {
+		dev_err(&client->dev, "%s gpio_request failed for %d ret=%d\n",
+			__func__, pdata->chg_susp_gpio, ret);
+		goto free_smb349_chg;
+	}
+	smb349_chg->chg_susp_gpio = pdata->chg_susp_gpio;
+
+	ret = gpio_request(pdata->en_n_gpio, "smb349_charger_enable");
+	if (ret) {
+		dev_err(&client->dev, "%s gpio_request failed for %d ret=%d\n",
+			__func__, pdata->en_n_gpio, ret);
+		goto chg_susp_gpio_fail;
+	}
+	smb349_chg->en_n_gpio = pdata->en_n_gpio;
+
+	i2c_set_clientdata(client, smb349_chg);
+
+	ret = smb349_hwinit(smb349_chg);
+	if (ret)
+		goto free_smb349_chg;
+
+	ret = smb349_init_ext_chg(smb349_chg);
+	if (ret)
+		goto chg_en_gpio_fail;
+
+	the_smb349_chg = smb349_chg;
+
+	create_debugfs_entries(smb349_chg);
+	INIT_WORK(&smb349_chg->hwinit_work, hwinit_worker);
+
+	pr_info("OK connector present = %d\n", smb349_chg->present);
+	return 0;
+
+chg_en_gpio_fail:
+	gpio_free(smb349_chg->en_n_gpio);
+chg_susp_gpio_fail:
+	gpio_free(smb349_chg->chg_susp_gpio);
+free_smb349_chg:
+	kfree(smb349_chg);
+out:
+	return ret;
+}
+
+static int __devexit smb349_remove(struct i2c_client *client)
+{
+	const struct smb349_platform_data *pdata;
+	struct smb349_struct *smb349_chg = i2c_get_clientdata(client);
+
+	flush_work(&smb349_chg->hwinit_work);
+	pdata = client->dev.platform_data;
+	power_supply_unregister(&smb349_chg->dc_psy);
+	gpio_free(pdata->en_n_gpio);
+	gpio_free(pdata->chg_susp_gpio);
+	remove_debugfs_entries(smb349_chg);
+	kfree(smb349_chg);
+	return 0;
+}
+
+static int smb349_suspend(struct device *dev)
+{
+	struct smb349_struct *smb349_chg = dev_get_drvdata(dev);
+
+	pr_debug("suspend\n");
+	if (smb349_chg->charging)
+		return -EBUSY;
+	return 0;
+}
+
+static int smb349_resume(struct device *dev)
+{
+	pr_debug("resume\n");
+
+	return 0;
+}
+
+static const struct dev_pm_ops smb349_pm_ops = {
+	.suspend	= smb349_suspend,
+	.resume		= smb349_resume,
+};
+
+static const struct i2c_device_id smb349_id[] = {
+	{SMB349_NAME, 0},
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, smb349_id);
+
+static struct i2c_driver smb349_driver = {
+	.driver	= {
+		   .name	= SMB349_NAME,
+		   .owner	= THIS_MODULE,
+		   .pm		= &smb349_pm_ops,
+	},
+	.probe		= smb349_probe,
+	.remove		= __devexit_p(smb349_remove),
+	.id_table	= smb349_id,
+};
+
+static int __init smb349_init(void)
+{
+	return i2c_add_driver(&smb349_driver);
+}
+module_init(smb349_init);
+
+static void __exit smb349_exit(void)
+{
+	return i2c_del_driver(&smb349_driver);
+}
+module_exit(smb349_exit);
+
+MODULE_DESCRIPTION("Driver for SMB349 charger chip");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("i2c:" SMB349_NAME);
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 36db5a4..2bbc796 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -366,5 +366,65 @@
 	  This driver provides support for the voltage regulators on the
 	  WM8994 CODEC.
 
-endif
+config REGULATOR_PMIC8058
+        tristate "PMIC8058 regulator driver"
+        depends on PMIC8058 && (ARCH_MSM8X60 || ARCH_FSM9XXX)
+        default y if PMIC8058 && (ARCH_MSM8X60 || ARCH_FSM9XXX)
+        help
+         Say Y here to support the voltage regulators on PMIC8058
 
+config REGULATOR_PMIC8901
+        tristate "PMIC8901 regulator driver"
+        depends on PMIC8901 && ARCH_MSM8X60
+        default y if PMIC8901 && ARCH_MSM8X60
+        help
+         Say Y here to support the voltage regulators on PMIC8901
+
+config REGULATOR_PM8XXX
+	tristate "Qualcomm PM8XXX PMIC Voltage regulators"
+	depends on MFD_PM8XXX
+	help
+	  This driver supports voltage regulators in Qualcomm PM8XXX PMIC chips.
+	  PM8XXX chips provide several different varieties of LDO and switching
+	  regulators.  They also provide negative charge pumps and voltage
+	  switches.
+
+config REGULATOR_MSM_GPIO
+	tristate "MSM GPIO regulator"
+	depends on GPIOLIB
+	help
+	  This driver provides a regulator wrapper around a GPIO pin that is set
+	  to output.  It is intended to be used for GPIO pins that provide the
+	  enable signal to a physical regulator.  The GPIO enable signal can
+	  be configured to be active high (default) or active low.
+
+config REGULATOR_PM8058_XO
+	tristate "PM8058 XO Buffer driver"
+	depends on PMIC8058
+	default n
+	help
+	  This driver supports xo buffer control in the Qualcomm PM8058 PMIC
+	  chip. It is only supposed to be used when Linux on application
+	  processor is the master in control of XO buffers.
+
+config REGULATOR_STUB
+	tristate "Stub Regulator"
+	help
+	  This driver adds stub regulator support. The driver is absent of any
+	  real hardware based implementation. It allows for clients to register
+	  their regulator device constraints and use all of the standard
+	  regulator interfaces. This is useful for bringing up new platforms
+	  when the real hardware based implementation may not be yet available.
+	  Clients can use the real regulator device names with proper
+	  constraint checking while the real driver is being developed.
+
+config REGULATOR_QPNP
+	depends on OF_SPMI
+	depends on MSM_QPNP
+	tristate "Qualcomm QPNP regulator support"
+	help
+	  This driver supports voltage regulators in Qualcomm PMIC chips which
+	  comply with QPNP.  QPNP is a SPMI based PMIC implementation.  These
+	  chips provide several different varieties of LDO and switching
+	  regulators.  They also provide voltage switches and boost regulators.
+endif
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 94b5274..7fa396f 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -44,6 +44,13 @@
 obj-$(CONFIG_REGULATOR_TPS6524X) += tps6524x-regulator.o
 obj-$(CONFIG_REGULATOR_TPS6586X) += tps6586x-regulator.o
 obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o
+obj-$(CONFIG_REGULATOR_AAT2870) += aat2870-regulator.o
+obj-$(CONFIG_REGULATOR_PMIC8058) += pmic8058-regulator.o
+obj-$(CONFIG_REGULATOR_PMIC8901) += pmic8901-regulator.o
+obj-$(CONFIG_REGULATOR_MSM_GPIO) += msm-gpio-regulator.o
+obj-$(CONFIG_REGULATOR_PM8058_XO) += pm8058-xo.o
+obj-$(CONFIG_REGULATOR_PM8XXX) += pm8xxx-regulator.o
+obj-$(CONFIG_REGULATOR_STUB) += stub-regulator.o
 obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o
 obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o
 obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o
@@ -52,6 +59,6 @@
 obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o
 obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o
 obj-$(CONFIG_REGULATOR_WM8994) += wm8994-regulator.o
-
+obj-$(CONFIG_REGULATOR_QPNP) += qpnp-regulator.o
 
 ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 046fb1b..986d55b 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -24,6 +24,8 @@
 #include <linux/suspend.h>
 #include <linux/delay.h>
 #include <linux/of.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
 #include <linux/regulator/of_regulator.h>
 #include <linux/regulator/consumer.h>
 #include <linux/regulator/driver.h>
@@ -51,6 +53,7 @@
 static LIST_HEAD(regulator_map_list);
 static bool has_full_constraints;
 static bool board_wants_dummy_regulator;
+static int suppress_info_printing;
 
 static struct dentry *debugfs_root;
 
@@ -77,6 +80,7 @@
 	int uA_load;
 	int min_uV;
 	int max_uV;
+	int enabled;
 	char *supply_name;
 	struct device_attribute dev_attr;
 	struct regulator_dev *rdev;
@@ -170,6 +174,15 @@
 		return -EPERM;
 	}
 
+	/* check if requested voltage range actually overlaps the constraints */
+	if (*max_uV < rdev->constraints->min_uV ||
+	    *min_uV > rdev->constraints->max_uV) {
+		rdev_err(rdev, "requested voltage range [%d, %d] does not fit "
+			"within constraints: [%d, %d]\n", *min_uV, *max_uV,
+			rdev->constraints->min_uV, rdev->constraints->max_uV);
+		return -EINVAL;
+	}
+
 	if (*max_uV > rdev->constraints->max_uV)
 		*max_uV = rdev->constraints->max_uV;
 	if (*min_uV < rdev->constraints->min_uV)
@@ -191,6 +204,8 @@
 				     int *min_uV, int *max_uV)
 {
 	struct regulator *regulator;
+	int init_min_uV = *min_uV;
+	int init_max_uV = *max_uV;
 
 	list_for_each_entry(regulator, &rdev->consumer_list, list) {
 		/*
@@ -200,6 +215,13 @@
 		if (!regulator->min_uV && !regulator->max_uV)
 			continue;
 
+		if (init_max_uV < regulator->min_uV
+		    || init_min_uV > regulator->max_uV)
+			rdev_err(rdev, "requested voltage range [%d, %d] does "
+				"not fit within previously voted range: "
+				"[%d, %d]\n", init_min_uV, init_max_uV,
+				regulator->min_uV, regulator->max_uV);
+
 		if (*max_uV > regulator->max_uV)
 			*max_uV = regulator->max_uV;
 		if (*min_uV < regulator->min_uV)
@@ -632,7 +654,7 @@
 {
 	struct regulator *sibling;
 	int current_uA = 0, output_uV, input_uV, err;
-	unsigned int mode;
+	unsigned int regulator_curr_mode, mode;
 
 	err = regulator_check_drms(rdev);
 	if (err < 0 || !rdev->desc->ops->get_optimum_mode ||
@@ -665,6 +687,14 @@
 
 	/* check the new mode is allowed */
 	err = regulator_mode_constrain(rdev, &mode);
+	/* return if the same mode is requested */
+	if (rdev->desc->ops->get_mode) {
+		regulator_curr_mode = rdev->desc->ops->get_mode(rdev);
+		if (regulator_curr_mode == mode)
+			return;
+	} else
+		return;
+
 	if (err == 0)
 		rdev->desc->ops->set_mode(rdev, mode);
 }
@@ -959,7 +989,8 @@
 		}
 	}
 
-	print_constraints(rdev);
+	if (!suppress_info_printing)
+		print_constraints(rdev);
 	return 0;
 out:
 	kfree(rdev->constraints);
@@ -981,7 +1012,8 @@
 {
 	int err;
 
-	rdev_info(rdev, "supplied by %s\n", rdev_get_name(supply_rdev));
+	if (!suppress_info_printing)
+		rdev_info(rdev, "supplied by %s\n", rdev_get_name(supply_rdev));
 
 	rdev->supply = create_regulator(supply_rdev, &rdev->dev, "SUPPLY");
 	if (rdev->supply == NULL) {
@@ -1535,7 +1567,11 @@
 	}
 
 	mutex_lock(&rdev->mutex);
+
 	ret = _regulator_enable(rdev);
+	if (ret == 0)
+		regulator->enabled++;
+
 	mutex_unlock(&rdev->mutex);
 
 	if (ret != 0 && rdev->supply)
@@ -1608,6 +1644,8 @@
 
 	mutex_lock(&rdev->mutex);
 	ret = _regulator_disable(rdev);
+	if (ret == 0)
+		regulator->enabled--;
 	mutex_unlock(&rdev->mutex);
 
 	if (ret == 0 && rdev->supply)
@@ -1949,6 +1987,7 @@
 int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV)
 {
 	struct regulator_dev *rdev = regulator->rdev;
+	int prev_min_uV, prev_max_uV;
 	int ret = 0;
 
 	mutex_lock(&rdev->mutex);
@@ -1971,12 +2010,19 @@
 	ret = regulator_check_voltage(rdev, &min_uV, &max_uV);
 	if (ret < 0)
 		goto out;
+
+	prev_min_uV = regulator->min_uV;
+	prev_max_uV = regulator->max_uV;
+
 	regulator->min_uV = min_uV;
 	regulator->max_uV = max_uV;
 
 	ret = regulator_check_consumers(rdev, &min_uV, &max_uV);
-	if (ret < 0)
+	if (ret < 0) {
+		regulator->min_uV = prev_min_uV;
+		regulator->max_uV = prev_max_uV;
 		goto out;
+	}
 
 	ret = _regulator_do_set_voltage(rdev, min_uV, max_uV);
 
@@ -2554,6 +2600,42 @@
 EXPORT_SYMBOL_GPL(regulator_bulk_enable);
 
 /**
+ * regulator_bulk_set_voltage - set voltage for multiple regulator consumers
+ *
+ * @num_consumers: Number of consumers
+ * @consumers:     Consumer data; clients are stored here.
+ * @return         0 on success, an errno on failure
+ *
+ * This convenience API allows the voted voltage ranges of multiple regulator
+ * clients to be set in a single API call. If any consumers cannot have their
+ * voltages set, this function returns WITHOUT withdrawing votes for any
+ * consumers that have already been set.
+ */
+int regulator_bulk_set_voltage(int num_consumers,
+			       struct regulator_bulk_data *consumers)
+{
+	int i;
+	int rc;
+
+	for (i = 0; i < num_consumers; i++) {
+		if (!consumers[i].min_uV && !consumers[i].max_uV)
+			continue;
+		rc = regulator_set_voltage(consumers[i].consumer,
+				consumers[i].min_uV,
+				consumers[i].max_uV);
+		if (rc)
+			goto err;
+	}
+
+	return 0;
+
+err:
+	pr_err("Failed to set voltage for %s: %d\n", consumers[i].supply, rc);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(regulator_bulk_set_voltage);
+
+/**
  * regulator_bulk_disable - disable multiple regulator consumers
  *
  * @num_consumers: Number of consumers
@@ -2806,19 +2888,356 @@
 	return status;
 }
 
+#ifdef CONFIG_DEBUG_FS
+
+#define MAX_DEBUG_BUF_LEN 50
+
+static DEFINE_MUTEX(debug_buf_mutex);
+static char debug_buf[MAX_DEBUG_BUF_LEN];
+
+static int reg_debug_enable_set(void *data, u64 val)
+{
+	int err_info;
+	if (IS_ERR(data) || data == NULL) {
+		pr_err("Function Input Error %ld\n", PTR_ERR(data));
+		return -ENOMEM;
+	}
+
+	if (val)
+		err_info = regulator_enable(data);
+	else
+		err_info = regulator_disable(data);
+
+	return err_info;
+}
+
+static int reg_debug_enable_get(void *data, u64 *val)
+{
+	if (IS_ERR(data) || data == NULL) {
+		pr_err("Function Input Error %ld\n", PTR_ERR(data));
+		return -ENOMEM;
+	}
+
+	*val = regulator_is_enabled(data);
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(reg_enable_fops, reg_debug_enable_get,
+			reg_debug_enable_set, "%llu\n");
+
+static int reg_debug_fdisable_set(void *data, u64 val)
+{
+	int err_info;
+	if (IS_ERR(data) || data == NULL) {
+		pr_err("Function Input Error %ld\n", PTR_ERR(data));
+		return -ENOMEM;
+	}
+
+	if (val > 0)
+		err_info = regulator_force_disable(data);
+	else
+		err_info = 0;
+
+	return err_info;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(reg_fdisable_fops, reg_debug_enable_get,
+			reg_debug_fdisable_set, "%llu\n");
+
+static ssize_t reg_debug_volt_set(struct file *file, const char __user *buf,
+					size_t count, loff_t *ppos)
+{
+	int err_info, filled;
+	int min, max = -1;
+	if (IS_ERR(file) || file == NULL) {
+		pr_err("Function Input Error %ld\n", PTR_ERR(file));
+		return -ENOMEM;
+	}
+
+	if (count < MAX_DEBUG_BUF_LEN) {
+		mutex_lock(&debug_buf_mutex);
+
+		if (copy_from_user(debug_buf, (void __user *) buf, count))
+			return -EFAULT;
+
+		debug_buf[count] = '\0';
+		filled = sscanf(debug_buf, "%d %d", &min, &max);
+
+		mutex_unlock(&debug_buf_mutex);
+		/* check that user entered two numbers */
+		if (filled < 2 || min < 0 || max < min) {
+			pr_info("Error, correct format: 'echo \"min max\""
+				" > voltage");
+			return -ENOMEM;
+		} else {
+			err_info = regulator_set_voltage(file->private_data,
+							min, max);
+		}
+	} else {
+		pr_err("Error-Input voltage pair"
+				" string exceeds maximum buffer length");
+
+		return -ENOMEM;
+	}
+
+	return count;
+}
+
+static ssize_t reg_debug_volt_get(struct file *file, char __user *buf,
+					size_t count, loff_t *ppos)
+{
+	int voltage, output, rc;
+	if (IS_ERR(file) || file == NULL) {
+		pr_err("Function Input Error %ld\n", PTR_ERR(file));
+		return -ENOMEM;
+	}
+
+	voltage = regulator_get_voltage(file->private_data);
+	mutex_lock(&debug_buf_mutex);
+
+	output = snprintf(debug_buf, MAX_DEBUG_BUF_LEN-1, "%d\n", voltage);
+	rc = simple_read_from_buffer((void __user *) buf, output, ppos,
+					(void *) debug_buf, output);
+
+	mutex_unlock(&debug_buf_mutex);
+
+	return rc;
+}
+
+static int reg_debug_volt_open(struct inode *inode, struct file *file)
+{
+	if (IS_ERR(file) || file == NULL) {
+		pr_err("Function Input Error %ld\n", PTR_ERR(file));
+		return -ENOMEM;
+	}
+
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static const struct file_operations reg_volt_fops = {
+	.write	= reg_debug_volt_set,
+	.open   = reg_debug_volt_open,
+	.read	= reg_debug_volt_get,
+};
+
+static int reg_debug_mode_set(void *data, u64 val)
+{
+	int err_info;
+	if (IS_ERR(data) || data == NULL) {
+		pr_err("Function Input Error %ld\n", PTR_ERR(data));
+		return -ENOMEM;
+	}
+
+	err_info = regulator_set_mode(data, (unsigned int)val);
+
+	return err_info;
+}
+
+static int reg_debug_mode_get(void *data, u64 *val)
+{
+	int err_info;
+	if (IS_ERR(data) || data == NULL) {
+		pr_err("Function Input Error %ld\n", PTR_ERR(data));
+		return -ENOMEM;
+	}
+
+	err_info = regulator_get_mode(data);
+
+	if (err_info < 0) {
+		pr_err("Regulator_get_mode returned an error!\n");
+		return -ENOMEM;
+	} else {
+		*val = err_info;
+		return 0;
+	}
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(reg_mode_fops, reg_debug_mode_get,
+			reg_debug_mode_set, "%llu\n");
+
+static int reg_debug_optimum_mode_set(void *data, u64 val)
+{
+	int err_info;
+	if (IS_ERR(data) || data == NULL) {
+		pr_err("Function Input Error %ld\n", PTR_ERR(data));
+		return -ENOMEM;
+	}
+
+	err_info = regulator_set_optimum_mode(data, (unsigned int)val);
+
+	if (err_info < 0) {
+		pr_err("Regulator_set_optimum_mode returned an error!\n");
+		return err_info;
+	}
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(reg_optimum_mode_fops, reg_debug_mode_get,
+			reg_debug_optimum_mode_set, "%llu\n");
+
+static int reg_debug_consumers_show(struct seq_file *m, void *v)
+{
+	struct regulator_dev *rdev = m->private;
+	struct regulator *reg;
+	char *supply_name;
+
+	if (!rdev) {
+		pr_err("regulator device missing");
+		return -EINVAL;
+	}
+
+	mutex_lock(&rdev->mutex);
+
+	/* Print a header if there are consumers. */
+	if (rdev->open_count)
+		seq_printf(m, "Device-Supply                    "
+			"EN    Min_uV   Max_uV  load_uA\n");
+
+	list_for_each_entry(reg, &rdev->consumer_list, list) {
+		if (reg->supply_name)
+			supply_name = reg->supply_name;
+		else
+			supply_name = "(null)-(null)";
+
+		seq_printf(m, "%-32s %c   %8d %8d %8d\n", supply_name,
+			(reg->enabled ? 'Y' : 'N'), reg->min_uV, reg->max_uV,
+			reg->uA_load);
+	}
+
+	mutex_unlock(&rdev->mutex);
+
+	return 0;
+}
+
+static int reg_debug_consumers_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, reg_debug_consumers_show, inode->i_private);
+}
+
+static const struct file_operations reg_consumers_fops = {
+	.owner		= THIS_MODULE,
+	.open		= reg_debug_consumers_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
 static void rdev_init_debugfs(struct regulator_dev *rdev)
 {
+	struct dentry *err_ptr = NULL;
+	struct regulator *reg;
+	struct regulator_ops *reg_ops;
+	mode_t mode;
+
+	if (IS_ERR(rdev) || rdev == NULL ||
+		IS_ERR(debugfs_root) || debugfs_root == NULL) {
+		pr_err("Error-Bad Function Input\n");
+		goto error;
+	}
+
 	rdev->debugfs = debugfs_create_dir(rdev_get_name(rdev), debugfs_root);
-	if (!rdev->debugfs) {
+	if (IS_ERR(rdev->debugfs) || !rdev->debugfs) {
 		rdev_warn(rdev, "Failed to create debugfs directory\n");
-		return;
+		rdev->debugfs = NULL;
+		goto error;
 	}
 
 	debugfs_create_u32("use_count", 0444, rdev->debugfs,
 			   &rdev->use_count);
 	debugfs_create_u32("open_count", 0444, rdev->debugfs,
 			   &rdev->open_count);
+	debugfs_create_file("consumers", 0444, rdev->debugfs, rdev,
+			    &reg_consumers_fops);
+
+	reg = regulator_get(NULL, rdev->desc->name);
+	if (IS_ERR(reg) || reg == NULL) {
+		pr_err("Error-Bad Function Input\n");
+		goto error;
+	}
+
+	reg_ops = rdev->desc->ops;
+	mode = S_IRUGO | S_IWUSR;
+	/* Enabled File */
+	if (mode)
+		err_ptr = debugfs_create_file("enable", mode, rdev->debugfs,
+						reg, &reg_enable_fops);
+	if (IS_ERR(err_ptr)) {
+		pr_err("Error-Could not create enable file\n");
+		debugfs_remove_recursive(rdev->debugfs);
+		goto error;
+	}
+
+	mode = 0;
+	/* Force-Disable File */
+	if (reg_ops->is_enabled)
+		mode |= S_IRUGO;
+	if (reg_ops->enable || reg_ops->disable)
+		mode |= S_IWUSR;
+	if (mode)
+		err_ptr = debugfs_create_file("force_disable", mode,
+					rdev->debugfs, reg, &reg_fdisable_fops);
+	if (IS_ERR(err_ptr)) {
+		pr_err("Error-Could not create force_disable file\n");
+		debugfs_remove_recursive(rdev->debugfs);
+		goto error;
+	}
+
+	mode = 0;
+	/* Voltage File */
+	if (reg_ops->get_voltage)
+		mode |= S_IRUGO;
+	if (reg_ops->set_voltage)
+		mode |= S_IWUSR;
+	if (mode)
+		err_ptr = debugfs_create_file("voltage", mode, rdev->debugfs,
+						reg, &reg_volt_fops);
+	if (IS_ERR(err_ptr)) {
+		pr_err("Error-Could not create voltage file\n");
+		debugfs_remove_recursive(rdev->debugfs);
+		goto error;
+	}
+
+	mode = 0;
+	/* Mode File */
+	if (reg_ops->get_mode)
+		mode |= S_IRUGO;
+	if (reg_ops->set_mode)
+		mode |= S_IWUSR;
+	if (mode)
+		err_ptr = debugfs_create_file("mode", mode, rdev->debugfs,
+						reg, &reg_mode_fops);
+	if (IS_ERR(err_ptr)) {
+		pr_err("Error-Could not create mode file\n");
+		debugfs_remove_recursive(rdev->debugfs);
+		goto error;
+	}
+
+	mode = 0;
+	/* Optimum Mode File */
+	if (reg_ops->get_mode)
+		mode |= S_IRUGO;
+	if (reg_ops->set_mode)
+		mode |= S_IWUSR;
+	if (mode)
+		err_ptr = debugfs_create_file("optimum_mode", mode,
+				rdev->debugfs, reg, &reg_optimum_mode_fops);
+	if (IS_ERR(err_ptr)) {
+		pr_err("Error-Could not create optimum_mode file\n");
+		debugfs_remove_recursive(rdev->debugfs);
+		goto error;
+	}
+
+error:
+	return;
 }
+#else
+static inline void rdev_init_debugfs(struct regulator_dev *rdev)
+{
+	return;
+}
+#endif
 
 /**
  * regulator_register - register regulator
@@ -2962,7 +3381,10 @@
 
 	list_add(&rdev->list, &regulator_list);
 
+	mutex_unlock(&regulator_list_mutex);
 	rdev_init_debugfs(rdev);
+	return rdev;
+
 out:
 	mutex_unlock(&regulator_list_mutex);
 	return rdev;
@@ -3118,6 +3540,22 @@
 EXPORT_SYMBOL_GPL(regulator_use_dummy_regulator);
 
 /**
+ * regulator_suppress_info_printing - disable printing of info messages
+ *
+ * The regulator framework calls print_constraints() when a regulator is
+ * registered.  It also prints a disable message for each unused regulator in
+ * regulator_init_complete().
+ *
+ * Calling this function ensures that such messages do not end up in the
+ * log.
+ */
+void regulator_suppress_info_printing(void)
+{
+	suppress_info_printing = 1;
+}
+EXPORT_SYMBOL_GPL(regulator_suppress_info_printing);
+
+/**
  * rdev_get_drvdata - get rdev regulator driver data
  * @rdev: regulator
  *
@@ -3273,7 +3711,8 @@
 		if (has_full_constraints) {
 			/* We log since this may kill the system if it
 			 * goes wrong. */
-			rdev_info(rdev, "disabling\n");
+			if (!suppress_info_printing)
+				rdev_info(rdev, "disabling\n");
 			ret = ops->disable(rdev);
 			if (ret != 0) {
 				rdev_err(rdev, "couldn't disable: %d\n", ret);
@@ -3284,7 +3723,9 @@
 			 * so warn even if we aren't going to do
 			 * anything here.
 			 */
-			rdev_warn(rdev, "incomplete constraints, leaving on\n");
+			if (!suppress_info_printing)
+				rdev_warn(rdev, "incomplete constraints, "
+						"leaving on\n");
 		}
 
 unlock:
diff --git a/drivers/regulator/msm-gpio-regulator.c b/drivers/regulator/msm-gpio-regulator.c
new file mode 100644
index 0000000..5c99f4c
--- /dev/null
+++ b/drivers/regulator/msm-gpio-regulator.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/msm-gpio-regulator.h>
+
+struct gpio_vreg {
+	struct regulator_desc	desc;
+	struct regulator_dev	*rdev;
+	char			*gpio_label;
+	char			*name;
+	unsigned		gpio;
+	int			active_low;
+	bool			gpio_requested;
+};
+
+static int gpio_vreg_request_gpio(struct gpio_vreg *vreg)
+{
+	int rc = 0;
+
+	/* Request GPIO now if it hasn't been requested before. */
+	if (!vreg->gpio_requested) {
+		rc = gpio_request(vreg->gpio, vreg->gpio_label);
+		if (rc < 0)
+			pr_err("failed to request gpio %u (%s), rc=%d\n",
+				vreg->gpio, vreg->gpio_label, rc);
+		else
+			vreg->gpio_requested = true;
+
+		rc = gpio_sysfs_set_active_low(vreg->gpio, vreg->active_low);
+		if (rc < 0)
+			pr_err("active_low=%d failed for gpio %u, rc=%d\n",
+				vreg->active_low, vreg->gpio, rc);
+	}
+
+	return rc;
+}
+
+static int gpio_vreg_is_enabled(struct regulator_dev *rdev)
+{
+	struct gpio_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc;
+
+	rc = gpio_vreg_request_gpio(vreg);
+	if (rc < 0)
+		return rc;
+
+	return (gpio_get_value_cansleep(vreg->gpio) ? 1 : 0) ^ vreg->active_low;
+}
+
+static int gpio_vreg_enable(struct regulator_dev *rdev)
+{
+	struct gpio_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc;
+
+	rc = gpio_vreg_request_gpio(vreg);
+	if (rc < 0)
+		return rc;
+
+	return gpio_direction_output(vreg->gpio, !vreg->active_low);
+}
+
+static int gpio_vreg_disable(struct regulator_dev *rdev)
+{
+	struct gpio_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc;
+
+	rc = gpio_vreg_request_gpio(vreg);
+	if (rc < 0)
+		return rc;
+
+	return gpio_direction_output(vreg->gpio, vreg->active_low);
+}
+
+static struct regulator_ops gpio_vreg_ops = {
+	.enable		= gpio_vreg_enable,
+	.disable	= gpio_vreg_disable,
+	.is_enabled	= gpio_vreg_is_enabled,
+};
+
+static int __devinit gpio_vreg_probe(struct platform_device *pdev)
+{
+	const struct gpio_regulator_platform_data *pdata;
+	struct gpio_vreg *vreg;
+	int rc = 0;
+
+	pdata = pdev->dev.platform_data;
+
+	if (!pdata) {
+		pr_err("platform data required.\n");
+		return -EINVAL;
+	}
+
+	if (!pdata->gpio_label) {
+		pr_err("gpio_label required.\n");
+		return -EINVAL;
+	}
+
+	if (!pdata->regulator_name) {
+		pr_err("regulator_name required.\n");
+		return -EINVAL;
+	}
+
+	vreg = kzalloc(sizeof(struct gpio_vreg), GFP_KERNEL);
+	if (!vreg) {
+		pr_err("kzalloc failed.\n");
+		return -ENOMEM;
+	}
+
+	vreg->name = kstrdup(pdata->regulator_name, GFP_KERNEL);
+	if (!vreg->name) {
+		pr_err("kzalloc failed.\n");
+		rc = -ENOMEM;
+		goto free_vreg;
+	}
+
+	vreg->gpio_label = kstrdup(pdata->gpio_label, GFP_KERNEL);
+	if (!vreg->gpio_label) {
+		pr_err("kzalloc failed.\n");
+		rc = -ENOMEM;
+		goto free_name;
+	}
+
+	vreg->gpio		= pdata->gpio;
+	vreg->active_low	= (pdata->active_low ? 1 : 0);
+	vreg->gpio_requested	= false;
+
+	vreg->desc.name		= vreg->name;
+	vreg->desc.id		= pdev->id;
+	vreg->desc.ops		= &gpio_vreg_ops;
+	vreg->desc.type		= REGULATOR_VOLTAGE;
+	vreg->desc.owner	= THIS_MODULE;
+
+	vreg->rdev = regulator_register(&vreg->desc, &pdev->dev,
+					&pdata->init_data, vreg, NULL);
+	if (IS_ERR(vreg->rdev)) {
+		rc = PTR_ERR(vreg->rdev);
+		pr_err("%s: regulator_register failed, rc=%d.\n", vreg->name,
+			rc);
+		goto free_gpio_label;
+	}
+
+	platform_set_drvdata(pdev, vreg);
+
+	pr_info("id=%d, name=%s, gpio=%u, gpio_label=%s\n", pdev->id,
+		vreg->name, vreg->gpio, vreg->gpio_label);
+
+	return rc;
+
+free_gpio_label:
+	kfree(vreg->gpio_label);
+free_name:
+	kfree(vreg->name);
+free_vreg:
+	kfree(vreg);
+
+	return rc;
+}
+
+static int __devexit gpio_vreg_remove(struct platform_device *pdev)
+{
+	struct gpio_vreg *vreg = platform_get_drvdata(pdev);
+
+	if (vreg->gpio_requested)
+		gpio_free(vreg->gpio);
+
+	regulator_unregister(vreg->rdev);
+	kfree(vreg->name);
+	kfree(vreg->gpio_label);
+	kfree(vreg);
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver gpio_vreg_driver = {
+	.probe = gpio_vreg_probe,
+	.remove = __devexit_p(gpio_vreg_remove),
+	.driver = {
+		.name = GPIO_REGULATOR_DEV_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init gpio_vreg_init(void)
+{
+	return platform_driver_register(&gpio_vreg_driver);
+}
+
+static void __exit gpio_vreg_exit(void)
+{
+	platform_driver_unregister(&gpio_vreg_driver);
+}
+
+postcore_initcall(gpio_vreg_init);
+module_exit(gpio_vreg_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("GPIO regulator driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" GPIO_REGULATOR_DEV_NAME);
diff --git a/drivers/regulator/pm8058-xo.c b/drivers/regulator/pm8058-xo.c
new file mode 100644
index 0000000..0d57c02
--- /dev/null
+++ b/drivers/regulator/pm8058-xo.c
@@ -0,0 +1,214 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/mfd/pmic8058.h>
+#include <linux/regulator/driver.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/regulator/pm8058-xo.h>
+
+/* XO buffer masks and values */
+
+#define XO_PULLDOWN_MASK	0x08
+#define XO_PULLDOWN_ENABLE	0x08
+#define XO_PULLDOWN_DISABLE	0x00
+
+#define XO_BUFFER_MASK		0x04
+#define XO_BUFFER_ENABLE	0x04
+#define XO_BUFFER_DISABLE	0x00
+
+#define XO_MODE_MASK		0x01
+#define XO_MODE_MANUAL		0x00
+
+#define XO_ENABLE_MASK		(XO_MODE_MASK | XO_BUFFER_MASK)
+#define XO_ENABLE		(XO_MODE_MANUAL | XO_BUFFER_ENABLE)
+#define XO_DISABLE		(XO_MODE_MANUAL | XO_BUFFER_DISABLE)
+
+struct pm8058_xo_buffer {
+	struct device			*dev;
+	struct pm8058_xo_pdata		*pdata;
+	struct regulator_dev		*rdev;
+	u16				ctrl_addr;
+	u8				ctrl_reg;
+};
+
+#define XO_BUFFER(_id, _ctrl_addr) \
+	[PM8058_XO_ID_##_id] = { \
+		.ctrl_addr = _ctrl_addr, \
+	}
+
+static struct pm8058_xo_buffer pm8058_xo_buffer[] = {
+	XO_BUFFER(A0, 0x185),
+	XO_BUFFER(A1, 0x186),
+};
+
+static int pm8058_xo_buffer_write(struct pm8058_xo_buffer *xo,
+		u16 addr, u8 val, u8 mask, u8 *reg_save)
+{
+	u8	reg;
+	int	rc = 0;
+
+	reg = (*reg_save & ~mask) | (val & mask);
+	if (reg != *reg_save)
+		rc = pm8xxx_writeb(xo->dev->parent, addr, reg);
+
+	if (rc)
+		pr_err("FAIL: pm8xxx_write: rc=%d\n", rc);
+	else
+		*reg_save = reg;
+	return rc;
+}
+
+static int pm8058_xo_buffer_enable(struct regulator_dev *dev)
+{
+	struct pm8058_xo_buffer *xo = rdev_get_drvdata(dev);
+	int rc;
+
+	rc = pm8058_xo_buffer_write(xo, xo->ctrl_addr, XO_ENABLE,
+				    XO_ENABLE_MASK, &xo->ctrl_reg);
+	if (rc)
+		pr_err("FAIL: pm8058_xo_buffer_write: rc=%d\n", rc);
+
+	return rc;
+}
+
+static int pm8058_xo_buffer_is_enabled(struct regulator_dev *dev)
+{
+	struct pm8058_xo_buffer *xo = rdev_get_drvdata(dev);
+
+	if (xo->ctrl_reg & XO_BUFFER_ENABLE)
+		return 1;
+	else
+		return 0;
+}
+
+static int pm8058_xo_buffer_disable(struct regulator_dev *dev)
+{
+	struct pm8058_xo_buffer *xo = rdev_get_drvdata(dev);
+	int rc;
+
+	rc = pm8058_xo_buffer_write(xo, xo->ctrl_addr, XO_DISABLE,
+				    XO_ENABLE_MASK, &xo->ctrl_reg);
+	if (rc)
+		pr_err("FAIL: pm8058_xo_buffer_write: rc=%d\n", rc);
+
+	return rc;
+}
+
+static struct regulator_ops pm8058_xo_ops = {
+	.enable = pm8058_xo_buffer_enable,
+	.disable = pm8058_xo_buffer_disable,
+	.is_enabled = pm8058_xo_buffer_is_enabled,
+};
+
+#define VREG_DESCRIP(_id, _name, _ops) \
+	[_id] = { \
+		.id = _id, \
+		.name = _name, \
+		.ops = _ops, \
+		.type = REGULATOR_VOLTAGE, \
+		.owner = THIS_MODULE, \
+	}
+
+static struct regulator_desc pm8058_xo_buffer_desc[] = {
+	VREG_DESCRIP(PM8058_XO_ID_A0, "8058_xo_a0", &pm8058_xo_ops),
+	VREG_DESCRIP(PM8058_XO_ID_A1, "8058_xo_a1", &pm8058_xo_ops),
+};
+
+static int pm8058_init_xo_buffer(struct pm8058_xo_buffer *xo)
+{
+	int	rc;
+
+	/* Save the current control register state */
+	rc = pm8xxx_readb(xo->dev->parent, xo->ctrl_addr, &xo->ctrl_reg);
+
+	if (rc)
+		pr_err("FAIL: pm8xxx_read: rc=%d\n", rc);
+	return rc;
+}
+
+static int __devinit pm8058_xo_buffer_probe(struct platform_device *pdev)
+{
+	struct regulator_desc *rdesc;
+	struct pm8058_xo_buffer *xo;
+	int rc = 0;
+
+	if (pdev == NULL)
+		return -EINVAL;
+
+	if (pdev->id >= 0 && pdev->id < PM8058_XO_ID_MAX) {
+		rdesc = &pm8058_xo_buffer_desc[pdev->id];
+		xo = &pm8058_xo_buffer[pdev->id];
+		xo->pdata = pdev->dev.platform_data;
+		xo->dev  = &pdev->dev;
+
+		rc = pm8058_init_xo_buffer(xo);
+		if (rc)
+			goto bail;
+
+		xo->rdev = regulator_register(rdesc, &pdev->dev,
+					&xo->pdata->init_data, xo, NULL);
+		if (IS_ERR(xo->rdev)) {
+			rc = PTR_ERR(xo->rdev);
+			pr_err("FAIL: regulator_register(%s): rc=%d\n",
+				pm8058_xo_buffer_desc[pdev->id].name, rc);
+		}
+	} else {
+		rc = -ENODEV;
+	}
+
+bail:
+	if (rc)
+		pr_err("Error: xo-id=%d, rc=%d\n", pdev->id, rc);
+
+	return rc;
+}
+
+static int __devexit pm8058_xo_buffer_remove(struct platform_device *pdev)
+{
+	regulator_unregister(pm8058_xo_buffer[pdev->id].rdev);
+	return 0;
+}
+
+static struct platform_driver pm8058_xo_buffer_driver = {
+	.probe = pm8058_xo_buffer_probe,
+	.remove = __devexit_p(pm8058_xo_buffer_remove),
+	.driver = {
+		.name = PM8058_XO_BUFFER_DEV_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init pm8058_xo_buffer_init(void)
+{
+	return platform_driver_register(&pm8058_xo_buffer_driver);
+}
+
+static void __exit pm8058_xo_buffer_exit(void)
+{
+	platform_driver_unregister(&pm8058_xo_buffer_driver);
+}
+
+subsys_initcall(pm8058_xo_buffer_init);
+module_exit(pm8058_xo_buffer_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC8058 XO buffer driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8058_XO_BUFFER_DEV_NAME);
diff --git a/drivers/regulator/pm8xxx-regulator.c b/drivers/regulator/pm8xxx-regulator.c
new file mode 100644
index 0000000..833c513
--- /dev/null
+++ b/drivers/regulator/pm8xxx-regulator.c
@@ -0,0 +1,3281 @@
+/*
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/regulator.h>
+
+/* Debug Flag Definitions */
+enum {
+	PM8XXX_VREG_DEBUG_REQUEST	= BIT(0),
+	PM8XXX_VREG_DEBUG_DUPLICATE	= BIT(1),
+	PM8XXX_VREG_DEBUG_INIT		= BIT(2),
+	PM8XXX_VREG_DEBUG_WRITES	= BIT(3), /* SSBI writes */
+};
+
+static int pm8xxx_vreg_debug_mask;
+module_param_named(
+	debug_mask, pm8xxx_vreg_debug_mask, int, S_IRUSR | S_IWUSR
+);
+
+/* Common Masks */
+#define REGULATOR_ENABLE_MASK		0x80
+#define REGULATOR_ENABLE		0x80
+#define REGULATOR_DISABLE		0x00
+
+#define REGULATOR_BANK_MASK		0xF0
+#define REGULATOR_BANK_SEL(n)		((n) << 4)
+#define REGULATOR_BANK_WRITE		0x80
+
+#define LDO_TEST_BANKS			7
+#define NLDO1200_TEST_BANKS		5
+#define SMPS_TEST_BANKS			8
+
+/*
+ * This voltage in uV is returned by get_voltage functions when there is no way
+ * to determine the current voltage level.  It is needed because the regulator
+ * framework treats a 0 uV voltage as an error.
+ */
+#define VOLTAGE_UNKNOWN			1
+
+/* LDO masks and values */
+
+/* CTRL register */
+#define LDO_ENABLE_MASK			0x80
+#define LDO_DISABLE			0x00
+#define LDO_ENABLE			0x80
+#define LDO_PULL_DOWN_ENABLE_MASK	0x40
+#define LDO_PULL_DOWN_ENABLE		0x40
+
+#define LDO_CTRL_PM_MASK		0x20
+#define LDO_CTRL_PM_HPM			0x00
+#define LDO_CTRL_PM_LPM			0x20
+
+#define LDO_CTRL_VPROG_MASK		0x1F
+
+/* TEST register bank 0 */
+#define LDO_TEST_LPM_MASK		0x04
+#define LDO_TEST_LPM_SEL_CTRL		0x00
+#define LDO_TEST_LPM_SEL_TCXO		0x04
+
+/* TEST register bank 2 */
+#define LDO_TEST_VPROG_UPDATE_MASK	0x08
+#define LDO_TEST_RANGE_SEL_MASK		0x04
+#define LDO_TEST_FINE_STEP_MASK		0x02
+#define LDO_TEST_FINE_STEP_SHIFT	1
+
+/* TEST register bank 4 */
+#define LDO_TEST_RANGE_EXT_MASK		0x01
+
+/* TEST register bank 5 */
+#define LDO_TEST_PIN_CTRL_MASK		0x0F
+#define LDO_TEST_PIN_CTRL_EN3		0x08
+#define LDO_TEST_PIN_CTRL_EN2		0x04
+#define LDO_TEST_PIN_CTRL_EN1		0x02
+#define LDO_TEST_PIN_CTRL_EN0		0x01
+
+/* TEST register bank 6 */
+#define LDO_TEST_PIN_CTRL_LPM_MASK	0x0F
+
+/*
+ * If a given voltage could be output by two ranges, then the preferred one must
+ * be determined by the range limits.  Specified voltage ranges should must
+ * not overlap.
+ *
+ * Allowable voltage ranges:
+ */
+#define PLDO_LOW_UV_MIN			750000
+#define PLDO_LOW_UV_MAX			1487500
+#define PLDO_LOW_UV_FINE_STEP		12500
+
+#define PLDO_NORM_UV_MIN		1500000
+#define PLDO_NORM_UV_MAX		3075000
+#define PLDO_NORM_UV_FINE_STEP		25000
+
+#define PLDO_HIGH_UV_MIN		1750000
+#define PLDO_HIGH_UV_SET_POINT_MIN	3100000
+#define PLDO_HIGH_UV_MAX		4900000
+#define PLDO_HIGH_UV_FINE_STEP		50000
+
+#define PLDO_LOW_SET_POINTS		((PLDO_LOW_UV_MAX - PLDO_LOW_UV_MIN) \
+						/ PLDO_LOW_UV_FINE_STEP + 1)
+#define PLDO_NORM_SET_POINTS		((PLDO_NORM_UV_MAX - PLDO_NORM_UV_MIN) \
+						/ PLDO_NORM_UV_FINE_STEP + 1)
+#define PLDO_HIGH_SET_POINTS		((PLDO_HIGH_UV_MAX \
+						- PLDO_HIGH_UV_SET_POINT_MIN) \
+					/ PLDO_HIGH_UV_FINE_STEP + 1)
+#define PLDO_SET_POINTS			(PLDO_LOW_SET_POINTS \
+						+ PLDO_NORM_SET_POINTS \
+						+ PLDO_HIGH_SET_POINTS)
+
+#define NLDO_UV_MIN			750000
+#define NLDO_UV_MAX			1537500
+#define NLDO_UV_FINE_STEP		12500
+
+#define NLDO_SET_POINTS			((NLDO_UV_MAX - NLDO_UV_MIN) \
+						/ NLDO_UV_FINE_STEP + 1)
+
+/* NLDO1200 masks and values */
+
+/* CTRL register */
+#define NLDO1200_ENABLE_MASK		0x80
+#define NLDO1200_DISABLE		0x00
+#define NLDO1200_ENABLE			0x80
+
+/* Legacy mode */
+#define NLDO1200_LEGACY_PM_MASK		0x20
+#define NLDO1200_LEGACY_PM_HPM		0x00
+#define NLDO1200_LEGACY_PM_LPM		0x20
+
+/* Advanced mode */
+#define NLDO1200_CTRL_RANGE_MASK	0x40
+#define NLDO1200_CTRL_RANGE_HIGH	0x00
+#define NLDO1200_CTRL_RANGE_LOW		0x40
+#define NLDO1200_CTRL_VPROG_MASK	0x3F
+
+#define NLDO1200_LOW_UV_MIN		375000
+#define NLDO1200_LOW_UV_MAX		743750
+#define NLDO1200_LOW_UV_STEP		6250
+
+#define NLDO1200_HIGH_UV_MIN		750000
+#define NLDO1200_HIGH_UV_MAX		1537500
+#define NLDO1200_HIGH_UV_STEP		12500
+
+#define NLDO1200_LOW_SET_POINTS		((NLDO1200_LOW_UV_MAX \
+						- NLDO1200_LOW_UV_MIN) \
+					/ NLDO1200_LOW_UV_STEP + 1)
+#define NLDO1200_HIGH_SET_POINTS	((NLDO1200_HIGH_UV_MAX \
+						- NLDO1200_HIGH_UV_MIN) \
+					/ NLDO1200_HIGH_UV_STEP + 1)
+#define NLDO1200_SET_POINTS		(NLDO1200_LOW_SET_POINTS \
+						+ NLDO1200_HIGH_SET_POINTS)
+
+/* TEST register bank 0 */
+#define NLDO1200_TEST_LPM_MASK		0x04
+#define NLDO1200_TEST_LPM_SEL_CTRL	0x00
+#define NLDO1200_TEST_LPM_SEL_TCXO	0x04
+
+/* TEST register bank 1 */
+#define NLDO1200_PULL_DOWN_ENABLE_MASK	0x02
+#define NLDO1200_PULL_DOWN_ENABLE	0x02
+
+/* TEST register bank 2 */
+#define NLDO1200_ADVANCED_MODE_MASK	0x08
+#define NLDO1200_ADVANCED_MODE		0x00
+#define NLDO1200_LEGACY_MODE		0x08
+
+/* Advanced mode power mode control */
+#define NLDO1200_ADVANCED_PM_MASK	0x02
+#define NLDO1200_ADVANCED_PM_HPM	0x00
+#define NLDO1200_ADVANCED_PM_LPM	0x02
+
+#define NLDO1200_IN_ADVANCED_MODE(vreg) \
+	((vreg->test_reg[2] & NLDO1200_ADVANCED_MODE_MASK) \
+	 == NLDO1200_ADVANCED_MODE)
+
+/* SMPS masks and values */
+
+/* CTRL register */
+
+/* Legacy mode */
+#define SMPS_LEGACY_ENABLE_MASK		0x80
+#define SMPS_LEGACY_DISABLE		0x00
+#define SMPS_LEGACY_ENABLE		0x80
+#define SMPS_LEGACY_PULL_DOWN_ENABLE	0x40
+#define SMPS_LEGACY_VREF_SEL_MASK	0x20
+#define SMPS_LEGACY_VPROG_MASK		0x1F
+
+/* Advanced mode */
+#define SMPS_ADVANCED_BAND_MASK		0xC0
+#define SMPS_ADVANCED_BAND_OFF		0x00
+#define SMPS_ADVANCED_BAND_1		0x40
+#define SMPS_ADVANCED_BAND_2		0x80
+#define SMPS_ADVANCED_BAND_3		0xC0
+#define SMPS_ADVANCED_VPROG_MASK	0x3F
+
+/* Legacy mode voltage ranges */
+#define SMPS_MODE3_UV_MIN		375000
+#define SMPS_MODE3_UV_MAX		725000
+#define SMPS_MODE3_UV_STEP		25000
+
+#define SMPS_MODE2_UV_MIN		750000
+#define SMPS_MODE2_UV_MAX		1475000
+#define SMPS_MODE2_UV_STEP		25000
+
+#define SMPS_MODE1_UV_MIN		1500000
+#define SMPS_MODE1_UV_MAX		3050000
+#define SMPS_MODE1_UV_STEP		50000
+
+#define SMPS_MODE3_SET_POINTS		((SMPS_MODE3_UV_MAX \
+						- SMPS_MODE3_UV_MIN) \
+					/ SMPS_MODE3_UV_STEP + 1)
+#define SMPS_MODE2_SET_POINTS		((SMPS_MODE2_UV_MAX \
+						- SMPS_MODE2_UV_MIN) \
+					/ SMPS_MODE2_UV_STEP + 1)
+#define SMPS_MODE1_SET_POINTS		((SMPS_MODE1_UV_MAX \
+						- SMPS_MODE1_UV_MIN) \
+					/ SMPS_MODE1_UV_STEP + 1)
+#define SMPS_LEGACY_SET_POINTS		(SMPS_MODE3_SET_POINTS \
+						+ SMPS_MODE2_SET_POINTS \
+						+ SMPS_MODE1_SET_POINTS)
+
+/* Advanced mode voltage ranges */
+#define SMPS_BAND1_UV_MIN		375000
+#define SMPS_BAND1_UV_MAX		737500
+#define SMPS_BAND1_UV_STEP		12500
+
+#define SMPS_BAND2_UV_MIN		750000
+#define SMPS_BAND2_UV_MAX		1487500
+#define SMPS_BAND2_UV_STEP		12500
+
+#define SMPS_BAND3_UV_MIN		1500000
+#define SMPS_BAND3_UV_MAX		3075000
+#define SMPS_BAND3_UV_STEP		25000
+
+#define SMPS_BAND1_SET_POINTS		((SMPS_BAND1_UV_MAX \
+						- SMPS_BAND1_UV_MIN) \
+					/ SMPS_BAND1_UV_STEP + 1)
+#define SMPS_BAND2_SET_POINTS		((SMPS_BAND2_UV_MAX \
+						- SMPS_BAND2_UV_MIN) \
+					/ SMPS_BAND2_UV_STEP + 1)
+#define SMPS_BAND3_SET_POINTS		((SMPS_BAND3_UV_MAX \
+						- SMPS_BAND3_UV_MIN) \
+					/ SMPS_BAND3_UV_STEP + 1)
+#define SMPS_ADVANCED_SET_POINTS	(SMPS_BAND1_SET_POINTS \
+						+ SMPS_BAND2_SET_POINTS \
+						+ SMPS_BAND3_SET_POINTS)
+
+/* Test2 register bank 1 */
+#define SMPS_LEGACY_VLOW_SEL_MASK	0x01
+
+/* Test2 register bank 6 */
+#define SMPS_ADVANCED_PULL_DOWN_ENABLE	0x08
+
+/* Test2 register bank 7 */
+#define SMPS_ADVANCED_MODE_MASK		0x02
+#define SMPS_ADVANCED_MODE		0x02
+#define SMPS_LEGACY_MODE		0x00
+
+#define SMPS_IN_ADVANCED_MODE(vreg) \
+	((vreg->test_reg[7] & SMPS_ADVANCED_MODE_MASK) == SMPS_ADVANCED_MODE)
+
+/* BUCK_SLEEP_CNTRL register */
+#define SMPS_PIN_CTRL_MASK		0xF0
+#define SMPS_PIN_CTRL_EN3		0x80
+#define SMPS_PIN_CTRL_EN2		0x40
+#define SMPS_PIN_CTRL_EN1		0x20
+#define SMPS_PIN_CTRL_EN0		0x10
+
+#define SMPS_PIN_CTRL_LPM_MASK		0x0F
+#define SMPS_PIN_CTRL_LPM_EN3		0x08
+#define SMPS_PIN_CTRL_LPM_EN2		0x04
+#define SMPS_PIN_CTRL_LPM_EN1		0x02
+#define SMPS_PIN_CTRL_LPM_EN0		0x01
+
+/* BUCK_CLOCK_CNTRL register */
+#define SMPS_CLK_DIVIDE2		0x40
+
+#define SMPS_CLK_CTRL_MASK		0x30
+#define SMPS_CLK_CTRL_FOLLOW_TCXO	0x00
+#define SMPS_CLK_CTRL_PWM		0x10
+#define SMPS_CLK_CTRL_PFM		0x20
+
+/* FTSMPS masks and values */
+
+/* CTRL register */
+#define FTSMPS_VCTRL_BAND_MASK		0xC0
+#define FTSMPS_VCTRL_BAND_OFF		0x00
+#define FTSMPS_VCTRL_BAND_1		0x40
+#define FTSMPS_VCTRL_BAND_2		0x80
+#define FTSMPS_VCTRL_BAND_3		0xC0
+#define FTSMPS_VCTRL_VPROG_MASK		0x3F
+
+#define FTSMPS_BAND1_UV_MIN		350000
+#define FTSMPS_BAND1_UV_MAX		650000
+/* 3 LSB's of program voltage must be 0 in band 1. */
+/* Logical step size */
+#define FTSMPS_BAND1_UV_LOG_STEP	50000
+/* Physical step size */
+#define FTSMPS_BAND1_UV_PHYS_STEP	6250
+
+#define FTSMPS_BAND2_UV_MIN		700000
+#define FTSMPS_BAND2_UV_MAX		1400000
+#define FTSMPS_BAND2_UV_STEP		12500
+
+#define FTSMPS_BAND3_UV_MIN		1400000
+#define FTSMPS_BAND3_UV_SET_POINT_MIN	1500000
+#define FTSMPS_BAND3_UV_MAX		3300000
+#define FTSMPS_BAND3_UV_STEP		50000
+
+#define FTSMPS_BAND1_SET_POINTS		((FTSMPS_BAND1_UV_MAX \
+						- FTSMPS_BAND1_UV_MIN) \
+					/ FTSMPS_BAND1_UV_LOG_STEP + 1)
+#define FTSMPS_BAND2_SET_POINTS		((FTSMPS_BAND2_UV_MAX \
+						- FTSMPS_BAND2_UV_MIN) \
+					/ FTSMPS_BAND2_UV_STEP + 1)
+#define FTSMPS_BAND3_SET_POINTS		((FTSMPS_BAND3_UV_MAX \
+					  - FTSMPS_BAND3_UV_SET_POINT_MIN) \
+					/ FTSMPS_BAND3_UV_STEP + 1)
+#define FTSMPS_SET_POINTS		(FTSMPS_BAND1_SET_POINTS \
+						+ FTSMPS_BAND2_SET_POINTS \
+						+ FTSMPS_BAND3_SET_POINTS)
+
+/* FTS_CNFG1 register bank 0 */
+#define FTSMPS_CNFG1_PM_MASK		0x0C
+#define FTSMPS_CNFG1_PM_PWM		0x00
+#define FTSMPS_CNFG1_PM_PFM		0x08
+
+/* PWR_CNFG register */
+#define FTSMPS_PULL_DOWN_ENABLE_MASK	0x40
+#define FTSMPS_PULL_DOWN_ENABLE		0x40
+
+/* VS masks and values */
+
+/* CTRL register */
+#define VS_ENABLE_MASK			0x80
+#define VS_DISABLE			0x00
+#define VS_ENABLE			0x80
+#define VS_PULL_DOWN_ENABLE_MASK	0x40
+#define VS_PULL_DOWN_DISABLE		0x40
+#define VS_PULL_DOWN_ENABLE		0x00
+
+#define VS_MODE_MASK			0x30
+#define VS_MODE_NORMAL			0x10
+#define VS_MODE_LPM			0x20
+
+#define VS_PIN_CTRL_MASK		0x0F
+#define VS_PIN_CTRL_EN0			0x08
+#define VS_PIN_CTRL_EN1			0x04
+#define VS_PIN_CTRL_EN2			0x02
+#define VS_PIN_CTRL_EN3			0x01
+
+/* TEST register */
+#define VS_OCP_MASK			0x10
+#define VS_OCP_ENABLE			0x00
+#define VS_OCP_DISABLE			0x10
+
+/* VS300 masks and values */
+
+/* CTRL register */
+#define VS300_CTRL_ENABLE_MASK		0xC0
+#define VS300_CTRL_DISABLE		0x00
+#define VS300_CTRL_ENABLE		0x40
+
+#define VS300_PULL_DOWN_ENABLE_MASK	0x20
+#define VS300_PULL_DOWN_ENABLE		0x20
+
+#define VS300_MODE_MASK			0x18
+#define VS300_MODE_NORMAL		0x00
+#define VS300_MODE_LPM			0x08
+
+/* NCP masks and values */
+
+/* CTRL register */
+#define NCP_ENABLE_MASK			0x80
+#define NCP_DISABLE			0x00
+#define NCP_ENABLE			0x80
+#define NCP_VPROG_MASK			0x1F
+
+#define NCP_UV_MIN			1500000
+#define NCP_UV_MAX			3050000
+#define NCP_UV_STEP			50000
+
+#define NCP_SET_POINTS			((NCP_UV_MAX - NCP_UV_MIN) \
+						/ NCP_UV_STEP + 1)
+
+/* Boost masks and values */
+#define BOOST_ENABLE_MASK		0x80
+#define BOOST_DISABLE			0x00
+#define BOOST_ENABLE			0x80
+#define BOOST_VPROG_MASK		0x1F
+
+#define BOOST_UV_MIN			4000000
+#define BOOST_UV_MAX			5550000
+#define BOOST_UV_STEP			50000
+
+#define BOOST_SET_POINTS		((BOOST_UV_MAX - BOOST_UV_MIN) \
+						/ BOOST_UV_STEP + 1)
+
+#define vreg_err(vreg, fmt, ...) \
+	pr_err("%s: " fmt, vreg->rdesc.name, ##__VA_ARGS__)
+
+/* Determines which label to add to the print. */
+enum pm8xxx_regulator_action {
+	PM8XXX_REGULATOR_ACTION_INIT,
+	PM8XXX_REGULATOR_ACTION_ENABLE,
+	PM8XXX_REGULATOR_ACTION_DISABLE,
+	PM8XXX_REGULATOR_ACTION_VOLTAGE,
+	PM8XXX_REGULATOR_ACTION_MODE,
+	PM8XXX_REGULATOR_ACTION_PIN_CTRL,
+};
+
+/* Debug state printing */
+static void pm8xxx_vreg_show_state(struct regulator_dev *rdev,
+				   enum pm8xxx_regulator_action action);
+
+/*
+ * Perform a masked write to a PMIC register only if the new value differs
+ * from the last value written to the register.  This removes redundant
+ * register writing.
+ *
+ * No locking is required because registers are not shared between regulators.
+ */
+static int pm8xxx_vreg_masked_write(struct pm8xxx_vreg *vreg, u16 addr, u8 val,
+		u8 mask, u8 *reg_save)
+{
+	int rc = 0;
+	u8 reg;
+
+	reg = (*reg_save & ~mask) | (val & mask);
+	if (reg != *reg_save) {
+		rc = pm8xxx_writeb(vreg->dev->parent, addr, reg);
+
+		if (rc) {
+			pr_err("%s: pm8xxx_writeb failed; addr=0x%03X, rc=%d\n",
+				vreg->rdesc.name, addr, rc);
+		} else {
+			*reg_save = reg;
+			vreg->write_count++;
+			if (pm8xxx_vreg_debug_mask & PM8XXX_VREG_DEBUG_WRITES)
+				pr_info("%s: write(0x%03X)=0x%02X\n",
+					vreg->rdesc.name, addr, reg);
+		}
+	}
+
+	return rc;
+}
+
+/*
+ * Perform a masked write to a PMIC register without checking the previously
+ * written value.  This is needed for registers that must be rewritten even if
+ * the value hasn't changed in order for changes in other registers to take
+ * effect.
+ */
+static int pm8xxx_vreg_masked_write_forced(struct pm8xxx_vreg *vreg, u16 addr,
+		u8 val, u8 mask, u8 *reg_save)
+{
+	int rc = 0;
+	u8 reg;
+
+	reg = (*reg_save & ~mask) | (val & mask);
+	rc = pm8xxx_writeb(vreg->dev->parent, addr, reg);
+
+	if (rc) {
+		pr_err("%s: pm8xxx_writeb failed; addr=0x%03X, rc=%d\n",
+			vreg->rdesc.name, addr, rc);
+	} else {
+		*reg_save = reg;
+		vreg->write_count++;
+		if (pm8xxx_vreg_debug_mask & PM8XXX_VREG_DEBUG_WRITES)
+			pr_info("%s: write(0x%03X)=0x%02X\n", vreg->rdesc.name,
+				addr, reg);
+	}
+
+	return rc;
+}
+
+static int pm8xxx_vreg_is_pin_controlled(struct pm8xxx_vreg *vreg)
+{
+	int ret = 0;
+
+	switch (vreg->type) {
+	case PM8XXX_REGULATOR_TYPE_PLDO:
+	case PM8XXX_REGULATOR_TYPE_NLDO:
+		ret = ((vreg->test_reg[5] & LDO_TEST_PIN_CTRL_MASK) << 4)
+			| (vreg->test_reg[6] & LDO_TEST_PIN_CTRL_LPM_MASK);
+		break;
+	case PM8XXX_REGULATOR_TYPE_SMPS:
+		ret = vreg->sleep_ctrl_reg
+			& (SMPS_PIN_CTRL_MASK | SMPS_PIN_CTRL_LPM_MASK);
+		break;
+	case PM8XXX_REGULATOR_TYPE_VS:
+		ret = vreg->ctrl_reg & VS_PIN_CTRL_MASK;
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+/*
+ * Returns the logical pin control enable state because the pin control options
+ * present in the hardware out of restart could be different from those desired
+ * by the consumer.
+ */
+static int pm8xxx_vreg_pin_control_is_enabled(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int enabled;
+
+	mutex_lock(&vreg->pc_lock);
+	enabled = vreg->is_enabled_pc;
+	mutex_unlock(&vreg->pc_lock);
+
+	return enabled;
+}
+
+/* Returns the physical enable state of the regulator. */
+static int _pm8xxx_vreg_is_enabled(struct pm8xxx_vreg *vreg)
+{
+	int rc = 0;
+
+	/*
+	 * All regulator types except advanced mode SMPS, FTSMPS, and VS300 have
+	 * enable bit in bit 7 of the control register.
+	 */
+	switch (vreg->type) {
+	case PM8XXX_REGULATOR_TYPE_FTSMPS:
+		if ((vreg->ctrl_reg & FTSMPS_VCTRL_BAND_MASK)
+		    != FTSMPS_VCTRL_BAND_OFF)
+			rc = 1;
+		break;
+	case PM8XXX_REGULATOR_TYPE_VS300:
+		if ((vreg->ctrl_reg & VS300_CTRL_ENABLE_MASK)
+		    != VS300_CTRL_DISABLE)
+			rc = 1;
+		break;
+	case PM8XXX_REGULATOR_TYPE_SMPS:
+		if (SMPS_IN_ADVANCED_MODE(vreg)) {
+			if ((vreg->ctrl_reg & SMPS_ADVANCED_BAND_MASK)
+			    != SMPS_ADVANCED_BAND_OFF)
+				rc = 1;
+			break;
+		}
+		/* Fall through for legacy mode SMPS. */
+	default:
+		if ((vreg->ctrl_reg & REGULATOR_ENABLE_MASK)
+		    == REGULATOR_ENABLE)
+			rc = 1;
+	}
+
+	return rc;
+}
+
+/*
+ * Returns the logical enable state of the regulator which may be different from
+ * the physical enable state thanks to HPM/LPM pin control.
+ */
+static int pm8xxx_vreg_is_enabled(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int enabled;
+
+	if (vreg->type == PM8XXX_REGULATOR_TYPE_PLDO
+	    || vreg->type == PM8XXX_REGULATOR_TYPE_NLDO
+	    || vreg->type == PM8XXX_REGULATOR_TYPE_SMPS
+	    || vreg->type == PM8XXX_REGULATOR_TYPE_VS) {
+		/* Pin controllable */
+		mutex_lock(&vreg->pc_lock);
+		enabled = vreg->is_enabled;
+		mutex_unlock(&vreg->pc_lock);
+	} else {
+		/* Not pin controlable */
+		enabled = _pm8xxx_vreg_is_enabled(vreg);
+	}
+
+	return enabled;
+}
+
+static int pm8xxx_pldo_get_voltage(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int vmin, fine_step;
+	u8 range_ext, range_sel, vprog, fine_step_reg;
+
+	mutex_lock(&vreg->pc_lock);
+
+	fine_step_reg = vreg->test_reg[2] & LDO_TEST_FINE_STEP_MASK;
+	range_sel = vreg->test_reg[2] & LDO_TEST_RANGE_SEL_MASK;
+	range_ext = vreg->test_reg[4] & LDO_TEST_RANGE_EXT_MASK;
+	vprog = vreg->ctrl_reg & LDO_CTRL_VPROG_MASK;
+
+	mutex_unlock(&vreg->pc_lock);
+
+	vprog = (vprog << 1) | (fine_step_reg >> LDO_TEST_FINE_STEP_SHIFT);
+
+	if (range_sel) {
+		/* low range mode */
+		fine_step = PLDO_LOW_UV_FINE_STEP;
+		vmin = PLDO_LOW_UV_MIN;
+	} else if (!range_ext) {
+		/* normal mode */
+		fine_step = PLDO_NORM_UV_FINE_STEP;
+		vmin = PLDO_NORM_UV_MIN;
+	} else {
+		/* high range mode */
+		fine_step = PLDO_HIGH_UV_FINE_STEP;
+		vmin = PLDO_HIGH_UV_MIN;
+	}
+
+	return fine_step * vprog + vmin;
+}
+
+static int pm8xxx_pldo_list_voltage(struct regulator_dev *rdev,
+				    unsigned selector)
+{
+	int uV;
+
+	if (selector >= PLDO_SET_POINTS)
+		return 0;
+
+	if (selector < PLDO_LOW_SET_POINTS)
+		uV = selector * PLDO_LOW_UV_FINE_STEP + PLDO_LOW_UV_MIN;
+	else if (selector < (PLDO_LOW_SET_POINTS + PLDO_NORM_SET_POINTS))
+		uV = (selector - PLDO_LOW_SET_POINTS) * PLDO_NORM_UV_FINE_STEP
+			+ PLDO_NORM_UV_MIN;
+	else
+		uV = (selector - PLDO_LOW_SET_POINTS - PLDO_NORM_SET_POINTS)
+				* PLDO_HIGH_UV_FINE_STEP
+			+ PLDO_HIGH_UV_SET_POINT_MIN;
+
+	return uV;
+}
+
+static int pm8xxx_pldo_set_voltage(struct regulator_dev *rdev, int min_uV,
+				   int max_uV, unsigned *selector)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc = 0, uV = min_uV;
+	int vmin;
+	unsigned vprog, fine_step;
+	u8 range_ext, range_sel, fine_step_reg, prev_reg;
+	bool reg_changed = false;
+
+	if (uV < PLDO_LOW_UV_MIN && max_uV >= PLDO_LOW_UV_MIN)
+		uV = PLDO_LOW_UV_MIN;
+
+	if (uV < PLDO_LOW_UV_MIN || uV > PLDO_HIGH_UV_MAX) {
+		vreg_err(vreg,
+			"request v=[%d, %d] is outside possible v=[%d, %d]\n",
+			 min_uV, max_uV, PLDO_LOW_UV_MIN, PLDO_HIGH_UV_MAX);
+		return -EINVAL;
+	}
+
+	if (uV > PLDO_NORM_UV_MAX) {
+		vmin = PLDO_HIGH_UV_MIN;
+		fine_step = PLDO_HIGH_UV_FINE_STEP;
+		range_ext = LDO_TEST_RANGE_EXT_MASK;
+		range_sel = 0;
+	} else if (uV > PLDO_LOW_UV_MAX) {
+		vmin = PLDO_NORM_UV_MIN;
+		fine_step = PLDO_NORM_UV_FINE_STEP;
+		range_ext = 0;
+		range_sel = 0;
+	} else {
+		vmin = PLDO_LOW_UV_MIN;
+		fine_step = PLDO_LOW_UV_FINE_STEP;
+		range_ext = 0;
+		range_sel = LDO_TEST_RANGE_SEL_MASK;
+	}
+
+	vprog = (uV - vmin + fine_step - 1) / fine_step;
+	uV = vprog * fine_step + vmin;
+	fine_step_reg = (vprog & 1) << LDO_TEST_FINE_STEP_SHIFT;
+	vprog >>= 1;
+
+	if (uV > max_uV) {
+		vreg_err(vreg,
+			"request v=[%d, %d] cannot be met by any set point\n",
+			min_uV, max_uV);
+		return -EINVAL;
+	}
+
+	mutex_lock(&vreg->pc_lock);
+
+	/* Write fine step, range select and program voltage update. */
+	prev_reg = vreg->test_reg[2];
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+			fine_step_reg | range_sel | REGULATOR_BANK_SEL(2)
+			 | REGULATOR_BANK_WRITE | LDO_TEST_VPROG_UPDATE_MASK,
+			LDO_TEST_FINE_STEP_MASK | LDO_TEST_RANGE_SEL_MASK
+			 | REGULATOR_BANK_MASK | LDO_TEST_VPROG_UPDATE_MASK,
+			&vreg->test_reg[2]);
+	if (rc)
+		goto bail;
+	if (prev_reg != vreg->test_reg[2])
+		reg_changed = true;
+
+	/* Write range extension. */
+	prev_reg = vreg->test_reg[4];
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+			range_ext | REGULATOR_BANK_SEL(4)
+			 | REGULATOR_BANK_WRITE,
+			LDO_TEST_RANGE_EXT_MASK | REGULATOR_BANK_MASK,
+			&vreg->test_reg[4]);
+	if (rc)
+		goto bail;
+	if (prev_reg != vreg->test_reg[4])
+		reg_changed = true;
+
+	/* Write new voltage. */
+	if (reg_changed) {
+		/*
+		 * Force a CTRL register write even if the value hasn't changed.
+		 * This is neccessary because range select, range extension, and
+		 * fine step will not update until a value is written into the
+		 * control register.
+		 */
+		rc = pm8xxx_vreg_masked_write_forced(vreg, vreg->ctrl_addr,
+			vprog, LDO_CTRL_VPROG_MASK, &vreg->ctrl_reg);
+	} else {
+		/* Only write to control register if new value is different. */
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, vprog,
+			LDO_CTRL_VPROG_MASK, &vreg->ctrl_reg);
+	}
+bail:
+	mutex_unlock(&vreg->pc_lock);
+
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+	else
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_VOLTAGE);
+
+	return rc;
+}
+
+static int pm8xxx_nldo_get_voltage(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	u8 vprog, fine_step_reg;
+
+	mutex_lock(&vreg->pc_lock);
+
+	fine_step_reg = vreg->test_reg[2] & LDO_TEST_FINE_STEP_MASK;
+	vprog = vreg->ctrl_reg & LDO_CTRL_VPROG_MASK;
+
+	mutex_unlock(&vreg->pc_lock);
+
+	vprog = (vprog << 1) | (fine_step_reg >> LDO_TEST_FINE_STEP_SHIFT);
+
+	return NLDO_UV_FINE_STEP * vprog + NLDO_UV_MIN;
+}
+
+static int pm8xxx_nldo_list_voltage(struct regulator_dev *rdev,
+				    unsigned selector)
+{
+	if (selector >= NLDO_SET_POINTS)
+		return 0;
+
+	return selector * NLDO_UV_FINE_STEP + NLDO_UV_MIN;
+}
+
+static int pm8xxx_nldo_set_voltage(struct regulator_dev *rdev, int min_uV,
+				   int max_uV, unsigned *selector)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	unsigned vprog, fine_step_reg, prev_reg;
+	int rc;
+	int uV = min_uV;
+
+	if (uV < NLDO_UV_MIN && max_uV >= NLDO_UV_MIN)
+		uV = NLDO_UV_MIN;
+
+	if (uV < NLDO_UV_MIN || uV > NLDO_UV_MAX) {
+		vreg_err(vreg,
+			"request v=[%d, %d] is outside possible v=[%d, %d]\n",
+			 min_uV, max_uV, NLDO_UV_MIN, NLDO_UV_MAX);
+		return -EINVAL;
+	}
+
+	vprog = (uV - NLDO_UV_MIN + NLDO_UV_FINE_STEP - 1) / NLDO_UV_FINE_STEP;
+	uV = vprog * NLDO_UV_FINE_STEP + NLDO_UV_MIN;
+	fine_step_reg = (vprog & 1) << LDO_TEST_FINE_STEP_SHIFT;
+	vprog >>= 1;
+
+	if (uV > max_uV) {
+		vreg_err(vreg,
+			"request v=[%d, %d] cannot be met by any set point\n",
+			min_uV, max_uV);
+		return -EINVAL;
+	}
+
+	mutex_lock(&vreg->pc_lock);
+
+	/* Write fine step. */
+	prev_reg = vreg->test_reg[2];
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+			fine_step_reg | REGULATOR_BANK_SEL(2)
+			 | REGULATOR_BANK_WRITE | LDO_TEST_VPROG_UPDATE_MASK,
+			LDO_TEST_FINE_STEP_MASK | REGULATOR_BANK_MASK
+			 | LDO_TEST_VPROG_UPDATE_MASK,
+		       &vreg->test_reg[2]);
+	if (rc)
+		goto bail;
+
+	/* Write new voltage. */
+	if (prev_reg != vreg->test_reg[2]) {
+		/*
+		 * Force a CTRL register write even if the value hasn't changed.
+		 * This is neccessary because fine step will not update until a
+		 * value is written into the control register.
+		 */
+		rc = pm8xxx_vreg_masked_write_forced(vreg, vreg->ctrl_addr,
+			vprog, LDO_CTRL_VPROG_MASK, &vreg->ctrl_reg);
+	} else {
+		/* Only write to control register if new value is different. */
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, vprog,
+			LDO_CTRL_VPROG_MASK, &vreg->ctrl_reg);
+	}
+bail:
+	mutex_unlock(&vreg->pc_lock);
+
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+	else
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_VOLTAGE);
+
+	return rc;
+}
+
+static int _pm8xxx_nldo1200_get_voltage(struct pm8xxx_vreg *vreg)
+{
+	int uV = 0;
+	int vprog;
+
+	if (!NLDO1200_IN_ADVANCED_MODE(vreg)) {
+		pr_warn("%s: currently in legacy mode; voltage unknown.\n",
+			vreg->rdesc.name);
+		return vreg->save_uV;
+	}
+
+	vprog = vreg->ctrl_reg & NLDO1200_CTRL_VPROG_MASK;
+
+	if ((vreg->ctrl_reg & NLDO1200_CTRL_RANGE_MASK)
+	    == NLDO1200_CTRL_RANGE_LOW)
+		uV = vprog * NLDO1200_LOW_UV_STEP + NLDO1200_LOW_UV_MIN;
+	else
+		uV = vprog * NLDO1200_HIGH_UV_STEP + NLDO1200_HIGH_UV_MIN;
+
+	return uV;
+}
+
+static int pm8xxx_nldo1200_get_voltage(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+
+	return _pm8xxx_nldo1200_get_voltage(vreg);
+}
+
+static int pm8xxx_nldo1200_list_voltage(struct regulator_dev *rdev,
+					unsigned selector)
+{
+	int uV;
+
+	if (selector >= NLDO1200_SET_POINTS)
+		return 0;
+
+	if (selector < NLDO1200_LOW_SET_POINTS)
+		uV = selector * NLDO1200_LOW_UV_STEP + NLDO1200_LOW_UV_MIN;
+	else
+		uV = (selector - NLDO1200_LOW_SET_POINTS)
+				* NLDO1200_HIGH_UV_STEP
+			+ NLDO1200_HIGH_UV_MIN;
+
+	return uV;
+}
+
+static int _pm8xxx_nldo1200_set_voltage(struct pm8xxx_vreg *vreg, int min_uV,
+		int max_uV)
+{
+	u8 vprog, range;
+	int rc;
+	int uV = min_uV;
+
+	if (uV < NLDO1200_LOW_UV_MIN && max_uV >= NLDO1200_LOW_UV_MIN)
+		uV = NLDO1200_LOW_UV_MIN;
+
+	if (uV < NLDO1200_LOW_UV_MIN || uV > NLDO1200_HIGH_UV_MAX) {
+		vreg_err(vreg,
+			"request v=[%d, %d] is outside possible v=[%d, %d]\n",
+			 min_uV, max_uV, NLDO_UV_MIN, NLDO_UV_MAX);
+		return -EINVAL;
+	}
+
+	if (uV > NLDO1200_LOW_UV_MAX) {
+		vprog = (uV - NLDO1200_HIGH_UV_MIN + NLDO1200_HIGH_UV_STEP - 1)
+			/ NLDO1200_HIGH_UV_STEP;
+		uV = vprog * NLDO1200_HIGH_UV_STEP + NLDO1200_HIGH_UV_MIN;
+		vprog &= NLDO1200_CTRL_VPROG_MASK;
+		range = NLDO1200_CTRL_RANGE_HIGH;
+	} else {
+		vprog = (uV - NLDO1200_LOW_UV_MIN + NLDO1200_LOW_UV_STEP - 1)
+			/ NLDO1200_LOW_UV_STEP;
+		uV = vprog * NLDO1200_LOW_UV_STEP + NLDO1200_LOW_UV_MIN;
+		vprog &= NLDO1200_CTRL_VPROG_MASK;
+		range = NLDO1200_CTRL_RANGE_LOW;
+	}
+
+	if (uV > max_uV) {
+		vreg_err(vreg,
+			"request v=[%d, %d] cannot be met by any set point\n",
+			min_uV, max_uV);
+		return -EINVAL;
+	}
+
+	/* Set to advanced mode */
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+		NLDO1200_ADVANCED_MODE | REGULATOR_BANK_SEL(2)
+		| REGULATOR_BANK_WRITE, NLDO1200_ADVANCED_MODE_MASK
+		| REGULATOR_BANK_MASK, &vreg->test_reg[2]);
+	if (rc)
+		goto bail;
+
+	/* Set voltage and range selection. */
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, vprog | range,
+			NLDO1200_CTRL_VPROG_MASK | NLDO1200_CTRL_RANGE_MASK,
+			&vreg->ctrl_reg);
+	if (rc)
+		goto bail;
+
+	vreg->save_uV = uV;
+
+bail:
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+
+	return rc;
+}
+
+static int pm8xxx_nldo1200_set_voltage(struct regulator_dev *rdev, int min_uV,
+				   int max_uV, unsigned *selector)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc;
+
+	rc = _pm8xxx_nldo1200_set_voltage(vreg, min_uV, max_uV);
+
+	if (!rc)
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_VOLTAGE);
+
+	return rc;
+}
+
+static int pm8xxx_smps_get_voltage_advanced(struct pm8xxx_vreg *vreg)
+{
+	u8 vprog, band;
+	int uV = 0;
+
+	vprog = vreg->ctrl_reg & SMPS_ADVANCED_VPROG_MASK;
+	band = vreg->ctrl_reg & SMPS_ADVANCED_BAND_MASK;
+
+	if (band == SMPS_ADVANCED_BAND_1)
+		uV = vprog * SMPS_BAND1_UV_STEP + SMPS_BAND1_UV_MIN;
+	else if (band == SMPS_ADVANCED_BAND_2)
+		uV = vprog * SMPS_BAND2_UV_STEP + SMPS_BAND2_UV_MIN;
+	else if (band == SMPS_ADVANCED_BAND_3)
+		uV = vprog * SMPS_BAND3_UV_STEP + SMPS_BAND3_UV_MIN;
+	else if (vreg->save_uV > 0)
+		uV = vreg->save_uV;
+	else
+		uV = VOLTAGE_UNKNOWN;
+
+	return uV;
+}
+
+static int pm8xxx_smps_get_voltage_legacy(struct pm8xxx_vreg *vreg)
+{
+	u8 vlow, vref, vprog;
+	int uV;
+
+	vlow = vreg->test_reg[1] & SMPS_LEGACY_VLOW_SEL_MASK;
+	vref = vreg->ctrl_reg & SMPS_LEGACY_VREF_SEL_MASK;
+	vprog = vreg->ctrl_reg & SMPS_LEGACY_VPROG_MASK;
+
+	if (vlow && vref) {
+		/* mode 3 */
+		uV = vprog * SMPS_MODE3_UV_STEP + SMPS_MODE3_UV_MIN;
+	} else if (vref) {
+		/* mode 2 */
+		uV = vprog * SMPS_MODE2_UV_STEP + SMPS_MODE2_UV_MIN;
+	} else {
+		/* mode 1 */
+		uV = vprog * SMPS_MODE1_UV_STEP + SMPS_MODE1_UV_MIN;
+	}
+
+	return uV;
+}
+
+static int _pm8xxx_smps_get_voltage(struct pm8xxx_vreg *vreg)
+{
+	if (SMPS_IN_ADVANCED_MODE(vreg))
+		return pm8xxx_smps_get_voltage_advanced(vreg);
+
+	return pm8xxx_smps_get_voltage_legacy(vreg);
+}
+
+static int pm8xxx_smps_list_voltage(struct regulator_dev *rdev,
+				    unsigned selector)
+{
+	int uV;
+
+	if (selector >= SMPS_ADVANCED_SET_POINTS)
+		return 0;
+
+	if (selector < SMPS_BAND1_SET_POINTS)
+		uV = selector * SMPS_BAND1_UV_STEP + SMPS_BAND1_UV_MIN;
+	else if (selector < (SMPS_BAND1_SET_POINTS + SMPS_BAND2_SET_POINTS))
+		uV = (selector - SMPS_BAND1_SET_POINTS) * SMPS_BAND2_UV_STEP
+			+ SMPS_BAND2_UV_MIN;
+	else
+		uV = (selector - SMPS_BAND1_SET_POINTS - SMPS_BAND2_SET_POINTS)
+				* SMPS_BAND3_UV_STEP
+			+ SMPS_BAND3_UV_MIN;
+
+	return uV;
+}
+
+static int pm8xxx_smps_get_voltage(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int uV;
+
+	mutex_lock(&vreg->pc_lock);
+	uV = _pm8xxx_smps_get_voltage(vreg);
+	mutex_unlock(&vreg->pc_lock);
+
+	return uV;
+}
+
+static int pm8xxx_smps_set_voltage_advanced(struct pm8xxx_vreg *vreg,
+					   int min_uV, int max_uV, int force_on)
+{
+	u8 vprog, band;
+	int rc;
+	int uV = min_uV;
+
+	if (uV < SMPS_BAND1_UV_MIN && max_uV >= SMPS_BAND1_UV_MIN)
+		uV = SMPS_BAND1_UV_MIN;
+
+	if (uV < SMPS_BAND1_UV_MIN || uV > SMPS_BAND3_UV_MAX) {
+		vreg_err(vreg,
+			"request v=[%d, %d] is outside possible v=[%d, %d]\n",
+			 min_uV, max_uV, SMPS_BAND1_UV_MIN, SMPS_BAND3_UV_MAX);
+		return -EINVAL;
+	}
+
+	if (uV > SMPS_BAND2_UV_MAX) {
+		vprog = (uV - SMPS_BAND3_UV_MIN + SMPS_BAND3_UV_STEP - 1)
+			/ SMPS_BAND3_UV_STEP;
+		band = SMPS_ADVANCED_BAND_3;
+		uV = SMPS_BAND3_UV_MIN + vprog * SMPS_BAND3_UV_STEP;
+	} else if (uV > SMPS_BAND1_UV_MAX) {
+		vprog = (uV - SMPS_BAND2_UV_MIN + SMPS_BAND2_UV_STEP - 1)
+			/ SMPS_BAND2_UV_STEP;
+		band = SMPS_ADVANCED_BAND_2;
+		uV = SMPS_BAND2_UV_MIN + vprog * SMPS_BAND2_UV_STEP;
+	} else {
+		vprog = (uV - SMPS_BAND1_UV_MIN + SMPS_BAND1_UV_STEP - 1)
+			/ SMPS_BAND1_UV_STEP;
+		band = SMPS_ADVANCED_BAND_1;
+		uV = SMPS_BAND1_UV_MIN + vprog * SMPS_BAND1_UV_STEP;
+	}
+
+	if (uV > max_uV) {
+		vreg_err(vreg,
+			"request v=[%d, %d] cannot be met by any set point\n",
+			min_uV, max_uV);
+		return -EINVAL;
+	}
+
+	/* Do not set band if regulator currently disabled. */
+	if (!_pm8xxx_vreg_is_enabled(vreg) && !force_on)
+		band = SMPS_ADVANCED_BAND_OFF;
+
+	/* Set advanced mode bit to 1. */
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr, SMPS_ADVANCED_MODE
+		| REGULATOR_BANK_WRITE | REGULATOR_BANK_SEL(7),
+		SMPS_ADVANCED_MODE_MASK | REGULATOR_BANK_MASK,
+		&vreg->test_reg[7]);
+	if (rc)
+		goto bail;
+
+	/* Set voltage and voltage band. */
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, band | vprog,
+			SMPS_ADVANCED_BAND_MASK | SMPS_ADVANCED_VPROG_MASK,
+			&vreg->ctrl_reg);
+	if (rc)
+		goto bail;
+
+	vreg->save_uV = uV;
+
+bail:
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+
+	return rc;
+}
+
+static int pm8xxx_smps_set_voltage_legacy(struct pm8xxx_vreg *vreg, int min_uV,
+					  int max_uV)
+{
+	u8 vlow, vref, vprog, pd, en;
+	int rc;
+	int uV = min_uV;
+
+	if (uV < SMPS_MODE3_UV_MIN && max_uV >= SMPS_MODE3_UV_MIN)
+		uV = SMPS_MODE3_UV_MIN;
+
+	if (uV < SMPS_MODE3_UV_MIN || uV > SMPS_MODE1_UV_MAX) {
+		vreg_err(vreg,
+			"request v=[%d, %d] is outside possible v=[%d, %d]\n",
+			 min_uV, max_uV, SMPS_MODE3_UV_MIN, SMPS_MODE1_UV_MAX);
+		return -EINVAL;
+	}
+
+	if (uV > SMPS_MODE2_UV_MAX) {
+		vprog = (uV - SMPS_MODE1_UV_MIN + SMPS_MODE1_UV_STEP - 1)
+			/ SMPS_MODE1_UV_STEP;
+		vref = 0;
+		vlow = 0;
+		uV = SMPS_MODE1_UV_MIN + vprog * SMPS_MODE1_UV_STEP;
+	} else if (uV > SMPS_MODE3_UV_MAX) {
+		vprog = (uV - SMPS_MODE2_UV_MIN + SMPS_MODE2_UV_STEP - 1)
+			/ SMPS_MODE2_UV_STEP;
+		vref = SMPS_LEGACY_VREF_SEL_MASK;
+		vlow = 0;
+		uV = SMPS_MODE2_UV_MIN + vprog * SMPS_MODE2_UV_STEP;
+	} else {
+		vprog = (uV - SMPS_MODE3_UV_MIN + SMPS_MODE3_UV_STEP - 1)
+			/ SMPS_MODE3_UV_STEP;
+		vref = SMPS_LEGACY_VREF_SEL_MASK;
+		vlow = SMPS_LEGACY_VLOW_SEL_MASK;
+		uV = SMPS_MODE3_UV_MIN + vprog * SMPS_MODE3_UV_STEP;
+	}
+
+	if (uV > max_uV) {
+		vreg_err(vreg,
+			"request v=[%d, %d] cannot be met by any set point\n",
+			min_uV, max_uV);
+		return -EINVAL;
+	}
+
+	/* set vlow bit for ultra low voltage mode */
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+		vlow | REGULATOR_BANK_WRITE | REGULATOR_BANK_SEL(1),
+		REGULATOR_BANK_MASK | SMPS_LEGACY_VLOW_SEL_MASK,
+		&vreg->test_reg[1]);
+	if (rc)
+		goto bail;
+
+	/* Set advanced mode bit to 0. */
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr, SMPS_LEGACY_MODE
+		| REGULATOR_BANK_WRITE | REGULATOR_BANK_SEL(7),
+		SMPS_ADVANCED_MODE_MASK | REGULATOR_BANK_MASK,
+		&vreg->test_reg[7]);
+	if (rc)
+		goto bail;
+
+	en = (_pm8xxx_vreg_is_enabled(vreg) ? SMPS_LEGACY_ENABLE : 0);
+	pd = (vreg->pdata.pull_down_enable ? SMPS_LEGACY_PULL_DOWN_ENABLE : 0);
+
+	/* Set voltage (and the rest of the control register). */
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+		en | pd | vref | vprog,
+		SMPS_LEGACY_ENABLE_MASK | SMPS_LEGACY_PULL_DOWN_ENABLE
+		  | SMPS_LEGACY_VREF_SEL_MASK | SMPS_LEGACY_VPROG_MASK,
+		&vreg->ctrl_reg);
+
+	vreg->save_uV = uV;
+
+bail:
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+
+	return rc;
+}
+
+static int pm8xxx_smps_set_voltage(struct regulator_dev *rdev, int min_uV,
+				   int max_uV, unsigned *selector)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc = 0;
+
+	mutex_lock(&vreg->pc_lock);
+
+	if (SMPS_IN_ADVANCED_MODE(vreg) || !pm8xxx_vreg_is_pin_controlled(vreg))
+		rc = pm8xxx_smps_set_voltage_advanced(vreg, min_uV, max_uV, 0);
+	else
+		rc = pm8xxx_smps_set_voltage_legacy(vreg, min_uV, max_uV);
+
+	mutex_unlock(&vreg->pc_lock);
+
+	if (!rc)
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_VOLTAGE);
+
+	return rc;
+}
+
+static int _pm8xxx_ftsmps_get_voltage(struct pm8xxx_vreg *vreg)
+{
+	u8 vprog, band;
+	int uV = 0;
+
+	if ((vreg->test_reg[0] & FTSMPS_CNFG1_PM_MASK) == FTSMPS_CNFG1_PM_PFM) {
+		vprog = vreg->pfm_ctrl_reg & FTSMPS_VCTRL_VPROG_MASK;
+		band = vreg->pfm_ctrl_reg & FTSMPS_VCTRL_BAND_MASK;
+		if (band == FTSMPS_VCTRL_BAND_OFF && vprog == 0) {
+			/* PWM_VCTRL overrides PFM_VCTRL */
+			vprog = vreg->ctrl_reg & FTSMPS_VCTRL_VPROG_MASK;
+			band = vreg->ctrl_reg & FTSMPS_VCTRL_BAND_MASK;
+		}
+	} else {
+		vprog = vreg->ctrl_reg & FTSMPS_VCTRL_VPROG_MASK;
+		band = vreg->ctrl_reg & FTSMPS_VCTRL_BAND_MASK;
+	}
+
+	if (band == FTSMPS_VCTRL_BAND_1)
+		uV = vprog * FTSMPS_BAND1_UV_PHYS_STEP + FTSMPS_BAND1_UV_MIN;
+	else if (band == FTSMPS_VCTRL_BAND_2)
+		uV = vprog * FTSMPS_BAND2_UV_STEP + FTSMPS_BAND2_UV_MIN;
+	else if (band == FTSMPS_VCTRL_BAND_3)
+		uV = vprog * FTSMPS_BAND3_UV_STEP + FTSMPS_BAND3_UV_MIN;
+	else if (vreg->save_uV > 0)
+		uV = vreg->save_uV;
+	else
+		uV = VOLTAGE_UNKNOWN;
+
+	return uV;
+}
+
+static int pm8xxx_ftsmps_get_voltage(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+
+	return _pm8xxx_ftsmps_get_voltage(vreg);
+}
+
+static int pm8xxx_ftsmps_list_voltage(struct regulator_dev *rdev,
+				      unsigned selector)
+{
+	int uV;
+
+	if (selector >= FTSMPS_SET_POINTS)
+		return 0;
+
+	if (selector < FTSMPS_BAND1_SET_POINTS)
+		uV = selector * FTSMPS_BAND1_UV_LOG_STEP + FTSMPS_BAND1_UV_MIN;
+	else if (selector < (FTSMPS_BAND1_SET_POINTS + FTSMPS_BAND2_SET_POINTS))
+		uV = (selector - FTSMPS_BAND1_SET_POINTS) * FTSMPS_BAND2_UV_STEP
+			+ FTSMPS_BAND2_UV_MIN;
+	else
+		uV = (selector - FTSMPS_BAND1_SET_POINTS
+			- FTSMPS_BAND2_SET_POINTS)
+				* FTSMPS_BAND3_UV_STEP
+			+ FTSMPS_BAND3_UV_SET_POINT_MIN;
+
+	return uV;
+}
+
+static int _pm8xxx_ftsmps_set_voltage(struct pm8xxx_vreg *vreg, int min_uV,
+				      int max_uV, int force_on)
+{
+	int rc = 0;
+	u8 vprog, band;
+	int uV = min_uV;
+
+	if (uV < FTSMPS_BAND1_UV_MIN && max_uV >= FTSMPS_BAND1_UV_MIN)
+		uV = FTSMPS_BAND1_UV_MIN;
+
+	if (uV < FTSMPS_BAND1_UV_MIN || uV > FTSMPS_BAND3_UV_MAX) {
+		vreg_err(vreg,
+			"request v=[%d, %d] is outside possible v=[%d, %d]\n",
+			 min_uV, max_uV, FTSMPS_BAND1_UV_MIN,
+			 FTSMPS_BAND3_UV_MAX);
+		return -EINVAL;
+	}
+
+	/* Round up for set points in the gaps between bands. */
+	if (uV > FTSMPS_BAND1_UV_MAX && uV < FTSMPS_BAND2_UV_MIN)
+		uV = FTSMPS_BAND2_UV_MIN;
+	else if (uV > FTSMPS_BAND2_UV_MAX
+			&& uV < FTSMPS_BAND3_UV_SET_POINT_MIN)
+		uV = FTSMPS_BAND3_UV_SET_POINT_MIN;
+
+	if (uV > FTSMPS_BAND2_UV_MAX) {
+		vprog = (uV - FTSMPS_BAND3_UV_MIN + FTSMPS_BAND3_UV_STEP - 1)
+			/ FTSMPS_BAND3_UV_STEP;
+		band = FTSMPS_VCTRL_BAND_3;
+		uV = FTSMPS_BAND3_UV_MIN + vprog * FTSMPS_BAND3_UV_STEP;
+	} else if (uV > FTSMPS_BAND1_UV_MAX) {
+		vprog = (uV - FTSMPS_BAND2_UV_MIN + FTSMPS_BAND2_UV_STEP - 1)
+			/ FTSMPS_BAND2_UV_STEP;
+		band = FTSMPS_VCTRL_BAND_2;
+		uV = FTSMPS_BAND2_UV_MIN + vprog * FTSMPS_BAND2_UV_STEP;
+	} else {
+		vprog = (uV - FTSMPS_BAND1_UV_MIN
+				+ FTSMPS_BAND1_UV_LOG_STEP - 1)
+			/ FTSMPS_BAND1_UV_LOG_STEP;
+		uV = FTSMPS_BAND1_UV_MIN + vprog * FTSMPS_BAND1_UV_LOG_STEP;
+		vprog *= FTSMPS_BAND1_UV_LOG_STEP / FTSMPS_BAND1_UV_PHYS_STEP;
+		band = FTSMPS_VCTRL_BAND_1;
+	}
+
+	if (uV > max_uV) {
+		vreg_err(vreg,
+			"request v=[%d, %d] cannot be met by any set point\n",
+			min_uV, max_uV);
+		return -EINVAL;
+	}
+
+	/*
+	 * Do not set voltage if regulator is currently disabled because doing
+	 * so will enable it.
+	 */
+	if (_pm8xxx_vreg_is_enabled(vreg) || force_on) {
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+			band | vprog,
+			FTSMPS_VCTRL_BAND_MASK | FTSMPS_VCTRL_VPROG_MASK,
+			&vreg->ctrl_reg);
+		if (rc)
+			goto bail;
+
+		/* Program PFM_VCTRL as 0x00 so that PWM_VCTRL overrides it. */
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->pfm_ctrl_addr, 0x00,
+			FTSMPS_VCTRL_BAND_MASK | FTSMPS_VCTRL_VPROG_MASK,
+			&vreg->pfm_ctrl_reg);
+		if (rc)
+			goto bail;
+	}
+
+	vreg->save_uV = uV;
+
+bail:
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+
+	return rc;
+}
+
+static int pm8xxx_ftsmps_set_voltage(struct regulator_dev *rdev, int min_uV,
+				     int max_uV, unsigned *selector)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc;
+
+	rc = _pm8xxx_ftsmps_set_voltage(vreg, min_uV, max_uV, 0);
+
+	if (!rc)
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_VOLTAGE);
+
+	return rc;
+}
+
+static int pm8xxx_ncp_get_voltage(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	u8 vprog;
+
+	vprog = vreg->ctrl_reg & NCP_VPROG_MASK;
+
+	return NCP_UV_MIN + vprog * NCP_UV_STEP;
+}
+
+static int pm8xxx_ncp_list_voltage(struct regulator_dev *rdev,
+				   unsigned selector)
+{
+	if (selector >= NCP_SET_POINTS)
+		return 0;
+
+	return selector * NCP_UV_STEP + NCP_UV_MIN;
+}
+
+static int pm8xxx_ncp_set_voltage(struct regulator_dev *rdev, int min_uV,
+				  int max_uV, unsigned *selector)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc;
+	int uV = min_uV;
+	u8 val;
+
+	if (uV < NCP_UV_MIN && max_uV >= NCP_UV_MIN)
+		uV = NCP_UV_MIN;
+
+	if (uV < NCP_UV_MIN || uV > NCP_UV_MAX) {
+		vreg_err(vreg,
+			"request v=[%d, %d] is outside possible v=[%d, %d]\n",
+			 min_uV, max_uV, NCP_UV_MIN, NCP_UV_MAX);
+		return -EINVAL;
+	}
+
+	val = (uV - NCP_UV_MIN + NCP_UV_STEP - 1) / NCP_UV_STEP;
+	uV = val * NCP_UV_STEP + NCP_UV_MIN;
+
+	if (uV > max_uV) {
+		vreg_err(vreg,
+			"request v=[%d, %d] cannot be met by any set point\n",
+			min_uV, max_uV);
+		return -EINVAL;
+	}
+
+	/* voltage setting */
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, val,
+			NCP_VPROG_MASK, &vreg->ctrl_reg);
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+	else
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_VOLTAGE);
+
+	return rc;
+}
+
+static int pm8xxx_boost_get_voltage(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	u8 vprog;
+
+	vprog = vreg->ctrl_reg & BOOST_VPROG_MASK;
+
+	return BOOST_UV_STEP * vprog + BOOST_UV_MIN;
+}
+
+static int pm8xxx_boost_list_voltage(struct regulator_dev *rdev,
+				    unsigned selector)
+{
+	if (selector >= BOOST_SET_POINTS)
+		return 0;
+
+	return selector * BOOST_UV_STEP + BOOST_UV_MIN;
+}
+
+static int pm8xxx_boost_set_voltage(struct regulator_dev *rdev, int min_uV,
+				   int max_uV, unsigned *selector)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc;
+	int uV = min_uV;
+	u8 val;
+
+	if (uV < BOOST_UV_MIN && max_uV >= BOOST_UV_MIN)
+		uV = BOOST_UV_MIN;
+
+	if (uV < BOOST_UV_MIN || uV > BOOST_UV_MAX) {
+		vreg_err(vreg,
+			"request v=[%d, %d] is outside possible v=[%d, %d]\n",
+			 min_uV, max_uV, BOOST_UV_MIN, BOOST_UV_MAX);
+		return -EINVAL;
+	}
+
+	val = (uV - BOOST_UV_MIN + BOOST_UV_STEP - 1) / BOOST_UV_STEP;
+	uV = val * BOOST_UV_STEP + BOOST_UV_MIN;
+
+	if (uV > max_uV) {
+		vreg_err(vreg,
+			"request v=[%d, %d] cannot be met by any set point\n",
+			min_uV, max_uV);
+		return -EINVAL;
+	}
+
+	/* voltage setting */
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, val,
+			BOOST_VPROG_MASK, &vreg->ctrl_reg);
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+	else
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_VOLTAGE);
+
+	return rc;
+}
+
+static unsigned int pm8xxx_ldo_get_mode(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	unsigned int mode = 0;
+
+	mutex_lock(&vreg->pc_lock);
+	mode = vreg->mode;
+	mutex_unlock(&vreg->pc_lock);
+
+	return mode;
+}
+
+static int pm8xxx_ldo_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc = 0;
+
+	if (mode != REGULATOR_MODE_NORMAL && mode != REGULATOR_MODE_IDLE) {
+		vreg_err(vreg, "invalid mode: %u\n", mode);
+		return -EINVAL;
+	}
+
+	mutex_lock(&vreg->pc_lock);
+
+	if (mode == REGULATOR_MODE_NORMAL
+	    || (vreg->is_enabled_pc
+		&& vreg->pdata.pin_fn == PM8XXX_VREG_PIN_FN_ENABLE)) {
+		/* HPM */
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+			LDO_CTRL_PM_HPM, LDO_CTRL_PM_MASK, &vreg->ctrl_reg);
+	} else {
+		/* LPM */
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+			LDO_CTRL_PM_LPM, LDO_CTRL_PM_MASK, &vreg->ctrl_reg);
+		if (rc)
+			goto bail;
+
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+			LDO_TEST_LPM_SEL_CTRL | REGULATOR_BANK_WRITE
+			  | REGULATOR_BANK_SEL(0),
+			LDO_TEST_LPM_MASK | REGULATOR_BANK_MASK,
+			&vreg->test_reg[0]);
+	}
+
+bail:
+	if (!rc)
+		vreg->mode = mode;
+
+	mutex_unlock(&vreg->pc_lock);
+
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+	else
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_MODE);
+
+	return rc;
+}
+
+static unsigned int pm8xxx_nldo1200_get_mode(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	unsigned int mode = 0;
+
+	if (NLDO1200_IN_ADVANCED_MODE(vreg)) {
+		/* Advanced mode */
+		if ((vreg->test_reg[2] & NLDO1200_ADVANCED_PM_MASK)
+		    == NLDO1200_ADVANCED_PM_LPM)
+			mode = REGULATOR_MODE_IDLE;
+		else
+			mode = REGULATOR_MODE_NORMAL;
+	} else {
+		/* Legacy mode */
+		if ((vreg->ctrl_reg & NLDO1200_LEGACY_PM_MASK)
+		    == NLDO1200_LEGACY_PM_LPM)
+			mode = REGULATOR_MODE_IDLE;
+		else
+			mode = REGULATOR_MODE_NORMAL;
+	}
+
+	return mode;
+}
+
+static int pm8xxx_nldo1200_set_mode(struct regulator_dev *rdev,
+				    unsigned int mode)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc = 0;
+
+	if (mode != REGULATOR_MODE_NORMAL && mode != REGULATOR_MODE_IDLE) {
+		vreg_err(vreg, "invalid mode: %u\n", mode);
+		return -EINVAL;
+	}
+
+	/*
+	 * Make sure that advanced mode is in use.  If it isn't, then set it
+	 * and update the voltage accordingly.
+	 */
+	if (!NLDO1200_IN_ADVANCED_MODE(vreg)) {
+		rc = _pm8xxx_nldo1200_set_voltage(vreg, vreg->save_uV,
+			vreg->save_uV);
+		if (rc)
+			goto bail;
+	}
+
+	if (mode == REGULATOR_MODE_NORMAL) {
+		/* HPM */
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+			NLDO1200_ADVANCED_PM_HPM | REGULATOR_BANK_WRITE
+			| REGULATOR_BANK_SEL(2), NLDO1200_ADVANCED_PM_MASK
+			| REGULATOR_BANK_MASK, &vreg->test_reg[2]);
+	} else {
+		/* LPM */
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+			NLDO1200_ADVANCED_PM_LPM | REGULATOR_BANK_WRITE
+			| REGULATOR_BANK_SEL(2), NLDO1200_ADVANCED_PM_MASK
+			| REGULATOR_BANK_MASK, &vreg->test_reg[2]);
+	}
+
+bail:
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+	else
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_MODE);
+
+	return rc;
+}
+
+static unsigned int pm8xxx_smps_get_mode(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	unsigned int mode = 0;
+
+	mutex_lock(&vreg->pc_lock);
+	mode = vreg->mode;
+	mutex_unlock(&vreg->pc_lock);
+
+	return mode;
+}
+
+static int pm8xxx_smps_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc = 0;
+
+	if (mode != REGULATOR_MODE_NORMAL && mode != REGULATOR_MODE_IDLE) {
+		vreg_err(vreg, "invalid mode: %u\n", mode);
+		return -EINVAL;
+	}
+
+	mutex_lock(&vreg->pc_lock);
+
+	if (mode == REGULATOR_MODE_NORMAL
+	    || (vreg->is_enabled_pc
+		&& vreg->pdata.pin_fn == PM8XXX_VREG_PIN_FN_ENABLE)) {
+		/* HPM */
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->clk_ctrl_addr,
+				       SMPS_CLK_CTRL_PWM, SMPS_CLK_CTRL_MASK,
+				       &vreg->clk_ctrl_reg);
+	} else {
+		/* LPM */
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->clk_ctrl_addr,
+				       SMPS_CLK_CTRL_PFM, SMPS_CLK_CTRL_MASK,
+				       &vreg->clk_ctrl_reg);
+	}
+
+	if (!rc)
+		vreg->mode = mode;
+
+	mutex_unlock(&vreg->pc_lock);
+
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+	else
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_MODE);
+
+	return rc;
+}
+
+static unsigned int pm8xxx_ftsmps_get_mode(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	unsigned int mode = 0;
+
+	if ((vreg->test_reg[0] & FTSMPS_CNFG1_PM_MASK) == FTSMPS_CNFG1_PM_PFM)
+		mode = REGULATOR_MODE_IDLE;
+	else
+		mode = REGULATOR_MODE_NORMAL;
+
+	return mode;
+}
+
+static int pm8xxx_ftsmps_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc = 0;
+
+	if (mode == REGULATOR_MODE_NORMAL) {
+		/* HPM */
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+				FTSMPS_CNFG1_PM_PWM | REGULATOR_BANK_WRITE
+				| REGULATOR_BANK_SEL(0), FTSMPS_CNFG1_PM_MASK
+				| REGULATOR_BANK_MASK, &vreg->test_reg[0]);
+	} else if (mode == REGULATOR_MODE_IDLE) {
+		/* LPM */
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+				FTSMPS_CNFG1_PM_PFM | REGULATOR_BANK_WRITE
+				| REGULATOR_BANK_SEL(0), FTSMPS_CNFG1_PM_MASK
+				| REGULATOR_BANK_MASK, &vreg->test_reg[0]);
+	} else {
+		vreg_err(vreg, "invalid mode: %u\n", mode);
+		return -EINVAL;
+	}
+
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+	else
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_MODE);
+
+	return rc;
+}
+
+static unsigned int pm8xxx_vreg_get_optimum_mode(struct regulator_dev *rdev,
+		int input_uV, int output_uV, int load_uA)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	unsigned int mode;
+
+	if (load_uA + vreg->pdata.system_uA >= vreg->hpm_min_load)
+		mode = REGULATOR_MODE_NORMAL;
+	else
+		mode = REGULATOR_MODE_IDLE;
+
+	return mode;
+}
+
+static int pm8xxx_ldo_enable(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc, val;
+
+	mutex_lock(&vreg->pc_lock);
+
+	/*
+	 * Choose HPM if previously set to HPM or if pin control is enabled in
+	 * on/off mode.
+	 */
+	val = LDO_CTRL_PM_LPM;
+	if (vreg->mode == REGULATOR_MODE_NORMAL
+		|| (vreg->is_enabled_pc
+			&& vreg->pdata.pin_fn == PM8XXX_VREG_PIN_FN_ENABLE))
+		val = LDO_CTRL_PM_HPM;
+
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, val | LDO_ENABLE,
+		LDO_ENABLE_MASK | LDO_CTRL_PM_MASK, &vreg->ctrl_reg);
+
+	if (!rc)
+		vreg->is_enabled = true;
+
+	mutex_unlock(&vreg->pc_lock);
+
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+	else
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_ENABLE);
+
+	return rc;
+}
+
+static int pm8xxx_ldo_disable(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc;
+
+	mutex_lock(&vreg->pc_lock);
+
+	/*
+	 * Only disable the regulator if it isn't still required for HPM/LPM
+	 * pin control.
+	 */
+	if (!vreg->is_enabled_pc
+	    || vreg->pdata.pin_fn != PM8XXX_VREG_PIN_FN_MODE) {
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+			LDO_DISABLE, LDO_ENABLE_MASK, &vreg->ctrl_reg);
+		if (rc)
+			goto bail;
+	}
+
+	/* Change to LPM if HPM/LPM pin control is enabled. */
+	if (vreg->is_enabled_pc
+	    && vreg->pdata.pin_fn == PM8XXX_VREG_PIN_FN_MODE) {
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+			LDO_CTRL_PM_LPM, LDO_CTRL_PM_MASK, &vreg->ctrl_reg);
+		if (rc)
+			goto bail;
+
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+			LDO_TEST_LPM_SEL_CTRL | REGULATOR_BANK_WRITE
+			  | REGULATOR_BANK_SEL(0),
+			LDO_TEST_LPM_MASK | REGULATOR_BANK_MASK,
+			&vreg->test_reg[0]);
+	}
+
+	if (!rc)
+		vreg->is_enabled = false;
+bail:
+	mutex_unlock(&vreg->pc_lock);
+
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+	else
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_DISABLE);
+
+	return rc;
+}
+
+static int pm8xxx_nldo1200_enable(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc;
+
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, NLDO1200_ENABLE,
+		NLDO1200_ENABLE_MASK, &vreg->ctrl_reg);
+
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+	else
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_ENABLE);
+
+	return rc;
+}
+
+static int pm8xxx_nldo1200_disable(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc;
+
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, NLDO1200_DISABLE,
+		NLDO1200_ENABLE_MASK, &vreg->ctrl_reg);
+
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+	else
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_DISABLE);
+
+	return rc;
+}
+
+static int pm8xxx_smps_enable(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc = 0;
+	int val;
+
+	mutex_lock(&vreg->pc_lock);
+
+	if (SMPS_IN_ADVANCED_MODE(vreg)
+	     || !pm8xxx_vreg_is_pin_controlled(vreg)) {
+		/* Enable in advanced mode if not using pin control. */
+		rc = pm8xxx_smps_set_voltage_advanced(vreg, vreg->save_uV,
+			vreg->save_uV, 1);
+	} else {
+		rc = pm8xxx_smps_set_voltage_legacy(vreg, vreg->save_uV,
+			vreg->save_uV);
+		if (rc)
+			goto bail;
+
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+			SMPS_LEGACY_ENABLE, SMPS_LEGACY_ENABLE_MASK,
+			&vreg->ctrl_reg);
+	}
+
+	/*
+	 * Choose HPM if previously set to HPM or if pin control is enabled in
+	 * on/off mode.
+	 */
+	val = SMPS_CLK_CTRL_PFM;
+	if (vreg->mode == REGULATOR_MODE_NORMAL
+		|| (vreg->is_enabled_pc
+			&& vreg->pdata.pin_fn == PM8XXX_VREG_PIN_FN_ENABLE))
+		val = SMPS_CLK_CTRL_PWM;
+
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->clk_ctrl_addr, val,
+			SMPS_CLK_CTRL_MASK, &vreg->clk_ctrl_reg);
+
+	if (!rc)
+		vreg->is_enabled = true;
+bail:
+	mutex_unlock(&vreg->pc_lock);
+
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+	else
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_ENABLE);
+
+	return rc;
+}
+
+static int pm8xxx_smps_disable(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc;
+
+	mutex_lock(&vreg->pc_lock);
+
+	if (SMPS_IN_ADVANCED_MODE(vreg)) {
+		/* Change SMPS to legacy mode before disabling. */
+		rc = pm8xxx_smps_set_voltage_legacy(vreg, vreg->save_uV,
+				vreg->save_uV);
+		if (rc)
+			goto bail;
+	}
+
+	/*
+	 * Only disable the regulator if it isn't still required for HPM/LPM
+	 * pin control.
+	 */
+	if (!vreg->is_enabled_pc
+	    || vreg->pdata.pin_fn != PM8XXX_VREG_PIN_FN_MODE) {
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+			SMPS_LEGACY_DISABLE, SMPS_LEGACY_ENABLE_MASK,
+			&vreg->ctrl_reg);
+		if (rc)
+			goto bail;
+	}
+
+	/* Change to LPM if HPM/LPM pin control is enabled. */
+	if (vreg->is_enabled_pc
+	    && vreg->pdata.pin_fn == PM8XXX_VREG_PIN_FN_MODE)
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->clk_ctrl_addr,
+		       SMPS_CLK_CTRL_PFM, SMPS_CLK_CTRL_MASK,
+		       &vreg->clk_ctrl_reg);
+
+	if (!rc)
+		vreg->is_enabled = false;
+
+bail:
+	mutex_unlock(&vreg->pc_lock);
+
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+	else
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_DISABLE);
+
+	return rc;
+}
+
+static int pm8xxx_ftsmps_enable(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc;
+
+	rc = _pm8xxx_ftsmps_set_voltage(vreg, vreg->save_uV, vreg->save_uV, 1);
+
+	if (rc)
+		vreg_err(vreg, "set voltage failed, rc=%d\n", rc);
+	else
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_ENABLE);
+
+	return rc;
+}
+
+static int pm8xxx_ftsmps_disable(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc;
+
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+		FTSMPS_VCTRL_BAND_OFF, FTSMPS_VCTRL_BAND_MASK, &vreg->ctrl_reg);
+	if (rc)
+		goto bail;
+
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->pfm_ctrl_addr,
+		FTSMPS_VCTRL_BAND_OFF, FTSMPS_VCTRL_BAND_MASK,
+		&vreg->pfm_ctrl_reg);
+bail:
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+	else
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_DISABLE);
+
+	return rc;
+}
+
+static int pm8xxx_vs_enable(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc;
+
+	mutex_lock(&vreg->pc_lock);
+
+	if (vreg->pdata.ocp_enable) {
+		/* Disable OCP. */
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+			VS_OCP_DISABLE, VS_OCP_MASK, &vreg->test_reg[0]);
+		if (rc)
+			goto done;
+
+		/* Enable the switch while OCP is disabled. */
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+			VS_ENABLE | VS_MODE_NORMAL,
+			VS_ENABLE_MASK | VS_MODE_MASK,
+			&vreg->ctrl_reg);
+		if (rc)
+			goto done;
+
+		/* Wait for inrush current to subside, then enable OCP. */
+		udelay(vreg->pdata.ocp_enable_time);
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+			VS_OCP_ENABLE, VS_OCP_MASK, &vreg->test_reg[0]);
+	} else {
+		/* Enable the switch without touching OCP. */
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, VS_ENABLE,
+			VS_ENABLE_MASK, &vreg->ctrl_reg);
+	}
+
+done:
+	if (!rc)
+		vreg->is_enabled = true;
+
+	mutex_unlock(&vreg->pc_lock);
+
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+	else
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_ENABLE);
+
+	return rc;
+}
+
+static int pm8xxx_vs_disable(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc;
+
+	mutex_lock(&vreg->pc_lock);
+
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, VS_DISABLE,
+		VS_ENABLE_MASK, &vreg->ctrl_reg);
+
+	if (!rc)
+		vreg->is_enabled = false;
+
+	mutex_unlock(&vreg->pc_lock);
+
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+	else
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_DISABLE);
+
+	return rc;
+}
+
+static int pm8xxx_vs300_enable(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc;
+
+	if (vreg->pdata.ocp_enable) {
+		/* Disable OCP. */
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+			VS_OCP_DISABLE, VS_OCP_MASK, &vreg->test_reg[0]);
+		if (rc)
+			goto done;
+
+		/* Enable the switch while OCP is disabled. */
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+			VS300_CTRL_ENABLE | VS300_MODE_NORMAL,
+			VS300_CTRL_ENABLE_MASK | VS300_MODE_MASK,
+			&vreg->ctrl_reg);
+		if (rc)
+			goto done;
+
+		/* Wait for inrush current to subside, then enable OCP. */
+		udelay(vreg->pdata.ocp_enable_time);
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+			VS_OCP_ENABLE, VS_OCP_MASK, &vreg->test_reg[0]);
+	} else {
+		/* Enable the regulator without touching OCP. */
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+			VS300_CTRL_ENABLE, VS300_CTRL_ENABLE_MASK,
+			&vreg->ctrl_reg);
+	}
+
+done:
+	if (rc) {
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+	} else {
+		vreg->is_enabled = true;
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_ENABLE);
+	}
+
+	return rc;
+}
+
+static int pm8xxx_vs300_disable(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc;
+
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, VS300_CTRL_DISABLE,
+		VS300_CTRL_ENABLE_MASK, &vreg->ctrl_reg);
+
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+	else
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_DISABLE);
+
+	return rc;
+}
+
+static int pm8xxx_ncp_enable(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc;
+
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, NCP_ENABLE,
+		NCP_ENABLE_MASK, &vreg->ctrl_reg);
+
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+	else
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_ENABLE);
+
+	return rc;
+}
+
+static int pm8xxx_ncp_disable(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc;
+
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, NCP_DISABLE,
+		NCP_ENABLE_MASK, &vreg->ctrl_reg);
+
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+	else
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_DISABLE);
+
+	return rc;
+}
+
+static int pm8xxx_boost_enable(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc;
+
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, BOOST_ENABLE,
+		BOOST_ENABLE_MASK, &vreg->ctrl_reg);
+
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+	else
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_ENABLE);
+
+	return rc;
+}
+
+static int pm8xxx_boost_disable(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc;
+
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, BOOST_DISABLE,
+		BOOST_ENABLE_MASK, &vreg->ctrl_reg);
+
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+	else
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_DISABLE);
+
+	return rc;
+}
+
+static int pm8xxx_ldo_pin_control_enable(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc = 0;
+	int bank;
+	u8 val = 0;
+	u8 mask;
+
+	mutex_lock(&vreg->pc_lock);
+
+	if (vreg->pdata.pin_ctrl & PM8XXX_VREG_PIN_CTRL_EN0)
+		val |= LDO_TEST_PIN_CTRL_EN0;
+	if (vreg->pdata.pin_ctrl & PM8XXX_VREG_PIN_CTRL_EN1)
+		val |= LDO_TEST_PIN_CTRL_EN1;
+	if (vreg->pdata.pin_ctrl & PM8XXX_VREG_PIN_CTRL_EN2)
+		val |= LDO_TEST_PIN_CTRL_EN2;
+	if (vreg->pdata.pin_ctrl & PM8XXX_VREG_PIN_CTRL_EN3)
+		val |= LDO_TEST_PIN_CTRL_EN3;
+
+	bank = (vreg->pdata.pin_fn == PM8XXX_VREG_PIN_FN_ENABLE ? 5 : 6);
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+		val | REGULATOR_BANK_SEL(bank) | REGULATOR_BANK_WRITE,
+		LDO_TEST_PIN_CTRL_MASK | REGULATOR_BANK_MASK,
+		&vreg->test_reg[bank]);
+	if (rc)
+		goto bail;
+
+	/* Unset pin control bits in unused bank. */
+	bank = (bank == 5 ? 6 : 5);
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+		REGULATOR_BANK_SEL(bank) | REGULATOR_BANK_WRITE,
+		LDO_TEST_PIN_CTRL_MASK | REGULATOR_BANK_MASK,
+		&vreg->test_reg[bank]);
+	if (rc)
+		goto bail;
+
+	val = LDO_TEST_LPM_SEL_CTRL | REGULATOR_BANK_WRITE
+		| REGULATOR_BANK_SEL(0);
+	mask = LDO_TEST_LPM_MASK | REGULATOR_BANK_MASK;
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr, val, mask,
+		&vreg->test_reg[0]);
+	if (rc)
+		goto bail;
+
+	if (vreg->pdata.pin_fn == PM8XXX_VREG_PIN_FN_ENABLE) {
+		/* Pin control ON/OFF */
+		val = LDO_CTRL_PM_HPM;
+		/* Leave physically enabled if already enabled. */
+		val |= (vreg->is_enabled ? LDO_ENABLE : LDO_DISABLE);
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, val,
+			LDO_ENABLE_MASK | LDO_CTRL_PM_MASK, &vreg->ctrl_reg);
+		if (rc)
+			goto bail;
+	} else {
+		/* Pin control LPM/HPM */
+		val = LDO_ENABLE;
+		/* Leave in HPM if already enabled in HPM. */
+		val |= (vreg->is_enabled && vreg->mode == REGULATOR_MODE_NORMAL
+			?  LDO_CTRL_PM_HPM : LDO_CTRL_PM_LPM);
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, val,
+			LDO_ENABLE_MASK | LDO_CTRL_PM_MASK, &vreg->ctrl_reg);
+		if (rc)
+			goto bail;
+	}
+
+bail:
+	if (!rc)
+		vreg->is_enabled_pc = true;
+
+	mutex_unlock(&vreg->pc_lock);
+
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+	else
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_PIN_CTRL);
+
+	return rc;
+}
+
+static int pm8xxx_ldo_pin_control_disable(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc;
+
+	mutex_lock(&vreg->pc_lock);
+
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+			REGULATOR_BANK_SEL(5) | REGULATOR_BANK_WRITE,
+			LDO_TEST_PIN_CTRL_MASK | REGULATOR_BANK_MASK,
+			&vreg->test_reg[5]);
+	if (rc)
+		goto bail;
+
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+			REGULATOR_BANK_SEL(6) | REGULATOR_BANK_WRITE,
+			LDO_TEST_PIN_CTRL_MASK | REGULATOR_BANK_MASK,
+			&vreg->test_reg[6]);
+
+	/*
+	 * Physically disable the regulator if it was enabled in HPM/LPM pin
+	 * control mode previously and it logically should not be enabled.
+	 */
+	if ((vreg->ctrl_reg & LDO_ENABLE_MASK) == LDO_ENABLE
+	    && !vreg->is_enabled) {
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+			LDO_DISABLE, LDO_ENABLE_MASK, &vreg->ctrl_reg);
+		if (rc)
+			goto bail;
+	}
+
+	/* Change to LPM if LPM was enabled. */
+	if (vreg->is_enabled && vreg->mode == REGULATOR_MODE_IDLE) {
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+			LDO_CTRL_PM_LPM, LDO_CTRL_PM_MASK, &vreg->ctrl_reg);
+		if (rc)
+			goto bail;
+
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+			LDO_TEST_LPM_SEL_CTRL | REGULATOR_BANK_WRITE
+			  | REGULATOR_BANK_SEL(0),
+			LDO_TEST_LPM_MASK | REGULATOR_BANK_MASK,
+			&vreg->test_reg[0]);
+		if (rc)
+			goto bail;
+	}
+
+bail:
+	if (!rc)
+		vreg->is_enabled_pc = false;
+
+	mutex_unlock(&vreg->pc_lock);
+
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+	else
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_PIN_CTRL);
+
+	return rc;
+}
+
+static int pm8xxx_smps_pin_control_enable(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc = 0;
+	u8 val = 0;
+
+	mutex_lock(&vreg->pc_lock);
+
+	if (vreg->pdata.pin_fn == PM8XXX_VREG_PIN_FN_ENABLE) {
+		/* Pin control ON/OFF */
+		if (vreg->pdata.pin_ctrl & PM8XXX_VREG_PIN_CTRL_EN0)
+			val |= SMPS_PIN_CTRL_EN0;
+		if (vreg->pdata.pin_ctrl & PM8XXX_VREG_PIN_CTRL_EN1)
+			val |= SMPS_PIN_CTRL_EN1;
+		if (vreg->pdata.pin_ctrl & PM8XXX_VREG_PIN_CTRL_EN2)
+			val |= SMPS_PIN_CTRL_EN2;
+		if (vreg->pdata.pin_ctrl & PM8XXX_VREG_PIN_CTRL_EN3)
+			val |= SMPS_PIN_CTRL_EN3;
+	} else {
+		/* Pin control LPM/HPM */
+		if (vreg->pdata.pin_ctrl & PM8XXX_VREG_PIN_CTRL_EN0)
+			val |= SMPS_PIN_CTRL_LPM_EN0;
+		if (vreg->pdata.pin_ctrl & PM8XXX_VREG_PIN_CTRL_EN1)
+			val |= SMPS_PIN_CTRL_LPM_EN1;
+		if (vreg->pdata.pin_ctrl & PM8XXX_VREG_PIN_CTRL_EN2)
+			val |= SMPS_PIN_CTRL_LPM_EN2;
+		if (vreg->pdata.pin_ctrl & PM8XXX_VREG_PIN_CTRL_EN3)
+			val |= SMPS_PIN_CTRL_LPM_EN3;
+	}
+
+	rc = pm8xxx_smps_set_voltage_legacy(vreg, vreg->save_uV, vreg->save_uV);
+	if (rc)
+		goto bail;
+
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->sleep_ctrl_addr, val,
+			SMPS_PIN_CTRL_MASK | SMPS_PIN_CTRL_LPM_MASK,
+			&vreg->sleep_ctrl_reg);
+	if (rc)
+		goto bail;
+
+	/*
+	 * Physically enable the regulator if using HPM/LPM pin control mode or
+	 * if the regulator should be logically left on.
+	 */
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+		((vreg->pdata.pin_fn == PM8XXX_VREG_PIN_FN_MODE
+		  || vreg->is_enabled) ?
+			SMPS_LEGACY_ENABLE : SMPS_LEGACY_DISABLE),
+		SMPS_LEGACY_ENABLE_MASK, &vreg->ctrl_reg);
+	if (rc)
+		goto bail;
+
+	/*
+	 * Set regulator to HPM if using on/off pin control or if the regulator
+	 * is already enabled in HPM.  Otherwise, set it to LPM.
+	 */
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->clk_ctrl_addr,
+			(vreg->pdata.pin_fn == PM8XXX_VREG_PIN_FN_ENABLE
+			 || (vreg->is_enabled
+			     && vreg->mode == REGULATOR_MODE_NORMAL)
+				? SMPS_CLK_CTRL_PWM : SMPS_CLK_CTRL_PFM),
+			SMPS_CLK_CTRL_MASK, &vreg->clk_ctrl_reg);
+
+bail:
+	if (!rc)
+		vreg->is_enabled_pc = true;
+
+	mutex_unlock(&vreg->pc_lock);
+
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+	else
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_PIN_CTRL);
+
+	return rc;
+}
+
+static int pm8xxx_smps_pin_control_disable(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc;
+
+	mutex_lock(&vreg->pc_lock);
+
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->sleep_ctrl_addr, 0,
+			SMPS_PIN_CTRL_MASK | SMPS_PIN_CTRL_LPM_MASK,
+			&vreg->sleep_ctrl_reg);
+	if (rc)
+		goto bail;
+
+	/*
+	 * Physically disable the regulator if it was enabled in HPM/LPM pin
+	 * control mode previously and it logically should not be enabled.
+	 */
+	if ((vreg->ctrl_reg & SMPS_LEGACY_ENABLE_MASK) == SMPS_LEGACY_ENABLE
+	    && vreg->is_enabled == false) {
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+			SMPS_LEGACY_DISABLE, SMPS_LEGACY_ENABLE_MASK,
+			&vreg->ctrl_reg);
+		if (rc)
+			goto bail;
+	}
+
+	/* Change to LPM if LPM was enabled. */
+	if (vreg->is_enabled && vreg->mode == REGULATOR_MODE_IDLE) {
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->clk_ctrl_addr,
+		       SMPS_CLK_CTRL_PFM, SMPS_CLK_CTRL_MASK,
+		       &vreg->clk_ctrl_reg);
+		if (rc)
+			goto bail;
+	}
+
+	rc = pm8xxx_smps_set_voltage_advanced(vreg, vreg->save_uV,
+			vreg->save_uV, 0);
+
+bail:
+	if (!rc)
+		vreg->is_enabled_pc = false;
+
+	mutex_unlock(&vreg->pc_lock);
+
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+	else
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_PIN_CTRL);
+
+	return rc;
+}
+
+static int pm8xxx_vs_pin_control_enable(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc;
+	u8 val = 0;
+
+	mutex_lock(&vreg->pc_lock);
+
+	if (vreg->pdata.pin_ctrl & PM8XXX_VREG_PIN_CTRL_EN0)
+		val |= VS_PIN_CTRL_EN0;
+	if (vreg->pdata.pin_ctrl & PM8XXX_VREG_PIN_CTRL_EN1)
+		val |= VS_PIN_CTRL_EN1;
+	if (vreg->pdata.pin_ctrl & PM8XXX_VREG_PIN_CTRL_EN2)
+		val |= VS_PIN_CTRL_EN2;
+	if (vreg->pdata.pin_ctrl & PM8XXX_VREG_PIN_CTRL_EN3)
+		val |= VS_PIN_CTRL_EN3;
+
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, val,
+			VS_PIN_CTRL_MASK | VS_ENABLE_MASK, &vreg->ctrl_reg);
+
+	if (!rc)
+		vreg->is_enabled_pc = true;
+
+	mutex_unlock(&vreg->pc_lock);
+
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+	else
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_PIN_CTRL);
+
+	return rc;
+}
+
+static int pm8xxx_vs_pin_control_disable(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc;
+
+	mutex_lock(&vreg->pc_lock);
+
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, 0,
+				      VS_PIN_CTRL_MASK, &vreg->ctrl_reg);
+
+	if (!rc)
+		vreg->is_enabled_pc = false;
+
+	mutex_unlock(&vreg->pc_lock);
+
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+	else
+		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_PIN_CTRL);
+
+	return rc;
+}
+
+static int pm8xxx_enable_time(struct regulator_dev *rdev)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+
+	return vreg->pdata.enable_time;
+}
+
+static const char const *pm8xxx_print_actions[] = {
+	[PM8XXX_REGULATOR_ACTION_INIT]		= "initial    ",
+	[PM8XXX_REGULATOR_ACTION_ENABLE]	= "enable     ",
+	[PM8XXX_REGULATOR_ACTION_DISABLE]	= "disable    ",
+	[PM8XXX_REGULATOR_ACTION_VOLTAGE]	= "set voltage",
+	[PM8XXX_REGULATOR_ACTION_MODE]		= "set mode   ",
+	[PM8XXX_REGULATOR_ACTION_PIN_CTRL]	= "pin control",
+};
+
+static void pm8xxx_vreg_show_state(struct regulator_dev *rdev,
+				   enum pm8xxx_regulator_action action)
+{
+	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+	int uV, pc;
+	unsigned int mode;
+	const char *pc_en0 = "", *pc_en1 = "", *pc_en2 = "", *pc_en3 = "";
+	const char *pc_total = "";
+	const char *action_label = pm8xxx_print_actions[action];
+	const char *enable_label;
+
+	mutex_lock(&vreg->pc_lock);
+
+	/*
+	 * Do not print unless REQUEST is specified and SSBI writes have taken
+	 * place, or DUPLICATE is specified.
+	 */
+	if (!((pm8xxx_vreg_debug_mask & PM8XXX_VREG_DEBUG_DUPLICATE)
+	      || ((pm8xxx_vreg_debug_mask & PM8XXX_VREG_DEBUG_REQUEST)
+		  && (vreg->write_count != vreg->prev_write_count)))) {
+		mutex_unlock(&vreg->pc_lock);
+		return;
+	}
+
+	vreg->prev_write_count = vreg->write_count;
+
+	pc = vreg->pdata.pin_ctrl;
+	if (vreg->is_enabled_pc) {
+		if (pc & PM8XXX_VREG_PIN_CTRL_EN0)
+			pc_en0 = " EN0";
+		if (pc & PM8XXX_VREG_PIN_CTRL_EN1)
+			pc_en1 = " EN1";
+		if (pc & PM8XXX_VREG_PIN_CTRL_EN2)
+			pc_en2 = " EN2";
+		if (pc & PM8XXX_VREG_PIN_CTRL_EN3)
+			pc_en3 = " EN3";
+		if (pc == PM8XXX_VREG_PIN_CTRL_NONE)
+			pc_total = " none";
+	} else {
+		pc_total = " none";
+	}
+
+	mutex_unlock(&vreg->pc_lock);
+
+	enable_label = pm8xxx_vreg_is_enabled(rdev) ? "on " : "off";
+
+	switch (vreg->type) {
+	case PM8XXX_REGULATOR_TYPE_PLDO:
+		uV = pm8xxx_pldo_get_voltage(rdev);
+		mode = pm8xxx_ldo_get_mode(rdev);
+		pr_info("%s %-9s: %s, v=%7d uV, mode=%s, pc=%s%s%s%s%s\n",
+			action_label, vreg->rdesc.name, enable_label, uV,
+			(mode == REGULATOR_MODE_NORMAL ? "HPM" : "LPM"),
+			pc_en0, pc_en1, pc_en2, pc_en3, pc_total);
+		break;
+	case PM8XXX_REGULATOR_TYPE_NLDO:
+		uV = pm8xxx_nldo_get_voltage(rdev);
+		mode = pm8xxx_ldo_get_mode(rdev);
+		pr_info("%s %-9s: %s, v=%7d uV, mode=%s, pc=%s%s%s%s%s\n",
+			action_label, vreg->rdesc.name, enable_label, uV,
+			(mode == REGULATOR_MODE_NORMAL ? "HPM" : "LPM"),
+			pc_en0, pc_en1, pc_en2, pc_en3, pc_total);
+		break;
+	case PM8XXX_REGULATOR_TYPE_NLDO1200:
+		uV = pm8xxx_nldo1200_get_voltage(rdev);
+		mode = pm8xxx_nldo1200_get_mode(rdev);
+		pr_info("%s %-9s: %s, v=%7d uV, mode=%s\n",
+			action_label, vreg->rdesc.name, enable_label, uV,
+			(mode == REGULATOR_MODE_NORMAL ? "HPM" : "LPM"));
+		break;
+	case PM8XXX_REGULATOR_TYPE_SMPS:
+		uV = pm8xxx_smps_get_voltage(rdev);
+		mode = pm8xxx_smps_get_mode(rdev);
+		pr_info("%s %-9s: %s, v=%7d uV, mode=%s, pc=%s%s%s%s%s\n",
+			action_label, vreg->rdesc.name, enable_label, uV,
+			(mode == REGULATOR_MODE_NORMAL ? "HPM" : "LPM"),
+			pc_en0, pc_en1, pc_en2, pc_en3, pc_total);
+		break;
+	case PM8XXX_REGULATOR_TYPE_FTSMPS:
+		uV = pm8xxx_ftsmps_get_voltage(rdev);
+		mode = pm8xxx_ftsmps_get_mode(rdev);
+		pr_info("%s %-9s: %s, v=%7d uV, mode=%s\n",
+			action_label, vreg->rdesc.name, enable_label, uV,
+			(mode == REGULATOR_MODE_NORMAL ? "HPM" : "LPM"));
+		break;
+	case PM8XXX_REGULATOR_TYPE_VS:
+		pr_info("%s %-9s: %s, pc=%s%s%s%s%s\n",
+			action_label, vreg->rdesc.name, enable_label,
+			pc_en0, pc_en1, pc_en2, pc_en3, pc_total);
+		break;
+	case PM8XXX_REGULATOR_TYPE_VS300:
+		pr_info("%s %-9s: %s\n",
+			action_label, vreg->rdesc.name, enable_label);
+		break;
+	case PM8XXX_REGULATOR_TYPE_NCP:
+		uV = pm8xxx_ncp_get_voltage(rdev);
+		pr_info("%s %-9s: %s, v=%7d uV\n",
+			action_label, vreg->rdesc.name, enable_label, uV);
+		break;
+	case PM8XXX_REGULATOR_TYPE_BOOST:
+		uV = pm8xxx_boost_get_voltage(rdev);
+		pr_info("%s %-9s: %s, v=%7d uV\n",
+			action_label, vreg->rdesc.name, enable_label, uV);
+		break;
+	default:
+		break;
+	}
+}
+
+/* Real regulator operations. */
+static struct regulator_ops pm8xxx_pldo_ops = {
+	.enable			= pm8xxx_ldo_enable,
+	.disable		= pm8xxx_ldo_disable,
+	.is_enabled		= pm8xxx_vreg_is_enabled,
+	.set_voltage		= pm8xxx_pldo_set_voltage,
+	.get_voltage		= pm8xxx_pldo_get_voltage,
+	.list_voltage		= pm8xxx_pldo_list_voltage,
+	.set_mode		= pm8xxx_ldo_set_mode,
+	.get_mode		= pm8xxx_ldo_get_mode,
+	.get_optimum_mode	= pm8xxx_vreg_get_optimum_mode,
+	.enable_time		= pm8xxx_enable_time,
+};
+
+static struct regulator_ops pm8xxx_nldo_ops = {
+	.enable			= pm8xxx_ldo_enable,
+	.disable		= pm8xxx_ldo_disable,
+	.is_enabled		= pm8xxx_vreg_is_enabled,
+	.set_voltage		= pm8xxx_nldo_set_voltage,
+	.get_voltage		= pm8xxx_nldo_get_voltage,
+	.list_voltage		= pm8xxx_nldo_list_voltage,
+	.set_mode		= pm8xxx_ldo_set_mode,
+	.get_mode		= pm8xxx_ldo_get_mode,
+	.get_optimum_mode	= pm8xxx_vreg_get_optimum_mode,
+	.enable_time		= pm8xxx_enable_time,
+};
+
+static struct regulator_ops pm8xxx_nldo1200_ops = {
+	.enable			= pm8xxx_nldo1200_enable,
+	.disable		= pm8xxx_nldo1200_disable,
+	.is_enabled		= pm8xxx_vreg_is_enabled,
+	.set_voltage		= pm8xxx_nldo1200_set_voltage,
+	.get_voltage		= pm8xxx_nldo1200_get_voltage,
+	.list_voltage		= pm8xxx_nldo1200_list_voltage,
+	.set_mode		= pm8xxx_nldo1200_set_mode,
+	.get_mode		= pm8xxx_nldo1200_get_mode,
+	.get_optimum_mode	= pm8xxx_vreg_get_optimum_mode,
+	.enable_time		= pm8xxx_enable_time,
+};
+
+static struct regulator_ops pm8xxx_smps_ops = {
+	.enable			= pm8xxx_smps_enable,
+	.disable		= pm8xxx_smps_disable,
+	.is_enabled		= pm8xxx_vreg_is_enabled,
+	.set_voltage		= pm8xxx_smps_set_voltage,
+	.get_voltage		= pm8xxx_smps_get_voltage,
+	.list_voltage		= pm8xxx_smps_list_voltage,
+	.set_mode		= pm8xxx_smps_set_mode,
+	.get_mode		= pm8xxx_smps_get_mode,
+	.get_optimum_mode	= pm8xxx_vreg_get_optimum_mode,
+	.enable_time		= pm8xxx_enable_time,
+};
+
+static struct regulator_ops pm8xxx_ftsmps_ops = {
+	.enable			= pm8xxx_ftsmps_enable,
+	.disable		= pm8xxx_ftsmps_disable,
+	.is_enabled		= pm8xxx_vreg_is_enabled,
+	.set_voltage		= pm8xxx_ftsmps_set_voltage,
+	.get_voltage		= pm8xxx_ftsmps_get_voltage,
+	.list_voltage		= pm8xxx_ftsmps_list_voltage,
+	.set_mode		= pm8xxx_ftsmps_set_mode,
+	.get_mode		= pm8xxx_ftsmps_get_mode,
+	.get_optimum_mode	= pm8xxx_vreg_get_optimum_mode,
+	.enable_time		= pm8xxx_enable_time,
+};
+
+static struct regulator_ops pm8xxx_vs_ops = {
+	.enable			= pm8xxx_vs_enable,
+	.disable		= pm8xxx_vs_disable,
+	.is_enabled		= pm8xxx_vreg_is_enabled,
+	.enable_time		= pm8xxx_enable_time,
+};
+
+static struct regulator_ops pm8xxx_vs300_ops = {
+	.enable			= pm8xxx_vs300_enable,
+	.disable		= pm8xxx_vs300_disable,
+	.is_enabled		= pm8xxx_vreg_is_enabled,
+	.enable_time		= pm8xxx_enable_time,
+};
+
+static struct regulator_ops pm8xxx_ncp_ops = {
+	.enable			= pm8xxx_ncp_enable,
+	.disable		= pm8xxx_ncp_disable,
+	.is_enabled		= pm8xxx_vreg_is_enabled,
+	.set_voltage		= pm8xxx_ncp_set_voltage,
+	.get_voltage		= pm8xxx_ncp_get_voltage,
+	.list_voltage		= pm8xxx_ncp_list_voltage,
+	.enable_time		= pm8xxx_enable_time,
+};
+
+static struct regulator_ops pm8xxx_boost_ops = {
+	.enable			= pm8xxx_boost_enable,
+	.disable		= pm8xxx_boost_disable,
+	.is_enabled		= pm8xxx_vreg_is_enabled,
+	.set_voltage		= pm8xxx_boost_set_voltage,
+	.get_voltage		= pm8xxx_boost_get_voltage,
+	.list_voltage		= pm8xxx_boost_list_voltage,
+	.enable_time		= pm8xxx_enable_time,
+};
+
+/* Pin control regulator operations. */
+static struct regulator_ops pm8xxx_ldo_pc_ops = {
+	.enable			= pm8xxx_ldo_pin_control_enable,
+	.disable		= pm8xxx_ldo_pin_control_disable,
+	.is_enabled		= pm8xxx_vreg_pin_control_is_enabled,
+};
+
+static struct regulator_ops pm8xxx_smps_pc_ops = {
+	.enable			= pm8xxx_smps_pin_control_enable,
+	.disable		= pm8xxx_smps_pin_control_disable,
+	.is_enabled		= pm8xxx_vreg_pin_control_is_enabled,
+};
+
+static struct regulator_ops pm8xxx_vs_pc_ops = {
+	.enable			= pm8xxx_vs_pin_control_enable,
+	.disable		= pm8xxx_vs_pin_control_disable,
+	.is_enabled		= pm8xxx_vreg_pin_control_is_enabled,
+};
+
+static struct regulator_ops *pm8xxx_reg_ops[PM8XXX_REGULATOR_TYPE_MAX] = {
+	[PM8XXX_REGULATOR_TYPE_PLDO]		= &pm8xxx_pldo_ops,
+	[PM8XXX_REGULATOR_TYPE_NLDO]		= &pm8xxx_nldo_ops,
+	[PM8XXX_REGULATOR_TYPE_NLDO1200]	= &pm8xxx_nldo1200_ops,
+	[PM8XXX_REGULATOR_TYPE_SMPS]		= &pm8xxx_smps_ops,
+	[PM8XXX_REGULATOR_TYPE_FTSMPS]		= &pm8xxx_ftsmps_ops,
+	[PM8XXX_REGULATOR_TYPE_VS]		= &pm8xxx_vs_ops,
+	[PM8XXX_REGULATOR_TYPE_VS300]		= &pm8xxx_vs300_ops,
+	[PM8XXX_REGULATOR_TYPE_NCP]		= &pm8xxx_ncp_ops,
+	[PM8XXX_REGULATOR_TYPE_BOOST]		= &pm8xxx_boost_ops,
+};
+
+static struct regulator_ops *pm8xxx_reg_pc_ops[PM8XXX_REGULATOR_TYPE_MAX] = {
+	[PM8XXX_REGULATOR_TYPE_PLDO]		= &pm8xxx_ldo_pc_ops,
+	[PM8XXX_REGULATOR_TYPE_NLDO]		= &pm8xxx_ldo_pc_ops,
+	[PM8XXX_REGULATOR_TYPE_SMPS]		= &pm8xxx_smps_pc_ops,
+	[PM8XXX_REGULATOR_TYPE_VS]		= &pm8xxx_vs_pc_ops,
+};
+
+static unsigned pm8xxx_n_voltages[PM8XXX_REGULATOR_TYPE_MAX] = {
+	[PM8XXX_REGULATOR_TYPE_PLDO]		= PLDO_SET_POINTS,
+	[PM8XXX_REGULATOR_TYPE_NLDO]		= NLDO_SET_POINTS,
+	[PM8XXX_REGULATOR_TYPE_NLDO1200]	= NLDO1200_SET_POINTS,
+	[PM8XXX_REGULATOR_TYPE_SMPS]		= SMPS_ADVANCED_SET_POINTS,
+	[PM8XXX_REGULATOR_TYPE_FTSMPS]		= FTSMPS_SET_POINTS,
+	[PM8XXX_REGULATOR_TYPE_VS]		= 0,
+	[PM8XXX_REGULATOR_TYPE_VS300]		= 0,
+	[PM8XXX_REGULATOR_TYPE_NCP]		= NCP_SET_POINTS,
+	[PM8XXX_REGULATOR_TYPE_BOOST]		= BOOST_SET_POINTS,
+};
+
+static int pm8xxx_init_ldo(struct pm8xxx_vreg *vreg, bool is_real)
+{
+	int rc = 0;
+	int i;
+	u8 bank;
+
+	/* Save the current control register state. */
+	rc = pm8xxx_readb(vreg->dev->parent, vreg->ctrl_addr, &vreg->ctrl_reg);
+	if (rc)
+		goto bail;
+
+	/* Save the current test register state. */
+	for (i = 0; i < LDO_TEST_BANKS; i++) {
+		bank = REGULATOR_BANK_SEL(i);
+		rc = pm8xxx_writeb(vreg->dev->parent, vreg->test_addr, bank);
+		if (rc)
+			goto bail;
+
+		rc = pm8xxx_readb(vreg->dev->parent, vreg->test_addr,
+				  &vreg->test_reg[i]);
+		if (rc)
+			goto bail;
+		vreg->test_reg[i] |= REGULATOR_BANK_WRITE;
+	}
+
+	if (is_real) {
+		/* Set pull down enable based on platform data. */
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+		      (vreg->pdata.pull_down_enable ? LDO_PULL_DOWN_ENABLE : 0),
+		      LDO_PULL_DOWN_ENABLE_MASK, &vreg->ctrl_reg);
+
+		vreg->is_enabled = !!_pm8xxx_vreg_is_enabled(vreg);
+
+		vreg->mode = ((vreg->ctrl_reg & LDO_CTRL_PM_MASK)
+					== LDO_CTRL_PM_LPM ?
+				REGULATOR_MODE_IDLE : REGULATOR_MODE_NORMAL);
+	}
+bail:
+	if (rc)
+		vreg_err(vreg, "pm8xxx_readb/writeb failed, rc=%d\n", rc);
+
+	return rc;
+}
+
+static int pm8xxx_init_nldo1200(struct pm8xxx_vreg *vreg)
+{
+	int rc = 0;
+	int i;
+	u8 bank;
+
+	/* Save the current control register state. */
+	rc = pm8xxx_readb(vreg->dev->parent, vreg->ctrl_addr, &vreg->ctrl_reg);
+	if (rc)
+		goto bail;
+
+	/* Save the current test register state. */
+	for (i = 0; i < LDO_TEST_BANKS; i++) {
+		bank = REGULATOR_BANK_SEL(i);
+		rc = pm8xxx_writeb(vreg->dev->parent, vreg->test_addr, bank);
+		if (rc)
+			goto bail;
+
+		rc = pm8xxx_readb(vreg->dev->parent, vreg->test_addr,
+				  &vreg->test_reg[i]);
+		if (rc)
+			goto bail;
+		vreg->test_reg[i] |= REGULATOR_BANK_WRITE;
+	}
+
+	vreg->save_uV = _pm8xxx_nldo1200_get_voltage(vreg);
+
+	/* Set pull down enable based on platform data. */
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+		 (vreg->pdata.pull_down_enable ? NLDO1200_PULL_DOWN_ENABLE : 0)
+		 | REGULATOR_BANK_SEL(1) | REGULATOR_BANK_WRITE,
+		 NLDO1200_PULL_DOWN_ENABLE_MASK | REGULATOR_BANK_MASK,
+		 &vreg->test_reg[1]);
+
+bail:
+	if (rc)
+		vreg_err(vreg, "pm8xxx_readb/writeb failed, rc=%d\n", rc);
+
+	return rc;
+}
+
+static int pm8xxx_init_smps(struct pm8xxx_vreg *vreg, bool is_real)
+{
+	int rc = 0;
+	int i;
+	u8 bank;
+
+	/* Save the current control register state. */
+	rc = pm8xxx_readb(vreg->dev->parent, vreg->ctrl_addr, &vreg->ctrl_reg);
+	if (rc)
+		goto bail;
+
+	/* Save the current test2 register state. */
+	for (i = 0; i < SMPS_TEST_BANKS; i++) {
+		bank = REGULATOR_BANK_SEL(i);
+		rc = pm8xxx_writeb(vreg->dev->parent, vreg->test_addr, bank);
+		if (rc)
+			goto bail;
+
+		rc = pm8xxx_readb(vreg->dev->parent, vreg->test_addr,
+				  &vreg->test_reg[i]);
+		if (rc)
+			goto bail;
+		vreg->test_reg[i] |= REGULATOR_BANK_WRITE;
+	}
+
+	/* Save the current clock control register state. */
+	rc = pm8xxx_readb(vreg->dev->parent, vreg->clk_ctrl_addr,
+			  &vreg->clk_ctrl_reg);
+	if (rc)
+		goto bail;
+
+	/* Save the current sleep control register state. */
+	rc = pm8xxx_readb(vreg->dev->parent, vreg->sleep_ctrl_addr,
+			  &vreg->sleep_ctrl_reg);
+	if (rc)
+		goto bail;
+
+	vreg->save_uV = _pm8xxx_smps_get_voltage(vreg);
+
+	if (is_real) {
+		/* Set advanced mode pull down enable based on platform data. */
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+			(vreg->pdata.pull_down_enable
+				? SMPS_ADVANCED_PULL_DOWN_ENABLE : 0)
+			| REGULATOR_BANK_SEL(6) | REGULATOR_BANK_WRITE,
+			REGULATOR_BANK_MASK | SMPS_ADVANCED_PULL_DOWN_ENABLE,
+			&vreg->test_reg[6]);
+		if (rc)
+			goto bail;
+
+		vreg->is_enabled = !!_pm8xxx_vreg_is_enabled(vreg);
+
+		vreg->mode = ((vreg->clk_ctrl_reg & SMPS_CLK_CTRL_MASK)
+					== SMPS_CLK_CTRL_PFM ?
+				REGULATOR_MODE_IDLE : REGULATOR_MODE_NORMAL);
+	}
+
+	if (!SMPS_IN_ADVANCED_MODE(vreg) && is_real) {
+		/* Set legacy mode pull down enable based on platform data. */
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+			(vreg->pdata.pull_down_enable
+				? SMPS_LEGACY_PULL_DOWN_ENABLE : 0),
+			SMPS_LEGACY_PULL_DOWN_ENABLE, &vreg->ctrl_reg);
+		if (rc)
+			goto bail;
+	}
+
+bail:
+	if (rc)
+		vreg_err(vreg, "pm8xxx_readb/writeb failed, rc=%d\n", rc);
+
+	return rc;
+}
+
+static int pm8xxx_init_ftsmps(struct pm8xxx_vreg *vreg)
+{
+	int rc, i;
+	u8 bank;
+
+	/* Save the current control register state. */
+	rc = pm8xxx_readb(vreg->dev->parent, vreg->ctrl_addr, &vreg->ctrl_reg);
+	if (rc)
+		goto bail;
+
+	/* Store current regulator register values. */
+	rc = pm8xxx_readb(vreg->dev->parent, vreg->pfm_ctrl_addr,
+			  &vreg->pfm_ctrl_reg);
+	if (rc)
+		goto bail;
+
+	rc = pm8xxx_readb(vreg->dev->parent, vreg->pwr_cnfg_addr,
+			  &vreg->pwr_cnfg_reg);
+	if (rc)
+		goto bail;
+
+	/* Save the current fts_cnfg1 register state (uses 'test' member). */
+	for (i = 0; i < SMPS_TEST_BANKS; i++) {
+		bank = REGULATOR_BANK_SEL(i);
+		rc = pm8xxx_writeb(vreg->dev->parent, vreg->test_addr, bank);
+		if (rc)
+			goto bail;
+
+		rc = pm8xxx_readb(vreg->dev->parent, vreg->test_addr,
+				  &vreg->test_reg[i]);
+		if (rc)
+			goto bail;
+		vreg->test_reg[i] |= REGULATOR_BANK_WRITE;
+	}
+
+	vreg->save_uV = _pm8xxx_ftsmps_get_voltage(vreg);
+
+	/* Set pull down enable based on platform data. */
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->pwr_cnfg_addr,
+		(vreg->pdata.pull_down_enable ? FTSMPS_PULL_DOWN_ENABLE : 0),
+		FTSMPS_PULL_DOWN_ENABLE_MASK, &vreg->pwr_cnfg_reg);
+
+bail:
+	if (rc)
+		vreg_err(vreg, "pm8xxx_readb/writeb failed, rc=%d\n", rc);
+
+	return rc;
+}
+
+static int pm8xxx_init_vs(struct pm8xxx_vreg *vreg, bool is_real)
+{
+	int rc = 0;
+
+	/* Save the current control register state. */
+	rc = pm8xxx_readb(vreg->dev->parent, vreg->ctrl_addr, &vreg->ctrl_reg);
+	if (rc) {
+		vreg_err(vreg, "pm8xxx_readb failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	/* Save the current test register state. */
+	rc = pm8xxx_readb(vreg->dev->parent, vreg->test_addr,
+		&vreg->test_reg[0]);
+	if (rc) {
+		vreg_err(vreg, "pm8xxx_readb failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	if (is_real) {
+		/* Set pull down enable based on platform data. */
+		rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+		       (vreg->pdata.pull_down_enable ? VS_PULL_DOWN_ENABLE
+						     : VS_PULL_DOWN_DISABLE),
+		       VS_PULL_DOWN_ENABLE_MASK, &vreg->ctrl_reg);
+
+		if (rc)
+			vreg_err(vreg,
+				"pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+
+		vreg->is_enabled = !!_pm8xxx_vreg_is_enabled(vreg);
+	}
+
+	return rc;
+}
+
+static int pm8xxx_init_vs300(struct pm8xxx_vreg *vreg)
+{
+	int rc;
+
+	/* Save the current control register state. */
+	rc = pm8xxx_readb(vreg->dev->parent, vreg->ctrl_addr, &vreg->ctrl_reg);
+	if (rc) {
+		vreg_err(vreg, "pm8xxx_readb failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	/* Save the current test register state. */
+	rc = pm8xxx_readb(vreg->dev->parent, vreg->test_addr,
+		&vreg->test_reg[0]);
+	if (rc) {
+		vreg_err(vreg, "pm8xxx_readb failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	/* Set pull down enable based on platform data. */
+	rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+		    (vreg->pdata.pull_down_enable ? VS300_PULL_DOWN_ENABLE : 0),
+		    VS300_PULL_DOWN_ENABLE_MASK, &vreg->ctrl_reg);
+
+	if (rc)
+		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+
+	return rc;
+}
+
+static int pm8xxx_init_ncp(struct pm8xxx_vreg *vreg)
+{
+	int rc;
+
+	/* Save the current control register state. */
+	rc = pm8xxx_readb(vreg->dev->parent, vreg->ctrl_addr, &vreg->ctrl_reg);
+	if (rc) {
+		vreg_err(vreg, "pm8xxx_readb failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	return rc;
+}
+
+static int pm8xxx_init_boost(struct pm8xxx_vreg *vreg)
+{
+	int rc;
+
+	/* Save the current control register state. */
+	rc = pm8xxx_readb(vreg->dev->parent, vreg->ctrl_addr, &vreg->ctrl_reg);
+	if (rc) {
+		vreg_err(vreg, "pm8xxx_readb failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	return rc;
+}
+
+static int __devinit pm8xxx_vreg_probe(struct platform_device *pdev)
+{
+	struct pm8xxx_regulator_core_platform_data *core_data;
+	const struct pm8xxx_regulator_platform_data *pdata;
+	enum pm8xxx_vreg_pin_function pin_fn;
+	struct regulator_desc *rdesc;
+	struct pm8xxx_vreg *vreg;
+	unsigned pin_ctrl;
+	int rc = 0;
+
+	if (pdev == NULL) {
+		pr_err("no platform device specified\n");
+		return -EINVAL;
+	}
+
+	core_data = pdev->dev.platform_data;
+	if (core_data == NULL) {
+		pr_err("no core data specified\n");
+		return -EINVAL;
+	}
+
+	pdata = core_data->pdata;
+	vreg = core_data->vreg;
+	if (pdata == NULL) {
+		pr_err("no pdata specified\n");
+		return -EINVAL;
+	} else if (vreg == NULL) {
+		pr_err("no vreg specified\n");
+		return -EINVAL;
+	}
+
+	if (vreg->rdesc.name == NULL) {
+		pr_err("regulator name missing\n");
+		return -EINVAL;
+	} else if (vreg->type < 0 || vreg->type >= PM8XXX_REGULATOR_TYPE_MAX) {
+		pr_err("%s: regulator type=%d is invalid\n", vreg->rdesc.name,
+			vreg->type);
+		return -EINVAL;
+	} else if (core_data->is_pin_controlled
+		   && pm8xxx_reg_pc_ops[vreg->type] == NULL) {
+		pr_err("%s: regulator type=%d does not support pin control\n",
+			vreg->rdesc.name, vreg->type);
+		return -EINVAL;
+	} else if (core_data->is_pin_controlled
+		   && vreg->rdesc_pc.name == NULL) {
+		pr_err("%s: regulator pin control name missing\n",
+			vreg->rdesc.name);
+		return -EINVAL;
+	}
+
+	if (core_data->is_pin_controlled)
+		rdesc = &vreg->rdesc_pc;
+	else
+		rdesc = &vreg->rdesc;
+	if (!pdata) {
+		pr_err("%s requires platform data\n", vreg->rdesc.name);
+		return -EINVAL;
+	}
+
+	rdesc->id    = pdev->id;
+	rdesc->owner = THIS_MODULE;
+	rdesc->type  = REGULATOR_VOLTAGE;
+	if (core_data->is_pin_controlled) {
+		rdesc->ops = pm8xxx_reg_pc_ops[vreg->type];
+		rdesc->n_voltages = 0;
+	} else {
+		rdesc->ops = pm8xxx_reg_ops[vreg->type];
+		rdesc->n_voltages = pm8xxx_n_voltages[vreg->type];
+	}
+
+	mutex_lock(&vreg->pc_lock);
+
+	if (!core_data->is_pin_controlled) {
+		/* Do not modify pin control and pin function values. */
+		pin_ctrl = vreg->pdata.pin_ctrl;
+		pin_fn = vreg->pdata.pin_fn;
+		memcpy(&(vreg->pdata), pdata,
+			sizeof(struct pm8xxx_regulator_platform_data));
+		vreg->pdata.pin_ctrl = pin_ctrl;
+		vreg->pdata.pin_fn = pin_fn;
+		vreg->dev = &pdev->dev;
+	} else {
+		/* Pin control regulator */
+		if ((pdata->pin_ctrl & PM8XXX_VREG_PIN_CTRL_ALL)
+		    == PM8XXX_VREG_PIN_CTRL_NONE) {
+			pr_err("%s: no pin control input specified\n",
+				vreg->rdesc.name);
+			mutex_unlock(&vreg->pc_lock);
+			return -EINVAL;
+		}
+		vreg->pdata.pin_ctrl = pdata->pin_ctrl;
+		vreg->pdata.pin_fn = pdata->pin_fn;
+		vreg->dev_pc = &pdev->dev;
+		if (!vreg->dev)
+			vreg->dev = &pdev->dev;
+	}
+
+	/* Initialize register values. */
+	switch (vreg->type) {
+	case PM8XXX_REGULATOR_TYPE_PLDO:
+	case PM8XXX_REGULATOR_TYPE_NLDO:
+		rc = pm8xxx_init_ldo(vreg, !core_data->is_pin_controlled);
+		break;
+	case PM8XXX_REGULATOR_TYPE_NLDO1200:
+		rc = pm8xxx_init_nldo1200(vreg);
+		break;
+	case PM8XXX_REGULATOR_TYPE_SMPS:
+		rc = pm8xxx_init_smps(vreg, !core_data->is_pin_controlled);
+		break;
+	case PM8XXX_REGULATOR_TYPE_FTSMPS:
+		rc = pm8xxx_init_ftsmps(vreg);
+		break;
+	case PM8XXX_REGULATOR_TYPE_VS:
+		rc = pm8xxx_init_vs(vreg, !core_data->is_pin_controlled);
+		break;
+	case PM8XXX_REGULATOR_TYPE_VS300:
+		rc = pm8xxx_init_vs300(vreg);
+		break;
+	case PM8XXX_REGULATOR_TYPE_NCP:
+		rc = pm8xxx_init_ncp(vreg);
+		break;
+	case PM8XXX_REGULATOR_TYPE_BOOST:
+		rc = pm8xxx_init_boost(vreg);
+		break;
+	default:
+		break;
+	}
+
+	mutex_unlock(&vreg->pc_lock);
+
+	if (rc)
+		goto bail;
+
+	if (!core_data->is_pin_controlled) {
+		vreg->rdev = regulator_register(rdesc, &pdev->dev,
+				&(pdata->init_data), vreg, NULL);
+		if (IS_ERR(vreg->rdev)) {
+			rc = PTR_ERR(vreg->rdev);
+			vreg->rdev = NULL;
+			pr_err("regulator_register failed: %s, rc=%d\n",
+				vreg->rdesc.name, rc);
+		}
+	} else {
+		vreg->rdev_pc = regulator_register(rdesc, &pdev->dev,
+				&(pdata->init_data), vreg, NULL);
+		if (IS_ERR(vreg->rdev_pc)) {
+			rc = PTR_ERR(vreg->rdev_pc);
+			vreg->rdev_pc = NULL;
+			pr_err("regulator_register failed: %s, rc=%d\n",
+				vreg->rdesc.name, rc);
+		}
+	}
+	if ((pm8xxx_vreg_debug_mask & PM8XXX_VREG_DEBUG_INIT) && !rc
+	    && vreg->rdev)
+		pm8xxx_vreg_show_state(vreg->rdev,
+					PM8XXX_REGULATOR_ACTION_INIT);
+
+	platform_set_drvdata(pdev, core_data);
+
+bail:
+	if (rc)
+		pr_err("error for %s, rc=%d\n", vreg->rdesc.name, rc);
+
+	return rc;
+}
+
+static int __devexit pm8xxx_vreg_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_regulator_core_platform_data *core_data;
+
+	core_data = platform_get_drvdata(pdev);
+	platform_set_drvdata(pdev, NULL);
+
+	if (core_data) {
+		if (core_data->is_pin_controlled)
+			regulator_unregister(core_data->vreg->rdev_pc);
+		else
+			regulator_unregister(core_data->vreg->rdev);
+	}
+
+	return 0;
+}
+
+static struct platform_driver pm8xxx_vreg_driver = {
+	.probe	= pm8xxx_vreg_probe,
+	.remove	= __devexit_p(pm8xxx_vreg_remove),
+	.driver	= {
+		.name	= PM8XXX_REGULATOR_DEV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init pm8xxx_vreg_init(void)
+{
+	return platform_driver_register(&pm8xxx_vreg_driver);
+}
+postcore_initcall(pm8xxx_vreg_init);
+
+static void __exit pm8xxx_vreg_exit(void)
+{
+	platform_driver_unregister(&pm8xxx_vreg_driver);
+}
+module_exit(pm8xxx_vreg_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC PM8XXX regulator driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8XXX_REGULATOR_DEV_NAME);
diff --git a/drivers/regulator/pmic8058-regulator.c b/drivers/regulator/pmic8058-regulator.c
new file mode 100644
index 0000000..f9b0319
--- /dev/null
+++ b/drivers/regulator/pmic8058-regulator.c
@@ -0,0 +1,1756 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/mfd/pmic8058.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/regulator/pmic8058-regulator.h>
+
+/* Regulator types */
+#define REGULATOR_TYPE_LDO		0
+#define REGULATOR_TYPE_SMPS		1
+#define REGULATOR_TYPE_LVS		2
+#define REGULATOR_TYPE_NCP		3
+
+/* Common masks */
+#define REGULATOR_EN_MASK		0x80
+
+#define REGULATOR_BANK_MASK		0xF0
+#define REGULATOR_BANK_SEL(n)		((n) << 4)
+#define REGULATOR_BANK_WRITE		0x80
+
+#define LDO_TEST_BANKS			7
+#define SMPS_TEST_BANKS			8
+#define REGULATOR_TEST_BANKS_MAX	SMPS_TEST_BANKS
+
+/* LDO programming */
+
+/* CTRL register */
+#define LDO_ENABLE_MASK			0x80
+#define LDO_ENABLE			0x80
+#define LDO_PULL_DOWN_ENABLE_MASK	0x40
+#define LDO_PULL_DOWN_ENABLE		0x40
+
+#define LDO_CTRL_PM_MASK		0x20
+#define LDO_CTRL_PM_HPM			0x00
+#define LDO_CTRL_PM_LPM			0x20
+
+#define LDO_CTRL_VPROG_MASK		0x1F
+
+/* TEST register bank 0 */
+#define LDO_TEST_LPM_MASK		0x40
+#define LDO_TEST_LPM_SEL_CTRL		0x00
+#define LDO_TEST_LPM_SEL_TCXO		0x40
+
+/* TEST register bank 2 */
+#define LDO_TEST_VPROG_UPDATE_MASK	0x08
+#define LDO_TEST_RANGE_SEL_MASK		0x04
+#define LDO_TEST_FINE_STEP_MASK		0x02
+#define LDO_TEST_FINE_STEP_SHIFT	1
+
+/* TEST register bank 4 */
+#define LDO_TEST_RANGE_EXT_MASK		0x01
+
+/* TEST register bank 5 */
+#define LDO_TEST_PIN_CTRL_MASK		0x0F
+#define LDO_TEST_PIN_CTRL_EN3		0x08
+#define LDO_TEST_PIN_CTRL_EN2		0x04
+#define LDO_TEST_PIN_CTRL_EN1		0x02
+#define LDO_TEST_PIN_CTRL_EN0		0x01
+
+/* TEST register bank 6 */
+#define LDO_TEST_PIN_CTRL_LPM_MASK	0x0F
+
+/* Allowable voltage ranges */
+#define PLDO_LOW_UV_MIN			750000
+#define PLDO_LOW_UV_MAX			1537500
+#define PLDO_LOW_FINE_STEP_UV		12500
+
+#define PLDO_NORM_UV_MIN		1500000
+#define PLDO_NORM_UV_MAX		3075000
+#define PLDO_NORM_FINE_STEP_UV		25000
+
+#define PLDO_HIGH_UV_MIN		1750000
+#define PLDO_HIGH_UV_MAX		4900000
+#define PLDO_HIGH_FINE_STEP_UV		50000
+
+#define NLDO_UV_MIN			750000
+#define NLDO_UV_MAX			1537500
+#define NLDO_FINE_STEP_UV		12500
+
+/* SMPS masks and values */
+
+/* CTRL register */
+
+/* Legacy mode */
+#define SMPS_LEGACY_ENABLE		0x80
+#define SMPS_LEGACY_PULL_DOWN_ENABLE	0x40
+#define SMPS_LEGACY_VREF_SEL_MASK	0x20
+#define SMPS_LEGACY_VPROG_MASK		0x1F
+
+/* Advanced mode */
+#define SMPS_ADVANCED_BAND_MASK		0xC0
+#define SMPS_ADVANCED_BAND_OFF		0x00
+#define SMPS_ADVANCED_BAND_1		0x40
+#define SMPS_ADVANCED_BAND_2		0x80
+#define SMPS_ADVANCED_BAND_3		0xC0
+#define SMPS_ADVANCED_VPROG_MASK	0x3F
+
+/* Legacy mode voltage ranges */
+#define SMPS_MODE1_UV_MIN		1500000
+#define SMPS_MODE1_UV_MAX		3050000
+#define SMPS_MODE1_UV_STEP		50000
+
+#define SMPS_MODE2_UV_MIN		750000
+#define SMPS_MODE2_UV_MAX		1525000
+#define SMPS_MODE2_UV_STEP		25000
+
+#define SMPS_MODE3_UV_MIN		375000
+#define SMPS_MODE3_UV_MAX		1150000
+#define SMPS_MODE3_UV_STEP		25000
+
+/* Advanced mode voltage ranges */
+#define SMPS_BAND3_UV_MIN		1500000
+#define SMPS_BAND3_UV_MAX		3075000
+#define SMPS_BAND3_UV_STEP		25000
+
+#define SMPS_BAND2_UV_MIN		750000
+#define SMPS_BAND2_UV_MAX		1537500
+#define SMPS_BAND2_UV_STEP		12500
+
+#define SMPS_BAND1_UV_MIN		375000
+#define SMPS_BAND1_UV_MAX		1162500
+#define SMPS_BAND1_UV_STEP		12500
+
+#define SMPS_UV_MIN			SMPS_MODE3_UV_MIN
+#define SMPS_UV_MAX			SMPS_MODE1_UV_MAX
+
+/* Test2 register bank 1 */
+#define SMPS_LEGACY_VLOW_SEL_MASK	0x01
+
+/* Test2 register bank 6 */
+#define SMPS_ADVANCED_PULL_DOWN_ENABLE	0x08
+
+/* Test2 register bank 7 */
+#define SMPS_ADVANCED_MODE_MASK		0x02
+#define SMPS_ADVANCED_MODE		0x02
+#define SMPS_LEGACY_MODE		0x00
+
+#define SMPS_IN_ADVANCED_MODE(vreg) \
+	((vreg->test_reg[7] & SMPS_ADVANCED_MODE_MASK) == SMPS_ADVANCED_MODE)
+
+/* BUCK_SLEEP_CNTRL register */
+#define SMPS_PIN_CTRL_MASK		0xF0
+#define SMPS_PIN_CTRL_A1		0x80
+#define SMPS_PIN_CTRL_A0		0x40
+#define SMPS_PIN_CTRL_D1		0x20
+#define SMPS_PIN_CTRL_D0		0x10
+
+#define SMPS_PIN_CTRL_LPM_MASK		0x0F
+#define SMPS_PIN_CTRL_LPM_A1		0x08
+#define SMPS_PIN_CTRL_LPM_A0		0x04
+#define SMPS_PIN_CTRL_LPM_D1		0x02
+#define SMPS_PIN_CTRL_LPM_D0		0x01
+
+/* BUCK_CLOCK_CNTRL register */
+#define SMPS_CLK_DIVIDE2		0x40
+
+#define SMPS_CLK_CTRL_MASK		0x30
+#define SMPS_CLK_CTRL_FOLLOW_TCXO	0x00
+#define SMPS_CLK_CTRL_PWM		0x10
+#define SMPS_CLK_CTRL_PFM		0x20
+
+/* LVS masks and values */
+
+/* CTRL register */
+#define LVS_ENABLE_MASK			0x80
+#define LVS_ENABLE			0x80
+#define LVS_PULL_DOWN_ENABLE_MASK	0x40
+#define LVS_PULL_DOWN_ENABLE		0x00
+#define LVS_PULL_DOWN_DISABLE		0x40
+
+#define LVS_PIN_CTRL_MASK		0x0F
+#define LVS_PIN_CTRL_EN0		0x08
+#define LVS_PIN_CTRL_EN1		0x04
+#define LVS_PIN_CTRL_EN2		0x02
+#define LVS_PIN_CTRL_EN3		0x01
+
+/* NCP masks and values */
+
+/* CTRL register */
+#define NCP_VPROG_MASK			0x1F
+
+#define NCP_UV_MIN			1500000
+#define NCP_UV_MAX			3050000
+#define NCP_UV_STEP			50000
+
+#define GLOBAL_ENABLE_MAX		(2)
+struct pm8058_enable {
+	u16				addr;
+	u8				reg;
+};
+
+struct pm8058_vreg {
+	struct device			*dev;
+	struct pm8058_vreg_pdata	*pdata;
+	struct regulator_dev		*rdev;
+	struct pm8058_enable		*global_enable[GLOBAL_ENABLE_MAX];
+	int				hpm_min_load;
+	int				save_uV;
+	unsigned			pc_vote;
+	unsigned			optimum;
+	unsigned			mode_initialized;
+	u16				ctrl_addr;
+	u16				test_addr;
+	u16				clk_ctrl_addr;
+	u16				sleep_ctrl_addr;
+	u8				type;
+	u8				ctrl_reg;
+	u8				test_reg[REGULATOR_TEST_BANKS_MAX];
+	u8				clk_ctrl_reg;
+	u8				sleep_ctrl_reg;
+	u8				is_nmos;
+	u8				global_enable_mask[GLOBAL_ENABLE_MAX];
+};
+
+#define LDO_M2(_id, _ctrl_addr, _test_addr, _is_nmos, _hpm_min_load, \
+	      _en0, _en0_mask, _en1, _en1_mask) \
+	[PM8058_VREG_ID_##_id] = { \
+		.ctrl_addr = _ctrl_addr, \
+		.test_addr = _test_addr, \
+		.type = REGULATOR_TYPE_LDO, \
+		.hpm_min_load = PM8058_VREG_##_hpm_min_load##_HPM_MIN_LOAD, \
+		.is_nmos = _is_nmos, \
+		.global_enable = { \
+			[0] = _en0, \
+			[1] = _en1, \
+		}, \
+		.global_enable_mask = { \
+			[0] = _en0_mask, \
+			[1] = _en1_mask, \
+		}, \
+	}
+
+#define LDO(_id, _ctrl_addr, _test_addr, _is_nmos, _hpm_min_load, \
+	    _en0, _en0_mask) \
+		LDO_M2(_id, _ctrl_addr, _test_addr, _is_nmos, _hpm_min_load, \
+		      _en0, _en0_mask, NULL, 0)
+
+#define SMPS(_id, _ctrl_addr, _test_addr, _clk_ctrl_addr, _sleep_ctrl_addr, \
+	     _hpm_min_load, _en0, _en0_mask) \
+	[PM8058_VREG_ID_##_id] = { \
+		.ctrl_addr = _ctrl_addr, \
+		.test_addr = _test_addr, \
+		.clk_ctrl_addr = _clk_ctrl_addr, \
+		.sleep_ctrl_addr = _sleep_ctrl_addr, \
+		.type = REGULATOR_TYPE_SMPS, \
+		.hpm_min_load = PM8058_VREG_##_hpm_min_load##_HPM_MIN_LOAD, \
+		.global_enable = { \
+			[0] = _en0, \
+			[1] = NULL, \
+		}, \
+		.global_enable_mask = { \
+			[0] = _en0_mask, \
+			[1] = 0, \
+		}, \
+	}
+
+#define LVS(_id, _ctrl_addr, _en0, _en0_mask) \
+	[PM8058_VREG_ID_##_id] = { \
+		.ctrl_addr = _ctrl_addr, \
+		.type = REGULATOR_TYPE_LVS, \
+		.global_enable = { \
+			[0] = _en0, \
+			[1] = NULL, \
+		}, \
+		.global_enable_mask = { \
+			[0] = _en0_mask, \
+			[1] = 0, \
+		}, \
+	}
+
+#define NCP(_id, _ctrl_addr, _test1) \
+	[PM8058_VREG_ID_##_id] = { \
+		.ctrl_addr = _ctrl_addr, \
+		.type = REGULATOR_TYPE_NCP, \
+		.test_addr = _test1, \
+		.global_enable = { \
+			[0] = NULL, \
+			[1] = NULL, \
+		}, \
+		.global_enable_mask = { \
+			[0] = 0, \
+			[1] = 0, \
+		}, \
+	}
+
+#define MASTER_ENABLE_COUNT	6
+
+#define EN_MSM			0
+#define EN_PH			1
+#define EN_RF			2
+#define EN_GRP_5_4		3
+#define EN_GRP_3_2		4
+#define EN_GRP_1_0		5
+
+/* Master regulator control registers */
+static struct pm8058_enable m_en[MASTER_ENABLE_COUNT] = {
+	[EN_MSM] = {
+		.addr = 0x018, /* VREG_EN_MSM */
+	},
+	[EN_PH] = {
+		.addr = 0x019, /* VREG_EN_PH */
+	},
+	[EN_RF] = {
+		.addr = 0x01A, /* VREG_EN_RF */
+	},
+	[EN_GRP_5_4] = {
+		.addr = 0x1C8, /* VREG_EN_MSM_GRP_5-4 */
+	},
+	[EN_GRP_3_2] = {
+		.addr = 0x1C9, /* VREG_EN_MSM_GRP_3-2 */
+	},
+	[EN_GRP_1_0] = {
+		.addr = 0x1CA, /* VREG_EN_MSM_GRP_1-0 */
+	},
+};
+
+
+static struct pm8058_vreg pm8058_vreg[] = {
+	/*  id   ctrl   test  n/p hpm_min  m_en		      m_en_mask */
+	LDO(L0,  0x009, 0x065, 1, LDO_150, &m_en[EN_GRP_5_4], BIT(3)),
+	LDO(L1,  0x00A, 0x066, 1, LDO_300, &m_en[EN_GRP_5_4], BIT(6) | BIT(2)),
+	LDO(L2,  0x00B, 0x067, 0, LDO_300, &m_en[EN_GRP_3_2], BIT(2)),
+	LDO(L3,  0x00C, 0x068, 0, LDO_150, &m_en[EN_GRP_1_0], BIT(1)),
+	LDO(L4,  0x00D, 0x069, 0, LDO_50,  &m_en[EN_MSM],     0),
+	LDO(L5,  0x00E, 0x06A, 0, LDO_300, &m_en[EN_GRP_1_0], BIT(7)),
+	LDO(L6,  0x00F, 0x06B, 0, LDO_50,  &m_en[EN_GRP_1_0], BIT(2)),
+	LDO(L7,  0x010, 0x06C, 0, LDO_50,  &m_en[EN_GRP_3_2], BIT(3)),
+	LDO(L8,  0x011, 0x06D, 0, LDO_300, &m_en[EN_PH],      BIT(7)),
+	LDO(L9,  0x012, 0x06E, 0, LDO_300, &m_en[EN_GRP_1_0], BIT(3)),
+	LDO(L10, 0x013, 0x06F, 0, LDO_300, &m_en[EN_GRP_3_2], BIT(4)),
+	LDO(L11, 0x014, 0x070, 0, LDO_150, &m_en[EN_PH],      BIT(4)),
+	LDO(L12, 0x015, 0x071, 0, LDO_150, &m_en[EN_PH],      BIT(3)),
+	LDO(L13, 0x016, 0x072, 0, LDO_300, &m_en[EN_GRP_3_2], BIT(1)),
+	LDO(L14, 0x017, 0x073, 0, LDO_300, &m_en[EN_GRP_1_0], BIT(5)),
+	LDO(L15, 0x089, 0x0E5, 0, LDO_300, &m_en[EN_GRP_1_0], BIT(4)),
+	LDO(L16, 0x08A, 0x0E6, 0, LDO_300, &m_en[EN_GRP_3_2], BIT(0)),
+	LDO(L17, 0x08B, 0x0E7, 0, LDO_150, &m_en[EN_RF],      BIT(7)),
+	LDO(L18, 0x11D, 0x125, 0, LDO_150, &m_en[EN_RF],      BIT(6)),
+	LDO(L19, 0x11E, 0x126, 0, LDO_150, &m_en[EN_RF],      BIT(5)),
+	LDO(L20, 0x11F, 0x127, 0, LDO_150, &m_en[EN_RF],      BIT(4)),
+	LDO_M2(L21, 0x120, 0x128, 1, LDO_150, &m_en[EN_GRP_5_4], BIT(1),
+		&m_en[EN_GRP_1_0], BIT(6)),
+	LDO(L22, 0x121, 0x129, 1, LDO_300, &m_en[EN_GRP_3_2], BIT(7)),
+	LDO(L23, 0x122, 0x12A, 1, LDO_300, &m_en[EN_GRP_5_4], BIT(0)),
+	LDO(L24, 0x123, 0x12B, 1, LDO_150, &m_en[EN_RF],      BIT(3)),
+	LDO(L25, 0x124, 0x12C, 1, LDO_150, &m_en[EN_RF],      BIT(2)),
+
+	/*   id  ctrl   test2  clk    sleep hpm_min  m_en	    m_en_mask */
+	SMPS(S0, 0x004, 0x084, 0x1D1, 0x1D8, SMPS, &m_en[EN_MSM],    BIT(7)),
+	SMPS(S1, 0x005, 0x085, 0x1D2, 0x1DB, SMPS, &m_en[EN_MSM],    BIT(6)),
+	SMPS(S2, 0x110, 0x119, 0x1D3, 0x1DE, SMPS, &m_en[EN_GRP_5_4], BIT(5)),
+	SMPS(S3, 0x111, 0x11A, 0x1D4, 0x1E1, SMPS, &m_en[EN_GRP_5_4],
+		BIT(7) | BIT(4)),
+	SMPS(S4, 0x112, 0x11B, 0x1D5, 0x1E4, SMPS, &m_en[EN_GRP_3_2], BIT(5)),
+
+	/*  id	  ctrl   m_en		    m_en_mask */
+	LVS(LVS0, 0x12D, &m_en[EN_RF],      BIT(1)),
+	LVS(LVS1, 0x12F, &m_en[EN_GRP_1_0], BIT(0)),
+
+	/*  id   ctrl   test1 */
+	NCP(NCP, 0x090, 0x0EC),
+};
+
+static int pm8058_smps_set_voltage_advanced(struct pm8058_vreg *vreg, int uV,
+					int force_on);
+static int pm8058_smps_set_voltage_legacy(struct pm8058_vreg *vreg, int uV);
+static int _pm8058_vreg_is_enabled(struct pm8058_vreg *vreg);
+
+static unsigned int pm8058_vreg_get_mode(struct regulator_dev *dev);
+
+static void print_write_error(struct pm8058_vreg *vreg, int rc,
+				const char *func);
+
+static int pm8058_vreg_write(struct pm8058_vreg *vreg,
+		u16 addr, u8 val, u8 mask, u8 *reg_save)
+{
+	int rc = 0;
+	u8 reg;
+
+	reg = (*reg_save & ~mask) | (val & mask);
+	if (reg != *reg_save)
+		rc = pm8xxx_writeb(vreg->dev->parent, addr, reg);
+	if (rc)
+		pr_err("%s: pm8xxx_write failed, rc=%d\n", __func__, rc);
+	else
+		*reg_save = reg;
+	return rc;
+}
+
+static int pm8058_vreg_is_global_enabled(struct pm8058_vreg *vreg)
+{
+	int ret = 0, i;
+
+	for (i = 0;
+	     (i < GLOBAL_ENABLE_MAX) && !ret && vreg->global_enable[i]; i++)
+		ret = vreg->global_enable[i]->reg &
+			vreg->global_enable_mask[i];
+
+	return ret;
+}
+
+
+static int pm8058_vreg_set_global_enable(struct pm8058_vreg *vreg, int on)
+{
+	int rc = 0, i;
+
+	for (i = 0;
+	     (i < GLOBAL_ENABLE_MAX) && !rc && vreg->global_enable[i]; i++)
+		rc = pm8058_vreg_write(vreg, vreg->global_enable[i]->addr,
+					(on ? vreg->global_enable_mask[i] : 0),
+					vreg->global_enable_mask[i],
+					&vreg->global_enable[i]->reg);
+
+	return rc;
+}
+
+static int pm8058_vreg_using_pin_ctrl(struct pm8058_vreg *vreg)
+{
+	int ret = 0;
+
+	switch (vreg->type) {
+	case REGULATOR_TYPE_LDO:
+		ret = ((vreg->test_reg[5] & LDO_TEST_PIN_CTRL_MASK) << 4)
+			| (vreg->test_reg[6] & LDO_TEST_PIN_CTRL_LPM_MASK);
+		break;
+	case REGULATOR_TYPE_SMPS:
+		ret = vreg->sleep_ctrl_reg
+			& (SMPS_PIN_CTRL_MASK | SMPS_PIN_CTRL_LPM_MASK);
+		break;
+	case REGULATOR_TYPE_LVS:
+		ret = vreg->ctrl_reg & LVS_PIN_CTRL_MASK;
+		break;
+	}
+
+	return ret;
+}
+
+static int pm8058_vreg_set_pin_ctrl(struct pm8058_vreg *vreg, int on)
+{
+	int rc = 0, bank;
+	u8 val = 0, mask;
+	unsigned pc = vreg->pdata->pin_ctrl;
+	unsigned pf = vreg->pdata->pin_fn;
+
+	switch (vreg->type) {
+	case REGULATOR_TYPE_LDO:
+		if (on) {
+			if (pc & PM8058_VREG_PIN_CTRL_D0)
+				val |= LDO_TEST_PIN_CTRL_EN0;
+			if (pc & PM8058_VREG_PIN_CTRL_D1)
+				val |= LDO_TEST_PIN_CTRL_EN1;
+			if (pc & PM8058_VREG_PIN_CTRL_A0)
+				val |= LDO_TEST_PIN_CTRL_EN2;
+			if (pc & PM8058_VREG_PIN_CTRL_A1)
+				val |= LDO_TEST_PIN_CTRL_EN3;
+
+			bank = (pf == PM8058_VREG_PIN_FN_ENABLE ? 5 : 6);
+			rc = pm8058_vreg_write(vreg, vreg->test_addr,
+				val | REGULATOR_BANK_SEL(bank)
+				  | REGULATOR_BANK_WRITE,
+				LDO_TEST_PIN_CTRL_MASK | REGULATOR_BANK_MASK,
+				&vreg->test_reg[bank]);
+			if (rc)
+				goto bail;
+
+			val = LDO_TEST_LPM_SEL_CTRL | REGULATOR_BANK_WRITE
+				| REGULATOR_BANK_SEL(0);
+			mask = LDO_TEST_LPM_MASK | REGULATOR_BANK_MASK;
+			rc = pm8058_vreg_write(vreg, vreg->test_addr, val, mask,
+						&vreg->test_reg[0]);
+			if (rc)
+				goto bail;
+
+			if (pf == PM8058_VREG_PIN_FN_ENABLE) {
+				/* Pin control ON/OFF */
+				rc = pm8058_vreg_write(vreg, vreg->ctrl_addr,
+					LDO_CTRL_PM_HPM,
+					LDO_ENABLE_MASK | LDO_CTRL_PM_MASK,
+					&vreg->ctrl_reg);
+				if (rc)
+					goto bail;
+				rc = pm8058_vreg_set_global_enable(vreg, 0);
+				if (rc)
+					goto bail;
+			} else {
+				/* Pin control LPM/HPM */
+				rc = pm8058_vreg_write(vreg, vreg->ctrl_addr,
+					LDO_ENABLE | LDO_CTRL_PM_LPM,
+					LDO_ENABLE_MASK | LDO_CTRL_PM_MASK,
+					&vreg->ctrl_reg);
+				if (rc)
+					goto bail;
+			}
+		} else {
+			/* Pin control off */
+			rc = pm8058_vreg_write(vreg, vreg->test_addr,
+				REGULATOR_BANK_SEL(5) | REGULATOR_BANK_WRITE,
+				LDO_TEST_PIN_CTRL_MASK | REGULATOR_BANK_MASK,
+				&vreg->test_reg[5]);
+			if (rc)
+				goto bail;
+
+			rc = pm8058_vreg_write(vreg, vreg->test_addr,
+				REGULATOR_BANK_SEL(6) | REGULATOR_BANK_WRITE,
+				LDO_TEST_PIN_CTRL_MASK | REGULATOR_BANK_MASK,
+				&vreg->test_reg[6]);
+			if (rc)
+				goto bail;
+		}
+		break;
+
+	case REGULATOR_TYPE_SMPS:
+		if (on) {
+			if (pf == PM8058_VREG_PIN_FN_ENABLE) {
+				/* Pin control ON/OFF */
+				if (pc & PM8058_VREG_PIN_CTRL_D0)
+					val |= SMPS_PIN_CTRL_D0;
+				if (pc & PM8058_VREG_PIN_CTRL_D1)
+					val |= SMPS_PIN_CTRL_D1;
+				if (pc & PM8058_VREG_PIN_CTRL_A0)
+					val |= SMPS_PIN_CTRL_A0;
+				if (pc & PM8058_VREG_PIN_CTRL_A1)
+					val |= SMPS_PIN_CTRL_A1;
+			} else {
+				/* Pin control LPM/HPM */
+				if (pc & PM8058_VREG_PIN_CTRL_D0)
+					val |= SMPS_PIN_CTRL_LPM_D0;
+				if (pc & PM8058_VREG_PIN_CTRL_D1)
+					val |= SMPS_PIN_CTRL_LPM_D1;
+				if (pc & PM8058_VREG_PIN_CTRL_A0)
+					val |= SMPS_PIN_CTRL_LPM_A0;
+				if (pc & PM8058_VREG_PIN_CTRL_A1)
+					val |= SMPS_PIN_CTRL_LPM_A1;
+			}
+			rc = pm8058_vreg_set_global_enable(vreg, 0);
+			if (rc)
+				goto bail;
+
+			rc = pm8058_smps_set_voltage_legacy(vreg,
+							vreg->save_uV);
+			if (rc)
+				goto bail;
+
+			rc = pm8058_vreg_write(vreg, vreg->sleep_ctrl_addr, val,
+				SMPS_PIN_CTRL_MASK | SMPS_PIN_CTRL_LPM_MASK,
+				&vreg->sleep_ctrl_reg);
+			if (rc)
+				goto bail;
+
+			rc = pm8058_vreg_write(vreg, vreg->ctrl_addr,
+				(pf == PM8058_VREG_PIN_FN_ENABLE
+				       ? 0 : SMPS_LEGACY_ENABLE),
+				SMPS_LEGACY_ENABLE, &vreg->ctrl_reg);
+			if (rc)
+				goto bail;
+
+			rc = pm8058_vreg_write(vreg, vreg->clk_ctrl_addr,
+				(pf == PM8058_VREG_PIN_FN_ENABLE
+				       ? SMPS_CLK_CTRL_PWM : SMPS_CLK_CTRL_PFM),
+				SMPS_CLK_CTRL_MASK, &vreg->clk_ctrl_reg);
+			if (rc)
+				goto bail;
+		} else {
+			/* Pin control off */
+			if (!SMPS_IN_ADVANCED_MODE(vreg)) {
+				if (_pm8058_vreg_is_enabled(vreg))
+					val = SMPS_LEGACY_ENABLE;
+				rc = pm8058_vreg_write(vreg, vreg->ctrl_addr,
+					val, SMPS_LEGACY_ENABLE,
+					&vreg->ctrl_reg);
+				if (rc)
+					goto bail;
+			}
+
+			rc = pm8058_vreg_write(vreg, vreg->sleep_ctrl_addr, 0,
+				SMPS_PIN_CTRL_MASK | SMPS_PIN_CTRL_LPM_MASK,
+				&vreg->sleep_ctrl_reg);
+			if (rc)
+				goto bail;
+
+			rc = pm8058_smps_set_voltage_advanced(vreg,
+							 vreg->save_uV, 0);
+			if (rc)
+				goto bail;
+		}
+		break;
+
+	case REGULATOR_TYPE_LVS:
+		if (on) {
+			if (pc & PM8058_VREG_PIN_CTRL_D0)
+				val |= LVS_PIN_CTRL_EN0;
+			if (pc & PM8058_VREG_PIN_CTRL_D1)
+				val |= LVS_PIN_CTRL_EN1;
+			if (pc & PM8058_VREG_PIN_CTRL_A0)
+				val |= LVS_PIN_CTRL_EN2;
+			if (pc & PM8058_VREG_PIN_CTRL_A1)
+				val |= LVS_PIN_CTRL_EN3;
+
+			rc = pm8058_vreg_write(vreg, vreg->ctrl_addr, val,
+					LVS_PIN_CTRL_MASK | LVS_ENABLE_MASK,
+					&vreg->ctrl_reg);
+			if (rc)
+				goto bail;
+
+			rc = pm8058_vreg_set_global_enable(vreg, 0);
+			if (rc)
+				goto bail;
+		} else {
+			/* Pin control off */
+			if (_pm8058_vreg_is_enabled(vreg))
+				val = LVS_ENABLE;
+
+			rc = pm8058_vreg_write(vreg, vreg->ctrl_addr, val,
+					LVS_ENABLE_MASK | LVS_PIN_CTRL_MASK,
+					&vreg->ctrl_reg);
+			if (rc)
+				goto bail;
+
+		}
+		break;
+	}
+
+bail:
+	if (rc)
+		print_write_error(vreg, rc, __func__);
+
+	return rc;
+}
+
+static int pm8058_vreg_enable(struct regulator_dev *dev)
+{
+	struct pm8058_vreg *vreg = rdev_get_drvdata(dev);
+	int mode;
+	int rc = 0;
+
+	mode = pm8058_vreg_get_mode(dev);
+
+	if (mode == REGULATOR_MODE_IDLE) {
+		/* Turn on pin control. */
+		rc = pm8058_vreg_set_pin_ctrl(vreg, 1);
+		if (rc)
+			goto bail;
+		return rc;
+	}
+	if (vreg->type == REGULATOR_TYPE_SMPS && SMPS_IN_ADVANCED_MODE(vreg))
+		rc = pm8058_smps_set_voltage_advanced(vreg, vreg->save_uV, 1);
+	else
+		rc = pm8058_vreg_write(vreg, vreg->ctrl_addr, REGULATOR_EN_MASK,
+			REGULATOR_EN_MASK, &vreg->ctrl_reg);
+bail:
+	if (rc)
+		print_write_error(vreg, rc, __func__);
+
+	return rc;
+}
+
+static int _pm8058_vreg_is_enabled(struct pm8058_vreg *vreg)
+{
+	/*
+	 * All regulator types except advanced mode SMPS have enable bit in
+	 * bit 7 of the control register.  Global enable  and pin control also
+	 * do not work for advanced mode SMPS.
+	 */
+	if (!(vreg->type == REGULATOR_TYPE_SMPS && SMPS_IN_ADVANCED_MODE(vreg))
+		&& ((vreg->ctrl_reg & REGULATOR_EN_MASK)
+			|| pm8058_vreg_is_global_enabled(vreg)
+			|| pm8058_vreg_using_pin_ctrl(vreg)))
+		return 1;
+	else if (vreg->type == REGULATOR_TYPE_SMPS
+		&& SMPS_IN_ADVANCED_MODE(vreg)
+		&& ((vreg->ctrl_reg & SMPS_ADVANCED_BAND_MASK)
+			!= SMPS_ADVANCED_BAND_OFF))
+		return 1;
+
+	return 0;
+}
+
+static int pm8058_vreg_is_enabled(struct regulator_dev *dev)
+{
+	struct pm8058_vreg *vreg = rdev_get_drvdata(dev);
+
+	return _pm8058_vreg_is_enabled(vreg);
+}
+
+static int pm8058_vreg_disable(struct regulator_dev *dev)
+{
+	struct pm8058_vreg *vreg = rdev_get_drvdata(dev);
+	int rc = 0;
+
+	/* Disable in global control register. */
+	rc = pm8058_vreg_set_global_enable(vreg, 0);
+	if (rc)
+		goto bail;
+
+	/* Turn off pin control. */
+	rc = pm8058_vreg_set_pin_ctrl(vreg, 0);
+	if (rc)
+		goto bail;
+
+	/* Disable in local control register. */
+	if (vreg->type == REGULATOR_TYPE_SMPS && SMPS_IN_ADVANCED_MODE(vreg))
+		rc = pm8058_vreg_write(vreg, vreg->ctrl_addr,
+			SMPS_ADVANCED_BAND_OFF, SMPS_ADVANCED_BAND_MASK,
+			&vreg->ctrl_reg);
+	else
+		rc = pm8058_vreg_write(vreg, vreg->ctrl_addr, 0,
+			REGULATOR_EN_MASK, &vreg->ctrl_reg);
+
+bail:
+	if (rc)
+		print_write_error(vreg, rc, __func__);
+
+	return rc;
+}
+
+static int pm8058_pldo_set_voltage(struct pm8058_vreg *vreg, int uV)
+{
+	int vmin, rc = 0;
+	unsigned vprog, fine_step;
+	u8 range_ext, range_sel, fine_step_reg;
+
+	if (uV < PLDO_LOW_UV_MIN || uV > PLDO_HIGH_UV_MAX)
+		return -EINVAL;
+
+	if (uV < PLDO_LOW_UV_MAX + PLDO_LOW_FINE_STEP_UV) {
+		vmin = PLDO_LOW_UV_MIN;
+		fine_step = PLDO_LOW_FINE_STEP_UV;
+		range_ext = 0;
+		range_sel = LDO_TEST_RANGE_SEL_MASK;
+	} else if (uV < PLDO_NORM_UV_MAX + PLDO_NORM_FINE_STEP_UV) {
+		vmin = PLDO_NORM_UV_MIN;
+		fine_step = PLDO_NORM_FINE_STEP_UV;
+		range_ext = 0;
+		range_sel = 0;
+	} else {
+		vmin = PLDO_HIGH_UV_MIN;
+		fine_step = PLDO_HIGH_FINE_STEP_UV;
+		range_ext = LDO_TEST_RANGE_EXT_MASK;
+		range_sel = 0;
+	}
+
+	vprog = (uV - vmin) / fine_step;
+	fine_step_reg = (vprog & 1) << LDO_TEST_FINE_STEP_SHIFT;
+	vprog >>= 1;
+
+	/*
+	 * Disable program voltage update if range extension, range select,
+	 * or fine step have changed and the regulator is enabled.
+	 */
+	if (_pm8058_vreg_is_enabled(vreg) &&
+		(((range_ext ^ vreg->test_reg[4]) & LDO_TEST_RANGE_EXT_MASK)
+		|| ((range_sel ^ vreg->test_reg[2]) & LDO_TEST_RANGE_SEL_MASK)
+		|| ((fine_step_reg ^ vreg->test_reg[2])
+			& LDO_TEST_FINE_STEP_MASK))) {
+		rc = pm8058_vreg_write(vreg, vreg->test_addr,
+			REGULATOR_BANK_SEL(2) | REGULATOR_BANK_WRITE,
+			REGULATOR_BANK_MASK | LDO_TEST_VPROG_UPDATE_MASK,
+			&vreg->test_reg[2]);
+		if (rc)
+			goto bail;
+	}
+
+	/* Write new voltage. */
+	rc = pm8058_vreg_write(vreg, vreg->ctrl_addr, vprog,
+				LDO_CTRL_VPROG_MASK, &vreg->ctrl_reg);
+	if (rc)
+		goto bail;
+
+	/* Write range extension. */
+	rc = pm8058_vreg_write(vreg, vreg->test_addr,
+			range_ext | REGULATOR_BANK_SEL(4)
+			 | REGULATOR_BANK_WRITE,
+			LDO_TEST_RANGE_EXT_MASK | REGULATOR_BANK_MASK,
+			&vreg->test_reg[4]);
+	if (rc)
+		goto bail;
+
+	/* Write fine step, range select and program voltage update. */
+	rc = pm8058_vreg_write(vreg, vreg->test_addr,
+			fine_step_reg | range_sel | REGULATOR_BANK_SEL(2)
+			 | REGULATOR_BANK_WRITE | LDO_TEST_VPROG_UPDATE_MASK,
+			LDO_TEST_FINE_STEP_MASK | LDO_TEST_RANGE_SEL_MASK
+			 | REGULATOR_BANK_MASK | LDO_TEST_VPROG_UPDATE_MASK,
+			&vreg->test_reg[2]);
+bail:
+	if (rc)
+		print_write_error(vreg, rc, __func__);
+
+	return rc;
+}
+
+static int pm8058_nldo_set_voltage(struct pm8058_vreg *vreg, int uV)
+{
+	unsigned vprog, fine_step_reg;
+	int rc;
+
+	if (uV < NLDO_UV_MIN || uV > NLDO_UV_MAX)
+		return -EINVAL;
+
+	vprog = (uV - NLDO_UV_MIN) / NLDO_FINE_STEP_UV;
+	fine_step_reg = (vprog & 1) << LDO_TEST_FINE_STEP_SHIFT;
+	vprog >>= 1;
+
+	/* Write new voltage. */
+	rc = pm8058_vreg_write(vreg, vreg->ctrl_addr, vprog,
+				LDO_CTRL_VPROG_MASK, &vreg->ctrl_reg);
+	if (rc)
+		goto bail;
+
+	/* Write fine step. */
+	rc = pm8058_vreg_write(vreg, vreg->test_addr,
+			fine_step_reg | REGULATOR_BANK_SEL(2)
+			 | REGULATOR_BANK_WRITE | LDO_TEST_VPROG_UPDATE_MASK,
+			LDO_TEST_FINE_STEP_MASK | REGULATOR_BANK_MASK
+			 | LDO_TEST_VPROG_UPDATE_MASK,
+		       &vreg->test_reg[2]);
+bail:
+	if (rc)
+		print_write_error(vreg, rc, __func__);
+
+	return rc;
+}
+
+static int pm8058_ldo_set_voltage(struct regulator_dev *dev,
+		int min_uV, int max_uV, unsigned *selector)
+{
+	struct pm8058_vreg *vreg = rdev_get_drvdata(dev);
+
+	if (vreg->is_nmos)
+		return pm8058_nldo_set_voltage(vreg, min_uV);
+	else
+		return pm8058_pldo_set_voltage(vreg, min_uV);
+}
+
+static int pm8058_pldo_get_voltage(struct pm8058_vreg *vreg)
+{
+	int vmin, fine_step;
+	u8 range_ext, range_sel, vprog, fine_step_reg;
+
+	fine_step_reg = vreg->test_reg[2] & LDO_TEST_FINE_STEP_MASK;
+	range_sel = vreg->test_reg[2] & LDO_TEST_RANGE_SEL_MASK;
+	range_ext = vreg->test_reg[4] & LDO_TEST_RANGE_EXT_MASK;
+	vprog = vreg->ctrl_reg & LDO_CTRL_VPROG_MASK;
+
+	vprog = (vprog << 1) | (fine_step_reg >> LDO_TEST_FINE_STEP_SHIFT);
+
+	if (range_sel) {
+		/* low range mode */
+		fine_step = PLDO_LOW_FINE_STEP_UV;
+		vmin = PLDO_LOW_UV_MIN;
+	} else if (!range_ext) {
+		/* normal mode */
+		fine_step = PLDO_NORM_FINE_STEP_UV;
+		vmin = PLDO_NORM_UV_MIN;
+	} else {
+		/* high range mode */
+		fine_step = PLDO_HIGH_FINE_STEP_UV;
+		vmin = PLDO_HIGH_UV_MIN;
+	}
+
+	return fine_step * vprog + vmin;
+}
+
+static int pm8058_nldo_get_voltage(struct pm8058_vreg *vreg)
+{
+	u8 vprog, fine_step_reg;
+
+	fine_step_reg = vreg->test_reg[2] & LDO_TEST_FINE_STEP_MASK;
+	vprog = vreg->ctrl_reg & LDO_CTRL_VPROG_MASK;
+
+	vprog = (vprog << 1) | (fine_step_reg >> LDO_TEST_FINE_STEP_SHIFT);
+
+	return NLDO_FINE_STEP_UV * vprog + NLDO_UV_MIN;
+}
+
+static int pm8058_ldo_get_voltage(struct regulator_dev *dev)
+{
+	struct pm8058_vreg *vreg = rdev_get_drvdata(dev);
+
+	if (vreg->is_nmos)
+		return pm8058_nldo_get_voltage(vreg);
+	else
+		return pm8058_pldo_get_voltage(vreg);
+}
+
+static int pm8058_smps_get_voltage_advanced(struct pm8058_vreg *vreg)
+{
+	u8 vprog, band;
+	int uV = 0;
+
+	vprog = vreg->ctrl_reg & SMPS_ADVANCED_VPROG_MASK;
+	band = vreg->ctrl_reg & SMPS_ADVANCED_BAND_MASK;
+
+	if (band == SMPS_ADVANCED_BAND_1)
+		uV = vprog * SMPS_BAND1_UV_STEP + SMPS_BAND1_UV_MIN;
+	else if (band == SMPS_ADVANCED_BAND_2)
+		uV = vprog * SMPS_BAND2_UV_STEP + SMPS_BAND2_UV_MIN;
+	else if (band == SMPS_ADVANCED_BAND_3)
+		uV = vprog * SMPS_BAND3_UV_STEP + SMPS_BAND3_UV_MIN;
+	else
+		uV = vreg->save_uV;
+
+	return uV;
+}
+
+static int pm8058_smps_get_voltage_legacy(struct pm8058_vreg *vreg)
+{
+	u8 vlow, vref, vprog;
+	int uV;
+
+	vlow = vreg->test_reg[1] & SMPS_LEGACY_VLOW_SEL_MASK;
+	vref = vreg->ctrl_reg & SMPS_LEGACY_VREF_SEL_MASK;
+	vprog = vreg->ctrl_reg & SMPS_LEGACY_VPROG_MASK;
+
+	if (vlow && vref) {
+		/* mode 3 */
+		uV = vprog * SMPS_MODE3_UV_STEP + SMPS_MODE3_UV_MIN;
+	} else if (vref) {
+		/* mode 2 */
+		uV = vprog * SMPS_MODE2_UV_STEP + SMPS_MODE2_UV_MIN;
+	} else {
+		/* mode 1 */
+		uV = vprog * SMPS_MODE1_UV_STEP + SMPS_MODE1_UV_MIN;
+	}
+
+	return uV;
+}
+
+static int _pm8058_smps_get_voltage(struct pm8058_vreg *vreg)
+{
+	if (SMPS_IN_ADVANCED_MODE(vreg))
+		return pm8058_smps_get_voltage_advanced(vreg);
+
+	return pm8058_smps_get_voltage_legacy(vreg);
+}
+
+static int pm8058_smps_get_voltage(struct regulator_dev *dev)
+{
+	struct pm8058_vreg *vreg = rdev_get_drvdata(dev);
+
+	return _pm8058_smps_get_voltage(vreg);
+}
+
+static int pm8058_smps_set_voltage_advanced(struct pm8058_vreg *vreg,
+					int uV,	int force_on)
+{
+	u8 vprog, band;
+	int rc, new_uV;
+
+	if (uV < SMPS_BAND1_UV_MAX + SMPS_BAND1_UV_STEP) {
+		vprog = ((uV - SMPS_BAND1_UV_MIN) / SMPS_BAND1_UV_STEP);
+		band = SMPS_ADVANCED_BAND_1;
+		new_uV = SMPS_BAND1_UV_MIN + vprog * SMPS_BAND1_UV_STEP;
+	} else if (uV < SMPS_BAND2_UV_MAX + SMPS_BAND2_UV_STEP) {
+		vprog = ((uV - SMPS_BAND2_UV_MIN) / SMPS_BAND2_UV_STEP);
+		band = SMPS_ADVANCED_BAND_2;
+		new_uV = SMPS_BAND2_UV_MIN + vprog * SMPS_BAND2_UV_STEP;
+	} else {
+		vprog = ((uV - SMPS_BAND3_UV_MIN) / SMPS_BAND3_UV_STEP);
+		band = SMPS_ADVANCED_BAND_3;
+		new_uV = SMPS_BAND3_UV_MIN + vprog * SMPS_BAND3_UV_STEP;
+	}
+
+	/* Do not set band if regulator currently disabled. */
+	if (!_pm8058_vreg_is_enabled(vreg) && !force_on)
+		band = SMPS_ADVANCED_BAND_OFF;
+
+	/* Set advanced mode bit to 1. */
+	rc = pm8058_vreg_write(vreg, vreg->test_addr, SMPS_ADVANCED_MODE
+		| REGULATOR_BANK_WRITE | REGULATOR_BANK_SEL(7),
+		SMPS_ADVANCED_MODE_MASK | REGULATOR_BANK_MASK,
+		&vreg->test_reg[7]);
+	if (rc)
+		goto bail;
+
+	/* Set voltage and voltage band. */
+	rc = pm8058_vreg_write(vreg, vreg->ctrl_addr, band | vprog,
+			SMPS_ADVANCED_BAND_MASK | SMPS_ADVANCED_VPROG_MASK,
+			&vreg->ctrl_reg);
+	if (rc)
+		goto bail;
+
+	vreg->save_uV = new_uV;
+
+bail:
+	return rc;
+}
+
+static int pm8058_smps_set_voltage_legacy(struct pm8058_vreg *vreg, int uV)
+{
+	u8 vlow, vref, vprog, pd, en;
+	int rc;
+
+	if (uV < SMPS_MODE3_UV_MAX + SMPS_MODE3_UV_STEP) {
+		vprog = ((uV - SMPS_MODE3_UV_MIN) / SMPS_MODE3_UV_STEP);
+		vref = SMPS_LEGACY_VREF_SEL_MASK;
+		vlow = SMPS_LEGACY_VLOW_SEL_MASK;
+	} else if (uV < SMPS_MODE2_UV_MAX + SMPS_MODE2_UV_STEP) {
+		vprog = ((uV - SMPS_MODE2_UV_MIN) / SMPS_MODE2_UV_STEP);
+		vref = SMPS_LEGACY_VREF_SEL_MASK;
+		vlow = 0;
+	} else {
+		vprog = ((uV - SMPS_MODE1_UV_MIN) / SMPS_MODE1_UV_STEP);
+		vref = 0;
+		vlow = 0;
+	}
+
+	/* set vlow bit for ultra low voltage mode */
+	rc = pm8058_vreg_write(vreg, vreg->test_addr,
+		vlow | REGULATOR_BANK_WRITE | REGULATOR_BANK_SEL(1),
+		REGULATOR_BANK_MASK | SMPS_LEGACY_VLOW_SEL_MASK,
+		&vreg->test_reg[1]);
+	if (rc)
+		goto bail;
+
+	/* Set advanced mode bit to 0. */
+	rc = pm8058_vreg_write(vreg, vreg->test_addr, SMPS_LEGACY_MODE
+		| REGULATOR_BANK_WRITE | REGULATOR_BANK_SEL(7),
+		SMPS_ADVANCED_MODE_MASK | REGULATOR_BANK_MASK,
+		&vreg->test_reg[7]);
+	if (rc)
+		goto bail;
+
+	en = (_pm8058_vreg_is_enabled(vreg) ? SMPS_LEGACY_ENABLE : 0);
+	pd = (vreg->pdata->pull_down_enable ? SMPS_LEGACY_PULL_DOWN_ENABLE : 0);
+
+	/* Set voltage (and the rest of the control register). */
+	rc = pm8058_vreg_write(vreg, vreg->ctrl_addr, en | pd | vref | vprog,
+		SMPS_LEGACY_ENABLE | SMPS_LEGACY_PULL_DOWN_ENABLE
+		| SMPS_LEGACY_VREF_SEL_MASK | SMPS_LEGACY_VPROG_MASK,
+		&vreg->ctrl_reg);
+
+	vreg->save_uV = pm8058_smps_get_voltage_legacy(vreg);
+
+bail:
+	return rc;
+}
+
+static int pm8058_smps_set_voltage(struct regulator_dev *dev,
+		int min_uV, int max_uV, unsigned *selector)
+{
+	struct pm8058_vreg *vreg = rdev_get_drvdata(dev);
+	int rc = 0;
+
+	if (min_uV < SMPS_UV_MIN || min_uV > SMPS_UV_MAX)
+		return -EINVAL;
+
+	if (SMPS_IN_ADVANCED_MODE(vreg))
+		rc = pm8058_smps_set_voltage_advanced(vreg, min_uV, 0);
+	else
+		rc = pm8058_smps_set_voltage_legacy(vreg, min_uV);
+
+	if (rc)
+		print_write_error(vreg, rc, __func__);
+
+	return rc;
+}
+
+static int pm8058_ncp_set_voltage(struct regulator_dev *dev,
+		int min_uV, int max_uV, unsigned *selector)
+{
+	struct pm8058_vreg *vreg = rdev_get_drvdata(dev);
+	int rc;
+	u8 val;
+
+	if (min_uV < NCP_UV_MIN || min_uV > NCP_UV_MAX)
+		return -EINVAL;
+
+	val = (min_uV - NCP_UV_MIN) / NCP_UV_STEP;
+
+	/* voltage setting */
+	rc = pm8058_vreg_write(vreg, vreg->ctrl_addr, val, NCP_VPROG_MASK,
+			&vreg->ctrl_reg);
+	if (rc)
+		print_write_error(vreg, rc, __func__);
+
+	return rc;
+}
+
+static int pm8058_ncp_get_voltage(struct regulator_dev *dev)
+{
+	struct pm8058_vreg *vreg = rdev_get_drvdata(dev);
+	u8 vprog = vreg->ctrl_reg & NCP_VPROG_MASK;
+	return NCP_UV_MIN + vprog * NCP_UV_STEP;
+}
+
+static int pm8058_ldo_set_mode(struct pm8058_vreg *vreg, unsigned int mode)
+{
+	int rc = 0;
+	u8 mask, val;
+
+	switch (mode) {
+	case REGULATOR_MODE_FAST:
+		/* HPM */
+		val = (_pm8058_vreg_is_enabled(vreg) ? LDO_ENABLE : 0)
+			| LDO_CTRL_PM_HPM;
+		mask = LDO_ENABLE_MASK | LDO_CTRL_PM_MASK;
+		rc = pm8058_vreg_write(vreg, vreg->ctrl_addr, val, mask,
+					&vreg->ctrl_reg);
+		if (rc)
+			goto bail;
+
+		if (pm8058_vreg_using_pin_ctrl(vreg))
+			rc = pm8058_vreg_set_pin_ctrl(vreg, 0);
+		if (rc)
+			goto bail;
+		break;
+
+	case REGULATOR_MODE_STANDBY:
+		/* LPM */
+		val = (_pm8058_vreg_is_enabled(vreg) ? LDO_ENABLE : 0)
+			| LDO_CTRL_PM_LPM;
+		mask = LDO_ENABLE_MASK | LDO_CTRL_PM_MASK;
+		rc = pm8058_vreg_write(vreg, vreg->ctrl_addr, val, mask,
+					&vreg->ctrl_reg);
+		if (rc)
+			goto bail;
+
+		val = LDO_TEST_LPM_SEL_CTRL | REGULATOR_BANK_WRITE
+			| REGULATOR_BANK_SEL(0);
+		mask = LDO_TEST_LPM_MASK | REGULATOR_BANK_MASK;
+		rc = pm8058_vreg_write(vreg, vreg->test_addr, val, mask,
+					&vreg->test_reg[0]);
+		if (rc)
+			goto bail;
+
+		if (pm8058_vreg_using_pin_ctrl(vreg))
+			rc = pm8058_vreg_set_pin_ctrl(vreg, 0);
+		if (rc)
+			goto bail;
+		break;
+
+	case REGULATOR_MODE_IDLE:
+		/* Pin Control */
+		if (_pm8058_vreg_is_enabled(vreg))
+			rc = pm8058_vreg_set_pin_ctrl(vreg, 1);
+		if (rc)
+			goto bail;
+		break;
+
+	default:
+		pr_err("%s: invalid mode: %u\n", __func__, mode);
+		return -EINVAL;
+	}
+
+bail:
+	if (rc)
+		print_write_error(vreg, rc, __func__);
+
+	return rc;
+}
+
+static int pm8058_smps_set_mode(struct pm8058_vreg *vreg, unsigned int mode)
+{
+	int rc = 0;
+	u8 mask, val;
+
+	switch (mode) {
+	case REGULATOR_MODE_FAST:
+		/* HPM */
+		val = SMPS_CLK_CTRL_PWM;
+		mask = SMPS_CLK_CTRL_MASK;
+		rc = pm8058_vreg_write(vreg, vreg->clk_ctrl_addr, val, mask,
+					&vreg->clk_ctrl_reg);
+		if (rc)
+			goto bail;
+
+		if (pm8058_vreg_using_pin_ctrl(vreg))
+			rc = pm8058_vreg_set_pin_ctrl(vreg, 0);
+		if (rc)
+			goto bail;
+		break;
+
+	case REGULATOR_MODE_STANDBY:
+		/* LPM */
+		val = SMPS_CLK_CTRL_PFM;
+		mask = SMPS_CLK_CTRL_MASK;
+		rc = pm8058_vreg_write(vreg, vreg->clk_ctrl_addr, val, mask,
+					&vreg->clk_ctrl_reg);
+		if (rc)
+			goto bail;
+
+		if (pm8058_vreg_using_pin_ctrl(vreg))
+			rc = pm8058_vreg_set_pin_ctrl(vreg, 0);
+		if (rc)
+			goto bail;
+		break;
+
+	case REGULATOR_MODE_IDLE:
+		/* Pin Control */
+		if (_pm8058_vreg_is_enabled(vreg))
+			rc = pm8058_vreg_set_pin_ctrl(vreg, 1);
+		if (rc)
+			goto bail;
+		break;
+
+	default:
+		pr_err("%s: invalid mode: %u\n", __func__, mode);
+		return -EINVAL;
+	}
+
+bail:
+	if (rc)
+		print_write_error(vreg, rc, __func__);
+
+	return rc;
+}
+
+static int pm8058_lvs_set_mode(struct pm8058_vreg *vreg, unsigned int mode)
+{
+	int rc = 0;
+
+	if (mode == REGULATOR_MODE_IDLE) {
+		/* Use pin control. */
+		if (_pm8058_vreg_is_enabled(vreg))
+			rc = pm8058_vreg_set_pin_ctrl(vreg, 1);
+	} else {
+		/* Turn off pin control. */
+		rc = pm8058_vreg_set_pin_ctrl(vreg, 0);
+	}
+
+	return rc;
+}
+
+/*
+ * Optimum mode programming:
+ * REGULATOR_MODE_FAST: Go to HPM (highest priority)
+ * REGULATOR_MODE_STANDBY: Go to pin ctrl mode if there are any pin ctrl
+ * votes, else go to LPM
+ *
+ * Pin ctrl mode voting via regulator set_mode:
+ * REGULATOR_MODE_IDLE: Go to pin ctrl mode if the optimum mode is LPM, else
+ * go to HPM
+ * REGULATOR_MODE_NORMAL: Go to LPM if it is the optimum mode, else go to HPM
+ */
+static int pm8058_vreg_set_mode(struct regulator_dev *dev, unsigned int mode)
+{
+	struct pm8058_vreg *vreg = rdev_get_drvdata(dev);
+	unsigned prev_optimum = vreg->optimum;
+	unsigned prev_pc_vote = vreg->pc_vote;
+	unsigned prev_mode_initialized = vreg->mode_initialized;
+	int new_mode = REGULATOR_MODE_FAST;
+	int rc = 0;
+
+	/* Determine new mode to go into. */
+	switch (mode) {
+	case REGULATOR_MODE_FAST:
+		new_mode = REGULATOR_MODE_FAST;
+		vreg->optimum = mode;
+		vreg->mode_initialized = 1;
+		break;
+
+	case REGULATOR_MODE_STANDBY:
+		if (vreg->pc_vote)
+			new_mode = REGULATOR_MODE_IDLE;
+		else
+			new_mode = REGULATOR_MODE_STANDBY;
+		vreg->optimum = mode;
+		vreg->mode_initialized = 1;
+		break;
+
+	case REGULATOR_MODE_IDLE:
+		if (vreg->pc_vote++)
+			goto done; /* already taken care of */
+
+		if (vreg->mode_initialized
+		    && vreg->optimum == REGULATOR_MODE_FAST)
+			new_mode = REGULATOR_MODE_FAST;
+		else
+			new_mode = REGULATOR_MODE_IDLE;
+		break;
+
+	case REGULATOR_MODE_NORMAL:
+		if (vreg->pc_vote && --(vreg->pc_vote))
+			goto done; /* already taken care of */
+
+		if (vreg->optimum == REGULATOR_MODE_STANDBY)
+			new_mode = REGULATOR_MODE_STANDBY;
+		else
+			new_mode = REGULATOR_MODE_FAST;
+		break;
+
+	default:
+		pr_err("%s: unknown mode, mode=%u\n", __func__, mode);
+		return -EINVAL;
+	}
+
+	switch (vreg->type) {
+	case REGULATOR_TYPE_LDO:
+		rc = pm8058_ldo_set_mode(vreg, new_mode);
+		break;
+	case REGULATOR_TYPE_SMPS:
+		rc = pm8058_smps_set_mode(vreg, new_mode);
+		break;
+	case REGULATOR_TYPE_LVS:
+		rc = pm8058_lvs_set_mode(vreg, new_mode);
+		break;
+	}
+
+	if (rc) {
+		print_write_error(vreg, rc, __func__);
+		vreg->mode_initialized = prev_mode_initialized;
+		vreg->optimum = prev_optimum;
+		vreg->pc_vote = prev_pc_vote;
+		return rc;
+	}
+
+done:
+	return 0;
+}
+
+static unsigned int pm8058_vreg_get_mode(struct regulator_dev *dev)
+{
+	struct pm8058_vreg *vreg = rdev_get_drvdata(dev);
+
+	if (!vreg->mode_initialized && vreg->pc_vote)
+		return REGULATOR_MODE_IDLE;
+
+	/* Check physical pin control state. */
+	switch (vreg->type) {
+	case REGULATOR_TYPE_LDO:
+		if (!(vreg->ctrl_reg & LDO_ENABLE_MASK)
+		    && !pm8058_vreg_is_global_enabled(vreg)
+		    && (vreg->test_reg[5] & LDO_TEST_PIN_CTRL_MASK))
+			return REGULATOR_MODE_IDLE;
+		else if (((vreg->ctrl_reg & LDO_ENABLE_MASK)
+				|| pm8058_vreg_is_global_enabled(vreg))
+		    && (vreg->ctrl_reg & LDO_CTRL_PM_MASK)
+		    && (vreg->test_reg[6] & LDO_TEST_PIN_CTRL_LPM_MASK))
+			return REGULATOR_MODE_IDLE;
+		break;
+	case REGULATOR_TYPE_SMPS:
+		if (!SMPS_IN_ADVANCED_MODE(vreg)
+		    && !(vreg->ctrl_reg & REGULATOR_EN_MASK)
+		    && !pm8058_vreg_is_global_enabled(vreg)
+		    && (vreg->sleep_ctrl_reg & SMPS_PIN_CTRL_MASK))
+			return REGULATOR_MODE_IDLE;
+		else if (!SMPS_IN_ADVANCED_MODE(vreg)
+		    && ((vreg->ctrl_reg & REGULATOR_EN_MASK)
+			|| pm8058_vreg_is_global_enabled(vreg))
+		    && ((vreg->clk_ctrl_reg & SMPS_CLK_CTRL_MASK)
+			== SMPS_CLK_CTRL_PFM)
+		    && (vreg->sleep_ctrl_reg & SMPS_PIN_CTRL_LPM_MASK))
+			return REGULATOR_MODE_IDLE;
+		break;
+	case REGULATOR_TYPE_LVS:
+		if (!(vreg->ctrl_reg & LVS_ENABLE_MASK)
+		    && !pm8058_vreg_is_global_enabled(vreg)
+		    && (vreg->ctrl_reg & LVS_PIN_CTRL_MASK))
+			return REGULATOR_MODE_IDLE;
+	}
+
+	if (vreg->optimum == REGULATOR_MODE_FAST)
+		return REGULATOR_MODE_FAST;
+	else if (vreg->pc_vote)
+		return REGULATOR_MODE_IDLE;
+	else if (vreg->optimum == REGULATOR_MODE_STANDBY)
+		return REGULATOR_MODE_STANDBY;
+	return REGULATOR_MODE_FAST;
+}
+
+unsigned int pm8058_vreg_get_optimum_mode(struct regulator_dev *dev,
+		int input_uV, int output_uV, int load_uA)
+{
+	struct pm8058_vreg *vreg = rdev_get_drvdata(dev);
+
+	if (load_uA <= 0) {
+		/*
+		 * pm8058_vreg_get_optimum_mode is being called before consumers
+		 * have specified their load currents via
+		 * regulator_set_optimum_mode. Return whatever the existing mode
+		 * is.
+		 */
+		return pm8058_vreg_get_mode(dev);
+	}
+
+	if (load_uA >= vreg->hpm_min_load)
+		return REGULATOR_MODE_FAST;
+	return REGULATOR_MODE_STANDBY;
+}
+
+static struct regulator_ops pm8058_ldo_ops = {
+	.enable = pm8058_vreg_enable,
+	.disable = pm8058_vreg_disable,
+	.is_enabled = pm8058_vreg_is_enabled,
+	.set_voltage = pm8058_ldo_set_voltage,
+	.get_voltage = pm8058_ldo_get_voltage,
+	.set_mode = pm8058_vreg_set_mode,
+	.get_mode = pm8058_vreg_get_mode,
+	.get_optimum_mode = pm8058_vreg_get_optimum_mode,
+};
+
+static struct regulator_ops pm8058_smps_ops = {
+	.enable = pm8058_vreg_enable,
+	.disable = pm8058_vreg_disable,
+	.is_enabled = pm8058_vreg_is_enabled,
+	.set_voltage = pm8058_smps_set_voltage,
+	.get_voltage = pm8058_smps_get_voltage,
+	.set_mode = pm8058_vreg_set_mode,
+	.get_mode = pm8058_vreg_get_mode,
+	.get_optimum_mode = pm8058_vreg_get_optimum_mode,
+};
+
+static struct regulator_ops pm8058_lvs_ops = {
+	.enable = pm8058_vreg_enable,
+	.disable = pm8058_vreg_disable,
+	.is_enabled = pm8058_vreg_is_enabled,
+	.set_mode = pm8058_vreg_set_mode,
+	.get_mode = pm8058_vreg_get_mode,
+};
+
+static struct regulator_ops pm8058_ncp_ops = {
+	.enable = pm8058_vreg_enable,
+	.disable = pm8058_vreg_disable,
+	.is_enabled = pm8058_vreg_is_enabled,
+	.set_voltage = pm8058_ncp_set_voltage,
+	.get_voltage = pm8058_ncp_get_voltage,
+};
+
+#define VREG_DESCRIP(_id, _name, _ops) \
+	[_id] = { \
+		.id = _id, \
+		.name = _name, \
+		.ops = _ops, \
+		.type = REGULATOR_VOLTAGE, \
+		.owner = THIS_MODULE, \
+	}
+
+static struct regulator_desc pm8058_vreg_descrip[] = {
+	VREG_DESCRIP(PM8058_VREG_ID_L0,  "8058_l0",  &pm8058_ldo_ops),
+	VREG_DESCRIP(PM8058_VREG_ID_L1,  "8058_l1",  &pm8058_ldo_ops),
+	VREG_DESCRIP(PM8058_VREG_ID_L2,  "8058_l2",  &pm8058_ldo_ops),
+	VREG_DESCRIP(PM8058_VREG_ID_L3,  "8058_l3",  &pm8058_ldo_ops),
+	VREG_DESCRIP(PM8058_VREG_ID_L4,  "8058_l4",  &pm8058_ldo_ops),
+	VREG_DESCRIP(PM8058_VREG_ID_L5,  "8058_l5",  &pm8058_ldo_ops),
+	VREG_DESCRIP(PM8058_VREG_ID_L6,  "8058_l6",  &pm8058_ldo_ops),
+	VREG_DESCRIP(PM8058_VREG_ID_L7,  "8058_l7",  &pm8058_ldo_ops),
+	VREG_DESCRIP(PM8058_VREG_ID_L8,  "8058_l8",  &pm8058_ldo_ops),
+	VREG_DESCRIP(PM8058_VREG_ID_L9,  "8058_l9",  &pm8058_ldo_ops),
+	VREG_DESCRIP(PM8058_VREG_ID_L10, "8058_l10", &pm8058_ldo_ops),
+	VREG_DESCRIP(PM8058_VREG_ID_L11, "8058_l11", &pm8058_ldo_ops),
+	VREG_DESCRIP(PM8058_VREG_ID_L12, "8058_l12", &pm8058_ldo_ops),
+	VREG_DESCRIP(PM8058_VREG_ID_L13, "8058_l13", &pm8058_ldo_ops),
+	VREG_DESCRIP(PM8058_VREG_ID_L14, "8058_l14", &pm8058_ldo_ops),
+	VREG_DESCRIP(PM8058_VREG_ID_L15, "8058_l15", &pm8058_ldo_ops),
+	VREG_DESCRIP(PM8058_VREG_ID_L16, "8058_l16", &pm8058_ldo_ops),
+	VREG_DESCRIP(PM8058_VREG_ID_L17, "8058_l17", &pm8058_ldo_ops),
+	VREG_DESCRIP(PM8058_VREG_ID_L18, "8058_l18", &pm8058_ldo_ops),
+	VREG_DESCRIP(PM8058_VREG_ID_L19, "8058_l19", &pm8058_ldo_ops),
+	VREG_DESCRIP(PM8058_VREG_ID_L20, "8058_l20", &pm8058_ldo_ops),
+	VREG_DESCRIP(PM8058_VREG_ID_L21, "8058_l21", &pm8058_ldo_ops),
+	VREG_DESCRIP(PM8058_VREG_ID_L22, "8058_l22", &pm8058_ldo_ops),
+	VREG_DESCRIP(PM8058_VREG_ID_L23, "8058_l23", &pm8058_ldo_ops),
+	VREG_DESCRIP(PM8058_VREG_ID_L24, "8058_l24", &pm8058_ldo_ops),
+	VREG_DESCRIP(PM8058_VREG_ID_L25, "8058_l25", &pm8058_ldo_ops),
+
+	VREG_DESCRIP(PM8058_VREG_ID_S0, "8058_s0", &pm8058_smps_ops),
+	VREG_DESCRIP(PM8058_VREG_ID_S1, "8058_s1", &pm8058_smps_ops),
+	VREG_DESCRIP(PM8058_VREG_ID_S2, "8058_s2", &pm8058_smps_ops),
+	VREG_DESCRIP(PM8058_VREG_ID_S3, "8058_s3", &pm8058_smps_ops),
+	VREG_DESCRIP(PM8058_VREG_ID_S4, "8058_s4", &pm8058_smps_ops),
+
+	VREG_DESCRIP(PM8058_VREG_ID_LVS0, "8058_lvs0", &pm8058_lvs_ops),
+	VREG_DESCRIP(PM8058_VREG_ID_LVS1, "8058_lvs1", &pm8058_lvs_ops),
+
+	VREG_DESCRIP(PM8058_VREG_ID_NCP, "8058_ncp", &pm8058_ncp_ops),
+};
+
+static int pm8058_master_enable_init(struct pm8058_vreg *vreg)
+{
+	int rc = 0, i;
+
+	for (i = 0; i < MASTER_ENABLE_COUNT; i++) {
+		rc = pm8xxx_readb(vreg->dev->parent, m_en[i].addr,
+							&(m_en[i].reg));
+		if (rc)
+			goto bail;
+	}
+
+bail:
+	if (rc)
+		pr_err("%s: pm8xxx_read failed, rc=%d\n", __func__, rc);
+
+	return rc;
+}
+
+static int pm8058_init_ldo(struct pm8058_vreg *vreg)
+{
+	int rc = 0, i;
+	u8 bank;
+
+	/* Save the current test register state. */
+	for (i = 0; i < LDO_TEST_BANKS; i++) {
+		bank = REGULATOR_BANK_SEL(i);
+		rc = pm8xxx_writeb(vreg->dev->parent, vreg->test_addr, bank);
+		if (rc)
+			goto bail;
+
+		rc = pm8xxx_readb(vreg->dev->parent, vreg->test_addr,
+							&vreg->test_reg[i]);
+		if (rc)
+			goto bail;
+		vreg->test_reg[i] |= REGULATOR_BANK_WRITE;
+	}
+
+	if ((vreg->ctrl_reg & LDO_CTRL_PM_MASK) == LDO_CTRL_PM_LPM)
+		vreg->optimum = REGULATOR_MODE_STANDBY;
+	else
+		vreg->optimum = REGULATOR_MODE_FAST;
+
+	/* Set pull down enable based on platform data. */
+	rc = pm8058_vreg_write(vreg, vreg->ctrl_addr,
+		     (vreg->pdata->pull_down_enable ? LDO_PULL_DOWN_ENABLE : 0),
+		     LDO_PULL_DOWN_ENABLE_MASK, &vreg->ctrl_reg);
+bail:
+	return rc;
+}
+
+static int pm8058_init_smps(struct pm8058_vreg *vreg)
+{
+	int rc = 0, i;
+	u8 bank;
+
+	/* Save the current test2 register state. */
+	for (i = 0; i < SMPS_TEST_BANKS; i++) {
+		bank = REGULATOR_BANK_SEL(i);
+		rc = pm8xxx_writeb(vreg->dev->parent, vreg->test_addr, bank);
+		if (rc)
+			goto bail;
+
+		rc = pm8xxx_readb(vreg->dev->parent, vreg->test_addr,
+							&vreg->test_reg[i]);
+		if (rc)
+			goto bail;
+		vreg->test_reg[i] |= REGULATOR_BANK_WRITE;
+	}
+
+	/* Save the current clock control register state. */
+	rc = pm8xxx_readb(vreg->dev->parent, vreg->clk_ctrl_addr,
+						&vreg->clk_ctrl_reg);
+	if (rc)
+		goto bail;
+
+	/* Save the current sleep control register state. */
+	rc = pm8xxx_readb(vreg->dev->parent, vreg->sleep_ctrl_addr,
+						&vreg->sleep_ctrl_reg);
+	if (rc)
+		goto bail;
+
+	vreg->save_uV = 1; /* This is not a no-op. */
+	vreg->save_uV = _pm8058_smps_get_voltage(vreg);
+
+	if ((vreg->clk_ctrl_reg & SMPS_CLK_CTRL_MASK) == SMPS_CLK_CTRL_PFM)
+		vreg->optimum = REGULATOR_MODE_STANDBY;
+	else
+		vreg->optimum = REGULATOR_MODE_FAST;
+
+	/* Set advanced mode pull down enable based on platform data. */
+	rc = pm8058_vreg_write(vreg, vreg->test_addr,
+		(vreg->pdata->pull_down_enable
+			? SMPS_ADVANCED_PULL_DOWN_ENABLE : 0)
+		| REGULATOR_BANK_SEL(6) | REGULATOR_BANK_WRITE,
+		REGULATOR_BANK_MASK | SMPS_ADVANCED_PULL_DOWN_ENABLE,
+		&vreg->test_reg[6]);
+	if (rc)
+		goto bail;
+
+	if (!SMPS_IN_ADVANCED_MODE(vreg)) {
+		/* Set legacy mode pull down enable based on platform data. */
+		rc = pm8058_vreg_write(vreg, vreg->ctrl_addr,
+			(vreg->pdata->pull_down_enable
+				? SMPS_LEGACY_PULL_DOWN_ENABLE : 0),
+			SMPS_LEGACY_PULL_DOWN_ENABLE, &vreg->ctrl_reg);
+		if (rc)
+			goto bail;
+	}
+
+bail:
+	return rc;
+}
+
+static int pm8058_init_lvs(struct pm8058_vreg *vreg)
+{
+	int rc = 0;
+
+	vreg->optimum = REGULATOR_MODE_FAST;
+
+	/* Set pull down enable based on platform data. */
+	rc = pm8058_vreg_write(vreg, vreg->ctrl_addr,
+		(vreg->pdata->pull_down_enable
+			? LVS_PULL_DOWN_ENABLE : LVS_PULL_DOWN_DISABLE),
+		LVS_PULL_DOWN_ENABLE_MASK, &vreg->ctrl_reg);
+	return rc;
+}
+
+static int pm8058_init_ncp(struct pm8058_vreg *vreg)
+{
+	int rc = 0;
+
+	/* Save the current test1 register state. */
+	rc = pm8xxx_readb(vreg->dev->parent, vreg->test_addr,
+					&vreg->test_reg[0]);
+	if (rc)
+		goto bail;
+
+	vreg->optimum = REGULATOR_MODE_FAST;
+
+bail:
+	return rc;
+}
+
+static int pm8058_init_regulator(struct pm8058_vreg *vreg)
+{
+	static int master_enable_inited;
+	int rc = 0;
+
+	vreg->mode_initialized = 0;
+
+	if (!master_enable_inited) {
+		rc = pm8058_master_enable_init(vreg);
+		if (!rc)
+			master_enable_inited = 1;
+	}
+
+	/* save the current control register state */
+	rc = pm8xxx_readb(vreg->dev->parent, vreg->ctrl_addr, &vreg->ctrl_reg);
+	if (rc)
+		goto bail;
+
+	switch (vreg->type) {
+	case REGULATOR_TYPE_LDO:
+		rc = pm8058_init_ldo(vreg);
+		break;
+	case REGULATOR_TYPE_SMPS:
+		rc = pm8058_init_smps(vreg);
+		break;
+	case REGULATOR_TYPE_LVS:
+		rc = pm8058_init_lvs(vreg);
+		break;
+	case REGULATOR_TYPE_NCP:
+		rc = pm8058_init_ncp(vreg);
+		break;
+	}
+
+bail:
+	if (rc)
+		pr_err("%s: pm8058_read/write failed; initial register states "
+			"unknown, rc=%d\n", __func__, rc);
+	return rc;
+}
+
+static int __devinit pm8058_vreg_probe(struct platform_device *pdev)
+{
+	struct regulator_desc *rdesc;
+	struct pm8058_vreg *vreg;
+	const char *reg_name = NULL;
+	int rc = 0;
+
+	if (pdev == NULL)
+		return -EINVAL;
+
+	if (pdev->id >= 0 && pdev->id < PM8058_VREG_MAX) {
+		rdesc = &pm8058_vreg_descrip[pdev->id];
+		vreg = &pm8058_vreg[pdev->id];
+		vreg->pdata = pdev->dev.platform_data;
+		reg_name = pm8058_vreg_descrip[pdev->id].name;
+		vreg->dev = &pdev->dev;
+
+		rc = pm8058_init_regulator(vreg);
+		if (rc)
+			goto bail;
+
+		/* Disallow idle and normal modes if pin control isn't set. */
+		if (vreg->pdata->pin_ctrl == 0)
+			vreg->pdata->init_data.constraints.valid_modes_mask
+			      &= ~(REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE);
+
+		vreg->rdev = regulator_register(rdesc, &pdev->dev,
+				&vreg->pdata->init_data, vreg, NULL);
+		if (IS_ERR(vreg->rdev)) {
+			rc = PTR_ERR(vreg->rdev);
+			pr_err("%s: regulator_register failed for %s, rc=%d\n",
+				__func__, reg_name, rc);
+		}
+	} else {
+		rc = -ENODEV;
+	}
+
+bail:
+	if (rc)
+		pr_err("%s: error for %s, rc=%d\n", __func__, reg_name, rc);
+
+	return rc;
+}
+
+static int __devexit pm8058_vreg_remove(struct platform_device *pdev)
+{
+	regulator_unregister(pm8058_vreg[pdev->id].rdev);
+	return 0;
+}
+
+static struct platform_driver pm8058_vreg_driver = {
+	.probe = pm8058_vreg_probe,
+	.remove = __devexit_p(pm8058_vreg_remove),
+	.driver = {
+		.name = "pm8058-regulator",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init pm8058_vreg_init(void)
+{
+	return platform_driver_register(&pm8058_vreg_driver);
+}
+
+static void __exit pm8058_vreg_exit(void)
+{
+	platform_driver_unregister(&pm8058_vreg_driver);
+}
+
+static void print_write_error(struct pm8058_vreg *vreg, int rc,
+				const char *func)
+{
+	const char *reg_name = NULL;
+	ptrdiff_t id = vreg - pm8058_vreg;
+
+	if (id >= 0 && id < PM8058_VREG_MAX)
+		reg_name = pm8058_vreg_descrip[id].name;
+	pr_err("%s: pm8058_vreg_write failed for %s, rc=%d\n",
+		func, reg_name, rc);
+}
+
+subsys_initcall(pm8058_vreg_init);
+module_exit(pm8058_vreg_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC8058 regulator driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pm8058-regulator");
diff --git a/drivers/regulator/pmic8901-regulator.c b/drivers/regulator/pmic8901-regulator.c
new file mode 100644
index 0000000..02c9549
--- /dev/null
+++ b/drivers/regulator/pmic8901-regulator.c
@@ -0,0 +1,1018 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/mfd/pmic8901.h>
+#include <linux/regulator/driver.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/regulator/pmic8901-regulator.h>
+#include <linux/module.h>
+
+/* Regulator types */
+#define REGULATOR_TYPE_LDO		0
+#define REGULATOR_TYPE_SMPS		1
+#define REGULATOR_TYPE_VS		2
+
+/* Bank select/write macros */
+#define REGULATOR_BANK_SEL(n)           ((n) << 4)
+#define REGULATOR_BANK_WRITE            0x80
+#define LDO_TEST_BANKS			7
+#define REGULATOR_BANK_MASK		0xF0
+
+/* Pin mask resource register programming */
+#define VREG_PMR_STATE_MASK		0x60
+#define VREG_PMR_STATE_HPM		0x60
+#define VREG_PMR_STATE_LPM		0x40
+#define VREG_PMR_STATE_OFF		0x20
+#define VREG_PMR_STATE_PIN_CTRL		0x20
+
+#define VREG_PMR_MODE_ACTION_MASK	0x10
+#define VREG_PMR_MODE_ACTION_SLEEP	0x10
+#define VREG_PMR_MODE_ACTION_OFF	0x00
+
+#define VREG_PMR_MODE_PIN_MASK		0x08
+#define VREG_PMR_MODE_PIN_MASKED	0x08
+
+#define VREG_PMR_CTRL_PIN2_MASK		0x04
+#define VREG_PMR_CTRL_PIN2_MASKED	0x04
+
+#define VREG_PMR_CTRL_PIN1_MASK		0x02
+#define VREG_PMR_CTRL_PIN1_MASKED	0x02
+
+#define VREG_PMR_CTRL_PIN0_MASK		0x01
+#define VREG_PMR_CTRL_PIN0_MASKED	0x01
+
+#define VREG_PMR_PIN_CTRL_ALL_MASK	0x1F
+#define VREG_PMR_PIN_CTRL_ALL_MASKED	0x1F
+
+#define REGULATOR_IS_EN(pmr_reg) \
+	((pmr_reg & VREG_PMR_STATE_MASK) == VREG_PMR_STATE_HPM || \
+	 (pmr_reg & VREG_PMR_STATE_MASK) == VREG_PMR_STATE_LPM)
+
+/* FTSMPS programming */
+
+/* CTRL register */
+#define SMPS_VCTRL_BAND_MASK		0xC0
+#define SMPS_VCTRL_BAND_OFF		0x00
+#define SMPS_VCTRL_BAND_1		0x40
+#define SMPS_VCTRL_BAND_2		0x80
+#define SMPS_VCTRL_BAND_3		0xC0
+#define SMPS_VCTRL_VPROG_MASK		0x3F
+
+#define SMPS_BAND_1_UV_MIN		350000
+#define SMPS_BAND_1_UV_MAX		650000
+#define SMPS_BAND_1_UV_STEP		6250
+
+#define SMPS_BAND_2_UV_MIN		700000
+#define SMPS_BAND_2_UV_MAX		1400000
+#define SMPS_BAND_2_UV_STEP		12500
+
+#define SMPS_BAND_3_UV_SETPOINT_MIN	1500000
+#define SMPS_BAND_3_UV_MIN		1400000
+#define SMPS_BAND_3_UV_MAX		3300000
+#define SMPS_BAND_3_UV_STEP		50000
+
+#define SMPS_UV_MIN			SMPS_BAND_1_UV_MIN
+#define SMPS_UV_MAX			SMPS_BAND_3_UV_MAX
+
+/* PWR_CNFG register */
+#define SMPS_PULL_DOWN_ENABLE_MASK	0x40
+#define SMPS_PULL_DOWN_ENABLE		0x40
+
+/* LDO programming */
+
+/* CTRL register */
+#define LDO_LOCAL_ENABLE_MASK		0x80
+#define LDO_LOCAL_ENABLE		0x80
+
+#define LDO_PULL_DOWN_ENABLE_MASK	0x40
+#define LDO_PULL_DOWN_ENABLE		0x40
+
+#define LDO_CTRL_VPROG_MASK		0x1F
+
+/* TEST register bank 2 */
+#define LDO_TEST_VPROG_UPDATE_MASK	0x08
+#define LDO_TEST_RANGE_SEL_MASK		0x04
+#define LDO_TEST_FINE_STEP_MASK		0x02
+#define LDO_TEST_FINE_STEP_SHIFT	1
+
+/* TEST register bank 4 */
+#define LDO_TEST_RANGE_EXT_MASK	0x01
+
+/* Allowable voltage ranges */
+#define PLDO_LOW_UV_MIN			750000
+#define PLDO_LOW_UV_MAX			1537500
+#define PLDO_LOW_FINE_STEP_UV		12500
+
+#define PLDO_NORM_UV_MIN		1500000
+#define PLDO_NORM_UV_MAX		3075000
+#define PLDO_NORM_FINE_STEP_UV		25000
+
+#define PLDO_HIGH_UV_MIN		1750000
+#define PLDO_HIGH_UV_MAX		4900000
+#define PLDO_HIGH_FINE_STEP_UV		50000
+
+#define NLDO_UV_MIN			750000
+#define NLDO_UV_MAX			1537500
+#define NLDO_FINE_STEP_UV		12500
+
+/* VS programming */
+
+/* CTRL register */
+#define VS_CTRL_ENABLE_MASK		0xC0
+#define VS_CTRL_DISABLE			0x00
+#define VS_CTRL_ENABLE			0x40
+#define VS_CTRL_USE_PMR			0xC0
+
+#define VS_PULL_DOWN_ENABLE_MASK	0x20
+#define VS_PULL_DOWN_ENABLE		0x20
+
+struct pm8901_vreg {
+	struct device			*dev;
+	struct pm8901_vreg_pdata	*pdata;
+	struct regulator_dev		*rdev;
+	int				hpm_min_load;
+	unsigned			pc_vote;
+	unsigned			optimum;
+	unsigned			mode_initialized;
+	u16				ctrl_addr;
+	u16				pmr_addr;
+	u16				test_addr;
+	u16				pfm_ctrl_addr;
+	u16				pwr_cnfg_addr;
+	u8				type;
+	u8				ctrl_reg;
+	u8				pmr_reg;
+	u8				test_reg[LDO_TEST_BANKS];
+	u8				pfm_ctrl_reg;
+	u8				pwr_cnfg_reg;
+	u8				is_nmos;
+	u8				state;
+};
+
+/*
+ * These are used to compensate for the PMIC 8901 v1 FTS regulators which
+ * output ~10% higher than the programmed set point.
+ */
+#define IS_PMIC_8901_V1(rev)		((rev) == PM8XXX_REVISION_8901_1p0 || \
+					 (rev) == PM8XXX_REVISION_8901_1p1)
+
+#define PMIC_8901_V1_SCALE(uV)		((((uV) - 62100) * 23) / 25)
+
+#define PMIC_8901_V1_SCALE_INV(uV)	(((uV) * 25) / 23 + 62100)
+
+/*
+ * Band 1 of PMIC 8901 SMPS regulators only supports set points with the 3 LSB's
+ * equal to 0.  This is accomplished in the macro by truncating the bits.
+ */
+#define PM8901_SMPS_BAND_1_COMPENSATE(vprog)	((vprog) & 0xF8)
+
+#define LDO(_id, _ctrl_addr, _pmr_addr, _test_addr, _is_nmos) \
+	[_id] = { \
+		.ctrl_addr = _ctrl_addr, \
+		.pmr_addr = _pmr_addr, \
+		.test_addr = _test_addr, \
+		.type = REGULATOR_TYPE_LDO, \
+		.is_nmos = _is_nmos, \
+		.hpm_min_load = PM8901_VREG_LDO_300_HPM_MIN_LOAD, \
+	}
+
+#define SMPS(_id, _ctrl_addr, _pmr_addr, _pfm_ctrl_addr, _pwr_cnfg_addr) \
+	[_id] = { \
+		.ctrl_addr = _ctrl_addr, \
+		.pmr_addr = _pmr_addr, \
+		.pfm_ctrl_addr = _pfm_ctrl_addr, \
+		.pwr_cnfg_addr = _pwr_cnfg_addr, \
+		.type = REGULATOR_TYPE_SMPS, \
+		.hpm_min_load = PM8901_VREG_FTSMPS_HPM_MIN_LOAD, \
+	}
+
+#define VS(_id, _ctrl_addr, _pmr_addr) \
+	[_id] = { \
+		.ctrl_addr = _ctrl_addr, \
+		.pmr_addr = _pmr_addr, \
+		.type = REGULATOR_TYPE_VS, \
+	}
+
+static struct pm8901_vreg pm8901_vreg[] = {
+	/*  id                 ctrl   pmr    tst    n/p */
+	LDO(PM8901_VREG_ID_L0, 0x02F, 0x0AB, 0x030, 1),
+	LDO(PM8901_VREG_ID_L1, 0x031, 0x0AC, 0x032, 0),
+	LDO(PM8901_VREG_ID_L2, 0x033, 0x0AD, 0x034, 0),
+	LDO(PM8901_VREG_ID_L3, 0x035, 0x0AE, 0x036, 0),
+	LDO(PM8901_VREG_ID_L4, 0x037, 0x0AF, 0x038, 0),
+	LDO(PM8901_VREG_ID_L5, 0x039, 0x0B0, 0x03A, 0),
+	LDO(PM8901_VREG_ID_L6, 0x03B, 0x0B1, 0x03C, 0),
+
+	/*   id                 ctrl   pmr    pfm    pwr */
+	SMPS(PM8901_VREG_ID_S0, 0x05B, 0x0A6, 0x05C, 0x0E3),
+	SMPS(PM8901_VREG_ID_S1, 0x06A, 0x0A7, 0x06B, 0x0EC),
+	SMPS(PM8901_VREG_ID_S2, 0x079, 0x0A8, 0x07A, 0x0F1),
+	SMPS(PM8901_VREG_ID_S3, 0x088, 0x0A9, 0x089, 0x0F6),
+	SMPS(PM8901_VREG_ID_S4, 0x097, 0x0AA, 0x098, 0x0FB),
+
+	/* id                       ctrl   pmr */
+	VS(PM8901_VREG_ID_LVS0,     0x046, 0x0B2),
+	VS(PM8901_VREG_ID_LVS1,     0x048, 0x0B3),
+	VS(PM8901_VREG_ID_LVS2,     0x04A, 0x0B4),
+	VS(PM8901_VREG_ID_LVS3,     0x04C, 0x0B5),
+	VS(PM8901_VREG_ID_MVS0,     0x052, 0x0B6),
+	VS(PM8901_VREG_ID_USB_OTG,  0x055, 0x0B7),
+	VS(PM8901_VREG_ID_HDMI_MVS, 0x058, 0x0B8),
+};
+
+static void print_write_error(struct pm8901_vreg *vreg, int rc,
+				const char *func);
+
+static int pm8901_vreg_write(struct pm8901_vreg *vreg,
+		u16 addr, u8 val, u8 mask, u8 *reg_save)
+{
+	int rc = 0;
+	u8 reg;
+
+	reg = (*reg_save & ~mask) | (val & mask);
+	if (reg != *reg_save)
+		rc = pm8xxx_writeb(vreg->dev->parent, addr, reg);
+	if (!rc)
+		*reg_save = reg;
+	return rc;
+}
+
+/* Set pin control bits based on new mode. */
+static int pm8901_vreg_select_pin_ctrl(struct pm8901_vreg *vreg, u8 *pmr_reg)
+{
+	*pmr_reg |= VREG_PMR_PIN_CTRL_ALL_MASKED;
+
+	if ((*pmr_reg & VREG_PMR_STATE_MASK) == VREG_PMR_STATE_PIN_CTRL) {
+		if (vreg->pdata->pin_fn == PM8901_VREG_PIN_FN_MODE)
+			*pmr_reg = (*pmr_reg & ~VREG_PMR_STATE_MASK)
+				   | VREG_PMR_STATE_LPM;
+		if (vreg->pdata->pin_ctrl & PM8901_VREG_PIN_CTRL_A0)
+			*pmr_reg &= ~VREG_PMR_CTRL_PIN0_MASKED;
+		if (vreg->pdata->pin_ctrl & PM8901_VREG_PIN_CTRL_A1)
+			*pmr_reg &= ~VREG_PMR_CTRL_PIN1_MASKED;
+		if (vreg->pdata->pin_ctrl & PM8901_VREG_PIN_CTRL_D0)
+			*pmr_reg &= ~VREG_PMR_CTRL_PIN2_MASKED;
+	}
+
+	return 0;
+}
+
+static int pm8901_vreg_enable(struct regulator_dev *dev)
+{
+	struct pm8901_vreg *vreg = rdev_get_drvdata(dev);
+	u8 val = VREG_PMR_STATE_HPM;
+	int rc;
+
+	if (!vreg->mode_initialized && vreg->pc_vote)
+		val = VREG_PMR_STATE_PIN_CTRL;
+	else if (vreg->optimum == REGULATOR_MODE_FAST)
+		val = VREG_PMR_STATE_HPM;
+	else if (vreg->pc_vote)
+		val = VREG_PMR_STATE_PIN_CTRL;
+	else if (vreg->optimum == REGULATOR_MODE_STANDBY)
+		val = VREG_PMR_STATE_LPM;
+
+	pm8901_vreg_select_pin_ctrl(vreg, &val);
+
+	rc = pm8901_vreg_write(vreg, vreg->pmr_addr,
+			val,
+			VREG_PMR_STATE_MASK | VREG_PMR_PIN_CTRL_ALL_MASK,
+			&vreg->pmr_reg);
+	if (rc)
+		print_write_error(vreg, rc, __func__);
+
+	return rc;
+}
+
+static int pm8901_vreg_disable(struct regulator_dev *dev)
+{
+	struct pm8901_vreg *vreg = rdev_get_drvdata(dev);
+	int rc;
+
+	rc = pm8901_vreg_write(vreg, vreg->pmr_addr,
+			VREG_PMR_STATE_OFF | VREG_PMR_PIN_CTRL_ALL_MASKED,
+			VREG_PMR_STATE_MASK | VREG_PMR_PIN_CTRL_ALL_MASK,
+			&vreg->pmr_reg);
+	if (rc)
+		print_write_error(vreg, rc, __func__);
+
+	return rc;
+}
+
+/*
+ * Cases that count as enabled:
+ *
+ * 1. PMR register has mode == HPM or LPM.
+ * 2. Any pin control bits are unmasked.
+ * 3. The regulator is an LDO and its local enable bit is set.
+ */
+static int _pm8901_vreg_is_enabled(struct pm8901_vreg *vreg)
+{
+	if ((vreg->type == REGULATOR_TYPE_LDO)
+	    && (vreg->ctrl_reg & LDO_LOCAL_ENABLE_MASK))
+		return 1;
+	else if (vreg->type == REGULATOR_TYPE_VS) {
+		if ((vreg->ctrl_reg & VS_CTRL_ENABLE_MASK) == VS_CTRL_ENABLE)
+			return 1;
+		else if ((vreg->ctrl_reg & VS_CTRL_ENABLE_MASK)
+			 == VS_CTRL_DISABLE)
+			return 0;
+	}
+
+	return REGULATOR_IS_EN(vreg->pmr_reg)
+		|| ((vreg->pmr_reg & VREG_PMR_PIN_CTRL_ALL_MASK)
+		   != VREG_PMR_PIN_CTRL_ALL_MASKED);
+}
+
+static int pm8901_vreg_is_enabled(struct regulator_dev *dev)
+{
+	struct pm8901_vreg *vreg = rdev_get_drvdata(dev);
+
+	return _pm8901_vreg_is_enabled(vreg);
+}
+
+static int pm8901_ldo_disable(struct regulator_dev *dev)
+{
+	struct pm8901_vreg *vreg = rdev_get_drvdata(dev);
+	int rc;
+
+	/* Disassert local enable bit in CTRL register. */
+	rc = pm8901_vreg_write(vreg, vreg->ctrl_addr, 0, LDO_LOCAL_ENABLE_MASK,
+			&vreg->ctrl_reg);
+	if (rc)
+		print_write_error(vreg, rc, __func__);
+
+	/* Disassert enable bit in PMR register. */
+	rc = pm8901_vreg_disable(dev);
+
+	return rc;
+}
+
+static int pm8901_pldo_set_voltage(struct pm8901_vreg *vreg, int uV)
+{
+	int vmin, rc = 0;
+	unsigned vprog, fine_step;
+	u8 range_ext, range_sel, fine_step_reg;
+
+	if (uV < PLDO_LOW_UV_MIN || uV > PLDO_HIGH_UV_MAX)
+		return -EINVAL;
+
+	if (uV < PLDO_LOW_UV_MAX + PLDO_LOW_FINE_STEP_UV) {
+		vmin = PLDO_LOW_UV_MIN;
+		fine_step = PLDO_LOW_FINE_STEP_UV;
+		range_ext = 0;
+		range_sel = LDO_TEST_RANGE_SEL_MASK;
+	} else if (uV < PLDO_NORM_UV_MAX + PLDO_NORM_FINE_STEP_UV) {
+		vmin = PLDO_NORM_UV_MIN;
+		fine_step = PLDO_NORM_FINE_STEP_UV;
+		range_ext = 0;
+		range_sel = 0;
+	} else {
+		vmin = PLDO_HIGH_UV_MIN;
+		fine_step = PLDO_HIGH_FINE_STEP_UV;
+		range_ext = LDO_TEST_RANGE_EXT_MASK;
+		range_sel = 0;
+	}
+
+	vprog = (uV - vmin) / fine_step;
+	fine_step_reg = (vprog & 1) << LDO_TEST_FINE_STEP_SHIFT;
+	vprog >>= 1;
+
+	/*
+	 * Disable program voltage update if range extension, range select,
+	 * or fine step have changed and the regulator is enabled.
+	 */
+	if (_pm8901_vreg_is_enabled(vreg) &&
+		(((range_ext ^ vreg->test_reg[4]) & LDO_TEST_RANGE_EXT_MASK)
+		|| ((range_sel ^ vreg->test_reg[2]) & LDO_TEST_RANGE_SEL_MASK)
+		|| ((fine_step_reg ^ vreg->test_reg[2])
+			& LDO_TEST_FINE_STEP_MASK))) {
+		rc = pm8901_vreg_write(vreg, vreg->test_addr,
+			REGULATOR_BANK_SEL(2) | REGULATOR_BANK_WRITE,
+			REGULATOR_BANK_MASK | LDO_TEST_VPROG_UPDATE_MASK,
+			&vreg->test_reg[2]);
+		if (rc)
+			goto bail;
+	}
+
+	/* Write new voltage. */
+	rc = pm8901_vreg_write(vreg, vreg->ctrl_addr, vprog,
+				LDO_CTRL_VPROG_MASK, &vreg->ctrl_reg);
+	if (rc)
+		goto bail;
+
+	/* Write range extension. */
+	rc = pm8901_vreg_write(vreg, vreg->test_addr,
+			range_ext | REGULATOR_BANK_SEL(4)
+			 | REGULATOR_BANK_WRITE,
+			LDO_TEST_RANGE_EXT_MASK | REGULATOR_BANK_MASK,
+			&vreg->test_reg[4]);
+	if (rc)
+		goto bail;
+
+	/* Write fine step, range select and program voltage update. */
+	rc = pm8901_vreg_write(vreg, vreg->test_addr,
+			fine_step_reg | range_sel | REGULATOR_BANK_SEL(2)
+			 | REGULATOR_BANK_WRITE | LDO_TEST_VPROG_UPDATE_MASK,
+			LDO_TEST_FINE_STEP_MASK | LDO_TEST_RANGE_SEL_MASK
+			 | REGULATOR_BANK_MASK | LDO_TEST_VPROG_UPDATE_MASK,
+			&vreg->test_reg[2]);
+bail:
+	if (rc)
+		print_write_error(vreg, rc, __func__);
+
+	return rc;
+}
+
+static int pm8901_nldo_set_voltage(struct pm8901_vreg *vreg, int uV)
+{
+	unsigned vprog, fine_step_reg;
+	int rc;
+
+	if (uV < NLDO_UV_MIN || uV > NLDO_UV_MAX)
+		return -EINVAL;
+
+	vprog = (uV - NLDO_UV_MIN) / NLDO_FINE_STEP_UV;
+	fine_step_reg = (vprog & 1) << LDO_TEST_FINE_STEP_SHIFT;
+	vprog >>= 1;
+
+	/* Write new voltage. */
+	rc = pm8901_vreg_write(vreg, vreg->ctrl_addr, vprog,
+				LDO_CTRL_VPROG_MASK, &vreg->ctrl_reg);
+	if (rc)
+		print_write_error(vreg, rc, __func__);
+
+	/* Write fine step. */
+	rc = pm8901_vreg_write(vreg, vreg->test_addr,
+			fine_step_reg | REGULATOR_BANK_SEL(2)
+			 | REGULATOR_BANK_WRITE | LDO_TEST_VPROG_UPDATE_MASK,
+			LDO_TEST_FINE_STEP_MASK | REGULATOR_BANK_MASK
+			 | LDO_TEST_VPROG_UPDATE_MASK,
+		       &vreg->test_reg[2]);
+	if (rc)
+		print_write_error(vreg, rc, __func__);
+
+	return rc;
+}
+
+static int pm8901_ldo_set_voltage(struct regulator_dev *dev,
+		int min_uV, int max_uV, unsigned *selector)
+{
+	struct pm8901_vreg *vreg = rdev_get_drvdata(dev);
+
+	if (vreg->is_nmos)
+		return pm8901_nldo_set_voltage(vreg, min_uV);
+	else
+		return pm8901_pldo_set_voltage(vreg, min_uV);
+}
+
+static int pm8901_pldo_get_voltage(struct pm8901_vreg *vreg)
+{
+	int vmin, fine_step;
+	u8 range_ext, range_sel, vprog, fine_step_reg;
+
+	fine_step_reg = vreg->test_reg[2] & LDO_TEST_FINE_STEP_MASK;
+	range_sel = vreg->test_reg[2] & LDO_TEST_RANGE_SEL_MASK;
+	range_ext = vreg->test_reg[4] & LDO_TEST_RANGE_EXT_MASK;
+	vprog = vreg->ctrl_reg & LDO_CTRL_VPROG_MASK;
+
+	vprog = (vprog << 1) | (fine_step_reg >> LDO_TEST_FINE_STEP_SHIFT);
+
+	if (range_sel) {
+		/* low range mode */
+		fine_step = PLDO_LOW_FINE_STEP_UV;
+		vmin = PLDO_LOW_UV_MIN;
+	} else if (!range_ext) {
+		/* normal mode */
+		fine_step = PLDO_NORM_FINE_STEP_UV;
+		vmin = PLDO_NORM_UV_MIN;
+	} else {
+		/* high range mode */
+		fine_step = PLDO_HIGH_FINE_STEP_UV;
+		vmin = PLDO_HIGH_UV_MIN;
+	}
+
+	return fine_step * vprog + vmin;
+}
+
+static int pm8901_nldo_get_voltage(struct pm8901_vreg *vreg)
+{
+	u8 vprog, fine_step_reg;
+
+	fine_step_reg = vreg->test_reg[2] & LDO_TEST_FINE_STEP_MASK;
+	vprog = vreg->ctrl_reg & LDO_CTRL_VPROG_MASK;
+
+	vprog = (vprog << 1) | (fine_step_reg >> LDO_TEST_FINE_STEP_SHIFT);
+
+	return NLDO_FINE_STEP_UV * vprog + NLDO_UV_MIN;
+}
+
+static int pm8901_ldo_get_voltage(struct regulator_dev *dev)
+{
+	struct pm8901_vreg *vreg = rdev_get_drvdata(dev);
+
+	if (vreg->is_nmos)
+		return pm8901_nldo_get_voltage(vreg);
+	else
+		return pm8901_pldo_get_voltage(vreg);
+}
+
+/*
+ * Optimum mode programming:
+ * REGULATOR_MODE_FAST: Go to HPM (highest priority)
+ * REGULATOR_MODE_STANDBY: Go to pin ctrl mode if there are any pin ctrl
+ * votes, else go to LPM
+ *
+ * Pin ctrl mode voting via regulator set_mode:
+ * REGULATOR_MODE_IDLE: Go to pin ctrl mode if the optimum mode is LPM, else
+ * go to HPM
+ * REGULATOR_MODE_NORMAL: Go to LPM if it is the optimum mode, else go to HPM
+ */
+static int pm8901_vreg_set_mode(struct regulator_dev *dev, unsigned int mode)
+{
+	struct pm8901_vreg *vreg = rdev_get_drvdata(dev);
+	unsigned optimum = vreg->optimum;
+	unsigned pc_vote = vreg->pc_vote;
+	unsigned mode_initialized = vreg->mode_initialized;
+	u8 val = 0;
+	int rc = 0;
+
+	/* Determine new mode to go into. */
+	switch (mode) {
+	case REGULATOR_MODE_FAST:
+		val = VREG_PMR_STATE_HPM;
+		optimum = mode;
+		mode_initialized = 1;
+		break;
+
+	case REGULATOR_MODE_STANDBY:
+		if (pc_vote)
+			val = VREG_PMR_STATE_PIN_CTRL;
+		else
+			val = VREG_PMR_STATE_LPM;
+		optimum = mode;
+		mode_initialized = 1;
+		break;
+
+	case REGULATOR_MODE_IDLE:
+		if (pc_vote++)
+			goto done; /* already taken care of */
+
+		if (mode_initialized && optimum == REGULATOR_MODE_FAST)
+			val = VREG_PMR_STATE_HPM;
+		else
+			val = VREG_PMR_STATE_PIN_CTRL;
+		break;
+
+	case REGULATOR_MODE_NORMAL:
+		if (pc_vote && --pc_vote)
+			goto done; /* already taken care of */
+
+		if (optimum == REGULATOR_MODE_STANDBY)
+			val = VREG_PMR_STATE_LPM;
+		else
+			val = VREG_PMR_STATE_HPM;
+		break;
+
+	default:
+		pr_err("%s: unknown mode, mode=%u\n", __func__, mode);
+		return -EINVAL;
+	}
+
+	/* Set pin control bits based on new mode. */
+	pm8901_vreg_select_pin_ctrl(vreg, &val);
+
+	/* Only apply mode setting to hardware if currently enabled. */
+	if (pm8901_vreg_is_enabled(dev))
+		rc = pm8901_vreg_write(vreg, vreg->pmr_addr, val,
+			       VREG_PMR_STATE_MASK | VREG_PMR_PIN_CTRL_ALL_MASK,
+			       &vreg->pmr_reg);
+
+	if (rc) {
+		print_write_error(vreg, rc, __func__);
+		return rc;
+	}
+
+done:
+	vreg->mode_initialized = mode_initialized;
+	vreg->optimum = optimum;
+	vreg->pc_vote = pc_vote;
+
+	return 0;
+}
+
+static unsigned int pm8901_vreg_get_mode(struct regulator_dev *dev)
+{
+	struct pm8901_vreg *vreg = rdev_get_drvdata(dev);
+	int pin_mask = VREG_PMR_CTRL_PIN0_MASK | VREG_PMR_CTRL_PIN1_MASK
+			| VREG_PMR_CTRL_PIN2_MASK;
+
+	if (!vreg->mode_initialized && vreg->pc_vote)
+		return REGULATOR_MODE_IDLE;
+	else if (((vreg->pmr_reg & VREG_PMR_STATE_MASK) == VREG_PMR_STATE_OFF)
+		 && ((vreg->pmr_reg & pin_mask) != pin_mask))
+		return REGULATOR_MODE_IDLE;
+	else if (((vreg->pmr_reg & VREG_PMR_STATE_MASK) == VREG_PMR_STATE_LPM)
+		 && ((vreg->pmr_reg & pin_mask) != pin_mask))
+		return REGULATOR_MODE_IDLE;
+	else if (vreg->optimum == REGULATOR_MODE_FAST)
+		return REGULATOR_MODE_FAST;
+	else if (vreg->pc_vote)
+		return REGULATOR_MODE_IDLE;
+	else if (vreg->optimum == REGULATOR_MODE_STANDBY)
+		return REGULATOR_MODE_STANDBY;
+	return REGULATOR_MODE_FAST;
+}
+
+unsigned int pm8901_vreg_get_optimum_mode(struct regulator_dev *dev,
+		int input_uV, int output_uV, int load_uA)
+{
+	struct pm8901_vreg *vreg = rdev_get_drvdata(dev);
+
+	if (load_uA <= 0) {
+		/*
+		 * pm8901_vreg_get_optimum_mode is being called before consumers
+		 * have specified their load currents via
+		 * regulator_set_optimum_mode. Return whatever the existing mode
+		 * is.
+		 */
+		return pm8901_vreg_get_mode(dev);
+	}
+
+	if (load_uA >= vreg->hpm_min_load)
+		return REGULATOR_MODE_FAST;
+	return REGULATOR_MODE_STANDBY;
+}
+
+static int pm8901_smps_set_voltage(struct regulator_dev *dev,
+		int min_uV, int max_uV, unsigned *selector)
+{
+	struct pm8901_vreg *vreg = rdev_get_drvdata(dev);
+	int rc;
+	u8 val, band;
+
+	if (IS_PMIC_8901_V1(pm8xxx_get_revision(vreg->dev->parent)))
+		min_uV = PMIC_8901_V1_SCALE(min_uV);
+
+	if (min_uV < SMPS_BAND_1_UV_MIN || min_uV > SMPS_BAND_3_UV_MAX)
+		return -EINVAL;
+
+	/* Round down for set points in the gaps between bands. */
+	if (min_uV > SMPS_BAND_1_UV_MAX && min_uV < SMPS_BAND_2_UV_MIN)
+		min_uV = SMPS_BAND_1_UV_MAX;
+	else if (min_uV > SMPS_BAND_2_UV_MAX
+			&& min_uV < SMPS_BAND_3_UV_SETPOINT_MIN)
+		min_uV = SMPS_BAND_2_UV_MAX;
+
+	if (min_uV < SMPS_BAND_2_UV_MIN) {
+		val = ((min_uV - SMPS_BAND_1_UV_MIN) / SMPS_BAND_1_UV_STEP);
+		val = PM8901_SMPS_BAND_1_COMPENSATE(val);
+		band = SMPS_VCTRL_BAND_1;
+	} else if (min_uV < SMPS_BAND_3_UV_SETPOINT_MIN) {
+		val = ((min_uV - SMPS_BAND_2_UV_MIN) / SMPS_BAND_2_UV_STEP);
+		band = SMPS_VCTRL_BAND_2;
+	} else {
+		val = ((min_uV - SMPS_BAND_3_UV_MIN) / SMPS_BAND_3_UV_STEP);
+		band = SMPS_VCTRL_BAND_3;
+	}
+
+	rc = pm8901_vreg_write(vreg, vreg->ctrl_addr, band | val,
+			SMPS_VCTRL_BAND_MASK | SMPS_VCTRL_VPROG_MASK,
+			&vreg->ctrl_reg);
+	if (rc)
+		goto bail;
+
+	rc = pm8901_vreg_write(vreg, vreg->pfm_ctrl_addr, band | val,
+			SMPS_VCTRL_BAND_MASK | SMPS_VCTRL_VPROG_MASK,
+			&vreg->pfm_ctrl_reg);
+bail:
+	if (rc)
+		print_write_error(vreg, rc, __func__);
+
+	return rc;
+}
+
+static int pm8901_smps_get_voltage(struct regulator_dev *dev)
+{
+	struct pm8901_vreg *vreg = rdev_get_drvdata(dev);
+	u8 vprog, band;
+	int ret = 0;
+
+	if ((vreg->pmr_reg & VREG_PMR_STATE_MASK) == VREG_PMR_STATE_LPM) {
+		vprog = vreg->pfm_ctrl_reg & SMPS_VCTRL_VPROG_MASK;
+		band = vreg->pfm_ctrl_reg & SMPS_VCTRL_BAND_MASK;
+	} else {
+		vprog = vreg->ctrl_reg & SMPS_VCTRL_VPROG_MASK;
+		band = vreg->ctrl_reg & SMPS_VCTRL_BAND_MASK;
+	}
+
+	if (band == SMPS_VCTRL_BAND_1)
+		ret = vprog * SMPS_BAND_1_UV_STEP + SMPS_BAND_1_UV_MIN;
+	else if (band == SMPS_VCTRL_BAND_2)
+		ret = vprog * SMPS_BAND_2_UV_STEP + SMPS_BAND_2_UV_MIN;
+	else
+		ret = vprog * SMPS_BAND_3_UV_STEP + SMPS_BAND_3_UV_MIN;
+
+	if (IS_PMIC_8901_V1(pm8xxx_get_revision(vreg->dev->parent)))
+		ret = PMIC_8901_V1_SCALE_INV(ret);
+
+	return ret;
+}
+
+static int pm8901_vs_enable(struct regulator_dev *dev)
+{
+	struct pm8901_vreg *vreg = rdev_get_drvdata(dev);
+	int rc;
+
+	/* Assert enable bit in PMR register. */
+	rc = pm8901_vreg_enable(dev);
+
+	/* Make sure that switch is controlled via PMR register */
+	rc = pm8901_vreg_write(vreg, vreg->ctrl_addr, VS_CTRL_USE_PMR,
+			VS_CTRL_ENABLE_MASK, &vreg->ctrl_reg);
+	if (rc)
+		print_write_error(vreg, rc, __func__);
+
+	return rc;
+}
+
+static int pm8901_vs_disable(struct regulator_dev *dev)
+{
+	struct pm8901_vreg *vreg = rdev_get_drvdata(dev);
+	int rc;
+
+	/* Disassert enable bit in PMR register. */
+	rc = pm8901_vreg_disable(dev);
+
+	/* Make sure that switch is controlled via PMR register */
+	rc = pm8901_vreg_write(vreg, vreg->ctrl_addr, VS_CTRL_USE_PMR,
+			VS_CTRL_ENABLE_MASK, &vreg->ctrl_reg);
+	if (rc)
+		print_write_error(vreg, rc, __func__);
+
+	return rc;
+}
+
+static struct regulator_ops pm8901_ldo_ops = {
+	.enable = pm8901_vreg_enable,
+	.disable = pm8901_ldo_disable,
+	.is_enabled = pm8901_vreg_is_enabled,
+	.set_voltage = pm8901_ldo_set_voltage,
+	.get_voltage = pm8901_ldo_get_voltage,
+	.set_mode = pm8901_vreg_set_mode,
+	.get_mode = pm8901_vreg_get_mode,
+	.get_optimum_mode = pm8901_vreg_get_optimum_mode,
+};
+
+static struct regulator_ops pm8901_smps_ops = {
+	.enable = pm8901_vreg_enable,
+	.disable = pm8901_vreg_disable,
+	.is_enabled = pm8901_vreg_is_enabled,
+	.set_voltage = pm8901_smps_set_voltage,
+	.get_voltage = pm8901_smps_get_voltage,
+	.set_mode = pm8901_vreg_set_mode,
+	.get_mode = pm8901_vreg_get_mode,
+	.get_optimum_mode = pm8901_vreg_get_optimum_mode,
+};
+
+static struct regulator_ops pm8901_vs_ops = {
+	.enable = pm8901_vs_enable,
+	.disable = pm8901_vs_disable,
+	.is_enabled = pm8901_vreg_is_enabled,
+	.set_mode = pm8901_vreg_set_mode,
+	.get_mode = pm8901_vreg_get_mode,
+};
+
+#define VREG_DESCRIP(_id, _name, _ops) \
+	[_id] = { \
+		.name = _name, \
+		.id = _id, \
+		.ops = _ops, \
+		.type = REGULATOR_VOLTAGE, \
+		.owner = THIS_MODULE, \
+	}
+
+static struct regulator_desc pm8901_vreg_descrip[] = {
+	VREG_DESCRIP(PM8901_VREG_ID_L0, "8901_l0", &pm8901_ldo_ops),
+	VREG_DESCRIP(PM8901_VREG_ID_L1, "8901_l1", &pm8901_ldo_ops),
+	VREG_DESCRIP(PM8901_VREG_ID_L2, "8901_l2", &pm8901_ldo_ops),
+	VREG_DESCRIP(PM8901_VREG_ID_L3, "8901_l3", &pm8901_ldo_ops),
+	VREG_DESCRIP(PM8901_VREG_ID_L4, "8901_l4", &pm8901_ldo_ops),
+	VREG_DESCRIP(PM8901_VREG_ID_L5, "8901_l5", &pm8901_ldo_ops),
+	VREG_DESCRIP(PM8901_VREG_ID_L6, "8901_l6", &pm8901_ldo_ops),
+
+	VREG_DESCRIP(PM8901_VREG_ID_S0, "8901_s0", &pm8901_smps_ops),
+	VREG_DESCRIP(PM8901_VREG_ID_S1, "8901_s1", &pm8901_smps_ops),
+	VREG_DESCRIP(PM8901_VREG_ID_S2, "8901_s2", &pm8901_smps_ops),
+	VREG_DESCRIP(PM8901_VREG_ID_S3, "8901_s3", &pm8901_smps_ops),
+	VREG_DESCRIP(PM8901_VREG_ID_S4, "8901_s4", &pm8901_smps_ops),
+
+	VREG_DESCRIP(PM8901_VREG_ID_LVS0,     "8901_lvs0",     &pm8901_vs_ops),
+	VREG_DESCRIP(PM8901_VREG_ID_LVS1,     "8901_lvs1",     &pm8901_vs_ops),
+	VREG_DESCRIP(PM8901_VREG_ID_LVS2,     "8901_lvs2",     &pm8901_vs_ops),
+	VREG_DESCRIP(PM8901_VREG_ID_LVS3,     "8901_lvs3",     &pm8901_vs_ops),
+	VREG_DESCRIP(PM8901_VREG_ID_MVS0,     "8901_mvs0",     &pm8901_vs_ops),
+	VREG_DESCRIP(PM8901_VREG_ID_USB_OTG,  "8901_usb_otg",  &pm8901_vs_ops),
+	VREG_DESCRIP(PM8901_VREG_ID_HDMI_MVS, "8901_hdmi_mvs", &pm8901_vs_ops),
+};
+
+static int pm8901_init_ldo(struct pm8901_vreg *vreg)
+{
+	int rc = 0, i;
+	u8 bank;
+
+	/* Store current regulator register values. */
+	for (i = 0; i < LDO_TEST_BANKS; i++) {
+		bank = REGULATOR_BANK_SEL(i);
+		rc = pm8xxx_writeb(vreg->dev->parent, vreg->test_addr, bank);
+		if (rc)
+			goto bail;
+
+		rc = pm8xxx_readb(vreg->dev->parent, vreg->test_addr,
+							&vreg->test_reg[i]);
+		if (rc)
+			goto bail;
+
+		vreg->test_reg[i] |= REGULATOR_BANK_WRITE;
+	}
+
+	/* Set pull down enable based on platform data. */
+	rc = pm8901_vreg_write(vreg, vreg->ctrl_addr,
+		     (vreg->pdata->pull_down_enable ? LDO_PULL_DOWN_ENABLE : 0),
+		     LDO_PULL_DOWN_ENABLE_MASK, &vreg->ctrl_reg);
+bail:
+	return rc;
+}
+
+static int pm8901_init_smps(struct pm8901_vreg *vreg)
+{
+	int rc;
+
+	/* Store current regulator register values. */
+	rc = pm8xxx_readb(vreg->dev->parent, vreg->pfm_ctrl_addr,
+					 &vreg->pfm_ctrl_reg);
+	if (rc)
+		goto bail;
+
+	rc = pm8xxx_readb(vreg->dev->parent, vreg->pwr_cnfg_addr,
+				 &vreg->pwr_cnfg_reg);
+	if (rc)
+		goto bail;
+
+	/* Set pull down enable based on platform data. */
+	rc = pm8901_vreg_write(vreg, vreg->pwr_cnfg_addr,
+		    (vreg->pdata->pull_down_enable ? SMPS_PULL_DOWN_ENABLE : 0),
+		    SMPS_PULL_DOWN_ENABLE_MASK, &vreg->pwr_cnfg_reg);
+
+bail:
+	return rc;
+}
+
+static int pm8901_init_vs(struct pm8901_vreg *vreg)
+{
+	int rc = 0;
+
+	/* Set pull down enable based on platform data. */
+	rc = pm8901_vreg_write(vreg, vreg->ctrl_addr,
+		      (vreg->pdata->pull_down_enable ? VS_PULL_DOWN_ENABLE : 0),
+		      VS_PULL_DOWN_ENABLE_MASK, &vreg->ctrl_reg);
+
+	return rc;
+}
+
+static int pm8901_init_regulator(struct pm8901_vreg *vreg)
+{
+	int rc;
+
+	/* Store current regulator register values. */
+	rc = pm8xxx_readb(vreg->dev->parent, vreg->ctrl_addr, &vreg->ctrl_reg);
+	if (rc)
+		goto bail;
+
+	rc = pm8xxx_readb(vreg->dev->parent, vreg->pmr_addr, &vreg->pmr_reg);
+	if (rc)
+		goto bail;
+
+	/* Set initial mode based on hardware state. */
+	if ((vreg->pmr_reg & VREG_PMR_STATE_MASK) == VREG_PMR_STATE_LPM)
+		vreg->optimum = REGULATOR_MODE_STANDBY;
+	else
+		vreg->optimum = REGULATOR_MODE_FAST;
+
+	vreg->mode_initialized = 0;
+
+	if (vreg->type == REGULATOR_TYPE_LDO)
+		rc = pm8901_init_ldo(vreg);
+	else if (vreg->type == REGULATOR_TYPE_SMPS)
+		rc = pm8901_init_smps(vreg);
+	else if (vreg->type == REGULATOR_TYPE_VS)
+		rc = pm8901_init_vs(vreg);
+bail:
+	if (rc)
+		pr_err("%s: pm8901_read/write failed; initial register states "
+			"unknown, rc=%d\n", __func__, rc);
+
+	return rc;
+}
+
+static int __devinit pm8901_vreg_probe(struct platform_device *pdev)
+{
+	struct regulator_desc *rdesc;
+	struct pm8901_vreg *vreg;
+	const char *reg_name = NULL;
+	int rc = 0;
+
+	if (pdev == NULL)
+		return -EINVAL;
+
+	if (pdev->id >= 0 && pdev->id < PM8901_VREG_MAX) {
+		rdesc = &pm8901_vreg_descrip[pdev->id];
+		vreg = &pm8901_vreg[pdev->id];
+		vreg->pdata = pdev->dev.platform_data;
+		reg_name = pm8901_vreg_descrip[pdev->id].name;
+		vreg->dev = &pdev->dev;
+
+		rc = pm8901_init_regulator(vreg);
+		if (rc)
+			goto bail;
+
+		/* Disallow idle and normal modes if pin control isn't set. */
+		if (vreg->pdata->pin_ctrl == 0)
+			vreg->pdata->init_data.constraints.valid_modes_mask
+			      &= ~(REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE);
+
+		vreg->rdev = regulator_register(rdesc, &pdev->dev,
+				&vreg->pdata->init_data, vreg, NULL);
+		if (IS_ERR(vreg->rdev)) {
+			rc = PTR_ERR(vreg->rdev);
+			pr_err("%s: regulator_register failed for %s, rc=%d\n",
+				__func__, reg_name, rc);
+		}
+	} else {
+		rc = -ENODEV;
+	}
+
+bail:
+	if (rc)
+		pr_err("%s: error for %s, rc=%d\n", __func__, reg_name, rc);
+
+	return rc;
+}
+
+static int __devexit pm8901_vreg_remove(struct platform_device *pdev)
+{
+	regulator_unregister(pm8901_vreg[pdev->id].rdev);
+	return 0;
+}
+
+static struct platform_driver pm8901_vreg_driver = {
+	.probe = pm8901_vreg_probe,
+	.remove = __devexit_p(pm8901_vreg_remove),
+	.driver = {
+		.name = "pm8901-regulator",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init pm8901_vreg_init(void)
+{
+	return platform_driver_register(&pm8901_vreg_driver);
+}
+
+static void __exit pm8901_vreg_exit(void)
+{
+	platform_driver_unregister(&pm8901_vreg_driver);
+}
+
+static void print_write_error(struct pm8901_vreg *vreg, int rc,
+				const char *func)
+{
+	const char *reg_name = NULL;
+	ptrdiff_t id = vreg - pm8901_vreg;
+
+	if (id >= 0 && id < PM8901_VREG_MAX)
+		reg_name = pm8901_vreg_descrip[id].name;
+	pr_err("%s: pm8901_vreg_write failed for %s, rc=%d\n",
+		func, reg_name, rc);
+}
+
+subsys_initcall(pm8901_vreg_init);
+module_exit(pm8901_vreg_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC8901 regulator driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pm8901-regulator");
diff --git a/drivers/regulator/qpnp-regulator.c b/drivers/regulator/qpnp-regulator.c
new file mode 100644
index 0000000..120d17e
--- /dev/null
+++ b/drivers/regulator/qpnp-regulator.c
@@ -0,0 +1,1472 @@
+/*
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/spmi.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/qpnp-regulator.h>
+
+#include <mach/qpnp.h>
+
+/* Debug Flag Definitions */
+enum {
+	QPNP_VREG_DEBUG_REQUEST		= BIT(0), /* Show requests */
+	QPNP_VREG_DEBUG_DUPLICATE	= BIT(1), /* Show duplicate requests */
+	QPNP_VREG_DEBUG_INIT		= BIT(2), /* Show state after probe */
+	QPNP_VREG_DEBUG_WRITES		= BIT(3), /* Show SPMI writes */
+	QPNP_VREG_DEBUG_READS		= BIT(4), /* Show SPMI reads */
+};
+
+static int qpnp_vreg_debug_mask;
+module_param_named(
+	debug_mask, qpnp_vreg_debug_mask, int, S_IRUSR | S_IWUSR
+);
+
+#define vreg_err(vreg, fmt, ...) \
+	pr_err("%s: " fmt, vreg->rdesc.name, ##__VA_ARGS__)
+
+/* These types correspond to unique register layouts. */
+enum qpnp_regulator_logical_type {
+	QPNP_REGULATOR_LOGICAL_TYPE_SMPS,
+	QPNP_REGULATOR_LOGICAL_TYPE_LDO,
+	QPNP_REGULATOR_LOGICAL_TYPE_VS,
+	QPNP_REGULATOR_LOGICAL_TYPE_BOOST,
+	QPNP_REGULATOR_LOGICAL_TYPE_FTSMPS,
+};
+
+enum qpnp_regulator_type {
+	QPNP_REGULATOR_TYPE_HF_BUCK		= 0x03,
+	QPNP_REGULATOR_TYPE_LDO			= 0x04,
+	QPNP_REGULATOR_TYPE_VS			= 0x05,
+	QPNP_REGULATOR_TYPE_BOOST		= 0x1B,
+	QPNP_REGULATOR_TYPE_FTS			= 0x1C,
+};
+
+enum qpnp_regulator_subtype {
+	QPNP_REGULATOR_SUBTYPE_GP_CTL		= 0x08,
+	QPNP_REGULATOR_SUBTYPE_RF_CTL		= 0x09,
+	QPNP_REGULATOR_SUBTYPE_N50		= 0x01,
+	QPNP_REGULATOR_SUBTYPE_N150		= 0x02,
+	QPNP_REGULATOR_SUBTYPE_N300		= 0x03,
+	QPNP_REGULATOR_SUBTYPE_N600		= 0x04,
+	QPNP_REGULATOR_SUBTYPE_N1200		= 0x05,
+	QPNP_REGULATOR_SUBTYPE_P50		= 0x08,
+	QPNP_REGULATOR_SUBTYPE_P150		= 0x09,
+	QPNP_REGULATOR_SUBTYPE_P300		= 0x0A,
+	QPNP_REGULATOR_SUBTYPE_P600		= 0x0B,
+	QPNP_REGULATOR_SUBTYPE_P1200		= 0x0C,
+	QPNP_REGULATOR_SUBTYPE_LV100		= 0x01,
+	QPNP_REGULATOR_SUBTYPE_LV300		= 0x02,
+	QPNP_REGULATOR_SUBTYPE_MV300		= 0x08,
+	QPNP_REGULATOR_SUBTYPE_MV500		= 0x09,
+	QPNP_REGULATOR_SUBTYPE_HDMI		= 0x10,
+	QPNP_REGULATOR_SUBTYPE_OTG		= 0x11,
+	QPNP_REGULATOR_SUBTYPE_5V_BOOST		= 0x01,
+	QPNP_REGULATOR_SUBTYPE_FTS_CTL		= 0x08,
+};
+
+enum qpnp_common_regulator_registers {
+	QPNP_COMMON_REG_TYPE			= 0x04,
+	QPNP_COMMON_REG_SUBTYPE			= 0x05,
+	QPNP_COMMON_REG_VOLTAGE_RANGE		= 0x40,
+	QPNP_COMMON_REG_VOLTAGE_SET		= 0x41,
+	QPNP_COMMON_REG_MODE			= 0x45,
+	QPNP_COMMON_REG_ENABLE			= 0x46,
+	QPNP_COMMON_REG_PULL_DOWN		= 0x48,
+};
+
+enum qpnp_ldo_registers {
+	QPNP_LDO_REG_SOFT_START			= 0x4C,
+};
+
+enum qpnp_vs_registers {
+	QPNP_VS_REG_OCP				= 0x4A,
+	QPNP_VS_REG_SOFT_START			= 0x4C,
+};
+
+enum qpnp_boost_registers {
+	QPNP_BOOST_REG_CURRENT_LIMIT		= 0x40,
+};
+
+/* Used for indexing into ctrl_reg.  These are offets from 0x40 */
+enum qpnp_common_control_register_index {
+	QPNP_COMMON_IDX_VOLTAGE_RANGE		= 0,
+	QPNP_COMMON_IDX_VOLTAGE_SET		= 1,
+	QPNP_COMMON_IDX_MODE			= 5,
+	QPNP_COMMON_IDX_ENABLE			= 6,
+};
+
+enum qpnp_boost_control_register_index {
+	QPNP_BOOST_IDX_CURRENT_LIMIT		= 0,
+};
+
+/* Common regulator control register layout */
+#define QPNP_COMMON_ENABLE_MASK			0x80
+#define QPNP_COMMON_ENABLE			0x80
+#define QPNP_COMMON_DISABLE			0x00
+#define QPNP_COMMON_ENABLE_FOLLOW_HW_EN3_MASK	0x08
+#define QPNP_COMMON_ENABLE_FOLLOW_HW_EN2_MASK	0x04
+#define QPNP_COMMON_ENABLE_FOLLOW_HW_EN1_MASK	0x02
+#define QPNP_COMMON_ENABLE_FOLLOW_HW_EN0_MASK	0x01
+#define QPNP_COMMON_ENABLE_FOLLOW_ALL_MASK	0x0F
+
+/* Common regulator mode register layout */
+#define QPNP_COMMON_MODE_HPM_MASK		0x80
+#define QPNP_COMMON_MODE_AUTO_MASK		0x40
+#define QPNP_COMMON_MODE_BYPASS_MASK		0x20
+#define QPNP_COMMON_MODE_FOLLOW_AWAKE_MASK	0x10
+#define QPNP_COMMON_MODE_FOLLOW_HW_EN3_MASK	0x08
+#define QPNP_COMMON_MODE_FOLLOW_HW_EN2_MASK	0x04
+#define QPNP_COMMON_MODE_FOLLOW_HW_EN1_MASK	0x02
+#define QPNP_COMMON_MODE_FOLLOW_HW_EN0_MASK	0x01
+#define QPNP_COMMON_MODE_FOLLOW_ALL_MASK	0x1F
+
+/* Common regulator pull down control register layout */
+#define QPNP_COMMON_PULL_DOWN_ENABLE_MASK	0x80
+
+/* LDO regulator current limit control register layout */
+#define QPNP_LDO_CURRENT_LIMIT_ENABLE_MASK	0x80
+
+/* LDO regulator soft start control register layout */
+#define QPNP_LDO_SOFT_START_ENABLE_MASK		0x80
+
+/* VS regulator over current protection control register layout */
+#define QPNP_VS_OCP_ENABLE_MASK			0x80
+#define QPNP_VS_OCP_OVERRIDE_MASK		0x01
+#define QPNP_VS_OCP_DISABLE			0x00
+
+/* VS regulator soft start control register layout */
+#define QPNP_VS_SOFT_START_ENABLE_MASK		0x80
+#define QPNP_VS_SOFT_START_SEL_MASK		0x03
+
+/* Boost regulator current limit control register layout */
+#define QPNP_BOOST_CURRENT_LIMIT_ENABLE_MASK	0x80
+#define QPNP_BOOST_CURRENT_LIMIT_MASK		0x07
+
+/*
+ * This voltage in uV is returned by get_voltage functions when there is no way
+ * to determine the current voltage level.  It is needed because the regulator
+ * framework treats a 0 uV voltage as an error.
+ */
+#define VOLTAGE_UNKNOWN 1
+
+struct qpnp_voltage_range {
+	int					min_uV;
+	int					max_uV;
+	int					step_uV;
+	int					set_point_min_uV;
+	unsigned				n_voltages;
+	u8					range_sel;
+};
+
+struct qpnp_voltage_set_points {
+	struct qpnp_voltage_range		*range;
+	int					count;
+	unsigned				n_voltages;
+};
+
+struct qpnp_regulator_mapping {
+	enum qpnp_regulator_type		type;
+	enum qpnp_regulator_subtype		subtype;
+	enum qpnp_regulator_logical_type	logical_type;
+	struct regulator_ops			*ops;
+	struct qpnp_voltage_set_points		*set_points;
+	int					hpm_min_load;
+};
+
+struct qpnp_regulator {
+	struct regulator_desc			rdesc;
+	struct spmi_device			*spmi_dev;
+	struct regulator_dev			*rdev;
+	struct qpnp_voltage_set_points		*set_points;
+	enum qpnp_regulator_logical_type	logical_type;
+	int					enable_time;
+	int					ocp_enable_time;
+	int					ocp_enable;
+	int					system_load;
+	int					hpm_min_load;
+	u32					write_count;
+	u32					prev_write_count;
+	u16					base_addr;
+	/* ctrl_reg provides a shadow copy of register values 0x40 to 0x47. */
+	u8					ctrl_reg[8];
+};
+
+#define QPNP_VREG_MAP(_type, _subtype, _logical_type, _ops_val, \
+		      _set_points_val, _hpm_min_load) \
+	{ \
+		.type		= QPNP_REGULATOR_TYPE_##_type, \
+		.subtype	= QPNP_REGULATOR_SUBTYPE_##_subtype, \
+		.logical_type	= QPNP_REGULATOR_LOGICAL_TYPE_##_logical_type, \
+		.ops		= &qpnp_##_ops_val##_ops, \
+		.set_points	= &_set_points_val##_set_points, \
+		.hpm_min_load	= _hpm_min_load, \
+	}
+
+#define VOLTAGE_RANGE(_range_sel, _min_uV, _set_point_min_uV, _max_uV, \
+			_step_uV) \
+	{ \
+		.min_uV			= _min_uV, \
+		.set_point_min_uV	= _set_point_min_uV, \
+		.max_uV			= _max_uV, \
+		.step_uV		= _step_uV, \
+		.range_sel		= _range_sel, \
+	}
+
+#define SET_POINTS(_ranges) \
+{ \
+	.range	= _ranges, \
+	.count	= ARRAY_SIZE(_ranges), \
+};
+
+/*
+ * These tables contain the physically available PMIC regulator voltage setpoint
+ * ranges.  Where two ranges overlap in hardware, one of the ranges is trimmed
+ * to ensure that the setpoints available to software are monotonically
+ * increasing and unique.  The set_voltage callback functions expect these
+ * properties to hold.
+ */
+static struct qpnp_voltage_range pldo_ranges[] = {
+	VOLTAGE_RANGE(2,  750000,  750000, 1537500, 12500),
+	VOLTAGE_RANGE(3, 1500000, 1550000, 3075000, 25000),
+	VOLTAGE_RANGE(4, 1750000, 3100000, 4900000, 50000),
+};
+
+static struct qpnp_voltage_range nldo1_ranges[] = {
+	VOLTAGE_RANGE(2,  750000,  750000, 1537500, 12500),
+};
+
+static struct qpnp_voltage_range nldo2_ranges[] = {
+	VOLTAGE_RANGE(1,  375000,  375000,  768750,  6250),
+	VOLTAGE_RANGE(2,  750000,  775000, 1537500, 12500),
+};
+
+static struct qpnp_voltage_range smps_ranges[] = {
+	VOLTAGE_RANGE(0,  375000,  375000, 1562500, 12500),
+	VOLTAGE_RANGE(1, 1550000, 1575000, 3125000, 25000),
+};
+
+static struct qpnp_voltage_range ftsmps_ranges[] = {
+	VOLTAGE_RANGE(0,   80000,  350000, 1355000,  5000),
+	VOLTAGE_RANGE(1,  160000, 1360000, 2710000, 10000),
+};
+
+static struct qpnp_voltage_range boost_ranges[] = {
+	VOLTAGE_RANGE(0, 4000000, 4000000, 5550000, 50000),
+};
+
+static struct qpnp_voltage_set_points pldo_set_points = SET_POINTS(pldo_ranges);
+static struct qpnp_voltage_set_points nldo1_set_points
+					= SET_POINTS(nldo1_ranges);
+static struct qpnp_voltage_set_points nldo2_set_points
+					= SET_POINTS(nldo2_ranges);
+static struct qpnp_voltage_set_points smps_set_points = SET_POINTS(smps_ranges);
+static struct qpnp_voltage_set_points ftsmps_set_points
+					= SET_POINTS(ftsmps_ranges);
+static struct qpnp_voltage_set_points boost_set_points
+					= SET_POINTS(boost_ranges);
+static struct qpnp_voltage_set_points none_set_points;
+
+static struct qpnp_voltage_set_points *all_set_points[] = {
+	&pldo_set_points,
+	&nldo1_set_points,
+	&nldo2_set_points,
+	&smps_set_points,
+	&ftsmps_set_points,
+	&boost_set_points,
+};
+
+/* Determines which label to add to a debug print statement. */
+enum qpnp_regulator_action {
+	QPNP_REGULATOR_ACTION_INIT,
+	QPNP_REGULATOR_ACTION_ENABLE,
+	QPNP_REGULATOR_ACTION_DISABLE,
+	QPNP_REGULATOR_ACTION_VOLTAGE,
+	QPNP_REGULATOR_ACTION_MODE,
+};
+
+static void qpnp_vreg_show_state(struct regulator_dev *rdev,
+				   enum qpnp_regulator_action action);
+
+#define DEBUG_PRINT_BUFFER_SIZE 64
+static void fill_string(char *str, size_t str_len, u8 *buf, int buf_len)
+{
+	int pos = 0;
+	int i;
+
+	for (i = 0; i < buf_len; i++) {
+		pos += scnprintf(str + pos, str_len - pos, "0x%02X", buf[i]);
+		if (i < buf_len - 1)
+			pos += scnprintf(str + pos, str_len - pos, ", ");
+	}
+}
+
+static inline int qpnp_vreg_read(struct qpnp_regulator *vreg, u16 addr, u8 *buf,
+				 int len)
+{
+	char str[DEBUG_PRINT_BUFFER_SIZE];
+	int rc = 0;
+
+	rc = spmi_ext_register_readl(vreg->spmi_dev->ctrl, vreg->spmi_dev->sid,
+		vreg->base_addr + addr, buf, len);
+
+	if (!rc && (qpnp_vreg_debug_mask & QPNP_VREG_DEBUG_READS)) {
+		str[0] = '\0';
+		fill_string(str, DEBUG_PRINT_BUFFER_SIZE, buf, len);
+		pr_info(" %-11s:  read(0x%04X), sid=%d, len=%d; %s\n",
+			vreg->rdesc.name, vreg->base_addr + addr,
+			vreg->spmi_dev->sid, len, str);
+	}
+
+	return rc;
+}
+
+static inline int qpnp_vreg_write(struct qpnp_regulator *vreg, u16 addr,
+				u8 *buf, int len)
+{
+	char str[DEBUG_PRINT_BUFFER_SIZE];
+	int rc = 0;
+
+	if (qpnp_vreg_debug_mask & QPNP_VREG_DEBUG_WRITES) {
+		str[0] = '\0';
+		fill_string(str, DEBUG_PRINT_BUFFER_SIZE, buf, len);
+		pr_info("%-11s: write(0x%04X), sid=%d, len=%d; %s\n",
+			vreg->rdesc.name, vreg->base_addr + addr,
+			vreg->spmi_dev->sid, len, str);
+	}
+
+	rc = spmi_ext_register_writel(vreg->spmi_dev->ctrl,
+		vreg->spmi_dev->sid, vreg->base_addr + addr, buf, len);
+	if (!rc)
+		vreg->write_count += len;
+
+	return rc;
+}
+
+/*
+ * qpnp_vreg_write_optimized - write the minimum sized contiguous subset of buf
+ * @vreg:	qpnp_regulator pointer for this regulator
+ * @addr:	local SPMI address offset from this peripheral's base address
+ * @buf:	new data to write into the SPMI registers
+ * @buf_save:	old data in the registers
+ * @len:	number of bytes to write
+ *
+ * This function checks for unchanged register values between buf and buf_save
+ * starting at both ends of buf.  Only the contiguous subset in the middle of
+ * buf starting and ending with new values is sent.
+ *
+ * Consider the following example:
+ * buf offset: 0 1 2 3 4 5 6 7
+ * reg state:  U U C C U C U U
+ * (U = unchanged, C = changed)
+ * In this example registers 2 through 5 will be written with a single
+ * transaction.
+ */
+static inline int qpnp_vreg_write_optimized(struct qpnp_regulator *vreg,
+		u16 addr, u8 *buf, u8 *buf_save, int len)
+{
+	int i, rc, start, end;
+
+	for (i = 0; i < len; i++)
+		if (buf[i] != buf_save[i])
+			break;
+	start = i;
+
+	for (i = len - 1; i >= 0; i--)
+		if (buf[i] != buf_save[i])
+			break;
+	end = i;
+
+	if (start > end) {
+		/* No modified register values present. */
+		return 0;
+	}
+
+	rc = qpnp_vreg_write(vreg, addr + start, &buf[start], end - start + 1);
+	if (!rc)
+		for (i = start; i <= end; i++)
+			buf_save[i] = buf[i];
+
+	return rc;
+}
+
+/*
+ * Perform a masked write to a PMIC register only if the new value differs
+ * from the last value written to the register.  This removes redundant
+ * register writing.
+ */
+static int qpnp_vreg_masked_write(struct qpnp_regulator *vreg, u16 addr, u8 val,
+		u8 mask, u8 *reg_save)
+{
+	int rc = 0;
+	u8 reg;
+
+	reg = (*reg_save & ~mask) | (val & mask);
+	if (reg != *reg_save) {
+		rc = qpnp_vreg_write(vreg, addr, &reg, 1);
+
+		if (rc) {
+			vreg_err(vreg, "write failed; addr=0x%03X, rc=%d\n",
+				addr, rc);
+		} else {
+			*reg_save = reg;
+		}
+	}
+
+	return rc;
+}
+
+/*
+ * Perform a masked read-modify-write to a PMIC register only if the new value
+ * differs from the value currently in the register.  This removes redundant
+ * register writing.
+ */
+static int qpnp_vreg_masked_read_write(struct qpnp_regulator *vreg, u16 addr,
+		u8 val, u8 mask)
+{
+	int rc;
+	u8 reg;
+
+	rc = qpnp_vreg_read(vreg, addr, &reg, 1);
+	if (rc) {
+		vreg_err(vreg, "read failed; addr=0x%03X, rc=%d\n", addr, rc);
+		return rc;
+	}
+
+	return qpnp_vreg_masked_write(vreg, addr, val, mask, &reg);
+}
+
+static int qpnp_regulator_common_is_enabled(struct regulator_dev *rdev)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+
+	return (vreg->ctrl_reg[QPNP_COMMON_IDX_ENABLE]
+		& QPNP_COMMON_ENABLE_MASK)
+			== QPNP_COMMON_ENABLE;
+}
+
+static int qpnp_regulator_common_enable(struct regulator_dev *rdev)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+	int rc;
+
+	rc = qpnp_vreg_masked_write(vreg, QPNP_COMMON_REG_ENABLE,
+		QPNP_COMMON_ENABLE, QPNP_COMMON_ENABLE_MASK,
+		&vreg->ctrl_reg[QPNP_COMMON_IDX_ENABLE]);
+
+	if (rc)
+		vreg_err(vreg, "qpnp_vreg_masked_write failed, rc=%d\n", rc);
+	else
+		qpnp_vreg_show_state(rdev, QPNP_REGULATOR_ACTION_ENABLE);
+
+	return rc;
+}
+
+static int qpnp_regulator_vs_enable(struct regulator_dev *rdev)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+	int rc;
+	u8 reg;
+
+	if (vreg->ocp_enable == QPNP_REGULATOR_ENABLE) {
+		/* Disable OCP */
+		reg = QPNP_VS_OCP_DISABLE;
+		rc = qpnp_vreg_write(vreg, QPNP_VS_REG_OCP, &reg, 1);
+		if (rc)
+			goto fail;
+	}
+
+	rc = qpnp_regulator_common_enable(rdev);
+	if (rc)
+		goto fail;
+
+	if (vreg->ocp_enable == QPNP_REGULATOR_ENABLE) {
+		/* Wait for inrush current to subsided, then enable OCP. */
+		udelay(vreg->ocp_enable_time);
+		reg = QPNP_VS_OCP_ENABLE_MASK;
+		rc = qpnp_vreg_write(vreg, QPNP_VS_REG_OCP, &reg, 1);
+		if (rc)
+			goto fail;
+	}
+
+	return rc;
+fail:
+	vreg_err(vreg, "qpnp_vreg_write failed, rc=%d\n", rc);
+
+	return rc;
+}
+
+static int qpnp_regulator_common_disable(struct regulator_dev *rdev)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+	int rc;
+
+	rc = qpnp_vreg_masked_write(vreg, QPNP_COMMON_REG_ENABLE,
+		QPNP_COMMON_DISABLE, QPNP_COMMON_ENABLE_MASK,
+		&vreg->ctrl_reg[QPNP_COMMON_IDX_ENABLE]);
+
+	if (rc)
+		vreg_err(vreg, "qpnp_vreg_masked_write failed, rc=%d\n", rc);
+	else
+		qpnp_vreg_show_state(rdev, QPNP_REGULATOR_ACTION_DISABLE);
+
+	return rc;
+}
+
+static int qpnp_regulator_select_voltage(struct qpnp_regulator *vreg,
+		int min_uV, int max_uV, int *range_sel, int *voltage_sel)
+{
+	struct qpnp_voltage_range *range;
+	int uV = min_uV;
+	int lim_min_uV, lim_max_uV, i;
+
+	/* Check if request voltage is outside of physically settable range. */
+	lim_min_uV = vreg->set_points->range[0].set_point_min_uV;
+	lim_max_uV =
+		vreg->set_points->range[vreg->set_points->count - 1].max_uV;
+
+	if (uV < lim_min_uV && max_uV >= lim_min_uV)
+		uV = lim_min_uV;
+
+	if (uV < lim_min_uV || uV > lim_max_uV) {
+		vreg_err(vreg,
+			"request v=[%d, %d] is outside possible v=[%d, %d]\n",
+			 min_uV, max_uV, lim_min_uV, lim_max_uV);
+		return -EINVAL;
+	}
+
+	/* Find the range which uV is inside of. */
+	for (i = vreg->set_points->count - 1; i > 0; i--)
+		if (uV > vreg->set_points->range[i - 1].max_uV)
+			break;
+	range = &vreg->set_points->range[i];
+	*range_sel = range->range_sel;
+
+	/*
+	 * Force uV to be an allowed set point by applying a ceiling function to
+	 * the uV value.
+	 */
+	*voltage_sel = (uV - range->min_uV + range->step_uV - 1)
+			/ range->step_uV;
+	uV = *voltage_sel * range->step_uV + range->min_uV;
+
+	if (uV > max_uV) {
+		vreg_err(vreg,
+			"request v=[%d, %d] cannot be met by any set point; "
+			"next set point: %d\n",
+			min_uV, max_uV, uV);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int qpnp_regulator_common_set_voltage(struct regulator_dev *rdev,
+		int min_uV, int max_uV, unsigned *selector)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+	int rc, range_sel, voltage_sel;
+	u8 buf[2];
+
+	rc = qpnp_regulator_select_voltage(vreg, min_uV, max_uV, &range_sel,
+		&voltage_sel);
+	if (rc) {
+		vreg_err(vreg, "could not set voltage, rc=%d\n", rc);
+		return rc;
+	}
+
+	buf[0] = range_sel;
+	buf[1] = voltage_sel;
+	if ((vreg->ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_RANGE] != range_sel)
+	    && (vreg->ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_SET] == voltage_sel)) {
+		/* Handle latched range change. */
+		rc = qpnp_vreg_write(vreg, QPNP_COMMON_REG_VOLTAGE_RANGE,
+				buf, 2);
+		if (!rc) {
+			vreg->ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_RANGE] = buf[0];
+			vreg->ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_SET] = buf[1];
+		}
+	} else {
+		/* Either write can be optimized away safely. */
+		rc = qpnp_vreg_write_optimized(vreg,
+			QPNP_COMMON_REG_VOLTAGE_RANGE, buf,
+			&vreg->ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_RANGE], 2);
+	}
+
+	if (rc)
+		vreg_err(vreg, "SPMI write failed, rc=%d\n", rc);
+	else
+		qpnp_vreg_show_state(rdev, QPNP_REGULATOR_ACTION_VOLTAGE);
+
+	return rc;
+}
+
+static int qpnp_regulator_common_get_voltage(struct regulator_dev *rdev)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+	struct qpnp_voltage_range *range = NULL;
+	int range_sel, voltage_sel, i;
+
+	range_sel = vreg->ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_RANGE];
+	voltage_sel = vreg->ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_SET];
+
+	for (i = 0; i < vreg->set_points->count; i++) {
+		if (vreg->set_points->range[i].range_sel == range_sel) {
+			range = &vreg->set_points->range[i];
+			break;
+		}
+	}
+
+	if (!range) {
+		vreg_err(vreg, "voltage unknown, range %d is invalid\n",
+			range_sel);
+		return VOLTAGE_UNKNOWN;
+	}
+
+	return range->step_uV * voltage_sel + range->min_uV;
+}
+
+static int qpnp_regulator_boost_set_voltage(struct regulator_dev *rdev,
+		int min_uV, int max_uV, unsigned *selector)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+	int rc, range_sel, voltage_sel;
+
+	rc = qpnp_regulator_select_voltage(vreg, min_uV, max_uV, &range_sel,
+		&voltage_sel);
+	if (rc) {
+		vreg_err(vreg, "could not set voltage, rc=%d\n", rc);
+		return rc;
+	}
+
+	/*
+	 * Boost type regulators do not have range select register so only
+	 * voltage set register needs to be written.
+	 */
+	rc = qpnp_vreg_masked_write(vreg, QPNP_COMMON_REG_VOLTAGE_SET,
+	       voltage_sel, 0xFF, &vreg->ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_SET]);
+
+	if (rc)
+		vreg_err(vreg, "SPMI write failed, rc=%d\n", rc);
+	else
+		qpnp_vreg_show_state(rdev, QPNP_REGULATOR_ACTION_VOLTAGE);
+
+	return rc;
+}
+
+static int qpnp_regulator_boost_get_voltage(struct regulator_dev *rdev)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+	int voltage_sel = vreg->ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_SET];
+
+	return boost_ranges[0].step_uV * voltage_sel + boost_ranges[0].min_uV;
+}
+
+static int qpnp_regulator_common_list_voltage(struct regulator_dev *rdev,
+			unsigned selector)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+	int uV = 0;
+	int i;
+
+	if (selector >= vreg->set_points->n_voltages)
+		return 0;
+
+	for (i = 0; i < vreg->set_points->count; i++) {
+		if (selector < vreg->set_points->range[i].n_voltages) {
+			uV = selector * vreg->set_points->range[i].step_uV
+				+ vreg->set_points->range[i].set_point_min_uV;
+			break;
+		} else {
+			selector -= vreg->set_points->range[i].n_voltages;
+		}
+	}
+
+	return uV;
+}
+
+static unsigned int qpnp_regulator_common_get_mode(struct regulator_dev *rdev)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+
+	return (vreg->ctrl_reg[QPNP_COMMON_IDX_MODE]
+		& QPNP_COMMON_MODE_HPM_MASK)
+			? REGULATOR_MODE_NORMAL : REGULATOR_MODE_IDLE;
+}
+
+static int qpnp_regulator_common_set_mode(struct regulator_dev *rdev,
+					unsigned int mode)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+	int rc = 0;
+	u8 val;
+
+	if (mode != REGULATOR_MODE_NORMAL && mode != REGULATOR_MODE_IDLE) {
+		vreg_err(vreg, "invalid mode: %u\n", mode);
+		return -EINVAL;
+	}
+
+	val = (mode == REGULATOR_MODE_NORMAL ? QPNP_COMMON_MODE_HPM_MASK : 0);
+
+	rc = qpnp_vreg_masked_write(vreg, QPNP_COMMON_REG_MODE, val,
+		QPNP_COMMON_MODE_HPM_MASK,
+		&vreg->ctrl_reg[QPNP_COMMON_IDX_MODE]);
+
+	if (rc)
+		vreg_err(vreg, "SPMI write failed, rc=%d\n", rc);
+	else
+		qpnp_vreg_show_state(rdev, QPNP_REGULATOR_ACTION_MODE);
+
+	return rc;
+}
+
+static unsigned int qpnp_regulator_common_get_optimum_mode(
+		struct regulator_dev *rdev, int input_uV, int output_uV,
+		int load_uA)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+	unsigned int mode;
+
+	if (load_uA + vreg->system_load >= vreg->hpm_min_load)
+		mode = REGULATOR_MODE_NORMAL;
+	else
+		mode = REGULATOR_MODE_IDLE;
+
+	return mode;
+}
+
+static int qpnp_regulator_common_enable_time(struct regulator_dev *rdev)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+
+	return vreg->enable_time;
+}
+
+static const char const *qpnp_print_actions[] = {
+	[QPNP_REGULATOR_ACTION_INIT]	= "initial    ",
+	[QPNP_REGULATOR_ACTION_ENABLE]	= "enable     ",
+	[QPNP_REGULATOR_ACTION_DISABLE]	= "disable    ",
+	[QPNP_REGULATOR_ACTION_VOLTAGE]	= "set voltage",
+	[QPNP_REGULATOR_ACTION_MODE]	= "set mode   ",
+};
+
+static void qpnp_vreg_show_state(struct regulator_dev *rdev,
+				   enum qpnp_regulator_action action)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+	const char *action_label = qpnp_print_actions[action];
+	unsigned int mode = 0;
+	int uV = 0;
+	const char *mode_label = "";
+	enum qpnp_regulator_logical_type type;
+	const char *enable_label;
+	char pc_enable_label[5] = {'\0'};
+	char pc_mode_label[8] = {'\0'};
+	bool show_req, show_dupe, show_init, has_changed;
+	u8 en_reg, mode_reg;
+
+	/* Do not print unless appropriate flags are set. */
+	show_req = qpnp_vreg_debug_mask & QPNP_VREG_DEBUG_REQUEST;
+	show_dupe = qpnp_vreg_debug_mask & QPNP_VREG_DEBUG_DUPLICATE;
+	show_init = qpnp_vreg_debug_mask & QPNP_VREG_DEBUG_INIT;
+	has_changed = vreg->write_count != vreg->prev_write_count;
+	if (!((show_init && action == QPNP_REGULATOR_ACTION_INIT)
+	      || (show_req && (has_changed || show_dupe)))) {
+		return;
+	}
+
+	vreg->prev_write_count = vreg->write_count;
+
+	type = vreg->logical_type;
+
+	enable_label = qpnp_regulator_common_is_enabled(rdev) ? "on " : "off";
+
+	if (type == QPNP_REGULATOR_LOGICAL_TYPE_SMPS
+	    || type == QPNP_REGULATOR_LOGICAL_TYPE_LDO
+	    || type == QPNP_REGULATOR_LOGICAL_TYPE_FTSMPS)
+		uV = qpnp_regulator_common_get_voltage(rdev);
+
+	if (type == QPNP_REGULATOR_LOGICAL_TYPE_BOOST)
+		uV = qpnp_regulator_boost_get_voltage(rdev);
+
+	if (type == QPNP_REGULATOR_LOGICAL_TYPE_SMPS
+	    || type == QPNP_REGULATOR_LOGICAL_TYPE_LDO
+	    || type == QPNP_REGULATOR_LOGICAL_TYPE_FTSMPS) {
+		mode = qpnp_regulator_common_get_mode(rdev);
+		mode_label = mode == REGULATOR_MODE_NORMAL ? "HPM" : "LPM";
+	}
+
+	if (type == QPNP_REGULATOR_LOGICAL_TYPE_SMPS
+	    || type == QPNP_REGULATOR_LOGICAL_TYPE_LDO
+	    || type == QPNP_REGULATOR_LOGICAL_TYPE_VS) {
+		en_reg = vreg->ctrl_reg[QPNP_COMMON_IDX_ENABLE];
+		pc_enable_label[0] =
+		     en_reg & QPNP_COMMON_ENABLE_FOLLOW_HW_EN3_MASK ? '3' : '_';
+		pc_enable_label[1] =
+		     en_reg & QPNP_COMMON_ENABLE_FOLLOW_HW_EN2_MASK ? '2' : '_';
+		pc_enable_label[2] =
+		     en_reg & QPNP_COMMON_ENABLE_FOLLOW_HW_EN1_MASK ? '1' : '_';
+		pc_enable_label[3] =
+		     en_reg & QPNP_COMMON_ENABLE_FOLLOW_HW_EN0_MASK ? '0' : '_';
+	}
+
+	switch (type) {
+	case QPNP_REGULATOR_LOGICAL_TYPE_SMPS:
+		mode_reg = vreg->ctrl_reg[QPNP_COMMON_IDX_MODE];
+		pc_mode_label[0] =
+		     mode_reg & QPNP_COMMON_MODE_AUTO_MASK          ? 'A' : '_';
+		pc_mode_label[1] =
+		     mode_reg & QPNP_COMMON_MODE_FOLLOW_AWAKE_MASK  ? 'W' : '_';
+		pc_mode_label[2] =
+		     mode_reg & QPNP_COMMON_MODE_FOLLOW_HW_EN3_MASK ? '3' : '_';
+		pc_mode_label[3] =
+		     mode_reg & QPNP_COMMON_MODE_FOLLOW_HW_EN2_MASK ? '2' : '_';
+		pc_mode_label[4] =
+		     mode_reg & QPNP_COMMON_MODE_FOLLOW_HW_EN1_MASK ? '1' : '_';
+		pc_mode_label[5] =
+		     mode_reg & QPNP_COMMON_MODE_FOLLOW_HW_EN0_MASK ? '0' : '_';
+
+		pr_info("%s %-11s: %s, v=%7d uV, mode=%s, pc_en=%s, "
+			"alt_mode=%s\n",
+			action_label, vreg->rdesc.name, enable_label, uV,
+			mode_label, pc_enable_label, pc_mode_label);
+		break;
+	case QPNP_REGULATOR_LOGICAL_TYPE_LDO:
+		mode_reg = vreg->ctrl_reg[QPNP_COMMON_IDX_MODE];
+		pc_mode_label[0] =
+		     mode_reg & QPNP_COMMON_MODE_AUTO_MASK          ? 'A' : '_';
+		pc_mode_label[1] =
+		     mode_reg & QPNP_COMMON_MODE_BYPASS_MASK        ? 'B' : '_';
+		pc_mode_label[2] =
+		     mode_reg & QPNP_COMMON_MODE_FOLLOW_AWAKE_MASK  ? 'W' : '_';
+		pc_mode_label[3] =
+		     mode_reg & QPNP_COMMON_MODE_FOLLOW_HW_EN3_MASK ? '3' : '_';
+		pc_mode_label[4] =
+		     mode_reg & QPNP_COMMON_MODE_FOLLOW_HW_EN2_MASK ? '2' : '_';
+		pc_mode_label[5] =
+		     mode_reg & QPNP_COMMON_MODE_FOLLOW_HW_EN1_MASK ? '1' : '_';
+		pc_mode_label[6] =
+		     mode_reg & QPNP_COMMON_MODE_FOLLOW_HW_EN0_MASK ? '0' : '_';
+
+		pr_info("%s %-11s: %s, v=%7d uV, mode=%s, pc_en=%s, "
+			"alt_mode=%s\n",
+			action_label, vreg->rdesc.name, enable_label, uV,
+			mode_label, pc_enable_label, pc_mode_label);
+		break;
+	case QPNP_REGULATOR_LOGICAL_TYPE_VS:
+		mode_reg = vreg->ctrl_reg[QPNP_COMMON_IDX_MODE];
+		pc_mode_label[0] =
+		     mode_reg & QPNP_COMMON_MODE_AUTO_MASK          ? 'A' : '_';
+		pc_mode_label[1] =
+		     mode_reg & QPNP_COMMON_MODE_FOLLOW_AWAKE_MASK  ? 'W' : '_';
+
+		pr_info("%s %-11s: %s, pc_en=%s, alt_mode=%s\n",
+			action_label, vreg->rdesc.name, enable_label,
+			pc_enable_label, pc_mode_label);
+		break;
+	case QPNP_REGULATOR_LOGICAL_TYPE_BOOST:
+		pr_info("%s %-11s: %s, v=%7d uV\n",
+			action_label, vreg->rdesc.name, enable_label, uV);
+		break;
+	case QPNP_REGULATOR_LOGICAL_TYPE_FTSMPS:
+		mode_reg = vreg->ctrl_reg[QPNP_COMMON_IDX_MODE];
+		pc_mode_label[0] =
+		     mode_reg & QPNP_COMMON_MODE_AUTO_MASK          ? 'A' : '_';
+
+		pr_info("%s %-11s: %s, v=%7d uV, mode=%s, alt_mode=%s\n",
+			action_label, vreg->rdesc.name, enable_label, uV,
+			mode_label, pc_mode_label);
+		break;
+	default:
+		break;
+	}
+}
+
+static struct regulator_ops qpnp_smps_ops = {
+	.enable			= qpnp_regulator_common_enable,
+	.disable		= qpnp_regulator_common_disable,
+	.is_enabled		= qpnp_regulator_common_is_enabled,
+	.set_voltage		= qpnp_regulator_common_set_voltage,
+	.get_voltage		= qpnp_regulator_common_get_voltage,
+	.list_voltage		= qpnp_regulator_common_list_voltage,
+	.set_mode		= qpnp_regulator_common_set_mode,
+	.get_mode		= qpnp_regulator_common_get_mode,
+	.get_optimum_mode	= qpnp_regulator_common_get_optimum_mode,
+	.enable_time		= qpnp_regulator_common_enable_time,
+};
+
+static struct regulator_ops qpnp_ldo_ops = {
+	.enable			= qpnp_regulator_common_enable,
+	.disable		= qpnp_regulator_common_disable,
+	.is_enabled		= qpnp_regulator_common_is_enabled,
+	.set_voltage		= qpnp_regulator_common_set_voltage,
+	.get_voltage		= qpnp_regulator_common_get_voltage,
+	.list_voltage		= qpnp_regulator_common_list_voltage,
+	.set_mode		= qpnp_regulator_common_set_mode,
+	.get_mode		= qpnp_regulator_common_get_mode,
+	.get_optimum_mode	= qpnp_regulator_common_get_optimum_mode,
+	.enable_time		= qpnp_regulator_common_enable_time,
+};
+
+static struct regulator_ops qpnp_vs_ops = {
+	.enable			= qpnp_regulator_vs_enable,
+	.disable		= qpnp_regulator_common_disable,
+	.is_enabled		= qpnp_regulator_common_is_enabled,
+	.enable_time		= qpnp_regulator_common_enable_time,
+};
+
+static struct regulator_ops qpnp_boost_ops = {
+	.enable			= qpnp_regulator_common_enable,
+	.disable		= qpnp_regulator_common_disable,
+	.is_enabled		= qpnp_regulator_common_is_enabled,
+	.set_voltage		= qpnp_regulator_boost_set_voltage,
+	.get_voltage		= qpnp_regulator_boost_get_voltage,
+	.list_voltage		= qpnp_regulator_common_list_voltage,
+	.enable_time		= qpnp_regulator_common_enable_time,
+};
+
+static struct regulator_ops qpnp_ftsmps_ops = {
+	.enable			= qpnp_regulator_common_enable,
+	.disable		= qpnp_regulator_common_disable,
+	.is_enabled		= qpnp_regulator_common_is_enabled,
+	.set_voltage		= qpnp_regulator_common_set_voltage,
+	.get_voltage		= qpnp_regulator_common_get_voltage,
+	.list_voltage		= qpnp_regulator_common_list_voltage,
+	.set_mode		= qpnp_regulator_common_set_mode,
+	.get_mode		= qpnp_regulator_common_get_mode,
+	.get_optimum_mode	= qpnp_regulator_common_get_optimum_mode,
+	.enable_time		= qpnp_regulator_common_enable_time,
+};
+
+static const struct qpnp_regulator_mapping supported_regulators[] = {
+	QPNP_VREG_MAP(HF_BUCK,  GP_CTL,    SMPS,   smps,   smps,   100000),
+	QPNP_VREG_MAP(LDO,      N300,      LDO,    ldo,    nldo1,   10000),
+	QPNP_VREG_MAP(LDO,      N600,      LDO,    ldo,    nldo2,   10000),
+	QPNP_VREG_MAP(LDO,      N1200,     LDO,    ldo,    nldo2,   10000),
+	QPNP_VREG_MAP(LDO,      P50,       LDO,    ldo,    pldo,     5000),
+	QPNP_VREG_MAP(LDO,      P150,      LDO,    ldo,    pldo,    10000),
+	QPNP_VREG_MAP(LDO,      P300,      LDO,    ldo,    pldo,    10000),
+	QPNP_VREG_MAP(LDO,      P600,      LDO,    ldo,    pldo,    10000),
+	QPNP_VREG_MAP(LDO,      P1200,     LDO,    ldo,    pldo,    10000),
+	QPNP_VREG_MAP(VS,       LV100,     VS,     vs,     none,        0),
+	QPNP_VREG_MAP(VS,       LV300,     VS,     vs,     none,        0),
+	QPNP_VREG_MAP(VS,       MV300,     VS,     vs,     none,        0),
+	QPNP_VREG_MAP(VS,       MV500,     VS,     vs,     none,        0),
+	QPNP_VREG_MAP(VS,       HDMI,      VS,     vs,     none,        0),
+	QPNP_VREG_MAP(VS,       OTG,       VS,     vs,     none,        0),
+	QPNP_VREG_MAP(BOOST,    5V_BOOST,  BOOST,  boost,  boost,       0),
+	QPNP_VREG_MAP(FTS,      FTS_CTL,   FTSMPS, ftsmps, ftsmps, 100000),
+};
+
+static int qpnp_regulator_match(struct qpnp_regulator *vreg)
+{
+	const struct qpnp_regulator_mapping *mapping;
+	int rc, i;
+	u8 raw_type[2], type, subtype;
+
+	rc = qpnp_vreg_read(vreg, QPNP_COMMON_REG_TYPE, raw_type, 2);
+	if (rc) {
+		vreg_err(vreg, "could not read type register, rc=%d\n", rc);
+		return rc;
+	}
+	type = raw_type[0];
+	subtype = raw_type[1];
+
+	rc = -ENODEV;
+	for (i = 0; i < ARRAY_SIZE(supported_regulators); i++) {
+		mapping = &supported_regulators[i];
+		if (mapping->type == type && mapping->subtype == subtype) {
+			vreg->logical_type	= mapping->logical_type;
+			vreg->set_points	= mapping->set_points;
+			vreg->hpm_min_load	= mapping->hpm_min_load;
+			vreg->rdesc.ops		= mapping->ops;
+			vreg->rdesc.n_voltages
+				= mapping->set_points->n_voltages;
+			rc = 0;
+			break;
+		}
+	}
+
+	return rc;
+}
+
+static int qpnp_regulator_init_registers(struct qpnp_regulator *vreg,
+				struct qpnp_regulator_platform_data *pdata)
+{
+	int rc, i;
+	enum qpnp_regulator_logical_type type;
+	u8 ctrl_reg[8], reg, mask;
+
+	type = vreg->logical_type;
+
+	rc = qpnp_vreg_read(vreg, QPNP_COMMON_REG_VOLTAGE_RANGE,
+			    vreg->ctrl_reg, 8);
+	if (rc) {
+		vreg_err(vreg, "spmi read failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(ctrl_reg); i++)
+		ctrl_reg[i] = vreg->ctrl_reg[i];
+
+	/* Set up enable pin control. */
+	if ((type == QPNP_REGULATOR_LOGICAL_TYPE_SMPS
+	     || type == QPNP_REGULATOR_LOGICAL_TYPE_LDO
+	     || type == QPNP_REGULATOR_LOGICAL_TYPE_VS)
+	    && !(pdata->pin_ctrl_enable
+			& QPNP_REGULATOR_PIN_CTRL_ENABLE_HW_DEFAULT)) {
+		ctrl_reg[QPNP_COMMON_IDX_ENABLE] &=
+			~QPNP_COMMON_ENABLE_FOLLOW_ALL_MASK;
+		ctrl_reg[QPNP_COMMON_IDX_ENABLE] |=
+		    pdata->pin_ctrl_enable & QPNP_COMMON_ENABLE_FOLLOW_ALL_MASK;
+	}
+
+	/* Set up auto mode control. */
+	if ((type == QPNP_REGULATOR_LOGICAL_TYPE_SMPS
+	     || type == QPNP_REGULATOR_LOGICAL_TYPE_LDO
+	     || type == QPNP_REGULATOR_LOGICAL_TYPE_VS
+	     || type == QPNP_REGULATOR_LOGICAL_TYPE_FTSMPS)
+	    && (pdata->auto_mode_enable != QPNP_REGULATOR_USE_HW_DEFAULT)) {
+		ctrl_reg[QPNP_COMMON_IDX_MODE] &=
+			~QPNP_COMMON_MODE_AUTO_MASK;
+		ctrl_reg[QPNP_COMMON_IDX_MODE] |=
+		     (pdata->auto_mode_enable ? QPNP_COMMON_MODE_AUTO_MASK : 0);
+	}
+
+	/* Set up mode pin control. */
+	if ((type == QPNP_REGULATOR_LOGICAL_TYPE_SMPS
+	    || type == QPNP_REGULATOR_LOGICAL_TYPE_LDO)
+		&& !(pdata->pin_ctrl_hpm
+			& QPNP_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT)) {
+		ctrl_reg[QPNP_COMMON_IDX_MODE] &=
+			~QPNP_COMMON_MODE_FOLLOW_ALL_MASK;
+		ctrl_reg[QPNP_COMMON_IDX_MODE] |=
+			pdata->pin_ctrl_hpm & QPNP_COMMON_MODE_FOLLOW_ALL_MASK;
+	}
+
+	if (type == QPNP_REGULATOR_LOGICAL_TYPE_VS
+	   && !(pdata->pin_ctrl_hpm & QPNP_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT)) {
+		ctrl_reg[QPNP_COMMON_IDX_MODE] &=
+			~QPNP_COMMON_MODE_FOLLOW_AWAKE_MASK;
+		ctrl_reg[QPNP_COMMON_IDX_MODE] |=
+		       pdata->pin_ctrl_hpm & QPNP_COMMON_MODE_FOLLOW_AWAKE_MASK;
+	}
+
+	if (type == QPNP_REGULATOR_LOGICAL_TYPE_LDO
+	    && pdata->bypass_mode_enable != QPNP_REGULATOR_USE_HW_DEFAULT) {
+		ctrl_reg[QPNP_COMMON_IDX_MODE] &=
+			~QPNP_COMMON_MODE_BYPASS_MASK;
+		ctrl_reg[QPNP_COMMON_IDX_MODE] |=
+			(pdata->bypass_mode_enable
+				? QPNP_COMMON_MODE_BYPASS_MASK : 0);
+	}
+
+	/* Set boost current limit. */
+	if (type == QPNP_REGULATOR_LOGICAL_TYPE_BOOST
+		&& pdata->boost_current_limit
+			!= QPNP_BOOST_CURRENT_LIMIT_HW_DEFAULT) {
+		ctrl_reg[QPNP_BOOST_IDX_CURRENT_LIMIT] &=
+			~QPNP_BOOST_CURRENT_LIMIT_MASK;
+		ctrl_reg[QPNP_BOOST_IDX_CURRENT_LIMIT] |=
+		     pdata->boost_current_limit & QPNP_BOOST_CURRENT_LIMIT_MASK;
+	}
+
+	/* Write back any control register values that were modified. */
+	rc = qpnp_vreg_write_optimized(vreg, QPNP_COMMON_REG_VOLTAGE_RANGE,
+		ctrl_reg, vreg->ctrl_reg, 8);
+	if (rc) {
+		vreg_err(vreg, "spmi write failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	/* Set pull down. */
+	if ((type == QPNP_REGULATOR_LOGICAL_TYPE_SMPS
+	    || type == QPNP_REGULATOR_LOGICAL_TYPE_LDO
+	    || type == QPNP_REGULATOR_LOGICAL_TYPE_VS)
+	    && pdata->pull_down_enable != QPNP_REGULATOR_USE_HW_DEFAULT) {
+		reg = pdata->pull_down_enable
+			? QPNP_COMMON_PULL_DOWN_ENABLE_MASK : 0;
+		rc = qpnp_vreg_write(vreg, QPNP_COMMON_REG_PULL_DOWN, &reg, 1);
+		if (rc) {
+			vreg_err(vreg, "spmi write failed, rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	if (type == QPNP_REGULATOR_LOGICAL_TYPE_FTSMPS
+	    && pdata->pull_down_enable != QPNP_REGULATOR_USE_HW_DEFAULT) {
+		/* FTSMPS has other bits in the pull down control register. */
+		reg = pdata->pull_down_enable
+			? QPNP_COMMON_PULL_DOWN_ENABLE_MASK : 0;
+		rc = qpnp_vreg_masked_read_write(vreg,
+			QPNP_COMMON_REG_PULL_DOWN, reg,
+			QPNP_COMMON_PULL_DOWN_ENABLE_MASK);
+		if (rc) {
+			vreg_err(vreg, "spmi write failed, rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	/* Set soft start for LDO. */
+	if (type == QPNP_REGULATOR_LOGICAL_TYPE_LDO
+	    && pdata->soft_start_enable != QPNP_REGULATOR_USE_HW_DEFAULT) {
+		reg = pdata->soft_start_enable
+			? QPNP_LDO_SOFT_START_ENABLE_MASK : 0;
+		rc = qpnp_vreg_write(vreg, QPNP_LDO_REG_SOFT_START, &reg, 1);
+		if (rc) {
+			vreg_err(vreg, "spmi write failed, rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	/* Set soft start strength and over current protection for VS. */
+	if (type == QPNP_REGULATOR_LOGICAL_TYPE_VS) {
+		reg = 0;
+		mask = 0;
+		if (pdata->soft_start_enable != QPNP_REGULATOR_USE_HW_DEFAULT) {
+			reg |= pdata->soft_start_enable
+				? QPNP_VS_SOFT_START_ENABLE_MASK : 0;
+			mask |= QPNP_VS_SOFT_START_ENABLE_MASK;
+		}
+		if (pdata->vs_soft_start_strength
+				!= QPNP_VS_SOFT_START_STR_HW_DEFAULT) {
+			reg |= pdata->vs_soft_start_strength
+				& QPNP_VS_SOFT_START_SEL_MASK;
+			mask |= QPNP_VS_SOFT_START_SEL_MASK;
+		}
+		rc = qpnp_vreg_masked_read_write(vreg, QPNP_VS_REG_SOFT_START,
+						 reg, mask);
+		if (rc) {
+			vreg_err(vreg, "spmi write failed, rc=%d\n", rc);
+			return rc;
+		}
+
+		if (pdata->ocp_enable != QPNP_REGULATOR_USE_HW_DEFAULT) {
+			reg = pdata->ocp_enable ? QPNP_VS_OCP_ENABLE_MASK : 0;
+			rc = qpnp_vreg_write(vreg, QPNP_VS_REG_OCP, &reg, 1);
+			if (rc) {
+				vreg_err(vreg, "spmi write failed, rc=%d\n",
+					rc);
+				return rc;
+			}
+		}
+	}
+
+	return rc;
+}
+
+/* Fill in pdata elements based on values found in device tree. */
+static int qpnp_regulator_get_dt_config(struct spmi_device *spmi,
+				struct qpnp_regulator_platform_data *pdata)
+{
+	struct resource *res;
+	struct device_node *node = spmi->dev.of_node;
+	int rc = 0;
+
+	pdata->init_data.constraints.input_uV
+		= pdata->init_data.constraints.max_uV;
+
+	res = qpnp_get_resource(spmi, 0, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&spmi->dev, "%s: node is missing base address\n",
+			__func__);
+		return -EINVAL;
+	}
+	pdata->base_addr = res->start;
+
+	/*
+	 * Initialize configuration parameters to use hardware default in case
+	 * no value is specified via device tree.
+	 */
+	pdata->auto_mode_enable		= QPNP_REGULATOR_USE_HW_DEFAULT;
+	pdata->bypass_mode_enable	= QPNP_REGULATOR_USE_HW_DEFAULT;
+	pdata->ocp_enable		= QPNP_REGULATOR_USE_HW_DEFAULT;
+	pdata->pull_down_enable		= QPNP_REGULATOR_USE_HW_DEFAULT;
+	pdata->soft_start_enable	= QPNP_REGULATOR_USE_HW_DEFAULT;
+	pdata->boost_current_limit	= QPNP_BOOST_CURRENT_LIMIT_HW_DEFAULT;
+	pdata->pin_ctrl_enable	    = QPNP_REGULATOR_PIN_CTRL_ENABLE_HW_DEFAULT;
+	pdata->pin_ctrl_hpm	    = QPNP_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT;
+	pdata->vs_soft_start_strength	= QPNP_VS_SOFT_START_STR_HW_DEFAULT;
+
+	/* These bindings are optional, so it is okay if they are not found. */
+	of_property_read_u32(node, "qcom,auto-mode-enable",
+		&pdata->auto_mode_enable);
+	of_property_read_u32(node, "qcom,bypass-mode-enable",
+		&pdata->bypass_mode_enable);
+	of_property_read_u32(node, "qcom,ocp-enable", &pdata->ocp_enable);
+	of_property_read_u32(node, "qcom,pull-down-enable",
+		&pdata->pull_down_enable);
+	of_property_read_u32(node, "qcom,soft-start-enable",
+		&pdata->soft_start_enable);
+	of_property_read_u32(node, "qcom,boost-current-limit",
+		&pdata->boost_current_limit);
+	of_property_read_u32(node, "qcom,pin-ctrl-enable",
+		&pdata->pin_ctrl_enable);
+	of_property_read_u32(node, "qcom,pin-ctrl-hpm", &pdata->pin_ctrl_hpm);
+	of_property_read_u32(node, "qcom,vs-soft-start-strength",
+		&pdata->vs_soft_start_strength);
+	of_property_read_u32(node, "qcom,system-load", &pdata->system_load);
+	of_property_read_u32(node, "qcom,enable-time", &pdata->enable_time);
+	of_property_read_u32(node, "qcom,ocp-enable-time",
+		&pdata->ocp_enable_time);
+
+	return rc;
+}
+
+static struct of_device_id spmi_match_table[];
+
+#define MAX_NAME_LEN	127
+
+static int __devinit qpnp_regulator_probe(struct spmi_device *spmi)
+{
+	struct qpnp_regulator_platform_data *pdata;
+	struct qpnp_regulator *vreg;
+	struct regulator_desc *rdesc;
+	struct qpnp_regulator_platform_data of_pdata;
+	struct regulator_init_data *init_data;
+	char *reg_name;
+	int rc;
+	bool is_dt;
+
+	vreg = kzalloc(sizeof(struct qpnp_regulator), GFP_KERNEL);
+	if (!vreg) {
+		dev_err(&spmi->dev, "%s: Can't allocate qpnp_regulator\n",
+			__func__);
+		return -ENOMEM;
+	}
+
+	is_dt = of_match_device(spmi_match_table, &spmi->dev);
+
+	/* Check if device tree is in use. */
+	if (is_dt) {
+		init_data = of_get_regulator_init_data(&spmi->dev,
+						       spmi->dev.of_node);
+		if (!init_data) {
+			dev_err(&spmi->dev, "%s: unable to allocate memory\n",
+					__func__);
+			kfree(vreg);
+			return -ENOMEM;
+		}
+		memset(&of_pdata, 0,
+			sizeof(struct qpnp_regulator_platform_data));
+		memcpy(&of_pdata.init_data, init_data,
+			sizeof(struct regulator_init_data));
+
+		if (of_get_property(spmi->dev.of_node, "parent-supply", NULL))
+			of_pdata.init_data.supply_regulator = "parent";
+
+		rc = qpnp_regulator_get_dt_config(spmi, &of_pdata);
+		if (rc) {
+			dev_err(&spmi->dev, "%s: DT parsing failed, rc=%d\n",
+					__func__, rc);
+			kfree(vreg);
+			return -ENOMEM;
+		}
+
+		pdata = &of_pdata;
+	} else {
+		pdata = spmi->dev.platform_data;
+	}
+
+	if (pdata == NULL) {
+		dev_err(&spmi->dev, "%s: no platform data specified\n",
+			__func__);
+		kfree(vreg);
+		return -EINVAL;
+	}
+
+	vreg->spmi_dev		= spmi;
+	vreg->prev_write_count	= -1;
+	vreg->write_count	= 0;
+	vreg->base_addr		= pdata->base_addr;
+	vreg->enable_time	= pdata->enable_time;
+	vreg->system_load	= pdata->system_load;
+	vreg->ocp_enable	= pdata->ocp_enable;
+	vreg->ocp_enable_time	= pdata->ocp_enable_time;
+
+	rdesc			= &vreg->rdesc;
+	rdesc->id		= spmi->ctrl->nr;
+	rdesc->owner		= THIS_MODULE;
+	rdesc->type		= REGULATOR_VOLTAGE;
+
+	reg_name = kzalloc(strnlen(pdata->init_data.constraints.name,
+				MAX_NAME_LEN) + 1, GFP_KERNEL);
+	if (!reg_name) {
+		dev_err(&spmi->dev, "%s: Can't allocate regulator name\n",
+			__func__);
+		kfree(vreg);
+		return -ENOMEM;
+	}
+	strlcpy(reg_name, pdata->init_data.constraints.name,
+		strnlen(pdata->init_data.constraints.name, MAX_NAME_LEN) + 1);
+	rdesc->name = reg_name;
+
+	dev_set_drvdata(&spmi->dev, vreg);
+
+	rc = qpnp_regulator_match(vreg);
+	if (rc) {
+		vreg_err(vreg, "regulator type unknown, rc=%d\n", rc);
+		goto bail;
+	}
+
+	if (is_dt && rdesc->ops) {
+		/* Fill in ops and mode masks when using device tree. */
+		if (rdesc->ops->enable)
+			pdata->init_data.constraints.valid_ops_mask
+				|= REGULATOR_CHANGE_STATUS;
+		if (rdesc->ops->get_voltage)
+			pdata->init_data.constraints.valid_ops_mask
+				|= REGULATOR_CHANGE_VOLTAGE;
+		if (rdesc->ops->get_mode) {
+			pdata->init_data.constraints.valid_ops_mask
+				|= REGULATOR_CHANGE_MODE
+				| REGULATOR_CHANGE_DRMS;
+			pdata->init_data.constraints.valid_modes_mask
+				= REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE;
+		}
+	}
+
+	rc = qpnp_regulator_init_registers(vreg, pdata);
+	if (rc) {
+		vreg_err(vreg, "common initialization failed, rc=%d\n", rc);
+		goto bail;
+	}
+
+	vreg->rdev = regulator_register(rdesc, &spmi->dev,
+			&(pdata->init_data), vreg, spmi->dev.of_node);
+	if (IS_ERR(vreg->rdev)) {
+		rc = PTR_ERR(vreg->rdev);
+		vreg_err(vreg, "regulator_register failed, rc=%d\n", rc);
+		goto bail;
+	}
+
+	qpnp_vreg_show_state(vreg->rdev, QPNP_REGULATOR_ACTION_INIT);
+
+	return 0;
+
+bail:
+	if (rc)
+		vreg_err(vreg, "probe failed, rc=%d\n", rc);
+
+	kfree(vreg->rdesc.name);
+	kfree(vreg);
+
+	return rc;
+}
+
+static int __devexit qpnp_regulator_remove(struct spmi_device *spmi)
+{
+	struct qpnp_regulator *vreg;
+
+	vreg = dev_get_drvdata(&spmi->dev);
+	dev_set_drvdata(&spmi->dev, NULL);
+
+	if (vreg) {
+		regulator_unregister(vreg->rdev);
+		kfree(vreg->rdesc.name);
+		kfree(vreg);
+	}
+
+	return 0;
+}
+
+static struct of_device_id spmi_match_table[] = {
+	{ .compatible = QPNP_REGULATOR_DRIVER_NAME, },
+	{}
+};
+
+static const struct spmi_device_id qpnp_regulator_id[] = {
+	{ QPNP_REGULATOR_DRIVER_NAME, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(spmi, qpnp_regulator_id);
+
+static struct spmi_driver qpnp_regulator_driver = {
+	.driver		= {
+		.name	= QPNP_REGULATOR_DRIVER_NAME,
+		.of_match_table = spmi_match_table,
+		.owner = THIS_MODULE,
+	},
+	.probe		= qpnp_regulator_probe,
+	.remove		= __devexit_p(qpnp_regulator_remove),
+	.id_table	= qpnp_regulator_id,
+};
+
+/*
+ * Pre-compute the number of set points available for each regulator type to
+ * avoid unnecessary calculations later in runtime.
+ */
+static void qpnp_regulator_set_point_init(void)
+{
+	struct qpnp_voltage_set_points **set_points;
+	int i, j, temp;
+
+	set_points = all_set_points;
+
+	for (i = 0; i < ARRAY_SIZE(all_set_points); i++) {
+		temp = 0;
+		for (j = 0; j < all_set_points[i]->count; j++) {
+			all_set_points[i]->range[j].n_voltages
+				= (all_set_points[i]->range[j].max_uV
+				 - all_set_points[i]->range[j].set_point_min_uV)
+				   / all_set_points[i]->range[j].step_uV + 1;
+			temp += all_set_points[i]->range[j].n_voltages;
+		}
+		all_set_points[i]->n_voltages = temp;
+	}
+}
+
+/**
+ * qpnp_regulator_init() - register spmi driver for qpnp-regulator
+ *
+ * This initialization function should be called in systems in which driver
+ * registration ordering must be controlled precisely.
+ */
+int __init qpnp_regulator_init(void)
+{
+	static bool has_registered;
+
+	if (has_registered)
+		return 0;
+	else
+		has_registered = true;
+
+	qpnp_regulator_set_point_init();
+
+	return spmi_driver_register(&qpnp_regulator_driver);
+}
+EXPORT_SYMBOL(qpnp_regulator_init);
+
+static void __exit qpnp_regulator_exit(void)
+{
+	spmi_driver_unregister(&qpnp_regulator_driver);
+}
+
+MODULE_DESCRIPTION("QPNP PMIC regulator driver");
+MODULE_LICENSE("GPL v2");
+
+arch_initcall(qpnp_regulator_init);
+module_exit(qpnp_regulator_exit);
diff --git a/drivers/regulator/stub-regulator.c b/drivers/regulator/stub-regulator.c
new file mode 100644
index 0000000..1c4b935
--- /dev/null
+++ b/drivers/regulator/stub-regulator.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/stub-regulator.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#define STUB_REGULATOR_MAX_NAME 40
+
+struct regulator_stub {
+	struct regulator_desc	rdesc;
+	struct regulator_dev	*rdev;
+	int			voltage;
+	bool			enabled;
+	int			mode;
+	int			hpm_min_load;
+	int			system_uA;
+	char			name[STUB_REGULATOR_MAX_NAME];
+};
+
+static int regulator_stub_set_voltage(struct regulator_dev *rdev, int min_uV,
+				  int max_uV, unsigned *selector)
+{
+	struct regulator_stub *vreg_priv = rdev_get_drvdata(rdev);
+	vreg_priv->voltage = min_uV;
+	return 0;
+}
+
+static int regulator_stub_get_voltage(struct regulator_dev *rdev)
+{
+	struct regulator_stub *vreg_priv = rdev_get_drvdata(rdev);
+	return vreg_priv->voltage;
+}
+
+static int regulator_stub_list_voltage(struct regulator_dev *rdev,
+				    unsigned selector)
+{
+	struct regulation_constraints *constraints = rdev->constraints;
+
+	if (selector >= 2)
+		return -EINVAL;
+	else if (selector == 0)
+		return constraints->min_uV;
+	else
+		return constraints->max_uV;
+}
+
+static unsigned int regulator_stub_get_mode(struct regulator_dev *rdev)
+{
+	struct regulator_stub *vreg_priv = rdev_get_drvdata(rdev);
+	return vreg_priv->mode;
+}
+
+static int regulator_stub_set_mode(struct regulator_dev *rdev,
+				   unsigned int mode)
+{
+	struct regulator_stub *vreg_priv = rdev_get_drvdata(rdev);
+
+	if (mode != REGULATOR_MODE_NORMAL && mode != REGULATOR_MODE_IDLE) {
+		dev_err(&rdev->dev, "%s: invalid mode requested %u\n",
+							__func__, mode);
+		return -EINVAL;
+	}
+	vreg_priv->mode = mode;
+	return 0;
+}
+
+static unsigned int regulator_stub_get_optimum_mode(struct regulator_dev *rdev,
+		int input_uV, int output_uV, int load_uA)
+{
+	struct regulator_stub *vreg_priv = rdev_get_drvdata(rdev);
+	unsigned int mode;
+
+	if (load_uA + vreg_priv->system_uA >= vreg_priv->hpm_min_load)
+		mode = REGULATOR_MODE_NORMAL;
+	else
+		mode = REGULATOR_MODE_IDLE;
+
+	return mode;
+}
+
+static int regulator_stub_enable(struct regulator_dev *rdev)
+{
+	struct regulator_stub *vreg_priv = rdev_get_drvdata(rdev);
+	vreg_priv->enabled = true;
+	return 0;
+}
+
+static int regulator_stub_disable(struct regulator_dev *rdev)
+{
+	struct regulator_stub *vreg_priv = rdev_get_drvdata(rdev);
+	vreg_priv->enabled = false;
+	return 0;
+}
+
+static int regulator_stub_is_enabled(struct regulator_dev *rdev)
+{
+	struct regulator_stub *vreg_priv = rdev_get_drvdata(rdev);
+	return vreg_priv->enabled;
+}
+
+/* Real regulator operations. */
+static struct regulator_ops regulator_stub_ops = {
+	.enable			= regulator_stub_enable,
+	.disable		= regulator_stub_disable,
+	.is_enabled		= regulator_stub_is_enabled,
+	.set_voltage		= regulator_stub_set_voltage,
+	.get_voltage		= regulator_stub_get_voltage,
+	.list_voltage		= regulator_stub_list_voltage,
+	.set_mode		= regulator_stub_set_mode,
+	.get_mode		= regulator_stub_get_mode,
+	.get_optimum_mode	= regulator_stub_get_optimum_mode,
+};
+
+static void regulator_stub_cleanup(struct regulator_stub *vreg_priv)
+{
+	if (vreg_priv && vreg_priv->rdev)
+		regulator_unregister(vreg_priv->rdev);
+	kfree(vreg_priv);
+}
+
+static int __devinit regulator_stub_probe(struct platform_device *pdev)
+{
+	struct stub_regulator_pdata *vreg_pdata;
+	struct regulator_desc *rdesc;
+	struct regulator_stub *vreg_priv;
+	int rc;
+
+	vreg_pdata = pdev->dev.platform_data;
+	if (!vreg_pdata) {
+		dev_err(&pdev->dev, "%s: no platform data\n", __func__);
+		return -EINVAL;
+	}
+
+	vreg_priv = kzalloc(sizeof(*vreg_priv), GFP_KERNEL);
+	if (!vreg_priv) {
+		dev_err(&pdev->dev, "%s: Unable to allocate memory\n",
+				__func__);
+		return -ENOMEM;
+	}
+	dev_set_drvdata(&pdev->dev, vreg_priv);
+
+	rdesc = &vreg_priv->rdesc;
+	strncpy(vreg_priv->name, vreg_pdata->init_data.constraints.name,
+						   STUB_REGULATOR_MAX_NAME);
+	rdesc->name = vreg_priv->name;
+	rdesc->ops = &regulator_stub_ops;
+
+	/*
+	 * Ensure that voltage set points are handled correctly for regulators
+	 * which have a specified voltage constraint range, as well as those
+	 * that do not.
+	 */
+	if (vreg_pdata->init_data.constraints.min_uV == 0 &&
+	    vreg_pdata->init_data.constraints.max_uV == 0)
+		rdesc->n_voltages = 0;
+	else
+		rdesc->n_voltages = 2;
+
+	rdesc->id    = pdev->id;
+	rdesc->owner = THIS_MODULE;
+	rdesc->type  = REGULATOR_VOLTAGE;
+	vreg_priv->system_uA = vreg_pdata->system_uA;
+	vreg_priv->hpm_min_load = vreg_pdata->hpm_min_load;
+	vreg_priv->voltage = vreg_pdata->init_data.constraints.min_uV;
+
+	vreg_priv->rdev = regulator_register(rdesc, &pdev->dev,
+			&(vreg_pdata->init_data), vreg_priv, NULL);
+	if (IS_ERR(vreg_priv->rdev)) {
+		rc = PTR_ERR(vreg_priv->rdev);
+		vreg_priv->rdev = NULL;
+		dev_err(&pdev->dev, "%s: regulator_register failed\n",
+				__func__);
+		goto err_probe;
+	}
+
+	return 0;
+
+err_probe:
+	regulator_stub_cleanup(vreg_priv);
+	return rc;
+}
+
+static int __devexit regulator_stub_remove(struct platform_device *pdev)
+{
+	struct regulator_stub *vreg_priv = dev_get_drvdata(&pdev->dev);
+
+	regulator_stub_cleanup(vreg_priv);
+	return 0;
+}
+
+static struct platform_driver regulator_stub_driver = {
+	.probe	= regulator_stub_probe,
+	.remove	= __devexit_p(regulator_stub_remove),
+	.driver	= {
+		.name	= STUB_REGULATOR_DRIVER_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+int __init regulator_stub_init(void)
+{
+	static int registered;
+
+	if (registered)
+		return 0;
+	else
+		registered = 1;
+	return platform_driver_register(&regulator_stub_driver);
+}
+postcore_initcall(regulator_stub_init);
+EXPORT_SYMBOL(regulator_stub_init);
+
+static void __exit regulator_stub_exit(void)
+{
+	platform_driver_unregister(&regulator_stub_driver);
+}
+module_exit(regulator_stub_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("stub regulator driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform: " STUB_REGULATOR_DRIVER_NAME);
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 8c8377d..fc0d02a 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -94,6 +94,23 @@
 
 	  If unsure, say Y.
 
+config RTC_INTF_ALARM
+	bool "Android alarm driver"
+	depends on RTC_CLASS
+	default y
+	help
+	  Provides non-wakeup and rtc backed wakeup alarms based on rtc or
+	  elapsed realtime, and a non-wakeup alarm on the monotonic clock.
+	  Also provides an interface to set the wall time which must be used
+	  for elapsed realtime to work.
+
+config RTC_INTF_ALARM_DEV
+	bool "Android alarm device"
+	depends on RTC_INTF_ALARM
+	default y
+	help
+	  Exports the alarm interface to user-space.
+
 config RTC_INTF_DEV_UIE_EMUL
 	bool "RTC UIE emulation on dev interface"
 	depends on RTC_INTF_DEV
@@ -106,6 +123,24 @@
 	  clock several times per second, please enable this option
 	  only if you know that you really need it.
 
+config RTC_INTF_ALARM
+	bool "Android alarm driver"
+	depends on RTC_CLASS
+	default y
+	help
+	  Provides non-wakeup and rtc backed wakeup alarms based on rtc or
+	  elapsed realtime, and a non-wakeup alarm on the monotonic clock.
+	  Also provides an interface to set the wall time which must be used
+	  for elapsed realtime to work.
+
+config RTC_INTF_ALARM_DEV
+	bool "Android alarm device"
+	depends on RTC_INTF_ALARM
+	default y
+	help
+	  Exports the alarm interface to user-space.
+
+
 config RTC_DRV_TEST
 	tristate "Test driver/device"
 	help
@@ -728,6 +763,37 @@
 
 comment "on-CPU RTC drivers"
 
+config RTC_DRV_MSM
+	tristate "RTC on Qualcomm Chipsets"
+	depends on ARCH_MSM
+	default y
+	help
+	  RTC driver for Qualcomm chipsets
+
+
+config RTC_SECURE_TIME_SUPPORT
+        bool "Support for secure time on Qualcomm Chipsets"
+        depends on RTC_DRV_MSM = y
+        default y
+        help
+          Say yes here to have additional handle for reading secure time
+          maintained by ARM9.
+
+config RTC_ASYNC_MODEM_SUPPORT
+	bool "Support for time update on async modem boot"
+        depends on RTC_DRV_MSM && (ARCH_MSM8X60 || ARCH_QSD8X50)
+	default n
+	help
+	 Say yes here to have the system time updated if there is
+	 an asynchronous MODEM boot.
+
+config RTC_DRV_MSM7X00A
+	tristate "MSM7X00A"
+	depends on ARCH_MSM
+	default n
+	help
+	  RTC driver for Qualcomm MSM7K chipsets
+
 config RTC_DRV_DAVINCI
 	tristate "TI DaVinci RTC"
 	depends on ARCH_DAVINCI_DM365
@@ -738,6 +804,13 @@
 	  This driver can also be built as a module. If so, the module
 	  will be called rtc-davinci.
 
+config RTC_DRV_MSM7X00A
+	tristate "MSM7X00A"
+	depends on ARCH_MSM
+	default y
+	help
+	  RTC driver for Qualcomm MSM7K chipsets
+
 config RTC_DRV_OMAP
 	tristate "TI OMAP1"
 	depends on ARCH_OMAP15XX || ARCH_OMAP16XX || ARCH_OMAP730 || ARCH_DAVINCI_DA8XX
@@ -1077,6 +1150,15 @@
 	  This drive can also be built as a module. If so, the module
 	  will be called rtc-puv3.
 
+config RTC_DRV_PM8XXX
+	tristate "Qualcomm PMIC8XXX RTC"
+	depends on MFD_PM8XXX
+	help
+	  Say Y here if you want to support the Qualcomm PMIC8XXX RTC.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rtc-pm8xxx.
+
 config RTC_DRV_LOONGSON1
 	tristate "loongson1 RTC support"
 	depends on MACH_LOONGSON1
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 727ae77..8a3cecd 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -9,6 +9,8 @@
 obj-$(CONFIG_RTC_CLASS)		+= rtc-core.o
 rtc-core-y			:= class.o interface.o
 
+obj-$(CONFIG_RTC_INTF_ALARM) += alarm.o
+obj-$(CONFIG_RTC_INTF_ALARM_DEV) += alarm-dev.o
 rtc-core-$(CONFIG_RTC_INTF_DEV)	+= rtc-dev.o
 rtc-core-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o
 rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o
@@ -67,6 +69,8 @@
 obj-$(CONFIG_RTC_DRV_MAX8998)	+= rtc-max8998.o
 obj-$(CONFIG_RTC_DRV_MAX6902)	+= rtc-max6902.o
 obj-$(CONFIG_RTC_DRV_MC13XXX)	+= rtc-mc13xxx.o
+obj-$(CONFIG_RTC_DRV_MSM)	+= rtc-msm.o
+obj-$(CONFIG_RTC_DRV_MSM7X00A)	+= rtc-msm7x00a.o
 obj-$(CONFIG_RTC_DRV_MSM6242)	+= rtc-msm6242.o
 obj-$(CONFIG_RTC_DRV_MPC5121)	+= rtc-mpc5121.o
 obj-$(CONFIG_RTC_DRV_MV)	+= rtc-mv.o
@@ -77,6 +81,7 @@
 obj-$(CONFIG_RTC_DRV_PCF8583)	+= rtc-pcf8583.o
 obj-$(CONFIG_RTC_DRV_PCF2123)	+= rtc-pcf2123.o
 obj-$(CONFIG_RTC_DRV_PCF50633)	+= rtc-pcf50633.o
+obj-$(CONFIG_RTC_DRV_PM8XXX)    += rtc-pm8xxx.o
 obj-$(CONFIG_RTC_DRV_PL030)	+= rtc-pl030.o
 obj-$(CONFIG_RTC_DRV_PL031)	+= rtc-pl031.o
 obj-$(CONFIG_RTC_DRV_PM8XXX)	+= rtc-pm8xxx.o
diff --git a/drivers/rtc/alarm-dev.c b/drivers/rtc/alarm-dev.c
new file mode 100644
index 0000000..bfcaebc
--- /dev/null
+++ b/drivers/rtc/alarm-dev.c
@@ -0,0 +1,287 @@
+/* drivers/rtc/alarm-dev.c
+ *
+ * Copyright (C) 2007-2009 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/android_alarm.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/wakelock.h>
+
+#include <asm/mach/time.h>
+
+#define ANDROID_ALARM_PRINT_INFO (1U << 0)
+#define ANDROID_ALARM_PRINT_IO (1U << 1)
+#define ANDROID_ALARM_PRINT_INT (1U << 2)
+
+static int debug_mask = ANDROID_ALARM_PRINT_INFO;
+module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+#define pr_alarm(debug_level_mask, args...) \
+	do { \
+		if (debug_mask & ANDROID_ALARM_PRINT_##debug_level_mask) { \
+			pr_info(args); \
+		} \
+	} while (0)
+
+#define ANDROID_ALARM_WAKEUP_MASK ( \
+	ANDROID_ALARM_RTC_WAKEUP_MASK | \
+	ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK)
+
+/* support old usespace code */
+#define ANDROID_ALARM_SET_OLD               _IOW('a', 2, time_t) /* set alarm */
+#define ANDROID_ALARM_SET_AND_WAIT_OLD      _IOW('a', 3, time_t)
+
+static int alarm_opened;
+static DEFINE_SPINLOCK(alarm_slock);
+static struct wake_lock alarm_wake_lock;
+static DECLARE_WAIT_QUEUE_HEAD(alarm_wait_queue);
+static uint32_t alarm_pending;
+static uint32_t alarm_enabled;
+static uint32_t wait_pending;
+
+static struct alarm alarms[ANDROID_ALARM_TYPE_COUNT];
+
+static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	int rv = 0;
+	unsigned long flags;
+	struct timespec new_alarm_time;
+	struct timespec new_rtc_time;
+	struct timespec tmp_time;
+	enum android_alarm_type alarm_type = ANDROID_ALARM_IOCTL_TO_TYPE(cmd);
+	uint32_t alarm_type_mask = 1U << alarm_type;
+
+	if (alarm_type >= ANDROID_ALARM_TYPE_COUNT)
+		return -EINVAL;
+
+	if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_GET_TIME(0)) {
+		if ((file->f_flags & O_ACCMODE) == O_RDONLY)
+			return -EPERM;
+		if (file->private_data == NULL &&
+		    cmd != ANDROID_ALARM_SET_RTC) {
+			spin_lock_irqsave(&alarm_slock, flags);
+			if (alarm_opened) {
+				spin_unlock_irqrestore(&alarm_slock, flags);
+				return -EBUSY;
+			}
+			alarm_opened = 1;
+			file->private_data = (void *)1;
+			spin_unlock_irqrestore(&alarm_slock, flags);
+		}
+	}
+
+	switch (ANDROID_ALARM_BASE_CMD(cmd)) {
+	case ANDROID_ALARM_CLEAR(0):
+		spin_lock_irqsave(&alarm_slock, flags);
+		pr_alarm(IO, "alarm %d clear\n", alarm_type);
+		alarm_try_to_cancel(&alarms[alarm_type]);
+		if (alarm_pending) {
+			alarm_pending &= ~alarm_type_mask;
+			if (!alarm_pending && !wait_pending)
+				wake_unlock(&alarm_wake_lock);
+		}
+		alarm_enabled &= ~alarm_type_mask;
+		spin_unlock_irqrestore(&alarm_slock, flags);
+		break;
+
+	case ANDROID_ALARM_SET_OLD:
+	case ANDROID_ALARM_SET_AND_WAIT_OLD:
+		if (get_user(new_alarm_time.tv_sec, (int __user *)arg)) {
+			rv = -EFAULT;
+			goto err1;
+		}
+		new_alarm_time.tv_nsec = 0;
+		goto from_old_alarm_set;
+
+	case ANDROID_ALARM_SET_AND_WAIT(0):
+	case ANDROID_ALARM_SET(0):
+		if (copy_from_user(&new_alarm_time, (void __user *)arg,
+		    sizeof(new_alarm_time))) {
+			rv = -EFAULT;
+			goto err1;
+		}
+from_old_alarm_set:
+		spin_lock_irqsave(&alarm_slock, flags);
+		pr_alarm(IO, "alarm %d set %ld.%09ld\n", alarm_type,
+			new_alarm_time.tv_sec, new_alarm_time.tv_nsec);
+		alarm_enabled |= alarm_type_mask;
+		alarm_start_range(&alarms[alarm_type],
+			timespec_to_ktime(new_alarm_time),
+			timespec_to_ktime(new_alarm_time));
+		spin_unlock_irqrestore(&alarm_slock, flags);
+		if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_SET_AND_WAIT(0)
+		    && cmd != ANDROID_ALARM_SET_AND_WAIT_OLD)
+			break;
+		/* fall though */
+	case ANDROID_ALARM_WAIT:
+		spin_lock_irqsave(&alarm_slock, flags);
+		pr_alarm(IO, "alarm wait\n");
+		if (!alarm_pending && wait_pending) {
+			wake_unlock(&alarm_wake_lock);
+			wait_pending = 0;
+		}
+		spin_unlock_irqrestore(&alarm_slock, flags);
+		rv = wait_event_interruptible(alarm_wait_queue, alarm_pending);
+		if (rv)
+			goto err1;
+		spin_lock_irqsave(&alarm_slock, flags);
+		rv = alarm_pending;
+		wait_pending = 1;
+		alarm_pending = 0;
+		spin_unlock_irqrestore(&alarm_slock, flags);
+		break;
+	case ANDROID_ALARM_SET_RTC:
+		if (copy_from_user(&new_rtc_time, (void __user *)arg,
+		    sizeof(new_rtc_time))) {
+			rv = -EFAULT;
+			goto err1;
+		}
+		rv = alarm_set_rtc(new_rtc_time);
+		spin_lock_irqsave(&alarm_slock, flags);
+		alarm_pending |= ANDROID_ALARM_TIME_CHANGE_MASK;
+		wake_up(&alarm_wait_queue);
+		spin_unlock_irqrestore(&alarm_slock, flags);
+		if (rv < 0)
+			goto err1;
+		break;
+	case ANDROID_ALARM_GET_TIME(0):
+		switch (alarm_type) {
+		case ANDROID_ALARM_RTC_WAKEUP:
+		case ANDROID_ALARM_RTC:
+			getnstimeofday(&tmp_time);
+			break;
+		case ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP:
+		case ANDROID_ALARM_ELAPSED_REALTIME:
+			tmp_time =
+				ktime_to_timespec(alarm_get_elapsed_realtime());
+			break;
+		case ANDROID_ALARM_TYPE_COUNT:
+		case ANDROID_ALARM_SYSTEMTIME:
+			ktime_get_ts(&tmp_time);
+			break;
+		}
+		if (copy_to_user((void __user *)arg, &tmp_time,
+		    sizeof(tmp_time))) {
+			rv = -EFAULT;
+			goto err1;
+		}
+		break;
+
+	default:
+		rv = -EINVAL;
+		goto err1;
+	}
+err1:
+	return rv;
+}
+
+static int alarm_open(struct inode *inode, struct file *file)
+{
+	file->private_data = NULL;
+	return 0;
+}
+
+static int alarm_release(struct inode *inode, struct file *file)
+{
+	int i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&alarm_slock, flags);
+	if (file->private_data != 0) {
+		for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++) {
+			uint32_t alarm_type_mask = 1U << i;
+			if (alarm_enabled & alarm_type_mask) {
+				pr_alarm(INFO, "alarm_release: clear alarm, "
+					"pending %d\n",
+					!!(alarm_pending & alarm_type_mask));
+				alarm_enabled &= ~alarm_type_mask;
+			}
+			spin_unlock_irqrestore(&alarm_slock, flags);
+			alarm_cancel(&alarms[i]);
+			spin_lock_irqsave(&alarm_slock, flags);
+		}
+		if (alarm_pending | wait_pending) {
+			if (alarm_pending)
+				pr_alarm(INFO, "alarm_release: clear "
+					"pending alarms %x\n", alarm_pending);
+			wake_unlock(&alarm_wake_lock);
+			wait_pending = 0;
+			alarm_pending = 0;
+		}
+		alarm_opened = 0;
+	}
+	spin_unlock_irqrestore(&alarm_slock, flags);
+	return 0;
+}
+
+static void alarm_triggered(struct alarm *alarm)
+{
+	unsigned long flags;
+	uint32_t alarm_type_mask = 1U << alarm->type;
+
+	pr_alarm(INT, "alarm_triggered type %d\n", alarm->type);
+	spin_lock_irqsave(&alarm_slock, flags);
+	if (alarm_enabled & alarm_type_mask) {
+		wake_lock_timeout(&alarm_wake_lock, 5 * HZ);
+		alarm_enabled &= ~alarm_type_mask;
+		alarm_pending |= alarm_type_mask;
+		wake_up(&alarm_wait_queue);
+	}
+	spin_unlock_irqrestore(&alarm_slock, flags);
+}
+
+static const struct file_operations alarm_fops = {
+	.owner = THIS_MODULE,
+	.unlocked_ioctl = alarm_ioctl,
+	.open = alarm_open,
+	.release = alarm_release,
+};
+
+static struct miscdevice alarm_device = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "alarm",
+	.fops = &alarm_fops,
+};
+
+static int __init alarm_dev_init(void)
+{
+	int err;
+	int i;
+
+	err = misc_register(&alarm_device);
+	if (err)
+		return err;
+
+	for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++)
+		alarm_init(&alarms[i], i, alarm_triggered);
+	wake_lock_init(&alarm_wake_lock, WAKE_LOCK_SUSPEND, "alarm");
+
+	return 0;
+}
+
+static void  __exit alarm_dev_exit(void)
+{
+	misc_deregister(&alarm_device);
+	wake_lock_destroy(&alarm_wake_lock);
+}
+
+module_init(alarm_dev_init);
+module_exit(alarm_dev_exit);
+
diff --git a/drivers/rtc/alarm.c b/drivers/rtc/alarm.c
new file mode 100644
index 0000000..9340af7
--- /dev/null
+++ b/drivers/rtc/alarm.c
@@ -0,0 +1,615 @@
+/* drivers/rtc/alarm.c
+ *
+ * Copyright (C) 2007-2009 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/android_alarm.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/wakelock.h>
+
+#include <asm/mach/time.h>
+
+#define ANDROID_ALARM_PRINT_ERROR (1U << 0)
+#define ANDROID_ALARM_PRINT_INIT_STATUS (1U << 1)
+#define ANDROID_ALARM_PRINT_TSET (1U << 2)
+#define ANDROID_ALARM_PRINT_CALL (1U << 3)
+#define ANDROID_ALARM_PRINT_SUSPEND (1U << 4)
+#define ANDROID_ALARM_PRINT_INT (1U << 5)
+#define ANDROID_ALARM_PRINT_FLOW (1U << 6)
+
+static int debug_mask = ANDROID_ALARM_PRINT_ERROR | \
+			ANDROID_ALARM_PRINT_INIT_STATUS;
+module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+#define pr_alarm(debug_level_mask, args...) \
+	do { \
+		if (debug_mask & ANDROID_ALARM_PRINT_##debug_level_mask) { \
+			pr_info(args); \
+		} \
+	} while (0)
+
+#define ANDROID_ALARM_WAKEUP_MASK ( \
+	ANDROID_ALARM_RTC_WAKEUP_MASK | \
+	ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK)
+
+/* support old usespace code */
+#define ANDROID_ALARM_SET_OLD               _IOW('a', 2, time_t) /* set alarm */
+#define ANDROID_ALARM_SET_AND_WAIT_OLD      _IOW('a', 3, time_t)
+
+struct alarm_queue {
+	struct rb_root alarms;
+	struct rb_node *first;
+	struct hrtimer timer;
+	ktime_t delta;
+	bool stopped;
+	ktime_t stopped_time;
+};
+
+static struct rtc_device *alarm_rtc_dev;
+static DEFINE_SPINLOCK(alarm_slock);
+static DEFINE_MUTEX(alarm_setrtc_mutex);
+static struct wake_lock alarm_rtc_wake_lock;
+static struct platform_device *alarm_platform_dev;
+struct alarm_queue alarms[ANDROID_ALARM_TYPE_COUNT];
+static bool suspended;
+
+static void update_timer_locked(struct alarm_queue *base, bool head_removed)
+{
+	struct alarm *alarm;
+	bool is_wakeup = base == &alarms[ANDROID_ALARM_RTC_WAKEUP] ||
+			base == &alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP];
+
+	if (base->stopped) {
+		pr_alarm(FLOW, "changed alarm while setting the wall time\n");
+		return;
+	}
+
+	if (is_wakeup && !suspended && head_removed)
+		wake_unlock(&alarm_rtc_wake_lock);
+
+	if (!base->first)
+		return;
+
+	alarm = container_of(base->first, struct alarm, node);
+
+	pr_alarm(FLOW, "selected alarm, type %d, func %pF at %lld\n",
+		alarm->type, alarm->function, ktime_to_ns(alarm->expires));
+
+	if (is_wakeup && suspended) {
+		pr_alarm(FLOW, "changed alarm while suspened\n");
+		wake_lock_timeout(&alarm_rtc_wake_lock, 1 * HZ);
+		return;
+	}
+
+	hrtimer_try_to_cancel(&base->timer);
+	base->timer.node.expires = ktime_add(base->delta, alarm->expires);
+	base->timer._softexpires = ktime_add(base->delta, alarm->softexpires);
+	hrtimer_start_expires(&base->timer, HRTIMER_MODE_ABS);
+}
+
+static void alarm_enqueue_locked(struct alarm *alarm)
+{
+	struct alarm_queue *base = &alarms[alarm->type];
+	struct rb_node **link = &base->alarms.rb_node;
+	struct rb_node *parent = NULL;
+	struct alarm *entry;
+	int leftmost = 1;
+	bool was_first = false;
+
+	pr_alarm(FLOW, "added alarm, type %d, func %pF at %lld\n",
+		alarm->type, alarm->function, ktime_to_ns(alarm->expires));
+
+	if (base->first == &alarm->node) {
+		base->first = rb_next(&alarm->node);
+		was_first = true;
+	}
+	if (!RB_EMPTY_NODE(&alarm->node)) {
+		rb_erase(&alarm->node, &base->alarms);
+		RB_CLEAR_NODE(&alarm->node);
+	}
+
+	while (*link) {
+		parent = *link;
+		entry = rb_entry(parent, struct alarm, node);
+		/*
+		* We dont care about collisions. Nodes with
+		* the same expiry time stay together.
+		*/
+		if (alarm->expires.tv64 < entry->expires.tv64) {
+			link = &(*link)->rb_left;
+		} else {
+			link = &(*link)->rb_right;
+			leftmost = 0;
+		}
+	}
+	if (leftmost)
+		base->first = &alarm->node;
+	if (leftmost || was_first)
+		update_timer_locked(base, was_first);
+
+	rb_link_node(&alarm->node, parent, link);
+	rb_insert_color(&alarm->node, &base->alarms);
+}
+
+/**
+ * alarm_init - initialize an alarm
+ * @alarm:	the alarm to be initialized
+ * @type:	the alarm type to be used
+ * @function:	alarm callback function
+ */
+void alarm_init(struct alarm *alarm,
+	enum android_alarm_type type, void (*function)(struct alarm *))
+{
+	RB_CLEAR_NODE(&alarm->node);
+	alarm->type = type;
+	alarm->function = function;
+
+	pr_alarm(FLOW, "created alarm, type %d, func %pF\n", type, function);
+}
+
+
+/**
+ * alarm_start_range - (re)start an alarm
+ * @alarm:	the alarm to be added
+ * @start:	earliest expiry time
+ * @end:	expiry time
+ */
+void alarm_start_range(struct alarm *alarm, ktime_t start, ktime_t end)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&alarm_slock, flags);
+	alarm->softexpires = start;
+	alarm->expires = end;
+	alarm_enqueue_locked(alarm);
+	spin_unlock_irqrestore(&alarm_slock, flags);
+}
+
+/**
+ * alarm_try_to_cancel - try to deactivate an alarm
+ * @alarm:	alarm to stop
+ *
+ * Returns:
+ *  0 when the alarm was not active
+ *  1 when the alarm was active
+ * -1 when the alarm may currently be excuting the callback function and
+ *    cannot be stopped (it may also be inactive)
+ */
+int alarm_try_to_cancel(struct alarm *alarm)
+{
+	struct alarm_queue *base = &alarms[alarm->type];
+	unsigned long flags;
+	bool first = false;
+	int ret = 0;
+
+	spin_lock_irqsave(&alarm_slock, flags);
+	if (!RB_EMPTY_NODE(&alarm->node)) {
+		pr_alarm(FLOW, "canceled alarm, type %d, func %pF at %lld\n",
+			alarm->type, alarm->function,
+			ktime_to_ns(alarm->expires));
+		ret = 1;
+		if (base->first == &alarm->node) {
+			base->first = rb_next(&alarm->node);
+			first = true;
+		}
+		rb_erase(&alarm->node, &base->alarms);
+		RB_CLEAR_NODE(&alarm->node);
+		if (first)
+			update_timer_locked(base, true);
+	} else
+		pr_alarm(FLOW, "tried to cancel alarm, type %d, func %pF\n",
+			alarm->type, alarm->function);
+	spin_unlock_irqrestore(&alarm_slock, flags);
+	if (!ret && hrtimer_callback_running(&base->timer))
+		ret = -1;
+	return ret;
+}
+
+/**
+ * alarm_cancel - cancel an alarm and wait for the handler to finish.
+ * @alarm:	the alarm to be cancelled
+ *
+ * Returns:
+ *  0 when the alarm was not active
+ *  1 when the alarm was active
+ */
+int alarm_cancel(struct alarm *alarm)
+{
+	for (;;) {
+		int ret = alarm_try_to_cancel(alarm);
+		if (ret >= 0)
+			return ret;
+		cpu_relax();
+	}
+}
+
+/**
+ * alarm_set_rtc - set the kernel and rtc walltime
+ * @new_time:	timespec value containing the new time
+ */
+int alarm_set_rtc(struct timespec new_time)
+{
+	int i;
+	int ret;
+	unsigned long flags;
+	struct rtc_time rtc_new_rtc_time;
+	struct timespec tmp_time;
+
+	rtc_time_to_tm(new_time.tv_sec, &rtc_new_rtc_time);
+
+	pr_alarm(TSET, "set rtc %ld %ld - rtc %02d:%02d:%02d %02d/%02d/%04d\n",
+		new_time.tv_sec, new_time.tv_nsec,
+		rtc_new_rtc_time.tm_hour, rtc_new_rtc_time.tm_min,
+		rtc_new_rtc_time.tm_sec, rtc_new_rtc_time.tm_mon + 1,
+		rtc_new_rtc_time.tm_mday,
+		rtc_new_rtc_time.tm_year + 1900);
+
+	mutex_lock(&alarm_setrtc_mutex);
+	spin_lock_irqsave(&alarm_slock, flags);
+	wake_lock(&alarm_rtc_wake_lock);
+	getnstimeofday(&tmp_time);
+	for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) {
+		hrtimer_try_to_cancel(&alarms[i].timer);
+		alarms[i].stopped = true;
+		alarms[i].stopped_time = timespec_to_ktime(tmp_time);
+	}
+	alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP].delta =
+		alarms[ANDROID_ALARM_ELAPSED_REALTIME].delta =
+		ktime_sub(alarms[ANDROID_ALARM_ELAPSED_REALTIME].delta,
+			timespec_to_ktime(timespec_sub(tmp_time, new_time)));
+	spin_unlock_irqrestore(&alarm_slock, flags);
+	ret = do_settimeofday(&new_time);
+	spin_lock_irqsave(&alarm_slock, flags);
+	for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) {
+		alarms[i].stopped = false;
+		update_timer_locked(&alarms[i], false);
+	}
+	spin_unlock_irqrestore(&alarm_slock, flags);
+	if (ret < 0) {
+		pr_alarm(ERROR, "alarm_set_rtc: Failed to set time\n");
+		goto err;
+	}
+	if (!alarm_rtc_dev) {
+		pr_alarm(ERROR,
+			"alarm_set_rtc: no RTC, time will be lost on reboot\n");
+		goto err;
+	}
+	ret = rtc_set_time(alarm_rtc_dev, &rtc_new_rtc_time);
+	if (ret < 0)
+		pr_alarm(ERROR, "alarm_set_rtc: "
+			"Failed to set RTC, time will be lost on reboot\n");
+err:
+	wake_unlock(&alarm_rtc_wake_lock);
+	mutex_unlock(&alarm_setrtc_mutex);
+	return ret;
+}
+
+
+void
+alarm_update_timedelta(struct timespec tmp_time, struct timespec new_time)
+{
+	int i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&alarm_slock, flags);
+	for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) {
+		hrtimer_try_to_cancel(&alarms[i].timer);
+		alarms[i].stopped = true;
+		alarms[i].stopped_time = timespec_to_ktime(tmp_time);
+	}
+	alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP].delta =
+		alarms[ANDROID_ALARM_ELAPSED_REALTIME].delta =
+		ktime_sub(alarms[ANDROID_ALARM_ELAPSED_REALTIME].delta,
+			timespec_to_ktime(timespec_sub(tmp_time, new_time)));
+	for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) {
+		alarms[i].stopped = false;
+		update_timer_locked(&alarms[i], false);
+	}
+	spin_unlock_irqrestore(&alarm_slock, flags);
+}
+
+/**
+ * alarm_get_elapsed_realtime - get the elapsed real time in ktime_t format
+ *
+ * returns the time in ktime_t format
+ */
+ktime_t alarm_get_elapsed_realtime(void)
+{
+	ktime_t now;
+	unsigned long flags;
+	struct alarm_queue *base = &alarms[ANDROID_ALARM_ELAPSED_REALTIME];
+
+	spin_lock_irqsave(&alarm_slock, flags);
+	now = base->stopped ? base->stopped_time : ktime_get_real();
+	now = ktime_sub(now, base->delta);
+	spin_unlock_irqrestore(&alarm_slock, flags);
+	return now;
+}
+
+static enum hrtimer_restart alarm_timer_triggered(struct hrtimer *timer)
+{
+	struct alarm_queue *base;
+	struct alarm *alarm;
+	unsigned long flags;
+	ktime_t now;
+
+	spin_lock_irqsave(&alarm_slock, flags);
+
+	base = container_of(timer, struct alarm_queue, timer);
+	now = base->stopped ? base->stopped_time : hrtimer_cb_get_time(timer);
+	now = ktime_sub(now, base->delta);
+
+	pr_alarm(INT, "alarm_timer_triggered type %d at %lld\n",
+		base - alarms, ktime_to_ns(now));
+
+	while (base->first) {
+		alarm = container_of(base->first, struct alarm, node);
+		if (alarm->softexpires.tv64 > now.tv64) {
+			pr_alarm(FLOW, "don't call alarm, %pF, %lld (s %lld)\n",
+				alarm->function, ktime_to_ns(alarm->expires),
+				ktime_to_ns(alarm->softexpires));
+			break;
+		}
+		base->first = rb_next(&alarm->node);
+		rb_erase(&alarm->node, &base->alarms);
+		RB_CLEAR_NODE(&alarm->node);
+		pr_alarm(CALL, "call alarm, type %d, func %pF, %lld (s %lld)\n",
+			alarm->type, alarm->function,
+			ktime_to_ns(alarm->expires),
+			ktime_to_ns(alarm->softexpires));
+		spin_unlock_irqrestore(&alarm_slock, flags);
+		alarm->function(alarm);
+		spin_lock_irqsave(&alarm_slock, flags);
+	}
+	if (!base->first)
+		pr_alarm(FLOW, "no more alarms of type %d\n", base - alarms);
+	update_timer_locked(base, true);
+	spin_unlock_irqrestore(&alarm_slock, flags);
+	return HRTIMER_NORESTART;
+}
+
+static void alarm_triggered_func(void *p)
+{
+	struct rtc_device *rtc = alarm_rtc_dev;
+	if (!(rtc->irq_data & RTC_AF))
+		return;
+	pr_alarm(INT, "rtc alarm triggered\n");
+	wake_lock_timeout(&alarm_rtc_wake_lock, 1 * HZ);
+}
+
+static int alarm_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	int                 err = 0;
+	unsigned long       flags;
+	struct rtc_wkalrm   rtc_alarm;
+	struct rtc_time     rtc_current_rtc_time;
+	unsigned long       rtc_current_time;
+	unsigned long       rtc_alarm_time;
+	struct timespec     rtc_delta;
+	struct timespec     wall_time;
+	struct alarm_queue *wakeup_queue = NULL;
+	struct alarm_queue *tmp_queue = NULL;
+
+	pr_alarm(SUSPEND, "alarm_suspend(%p, %d)\n", pdev, state.event);
+
+	spin_lock_irqsave(&alarm_slock, flags);
+	suspended = true;
+	spin_unlock_irqrestore(&alarm_slock, flags);
+
+	hrtimer_cancel(&alarms[ANDROID_ALARM_RTC_WAKEUP].timer);
+	hrtimer_cancel(&alarms[
+			ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP].timer);
+
+	tmp_queue = &alarms[ANDROID_ALARM_RTC_WAKEUP];
+	if (tmp_queue->first)
+		wakeup_queue = tmp_queue;
+	tmp_queue = &alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP];
+	if (tmp_queue->first && (!wakeup_queue ||
+				hrtimer_get_expires(&tmp_queue->timer).tv64 <
+				hrtimer_get_expires(&wakeup_queue->timer).tv64))
+		wakeup_queue = tmp_queue;
+	if (wakeup_queue) {
+		rtc_read_time(alarm_rtc_dev, &rtc_current_rtc_time);
+		getnstimeofday(&wall_time);
+		rtc_tm_to_time(&rtc_current_rtc_time, &rtc_current_time);
+		set_normalized_timespec(&rtc_delta,
+					wall_time.tv_sec - rtc_current_time,
+					wall_time.tv_nsec);
+
+		rtc_alarm_time = timespec_sub(ktime_to_timespec(
+			hrtimer_get_expires(&wakeup_queue->timer)),
+			rtc_delta).tv_sec;
+
+		rtc_time_to_tm(rtc_alarm_time, &rtc_alarm.time);
+		rtc_alarm.enabled = 1;
+		rtc_set_alarm(alarm_rtc_dev, &rtc_alarm);
+		rtc_read_time(alarm_rtc_dev, &rtc_current_rtc_time);
+		rtc_tm_to_time(&rtc_current_rtc_time, &rtc_current_time);
+		pr_alarm(SUSPEND,
+			"rtc alarm set at %ld, now %ld, rtc delta %ld.%09ld\n",
+			rtc_alarm_time, rtc_current_time,
+			rtc_delta.tv_sec, rtc_delta.tv_nsec);
+		if (rtc_current_time + 1 >= rtc_alarm_time) {
+			pr_alarm(SUSPEND, "alarm about to go off\n");
+			memset(&rtc_alarm, 0, sizeof(rtc_alarm));
+			rtc_alarm.enabled = 0;
+			rtc_set_alarm(alarm_rtc_dev, &rtc_alarm);
+
+			spin_lock_irqsave(&alarm_slock, flags);
+			suspended = false;
+			wake_lock_timeout(&alarm_rtc_wake_lock, 2 * HZ);
+			update_timer_locked(&alarms[ANDROID_ALARM_RTC_WAKEUP],
+									false);
+			update_timer_locked(&alarms[
+				ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP], false);
+			err = -EBUSY;
+			spin_unlock_irqrestore(&alarm_slock, flags);
+		}
+	}
+	return err;
+}
+
+static int alarm_resume(struct platform_device *pdev)
+{
+	struct rtc_wkalrm alarm;
+	unsigned long       flags;
+
+	pr_alarm(SUSPEND, "alarm_resume(%p)\n", pdev);
+
+	memset(&alarm, 0, sizeof(alarm));
+	alarm.enabled = 0;
+	rtc_set_alarm(alarm_rtc_dev, &alarm);
+
+	spin_lock_irqsave(&alarm_slock, flags);
+	suspended = false;
+	update_timer_locked(&alarms[ANDROID_ALARM_RTC_WAKEUP], false);
+	update_timer_locked(&alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP],
+									false);
+	spin_unlock_irqrestore(&alarm_slock, flags);
+
+	return 0;
+}
+
+static struct rtc_task alarm_rtc_task = {
+	.func = alarm_triggered_func
+};
+
+static int rtc_alarm_add_device(struct device *dev,
+				struct class_interface *class_intf)
+{
+	int err;
+	struct rtc_device *rtc = to_rtc_device(dev);
+
+	mutex_lock(&alarm_setrtc_mutex);
+
+	if (alarm_rtc_dev) {
+		err = -EBUSY;
+		goto err1;
+	}
+
+	alarm_platform_dev =
+		platform_device_register_simple("alarm", -1, NULL, 0);
+	if (IS_ERR(alarm_platform_dev)) {
+		err = PTR_ERR(alarm_platform_dev);
+		goto err2;
+	}
+	err = rtc_irq_register(rtc, &alarm_rtc_task);
+	if (err)
+		goto err3;
+	alarm_rtc_dev = rtc;
+	pr_alarm(INIT_STATUS, "using rtc device, %s, for alarms", rtc->name);
+	mutex_unlock(&alarm_setrtc_mutex);
+
+	return 0;
+
+err3:
+	platform_device_unregister(alarm_platform_dev);
+err2:
+err1:
+	mutex_unlock(&alarm_setrtc_mutex);
+	return err;
+}
+
+static void rtc_alarm_remove_device(struct device *dev,
+				    struct class_interface *class_intf)
+{
+	if (dev == &alarm_rtc_dev->dev) {
+		pr_alarm(INIT_STATUS, "lost rtc device for alarms");
+		rtc_irq_unregister(alarm_rtc_dev, &alarm_rtc_task);
+		platform_device_unregister(alarm_platform_dev);
+		alarm_rtc_dev = NULL;
+	}
+}
+
+static struct class_interface rtc_alarm_interface = {
+	.add_dev = &rtc_alarm_add_device,
+	.remove_dev = &rtc_alarm_remove_device,
+};
+
+static struct platform_driver alarm_driver = {
+	.suspend = alarm_suspend,
+	.resume = alarm_resume,
+	.driver = {
+		.name = "alarm"
+	}
+};
+
+static int __init alarm_late_init(void)
+{
+	unsigned long   flags;
+	struct timespec tmp_time, system_time;
+
+	/* this needs to run after the rtc is read at boot */
+	spin_lock_irqsave(&alarm_slock, flags);
+	/* We read the current rtc and system time so we can later calulate
+	 * elasped realtime to be (boot_systemtime + rtc - boot_rtc) ==
+	 * (rtc - (boot_rtc - boot_systemtime))
+	 */
+	getnstimeofday(&tmp_time);
+	ktime_get_ts(&system_time);
+	alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP].delta =
+		alarms[ANDROID_ALARM_ELAPSED_REALTIME].delta =
+			timespec_to_ktime(timespec_sub(tmp_time, system_time));
+
+	spin_unlock_irqrestore(&alarm_slock, flags);
+	return 0;
+}
+
+static int __init alarm_driver_init(void)
+{
+	int err;
+	int i;
+
+	for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) {
+		hrtimer_init(&alarms[i].timer,
+				CLOCK_REALTIME, HRTIMER_MODE_ABS);
+		alarms[i].timer.function = alarm_timer_triggered;
+	}
+	hrtimer_init(&alarms[ANDROID_ALARM_SYSTEMTIME].timer,
+		     CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
+	alarms[ANDROID_ALARM_SYSTEMTIME].timer.function = alarm_timer_triggered;
+	err = platform_driver_register(&alarm_driver);
+	if (err < 0)
+		goto err1;
+	wake_lock_init(&alarm_rtc_wake_lock, WAKE_LOCK_SUSPEND, "alarm_rtc");
+	rtc_alarm_interface.class = rtc_class;
+	err = class_interface_register(&rtc_alarm_interface);
+	if (err < 0)
+		goto err2;
+
+	return 0;
+
+err2:
+	wake_lock_destroy(&alarm_rtc_wake_lock);
+	platform_driver_unregister(&alarm_driver);
+err1:
+	return err;
+}
+
+static void  __exit alarm_exit(void)
+{
+	class_interface_unregister(&rtc_alarm_interface);
+	wake_lock_destroy(&alarm_rtc_wake_lock);
+	platform_driver_unregister(&alarm_driver);
+}
+
+late_initcall(alarm_late_init);
+module_init(alarm_driver_init);
+module_exit(alarm_exit);
+
diff --git a/drivers/rtc/hctosys.c b/drivers/rtc/hctosys.c
index bc90b09..29735c2 100644
--- a/drivers/rtc/hctosys.c
+++ b/drivers/rtc/hctosys.c
@@ -24,7 +24,7 @@
 
 int rtc_hctosys_ret = -ENODEV;
 
-static int __init rtc_hctosys(void)
+int rtc_hctosys(void)
 {
 	int err = -ENODEV;
 	struct rtc_time tm;
diff --git a/drivers/rtc/rtc-msm.c b/drivers/rtc/rtc-msm.c
new file mode 100644
index 0000000..c17e461
--- /dev/null
+++ b/drivers/rtc/rtc-msm.c
@@ -0,0 +1,819 @@
+/*
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (c) 2009-2011 Code Aurora Forum. All rights reserved.
+ * Author: San Mehat <san@google.com>
+ *
+ * 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/version.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/android_alarm.h>
+
+#include <linux/rtc.h>
+#include <linux/rtc-msm.h>
+#include <linux/msm_rpcrouter.h>
+#include <mach/msm_rpcrouter.h>
+
+#define APP_TIMEREMOTE_PDEV_NAME "rs00000000"
+
+#define TIMEREMOTE_PROCEEDURE_SET_JULIAN	6
+#define TIMEREMOTE_PROCEEDURE_GET_JULIAN	7
+#ifdef CONFIG_RTC_SECURE_TIME_SUPPORT
+#define TIMEREMOTE_PROCEEDURE_GET_SECURE_JULIAN	11
+#define TIMEREMOTE_PROCEEDURE_SET_SECURE_JULIAN	16
+#endif
+#define TIMEREMOTE_PROG_NUMBER 0x30000048
+#define TIMEREMOTE_PROG_VER_1 0x00010001
+#define TIMEREMOTE_PROG_VER_2 0x00040001
+
+#define RTC_REQUEST_CB_PROC		0x17
+#define RTC_CLIENT_INIT_PROC		0x12
+#define RTC_EVENT_CB_PROC		0x1
+#define RTC_CB_ID			0x1
+
+/* Client request errors */
+enum rtc_rpc_err {
+	ERR_NONE,
+	ERR_CLIENT_ID_PTR,		/* Invalid client ID pointer */
+	ERR_CLIENT_TYPE,		/* Invalid client type */
+	ERR_CLIENT_ID,			/* Invalid client ID */
+	ERR_TASK_NOT_READY,		/* task is not ready for clients */
+	ERR_INVALID_PROCESSOR,		/* Invalid processor id */
+	ERR_UNSUPPORTED,		/* Unsupported request */
+	ERR_GENERAL,			/* Any General Error */
+	ERR_RPC,			/* Any ONCRPC Error */
+	ERR_ALREADY_REG,		/* Client already registered */
+	ERR_MAX
+};
+
+enum processor_type {
+	CLIENT_PROCESSOR_NONE   = 0,
+	CLIENT_PROCESSOR_MODEM,
+	CLIENT_PROCESSOR_APP1,
+	CLIENT_PROCESSOR_APP2,
+	CLIENT_PROCESSOR_MAX
+};
+
+/* Client types */
+enum client_type {
+	CLIENT_TYPE_GEN1 = 0,
+	CLIENT_FLOATING1,
+	CLIENT_FLOATING2,
+	CLIENT_TYPE_INTERNAL,
+	CLIENT_TYPE_GENOFF_UPDATE,
+	CLIENT_TYPE_MAX
+};
+
+/* Event types */
+enum event_type {
+	EVENT_TOD_CHANGE = 0,
+	EVENT_GENOFF_CHANGE,
+	EVENT_MAX
+};
+
+struct tod_update_info {
+	uint32_t	tick;
+	uint64_t	stamp;
+	uint32_t	freq;
+};
+
+enum time_bases_info {
+	TIME_RTC = 0,
+	TIME_TOD,
+	TIME_USER,
+	TIME_SECURE,
+	TIME_INVALID
+};
+
+struct genoff_update_info {
+	enum time_bases_info time_base;
+	uint64_t	offset;
+};
+
+union cb_info {
+	struct tod_update_info tod_update;
+	struct genoff_update_info genoff_update;
+};
+
+struct rtc_cb_recv {
+	uint32_t client_cb_id;
+	enum event_type event;
+	uint32_t cb_info_ptr;
+	union cb_info cb_info_data;
+};
+
+struct msm_rtc {
+	int proc;
+	struct msm_rpc_client *rpc_client;
+	u8 client_id;
+	struct rtc_device *rtc;
+#ifdef CONFIG_RTC_SECURE_TIME_SUPPORT
+	struct rtc_device *rtcsecure;
+#endif
+	unsigned long rtcalarm_time;
+};
+
+struct rpc_time_julian {
+	uint32_t year;
+	uint32_t month;
+	uint32_t day;
+	uint32_t hour;
+	uint32_t minute;
+	uint32_t second;
+	uint32_t day_of_week;
+};
+
+struct rtc_tod_args {
+	int proc;
+	struct rtc_time *tm;
+};
+
+#ifdef CONFIG_PM
+struct suspend_state_info {
+	atomic_t state;
+	int64_t tick_at_suspend;
+};
+
+static struct suspend_state_info suspend_state = {ATOMIC_INIT(0), 0};
+
+void msmrtc_updateatsuspend(struct timespec *ts)
+{
+	int64_t now, sleep, sclk_max;
+
+	if (atomic_read(&suspend_state.state)) {
+		now = msm_timer_get_sclk_time(&sclk_max);
+
+		if (now && suspend_state.tick_at_suspend) {
+			if (now < suspend_state.tick_at_suspend) {
+				sleep = sclk_max -
+					suspend_state.tick_at_suspend + now;
+			} else
+				sleep = now - suspend_state.tick_at_suspend;
+
+			timespec_add_ns(ts, sleep);
+			suspend_state.tick_at_suspend = now;
+		} else
+			pr_err("%s: Invalid ticks from SCLK now=%lld"
+				"tick_at_suspend=%lld", __func__, now,
+				suspend_state.tick_at_suspend);
+	}
+
+}
+#else
+void msmrtc_updateatsuspend(struct timespec *ts) { }
+#endif
+EXPORT_SYMBOL(msmrtc_updateatsuspend);
+
+static int msmrtc_tod_proc_args(struct msm_rpc_client *client, void *buff,
+							void *data)
+{
+	struct rtc_tod_args *rtc_args = data;
+
+	if ((rtc_args->proc == TIMEREMOTE_PROCEEDURE_SET_JULIAN)
+#ifdef CONFIG_RTC_SECURE_TIME_SUPPORT
+	|| (rtc_args->proc == TIMEREMOTE_PROCEEDURE_SET_SECURE_JULIAN)
+#endif
+	) {
+		struct timeremote_set_julian_req {
+			uint32_t opt_arg;
+			struct rpc_time_julian time;
+		};
+		struct timeremote_set_julian_req *set_req = buff;
+
+		set_req->opt_arg = cpu_to_be32(0x1);
+		set_req->time.year = cpu_to_be32(rtc_args->tm->tm_year);
+		set_req->time.month = cpu_to_be32(rtc_args->tm->tm_mon + 1);
+		set_req->time.day = cpu_to_be32(rtc_args->tm->tm_mday);
+		set_req->time.hour = cpu_to_be32(rtc_args->tm->tm_hour);
+		set_req->time.minute = cpu_to_be32(rtc_args->tm->tm_min);
+		set_req->time.second = cpu_to_be32(rtc_args->tm->tm_sec);
+		set_req->time.day_of_week = cpu_to_be32(rtc_args->tm->tm_wday);
+
+		return sizeof(*set_req);
+
+	} else if ((rtc_args->proc == TIMEREMOTE_PROCEEDURE_GET_JULIAN)
+#ifdef CONFIG_RTC_SECURE_TIME_SUPPORT
+	|| (rtc_args->proc == TIMEREMOTE_PROCEEDURE_GET_SECURE_JULIAN)
+#endif
+	) {
+		*(uint32_t *)buff = (uint32_t) cpu_to_be32(0x1);
+
+		return sizeof(uint32_t);
+	} else
+		return 0;
+}
+
+static bool rtc_check_overflow(struct rtc_time *tm)
+{
+	if (tm->tm_year < 138)
+		return false;
+
+	if (tm->tm_year > 138)
+		return true;
+
+	if ((tm->tm_year == 138) && (tm->tm_mon == 0) && (tm->tm_mday < 19))
+		return false;
+
+	return true;
+}
+
+static int msmrtc_tod_proc_result(struct msm_rpc_client *client, void *buff,
+							void *data)
+{
+	struct rtc_tod_args *rtc_args = data;
+
+	if ((rtc_args->proc == TIMEREMOTE_PROCEEDURE_GET_JULIAN)
+#ifdef CONFIG_RTC_SECURE_TIME_SUPPORT
+	|| (rtc_args->proc == TIMEREMOTE_PROCEEDURE_GET_SECURE_JULIAN)
+#endif
+	)  {
+		struct timeremote_get_julian_rep {
+			uint32_t opt_arg;
+			struct rpc_time_julian time;
+		};
+		struct timeremote_get_julian_rep *result = buff;
+
+		if (be32_to_cpu(result->opt_arg) != 0x1)
+			return -ENODATA;
+
+		rtc_args->tm->tm_year = be32_to_cpu(result->time.year);
+		rtc_args->tm->tm_mon = be32_to_cpu(result->time.month);
+		rtc_args->tm->tm_mday = be32_to_cpu(result->time.day);
+		rtc_args->tm->tm_hour = be32_to_cpu(result->time.hour);
+		rtc_args->tm->tm_min = be32_to_cpu(result->time.minute);
+		rtc_args->tm->tm_sec = be32_to_cpu(result->time.second);
+		rtc_args->tm->tm_wday = be32_to_cpu(result->time.day_of_week);
+
+		pr_debug("%s: %.2u/%.2u/%.4u %.2u:%.2u:%.2u (%.2u)\n",
+			__func__, rtc_args->tm->tm_mon, rtc_args->tm->tm_mday,
+			rtc_args->tm->tm_year, rtc_args->tm->tm_hour,
+			rtc_args->tm->tm_min, rtc_args->tm->tm_sec,
+			rtc_args->tm->tm_wday);
+
+		/* RTC layer expects years to start at 1900 */
+		rtc_args->tm->tm_year -= 1900;
+		/* RTC layer expects mons to be 0 based */
+		rtc_args->tm->tm_mon--;
+
+		if (rtc_valid_tm(rtc_args->tm) < 0) {
+			pr_err("%s: Retrieved data/time not valid\n", __func__);
+			rtc_time_to_tm(0, rtc_args->tm);
+		}
+
+		/*
+		 * Check if the time received is > 01-19-2038, to prevent
+		 * overflow. In such a case, return the EPOCH time.
+		 */
+		if (rtc_check_overflow(rtc_args->tm) == true) {
+			pr_err("Invalid time (year > 2038)\n");
+			rtc_time_to_tm(0, rtc_args->tm);
+		}
+
+		return 0;
+	} else
+		return 0;
+}
+
+static int
+msmrtc_timeremote_set_time(struct device *dev, struct rtc_time *tm)
+{
+	int rc;
+	struct rtc_tod_args rtc_args;
+	struct msm_rtc *rtc_pdata = dev_get_drvdata(dev);
+
+	if (tm->tm_year < 1900)
+		tm->tm_year += 1900;
+
+	if (tm->tm_year < 1970)
+		return -EINVAL;
+
+	dev_dbg(dev, "%s: %.2u/%.2u/%.4u %.2u:%.2u:%.2u (%.2u)\n",
+	       __func__, tm->tm_mon, tm->tm_mday, tm->tm_year,
+	       tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_wday);
+
+	rtc_args.proc = TIMEREMOTE_PROCEEDURE_SET_JULIAN;
+	rtc_args.tm = tm;
+	rc = msm_rpc_client_req(rtc_pdata->rpc_client,
+				TIMEREMOTE_PROCEEDURE_SET_JULIAN,
+				msmrtc_tod_proc_args, &rtc_args,
+				NULL, NULL, -1);
+	if (rc) {
+		dev_err(dev, "%s: rtc time (TOD) could not be set\n", __func__);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int
+msmrtc_timeremote_read_time(struct device *dev, struct rtc_time *tm)
+{
+	int rc;
+	struct rtc_tod_args rtc_args;
+	struct msm_rtc *rtc_pdata = dev_get_drvdata(dev);
+
+	rtc_args.proc = TIMEREMOTE_PROCEEDURE_GET_JULIAN;
+	rtc_args.tm = tm;
+
+	rc = msm_rpc_client_req(rtc_pdata->rpc_client,
+				TIMEREMOTE_PROCEEDURE_GET_JULIAN,
+				msmrtc_tod_proc_args, &rtc_args,
+				msmrtc_tod_proc_result, &rtc_args, -1);
+
+	if (rc) {
+		dev_err(dev, "%s: Error retrieving rtc (TOD) time\n", __func__);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int
+msmrtc_virtual_alarm_set(struct device *dev, struct rtc_wkalrm *a)
+{
+	struct msm_rtc *rtc_pdata = dev_get_drvdata(dev);
+	unsigned long now = get_seconds();
+
+	if (!a->enabled) {
+		rtc_pdata->rtcalarm_time = 0;
+		return 0;
+	} else
+		rtc_tm_to_time(&a->time, &(rtc_pdata->rtcalarm_time));
+
+	if (now > rtc_pdata->rtcalarm_time) {
+		dev_err(dev, "%s: Attempt to set alarm in the past\n",
+		       __func__);
+		rtc_pdata->rtcalarm_time = 0;
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static struct rtc_class_ops msm_rtc_ops = {
+	.read_time	= msmrtc_timeremote_read_time,
+	.set_time	= msmrtc_timeremote_set_time,
+	.set_alarm	= msmrtc_virtual_alarm_set,
+};
+
+#ifdef CONFIG_RTC_SECURE_TIME_SUPPORT
+static int
+msmrtc_timeremote_set_time_secure(struct device *dev, struct rtc_time *tm)
+{
+	int rc;
+	struct rtc_tod_args rtc_args;
+	struct msm_rtc *rtc_pdata = dev_get_drvdata(dev);
+
+	if (tm->tm_year < 1900)
+		tm->tm_year += 1900;
+
+	if (tm->tm_year < 1970)
+		return -EINVAL;
+
+	dev_dbg(dev, "%s: %.2u/%.2u/%.4u %.2u:%.2u:%.2u (%.2u)\n",
+	       __func__, tm->tm_mon, tm->tm_mday, tm->tm_year,
+	       tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_wday);
+
+	rtc_args.proc = TIMEREMOTE_PROCEEDURE_SET_SECURE_JULIAN;
+	rtc_args.tm = tm;
+
+	rc = msm_rpc_client_req(rtc_pdata->rpc_client,
+			TIMEREMOTE_PROCEEDURE_SET_SECURE_JULIAN,
+				msmrtc_tod_proc_args, &rtc_args,
+				NULL, NULL, -1);
+	if (rc) {
+		dev_err(dev,
+			"%s: rtc secure time could not be set\n", __func__);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int
+msmrtc_timeremote_read_time_secure(struct device *dev, struct rtc_time *tm)
+{
+	int rc;
+	struct rtc_tod_args rtc_args;
+	struct msm_rtc *rtc_pdata = dev_get_drvdata(dev);
+	rtc_args.proc = TIMEREMOTE_PROCEEDURE_GET_SECURE_JULIAN;
+	rtc_args.tm = tm;
+
+	rc = msm_rpc_client_req(rtc_pdata->rpc_client,
+		TIMEREMOTE_PROCEEDURE_GET_SECURE_JULIAN, msmrtc_tod_proc_args,
+		&rtc_args, msmrtc_tod_proc_result, &rtc_args, -1);
+
+	if (rc) {
+		dev_err(dev,
+			"%s: Error retrieving secure rtc time\n", __func__);
+		return rc;
+	}
+
+	return 0;
+}
+
+static struct rtc_class_ops msm_rtc_ops_secure = {
+	.read_time	= msmrtc_timeremote_read_time_secure,
+	.set_time	= msmrtc_timeremote_set_time_secure,
+};
+#endif
+
+static void process_cb_request(void *buffer)
+{
+	struct rtc_cb_recv *rtc_cb = buffer;
+	struct timespec ts, tv;
+
+	rtc_cb->client_cb_id = be32_to_cpu(rtc_cb->client_cb_id);
+	rtc_cb->event = be32_to_cpu(rtc_cb->event);
+	rtc_cb->cb_info_ptr = be32_to_cpu(rtc_cb->cb_info_ptr);
+
+	if (rtc_cb->event == EVENT_TOD_CHANGE) {
+		/* A TOD update has been received from the Modem */
+		rtc_cb->cb_info_data.tod_update.tick =
+			be32_to_cpu(rtc_cb->cb_info_data.tod_update.tick);
+		rtc_cb->cb_info_data.tod_update.stamp =
+			be64_to_cpu(rtc_cb->cb_info_data.tod_update.stamp);
+		rtc_cb->cb_info_data.tod_update.freq =
+			be32_to_cpu(rtc_cb->cb_info_data.tod_update.freq);
+		pr_info("RPC CALL -- TOD TIME UPDATE: ttick = %d\n"
+			"stamp=%lld, freq = %d\n",
+			rtc_cb->cb_info_data.tod_update.tick,
+			rtc_cb->cb_info_data.tod_update.stamp,
+			rtc_cb->cb_info_data.tod_update.freq);
+
+		getnstimeofday(&ts);
+		msmrtc_updateatsuspend(&ts);
+		rtc_hctosys();
+		getnstimeofday(&tv);
+		/* Update the alarm information with the new time info. */
+		alarm_update_timedelta(ts, tv);
+
+	} else
+		pr_err("%s: Unknown event EVENT=%x\n",
+					__func__, rtc_cb->event);
+}
+
+static int msmrtc_cb_func(struct msm_rpc_client *client, void *buffer, int size)
+{
+	int rc = -1;
+	struct rpc_request_hdr *recv = buffer;
+
+	recv->xid = be32_to_cpu(recv->xid);
+	recv->type = be32_to_cpu(recv->type);
+	recv->rpc_vers = be32_to_cpu(recv->rpc_vers);
+	recv->prog = be32_to_cpu(recv->prog);
+	recv->vers = be32_to_cpu(recv->vers);
+	recv->procedure = be32_to_cpu(recv->procedure);
+
+	if (recv->procedure == RTC_EVENT_CB_PROC)
+		process_cb_request((void *) (recv + 1));
+
+	msm_rpc_start_accepted_reply(client, recv->xid,
+				RPC_ACCEPTSTAT_SUCCESS);
+
+	rc = msm_rpc_send_accepted_reply(client, 0);
+	if (rc) {
+		pr_debug("%s: sending reply failed: %d\n", __func__, rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int msmrtc_rpc_proc_args(struct msm_rpc_client *client, void *buff,
+							void *data)
+{
+	struct msm_rtc *rtc_pdata = data;
+
+	if (rtc_pdata->proc == RTC_CLIENT_INIT_PROC) {
+		/* arguments passed to the client_init function */
+		struct rtc_client_init_req {
+			enum client_type client;
+			uint32_t client_id_ptr;
+			u8 client_id;
+			enum processor_type processor;
+		};
+		struct rtc_client_init_req *req_1 = buff;
+
+		req_1->client = cpu_to_be32(CLIENT_TYPE_INTERNAL);
+		req_1->client_id_ptr = cpu_to_be32(0x1);
+		req_1->client_id = (u8) cpu_to_be32(0x1);
+		req_1->processor = cpu_to_be32(CLIENT_PROCESSOR_APP1);
+
+		return sizeof(*req_1);
+
+	} else if (rtc_pdata->proc == RTC_REQUEST_CB_PROC) {
+		/* arguments passed to the request_cb function */
+		struct rtc_event_req {
+			u8 client_id;
+			uint32_t rtc_cb_id;
+		};
+		struct rtc_event_req *req_2 = buff;
+
+		req_2->client_id =  (u8) cpu_to_be32(rtc_pdata->client_id);
+		req_2->rtc_cb_id = cpu_to_be32(RTC_CB_ID);
+
+		return sizeof(*req_2);
+	} else
+		return 0;
+}
+
+static int msmrtc_rpc_proc_result(struct msm_rpc_client *client, void *buff,
+							void *data)
+{
+	uint32_t result = -EINVAL;
+	struct msm_rtc *rtc_pdata = data;
+
+	if (rtc_pdata->proc == RTC_CLIENT_INIT_PROC) {
+		/* process reply received from client_init function */
+		uint32_t client_id_ptr;
+		result = be32_to_cpu(*(uint32_t *)buff);
+		buff += sizeof(uint32_t);
+		client_id_ptr = be32_to_cpu(*(uint32_t *)(buff));
+		buff += sizeof(uint32_t);
+		if (client_id_ptr == 1)
+			rtc_pdata->client_id = (u8)
+					be32_to_cpu(*(uint32_t *)(buff));
+		else {
+			pr_debug("%s: Client-id not received from Modem\n",
+								__func__);
+			return -EINVAL;
+		}
+	} else if (rtc_pdata->proc == RTC_REQUEST_CB_PROC) {
+		/* process reply received from request_cb function */
+		result = be32_to_cpu(*(uint32_t *)buff);
+	}
+
+	if (result == ERR_NONE) {
+		pr_debug("%s: RPC client reply for PROC=%x success\n",
+					 __func__, rtc_pdata->proc);
+		return 0;
+	}
+
+	pr_debug("%s: RPC client registration failed ERROR=%x\n",
+						__func__, result);
+	return -EINVAL;
+}
+
+static int msmrtc_setup_cb(struct msm_rtc *rtc_pdata)
+{
+	int rc;
+
+	/* Register with the server with client specific info */
+	rtc_pdata->proc = RTC_CLIENT_INIT_PROC;
+	rc = msm_rpc_client_req(rtc_pdata->rpc_client, RTC_CLIENT_INIT_PROC,
+				msmrtc_rpc_proc_args, rtc_pdata,
+				msmrtc_rpc_proc_result, rtc_pdata, -1);
+	if (rc) {
+		pr_debug("%s: RPC client registration for PROC:%x failed\n",
+					__func__, RTC_CLIENT_INIT_PROC);
+		return rc;
+	}
+
+	/* Register with server for the callback event */
+	rtc_pdata->proc = RTC_REQUEST_CB_PROC;
+	rc = msm_rpc_client_req(rtc_pdata->rpc_client, RTC_REQUEST_CB_PROC,
+				msmrtc_rpc_proc_args, rtc_pdata,
+				msmrtc_rpc_proc_result, rtc_pdata, -1);
+	if (rc) {
+		pr_debug("%s: RPC client registration for PROC:%x failed\n",
+					__func__, RTC_REQUEST_CB_PROC);
+	}
+
+	return rc;
+}
+
+static int __devinit
+msmrtc_probe(struct platform_device *pdev)
+{
+	int rc;
+	struct msm_rtc *rtc_pdata = NULL;
+	struct rpcsvr_platform_device *rdev =
+		container_of(pdev, struct rpcsvr_platform_device, base);
+	uint32_t prog_version;
+
+
+	if (pdev->id == (TIMEREMOTE_PROG_VER_1 & RPC_VERSION_MAJOR_MASK))
+		prog_version = TIMEREMOTE_PROG_VER_1;
+	else if (pdev->id == (TIMEREMOTE_PROG_VER_2 &
+			      RPC_VERSION_MAJOR_MASK))
+		prog_version = TIMEREMOTE_PROG_VER_2;
+	else
+		return -EINVAL;
+
+	rtc_pdata = kzalloc(sizeof(*rtc_pdata), GFP_KERNEL);
+	if (rtc_pdata == NULL) {
+		dev_err(&pdev->dev,
+			"%s: Unable to allocate memory\n", __func__);
+		return -ENOMEM;
+	}
+	rtc_pdata->rpc_client = msm_rpc_register_client("rtc", rdev->prog,
+				prog_version, 1, msmrtc_cb_func);
+	if (IS_ERR(rtc_pdata->rpc_client)) {
+		dev_err(&pdev->dev,
+			 "%s: init RPC failed! VERS = %x\n", __func__,
+					prog_version);
+		rc = PTR_ERR(rtc_pdata->rpc_client);
+		kfree(rtc_pdata);
+		return rc;
+	}
+
+	/*
+	 * Set up the callback client.
+	 * For older targets this initialization will fail
+	 */
+	rc = msmrtc_setup_cb(rtc_pdata);
+	if (rc)
+		dev_dbg(&pdev->dev, "%s: Could not initialize RPC callback\n",
+								__func__);
+
+	rtc_pdata->rtcalarm_time = 0;
+	platform_set_drvdata(pdev, rtc_pdata);
+
+	rtc_pdata->rtc = rtc_device_register("msm_rtc",
+				  &pdev->dev,
+				  &msm_rtc_ops,
+				  THIS_MODULE);
+	if (IS_ERR(rtc_pdata->rtc)) {
+		dev_err(&pdev->dev, "%s: Can't register RTC device (%ld)\n",
+		       pdev->name, PTR_ERR(rtc_pdata->rtc));
+		rc = PTR_ERR(rtc_pdata->rtc);
+		goto fail_cb_setup;
+	}
+
+#ifdef CONFIG_RTC_SECURE_TIME_SUPPORT
+	rtc_pdata->rtcsecure = rtc_device_register("msm_rtc_secure",
+				  &pdev->dev,
+				  &msm_rtc_ops_secure,
+				  THIS_MODULE);
+
+	if (IS_ERR(rtc_pdata->rtcsecure)) {
+		dev_err(&pdev->dev,
+			"%s: Can't register RTC Secure device (%ld)\n",
+		       pdev->name, PTR_ERR(rtc_pdata->rtcsecure));
+		rtc_device_unregister(rtc_pdata->rtc);
+		rc = PTR_ERR(rtc_pdata->rtcsecure);
+		goto fail_cb_setup;
+	}
+#endif
+
+#ifdef CONFIG_RTC_ASYNC_MODEM_SUPPORT
+	rtc_hctosys();
+#endif
+
+	return 0;
+
+fail_cb_setup:
+	msm_rpc_unregister_client(rtc_pdata->rpc_client);
+	kfree(rtc_pdata);
+	return rc;
+}
+
+
+#ifdef CONFIG_PM
+
+static void
+msmrtc_alarmtimer_expired(unsigned long _data,
+				struct msm_rtc *rtc_pdata)
+{
+	pr_debug("%s: Generating alarm event (src %lu)\n",
+	       rtc_pdata->rtc->name, _data);
+
+	rtc_update_irq(rtc_pdata->rtc, 1, RTC_IRQF | RTC_AF);
+	rtc_pdata->rtcalarm_time = 0;
+}
+
+static int
+msmrtc_suspend(struct platform_device *dev, pm_message_t state)
+{
+	int rc, diff;
+	struct rtc_time tm;
+	unsigned long now;
+	struct msm_rtc *rtc_pdata = platform_get_drvdata(dev);
+
+	suspend_state.tick_at_suspend = msm_timer_get_sclk_time(NULL);
+	if (rtc_pdata->rtcalarm_time) {
+		rc = msmrtc_timeremote_read_time(&dev->dev, &tm);
+		if (rc) {
+			dev_err(&dev->dev,
+				"%s: Unable to read from RTC\n", __func__);
+			return rc;
+		}
+		rtc_tm_to_time(&tm, &now);
+		diff = rtc_pdata->rtcalarm_time - now;
+		if (diff <= 0) {
+			msmrtc_alarmtimer_expired(1 , rtc_pdata);
+			msm_pm_set_max_sleep_time(0);
+			atomic_inc(&suspend_state.state);
+			return 0;
+		}
+		msm_pm_set_max_sleep_time((int64_t)
+			((int64_t) diff * NSEC_PER_SEC));
+	} else
+		msm_pm_set_max_sleep_time(0);
+	atomic_inc(&suspend_state.state);
+	return 0;
+}
+
+static int
+msmrtc_resume(struct platform_device *dev)
+{
+	int rc, diff;
+	struct rtc_time tm;
+	unsigned long now;
+	struct msm_rtc *rtc_pdata = platform_get_drvdata(dev);
+
+	if (rtc_pdata->rtcalarm_time) {
+		rc = msmrtc_timeremote_read_time(&dev->dev, &tm);
+		if (rc) {
+			dev_err(&dev->dev,
+				"%s: Unable to read from RTC\n", __func__);
+			return rc;
+		}
+		rtc_tm_to_time(&tm, &now);
+		diff = rtc_pdata->rtcalarm_time - now;
+		if (diff <= 0)
+			msmrtc_alarmtimer_expired(2 , rtc_pdata);
+	}
+	suspend_state.tick_at_suspend = 0;
+	atomic_dec(&suspend_state.state);
+	return 0;
+}
+#else
+#define msmrtc_suspend NULL
+#define msmrtc_resume  NULL
+#endif
+
+static int __devexit msmrtc_remove(struct platform_device *pdev)
+{
+	struct msm_rtc *rtc_pdata = platform_get_drvdata(pdev);
+
+	rtc_device_unregister(rtc_pdata->rtc);
+#ifdef CONFIG_RTC_SECURE_TIME_SUPPORT
+	rtc_device_unregister(rtc_pdata->rtcsecure);
+#endif
+	msm_rpc_unregister_client(rtc_pdata->rpc_client);
+	kfree(rtc_pdata);
+
+	return 0;
+}
+
+static struct platform_driver msmrtc_driver = {
+	.probe		= msmrtc_probe,
+	.suspend	= msmrtc_suspend,
+	.resume		= msmrtc_resume,
+	.remove		= __devexit_p(msmrtc_remove),
+	.driver	= {
+		.name	= APP_TIMEREMOTE_PDEV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init msmrtc_init(void)
+{
+	int rc;
+
+	/*
+	 * For backward compatibility, register multiple platform
+	 * drivers with the RPC PROG_VERS to be supported.
+	 *
+	 * Explicit cast away of 'constness' for driver.name in order to
+	 * initialize it here.
+	 */
+	snprintf((char *)msmrtc_driver.driver.name,
+		 strlen(msmrtc_driver.driver.name)+1,
+		 "rs%08x", TIMEREMOTE_PROG_NUMBER);
+	pr_debug("RTC Registering with %s\n", msmrtc_driver.driver.name);
+
+	rc = platform_driver_register(&msmrtc_driver);
+	if (rc)
+		pr_err("%s: platfrom_driver_register failed\n", __func__);
+
+	return rc;
+}
+
+static void __exit msmrtc_exit(void)
+{
+	platform_driver_unregister(&msmrtc_driver);
+}
+
+module_init(msmrtc_init);
+module_exit(msmrtc_exit);
+
+MODULE_DESCRIPTION("RTC driver for Qualcomm MSM7x00a chipsets");
+MODULE_AUTHOR("San Mehat <san@android.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-msm7x00a.c b/drivers/rtc/rtc-msm7x00a.c
new file mode 100644
index 0000000..690bc39
--- /dev/null
+++ b/drivers/rtc/rtc-msm7x00a.c
@@ -0,0 +1,280 @@
+/* drivers/rtc/rtc-msm7x00a.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: San Mehat <san@google.com>
+ *
+ * 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/version.h>
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/rtc.h>
+#include <linux/msm_rpcrouter.h>
+
+#include <mach/msm_rpcrouter.h>
+
+#define RTC_DEBUG 0
+
+extern void msm_pm_set_max_sleep_time(int64_t sleep_time_ns);
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350 || defined(CONFIG_ARCH_QSD8X50)
+#define APP_TIMEREMOTE_PDEV_NAME "rs30000048:00010000"
+#else
+#define APP_TIMEREMOTE_PDEV_NAME "rs30000048:0da5b528"
+#endif
+
+#define TIMEREMOTE_PROCEEDURE_SET_JULIAN	6
+#define TIMEREMOTE_PROCEEDURE_GET_JULIAN	7
+
+struct rpc_time_julian {
+	uint32_t year;
+	uint32_t month;
+	uint32_t day;
+	uint32_t hour;
+	uint32_t minute;
+	uint32_t second;
+	uint32_t day_of_week;
+};
+
+static struct msm_rpc_endpoint *ep;
+static struct rtc_device *rtc;
+static unsigned long rtcalarm_time;
+
+static int
+msmrtc_timeremote_set_time(struct device *dev, struct rtc_time *tm)
+{
+	int rc;
+
+	struct timeremote_set_julian_req {
+		struct rpc_request_hdr hdr;
+		uint32_t opt_arg;
+
+		struct rpc_time_julian time;
+	} req;
+
+	struct timeremote_set_julian_rep {
+		struct rpc_reply_hdr hdr;
+	} rep;
+
+	if (tm->tm_year < 1900)
+		tm->tm_year += 1900;
+
+	if (tm->tm_year < 1970)
+		return -EINVAL;
+
+#if RTC_DEBUG
+	printk(KERN_DEBUG "%s: %.2u/%.2u/%.4u %.2u:%.2u:%.2u (%.2u)\n",
+	       __func__, tm->tm_mon, tm->tm_mday, tm->tm_year,
+	       tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_wday);
+#endif
+
+	req.opt_arg = cpu_to_be32(1);
+	req.time.year = cpu_to_be32(tm->tm_year);
+	req.time.month = cpu_to_be32(tm->tm_mon + 1);
+	req.time.day = cpu_to_be32(tm->tm_mday);
+	req.time.hour = cpu_to_be32(tm->tm_hour);
+	req.time.minute = cpu_to_be32(tm->tm_min);
+	req.time.second = cpu_to_be32(tm->tm_sec);
+	req.time.day_of_week = cpu_to_be32(tm->tm_wday);
+
+
+	rc = msm_rpc_call_reply(ep, TIMEREMOTE_PROCEEDURE_SET_JULIAN,
+				&req, sizeof(req),
+				&rep, sizeof(rep),
+				5 * HZ);
+	return rc;
+}
+
+static int
+msmrtc_timeremote_read_time(struct device *dev, struct rtc_time *tm)
+{
+	int rc;
+
+	struct timeremote_get_julian_req {
+		struct rpc_request_hdr hdr;
+		uint32_t julian_time_not_null;
+	} req;
+
+	struct timeremote_get_julian_rep {
+		struct rpc_reply_hdr hdr;
+		uint32_t opt_arg;
+		struct rpc_time_julian time;
+	} rep;
+
+	req.julian_time_not_null = cpu_to_be32(1);
+
+	rc = msm_rpc_call_reply(ep, TIMEREMOTE_PROCEEDURE_GET_JULIAN,
+				&req, sizeof(req),
+				&rep, sizeof(rep),
+				5 * HZ);
+	if (rc < 0)
+		return rc;
+
+	if (!be32_to_cpu(rep.opt_arg)) {
+		printk(KERN_ERR "%s: No data from RTC\n", __func__);
+		return -ENODATA;
+	}
+
+	tm->tm_year = be32_to_cpu(rep.time.year);
+	tm->tm_mon = be32_to_cpu(rep.time.month);
+	tm->tm_mday = be32_to_cpu(rep.time.day);
+	tm->tm_hour = be32_to_cpu(rep.time.hour);
+	tm->tm_min = be32_to_cpu(rep.time.minute);
+	tm->tm_sec = be32_to_cpu(rep.time.second);
+	tm->tm_wday = be32_to_cpu(rep.time.day_of_week);
+
+#if RTC_DEBUG
+	printk(KERN_DEBUG "%s: %.2u/%.2u/%.4u %.2u:%.2u:%.2u (%.2u)\n",
+	       __func__, tm->tm_mon, tm->tm_mday, tm->tm_year,
+	       tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_wday);
+#endif
+
+	tm->tm_year -= 1900;	/* RTC layer expects years to start at 1900 */
+	tm->tm_mon--;		/* RTC layer expects mons to be 0 based */
+
+	if (rtc_valid_tm(tm) < 0) {
+		dev_err(dev, "retrieved date/time is not valid.\n");
+		rtc_time_to_tm(0, tm);
+	}
+
+	return 0;
+}
+
+
+static int
+msmrtc_virtual_alarm_set(struct device *dev, struct rtc_wkalrm *a)
+{
+	unsigned long now = get_seconds();
+
+	if (!a->enabled) {
+		rtcalarm_time = 0;
+		return 0;
+	} else
+		rtc_tm_to_time(&a->time, &rtcalarm_time);
+
+	if (now > rtcalarm_time) {
+		printk(KERN_ERR "%s: Attempt to set alarm in the past\n",
+		       __func__);
+		rtcalarm_time = 0;
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static struct rtc_class_ops msm_rtc_ops = {
+	.read_time	= msmrtc_timeremote_read_time,
+	.set_time	= msmrtc_timeremote_set_time,
+	.set_alarm	= msmrtc_virtual_alarm_set,
+};
+
+static void
+msmrtc_alarmtimer_expired(unsigned long _data)
+{
+#if RTC_DEBUG
+	printk(KERN_DEBUG "%s: Generating alarm event (src %lu)\n",
+	       rtc->name, _data);
+#endif
+	rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF);
+	rtcalarm_time = 0;
+}
+
+static int
+msmrtc_probe(struct platform_device *pdev)
+{
+	struct rpcsvr_platform_device *rdev =
+		container_of(pdev, struct rpcsvr_platform_device, base);
+
+	ep = msm_rpc_connect(rdev->prog, rdev->vers, 0);
+	if (IS_ERR(ep)) {
+		printk(KERN_ERR "%s: init rpc failed! rc = %ld\n",
+		       __func__, PTR_ERR(ep));
+		return PTR_ERR(ep);
+	}
+
+	rtc = rtc_device_register("msm_rtc",
+				  &pdev->dev,
+				  &msm_rtc_ops,
+				  THIS_MODULE);
+	if (IS_ERR(rtc)) {
+		printk(KERN_ERR "%s: Can't register RTC device (%ld)\n",
+		       pdev->name, PTR_ERR(rtc));
+		return PTR_ERR(rtc);
+	}
+	return 0;
+}
+
+
+static unsigned long msmrtc_get_seconds(void)
+{
+	struct rtc_time tm;
+	unsigned long now;
+
+	msmrtc_timeremote_read_time(NULL, &tm);
+	rtc_tm_to_time(&tm, &now);
+	return now;
+}
+
+static int
+msmrtc_suspend(struct platform_device *dev, pm_message_t state)
+{
+	if (rtcalarm_time) {
+		unsigned long now = msmrtc_get_seconds();
+		int diff = rtcalarm_time - now;
+		if (diff <= 0) {
+			msmrtc_alarmtimer_expired(1);
+			msm_pm_set_max_sleep_time(0);
+			return 0;
+		}
+		msm_pm_set_max_sleep_time((int64_t) ((int64_t) diff * NSEC_PER_SEC));
+	} else
+		msm_pm_set_max_sleep_time(0);
+	return 0;
+}
+
+static int
+msmrtc_resume(struct platform_device *dev)
+{
+	if (rtcalarm_time) {
+		unsigned long now = msmrtc_get_seconds();
+		int diff = rtcalarm_time - now;
+		if (diff <= 0)
+			msmrtc_alarmtimer_expired(2);
+	}
+	return 0;
+}
+
+static struct platform_driver msmrtc_driver = {
+	.probe		= msmrtc_probe,
+	.suspend	= msmrtc_suspend,
+	.resume		= msmrtc_resume,
+	.driver	= {
+		.name	= APP_TIMEREMOTE_PDEV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init msmrtc_init(void)
+{
+	rtcalarm_time = 0;
+	return platform_driver_register(&msmrtc_driver);
+}
+
+module_init(msmrtc_init);
+
+MODULE_DESCRIPTION("RTC driver for Qualcomm MSM7x00a chipsets");
+MODULE_AUTHOR("San Mehat <san@android.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-pm8xxx.c b/drivers/rtc/rtc-pm8xxx.c
index d00bd24..e53374e 100644
--- a/drivers/rtc/rtc-pm8xxx.c
+++ b/drivers/rtc/rtc-pm8xxx.c
@@ -15,36 +15,31 @@
 #include <linux/rtc.h>
 #include <linux/pm.h>
 #include <linux/slab.h>
-#include <linux/spinlock.h>
+#include<linux/spinlock.h>
 
 #include <linux/mfd/pm8xxx/core.h>
 #include <linux/mfd/pm8xxx/rtc.h>
 
 
 /* RTC Register offsets from RTC CTRL REG */
-#define PM8XXX_ALARM_CTRL_OFFSET	0x01
-#define PM8XXX_RTC_WRITE_OFFSET		0x02
-#define PM8XXX_RTC_READ_OFFSET		0x06
-#define PM8XXX_ALARM_RW_OFFSET		0x0A
+#define PM8XXX_ALARM_CTRL_OFFSET 0x01
+#define PM8XXX_RTC_WRITE_OFFSET 0x02
+#define PM8XXX_RTC_READ_OFFSET 0x06
+#define PM8XXX_ALARM_RW_OFFSET 0x0A
 
 /* RTC_CTRL register bit fields */
-#define PM8xxx_RTC_ENABLE		BIT(7)
-#define PM8xxx_RTC_ALARM_ENABLE		BIT(1)
-#define PM8xxx_RTC_ALARM_CLEAR		BIT(0)
+#define PM8xxx_RTC_ENABLE	BIT(7)
+#define PM8xxx_RTC_ALARM_ENABLE	BIT(1)
+#define PM8xxx_RTC_ABORT_ENABLE BIT(0)
 
-#define NUM_8_BIT_RTC_REGS		0x4
+#define PM8xxx_RTC_ALARM_CLEAR  BIT(0)
+
+#define NUM_8_BIT_RTC_REGS	0x4
 
 /**
- * struct pm8xxx_rtc -  rtc driver internal structure
- * @rtc:		rtc device for this driver.
- * @rtc_alarm_irq:	rtc alarm irq number.
- * @rtc_base:		address of rtc control register.
- * @rtc_read_base:	base address of read registers.
- * @rtc_write_base:	base address of write registers.
- * @alarm_rw_base:	base address of alarm registers.
- * @ctrl_reg:		rtc control register.
- * @rtc_dev:		device structure.
- * @ctrl_reg_lock:	spinlock protecting access to ctrl_reg.
+ * struct pm8xxx_rtc - rtc driver internal structure
+ * @rtc: rtc device for this driver
+ * @rtc_alarm_irq: rtc alarm irq number
  */
 struct pm8xxx_rtc {
 	struct rtc_device *rtc;
@@ -62,8 +57,9 @@
  * The RTC registers need to be read/written one byte at a time. This is a
  * hardware limitation.
  */
+
 static int pm8xxx_read_wrapper(struct pm8xxx_rtc *rtc_dd, u8 *rtc_val,
-		int base, int count)
+			int base, int count)
 {
 	int i, rc;
 	struct device *parent = rtc_dd->rtc_dev->parent;
@@ -71,7 +67,7 @@
 	for (i = 0; i < count; i++) {
 		rc = pm8xxx_readb(parent, base + i, &rtc_val[i]);
 		if (rc < 0) {
-			dev_err(rtc_dd->rtc_dev, "PMIC read failed\n");
+			dev_err(rtc_dd->rtc_dev, "PM8xxx read failed\n");
 			return rc;
 		}
 	}
@@ -80,7 +76,7 @@
 }
 
 static int pm8xxx_write_wrapper(struct pm8xxx_rtc *rtc_dd, u8 *rtc_val,
-		int base, int count)
+			int base, int count)
 {
 	int i, rc;
 	struct device *parent = rtc_dd->rtc_dev->parent;
@@ -88,7 +84,7 @@
 	for (i = 0; i < count; i++) {
 		rc = pm8xxx_writeb(parent, base + i, rtc_val[i]);
 		if (rc < 0) {
-			dev_err(rtc_dd->rtc_dev, "PMIC write failed\n");
+			dev_err(rtc_dd->rtc_dev, "PM8xxx write failed\n");
 			return rc;
 		}
 	}
@@ -96,6 +92,7 @@
 	return 0;
 }
 
+
 /*
  * Steps to write the RTC registers.
  * 1. Disable alarm if enabled.
@@ -103,19 +100,20 @@
  * 3. Write Byte[1], Byte[2], Byte[3] then Byte[0].
  * 4. Enable alarm if disabled in step 1.
  */
-static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm)
+static int
+pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm)
 {
-	int rc, i;
+	int rc;
 	unsigned long secs, irq_flags;
-	u8 value[NUM_8_BIT_RTC_REGS], reg = 0, alarm_enabled = 0, ctrl_reg;
+	u8 value[4], reg = 0, alarm_enabled = 0, ctrl_reg;
 	struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
 
 	rtc_tm_to_time(tm, &secs);
 
-	for (i = 0; i < NUM_8_BIT_RTC_REGS; i++) {
-		value[i] = secs & 0xFF;
-		secs >>= 8;
-	}
+	value[0] = secs & 0xFF;
+	value[1] = (secs >> 8) & 0xFF;
+	value[2] = (secs >> 16) & 0xFF;
+	value[3] = (secs >> 24) & 0xFF;
 
 	dev_dbg(dev, "Seconds value to be written to RTC = %lu\n", secs);
 
@@ -126,21 +124,20 @@
 		alarm_enabled = 1;
 		ctrl_reg &= ~PM8xxx_RTC_ALARM_ENABLE;
 		rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base,
-				1);
+									1);
 		if (rc < 0) {
-			dev_err(dev, "Write to RTC control register "
-								"failed\n");
+			dev_err(dev, "PM8xxx write failed\n");
 			goto rtc_rw_fail;
 		}
-		rtc_dd->ctrl_reg = ctrl_reg;
 	} else
 		spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
 
+	/* Write Byte[1], Byte[2], Byte[3], Byte[0] */
 	/* Write 0 to Byte[0] */
 	reg = 0;
 	rc = pm8xxx_write_wrapper(rtc_dd, &reg, rtc_dd->rtc_write_base, 1);
 	if (rc < 0) {
-		dev_err(dev, "Write to RTC write data register failed\n");
+		dev_err(dev, "PM8xxx write failed\n");
 		goto rtc_rw_fail;
 	}
 
@@ -148,14 +145,14 @@
 	rc = pm8xxx_write_wrapper(rtc_dd, value + 1,
 					rtc_dd->rtc_write_base + 1, 3);
 	if (rc < 0) {
-		dev_err(dev, "Write to RTC write data register failed\n");
+		dev_err(dev, "Write to RTC registers failed\n");
 		goto rtc_rw_fail;
 	}
 
 	/* Write Byte[0] */
 	rc = pm8xxx_write_wrapper(rtc_dd, value, rtc_dd->rtc_write_base, 1);
 	if (rc < 0) {
-		dev_err(dev, "Write to RTC write data register failed\n");
+		dev_err(dev, "Write to RTC register failed\n");
 		goto rtc_rw_fail;
 	}
 
@@ -164,13 +161,13 @@
 		rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base,
 									1);
 		if (rc < 0) {
-			dev_err(dev, "Write to RTC control register "
-								"failed\n");
+			dev_err(dev, "PM8xxx write failed\n");
 			goto rtc_rw_fail;
 		}
-		rtc_dd->ctrl_reg = ctrl_reg;
 	}
 
+	rtc_dd->ctrl_reg = ctrl_reg;
+
 rtc_rw_fail:
 	if (alarm_enabled)
 		spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
@@ -178,17 +175,18 @@
 	return rc;
 }
 
-static int pm8xxx_rtc_read_time(struct device *dev, struct rtc_time *tm)
+static int
+pm8xxx_rtc_read_time(struct device *dev, struct rtc_time *tm)
 {
 	int rc;
-	u8 value[NUM_8_BIT_RTC_REGS], reg;
+	u8 value[4], reg;
 	unsigned long secs;
 	struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
 
 	rc = pm8xxx_read_wrapper(rtc_dd, value, rtc_dd->rtc_read_base,
 							NUM_8_BIT_RTC_REGS);
 	if (rc < 0) {
-		dev_err(dev, "RTC read data register failed\n");
+		dev_err(dev, "RTC time read failed\n");
 		return rc;
 	}
 
@@ -198,7 +196,7 @@
 	 */
 	rc = pm8xxx_read_wrapper(rtc_dd, &reg, rtc_dd->rtc_read_base, 1);
 	if (rc < 0) {
-		dev_err(dev, "RTC read data register failed\n");
+		dev_err(dev, "PM8xxx read failed\n");
 		return rc;
 	}
 
@@ -206,76 +204,96 @@
 		rc = pm8xxx_read_wrapper(rtc_dd, value,
 				rtc_dd->rtc_read_base, NUM_8_BIT_RTC_REGS);
 		if (rc < 0) {
-			dev_err(dev, "RTC read data register failed\n");
+			dev_err(dev, "RTC time read failed\n");
 			return rc;
 		}
 	}
 
-	secs = value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24);
+	secs = value[0] | (value[1] << 8) | (value[2] << 16) \
+						| (value[3] << 24);
 
 	rtc_time_to_tm(secs, tm);
 
 	rc = rtc_valid_tm(tm);
 	if (rc < 0) {
-		dev_err(dev, "Invalid time read from RTC\n");
+		dev_err(dev, "Invalid time read from PM8xxx\n");
 		return rc;
 	}
 
 	dev_dbg(dev, "secs = %lu, h:m:s == %d:%d:%d, d/m/y = %d/%d/%d\n",
-				secs, tm->tm_hour, tm->tm_min, tm->tm_sec,
-				tm->tm_mday, tm->tm_mon, tm->tm_year);
+			secs, tm->tm_hour, tm->tm_min, tm->tm_sec,
+			tm->tm_mday, tm->tm_mon, tm->tm_year);
 
 	return 0;
 }
 
-static int pm8xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+static int
+pm8xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 {
-	int rc, i;
-	u8 value[NUM_8_BIT_RTC_REGS], ctrl_reg;
-	unsigned long secs, irq_flags;
+	int rc;
+	u8 value[4], ctrl_reg;
+	unsigned long secs, secs_rtc, irq_flags;
 	struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
+	struct rtc_time rtc_tm;
 
 	rtc_tm_to_time(&alarm->time, &secs);
 
-	for (i = 0; i < NUM_8_BIT_RTC_REGS; i++) {
-		value[i] = secs & 0xFF;
-		secs >>= 8;
+	/*
+	 * Read the current RTC time and verify if the alarm time is in the
+	 * past. If yes, return invalid.
+	 */
+	rc = pm8xxx_rtc_read_time(dev, &rtc_tm);
+	if (rc < 0) {
+		dev_err(dev, "Unamble to read RTC time\n");
+		return -EINVAL;
 	}
 
+	rtc_tm_to_time(&rtc_tm, &secs_rtc);
+	if (secs < secs_rtc) {
+		dev_err(dev, "Trying to set alarm in the past\n");
+		return -EINVAL;
+	}
+
+	value[0] = secs & 0xFF;
+	value[1] = (secs >> 8) & 0xFF;
+	value[2] = (secs >> 16) & 0xFF;
+	value[3] = (secs >> 24) & 0xFF;
+
 	spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags);
 
 	rc = pm8xxx_write_wrapper(rtc_dd, value, rtc_dd->alarm_rw_base,
 							NUM_8_BIT_RTC_REGS);
 	if (rc < 0) {
-		dev_err(dev, "Write to RTC ALARM register failed\n");
+		dev_err(dev, "Write to RTC ALARM registers failed\n");
 		goto rtc_rw_fail;
 	}
 
 	ctrl_reg = rtc_dd->ctrl_reg;
-	ctrl_reg = alarm->enabled ? (ctrl_reg | PM8xxx_RTC_ALARM_ENABLE) :
+	ctrl_reg = (alarm->enabled) ? (ctrl_reg | PM8xxx_RTC_ALARM_ENABLE) :
 					(ctrl_reg & ~PM8xxx_RTC_ALARM_ENABLE);
 
 	rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, 1);
 	if (rc < 0) {
-		dev_err(dev, "Write to RTC control register failed\n");
+		dev_err(dev, "PM8xxx write failed\n");
 		goto rtc_rw_fail;
 	}
 
 	rtc_dd->ctrl_reg = ctrl_reg;
 
 	dev_dbg(dev, "Alarm Set for h:r:s=%d:%d:%d, d/m/y=%d/%d/%d\n",
-				alarm->time.tm_hour, alarm->time.tm_min,
-				alarm->time.tm_sec, alarm->time.tm_mday,
-				alarm->time.tm_mon, alarm->time.tm_year);
+			alarm->time.tm_hour, alarm->time.tm_min,
+			alarm->time.tm_sec, alarm->time.tm_mday,
+			alarm->time.tm_mon, alarm->time.tm_year);
 rtc_rw_fail:
 	spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
 	return rc;
 }
 
-static int pm8xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+static int
+pm8xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 {
 	int rc;
-	u8 value[NUM_8_BIT_RTC_REGS];
+	u8 value[4];
 	unsigned long secs;
 	struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
 
@@ -286,25 +304,28 @@
 		return rc;
 	}
 
-	secs = value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24);
+	secs = value[0] | (value[1] << 8) | (value[2] << 16) | \
+						 (value[3] << 24);
 
 	rtc_time_to_tm(secs, &alarm->time);
 
 	rc = rtc_valid_tm(&alarm->time);
 	if (rc < 0) {
-		dev_err(dev, "Invalid alarm time read from RTC\n");
+		dev_err(dev, "Invalid time read from PM8xxx\n");
 		return rc;
 	}
 
 	dev_dbg(dev, "Alarm set for - h:r:s=%d:%d:%d, d/m/y=%d/%d/%d\n",
-				alarm->time.tm_hour, alarm->time.tm_min,
+		alarm->time.tm_hour, alarm->time.tm_min,
 				alarm->time.tm_sec, alarm->time.tm_mday,
 				alarm->time.tm_mon, alarm->time.tm_year);
 
 	return 0;
 }
 
-static int pm8xxx_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
+
+static int
+pm8xxx_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
 {
 	int rc;
 	unsigned long irq_flags;
@@ -313,12 +334,12 @@
 
 	spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags);
 	ctrl_reg = rtc_dd->ctrl_reg;
-	ctrl_reg = (enable) ? (ctrl_reg | PM8xxx_RTC_ALARM_ENABLE) :
+	ctrl_reg = (enabled) ? (ctrl_reg | PM8xxx_RTC_ALARM_ENABLE) :
 				(ctrl_reg & ~PM8xxx_RTC_ALARM_ENABLE);
 
 	rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, 1);
 	if (rc < 0) {
-		dev_err(dev, "Write to RTC control register failed\n");
+		dev_err(dev, "PM8xxx write failed\n");
 		goto rtc_rw_fail;
 	}
 
@@ -354,8 +375,7 @@
 	rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, 1);
 	if (rc < 0) {
 		spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
-		dev_err(rtc_dd->rtc_dev, "Write to RTC control register "
-								"failed\n");
+		dev_err(rtc_dd->rtc_dev, "PM8xxx write failed!\n");
 		goto rtc_alarm_handled;
 	}
 
@@ -366,8 +386,7 @@
 	rc = pm8xxx_read_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base +
 						PM8XXX_ALARM_CTRL_OFFSET, 1);
 	if (rc < 0) {
-		dev_err(rtc_dd->rtc_dev, "RTC Alarm control register read "
-								"failed\n");
+		dev_err(rtc_dd->rtc_dev, "PM8xxx write failed!\n");
 		goto rtc_alarm_handled;
 	}
 
@@ -375,8 +394,7 @@
 	rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base +
 						PM8XXX_ALARM_CTRL_OFFSET, 1);
 	if (rc < 0)
-		dev_err(rtc_dd->rtc_dev, "Write to RTC Alarm control register"
-								" failed\n");
+		dev_err(rtc_dd->rtc_dev, "PM8xxx write failed!\n");
 
 rtc_alarm_handled:
 	return IRQ_HANDLED;
@@ -390,7 +408,7 @@
 	struct pm8xxx_rtc *rtc_dd;
 	struct resource *rtc_resource;
 	const struct pm8xxx_rtc_platform_data *pdata =
-						dev_get_platdata(&pdev->dev);
+		pdev->dev.platform_data;
 
 	if (pdata != NULL)
 		rtc_write_enable = pdata->rtc_write_enable;
@@ -401,7 +419,7 @@
 		return -ENOMEM;
 	}
 
-	/* Initialise spinlock to protect RTC control register */
+	/* Initialise spinlock to protect RTC cntrol register */
 	spin_lock_init(&rtc_dd->ctrl_reg_lock);
 
 	rtc_dd->rtc_alarm_irq = platform_get_irq(pdev, 0);
@@ -426,12 +444,12 @@
 	rtc_dd->rtc_read_base = rtc_dd->rtc_base + PM8XXX_RTC_READ_OFFSET;
 	rtc_dd->alarm_rw_base = rtc_dd->rtc_base + PM8XXX_ALARM_RW_OFFSET;
 
-	rtc_dd->rtc_dev = &pdev->dev;
+	rtc_dd->rtc_dev = &(pdev->dev);
 
 	/* Check if the RTC is on, else turn it on */
 	rc = pm8xxx_read_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, 1);
 	if (rc < 0) {
-		dev_err(&pdev->dev, "RTC control register read failed!\n");
+		dev_err(&pdev->dev, "PM8xxx read failed!\n");
 		goto fail_rtc_enable;
 	}
 
@@ -440,12 +458,19 @@
 		rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base,
 									1);
 		if (rc < 0) {
-			dev_err(&pdev->dev, "Write to RTC control register "
-								"failed\n");
+			dev_err(&pdev->dev, "PM8xxx write failed!\n");
 			goto fail_rtc_enable;
 		}
 	}
 
+	/* Enable abort enable feature */
+	ctrl_reg |= PM8xxx_RTC_ABORT_ENABLE;
+	rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, 1);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "PM8xxx write failed!\n");
+		goto fail_rtc_enable;
+	}
+
 	rtc_dd->ctrl_reg = ctrl_reg;
 	if (rtc_write_enable == true)
 		pm8xxx_rtc_ops.set_time = pm8xxx_rtc_set_time;
@@ -485,20 +510,7 @@
 	return rc;
 }
 
-static int __devexit pm8xxx_rtc_remove(struct platform_device *pdev)
-{
-	struct pm8xxx_rtc *rtc_dd = platform_get_drvdata(pdev);
-
-	device_init_wakeup(&pdev->dev, 0);
-	free_irq(rtc_dd->rtc_alarm_irq, rtc_dd);
-	rtc_device_unregister(rtc_dd->rtc);
-	platform_set_drvdata(pdev, NULL);
-	kfree(rtc_dd);
-
-	return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM
 static int pm8xxx_rtc_resume(struct device *dev)
 {
 	struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
@@ -518,21 +530,87 @@
 
 	return 0;
 }
-#endif
 
-static SIMPLE_DEV_PM_OPS(pm8xxx_rtc_pm_ops, pm8xxx_rtc_suspend, pm8xxx_rtc_resume);
+static const struct dev_pm_ops pm8xxx_rtc_pm_ops = {
+	.suspend = pm8xxx_rtc_suspend,
+	.resume = pm8xxx_rtc_resume,
+};
+#endif
+static int __devexit pm8xxx_rtc_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_rtc *rtc_dd = platform_get_drvdata(pdev);
+
+	device_init_wakeup(&pdev->dev, 0);
+	free_irq(rtc_dd->rtc_alarm_irq, rtc_dd);
+	rtc_device_unregister(rtc_dd->rtc);
+	platform_set_drvdata(pdev, NULL);
+	kfree(rtc_dd);
+
+	return 0;
+}
+
+static void pm8xxx_rtc_shutdown(struct platform_device *pdev)
+{
+	u8 value[4] = {0, 0, 0, 0};
+	u8 reg;
+	int rc;
+	unsigned long irq_flags;
+	bool rtc_alarm_powerup = false;
+	struct pm8xxx_rtc *rtc_dd = platform_get_drvdata(pdev);
+	struct pm8xxx_rtc_platform_data *pdata = pdev->dev.platform_data;
+
+	if (pdata != NULL)
+		rtc_alarm_powerup =  pdata->rtc_alarm_powerup;
+
+	if (!rtc_alarm_powerup) {
+
+		spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags);
+		dev_dbg(&pdev->dev, "Disabling alarm interrupts\n");
+
+		/* Disable RTC alarms */
+		reg = rtc_dd->ctrl_reg;
+		reg &= ~PM8xxx_RTC_ALARM_ENABLE;
+		rc = pm8xxx_write_wrapper(rtc_dd, &reg, rtc_dd->rtc_base, 1);
+		if (rc < 0) {
+			dev_err(rtc_dd->rtc_dev, "PM8xxx write failed\n");
+			goto fail_alarm_disable;
+		}
+
+		/* Clear Alarm register */
+		rc = pm8xxx_write_wrapper(rtc_dd, value,
+				rtc_dd->alarm_rw_base, NUM_8_BIT_RTC_REGS);
+		if (rc < 0)
+			dev_err(rtc_dd->rtc_dev, "PM8xxx write failed\n");
+
+fail_alarm_disable:
+		spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
+	}
+}
 
 static struct platform_driver pm8xxx_rtc_driver = {
 	.probe		= pm8xxx_rtc_probe,
 	.remove		= __devexit_p(pm8xxx_rtc_remove),
+	.shutdown	= pm8xxx_rtc_shutdown,
 	.driver	= {
 		.name	= PM8XXX_RTC_DEV_NAME,
 		.owner	= THIS_MODULE,
+#ifdef CONFIG_PM
 		.pm	= &pm8xxx_rtc_pm_ops,
+#endif
 	},
 };
 
-module_platform_driver(pm8xxx_rtc_driver);
+static int __init pm8xxx_rtc_init(void)
+{
+	return platform_driver_register(&pm8xxx_rtc_driver);
+}
+module_init(pm8xxx_rtc_init);
+
+static void __exit pm8xxx_rtc_exit(void)
+{
+	platform_driver_unregister(&pm8xxx_rtc_driver);
+}
+module_exit(pm8xxx_rtc_exit);
 
 MODULE_ALIAS("platform:rtc-pm8xxx");
 MODULE_DESCRIPTION("PMIC8xxx RTC driver");
diff --git a/drivers/scsi/isci/phy.c b/drivers/scsi/isci/phy.c
index fab3586..ef9c77d 100644
--- a/drivers/scsi/isci/phy.c
+++ b/drivers/scsi/isci/phy.c
@@ -293,6 +293,18 @@
 
 	writel(sp_timeouts, &llr->sas_phy_timeouts);
 
+	sp_timeouts = readl(&iphy->link_layer_registers->sas_phy_timeouts);
+
+	/* Clear the default 0x36 (54us) RATE_CHANGE timeout value. */
+	sp_timeouts &= ~SCU_SAS_PHYTOV_GEN_VAL(RATE_CHANGE, 0xFF);
+
+	/* Set RATE_CHANGE timeout value to 0x3B (59us).  This ensures SCU can
+	 * lock with 3Gb drive when SCU max rate is set to 1.5Gb.
+	 */
+	sp_timeouts |= SCU_SAS_PHYTOV_GEN_VAL(RATE_CHANGE, 0x3B);
+
+	writel(sp_timeouts, &iphy->link_layer_registers->sas_phy_timeouts);
+
 	if (is_a2(ihost->pdev)) {
 		/* Program the max ARB time for the PHY to 700us so we
 		 * inter-operate with the PMC expander which shuts down
diff --git a/drivers/slimbus/Kconfig b/drivers/slimbus/Kconfig
new file mode 100644
index 0000000..a6a068d
--- /dev/null
+++ b/drivers/slimbus/Kconfig
@@ -0,0 +1,19 @@
+#
+# SLIMBUS driver configuration
+#
+menuconfig SLIMBUS
+	bool "Slimbus support"
+	depends on HAS_IOMEM
+	help
+	  Slimbus is standard interface between baseband and
+	  application processors and peripheral components in mobile
+	  terminals.
+
+if SLIMBUS
+config SLIMBUS_MSM_CTRL
+	tristate "Qualcomm Slimbus Master Component"
+	default n
+	help
+	  Select driver for Qualcomm's Slimbus Master Component.
+
+endif
diff --git a/drivers/slimbus/Makefile b/drivers/slimbus/Makefile
new file mode 100644
index 0000000..436822d
--- /dev/null
+++ b/drivers/slimbus/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for kernel slimbus framework.
+#
+obj-$(CONFIG_SLIMBUS)			+= slimbus.o
+obj-$(CONFIG_SLIMBUS_MSM_CTRL)		+= slim-msm-ctrl.o
diff --git a/drivers/slimbus/slim-msm-ctrl.c b/drivers/slimbus/slim-msm-ctrl.c
new file mode 100644
index 0000000..fa9d1df
--- /dev/null
+++ b/drivers/slimbus/slim-msm-ctrl.c
@@ -0,0 +1,2270 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/slimbus/slimbus.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_slimbus.h>
+#include <mach/sps.h>
+
+/* Per spec.max 40 bytes per received message */
+#define SLIM_RX_MSGQ_BUF_LEN	40
+
+#define SLIM_USR_MC_GENERIC_ACK		0x25
+#define SLIM_USR_MC_MASTER_CAPABILITY	0x0
+#define SLIM_USR_MC_REPORT_SATELLITE	0x1
+#define SLIM_USR_MC_ADDR_QUERY		0xD
+#define SLIM_USR_MC_ADDR_REPLY		0xE
+#define SLIM_USR_MC_DEFINE_CHAN		0x20
+#define SLIM_USR_MC_DEF_ACT_CHAN	0x21
+#define SLIM_USR_MC_CHAN_CTRL		0x23
+#define SLIM_USR_MC_RECONFIG_NOW	0x24
+#define SLIM_USR_MC_REQ_BW		0x28
+#define SLIM_USR_MC_CONNECT_SRC		0x2C
+#define SLIM_USR_MC_CONNECT_SINK	0x2D
+#define SLIM_USR_MC_DISCONNECT_PORT	0x2E
+
+/* MSM Slimbus peripheral settings */
+#define MSM_SLIM_PERF_SUMM_THRESHOLD	0x8000
+#define MSM_SLIM_NCHANS			32
+#define MSM_SLIM_NPORTS			24
+#define MSM_SLIM_AUTOSUSPEND		MSEC_PER_SEC
+
+/*
+ * Need enough descriptors to receive present messages from slaves
+ * if received simultaneously. Present message needs 3 descriptors
+ * and this size will ensure around 10 simultaneous reports.
+ */
+#define MSM_SLIM_DESC_NUM		32
+
+#define SLIM_MSG_ASM_FIRST_WORD(l, mt, mc, dt, ad) \
+		((l) | ((mt) << 5) | ((mc) << 8) | ((dt) << 15) | ((ad) << 16))
+
+#define MSM_SLIM_NAME	"msm_slim_ctrl"
+#define SLIM_ROOT_FREQ 24576000
+
+#define MSM_CONCUR_MSG	8
+#define SAT_CONCUR_MSG	8
+#define DEF_WATERMARK	(8 << 1)
+#define DEF_ALIGN	0
+#define DEF_PACK	(1 << 6)
+#define ENABLE_PORT	1
+
+#define DEF_BLKSZ	0
+#define DEF_TRANSZ	0
+
+#define SAT_MAGIC_LSB	0xD9
+#define SAT_MAGIC_MSB	0xC5
+#define SAT_MSG_VER	0x1
+#define SAT_MSG_PROT	0x1
+#define MSM_SAT_SUCCSS	0x20
+#define MSM_MAX_NSATS	2
+#define MSM_MAX_SATCH	32
+
+#define QC_MFGID_LSB	0x2
+#define QC_MFGID_MSB	0x17
+#define QC_CHIPID_SL	0x10
+#define QC_DEVID_SAT1	0x3
+#define QC_DEVID_SAT2	0x4
+#define QC_DEVID_PGD	0x5
+#define QC_MSM_DEVS	5
+
+#define PGD_THIS_EE(r, v) ((v) ? PGD_THIS_EE_V2(r) : PGD_THIS_EE_V1(r))
+#define PGD_PORT(r, p, v) ((v) ? PGD_PORT_V2(r, p) : PGD_PORT_V1(r, p))
+#define CFG_PORT(r, v) ((v) ? CFG_PORT_V2(r) : CFG_PORT_V1(r))
+
+#define PGD_THIS_EE_V2(r) (dev->base + (r ## _V2) + (dev->ee * 0x1000))
+#define PGD_PORT_V2(r, p) (dev->base + (r ## _V2) + ((p) * 0x1000))
+#define CFG_PORT_V2(r) ((r ## _V2))
+/* Component registers */
+enum comp_reg_v2 {
+	COMP_CFG_V2		= 4,
+	COMP_TRUST_CFG_V2	= 0x3000,
+};
+
+/* Manager PGD registers */
+enum pgd_reg_v2 {
+	PGD_CFG_V2		= 0x800,
+	PGD_STAT_V2		= 0x804,
+	PGD_INT_EN_V2		= 0x810,
+	PGD_INT_STAT_V2		= 0x814,
+	PGD_INT_CLR_V2		= 0x818,
+	PGD_OWN_EEn_V2		= 0x300C,
+	PGD_PORT_INT_EN_EEn_V2	= 0x5000,
+	PGD_PORT_INT_ST_EEn_V2	= 0x5004,
+	PGD_PORT_INT_CL_EEn_V2	= 0x5008,
+	PGD_PORT_CFGn_V2	= 0x14000,
+	PGD_PORT_STATn_V2	= 0x14004,
+	PGD_PORT_PARAMn_V2	= 0x14008,
+	PGD_PORT_BLKn_V2	= 0x1400C,
+	PGD_PORT_TRANn_V2	= 0x14010,
+	PGD_PORT_MCHANn_V2	= 0x14014,
+	PGD_PORT_PSHPLLn_V2	= 0x14018,
+	PGD_PORT_PC_CFGn_V2	= 0x8000,
+	PGD_PORT_PC_VALn_V2	= 0x8004,
+	PGD_PORT_PC_VFR_TSn_V2	= 0x8008,
+	PGD_PORT_PC_VFR_STn_V2	= 0x800C,
+	PGD_PORT_PC_VFR_CLn_V2	= 0x8010,
+	PGD_IE_STAT_V2		= 0x820,
+	PGD_VE_STAT_V2		= 0x830,
+};
+
+#define PGD_THIS_EE_V1(r) (dev->base + (r ## _V1) + (dev->ee * 16))
+#define PGD_PORT_V1(r, p) (dev->base + (r ## _V1) + ((p) * 32))
+#define CFG_PORT_V1(r) ((r ## _V1))
+/* Component registers */
+enum comp_reg_v1 {
+	COMP_CFG_V1		= 0,
+	COMP_TRUST_CFG_V1	= 0x14,
+};
+
+/* Manager PGD registers */
+enum pgd_reg_v1 {
+	PGD_CFG_V1		= 0x1000,
+	PGD_STAT_V1		= 0x1004,
+	PGD_INT_EN_V1		= 0x1010,
+	PGD_INT_STAT_V1		= 0x1014,
+	PGD_INT_CLR_V1		= 0x1018,
+	PGD_OWN_EEn_V1		= 0x1020,
+	PGD_PORT_INT_EN_EEn_V1	= 0x1030,
+	PGD_PORT_INT_ST_EEn_V1	= 0x1034,
+	PGD_PORT_INT_CL_EEn_V1	= 0x1038,
+	PGD_PORT_CFGn_V1	= 0x1080,
+	PGD_PORT_STATn_V1	= 0x1084,
+	PGD_PORT_PARAMn_V1	= 0x1088,
+	PGD_PORT_BLKn_V1	= 0x108C,
+	PGD_PORT_TRANn_V1	= 0x1090,
+	PGD_PORT_MCHANn_V1	= 0x1094,
+	PGD_PORT_PSHPLLn_V1	= 0x1098,
+	PGD_PORT_PC_CFGn_V1	= 0x1600,
+	PGD_PORT_PC_VALn_V1	= 0x1604,
+	PGD_PORT_PC_VFR_TSn_V1	= 0x1608,
+	PGD_PORT_PC_VFR_STn_V1	= 0x160C,
+	PGD_PORT_PC_VFR_CLn_V1	= 0x1610,
+	PGD_IE_STAT_V1		= 0x1700,
+	PGD_VE_STAT_V1		= 0x1710,
+};
+
+/* Manager registers */
+enum mgr_reg {
+	MGR_CFG		= 0x200,
+	MGR_STATUS	= 0x204,
+	MGR_RX_MSGQ_CFG	= 0x208,
+	MGR_INT_EN	= 0x210,
+	MGR_INT_STAT	= 0x214,
+	MGR_INT_CLR	= 0x218,
+	MGR_TX_MSG	= 0x230,
+	MGR_RX_MSG	= 0x270,
+	MGR_VE_STAT	= 0x300,
+};
+
+enum msg_cfg {
+	MGR_CFG_ENABLE		= 1,
+	MGR_CFG_RX_MSGQ_EN	= 1 << 1,
+	MGR_CFG_TX_MSGQ_EN_HIGH	= 1 << 2,
+	MGR_CFG_TX_MSGQ_EN_LOW	= 1 << 3,
+};
+/* Message queue types */
+enum msm_slim_msgq_type {
+	MSGQ_RX		= 0,
+	MSGQ_TX_LOW	= 1,
+	MSGQ_TX_HIGH	= 2,
+};
+/* Framer registers */
+enum frm_reg {
+	FRM_CFG		= 0x400,
+	FRM_STAT	= 0x404,
+	FRM_INT_EN	= 0x410,
+	FRM_INT_STAT	= 0x414,
+	FRM_INT_CLR	= 0x418,
+	FRM_WAKEUP	= 0x41C,
+	FRM_CLKCTL_DONE	= 0x420,
+	FRM_IE_STAT	= 0x430,
+	FRM_VE_STAT	= 0x440,
+};
+
+/* Interface registers */
+enum intf_reg {
+	INTF_CFG	= 0x600,
+	INTF_STAT	= 0x604,
+	INTF_INT_EN	= 0x610,
+	INTF_INT_STAT	= 0x614,
+	INTF_INT_CLR	= 0x618,
+	INTF_IE_STAT	= 0x630,
+	INTF_VE_STAT	= 0x640,
+};
+
+enum rsc_grp {
+	EE_MGR_RSC_GRP	= 1 << 10,
+	EE_NGD_2	= 2 << 6,
+	EE_NGD_1	= 0,
+};
+
+enum mgr_intr {
+	MGR_INT_RECFG_DONE	= 1 << 24,
+	MGR_INT_TX_NACKED_2	= 1 << 25,
+	MGR_INT_MSG_BUF_CONTE	= 1 << 26,
+	MGR_INT_RX_MSG_RCVD	= 1 << 30,
+	MGR_INT_TX_MSG_SENT	= 1 << 31,
+};
+
+enum frm_cfg {
+	FRM_ACTIVE	= 1,
+	CLK_GEAR	= 7,
+	ROOT_FREQ	= 11,
+	REF_CLK_GEAR	= 15,
+};
+
+enum msm_ctrl_state {
+	MSM_CTRL_AWAKE,
+	MSM_CTRL_SLEEPING,
+	MSM_CTRL_ASLEEP,
+};
+
+struct msm_slim_sps_bam {
+	u32			hdl;
+	void __iomem		*base;
+	int			irq;
+};
+
+struct msm_slim_endp {
+	struct sps_pipe			*sps;
+	struct sps_connect		config;
+	struct sps_register_event	event;
+	struct sps_mem_buffer		buf;
+	struct completion		*xcomp;
+	bool				connected;
+};
+
+struct msm_slim_ctrl {
+	struct slim_controller  ctrl;
+	struct slim_framer	framer;
+	struct device		*dev;
+	void __iomem		*base;
+	struct resource		*slew_mem;
+	u32			curr_bw;
+	u8			msg_cnt;
+	u32			tx_buf[10];
+	u8			rx_msgs[MSM_CONCUR_MSG][SLIM_RX_MSGQ_BUF_LEN];
+	spinlock_t		rx_lock;
+	int			head;
+	int			tail;
+	int			irq;
+	int			err;
+	int			ee;
+	struct completion	*wr_comp;
+	struct msm_slim_sat	*satd[MSM_MAX_NSATS];
+	struct msm_slim_endp	pipes[7];
+	struct msm_slim_sps_bam	bam;
+	struct msm_slim_endp	rx_msgq;
+	struct completion	rx_msgq_notify;
+	struct task_struct	*rx_msgq_thread;
+	struct clk		*rclk;
+	struct mutex		tx_lock;
+	u8			pgdla;
+	bool			use_rx_msgqs;
+	int			pipe_b;
+	struct completion	reconf;
+	bool			reconf_busy;
+	bool			chan_active;
+	enum msm_ctrl_state	state;
+	int			nsats;
+	u32			ver;
+};
+
+struct msm_sat_chan {
+	u8 chan;
+	u16 chanh;
+	int req_rem;
+	int req_def;
+};
+
+struct msm_slim_sat {
+	struct slim_device	satcl;
+	struct msm_slim_ctrl	*dev;
+	struct workqueue_struct *wq;
+	struct work_struct	wd;
+	u8			sat_msgs[SAT_CONCUR_MSG][40];
+	struct msm_sat_chan	*satch;
+	u8			nsatch;
+	bool			sent_capability;
+	bool			pending_reconf;
+	bool			pending_capability;
+	int			shead;
+	int			stail;
+	spinlock_t lock;
+};
+
+static struct msm_slim_sat *msm_slim_alloc_sat(struct msm_slim_ctrl *dev);
+
+static int msm_slim_rx_enqueue(struct msm_slim_ctrl *dev, u32 *buf, u8 len)
+{
+	spin_lock(&dev->rx_lock);
+	if ((dev->tail + 1) % MSM_CONCUR_MSG == dev->head) {
+		spin_unlock(&dev->rx_lock);
+		dev_err(dev->dev, "RX QUEUE full!");
+		return -EXFULL;
+	}
+	memcpy((u8 *)dev->rx_msgs[dev->tail], (u8 *)buf, len);
+	dev->tail = (dev->tail + 1) % MSM_CONCUR_MSG;
+	spin_unlock(&dev->rx_lock);
+	return 0;
+}
+
+static int msm_slim_rx_dequeue(struct msm_slim_ctrl *dev, u8 *buf)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&dev->rx_lock, flags);
+	if (dev->tail == dev->head) {
+		spin_unlock_irqrestore(&dev->rx_lock, flags);
+		return -ENODATA;
+	}
+	memcpy(buf, (u8 *)dev->rx_msgs[dev->head], 40);
+	dev->head = (dev->head + 1) % MSM_CONCUR_MSG;
+	spin_unlock_irqrestore(&dev->rx_lock, flags);
+	return 0;
+}
+
+static int msm_sat_enqueue(struct msm_slim_sat *sat, u32 *buf, u8 len)
+{
+	struct msm_slim_ctrl *dev = sat->dev;
+	spin_lock(&sat->lock);
+	if ((sat->stail + 1) % SAT_CONCUR_MSG == sat->shead) {
+		spin_unlock(&sat->lock);
+		dev_err(dev->dev, "SAT QUEUE full!");
+		return -EXFULL;
+	}
+	memcpy(sat->sat_msgs[sat->stail], (u8 *)buf, len);
+	sat->stail = (sat->stail + 1) % SAT_CONCUR_MSG;
+	spin_unlock(&sat->lock);
+	return 0;
+}
+
+static int msm_sat_dequeue(struct msm_slim_sat *sat, u8 *buf)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&sat->lock, flags);
+	if (sat->stail == sat->shead) {
+		spin_unlock_irqrestore(&sat->lock, flags);
+		return -ENODATA;
+	}
+	memcpy(buf, sat->sat_msgs[sat->shead], 40);
+	sat->shead = (sat->shead + 1) % SAT_CONCUR_MSG;
+	spin_unlock_irqrestore(&sat->lock, flags);
+	return 0;
+}
+
+static void msm_get_eaddr(u8 *e_addr, u32 *buffer)
+{
+	e_addr[0] = (buffer[1] >> 24) & 0xff;
+	e_addr[1] = (buffer[1] >> 16) & 0xff;
+	e_addr[2] = (buffer[1] >> 8) & 0xff;
+	e_addr[3] = buffer[1] & 0xff;
+	e_addr[4] = (buffer[0] >> 24) & 0xff;
+	e_addr[5] = (buffer[0] >> 16) & 0xff;
+}
+
+static bool msm_is_sat_dev(u8 *e_addr)
+{
+	if (e_addr[5] == QC_MFGID_LSB && e_addr[4] == QC_MFGID_MSB &&
+		e_addr[2] != QC_CHIPID_SL &&
+		(e_addr[1] == QC_DEVID_SAT1 || e_addr[1] == QC_DEVID_SAT2))
+		return true;
+	return false;
+}
+
+static int msm_slim_get_ctrl(struct msm_slim_ctrl *dev)
+{
+#ifdef CONFIG_PM_RUNTIME
+	int ref = 0;
+	int ret = pm_runtime_get_sync(dev->dev);
+	if (ret >= 0) {
+		ref = atomic_read(&dev->dev->power.usage_count);
+		if (ref <= 0) {
+			dev_err(dev->dev, "reference count -ve:%d", ref);
+			ret = -ENODEV;
+		}
+	}
+	return ret;
+#else
+	return -ENODEV;
+#endif
+}
+static void msm_slim_put_ctrl(struct msm_slim_ctrl *dev)
+{
+#ifdef CONFIG_PM_RUNTIME
+	int ref;
+	pm_runtime_mark_last_busy(dev->dev);
+	ref = atomic_read(&dev->dev->power.usage_count);
+	if (ref <= 0)
+		dev_err(dev->dev, "reference count mismatch:%d", ref);
+	else
+		pm_runtime_put(dev->dev);
+#endif
+}
+
+static struct msm_slim_sat *addr_to_sat(struct msm_slim_ctrl *dev, u8 laddr)
+{
+	struct msm_slim_sat *sat = NULL;
+	int i = 0;
+	while (!sat && i < dev->nsats) {
+		if (laddr == dev->satd[i]->satcl.laddr)
+			sat = dev->satd[i];
+		i++;
+	}
+	return sat;
+}
+
+static irqreturn_t msm_slim_interrupt(int irq, void *d)
+{
+	struct msm_slim_ctrl *dev = d;
+	u32 pstat;
+	u32 stat = readl_relaxed(dev->base + MGR_INT_STAT);
+
+	if (stat & MGR_INT_TX_MSG_SENT || stat & MGR_INT_TX_NACKED_2) {
+		if (stat & MGR_INT_TX_MSG_SENT)
+			writel_relaxed(MGR_INT_TX_MSG_SENT,
+					dev->base + MGR_INT_CLR);
+		else {
+			writel_relaxed(MGR_INT_TX_NACKED_2,
+					dev->base + MGR_INT_CLR);
+			dev->err = -EIO;
+		}
+		/*
+		 * Guarantee that interrupt clear bit write goes through before
+		 * signalling completion/exiting ISR
+		 */
+		mb();
+		if (dev->wr_comp)
+			complete(dev->wr_comp);
+	}
+	if (stat & MGR_INT_RX_MSG_RCVD) {
+		u32 rx_buf[10];
+		u32 mc, mt;
+		u8 len, i;
+		rx_buf[0] = readl_relaxed(dev->base + MGR_RX_MSG);
+		len = rx_buf[0] & 0x1F;
+		for (i = 1; i < ((len + 3) >> 2); i++) {
+			rx_buf[i] = readl_relaxed(dev->base + MGR_RX_MSG +
+						(4 * i));
+			dev_dbg(dev->dev, "reading data: %x\n", rx_buf[i]);
+		}
+		mt = (rx_buf[0] >> 5) & 0x7;
+		mc = (rx_buf[0] >> 8) & 0xff;
+		dev_dbg(dev->dev, "MC: %x, MT: %x\n", mc, mt);
+		if (mt == SLIM_MSG_MT_DEST_REFERRED_USER ||
+				mt == SLIM_MSG_MT_SRC_REFERRED_USER) {
+			u8 laddr = (u8)((rx_buf[0] >> 16) & 0xFF);
+			struct msm_slim_sat *sat = addr_to_sat(dev, laddr);
+			if (sat)
+				msm_sat_enqueue(sat, rx_buf, len);
+			else
+				dev_err(dev->dev, "unknown sat:%d message",
+						laddr);
+			writel_relaxed(MGR_INT_RX_MSG_RCVD,
+					dev->base + MGR_INT_CLR);
+			/*
+			 * Guarantee that CLR bit write goes through before
+			 * queuing work
+			 */
+			mb();
+			if (sat)
+				queue_work(sat->wq, &sat->wd);
+		} else if (mt == SLIM_MSG_MT_CORE &&
+			mc == SLIM_MSG_MC_REPORT_PRESENT) {
+			u8 e_addr[6];
+			msm_get_eaddr(e_addr, rx_buf);
+			msm_slim_rx_enqueue(dev, rx_buf, len);
+			writel_relaxed(MGR_INT_RX_MSG_RCVD, dev->base +
+						MGR_INT_CLR);
+			/*
+			 * Guarantee that CLR bit write goes through
+			 * before signalling completion
+			 */
+			mb();
+			complete(&dev->rx_msgq_notify);
+		} else if (mc == SLIM_MSG_MC_REPLY_INFORMATION ||
+				mc == SLIM_MSG_MC_REPLY_VALUE) {
+			msm_slim_rx_enqueue(dev, rx_buf, len);
+			writel_relaxed(MGR_INT_RX_MSG_RCVD, dev->base +
+						MGR_INT_CLR);
+			/*
+			 * Guarantee that CLR bit write goes through
+			 * before signalling completion
+			 */
+			mb();
+			complete(&dev->rx_msgq_notify);
+		} else if (mc == SLIM_MSG_MC_REPORT_INFORMATION) {
+			u8 *buf = (u8 *)rx_buf;
+			u8 l_addr = buf[2];
+			u16 ele = (u16)buf[4] << 4;
+			ele |= ((buf[3] & 0xf0) >> 4);
+			dev_err(dev->dev, "Slim-dev:%d report inf element:0x%x",
+					l_addr, ele);
+			for (i = 0; i < len - 5; i++)
+				dev_err(dev->dev, "offset:0x%x:bit mask:%x",
+						i, buf[i+5]);
+			writel_relaxed(MGR_INT_RX_MSG_RCVD, dev->base +
+						MGR_INT_CLR);
+			/*
+			 * Guarantee that CLR bit write goes through
+			 * before exiting
+			 */
+			mb();
+		} else {
+			dev_err(dev->dev, "Unexpected MC,%x MT:%x, len:%d",
+						mc, mt, len);
+			for (i = 0; i < ((len + 3) >> 2); i++)
+				dev_err(dev->dev, "error msg: %x", rx_buf[i]);
+			writel_relaxed(MGR_INT_RX_MSG_RCVD, dev->base +
+						MGR_INT_CLR);
+			/*
+			 * Guarantee that CLR bit write goes through
+			 * before exiting
+			 */
+			mb();
+		}
+	}
+	if (stat & MGR_INT_RECFG_DONE) {
+		writel_relaxed(MGR_INT_RECFG_DONE, dev->base + MGR_INT_CLR);
+		/*
+		 * Guarantee that CLR bit write goes through
+		 * before exiting ISR
+		 */
+		mb();
+		complete(&dev->reconf);
+	}
+	pstat = readl_relaxed(PGD_THIS_EE(PGD_PORT_INT_ST_EEn, dev->ver));
+	if (pstat != 0) {
+		int i = 0;
+		for (i = dev->pipe_b; i < MSM_SLIM_NPORTS; i++) {
+			if (pstat & 1 << i) {
+				u32 val = readl_relaxed(PGD_PORT(PGD_PORT_STATn,
+							i, dev->ver));
+				if (val & (1 << 19)) {
+					dev->ctrl.ports[i].err =
+						SLIM_P_DISCONNECT;
+					dev->pipes[i-dev->pipe_b].connected =
+							false;
+					/*
+					 * SPS will call completion since
+					 * ERROR flags are registered
+					 */
+				} else if (val & (1 << 2))
+					dev->ctrl.ports[i].err =
+							SLIM_P_OVERFLOW;
+				else if (val & (1 << 3))
+					dev->ctrl.ports[i].err =
+						SLIM_P_UNDERFLOW;
+			}
+			writel_relaxed(1, PGD_THIS_EE(PGD_PORT_INT_CL_EEn,
+							dev->ver));
+		}
+		/*
+		 * Guarantee that port interrupt bit(s) clearing writes go
+		 * through before exiting ISR
+		 */
+		mb();
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int
+msm_slim_init_endpoint(struct msm_slim_ctrl *dev, struct msm_slim_endp *ep)
+{
+	int ret;
+	struct sps_pipe *endpoint;
+	struct sps_connect *config = &ep->config;
+
+	/* Allocate the endpoint */
+	endpoint = sps_alloc_endpoint();
+	if (!endpoint) {
+		dev_err(dev->dev, "sps_alloc_endpoint failed\n");
+		return -ENOMEM;
+	}
+
+	/* Get default connection configuration for an endpoint */
+	ret = sps_get_config(endpoint, config);
+	if (ret) {
+		dev_err(dev->dev, "sps_get_config failed 0x%x\n", ret);
+		goto sps_config_failed;
+	}
+
+	ep->sps = endpoint;
+	return 0;
+
+sps_config_failed:
+	sps_free_endpoint(endpoint);
+	return ret;
+}
+
+static void
+msm_slim_free_endpoint(struct msm_slim_endp *ep)
+{
+	sps_free_endpoint(ep->sps);
+	ep->sps = NULL;
+}
+
+static int msm_slim_sps_mem_alloc(
+		struct msm_slim_ctrl *dev, struct sps_mem_buffer *mem, u32 len)
+{
+	dma_addr_t phys;
+
+	mem->size = len;
+	mem->min_size = 0;
+	mem->base = dma_alloc_coherent(dev->dev, mem->size, &phys, GFP_KERNEL);
+
+	if (!mem->base) {
+		dev_err(dev->dev, "dma_alloc_coherent(%d) failed\n", len);
+		return -ENOMEM;
+	}
+
+	mem->phys_base = phys;
+	memset(mem->base, 0x00, mem->size);
+	return 0;
+}
+
+static void
+msm_slim_sps_mem_free(struct msm_slim_ctrl *dev, struct sps_mem_buffer *mem)
+{
+	dma_free_coherent(dev->dev, mem->size, mem->base, mem->phys_base);
+	mem->size = 0;
+	mem->base = NULL;
+	mem->phys_base = 0;
+}
+
+static void msm_hw_set_port(struct msm_slim_ctrl *dev, u8 pn)
+{
+	u32 set_cfg = DEF_WATERMARK | DEF_ALIGN | DEF_PACK | ENABLE_PORT;
+	u32 int_port = readl_relaxed(PGD_THIS_EE(PGD_PORT_INT_EN_EEn,
+					dev->ver));
+	writel_relaxed(set_cfg, PGD_PORT(PGD_PORT_CFGn, pn, dev->ver));
+	writel_relaxed(DEF_BLKSZ, PGD_PORT(PGD_PORT_BLKn, pn, dev->ver));
+	writel_relaxed(DEF_TRANSZ, PGD_PORT(PGD_PORT_TRANn, pn, dev->ver));
+	writel_relaxed((int_port | 1 << pn) , PGD_THIS_EE(PGD_PORT_INT_EN_EEn,
+								dev->ver));
+	/* Make sure that port registers are updated before returning */
+	mb();
+}
+
+static int msm_slim_connect_pipe_port(struct msm_slim_ctrl *dev, u8 pn)
+{
+	struct msm_slim_endp *endpoint = &dev->pipes[pn];
+	struct sps_connect *cfg = &endpoint->config;
+	u32 stat;
+	int ret = sps_get_config(dev->pipes[pn].sps, cfg);
+	if (ret) {
+		dev_err(dev->dev, "sps pipe-port get config error%x\n", ret);
+		return ret;
+	}
+	cfg->options = SPS_O_DESC_DONE | SPS_O_ERROR |
+				SPS_O_ACK_TRANSFERS | SPS_O_AUTO_ENABLE;
+
+	if (dev->pipes[pn].connected) {
+		ret = sps_set_config(dev->pipes[pn].sps, cfg);
+		if (ret) {
+			dev_err(dev->dev, "sps pipe-port set config erro:%x\n",
+						ret);
+			return ret;
+		}
+	}
+
+	stat = readl_relaxed(PGD_PORT(PGD_PORT_STATn, (pn + dev->pipe_b),
+					dev->ver));
+	if (dev->ctrl.ports[pn].flow == SLIM_SRC) {
+		cfg->destination = dev->bam.hdl;
+		cfg->source = SPS_DEV_HANDLE_MEM;
+		cfg->dest_pipe_index = ((stat & (0xFF << 4)) >> 4);
+		cfg->src_pipe_index = 0;
+		dev_dbg(dev->dev, "flow src:pipe num:%d",
+					cfg->dest_pipe_index);
+		cfg->mode = SPS_MODE_DEST;
+	} else {
+		cfg->source = dev->bam.hdl;
+		cfg->destination = SPS_DEV_HANDLE_MEM;
+		cfg->src_pipe_index = ((stat & (0xFF << 4)) >> 4);
+		cfg->dest_pipe_index = 0;
+		dev_dbg(dev->dev, "flow dest:pipe num:%d",
+					cfg->src_pipe_index);
+		cfg->mode = SPS_MODE_SRC;
+	}
+	/* Space for desciptor FIFOs */
+	cfg->desc.size = MSM_SLIM_DESC_NUM * sizeof(struct sps_iovec);
+	cfg->config = SPS_CONFIG_DEFAULT;
+	ret = sps_connect(dev->pipes[pn].sps, cfg);
+	if (!ret) {
+		dev->pipes[pn].connected = true;
+		msm_hw_set_port(dev, pn + dev->pipe_b);
+	}
+	return ret;
+}
+
+static u32 *msm_get_msg_buf(struct slim_controller *ctrl, int len)
+{
+	struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+	/*
+	 * Currently we block a transaction until the current one completes.
+	 * In case we need multiple transactions, use message Q
+	 */
+	return dev->tx_buf;
+}
+
+static int msm_send_msg_buf(struct slim_controller *ctrl, u32 *buf, u8 len)
+{
+	int i;
+	struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+	for (i = 0; i < (len + 3) >> 2; i++) {
+		dev_dbg(dev->dev, "TX data:0x%x\n", buf[i]);
+		writel_relaxed(buf[i], dev->base + MGR_TX_MSG + (i * 4));
+	}
+	/* Guarantee that message is sent before returning */
+	mb();
+	return 0;
+}
+
+static int msm_xfer_msg(struct slim_controller *ctrl, struct slim_msg_txn *txn)
+{
+	DECLARE_COMPLETION_ONSTACK(done);
+	struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+	u32 *pbuf;
+	u8 *puc;
+	int timeout;
+	int msgv = -1;
+	u8 la = txn->la;
+	u8 mc = (u8)(txn->mc & 0xFF);
+	/*
+	 * Voting for runtime PM: Slimbus has 2 possible use cases:
+	 * 1. messaging
+	 * 2. Data channels
+	 * Messaging case goes through messaging slots and data channels
+	 * use their own slots
+	 * This "get" votes for messaging bandwidth
+	 */
+	if (!(txn->mc & SLIM_MSG_CLK_PAUSE_SEQ_FLG))
+		msgv = msm_slim_get_ctrl(dev);
+	mutex_lock(&dev->tx_lock);
+	if (dev->state == MSM_CTRL_ASLEEP ||
+		((!(txn->mc & SLIM_MSG_CLK_PAUSE_SEQ_FLG)) &&
+		dev->state == MSM_CTRL_SLEEPING)) {
+		dev_err(dev->dev, "runtime or system PM suspended state");
+		mutex_unlock(&dev->tx_lock);
+		if (msgv >= 0)
+			msm_slim_put_ctrl(dev);
+		return -EBUSY;
+	}
+	if (txn->mt == SLIM_MSG_MT_CORE &&
+		mc == SLIM_MSG_MC_BEGIN_RECONFIGURATION) {
+		if (dev->reconf_busy) {
+			wait_for_completion(&dev->reconf);
+			dev->reconf_busy = false;
+		}
+		/* This "get" votes for data channels */
+		if (dev->ctrl.sched.usedslots != 0 &&
+			!dev->chan_active) {
+			int chv = msm_slim_get_ctrl(dev);
+			if (chv >= 0)
+				dev->chan_active = true;
+		}
+	}
+	txn->rl--;
+	pbuf = msm_get_msg_buf(ctrl, txn->rl);
+	dev->wr_comp = NULL;
+	dev->err = 0;
+
+	if (txn->dt == SLIM_MSG_DEST_ENUMADDR) {
+		mutex_unlock(&dev->tx_lock);
+		if (msgv >= 0)
+			msm_slim_put_ctrl(dev);
+		return -EPROTONOSUPPORT;
+	}
+	if (txn->mt == SLIM_MSG_MT_CORE && txn->la == 0xFF &&
+		(mc == SLIM_MSG_MC_CONNECT_SOURCE ||
+		 mc == SLIM_MSG_MC_CONNECT_SINK ||
+		 mc == SLIM_MSG_MC_DISCONNECT_PORT))
+		la = dev->pgdla;
+	if (txn->dt == SLIM_MSG_DEST_LOGICALADDR)
+		*pbuf = SLIM_MSG_ASM_FIRST_WORD(txn->rl, txn->mt, mc, 0, la);
+	else
+		*pbuf = SLIM_MSG_ASM_FIRST_WORD(txn->rl, txn->mt, mc, 1, la);
+	if (txn->dt == SLIM_MSG_DEST_LOGICALADDR)
+		puc = ((u8 *)pbuf) + 3;
+	else
+		puc = ((u8 *)pbuf) + 2;
+	if (txn->rbuf)
+		*(puc++) = txn->tid;
+	if ((txn->mt == SLIM_MSG_MT_CORE) &&
+		((mc >= SLIM_MSG_MC_REQUEST_INFORMATION &&
+		mc <= SLIM_MSG_MC_REPORT_INFORMATION) ||
+		(mc >= SLIM_MSG_MC_REQUEST_VALUE &&
+		 mc <= SLIM_MSG_MC_CHANGE_VALUE))) {
+		*(puc++) = (txn->ec & 0xFF);
+		*(puc++) = (txn->ec >> 8)&0xFF;
+	}
+	if (txn->wbuf)
+		memcpy(puc, txn->wbuf, txn->len);
+	if (txn->mt == SLIM_MSG_MT_CORE && txn->la == 0xFF &&
+		(mc == SLIM_MSG_MC_CONNECT_SOURCE ||
+		 mc == SLIM_MSG_MC_CONNECT_SINK ||
+		 mc == SLIM_MSG_MC_DISCONNECT_PORT)) {
+		if (mc != SLIM_MSG_MC_DISCONNECT_PORT)
+			dev->err = msm_slim_connect_pipe_port(dev, *puc);
+		else {
+			struct msm_slim_endp *endpoint = &dev->pipes[*puc];
+			struct sps_register_event sps_event;
+			memset(&sps_event, 0, sizeof(sps_event));
+			sps_register_event(endpoint->sps, &sps_event);
+			sps_disconnect(endpoint->sps);
+			/*
+			 * Remove channel disconnects master-side ports from
+			 * channel. No need to send that again on the bus
+			 */
+			dev->pipes[*puc].connected = false;
+			mutex_unlock(&dev->tx_lock);
+			if (msgv >= 0)
+				msm_slim_put_ctrl(dev);
+			return 0;
+		}
+		if (dev->err) {
+			dev_err(dev->dev, "pipe-port connect err:%d", dev->err);
+			mutex_unlock(&dev->tx_lock);
+			if (msgv >= 0)
+				msm_slim_put_ctrl(dev);
+			return dev->err;
+		}
+		*(puc) = *(puc) + dev->pipe_b;
+	}
+	if (txn->mt == SLIM_MSG_MT_CORE &&
+		mc == SLIM_MSG_MC_BEGIN_RECONFIGURATION)
+		dev->reconf_busy = true;
+	dev->wr_comp = &done;
+	msm_send_msg_buf(ctrl, pbuf, txn->rl);
+	timeout = wait_for_completion_timeout(&done, HZ);
+
+	if (mc == SLIM_MSG_MC_RECONFIGURE_NOW) {
+		if ((txn->mc == (SLIM_MSG_MC_RECONFIGURE_NOW |
+					SLIM_MSG_CLK_PAUSE_SEQ_FLG)) &&
+				timeout) {
+			timeout = wait_for_completion_timeout(&dev->reconf, HZ);
+			dev->reconf_busy = false;
+			if (timeout) {
+				clk_disable_unprepare(dev->rclk);
+				disable_irq(dev->irq);
+			}
+		}
+		if ((txn->mc == (SLIM_MSG_MC_RECONFIGURE_NOW |
+					SLIM_MSG_CLK_PAUSE_SEQ_FLG)) &&
+				!timeout) {
+			dev->reconf_busy = false;
+			dev_err(dev->dev, "clock pause failed");
+			mutex_unlock(&dev->tx_lock);
+			return -ETIMEDOUT;
+		}
+		if (txn->mt == SLIM_MSG_MT_CORE &&
+			txn->mc == SLIM_MSG_MC_RECONFIGURE_NOW) {
+			if (dev->ctrl.sched.usedslots == 0 &&
+					dev->chan_active) {
+				dev->chan_active = false;
+				msm_slim_put_ctrl(dev);
+			}
+		}
+	}
+	mutex_unlock(&dev->tx_lock);
+	if (msgv >= 0)
+		msm_slim_put_ctrl(dev);
+
+	if (!timeout)
+		dev_err(dev->dev, "TX timed out:MC:0x%x,mt:0x%x", txn->mc,
+					txn->mt);
+
+	return timeout ? dev->err : -ETIMEDOUT;
+}
+
+static int msm_set_laddr(struct slim_controller *ctrl, const u8 *ea,
+				u8 elen, u8 laddr)
+{
+	struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+	DECLARE_COMPLETION_ONSTACK(done);
+	int timeout;
+	u32 *buf;
+	mutex_lock(&dev->tx_lock);
+	buf = msm_get_msg_buf(ctrl, 9);
+	buf[0] = SLIM_MSG_ASM_FIRST_WORD(9, SLIM_MSG_MT_CORE,
+					SLIM_MSG_MC_ASSIGN_LOGICAL_ADDRESS,
+					SLIM_MSG_DEST_LOGICALADDR,
+					ea[5] | ea[4] << 8);
+	buf[1] = ea[3] | (ea[2] << 8) | (ea[1] << 16) | (ea[0] << 24);
+	buf[2] = laddr;
+
+	dev->wr_comp = &done;
+	msm_send_msg_buf(ctrl, buf, 9);
+	timeout = wait_for_completion_timeout(&done, HZ);
+	mutex_unlock(&dev->tx_lock);
+	return timeout ? dev->err : -ETIMEDOUT;
+}
+
+static int msm_clk_pause_wakeup(struct slim_controller *ctrl)
+{
+	struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+	enable_irq(dev->irq);
+	clk_prepare_enable(dev->rclk);
+	writel_relaxed(1, dev->base + FRM_WAKEUP);
+	/* Make sure framer wakeup write goes through before exiting function */
+	mb();
+	/*
+	 * Workaround: Currently, slave is reporting lost-sync messages
+	 * after slimbus comes out of clock pause.
+	 * Transaction with slave fail before slave reports that message
+	 * Give some time for that report to come
+	 * Slimbus wakes up in clock gear 10 at 24.576MHz. With each superframe
+	 * being 250 usecs, we wait for 20 superframes here to ensure
+	 * we get the message
+	 */
+	usleep_range(5000, 5000);
+	return 0;
+}
+
+static int msm_config_port(struct slim_controller *ctrl, u8 pn)
+{
+	struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+	struct msm_slim_endp *endpoint;
+	int ret = 0;
+	if (ctrl->ports[pn].req == SLIM_REQ_HALF_DUP ||
+		ctrl->ports[pn].req == SLIM_REQ_MULTI_CH)
+		return -EPROTONOSUPPORT;
+	if (pn >= (MSM_SLIM_NPORTS - dev->pipe_b))
+		return -ENODEV;
+
+	endpoint = &dev->pipes[pn];
+	ret = msm_slim_init_endpoint(dev, endpoint);
+	dev_dbg(dev->dev, "sps register bam error code:%x\n", ret);
+	return ret;
+}
+
+static enum slim_port_err msm_slim_port_xfer_status(struct slim_controller *ctr,
+				u8 pn, u8 **done_buf, u32 *done_len)
+{
+	struct msm_slim_ctrl *dev = slim_get_ctrldata(ctr);
+	struct sps_iovec sio;
+	int ret;
+	if (done_len)
+		*done_len = 0;
+	if (done_buf)
+		*done_buf = NULL;
+	if (!dev->pipes[pn].connected)
+		return SLIM_P_DISCONNECT;
+	ret = sps_get_iovec(dev->pipes[pn].sps, &sio);
+	if (!ret) {
+		if (done_len)
+			*done_len = sio.size;
+		if (done_buf)
+			*done_buf = (u8 *)sio.addr;
+	}
+	dev_dbg(dev->dev, "get iovec returned %d\n", ret);
+	return SLIM_P_INPROGRESS;
+}
+
+static int msm_slim_port_xfer(struct slim_controller *ctrl, u8 pn, u8 *iobuf,
+			u32 len, struct completion *comp)
+{
+	struct sps_register_event sreg;
+	int ret;
+	struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+	if (pn >= 7)
+		return -ENODEV;
+
+
+	ctrl->ports[pn].xcomp = comp;
+	sreg.options = (SPS_EVENT_DESC_DONE|SPS_EVENT_ERROR);
+	sreg.mode = SPS_TRIGGER_WAIT;
+	sreg.xfer_done = comp;
+	sreg.callback = NULL;
+	sreg.user = &ctrl->ports[pn];
+	ret = sps_register_event(dev->pipes[pn].sps, &sreg);
+	if (ret) {
+		dev_dbg(dev->dev, "sps register event error:%x\n", ret);
+		return ret;
+	}
+	ret = sps_transfer_one(dev->pipes[pn].sps, (u32)iobuf, len, NULL,
+				SPS_IOVEC_FLAG_INT);
+	dev_dbg(dev->dev, "sps submit xfer error code:%x\n", ret);
+
+	return ret;
+}
+
+static int msm_sat_define_ch(struct msm_slim_sat *sat, u8 *buf, u8 len, u8 mc)
+{
+	struct msm_slim_ctrl *dev = sat->dev;
+	enum slim_ch_control oper;
+	int i;
+	int ret = 0;
+	if (mc == SLIM_USR_MC_CHAN_CTRL) {
+		for (i = 0; i < sat->nsatch; i++) {
+			if (buf[5] == sat->satch[i].chan)
+				break;
+		}
+		if (i >= sat->nsatch)
+			return -ENOTCONN;
+		oper = ((buf[3] & 0xC0) >> 6);
+		/* part of grp. activating/removing 1 will take care of rest */
+		ret = slim_control_ch(&sat->satcl, sat->satch[i].chanh, oper,
+					false);
+		if (!ret) {
+			for (i = 5; i < len; i++) {
+				int j;
+				for (j = 0; j < sat->nsatch; j++) {
+					if (buf[i] == sat->satch[j].chan) {
+						if (oper == SLIM_CH_REMOVE)
+							sat->satch[j].req_rem++;
+						else
+							sat->satch[j].req_def++;
+						break;
+					}
+				}
+			}
+		}
+	} else {
+		u16 chh[40];
+		struct slim_ch prop;
+		u32 exp;
+		u8 coeff, cc;
+		u8 prrate = buf[6];
+		if (len <= 8)
+			return -EINVAL;
+		for (i = 8; i < len; i++) {
+			int j = 0;
+			for (j = 0; j < sat->nsatch; j++) {
+				if (sat->satch[j].chan == buf[i]) {
+					chh[i - 8] = sat->satch[j].chanh;
+					break;
+				}
+			}
+			if (j < sat->nsatch) {
+				u16 dummy;
+				ret = slim_query_ch(&sat->satcl, buf[i],
+							&dummy);
+				if (ret)
+					return ret;
+				if (mc == SLIM_USR_MC_DEF_ACT_CHAN)
+					sat->satch[j].req_def++;
+				continue;
+			}
+			if (sat->nsatch >= MSM_MAX_SATCH)
+				return -EXFULL;
+			ret = slim_query_ch(&sat->satcl, buf[i], &chh[i - 8]);
+			if (ret)
+				return ret;
+			sat->satch[j].chan = buf[i];
+			sat->satch[j].chanh = chh[i - 8];
+			if (mc == SLIM_USR_MC_DEF_ACT_CHAN)
+				sat->satch[j].req_def++;
+			sat->nsatch++;
+		}
+		prop.dataf = (enum slim_ch_dataf)((buf[3] & 0xE0) >> 5);
+		prop.auxf = (enum slim_ch_auxf)((buf[4] & 0xC0) >> 5);
+		prop.baser = SLIM_RATE_4000HZ;
+		if (prrate & 0x8)
+			prop.baser = SLIM_RATE_11025HZ;
+		else
+			prop.baser = SLIM_RATE_4000HZ;
+		prop.prot = (enum slim_ch_proto)(buf[5] & 0x0F);
+		prop.sampleszbits = (buf[4] & 0x1F)*SLIM_CL_PER_SL;
+		exp = (u32)((buf[5] & 0xF0) >> 4);
+		coeff = (buf[4] & 0x20) >> 5;
+		cc = (coeff ? 3 : 1);
+		prop.ratem = cc * (1 << exp);
+		if (i > 9)
+			ret = slim_define_ch(&sat->satcl, &prop, chh, len - 8,
+					true, &chh[0]);
+		else
+			ret = slim_define_ch(&sat->satcl, &prop,
+					&chh[0], 1, false, NULL);
+		dev_dbg(dev->dev, "define sat grp returned:%d", ret);
+		if (ret)
+			return ret;
+
+		/* part of group so activating 1 will take care of rest */
+		if (mc == SLIM_USR_MC_DEF_ACT_CHAN)
+			ret = slim_control_ch(&sat->satcl,
+					chh[0],
+					SLIM_CH_ACTIVATE, false);
+	}
+	return ret;
+}
+
+static void msm_slim_rxwq(struct msm_slim_ctrl *dev)
+{
+	u8 buf[40];
+	u8 mc, mt, len;
+	int i, ret;
+	if ((msm_slim_rx_dequeue(dev, (u8 *)buf)) != -ENODATA) {
+		len = buf[0] & 0x1F;
+		mt = (buf[0] >> 5) & 0x7;
+		mc = buf[1];
+		if (mt == SLIM_MSG_MT_CORE &&
+			mc == SLIM_MSG_MC_REPORT_PRESENT) {
+			u8 laddr;
+			u8 e_addr[6];
+			for (i = 0; i < 6; i++)
+				e_addr[i] = buf[7-i];
+
+			ret = slim_assign_laddr(&dev->ctrl, e_addr, 6, &laddr);
+			/* Is this Qualcomm ported generic device? */
+			if (!ret && e_addr[5] == QC_MFGID_LSB &&
+				e_addr[4] == QC_MFGID_MSB &&
+				e_addr[1] == QC_DEVID_PGD &&
+				e_addr[2] != QC_CHIPID_SL)
+				dev->pgdla = laddr;
+			if (!ret && !pm_runtime_enabled(dev->dev) &&
+				laddr == (QC_MSM_DEVS - 1))
+				pm_runtime_enable(dev->dev);
+
+			if (!ret && msm_is_sat_dev(e_addr)) {
+				struct msm_slim_sat *sat = addr_to_sat(dev,
+								laddr);
+				if (!sat)
+					sat = msm_slim_alloc_sat(dev);
+				if (!sat)
+					return;
+
+				sat->satcl.laddr = laddr;
+				msm_sat_enqueue(sat, (u32 *)buf, len);
+				queue_work(sat->wq, &sat->wd);
+			}
+		} else if (mc == SLIM_MSG_MC_REPLY_INFORMATION ||
+				mc == SLIM_MSG_MC_REPLY_VALUE) {
+			u8 tid = buf[3];
+			dev_dbg(dev->dev, "tid:%d, len:%d\n", tid, len - 4);
+			slim_msg_response(&dev->ctrl, &buf[4], tid,
+						len - 4);
+			pm_runtime_mark_last_busy(dev->dev);
+		} else if (mc == SLIM_MSG_MC_REPORT_INFORMATION) {
+			u8 l_addr = buf[2];
+			u16 ele = (u16)buf[4] << 4;
+			ele |= ((buf[3] & 0xf0) >> 4);
+			dev_err(dev->dev, "Slim-dev:%d report inf element:0x%x",
+					l_addr, ele);
+			for (i = 0; i < len - 5; i++)
+				dev_err(dev->dev, "offset:0x%x:bit mask:%x",
+						i, buf[i+5]);
+		} else {
+			dev_err(dev->dev, "unexpected message:mc:%x, mt:%x",
+					mc, mt);
+			for (i = 0; i < len; i++)
+				dev_err(dev->dev, "error msg: %x", buf[i]);
+
+		}
+	} else
+		dev_err(dev->dev, "rxwq called and no dequeue");
+}
+
+static void slim_sat_rxprocess(struct work_struct *work)
+{
+	struct msm_slim_sat *sat = container_of(work, struct msm_slim_sat, wd);
+	struct msm_slim_ctrl *dev = sat->dev;
+	u8 buf[40];
+
+	while ((msm_sat_dequeue(sat, buf)) != -ENODATA) {
+		struct slim_msg_txn txn;
+		u8 len, mc, mt;
+		u32 bw_sl;
+		int ret = 0;
+		int satv = -1;
+		bool gen_ack = false;
+		u8 tid;
+		u8 wbuf[8];
+		int i;
+		txn.mt = SLIM_MSG_MT_SRC_REFERRED_USER;
+		txn.dt = SLIM_MSG_DEST_LOGICALADDR;
+		txn.ec = 0;
+		txn.rbuf = NULL;
+		txn.la = sat->satcl.laddr;
+		/* satellite handling */
+		len = buf[0] & 0x1F;
+		mc = buf[1];
+		mt = (buf[0] >> 5) & 0x7;
+
+		if (mt == SLIM_MSG_MT_CORE &&
+			mc == SLIM_MSG_MC_REPORT_PRESENT) {
+			u8 laddr;
+			u8 e_addr[6];
+			for (i = 0; i < 6; i++)
+				e_addr[i] = buf[7-i];
+
+			if (pm_runtime_enabled(dev->dev)) {
+				satv = msm_slim_get_ctrl(dev);
+				if (satv >= 0)
+					sat->pending_capability = true;
+			}
+			slim_assign_laddr(&dev->ctrl, e_addr, 6, &laddr);
+			sat->satcl.laddr = laddr;
+			/*
+			 * Since capability message is already sent, present
+			 * message will indicate subsystem hosting this
+			 * satellite has restarted.
+			 * Remove all active channels of this satellite
+			 * when this is detected
+			 */
+			if (sat->sent_capability) {
+				for (i = 0; i < sat->nsatch; i++) {
+					enum slim_ch_state chs =
+						slim_get_ch_state(&sat->satcl,
+							sat->satch[i].chanh);
+					pr_err("Slim-SSR, sat:%d, rm chan:%d",
+							laddr,
+							sat->satch[i].chan);
+					if (chs == SLIM_CH_ACTIVE)
+						slim_control_ch(&sat->satcl,
+							sat->satch[i].chanh,
+							SLIM_CH_REMOVE, true);
+				}
+			}
+		} else if (mt != SLIM_MSG_MT_CORE &&
+				mc != SLIM_MSG_MC_REPORT_PRESENT) {
+			satv = msm_slim_get_ctrl(dev);
+		}
+		switch (mc) {
+		case SLIM_MSG_MC_REPORT_PRESENT:
+			/* Remove runtime_pm vote once satellite acks */
+			if (mt != SLIM_MSG_MT_CORE) {
+				if (pm_runtime_enabled(dev->dev) &&
+					sat->pending_capability) {
+					msm_slim_put_ctrl(dev);
+					sat->pending_capability = false;
+				}
+				continue;
+			}
+			/* send a Manager capability msg */
+			if (sat->sent_capability) {
+				if (mt == SLIM_MSG_MT_CORE)
+					goto send_capability;
+				else
+					continue;
+			}
+			ret = slim_add_device(&dev->ctrl, &sat->satcl);
+			if (ret) {
+				dev_err(dev->dev,
+					"Satellite-init failed");
+				continue;
+			}
+			/* Satellite-channels */
+			sat->satch = kzalloc(MSM_MAX_SATCH *
+					sizeof(struct msm_sat_chan),
+					GFP_KERNEL);
+send_capability:
+			txn.mc = SLIM_USR_MC_MASTER_CAPABILITY;
+			txn.mt = SLIM_MSG_MT_SRC_REFERRED_USER;
+			txn.la = sat->satcl.laddr;
+			txn.rl = 8;
+			wbuf[0] = SAT_MAGIC_LSB;
+			wbuf[1] = SAT_MAGIC_MSB;
+			wbuf[2] = SAT_MSG_VER;
+			wbuf[3] = SAT_MSG_PROT;
+			txn.wbuf = wbuf;
+			txn.len = 4;
+			sat->sent_capability = true;
+			msm_xfer_msg(&dev->ctrl, &txn);
+			break;
+		case SLIM_USR_MC_ADDR_QUERY:
+			memcpy(&wbuf[1], &buf[4], 6);
+			ret = slim_get_logical_addr(&sat->satcl,
+					&wbuf[1], 6, &wbuf[7]);
+			if (ret)
+				memset(&wbuf[1], 0, 6);
+			wbuf[0] = buf[3];
+			txn.mc = SLIM_USR_MC_ADDR_REPLY;
+			txn.rl = 12;
+			txn.len = 8;
+			txn.wbuf = wbuf;
+			msm_xfer_msg(&dev->ctrl, &txn);
+			break;
+		case SLIM_USR_MC_DEFINE_CHAN:
+		case SLIM_USR_MC_DEF_ACT_CHAN:
+		case SLIM_USR_MC_CHAN_CTRL:
+			if (mc != SLIM_USR_MC_CHAN_CTRL)
+				tid = buf[7];
+			else
+				tid = buf[4];
+			gen_ack = true;
+			ret = msm_sat_define_ch(sat, buf, len, mc);
+			if (ret) {
+				dev_err(dev->dev,
+					"SAT define_ch returned:%d",
+					ret);
+			}
+			if (!sat->pending_reconf) {
+				int chv = msm_slim_get_ctrl(dev);
+				if (chv >= 0)
+					sat->pending_reconf = true;
+			}
+			break;
+		case SLIM_USR_MC_RECONFIG_NOW:
+			tid = buf[3];
+			gen_ack = true;
+			ret = slim_reconfigure_now(&sat->satcl);
+			for (i = 0; i < sat->nsatch; i++) {
+				struct msm_sat_chan *sch = &sat->satch[i];
+				if (sch->req_rem) {
+					if (!ret)
+						slim_dealloc_ch(&sat->satcl,
+								sch->chanh);
+					sch->req_rem--;
+				} else if (sch->req_def) {
+					if (ret)
+						slim_dealloc_ch(&sat->satcl,
+								sch->chanh);
+					sch->req_def--;
+				}
+			}
+			if (sat->pending_reconf) {
+				msm_slim_put_ctrl(dev);
+				sat->pending_reconf = false;
+			}
+			break;
+		case SLIM_USR_MC_REQ_BW:
+			/* what we get is in SLOTS */
+			bw_sl = (u32)buf[4] << 3 |
+						((buf[3] & 0xE0) >> 5);
+			sat->satcl.pending_msgsl = bw_sl;
+			tid = buf[5];
+			gen_ack = true;
+			break;
+		case SLIM_USR_MC_CONNECT_SRC:
+		case SLIM_USR_MC_CONNECT_SINK:
+			if (mc == SLIM_USR_MC_CONNECT_SRC)
+				txn.mc = SLIM_MSG_MC_CONNECT_SOURCE;
+			else
+				txn.mc = SLIM_MSG_MC_CONNECT_SINK;
+			wbuf[0] = buf[4] & 0x1F;
+			wbuf[1] = buf[5];
+			tid = buf[6];
+			txn.la = buf[3];
+			txn.mt = SLIM_MSG_MT_CORE;
+			txn.rl = 6;
+			txn.len = 2;
+			txn.wbuf = wbuf;
+			gen_ack = true;
+			ret = msm_xfer_msg(&dev->ctrl, &txn);
+			break;
+		case SLIM_USR_MC_DISCONNECT_PORT:
+			txn.mc = SLIM_MSG_MC_DISCONNECT_PORT;
+			wbuf[0] = buf[4] & 0x1F;
+			tid = buf[5];
+			txn.la = buf[3];
+			txn.rl = 5;
+			txn.len = 1;
+			txn.mt = SLIM_MSG_MT_CORE;
+			txn.wbuf = wbuf;
+			gen_ack = true;
+			ret = msm_xfer_msg(&dev->ctrl, &txn);
+		default:
+			break;
+		}
+		if (!gen_ack) {
+			if (mc != SLIM_MSG_MC_REPORT_PRESENT && satv >= 0)
+				msm_slim_put_ctrl(dev);
+			continue;
+		}
+
+		wbuf[0] = tid;
+		if (!ret)
+			wbuf[1] = MSM_SAT_SUCCSS;
+		else
+			wbuf[1] = 0;
+		txn.mc = SLIM_USR_MC_GENERIC_ACK;
+		txn.la = sat->satcl.laddr;
+		txn.rl = 6;
+		txn.len = 2;
+		txn.wbuf = wbuf;
+		txn.mt = SLIM_MSG_MT_SRC_REFERRED_USER;
+		msm_xfer_msg(&dev->ctrl, &txn);
+		if (satv >= 0)
+			msm_slim_put_ctrl(dev);
+	}
+}
+
+static struct msm_slim_sat *msm_slim_alloc_sat(struct msm_slim_ctrl *dev)
+{
+	struct msm_slim_sat *sat;
+	char *name;
+	if (dev->nsats >= MSM_MAX_NSATS)
+		return NULL;
+
+	sat = kzalloc(sizeof(struct msm_slim_sat), GFP_KERNEL);
+	if (!sat) {
+		dev_err(dev->dev, "no memory for satellite");
+		return NULL;
+	}
+	name = kzalloc(SLIMBUS_NAME_SIZE, GFP_KERNEL);
+	if (!name) {
+		dev_err(dev->dev, "no memory for satellite name");
+		kfree(sat);
+		return NULL;
+	}
+	dev->satd[dev->nsats] = sat;
+	sat->dev = dev;
+	snprintf(name, SLIMBUS_NAME_SIZE, "msm_sat%d", dev->nsats);
+	sat->satcl.name = name;
+	spin_lock_init(&sat->lock);
+	INIT_WORK(&sat->wd, slim_sat_rxprocess);
+	sat->wq = create_singlethread_workqueue(sat->satcl.name);
+	if (!sat->wq) {
+		kfree(name);
+		kfree(sat);
+		return NULL;
+	}
+	/*
+	 * Both sats will be allocated from RX thread and RX thread will
+	 * process messages sequentially. No synchronization necessary
+	 */
+	dev->nsats++;
+	return sat;
+}
+
+static void
+msm_slim_rx_msgq_event(struct msm_slim_ctrl *dev, struct sps_event_notify *ev)
+{
+	u32 *buf = ev->data.transfer.user;
+	struct sps_iovec *iovec = &ev->data.transfer.iovec;
+
+	/*
+	 * Note the virtual address needs to be offset by the same index
+	 * as the physical address or just pass in the actual virtual address
+	 * if the sps_mem_buffer is not needed.  Note that if completion is
+	 * used, the virtual address won't be available and will need to be
+	 * calculated based on the offset of the physical address
+	 */
+	if (ev->event_id == SPS_EVENT_DESC_DONE) {
+
+		pr_debug("buf = 0x%p, data = 0x%x\n", buf, *buf);
+
+		pr_debug("iovec = (0x%x 0x%x 0x%x)\n",
+			iovec->addr, iovec->size, iovec->flags);
+
+	} else {
+		dev_err(dev->dev, "%s: unknown event %d\n",
+					__func__, ev->event_id);
+	}
+}
+
+static void msm_slim_rx_msgq_cb(struct sps_event_notify *notify)
+{
+	struct msm_slim_ctrl *dev = (struct msm_slim_ctrl *)notify->user;
+	msm_slim_rx_msgq_event(dev, notify);
+}
+
+/* Queue up Rx message buffer */
+static inline int
+msm_slim_post_rx_msgq(struct msm_slim_ctrl *dev, int ix)
+{
+	int ret;
+	u32 flags = SPS_IOVEC_FLAG_INT;
+	struct msm_slim_endp *endpoint = &dev->rx_msgq;
+	struct sps_mem_buffer *mem = &endpoint->buf;
+	struct sps_pipe *pipe = endpoint->sps;
+
+	/* Rx message queue buffers are 4 bytes in length */
+	u8 *virt_addr = mem->base + (4 * ix);
+	u32 phys_addr = mem->phys_base + (4 * ix);
+
+	pr_debug("index:%d, phys:0x%x, virt:0x%p\n", ix, phys_addr, virt_addr);
+
+	ret = sps_transfer_one(pipe, phys_addr, 4, virt_addr, flags);
+	if (ret)
+		dev_err(dev->dev, "transfer_one() failed 0x%x, %d\n", ret, ix);
+
+	return ret;
+}
+
+static inline int
+msm_slim_rx_msgq_get(struct msm_slim_ctrl *dev, u32 *data, int offset)
+{
+	struct msm_slim_endp *endpoint = &dev->rx_msgq;
+	struct sps_mem_buffer *mem = &endpoint->buf;
+	struct sps_pipe *pipe = endpoint->sps;
+	struct sps_iovec iovec;
+	int index;
+	int ret;
+
+	ret = sps_get_iovec(pipe, &iovec);
+	if (ret) {
+		dev_err(dev->dev, "sps_get_iovec() failed 0x%x\n", ret);
+		goto err_exit;
+	}
+
+	pr_debug("iovec = (0x%x 0x%x 0x%x)\n",
+		iovec.addr, iovec.size, iovec.flags);
+	BUG_ON(iovec.addr < mem->phys_base);
+	BUG_ON(iovec.addr >= mem->phys_base + mem->size);
+
+	/* Calculate buffer index */
+	index = (iovec.addr - mem->phys_base) / 4;
+	*(data + offset) = *((u32 *)mem->base + index);
+
+	pr_debug("buf = 0x%p, data = 0x%x\n", (u32 *)mem->base + index, *data);
+
+	/* Add buffer back to the queue */
+	(void)msm_slim_post_rx_msgq(dev, index);
+
+err_exit:
+	return ret;
+}
+
+static int msm_slim_rx_msgq_thread(void *data)
+{
+	struct msm_slim_ctrl *dev = (struct msm_slim_ctrl *)data;
+	struct completion *notify = &dev->rx_msgq_notify;
+	struct msm_slim_sat *sat = NULL;
+	u32 mc = 0;
+	u32 mt = 0;
+	u32 buffer[10];
+	int index = 0;
+	u8 msg_len = 0;
+	int ret;
+
+	dev_dbg(dev->dev, "rx thread started");
+
+	while (!kthread_should_stop()) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		ret = wait_for_completion_interruptible(notify);
+
+		if (ret)
+			dev_err(dev->dev, "rx thread wait error:%d", ret);
+
+		/* 1 irq notification per message */
+		if (!dev->use_rx_msgqs) {
+			msm_slim_rxwq(dev);
+			continue;
+		}
+
+		ret = msm_slim_rx_msgq_get(dev, buffer, index);
+		if (ret) {
+			dev_err(dev->dev, "rx_msgq_get() failed 0x%x\n", ret);
+			continue;
+		}
+
+		pr_debug("message[%d] = 0x%x\n", index, *buffer);
+
+		/* Decide if we use generic RX or satellite RX */
+		if (index++ == 0) {
+			msg_len = *buffer & 0x1F;
+			pr_debug("Start of new message, len = %d\n", msg_len);
+			mt = (buffer[0] >> 5) & 0x7;
+			mc = (buffer[0] >> 8) & 0xff;
+			dev_dbg(dev->dev, "MC: %x, MT: %x\n", mc, mt);
+			if (mt == SLIM_MSG_MT_DEST_REFERRED_USER ||
+				mt == SLIM_MSG_MT_SRC_REFERRED_USER) {
+				u8 laddr;
+				laddr = (u8)((buffer[0] >> 16) & 0xff);
+				sat = addr_to_sat(dev, laddr);
+			}
+		} else if ((index * 4) >= msg_len) {
+			index = 0;
+			if (sat) {
+				msm_sat_enqueue(sat, buffer, msg_len);
+				queue_work(sat->wq, &sat->wd);
+				sat = NULL;
+			} else {
+				msm_slim_rx_enqueue(dev, buffer, msg_len);
+				msm_slim_rxwq(dev);
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int __devinit msm_slim_init_rx_msgq(struct msm_slim_ctrl *dev)
+{
+	int i, ret;
+	u32 pipe_offset;
+	struct msm_slim_endp *endpoint = &dev->rx_msgq;
+	struct sps_connect *config = &endpoint->config;
+	struct sps_mem_buffer *descr = &config->desc;
+	struct sps_mem_buffer *mem = &endpoint->buf;
+	struct completion *notify = &dev->rx_msgq_notify;
+
+	struct sps_register_event sps_error_event; /* SPS_ERROR */
+	struct sps_register_event sps_descr_event; /* DESCR_DONE */
+
+	init_completion(notify);
+	if (!dev->use_rx_msgqs)
+		goto rx_thread_create;
+
+	/* Allocate the endpoint */
+	ret = msm_slim_init_endpoint(dev, endpoint);
+	if (ret) {
+		dev_err(dev->dev, "init_endpoint failed 0x%x\n", ret);
+		goto sps_init_endpoint_failed;
+	}
+
+	/* Get the pipe indices for the message queues */
+	pipe_offset = (readl_relaxed(dev->base + MGR_STATUS) & 0xfc) >> 2;
+	dev_dbg(dev->dev, "Message queue pipe offset %d\n", pipe_offset);
+
+	config->mode = SPS_MODE_SRC;
+	config->source = dev->bam.hdl;
+	config->destination = SPS_DEV_HANDLE_MEM;
+	config->src_pipe_index = pipe_offset;
+	config->options = SPS_O_DESC_DONE | SPS_O_ERROR |
+				SPS_O_ACK_TRANSFERS | SPS_O_AUTO_ENABLE;
+
+	/* Allocate memory for the FIFO descriptors */
+	ret = msm_slim_sps_mem_alloc(dev, descr,
+				MSM_SLIM_DESC_NUM * sizeof(struct sps_iovec));
+	if (ret) {
+		dev_err(dev->dev, "unable to allocate SPS descriptors\n");
+		goto alloc_descr_failed;
+	}
+
+	ret = sps_connect(endpoint->sps, config);
+	if (ret) {
+		dev_err(dev->dev, "sps_connect failed 0x%x\n", ret);
+		goto sps_connect_failed;
+	}
+
+	/* Register completion for DESC_DONE */
+	init_completion(notify);
+	memset(&sps_descr_event, 0x00, sizeof(sps_descr_event));
+
+	sps_descr_event.mode = SPS_TRIGGER_CALLBACK;
+	sps_descr_event.options = SPS_O_DESC_DONE;
+	sps_descr_event.user = (void *)dev;
+	sps_descr_event.xfer_done = notify;
+
+	ret = sps_register_event(endpoint->sps, &sps_descr_event);
+	if (ret) {
+		dev_err(dev->dev, "sps_connect() failed 0x%x\n", ret);
+		goto sps_reg_event_failed;
+	}
+
+	/* Register callback for errors */
+	memset(&sps_error_event, 0x00, sizeof(sps_error_event));
+	sps_error_event.mode = SPS_TRIGGER_CALLBACK;
+	sps_error_event.options = SPS_O_ERROR;
+	sps_error_event.user = (void *)dev;
+	sps_error_event.callback = msm_slim_rx_msgq_cb;
+
+	ret = sps_register_event(endpoint->sps, &sps_error_event);
+	if (ret) {
+		dev_err(dev->dev, "sps_connect() failed 0x%x\n", ret);
+		goto sps_reg_event_failed;
+	}
+
+	/* Allocate memory for the message buffer(s), N descrs, 4-byte mesg */
+	ret = msm_slim_sps_mem_alloc(dev, mem, MSM_SLIM_DESC_NUM * 4);
+	if (ret) {
+		dev_err(dev->dev, "dma_alloc_coherent failed\n");
+		goto alloc_buffer_failed;
+	}
+
+	/*
+	 * Call transfer_one for each 4-byte buffer
+	 * Use (buf->size/4) - 1 for the number of buffer to post
+	 */
+
+	/* Setup the transfer */
+	for (i = 0; i < (MSM_SLIM_DESC_NUM - 1); i++) {
+		ret = msm_slim_post_rx_msgq(dev, i);
+		if (ret) {
+			dev_err(dev->dev, "post_rx_msgq() failed 0x%x\n", ret);
+			goto sps_transfer_failed;
+		}
+	}
+
+rx_thread_create:
+	/* Fire up the Rx message queue thread */
+	dev->rx_msgq_thread = kthread_run(msm_slim_rx_msgq_thread, dev,
+					MSM_SLIM_NAME "_rx_msgq_thread");
+	if (!dev->rx_msgq_thread) {
+		dev_err(dev->dev, "Failed to start Rx message queue thread\n");
+		/* Tear-down BAMs or return? */
+		if (!dev->use_rx_msgqs)
+			return -EIO;
+		else
+			ret = -EIO;
+	} else
+		return 0;
+
+sps_transfer_failed:
+	msm_slim_sps_mem_free(dev, mem);
+alloc_buffer_failed:
+	memset(&sps_error_event, 0x00, sizeof(sps_error_event));
+	sps_register_event(endpoint->sps, &sps_error_event);
+sps_reg_event_failed:
+	sps_disconnect(endpoint->sps);
+sps_connect_failed:
+	msm_slim_sps_mem_free(dev, descr);
+alloc_descr_failed:
+	msm_slim_free_endpoint(endpoint);
+sps_init_endpoint_failed:
+	dev->use_rx_msgqs = 0;
+	return ret;
+}
+
+/* Registers BAM h/w resource with SPS driver and initializes msgq endpoints */
+static int __devinit
+msm_slim_sps_init(struct msm_slim_ctrl *dev, struct resource *bam_mem)
+{
+	int i, ret;
+	u32 bam_handle;
+	struct sps_bam_props bam_props = {0};
+
+	static struct sps_bam_sec_config_props sec_props = {
+		.ees = {
+			[0] = {		/* LPASS */
+				.vmid = 0,
+				.pipe_mask = 0xFFFF98,
+			},
+			[1] = {		/* Krait Apps */
+				.vmid = 1,
+				.pipe_mask = 0x3F000007,
+			},
+			[2] = {		/* Modem */
+				.vmid = 2,
+				.pipe_mask = 0x00000060,
+			},
+		},
+	};
+
+	if (!dev->use_rx_msgqs)
+		goto init_rx_msgq;
+
+	bam_props.ee = dev->ee;
+	bam_props.virt_addr = dev->bam.base;
+	bam_props.phys_addr = bam_mem->start;
+	bam_props.irq = dev->bam.irq;
+	bam_props.manage = SPS_BAM_MGR_LOCAL;
+	bam_props.summing_threshold = MSM_SLIM_PERF_SUMM_THRESHOLD;
+
+	bam_props.sec_config = SPS_BAM_SEC_DO_CONFIG;
+	bam_props.p_sec_config_props = &sec_props;
+
+	bam_props.options = SPS_O_DESC_DONE | SPS_O_ERROR |
+				SPS_O_ACK_TRANSFERS | SPS_O_AUTO_ENABLE;
+
+	/* First 7 bits are for message Qs */
+	for (i = 7; i < 32; i++) {
+		/* Check what pipes are owned by Apps. */
+		if ((sec_props.ees[dev->ee].pipe_mask >> i) & 0x1)
+			break;
+	}
+	dev->pipe_b = i - 7;
+
+	/* Register the BAM device with the SPS driver */
+	ret = sps_register_bam_device(&bam_props, &bam_handle);
+	if (ret) {
+		dev_err(dev->dev, "disabling BAM: reg-bam failed 0x%x\n", ret);
+		dev->use_rx_msgqs = 0;
+		goto init_rx_msgq;
+	}
+	dev->bam.hdl = bam_handle;
+	dev_dbg(dev->dev, "SLIM BAM registered, handle = 0x%x\n", bam_handle);
+
+init_rx_msgq:
+	ret = msm_slim_init_rx_msgq(dev);
+	if (ret)
+		dev_err(dev->dev, "msm_slim_init_rx_msgq failed 0x%x\n", ret);
+	if (!dev->use_rx_msgqs && bam_handle) {
+		sps_deregister_bam_device(bam_handle);
+		dev->bam.hdl = 0L;
+	}
+	return ret;
+}
+
+static void msm_slim_sps_exit(struct msm_slim_ctrl *dev)
+{
+	if (dev->use_rx_msgqs) {
+		struct msm_slim_endp *endpoint = &dev->rx_msgq;
+		struct sps_connect *config = &endpoint->config;
+		struct sps_mem_buffer *descr = &config->desc;
+		struct sps_mem_buffer *mem = &endpoint->buf;
+		struct sps_register_event sps_event;
+		memset(&sps_event, 0x00, sizeof(sps_event));
+		msm_slim_sps_mem_free(dev, mem);
+		sps_register_event(endpoint->sps, &sps_event);
+		sps_disconnect(endpoint->sps);
+		msm_slim_sps_mem_free(dev, descr);
+		msm_slim_free_endpoint(endpoint);
+		sps_deregister_bam_device(dev->bam.hdl);
+	}
+}
+
+static void msm_slim_prg_slew(struct platform_device *pdev,
+				struct msm_slim_ctrl *dev)
+{
+	struct resource *slew_io;
+	void __iomem *slew_reg;
+	/* SLEW RATE register for this slimbus */
+	dev->slew_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						"slimbus_slew_reg");
+	if (!dev->slew_mem) {
+		dev_dbg(&pdev->dev, "no slimbus slew resource\n");
+		return;
+	}
+	slew_io = request_mem_region(dev->slew_mem->start,
+				resource_size(dev->slew_mem), pdev->name);
+	if (!slew_io) {
+		dev_dbg(&pdev->dev, "slimbus-slew mem claimed\n");
+		dev->slew_mem = NULL;
+		return;
+	}
+
+	slew_reg = ioremap(dev->slew_mem->start, resource_size(dev->slew_mem));
+	if (!slew_reg) {
+		dev_dbg(dev->dev, "slew register mapping failed");
+		release_mem_region(dev->slew_mem->start,
+					resource_size(dev->slew_mem));
+		dev->slew_mem = NULL;
+		return;
+	}
+	writel_relaxed(1, slew_reg);
+	/* Make sure slimbus-slew rate enabling goes through */
+	wmb();
+	iounmap(slew_reg);
+}
+
+static int __devinit msm_slim_probe(struct platform_device *pdev)
+{
+	struct msm_slim_ctrl *dev;
+	int ret;
+	struct resource		*bam_mem, *bam_io;
+	struct resource		*slim_mem, *slim_io;
+	struct resource		*irq, *bam_irq;
+	slim_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						"slimbus_physical");
+	if (!slim_mem) {
+		dev_err(&pdev->dev, "no slimbus physical memory resource\n");
+		return -ENODEV;
+	}
+	slim_io = request_mem_region(slim_mem->start, resource_size(slim_mem),
+					pdev->name);
+	if (!slim_io) {
+		dev_err(&pdev->dev, "slimbus memory already claimed\n");
+		return -EBUSY;
+	}
+
+	bam_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						"slimbus_bam_physical");
+	if (!bam_mem) {
+		dev_err(&pdev->dev, "no slimbus BAM memory resource\n");
+		ret = -ENODEV;
+		goto err_get_res_bam_failed;
+	}
+	bam_io = request_mem_region(bam_mem->start, resource_size(bam_mem),
+					pdev->name);
+	if (!bam_io) {
+		release_mem_region(slim_mem->start, resource_size(slim_mem));
+		dev_err(&pdev->dev, "slimbus BAM memory already claimed\n");
+		ret = -EBUSY;
+		goto err_get_res_bam_failed;
+	}
+	irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+						"slimbus_irq");
+	if (!irq) {
+		dev_err(&pdev->dev, "no slimbus IRQ resource\n");
+		ret = -ENODEV;
+		goto err_get_res_failed;
+	}
+	bam_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+						"slimbus_bam_irq");
+	if (!bam_irq) {
+		dev_err(&pdev->dev, "no slimbus BAM IRQ resource\n");
+		ret = -ENODEV;
+		goto err_get_res_failed;
+	}
+
+	dev = kzalloc(sizeof(struct msm_slim_ctrl), GFP_KERNEL);
+	if (!dev) {
+		dev_err(&pdev->dev, "no memory for MSM slimbus controller\n");
+		ret = -ENOMEM;
+		goto err_get_res_failed;
+	}
+	dev->dev = &pdev->dev;
+	platform_set_drvdata(pdev, dev);
+	slim_set_ctrldata(&dev->ctrl, dev);
+	dev->base = ioremap(slim_mem->start, resource_size(slim_mem));
+	if (!dev->base) {
+		dev_err(&pdev->dev, "IOremap failed\n");
+		ret = -ENOMEM;
+		goto err_ioremap_failed;
+	}
+	dev->bam.base = ioremap(bam_mem->start, resource_size(bam_mem));
+	if (!dev->bam.base) {
+		dev_err(&pdev->dev, "BAM IOremap failed\n");
+		ret = -ENOMEM;
+		goto err_ioremap_bam_failed;
+	}
+	if (pdev->dev.of_node) {
+
+		ret = of_property_read_u32(pdev->dev.of_node, "cell-index",
+					&dev->ctrl.nr);
+		if (ret) {
+			dev_err(&pdev->dev, "Cell index not specified:%d", ret);
+			goto err_of_init_failed;
+		}
+		/* Optional properties */
+		ret = of_property_read_u32(pdev->dev.of_node,
+					"qcom,min-clk-gear", &dev->ctrl.min_cg);
+		ret = of_property_read_u32(pdev->dev.of_node,
+					"qcom,max-clk-gear", &dev->ctrl.max_cg);
+		pr_err("min_cg:%d, max_cg:%d, ret:%d", dev->ctrl.min_cg,
+					dev->ctrl.max_cg, ret);
+	} else {
+		dev->ctrl.nr = pdev->id;
+	}
+	dev->ctrl.nchans = MSM_SLIM_NCHANS;
+	dev->ctrl.nports = MSM_SLIM_NPORTS;
+	dev->ctrl.set_laddr = msm_set_laddr;
+	dev->ctrl.xfer_msg = msm_xfer_msg;
+	dev->ctrl.wakeup =  msm_clk_pause_wakeup;
+	dev->ctrl.config_port = msm_config_port;
+	dev->ctrl.port_xfer = msm_slim_port_xfer;
+	dev->ctrl.port_xfer_status = msm_slim_port_xfer_status;
+	/* Reserve some messaging BW for satellite-apps driver communication */
+	dev->ctrl.sched.pending_msgsl = 30;
+
+	init_completion(&dev->reconf);
+	mutex_init(&dev->tx_lock);
+	spin_lock_init(&dev->rx_lock);
+	dev->ee = 1;
+	dev->use_rx_msgqs = 1;
+	dev->irq = irq->start;
+	dev->bam.irq = bam_irq->start;
+
+	ret = msm_slim_sps_init(dev, bam_mem);
+	if (ret != 0) {
+		dev_err(dev->dev, "error SPS init\n");
+		goto err_sps_init_failed;
+	}
+
+
+	dev->framer.rootfreq = SLIM_ROOT_FREQ >> 3;
+	dev->framer.superfreq =
+		dev->framer.rootfreq / SLIM_CL_PER_SUPERFRAME_DIV8;
+	dev->ctrl.a_framer = &dev->framer;
+	dev->ctrl.clkgear = SLIM_MAX_CLK_GEAR;
+	dev->ctrl.dev.parent = &pdev->dev;
+	dev->ctrl.dev.of_node = pdev->dev.of_node;
+
+	ret = request_irq(dev->irq, msm_slim_interrupt, IRQF_TRIGGER_HIGH,
+				"msm_slim_irq", dev);
+	if (ret) {
+		dev_err(&pdev->dev, "request IRQ failed\n");
+		goto err_request_irq_failed;
+	}
+
+	msm_slim_prg_slew(pdev, dev);
+
+	/* Register with framework before enabling frame, clock */
+	ret = slim_add_numbered_controller(&dev->ctrl);
+	if (ret) {
+		dev_err(dev->dev, "error adding controller\n");
+		goto err_ctrl_failed;
+	}
+
+
+	dev->rclk = clk_get(dev->dev, "core_clk");
+	if (!dev->rclk) {
+		dev_err(dev->dev, "slimbus clock not found");
+		goto err_clk_get_failed;
+	}
+	clk_set_rate(dev->rclk, SLIM_ROOT_FREQ);
+	clk_prepare_enable(dev->rclk);
+
+	dev->ver = readl_relaxed(dev->base);
+	/* Version info in 16 MSbits */
+	dev->ver >>= 16;
+	/* Component register initialization */
+	writel_relaxed(1, dev->base + CFG_PORT(COMP_CFG, dev->ver));
+	writel_relaxed((EE_MGR_RSC_GRP | EE_NGD_2 | EE_NGD_1),
+				dev->base + CFG_PORT(COMP_TRUST_CFG, dev->ver));
+
+	/*
+	 * Manager register initialization
+	 * If RX msg Q is used, disable RX_MSG_RCVD interrupt
+	 */
+	if (dev->use_rx_msgqs)
+		writel_relaxed((MGR_INT_RECFG_DONE | MGR_INT_TX_NACKED_2 |
+			MGR_INT_MSG_BUF_CONTE | /* MGR_INT_RX_MSG_RCVD | */
+			MGR_INT_TX_MSG_SENT), dev->base + MGR_INT_EN);
+	else
+		writel_relaxed((MGR_INT_RECFG_DONE | MGR_INT_TX_NACKED_2 |
+			MGR_INT_MSG_BUF_CONTE | MGR_INT_RX_MSG_RCVD |
+			MGR_INT_TX_MSG_SENT), dev->base + MGR_INT_EN);
+	writel_relaxed(1, dev->base + MGR_CFG);
+	/*
+	 * Framer registers are beyond 1K memory region after Manager and/or
+	 * component registers. Make sure those writes are ordered
+	 * before framer register writes
+	 */
+	wmb();
+
+	/* Framer register initialization */
+	writel_relaxed((0xA << REF_CLK_GEAR) | (0xA << CLK_GEAR) |
+		(1 << ROOT_FREQ) | (1 << FRM_ACTIVE) | 1,
+		dev->base + FRM_CFG);
+	/*
+	 * Make sure that framer wake-up and enabling writes go through
+	 * before any other component is enabled. Framer is responsible for
+	 * clocking the bus and enabling framer first will ensure that other
+	 * devices can report presence when they are enabled
+	 */
+	mb();
+
+	/* Enable RX msg Q */
+	if (dev->use_rx_msgqs)
+		writel_relaxed(MGR_CFG_ENABLE | MGR_CFG_RX_MSGQ_EN,
+					dev->base + MGR_CFG);
+	else
+		writel_relaxed(MGR_CFG_ENABLE, dev->base + MGR_CFG);
+	/*
+	 * Make sure that manager-enable is written through before interface
+	 * device is enabled
+	 */
+	mb();
+	writel_relaxed(1, dev->base + INTF_CFG);
+	/*
+	 * Make sure that interface-enable is written through before enabling
+	 * ported generic device inside MSM manager
+	 */
+	mb();
+	writel_relaxed(1, dev->base + CFG_PORT(PGD_CFG, dev->ver));
+	writel_relaxed(0x3F<<17, dev->base + CFG_PORT(PGD_OWN_EEn, dev->ver) +
+				(4 * dev->ee));
+	/*
+	 * Make sure that ported generic device is enabled and port-EE settings
+	 * are written through before finally enabling the component
+	 */
+	mb();
+
+	writel_relaxed(1, dev->base + CFG_PORT(COMP_CFG, dev->ver));
+	/*
+	 * Make sure that all writes have gone through before exiting this
+	 * function
+	 */
+	mb();
+	if (pdev->dev.of_node)
+		of_register_slim_devices(&dev->ctrl);
+
+	pm_runtime_use_autosuspend(&pdev->dev);
+	pm_runtime_set_autosuspend_delay(&pdev->dev, MSM_SLIM_AUTOSUSPEND);
+	pm_runtime_set_active(&pdev->dev);
+
+	dev_dbg(dev->dev, "MSM SB controller is up!\n");
+	return 0;
+
+err_ctrl_failed:
+	writel_relaxed(0, dev->base + CFG_PORT(COMP_CFG, dev->ver));
+err_clk_get_failed:
+	kfree(dev->satd);
+err_request_irq_failed:
+	msm_slim_sps_exit(dev);
+err_sps_init_failed:
+err_of_init_failed:
+	iounmap(dev->bam.base);
+err_ioremap_bam_failed:
+	iounmap(dev->base);
+err_ioremap_failed:
+	kfree(dev);
+err_get_res_failed:
+	release_mem_region(bam_mem->start, resource_size(bam_mem));
+err_get_res_bam_failed:
+	release_mem_region(slim_mem->start, resource_size(slim_mem));
+	return ret;
+}
+
+static int __devexit msm_slim_remove(struct platform_device *pdev)
+{
+	struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
+	struct resource *bam_mem;
+	struct resource *slim_mem;
+	struct resource *slew_mem = dev->slew_mem;
+	int i;
+	for (i = 0; i < dev->nsats; i++) {
+		struct msm_slim_sat *sat = dev->satd[i];
+		int j;
+		for (j = 0; j < sat->nsatch; j++)
+			slim_dealloc_ch(&sat->satcl, sat->satch[j].chanh);
+		slim_remove_device(&sat->satcl);
+		kfree(sat->satch);
+		destroy_workqueue(sat->wq);
+		kfree(sat->satcl.name);
+		kfree(sat);
+	}
+	pm_runtime_disable(&pdev->dev);
+	pm_runtime_set_suspended(&pdev->dev);
+	free_irq(dev->irq, dev);
+	slim_del_controller(&dev->ctrl);
+	clk_put(dev->rclk);
+	msm_slim_sps_exit(dev);
+	kthread_stop(dev->rx_msgq_thread);
+	iounmap(dev->bam.base);
+	iounmap(dev->base);
+	kfree(dev);
+	bam_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						"slimbus_bam_physical");
+	if (bam_mem)
+		release_mem_region(bam_mem->start, resource_size(bam_mem));
+	if (slew_mem)
+		release_mem_region(slew_mem->start, resource_size(slew_mem));
+	slim_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						"slimbus_physical");
+	if (slim_mem)
+		release_mem_region(slim_mem->start, resource_size(slim_mem));
+	return 0;
+}
+
+#ifdef CONFIG_PM_RUNTIME
+static int msm_slim_runtime_idle(struct device *device)
+{
+	dev_dbg(device, "pm_runtime: idle...\n");
+	pm_request_autosuspend(device);
+	return -EAGAIN;
+}
+#endif
+
+/*
+ * If PM_RUNTIME is not defined, these 2 functions become helper
+ * functions to be called from system suspend/resume. So they are not
+ * inside ifdef CONFIG_PM_RUNTIME
+ */
+#ifdef CONFIG_PM_SLEEP
+static int msm_slim_runtime_suspend(struct device *device)
+{
+	struct platform_device *pdev = to_platform_device(device);
+	struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
+	int ret;
+	dev_dbg(device, "pm_runtime: suspending...\n");
+	dev->state = MSM_CTRL_SLEEPING;
+	ret = slim_ctrl_clk_pause(&dev->ctrl, false, SLIM_CLK_UNSPECIFIED);
+	if (ret) {
+		dev_err(device, "clk pause not entered:%d", ret);
+		dev->state = MSM_CTRL_AWAKE;
+	} else {
+		dev->state = MSM_CTRL_ASLEEP;
+	}
+	return ret;
+}
+
+static int msm_slim_runtime_resume(struct device *device)
+{
+	struct platform_device *pdev = to_platform_device(device);
+	struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
+	int ret = 0;
+	dev_dbg(device, "pm_runtime: resuming...\n");
+	if (dev->state == MSM_CTRL_ASLEEP)
+		ret = slim_ctrl_clk_pause(&dev->ctrl, true, 0);
+	if (ret) {
+		dev_err(device, "clk pause not exited:%d", ret);
+		dev->state = MSM_CTRL_ASLEEP;
+	} else {
+		dev->state = MSM_CTRL_AWAKE;
+	}
+	return ret;
+}
+
+static int msm_slim_suspend(struct device *dev)
+{
+	int ret = 0;
+	if (!pm_runtime_enabled(dev) || !pm_runtime_suspended(dev)) {
+		dev_dbg(dev, "system suspend");
+		ret = msm_slim_runtime_suspend(dev);
+	}
+	if (ret == -EBUSY) {
+		/*
+		* If the clock pause failed due to active channels, there is
+		* a possibility that some audio stream is active during suspend
+		* We dont want to return suspend failure in that case so that
+		* display and relevant components can still go to suspend.
+		* If there is some other error, then it should be passed-on
+		* to system level suspend
+		*/
+		ret = 0;
+	}
+	return ret;
+}
+
+static int msm_slim_resume(struct device *dev)
+{
+	/* If runtime_pm is enabled, this resume shouldn't do anything */
+	if (!pm_runtime_enabled(dev) || !pm_runtime_suspended(dev)) {
+		int ret;
+		dev_dbg(dev, "system resume");
+		ret = msm_slim_runtime_resume(dev);
+		if (!ret) {
+			pm_runtime_mark_last_busy(dev);
+			pm_request_autosuspend(dev);
+		}
+		return ret;
+
+	}
+	return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops msm_slim_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(
+		msm_slim_suspend,
+		msm_slim_resume
+	)
+	SET_RUNTIME_PM_OPS(
+		msm_slim_runtime_suspend,
+		msm_slim_runtime_resume,
+		msm_slim_runtime_idle
+	)
+};
+
+static struct of_device_id msm_slim_dt_match[] = {
+	{
+		.compatible = "qcom,slim-msm",
+	},
+	{}
+};
+
+static struct platform_driver msm_slim_driver = {
+	.probe = msm_slim_probe,
+	.remove = msm_slim_remove,
+	.driver	= {
+		.name = MSM_SLIM_NAME,
+		.owner = THIS_MODULE,
+		.pm = &msm_slim_dev_pm_ops,
+		.of_match_table = msm_slim_dt_match,
+	},
+};
+
+static int msm_slim_init(void)
+{
+	return platform_driver_register(&msm_slim_driver);
+}
+subsys_initcall(msm_slim_init);
+
+static void msm_slim_exit(void)
+{
+	platform_driver_unregister(&msm_slim_driver);
+}
+module_exit(msm_slim_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.1");
+MODULE_DESCRIPTION("MSM Slimbus controller");
+MODULE_ALIAS("platform:msm-slim");
diff --git a/drivers/slimbus/slimbus.c b/drivers/slimbus/slimbus.c
new file mode 100644
index 0000000..24da4d1
--- /dev/null
+++ b/drivers/slimbus/slimbus.c
@@ -0,0 +1,2953 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/completion.h>
+#include <linux/idr.h>
+#include <linux/pm_runtime.h>
+#include <linux/slimbus/slimbus.h>
+
+#define SLIM_PORT_HDL(la, f, p) ((la)<<24 | (f) << 16 | (p))
+
+#define SLIM_HDL_TO_LA(hdl)	((u32)((hdl) & 0xFF000000) >> 24)
+#define SLIM_HDL_TO_FLOW(hdl)	(((u32)(hdl) & 0xFF0000) >> 16)
+#define SLIM_HDL_TO_PORT(hdl)	((u32)(hdl) & 0xFF)
+
+#define SLIM_HDL_TO_CHIDX(hdl)	((u16)(hdl) & 0xFF)
+
+#define SLIM_SLAVE_PORT(p, la)	(((la)<<16) | (p))
+#define SLIM_MGR_PORT(p)	((0xFF << 16) | (p))
+#define SLIM_LA_MANAGER		0xFF
+
+#define SLIM_START_GRP		(1 << 8)
+#define SLIM_END_GRP		(1 << 9)
+
+#define SLIM_MAX_INTR_COEFF_3	(SLIM_SL_PER_SUPERFRAME/3)
+#define SLIM_MAX_INTR_COEFF_1	SLIM_SL_PER_SUPERFRAME
+
+static DEFINE_MUTEX(slim_lock);
+static DEFINE_IDR(ctrl_idr);
+static struct device_type slim_dev_type;
+static struct device_type slim_ctrl_type;
+
+static const struct slim_device_id *slim_match(const struct slim_device_id *id,
+					const struct slim_device *slim_dev)
+{
+	while (id->name[0]) {
+		if (strncmp(slim_dev->name, id->name, SLIMBUS_NAME_SIZE) == 0)
+			return id;
+		id++;
+	}
+	return NULL;
+}
+
+static int slim_device_match(struct device *dev, struct device_driver *driver)
+{
+	struct slim_device *slim_dev;
+	struct slim_driver *drv = to_slim_driver(driver);
+
+	if (dev->type == &slim_dev_type)
+		slim_dev = to_slim_device(dev);
+	else
+		return 0;
+	if (drv->id_table)
+		return slim_match(drv->id_table, slim_dev) != NULL;
+
+	if (driver->name)
+		return strncmp(slim_dev->name, driver->name, SLIMBUS_NAME_SIZE)
+			== 0;
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int slim_legacy_suspend(struct device *dev, pm_message_t mesg)
+{
+	struct slim_device *slim_dev = NULL;
+	struct slim_driver *driver;
+	if (dev->type == &slim_dev_type)
+		slim_dev = to_slim_device(dev);
+
+	if (!slim_dev || !dev->driver)
+		return 0;
+
+	driver = to_slim_driver(dev->driver);
+	if (!driver->suspend)
+		return 0;
+
+	return driver->suspend(slim_dev, mesg);
+}
+
+static int slim_legacy_resume(struct device *dev)
+{
+	struct slim_device *slim_dev = NULL;
+	struct slim_driver *driver;
+	if (dev->type == &slim_dev_type)
+		slim_dev = to_slim_device(dev);
+
+	if (!slim_dev || !dev->driver)
+		return 0;
+
+	driver = to_slim_driver(dev->driver);
+	if (!driver->resume)
+		return 0;
+
+	return driver->resume(slim_dev);
+}
+
+static int slim_pm_suspend(struct device *dev)
+{
+	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+	if (pm)
+		return pm_generic_suspend(dev);
+	else
+		return slim_legacy_suspend(dev, PMSG_SUSPEND);
+}
+
+static int slim_pm_resume(struct device *dev)
+{
+	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+	if (pm)
+		return pm_generic_resume(dev);
+	else
+		return slim_legacy_resume(dev);
+}
+
+#else
+#define slim_pm_suspend		NULL
+#define slim_pm_resume		NULL
+#endif
+
+static const struct dev_pm_ops slimbus_pm = {
+	.suspend = slim_pm_suspend,
+	.resume = slim_pm_resume,
+	SET_RUNTIME_PM_OPS(
+		pm_generic_suspend,
+		pm_generic_resume,
+		pm_generic_runtime_idle
+		)
+};
+struct bus_type slimbus_type = {
+	.name		= "slimbus",
+	.match		= slim_device_match,
+	.pm		= &slimbus_pm,
+};
+EXPORT_SYMBOL_GPL(slimbus_type);
+
+struct device slimbus_dev = {
+	.init_name = "slimbus",
+};
+
+static void __exit slimbus_exit(void)
+{
+	device_unregister(&slimbus_dev);
+	bus_unregister(&slimbus_type);
+}
+
+static int __init slimbus_init(void)
+{
+	int retval;
+
+	retval = bus_register(&slimbus_type);
+	if (!retval)
+		retval = device_register(&slimbus_dev);
+
+	if (retval)
+		bus_unregister(&slimbus_type);
+
+	return retval;
+}
+postcore_initcall(slimbus_init);
+module_exit(slimbus_exit);
+
+static int slim_drv_probe(struct device *dev)
+{
+	const struct slim_driver *sdrv = to_slim_driver(dev->driver);
+
+	if (sdrv->probe)
+		return sdrv->probe(to_slim_device(dev));
+	return -ENODEV;
+}
+
+static int slim_drv_remove(struct device *dev)
+{
+	const struct slim_driver *sdrv = to_slim_driver(dev->driver);
+
+	if (sdrv->remove)
+		return sdrv->remove(to_slim_device(dev));
+	return -ENODEV;
+}
+
+static void slim_drv_shutdown(struct device *dev)
+{
+	const struct slim_driver *sdrv = to_slim_driver(dev->driver);
+
+	if (sdrv->shutdown)
+		sdrv->shutdown(to_slim_device(dev));
+}
+
+/*
+ * slim_driver_register: Client driver registration with slimbus
+ * @drv:Client driver to be associated with client-device.
+ * This API will register the client driver with the slimbus
+ * It is called from the driver's module-init function.
+ */
+int slim_driver_register(struct slim_driver *drv)
+{
+	drv->driver.bus = &slimbus_type;
+	if (drv->probe)
+		drv->driver.probe = slim_drv_probe;
+
+	if (drv->remove)
+		drv->driver.remove = slim_drv_remove;
+
+	if (drv->shutdown)
+		drv->driver.shutdown = slim_drv_shutdown;
+
+	return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(slim_driver_register);
+
+#define slim_ctrl_attr_gr NULL
+
+static void slim_ctrl_release(struct device *dev)
+{
+	struct slim_controller *ctrl = to_slim_controller(dev);
+
+	complete(&ctrl->dev_released);
+}
+
+static struct device_type slim_ctrl_type = {
+	.groups		= slim_ctrl_attr_gr,
+	.release	= slim_ctrl_release,
+};
+
+static struct slim_controller *slim_ctrl_get(struct slim_controller *ctrl)
+{
+	if (!ctrl || !get_device(&ctrl->dev))
+		return NULL;
+
+	return ctrl;
+}
+
+static void slim_ctrl_put(struct slim_controller *ctrl)
+{
+	if (ctrl)
+		put_device(&ctrl->dev);
+}
+
+#define slim_device_attr_gr NULL
+#define slim_device_uevent NULL
+static void slim_dev_release(struct device *dev)
+{
+	struct slim_device *sbdev = to_slim_device(dev);
+	slim_ctrl_put(sbdev->ctrl);
+}
+
+static struct device_type slim_dev_type = {
+	.groups		= slim_device_attr_gr,
+	.uevent		= slim_device_uevent,
+	.release	= slim_dev_release,
+};
+
+/*
+ * slim_add_device: Add a new device without register board info.
+ * @ctrl: Controller to which this device is to be added to.
+ * Called when device doesn't have an explicit client-driver to be probed, or
+ * the client-driver is a module installed dynamically.
+ */
+int slim_add_device(struct slim_controller *ctrl, struct slim_device *sbdev)
+{
+	int ret = 0;
+
+	sbdev->dev.bus = &slimbus_type;
+	sbdev->dev.parent = ctrl->dev.parent;
+	sbdev->dev.type = &slim_dev_type;
+	sbdev->ctrl = ctrl;
+	slim_ctrl_get(ctrl);
+	dev_set_name(&sbdev->dev, "%s", sbdev->name);
+	/* probe slave on this controller */
+	ret = device_register(&sbdev->dev);
+
+	if (ret)
+		return ret;
+
+	mutex_init(&sbdev->sldev_reconf);
+	INIT_LIST_HEAD(&sbdev->mark_define);
+	INIT_LIST_HEAD(&sbdev->mark_suspend);
+	INIT_LIST_HEAD(&sbdev->mark_removal);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(slim_add_device);
+
+struct sbi_boardinfo {
+	struct list_head	list;
+	struct slim_boardinfo	board_info;
+};
+
+static LIST_HEAD(board_list);
+static LIST_HEAD(slim_ctrl_list);
+static DEFINE_MUTEX(board_lock);
+
+/* If controller is not present, only add to boards list */
+static void slim_match_ctrl_to_boardinfo(struct slim_controller *ctrl,
+				struct slim_boardinfo *bi)
+{
+	int ret;
+	if (ctrl->nr != bi->bus_num)
+		return;
+
+	ret = slim_add_device(ctrl, bi->slim_slave);
+	if (ret != 0)
+		dev_err(ctrl->dev.parent, "can't create new device for %s\n",
+			bi->slim_slave->name);
+}
+
+/*
+ * slim_register_board_info: Board-initialization routine.
+ * @info: List of all devices on all controllers present on the board.
+ * @n: number of entries.
+ * API enumerates respective devices on corresponding controller.
+ * Called from board-init function.
+ */
+int slim_register_board_info(struct slim_boardinfo const *info, unsigned n)
+{
+	struct sbi_boardinfo *bi;
+	int i;
+
+	bi = kzalloc(n * sizeof(*bi), GFP_KERNEL);
+	if (!bi)
+		return -ENOMEM;
+
+	for (i = 0; i < n; i++, bi++, info++) {
+		struct slim_controller *ctrl;
+
+		memcpy(&bi->board_info, info, sizeof(*info));
+		mutex_lock(&board_lock);
+		list_add_tail(&bi->list, &board_list);
+		list_for_each_entry(ctrl, &slim_ctrl_list, list)
+			slim_match_ctrl_to_boardinfo(ctrl, &bi->board_info);
+		mutex_unlock(&board_lock);
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(slim_register_board_info);
+
+/*
+ * slim_busnum_to_ctrl: Map bus number to controller
+ * @busnum: Bus number
+ * Returns controller representing this bus number
+ */
+struct slim_controller *slim_busnum_to_ctrl(u32 bus_num)
+{
+	struct slim_controller *ctrl;
+	mutex_lock(&board_lock);
+	list_for_each_entry(ctrl, &slim_ctrl_list, list)
+		if (bus_num == ctrl->nr) {
+			mutex_unlock(&board_lock);
+			return ctrl;
+		}
+	mutex_unlock(&board_lock);
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(slim_busnum_to_ctrl);
+
+static int slim_register_controller(struct slim_controller *ctrl)
+{
+	int ret = 0;
+	struct sbi_boardinfo *bi;
+
+	/* Can't register until after driver model init */
+	if (WARN_ON(!slimbus_type.p)) {
+		ret = -EAGAIN;
+		goto out_list;
+	}
+
+	dev_set_name(&ctrl->dev, "sb-%d", ctrl->nr);
+	ctrl->dev.bus = &slimbus_type;
+	ctrl->dev.type = &slim_ctrl_type;
+	ctrl->num_dev = 0;
+	if (!ctrl->min_cg)
+		ctrl->min_cg = SLIM_MIN_CLK_GEAR;
+	if (!ctrl->max_cg)
+		ctrl->max_cg = SLIM_MAX_CLK_GEAR;
+	mutex_init(&ctrl->m_ctrl);
+	mutex_init(&ctrl->sched.m_reconf);
+	ret = device_register(&ctrl->dev);
+	if (ret)
+		goto out_list;
+
+	dev_dbg(&ctrl->dev, "Bus [%s] registered:dev:%x\n", ctrl->name,
+							(u32)&ctrl->dev);
+
+	if (ctrl->nports) {
+		ctrl->ports = kzalloc(ctrl->nports * sizeof(struct slim_port),
+					GFP_KERNEL);
+		if (!ctrl->ports) {
+			ret = -ENOMEM;
+			goto err_port_failed;
+		}
+	}
+	if (ctrl->nchans) {
+		ctrl->chans = kzalloc(ctrl->nchans * sizeof(struct slim_ich),
+					GFP_KERNEL);
+		if (!ctrl->chans) {
+			ret = -ENOMEM;
+			goto err_chan_failed;
+		}
+
+		ctrl->sched.chc1 =
+			kzalloc(ctrl->nchans * sizeof(struct slim_ich *),
+			GFP_KERNEL);
+		if (!ctrl->sched.chc1) {
+			kfree(ctrl->chans);
+			ret = -ENOMEM;
+			goto err_chan_failed;
+		}
+		ctrl->sched.chc3 =
+			kzalloc(ctrl->nchans * sizeof(struct slim_ich *),
+			GFP_KERNEL);
+		if (!ctrl->sched.chc3) {
+			kfree(ctrl->sched.chc1);
+			kfree(ctrl->chans);
+			ret = -ENOMEM;
+			goto err_chan_failed;
+		}
+	}
+#ifdef DEBUG
+	ctrl->sched.slots = kzalloc(SLIM_SL_PER_SUPERFRAME, GFP_KERNEL);
+#endif
+	init_completion(&ctrl->pause_comp);
+	/*
+	 * If devices on a controller were registered before controller,
+	 * this will make sure that they get probed now that controller is up
+	 */
+	mutex_lock(&board_lock);
+	list_add_tail(&ctrl->list, &slim_ctrl_list);
+	list_for_each_entry(bi, &board_list, list)
+		slim_match_ctrl_to_boardinfo(ctrl, &bi->board_info);
+	mutex_unlock(&board_lock);
+
+	return 0;
+
+err_chan_failed:
+	kfree(ctrl->ports);
+err_port_failed:
+	device_unregister(&ctrl->dev);
+out_list:
+	mutex_lock(&slim_lock);
+	idr_remove(&ctrl_idr, ctrl->nr);
+	mutex_unlock(&slim_lock);
+	return ret;
+}
+
+/* slim_remove_device: Remove the effect of slim_add_device() */
+void slim_remove_device(struct slim_device *sbdev)
+{
+	device_unregister(&sbdev->dev);
+}
+EXPORT_SYMBOL_GPL(slim_remove_device);
+
+static void slim_ctrl_remove_device(struct slim_controller *ctrl,
+				struct slim_boardinfo *bi)
+{
+	if (ctrl->nr == bi->bus_num)
+		slim_remove_device(bi->slim_slave);
+}
+
+/*
+ * slim_del_controller: Controller tear-down.
+ * Controller added with the above API is teared down using this API.
+ */
+int slim_del_controller(struct slim_controller *ctrl)
+{
+	struct slim_controller *found;
+	struct sbi_boardinfo *bi;
+
+	/* First make sure that this bus was added */
+	mutex_lock(&slim_lock);
+	found = idr_find(&ctrl_idr, ctrl->nr);
+	mutex_unlock(&slim_lock);
+	if (found != ctrl)
+		return -EINVAL;
+
+	/* Remove all clients */
+	mutex_lock(&board_lock);
+	list_for_each_entry(bi, &board_list, list)
+		slim_ctrl_remove_device(ctrl, &bi->board_info);
+	mutex_unlock(&board_lock);
+
+	init_completion(&ctrl->dev_released);
+	device_unregister(&ctrl->dev);
+
+	wait_for_completion(&ctrl->dev_released);
+	list_del(&ctrl->list);
+	/* free bus id */
+	mutex_lock(&slim_lock);
+	idr_remove(&ctrl_idr, ctrl->nr);
+	mutex_unlock(&slim_lock);
+
+	kfree(ctrl->sched.chc1);
+	kfree(ctrl->sched.chc3);
+#ifdef DEBUG
+	kfree(ctrl->sched.slots);
+#endif
+	kfree(ctrl->chans);
+	kfree(ctrl->ports);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(slim_del_controller);
+
+/*
+ * slim_add_numbered_controller: Controller bring-up.
+ * @ctrl: Controller to be registered.
+ * A controller is registered with the framework using this API. ctrl->nr is the
+ * desired number with which slimbus framework registers the controller.
+ * Function will return -EBUSY if the number is in use.
+ */
+int slim_add_numbered_controller(struct slim_controller *ctrl)
+{
+	int	id;
+	int	status;
+
+	if (ctrl->nr & ~MAX_ID_MASK)
+		return -EINVAL;
+
+retry:
+	if (idr_pre_get(&ctrl_idr, GFP_KERNEL) == 0)
+		return -ENOMEM;
+
+	mutex_lock(&slim_lock);
+	status = idr_get_new_above(&ctrl_idr, ctrl, ctrl->nr, &id);
+	if (status == 0 && id != ctrl->nr) {
+		status = -EAGAIN;
+		idr_remove(&ctrl_idr, id);
+	}
+	mutex_unlock(&slim_lock);
+	if (status == -EAGAIN)
+		goto retry;
+
+	if (status == 0)
+		status = slim_register_controller(ctrl);
+	return status;
+}
+EXPORT_SYMBOL_GPL(slim_add_numbered_controller);
+
+/*
+ * slim_msg_response: Deliver Message response received from a device to the
+ *	framework.
+ * @ctrl: Controller handle
+ * @reply: Reply received from the device
+ * @len: Length of the reply
+ * @tid: Transaction ID received with which framework can associate reply.
+ * Called by controller to inform framework about the response received.
+ * This helps in making the API asynchronous, and controller-driver doesn't need
+ * to manage 1 more table other than the one managed by framework mapping TID
+ * with buffers
+ */
+void slim_msg_response(struct slim_controller *ctrl, u8 *reply, u8 tid, u8 len)
+{
+	int i;
+	struct slim_msg_txn *txn;
+
+	mutex_lock(&ctrl->m_ctrl);
+	txn = ctrl->txnt[tid];
+	if (txn == NULL) {
+		dev_err(&ctrl->dev, "Got response to invalid TID:%d, len:%d",
+				tid, len);
+		mutex_unlock(&ctrl->m_ctrl);
+		return;
+	}
+	for (i = 0; i < len; i++)
+		txn->rbuf[i] = reply[i];
+	if (txn->comp)
+		complete(txn->comp);
+	ctrl->txnt[tid] = NULL;
+	mutex_unlock(&ctrl->m_ctrl);
+	kfree(txn);
+}
+EXPORT_SYMBOL_GPL(slim_msg_response);
+
+static int slim_processtxn(struct slim_controller *ctrl, u8 dt, u16 mc, u16 ec,
+			u8 mt, u8 *rbuf, const u8 *wbuf, u8 len, u8 mlen,
+			struct completion *comp, u8 la, u8 *tid)
+{
+	u8 i = 0;
+	int ret = 0;
+	struct slim_msg_txn *txn = kmalloc(sizeof(struct slim_msg_txn),
+					GFP_KERNEL);
+	if (!txn)
+		return -ENOMEM;
+	if (tid) {
+		mutex_lock(&ctrl->m_ctrl);
+		for (i = 0; i < ctrl->last_tid; i++) {
+			if (ctrl->txnt[i] == NULL)
+				break;
+		}
+		if (i >= ctrl->last_tid) {
+			if (ctrl->last_tid == 255) {
+				mutex_unlock(&ctrl->m_ctrl);
+				kfree(txn);
+				return -ENOMEM;
+			}
+			ctrl->txnt = krealloc(ctrl->txnt,
+					(i + 1) * sizeof(struct slim_msg_txn *),
+					GFP_KERNEL);
+			if (!ctrl->txnt) {
+				mutex_unlock(&ctrl->m_ctrl);
+				kfree(txn);
+				return -ENOMEM;
+			}
+			ctrl->last_tid++;
+		}
+		ctrl->txnt[i] = txn;
+		mutex_unlock(&ctrl->m_ctrl);
+		txn->tid = i;
+		*tid = i;
+	}
+	txn->mc = mc;
+	txn->mt = mt;
+	txn->dt = dt;
+	txn->ec = ec;
+	txn->la = la;
+	txn->rbuf = rbuf;
+	txn->wbuf = wbuf;
+	txn->rl = mlen;
+	txn->len = len;
+	txn->comp = comp;
+
+	ret = ctrl->xfer_msg(ctrl, txn);
+	if (!tid)
+		kfree(txn);
+	return ret;
+}
+
+static int ctrl_getlogical_addr(struct slim_controller *ctrl, const u8 *eaddr,
+				u8 e_len, u8 *laddr)
+{
+	u8 i;
+	for (i = 0; i < ctrl->num_dev; i++) {
+		if (ctrl->addrt[i].valid &&
+			memcmp(ctrl->addrt[i].eaddr, eaddr, e_len) == 0) {
+			*laddr = i;
+			return 0;
+		}
+	}
+	return -ENXIO;
+}
+
+/*
+ * slim_assign_laddr: Assign logical address to a device enumerated.
+ * @ctrl: Controller with which device is enumerated.
+ * @e_addr: 6-byte elemental address of the device.
+ * @e_len: buffer length for e_addr
+ * @laddr: Return logical address.
+ * Called by controller in response to REPORT_PRESENT. Framework will assign
+ * a logical address to this enumeration address.
+ * Function returns -EXFULL to indicate that all logical addresses are already
+ * taken.
+ */
+int slim_assign_laddr(struct slim_controller *ctrl, const u8 *e_addr,
+				u8 e_len, u8 *laddr)
+{
+	int ret;
+	u8 i;
+	mutex_lock(&ctrl->m_ctrl);
+	/* already assigned */
+	if (ctrl_getlogical_addr(ctrl, e_addr, e_len, laddr) == 0)
+		i = *laddr;
+	else {
+		if (ctrl->num_dev >= 254) {
+			ret = -EXFULL;
+			goto ret_assigned_laddr;
+		}
+		for (i = 0; i < ctrl->num_dev; i++) {
+			if (ctrl->addrt[i].valid == false)
+				break;
+		}
+		if (i == ctrl->num_dev) {
+			ctrl->addrt = krealloc(ctrl->addrt,
+					(ctrl->num_dev + 1) *
+					sizeof(struct slim_addrt),
+					GFP_KERNEL);
+			if (!ctrl->addrt) {
+				ret = -ENOMEM;
+				goto ret_assigned_laddr;
+			}
+			ctrl->num_dev++;
+		}
+		memcpy(ctrl->addrt[i].eaddr, e_addr, e_len);
+		ctrl->addrt[i].valid = true;
+	}
+
+	ret = ctrl->set_laddr(ctrl, ctrl->addrt[i].eaddr, 6, i);
+	if (ret) {
+		ctrl->addrt[i].valid = false;
+		goto ret_assigned_laddr;
+	}
+	*laddr = i;
+
+	dev_dbg(&ctrl->dev, "setting slimbus l-addr:%x\n", i);
+ret_assigned_laddr:
+	mutex_unlock(&ctrl->m_ctrl);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(slim_assign_laddr);
+
+/*
+ * slim_get_logical_addr: Return the logical address of a slimbus device.
+ * @sb: client handle requesting the adddress.
+ * @e_addr: Elemental address of the device.
+ * @e_len: Length of e_addr
+ * @laddr: output buffer to store the address
+ * context: can sleep
+ * -EINVAL is returned in case of invalid parameters, and -ENXIO is returned if
+ *  the device with this elemental address is not found.
+ */
+int slim_get_logical_addr(struct slim_device *sb, const u8 *e_addr,
+				u8 e_len, u8 *laddr)
+{
+	int ret = 0;
+	struct slim_controller *ctrl = sb->ctrl;
+	if (!ctrl || !laddr || !e_addr || e_len != 6)
+		return -EINVAL;
+	mutex_lock(&ctrl->m_ctrl);
+	ret = ctrl_getlogical_addr(ctrl, e_addr, e_len, laddr);
+	mutex_unlock(&ctrl->m_ctrl);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(slim_get_logical_addr);
+
+static int slim_ele_access_sanity(struct slim_ele_access *msg, int oper,
+				u8 *rbuf, const u8 *wbuf, u8 len)
+{
+	if (!msg || msg->num_bytes > 16 || msg->start_offset + len > 0xC00)
+		return -EINVAL;
+	switch (oper) {
+	case SLIM_MSG_MC_REQUEST_VALUE:
+	case SLIM_MSG_MC_REQUEST_INFORMATION:
+		if (rbuf == NULL)
+			return -EINVAL;
+		return 0;
+	case SLIM_MSG_MC_CHANGE_VALUE:
+	case SLIM_MSG_MC_CLEAR_INFORMATION:
+		if (wbuf == NULL)
+			return -EINVAL;
+		return 0;
+	case SLIM_MSG_MC_REQUEST_CHANGE_VALUE:
+	case SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION:
+		if (rbuf == NULL || wbuf == NULL)
+			return -EINVAL;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static u16 slim_slicecodefromsize(u32 req)
+{
+	u8 codetosize[8] = {1, 2, 3, 4, 6, 8, 12, 16};
+	if (req >= 8)
+		return 0;
+	else
+		return codetosize[req];
+}
+
+static u16 slim_slicesize(u32 code)
+{
+	u8 sizetocode[16] = {0, 1, 2, 3, 3, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7};
+	if (code == 0)
+		code = 1;
+	if (code > 16)
+		code = 16;
+	return sizetocode[code - 1];
+}
+
+
+/* Message APIs Unicast message APIs used by slimbus slave drivers */
+
+/*
+ * Message API access routines.
+ * @sb: client handle requesting elemental message reads, writes.
+ * @msg: Input structure for start-offset, number of bytes to read.
+ * @rbuf: data buffer to be filled with values read.
+ * @len: data buffer size
+ * @wbuf: data buffer containing value/information to be written
+ * context: can sleep
+ * Returns:
+ * -EINVAL: Invalid parameters
+ * -ETIMEDOUT: If controller could not complete the request. This may happen if
+ *  the bus lines are not clocked, controller is not powered-on, slave with
+ *  given address is not enumerated/responding.
+ */
+int slim_request_val_element(struct slim_device *sb,
+				struct slim_ele_access *msg, u8 *buf, u8 len)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+	if (!ctrl)
+		return -EINVAL;
+	return slim_xfer_msg(ctrl, sb, msg, SLIM_MSG_MC_REQUEST_VALUE, buf,
+			NULL, len);
+}
+EXPORT_SYMBOL_GPL(slim_request_val_element);
+
+int slim_request_inf_element(struct slim_device *sb,
+				struct slim_ele_access *msg, u8 *buf, u8 len)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+	if (!ctrl)
+		return -EINVAL;
+	return slim_xfer_msg(ctrl, sb, msg, SLIM_MSG_MC_REQUEST_INFORMATION,
+			buf, NULL, len);
+}
+EXPORT_SYMBOL_GPL(slim_request_inf_element);
+
+int slim_change_val_element(struct slim_device *sb, struct slim_ele_access *msg,
+				const u8 *buf, u8 len)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+	if (!ctrl)
+		return -EINVAL;
+	return slim_xfer_msg(ctrl, sb, msg, SLIM_MSG_MC_CHANGE_VALUE, NULL, buf,
+					len);
+}
+EXPORT_SYMBOL_GPL(slim_change_val_element);
+
+int slim_clear_inf_element(struct slim_device *sb, struct slim_ele_access *msg,
+				u8 *buf, u8 len)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+	if (!ctrl)
+		return -EINVAL;
+	return slim_xfer_msg(ctrl, sb, msg, SLIM_MSG_MC_CLEAR_INFORMATION, NULL,
+					buf, len);
+}
+EXPORT_SYMBOL_GPL(slim_clear_inf_element);
+
+int slim_request_change_val_element(struct slim_device *sb,
+					struct slim_ele_access *msg, u8 *rbuf,
+					const u8 *wbuf, u8 len)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+	if (!ctrl)
+		return -EINVAL;
+	return slim_xfer_msg(ctrl, sb, msg, SLIM_MSG_MC_REQUEST_CHANGE_VALUE,
+					rbuf, wbuf, len);
+}
+EXPORT_SYMBOL_GPL(slim_request_change_val_element);
+
+int slim_request_clear_inf_element(struct slim_device *sb,
+					struct slim_ele_access *msg, u8 *rbuf,
+					const u8 *wbuf, u8 len)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+	if (!ctrl)
+		return -EINVAL;
+	return slim_xfer_msg(ctrl, sb, msg,
+					SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION,
+					rbuf, wbuf, len);
+}
+EXPORT_SYMBOL_GPL(slim_request_clear_inf_element);
+
+/*
+ * Broadcast message API:
+ * call this API directly with sbdev = NULL.
+ * For broadcast reads, make sure that buffers are big-enough to incorporate
+ * replies from all logical addresses.
+ * All controllers may not support broadcast
+ */
+int slim_xfer_msg(struct slim_controller *ctrl, struct slim_device *sbdev,
+			struct slim_ele_access *msg, u16 mc, u8 *rbuf,
+			const u8 *wbuf, u8 len)
+{
+	DECLARE_COMPLETION_ONSTACK(complete);
+	int ret;
+	u16 sl, cur;
+	u16 ec;
+	u8 tid, mlen = 6;
+
+	if (sbdev->laddr != SLIM_LA_MANAGER && sbdev->laddr >= ctrl->num_dev)
+		return -ENXIO;
+	ret = slim_ele_access_sanity(msg, mc, rbuf, wbuf, len);
+	if (ret)
+		goto xfer_err;
+
+	sl = slim_slicesize(len);
+	dev_dbg(&ctrl->dev, "SB xfer msg:os:%x, len:%d, MC:%x, sl:%x\n",
+				msg->start_offset, len, mc, sl);
+
+	cur = slim_slicecodefromsize(sl);
+	ec = ((sl | (1 << 3)) | ((msg->start_offset & 0xFFF) << 4));
+
+	if (wbuf)
+		mlen += len;
+	if (rbuf) {
+		mlen++;
+		if (!msg->comp)
+			ret = slim_processtxn(ctrl, SLIM_MSG_DEST_LOGICALADDR,
+				mc, ec, SLIM_MSG_MT_CORE, rbuf, wbuf, len, mlen,
+				&complete, sbdev->laddr, &tid);
+		else
+			ret = slim_processtxn(ctrl, SLIM_MSG_DEST_LOGICALADDR,
+				mc, ec, SLIM_MSG_MT_CORE, rbuf, wbuf, len, mlen,
+				msg->comp, sbdev->laddr, &tid);
+		/* sync read */
+		if (!ret && !msg->comp) {
+			ret = wait_for_completion_timeout(&complete, HZ);
+			if (!ret) {
+				struct slim_msg_txn *txn;
+				dev_err(&ctrl->dev, "slimbus Read timed out");
+				mutex_lock(&ctrl->m_ctrl);
+				txn = ctrl->txnt[tid];
+				/* Invalidate the transaction */
+				ctrl->txnt[tid] = NULL;
+				mutex_unlock(&ctrl->m_ctrl);
+				kfree(txn);
+				ret = -ETIMEDOUT;
+			} else
+				ret = 0;
+		}
+
+	} else
+		ret = slim_processtxn(ctrl, SLIM_MSG_DEST_LOGICALADDR, mc, ec,
+				SLIM_MSG_MT_CORE, rbuf, wbuf, len, mlen,
+				NULL, sbdev->laddr, NULL);
+xfer_err:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(slim_xfer_msg);
+
+/*
+ * slim_alloc_mgrports: Allocate port on manager side.
+ * @sb: device/client handle.
+ * @req: Port request type.
+ * @nports: Number of ports requested
+ * @rh: output buffer to store the port handles
+ * @hsz: size of buffer storing handles
+ * context: can sleep
+ * This port will be typically used by SW. e.g. client driver wants to receive
+ * some data from audio codec HW using a data channel.
+ * Port allocated using this API will be used to receive the data.
+ * If half-duplex ports are requested, two adjacent ports are allocated for
+ * 1 half-duplex port. So the handle-buffer size should be twice the number
+ * of half-duplex ports to be allocated.
+ * -EDQUOT is returned if all ports are in use.
+ */
+int slim_alloc_mgrports(struct slim_device *sb, enum slim_port_req req,
+				int nports, u32 *rh, int hsz)
+{
+	int i, j;
+	int ret = -EINVAL;
+	int nphysp = nports;
+	struct slim_controller *ctrl = sb->ctrl;
+
+	if (!rh || !ctrl)
+		return -EINVAL;
+	if (req == SLIM_REQ_HALF_DUP)
+		nphysp *= 2;
+	if (hsz/sizeof(u32) < nphysp)
+		return -EINVAL;
+	mutex_lock(&ctrl->m_ctrl);
+
+	for (i = 0; i < ctrl->nports; i++) {
+		bool multiok = true;
+		if (ctrl->ports[i].state != SLIM_P_FREE)
+			continue;
+		/* Start half duplex channel at even port */
+		if (req == SLIM_REQ_HALF_DUP && (i % 2))
+			continue;
+		/* Allocate ports contiguously for multi-ch */
+		if (ctrl->nports < (i + nphysp)) {
+			i = ctrl->nports;
+			break;
+		}
+		if (req == SLIM_REQ_MULTI_CH) {
+			multiok = true;
+			for (j = i; j < i + nphysp; j++) {
+				if (ctrl->ports[j].state != SLIM_P_FREE) {
+					multiok = false;
+					break;
+				}
+			}
+			if (!multiok)
+				continue;
+		}
+		break;
+	}
+	if (i >= ctrl->nports)
+		ret = -EDQUOT;
+	for (j = i; j < i + nphysp; j++) {
+		ctrl->ports[j].state = SLIM_P_UNCFG;
+		ctrl->ports[j].req = req;
+		if (req == SLIM_REQ_HALF_DUP && (j % 2))
+			ctrl->ports[j].flow = SLIM_SINK;
+		else
+			ctrl->ports[j].flow = SLIM_SRC;
+		ret = ctrl->config_port(ctrl, j);
+		if (ret) {
+			for (; j >= i; j--)
+				ctrl->ports[j].state = SLIM_P_FREE;
+			goto alloc_err;
+		}
+		*rh++ = SLIM_PORT_HDL(SLIM_LA_MANAGER, 0, j);
+	}
+alloc_err:
+	mutex_unlock(&ctrl->m_ctrl);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(slim_alloc_mgrports);
+
+/* Deallocate the port(s) allocated using the API above */
+int slim_dealloc_mgrports(struct slim_device *sb, u32 *hdl, int nports)
+{
+	int i;
+	struct slim_controller *ctrl = sb->ctrl;
+
+	if (!ctrl || !hdl)
+		return -EINVAL;
+
+	mutex_lock(&ctrl->m_ctrl);
+
+	for (i = 0; i < nports; i++) {
+		u8 pn;
+		pn = SLIM_HDL_TO_PORT(hdl[i]);
+		if (ctrl->ports[pn].state == SLIM_P_CFG) {
+			int j;
+			dev_err(&ctrl->dev, "Can't dealloc connected port:%d",
+					i);
+			for (j = i - 1; j >= 0; j--) {
+				pn = SLIM_HDL_TO_PORT(hdl[j]);
+				ctrl->ports[pn].state = SLIM_P_UNCFG;
+			}
+			mutex_unlock(&ctrl->m_ctrl);
+			return -EISCONN;
+		}
+		ctrl->ports[pn].state = SLIM_P_FREE;
+	}
+	mutex_unlock(&ctrl->m_ctrl);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(slim_dealloc_mgrports);
+
+/*
+ * slim_get_slaveport: Get slave port handle
+ * @la: slave device logical address.
+ * @idx: port index at slave
+ * @rh: return handle
+ * @flw: Flow type (source or destination)
+ * This API only returns a slave port's representation as expected by slimbus
+ * driver. This port is not managed by the slimbus driver. Caller is expected
+ * to have visibility of this port since it's a device-port.
+ */
+int slim_get_slaveport(u8 la, int idx, u32 *rh, enum slim_port_flow flw)
+{
+	if (rh == NULL)
+		return -EINVAL;
+	*rh = SLIM_PORT_HDL(la, flw, idx);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(slim_get_slaveport);
+
+static int connect_port_ch(struct slim_controller *ctrl, u8 ch, u32 ph,
+				enum slim_port_flow flow)
+{
+	int ret;
+	u16 mc;
+	u8 buf[2];
+	u32 la = SLIM_HDL_TO_LA(ph);
+	u8 pn = (u8)SLIM_HDL_TO_PORT(ph);
+
+	if (flow == SLIM_SRC)
+		mc = SLIM_MSG_MC_CONNECT_SOURCE;
+	else
+		mc = SLIM_MSG_MC_CONNECT_SINK;
+	buf[0] = pn;
+	buf[1] = ctrl->chans[ch].chan;
+	if (la == SLIM_LA_MANAGER)
+		ctrl->ports[pn].flow = flow;
+	ret = slim_processtxn(ctrl, SLIM_MSG_DEST_LOGICALADDR, mc, 0,
+				SLIM_MSG_MT_CORE, NULL, buf, 2, 6, NULL, la,
+				NULL);
+	if (!ret && la == SLIM_LA_MANAGER)
+		ctrl->ports[pn].state = SLIM_P_CFG;
+	return ret;
+}
+
+static int disconnect_port_ch(struct slim_controller *ctrl, u32 ph)
+{
+	int ret;
+	u16 mc;
+	u32 la = SLIM_HDL_TO_LA(ph);
+	u8 pn = (u8)SLIM_HDL_TO_PORT(ph);
+
+	mc = SLIM_MSG_MC_DISCONNECT_PORT;
+	ret = slim_processtxn(ctrl, SLIM_MSG_DEST_LOGICALADDR, mc, 0,
+				SLIM_MSG_MT_CORE, NULL, &pn, 1, 5,
+				NULL, la, NULL);
+	if (ret)
+		return ret;
+	if (la == SLIM_LA_MANAGER)
+		ctrl->ports[pn].state = SLIM_P_UNCFG;
+	return 0;
+}
+
+/*
+ * slim_connect_src: Connect source port to channel.
+ * @sb: client handle
+ * @srch: source handle to be connected to this channel
+ * @chanh: Channel with which the ports need to be associated with.
+ * Per slimbus specification, a channel may have 1 source port.
+ * Channel specified in chanh needs to be allocated first.
+ * Returns -EALREADY if source is already configured for this channel.
+ * Returns -ENOTCONN if channel is not allocated
+ */
+int slim_connect_src(struct slim_device *sb, u32 srch, u16 chanh)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+	int ret;
+	u8 chan = SLIM_HDL_TO_CHIDX(chanh);
+	struct slim_ich *slc = &ctrl->chans[chan];
+	enum slim_port_flow flow = SLIM_HDL_TO_FLOW(srch);
+
+	if (flow != SLIM_SRC)
+		return -EINVAL;
+
+	mutex_lock(&ctrl->m_ctrl);
+
+	if (slc->state == SLIM_CH_FREE) {
+		ret = -ENOTCONN;
+		goto connect_src_err;
+	}
+	/*
+	 * Once channel is removed, its ports can be considered disconnected
+	 * So its ports can be reassigned. Source port is zeroed
+	 * when channel is deallocated.
+	 */
+	if (slc->srch) {
+		ret = -EALREADY;
+		goto connect_src_err;
+	}
+
+	ret = connect_port_ch(ctrl, chan, srch, SLIM_SRC);
+
+	if (!ret)
+		slc->srch = srch;
+
+connect_src_err:
+	mutex_unlock(&ctrl->m_ctrl);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(slim_connect_src);
+
+/*
+ * slim_connect_sink: Connect sink port(s) to channel.
+ * @sb: client handle
+ * @sinkh: sink handle(s) to be connected to this channel
+ * @nsink: number of sinks
+ * @chanh: Channel with which the ports need to be associated with.
+ * Per slimbus specification, a channel may have multiple sink-ports.
+ * Channel specified in chanh needs to be allocated first.
+ * Returns -EALREADY if sink is already configured for this channel.
+ * Returns -ENOTCONN if channel is not allocated
+ */
+int slim_connect_sink(struct slim_device *sb, u32 *sinkh, int nsink, u16 chanh)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+	int j;
+	int ret = 0;
+	u8 chan = SLIM_HDL_TO_CHIDX(chanh);
+	struct slim_ich *slc = &ctrl->chans[chan];
+
+	if (!sinkh || !nsink)
+		return -EINVAL;
+
+	mutex_lock(&ctrl->m_ctrl);
+
+	/*
+	 * Once channel is removed, its ports can be considered disconnected
+	 * So its ports can be reassigned. Sink ports are freed when channel
+	 * is deallocated.
+	 */
+	if (slc->state == SLIM_CH_FREE) {
+		ret = -ENOTCONN;
+		goto connect_sink_err;
+	}
+
+	for (j = 0; j < nsink; j++) {
+		enum slim_port_flow flow = SLIM_HDL_TO_FLOW(sinkh[j]);
+		if (flow != SLIM_SINK)
+			ret = -EINVAL;
+		else
+			ret = connect_port_ch(ctrl, chan, sinkh[j], SLIM_SINK);
+		if (ret) {
+			for (j = j - 1; j >= 0; j--)
+				disconnect_port_ch(ctrl, sinkh[j]);
+			goto connect_sink_err;
+		}
+	}
+
+	slc->sinkh = krealloc(slc->sinkh, (sizeof(u32) * (slc->nsink + nsink)),
+				GFP_KERNEL);
+	if (!slc->sinkh) {
+		ret = -ENOMEM;
+		for (j = 0; j < nsink; j++)
+			disconnect_port_ch(ctrl, sinkh[j]);
+		goto connect_sink_err;
+	}
+
+	memcpy(slc->sinkh + slc->nsink, sinkh, (sizeof(u32) * nsink));
+	slc->nsink += nsink;
+
+connect_sink_err:
+	mutex_unlock(&ctrl->m_ctrl);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(slim_connect_sink);
+
+/*
+ * slim_disconnect_ports: Disconnect port(s) from channel
+ * @sb: client handle
+ * @ph: ports to be disconnected
+ * @nph: number of ports.
+ * Disconnects ports from a channel.
+ */
+int slim_disconnect_ports(struct slim_device *sb, u32 *ph, int nph)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+	int i;
+
+	mutex_lock(&ctrl->m_ctrl);
+
+	for (i = 0; i < nph; i++)
+		disconnect_port_ch(ctrl, ph[i]);
+	mutex_unlock(&ctrl->m_ctrl);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(slim_disconnect_ports);
+
+/*
+ * slim_port_xfer: Schedule buffer to be transferred/received using port-handle.
+ * @sb: client handle
+ * @ph: port-handle
+ * @iobuf: buffer to be transferred or populated
+ * @len: buffer size.
+ * @comp: completion signal to indicate transfer done or error.
+ * context: can sleep
+ * Returns number of bytes transferred/received if used synchronously.
+ * Will return 0 if used asynchronously.
+ * Client will call slim_port_get_xfer_status to get error and/or number of
+ * bytes transferred if used asynchronously.
+ */
+int slim_port_xfer(struct slim_device *sb, u32 ph, u8 *iobuf, u32 len,
+				struct completion *comp)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+	u8 pn = SLIM_HDL_TO_PORT(ph);
+	dev_dbg(&ctrl->dev, "port xfer: num:%d", pn);
+	return ctrl->port_xfer(ctrl, pn, iobuf, len, comp);
+}
+EXPORT_SYMBOL_GPL(slim_port_xfer);
+
+/*
+ * slim_port_get_xfer_status: Poll for port transfers, or get transfer status
+ *	after completion is done.
+ * @sb: client handle
+ * @ph: port-handle
+ * @done_buf: return pointer (iobuf from slim_port_xfer) which is processed.
+ * @done_len: Number of bytes transferred.
+ * This can be called when port_xfer complition is signalled.
+ * The API will return port transfer error (underflow/overflow/disconnect)
+ * and/or done_len will reflect number of bytes transferred. Note that
+ * done_len may be valid even if port error (overflow/underflow) has happened.
+ * e.g. If the transfer was scheduled with a few bytes to be transferred and
+ * client has not supplied more data to be transferred, done_len will indicate
+ * number of bytes transferred with underflow error. To avoid frequent underflow
+ * errors, multiple transfers can be queued (e.g. ping-pong buffers) so that
+ * channel has data to be transferred even if client is not ready to transfer
+ * data all the time. done_buf will indicate address of the last buffer
+ * processed from the multiple transfers.
+ */
+enum slim_port_err slim_port_get_xfer_status(struct slim_device *sb, u32 ph,
+			u8 **done_buf, u32 *done_len)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+	u8 pn = SLIM_HDL_TO_PORT(ph);
+	u32 la = SLIM_HDL_TO_LA(ph);
+	enum slim_port_err err;
+	dev_dbg(&ctrl->dev, "get status port num:%d", pn);
+	/*
+	 * Framework only has insight into ports managed by ported device
+	 * used by the manager and not slave
+	 */
+	if (la != SLIM_LA_MANAGER) {
+		if (done_buf)
+			*done_buf = NULL;
+		if (done_len)
+			*done_len = 0;
+		return SLIM_P_NOT_OWNED;
+	}
+	err = ctrl->port_xfer_status(ctrl, pn, done_buf, done_len);
+	if (err == SLIM_P_INPROGRESS)
+		err = ctrl->ports[pn].err;
+	return err;
+}
+EXPORT_SYMBOL_GPL(slim_port_get_xfer_status);
+
+static void slim_add_ch(struct slim_controller *ctrl, struct slim_ich *slc)
+{
+	struct slim_ich **arr;
+	int i, j;
+	int *len;
+	int sl = slc->seglen << slc->rootexp;
+	/* Channel is already active and other end is transmitting data */
+	if (slc->state >= SLIM_CH_ACTIVE)
+		return;
+	if (slc->coeff == SLIM_COEFF_1) {
+		arr = ctrl->sched.chc1;
+		len = &ctrl->sched.num_cc1;
+	} else {
+		arr = ctrl->sched.chc3;
+		len = &ctrl->sched.num_cc3;
+		sl *= 3;
+	}
+
+	*len += 1;
+
+	/* Insert the channel based on rootexp and seglen */
+	for (i = 0; i < *len - 1; i++) {
+		/*
+		 * Primary key: exp low to high.
+		 * Secondary key: seglen: high to low
+		 */
+		if ((slc->rootexp > arr[i]->rootexp) ||
+			((slc->rootexp == arr[i]->rootexp) &&
+			(slc->seglen < arr[i]->seglen)))
+			continue;
+		else
+			break;
+	}
+	for (j = *len - 1; j > i; j--)
+		arr[j] = arr[j - 1];
+	arr[i] = slc;
+	ctrl->sched.usedslots += sl;
+
+	return;
+}
+
+static int slim_remove_ch(struct slim_controller *ctrl, struct slim_ich *slc)
+{
+	struct slim_ich **arr;
+	int i;
+	u32 la, ph;
+	int *len;
+	if (slc->coeff == SLIM_COEFF_1) {
+		arr = ctrl->sched.chc1;
+		len = &ctrl->sched.num_cc1;
+	} else {
+		arr = ctrl->sched.chc3;
+		len = &ctrl->sched.num_cc3;
+	}
+
+	for (i = 0; i < *len; i++) {
+		if (arr[i] == slc)
+			break;
+	}
+	if (i >= *len)
+		return -EXFULL;
+	for (; i < *len - 1; i++)
+		arr[i] = arr[i + 1];
+	*len -= 1;
+	arr[*len] = NULL;
+
+	slc->state = SLIM_CH_ALLOCATED;
+	slc->newintr = 0;
+	slc->newoff = 0;
+	for (i = 0; i < slc->nsink; i++) {
+		ph = slc->sinkh[i];
+		la = SLIM_HDL_TO_LA(ph);
+		/*
+		 * For ports managed by manager's ported device, no need to send
+		 * disconnect. It is client's responsibility to call disconnect
+		 * on ports owned by the slave device
+		 */
+		if (la == SLIM_LA_MANAGER)
+			ctrl->ports[SLIM_HDL_TO_PORT(ph)].state = SLIM_P_UNCFG;
+	}
+
+	ph = slc->srch;
+	la = SLIM_HDL_TO_LA(ph);
+	if (la == SLIM_LA_MANAGER)
+		ctrl->ports[SLIM_HDL_TO_PORT(ph)].state = SLIM_P_UNCFG;
+
+	kfree(slc->sinkh);
+	slc->sinkh = NULL;
+	slc->srch = 0;
+	slc->nsink = 0;
+	return 0;
+}
+
+static u32 slim_calc_prrate(struct slim_controller *ctrl, struct slim_ch *prop)
+{
+	u32 rate = 0, rate4k = 0, rate11k = 0;
+	u32 exp = 0;
+	u32 pr = 0;
+	bool exact = true;
+	bool done = false;
+	enum slim_ch_rate ratefam;
+
+	if (prop->prot >= SLIM_PUSH)
+		return 0;
+	if (prop->baser == SLIM_RATE_1HZ) {
+		rate = prop->ratem / 4000;
+		rate4k = rate;
+		if (rate * 4000 == prop->ratem)
+			ratefam = SLIM_RATE_4000HZ;
+		else {
+			rate = prop->ratem / 11025;
+			rate11k = rate;
+			if (rate * 11025 == prop->ratem)
+				ratefam = SLIM_RATE_11025HZ;
+			else
+				ratefam = SLIM_RATE_1HZ;
+		}
+	} else {
+		ratefam = prop->baser;
+		rate = prop->ratem;
+	}
+	if (ratefam == SLIM_RATE_1HZ) {
+		exact = false;
+		if ((rate4k + 1) * 4000 < (rate11k + 1) * 11025) {
+			rate = rate4k + 1;
+			ratefam = SLIM_RATE_4000HZ;
+		} else {
+			rate = rate11k + 1;
+			ratefam = SLIM_RATE_11025HZ;
+		}
+	}
+	/* covert rate to coeff-exp */
+	while (!done) {
+		while ((rate & 0x1) != 0x1) {
+			rate >>= 1;
+			exp++;
+		}
+		if (rate > 3) {
+			/* roundup if not exact */
+			rate++;
+			exact = false;
+		} else
+			done = true;
+	}
+	if (ratefam == SLIM_RATE_4000HZ) {
+		if (rate == 1)
+			pr = 0x10;
+		else {
+			pr = 0;
+			exp++;
+		}
+	} else {
+		pr = 8;
+		exp++;
+	}
+	if (exp <= 7) {
+		pr |= exp;
+		if (exact)
+			pr |= 0x80;
+	} else
+		pr = 0;
+	return pr;
+}
+
+static int slim_nextdefine_ch(struct slim_device *sb, u8 chan)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+	u32 chrate = 0;
+	u32 exp = 0;
+	u32 coeff = 0;
+	bool exact = true;
+	bool done = false;
+	int ret = 0;
+	struct slim_ich *slc = &ctrl->chans[chan];
+	struct slim_ch *prop = &slc->prop;
+
+	slc->prrate = slim_calc_prrate(ctrl, prop);
+	dev_dbg(&ctrl->dev, "ch:%d, chan PR rate:%x\n", chan, slc->prrate);
+	if (prop->baser == SLIM_RATE_4000HZ)
+		chrate = 4000 * prop->ratem;
+	else if (prop->baser == SLIM_RATE_11025HZ)
+		chrate = 11025 * prop->ratem;
+	else
+		chrate = prop->ratem;
+	/* max allowed sample freq = 768 seg/frame */
+	if (chrate > 3600000)
+		return -EDQUOT;
+	if (prop->baser == SLIM_RATE_4000HZ &&
+			ctrl->a_framer->superfreq == 4000)
+		coeff = prop->ratem;
+	else if (prop->baser == SLIM_RATE_11025HZ &&
+			ctrl->a_framer->superfreq == 3675)
+		coeff = 3 * prop->ratem;
+	else {
+		u32 tempr = 0;
+		tempr = chrate * SLIM_CL_PER_SUPERFRAME_DIV8;
+		coeff = tempr / ctrl->a_framer->rootfreq;
+		if (coeff * ctrl->a_framer->rootfreq != tempr) {
+			coeff++;
+			exact = false;
+		}
+	}
+
+	/* convert coeff to coeff-exponent */
+	exp = 0;
+	while (!done) {
+		while ((coeff & 0x1) != 0x1) {
+			coeff >>= 1;
+			exp++;
+		}
+		if (coeff > 3) {
+			coeff++;
+			exact = false;
+		} else
+			done = true;
+	}
+	if (prop->prot == SLIM_HARD_ISO && !exact)
+		return -EPROTONOSUPPORT;
+	else if (prop->prot == SLIM_AUTO_ISO) {
+		if (exact)
+			prop->prot = SLIM_HARD_ISO;
+		else {
+			/* Push-Pull not supported for now */
+			return -EPROTONOSUPPORT;
+		}
+	}
+	slc->rootexp = exp;
+	slc->seglen = prop->sampleszbits/SLIM_CL_PER_SL;
+	if (prop->prot != SLIM_HARD_ISO)
+		slc->seglen++;
+	if (prop->prot >= SLIM_EXT_SMPLX)
+		slc->seglen++;
+	/* convert coeff to enum */
+	if (coeff == 1) {
+		if (exp > 9)
+			ret = -EIO;
+		coeff = SLIM_COEFF_1;
+	} else {
+		if (exp > 8)
+			ret = -EIO;
+		coeff = SLIM_COEFF_3;
+	}
+	slc->coeff = coeff;
+
+	return ret;
+}
+
+/*
+ * slim_alloc_ch: Allocate a slimbus channel and return its handle.
+ * @sb: client handle.
+ * @chanh: return channel handle
+ * Slimbus channels are limited to 256 per specification.
+ * -EXFULL is returned if all channels are in use.
+ * Although slimbus specification supports 256 channels, a controller may not
+ * support that many channels.
+ */
+int slim_alloc_ch(struct slim_device *sb, u16 *chanh)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+	u16 i;
+
+	if (!ctrl)
+		return -EINVAL;
+	mutex_lock(&ctrl->m_ctrl);
+	for (i = 0; i < ctrl->nchans; i++) {
+		if (ctrl->chans[i].state == SLIM_CH_FREE)
+			break;
+	}
+	if (i >= ctrl->nchans) {
+		mutex_unlock(&ctrl->m_ctrl);
+		return -EXFULL;
+	}
+	*chanh = i;
+	ctrl->chans[i].nextgrp = 0;
+	ctrl->chans[i].state = SLIM_CH_ALLOCATED;
+	ctrl->chans[i].chan = (u8)(ctrl->reserved + i);
+
+	mutex_unlock(&ctrl->m_ctrl);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(slim_alloc_ch);
+
+/*
+ * slim_query_ch: Get reference-counted handle for a channel number. Every
+ * channel is reference counted by upto one as producer and the others as
+ * consumer)
+ * @sb: client handle
+ * @chan: slimbus channel number
+ * @chanh: return channel handle
+ * If request channel number is not in use, it is allocated, and reference
+ * count is set to one. If the channel was was already allocated, this API
+ * will return handle to that channel and reference count is incremented.
+ * -EXFULL is returned if all channels are in use
+ */
+int slim_query_ch(struct slim_device *sb, u8 ch, u16 *chanh)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+	u16 i, j;
+	int ret = 0;
+	if (!ctrl || !chanh)
+		return -EINVAL;
+	mutex_lock(&ctrl->m_ctrl);
+	/* start with modulo number */
+	i = ch % ctrl->nchans;
+
+	for (j = 0; j < ctrl->nchans; j++) {
+		if (ctrl->chans[i].chan == ch) {
+			*chanh = i;
+			ctrl->chans[i].ref++;
+			if (ctrl->chans[i].state == SLIM_CH_FREE)
+				ctrl->chans[i].state = SLIM_CH_ALLOCATED;
+			goto query_out;
+		}
+		i = (i + 1) % ctrl->nchans;
+	}
+
+	/* Channel not in table yet */
+	ret = -EXFULL;
+	for (j = 0; j < ctrl->nchans; j++) {
+		if (ctrl->chans[i].state == SLIM_CH_FREE) {
+			ctrl->chans[i].state =
+				SLIM_CH_ALLOCATED;
+			*chanh = i;
+			ctrl->chans[i].ref++;
+			ctrl->chans[i].chan = ch;
+			ctrl->chans[i].nextgrp = 0;
+			ret = 0;
+			break;
+		}
+		i = (i + 1) % ctrl->nchans;
+	}
+query_out:
+	mutex_unlock(&ctrl->m_ctrl);
+	dev_dbg(&ctrl->dev, "query ch:%d,hdl:%d,ref:%d,ret:%d",
+				ch, i, ctrl->chans[i].ref, ret);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(slim_query_ch);
+
+/*
+ * slim_dealloc_ch: Deallocate channel allocated using the API above
+ * -EISCONN is returned if the channel is tried to be deallocated without
+ *  being removed first.
+ *  -ENOTCONN is returned if deallocation is tried on a channel that's not
+ *  allocated.
+ */
+int slim_dealloc_ch(struct slim_device *sb, u16 chanh)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+	u8 chan = SLIM_HDL_TO_CHIDX(chanh);
+	struct slim_ich *slc = &ctrl->chans[chan];
+	if (!ctrl)
+		return -EINVAL;
+
+	mutex_lock(&ctrl->m_ctrl);
+	if (slc->state == SLIM_CH_FREE) {
+		mutex_unlock(&ctrl->m_ctrl);
+		return -ENOTCONN;
+	}
+	if (slc->ref > 1) {
+		slc->ref--;
+		mutex_unlock(&ctrl->m_ctrl);
+		dev_dbg(&ctrl->dev, "remove chan:%d,hdl:%d,ref:%d",
+					slc->chan, chanh, slc->ref);
+		return 0;
+	}
+	if (slc->state >= SLIM_CH_PENDING_ACTIVE) {
+		dev_err(&ctrl->dev, "Channel:%d should be removed first", chan);
+		mutex_unlock(&ctrl->m_ctrl);
+		return -EISCONN;
+	}
+	slc->ref--;
+	slc->state = SLIM_CH_FREE;
+	mutex_unlock(&ctrl->m_ctrl);
+	dev_dbg(&ctrl->dev, "remove chan:%d,hdl:%d,ref:%d",
+				slc->chan, chanh, slc->ref);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(slim_dealloc_ch);
+
+/*
+ * slim_get_ch_state: Channel state.
+ * This API returns the channel's state (active, suspended, inactive etc)
+ */
+enum slim_ch_state slim_get_ch_state(struct slim_device *sb, u16 chanh)
+{
+	u8 chan = SLIM_HDL_TO_CHIDX(chanh);
+	struct slim_ich *slc = &sb->ctrl->chans[chan];
+	return slc->state;
+}
+EXPORT_SYMBOL_GPL(slim_get_ch_state);
+
+/*
+ * slim_define_ch: Define a channel.This API defines channel parameters for a
+ *	given channel.
+ * @sb: client handle.
+ * @prop: slim_ch structure with channel parameters desired to be used.
+ * @chanh: list of channels to be defined.
+ * @nchan: number of channels in a group (1 if grp is false)
+ * @grp: Are the channels grouped
+ * @grph: return group handle if grouping of channels is desired.
+ * Channels can be grouped if multiple channels use same parameters
+ * (e.g. 5.1 audio has 6 channels with same parameters. They will all be grouped
+ * and given 1 handle for simplicity and avoid repeatedly calling the API)
+ * -EISCONN is returned if channel is already used with different parameters.
+ * -ENXIO is returned if the channel is not yet allocated.
+ */
+int slim_define_ch(struct slim_device *sb, struct slim_ch *prop, u16 *chanh,
+			u8 nchan, bool grp, u16 *grph)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+	int i, ret = 0;
+
+	if (!ctrl || !chanh || !prop || !nchan)
+		return -EINVAL;
+	mutex_lock(&ctrl->m_ctrl);
+	for (i = 0; i < nchan; i++) {
+		u8 chan = SLIM_HDL_TO_CHIDX(chanh[i]);
+		struct slim_ich *slc = &ctrl->chans[chan];
+		dev_dbg(&ctrl->dev, "define_ch: ch:%d, state:%d", chan,
+				(int)ctrl->chans[chan].state);
+		if (slc->state < SLIM_CH_ALLOCATED) {
+			ret = -ENXIO;
+			goto err_define_ch;
+		}
+		if (slc->state >= SLIM_CH_DEFINED && slc->ref >= 2) {
+			if (prop->ratem != slc->prop.ratem ||
+			prop->sampleszbits != slc->prop.sampleszbits ||
+			prop->baser != slc->prop.baser) {
+				ret = -EISCONN;
+				goto err_define_ch;
+			}
+		} else if (slc->state > SLIM_CH_DEFINED) {
+			ret = -EISCONN;
+			goto err_define_ch;
+		} else {
+			ctrl->chans[chan].prop = *prop;
+			ret = slim_nextdefine_ch(sb, chan);
+			if (ret)
+				goto err_define_ch;
+		}
+		if (i < (nchan - 1))
+			ctrl->chans[chan].nextgrp = chanh[i + 1];
+		if (i == 0)
+			ctrl->chans[chan].nextgrp |= SLIM_START_GRP;
+		if (i == (nchan - 1))
+			ctrl->chans[chan].nextgrp |= SLIM_END_GRP;
+	}
+
+	if (grp)
+		*grph = chanh[0];
+	for (i = 0; i < nchan; i++) {
+		u8 chan = SLIM_HDL_TO_CHIDX(chanh[i]);
+		struct slim_ich *slc = &ctrl->chans[chan];
+		if (slc->state == SLIM_CH_ALLOCATED)
+			slc->state = SLIM_CH_DEFINED;
+	}
+err_define_ch:
+	dev_dbg(&ctrl->dev, "define_ch: ch:%d, ret:%d", *chanh, ret);
+	mutex_unlock(&ctrl->m_ctrl);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(slim_define_ch);
+
+static u32 getsubfrmcoding(u32 *ctrlw, u32 *subfrml, u32 *msgsl)
+{
+	u32 code = 0;
+	if (*ctrlw == *subfrml) {
+		*ctrlw = 8;
+		*subfrml = 8;
+		*msgsl = SLIM_SL_PER_SUPERFRAME - SLIM_FRM_SLOTS_PER_SUPERFRAME
+				- SLIM_GDE_SLOTS_PER_SUPERFRAME;
+		return 0;
+	}
+	if (*subfrml == 6) {
+		code = 0;
+		*msgsl = 256;
+	} else if (*subfrml == 8) {
+		code = 1;
+		*msgsl = 192;
+	} else if (*subfrml == 24) {
+		code = 2;
+		*msgsl = 64;
+	} else { /* 32 */
+		code = 3;
+		*msgsl = 48;
+	}
+
+	if (*ctrlw < 8) {
+		if (*ctrlw >= 6) {
+			*ctrlw = 6;
+			code |= 0x14;
+		} else {
+			if (*ctrlw == 5)
+				*ctrlw = 4;
+			code |= (*ctrlw << 2);
+		}
+	} else {
+		code -= 2;
+		if (*ctrlw >= 24) {
+			*ctrlw = 24;
+			code |= 0x1e;
+		} else if (*ctrlw >= 16) {
+			*ctrlw = 16;
+			code |= 0x1c;
+		} else if (*ctrlw >= 12) {
+			*ctrlw = 12;
+			code |= 0x1a;
+		} else {
+			*ctrlw = 8;
+			code |= 0x18;
+		}
+	}
+
+	*msgsl = (*msgsl * *ctrlw) - SLIM_FRM_SLOTS_PER_SUPERFRAME -
+				SLIM_GDE_SLOTS_PER_SUPERFRAME;
+	return code;
+}
+
+static void shiftsegoffsets(struct slim_controller *ctrl, struct slim_ich **ach,
+				int sz, u32 shft)
+{
+	int i;
+	u32 oldoff;
+	for (i = 0; i < sz; i++) {
+		struct slim_ich *slc;
+		if (ach[i] == NULL)
+			continue;
+		slc = ach[i];
+		if (slc->state == SLIM_CH_PENDING_REMOVAL)
+			continue;
+		oldoff = slc->newoff;
+		slc->newoff += shft;
+		/* seg. offset must be <= interval */
+		if (slc->newoff >= slc->newintr)
+			slc->newoff -= slc->newintr;
+	}
+}
+
+static int slim_sched_chans(struct slim_device *sb, u32 clkgear,
+			u32 *ctrlw, u32 *subfrml)
+{
+	int coeff1, coeff3;
+	enum slim_ch_coeff bias;
+	struct slim_controller *ctrl = sb->ctrl;
+	int last1 = ctrl->sched.num_cc1 - 1;
+	int last3 = ctrl->sched.num_cc3 - 1;
+
+	/*
+	 * Find first channels with coeff 1 & 3 as starting points for
+	 * scheduling
+	 */
+	for (coeff3 = 0; coeff3 < ctrl->sched.num_cc3; coeff3++) {
+		struct slim_ich *slc = ctrl->sched.chc3[coeff3];
+		if (slc->state == SLIM_CH_PENDING_REMOVAL)
+			continue;
+		else
+			break;
+	}
+	for (coeff1 = 0; coeff1 < ctrl->sched.num_cc1; coeff1++) {
+		struct slim_ich *slc = ctrl->sched.chc1[coeff1];
+		if (slc->state == SLIM_CH_PENDING_REMOVAL)
+			continue;
+		else
+			break;
+	}
+	if (coeff3 == ctrl->sched.num_cc3 && coeff1 == ctrl->sched.num_cc1) {
+		*ctrlw = 8;
+		*subfrml = 8;
+		return 0;
+	} else if (coeff3 == ctrl->sched.num_cc3)
+		bias = SLIM_COEFF_1;
+	else
+		bias = SLIM_COEFF_3;
+
+	/*
+	 * Find last chan in coeff1, 3 list, we will use to know when we
+	* have done scheduling all coeff1 channels
+	*/
+	while (last1 >= 0) {
+		if (ctrl->sched.chc1[last1] != NULL &&
+			(ctrl->sched.chc1[last1])->state !=
+			SLIM_CH_PENDING_REMOVAL)
+			break;
+		last1--;
+	}
+	while (last3 >= 0) {
+		if (ctrl->sched.chc3[last3] != NULL &&
+			(ctrl->sched.chc3[last3])->state !=
+			SLIM_CH_PENDING_REMOVAL)
+			break;
+		last3--;
+	}
+
+	if (bias == SLIM_COEFF_1) {
+		struct slim_ich *slc1 = ctrl->sched.chc1[coeff1];
+		u32 expshft = SLIM_MAX_CLK_GEAR - clkgear;
+		int curexp, finalexp;
+		u32 curintr, curmaxsl;
+		int opensl1[2];
+		int maxctrlw1;
+
+		finalexp = (ctrl->sched.chc1[last1])->rootexp;
+		curexp = (int)expshft - 1;
+
+		curintr = (SLIM_MAX_INTR_COEFF_1 * 2) >> (curexp + 1);
+		curmaxsl = curintr >> 1;
+		opensl1[0] = opensl1[1] = curmaxsl;
+
+		while ((coeff1 < ctrl->sched.num_cc1) || (curintr > 24)) {
+			curintr >>= 1;
+			curmaxsl >>= 1;
+
+			/* update 4K family open slot records */
+			if (opensl1[1] < opensl1[0])
+				opensl1[1] -= curmaxsl;
+			else
+				opensl1[1] = opensl1[0] - curmaxsl;
+			opensl1[0] = curmaxsl;
+			if (opensl1[1] < 0) {
+				opensl1[0] += opensl1[1];
+				opensl1[1] = 0;
+			}
+			if (opensl1[0] <= 0) {
+				dev_dbg(&ctrl->dev, "reconfig failed:%d\n",
+						__LINE__);
+				return -EXFULL;
+			}
+			curexp++;
+			/* schedule 4k family channels */
+
+			while ((coeff1 < ctrl->sched.num_cc1) && (curexp ==
+					(int)(slc1->rootexp + expshft))) {
+				if (slc1->state == SLIM_CH_PENDING_REMOVAL) {
+					coeff1++;
+					slc1 = ctrl->sched.chc1[coeff1];
+					continue;
+				}
+				if (opensl1[1] >= opensl1[0] ||
+					(finalexp == (int)slc1->rootexp &&
+					 curintr <= 24 &&
+					 opensl1[0] == curmaxsl)) {
+					opensl1[1] -= slc1->seglen;
+					slc1->newoff = curmaxsl + opensl1[1];
+					if (opensl1[1] < 0 &&
+						opensl1[0] == curmaxsl) {
+						opensl1[0] += opensl1[1];
+						opensl1[1] = 0;
+						if (opensl1[0] < 0) {
+							dev_dbg(&ctrl->dev,
+							"reconfig failed:%d\n",
+							__LINE__);
+							return -EXFULL;
+						}
+					}
+				} else {
+					if (slc1->seglen > opensl1[0]) {
+						dev_dbg(&ctrl->dev,
+						"reconfig failed:%d\n",
+						__LINE__);
+						return -EXFULL;
+					}
+					slc1->newoff = opensl1[0] -
+							slc1->seglen;
+					opensl1[0] = slc1->newoff;
+				}
+				slc1->newintr = curintr;
+				coeff1++;
+				slc1 = ctrl->sched.chc1[coeff1];
+			}
+		}
+		/* Leave some slots for messaging space */
+		if (opensl1[1] == 0 && opensl1[0] == 0)
+			return -EXFULL;
+		if (opensl1[1] > opensl1[0]) {
+			int temp = opensl1[0];
+			opensl1[0] = opensl1[1];
+			opensl1[1] = temp;
+			shiftsegoffsets(ctrl, ctrl->sched.chc1,
+					ctrl->sched.num_cc1, curmaxsl);
+		}
+		/* choose subframe mode to maximize bw */
+		maxctrlw1 = opensl1[0];
+		if (opensl1[0] == curmaxsl)
+			maxctrlw1 += opensl1[1];
+		if (curintr >= 24) {
+			*subfrml = 24;
+			*ctrlw = maxctrlw1;
+		} else if (curintr == 12) {
+			if (maxctrlw1 > opensl1[1] * 4) {
+				*subfrml = 24;
+				*ctrlw = maxctrlw1;
+			} else {
+				*subfrml = 6;
+				*ctrlw = opensl1[1];
+			}
+		} else {
+			*subfrml = 6;
+			*ctrlw = maxctrlw1;
+		}
+	} else {
+		struct slim_ich *slc1 = NULL;
+		struct slim_ich *slc3 = ctrl->sched.chc3[coeff3];
+		u32 expshft = SLIM_MAX_CLK_GEAR - clkgear;
+		int curexp, finalexp, exp1;
+		u32 curintr, curmaxsl;
+		int opensl3[2];
+		int opensl1[6];
+		bool opensl1valid = false;
+		int maxctrlw1, maxctrlw3, i;
+		finalexp = (ctrl->sched.chc3[last3])->rootexp;
+		if (last1 >= 0) {
+			slc1 = ctrl->sched.chc1[coeff1];
+			exp1 = (ctrl->sched.chc1[last1])->rootexp;
+			if (exp1 > finalexp)
+				finalexp = exp1;
+		}
+		curexp = (int)expshft - 1;
+
+		curintr = (SLIM_MAX_INTR_COEFF_3 * 2) >> (curexp + 1);
+		curmaxsl = curintr >> 1;
+		opensl3[0] = opensl3[1] = curmaxsl;
+
+		while (coeff1 < ctrl->sched.num_cc1 ||
+			coeff3 < ctrl->sched.num_cc3 ||
+			curintr > 32) {
+			curintr >>= 1;
+			curmaxsl >>= 1;
+
+			/* update 12k family open slot records */
+			if (opensl3[1] < opensl3[0])
+				opensl3[1] -= curmaxsl;
+			else
+				opensl3[1] = opensl3[0] - curmaxsl;
+			opensl3[0] = curmaxsl;
+			if (opensl3[1] < 0) {
+				opensl3[0] += opensl3[1];
+				opensl3[1] = 0;
+			}
+			if (opensl3[0] <= 0) {
+				dev_dbg(&ctrl->dev, "reconfig failed:%d\n",
+						__LINE__);
+				return -EXFULL;
+			}
+			curexp++;
+
+			/* schedule 12k family channels */
+			while (coeff3 < ctrl->sched.num_cc3 &&
+				curexp == (int)slc3->rootexp + expshft) {
+				if (slc3->state == SLIM_CH_PENDING_REMOVAL) {
+					coeff3++;
+					slc3 = ctrl->sched.chc3[coeff3];
+					continue;
+				}
+				opensl1valid = false;
+				if (opensl3[1] >= opensl3[0] ||
+					(finalexp == (int)slc3->rootexp &&
+					 curintr <= 32 &&
+					 opensl3[0] == curmaxsl &&
+					 last1 < 0)) {
+					opensl3[1] -= slc3->seglen;
+					slc3->newoff = curmaxsl + opensl3[1];
+					if (opensl3[1] < 0 &&
+						opensl3[0] == curmaxsl) {
+						opensl3[0] += opensl3[1];
+						opensl3[1] = 0;
+					}
+					if (opensl3[0] < 0) {
+						dev_dbg(&ctrl->dev,
+						"reconfig failed:%d\n",
+						__LINE__);
+						return -EXFULL;
+					}
+				} else {
+					if (slc3->seglen > opensl3[0]) {
+						dev_dbg(&ctrl->dev,
+						"reconfig failed:%d\n",
+						__LINE__);
+						return -EXFULL;
+					}
+					slc3->newoff = opensl3[0] -
+							slc3->seglen;
+					opensl3[0] = slc3->newoff;
+				}
+				slc3->newintr = curintr;
+				coeff3++;
+				slc3 = ctrl->sched.chc3[coeff3];
+			}
+			/* update 4k openslot records */
+			if (opensl1valid == false) {
+				for (i = 0; i < 3; i++) {
+					opensl1[i * 2] = opensl3[0];
+					opensl1[(i * 2) + 1] = opensl3[1];
+				}
+			} else {
+				int opensl1p[6];
+				memcpy(opensl1p, opensl1, sizeof(opensl1));
+				for (i = 0; i < 3; i++) {
+					if (opensl1p[i] < opensl1p[i + 3])
+						opensl1[(i * 2) + 1] =
+							opensl1p[i];
+					else
+						opensl1[(i * 2) + 1] =
+							opensl1p[i + 3];
+				}
+				for (i = 0; i < 3; i++) {
+					opensl1[(i * 2) + 1] -= curmaxsl;
+					opensl1[i * 2] = curmaxsl;
+					if (opensl1[(i * 2) + 1] < 0) {
+						opensl1[i * 2] +=
+							opensl1[(i * 2) + 1];
+						opensl1[(i * 2) + 1] = 0;
+					}
+					if (opensl1[i * 2] < 0) {
+						dev_dbg(&ctrl->dev,
+						"reconfig failed:%d\n",
+						__LINE__);
+						return -EXFULL;
+					}
+				}
+			}
+			/* schedule 4k family channels */
+			while (coeff1 < ctrl->sched.num_cc1 &&
+				curexp == (int)slc1->rootexp + expshft) {
+				/* searchorder effective when opensl valid */
+				static const int srcho[] = { 5, 2, 4, 1, 3, 0 };
+				int maxopensl = 0;
+				int maxi = 0;
+				if (slc1->state == SLIM_CH_PENDING_REMOVAL) {
+					coeff1++;
+					slc1 = ctrl->sched.chc1[coeff1];
+					continue;
+				}
+				opensl1valid = true;
+				for (i = 0; i < 6; i++) {
+					if (opensl1[srcho[i]] > maxopensl) {
+						maxopensl = opensl1[srcho[i]];
+						maxi = srcho[i];
+					}
+				}
+				opensl1[maxi] -= slc1->seglen;
+				slc1->newoff = (curmaxsl * maxi) +
+						opensl1[maxi];
+				if (opensl1[maxi] < 0) {
+					if (((maxi & 1) == 1) &&
+					(opensl1[maxi - 1] == curmaxsl)) {
+						opensl1[maxi - 1] +=
+							opensl1[maxi];
+						if (opensl3[0] >
+							opensl1[maxi - 1])
+							opensl3[0] =
+							opensl1[maxi - 1];
+						opensl3[1] = 0;
+						opensl1[maxi] = 0;
+						if (opensl1[maxi - 1] < 0) {
+							dev_dbg(&ctrl->dev,
+							"reconfig failed:%d\n",
+							__LINE__);
+							return -EXFULL;
+						}
+					} else {
+						dev_dbg(&ctrl->dev,
+						"reconfig failed:%d\n",
+						__LINE__);
+						return -EXFULL;
+					}
+				} else {
+					if (opensl3[maxi & 1] > opensl1[maxi])
+						opensl3[maxi & 1] =
+							opensl1[maxi];
+				}
+				slc1->newintr = curintr * 3;
+				coeff1++;
+				slc1 = ctrl->sched.chc1[coeff1];
+			}
+		}
+		/* Leave some slots for messaging space */
+		if (opensl3[1] == 0 && opensl3[0] == 0)
+			return -EXFULL;
+		/* swap 1st and 2nd bucket if 2nd bucket has more open slots */
+		if (opensl3[1] > opensl3[0]) {
+			int temp = opensl3[0];
+			opensl3[0] = opensl3[1];
+			opensl3[1] = temp;
+			temp = opensl1[5];
+			opensl1[5] = opensl1[4];
+			opensl1[4] = opensl1[3];
+			opensl1[3] = opensl1[2];
+			opensl1[2] = opensl1[1];
+			opensl1[1] = opensl1[0];
+			opensl1[0] = temp;
+			shiftsegoffsets(ctrl, ctrl->sched.chc1,
+					ctrl->sched.num_cc1, curmaxsl);
+			shiftsegoffsets(ctrl, ctrl->sched.chc3,
+					ctrl->sched.num_cc3, curmaxsl);
+		}
+		/* subframe mode to maximize BW */
+		maxctrlw3 = opensl3[0];
+		maxctrlw1 = opensl1[0];
+		if (opensl3[0] == curmaxsl)
+			maxctrlw3 += opensl3[1];
+		for (i = 0; i < 5 && opensl1[i] == curmaxsl; i++)
+			maxctrlw1 += opensl1[i + 1];
+		if (curintr >= 32) {
+			*subfrml = 32;
+			*ctrlw = maxctrlw3;
+		} else if (curintr == 16) {
+			if (maxctrlw3 > (opensl3[1] * 4)) {
+				*subfrml = 32;
+				*ctrlw = maxctrlw3;
+			} else {
+				*subfrml = 8;
+				*ctrlw = opensl3[1];
+			}
+		} else {
+			if ((maxctrlw1 * 8) >= (maxctrlw3 * 24)) {
+				*subfrml = 24;
+				*ctrlw = maxctrlw1;
+			} else {
+				*subfrml = 8;
+				*ctrlw = maxctrlw3;
+			}
+		}
+	}
+	return 0;
+}
+
+#ifdef DEBUG
+static int slim_verifychansched(struct slim_controller *ctrl, u32 ctrlw,
+				u32 subfrml, u32 clkgear)
+{
+	int sl, i;
+	int cc1 = 0;
+	int cc3 = 0;
+	struct slim_ich *slc = NULL;
+	if (!ctrl->sched.slots)
+		return 0;
+	memset(ctrl->sched.slots, 0, SLIM_SL_PER_SUPERFRAME);
+	dev_dbg(&ctrl->dev, "Clock gear is:%d\n", clkgear);
+	for (sl = 0; sl < SLIM_SL_PER_SUPERFRAME; sl += subfrml) {
+		for (i = 0; i < ctrlw; i++)
+			ctrl->sched.slots[sl + i] = 33;
+	}
+	while (cc1 < ctrl->sched.num_cc1) {
+		slc = ctrl->sched.chc1[cc1];
+		if (slc == NULL) {
+			dev_err(&ctrl->dev, "SLC1 null in verify: chan%d\n",
+				cc1);
+			return -EIO;
+		}
+		dev_dbg(&ctrl->dev, "chan:%d, offset:%d, intr:%d, seglen:%d\n",
+				(slc - ctrl->chans), slc->newoff,
+				slc->newintr, slc->seglen);
+
+		if (slc->state != SLIM_CH_PENDING_REMOVAL) {
+			for (sl = slc->newoff;
+				sl < SLIM_SL_PER_SUPERFRAME;
+				sl += slc->newintr) {
+				for (i = 0; i < slc->seglen; i++) {
+					if (ctrl->sched.slots[sl + i])
+						return -EXFULL;
+					ctrl->sched.slots[sl + i] = cc1 + 1;
+				}
+			}
+		}
+		cc1++;
+	}
+	while (cc3 < ctrl->sched.num_cc3) {
+		slc = ctrl->sched.chc3[cc3];
+		if (slc == NULL) {
+			dev_err(&ctrl->dev, "SLC3 null in verify: chan%d\n",
+				cc3);
+			return -EIO;
+		}
+		dev_dbg(&ctrl->dev, "chan:%d, offset:%d, intr:%d, seglen:%d\n",
+				(slc - ctrl->chans), slc->newoff,
+				slc->newintr, slc->seglen);
+		if (slc->state != SLIM_CH_PENDING_REMOVAL) {
+			for (sl = slc->newoff;
+				sl < SLIM_SL_PER_SUPERFRAME;
+				sl += slc->newintr) {
+				for (i = 0; i < slc->seglen; i++) {
+					if (ctrl->sched.slots[sl + i])
+						return -EXFULL;
+					ctrl->sched.slots[sl + i] = cc3 + 1;
+				}
+			}
+		}
+		cc3++;
+	}
+
+	return 0;
+}
+#else
+static int slim_verifychansched(struct slim_controller *ctrl, u32 ctrlw,
+				u32 subfrml, u32 clkgear)
+{
+	return 0;
+}
+#endif
+
+static void slim_sort_chan_grp(struct slim_controller *ctrl,
+				struct slim_ich *slc)
+{
+	u8  last = (u8)-1;
+	u8 second = 0;
+
+	for (; last > 0; last--) {
+		struct slim_ich *slc1 = slc;
+		struct slim_ich *slc2;
+		u8 next = SLIM_HDL_TO_CHIDX(slc1->nextgrp);
+		slc2 = &ctrl->chans[next];
+		for (second = 1; second <= last && slc2 &&
+			(slc2->state == SLIM_CH_ACTIVE ||
+			 slc2->state == SLIM_CH_PENDING_ACTIVE); second++) {
+			if (slc1->newoff > slc2->newoff) {
+				u32 temp = slc2->newoff;
+				slc2->newoff = slc1->newoff;
+				slc1->newoff = temp;
+			}
+			if (slc2->nextgrp & SLIM_END_GRP) {
+				last = second;
+				break;
+			}
+			slc1 = slc2;
+			next = SLIM_HDL_TO_CHIDX(slc1->nextgrp);
+			slc2 = &ctrl->chans[next];
+		}
+		if (slc2 == NULL)
+			last = second - 1;
+	}
+}
+
+
+static int slim_allocbw(struct slim_device *sb, int *subfrmc, int *clkgear)
+{
+	u32 msgsl = 0;
+	u32 ctrlw = 0;
+	u32 subfrml = 0;
+	int ret = -EIO;
+	struct slim_controller *ctrl = sb->ctrl;
+	u32 usedsl = ctrl->sched.usedslots + ctrl->sched.pending_msgsl;
+	u32 availsl = SLIM_SL_PER_SUPERFRAME - SLIM_FRM_SLOTS_PER_SUPERFRAME -
+			SLIM_GDE_SLOTS_PER_SUPERFRAME;
+	*clkgear = SLIM_MAX_CLK_GEAR;
+
+	dev_dbg(&ctrl->dev, "used sl:%u, availlable sl:%u\n", usedsl, availsl);
+	dev_dbg(&ctrl->dev, "pending:chan sl:%u, :msg sl:%u, clkgear:%u\n",
+				ctrl->sched.usedslots,
+				ctrl->sched.pending_msgsl, *clkgear);
+	/*
+	 * If number of slots are 0, that means channels are inactive.
+	 * It is very likely that the manager will call clock pause very soon.
+	 * By making sure that bus is in MAX_GEAR, clk pause sequence will take
+	 * minimum amount of time.
+	 */
+	if (ctrl->sched.usedslots != 0) {
+		while ((usedsl * 2 <= availsl) && (*clkgear > ctrl->min_cg)) {
+			*clkgear -= 1;
+			usedsl *= 2;
+		}
+	}
+
+	/*
+	 * Try scheduling data channels at current clock gear, if all channels
+	 * can be scheduled, or reserved BW can't be satisfied, increase clock
+	 * gear and try again
+	 */
+	for (; *clkgear <= ctrl->max_cg; (*clkgear)++) {
+		ret = slim_sched_chans(sb, *clkgear, &ctrlw, &subfrml);
+
+		if (ret == 0) {
+			*subfrmc = getsubfrmcoding(&ctrlw, &subfrml, &msgsl);
+			if ((msgsl >> (ctrl->max_cg - *clkgear) <
+				ctrl->sched.pending_msgsl) &&
+				(*clkgear < ctrl->max_cg))
+				continue;
+			else
+				break;
+		}
+	}
+	if (ret == 0) {
+		int i;
+		/* Sort channel-groups */
+		for (i = 0; i < ctrl->sched.num_cc1; i++) {
+			struct slim_ich *slc = ctrl->sched.chc1[i];
+			if (slc->state == SLIM_CH_PENDING_REMOVAL)
+				continue;
+			if ((slc->nextgrp & SLIM_START_GRP) &&
+				!(slc->nextgrp & SLIM_END_GRP)) {
+				slim_sort_chan_grp(ctrl, slc);
+			}
+		}
+		for (i = 0; i < ctrl->sched.num_cc3; i++) {
+			struct slim_ich *slc = ctrl->sched.chc3[i];
+			if (slc->state == SLIM_CH_PENDING_REMOVAL)
+				continue;
+			if ((slc->nextgrp & SLIM_START_GRP) &&
+				!(slc->nextgrp & SLIM_END_GRP)) {
+				slim_sort_chan_grp(ctrl, slc);
+			}
+		}
+
+		ret = slim_verifychansched(ctrl, ctrlw, subfrml, *clkgear);
+	}
+
+	return ret;
+}
+
+static void slim_change_existing_chans(struct slim_controller *ctrl, int coeff)
+{
+	struct slim_ich **arr;
+	int len, i;
+	if (coeff == SLIM_COEFF_1) {
+		arr = ctrl->sched.chc1;
+		len = ctrl->sched.num_cc1;
+	} else {
+		arr = ctrl->sched.chc3;
+		len = ctrl->sched.num_cc3;
+	}
+	for (i = 0; i < len; i++) {
+		struct slim_ich *slc = arr[i];
+		if (slc->state == SLIM_CH_ACTIVE ||
+			slc->state == SLIM_CH_SUSPENDED)
+			slc->offset = slc->newoff;
+			slc->interval = slc->newintr;
+	}
+}
+static void slim_chan_changes(struct slim_device *sb, bool revert)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+	while (!list_empty(&sb->mark_define)) {
+		struct slim_ich *slc;
+		struct slim_pending_ch *pch =
+				list_entry(sb->mark_define.next,
+					struct slim_pending_ch, pending);
+		slc = &ctrl->chans[pch->chan];
+		if (revert) {
+			if (slc->state == SLIM_CH_PENDING_ACTIVE) {
+				u32 sl = slc->seglen << slc->rootexp;
+				if (slc->coeff == SLIM_COEFF_3)
+					sl *= 3;
+				ctrl->sched.usedslots -= sl;
+				slim_remove_ch(ctrl, slc);
+				slc->state = SLIM_CH_DEFINED;
+			}
+		} else {
+			slc->state = SLIM_CH_ACTIVE;
+			slc->def++;
+		}
+		list_del_init(&pch->pending);
+		kfree(pch);
+	}
+
+	while (!list_empty(&sb->mark_removal)) {
+		struct slim_pending_ch *pch =
+				list_entry(sb->mark_removal.next,
+					struct slim_pending_ch, pending);
+		struct slim_ich *slc = &ctrl->chans[pch->chan];
+		u32 sl = slc->seglen << slc->rootexp;
+		if (revert) {
+			if (slc->coeff == SLIM_COEFF_3)
+				sl *= 3;
+			ctrl->sched.usedslots += sl;
+			slc->def = 1;
+			slc->state = SLIM_CH_ACTIVE;
+		} else
+			slim_remove_ch(ctrl, slc);
+		list_del_init(&pch->pending);
+		kfree(pch);
+	}
+
+	while (!list_empty(&sb->mark_suspend)) {
+		struct slim_pending_ch *pch =
+				list_entry(sb->mark_suspend.next,
+					struct slim_pending_ch, pending);
+		struct slim_ich *slc = &ctrl->chans[pch->chan];
+		if (revert)
+			slc->state = SLIM_CH_ACTIVE;
+		list_del_init(&pch->pending);
+		kfree(pch);
+	}
+	/* Change already active channel if reconfig succeeded */
+	if (!revert) {
+		slim_change_existing_chans(ctrl, SLIM_COEFF_1);
+		slim_change_existing_chans(ctrl, SLIM_COEFF_3);
+	}
+}
+
+/*
+ * slim_reconfigure_now: Request reconfiguration now.
+ * @sb: client handle
+ * This API does what commit flag in other scheduling APIs do.
+ * -EXFULL is returned if there is no space in TDM to reserve the
+ * bandwidth. -EBUSY is returned if reconfiguration request is already in
+ * progress.
+ */
+int slim_reconfigure_now(struct slim_device *sb)
+{
+	u8 i;
+	u8 wbuf[4];
+	u32 clkgear, subframe;
+	u32 curexp;
+	int ret;
+	struct slim_controller *ctrl = sb->ctrl;
+	u32 expshft;
+	u32 segdist;
+	struct slim_pending_ch *pch;
+
+	mutex_lock(&ctrl->sched.m_reconf);
+	mutex_lock(&ctrl->m_ctrl);
+	ctrl->sched.pending_msgsl += sb->pending_msgsl - sb->cur_msgsl;
+	list_for_each_entry(pch, &sb->mark_define, pending) {
+		struct slim_ich *slc = &ctrl->chans[pch->chan];
+		slim_add_ch(ctrl, slc);
+		if (slc->state < SLIM_CH_ACTIVE)
+			slc->state = SLIM_CH_PENDING_ACTIVE;
+	}
+
+	list_for_each_entry(pch, &sb->mark_removal, pending) {
+		struct slim_ich *slc = &ctrl->chans[pch->chan];
+		u32 sl = slc->seglen << slc->rootexp;
+		if (slc->coeff == SLIM_COEFF_3)
+			sl *= 3;
+		ctrl->sched.usedslots -= sl;
+		slc->state = SLIM_CH_PENDING_REMOVAL;
+	}
+	list_for_each_entry(pch, &sb->mark_suspend, pending) {
+		struct slim_ich *slc = &ctrl->chans[pch->chan];
+		slc->state = SLIM_CH_SUSPENDED;
+	}
+	mutex_unlock(&ctrl->m_ctrl);
+
+	ret = slim_allocbw(sb, &subframe, &clkgear);
+
+	if (!ret) {
+		ret = slim_processtxn(ctrl, SLIM_MSG_DEST_BROADCAST,
+			SLIM_MSG_MC_BEGIN_RECONFIGURATION, 0, SLIM_MSG_MT_CORE,
+			NULL, NULL, 0, 3, NULL, 0, NULL);
+		dev_dbg(&ctrl->dev, "sending begin_reconfig:ret:%d\n", ret);
+	}
+
+	if (!ret && subframe != ctrl->sched.subfrmcode) {
+		wbuf[0] = (u8)(subframe & 0xFF);
+		ret = slim_processtxn(ctrl, SLIM_MSG_DEST_BROADCAST,
+			SLIM_MSG_MC_NEXT_SUBFRAME_MODE, 0, SLIM_MSG_MT_CORE,
+			NULL, (u8 *)&subframe, 1, 4, NULL, 0, NULL);
+		dev_dbg(&ctrl->dev, "sending subframe:%d,ret:%d\n",
+				(int)wbuf[0], ret);
+	}
+	if (!ret && clkgear != ctrl->clkgear) {
+		wbuf[0] = (u8)(clkgear & 0xFF);
+		ret = slim_processtxn(ctrl, SLIM_MSG_DEST_BROADCAST,
+			SLIM_MSG_MC_NEXT_CLOCK_GEAR, 0, SLIM_MSG_MT_CORE,
+			NULL, wbuf, 1, 4, NULL, 0, NULL);
+		dev_dbg(&ctrl->dev, "sending clkgear:%d,ret:%d\n",
+				(int)wbuf[0], ret);
+	}
+	if (ret)
+		goto revert_reconfig;
+
+	expshft = SLIM_MAX_CLK_GEAR - clkgear;
+	/* activate/remove channel */
+	list_for_each_entry(pch, &sb->mark_define, pending) {
+		struct slim_ich *slc = &ctrl->chans[pch->chan];
+		/* Define content */
+		wbuf[0] = slc->chan;
+		wbuf[1] = slc->prrate;
+		wbuf[2] = slc->prop.dataf | (slc->prop.auxf << 4);
+		wbuf[3] = slc->prop.sampleszbits / SLIM_CL_PER_SL;
+		dev_dbg(&ctrl->dev, "define content, activate:%x, %x, %x, %x\n",
+				wbuf[0], wbuf[1], wbuf[2], wbuf[3]);
+		/* Right now, channel link bit is not supported */
+		ret = slim_processtxn(ctrl, SLIM_MSG_DEST_BROADCAST,
+				SLIM_MSG_MC_NEXT_DEFINE_CONTENT, 0,
+				SLIM_MSG_MT_CORE, NULL, (u8 *)&wbuf, 4, 7,
+				NULL, 0, NULL);
+		if (ret)
+			goto revert_reconfig;
+
+		ret = slim_processtxn(ctrl, SLIM_MSG_DEST_BROADCAST,
+				SLIM_MSG_MC_NEXT_ACTIVATE_CHANNEL, 0,
+				SLIM_MSG_MT_CORE, NULL, (u8 *)&wbuf, 1, 4,
+				NULL, 0, NULL);
+		if (ret)
+			goto revert_reconfig;
+	}
+
+	list_for_each_entry(pch, &sb->mark_removal, pending) {
+		struct slim_ich *slc = &ctrl->chans[pch->chan];
+		dev_dbg(&ctrl->dev, "remove chan:%x\n", pch->chan);
+		wbuf[0] = slc->chan;
+		ret = slim_processtxn(ctrl, SLIM_MSG_DEST_BROADCAST,
+				SLIM_MSG_MC_NEXT_REMOVE_CHANNEL, 0,
+				SLIM_MSG_MT_CORE, NULL, wbuf, 1, 4,
+				NULL, 0, NULL);
+		if (ret)
+			goto revert_reconfig;
+	}
+	list_for_each_entry(pch, &sb->mark_suspend, pending) {
+		struct slim_ich *slc = &ctrl->chans[pch->chan];
+		dev_dbg(&ctrl->dev, "suspend chan:%x\n", pch->chan);
+		wbuf[0] = slc->chan;
+		ret = slim_processtxn(ctrl, SLIM_MSG_DEST_BROADCAST,
+				SLIM_MSG_MC_NEXT_DEACTIVATE_CHANNEL, 0,
+				SLIM_MSG_MT_CORE, NULL, wbuf, 1, 4,
+				NULL, 0, NULL);
+		if (ret)
+			goto revert_reconfig;
+	}
+
+	/* Define CC1 channel */
+	for (i = 0; i < ctrl->sched.num_cc1; i++) {
+		struct slim_ich *slc = ctrl->sched.chc1[i];
+		if (slc->state == SLIM_CH_PENDING_REMOVAL)
+			continue;
+		curexp = slc->rootexp + expshft;
+		segdist = (slc->newoff << curexp) & 0x1FF;
+		expshft = SLIM_MAX_CLK_GEAR - clkgear;
+		dev_dbg(&ctrl->dev, "new-intr:%d, old-intr:%d, dist:%d\n",
+				slc->newintr, slc->interval, segdist);
+		dev_dbg(&ctrl->dev, "new-off:%d, old-off:%d\n",
+				slc->newoff, slc->offset);
+
+		if (slc->state < SLIM_CH_ACTIVE || slc->def < slc->ref ||
+			slc->newintr != slc->interval ||
+			slc->newoff != slc->offset) {
+			segdist |= 0x200;
+			segdist >>= curexp;
+			segdist |= (slc->newoff << (curexp + 1)) & 0xC00;
+			wbuf[0] = slc->chan;
+			wbuf[1] = (u8)(segdist & 0xFF);
+			wbuf[2] = (u8)((segdist & 0xF00) >> 8) |
+					(slc->prop.prot << 4);
+			wbuf[3] = slc->seglen;
+			ret = slim_processtxn(ctrl, SLIM_MSG_DEST_BROADCAST,
+					SLIM_MSG_MC_NEXT_DEFINE_CHANNEL, 0,
+					SLIM_MSG_MT_CORE, NULL, (u8 *)wbuf, 4,
+					7, NULL, 0, NULL);
+			if (ret)
+				goto revert_reconfig;
+		}
+	}
+
+	/* Define CC3 channels */
+	for (i = 0; i < ctrl->sched.num_cc3; i++) {
+		struct slim_ich *slc = ctrl->sched.chc3[i];
+		if (slc->state == SLIM_CH_PENDING_REMOVAL)
+			continue;
+		curexp = slc->rootexp + expshft;
+		segdist = (slc->newoff << curexp) & 0x1FF;
+		expshft = SLIM_MAX_CLK_GEAR - clkgear;
+		dev_dbg(&ctrl->dev, "new-intr:%d, old-intr:%d, dist:%d\n",
+				slc->newintr, slc->interval, segdist);
+		dev_dbg(&ctrl->dev, "new-off:%d, old-off:%d\n",
+				slc->newoff, slc->offset);
+
+		if (slc->state < SLIM_CH_ACTIVE || slc->def < slc->ref ||
+			slc->newintr != slc->interval ||
+			slc->newoff != slc->offset) {
+			segdist |= 0x200;
+			segdist >>= curexp;
+			segdist |= 0xC00;
+			wbuf[0] = slc->chan;
+			wbuf[1] = (u8)(segdist & 0xFF);
+			wbuf[2] = (u8)((segdist & 0xF00) >> 8) |
+					(slc->prop.prot << 4);
+			wbuf[3] = (u8)(slc->seglen);
+			ret = slim_processtxn(ctrl, SLIM_MSG_DEST_BROADCAST,
+					SLIM_MSG_MC_NEXT_DEFINE_CHANNEL, 0,
+					SLIM_MSG_MT_CORE, NULL, (u8 *)wbuf, 4,
+					7, NULL, 0, NULL);
+			if (ret)
+				goto revert_reconfig;
+		}
+	}
+	ret = slim_processtxn(ctrl, SLIM_MSG_DEST_BROADCAST,
+			SLIM_MSG_MC_RECONFIGURE_NOW, 0, SLIM_MSG_MT_CORE, NULL,
+			NULL, 0, 3, NULL, 0, NULL);
+	dev_dbg(&ctrl->dev, "reconfig now:ret:%d\n", ret);
+	if (!ret) {
+		mutex_lock(&ctrl->m_ctrl);
+		ctrl->sched.subfrmcode = subframe;
+		ctrl->clkgear = clkgear;
+		ctrl->sched.msgsl = ctrl->sched.pending_msgsl;
+		sb->cur_msgsl = sb->pending_msgsl;
+		slim_chan_changes(sb, false);
+		mutex_unlock(&ctrl->m_ctrl);
+		mutex_unlock(&ctrl->sched.m_reconf);
+		return 0;
+	}
+
+revert_reconfig:
+	mutex_lock(&ctrl->m_ctrl);
+	/* Revert channel changes */
+	slim_chan_changes(sb, true);
+	mutex_unlock(&ctrl->m_ctrl);
+	mutex_unlock(&ctrl->sched.m_reconf);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(slim_reconfigure_now);
+
+static int add_pending_ch(struct list_head *listh, u8 chan)
+{
+	struct slim_pending_ch *pch;
+	pch = kmalloc(sizeof(struct slim_pending_ch), GFP_KERNEL);
+	if (!pch)
+		return -ENOMEM;
+	pch->chan = chan;
+	list_add_tail(&pch->pending, listh);
+	return 0;
+}
+
+/*
+ * slim_control_ch: Channel control API.
+ * @sb: client handle
+ * @chanh: group or channel handle to be controlled
+ * @chctrl: Control command (activate/suspend/remove)
+ * @commit: flag to indicate whether the control should take effect right-away.
+ * This API activates, removes or suspends a channel (or group of channels)
+ * chanh indicates the channel or group handle (returned by the define_ch API).
+ * Reconfiguration may be time-consuming since it can change all other active
+ * channel allocations on the bus, change in clock gear used by the slimbus,
+ * and change in the control space width used for messaging.
+ * commit makes sure that multiple channels can be activated/deactivated before
+ * reconfiguration is started.
+ * -EXFULL is returned if there is no space in TDM to reserve the bandwidth.
+ * -EISCONN/-ENOTCONN is returned if the channel is already connected or not
+ * yet defined.
+ * -EINVAL is returned if individual control of a grouped-channel is attempted.
+ */
+int slim_control_ch(struct slim_device *sb, u16 chanh,
+			enum slim_ch_control chctrl, bool commit)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+	int ret = 0;
+	/* Get rid of the group flag in MSB if any */
+	u8 chan = SLIM_HDL_TO_CHIDX(chanh);
+	struct slim_ich *slc = &ctrl->chans[chan];
+	if (!(slc->nextgrp & SLIM_START_GRP))
+		return -EINVAL;
+
+	mutex_lock(&sb->sldev_reconf);
+	mutex_lock(&ctrl->m_ctrl);
+	do {
+		slc = &ctrl->chans[chan];
+		dev_dbg(&ctrl->dev, "chan:%d,ctrl:%d,def:%d", chan, chctrl,
+					slc->def);
+		if (slc->state < SLIM_CH_DEFINED) {
+			ret = -ENOTCONN;
+			break;
+		}
+		if (chctrl == SLIM_CH_SUSPEND) {
+			ret = add_pending_ch(&sb->mark_suspend, chan);
+			if (ret)
+				break;
+		} else if (chctrl == SLIM_CH_ACTIVATE) {
+			if (slc->state > SLIM_CH_ACTIVE) {
+				ret = -EISCONN;
+				break;
+			}
+			ret = add_pending_ch(&sb->mark_define, chan);
+			if (ret)
+				break;
+		} else {
+			if (slc->state < SLIM_CH_ACTIVE) {
+				ret = -ENOTCONN;
+				break;
+			}
+			if (slc->def > 0)
+				slc->def--;
+			if (slc->def == 0)
+				ret = add_pending_ch(&sb->mark_removal, chan);
+			if (ret)
+				break;
+		}
+
+		if (!(slc->nextgrp & SLIM_END_GRP))
+			chan = SLIM_HDL_TO_CHIDX(slc->nextgrp);
+	} while (!(slc->nextgrp & SLIM_END_GRP));
+	mutex_unlock(&ctrl->m_ctrl);
+	if (!ret && commit == true)
+		ret = slim_reconfigure_now(sb);
+	mutex_unlock(&sb->sldev_reconf);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(slim_control_ch);
+
+/*
+ * slim_reservemsg_bw: Request to reserve bandwidth for messages.
+ * @sb: client handle
+ * @bw_bps: message bandwidth in bits per second to be requested
+ * @commit: indicates whether the reconfiguration needs to be acted upon.
+ * This API call can be grouped with slim_control_ch API call with only one of
+ * the APIs specifying the commit flag to avoid reconfiguration being called too
+ * frequently. -EXFULL is returned if there is no space in TDM to reserve the
+ * bandwidth. -EBUSY is returned if reconfiguration is requested, but a request
+ * is already in progress.
+ */
+int slim_reservemsg_bw(struct slim_device *sb, u32 bw_bps, bool commit)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+	int ret = 0;
+	int sl;
+	mutex_lock(&sb->sldev_reconf);
+	if ((bw_bps >> 3) >= ctrl->a_framer->rootfreq)
+		sl = SLIM_SL_PER_SUPERFRAME;
+	else {
+		sl = (bw_bps * (SLIM_CL_PER_SUPERFRAME_DIV8/SLIM_CL_PER_SL/2) +
+			(ctrl->a_framer->rootfreq/2 - 1)) /
+			(ctrl->a_framer->rootfreq/2);
+	}
+	dev_dbg(&ctrl->dev, "request:bw:%d, slots:%d, current:%d\n", bw_bps, sl,
+						sb->cur_msgsl);
+	sb->pending_msgsl = sl;
+	if (commit == true)
+		ret = slim_reconfigure_now(sb);
+	mutex_unlock(&sb->sldev_reconf);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(slim_reservemsg_bw);
+
+/*
+ * slim_ctrl_clk_pause: Called by slimbus controller to request clock to be
+ *	paused or woken up out of clock pause
+ * or woken up from clock pause
+ * @ctrl: controller requesting bus to be paused or woken up
+ * @wakeup: Wakeup this controller from clock pause.
+ * @restart: Restart time value per spec used for clock pause. This value
+ *	isn't used when controller is to be woken up.
+ * This API executes clock pause reconfiguration sequence if wakeup is false.
+ * If wakeup is true, controller's wakeup is called
+ * Slimbus clock is idle and can be disabled by the controller later.
+ */
+int slim_ctrl_clk_pause(struct slim_controller *ctrl, bool wakeup, u8 restart)
+{
+	int ret = 0;
+	int i;
+
+	if (wakeup == false && restart > SLIM_CLK_UNSPECIFIED)
+		return -EINVAL;
+	mutex_lock(&ctrl->m_ctrl);
+	if (wakeup) {
+		if (ctrl->clk_state == SLIM_CLK_ACTIVE) {
+			mutex_unlock(&ctrl->m_ctrl);
+			return 0;
+		}
+		wait_for_completion(&ctrl->pause_comp);
+		/*
+		 * Slimbus framework will call controller wakeup
+		 * Controller should make sure that it sets active framer
+		 * out of clock pause by doing appropriate setting
+		 */
+		if (ctrl->clk_state == SLIM_CLK_PAUSED && ctrl->wakeup)
+			ret = ctrl->wakeup(ctrl);
+		if (!ret)
+			ctrl->clk_state = SLIM_CLK_ACTIVE;
+		mutex_unlock(&ctrl->m_ctrl);
+		return ret;
+	} else {
+		switch (ctrl->clk_state) {
+		case SLIM_CLK_ENTERING_PAUSE:
+		case SLIM_CLK_PAUSE_FAILED:
+			/*
+			 * If controller is already trying to enter clock pause,
+			 * let it finish.
+			 * In case of error, retry
+			 * In both cases, previous clock pause has signalled
+			 * completion.
+			 */
+			wait_for_completion(&ctrl->pause_comp);
+			/* retry upon failure */
+			if (ctrl->clk_state == SLIM_CLK_PAUSE_FAILED) {
+				ctrl->clk_state = SLIM_CLK_ACTIVE;
+				break;
+			} else {
+				mutex_unlock(&ctrl->m_ctrl);
+				/*
+				 * Signal completion so that wakeup can wait on
+				 * it.
+				 */
+				complete(&ctrl->pause_comp);
+				return 0;
+			}
+			break;
+		case SLIM_CLK_PAUSED:
+			/* already paused */
+			mutex_unlock(&ctrl->m_ctrl);
+			return 0;
+		case SLIM_CLK_ACTIVE:
+		default:
+			break;
+		}
+	}
+	/* Pending response for a message */
+	for (i = 0; i < ctrl->last_tid; i++) {
+		if (ctrl->txnt[i]) {
+			ret = -EBUSY;
+			mutex_unlock(&ctrl->m_ctrl);
+			return -EBUSY;
+		}
+	}
+	ctrl->clk_state = SLIM_CLK_ENTERING_PAUSE;
+	mutex_unlock(&ctrl->m_ctrl);
+
+	mutex_lock(&ctrl->sched.m_reconf);
+	/* Data channels active */
+	if (ctrl->sched.usedslots) {
+		ret = -EBUSY;
+		goto clk_pause_ret;
+	}
+
+	ret = slim_processtxn(ctrl, SLIM_MSG_DEST_BROADCAST,
+		SLIM_MSG_CLK_PAUSE_SEQ_FLG | SLIM_MSG_MC_BEGIN_RECONFIGURATION,
+		0, SLIM_MSG_MT_CORE, NULL, NULL, 0, 3, NULL, 0, NULL);
+	if (ret)
+		goto clk_pause_ret;
+
+	ret = slim_processtxn(ctrl, SLIM_MSG_DEST_BROADCAST,
+		SLIM_MSG_CLK_PAUSE_SEQ_FLG | SLIM_MSG_MC_NEXT_PAUSE_CLOCK, 0,
+		SLIM_MSG_MT_CORE, NULL, &restart, 1, 4, NULL, 0, NULL);
+	if (ret)
+		goto clk_pause_ret;
+
+	ret = slim_processtxn(ctrl, SLIM_MSG_DEST_BROADCAST,
+		SLIM_MSG_CLK_PAUSE_SEQ_FLG | SLIM_MSG_MC_RECONFIGURE_NOW, 0,
+		SLIM_MSG_MT_CORE, NULL, NULL, 0, 3, NULL, 0, NULL);
+	if (ret)
+		goto clk_pause_ret;
+
+clk_pause_ret:
+	if (ret)
+		ctrl->clk_state = SLIM_CLK_PAUSE_FAILED;
+	else
+		ctrl->clk_state = SLIM_CLK_PAUSED;
+	complete(&ctrl->pause_comp);
+	mutex_unlock(&ctrl->sched.m_reconf);
+	return ret;
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.1");
+MODULE_DESCRIPTION("Slimbus module");
+MODULE_ALIAS("platform:slimbus");
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 00c0240..6946b066 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -403,6 +403,26 @@
 	help
 	  SPI driver for Nuvoton NUC900 series ARM SoCs
 
+config SPI_QSD
+	tristate "Qualcomm MSM SPI support"
+	default n
+	depends on ARCH_MSM_SCORPION && !MSM_SMP
+	help
+	  Support for Serial Peripheral Interface for Qualcomm MSM
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called spi_qsd.
+
+config SPI_QUP
+	tristate "Qualcomm MSM SPI QUPe Support"
+	depends on ARCH_MSM && !SPI_QSD
+	default n
+	help
+	  Support for Serial Peripheral Interface for Qualcomm Universal
+          Peripheral.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called spi_qsd.
 #
 # Add new SPI master controllers in alphabetical order above this line
 #
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 9d75d21..a593faa 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -45,6 +45,8 @@
 obj-$(CONFIG_SPI_PPC4xx)		+= spi-ppc4xx.o
 obj-$(CONFIG_SPI_PXA2XX)		+= spi-pxa2xx.o
 obj-$(CONFIG_SPI_PXA2XX_PCI)		+= spi-pxa2xx-pci.o
+obj-$(CONFIG_SPI_QSD)			+= spi_qsd.o
+obj-$(CONFIG_SPI_QUP)			+= spi_qsd.o
 obj-$(CONFIG_SPI_RSPI)			+= spi-rspi.o
 obj-$(CONFIG_SPI_S3C24XX)		+= spi-s3c24xx-hw.o
 spi-s3c24xx-hw-y			:= spi-s3c24xx.o
diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c
new file mode 100644
index 0000000..2c86e83
--- /dev/null
+++ b/drivers/spi/spi_qsd.c
@@ -0,0 +1,2175 @@
+/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+/*
+ * SPI driver for Qualcomm MSM platforms
+ *
+ */
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+#include <mach/msm_spi.h>
+#include <linux/dma-mapping.h>
+#include <linux/sched.h>
+#include <mach/dma.h>
+#include <asm/atomic.h>
+#include <linux/mutex.h>
+#include <linux/gpio.h>
+#include <linux/remote_spinlock.h>
+#include <linux/pm_qos.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include "spi_qsd.h"
+
+static inline int msm_spi_configure_gsbi(struct msm_spi *dd,
+					struct platform_device *pdev)
+{
+	struct resource *resource;
+	unsigned long   gsbi_mem_phys_addr;
+	size_t          gsbi_mem_size;
+	void __iomem    *gsbi_base;
+
+	resource  = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!resource)
+		return 0;
+
+	gsbi_mem_phys_addr = resource->start;
+	gsbi_mem_size = resource_size(resource);
+	if (!devm_request_mem_region(&pdev->dev, gsbi_mem_phys_addr,
+					gsbi_mem_size, SPI_DRV_NAME))
+		return -ENXIO;
+
+	gsbi_base = devm_ioremap(&pdev->dev, gsbi_mem_phys_addr,
+					gsbi_mem_size);
+	if (!gsbi_base)
+		return -ENXIO;
+
+	/* Set GSBI to SPI mode */
+	writel_relaxed(GSBI_SPI_CONFIG, gsbi_base + GSBI_CTRL_REG);
+
+	return 0;
+}
+
+static inline void msm_spi_register_init(struct msm_spi *dd)
+{
+	writel_relaxed(0x00000001, dd->base + SPI_SW_RESET);
+	msm_spi_set_state(dd, SPI_OP_STATE_RESET);
+	writel_relaxed(0x00000000, dd->base + SPI_OPERATIONAL);
+	writel_relaxed(0x00000000, dd->base + SPI_CONFIG);
+	writel_relaxed(0x00000000, dd->base + SPI_IO_MODES);
+	if (dd->qup_ver)
+		writel_relaxed(0x00000000, dd->base + QUP_OPERATIONAL_MASK);
+}
+
+static inline int msm_spi_request_gpios(struct msm_spi *dd)
+{
+	int i;
+	int result = 0;
+
+	for (i = 0; i < ARRAY_SIZE(spi_rsrcs); ++i) {
+		if (dd->spi_gpios[i] >= 0) {
+			result = gpio_request(dd->spi_gpios[i], spi_rsrcs[i]);
+			if (result) {
+				dev_err(dd->dev, "%s: gpio_request for pin %d "
+					"failed with error %d\n", __func__,
+					dd->spi_gpios[i], result);
+				goto error;
+			}
+		}
+	}
+	return 0;
+
+error:
+	for (; --i >= 0;) {
+		if (dd->spi_gpios[i] >= 0)
+			gpio_free(dd->spi_gpios[i]);
+	}
+	return result;
+}
+
+static inline void msm_spi_free_gpios(struct msm_spi *dd)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(spi_rsrcs); ++i) {
+		if (dd->spi_gpios[i] >= 0)
+			gpio_free(dd->spi_gpios[i]);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(spi_cs_rsrcs); ++i) {
+		if (dd->cs_gpios[i].valid) {
+			gpio_free(dd->cs_gpios[i].gpio_num);
+			dd->cs_gpios[i].valid = 0;
+		}
+	}
+}
+
+static void msm_spi_clock_set(struct msm_spi *dd, int speed)
+{
+	int rc;
+
+	rc = clk_set_rate(dd->clk, speed);
+	if (!rc)
+		dd->clock_speed = speed;
+}
+
+static int msm_spi_calculate_size(int *fifo_size,
+				  int *block_size,
+				  int block,
+				  int mult)
+{
+	int words;
+
+	switch (block) {
+	case 0:
+		words = 1; /* 4 bytes */
+		break;
+	case 1:
+		words = 4; /* 16 bytes */
+		break;
+	case 2:
+		words = 8; /* 32 bytes */
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (mult) {
+	case 0:
+		*fifo_size = words * 2;
+		break;
+	case 1:
+		*fifo_size = words * 4;
+		break;
+	case 2:
+		*fifo_size = words * 8;
+		break;
+	case 3:
+		*fifo_size = words * 16;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	*block_size = words * sizeof(u32); /* in bytes */
+	return 0;
+}
+
+static void get_next_transfer(struct msm_spi *dd)
+{
+	struct spi_transfer *t = dd->cur_transfer;
+
+	if (t->transfer_list.next != &dd->cur_msg->transfers) {
+		dd->cur_transfer = list_entry(t->transfer_list.next,
+					      struct spi_transfer,
+					      transfer_list);
+		dd->write_buf          = dd->cur_transfer->tx_buf;
+		dd->read_buf           = dd->cur_transfer->rx_buf;
+	}
+}
+
+static void __init msm_spi_calculate_fifo_size(struct msm_spi *dd)
+{
+	u32 spi_iom;
+	int block;
+	int mult;
+
+	spi_iom = readl_relaxed(dd->base + SPI_IO_MODES);
+
+	block = (spi_iom & SPI_IO_M_INPUT_BLOCK_SIZE) >> INPUT_BLOCK_SZ_SHIFT;
+	mult = (spi_iom & SPI_IO_M_INPUT_FIFO_SIZE) >> INPUT_FIFO_SZ_SHIFT;
+	if (msm_spi_calculate_size(&dd->input_fifo_size, &dd->input_block_size,
+				   block, mult)) {
+		goto fifo_size_err;
+	}
+
+	block = (spi_iom & SPI_IO_M_OUTPUT_BLOCK_SIZE) >> OUTPUT_BLOCK_SZ_SHIFT;
+	mult = (spi_iom & SPI_IO_M_OUTPUT_FIFO_SIZE) >> OUTPUT_FIFO_SZ_SHIFT;
+	if (msm_spi_calculate_size(&dd->output_fifo_size,
+				   &dd->output_block_size, block, mult)) {
+		goto fifo_size_err;
+	}
+	/* DM mode is not available for this block size */
+	if (dd->input_block_size == 4 || dd->output_block_size == 4)
+		dd->use_dma = 0;
+
+	/* DM mode is currently unsupported for different block sizes */
+	if (dd->input_block_size != dd->output_block_size)
+		dd->use_dma = 0;
+
+	if (dd->use_dma)
+		dd->burst_size = max(dd->input_block_size, DM_BURST_SIZE);
+
+	return;
+
+fifo_size_err:
+	dd->use_dma = 0;
+	pr_err("%s: invalid FIFO size, SPI_IO_MODES=0x%x\n", __func__, spi_iom);
+	return;
+}
+
+static void msm_spi_read_word_from_fifo(struct msm_spi *dd)
+{
+	u32   data_in;
+	int   i;
+	int   shift;
+
+	data_in = readl_relaxed(dd->base + SPI_INPUT_FIFO);
+	if (dd->read_buf) {
+		for (i = 0; (i < dd->bytes_per_word) &&
+			     dd->rx_bytes_remaining; i++) {
+			/* The data format depends on bytes_per_word:
+			   4 bytes: 0x12345678
+			   3 bytes: 0x00123456
+			   2 bytes: 0x00001234
+			   1 byte : 0x00000012
+			*/
+			shift = 8 * (dd->bytes_per_word - i - 1);
+			*dd->read_buf++ = (data_in & (0xFF << shift)) >> shift;
+			dd->rx_bytes_remaining--;
+		}
+	} else {
+		if (dd->rx_bytes_remaining >= dd->bytes_per_word)
+			dd->rx_bytes_remaining -= dd->bytes_per_word;
+		else
+			dd->rx_bytes_remaining = 0;
+	}
+
+	dd->read_xfr_cnt++;
+	if (dd->multi_xfr) {
+		if (!dd->rx_bytes_remaining)
+			dd->read_xfr_cnt = 0;
+		else if ((dd->read_xfr_cnt * dd->bytes_per_word) ==
+						dd->read_len) {
+			struct spi_transfer *t = dd->cur_rx_transfer;
+			if (t->transfer_list.next != &dd->cur_msg->transfers) {
+				t = list_entry(t->transfer_list.next,
+						struct spi_transfer,
+						transfer_list);
+				dd->read_buf = t->rx_buf;
+				dd->read_len = t->len;
+				dd->read_xfr_cnt = 0;
+				dd->cur_rx_transfer = t;
+			}
+		}
+	}
+}
+
+static inline bool msm_spi_is_valid_state(struct msm_spi *dd)
+{
+	u32 spi_op = readl_relaxed(dd->base + SPI_STATE);
+
+	return spi_op & SPI_OP_STATE_VALID;
+}
+
+static inline int msm_spi_wait_valid(struct msm_spi *dd)
+{
+	unsigned long delay = 0;
+	unsigned long timeout = 0;
+
+	if (dd->clock_speed == 0)
+		return -EINVAL;
+	/*
+	 * Based on the SPI clock speed, sufficient time
+	 * should be given for the SPI state transition
+	 * to occur
+	 */
+	delay = (10 * USEC_PER_SEC) / dd->clock_speed;
+	/*
+	 * For small delay values, the default timeout would
+	 * be one jiffy
+	 */
+	if (delay < SPI_DELAY_THRESHOLD)
+		delay = SPI_DELAY_THRESHOLD;
+
+	/* Adding one to round off to the nearest jiffy */
+	timeout = jiffies + msecs_to_jiffies(delay * SPI_DEFAULT_TIMEOUT) + 1;
+	while (!msm_spi_is_valid_state(dd)) {
+		if (time_after(jiffies, timeout)) {
+			if (!msm_spi_is_valid_state(dd)) {
+				if (dd->cur_msg)
+					dd->cur_msg->status = -EIO;
+				dev_err(dd->dev, "%s: SPI operational state"
+					"not valid\n", __func__);
+				return -ETIMEDOUT;
+			} else
+				return 0;
+		}
+		/*
+		 * For smaller values of delay, context switch time
+		 * would negate the usage of usleep
+		 */
+		if (delay > 20)
+			usleep(delay);
+		else if (delay)
+			udelay(delay);
+	}
+	return 0;
+}
+
+static inline int msm_spi_set_state(struct msm_spi *dd,
+				    enum msm_spi_state state)
+{
+	enum msm_spi_state cur_state;
+	if (msm_spi_wait_valid(dd))
+		return -EIO;
+	cur_state = readl_relaxed(dd->base + SPI_STATE);
+	/* Per spec:
+	   For PAUSE_STATE to RESET_STATE, two writes of (10) are required */
+	if (((cur_state & SPI_OP_STATE) == SPI_OP_STATE_PAUSE) &&
+			(state == SPI_OP_STATE_RESET)) {
+		writel_relaxed(SPI_OP_STATE_CLEAR_BITS, dd->base + SPI_STATE);
+		writel_relaxed(SPI_OP_STATE_CLEAR_BITS, dd->base + SPI_STATE);
+	} else {
+		writel_relaxed((cur_state & ~SPI_OP_STATE) | state,
+		       dd->base + SPI_STATE);
+	}
+	if (msm_spi_wait_valid(dd))
+		return -EIO;
+
+	return 0;
+}
+
+static inline void msm_spi_add_configs(struct msm_spi *dd, u32 *config, int n)
+{
+	*config &= ~(SPI_NO_INPUT|SPI_NO_OUTPUT);
+
+	if (n != (*config & SPI_CFG_N))
+		*config = (*config & ~SPI_CFG_N) | n;
+
+	if ((dd->mode == SPI_DMOV_MODE) && (!dd->read_len)) {
+		if (dd->read_buf == NULL)
+			*config |= SPI_NO_INPUT;
+		if (dd->write_buf == NULL)
+			*config |= SPI_NO_OUTPUT;
+	}
+}
+
+static void msm_spi_set_config(struct msm_spi *dd, int bpw)
+{
+	u32 spi_config;
+
+	spi_config = readl_relaxed(dd->base + SPI_CONFIG);
+
+	if (dd->cur_msg->spi->mode & SPI_CPHA)
+		spi_config &= ~SPI_CFG_INPUT_FIRST;
+	else
+		spi_config |= SPI_CFG_INPUT_FIRST;
+	if (dd->cur_msg->spi->mode & SPI_LOOP)
+		spi_config |= SPI_CFG_LOOPBACK;
+	else
+		spi_config &= ~SPI_CFG_LOOPBACK;
+	msm_spi_add_configs(dd, &spi_config, bpw-1);
+	writel_relaxed(spi_config, dd->base + SPI_CONFIG);
+	msm_spi_set_qup_config(dd, bpw);
+}
+
+static void msm_spi_setup_dm_transfer(struct msm_spi *dd)
+{
+	dmov_box *box;
+	int bytes_to_send, num_rows, bytes_sent;
+	u32 num_transfers;
+
+	atomic_set(&dd->rx_irq_called, 0);
+	atomic_set(&dd->tx_irq_called, 0);
+	if (dd->write_len && !dd->read_len) {
+		/* WR-WR transfer */
+		bytes_sent = dd->cur_msg_len - dd->tx_bytes_remaining;
+		dd->write_buf = dd->temp_buf;
+	} else {
+		bytes_sent = dd->cur_transfer->len - dd->tx_bytes_remaining;
+		/* For WR-RD transfer, bytes_sent can be negative */
+		if (bytes_sent < 0)
+			bytes_sent = 0;
+	}
+
+	/* We'll send in chunks of SPI_MAX_LEN if larger */
+	bytes_to_send = dd->tx_bytes_remaining / SPI_MAX_LEN ?
+			  SPI_MAX_LEN : dd->tx_bytes_remaining;
+	num_transfers = DIV_ROUND_UP(bytes_to_send, dd->bytes_per_word);
+	dd->unaligned_len = bytes_to_send % dd->burst_size;
+	num_rows = bytes_to_send / dd->burst_size;
+
+	dd->mode = SPI_DMOV_MODE;
+
+	if (num_rows) {
+		/* src in 16 MSB, dst in 16 LSB */
+		box = &dd->tx_dmov_cmd->box;
+		box->src_row_addr = dd->cur_transfer->tx_dma + bytes_sent;
+		box->src_dst_len = (dd->burst_size << 16) | dd->burst_size;
+		box->num_rows = (num_rows << 16) | num_rows;
+		box->row_offset = (dd->burst_size << 16) | 0;
+
+		box = &dd->rx_dmov_cmd->box;
+		box->dst_row_addr = dd->cur_transfer->rx_dma + bytes_sent;
+		box->src_dst_len = (dd->burst_size << 16) | dd->burst_size;
+		box->num_rows = (num_rows << 16) | num_rows;
+		box->row_offset = (0 << 16) | dd->burst_size;
+
+		dd->tx_dmov_cmd->cmd_ptr = CMD_PTR_LP |
+				   DMOV_CMD_ADDR(dd->tx_dmov_cmd_dma +
+				   offsetof(struct spi_dmov_cmd, box));
+		dd->rx_dmov_cmd->cmd_ptr = CMD_PTR_LP |
+				   DMOV_CMD_ADDR(dd->rx_dmov_cmd_dma +
+				   offsetof(struct spi_dmov_cmd, box));
+	} else {
+		dd->tx_dmov_cmd->cmd_ptr = CMD_PTR_LP |
+				   DMOV_CMD_ADDR(dd->tx_dmov_cmd_dma +
+				   offsetof(struct spi_dmov_cmd, single_pad));
+		dd->rx_dmov_cmd->cmd_ptr = CMD_PTR_LP |
+				   DMOV_CMD_ADDR(dd->rx_dmov_cmd_dma +
+				   offsetof(struct spi_dmov_cmd, single_pad));
+	}
+
+	if (!dd->unaligned_len) {
+		dd->tx_dmov_cmd->box.cmd |= CMD_LC;
+		dd->rx_dmov_cmd->box.cmd |= CMD_LC;
+	} else {
+		dmov_s *tx_cmd = &(dd->tx_dmov_cmd->single_pad);
+		dmov_s *rx_cmd = &(dd->rx_dmov_cmd->single_pad);
+		u32 offset = dd->cur_transfer->len - dd->unaligned_len;
+
+		if ((dd->multi_xfr) && (dd->read_len <= 0))
+			offset = dd->cur_msg_len - dd->unaligned_len;
+
+		dd->tx_dmov_cmd->box.cmd &= ~CMD_LC;
+		dd->rx_dmov_cmd->box.cmd &= ~CMD_LC;
+
+		memset(dd->tx_padding, 0, dd->burst_size);
+		memset(dd->rx_padding, 0, dd->burst_size);
+		if (dd->write_buf)
+			memcpy(dd->tx_padding, dd->write_buf + offset,
+			       dd->unaligned_len);
+
+		tx_cmd->src = dd->tx_padding_dma;
+		rx_cmd->dst = dd->rx_padding_dma;
+		tx_cmd->len = rx_cmd->len = dd->burst_size;
+	}
+	/* This also takes care of the padding dummy buf
+	   Since this is set to the correct length, the
+	   dummy bytes won't be actually sent */
+	if (dd->multi_xfr) {
+		u32 write_transfers = 0;
+		u32 read_transfers = 0;
+
+		if (dd->write_len > 0) {
+			write_transfers = DIV_ROUND_UP(dd->write_len,
+						       dd->bytes_per_word);
+			writel_relaxed(write_transfers,
+				       dd->base + SPI_MX_OUTPUT_COUNT);
+		}
+		if (dd->read_len > 0) {
+			/*
+			 *  The read following a write transfer must take
+			 *  into account, that the bytes pertaining to
+			 *  the write transfer needs to be discarded,
+			 *  before the actual read begins.
+			 */
+			read_transfers = DIV_ROUND_UP(dd->read_len +
+						      dd->write_len,
+						      dd->bytes_per_word);
+			writel_relaxed(read_transfers,
+				       dd->base + SPI_MX_INPUT_COUNT);
+		}
+	} else {
+		if (dd->write_buf)
+			writel_relaxed(num_transfers,
+				       dd->base + SPI_MX_OUTPUT_COUNT);
+		if (dd->read_buf)
+			writel_relaxed(num_transfers,
+				       dd->base + SPI_MX_INPUT_COUNT);
+	}
+}
+
+static void msm_spi_enqueue_dm_commands(struct msm_spi *dd)
+{
+	dma_coherent_pre_ops();
+	if (dd->write_buf)
+		msm_dmov_enqueue_cmd(dd->tx_dma_chan, &dd->tx_hdr);
+	if (dd->read_buf)
+		msm_dmov_enqueue_cmd(dd->rx_dma_chan, &dd->rx_hdr);
+}
+
+/* SPI core can send maximum of 4K transfers, because there is HW problem
+   with infinite mode.
+   Therefore, we are sending several chunks of 3K or less (depending on how
+   much is left).
+   Upon completion we send the next chunk, or complete the transfer if
+   everything is finished.
+*/
+static int msm_spi_dm_send_next(struct msm_spi *dd)
+{
+	/* By now we should have sent all the bytes in FIFO mode,
+	 * However to make things right, we'll check anyway.
+	 */
+	if (dd->mode != SPI_DMOV_MODE)
+		return 0;
+
+	/* We need to send more chunks, if we sent max last time */
+	if (dd->tx_bytes_remaining > SPI_MAX_LEN) {
+		dd->tx_bytes_remaining -= SPI_MAX_LEN;
+		if (msm_spi_set_state(dd, SPI_OP_STATE_RESET))
+			return 0;
+		dd->read_len = dd->write_len = 0;
+		msm_spi_setup_dm_transfer(dd);
+		msm_spi_enqueue_dm_commands(dd);
+		if (msm_spi_set_state(dd, SPI_OP_STATE_RUN))
+			return 0;
+		return 1;
+	} else if (dd->read_len && dd->write_len) {
+		dd->tx_bytes_remaining -= dd->cur_transfer->len;
+		if (list_is_last(&dd->cur_transfer->transfer_list,
+					    &dd->cur_msg->transfers))
+			return 0;
+		get_next_transfer(dd);
+		if (msm_spi_set_state(dd, SPI_OP_STATE_PAUSE))
+			return 0;
+		dd->tx_bytes_remaining = dd->read_len + dd->write_len;
+		dd->read_buf = dd->temp_buf;
+		dd->read_len = dd->write_len = -1;
+		msm_spi_setup_dm_transfer(dd);
+		msm_spi_enqueue_dm_commands(dd);
+		if (msm_spi_set_state(dd, SPI_OP_STATE_RUN))
+			return 0;
+		return 1;
+	}
+	return 0;
+}
+
+static inline void msm_spi_ack_transfer(struct msm_spi *dd)
+{
+	writel_relaxed(SPI_OP_MAX_INPUT_DONE_FLAG |
+		       SPI_OP_MAX_OUTPUT_DONE_FLAG,
+		       dd->base + SPI_OPERATIONAL);
+	/* Ensure done flag was cleared before proceeding further */
+	mb();
+}
+
+/* Figure which irq occured and call the relevant functions */
+static inline irqreturn_t msm_spi_qup_irq(int irq, void *dev_id)
+{
+	u32 op, ret = IRQ_NONE;
+	struct msm_spi *dd = dev_id;
+
+	if (readl_relaxed(dd->base + SPI_ERROR_FLAGS) ||
+	    readl_relaxed(dd->base + QUP_ERROR_FLAGS)) {
+		struct spi_master *master = dev_get_drvdata(dd->dev);
+		ret |= msm_spi_error_irq(irq, master);
+	}
+
+	op = readl_relaxed(dd->base + SPI_OPERATIONAL);
+	if (op & SPI_OP_INPUT_SERVICE_FLAG) {
+		writel_relaxed(SPI_OP_INPUT_SERVICE_FLAG,
+			       dd->base + SPI_OPERATIONAL);
+		/*
+		 * Ensure service flag was cleared before further
+		 * processing of interrupt.
+		 */
+		mb();
+		ret |= msm_spi_input_irq(irq, dev_id);
+	}
+
+	if (op & SPI_OP_OUTPUT_SERVICE_FLAG) {
+		writel_relaxed(SPI_OP_OUTPUT_SERVICE_FLAG,
+			       dd->base + SPI_OPERATIONAL);
+		/*
+		 * Ensure service flag was cleared before further
+		 * processing of interrupt.
+		 */
+		mb();
+		ret |= msm_spi_output_irq(irq, dev_id);
+	}
+
+	if (dd->done) {
+		complete(&dd->transfer_complete);
+		dd->done = 0;
+	}
+	return ret;
+}
+
+static irqreturn_t msm_spi_input_irq(int irq, void *dev_id)
+{
+	struct msm_spi	       *dd = dev_id;
+
+	dd->stat_rx++;
+
+	if (dd->mode == SPI_MODE_NONE)
+		return IRQ_HANDLED;
+
+	if (dd->mode == SPI_DMOV_MODE) {
+		u32 op = readl_relaxed(dd->base + SPI_OPERATIONAL);
+		if ((!dd->read_buf || op & SPI_OP_MAX_INPUT_DONE_FLAG) &&
+		    (!dd->write_buf || op & SPI_OP_MAX_OUTPUT_DONE_FLAG)) {
+			msm_spi_ack_transfer(dd);
+			if (dd->unaligned_len == 0) {
+				if (atomic_inc_return(&dd->rx_irq_called) == 1)
+					return IRQ_HANDLED;
+			}
+			msm_spi_complete(dd);
+			return IRQ_HANDLED;
+		}
+		return IRQ_NONE;
+	}
+
+	if (dd->mode == SPI_FIFO_MODE) {
+		while ((readl_relaxed(dd->base + SPI_OPERATIONAL) &
+			SPI_OP_IP_FIFO_NOT_EMPTY) &&
+			(dd->rx_bytes_remaining > 0)) {
+			msm_spi_read_word_from_fifo(dd);
+		}
+		if (dd->rx_bytes_remaining == 0)
+			msm_spi_complete(dd);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void msm_spi_write_word_to_fifo(struct msm_spi *dd)
+{
+	u32    word;
+	u8     byte;
+	int    i;
+
+	word = 0;
+	if (dd->write_buf) {
+		for (i = 0; (i < dd->bytes_per_word) &&
+			     dd->tx_bytes_remaining; i++) {
+			dd->tx_bytes_remaining--;
+			byte = *dd->write_buf++;
+			word |= (byte << (BITS_PER_BYTE * (3 - i)));
+		}
+	} else
+		if (dd->tx_bytes_remaining > dd->bytes_per_word)
+			dd->tx_bytes_remaining -= dd->bytes_per_word;
+		else
+			dd->tx_bytes_remaining = 0;
+	dd->write_xfr_cnt++;
+	if (dd->multi_xfr) {
+		if (!dd->tx_bytes_remaining)
+			dd->write_xfr_cnt = 0;
+		else if ((dd->write_xfr_cnt * dd->bytes_per_word) ==
+						dd->write_len) {
+			struct spi_transfer *t = dd->cur_tx_transfer;
+			if (t->transfer_list.next != &dd->cur_msg->transfers) {
+				t = list_entry(t->transfer_list.next,
+						struct spi_transfer,
+						transfer_list);
+				dd->write_buf = t->tx_buf;
+				dd->write_len = t->len;
+				dd->write_xfr_cnt = 0;
+				dd->cur_tx_transfer = t;
+			}
+		}
+	}
+	writel_relaxed(word, dd->base + SPI_OUTPUT_FIFO);
+}
+
+static inline void msm_spi_write_rmn_to_fifo(struct msm_spi *dd)
+{
+	int count = 0;
+
+	while ((dd->tx_bytes_remaining > 0) && (count < dd->input_fifo_size) &&
+	       !(readl_relaxed(dd->base + SPI_OPERATIONAL) &
+		SPI_OP_OUTPUT_FIFO_FULL)) {
+		msm_spi_write_word_to_fifo(dd);
+		count++;
+	}
+}
+
+static irqreturn_t msm_spi_output_irq(int irq, void *dev_id)
+{
+	struct msm_spi	       *dd = dev_id;
+
+	dd->stat_tx++;
+
+	if (dd->mode == SPI_MODE_NONE)
+		return IRQ_HANDLED;
+
+	if (dd->mode == SPI_DMOV_MODE) {
+		/* TX_ONLY transaction is handled here
+		   This is the only place we send complete at tx and not rx */
+		if (dd->read_buf == NULL &&
+		    readl_relaxed(dd->base + SPI_OPERATIONAL) &
+		    SPI_OP_MAX_OUTPUT_DONE_FLAG) {
+			msm_spi_ack_transfer(dd);
+			if (atomic_inc_return(&dd->tx_irq_called) == 1)
+				return IRQ_HANDLED;
+			msm_spi_complete(dd);
+			return IRQ_HANDLED;
+		}
+		return IRQ_NONE;
+	}
+
+	/* Output FIFO is empty. Transmit any outstanding write data. */
+	if (dd->mode == SPI_FIFO_MODE)
+		msm_spi_write_rmn_to_fifo(dd);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t msm_spi_error_irq(int irq, void *dev_id)
+{
+	struct spi_master	*master = dev_id;
+	struct msm_spi          *dd = spi_master_get_devdata(master);
+	u32                      spi_err;
+
+	spi_err = readl_relaxed(dd->base + SPI_ERROR_FLAGS);
+	if (spi_err & SPI_ERR_OUTPUT_OVER_RUN_ERR)
+		dev_warn(master->dev.parent, "SPI output overrun error\n");
+	if (spi_err & SPI_ERR_INPUT_UNDER_RUN_ERR)
+		dev_warn(master->dev.parent, "SPI input underrun error\n");
+	if (spi_err & SPI_ERR_OUTPUT_UNDER_RUN_ERR)
+		dev_warn(master->dev.parent, "SPI output underrun error\n");
+	msm_spi_get_clk_err(dd, &spi_err);
+	if (spi_err & SPI_ERR_CLK_OVER_RUN_ERR)
+		dev_warn(master->dev.parent, "SPI clock overrun error\n");
+	if (spi_err & SPI_ERR_CLK_UNDER_RUN_ERR)
+		dev_warn(master->dev.parent, "SPI clock underrun error\n");
+	msm_spi_clear_error_flags(dd);
+	msm_spi_ack_clk_err(dd);
+	/* Ensure clearing of QUP_ERROR_FLAGS was completed */
+	mb();
+	return IRQ_HANDLED;
+}
+
+static int msm_spi_map_dma_buffers(struct msm_spi *dd)
+{
+	struct device *dev;
+	struct spi_transfer *first_xfr;
+	struct spi_transfer *nxt_xfr = NULL;
+	void *tx_buf, *rx_buf;
+	unsigned tx_len, rx_len;
+	int ret = -EINVAL;
+
+	dev = &dd->cur_msg->spi->dev;
+	first_xfr = dd->cur_transfer;
+	tx_buf = (void *)first_xfr->tx_buf;
+	rx_buf = first_xfr->rx_buf;
+	tx_len = rx_len = first_xfr->len;
+
+	/*
+	 * For WR-WR and WR-RD transfers, we allocate our own temporary
+	 * buffer and copy the data to/from the client buffers.
+	 */
+	if (dd->multi_xfr) {
+		dd->temp_buf = kzalloc(dd->cur_msg_len,
+				       GFP_KERNEL | __GFP_DMA);
+		if (!dd->temp_buf)
+			return -ENOMEM;
+		nxt_xfr = list_entry(first_xfr->transfer_list.next,
+				     struct spi_transfer, transfer_list);
+
+		if (dd->write_len && !dd->read_len) {
+			if (!first_xfr->tx_buf || !nxt_xfr->tx_buf)
+				goto error;
+
+			memcpy(dd->temp_buf, first_xfr->tx_buf, first_xfr->len);
+			memcpy(dd->temp_buf + first_xfr->len, nxt_xfr->tx_buf,
+			       nxt_xfr->len);
+			tx_buf = dd->temp_buf;
+			tx_len = dd->cur_msg_len;
+		} else {
+			if (!first_xfr->tx_buf || !nxt_xfr->rx_buf)
+				goto error;
+
+			rx_buf = dd->temp_buf;
+			rx_len = dd->cur_msg_len;
+		}
+	}
+	if (tx_buf != NULL) {
+		first_xfr->tx_dma = dma_map_single(dev, tx_buf,
+						   tx_len, DMA_TO_DEVICE);
+		if (dma_mapping_error(NULL, first_xfr->tx_dma)) {
+			dev_err(dev, "dma %cX %d bytes error\n",
+				'T', tx_len);
+			ret = -ENOMEM;
+			goto error;
+		}
+	}
+	if (rx_buf != NULL) {
+		dma_addr_t dma_handle;
+		dma_handle = dma_map_single(dev, rx_buf,
+					    rx_len, DMA_FROM_DEVICE);
+		if (dma_mapping_error(NULL, dma_handle)) {
+			dev_err(dev, "dma %cX %d bytes error\n",
+				'R', rx_len);
+			if (tx_buf != NULL)
+				dma_unmap_single(NULL, first_xfr->tx_dma,
+						 tx_len, DMA_TO_DEVICE);
+			ret = -ENOMEM;
+			goto error;
+		}
+		if (dd->multi_xfr)
+			nxt_xfr->rx_dma = dma_handle;
+		else
+			first_xfr->rx_dma = dma_handle;
+	}
+	return 0;
+
+error:
+	kfree(dd->temp_buf);
+	dd->temp_buf = NULL;
+	return ret;
+}
+
+static void msm_spi_unmap_dma_buffers(struct msm_spi *dd)
+{
+	struct device *dev;
+	u32 offset;
+
+	dev = &dd->cur_msg->spi->dev;
+	if (dd->cur_msg->is_dma_mapped)
+		goto unmap_end;
+
+	if (dd->multi_xfr) {
+		if (dd->write_len && !dd->read_len) {
+			dma_unmap_single(dev,
+					 dd->cur_transfer->tx_dma,
+					 dd->cur_msg_len,
+					 DMA_TO_DEVICE);
+		} else {
+			struct spi_transfer *prev_xfr;
+			prev_xfr = list_entry(
+				   dd->cur_transfer->transfer_list.prev,
+				   struct spi_transfer,
+				   transfer_list);
+			if (dd->cur_transfer->rx_buf) {
+				dma_unmap_single(dev,
+						 dd->cur_transfer->rx_dma,
+						 dd->cur_msg_len,
+						 DMA_FROM_DEVICE);
+			}
+			if (prev_xfr->tx_buf) {
+				dma_unmap_single(dev,
+						 prev_xfr->tx_dma,
+						 prev_xfr->len,
+						 DMA_TO_DEVICE);
+			}
+			if (dd->unaligned_len && dd->read_buf) {
+				offset = dd->cur_msg_len - dd->unaligned_len;
+				dma_coherent_post_ops();
+				memcpy(dd->read_buf + offset, dd->rx_padding,
+				       dd->unaligned_len);
+				memcpy(dd->cur_transfer->rx_buf,
+				       dd->read_buf + prev_xfr->len,
+				       dd->cur_transfer->len);
+			}
+		}
+		kfree(dd->temp_buf);
+		dd->temp_buf = NULL;
+		return;
+	} else {
+		if (dd->cur_transfer->rx_buf)
+			dma_unmap_single(dev, dd->cur_transfer->rx_dma,
+					 dd->cur_transfer->len,
+					 DMA_FROM_DEVICE);
+		if (dd->cur_transfer->tx_buf)
+			dma_unmap_single(dev, dd->cur_transfer->tx_dma,
+					 dd->cur_transfer->len,
+					 DMA_TO_DEVICE);
+	}
+
+unmap_end:
+	/* If we padded the transfer, we copy it from the padding buf */
+	if (dd->unaligned_len && dd->read_buf) {
+		offset = dd->cur_transfer->len - dd->unaligned_len;
+		dma_coherent_post_ops();
+		memcpy(dd->read_buf + offset, dd->rx_padding,
+		       dd->unaligned_len);
+	}
+}
+
+/**
+ * msm_use_dm - decides whether to use data mover for this
+ * 		transfer
+ * @dd:       device
+ * @tr:       transfer
+ *
+ * Start using DM if:
+ * 1. Transfer is longer than 3*block size.
+ * 2. Buffers should be aligned to cache line.
+ * 3. For WR-RD or WR-WR transfers, if condition (1) and (2) above are met.
+  */
+static inline int msm_use_dm(struct msm_spi *dd, struct spi_transfer *tr,
+			     u8 bpw)
+{
+	u32 cache_line = dma_get_cache_alignment();
+
+	if (!dd->use_dma)
+		return 0;
+
+	if (dd->cur_msg_len < 3*dd->input_block_size)
+		return 0;
+
+	if (dd->multi_xfr && !dd->read_len && !dd->write_len)
+		return 0;
+
+	if (tr->tx_buf) {
+		if (!IS_ALIGNED((size_t)tr->tx_buf, cache_line))
+			return 0;
+	}
+	if (tr->rx_buf) {
+		if (!IS_ALIGNED((size_t)tr->rx_buf, cache_line))
+			return 0;
+	}
+
+	if (tr->cs_change &&
+	   ((bpw != 8) || (bpw != 16) || (bpw != 32)))
+		return 0;
+	return 1;
+}
+
+static void msm_spi_process_transfer(struct msm_spi *dd)
+{
+	u8  bpw;
+	u32 spi_ioc;
+	u32 spi_iom;
+	u32 spi_ioc_orig;
+	u32 max_speed;
+	u32 chip_select;
+	u32 read_count;
+	u32 timeout;
+	u32 int_loopback = 0;
+
+	dd->tx_bytes_remaining = dd->cur_msg_len;
+	dd->rx_bytes_remaining = dd->cur_msg_len;
+	dd->read_buf           = dd->cur_transfer->rx_buf;
+	dd->write_buf          = dd->cur_transfer->tx_buf;
+	init_completion(&dd->transfer_complete);
+	if (dd->cur_transfer->bits_per_word)
+		bpw = dd->cur_transfer->bits_per_word;
+	else
+		if (dd->cur_msg->spi->bits_per_word)
+			bpw = dd->cur_msg->spi->bits_per_word;
+		else
+			bpw = 8;
+	dd->bytes_per_word = (bpw + 7) / 8;
+
+	if (dd->cur_transfer->speed_hz)
+		max_speed = dd->cur_transfer->speed_hz;
+	else
+		max_speed = dd->cur_msg->spi->max_speed_hz;
+	if (!dd->clock_speed || max_speed != dd->clock_speed)
+		msm_spi_clock_set(dd, max_speed);
+
+	read_count = DIV_ROUND_UP(dd->cur_msg_len, dd->bytes_per_word);
+	if (dd->cur_msg->spi->mode & SPI_LOOP)
+		int_loopback = 1;
+	if (int_loopback && dd->multi_xfr &&
+			(read_count > dd->input_fifo_size)) {
+		if (dd->read_len && dd->write_len)
+			pr_err(
+			"%s:Internal Loopback does not support > fifo size"
+			"for write-then-read transactions\n",
+			__func__);
+		else if (dd->write_len && !dd->read_len)
+			pr_err(
+			"%s:Internal Loopback does not support > fifo size"
+			"for write-then-write transactions\n",
+			__func__);
+		return;
+	}
+	if (!msm_use_dm(dd, dd->cur_transfer, bpw)) {
+		dd->mode = SPI_FIFO_MODE;
+		if (dd->multi_xfr) {
+			dd->read_len = dd->cur_transfer->len;
+			dd->write_len = dd->cur_transfer->len;
+		}
+		/* read_count cannot exceed fifo_size, and only one READ COUNT
+		   interrupt is generated per transaction, so for transactions
+		   larger than fifo size READ COUNT must be disabled.
+		   For those transactions we usually move to Data Mover mode.
+		*/
+		if (read_count <= dd->input_fifo_size) {
+			writel_relaxed(read_count,
+				       dd->base + SPI_MX_READ_COUNT);
+			msm_spi_set_write_count(dd, read_count);
+		} else {
+			writel_relaxed(0, dd->base + SPI_MX_READ_COUNT);
+			msm_spi_set_write_count(dd, 0);
+		}
+	} else {
+		dd->mode = SPI_DMOV_MODE;
+		if (dd->write_len && dd->read_len) {
+			dd->tx_bytes_remaining = dd->write_len;
+			dd->rx_bytes_remaining = dd->read_len;
+		}
+	}
+
+	/* Write mode - fifo or data mover*/
+	spi_iom = readl_relaxed(dd->base + SPI_IO_MODES);
+	spi_iom &= ~(SPI_IO_M_INPUT_MODE | SPI_IO_M_OUTPUT_MODE);
+	spi_iom = (spi_iom | (dd->mode << OUTPUT_MODE_SHIFT));
+	spi_iom = (spi_iom | (dd->mode << INPUT_MODE_SHIFT));
+	/* Turn on packing for data mover */
+	if (dd->mode == SPI_DMOV_MODE)
+		spi_iom |= SPI_IO_M_PACK_EN | SPI_IO_M_UNPACK_EN;
+	else
+		spi_iom &= ~(SPI_IO_M_PACK_EN | SPI_IO_M_UNPACK_EN);
+	writel_relaxed(spi_iom, dd->base + SPI_IO_MODES);
+
+	msm_spi_set_config(dd, bpw);
+
+	spi_ioc = readl_relaxed(dd->base + SPI_IO_CONTROL);
+	spi_ioc_orig = spi_ioc;
+	if (dd->cur_msg->spi->mode & SPI_CPOL)
+		spi_ioc |= SPI_IO_C_CLK_IDLE_HIGH;
+	else
+		spi_ioc &= ~SPI_IO_C_CLK_IDLE_HIGH;
+	chip_select = dd->cur_msg->spi->chip_select << 2;
+	if ((spi_ioc & SPI_IO_C_CS_SELECT) != chip_select)
+		spi_ioc = (spi_ioc & ~SPI_IO_C_CS_SELECT) | chip_select;
+	if (!dd->cur_transfer->cs_change)
+		spi_ioc |= SPI_IO_C_MX_CS_MODE;
+	if (spi_ioc != spi_ioc_orig)
+		writel_relaxed(spi_ioc, dd->base + SPI_IO_CONTROL);
+
+	if (dd->mode == SPI_DMOV_MODE) {
+		msm_spi_setup_dm_transfer(dd);
+		msm_spi_enqueue_dm_commands(dd);
+	}
+	/* The output fifo interrupt handler will handle all writes after
+	   the first. Restricting this to one write avoids contention
+	   issues and race conditions between this thread and the int handler
+	*/
+	else if (dd->mode == SPI_FIFO_MODE) {
+		if (msm_spi_prepare_for_write(dd))
+			goto transfer_end;
+		msm_spi_start_write(dd, read_count);
+	}
+
+	/* Only enter the RUN state after the first word is written into
+	   the output FIFO. Otherwise, the output FIFO EMPTY interrupt
+	   might fire before the first word is written resulting in a
+	   possible race condition.
+	 */
+	if (msm_spi_set_state(dd, SPI_OP_STATE_RUN))
+		goto transfer_end;
+
+	timeout = 100 * msecs_to_jiffies(
+	      DIV_ROUND_UP(dd->cur_msg_len * 8,
+		 DIV_ROUND_UP(max_speed, MSEC_PER_SEC)));
+
+	/* Assume success, this might change later upon transaction result */
+	dd->cur_msg->status = 0;
+	do {
+		if (!wait_for_completion_timeout(&dd->transfer_complete,
+						 timeout)) {
+				dev_err(dd->dev, "%s: SPI transaction "
+						 "timeout\n", __func__);
+				dd->cur_msg->status = -EIO;
+				if (dd->mode == SPI_DMOV_MODE) {
+					msm_dmov_flush(dd->tx_dma_chan, 1);
+					msm_dmov_flush(dd->rx_dma_chan, 1);
+				}
+				break;
+		}
+	} while (msm_spi_dm_send_next(dd));
+
+transfer_end:
+	if (dd->mode == SPI_DMOV_MODE)
+		msm_spi_unmap_dma_buffers(dd);
+	dd->mode = SPI_MODE_NONE;
+
+	msm_spi_set_state(dd, SPI_OP_STATE_RESET);
+	writel_relaxed(spi_ioc & ~SPI_IO_C_MX_CS_MODE,
+		       dd->base + SPI_IO_CONTROL);
+}
+
+static void get_transfer_length(struct msm_spi *dd)
+{
+	struct spi_transfer *tr;
+	int num_xfrs = 0;
+	int readlen = 0;
+	int writelen = 0;
+
+	dd->cur_msg_len = 0;
+	dd->multi_xfr = 0;
+	dd->read_len = dd->write_len = 0;
+
+	list_for_each_entry(tr, &dd->cur_msg->transfers, transfer_list) {
+		if (tr->tx_buf)
+			writelen += tr->len;
+		if (tr->rx_buf)
+			readlen += tr->len;
+		dd->cur_msg_len += tr->len;
+		num_xfrs++;
+	}
+
+	if (num_xfrs == 2) {
+		struct spi_transfer *first_xfr = dd->cur_transfer;
+
+		dd->multi_xfr = 1;
+		tr = list_entry(first_xfr->transfer_list.next,
+				struct spi_transfer,
+				transfer_list);
+		/*
+		 * We update dd->read_len and dd->write_len only
+		 * for WR-WR and WR-RD transfers.
+		 */
+		if ((first_xfr->tx_buf) && (!first_xfr->rx_buf)) {
+			if (((tr->tx_buf) && (!tr->rx_buf)) ||
+			    ((!tr->tx_buf) && (tr->rx_buf))) {
+				dd->read_len = readlen;
+				dd->write_len = writelen;
+			}
+		}
+	} else if (num_xfrs > 1)
+		dd->multi_xfr = 1;
+}
+
+static inline int combine_transfers(struct msm_spi *dd)
+{
+	struct spi_transfer *t = dd->cur_transfer;
+	struct spi_transfer *nxt;
+	int xfrs_grped = 1;
+
+	dd->cur_msg_len = dd->cur_transfer->len;
+	while (t->transfer_list.next != &dd->cur_msg->transfers) {
+		nxt = list_entry(t->transfer_list.next,
+				 struct spi_transfer,
+				 transfer_list);
+		if (t->cs_change != nxt->cs_change)
+			return xfrs_grped;
+		dd->cur_msg_len += nxt->len;
+		xfrs_grped++;
+		t = nxt;
+	}
+	return xfrs_grped;
+}
+
+static inline void write_force_cs(struct msm_spi *dd, bool set_flag)
+{
+	u32 spi_ioc;
+	u32 spi_ioc_orig;
+
+	spi_ioc = readl_relaxed(dd->base + SPI_IO_CONTROL);
+	spi_ioc_orig = spi_ioc;
+	if (set_flag)
+		spi_ioc |= SPI_IO_C_FORCE_CS;
+	else
+		spi_ioc &= ~SPI_IO_C_FORCE_CS;
+
+	if (spi_ioc != spi_ioc_orig)
+		writel_relaxed(spi_ioc, dd->base + SPI_IO_CONTROL);
+}
+
+static void msm_spi_process_message(struct msm_spi *dd)
+{
+	int xfrs_grped = 0;
+	int cs_num;
+	int rc;
+
+	dd->write_xfr_cnt = dd->read_xfr_cnt = 0;
+	cs_num = dd->cur_msg->spi->chip_select;
+	if ((!(dd->cur_msg->spi->mode & SPI_LOOP)) &&
+		(!(dd->cs_gpios[cs_num].valid)) &&
+		(dd->cs_gpios[cs_num].gpio_num >= 0)) {
+		rc = gpio_request(dd->cs_gpios[cs_num].gpio_num,
+				spi_cs_rsrcs[cs_num]);
+		if (rc) {
+			dev_err(dd->dev, "gpio_request for pin %d failed with "
+				"error %d\n", dd->cs_gpios[cs_num].gpio_num,
+				rc);
+			return;
+		}
+		dd->cs_gpios[cs_num].valid = 1;
+	}
+
+	if (dd->qup_ver) {
+		write_force_cs(dd, 0);
+		list_for_each_entry(dd->cur_transfer,
+				&dd->cur_msg->transfers,
+				transfer_list) {
+			struct spi_transfer *t = dd->cur_transfer;
+			struct spi_transfer *nxt;
+
+			if (t->transfer_list.next != &dd->cur_msg->transfers) {
+				nxt = list_entry(t->transfer_list.next,
+						struct spi_transfer,
+						transfer_list);
+
+				if (t->cs_change == nxt->cs_change)
+					write_force_cs(dd, 1);
+				else
+					write_force_cs(dd, 0);
+			}
+
+			dd->cur_msg_len = dd->cur_transfer->len;
+			msm_spi_process_transfer(dd);
+		}
+	} else {
+		dd->cur_transfer = list_first_entry(&dd->cur_msg->transfers,
+						    struct spi_transfer,
+						    transfer_list);
+		get_transfer_length(dd);
+		if (dd->multi_xfr && !dd->read_len && !dd->write_len) {
+			/*
+			 * Handling of multi-transfers.
+			 * FIFO mode is used by default
+			 */
+			list_for_each_entry(dd->cur_transfer,
+					    &dd->cur_msg->transfers,
+					    transfer_list) {
+				if (!dd->cur_transfer->len)
+					goto error;
+				if (xfrs_grped) {
+					xfrs_grped--;
+					continue;
+				} else {
+					dd->read_len = dd->write_len = 0;
+					xfrs_grped = combine_transfers(dd);
+				}
+
+				dd->cur_tx_transfer = dd->cur_transfer;
+				dd->cur_rx_transfer = dd->cur_transfer;
+				msm_spi_process_transfer(dd);
+				xfrs_grped--;
+			}
+		} else {
+			/* Handling of a single transfer or
+			 * WR-WR or WR-RD transfers
+			 */
+			if ((!dd->cur_msg->is_dma_mapped) &&
+			    (msm_use_dm(dd, dd->cur_transfer,
+					dd->cur_transfer->bits_per_word))) {
+				/* Mapping of DMA buffers */
+				int ret = msm_spi_map_dma_buffers(dd);
+				if (ret < 0) {
+					dd->cur_msg->status = ret;
+					goto error;
+				}
+			}
+
+			dd->cur_tx_transfer = dd->cur_transfer;
+			dd->cur_rx_transfer = dd->cur_transfer;
+			msm_spi_process_transfer(dd);
+		}
+	}
+
+	return;
+
+error:
+	if (dd->cs_gpios[cs_num].valid) {
+		gpio_free(dd->cs_gpios[cs_num].gpio_num);
+		dd->cs_gpios[cs_num].valid = 0;
+	}
+}
+
+/* workqueue - pull messages from queue & process */
+static void msm_spi_workq(struct work_struct *work)
+{
+	struct msm_spi      *dd =
+		container_of(work, struct msm_spi, work_data);
+	unsigned long        flags;
+	u32                  status_error = 0;
+
+	mutex_lock(&dd->core_lock);
+
+	/* Don't allow power collapse until we release mutex */
+	if (pm_qos_request_active(&qos_req_list))
+		pm_qos_update_request(&qos_req_list,
+				  dd->pm_lat);
+	if (dd->use_rlock)
+		remote_mutex_lock(&dd->r_lock);
+
+	clk_prepare_enable(dd->clk);
+	clk_prepare_enable(dd->pclk);
+	msm_spi_enable_irqs(dd);
+
+	if (!msm_spi_is_valid_state(dd)) {
+		dev_err(dd->dev, "%s: SPI operational state not valid\n",
+			__func__);
+		status_error = 1;
+	}
+
+	spin_lock_irqsave(&dd->queue_lock, flags);
+	while (!list_empty(&dd->queue)) {
+		dd->cur_msg = list_entry(dd->queue.next,
+					 struct spi_message, queue);
+		list_del_init(&dd->cur_msg->queue);
+		spin_unlock_irqrestore(&dd->queue_lock, flags);
+		if (status_error)
+			dd->cur_msg->status = -EIO;
+		else
+			msm_spi_process_message(dd);
+		if (dd->cur_msg->complete)
+			dd->cur_msg->complete(dd->cur_msg->context);
+		spin_lock_irqsave(&dd->queue_lock, flags);
+	}
+	dd->transfer_pending = 0;
+	spin_unlock_irqrestore(&dd->queue_lock, flags);
+
+	msm_spi_disable_irqs(dd);
+	clk_disable_unprepare(dd->clk);
+	clk_disable_unprepare(dd->pclk);
+
+	if (dd->use_rlock)
+		remote_mutex_unlock(&dd->r_lock);
+
+	if (pm_qos_request_active(&qos_req_list))
+		pm_qos_update_request(&qos_req_list,
+				  PM_QOS_DEFAULT_VALUE);
+
+	mutex_unlock(&dd->core_lock);
+	/* If needed, this can be done after the current message is complete,
+	   and work can be continued upon resume. No motivation for now. */
+	if (dd->suspended)
+		wake_up_interruptible(&dd->continue_suspend);
+}
+
+static int msm_spi_transfer(struct spi_device *spi, struct spi_message *msg)
+{
+	struct msm_spi	*dd;
+	unsigned long    flags;
+	struct spi_transfer *tr;
+
+	dd = spi_master_get_devdata(spi->master);
+	if (dd->suspended)
+		return -EBUSY;
+
+	if (list_empty(&msg->transfers) || !msg->complete)
+		return -EINVAL;
+
+	list_for_each_entry(tr, &msg->transfers, transfer_list) {
+		/* Check message parameters */
+		if (tr->speed_hz > dd->pdata->max_clock_speed ||
+		    (tr->bits_per_word &&
+		     (tr->bits_per_word < 4 || tr->bits_per_word > 32)) ||
+		    (tr->tx_buf == NULL && tr->rx_buf == NULL)) {
+			dev_err(&spi->dev, "Invalid transfer: %d Hz, %d bpw"
+					   "tx=%p, rx=%p\n",
+					    tr->speed_hz, tr->bits_per_word,
+					    tr->tx_buf, tr->rx_buf);
+			return -EINVAL;
+		}
+	}
+
+	spin_lock_irqsave(&dd->queue_lock, flags);
+	if (dd->suspended) {
+		spin_unlock_irqrestore(&dd->queue_lock, flags);
+		return -EBUSY;
+	}
+	dd->transfer_pending = 1;
+	list_add_tail(&msg->queue, &dd->queue);
+	spin_unlock_irqrestore(&dd->queue_lock, flags);
+	queue_work(dd->workqueue, &dd->work_data);
+	return 0;
+}
+
+static int msm_spi_setup(struct spi_device *spi)
+{
+	struct msm_spi	*dd;
+	int              rc = 0;
+	u32              spi_ioc;
+	u32              spi_config;
+	u32              mask;
+
+	if (spi->bits_per_word < 4 || spi->bits_per_word > 32) {
+		dev_err(&spi->dev, "%s: invalid bits_per_word %d\n",
+			__func__, spi->bits_per_word);
+		rc = -EINVAL;
+	}
+	if (spi->chip_select > SPI_NUM_CHIPSELECTS-1) {
+		dev_err(&spi->dev, "%s, chip select %d exceeds max value %d\n",
+			__func__, spi->chip_select, SPI_NUM_CHIPSELECTS - 1);
+		rc = -EINVAL;
+	}
+
+	if (rc)
+		goto err_setup_exit;
+
+	dd = spi_master_get_devdata(spi->master);
+
+	mutex_lock(&dd->core_lock);
+	if (dd->suspended) {
+		mutex_unlock(&dd->core_lock);
+		return -EBUSY;
+	}
+
+	if (dd->use_rlock)
+		remote_mutex_lock(&dd->r_lock);
+
+	clk_prepare_enable(dd->clk);
+	clk_prepare_enable(dd->pclk);
+
+	spi_ioc = readl_relaxed(dd->base + SPI_IO_CONTROL);
+	mask = SPI_IO_C_CS_N_POLARITY_0 << spi->chip_select;
+	if (spi->mode & SPI_CS_HIGH)
+		spi_ioc |= mask;
+	else
+		spi_ioc &= ~mask;
+	if (spi->mode & SPI_CPOL)
+		spi_ioc |= SPI_IO_C_CLK_IDLE_HIGH;
+	else
+		spi_ioc &= ~SPI_IO_C_CLK_IDLE_HIGH;
+
+	writel_relaxed(spi_ioc, dd->base + SPI_IO_CONTROL);
+
+	spi_config = readl_relaxed(dd->base + SPI_CONFIG);
+	if (spi->mode & SPI_LOOP)
+		spi_config |= SPI_CFG_LOOPBACK;
+	else
+		spi_config &= ~SPI_CFG_LOOPBACK;
+	if (spi->mode & SPI_CPHA)
+		spi_config &= ~SPI_CFG_INPUT_FIRST;
+	else
+		spi_config |= SPI_CFG_INPUT_FIRST;
+	writel_relaxed(spi_config, dd->base + SPI_CONFIG);
+
+	/* Ensure previous write completed before disabling the clocks */
+	mb();
+	clk_disable_unprepare(dd->clk);
+	clk_disable_unprepare(dd->pclk);
+
+	if (dd->use_rlock)
+		remote_mutex_unlock(&dd->r_lock);
+	mutex_unlock(&dd->core_lock);
+
+err_setup_exit:
+	return rc;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int debugfs_iomem_x32_set(void *data, u64 val)
+{
+	writel_relaxed(val, data);
+	/* Ensure the previous write completed. */
+	mb();
+	return 0;
+}
+
+static int debugfs_iomem_x32_get(void *data, u64 *val)
+{
+	*val = readl_relaxed(data);
+	/* Ensure the previous read completed. */
+	mb();
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x32, debugfs_iomem_x32_get,
+			debugfs_iomem_x32_set, "0x%08llx\n");
+
+static void spi_debugfs_init(struct msm_spi *dd)
+{
+	dd->dent_spi = debugfs_create_dir(dev_name(dd->dev), NULL);
+	if (dd->dent_spi) {
+		int i;
+
+		for (i = 0; i < ARRAY_SIZE(debugfs_spi_regs); i++) {
+			dd->debugfs_spi_regs[i] =
+			   debugfs_create_file(
+			       debugfs_spi_regs[i].name,
+			       debugfs_spi_regs[i].mode,
+			       dd->dent_spi,
+			       dd->base + debugfs_spi_regs[i].offset,
+			       &fops_iomem_x32);
+		}
+	}
+}
+
+static void spi_debugfs_exit(struct msm_spi *dd)
+{
+	if (dd->dent_spi) {
+		int i;
+
+		debugfs_remove_recursive(dd->dent_spi);
+		dd->dent_spi = NULL;
+		for (i = 0; i < ARRAY_SIZE(debugfs_spi_regs); i++)
+			dd->debugfs_spi_regs[i] = NULL;
+	}
+}
+#else
+static void spi_debugfs_init(struct msm_spi *dd) {}
+static void spi_debugfs_exit(struct msm_spi *dd) {}
+#endif
+
+/* ===Device attributes begin=== */
+static ssize_t show_stats(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	struct spi_master *master = dev_get_drvdata(dev);
+	struct msm_spi *dd =  spi_master_get_devdata(master);
+
+	return snprintf(buf, PAGE_SIZE,
+			"Device       %s\n"
+			"rx fifo_size = %d spi words\n"
+			"tx fifo_size = %d spi words\n"
+			"use_dma ?    %s\n"
+			"rx block size = %d bytes\n"
+			"tx block size = %d bytes\n"
+			"burst size = %d bytes\n"
+			"DMA configuration:\n"
+			"tx_ch=%d, rx_ch=%d, tx_crci= %d, rx_crci=%d\n"
+			"--statistics--\n"
+			"Rx isrs  = %d\n"
+			"Tx isrs  = %d\n"
+			"DMA error  = %d\n"
+			"--debug--\n"
+			"NA yet\n",
+			dev_name(dev),
+			dd->input_fifo_size,
+			dd->output_fifo_size,
+			dd->use_dma ? "yes" : "no",
+			dd->input_block_size,
+			dd->output_block_size,
+			dd->burst_size,
+			dd->tx_dma_chan,
+			dd->rx_dma_chan,
+			dd->tx_dma_crci,
+			dd->rx_dma_crci,
+			dd->stat_rx + dd->stat_dmov_rx,
+			dd->stat_tx + dd->stat_dmov_tx,
+			dd->stat_dmov_tx_err + dd->stat_dmov_rx_err
+			);
+}
+
+/* Reset statistics on write */
+static ssize_t set_stats(struct device *dev, struct device_attribute *attr,
+			 const char *buf, size_t count)
+{
+	struct msm_spi *dd = dev_get_drvdata(dev);
+	dd->stat_rx = 0;
+	dd->stat_tx = 0;
+	dd->stat_dmov_rx = 0;
+	dd->stat_dmov_tx = 0;
+	dd->stat_dmov_rx_err = 0;
+	dd->stat_dmov_tx_err = 0;
+	return count;
+}
+
+static DEVICE_ATTR(stats, S_IRUGO | S_IWUSR, show_stats, set_stats);
+
+static struct attribute *dev_attrs[] = {
+	&dev_attr_stats.attr,
+	NULL,
+};
+
+static struct attribute_group dev_attr_grp = {
+	.attrs = dev_attrs,
+};
+/* ===Device attributes end=== */
+
+/**
+ * spi_dmov_tx_complete_func - DataMover tx completion callback
+ *
+ * Executed in IRQ context (Data Mover's IRQ) DataMover's
+ * spinlock @msm_dmov_lock held.
+ */
+static void spi_dmov_tx_complete_func(struct msm_dmov_cmd *cmd,
+				      unsigned int result,
+				      struct msm_dmov_errdata *err)
+{
+	struct msm_spi *dd;
+
+	if (!(result & DMOV_RSLT_VALID)) {
+		pr_err("Invalid DMOV result: rc=0x%08x, cmd = %p", result, cmd);
+		return;
+	}
+	/* restore original context */
+	dd = container_of(cmd, struct msm_spi, tx_hdr);
+	if (result & DMOV_RSLT_DONE) {
+		dd->stat_dmov_tx++;
+		if ((atomic_inc_return(&dd->tx_irq_called) == 1))
+			return;
+		complete(&dd->transfer_complete);
+	} else {
+		/* Error or flush */
+		if (result & DMOV_RSLT_ERROR) {
+			dev_err(dd->dev, "DMA error (0x%08x)\n", result);
+			dd->stat_dmov_tx_err++;
+		}
+		if (result & DMOV_RSLT_FLUSH) {
+			/*
+			 * Flushing normally happens in process of
+			 * removing, when we are waiting for outstanding
+			 * DMA commands to be flushed.
+			 */
+			dev_info(dd->dev,
+				 "DMA channel flushed (0x%08x)\n", result);
+		}
+		if (err)
+			dev_err(dd->dev,
+				"Flush data(%08x %08x %08x %08x %08x %08x)\n",
+				err->flush[0], err->flush[1], err->flush[2],
+				err->flush[3], err->flush[4], err->flush[5]);
+		dd->cur_msg->status = -EIO;
+		complete(&dd->transfer_complete);
+	}
+}
+
+/**
+ * spi_dmov_rx_complete_func - DataMover rx completion callback
+ *
+ * Executed in IRQ context (Data Mover's IRQ)
+ * DataMover's spinlock @msm_dmov_lock held.
+ */
+static void spi_dmov_rx_complete_func(struct msm_dmov_cmd *cmd,
+				      unsigned int result,
+				      struct msm_dmov_errdata *err)
+{
+	struct msm_spi *dd;
+
+	if (!(result & DMOV_RSLT_VALID)) {
+		pr_err("Invalid DMOV result(rc = 0x%08x, cmd = %p)",
+		       result, cmd);
+		return;
+	}
+	/* restore original context */
+	dd = container_of(cmd, struct msm_spi, rx_hdr);
+	if (result & DMOV_RSLT_DONE) {
+		dd->stat_dmov_rx++;
+		if (atomic_inc_return(&dd->rx_irq_called) == 1)
+			return;
+		complete(&dd->transfer_complete);
+	} else {
+		/** Error or flush  */
+		if (result & DMOV_RSLT_ERROR) {
+			dev_err(dd->dev, "DMA error(0x%08x)\n", result);
+			dd->stat_dmov_rx_err++;
+		}
+		if (result & DMOV_RSLT_FLUSH) {
+			dev_info(dd->dev,
+				"DMA channel flushed(0x%08x)\n", result);
+		}
+		if (err)
+			dev_err(dd->dev,
+				"Flush data(%08x %08x %08x %08x %08x %08x)\n",
+				err->flush[0], err->flush[1], err->flush[2],
+				err->flush[3], err->flush[4], err->flush[5]);
+		dd->cur_msg->status = -EIO;
+		complete(&dd->transfer_complete);
+	}
+}
+
+static inline u32 get_chunk_size(struct msm_spi *dd)
+{
+	u32 cache_line = dma_get_cache_alignment();
+
+	return (roundup(sizeof(struct spi_dmov_cmd), DM_BYTE_ALIGN) +
+			  roundup(dd->burst_size, cache_line))*2;
+}
+
+static void msm_spi_teardown_dma(struct msm_spi *dd)
+{
+	int limit = 0;
+
+	if (!dd->use_dma)
+		return;
+
+	while (dd->mode == SPI_DMOV_MODE && limit++ < 50) {
+		msm_dmov_flush(dd->tx_dma_chan, 1);
+		msm_dmov_flush(dd->rx_dma_chan, 1);
+		msleep(10);
+	}
+
+	dma_free_coherent(NULL, get_chunk_size(dd), dd->tx_dmov_cmd,
+			  dd->tx_dmov_cmd_dma);
+	dd->tx_dmov_cmd = dd->rx_dmov_cmd = NULL;
+	dd->tx_padding = dd->rx_padding = NULL;
+}
+
+static __init int msm_spi_init_dma(struct msm_spi *dd)
+{
+	dmov_box *box;
+	u32 cache_line = dma_get_cache_alignment();
+
+	/* Allocate all as one chunk, since all is smaller than page size */
+
+	/* We send NULL device, since it requires coherent_dma_mask id
+	   device definition, we're okay with using system pool */
+	dd->tx_dmov_cmd = dma_alloc_coherent(NULL, get_chunk_size(dd),
+					     &dd->tx_dmov_cmd_dma, GFP_KERNEL);
+	if (dd->tx_dmov_cmd == NULL)
+		return -ENOMEM;
+
+	/* DMA addresses should be 64 bit aligned aligned */
+	dd->rx_dmov_cmd = (struct spi_dmov_cmd *)
+			  ALIGN((size_t)&dd->tx_dmov_cmd[1], DM_BYTE_ALIGN);
+	dd->rx_dmov_cmd_dma = ALIGN(dd->tx_dmov_cmd_dma +
+			      sizeof(struct spi_dmov_cmd), DM_BYTE_ALIGN);
+
+	/* Buffers should be aligned to cache line */
+	dd->tx_padding = (u8 *)ALIGN((size_t)&dd->rx_dmov_cmd[1], cache_line);
+	dd->tx_padding_dma = ALIGN(dd->rx_dmov_cmd_dma +
+			      sizeof(struct spi_dmov_cmd), cache_line);
+	dd->rx_padding = (u8 *)ALIGN((size_t)(dd->tx_padding + dd->burst_size),
+				     cache_line);
+	dd->rx_padding_dma = ALIGN(dd->tx_padding_dma + dd->burst_size,
+				      cache_line);
+
+	/* Setup DM commands */
+	box = &(dd->rx_dmov_cmd->box);
+	box->cmd = CMD_MODE_BOX | CMD_SRC_CRCI(dd->rx_dma_crci);
+	box->src_row_addr = (uint32_t)dd->mem_phys_addr + SPI_INPUT_FIFO;
+	dd->rx_hdr.cmdptr = DMOV_CMD_PTR_LIST |
+				   DMOV_CMD_ADDR(dd->rx_dmov_cmd_dma +
+				   offsetof(struct spi_dmov_cmd, cmd_ptr));
+	dd->rx_hdr.complete_func = spi_dmov_rx_complete_func;
+
+	box = &(dd->tx_dmov_cmd->box);
+	box->cmd = CMD_MODE_BOX | CMD_DST_CRCI(dd->tx_dma_crci);
+	box->dst_row_addr = (uint32_t)dd->mem_phys_addr + SPI_OUTPUT_FIFO;
+	dd->tx_hdr.cmdptr = DMOV_CMD_PTR_LIST |
+			    DMOV_CMD_ADDR(dd->tx_dmov_cmd_dma +
+			    offsetof(struct spi_dmov_cmd, cmd_ptr));
+	dd->tx_hdr.complete_func = spi_dmov_tx_complete_func;
+
+	dd->tx_dmov_cmd->single_pad.cmd = CMD_MODE_SINGLE | CMD_LC |
+					  CMD_DST_CRCI(dd->tx_dma_crci);
+	dd->tx_dmov_cmd->single_pad.dst = (uint32_t)dd->mem_phys_addr +
+					   SPI_OUTPUT_FIFO;
+	dd->rx_dmov_cmd->single_pad.cmd = CMD_MODE_SINGLE | CMD_LC |
+					  CMD_SRC_CRCI(dd->rx_dma_crci);
+	dd->rx_dmov_cmd->single_pad.src = (uint32_t)dd->mem_phys_addr +
+					  SPI_INPUT_FIFO;
+
+	/* Clear remaining activities on channel */
+	msm_dmov_flush(dd->tx_dma_chan, 1);
+	msm_dmov_flush(dd->rx_dma_chan, 1);
+
+	return 0;
+}
+
+struct msm_spi_platform_data *msm_spi_dt_to_pdata(struct platform_device *pdev)
+{
+	struct device_node *node = pdev->dev.of_node;
+	struct msm_spi_platform_data *pdata;
+
+	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata) {
+		pr_err("Unable to allocate platform data\n");
+		return NULL;
+	}
+
+	of_property_read_u32(node, "spi-max-frequency",
+			&pdata->max_clock_speed);
+
+	return pdata;
+}
+
+static int __init msm_spi_probe(struct platform_device *pdev)
+{
+	struct spi_master      *master;
+	struct msm_spi	       *dd;
+	struct resource	       *resource;
+	int                     rc = -ENXIO;
+	int                     locked = 0;
+	int                     i = 0;
+	int                     clk_enabled = 0;
+	int                     pclk_enabled = 0;
+	struct msm_spi_platform_data *pdata;
+	enum of_gpio_flags flags;
+
+	master = spi_alloc_master(&pdev->dev, sizeof(struct msm_spi));
+	if (!master) {
+		rc = -ENOMEM;
+		dev_err(&pdev->dev, "master allocation failed\n");
+		goto err_probe_exit;
+	}
+
+	master->bus_num        = pdev->id;
+	master->mode_bits      = SPI_SUPPORTED_MODES;
+	master->num_chipselect = SPI_NUM_CHIPSELECTS;
+	master->setup          = msm_spi_setup;
+	master->transfer       = msm_spi_transfer;
+	platform_set_drvdata(pdev, master);
+	dd = spi_master_get_devdata(master);
+
+	if (pdev->dev.of_node) {
+		dd->qup_ver = SPI_QUP_VERSION_BFAM;
+		master->dev.of_node = pdev->dev.of_node;
+		pdata = msm_spi_dt_to_pdata(pdev);
+		if (!pdata) {
+			rc = -ENOMEM;
+			goto err_probe_exit;
+		}
+
+		for (i = 0; i < ARRAY_SIZE(spi_rsrcs); ++i) {
+			dd->spi_gpios[i] = of_get_gpio_flags(pdev->dev.of_node,
+								i, &flags);
+		}
+
+		for (i = 0; i < ARRAY_SIZE(spi_cs_rsrcs); ++i) {
+			dd->cs_gpios[i].gpio_num = of_get_named_gpio_flags(
+						pdev->dev.of_node, "cs-gpios",
+						i, &flags);
+			dd->cs_gpios[i].valid = 0;
+		}
+	} else {
+		pdata = pdev->dev.platform_data;
+		dd->qup_ver = SPI_QUP_VERSION_NONE;
+
+		for (i = 0; i < ARRAY_SIZE(spi_rsrcs); ++i) {
+			resource = platform_get_resource(pdev, IORESOURCE_IO,
+							i);
+			dd->spi_gpios[i] = resource ? resource->start : -1;
+		}
+
+		for (i = 0; i < ARRAY_SIZE(spi_cs_rsrcs); ++i) {
+			resource = platform_get_resource(pdev, IORESOURCE_IO,
+						i + ARRAY_SIZE(spi_rsrcs));
+			dd->cs_gpios[i].gpio_num = resource ?
+							resource->start : -1;
+			dd->cs_gpios[i].valid = 0;
+		}
+	}
+
+	dd->pdata = pdata;
+	resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!resource) {
+		rc = -ENXIO;
+		goto err_probe_res;
+	}
+
+	dd->mem_phys_addr = resource->start;
+	dd->mem_size = resource_size(resource);
+
+	if (pdata) {
+		if (pdata->dma_config) {
+			rc = pdata->dma_config();
+			if (rc) {
+				dev_warn(&pdev->dev,
+					"%s: DM mode not supported\n",
+					__func__);
+				dd->use_dma = 0;
+				goto skip_dma_resources;
+			}
+		}
+		resource = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+		if (resource) {
+			dd->rx_dma_chan = resource->start;
+			dd->tx_dma_chan = resource->end;
+			resource = platform_get_resource(pdev, IORESOURCE_DMA,
+							1);
+			if (!resource) {
+				rc = -ENXIO;
+				goto err_probe_res;
+			}
+
+			dd->rx_dma_crci = resource->start;
+			dd->tx_dma_crci = resource->end;
+			dd->use_dma = 1;
+			master->dma_alignment =	dma_get_cache_alignment();
+		}
+
+skip_dma_resources:
+		if (pdata->gpio_config) {
+			rc = pdata->gpio_config();
+			if (rc) {
+				dev_err(&pdev->dev,
+					"%s: error configuring GPIOs\n",
+					__func__);
+				goto err_probe_gpio;
+			}
+		}
+	}
+
+	rc = msm_spi_request_gpios(dd);
+	if (rc)
+		goto err_probe_gpio;
+
+	spin_lock_init(&dd->queue_lock);
+	mutex_init(&dd->core_lock);
+	INIT_LIST_HEAD(&dd->queue);
+	INIT_WORK(&dd->work_data, msm_spi_workq);
+	init_waitqueue_head(&dd->continue_suspend);
+	dd->workqueue = create_singlethread_workqueue(
+			dev_name(master->dev.parent));
+	if (!dd->workqueue)
+		goto err_probe_workq;
+
+	if (!devm_request_mem_region(&pdev->dev, dd->mem_phys_addr,
+					dd->mem_size, SPI_DRV_NAME)) {
+		rc = -ENXIO;
+		goto err_probe_reqmem;
+	}
+
+	dd->base = devm_ioremap(&pdev->dev, dd->mem_phys_addr, dd->mem_size);
+	if (!dd->base) {
+		rc = -ENOMEM;
+		goto err_probe_reqmem;
+	}
+
+	if (pdata && pdata->rsl_id) {
+		struct remote_mutex_id rmid;
+		rmid.r_spinlock_id = pdata->rsl_id;
+		rmid.delay_us = SPI_TRYLOCK_DELAY;
+
+		rc = remote_mutex_init(&dd->r_lock, &rmid);
+		if (rc) {
+			dev_err(&pdev->dev, "%s: unable to init remote_mutex "
+				"(%s), (rc=%d)\n", rmid.r_spinlock_id,
+				__func__, rc);
+			goto err_probe_rlock_init;
+		}
+
+		dd->use_rlock = 1;
+		dd->pm_lat = pdata->pm_lat;
+		pm_qos_add_request(&qos_req_list, PM_QOS_CPU_DMA_LATENCY, 
+					    	 PM_QOS_DEFAULT_VALUE);
+	}
+
+	mutex_lock(&dd->core_lock);
+	if (dd->use_rlock)
+		remote_mutex_lock(&dd->r_lock);
+
+	locked = 1;
+	dd->dev = &pdev->dev;
+	dd->clk = clk_get(&pdev->dev, "core_clk");
+	if (IS_ERR(dd->clk)) {
+		dev_err(&pdev->dev, "%s: unable to get core_clk\n", __func__);
+		rc = PTR_ERR(dd->clk);
+		goto err_probe_clk_get;
+	}
+
+	dd->pclk = clk_get(&pdev->dev, "iface_clk");
+	if (IS_ERR(dd->pclk)) {
+		dev_err(&pdev->dev, "%s: unable to get iface_clk\n", __func__);
+		rc = PTR_ERR(dd->pclk);
+		goto err_probe_pclk_get;
+	}
+
+	if (pdata && pdata->max_clock_speed)
+		msm_spi_clock_set(dd, dd->pdata->max_clock_speed);
+
+	rc = clk_prepare_enable(dd->clk);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: unable to enable core_clk\n",
+			__func__);
+		goto err_probe_clk_enable;
+	}
+
+	clk_enabled = 1;
+	rc = clk_prepare_enable(dd->pclk);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: unable to enable iface_clk\n",
+		__func__);
+		goto err_probe_pclk_enable;
+	}
+
+	pclk_enabled = 1;
+	rc = msm_spi_configure_gsbi(dd, pdev);
+	if (rc)
+		goto err_probe_gsbi;
+
+	msm_spi_calculate_fifo_size(dd);
+	if (dd->use_dma) {
+		rc = msm_spi_init_dma(dd);
+		if (rc)
+			goto err_probe_dma;
+	}
+
+	msm_spi_register_init(dd);
+	/*
+	 * The SPI core generates a bogus input overrun error on some targets,
+	 * when a transition from run to reset state occurs and if the FIFO has
+	 * an odd number of entries. Hence we disable the INPUT_OVER_RUN_ERR_EN
+	 * bit.
+	 */
+	msm_spi_enable_error_flags(dd);
+
+	writel_relaxed(SPI_IO_C_NO_TRI_STATE, dd->base + SPI_IO_CONTROL);
+	rc = msm_spi_set_state(dd, SPI_OP_STATE_RESET);
+	if (rc)
+		goto err_probe_state;
+
+	clk_disable_unprepare(dd->clk);
+	clk_disable_unprepare(dd->pclk);
+	clk_enabled = 0;
+	pclk_enabled = 0;
+
+	dd->suspended = 0;
+	dd->transfer_pending = 0;
+	dd->multi_xfr = 0;
+	dd->mode = SPI_MODE_NONE;
+
+	rc = msm_spi_request_irq(dd, pdev, master);
+	if (rc)
+		goto err_probe_irq;
+
+	msm_spi_disable_irqs(dd);
+	if (dd->use_rlock)
+		remote_mutex_unlock(&dd->r_lock);
+
+	mutex_unlock(&dd->core_lock);
+	locked = 0;
+
+	rc = spi_register_master(master);
+	if (rc)
+		goto err_probe_reg_master;
+
+	rc = sysfs_create_group(&(dd->dev->kobj), &dev_attr_grp);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to create dev. attrs : %d\n", rc);
+		goto err_attrs;
+	}
+
+	spi_debugfs_init(dd);
+	return 0;
+
+err_attrs:
+	spi_unregister_master(master);
+err_probe_reg_master:
+err_probe_irq:
+err_probe_state:
+	msm_spi_teardown_dma(dd);
+err_probe_dma:
+err_probe_gsbi:
+	if (pclk_enabled)
+		clk_disable_unprepare(dd->pclk);
+err_probe_pclk_enable:
+	if (clk_enabled)
+		clk_disable_unprepare(dd->clk);
+err_probe_clk_enable:
+	clk_put(dd->pclk);
+err_probe_pclk_get:
+	clk_put(dd->clk);
+err_probe_clk_get:
+	if (locked) {
+		if (dd->use_rlock)
+			remote_mutex_unlock(&dd->r_lock);
+
+		mutex_unlock(&dd->core_lock);
+	}
+err_probe_rlock_init:
+err_probe_reqmem:
+	destroy_workqueue(dd->workqueue);
+err_probe_workq:
+	msm_spi_free_gpios(dd);
+err_probe_gpio:
+	if (pdata && pdata->gpio_release)
+		pdata->gpio_release();
+err_probe_res:
+	spi_master_put(master);
+err_probe_exit:
+	return rc;
+}
+
+#ifdef CONFIG_PM
+static int msm_spi_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct spi_master *master = platform_get_drvdata(pdev);
+	struct msm_spi    *dd;
+	unsigned long      flags;
+
+	if (!master)
+		goto suspend_exit;
+	dd = spi_master_get_devdata(master);
+	if (!dd)
+		goto suspend_exit;
+
+	/* Make sure nothing is added to the queue while we're suspending */
+	spin_lock_irqsave(&dd->queue_lock, flags);
+	dd->suspended = 1;
+	spin_unlock_irqrestore(&dd->queue_lock, flags);
+
+	/* Wait for transactions to end, or time out */
+	wait_event_interruptible(dd->continue_suspend, !dd->transfer_pending);
+	msm_spi_free_gpios(dd);
+
+suspend_exit:
+	return 0;
+}
+
+static int msm_spi_resume(struct platform_device *pdev)
+{
+	struct spi_master *master = platform_get_drvdata(pdev);
+	struct msm_spi    *dd;
+
+	if (!master)
+		goto resume_exit;
+	dd = spi_master_get_devdata(master);
+	if (!dd)
+		goto resume_exit;
+
+	BUG_ON(msm_spi_request_gpios(dd) != 0);
+	dd->suspended = 0;
+resume_exit:
+	return 0;
+}
+#else
+#define msm_spi_suspend NULL
+#define msm_spi_resume NULL
+#endif /* CONFIG_PM */
+
+static int __devexit msm_spi_remove(struct platform_device *pdev)
+{
+	struct spi_master *master = platform_get_drvdata(pdev);
+	struct msm_spi    *dd = spi_master_get_devdata(master);
+	struct msm_spi_platform_data *pdata = pdev->dev.platform_data;
+
+	pm_qos_remove_request(&qos_req_list);
+	spi_debugfs_exit(dd);
+	sysfs_remove_group(&pdev->dev.kobj, &dev_attr_grp);
+
+	msm_spi_teardown_dma(dd);
+	if (pdata && pdata->gpio_release)
+		pdata->gpio_release();
+
+	msm_spi_free_gpios(dd);
+	clk_put(dd->clk);
+	clk_put(dd->pclk);
+	destroy_workqueue(dd->workqueue);
+	platform_set_drvdata(pdev, 0);
+	spi_unregister_master(master);
+	spi_master_put(master);
+
+	return 0;
+}
+
+static struct of_device_id msm_spi_dt_match[] = {
+	{
+		.compatible = "qcom,spi-qup-v2",
+	},
+	{}
+};
+
+static struct platform_driver msm_spi_driver = {
+	.driver		= {
+		.name	= SPI_DRV_NAME,
+		.owner	= THIS_MODULE,
+		.of_match_table = msm_spi_dt_match,
+	},
+	.suspend        = msm_spi_suspend,
+	.resume         = msm_spi_resume,
+	.remove		= __exit_p(msm_spi_remove),
+};
+
+static int __init msm_spi_init(void)
+{
+	return platform_driver_probe(&msm_spi_driver, msm_spi_probe);
+}
+module_init(msm_spi_init);
+
+static void __exit msm_spi_exit(void)
+{
+	platform_driver_unregister(&msm_spi_driver);
+}
+module_exit(msm_spi_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.4");
+MODULE_ALIAS("platform:"SPI_DRV_NAME);
diff --git a/drivers/spi/spi_qsd.h b/drivers/spi/spi_qsd.h
new file mode 100644
index 0000000..b0d72b7
--- /dev/null
+++ b/drivers/spi/spi_qsd.h
@@ -0,0 +1,493 @@
+/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _SPI_QSD_H
+#define _SPI_QSD_H
+
+#define SPI_DRV_NAME                  "spi_qsd"
+
+#if defined(CONFIG_SPI_QSD) || defined(CONFIG_SPI_QSD_MODULE)
+
+#define QSD_REG(x) (x)
+#define QUP_REG(x)
+
+#define SPI_FIFO_WORD_CNT             0x0048
+
+#else
+
+#define QSD_REG(x)
+#define QUP_REG(x) (x)
+
+#define QUP_CONFIG                    0x0000 /* N & NO_INPUT/NO_OUPUT bits */
+#define QUP_ERROR_FLAGS_EN            0x030C
+#define QUP_ERR_MASK                  0x3
+#define SPI_OUTPUT_FIFO_WORD_CNT      0x010C
+#define SPI_INPUT_FIFO_WORD_CNT       0x0214
+#define QUP_MX_WRITE_COUNT            0x0150
+#define QUP_MX_WRITE_CNT_CURRENT      0x0154
+
+#define QUP_CONFIG_SPI_MODE           0x0100
+#endif
+
+#define GSBI_CTRL_REG                 0x0
+#define GSBI_SPI_CONFIG               0x30
+#define QUP_HARDWARE_VER              0x0030
+#define QUP_OPERATIONAL_MASK          0x0028
+#define QUP_ERROR_FLAGS               0x0308
+
+#define SPI_CONFIG                    QSD_REG(0x0000) QUP_REG(0x0300)
+#define SPI_IO_CONTROL                QSD_REG(0x0004) QUP_REG(0x0304)
+#define SPI_IO_MODES                  QSD_REG(0x0008) QUP_REG(0x0008)
+#define SPI_SW_RESET                  QSD_REG(0x000C) QUP_REG(0x000C)
+#define SPI_TIME_OUT_CURRENT          QSD_REG(0x0014) QUP_REG(0x0014)
+#define SPI_MX_OUTPUT_COUNT           QSD_REG(0x0018) QUP_REG(0x0100)
+#define SPI_MX_OUTPUT_CNT_CURRENT     QSD_REG(0x001C) QUP_REG(0x0104)
+#define SPI_MX_INPUT_COUNT            QSD_REG(0x0020) QUP_REG(0x0200)
+#define SPI_MX_INPUT_CNT_CURRENT      QSD_REG(0x0024) QUP_REG(0x0204)
+#define SPI_MX_READ_COUNT             QSD_REG(0x0028) QUP_REG(0x0208)
+#define SPI_MX_READ_CNT_CURRENT       QSD_REG(0x002C) QUP_REG(0x020C)
+#define SPI_OPERATIONAL               QSD_REG(0x0030) QUP_REG(0x0018)
+#define SPI_ERROR_FLAGS               QSD_REG(0x0034) QUP_REG(0x001C)
+#define SPI_ERROR_FLAGS_EN            QSD_REG(0x0038) QUP_REG(0x0020)
+#define SPI_DEASSERT_WAIT             QSD_REG(0x003C) QUP_REG(0x0310)
+#define SPI_OUTPUT_DEBUG              QSD_REG(0x0040) QUP_REG(0x0108)
+#define SPI_INPUT_DEBUG               QSD_REG(0x0044) QUP_REG(0x0210)
+#define SPI_TEST_CTRL                 QSD_REG(0x004C) QUP_REG(0x0024)
+#define SPI_OUTPUT_FIFO               QSD_REG(0x0100) QUP_REG(0x0110)
+#define SPI_INPUT_FIFO                QSD_REG(0x0200) QUP_REG(0x0218)
+#define SPI_STATE                     QSD_REG(SPI_OPERATIONAL) QUP_REG(0x0004)
+
+/* SPI_CONFIG fields */
+#define SPI_CFG_INPUT_FIRST           0x00000200
+#define SPI_NO_INPUT                  0x00000080
+#define SPI_NO_OUTPUT                 0x00000040
+#define SPI_CFG_LOOPBACK              0x00000100
+#define SPI_CFG_N                     0x0000001F
+
+/* SPI_IO_CONTROL fields */
+#define SPI_IO_C_FORCE_CS             0x00000800
+#define SPI_IO_C_CLK_IDLE_HIGH        0x00000400
+#define SPI_IO_C_MX_CS_MODE           0x00000100
+#define SPI_IO_C_CS_N_POLARITY        0x000000F0
+#define SPI_IO_C_CS_N_POLARITY_0      0x00000010
+#define SPI_IO_C_CS_SELECT            0x0000000C
+#define SPI_IO_C_TRISTATE_CS          0x00000002
+#define SPI_IO_C_NO_TRI_STATE         0x00000001
+
+/* SPI_IO_MODES fields */
+#define SPI_IO_M_OUTPUT_BIT_SHIFT_EN  QSD_REG(0x00004000) QUP_REG(0x00010000)
+#define SPI_IO_M_PACK_EN              QSD_REG(0x00002000) QUP_REG(0x00008000)
+#define SPI_IO_M_UNPACK_EN            QSD_REG(0x00001000) QUP_REG(0x00004000)
+#define SPI_IO_M_INPUT_MODE           QSD_REG(0x00000C00) QUP_REG(0x00003000)
+#define SPI_IO_M_OUTPUT_MODE          QSD_REG(0x00000300) QUP_REG(0x00000C00)
+#define SPI_IO_M_INPUT_FIFO_SIZE      QSD_REG(0x000000C0) QUP_REG(0x00000380)
+#define SPI_IO_M_INPUT_BLOCK_SIZE     QSD_REG(0x00000030) QUP_REG(0x00000060)
+#define SPI_IO_M_OUTPUT_FIFO_SIZE     QSD_REG(0x0000000C) QUP_REG(0x0000001C)
+#define SPI_IO_M_OUTPUT_BLOCK_SIZE    QSD_REG(0x00000003) QUP_REG(0x00000003)
+
+#define INPUT_BLOCK_SZ_SHIFT          QSD_REG(4)          QUP_REG(5)
+#define INPUT_FIFO_SZ_SHIFT           QSD_REG(6)          QUP_REG(7)
+#define OUTPUT_BLOCK_SZ_SHIFT         QSD_REG(0)          QUP_REG(0)
+#define OUTPUT_FIFO_SZ_SHIFT          QSD_REG(2)          QUP_REG(2)
+#define OUTPUT_MODE_SHIFT             QSD_REG(8)          QUP_REG(10)
+#define INPUT_MODE_SHIFT              QSD_REG(10)         QUP_REG(12)
+
+/* SPI_OPERATIONAL fields */
+#define SPI_OP_MAX_INPUT_DONE_FLAG    0x00000800
+#define SPI_OP_MAX_OUTPUT_DONE_FLAG   0x00000400
+#define SPI_OP_INPUT_SERVICE_FLAG     0x00000200
+#define SPI_OP_OUTPUT_SERVICE_FLAG    0x00000100
+#define SPI_OP_INPUT_FIFO_FULL        0x00000080
+#define SPI_OP_OUTPUT_FIFO_FULL       0x00000040
+#define SPI_OP_IP_FIFO_NOT_EMPTY      0x00000020
+#define SPI_OP_OP_FIFO_NOT_EMPTY      0x00000010
+#define SPI_OP_STATE_VALID            0x00000004
+#define SPI_OP_STATE                  0x00000003
+
+#define SPI_OP_STATE_CLEAR_BITS       0x2
+enum msm_spi_state {
+	SPI_OP_STATE_RESET = 0x00000000,
+	SPI_OP_STATE_RUN   = 0x00000001,
+	SPI_OP_STATE_PAUSE = 0x00000003,
+};
+
+/* SPI_ERROR_FLAGS fields */
+#define SPI_ERR_OUTPUT_OVER_RUN_ERR   0x00000020
+#define SPI_ERR_INPUT_UNDER_RUN_ERR   0x00000010
+#define SPI_ERR_OUTPUT_UNDER_RUN_ERR  0x00000008
+#define SPI_ERR_INPUT_OVER_RUN_ERR    0x00000004
+#define SPI_ERR_CLK_OVER_RUN_ERR      0x00000002
+#define SPI_ERR_CLK_UNDER_RUN_ERR     0x00000001
+
+/* We don't allow transactions larger than 4K-64 or 64K-64 due to
+   mx_input/output_cnt register size */
+#define SPI_MAX_TRANSFERS             QSD_REG(0xFC0) QUP_REG(0xFC0)
+#define SPI_MAX_LEN                   (SPI_MAX_TRANSFERS * dd->bytes_per_word)
+
+#define SPI_NUM_CHIPSELECTS           4
+#define SPI_SUPPORTED_MODES  (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP)
+
+#define SPI_DELAY_THRESHOLD           1
+/* Default timeout is 10 milliseconds */
+#define SPI_DEFAULT_TIMEOUT           10
+/* 250 microseconds */
+#define SPI_TRYLOCK_DELAY             250
+
+/* Data Mover burst size */
+#define DM_BURST_SIZE                 16
+/* Data Mover commands should be aligned to 64 bit(8 bytes) */
+#define DM_BYTE_ALIGN                 8
+
+#define SPI_QUP_VERSION_NONE      0x0
+#define SPI_QUP_VERSION_BFAM      0x2
+
+static char const * const spi_rsrcs[] = {
+	"spi_clk",
+	"spi_miso",
+	"spi_mosi"
+};
+
+static char const * const spi_cs_rsrcs[] = {
+	"spi_cs",
+	"spi_cs1",
+	"spi_cs2",
+	"spi_cs3",
+};
+
+enum msm_spi_mode {
+	SPI_FIFO_MODE  = 0x0,  /* 00 */
+	SPI_BLOCK_MODE = 0x1,  /* 01 */
+	SPI_DMOV_MODE  = 0x2,  /* 10 */
+	SPI_BAM_MODE   = 0x3,  /* 11 */
+	SPI_MODE_NONE  = 0xFF, /* invalid value */
+};
+
+/* Structure for SPI CS GPIOs */
+struct spi_cs_gpio {
+	int  gpio_num;
+	bool valid;
+};
+
+/* Structures for Data Mover */
+struct spi_dmov_cmd {
+	dmov_box box;      /* data aligned to max(dm_burst_size, block_size)
+							   (<= fifo_size) */
+	dmov_s single_pad; /* data unaligned to max(dm_burst_size, block_size)
+			      padded to fit */
+	dma_addr_t cmd_ptr;
+};
+
+static struct pm_qos_request qos_req_list;
+
+#ifdef CONFIG_DEBUG_FS
+/* Used to create debugfs entries */
+static const struct {
+	const char *name;
+	mode_t mode;
+	int offset;
+} debugfs_spi_regs[] = {
+	{"config",                S_IRUGO | S_IWUSR, SPI_CONFIG},
+	{"io_control",            S_IRUGO | S_IWUSR, SPI_IO_CONTROL},
+	{"io_modes",              S_IRUGO | S_IWUSR, SPI_IO_MODES},
+	{"sw_reset",                        S_IWUSR, SPI_SW_RESET},
+	{"time_out_current",      S_IRUGO,           SPI_TIME_OUT_CURRENT},
+	{"mx_output_count",       S_IRUGO | S_IWUSR, SPI_MX_OUTPUT_COUNT},
+	{"mx_output_cnt_current", S_IRUGO,           SPI_MX_OUTPUT_CNT_CURRENT},
+	{"mx_input_count",        S_IRUGO | S_IWUSR, SPI_MX_INPUT_COUNT},
+	{"mx_input_cnt_current",  S_IRUGO,           SPI_MX_INPUT_CNT_CURRENT},
+	{"mx_read_count",         S_IRUGO | S_IWUSR, SPI_MX_READ_COUNT},
+	{"mx_read_cnt_current",   S_IRUGO,           SPI_MX_READ_CNT_CURRENT},
+	{"operational",           S_IRUGO | S_IWUSR, SPI_OPERATIONAL},
+	{"error_flags",           S_IRUGO | S_IWUSR, SPI_ERROR_FLAGS},
+	{"error_flags_en",        S_IRUGO | S_IWUSR, SPI_ERROR_FLAGS_EN},
+	{"deassert_wait",         S_IRUGO | S_IWUSR, SPI_DEASSERT_WAIT},
+	{"output_debug",          S_IRUGO,           SPI_OUTPUT_DEBUG},
+	{"input_debug",           S_IRUGO,           SPI_INPUT_DEBUG},
+	{"test_ctrl",             S_IRUGO | S_IWUSR, SPI_TEST_CTRL},
+	{"output_fifo",                     S_IWUSR, SPI_OUTPUT_FIFO},
+	{"input_fifo" ,           S_IRUSR,           SPI_INPUT_FIFO},
+	{"spi_state",             S_IRUGO | S_IWUSR, SPI_STATE},
+#if defined(CONFIG_SPI_QSD) || defined(CONFIG_SPI_QSD_MODULE)
+	{"fifo_word_cnt",         S_IRUGO,           SPI_FIFO_WORD_CNT},
+#else
+	{"qup_config",            S_IRUGO | S_IWUSR, QUP_CONFIG},
+	{"qup_error_flags",       S_IRUGO | S_IWUSR, QUP_ERROR_FLAGS},
+	{"qup_error_flags_en",    S_IRUGO | S_IWUSR, QUP_ERROR_FLAGS_EN},
+	{"mx_write_cnt",          S_IRUGO | S_IWUSR, QUP_MX_WRITE_COUNT},
+	{"mx_write_cnt_current",  S_IRUGO,           QUP_MX_WRITE_CNT_CURRENT},
+	{"output_fifo_word_cnt",  S_IRUGO,           SPI_OUTPUT_FIFO_WORD_CNT},
+	{"input_fifo_word_cnt",   S_IRUGO,           SPI_INPUT_FIFO_WORD_CNT},
+#endif
+};
+#endif
+
+struct msm_spi {
+	u8                      *read_buf;
+	const u8                *write_buf;
+	void __iomem            *base;
+	struct device           *dev;
+	spinlock_t               queue_lock;
+	struct mutex             core_lock;
+	struct list_head         queue;
+	struct workqueue_struct *workqueue;
+	struct work_struct       work_data;
+	struct spi_message      *cur_msg;
+	struct spi_transfer     *cur_transfer;
+	struct completion        transfer_complete;
+	struct clk              *clk;
+	struct clk              *pclk;
+	unsigned long            mem_phys_addr;
+	size_t                   mem_size;
+	int                      input_fifo_size;
+	int                      output_fifo_size;
+	u32                      rx_bytes_remaining;
+	u32                      tx_bytes_remaining;
+	u32                      clock_speed;
+	int                      irq_in;
+	int                      read_xfr_cnt;
+	int                      write_xfr_cnt;
+	int                      write_len;
+	int                      read_len;
+#if defined(CONFIG_SPI_QSD) || defined(CONFIG_SPI_QSD_MODULE)
+	int                      irq_out;
+	int                      irq_err;
+#endif
+	int                      bytes_per_word;
+	bool                     suspended;
+	bool                     transfer_pending;
+	wait_queue_head_t        continue_suspend;
+	/* DMA data */
+	enum msm_spi_mode        mode;
+	bool                     use_dma;
+	int                      tx_dma_chan;
+	int                      tx_dma_crci;
+	int                      rx_dma_chan;
+	int                      rx_dma_crci;
+	/* Data Mover Commands */
+	struct spi_dmov_cmd      *tx_dmov_cmd;
+	struct spi_dmov_cmd      *rx_dmov_cmd;
+	/* Physical address of the tx dmov box command */
+	dma_addr_t               tx_dmov_cmd_dma;
+	dma_addr_t               rx_dmov_cmd_dma;
+	struct msm_dmov_cmd      tx_hdr;
+	struct msm_dmov_cmd      rx_hdr;
+	int                      input_block_size;
+	int                      output_block_size;
+	int                      burst_size;
+	atomic_t                 rx_irq_called;
+	atomic_t                 tx_irq_called;
+	/* Used to pad messages unaligned to block size */
+	u8                       *tx_padding;
+	dma_addr_t               tx_padding_dma;
+	u8                       *rx_padding;
+	dma_addr_t               rx_padding_dma;
+	u32                      unaligned_len;
+	/* DMA statistics */
+	int                      stat_dmov_tx_err;
+	int                      stat_dmov_rx_err;
+	int                      stat_rx;
+	int                      stat_dmov_rx;
+	int                      stat_tx;
+	int                      stat_dmov_tx;
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *dent_spi;
+	struct dentry *debugfs_spi_regs[ARRAY_SIZE(debugfs_spi_regs)];
+#endif
+	struct msm_spi_platform_data *pdata; /* Platform data */
+	/* Remote Spinlock Data */
+	bool                     use_rlock;
+	remote_mutex_t           r_lock;
+	uint32_t                 pm_lat;
+	/* When set indicates multiple transfers in a single message */
+	bool                     multi_xfr;
+	bool                     done;
+	u32                      cur_msg_len;
+	/* Used in FIFO mode to keep track of the transfer being processed */
+	struct spi_transfer     *cur_tx_transfer;
+	struct spi_transfer     *cur_rx_transfer;
+	/* Temporary buffer used for WR-WR or WR-RD transfers */
+	u8                      *temp_buf;
+	/* GPIO pin numbers for SPI clk, miso and mosi */
+	int                      spi_gpios[ARRAY_SIZE(spi_rsrcs)];
+	/* SPI CS GPIOs for each slave */
+	struct spi_cs_gpio       cs_gpios[ARRAY_SIZE(spi_cs_rsrcs)];
+	int                      qup_ver;
+};
+
+/* Forward declaration */
+static irqreturn_t msm_spi_input_irq(int irq, void *dev_id);
+static irqreturn_t msm_spi_output_irq(int irq, void *dev_id);
+static irqreturn_t msm_spi_error_irq(int irq, void *dev_id);
+static inline int msm_spi_set_state(struct msm_spi *dd,
+				    enum msm_spi_state state);
+static void msm_spi_write_word_to_fifo(struct msm_spi *dd);
+static inline void msm_spi_write_rmn_to_fifo(struct msm_spi *dd);
+static inline irqreturn_t msm_spi_qup_irq(int irq, void *dev_id);
+
+#if defined(CONFIG_SPI_QSD) || defined(CONFIG_SPI_QSD_MODULE)
+static inline void msm_spi_disable_irqs(struct msm_spi *dd)
+{
+	disable_irq(dd->irq_in);
+	disable_irq(dd->irq_out);
+	disable_irq(dd->irq_err);
+}
+
+static inline void msm_spi_enable_irqs(struct msm_spi *dd)
+{
+	enable_irq(dd->irq_in);
+	enable_irq(dd->irq_out);
+	enable_irq(dd->irq_err);
+}
+
+static inline int msm_spi_request_irq(struct msm_spi *dd,
+				struct platform_device *pdev,
+				struct spi_master *master)
+{
+	int rc;
+
+	dd->irq_in  = platform_get_irq(pdev, 0);
+	dd->irq_out = platform_get_irq(pdev, 1);
+	dd->irq_err = platform_get_irq(pdev, 2);
+	if ((dd->irq_in < 0) || (dd->irq_out < 0) || (dd->irq_err < 0))
+		return -EINVAL;
+
+	rc = devm_request_irq(dd->dev, dd->irq_in, msm_spi_input_irq,
+		IRQF_TRIGGER_RISING, pdev->name, dd);
+	if (rc)
+		goto error_irq;
+
+	rc = devm_request_irq(dd->dev, dd->irq_out, msm_spi_output_irq,
+		IRQF_TRIGGER_RISING, pdev->name, dd);
+	if (rc)
+		goto error_irq;
+
+	rc = devm_request_irq(dd->dev, dd->irq_err, msm_spi_error_irq,
+		IRQF_TRIGGER_RISING, pdev->name, master);
+	if (rc)
+		goto error_irq;
+
+error_irq:
+	return rc;
+}
+
+static inline void msm_spi_get_clk_err(struct msm_spi *dd, u32 *spi_err) {}
+static inline void msm_spi_ack_clk_err(struct msm_spi *dd) {}
+static inline void msm_spi_set_qup_config(struct msm_spi *dd, int bpw) {}
+
+static inline int msm_spi_prepare_for_write(struct msm_spi *dd) { return 0; }
+static inline void msm_spi_start_write(struct msm_spi *dd, u32 read_count)
+{
+	msm_spi_write_word_to_fifo(dd);
+}
+static inline void msm_spi_set_write_count(struct msm_spi *dd, int val) {}
+
+static inline void msm_spi_complete(struct msm_spi *dd)
+{
+	complete(&dd->transfer_complete);
+}
+
+static inline void msm_spi_enable_error_flags(struct msm_spi *dd)
+{
+	writel_relaxed(0x0000007B, dd->base + SPI_ERROR_FLAGS_EN);
+}
+
+static inline void msm_spi_clear_error_flags(struct msm_spi *dd)
+{
+	writel_relaxed(0x0000007F, dd->base + SPI_ERROR_FLAGS);
+}
+
+#else
+/* In QUP the same interrupt line is used for input, output and error*/
+static inline int msm_spi_request_irq(struct msm_spi *dd,
+				struct platform_device *pdev,
+				struct spi_master *master)
+{
+	dd->irq_in  = platform_get_irq(pdev, 0);
+	if (dd->irq_in < 0)
+		return -EINVAL;
+
+	return devm_request_irq(dd->dev, dd->irq_in, msm_spi_qup_irq,
+		IRQF_TRIGGER_HIGH, pdev->name, dd);
+}
+
+static inline void msm_spi_disable_irqs(struct msm_spi *dd)
+{
+	disable_irq(dd->irq_in);
+}
+
+static inline void msm_spi_enable_irqs(struct msm_spi *dd)
+{
+	enable_irq(dd->irq_in);
+}
+
+static inline void msm_spi_get_clk_err(struct msm_spi *dd, u32 *spi_err)
+{
+	*spi_err = readl_relaxed(dd->base + QUP_ERROR_FLAGS);
+}
+
+static inline void msm_spi_ack_clk_err(struct msm_spi *dd)
+{
+	writel_relaxed(QUP_ERR_MASK, dd->base + QUP_ERROR_FLAGS);
+}
+
+static inline void msm_spi_add_configs(struct msm_spi *dd, u32 *config, int n);
+
+/* QUP has no_input, no_output, and N bits at QUP_CONFIG */
+static inline void msm_spi_set_qup_config(struct msm_spi *dd, int bpw)
+{
+	u32 qup_config = readl_relaxed(dd->base + QUP_CONFIG);
+
+	msm_spi_add_configs(dd, &qup_config, bpw-1);
+	writel_relaxed(qup_config | QUP_CONFIG_SPI_MODE,
+		       dd->base + QUP_CONFIG);
+}
+
+static inline int msm_spi_prepare_for_write(struct msm_spi *dd)
+{
+	if (msm_spi_set_state(dd, SPI_OP_STATE_RUN))
+		return -EINVAL;
+	if (msm_spi_set_state(dd, SPI_OP_STATE_PAUSE))
+		return -EINVAL;
+	return 0;
+}
+
+static inline void msm_spi_start_write(struct msm_spi *dd, u32 read_count)
+{
+	if (read_count <= dd->input_fifo_size)
+		msm_spi_write_rmn_to_fifo(dd);
+	else
+		msm_spi_write_word_to_fifo(dd);
+}
+
+static inline void msm_spi_set_write_count(struct msm_spi *dd, int val)
+{
+	writel_relaxed(val, dd->base + QUP_MX_WRITE_COUNT);
+}
+
+static inline void msm_spi_complete(struct msm_spi *dd)
+{
+	dd->done = 1;
+}
+
+static inline void msm_spi_enable_error_flags(struct msm_spi *dd)
+{
+	writel_relaxed(0x00000078, dd->base + SPI_ERROR_FLAGS_EN);
+}
+
+static inline void msm_spi_clear_error_flags(struct msm_spi *dd)
+{
+	writel_relaxed(0x0000007C, dd->base + SPI_ERROR_FLAGS);
+}
+
+#endif
+#endif
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index 830adbe..aaf0265 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -83,6 +83,7 @@
 	struct mutex		buf_lock;
 	unsigned		users;
 	u8			*buffer;
+	u8			*bufferrx;
 };
 
 static LIST_HEAD(device_list);
@@ -92,6 +93,30 @@
 module_param(bufsiz, uint, S_IRUGO);
 MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message");
 
+/*
+ * This can be used for testing the controller, given the busnum and the
+ * cs required to use. If those parameters are used, spidev is
+ * dynamically added as device on the busnum, and messages can be sent
+ * via this interface.
+ */
+static int busnum = -1;
+module_param(busnum, int, S_IRUGO);
+MODULE_PARM_DESC(busnum, "bus num of the controller");
+
+static int chipselect = -1;
+module_param(chipselect, int, S_IRUGO);
+MODULE_PARM_DESC(chipselect, "chip select of the desired device");
+
+static int maxspeed = 10000000;
+module_param(maxspeed, int, S_IRUGO);
+MODULE_PARM_DESC(maxspeed, "max_speed of the desired device");
+
+static int spimode = SPI_MODE_3;
+module_param(spimode, int, S_IRUGO);
+MODULE_PARM_DESC(spimode, "mode of the desired device");
+
+static struct spi_device *spi;
+
 /*-------------------------------------------------------------------------*/
 
 /*
@@ -221,7 +246,7 @@
 	struct spi_transfer	*k_tmp;
 	struct spi_ioc_transfer *u_tmp;
 	unsigned		n, total;
-	u8			*buf;
+	u8			*buf, *bufrx;
 	int			status = -EFAULT;
 
 	spi_message_init(&msg);
@@ -234,6 +259,7 @@
 	 * to initialize a kernel version of the same transfer.
 	 */
 	buf = spidev->buffer;
+	bufrx = spidev->bufferrx;
 	total = 0;
 	for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;
 			n;
@@ -247,7 +273,7 @@
 		}
 
 		if (u_tmp->rx_buf) {
-			k_tmp->rx_buf = buf;
+			k_tmp->rx_buf = bufrx;
 			if (!access_ok(VERIFY_WRITE, (u8 __user *)
 						(uintptr_t) u_tmp->rx_buf,
 						u_tmp->len))
@@ -261,6 +287,7 @@
 				goto done;
 		}
 		buf += k_tmp->len;
+		bufrx += k_tmp->len;
 
 		k_tmp->cs_change = !!u_tmp->cs_change;
 		k_tmp->bits_per_word = u_tmp->bits_per_word;
@@ -285,7 +312,7 @@
 		goto done;
 
 	/* copy any rx data out of bounce buffer */
-	buf = spidev->buffer;
+	buf = spidev->bufferrx;
 	for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) {
 		if (u_tmp->rx_buf) {
 			if (__copy_to_user((u8 __user *)
@@ -503,6 +530,15 @@
 				status = -ENOMEM;
 			}
 		}
+		if (!spidev->bufferrx) {
+			spidev->bufferrx = kmalloc(bufsiz, GFP_KERNEL);
+			if (!spidev->bufferrx) {
+				dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");
+				kfree(spidev->buffer);
+				spidev->buffer = NULL;
+				status = -ENOMEM;
+			}
+		}
 		if (status == 0) {
 			spidev->users++;
 			filp->private_data = spidev;
@@ -531,6 +567,8 @@
 
 		kfree(spidev->buffer);
 		spidev->buffer = NULL;
+		kfree(spidev->bufferrx);
+		spidev->bufferrx = NULL;
 
 		/* ... after we unbound from the underlying device? */
 		spin_lock_irq(&spidev->spi_lock);
@@ -674,21 +712,58 @@
 
 	spidev_class = class_create(THIS_MODULE, "spidev");
 	if (IS_ERR(spidev_class)) {
-		unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
-		return PTR_ERR(spidev_class);
+		status = PTR_ERR(spidev_class);
+		goto error_class;
 	}
 
 	status = spi_register_driver(&spidev_spi_driver);
-	if (status < 0) {
-		class_destroy(spidev_class);
-		unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
+	if (status < 0)
+		goto error_register;
+
+	if (busnum != -1 && chipselect != -1) {
+		struct spi_board_info chip = {
+					.modalias	= "spidev",
+					.mode		= spimode,
+					.bus_num	= busnum,
+					.chip_select	= chipselect,
+					.max_speed_hz	= maxspeed,
+		};
+
+		struct spi_master *master;
+
+		master = spi_busnum_to_master(busnum);
+		if (!master) {
+			status = -ENODEV;
+			goto error_busnum;
+		}
+
+		/* We create a virtual device that will sit on the bus */
+		spi = spi_new_device(master, &chip);
+		if (!spi) {
+			status = -EBUSY;
+			goto error_mem;
+		}
+		dev_dbg(&spi->dev, "busnum=%d cs=%d bufsiz=%d maxspeed=%d",
+			busnum, chipselect, bufsiz, maxspeed);
 	}
+	return 0;
+error_mem:
+error_busnum:
+	spi_unregister_driver(&spidev_spi_driver);
+error_register:
+	class_destroy(spidev_class);
+error_class:
+	unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
 	return status;
 }
 module_init(spidev_init);
 
 static void __exit spidev_exit(void)
 {
+	if (spi) {
+		spi_unregister_device(spi);
+		spi = NULL;
+	}
 	spi_unregister_driver(&spidev_spi_driver);
 	class_destroy(spidev_class);
 	unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig
new file mode 100644
index 0000000..84fd462
--- /dev/null
+++ b/drivers/spmi/Kconfig
@@ -0,0 +1,38 @@
+#
+# SPMI driver configuration
+#
+menuconfig SPMI
+	bool "SPMI support"
+	help
+	  SPMI (System Power Management Interface) is a two-wire
+	  serial interface between baseband and application processors
+	  and Power Management Integrated Circuits (PMIC).
+
+if SPMI
+config SPMI_MSM_PMIC_ARB
+	tristate "Qualcomm MSM SPMI Controller (PMIC Arbiter)"
+	help
+	  If you say yes to this option, support will be included for the
+	  built-in SPMI PMIC Arbiter interface on Qualcomm MSM family
+	  processors.
+
+	  This is required for communicating with Qualcomm PMICs and
+	  other devices that have the SPMI interface.
+
+config MSM_QPNP
+	depends on ARCH_MSMCOPPER
+	depends on OF_SPMI
+	bool "MSM QPNP"
+	help
+	  Say 'y' here to include support for the Qualcomm QPNP
+
+config MSM_QPNP_INT
+	depends on SPARSE_IRQ
+	depends on ARCH_MSMCOPPER
+	depends on OF_SPMI
+	depends on MSM_QPNP
+	bool "MSM QPNP INT"
+	help
+	  Say 'y' here to include support for the Qualcomm QPNP interrupt
+	  support. QPNP is a SPMI based PMIC implementation.
+endif
diff --git a/drivers/spmi/Makefile b/drivers/spmi/Makefile
new file mode 100644
index 0000000..d59a610
--- /dev/null
+++ b/drivers/spmi/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for kernel SPMI framework.
+#
+obj-$(CONFIG_SPMI)			+= spmi.o
+obj-$(CONFIG_SPMI_MSM_PMIC_ARB)		+= spmi-pmic-arb.o
+obj-$(CONFIG_MSM_QPNP)                  += qpnp.o
+obj-$(CONFIG_MSM_QPNP_INT)		+= qpnp-int.o
diff --git a/drivers/spmi/qpnp-int.c b/drivers/spmi/qpnp-int.c
new file mode 100644
index 0000000..2998c01
--- /dev/null
+++ b/drivers/spmi/qpnp-int.c
@@ -0,0 +1,510 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/interrupt.h>
+#include <linux/spmi.h>
+#include <linux/radix-tree.h>
+#include <linux/slab.h>
+#include <linux/printk.h>
+
+#include <asm/irq.h>
+#include <asm/mach/irq.h>
+#include <mach/qpnp-int.h>
+
+#define QPNPINT_MAX_BUSSES 1
+
+/* 16 slave_ids, 256 per_ids per slave, and 8 ints per per_id */
+#define QPNPINT_NR_IRQS (16 * 256 * 8)
+
+enum qpnpint_regs {
+	QPNPINT_REG_RT_STS		= 0x10,
+	QPNPINT_REG_SET_TYPE		= 0x11,
+	QPNPINT_REG_POLARITY_HIGH	= 0x12,
+	QPNPINT_REG_POLARITY_LOW	= 0x13,
+	QPNPINT_REG_LATCHED_CLR		= 0x14,
+	QPNPINT_REG_EN_SET		= 0x15,
+	QPNPINT_REG_EN_CLR		= 0x16,
+	QPNPINT_REG_LATCHED_STS		= 0x18,
+};
+
+struct q_perip_data {
+	uint8_t type;	    /* bitmap */
+	uint8_t pol_high;   /* bitmap */
+	uint8_t pol_low;    /* bitmap */
+	uint8_t int_en;     /* bitmap */
+	uint8_t use_count;
+};
+
+struct q_irq_data {
+	uint32_t priv_d; /* data to optimize arbiter interactions */
+	struct q_chip_data *chip_d;
+	struct q_perip_data *per_d;
+	uint8_t mask_shift;
+	uint8_t spmi_slave;
+	uint16_t spmi_offset;
+};
+
+struct q_chip_data {
+	int bus_nr;
+	struct irq_domain domain;
+	struct qpnp_local_int cb;
+	struct spmi_controller *spmi_ctrl;
+	struct radix_tree_root per_tree;
+};
+
+static struct q_chip_data chip_data[QPNPINT_MAX_BUSSES] __read_mostly;
+
+/**
+ * qpnpint_encode_hwirq - translate between qpnp_irq_spec and
+ *			  hwirq representation.
+ *
+ * slave_offset = (addr->slave * 256 * 8);
+ * perip_offset = slave_offset + (addr->perip * 8);
+ * return perip_offset + addr->irq;
+ */
+static inline int qpnpint_encode_hwirq(struct qpnp_irq_spec *spec)
+{
+	uint32_t hwirq;
+
+	if (spec->slave > 15 || spec->irq > 7)
+		return -EINVAL;
+
+	hwirq = (spec->slave << 11);
+	hwirq |= (spec->per << 3);
+	hwirq |= spec->irq;
+
+	return hwirq;
+}
+/**
+ * qpnpint_decode_hwirq - translate between hwirq and
+ *			  qpnp_irq_spec representation.
+ */
+static inline int qpnpint_decode_hwirq(unsigned long hwirq,
+					struct qpnp_irq_spec *spec)
+{
+	if (hwirq > 65535)
+		return -EINVAL;
+
+	spec->slave = (hwirq >> 11) & 0xF;
+	spec->per = (hwirq >> 3) & 0xFF;
+	spec->irq = hwirq & 0x7;
+	return 0;
+}
+
+static int qpnpint_spmi_write(struct q_irq_data *irq_d, uint8_t reg,
+			      void *buf, uint32_t len)
+{
+	struct q_chip_data *chip_d = irq_d->chip_d;
+	int rc;
+
+	if (!chip_d->spmi_ctrl)
+		return -ENODEV;
+
+	rc = spmi_ext_register_writel(chip_d->spmi_ctrl, irq_d->spmi_slave,
+				      irq_d->spmi_offset + reg, buf, len);
+	return rc;
+}
+
+static void qpnpint_irq_mask(struct irq_data *d)
+{
+	struct q_irq_data *irq_d = irq_data_get_irq_chip_data(d);
+	struct q_chip_data *chip_d = irq_d->chip_d;
+	struct q_perip_data *per_d = irq_d->per_d;
+	struct qpnp_irq_spec q_spec;
+	int rc;
+
+	pr_debug("hwirq %lu irq: %d\n", d->hwirq, d->irq);
+
+	if (chip_d->cb.mask) {
+		rc = qpnpint_decode_hwirq(d->hwirq, &q_spec);
+		if (rc)
+			pr_err("%s: decode failed on hwirq %lu\n",
+						 __func__, d->hwirq);
+		else
+			chip_d->cb.mask(chip_d->spmi_ctrl, &q_spec,
+								irq_d->priv_d);
+	}
+
+	per_d->int_en &= ~irq_d->mask_shift;
+
+	rc = qpnpint_spmi_write(irq_d, QPNPINT_REG_EN_CLR,
+					(u8 *)&irq_d->mask_shift, 1);
+	if (rc)
+		pr_err("%s: spmi failure on irq %d\n",
+						 __func__, d->irq);
+}
+
+static void qpnpint_irq_mask_ack(struct irq_data *d)
+{
+	struct q_irq_data *irq_d = irq_data_get_irq_chip_data(d);
+	struct q_chip_data *chip_d = irq_d->chip_d;
+	struct q_perip_data *per_d = irq_d->per_d;
+	struct qpnp_irq_spec q_spec;
+	int rc;
+
+	pr_debug("hwirq %lu irq: %d mask: 0x%x\n", d->hwirq, d->irq,
+							irq_d->mask_shift);
+
+	if (chip_d->cb.mask) {
+		rc = qpnpint_decode_hwirq(d->hwirq, &q_spec);
+		if (rc)
+			pr_err("%s: decode failed on hwirq %lu\n",
+						 __func__, d->hwirq);
+		else
+			chip_d->cb.mask(chip_d->spmi_ctrl, &q_spec,
+								irq_d->priv_d);
+	}
+
+	per_d->int_en &= ~irq_d->mask_shift;
+
+	rc = qpnpint_spmi_write(irq_d, QPNPINT_REG_EN_CLR,
+							&irq_d->mask_shift, 1);
+	if (rc)
+		pr_err("%s: spmi failure on irq %d\n",
+						 __func__, d->irq);
+
+	rc = qpnpint_spmi_write(irq_d, QPNPINT_REG_LATCHED_CLR,
+							&irq_d->mask_shift, 1);
+	if (rc)
+		pr_err("%s: spmi failure on irq %d\n",
+						 __func__, d->irq);
+}
+
+static void qpnpint_irq_unmask(struct irq_data *d)
+{
+	struct q_irq_data *irq_d = irq_data_get_irq_chip_data(d);
+	struct q_chip_data *chip_d = irq_d->chip_d;
+	struct q_perip_data *per_d = irq_d->per_d;
+	struct qpnp_irq_spec q_spec;
+	int rc;
+
+	pr_debug("hwirq %lu irq: %d\n", d->hwirq, d->irq);
+
+	if (chip_d->cb.unmask) {
+		rc = qpnpint_decode_hwirq(d->hwirq, &q_spec);
+		if (rc)
+			pr_err("%s: decode failed on hwirq %lu\n",
+						 __func__, d->hwirq);
+		else
+			chip_d->cb.unmask(chip_d->spmi_ctrl, &q_spec,
+								irq_d->priv_d);
+	}
+
+	per_d->int_en |= irq_d->mask_shift;
+	rc = qpnpint_spmi_write(irq_d, QPNPINT_REG_EN_SET,
+					&irq_d->mask_shift, 1);
+	if (rc)
+		pr_err("%s: spmi failure on irq %d\n",
+						 __func__, d->irq);
+}
+
+static int qpnpint_irq_set_type(struct irq_data *d, unsigned int flow_type)
+{
+	struct q_irq_data *irq_d = irq_data_get_irq_chip_data(d);
+	struct q_perip_data *per_d = irq_d->per_d;
+	int rc;
+	u8 buf[3];
+
+	pr_debug("hwirq %lu irq: %d flow: 0x%x\n", d->hwirq,
+							d->irq, flow_type);
+
+	per_d->pol_high &= ~irq_d->mask_shift;
+	per_d->pol_low &= ~irq_d->mask_shift;
+	if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
+		per_d->type |= irq_d->mask_shift; /* edge trig */
+		if (flow_type & IRQF_TRIGGER_RISING)
+			per_d->pol_high |= irq_d->mask_shift;
+		if (flow_type & IRQF_TRIGGER_FALLING)
+			per_d->pol_low |= irq_d->mask_shift;
+	} else {
+		if ((flow_type & IRQF_TRIGGER_HIGH) &&
+		    (flow_type & IRQF_TRIGGER_LOW))
+			return -EINVAL;
+		per_d->type &= ~irq_d->mask_shift; /* level trig */
+		if (flow_type & IRQF_TRIGGER_HIGH)
+			per_d->pol_high |= irq_d->mask_shift;
+		else
+			per_d->pol_high &= ~irq_d->mask_shift;
+	}
+
+	buf[0] = per_d->type;
+	buf[1] = per_d->pol_high;
+	buf[2] = per_d->pol_low;
+
+	rc = qpnpint_spmi_write(irq_d, QPNPINT_REG_SET_TYPE, &buf, 3);
+	if (rc)
+		pr_err("%s: spmi failure on irq %d\n",
+						 __func__, d->irq);
+	return rc;
+}
+
+static struct irq_chip qpnpint_chip = {
+	.name		= "qpnp-int",
+	.irq_mask	= qpnpint_irq_mask,
+	.irq_mask_ack	= qpnpint_irq_mask_ack,
+	.irq_unmask	= qpnpint_irq_unmask,
+	.irq_set_type	= qpnpint_irq_set_type,
+};
+
+static int qpnpint_init_irq_data(struct q_chip_data *chip_d,
+				 struct q_irq_data *irq_d,
+				 unsigned long hwirq)
+{
+	struct qpnp_irq_spec q_spec;
+	int rc;
+
+	irq_d->mask_shift = 1 << (hwirq & 0x7);
+	rc = qpnpint_decode_hwirq(hwirq, &q_spec);
+	if (rc < 0)
+		return rc;
+	irq_d->spmi_slave = q_spec.slave;
+	irq_d->spmi_offset = q_spec.per << 8;
+	irq_d->per_d->use_count++;
+	irq_d->chip_d = chip_d;
+
+	if (chip_d->cb.register_priv_data)
+		rc = chip_d->cb.register_priv_data(chip_d->spmi_ctrl, &q_spec,
+							&irq_d->priv_d);
+	return rc;
+}
+
+static struct q_irq_data *qpnpint_alloc_irq_data(
+					struct q_chip_data *chip_d,
+					unsigned long hwirq)
+{
+	struct q_irq_data *irq_d;
+	struct q_perip_data *per_d;
+
+	irq_d = kzalloc(sizeof(struct q_irq_data), GFP_KERNEL);
+	if (!irq_d)
+		return ERR_PTR(-ENOMEM);
+
+	/**
+	 * The Peripheral Tree is keyed from the slave + per_id. We're
+	 * ignoring the irq bits here since this peripheral structure
+	 * should be common for all irqs on the same peripheral.
+	 */
+	per_d = radix_tree_lookup(&chip_d->per_tree, (hwirq & ~0x7));
+	if (!per_d) {
+		per_d = kzalloc(sizeof(struct q_perip_data), GFP_KERNEL);
+		if (!per_d)
+			return ERR_PTR(-ENOMEM);
+		radix_tree_insert(&chip_d->per_tree,
+				  (hwirq & ~0x7), per_d);
+	}
+	irq_d->per_d = per_d;
+
+	return irq_d;
+}
+
+static int qpnpint_register_int(uint32_t busno, unsigned long hwirq)
+{
+	int irq, rc;
+	struct irq_domain *domain;
+	struct q_irq_data *irq_d;
+
+	pr_debug("busno = %u hwirq = %lu\n", busno, hwirq);
+
+	if (hwirq < 0 || hwirq >= 32768) {
+		pr_err("%s: hwirq %lu out of qpnp interrupt bounds\n",
+							__func__, hwirq);
+		return -EINVAL;
+	}
+
+	if (busno < 0 || busno > QPNPINT_MAX_BUSSES) {
+		pr_err("%s: invalid bus number %d\n", __func__, busno);
+		return -EINVAL;
+	}
+
+	domain = &chip_data[busno].domain;
+	irq = irq_domain_to_irq(domain, hwirq);
+
+	rc = irq_alloc_desc_at(irq, numa_node_id());
+	if (rc < 0) {
+		if (rc != -EEXIST)
+			pr_err("%s: failed to alloc irq at %d with "
+					"rc %d\n", __func__, irq, rc);
+		return rc;
+	}
+	irq_d = qpnpint_alloc_irq_data(&chip_data[busno], hwirq);
+	if (IS_ERR(irq_d)) {
+		pr_err("%s: failed to alloc irq data %d with "
+					"rc %d\n", __func__, irq, rc);
+		rc = PTR_ERR(irq_d);
+		goto register_err_cleanup;
+	}
+	rc = qpnpint_init_irq_data(&chip_data[busno], irq_d, hwirq);
+	if (rc) {
+		pr_err("%s: failed to init irq data %d with "
+					"rc %d\n", __func__, irq, rc);
+		goto register_err_cleanup;
+	}
+
+	irq_domain_register_irq(domain, hwirq);
+
+	irq_set_chip_and_handler(irq,
+			&qpnpint_chip,
+			handle_level_irq);
+	irq_set_chip_data(irq, irq_d);
+#ifdef CONFIG_ARM
+	set_irq_flags(irq, IRQF_VALID);
+#else
+	irq_set_noprobe(irq);
+#endif
+	return 0;
+
+register_err_cleanup:
+	irq_free_desc(irq);
+	if (!IS_ERR(irq_d)) {
+		if (irq_d->per_d->use_count == 1)
+			kfree(irq_d->per_d);
+		else
+			irq_d->per_d->use_count--;
+		kfree(irq_d);
+	}
+	return rc;
+}
+
+static int qpnpint_irq_domain_dt_translate(struct irq_domain *d,
+				       struct device_node *controller,
+				       const u32 *intspec, unsigned int intsize,
+				       unsigned long *out_hwirq,
+				       unsigned int *out_type)
+{
+	struct qpnp_irq_spec addr;
+	struct q_chip_data *chip_d = d->priv;
+	int ret;
+
+	pr_debug("%s: intspec[0] 0x%x intspec[1] 0x%x intspec[2] 0x%x\n",
+				__func__, intspec[0], intspec[1], intspec[2]);
+
+	if (d->of_node != controller)
+		return -EINVAL;
+	if (intsize != 3)
+		return -EINVAL;
+
+	addr.irq = intspec[2] & 0x7;
+	addr.per = intspec[1] & 0xFF;
+	addr.slave = intspec[0] & 0xF;
+
+	ret = qpnpint_encode_hwirq(&addr);
+	if (ret < 0) {
+		pr_err("%s: invalid intspec\n", __func__);
+		return ret;
+	}
+	*out_hwirq = ret;
+	*out_type = IRQ_TYPE_NONE;
+
+	/**
+	 * Register the interrupt if it's not already registered.
+	 * This implies that mapping a qpnp interrupt allocates
+	 * resources.
+	 */
+	ret = qpnpint_register_int(chip_d->bus_nr, *out_hwirq);
+	if (ret && ret != -EEXIST) {
+		pr_err("%s: Cannot register hwirq %lu\n", __func__, *out_hwirq);
+		return ret;
+	}
+
+	return 0;
+}
+
+const struct irq_domain_ops qpnpint_irq_domain_ops = {
+	.dt_translate = qpnpint_irq_domain_dt_translate,
+};
+
+int qpnpint_register_controller(unsigned int busno,
+				struct qpnp_local_int *li_cb)
+{
+	if (busno >= QPNPINT_MAX_BUSSES)
+		return -EINVAL;
+	chip_data[busno].cb = *li_cb;
+	chip_data[busno].spmi_ctrl = spmi_busnum_to_ctrl(busno);
+	if (!chip_data[busno].spmi_ctrl)
+		return -ENOENT;
+
+	return 0;
+}
+EXPORT_SYMBOL(qpnpint_register_controller);
+
+int qpnpint_handle_irq(struct spmi_controller *spmi_ctrl,
+		       struct qpnp_irq_spec *spec)
+{
+	struct irq_domain *domain;
+	unsigned long hwirq, busno;
+	int irq;
+
+	pr_debug("spec slave = %u per = %u irq = %u\n",
+					spec->slave, spec->per, spec->irq);
+
+	if (!spec || !spmi_ctrl)
+		return -EINVAL;
+
+	busno = spmi_ctrl->nr;
+	if (busno >= QPNPINT_MAX_BUSSES)
+		return -EINVAL;
+
+	hwirq = qpnpint_encode_hwirq(spec);
+	if (hwirq < 0) {
+		pr_err("%s: invalid irq spec passed\n", __func__);
+		return -EINVAL;
+	}
+
+	domain = &chip_data[busno].domain;
+	irq = irq_domain_to_irq(domain, hwirq);
+
+	generic_handle_irq(irq);
+
+	return 0;
+}
+EXPORT_SYMBOL(qpnpint_handle_irq);
+
+/**
+ * This assumes that there's a relationship between the order of the interrupt
+ * controllers specified to of_irq_match() is the SPMI device topology. If
+ * this ever turns out to be a bad assumption, then of_irq_init_cb_t should
+ * be modified to pass a parameter to this function.
+ */
+static int qpnpint_cnt __initdata;
+
+int __init qpnpint_of_init(struct device_node *node, struct device_node *parent)
+{
+	struct q_chip_data *chip_d = &chip_data[qpnpint_cnt];
+	struct irq_domain *domain = &chip_d->domain;
+
+	INIT_RADIX_TREE(&chip_d->per_tree, GFP_ATOMIC);
+
+	domain->irq_base = irq_domain_find_free_range(0, QPNPINT_NR_IRQS);
+	domain->nr_irq = QPNPINT_NR_IRQS;
+	domain->of_node = of_node_get(node);
+	domain->priv = chip_d;
+	domain->ops = &qpnpint_irq_domain_ops;
+	irq_domain_add(domain);
+
+	pr_info("irq_base = %d\n", domain->irq_base);
+
+	qpnpint_cnt++;
+
+	return 0;
+}
+EXPORT_SYMBOL(qpnpint_of_init);
diff --git a/drivers/spmi/qpnp.c b/drivers/spmi/qpnp.c
new file mode 100644
index 0000000..a164efb
--- /dev/null
+++ b/drivers/spmi/qpnp.c
@@ -0,0 +1,56 @@
+/* Copyright (c) 2002-3 Patrick Mochel
+ * Copyright (c) 2002-3 Open Source Development Labs
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Resource handling based on platform.c.
+ */
+
+#include <linux/export.h>
+#include <mach/qpnp.h>
+
+/**
+ * qpnp_get_resource - get a resource for a device
+ * @dev: qpnp device
+ * @type: resource type
+ * @num: resource index
+ */
+struct resource *qpnp_get_resource(struct spmi_device *dev,
+				   unsigned int node_idx, unsigned int type,
+				   unsigned int res_num)
+{
+	int i;
+
+	for (i = 0; i < dev->dev_node[node_idx].num_resources; i++) {
+		struct resource *r = &dev->dev_node[node_idx].resource[i];
+
+		if (type == resource_type(r) && res_num-- == 0)
+			return r;
+	}
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(qpnp_get_resource);
+
+/**
+ * qpnp_get_irq - get an IRQ for a device
+ * @dev: qpnp device
+ * @num: IRQ number index
+ */
+int qpnp_get_irq(struct spmi_device *dev, unsigned int node_idx,
+					  unsigned int res_num)
+{
+	struct resource *r = qpnp_get_resource(dev, node_idx,
+						IORESOURCE_IRQ, res_num);
+
+	return r ? r->start : -ENXIO;
+}
+EXPORT_SYMBOL_GPL(qpnp_get_irq);
+
diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c
new file mode 100644
index 0000000..f22b900
--- /dev/null
+++ b/drivers/spmi/spmi-pmic-arb.c
@@ -0,0 +1,722 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spmi.h>
+#include <linux/of.h>
+#include <linux/interrupt.h>
+#include <linux/of_spmi.h>
+#include <linux/module.h>
+#include <mach/qpnp-int.h>
+
+#define SPMI_PMIC_ARB_NAME		"spmi_pmic_arb"
+
+/* PMIC Arbiter configuration registers */
+#define PMIC_ARB_VERSION		0x0000
+#define PMIC_ARB_INT_EN			0x0004
+
+/* PMIC Arbiter channel registers */
+#define PMIC_ARB_CMD(N)			(0x0800 + (0x80 * (N)))
+#define PMIC_ARB_CONFIG(N)		(0x0804 + (0x80 * (N)))
+#define PMIC_ARB_STATUS(N)		(0x0808 + (0x80 * (N)))
+#define PMIC_ARB_WDATA0(N)		(0x0810 + (0x80 * (N)))
+#define PMIC_ARB_WDATA1(N)		(0x0814 + (0x80 * (N)))
+#define PMIC_ARB_RDATA0(N)		(0x0818 + (0x80 * (N)))
+#define PMIC_ARB_RDATA1(N)		(0x081C + (0x80 * (N)))
+
+/* Interrupt Controller */
+#define SPMI_PIC_OWNER_ACC_STATUS(M, N)	(0x0000 + ((32 * (M)) + (4 * (N))))
+#define SPMI_PIC_ACC_ENABLE(N)		(0x0200 + (4 * (N)))
+#define SPMI_PIC_IRQ_STATUS(N)		(0x0600 + (4 * (N)))
+#define SPMI_PIC_IRQ_CLEAR(N)		(0x0A00 + (4 * (N)))
+
+/* Channel Status fields */
+enum pmic_arb_chnl_status {
+	PMIC_ARB_STATUS_DONE	= (1 << 0),
+	PMIC_ARB_STATUS_FAILURE	= (1 << 1),
+	PMIC_ARB_STATUS_DENIED	= (1 << 2),
+	PMIC_ARB_STATUS_DROPPED	= (1 << 3),
+};
+
+/* Command register fields */
+#define PMIC_ARB_CMD_MAX_BYTE_COUNT	8
+
+/* Command Opcodes */
+enum pmic_arb_cmd_op_code {
+	PMIC_ARB_OP_EXT_WRITEL = 0,
+	PMIC_ARB_OP_EXT_READL = 1,
+	PMIC_ARB_OP_EXT_WRITE = 2,
+	PMIC_ARB_OP_RESET = 3,
+	PMIC_ARB_OP_SLEEP = 4,
+	PMIC_ARB_OP_SHUTDOWN = 5,
+	PMIC_ARB_OP_WAKEUP = 6,
+	PMIC_ARB_OP_AUTHENTICATE = 7,
+	PMIC_ARB_OP_MSTR_READ = 8,
+	PMIC_ARB_OP_MSTR_WRITE = 9,
+	PMIC_ARB_OP_EXT_READ = 13,
+	PMIC_ARB_OP_WRITE = 14,
+	PMIC_ARB_OP_READ = 15,
+	PMIC_ARB_OP_ZERO_WRITE = 16,
+};
+
+/* Maximum number of support PMIC peripherals */
+#define PMIC_ARB_MAX_PERIPHS		256
+#define PMIC_ARB_PERIPH_ID_VALID	(1 << 15)
+#define PMIC_ARB_TIMEOUT_US		100
+
+#define PMIC_ARB_APID_MASK				0xFF
+#define PMIC_ARB_PPID_MASK				0xFFF
+/* extract PPID and APID from interrupt map in .dts config file format */
+#define PMIC_ARB_DEV_TRE_2_PPID(MAP_COMPRS_VAL)		\
+			((MAP_COMPRS_VAL) >> (20))
+#define PMIC_ARB_DEV_TRE_2_APID(MAP_COMPRS_VAL)		\
+			((MAP_COMPRS_VAL) &  PMIC_ARB_APID_MASK)
+
+/**
+ * base - base address of the PMIC Arbiter core registers.
+ * intr - base address of the SPMI interrupt control registers
+ */
+struct spmi_pmic_arb_dev {
+	struct spmi_controller	controller;
+	struct device		*dev;
+	struct device		*slave;
+	void __iomem		*base;
+	void __iomem		*intr;
+	int			pic_irq;
+	spinlock_t		lock;
+	u8			owner;
+	u8			channel;
+	u8			min_apid;
+	u8			max_apid;
+	u16			periph_id_map[PMIC_ARB_MAX_PERIPHS];
+};
+
+static u32 pmic_arb_read(struct spmi_pmic_arb_dev *dev, u32 offset)
+{
+	u32 val = readl_relaxed(dev->base + offset);
+	pr_debug("address 0x%p, val 0x%x\n", dev->base + offset, val);
+	return val;
+}
+
+static void pmic_arb_write(struct spmi_pmic_arb_dev *dev, u32 offset, u32 val)
+{
+	pr_debug("address 0x%p, val 0x%x\n", dev->base + offset, val);
+	writel_relaxed(val, dev->base + offset);
+}
+
+static int pmic_arb_wait_for_done(struct spmi_pmic_arb_dev *dev)
+{
+	u32 status = 0;
+	u32 timeout = PMIC_ARB_TIMEOUT_US;
+	u32 offset = PMIC_ARB_STATUS(dev->channel);
+
+	while (timeout--) {
+		status = pmic_arb_read(dev, offset);
+
+		if (status & PMIC_ARB_STATUS_DONE) {
+			if (status & PMIC_ARB_STATUS_DENIED) {
+				dev_err(dev->dev,
+					"%s: transaction denied (0x%x)\n",
+					__func__, status);
+				return -EPERM;
+			}
+
+			if (status & PMIC_ARB_STATUS_FAILURE) {
+				dev_err(dev->dev,
+					"%s: transaction failed (0x%x)\n",
+					__func__, status);
+				return -EIO;
+			}
+
+			if (status & PMIC_ARB_STATUS_DROPPED) {
+				dev_err(dev->dev,
+					"%s: transaction dropped (0x%x)\n",
+					__func__, status);
+				return -EIO;
+			}
+
+			return 0;
+		}
+		udelay(1);
+	}
+
+	dev_err(dev->dev, "%s: timeout, status 0x%x\n", __func__, status);
+	return -ETIMEDOUT;
+}
+
+static void pa_read_data(struct spmi_pmic_arb_dev *dev, u8 *buf, u32 reg, u8 bc)
+{
+	u32 data = pmic_arb_read(dev, reg);
+
+	switch (bc & 0x3) {
+	case 3:
+		*buf++ = data & 0xff;
+		data >>= 8;
+	case 2:
+		*buf++ = data & 0xff;
+		data >>= 8;
+	case 1:
+		*buf++ = data & 0xff;
+		data >>= 8;
+	case 0:
+		*buf++ = data & 0xff;
+	default:
+		break;
+	}
+}
+
+static void
+pa_write_data(struct spmi_pmic_arb_dev *dev, u8 *buf, u32 reg, u8 bc)
+{
+	u32 data = 0;
+
+	switch (bc & 0x3) {
+	case 3:
+		data = (buf[0]|buf[1]<<8|buf[2]<<16|buf[3]<<24);
+		break;
+	case 2:
+		data = (buf[0]|buf[1]<<8|buf[2]<<16);
+		break;
+	case 1:
+		data = (buf[0]|buf[1]<<8);
+		break;
+	case 0:
+		data = (buf[0]);
+		break;
+	default:
+		break;
+	}
+
+	pmic_arb_write(dev, reg, data);
+}
+
+/* Non-data command */
+static int pmic_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid)
+{
+	struct spmi_pmic_arb_dev *pmic_arb = spmi_get_ctrldata(ctrl);
+	unsigned long flags;
+	u32 cmd;
+	int rc;
+
+	pr_debug("op:0x%x sid:%d\n", opc, sid);
+
+	/* Check for valid non-data command */
+	if (opc < SPMI_CMD_RESET || opc > SPMI_CMD_WAKEUP)
+		return -EINVAL;
+
+	cmd = ((opc | 0x40) << 27) | ((sid & 0xf) << 20);
+
+	spin_lock_irqsave(&pmic_arb->lock, flags);
+	pmic_arb_write(pmic_arb, PMIC_ARB_CMD(pmic_arb->channel), cmd);
+	rc = pmic_arb_wait_for_done(pmic_arb);
+	spin_unlock_irqrestore(&pmic_arb->lock, flags);
+
+	return rc;
+}
+
+static int pmic_arb_read_cmd(struct spmi_controller *ctrl,
+				u8 opc, u8 sid, u16 addr, u8 bc, u8 *buf)
+{
+	struct spmi_pmic_arb_dev *pmic_arb = spmi_get_ctrldata(ctrl);
+	unsigned long flags;
+	u32 cmd;
+	int rc;
+
+	pr_debug("op:0x%x sid:%d bc:%d addr:0x%x\n", opc, sid, bc, addr);
+
+	/* Check the opcode */
+	if (opc >= 0x60 && opc <= 0x7F)
+		opc = PMIC_ARB_OP_READ;
+	else if (opc >= 0x20 && opc <= 0x2F)
+		opc = PMIC_ARB_OP_EXT_READ;
+	else if (opc >= 0x38 && opc <= 0x3F)
+		opc = PMIC_ARB_OP_EXT_READL;
+	else
+		return -EINVAL;
+
+	cmd = (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7);
+
+	spin_lock_irqsave(&pmic_arb->lock, flags);
+	pmic_arb_write(pmic_arb, PMIC_ARB_CMD(pmic_arb->channel), cmd);
+	rc = pmic_arb_wait_for_done(pmic_arb);
+	if (rc)
+		goto done;
+
+	/* Read from FIFO, note 'bc' is actually number of bytes minus 1 */
+	pa_read_data(pmic_arb, buf, PMIC_ARB_RDATA0(pmic_arb->channel), bc);
+
+	if (bc > 3)
+		pa_read_data(pmic_arb, buf + 4,
+				PMIC_ARB_RDATA1(pmic_arb->channel), bc);
+
+done:
+	spin_unlock_irqrestore(&pmic_arb->lock, flags);
+	return rc;
+}
+
+static int pmic_arb_write_cmd(struct spmi_controller *ctrl,
+				u8 opc, u8 sid, u16 addr, u8 bc, u8 *buf)
+{
+	struct spmi_pmic_arb_dev *pmic_arb = spmi_get_ctrldata(ctrl);
+	unsigned long flags;
+	u32 cmd;
+	int rc;
+
+	pr_debug("op:0x%x sid:%d bc:%d addr:0x%x\n", opc, sid, bc, addr);
+
+	/* Check the opcode */
+	if (opc >= 0x40 && opc <= 0x5F)
+		opc = PMIC_ARB_OP_WRITE;
+	else if (opc >= 0x00 && opc <= 0x0F)
+		opc = PMIC_ARB_OP_EXT_WRITE;
+	else if (opc >= 0x30 && opc <= 0x37)
+		opc = PMIC_ARB_OP_EXT_WRITEL;
+	else if (opc >= 0x80 && opc <= 0xFF)
+		opc = PMIC_ARB_OP_ZERO_WRITE;
+	else
+		return -EINVAL;
+
+	cmd = (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7);
+
+	/* Write data to FIFOs */
+	spin_lock_irqsave(&pmic_arb->lock, flags);
+	pa_write_data(pmic_arb, buf, PMIC_ARB_WDATA0(pmic_arb->channel), bc);
+
+	if (bc > 3)
+		pa_write_data(pmic_arb, buf + 4,
+				PMIC_ARB_WDATA1(pmic_arb->channel), bc);
+
+	/* Start the transaction */
+	pmic_arb_write(pmic_arb, PMIC_ARB_CMD(pmic_arb->channel), cmd);
+	rc = pmic_arb_wait_for_done(pmic_arb);
+	spin_unlock_irqrestore(&pmic_arb->lock, flags);
+
+	return rc;
+}
+
+/* APID to PPID */
+static u16 get_peripheral_id(struct spmi_pmic_arb_dev *pmic_arb, u8 apid)
+{
+	return pmic_arb->periph_id_map[apid] & PMIC_ARB_PPID_MASK;
+}
+
+/* APID to PPID, returns valid flag */
+static int is_apid_valid(struct spmi_pmic_arb_dev *pmic_arb, u8 apid)
+{
+	return pmic_arb->periph_id_map[apid] & PMIC_ARB_PERIPH_ID_VALID;
+}
+
+/* PPID to APID */
+static uint32_t map_peripheral_id(struct spmi_pmic_arb_dev *pmic_arb, u16 ppid)
+{
+	int first = pmic_arb->min_apid;
+	int last = pmic_arb->max_apid;
+	int i;
+
+	/* Search table for a matching PPID */
+	for (i = first; i <= last; ++i) {
+		if ((pmic_arb->periph_id_map[i] & PMIC_ARB_PPID_MASK) == ppid)
+			return i;
+	}
+
+	dev_err(pmic_arb->dev, "Unknown ppid 0x%x\n", ppid);
+	return PMIC_ARB_MAX_PERIPHS;
+}
+
+/* Enable interrupt at the PMIC Arbiter PIC */
+static int pmic_arb_pic_enable(struct spmi_controller *ctrl,
+				struct qpnp_irq_spec *spec, uint32_t data)
+{
+	struct spmi_pmic_arb_dev *pmic_arb = spmi_get_ctrldata(ctrl);
+	u8 apid = data & PMIC_ARB_APID_MASK;
+	unsigned long flags;
+	u32 status;
+
+	dev_dbg(pmic_arb->dev, "PIC enable, apid:0x%x, sid:0x%x, pid:0x%x\n",
+				apid, spec->slave, spec->per);
+
+	if (data < pmic_arb->min_apid || data > pmic_arb->max_apid) {
+		dev_err(pmic_arb->dev, "int enable: invalid APID %d\n", data);
+		return -EINVAL;
+	}
+
+	if (!is_apid_valid(pmic_arb, apid)) {
+		dev_err(pmic_arb->dev, "int enable: int not supported\n");
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&pmic_arb->lock, flags);
+	status = readl_relaxed(pmic_arb->intr + SPMI_PIC_ACC_ENABLE(apid));
+	if (!status) {
+		writel_relaxed(0x1, pmic_arb->intr + SPMI_PIC_ACC_ENABLE(apid));
+		/* Interrupt needs to be enabled before returning to caller */
+		wmb();
+	}
+	spin_unlock_irqrestore(&pmic_arb->lock, flags);
+	return 0;
+}
+
+/* Disable interrupt at the PMIC Arbiter PIC */
+static int pmic_arb_pic_disable(struct spmi_controller *ctrl,
+				struct qpnp_irq_spec *spec, uint32_t data)
+{
+	struct spmi_pmic_arb_dev *pmic_arb = spmi_get_ctrldata(ctrl);
+	u8 apid = data & PMIC_ARB_APID_MASK;
+	unsigned long flags;
+	u32 status;
+
+	dev_dbg(pmic_arb->dev, "PIC disable, apid:0x%x, sid:0x%x, pid:0x%x\n",
+				apid, spec->slave, spec->per);
+
+	if (data < pmic_arb->min_apid || data > pmic_arb->max_apid) {
+		dev_err(pmic_arb->dev, "int disable: invalid APID %d\n", data);
+		return -EINVAL;
+	}
+
+	if (!is_apid_valid(pmic_arb, apid)) {
+		dev_err(pmic_arb->dev, "int disable: int not supported\n");
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&pmic_arb->lock, flags);
+	status = readl_relaxed(pmic_arb->intr + SPMI_PIC_ACC_ENABLE(apid));
+	if (status) {
+		writel_relaxed(0x0, pmic_arb->intr + SPMI_PIC_ACC_ENABLE(apid));
+		/* Interrupt needs to be disabled before returning to caller */
+		wmb();
+	}
+	spin_unlock_irqrestore(&pmic_arb->lock, flags);
+	return 0;
+}
+
+static irqreturn_t
+periph_interrupt(struct spmi_pmic_arb_dev *pmic_arb, u8 apid)
+{
+	u16 ppid = get_peripheral_id(pmic_arb, apid);
+	void __iomem *base = pmic_arb->intr;
+	u8 sid = (ppid >> 8) & 0x0F;
+	u8 pid = ppid & 0xFF;
+	u32 status;
+	int i;
+
+	if (!is_apid_valid(pmic_arb, apid)) {
+		dev_err(pmic_arb->dev, "unknown peripheral id 0x%x\n", ppid);
+		/* return IRQ_NONE; */
+	}
+
+	/* Read the peripheral specific interrupt bits */
+	status = readl_relaxed(base + SPMI_PIC_IRQ_STATUS(apid));
+
+	/* Clear the peripheral interrupts */
+	writel_relaxed(status, base + SPMI_PIC_IRQ_CLEAR(apid));
+	/* Interrupt needs to be cleared/acknowledged before exiting ISR */
+	mb();
+
+	dev_dbg(pmic_arb->dev,
+		"interrupt, apid:0x%x, sid:0x%x, pid:0x%x, intr:0x%x\n",
+						apid, sid, pid, status);
+
+	/* Send interrupt notification */
+	for (i = 0; status && i < 8; ++i, status >>= 1) {
+		if (status & 0x1) {
+			struct qpnp_irq_spec irq_spec = {
+				.slave = sid,
+				.per = pid,
+				.irq = i,
+			};
+			qpnpint_handle_irq(&pmic_arb->controller, &irq_spec);
+		}
+	}
+	return IRQ_HANDLED;
+}
+
+/* Peripheral interrupt handler */
+static irqreturn_t pmic_arb_periph_irq(int irq, void *dev_id)
+{
+	struct spmi_pmic_arb_dev *pmic_arb = dev_id;
+	void __iomem *intr = pmic_arb->intr;
+	u8 ee = pmic_arb->owner;
+	u32 ret = IRQ_NONE;
+	u32 status;
+
+	int first = pmic_arb->min_apid >> 5;
+	int last = pmic_arb->max_apid >> 5;
+	int i, j;
+
+	dev_dbg(pmic_arb->dev, "Peripheral interrupt detected\n");
+
+	/* Check the accumulated interrupt status */
+	for (i = first; i <= last; ++i) {
+		status = readl_relaxed(intr + SPMI_PIC_OWNER_ACC_STATUS(ee, i));
+
+		for (j = 0; status && j < 32; ++j, status >>= 1) {
+			if (status & 0x1) {
+				u8 id = (i * 32) + j;
+				ret |= periph_interrupt(pmic_arb, id);
+			}
+		}
+	}
+
+	return ret;
+}
+
+/* Callback to register an APID for specific slave/peripheral */
+static int pmic_arb_intr_priv_data(struct spmi_controller *ctrl,
+				struct qpnp_irq_spec *spec, uint32_t *data)
+{
+	struct spmi_pmic_arb_dev *pmic_arb = spmi_get_ctrldata(ctrl);
+	u16 ppid = ((spec->slave & 0x0F) << 8) | (spec->per & 0xFF);
+	*data = map_peripheral_id(pmic_arb, ppid);
+	return 0;
+}
+
+static int __devinit
+spmi_pmic_arb_get_property(struct platform_device *pdev, char *pname, u32 *prop)
+{
+	int ret = of_property_read_u32(pdev->dev.of_node, pname, prop);
+
+	if (ret)
+		dev_err(&pdev->dev, "missing property: %s\n", pname);
+	else
+		pr_debug("%s = 0x%x\n", pname, *prop);
+
+	return ret;
+}
+
+static int __devinit spmi_pmic_arb_get_map_data(struct platform_device *pdev,
+					struct spmi_pmic_arb_dev *pmic_arb)
+{
+	int i;
+	int ret;
+	int map_size;
+	u32 *map_data;
+	const int map_width = sizeof(*map_data);
+	const struct device_node *of_node = pdev->dev.of_node;
+
+	/* Get size of the mapping table (in bytes) */
+	if (!of_get_property(of_node, "qcom,pmic-arb-ppid-map", &map_size)) {
+		dev_err(&pdev->dev, "missing ppid mapping table\n");
+		return -ENODEV;
+	}
+
+	/* Map size can't exceed the maximum number of peripherals */
+	if (map_size == 0 || map_size > map_width * PMIC_ARB_MAX_PERIPHS) {
+		dev_err(&pdev->dev, "map size of %d is not valid\n", map_size);
+		return -ENODEV;
+	}
+
+	map_data = kzalloc(map_size, GFP_KERNEL);
+	if (!map_data) {
+		dev_err(&pdev->dev, "can not allocate map data\n");
+		return -ENOMEM;
+	}
+
+	ret = of_property_read_u32_array(of_node,
+		"qcom,pmic-arb-ppid-map", map_data, map_size/sizeof(u32));
+	if (ret) {
+		dev_err(&pdev->dev, "invalid or missing property: ppid-map\n");
+		goto err;
+	};
+
+	pmic_arb->max_apid = 0;
+	pmic_arb->min_apid = PMIC_ARB_MAX_PERIPHS - 1;
+
+	/* Build the mapping table from the data */
+	for (i = 0; i < map_size/sizeof(u32);) {
+		u32 map_compressed_val = map_data[i++];
+		u32 ppid = PMIC_ARB_DEV_TRE_2_PPID(map_compressed_val) ;
+		u32 apid = PMIC_ARB_DEV_TRE_2_APID(map_compressed_val) ;
+
+		if (pmic_arb->periph_id_map[apid] & PMIC_ARB_PERIPH_ID_VALID)
+			dev_warn(&pdev->dev, "duplicate APID 0x%x\n", apid);
+
+		pmic_arb->periph_id_map[apid] = ppid | PMIC_ARB_PERIPH_ID_VALID;
+
+		if (apid > pmic_arb->max_apid)
+			pmic_arb->max_apid = apid;
+
+		if (apid < pmic_arb->min_apid)
+			pmic_arb->min_apid = apid;
+	}
+
+	pr_debug("%d value(s) mapped, min:%d, max:%d\n",
+		map_size/map_width, pmic_arb->min_apid, pmic_arb->max_apid);
+
+err:
+	kfree(map_data);
+	return ret;
+}
+
+static struct qpnp_local_int spmi_pmic_arb_intr_cb = {
+	.mask = pmic_arb_pic_disable,
+	.unmask = pmic_arb_pic_enable,
+	.register_priv_data = pmic_arb_intr_priv_data,
+};
+
+static int __devinit spmi_pmic_arb_probe(struct platform_device *pdev)
+{
+	struct spmi_pmic_arb_dev *pmic_arb;
+	struct resource *mem_res;
+	u32 cell_index;
+	u32 prop;
+	int ret = 0;
+
+	pr_debug("SPMI PMIC Arbiter\n");
+
+	pmic_arb = devm_kzalloc(&pdev->dev,
+				sizeof(struct spmi_pmic_arb_dev), GFP_KERNEL);
+	if (!pmic_arb) {
+		dev_err(&pdev->dev, "can not allocate pmic_arb data\n");
+		return -ENOMEM;
+	}
+
+	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem_res) {
+		dev_err(&pdev->dev, "missing base memory resource\n");
+		return -ENODEV;
+	}
+
+	pmic_arb->base = devm_ioremap(&pdev->dev,
+					mem_res->start, resource_size(mem_res));
+	if (!pmic_arb->base) {
+		dev_err(&pdev->dev, "ioremap of 'base' failed\n");
+		return -ENOMEM;
+	}
+
+	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!mem_res) {
+		dev_err(&pdev->dev, "missing mem resource (interrupts)\n");
+		return -ENODEV;
+	}
+
+	pmic_arb->intr = devm_ioremap(&pdev->dev,
+					mem_res->start, resource_size(mem_res));
+	if (!pmic_arb->intr) {
+		dev_err(&pdev->dev, "ioremap of 'intr' failed\n");
+		return -ENOMEM;
+	}
+
+	pmic_arb->pic_irq = platform_get_irq(pdev, 0);
+	if (!pmic_arb->pic_irq) {
+		dev_err(&pdev->dev, "missing IRQ resource\n");
+		return -ENODEV;
+	}
+
+	ret = devm_request_irq(&pdev->dev, pmic_arb->pic_irq,
+		pmic_arb_periph_irq, IRQF_TRIGGER_HIGH, pdev->name, pmic_arb);
+	if (ret) {
+		dev_err(&pdev->dev, "request IRQ failed\n");
+		return ret;
+	}
+
+	/* Get properties from the device tree */
+	ret = spmi_pmic_arb_get_property(pdev, "cell-index", &cell_index);
+	if (ret)
+		return -ENODEV;
+
+	ret = spmi_pmic_arb_get_map_data(pdev, pmic_arb);
+	if (ret)
+		return ret;
+
+	ret = spmi_pmic_arb_get_property(pdev, "qcom,pmic-arb-ee", &prop);
+	if (ret)
+		return -ENODEV;
+	pmic_arb->owner = (u8)prop;
+
+	ret = spmi_pmic_arb_get_property(pdev, "qcom,pmic-arb-channel", &prop);
+	if (ret)
+		return -ENODEV;
+	pmic_arb->channel = (u8)prop;
+
+	pmic_arb->dev = &pdev->dev;
+	platform_set_drvdata(pdev, pmic_arb);
+	spmi_set_ctrldata(&pmic_arb->controller, pmic_arb);
+
+	spin_lock_init(&pmic_arb->lock);
+
+	pmic_arb->controller.nr = cell_index;
+	pmic_arb->controller.dev.parent = pdev->dev.parent;
+	pmic_arb->controller.dev.of_node = of_node_get(pdev->dev.of_node);
+
+	/* Callbacks */
+	pmic_arb->controller.cmd = pmic_arb_cmd;
+	pmic_arb->controller.read_cmd = pmic_arb_read_cmd;
+	pmic_arb->controller.write_cmd =  pmic_arb_write_cmd;
+
+	ret = spmi_add_controller(&pmic_arb->controller);
+	if (ret)
+		goto err_add_controller;
+
+	/* Register the interrupt enable/disable functions */
+	qpnpint_register_controller(cell_index, &spmi_pmic_arb_intr_cb);
+
+	/* Register device(s) from the device tree */
+	of_spmi_register_devices(&pmic_arb->controller);
+
+	pr_debug("PMIC Arb Version 0x%x\n",
+			pmic_arb_read(pmic_arb, PMIC_ARB_VERSION));
+
+	return 0;
+
+err_add_controller:
+	platform_set_drvdata(pdev, NULL);
+	return ret;
+}
+
+static int __devexit spmi_pmic_arb_remove(struct platform_device *pdev)
+{
+	struct spmi_pmic_arb_dev *pmic_arb = platform_get_drvdata(pdev);
+
+	free_irq(pmic_arb->pic_irq, pmic_arb);
+	platform_set_drvdata(pdev, NULL);
+	spmi_del_controller(&pmic_arb->controller);
+	return 0;
+}
+
+static struct of_device_id spmi_pmic_arb_match_table[] = {
+	{	.compatible = "qcom,spmi-pmic-arb",
+	},
+	{}
+};
+
+static struct platform_driver spmi_pmic_arb_driver = {
+	.probe		= spmi_pmic_arb_probe,
+	.remove		= __exit_p(spmi_pmic_arb_remove),
+	.driver		= {
+		.name	= SPMI_PMIC_ARB_NAME,
+		.owner	= THIS_MODULE,
+		.of_match_table = spmi_pmic_arb_match_table,
+	},
+};
+
+static int __init spmi_pmic_arb_init(void)
+{
+	return platform_driver_register(&spmi_pmic_arb_driver);
+}
+postcore_initcall(spmi_pmic_arb_init);
+
+static void __exit spmi_pmic_arb_exit(void)
+{
+	platform_driver_unregister(&spmi_pmic_arb_driver);
+}
+module_exit(spmi_pmic_arb_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:spmi_pmic_arb");
diff --git a/drivers/spmi/spmi.c b/drivers/spmi/spmi.c
new file mode 100644
index 0000000..3f63532
--- /dev/null
+++ b/drivers/spmi/spmi.c
@@ -0,0 +1,790 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/idr.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/spmi.h>
+#include <linux/module.h>
+
+struct spmii_boardinfo {
+	struct list_head	list;
+	struct spmi_boardinfo	board_info;
+};
+
+static DEFINE_MUTEX(board_lock);
+static LIST_HEAD(board_list);
+static LIST_HEAD(spmi_ctrl_list);
+static DEFINE_IDR(ctrl_idr);
+static struct device_type spmi_ctrl_type = { 0 };
+
+#define to_spmi(dev)	platform_get_drvdata(to_platform_device(dev))
+
+/* Forward declarations */
+struct bus_type spmi_bus_type;
+static int spmi_register_controller(struct spmi_controller *ctrl);
+
+/**
+ * spmi_busnum_to_ctrl: Map bus number to controller
+ * @busnum: bus number
+ * Returns controller representing this bus number
+ */
+struct spmi_controller *spmi_busnum_to_ctrl(u32 bus_num)
+{
+	struct spmi_controller *ctrl;
+
+	mutex_lock(&board_lock);
+	list_for_each_entry(ctrl, &spmi_ctrl_list, list) {
+		if (bus_num == ctrl->nr) {
+			mutex_unlock(&board_lock);
+			return ctrl;
+		}
+	}
+	mutex_unlock(&board_lock);
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(spmi_busnum_to_ctrl);
+
+/**
+ * spmi_add_controller: Controller bring-up.
+ * @ctrl: controller to be registered.
+ * A controller is registered with the framework using this API. ctrl->nr is the
+ * desired number with which SPMI framework registers the controller.
+ * Function will return -EBUSY if the number is in use.
+ */
+int spmi_add_controller(struct spmi_controller *ctrl)
+{
+	int	id;
+	int	status;
+
+	pr_debug("adding controller for bus %d (0x%p)\n", ctrl->nr, ctrl);
+
+	if (ctrl->nr & ~MAX_ID_MASK) {
+		pr_err("invalid bus identifier %d\n", ctrl->nr);
+		return -EINVAL;
+	}
+
+retry:
+	if (idr_pre_get(&ctrl_idr, GFP_KERNEL) == 0) {
+		pr_err("no free memory for idr\n");
+		return -ENOMEM;
+	}
+
+	mutex_lock(&board_lock);
+	status = idr_get_new_above(&ctrl_idr, ctrl, ctrl->nr, &id);
+	if (status == 0 && id != ctrl->nr) {
+		status = -EAGAIN;
+		idr_remove(&ctrl_idr, id);
+	}
+	mutex_unlock(&board_lock);
+	if (status == -EAGAIN)
+		goto retry;
+
+	if (status == 0)
+		status = spmi_register_controller(ctrl);
+	return status;
+}
+EXPORT_SYMBOL_GPL(spmi_add_controller);
+
+/**
+ * spmi_del_controller: Controller tear-down.
+ * @ctrl: controller to which this device is to be added to.
+ *
+ * Controller added with the above API is torn down using this API.
+ */
+int spmi_del_controller(struct spmi_controller *ctrl)
+{
+	return -ENXIO;
+}
+EXPORT_SYMBOL_GPL(spmi_del_controller);
+
+#define spmi_device_attr_gr NULL
+#define spmi_device_uevent NULL
+static void spmi_dev_release(struct device *dev)
+{
+	struct spmi_device *spmidev = to_spmi_device(dev);
+	kfree(spmidev);
+}
+
+static struct device_type spmi_dev_type = {
+	.groups		= spmi_device_attr_gr,
+	.uevent		= spmi_device_uevent,
+	.release	= spmi_dev_release,
+};
+
+/**
+ * spmi_alloc_device: Allocate a new SPMI devices.
+ * @ctrl: controller to which this device is to be added to.
+ * Context: can sleep
+ *
+ * Allows a driver to allocate and initialize a SPMI device without
+ * registering it immediately.  This allows a driver to directly fill
+ * the spmi_device structure before calling spmi_add_device().
+ *
+ * Caller is responsible to call spmi_add_device() on the returned
+ * spmi_device.  If the caller needs to discard the spmi_device without
+ * adding it, then spmi_dev_put() should be called.
+ */
+struct spmi_device *spmi_alloc_device(struct spmi_controller *ctrl)
+{
+	struct spmi_device *spmidev;
+
+	if (!ctrl) {
+		pr_err("Missing SPMI controller\n");
+		return NULL;
+	}
+
+	spmidev = kzalloc(sizeof(*spmidev), GFP_KERNEL);
+	if (!spmidev) {
+		dev_err(&ctrl->dev, "unable to allocate spmi_device\n");
+		return NULL;
+	}
+
+	spmidev->ctrl = ctrl;
+	spmidev->dev.parent = ctrl->dev.parent;
+	spmidev->dev.bus = &spmi_bus_type;
+	spmidev->dev.type = &spmi_dev_type;
+	device_initialize(&spmidev->dev);
+
+	return spmidev;
+}
+EXPORT_SYMBOL_GPL(spmi_alloc_device);
+
+/* Validate the SPMI device structure */
+static struct device *get_valid_device(struct spmi_device *spmidev)
+{
+	struct device *dev;
+
+	if (!spmidev)
+		return NULL;
+
+	dev = &spmidev->dev;
+	if (dev->bus != &spmi_bus_type || dev->type != &spmi_dev_type)
+		return NULL;
+
+	return dev;
+}
+
+/**
+ * spmi_add_device: Add a new device without register board info.
+ * @ctrl: controller to which this device is to be added to.
+ *
+ * Called when device doesn't have an explicit client-driver to be probed, or
+ * the client-driver is a module installed dynamically.
+ */
+int spmi_add_device(struct spmi_device *spmidev)
+{
+	int rc;
+	struct device *dev = get_valid_device(spmidev);
+
+	if (!dev) {
+		pr_err("%s: invalid SPMI device\n", __func__);
+		return -EINVAL;
+	}
+
+	/* Set the device name */
+	dev_set_name(dev, "%s-%p", spmidev->name, spmidev);
+
+	/* Device may be bound to an active driver when this returns */
+	rc = device_add(dev);
+
+	if (rc < 0)
+		dev_err(dev, "Can't add %s, status %d\n", dev_name(dev), rc);
+	else
+		dev_dbg(dev, "device %s registered\n", dev_name(dev));
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(spmi_add_device);
+
+/**
+ * spmi_new_device: Instantiates a new SPMI device
+ * @ctrl: controller to which this device is to be added to.
+ * @info: board information for this device.
+ *
+ * Returns the new device or NULL.
+ */
+struct spmi_device *spmi_new_device(struct spmi_controller *ctrl,
+					struct spmi_boardinfo const *info)
+{
+	struct spmi_device *spmidev;
+	int rc;
+
+	if (!ctrl || !info)
+		return NULL;
+
+	spmidev = spmi_alloc_device(ctrl);
+	if (!spmidev)
+		return NULL;
+
+	spmidev->name = info->name;
+	spmidev->sid  = info->slave_id;
+	spmidev->dev.of_node = info->of_node;
+	spmidev->dev.platform_data = (void *)info->platform_data;
+	spmidev->num_dev_node = info->num_dev_node;
+	spmidev->dev_node = info->dev_node;
+
+	rc = spmi_add_device(spmidev);
+	if (rc < 0) {
+		spmi_dev_put(spmidev);
+		return NULL;
+	}
+
+	return spmidev;
+}
+EXPORT_SYMBOL_GPL(spmi_new_device);
+
+/* spmi_remove_device: Remove the effect of spmi_add_device() */
+void spmi_remove_device(struct spmi_device *spmi_dev)
+{
+	device_unregister(&spmi_dev->dev);
+}
+EXPORT_SYMBOL_GPL(spmi_remove_device);
+
+/* If controller is not present, only add to boards list */
+static void spmi_match_ctrl_to_boardinfo(struct spmi_controller *ctrl,
+				struct spmi_boardinfo *bi)
+{
+	struct spmi_device *spmidev;
+
+	spmidev = spmi_new_device(ctrl, bi);
+	if (!spmidev)
+		dev_err(ctrl->dev.parent, "can't create new device for %s\n",
+			bi->name);
+}
+
+/**
+ * spmi_register_board_info: Board-initialization routine.
+ * @bus_num: controller number (bus) on which this device will sit.
+ * @info: list of all devices on all controllers present on the board.
+ * @n: number of entries.
+ * API enumerates respective devices on corresponding controller.
+ * Called from board-init function.
+ */
+int spmi_register_board_info(int busnum,
+			struct spmi_boardinfo const *info, unsigned n)
+{
+	int i;
+	struct spmii_boardinfo *bi;
+
+	bi = kzalloc(n * sizeof(*bi), GFP_KERNEL);
+	if (!bi)
+		return -ENOMEM;
+
+	for (i = 0; i < n; i++, bi++, info++) {
+		struct spmi_controller *ctrl;
+
+		memcpy(&bi->board_info, info, sizeof(*info));
+		mutex_lock(&board_lock);
+		list_add_tail(&bi->list, &board_list);
+		list_for_each_entry(ctrl, &spmi_ctrl_list, list)
+			if (ctrl->nr == busnum)
+				spmi_match_ctrl_to_boardinfo(ctrl,
+							&bi->board_info);
+		mutex_unlock(&board_lock);
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(spmi_register_board_info);
+
+/* ------------------------------------------------------------------------- */
+
+static inline int
+spmi_cmd(struct spmi_controller *ctrl, u8 opcode, u8 sid)
+{
+	BUG_ON(!ctrl || !ctrl->cmd);
+	return ctrl->cmd(ctrl, opcode, sid);
+}
+
+static inline int spmi_read_cmd(struct spmi_controller *ctrl,
+				u8 opcode, u8 sid, u16 addr, u8 bc, u8 *buf)
+{
+	BUG_ON(!ctrl || !ctrl->read_cmd);
+	return ctrl->read_cmd(ctrl, opcode, sid, addr, bc, buf);
+}
+
+static inline int spmi_write_cmd(struct spmi_controller *ctrl,
+				u8 opcode, u8 sid, u16 addr, u8 bc, u8 *buf)
+{
+	BUG_ON(!ctrl || !ctrl->write_cmd);
+	return ctrl->write_cmd(ctrl, opcode, sid, addr, bc, buf);
+}
+
+/*
+ * register read/write: 5-bit address, 1 byte of data
+ * extended register read/write: 8-bit address, up to 16 bytes of data
+ * extended register read/write long: 16-bit address, up to 8 bytes of data
+ */
+
+/**
+ * spmi_register_read() - register read
+ * @dev: SPMI device.
+ * @sid: slave identifier.
+ * @ad: slave register address (5-bit address).
+ * @buf: buffer to be populated with data from the Slave.
+ *
+ * Reads 1 byte of data from a Slave device register.
+ */
+int spmi_register_read(struct spmi_controller *ctrl, u8 sid, u8 addr, u8 *buf)
+{
+	/* 4-bit Slave Identifier, 5-bit register address */
+	if (sid > SPMI_MAX_SLAVE_ID || addr > 0x1F)
+		return -EINVAL;
+
+	return spmi_read_cmd(ctrl, SPMI_CMD_READ, sid, addr, 0, buf);
+}
+EXPORT_SYMBOL_GPL(spmi_register_read);
+
+/**
+ * spmi_ext_register_read() - extended register read
+ * @dev: SPMI device.
+ * @sid: slave identifier.
+ * @ad: slave register address (8-bit address).
+ * @len: the request number of bytes to read (up to 16 bytes).
+ * @buf: buffer to be populated with data from the Slave.
+ *
+ * Reads up to 16 bytes of data from the extended register space on a
+ * Slave device.
+ */
+int spmi_ext_register_read(struct spmi_controller *ctrl,
+				u8 sid, u8 addr, u8 *buf, int len)
+{
+	/* 4-bit Slave Identifier, 8-bit register address, up to 16 bytes */
+	if (sid > SPMI_MAX_SLAVE_ID || len <= 0 || len > 16)
+		return -EINVAL;
+
+	return spmi_read_cmd(ctrl, SPMI_CMD_EXT_READ, sid, addr, len - 1, buf);
+}
+EXPORT_SYMBOL_GPL(spmi_ext_register_read);
+
+/**
+ * spmi_ext_register_readl() - extended register read long
+ * @dev: SPMI device.
+ * @sid: slave identifier.
+ * @ad: slave register address (16-bit address).
+ * @len: the request number of bytes to read (up to 8 bytes).
+ * @buf: buffer to be populated with data from the Slave.
+ *
+ * Reads up to 8 bytes of data from the extended register space on a
+ * Slave device using 16-bit address.
+ */
+int spmi_ext_register_readl(struct spmi_controller *ctrl,
+				u8 sid, u16 addr, u8 *buf, int len)
+{
+	/* 4-bit Slave Identifier, 16-bit register address, up to 8 bytes */
+	if (sid > SPMI_MAX_SLAVE_ID || len <= 0 || len > 8)
+		return -EINVAL;
+
+	return spmi_read_cmd(ctrl, SPMI_CMD_EXT_READL, sid, addr, len - 1, buf);
+}
+EXPORT_SYMBOL_GPL(spmi_ext_register_readl);
+
+/**
+ * spmi_register_write() - register write
+ * @dev: SPMI device.
+ * @sid: slave identifier.
+ * @ad: slave register address (5-bit address).
+ * @buf: buffer containing the data to be transferred to the Slave.
+ *
+ * Writes 1 byte of data to a Slave device register.
+ */
+int spmi_register_write(struct spmi_controller *ctrl, u8 sid, u8 addr, u8 *buf)
+{
+	u8 op = SPMI_CMD_WRITE;
+
+	/* 4-bit Slave Identifier, 5-bit register address */
+	if (sid > SPMI_MAX_SLAVE_ID || addr > 0x1F)
+		return -EINVAL;
+
+	return spmi_write_cmd(ctrl, op, sid, addr, 0, buf);
+}
+EXPORT_SYMBOL_GPL(spmi_register_write);
+
+/**
+ * spmi_register_zero_write() - register zero write
+ * @dev: SPMI device.
+ * @sid: slave identifier.
+ * @data: the data to be written to register 0 (7-bits).
+ *
+ * Writes data to register 0 of the Slave device.
+ */
+int spmi_register_zero_write(struct spmi_controller *ctrl, u8 sid, u8 data)
+{
+	u8 op = SPMI_CMD_ZERO_WRITE;
+
+	/* 4-bit Slave Identifier, 5-bit register address */
+	if (sid > SPMI_MAX_SLAVE_ID)
+		return -EINVAL;
+
+	return spmi_write_cmd(ctrl, op, sid, 0, 0, &data);
+}
+EXPORT_SYMBOL_GPL(spmi_register_zero_write);
+
+/**
+ * spmi_ext_register_write() - extended register write
+ * @dev: SPMI device.
+ * @sid: slave identifier.
+ * @ad: slave register address (8-bit address).
+ * @buf: buffer containing the data to be transferred to the Slave.
+ * @len: the request number of bytes to read (up to 16 bytes).
+ *
+ * Writes up to 16 bytes of data to the extended register space of a
+ * Slave device.
+ */
+int spmi_ext_register_write(struct spmi_controller *ctrl,
+				u8 sid, u8 addr, u8 *buf, int len)
+{
+	u8 op = SPMI_CMD_EXT_WRITE;
+
+	/* 4-bit Slave Identifier, 8-bit register address, up to 16 bytes */
+	if (sid > SPMI_MAX_SLAVE_ID || len <= 0 || len > 16)
+		return -EINVAL;
+
+	return spmi_write_cmd(ctrl, op, sid, addr, len - 1, buf);
+}
+EXPORT_SYMBOL_GPL(spmi_ext_register_write);
+
+/**
+ * spmi_ext_register_writel() - extended register write long
+ * @dev: SPMI device.
+ * @sid: slave identifier.
+ * @ad: slave register address (16-bit address).
+ * @buf: buffer containing the data to be transferred to the Slave.
+ * @len: the request number of bytes to read (up to 8 bytes).
+ *
+ * Writes up to 8 bytes of data to the extended register space of a
+ * Slave device using 16-bit address.
+ */
+int spmi_ext_register_writel(struct spmi_controller *ctrl,
+				u8 sid, u16 addr, u8 *buf, int len)
+{
+	u8 op = SPMI_CMD_EXT_WRITEL;
+
+	/* 4-bit Slave Identifier, 16-bit register address, up to 8 bytes */
+	if (sid > SPMI_MAX_SLAVE_ID || len <= 0 || len > 8)
+		return -EINVAL;
+
+	return spmi_write_cmd(ctrl, op, sid, addr, len - 1, buf);
+}
+EXPORT_SYMBOL_GPL(spmi_ext_register_writel);
+
+/**
+ * spmi_command_reset() - sends RESET command to the specified slave
+ * @dev: SPMI device.
+ * @sid: slave identifier.
+ *
+ * The Reset command initializes the Slave and forces all registers to
+ * their reset values. The Slave shall enter the STARTUP state after
+ * receiving a Reset command.
+ *
+ * Returns
+ * -EINVAL for invalid Slave Identifier.
+ * -EPERM if the SPMI transaction is denied due to permission issues.
+ * -EIO if the SPMI transaction fails (parity errors, etc).
+ * -ETIMEDOUT if the SPMI transaction times out.
+ */
+int spmi_command_reset(struct spmi_controller *ctrl, u8 sid)
+{
+	if (sid > SPMI_MAX_SLAVE_ID)
+		return -EINVAL;
+	return spmi_cmd(ctrl, SPMI_CMD_RESET, sid);
+}
+EXPORT_SYMBOL_GPL(spmi_command_reset);
+
+/**
+ * spmi_command_sleep() - sends SLEEP command to the specified slave
+ * @dev: SPMI device.
+ * @sid: slave identifier.
+ *
+ * The Sleep command causes the Slave to enter the user defined SLEEP state.
+ *
+ * Returns
+ * -EINVAL for invalid Slave Identifier.
+ * -EPERM if the SPMI transaction is denied due to permission issues.
+ * -EIO if the SPMI transaction fails (parity errors, etc).
+ * -ETIMEDOUT if the SPMI transaction times out.
+ */
+int spmi_command_sleep(struct spmi_controller *ctrl, u8 sid)
+{
+	if (sid > SPMI_MAX_SLAVE_ID)
+		return -EINVAL;
+	return spmi_cmd(ctrl, SPMI_CMD_SLEEP, sid);
+}
+EXPORT_SYMBOL_GPL(spmi_command_sleep);
+
+/**
+ * spmi_command_wakeup() - sends WAKEUP command to the specified slave
+ * @dev: SPMI device.
+ * @sid: slave identifier.
+ *
+ * The Wakeup command causes the Slave to move from the SLEEP state to
+ * the ACTIVE state.
+ *
+ * Returns
+ * -EINVAL for invalid Slave Identifier.
+ * -EPERM if the SPMI transaction is denied due to permission issues.
+ * -EIO if the SPMI transaction fails (parity errors, etc).
+ * -ETIMEDOUT if the SPMI transaction times out.
+ */
+int spmi_command_wakeup(struct spmi_controller *ctrl, u8 sid)
+{
+	if (sid > SPMI_MAX_SLAVE_ID)
+		return -EINVAL;
+	return spmi_cmd(ctrl, SPMI_CMD_WAKEUP, sid);
+}
+EXPORT_SYMBOL_GPL(spmi_command_wakeup);
+
+/**
+ * spmi_command_shutdown() - sends SHUTDOWN command to the specified slave
+ * @dev: SPMI device.
+ * @sid: slave identifier.
+ *
+ * The Shutdown command causes the Slave to enter the SHUTDOWN state.
+ *
+ * Returns
+ * -EINVAL for invalid Slave Identifier.
+ * -EPERM if the SPMI transaction is denied due to permission issues.
+ * -EIO if the SPMI transaction fails (parity errors, etc).
+ * -ETIMEDOUT if the SPMI transaction times out.
+ */
+int spmi_command_shutdown(struct spmi_controller *ctrl, u8 sid)
+{
+	if (sid > SPMI_MAX_SLAVE_ID)
+		return -EINVAL;
+	return spmi_cmd(ctrl, SPMI_CMD_SHUTDOWN, sid);
+}
+EXPORT_SYMBOL_GPL(spmi_command_shutdown);
+
+/* ------------------------------------------------------------------------- */
+
+static const struct spmi_device_id *spmi_match(const struct spmi_device_id *id,
+		const struct spmi_device *spmi_dev)
+{
+	while (id->name[0]) {
+		if (strncmp(spmi_dev->name, id->name, SPMI_NAME_SIZE) == 0)
+			return id;
+		id++;
+	}
+	return NULL;
+}
+
+static int spmi_device_match(struct device *dev, struct device_driver *drv)
+{
+	struct spmi_device *spmi_dev;
+	struct spmi_driver *sdrv = to_spmi_driver(drv);
+
+	if (dev->type == &spmi_dev_type)
+		spmi_dev = to_spmi_device(dev);
+	else
+		return 0;
+
+	/* Attempt an OF style match */
+	if (of_driver_match_device(dev, drv))
+		return 1;
+
+	if (sdrv->id_table)
+		return spmi_match(sdrv->id_table, spmi_dev) != NULL;
+
+	if (drv->name)
+		return strncmp(spmi_dev->name, drv->name, SPMI_NAME_SIZE) == 0;
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int spmi_legacy_suspend(struct device *dev, pm_message_t mesg)
+{
+	struct spmi_device *spmi_dev = NULL;
+	struct spmi_driver *driver;
+	if (dev->type == &spmi_dev_type)
+		spmi_dev = to_spmi_device(dev);
+
+	if (!spmi_dev || !dev->driver)
+		return 0;
+
+	driver = to_spmi_driver(dev->driver);
+	if (!driver->suspend)
+		return 0;
+
+	return driver->suspend(spmi_dev, mesg);
+}
+
+static int spmi_legacy_resume(struct device *dev)
+{
+	struct spmi_device *spmi_dev = NULL;
+	struct spmi_driver *driver;
+	if (dev->type == &spmi_dev_type)
+		spmi_dev = to_spmi_device(dev);
+
+	if (!spmi_dev || !dev->driver)
+		return 0;
+
+	driver = to_spmi_driver(dev->driver);
+	if (!driver->resume)
+		return 0;
+
+	return driver->resume(spmi_dev);
+}
+
+static int spmi_pm_suspend(struct device *dev)
+{
+	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+	if (pm)
+		return pm_generic_suspend(dev);
+	else
+		return spmi_legacy_suspend(dev, PMSG_SUSPEND);
+}
+
+static int spmi_pm_resume(struct device *dev)
+{
+	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+	if (pm)
+		return pm_generic_resume(dev);
+	else
+		return spmi_legacy_resume(dev);
+}
+
+#else
+#define spmi_pm_suspend		NULL
+#define spmi_pm_resume		NULL
+#endif
+
+static const struct dev_pm_ops spmi_pm_ops = {
+	.suspend = spmi_pm_suspend,
+	.resume = spmi_pm_resume,
+	SET_RUNTIME_PM_OPS(
+		pm_generic_suspend,
+		pm_generic_resume,
+		pm_generic_runtime_idle
+		)
+};
+struct bus_type spmi_bus_type = {
+	.name		= "spmi",
+	.match		= spmi_device_match,
+	.pm		= &spmi_pm_ops,
+};
+EXPORT_SYMBOL_GPL(spmi_bus_type);
+
+struct device spmi_dev = {
+	.init_name = "spmi",
+};
+
+static int spmi_drv_probe(struct device *dev)
+{
+	const struct spmi_driver *sdrv = to_spmi_driver(dev->driver);
+
+	return sdrv->probe(to_spmi_device(dev));
+}
+
+static int spmi_drv_remove(struct device *dev)
+{
+	const struct spmi_driver *sdrv = to_spmi_driver(dev->driver);
+
+	return sdrv->remove(to_spmi_device(dev));
+}
+
+static void spmi_drv_shutdown(struct device *dev)
+{
+	const struct spmi_driver *sdrv = to_spmi_driver(dev->driver);
+
+	sdrv->shutdown(to_spmi_device(dev));
+}
+
+/**
+ * spmi_driver_register: Client driver registration with SPMI framework.
+ * @drv: client driver to be associated with client-device.
+ *
+ * This API will register the client driver with the SPMI framework.
+ * It is called from the driver's module-init function.
+ */
+int spmi_driver_register(struct spmi_driver *drv)
+{
+	drv->driver.bus = &spmi_bus_type;
+
+	if (drv->probe)
+		drv->driver.probe = spmi_drv_probe;
+
+	if (drv->remove)
+		drv->driver.remove = spmi_drv_remove;
+
+	if (drv->shutdown)
+		drv->driver.shutdown = spmi_drv_shutdown;
+
+	return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(spmi_driver_register);
+
+static int spmi_register_controller(struct spmi_controller *ctrl)
+{
+	int ret = 0;
+
+	/* Can't register until after driver model init */
+	if (WARN_ON(!spmi_bus_type.p)) {
+		ret = -EAGAIN;
+		goto exit;
+	}
+
+	dev_set_name(&ctrl->dev, "spmi-%d", ctrl->nr);
+	ctrl->dev.bus = &spmi_bus_type;
+	ctrl->dev.type = &spmi_ctrl_type;
+	ret = device_register(&ctrl->dev);
+	if (ret)
+		goto exit;
+
+	dev_dbg(&ctrl->dev, "Bus spmi-%d registered: dev:%x\n",
+					ctrl->nr, (u32)&ctrl->dev);
+
+	mutex_lock(&board_lock);
+	list_add_tail(&ctrl->list, &spmi_ctrl_list);
+	mutex_unlock(&board_lock);
+
+	return 0;
+
+exit:
+	mutex_lock(&board_lock);
+	idr_remove(&ctrl_idr, ctrl->nr);
+	mutex_unlock(&board_lock);
+	return ret;
+}
+
+static void __exit spmi_exit(void)
+{
+	device_unregister(&spmi_dev);
+	bus_unregister(&spmi_bus_type);
+}
+
+static int __init spmi_init(void)
+{
+	int retval;
+
+	retval = bus_register(&spmi_bus_type);
+	if (!retval)
+		retval = device_register(&spmi_dev);
+
+	if (retval)
+		bus_unregister(&spmi_bus_type);
+
+	return retval;
+}
+postcore_initcall(spmi_init);
+module_exit(spmi_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("1.0");
+MODULE_DESCRIPTION("SPMI module");
+MODULE_ALIAS("platform:spmi");
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 97d412d..bcec934 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -84,6 +84,8 @@
 
 source "drivers/staging/zcache/Kconfig"
 
+source "drivers/staging/qcache/Kconfig"
+
 source "drivers/staging/zsmalloc/Kconfig"
 
 source "drivers/staging/wlags49_h2/Kconfig"
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index ffe7d44..c31f2ec 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -34,6 +34,7 @@
 obj-$(CONFIG_IIO)		+= iio/
 obj-$(CONFIG_ZRAM)		+= zram/
 obj-$(CONFIG_ZCACHE)		+= zcache/
+obj-$(CONFIG_QCACHE)		+= qcache/
 obj-$(CONFIG_ZSMALLOC)		+= zsmalloc/
 obj-$(CONFIG_WLAGS49_H2)	+= wlags49_h2/
 obj-$(CONFIG_WLAGS49_H25)	+= wlags49_h25/
diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c
index bda20bf..8390f5d 100644
--- a/drivers/staging/android/ashmem.c
+++ b/drivers/staging/android/ashmem.c
@@ -28,7 +28,8 @@
 #include <linux/bitops.h>
 #include <linux/mutex.h>
 #include <linux/shmem_fs.h>
-#include "ashmem.h"
+#include <linux/ashmem.h>
+#include <asm/cacheflush.h>
 
 #define ASHMEM_NAME_PREFIX "dev/ashmem/"
 #define ASHMEM_NAME_PREFIX_LEN (sizeof(ASHMEM_NAME_PREFIX) - 1)
@@ -45,6 +46,8 @@
 	struct list_head unpinned_list;	 /* list of all ashmem areas */
 	struct file *file;		 /* the shmem-based backing file */
 	size_t size;			 /* size of the mapping, in bytes */
+	unsigned long vm_start;		 /* Start address of vm_area
+					  * which maps this ashmem */
 	unsigned long prot_mask;	 /* allowed prot bits, as vm_flags */
 };
 
@@ -322,6 +325,7 @@
 		vma->vm_file = asma->file;
 	}
 	vma->vm_flags |= VM_CAN_NONLINEAR;
+	asma->vm_start = vma->vm_start;
 
 out:
 	mutex_unlock(&ashmem_mutex);
@@ -622,6 +626,90 @@
 	return ret;
 }
 
+#ifdef CONFIG_OUTER_CACHE
+static unsigned int virtaddr_to_physaddr(unsigned int virtaddr)
+{
+	unsigned int physaddr = 0;
+	pgd_t *pgd_ptr = NULL;
+	pmd_t *pmd_ptr = NULL;
+	pte_t *pte_ptr = NULL, pte;
+
+	spin_lock(&current->mm->page_table_lock);
+	pgd_ptr = pgd_offset(current->mm, virtaddr);
+	if (pgd_none(*pgd) || pgd_bad(*pgd)) {
+		pr_err("Failed to convert virtaddr %x to pgd_ptr\n",
+			virtaddr);
+		goto done;
+	}
+
+	pmd_ptr = pmd_offset(pgd_ptr, virtaddr);
+	if (pmd_none(*pmd_ptr) || pmd_bad(*pmd_ptr)) {
+		pr_err("Failed to convert pgd_ptr %p to pmd_ptr\n",
+			(void *)pgd_ptr);
+		goto done;
+	}
+
+	pte_ptr = pte_offset_map(pmd_ptr, virtaddr);
+	if (!pte_ptr) {
+		pr_err("Failed to convert pmd_ptr %p to pte_ptr\n",
+			(void *)pmd_ptr);
+		goto done;
+	}
+	pte = *pte_ptr;
+	physaddr = pte_pfn(pte);
+	pte_unmap(pte_ptr);
+done:
+	spin_unlock(&current->mm->page_table_lock);
+	physaddr <<= PAGE_SHIFT;
+	return physaddr;
+}
+#endif
+
+static int ashmem_cache_op(struct ashmem_area *asma,
+	void (*cache_func)(unsigned long vstart, unsigned long length,
+				unsigned long pstart))
+{
+	int ret = 0;
+	struct vm_area_struct *vma;
+#ifdef CONFIG_OUTER_CACHE
+	unsigned long vaddr;
+#endif
+	if (!asma->vm_start)
+		return -EINVAL;
+
+	down_read(&current->mm->mmap_sem);
+	vma = find_vma(current->mm, asma->vm_start);
+	if (!vma) {
+		ret = -EINVAL;
+		goto done;
+	}
+	if (vma->vm_file != asma->file) {
+		ret = -EINVAL;
+		goto done;
+	}
+	if ((asma->vm_start + asma->size) > (vma->vm_start + vma->vm_end)) {
+		ret = -EINVAL;
+		goto done;
+	}
+#ifndef CONFIG_OUTER_CACHE
+	cache_func(asma->vm_start, asma->size, 0);
+#else
+	for (vaddr = asma->vm_start; vaddr < asma->vm_start + asma->size;
+		vaddr += PAGE_SIZE) {
+		unsigned long physaddr;
+		physaddr = virtaddr_to_physaddr(vaddr);
+		if (!physaddr)
+			return -EINVAL;
+		cache_func(vaddr, PAGE_SIZE, physaddr);
+	}
+#endif
+done:
+	up_read(&current->mm->mmap_sem);
+	if (ret)
+		asma->vm_start = 0;
+	return ret;
+}
+
 static long ashmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
 	struct ashmem_area *asma = file->private_data;
@@ -667,11 +755,73 @@
 			ashmem_shrink(&ashmem_shrinker, &sc);
 		}
 		break;
+	case ASHMEM_CACHE_FLUSH_RANGE:
+		ret = ashmem_cache_op(asma, &clean_and_invalidate_caches);
+		break;
+	case ASHMEM_CACHE_CLEAN_RANGE:
+		ret = ashmem_cache_op(asma, &clean_caches);
+		break;
+	case ASHMEM_CACHE_INV_RANGE:
+		ret = ashmem_cache_op(asma, &invalidate_caches);
+		break;
 	}
 
 	return ret;
 }
 
+static int is_ashmem_file(struct file *file)
+{
+	char fname[256], *name;
+	name = dentry_path(file->f_dentry, fname, 256);
+	return strcmp(name, "/ashmem") ? 0 : 1;
+}
+
+int get_ashmem_file(int fd, struct file **filp, struct file **vm_file,
+			unsigned long *len)
+{
+	int ret = -1;
+	struct file *file = fget(fd);
+	*filp = NULL;
+	*vm_file = NULL;
+	if (unlikely(file == NULL)) {
+		pr_err("ashmem: %s: requested data from file "
+			"descriptor that doesn't exist.\n", __func__);
+	} else {
+		char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1];
+		pr_debug("filp %p rdev %d pid %u(%s) file %p(%ld)"
+			" dev id: %d\n", filp,
+			file->f_dentry->d_inode->i_rdev,
+			current->pid, get_task_comm(currtask_name, current),
+			file, file_count(file),
+			MINOR(file->f_dentry->d_inode->i_rdev));
+		if (is_ashmem_file(file)) {
+			struct ashmem_area *asma = file->private_data;
+			*filp = file;
+			*vm_file = asma->file;
+			*len = asma->size;
+			ret = 0;
+		} else {
+			pr_err("file descriptor is not an ashmem "
+				"region fd: %d\n", fd);
+			fput(file);
+		}
+	}
+	return ret;
+}
+EXPORT_SYMBOL(get_ashmem_file);
+
+void put_ashmem_file(struct file *file)
+{
+	char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1];
+	pr_debug("rdev %d pid %u(%s) file %p(%ld)" " dev id: %d\n",
+		file->f_dentry->d_inode->i_rdev, current->pid,
+		get_task_comm(currtask_name, current), file,
+		file_count(file), MINOR(file->f_dentry->d_inode->i_rdev));
+	if (file && is_ashmem_file(file))
+		fput(file);
+}
+EXPORT_SYMBOL(put_ashmem_file);
+
 static const struct file_operations ashmem_fops = {
 	.owner = THIS_MODULE,
 	.open = ashmem_open,
diff --git a/drivers/staging/android/ashmem.h b/drivers/staging/android/ashmem.h
deleted file mode 100644
index 1976b10..0000000
--- a/drivers/staging/android/ashmem.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * include/linux/ashmem.h
- *
- * Copyright 2008 Google Inc.
- * Author: Robert Love
- *
- * This file is dual licensed.  It may be redistributed and/or modified
- * under the terms of the Apache 2.0 License OR version 2 of the GNU
- * General Public License.
- */
-
-#ifndef _LINUX_ASHMEM_H
-#define _LINUX_ASHMEM_H
-
-#include <linux/limits.h>
-#include <linux/ioctl.h>
-
-#define ASHMEM_NAME_LEN		256
-
-#define ASHMEM_NAME_DEF		"dev/ashmem"
-
-/* Return values from ASHMEM_PIN: Was the mapping purged while unpinned? */
-#define ASHMEM_NOT_PURGED	0
-#define ASHMEM_WAS_PURGED	1
-
-/* Return values from ASHMEM_GET_PIN_STATUS: Is the mapping pinned? */
-#define ASHMEM_IS_UNPINNED	0
-#define ASHMEM_IS_PINNED	1
-
-struct ashmem_pin {
-	__u32 offset;	/* offset into region, in bytes, page-aligned */
-	__u32 len;	/* length forward from offset, in bytes, page-aligned */
-};
-
-#define __ASHMEMIOC		0x77
-
-#define ASHMEM_SET_NAME		_IOW(__ASHMEMIOC, 1, char[ASHMEM_NAME_LEN])
-#define ASHMEM_GET_NAME		_IOR(__ASHMEMIOC, 2, char[ASHMEM_NAME_LEN])
-#define ASHMEM_SET_SIZE		_IOW(__ASHMEMIOC, 3, size_t)
-#define ASHMEM_GET_SIZE		_IO(__ASHMEMIOC, 4)
-#define ASHMEM_SET_PROT_MASK	_IOW(__ASHMEMIOC, 5, unsigned long)
-#define ASHMEM_GET_PROT_MASK	_IO(__ASHMEMIOC, 6)
-#define ASHMEM_PIN		_IOW(__ASHMEMIOC, 7, struct ashmem_pin)
-#define ASHMEM_UNPIN		_IOW(__ASHMEMIOC, 8, struct ashmem_pin)
-#define ASHMEM_GET_PIN_STATUS	_IO(__ASHMEMIOC, 9)
-#define ASHMEM_PURGE_ALL_CACHES	_IO(__ASHMEMIOC, 10)
-
-#endif	/* _LINUX_ASHMEM_H */
diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c
index c283212..6cd30f1 100644
--- a/drivers/staging/android/binder.c
+++ b/drivers/staging/android/binder.c
@@ -98,9 +98,9 @@
 	BINDER_DEBUG_BUFFER_ALLOC           = 1U << 13,
 	BINDER_DEBUG_PRIORITY_CAP           = 1U << 14,
 	BINDER_DEBUG_BUFFER_ALLOC_ASYNC     = 1U << 15,
+	BINDER_DEBUG_TOP_ERRORS		    = 1U << 16,
 };
-static uint32_t binder_debug_mask = BINDER_DEBUG_USER_ERROR |
-	BINDER_DEBUG_FAILED_TRANSACTION | BINDER_DEBUG_DEAD_TRANSACTION;
+static uint32_t binder_debug_mask;
 module_param_named(debug_mask, binder_debug_mask, uint, S_IWUSR | S_IRUGO);
 
 static bool binder_debug_no_lock;
@@ -644,8 +644,9 @@
 		goto free_range;
 
 	if (vma == NULL) {
-		printk(KERN_ERR "binder: %d: binder_alloc_buf failed to "
-		       "map pages in userspace, no vma\n", proc->pid);
+		binder_debug(BINDER_DEBUG_TOP_ERRORS,
+			     "binder: %d: binder_alloc_buf failed to "
+			     "map pages in userspace, no vma\n", proc->pid);
 		goto err_no_vma;
 	}
 
@@ -657,8 +658,9 @@
 		BUG_ON(*page);
 		*page = alloc_page(GFP_KERNEL | __GFP_ZERO);
 		if (*page == NULL) {
-			printk(KERN_ERR "binder: %d: binder_alloc_buf failed "
-			       "for page at %p\n", proc->pid, page_addr);
+			binder_debug(BINDER_DEBUG_TOP_ERRORS,
+				     "binder: %d: binder_alloc_buf failed "
+				     "for page at %p\n", proc->pid, page_addr);
 			goto err_alloc_page_failed;
 		}
 		tmp_area.addr = page_addr;
@@ -666,18 +668,20 @@
 		page_array_ptr = page;
 		ret = map_vm_area(&tmp_area, PAGE_KERNEL, &page_array_ptr);
 		if (ret) {
-			printk(KERN_ERR "binder: %d: binder_alloc_buf failed "
-			       "to map page at %p in kernel\n",
-			       proc->pid, page_addr);
+			binder_debug(BINDER_DEBUG_TOP_ERRORS,
+				     "binder: %d: binder_alloc_buf failed "
+				     "to map page at %p in kernel\n",
+				     proc->pid, page_addr);
 			goto err_map_kernel_failed;
 		}
 		user_page_addr =
 			(uintptr_t)page_addr + proc->user_buffer_offset;
 		ret = vm_insert_page(vma, user_page_addr, page[0]);
 		if (ret) {
-			printk(KERN_ERR "binder: %d: binder_alloc_buf failed "
-			       "to map page at %lx in userspace\n",
-			       proc->pid, user_page_addr);
+			binder_debug(BINDER_DEBUG_TOP_ERRORS,
+				     "binder: %d: binder_alloc_buf failed "
+				     "to map page at %lx in userspace\n",
+				     proc->pid, user_page_addr);
 			goto err_vm_insert_page_failed;
 		}
 		/* vm_insert_page does not seem to increment the refcount */
@@ -724,8 +728,9 @@
 	size_t size;
 
 	if (proc->vma == NULL) {
-		printk(KERN_ERR "binder: %d: binder_alloc_buf, no vma\n",
-		       proc->pid);
+		binder_debug(BINDER_DEBUG_TOP_ERRORS,
+			     "binder: %d: binder_alloc_buf, no vma\n",
+			     proc->pid);
 		return NULL;
 	}
 
@@ -762,8 +767,9 @@
 		}
 	}
 	if (best_fit == NULL) {
-		printk(KERN_ERR "binder: %d: binder_alloc_buf size %zd failed, "
-		       "no address space\n", proc->pid, size);
+		binder_debug(BINDER_DEBUG_TOP_ERRORS,
+			     "binder: %d: binder_alloc_buf size %zd failed, "
+			     "no address space\n", proc->pid, size);
 		return NULL;
 	}
 	if (n == NULL) {
@@ -997,8 +1003,9 @@
 			    node->internal_strong_refs == 0 &&
 			    !(node == binder_context_mgr_node &&
 			    node->has_strong_ref)) {
-				printk(KERN_ERR "binder: invalid inc strong "
-					"node for %d\n", node->debug_id);
+				binder_debug(BINDER_DEBUG_TOP_ERRORS,
+					     "binder: invalid inc strong "
+					     "node for %d\n", node->debug_id);
 				return -EINVAL;
 			}
 			node->internal_strong_refs++;
@@ -1013,8 +1020,9 @@
 			node->local_weak_refs++;
 		if (!node->has_weak_ref && list_empty(&node->work.entry)) {
 			if (target_list == NULL) {
-				printk(KERN_ERR "binder: invalid inc weak node "
-					"for %d\n", node->debug_id);
+				binder_debug(BINDER_DEBUG_TOP_ERRORS,
+					     "binder: invalid inc weak node "
+					     "for %d\n", node->debug_id);
 				return -EINVAL;
 			}
 			list_add_tail(&node->work.entry, target_list);
@@ -1276,11 +1284,13 @@
 				target_thread->return_error = error_code;
 				wake_up_interruptible(&target_thread->wait);
 			} else {
-				printk(KERN_ERR "binder: reply failed, target "
-					"thread, %d:%d, has error code %d "
-					"already\n", target_thread->proc->pid,
-					target_thread->pid,
-					target_thread->return_error);
+				binder_debug(BINDER_DEBUG_TOP_ERRORS,
+					     "binder: reply failed, target "
+					     "thread, %d:%d, has error code %d "
+					     "already\n",
+					     target_thread->proc->pid,
+					     target_thread->pid,
+					     target_thread->return_error);
 			}
 			return;
 		} else {
@@ -1331,9 +1341,10 @@
 		if (*offp > buffer->data_size - sizeof(*fp) ||
 		    buffer->data_size < sizeof(*fp) ||
 		    !IS_ALIGNED(*offp, sizeof(void *))) {
-			printk(KERN_ERR "binder: transaction release %d bad"
-					"offset %zd, size %zd\n", debug_id,
-					*offp, buffer->data_size);
+			binder_debug(BINDER_DEBUG_TOP_ERRORS,
+				     "binder: transaction release %d bad"
+				     "offset %zd, size %zd\n", debug_id,
+				     *offp, buffer->data_size);
 			continue;
 		}
 		fp = (struct flat_binder_object *)(buffer->data + *offp);
@@ -1342,8 +1353,10 @@
 		case BINDER_TYPE_WEAK_BINDER: {
 			struct binder_node *node = binder_get_node(proc, fp->binder);
 			if (node == NULL) {
-				printk(KERN_ERR "binder: transaction release %d"
-				       " bad node %p\n", debug_id, fp->binder);
+				binder_debug(BINDER_DEBUG_TOP_ERRORS,
+					     "binder: transaction release %d"
+					     " bad node %p\n", debug_id,
+					     fp->binder);
 				break;
 			}
 			binder_debug(BINDER_DEBUG_TRANSACTION,
@@ -1355,9 +1368,10 @@
 		case BINDER_TYPE_WEAK_HANDLE: {
 			struct binder_ref *ref = binder_get_ref(proc, fp->handle);
 			if (ref == NULL) {
-				printk(KERN_ERR "binder: transaction release %d"
-				       " bad handle %ld\n", debug_id,
-				       fp->handle);
+				binder_debug(BINDER_DEBUG_TOP_ERRORS,
+					     "binder: transaction release %d"
+					     " bad handle %ld\n", debug_id,
+					     fp->handle);
 				break;
 			}
 			binder_debug(BINDER_DEBUG_TRANSACTION,
@@ -1374,8 +1388,9 @@
 			break;
 
 		default:
-			printk(KERN_ERR "binder: transaction release %d bad "
-			       "object type %lx\n", debug_id, fp->type);
+			binder_debug(BINDER_DEBUG_TOP_ERRORS,
+				     "binder: transaction release %d bad "
+				     "object type %lx\n", debug_id, fp->type);
 			break;
 		}
 	}
@@ -1925,10 +1940,12 @@
 			break;
 		}
 		case BC_ATTEMPT_ACQUIRE:
-			printk(KERN_ERR "binder: BC_ATTEMPT_ACQUIRE not supported\n");
+			binder_debug(BINDER_DEBUG_TOP_ERRORS,
+				     "binder: BC_ATTEMPT_ACQUIRE not supported\n");
 			return -EINVAL;
 		case BC_ACQUIRE_RESULT:
-			printk(KERN_ERR "binder: BC_ACQUIRE_RESULT not supported\n");
+		        binder_debug(BINDER_DEBUG_TOP_ERRORS,
+				     "binder: BC_ACQUIRE_RESULT not supported\n");
 			return -EINVAL;
 
 		case BC_FREE_BUFFER: {
@@ -2165,8 +2182,9 @@
 		} break;
 
 		default:
-			printk(KERN_ERR "binder: %d:%d unknown command %d\n",
-			       proc->pid, thread->pid, cmd);
+			binder_debug(BINDER_DEBUG_TOP_ERRORS,
+				     "binder: %d:%d unknown command %d\n",
+				     proc->pid, thread->pid, cmd);
 			return -EINVAL;
 		}
 		*consumed = ptr - buffer;
@@ -2701,16 +2719,18 @@
 		break;
 	case BINDER_SET_CONTEXT_MGR:
 		if (binder_context_mgr_node != NULL) {
-			printk(KERN_ERR "binder: BINDER_SET_CONTEXT_MGR already set\n");
+			binder_debug(BINDER_DEBUG_TOP_ERRORS,
+				     "binder: BINDER_SET_CONTEXT_MGR already set\n");
 			ret = -EBUSY;
 			goto err;
 		}
 		if (binder_context_mgr_uid != -1) {
 			if (binder_context_mgr_uid != current->cred->euid) {
-				printk(KERN_ERR "binder: BINDER_SET_"
-				       "CONTEXT_MGR bad uid %d != %d\n",
-				       current->cred->euid,
-				       binder_context_mgr_uid);
+				binder_debug(BINDER_DEBUG_TOP_ERRORS,
+					     "binder: BINDER_SET_"
+					     "CONTEXT_MGR bad uid %d != %d\n",
+					     current->cred->euid,
+					     binder_context_mgr_uid);
 				ret = -EPERM;
 				goto err;
 			}
@@ -2753,7 +2773,9 @@
 	mutex_unlock(&binder_lock);
 	wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
 	if (ret && ret != -ERESTARTSYS)
-		printk(KERN_INFO "binder: %d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret);
+		binder_debug(BINDER_DEBUG_TOP_ERRORS,
+			     "binder: %d:%d ioctl %x %lx returned %d\n",
+			     proc->pid, current->pid, cmd, arg, ret);
 	return ret;
 }
 
@@ -2829,7 +2851,8 @@
 #ifdef CONFIG_CPU_CACHE_VIPT
 	if (cache_is_vipt_aliasing()) {
 		while (CACHE_COLOUR((vma->vm_start ^ (uint32_t)proc->buffer))) {
-			printk(KERN_INFO "binder_mmap: %d %lx-%lx maps %p bad alignment\n", proc->pid, vma->vm_start, vma->vm_end, proc->buffer);
+			binder_debug(BINDER_DEBUG_TOP_ERRORS,
+				     "binder_mmap: %d %lx-%lx maps %p bad alignment\n", proc->pid, vma->vm_start, vma->vm_end, proc->buffer);
 			vma->vm_start += PAGE_SIZE;
 		}
 	}
@@ -2876,8 +2899,10 @@
 err_already_mapped:
 	mutex_unlock(&binder_mmap_lock);
 err_bad_arg:
-	printk(KERN_ERR "binder_mmap: %d %lx-%lx %s failed %d\n",
-	       proc->pid, vma->vm_start, vma->vm_end, failure_string, ret);
+	binder_debug(BINDER_DEBUG_TOP_ERRORS,
+		     "binder_mmap: %d %lx-%lx %s failed %d\n",
+		     proc->pid, vma->vm_start, vma->vm_end, failure_string,
+		     ret);
 	return ret;
 }
 
@@ -3031,9 +3056,10 @@
 		if (t) {
 			t->buffer = NULL;
 			buffer->transaction = NULL;
-			printk(KERN_ERR "binder: release proc %d, "
-			       "transaction %d, not freed\n",
-			       proc->pid, t->debug_id);
+			binder_debug(BINDER_DEBUG_TOP_ERRORS,
+				     "binder: release proc %d, "
+				     "transaction %d, not freed\n",
+				     proc->pid, t->debug_id);
 			/*BUG();*/
 		}
 		binder_free_buf(proc, buffer);
diff --git a/drivers/staging/ath6kl/os/linux/include/athendpack_linux.h b/drivers/staging/ath6kl/os/linux/include/athendpack_linux.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/drivers/staging/ath6kl/os/linux/include/athendpack_linux.h
diff --git a/drivers/staging/ath6kl/os/linux/include/athstartpack_linux.h b/drivers/staging/ath6kl/os/linux/include/athstartpack_linux.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/drivers/staging/ath6kl/os/linux/include/athstartpack_linux.h
diff --git a/drivers/staging/dream/Kconfig b/drivers/staging/dream/Kconfig
new file mode 100644
index 0000000..0c30b19
--- /dev/null
+++ b/drivers/staging/dream/Kconfig
@@ -0,0 +1,13 @@
+config DREAM
+	tristate "HTC Dream support"
+	depends on MACH_TROUT
+
+if DREAM
+
+source "drivers/staging/dream/camera/Kconfig"
+
+config INPUT_GPIO
+	tristate "GPIO driver support"
+	help
+	  Say Y here if you want to support gpio based keys, wheels etc...
+endif
diff --git a/drivers/staging/dream/Makefile b/drivers/staging/dream/Makefile
new file mode 100644
index 0000000..fbea0ab
--- /dev/null
+++ b/drivers/staging/dream/Makefile
@@ -0,0 +1,5 @@
+EXTRA_CFLAGS=-Idrivers/staging/dream/include
+obj-$(CONFIG_MSM_ADSP)		+= qdsp5/
+obj-$(CONFIG_MSM_CAMERA)	+= camera/
+obj-$(CONFIG_INPUT_GPIO)	+= gpio_axis.o gpio_event.o gpio_input.o gpio_matrix.o gpio_output.o
+
diff --git a/drivers/staging/gobi/Kconfig b/drivers/staging/gobi/Kconfig
new file mode 100644
index 0000000..99d3b8d
--- /dev/null
+++ b/drivers/staging/gobi/Kconfig
@@ -0,0 +1,5 @@
+config GOBI_USBNET
+        tristate "Qualcomm GOBI2k and QCQMI support"
+        help
+        This module adds network device support for GOBI2k 3G radios.
+~
diff --git a/drivers/staging/gobi/QCUSBNet2k/Makefile b/drivers/staging/gobi/QCUSBNet2k/Makefile
new file mode 100644
index 0000000..66c1590
--- /dev/null
+++ b/drivers/staging/gobi/QCUSBNet2k/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_GOBI_USBNET) += QCUSBNet2k.o
+QCUSBNet2k-objs += QCUSBNet.o QMIDevice.o QMI.o
diff --git a/drivers/staging/gobi/QCUSBNet2k/QCUSBNet.c b/drivers/staging/gobi/QCUSBNet2k/QCUSBNet.c
new file mode 100644
index 0000000..e7f72e7
--- /dev/null
+++ b/drivers/staging/gobi/QCUSBNet2k/QCUSBNet.c
@@ -0,0 +1,1227 @@
+/*===========================================================================
+FILE:
+   QCUSBNet.c
+
+DESCRIPTION:
+   Qualcomm USB Network device for Gobi 2000 
+   
+FUNCTIONS:
+   QCSuspend
+   QCResume
+   QCNetDriverBind
+   QCNetDriverUnbind
+   QCUSBNetURBCallback
+   QCUSBNetTXTimeout
+   QCUSBNetAutoPMThread
+   QCUSBNetStartXmit
+   QCUSBNetOpen
+   QCUSBNetStop
+   QCUSBNetProbe
+   QCUSBNetModInit
+   QCUSBNetModExit
+
+Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 2 and
+only version 2 as published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+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 Street, Fifth Floor, Boston, MA
+02110-1301, USA.
+
+===========================================================================*/
+
+//---------------------------------------------------------------------------
+// Include Files
+//---------------------------------------------------------------------------
+
+#include "Structs.h"
+#include "QMIDevice.h"
+#include "QMI.h"
+
+//-----------------------------------------------------------------------------
+// Definitions
+//-----------------------------------------------------------------------------
+
+// Version Information
+#define DRIVER_VERSION "1.0.110"
+#define DRIVER_DESC "QCUSBNet2k"
+
+// Debug flag
+int debug;
+
+// Class should be created during module init, so needs to be global
+static struct class * gpClass;
+
+/*===========================================================================
+METHOD:
+   QCSuspend (Public Method)
+
+DESCRIPTION:
+   Stops QMI traffic while device is suspended
+
+PARAMETERS
+   pIntf          [ I ] - Pointer to interface
+   powerEvent     [ I ] - Power management event
+
+RETURN VALUE:
+   int - 0 for success
+         negative errno for failure
+===========================================================================*/
+int QCSuspend( 
+   struct usb_interface *     pIntf,
+   pm_message_t               powerEvent )
+{
+   struct usbnet * pDev;
+   sQCUSBNet * pQCDev;
+   
+   if (pIntf == 0)
+   {
+      return -ENOMEM;
+   }
+   
+#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,23 ))
+   pDev = usb_get_intfdata( pIntf );
+#else
+   pDev = (struct usbnet *)pIntf->dev.platform_data;
+#endif
+
+   if (pDev == NULL || pDev->net == NULL)
+   {
+      DBG( "failed to get netdevice\n" );
+      return -ENXIO;
+   }
+   
+   pQCDev = (sQCUSBNet *)pDev->data[0];
+   if (pQCDev == NULL)
+   {
+      DBG( "failed to get QMIDevice\n" );
+      return -ENXIO;
+   }
+
+   // Is this autosuspend or system suspend?
+   //    do we allow remote wakeup?
+#if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,33 ))
+   if (pDev->udev->auto_pm == 0)
+#else
+   if ((powerEvent.event & PM_EVENT_AUTO) == 0)
+#endif
+   {
+      DBG( "device suspended to power level %d\n", 
+           powerEvent.event );
+      QSetDownReason( pQCDev, DRIVER_SUSPENDED );
+   }
+   else
+   {
+      DBG( "device autosuspend\n" );
+   }    
+
+   if (powerEvent.event & PM_EVENT_SUSPEND)
+   {
+      // Stop QMI read callbacks
+      KillRead( pQCDev );
+      pDev->udev->reset_resume = 0;
+      
+      // Store power state to avoid duplicate resumes
+      pIntf->dev.power.power_state.event = powerEvent.event;
+   }
+   else
+   {
+      // Other power modes cause QMI connection to be lost
+      pDev->udev->reset_resume = 1;
+   }
+   
+   // Run usbnet's suspend function
+   return usbnet_suspend( pIntf, powerEvent );
+}
+   
+/*===========================================================================
+METHOD:
+   QCResume (Public Method)
+
+DESCRIPTION:
+   Resume QMI traffic or recreate QMI device
+
+PARAMETERS
+   pIntf          [ I ] - Pointer to interface
+
+RETURN VALUE:
+   int - 0 for success
+         negative errno for failure
+===========================================================================*/
+int QCResume( struct usb_interface * pIntf )
+{
+   struct usbnet * pDev;
+   sQCUSBNet * pQCDev;
+   int nRet;
+   int oldPowerState;
+   
+   if (pIntf == 0)
+   {
+      return -ENOMEM;
+   }
+   
+#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,23 ))
+   pDev = usb_get_intfdata( pIntf );
+#else
+   pDev = (struct usbnet *)pIntf->dev.platform_data;
+#endif
+
+   if (pDev == NULL || pDev->net == NULL)
+   {
+      DBG( "failed to get netdevice\n" );
+      return -ENXIO;
+   }
+   
+   pQCDev = (sQCUSBNet *)pDev->data[0];
+   if (pQCDev == NULL)
+   {
+      DBG( "failed to get QMIDevice\n" );
+      return -ENXIO;
+   }
+
+   oldPowerState = pIntf->dev.power.power_state.event;
+   pIntf->dev.power.power_state.event = PM_EVENT_ON;
+   DBG( "resuming from power mode %d\n", oldPowerState );
+
+   if (oldPowerState & PM_EVENT_SUSPEND)
+   {
+      // It doesn't matter if this is autoresume or system resume
+      QClearDownReason( pQCDev, DRIVER_SUSPENDED );
+   
+      nRet = usbnet_resume( pIntf );
+      if (nRet != 0)
+      {
+         DBG( "usbnet_resume error %d\n", nRet );
+         return nRet;
+      }
+
+      // Restart QMI read callbacks
+      nRet = StartRead( pQCDev );
+      if (nRet != 0)
+      {
+         DBG( "StartRead error %d\n", nRet );
+         return nRet;
+      }
+
+      // Kick Auto PM thread to process any queued URBs
+      up( &pQCDev->mAutoPM.mThreadDoWork );
+   }
+   else
+   {
+      DBG( "nothing to resume\n" );
+      return 0;
+   }
+   
+   return nRet;
+}
+
+/*===========================================================================
+METHOD:
+   QCNetDriverBind (Public Method)
+
+DESCRIPTION:
+   Setup in and out pipes
+
+PARAMETERS
+   pDev           [ I ] - Pointer to usbnet device
+   pIntf          [ I ] - Pointer to interface
+
+RETURN VALUE:
+   int - 0 for success
+         Negative errno for error
+===========================================================================*/
+static int QCNetDriverBind( 
+   struct usbnet *         pDev, 
+   struct usb_interface *  pIntf )
+{
+   int numEndpoints;
+   int endpointIndex;
+   struct usb_host_endpoint * pEndpoint = NULL;
+   struct usb_host_endpoint * pIn = NULL;
+   struct usb_host_endpoint * pOut = NULL;
+   
+   // Verify one altsetting
+   if (pIntf->num_altsetting != 1)
+   {
+      DBG( "invalid num_altsetting %u\n", pIntf->num_altsetting );
+      return -EINVAL;
+   }
+
+   // Verify correct interface (0)
+   if (pIntf->cur_altsetting->desc.bInterfaceNumber != 0)
+   {
+      DBG( "invalid interface %d\n", 
+           pIntf->cur_altsetting->desc.bInterfaceNumber );
+      return -EINVAL;
+   }
+   
+   // Collect In and Out endpoints
+   numEndpoints = pIntf->cur_altsetting->desc.bNumEndpoints;
+   for (endpointIndex = 0; endpointIndex < numEndpoints; endpointIndex++)
+   {
+      pEndpoint = pIntf->cur_altsetting->endpoint + endpointIndex;
+      if (pEndpoint == NULL)
+      {
+         DBG( "invalid endpoint %u\n", endpointIndex );
+         return -EINVAL;
+      }
+      
+      if (usb_endpoint_dir_in( &pEndpoint->desc ) == true
+      &&  usb_endpoint_xfer_int( &pEndpoint->desc ) == false)
+      {
+         pIn = pEndpoint;
+      }
+      else if (usb_endpoint_dir_out( &pEndpoint->desc ) == true)
+      {
+         pOut = pEndpoint;
+      }
+   }
+   
+   if (pIn == NULL || pOut == NULL)
+   {
+      DBG( "invalid endpoints\n" );
+      return -EINVAL;
+   }
+
+   if (usb_set_interface( pDev->udev, 
+                          pIntf->cur_altsetting->desc.bInterfaceNumber,
+                          0 ) != 0)
+   {
+      DBG( "unable to set interface\n" );
+      return -EINVAL;
+   }
+
+   pDev->in = usb_rcvbulkpipe( pDev->udev,
+                   pIn->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK );
+   pDev->out = usb_sndbulkpipe( pDev->udev,
+                   pOut->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK );
+                   
+   DBG( "in %x, out %x\n", 
+        pIn->desc.bEndpointAddress, 
+        pOut->desc.bEndpointAddress );
+
+   // In later versions of the kernel, usbnet helps with this
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION( 2,6,23 ))
+   pIntf->dev.platform_data = (void *)pDev;
+#endif
+
+   return 0;
+}
+
+/*===========================================================================
+METHOD:
+   QCNetDriverUnbind (Public Method)
+
+DESCRIPTION:
+   Deregisters QMI device (Registration happened in the probe function)
+
+PARAMETERS
+   pDev           [ I ] - Pointer to usbnet device
+   pIntfUnused    [ I ] - Pointer to interface
+
+RETURN VALUE:
+   None
+===========================================================================*/
+static void QCNetDriverUnbind( 
+   struct usbnet *         pDev, 
+   struct usb_interface *  pIntf)
+{
+   sQCUSBNet * pQCDev = (sQCUSBNet *)pDev->data[0];
+
+   // Should already be down, but just in case...
+   netif_carrier_off( pDev->net );
+
+   DeregisterQMIDevice( pQCDev );
+   
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION( 2,6,29 ))
+   kfree( pDev->net->netdev_ops );
+   pDev->net->netdev_ops = NULL;
+#endif
+
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION( 2,6,23 ))
+   pIntf->dev.platform_data = NULL;
+#endif
+
+   kfree( pQCDev );
+   pQCDev = NULL;
+}
+
+/*===========================================================================
+METHOD:
+   QCUSBNetURBCallback (Public Method)
+
+DESCRIPTION:
+   Write is complete, cleanup and signal that we're ready for next packet
+
+PARAMETERS
+   pURB     [ I ] - Pointer to sAutoPM struct
+
+RETURN VALUE:
+   None
+===========================================================================*/
+void QCUSBNetURBCallback( struct urb * pURB )
+{
+   unsigned long activeURBflags;
+   sAutoPM * pAutoPM = (sAutoPM *)pURB->context;
+   if (pAutoPM == NULL)
+   {
+      // Should never happen
+      DBG( "bad context\n" );
+      return;
+   }
+
+   if (pURB->status != 0)
+   {
+      // Note that in case of an error, the behaviour is no different
+      DBG( "urb finished with error %d\n", pURB->status );
+   }
+
+   // Remove activeURB (memory to be freed later)
+   spin_lock_irqsave( &pAutoPM->mActiveURBLock, activeURBflags );
+
+   // EAGAIN used to signify callback is done
+   pAutoPM->mpActiveURB = ERR_PTR( -EAGAIN );
+
+   spin_unlock_irqrestore( &pAutoPM->mActiveURBLock, activeURBflags );
+
+   up( &pAutoPM->mThreadDoWork );
+   
+   usb_free_urb( pURB );
+}
+
+/*===========================================================================
+METHOD:
+   QCUSBNetTXTimeout (Public Method)
+
+DESCRIPTION:
+   Timeout declared by the net driver.  Stop all transfers
+
+PARAMETERS
+   pNet     [ I ] - Pointer to net device
+
+RETURN VALUE:
+   None
+===========================================================================*/
+void QCUSBNetTXTimeout( struct net_device * pNet )
+{
+   struct sQCUSBNet * pQCDev;
+   sAutoPM * pAutoPM;
+   sURBList * pURBListEntry;
+   unsigned long activeURBflags, URBListFlags;
+   struct usbnet * pDev = netdev_priv( pNet );
+
+   if (pDev == NULL || pDev->net == NULL)
+   {
+      DBG( "failed to get usbnet device\n" );
+      return;
+   }
+   
+   pQCDev = (sQCUSBNet *)pDev->data[0];
+   if (pQCDev == NULL)
+   {
+      DBG( "failed to get QMIDevice\n" );
+      return;
+   }
+   pAutoPM = &pQCDev->mAutoPM;
+
+   DBG( "\n" );
+
+   // Stop activeURB
+   spin_lock_irqsave( &pAutoPM->mActiveURBLock, activeURBflags );
+
+   if (pAutoPM->mpActiveURB != NULL)
+   {
+      usb_kill_urb( pAutoPM->mpActiveURB );
+   }
+
+   spin_unlock_irqrestore( &pAutoPM->mActiveURBLock, activeURBflags );
+
+   // Cleanup URB List
+   spin_lock_irqsave( &pAutoPM->mURBListLock, URBListFlags );
+
+   pURBListEntry = pAutoPM->mpURBList;
+   while (pURBListEntry != NULL)
+   {
+      pAutoPM->mpURBList = pAutoPM->mpURBList->mpNext;
+      usb_free_urb( pURBListEntry->mpURB );
+      kfree( pURBListEntry );
+      pURBListEntry = pAutoPM->mpURBList;
+   }
+
+   spin_unlock_irqrestore( &pAutoPM->mURBListLock, URBListFlags );
+
+   up( &pAutoPM->mThreadDoWork );
+
+   return;
+}
+
+/*===========================================================================
+METHOD:
+   QCUSBNetAutoPMThread (Public Method)
+
+DESCRIPTION:
+   Handle device Auto PM state asynchronously
+   Handle network packet transmission asynchronously
+
+PARAMETERS
+   pData     [ I ] - Pointer to sAutoPM struct
+
+RETURN VALUE:
+   int - 0 for success
+         Negative errno for error
+===========================================================================*/
+static int QCUSBNetAutoPMThread( void * pData )
+{
+   unsigned long activeURBflags, URBListFlags;
+   sURBList * pURBListEntry;
+   int status;
+   struct usb_device * pUdev;
+   sAutoPM * pAutoPM = (sAutoPM *)pData;
+   if (pAutoPM == NULL)
+   {
+      DBG( "passed null pointer\n" );
+      return -EINVAL;
+   }
+   
+   pUdev = interface_to_usbdev( pAutoPM->mpIntf );
+
+   DBG( "traffic thread started\n" );
+
+   while (pAutoPM->mbExit == false)
+   {
+      // Wait for someone to poke us
+      down( &pAutoPM->mThreadDoWork );
+
+      // Time to exit?
+      if (pAutoPM->mbExit == true)
+      {
+         // Stop activeURB
+         spin_lock_irqsave( &pAutoPM->mActiveURBLock, activeURBflags );
+
+         if (pAutoPM->mpActiveURB != NULL)
+         {
+            usb_kill_urb( pAutoPM->mpActiveURB );
+         }
+         // Will be freed in callback function
+
+         spin_unlock_irqrestore( &pAutoPM->mActiveURBLock, activeURBflags );
+
+         // Cleanup URB List
+         spin_lock_irqsave( &pAutoPM->mURBListLock, URBListFlags );
+
+         pURBListEntry = pAutoPM->mpURBList;
+         while (pURBListEntry != NULL)
+         {
+            pAutoPM->mpURBList = pAutoPM->mpURBList->mpNext;
+            usb_free_urb( pURBListEntry->mpURB );
+            kfree( pURBListEntry );
+            pURBListEntry = pAutoPM->mpURBList;
+         }
+
+         spin_unlock_irqrestore( &pAutoPM->mURBListLock, URBListFlags );
+
+         break;
+      }
+      
+      // Is our URB active?
+      spin_lock_irqsave( &pAutoPM->mActiveURBLock, activeURBflags );
+
+      // EAGAIN used to signify callback is done
+      if (IS_ERR( pAutoPM->mpActiveURB ) 
+      &&  PTR_ERR( pAutoPM->mpActiveURB ) == -EAGAIN )
+      {
+         pAutoPM->mpActiveURB = NULL;
+
+         // Restore IRQs so task can sleep
+         spin_unlock_irqrestore( &pAutoPM->mActiveURBLock, activeURBflags );
+         
+         // URB is done, decrement the Auto PM usage count
+         usb_autopm_put_interface( pAutoPM->mpIntf );
+
+         // Lock ActiveURB again
+         spin_lock_irqsave( &pAutoPM->mActiveURBLock, activeURBflags );
+      }
+
+      if (pAutoPM->mpActiveURB != NULL)
+      {
+         // There is already a URB active, go back to sleep
+         spin_unlock_irqrestore( &pAutoPM->mActiveURBLock, activeURBflags );
+         continue;
+      }
+      
+      // Is there a URB waiting to be submitted?
+      spin_lock_irqsave( &pAutoPM->mURBListLock, URBListFlags );
+      if (pAutoPM->mpURBList == NULL)
+      {
+         // No more URBs to submit, go back to sleep
+         spin_unlock_irqrestore( &pAutoPM->mURBListLock, URBListFlags );
+         spin_unlock_irqrestore( &pAutoPM->mActiveURBLock, activeURBflags );
+         continue;
+      }
+
+      // Pop an element
+      pURBListEntry = pAutoPM->mpURBList;
+      pAutoPM->mpURBList = pAutoPM->mpURBList->mpNext;
+      spin_unlock_irqrestore( &pAutoPM->mURBListLock, URBListFlags );
+
+      // Set ActiveURB
+      pAutoPM->mpActiveURB = pURBListEntry->mpURB;
+      spin_unlock_irqrestore( &pAutoPM->mActiveURBLock, activeURBflags );
+
+      // Tell autopm core we need device woken up
+      status = usb_autopm_get_interface( pAutoPM->mpIntf );
+      if (status < 0)
+      {
+         DBG( "unable to autoresume interface: %d\n", status );
+
+         // likely caused by device going from autosuspend -> full suspend
+         if (status == -EPERM)
+         {
+#if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,33 ))
+            pUdev->auto_pm = 0;
+#endif
+            QCSuspend( pAutoPM->mpIntf, PMSG_SUSPEND );
+         }
+
+         // Add pURBListEntry back onto pAutoPM->mpURBList
+         spin_lock_irqsave( &pAutoPM->mURBListLock, URBListFlags );
+         pURBListEntry->mpNext = pAutoPM->mpURBList;
+         pAutoPM->mpURBList = pURBListEntry;
+         spin_unlock_irqrestore( &pAutoPM->mURBListLock, URBListFlags );
+         
+         spin_lock_irqsave( &pAutoPM->mActiveURBLock, activeURBflags );
+         pAutoPM->mpActiveURB = NULL;
+         spin_unlock_irqrestore( &pAutoPM->mActiveURBLock, activeURBflags );
+         
+         // Go back to sleep
+         continue;
+      }
+
+      // Submit URB
+      status = usb_submit_urb( pAutoPM->mpActiveURB, GFP_KERNEL );
+      if (status < 0)
+      {
+         // Could happen for a number of reasons
+         DBG( "Failed to submit URB: %d.  Packet dropped\n", status );
+         spin_lock_irqsave( &pAutoPM->mActiveURBLock, activeURBflags );
+         usb_free_urb( pAutoPM->mpActiveURB );
+         pAutoPM->mpActiveURB = NULL;
+         spin_unlock_irqrestore( &pAutoPM->mActiveURBLock, activeURBflags );
+         usb_autopm_put_interface( pAutoPM->mpIntf );
+
+         // Loop again
+         up( &pAutoPM->mThreadDoWork );
+      }
+      
+      kfree( pURBListEntry );
+   }   
+   
+   DBG( "traffic thread exiting\n" );
+   pAutoPM->mpThread = NULL;
+   return 0;
+}      
+
+/*===========================================================================
+METHOD:
+   QCUSBNetStartXmit (Public Method)
+
+DESCRIPTION:
+   Convert sk_buff to usb URB and queue for transmit
+
+PARAMETERS
+   pNet     [ I ] - Pointer to net device
+
+RETURN VALUE:
+   NETDEV_TX_OK on success
+   NETDEV_TX_BUSY on error
+===========================================================================*/
+int QCUSBNetStartXmit( 
+   struct sk_buff *     pSKB,
+   struct net_device *  pNet )
+{
+   unsigned long URBListFlags;
+   struct sQCUSBNet * pQCDev;
+   sAutoPM * pAutoPM;
+   sURBList * pURBListEntry, ** ppURBListEnd;
+   void * pURBData;
+   struct usbnet * pDev = netdev_priv( pNet );
+   
+   DBG( "\n" );
+   
+   if (pDev == NULL || pDev->net == NULL)
+   {
+      DBG( "failed to get usbnet device\n" );
+      return NETDEV_TX_BUSY;
+   }
+   
+   pQCDev = (sQCUSBNet *)pDev->data[0];
+   if (pQCDev == NULL)
+   {
+      DBG( "failed to get QMIDevice\n" );
+      return NETDEV_TX_BUSY;
+   }
+   pAutoPM = &pQCDev->mAutoPM;
+   
+   if (QTestDownReason( pQCDev, DRIVER_SUSPENDED ) == true)
+   {
+      // Should not happen
+      DBG( "device is suspended\n" );
+      dump_stack();
+      return NETDEV_TX_BUSY;
+   }
+   
+   // Convert the sk_buff into a URB
+
+   // Allocate URBListEntry
+   pURBListEntry = kmalloc( sizeof( sURBList ), GFP_ATOMIC );
+   if (pURBListEntry == NULL)
+   {
+      DBG( "unable to allocate URBList memory\n" );
+      return NETDEV_TX_BUSY;
+   }
+   pURBListEntry->mpNext = NULL;
+
+   // Allocate URB
+   pURBListEntry->mpURB = usb_alloc_urb( 0, GFP_ATOMIC );
+   if (pURBListEntry->mpURB == NULL)
+   {
+      DBG( "unable to allocate URB\n" );
+      return NETDEV_TX_BUSY;
+   }
+
+   // Allocate URB transfer_buffer
+   pURBData = kmalloc( pSKB->len, GFP_ATOMIC );
+   if (pURBData == NULL)
+   {
+      DBG( "unable to allocate URB data\n" );
+      return NETDEV_TX_BUSY;
+   }
+   // Fill will SKB's data
+   memcpy( pURBData, pSKB->data, pSKB->len );
+
+   usb_fill_bulk_urb( pURBListEntry->mpURB,
+                      pQCDev->mpNetDev->udev,
+                      pQCDev->mpNetDev->out,
+                      pURBData,
+                      pSKB->len,
+                      QCUSBNetURBCallback,
+                      pAutoPM );
+   
+   // Aquire lock on URBList
+   spin_lock_irqsave( &pAutoPM->mURBListLock, URBListFlags );
+   
+   // Add URB to end of list
+   ppURBListEnd = &pAutoPM->mpURBList;
+   while ((*ppURBListEnd) != NULL)
+   {
+      ppURBListEnd = &(*ppURBListEnd)->mpNext;
+   }
+   *ppURBListEnd = pURBListEntry;
+
+   spin_unlock_irqrestore( &pAutoPM->mURBListLock, URBListFlags );
+
+   up( &pAutoPM->mThreadDoWork );
+
+   // Start transfer timer
+   pNet->trans_start = jiffies;
+   // Free SKB
+   dev_kfree_skb_any( pSKB );
+
+   return NETDEV_TX_OK;
+}
+
+/*===========================================================================
+METHOD:
+   QCUSBNetOpen (Public Method)
+
+DESCRIPTION:
+   Wrapper to usbnet_open, correctly handling autosuspend
+   Start AutoPM thread
+
+PARAMETERS
+   pNet     [ I ] - Pointer to net device
+
+RETURN VALUE:
+   int - 0 for success
+         Negative errno for error
+===========================================================================*/
+int QCUSBNetOpen( struct net_device * pNet )
+{
+   int status = 0;
+   struct sQCUSBNet * pQCDev;
+   struct usbnet * pDev = netdev_priv( pNet );
+   
+   if (pDev == NULL)
+   {
+      DBG( "failed to get usbnet device\n" );
+      return -ENXIO;
+   }
+   
+   pQCDev = (sQCUSBNet *)pDev->data[0];
+   if (pQCDev == NULL)
+   {
+      DBG( "failed to get QMIDevice\n" );
+      return -ENXIO;
+   }
+
+   DBG( "\n" );
+
+   // Start the AutoPM thread
+   pQCDev->mAutoPM.mpIntf = pQCDev->mpIntf;
+   pQCDev->mAutoPM.mbExit = false;
+   pQCDev->mAutoPM.mpURBList = NULL;
+   pQCDev->mAutoPM.mpActiveURB = NULL;
+   spin_lock_init( &pQCDev->mAutoPM.mURBListLock );
+   spin_lock_init( &pQCDev->mAutoPM.mActiveURBLock );
+   sema_init( &pQCDev->mAutoPM.mThreadDoWork, 0 );
+   
+   pQCDev->mAutoPM.mpThread = kthread_run( QCUSBNetAutoPMThread, 
+                              &pQCDev->mAutoPM, 
+                              "QCUSBNetAutoPMThread" );
+   if (IS_ERR( pQCDev->mAutoPM.mpThread ))
+   {
+      DBG( "AutoPM thread creation error\n" );
+      return PTR_ERR( pQCDev->mAutoPM.mpThread );
+   }
+
+   // Allow traffic
+   QClearDownReason( pQCDev, NET_IFACE_STOPPED );
+
+   // Pass to usbnet_open if defined
+   if (pQCDev->mpUSBNetOpen != NULL)
+   {
+      status = pQCDev->mpUSBNetOpen( pNet );
+   
+      // If usbnet_open was successful enable Auto PM
+      if (status == 0)
+      {
+#if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,33 ))
+         usb_autopm_enable( pQCDev->mpIntf );
+#else
+         usb_autopm_put_interface( pQCDev->mpIntf );
+#endif
+      }
+   }
+   else
+   {
+      DBG( "no USBNetOpen defined\n" );
+   }
+   
+   return status;
+}
+
+/*===========================================================================
+METHOD:
+   QCUSBNetStop (Public Method)
+
+DESCRIPTION:
+   Wrapper to usbnet_stop, correctly handling autosuspend
+   Stop AutoPM thread
+
+PARAMETERS
+   pNet     [ I ] - Pointer to net device
+
+RETURN VALUE:
+   int - 0 for success
+         Negative errno for error
+===========================================================================*/
+int QCUSBNetStop( struct net_device * pNet )
+{
+   struct sQCUSBNet * pQCDev;
+   struct usbnet * pDev = netdev_priv( pNet );
+
+   if (pDev == NULL || pDev->net == NULL)
+   {
+      DBG( "failed to get netdevice\n" );
+      return -ENXIO;
+   }
+   
+   pQCDev = (sQCUSBNet *)pDev->data[0];
+   if (pQCDev == NULL)
+   {
+      DBG( "failed to get QMIDevice\n" );
+      return -ENXIO;
+   }
+
+   // Stop traffic
+   QSetDownReason( pQCDev, NET_IFACE_STOPPED );
+
+   // Tell traffic thread to exit
+   pQCDev->mAutoPM.mbExit = true;
+   up( &pQCDev->mAutoPM.mThreadDoWork );
+   
+   // Wait for it to exit
+   while( pQCDev->mAutoPM.mpThread != NULL )
+   {
+      msleep( 100 );
+   }
+   DBG( "thread stopped\n" );
+
+   // Pass to usbnet_stop, if defined
+   if (pQCDev->mpUSBNetStop != NULL)
+   {
+      return pQCDev->mpUSBNetStop( pNet );
+   }
+   else
+   {
+      return 0;
+   }
+}
+
+/*=========================================================================*/
+// Struct driver_info
+/*=========================================================================*/
+static const struct driver_info QCNetInfo = 
+{
+   .description   = "QCUSBNet Ethernet Device",
+   .flags         = FLAG_ETHER,
+   .bind          = QCNetDriverBind,
+   .unbind        = QCNetDriverUnbind,
+   .data          = 0,
+};
+
+/*=========================================================================*/
+// Qualcomm Gobi 2000 VID/PIDs
+/*=========================================================================*/
+static const struct usb_device_id QCVIDPIDTable [] =
+{
+   // Acer Gobi 2000
+   {
+      USB_DEVICE( 0x05c6, 0x9215 ),
+      .driver_info = (unsigned long)&QCNetInfo
+   },
+   // Asus Gobi 2000
+   {
+      USB_DEVICE( 0x05c6, 0x9265 ),
+      .driver_info = (unsigned long)&QCNetInfo
+   },
+   // CMOTech Gobi 2000
+   {
+      USB_DEVICE( 0x16d8, 0x8002 ),
+      .driver_info = (unsigned long)&QCNetInfo
+   },
+   // Dell Gobi 2000
+   {
+      USB_DEVICE( 0x413c, 0x8186 ),
+      .driver_info = (unsigned long)&QCNetInfo
+   },
+   // Entourage Gobi 2000
+   {
+      USB_DEVICE( 0x1410, 0xa010 ),
+      .driver_info = (unsigned long)&QCNetInfo
+   },
+   // Entourage Gobi 2000
+   {
+      USB_DEVICE( 0x1410, 0xa011 ),
+      .driver_info = (unsigned long)&QCNetInfo
+   },
+   // Entourage Gobi 2000
+   {
+      USB_DEVICE( 0x1410, 0xa012 ),
+      .driver_info = (unsigned long)&QCNetInfo
+   },
+   // Entourage Gobi 2000
+   {
+      USB_DEVICE( 0x1410, 0xa013 ),
+      .driver_info = (unsigned long)&QCNetInfo
+   },
+   // HP Gobi 2000
+   { 
+      USB_DEVICE( 0x03f0, 0x251d ),
+      .driver_info = (unsigned long)&QCNetInfo 
+   },
+   // Lenovo Gobi 2000
+   {
+      USB_DEVICE( 0x05c6, 0x9205 ),
+      .driver_info = (unsigned long)&QCNetInfo
+   },
+   // Panasonic Gobi 2000
+   {
+      USB_DEVICE( 0x04da, 0x250f ),
+      .driver_info = (unsigned long)&QCNetInfo
+   },
+   // Samsung Gobi 2000
+   {
+      USB_DEVICE( 0x05c6, 0x9245 ),
+      .driver_info = (unsigned long)&QCNetInfo
+   },
+   // Sierra Wireless Gobi 2000
+   {
+      USB_DEVICE( 0x1199, 0x9001 ),
+      .driver_info = (unsigned long)&QCNetInfo
+   },
+   // Sierra Wireless Gobi 2000
+   {
+      USB_DEVICE( 0x1199, 0x9002 ),
+      .driver_info = (unsigned long)&QCNetInfo
+   },
+   // Sierra Wireless Gobi 2000
+   {
+      USB_DEVICE( 0x1199, 0x9003 ),
+      .driver_info = (unsigned long)&QCNetInfo
+   },
+   // Sierra Wireless Gobi 2000
+   {
+      USB_DEVICE( 0x1199, 0x9004 ),
+      .driver_info = (unsigned long)&QCNetInfo
+   },
+   // Sierra Wireless Gobi 2000
+   {
+      USB_DEVICE( 0x1199, 0x9005 ),
+      .driver_info = (unsigned long)&QCNetInfo
+   },
+   // Sierra Wireless Gobi 2000
+   {
+      USB_DEVICE( 0x1199, 0x9006 ),
+      .driver_info = (unsigned long)&QCNetInfo
+   },
+   // Sierra Wireless Gobi 2000
+   {
+      USB_DEVICE( 0x1199, 0x9007 ),
+      .driver_info = (unsigned long)&QCNetInfo
+   },
+   // Sierra Wireless Gobi 2000
+   {
+      USB_DEVICE( 0x1199, 0x9008 ),
+      .driver_info = (unsigned long)&QCNetInfo
+   },
+   // Sierra Wireless Gobi 2000
+   {
+      USB_DEVICE( 0x1199, 0x9009 ),
+      .driver_info = (unsigned long)&QCNetInfo
+   },
+   // Sierra Wireless Gobi 2000
+   {
+      USB_DEVICE( 0x1199, 0x900a ),
+      .driver_info = (unsigned long)&QCNetInfo
+   },
+   // Sony Gobi 2000
+   {
+      USB_DEVICE( 0x05c6, 0x9225 ),
+      .driver_info = (unsigned long)&QCNetInfo
+   },
+   // Top Global Gobi 2000
+   {
+      USB_DEVICE( 0x05c6, 0x9235 ),
+      .driver_info = (unsigned long)&QCNetInfo
+   },
+   // iRex Technologies Gobi 2000
+   {
+      USB_DEVICE( 0x05c6, 0x9275 ),
+      .driver_info = (unsigned long)&QCNetInfo
+   },
+   // Generic Gobi 2000
+   {
+      USB_DEVICE( 0x5c6, 0x920B ),
+      .driver_info = (unsigned long)&QCNetInfo
+   },
+
+   //Terminating entry
+   { }
+};
+
+MODULE_DEVICE_TABLE( usb, QCVIDPIDTable );
+
+/*===========================================================================
+METHOD:
+   QCUSBNetProbe (Public Method)
+
+DESCRIPTION:
+   Run usbnet_probe
+   Setup QMI device
+
+PARAMETERS
+   pIntf        [ I ] - Pointer to interface
+   pVIDPIDs     [ I ] - Pointer to VID/PID table
+
+RETURN VALUE:
+   int - 0 for success
+         Negative errno for error
+===========================================================================*/
+int QCUSBNetProbe( 
+   struct usb_interface *        pIntf, 
+   const struct usb_device_id *  pVIDPIDs )
+{
+   int status;
+   struct usbnet * pDev;
+   sQCUSBNet * pQCDev;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION( 2,6,29 ))
+   struct net_device_ops * pNetDevOps;
+#endif
+
+   status = usbnet_probe( pIntf, pVIDPIDs );
+   if(status < 0 )
+   {
+      DBG( "usbnet_probe failed %d\n", status );
+      return status;
+   }
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,23 ))
+   pDev = usb_get_intfdata( pIntf );
+#else
+   pDev = (struct usbnet *)pIntf->dev.platform_data;
+#endif
+
+   if (pDev == NULL || pDev->net == NULL)
+   {
+      DBG( "failed to get netdevice\n" );
+      return -ENXIO;
+   }
+
+   pQCDev = kmalloc( sizeof( sQCUSBNet ), GFP_KERNEL );
+   if (pQCDev == NULL)
+   {
+      DBG( "falied to allocate device buffers" );
+      return -ENOMEM;
+   }
+   
+   pDev->data[0] = (unsigned long)pQCDev;
+   
+   pQCDev->mpNetDev = pDev;
+
+   // Overload PM related network functions
+#if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,29 ))
+   pQCDev->mpUSBNetOpen = pDev->net->open;
+   pDev->net->open = QCUSBNetOpen;
+   pQCDev->mpUSBNetStop = pDev->net->stop;
+   pDev->net->stop = QCUSBNetStop;
+   pDev->net->hard_start_xmit = QCUSBNetStartXmit;
+   pDev->net->tx_timeout = QCUSBNetTXTimeout;
+#else
+   pNetDevOps = kmalloc( sizeof( struct net_device_ops ), GFP_KERNEL );
+   if (pNetDevOps == NULL)
+   {
+      DBG( "falied to allocate net device ops" );
+      return -ENOMEM;
+   }
+   memcpy( pNetDevOps, pDev->net->netdev_ops, sizeof( struct net_device_ops ) );
+   
+   pQCDev->mpUSBNetOpen = pNetDevOps->ndo_open;
+   pNetDevOps->ndo_open = QCUSBNetOpen;
+   pQCDev->mpUSBNetStop = pNetDevOps->ndo_stop;
+   pNetDevOps->ndo_stop = QCUSBNetStop;
+   pNetDevOps->ndo_start_xmit = QCUSBNetStartXmit;
+   pNetDevOps->ndo_tx_timeout = QCUSBNetTXTimeout;
+
+   pDev->net->netdev_ops = pNetDevOps;
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,31 ))
+   memset( &(pQCDev->mpNetDev->stats), 0, sizeof( struct net_device_stats ) );
+#else
+   memset( &(pQCDev->mpNetDev->net->stats), 0, sizeof( struct net_device_stats ) );
+#endif
+
+   pQCDev->mpIntf = pIntf;
+   memset( &(pQCDev->mMEID), '0', 14 );
+   
+   DBG( "Mac Address:\n" );
+   PrintHex( &pQCDev->mpNetDev->net->dev_addr[0], 6 );
+
+   pQCDev->mbQMIValid = false;
+   memset( &pQCDev->mQMIDev, 0, sizeof( sQMIDev ) );
+
+   pQCDev->mQMIDev.mpDevClass = gpClass;
+   
+   sema_init( &pQCDev->mAutoPM.mThreadDoWork, 0 );
+   spin_lock_init( &pQCDev->mQMIDev.mClientMemLock );
+
+   // Default to device down
+   pQCDev->mDownReason = 0;
+   QSetDownReason( pQCDev, NO_NDIS_CONNECTION );
+   QSetDownReason( pQCDev, NET_IFACE_STOPPED );
+
+   // Register QMI
+   status = RegisterQMIDevice( pQCDev );
+   if (status != 0)
+   {
+      // Clean up
+      DeregisterQMIDevice( pQCDev );
+      return status;
+   }
+   
+   // Success
+   return status;
+}
+
+EXPORT_SYMBOL_GPL( QCUSBNetProbe );
+
+static struct usb_driver QCUSBNet =
+{
+   .name       = "QCUSBNet2k",
+   .id_table   = QCVIDPIDTable,
+   .probe      = QCUSBNetProbe,
+   .disconnect = usbnet_disconnect,
+   .suspend    = QCSuspend,
+   .resume     = QCResume,
+   .supports_autosuspend = true,
+};
+
+/*===========================================================================
+METHOD:
+   QCUSBNetModInit (Public Method)
+
+DESCRIPTION:
+   Initialize module
+   Create device class
+   Register out usb_driver struct
+
+RETURN VALUE:
+   int - 0 for success
+         Negative errno for error
+===========================================================================*/
+static int __init QCUSBNetModInit( void )
+{
+   gpClass = class_create( THIS_MODULE, "QCQMI" );
+   if (IS_ERR( gpClass ) == true)
+   {
+      DBG( "error at class_create %ld\n",
+           PTR_ERR( gpClass ) );
+      return -ENOMEM;
+   }
+
+   // This will be shown whenever driver is loaded
+   printk( KERN_INFO "%s: %s\n", DRIVER_DESC, DRIVER_VERSION );
+
+   return usb_register( &QCUSBNet );
+}
+module_init( QCUSBNetModInit );
+
+/*===========================================================================
+METHOD:
+   QCUSBNetModExit (Public Method)
+
+DESCRIPTION:
+   Deregister module
+   Destroy device class
+
+RETURN VALUE:
+   void
+===========================================================================*/
+static void __exit QCUSBNetModExit( void )
+{
+   usb_deregister( &QCUSBNet );
+
+   class_destroy( gpClass );
+}
+module_exit( QCUSBNetModExit );
+
+#ifdef bool
+#undef bool
+#endif
+
+MODULE_VERSION( DRIVER_VERSION );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE( "GPL v2" );
+
+module_param( debug, bool, S_IRUGO | S_IWUSR );
+MODULE_PARM_DESC( debug, "Debuging enabled or not" );
+
diff --git a/drivers/staging/gobi/QCUSBNet2k/QMI.c b/drivers/staging/gobi/QCUSBNet2k/QMI.c
new file mode 100644
index 0000000..fe7eebe
--- /dev/null
+++ b/drivers/staging/gobi/QCUSBNet2k/QMI.c
@@ -0,0 +1,954 @@
+/*===========================================================================
+FILE:
+   QMI.c
+
+DESCRIPTION:
+   Qualcomm QMI driver code
+   
+FUNCTIONS:
+   Generic QMUX functions
+      ParseQMUX
+      FillQMUX
+   
+   Generic QMI functions
+      GetTLV
+      ValidQMIMessage
+      GetQMIMessageID
+
+   Fill Buffers with QMI requests
+      QMICTLGetClientIDReq
+      QMICTLReleaseClientIDReq
+      QMICTLReadyReq
+      QMIWDSSetEventReportReq
+      QMIWDSGetPKGSRVCStatusReq
+      QMIDMSGetMEIDReq
+      
+   Parse data from QMI responses
+      QMICTLGetClientIDResp
+      QMICTLReleaseClientIDResp
+      QMIWDSEventResp
+      QMIDMSGetMEIDResp
+
+Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 2 and
+only version 2 as published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+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 Street, Fifth Floor, Boston, MA
+02110-1301, USA.
+
+===========================================================================*/
+
+//---------------------------------------------------------------------------
+// Include Files
+//---------------------------------------------------------------------------
+#include "QMI.h"
+
+
+/*=========================================================================*/
+// Get sizes of buffers needed by QMI requests
+/*=========================================================================*/
+
+/*===========================================================================
+METHOD:
+   QMUXHeaderSize (Public Method)
+
+DESCRIPTION:
+   Get size of buffer needed for QMUX
+ 
+RETURN VALUE:
+   u16 - size of buffer
+===========================================================================*/
+u16 QMUXHeaderSize( void )
+{
+   return sizeof( sQMUX );
+}
+
+/*===========================================================================
+METHOD:
+   QMICTLGetClientIDReqSize (Public Method)
+
+DESCRIPTION:
+   Get size of buffer needed for QMUX + QMICTLGetClientIDReq
+ 
+RETURN VALUE:
+   u16 - size of buffer
+===========================================================================*/
+u16 QMICTLGetClientIDReqSize( void )
+{
+   return sizeof( sQMUX ) + 10;
+}
+
+/*===========================================================================
+METHOD:
+   QMICTLReleaseClientIDReqSize (Public Method)
+
+DESCRIPTION:
+   Get size of buffer needed for QMUX + QMICTLReleaseClientIDReq
+ 
+RETURN VALUE:
+   u16 - size of header
+===========================================================================*/
+u16 QMICTLReleaseClientIDReqSize( void )
+{
+   return sizeof( sQMUX ) + 11;
+}
+
+/*===========================================================================
+METHOD:
+   QMICTLReadyReqSize (Public Method)
+
+DESCRIPTION:
+   Get size of buffer needed for QMUX + QMICTLReadyReq
+ 
+RETURN VALUE:
+   u16 - size of buffer
+===========================================================================*/
+u16 QMICTLReadyReqSize( void )
+{
+   return sizeof( sQMUX ) + 6;
+}
+
+/*===========================================================================
+METHOD:
+   QMIWDSSetEventReportReqSize (Public Method)
+
+DESCRIPTION:
+   Get size of buffer needed for QMUX + QMIWDSSetEventReportReq
+ 
+RETURN VALUE:
+   u16 - size of buffer
+===========================================================================*/
+u16 QMIWDSSetEventReportReqSize( void )
+{
+   return sizeof( sQMUX ) + 15;
+}
+
+/*===========================================================================
+METHOD:
+   QMIWDSGetPKGSRVCStatusReqSize (Public Method)
+
+DESCRIPTION:
+   Get size of buffer needed for QMUX + QMIWDSGetPKGSRVCStatusReq
+ 
+RETURN VALUE:
+   u16 - size of buffer
+===========================================================================*/
+u16 QMIWDSGetPKGSRVCStatusReqSize( void )
+{
+   return sizeof( sQMUX ) + 7;
+}
+
+/*===========================================================================
+METHOD:
+   QMIDMSGetMEIDReqSize (Public Method)
+
+DESCRIPTION:
+   Get size of buffer needed for QMUX + QMIDMSGetMEIDReq
+ 
+RETURN VALUE:
+   u16 - size of buffer
+===========================================================================*/
+u16 QMIDMSGetMEIDReqSize( void )
+{
+   return sizeof( sQMUX ) + 7;
+}
+
+/*=========================================================================*/
+// Generic QMUX functions
+/*=========================================================================*/
+
+/*===========================================================================
+METHOD:
+   ParseQMUX (Public Method)
+
+DESCRIPTION:
+   Remove QMUX headers from a buffer
+
+PARAMETERS
+   pClientID       [ O ] - On success, will point to Client ID
+   pBuffer         [ I ] - Full Message passed in
+   buffSize        [ I ] - Size of pBuffer
+
+RETURN VALUE:
+   int - Positive for size of QMUX header
+         Negative errno for error
+===========================================================================*/
+int ParseQMUX(
+   u16 *    pClientID,
+   void *   pBuffer,
+   u16      buffSize )
+{
+   sQMUX * pQMUXHeader;
+   
+   if (pBuffer == 0 || buffSize < 12)
+   {
+      return -ENOMEM;
+   }
+
+   // QMUX Header
+   pQMUXHeader = (sQMUX *)pBuffer;
+
+   if (pQMUXHeader->mTF != 1
+   ||  pQMUXHeader->mLength != buffSize - 1
+   ||  pQMUXHeader->mCtrlFlag != 0x80 )
+   {
+      return -EINVAL;
+   }
+
+   // Client ID   
+   *pClientID = (pQMUXHeader->mQMIClientID << 8) 
+              + pQMUXHeader->mQMIService;
+   
+   return sizeof( sQMUX );
+}
+
+/*===========================================================================
+METHOD:
+   FillQMUX (Public Method)
+
+DESCRIPTION:
+   Fill buffer with QMUX headers
+
+PARAMETERS
+   clientID        [ I ] - Client ID
+   pBuffer         [ O ] - Buffer to be filled
+   buffSize        [ I ] - Size of pBuffer (must be at least 6)
+
+RETURN VALUE:
+   int - 0 for success
+         Negative errno for error
+===========================================================================*/
+int FillQMUX(
+   u16      clientID,
+   void *   pBuffer,
+   u16      buffSize )
+{
+   sQMUX * pQMUXHeader;
+
+   if (pBuffer == 0 ||  buffSize < sizeof( sQMUX ))
+   {
+      return -ENOMEM;
+   }
+
+   // QMUX Header
+   pQMUXHeader = (sQMUX *)pBuffer;
+
+   pQMUXHeader->mTF = 1;
+   pQMUXHeader->mLength = buffSize - 1;
+   pQMUXHeader->mCtrlFlag = 0;
+
+   // Service and Client ID   
+   pQMUXHeader->mQMIService = clientID & 0xff;
+   pQMUXHeader->mQMIClientID = clientID >> 8;
+
+   return 0;
+}
+
+/*=========================================================================*/
+// Generic QMI functions
+/*=========================================================================*/
+
+/*===========================================================================
+METHOD:
+   GetTLV (Public Method)
+
+DESCRIPTION:
+   Get data bufffer of a specified TLV from a QMI message
+
+   QMI Message shall NOT include SDU
+   
+PARAMETERS
+   pQMIMessage    [ I ] - QMI Message buffer
+   messageLen     [ I ] - Size of QMI Message buffer
+   type           [ I ] - Desired Type
+   pOutDataBuf    [ O ] - Buffer to be filled with TLV
+   messageLen     [ I ] - Size of QMI Message buffer
+
+RETURN VALUE:
+   u16 - Size of TLV for success
+         Negative errno for error
+===========================================================================*/
+u16 GetTLV(
+   void *   pQMIMessage,
+   u16      messageLen,
+   u8       type,
+   void *   pOutDataBuf,
+   u16      bufferLen )
+{
+   u16 pos;
+   u16 tlvSize = 0;
+   u16 cpyCount;
+   
+   if (pQMIMessage == 0 || pOutDataBuf == 0)
+   {
+      return -ENOMEM;
+   }   
+   
+   for (pos = 4; 
+        pos + 3 < messageLen; 
+        pos += tlvSize + 3)
+   {
+      tlvSize = *(u16 *)(pQMIMessage + pos + 1);
+      if (*(u8 *)(pQMIMessage + pos) == type)
+      {
+         if (bufferLen < tlvSize)
+         {
+            return -ENOMEM;
+         }
+        
+         /* replacement memcpy
+            memcpy( pOutDataBuf,
+                    pQMIMessage + pos + 3,
+                    tlvSize ); */
+         
+         for (cpyCount = 0; cpyCount < tlvSize; cpyCount++)
+         {
+            *((char*)(pOutDataBuf + cpyCount)) = *((char*)(pQMIMessage + pos + 3 + cpyCount));
+         }
+         
+         return tlvSize;
+      }
+   }
+   
+   return -ENOMSG;
+}
+
+/*===========================================================================
+METHOD:
+   ValidQMIMessage (Public Method)
+
+DESCRIPTION:
+   Check mandatory TLV in a QMI message
+
+   QMI Message shall NOT include SDU
+
+PARAMETERS
+   pQMIMessage    [ I ] - QMI Message buffer
+   messageLen     [ I ] - Size of QMI Message buffer
+
+RETURN VALUE:
+   int - 0 for success (no error)
+         Negative errno for error
+         Positive for QMI error code
+===========================================================================*/
+int ValidQMIMessage(
+   void *   pQMIMessage,
+   u16      messageLen )
+{
+   char mandTLV[4];
+
+   if (GetTLV( pQMIMessage, messageLen, 2, &mandTLV[0], 4 ) == 4)
+   {
+      // Found TLV
+      if (*(u16 *)&mandTLV[0] != 0)
+      {
+         return *(u16 *)&mandTLV[2];
+      }
+      else
+      {
+         return 0;
+      }
+   }
+   else
+   {
+      return -ENOMSG;
+   }
+}      
+
+/*===========================================================================
+METHOD:
+   GetQMIMessageID (Public Method)
+
+DESCRIPTION:
+   Get the message ID of a QMI message
+   
+   QMI Message shall NOT include SDU
+
+PARAMETERS
+   pQMIMessage    [ I ] - QMI Message buffer
+   messageLen     [ I ] - Size of QMI Message buffer
+
+RETURN VALUE:
+   int - Positive for message ID
+         Negative errno for error
+===========================================================================*/
+int GetQMIMessageID(
+   void *   pQMIMessage,
+   u16      messageLen )
+{
+   if (messageLen < 2)
+   {
+      return -ENODATA;
+   }
+   else
+   {
+      return *(u16 *)pQMIMessage;
+   }
+}
+
+/*=========================================================================*/
+// Fill Buffers with QMI requests
+/*=========================================================================*/
+
+/*===========================================================================
+METHOD:
+   QMICTLGetClientIDReq (Public Method)
+
+DESCRIPTION:
+   Fill buffer with QMI CTL Get Client ID Request
+
+PARAMETERS
+   pBuffer         [ 0 ] - Buffer to be filled
+   buffSize        [ I ] - Size of pBuffer
+   transactionID   [ I ] - Transaction ID
+   serviceType     [ I ] - Service type requested
+
+RETURN VALUE:
+   int - Positive for resulting size of pBuffer
+         Negative errno for error
+===========================================================================*/
+int QMICTLGetClientIDReq(
+   void *   pBuffer,
+   u16      buffSize,
+   u8       transactionID,
+   u8       serviceType )
+{
+   if (pBuffer == 0 || buffSize < QMICTLGetClientIDReqSize() )
+   {
+      return -ENOMEM;
+   }
+
+   // QMI CTL GET CLIENT ID
+   // Request
+   *(u8 *)(pBuffer + sizeof( sQMUX ))= 0x00;
+   // Transaction ID
+   *(u8 *)(pBuffer + sizeof( sQMUX ) + 1) = transactionID;
+   // Message ID
+   *(u16 *)(pBuffer + sizeof( sQMUX ) + 2) = 0x0022;
+   // Size of TLV's
+   *(u16 *)(pBuffer + sizeof( sQMUX ) + 4) = 0x0004;
+      // QMI Service Type
+      *(u8 *)(pBuffer + sizeof( sQMUX ) + 6)  = 0x01;
+      // Size
+      *(u16 *)(pBuffer + sizeof( sQMUX ) + 7) = 0x0001;
+      // QMI svc type
+      *(u8 *)(pBuffer + sizeof( sQMUX ) + 9)  = serviceType;
+
+  // success
+  return sizeof( sQMUX ) + 10;
+}
+
+/*===========================================================================
+METHOD:
+   QMICTLReleaseClientIDReq (Public Method)
+
+DESCRIPTION:
+   Fill buffer with QMI CTL Release Client ID Request
+
+PARAMETERS
+   pBuffer         [ 0 ] - Buffer to be filled
+   buffSize        [ I ] - Size of pBuffer
+   transactionID   [ I ] - Transaction ID
+   clientID        [ I ] - Service type requested
+
+RETURN VALUE:
+   int - Positive for resulting size of pBuffer
+         Negative errno for error
+===========================================================================*/
+int QMICTLReleaseClientIDReq(
+   void *   pBuffer,
+   u16      buffSize,
+   u8       transactionID,
+   u16      clientID )
+{
+   if (pBuffer == 0 || buffSize < QMICTLReleaseClientIDReqSize() )
+   {
+      return -ENOMEM;
+   }
+
+   // QMI CTL RELEASE CLIENT ID REQ
+   // Request
+   *(u8 *)(pBuffer + sizeof( sQMUX ))  = 0x00;
+   // Transaction ID
+   *(u8 *)(pBuffer + sizeof( sQMUX ) + 1 ) = transactionID;
+   // Message ID
+   *(u16 *)(pBuffer + sizeof( sQMUX ) + 2) = 0x0023;
+   // Size of TLV's
+   *(u16 *)(pBuffer + sizeof( sQMUX ) + 4) = 0x0005;
+      // Release client ID
+      *(u8 *)(pBuffer + sizeof( sQMUX ) + 6)  = 0x01;
+      // Size
+      *(u16 *)(pBuffer + sizeof( sQMUX ) + 7) = 0x0002;
+      // QMI svs type / Client ID
+      *(u16 *)(pBuffer + sizeof( sQMUX ) + 9)  = clientID;
+      
+  // success
+  return sizeof( sQMUX ) + 11;
+}
+
+/*===========================================================================
+METHOD:
+   QMICTLReadyReq (Public Method)
+
+DESCRIPTION:
+   Fill buffer with QMI CTL Get Version Info Request
+
+PARAMETERS
+   pBuffer         [ 0 ] - Buffer to be filled
+   buffSize        [ I ] - Size of pBuffer
+   transactionID   [ I ] - Transaction ID
+
+RETURN VALUE:
+   int - Positive for resulting size of pBuffer
+         Negative errno for error
+===========================================================================*/
+int QMICTLReadyReq(
+   void *   pBuffer,
+   u16      buffSize,
+   u8       transactionID )
+{
+   if (pBuffer == 0 || buffSize < QMICTLReadyReqSize() )
+   {
+      return -ENOMEM;
+   }
+
+   // QMI CTL GET VERSION INFO REQ
+   // Request
+   *(u8 *)(pBuffer + sizeof( sQMUX ))  = 0x00;
+   // Transaction ID
+   *(u8 *)(pBuffer + sizeof( sQMUX ) + 1) = transactionID;
+   // Message ID
+   *(u16 *)(pBuffer + sizeof( sQMUX ) + 2) = 0x0021;
+   // Size of TLV's
+   *(u16 *)(pBuffer + sizeof( sQMUX ) + 4) = 0x0000;
+
+  // success
+  return sizeof( sQMUX ) + 6;
+}
+
+/*===========================================================================
+METHOD:
+   QMIWDSSetEventReportReq (Public Method)
+
+DESCRIPTION:
+   Fill buffer with QMI WDS Set Event Report Request
+
+PARAMETERS
+   pBuffer         [ 0 ] - Buffer to be filled
+   buffSize        [ I ] - Size of pBuffer
+   transactionID   [ I ] - Transaction ID
+
+RETURN VALUE:
+   int - Positive for resulting size of pBuffer
+         Negative errno for error
+===========================================================================*/
+int QMIWDSSetEventReportReq(
+   void *   pBuffer,
+   u16      buffSize,
+   u16      transactionID )
+{
+   if (pBuffer == 0 || buffSize < QMIWDSSetEventReportReqSize() )
+   {
+      return -ENOMEM;
+   }
+
+   // QMI WDS SET EVENT REPORT REQ
+   // Request
+   *(u8 *)(pBuffer + sizeof( sQMUX ))  = 0x00;
+   // Transaction ID
+   *(u16 *)(pBuffer + sizeof( sQMUX ) + 1) = transactionID;
+   // Message ID
+   *(u16 *)(pBuffer + sizeof( sQMUX ) + 3) = 0x0001;
+   // Size of TLV's
+   *(u16 *)(pBuffer + sizeof( sQMUX ) + 5) = 0x0008;
+      // Report channel rate TLV
+      *(u8 *)(pBuffer + sizeof( sQMUX ) + 7)  = 0x11;
+      // Size
+      *(u16 *)(pBuffer + sizeof( sQMUX ) + 8) = 0x0005;
+      // Stats period
+      *(u8 *)(pBuffer + sizeof( sQMUX ) + 10)  = 0x01;
+      // Stats mask
+      *(u32 *)(pBuffer + sizeof( sQMUX ) + 11)  = 0x000000ff;
+
+  // success
+  return sizeof( sQMUX ) + 15;
+}
+
+/*===========================================================================
+METHOD:
+   QMIWDSGetPKGSRVCStatusReq (Public Method)
+
+DESCRIPTION:
+   Fill buffer with QMI WDS Get PKG SRVC Status Request
+
+PARAMETERS
+   pBuffer         [ 0 ] - Buffer to be filled
+   buffSize        [ I ] - Size of pBuffer
+   transactionID   [ I ] - Transaction ID
+
+RETURN VALUE:
+   int - Positive for resulting size of pBuffer
+         Negative errno for error
+===========================================================================*/
+int QMIWDSGetPKGSRVCStatusReq(
+   void *   pBuffer,
+   u16      buffSize,
+   u16      transactionID )
+{
+   if (pBuffer == 0 || buffSize < QMIWDSGetPKGSRVCStatusReqSize() )
+   {
+      return -ENOMEM;
+   }
+
+   // QMI WDS Get PKG SRVC Status REQ
+   // Request
+   *(u8 *)(pBuffer + sizeof( sQMUX ))  = 0x00;
+   // Transaction ID
+   *(u16 *)(pBuffer + sizeof( sQMUX ) + 1) = transactionID;
+   // Message ID
+   *(u16 *)(pBuffer + sizeof( sQMUX ) + 3) = 0x0022;
+   // Size of TLV's
+   *(u16 *)(pBuffer + sizeof( sQMUX ) + 5) = 0x0000;
+
+  // success
+  return sizeof( sQMUX ) + 7;
+}
+
+/*===========================================================================
+METHOD:
+   QMIDMSGetMEIDReq (Public Method)
+
+DESCRIPTION:
+   Fill buffer with QMI DMS Get Serial Numbers Request
+
+PARAMETERS
+   pBuffer         [ 0 ] - Buffer to be filled
+   buffSize        [ I ] - Size of pBuffer
+   transactionID   [ I ] - Transaction ID
+
+RETURN VALUE:
+   int - Positive for resulting size of pBuffer
+         Negative errno for error
+===========================================================================*/
+int QMIDMSGetMEIDReq(
+   void *   pBuffer,
+   u16      buffSize,
+   u16      transactionID )
+{
+   if (pBuffer == 0 || buffSize < QMIDMSGetMEIDReqSize() )
+   {
+      return -ENOMEM;
+   }
+
+   // QMI DMS GET SERIAL NUMBERS REQ
+   // Request
+   *(u8 *)(pBuffer + sizeof( sQMUX ))  = 0x00;
+   // Transaction ID
+   *(u16 *)(pBuffer + sizeof( sQMUX ) + 1) = transactionID;
+   // Message ID
+   *(u16 *)(pBuffer + sizeof( sQMUX ) + 3) = 0x0025;
+   // Size of TLV's
+   *(u16 *)(pBuffer + sizeof( sQMUX ) + 5) = 0x0000;
+
+  // success
+  return sizeof( sQMUX ) + 7;
+}
+
+/*=========================================================================*/
+// Parse data from QMI responses
+/*=========================================================================*/
+
+/*===========================================================================
+METHOD:
+   QMICTLGetClientIDResp (Public Method)
+
+DESCRIPTION:
+   Parse the QMI CTL Get Client ID Resp
+
+PARAMETERS
+   pBuffer         [ I ] - Buffer to be parsed
+   buffSize        [ I ] - Size of pBuffer
+   pClientID       [ 0 ] - Recieved client ID
+
+RETURN VALUE:
+   int - 0 for success
+         Negative errno for error
+===========================================================================*/
+int QMICTLGetClientIDResp(
+   void * pBuffer,
+   u16    buffSize,
+   u16 *  pClientID )
+{
+   int result;
+   
+   // Ignore QMUX and SDU
+   //    QMI CTL SDU is 2 bytes, not 3
+   u8 offset = sizeof( sQMUX ) + 2;
+
+   if (pBuffer == 0 || buffSize < offset )
+   {
+      return -ENOMEM;
+   }
+
+   pBuffer = pBuffer + offset;
+   buffSize -= offset;
+
+   result = GetQMIMessageID( pBuffer, buffSize );
+   if (result != 0x22)
+   {
+      return -EFAULT;
+   }
+
+   result = ValidQMIMessage( pBuffer, buffSize );
+   if (result != 0)
+   {
+      return -EFAULT;
+   }
+
+   result = GetTLV( pBuffer, buffSize, 0x01, pClientID, 2 );
+   if (result != 2)
+   {
+      return -EFAULT;
+   }
+
+   return 0;
+}
+
+/*===========================================================================
+METHOD:
+   QMICTLReleaseClientIDResp (Public Method)
+
+DESCRIPTION:
+   Verify the QMI CTL Release Client ID Resp is valid
+
+PARAMETERS
+   pBuffer         [ I ] - Buffer to be parsed
+   buffSize        [ I ] - Size of pBuffer
+
+RETURN VALUE:
+   int - 0 for success
+         Negative errno for error
+===========================================================================*/
+int QMICTLReleaseClientIDResp(
+   void *   pBuffer,
+   u16      buffSize )
+{
+   int result;
+   
+   // Ignore QMUX and SDU
+   //    QMI CTL SDU is 2 bytes, not 3
+   u8 offset = sizeof( sQMUX ) + 2;
+
+   if (pBuffer == 0 || buffSize < offset )
+   {
+      return -ENOMEM;
+   }
+
+   pBuffer = pBuffer + offset;
+   buffSize -= offset;
+
+   result = GetQMIMessageID( pBuffer, buffSize );
+   if (result != 0x23)
+   {
+      return -EFAULT;
+   }
+
+   result = ValidQMIMessage( pBuffer, buffSize );
+   if (result != 0)
+   {
+      return -EFAULT;
+   }
+
+   return 0;
+}
+
+/*===========================================================================
+METHOD:
+   QMIWDSEventResp (Public Method)
+
+DESCRIPTION:
+   Parse the QMI WDS Set Event Report Resp/Indication or
+      QMI WDS Get PKG SRVC Status Resp/Indication
+
+   Return parameters will only be updated if value was received
+
+PARAMETERS
+   pBuffer         [ I ] - Buffer to be parsed
+   buffSize        [ I ] - Size of pBuffer
+   pTXOk           [ O ] - Number of transmitted packets without errors
+   pRXOk           [ O ] - Number of recieved packets without errors
+   pTXErr          [ O ] - Number of transmitted packets with framing errors
+   pRXErr          [ O ] - Number of recieved packets with framing errors
+   pTXOfl          [ O ] - Number of transmitted packets dropped due to overflow
+   pRXOfl          [ O ] - Number of recieved packets dropped due to overflow
+   pTXBytesOk      [ O ] - Number of transmitted bytes without errors
+   pRXBytesOk      [ O ] - Number of recieved bytes without errors
+   pbLinkState     [ 0 ] - Is the link active?
+   pbReconfigure   [ 0 ] - Must interface be reconfigured? (reset IP address)
+
+RETURN VALUE:
+   int - 0 for success
+         Negative errno for error
+===========================================================================*/
+int QMIWDSEventResp(
+   void *   pBuffer,
+   u16      buffSize,
+   u32 *    pTXOk,
+   u32 *    pRXOk,
+   u32 *    pTXErr,
+   u32 *    pRXErr,
+   u32 *    pTXOfl,
+   u32 *    pRXOfl,
+   u64 *    pTXBytesOk,
+   u64 *    pRXBytesOk,
+   bool *   pbLinkState,
+   bool *   pbReconfigure )
+{
+   int result;
+   u8 pktStatusRead[2];
+
+   // Ignore QMUX and SDU
+   u8 offset = sizeof( sQMUX ) + 3;
+
+   if (pBuffer == 0 
+   || buffSize < offset
+   || pTXOk == 0
+   || pRXOk == 0
+   || pTXErr == 0
+   || pRXErr == 0
+   || pTXOfl == 0
+   || pRXOfl == 0
+   || pTXBytesOk == 0
+   || pRXBytesOk == 0
+   || pbLinkState == 0
+   || pbReconfigure == 0 )
+   {
+      return -ENOMEM;
+   }
+
+   pBuffer = pBuffer + offset;
+   buffSize -= offset;
+
+   // Note: Indications.  No Mandatory TLV required
+
+   result = GetQMIMessageID( pBuffer, buffSize );
+   // QMI WDS Set Event Report Resp
+   if (result == 0x01)
+   {
+      // TLV's are not mandatory
+      GetTLV( pBuffer, buffSize, 0x10, (void*)pTXOk, 4 );
+      GetTLV( pBuffer, buffSize, 0x11, (void*)pRXOk, 4 );
+      GetTLV( pBuffer, buffSize, 0x12, (void*)pTXErr, 4 );
+      GetTLV( pBuffer, buffSize, 0x13, (void*)pRXErr, 4 );
+      GetTLV( pBuffer, buffSize, 0x14, (void*)pTXOfl, 4 );
+      GetTLV( pBuffer, buffSize, 0x15, (void*)pRXOfl, 4 );
+      GetTLV( pBuffer, buffSize, 0x19, (void*)pTXBytesOk, 8 );
+      GetTLV( pBuffer, buffSize, 0x1A, (void*)pRXBytesOk, 8 );
+   }
+   // QMI WDS Get PKG SRVC Status Resp
+   else if (result == 0x22)
+   {
+      result = GetTLV( pBuffer, buffSize, 0x01, &pktStatusRead[0], 2 );
+      // 1 or 2 bytes may be received
+      if (result >= 1)
+      {
+         if (pktStatusRead[0] == 0x02)
+         {
+            *pbLinkState = true;
+         }
+         else
+         {
+            *pbLinkState = false;
+         }
+      }
+      if (result == 2)
+      {
+         if (pktStatusRead[1] == 0x01)
+         {
+            *pbReconfigure = true;
+         }
+         else
+         {
+            *pbReconfigure = false;
+         }
+      }
+      
+      if (result < 0)
+      {
+         return result;
+      }
+   }
+   else
+   {
+      return -EFAULT;
+   }
+
+   return 0;
+}
+
+/*===========================================================================
+METHOD:
+   QMIDMSGetMEIDResp (Public Method)
+
+DESCRIPTION:
+   Parse the QMI DMS Get Serial Numbers Resp
+
+PARAMETERS
+   pBuffer         [ I ] - Buffer to be parsed
+   buffSize        [ I ] - Size of pBuffer
+   pMEID           [ O ] - Device MEID
+   meidSize        [ I ] - Size of MEID buffer (at least 14)
+
+RETURN VALUE:
+   int - 0 for success
+         Negative errno for error
+===========================================================================*/
+int QMIDMSGetMEIDResp(
+   void *   pBuffer,
+   u16      buffSize,
+   char *   pMEID,
+   int      meidSize )
+{
+   int result;
+
+   // Ignore QMUX and SDU
+   u8 offset = sizeof( sQMUX ) + 3;
+
+   if (pBuffer == 0 || buffSize < offset || meidSize < 14 )
+   {
+      return -ENOMEM;
+   }
+
+   pBuffer = pBuffer + offset;
+   buffSize -= offset;
+
+   result = GetQMIMessageID( pBuffer, buffSize );
+   if (result != 0x25)
+   {
+      return -EFAULT;
+   }
+
+   result = ValidQMIMessage( pBuffer, buffSize );
+   if (result != 0)
+   {
+      return -EFAULT;
+   }
+
+   result = GetTLV( pBuffer, buffSize, 0x12, (void*)pMEID, 14 );
+   if (result != 14)
+   {
+      return -EFAULT;
+   }
+
+   return 0;
+}
+
diff --git a/drivers/staging/gobi/QCUSBNet2k/QMI.h b/drivers/staging/gobi/QCUSBNet2k/QMI.h
new file mode 100644
index 0000000..4da1285
--- /dev/null
+++ b/drivers/staging/gobi/QCUSBNet2k/QMI.h
@@ -0,0 +1,251 @@
+/*===========================================================================
+FILE:
+   QMI.h
+
+DESCRIPTION:
+   Qualcomm QMI driver header
+   
+FUNCTIONS:
+   Generic QMUX functions
+      ParseQMUX
+      FillQMUX
+   
+   Generic QMI functions
+      GetTLV
+      ValidQMIMessage
+      GetQMIMessageID
+
+   Get sizes of buffers needed by QMI requests
+      QMUXHeaderSize
+      QMICTLGetClientIDReqSize
+      QMICTLReleaseClientIDReqSize
+      QMICTLReadyReqSize
+      QMIWDSSetEventReportReqSize
+      QMIWDSGetPKGSRVCStatusReqSize
+      QMIDMSGetMEIDReqSize
+
+   Fill Buffers with QMI requests
+      QMICTLGetClientIDReq
+      QMICTLReleaseClientIDReq
+      QMICTLReadyReq
+      QMIWDSSetEventReportReq
+      QMIWDSGetPKGSRVCStatusReq
+      QMIDMSGetMEIDReq
+      
+   Parse data from QMI responses
+      QMICTLGetClientIDResp
+      QMICTLReleaseClientIDResp
+      QMIWDSEventResp
+      QMIDMSGetMEIDResp
+
+Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 2 and
+only version 2 as published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+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 Street, Fifth Floor, Boston, MA
+02110-1301, USA.
+
+===========================================================================*/
+
+#pragma once
+
+/*=========================================================================*/
+// Definitions
+/*=========================================================================*/
+
+// QMI Service Types
+#define QMICTL 0
+#define QMIWDS 1
+#define QMIDMS 2
+
+#define u8        unsigned char
+#define u16       unsigned short
+#define u32       unsigned int
+#define u64       unsigned long long
+
+#define bool      u8
+#define true      1
+#define false     0
+
+#define ENOMEM    12
+#define EFAULT    14
+#define EINVAL    22
+#define ENOMSG    42
+#define ENODATA   61
+
+/*=========================================================================*/
+// Struct sQMUX
+//
+//    Structure that defines a QMUX header
+/*=========================================================================*/
+typedef struct sQMUX
+{
+   /* T\F, always 1 */
+   u8         mTF;
+
+   /* Size of message */
+   u16        mLength;
+
+   /* Control flag */
+   u8         mCtrlFlag;
+   
+   /* Service Type */
+   u8         mQMIService;
+   
+   /* Client ID */
+   u8         mQMIClientID;
+
+}__attribute__((__packed__)) sQMUX;
+
+/*=========================================================================*/
+// Generic QMUX functions
+/*=========================================================================*/
+
+// Remove QMUX headers from a buffer
+int ParseQMUX(
+   u16 *    pClientID,
+   void *   pBuffer,
+   u16      buffSize );
+
+// Fill buffer with QMUX headers
+int FillQMUX(
+   u16      clientID,
+   void *   pBuffer,
+   u16      buffSize );
+
+/*=========================================================================*/
+// Generic QMI functions
+/*=========================================================================*/
+
+// Get data bufffer of a specified TLV from a QMI message
+u16 GetTLV(
+   void *   pQMIMessage,
+   u16      messageLen,
+   u8       type,
+   void *   pOutDataBuf,
+   u16      bufferLen );
+
+// Check mandatory TLV in a QMI message
+int ValidQMIMessage(
+   void *   pQMIMessage,
+   u16      messageLen );
+
+// Get the message ID of a QMI message
+int GetQMIMessageID(
+   void *   pQMIMessage,
+   u16      messageLen );
+
+/*=========================================================================*/
+// Get sizes of buffers needed by QMI requests
+/*=========================================================================*/
+
+// Get size of buffer needed for QMUX
+u16 QMUXHeaderSize( void );
+
+// Get size of buffer needed for QMUX + QMICTLGetClientIDReq
+u16 QMICTLGetClientIDReqSize( void );
+
+// Get size of buffer needed for QMUX + QMICTLReleaseClientIDReq
+u16 QMICTLReleaseClientIDReqSize( void );
+
+// Get size of buffer needed for QMUX + QMICTLReadyReq
+u16 QMICTLReadyReqSize( void );
+
+// Get size of buffer needed for QMUX + QMIWDSSetEventReportReq
+u16 QMIWDSSetEventReportReqSize( void );
+
+// Get size of buffer needed for QMUX + QMIWDSGetPKGSRVCStatusReq
+u16 QMIWDSGetPKGSRVCStatusReqSize( void );
+
+// Get size of buffer needed for QMUX + QMIDMSGetMEIDReq
+u16 QMIDMSGetMEIDReqSize( void );
+
+/*=========================================================================*/
+// Fill Buffers with QMI requests
+/*=========================================================================*/
+
+// Fill buffer with QMI CTL Get Client ID Request
+int QMICTLGetClientIDReq(
+   void *   pBuffer,
+   u16      buffSize,
+   u8       transactionID,
+   u8       serviceType );
+
+// Fill buffer with QMI CTL Release Client ID Request
+int QMICTLReleaseClientIDReq(
+   void *   pBuffer,
+   u16      buffSize,
+   u8       transactionID,
+   u16      clientID );
+
+// Fill buffer with QMI CTL Get Version Info Request
+int QMICTLReadyReq(
+   void *   pBuffer,
+   u16      buffSize,
+   u8       transactionID );
+
+// Fill buffer with QMI WDS Set Event Report Request
+int QMIWDSSetEventReportReq(
+   void *   pBuffer,
+   u16      buffSize,
+   u16      transactionID );
+
+// Fill buffer with QMI WDS Get PKG SRVC Status Request
+int QMIWDSGetPKGSRVCStatusReq(
+   void *   pBuffer,
+   u16      buffSize,
+   u16      transactionID );
+
+// Fill buffer with QMI DMS Get Serial Numbers Request
+int QMIDMSGetMEIDReq(
+   void *   pBuffer,
+   u16      buffSize,
+   u16      transactionID );
+
+/*=========================================================================*/
+// Parse data from QMI responses
+/*=========================================================================*/
+
+// Parse the QMI CTL Get Client ID Resp
+int QMICTLGetClientIDResp(
+   void * pBuffer,
+   u16    buffSize,
+   u16 *  pClientID );
+
+// Verify the QMI CTL Release Client ID Resp is valid
+int QMICTLReleaseClientIDResp(
+   void *   pBuffer,
+   u16      buffSize );
+
+// Parse the QMI WDS Set Event Report Resp/Indication or
+//    QMI WDS Get PKG SRVC Status Resp/Indication
+int QMIWDSEventResp(
+   void *   pBuffer,
+   u16      buffSize,
+   u32 *    pTXOk,
+   u32 *    pRXOk,
+   u32 *    pTXErr,
+   u32 *    pRXErr,
+   u32 *    pTXOfl,
+   u32 *    pRXOfl,
+   u64 *    pTXBytesOk,
+   u64 *    pRXBytesOk,
+   bool *   pbLinkState,
+   bool *   pbReconfigure );
+
+// Parse the QMI DMS Get Serial Numbers Resp
+int QMIDMSGetMEIDResp(
+   void *   pBuffer,
+   u16      buffSize,
+   char *   pMEID,
+   int      meidSize );
+
diff --git a/drivers/staging/gobi/QCUSBNet2k/QMIDevice.c b/drivers/staging/gobi/QCUSBNet2k/QMIDevice.c
new file mode 100644
index 0000000..668328c
--- /dev/null
+++ b/drivers/staging/gobi/QCUSBNet2k/QMIDevice.c
@@ -0,0 +1,3129 @@
+/*===========================================================================
+FILE:
+   QMIDevice.c
+
+DESCRIPTION:
+   Functions related to the QMI interface device
+   
+FUNCTIONS:
+   Generic functions
+      IsDeviceValid
+      PrintHex
+      QSetDownReason
+      QClearDownReason
+      QTestDownReason
+
+   Driver level asynchronous read functions
+      ReadCallback
+      IntCallback
+      StartRead
+      KillRead
+
+   Internal read/write functions
+      ReadAsync
+      UpSem
+      ReadSync
+      WriteSyncCallback
+      WriteSync
+
+   Internal memory management functions
+      GetClientID
+      ReleaseClientID
+      FindClientMem
+      AddToReadMemList
+      PopFromReadMemList
+      AddToNotifyList
+      NotifyAndPopNotifyList
+      AddToURBList
+      PopFromURBList
+
+   Userspace wrappers
+      UserspaceOpen
+      UserspaceIOCTL
+      UserspaceClose
+      UserspaceRead
+      UserspaceWrite
+
+   Initializer and destructor
+      RegisterQMIDevice
+      DeregisterQMIDevice
+
+   Driver level client management
+      QMIReady
+      QMIWDSCallback
+      SetupQMIWDSCallback
+      QMIDMSGetMEID
+
+Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 2 and
+only version 2 as published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+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 Street, Fifth Floor, Boston, MA
+02110-1301, USA.
+
+===========================================================================*/
+
+//---------------------------------------------------------------------------
+// Include Files
+//---------------------------------------------------------------------------
+#include "QMIDevice.h"
+
+//-----------------------------------------------------------------------------
+// Definitions
+//-----------------------------------------------------------------------------
+
+extern int debug;
+
+// Prototype to QCSuspend function
+int QCSuspend( 
+   struct usb_interface *     pIntf,
+   pm_message_t               powerEvent );
+
+// IOCTL to generate a client ID for this service type
+#define IOCTL_QMI_GET_SERVICE_FILE 0x8BE0 + 1
+
+// IOCTL to get the VIDPID of the device
+#define IOCTL_QMI_GET_DEVICE_VIDPID 0x8BE0 + 2
+
+// IOCTL to get the MEID of the device
+#define IOCTL_QMI_GET_DEVICE_MEID 0x8BE0 + 3
+
+// CDC GET_ENCAPSULATED_RESPONSE packet
+#define CDC_GET_ENCAPSULATED_RESPONSE 0x01A1ll
+
+// CDC CONNECTION_SPEED_CHANGE indication packet
+#define CDC_CONNECTION_SPEED_CHANGE 0x08000000002AA1ll
+
+/*=========================================================================*/
+// UserspaceQMIFops
+//    QMI device's userspace file operations
+/*=========================================================================*/
+struct file_operations UserspaceQMIFops = 
+{
+   .owner     = THIS_MODULE,
+   .read      = UserspaceRead,
+   .write     = UserspaceWrite,
+   .ioctl     = UserspaceIOCTL,
+   .open      = UserspaceOpen,
+   .flush     = UserspaceClose,
+};
+
+/*=========================================================================*/
+// Generic functions
+/*=========================================================================*/
+
+/*===========================================================================
+METHOD:
+   IsDeviceValid (Public Method)
+
+DESCRIPTION:
+   Basic test to see if device memory is valid
+
+PARAMETERS:
+   pDev     [ I ] - Device specific memory
+
+RETURN VALUE:
+   bool
+===========================================================================*/
+bool IsDeviceValid( sQCUSBNet * pDev )
+{
+   if (pDev == NULL)
+   {
+      return false;
+   }
+
+   if (pDev->mbQMIValid == false)
+   {
+      return false;
+   }
+   
+   return true;
+} 
+
+/*===========================================================================
+METHOD:
+   PrintHex (Public Method)
+
+DESCRIPTION:
+   Print Hex data, for debug purposes
+
+PARAMETERS:
+   pBuffer       [ I ] - Data buffer
+   bufSize       [ I ] - Size of data buffer
+
+RETURN VALUE:
+   None
+===========================================================================*/
+void PrintHex(
+   void *      pBuffer,
+   u16         bufSize )
+{
+   char * pPrintBuf;
+   u16 pos;
+   int status;
+   
+   pPrintBuf = kmalloc( bufSize * 3 + 1, GFP_ATOMIC );
+   if (pPrintBuf == NULL)
+   {
+      DBG( "Unable to allocate buffer\n" );
+      return;
+   }
+   memset( pPrintBuf, 0 , bufSize * 3 + 1 );
+   
+   for (pos = 0; pos < bufSize; pos++)
+   {
+      status = snprintf( (pPrintBuf + (pos * 3)), 
+                         4, 
+                         "%02X ", 
+                         *(u8 *)(pBuffer + pos) );
+      if (status != 3)
+      {
+         DBG( "snprintf error %d\n", status );
+         return;
+      }
+   }
+   
+   DBG( "   : %s\n", pPrintBuf );
+
+   kfree( pPrintBuf );
+   pPrintBuf = NULL;
+   return;   
+}
+
+/*===========================================================================
+METHOD:
+   QSetDownReason (Public Method)
+
+DESCRIPTION:
+   Sets mDownReason and turns carrier off
+
+PARAMETERS
+   pDev     [ I ] - Device specific memory
+   reason   [ I ] - Reason device is down
+
+RETURN VALUE:
+   None
+===========================================================================*/
+void QSetDownReason(
+   sQCUSBNet *    pDev,
+   u8             reason )
+{
+   set_bit( reason, &pDev->mDownReason );
+   
+   netif_carrier_off( pDev->mpNetDev->net );
+}
+
+/*===========================================================================
+METHOD:
+   QClearDownReason (Public Method)
+
+DESCRIPTION:
+   Clear mDownReason and may turn carrier on
+
+PARAMETERS
+   pDev     [ I ] - Device specific memory
+   reason   [ I ] - Reason device is no longer down
+
+RETURN VALUE:
+   None
+===========================================================================*/
+void QClearDownReason(
+   sQCUSBNet *    pDev,
+   u8             reason )
+{
+   clear_bit( reason, &pDev->mDownReason );
+   
+   if (pDev->mDownReason == 0)
+   {
+      netif_carrier_on( pDev->mpNetDev->net );
+   }
+}
+
+/*===========================================================================
+METHOD:
+   QTestDownReason (Public Method)
+
+DESCRIPTION:
+   Test mDownReason and returns whether reason is set
+
+PARAMETERS
+   pDev     [ I ] - Device specific memory
+   reason   [ I ] - Reason device is down
+
+RETURN VALUE:
+   bool
+===========================================================================*/
+bool QTestDownReason(
+   sQCUSBNet *    pDev,
+   u8             reason )
+{
+   return test_bit( reason, &pDev->mDownReason );
+}
+
+/*=========================================================================*/
+// Driver level asynchronous read functions
+/*=========================================================================*/
+
+/*===========================================================================
+METHOD:
+   ReadCallback (Public Method)
+
+DESCRIPTION:
+   Put the data in storage and notify anyone waiting for data
+
+PARAMETERS
+   pReadURB       [ I ] - URB this callback is run for
+
+RETURN VALUE:
+   None
+===========================================================================*/
+void ReadCallback( struct urb * pReadURB )
+{
+   int result;
+   u16 clientID;
+   sClientMemList * pClientMem;
+   void * pData;
+   void * pDataCopy;
+   u16 dataSize;
+   sQCUSBNet * pDev;
+   unsigned long flags;
+   u16 transactionID;
+
+   if (pReadURB == NULL)
+   {
+      DBG( "bad read URB\n" );
+      return;
+   }
+   
+   pDev = pReadURB->context;
+   if (IsDeviceValid( pDev ) == false)
+   {
+      DBG( "Invalid device!\n" );
+      return;
+   }   
+
+   if (pReadURB->status != 0)
+   {
+      DBG( "Read status = %d\n", pReadURB->status );
+      return;
+   }
+   DBG( "Read %d bytes\n", pReadURB->actual_length );
+   
+   pData = pReadURB->transfer_buffer;
+   dataSize = pReadURB->actual_length;
+
+   PrintHex( pData, dataSize );
+
+   result = ParseQMUX( &clientID,
+                       pData,
+                       dataSize );
+   if (result < 0)
+   {
+      DBG( "Read error parsing QMUX %d\n", result );
+      return;
+   }
+   
+   // Grab transaction ID
+
+   // Data large enough?
+   if (dataSize < result + 3)
+   {
+      DBG( "Data buffer too small to parse\n" );
+      return;
+   }
+   
+   // Transaction ID size is 1 for QMICTL, 2 for others
+   if (clientID == QMICTL)
+   {
+      transactionID = *(u8*)(pData + result + 1);
+   }
+   else
+   {
+      transactionID = *(u16*)(pData + result + 1);
+   }
+   
+   // Critical section
+   spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags );
+
+   // Find memory storage for this service and Client ID
+   // Not using FindClientMem because it can't handle broadcasts
+   pClientMem = pDev->mQMIDev.mpClientMemList;
+   while (pClientMem != NULL)
+   {
+      if (pClientMem->mClientID == clientID 
+      ||  (pClientMem->mClientID | 0xff00) == clientID)
+      {
+         // Make copy of pData
+         pDataCopy = kmalloc( dataSize, GFP_ATOMIC );
+         memcpy( pDataCopy, pData, dataSize );
+
+         if (AddToReadMemList( pDev,
+                               pClientMem->mClientID,
+                               transactionID,
+                               pDataCopy,
+                               dataSize ) == false)
+         {
+            DBG( "Error allocating pReadMemListEntry "
+                 "read will be discarded\n" );
+            kfree( pDataCopy );
+            
+            // End critical section
+            spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
+            return;
+         }
+
+         // Success
+         DBG( "Creating new readListEntry for client 0x%04X, TID %x\n",
+              clientID,
+              transactionID );
+
+         // Notify this client data exists
+         NotifyAndPopNotifyList( pDev,
+                                 pClientMem->mClientID,
+                                 transactionID );
+
+         // Not a broadcast
+         if (clientID >> 8 != 0xff)
+         {
+            break;
+         }
+      }
+      
+      // Next element
+      pClientMem = pClientMem->mpNext;
+   }
+   
+   // End critical section
+   spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
+}
+
+/*===========================================================================
+METHOD:
+   IntCallback (Public Method)
+
+DESCRIPTION:
+   Data is available, fire off a read URB
+
+PARAMETERS
+   pIntURB       [ I ] - URB this callback is run for
+
+RETURN VALUE:
+   None
+===========================================================================*/
+void IntCallback( struct urb * pIntURB )
+{
+   int status;
+   int interval;
+   
+   sQCUSBNet * pDev = (sQCUSBNet *)pIntURB->context;
+   if (IsDeviceValid( pDev ) == false)
+   {
+      DBG( "Invalid device!\n" );
+      return;
+   }
+
+   // Verify this was a normal interrupt
+   if (pIntURB->status != 0)
+   {
+      DBG( "Int status = %d\n", pIntURB->status );
+      
+      // Ignore EOVERFLOW errors
+      if (pIntURB->status != -EOVERFLOW)
+      {
+         // Read 'thread' dies here
+         return;
+      }
+   }
+   else
+   {
+      // CDC GET_ENCAPSULATED_RESPONSE
+      if ((pIntURB->actual_length == 8)
+      &&  (*(u64*)pIntURB->transfer_buffer == CDC_GET_ENCAPSULATED_RESPONSE))
+      {
+         // Time to read
+         usb_fill_control_urb( pDev->mQMIDev.mpReadURB,
+                               pDev->mpNetDev->udev,
+                               usb_rcvctrlpipe( pDev->mpNetDev->udev, 0 ),
+                               (unsigned char *)pDev->mQMIDev.mpReadSetupPacket,
+                               pDev->mQMIDev.mpReadBuffer,
+                               DEFAULT_READ_URB_LENGTH,
+                               ReadCallback,
+                               pDev );
+         status = usb_submit_urb( pDev->mQMIDev.mpReadURB, GFP_ATOMIC );
+         if (status != 0)
+         {
+            DBG( "Error submitting Read URB %d\n", status );
+            return;
+         }
+      }
+      // CDC CONNECTION_SPEED_CHANGE
+      else if ((pIntURB->actual_length == 16)
+      &&       (*(u64*)pIntURB->transfer_buffer == CDC_CONNECTION_SPEED_CHANGE))
+      {
+         // if upstream or downstream is 0, stop traffic.  Otherwise resume it
+         if ((*(u32*)(pIntURB->transfer_buffer + 8) == 0)
+         ||  (*(u32*)(pIntURB->transfer_buffer + 12) == 0))
+         {
+            QSetDownReason( pDev, CDC_CONNECTION_SPEED );
+            DBG( "traffic stopping due to CONNECTION_SPEED_CHANGE\n" );
+         }
+         else
+         {
+            QClearDownReason( pDev, CDC_CONNECTION_SPEED );
+            DBG( "resuming traffic due to CONNECTION_SPEED_CHANGE\n" );
+         }
+      }
+      else
+      {
+         DBG( "ignoring invalid interrupt in packet\n" );
+         PrintHex( pIntURB->transfer_buffer, pIntURB->actual_length );
+      }
+   }
+
+   interval = (pDev->mpNetDev->udev->speed == USB_SPEED_HIGH) ? 7 : 3;
+
+   // Reschedule interrupt URB
+   usb_fill_int_urb( pIntURB,
+                     pIntURB->dev,
+                     pIntURB->pipe,
+                     pIntURB->transfer_buffer,
+                     pIntURB->transfer_buffer_length,
+                     pIntURB->complete,
+                     pIntURB->context,
+                     interval );
+   status = usb_submit_urb( pIntURB, GFP_ATOMIC );
+   if (status != 0)
+   {
+      DBG( "Error re-submitting Int URB %d\n", status );
+   }   
+   return;
+}
+
+/*===========================================================================
+METHOD:
+   StartRead (Public Method)
+
+DESCRIPTION:
+   Start continuous read "thread" (callback driven)
+   
+PARAMETERS:
+   pDev     [ I ] - Device specific memory
+
+RETURN VALUE:
+   int - 0 for success
+         negative errno for failure
+===========================================================================*/
+int StartRead( sQCUSBNet * pDev )
+{
+   int interval;
+
+   if (IsDeviceValid( pDev ) == false)
+   {
+      DBG( "Invalid device!\n" );
+      return -ENXIO;
+   }
+   
+   // Allocate URB buffers
+   pDev->mQMIDev.mpReadURB = usb_alloc_urb( 0, GFP_KERNEL );
+   if (pDev->mQMIDev.mpReadURB == NULL)
+   {
+      DBG( "Error allocating read urb\n" );
+      return -ENOMEM;
+   }
+   
+   pDev->mQMIDev.mpIntURB = usb_alloc_urb( 0, GFP_KERNEL );
+   if (pDev->mQMIDev.mpIntURB == NULL)
+   {
+      DBG( "Error allocating int urb\n" );
+      return -ENOMEM;
+   }
+
+   // Create data buffers
+   pDev->mQMIDev.mpReadBuffer = kmalloc( DEFAULT_READ_URB_LENGTH, GFP_KERNEL );
+   if (pDev->mQMIDev.mpReadBuffer == NULL)
+   {
+      DBG( "Error allocating read buffer\n" );
+      return -ENOMEM;
+   }
+   
+   pDev->mQMIDev.mpIntBuffer = kmalloc( DEFAULT_READ_URB_LENGTH, GFP_KERNEL );
+   if (pDev->mQMIDev.mpIntBuffer == NULL)
+   {
+      DBG( "Error allocating int buffer\n" );
+      return -ENOMEM;
+   }      
+   
+   pDev->mQMIDev.mpReadSetupPacket = kmalloc( sizeof( sURBSetupPacket ), 
+                                              GFP_KERNEL );
+   if (pDev->mQMIDev.mpReadSetupPacket == NULL)
+   {
+      DBG( "Error allocating setup packet buffer\n" );
+      return -ENOMEM;
+   }
+
+   // CDC Get Encapsulated Response packet
+   pDev->mQMIDev.mpReadSetupPacket->mRequestType = 0xA1;
+   pDev->mQMIDev.mpReadSetupPacket->mRequestCode = 1;
+   pDev->mQMIDev.mpReadSetupPacket->mValue = 0;
+   pDev->mQMIDev.mpReadSetupPacket->mIndex = 0;
+   pDev->mQMIDev.mpReadSetupPacket->mLength = DEFAULT_READ_URB_LENGTH;
+
+   interval = (pDev->mpNetDev->udev->speed == USB_SPEED_HIGH) ? 7 : 3;
+   
+   // Schedule interrupt URB
+   usb_fill_int_urb( pDev->mQMIDev.mpIntURB,
+                     pDev->mpNetDev->udev,
+                     usb_rcvintpipe( pDev->mpNetDev->udev, 0x81 ),
+                     pDev->mQMIDev.mpIntBuffer,
+                     DEFAULT_READ_URB_LENGTH,
+                     IntCallback,
+                     pDev,
+                     interval );
+   return usb_submit_urb( pDev->mQMIDev.mpIntURB, GFP_KERNEL );
+}
+
+/*===========================================================================
+METHOD:
+   KillRead (Public Method)
+
+DESCRIPTION:
+   Kill continuous read "thread"
+   
+PARAMETERS:
+   pDev     [ I ] - Device specific memory
+
+RETURN VALUE:
+   None
+===========================================================================*/
+void KillRead( sQCUSBNet * pDev )
+{
+   // Stop reading
+   if (pDev->mQMIDev.mpReadURB != NULL)
+   {
+      DBG( "Killng read URB\n" );
+      usb_kill_urb( pDev->mQMIDev.mpReadURB );
+   }
+
+   if (pDev->mQMIDev.mpIntURB != NULL)
+   {
+      DBG( "Killng int URB\n" );
+      usb_kill_urb( pDev->mQMIDev.mpIntURB );
+   }
+
+   // Release buffers
+   kfree( pDev->mQMIDev.mpReadSetupPacket );
+   pDev->mQMIDev.mpReadSetupPacket = NULL;
+   kfree( pDev->mQMIDev.mpReadBuffer );
+   pDev->mQMIDev.mpReadBuffer = NULL;
+   kfree( pDev->mQMIDev.mpIntBuffer );
+   pDev->mQMIDev.mpIntBuffer = NULL;
+   
+   // Release URB's
+   usb_free_urb( pDev->mQMIDev.mpReadURB );
+   pDev->mQMIDev.mpReadURB = NULL;
+   usb_free_urb( pDev->mQMIDev.mpIntURB );
+   pDev->mQMIDev.mpIntURB = NULL;
+}
+
+/*=========================================================================*/
+// Internal read/write functions
+/*=========================================================================*/
+
+/*===========================================================================
+METHOD:
+   ReadAsync (Public Method)
+
+DESCRIPTION:
+   Start asynchronous read
+   NOTE: Reading client's data store, not device
+
+PARAMETERS:
+   pDev              [ I ] - Device specific memory
+   clientID          [ I ] - Requester's client ID
+   transactionID     [ I ] - Transaction ID or 0 for any
+   pCallback         [ I ] - Callback to be executed when data is available
+   pData             [ I ] - Data buffer that willl be passed (unmodified) 
+                             to callback
+
+RETURN VALUE:
+   int - 0 for success
+         negative errno for failure
+===========================================================================*/
+int ReadAsync(
+   sQCUSBNet *    pDev,
+   u16            clientID,
+   u16            transactionID,
+   void           (*pCallback)(sQCUSBNet*, u16, void *),
+   void *         pData )
+{
+   sClientMemList * pClientMem;
+   sReadMemList ** ppReadMemList;
+   
+   unsigned long flags;
+
+   if (IsDeviceValid( pDev ) == false)
+   {
+      DBG( "Invalid device!\n" );
+      return -ENXIO;
+   }
+
+   // Critical section
+   spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags );
+
+   // Find memory storage for this client ID
+   pClientMem = FindClientMem( pDev, clientID );
+   if (pClientMem == NULL)
+   {
+      DBG( "Could not find matching client ID 0x%04X\n",
+           clientID );
+           
+      // End critical section
+      spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
+      return -ENXIO;
+   }
+   
+   ppReadMemList = &(pClientMem->mpList);
+   
+   // Does data already exist?
+   while (*ppReadMemList != NULL)
+   {
+      // Is this element our data?
+      if (transactionID == 0 
+      ||  transactionID == (*ppReadMemList)->mTransactionID)
+      {
+         // End critical section
+         spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
+
+         // Run our own callback
+         pCallback( pDev, clientID, pData );
+         
+         return 0;
+      }
+      
+      // Next
+      ppReadMemList = &(*ppReadMemList)->mpNext;
+   }
+
+   // Data not found, add ourself to list of waiters
+   if (AddToNotifyList( pDev,
+                        clientID,
+                        transactionID, 
+                        pCallback, 
+                        pData ) == false)
+   {
+      DBG( "Unable to register for notification\n" );
+   }
+
+   // End critical section
+   spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
+
+   // Success
+   return 0;
+}
+
+/*===========================================================================
+METHOD:
+   UpSem (Public Method)
+
+DESCRIPTION:
+   Notification function for synchronous read
+
+PARAMETERS:
+   pDev              [ I ] - Device specific memory
+   clientID          [ I ] - Requester's client ID
+   pData             [ I ] - Buffer that holds semaphore to be up()-ed
+
+RETURN VALUE:
+   None
+===========================================================================*/
+void UpSem( 
+   sQCUSBNet * pDev,
+   u16         clientID,
+   void *      pData )
+{
+   DBG( "0x%04X\n", clientID );
+        
+   up( (struct semaphore *)pData );
+   return;
+}
+
+/*===========================================================================
+METHOD:
+   ReadSync (Public Method)
+
+DESCRIPTION:
+   Start synchronous read
+   NOTE: Reading client's data store, not device
+
+PARAMETERS:
+   pDev              [ I ] - Device specific memory
+   ppOutBuffer       [I/O] - On success, will be filled with a 
+                             pointer to read buffer
+   clientID          [ I ] - Requester's client ID
+   transactionID     [ I ] - Transaction ID or 0 for any
+
+RETURN VALUE:
+   int - size of data read for success
+         negative errno for failure
+===========================================================================*/
+int ReadSync(
+   sQCUSBNet *    pDev,
+   void **        ppOutBuffer,
+   u16            clientID,
+   u16            transactionID )
+{
+   int result;
+   sClientMemList * pClientMem;
+   sNotifyList ** ppNotifyList, * pDelNotifyListEntry;
+   struct semaphore readSem;
+   void * pData;
+   unsigned long flags;
+   u16 dataSize;
+
+   if (IsDeviceValid( pDev ) == false)
+   {
+      DBG( "Invalid device!\n" );
+      return -ENXIO;
+   }
+   
+   // Critical section
+   spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags );
+
+   // Find memory storage for this Client ID
+   pClientMem = FindClientMem( pDev, clientID );
+   if (pClientMem == NULL)
+   {
+      DBG( "Could not find matching client ID 0x%04X\n",
+           clientID );
+      
+      // End critical section
+      spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
+      return -ENXIO;
+   }
+   
+   // Note: in cases where read is interrupted, 
+   //    this will verify client is still valid
+   while (PopFromReadMemList( pDev,
+                              clientID,
+                              transactionID,
+                              &pData,
+                              &dataSize ) == false)
+   {
+      // Data does not yet exist, wait
+      sema_init( &readSem, 0 );
+
+      // Add ourself to list of waiters
+      if (AddToNotifyList( pDev, 
+                           clientID, 
+                           transactionID, 
+                           UpSem, 
+                           &readSem ) == false)
+      {
+         DBG( "unable to register for notification\n" );
+         spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
+         return -EFAULT;
+      }
+
+      // End critical section while we block
+      spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
+
+      // Wait for notification
+      result = down_interruptible( &readSem );
+      if (result != 0)
+      {
+         DBG( "Interrupted %d\n", result );
+
+         // readSem will fall out of scope, 
+         // remove from notify list so it's not referenced
+         spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags );
+         ppNotifyList = &(pClientMem->mpReadNotifyList);
+         pDelNotifyListEntry = NULL;
+
+         // Find and delete matching entry
+         while (*ppNotifyList != NULL)
+         {
+            if ((*ppNotifyList)->mpData == &readSem)
+            {
+               pDelNotifyListEntry = *ppNotifyList;
+               *ppNotifyList = (*ppNotifyList)->mpNext;
+               kfree( pDelNotifyListEntry );
+               break;
+            }
+
+            // Next
+            ppNotifyList = &(*ppNotifyList)->mpNext;
+         }
+
+         spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
+         return -EINTR;
+      }
+      
+      // Verify device is still valid
+      if (IsDeviceValid( pDev ) == false)
+      {
+         DBG( "Invalid device!\n" );
+         return -ENXIO;
+      }
+      
+      // Restart critical section and continue loop
+      spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags );
+   }
+   
+   // End Critical section
+   spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
+
+   // Success
+   *ppOutBuffer = pData;
+
+   return dataSize;
+}
+
+/*===========================================================================
+METHOD:
+   WriteSyncCallback (Public Method)
+
+DESCRIPTION:
+   Write callback
+
+PARAMETERS
+   pWriteURB       [ I ] - URB this callback is run for
+
+RETURN VALUE:
+   None
+===========================================================================*/
+void WriteSyncCallback( struct urb * pWriteURB )
+{
+   if (pWriteURB == NULL)
+   {
+      DBG( "null urb\n" );
+      return;
+   }
+
+   DBG( "Write status/size %d/%d\n", 
+        pWriteURB->status, 
+        pWriteURB->actual_length );
+
+   // Notify that write has completed by up()-ing semeaphore
+   up( (struct semaphore * )pWriteURB->context );
+   
+   return;
+}
+
+/*===========================================================================
+METHOD:
+   WriteSync (Public Method)
+
+DESCRIPTION:
+   Start synchronous write
+
+PARAMETERS:
+   pDev                 [ I ] - Device specific memory
+   pWriteBuffer         [ I ] - Data to be written
+   writeBufferSize      [ I ] - Size of data to be written
+   clientID             [ I ] - Client ID of requester
+
+RETURN VALUE:
+   int - write size (includes QMUX)
+         negative errno for failure
+===========================================================================*/
+int WriteSync(
+   sQCUSBNet *        pDev,
+   char *             pWriteBuffer,
+   int                writeBufferSize,
+   u16                clientID )
+{
+   int result;
+   struct semaphore writeSem;
+   struct urb * pWriteURB;
+   sURBSetupPacket writeSetup;
+   unsigned long flags;
+
+   if (IsDeviceValid( pDev ) == false)
+   {
+      DBG( "Invalid device!\n" );
+      return -ENXIO;
+   }
+
+   pWriteURB = usb_alloc_urb( 0, GFP_KERNEL );
+   if (pWriteURB == NULL)
+   {
+      DBG( "URB mem error\n" );
+      return -ENOMEM;
+   }
+
+   // Fill writeBuffer with QMUX
+   result = FillQMUX( clientID, pWriteBuffer, writeBufferSize );
+   if (result < 0)
+   {
+      usb_free_urb( pWriteURB );
+      return result;
+   }
+
+   // CDC Send Encapsulated Request packet
+   writeSetup.mRequestType = 0x21;
+   writeSetup.mRequestCode = 0;
+   writeSetup.mValue = 0;
+   writeSetup.mIndex = 0;
+   writeSetup.mLength = 0;
+   writeSetup.mLength = writeBufferSize;
+
+   // Create URB   
+   usb_fill_control_urb( pWriteURB,
+                         pDev->mpNetDev->udev,
+                         usb_sndctrlpipe( pDev->mpNetDev->udev, 0 ),
+                         (unsigned char *)&writeSetup,
+                         (void*)pWriteBuffer,
+                         writeBufferSize,
+                         NULL,
+                         pDev );
+
+   DBG( "Actual Write:\n" );
+   PrintHex( pWriteBuffer, writeBufferSize );
+
+   sema_init( &writeSem, 0 );
+   
+   pWriteURB->complete = WriteSyncCallback;
+   pWriteURB->context = &writeSem;
+   
+   // Wake device
+   result = usb_autopm_get_interface( pDev->mpIntf );
+   if (result < 0)
+   {
+      DBG( "unable to resume interface: %d\n", result );
+      
+      // Likely caused by device going from autosuspend -> full suspend
+      if (result == -EPERM)
+      {
+#if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,33 ))
+         pDev->mpNetDev->udev->auto_pm = 0;
+#endif
+         QCSuspend( pDev->mpIntf, PMSG_SUSPEND );
+      }
+
+      return result;
+   }
+
+   // Critical section
+   spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags );
+
+   if (AddToURBList( pDev, clientID, pWriteURB ) == false)
+   {
+      usb_free_urb( pWriteURB );
+
+      // End critical section
+      spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );   
+      usb_autopm_put_interface( pDev->mpIntf );
+      return -EINVAL;
+   }
+
+   result = usb_submit_urb( pWriteURB, GFP_KERNEL );
+   if (result < 0)
+   {
+      DBG( "submit URB error %d\n", result );
+      
+      // Get URB back so we can destroy it
+      if (PopFromURBList( pDev, clientID ) != pWriteURB)
+      {
+         // This shouldn't happen
+         DBG( "Didn't get write URB back\n" );
+      }
+
+      usb_free_urb( pWriteURB );
+
+      // End critical section
+      spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
+      usb_autopm_put_interface( pDev->mpIntf );
+      return result;
+   }
+   
+   // End critical section while we block
+   spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );   
+
+   // Wait for write to finish
+   result = down_interruptible( &writeSem );
+
+   // Verify device is still valid
+   if (IsDeviceValid( pDev ) == false)
+   {
+      DBG( "Invalid device!\n" );
+      return -ENXIO;
+   }
+
+   // Write is done, release device
+   usb_autopm_put_interface( pDev->mpIntf );
+
+   // Restart critical section
+   spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags );
+
+   // Get URB back so we can destroy it
+   if (PopFromURBList( pDev, clientID ) != pWriteURB)
+   {
+      // This shouldn't happen
+      DBG( "Didn't get write URB back\n" );
+   
+      // End critical section
+      spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );   
+      return -EINVAL;
+   }
+
+   // End critical section
+   spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );   
+
+   if (result == 0)
+   {
+      // Write is finished
+      if (pWriteURB->status == 0)
+      {
+         // Return number of bytes that were supposed to have been written,
+         //   not size of QMI request
+         result = writeBufferSize;
+      }
+      else
+      {
+         DBG( "bad status = %d\n", pWriteURB->status );
+         
+         // Return error value
+         result = pWriteURB->status;
+      }
+   }
+   else
+   {
+      // We have been forcibly interrupted
+      DBG( "Interrupted %d !!!\n", result );
+      DBG( "Device may be in bad state and need reset !!!\n" );
+
+      // URB has not finished
+      usb_kill_urb( pWriteURB );
+   }
+
+   usb_free_urb( pWriteURB );
+
+   return result;
+}
+
+/*=========================================================================*/
+// Internal memory management functions
+/*=========================================================================*/
+
+/*===========================================================================
+METHOD:
+   GetClientID (Public Method)
+
+DESCRIPTION:
+   Construct object/load file into memory
+
+PARAMETERS:
+   pDev           [ I ] - Device specific memory
+   serviceType    [ I ] - Desired QMI service type
+
+RETURN VALUE:
+   int - Client ID for success (positive)
+         Negative errno for error
+===========================================================================*/
+int GetClientID( 
+   sQCUSBNet *    pDev,
+   u8             serviceType )
+{
+   u16 clientID;
+   sClientMemList ** ppClientMem;
+   int result;
+   void * pWriteBuffer;
+   u16 writeBufferSize;
+   void * pReadBuffer;
+   u16 readBufferSize;
+   unsigned long flags;
+   u8 transactionID;
+   
+   if (IsDeviceValid( pDev ) == false)
+   {
+      DBG( "Invalid device!\n" );
+      return -ENXIO;
+   }
+
+   // Run QMI request to be asigned a Client ID
+   if (serviceType != 0)
+   {
+      writeBufferSize = QMICTLGetClientIDReqSize();
+      pWriteBuffer = kmalloc( writeBufferSize, GFP_KERNEL );
+      if (pWriteBuffer == NULL)
+      {
+         return -ENOMEM;
+      }
+
+      transactionID = atomic_add_return( 1, &pDev->mQMIDev.mQMICTLTransactionID );
+      if (transactionID == 0)
+      {
+         atomic_add_return( 1, &pDev->mQMIDev.mQMICTLTransactionID );
+      }
+      result = QMICTLGetClientIDReq( pWriteBuffer, 
+                                     writeBufferSize,
+                                     transactionID,
+                                     serviceType );
+      if (result < 0)
+      {
+         kfree( pWriteBuffer );
+         return result;
+      }
+      
+      result = WriteSync( pDev,
+                          pWriteBuffer,
+                          writeBufferSize,
+                          QMICTL );
+      kfree( pWriteBuffer );
+
+      if (result < 0)
+      {
+         return result;
+      }
+
+      result = ReadSync( pDev,
+                         &pReadBuffer,
+                         QMICTL,
+                         transactionID );
+      if (result < 0)
+      {
+         DBG( "bad read data %d\n", result );
+         return result;
+      }
+      readBufferSize = result;
+
+      result = QMICTLGetClientIDResp( pReadBuffer,
+                                      readBufferSize,
+                                      &clientID );
+      kfree( pReadBuffer );
+
+      if (result < 0)
+      {
+         return result;
+      }
+   }
+   else
+   {
+      // QMI CTL will always have client ID 0
+      clientID = 0;
+   }
+
+   // Critical section
+   spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags );
+
+   // Verify client is not already allocated
+   if (FindClientMem( pDev, clientID ) != NULL)
+   {
+      DBG( "Client memory already exists\n" );
+
+      // End Critical section
+      spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
+      return -ETOOMANYREFS;
+   }
+
+   // Go to last entry in client mem list
+   ppClientMem = &pDev->mQMIDev.mpClientMemList;
+   while (*ppClientMem != NULL)
+   {
+      ppClientMem = &(*ppClientMem)->mpNext;
+   }
+   
+   // Create locations for read to place data into
+   *ppClientMem = kmalloc( sizeof( sClientMemList ), GFP_ATOMIC );
+   if (*ppClientMem == NULL)
+   {
+      DBG( "Error allocating read list\n" );
+
+      // End critical section
+      spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
+      return -ENOMEM;
+   }
+      
+   (*ppClientMem)->mClientID = clientID;
+   (*ppClientMem)->mpList = NULL;
+   (*ppClientMem)->mpReadNotifyList = NULL;
+   (*ppClientMem)->mpURBList = NULL;
+   (*ppClientMem)->mpNext = NULL;
+
+
+   // End Critical section
+   spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
+   
+   return clientID;
+}
+
+/*===========================================================================
+METHOD:
+   ReleaseClientID (Public Method)
+
+DESCRIPTION:
+   Release QMI client and free memory
+
+PARAMETERS:
+   pDev           [ I ] - Device specific memory
+   clientID       [ I ] - Requester's client ID
+
+RETURN VALUE:
+   None
+===========================================================================*/
+void ReleaseClientID(
+   sQCUSBNet *    pDev,
+   u16            clientID )
+{
+   int result;
+   sClientMemList ** ppDelClientMem;
+   sClientMemList * pNextClientMem;
+   struct urb * pDelURB;
+   void * pDelData;
+   u16 dataSize;
+   void * pWriteBuffer;
+   u16 writeBufferSize;
+   void * pReadBuffer;
+   u16 readBufferSize;
+   unsigned long flags;
+   u8 transactionID;
+
+   // Is device is still valid?
+   if (IsDeviceValid( pDev ) == false)
+   {
+      DBG( "invalid device\n" );
+      return;
+   }
+   
+   DBG( "releasing 0x%04X\n", clientID );
+
+   // Run QMI ReleaseClientID if this isn't QMICTL   
+   if (clientID != QMICTL)
+   {
+      // Note: all errors are non fatal, as we always want to delete 
+      //    client memory in latter part of function
+      
+      writeBufferSize = QMICTLReleaseClientIDReqSize();
+      pWriteBuffer = kmalloc( writeBufferSize, GFP_KERNEL );
+      if (pWriteBuffer == NULL)
+      {
+         DBG( "memory error\n" );
+      }
+      else
+      {
+         transactionID = atomic_add_return( 1, &pDev->mQMIDev.mQMICTLTransactionID );
+         if (transactionID == 0)
+         {
+            transactionID = atomic_add_return( 1, &pDev->mQMIDev.mQMICTLTransactionID );
+         }
+         result = QMICTLReleaseClientIDReq( pWriteBuffer, 
+                                            writeBufferSize,
+                                            transactionID,
+                                            clientID );
+         if (result < 0)
+         {
+            kfree( pWriteBuffer );
+            DBG( "error %d filling req buffer\n", result );
+         }
+         else
+         {
+            result = WriteSync( pDev,
+                                pWriteBuffer,
+                                writeBufferSize,
+                                QMICTL );
+            kfree( pWriteBuffer );
+
+            if (result < 0)
+            {
+               DBG( "bad write status %d\n", result );
+            }
+            else
+            {
+               result = ReadSync( pDev,
+                                  &pReadBuffer,
+                                  QMICTL,
+                                  transactionID );
+               if (result < 0)
+               {
+                  DBG( "bad read status %d\n", result );
+               }
+               else
+               {
+                  readBufferSize = result;
+
+                  result = QMICTLReleaseClientIDResp( pReadBuffer,
+                                                      readBufferSize );
+                  kfree( pReadBuffer );
+
+                  if (result < 0)
+                  {
+                     DBG( "error %d parsing response\n", result );
+                  }
+               }
+            }
+         }
+      }
+   }
+
+   // Cleaning up client memory
+   
+   // Critical section
+   spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags );
+
+   // Can't use FindClientMem, I need to keep pointer of previous
+   ppDelClientMem = &pDev->mQMIDev.mpClientMemList;
+   while (*ppDelClientMem != NULL)
+   {
+      if ((*ppDelClientMem)->mClientID == clientID)
+      {
+         pNextClientMem = (*ppDelClientMem)->mpNext;
+
+         // Notify all clients
+         while (NotifyAndPopNotifyList( pDev,
+                                        clientID,
+                                        0 ) == true );         
+
+         // Kill and free all URB's
+         pDelURB = PopFromURBList( pDev, clientID );
+         while (pDelURB != NULL)
+         {
+            usb_kill_urb( pDelURB );
+            usb_free_urb( pDelURB );
+            pDelURB = PopFromURBList( pDev, clientID );
+         }
+
+         // Free any unread data
+         while (PopFromReadMemList( pDev, 
+                                    clientID,
+                                    0,
+                                    &pDelData,
+                                    &dataSize ) == true )
+         {
+            kfree( pDelData );
+         }
+
+         // Delete client Mem
+         kfree( *ppDelClientMem );
+
+         // Overwrite the pointer that was to this client mem
+         *ppDelClientMem = pNextClientMem;
+      }
+      else
+      {
+         // I now point to ( a pointer of ((the node I was at)'s mpNext))
+         ppDelClientMem = &(*ppDelClientMem)->mpNext;
+      }
+   }
+   
+   // End Critical section
+   spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
+
+   return;
+}
+
+/*===========================================================================
+METHOD:
+   FindClientMem (Public Method)
+
+DESCRIPTION:
+   Find this client's memory
+
+   Caller MUST have lock on mClientMemLock
+
+PARAMETERS:
+   pDev           [ I ] - Device specific memory
+   clientID       [ I ] - Requester's client ID
+
+RETURN VALUE:
+   sClientMemList - Pointer to requested sClientMemList for success
+                    NULL for error
+===========================================================================*/
+sClientMemList * FindClientMem( 
+   sQCUSBNet *      pDev,
+   u16              clientID )
+{
+   sClientMemList * pClientMem;
+   
+   if (IsDeviceValid( pDev ) == false)
+   {
+      DBG( "Invalid device\n" );
+      return NULL;
+   }
+   
+#ifdef CONFIG_SMP
+   // Verify Lock
+   if (spin_is_locked( &pDev->mQMIDev.mClientMemLock ) == 0)
+   {
+      DBG( "unlocked\n" );
+      BUG();
+   }
+#endif
+   
+   pClientMem = pDev->mQMIDev.mpClientMemList;
+   while (pClientMem != NULL)
+   {
+      if (pClientMem->mClientID == clientID)
+      {
+         // Success
+         //DBG( "Found client mem %p\n", pClientMem );
+         return pClientMem;
+      }
+      
+      pClientMem = pClientMem->mpNext;
+   }
+
+   DBG( "Could not find client mem 0x%04X\n", clientID );
+   return NULL;
+}
+
+/*===========================================================================
+METHOD:
+   AddToReadMemList (Public Method)
+
+DESCRIPTION:
+   Add Data to this client's ReadMem list
+   
+   Caller MUST have lock on mClientMemLock
+
+PARAMETERS:
+   pDev           [ I ] - Device specific memory
+   clientID       [ I ] - Requester's client ID
+   transactionID  [ I ] - Transaction ID or 0 for any
+   pData          [ I ] - Data to add
+   dataSize       [ I ] - Size of data to add
+
+RETURN VALUE:
+   bool
+===========================================================================*/
+bool AddToReadMemList( 
+   sQCUSBNet *      pDev,
+   u16              clientID,
+   u16              transactionID,
+   void *           pData,
+   u16              dataSize )
+{
+   sClientMemList * pClientMem;
+   sReadMemList ** ppThisReadMemList;
+
+#ifdef CONFIG_SMP
+   // Verify Lock
+   if (spin_is_locked( &pDev->mQMIDev.mClientMemLock ) == 0)
+   {
+      DBG( "unlocked\n" );
+      BUG();
+   }
+#endif
+
+   // Get this client's memory location
+   pClientMem = FindClientMem( pDev, clientID );
+   if (pClientMem == NULL)
+   {
+      DBG( "Could not find this client's memory 0x%04X\n",
+           clientID );
+
+      return false;
+   }
+
+   // Go to last ReadMemList entry
+   ppThisReadMemList = &pClientMem->mpList;
+   while (*ppThisReadMemList != NULL)
+   {
+      ppThisReadMemList = &(*ppThisReadMemList)->mpNext;
+   }
+   
+   *ppThisReadMemList = kmalloc( sizeof( sReadMemList ), GFP_ATOMIC );
+   if (*ppThisReadMemList == NULL)
+   {
+      DBG( "Mem error\n" );
+
+      return false;
+   }   
+   
+   (*ppThisReadMemList)->mpNext = NULL;
+   (*ppThisReadMemList)->mpData = pData;
+   (*ppThisReadMemList)->mDataSize = dataSize;
+   (*ppThisReadMemList)->mTransactionID = transactionID;
+   
+   return true;
+}
+
+/*===========================================================================
+METHOD:
+   PopFromReadMemList (Public Method)
+
+DESCRIPTION:
+   Remove data from this client's ReadMem list if it matches 
+   the specified transaction ID.
+   
+   Caller MUST have lock on mClientMemLock
+
+PARAMETERS:
+   pDev              [ I ] - Device specific memory
+   clientID          [ I ] - Requester's client ID
+   transactionID     [ I ] - Transaction ID or 0 for any
+   ppData            [I/O] - On success, will be filled with a 
+                             pointer to read buffer
+   pDataSize         [I/O] - On succces, will be filled with the 
+                             read buffer's size
+
+RETURN VALUE:
+   bool
+===========================================================================*/
+bool PopFromReadMemList( 
+   sQCUSBNet *      pDev,
+   u16              clientID,
+   u16              transactionID,
+   void **          ppData,
+   u16 *            pDataSize )
+{
+   sClientMemList * pClientMem;
+   sReadMemList * pDelReadMemList, ** ppReadMemList;
+
+#ifdef CONFIG_SMP
+   // Verify Lock
+   if (spin_is_locked( &pDev->mQMIDev.mClientMemLock ) == 0)
+   {
+      DBG( "unlocked\n" );
+      BUG();
+   }
+#endif
+
+   // Get this client's memory location
+   pClientMem = FindClientMem( pDev, clientID );
+   if (pClientMem == NULL)
+   {
+      DBG( "Could not find this client's memory 0x%04X\n",
+           clientID );
+
+      return false;
+   }
+   
+   ppReadMemList = &(pClientMem->mpList);
+   pDelReadMemList = NULL;
+   
+   // Find first message that matches this transaction ID
+   while (*ppReadMemList != NULL)
+   {
+      // Do we care about transaction ID?
+      if (transactionID == 0
+      ||  transactionID == (*ppReadMemList)->mTransactionID )
+      {
+         pDelReadMemList = *ppReadMemList;
+         break;
+      }
+      
+      DBG( "skipping 0x%04X data TID = %x\n", clientID, (*ppReadMemList)->mTransactionID );
+      
+      // Next
+      ppReadMemList = &(*ppReadMemList)->mpNext;
+   }
+   
+   if (pDelReadMemList != NULL)
+   {
+      *ppReadMemList = (*ppReadMemList)->mpNext;
+      
+      // Copy to output
+      *ppData = pDelReadMemList->mpData;
+      *pDataSize = pDelReadMemList->mDataSize;
+      
+      // Free memory
+      kfree( pDelReadMemList );
+      
+      return true;
+   }
+   else
+   {
+      DBG( "No read memory to pop, Client 0x%04X, TID = %x\n", 
+           clientID, 
+           transactionID );
+      return false;
+   }
+}
+
+/*===========================================================================
+METHOD:
+   AddToNotifyList (Public Method)
+
+DESCRIPTION:
+   Add Notify entry to this client's notify List
+   
+   Caller MUST have lock on mClientMemLock
+
+PARAMETERS:
+   pDev              [ I ] - Device specific memory
+   clientID          [ I ] - Requester's client ID
+   transactionID     [ I ] - Transaction ID or 0 for any
+   pNotifyFunct      [ I ] - Callback function to be run when data is available
+   pData             [ I ] - Data buffer that willl be passed (unmodified) 
+                             to callback
+
+RETURN VALUE:
+   bool
+===========================================================================*/
+bool AddToNotifyList( 
+   sQCUSBNet *      pDev,
+   u16              clientID,
+   u16              transactionID,
+   void             (* pNotifyFunct)(sQCUSBNet *, u16, void *),
+   void *           pData )
+{
+   sClientMemList * pClientMem;
+   sNotifyList ** ppThisNotifyList;
+
+#ifdef CONFIG_SMP
+   // Verify Lock
+   if (spin_is_locked( &pDev->mQMIDev.mClientMemLock ) == 0)
+   {
+      DBG( "unlocked\n" );
+      BUG();
+   }
+#endif
+
+   // Get this client's memory location
+   pClientMem = FindClientMem( pDev, clientID );
+   if (pClientMem == NULL)
+   {
+      DBG( "Could not find this client's memory 0x%04X\n", clientID );
+      return false;
+   }
+
+   // Go to last URBList entry
+   ppThisNotifyList = &pClientMem->mpReadNotifyList;
+   while (*ppThisNotifyList != NULL)
+   {
+      ppThisNotifyList = &(*ppThisNotifyList)->mpNext;
+   }
+   
+   *ppThisNotifyList = kmalloc( sizeof( sNotifyList ), GFP_ATOMIC );
+   if (*ppThisNotifyList == NULL)
+   {
+      DBG( "Mem error\n" );
+      return false;
+   }   
+   
+   (*ppThisNotifyList)->mpNext = NULL;
+   (*ppThisNotifyList)->mpNotifyFunct = pNotifyFunct;
+   (*ppThisNotifyList)->mpData = pData;
+   (*ppThisNotifyList)->mTransactionID = transactionID;
+   
+   return true;
+}
+
+/*===========================================================================
+METHOD:
+   NotifyAndPopNotifyList (Public Method)
+
+DESCRIPTION:
+   Remove first Notify entry from this client's notify list 
+   and Run function
+   
+   Caller MUST have lock on mClientMemLock
+
+PARAMETERS:
+   pDev              [ I ] - Device specific memory
+   clientID          [ I ] - Requester's client ID
+   transactionID     [ I ] - Transaction ID or 0 for any
+
+RETURN VALUE:
+   bool
+===========================================================================*/
+bool NotifyAndPopNotifyList( 
+   sQCUSBNet *      pDev,
+   u16              clientID,
+   u16              transactionID )
+{
+   sClientMemList * pClientMem;
+   sNotifyList * pDelNotifyList, ** ppNotifyList;
+
+#ifdef CONFIG_SMP
+   // Verify Lock
+   if (spin_is_locked( &pDev->mQMIDev.mClientMemLock ) == 0)
+   {
+      DBG( "unlocked\n" );
+      BUG();
+   }
+#endif
+
+   // Get this client's memory location
+   pClientMem = FindClientMem( pDev, clientID );
+   if (pClientMem == NULL)
+   {
+      DBG( "Could not find this client's memory 0x%04X\n", clientID );
+      return false;
+   }
+
+   ppNotifyList = &(pClientMem->mpReadNotifyList);
+   pDelNotifyList = NULL;
+
+   // Remove from list
+   while (*ppNotifyList != NULL)
+   {
+      // Do we care about transaction ID?
+      if (transactionID == 0
+      ||  (*ppNotifyList)->mTransactionID == 0
+      ||  transactionID == (*ppNotifyList)->mTransactionID)
+      {
+         pDelNotifyList = *ppNotifyList;
+         break;
+      }
+      
+      DBG( "skipping data TID = %x\n", (*ppNotifyList)->mTransactionID );
+      
+      // next
+      ppNotifyList = &(*ppNotifyList)->mpNext;
+   }
+   
+   if (pDelNotifyList != NULL)
+   {
+      // Remove element
+      *ppNotifyList = (*ppNotifyList)->mpNext;
+      
+      // Run notification function
+      if (pDelNotifyList->mpNotifyFunct != NULL)
+      {
+         // Unlock for callback
+         spin_unlock( &pDev->mQMIDev.mClientMemLock );
+      
+         pDelNotifyList->mpNotifyFunct( pDev,
+                                        clientID,
+                                        pDelNotifyList->mpData );
+                                        
+         // Restore lock
+         spin_lock( &pDev->mQMIDev.mClientMemLock );
+      }
+      
+      // Delete memory
+      kfree( pDelNotifyList );
+
+      return true;
+   }
+   else
+   {
+      DBG( "no one to notify for TID %x\n", transactionID );
+      
+      return false;
+   }
+}
+
+/*===========================================================================
+METHOD:
+   AddToURBList (Public Method)
+
+DESCRIPTION:
+   Add URB to this client's URB list
+   
+   Caller MUST have lock on mClientMemLock
+
+PARAMETERS:
+   pDev              [ I ] - Device specific memory
+   clientID          [ I ] - Requester's client ID
+   pURB              [ I ] - URB to be added
+
+RETURN VALUE:
+   bool
+===========================================================================*/
+bool AddToURBList( 
+   sQCUSBNet *      pDev,
+   u16              clientID,
+   struct urb *     pURB )
+{
+   sClientMemList * pClientMem;
+   sURBList ** ppThisURBList;
+
+#ifdef CONFIG_SMP
+   // Verify Lock
+   if (spin_is_locked( &pDev->mQMIDev.mClientMemLock ) == 0)
+   {
+      DBG( "unlocked\n" );
+      BUG();
+   }
+#endif
+
+   // Get this client's memory location
+   pClientMem = FindClientMem( pDev, clientID );
+   if (pClientMem == NULL)
+   {
+      DBG( "Could not find this client's memory 0x%04X\n", clientID );
+      return false;
+   }
+
+   // Go to last URBList entry
+   ppThisURBList = &pClientMem->mpURBList;
+   while (*ppThisURBList != NULL)
+   {
+      ppThisURBList = &(*ppThisURBList)->mpNext;
+   }
+   
+   *ppThisURBList = kmalloc( sizeof( sURBList ), GFP_ATOMIC );
+   if (*ppThisURBList == NULL)
+   {
+      DBG( "Mem error\n" );
+      return false;
+   }   
+   
+   (*ppThisURBList)->mpNext = NULL;
+   (*ppThisURBList)->mpURB = pURB;
+   
+   return true;
+}
+
+/*===========================================================================
+METHOD:
+   PopFromURBList (Public Method)
+
+DESCRIPTION:
+   Remove URB from this client's URB list
+   
+   Caller MUST have lock on mClientMemLock
+
+PARAMETERS:
+   pDev           [ I ] - Device specific memory
+   clientID       [ I ] - Requester's client ID
+
+RETURN VALUE:
+   struct urb - Pointer to requested client's URB
+                NULL for error
+===========================================================================*/
+struct urb * PopFromURBList( 
+   sQCUSBNet *      pDev,
+   u16              clientID )
+{
+   sClientMemList * pClientMem;
+   sURBList * pDelURBList;
+   struct urb * pURB;
+
+#ifdef CONFIG_SMP
+   // Verify Lock
+   if (spin_is_locked( &pDev->mQMIDev.mClientMemLock ) == 0)
+   {
+      DBG( "unlocked\n" );
+      BUG();
+   }
+#endif
+
+   // Get this client's memory location
+   pClientMem = FindClientMem( pDev, clientID );
+   if (pClientMem == NULL)
+   {
+      DBG( "Could not find this client's memory 0x%04X\n", clientID );
+      return NULL;
+   }
+
+   // Remove from list
+   if (pClientMem->mpURBList != NULL)
+   {
+      pDelURBList = pClientMem->mpURBList;
+      pClientMem->mpURBList = pClientMem->mpURBList->mpNext;
+      
+      // Copy to output
+      pURB = pDelURBList->mpURB;
+      
+      // Delete memory
+      kfree( pDelURBList );
+
+      return pURB;
+   }
+   else
+   {
+      DBG( "No URB's to pop\n" );
+      
+      return NULL;
+   }
+}
+
+/*=========================================================================*/
+// Userspace wrappers
+/*=========================================================================*/
+
+/*===========================================================================
+METHOD:
+   UserspaceOpen (Public Method)
+
+DESCRIPTION:
+   Userspace open
+      IOCTL must be called before reads or writes
+
+PARAMETERS
+   pInode       [ I ] - kernel file descriptor
+   pFilp        [ I ] - userspace file descriptor
+
+RETURN VALUE:
+   int - 0 for success
+         Negative errno for failure
+===========================================================================*/
+int UserspaceOpen( 
+   struct inode *         pInode, 
+   struct file *          pFilp )
+{
+   sQMIFilpStorage * pFilpData;
+   
+   // Optain device pointer from pInode
+   sQMIDev * pQMIDev = container_of( pInode->i_cdev,
+                                     sQMIDev,
+                                     mCdev );
+   sQCUSBNet * pDev = container_of( pQMIDev,
+                                    sQCUSBNet,
+                                    mQMIDev );                                    
+
+   if (IsDeviceValid( pDev ) == false)
+   {
+      DBG( "Invalid device\n" );
+      return -ENXIO;
+   }
+
+   // Setup data in pFilp->private_data
+   pFilp->private_data = kmalloc( sizeof( sQMIFilpStorage ), GFP_KERNEL );
+   if (pFilp->private_data == NULL)
+   {
+      DBG( "Mem error\n" );
+      return -ENOMEM;
+   }
+   
+   pFilpData = (sQMIFilpStorage *)pFilp->private_data;
+   pFilpData->mClientID = (u16)-1;
+   pFilpData->mpDev = pDev;
+   
+   return 0;
+}
+
+/*===========================================================================
+METHOD:
+   UserspaceIOCTL (Public Method)
+
+DESCRIPTION:
+   Userspace IOCTL functions
+
+PARAMETERS
+   pUnusedInode [ I ] - (unused) kernel file descriptor
+   pFilp        [ I ] - userspace file descriptor
+   cmd          [ I ] - IOCTL command
+   arg          [ I ] - IOCTL argument
+
+RETURN VALUE:
+   int - 0 for success
+         Negative errno for failure
+===========================================================================*/
+int UserspaceIOCTL( 
+   struct inode *    pUnusedInode, 
+   struct file *     pFilp,
+   unsigned int      cmd, 
+   unsigned long     arg )
+{
+   int result;
+   u32 devVIDPID;
+   
+   sQMIFilpStorage * pFilpData = (sQMIFilpStorage *)pFilp->private_data;
+
+   if (pFilpData == NULL)
+   {
+      DBG( "Bad file data\n" );
+      return -EBADF;
+   }
+   
+   if (IsDeviceValid( pFilpData->mpDev ) == false)
+   {
+      DBG( "Invalid device! Updating f_ops\n" );
+      pFilp->f_op = pFilp->f_dentry->d_inode->i_fop;
+      return -ENXIO;
+   }
+
+   switch (cmd)
+   {
+      case IOCTL_QMI_GET_SERVICE_FILE:
+      
+         DBG( "Setting up QMI for service %lu\n", arg );
+         if ((u8)arg == 0)
+         {
+            DBG( "Cannot use QMICTL from userspace\n" );
+            return -EINVAL;
+         }
+
+         // Connection is already setup
+         if (pFilpData->mClientID != (u16)-1)
+         {
+            DBG( "Close the current connection before opening a new one\n" );
+            return -EBADR;
+         }
+         
+         result = GetClientID( pFilpData->mpDev, (u8)arg );
+         if (result < 0)
+         {
+            return result;
+         }
+         pFilpData->mClientID = result;
+
+         return 0;
+         break;
+
+
+      case IOCTL_QMI_GET_DEVICE_VIDPID:
+         if (arg == 0)
+         {
+            DBG( "Bad VIDPID buffer\n" );
+            return -EINVAL;
+         }
+         
+         // Extra verification
+         if (pFilpData->mpDev->mpNetDev == 0)
+         {
+            DBG( "Bad mpNetDev\n" );
+            return -ENOMEM;
+         }
+         if (pFilpData->mpDev->mpNetDev->udev == 0)
+         {
+            DBG( "Bad udev\n" );
+            return -ENOMEM;
+         }
+
+         devVIDPID = ((le16_to_cpu( pFilpData->mpDev->mpNetDev->udev->descriptor.idVendor ) << 16)
+                     + le16_to_cpu( pFilpData->mpDev->mpNetDev->udev->descriptor.idProduct ) );
+
+         result = copy_to_user( (unsigned int *)arg, &devVIDPID, 4 );
+         if (result != 0)
+         {
+            DBG( "Copy to userspace failure\n" );
+         }
+
+         return result;
+                 
+         break;
+
+      case IOCTL_QMI_GET_DEVICE_MEID:
+         if (arg == 0)
+         {
+            DBG( "Bad MEID buffer\n" );
+            return -EINVAL;
+         }
+         
+         result = copy_to_user( (unsigned int *)arg, &pFilpData->mpDev->mMEID[0], 14 );
+         if (result != 0)
+         {
+            DBG( "copy to userspace failure\n" );
+         }
+
+         return result;
+                 
+         break;
+         
+      default:
+         return -EBADRQC;       
+   }
+}
+
+/*===========================================================================
+METHOD:
+   UserspaceClose (Public Method)
+
+DESCRIPTION:
+   Userspace close
+      Release client ID and free memory
+
+PARAMETERS
+   pFilp           [ I ] - userspace file descriptor
+   unusedFileTable [ I ] - (unused) file table
+
+RETURN VALUE:
+   int - 0 for success
+         Negative errno for failure
+===========================================================================*/
+int UserspaceClose(
+   struct file *       pFilp,
+   fl_owner_t          unusedFileTable )
+{
+   sQMIFilpStorage * pFilpData = (sQMIFilpStorage *)pFilp->private_data;
+   struct list_head * pTasks;
+   struct task_struct * pEachTask;
+   struct fdtable * pFDT;
+   int count = 0;
+   int used = 0;
+   unsigned long flags;
+
+   if (pFilpData == NULL)
+   {
+      DBG( "bad file data\n" );
+      return -EBADF;
+   }
+
+   // Fallthough.  If f_count == 1 no need to do more checks
+   if (atomic_read( &pFilp->f_count ) != 1)
+   {
+      // "group_leader" points to the main process' task, which resides in
+      // the global "tasks" list.
+      list_for_each( pTasks, &current->group_leader->tasks )
+      {
+         pEachTask = container_of( pTasks, struct task_struct, tasks );
+         if (pEachTask == NULL || pEachTask->files == NULL)
+         {
+            // Some tasks may not have files (e.g. Xsession)
+            continue;
+         }
+         spin_lock_irqsave( &pEachTask->files->file_lock, flags );
+         pFDT = files_fdtable( pEachTask->files );
+         for (count = 0; count < pFDT->max_fds; count++)
+         {
+            // Before this function was called, this file was removed
+            // from our task's file table so if we find it in a file
+            // table then it is being used by another task
+            if (pFDT->fd[count] == pFilp)
+            {
+               used++;
+               break;
+            }
+         }
+         spin_unlock_irqrestore( &pEachTask->files->file_lock, flags );
+      }
+      
+      if (used > 0)
+      {
+         DBG( "not closing, as this FD is open by %d other process\n", used );
+         return 0;
+      }
+   }
+
+   if (IsDeviceValid( pFilpData->mpDev ) == false)
+   {
+      DBG( "Invalid device! Updating f_ops\n" );
+      pFilp->f_op = pFilp->f_dentry->d_inode->i_fop;
+      return -ENXIO;
+   }
+   
+   DBG( "0x%04X\n", pFilpData->mClientID );
+   
+   // Disable pFilpData so they can't keep sending read or write 
+   //    should this function hang
+   // Note: memory pointer is still saved in pFilpData to be deleted later
+   pFilp->private_data = NULL;
+
+   if (pFilpData->mClientID != (u16)-1)
+   {
+      ReleaseClientID( pFilpData->mpDev,
+                       pFilpData->mClientID );
+   }
+      
+   kfree( pFilpData );
+   return 0;
+}
+
+/*===========================================================================
+METHOD:
+   UserspaceRead (Public Method)
+
+DESCRIPTION:
+   Userspace read (synchronous)
+
+PARAMETERS
+   pFilp           [ I ] - userspace file descriptor
+   pBuf            [ I ] - read buffer
+   size            [ I ] - size of read buffer
+   pUnusedFpos     [ I ] - (unused) file position
+
+RETURN VALUE:
+   ssize_t - Number of bytes read for success
+             Negative errno for failure
+===========================================================================*/
+ssize_t UserspaceRead( 
+   struct file *          pFilp,
+   char __user *          pBuf, 
+   size_t                 size,
+   loff_t *               pUnusedFpos )
+{
+   int result;
+   void * pReadData = NULL;
+   void * pSmallReadData;
+   sQMIFilpStorage * pFilpData = (sQMIFilpStorage *)pFilp->private_data;
+
+   if (pFilpData == NULL)
+   {
+      DBG( "Bad file data\n" );
+      return -EBADF;
+   }
+
+   if (IsDeviceValid( pFilpData->mpDev ) == false)
+   {
+      DBG( "Invalid device! Updating f_ops\n" );
+      pFilp->f_op = pFilp->f_dentry->d_inode->i_fop;
+      return -ENXIO;
+   }
+   
+   if (pFilpData->mClientID == (u16)-1)
+   {
+      DBG( "Client ID must be set before reading 0x%04X\n",
+           pFilpData->mClientID );
+      return -EBADR;
+   }
+   
+   // Perform synchronous read
+   result = ReadSync( pFilpData->mpDev,
+                      &pReadData,
+                      pFilpData->mClientID,
+                      0 );
+   if (result <= 0)
+   {
+      return result;
+   }
+   
+   // Discard QMUX header
+   result -= QMUXHeaderSize();
+   pSmallReadData = pReadData + QMUXHeaderSize();
+
+   if (result > size)
+   {
+      DBG( "Read data is too large for amount user has requested\n" );
+      kfree( pReadData );
+      return -EOVERFLOW;
+   }
+
+   if (copy_to_user( pBuf, pSmallReadData, result ) != 0)
+   {
+      DBG( "Error copying read data to user\n" );
+      result = -EFAULT;
+   }
+   
+   // Reader is responsible for freeing read buffer
+   kfree( pReadData );
+   
+   return result;
+}
+
+/*===========================================================================
+METHOD:
+   UserspaceWrite (Public Method)
+
+DESCRIPTION:
+   Userspace write (synchronous)
+
+PARAMETERS
+   pFilp           [ I ] - userspace file descriptor
+   pBuf            [ I ] - write buffer
+   size            [ I ] - size of write buffer
+   pUnusedFpos     [ I ] - (unused) file position
+
+RETURN VALUE:
+   ssize_t - Number of bytes read for success
+             Negative errno for failure
+===========================================================================*/
+ssize_t UserspaceWrite (
+   struct file *        pFilp, 
+   const char __user *  pBuf, 
+   size_t               size,
+   loff_t *             pUnusedFpos )
+{
+   int status;
+   void * pWriteBuffer;
+   sQMIFilpStorage * pFilpData = (sQMIFilpStorage *)pFilp->private_data;
+
+   if (pFilpData == NULL)
+   {
+      DBG( "Bad file data\n" );
+      return -EBADF;
+   }
+
+   if (IsDeviceValid( pFilpData->mpDev ) == false)
+   {
+      DBG( "Invalid device! Updating f_ops\n" );
+      pFilp->f_op = pFilp->f_dentry->d_inode->i_fop;
+      return -ENXIO;
+   }
+
+   if (pFilpData->mClientID == (u16)-1)
+   {
+      DBG( "Client ID must be set before writing 0x%04X\n",
+           pFilpData->mClientID );
+      return -EBADR;
+   }
+   
+   // Copy data from user to kernel space
+   pWriteBuffer = kmalloc( size + QMUXHeaderSize(), GFP_KERNEL );
+   if (pWriteBuffer == NULL)
+   {
+      return -ENOMEM;
+   }
+   status = copy_from_user( pWriteBuffer + QMUXHeaderSize(), pBuf, size );
+   if (status != 0)
+   {
+      DBG( "Unable to copy data from userspace %d\n", status );
+      kfree( pWriteBuffer );
+      return status;
+   }
+
+   status = WriteSync( pFilpData->mpDev,
+                       pWriteBuffer, 
+                       size + QMUXHeaderSize(),
+                       pFilpData->mClientID );
+
+   kfree( pWriteBuffer );
+   
+   // On success, return requested size, not full QMI reqest size
+   if (status == size + QMUXHeaderSize())
+   {
+      return size;
+   }
+   else
+   {
+      return status;
+   }
+}
+
+/*=========================================================================*/
+// Initializer and destructor
+/*=========================================================================*/
+
+/*===========================================================================
+METHOD:
+   RegisterQMIDevice (Public Method)
+
+DESCRIPTION:
+   QMI Device initialization function
+
+PARAMETERS:
+   pDev     [ I ] - Device specific memory
+   
+RETURN VALUE:
+   int - 0 for success
+         Negative errno for failure
+===========================================================================*/
+int RegisterQMIDevice( sQCUSBNet * pDev )
+{
+   int result;
+   int QCQMIIndex = 0;
+   dev_t devno; 
+   char * pDevName;
+   
+   pDev->mbQMIValid = true;
+
+   // Set up for QMICTL
+   //    (does not send QMI message, just sets up memory)
+   result = GetClientID( pDev, QMICTL );
+   if (result != 0)
+   {
+      pDev->mbQMIValid = false;
+      return result;
+   }
+   atomic_set( &pDev->mQMIDev.mQMICTLTransactionID, 1 );
+
+   // Start Async reading
+   result = StartRead( pDev );
+   if (result != 0)
+   {
+      pDev->mbQMIValid = false;
+      return result;
+   }
+   
+   // Device is not ready for QMI connections right away
+   //   Wait up to 30 seconds before failing
+   if (QMIReady( pDev, 30000 ) == false)
+   {
+      DBG( "Device unresponsive to QMI\n" );
+      return -ETIMEDOUT;
+   }
+
+   // Setup WDS callback
+   result = SetupQMIWDSCallback( pDev );
+   if (result != 0)
+   {
+      pDev->mbQMIValid = false;
+      return result;
+   }
+
+   // Fill MEID for device
+   result = QMIDMSGetMEID( pDev );
+   if (result != 0)
+   {
+      pDev->mbQMIValid = false;
+      return result;
+   }
+
+   // allocate and fill devno with numbers
+   result = alloc_chrdev_region( &devno, 0, 1, "qcqmi" );
+   if (result < 0)
+   {
+      return result;
+   }
+
+   // Create cdev
+   cdev_init( &pDev->mQMIDev.mCdev, &UserspaceQMIFops );
+   pDev->mQMIDev.mCdev.owner = THIS_MODULE;
+   pDev->mQMIDev.mCdev.ops = &UserspaceQMIFops;
+
+   result = cdev_add( &pDev->mQMIDev.mCdev, devno, 1 );
+   if (result != 0)
+   {
+      DBG( "error adding cdev\n" );
+      return result;
+   }
+
+   // Match interface number (usb#)
+   pDevName = strstr( pDev->mpNetDev->net->name, "usb" );
+   if (pDevName == NULL)
+   {
+      DBG( "Bad net name: %s\n", pDev->mpNetDev->net->name );
+      return -ENXIO;
+   }
+   pDevName += strlen("usb");
+   QCQMIIndex = simple_strtoul( pDevName, NULL, 10 );
+   if(QCQMIIndex < 0 )
+   {
+      DBG( "Bad minor number\n" );
+      return -ENXIO;
+   }
+
+   // Always print this output
+   printk( KERN_INFO "creating qcqmi%d\n",
+           QCQMIIndex );
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION( 2,6,27 ))
+   // kernel 2.6.27 added a new fourth parameter to device_create
+   //    void * drvdata : the data to be added to the device for callbacks
+   device_create( pDev->mQMIDev.mpDevClass,
+                  NULL, 
+                  devno,
+                  NULL,
+                  "qcqmi%d", 
+                  QCQMIIndex );
+#else
+   device_create( pDev->mQMIDev.mpDevClass,
+                  NULL, 
+                  devno,
+                  "qcqmi%d", 
+                  QCQMIIndex );
+#endif
+   
+   pDev->mQMIDev.mDevNum = devno;
+   
+   // Success
+   return 0;
+}
+
+/*===========================================================================
+METHOD:
+   DeregisterQMIDevice (Public Method)
+
+DESCRIPTION:
+   QMI Device cleanup function
+   
+   NOTE: When this function is run the device is no longer valid
+
+PARAMETERS:
+   pDev     [ I ] - Device specific memory
+
+RETURN VALUE:
+   None
+===========================================================================*/
+void DeregisterQMIDevice( sQCUSBNet * pDev )
+{
+   struct inode * pOpenInode;
+   struct list_head * pInodeList;
+   struct list_head * pTasks;
+   struct task_struct * pEachTask;
+   struct fdtable * pFDT;
+   struct file * pFilp;
+   unsigned long flags;
+   int count = 0;
+
+   // Should never happen, but check anyway
+   if (IsDeviceValid( pDev ) == false)
+   {
+      DBG( "wrong device\n" );
+      return;
+   }
+
+   // Release all clients
+   while (pDev->mQMIDev.mpClientMemList != NULL)
+   {
+      DBG( "release 0x%04X\n", pDev->mQMIDev.mpClientMemList->mClientID );
+   
+      ReleaseClientID( pDev,
+                       pDev->mQMIDev.mpClientMemList->mClientID );
+      // NOTE: pDev->mQMIDev.mpClientMemList will 
+      //       be updated in ReleaseClientID()
+   }
+
+   // Stop all reads
+   KillRead( pDev );
+
+   pDev->mbQMIValid = false;
+
+   // Find each open file handle, and manually close it
+   
+   // Generally there will only be only one inode, but more are possible
+   list_for_each( pInodeList, &pDev->mQMIDev.mCdev.list )
+   {
+      // Get the inode
+      pOpenInode = container_of( pInodeList, struct inode, i_devices );
+      if (pOpenInode != NULL && (IS_ERR( pOpenInode ) == false))
+      {
+         // Look for this inode in each task
+
+         // "group_leader" points to the main process' task, which resides in
+         // the global "tasks" list.
+         list_for_each( pTasks, &current->group_leader->tasks )
+         {
+            pEachTask = container_of( pTasks, struct task_struct, tasks );
+            if (pEachTask == NULL || pEachTask->files == NULL)
+            {
+               // Some tasks may not have files (e.g. Xsession)
+               continue;
+            }
+            // For each file this task has open, check if it's referencing
+            // our inode.
+            spin_lock_irqsave( &pEachTask->files->file_lock, flags );
+            pFDT = files_fdtable( pEachTask->files );
+            for (count = 0; count < pFDT->max_fds; count++)
+            {
+               pFilp = pFDT->fd[count];
+               if (pFilp != NULL &&  pFilp->f_dentry != NULL )
+               {
+                  if (pFilp->f_dentry->d_inode == pOpenInode)
+                  {
+                     // Close this file handle
+                     rcu_assign_pointer( pFDT->fd[count], NULL );                     
+                     spin_unlock_irqrestore( &pEachTask->files->file_lock, flags );
+                     
+                     DBG( "forcing close of open file handle\n" );
+                     filp_close( pFilp, pEachTask->files );
+
+                     spin_lock_irqsave( &pEachTask->files->file_lock, flags );
+                  }
+               }
+            }
+            spin_unlock_irqrestore( &pEachTask->files->file_lock, flags );
+         }
+      }
+   }
+
+   // Remove device (so no more calls can be made by users)
+   if (IS_ERR(pDev->mQMIDev.mpDevClass) == false)
+   {
+      device_destroy( pDev->mQMIDev.mpDevClass, 
+                      pDev->mQMIDev.mDevNum );   
+   }
+   cdev_del( &pDev->mQMIDev.mCdev );
+   
+   unregister_chrdev_region( pDev->mQMIDev.mDevNum, 1 );
+
+   return;
+}
+
+/*=========================================================================*/
+// Driver level client management
+/*=========================================================================*/
+
+/*===========================================================================
+METHOD:
+   QMIReady (Public Method)
+
+DESCRIPTION:
+   Send QMI CTL GET VERSION INFO REQ
+   Wait for response or timeout
+
+PARAMETERS:
+   pDev     [ I ] - Device specific memory
+   timeout  [ I ] - Milliseconds to wait for response
+
+RETURN VALUE:
+   bool
+===========================================================================*/
+bool QMIReady(
+   sQCUSBNet *    pDev,
+   u16            timeout )
+{
+   int result;
+   void * pWriteBuffer;
+   u16 writeBufferSize;
+   void * pReadBuffer;
+   u16 readBufferSize;
+   struct semaphore readSem;
+   u16 curTime;
+   unsigned long flags;
+   u8 transactionID;
+   
+   if (IsDeviceValid( pDev ) == false)
+   {
+      DBG( "Invalid device\n" );
+      return -EFAULT;
+   }
+
+   writeBufferSize = QMICTLReadyReqSize();
+   pWriteBuffer = kmalloc( writeBufferSize, GFP_KERNEL );
+   if (pWriteBuffer == NULL)
+   {
+      return -ENOMEM;
+   }
+
+   // An implimentation of down_timeout has not been agreed on,
+   //    so it's been added and removed from the kernel several times.
+   //    We're just going to ignore it and poll the semaphore.
+
+   // Send a write every 100 ms and see if we get a response
+   for (curTime = 0; curTime < timeout; curTime += 100)
+   {
+      // Start read
+      sema_init( &readSem, 0 );
+   
+      transactionID = atomic_add_return( 1, &pDev->mQMIDev.mQMICTLTransactionID );
+      if (transactionID == 0)
+      {
+         transactionID = atomic_add_return( 1, &pDev->mQMIDev.mQMICTLTransactionID );
+      }
+      result = ReadAsync( pDev, QMICTL, transactionID, UpSem, &readSem );
+      if (result != 0)
+      {
+         return false;
+      }
+
+      // Fill buffer
+      result = QMICTLReadyReq( pWriteBuffer, 
+                               writeBufferSize,
+                               transactionID );
+      if (result < 0)
+      {
+         kfree( pWriteBuffer );
+         return false;
+      }
+
+      // Disregard status.  On errors, just try again
+      WriteSync( pDev,
+                 pWriteBuffer,
+                 writeBufferSize,
+                 QMICTL );
+
+      msleep( 100 );
+      if (down_trylock( &readSem ) == 0)
+      {
+         // Enter critical section
+         spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags );
+
+         // Pop the read data
+         if (PopFromReadMemList( pDev,
+                                 QMICTL,
+                                 transactionID,
+                                 &pReadBuffer,
+                                 &readBufferSize ) == true)
+         {
+            // Success
+
+            // End critical section
+            spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
+         
+            // We don't care about the result
+            kfree( pReadBuffer );
+
+            break;
+         }
+      }
+      else
+      {
+         // Enter critical section
+         spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags );
+         
+         // Timeout, remove the async read
+         NotifyAndPopNotifyList( pDev, QMICTL, transactionID );
+         
+         // End critical section
+         spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
+      }
+   }
+
+   kfree( pWriteBuffer );
+
+   // Did we time out?   
+   if (curTime >= timeout)
+   {
+      return false;
+   }
+   
+   DBG( "QMI Ready after %u milliseconds\n", curTime );
+   
+   // TODO: 3580 and newer firmware does not require this delay
+   msleep( 5000 );
+
+   // Success
+   return true;
+}
+
+/*===========================================================================
+METHOD:
+   QMIWDSCallback (Public Method)
+
+DESCRIPTION:
+   QMI WDS callback function
+   Update net stats or link state
+
+PARAMETERS:
+   pDev     [ I ] - Device specific memory
+   clientID [ I ] - Client ID
+   pData    [ I ] - Callback data (unused)
+
+RETURN VALUE:
+   None
+===========================================================================*/
+void QMIWDSCallback(
+   sQCUSBNet *    pDev,
+   u16            clientID,
+   void *         pData )
+{
+   bool bRet;
+   int result;
+   void * pReadBuffer;
+   u16 readBufferSize;
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,31 ))
+   struct net_device_stats * pStats = &(pDev->mpNetDev->stats);
+#else
+   struct net_device_stats * pStats = &(pDev->mpNetDev->net->stats);
+#endif
+
+   u32 TXOk = (u32)-1;
+   u32 RXOk = (u32)-1;
+   u32 TXErr = (u32)-1;
+   u32 RXErr = (u32)-1;
+   u32 TXOfl = (u32)-1;
+   u32 RXOfl = (u32)-1;
+   u64 TXBytesOk = (u64)-1;
+   u64 RXBytesOk = (u64)-1;
+   bool bLinkState;
+   bool bReconfigure;
+   unsigned long flags;
+   
+   if (IsDeviceValid( pDev ) == false)
+   {
+      DBG( "Invalid device\n" );
+      return;
+   }
+
+   // Critical section
+   spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags );
+   
+   bRet = PopFromReadMemList( pDev,
+                              clientID,
+                              0,
+                              &pReadBuffer,
+                              &readBufferSize );
+   
+   // End critical section
+   spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags ); 
+   
+   if (bRet == false)
+   {
+      DBG( "WDS callback failed to get data\n" );
+      return;
+   }
+   
+   // Default values
+   bLinkState = ! QTestDownReason( pDev, NO_NDIS_CONNECTION );
+   bReconfigure = false;
+
+   result = QMIWDSEventResp( pReadBuffer,
+                             readBufferSize,
+                             &TXOk,
+                             &RXOk,
+                             &TXErr,
+                             &RXErr,
+                             &TXOfl,
+                             &RXOfl,
+                             &TXBytesOk,
+                             &RXBytesOk,
+                             &bLinkState,
+                             &bReconfigure );
+   if (result < 0)
+   {
+      DBG( "bad WDS packet\n" );
+   }
+   else
+   {
+
+      // Fill in new values, ignore max values
+      if (TXOfl != (u32)-1)
+      {
+         pStats->tx_fifo_errors = TXOfl;
+      }
+      
+      if (RXOfl != (u32)-1)
+      {
+         pStats->rx_fifo_errors = RXOfl;
+      }
+
+      if (TXErr != (u32)-1)
+      {
+         pStats->tx_errors = TXErr;
+      }
+      
+      if (RXErr != (u32)-1)
+      {
+         pStats->rx_errors = RXErr;
+      }
+
+      if (TXOk != (u32)-1)
+      {
+         pStats->tx_packets = TXOk + pStats->tx_errors;
+      }
+      
+      if (RXOk != (u32)-1)
+      {
+         pStats->rx_packets = RXOk + pStats->rx_errors;
+      }
+
+      if (TXBytesOk != (u64)-1)
+      {
+         pStats->tx_bytes = TXBytesOk;
+      }
+      
+      if (RXBytesOk != (u64)-1)
+      {
+         pStats->rx_bytes = RXBytesOk;
+      }
+
+      if (bReconfigure == true)
+      {
+         DBG( "Net device link reset\n" );
+         QSetDownReason( pDev, NO_NDIS_CONNECTION );
+         QClearDownReason( pDev, NO_NDIS_CONNECTION );
+      }
+      else 
+      {
+         if (bLinkState == true)
+         {
+            DBG( "Net device link is connected\n" );
+            QClearDownReason( pDev, NO_NDIS_CONNECTION );
+         }
+         else
+         {
+            DBG( "Net device link is disconnected\n" );
+            QSetDownReason( pDev, NO_NDIS_CONNECTION );
+         }
+      }
+   }
+
+   kfree( pReadBuffer );
+
+   // Setup next read
+   result = ReadAsync( pDev,
+                       clientID,
+                       0,
+                       QMIWDSCallback,
+                       pData );
+   if (result != 0)
+   {
+      DBG( "unable to setup next async read\n" );
+   }
+
+   return;
+}
+
+/*===========================================================================
+METHOD:
+   SetupQMIWDSCallback (Public Method)
+
+DESCRIPTION:
+   Request client and fire off reqests and start async read for 
+   QMI WDS callback
+
+PARAMETERS:
+   pDev     [ I ] - Device specific memory
+
+RETURN VALUE:
+   int - 0 for success
+         Negative errno for failure
+===========================================================================*/
+int SetupQMIWDSCallback( sQCUSBNet * pDev )
+{
+   int result;
+   void * pWriteBuffer;
+   u16 writeBufferSize;
+   u16 WDSClientID;
+
+   if (IsDeviceValid( pDev ) == false)
+   {
+      DBG( "Invalid device\n" );
+      return -EFAULT;
+   }
+   
+   result = GetClientID( pDev, QMIWDS );
+   if (result < 0)
+   {
+      return result;
+   }
+   WDSClientID = result;
+
+   // QMI WDS Set Event Report
+   writeBufferSize = QMIWDSSetEventReportReqSize();
+   pWriteBuffer = kmalloc( writeBufferSize, GFP_KERNEL );
+   if (pWriteBuffer == NULL)
+   {
+      return -ENOMEM;
+   }
+   
+   result = QMIWDSSetEventReportReq( pWriteBuffer, 
+                                     writeBufferSize,
+                                     1 );
+   if (result < 0)
+   {
+      kfree( pWriteBuffer );
+      return result;
+   }
+
+   result = WriteSync( pDev,
+                       pWriteBuffer,
+                       writeBufferSize,
+                       WDSClientID );
+   kfree( pWriteBuffer );
+
+   if (result < 0)
+   {
+      return result;
+   }
+
+   // QMI WDS Get PKG SRVC Status
+   writeBufferSize = QMIWDSGetPKGSRVCStatusReqSize();
+   pWriteBuffer = kmalloc( writeBufferSize, GFP_KERNEL );
+   if (pWriteBuffer == NULL)
+   {
+      return -ENOMEM;
+   }
+
+   result = QMIWDSGetPKGSRVCStatusReq( pWriteBuffer, 
+                                       writeBufferSize,
+                                       2 );
+   if (result < 0)
+   {
+      kfree( pWriteBuffer );
+      return result;
+   }
+   
+   result = WriteSync( pDev,
+                       pWriteBuffer,
+                       writeBufferSize,
+                       WDSClientID );
+   kfree( pWriteBuffer );
+
+   if (result < 0)
+   {
+      return result;
+   }
+
+   // Setup asnyc read callback
+   result = ReadAsync( pDev,
+                       WDSClientID,
+                       0,
+                       QMIWDSCallback,
+                       NULL );
+   if (result != 0)
+   {
+      DBG( "unable to setup async read\n" );
+      return result;
+   }
+
+   // Send SetControlLineState request (USB_CDC)
+   //   Required for Autoconnect
+   result = usb_control_msg( pDev->mpNetDev->udev,
+                             usb_sndctrlpipe( pDev->mpNetDev->udev, 0 ),
+                             0x22,
+                             0x21,
+                             1, // DTR present
+                             0,
+                             NULL,
+                             0,
+                             100 );
+   if (result < 0)
+   {
+      DBG( "Bad SetControlLineState status %d\n", result );
+      return result;
+   }
+
+   return 0;
+}
+
+/*===========================================================================
+METHOD:
+   QMIDMSGetMEID (Public Method)
+
+DESCRIPTION:
+   Register DMS client
+   send MEID req and parse response
+   Release DMS client
+
+PARAMETERS:
+   pDev     [ I ] - Device specific memory
+
+RETURN VALUE:
+   None
+===========================================================================*/
+int QMIDMSGetMEID( sQCUSBNet * pDev )
+{
+   int result;
+   void * pWriteBuffer;
+   u16 writeBufferSize;
+   void * pReadBuffer;
+   u16 readBufferSize;
+   u16 DMSClientID;
+
+   if (IsDeviceValid( pDev ) == false)
+   {
+      DBG( "Invalid device\n" );
+      return -EFAULT;
+   }
+
+   result = GetClientID( pDev, QMIDMS );
+   if (result < 0)
+   {
+      return result;
+   }
+   DMSClientID = result;
+
+   // QMI DMS Get Serial numbers Req
+   writeBufferSize = QMIDMSGetMEIDReqSize();
+   pWriteBuffer = kmalloc( writeBufferSize, GFP_KERNEL );
+   if (pWriteBuffer == NULL)
+   {
+      return -ENOMEM;
+   }
+
+   result = QMIDMSGetMEIDReq( pWriteBuffer, 
+                              writeBufferSize,
+                              1 );
+   if (result < 0)
+   {
+      kfree( pWriteBuffer );
+      return result;
+   }
+
+   result = WriteSync( pDev,
+                       pWriteBuffer,
+                       writeBufferSize,
+                       DMSClientID );
+   kfree( pWriteBuffer );
+
+   if (result < 0)
+   {
+      return result;
+   }
+
+   // QMI DMS Get Serial numbers Resp
+   result = ReadSync( pDev,
+                      &pReadBuffer,
+                      DMSClientID,
+                      1 );
+   if (result < 0)
+   {
+      return result;
+   }
+   readBufferSize = result;
+
+   result = QMIDMSGetMEIDResp( pReadBuffer,
+                               readBufferSize,
+                               &pDev->mMEID[0],
+                               14 );
+   kfree( pReadBuffer );
+
+   if (result < 0)
+   {
+      DBG( "bad get MEID resp\n" );
+      
+      // Non fatal error, device did not return any MEID
+      //    Fill with 0's
+      memset( &pDev->mMEID[0], '0', 14 );
+   }
+
+   ReleaseClientID( pDev, DMSClientID );
+
+   // Success
+   return 0;
+}
diff --git a/drivers/staging/gobi/QCUSBNet2k/QMIDevice.h b/drivers/staging/gobi/QCUSBNet2k/QMIDevice.h
new file mode 100644
index 0000000..6fb9c47
--- /dev/null
+++ b/drivers/staging/gobi/QCUSBNet2k/QMIDevice.h
@@ -0,0 +1,297 @@
+/*===========================================================================
+FILE:
+   QMIDevice.h
+
+DESCRIPTION:
+   Functions related to the QMI interface device
+   
+FUNCTIONS:
+   Generic functions
+      IsDeviceValid
+      PrintHex
+      QSetDownReason
+      QClearDownReason
+      QTestDownReason
+
+   Driver level asynchronous read functions
+      ReadCallback
+      IntCallback
+      StartRead
+      KillRead
+
+   Internal read/write functions
+      ReadAsync
+      UpSem
+      ReadSync
+      WriteSyncCallback
+      WriteSync
+
+   Internal memory management functions
+      GetClientID
+      ReleaseClientID
+      FindClientMem
+      AddToReadMemList
+      PopFromReadMemList
+      AddToNotifyList
+      NotifyAndPopNotifyList
+      AddToURBList
+      PopFromURBList
+
+   Userspace wrappers
+      UserspaceOpen
+      UserspaceIOCTL
+      UserspaceClose
+      UserspaceRead
+      UserspaceWrite
+
+   Initializer and destructor
+      RegisterQMIDevice
+      DeregisterQMIDevice
+
+   Driver level client management
+      QMIReady
+      QMIWDSCallback
+      SetupQMIWDSCallback
+      QMIDMSGetMEID
+
+Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 2 and
+only version 2 as published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+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 Street, Fifth Floor, Boston, MA
+02110-1301, USA.
+
+===========================================================================*/
+
+//---------------------------------------------------------------------------
+// Pragmas
+//---------------------------------------------------------------------------
+#pragma once
+
+//---------------------------------------------------------------------------
+// Include Files
+//---------------------------------------------------------------------------
+#include "Structs.h"
+#include "QMI.h"
+
+/*=========================================================================*/
+// Generic functions
+/*=========================================================================*/
+
+// Basic test to see if device memory is valid
+bool IsDeviceValid( sQCUSBNet * pDev );
+
+// Print Hex data, for debug purposes
+void PrintHex(
+   void *         pBuffer,
+   u16            bufSize );
+
+// Sets mDownReason and turns carrier off
+void QSetDownReason(
+   sQCUSBNet *    pDev,
+   u8             reason );
+
+// Clear mDownReason and may turn carrier on
+void QClearDownReason(
+   sQCUSBNet *    pDev,
+   u8             reason );
+
+// Tests mDownReason and returns whether reason is set
+bool QTestDownReason(
+   sQCUSBNet *    pDev,
+   u8             reason );
+
+/*=========================================================================*/
+// Driver level asynchronous read functions
+/*=========================================================================*/
+
+// Read callback
+//    Put the data in storage and notify anyone waiting for data
+void ReadCallback( struct urb * pReadURB );
+
+// Inturrupt callback
+//    Data is available, start a read URB
+void IntCallback( struct urb * pIntURB );
+
+// Start continuous read "thread"
+int StartRead( sQCUSBNet * pDev );
+
+// Kill continuous read "thread"
+void KillRead( sQCUSBNet * pDev );
+
+/*=========================================================================*/
+// Internal read/write functions
+/*=========================================================================*/
+
+// Start asynchronous read
+//     Reading client's data store, not device
+int ReadAsync(
+   sQCUSBNet *    pDev,
+   u16            clientID,
+   u16            transactionID,
+   void           (*pCallback)(sQCUSBNet *, u16, void *),
+   void *         pData );
+
+// Notification function for synchronous read
+void UpSem( 
+   sQCUSBNet *    pDev,
+   u16            clientID,
+   void *         pData );
+
+// Start synchronous read
+//     Reading client's data store, not device
+int ReadSync(
+   sQCUSBNet *    pDev,
+   void **        ppOutBuffer,
+   u16            clientID,
+   u16            transactionID );
+
+// Write callback
+void WriteSyncCallback( struct urb * pWriteURB );
+
+// Start synchronous write
+int WriteSync(
+   sQCUSBNet *    pDev,
+   char *         pInWriteBuffer,
+   int            size,
+   u16            clientID );
+
+/*=========================================================================*/
+// Internal memory management functions
+/*=========================================================================*/
+
+// Create client and allocate memory
+int GetClientID( 
+   sQCUSBNet *      pDev,
+   u8               serviceType );
+
+// Release client and free memory
+void ReleaseClientID(
+   sQCUSBNet *      pDev,
+   u16              clientID );
+
+// Find this client's memory
+sClientMemList * FindClientMem(
+   sQCUSBNet *      pDev,
+   u16              clientID );
+
+// Add Data to this client's ReadMem list
+bool AddToReadMemList( 
+   sQCUSBNet *      pDev,
+   u16              clientID,
+   u16              transactionID,
+   void *           pData,
+   u16              dataSize );
+
+// Remove data from this client's ReadMem list if it matches 
+// the specified transaction ID.
+bool PopFromReadMemList( 
+   sQCUSBNet *      pDev,
+   u16              clientID,
+   u16              transactionID,
+   void **          ppData,
+   u16 *            pDataSize );
+
+// Add Notify entry to this client's notify List
+bool AddToNotifyList( 
+   sQCUSBNet *      pDev,
+   u16              clientID,
+   u16              transactionID,
+   void             (* pNotifyFunct)(sQCUSBNet *, u16, void *),
+   void *           pData );
+
+// Remove first Notify entry from this client's notify list 
+//    and Run function
+bool NotifyAndPopNotifyList( 
+   sQCUSBNet *      pDev,
+   u16              clientID,
+   u16              transactionID );
+
+// Add URB to this client's URB list
+bool AddToURBList( 
+   sQCUSBNet *      pDev,
+   u16              clientID,
+   struct urb *     pURB );
+
+// Remove URB from this client's URB list
+struct urb * PopFromURBList( 
+   sQCUSBNet *      pDev,
+   u16              clientID );
+
+/*=========================================================================*/
+// Userspace wrappers
+/*=========================================================================*/
+
+// Userspace open
+int UserspaceOpen( 
+   struct inode *   pInode, 
+   struct file *    pFilp );
+
+// Userspace ioctl
+int UserspaceIOCTL( 
+   struct inode *    pUnusedInode, 
+   struct file *     pFilp,
+   unsigned int      cmd, 
+   unsigned long     arg );
+
+// Userspace close
+int UserspaceClose( 
+   struct file *       pFilp,
+   fl_owner_t          unusedFileTable );
+
+// Userspace read (synchronous)
+ssize_t UserspaceRead( 
+   struct file *        pFilp,
+   char __user *        pBuf, 
+   size_t               size,
+   loff_t *             pUnusedFpos );
+
+// Userspace write (synchronous)
+ssize_t UserspaceWrite(
+   struct file *        pFilp, 
+   const char __user *  pBuf, 
+   size_t               size,
+   loff_t *             pUnusedFpos );
+
+/*=========================================================================*/
+// Initializer and destructor
+/*=========================================================================*/
+
+// QMI Device initialization function
+int RegisterQMIDevice( sQCUSBNet * pDev );
+
+// QMI Device cleanup function
+void DeregisterQMIDevice( sQCUSBNet * pDev );
+
+/*=========================================================================*/
+// Driver level client management
+/*=========================================================================*/
+
+// Check if QMI is ready for use
+bool QMIReady(
+   sQCUSBNet *    pDev,
+   u16            timeout );
+
+// QMI WDS callback function
+void QMIWDSCallback(
+   sQCUSBNet *    pDev,
+   u16            clientID,
+   void *         pData );
+
+// Fire off reqests and start async read for QMI WDS callback
+int SetupQMIWDSCallback( sQCUSBNet * pDev );
+
+// Register client, send req and parse MEID response, release client
+int QMIDMSGetMEID( sQCUSBNet * pDev );
+
+
+
diff --git a/drivers/staging/gobi/QCUSBNet2k/Structs.h b/drivers/staging/gobi/QCUSBNet2k/Structs.h
new file mode 100644
index 0000000..07e3193
--- /dev/null
+++ b/drivers/staging/gobi/QCUSBNet2k/Structs.h
@@ -0,0 +1,318 @@
+/*===========================================================================
+FILE:
+   Structs.h
+
+DESCRIPTION:
+   Declaration of structures used by the Qualcomm Linux USB Network driver
+   
+FUNCTIONS:
+   none
+
+Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 2 and
+only version 2 as published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+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 Street, Fifth Floor, Boston, MA
+02110-1301, USA.
+
+===========================================================================*/
+
+//---------------------------------------------------------------------------
+// Pragmas
+//---------------------------------------------------------------------------
+#pragma once
+
+//---------------------------------------------------------------------------
+// Include Files
+//---------------------------------------------------------------------------
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/version.h>
+#include <linux/cdev.h>
+#include <linux/kthread.h>
+
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION( 2,6,24 ))
+   #include "usbnet.h"
+#else
+   #include <linux/usb/usbnet.h>
+#endif
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,25 ))
+   #include <linux/fdtable.h>
+#else
+   #include <linux/file.h>
+#endif
+
+// DBG macro
+#define DBG( format, arg... ) \
+   if (debug == 1)\
+   { \
+      printk( KERN_INFO "QCUSBNet2k::%s " format, __FUNCTION__, ## arg ); \
+   } \
+
+
+// Used in recursion, defined later below
+struct sQCUSBNet;
+
+/*=========================================================================*/
+// Struct sReadMemList
+//
+//    Structure that defines an entry in a Read Memory linked list
+/*=========================================================================*/
+typedef struct sReadMemList
+{
+   /* Data buffer */
+   void *                     mpData;
+   
+   /* Transaction ID */
+   u16                        mTransactionID;
+
+   /* Size of data buffer */
+   u16                        mDataSize;
+
+   /* Next entry in linked list */
+   struct sReadMemList *      mpNext;
+
+} sReadMemList;
+
+/*=========================================================================*/
+// Struct sNotifyList
+//
+//    Structure that defines an entry in a Notification linked list
+/*=========================================================================*/
+typedef struct sNotifyList
+{
+   /* Function to be run when data becomes available */
+   void                  (* mpNotifyFunct)(struct sQCUSBNet *, u16, void *);
+   
+   /* Transaction ID */
+   u16                   mTransactionID;
+
+   /* Data to provide as parameter to mpNotifyFunct */
+   void *                mpData;
+   
+   /* Next entry in linked list */
+   struct sNotifyList *  mpNext;
+
+} sNotifyList;
+
+/*=========================================================================*/
+// Struct sURBList
+//
+//    Structure that defines an entry in a URB linked list
+/*=========================================================================*/
+typedef struct sURBList
+{
+   /* The current URB */
+   struct urb *       mpURB;
+
+   /* Next entry in linked list */
+   struct sURBList *  mpNext;
+
+} sURBList;
+
+/*=========================================================================*/
+// Struct sClientMemList
+//
+//    Structure that defines an entry in a Client Memory linked list
+//      Stores data specific to a Service Type and Client ID
+/*=========================================================================*/
+typedef struct sClientMemList
+{
+   /* Client ID for this Client */
+   u16                          mClientID;
+
+   /* Linked list of Read entries */
+   /*    Stores data read from device before sending to client */
+   sReadMemList *               mpList;
+   
+   /* Linked list of Notification entries */
+   /*    Stores notification functions to be run as data becomes 
+         available or the device is removed */
+   sNotifyList *                mpReadNotifyList;
+
+   /* Linked list of URB entries */
+   /*    Stores pointers to outstanding URBs which need canceled 
+         when the client is deregistered or the device is removed */
+   sURBList *                   mpURBList;
+   
+   /* Next entry in linked list */
+   struct sClientMemList *      mpNext;
+
+} sClientMemList;
+
+/*=========================================================================*/
+// Struct sURBSetupPacket
+//
+//    Structure that defines a USB Setup packet for Control URBs
+//    Taken from USB CDC specifications
+/*=========================================================================*/
+typedef struct sURBSetupPacket
+{
+   /* Request type */
+   u8    mRequestType;
+
+   /* Request code */
+   u8    mRequestCode;
+
+   /* Value */
+   u16   mValue;
+
+   /* Index */
+   u16   mIndex;
+
+   /* Length of Control URB */
+   u16   mLength;
+
+} sURBSetupPacket;
+
+// Common value for sURBSetupPacket.mLength
+#define DEFAULT_READ_URB_LENGTH 0x1000
+
+
+/*=========================================================================*/
+// Struct sAutoPM
+//
+//    Structure used to manage AutoPM thread which determines whether the
+//    device is in use or may enter autosuspend.  Also submits net 
+//    transmissions asynchronously.
+/*=========================================================================*/
+typedef struct sAutoPM
+{
+   /* Thread for atomic autopm function */
+   struct task_struct *       mpThread;
+
+   /* Up this semaphore when it's time for the thread to work */
+   struct semaphore           mThreadDoWork;
+
+   /* Time to exit? */
+   bool                       mbExit;
+
+   /* List of URB's queued to be sent to the device */
+   sURBList *                 mpURBList;
+
+   /* URB list lock (for adding and removing elements) */
+   spinlock_t                 mURBListLock;
+   
+   /* Active URB */
+   struct urb *               mpActiveURB;
+
+   /* Active URB lock (for adding and removing elements) */
+   spinlock_t                 mActiveURBLock;
+   
+   /* Duplicate pointer to USB device interface */
+   struct usb_interface *     mpIntf;
+
+} sAutoPM;
+
+
+/*=========================================================================*/
+// Struct sQMIDev
+//
+//    Structure that defines the data for the QMI device
+/*=========================================================================*/
+typedef struct sQMIDev
+{
+   /* Device number */
+   dev_t                      mDevNum;
+
+   /* Device class */
+   struct class *             mpDevClass;
+
+   /* cdev struct */
+   struct cdev                mCdev;
+
+   /* Pointer to read URB */
+   struct urb *               mpReadURB;
+
+   /* Read setup packet */
+   sURBSetupPacket *          mpReadSetupPacket;
+
+   /* Read buffer attached to current read URB */
+   void *                     mpReadBuffer;
+   
+   /* Inturrupt URB */
+   /*    Used to asynchronously notify when read data is available */
+   struct urb *               mpIntURB;
+
+   /* Buffer used by Inturrupt URB */
+   void *                     mpIntBuffer;
+   
+   /* Pointer to memory linked list for all clients */
+   sClientMemList *           mpClientMemList;
+   
+   /* Spinlock for client Memory entries */
+   spinlock_t                 mClientMemLock;
+
+   /* Transaction ID associated with QMICTL "client" */
+   atomic_t                   mQMICTLTransactionID;
+
+} sQMIDev;
+
+/*=========================================================================*/
+// Struct sQCUSBNet
+//
+//    Structure that defines the data associated with the Qualcomm USB device
+/*=========================================================================*/
+typedef struct sQCUSBNet
+{
+   /* Net device structure */
+   struct usbnet *        mpNetDev;
+
+   /* Usb device interface */
+   struct usb_interface * mpIntf;
+   
+   /* Pointers to usbnet_open and usbnet_stop functions */
+   int                  (* mpUSBNetOpen)(struct net_device *);
+   int                  (* mpUSBNetStop)(struct net_device *);
+   
+   /* Reason(s) why interface is down */
+   /* Used by Q*DownReason */
+   unsigned long          mDownReason;
+#define NO_NDIS_CONNECTION    0
+#define CDC_CONNECTION_SPEED  1
+#define DRIVER_SUSPENDED      2
+#define NET_IFACE_STOPPED     3
+
+   /* QMI "device" status */
+   bool                   mbQMIValid;
+
+   /* QMI "device" memory */
+   sQMIDev                mQMIDev;
+
+   /* Device MEID */
+   char                   mMEID[14];
+   
+   /* AutoPM thread */
+   sAutoPM                mAutoPM;
+
+} sQCUSBNet;
+
+/*=========================================================================*/
+// Struct sQMIFilpStorage
+//
+//    Structure that defines the storage each file handle contains
+//       Relates the file handle to a client
+/*=========================================================================*/
+typedef struct sQMIFilpStorage
+{
+   /* Client ID */
+   u16                  mClientID;
+   
+   /* Device pointer */
+   sQCUSBNet *          mpDev;
+
+} sQMIFilpStorage;
+
+
diff --git a/drivers/staging/qcache/Kconfig b/drivers/staging/qcache/Kconfig
new file mode 100644
index 0000000..389341c
--- /dev/null
+++ b/drivers/staging/qcache/Kconfig
@@ -0,0 +1,8 @@
+config QCACHE
+	tristate "Dynamic compression of clean pagecache pages"
+	depends on CLEANCACHE
+	select LZO_COMPRESS
+	select LZO_DECOMPRESS
+	default n
+	help
+	  Qcache is the backend for fmem
diff --git a/drivers/staging/qcache/Makefile b/drivers/staging/qcache/Makefile
new file mode 100644
index 0000000..4fdf05c
--- /dev/null
+++ b/drivers/staging/qcache/Makefile
@@ -0,0 +1,3 @@
+qcache-y	:=	qcache-main.o tmem.o fmem.o
+
+obj-$(CONFIG_QCACHE)	+=	qcache.o
diff --git a/drivers/staging/qcache/fmem.c b/drivers/staging/qcache/fmem.c
new file mode 100644
index 0000000..8f9e0ef
--- /dev/null
+++ b/drivers/staging/qcache/fmem.c
@@ -0,0 +1,344 @@
+/*
+ *
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/export.h>
+#include <linux/fmem.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#ifdef CONFIG_MEMORY_HOTPLUG
+#include <linux/memory.h>
+#include <linux/memory_hotplug.h>
+#endif
+#include "tmem.h"
+#include <asm/mach/map.h>
+
+struct fmem_data fmem_data;
+enum fmem_state fmem_state;
+static spinlock_t fmem_state_lock;
+
+#ifdef CONFIG_MEMORY_HOTPLUG
+static unsigned int section_powered_off[NR_MEM_SECTIONS];
+static unsigned int fmem_section_start, fmem_section_end;
+#endif
+
+void *fmem_map_virtual_area(int cacheability)
+{
+	unsigned long addr;
+	const struct mem_type *type;
+	int ret;
+
+	addr = (unsigned long) fmem_data.area->addr;
+	type = get_mem_type(cacheability);
+	ret = ioremap_pages(addr, fmem_data.phys, fmem_data.size, type);
+	if (ret)
+		return ERR_PTR(ret);
+
+	fmem_data.virt = fmem_data.area->addr;
+
+	return fmem_data.virt;
+}
+
+void fmem_unmap_virtual_area(void)
+{
+	unmap_kernel_range((unsigned long)fmem_data.virt, fmem_data.size);
+	fmem_data.virt = NULL;
+}
+
+static int fmem_probe(struct platform_device *pdev)
+{
+	struct fmem_platform_data *pdata = pdev->dev.platform_data;
+
+	if (!pdata->phys)
+		pdata->phys = allocate_contiguous_ebi_nomap(pdata->size,
+			pdata->align);
+
+#ifdef CONFIG_MEMORY_HOTPLUG
+	fmem_section_start = pdata->phys >> PA_SECTION_SHIFT;
+	fmem_section_end = (pdata->phys - 1 + pdata->size) >> PA_SECTION_SHIFT;
+#endif
+	fmem_data.phys = pdata->phys + pdata->reserved_size_low;
+	fmem_data.size = pdata->size - pdata->reserved_size_low -
+					pdata->reserved_size_high;
+	fmem_data.reserved_size_low = pdata->reserved_size_low;
+	fmem_data.reserved_size_high = pdata->reserved_size_high;
+
+	if (!fmem_data.size)
+		return -ENODEV;
+
+	fmem_data.area = get_vm_area(fmem_data.size, VM_IOREMAP);
+	if (!fmem_data.area)
+		return -ENOMEM;
+
+	if (!fmem_map_virtual_area(MT_DEVICE_CACHED)) {
+		remove_vm_area(fmem_data.area->addr);
+		return -ENOMEM;
+	}
+	pr_info("fmem phys %lx virt %p size %lx\n",
+		fmem_data.phys, fmem_data.virt, fmem_data.size);
+
+	spin_lock_init(&fmem_state_lock);
+
+	return 0;
+}
+
+static int fmem_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static struct platform_driver fmem_driver = {
+	.probe = fmem_probe,
+	.remove = fmem_remove,
+	.driver = { .name = "fmem" }
+};
+
+#ifdef CONFIG_SYSFS
+static ssize_t fmem_state_show(struct kobject *kobj,
+				    struct kobj_attribute *attr,
+				    char *buf)
+{
+	if (fmem_state == FMEM_T_STATE)
+		return snprintf(buf, 3, "t\n");
+	else if (fmem_state == FMEM_C_STATE)
+		return snprintf(buf, 3, "c\n");
+#ifdef CONFIG_MEMORY_HOTPLUG
+	else if (fmem_state == FMEM_O_STATE)
+		return snprintf(buf, 3, "o\n");
+#endif
+	else if (fmem_state == FMEM_UNINITIALIZED)
+		return snprintf(buf, 15, "uninitialized\n");
+	return snprintf(buf, 3, "?\n");
+}
+
+static ssize_t fmem_state_store(struct kobject *kobj,
+				    struct kobj_attribute *attr,
+				    const char *buf, size_t count)
+{
+	int ret = -EINVAL;
+
+	if (!strncmp(buf, "t", 1))
+		ret = fmem_set_state(FMEM_T_STATE);
+	else if (!strncmp(buf, "c", 1))
+		ret = fmem_set_state(FMEM_C_STATE);
+#ifdef CONFIG_MEMORY_HOTPLUG
+	else if (!strncmp(buf, "o", 1))
+		ret = fmem_set_state(FMEM_O_STATE);
+#endif
+	if (ret)
+		return ret;
+	return 1;
+}
+
+static struct kobj_attribute fmem_state_attr = {
+		.attr = { .name = "state", .mode = 0644 },
+		.show = fmem_state_show,
+		.store = fmem_state_store,
+};
+
+static struct attribute *fmem_attrs[] = {
+	&fmem_state_attr.attr,
+	NULL,
+};
+
+static struct attribute_group fmem_attr_group = {
+	.attrs = fmem_attrs,
+	.name = "fmem",
+};
+
+static int fmem_create_sysfs(void)
+{
+	int ret = 0;
+
+	ret = sysfs_create_group(mm_kobj, &fmem_attr_group);
+	if (ret)
+		pr_err("fmem: can't create sysfs\n");
+	return ret;
+}
+
+#endif
+
+#ifdef CONFIG_MEMORY_HOTPLUG
+bool fmem_is_disjoint(unsigned long start_pfn, unsigned long nr_pages)
+{
+	unsigned long fmem_start_pfn, fmem_end_pfn;
+	unsigned long unstable_end_pfn;
+	unsigned long highest_start_pfn, lowest_end_pfn;
+
+	fmem_start_pfn = (fmem_data.phys - fmem_data.reserved_size_low)
+		>> PAGE_SHIFT;
+	fmem_end_pfn = (fmem_data.phys + fmem_data.size +
+		fmem_data.reserved_size_high - 1) >> PAGE_SHIFT;
+	unstable_end_pfn = start_pfn + nr_pages - 1;
+
+	highest_start_pfn = max(fmem_start_pfn, start_pfn);
+	lowest_end_pfn = min(fmem_end_pfn, unstable_end_pfn);
+
+	return lowest_end_pfn < highest_start_pfn;
+}
+
+static int fmem_mem_going_offline_callback(void *arg)
+{
+	struct memory_notify *marg = arg;
+
+	if (fmem_is_disjoint(marg->start_pfn, marg->nr_pages))
+		return 0;
+	return fmem_set_state(FMEM_O_STATE);
+}
+
+static void fmem_mem_online_callback(void *arg)
+{
+	struct memory_notify *marg = arg;
+	int i;
+
+	section_powered_off[marg->start_pfn >> PFN_SECTION_SHIFT] = 0;
+
+	if (fmem_state != FMEM_O_STATE)
+		return;
+
+	for (i = fmem_section_start; i <= fmem_section_end; i++) {
+		if (section_powered_off[i])
+			return;
+	}
+
+	fmem_set_state(FMEM_T_STATE);
+}
+
+static void fmem_mem_offline_callback(void *arg)
+{
+	struct memory_notify *marg = arg;
+
+	section_powered_off[marg->start_pfn >> PFN_SECTION_SHIFT] = 1;
+}
+
+static int fmem_memory_callback(struct notifier_block *self,
+				unsigned long action, void *arg)
+{
+	int ret = 0;
+
+	if (fmem_state == FMEM_UNINITIALIZED)
+		return NOTIFY_OK;
+
+	switch (action) {
+	case MEM_ONLINE:
+		fmem_mem_online_callback(arg);
+		break;
+	case MEM_GOING_OFFLINE:
+		ret = fmem_mem_going_offline_callback(arg);
+		break;
+	case MEM_OFFLINE:
+		fmem_mem_offline_callback(arg);
+		break;
+	case MEM_GOING_ONLINE:
+	case MEM_CANCEL_ONLINE:
+	case MEM_CANCEL_OFFLINE:
+		break;
+	}
+	if (ret)
+		ret = notifier_from_errno(ret);
+	else
+		ret = NOTIFY_OK;
+	return ret;
+}
+#endif
+
+static int __init fmem_init(void)
+{
+#ifdef CONFIG_MEMORY_HOTPLUG
+	hotplug_memory_notifier(fmem_memory_callback, 0);
+#endif
+	return platform_driver_register(&fmem_driver);
+}
+
+static void __exit fmem_exit(void)
+{
+	platform_driver_unregister(&fmem_driver);
+}
+
+struct fmem_data *fmem_get_info(void)
+{
+	return &fmem_data;
+}
+EXPORT_SYMBOL(fmem_get_info);
+
+void lock_fmem_state(void)
+{
+	spin_lock(&fmem_state_lock);
+}
+
+void unlock_fmem_state(void)
+{
+	spin_unlock(&fmem_state_lock);
+}
+
+int fmem_set_state(enum fmem_state new_state)
+{
+	int ret = 0;
+	int create_sysfs = 0;
+
+	lock_fmem_state();
+	if (fmem_state == new_state)
+		goto out;
+
+	if (fmem_state == FMEM_UNINITIALIZED) {
+		if (new_state == FMEM_T_STATE) {
+			tmem_enable();
+			create_sysfs = 1;
+			goto out_set;
+		} else {
+			ret = -EINVAL;
+			goto out;
+		}
+	}
+
+#ifdef CONFIG_MEMORY_HOTPLUG
+	if (fmem_state == FMEM_C_STATE && new_state == FMEM_O_STATE) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
+	if (fmem_state == FMEM_O_STATE && new_state == FMEM_C_STATE) {
+		pr_warn("attempting to use powered off memory as fmem\n");
+		ret = -EAGAIN;
+		goto out;
+	}
+#endif
+
+	if (new_state == FMEM_T_STATE) {
+		void *v;
+		v = fmem_map_virtual_area(MT_DEVICE_CACHED);
+		if (IS_ERR_OR_NULL(v)) {
+			ret = PTR_ERR(v);
+			goto out;
+		}
+		tmem_enable();
+	} else {
+		tmem_disable();
+		fmem_unmap_virtual_area();
+	}
+
+out_set:
+	fmem_state = new_state;
+out:
+	unlock_fmem_state();
+#ifdef CONFIG_SYSFS
+	if (create_sysfs)
+		fmem_create_sysfs();
+#endif
+	return ret;
+}
+EXPORT_SYMBOL(fmem_set_state);
+
+arch_initcall(fmem_init);
+module_exit(fmem_exit);
diff --git a/drivers/staging/qcache/qcache-main.c b/drivers/staging/qcache/qcache-main.c
new file mode 100644
index 0000000..063c6fc
--- /dev/null
+++ b/drivers/staging/qcache/qcache-main.c
@@ -0,0 +1,1358 @@
+/*
+ * Copyright (c) 2010,2011, Dan Magenheimer, Oracle Corp.
+ * Copyright (c) 2010,2011, Nitin Gupta
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * Qcache provides an in-kernel "host implementation" for transcendent memory
+ * and, thus indirectly, for cleancache and frontswap.  Qcache includes a
+ * page-accessible memory [1] interface, utilizing lzo1x compression:
+ * 1) "compression buddies" ("zbud") is used for ephemeral pages
+ * Zbud allows pairs (and potentially,
+ * in the future, more than a pair of) compressed pages to be closely linked
+ * so that reclaiming can be done via the kernel's physical-page-oriented
+ * "shrinker" interface.
+ *
+ * [1] For a definition of page-accessible memory (aka PAM), see:
+ *   http://marc.info/?l=linux-mm&m=127811271605009
+ */
+
+#include <linux/module.h>
+#include <linux/cpu.h>
+#include <linux/highmem.h>
+#include <linux/list.h>
+#include <linux/lzo.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/atomic.h>
+#include <linux/math64.h>
+#include <linux/bitmap.h>
+#include <linux/fmem.h>
+#include "tmem.h"
+
+#if !defined(CONFIG_CLEANCACHE)
+#error "qcache is useless without CONFIG_CLEANCACHE"
+#endif
+#include <linux/cleancache.h>
+
+#define ZCACHE_GFP_MASK \
+	(__GFP_FS | __GFP_NORETRY | __GFP_NOWARN | __GFP_NOMEMALLOC)
+
+#define MAX_POOLS_PER_CLIENT 16
+
+#define MAX_CLIENTS 16
+#define LOCAL_CLIENT ((uint16_t)-1)
+
+MODULE_LICENSE("GPL");
+
+struct zcache_client {
+	struct tmem_pool *tmem_pools[MAX_POOLS_PER_CLIENT];
+	struct xv_pool *xvpool;
+	bool allocated;
+	atomic_t refcount;
+};
+
+struct qcache_info {
+	void *addr;
+	unsigned long *bitmap;
+	spinlock_t lock;
+	unsigned pages;
+};
+static struct qcache_info qcache_info;
+static unsigned long zcache_qc_allocated;
+static unsigned long zcache_qc_freed;
+static unsigned long zcache_qc_used;
+static unsigned long zcache_qc_max_used;
+
+static struct zcache_client zcache_host;
+static struct zcache_client zcache_clients[MAX_CLIENTS];
+
+static inline uint16_t get_client_id_from_client(struct zcache_client *cli)
+{
+	BUG_ON(cli == NULL);
+	if (cli == &zcache_host)
+		return LOCAL_CLIENT;
+	return cli - &zcache_clients[0];
+}
+
+static inline bool is_local_client(struct zcache_client *cli)
+{
+	return cli == &zcache_host;
+}
+
+/**********
+ * Compression buddies ("zbud") provides for packing two (or, possibly
+ * in the future, more) compressed ephemeral pages into a single "raw"
+ * (physical) page and tracking them with data structures so that
+ * the raw pages can be easily reclaimed.
+ *
+ * A zbud page ("zbpg") is an aligned page containing a list_head,
+ * a lock, and two "zbud headers".  The remainder of the physical
+ * page is divided up into aligned 64-byte "chunks" which contain
+ * the compressed data for zero, one, or two zbuds.  Each zbpg
+ * resides on: (1) an "unused list" if it has no zbuds; (2) a
+ * "buddied" list if it is fully populated  with two zbuds; or
+ * (3) one of PAGE_SIZE/64 "unbuddied" lists indexed by how many chunks
+ * the one unbuddied zbud uses.  The data inside a zbpg cannot be
+ * read or written unless the zbpg's lock is held.
+ */
+
+#define ZBH_SENTINEL  0x43214321
+#define ZBPG_SENTINEL  0xdeadbeef
+
+#define ZBUD_MAX_BUDS 2
+
+struct zbud_hdr {
+	uint16_t client_id;
+	uint16_t pool_id;
+	struct tmem_oid oid;
+	uint32_t index;
+	uint16_t size; /* compressed size in bytes, zero means unused */
+	DECL_SENTINEL
+};
+
+struct zbud_page {
+	struct list_head bud_list;
+	spinlock_t lock;
+	struct zbud_hdr buddy[ZBUD_MAX_BUDS];
+	DECL_SENTINEL
+	/* followed by NUM_CHUNK aligned CHUNK_SIZE-byte chunks */
+};
+
+#define CHUNK_SHIFT	6
+#define CHUNK_SIZE	(1 << CHUNK_SHIFT)
+#define CHUNK_MASK	(~(CHUNK_SIZE-1))
+#define NCHUNKS		(((PAGE_SIZE - sizeof(struct zbud_page)) & \
+				CHUNK_MASK) >> CHUNK_SHIFT)
+#define MAX_CHUNK	(NCHUNKS-1)
+
+static struct {
+	struct list_head list;
+	unsigned count;
+} zbud_unbuddied[NCHUNKS];
+/* list N contains pages with N chunks USED and NCHUNKS-N unused */
+/* element 0 is never used but optimizing that isn't worth it */
+static unsigned long zbud_cumul_chunk_counts[NCHUNKS];
+
+struct list_head zbud_buddied_list;
+static unsigned long zcache_zbud_buddied_count;
+
+/* protects the buddied list and all unbuddied lists */
+static DEFINE_SPINLOCK(zbud_budlists_spinlock);
+
+static atomic_t zcache_zbud_curr_raw_pages;
+static atomic_t zcache_zbud_curr_zpages;
+static unsigned long zcache_zbud_curr_zbytes;
+static unsigned long zcache_zbud_cumul_zpages;
+static unsigned long zcache_zbud_cumul_zbytes;
+static unsigned long zcache_compress_poor;
+static unsigned long zcache_mean_compress_poor;
+
+/* forward references */
+static void *zcache_get_free_page(void);
+
+static void *qcache_alloc(void)
+{
+	void *addr;
+	unsigned long flags;
+	int offset;
+	struct qcache_info *qc = &qcache_info;
+
+	spin_lock_irqsave(&qc->lock, flags);
+	offset = bitmap_find_free_region(qc->bitmap, qc->pages, 0);
+
+	if (offset < 0) {
+		spin_unlock_irqrestore(&qc->lock, flags);
+		return NULL;
+	}
+
+	zcache_qc_allocated++;
+	zcache_qc_used++;
+	zcache_qc_max_used = max(zcache_qc_max_used, zcache_qc_used);
+	spin_unlock_irqrestore(&qc->lock, flags);
+
+	addr = qc->addr + offset * PAGE_SIZE;
+
+	return addr;
+}
+
+static void qcache_free(void *addr)
+{
+	unsigned long flags;
+	int offset;
+	struct qcache_info *qc = &qcache_info;
+
+	offset = (addr - qc->addr) / PAGE_SIZE;
+
+	spin_lock_irqsave(&qc->lock, flags);
+	bitmap_release_region(qc->bitmap, offset, 0);
+
+	zcache_qc_freed++;
+	zcache_qc_used--;
+	spin_unlock_irqrestore(&qc->lock, flags);
+}
+
+/*
+ * zbud helper functions
+ */
+
+static inline unsigned zbud_max_buddy_size(void)
+{
+	return MAX_CHUNK << CHUNK_SHIFT;
+}
+
+static inline unsigned zbud_size_to_chunks(unsigned size)
+{
+	BUG_ON(size == 0 || size > zbud_max_buddy_size());
+	return (size + CHUNK_SIZE - 1) >> CHUNK_SHIFT;
+}
+
+static inline int zbud_budnum(struct zbud_hdr *zh)
+{
+	unsigned offset = (unsigned long)zh & (PAGE_SIZE - 1);
+	struct zbud_page *zbpg = NULL;
+	unsigned budnum = -1U;
+	int i;
+
+	for (i = 0; i < ZBUD_MAX_BUDS; i++)
+		if (offset == offsetof(typeof(*zbpg), buddy[i])) {
+			budnum = i;
+			break;
+		}
+	BUG_ON(budnum == -1U);
+	return budnum;
+}
+
+static char *zbud_data(struct zbud_hdr *zh, unsigned size)
+{
+	struct zbud_page *zbpg;
+	char *p;
+	unsigned budnum;
+
+	ASSERT_SENTINEL(zh, ZBH);
+	budnum = zbud_budnum(zh);
+	BUG_ON(size == 0 || size > zbud_max_buddy_size());
+	zbpg = container_of(zh, struct zbud_page, buddy[budnum]);
+	p = (char *)zbpg;
+	if (budnum == 0)
+		p += ((sizeof(struct zbud_page) + CHUNK_SIZE - 1) &
+							CHUNK_MASK);
+	else if (budnum == 1)
+		p += PAGE_SIZE - ((size + CHUNK_SIZE - 1) & CHUNK_MASK);
+	return p;
+}
+
+/*
+ * zbud raw page management
+ */
+
+static struct zbud_page *zbud_alloc_raw_page(void)
+{
+	struct zbud_page *zbpg = NULL;
+	struct zbud_hdr *zh0, *zh1;
+
+	zbpg = zcache_get_free_page();
+	if (likely(zbpg != NULL)) {
+		INIT_LIST_HEAD(&zbpg->bud_list);
+		zh0 = &zbpg->buddy[0]; zh1 = &zbpg->buddy[1];
+		spin_lock_init(&zbpg->lock);
+		atomic_inc(&zcache_zbud_curr_raw_pages);
+		INIT_LIST_HEAD(&zbpg->bud_list);
+		SET_SENTINEL(zbpg, ZBPG);
+		zh0->size = 0; zh1->size = 0;
+		tmem_oid_set_invalid(&zh0->oid);
+		tmem_oid_set_invalid(&zh1->oid);
+	}
+	return zbpg;
+}
+
+static void zbud_free_raw_page(struct zbud_page *zbpg)
+{
+	struct zbud_hdr *zh0 = &zbpg->buddy[0], *zh1 = &zbpg->buddy[1];
+
+	ASSERT_SENTINEL(zbpg, ZBPG);
+	BUG_ON(!list_empty(&zbpg->bud_list));
+	BUG_ON(zh0->size != 0 || tmem_oid_valid(&zh0->oid));
+	BUG_ON(zh1->size != 0 || tmem_oid_valid(&zh1->oid));
+	INVERT_SENTINEL(zbpg, ZBPG);
+	spin_unlock(&zbpg->lock);
+	qcache_free(zbpg);
+}
+
+/*
+ * core zbud handling routines
+ */
+
+static unsigned zbud_free(struct zbud_hdr *zh)
+{
+	unsigned size;
+
+	ASSERT_SENTINEL(zh, ZBH);
+	BUG_ON(!tmem_oid_valid(&zh->oid));
+	size = zh->size;
+	BUG_ON(zh->size == 0 || zh->size > zbud_max_buddy_size());
+	zh->size = 0;
+	tmem_oid_set_invalid(&zh->oid);
+	INVERT_SENTINEL(zh, ZBH);
+	zcache_zbud_curr_zbytes -= size;
+	atomic_dec(&zcache_zbud_curr_zpages);
+	return size;
+}
+
+static void zbud_free_and_delist(struct zbud_hdr *zh)
+{
+	unsigned chunks;
+	struct zbud_hdr *zh_other;
+	unsigned budnum = zbud_budnum(zh), size;
+	struct zbud_page *zbpg =
+		container_of(zh, struct zbud_page, buddy[budnum]);
+
+	spin_lock(&zbpg->lock);
+	if (list_empty(&zbpg->bud_list)) {
+		spin_unlock(&zbpg->lock);
+		return;
+	}
+	size = zbud_free(zh);
+	zh_other = &zbpg->buddy[(budnum == 0) ? 1 : 0];
+	if (zh_other->size == 0) { /* was unbuddied: unlist and free */
+		chunks = zbud_size_to_chunks(size) ;
+		spin_lock(&zbud_budlists_spinlock);
+		BUG_ON(list_empty(&zbud_unbuddied[chunks].list));
+		list_del_init(&zbpg->bud_list);
+		zbud_unbuddied[chunks].count--;
+		spin_unlock(&zbud_budlists_spinlock);
+		zbud_free_raw_page(zbpg);
+	} else { /* was buddied: move remaining buddy to unbuddied list */
+		chunks = zbud_size_to_chunks(zh_other->size) ;
+		spin_lock(&zbud_budlists_spinlock);
+		list_del_init(&zbpg->bud_list);
+		zcache_zbud_buddied_count--;
+		list_add_tail(&zbpg->bud_list, &zbud_unbuddied[chunks].list);
+		zbud_unbuddied[chunks].count++;
+		spin_unlock(&zbud_budlists_spinlock);
+		spin_unlock(&zbpg->lock);
+	}
+}
+
+static struct zbud_hdr *zbud_create(uint16_t client_id, uint16_t pool_id,
+					struct tmem_oid *oid,
+					uint32_t index, struct page *page,
+					void *cdata, unsigned size)
+{
+	struct zbud_hdr *zh0, *zh1, *zh = NULL;
+	struct zbud_page *zbpg = NULL, *ztmp;
+	unsigned nchunks;
+	char *to;
+	int i, found_good_buddy = 0;
+
+	nchunks = zbud_size_to_chunks(size) ;
+	for (i = MAX_CHUNK - nchunks + 1; i > 0; i--) {
+		spin_lock(&zbud_budlists_spinlock);
+		if (!list_empty(&zbud_unbuddied[i].list)) {
+			list_for_each_entry_safe(zbpg, ztmp,
+				    &zbud_unbuddied[i].list, bud_list) {
+				if (spin_trylock(&zbpg->lock)) {
+					found_good_buddy = i;
+					goto found_unbuddied;
+				}
+			}
+		}
+		spin_unlock(&zbud_budlists_spinlock);
+	}
+	/* didn't find a good buddy, try allocating a new page */
+	zbpg = zbud_alloc_raw_page();
+	if (unlikely(zbpg == NULL))
+		goto out;
+	/* ok, have a page, now compress the data before taking locks */
+	spin_lock(&zbpg->lock);
+	spin_lock(&zbud_budlists_spinlock);
+	list_add_tail(&zbpg->bud_list, &zbud_unbuddied[nchunks].list);
+	zbud_unbuddied[nchunks].count++;
+	zh = &zbpg->buddy[0];
+	goto init_zh;
+
+found_unbuddied:
+	zh0 = &zbpg->buddy[0]; zh1 = &zbpg->buddy[1];
+	BUG_ON(!((zh0->size == 0) ^ (zh1->size == 0)));
+	if (zh0->size != 0) { /* buddy0 in use, buddy1 is vacant */
+		ASSERT_SENTINEL(zh0, ZBH);
+		zh = zh1;
+	} else if (zh1->size != 0) { /* buddy1 in use, buddy0 is vacant */
+		ASSERT_SENTINEL(zh1, ZBH);
+		zh = zh0;
+	} else
+		BUG();
+	list_del_init(&zbpg->bud_list);
+	zbud_unbuddied[found_good_buddy].count--;
+	list_add_tail(&zbpg->bud_list, &zbud_buddied_list);
+	zcache_zbud_buddied_count++;
+
+init_zh:
+	SET_SENTINEL(zh, ZBH);
+	zh->size = size;
+	zh->index = index;
+	zh->oid = *oid;
+	zh->pool_id = pool_id;
+	zh->client_id = client_id;
+	/* can wait to copy the data until the list locks are dropped */
+	spin_unlock(&zbud_budlists_spinlock);
+
+	to = zbud_data(zh, size);
+	memcpy(to, cdata, size);
+	spin_unlock(&zbpg->lock);
+	zbud_cumul_chunk_counts[nchunks]++;
+	atomic_inc(&zcache_zbud_curr_zpages);
+	zcache_zbud_cumul_zpages++;
+	zcache_zbud_curr_zbytes += size;
+	zcache_zbud_cumul_zbytes += size;
+out:
+	return zh;
+}
+
+static int zbud_decompress(struct page *page, struct zbud_hdr *zh)
+{
+	struct zbud_page *zbpg;
+	unsigned budnum = zbud_budnum(zh);
+	size_t out_len = PAGE_SIZE;
+	char *to_va, *from_va;
+	unsigned size;
+	int ret = 0;
+
+	zbpg = container_of(zh, struct zbud_page, buddy[budnum]);
+	spin_lock(&zbpg->lock);
+	if (list_empty(&zbpg->bud_list)) {
+		ret = -EINVAL;
+		goto out;
+	}
+	ASSERT_SENTINEL(zh, ZBH);
+	BUG_ON(zh->size == 0 || zh->size > zbud_max_buddy_size());
+	to_va = kmap_atomic(page);
+	size = zh->size;
+	from_va = zbud_data(zh, size);
+	ret = lzo1x_decompress_safe(from_va, size, to_va, &out_len);
+	BUG_ON(ret != LZO_E_OK);
+	BUG_ON(out_len != PAGE_SIZE);
+	kunmap_atomic(to_va);
+out:
+	spin_unlock(&zbpg->lock);
+	return ret;
+}
+
+static struct tmem_pool *zcache_get_pool_by_id(uint16_t cli_id,
+						uint16_t poolid);
+static void zcache_put_pool(struct tmem_pool *pool);
+
+static void zbud_init(void)
+{
+	int i;
+
+	INIT_LIST_HEAD(&zbud_buddied_list);
+	zcache_zbud_buddied_count = 0;
+	for (i = 0; i < NCHUNKS; i++) {
+		INIT_LIST_HEAD(&zbud_unbuddied[i].list);
+		zbud_unbuddied[i].count = 0;
+	}
+}
+
+#ifdef CONFIG_SYSFS
+/*
+ * These sysfs routines show a nice distribution of how many zbpg's are
+ * currently (and have ever been placed) in each unbuddied list.  It's fun
+ * to watch but can probably go away before final merge.
+ */
+static int zbud_show_unbuddied_list_counts(char *buf)
+{
+	int i;
+	char *p = buf;
+
+	for (i = 0; i < NCHUNKS; i++)
+		p += sprintf(p, "%u ", zbud_unbuddied[i].count);
+	return p - buf;
+}
+
+static int zbud_show_cumul_chunk_counts(char *buf)
+{
+	unsigned long i, chunks = 0, total_chunks = 0, sum_total_chunks = 0;
+	unsigned long total_chunks_lte_21 = 0, total_chunks_lte_32 = 0;
+	unsigned long total_chunks_lte_42 = 0;
+	char *p = buf;
+
+	for (i = 0; i < NCHUNKS; i++) {
+		p += sprintf(p, "%lu ", zbud_cumul_chunk_counts[i]);
+		chunks += zbud_cumul_chunk_counts[i];
+		total_chunks += zbud_cumul_chunk_counts[i];
+		sum_total_chunks += i * zbud_cumul_chunk_counts[i];
+		if (i == 21)
+			total_chunks_lte_21 = total_chunks;
+		if (i == 32)
+			total_chunks_lte_32 = total_chunks;
+		if (i == 42)
+			total_chunks_lte_42 = total_chunks;
+	}
+	p += sprintf(p, "<=21:%lu <=32:%lu <=42:%lu, mean:%lu\n",
+		total_chunks_lte_21, total_chunks_lte_32, total_chunks_lte_42,
+		chunks == 0 ? 0 : sum_total_chunks / chunks);
+	return p - buf;
+}
+#endif
+
+/*
+ * zcache core code starts here
+ */
+
+/* useful stats not collected by cleancache or frontswap */
+static unsigned long zcache_flush_total;
+static unsigned long zcache_flush_found;
+static unsigned long zcache_flobj_total;
+static unsigned long zcache_flobj_found;
+static unsigned long zcache_failed_eph_puts;
+
+/*
+ * Tmem operations assume the poolid implies the invoking client.
+ * Zcache only has one client (the kernel itself): LOCAL_CLIENT.
+ * RAMster has each client numbered by cluster node, and a KVM version
+ * of zcache would have one client per guest and each client might
+ * have a poolid==N.
+ */
+static struct tmem_pool *zcache_get_pool_by_id(uint16_t cli_id, uint16_t poolid)
+{
+	struct tmem_pool *pool = NULL;
+	struct zcache_client *cli = NULL;
+
+	if (cli_id == LOCAL_CLIENT)
+		cli = &zcache_host;
+	else {
+		if (cli_id >= MAX_CLIENTS)
+			goto out;
+		cli = &zcache_clients[cli_id];
+		if (cli == NULL)
+			goto out;
+		atomic_inc(&cli->refcount);
+	}
+	if (poolid < MAX_POOLS_PER_CLIENT) {
+		pool = cli->tmem_pools[poolid];
+		if (pool != NULL)
+			atomic_inc(&pool->refcount);
+	}
+out:
+	return pool;
+}
+
+static void zcache_put_pool(struct tmem_pool *pool)
+{
+	struct zcache_client *cli = NULL;
+
+	if (pool == NULL)
+		BUG();
+	cli = pool->client;
+	atomic_dec(&pool->refcount);
+	atomic_dec(&cli->refcount);
+}
+
+int zcache_new_client(uint16_t cli_id)
+{
+	struct zcache_client *cli = NULL;
+	int ret = -1;
+
+	if (cli_id == LOCAL_CLIENT)
+		cli = &zcache_host;
+	else if ((unsigned int)cli_id < MAX_CLIENTS)
+		cli = &zcache_clients[cli_id];
+	if (cli == NULL)
+		goto out;
+	if (cli->allocated)
+		goto out;
+	cli->allocated = 1;
+	ret = 0;
+out:
+	return ret;
+}
+
+/* counters for debugging */
+static unsigned long zcache_failed_get_free_pages;
+static unsigned long zcache_failed_alloc;
+static unsigned long zcache_put_to_flush;
+static unsigned long zcache_aborted_preload;
+static unsigned long zcache_aborted_shrink;
+
+/*
+ * Ensure that memory allocation requests in zcache don't result
+ * in direct reclaim requests via the shrinker, which would cause
+ * an infinite loop.  Maybe a GFP flag would be better?
+ */
+static DEFINE_SPINLOCK(zcache_direct_reclaim_lock);
+
+/*
+ * for now, used named slabs so can easily track usage; later can
+ * either just use kmalloc, or perhaps add a slab-like allocator
+ * to more carefully manage total memory utilization
+ */
+static struct kmem_cache *zcache_objnode_cache;
+static struct kmem_cache *zcache_obj_cache;
+static atomic_t zcache_curr_obj_count = ATOMIC_INIT(0);
+static unsigned long zcache_curr_obj_count_max;
+static atomic_t zcache_curr_objnode_count = ATOMIC_INIT(0);
+static unsigned long zcache_curr_objnode_count_max;
+
+/*
+ * to avoid memory allocation recursion (e.g. due to direct reclaim), we
+ * preload all necessary data structures so the hostops callbacks never
+ * actually do a malloc
+ */
+struct zcache_preload {
+	void *page;
+	struct tmem_obj *obj;
+	int nr;
+	struct tmem_objnode *objnodes[OBJNODE_TREE_MAX_PATH];
+};
+static DEFINE_PER_CPU(struct zcache_preload, zcache_preloads) = { 0, };
+
+static int zcache_do_preload(struct tmem_pool *pool)
+{
+	struct zcache_preload *kp;
+	struct tmem_objnode *objnode;
+	struct tmem_obj *obj;
+	void *page;
+	int ret = -ENOMEM;
+
+	if (unlikely(zcache_objnode_cache == NULL))
+		goto out;
+	if (unlikely(zcache_obj_cache == NULL))
+		goto out;
+	if (!spin_trylock(&zcache_direct_reclaim_lock)) {
+		zcache_aborted_preload++;
+		goto out;
+	}
+	preempt_disable();
+	kp = &__get_cpu_var(zcache_preloads);
+	while (kp->nr < ARRAY_SIZE(kp->objnodes)) {
+		preempt_enable_no_resched();
+		objnode = kmem_cache_alloc(zcache_objnode_cache,
+				ZCACHE_GFP_MASK);
+		if (unlikely(objnode == NULL)) {
+			zcache_failed_alloc++;
+			goto unlock_out;
+		}
+		preempt_disable();
+		kp = &__get_cpu_var(zcache_preloads);
+		if (kp->nr < ARRAY_SIZE(kp->objnodes))
+			kp->objnodes[kp->nr++] = objnode;
+		else
+			kmem_cache_free(zcache_objnode_cache, objnode);
+	}
+	preempt_enable_no_resched();
+	obj = kmem_cache_alloc(zcache_obj_cache, ZCACHE_GFP_MASK);
+	if (unlikely(obj == NULL)) {
+		zcache_failed_alloc++;
+		goto unlock_out;
+	}
+	page = qcache_alloc();
+	if (unlikely(page == NULL)) {
+		zcache_failed_get_free_pages++;
+		kmem_cache_free(zcache_obj_cache, obj);
+		goto unlock_out;
+	}
+	preempt_disable();
+	kp = &__get_cpu_var(zcache_preloads);
+	if (kp->obj == NULL)
+		kp->obj = obj;
+	else
+		kmem_cache_free(zcache_obj_cache, obj);
+	if (kp->page == NULL)
+		kp->page = page;
+	else
+		qcache_free(page);
+	ret = 0;
+unlock_out:
+	spin_unlock(&zcache_direct_reclaim_lock);
+out:
+	return ret;
+}
+
+static void *zcache_get_free_page(void)
+{
+	struct zcache_preload *kp;
+	void *page;
+
+	kp = &__get_cpu_var(zcache_preloads);
+	page = kp->page;
+	BUG_ON(page == NULL);
+	kp->page = NULL;
+	return page;
+}
+
+/*
+ * zcache implementation for tmem host ops
+ */
+
+static struct tmem_objnode *zcache_objnode_alloc(struct tmem_pool *pool)
+{
+	struct tmem_objnode *objnode = NULL;
+	unsigned long count;
+	struct zcache_preload *kp;
+
+	kp = &__get_cpu_var(zcache_preloads);
+	if (kp->nr <= 0)
+		goto out;
+	objnode = kp->objnodes[kp->nr - 1];
+	BUG_ON(objnode == NULL);
+	kp->objnodes[kp->nr - 1] = NULL;
+	kp->nr--;
+	count = atomic_inc_return(&zcache_curr_objnode_count);
+	if (count > zcache_curr_objnode_count_max)
+		zcache_curr_objnode_count_max = count;
+out:
+	return objnode;
+}
+
+static void zcache_objnode_free(struct tmem_objnode *objnode,
+					struct tmem_pool *pool)
+{
+	atomic_dec(&zcache_curr_objnode_count);
+	BUG_ON(atomic_read(&zcache_curr_objnode_count) < 0);
+	kmem_cache_free(zcache_objnode_cache, objnode);
+}
+
+static struct tmem_obj *zcache_obj_alloc(struct tmem_pool *pool)
+{
+	struct tmem_obj *obj = NULL;
+	unsigned long count;
+	struct zcache_preload *kp;
+
+	kp = &__get_cpu_var(zcache_preloads);
+	obj = kp->obj;
+	BUG_ON(obj == NULL);
+	kp->obj = NULL;
+	count = atomic_inc_return(&zcache_curr_obj_count);
+	if (count > zcache_curr_obj_count_max)
+		zcache_curr_obj_count_max = count;
+	return obj;
+}
+
+static void zcache_obj_free(struct tmem_obj *obj, struct tmem_pool *pool)
+{
+	atomic_dec(&zcache_curr_obj_count);
+	BUG_ON(atomic_read(&zcache_curr_obj_count) < 0);
+	kmem_cache_free(zcache_obj_cache, obj);
+}
+
+static void zcache_flush_all_obj(void)
+{
+	struct tmem_pool *pool;
+	int pool_id;
+	struct zcache_preload *kp;
+
+	kp = &__get_cpu_var(zcache_preloads);
+
+	for (pool_id = 0; pool_id < MAX_POOLS_PER_CLIENT; pool_id++) {
+		pool = zcache_get_pool_by_id(LOCAL_CLIENT, pool_id);
+		tmem_flush_pool(pool);
+		if (pool)
+			zcache_put_pool(pool);
+	}
+	if (kp->page) {
+		qcache_free(kp->page);
+		kp->page = NULL;
+	}
+	if (zcache_qc_used)
+		pr_warn("pages used not 0 after qcache flush all, is %ld\n",
+			zcache_qc_used);
+}
+
+/*
+ * When zcache is disabled ("frozen"), pools can be created and destroyed,
+ * but all puts (and thus all other operations that require memory allocation)
+ * must fail.  If zcache is unfrozen, accepts puts, then frozen again,
+ * data consistency requires all puts while frozen to be converted into
+ * flushes.
+ */
+static bool zcache_freeze;
+
+static void zcache_control(bool freeze)
+{
+	zcache_freeze = freeze;
+}
+
+static struct tmem_hostops zcache_hostops = {
+	.obj_alloc = zcache_obj_alloc,
+	.obj_free = zcache_obj_free,
+	.objnode_alloc = zcache_objnode_alloc,
+	.objnode_free = zcache_objnode_free,
+	.flush_all_obj = zcache_flush_all_obj,
+	.control = zcache_control,
+};
+
+/*
+ * zcache implementations for PAM page descriptor ops
+ */
+
+static atomic_t zcache_curr_eph_pampd_count = ATOMIC_INIT(0);
+static unsigned long zcache_curr_eph_pampd_count_max;
+
+/* forward reference */
+static int zcache_compress(struct page *from, void **out_va, size_t *out_len);
+
+static void *zcache_pampd_create(char *data, size_t size, bool raw, int eph,
+				struct tmem_pool *pool, struct tmem_oid *oid,
+				 uint32_t index)
+{
+	void *pampd = NULL, *cdata;
+	size_t clen;
+	int ret;
+	unsigned long count;
+	struct page *page = (struct page *)(data);
+	struct zcache_client *cli = pool->client;
+	uint16_t client_id = get_client_id_from_client(cli);
+
+	ret = zcache_compress(page, &cdata, &clen);
+	if (ret == 0)
+		goto out;
+	if (clen == 0 || clen > zbud_max_buddy_size()) {
+		zcache_compress_poor++;
+		goto out;
+	}
+	pampd = (void *)zbud_create(client_id, pool->pool_id, oid,
+					index, page, cdata, clen);
+	if (pampd != NULL) {
+		count = atomic_inc_return(&zcache_curr_eph_pampd_count);
+		if (count > zcache_curr_eph_pampd_count_max)
+			zcache_curr_eph_pampd_count_max = count;
+	}
+out:
+	return pampd;
+}
+
+/*
+ * fill the pageframe corresponding to the struct page with the data
+ * from the passed pampd
+ */
+static int zcache_pampd_get_data(char *data, size_t *bufsize, bool raw,
+					void *pampd, struct tmem_pool *pool,
+					struct tmem_oid *oid, uint32_t index)
+{
+	BUG();
+	return 0;
+}
+
+/*
+ * fill the pageframe corresponding to the struct page with the data
+ * from the passed pampd
+ */
+static int zcache_pampd_get_data_and_free(char *data, size_t *bufsize, bool raw,
+					void *pampd, struct tmem_pool *pool,
+					struct tmem_oid *oid, uint32_t index)
+{
+	int ret = 0;
+
+	zbud_decompress((struct page *)(data), pampd);
+	zbud_free_and_delist((struct zbud_hdr *)pampd);
+	atomic_dec(&zcache_curr_eph_pampd_count);
+	return ret;
+}
+
+/*
+ * free the pampd and remove it from any zcache lists
+ * pampd must no longer be pointed to from any tmem data structures!
+ */
+static void zcache_pampd_free(void *pampd, struct tmem_pool *pool,
+				struct tmem_oid *oid, uint32_t index)
+{
+	zbud_free_and_delist((struct zbud_hdr *)pampd);
+	atomic_dec(&zcache_curr_eph_pampd_count);
+	BUG_ON(atomic_read(&zcache_curr_eph_pampd_count) < 0);
+}
+
+static void zcache_pampd_free_obj(struct tmem_pool *pool, struct tmem_obj *obj)
+{
+}
+
+static void zcache_pampd_new_obj(struct tmem_obj *obj)
+{
+}
+
+static int zcache_pampd_replace_in_obj(void *pampd, struct tmem_obj *obj)
+{
+	return -1;
+}
+
+static bool zcache_pampd_is_remote(void *pampd)
+{
+	return 0;
+}
+
+static struct tmem_pamops zcache_pamops = {
+	.create = zcache_pampd_create,
+	.get_data = zcache_pampd_get_data,
+	.get_data_and_free = zcache_pampd_get_data_and_free,
+	.free = zcache_pampd_free,
+	.free_obj = zcache_pampd_free_obj,
+	.new_obj = zcache_pampd_new_obj,
+	.replace_in_obj = zcache_pampd_replace_in_obj,
+	.is_remote = zcache_pampd_is_remote,
+};
+
+/*
+ * zcache compression/decompression and related per-cpu stuff
+ */
+
+#define LZO_WORKMEM_BYTES LZO1X_1_MEM_COMPRESS
+#define LZO_DSTMEM_PAGE_ORDER 1
+static DEFINE_PER_CPU(unsigned char *, zcache_workmem);
+static DEFINE_PER_CPU(unsigned char *, zcache_dstmem);
+
+static int zcache_compress(struct page *from, void **out_va, size_t *out_len)
+{
+	int ret = 0;
+	unsigned char *dmem = __get_cpu_var(zcache_dstmem);
+	unsigned char *wmem = __get_cpu_var(zcache_workmem);
+	char *from_va;
+
+	BUG_ON(!irqs_disabled());
+	if (unlikely(dmem == NULL || wmem == NULL))
+		goto out;  /* no buffer, so can't compress */
+	from_va = kmap_atomic(from);
+	mb();
+	ret = lzo1x_1_compress(from_va, PAGE_SIZE, dmem, out_len, wmem);
+	BUG_ON(ret != LZO_E_OK);
+	*out_va = dmem;
+	kunmap_atomic(from_va);
+	ret = 1;
+out:
+	return ret;
+}
+
+#ifdef CONFIG_SYSFS
+#define ZCACHE_SYSFS_RO(_name) \
+	static ssize_t zcache_##_name##_show(struct kobject *kobj, \
+				struct kobj_attribute *attr, char *buf) \
+	{ \
+		return sprintf(buf, "%lu\n", zcache_##_name); \
+	} \
+	static struct kobj_attribute zcache_##_name##_attr = { \
+		.attr = { .name = __stringify(_name), .mode = 0444 }, \
+		.show = zcache_##_name##_show, \
+	}
+
+#define ZCACHE_SYSFS_RO_ATOMIC(_name) \
+	static ssize_t zcache_##_name##_show(struct kobject *kobj, \
+				struct kobj_attribute *attr, char *buf) \
+	{ \
+	    return sprintf(buf, "%d\n", atomic_read(&zcache_##_name)); \
+	} \
+	static struct kobj_attribute zcache_##_name##_attr = { \
+		.attr = { .name = __stringify(_name), .mode = 0444 }, \
+		.show = zcache_##_name##_show, \
+	}
+
+#define ZCACHE_SYSFS_RO_CUSTOM(_name, _func) \
+	static ssize_t zcache_##_name##_show(struct kobject *kobj, \
+				struct kobj_attribute *attr, char *buf) \
+	{ \
+	    return _func(buf); \
+	} \
+	static struct kobj_attribute zcache_##_name##_attr = { \
+		.attr = { .name = __stringify(_name), .mode = 0444 }, \
+		.show = zcache_##_name##_show, \
+	}
+
+ZCACHE_SYSFS_RO(curr_obj_count_max);
+ZCACHE_SYSFS_RO(curr_objnode_count_max);
+ZCACHE_SYSFS_RO(flush_total);
+ZCACHE_SYSFS_RO(flush_found);
+ZCACHE_SYSFS_RO(flobj_total);
+ZCACHE_SYSFS_RO(flobj_found);
+ZCACHE_SYSFS_RO(failed_eph_puts);
+ZCACHE_SYSFS_RO(zbud_curr_zbytes);
+ZCACHE_SYSFS_RO(zbud_cumul_zpages);
+ZCACHE_SYSFS_RO(zbud_cumul_zbytes);
+ZCACHE_SYSFS_RO(zbud_buddied_count);
+ZCACHE_SYSFS_RO(failed_get_free_pages);
+ZCACHE_SYSFS_RO(failed_alloc);
+ZCACHE_SYSFS_RO(put_to_flush);
+ZCACHE_SYSFS_RO(aborted_preload);
+ZCACHE_SYSFS_RO(aborted_shrink);
+ZCACHE_SYSFS_RO(compress_poor);
+ZCACHE_SYSFS_RO(mean_compress_poor);
+ZCACHE_SYSFS_RO(qc_allocated);
+ZCACHE_SYSFS_RO(qc_freed);
+ZCACHE_SYSFS_RO(qc_used);
+ZCACHE_SYSFS_RO(qc_max_used);
+ZCACHE_SYSFS_RO_ATOMIC(zbud_curr_raw_pages);
+ZCACHE_SYSFS_RO_ATOMIC(zbud_curr_zpages);
+ZCACHE_SYSFS_RO_ATOMIC(curr_obj_count);
+ZCACHE_SYSFS_RO_ATOMIC(curr_objnode_count);
+ZCACHE_SYSFS_RO_CUSTOM(zbud_unbuddied_list_counts,
+			zbud_show_unbuddied_list_counts);
+ZCACHE_SYSFS_RO_CUSTOM(zbud_cumul_chunk_counts,
+			zbud_show_cumul_chunk_counts);
+
+static struct attribute *qcache_attrs[] = {
+	&zcache_curr_obj_count_attr.attr,
+	&zcache_curr_obj_count_max_attr.attr,
+	&zcache_curr_objnode_count_attr.attr,
+	&zcache_curr_objnode_count_max_attr.attr,
+	&zcache_flush_total_attr.attr,
+	&zcache_flobj_total_attr.attr,
+	&zcache_flush_found_attr.attr,
+	&zcache_flobj_found_attr.attr,
+	&zcache_failed_eph_puts_attr.attr,
+	&zcache_compress_poor_attr.attr,
+	&zcache_mean_compress_poor_attr.attr,
+	&zcache_zbud_curr_raw_pages_attr.attr,
+	&zcache_zbud_curr_zpages_attr.attr,
+	&zcache_zbud_curr_zbytes_attr.attr,
+	&zcache_zbud_cumul_zpages_attr.attr,
+	&zcache_zbud_cumul_zbytes_attr.attr,
+	&zcache_zbud_buddied_count_attr.attr,
+	&zcache_failed_get_free_pages_attr.attr,
+	&zcache_failed_alloc_attr.attr,
+	&zcache_put_to_flush_attr.attr,
+	&zcache_aborted_preload_attr.attr,
+	&zcache_aborted_shrink_attr.attr,
+	&zcache_zbud_unbuddied_list_counts_attr.attr,
+	&zcache_zbud_cumul_chunk_counts_attr.attr,
+	&zcache_qc_allocated_attr.attr,
+	&zcache_qc_freed_attr.attr,
+	&zcache_qc_used_attr.attr,
+	&zcache_qc_max_used_attr.attr,
+	NULL,
+};
+
+static struct attribute_group qcache_attr_group = {
+	.attrs = qcache_attrs,
+	.name = "qcache",
+};
+
+#endif /* CONFIG_SYSFS */
+
+/*
+ * zcache shims between cleancache ops and tmem
+ */
+
+static int zcache_put_page(int cli_id, int pool_id, struct tmem_oid *oidp,
+				uint32_t index, struct page *page)
+{
+	struct tmem_pool *pool;
+	int ret = -1;
+
+	BUG_ON(!irqs_disabled());
+	pool = zcache_get_pool_by_id(cli_id, pool_id);
+	if (unlikely(pool == NULL))
+		goto out;
+	if (!zcache_freeze && zcache_do_preload(pool) == 0) {
+		/* preload does preempt_disable on success */
+		ret = tmem_put(pool, oidp, index, (char *)(page),
+				PAGE_SIZE, 0, is_ephemeral(pool));
+		if (ret < 0) {
+			zcache_failed_eph_puts++;
+		}
+		zcache_put_pool(pool);
+		preempt_enable_no_resched();
+	} else {
+		zcache_put_to_flush++;
+		if (atomic_read(&pool->obj_count) > 0)
+			/* the put fails whether the flush succeeds or not */
+			(void)tmem_flush_page(pool, oidp, index);
+		zcache_put_pool(pool);
+	}
+out:
+	return ret;
+}
+
+static int zcache_get_page(int cli_id, int pool_id, struct tmem_oid *oidp,
+				uint32_t index, struct page *page)
+{
+	struct tmem_pool *pool;
+	int ret = -1;
+	unsigned long flags;
+	size_t size = PAGE_SIZE;
+
+	local_irq_save(flags);
+	pool = zcache_get_pool_by_id(cli_id, pool_id);
+	if (likely(pool != NULL)) {
+		if (atomic_read(&pool->obj_count) > 0)
+			ret = tmem_get(pool, oidp, index, (char *)(page),
+					&size, 0, is_ephemeral(pool));
+		zcache_put_pool(pool);
+	}
+	local_irq_restore(flags);
+	return ret;
+}
+
+static int zcache_flush_page(int cli_id, int pool_id,
+				struct tmem_oid *oidp, uint32_t index)
+{
+	struct tmem_pool *pool;
+	int ret = -1;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	zcache_flush_total++;
+	pool = zcache_get_pool_by_id(cli_id, pool_id);
+	if (likely(pool != NULL)) {
+		if (atomic_read(&pool->obj_count) > 0)
+			ret = tmem_flush_page(pool, oidp, index);
+		zcache_put_pool(pool);
+	}
+	if (ret >= 0)
+		zcache_flush_found++;
+	local_irq_restore(flags);
+	return ret;
+}
+
+static int zcache_flush_object(int cli_id, int pool_id,
+				struct tmem_oid *oidp)
+{
+	struct tmem_pool *pool;
+	int ret = -1;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	zcache_flobj_total++;
+	pool = zcache_get_pool_by_id(cli_id, pool_id);
+	if (likely(pool != NULL)) {
+		if (atomic_read(&pool->obj_count) > 0)
+			ret = tmem_flush_object(pool, oidp);
+		zcache_put_pool(pool);
+	}
+	if (ret >= 0)
+		zcache_flobj_found++;
+	local_irq_restore(flags);
+	return ret;
+}
+
+static int zcache_destroy_pool(int cli_id, int pool_id)
+{
+	struct tmem_pool *pool = NULL;
+	struct zcache_client *cli = NULL;
+	int ret = -1;
+
+	if (pool_id < 0)
+		goto out;
+	if (cli_id == LOCAL_CLIENT)
+		cli = &zcache_host;
+	else if ((unsigned int)cli_id < MAX_CLIENTS)
+		cli = &zcache_clients[cli_id];
+	if (cli == NULL)
+		goto out;
+	atomic_inc(&cli->refcount);
+	pool = cli->tmem_pools[pool_id];
+	if (pool == NULL)
+		goto out;
+	cli->tmem_pools[pool_id] = NULL;
+	/* wait for pool activity on other cpus to quiesce */
+	while (atomic_read(&pool->refcount) != 0)
+		;
+	atomic_dec(&cli->refcount);
+	local_bh_disable();
+	ret = tmem_destroy_pool(pool);
+	local_bh_enable();
+	kfree(pool);
+	pr_info("qcache: destroyed pool id=%d, cli_id=%d\n",
+			pool_id, cli_id);
+out:
+	return ret;
+}
+
+static int zcache_new_pool(uint16_t cli_id, uint32_t flags)
+{
+	int poolid = -1;
+	struct tmem_pool *pool;
+	struct zcache_client *cli = NULL;
+
+	if (cli_id == LOCAL_CLIENT)
+		cli = &zcache_host;
+	else if ((unsigned int)cli_id < MAX_CLIENTS)
+		cli = &zcache_clients[cli_id];
+	if (cli == NULL)
+		goto out;
+	atomic_inc(&cli->refcount);
+	pool = kmalloc(sizeof(struct tmem_pool), GFP_KERNEL);
+	if (pool == NULL) {
+		pr_info("qcache: pool creation failed: out of memory\n");
+		goto out;
+	}
+
+	for (poolid = 0; poolid < MAX_POOLS_PER_CLIENT; poolid++)
+		if (cli->tmem_pools[poolid] == NULL)
+			break;
+	if (poolid >= MAX_POOLS_PER_CLIENT) {
+		pr_info("qcache: pool creation failed: max exceeded\n");
+		kfree(pool);
+		poolid = -1;
+		goto out;
+	}
+	atomic_set(&pool->refcount, 0);
+	pool->client = cli;
+	pool->pool_id = poolid;
+	tmem_new_pool(pool, flags);
+	cli->tmem_pools[poolid] = pool;
+	pr_info("qcache: created %s tmem pool, id=%d, client=%d\n",
+		flags & TMEM_POOL_PERSIST ? "persistent" : "ephemeral",
+		poolid, cli_id);
+out:
+	if (cli != NULL)
+		atomic_dec(&cli->refcount);
+	return poolid;
+}
+
+/**********
+ * Two kernel functionalities currently can be layered on top of tmem.
+ * These are "cleancache" which is used as a second-chance cache for clean
+ * page cache pages; and "frontswap" which is used for swap pages
+ * to avoid writes to disk.  A generic "shim" is provided here for each
+ * to translate in-kernel semantics to zcache semantics.
+ */
+
+static void zcache_cleancache_put_page(int pool_id,
+					struct cleancache_filekey key,
+					pgoff_t index, struct page *page)
+{
+	u32 ind = (u32) index;
+	struct tmem_oid oid = *(struct tmem_oid *)&key;
+
+	if (likely(ind == index))
+		(void)zcache_put_page(LOCAL_CLIENT, pool_id, &oid, index, page);
+}
+
+static int zcache_cleancache_get_page(int pool_id,
+					struct cleancache_filekey key,
+					pgoff_t index, struct page *page)
+{
+	u32 ind = (u32) index;
+	struct tmem_oid oid = *(struct tmem_oid *)&key;
+	int ret = -1;
+
+	if (likely(ind == index))
+		ret = zcache_get_page(LOCAL_CLIENT, pool_id, &oid, index, page);
+	return ret;
+}
+
+static void zcache_cleancache_flush_page(int pool_id,
+					struct cleancache_filekey key,
+					pgoff_t index)
+{
+	u32 ind = (u32) index;
+	struct tmem_oid oid = *(struct tmem_oid *)&key;
+
+	if (likely(ind == index))
+		(void)zcache_flush_page(LOCAL_CLIENT, pool_id, &oid, ind);
+}
+
+static void zcache_cleancache_flush_inode(int pool_id,
+					struct cleancache_filekey key)
+{
+	struct tmem_oid oid = *(struct tmem_oid *)&key;
+
+	(void)zcache_flush_object(LOCAL_CLIENT, pool_id, &oid);
+}
+
+static void zcache_cleancache_flush_fs(int pool_id)
+{
+	if (pool_id >= 0)
+		(void)zcache_destroy_pool(LOCAL_CLIENT, pool_id);
+}
+
+static int zcache_cleancache_init_fs(size_t pagesize)
+{
+	BUG_ON(sizeof(struct cleancache_filekey) !=
+				sizeof(struct tmem_oid));
+	BUG_ON(pagesize != PAGE_SIZE);
+	return zcache_new_pool(LOCAL_CLIENT, 0);
+}
+
+static int zcache_cleancache_init_shared_fs(char *uuid, size_t pagesize)
+{
+	/* shared pools are unsupported and map to private */
+	BUG_ON(sizeof(struct cleancache_filekey) !=
+				sizeof(struct tmem_oid));
+	BUG_ON(pagesize != PAGE_SIZE);
+	return zcache_new_pool(LOCAL_CLIENT, 0);
+}
+
+static struct cleancache_ops zcache_cleancache_ops = {
+	.put_page = zcache_cleancache_put_page,
+	.get_page = zcache_cleancache_get_page,
+	.invalidate_page = zcache_cleancache_flush_page,
+	.invalidate_inode = zcache_cleancache_flush_inode,
+	.invalidate_fs = zcache_cleancache_flush_fs,
+	.init_shared_fs = zcache_cleancache_init_shared_fs,
+	.init_fs = zcache_cleancache_init_fs
+};
+
+struct cleancache_ops zcache_cleancache_register_ops(void)
+{
+	struct cleancache_ops old_ops =
+		cleancache_register_ops(&zcache_cleancache_ops);
+
+	return old_ops;
+}
+
+static int __init qcache_init(void)
+{
+	int ret = 0;
+	struct qcache_info *qc = &qcache_info;
+	struct fmem_data *fdp;
+	int bitmap_size;
+	unsigned int cpu;
+	struct cleancache_ops old_ops;
+
+#ifdef CONFIG_SYSFS
+	ret = sysfs_create_group(mm_kobj, &qcache_attr_group);
+	if (ret) {
+		pr_err("qcache: can't create sysfs\n");
+		goto out;
+	}
+#endif /* CONFIG_SYSFS */
+
+	fdp = fmem_get_info();
+	qc->addr = fdp->virt;
+	qc->pages = fdp->size >> PAGE_SHIFT;
+	if (!qc->pages)
+		goto out;
+
+	tmem_register_hostops(&zcache_hostops);
+	tmem_register_pamops(&zcache_pamops);
+	for_each_online_cpu(cpu) {
+		per_cpu(zcache_dstmem, cpu) = (void *)__get_free_pages(
+			GFP_KERNEL | __GFP_REPEAT,
+			LZO_DSTMEM_PAGE_ORDER),
+		per_cpu(zcache_workmem, cpu) =
+			kzalloc(LZO1X_MEM_COMPRESS,
+				GFP_KERNEL | __GFP_REPEAT);
+	}
+	zcache_objnode_cache = kmem_cache_create("zcache_objnode",
+				sizeof(struct tmem_objnode), 0, 0, NULL);
+	zcache_obj_cache = kmem_cache_create("zcache_obj",
+				sizeof(struct tmem_obj), 0, 0, NULL);
+	ret = zcache_new_client(LOCAL_CLIENT);
+	if (ret) {
+		pr_err("qcache: can't create client\n");
+		goto out;
+	}
+
+	zbud_init();
+	old_ops = zcache_cleancache_register_ops();
+	pr_info("qcache: cleancache enabled using kernel "
+		"transcendent memory and compression buddies\n");
+	if (old_ops.init_fs != NULL)
+		pr_warning("qcache: cleancache_ops overridden");
+
+
+	bitmap_size = BITS_TO_LONGS(qc->pages) * sizeof(long);
+
+	qc->bitmap = kzalloc(bitmap_size, GFP_KERNEL);
+	if (!qc->bitmap) {
+		pr_info("can't allocate qcache bitmap!\n");
+		ret = -ENOMEM;
+		goto out;
+	}
+	spin_lock_init(&qc->lock);
+
+	fmem_set_state(FMEM_T_STATE);
+
+out:
+	return ret;
+}
+
+module_init(qcache_init)
diff --git a/drivers/staging/qcache/tmem.c b/drivers/staging/qcache/tmem.c
new file mode 100644
index 0000000..e5c3f30
--- /dev/null
+++ b/drivers/staging/qcache/tmem.c
@@ -0,0 +1,833 @@
+/*
+ * In-kernel transcendent memory (generic implementation)
+ *
+ * Copyright (c) 2009-2011, Dan Magenheimer, Oracle Corp.
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * The primary purpose of Transcedent Memory ("tmem") is to map object-oriented
+ * "handles" (triples containing a pool id, and object id, and an index), to
+ * pages in a page-accessible memory (PAM).  Tmem references the PAM pages via
+ * an abstract "pampd" (PAM page-descriptor), which can be operated on by a
+ * set of functions (pamops).  Each pampd contains some representation of
+ * PAGE_SIZE bytes worth of data. Tmem must support potentially millions of
+ * pages and must be able to insert, find, and delete these pages at a
+ * potential frequency of thousands per second concurrently across many CPUs,
+ * (and, if used with KVM, across many vcpus across many guests).
+ * Tmem is tracked with a hierarchy of data structures, organized by
+ * the elements in a handle-tuple: pool_id, object_id, and page index.
+ * One or more "clients" (e.g. guests) each provide one or more tmem_pools.
+ * Each pool, contains a hash table of rb_trees of tmem_objs.  Each
+ * tmem_obj contains a radix-tree-like tree of pointers, with intermediate
+ * nodes called tmem_objnodes.  Each leaf pointer in this tree points to
+ * a pampd, which is accessible only through a small set of callbacks
+ * registered by the PAM implementation (see tmem_register_pamops). Tmem
+ * does all memory allocation via a set of callbacks registered by the tmem
+ * host implementation (e.g. see tmem_register_hostops).
+ */
+
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/atomic.h>
+
+#include "tmem.h"
+
+/* data structure sentinels used for debugging... see tmem.h */
+#define POOL_SENTINEL 0x87658765
+#define OBJ_SENTINEL 0x12345678
+#define OBJNODE_SENTINEL 0xfedcba09
+
+static bool tmem_enabled;
+
+static void lock_tmem_state(void)
+{
+	lock_fmem_state();
+}
+
+static void unlock_tmem_state(void)
+{
+	unlock_fmem_state();
+}
+
+/*
+ * A tmem host implementation must use this function to register callbacks
+ * for memory allocation.
+ */
+static struct tmem_hostops tmem_hostops;
+
+static void tmem_objnode_tree_init(void);
+
+void tmem_register_hostops(struct tmem_hostops *m)
+{
+	tmem_objnode_tree_init();
+	tmem_hostops = *m;
+}
+
+/*
+ * A tmem host implementation must use this function to register
+ * callbacks for a page-accessible memory (PAM) implementation
+ */
+static struct tmem_pamops tmem_pamops;
+
+void tmem_register_pamops(struct tmem_pamops *m)
+{
+	tmem_pamops = *m;
+}
+
+/*
+ * Oid's are potentially very sparse and tmem_objs may have an indeterminately
+ * short life, being added and deleted at a relatively high frequency.
+ * So an rb_tree is an ideal data structure to manage tmem_objs.  But because
+ * of the potentially huge number of tmem_objs, each pool manages a hashtable
+ * of rb_trees to reduce search, insert, delete, and rebalancing time.
+ * Each hashbucket also has a lock to manage concurrent access.
+ *
+ * The following routines manage tmem_objs.  When any tmem_obj is accessed,
+ * the hashbucket lock must be held.
+ */
+
+/* searches for object==oid in pool, returns locked object if found */
+static struct tmem_obj *tmem_obj_find(struct tmem_hashbucket *hb,
+					struct tmem_oid *oidp)
+{
+	struct rb_node *rbnode;
+	struct tmem_obj *obj;
+
+	rbnode = hb->obj_rb_root.rb_node;
+	while (rbnode) {
+		BUG_ON(RB_EMPTY_NODE(rbnode));
+		obj = rb_entry(rbnode, struct tmem_obj, rb_tree_node);
+		switch (tmem_oid_compare(oidp, &obj->oid)) {
+		case 0: /* equal */
+			goto out;
+		case -1:
+			rbnode = rbnode->rb_left;
+			break;
+		case 1:
+			rbnode = rbnode->rb_right;
+			break;
+		}
+	}
+	obj = NULL;
+out:
+	return obj;
+}
+
+static void tmem_pampd_destroy_all_in_obj(struct tmem_obj *);
+
+/* free an object that has no more pampds in it */
+static void tmem_obj_free(struct tmem_obj *obj, struct tmem_hashbucket *hb)
+{
+	struct tmem_pool *pool;
+
+	BUG_ON(obj == NULL);
+	ASSERT_SENTINEL(obj, OBJ);
+	BUG_ON(obj->pampd_count > 0);
+	pool = obj->pool;
+	BUG_ON(pool == NULL);
+	if (obj->objnode_tree_root != NULL) /* may be "stump" with no leaves */
+		tmem_pampd_destroy_all_in_obj(obj);
+	BUG_ON(obj->objnode_tree_root != NULL);
+	BUG_ON((long)obj->objnode_count != 0);
+	atomic_dec(&pool->obj_count);
+	BUG_ON(atomic_read(&pool->obj_count) < 0);
+	INVERT_SENTINEL(obj, OBJ);
+	obj->pool = NULL;
+	tmem_oid_set_invalid(&obj->oid);
+	rb_erase(&obj->rb_tree_node, &hb->obj_rb_root);
+}
+
+/*
+ * initialize, and insert an tmem_object_root (called only if find failed)
+ */
+static void tmem_obj_init(struct tmem_obj *obj, struct tmem_hashbucket *hb,
+					struct tmem_pool *pool,
+					struct tmem_oid *oidp)
+{
+	struct rb_root *root = &hb->obj_rb_root;
+	struct rb_node **new = &(root->rb_node), *parent = NULL;
+	struct tmem_obj *this;
+
+	BUG_ON(pool == NULL);
+	atomic_inc(&pool->obj_count);
+	obj->objnode_tree_height = 0;
+	obj->objnode_tree_root = NULL;
+	obj->pool = pool;
+	obj->oid = *oidp;
+	obj->objnode_count = 0;
+	obj->pampd_count = 0;
+	(*tmem_pamops.new_obj)(obj);
+	SET_SENTINEL(obj, OBJ);
+	while (*new) {
+		BUG_ON(RB_EMPTY_NODE(*new));
+		this = rb_entry(*new, struct tmem_obj, rb_tree_node);
+		parent = *new;
+		switch (tmem_oid_compare(oidp, &this->oid)) {
+		case 0:
+			BUG(); /* already present; should never happen! */
+			break;
+		case -1:
+			new = &(*new)->rb_left;
+			break;
+		case 1:
+			new = &(*new)->rb_right;
+			break;
+		}
+	}
+	rb_link_node(&obj->rb_tree_node, parent, new);
+	rb_insert_color(&obj->rb_tree_node, root);
+}
+
+/*
+ * Tmem is managed as a set of tmem_pools with certain attributes, such as
+ * "ephemeral" vs "persistent".  These attributes apply to all tmem_objs
+ * and all pampds that belong to a tmem_pool.  A tmem_pool is created
+ * or deleted relatively rarely (for example, when a filesystem is
+ * mounted or unmounted.
+ */
+
+/* flush all data from a pool and, optionally, free it */
+static void tmem_pool_flush(struct tmem_pool *pool, bool destroy)
+{
+	struct rb_node *rbnode;
+	struct tmem_obj *obj;
+	struct tmem_hashbucket *hb = &pool->hashbucket[0];
+	int i;
+
+	BUG_ON(pool == NULL);
+	for (i = 0; i < TMEM_HASH_BUCKETS; i++, hb++) {
+		spin_lock(&hb->lock);
+		rbnode = rb_first(&hb->obj_rb_root);
+		while (rbnode != NULL) {
+			obj = rb_entry(rbnode, struct tmem_obj, rb_tree_node);
+			rbnode = rb_next(rbnode);
+			tmem_pampd_destroy_all_in_obj(obj);
+			tmem_obj_free(obj, hb);
+			(*tmem_hostops.obj_free)(obj, pool);
+		}
+		spin_unlock(&hb->lock);
+	}
+	if (destroy)
+		list_del(&pool->pool_list);
+}
+
+/*
+ * A tmem_obj contains a radix-tree-like tree in which the intermediate
+ * nodes are called tmem_objnodes.  (The kernel lib/radix-tree.c implementation
+ * is very specialized and tuned for specific uses and is not particularly
+ * suited for use from this code, though some code from the core algorithms has
+ * been reused, thus the copyright notices below).  Each tmem_objnode contains
+ * a set of pointers which point to either a set of intermediate tmem_objnodes
+ * or a set of of pampds.
+ *
+ * Portions Copyright (C) 2001 Momchil Velikov
+ * Portions Copyright (C) 2001 Christoph Hellwig
+ * Portions Copyright (C) 2005 SGI, Christoph Lameter <clameter@sgi.com>
+ */
+
+struct tmem_objnode_tree_path {
+	struct tmem_objnode *objnode;
+	int offset;
+};
+
+/* objnode height_to_maxindex translation */
+static unsigned long tmem_objnode_tree_h2max[OBJNODE_TREE_MAX_PATH + 1];
+
+static void tmem_objnode_tree_init(void)
+{
+	unsigned int ht, tmp;
+
+	for (ht = 0; ht < ARRAY_SIZE(tmem_objnode_tree_h2max); ht++) {
+		tmp = ht * OBJNODE_TREE_MAP_SHIFT;
+		if (tmp >= OBJNODE_TREE_INDEX_BITS)
+			tmem_objnode_tree_h2max[ht] = ~0UL;
+		else
+			tmem_objnode_tree_h2max[ht] =
+			    (~0UL >> (OBJNODE_TREE_INDEX_BITS - tmp - 1)) >> 1;
+	}
+}
+
+static struct tmem_objnode *tmem_objnode_alloc(struct tmem_obj *obj)
+{
+	struct tmem_objnode *objnode;
+
+	ASSERT_SENTINEL(obj, OBJ);
+	BUG_ON(obj->pool == NULL);
+	ASSERT_SENTINEL(obj->pool, POOL);
+	objnode = (*tmem_hostops.objnode_alloc)(obj->pool);
+	if (unlikely(objnode == NULL))
+		goto out;
+	objnode->obj = obj;
+	SET_SENTINEL(objnode, OBJNODE);
+	memset(&objnode->slots, 0, sizeof(objnode->slots));
+	objnode->slots_in_use = 0;
+	obj->objnode_count++;
+out:
+	return objnode;
+}
+
+static void tmem_objnode_free(struct tmem_objnode *objnode)
+{
+	struct tmem_pool *pool;
+	int i;
+
+	BUG_ON(objnode == NULL);
+	for (i = 0; i < OBJNODE_TREE_MAP_SIZE; i++)
+		BUG_ON(objnode->slots[i] != NULL);
+	ASSERT_SENTINEL(objnode, OBJNODE);
+	INVERT_SENTINEL(objnode, OBJNODE);
+	BUG_ON(objnode->obj == NULL);
+	ASSERT_SENTINEL(objnode->obj, OBJ);
+	pool = objnode->obj->pool;
+	BUG_ON(pool == NULL);
+	ASSERT_SENTINEL(pool, POOL);
+	objnode->obj->objnode_count--;
+	objnode->obj = NULL;
+	(*tmem_hostops.objnode_free)(objnode, pool);
+}
+
+/*
+ * lookup index in object and return associated pampd (or NULL if not found)
+ */
+static void **__tmem_pampd_lookup_in_obj(struct tmem_obj *obj, uint32_t index)
+{
+	unsigned int height, shift;
+	struct tmem_objnode **slot = NULL;
+
+	BUG_ON(obj == NULL);
+	ASSERT_SENTINEL(obj, OBJ);
+	BUG_ON(obj->pool == NULL);
+	ASSERT_SENTINEL(obj->pool, POOL);
+
+	height = obj->objnode_tree_height;
+	if (index > tmem_objnode_tree_h2max[obj->objnode_tree_height])
+		goto out;
+	if (height == 0 && obj->objnode_tree_root) {
+		slot = &obj->objnode_tree_root;
+		goto out;
+	}
+	shift = (height-1) * OBJNODE_TREE_MAP_SHIFT;
+	slot = &obj->objnode_tree_root;
+	while (height > 0) {
+		if (*slot == NULL)
+			goto out;
+		slot = (struct tmem_objnode **)
+			((*slot)->slots +
+			 ((index >> shift) & OBJNODE_TREE_MAP_MASK));
+		shift -= OBJNODE_TREE_MAP_SHIFT;
+		height--;
+	}
+out:
+	return slot != NULL ? (void **)slot : NULL;
+}
+
+static void *tmem_pampd_lookup_in_obj(struct tmem_obj *obj, uint32_t index)
+{
+	struct tmem_objnode **slot;
+
+	slot = (struct tmem_objnode **)__tmem_pampd_lookup_in_obj(obj, index);
+	return slot != NULL ? *slot : NULL;
+}
+
+static void *tmem_pampd_replace_in_obj(struct tmem_obj *obj, uint32_t index,
+					void *new_pampd)
+{
+	struct tmem_objnode **slot;
+	void *ret = NULL;
+
+	slot = (struct tmem_objnode **)__tmem_pampd_lookup_in_obj(obj, index);
+	if ((slot != NULL) && (*slot != NULL)) {
+		void *old_pampd = *(void **)slot;
+		*(void **)slot = new_pampd;
+		(*tmem_pamops.free)(old_pampd, obj->pool, NULL, 0);
+		ret = new_pampd;
+	}
+	return ret;
+}
+
+static int tmem_pampd_add_to_obj(struct tmem_obj *obj, uint32_t index,
+					void *pampd)
+{
+	int ret = 0;
+	struct tmem_objnode *objnode = NULL, *newnode, *slot;
+	unsigned int height, shift;
+	int offset = 0;
+
+	/* if necessary, extend the tree to be higher  */
+	if (index > tmem_objnode_tree_h2max[obj->objnode_tree_height]) {
+		height = obj->objnode_tree_height + 1;
+		if (index > tmem_objnode_tree_h2max[height])
+			while (index > tmem_objnode_tree_h2max[height])
+				height++;
+		if (obj->objnode_tree_root == NULL) {
+			obj->objnode_tree_height = height;
+			goto insert;
+		}
+		do {
+			newnode = tmem_objnode_alloc(obj);
+			if (!newnode) {
+				ret = -ENOMEM;
+				goto out;
+			}
+			newnode->slots[0] = obj->objnode_tree_root;
+			newnode->slots_in_use = 1;
+			obj->objnode_tree_root = newnode;
+			obj->objnode_tree_height++;
+		} while (height > obj->objnode_tree_height);
+	}
+insert:
+	slot = obj->objnode_tree_root;
+	height = obj->objnode_tree_height;
+	shift = (height-1) * OBJNODE_TREE_MAP_SHIFT;
+	while (height > 0) {
+		if (slot == NULL) {
+			/* add a child objnode.  */
+			slot = tmem_objnode_alloc(obj);
+			if (!slot) {
+				ret = -ENOMEM;
+				goto out;
+			}
+			if (objnode) {
+
+				objnode->slots[offset] = slot;
+				objnode->slots_in_use++;
+			} else
+				obj->objnode_tree_root = slot;
+		}
+		/* go down a level */
+		offset = (index >> shift) & OBJNODE_TREE_MAP_MASK;
+		objnode = slot;
+		slot = objnode->slots[offset];
+		shift -= OBJNODE_TREE_MAP_SHIFT;
+		height--;
+	}
+	BUG_ON(slot != NULL);
+	if (objnode) {
+		objnode->slots_in_use++;
+		objnode->slots[offset] = pampd;
+	} else
+		obj->objnode_tree_root = pampd;
+	obj->pampd_count++;
+out:
+	return ret;
+}
+
+static void *tmem_pampd_delete_from_obj(struct tmem_obj *obj, uint32_t index)
+{
+	struct tmem_objnode_tree_path path[OBJNODE_TREE_MAX_PATH + 1];
+	struct tmem_objnode_tree_path *pathp = path;
+	struct tmem_objnode *slot = NULL;
+	unsigned int height, shift;
+	int offset;
+
+	BUG_ON(obj == NULL);
+	ASSERT_SENTINEL(obj, OBJ);
+	BUG_ON(obj->pool == NULL);
+	ASSERT_SENTINEL(obj->pool, POOL);
+	height = obj->objnode_tree_height;
+	if (index > tmem_objnode_tree_h2max[height])
+		goto out;
+	slot = obj->objnode_tree_root;
+	if (height == 0 && obj->objnode_tree_root) {
+		obj->objnode_tree_root = NULL;
+		goto out;
+	}
+	shift = (height - 1) * OBJNODE_TREE_MAP_SHIFT;
+	pathp->objnode = NULL;
+	do {
+		if (slot == NULL)
+			goto out;
+		pathp++;
+		offset = (index >> shift) & OBJNODE_TREE_MAP_MASK;
+		pathp->offset = offset;
+		pathp->objnode = slot;
+		slot = slot->slots[offset];
+		shift -= OBJNODE_TREE_MAP_SHIFT;
+		height--;
+	} while (height > 0);
+	if (slot == NULL)
+		goto out;
+	while (pathp->objnode) {
+		pathp->objnode->slots[pathp->offset] = NULL;
+		pathp->objnode->slots_in_use--;
+		if (pathp->objnode->slots_in_use) {
+			if (pathp->objnode == obj->objnode_tree_root) {
+				while (obj->objnode_tree_height > 0 &&
+				  obj->objnode_tree_root->slots_in_use == 1 &&
+				  obj->objnode_tree_root->slots[0]) {
+					struct tmem_objnode *to_free =
+						obj->objnode_tree_root;
+
+					obj->objnode_tree_root =
+							to_free->slots[0];
+					obj->objnode_tree_height--;
+					to_free->slots[0] = NULL;
+					to_free->slots_in_use = 0;
+					tmem_objnode_free(to_free);
+				}
+			}
+			goto out;
+		}
+		tmem_objnode_free(pathp->objnode); /* 0 slots used, free it */
+		pathp--;
+	}
+	obj->objnode_tree_height = 0;
+	obj->objnode_tree_root = NULL;
+
+out:
+	if (slot != NULL)
+		obj->pampd_count--;
+	BUG_ON(obj->pampd_count < 0);
+	return slot;
+}
+
+/* recursively walk the objnode_tree destroying pampds and objnodes */
+static void tmem_objnode_node_destroy(struct tmem_obj *obj,
+					struct tmem_objnode *objnode,
+					unsigned int ht)
+{
+	int i;
+
+	if (ht == 0)
+		return;
+	for (i = 0; i < OBJNODE_TREE_MAP_SIZE; i++) {
+		if (objnode->slots[i]) {
+			if (ht == 1) {
+				obj->pampd_count--;
+				(*tmem_pamops.free)(objnode->slots[i],
+						obj->pool, NULL, 0);
+				objnode->slots[i] = NULL;
+				continue;
+			}
+			tmem_objnode_node_destroy(obj, objnode->slots[i], ht-1);
+			tmem_objnode_free(objnode->slots[i]);
+			objnode->slots[i] = NULL;
+		}
+	}
+}
+
+static void tmem_pampd_destroy_all_in_obj(struct tmem_obj *obj)
+{
+	if (obj->objnode_tree_root == NULL)
+		return;
+	if (obj->objnode_tree_height == 0) {
+		obj->pampd_count--;
+		(*tmem_pamops.free)(obj->objnode_tree_root, obj->pool, NULL, 0);
+	} else {
+		tmem_objnode_node_destroy(obj, obj->objnode_tree_root,
+					obj->objnode_tree_height);
+		tmem_objnode_free(obj->objnode_tree_root);
+		obj->objnode_tree_height = 0;
+	}
+	obj->objnode_tree_root = NULL;
+	(*tmem_pamops.free_obj)(obj->pool, obj);
+}
+
+/*
+ * Tmem is operated on by a set of well-defined actions:
+ * "put", "get", "flush", "flush_object", "new pool" and "destroy pool".
+ * (The tmem ABI allows for subpages and exchanges but these operations
+ * are not included in this implementation.)
+ *
+ * These "tmem core" operations are implemented in the following functions.
+ */
+
+/*
+ * "Put" a page, e.g. copy a page from the kernel into newly allocated
+ * PAM space (if such space is available).  Tmem_put is complicated by
+ * a corner case: What if a page with matching handle already exists in
+ * tmem?  To guarantee coherency, one of two actions is necessary: Either
+ * the data for the page must be overwritten, or the page must be
+ * "flushed" so that the data is not accessible to a subsequent "get".
+ * Since these "duplicate puts" are relatively rare, this implementation
+ * always flushes for simplicity.
+ */
+int tmem_put(struct tmem_pool *pool, struct tmem_oid *oidp, uint32_t index,
+		char *data, size_t size, bool raw, bool ephemeral)
+{
+	struct tmem_obj *obj = NULL, *objfound = NULL, *objnew = NULL;
+	void *pampd = NULL, *pampd_del = NULL;
+	int ret = -ENOMEM;
+	struct tmem_hashbucket *hb;
+
+	lock_tmem_state();
+	if (!tmem_enabled)
+		goto disabled;
+	hb = &pool->hashbucket[tmem_oid_hash(oidp)];
+	spin_lock(&hb->lock);
+	obj = objfound = tmem_obj_find(hb, oidp);
+	if (obj != NULL) {
+		pampd = tmem_pampd_lookup_in_obj(objfound, index);
+		if (pampd != NULL) {
+			/* if found, is a dup put, flush the old one */
+			pampd_del = tmem_pampd_delete_from_obj(obj, index);
+			BUG_ON(pampd_del != pampd);
+			(*tmem_pamops.free)(pampd, pool, oidp, index);
+			if (obj->pampd_count == 0) {
+				objnew = obj;
+				objfound = NULL;
+			}
+			pampd = NULL;
+		}
+	} else {
+		obj = objnew = (*tmem_hostops.obj_alloc)(pool);
+		if (unlikely(obj == NULL)) {
+			ret = -ENOMEM;
+			goto out;
+		}
+		tmem_obj_init(obj, hb, pool, oidp);
+	}
+	BUG_ON(obj == NULL);
+	BUG_ON(((objnew != obj) && (objfound != obj)) || (objnew == objfound));
+	pampd = (*tmem_pamops.create)(data, size, raw, ephemeral,
+					obj->pool, &obj->oid, index);
+	if (unlikely(pampd == NULL))
+		goto free;
+	ret = tmem_pampd_add_to_obj(obj, index, pampd);
+	if (unlikely(ret == -ENOMEM))
+		/* may have partially built objnode tree ("stump") */
+		goto delete_and_free;
+	goto out;
+
+delete_and_free:
+	(void)tmem_pampd_delete_from_obj(obj, index);
+free:
+	if (pampd)
+		(*tmem_pamops.free)(pampd, pool, NULL, 0);
+	if (objnew) {
+		tmem_obj_free(objnew, hb);
+		(*tmem_hostops.obj_free)(objnew, pool);
+	}
+out:
+	spin_unlock(&hb->lock);
+disabled:
+	unlock_tmem_state();
+	return ret;
+}
+
+/*
+ * "Get" a page, e.g. if one can be found, copy the tmem page with the
+ * matching handle from PAM space to the kernel.  By tmem definition,
+ * when a "get" is successful on an ephemeral page, the page is "flushed",
+ * and when a "get" is successful on a persistent page, the page is retained
+ * in tmem.  Note that to preserve
+ * coherency, "get" can never be skipped if tmem contains the data.
+ * That is, if a get is done with a certain handle and fails, any
+ * subsequent "get" must also fail (unless of course there is a
+ * "put" done with the same handle).
+
+ */
+int tmem_get(struct tmem_pool *pool, struct tmem_oid *oidp, uint32_t index,
+		char *data, size_t *size, bool raw, int get_and_free)
+{
+	struct tmem_obj *obj;
+	void *pampd;
+	bool ephemeral = is_ephemeral(pool);
+	int ret = -1;
+	struct tmem_hashbucket *hb;
+	bool free = (get_and_free == 1) || ((get_and_free == 0) && ephemeral);
+	bool lock_held = false;
+
+	lock_tmem_state();
+	if (!tmem_enabled)
+		goto disabled;
+	hb = &pool->hashbucket[tmem_oid_hash(oidp)];
+	spin_lock(&hb->lock);
+	lock_held = true;
+	obj = tmem_obj_find(hb, oidp);
+	if (obj == NULL)
+		goto out;
+	if (free)
+		pampd = tmem_pampd_delete_from_obj(obj, index);
+	else
+		pampd = tmem_pampd_lookup_in_obj(obj, index);
+	if (pampd == NULL)
+		goto out;
+	if (free) {
+		if (obj->pampd_count == 0) {
+			tmem_obj_free(obj, hb);
+			(*tmem_hostops.obj_free)(obj, pool);
+			obj = NULL;
+		}
+	}
+	if (tmem_pamops.is_remote(pampd)) {
+		lock_held = false;
+		spin_unlock(&hb->lock);
+	}
+	if (free)
+		ret = (*tmem_pamops.get_data_and_free)(
+				data, size, raw, pampd, pool, oidp, index);
+	else
+		ret = (*tmem_pamops.get_data)(
+				data, size, raw, pampd, pool, oidp, index);
+	if (ret < 0)
+		goto out;
+	ret = 0;
+out:
+	if (lock_held)
+		spin_unlock(&hb->lock);
+disabled:
+	unlock_tmem_state();
+	return ret;
+}
+
+/*
+ * If a page in tmem matches the handle, "flush" this page from tmem such
+ * that any subsequent "get" does not succeed (unless, of course, there
+ * was another "put" with the same handle).
+ */
+int tmem_flush_page(struct tmem_pool *pool,
+				struct tmem_oid *oidp, uint32_t index)
+{
+	struct tmem_obj *obj;
+	void *pampd;
+	int ret = -1;
+	struct tmem_hashbucket *hb;
+
+	hb = &pool->hashbucket[tmem_oid_hash(oidp)];
+	spin_lock(&hb->lock);
+	obj = tmem_obj_find(hb, oidp);
+	if (obj == NULL)
+		goto out;
+	pampd = tmem_pampd_delete_from_obj(obj, index);
+	if (pampd == NULL)
+		goto out;
+	(*tmem_pamops.free)(pampd, pool, oidp, index);
+	if (obj->pampd_count == 0) {
+		tmem_obj_free(obj, hb);
+		(*tmem_hostops.obj_free)(obj, pool);
+	}
+	ret = 0;
+
+out:
+	spin_unlock(&hb->lock);
+	return ret;
+}
+
+/*
+ * If a page in tmem matches the handle, replace the page so that any
+ * subsequent "get" gets the new page.  Returns 0 if
+ * there was a page to replace, else returns -1.
+ */
+int tmem_replace(struct tmem_pool *pool, struct tmem_oid *oidp,
+			uint32_t index, void *new_pampd)
+{
+	struct tmem_obj *obj;
+	int ret = -1;
+	struct tmem_hashbucket *hb;
+
+	lock_tmem_state();
+	if (!tmem_enabled)
+		goto disabled;
+	hb = &pool->hashbucket[tmem_oid_hash(oidp)];
+	spin_lock(&hb->lock);
+	obj = tmem_obj_find(hb, oidp);
+	if (obj == NULL)
+		goto out;
+	new_pampd = tmem_pampd_replace_in_obj(obj, index, new_pampd);
+	ret = (*tmem_pamops.replace_in_obj)(new_pampd, obj);
+out:
+	spin_unlock(&hb->lock);
+disabled:
+	unlock_tmem_state();
+	return ret;
+}
+
+/*
+ * "Flush" all pages in tmem matching this oid.
+ */
+int tmem_flush_object(struct tmem_pool *pool, struct tmem_oid *oidp)
+{
+	struct tmem_obj *obj;
+	struct tmem_hashbucket *hb;
+	int ret = -1;
+
+	hb = &pool->hashbucket[tmem_oid_hash(oidp)];
+	spin_lock(&hb->lock);
+	obj = tmem_obj_find(hb, oidp);
+	if (obj == NULL)
+		goto out;
+	tmem_pampd_destroy_all_in_obj(obj);
+	tmem_obj_free(obj, hb);
+	(*tmem_hostops.obj_free)(obj, pool);
+	ret = 0;
+
+out:
+	spin_unlock(&hb->lock);
+	return ret;
+}
+
+/*
+ * "Flush" all pages (and tmem_objs) from this tmem_pool and disable
+ * all subsequent access to this tmem_pool.
+ */
+int tmem_destroy_pool(struct tmem_pool *pool)
+{
+	int ret = -1;
+
+	if (pool == NULL)
+		goto out;
+	tmem_pool_flush(pool, 1);
+	ret = 0;
+out:
+	return ret;
+}
+
+int tmem_flush_pool(struct tmem_pool *pool)
+{
+	int ret = -1;
+
+	if (pool == NULL)
+		goto out;
+	tmem_pool_flush(pool, 0);
+	ret = 0;
+out:
+	return ret;
+}
+
+static LIST_HEAD(tmem_global_pool_list);
+
+/*
+ * Create a new tmem_pool with the provided flag and return
+ * a pool id provided by the tmem host implementation.
+ */
+void tmem_new_pool(struct tmem_pool *pool, uint32_t flags)
+{
+	int persistent = flags & TMEM_POOL_PERSIST;
+	int shared = flags & TMEM_POOL_SHARED;
+	struct tmem_hashbucket *hb = &pool->hashbucket[0];
+	int i;
+
+	for (i = 0; i < TMEM_HASH_BUCKETS; i++, hb++) {
+		hb->obj_rb_root = RB_ROOT;
+		spin_lock_init(&hb->lock);
+	}
+	INIT_LIST_HEAD(&pool->pool_list);
+	atomic_set(&pool->obj_count, 0);
+	SET_SENTINEL(pool, POOL);
+	list_add_tail(&pool->pool_list, &tmem_global_pool_list);
+	pool->persistent = persistent;
+	pool->shared = shared;
+}
+
+/* The following must be called with tmem state locked */
+static void tmem_cleanup(void)
+{
+	(*tmem_hostops.flush_all_obj)();
+}
+
+void tmem_enable(void)
+{
+	pr_info("turning tmem on\n");
+	tmem_enabled = true;
+
+	(*tmem_hostops.control)(false);
+}
+
+void tmem_disable(void)
+{
+	pr_info("turning tmem off\n");
+	tmem_enabled = false;
+
+	tmem_cleanup();
+	(*tmem_hostops.control)(true);
+}
diff --git a/drivers/staging/qcache/tmem.h b/drivers/staging/qcache/tmem.h
new file mode 100644
index 0000000..dd8a6ea
--- /dev/null
+++ b/drivers/staging/qcache/tmem.h
@@ -0,0 +1,212 @@
+/*
+ * tmem.h
+ *
+ * Transcendent memory
+ *
+ * Copyright (c) 2009-2011, Dan Magenheimer, Oracle Corp.
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ */
+
+#ifndef _TMEM_H_
+#define _TMEM_H_
+
+#include <linux/types.h>
+#include <linux/highmem.h>
+#include <linux/hash.h>
+#include <linux/atomic.h>
+#include <linux/fmem.h>
+
+/*
+ * These are pre-defined by the Xen<->Linux ABI
+ */
+#define TMEM_PUT_PAGE			4
+#define TMEM_GET_PAGE			5
+#define TMEM_FLUSH_PAGE			6
+#define TMEM_FLUSH_OBJECT		7
+#define TMEM_POOL_PERSIST		1
+#define TMEM_POOL_SHARED		2
+#define TMEM_POOL_PRECOMPRESSED		4
+#define TMEM_POOL_PAGESIZE_SHIFT	4
+#define TMEM_POOL_PAGESIZE_MASK		0xf
+#define TMEM_POOL_RESERVED_BITS		0x00ffff00
+
+/*
+ * sentinels have proven very useful for debugging but can be removed
+ * or disabled before final merge.
+ */
+#define SENTINELS
+#ifdef SENTINELS
+#define DECL_SENTINEL uint32_t sentinel;
+#define SET_SENTINEL(_x, _y) (_x->sentinel = _y##_SENTINEL)
+#define INVERT_SENTINEL(_x, _y) (_x->sentinel = ~_y##_SENTINEL)
+#define ASSERT_SENTINEL(_x, _y) WARN_ON(_x->sentinel != _y##_SENTINEL)
+#define ASSERT_INVERTED_SENTINEL(_x, _y) WARN_ON(_x->sentinel != ~_y##_SENTINEL)
+#else
+#define DECL_SENTINEL
+#define SET_SENTINEL(_x, _y) do { } while (0)
+#define INVERT_SENTINEL(_x, _y) do { } while (0)
+#define ASSERT_SENTINEL(_x, _y) do { } while (0)
+#define ASSERT_INVERTED_SENTINEL(_x, _y) do { } while (0)
+#endif
+
+/*
+ * A pool is the highest-level data structure managed by tmem and
+ * usually corresponds to a large independent set of pages such as
+ * a filesystem.  Each pool has an id, and certain attributes and counters.
+ * It also contains a set of hash buckets, each of which contains an rbtree
+ * of objects and a lock to manage concurrency within the pool.
+ */
+
+#define TMEM_HASH_BUCKET_BITS	8
+#define TMEM_HASH_BUCKETS	(1<<TMEM_HASH_BUCKET_BITS)
+
+struct tmem_hashbucket {
+	struct rb_root obj_rb_root;
+	spinlock_t lock;
+};
+
+struct tmem_pool {
+	void *client; /* "up" for some clients, avoids table lookup */
+	struct list_head pool_list;
+	uint32_t pool_id;
+	bool persistent;
+	bool shared;
+	atomic_t obj_count;
+	atomic_t refcount;
+	struct tmem_hashbucket hashbucket[TMEM_HASH_BUCKETS];
+	DECL_SENTINEL
+};
+
+#define is_persistent(_p)  (_p->persistent)
+#define is_ephemeral(_p)   (!(_p->persistent))
+
+/*
+ * An object id ("oid") is large: 192-bits (to ensure, for example, files
+ * in a modern filesystem can be uniquely identified).
+ */
+
+struct tmem_oid {
+	uint64_t oid[3];
+};
+
+static inline void tmem_oid_set_invalid(struct tmem_oid *oidp)
+{
+	oidp->oid[0] = oidp->oid[1] = oidp->oid[2] = -1UL;
+}
+
+static inline bool tmem_oid_valid(struct tmem_oid *oidp)
+{
+	return oidp->oid[0] != -1UL || oidp->oid[1] != -1UL ||
+		oidp->oid[2] != -1UL;
+}
+
+static inline int tmem_oid_compare(struct tmem_oid *left,
+					struct tmem_oid *right)
+{
+	int ret;
+
+	if (left->oid[2] == right->oid[2]) {
+		if (left->oid[1] == right->oid[1]) {
+			if (left->oid[0] == right->oid[0])
+				ret = 0;
+			else if (left->oid[0] < right->oid[0])
+				ret = -1;
+			else
+				return 1;
+		} else if (left->oid[1] < right->oid[1])
+			ret = -1;
+		else
+			ret = 1;
+	} else if (left->oid[2] < right->oid[2])
+		ret = -1;
+	else
+		ret = 1;
+	return ret;
+}
+
+static inline unsigned tmem_oid_hash(struct tmem_oid *oidp)
+{
+	return hash_long(oidp->oid[0] ^ oidp->oid[1] ^ oidp->oid[2],
+				TMEM_HASH_BUCKET_BITS);
+}
+
+/*
+ * A tmem_obj contains an identifier (oid), pointers to the parent
+ * pool and the rb_tree to which it belongs, counters, and an ordered
+ * set of pampds, structured in a radix-tree-like tree.  The intermediate
+ * nodes of the tree are called tmem_objnodes.
+ */
+
+struct tmem_objnode;
+
+struct tmem_obj {
+	struct tmem_oid oid;
+	struct tmem_pool *pool;
+	struct rb_node rb_tree_node;
+	struct tmem_objnode *objnode_tree_root;
+	unsigned int objnode_tree_height;
+	unsigned long objnode_count;
+	long pampd_count;
+	void *extra; /* for private use by pampd implementation */
+	DECL_SENTINEL
+};
+
+#define OBJNODE_TREE_MAP_SHIFT 6
+#define OBJNODE_TREE_MAP_SIZE (1UL << OBJNODE_TREE_MAP_SHIFT)
+#define OBJNODE_TREE_MAP_MASK (OBJNODE_TREE_MAP_SIZE-1)
+#define OBJNODE_TREE_INDEX_BITS (8 /* CHAR_BIT */ * sizeof(unsigned long))
+#define OBJNODE_TREE_MAX_PATH \
+		(OBJNODE_TREE_INDEX_BITS/OBJNODE_TREE_MAP_SHIFT + 2)
+
+struct tmem_objnode {
+	struct tmem_obj *obj;
+	DECL_SENTINEL
+	void *slots[OBJNODE_TREE_MAP_SIZE];
+	unsigned int slots_in_use;
+};
+
+/* pampd abstract datatype methods provided by the PAM implementation */
+struct tmem_pamops {
+	void *(*create)(char *, size_t, bool, int,
+			struct tmem_pool *, struct tmem_oid *, uint32_t);
+	int (*get_data)(char *, size_t *, bool, void *, struct tmem_pool *,
+				struct tmem_oid *, uint32_t);
+	int (*get_data_and_free)(char *, size_t *, bool, void *,
+				struct tmem_pool *, struct tmem_oid *,
+				uint32_t);
+	void (*free)(void *, struct tmem_pool *, struct tmem_oid *, uint32_t);
+	void (*free_obj)(struct tmem_pool *, struct tmem_obj *);
+	bool (*is_remote)(void *);
+	void (*new_obj)(struct tmem_obj *);
+	int (*replace_in_obj)(void *, struct tmem_obj *);
+};
+extern void tmem_register_pamops(struct tmem_pamops *m);
+
+/* memory allocation methods provided by the host implementation */
+struct tmem_hostops {
+	struct tmem_obj *(*obj_alloc)(struct tmem_pool *);
+	void (*obj_free)(struct tmem_obj *, struct tmem_pool *);
+	struct tmem_objnode *(*objnode_alloc)(struct tmem_pool *);
+	void (*objnode_free)(struct tmem_objnode *, struct tmem_pool *);
+	void (*flush_all_obj)(void);
+	void (*control)(bool);
+};
+extern void tmem_register_hostops(struct tmem_hostops *m);
+
+/* core tmem accessor functions */
+extern int tmem_put(struct tmem_pool *, struct tmem_oid *, uint32_t index,
+			char *, size_t, bool, bool);
+extern int tmem_get(struct tmem_pool *, struct tmem_oid *, uint32_t index,
+			char *, size_t *, bool, int);
+extern int tmem_replace(struct tmem_pool *, struct tmem_oid *, uint32_t index,
+			void *);
+extern int tmem_flush_page(struct tmem_pool *, struct tmem_oid *,
+			uint32_t index);
+extern int tmem_flush_object(struct tmem_pool *, struct tmem_oid *);
+extern int tmem_destroy_pool(struct tmem_pool *);
+extern int tmem_flush_pool(struct tmem_pool *);
+extern void tmem_new_pool(struct tmem_pool *, uint32_t);
+
+extern void tmem_enable(void);
+extern void tmem_disable(void);
+#endif /* _TMEM_H */
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 514a691..fbe0dd7 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -19,6 +19,60 @@
 	depends on HWMON=y || HWMON=THERMAL
 	default y
 
+config THERMAL_MSM_POPMEM
+	tristate "Qualcomm MSM POP memory temperature sensor"
+	depends on THERMAL
+	default n
+	help
+	  This enables a thermal sysfs driver for MSM POP memory. It shows up in
+	  sysfs as a thermal zone with one trip point. Due to hardware
+	  limitations, the temperatures are reported as "Low Temperature" (20 C)
+	  "Normal Temperature" (50 C) and "Out of Spec High Temperature" (85 C).
+	  This driver is designed to be used in conjunction with a user space
+	  application to make all policy decisions.
+
+config THERMAL_TSENS
+	tristate "Qualcomm Tsens Temperature Alarm"
+	depends on THERMAL
+	default n
+	help
+	  This enables the thermal sysfs driver for the Tsens device. It shows
+	  up in Sysfs as a thermal zone with mutiple trip points. Disabling the
+	  thermal zone device via the mode file results in disabling the sensor.
+	  Also able to set threshold temperature for both hot and cold and update
+	  when a threshold is reached.
+
+config THERMAL_TSENS8960
+	tristate "Qualcomm 8960 Tsens Temperature Alarm"
+	depends on THERMAL
+	help
+	  This enables the thermal sysfs driver for the Tsens device. It shows
+	  up in Sysfs as a thermal zone with mutiple trip points. Disabling the
+	  thermal zone device via the mode file results in disabling the sensor.
+	  Also able to set threshold temperature for both hot and cold and update
+	  when a threshold is reached.
+
+config THERMAL_PM8XXX
+	tristate "Qualcomm PMIC PM8xxx Temperature Alarm"
+	depends on THERMAL
+	depends on MFD_PM8XXX
+	help
+	  This enables a thermal Sysfs driver for the PMIC PM8xxx devices. It
+	  shows up in Sysfs as a thermal zone with multiple trip points.
+	  Enabling the thermal zone device via the mode file results in
+	  shifting over temperature shutdown control of the PMIC from hardware
+	  to software.
+
+config THERMAL_MONITOR
+	bool "Monitor thermal state and limit CPU Frequency"
+	depends on THERMAL_TSENS8960
+	default n
+	help
+	  This enables thermal monitoring capability in the kernel in the
+	  absence of a system wide thermal monitoring entity or until such an
+	  entity starts running in the userspace. Monitors TSENS temperature
+	  and limits the max frequency of the cores.
+
 config SPEAR_THERMAL
 	bool "SPEAr thermal sensor driver"
 	depends on THERMAL
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index a9fff0b..275a692 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -3,4 +3,9 @@
 #
 
 obj-$(CONFIG_THERMAL)		+= thermal_sys.o
+obj-$(CONFIG_THERMAL_MSM_POPMEM)	+= msm_popmem-tm.o
+obj-$(CONFIG_THERMAL_TSENS)	+= msm_tsens.o
+obj-$(CONFIG_THERMAL_TSENS8960) += msm8960_tsens.o
+obj-$(CONFIG_THERMAL_PM8XXX)	+= pm8xxx-tm.o
+obj-$(CONFIG_THERMAL_MONITOR)	+= msm_thermal.o
 obj-$(CONFIG_SPEAR_THERMAL)		+= spear_thermal.o
\ No newline at end of file
diff --git a/drivers/thermal/msm8960_tsens.c b/drivers/thermal/msm8960_tsens.c
new file mode 100644
index 0000000..78a1292
--- /dev/null
+++ b/drivers/thermal/msm8960_tsens.c
@@ -0,0 +1,1082 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+/*
+ * Qualcomm MSM8960 TSENS driver
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/thermal.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/msm_tsens.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/pm.h>
+
+#include <mach/msm_iomap.h>
+#include <mach/socinfo.h>
+
+/* Trips: from very hot to very cold */
+enum tsens_trip_type {
+	TSENS_TRIP_STAGE3 = 0,
+	TSENS_TRIP_STAGE2,
+	TSENS_TRIP_STAGE1,
+	TSENS_TRIP_STAGE0,
+	TSENS_TRIP_NUM,
+};
+
+/* MSM8960 TSENS register info */
+#define TSENS_CAL_DEGC					30
+#define TSENS_MAIN_SENSOR				0
+
+#define TSENS_8960_QFPROM_ADDR0		(MSM_QFPROM_BASE + 0x00000404)
+#define TSENS_8960_QFPROM_SPARE_ADDR0	(MSM_QFPROM_BASE + 0x00000414)
+#define TSENS_8960_CONFIG				0x9b
+#define TSENS_8960_CONFIG_SHIFT				0
+#define TSENS_8960_CONFIG_MASK		(0xf << TSENS_8960_CONFIG_SHIFT)
+#define TSENS_CNTL_ADDR			(MSM_CLK_CTL_BASE + 0x00003620)
+#define TSENS_EN					BIT(0)
+#define TSENS_SW_RST					BIT(1)
+#define TSENS_ADC_CLK_SEL				BIT(2)
+#define SENSOR0_EN					BIT(3)
+#define SENSOR1_EN					BIT(4)
+#define SENSOR2_EN					BIT(5)
+#define SENSOR3_EN					BIT(6)
+#define SENSOR4_EN					BIT(7)
+#define SENSORS_EN			(SENSOR0_EN | SENSOR1_EN |	\
+					SENSOR2_EN | SENSOR3_EN | SENSOR4_EN)
+#define TSENS_STATUS_CNTL_OFFSET	8
+#define TSENS_MIN_STATUS_MASK		BIT((tsens_status_cntl_start))
+#define TSENS_LOWER_STATUS_CLR		BIT((tsens_status_cntl_start + 1))
+#define TSENS_UPPER_STATUS_CLR		BIT((tsens_status_cntl_start + 2))
+#define TSENS_MAX_STATUS_MASK		BIT((tsens_status_cntl_start + 3))
+
+#define TSENS_MEASURE_PERIOD				4 /* 1 sec. default */
+#define TSENS_8960_SLP_CLK_ENA				BIT(26)
+
+#define TSENS_THRESHOLD_ADDR		(MSM_CLK_CTL_BASE + 0x00003624)
+#define TSENS_THRESHOLD_MAX_CODE			0xff
+#define TSENS_THRESHOLD_MIN_CODE			0
+#define TSENS_THRESHOLD_MAX_LIMIT_SHIFT			24
+#define TSENS_THRESHOLD_MIN_LIMIT_SHIFT			16
+#define TSENS_THRESHOLD_UPPER_LIMIT_SHIFT		8
+#define TSENS_THRESHOLD_LOWER_LIMIT_SHIFT		0
+#define TSENS_THRESHOLD_MAX_LIMIT_MASK		(TSENS_THRESHOLD_MAX_CODE << \
+					TSENS_THRESHOLD_MAX_LIMIT_SHIFT)
+#define TSENS_THRESHOLD_MIN_LIMIT_MASK		(TSENS_THRESHOLD_MAX_CODE << \
+					TSENS_THRESHOLD_MIN_LIMIT_SHIFT)
+#define TSENS_THRESHOLD_UPPER_LIMIT_MASK	(TSENS_THRESHOLD_MAX_CODE << \
+					TSENS_THRESHOLD_UPPER_LIMIT_SHIFT)
+#define TSENS_THRESHOLD_LOWER_LIMIT_MASK	(TSENS_THRESHOLD_MAX_CODE << \
+					TSENS_THRESHOLD_LOWER_LIMIT_SHIFT)
+/* Initial temperature threshold values */
+#define TSENS_LOWER_LIMIT_TH				0x50
+#define TSENS_UPPER_LIMIT_TH				0xdf
+#define TSENS_MIN_LIMIT_TH				0x0
+#define TSENS_MAX_LIMIT_TH				0xff
+
+#define TSENS_S0_STATUS_ADDR			(MSM_CLK_CTL_BASE + 0x00003628)
+#define TSENS_STATUS_ADDR_OFFSET			2
+#define TSENS_SENSOR_STATUS_SIZE			4
+#define TSENS_INT_STATUS_ADDR			(MSM_CLK_CTL_BASE + 0x0000363c)
+
+#define TSENS_LOWER_INT_MASK				BIT(1)
+#define TSENS_UPPER_INT_MASK				BIT(2)
+#define TSENS_MAX_INT_MASK				BIT(3)
+#define TSENS_TRDY_MASK					BIT(7)
+
+#define TSENS_8960_CONFIG_ADDR			(MSM_CLK_CTL_BASE + 0x00003640)
+#define TSENS_TRDY_RDY_MIN_TIME				1000
+#define TSENS_TRDY_RDY_MAX_TIME				1100
+#define TSENS_SENSOR_SHIFT				16
+#define TSENS_RED_SHIFT					8
+#define TSENS_8960_QFPROM_SHIFT				4
+#define TSENS_SENSOR_QFPROM_SHIFT			2
+#define TSENS_SENSOR0_SHIFT				3
+#define TSENS_MASK1					1
+
+#define TSENS_8660_QFPROM_ADDR			(MSM_QFPROM_BASE + 0x000000bc)
+#define TSENS_8660_QFPROM_RED_TEMP_SENSOR0_SHIFT	24
+#define TSENS_8660_QFPROM_TEMP_SENSOR0_SHIFT		16
+#define TSENS_8660_QFPROM_TEMP_SENSOR0_MASK		(255		\
+					<< TSENS_8660_QFPROM_TEMP_SENSOR0_SHIFT)
+#define TSENS_8660_CONFIG				01
+#define TSENS_8660_CONFIG_SHIFT				28
+#define TSENS_8660_CONFIG_MASK			(3 << TSENS_8660_CONFIG_SHIFT)
+#define TSENS_8660_SLP_CLK_ENA				BIT(24)
+
+#define TSENS_8064_SENSOR5_EN				BIT(8)
+#define TSENS_8064_SENSOR6_EN				BIT(9)
+#define TSENS_8064_SENSOR7_EN				BIT(10)
+#define TSENS_8064_SENSOR8_EN				BIT(11)
+#define TSENS_8064_SENSOR9_EN				BIT(12)
+#define TSENS_8064_SENSOR10_EN				BIT(13)
+#define TSENS_8064_SENSORS_EN			(SENSORS_EN | \
+						TSENS_8064_SENSOR5_EN | \
+						TSENS_8064_SENSOR6_EN | \
+						TSENS_8064_SENSOR7_EN | \
+						TSENS_8064_SENSOR8_EN | \
+						TSENS_8064_SENSOR9_EN | \
+						TSENS_8064_SENSOR10_EN)
+#define TSENS_8064_STATUS_CNTL			(MSM_CLK_CTL_BASE + 0x00003660)
+#define TSENS_8064_S5_STATUS_ADDR		(MSM_CLK_CTL_BASE + 0x00003664)
+#define TSENS_8064_SEQ_SENSORS				5
+#define TSENS_8064_S4_S5_OFFSET				40
+#define TSENS_CNTL_RESUME_MASK				0xfffffff9
+#define TSENS_8960_SENSOR_MASK				0xf8
+#define TSENS_8064_SENSOR_MASK				0x3ff8
+
+static int tsens_status_cntl_start;
+
+struct tsens_tm_device_sensor {
+	struct thermal_zone_device	*tz_dev;
+	enum thermal_device_mode	mode;
+	unsigned int			sensor_num;
+	struct work_struct		work;
+	int				offset;
+	int				calib_data;
+	int				calib_data_backup;
+	uint32_t			slope_mul_tsens_factor;
+};
+
+struct tsens_tm_device {
+	bool				prev_reading_avail;
+	int				tsens_factor;
+	uint32_t			tsens_num_sensor;
+	enum platform_type		hw_type;
+	int				pm_tsens_thr_data;
+	int				pm_tsens_cntl;
+	struct tsens_tm_device_sensor	sensor[0];
+};
+
+struct tsens_tm_device *tmdev;
+
+/* Temperature on y axis and ADC-code on x-axis */
+static int tsens_tz_code_to_degC(int adc_code, int sensor_num)
+{
+	int degcbeforefactor, degc;
+	degcbeforefactor = (adc_code *
+			tmdev->sensor[sensor_num].slope_mul_tsens_factor
+			+ tmdev->sensor[sensor_num].offset);
+
+	if (degcbeforefactor == 0)
+		degc = degcbeforefactor;
+	else if (degcbeforefactor > 0)
+		degc = (degcbeforefactor + tmdev->tsens_factor/2)
+				/ tmdev->tsens_factor;
+	else
+		degc = (degcbeforefactor - tmdev->tsens_factor/2)
+				/ tmdev->tsens_factor;
+	return degc;
+}
+
+static int tsens_tz_degC_to_code(int degC, int sensor_num)
+{
+	int code = (degC * tmdev->tsens_factor -
+		tmdev->sensor[sensor_num].offset
+		+ tmdev->sensor[sensor_num].slope_mul_tsens_factor/2)
+		/ tmdev->sensor[sensor_num].slope_mul_tsens_factor;
+
+	if (code > TSENS_THRESHOLD_MAX_CODE)
+		code = TSENS_THRESHOLD_MAX_CODE;
+	else if (code < TSENS_THRESHOLD_MIN_CODE)
+		code = TSENS_THRESHOLD_MIN_CODE;
+	return code;
+}
+
+static void tsens8960_get_temp(int sensor_num, unsigned long *temp)
+{
+	unsigned int code, offset = 0, sensor_addr;
+
+	if (!tmdev->prev_reading_avail) {
+		while (!(readl_relaxed(TSENS_INT_STATUS_ADDR)
+					& TSENS_TRDY_MASK))
+			usleep_range(TSENS_TRDY_RDY_MIN_TIME,
+				TSENS_TRDY_RDY_MAX_TIME);
+		tmdev->prev_reading_avail = true;
+	}
+
+	sensor_addr = (unsigned int)TSENS_S0_STATUS_ADDR;
+	if (tmdev->hw_type == APQ_8064 &&
+			sensor_num >= TSENS_8064_SEQ_SENSORS)
+		offset = TSENS_8064_S4_S5_OFFSET;
+	code = readl_relaxed(sensor_addr + offset +
+			(sensor_num << TSENS_STATUS_ADDR_OFFSET));
+	*temp = tsens_tz_code_to_degC(code, sensor_num);
+}
+
+static int tsens_tz_get_temp(struct thermal_zone_device *thermal,
+			     unsigned long *temp)
+{
+	struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+
+	if (!tm_sensor || tm_sensor->mode != THERMAL_DEVICE_ENABLED || !temp)
+		return -EINVAL;
+
+	tsens8960_get_temp(tm_sensor->sensor_num, temp);
+
+	return 0;
+}
+
+int tsens_get_temp(struct tsens_device *device, unsigned long *temp)
+{
+	if (!tmdev)
+		return -ENODEV;
+
+	tsens8960_get_temp(device->sensor_num, temp);
+
+	return 0;
+}
+EXPORT_SYMBOL(tsens_get_temp);
+
+static int tsens_tz_get_mode(struct thermal_zone_device *thermal,
+			      enum thermal_device_mode *mode)
+{
+	struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+
+	if (!tm_sensor || !mode)
+		return -EINVAL;
+
+	*mode = tm_sensor->mode;
+
+	return 0;
+}
+
+/* Function to enable the mode.
+ * If the main sensor is disabled all the sensors are disable and
+ * the clock is disabled.
+ * If the main sensor is not enabled and sub sensor is enabled
+ * returns with an error stating the main sensor is not enabled.
+ */
+static int tsens_tz_set_mode(struct thermal_zone_device *thermal,
+			      enum thermal_device_mode mode)
+{
+	struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+	unsigned int reg, mask, i;
+
+	if (!tm_sensor)
+		return -EINVAL;
+
+	if (mode != tm_sensor->mode) {
+		reg = readl_relaxed(TSENS_CNTL_ADDR);
+
+		mask = 1 << (tm_sensor->sensor_num + TSENS_SENSOR0_SHIFT);
+		if (mode == THERMAL_DEVICE_ENABLED) {
+			if ((mask != SENSOR0_EN) && !(reg & SENSOR0_EN)) {
+				pr_info("Main sensor not enabled\n");
+				return -EINVAL;
+			}
+			writel_relaxed(reg | TSENS_SW_RST, TSENS_CNTL_ADDR);
+			if (tmdev->hw_type == MSM_8960 ||
+				tmdev->hw_type == MDM_9615 ||
+				tmdev->hw_type == APQ_8064)
+				reg |= mask | TSENS_8960_SLP_CLK_ENA
+							| TSENS_EN;
+			else
+				reg |= mask | TSENS_8660_SLP_CLK_ENA
+							| TSENS_EN;
+			tmdev->prev_reading_avail = false;
+		} else {
+			reg &= ~mask;
+			if (!(reg & SENSOR0_EN)) {
+				if (tmdev->hw_type == APQ_8064)
+					reg &= ~(TSENS_8064_SENSORS_EN |
+						TSENS_8960_SLP_CLK_ENA |
+						TSENS_EN);
+				else if (tmdev->hw_type == MSM_8960 ||
+						tmdev->hw_type == MDM_9615)
+					reg &= ~(SENSORS_EN |
+						TSENS_8960_SLP_CLK_ENA |
+						TSENS_EN);
+				else
+					reg &= ~(SENSORS_EN |
+						TSENS_8660_SLP_CLK_ENA |
+						TSENS_EN);
+
+				for (i = 1; i < tmdev->tsens_num_sensor; i++)
+					tmdev->sensor[i].mode = mode;
+
+			}
+		}
+		writel_relaxed(reg, TSENS_CNTL_ADDR);
+	}
+	tm_sensor->mode = mode;
+
+	return 0;
+}
+
+static int tsens_tz_get_trip_type(struct thermal_zone_device *thermal,
+				   int trip, enum thermal_trip_type *type)
+{
+	struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+
+	if (!tm_sensor || trip < 0 || !type)
+		return -EINVAL;
+
+	switch (trip) {
+	case TSENS_TRIP_STAGE3:
+		*type = THERMAL_TRIP_CRITICAL;
+		break;
+	case TSENS_TRIP_STAGE2:
+		*type = THERMAL_TRIP_CONFIGURABLE_HI;
+		break;
+	case TSENS_TRIP_STAGE1:
+		*type = THERMAL_TRIP_CONFIGURABLE_LOW;
+		break;
+	case TSENS_TRIP_STAGE0:
+		*type = THERMAL_TRIP_CRITICAL_LOW;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int tsens_tz_activate_trip_type(struct thermal_zone_device *thermal,
+			int trip, enum thermal_trip_activation_mode mode)
+{
+	struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+	unsigned int reg_cntl, reg_th, code, hi_code, lo_code, mask;
+
+	if (!tm_sensor || trip < 0)
+		return -EINVAL;
+
+	lo_code = TSENS_THRESHOLD_MIN_CODE;
+	hi_code = TSENS_THRESHOLD_MAX_CODE;
+
+	if (tmdev->hw_type == APQ_8064)
+		reg_cntl = readl_relaxed(TSENS_8064_STATUS_CNTL);
+	else
+		reg_cntl = readl_relaxed(TSENS_CNTL_ADDR);
+
+	reg_th = readl_relaxed(TSENS_THRESHOLD_ADDR);
+	switch (trip) {
+	case TSENS_TRIP_STAGE3:
+		code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK)
+					>> TSENS_THRESHOLD_MAX_LIMIT_SHIFT;
+		mask = TSENS_MAX_STATUS_MASK;
+
+		if (!(reg_cntl & TSENS_UPPER_STATUS_CLR))
+			lo_code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK)
+					>> TSENS_THRESHOLD_UPPER_LIMIT_SHIFT;
+		else if (!(reg_cntl & TSENS_LOWER_STATUS_CLR))
+			lo_code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK)
+					>> TSENS_THRESHOLD_LOWER_LIMIT_SHIFT;
+		else if (!(reg_cntl & TSENS_MIN_STATUS_MASK))
+			lo_code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK)
+					>> TSENS_THRESHOLD_MIN_LIMIT_SHIFT;
+		break;
+	case TSENS_TRIP_STAGE2:
+		code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK)
+					>> TSENS_THRESHOLD_UPPER_LIMIT_SHIFT;
+		mask = TSENS_UPPER_STATUS_CLR;
+
+		if (!(reg_cntl & TSENS_MAX_STATUS_MASK))
+			hi_code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK)
+					>> TSENS_THRESHOLD_MAX_LIMIT_SHIFT;
+		if (!(reg_cntl & TSENS_LOWER_STATUS_CLR))
+			lo_code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK)
+					>> TSENS_THRESHOLD_LOWER_LIMIT_SHIFT;
+		else if (!(reg_cntl & TSENS_MIN_STATUS_MASK))
+			lo_code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK)
+					>> TSENS_THRESHOLD_MIN_LIMIT_SHIFT;
+		break;
+	case TSENS_TRIP_STAGE1:
+		code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK)
+					>> TSENS_THRESHOLD_LOWER_LIMIT_SHIFT;
+		mask = TSENS_LOWER_STATUS_CLR;
+
+		if (!(reg_cntl & TSENS_MIN_STATUS_MASK))
+			lo_code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK)
+					>> TSENS_THRESHOLD_MIN_LIMIT_SHIFT;
+		if (!(reg_cntl & TSENS_UPPER_STATUS_CLR))
+			hi_code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK)
+					>> TSENS_THRESHOLD_UPPER_LIMIT_SHIFT;
+		else if (!(reg_cntl & TSENS_MAX_STATUS_MASK))
+			hi_code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK)
+					>> TSENS_THRESHOLD_MAX_LIMIT_SHIFT;
+		break;
+	case TSENS_TRIP_STAGE0:
+		code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK)
+					>> TSENS_THRESHOLD_MIN_LIMIT_SHIFT;
+		mask = TSENS_MIN_STATUS_MASK;
+
+		if (!(reg_cntl & TSENS_LOWER_STATUS_CLR))
+			hi_code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK)
+					>> TSENS_THRESHOLD_LOWER_LIMIT_SHIFT;
+		else if (!(reg_cntl & TSENS_UPPER_STATUS_CLR))
+			hi_code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK)
+					>> TSENS_THRESHOLD_UPPER_LIMIT_SHIFT;
+		else if (!(reg_cntl & TSENS_MAX_STATUS_MASK))
+			hi_code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK)
+					>> TSENS_THRESHOLD_MAX_LIMIT_SHIFT;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (mode == THERMAL_TRIP_ACTIVATION_DISABLED) {
+		if (tmdev->hw_type == APQ_8064)
+			writel_relaxed(reg_cntl | mask, TSENS_8064_STATUS_CNTL);
+		else
+			writel_relaxed(reg_cntl | mask, TSENS_CNTL_ADDR);
+	} else {
+		if (code < lo_code || code > hi_code) {
+			pr_info("%s with invalid code %x\n", __func__, code);
+			return -EINVAL;
+		}
+		if (tmdev->hw_type == APQ_8064)
+			writel_relaxed(reg_cntl & ~mask,
+					TSENS_8064_STATUS_CNTL);
+		else
+			writel_relaxed(reg_cntl & ~mask, TSENS_CNTL_ADDR);
+	}
+	mb();
+	return 0;
+}
+
+static int tsens_tz_get_trip_temp(struct thermal_zone_device *thermal,
+				   int trip, unsigned long *temp)
+{
+	struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+	unsigned int reg;
+
+	if (!tm_sensor || trip < 0 || !temp)
+		return -EINVAL;
+
+	reg = readl_relaxed(TSENS_THRESHOLD_ADDR);
+	switch (trip) {
+	case TSENS_TRIP_STAGE3:
+		reg = (reg & TSENS_THRESHOLD_MAX_LIMIT_MASK)
+					>> TSENS_THRESHOLD_MAX_LIMIT_SHIFT;
+		break;
+	case TSENS_TRIP_STAGE2:
+		reg = (reg & TSENS_THRESHOLD_UPPER_LIMIT_MASK)
+					>> TSENS_THRESHOLD_UPPER_LIMIT_SHIFT;
+		break;
+	case TSENS_TRIP_STAGE1:
+		reg = (reg & TSENS_THRESHOLD_LOWER_LIMIT_MASK)
+					>> TSENS_THRESHOLD_LOWER_LIMIT_SHIFT;
+		break;
+	case TSENS_TRIP_STAGE0:
+		reg = (reg & TSENS_THRESHOLD_MIN_LIMIT_MASK)
+					>> TSENS_THRESHOLD_MIN_LIMIT_SHIFT;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	*temp = tsens_tz_code_to_degC(reg, tm_sensor->sensor_num);
+
+	return 0;
+}
+
+static int tsens_tz_get_crit_temp(struct thermal_zone_device *thermal,
+				  unsigned long *temp)
+{
+	return tsens_tz_get_trip_temp(thermal, TSENS_TRIP_STAGE3, temp);
+}
+
+static int tsens_tz_notify(struct thermal_zone_device *thermal,
+				int count, enum thermal_trip_type type)
+{
+	/* TSENS driver does not shutdown the device.
+	   All Thermal notification are sent to the
+	   thermal daemon to take appropriate action */
+	return 1;
+}
+
+static int tsens_tz_set_trip_temp(struct thermal_zone_device *thermal,
+				   int trip, long temp)
+{
+	struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+	unsigned int reg_th, reg_cntl;
+	int code, hi_code, lo_code, code_err_chk;
+
+	code_err_chk = code = tsens_tz_degC_to_code(temp,
+					tm_sensor->sensor_num);
+	if (!tm_sensor || trip < 0)
+		return -EINVAL;
+
+	lo_code = TSENS_THRESHOLD_MIN_CODE;
+	hi_code = TSENS_THRESHOLD_MAX_CODE;
+
+	if (tmdev->hw_type == APQ_8064)
+		reg_cntl = readl_relaxed(TSENS_8064_STATUS_CNTL);
+	else
+		reg_cntl = readl_relaxed(TSENS_CNTL_ADDR);
+	reg_th = readl_relaxed(TSENS_THRESHOLD_ADDR);
+	switch (trip) {
+	case TSENS_TRIP_STAGE3:
+		code <<= TSENS_THRESHOLD_MAX_LIMIT_SHIFT;
+		reg_th &= ~TSENS_THRESHOLD_MAX_LIMIT_MASK;
+
+		if (!(reg_cntl & TSENS_UPPER_STATUS_CLR))
+			lo_code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK)
+					>> TSENS_THRESHOLD_UPPER_LIMIT_SHIFT;
+		else if (!(reg_cntl & TSENS_LOWER_STATUS_CLR))
+			lo_code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK)
+					>> TSENS_THRESHOLD_LOWER_LIMIT_SHIFT;
+		else if (!(reg_cntl & TSENS_MIN_STATUS_MASK))
+			lo_code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK)
+					>> TSENS_THRESHOLD_MIN_LIMIT_SHIFT;
+		break;
+	case TSENS_TRIP_STAGE2:
+		code <<= TSENS_THRESHOLD_UPPER_LIMIT_SHIFT;
+		reg_th &= ~TSENS_THRESHOLD_UPPER_LIMIT_MASK;
+
+		if (!(reg_cntl & TSENS_MAX_STATUS_MASK))
+			hi_code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK)
+					>> TSENS_THRESHOLD_MAX_LIMIT_SHIFT;
+		if (!(reg_cntl & TSENS_LOWER_STATUS_CLR))
+			lo_code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK)
+					>> TSENS_THRESHOLD_LOWER_LIMIT_SHIFT;
+		else if (!(reg_cntl & TSENS_MIN_STATUS_MASK))
+			lo_code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK)
+					>> TSENS_THRESHOLD_MIN_LIMIT_SHIFT;
+		break;
+	case TSENS_TRIP_STAGE1:
+		code <<= TSENS_THRESHOLD_LOWER_LIMIT_SHIFT;
+		reg_th &= ~TSENS_THRESHOLD_LOWER_LIMIT_MASK;
+
+		if (!(reg_cntl & TSENS_MIN_STATUS_MASK))
+			lo_code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK)
+					>> TSENS_THRESHOLD_MIN_LIMIT_SHIFT;
+		if (!(reg_cntl & TSENS_UPPER_STATUS_CLR))
+			hi_code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK)
+					>> TSENS_THRESHOLD_UPPER_LIMIT_SHIFT;
+		else if (!(reg_cntl & TSENS_MAX_STATUS_MASK))
+			hi_code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK)
+					>> TSENS_THRESHOLD_MAX_LIMIT_SHIFT;
+		break;
+	case TSENS_TRIP_STAGE0:
+		code <<= TSENS_THRESHOLD_MIN_LIMIT_SHIFT;
+		reg_th &= ~TSENS_THRESHOLD_MIN_LIMIT_MASK;
+
+		if (!(reg_cntl & TSENS_LOWER_STATUS_CLR))
+			hi_code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK)
+					>> TSENS_THRESHOLD_LOWER_LIMIT_SHIFT;
+		else if (!(reg_cntl & TSENS_UPPER_STATUS_CLR))
+			hi_code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK)
+					>> TSENS_THRESHOLD_UPPER_LIMIT_SHIFT;
+		else if (!(reg_cntl & TSENS_MAX_STATUS_MASK))
+			hi_code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK)
+					>> TSENS_THRESHOLD_MAX_LIMIT_SHIFT;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (code_err_chk < lo_code || code_err_chk > hi_code)
+		return -EINVAL;
+
+	writel_relaxed(reg_th | code, TSENS_THRESHOLD_ADDR);
+
+	return 0;
+}
+
+static struct thermal_zone_device_ops tsens_thermal_zone_ops = {
+	.get_temp = tsens_tz_get_temp,
+	.get_mode = tsens_tz_get_mode,
+	.set_mode = tsens_tz_set_mode,
+	.get_trip_type = tsens_tz_get_trip_type,
+	.activate_trip_type = tsens_tz_activate_trip_type,
+	.get_trip_temp = tsens_tz_get_trip_temp,
+	.set_trip_temp = tsens_tz_set_trip_temp,
+	.get_crit_temp = tsens_tz_get_crit_temp,
+	.notify = tsens_tz_notify,
+};
+
+static void notify_uspace_tsens_fn(struct work_struct *work)
+{
+	struct tsens_tm_device_sensor *tm = container_of(work,
+		struct tsens_tm_device_sensor, work);
+
+	sysfs_notify(&tm->tz_dev->device.kobj,
+					NULL, "type");
+}
+
+static irqreturn_t tsens_isr(int irq, void *data)
+{
+	struct tsens_tm_device *tm = data;
+	unsigned int threshold, threshold_low, i, code, reg, sensor, mask;
+	unsigned int sensor_addr;
+	bool upper_th_x, lower_th_x;
+	int adc_code;
+
+	if (tmdev->hw_type == APQ_8064) {
+		reg = readl_relaxed(TSENS_8064_STATUS_CNTL);
+		writel_relaxed(reg | TSENS_LOWER_STATUS_CLR |
+			TSENS_UPPER_STATUS_CLR, TSENS_8064_STATUS_CNTL);
+	} else {
+		reg = readl_relaxed(TSENS_CNTL_ADDR);
+		writel_relaxed(reg | TSENS_LOWER_STATUS_CLR |
+			TSENS_UPPER_STATUS_CLR, TSENS_CNTL_ADDR);
+	}
+	mask = ~(TSENS_LOWER_STATUS_CLR | TSENS_UPPER_STATUS_CLR);
+	threshold = readl_relaxed(TSENS_THRESHOLD_ADDR);
+	threshold_low = (threshold & TSENS_THRESHOLD_LOWER_LIMIT_MASK)
+					>> TSENS_THRESHOLD_LOWER_LIMIT_SHIFT;
+	threshold = (threshold & TSENS_THRESHOLD_UPPER_LIMIT_MASK)
+					>> TSENS_THRESHOLD_UPPER_LIMIT_SHIFT;
+	sensor = readl_relaxed(TSENS_CNTL_ADDR);
+	if (tmdev->hw_type == APQ_8064) {
+		reg = readl_relaxed(TSENS_8064_STATUS_CNTL);
+		sensor &= (uint32_t) TSENS_8064_SENSORS_EN;
+	} else {
+		reg = sensor;
+		sensor &= (uint32_t) SENSORS_EN;
+	}
+	sensor >>= TSENS_SENSOR0_SHIFT;
+	sensor_addr = (unsigned int)TSENS_S0_STATUS_ADDR;
+	for (i = 0; i < tmdev->tsens_num_sensor; i++) {
+		if (i == TSENS_8064_SEQ_SENSORS)
+			sensor_addr += TSENS_8064_S4_S5_OFFSET;
+		if (sensor & TSENS_MASK1) {
+			code = readl_relaxed(sensor_addr);
+			upper_th_x = code >= threshold;
+			lower_th_x = code <= threshold_low;
+			if (upper_th_x)
+				mask |= TSENS_UPPER_STATUS_CLR;
+			if (lower_th_x)
+				mask |= TSENS_LOWER_STATUS_CLR;
+			if (upper_th_x || lower_th_x) {
+				/* Notify user space */
+				schedule_work(&tm->sensor[i].work);
+				adc_code = readl_relaxed(sensor_addr);
+				pr_info("\nTrip point triggered by "
+					"current temperature (%d degrees) "
+					"measured by Temperature-Sensor %d\n",
+					tsens_tz_code_to_degC(adc_code, i), i);
+			}
+		}
+		sensor >>= 1;
+		sensor_addr += TSENS_SENSOR_STATUS_SIZE;
+	}
+	if (tmdev->hw_type == APQ_8064)
+		writel_relaxed(reg & mask, TSENS_8064_STATUS_CNTL);
+	else
+	writel_relaxed(reg & mask, TSENS_CNTL_ADDR);
+	mb();
+	return IRQ_HANDLED;
+}
+
+static void tsens8960_sensor_mode_init(void)
+{
+	unsigned int reg_cntl = 0;
+
+	reg_cntl = readl_relaxed(TSENS_CNTL_ADDR);
+	if (tmdev->hw_type == MSM_8960 || tmdev->hw_type == MDM_9615 ||
+			tmdev->hw_type == APQ_8064) {
+		writel_relaxed(reg_cntl &
+				~((((1 << tmdev->tsens_num_sensor) - 1) >> 1)
+				<< (TSENS_SENSOR0_SHIFT + 1)), TSENS_CNTL_ADDR);
+		tmdev->sensor[TSENS_MAIN_SENSOR].mode = THERMAL_DEVICE_ENABLED;
+	}
+}
+
+#ifdef CONFIG_PM
+static int tsens_suspend(struct device *dev)
+{
+	int i = 0;
+
+	tmdev->pm_tsens_thr_data = readl_relaxed(TSENS_THRESHOLD_ADDR);
+	tmdev->pm_tsens_cntl = readl_relaxed(TSENS_CNTL_ADDR);
+	writel_relaxed(tmdev->pm_tsens_cntl &
+		~(TSENS_8960_SLP_CLK_ENA | TSENS_EN), TSENS_CNTL_ADDR);
+	tmdev->prev_reading_avail = 0;
+	for (i = 0; i < tmdev->tsens_num_sensor; i++)
+		tmdev->sensor[i].mode = THERMAL_DEVICE_DISABLED;
+	disable_irq_nosync(TSENS_UPPER_LOWER_INT);
+	mb();
+	return 0;
+}
+
+static int tsens_resume(struct device *dev)
+{
+	unsigned int reg_cntl = 0, reg_cfg = 0, reg_sensor_mask = 0;
+	unsigned int reg_status_cntl = 0, reg_thr_data = 0, i = 0;
+
+	reg_cntl = readl_relaxed(TSENS_CNTL_ADDR);
+	writel_relaxed(reg_cntl | TSENS_SW_RST, TSENS_CNTL_ADDR);
+
+	if (tmdev->hw_type == MSM_8960 || tmdev->hw_type == MDM_9615) {
+		reg_cntl |= TSENS_8960_SLP_CLK_ENA |
+			(TSENS_MEASURE_PERIOD << 18) |
+			TSENS_MIN_STATUS_MASK | TSENS_MAX_STATUS_MASK |
+			SENSORS_EN;
+		writel_relaxed(reg_cntl, TSENS_CNTL_ADDR);
+	} else if (tmdev->hw_type == APQ_8064) {
+		reg_cntl |= TSENS_8960_SLP_CLK_ENA |
+			(TSENS_MEASURE_PERIOD << 18) |
+			(((1 << tmdev->tsens_num_sensor) - 1)
+					<< TSENS_SENSOR0_SHIFT);
+		writel_relaxed(reg_cntl, TSENS_CNTL_ADDR);
+		reg_status_cntl = readl_relaxed(TSENS_8064_STATUS_CNTL);
+		reg_status_cntl |= TSENS_MIN_STATUS_MASK |
+			TSENS_MAX_STATUS_MASK;
+		writel_relaxed(reg_status_cntl, TSENS_8064_STATUS_CNTL);
+	}
+
+	reg_cfg = readl_relaxed(TSENS_8960_CONFIG_ADDR);
+	reg_cfg = (reg_cfg & ~TSENS_8960_CONFIG_MASK) |
+		(TSENS_8960_CONFIG << TSENS_8960_CONFIG_SHIFT);
+	writel_relaxed(reg_cfg, TSENS_8960_CONFIG_ADDR);
+
+	writel_relaxed((tmdev->pm_tsens_cntl & TSENS_CNTL_RESUME_MASK),
+						TSENS_CNTL_ADDR);
+	reg_cntl = readl_relaxed(TSENS_CNTL_ADDR);
+	writel_relaxed(tmdev->pm_tsens_thr_data, TSENS_THRESHOLD_ADDR);
+	reg_thr_data = readl_relaxed(TSENS_THRESHOLD_ADDR);
+	if (tmdev->hw_type == MSM_8960 || tmdev->hw_type == MDM_9615)
+		reg_sensor_mask = ((reg_cntl & TSENS_8960_SENSOR_MASK)
+				>> TSENS_SENSOR0_SHIFT);
+	else {
+		reg_sensor_mask = ((reg_cntl & TSENS_8064_SENSOR_MASK)
+				>> TSENS_SENSOR0_SHIFT);
+	}
+
+	for (i = 0; i < tmdev->tsens_num_sensor; i++) {
+		if (reg_sensor_mask & TSENS_MASK1)
+			tmdev->sensor[i].mode = THERMAL_DEVICE_ENABLED;
+		reg_sensor_mask >>= 1;
+	}
+
+	enable_irq(TSENS_UPPER_LOWER_INT);
+	mb();
+	return 0;
+}
+
+static const struct dev_pm_ops tsens_pm_ops = {
+	.suspend	= tsens_suspend,
+	.resume		= tsens_resume,
+};
+#endif
+
+static void tsens_disable_mode(void)
+{
+	unsigned int reg_cntl = 0;
+
+	reg_cntl = readl_relaxed(TSENS_CNTL_ADDR);
+	if (tmdev->hw_type == MSM_8960 || tmdev->hw_type == MDM_9615 ||
+			tmdev->hw_type == APQ_8064)
+		writel_relaxed(reg_cntl &
+				~((((1 << tmdev->tsens_num_sensor) - 1) <<
+				TSENS_SENSOR0_SHIFT) | TSENS_8960_SLP_CLK_ENA
+				| TSENS_EN), TSENS_CNTL_ADDR);
+	else if (tmdev->hw_type == MSM_8660)
+		writel_relaxed(reg_cntl &
+				~((((1 << tmdev->tsens_num_sensor) - 1) <<
+				TSENS_SENSOR0_SHIFT) | TSENS_8660_SLP_CLK_ENA
+				| TSENS_EN), TSENS_CNTL_ADDR);
+}
+
+static void tsens_hw_init(void)
+{
+	unsigned int reg_cntl = 0, reg_cfg = 0, reg_thr = 0;
+	unsigned int reg_status_cntl = 0;
+
+	reg_cntl = readl_relaxed(TSENS_CNTL_ADDR);
+	writel_relaxed(reg_cntl | TSENS_SW_RST, TSENS_CNTL_ADDR);
+
+	if (tmdev->hw_type == MSM_8960 || tmdev->hw_type == MDM_9615) {
+		reg_cntl |= TSENS_8960_SLP_CLK_ENA |
+			(TSENS_MEASURE_PERIOD << 18) |
+			TSENS_LOWER_STATUS_CLR | TSENS_UPPER_STATUS_CLR |
+			TSENS_MIN_STATUS_MASK | TSENS_MAX_STATUS_MASK |
+			SENSORS_EN;
+		writel_relaxed(reg_cntl, TSENS_CNTL_ADDR);
+		reg_cntl |= TSENS_EN;
+		writel_relaxed(reg_cntl, TSENS_CNTL_ADDR);
+
+		reg_cfg = readl_relaxed(TSENS_8960_CONFIG_ADDR);
+		reg_cfg = (reg_cfg & ~TSENS_8960_CONFIG_MASK) |
+			(TSENS_8960_CONFIG << TSENS_8960_CONFIG_SHIFT);
+		writel_relaxed(reg_cfg, TSENS_8960_CONFIG_ADDR);
+	} else if (tmdev->hw_type == MSM_8660) {
+		reg_cntl |= TSENS_8660_SLP_CLK_ENA | TSENS_EN |
+			(TSENS_MEASURE_PERIOD << 16) |
+			TSENS_LOWER_STATUS_CLR | TSENS_UPPER_STATUS_CLR |
+			TSENS_MIN_STATUS_MASK | TSENS_MAX_STATUS_MASK |
+			(((1 << tmdev->tsens_num_sensor) - 1) <<
+			TSENS_SENSOR0_SHIFT);
+
+		/* set TSENS_CONFIG bits (bits 29:28 of TSENS_CNTL) to '01';
+			this setting found to be optimal. */
+		reg_cntl = (reg_cntl & ~TSENS_8660_CONFIG_MASK) |
+				(TSENS_8660_CONFIG << TSENS_8660_CONFIG_SHIFT);
+
+		writel_relaxed(reg_cntl, TSENS_CNTL_ADDR);
+	} else if (tmdev->hw_type == APQ_8064) {
+		reg_cntl |= TSENS_8960_SLP_CLK_ENA |
+			(TSENS_MEASURE_PERIOD << 18) |
+			(((1 << tmdev->tsens_num_sensor) - 1)
+					<< TSENS_SENSOR0_SHIFT);
+		writel_relaxed(reg_cntl, TSENS_CNTL_ADDR);
+		reg_status_cntl = readl_relaxed(TSENS_8064_STATUS_CNTL);
+		reg_status_cntl |= TSENS_LOWER_STATUS_CLR |
+			TSENS_UPPER_STATUS_CLR |
+			TSENS_MIN_STATUS_MASK |
+			TSENS_MAX_STATUS_MASK;
+		writel_relaxed(reg_status_cntl, TSENS_8064_STATUS_CNTL);
+		reg_cntl |= TSENS_EN;
+		writel_relaxed(reg_cntl, TSENS_CNTL_ADDR);
+
+		reg_cfg = readl_relaxed(TSENS_8960_CONFIG_ADDR);
+		reg_cfg = (reg_cfg & ~TSENS_8960_CONFIG_MASK) |
+			(TSENS_8960_CONFIG << TSENS_8960_CONFIG_SHIFT);
+		writel_relaxed(reg_cfg, TSENS_8960_CONFIG_ADDR);
+	}
+
+	reg_thr |= (TSENS_LOWER_LIMIT_TH << TSENS_THRESHOLD_LOWER_LIMIT_SHIFT) |
+		(TSENS_UPPER_LIMIT_TH << TSENS_THRESHOLD_UPPER_LIMIT_SHIFT) |
+		(TSENS_MIN_LIMIT_TH << TSENS_THRESHOLD_MIN_LIMIT_SHIFT) |
+		(TSENS_MAX_LIMIT_TH << TSENS_THRESHOLD_MAX_LIMIT_SHIFT);
+	writel_relaxed(reg_thr, TSENS_THRESHOLD_ADDR);
+}
+
+static int tsens_calib_sensors8660(void)
+{
+	uint32_t *main_sensor_addr, sensor_shift, red_sensor_shift;
+	uint32_t sensor_mask, red_sensor_mask;
+
+	main_sensor_addr = TSENS_8660_QFPROM_ADDR;
+	sensor_shift = TSENS_SENSOR_SHIFT;
+	red_sensor_shift = sensor_shift + TSENS_RED_SHIFT;
+	sensor_mask = TSENS_THRESHOLD_MAX_CODE << sensor_shift;
+	red_sensor_mask = TSENS_THRESHOLD_MAX_CODE << red_sensor_shift;
+	tmdev->sensor[TSENS_MAIN_SENSOR].calib_data =
+		(readl_relaxed(main_sensor_addr) & sensor_mask)
+						>> sensor_shift;
+	tmdev->sensor[TSENS_MAIN_SENSOR].calib_data_backup =
+				(readl_relaxed(main_sensor_addr)
+			& red_sensor_mask) >> red_sensor_shift;
+	if (tmdev->sensor[TSENS_MAIN_SENSOR].calib_data_backup)
+			tmdev->sensor[TSENS_MAIN_SENSOR].calib_data =
+			tmdev->sensor[TSENS_MAIN_SENSOR].calib_data_backup;
+	if (!tmdev->sensor[TSENS_MAIN_SENSOR].calib_data) {
+		pr_err("%s: No temperature sensor data for calibration"
+				" in QFPROM!\n", __func__);
+		return -ENODEV;
+	}
+
+	tmdev->sensor[TSENS_MAIN_SENSOR].offset = tmdev->tsens_factor *
+		TSENS_CAL_DEGC -
+		tmdev->sensor[TSENS_MAIN_SENSOR].slope_mul_tsens_factor *
+		tmdev->sensor[TSENS_MAIN_SENSOR].calib_data;
+
+	tmdev->prev_reading_avail = false;
+	INIT_WORK(&tmdev->sensor[TSENS_MAIN_SENSOR].work,
+						notify_uspace_tsens_fn);
+
+	return 0;
+}
+
+static int tsens_calib_sensors8960(void)
+{
+	uint32_t i;
+	uint8_t *main_sensor_addr, *backup_sensor_addr;
+	for (i = 0; i < tmdev->tsens_num_sensor; i++) {
+		main_sensor_addr = TSENS_8960_QFPROM_ADDR0 + i;
+		backup_sensor_addr = TSENS_8960_QFPROM_SPARE_ADDR0 + i;
+
+		tmdev->sensor[i].calib_data = readb_relaxed(main_sensor_addr);
+		tmdev->sensor[i].calib_data_backup =
+			readb_relaxed(backup_sensor_addr);
+		if (tmdev->sensor[i].calib_data_backup)
+			tmdev->sensor[i].calib_data =
+				tmdev->sensor[i].calib_data_backup;
+		if (!tmdev->sensor[i].calib_data) {
+			WARN(1, "%s: No temperature sensor:%d data for"
+			" calibration in QFPROM!\n", __func__, i);
+			return -ENODEV;
+		}
+		tmdev->sensor[i].offset = (TSENS_CAL_DEGC *
+			tmdev->tsens_factor)
+			- (tmdev->sensor[i].calib_data *
+			tmdev->sensor[i].slope_mul_tsens_factor);
+		tmdev->prev_reading_avail = false;
+		INIT_WORK(&tmdev->sensor[i].work, notify_uspace_tsens_fn);
+	}
+
+	return 0;
+}
+
+static int tsens_check_version_support(void)
+{
+	int rc = 0;
+
+	if (tmdev->hw_type == MSM_8960)
+		if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 1)
+			rc = -ENODEV;
+
+	return rc;
+}
+
+static int tsens_calib_sensors(void)
+{
+	int rc = -ENODEV;
+
+	if (tmdev->hw_type == MSM_8660)
+		rc = tsens_calib_sensors8660();
+	else if (tmdev->hw_type == MSM_8960 || tmdev->hw_type == MDM_9615 ||
+		tmdev->hw_type == APQ_8064)
+		rc = tsens_calib_sensors8960();
+
+	return rc;
+}
+
+int msm_tsens_early_init(struct tsens_platform_data *pdata)
+{
+	int rc = 0, i;
+
+	if (!pdata) {
+		pr_err("No TSENS Platform data\n");
+		return -EINVAL;
+	}
+
+	tmdev = kzalloc(sizeof(struct tsens_tm_device) +
+			pdata->tsens_num_sensor *
+			sizeof(struct tsens_tm_device_sensor),
+			GFP_ATOMIC);
+	if (tmdev == NULL) {
+		pr_err("%s: kzalloc() failed.\n", __func__);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < pdata->tsens_num_sensor; i++)
+		tmdev->sensor[i].slope_mul_tsens_factor = pdata->slope[i];
+	tmdev->tsens_factor = pdata->tsens_factor;
+	tmdev->tsens_num_sensor = pdata->tsens_num_sensor;
+	tmdev->hw_type = pdata->hw_type;
+
+	rc = tsens_check_version_support();
+	if (rc < 0) {
+		kfree(tmdev);
+		tmdev = NULL;
+		return rc;
+	}
+
+	rc = tsens_calib_sensors();
+	if (rc < 0) {
+		kfree(tmdev);
+		tmdev = NULL;
+		return rc;
+	}
+
+	if (tmdev->hw_type == APQ_8064)
+		tsens_status_cntl_start = 0;
+	else
+		tsens_status_cntl_start = TSENS_STATUS_CNTL_OFFSET;
+
+	tsens_hw_init();
+
+	pr_debug("msm_tsens_early_init: done\n");
+
+	return rc;
+}
+
+static int __devinit tsens_tm_probe(struct platform_device *pdev)
+{
+	int rc, i;
+
+	if (!tmdev) {
+		pr_info("%s : TSENS early init not done.\n", __func__);
+		return -EFAULT;
+	}
+
+	for (i = 0; i < tmdev->tsens_num_sensor; i++) {
+		char name[18];
+		snprintf(name, sizeof(name), "tsens_tz_sensor%d", i);
+		tmdev->sensor[i].mode = THERMAL_DEVICE_ENABLED;
+		tmdev->sensor[i].sensor_num = i;
+		tmdev->sensor[i].tz_dev = thermal_zone_device_register(name,
+				TSENS_TRIP_NUM, &tmdev->sensor[i],
+				&tsens_thermal_zone_ops, 0, 0, 0, 0);
+		if (IS_ERR(tmdev->sensor[i].tz_dev)) {
+			pr_err("%s: thermal_zone_device_register() failed.\n",
+			__func__);
+			rc = -ENODEV;
+			goto fail;
+		}
+		tmdev->sensor[i].mode = THERMAL_DEVICE_DISABLED;
+	}
+
+	tsens8960_sensor_mode_init();
+
+	rc = request_irq(TSENS_UPPER_LOWER_INT, tsens_isr,
+		IRQF_TRIGGER_RISING, "tsens_interrupt", tmdev);
+	if (rc < 0) {
+		pr_err("%s: request_irq FAIL: %d\n", __func__, rc);
+		for (i = 0; i < tmdev->tsens_num_sensor; i++)
+			thermal_zone_device_unregister(tmdev->sensor[i].tz_dev);
+		goto fail;
+	}
+
+	pr_debug("%s: OK\n", __func__);
+	mb();
+	return 0;
+fail:
+	tsens_disable_mode();
+	kfree(tmdev);
+	tmdev = NULL;
+	mb();
+	return rc;
+}
+
+static int __devexit tsens_tm_remove(struct platform_device *pdev)
+{
+	int i;
+
+	tsens_disable_mode();
+	mb();
+	free_irq(TSENS_UPPER_LOWER_INT, tmdev);
+	for (i = 0; i < tmdev->tsens_num_sensor; i++)
+		thermal_zone_device_unregister(tmdev->sensor[i].tz_dev);
+	kfree(tmdev);
+	tmdev = NULL;
+	return 0;
+}
+
+static struct platform_driver tsens_tm_driver = {
+	.probe = tsens_tm_probe,
+	.remove = tsens_tm_remove,
+	.driver = {
+		.name = "tsens8960-tm",
+		.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm	= &tsens_pm_ops,
+#endif
+	},
+};
+
+static int __init _tsens_tm_init(void)
+{
+	return platform_driver_register(&tsens_tm_driver);
+}
+module_init(_tsens_tm_init);
+
+static void __exit _tsens_tm_remove(void)
+{
+	platform_driver_unregister(&tsens_tm_driver);
+}
+module_exit(_tsens_tm_remove);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MSM8960 Temperature Sensor driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:tsens8960-tm");
diff --git a/drivers/thermal/msm_popmem-tm.c b/drivers/thermal/msm_popmem-tm.c
new file mode 100644
index 0000000..583b2db
--- /dev/null
+++ b/drivers/thermal/msm_popmem-tm.c
@@ -0,0 +1,284 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/thermal.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <mach/msm_memtypes.h>
+
+#define POP_MEM_LPDDR1_REFRESH_MASK	0x00000700
+#define POP_MEM_LPDDR1_REFRESH_SHIFT	0x8
+
+#define POP_MEM_LPDDR2_REFRESH_MASK	0x00000007
+#define POP_MEM_LPDDR2_REFRESH_SHIFT	0x0
+
+#define POP_MEM_REFRESH_REG		0x3C
+
+#define POP_MEM_LOW_TEMPERATURE		25000
+#define POP_MEM_NORMAL_TEMPERATURE	50000
+#define POP_MEM_HIGH_TEMPERATURE	85000
+
+#define POP_MEM_TRIP_OUT_OF_SPEC	0
+#define POP_MEM_TRIP_NUM		1
+
+struct pop_mem_tm_device {
+	unsigned long			baseaddr;
+	struct thermal_zone_device	*tz_dev;
+	unsigned long			refresh_mask;
+	unsigned int			refresh_shift;
+};
+
+
+static int pop_mem_tm_read_refresh(struct pop_mem_tm_device *tm,
+				   unsigned int *ref_rate){
+	unsigned int ref;
+
+	ref = __raw_readl(tm->baseaddr + POP_MEM_REFRESH_REG);
+	*ref_rate = (ref & tm->refresh_mask) >> tm->refresh_shift;
+
+	return 0;
+}
+
+
+static int pop_mem_tm_get_temperature(struct thermal_zone_device *thermal,
+			       unsigned long *temperature)
+{
+	struct pop_mem_tm_device *tm = thermal->devdata;
+	unsigned int ref_rate;
+	int rc;
+
+	if (!tm || !temperature)
+		return -EINVAL;
+
+	rc = pop_mem_tm_read_refresh(tm, &ref_rate);
+	if (rc < 0)
+		return rc;
+
+	switch (ref_rate) {
+	case 0:
+	case 1:
+	case 2:
+		*temperature = POP_MEM_LOW_TEMPERATURE;
+		break;
+	case 3:
+	case 4:
+		*temperature = POP_MEM_NORMAL_TEMPERATURE;
+		break;
+	case 5:
+	case 6:
+	case 7:
+		*temperature = POP_MEM_HIGH_TEMPERATURE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int pop_mem_tm_get_trip_type(struct thermal_zone_device *thermal,
+				    int trip, enum thermal_trip_type *type)
+{
+	struct pop_mem_tm_device *tm = thermal->devdata;
+
+	if (!tm || trip < 0 || !type)
+		return -EINVAL;
+
+	if (trip == POP_MEM_TRIP_OUT_OF_SPEC)
+		*type = THERMAL_TRIP_CRITICAL;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+static int pop_mem_tm_get_trip_temperature(struct thermal_zone_device *thermal,
+				    int trip, unsigned long *temperature)
+{
+	struct pop_mem_tm_device *tm = thermal->devdata;
+
+	if (!tm || trip < 0 || !temperature)
+		return -EINVAL;
+
+	if (trip == POP_MEM_TRIP_OUT_OF_SPEC)
+		*temperature = POP_MEM_HIGH_TEMPERATURE;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+
+static int pop_mem_tm_get_crit_temperature(struct thermal_zone_device *thermal,
+				    unsigned long *temperature)
+{
+	struct pop_mem_tm_device *tm = thermal->devdata;
+
+	if (!tm || !temperature)
+		return -EINVAL;
+
+	*temperature = POP_MEM_HIGH_TEMPERATURE;
+
+	return 0;
+}
+
+
+static struct thermal_zone_device_ops pop_mem_thermal_zone_ops = {
+	.get_temp = pop_mem_tm_get_temperature,
+	.get_trip_type = pop_mem_tm_get_trip_type,
+	.get_trip_temp = pop_mem_tm_get_trip_temperature,
+	.get_crit_temp = pop_mem_tm_get_crit_temperature,
+};
+
+
+static int __devinit pop_mem_tm_probe(struct platform_device *pdev)
+{
+	int rc, len, numcontrollers;
+	struct resource *controller_mem = NULL;
+	struct resource *res_mem = NULL;
+	struct pop_mem_tm_device *tmdev = NULL;
+	void __iomem *base = NULL;
+
+	rc = len = 0;
+	numcontrollers = get_num_populated_chipselects();
+
+	if (pdev->id >= numcontrollers) {
+		pr_err("%s: memory controller %d does not exist", __func__,
+			pdev->id);
+		rc = -ENODEV;
+		goto fail;
+	}
+
+	controller_mem = platform_get_resource_byname(pdev,
+						  IORESOURCE_MEM, "physbase");
+	if (!controller_mem) {
+		pr_err("%s: could not get resources for controller %d",
+			__func__, pdev->id);
+		rc = -EFAULT;
+		goto fail;
+	}
+
+	len = controller_mem->end - controller_mem->start + 1;
+
+	res_mem = request_mem_region(controller_mem->start, len,
+				     controller_mem->name);
+	if (!res_mem) {
+		pr_err("%s: Could not request memory region: "
+			"start=%p, len=%d\n", __func__,
+			(void *) controller_mem->start, len);
+		rc = -EBUSY;
+		goto fail;
+
+	}
+
+	base = ioremap(res_mem->start, len);
+	if (!base) {
+		pr_err("%s: Could not ioremap: start=%p, len=%d\n",
+			 __func__, (void *) controller_mem->start, len);
+		rc = -EBUSY;
+		goto fail;
+
+	}
+
+	tmdev = kzalloc(sizeof(*tmdev), GFP_KERNEL);
+	if (tmdev == NULL) {
+		pr_err("%s: kzalloc() failed.\n", __func__);
+		rc = -ENOMEM;
+		goto fail;
+	}
+
+	if (numcontrollers == 1) {
+		tmdev->refresh_mask = POP_MEM_LPDDR1_REFRESH_MASK;
+		tmdev->refresh_shift = POP_MEM_LPDDR1_REFRESH_SHIFT;
+	} else {
+		tmdev->refresh_mask = POP_MEM_LPDDR2_REFRESH_MASK;
+		tmdev->refresh_shift = POP_MEM_LPDDR2_REFRESH_SHIFT;
+	}
+	tmdev->baseaddr = (unsigned long) base;
+	tmdev->tz_dev = thermal_zone_device_register("msm_popmem_tz",
+						     POP_MEM_TRIP_NUM, tmdev,
+						     &pop_mem_thermal_zone_ops,
+						     0, 0, 0, 0);
+
+	if (tmdev->tz_dev == NULL) {
+		pr_err("%s: thermal_zone_device_register() failed.\n",
+			__func__);
+		goto fail;
+	}
+
+	platform_set_drvdata(pdev, tmdev);
+
+	pr_notice("%s: device %d probed successfully\n", __func__, pdev->id);
+
+	return rc;
+
+fail:
+	if (base)
+		iounmap(base);
+	if (res_mem)
+		release_mem_region(controller_mem->start, len);
+	kfree(tmdev);
+
+	return rc;
+}
+
+static int __devexit pop_mem_tm_remove(struct platform_device *pdev)
+{
+
+	int len;
+	struct pop_mem_tm_device *tmdev = platform_get_drvdata(pdev);
+	struct resource *controller_mem;
+
+	iounmap((void __iomem *)tmdev->baseaddr);
+
+	controller_mem = platform_get_resource_byname(pdev,
+						  IORESOURCE_MEM, "physbase");
+	len = controller_mem->end - controller_mem->start + 1;
+	release_mem_region(controller_mem->start, len);
+
+	thermal_zone_device_unregister(tmdev->tz_dev);
+	platform_set_drvdata(pdev, NULL);
+	kfree(tmdev);
+
+	return 0;
+}
+
+static struct platform_driver pop_mem_tm_driver = {
+	.probe          = pop_mem_tm_probe,
+	.remove         = pop_mem_tm_remove,
+	.driver         = {
+		.name = "msm_popmem-tm",
+		.owner = THIS_MODULE
+	},
+};
+
+static int __init pop_mem_tm_init(void)
+{
+	return platform_driver_register(&pop_mem_tm_driver);
+}
+
+static void __exit pop_mem_tm_exit(void)
+{
+	platform_driver_unregister(&pop_mem_tm_driver);
+}
+
+module_init(pop_mem_tm_init);
+module_exit(pop_mem_tm_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Pop memory thermal manager driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:popmem-tm");
diff --git a/drivers/thermal/msm_thermal.c b/drivers/thermal/msm_thermal.c
new file mode 100644
index 0000000..e0d8d14
--- /dev/null
+++ b/drivers/thermal/msm_thermal.c
@@ -0,0 +1,171 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/cpufreq.h>
+#include <linux/mutex.h>
+#include <linux/msm_tsens.h>
+#include <linux/workqueue.h>
+#include <linux/cpu.h>
+
+#define DEF_TEMP_SENSOR      0
+#define DEF_THERMAL_CHECK_MS 1000
+#define DEF_ALLOWED_MAX_HIGH 60
+#define DEF_ALLOWED_MAX_FREQ 918000
+
+static int enabled;
+static int allowed_max_high = DEF_ALLOWED_MAX_HIGH;
+static int allowed_max_low = (DEF_ALLOWED_MAX_HIGH - 10);
+static int allowed_max_freq = DEF_ALLOWED_MAX_FREQ;
+static int check_interval_ms = DEF_THERMAL_CHECK_MS;
+
+module_param(allowed_max_high, int, 0);
+module_param(allowed_max_freq, int, 0);
+module_param(check_interval_ms, int, 0);
+
+static struct delayed_work check_temp_work;
+
+static int update_cpu_max_freq(struct cpufreq_policy *cpu_policy,
+			       int cpu, int max_freq)
+{
+	int ret = 0;
+
+	if (!cpu_policy)
+		return -EINVAL;
+
+	cpufreq_verify_within_limits(cpu_policy,
+				cpu_policy->min, max_freq);
+	cpu_policy->user_policy.max = max_freq;
+
+	ret = cpufreq_update_policy(cpu);
+	if (!ret)
+		pr_info("msm_thermal: Limiting core%d max frequency to %d\n",
+			cpu, max_freq);
+
+	return ret;
+}
+
+static void check_temp(struct work_struct *work)
+{
+	struct cpufreq_policy *cpu_policy = NULL;
+	struct tsens_device tsens_dev;
+	unsigned long temp = 0;
+	unsigned int max_freq = 0;
+	int update_policy = 0;
+	int cpu = 0;
+	int ret = 0;
+
+	tsens_dev.sensor_num = DEF_TEMP_SENSOR;
+	ret = tsens_get_temp(&tsens_dev, &temp);
+	if (ret) {
+		pr_debug("msm_thermal: Unable to read TSENS sensor %d\n",
+				tsens_dev.sensor_num);
+		goto reschedule;
+	}
+
+	for_each_possible_cpu(cpu) {
+		update_policy = 0;
+		cpu_policy = cpufreq_cpu_get(cpu);
+		if (!cpu_policy) {
+			pr_debug("msm_thermal: NULL policy on cpu %d\n", cpu);
+			continue;
+		}
+		if (temp >= allowed_max_high) {
+			if (cpu_policy->max > allowed_max_freq) {
+				update_policy = 1;
+				max_freq = allowed_max_freq;
+			} else {
+				pr_debug("msm_thermal: policy max for cpu %d "
+					 "already < allowed_max_freq\n", cpu);
+			}
+		} else if (temp < allowed_max_low) {
+			if (cpu_policy->max < cpu_policy->cpuinfo.max_freq) {
+				max_freq = cpu_policy->cpuinfo.max_freq;
+				update_policy = 1;
+			} else {
+				pr_debug("msm_thermal: policy max for cpu %d "
+					 "already at max allowed\n", cpu);
+			}
+		}
+
+		if (update_policy)
+			update_cpu_max_freq(cpu_policy, cpu, max_freq);
+
+		cpufreq_cpu_put(cpu_policy);
+	}
+
+reschedule:
+	if (enabled)
+		schedule_delayed_work(&check_temp_work,
+				msecs_to_jiffies(check_interval_ms));
+}
+
+static void disable_msm_thermal(void)
+{
+	int cpu = 0;
+	struct cpufreq_policy *cpu_policy = NULL;
+
+	/* make sure check_temp is no longer running */
+	cancel_delayed_work(&check_temp_work);
+	flush_scheduled_work();
+
+	for_each_possible_cpu(cpu) {
+		cpu_policy = cpufreq_cpu_get(cpu);
+		if (cpu_policy) {
+			if (cpu_policy->max < cpu_policy->cpuinfo.max_freq)
+				update_cpu_max_freq(cpu_policy, cpu,
+						    cpu_policy->
+						    cpuinfo.max_freq);
+			cpufreq_cpu_put(cpu_policy);
+		}
+	}
+}
+
+static int set_enabled(const char *val, const struct kernel_param *kp)
+{
+	int ret = 0;
+
+	ret = param_set_bool(val, kp);
+	if (!enabled)
+		disable_msm_thermal();
+	else
+		pr_info("msm_thermal: no action for enabled = %d\n", enabled);
+
+	pr_info("msm_thermal: enabled = %d\n", enabled);
+
+	return ret;
+}
+
+static struct kernel_param_ops module_ops = {
+	.set = set_enabled,
+	.get = param_get_bool,
+};
+
+module_param_cb(enabled, &module_ops, &enabled, 0644);
+MODULE_PARM_DESC(enabled, "enforce thermal limit on cpu");
+
+static int __init msm_thermal_init(void)
+{
+	int ret = 0;
+
+	enabled = 1;
+	INIT_DELAYED_WORK(&check_temp_work, check_temp);
+
+	schedule_delayed_work(&check_temp_work, 0);
+
+	return ret;
+}
+fs_initcall(msm_thermal_init);
+
diff --git a/drivers/thermal/msm_tsens.c b/drivers/thermal/msm_tsens.c
new file mode 100644
index 0000000..401ad88
--- /dev/null
+++ b/drivers/thermal/msm_tsens.c
@@ -0,0 +1,665 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+/*
+ * Qualcomm TSENS Thermal Manager driver
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/thermal.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include <linux/io.h>
+#include <mach/msm_iomap.h>
+#include <linux/pm.h>
+
+/* Trips: from very hot to very cold */
+enum tsens_trip_type {
+	TSENS_TRIP_STAGE3 = 0,
+	TSENS_TRIP_STAGE2,
+	TSENS_TRIP_STAGE1,
+	TSENS_TRIP_STAGE0,
+	TSENS_TRIP_NUM,
+};
+
+#define TSENS_NUM_SENSORS	1 /* There are 5 but only 1 is useful now */
+#define TSENS_CAL_DEGC		30 /* degree C used for calibration */
+#define TSENS_QFPROM_ADDR (MSM_QFPROM_BASE + 0x000000bc)
+#define TSENS_QFPROM_RED_TEMP_SENSOR0_SHIFT 24
+#define TSENS_QFPROM_TEMP_SENSOR0_SHIFT 16
+#define TSENS_QFPROM_TEMP_SENSOR0_MASK (255 << TSENS_QFPROM_TEMP_SENSOR0_SHIFT)
+#define TSENS_SLOPE (0.702)  /* slope in (degrees_C / ADC_code) */
+#define TSENS_FACTOR (1000)  /* convert floating-point into integer */
+#define TSENS_CONFIG 01      /* this setting found to be optimal */
+#define TSENS_CONFIG_SHIFT 28
+#define TSENS_CONFIG_MASK (3 << TSENS_CONFIG_SHIFT)
+#define TSENS_CNTL_ADDR (MSM_CLK_CTL_BASE + 0x00003620)
+#define TSENS_EN (1 << 0)
+#define TSENS_SW_RST (1 << 1)
+#define SENSOR0_EN (1 << 3)
+#define SENSOR1_EN (1 << 4)
+#define SENSOR2_EN (1 << 5)
+#define SENSOR3_EN (1 << 6)
+#define SENSOR4_EN (1 << 7)
+#define TSENS_MIN_STATUS_MASK (1 << 8)
+#define TSENS_LOWER_STATUS_CLR (1 << 9)
+#define TSENS_UPPER_STATUS_CLR (1 << 10)
+#define TSENS_MAX_STATUS_MASK (1 << 11)
+#define TSENS_MEASURE_PERIOD 4 /* 1 sec. default as required by Willie */
+#define TSENS_SLP_CLK_ENA (1 << 24)
+#define TSENS_THRESHOLD_ADDR (MSM_CLK_CTL_BASE + 0x00003624)
+#define TSENS_THRESHOLD_MAX_CODE (0xff)
+#define TSENS_THRESHOLD_MAX_LIMIT_MASK (TSENS_THRESHOLD_MAX_CODE << 24)
+#define TSENS_THRESHOLD_MIN_LIMIT_MASK (TSENS_THRESHOLD_MAX_CODE << 16)
+#define TSENS_THRESHOLD_UPPER_LIMIT_MASK (TSENS_THRESHOLD_MAX_CODE << 8)
+#define TSENS_THRESHOLD_LOWER_LIMIT_MASK (TSENS_THRESHOLD_MAX_CODE << 0)
+/* Initial temperature threshold values */
+#define TSENS_LOWER_LIMIT_TH   0x50
+#define TSENS_UPPER_LIMIT_TH   0xdf
+#define TSENS_MIN_LIMIT_TH     0x38
+#define TSENS_MAX_LIMIT_TH     0xff
+
+#define TSENS_S0_STATUS_ADDR (MSM_CLK_CTL_BASE + 0x00003628)
+#define TSENS_INT_STATUS_ADDR (MSM_CLK_CTL_BASE + 0x0000363c)
+#define TSENS_LOWER_INT_MASK (1 << 1)
+#define TSENS_UPPER_INT_MASK (1 << 2)
+#define TSENS_TRDY_MASK (1 << 7)
+
+struct tsens_tm_device_sensor {
+	struct thermal_zone_device	*tz_dev;
+	enum thermal_device_mode	mode;
+	unsigned int			sensor_num;
+};
+
+struct tsens_tm_device {
+	struct tsens_tm_device_sensor sensor[TSENS_NUM_SENSORS];
+	bool prev_reading_avail;
+	int offset;
+	struct work_struct work;
+	uint32_t pm_tsens_thr_data;
+};
+
+struct tsens_tm_device *tmdev;
+
+/* Temperature on y axis and ADC-code on x-axis */
+static int tsens_tz_code_to_degC(int adc_code)
+{
+	int degC, degcbeforefactor;
+	degcbeforefactor = adc_code * (int)(TSENS_SLOPE * TSENS_FACTOR)
+				+ tmdev->offset;
+	if (degcbeforefactor == 0)
+		degC = degcbeforefactor;
+	else if (degcbeforefactor > 0)
+		degC = (degcbeforefactor + TSENS_FACTOR/2) / TSENS_FACTOR;
+	else  /* rounding for negative degrees */
+		degC = (degcbeforefactor - TSENS_FACTOR/2) / TSENS_FACTOR;
+	return degC;
+}
+
+static int tsens_tz_degC_to_code(int degC)
+{
+	int code = (degC * TSENS_FACTOR - tmdev->offset
+			+ (int)(TSENS_FACTOR * TSENS_SLOPE)/2)
+			/ (int)(TSENS_FACTOR * TSENS_SLOPE);
+	if (code > 255) /* upper bound */
+		code = 255;
+	else if (code < 0) /* lower bound */
+		code = 0;
+	return code;
+}
+
+static int tsens_tz_get_temp(struct thermal_zone_device *thermal,
+			     unsigned long *temp)
+{
+	struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+	unsigned int code;
+
+	if (!tm_sensor || tm_sensor->mode != THERMAL_DEVICE_ENABLED || !temp)
+		return -EINVAL;
+
+	if (!tmdev->prev_reading_avail) {
+		while (!(readl(TSENS_INT_STATUS_ADDR) & TSENS_TRDY_MASK))
+			msleep(1);
+		tmdev->prev_reading_avail = 1;
+	}
+
+	code = readl(TSENS_S0_STATUS_ADDR + (tm_sensor->sensor_num << 2));
+	*temp = tsens_tz_code_to_degC(code);
+
+	return 0;
+}
+
+static int tsens_tz_get_mode(struct thermal_zone_device *thermal,
+			      enum thermal_device_mode *mode)
+{
+	struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+
+	if (!tm_sensor || !mode)
+		return -EINVAL;
+
+	*mode = tm_sensor->mode;
+
+	return 0;
+}
+
+static int tsens_tz_set_mode(struct thermal_zone_device *thermal,
+			      enum thermal_device_mode mode)
+{
+	struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+	unsigned int reg, mask;
+
+	if (!tm_sensor)
+		return -EINVAL;
+
+	if (mode != tm_sensor->mode) {
+		pr_info("%s: mode: %d --> %d\n", __func__, tm_sensor->mode,
+									 mode);
+
+		reg = readl(TSENS_CNTL_ADDR);
+		mask = 1 << (tm_sensor->sensor_num + 3);
+		if (mode == THERMAL_DEVICE_ENABLED) {
+			writel(reg | TSENS_SW_RST, TSENS_CNTL_ADDR);
+			reg |= mask | TSENS_SLP_CLK_ENA | TSENS_EN;
+			tmdev->prev_reading_avail = 0;
+		} else {
+			reg &= ~mask;
+			if (!(reg & (((1 << TSENS_NUM_SENSORS) - 1) << 3)))
+				reg &= ~(TSENS_SLP_CLK_ENA | TSENS_EN);
+		}
+
+		writel(reg, TSENS_CNTL_ADDR);
+	}
+	tm_sensor->mode = mode;
+
+	return 0;
+}
+
+static int tsens_tz_get_trip_type(struct thermal_zone_device *thermal,
+				   int trip, enum thermal_trip_type *type)
+{
+	struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+
+	if (!tm_sensor || trip < 0 || !type)
+		return -EINVAL;
+
+	switch (trip) {
+	case TSENS_TRIP_STAGE3:
+		*type = THERMAL_TRIP_CRITICAL;
+		break;
+	case TSENS_TRIP_STAGE2:
+		*type = THERMAL_TRIP_CONFIGURABLE_HI;
+		break;
+	case TSENS_TRIP_STAGE1:
+		*type = THERMAL_TRIP_CONFIGURABLE_LOW;
+		break;
+	case TSENS_TRIP_STAGE0:
+		*type = THERMAL_TRIP_CRITICAL_LOW;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int tsens_tz_activate_trip_type(struct thermal_zone_device *thermal,
+			int trip, enum thermal_trip_activation_mode mode)
+{
+	struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+	unsigned int reg_cntl, reg_th, code, hi_code, lo_code, mask;
+
+	if (!tm_sensor || trip < 0)
+		return -EINVAL;
+
+	lo_code = 0;
+	hi_code = TSENS_THRESHOLD_MAX_CODE;
+
+	reg_cntl = readl(TSENS_CNTL_ADDR);
+	reg_th = readl(TSENS_THRESHOLD_ADDR);
+	switch (trip) {
+	case TSENS_TRIP_STAGE3:
+		code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK) >> 24;
+		mask = TSENS_MAX_STATUS_MASK;
+
+		if (!(reg_cntl & TSENS_UPPER_STATUS_CLR))
+			lo_code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK)
+									>> 8;
+		else if (!(reg_cntl & TSENS_LOWER_STATUS_CLR))
+			lo_code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK);
+		else if (!(reg_cntl & TSENS_MIN_STATUS_MASK))
+			lo_code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK)
+									>> 16;
+		break;
+	case TSENS_TRIP_STAGE2:
+		code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK) >> 8;
+		mask = TSENS_UPPER_STATUS_CLR;
+
+		if (!(reg_cntl & TSENS_MAX_STATUS_MASK))
+			hi_code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK)
+									>> 24;
+		if (!(reg_cntl & TSENS_LOWER_STATUS_CLR))
+			lo_code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK);
+		else if (!(reg_cntl & TSENS_MIN_STATUS_MASK))
+			lo_code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK)
+									>> 16;
+		break;
+	case TSENS_TRIP_STAGE1:
+		code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK) >> 0;
+		mask = TSENS_LOWER_STATUS_CLR;
+
+		if (!(reg_cntl & TSENS_MIN_STATUS_MASK))
+			lo_code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK)
+									>> 16;
+		if (!(reg_cntl & TSENS_UPPER_STATUS_CLR))
+			hi_code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK)
+									>> 8;
+		else if (!(reg_cntl & TSENS_MAX_STATUS_MASK))
+			hi_code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK)
+									>> 24;
+		break;
+	case TSENS_TRIP_STAGE0:
+		code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK) >> 16;
+		mask = TSENS_MIN_STATUS_MASK;
+
+		if (!(reg_cntl & TSENS_LOWER_STATUS_CLR))
+			hi_code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK);
+		else if (!(reg_cntl & TSENS_UPPER_STATUS_CLR))
+			hi_code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK)
+									>> 8;
+		else if (!(reg_cntl & TSENS_MAX_STATUS_MASK))
+			hi_code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK)
+									>> 24;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (mode == THERMAL_TRIP_ACTIVATION_DISABLED)
+		writel(reg_cntl | mask, TSENS_CNTL_ADDR);
+	else {
+		if (code < lo_code || code > hi_code)
+			return -EINVAL;
+		writel(reg_cntl & ~mask, TSENS_CNTL_ADDR);
+	}
+
+	return 0;
+}
+
+static int tsens_tz_get_trip_temp(struct thermal_zone_device *thermal,
+				   int trip, unsigned long *temp)
+{
+	struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+	unsigned int reg;
+
+	if (!tm_sensor || trip < 0 || !temp)
+		return -EINVAL;
+
+	reg = readl(TSENS_THRESHOLD_ADDR);
+	switch (trip) {
+	case TSENS_TRIP_STAGE3:
+		reg = (reg & TSENS_THRESHOLD_MAX_LIMIT_MASK) >> 24;
+		break;
+	case TSENS_TRIP_STAGE2:
+		reg = (reg & TSENS_THRESHOLD_UPPER_LIMIT_MASK) >> 8;
+		break;
+	case TSENS_TRIP_STAGE1:
+		reg = (reg & TSENS_THRESHOLD_LOWER_LIMIT_MASK) >> 0;
+		break;
+	case TSENS_TRIP_STAGE0:
+		reg = (reg & TSENS_THRESHOLD_MIN_LIMIT_MASK) >> 16;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	*temp = tsens_tz_code_to_degC(reg);
+
+	return 0;
+}
+
+static int tsens_tz_get_crit_temp(struct thermal_zone_device *thermal,
+				  unsigned long *temp)
+{
+	return tsens_tz_get_trip_temp(thermal, TSENS_TRIP_STAGE3, temp);
+}
+
+static int tsens_tz_set_trip_temp(struct thermal_zone_device *thermal,
+				   int trip, long temp)
+{
+	struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+	unsigned int reg_th, reg_cntl;
+	int code, hi_code, lo_code, code_err_chk;
+
+	code_err_chk = code = tsens_tz_degC_to_code(temp);
+	if (!tm_sensor || trip < 0)
+		return -EINVAL;
+
+	lo_code = 0;
+	hi_code = TSENS_THRESHOLD_MAX_CODE;
+
+	reg_cntl = readl(TSENS_CNTL_ADDR);
+	reg_th = readl(TSENS_THRESHOLD_ADDR);
+	switch (trip) {
+	case TSENS_TRIP_STAGE3:
+		code <<= 24;
+		reg_th &= ~TSENS_THRESHOLD_MAX_LIMIT_MASK;
+
+		if (!(reg_cntl & TSENS_UPPER_STATUS_CLR))
+			lo_code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK)
+									>> 8;
+		else if (!(reg_cntl & TSENS_LOWER_STATUS_CLR))
+			lo_code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK);
+		else if (!(reg_cntl & TSENS_MIN_STATUS_MASK))
+			lo_code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK)
+									>> 16;
+		break;
+	case TSENS_TRIP_STAGE2:
+		code <<= 8;
+		reg_th &= ~TSENS_THRESHOLD_UPPER_LIMIT_MASK;
+
+		if (!(reg_cntl & TSENS_MAX_STATUS_MASK))
+			hi_code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK)
+									>> 24;
+		if (!(reg_cntl & TSENS_LOWER_STATUS_CLR))
+			lo_code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK);
+		else if (!(reg_cntl & TSENS_MIN_STATUS_MASK))
+			lo_code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK)
+									>> 16;
+		break;
+	case TSENS_TRIP_STAGE1:
+		reg_th &= ~TSENS_THRESHOLD_LOWER_LIMIT_MASK;
+
+		if (!(reg_cntl & TSENS_MIN_STATUS_MASK))
+			lo_code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK)
+									>> 16;
+		if (!(reg_cntl & TSENS_UPPER_STATUS_CLR))
+			hi_code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK)
+									>> 8;
+		else if (!(reg_cntl & TSENS_MAX_STATUS_MASK))
+			hi_code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK)
+									>> 24;
+		break;
+	case TSENS_TRIP_STAGE0:
+		code <<= 16;
+		reg_th &= ~TSENS_THRESHOLD_MIN_LIMIT_MASK;
+
+		if (!(reg_cntl & TSENS_LOWER_STATUS_CLR))
+			hi_code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK);
+		else if (!(reg_cntl & TSENS_UPPER_STATUS_CLR))
+			hi_code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK)
+									>> 8;
+		else if (!(reg_cntl & TSENS_MAX_STATUS_MASK))
+			hi_code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK)
+									>> 24;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (code_err_chk < lo_code || code_err_chk > hi_code)
+		return -EINVAL;
+
+	writel(reg_th | code, TSENS_THRESHOLD_ADDR);
+	return 0;
+}
+
+static struct thermal_zone_device_ops tsens_thermal_zone_ops = {
+	.get_temp = tsens_tz_get_temp,
+	.get_mode = tsens_tz_get_mode,
+	.set_mode = tsens_tz_set_mode,
+	.get_trip_type = tsens_tz_get_trip_type,
+	.activate_trip_type = tsens_tz_activate_trip_type,
+	.get_trip_temp = tsens_tz_get_trip_temp,
+	.set_trip_temp = tsens_tz_set_trip_temp,
+	.get_crit_temp = tsens_tz_get_crit_temp,
+};
+
+static void notify_uspace_tsens_fn(struct work_struct *work)
+{
+	struct tsens_tm_device *tm = container_of(work, struct tsens_tm_device,
+					work);
+	/* Currently only Sensor0 is supported. We added support
+	   to notify only the supported Sensor and this portion
+	   needs to be revisited once other sensors are supported */
+	sysfs_notify(&tm->sensor[0].tz_dev->device.kobj,
+					NULL, "type");
+}
+
+static irqreturn_t tsens_isr(int irq, void *data)
+{
+	unsigned int reg = readl(TSENS_CNTL_ADDR);
+
+	writel(reg | TSENS_LOWER_STATUS_CLR | TSENS_UPPER_STATUS_CLR,
+			TSENS_CNTL_ADDR);
+
+	return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t tsens_isr_thread(int irq, void *data)
+{
+	struct tsens_tm_device *tm = data;
+	unsigned int threshold, threshold_low, i, code, reg, sensor, mask;
+	bool upper_th_x, lower_th_x;
+	int adc_code;
+
+	mask = ~(TSENS_LOWER_STATUS_CLR | TSENS_UPPER_STATUS_CLR);
+	threshold = readl(TSENS_THRESHOLD_ADDR);
+	threshold_low = threshold & TSENS_THRESHOLD_LOWER_LIMIT_MASK;
+	threshold = (threshold & TSENS_THRESHOLD_UPPER_LIMIT_MASK) >> 8;
+	reg = sensor = readl(TSENS_CNTL_ADDR);
+	sensor &= (SENSOR0_EN | SENSOR1_EN | SENSOR2_EN |
+						SENSOR3_EN | SENSOR4_EN);
+	sensor >>= 3;
+	for (i = 0; i < TSENS_NUM_SENSORS; i++) {
+		if (sensor & 1) {
+			code = readl(TSENS_S0_STATUS_ADDR + (i << 2));
+			upper_th_x = code >= threshold;
+			lower_th_x = code <= threshold_low;
+			if (upper_th_x)
+				mask |= TSENS_UPPER_STATUS_CLR;
+			if (lower_th_x)
+				mask |= TSENS_LOWER_STATUS_CLR;
+			if (upper_th_x || lower_th_x) {
+				/* Notify user space */
+				schedule_work(&tm->work);
+				adc_code = readl(TSENS_S0_STATUS_ADDR
+							+ (i << 2));
+				printk(KERN_INFO"\nTrip point triggered by "
+					"current temperature (%d degrees) "
+					"measured by Temperature-Sensor %d\n",
+					tsens_tz_code_to_degC(adc_code), i);
+			}
+		}
+		sensor >>= 1;
+	}
+	writel(reg & mask, TSENS_CNTL_ADDR);
+	return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_PM
+static int tsens_suspend(struct device *dev)
+{
+	unsigned int reg;
+
+	tmdev->pm_tsens_thr_data = readl_relaxed(TSENS_THRESHOLD_ADDR);
+	reg = readl_relaxed(TSENS_CNTL_ADDR);
+	writel_relaxed(reg & ~(TSENS_SLP_CLK_ENA | TSENS_EN), TSENS_CNTL_ADDR);
+	tmdev->prev_reading_avail = 0;
+
+	disable_irq_nosync(TSENS_UPPER_LOWER_INT);
+	mb();
+	return 0;
+}
+
+static int tsens_resume(struct device *dev)
+{
+	unsigned int reg;
+
+	reg = readl_relaxed(TSENS_CNTL_ADDR);
+	writel_relaxed(reg | TSENS_SW_RST, TSENS_CNTL_ADDR);
+	reg |= TSENS_SLP_CLK_ENA | TSENS_EN | (TSENS_MEASURE_PERIOD << 16) |
+		TSENS_MIN_STATUS_MASK | TSENS_MAX_STATUS_MASK |
+		(((1 << TSENS_NUM_SENSORS) - 1) << 3);
+
+	reg = (reg & ~TSENS_CONFIG_MASK) | (TSENS_CONFIG << TSENS_CONFIG_SHIFT);
+	writel_relaxed(reg, TSENS_CNTL_ADDR);
+
+	if (tmdev->sensor->mode == THERMAL_DEVICE_DISABLED) {
+		writel_relaxed(reg & ~((((1 << TSENS_NUM_SENSORS) - 1) << 3)
+			| TSENS_SLP_CLK_ENA | TSENS_EN), TSENS_CNTL_ADDR);
+	}
+
+	writel_relaxed(tmdev->pm_tsens_thr_data, TSENS_THRESHOLD_ADDR);
+
+	enable_irq(TSENS_UPPER_LOWER_INT);
+	mb();
+	return 0;
+}
+
+static const struct dev_pm_ops tsens_pm_ops = {
+	.suspend	= tsens_suspend,
+	.resume		= tsens_resume,
+};
+#endif
+
+static int __devinit tsens_tm_probe(struct platform_device *pdev)
+{
+	unsigned int reg, i, calib_data, calib_data_backup;
+	int rc;
+
+	calib_data = (readl(TSENS_QFPROM_ADDR) & TSENS_QFPROM_TEMP_SENSOR0_MASK)
+					>> TSENS_QFPROM_TEMP_SENSOR0_SHIFT;
+	calib_data_backup = readl(TSENS_QFPROM_ADDR)
+					>> TSENS_QFPROM_RED_TEMP_SENSOR0_SHIFT;
+
+	if (calib_data_backup)
+		calib_data = calib_data_backup;
+
+	if (!calib_data) {
+		pr_err("%s: No temperature sensor data for calibration"
+						" in QFPROM!\n", __func__);
+		return -ENODEV;
+	}
+
+	tmdev = kzalloc(sizeof(struct tsens_tm_device), GFP_KERNEL);
+	if (tmdev == NULL) {
+		pr_err("%s: kzalloc() failed.\n", __func__);
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(pdev, tmdev);
+
+	tmdev->offset = TSENS_FACTOR * TSENS_CAL_DEGC
+			- (int)(TSENS_FACTOR * TSENS_SLOPE) * calib_data;
+	tmdev->prev_reading_avail = 0;
+
+	INIT_WORK(&tmdev->work, notify_uspace_tsens_fn);
+
+	reg = readl(TSENS_CNTL_ADDR);
+	writel(reg | TSENS_SW_RST, TSENS_CNTL_ADDR);
+	reg |= TSENS_SLP_CLK_ENA | TSENS_EN | (TSENS_MEASURE_PERIOD << 16) |
+		TSENS_LOWER_STATUS_CLR | TSENS_UPPER_STATUS_CLR |
+		TSENS_MIN_STATUS_MASK | TSENS_MAX_STATUS_MASK |
+		(((1 << TSENS_NUM_SENSORS) - 1) << 3);
+
+	/* set TSENS_CONFIG bits (bits 29:28 of TSENS_CNTL) to '01';
+		this setting found to be optimal. */
+	reg = (reg & ~TSENS_CONFIG_MASK) | (TSENS_CONFIG << TSENS_CONFIG_SHIFT);
+
+	writel(reg, TSENS_CNTL_ADDR);
+
+	writel((TSENS_LOWER_LIMIT_TH << 0) | (TSENS_UPPER_LIMIT_TH << 8) |
+		(TSENS_MIN_LIMIT_TH << 16) | (TSENS_MAX_LIMIT_TH << 24),
+			TSENS_THRESHOLD_ADDR);
+
+	for (i = 0; i < TSENS_NUM_SENSORS; i++) {
+		char name[17];
+		sprintf(name, "tsens_tz_sensor%d", i);
+
+		tmdev->sensor[i].mode = THERMAL_DEVICE_ENABLED;
+		tmdev->sensor[i].tz_dev = thermal_zone_device_register(name,
+				TSENS_TRIP_NUM, &tmdev->sensor[i],
+				&tsens_thermal_zone_ops, 0, 0, 0, 0);
+		if (tmdev->sensor[i].tz_dev == NULL) {
+			pr_err("%s: thermal_zone_device_register() failed.\n",
+			__func__);
+			kfree(tmdev);
+			return -ENODEV;
+		}
+		tmdev->sensor[i].sensor_num = i;
+		tmdev->sensor[i].mode = THERMAL_DEVICE_DISABLED;
+	}
+
+	rc = request_threaded_irq(TSENS_UPPER_LOWER_INT, tsens_isr,
+		tsens_isr_thread, 0, "tsens", tmdev);
+	if (rc < 0) {
+		pr_err("%s: request_irq FAIL: %d\n", __func__, rc);
+		kfree(tmdev);
+		return rc;
+	}
+
+	writel(reg & ~((((1 << TSENS_NUM_SENSORS) - 1) << 3)
+			| TSENS_SLP_CLK_ENA | TSENS_EN), TSENS_CNTL_ADDR);
+	pr_notice("%s: OK\n", __func__);
+	return 0;
+}
+
+static int __devexit tsens_tm_remove(struct platform_device *pdev)
+{
+	struct tsens_tm_device *tmdev = platform_get_drvdata(pdev);
+	unsigned int reg, i;
+
+	reg = readl(TSENS_CNTL_ADDR);
+	writel(reg & ~(TSENS_SLP_CLK_ENA | TSENS_EN), TSENS_CNTL_ADDR);
+
+	for (i = 0; i < TSENS_NUM_SENSORS; i++)
+		thermal_zone_device_unregister(tmdev->sensor[i].tz_dev);
+	platform_set_drvdata(pdev, NULL);
+	free_irq(TSENS_UPPER_LOWER_INT, tmdev);
+	kfree(tmdev);
+
+	return 0;
+}
+
+static struct platform_driver tsens_tm_driver = {
+	.probe	= tsens_tm_probe,
+	.remove	= __devexit_p(tsens_tm_remove),
+	.driver	= {
+		.name = "tsens-tm",
+		.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm	= &tsens_pm_ops,
+#endif
+	},
+};
+
+static int __init tsens_init(void)
+{
+	return platform_driver_register(&tsens_tm_driver);
+}
+
+static void __exit tsens_exit(void)
+{
+	platform_driver_unregister(&tsens_tm_driver);
+}
+
+module_init(tsens_init);
+module_exit(tsens_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MSM Temperature Sensor driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:tsens-tm");
diff --git a/drivers/thermal/pm8xxx-tm.c b/drivers/thermal/pm8xxx-tm.c
new file mode 100644
index 0000000..50238f3
--- /dev/null
+++ b/drivers/thermal/pm8xxx-tm.c
@@ -0,0 +1,725 @@
+/*
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Qualcomm PMIC PM8xxx Thermal Manager driver
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/thermal.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/tm.h>
+#include <linux/completion.h>
+#include <linux/mfd/pm8xxx/pm8xxx-adc.h>
+#include <linux/msm_adc.h>
+
+/* Register TEMP_ALARM_CTRL bits */
+#define	TEMP_ALARM_CTRL_ST3_SD		0x80
+#define	TEMP_ALARM_CTRL_ST2_SD		0x40
+#define	TEMP_ALARM_CTRL_STATUS_MASK	0x30
+#define	TEMP_ALARM_CTRL_STATUS_SHIFT	4
+#define	TEMP_ALARM_CTRL_THRESH_MASK	0x0C
+#define	TEMP_ALARM_CTRL_THRESH_SHIFT	2
+#define	TEMP_ALARM_CTRL_OVRD_ST3	0x02
+#define	TEMP_ALARM_CTRL_OVRD_ST2	0x01
+#define	TEMP_ALARM_CTRL_OVRD_MASK	0x03
+
+#define	TEMP_STAGE_STEP			20000	/* Stage step: 20.000 C */
+#define	TEMP_STAGE_HYSTERESIS		2000
+
+#define	TEMP_THRESH_MIN			105000	/* Threshold Min: 105 C */
+#define	TEMP_THRESH_STEP		5000	/* Threshold step: 5 C */
+
+/* Register TEMP_ALARM_PWM bits */
+#define	TEMP_ALARM_PWM_EN_MASK		0xC0
+#define	TEMP_ALARM_PWM_EN_SHIFT		6
+#define	TEMP_ALARM_PWM_PER_PRE_MASK	0x38
+#define	TEMP_ALARM_PWM_PER_PRE_SHIFT	3
+#define	TEMP_ALARM_PWM_PER_DIV_MASK	0x07
+#define	TEMP_ALARM_PWM_PER_DIV_SHIFT	0
+
+/* Trips: from critical to less critical */
+#define TRIP_STAGE3			0
+#define TRIP_STAGE2			1
+#define TRIP_STAGE1			2
+#define TRIP_NUM			3
+
+struct pm8xxx_tm_chip {
+	struct pm8xxx_tm_core_data	cdata;
+	struct work_struct		irq_work;
+	struct device			*dev;
+	struct thermal_zone_device	*tz_dev;
+	unsigned long			temp;
+	enum thermal_device_mode	mode;
+	unsigned int			thresh;
+	unsigned int			stage;
+	unsigned int			tempstat_irq;
+	unsigned int			overtemp_irq;
+	void				*adc_handle;
+};
+
+enum pmic_thermal_override_mode {
+	SOFTWARE_OVERRIDE_DISABLED = 0,
+	SOFTWARE_OVERRIDE_ENABLED,
+};
+
+static inline int pm8xxx_tm_read_ctrl(struct pm8xxx_tm_chip *chip, u8 *reg)
+{
+	int rc;
+
+	rc = pm8xxx_readb(chip->dev->parent,
+			  chip->cdata.reg_addr_temp_alarm_ctrl, reg);
+	if (rc)
+		pr_err("%s: pm8xxx_readb(0x%03X) failed, rc=%d\n",
+			chip->cdata.tm_name,
+			chip->cdata.reg_addr_temp_alarm_ctrl, rc);
+
+	return rc;
+}
+
+static inline int pm8xxx_tm_write_ctrl(struct pm8xxx_tm_chip *chip, u8 reg)
+{
+	int rc;
+
+	rc = pm8xxx_writeb(chip->dev->parent,
+			   chip->cdata.reg_addr_temp_alarm_ctrl, reg);
+	if (rc)
+		pr_err("%s: pm8xxx_writeb(0x%03X)=0x%02X failed, rc=%d\n",
+		       chip->cdata.tm_name,
+		       chip->cdata.reg_addr_temp_alarm_ctrl, reg, rc);
+
+	return rc;
+}
+
+static inline int pm8xxx_tm_write_pwm(struct pm8xxx_tm_chip *chip, u8 reg)
+{
+	int rc;
+
+	rc = pm8xxx_writeb(chip->dev->parent,
+			   chip->cdata.reg_addr_temp_alarm_pwm, reg);
+	if (rc)
+		pr_err("%s: pm8xxx_writeb(0x%03X)=0x%02X failed, rc=%d\n",
+			chip->cdata.tm_name,
+			chip->cdata.reg_addr_temp_alarm_pwm, reg, rc);
+
+	return rc;
+}
+
+static inline int
+pm8xxx_tm_shutdown_override(struct pm8xxx_tm_chip *chip,
+			    enum pmic_thermal_override_mode mode)
+{
+	int rc;
+	u8 reg;
+
+	rc = pm8xxx_tm_read_ctrl(chip, &reg);
+	if (rc < 0)
+		return rc;
+
+	reg &= ~(TEMP_ALARM_CTRL_OVRD_MASK | TEMP_ALARM_CTRL_STATUS_MASK);
+	if (mode == SOFTWARE_OVERRIDE_ENABLED)
+		reg |= (TEMP_ALARM_CTRL_OVRD_ST3 | TEMP_ALARM_CTRL_OVRD_ST2) &
+			TEMP_ALARM_CTRL_OVRD_MASK;
+
+	rc = pm8xxx_tm_write_ctrl(chip, reg);
+
+	return rc;
+}
+
+/*
+ * This function initializes the internal temperature value based on only the
+ * current thermal stage and threshold.
+ */
+static int pm8xxx_tm_init_temp_no_adc(struct pm8xxx_tm_chip *chip)
+{
+	int rc;
+	u8 reg;
+
+	rc = pm8xxx_tm_read_ctrl(chip, &reg);
+	if (rc < 0)
+		return rc;
+
+	chip->stage = (reg & TEMP_ALARM_CTRL_STATUS_MASK)
+			>> TEMP_ALARM_CTRL_STATUS_SHIFT;
+	chip->thresh = (reg & TEMP_ALARM_CTRL_THRESH_MASK)
+			>> TEMP_ALARM_CTRL_THRESH_SHIFT;
+
+	if (chip->stage)
+		chip->temp = chip->thresh * TEMP_THRESH_MIN +
+			   (chip->stage - 1) * TEMP_STAGE_STEP +
+			   TEMP_THRESH_MIN;
+	else
+		chip->temp = chip->cdata.default_no_adc_temp;
+
+	return 0;
+}
+
+/*
+ * This function updates the internal temperature value based on the
+ * current thermal stage and threshold as well as the previous stage
+ */
+static int pm8xxx_tm_update_temp_no_adc(struct pm8xxx_tm_chip *chip)
+{
+	unsigned int stage;
+	int rc;
+	u8 reg;
+
+	rc = pm8xxx_tm_read_ctrl(chip, &reg);
+	if (rc < 0)
+		return rc;
+
+	stage = (reg & TEMP_ALARM_CTRL_STATUS_MASK)
+		>> TEMP_ALARM_CTRL_STATUS_SHIFT;
+	chip->thresh = (reg & TEMP_ALARM_CTRL_THRESH_MASK)
+			>> TEMP_ALARM_CTRL_THRESH_SHIFT;
+
+	if (stage > chip->stage) {
+		/* increasing stage, use lower bound */
+		chip->temp = (stage - 1) * TEMP_STAGE_STEP
+				+ chip->thresh * TEMP_THRESH_STEP
+				+ TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN;
+	} else if (stage < chip->stage) {
+		/* decreasing stage, use upper bound */
+		chip->temp = stage * TEMP_STAGE_STEP
+				+ chip->thresh * TEMP_THRESH_STEP
+				- TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN;
+	}
+
+	chip->stage = stage;
+
+	return 0;
+}
+
+static int pm8xxx_tz_get_temp_no_adc(struct thermal_zone_device *thermal,
+				     unsigned long *temp)
+{
+	struct pm8xxx_tm_chip *chip = thermal->devdata;
+	int rc;
+
+	if (!chip || !temp)
+		return -EINVAL;
+
+	rc = pm8xxx_tm_update_temp_no_adc(chip);
+	if (rc < 0)
+		return rc;
+
+	*temp = chip->temp;
+
+	return 0;
+}
+
+static int pm8xxx_tz_get_temp_pm8058_adc(struct thermal_zone_device *thermal,
+			      unsigned long *temp)
+{
+	struct pm8xxx_tm_chip *chip = thermal->devdata;
+	DECLARE_COMPLETION_ONSTACK(wait);
+	struct adc_chan_result adc_result = {
+		.physical = 0lu,
+	};
+	int rc;
+
+	if (!chip || !temp)
+		return -EINVAL;
+
+	*temp = chip->temp;
+
+	rc = adc_channel_request_conv(chip->adc_handle, &wait);
+	if (rc < 0) {
+		pr_err("%s: adc_channel_request_conv() failed, rc = %d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	wait_for_completion(&wait);
+
+	rc = adc_channel_read_result(chip->adc_handle, &adc_result);
+	if (rc < 0) {
+		pr_err("%s: adc_channel_read_result() failed, rc = %d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	*temp = adc_result.physical;
+	chip->temp = adc_result.physical;
+
+	return 0;
+}
+
+static int pm8xxx_tz_get_temp_pm8xxx_adc(struct thermal_zone_device *thermal,
+				      unsigned long *temp)
+{
+	struct pm8xxx_tm_chip *chip = thermal->devdata;
+	struct pm8xxx_adc_chan_result result = {
+		.physical = 0lu,
+	};
+	int rc;
+
+	if (!chip || !temp)
+		return -EINVAL;
+
+	*temp = chip->temp;
+
+	rc = pm8xxx_adc_read(chip->cdata.adc_channel, &result);
+	if (rc < 0) {
+		pr_err("%s: adc_channel_read_result() failed, rc = %d\n",
+			chip->cdata.tm_name, rc);
+		return rc;
+	}
+
+	*temp = result.physical;
+	chip->temp = result.physical;
+
+	return 0;
+}
+
+static int pm8xxx_tz_get_mode(struct thermal_zone_device *thermal,
+			      enum thermal_device_mode *mode)
+{
+	struct pm8xxx_tm_chip *chip = thermal->devdata;
+
+	if (!chip || !mode)
+		return -EINVAL;
+
+	*mode = chip->mode;
+
+	return 0;
+}
+
+static int pm8xxx_tz_set_mode(struct thermal_zone_device *thermal,
+			      enum thermal_device_mode mode)
+{
+	struct pm8xxx_tm_chip *chip = thermal->devdata;
+
+	if (!chip)
+		return -EINVAL;
+
+	if (mode != chip->mode) {
+		if (mode == THERMAL_DEVICE_ENABLED)
+			pm8xxx_tm_shutdown_override(chip,
+						    SOFTWARE_OVERRIDE_ENABLED);
+		else
+			pm8xxx_tm_shutdown_override(chip,
+						    SOFTWARE_OVERRIDE_DISABLED);
+	}
+	chip->mode = mode;
+
+	return 0;
+}
+
+static int pm8xxx_tz_get_trip_type(struct thermal_zone_device *thermal,
+				   int trip, enum thermal_trip_type *type)
+{
+	if (trip < 0 || !type)
+		return -EINVAL;
+
+	switch (trip) {
+	case TRIP_STAGE3:
+		*type = THERMAL_TRIP_CRITICAL;
+		break;
+	case TRIP_STAGE2:
+		*type = THERMAL_TRIP_HOT;
+		break;
+	case TRIP_STAGE1:
+		*type = THERMAL_TRIP_HOT;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int pm8xxx_tz_get_trip_temp(struct thermal_zone_device *thermal,
+				   int trip, unsigned long *temp)
+{
+	struct pm8xxx_tm_chip *chip = thermal->devdata;
+	int thresh_temp;
+
+	if (!chip || trip < 0 || !temp)
+		return -EINVAL;
+
+	thresh_temp = chip->thresh * TEMP_THRESH_STEP +
+			TEMP_THRESH_MIN;
+
+	switch (trip) {
+	case TRIP_STAGE3:
+		thresh_temp += 2 * TEMP_STAGE_STEP;
+		break;
+	case TRIP_STAGE2:
+		thresh_temp += TEMP_STAGE_STEP;
+		break;
+	case TRIP_STAGE1:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	*temp = thresh_temp;
+
+	return 0;
+}
+
+static int pm8xxx_tz_get_crit_temp(struct thermal_zone_device *thermal,
+				   unsigned long *temp)
+{
+	struct pm8xxx_tm_chip *chip = thermal->devdata;
+
+	if (!chip || !temp)
+		return -EINVAL;
+
+	*temp = chip->thresh * TEMP_THRESH_STEP + TEMP_THRESH_MIN +
+		2 * TEMP_STAGE_STEP;
+
+	return 0;
+}
+
+static struct thermal_zone_device_ops pm8xxx_thermal_zone_ops_no_adc = {
+	.get_temp = pm8xxx_tz_get_temp_no_adc,
+	.get_mode = pm8xxx_tz_get_mode,
+	.set_mode = pm8xxx_tz_set_mode,
+	.get_trip_type = pm8xxx_tz_get_trip_type,
+	.get_trip_temp = pm8xxx_tz_get_trip_temp,
+	.get_crit_temp = pm8xxx_tz_get_crit_temp,
+};
+
+static struct thermal_zone_device_ops pm8xxx_thermal_zone_ops_pm8xxx_adc = {
+	.get_temp = pm8xxx_tz_get_temp_pm8xxx_adc,
+	.get_mode = pm8xxx_tz_get_mode,
+	.set_mode = pm8xxx_tz_set_mode,
+	.get_trip_type = pm8xxx_tz_get_trip_type,
+	.get_trip_temp = pm8xxx_tz_get_trip_temp,
+	.get_crit_temp = pm8xxx_tz_get_crit_temp,
+};
+
+static struct thermal_zone_device_ops pm8xxx_thermal_zone_ops_pm8058_adc = {
+	.get_temp = pm8xxx_tz_get_temp_pm8058_adc,
+	.get_mode = pm8xxx_tz_get_mode,
+	.set_mode = pm8xxx_tz_set_mode,
+	.get_trip_type = pm8xxx_tz_get_trip_type,
+	.get_trip_temp = pm8xxx_tz_get_trip_temp,
+	.get_crit_temp = pm8xxx_tz_get_crit_temp,
+};
+
+static void pm8xxx_tm_work(struct work_struct *work)
+{
+	struct pm8xxx_tm_chip *chip
+		= container_of(work, struct pm8xxx_tm_chip, irq_work);
+	int rc;
+	u8 reg;
+
+	rc = pm8xxx_tm_read_ctrl(chip, &reg);
+	if (rc < 0)
+		goto bail;
+
+	if (chip->cdata.adc_type == PM8XXX_TM_ADC_NONE) {
+		rc = pm8xxx_tm_update_temp_no_adc(chip);
+		if (rc < 0)
+			goto bail;
+		pr_info("%s: Temp Alarm - stage=%u, threshold=%u, "
+			"temp=%lu mC\n", chip->cdata.tm_name, chip->stage,
+			chip->thresh, chip->temp);
+	} else {
+		chip->stage = (reg & TEMP_ALARM_CTRL_STATUS_MASK)
+				>> TEMP_ALARM_CTRL_STATUS_SHIFT;
+		chip->thresh = (reg & TEMP_ALARM_CTRL_THRESH_MASK)
+				>> TEMP_ALARM_CTRL_THRESH_SHIFT;
+		pr_info("%s: Temp Alarm - stage=%u, threshold=%u\n",
+			chip->cdata.tm_name, chip->stage, chip->thresh);
+	}
+
+	/* Clear status bits. */
+	if (reg & (TEMP_ALARM_CTRL_ST2_SD | TEMP_ALARM_CTRL_ST3_SD)) {
+		reg &= ~(TEMP_ALARM_CTRL_ST2_SD | TEMP_ALARM_CTRL_ST3_SD
+			 | TEMP_ALARM_CTRL_STATUS_MASK);
+
+		pm8xxx_tm_write_ctrl(chip, reg);
+	}
+
+	thermal_zone_device_update(chip->tz_dev);
+
+	/* Notify user space */
+	if (chip->mode == THERMAL_DEVICE_ENABLED)
+		kobject_uevent(&chip->tz_dev->device.kobj, KOBJ_CHANGE);
+
+bail:
+	enable_irq(chip->tempstat_irq);
+	enable_irq(chip->overtemp_irq);
+}
+
+static irqreturn_t pm8xxx_tm_isr(int irq, void *data)
+{
+	struct pm8xxx_tm_chip *chip = data;
+
+	disable_irq_nosync(chip->tempstat_irq);
+	disable_irq_nosync(chip->overtemp_irq);
+	schedule_work(&chip->irq_work);
+
+	return IRQ_HANDLED;
+}
+
+static int pm8xxx_tm_init_reg(struct pm8xxx_tm_chip *chip)
+{
+	int rc;
+	u8 reg;
+
+	rc = pm8xxx_tm_read_ctrl(chip, &reg);
+	if (rc < 0)
+		return rc;
+
+	chip->stage = (reg & TEMP_ALARM_CTRL_STATUS_MASK)
+			>> TEMP_ALARM_CTRL_STATUS_SHIFT;
+	chip->temp = 0;
+
+	/* Use temperature threshold set 0: (105, 125, 145) */
+	chip->thresh = 0;
+	reg = (chip->thresh << TEMP_ALARM_CTRL_THRESH_SHIFT)
+		& TEMP_ALARM_CTRL_THRESH_MASK;
+	rc = pm8xxx_tm_write_ctrl(chip, reg);
+	if (rc < 0)
+		return rc;
+
+	/*
+	 * Set the PMIC alarm module PWM to have a frequency of 8 Hz. This
+	 * helps cut down on the number of unnecessary interrupts fired when
+	 * changing between thermal stages.  Also, Enable the over temperature
+	 * PWM whenever the PMIC is enabled.
+	 */
+	reg =  (1 << TEMP_ALARM_PWM_EN_SHIFT)
+		| (3 << TEMP_ALARM_PWM_PER_PRE_SHIFT)
+		| (3 << TEMP_ALARM_PWM_PER_DIV_SHIFT);
+
+	rc = pm8xxx_tm_write_pwm(chip, reg);
+
+	return rc;
+}
+
+static int pm8xxx_init_adc(struct pm8xxx_tm_chip *chip, bool enable)
+{
+	int rc = 0;
+
+	if (chip->cdata.adc_type == PM8XXX_TM_ADC_PM8058_ADC) {
+		if (enable) {
+			rc = adc_channel_open(chip->cdata.adc_channel,
+						&(chip->adc_handle));
+			if (rc < 0)
+				pr_err("adc_channel_open() failed.\n");
+		} else {
+			adc_channel_close(chip->adc_handle);
+		}
+	}
+
+	return rc;
+}
+
+static int __devinit pm8xxx_tm_probe(struct platform_device *pdev)
+{
+	const struct pm8xxx_tm_core_data *cdata = pdev->dev.platform_data;
+	struct thermal_zone_device_ops *tz_ops;
+	struct pm8xxx_tm_chip *chip;
+	struct resource *res;
+	int rc = 0;
+
+	if (!cdata) {
+		pr_err("missing core data\n");
+		return -EINVAL;
+	}
+
+	chip = kzalloc(sizeof(struct pm8xxx_tm_chip), GFP_KERNEL);
+	if (chip == NULL) {
+		pr_err("kzalloc() failed.\n");
+		return -ENOMEM;
+	}
+
+	chip->dev = &pdev->dev;
+	memcpy(&(chip->cdata), cdata, sizeof(struct pm8xxx_tm_core_data));
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+		chip->cdata.irq_name_temp_stat);
+	if (res) {
+		chip->tempstat_irq = res->start;
+	} else {
+		pr_err("temp stat IRQ not specified\n");
+		goto err_free_chip;
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+		chip->cdata.irq_name_over_temp);
+	if (res) {
+		chip->overtemp_irq = res->start;
+	} else {
+		pr_err("over temp IRQ not specified\n");
+		goto err_free_chip;
+	}
+
+	rc = pm8xxx_init_adc(chip, true);
+	if (rc < 0) {
+		pr_err("Unable to initialize adc\n");
+		goto err_free_chip;
+	}
+
+	/* Select proper thermal zone ops functions based on ADC type. */
+	if (chip->cdata.adc_type == PM8XXX_TM_ADC_PM8XXX_ADC)
+		tz_ops = &pm8xxx_thermal_zone_ops_pm8xxx_adc;
+	else if (chip->cdata.adc_type == PM8XXX_TM_ADC_PM8058_ADC)
+		tz_ops = &pm8xxx_thermal_zone_ops_pm8058_adc;
+	else
+		tz_ops = &pm8xxx_thermal_zone_ops_no_adc;
+
+	chip->tz_dev = thermal_zone_device_register(chip->cdata.tm_name,
+			TRIP_NUM, chip, tz_ops, 0, 0, 0, 0);
+
+	if (chip->tz_dev == NULL) {
+		pr_err("thermal_zone_device_register() failed.\n");
+		rc = -ENODEV;
+		goto err_fail_adc;
+	}
+
+	rc = pm8xxx_tm_init_reg(chip);
+	if (rc < 0)
+		goto err_free_tz;
+	rc = pm8xxx_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_DISABLED);
+	if (rc < 0)
+		goto err_free_tz;
+
+	if (chip->cdata.adc_type == PM8XXX_TM_ADC_NONE) {
+		rc = pm8xxx_tm_init_temp_no_adc(chip);
+		if (rc < 0)
+			goto err_free_tz;
+	}
+
+	/* Start in HW control; switch to SW control when user changes mode. */
+	chip->mode = THERMAL_DEVICE_DISABLED;
+	thermal_zone_device_update(chip->tz_dev);
+
+	INIT_WORK(&chip->irq_work, pm8xxx_tm_work);
+
+	rc = request_irq(chip->tempstat_irq, pm8xxx_tm_isr, IRQF_TRIGGER_RISING,
+		chip->cdata.irq_name_temp_stat, chip);
+	if (rc < 0) {
+		pr_err("request_irq(%d) failed: %d\n", chip->tempstat_irq, rc);
+		goto err_cancel_work;
+	}
+
+	rc = request_irq(chip->overtemp_irq, pm8xxx_tm_isr, IRQF_TRIGGER_RISING,
+		chip->cdata.irq_name_over_temp, chip);
+	if (rc < 0) {
+		pr_err("request_irq(%d) failed: %d\n", chip->overtemp_irq, rc);
+		goto err_free_irq_tempstat;
+	}
+
+	platform_set_drvdata(pdev, chip);
+
+	pr_info("OK\n");
+
+	return 0;
+
+err_free_irq_tempstat:
+	free_irq(chip->tempstat_irq, chip);
+err_cancel_work:
+	cancel_work_sync(&chip->irq_work);
+err_free_tz:
+	thermal_zone_device_unregister(chip->tz_dev);
+err_fail_adc:
+	pm8xxx_init_adc(chip, false);
+err_free_chip:
+	kfree(chip);
+	return rc;
+}
+
+static int __devexit pm8xxx_tm_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_tm_chip *chip = platform_get_drvdata(pdev);
+
+	if (chip) {
+		platform_set_drvdata(pdev, NULL);
+		cancel_work_sync(&chip->irq_work);
+		free_irq(chip->overtemp_irq, chip);
+		free_irq(chip->tempstat_irq, chip);
+		pm8xxx_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_DISABLED);
+		pm8xxx_init_adc(chip, false);
+		thermal_zone_device_unregister(chip->tz_dev);
+		kfree(chip);
+	}
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int pm8xxx_tm_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct pm8xxx_tm_chip *chip = platform_get_drvdata(pdev);
+
+	/* Clear override bits in suspend to allow hardware control */
+	pm8xxx_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_DISABLED);
+
+	return 0;
+}
+
+static int pm8xxx_tm_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct pm8xxx_tm_chip *chip = platform_get_drvdata(pdev);
+
+	/* Override hardware actions so software can control */
+	if (chip->mode == THERMAL_DEVICE_ENABLED)
+		pm8xxx_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_ENABLED);
+
+	return 0;
+}
+
+static const struct dev_pm_ops pm8xxx_tm_pm_ops = {
+	.suspend = pm8xxx_tm_suspend,
+	.resume = pm8xxx_tm_resume,
+};
+
+#define PM8XXX_TM_PM_OPS	(&pm8xxx_tm_pm_ops)
+#else
+#define PM8XXX_TM_PM_OPS	NULL
+#endif
+
+static struct platform_driver pm8xxx_tm_driver = {
+	.probe	= pm8xxx_tm_probe,
+	.remove	= __devexit_p(pm8xxx_tm_remove),
+	.driver	= {
+		.name = PM8XXX_TM_DEV_NAME,
+		.owner = THIS_MODULE,
+		.pm = PM8XXX_TM_PM_OPS,
+	},
+};
+
+static int __init pm8xxx_tm_init(void)
+{
+	return platform_driver_register(&pm8xxx_tm_driver);
+}
+
+static void __exit pm8xxx_tm_exit(void)
+{
+	platform_driver_unregister(&pm8xxx_tm_driver);
+}
+
+module_init(pm8xxx_tm_init);
+module_exit(pm8xxx_tm_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PM8xxx Thermal Manager driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8XXX_TM_DEV_NAME);
diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c
index 022bacb..fd1a2fc 100644
--- a/drivers/thermal/thermal_sys.c
+++ b/drivers/thermal/thermal_sys.c
@@ -186,6 +186,12 @@
 		return sprintf(buf, "critical\n");
 	case THERMAL_TRIP_HOT:
 		return sprintf(buf, "hot\n");
+	case THERMAL_TRIP_CONFIGURABLE_HI:
+		return sprintf(buf, "configurable_hi\n");
+	case THERMAL_TRIP_CONFIGURABLE_LOW:
+		return sprintf(buf, "configurable_low\n");
+	case THERMAL_TRIP_CRITICAL_LOW:
+		return sprintf(buf, "critical_low\n");
 	case THERMAL_TRIP_PASSIVE:
 		return sprintf(buf, "passive\n");
 	case THERMAL_TRIP_ACTIVE:
@@ -196,6 +202,34 @@
 }
 
 static ssize_t
+trip_point_type_activate(struct device *dev, struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	struct thermal_zone_device *tz = to_thermal_zone(dev);
+	int trip, result;
+
+	if (!tz->ops->activate_trip_type)
+		return -EPERM;
+
+	if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip))
+		return -EINVAL;
+
+	if (!strncmp(buf, "enabled", sizeof("enabled")))
+		result = tz->ops->activate_trip_type(tz, trip,
+					THERMAL_TRIP_ACTIVATION_ENABLED);
+	else if (!strncmp(buf, "disabled", sizeof("disabled")))
+		result = tz->ops->activate_trip_type(tz, trip,
+					THERMAL_TRIP_ACTIVATION_DISABLED);
+	else
+		result = -EINVAL;
+
+	if (result)
+		return result;
+
+	return count;
+}
+
+static ssize_t
 trip_point_temp_show(struct device *dev, struct device_attribute *attr,
 		     char *buf)
 {
@@ -218,6 +252,30 @@
 }
 
 static ssize_t
+trip_point_temp_set(struct device *dev, struct device_attribute *attr,
+		     const char *buf, size_t count)
+{
+	struct thermal_zone_device *tz = to_thermal_zone(dev);
+	int trip, ret;
+	long temperature;
+
+	if (!tz->ops->set_trip_temp)
+		return -EPERM;
+
+	if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip))
+		return -EINVAL;
+
+	if (!sscanf(buf, "%ld", &temperature))
+		return -EINVAL;
+
+	ret = tz->ops->set_trip_temp(tz, trip, temperature);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static ssize_t
 passive_store(struct device *dev, struct device_attribute *attr,
 		    const char *buf, size_t count)
 {
@@ -284,30 +342,54 @@
 static DEVICE_ATTR(passive, S_IRUGO | S_IWUSR, passive_show, passive_store);
 
 static struct device_attribute trip_point_attrs[] = {
-	__ATTR(trip_point_0_type, 0444, trip_point_type_show, NULL),
-	__ATTR(trip_point_0_temp, 0444, trip_point_temp_show, NULL),
-	__ATTR(trip_point_1_type, 0444, trip_point_type_show, NULL),
-	__ATTR(trip_point_1_temp, 0444, trip_point_temp_show, NULL),
-	__ATTR(trip_point_2_type, 0444, trip_point_type_show, NULL),
-	__ATTR(trip_point_2_temp, 0444, trip_point_temp_show, NULL),
-	__ATTR(trip_point_3_type, 0444, trip_point_type_show, NULL),
-	__ATTR(trip_point_3_temp, 0444, trip_point_temp_show, NULL),
-	__ATTR(trip_point_4_type, 0444, trip_point_type_show, NULL),
-	__ATTR(trip_point_4_temp, 0444, trip_point_temp_show, NULL),
-	__ATTR(trip_point_5_type, 0444, trip_point_type_show, NULL),
-	__ATTR(trip_point_5_temp, 0444, trip_point_temp_show, NULL),
-	__ATTR(trip_point_6_type, 0444, trip_point_type_show, NULL),
-	__ATTR(trip_point_6_temp, 0444, trip_point_temp_show, NULL),
-	__ATTR(trip_point_7_type, 0444, trip_point_type_show, NULL),
-	__ATTR(trip_point_7_temp, 0444, trip_point_temp_show, NULL),
-	__ATTR(trip_point_8_type, 0444, trip_point_type_show, NULL),
-	__ATTR(trip_point_8_temp, 0444, trip_point_temp_show, NULL),
-	__ATTR(trip_point_9_type, 0444, trip_point_type_show, NULL),
-	__ATTR(trip_point_9_temp, 0444, trip_point_temp_show, NULL),
-	__ATTR(trip_point_10_type, 0444, trip_point_type_show, NULL),
-	__ATTR(trip_point_10_temp, 0444, trip_point_temp_show, NULL),
-	__ATTR(trip_point_11_type, 0444, trip_point_type_show, NULL),
-	__ATTR(trip_point_11_temp, 0444, trip_point_temp_show, NULL),
+	__ATTR(trip_point_0_type, 0644, trip_point_type_show,
+					trip_point_type_activate),
+	__ATTR(trip_point_0_temp, 0644, trip_point_temp_show,
+					trip_point_temp_set),
+	__ATTR(trip_point_1_type, 0644, trip_point_type_show,
+					trip_point_type_activate),
+	__ATTR(trip_point_1_temp, 0644, trip_point_temp_show,
+					trip_point_temp_set),
+	__ATTR(trip_point_2_type, 0644, trip_point_type_show,
+					trip_point_type_activate),
+	__ATTR(trip_point_2_temp, 0644, trip_point_temp_show,
+					trip_point_temp_set),
+	__ATTR(trip_point_3_type, 0644, trip_point_type_show,
+					trip_point_type_activate),
+	__ATTR(trip_point_3_temp, 0644, trip_point_temp_show,
+					trip_point_temp_set),
+	__ATTR(trip_point_4_type, 0644, trip_point_type_show,
+					trip_point_type_activate),
+	__ATTR(trip_point_4_temp, 0644, trip_point_temp_show,
+					trip_point_temp_set),
+	__ATTR(trip_point_5_type, 0644, trip_point_type_show,
+					trip_point_type_activate),
+	__ATTR(trip_point_5_temp, 0644, trip_point_temp_show,
+					trip_point_temp_set),
+	__ATTR(trip_point_6_type, 0644, trip_point_type_show,
+					trip_point_type_activate),
+	__ATTR(trip_point_6_temp, 0644, trip_point_temp_show,
+					trip_point_temp_set),
+	__ATTR(trip_point_7_type, 0644, trip_point_type_show,
+					trip_point_type_activate),
+	__ATTR(trip_point_7_temp, 0644, trip_point_temp_show,
+					trip_point_temp_set),
+	__ATTR(trip_point_8_type, 0644, trip_point_type_show,
+					trip_point_type_activate),
+	__ATTR(trip_point_8_temp, 0644, trip_point_temp_show,
+					trip_point_temp_set),
+	__ATTR(trip_point_9_type, 0644, trip_point_type_show,
+					trip_point_type_activate),
+	__ATTR(trip_point_9_temp, 0644, trip_point_temp_show,
+					trip_point_temp_set),
+	__ATTR(trip_point_10_type, 0644, trip_point_type_show,
+					trip_point_type_activate),
+	__ATTR(trip_point_10_temp, 0644, trip_point_temp_show,
+					trip_point_temp_set),
+	__ATTR(trip_point_11_type, 0644, trip_point_type_show,
+					trip_point_type_activate),
+	__ATTR(trip_point_11_temp, 0644, trip_point_temp_show,
+					trip_point_temp_set),
 };
 
 /* sys I/F for cooling device */
@@ -1049,6 +1131,29 @@
 				if (tz->ops->notify)
 					tz->ops->notify(tz, count, trip_type);
 			break;
+		case THERMAL_TRIP_CONFIGURABLE_HI:
+			if (temp >= trip_temp)
+				if (tz->ops->notify)
+					tz->ops->notify(tz, count, trip_type);
+			break;
+		case THERMAL_TRIP_CONFIGURABLE_LOW:
+			if (temp <= trip_temp)
+				if (tz->ops->notify)
+					tz->ops->notify(tz, count, trip_type);
+			break;
+		case THERMAL_TRIP_CRITICAL_LOW:
+			if (temp <= trip_temp) {
+				if (tz->ops->notify)
+					ret = tz->ops->notify(tz, count,
+								trip_type);
+			if (!ret) {
+				printk(KERN_EMERG
+				"Critical temperature reached (%ld C), \
+					shutting down.\n", temp/1000);
+				orderly_poweroff(true);
+				}
+			}
+			break;
 		case THERMAL_TRIP_ACTIVE:
 			list_for_each_entry(instance, &tz->cooling_devices,
 					    node) {
diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig
index 830cd62..27c49a6 100644
--- a/drivers/tty/Kconfig
+++ b/drivers/tty/Kconfig
@@ -323,6 +323,25 @@
 	  This line discipline provides support for the GSM MUX protocol and
 	  presents the mux as a set of 61 individual tty devices.
 
+config N_SMUX
+	tristate "SMUX line discipline support"
+	depends on NET && SERIAL_MSM_HS
+	help
+	  This line discipline provides support for the Serial MUX protocol
+	  and provides a TTY and kernel API for multiple logical channels.
+
+config N_SMUX_LOOPBACK
+	tristate "SMUX line discipline loopback support"
+	depends on N_SMUX
+	help
+	  Provides loopback and unit testing support for the Serial MUX Protocol.
+
+config SMUX_CTL
+	tristate "SMUX control driver"
+	depends on N_SMUX
+	help
+	  Support for SMUX control driver on top of serial MUX.
+
 config TRACE_ROUTER
 	tristate "Trace data router for MIPI P1149.7 cJTAG standard"
 	depends on TRACE_SINK
diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile
index 2953059..9ebb54a 100644
--- a/drivers/tty/Makefile
+++ b/drivers/tty/Makefile
@@ -6,6 +6,9 @@
 obj-$(CONFIG_MAGIC_SYSRQ)	+= sysrq.o
 obj-$(CONFIG_N_HDLC)		+= n_hdlc.o
 obj-$(CONFIG_N_GSM)		+= n_gsm.o
+obj-$(CONFIG_N_SMUX)		+= n_smux.o
+obj-$(CONFIG_N_SMUX_LOOPBACK)	+= smux_test.o smux_loopback.o
+obj-$(CONFIG_SMUX_CTL)		+= smux_ctl.o
 obj-$(CONFIG_TRACE_ROUTER)	+= n_tracerouter.o
 obj-$(CONFIG_TRACE_SINK)	+= n_tracesink.o
 obj-$(CONFIG_R3964)		+= n_r3964.o
diff --git a/drivers/tty/hvc/hvc_dcc.c b/drivers/tty/hvc/hvc_dcc.c
index 44fbeba..81429c2 100644
--- a/drivers/tty/hvc/hvc_dcc.c
+++ b/drivers/tty/hvc/hvc_dcc.c
@@ -8,11 +8,6 @@
  * 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 Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
  */
 
 #include <linux/console.h>
diff --git a/drivers/tty/n_smux.c b/drivers/tty/n_smux.c
new file mode 100644
index 0000000..df335d2
--- /dev/null
+++ b/drivers/tty/n_smux.c
@@ -0,0 +1,2934 @@
+/* drivers/tty/n_smux.c
+ *
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * 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/types.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/tty_driver.h>
+#include <linux/smux.h>
+#include <linux/list.h>
+#include <linux/kfifo.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <mach/msm_serial_hs.h>
+#include "smux_private.h"
+#include "smux_loopback.h"
+
+#define SMUX_NOTIFY_FIFO_SIZE	128
+#define SMUX_TX_QUEUE_SIZE	256
+#define SMUX_GET_RX_BUFF_MAX_RETRY_CNT 2
+#define SMUX_WM_LOW 2
+#define SMUX_WM_HIGH 4
+#define SMUX_PKT_LOG_SIZE 80
+
+/* Maximum size we can accept in a single RX buffer */
+#define TTY_RECEIVE_ROOM 65536
+#define TTY_BUFFER_FULL_WAIT_MS 50
+
+/* maximum sleep time between wakeup attempts */
+#define SMUX_WAKEUP_DELAY_MAX (1 << 20)
+
+/* minimum delay for scheduling delayed work */
+#define SMUX_WAKEUP_DELAY_MIN (1 << 15)
+
+/* inactivity timeout for no rx/tx activity */
+#define SMUX_INACTIVITY_TIMEOUT_MS 1000
+
+enum {
+	MSM_SMUX_DEBUG = 1U << 0,
+	MSM_SMUX_INFO = 1U << 1,
+	MSM_SMUX_POWER_INFO = 1U << 2,
+	MSM_SMUX_PKT = 1U << 3,
+};
+
+static int smux_debug_mask;
+module_param_named(debug_mask, smux_debug_mask,
+		   int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+/* Simulated wakeup used for testing */
+int smux_byte_loopback;
+module_param_named(byte_loopback, smux_byte_loopback,
+		   int, S_IRUGO | S_IWUSR | S_IWGRP);
+int smux_simulate_wakeup_delay = 1;
+module_param_named(simulate_wakeup_delay, smux_simulate_wakeup_delay,
+		   int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+#define SMUX_DBG(x...) do {                              \
+	if (smux_debug_mask & MSM_SMUX_DEBUG) \
+			pr_info(x);  \
+} while (0)
+
+#define SMUX_LOG_PKT_RX(pkt) do { \
+	if (smux_debug_mask & MSM_SMUX_PKT) \
+			smux_log_pkt(pkt, 1); \
+} while (0)
+
+#define SMUX_LOG_PKT_TX(pkt) do { \
+	if (smux_debug_mask & MSM_SMUX_PKT) \
+			smux_log_pkt(pkt, 0); \
+} while (0)
+
+/**
+ * Return true if channel is fully opened (both
+ * local and remote sides are in the OPENED state).
+ */
+#define IS_FULLY_OPENED(ch) \
+	(ch && (ch)->local_state == SMUX_LCH_LOCAL_OPENED \
+	 && (ch)->remote_state == SMUX_LCH_REMOTE_OPENED)
+
+static struct platform_device smux_devs[] = {
+	{.name = "SMUX_CTL", .id = -1},
+	{.name = "SMUX_RMNET", .id = -1},
+	{.name = "SMUX_DUN_DATA_HSUART", .id = 0},
+	{.name = "SMUX_RMNET_DATA_HSUART", .id = 1},
+	{.name = "SMUX_RMNET_CTL_HSUART", .id = 0},
+	{.name = "SMUX_DIAG", .id = -1},
+};
+
+enum {
+	SMUX_CMD_STATUS_RTC = 1 << 0,
+	SMUX_CMD_STATUS_RTR = 1 << 1,
+	SMUX_CMD_STATUS_RI = 1 << 2,
+	SMUX_CMD_STATUS_DCD = 1 << 3,
+	SMUX_CMD_STATUS_FLOW_CNTL = 1 << 4,
+};
+
+/* Channel mode */
+enum {
+	SMUX_LCH_MODE_NORMAL,
+	SMUX_LCH_MODE_LOCAL_LOOPBACK,
+	SMUX_LCH_MODE_REMOTE_LOOPBACK,
+};
+
+enum {
+	SMUX_RX_IDLE,
+	SMUX_RX_MAGIC,
+	SMUX_RX_HDR,
+	SMUX_RX_PAYLOAD,
+	SMUX_RX_FAILURE,
+};
+
+/**
+ * Power states.
+ *
+ * The _FLUSH states are internal transitional states and are not part of the
+ * official state machine.
+ */
+enum {
+	SMUX_PWR_OFF,
+	SMUX_PWR_TURNING_ON,
+	SMUX_PWR_ON,
+	SMUX_PWR_TURNING_OFF_FLUSH,
+	SMUX_PWR_TURNING_OFF,
+	SMUX_PWR_OFF_FLUSH,
+};
+
+/**
+ * Logical Channel Structure.  One instance per channel.
+ *
+ * Locking Hierarchy
+ * Each lock has a postfix that describes the locking level.  If multiple locks
+ * are required, only increasing lock hierarchy numbers may be locked which
+ * ensures avoiding a deadlock.
+ *
+ * Locking Example
+ * If state_lock_lhb1 is currently held and the TX list needs to be
+ * manipulated, then tx_lock_lhb2 may be locked since it's locking hierarchy
+ * is greater.  However, if tx_lock_lhb2 is held, then state_lock_lhb1 may
+ * not be acquired since it would result in a deadlock.
+ *
+ * Note that the Line Discipline locks (*_lha) should always be acquired
+ * before the logical channel locks.
+ */
+struct smux_lch_t {
+	/* channel state */
+	spinlock_t state_lock_lhb1;
+	uint8_t lcid;
+	unsigned local_state;
+	unsigned local_mode;
+	uint8_t local_tiocm;
+
+	unsigned remote_state;
+	unsigned remote_mode;
+	uint8_t remote_tiocm;
+
+	int tx_flow_control;
+
+	/* client callbacks and private data */
+	void *priv;
+	void (*notify)(void *priv, int event_type, const void *metadata);
+	int (*get_rx_buffer)(void *priv, void **pkt_priv, void **buffer,
+								int size);
+
+	/* TX Info */
+	spinlock_t tx_lock_lhb2;
+	struct list_head tx_queue;
+	struct list_head tx_ready_list;
+	unsigned tx_pending_data_cnt;
+	unsigned notify_lwm;
+};
+
+union notifier_metadata {
+	struct smux_meta_disconnected disconnected;
+	struct smux_meta_read read;
+	struct smux_meta_write write;
+	struct smux_meta_tiocm tiocm;
+};
+
+struct smux_notify_handle {
+	void (*notify)(void *priv, int event_type, const void *metadata);
+	void *priv;
+	int event_type;
+	union notifier_metadata *metadata;
+};
+
+/**
+ * Line discipline and module structure.
+ *
+ * Only one instance since multiple instances of line discipline are not
+ * allowed.
+ */
+struct smux_ldisc_t {
+	spinlock_t lock_lha0;
+
+	int is_initialized;
+	int in_reset;
+	int ld_open_count;
+	struct tty_struct *tty;
+
+	/* RX State Machine */
+	spinlock_t rx_lock_lha1;
+	unsigned char recv_buf[SMUX_MAX_PKT_SIZE];
+	unsigned int recv_len;
+	unsigned int pkt_remain;
+	unsigned rx_state;
+	unsigned rx_activity_flag;
+
+	/* TX / Power */
+	spinlock_t tx_lock_lha2;
+	struct list_head lch_tx_ready_list;
+	unsigned power_state;
+	unsigned pwr_wakeup_delay_us;
+	unsigned tx_activity_flag;
+	unsigned powerdown_enabled;
+};
+
+
+/* data structures */
+static struct smux_lch_t smux_lch[SMUX_NUM_LOGICAL_CHANNELS];
+static struct smux_ldisc_t smux;
+static const char *tty_error_type[] = {
+	[TTY_NORMAL] = "normal",
+	[TTY_OVERRUN] = "overrun",
+	[TTY_BREAK] = "break",
+	[TTY_PARITY] = "parity",
+	[TTY_FRAME] = "framing",
+};
+
+static const char *smux_cmds[] = {
+	[SMUX_CMD_DATA] = "DATA",
+	[SMUX_CMD_OPEN_LCH] = "OPEN",
+	[SMUX_CMD_CLOSE_LCH] = "CLOSE",
+	[SMUX_CMD_STATUS] = "STATUS",
+	[SMUX_CMD_PWR_CTL] = "PWR",
+	[SMUX_CMD_BYTE] = "Raw Byte",
+};
+
+static void smux_notify_local_fn(struct work_struct *work);
+static DECLARE_WORK(smux_notify_local, smux_notify_local_fn);
+
+static struct workqueue_struct *smux_notify_wq;
+static size_t handle_size;
+static struct kfifo smux_notify_fifo;
+static int queued_fifo_notifications;
+static DEFINE_SPINLOCK(notify_lock_lhc1);
+
+static struct workqueue_struct *smux_tx_wq;
+static void smux_tx_worker(struct work_struct *work);
+static DECLARE_WORK(smux_tx_work, smux_tx_worker);
+
+static void smux_wakeup_worker(struct work_struct *work);
+static DECLARE_WORK(smux_wakeup_work, smux_wakeup_worker);
+static DECLARE_DELAYED_WORK(smux_wakeup_delayed_work, smux_wakeup_worker);
+
+static void smux_inactivity_worker(struct work_struct *work);
+static DECLARE_WORK(smux_inactivity_work, smux_inactivity_worker);
+static DECLARE_DELAYED_WORK(smux_delayed_inactivity_work,
+		smux_inactivity_worker);
+
+static long msm_smux_tiocm_get_atomic(struct smux_lch_t *ch);
+static void list_channel(struct smux_lch_t *ch);
+static int smux_send_status_cmd(struct smux_lch_t *ch);
+static int smux_dispatch_rx_pkt(struct smux_pkt_t *pkt);
+
+/**
+ * Convert TTY Error Flags to string for logging purposes.
+ *
+ * @flag    TTY_* flag
+ * @returns String description or NULL if unknown
+ */
+static const char *tty_flag_to_str(unsigned flag)
+{
+	if (flag < ARRAY_SIZE(tty_error_type))
+		return tty_error_type[flag];
+	return NULL;
+}
+
+/**
+ * Convert SMUX Command to string for logging purposes.
+ *
+ * @cmd    SMUX command
+ * @returns String description or NULL if unknown
+ */
+static const char *cmd_to_str(unsigned cmd)
+{
+	if (cmd < ARRAY_SIZE(smux_cmds))
+		return smux_cmds[cmd];
+	return NULL;
+}
+
+/**
+ * Set the reset state due to an unrecoverable failure.
+ */
+static void smux_enter_reset(void)
+{
+	pr_err("%s: unrecoverable failure, waiting for ssr\n", __func__);
+	smux.in_reset = 1;
+}
+
+static int lch_init(void)
+{
+	unsigned int id;
+	struct smux_lch_t *ch;
+	int i = 0;
+
+	handle_size = sizeof(struct smux_notify_handle *);
+
+	smux_notify_wq = create_singlethread_workqueue("smux_notify_wq");
+	smux_tx_wq = create_singlethread_workqueue("smux_tx_wq");
+
+	if (IS_ERR(smux_notify_wq) || IS_ERR(smux_tx_wq)) {
+		SMUX_DBG("%s: create_singlethread_workqueue ENOMEM\n",
+							__func__);
+		return -ENOMEM;
+	}
+
+	i |= kfifo_alloc(&smux_notify_fifo,
+			SMUX_NOTIFY_FIFO_SIZE * handle_size,
+			GFP_KERNEL);
+	i |= smux_loopback_init();
+
+	if (i) {
+		pr_err("%s: out of memory error\n", __func__);
+		return -ENOMEM;
+	}
+
+	for (id = 0 ; id < SMUX_NUM_LOGICAL_CHANNELS; id++) {
+		ch = &smux_lch[id];
+
+		spin_lock_init(&ch->state_lock_lhb1);
+		ch->lcid = id;
+		ch->local_state = SMUX_LCH_LOCAL_CLOSED;
+		ch->local_mode = SMUX_LCH_MODE_NORMAL;
+		ch->local_tiocm = 0x0;
+		ch->remote_state = SMUX_LCH_REMOTE_CLOSED;
+		ch->remote_mode = SMUX_LCH_MODE_NORMAL;
+		ch->remote_tiocm = 0x0;
+		ch->tx_flow_control = 0;
+		ch->priv = 0;
+		ch->notify = 0;
+		ch->get_rx_buffer = 0;
+
+		spin_lock_init(&ch->tx_lock_lhb2);
+		INIT_LIST_HEAD(&ch->tx_queue);
+		INIT_LIST_HEAD(&ch->tx_ready_list);
+		ch->tx_pending_data_cnt = 0;
+		ch->notify_lwm = 0;
+	}
+
+	return 0;
+}
+
+int smux_assert_lch_id(uint32_t lcid)
+{
+	if (lcid >= SMUX_NUM_LOGICAL_CHANNELS)
+		return -ENXIO;
+	else
+		return 0;
+}
+
+/**
+ * Log packet information for debug purposes.
+ *
+ * @pkt     Packet to log
+ * @is_recv 1 = RX packet; 0 = TX Packet
+ *
+ * [DIR][LCID] [LOCAL_STATE][LOCAL_MODE]:[REMOTE_STATE][REMOTE_MODE] PKT Info
+ *
+ * PKT Info:
+ *   [CMD] flags [flags] len [PAYLOAD_LEN]:[PAD_LEN] [Payload hex bytes]
+ *
+ * Direction:  R = Receive, S = Send
+ * Local State:  C = Closed; c = closing; o = opening; O = Opened
+ * Local Mode: L = Local loopback; R = Remote loopback; N = Normal
+ * Remote State: C = Closed; O = Opened
+ * Remote Mode: R = Remote loopback; N = Normal
+ */
+static void smux_log_pkt(struct smux_pkt_t *pkt, int is_recv)
+{
+	char logbuf[SMUX_PKT_LOG_SIZE];
+	char cmd_extra[16];
+	int i = 0;
+	int count;
+	int len;
+	char local_state;
+	char local_mode;
+	char remote_state;
+	char remote_mode;
+	struct smux_lch_t *ch;
+	unsigned char *data;
+
+	ch = &smux_lch[pkt->hdr.lcid];
+
+	switch (ch->local_state) {
+	case SMUX_LCH_LOCAL_CLOSED:
+		local_state = 'C';
+		break;
+	case SMUX_LCH_LOCAL_OPENING:
+		local_state = 'o';
+		break;
+	case SMUX_LCH_LOCAL_OPENED:
+		local_state = 'O';
+		break;
+	case SMUX_LCH_LOCAL_CLOSING:
+		local_state = 'c';
+		break;
+	default:
+		local_state = 'U';
+		break;
+	}
+
+	switch (ch->local_mode) {
+	case SMUX_LCH_MODE_LOCAL_LOOPBACK:
+		local_mode = 'L';
+		break;
+	case SMUX_LCH_MODE_REMOTE_LOOPBACK:
+		local_mode = 'R';
+		break;
+	case SMUX_LCH_MODE_NORMAL:
+		local_mode = 'N';
+		break;
+	default:
+		local_mode = 'U';
+		break;
+	}
+
+	switch (ch->remote_state) {
+	case SMUX_LCH_REMOTE_CLOSED:
+		remote_state = 'C';
+		break;
+	case SMUX_LCH_REMOTE_OPENED:
+		remote_state = 'O';
+		break;
+
+	default:
+		remote_state = 'U';
+		break;
+	}
+
+	switch (ch->remote_mode) {
+	case SMUX_LCH_MODE_REMOTE_LOOPBACK:
+		remote_mode = 'R';
+		break;
+	case SMUX_LCH_MODE_NORMAL:
+		remote_mode = 'N';
+		break;
+	default:
+		remote_mode = 'U';
+		break;
+	}
+
+	/* determine command type (ACK, etc) */
+	cmd_extra[0] = '\0';
+	switch (pkt->hdr.cmd) {
+	case SMUX_CMD_OPEN_LCH:
+		if (pkt->hdr.flags & SMUX_CMD_OPEN_ACK)
+			snprintf(cmd_extra, sizeof(cmd_extra), " ACK");
+		break;
+	case SMUX_CMD_CLOSE_LCH:
+		if (pkt->hdr.flags & SMUX_CMD_CLOSE_ACK)
+			snprintf(cmd_extra, sizeof(cmd_extra), " ACK");
+		break;
+	};
+
+	i += snprintf(logbuf + i, SMUX_PKT_LOG_SIZE - i,
+			"smux: %c%d %c%c:%c%c %s%s flags %x len %d:%d ",
+			is_recv ? 'R' : 'S', pkt->hdr.lcid,
+			local_state, local_mode,
+			remote_state, remote_mode,
+			cmd_to_str(pkt->hdr.cmd), cmd_extra, pkt->hdr.flags,
+			pkt->hdr.payload_len, pkt->hdr.pad_len);
+
+	len = (pkt->hdr.payload_len > 16) ? 16 : pkt->hdr.payload_len;
+	data = (unsigned char *)pkt->payload;
+	for (count = 0; count < len; count++)
+		i += snprintf(logbuf + i, SMUX_PKT_LOG_SIZE - i,
+				"%02x ", (unsigned)data[count]);
+
+	pr_info("%s\n", logbuf);
+}
+
+static void smux_notify_local_fn(struct work_struct *work)
+{
+	struct smux_notify_handle *notify_handle = NULL;
+	union notifier_metadata *metadata = NULL;
+	unsigned long flags;
+	int i;
+
+	for (;;) {
+		/* retrieve notification */
+		spin_lock_irqsave(&notify_lock_lhc1, flags);
+		if (kfifo_len(&smux_notify_fifo) >= handle_size) {
+			i = kfifo_out(&smux_notify_fifo,
+				&notify_handle,
+				handle_size);
+		if (i != handle_size) {
+			pr_err("%s: unable to retrieve handle %d expected %d\n",
+					__func__, i, handle_size);
+			spin_unlock_irqrestore(&notify_lock_lhc1, flags);
+			break;
+			}
+		} else {
+			spin_unlock_irqrestore(&notify_lock_lhc1, flags);
+			break;
+		}
+		--queued_fifo_notifications;
+		spin_unlock_irqrestore(&notify_lock_lhc1, flags);
+
+		/* notify client */
+		metadata = notify_handle->metadata;
+		notify_handle->notify(notify_handle->priv,
+			notify_handle->event_type,
+			metadata);
+
+		kfree(metadata);
+		kfree(notify_handle);
+	}
+}
+
+/**
+ * Initialize existing packet.
+ */
+void smux_init_pkt(struct smux_pkt_t *pkt)
+{
+	memset(pkt, 0x0, sizeof(*pkt));
+	pkt->hdr.magic = SMUX_MAGIC;
+	INIT_LIST_HEAD(&pkt->list);
+}
+
+/**
+ * Allocate and initialize packet.
+ *
+ * If a payload is needed, either set it directly and ensure that it's freed or
+ * use smd_alloc_pkt_payload() to allocate a packet and it will be freed
+ * automatically when smd_free_pkt() is called.
+ */
+struct smux_pkt_t *smux_alloc_pkt(void)
+{
+	struct smux_pkt_t *pkt;
+
+	/* Consider a free list implementation instead of kmalloc */
+	pkt = kmalloc(sizeof(struct smux_pkt_t), GFP_ATOMIC);
+	if (!pkt) {
+		pr_err("%s: out of memory\n", __func__);
+		return NULL;
+	}
+	smux_init_pkt(pkt);
+	pkt->allocated = 1;
+
+	return pkt;
+}
+
+/**
+ * Free packet.
+ *
+ * @pkt Packet to free (may be NULL)
+ *
+ * If payload was allocated using smux_alloc_pkt_payload(), then it is freed as
+ * well.  Otherwise, the caller is responsible for freeing the payload.
+ */
+void smux_free_pkt(struct smux_pkt_t *pkt)
+{
+	if (pkt) {
+		if (pkt->free_payload)
+			kfree(pkt->payload);
+		if (pkt->allocated)
+			kfree(pkt);
+	}
+}
+
+/**
+ * Allocate packet payload.
+ *
+ * @pkt Packet to add payload to
+ *
+ * @returns 0 on success, <0 upon error
+ *
+ * A flag is set to signal smux_free_pkt() to free the payload.
+ */
+int smux_alloc_pkt_payload(struct smux_pkt_t *pkt)
+{
+	if (!pkt)
+		return -EINVAL;
+
+	pkt->payload = kmalloc(pkt->hdr.payload_len, GFP_ATOMIC);
+	pkt->free_payload = 1;
+	if (!pkt->payload) {
+		pr_err("%s: unable to malloc %d bytes for payload\n",
+				__func__, pkt->hdr.payload_len);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static int schedule_notify(uint8_t lcid, int event,
+			const union notifier_metadata *metadata)
+{
+	struct smux_notify_handle *notify_handle = 0;
+	union notifier_metadata *meta_copy = 0;
+	struct smux_lch_t *ch;
+	int i;
+	unsigned long flags;
+	int ret = 0;
+
+	ch = &smux_lch[lcid];
+	notify_handle = kzalloc(sizeof(struct smux_notify_handle),
+						GFP_ATOMIC);
+	if (!notify_handle) {
+		pr_err("%s: out of memory\n", __func__);
+		ret = -ENOMEM;
+		goto free_out;
+	}
+
+	notify_handle->notify = ch->notify;
+	notify_handle->priv = ch->priv;
+	notify_handle->event_type = event;
+	if (metadata) {
+		meta_copy = kzalloc(sizeof(union notifier_metadata),
+							GFP_ATOMIC);
+		if (!meta_copy) {
+			pr_err("%s: out of memory\n", __func__);
+			ret = -ENOMEM;
+			goto free_out;
+		}
+		*meta_copy = *metadata;
+		notify_handle->metadata = meta_copy;
+	} else {
+		notify_handle->metadata = NULL;
+	}
+
+	spin_lock_irqsave(&notify_lock_lhc1, flags);
+	i = kfifo_avail(&smux_notify_fifo);
+	if (i < handle_size) {
+		pr_err("%s: fifo full error %d expected %d\n",
+					__func__, i, handle_size);
+		ret = -ENOMEM;
+		goto unlock_out;
+	}
+
+	i = kfifo_in(&smux_notify_fifo, &notify_handle, handle_size);
+	if (i < 0 || i != handle_size) {
+		pr_err("%s: fifo not available error %d (expected %d)\n",
+				__func__, i, handle_size);
+		ret = -ENOSPC;
+		goto unlock_out;
+	}
+	++queued_fifo_notifications;
+
+unlock_out:
+	spin_unlock_irqrestore(&notify_lock_lhc1, flags);
+
+free_out:
+	queue_work(smux_notify_wq, &smux_notify_local);
+	if (ret < 0 && notify_handle) {
+		kfree(notify_handle->metadata);
+		kfree(notify_handle);
+	}
+	return ret;
+}
+
+/**
+ * Returns the serialized size of a packet.
+ *
+ * @pkt Packet to serialize
+ *
+ * @returns Serialized length of packet
+ */
+static unsigned int smux_serialize_size(struct smux_pkt_t *pkt)
+{
+	unsigned int size;
+
+	size = sizeof(struct smux_hdr_t);
+	size += pkt->hdr.payload_len;
+	size += pkt->hdr.pad_len;
+
+	return size;
+}
+
+/**
+ * Serialize packet @pkt into output buffer @data.
+ *
+ * @pkt		Packet to serialize
+ * @out     Destination buffer pointer
+ * @out_len	Size of serialized packet
+ *
+ * @returns 0 for success
+ */
+int smux_serialize(struct smux_pkt_t *pkt, char *out,
+					unsigned int *out_len)
+{
+	char *data_start = out;
+
+	if (smux_serialize_size(pkt) > SMUX_MAX_PKT_SIZE) {
+		pr_err("%s: packet size %d too big\n",
+				__func__, smux_serialize_size(pkt));
+		return -E2BIG;
+	}
+
+	memcpy(out, &pkt->hdr, sizeof(struct smux_hdr_t));
+	out += sizeof(struct smux_hdr_t);
+	if (pkt->payload) {
+		memcpy(out, pkt->payload, pkt->hdr.payload_len);
+		out += pkt->hdr.payload_len;
+	}
+	if (pkt->hdr.pad_len) {
+		memset(out, 0x0,  pkt->hdr.pad_len);
+		out += pkt->hdr.pad_len;
+	}
+	*out_len = out - data_start;
+	return 0;
+}
+
+/**
+ * Serialize header and provide pointer to the data.
+ *
+ * @pkt             Packet
+ * @out[out]        Pointer to the serialized header data
+ * @out_len[out]    Pointer to the serialized header length
+ */
+static void smux_serialize_hdr(struct smux_pkt_t *pkt, char **out,
+					unsigned int *out_len)
+{
+	*out = (char *)&pkt->hdr;
+	*out_len = sizeof(struct smux_hdr_t);
+}
+
+/**
+ * Serialize payload and provide pointer to the data.
+ *
+ * @pkt             Packet
+ * @out[out]        Pointer to the serialized payload data
+ * @out_len[out]    Pointer to the serialized payload length
+ */
+static void smux_serialize_payload(struct smux_pkt_t *pkt, char **out,
+					unsigned int *out_len)
+{
+	*out = pkt->payload;
+	*out_len = pkt->hdr.payload_len;
+}
+
+/**
+ * Serialize padding and provide pointer to the data.
+ *
+ * @pkt             Packet
+ * @out[out]        Pointer to the serialized padding (always NULL)
+ * @out_len[out]    Pointer to the serialized payload length
+ *
+ * Since the padding field value is undefined, only the size of the patting
+ * (@out_len) is set and the buffer pointer (@out) will always be NULL.
+ */
+static void smux_serialize_padding(struct smux_pkt_t *pkt, char **out,
+					unsigned int *out_len)
+{
+	*out = NULL;
+	*out_len = pkt->hdr.pad_len;
+}
+
+/**
+ * Write data to TTY framework and handle breaking the writes up if needed.
+ *
+ * @data    Data to write
+ * @len     Length of data
+ *
+ * @returns 0 for success, < 0 for failure
+ */
+static int write_to_tty(char *data, unsigned len)
+{
+	int data_written;
+
+	if (!data)
+		return 0;
+
+	while (len > 0) {
+		data_written = smux.tty->ops->write(smux.tty, data, len);
+		if (data_written >= 0) {
+			len -= data_written;
+			data += data_written;
+		} else {
+			pr_err("%s: TTY write returned error %d\n",
+					__func__, data_written);
+			return data_written;
+		}
+
+		if (len)
+			tty_wait_until_sent(smux.tty,
+				msecs_to_jiffies(TTY_BUFFER_FULL_WAIT_MS));
+
+		/* FUTURE - add SSR logic */
+	}
+	return 0;
+}
+
+/**
+ * Write packet to TTY.
+ *
+ * @pkt packet to write
+ *
+ * @returns 0 on success
+ */
+static int smux_tx_tty(struct smux_pkt_t *pkt)
+{
+	char *data;
+	unsigned int len;
+	int ret;
+
+	if (!smux.tty) {
+		pr_err("%s: TTY not initialized", __func__);
+		return -ENOTTY;
+	}
+
+	if (pkt->hdr.cmd == SMUX_CMD_BYTE) {
+		SMUX_DBG("%s: tty send single byte\n", __func__);
+		ret = write_to_tty(&pkt->hdr.flags, 1);
+		return ret;
+	}
+
+	smux_serialize_hdr(pkt, &data, &len);
+	ret = write_to_tty(data, len);
+	if (ret) {
+		pr_err("%s: failed %d to write header %d\n",
+				__func__, ret, len);
+		return ret;
+	}
+
+	smux_serialize_payload(pkt, &data, &len);
+	ret = write_to_tty(data, len);
+	if (ret) {
+		pr_err("%s: failed %d to write payload %d\n",
+				__func__, ret, len);
+		return ret;
+	}
+
+	smux_serialize_padding(pkt, &data, &len);
+	while (len > 0) {
+		char zero = 0x0;
+		ret = write_to_tty(&zero, 1);
+		if (ret) {
+			pr_err("%s: failed %d to write padding %d\n",
+					__func__, ret, len);
+			return ret;
+		}
+		--len;
+	}
+	return 0;
+}
+
+/**
+ * Send a single character.
+ *
+ * @ch Character to send
+ */
+static void smux_send_byte(char ch)
+{
+	struct smux_pkt_t pkt;
+
+	smux_init_pkt(&pkt);
+
+	pkt.hdr.cmd = SMUX_CMD_BYTE;
+	pkt.hdr.flags = ch;
+	pkt.hdr.lcid = 0;
+	pkt.hdr.flags = ch;
+	SMUX_LOG_PKT_TX(&pkt);
+	if (!smux_byte_loopback)
+		smux_tx_tty(&pkt);
+	else
+		smux_tx_loopback(&pkt);
+}
+
+/**
+ * Receive a single-character packet (used for internal testing).
+ *
+ * @ch   Character to receive
+ * @lcid Logical channel ID for packet
+ *
+ * @returns 0 for success
+ *
+ * Called with rx_lock_lha1 locked.
+ */
+static int smux_receive_byte(char ch, int lcid)
+{
+	struct smux_pkt_t pkt;
+
+	smux_init_pkt(&pkt);
+	pkt.hdr.lcid = lcid;
+	pkt.hdr.cmd = SMUX_CMD_BYTE;
+	pkt.hdr.flags = ch;
+
+	return smux_dispatch_rx_pkt(&pkt);
+}
+
+/**
+ * Queue packet for transmit.
+ *
+ * @pkt_ptr  Packet to queue
+ * @ch       Channel to queue packet on
+ * @queue    Queue channel on ready list
+ */
+static void smux_tx_queue(struct smux_pkt_t *pkt_ptr, struct smux_lch_t *ch,
+		int queue)
+{
+	unsigned long flags;
+
+	SMUX_DBG("%s: queuing pkt %p\n", __func__, pkt_ptr);
+
+	spin_lock_irqsave(&ch->tx_lock_lhb2, flags);
+	list_add_tail(&pkt_ptr->list, &ch->tx_queue);
+	spin_unlock_irqrestore(&ch->tx_lock_lhb2, flags);
+
+	if (queue)
+		list_channel(ch);
+}
+
+/**
+ * Handle receive OPEN ACK command.
+ *
+ * @pkt  Received packet
+ *
+ * @returns 0 for success
+ *
+ * Called with rx_lock_lha1 already locked.
+ */
+static int smux_handle_rx_open_ack(struct smux_pkt_t *pkt)
+{
+	uint8_t lcid;
+	int ret;
+	struct smux_lch_t *ch;
+	int enable_powerdown = 0;
+
+	lcid = pkt->hdr.lcid;
+	ch = &smux_lch[lcid];
+
+	spin_lock(&ch->state_lock_lhb1);
+	if (ch->local_state == SMUX_LCH_LOCAL_OPENING) {
+		SMUX_DBG("lcid %d local state 0x%x -> 0x%x\n", lcid,
+				ch->local_state,
+				SMUX_LCH_LOCAL_OPENED);
+
+		if (pkt->hdr.flags & SMUX_CMD_OPEN_POWER_COLLAPSE)
+			enable_powerdown = 1;
+
+		ch->local_state = SMUX_LCH_LOCAL_OPENED;
+		if (ch->remote_state == SMUX_LCH_REMOTE_OPENED)
+			schedule_notify(lcid, SMUX_CONNECTED, NULL);
+		ret = 0;
+	} else if (ch->remote_mode == SMUX_LCH_MODE_REMOTE_LOOPBACK) {
+		SMUX_DBG("Remote loopback OPEN ACK received\n");
+		ret = 0;
+	} else {
+		pr_err("%s: lcid %d state 0x%x open ack invalid\n",
+				__func__, lcid, ch->local_state);
+		ret = -EINVAL;
+	}
+	spin_unlock(&ch->state_lock_lhb1);
+
+	if (enable_powerdown) {
+		spin_lock(&smux.tx_lock_lha2);
+		if (!smux.powerdown_enabled) {
+			smux.powerdown_enabled = 1;
+			SMUX_DBG("%s: enabling power-collapse support\n",
+					__func__);
+		}
+		spin_unlock(&smux.tx_lock_lha2);
+	}
+
+	return ret;
+}
+
+static int smux_handle_close_ack(struct smux_pkt_t *pkt)
+{
+	uint8_t lcid;
+	int ret;
+	struct smux_lch_t *ch;
+	union notifier_metadata meta_disconnected;
+	unsigned long flags;
+
+	lcid = pkt->hdr.lcid;
+	ch = &smux_lch[lcid];
+	meta_disconnected.disconnected.is_ssr = 0;
+
+	spin_lock_irqsave(&ch->state_lock_lhb1, flags);
+
+	if (ch->local_state == SMUX_LCH_LOCAL_CLOSING) {
+		SMUX_DBG("lcid %d local state 0x%x -> 0x%x\n", lcid,
+				SMUX_LCH_LOCAL_CLOSING,
+				SMUX_LCH_LOCAL_CLOSED);
+		ch->local_state = SMUX_LCH_LOCAL_CLOSED;
+		if (ch->remote_state == SMUX_LCH_REMOTE_CLOSED)
+			schedule_notify(lcid, SMUX_DISCONNECTED,
+				&meta_disconnected);
+		ret = 0;
+	} else if (ch->remote_mode == SMUX_LCH_MODE_REMOTE_LOOPBACK) {
+		SMUX_DBG("Remote loopback CLOSE ACK received\n");
+		ret = 0;
+	} else {
+		pr_err("%s: lcid %d state 0x%x close ack invalid\n",
+				__func__, lcid,	ch->local_state);
+		ret = -EINVAL;
+	}
+	spin_unlock_irqrestore(&ch->state_lock_lhb1, flags);
+	return ret;
+}
+
+/**
+ * Handle receive OPEN command.
+ *
+ * @pkt  Received packet
+ *
+ * @returns 0 for success
+ *
+ * Called with rx_lock_lha1 already locked.
+ */
+static int smux_handle_rx_open_cmd(struct smux_pkt_t *pkt)
+{
+	uint8_t lcid;
+	int ret;
+	struct smux_lch_t *ch;
+	struct smux_pkt_t *ack_pkt;
+	int tx_ready = 0;
+	int enable_powerdown = 0;
+
+	if (pkt->hdr.flags & SMUX_CMD_OPEN_ACK)
+		return smux_handle_rx_open_ack(pkt);
+
+	lcid = pkt->hdr.lcid;
+	ch = &smux_lch[lcid];
+
+	spin_lock(&ch->state_lock_lhb1);
+
+	if (ch->remote_state == SMUX_LCH_REMOTE_CLOSED) {
+		SMUX_DBG("lcid %d remote state 0x%x -> 0x%x\n", lcid,
+				SMUX_LCH_REMOTE_CLOSED,
+				SMUX_LCH_REMOTE_OPENED);
+
+		ch->remote_state = SMUX_LCH_REMOTE_OPENED;
+		if (pkt->hdr.flags & SMUX_CMD_OPEN_POWER_COLLAPSE)
+			enable_powerdown = 1;
+
+		/* Send Open ACK */
+		ack_pkt = smux_alloc_pkt();
+		if (!ack_pkt) {
+			/* exit out to allow retrying this later */
+			ret = -ENOMEM;
+			goto out;
+		}
+		ack_pkt->hdr.cmd = SMUX_CMD_OPEN_LCH;
+		ack_pkt->hdr.flags = SMUX_CMD_OPEN_ACK
+			| SMUX_CMD_OPEN_POWER_COLLAPSE;
+		ack_pkt->hdr.lcid = lcid;
+		ack_pkt->hdr.payload_len = 0;
+		ack_pkt->hdr.pad_len = 0;
+		if (pkt->hdr.flags & SMUX_CMD_OPEN_REMOTE_LOOPBACK) {
+			ch->remote_mode = SMUX_LCH_MODE_REMOTE_LOOPBACK;
+			ack_pkt->hdr.flags |= SMUX_CMD_OPEN_REMOTE_LOOPBACK;
+		}
+		smux_tx_queue(ack_pkt, ch, 0);
+		tx_ready = 1;
+
+		if (ch->remote_mode == SMUX_LCH_MODE_REMOTE_LOOPBACK) {
+			/*
+			 * Send an Open command to the remote side to
+			 * simulate our local client doing it.
+			 */
+			ack_pkt = smux_alloc_pkt();
+			if (ack_pkt) {
+				ack_pkt->hdr.lcid = lcid;
+				ack_pkt->hdr.cmd = SMUX_CMD_OPEN_LCH;
+				ack_pkt->hdr.flags =
+					SMUX_CMD_OPEN_POWER_COLLAPSE;
+				ack_pkt->hdr.payload_len = 0;
+				ack_pkt->hdr.pad_len = 0;
+				smux_tx_queue(ack_pkt, ch, 0);
+				tx_ready = 1;
+			} else {
+				pr_err("%s: Remote loopack allocation failure\n",
+						__func__);
+			}
+		} else if (ch->local_state == SMUX_LCH_LOCAL_OPENED) {
+			schedule_notify(lcid, SMUX_CONNECTED, NULL);
+		}
+		ret = 0;
+	} else {
+		pr_err("%s: lcid %d remote state 0x%x open invalid\n",
+			   __func__, lcid, ch->remote_state);
+		ret = -EINVAL;
+	}
+
+out:
+	spin_unlock(&ch->state_lock_lhb1);
+
+	if (enable_powerdown) {
+		spin_lock(&smux.tx_lock_lha2);
+		smux.powerdown_enabled = 1;
+		SMUX_DBG("%s: enabling power-collapse support\n", __func__);
+		spin_unlock(&smux.tx_lock_lha2);
+	}
+
+	if (tx_ready)
+		list_channel(ch);
+
+	return ret;
+}
+
+/**
+ * Handle receive CLOSE command.
+ *
+ * @pkt  Received packet
+ *
+ * @returns 0 for success
+ *
+ * Called with rx_lock_lha1 already locked.
+ */
+static int smux_handle_rx_close_cmd(struct smux_pkt_t *pkt)
+{
+	uint8_t lcid;
+	int ret;
+	struct smux_lch_t *ch;
+	struct smux_pkt_t *ack_pkt;
+	union notifier_metadata meta_disconnected;
+	int tx_ready = 0;
+
+	if (pkt->hdr.flags & SMUX_CMD_CLOSE_ACK)
+		return smux_handle_close_ack(pkt);
+
+	lcid = pkt->hdr.lcid;
+	ch = &smux_lch[lcid];
+	meta_disconnected.disconnected.is_ssr = 0;
+
+	spin_lock(&ch->state_lock_lhb1);
+	if (ch->remote_state == SMUX_LCH_REMOTE_OPENED) {
+		SMUX_DBG("lcid %d remote state 0x%x -> 0x%x\n", lcid,
+				SMUX_LCH_REMOTE_OPENED,
+				SMUX_LCH_REMOTE_CLOSED);
+
+		ack_pkt = smux_alloc_pkt();
+		if (!ack_pkt) {
+			/* exit out to allow retrying this later */
+			ret = -ENOMEM;
+			goto out;
+		}
+		ch->remote_state = SMUX_LCH_REMOTE_CLOSED;
+		ack_pkt->hdr.cmd = SMUX_CMD_CLOSE_LCH;
+		ack_pkt->hdr.flags = SMUX_CMD_CLOSE_ACK;
+		ack_pkt->hdr.lcid = lcid;
+		ack_pkt->hdr.payload_len = 0;
+		ack_pkt->hdr.pad_len = 0;
+		smux_tx_queue(ack_pkt, ch, 0);
+		tx_ready = 1;
+
+		if (ch->remote_mode == SMUX_LCH_MODE_REMOTE_LOOPBACK) {
+			/*
+			 * Send a Close command to the remote side to simulate
+			 * our local client doing it.
+			 */
+			ack_pkt = smux_alloc_pkt();
+			if (ack_pkt) {
+				ack_pkt->hdr.lcid = lcid;
+				ack_pkt->hdr.cmd = SMUX_CMD_CLOSE_LCH;
+				ack_pkt->hdr.flags = 0;
+				ack_pkt->hdr.payload_len = 0;
+				ack_pkt->hdr.pad_len = 0;
+				smux_tx_queue(ack_pkt, ch, 0);
+				tx_ready = 1;
+			} else {
+				pr_err("%s: Remote loopack allocation failure\n",
+						__func__);
+			}
+		}
+
+		if (ch->local_state == SMUX_LCH_LOCAL_CLOSED)
+			schedule_notify(lcid, SMUX_DISCONNECTED,
+				&meta_disconnected);
+		ret = 0;
+	} else {
+		pr_err("%s: lcid %d remote state 0x%x close invalid\n",
+				__func__, lcid, ch->remote_state);
+		ret = -EINVAL;
+	}
+out:
+	spin_unlock(&ch->state_lock_lhb1);
+	if (tx_ready)
+		list_channel(ch);
+
+	return ret;
+}
+
+/*
+ * Handle receive DATA command.
+ *
+ * @pkt  Received packet
+ *
+ * @returns 0 for success
+ *
+ * Called with rx_lock_lha1 already locked.
+ */
+static int smux_handle_rx_data_cmd(struct smux_pkt_t *pkt)
+{
+	uint8_t lcid;
+	int ret;
+	int i;
+	int tmp;
+	int rx_len;
+	struct smux_lch_t *ch;
+	union notifier_metadata metadata;
+	int remote_loopback;
+	int tx_ready = 0;
+	struct smux_pkt_t *ack_pkt;
+	unsigned long flags;
+
+	if (!pkt || smux_assert_lch_id(pkt->hdr.lcid))
+		return -ENXIO;
+
+	lcid = pkt->hdr.lcid;
+	ch = &smux_lch[lcid];
+	spin_lock_irqsave(&ch->state_lock_lhb1, flags);
+	remote_loopback = ch->remote_mode == SMUX_LCH_MODE_REMOTE_LOOPBACK;
+
+	if (ch->local_state != SMUX_LCH_LOCAL_OPENED
+		&& !remote_loopback) {
+		pr_err("smux: ch %d error data on local state 0x%x",
+					lcid, ch->local_state);
+		ret = -EIO;
+		goto out;
+	}
+
+	if (ch->remote_state != SMUX_LCH_REMOTE_OPENED) {
+		pr_err("smux: ch %d error data on remote state 0x%x",
+					lcid, ch->remote_state);
+		ret = -EIO;
+		goto out;
+	}
+
+	rx_len = pkt->hdr.payload_len;
+	if (rx_len == 0) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	for (i = 0; i < SMUX_GET_RX_BUFF_MAX_RETRY_CNT; ++i) {
+		metadata.read.pkt_priv = 0;
+		metadata.read.buffer = 0;
+
+		if (!remote_loopback) {
+			tmp = ch->get_rx_buffer(ch->priv,
+					(void **)&metadata.read.pkt_priv,
+					(void **)&metadata.read.buffer,
+					rx_len);
+			if (tmp == 0 && metadata.read.buffer) {
+				/* place data into RX buffer */
+				memcpy(metadata.read.buffer, pkt->payload,
+								rx_len);
+				metadata.read.len = rx_len;
+				schedule_notify(lcid, SMUX_READ_DONE,
+								&metadata);
+				ret = 0;
+				break;
+			} else if (tmp == -EAGAIN) {
+				ret = -ENOMEM;
+			} else if (tmp < 0) {
+				schedule_notify(lcid, SMUX_READ_FAIL, NULL);
+				ret = -ENOMEM;
+				break;
+			} else if (!metadata.read.buffer) {
+				pr_err("%s: get_rx_buffer() buffer is NULL\n",
+					__func__);
+				ret = -ENOMEM;
+			}
+		} else {
+			/* Echo the data back to the remote client. */
+			ack_pkt = smux_alloc_pkt();
+			if (ack_pkt) {
+				ack_pkt->hdr.lcid = lcid;
+				ack_pkt->hdr.cmd = SMUX_CMD_DATA;
+				ack_pkt->hdr.flags = 0;
+				ack_pkt->hdr.payload_len = pkt->hdr.payload_len;
+				ack_pkt->payload = pkt->payload;
+				ack_pkt->hdr.pad_len = pkt->hdr.pad_len;
+				smux_tx_queue(ack_pkt, ch, 0);
+				tx_ready = 1;
+			} else {
+				pr_err("%s: Remote loopack allocation failure\n",
+						__func__);
+			}
+		}
+	}
+
+out:
+	spin_unlock_irqrestore(&ch->state_lock_lhb1, flags);
+
+	if (tx_ready)
+		list_channel(ch);
+
+	return ret;
+}
+
+/**
+ * Handle receive byte command for testing purposes.
+ *
+ * @pkt  Received packet
+ *
+ * @returns 0 for success
+ */
+static int smux_handle_rx_byte_cmd(struct smux_pkt_t *pkt)
+{
+	uint8_t lcid;
+	int ret;
+	struct smux_lch_t *ch;
+	union notifier_metadata metadata;
+	unsigned long flags;
+
+	if (!pkt || smux_assert_lch_id(pkt->hdr.lcid))
+		return -ENXIO;
+
+	lcid = pkt->hdr.lcid;
+	ch = &smux_lch[lcid];
+	spin_lock_irqsave(&ch->state_lock_lhb1, flags);
+
+	if (ch->local_state != SMUX_LCH_LOCAL_OPENED) {
+		pr_err("smux: ch %d error data on local state 0x%x",
+					lcid, ch->local_state);
+		ret = -EIO;
+		goto out;
+	}
+
+	if (ch->remote_state != SMUX_LCH_REMOTE_OPENED) {
+		pr_err("smux: ch %d error data on remote state 0x%x",
+					lcid, ch->remote_state);
+		ret = -EIO;
+		goto out;
+	}
+
+	metadata.read.pkt_priv = (void *)(int)pkt->hdr.flags;
+	metadata.read.buffer = 0;
+	schedule_notify(lcid, SMUX_READ_DONE, &metadata);
+	ret = 0;
+
+out:
+	spin_unlock_irqrestore(&ch->state_lock_lhb1, flags);
+	return ret;
+}
+
+/**
+ * Handle receive status command.
+ *
+ * @pkt  Received packet
+ *
+ * @returns 0 for success
+ *
+ * Called with rx_lock_lha1 already locked.
+ */
+static int smux_handle_rx_status_cmd(struct smux_pkt_t *pkt)
+{
+	uint8_t lcid;
+	int ret;
+	struct smux_lch_t *ch;
+	union notifier_metadata meta;
+	unsigned long flags;
+	int tx_ready = 0;
+
+	lcid = pkt->hdr.lcid;
+	ch = &smux_lch[lcid];
+
+	spin_lock_irqsave(&ch->state_lock_lhb1, flags);
+	meta.tiocm.tiocm_old = ch->remote_tiocm;
+	meta.tiocm.tiocm_new = pkt->hdr.flags;
+
+	/* update logical channel flow control */
+	if ((meta.tiocm.tiocm_old & SMUX_CMD_STATUS_FLOW_CNTL) ^
+		(meta.tiocm.tiocm_new & SMUX_CMD_STATUS_FLOW_CNTL)) {
+		/* logical channel flow control changed */
+		if (pkt->hdr.flags & SMUX_CMD_STATUS_FLOW_CNTL) {
+			/* disabled TX */
+			SMUX_DBG("TX Flow control enabled\n");
+			ch->tx_flow_control = 1;
+		} else {
+			/* re-enable channel */
+			SMUX_DBG("TX Flow control disabled\n");
+			ch->tx_flow_control = 0;
+			tx_ready = 1;
+		}
+	}
+	meta.tiocm.tiocm_old = msm_smux_tiocm_get_atomic(ch);
+	ch->remote_tiocm = pkt->hdr.flags;
+	meta.tiocm.tiocm_new = msm_smux_tiocm_get_atomic(ch);
+
+	/* client notification for status change */
+	if (IS_FULLY_OPENED(ch)) {
+		if (meta.tiocm.tiocm_old != meta.tiocm.tiocm_new)
+			schedule_notify(lcid, SMUX_TIOCM_UPDATE, &meta);
+		ret = 0;
+	}
+	spin_unlock_irqrestore(&ch->state_lock_lhb1, flags);
+	if (tx_ready)
+		list_channel(ch);
+
+	return ret;
+}
+
+/**
+ * Handle receive power command.
+ *
+ * @pkt  Received packet
+ *
+ * @returns 0 for success
+ *
+ * Called with rx_lock_lha1 already locked.
+ */
+static int smux_handle_rx_power_cmd(struct smux_pkt_t *pkt)
+{
+	int tx_ready = 0;
+	struct smux_pkt_t *ack_pkt = NULL;
+
+	spin_lock(&smux.tx_lock_lha2);
+	if (pkt->hdr.flags & SMUX_CMD_PWR_CTL_ACK) {
+		/* local sleep request ack */
+		if (smux.power_state == SMUX_PWR_TURNING_OFF) {
+			/* Power-down complete, turn off UART */
+			SMUX_DBG("%s: Power %d->%d\n", __func__,
+					smux.power_state, SMUX_PWR_OFF_FLUSH);
+			smux.power_state = SMUX_PWR_OFF_FLUSH;
+			queue_work(smux_tx_wq, &smux_inactivity_work);
+		} else {
+			pr_err("%s: sleep request ack invalid in state %d\n",
+					__func__, smux.power_state);
+		}
+	} else {
+		/* remote sleep request */
+		if (smux.power_state == SMUX_PWR_ON
+			|| smux.power_state == SMUX_PWR_TURNING_OFF) {
+			ack_pkt = smux_alloc_pkt();
+			if (ack_pkt) {
+				SMUX_DBG("%s: Power %d->%d\n", __func__,
+						smux.power_state,
+						SMUX_PWR_TURNING_OFF_FLUSH);
+
+				/* send power-down request */
+				ack_pkt->hdr.cmd = SMUX_CMD_PWR_CTL;
+				ack_pkt->hdr.flags = SMUX_CMD_PWR_CTL_ACK;
+				ack_pkt->hdr.lcid = pkt->hdr.lcid;
+				smux_tx_queue(ack_pkt,
+					      &smux_lch[ack_pkt->hdr.lcid], 0);
+				tx_ready = 1;
+				smux.power_state = SMUX_PWR_TURNING_OFF_FLUSH;
+				queue_delayed_work(smux_tx_wq,
+					&smux_delayed_inactivity_work,
+					msecs_to_jiffies(
+						SMUX_INACTIVITY_TIMEOUT_MS));
+			}
+		} else {
+			pr_err("%s: sleep request invalid in state %d\n",
+					__func__, smux.power_state);
+		}
+	}
+	spin_unlock(&smux.tx_lock_lha2);
+
+	if (tx_ready)
+		list_channel(&smux_lch[ack_pkt->hdr.lcid]);
+
+	return 0;
+}
+
+/**
+ * Handle dispatching a completed packet for receive processing.
+ *
+ * @pkt Packet to process
+ *
+ * @returns 0 for success
+ *
+ * Called with rx_lock_lha1 already locked.
+ */
+static int smux_dispatch_rx_pkt(struct smux_pkt_t *pkt)
+{
+	int ret;
+
+	SMUX_LOG_PKT_RX(pkt);
+
+	switch (pkt->hdr.cmd) {
+	case SMUX_CMD_OPEN_LCH:
+		ret = smux_handle_rx_open_cmd(pkt);
+		break;
+
+	case SMUX_CMD_DATA:
+		ret = smux_handle_rx_data_cmd(pkt);
+		break;
+
+	case SMUX_CMD_CLOSE_LCH:
+		ret = smux_handle_rx_close_cmd(pkt);
+		break;
+
+	case SMUX_CMD_STATUS:
+		ret = smux_handle_rx_status_cmd(pkt);
+		break;
+
+	case SMUX_CMD_PWR_CTL:
+		ret = smux_handle_rx_power_cmd(pkt);
+		break;
+
+	case SMUX_CMD_BYTE:
+		ret = smux_handle_rx_byte_cmd(pkt);
+		break;
+
+	default:
+		pr_err("%s: command %d unknown\n", __func__, pkt->hdr.cmd);
+		ret = -EINVAL;
+	}
+	return ret;
+}
+
+/**
+ * Deserializes a packet and dispatches it to the packet receive logic.
+ *
+ * @data    Raw data for one packet
+ * @len     Length of the data
+ *
+ * @returns 0 for success
+ *
+ * Called with rx_lock_lha1 already locked.
+ */
+static int smux_deserialize(unsigned char *data, int len)
+{
+	struct smux_pkt_t recv;
+	uint8_t lcid;
+
+	smux_init_pkt(&recv);
+
+	/*
+	 * It may be possible to optimize this to not use the
+	 * temporary buffer.
+	 */
+	memcpy(&recv.hdr, data, sizeof(struct smux_hdr_t));
+
+	if (recv.hdr.magic != SMUX_MAGIC) {
+		pr_err("%s: invalid header magic\n", __func__);
+		return -EINVAL;
+	}
+
+	lcid = recv.hdr.lcid;
+	if (smux_assert_lch_id(lcid)) {
+		pr_err("%s: invalid channel id %d\n", __func__, lcid);
+		return -ENXIO;
+	}
+
+	if (recv.hdr.payload_len)
+		recv.payload = data + sizeof(struct smux_hdr_t);
+
+	return smux_dispatch_rx_pkt(&recv);
+}
+
+/**
+ * Handle wakeup request byte.
+ *
+ * Called with rx_lock_lha1 already locked.
+ */
+static void smux_handle_wakeup_req(void)
+{
+	spin_lock(&smux.tx_lock_lha2);
+	if (smux.power_state == SMUX_PWR_OFF
+		|| smux.power_state == SMUX_PWR_TURNING_ON) {
+		/* wakeup system */
+		SMUX_DBG("%s: Power %d->%d\n", __func__,
+				smux.power_state, SMUX_PWR_ON);
+		smux.power_state = SMUX_PWR_ON;
+		queue_work(smux_tx_wq, &smux_wakeup_work);
+		queue_work(smux_tx_wq, &smux_tx_work);
+		queue_delayed_work(smux_tx_wq, &smux_delayed_inactivity_work,
+			msecs_to_jiffies(SMUX_INACTIVITY_TIMEOUT_MS));
+		smux_send_byte(SMUX_WAKEUP_ACK);
+	} else {
+		smux_send_byte(SMUX_WAKEUP_ACK);
+	}
+	spin_unlock(&smux.tx_lock_lha2);
+}
+
+/**
+ * Handle wakeup request ack.
+ *
+ * Called with rx_lock_lha1 already locked.
+ */
+static void smux_handle_wakeup_ack(void)
+{
+	spin_lock(&smux.tx_lock_lha2);
+	if (smux.power_state == SMUX_PWR_TURNING_ON) {
+		/* received response to wakeup request */
+		SMUX_DBG("%s: Power %d->%d\n", __func__,
+				smux.power_state, SMUX_PWR_ON);
+		smux.power_state = SMUX_PWR_ON;
+		queue_work(smux_tx_wq, &smux_tx_work);
+		queue_delayed_work(smux_tx_wq, &smux_delayed_inactivity_work,
+			msecs_to_jiffies(SMUX_INACTIVITY_TIMEOUT_MS));
+
+	} else if (smux.power_state != SMUX_PWR_ON) {
+		/* invalid message */
+		pr_err("%s: wakeup request ack invalid in state %d\n",
+				__func__, smux.power_state);
+	}
+	spin_unlock(&smux.tx_lock_lha2);
+}
+
+/**
+ * RX State machine - IDLE state processing.
+ *
+ * @data  New RX data to process
+ * @len   Length of the data
+ * @used  Return value of length processed
+ * @flag  Error flag - TTY_NORMAL 0 for no failure
+ *
+ * Called with rx_lock_lha1 locked.
+ */
+static void smux_rx_handle_idle(const unsigned char *data,
+		int len, int *used, int flag)
+{
+	int i;
+
+	if (flag) {
+		if (smux_byte_loopback)
+			smux_receive_byte(SMUX_UT_ECHO_ACK_FAIL,
+					smux_byte_loopback);
+		pr_err("%s: TTY error 0x%x - ignoring\n", __func__, flag);
+		++*used;
+		return;
+	}
+
+	for (i = *used; i < len && smux.rx_state == SMUX_RX_IDLE; i++) {
+		switch (data[i]) {
+		case SMUX_MAGIC_WORD1:
+			smux.rx_state = SMUX_RX_MAGIC;
+			break;
+		case SMUX_WAKEUP_REQ:
+			smux_handle_wakeup_req();
+			break;
+		case SMUX_WAKEUP_ACK:
+			smux_handle_wakeup_ack();
+			break;
+		default:
+			/* unexpected character */
+			if (smux_byte_loopback && data[i] == SMUX_UT_ECHO_REQ)
+				smux_receive_byte(SMUX_UT_ECHO_ACK_OK,
+						smux_byte_loopback);
+			pr_err("%s: parse error 0x%02x - ignoring\n", __func__,
+					(unsigned)data[i]);
+			break;
+		}
+	}
+
+	*used = i;
+}
+
+/**
+ * RX State machine - Header Magic state processing.
+ *
+ * @data  New RX data to process
+ * @len   Length of the data
+ * @used  Return value of length processed
+ * @flag  Error flag - TTY_NORMAL 0 for no failure
+ *
+ * Called with rx_lock_lha1 locked.
+ */
+static void smux_rx_handle_magic(const unsigned char *data,
+		int len, int *used, int flag)
+{
+	int i;
+
+	if (flag) {
+		pr_err("%s: TTY RX error %d\n", __func__, flag);
+		smux_enter_reset();
+		smux.rx_state = SMUX_RX_FAILURE;
+		++*used;
+		return;
+	}
+
+	for (i = *used; i < len && smux.rx_state == SMUX_RX_MAGIC; i++) {
+		/* wait for completion of the magic */
+		if (data[i] == SMUX_MAGIC_WORD2) {
+			smux.recv_len = 0;
+			smux.recv_buf[smux.recv_len++] = SMUX_MAGIC_WORD1;
+			smux.recv_buf[smux.recv_len++] = SMUX_MAGIC_WORD2;
+			smux.rx_state = SMUX_RX_HDR;
+		} else {
+			/* unexpected / trash character */
+			pr_err("%s: rx parse error for char %c; *used=%d, len=%d\n",
+					__func__, data[i], *used, len);
+			smux.rx_state = SMUX_RX_IDLE;
+		}
+	}
+
+	*used = i;
+}
+
+/**
+ * RX State machine - Packet Header state processing.
+ *
+ * @data  New RX data to process
+ * @len   Length of the data
+ * @used  Return value of length processed
+ * @flag  Error flag - TTY_NORMAL 0 for no failure
+ *
+ * Called with rx_lock_lha1 locked.
+ */
+static void smux_rx_handle_hdr(const unsigned char *data,
+		int len, int *used, int flag)
+{
+	int i;
+	struct smux_hdr_t *hdr;
+
+	if (flag) {
+		pr_err("%s: TTY RX error %d\n", __func__, flag);
+		smux_enter_reset();
+		smux.rx_state = SMUX_RX_FAILURE;
+		++*used;
+		return;
+	}
+
+	for (i = *used; i < len && smux.rx_state == SMUX_RX_HDR; i++) {
+		smux.recv_buf[smux.recv_len++] = data[i];
+
+		if (smux.recv_len == sizeof(struct smux_hdr_t)) {
+			/* complete header received */
+			hdr = (struct smux_hdr_t *)smux.recv_buf;
+			smux.pkt_remain = hdr->payload_len + hdr->pad_len;
+			smux.rx_state = SMUX_RX_PAYLOAD;
+		}
+	}
+	*used = i;
+}
+
+/**
+ * RX State machine - Packet Payload state processing.
+ *
+ * @data  New RX data to process
+ * @len   Length of the data
+ * @used  Return value of length processed
+ * @flag  Error flag - TTY_NORMAL 0 for no failure
+ *
+ * Called with rx_lock_lha1 locked.
+ */
+static void smux_rx_handle_pkt_payload(const unsigned char *data,
+		int len, int *used, int flag)
+{
+	int remaining;
+
+	if (flag) {
+		pr_err("%s: TTY RX error %d\n", __func__, flag);
+		smux_enter_reset();
+		smux.rx_state = SMUX_RX_FAILURE;
+		++*used;
+		return;
+	}
+
+	/* copy data into rx buffer */
+	if (smux.pkt_remain < (len - *used))
+		remaining = smux.pkt_remain;
+	else
+		remaining = len - *used;
+
+	memcpy(&smux.recv_buf[smux.recv_len], &data[*used], remaining);
+	smux.recv_len += remaining;
+	smux.pkt_remain -= remaining;
+	*used += remaining;
+
+	if (smux.pkt_remain == 0) {
+		/* complete packet received */
+		smux_deserialize(smux.recv_buf, smux.recv_len);
+		smux.rx_state = SMUX_RX_IDLE;
+	}
+}
+
+/**
+ * Feed data to the receive state machine.
+ *
+ * @data Pointer to data block
+ * @len  Length of data
+ * @flag TTY_NORMAL (0) for no error, otherwise TTY Error Flag
+ *
+ * Called with rx_lock_lha1 locked.
+ */
+void smux_rx_state_machine(const unsigned char *data,
+						int len, int flag)
+{
+	unsigned long flags;
+	int used;
+	int initial_rx_state;
+
+
+	SMUX_DBG("%s: %p, len=%d, flag=%d\n", __func__, data, len, flag);
+	spin_lock_irqsave(&smux.rx_lock_lha1, flags);
+	used = 0;
+	smux.rx_activity_flag = 1;
+	do {
+		SMUX_DBG("%s: state %d; %d of %d\n",
+				__func__, smux.rx_state, used, len);
+		initial_rx_state = smux.rx_state;
+
+		switch (smux.rx_state) {
+		case SMUX_RX_IDLE:
+			smux_rx_handle_idle(data, len, &used, flag);
+			break;
+		case SMUX_RX_MAGIC:
+			smux_rx_handle_magic(data, len, &used, flag);
+			break;
+		case SMUX_RX_HDR:
+			smux_rx_handle_hdr(data, len, &used, flag);
+			break;
+		case SMUX_RX_PAYLOAD:
+			smux_rx_handle_pkt_payload(data, len, &used, flag);
+			break;
+		default:
+			SMUX_DBG("%s: invalid state %d\n",
+					__func__, smux.rx_state);
+			smux.rx_state = SMUX_RX_IDLE;
+			break;
+		}
+	} while (used < len || smux.rx_state != initial_rx_state);
+	spin_unlock_irqrestore(&smux.rx_lock_lha1, flags);
+}
+
+/**
+ * Add channel to transmit-ready list and trigger transmit worker.
+ *
+ * @ch Channel to add
+ */
+static void list_channel(struct smux_lch_t *ch)
+{
+	unsigned long flags;
+
+	SMUX_DBG("%s: listing channel %d\n",
+			__func__, ch->lcid);
+
+	spin_lock_irqsave(&smux.tx_lock_lha2, flags);
+	spin_lock(&ch->tx_lock_lhb2);
+	smux.tx_activity_flag = 1;
+	if (list_empty(&ch->tx_ready_list))
+		list_add_tail(&ch->tx_ready_list, &smux.lch_tx_ready_list);
+	spin_unlock(&ch->tx_lock_lhb2);
+	spin_unlock_irqrestore(&smux.tx_lock_lha2, flags);
+
+	queue_work(smux_tx_wq, &smux_tx_work);
+}
+
+/**
+ * Transmit packet on correct transport and then perform client
+ * notification.
+ *
+ * @ch   Channel to transmit on
+ * @pkt  Packet to transmit
+ */
+static void smux_tx_pkt(struct smux_lch_t *ch, struct smux_pkt_t *pkt)
+{
+	union notifier_metadata meta_write;
+	int ret;
+
+	if (ch && pkt) {
+		SMUX_LOG_PKT_TX(pkt);
+		if (ch->local_mode == SMUX_LCH_MODE_LOCAL_LOOPBACK)
+			ret = smux_tx_loopback(pkt);
+		else
+			ret = smux_tx_tty(pkt);
+
+		if (pkt->hdr.cmd == SMUX_CMD_DATA) {
+			/* notify write-done */
+			meta_write.write.pkt_priv = pkt->priv;
+			meta_write.write.buffer = pkt->payload;
+			meta_write.write.len = pkt->hdr.payload_len;
+			if (ret >= 0) {
+				SMUX_DBG("%s: PKT write done", __func__);
+				schedule_notify(ch->lcid, SMUX_WRITE_DONE,
+						&meta_write);
+			} else {
+				pr_err("%s: failed to write pkt %d\n",
+						__func__, ret);
+				schedule_notify(ch->lcid, SMUX_WRITE_FAIL,
+						&meta_write);
+			}
+		}
+	}
+}
+
+/**
+ * Power-up the UART.
+ */
+static void smux_uart_power_on(void)
+{
+	struct uart_state *state;
+
+	if (!smux.tty || !smux.tty->driver_data) {
+		pr_err("%s: unable to find UART port for tty %p\n",
+				__func__, smux.tty);
+		return;
+	}
+	state = smux.tty->driver_data;
+	msm_hs_request_clock_on(state->uart_port);
+}
+
+/**
+ * Power down the UART.
+ */
+static void smux_uart_power_off(void)
+{
+	struct uart_state *state;
+
+	if (!smux.tty || !smux.tty->driver_data) {
+		pr_err("%s: unable to find UART port for tty %p\n",
+				__func__, smux.tty);
+		return;
+	}
+	state = smux.tty->driver_data;
+	msm_hs_request_clock_off(state->uart_port);
+}
+
+/**
+ * TX Wakeup Worker
+ *
+ * @work Not used
+ *
+ * Do an exponential back-off wakeup sequence with a maximum period
+ * of approximately 1 second (1 << 20 microseconds).
+ */
+static void smux_wakeup_worker(struct work_struct *work)
+{
+	unsigned long flags;
+	unsigned wakeup_delay;
+	int complete = 0;
+
+	for (;;) {
+		spin_lock_irqsave(&smux.tx_lock_lha2, flags);
+		if (smux.power_state == SMUX_PWR_ON) {
+			/* wakeup complete */
+			complete = 1;
+			spin_unlock_irqrestore(&smux.tx_lock_lha2, flags);
+			break;
+		} else {
+			/* retry */
+			wakeup_delay = smux.pwr_wakeup_delay_us;
+			smux.pwr_wakeup_delay_us <<= 1;
+			if (smux.pwr_wakeup_delay_us > SMUX_WAKEUP_DELAY_MAX)
+				smux.pwr_wakeup_delay_us =
+					SMUX_WAKEUP_DELAY_MAX;
+		}
+		spin_unlock_irqrestore(&smux.tx_lock_lha2, flags);
+		SMUX_DBG("%s: triggering wakeup\n", __func__);
+		smux_send_byte(SMUX_WAKEUP_REQ);
+
+		if (wakeup_delay < SMUX_WAKEUP_DELAY_MIN) {
+			SMUX_DBG("%s: sleeping for %u us\n", __func__,
+					wakeup_delay);
+			usleep_range(wakeup_delay, 2*wakeup_delay);
+		} else {
+			/* schedule delayed work */
+			SMUX_DBG("%s: scheduling delayed wakeup in %u ms\n",
+					__func__, wakeup_delay / 1000);
+			queue_delayed_work(smux_tx_wq,
+					&smux_wakeup_delayed_work,
+					msecs_to_jiffies(wakeup_delay / 1000));
+			break;
+		}
+	}
+
+	if (complete) {
+		SMUX_DBG("%s: wakeup complete\n", __func__);
+		/*
+		 * Cancel any pending retry.  This avoids a race condition with
+		 * a new power-up request because:
+		 * 1) this worker doesn't modify the state
+		 * 2) this worker is processed on the same single-threaded
+		 *    workqueue as new TX wakeup requests
+		 */
+		cancel_delayed_work(&smux_wakeup_delayed_work);
+	}
+}
+
+
+/**
+ * Inactivity timeout worker.  Periodically scheduled when link is active.
+ * When it detects inactivity, it will power-down the UART link.
+ *
+ * @work  Work structure (not used)
+ */
+static void smux_inactivity_worker(struct work_struct *work)
+{
+	int tx_ready = 0;
+	struct smux_pkt_t *pkt;
+	unsigned long flags;
+
+	spin_lock_irqsave(&smux.rx_lock_lha1, flags);
+	spin_lock(&smux.tx_lock_lha2);
+
+	if (!smux.tx_activity_flag && !smux.rx_activity_flag) {
+		/* no activity */
+		if (smux.powerdown_enabled) {
+			if (smux.power_state == SMUX_PWR_ON) {
+				/* start power-down sequence */
+				pkt = smux_alloc_pkt();
+				if (pkt) {
+					SMUX_DBG("%s: Power %d->%d\n", __func__,
+						smux.power_state,
+						SMUX_PWR_TURNING_OFF);
+					smux.power_state = SMUX_PWR_TURNING_OFF;
+
+					/* send power-down request */
+					pkt->hdr.cmd = SMUX_CMD_PWR_CTL;
+					pkt->hdr.flags = 0;
+					pkt->hdr.lcid = 0;
+					smux_tx_queue(pkt,
+						&smux_lch[SMUX_TEST_LCID],
+						0);
+					tx_ready = 1;
+				}
+			}
+		} else {
+			SMUX_DBG("%s: link inactive, but powerdown disabled\n",
+					__func__);
+		}
+	}
+	smux.tx_activity_flag = 0;
+	smux.rx_activity_flag = 0;
+
+	spin_unlock(&smux.tx_lock_lha2);
+	spin_unlock_irqrestore(&smux.rx_lock_lha1, flags);
+
+	if (tx_ready)
+		list_channel(&smux_lch[SMUX_TEST_LCID]);
+
+	if ((smux.power_state == SMUX_PWR_OFF_FLUSH) ||
+	    (smux.power_state == SMUX_PWR_TURNING_OFF_FLUSH)) {
+		/* ready to power-down the UART */
+		SMUX_DBG("%s: Power %d->%d\n", __func__,
+				smux.power_state, SMUX_PWR_OFF);
+		smux_uart_power_off();
+		spin_lock_irqsave(&smux.tx_lock_lha2, flags);
+		smux.power_state = SMUX_PWR_OFF;
+		spin_unlock_irqrestore(&smux.tx_lock_lha2, flags);
+	}
+
+	/* reschedule inactivity worker */
+	if (smux.power_state != SMUX_PWR_OFF)
+		queue_delayed_work(smux_tx_wq, &smux_delayed_inactivity_work,
+			msecs_to_jiffies(SMUX_INACTIVITY_TIMEOUT_MS));
+}
+
+/**
+ * Transmit worker handles serializing and transmitting packets onto the
+ * underlying transport.
+ *
+ * @work  Work structure (not used)
+ */
+static void smux_tx_worker(struct work_struct *work)
+{
+	struct smux_pkt_t *pkt;
+	struct smux_lch_t *ch;
+	unsigned low_wm_notif;
+	unsigned lcid;
+	unsigned long flags;
+
+
+	/*
+	 * Transmit packets in round-robin fashion based upon ready
+	 * channels.
+	 *
+	 * To eliminate the need to hold a lock for the entire
+	 * iteration through the channel ready list, the head of the
+	 * ready-channel list is always the next channel to be
+	 * processed.  To send a packet, the first valid packet in
+	 * the head channel is removed and the head channel is then
+	 * rescheduled at the end of the queue by removing it and
+	 * inserting after the tail.  The locks can then be released
+	 * while the packet is processed.
+	 */
+	for (;;) {
+		pkt = NULL;
+		low_wm_notif = 0;
+
+		/* get the next ready channel */
+		spin_lock_irqsave(&smux.tx_lock_lha2, flags);
+		if (list_empty(&smux.lch_tx_ready_list)) {
+			/* no ready channels */
+			SMUX_DBG("%s: no more ready channels, exiting\n",
+					__func__);
+			spin_unlock_irqrestore(&smux.tx_lock_lha2, flags);
+			break;
+		}
+		smux.tx_activity_flag = 1;
+
+		if (smux.power_state != SMUX_PWR_ON
+			&& smux.power_state != SMUX_PWR_TURNING_OFF
+			&& smux.power_state != SMUX_PWR_TURNING_OFF_FLUSH) {
+			/* Link isn't ready to transmit */
+			if (smux.power_state == SMUX_PWR_OFF) {
+				/* link is off, trigger wakeup */
+				smux.pwr_wakeup_delay_us = 1;
+				SMUX_DBG("%s: Power %d->%d\n", __func__,
+						smux.power_state,
+						SMUX_PWR_TURNING_ON);
+				smux.power_state = SMUX_PWR_TURNING_ON;
+				spin_unlock_irqrestore(&smux.tx_lock_lha2,
+						flags);
+				smux_uart_power_on();
+				queue_work(smux_tx_wq, &smux_wakeup_work);
+			} else {
+				SMUX_DBG("%s: can not tx with power state %d\n",
+						__func__,
+						smux.power_state);
+				spin_unlock_irqrestore(&smux.tx_lock_lha2,
+						flags);
+			}
+			break;
+		}
+
+		/* get the next packet to send and rotate channel list */
+		ch = list_first_entry(&smux.lch_tx_ready_list,
+						struct smux_lch_t,
+						tx_ready_list);
+
+		spin_lock(&ch->state_lock_lhb1);
+		spin_lock(&ch->tx_lock_lhb2);
+		if (!list_empty(&ch->tx_queue)) {
+			/*
+			 * If remote TX flow control is enabled or
+			 * the channel is not fully opened, then only
+			 * send command packets.
+			 */
+			if (ch->tx_flow_control || !IS_FULLY_OPENED(ch)) {
+				struct smux_pkt_t *curr;
+				list_for_each_entry(curr, &ch->tx_queue, list) {
+					if (curr->hdr.cmd != SMUX_CMD_DATA) {
+						pkt = curr;
+						break;
+					}
+				}
+			} else {
+				/* get next cmd/data packet to send */
+				pkt = list_first_entry(&ch->tx_queue,
+						struct smux_pkt_t, list);
+			}
+		}
+
+		if (pkt) {
+			list_del(&pkt->list);
+
+			/* update packet stats */
+			if (pkt->hdr.cmd == SMUX_CMD_DATA) {
+				--ch->tx_pending_data_cnt;
+				if (ch->notify_lwm &&
+					ch->tx_pending_data_cnt
+						<= SMUX_WM_LOW) {
+					ch->notify_lwm = 0;
+					low_wm_notif = 1;
+				}
+			}
+
+			/* advance to the next ready channel */
+			list_rotate_left(&smux.lch_tx_ready_list);
+		} else {
+			/* no data in channel to send, remove from ready list */
+			list_del(&ch->tx_ready_list);
+			INIT_LIST_HEAD(&ch->tx_ready_list);
+		}
+		lcid = ch->lcid;
+		spin_unlock(&ch->tx_lock_lhb2);
+		spin_unlock(&ch->state_lock_lhb1);
+		spin_unlock_irqrestore(&smux.tx_lock_lha2, flags);
+
+		if (low_wm_notif)
+			schedule_notify(lcid, SMUX_LOW_WM_HIT, NULL);
+
+		/* send the packet */
+		smux_tx_pkt(ch, pkt);
+		smux_free_pkt(pkt);
+	}
+}
+
+
+/**********************************************************************/
+/* Kernel API                                                         */
+/**********************************************************************/
+
+/**
+ * Set or clear channel option using the SMUX_CH_OPTION_* channel
+ * flags.
+ *
+ * @lcid   Logical channel ID
+ * @set    Options to set
+ * @clear  Options to clear
+ *
+ * @returns 0 for success, < 0 for failure
+ */
+int msm_smux_set_ch_option(uint8_t lcid, uint32_t set, uint32_t clear)
+{
+	unsigned long flags;
+	struct smux_lch_t *ch;
+	int tx_ready = 0;
+	int ret = 0;
+
+	if (smux_assert_lch_id(lcid))
+		return -ENXIO;
+
+	ch = &smux_lch[lcid];
+	spin_lock_irqsave(&ch->state_lock_lhb1, flags);
+
+	/* Local loopback mode */
+	if (set & SMUX_CH_OPTION_LOCAL_LOOPBACK)
+		ch->local_mode = SMUX_LCH_MODE_LOCAL_LOOPBACK;
+
+	if (clear & SMUX_CH_OPTION_LOCAL_LOOPBACK)
+		ch->local_mode = SMUX_LCH_MODE_NORMAL;
+
+	/* Remote loopback mode */
+	if (set & SMUX_CH_OPTION_REMOTE_LOOPBACK)
+		ch->local_mode = SMUX_LCH_MODE_REMOTE_LOOPBACK;
+
+	if (clear & SMUX_CH_OPTION_REMOTE_LOOPBACK)
+		ch->local_mode = SMUX_LCH_MODE_NORMAL;
+
+	/* Flow control */
+	if (set & SMUX_CH_OPTION_REMOTE_TX_STOP) {
+		ch->local_tiocm |= SMUX_CMD_STATUS_FLOW_CNTL;
+		ret = smux_send_status_cmd(ch);
+		tx_ready = 1;
+	}
+
+	if (clear & SMUX_CH_OPTION_REMOTE_TX_STOP) {
+		ch->local_tiocm &= ~SMUX_CMD_STATUS_FLOW_CNTL;
+		ret = smux_send_status_cmd(ch);
+		tx_ready = 1;
+	}
+
+	spin_unlock_irqrestore(&ch->state_lock_lhb1, flags);
+
+	if (tx_ready)
+		list_channel(ch);
+
+	return ret;
+}
+
+/**
+ * Starts the opening sequence for a logical channel.
+ *
+ * @lcid          Logical channel ID
+ * @priv          Free for client usage
+ * @notify        Event notification function
+ * @get_rx_buffer Function used to provide a receive buffer to SMUX
+ *
+ * @returns 0 for success, <0 otherwise
+ *
+ * A channel must be fully closed (either not previously opened or
+ * msm_smux_close() has been called and the SMUX_DISCONNECTED has been
+ * received.
+ *
+ * One the remote side is opened, the client will receive a SMUX_CONNECTED
+ * event.
+ */
+int msm_smux_open(uint8_t lcid, void *priv,
+	void (*notify)(void *priv, int event_type, const void *metadata),
+	int (*get_rx_buffer)(void *priv, void **pkt_priv, void **buffer,
+								int size))
+{
+	int ret;
+	struct smux_lch_t *ch;
+	struct smux_pkt_t *pkt;
+	int tx_ready = 0;
+	unsigned long flags;
+
+	if (smux_assert_lch_id(lcid))
+		return -ENXIO;
+
+	ch = &smux_lch[lcid];
+	spin_lock_irqsave(&ch->state_lock_lhb1, flags);
+
+	if (ch->local_state == SMUX_LCH_LOCAL_CLOSING) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
+	if (ch->local_state != SMUX_LCH_LOCAL_CLOSED) {
+		pr_err("%s: open lcid %d local state %x invalid\n",
+				__func__, lcid, ch->local_state);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	SMUX_DBG("lcid %d local state 0x%x -> 0x%x\n", lcid,
+			ch->local_state,
+			SMUX_LCH_LOCAL_OPENING);
+
+	ch->local_state = SMUX_LCH_LOCAL_OPENING;
+
+	ch->priv = priv;
+	ch->notify = notify;
+	ch->get_rx_buffer = get_rx_buffer;
+	ret = 0;
+
+	/* Send Open Command */
+	pkt = smux_alloc_pkt();
+	if (!pkt) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	pkt->hdr.magic = SMUX_MAGIC;
+	pkt->hdr.cmd = SMUX_CMD_OPEN_LCH;
+	pkt->hdr.flags = SMUX_CMD_OPEN_POWER_COLLAPSE;
+	if (ch->local_mode == SMUX_LCH_MODE_REMOTE_LOOPBACK)
+		pkt->hdr.flags |= SMUX_CMD_OPEN_REMOTE_LOOPBACK;
+	pkt->hdr.lcid = lcid;
+	pkt->hdr.payload_len = 0;
+	pkt->hdr.pad_len = 0;
+	smux_tx_queue(pkt, ch, 0);
+	tx_ready = 1;
+
+out:
+	spin_unlock_irqrestore(&ch->state_lock_lhb1, flags);
+	if (tx_ready)
+		list_channel(ch);
+	return ret;
+}
+
+/**
+ * Starts the closing sequence for a logical channel.
+ *
+ * @lcid    Logical channel ID
+ *
+ * @returns 0 for success, <0 otherwise
+ *
+ * Once the close event has been acknowledge by the remote side, the client
+ * will receive a SMUX_DISCONNECTED notification.
+ */
+int msm_smux_close(uint8_t lcid)
+{
+	int ret = 0;
+	struct smux_lch_t *ch;
+	struct smux_pkt_t *pkt;
+	int tx_ready = 0;
+	unsigned long flags;
+
+	if (smux_assert_lch_id(lcid))
+		return -ENXIO;
+
+	ch = &smux_lch[lcid];
+	spin_lock_irqsave(&ch->state_lock_lhb1, flags);
+	ch->local_tiocm = 0x0;
+	ch->remote_tiocm = 0x0;
+	ch->tx_pending_data_cnt = 0;
+	ch->notify_lwm = 0;
+
+	/* Purge TX queue */
+	spin_lock(&ch->tx_lock_lhb2);
+	while (!list_empty(&ch->tx_queue)) {
+		pkt = list_first_entry(&ch->tx_queue, struct smux_pkt_t,
+							list);
+		list_del(&pkt->list);
+
+		if (pkt->hdr.cmd == SMUX_CMD_OPEN_LCH) {
+			/* Open was never sent, just force to closed state */
+			union notifier_metadata meta_disconnected;
+
+			ch->local_state = SMUX_LCH_LOCAL_CLOSED;
+			meta_disconnected.disconnected.is_ssr = 0;
+			schedule_notify(lcid, SMUX_DISCONNECTED,
+				&meta_disconnected);
+		} else if (pkt->hdr.cmd == SMUX_CMD_DATA) {
+			/* Notify client of failed write */
+			union notifier_metadata meta_write;
+
+			meta_write.write.pkt_priv = pkt->priv;
+			meta_write.write.buffer = pkt->payload;
+			meta_write.write.len = pkt->hdr.payload_len;
+			schedule_notify(ch->lcid, SMUX_WRITE_FAIL, &meta_write);
+		}
+		smux_free_pkt(pkt);
+	}
+	spin_unlock(&ch->tx_lock_lhb2);
+
+	/* Send Close Command */
+	if (ch->local_state == SMUX_LCH_LOCAL_OPENED ||
+		ch->local_state == SMUX_LCH_LOCAL_OPENING) {
+		SMUX_DBG("lcid %d local state 0x%x -> 0x%x\n", lcid,
+				ch->local_state,
+				SMUX_LCH_LOCAL_CLOSING);
+
+		ch->local_state = SMUX_LCH_LOCAL_CLOSING;
+		pkt = smux_alloc_pkt();
+		if (pkt) {
+			pkt->hdr.cmd = SMUX_CMD_CLOSE_LCH;
+			pkt->hdr.flags = 0;
+			pkt->hdr.lcid = lcid;
+			pkt->hdr.payload_len = 0;
+			pkt->hdr.pad_len = 0;
+			smux_tx_queue(pkt, ch, 0);
+			tx_ready = 1;
+		} else {
+			pr_err("%s: pkt allocation failed\n", __func__);
+			ret = -ENOMEM;
+		}
+	}
+	spin_unlock_irqrestore(&ch->state_lock_lhb1, flags);
+
+	if (tx_ready)
+		list_channel(ch);
+
+	return ret;
+}
+
+/**
+ * Write data to a logical channel.
+ *
+ * @lcid      Logical channel ID
+ * @pkt_priv  Client data that will be returned with the SMUX_WRITE_DONE or
+ *            SMUX_WRITE_FAIL notification.
+ * @data      Data to write
+ * @len       Length of @data
+ *
+ * @returns   0 for success, <0 otherwise
+ *
+ * Data may be written immediately after msm_smux_open() is called,
+ * but the data will wait in the transmit queue until the channel has
+ * been fully opened.
+ *
+ * Once the data has been written, the client will receive either a completion
+ * (SMUX_WRITE_DONE) or a failure notice (SMUX_WRITE_FAIL).
+ */
+int msm_smux_write(uint8_t lcid, void *pkt_priv, const void *data, int len)
+{
+	struct smux_lch_t *ch;
+	struct smux_pkt_t *pkt;
+	int tx_ready = 0;
+	unsigned long flags;
+	int ret;
+
+	if (smux_assert_lch_id(lcid))
+		return -ENXIO;
+
+	ch = &smux_lch[lcid];
+	spin_lock_irqsave(&ch->state_lock_lhb1, flags);
+
+	if (ch->local_state != SMUX_LCH_LOCAL_OPENED &&
+		ch->local_state != SMUX_LCH_LOCAL_OPENING) {
+		pr_err("%s: hdr.invalid local state %d channel %d\n",
+					__func__, ch->local_state, lcid);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (len > SMUX_MAX_PKT_SIZE - sizeof(struct smux_hdr_t)) {
+		pr_err("%s: payload %d too large\n",
+				__func__, len);
+		ret = -E2BIG;
+		goto out;
+	}
+
+	pkt = smux_alloc_pkt();
+	if (!pkt) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	pkt->hdr.cmd = SMUX_CMD_DATA;
+	pkt->hdr.lcid = lcid;
+	pkt->hdr.flags = 0;
+	pkt->hdr.payload_len = len;
+	pkt->payload = (void *)data;
+	pkt->priv = pkt_priv;
+	pkt->hdr.pad_len = 0;
+
+	spin_lock(&ch->tx_lock_lhb2);
+	/* verify high watermark */
+	SMUX_DBG("%s: pending %d", __func__, ch->tx_pending_data_cnt);
+
+	if (ch->tx_pending_data_cnt >= SMUX_WM_HIGH) {
+		pr_err("%s: ch %d high watermark %d exceeded %d\n",
+				__func__, lcid, SMUX_WM_HIGH,
+				ch->tx_pending_data_cnt);
+		ret = -EAGAIN;
+		goto out_inner;
+	}
+
+	/* queue packet for transmit */
+	if (++ch->tx_pending_data_cnt == SMUX_WM_HIGH) {
+		ch->notify_lwm = 1;
+		pr_err("%s: high watermark hit\n", __func__);
+		schedule_notify(lcid, SMUX_HIGH_WM_HIT, NULL);
+	}
+	list_add_tail(&pkt->list, &ch->tx_queue);
+
+	/* add to ready list */
+	if (IS_FULLY_OPENED(ch))
+		tx_ready = 1;
+
+	ret = 0;
+
+out_inner:
+	spin_unlock(&ch->tx_lock_lhb2);
+
+out:
+	if (ret)
+		smux_free_pkt(pkt);
+	spin_unlock_irqrestore(&ch->state_lock_lhb1, flags);
+
+	if (tx_ready)
+		list_channel(ch);
+
+	return ret;
+}
+
+/**
+ * Returns true if the TX queue is currently full (high water mark).
+ *
+ * @lcid      Logical channel ID
+ * @returns   0 if channel is not full
+ *            1 if it is full
+ *            < 0 for error
+ */
+int msm_smux_is_ch_full(uint8_t lcid)
+{
+	struct smux_lch_t *ch;
+	unsigned long flags;
+	int is_full = 0;
+
+	if (smux_assert_lch_id(lcid))
+		return -ENXIO;
+
+	ch = &smux_lch[lcid];
+
+	spin_lock_irqsave(&ch->tx_lock_lhb2, flags);
+	if (ch->tx_pending_data_cnt >= SMUX_WM_HIGH)
+		is_full = 1;
+	spin_unlock_irqrestore(&ch->tx_lock_lhb2, flags);
+
+	return is_full;
+}
+
+/**
+ * Returns true if the TX queue has space for more packets it is at or
+ * below the low water mark).
+ *
+ * @lcid      Logical channel ID
+ * @returns   0 if channel is above low watermark
+ *            1 if it's at or below the low watermark
+ *            < 0 for error
+ */
+int msm_smux_is_ch_low(uint8_t lcid)
+{
+	struct smux_lch_t *ch;
+	unsigned long flags;
+	int is_low = 0;
+
+	if (smux_assert_lch_id(lcid))
+		return -ENXIO;
+
+	ch = &smux_lch[lcid];
+
+	spin_lock_irqsave(&ch->tx_lock_lhb2, flags);
+	if (ch->tx_pending_data_cnt <= SMUX_WM_LOW)
+		is_low = 1;
+	spin_unlock_irqrestore(&ch->tx_lock_lhb2, flags);
+
+	return is_low;
+}
+
+/**
+ * Send TIOCM status update.
+ *
+ * @ch  Channel for update
+ *
+ * @returns 0 for success, <0 for failure
+ *
+ * Channel lock must be held before calling.
+ */
+static int smux_send_status_cmd(struct smux_lch_t *ch)
+{
+	struct smux_pkt_t *pkt;
+
+	if (!ch)
+		return -EINVAL;
+
+	pkt = smux_alloc_pkt();
+	if (!pkt)
+		return -ENOMEM;
+
+	pkt->hdr.lcid = ch->lcid;
+	pkt->hdr.cmd = SMUX_CMD_STATUS;
+	pkt->hdr.flags = ch->local_tiocm;
+	pkt->hdr.payload_len = 0;
+	pkt->hdr.pad_len = 0;
+	smux_tx_queue(pkt, ch, 0);
+
+	return 0;
+}
+
+/**
+ * Internal helper function for getting the TIOCM status with
+ * state_lock_lhb1 already locked.
+ *
+ * @ch      Channel pointer
+ *
+ * @returns TIOCM status
+ */
+static long msm_smux_tiocm_get_atomic(struct smux_lch_t *ch)
+{
+	long status = 0x0;
+
+	status |= (ch->remote_tiocm & SMUX_CMD_STATUS_RTC) ? TIOCM_DSR : 0;
+	status |= (ch->remote_tiocm & SMUX_CMD_STATUS_RTR) ? TIOCM_CTS : 0;
+	status |= (ch->remote_tiocm & SMUX_CMD_STATUS_RI) ? TIOCM_RI : 0;
+	status |= (ch->remote_tiocm & SMUX_CMD_STATUS_DCD) ? TIOCM_CD : 0;
+
+	status |= (ch->local_tiocm & SMUX_CMD_STATUS_RTC) ? TIOCM_DTR : 0;
+	status |= (ch->local_tiocm & SMUX_CMD_STATUS_RTR) ? TIOCM_RTS : 0;
+
+	return status;
+}
+
+/**
+ * Get the TIOCM status bits.
+ *
+ * @lcid      Logical channel ID
+ *
+ * @returns   >= 0 TIOCM status bits
+ *            < 0  Error condition
+ */
+long msm_smux_tiocm_get(uint8_t lcid)
+{
+	struct smux_lch_t *ch;
+	unsigned long flags;
+	long status = 0x0;
+
+	if (smux_assert_lch_id(lcid))
+		return -ENXIO;
+
+	ch = &smux_lch[lcid];
+	spin_lock_irqsave(&ch->state_lock_lhb1, flags);
+	status = msm_smux_tiocm_get_atomic(ch);
+	spin_unlock_irqrestore(&ch->state_lock_lhb1, flags);
+
+	return status;
+}
+
+/**
+ * Set/clear the TIOCM status bits.
+ *
+ * @lcid      Logical channel ID
+ * @set       Bits to set
+ * @clear     Bits to clear
+ *
+ * @returns   0 for success; < 0 for failure
+ *
+ * If a bit is specified in both the @set and @clear masks, then the clear bit
+ * definition will dominate and the bit will be cleared.
+ */
+int msm_smux_tiocm_set(uint8_t lcid, uint32_t set, uint32_t clear)
+{
+	struct smux_lch_t *ch;
+	unsigned long flags;
+	uint8_t old_status;
+	uint8_t status_set = 0x0;
+	uint8_t status_clear = 0x0;
+	int tx_ready = 0;
+	int ret = 0;
+
+	if (smux_assert_lch_id(lcid))
+		return -ENXIO;
+
+	ch = &smux_lch[lcid];
+	spin_lock_irqsave(&ch->state_lock_lhb1, flags);
+
+	status_set |= (set & TIOCM_DTR) ? SMUX_CMD_STATUS_RTC : 0;
+	status_set |= (set & TIOCM_RTS) ? SMUX_CMD_STATUS_RTR : 0;
+	status_set |= (set & TIOCM_RI) ? SMUX_CMD_STATUS_RI : 0;
+	status_set |= (set & TIOCM_CD) ? SMUX_CMD_STATUS_DCD : 0;
+
+	status_clear |= (clear & TIOCM_DTR) ? SMUX_CMD_STATUS_RTC : 0;
+	status_clear |= (clear & TIOCM_RTS) ? SMUX_CMD_STATUS_RTR : 0;
+	status_clear |= (clear & TIOCM_RI) ? SMUX_CMD_STATUS_RI : 0;
+	status_clear |= (clear & TIOCM_CD) ? SMUX_CMD_STATUS_DCD : 0;
+
+	old_status = ch->local_tiocm;
+	ch->local_tiocm |= status_set;
+	ch->local_tiocm &= ~status_clear;
+
+	if (ch->local_tiocm != old_status) {
+		ret = smux_send_status_cmd(ch);
+		tx_ready = 1;
+	}
+	spin_unlock_irqrestore(&ch->state_lock_lhb1, flags);
+
+	if (tx_ready)
+		list_channel(ch);
+
+	return ret;
+}
+
+/**********************************************************************/
+/* Line Discipline Interface                                          */
+/**********************************************************************/
+static int smuxld_open(struct tty_struct *tty)
+{
+	int i;
+	int tmp;
+	unsigned long flags;
+
+	if (!smux.is_initialized)
+		return -ENODEV;
+
+	spin_lock_irqsave(&smux.lock_lha0, flags);
+	if (smux.ld_open_count) {
+		pr_err("%s: %p multiple instances not supported\n",
+			__func__, tty);
+		spin_unlock_irqrestore(&smux.lock_lha0, flags);
+		return -EEXIST;
+	}
+
+	++smux.ld_open_count;
+	if (tty->ops->write == NULL) {
+		spin_unlock_irqrestore(&smux.lock_lha0, flags);
+		return -EINVAL;
+	}
+
+	/* connect to TTY */
+	smux.tty = tty;
+	tty->disc_data = &smux;
+	tty->receive_room = TTY_RECEIVE_ROOM;
+	tty_driver_flush_buffer(tty);
+
+	/* power-down the UART if we are idle */
+	spin_lock(&smux.tx_lock_lha2);
+	if (smux.power_state == SMUX_PWR_OFF) {
+		SMUX_DBG("%s: powering off uart\n", __func__);
+		smux.power_state = SMUX_PWR_OFF_FLUSH;
+		spin_unlock(&smux.tx_lock_lha2);
+		queue_work(smux_tx_wq, &smux_inactivity_work);
+	} else {
+		spin_unlock(&smux.tx_lock_lha2);
+	}
+	spin_unlock_irqrestore(&smux.lock_lha0, flags);
+
+	/* register platform devices */
+	for (i = 0; i < ARRAY_SIZE(smux_devs); ++i) {
+		tmp = platform_device_register(&smux_devs[i]);
+		if (tmp)
+			pr_err("%s: error %d registering device %s\n",
+				   __func__, tmp, smux_devs[i].name);
+	}
+	return 0;
+}
+
+static void smuxld_close(struct tty_struct *tty)
+{
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&smux.lock_lha0, flags);
+	if (smux.ld_open_count <= 0) {
+		pr_err("%s: invalid ld count %d\n", __func__,
+			smux.ld_open_count);
+		spin_unlock_irqrestore(&smux.lock_lha0, flags);
+		return;
+	}
+	spin_unlock_irqrestore(&smux.lock_lha0, flags);
+
+	for (i = 0; i < ARRAY_SIZE(smux_devs); ++i)
+		platform_device_unregister(&smux_devs[i]);
+
+	--smux.ld_open_count;
+}
+
+/**
+ * Receive data from TTY Line Discipline.
+ *
+ * @tty  TTY structure
+ * @cp   Character data
+ * @fp   Flag data
+ * @count Size of character and flag data
+ */
+void smuxld_receive_buf(struct tty_struct *tty, const unsigned char *cp,
+			   char *fp, int count)
+{
+	int i;
+	int last_idx = 0;
+	const char *tty_name = NULL;
+	char *f;
+
+	if (smux_debug_mask & MSM_SMUX_DEBUG)
+		print_hex_dump(KERN_INFO, "smux tty rx: ", DUMP_PREFIX_OFFSET,
+				     16, 1, cp, count, true);
+
+	/* verify error flags */
+	for (i = 0, f = fp; i < count; ++i, ++f) {
+		if (*f != TTY_NORMAL) {
+			if (tty)
+				tty_name = tty->name;
+			pr_err("%s: TTY %s Error %d (%s)\n", __func__,
+				   tty_name, *f, tty_flag_to_str(*f));
+
+			/* feed all previous valid data to the parser */
+			smux_rx_state_machine(cp + last_idx, i - last_idx,
+					TTY_NORMAL);
+
+			/* feed bad data to parser */
+			smux_rx_state_machine(cp + i, 1, *f);
+			last_idx = i + 1;
+		}
+	}
+
+	/* feed data to RX state machine */
+	smux_rx_state_machine(cp + last_idx, count - last_idx, TTY_NORMAL);
+}
+
+static void smuxld_flush_buffer(struct tty_struct *tty)
+{
+	pr_err("%s: not supported\n", __func__);
+}
+
+static ssize_t	smuxld_chars_in_buffer(struct tty_struct *tty)
+{
+	pr_err("%s: not supported\n", __func__);
+	return -ENODEV;
+}
+
+static ssize_t	smuxld_read(struct tty_struct *tty, struct file *file,
+		unsigned char __user *buf, size_t nr)
+{
+	pr_err("%s: not supported\n", __func__);
+	return -ENODEV;
+}
+
+static ssize_t	smuxld_write(struct tty_struct *tty, struct file *file,
+		 const unsigned char *buf, size_t nr)
+{
+	pr_err("%s: not supported\n", __func__);
+	return -ENODEV;
+}
+
+static int	smuxld_ioctl(struct tty_struct *tty, struct file *file,
+		 unsigned int cmd, unsigned long arg)
+{
+	pr_err("%s: not supported\n", __func__);
+	return -ENODEV;
+}
+
+static unsigned int smuxld_poll(struct tty_struct *tty, struct file *file,
+			 struct poll_table_struct *tbl)
+{
+	pr_err("%s: not supported\n", __func__);
+	return -ENODEV;
+}
+
+static void smuxld_write_wakeup(struct tty_struct *tty)
+{
+	pr_err("%s: not supported\n", __func__);
+}
+
+static struct tty_ldisc_ops smux_ldisc_ops = {
+	.owner           = THIS_MODULE,
+	.magic           = TTY_LDISC_MAGIC,
+	.name            = "n_smux",
+	.open            = smuxld_open,
+	.close           = smuxld_close,
+	.flush_buffer    = smuxld_flush_buffer,
+	.chars_in_buffer = smuxld_chars_in_buffer,
+	.read            = smuxld_read,
+	.write           = smuxld_write,
+	.ioctl           = smuxld_ioctl,
+	.poll            = smuxld_poll,
+	.receive_buf     = smuxld_receive_buf,
+	.write_wakeup    = smuxld_write_wakeup
+};
+
+static int __init smux_init(void)
+{
+	int ret;
+
+	spin_lock_init(&smux.lock_lha0);
+
+	spin_lock_init(&smux.rx_lock_lha1);
+	smux.rx_state = SMUX_RX_IDLE;
+	smux.power_state = SMUX_PWR_OFF;
+	smux.pwr_wakeup_delay_us = 1;
+	smux.powerdown_enabled = 0;
+	smux.rx_activity_flag = 0;
+	smux.tx_activity_flag = 0;
+	smux.recv_len = 0;
+	smux.tty = NULL;
+	smux.ld_open_count = 0;
+	smux.in_reset = 0;
+	smux.is_initialized = 1;
+	smux_byte_loopback = 0;
+
+	spin_lock_init(&smux.tx_lock_lha2);
+	INIT_LIST_HEAD(&smux.lch_tx_ready_list);
+
+	ret	= tty_register_ldisc(N_SMUX, &smux_ldisc_ops);
+	if (ret != 0) {
+		pr_err("%s: error %d registering line discipline\n",
+				__func__, ret);
+		return ret;
+	}
+
+	ret = lch_init();
+	if (ret != 0) {
+		pr_err("%s: lch_init failed\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void __exit smux_exit(void)
+{
+	int ret;
+
+	ret	= tty_unregister_ldisc(N_SMUX);
+	if (ret != 0) {
+		pr_err("%s error %d unregistering line discipline\n",
+				__func__, ret);
+		return;
+	}
+}
+
+module_init(smux_init);
+module_exit(smux_exit);
+
+MODULE_DESCRIPTION("Serial Mux TTY Line Discipline");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS_LDISC(N_SMUX);
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 070b442..9274c17 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -988,6 +988,44 @@
 	  Choose M here to compile it as a module. The module will be
 	  called msm_serial_hs.
 
+config SERIAL_MSM_CLOCK_CONTROL
+	bool "Allow tty clients to make clock requests to msm uarts."
+	depends on SERIAL_MSM=y
+	default y
+	help
+	 Provides an interface for tty clients to request the msm uart clock
+	 to be turned on or off for power savings.
+
+config SERIAL_MSM_RX_WAKEUP
+	bool "Wakeup the msm uart clock on GPIO activity."
+	depends on SERIAL_MSM_CLOCK_CONTROL
+	default n
+	help
+	 Requires SERIAL_MSM_CLOCK_CONTROL. Wake up the uart while the uart
+	 clock is off, using a wakeup GPIO.
+
+config SERIAL_MSM_HSL
+	tristate "MSM UART High Speed : Legacy mode Serial Driver"
+	depends on ARM && ARCH_MSM
+	select SERIAL_CORE
+	default n
+	help
+	  Select this module to enable MSM high speed UART driver.
+
+config SERIAL_MSM_HSL_CONSOLE
+	bool "MSM High speed serial legacy mode console support"
+	depends on SERIAL_MSM_HSL=y
+	select SERIAL_CORE_CONSOLE
+	default n
+
+config SERIAL_BCM_BT_LPM
+	tristate "Broadcom Bluetooth Low Power Mode"
+	depends on ARM && ARCH_MSM
+	select SERIAL_CORE
+	default n
+	help
+	  Select this module for Broadcom Bluetooth low power management.
+
 config SERIAL_VT8500
 	bool "VIA VT8500 on-chip serial port support"
 	depends on ARM && ARCH_VT8500
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 7257c5d..4376e10 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -56,6 +56,8 @@
 obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o
 obj-$(CONFIG_SERIAL_MSM) += msm_serial.o
 obj-$(CONFIG_SERIAL_MSM_HS) += msm_serial_hs.o
+obj-$(CONFIG_SERIAL_MSM_HSL) += msm_serial_hs_lite.o
+obj-$(CONFIG_MSM_SERIAL_DEBUGGER) += msm_serial_debugger.o
 obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
 obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o
 obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL) += nwpserial.o
diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
index 8131e2c..72bc8de 100644
--- a/drivers/tty/serial/msm_serial.c
+++ b/drivers/tty/serial/msm_serial.c
@@ -1,9 +1,9 @@
 /*
- * Driver for msm7k serial device and console
+ * drivers/serial/msm_serial.c - driver for msm7k serial device and console
  *
  * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
  * Author: Robert Love <rlove@google.com>
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -19,44 +19,85 @@
 # define SUPPORT_SYSRQ
 #endif
 
-#include <linux/atomic.h>
 #include <linux/hrtimer.h>
 #include <linux/module.h>
 #include <linux/io.h>
 #include <linux/ioport.h>
 #include <linux/irq.h>
 #include <linux/init.h>
+#include <linux/delay.h>
 #include <linux/console.h>
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
 #include <linux/serial_core.h>
 #include <linux/serial.h>
+#include <linux/nmi.h>
 #include <linux/clk.h>
 #include <linux/platform_device.h>
-#include <linux/delay.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-
+#include <linux/pm_runtime.h>
+#include <mach/msm_serial_pdata.h>
 #include "msm_serial.h"
 
+
+#ifdef CONFIG_SERIAL_MSM_CLOCK_CONTROL
+enum msm_clk_states_e {
+	MSM_CLK_PORT_OFF,     /* uart port not in use */
+	MSM_CLK_OFF,          /* clock enabled */
+	MSM_CLK_REQUEST_OFF,  /* disable after TX flushed */
+	MSM_CLK_ON,           /* clock disabled */
+};
+#endif
+
+#ifdef CONFIG_SERIAL_MSM_RX_WAKEUP
+/* optional low power wakeup, typically on a GPIO RX irq */
+struct msm_wakeup {
+	int irq;  /* < 0 indicates low power wakeup disabled */
+	unsigned char ignore;  /* bool */
+
+	/* bool: inject char into rx tty on wakeup */
+	unsigned char inject_rx;
+	char rx_to_inject;
+};
+#endif
+
 struct msm_port {
 	struct uart_port	uart;
 	char			name[16];
 	struct clk		*clk;
-	struct clk		*pclk;
 	unsigned int		imr;
-	unsigned int            *gsbi_base;
-	int			is_uartdm;
-	unsigned int		old_snap_state;
+#ifdef CONFIG_SERIAL_MSM_CLOCK_CONTROL
+	enum msm_clk_states_e	clk_state;
+	struct hrtimer		clk_off_timer;
+	ktime_t			clk_off_delay;
+#endif
+#ifdef CONFIG_SERIAL_MSM_RX_WAKEUP
+	struct msm_wakeup wakeup;
+#endif
 };
 
-static inline void wait_for_xmitr(struct uart_port *port, int bits)
+#define UART_TO_MSM(uart_port)	((struct msm_port *) uart_port)
+#define is_console(port)	((port)->cons && \
+				(port)->cons->index == (port)->line)
+
+
+static inline void msm_write(struct uart_port *port, unsigned int val,
+			     unsigned int off)
 {
-	if (!(msm_read(port, UART_SR) & UART_SR_TX_EMPTY))
-		while ((msm_read(port, UART_ISR) & bits) != bits)
-			cpu_relax();
+	__raw_writel(val, port->membase + off);
 }
 
+static inline unsigned int msm_read(struct uart_port *port, unsigned int off)
+{
+	return __raw_readl(port->membase + off);
+}
+
+#ifdef CONFIG_SERIAL_MSM_RX_WAKEUP
+static inline unsigned int use_low_power_wakeup(struct msm_port *msm_port)
+{
+	return (msm_port->wakeup.irq >= 0);
+}
+#endif
+
 static void msm_stop_tx(struct uart_port *port)
 {
 	struct msm_port *msm_port = UART_TO_MSM(port);
@@ -89,61 +130,123 @@
 	msm_write(port, msm_port->imr, UART_IMR);
 }
 
-static void handle_rx_dm(struct uart_port *port, unsigned int misr)
-{
-	struct tty_struct *tty = port->state->port.tty;
-	unsigned int sr;
-	int count = 0;
+#ifdef CONFIG_SERIAL_MSM_CLOCK_CONTROL
+/* turn clock off if TX buffer is empty, otherwise reschedule */
+static enum hrtimer_restart msm_serial_clock_off(struct hrtimer *timer) {
+	struct msm_port *msm_port = container_of(timer, struct msm_port,
+						 clk_off_timer);
+	struct uart_port *port = &msm_port->uart;
+	struct circ_buf *xmit = &port->state->xmit;
+	unsigned long flags;
+	int ret = HRTIMER_NORESTART;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	if (msm_port->clk_state == MSM_CLK_REQUEST_OFF) {
+		if (uart_circ_empty(xmit)) {
+			struct msm_port *msm_port = UART_TO_MSM(port);
+			clk_disable(msm_port->clk);
+			msm_port->clk_state = MSM_CLK_OFF;
+#ifdef CONFIG_SERIAL_MSM_RX_WAKEUP
+			if (use_low_power_wakeup(msm_port)) {
+				msm_port->wakeup.ignore = 1;
+				enable_irq(msm_port->wakeup.irq);
+			}
+#endif
+		} else {
+			hrtimer_forward_now(timer, msm_port->clk_off_delay);
+			ret = HRTIMER_RESTART;
+		}
+	}
+
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	return HRTIMER_NORESTART;
+}
+
+/* request to turn off uart clock once pending TX is flushed */
+void msm_serial_clock_request_off(struct uart_port *port) {
+	unsigned long flags;
 	struct msm_port *msm_port = UART_TO_MSM(port);
 
-	if ((msm_read(port, UART_SR) & UART_SR_OVERRUN)) {
-		port->icount.overrun++;
-		tty_insert_flip_char(tty, 0, TTY_OVERRUN);
-		msm_write(port, UART_CR_CMD_RESET_ERR, UART_CR);
+	spin_lock_irqsave(&port->lock, flags);
+	if (msm_port->clk_state == MSM_CLK_ON) {
+		msm_port->clk_state = MSM_CLK_REQUEST_OFF;
+		/* turn off TX later. unfortunately not all msm uart's have a
+		 * TXDONE available, and TXLEV does not wait until completely
+		 * flushed, so a timer is our only option
+		 */
+		hrtimer_start(&msm_port->clk_off_timer,
+			      msm_port->clk_off_delay, HRTIMER_MODE_REL);
 	}
-
-	if (misr & UART_IMR_RXSTALE) {
-		count = msm_read(port, UARTDM_RX_TOTAL_SNAP) -
-			msm_port->old_snap_state;
-		msm_port->old_snap_state = 0;
-	} else {
-		count = 4 * (msm_read(port, UART_RFWR));
-		msm_port->old_snap_state += count;
-	}
-
-	/* TODO: Precise error reporting */
-
-	port->icount.rx += count;
-
-	while (count > 0) {
-		unsigned int c;
-
-		sr = msm_read(port, UART_SR);
-		if ((sr & UART_SR_RX_READY) == 0) {
-			msm_port->old_snap_state -= count;
-			break;
-		}
-		c = msm_read(port, UARTDM_RF);
-		if (sr & UART_SR_RX_BREAK) {
-			port->icount.brk++;
-			if (uart_handle_break(port))
-				continue;
-		} else if (sr & UART_SR_PAR_FRAME_ERR)
-			port->icount.frame++;
-
-		/* TODO: handle sysrq */
-		tty_insert_flip_string(tty, (char *) &c,
-				       (count > 4) ? 4 : count);
-		count -= 4;
-	}
-
-	tty_flip_buffer_push(tty);
-	if (misr & (UART_IMR_RXSTALE))
-		msm_write(port, UART_CR_CMD_RESET_STALE_INT, UART_CR);
-	msm_write(port, 0xFFFFFF, UARTDM_DMRX);
-	msm_write(port, UART_CR_CMD_STALE_EVENT_ENABLE, UART_CR);
+	spin_unlock_irqrestore(&port->lock, flags);
 }
 
+/* request to immediately turn on uart clock.
+ * ignored if there is a pending off request, unless force = 1.
+ */
+void msm_serial_clock_on(struct uart_port *port, int force) {
+	unsigned long flags;
+	struct msm_port *msm_port = UART_TO_MSM(port);
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	switch (msm_port->clk_state) {
+	case MSM_CLK_OFF:
+		clk_enable(msm_port->clk);
+#ifdef CONFIG_SERIAL_MSM_RX_WAKEUP
+		if (use_low_power_wakeup(msm_port))
+			disable_irq(msm_port->wakeup.irq);
+#endif
+		force = 1;
+	case MSM_CLK_REQUEST_OFF:
+		if (force) {
+			hrtimer_try_to_cancel(&msm_port->clk_off_timer);
+			msm_port->clk_state = MSM_CLK_ON;
+		}
+		break;
+	case MSM_CLK_ON: break;
+	case MSM_CLK_PORT_OFF: break;
+	}
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+#endif
+
+#ifdef CONFIG_SERIAL_MSM_RX_WAKEUP
+static irqreturn_t msm_rx_irq(int irq, void *dev_id)
+{
+	unsigned long flags;
+	struct uart_port *port = dev_id;
+	struct msm_port *msm_port = UART_TO_MSM(port);
+	int inject_wakeup = 0;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	if (msm_port->clk_state == MSM_CLK_OFF) {
+		/* ignore the first irq - it is a pending irq that occured
+		 * before enable_irq() */
+		if (msm_port->wakeup.ignore)
+			msm_port->wakeup.ignore = 0;
+		else
+			inject_wakeup = 1;
+	}
+
+	msm_serial_clock_on(port, 0);
+
+	/* we missed an rx while asleep - it must be a wakeup indicator
+	 */
+	if (inject_wakeup) {
+		struct tty_struct *tty = port->state->port.tty;
+		tty_insert_flip_char(tty, WAKE_UP_IND, TTY_NORMAL);
+		tty_flip_buffer_push(tty);
+	}
+
+	spin_unlock_irqrestore(&port->lock, flags);
+	return IRQ_HANDLED;
+}
+#endif
+
 static void handle_rx(struct uart_port *port)
 {
 	struct tty_struct *tty = port->state->port.tty;
@@ -192,12 +295,6 @@
 	tty_flip_buffer_push(tty);
 }
 
-static void reset_dm_count(struct uart_port *port)
-{
-	wait_for_xmitr(port, UART_ISR_TX_READY);
-	msm_write(port, 1, UARTDM_NCF_TX);
-}
-
 static void handle_tx(struct uart_port *port)
 {
 	struct circ_buf *xmit = &port->state->xmit;
@@ -205,18 +302,11 @@
 	int sent_tx;
 
 	if (port->x_char) {
-		if (msm_port->is_uartdm)
-			reset_dm_count(port);
-
-		msm_write(port, port->x_char,
-			  msm_port->is_uartdm ? UARTDM_TF : UART_TF);
+		msm_write(port, port->x_char, UART_TF);
 		port->icount.tx++;
 		port->x_char = 0;
 	}
 
-	if (msm_port->is_uartdm)
-		reset_dm_count(port);
-
 	while (msm_read(port, UART_SR) & UART_SR_TX_READY) {
 		if (uart_circ_empty(xmit)) {
 			/* disable tx interrupts */
@@ -224,17 +314,22 @@
 			msm_write(port, msm_port->imr, UART_IMR);
 			break;
 		}
-		msm_write(port, xmit->buf[xmit->tail],
-			  msm_port->is_uartdm ? UARTDM_TF : UART_TF);
 
-		if (msm_port->is_uartdm)
-			reset_dm_count(port);
+		msm_write(port, xmit->buf[xmit->tail], UART_TF);
 
 		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
 		port->icount.tx++;
 		sent_tx = 1;
 	}
 
+#ifdef CONFIG_SERIAL_MSM_CLOCK_CONTROL
+	if (sent_tx && msm_port->clk_state == MSM_CLK_REQUEST_OFF)
+		/* new TX - restart the timer */
+		if (hrtimer_try_to_cancel(&msm_port->clk_off_timer) == 1)
+			hrtimer_start(&msm_port->clk_off_timer,
+				msm_port->clk_off_delay, HRTIMER_MODE_REL);
+#endif
+
 	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
 		uart_write_wakeup(port);
 }
@@ -248,34 +343,34 @@
 
 static irqreturn_t msm_irq(int irq, void *dev_id)
 {
+	unsigned long flags;
 	struct uart_port *port = dev_id;
 	struct msm_port *msm_port = UART_TO_MSM(port);
 	unsigned int misr;
 
-	spin_lock(&port->lock);
+	spin_lock_irqsave(&port->lock, flags);
 	misr = msm_read(port, UART_MISR);
 	msm_write(port, 0, UART_IMR); /* disable interrupt */
 
-	if (misr & (UART_IMR_RXLEV | UART_IMR_RXSTALE)) {
-		if (msm_port->is_uartdm)
-			handle_rx_dm(port, misr);
-		else
-			handle_rx(port);
-	}
+	if (misr & (UART_IMR_RXLEV | UART_IMR_RXSTALE))
+		handle_rx(port);
 	if (misr & UART_IMR_TXLEV)
 		handle_tx(port);
 	if (misr & UART_IMR_DELTA_CTS)
 		handle_delta_cts(port);
 
 	msm_write(port, msm_port->imr, UART_IMR); /* restore interrupt */
-	spin_unlock(&port->lock);
+	spin_unlock_irqrestore(&port->lock, flags);
 
 	return IRQ_HANDLED;
 }
 
 static unsigned int msm_tx_empty(struct uart_port *port)
 {
-	return (msm_read(port, UART_SR) & UART_SR_TX_EMPTY) ? TIOCSER_TEMT : 0;
+	unsigned int ret;
+
+	ret = (msm_read(port, UART_SR) & UART_SR_TX_EMPTY) ? TIOCSER_TEMT : 0;
+	return ret;
 }
 
 static unsigned int msm_get_mctrl(struct uart_port *port)
@@ -283,21 +378,10 @@
 	return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR | TIOCM_RTS;
 }
 
-
-static void msm_reset(struct uart_port *port)
-{
-	/* reset everything */
-	msm_write(port, UART_CR_CMD_RESET_RX, UART_CR);
-	msm_write(port, UART_CR_CMD_RESET_TX, UART_CR);
-	msm_write(port, UART_CR_CMD_RESET_ERR, UART_CR);
-	msm_write(port, UART_CR_CMD_RESET_BREAK_INT, UART_CR);
-	msm_write(port, UART_CR_CMD_RESET_CTS, UART_CR);
-	msm_write(port, UART_CR_CMD_SET_RFR, UART_CR);
-}
-
-void msm_set_mctrl(struct uart_port *port, unsigned int mctrl)
+static void msm_set_mctrl(struct uart_port *port, unsigned int mctrl)
 {
 	unsigned int mr;
+
 	mr = msm_read(port, UART_MR1);
 
 	if (!(mctrl & TIOCM_RTS)) {
@@ -318,10 +402,9 @@
 		msm_write(port, UART_CR_CMD_STOP_BREAK, UART_CR);
 }
 
-static int msm_set_baud_rate(struct uart_port *port, unsigned int baud)
+static void msm_set_baud_rate(struct uart_port *port, unsigned int baud)
 {
 	unsigned int baud_code, rxstale, watermark;
-	struct msm_port *msm_port = UART_TO_MSM(port);
 
 	switch (baud) {
 	case 300:
@@ -371,14 +454,10 @@
 	case 115200:
 	default:
 		baud_code = UART_CSR_115200;
-		baud = 115200;
 		rxstale = 31;
 		break;
 	}
 
-	if (msm_port->is_uartdm)
-		msm_write(port, UART_CR_CMD_RESET_RX, UART_CR);
-
 	msm_write(port, baud_code, UART_CSR);
 
 	/* RX stale watermark */
@@ -393,27 +472,62 @@
 
 	/* set TX watermark */
 	msm_write(port, 10, UART_TFWR);
-
-	if (msm_port->is_uartdm) {
-		msm_write(port, UART_CR_CMD_RESET_STALE_INT, UART_CR);
-		msm_write(port, 0xFFFFFF, UARTDM_DMRX);
-		msm_write(port, UART_CR_CMD_STALE_EVENT_ENABLE, UART_CR);
-	}
-
-	return baud;
 }
 
+static void msm_reset(struct uart_port *port)
+{
+	/* reset everything */
+	msm_write(port, UART_CR_CMD_RESET_RX, UART_CR);
+	msm_write(port, UART_CR_CMD_RESET_TX, UART_CR);
+	msm_write(port, UART_CR_CMD_RESET_ERR, UART_CR);
+	msm_write(port, UART_CR_CMD_RESET_BREAK_INT, UART_CR);
+	msm_write(port, UART_CR_CMD_RESET_CTS, UART_CR);
+	msm_write(port, UART_CR_CMD_SET_RFR, UART_CR);
+}
 
 static void msm_init_clock(struct uart_port *port)
 {
+	int ret;
 	struct msm_port *msm_port = UART_TO_MSM(port);
 
-	clk_enable(msm_port->clk);
-	if (!IS_ERR(msm_port->pclk))
-		clk_enable(msm_port->pclk);
-	msm_serial_set_mnd_regs(port);
+	ret = clk_prepare_enable(msm_port->clk);
+	if (ret) {
+		pr_err("%s(): Can't enable uartclk. ret:%d\n", __func__, ret);
+		return;
+	}
+
+#ifdef CONFIG_SERIAL_MSM_CLOCK_CONTROL
+	msm_port->clk_state = MSM_CLK_ON;
+#endif
+
+	if (port->uartclk == 19200000) {
+		/* clock is TCXO (19.2MHz) */
+		msm_write(port, 0x06, UART_MREG);
+		msm_write(port, 0xF1, UART_NREG);
+		msm_write(port, 0x0F, UART_DREG);
+		msm_write(port, 0x1A, UART_MNDREG);
+	} else {
+		/* clock must be TCXO/4 */
+		msm_write(port, 0x18, UART_MREG);
+		msm_write(port, 0xF6, UART_NREG);
+		msm_write(port, 0x0F, UART_DREG);
+		msm_write(port, 0x0A, UART_MNDREG);
+	}
 }
 
+static void msm_deinit_clock(struct uart_port *port)
+{
+	struct msm_port *msm_port = UART_TO_MSM(port);
+
+#ifdef CONFIG_SERIAL_MSM_CLOCK_CONTROL
+	if (msm_port->clk_state != MSM_CLK_OFF)
+		clk_disable(msm_port->clk);
+	msm_port->clk_state = MSM_CLK_PORT_OFF;
+#else
+	clk_disable_unprepare(msm_port->clk);
+#endif
+
+}
 static int msm_startup(struct uart_port *port)
 {
 	struct msm_port *msm_port = UART_TO_MSM(port);
@@ -428,7 +542,15 @@
 	if (unlikely(ret))
 		return ret;
 
+	if (unlikely(irq_set_irq_wake(port->irq, 1))) {
+		free_irq(port->irq, port);
+		return -ENXIO;
+	}
+
+#ifndef CONFIG_PM_RUNTIME
 	msm_init_clock(port);
+#endif
+	pm_runtime_get_sync(port->dev);
 
 	if (likely(port->fifosize > 12))
 		rfr_level = port->fifosize - 12;
@@ -451,31 +573,29 @@
 		msm_write(port, data, UART_IPR);
 	}
 
-	data = 0;
-	if (!port->cons || (port->cons && !(port->cons->flags & CON_ENABLED))) {
-		msm_write(port, UART_CR_CMD_PROTECTION_EN, UART_CR);
-		msm_reset(port);
-		data = UART_CR_TX_ENABLE;
-	}
+	msm_reset(port);
 
-	data |= UART_CR_RX_ENABLE;
-	msm_write(port, data, UART_CR);	/* enable TX & RX */
-
-	/* Make sure IPR is not 0 to start with*/
-	if (msm_port->is_uartdm)
-		msm_write(port, UART_IPR_STALE_LSB, UART_IPR);
+	msm_write(port, 0x05, UART_CR);	/* enable TX & RX */
 
 	/* turn on RX and CTS interrupts */
 	msm_port->imr = UART_IMR_RXLEV | UART_IMR_RXSTALE |
 			UART_IMR_CURRENT_CTS;
-
-	if (msm_port->is_uartdm) {
-		msm_write(port, 0xFFFFFF, UARTDM_DMRX);
-		msm_write(port, UART_CR_CMD_RESET_STALE_INT, UART_CR);
-		msm_write(port, UART_CR_CMD_STALE_EVENT_ENABLE, UART_CR);
-	}
-
 	msm_write(port, msm_port->imr, UART_IMR);
+
+#ifdef CONFIG_SERIAL_MSM_RX_WAKEUP
+	if (use_low_power_wakeup(msm_port)) {
+		ret = irq_set_irq_wake(msm_port->wakeup.irq, 1);
+		if (unlikely(ret))
+			return ret;
+		ret = request_irq(msm_port->wakeup.irq, msm_rx_irq,
+				  IRQF_TRIGGER_FALLING,
+				  "msm_serial_wakeup", msm_port);
+		if (unlikely(ret))
+			return ret;
+		disable_irq(msm_port->wakeup.irq);
+	}
+#endif
+
 	return 0;
 }
 
@@ -486,9 +606,18 @@
 	msm_port->imr = 0;
 	msm_write(port, 0, UART_IMR); /* disable interrupts */
 
-	clk_disable(msm_port->clk);
-
 	free_irq(port->irq, port);
+
+#ifdef CONFIG_SERIAL_MSM_RX_WAKEUP
+	if (use_low_power_wakeup(msm_port)) {
+		irq_set_irq_wake(msm_port->wakeup.irq, 0);
+		free_irq(msm_port->wakeup.irq, msm_port);
+	}
+#endif
+#ifndef CONFIG_PM_RUNTIME
+	msm_deinit_clock(port);
+#endif
+	pm_runtime_put_sync(port->dev);
 }
 
 static void msm_set_termios(struct uart_port *port, struct ktermios *termios,
@@ -497,13 +626,14 @@
 	unsigned long flags;
 	unsigned int baud, mr;
 
+	if (!termios->c_cflag)
+		return;
+
 	spin_lock_irqsave(&port->lock, flags);
 
 	/* calculate and set baud rate */
 	baud = uart_get_baud_rate(port, termios, old, 300, 115200);
-	baud = msm_set_baud_rate(port, baud);
-	if (tty_termios_baud_rate(termios))
-		tty_termios_encode_baud_rate(termios, baud, baud);
+	msm_set_baud_rate(port, baud);
 
 	/* calculate parity */
 	mr = msm_read(port, UART_MR2);
@@ -562,7 +692,6 @@
 		port->read_status_mask |= UART_SR_RX_BREAK;
 
 	uart_update_timeout(port, termios->c_cflag, baud);
-
 	spin_unlock_irqrestore(&port->lock, flags);
 }
 
@@ -574,102 +703,48 @@
 static void msm_release_port(struct uart_port *port)
 {
 	struct platform_device *pdev = to_platform_device(port->dev);
-	struct msm_port *msm_port = UART_TO_MSM(port);
-	struct resource *uart_resource;
-	struct resource *gsbi_resource;
+	struct resource *resource;
 	resource_size_t size;
 
-	uart_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (unlikely(!uart_resource))
+	resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (unlikely(!resource))
 		return;
-	size = resource_size(uart_resource);
+	size = resource->end - resource->start + 1;
 
 	release_mem_region(port->mapbase, size);
 	iounmap(port->membase);
 	port->membase = NULL;
-
-	if (msm_port->gsbi_base) {
-		iowrite32(GSBI_PROTOCOL_IDLE, msm_port->gsbi_base +
-			  GSBI_CONTROL);
-
-		gsbi_resource = platform_get_resource(pdev,
-							IORESOURCE_MEM, 1);
-
-		if (unlikely(!gsbi_resource))
-			return;
-
-		size = resource_size(gsbi_resource);
-		release_mem_region(gsbi_resource->start, size);
-		iounmap(msm_port->gsbi_base);
-		msm_port->gsbi_base = NULL;
-	}
 }
 
 static int msm_request_port(struct uart_port *port)
 {
-	struct msm_port *msm_port = UART_TO_MSM(port);
 	struct platform_device *pdev = to_platform_device(port->dev);
-	struct resource *uart_resource;
-	struct resource *gsbi_resource;
+	struct resource *resource;
 	resource_size_t size;
-	int ret;
 
-	uart_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (unlikely(!uart_resource))
+	resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (unlikely(!resource))
 		return -ENXIO;
+	size = resource->end - resource->start + 1;
 
-	size = resource_size(uart_resource);
-
-	if (!request_mem_region(port->mapbase, size, "msm_serial"))
+	if (unlikely(!request_mem_region(port->mapbase, size, "msm_serial")))
 		return -EBUSY;
 
 	port->membase = ioremap(port->mapbase, size);
 	if (!port->membase) {
-		ret = -EBUSY;
-		goto fail_release_port;
-	}
-
-	gsbi_resource = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-	/* Is this a GSBI-based port? */
-	if (gsbi_resource) {
-		size = resource_size(gsbi_resource);
-
-		if (!request_mem_region(gsbi_resource->start, size,
-						 "msm_serial")) {
-			ret = -EBUSY;
-			goto fail_release_port;
-		}
-
-		msm_port->gsbi_base = ioremap(gsbi_resource->start, size);
-		if (!msm_port->gsbi_base) {
-			ret = -EBUSY;
-			goto fail_release_gsbi;
-		}
+		release_mem_region(port->mapbase, size);
+		return -EBUSY;
 	}
 
 	return 0;
-
-fail_release_gsbi:
-	release_mem_region(gsbi_resource->start, size);
-fail_release_port:
-	release_mem_region(port->mapbase, size);
-	return ret;
 }
 
 static void msm_config_port(struct uart_port *port, int flags)
 {
-	struct msm_port *msm_port = UART_TO_MSM(port);
-	int ret;
 	if (flags & UART_CONFIG_TYPE) {
 		port->type = PORT_MSM;
-		ret = msm_request_port(port);
-		if (ret)
-			return;
+		msm_request_port(port);
 	}
-
-	if (msm_port->is_uartdm)
-		iowrite32(GSBI_PROTOCOL_UART, msm_port->gsbi_base +
-			  GSBI_CONTROL);
 }
 
 static int msm_verify_port(struct uart_port *port, struct serial_struct *ser)
@@ -684,21 +759,22 @@
 static void msm_power(struct uart_port *port, unsigned int state,
 		      unsigned int oldstate)
 {
+	int ret;
 	struct msm_port *msm_port = UART_TO_MSM(port);
 
 	switch (state) {
 	case 0:
-		clk_enable(msm_port->clk);
-		if (!IS_ERR(msm_port->pclk))
-			clk_enable(msm_port->pclk);
+		ret = clk_prepare_enable(msm_port->clk);
+		if (ret)
+			pr_err("msm_serial: %s(): Can't enable uartclk.\n",
+						__func__);
 		break;
 	case 3:
-		clk_disable(msm_port->clk);
-		if (!IS_ERR(msm_port->pclk))
-			clk_disable(msm_port->pclk);
+		clk_disable_unprepare(msm_port->clk);
 		break;
 	default:
-		printk(KERN_ERR "msm_serial: Unknown PM state %d\n", state);
+		pr_err("msm_serial: %s(): Unknown PM state %d\n",
+						__func__, state);
 	}
 }
 
@@ -728,7 +804,7 @@
 			.iotype = UPIO_MEM,
 			.ops = &msm_uart_pops,
 			.flags = UPF_BOOT_AUTOCONF,
-			.fifosize = 64,
+			.fifosize = 512,
 			.line = 0,
 		},
 	},
@@ -737,7 +813,7 @@
 			.iotype = UPIO_MEM,
 			.ops = &msm_uart_pops,
 			.flags = UPF_BOOT_AUTOCONF,
-			.fifosize = 64,
+			.fifosize = 512,
 			.line = 1,
 		},
 	},
@@ -754,23 +830,56 @@
 
 #define UART_NR	ARRAY_SIZE(msm_uart_ports)
 
-static inline struct uart_port *get_port_from_line(unsigned int line)
+static inline struct uart_port * get_port_from_line(unsigned int line)
 {
 	return &msm_uart_ports[line].uart;
 }
 
 #ifdef CONFIG_SERIAL_MSM_CONSOLE
 
+/*
+ *  Wait for transmitter & holding register to empty
+ *  Derived from wait_for_xmitr in 8250 serial driver by Russell King
+ */
+static inline void wait_for_xmitr(struct uart_port *port, int bits)
+{
+	unsigned int status, mr, tmout = 10000;
+
+	/* Wait up to 10ms for the character(s) to be sent. */
+	do {
+		status = msm_read(port, UART_SR);
+
+		if (--tmout == 0)
+			break;
+		udelay(1);
+	} while ((status & bits) != bits);
+
+	mr = msm_read(port, UART_MR1);
+
+	/* Wait up to 1s for flow control if necessary */
+	if (mr & UART_MR1_CTS_CTL) {
+		unsigned int tmout;
+		for (tmout = 1000000; tmout; tmout--) {
+			unsigned int isr = msm_read(port, UART_ISR);
+
+			/* CTS input is active lo */
+			if (!(isr & UART_IMR_CURRENT_CTS))
+				break;
+			udelay(1);
+			touch_nmi_watchdog();
+		}
+	}
+}
+
+
 static void msm_console_putchar(struct uart_port *port, int c)
 {
-	struct msm_port *msm_port = UART_TO_MSM(port);
+	/* This call can incur significant delay if CTS flowcontrol is enabled
+	 * on port and no serial cable is attached.
+	 */
+	wait_for_xmitr(port, UART_SR_TX_READY);
 
-	if (msm_port->is_uartdm)
-		reset_dm_count(port);
-
-	while (!(msm_read(port, UART_SR) & UART_SR_TX_READY))
-		;
-	msm_write(port, c, msm_port->is_uartdm ? UARTDM_TF : UART_TF);
+	msm_write(port, c, UART_TF);
 }
 
 static void msm_console_write(struct console *co, const char *s,
@@ -778,33 +887,48 @@
 {
 	struct uart_port *port;
 	struct msm_port *msm_port;
+	int locked;
 
 	BUG_ON(co->index < 0 || co->index >= UART_NR);
 
 	port = get_port_from_line(co->index);
 	msm_port = UART_TO_MSM(port);
 
-	spin_lock(&port->lock);
+	/* not pretty, but we can end up here via various convoluted paths */
+	if (port->sysrq || oops_in_progress)
+		locked = spin_trylock(&port->lock);
+	else {
+		locked = 1;
+		spin_lock(&port->lock);
+	}
+
 	uart_console_write(port, s, count, msm_console_putchar);
-	spin_unlock(&port->lock);
+
+	if (locked)
+		spin_unlock(&port->lock);
 }
 
 static int __init msm_console_setup(struct console *co, char *options)
 {
 	struct uart_port *port;
-	struct msm_port *msm_port;
-	int baud, flow, bits, parity;
+	int baud = 0, flow, bits, parity;
 
 	if (unlikely(co->index >= UART_NR || co->index < 0))
 		return -ENXIO;
 
 	port = get_port_from_line(co->index);
-	msm_port = UART_TO_MSM(port);
 
 	if (unlikely(!port->membase))
 		return -ENXIO;
 
+	port->cons = co;
+
+	pm_runtime_get_noresume(port->dev);
+
+#ifndef CONFIG_PM_RUNTIME
 	msm_init_clock(port);
+#endif
+	pm_runtime_resume(port->dev);
 
 	if (options)
 		uart_parse_options(options, &baud, &parity, &bits, &flow);
@@ -821,11 +945,6 @@
 
 	msm_reset(port);
 
-	if (msm_port->is_uartdm) {
-		msm_write(port, UART_CR_CMD_PROTECTION_EN, UART_CR);
-		msm_write(port, UART_CR_TX_ENABLE, UART_CR);
-	}
-
 	printk(KERN_INFO "msm_serial: console setup on port #%d\n", port->line);
 
 	return uart_set_options(port, co, baud, parity, bits, flow);
@@ -843,7 +962,7 @@
 	.data = &msm_uart_driver,
 };
 
-#define MSM_CONSOLE	(&msm_console)
+#define MSM_CONSOLE	&msm_console
 
 #else
 #define MSM_CONSOLE	NULL
@@ -857,17 +976,15 @@
 	.cons = MSM_CONSOLE,
 };
 
-static atomic_t msm_uart_next_id = ATOMIC_INIT(0);
-
 static int __init msm_serial_probe(struct platform_device *pdev)
 {
 	struct msm_port *msm_port;
 	struct resource *resource;
 	struct uart_port *port;
 	int irq;
-
-	if (pdev->id == -1)
-		pdev->id = atomic_inc_return(&msm_uart_next_id) - 1;
+#ifdef CONFIG_SERIAL_MSM_RX_WAKEUP
+	struct msm_serial_platform_data *pdata = pdev->dev.platform_data;
+#endif
 
 	if (unlikely(pdev->id < 0 || pdev->id >= UART_NR))
 		return -ENXIO;
@@ -878,29 +995,12 @@
 	port->dev = &pdev->dev;
 	msm_port = UART_TO_MSM(port);
 
-	if (platform_get_resource(pdev, IORESOURCE_MEM, 1))
-		msm_port->is_uartdm = 1;
-	else
-		msm_port->is_uartdm = 0;
-
-	if (msm_port->is_uartdm) {
-		msm_port->clk = clk_get(&pdev->dev, "gsbi_uart_clk");
-		msm_port->pclk = clk_get(&pdev->dev, "gsbi_pclk");
-	} else {
-		msm_port->clk = clk_get(&pdev->dev, "uart_clk");
-		msm_port->pclk = ERR_PTR(-ENOENT);
-	}
-
-	if (unlikely(IS_ERR(msm_port->clk) || (IS_ERR(msm_port->pclk) &&
-					       msm_port->is_uartdm)))
-			return PTR_ERR(msm_port->clk);
-
-	if (msm_port->is_uartdm)
-		clk_set_rate(msm_port->clk, 7372800);
-
+	msm_port->clk = clk_get(&pdev->dev, "core_clk");
+	if (unlikely(IS_ERR(msm_port->clk)))
+		return PTR_ERR(msm_port->clk);
 	port->uartclk = clk_get_rate(msm_port->clk);
-	printk(KERN_INFO "uartclk = %d\n", port->uartclk);
-
+	if (!port->uartclk)
+		port->uartclk = 19200000;
 
 	resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (unlikely(!resource))
@@ -914,6 +1014,29 @@
 
 	platform_set_drvdata(pdev, port);
 
+
+#ifdef CONFIG_SERIAL_MSM_RX_WAKEUP
+	if (pdata == NULL)
+		msm_port->wakeup.irq = -1;
+	else {
+		msm_port->wakeup.irq = pdata->wakeup_irq;
+		msm_port->wakeup.ignore = 1;
+		msm_port->wakeup.inject_rx = pdata->inject_rx_on_wakeup;
+		msm_port->wakeup.rx_to_inject = pdata->rx_to_inject;
+
+		if (unlikely(msm_port->wakeup.irq <= 0))
+			return -EINVAL;
+	}
+#endif
+
+#ifdef CONFIG_SERIAL_MSM_CLOCK_CONTROL
+	msm_port->clk_state = MSM_CLK_PORT_OFF;
+	hrtimer_init(&msm_port->clk_off_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	msm_port->clk_off_timer.function = msm_serial_clock_off;
+	msm_port->clk_off_delay = ktime_set(0, 1000000);  /* 1 ms */
+#endif
+
+	pm_runtime_enable(port->dev);
 	return uart_add_one_port(&msm_uart_driver, port);
 }
 
@@ -921,14 +1044,76 @@
 {
 	struct msm_port *msm_port = platform_get_drvdata(pdev);
 
+	pm_runtime_put_sync(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+
 	clk_put(msm_port->clk);
 
 	return 0;
 }
 
-static struct of_device_id msm_match_table[] = {
-	{ .compatible = "qcom,msm-uart" },
-	{}
+#ifdef CONFIG_PM
+static int msm_serial_suspend(struct device *dev)
+{
+	struct uart_port *port;
+	struct platform_device *pdev = to_platform_device(dev);
+	port = get_port_from_line(pdev->id);
+
+	if (port) {
+		uart_suspend_port(&msm_uart_driver, port);
+		if (is_console(port))
+			msm_deinit_clock(port);
+	}
+
+	return 0;
+}
+
+static int msm_serial_resume(struct device *dev)
+{
+	struct uart_port *port;
+	struct platform_device *pdev = to_platform_device(dev);
+	port = get_port_from_line(pdev->id);
+
+	if (port) {
+		if (is_console(port))
+			msm_init_clock(port);
+		uart_resume_port(&msm_uart_driver, port);
+	}
+
+	return 0;
+}
+#else
+#define msm_serial_suspend NULL
+#define msm_serial_resume NULL
+#endif
+
+static int msm_serial_runtime_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct uart_port *port;
+	port = get_port_from_line(pdev->id);
+
+	dev_dbg(dev, "pm_runtime: suspending\n");
+	msm_deinit_clock(port);
+	return 0;
+}
+
+static int msm_serial_runtime_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct uart_port *port;
+	port = get_port_from_line(pdev->id);
+
+	dev_dbg(dev, "pm_runtime: resuming\n");
+	msm_init_clock(port);
+	return 0;
+}
+
+static struct dev_pm_ops msm_serial_dev_pm_ops = {
+	.suspend = msm_serial_suspend,
+	.resume = msm_serial_resume,
+	.runtime_suspend = msm_serial_runtime_suspend,
+	.runtime_resume = msm_serial_runtime_resume,
 };
 
 static struct platform_driver msm_platform_driver = {
@@ -936,7 +1121,7 @@
 	.driver = {
 		.name = "msm_serial",
 		.owner = THIS_MODULE,
-		.of_match_table = msm_match_table,
+		.pm = &msm_serial_dev_pm_ops,
 	},
 };
 
@@ -971,4 +1156,4 @@
 
 MODULE_AUTHOR("Robert Love <rlove@google.com>");
 MODULE_DESCRIPTION("Driver for msm7x serial device");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/tty/serial/msm_serial.h b/drivers/tty/serial/msm_serial.h
index e4acef5..65d0e30 100644
--- a/drivers/tty/serial/msm_serial.h
+++ b/drivers/tty/serial/msm_serial.h
@@ -46,11 +46,14 @@
 #define UART_CSR_19200	0xBB
 #define UART_CSR_14400	0xAA
 #define UART_CSR_9600	0x99
+#define UART_CSR_7200	0x88
 #define UART_CSR_4800	0x77
 #define UART_CSR_2400	0x55
 #define UART_CSR_1200	0x44
 #define UART_CSR_600	0x33
 #define UART_CSR_300	0x22
+#define UART_CSR_150	0x11
+#define UART_CSR_75	0x00
 
 #define UART_TF		0x000C
 #define UARTDM_TF	0x0070
@@ -128,60 +131,4 @@
 #define UARTDM_NCF_TX		0x40
 #define UARTDM_RX_TOTAL_SNAP	0x38
 
-#define UART_TO_MSM(uart_port)	((struct msm_port *) uart_port)
-
-static inline
-void msm_write(struct uart_port *port, unsigned int val, unsigned int off)
-{
-	__raw_writel(val, port->membase + off);
-}
-
-static inline
-unsigned int msm_read(struct uart_port *port, unsigned int off)
-{
-	return __raw_readl(port->membase + off);
-}
-
-/*
- * Setup the MND registers to use the TCXO clock.
- */
-static inline void msm_serial_set_mnd_regs_tcxo(struct uart_port *port)
-{
-	msm_write(port, 0x06, UART_MREG);
-	msm_write(port, 0xF1, UART_NREG);
-	msm_write(port, 0x0F, UART_DREG);
-	msm_write(port, 0x1A, UART_MNDREG);
-}
-
-/*
- * Setup the MND registers to use the TCXO clock divided by 4.
- */
-static inline void msm_serial_set_mnd_regs_tcxoby4(struct uart_port *port)
-{
-	msm_write(port, 0x18, UART_MREG);
-	msm_write(port, 0xF6, UART_NREG);
-	msm_write(port, 0x0F, UART_DREG);
-	msm_write(port, 0x0A, UART_MNDREG);
-}
-
-static inline
-void msm_serial_set_mnd_regs_from_uartclk(struct uart_port *port)
-{
-	if (port->uartclk == 19200000)
-		msm_serial_set_mnd_regs_tcxo(port);
-	else
-		msm_serial_set_mnd_regs_tcxoby4(port);
-}
-
-/*
- * TROUT has a specific defect that makes it report it's uartclk
- * as 19.2Mhz (TCXO) when it's actually 4.8Mhz (TCXO/4). This special
- * cases TROUT to use the right clock.
- */
-#ifdef CONFIG_MACH_TROUT
-#define msm_serial_set_mnd_regs msm_serial_set_mnd_regs_tcxoby4
-#else
-#define msm_serial_set_mnd_regs msm_serial_set_mnd_regs_from_uartclk
-#endif
-
 #endif	/* __DRIVERS_SERIAL_MSM_SERIAL_H */
diff --git a/drivers/tty/serial/msm_serial_debugger.c b/drivers/tty/serial/msm_serial_debugger.c
new file mode 100644
index 0000000..88b6784
--- /dev/null
+++ b/drivers/tty/serial/msm_serial_debugger.c
@@ -0,0 +1,421 @@
+/*
+ * drivers/serial/msm_serial_debuger.c
+ *
+ * Serial Debugger Interface for MSM7K
+ *
+ * Copyright (C) 2008 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 <stdarg.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/console.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/kernel_debugger.h>
+#include <linux/kernel_stat.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+
+#include <mach/system.h>
+#include <mach/fiq.h>
+
+#include "msm_serial.h"
+
+static unsigned int debug_port_base;
+static int debug_signal_irq;
+static struct clk *debug_clk;
+static int debug_enable;
+static int debugger_enable;
+static struct {
+	unsigned int	base;
+	int		irq;
+	struct device	*clk_device;
+	int		signal_irq;
+} init_data;
+
+static inline void msm_write(unsigned int val, unsigned int off)
+{
+	__raw_writel(val, debug_port_base + off);
+}
+
+static inline unsigned int msm_read(unsigned int off)
+{
+	return __raw_readl(debug_port_base + off);
+}
+
+static void debug_port_init(void)
+{
+	/* reset everything */
+	msm_write(UART_CR_CMD_RESET_RX, UART_CR);
+	msm_write(UART_CR_CMD_RESET_TX, UART_CR);
+	msm_write(UART_CR_CMD_RESET_ERR, UART_CR);
+	msm_write(UART_CR_CMD_RESET_BREAK_INT, UART_CR);
+	msm_write(UART_CR_CMD_RESET_CTS, UART_CR);
+	msm_write(UART_CR_CMD_SET_RFR, UART_CR);
+
+	/* setup clock dividers */
+	if (clk_get_rate(debug_clk) == 19200000) {
+		/* clock is TCXO (19.2MHz) */
+		msm_write(0x06, UART_MREG);
+		msm_write(0xF1, UART_NREG);
+		msm_write(0x0F, UART_DREG);
+		msm_write(0x1A, UART_MNDREG);
+	} else {
+		/* clock must be TCXO/4 */
+		msm_write(0x18, UART_MREG);
+		msm_write(0xF6, UART_NREG);
+		msm_write(0x0F, UART_DREG);
+		msm_write(0x0A, UART_MNDREG);
+	}
+
+	msm_write(UART_CSR_115200, UART_CSR);
+
+	/* rx interrupt on every character -- keep it simple */
+	msm_write(0, UART_RFWR);
+
+	/* enable TX and RX */
+	msm_write(0x05, UART_CR);
+
+	/* enable RX interrupt */
+	msm_write(UART_IMR_RXLEV, UART_IMR);
+}
+
+static inline int debug_getc(void)
+{
+	if (msm_read(UART_SR) & UART_SR_RX_READY) {
+		return msm_read(UART_RF);
+	} else {
+		return -1;
+	}
+}
+
+static inline void debug_putc(unsigned int c)
+{
+	while (!(msm_read(UART_SR) & UART_SR_TX_READY)) ;
+	msm_write(c, UART_TF);
+}
+
+static inline void debug_flush(void)
+{
+	while (!(msm_read(UART_SR) & UART_SR_TX_EMPTY)) ;
+}
+
+static void debug_puts(char *s)
+{
+	unsigned c;
+	while ((c = *s++)) {
+		if (c == '\n')
+			debug_putc('\r');
+		debug_putc(c);
+	}
+}
+
+static void debug_prompt(void)
+{
+	debug_puts("debug> ");
+}
+
+int log_buf_copy(char *dest, int idx, int len);
+static void dump_kernel_log(void)
+{
+	char buf[1024];
+	int idx = 0;
+	int ret;
+	int saved_oip;
+
+	/* setting oops_in_progress prevents log_buf_copy()
+	 * from trying to take a spinlock which will make it
+	 * very unhappy in some cases...
+	 */
+	saved_oip = oops_in_progress;
+	oops_in_progress = 1;
+	for (;;) {
+		ret = log_buf_copy(buf, idx, 1023);
+		if (ret <= 0)
+			break;
+		buf[ret] = 0;
+		debug_puts(buf);
+		idx += ret;
+	}
+	oops_in_progress = saved_oip;
+}
+
+static char *mode_name(unsigned cpsr)
+{
+	switch (cpsr & MODE_MASK) {
+	case USR_MODE: return "USR";
+	case FIQ_MODE: return "FIQ";
+	case IRQ_MODE: return "IRQ";
+	case SVC_MODE: return "SVC";
+	case ABT_MODE: return "ABT";
+	case UND_MODE: return "UND";
+	case SYSTEM_MODE: return "SYS";
+	default: return "???";
+	}
+}
+
+#define DEBUG_MAX 64
+static char debug_cmd[DEBUG_MAX];
+static int debug_busy;
+static int debug_abort;
+
+static int debug_printf(void *cookie, const char *fmt, ...)
+{
+	char buf[256];
+	va_list ap;
+
+	va_start(ap, fmt);
+	vsnprintf(buf, 128, fmt, ap);
+	va_end(ap);
+
+	debug_puts(buf);
+	return debug_abort;
+}
+
+/* Safe outside fiq context */
+static int debug_printf_nfiq(void *cookie, const char *fmt, ...)
+{
+	char buf[256];
+	va_list ap;
+	unsigned long irq_flags;
+
+	va_start(ap, fmt);
+	vsnprintf(buf, 128, fmt, ap);
+	va_end(ap);
+
+	local_irq_save(irq_flags);
+	debug_puts(buf);
+	debug_flush();
+	local_irq_restore(irq_flags);
+	return debug_abort;
+}
+
+#define dprintf(fmt...) debug_printf(0, fmt)
+
+unsigned int last_irqs[NR_IRQS];
+
+static void dump_irqs(void)
+{
+	int n;
+	dprintf("irqnr       total  since-last   status  name\n");
+	for (n = 1; n < NR_IRQS; n++) {
+		struct irqaction *act = irq_desc[n].action;
+		if (!act && !kstat_cpu(0).irqs[n])
+			continue;
+		dprintf("%5d: %10u %11u %8x  %s\n", n,
+			kstat_cpu(0).irqs[n],
+			kstat_cpu(0).irqs[n] - last_irqs[n],
+			irq_desc[n].status,
+			(act && act->name) ? act->name : "???");
+		last_irqs[n] = kstat_cpu(0).irqs[n];
+	}
+}
+
+static void debug_exec(const char *cmd, unsigned *regs)
+{
+	if (!strcmp(cmd, "pc")) {
+		dprintf(" pc %08x cpsr %08x mode %s\n",
+			regs[15], regs[16], mode_name(regs[16]));
+	} else if (!strcmp(cmd, "regs")) {
+		dprintf(" r0 %08x  r1 %08x  r2 %08x  r3 %08x\n",
+			regs[0], regs[1], regs[2], regs[3]);
+		dprintf(" r4 %08x  r5 %08x  r6 %08x  r7 %08x\n",
+			regs[4], regs[5], regs[6], regs[7]);
+		dprintf(" r8 %08x  r9 %08x r10 %08x r11 %08x  mode %s\n",
+			regs[8], regs[9], regs[10], regs[11],
+			mode_name(regs[16]));
+		dprintf(" ip %08x  sp %08x  lr %08x  pc %08x  cpsr %08x\n",
+			regs[10], regs[13], regs[14], regs[15], regs[16]);
+	} else if (!strcmp(cmd, "reboot")) {
+		if (msm_hw_reset_hook)
+			msm_hw_reset_hook();
+	} else if (!strcmp(cmd, "irqs")) {
+		dump_irqs();
+	} else if (!strcmp(cmd, "kmsg")) {
+		dump_kernel_log();
+	} else if (!strcmp(cmd, "version")) {
+		dprintf("%s\n", linux_banner);
+	} else {
+		if (debug_busy) {
+			dprintf("command processor busy. trying to abort.\n");
+			debug_abort = -1;
+		} else {
+			strcpy(debug_cmd, cmd);
+			debug_busy = 1;
+		}
+		msm_trigger_irq(debug_signal_irq);
+		return;
+	}
+	debug_prompt();
+}
+
+static irqreturn_t debug_irq(int irq, void *dev)
+{
+	if (debug_busy) {
+		struct kdbg_ctxt ctxt;
+
+		ctxt.printf = debug_printf_nfiq;
+		kernel_debugger(&ctxt, debug_cmd);
+		debug_prompt();
+
+		debug_busy = 0;
+	}
+	return IRQ_HANDLED;
+}
+
+static char debug_buf[DEBUG_MAX];
+static int debug_count;
+
+static void debug_fiq(void *data, void *regs)
+{
+	int c;
+	static int last_c;
+
+	while ((c = debug_getc()) != -1) {
+		if (!debug_enable) {
+			if ((c == 13) || (c == 10)) {
+				debug_enable = true;
+				debug_count = 0;
+				debug_prompt();
+			}
+		} else if ((c >= ' ') && (c < 127)) {
+			if (debug_count < (DEBUG_MAX - 1)) {
+				debug_buf[debug_count++] = c;
+				debug_putc(c);
+			}
+		} else if ((c == 8) || (c == 127)) {
+			if (debug_count > 0) {
+				debug_count--;
+				debug_putc(8);
+				debug_putc(' ');
+				debug_putc(8);
+			}
+		} else if ((c == 13) || (c == 10)) {
+			if (c == '\r' || (c == '\n' && last_c != '\r')) {
+				debug_putc('\r');
+				debug_putc('\n');
+			}
+			if (debug_count) {
+				debug_buf[debug_count] = 0;
+				debug_count = 0;
+				debug_exec(debug_buf, regs);
+			} else {
+				debug_prompt();
+			}
+		}
+		last_c = c;
+	}
+	debug_flush();
+}
+
+#if defined(CONFIG_MSM_SERIAL_DEBUGGER_CONSOLE)
+static void debug_console_write(struct console *co,
+				const char *s, unsigned int count)
+{
+	unsigned long irq_flags;
+
+	/* disable irq's while TXing outside of FIQ context */
+	local_irq_save(irq_flags);
+	while (count--) {
+		if (*s == '\n')
+			debug_putc('\r');
+		debug_putc(*s++);
+	}
+	debug_flush();
+	local_irq_restore(irq_flags);
+}
+
+static struct console msm_serial_debug_console = {
+	.name = "debug_console",
+	.write = debug_console_write,
+	.flags = CON_PRINTBUFFER | CON_ANYTIME | CON_ENABLED,
+};
+#endif
+
+void msm_serial_debug_enable(int enable) {
+	debug_enable = enable;
+}
+
+void msm_serial_debug_init(unsigned int base, int irq,
+			   struct device *clk_device, int signal_irq)
+{
+	int ret;
+	void *port;
+
+	debug_clk = clk_get(clk_device, "uart_clk");
+	if (debug_clk)
+		clk_enable(debug_clk);
+
+	port = ioremap(base, 4096);
+	if (!port)
+		return;
+
+	init_data.base = base;
+	init_data.irq = irq;
+	init_data.clk_device = clk_device;
+	init_data.signal_irq = signal_irq;
+	debug_port_base = (unsigned int) port;
+	debug_signal_irq = signal_irq;
+	debug_port_init();
+
+	debug_prompt();
+
+	msm_fiq_select(irq);
+	msm_fiq_set_handler(debug_fiq, 0);
+	msm_fiq_enable(irq);
+
+	ret = request_irq(signal_irq, debug_irq,
+			  IRQF_TRIGGER_RISING, "debug", 0);
+	if (ret)
+		printk(KERN_ERR
+		       "serial_debugger: could not install signal_irq");
+
+#if defined(CONFIG_MSM_SERIAL_DEBUGGER_CONSOLE)
+	register_console(&msm_serial_debug_console);
+#endif
+	debugger_enable = 1;
+}
+static int msm_serial_debug_remove(const char *val, struct kernel_param *kp)
+{
+	int ret;
+	static int pre_stat = 1;
+	ret = param_set_bool(val, kp);
+	if (ret)
+		return ret;
+
+	if (pre_stat == *(int *)kp->arg)
+		return 0;
+
+	pre_stat = *(int *)kp->arg;
+
+	if (*(int *)kp->arg) {
+		msm_serial_debug_init(init_data.base, init_data.irq,
+				init_data.clk_device, init_data.signal_irq);
+		printk(KERN_INFO "enable FIQ serial debugger\n");
+		return 0;
+	}
+
+#if defined(CONFIG_MSM_SERIAL_DEBUGGER_CONSOLE)
+	unregister_console(&msm_serial_debug_console);
+#endif
+	free_irq(init_data.signal_irq, 0);
+	msm_fiq_set_handler(NULL, 0);
+	msm_fiq_disable(init_data.irq);
+	msm_fiq_unselect(init_data.irq);
+	clk_disable(debug_clk);
+	printk(KERN_INFO "disable FIQ serial debugger\n");
+	return 0;
+}
+module_param_call(enable, msm_serial_debug_remove, param_get_bool,
+		&debugger_enable, S_IWUSR | S_IRUGO);
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index fca13dc..21b9669 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -1,10 +1,14 @@
-/*
- * MSM 7k/8k High speed uart driver
+/* drivers/serial/msm_serial_hs.c
  *
- * Copyright (c) 2007-2011, Code Aurora Forum. All rights reserved.
+ * MSM 7k High speed uart driver
+ *
  * Copyright (c) 2008 Google Inc.
+ * Copyright (c) 2007-2012, Code Aurora Forum. All rights reserved.
  * Modified: Nick Pelly <npelly@google.com>
  *
+ * All source code in this file is licensed under the following license
+ * except where indicated.
+ *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * version 2 as published by the Free Software Foundation.
@@ -30,8 +34,6 @@
 
 #include <linux/serial.h>
 #include <linux/serial_core.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
@@ -45,163 +47,26 @@
 #include <linux/pm_runtime.h>
 #include <linux/dma-mapping.h>
 #include <linux/dmapool.h>
+#include <linux/tty_flip.h>
 #include <linux/wait.h>
-#include <linux/workqueue.h>
-
-#include <linux/atomic.h>
+#include <linux/sysfs.h>
+#include <linux/stat.h>
+#include <linux/device.h>
+#include <linux/wakelock.h>
+#include <linux/debugfs.h>
+#include <asm/atomic.h>
 #include <asm/irq.h>
 
 #include <mach/hardware.h>
 #include <mach/dma.h>
-#include <linux/platform_data/msm_serial_hs.h>
+#include <mach/msm_serial_hs.h>
 
-/* HSUART Registers */
-#define UARTDM_MR1_ADDR 0x0
-#define UARTDM_MR2_ADDR 0x4
+#include "msm_serial_hs_hwreg.h"
 
-/* Data Mover result codes */
-#define RSLT_FIFO_CNTR_BMSK (0xE << 28)
-#define RSLT_VLD            BIT(1)
+static int hs_serial_debug_mask = 1;
+module_param_named(debug_mask, hs_serial_debug_mask,
+		   int, S_IRUGO | S_IWUSR | S_IWGRP);
 
-/* write only register */
-#define UARTDM_CSR_ADDR 0x8
-#define UARTDM_CSR_115200 0xFF
-#define UARTDM_CSR_57600  0xEE
-#define UARTDM_CSR_38400  0xDD
-#define UARTDM_CSR_28800  0xCC
-#define UARTDM_CSR_19200  0xBB
-#define UARTDM_CSR_14400  0xAA
-#define UARTDM_CSR_9600   0x99
-#define UARTDM_CSR_7200   0x88
-#define UARTDM_CSR_4800   0x77
-#define UARTDM_CSR_3600   0x66
-#define UARTDM_CSR_2400   0x55
-#define UARTDM_CSR_1200   0x44
-#define UARTDM_CSR_600    0x33
-#define UARTDM_CSR_300    0x22
-#define UARTDM_CSR_150    0x11
-#define UARTDM_CSR_75     0x00
-
-/* write only register */
-#define UARTDM_TF_ADDR 0x70
-#define UARTDM_TF2_ADDR 0x74
-#define UARTDM_TF3_ADDR 0x78
-#define UARTDM_TF4_ADDR 0x7C
-
-/* write only register */
-#define UARTDM_CR_ADDR 0x10
-#define UARTDM_IMR_ADDR 0x14
-
-#define UARTDM_IPR_ADDR 0x18
-#define UARTDM_TFWR_ADDR 0x1c
-#define UARTDM_RFWR_ADDR 0x20
-#define UARTDM_HCR_ADDR 0x24
-#define UARTDM_DMRX_ADDR 0x34
-#define UARTDM_IRDA_ADDR 0x38
-#define UARTDM_DMEN_ADDR 0x3c
-
-/* UART_DM_NO_CHARS_FOR_TX */
-#define UARTDM_NCF_TX_ADDR 0x40
-
-#define UARTDM_BADR_ADDR 0x44
-
-#define UARTDM_SIM_CFG_ADDR 0x80
-/* Read Only register */
-#define UARTDM_SR_ADDR 0x8
-
-/* Read Only register */
-#define UARTDM_RF_ADDR  0x70
-#define UARTDM_RF2_ADDR 0x74
-#define UARTDM_RF3_ADDR 0x78
-#define UARTDM_RF4_ADDR 0x7C
-
-/* Read Only register */
-#define UARTDM_MISR_ADDR 0x10
-
-/* Read Only register */
-#define UARTDM_ISR_ADDR 0x14
-#define UARTDM_RX_TOTAL_SNAP_ADDR 0x38
-
-#define UARTDM_RXFS_ADDR 0x50
-
-/* Register field Mask Mapping */
-#define UARTDM_SR_PAR_FRAME_BMSK        BIT(5)
-#define UARTDM_SR_OVERRUN_BMSK          BIT(4)
-#define UARTDM_SR_TXEMT_BMSK            BIT(3)
-#define UARTDM_SR_TXRDY_BMSK            BIT(2)
-#define UARTDM_SR_RXRDY_BMSK            BIT(0)
-
-#define UARTDM_CR_TX_DISABLE_BMSK       BIT(3)
-#define UARTDM_CR_RX_DISABLE_BMSK       BIT(1)
-#define UARTDM_CR_TX_EN_BMSK            BIT(2)
-#define UARTDM_CR_RX_EN_BMSK            BIT(0)
-
-/* UARTDM_CR channel_comman bit value (register field is bits 8:4) */
-#define RESET_RX                0x10
-#define RESET_TX                0x20
-#define RESET_ERROR_STATUS      0x30
-#define RESET_BREAK_INT         0x40
-#define START_BREAK             0x50
-#define STOP_BREAK              0x60
-#define RESET_CTS               0x70
-#define RESET_STALE_INT         0x80
-#define RFR_LOW                 0xD0
-#define RFR_HIGH                0xE0
-#define CR_PROTECTION_EN        0x100
-#define STALE_EVENT_ENABLE      0x500
-#define STALE_EVENT_DISABLE     0x600
-#define FORCE_STALE_EVENT       0x400
-#define CLEAR_TX_READY          0x300
-#define RESET_TX_ERROR          0x800
-#define RESET_TX_DONE           0x810
-
-#define UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK 0xffffff00
-#define UARTDM_MR1_AUTO_RFR_LEVEL0_BMSK 0x3f
-#define UARTDM_MR1_CTS_CTL_BMSK 0x40
-#define UARTDM_MR1_RX_RDY_CTL_BMSK 0x80
-
-#define UARTDM_MR2_ERROR_MODE_BMSK 0x40
-#define UARTDM_MR2_BITS_PER_CHAR_BMSK 0x30
-
-/* bits per character configuration */
-#define FIVE_BPC  (0 << 4)
-#define SIX_BPC   (1 << 4)
-#define SEVEN_BPC (2 << 4)
-#define EIGHT_BPC (3 << 4)
-
-#define UARTDM_MR2_STOP_BIT_LEN_BMSK 0xc
-#define STOP_BIT_ONE (1 << 2)
-#define STOP_BIT_TWO (3 << 2)
-
-#define UARTDM_MR2_PARITY_MODE_BMSK 0x3
-
-/* Parity configuration */
-#define NO_PARITY 0x0
-#define EVEN_PARITY 0x1
-#define ODD_PARITY 0x2
-#define SPACE_PARITY 0x3
-
-#define UARTDM_IPR_STALE_TIMEOUT_MSB_BMSK 0xffffff80
-#define UARTDM_IPR_STALE_LSB_BMSK 0x1f
-
-/* These can be used for both ISR and IMR register */
-#define UARTDM_ISR_TX_READY_BMSK        BIT(7)
-#define UARTDM_ISR_CURRENT_CTS_BMSK     BIT(6)
-#define UARTDM_ISR_DELTA_CTS_BMSK       BIT(5)
-#define UARTDM_ISR_RXLEV_BMSK           BIT(4)
-#define UARTDM_ISR_RXSTALE_BMSK         BIT(3)
-#define UARTDM_ISR_RXBREAK_BMSK         BIT(2)
-#define UARTDM_ISR_RXHUNT_BMSK          BIT(1)
-#define UARTDM_ISR_TXLEV_BMSK           BIT(0)
-
-/* Field definitions for UART_DM_DMEN*/
-#define UARTDM_TX_DM_EN_BMSK 0x1
-#define UARTDM_RX_DM_EN_BMSK 0x2
-
-#define UART_FIFOSIZE 64
-#define UARTCLK 7372800
-
-/* Rx DMA request states */
 enum flush_reason {
 	FLUSH_NONE,
 	FLUSH_DATA_READY,
@@ -211,7 +76,6 @@
 	FLUSH_SHUTDOWN,
 };
 
-/* UART clock states */
 enum msm_hs_clk_states_e {
 	MSM_HS_CLK_PORT_OFF,     /* port not in use */
 	MSM_HS_CLK_OFF,          /* clock disabled */
@@ -228,27 +92,9 @@
 	CLK_REQ_OFF_RXSTALE_FLUSHED,
 };
 
-/**
- * struct msm_hs_tx
- * @tx_ready_int_en: ok to dma more tx?
- * @dma_in_flight: tx dma in progress
- * @xfer: top level DMA command pointer structure
- * @command_ptr: third level command struct pointer
- * @command_ptr_ptr: second level command list struct pointer
- * @mapped_cmd_ptr: DMA view of third level command struct
- * @mapped_cmd_ptr_ptr: DMA view of second level command list struct
- * @tx_count: number of bytes to transfer in DMA transfer
- * @dma_base: DMA view of UART xmit buffer
- *
- * This structure describes a single Tx DMA transaction. MSM DMA
- * commands have two levels of indirection. The top level command
- * ptr points to a list of command ptr which in turn points to a
- * single DMA 'command'. In our case each Tx transaction consists
- * of a single second level pointer pointing to a 'box type' command.
- */
 struct msm_hs_tx {
-	unsigned int tx_ready_int_en;
-	unsigned int dma_in_flight;
+	unsigned int tx_ready_int_en;  /* ok to dma more tx */
+	unsigned int dma_in_flight;    /* tx dma in progress */
 	struct msm_dmov_cmd xfer;
 	dmov_box *command_ptr;
 	u32 *command_ptr_ptr;
@@ -256,25 +102,9 @@
 	dma_addr_t mapped_cmd_ptr_ptr;
 	int tx_count;
 	dma_addr_t dma_base;
+	struct tasklet_struct tlet;
 };
 
-/**
- * struct msm_hs_rx
- * @flush: Rx DMA request state
- * @xfer: top level DMA command pointer structure
- * @cmdptr_dmaaddr: DMA view of second level command structure
- * @command_ptr: third level DMA command pointer structure
- * @command_ptr_ptr: second level DMA command list pointer
- * @mapped_cmd_ptr: DMA view of the third level command structure
- * @wait: wait for DMA completion before shutdown
- * @buffer: destination buffer for RX DMA
- * @rbuffer: DMA view of buffer
- * @pool: dma pool out of which coherent rx buffer is allocated
- * @tty_work: private work-queue for tty flip buffer push task
- *
- * This structure describes a single Rx DMA transaction. Rx DMA
- * transactions use box mode DMA commands.
- */
 struct msm_hs_rx {
 	enum flush_reason flush;
 	struct msm_dmov_cmd xfer;
@@ -285,127 +115,281 @@
 	wait_queue_head_t wait;
 	dma_addr_t rbuffer;
 	unsigned char *buffer;
+	unsigned int buffer_pending;
 	struct dma_pool *pool;
-	struct work_struct tty_work;
+	struct wake_lock wake_lock;
+	struct delayed_work flip_insert_work;
+	struct tasklet_struct tlet;
 };
 
-/**
- * struct msm_hs_rx_wakeup
- * @irq: IRQ line to be configured as interrupt source on Rx activity
- * @ignore: boolean value. 1 = ignore the wakeup interrupt
- * @rx_to_inject: extra character to be inserted to Rx tty on wakeup
- * @inject_rx: 1 = insert rx_to_inject. 0 = do not insert extra character
- *
- * This is an optional structure required for UART Rx GPIO IRQ based
- * wakeup from low power state. UART wakeup can be triggered by RX activity
- * (using a wakeup GPIO on the UART RX pin). This should only be used if
- * there is not a wakeup GPIO on the UART CTS, and the first RX byte is
- * known (eg., with the Bluetooth Texas Instruments HCILL protocol),
- * since the first RX byte will always be lost. RTS will be asserted even
- * while the UART is clocked off in this mode of operation.
- */
-struct msm_hs_rx_wakeup {
+enum buffer_states {
+	NONE_PENDING = 0x0,
+	FIFO_OVERRUN = 0x1,
+	PARITY_ERROR = 0x2,
+	CHARS_NORMAL = 0x4,
+};
+
+/* optional low power wakeup, typically on a GPIO RX irq */
+struct msm_hs_wakeup {
 	int irq;  /* < 0 indicates low power wakeup disabled */
-	unsigned char ignore;
+	unsigned char ignore;  /* bool */
+
+	/* bool: inject char into rx tty on wakeup */
 	unsigned char inject_rx;
 	char rx_to_inject;
 };
 
-/**
- * struct msm_hs_port
- * @uport: embedded uart port structure
- * @imr_reg: shadow value of UARTDM_IMR
- * @clk: uart input clock handle
- * @tx: Tx transaction related data structure
- * @rx: Rx transaction related data structure
- * @dma_tx_channel: Tx DMA command channel
- * @dma_rx_channel Rx DMA command channel
- * @dma_tx_crci: Tx channel rate control interface number
- * @dma_rx_crci: Rx channel rate control interface number
- * @clk_off_timer: Timer to poll DMA event completion before clock off
- * @clk_off_delay: clk_off_timer poll interval
- * @clk_state: overall clock state
- * @clk_req_off_state: post flush clock states
- * @rx_wakeup: optional rx_wakeup feature related data
- * @exit_lpm_cb: optional callback to exit low power mode
- *
- * Low level serial port structure.
- */
 struct msm_hs_port {
 	struct uart_port uport;
-	unsigned long imr_reg;
+	unsigned long imr_reg;  /* shadow value of UARTDM_IMR */
 	struct clk *clk;
+	struct clk *pclk;
 	struct msm_hs_tx tx;
 	struct msm_hs_rx rx;
-
+	/* gsbi uarts have to do additional writes to gsbi memory */
+	/* block and top control status block. The following pointers */
+	/* keep a handle to these blocks. */
+	unsigned char __iomem	*mapped_gsbi;
 	int dma_tx_channel;
 	int dma_rx_channel;
 	int dma_tx_crci;
 	int dma_rx_crci;
-
-	struct hrtimer clk_off_timer;
+	struct hrtimer clk_off_timer;  /* to poll TXEMT before clock off */
 	ktime_t clk_off_delay;
 	enum msm_hs_clk_states_e clk_state;
 	enum msm_hs_clk_req_off_state_e clk_req_off_state;
 
-	struct msm_hs_rx_wakeup rx_wakeup;
-	void (*exit_lpm_cb)(struct uart_port *);
+	struct msm_hs_wakeup wakeup;
+	struct wake_lock dma_wake_lock;  /* held while any DMA active */
+
+	struct dentry *loopback_dir;
+	struct work_struct clock_off_w; /* work for actual clock off */
+	struct workqueue_struct *hsuart_wq; /* hsuart workqueue */
+	struct mutex clk_mutex; /* mutex to guard against clock off/clock on */
 };
 
 #define MSM_UARTDM_BURST_SIZE 16   /* DM burst size (in bytes) */
 #define UARTDM_TX_BUF_SIZE UART_XMIT_SIZE
 #define UARTDM_RX_BUF_SIZE 512
-
+#define RETRY_TIMEOUT 5
 #define UARTDM_NR 2
 
+static struct dentry *debug_base;
 static struct msm_hs_port q_uart_port[UARTDM_NR];
 static struct platform_driver msm_serial_hs_platform_driver;
 static struct uart_driver msm_hs_driver;
 static struct uart_ops msm_hs_ops;
-static struct workqueue_struct *msm_hs_workqueue;
 
 #define UARTDM_TO_MSM(uart_port) \
 	container_of((uart_port), struct msm_hs_port, uport)
 
-static unsigned int use_low_power_rx_wakeup(struct msm_hs_port
-						   *msm_uport)
+static ssize_t show_clock(struct device *dev, struct device_attribute *attr,
+			  char *buf)
 {
-	return (msm_uport->rx_wakeup.irq >= 0);
+	int state = 1;
+	enum msm_hs_clk_states_e clk_state;
+	unsigned long flags;
+
+	struct platform_device *pdev = container_of(dev, struct
+						    platform_device, dev);
+	struct msm_hs_port *msm_uport = &q_uart_port[pdev->id];
+
+	spin_lock_irqsave(&msm_uport->uport.lock, flags);
+	clk_state = msm_uport->clk_state;
+	spin_unlock_irqrestore(&msm_uport->uport.lock, flags);
+
+	if (clk_state <= MSM_HS_CLK_OFF)
+		state = 0;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", state);
 }
 
-static unsigned int msm_hs_read(struct uart_port *uport,
+static ssize_t set_clock(struct device *dev, struct device_attribute *attr,
+			 const char *buf, size_t count)
+{
+	int state;
+	struct platform_device *pdev = container_of(dev, struct
+						    platform_device, dev);
+	struct msm_hs_port *msm_uport = &q_uart_port[pdev->id];
+
+	state = buf[0] - '0';
+	switch (state) {
+	case 0: {
+		msm_hs_request_clock_off(&msm_uport->uport);
+		break;
+	}
+	case 1: {
+		msm_hs_request_clock_on(&msm_uport->uport);
+		break;
+	}
+	default: {
+		return -EINVAL;
+	}
+	}
+	return count;
+}
+
+static DEVICE_ATTR(clock, S_IWUSR | S_IRUGO, show_clock, set_clock);
+
+static inline unsigned int use_low_power_wakeup(struct msm_hs_port *msm_uport)
+{
+	return (msm_uport->wakeup.irq > 0);
+}
+
+static inline int is_gsbi_uart(struct msm_hs_port *msm_uport)
+{
+	/* assume gsbi uart if gsbi resource found in pdata */
+	return ((msm_uport->mapped_gsbi != NULL));
+}
+
+static inline unsigned int msm_hs_read(struct uart_port *uport,
 				       unsigned int offset)
 {
-	return ioread32(uport->membase + offset);
+	return readl_relaxed(uport->membase + offset);
 }
 
-static void msm_hs_write(struct uart_port *uport, unsigned int offset,
+static inline void msm_hs_write(struct uart_port *uport, unsigned int offset,
 				 unsigned int value)
 {
-	iowrite32(value, uport->membase + offset);
+	writel_relaxed(value, uport->membase + offset);
 }
 
 static void msm_hs_release_port(struct uart_port *port)
 {
-	iounmap(port->membase);
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(port);
+	struct platform_device *pdev = to_platform_device(port->dev);
+	struct resource *gsbi_resource;
+	resource_size_t size;
+
+	if (is_gsbi_uart(msm_uport)) {
+		iowrite32(GSBI_PROTOCOL_IDLE, msm_uport->mapped_gsbi +
+			  GSBI_CONTROL_ADDR);
+		gsbi_resource = platform_get_resource_byname(pdev,
+							     IORESOURCE_MEM,
+							     "gsbi_resource");
+		if (unlikely(!gsbi_resource))
+			return;
+
+		size = resource_size(gsbi_resource);
+		release_mem_region(gsbi_resource->start, size);
+		iounmap(msm_uport->mapped_gsbi);
+		msm_uport->mapped_gsbi = NULL;
+	}
 }
 
 static int msm_hs_request_port(struct uart_port *port)
 {
-	port->membase = ioremap(port->mapbase, PAGE_SIZE);
-	if (unlikely(!port->membase))
-		return -ENOMEM;
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(port);
+	struct platform_device *pdev = to_platform_device(port->dev);
+	struct resource *gsbi_resource;
+	resource_size_t size;
 
-	/* configure the CR Protection to Enable */
-	msm_hs_write(port, UARTDM_CR_ADDR, CR_PROTECTION_EN);
+	gsbi_resource = platform_get_resource_byname(pdev,
+						     IORESOURCE_MEM,
+						     "gsbi_resource");
+	if (gsbi_resource) {
+		size = resource_size(gsbi_resource);
+		if (unlikely(!request_mem_region(gsbi_resource->start, size,
+						 "msm_serial_hs")))
+			return -EBUSY;
+		msm_uport->mapped_gsbi = ioremap(gsbi_resource->start,
+						 size);
+		if (!msm_uport->mapped_gsbi) {
+			release_mem_region(gsbi_resource->start, size);
+			return -EBUSY;
+		}
+	}
+	/* no gsbi uart */
 	return 0;
 }
 
+static int msm_serial_loopback_enable_set(void *data, u64 val)
+{
+	struct msm_hs_port *msm_uport = data;
+	struct uart_port *uport = &(msm_uport->uport);
+	unsigned long flags;
+	int ret = 0;
+
+	clk_prepare_enable(msm_uport->clk);
+	if (msm_uport->pclk)
+		clk_prepare_enable(msm_uport->pclk);
+
+	if (val) {
+		spin_lock_irqsave(&uport->lock, flags);
+		ret = msm_hs_read(uport, UARTDM_MR2_ADDR);
+		ret |= UARTDM_MR2_LOOP_MODE_BMSK;
+		msm_hs_write(uport, UARTDM_MR2_ADDR, ret);
+		spin_unlock_irqrestore(&uport->lock, flags);
+	} else {
+		spin_lock_irqsave(&uport->lock, flags);
+		ret = msm_hs_read(uport, UARTDM_MR2_ADDR);
+		ret &= ~UARTDM_MR2_LOOP_MODE_BMSK;
+		msm_hs_write(uport, UARTDM_MR2_ADDR, ret);
+		spin_unlock_irqrestore(&uport->lock, flags);
+	}
+	/* Calling CLOCK API. Hence mb() requires here. */
+	mb();
+	clk_disable_unprepare(msm_uport->clk);
+	if (msm_uport->pclk)
+		clk_disable_unprepare(msm_uport->pclk);
+
+	return 0;
+}
+
+static int msm_serial_loopback_enable_get(void *data, u64 *val)
+{
+	struct msm_hs_port *msm_uport = data;
+	struct uart_port *uport = &(msm_uport->uport);
+	unsigned long flags;
+	int ret = 0;
+
+	clk_prepare_enable(msm_uport->clk);
+	if (msm_uport->pclk)
+		clk_prepare_enable(msm_uport->pclk);
+
+	spin_lock_irqsave(&uport->lock, flags);
+	ret = msm_hs_read(&msm_uport->uport, UARTDM_MR2_ADDR);
+	spin_unlock_irqrestore(&uport->lock, flags);
+
+	clk_disable_unprepare(msm_uport->clk);
+	if (msm_uport->pclk)
+		clk_disable_unprepare(msm_uport->pclk);
+
+	*val = (ret & UARTDM_MR2_LOOP_MODE_BMSK) ? 1 : 0;
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(loopback_enable_fops, msm_serial_loopback_enable_get,
+			msm_serial_loopback_enable_set, "%llu\n");
+
+/*
+ * msm_serial_hs debugfs node: <debugfs_root>/msm_serial_hs/loopback.<id>
+ * writing 1 turns on internal loopback mode in HW. Useful for automation
+ * test scripts.
+ * writing 0 disables the internal loopback mode. Default is disabled.
+ */
+static void __devinit msm_serial_debugfs_init(struct msm_hs_port *msm_uport,
+					   int id)
+{
+	char node_name[15];
+	snprintf(node_name, sizeof(node_name), "loopback.%d", id);
+	msm_uport->loopback_dir = debugfs_create_file(node_name,
+						S_IRUGO | S_IWUSR,
+						debug_base,
+						msm_uport,
+						&loopback_enable_fops);
+
+	if (IS_ERR_OR_NULL(msm_uport->loopback_dir))
+		pr_err("%s(): Cannot create loopback.%d debug entry",
+							__func__, id);
+}
+
 static int __devexit msm_hs_remove(struct platform_device *pdev)
 {
 
 	struct msm_hs_port *msm_uport;
 	struct device *dev;
+	struct msm_serial_hs_platform_data *pdata = pdev->dev.platform_data;
+
 
 	if (pdev->id < 0 || pdev->id >= UARTDM_NR) {
 		printk(KERN_ERR "Invalid plaform device ID = %d\n", pdev->id);
@@ -415,6 +399,13 @@
 	msm_uport = &q_uart_port[pdev->id];
 	dev = msm_uport->uport.dev;
 
+	if (pdata && pdata->gpio_config)
+		if (pdata->gpio_config(0))
+			dev_err(dev, "GPIO config error\n");
+
+	sysfs_remove_file(&pdev->dev.kobj, &dev_attr_clock.attr);
+	debugfs_remove(msm_uport->loopback_dir);
+
 	dma_unmap_single(dev, msm_uport->rx.mapped_cmd_ptr, sizeof(dmov_box),
 			 DMA_TO_DEVICE);
 	dma_pool_free(msm_uport->rx.pool, msm_uport->rx.buffer,
@@ -428,8 +419,15 @@
 	dma_unmap_single(dev, msm_uport->tx.mapped_cmd_ptr, sizeof(dmov_box),
 			 DMA_TO_DEVICE);
 
+	wake_lock_destroy(&msm_uport->rx.wake_lock);
+	wake_lock_destroy(&msm_uport->dma_wake_lock);
+	destroy_workqueue(msm_uport->hsuart_wq);
+	mutex_destroy(&msm_uport->clk_mutex);
+
 	uart_remove_one_port(&msm_hs_driver, &msm_uport->uport);
 	clk_put(msm_uport->clk);
+	if (msm_uport->pclk)
+		clk_put(msm_uport->pclk);
 
 	/* Free the tx resources */
 	kfree(msm_uport->tx.command_ptr);
@@ -444,64 +442,48 @@
 	return 0;
 }
 
-static int msm_hs_init_clk_locked(struct uart_port *uport)
+static int msm_hs_init_clk(struct uart_port *uport)
 {
 	int ret;
 	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
 
-	ret = clk_enable(msm_uport->clk);
-	if (ret) {
-		printk(KERN_ERR "Error could not turn on UART clk\n");
-		return ret;
-	}
-
 	/* Set up the MREG/NREG/DREG/MNDREG */
 	ret = clk_set_rate(msm_uport->clk, uport->uartclk);
 	if (ret) {
 		printk(KERN_WARNING "Error setting clock rate on UART\n");
-		clk_disable(msm_uport->clk);
 		return ret;
 	}
 
+	ret = clk_prepare_enable(msm_uport->clk);
+	if (ret) {
+		printk(KERN_ERR "Error could not turn on UART clk\n");
+		return ret;
+	}
+	if (msm_uport->pclk) {
+		ret = clk_prepare_enable(msm_uport->pclk);
+		if (ret) {
+			clk_disable_unprepare(msm_uport->clk);
+			dev_err(uport->dev,
+				"Error could not turn on UART pclk\n");
+			return ret;
+		}
+	}
+
 	msm_uport->clk_state = MSM_HS_CLK_ON;
 	return 0;
 }
 
-/* Enable and Disable clocks  (Used for power management) */
-static void msm_hs_pm(struct uart_port *uport, unsigned int state,
-		      unsigned int oldstate)
-{
-	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
-
-	if (use_low_power_rx_wakeup(msm_uport) ||
-	    msm_uport->exit_lpm_cb)
-		return;  /* ignore linux PM states,
-			    use msm_hs_request_clock API */
-
-	switch (state) {
-	case 0:
-		clk_enable(msm_uport->clk);
-		break;
-	case 3:
-		clk_disable(msm_uport->clk);
-		break;
-	default:
-		dev_err(uport->dev, "msm_serial: Unknown PM state %d\n",
-			state);
-	}
-}
-
 /*
  * programs the UARTDM_CSR register with correct bit rates
  *
  * Interrupts should be disabled before we are called, as
  * we modify Set Baud rate
- * Set receive stale interrupt level, dependent on Bit Rate
+ * Set receive stale interrupt level, dependant on Bit Rate
  * Goal is to have around 8 ms before indicate stale.
  * roundup (((Bit Rate * .008) / 10) + 1
  */
 static void msm_hs_set_bps_locked(struct uart_port *uport,
-				  unsigned int bps)
+			       unsigned int bps)
 {
 	unsigned long rxstale;
 	unsigned long data;
@@ -509,63 +491,63 @@
 
 	switch (bps) {
 	case 300:
-		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_75);
+		msm_hs_write(uport, UARTDM_CSR_ADDR, 0x00);
 		rxstale = 1;
 		break;
 	case 600:
-		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_150);
+		msm_hs_write(uport, UARTDM_CSR_ADDR, 0x11);
 		rxstale = 1;
 		break;
 	case 1200:
-		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_300);
+		msm_hs_write(uport, UARTDM_CSR_ADDR, 0x22);
 		rxstale = 1;
 		break;
 	case 2400:
-		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_600);
+		msm_hs_write(uport, UARTDM_CSR_ADDR, 0x33);
 		rxstale = 1;
 		break;
 	case 4800:
-		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_1200);
+		msm_hs_write(uport, UARTDM_CSR_ADDR, 0x44);
 		rxstale = 1;
 		break;
 	case 9600:
-		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_2400);
+		msm_hs_write(uport, UARTDM_CSR_ADDR, 0x55);
 		rxstale = 2;
 		break;
 	case 14400:
-		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_3600);
+		msm_hs_write(uport, UARTDM_CSR_ADDR, 0x66);
 		rxstale = 3;
 		break;
 	case 19200:
-		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_4800);
+		msm_hs_write(uport, UARTDM_CSR_ADDR, 0x77);
 		rxstale = 4;
 		break;
 	case 28800:
-		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_7200);
+		msm_hs_write(uport, UARTDM_CSR_ADDR, 0x88);
 		rxstale = 6;
 		break;
 	case 38400:
-		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_9600);
+		msm_hs_write(uport, UARTDM_CSR_ADDR, 0x99);
 		rxstale = 8;
 		break;
 	case 57600:
-		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_14400);
+		msm_hs_write(uport, UARTDM_CSR_ADDR, 0xaa);
 		rxstale = 16;
 		break;
 	case 76800:
-		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_19200);
+		msm_hs_write(uport, UARTDM_CSR_ADDR, 0xbb);
 		rxstale = 16;
 		break;
 	case 115200:
-		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_28800);
+		msm_hs_write(uport, UARTDM_CSR_ADDR, 0xcc);
 		rxstale = 31;
 		break;
 	case 230400:
-		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_57600);
+		msm_hs_write(uport, UARTDM_CSR_ADDR, 0xee);
 		rxstale = 31;
 		break;
 	case 460800:
-		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_115200);
+		msm_hs_write(uport, UARTDM_CSR_ADDR, 0xff);
 		rxstale = 31;
 		break;
 	case 4000000:
@@ -578,21 +560,28 @@
 	case 1152000:
 	case 1000000:
 	case 921600:
-		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_115200);
+		msm_hs_write(uport, UARTDM_CSR_ADDR, 0xff);
 		rxstale = 31;
 		break;
 	default:
-		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_2400);
+		msm_hs_write(uport, UARTDM_CSR_ADDR, 0xff);
 		/* default to 9600 */
 		bps = 9600;
 		rxstale = 2;
 		break;
 	}
-	if (bps > 460800)
+	/*
+	 * uart baud rate depends on CSR and MND Values
+	 * we are updating CSR before and then calling
+	 * clk_set_rate which updates MND Values. Hence
+	 * dsb requires here.
+	 */
+	mb();
+	if (bps > 460800) {
 		uport->uartclk = bps * 16;
-	else
-		uport->uartclk = UARTCLK;
-
+	} else {
+		uport->uartclk = 7372800;
+	}
 	if (clk_set_rate(msm_uport->clk, uport->uartclk)) {
 		printk(KERN_WARNING "Error setting clock rate on UART\n");
 		return;
@@ -602,6 +591,63 @@
 	data |= UARTDM_IPR_STALE_TIMEOUT_MSB_BMSK & (rxstale << 2);
 
 	msm_hs_write(uport, UARTDM_IPR_ADDR, data);
+	/*
+	 * It is suggested to do reset of transmitter and receiver after
+	 * changing any protocol configuration. Here Baud rate and stale
+	 * timeout are getting updated. Hence reset transmitter and receiver.
+	 */
+	msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX);
+	msm_hs_write(uport, UARTDM_CR_ADDR, RESET_RX);
+}
+
+
+static void msm_hs_set_std_bps_locked(struct uart_port *uport,
+			       unsigned int bps)
+{
+	unsigned long rxstale;
+	unsigned long data;
+
+	switch (bps) {
+	case 9600:
+		msm_hs_write(uport, UARTDM_CSR_ADDR, 0x99);
+		rxstale = 2;
+		break;
+	case 14400:
+		msm_hs_write(uport, UARTDM_CSR_ADDR, 0xaa);
+		rxstale = 3;
+		break;
+	case 19200:
+		msm_hs_write(uport, UARTDM_CSR_ADDR, 0xbb);
+		rxstale = 4;
+		break;
+	case 28800:
+		msm_hs_write(uport, UARTDM_CSR_ADDR, 0xcc);
+		rxstale = 6;
+		break;
+	case 38400:
+		msm_hs_write(uport, UARTDM_CSR_ADDR, 0xdd);
+		rxstale = 8;
+		break;
+	case 57600:
+		msm_hs_write(uport, UARTDM_CSR_ADDR, 0xee);
+		rxstale = 16;
+		break;
+	case 115200:
+		msm_hs_write(uport, UARTDM_CSR_ADDR, 0xff);
+		rxstale = 31;
+		break;
+	default:
+		msm_hs_write(uport, UARTDM_CSR_ADDR, 0x99);
+		/* default to 9600 */
+		bps = 9600;
+		rxstale = 2;
+		break;
+	}
+
+	data = rxstale & UARTDM_IPR_STALE_LSB_BMSK;
+	data |= UARTDM_IPR_STALE_TIMEOUT_MSB_BMSK & (rxstale << 2);
+
+	msm_hs_write(uport, UARTDM_IPR_ADDR, data);
 }
 
 /*
@@ -611,8 +657,8 @@
  * Configure the serial port
  */
 static void msm_hs_set_termios(struct uart_port *uport,
-			       struct ktermios *termios,
-			       struct ktermios *oldtermios)
+				   struct ktermios *termios,
+				   struct ktermios *oldtermios)
 {
 	unsigned int bps;
 	unsigned long data;
@@ -621,7 +667,19 @@
 	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
 
 	spin_lock_irqsave(&uport->lock, flags);
-	clk_enable(msm_uport->clk);
+
+	/*
+	 * Disable Rx channel of UARTDM
+	 * DMA Rx Stall happens if enqueue and flush of Rx command happens
+	 * concurrently. Hence before changing the baud rate/protocol
+	 * configuration and sending flush command to ADM, disable the Rx
+	 * channel of UARTDM.
+	 * Note: should not reset the receiver here immediately as it is not
+	 * suggested to do disable/reset or reset/disable at the same time.
+	 */
+	data = msm_hs_read(uport, UARTDM_DMEN_ADDR);
+	data &= ~UARTDM_RX_DM_EN_BMSK;
+	msm_hs_write(uport, UARTDM_DMEN_ADDR, data);
 
 	/* 300 is the minimum baud support by the driver  */
 	bps = uart_get_baud_rate(uport, termios, oldtermios, 200, 4000000);
@@ -630,18 +688,23 @@
 	if (bps == 200)
 		bps = 3200000;
 
-	msm_hs_set_bps_locked(uport, bps);
+	uport->uartclk = clk_get_rate(msm_uport->clk);
+	if (!uport->uartclk)
+		msm_hs_set_std_bps_locked(uport, bps);
+	else
+		msm_hs_set_bps_locked(uport, bps);
 
 	data = msm_hs_read(uport, UARTDM_MR2_ADDR);
 	data &= ~UARTDM_MR2_PARITY_MODE_BMSK;
 	/* set parity */
 	if (PARENB == (c_cflag & PARENB)) {
-		if (PARODD == (c_cflag & PARODD))
+		if (PARODD == (c_cflag & PARODD)) {
 			data |= ODD_PARITY;
-		else if (CMSPAR == (c_cflag & CMSPAR))
+		} else if (CMSPAR == (c_cflag & CMSPAR)) {
 			data |= SPACE_PARITY;
-		else
+		} else {
 			data |= EVEN_PARITY;
+		}
 	}
 
 	/* Set bits per char */
@@ -697,13 +760,20 @@
 	msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX);
 
 	if (msm_uport->rx.flush == FLUSH_NONE) {
+		wake_lock(&msm_uport->rx.wake_lock);
 		msm_uport->rx.flush = FLUSH_IGNORE;
-		msm_dmov_stop_cmd(msm_uport->dma_rx_channel, NULL, 1);
+		/*
+		 * Before using dmov APIs make sure that
+		 * previous writel are completed. Hence
+		 * dsb requires here.
+		 */
+		mb();
+		/* do discard flush */
+		msm_dmov_flush(msm_uport->dma_rx_channel, 0);
 	}
 
 	msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
-
-	clk_disable(msm_uport->clk);
+	mb();
 	spin_unlock_irqrestore(&uport->lock, flags);
 }
 
@@ -711,22 +781,18 @@
  *  Standard API, Transmitter
  *  Any character in the transmit shift register is sent
  */
-static unsigned int msm_hs_tx_empty(struct uart_port *uport)
+unsigned int msm_hs_tx_empty(struct uart_port *uport)
 {
 	unsigned int data;
 	unsigned int ret = 0;
-	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
-
-	clk_enable(msm_uport->clk);
 
 	data = msm_hs_read(uport, UARTDM_SR_ADDR);
 	if (data & UARTDM_SR_TXEMT_BMSK)
 		ret = TIOCSER_TEMT;
 
-	clk_disable(msm_uport->clk);
-
 	return ret;
 }
+EXPORT_SYMBOL(msm_hs_tx_empty);
 
 /*
  *  Standard API, Stop transmitter.
@@ -753,21 +819,21 @@
 	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
 	unsigned int data;
 
-	clk_enable(msm_uport->clk);
-
 	/* disable dlink */
 	data = msm_hs_read(uport, UARTDM_DMEN_ADDR);
 	data &= ~UARTDM_RX_DM_EN_BMSK;
 	msm_hs_write(uport, UARTDM_DMEN_ADDR, data);
 
+	/* calling DMOV or CLOCK API. Hence mb() */
+	mb();
 	/* Disable the receiver */
-	if (msm_uport->rx.flush == FLUSH_NONE)
-		msm_dmov_stop_cmd(msm_uport->dma_rx_channel, NULL, 1);
-
+	if (msm_uport->rx.flush == FLUSH_NONE) {
+		wake_lock(&msm_uport->rx.wake_lock);
+		/* do discard flush */
+		msm_dmov_flush(msm_uport->dma_rx_channel, 0);
+	}
 	if (msm_uport->rx.flush != FLUSH_SHUTDOWN)
 		msm_uport->rx.flush = FLUSH_STOP;
-
-	clk_disable(msm_uport->clk);
 }
 
 /*  Transmit the next chunk of data */
@@ -775,7 +841,9 @@
 {
 	int left;
 	int tx_count;
+	int aligned_tx_count;
 	dma_addr_t src_addr;
+	dma_addr_t aligned_src_addr;
 	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
 	struct msm_hs_tx *tx = &msm_uport->tx;
 	struct circ_buf *tx_buf = &msm_uport->uport.state->xmit;
@@ -798,8 +866,13 @@
 		tx_count = left;
 
 	src_addr = tx->dma_base + tx_buf->tail;
-	dma_sync_single_for_device(uport->dev, src_addr, tx_count,
-				   DMA_TO_DEVICE);
+	/* Mask the src_addr to align on a cache
+	 * and add those bytes to tx_count */
+	aligned_src_addr = src_addr & ~(dma_get_cache_alignment() - 1);
+	aligned_tx_count = tx_count + src_addr - aligned_src_addr;
+
+	dma_sync_single_for_device(uport->dev, aligned_src_addr,
+			aligned_tx_count, DMA_TO_DEVICE);
 
 	tx->command_ptr->num_rows = (((tx_count + 15) >> 4) << 16) |
 				     ((tx_count + 15) >> 4);
@@ -810,9 +883,6 @@
 
 	*tx->command_ptr_ptr = CMD_PTR_LP | DMOV_CMD_ADDR(tx->mapped_cmd_ptr);
 
-	dma_sync_single_for_device(uport->dev, tx->mapped_cmd_ptr_ptr,
-				   sizeof(u32), DMA_TO_DEVICE);
-
 	/* Save tx_count to use in Callback */
 	tx->tx_count = tx_count;
 	msm_hs_write(uport, UARTDM_NCF_TX_ADDR, tx_count);
@@ -820,6 +890,12 @@
 	/* Disable the tx_ready interrupt */
 	msm_uport->imr_reg &= ~UARTDM_ISR_TX_READY_BMSK;
 	msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
+	/* Calling next DMOV API. Hence mb() here. */
+	mb();
+
+	dma_sync_single_for_device(uport->dev, tx->mapped_cmd_ptr_ptr,
+				   sizeof(u32), DMA_TO_DEVICE);
+
 	msm_dmov_enqueue_cmd(msm_uport->dma_tx_channel, &tx->xfer);
 }
 
@@ -827,37 +903,203 @@
 static void msm_hs_start_rx_locked(struct uart_port *uport)
 {
 	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+	unsigned int buffer_pending = msm_uport->rx.buffer_pending;
+	unsigned int data;
+
+	msm_uport->rx.buffer_pending = 0;
+	if (buffer_pending && hs_serial_debug_mask)
+		printk(KERN_ERR "Error: rx started in buffer state = %x",
+		       buffer_pending);
 
 	msm_hs_write(uport, UARTDM_CR_ADDR, RESET_STALE_INT);
 	msm_hs_write(uport, UARTDM_DMRX_ADDR, UARTDM_RX_BUF_SIZE);
 	msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_ENABLE);
 	msm_uport->imr_reg |= UARTDM_ISR_RXLEV_BMSK;
+
+	/*
+	 * Enable UARTDM Rx Interface as previously it has been
+	 * disable in set_termios before configuring baud rate.
+	 */
+	data = msm_hs_read(uport, UARTDM_DMEN_ADDR);
+	data |= UARTDM_RX_DM_EN_BMSK;
+	msm_hs_write(uport, UARTDM_DMEN_ADDR, data);
 	msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
+	/* Calling next DMOV API. Hence mb() here. */
+	mb();
 
 	msm_uport->rx.flush = FLUSH_NONE;
 	msm_dmov_enqueue_cmd(msm_uport->dma_rx_channel, &msm_uport->rx.xfer);
 
-	/* might have finished RX and be ready to clock off */
-	hrtimer_start(&msm_uport->clk_off_timer, msm_uport->clk_off_delay,
-			HRTIMER_MODE_REL);
+}
+
+static void flip_insert_work(struct work_struct *work)
+{
+	unsigned long flags;
+	int retval;
+	struct msm_hs_port *msm_uport =
+		container_of(work, struct msm_hs_port,
+			     rx.flip_insert_work.work);
+	struct tty_struct *tty = msm_uport->uport.state->port.tty;
+
+	spin_lock_irqsave(&msm_uport->uport.lock, flags);
+	if (msm_uport->rx.buffer_pending == NONE_PENDING) {
+		if (hs_serial_debug_mask)
+			printk(KERN_ERR "Error: No buffer pending in %s",
+			       __func__);
+		return;
+	}
+	if (msm_uport->rx.buffer_pending & FIFO_OVERRUN) {
+		retval = tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+		if (retval)
+			msm_uport->rx.buffer_pending &= ~FIFO_OVERRUN;
+	}
+	if (msm_uport->rx.buffer_pending & PARITY_ERROR) {
+		retval = tty_insert_flip_char(tty, 0, TTY_PARITY);
+		if (retval)
+			msm_uport->rx.buffer_pending &= ~PARITY_ERROR;
+	}
+	if (msm_uport->rx.buffer_pending & CHARS_NORMAL) {
+		int rx_count, rx_offset;
+		rx_count = (msm_uport->rx.buffer_pending & 0xFFFF0000) >> 16;
+		rx_offset = (msm_uport->rx.buffer_pending & 0xFFD0) >> 5;
+		retval = tty_insert_flip_string(tty, msm_uport->rx.buffer +
+						rx_offset, rx_count);
+		msm_uport->rx.buffer_pending &= (FIFO_OVERRUN |
+						 PARITY_ERROR);
+		if (retval != rx_count)
+			msm_uport->rx.buffer_pending |= CHARS_NORMAL |
+				retval << 8 | (rx_count - retval) << 16;
+	}
+	if (msm_uport->rx.buffer_pending)
+		schedule_delayed_work(&msm_uport->rx.flip_insert_work,
+				      msecs_to_jiffies(RETRY_TIMEOUT));
+	else
+		if ((msm_uport->clk_state == MSM_HS_CLK_ON) &&
+		    (msm_uport->rx.flush <= FLUSH_IGNORE)) {
+			if (hs_serial_debug_mask)
+				printk(KERN_WARNING
+				       "msm_serial_hs: "
+				       "Pending buffers cleared. "
+				       "Restarting\n");
+			msm_hs_start_rx_locked(&msm_uport->uport);
+		}
+	spin_unlock_irqrestore(&msm_uport->uport.lock, flags);
+	tty_flip_buffer_push(tty);
+}
+
+static void msm_serial_hs_rx_tlet(unsigned long tlet_ptr)
+{
+	int retval;
+	int rx_count;
+	unsigned long status;
+	unsigned long flags;
+	unsigned int error_f = 0;
+	struct uart_port *uport;
+	struct msm_hs_port *msm_uport;
+	unsigned int flush;
+	struct tty_struct *tty;
+
+	msm_uport = container_of((struct tasklet_struct *)tlet_ptr,
+				 struct msm_hs_port, rx.tlet);
+	uport = &msm_uport->uport;
+	tty = uport->state->port.tty;
+
+	status = msm_hs_read(uport, UARTDM_SR_ADDR);
+
+	spin_lock_irqsave(&uport->lock, flags);
+
+	msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_DISABLE);
+
+	/* overflow is not connect to data in a FIFO */
+	if (unlikely((status & UARTDM_SR_OVERRUN_BMSK) &&
+		     (uport->read_status_mask & CREAD))) {
+		retval = tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+		if (!retval)
+			msm_uport->rx.buffer_pending |= TTY_OVERRUN;
+		uport->icount.buf_overrun++;
+		error_f = 1;
+	}
+
+	if (!(uport->ignore_status_mask & INPCK))
+		status = status & ~(UARTDM_SR_PAR_FRAME_BMSK);
+
+	if (unlikely(status & UARTDM_SR_PAR_FRAME_BMSK)) {
+		/* Can not tell difference between parity & frame error */
+		uport->icount.parity++;
+		error_f = 1;
+		if (uport->ignore_status_mask & IGNPAR) {
+			retval = tty_insert_flip_char(tty, 0, TTY_PARITY);
+			if (!retval)
+				msm_uport->rx.buffer_pending |= TTY_PARITY;
+		}
+	}
+
+	if (error_f)
+		msm_hs_write(uport, UARTDM_CR_ADDR, RESET_ERROR_STATUS);
+
+	if (msm_uport->clk_req_off_state == CLK_REQ_OFF_FLUSH_ISSUED)
+		msm_uport->clk_req_off_state = CLK_REQ_OFF_RXSTALE_FLUSHED;
+	flush = msm_uport->rx.flush;
+	if (flush == FLUSH_IGNORE)
+		if (!msm_uport->rx.buffer_pending)
+			msm_hs_start_rx_locked(uport);
+
+	if (flush == FLUSH_STOP) {
+		msm_uport->rx.flush = FLUSH_SHUTDOWN;
+		wake_up(&msm_uport->rx.wait);
+	}
+	if (flush >= FLUSH_DATA_INVALID)
+		goto out;
+
+	rx_count = msm_hs_read(uport, UARTDM_RX_TOTAL_SNAP_ADDR);
+
+	/* order the read of rx.buffer */
+	rmb();
+
+	if (0 != (uport->read_status_mask & CREAD)) {
+		retval = tty_insert_flip_string(tty, msm_uport->rx.buffer,
+						rx_count);
+		if (retval != rx_count) {
+			msm_uport->rx.buffer_pending |= CHARS_NORMAL |
+				retval << 5 | (rx_count - retval) << 16;
+		}
+	}
+
+	/* order the read of rx.buffer and the start of next rx xfer */
+	wmb();
+
+	if (!msm_uport->rx.buffer_pending)
+		msm_hs_start_rx_locked(uport);
+
+out:
+	if (msm_uport->rx.buffer_pending) {
+		if (hs_serial_debug_mask)
+			printk(KERN_WARNING
+			       "msm_serial_hs: "
+			       "tty buffer exhausted. "
+			       "Stalling\n");
+		schedule_delayed_work(&msm_uport->rx.flip_insert_work
+				      , msecs_to_jiffies(RETRY_TIMEOUT));
+	}
+	/* release wakelock in 500ms, not immediately, because higher layers
+	 * don't always take wakelocks when they should */
+	wake_lock_timeout(&msm_uport->rx.wake_lock, HZ / 2);
+	/* tty_flip_buffer_push() might call msm_hs_start(), so unlock */
+	spin_unlock_irqrestore(&uport->lock, flags);
+	if (flush < FLUSH_DATA_INVALID)
+		tty_flip_buffer_push(tty);
 }
 
 /* Enable the transmitter Interrupt */
-static void msm_hs_start_tx_locked(struct uart_port *uport)
+static void msm_hs_start_tx_locked(struct uart_port *uport )
 {
 	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
 
-	clk_enable(msm_uport->clk);
-
-	if (msm_uport->exit_lpm_cb)
-		msm_uport->exit_lpm_cb(uport);
-
 	if (msm_uport->tx.tx_ready_int_en == 0) {
 		msm_uport->tx.tx_ready_int_en = 1;
-		msm_hs_submit_tx_locked(uport);
+		if (msm_uport->tx.dma_in_flight == 0)
+			msm_hs_submit_tx_locked(uport);
 	}
-
-	clk_disable(msm_uport->clk);
 }
 
 /*
@@ -871,23 +1113,29 @@
 					unsigned int result,
 					struct msm_dmov_errdata *err)
 {
-	unsigned long flags;
 	struct msm_hs_port *msm_uport;
 
-	/* DMA did not finish properly */
-	WARN_ON((((result & RSLT_FIFO_CNTR_BMSK) >> 28) == 1) &&
-		!(result & RSLT_VLD));
+	WARN_ON(result != 0x80000002);  /* DMA did not finish properly */
 
 	msm_uport = container_of(cmd_ptr, struct msm_hs_port, tx.xfer);
 
-	spin_lock_irqsave(&msm_uport->uport.lock, flags);
-	clk_enable(msm_uport->clk);
+	tasklet_schedule(&msm_uport->tx.tlet);
+}
+
+static void msm_serial_hs_tx_tlet(unsigned long tlet_ptr)
+{
+	unsigned long flags;
+	struct msm_hs_port *msm_uport = container_of((struct tasklet_struct *)
+				tlet_ptr, struct msm_hs_port, tx.tlet);
+
+	spin_lock_irqsave(&(msm_uport->uport.lock), flags);
 
 	msm_uport->imr_reg |= UARTDM_ISR_TX_READY_BMSK;
-	msm_hs_write(&msm_uport->uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
+	msm_hs_write(&(msm_uport->uport), UARTDM_IMR_ADDR, msm_uport->imr_reg);
+	/* Calling clk API. Hence mb() requires. */
+	mb();
 
-	clk_disable(msm_uport->clk);
-	spin_unlock_irqrestore(&msm_uport->uport.lock, flags);
+	spin_unlock_irqrestore(&(msm_uport->uport.lock), flags);
 }
 
 /*
@@ -901,87 +1149,11 @@
 					unsigned int result,
 					struct msm_dmov_errdata *err)
 {
-	int retval;
-	int rx_count;
-	unsigned long status;
-	unsigned int error_f = 0;
-	unsigned long flags;
-	unsigned int flush;
-	struct tty_struct *tty;
-	struct uart_port *uport;
 	struct msm_hs_port *msm_uport;
 
 	msm_uport = container_of(cmd_ptr, struct msm_hs_port, rx.xfer);
-	uport = &msm_uport->uport;
 
-	spin_lock_irqsave(&uport->lock, flags);
-	clk_enable(msm_uport->clk);
-
-	tty = uport->state->port.tty;
-
-	msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_DISABLE);
-
-	status = msm_hs_read(uport, UARTDM_SR_ADDR);
-
-	/* overflow is not connect to data in a FIFO */
-	if (unlikely((status & UARTDM_SR_OVERRUN_BMSK) &&
-		     (uport->read_status_mask & CREAD))) {
-		tty_insert_flip_char(tty, 0, TTY_OVERRUN);
-		uport->icount.buf_overrun++;
-		error_f = 1;
-	}
-
-	if (!(uport->ignore_status_mask & INPCK))
-		status = status & ~(UARTDM_SR_PAR_FRAME_BMSK);
-
-	if (unlikely(status & UARTDM_SR_PAR_FRAME_BMSK)) {
-		/* Can not tell difference between parity & frame error */
-		uport->icount.parity++;
-		error_f = 1;
-		if (uport->ignore_status_mask & IGNPAR)
-			tty_insert_flip_char(tty, 0, TTY_PARITY);
-	}
-
-	if (error_f)
-		msm_hs_write(uport, UARTDM_CR_ADDR, RESET_ERROR_STATUS);
-
-	if (msm_uport->clk_req_off_state == CLK_REQ_OFF_FLUSH_ISSUED)
-		msm_uport->clk_req_off_state = CLK_REQ_OFF_RXSTALE_FLUSHED;
-
-	flush = msm_uport->rx.flush;
-	if (flush == FLUSH_IGNORE)
-		msm_hs_start_rx_locked(uport);
-	if (flush == FLUSH_STOP)
-		msm_uport->rx.flush = FLUSH_SHUTDOWN;
-	if (flush >= FLUSH_DATA_INVALID)
-		goto out;
-
-	rx_count = msm_hs_read(uport, UARTDM_RX_TOTAL_SNAP_ADDR);
-
-	if (0 != (uport->read_status_mask & CREAD)) {
-		retval = tty_insert_flip_string(tty, msm_uport->rx.buffer,
-						rx_count);
-		BUG_ON(retval != rx_count);
-	}
-
-	msm_hs_start_rx_locked(uport);
-
-out:
-	clk_disable(msm_uport->clk);
-
-	spin_unlock_irqrestore(&uport->lock, flags);
-
-	if (flush < FLUSH_DATA_INVALID)
-		queue_work(msm_hs_workqueue, &msm_uport->rx.tty_work);
-}
-
-static void msm_hs_tty_flip_buffer_work(struct work_struct *work)
-{
-	struct msm_hs_port *msm_uport =
-			container_of(work, struct msm_hs_port, rx.tty_work);
-	struct tty_struct *tty = msm_uport->uport.state->port.tty;
-
-	tty_flip_buffer_push(tty);
+	tasklet_schedule(&msm_uport->rx.tlet);
 }
 
 /*
@@ -1003,58 +1175,57 @@
 }
 
 /*
- * True enables UART auto RFR, which indicates we are ready for data if the RX
- * buffer is not full. False disables auto RFR, and deasserts RFR to indicate
- * we are not ready for data. Must be called with UART clock on.
+ *  Standard API, Set or clear RFR_signal
+ *
+ * Set RFR high, (Indicate we are not ready for data), we disable auto
+ * ready for receiving and then set RFR_N high. To set RFR to low we just turn
+ * back auto ready for receiving and it should lower RFR signal
+ * when hardware is ready
  */
-static void set_rfr_locked(struct uart_port *uport, int auto_rfr)
-{
-	unsigned int data;
-
-	data = msm_hs_read(uport, UARTDM_MR1_ADDR);
-
-	if (auto_rfr) {
-		/* enable auto ready-for-receiving */
-		data |= UARTDM_MR1_RX_RDY_CTL_BMSK;
-		msm_hs_write(uport, UARTDM_MR1_ADDR, data);
-	} else {
-		/* disable auto ready-for-receiving */
-		data &= ~UARTDM_MR1_RX_RDY_CTL_BMSK;
-		msm_hs_write(uport, UARTDM_MR1_ADDR, data);
-		/* RFR is active low, set high */
-		msm_hs_write(uport, UARTDM_CR_ADDR, RFR_HIGH);
-	}
-}
-
-/*
- *  Standard API, used to set or clear RFR
- */
-static void msm_hs_set_mctrl_locked(struct uart_port *uport,
+void msm_hs_set_mctrl_locked(struct uart_port *uport,
 				    unsigned int mctrl)
 {
-	unsigned int auto_rfr;
-	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+	unsigned int set_rts;
+	unsigned int data;
 
-	clk_enable(msm_uport->clk);
+	/* RTS is active low */
+	set_rts = TIOCM_RTS & mctrl ? 0 : 1;
 
-	auto_rfr = TIOCM_RTS & mctrl ? 1 : 0;
-	set_rfr_locked(uport, auto_rfr);
-
-	clk_disable(msm_uport->clk);
+	data = msm_hs_read(uport, UARTDM_MR1_ADDR);
+	if (set_rts) {
+		/*disable auto ready-for-receiving */
+		data &= ~UARTDM_MR1_RX_RDY_CTL_BMSK;
+		msm_hs_write(uport, UARTDM_MR1_ADDR, data);
+		/* set RFR_N to high */
+		msm_hs_write(uport, UARTDM_CR_ADDR, RFR_HIGH);
+	} else {
+		/* Enable auto ready-for-receiving */
+		data |= UARTDM_MR1_RX_RDY_CTL_BMSK;
+		msm_hs_write(uport, UARTDM_MR1_ADDR, data);
+	}
+	mb();
 }
 
+void msm_hs_set_mctrl(struct uart_port *uport,
+				    unsigned int mctrl)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&uport->lock, flags);
+	msm_hs_set_mctrl_locked(uport, mctrl);
+	spin_unlock_irqrestore(&uport->lock, flags);
+}
+EXPORT_SYMBOL(msm_hs_set_mctrl);
+
 /* Standard API, Enable modem status (CTS) interrupt  */
 static void msm_hs_enable_ms_locked(struct uart_port *uport)
 {
 	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
 
-	clk_enable(msm_uport->clk);
-
 	/* Enable DELTA_CTS Interrupt */
 	msm_uport->imr_reg |= UARTDM_ISR_DELTA_CTS_BMSK;
 	msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
-
-	clk_disable(msm_uport->clk);
+	mb();
 
 }
 
@@ -1066,38 +1237,45 @@
  */
 static void msm_hs_break_ctl(struct uart_port *uport, int ctl)
 {
-	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+	unsigned long flags;
 
-	clk_enable(msm_uport->clk);
+	spin_lock_irqsave(&uport->lock, flags);
 	msm_hs_write(uport, UARTDM_CR_ADDR, ctl ? START_BREAK : STOP_BREAK);
-	clk_disable(msm_uport->clk);
+	mb();
+	spin_unlock_irqrestore(&uport->lock, flags);
 }
 
 static void msm_hs_config_port(struct uart_port *uport, int cfg_flags)
 {
 	unsigned long flags;
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
 
-	spin_lock_irqsave(&uport->lock, flags);
 	if (cfg_flags & UART_CONFIG_TYPE) {
 		uport->type = PORT_MSM;
 		msm_hs_request_port(uport);
 	}
-	spin_unlock_irqrestore(&uport->lock, flags);
+
+	if (is_gsbi_uart(msm_uport)) {
+		if (msm_uport->pclk)
+			clk_prepare_enable(msm_uport->pclk);
+		spin_lock_irqsave(&uport->lock, flags);
+		iowrite32(GSBI_PROTOCOL_UART, msm_uport->mapped_gsbi +
+			  GSBI_CONTROL_ADDR);
+		spin_unlock_irqrestore(&uport->lock, flags);
+		if (msm_uport->pclk)
+			clk_disable_unprepare(msm_uport->pclk);
+	}
 }
 
 /*  Handle CTS changes (Called from interrupt handler) */
 static void msm_hs_handle_delta_cts_locked(struct uart_port *uport)
 {
-	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
-
-	clk_enable(msm_uport->clk);
-
 	/* clear interrupt */
 	msm_hs_write(uport, UARTDM_CR_ADDR, RESET_CTS);
+	/* Calling CLOCK API. Hence mb() requires here. */
+	mb();
 	uport->icount.cts++;
 
-	clk_disable(msm_uport->clk);
-
 	/* clear the IOCTL TIOCMIWAIT if called */
 	wake_up_interruptible(&uport->state->port.delta_msr_wait);
 }
@@ -1107,34 +1285,51 @@
  *        -1 did not clock off, do not retry
  *         1 if we clocked off
  */
-static int msm_hs_check_clock_off_locked(struct uart_port *uport)
+static int msm_hs_check_clock_off(struct uart_port *uport)
 {
 	unsigned long sr_status;
+	unsigned long flags;
 	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
 	struct circ_buf *tx_buf = &uport->state->xmit;
 
+	mutex_lock(&msm_uport->clk_mutex);
+	spin_lock_irqsave(&uport->lock, flags);
+
 	/* Cancel if tx tty buffer is not empty, dma is in flight,
-	 * or tx fifo is not empty, or rx fifo is not empty */
+	 * or tx fifo is not empty */
 	if (msm_uport->clk_state != MSM_HS_CLK_REQUEST_OFF ||
 	    !uart_circ_empty(tx_buf) || msm_uport->tx.dma_in_flight ||
-	    (msm_uport->imr_reg & UARTDM_ISR_TXLEV_BMSK) ||
-	    !(msm_uport->imr_reg & UARTDM_ISR_RXLEV_BMSK))  {
+	    msm_uport->imr_reg & UARTDM_ISR_TXLEV_BMSK) {
+		spin_unlock_irqrestore(&uport->lock, flags);
+		mutex_unlock(&msm_uport->clk_mutex);
 		return -1;
 	}
 
 	/* Make sure the uart is finished with the last byte */
 	sr_status = msm_hs_read(uport, UARTDM_SR_ADDR);
-	if (!(sr_status & UARTDM_SR_TXEMT_BMSK))
+	if (!(sr_status & UARTDM_SR_TXEMT_BMSK)) {
+		spin_unlock_irqrestore(&uport->lock, flags);
+		mutex_unlock(&msm_uport->clk_mutex);
 		return 0;  /* retry */
+	}
 
 	/* Make sure forced RXSTALE flush complete */
 	switch (msm_uport->clk_req_off_state) {
 	case CLK_REQ_OFF_START:
 		msm_uport->clk_req_off_state = CLK_REQ_OFF_RXSTALE_ISSUED;
 		msm_hs_write(uport, UARTDM_CR_ADDR, FORCE_STALE_EVENT);
+		/*
+		 * Before returning make sure that device writel completed.
+		 * Hence mb() requires here.
+		 */
+		mb();
+		spin_unlock_irqrestore(&uport->lock, flags);
+		mutex_unlock(&msm_uport->clk_mutex);
 		return 0;  /* RXSTALE flush not complete - retry */
 	case CLK_REQ_OFF_RXSTALE_ISSUED:
 	case CLK_REQ_OFF_FLUSH_ISSUED:
+		spin_unlock_irqrestore(&uport->lock, flags);
+		mutex_unlock(&msm_uport->clk_mutex);
 		return 0;  /* RXSTALE flush not complete - retry */
 	case CLK_REQ_OFF_RXSTALE_FLUSHED:
 		break;  /* continue */
@@ -1143,45 +1338,60 @@
 	if (msm_uport->rx.flush != FLUSH_SHUTDOWN) {
 		if (msm_uport->rx.flush == FLUSH_NONE)
 			msm_hs_stop_rx_locked(uport);
+
+		spin_unlock_irqrestore(&uport->lock, flags);
+		mutex_unlock(&msm_uport->clk_mutex);
 		return 0;  /* come back later to really clock off */
 	}
 
-	/* we really want to clock off */
-	clk_disable(msm_uport->clk);
-	msm_uport->clk_state = MSM_HS_CLK_OFF;
-
-	if (use_low_power_rx_wakeup(msm_uport)) {
-		msm_uport->rx_wakeup.ignore = 1;
-		enable_irq(msm_uport->rx_wakeup.irq);
-	}
-	return 1;
-}
-
-static enum hrtimer_restart msm_hs_clk_off_retry(struct hrtimer *timer)
-{
-	unsigned long flags;
-	int ret = HRTIMER_NORESTART;
-	struct msm_hs_port *msm_uport = container_of(timer, struct msm_hs_port,
-						     clk_off_timer);
-	struct uart_port *uport = &msm_uport->uport;
-
-	spin_lock_irqsave(&uport->lock, flags);
-
-	if (!msm_hs_check_clock_off_locked(uport)) {
-		hrtimer_forward_now(timer, msm_uport->clk_off_delay);
-		ret = HRTIMER_RESTART;
-	}
-
 	spin_unlock_irqrestore(&uport->lock, flags);
 
-	return ret;
+	/* we really want to clock off */
+	clk_disable_unprepare(msm_uport->clk);
+	if (msm_uport->pclk)
+		clk_disable_unprepare(msm_uport->pclk);
+
+	msm_uport->clk_state = MSM_HS_CLK_OFF;
+
+	spin_lock_irqsave(&uport->lock, flags);
+	if (use_low_power_wakeup(msm_uport)) {
+		msm_uport->wakeup.ignore = 1;
+		enable_irq(msm_uport->wakeup.irq);
+	}
+	wake_unlock(&msm_uport->dma_wake_lock);
+
+	spin_unlock_irqrestore(&uport->lock, flags);
+	mutex_unlock(&msm_uport->clk_mutex);
+	return 1;
+}
+
+static void hsuart_clock_off_work(struct work_struct *w)
+{
+	struct msm_hs_port *msm_uport = container_of(w, struct msm_hs_port,
+							clock_off_w);
+	struct uart_port *uport = &msm_uport->uport;
+
+	if (!msm_hs_check_clock_off(uport)) {
+		hrtimer_start(&msm_uport->clk_off_timer,
+				msm_uport->clk_off_delay,
+				HRTIMER_MODE_REL);
+	}
+}
+
+static enum hrtimer_restart msm_hs_clk_off_retry(struct hrtimer *timer)
+{
+	struct msm_hs_port *msm_uport = container_of(timer, struct msm_hs_port,
+							clk_off_timer);
+
+	queue_work(msm_uport->hsuart_wq, &msm_uport->clock_off_w);
+	return HRTIMER_NORESTART;
 }
 
 static irqreturn_t msm_hs_isr(int irq, void *dev)
 {
 	unsigned long flags;
 	unsigned long isr_status;
-	struct msm_hs_port *msm_uport = dev;
+	struct msm_hs_port *msm_uport = (struct msm_hs_port *)dev;
 	struct uart_port *uport = &msm_uport->uport;
 	struct circ_buf *tx_buf = &uport->state->xmit;
 	struct msm_hs_tx *tx = &msm_uport->tx;
@@ -1193,20 +1403,29 @@
 
 	/* Uart RX starting */
 	if (isr_status & UARTDM_ISR_RXLEV_BMSK) {
+		wake_lock(&rx->wake_lock);  /* hold wakelock while rx dma */
 		msm_uport->imr_reg &= ~UARTDM_ISR_RXLEV_BMSK;
 		msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
+		/* Complete device write for IMR. Hence mb() requires. */
+		mb();
 	}
 	/* Stale rx interrupt */
 	if (isr_status & UARTDM_ISR_RXSTALE_BMSK) {
 		msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_DISABLE);
 		msm_hs_write(uport, UARTDM_CR_ADDR, RESET_STALE_INT);
+		/*
+		 * Complete device write before calling DMOV API. Hence
+		 * mb() requires here.
+		 */
+		mb();
 
 		if (msm_uport->clk_req_off_state == CLK_REQ_OFF_RXSTALE_ISSUED)
 			msm_uport->clk_req_off_state =
-					CLK_REQ_OFF_FLUSH_ISSUED;
+				CLK_REQ_OFF_FLUSH_ISSUED;
+
 		if (rx->flush == FLUSH_NONE) {
 			rx->flush = FLUSH_DATA_READY;
-			msm_dmov_stop_cmd(msm_uport->dma_rx_channel, NULL, 1);
+			msm_dmov_flush(msm_uport->dma_rx_channel, 1);
 		}
 	}
 	/* tx ready interrupt */
@@ -1219,7 +1438,11 @@
 			msm_hs_write(uport, UARTDM_IMR_ADDR,
 				     msm_uport->imr_reg);
 		}
-
+		/*
+		 * Complete both writes before starting new TX.
+		 * Hence mb() requires here.
+		 */
+		mb();
 		/* Complete DMA TX transactions and submit new transactions */
 		tx_buf->tail = (tx_buf->tail + tx->tx_count) & ~UART_XMIT_SIZE;
 
@@ -1236,10 +1459,12 @@
 		/* TX FIFO is empty */
 		msm_uport->imr_reg &= ~UARTDM_ISR_TXLEV_BMSK;
 		msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
-		if (!msm_hs_check_clock_off_locked(uport))
-			hrtimer_start(&msm_uport->clk_off_timer,
-				      msm_uport->clk_off_delay,
-				      HRTIMER_MODE_REL);
+		/*
+		 * Complete device write before starting clock_off request.
+		 * Hence mb() requires here.
+		 */
+		mb();
+		queue_work(msm_uport->hsuart_wq, &msm_uport->clock_off_w);
 	}
 
 	/* Change in CTS interrupt */
@@ -1251,51 +1476,60 @@
 	return IRQ_HANDLED;
 }
 
-void msm_hs_request_clock_off_locked(struct uart_port *uport)
-{
+/* request to turn off uart clock once pending TX is flushed */
+void msm_hs_request_clock_off(struct uart_port *uport) {
+	unsigned long flags;
 	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
 
+	spin_lock_irqsave(&uport->lock, flags);
 	if (msm_uport->clk_state == MSM_HS_CLK_ON) {
 		msm_uport->clk_state = MSM_HS_CLK_REQUEST_OFF;
 		msm_uport->clk_req_off_state = CLK_REQ_OFF_START;
-		if (!use_low_power_rx_wakeup(msm_uport))
-			set_rfr_locked(uport, 0);
 		msm_uport->imr_reg |= UARTDM_ISR_TXLEV_BMSK;
 		msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
+		/*
+		 * Complete device write before retuning back.
+		 * Hence mb() requires here.
+		 */
+		mb();
 	}
-}
-
-/**
- * msm_hs_request_clock_off - request to (i.e. asynchronously) turn off uart
- * clock once pending TX is flushed and Rx DMA command is terminated.
- * @uport: uart_port structure for the device instance.
- *
- * This functions puts the device into a partially active low power mode. It
- * waits to complete all pending tx transactions, flushes ongoing Rx DMA
- * command and terminates UART side Rx transaction, puts UART HW in non DMA
- * mode and then clocks off the device. A client calls this when no UART
- * data is expected. msm_request_clock_on() must be called before any further
- * UART can be sent or received.
- */
-void msm_hs_request_clock_off(struct uart_port *uport)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&uport->lock, flags);
-	msm_hs_request_clock_off_locked(uport);
 	spin_unlock_irqrestore(&uport->lock, flags);
 }
+EXPORT_SYMBOL(msm_hs_request_clock_off);
 
-void msm_hs_request_clock_on_locked(struct uart_port *uport)
+void msm_hs_request_clock_on(struct uart_port *uport)
 {
 	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+	unsigned long flags;
 	unsigned int data;
+	int ret = 0;
+
+	mutex_lock(&msm_uport->clk_mutex);
+	spin_lock_irqsave(&uport->lock, flags);
 
 	switch (msm_uport->clk_state) {
 	case MSM_HS_CLK_OFF:
-		clk_enable(msm_uport->clk);
-		disable_irq_nosync(msm_uport->rx_wakeup.irq);
-		/* fall-through */
+		wake_lock(&msm_uport->dma_wake_lock);
+		disable_irq_nosync(msm_uport->wakeup.irq);
+		spin_unlock_irqrestore(&uport->lock, flags);
+		ret = clk_prepare_enable(msm_uport->clk);
+		if (ret) {
+			dev_err(uport->dev, "Clock ON Failure"
+			"For UART CLK Stalling HSUART\n");
+			break;
+		}
+
+		if (msm_uport->pclk) {
+			ret = clk_prepare_enable(msm_uport->pclk);
+			if (unlikely(ret)) {
+				clk_disable_unprepare(msm_uport->clk);
+				dev_err(uport->dev, "Clock ON Failure"
+				"For UART Pclk Stalling HSUART\n");
+				break;
+			}
+		}
+		spin_lock_irqsave(&uport->lock, flags);
+		/* else fall-through */
 	case MSM_HS_CLK_REQUEST_OFF:
 		if (msm_uport->rx.flush == FLUSH_STOP ||
 		    msm_uport->rx.flush == FLUSH_SHUTDOWN) {
@@ -1303,12 +1537,12 @@
 			data = msm_hs_read(uport, UARTDM_DMEN_ADDR);
 			data |= UARTDM_RX_DM_EN_BMSK;
 			msm_hs_write(uport, UARTDM_DMEN_ADDR, data);
+			/* Complete above device write. Hence mb() here. */
+			mb();
 		}
 		hrtimer_try_to_cancel(&msm_uport->clk_off_timer);
 		if (msm_uport->rx.flush == FLUSH_SHUTDOWN)
 			msm_hs_start_rx_locked(uport);
-		if (!use_low_power_rx_wakeup(msm_uport))
-			set_rfr_locked(uport, 1);
 		if (msm_uport->rx.flush == FLUSH_STOP)
 			msm_uport->rx.flush = FLUSH_IGNORE;
 		msm_uport->clk_state = MSM_HS_CLK_ON;
@@ -1318,40 +1552,26 @@
 	case MSM_HS_CLK_PORT_OFF:
 		break;
 	}
-}
 
-/**
- * msm_hs_request_clock_on - Switch the device from partially active low
- * power mode to fully active (i.e. clock on) mode.
- * @uport: uart_port structure for the device.
- *
- * This function switches on the input clock, puts UART HW into DMA mode
- * and enqueues an Rx DMA command if the device was in partially active
- * mode. It has no effect if called with the device in inactive state.
- */
-void msm_hs_request_clock_on(struct uart_port *uport)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&uport->lock, flags);
-	msm_hs_request_clock_on_locked(uport);
 	spin_unlock_irqrestore(&uport->lock, flags);
+	mutex_unlock(&msm_uport->clk_mutex);
 }
+EXPORT_SYMBOL(msm_hs_request_clock_on);
 
-static irqreturn_t msm_hs_rx_wakeup_isr(int irq, void *dev)
+static irqreturn_t msm_hs_wakeup_isr(int irq, void *dev)
 {
 	unsigned int wakeup = 0;
 	unsigned long flags;
-	struct msm_hs_port *msm_uport = dev;
+	struct msm_hs_port *msm_uport = (struct msm_hs_port *)dev;
 	struct uart_port *uport = &msm_uport->uport;
 	struct tty_struct *tty = NULL;
 
 	spin_lock_irqsave(&uport->lock, flags);
-	if (msm_uport->clk_state == MSM_HS_CLK_OFF) {
-		/* ignore the first irq - it is a pending irq that occurred
+	if (msm_uport->clk_state == MSM_HS_CLK_OFF)  {
+		/* ignore the first irq - it is a pending irq that occured
 		 * before enable_irq() */
-		if (msm_uport->rx_wakeup.ignore)
-			msm_uport->rx_wakeup.ignore = 0;
+		if (msm_uport->wakeup.ignore)
+			msm_uport->wakeup.ignore = 0;
 		else
 			wakeup = 1;
 	}
@@ -1359,24 +1579,27 @@
 	if (wakeup) {
 		/* the uart was clocked off during an rx, wake up and
 		 * optionally inject char into tty rx */
-		msm_hs_request_clock_on_locked(uport);
-		if (msm_uport->rx_wakeup.inject_rx) {
+		spin_unlock_irqrestore(&uport->lock, flags);
+		msm_hs_request_clock_on(uport);
+		spin_lock_irqsave(&uport->lock, flags);
+		if (msm_uport->wakeup.inject_rx) {
 			tty = uport->state->port.tty;
 			tty_insert_flip_char(tty,
-					     msm_uport->rx_wakeup.rx_to_inject,
+					     msm_uport->wakeup.rx_to_inject,
 					     TTY_NORMAL);
-			queue_work(msm_hs_workqueue, &msm_uport->rx.tty_work);
 		}
 	}
 
 	spin_unlock_irqrestore(&uport->lock, flags);
 
+	if (wakeup && msm_uport->wakeup.inject_rx)
+		tty_flip_buffer_push(tty);
 	return IRQ_HANDLED;
 }
 
 static const char *msm_hs_type(struct uart_port *port)
 {
-	return (port->type == PORT_MSM) ? "MSM_HS_UART" : NULL;
+	return ("MSM HS UART");
 }
 
 /* Called when port is opened */
@@ -1389,7 +1612,6 @@
 	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
 	struct circ_buf *tx_buf = &uport->state->xmit;
 	struct msm_hs_tx *tx = &msm_uport->tx;
-	struct msm_hs_rx *rx = &msm_uport->rx;
 
 	rfr_level = uport->fifosize;
 	if (rfr_level > 16)
@@ -1398,15 +1620,13 @@
 	tx->dma_base = dma_map_single(uport->dev, tx_buf->buf, UART_XMIT_SIZE,
 				      DMA_TO_DEVICE);
 
-	/* do not let tty layer execute RX in global workqueue, use a
-	 * dedicated workqueue managed by this driver */
-	uport->state->port.tty->low_latency = 1;
-
+	wake_lock(&msm_uport->dma_wake_lock);
 	/* turn on uart clk */
-	ret = msm_hs_init_clk_locked(uport);
+	ret = msm_hs_init_clk(uport);
 	if (unlikely(ret)) {
-		printk(KERN_ERR "Turning uartclk failed!\n");
-		goto err_msm_hs_init_clk;
+		pr_err("Turning ON uartclk error\n");
+		wake_unlock(&msm_uport->dma_wake_lock);
+		return ret;
 	}
 
 	/* Set auto RFR Level */
@@ -1447,7 +1667,6 @@
 	tx->dma_in_flight = 0;
 
 	tx->xfer.complete_func = msm_hs_dmov_tx_callback;
-	tx->xfer.execute_func = NULL;
 
 	tx->command_ptr->cmd = CMD_LC |
 	    CMD_DST_CRCI(msm_uport->dma_tx_crci) | CMD_MODE_BOX;
@@ -1460,49 +1679,47 @@
 	tx->command_ptr->dst_row_addr =
 	    msm_uport->uport.mapbase + UARTDM_TF_ADDR;
 
-
-	/* Turn on Uart Receive */
-	rx->xfer.complete_func = msm_hs_dmov_rx_callback;
-	rx->xfer.execute_func = NULL;
-
-	rx->command_ptr->cmd = CMD_LC |
-	    CMD_SRC_CRCI(msm_uport->dma_rx_crci) | CMD_MODE_BOX;
-
-	rx->command_ptr->src_dst_len = (MSM_UARTDM_BURST_SIZE << 16)
-					   | (MSM_UARTDM_BURST_SIZE);
-	rx->command_ptr->row_offset =  MSM_UARTDM_BURST_SIZE;
-	rx->command_ptr->src_row_addr = uport->mapbase + UARTDM_RF_ADDR;
-
-
 	msm_uport->imr_reg |= UARTDM_ISR_RXSTALE_BMSK;
 	/* Enable reading the current CTS, no harm even if CTS is ignored */
 	msm_uport->imr_reg |= UARTDM_ISR_CURRENT_CTS_BMSK;
 
 	msm_hs_write(uport, UARTDM_TFWR_ADDR, 0);  /* TXLEV on empty TX fifo */
+	/*
+	 * Complete all device write related configuration before
+	 * queuing RX request. Hence mb() requires here.
+	 */
+	mb();
 
+	if (use_low_power_wakeup(msm_uport)) {
+		ret = irq_set_irq_wake(msm_uport->wakeup.irq, 1);
+		if (unlikely(ret)) {
+			pr_err("%s():Err setting wakeup irq\n", __func__);
+			goto deinit_uart_clk;
+		}
+	}
 
 	ret = request_irq(uport->irq, msm_hs_isr, IRQF_TRIGGER_HIGH,
 			  "msm_hs_uart", msm_uport);
 	if (unlikely(ret)) {
-		printk(KERN_ERR "Request msm_hs_uart IRQ failed!\n");
-		goto err_request_irq;
+		pr_err("%s():Error getting uart irq\n", __func__);
+		goto free_wake_irq;
 	}
-	if (use_low_power_rx_wakeup(msm_uport)) {
-		ret = request_irq(msm_uport->rx_wakeup.irq,
-				  msm_hs_rx_wakeup_isr,
-				  IRQF_TRIGGER_FALLING,
-				  "msm_hs_rx_wakeup", msm_uport);
+	if (use_low_power_wakeup(msm_uport)) {
+
+		ret = request_threaded_irq(msm_uport->wakeup.irq, NULL,
+					msm_hs_wakeup_isr,
+					IRQF_TRIGGER_FALLING,
+					"msm_hs_wakeup", msm_uport);
+
 		if (unlikely(ret)) {
-			printk(KERN_ERR "Request msm_hs_rx_wakeup IRQ failed!\n");
-			free_irq(uport->irq, msm_uport);
-			goto err_request_irq;
+			pr_err("%s():Err getting uart wakeup_irq\n", __func__);
+			goto free_uart_irq;
 		}
-		disable_irq(msm_uport->rx_wakeup.irq);
+		disable_irq(msm_uport->wakeup.irq);
 	}
 
 	spin_lock_irqsave(&uport->lock, flags);
 
-	msm_hs_write(uport, UARTDM_RFWR_ADDR, 0);
 	msm_hs_start_rx_locked(uport);
 
 	spin_unlock_irqrestore(&uport->lock, flags);
@@ -1513,15 +1730,21 @@
 
 	return 0;
 
-err_request_irq:
-err_msm_hs_init_clk:
-	dma_unmap_single(uport->dev, tx->dma_base,
-				UART_XMIT_SIZE, DMA_TO_DEVICE);
+free_uart_irq:
+	free_irq(uport->irq, msm_uport);
+free_wake_irq:
+	irq_set_irq_wake(msm_uport->wakeup.irq, 0);
+deinit_uart_clk:
+	clk_disable_unprepare(msm_uport->clk);
+	if (msm_uport->pclk)
+		clk_disable_unprepare(msm_uport->pclk);
+	wake_unlock(&msm_uport->dma_wake_lock);
+
 	return ret;
 }
 
 /* Initialize tx and rx data structures */
-static int __devinit uartdm_init_port(struct uart_port *uport)
+static int uartdm_init_port(struct uart_port *uport)
 {
 	int ret = 0;
 	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
@@ -1536,7 +1759,7 @@
 	tx->command_ptr_ptr = kmalloc(sizeof(u32), GFP_KERNEL | __GFP_DMA);
 	if (!tx->command_ptr_ptr) {
 		ret = -ENOMEM;
-		goto err_tx_command_ptr_ptr;
+		goto free_tx_command_ptr;
 	}
 
 	tx->mapped_cmd_ptr = dma_map_single(uport->dev, tx->command_ptr,
@@ -1547,20 +1770,28 @@
 	tx->xfer.cmdptr = DMOV_CMD_ADDR(tx->mapped_cmd_ptr_ptr);
 
 	init_waitqueue_head(&rx->wait);
+	wake_lock_init(&rx->wake_lock, WAKE_LOCK_SUSPEND, "msm_serial_hs_rx");
+	wake_lock_init(&msm_uport->dma_wake_lock, WAKE_LOCK_SUSPEND,
+		       "msm_serial_hs_dma");
+
+	tasklet_init(&rx->tlet, msm_serial_hs_rx_tlet,
+			(unsigned long) &rx->tlet);
+	tasklet_init(&tx->tlet, msm_serial_hs_tx_tlet,
+			(unsigned long) &tx->tlet);
 
 	rx->pool = dma_pool_create("rx_buffer_pool", uport->dev,
 				   UARTDM_RX_BUF_SIZE, 16, 0);
 	if (!rx->pool) {
 		pr_err("%s(): cannot allocate rx_buffer_pool", __func__);
 		ret = -ENOMEM;
-		goto err_dma_pool_create;
+		goto exit_tasket_init;
 	}
 
 	rx->buffer = dma_pool_alloc(rx->pool, GFP_KERNEL, &rx->rbuffer);
 	if (!rx->buffer) {
 		pr_err("%s(): cannot allocate rx->buffer", __func__);
 		ret = -ENOMEM;
-		goto err_dma_pool_alloc;
+		goto free_pool;
 	}
 
 	/* Allocate the command pointer. Needs to be 64 bit aligned */
@@ -1568,14 +1799,14 @@
 	if (!rx->command_ptr) {
 		pr_err("%s(): cannot allocate rx->command_ptr", __func__);
 		ret = -ENOMEM;
-		goto err_rx_command_ptr;
+		goto free_rx_buffer;
 	}
 
 	rx->command_ptr_ptr = kmalloc(sizeof(u32), GFP_KERNEL | __GFP_DMA);
 	if (!rx->command_ptr_ptr) {
 		pr_err("%s(): cannot allocate rx->command_ptr_ptr", __func__);
 		ret = -ENOMEM;
-		goto err_rx_command_ptr_ptr;
+		goto free_rx_command_ptr;
 	}
 
 	rx->command_ptr->num_rows = ((UARTDM_RX_BUF_SIZE >> 4) << 16) |
@@ -1583,6 +1814,19 @@
 
 	rx->command_ptr->dst_row_addr = rx->rbuffer;
 
+	/* Set up Uart Receive */
+	msm_hs_write(uport, UARTDM_RFWR_ADDR, 0);
+
+	rx->xfer.complete_func = msm_hs_dmov_rx_callback;
+
+	rx->command_ptr->cmd = CMD_LC |
+	    CMD_SRC_CRCI(msm_uport->dma_rx_crci) | CMD_MODE_BOX;
+
+	rx->command_ptr->src_dst_len = (MSM_UARTDM_BURST_SIZE << 16)
+					   | (MSM_UARTDM_BURST_SIZE);
+	rx->command_ptr->row_offset =  MSM_UARTDM_BURST_SIZE;
+	rx->command_ptr->src_row_addr = uport->mapbase + UARTDM_RF_ADDR;
+
 	rx->mapped_cmd_ptr = dma_map_single(uport->dev, rx->command_ptr,
 					    sizeof(dmov_box), DMA_TO_DEVICE);
 
@@ -1592,24 +1836,32 @@
 					    sizeof(u32), DMA_TO_DEVICE);
 	rx->xfer.cmdptr = DMOV_CMD_ADDR(rx->cmdptr_dmaaddr);
 
-	INIT_WORK(&rx->tty_work, msm_hs_tty_flip_buffer_work);
+	INIT_DELAYED_WORK(&rx->flip_insert_work, flip_insert_work);
 
 	return ret;
 
-err_rx_command_ptr_ptr:
+free_rx_command_ptr:
 	kfree(rx->command_ptr);
-err_rx_command_ptr:
+
+free_rx_buffer:
 	dma_pool_free(msm_uport->rx.pool, msm_uport->rx.buffer,
-						msm_uport->rx.rbuffer);
-err_dma_pool_alloc:
+			msm_uport->rx.rbuffer);
+
+free_pool:
 	dma_pool_destroy(msm_uport->rx.pool);
-err_dma_pool_create:
+
+exit_tasket_init:
+	wake_lock_destroy(&msm_uport->rx.wake_lock);
+	wake_lock_destroy(&msm_uport->dma_wake_lock);
+	tasklet_kill(&msm_uport->tx.tlet);
+	tasklet_kill(&msm_uport->rx.tlet);
 	dma_unmap_single(uport->dev, msm_uport->tx.mapped_cmd_ptr_ptr,
-				sizeof(u32), DMA_TO_DEVICE);
+			sizeof(u32), DMA_TO_DEVICE);
 	dma_unmap_single(uport->dev, msm_uport->tx.mapped_cmd_ptr,
-				sizeof(dmov_box), DMA_TO_DEVICE);
+			sizeof(dmov_box), DMA_TO_DEVICE);
 	kfree(msm_uport->tx.command_ptr_ptr);
-err_tx_command_ptr_ptr:
+
+free_tx_command_ptr:
 	kfree(msm_uport->tx.command_ptr);
 	return ret;
 }
@@ -1620,8 +1872,7 @@
 	struct uart_port *uport;
 	struct msm_hs_port *msm_uport;
 	struct resource *resource;
-	const struct msm_serial_hs_platform_data *pdata =
-						pdev->dev.platform_data;
+	struct msm_serial_hs_platform_data *pdata = pdev->dev.platform_data;
 
 	if (pdev->id < 0 || pdev->id >= UARTDM_NR) {
 		printk(KERN_ERR "Invalid plaform device ID = %d\n", pdev->id);
@@ -1636,40 +1887,37 @@
 	resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (unlikely(!resource))
 		return -ENXIO;
+	uport->mapbase = resource->start;  /* virtual address */
 
-	uport->mapbase = resource->start;
+	uport->membase = ioremap(uport->mapbase, PAGE_SIZE);
+	if (unlikely(!uport->membase))
+		return -ENOMEM;
+
 	uport->irq = platform_get_irq(pdev, 0);
-	if (unlikely(uport->irq < 0))
+	if (unlikely((int)uport->irq < 0))
 		return -ENXIO;
 
-	if (unlikely(irq_set_irq_wake(uport->irq, 1)))
-		return -ENXIO;
-
-	if (pdata == NULL || pdata->rx_wakeup_irq < 0)
-		msm_uport->rx_wakeup.irq = -1;
-	else {
-		msm_uport->rx_wakeup.irq = pdata->rx_wakeup_irq;
-		msm_uport->rx_wakeup.ignore = 1;
-		msm_uport->rx_wakeup.inject_rx = pdata->inject_rx_on_wakeup;
-		msm_uport->rx_wakeup.rx_to_inject = pdata->rx_to_inject;
-
-		if (unlikely(msm_uport->rx_wakeup.irq < 0))
-			return -ENXIO;
-
-		if (unlikely(irq_set_irq_wake(msm_uport->rx_wakeup.irq, 1)))
-			return -ENXIO;
-	}
-
 	if (pdata == NULL)
-		msm_uport->exit_lpm_cb = NULL;
-	else
-		msm_uport->exit_lpm_cb = pdata->exit_lpm_cb;
+		msm_uport->wakeup.irq = -1;
+	else {
+		msm_uport->wakeup.irq = pdata->wakeup_irq;
+		msm_uport->wakeup.ignore = 1;
+		msm_uport->wakeup.inject_rx = pdata->inject_rx_on_wakeup;
+		msm_uport->wakeup.rx_to_inject = pdata->rx_to_inject;
+
+		if (unlikely(msm_uport->wakeup.irq < 0))
+			return -ENXIO;
+
+		if (pdata->gpio_config)
+			if (unlikely(pdata->gpio_config(1)))
+				dev_err(uport->dev, "Cannot configure"
+					"gpios\n");
+	}
 
 	resource = platform_get_resource_byname(pdev, IORESOURCE_DMA,
 						"uartdm_channels");
 	if (unlikely(!resource))
 		return -ENXIO;
-
 	msm_uport->dma_tx_channel = resource->start;
 	msm_uport->dma_rx_channel = resource->end;
 
@@ -1677,23 +1925,70 @@
 						"uartdm_crci");
 	if (unlikely(!resource))
 		return -ENXIO;
-
 	msm_uport->dma_tx_crci = resource->start;
 	msm_uport->dma_rx_crci = resource->end;
 
 	uport->iotype = UPIO_MEM;
-	uport->fifosize = UART_FIFOSIZE;
+	uport->fifosize = 64;
 	uport->ops = &msm_hs_ops;
 	uport->flags = UPF_BOOT_AUTOCONF;
-	uport->uartclk = UARTCLK;
+	uport->uartclk = 7372800;
 	msm_uport->imr_reg = 0x0;
-	msm_uport->clk = clk_get(&pdev->dev, "uartdm_clk");
+
+	msm_uport->clk = clk_get(&pdev->dev, "core_clk");
 	if (IS_ERR(msm_uport->clk))
 		return PTR_ERR(msm_uport->clk);
 
-	ret = uartdm_init_port(uport);
-	if (unlikely(ret))
+	msm_uport->pclk = clk_get(&pdev->dev, "iface_clk");
+	/*
+	 * Some configurations do not require explicit pclk control so
+	 * do not flag error on pclk get failure.
+	 */
+	if (IS_ERR(msm_uport->pclk))
+		msm_uport->pclk = NULL;
+
+	ret = clk_set_rate(msm_uport->clk, uport->uartclk);
+	if (ret) {
+		printk(KERN_WARNING "Error setting clock rate on UART\n");
 		return ret;
+	}
+
+	msm_uport->hsuart_wq = alloc_workqueue("k_hsuart",
+					WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
+	if (!msm_uport->hsuart_wq) {
+		pr_err("%s(): Unable to create workqueue hsuart_wq\n",
+								__func__);
+		return -ENOMEM;
+	}
+
+	INIT_WORK(&msm_uport->clock_off_w, hsuart_clock_off_work);
+	mutex_init(&msm_uport->clk_mutex);
+
+	clk_prepare_enable(msm_uport->clk);
+	if (msm_uport->pclk)
+		clk_prepare_enable(msm_uport->pclk);
+
+	ret = uartdm_init_port(uport);
+	if (unlikely(ret)) {
+		clk_disable_unprepare(msm_uport->clk);
+		if (msm_uport->pclk)
+			clk_disable_unprepare(msm_uport->pclk);
+		return ret;
+	}
+
+	/* configure the CR Protection to Enable */
+	msm_hs_write(uport, UARTDM_CR_ADDR, CR_PROTECTION_EN);
+
+	clk_disable_unprepare(msm_uport->clk);
+	if (msm_uport->pclk)
+		clk_disable_unprepare(msm_uport->pclk);
+
+	/*
+	 * Enable Command register protection before going ahead as this hw
+	 * configuration makes sure that issued cmd to CR register gets complete
+	 * before next issued cmd start. Hence mb() requires here.
+	 */
+	mb();
 
 	msm_uport->clk_state = MSM_HS_CLK_PORT_OFF;
 	hrtimer_init(&msm_uport->clk_off_timer, CLOCK_MONOTONIC,
@@ -1701,43 +1996,45 @@
 	msm_uport->clk_off_timer.function = msm_hs_clk_off_retry;
 	msm_uport->clk_off_delay = ktime_set(0, 1000000);  /* 1ms */
 
+	ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_clock.attr);
+	if (unlikely(ret))
+		return ret;
+
+	msm_serial_debugfs_init(msm_uport, pdev->id);
+
 	uport->line = pdev->id;
 	return uart_add_one_port(&msm_hs_driver, uport);
 }
 
 static int __init msm_serial_hs_init(void)
 {
-	int ret, i;
+	int ret;
+	int i;
 
 	/* Init all UARTS as non-configured */
 	for (i = 0; i < UARTDM_NR; i++)
 		q_uart_port[i].uport.type = PORT_UNKNOWN;
 
-	msm_hs_workqueue = create_singlethread_workqueue("msm_serial_hs");
-	if (unlikely(!msm_hs_workqueue))
-		return -ENOMEM;
-
 	ret = uart_register_driver(&msm_hs_driver);
 	if (unlikely(ret)) {
-		printk(KERN_ERR "%s failed to load\n", __func__);
-		goto err_uart_register_driver;
+		printk(KERN_ERR "%s failed to load\n", __FUNCTION__);
+		return ret;
 	}
+	debug_base = debugfs_create_dir("msm_serial_hs", NULL);
+	if (IS_ERR_OR_NULL(debug_base))
+		pr_info("msm_serial_hs: Cannot create debugfs dir\n");
 
 	ret = platform_driver_register(&msm_serial_hs_platform_driver);
 	if (ret) {
-		printk(KERN_ERR "%s failed to load\n", __func__);
-		goto err_platform_driver_register;
+		printk(KERN_ERR "%s failed to load\n", __FUNCTION__);
+		debugfs_remove_recursive(debug_base);
+		uart_unregister_driver(&msm_hs_driver);
+		return ret;
 	}
 
-	return ret;
-
-err_platform_driver_register:
-	uart_unregister_driver(&msm_hs_driver);
-err_uart_register_driver:
-	destroy_workqueue(msm_hs_workqueue);
+	printk(KERN_INFO "msm_serial_hs module loaded\n");
 	return ret;
 }
-module_init(msm_serial_hs_init);
 
 /*
  *  Called by the upper layer when port is closed.
@@ -1746,56 +2043,63 @@
  */
 static void msm_hs_shutdown(struct uart_port *uport)
 {
-	unsigned long flags;
 	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
 
 	BUG_ON(msm_uport->rx.flush < FLUSH_STOP);
+	tasklet_kill(&msm_uport->tx.tlet);
+	wait_event(msm_uport->rx.wait, msm_uport->rx.flush == FLUSH_SHUTDOWN);
+	tasklet_kill(&msm_uport->rx.tlet);
+	cancel_delayed_work_sync(&msm_uport->rx.flip_insert_work);
 
-	spin_lock_irqsave(&uport->lock, flags);
-	clk_enable(msm_uport->clk);
+	flush_workqueue(msm_uport->hsuart_wq);
+	pm_runtime_disable(uport->dev);
+	pm_runtime_set_suspended(uport->dev);
 
+	mutex_lock(&msm_uport->clk_mutex);
 	/* Disable the transmitter */
 	msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_TX_DISABLE_BMSK);
 	/* Disable the receiver */
 	msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_RX_DISABLE_BMSK);
 
-	pm_runtime_disable(uport->dev);
-	pm_runtime_set_suspended(uport->dev);
-
-	/* Free the interrupt */
-	free_irq(uport->irq, msm_uport);
-	if (use_low_power_rx_wakeup(msm_uport))
-		free_irq(msm_uport->rx_wakeup.irq, msm_uport);
-
 	msm_uport->imr_reg = 0;
 	msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
+	/*
+	 * Complete all device write before actually disabling uartclk.
+	 * Hence mb() requires here.
+	 */
+	mb();
 
-	wait_event(msm_uport->rx.wait, msm_uport->rx.flush == FLUSH_SHUTDOWN);
-
-	clk_disable(msm_uport->clk);  /* to balance local clk_enable() */
-	if (msm_uport->clk_state != MSM_HS_CLK_OFF)
-		clk_disable(msm_uport->clk);  /* to balance clk_state */
+	if (msm_uport->clk_state != MSM_HS_CLK_OFF) {
+		/* to balance clk_state */
+		clk_disable_unprepare(msm_uport->clk);
+		if (msm_uport->pclk)
+			clk_disable_unprepare(msm_uport->pclk);
+		wake_unlock(&msm_uport->dma_wake_lock);
+	}
 	msm_uport->clk_state = MSM_HS_CLK_PORT_OFF;
 
 	dma_unmap_single(uport->dev, msm_uport->tx.dma_base,
 			 UART_XMIT_SIZE, DMA_TO_DEVICE);
 
-	spin_unlock_irqrestore(&uport->lock, flags);
+	if (use_low_power_wakeup(msm_uport))
+		irq_set_irq_wake(msm_uport->wakeup.irq, 0);
 
-	if (cancel_work_sync(&msm_uport->rx.tty_work))
-		msm_hs_tty_flip_buffer_work(&msm_uport->rx.tty_work);
+	/* Free the interrupt */
+	free_irq(uport->irq, msm_uport);
+	if (use_low_power_wakeup(msm_uport))
+		free_irq(msm_uport->wakeup.irq, msm_uport);
+	mutex_unlock(&msm_uport->clk_mutex);
+	mutex_destroy(&msm_uport->clk_mutex);
 }
 
 static void __exit msm_serial_hs_exit(void)
 {
-	flush_workqueue(msm_hs_workqueue);
-	destroy_workqueue(msm_hs_workqueue);
+	printk(KERN_INFO "msm_serial_hs module removed\n");
+	debugfs_remove_recursive(debug_base);
 	platform_driver_unregister(&msm_serial_hs_platform_driver);
 	uart_unregister_driver(&msm_hs_driver);
 }
-module_exit(msm_serial_hs_exit);
 
-#ifdef CONFIG_PM_RUNTIME
 static int msm_hs_runtime_idle(struct device *dev)
 {
 	/*
@@ -1810,7 +2114,6 @@
 	struct platform_device *pdev = container_of(dev, struct
 						    platform_device, dev);
 	struct msm_hs_port *msm_uport = &q_uart_port[pdev->id];
-
 	msm_hs_request_clock_on(&msm_uport->uport);
 	return 0;
 }
@@ -1820,15 +2123,9 @@
 	struct platform_device *pdev = container_of(dev, struct
 						    platform_device, dev);
 	struct msm_hs_port *msm_uport = &q_uart_port[pdev->id];
-
 	msm_hs_request_clock_off(&msm_uport->uport);
 	return 0;
 }
-#else
-#define msm_hs_runtime_idle NULL
-#define msm_hs_runtime_resume NULL
-#define msm_hs_runtime_suspend NULL
-#endif
 
 static const struct dev_pm_ops msm_hs_dev_pm_ops = {
 	.runtime_suspend = msm_hs_runtime_suspend,
@@ -1837,11 +2134,10 @@
 };
 
 static struct platform_driver msm_serial_hs_platform_driver = {
-	.probe = msm_hs_probe,
+	.probe	= msm_hs_probe,
 	.remove = __devexit_p(msm_hs_remove),
 	.driver = {
 		.name = "msm_serial_hs",
-		.owner = THIS_MODULE,
 		.pm   = &msm_hs_dev_pm_ops,
 	},
 };
@@ -1866,13 +2162,14 @@
 	.startup = msm_hs_startup,
 	.shutdown = msm_hs_shutdown,
 	.set_termios = msm_hs_set_termios,
-	.pm = msm_hs_pm,
 	.type = msm_hs_type,
 	.config_port = msm_hs_config_port,
 	.release_port = msm_hs_release_port,
 	.request_port = msm_hs_request_port,
 };
 
+module_init(msm_serial_hs_init);
+module_exit(msm_serial_hs_exit);
 MODULE_DESCRIPTION("High Speed UART Driver for the MSM chipset");
 MODULE_VERSION("1.2");
 MODULE_LICENSE("GPL v2");
diff --git a/drivers/tty/serial/msm_serial_hs_hwreg.h b/drivers/tty/serial/msm_serial_hs_hwreg.h
new file mode 100644
index 0000000..81f3d54
--- /dev/null
+++ b/drivers/tty/serial/msm_serial_hs_hwreg.h
@@ -0,0 +1,207 @@
+/* drivers/serial/msm_serial_hs_hwreg.h
+ *
+ * Copyright (c) 2007-2009, 2012, Code Aurora Forum. All rights reserved.
+ * 
+ * All source code in this file is licensed under the following license
+ * except where indicated.
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org
+ */
+
+#ifndef MSM_SERIAL_HS_HWREG_H
+#define MSM_SERIAL_HS_HWREG_H
+
+#define GSBI_CONTROL_ADDR              0x0
+#define GSBI_PROTOCOL_CODE_MASK        0x30
+#define GSBI_PROTOCOL_I2C_UART         0x60
+#define GSBI_PROTOCOL_UART             0x40
+#define GSBI_PROTOCOL_IDLE             0x0
+
+#define TCSR_ADM_1_A_CRCI_MUX_SEL      0x78
+#define TCSR_ADM_1_B_CRCI_MUX_SEL      0x7C
+#define ADM1_CRCI_GSBI6_RX_SEL         0x800
+#define ADM1_CRCI_GSBI6_TX_SEL         0x400
+
+enum msm_hsl_regs {
+	UARTDM_MR1,
+	UARTDM_MR2,
+	UARTDM_IMR,
+	UARTDM_SR,
+	UARTDM_CR,
+	UARTDM_CSR,
+	UARTDM_IPR,
+	UARTDM_ISR,
+	UARTDM_RX_TOTAL_SNAP,
+	UARTDM_RFWR,
+	UARTDM_TFWR,
+	UARTDM_RF,
+	UARTDM_TF,
+	UARTDM_MISR,
+	UARTDM_DMRX,
+	UARTDM_NCF_TX,
+	UARTDM_DMEN,
+	UARTDM_BCR,
+	UARTDM_TXFS,
+	UARTDM_RXFS,
+	UARTDM_LAST,
+};
+
+#define UARTDM_MR1_ADDR 0x0
+#define UARTDM_MR2_ADDR 0x4
+
+/* write only register */
+#define UARTDM_CSR_ADDR    0x8
+#define UARTDM_CSR_115200 0xFF
+#define UARTDM_CSR_57600  0xEE
+#define UARTDM_CSR_38400  0xDD
+#define UARTDM_CSR_28800  0xCC
+#define UARTDM_CSR_19200  0xBB
+#define UARTDM_CSR_14400  0xAA
+#define UARTDM_CSR_9600   0x99
+#define UARTDM_CSR_7200   0x88
+#define UARTDM_CSR_4800   0x77
+#define UARTDM_CSR_3600   0x66
+#define UARTDM_CSR_2400   0x55
+#define UARTDM_CSR_1200   0x44
+#define UARTDM_CSR_600    0x33
+#define UARTDM_CSR_300    0x22
+#define UARTDM_CSR_150    0x11
+#define UARTDM_CSR_75     0x00
+
+/* write only register */
+#define UARTDM_TF_ADDR 0x70
+#define UARTDM_TF2_ADDR 0x74
+#define UARTDM_TF3_ADDR 0x78
+#define UARTDM_TF4_ADDR 0x7C
+
+/* write only register */
+#define UARTDM_CR_ADDR 0x10
+/* write only register */
+#define UARTDM_IMR_ADDR 0x14
+
+#define UARTDM_IPR_ADDR 0x18
+#define UARTDM_TFWR_ADDR 0x1c
+#define UARTDM_RFWR_ADDR 0x20
+#define UARTDM_HCR_ADDR 0x24
+#define UARTDM_DMRX_ADDR 0x34
+#define UARTDM_IRDA_ADDR 0x38
+#define UARTDM_DMEN_ADDR 0x3c
+
+/* UART_DM_NO_CHARS_FOR_TX */
+#define UARTDM_NCF_TX_ADDR 0x40
+
+#define UARTDM_BADR_ADDR 0x44
+
+#define UARTDM_SIM_CFG_ADDR 0x80
+
+/* Read Only register */
+#define UARTDM_SR_ADDR 0x8
+
+/* Read Only register */
+#define UARTDM_RF_ADDR  0x70
+#define UARTDM_RF2_ADDR 0x74
+#define UARTDM_RF3_ADDR 0x78
+#define UARTDM_RF4_ADDR 0x7C
+
+/* Read Only register */
+#define UARTDM_MISR_ADDR 0x10
+
+/* Read Only register */
+#define UARTDM_ISR_ADDR 0x14
+#define UARTDM_RX_TOTAL_SNAP_ADDR 0x38
+
+#define UARTDM_TXFS_ADDR 0x4C
+#define UARTDM_RXFS_ADDR 0x50
+
+/* Register field Mask Mapping */
+#define UARTDM_SR_RX_BREAK_BMSK	        BIT(6)
+#define UARTDM_SR_PAR_FRAME_BMSK	BIT(5)
+#define UARTDM_SR_OVERRUN_BMSK		BIT(4)
+#define UARTDM_SR_TXEMT_BMSK		BIT(3)
+#define UARTDM_SR_TXRDY_BMSK		BIT(2)
+#define UARTDM_SR_RXRDY_BMSK		BIT(0)
+
+#define UARTDM_CR_TX_DISABLE_BMSK	BIT(3)
+#define UARTDM_CR_RX_DISABLE_BMSK	BIT(1)
+#define UARTDM_CR_TX_EN_BMSK		BIT(2)
+#define UARTDM_CR_RX_EN_BMSK		BIT(0)
+
+/* UARTDM_CR channel_comman bit value (register field is bits 8:4) */
+#define RESET_RX		0x10
+#define RESET_TX		0x20
+#define RESET_ERROR_STATUS	0x30
+#define RESET_BREAK_INT		0x40
+#define START_BREAK		0x50
+#define STOP_BREAK		0x60
+#define RESET_CTS		0x70
+#define RESET_STALE_INT		0x80
+#define RFR_LOW			0xD0
+#define RFR_HIGH		0xE0
+#define CR_PROTECTION_EN	0x100
+#define STALE_EVENT_ENABLE	0x500
+#define STALE_EVENT_DISABLE	0x600
+#define FORCE_STALE_EVENT	0x400
+#define CLEAR_TX_READY		0x300
+#define RESET_TX_ERROR		0x800
+#define RESET_TX_DONE		0x810
+
+#define UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK 0xffffff00
+#define UARTDM_MR1_AUTO_RFR_LEVEL0_BMSK 0x3f
+#define UARTDM_MR1_CTS_CTL_BMSK 0x40
+#define UARTDM_MR1_RX_RDY_CTL_BMSK 0x80
+
+#define UARTDM_MR2_LOOP_MODE_BMSK		0x80
+#define UARTDM_MR2_ERROR_MODE_BMSK		0x40
+#define UARTDM_MR2_BITS_PER_CHAR_BMSK		0x30
+#define UARTDM_MR2_RX_ZERO_CHAR_OFF		0x100
+#define UARTDM_MR2_RX_ERROR_CHAR_OFF		0x200
+#define UARTDM_MR2_RX_BREAK_ZERO_CHAR_OFF	0x100
+
+#define UARTDM_MR2_BITS_PER_CHAR_8	(0x3 << 4)
+
+/* bits per character configuration */
+#define FIVE_BPC  (0 << 4)
+#define SIX_BPC   (1 << 4)
+#define SEVEN_BPC (2 << 4)
+#define EIGHT_BPC (3 << 4)
+
+#define UARTDM_MR2_STOP_BIT_LEN_BMSK 0xc
+#define STOP_BIT_ONE (1 << 2)
+#define STOP_BIT_TWO (3 << 2)
+
+#define UARTDM_MR2_PARITY_MODE_BMSK 0x3
+
+/* Parity configuration */
+#define NO_PARITY 0x0
+#define EVEN_PARITY 0x1
+#define ODD_PARITY 0x2
+#define SPACE_PARITY 0x3
+
+#define UARTDM_IPR_STALE_TIMEOUT_MSB_BMSK 0xffffff80
+#define UARTDM_IPR_STALE_LSB_BMSK 0x1f
+
+/* These can be used for both ISR and IMR register */
+#define UARTDM_ISR_TX_READY_BMSK	BIT(7)
+#define UARTDM_ISR_CURRENT_CTS_BMSK	BIT(6)
+#define UARTDM_ISR_DELTA_CTS_BMSK	BIT(5)
+#define UARTDM_ISR_RXLEV_BMSK		BIT(4)
+#define UARTDM_ISR_RXSTALE_BMSK		BIT(3)
+#define UARTDM_ISR_RXBREAK_BMSK		BIT(2)
+#define UARTDM_ISR_RXHUNT_BMSK		BIT(1)
+#define UARTDM_ISR_TXLEV_BMSK		BIT(0)
+
+/* Field definitions for UART_DM_DMEN*/
+#define UARTDM_TX_DM_EN_BMSK 0x1
+#define UARTDM_RX_DM_EN_BMSK 0x2
+
+#endif /* MSM_SERIAL_HS_HWREG_H */
diff --git a/drivers/tty/serial/msm_serial_hs_lite.c b/drivers/tty/serial/msm_serial_hs_lite.c
new file mode 100644
index 0000000..59104ed
--- /dev/null
+++ b/drivers/tty/serial/msm_serial_hs_lite.c
@@ -0,0 +1,1572 @@
+/*
+ * drivers/serial/msm_serial.c - driver for msm7k serial device and console
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * 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.
+ */
+
+/* Acknowledgements:
+ * This file is based on msm_serial.c, originally
+ * Written by Robert Love <rlove@google.com>  */
+
+#if defined(CONFIG_SERIAL_MSM_HSL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/atomic.h>
+#include <linux/hrtimer.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/console.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/nmi.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/gpio.h>
+#include <linux/debugfs.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <mach/board.h>
+#include <mach/msm_serial_hs_lite.h>
+#include <asm/mach-types.h>
+#include "msm_serial_hs_hwreg.h"
+
+struct msm_hsl_port {
+	struct uart_port	uart;
+	char			name[16];
+	struct clk		*clk;
+	struct clk		*pclk;
+	struct dentry		*loopback_dir;
+	unsigned int		imr;
+	unsigned int		*uart_csr_code;
+	unsigned int            *gsbi_mapbase;
+	unsigned int            *mapped_gsbi;
+	int			is_uartdm;
+	unsigned int            old_snap_state;
+	unsigned int		ver_id;
+	int			tx_timeout;
+};
+
+#define UARTDM_VERSION_11_13	0
+#define UARTDM_VERSION_14	1
+
+#define UART_TO_MSM(uart_port)	((struct msm_hsl_port *) uart_port)
+#define is_console(port)	((port)->cons && \
+				(port)->cons->index == (port)->line)
+
+static const unsigned int regmap[][UARTDM_LAST] = {
+	[UARTDM_VERSION_11_13] = {
+		[UARTDM_MR1] = UARTDM_MR1_ADDR,
+		[UARTDM_MR2] = UARTDM_MR2_ADDR,
+		[UARTDM_IMR] = UARTDM_IMR_ADDR,
+		[UARTDM_SR] = UARTDM_SR_ADDR,
+		[UARTDM_CR] = UARTDM_CR_ADDR,
+		[UARTDM_CSR] = UARTDM_CSR_ADDR,
+		[UARTDM_IPR] = UARTDM_IPR_ADDR,
+		[UARTDM_ISR] = UARTDM_ISR_ADDR,
+		[UARTDM_RX_TOTAL_SNAP] = UARTDM_RX_TOTAL_SNAP_ADDR,
+		[UARTDM_TFWR] = UARTDM_TFWR_ADDR,
+		[UARTDM_RFWR] = UARTDM_RFWR_ADDR,
+		[UARTDM_RF] = UARTDM_RF_ADDR,
+		[UARTDM_TF] = UARTDM_TF_ADDR,
+		[UARTDM_MISR] = UARTDM_MISR_ADDR,
+		[UARTDM_DMRX] = UARTDM_DMRX_ADDR,
+		[UARTDM_NCF_TX] = UARTDM_NCF_TX_ADDR,
+		[UARTDM_DMEN] = UARTDM_DMEN_ADDR,
+		[UARTDM_TXFS] = UARTDM_TXFS_ADDR,
+		[UARTDM_RXFS] = UARTDM_RXFS_ADDR,
+	},
+	[UARTDM_VERSION_14] = {
+		[UARTDM_MR1] = 0x0,
+		[UARTDM_MR2] = 0x4,
+		[UARTDM_IMR] = 0xb0,
+		[UARTDM_SR] = 0xa4,
+		[UARTDM_CR] = 0xa8,
+		[UARTDM_CSR] = 0xa0,
+		[UARTDM_IPR] = 0x18,
+		[UARTDM_ISR] = 0xb4,
+		[UARTDM_RX_TOTAL_SNAP] = 0xbc,
+		[UARTDM_TFWR] = 0x1c,
+		[UARTDM_RFWR] = 0x20,
+		[UARTDM_RF] = 0x140,
+		[UARTDM_TF] = 0x100,
+		[UARTDM_MISR] = 0xac,
+		[UARTDM_DMRX] = 0x34,
+		[UARTDM_NCF_TX] = 0x40,
+		[UARTDM_DMEN] = 0x3c,
+		[UARTDM_TXFS] = 0x4c,
+		[UARTDM_RXFS] = 0x50,
+	},
+};
+
+static struct of_device_id msm_hsl_match_table[] = {
+	{	.compatible = "qcom,msm-lsuart-v14",
+		.data = (void *)UARTDM_VERSION_14
+	},
+	{}
+};
+static struct dentry *debug_base;
+static inline void wait_for_xmitr(struct uart_port *port);
+static int get_console_state(struct uart_port *port);
+static inline void msm_hsl_write(struct uart_port *port,
+				 unsigned int val, unsigned int off)
+{
+	iowrite32(val, port->membase + off);
+}
+static inline unsigned int msm_hsl_read(struct uart_port *port,
+		     unsigned int off)
+{
+	return ioread32(port->membase + off);
+}
+
+static unsigned int msm_serial_hsl_has_gsbi(struct uart_port *port)
+{
+	return UART_TO_MSM(port)->is_uartdm;
+}
+
+static int get_line(struct platform_device *pdev)
+{
+	const struct msm_serial_hslite_platform_data *pdata =
+					pdev->dev.platform_data;
+	if (pdata)
+		return pdata->line;
+
+	return pdev->id;
+}
+
+static int clk_en(struct uart_port *port, int enable)
+{
+	struct msm_hsl_port *msm_hsl_port = UART_TO_MSM(port);
+	int ret = 0;
+
+	if (enable) {
+
+		ret = clk_prepare_enable(msm_hsl_port->clk);
+		if (ret)
+			goto err;
+		if (msm_hsl_port->pclk) {
+			ret = clk_prepare_enable(msm_hsl_port->pclk);
+			if (ret) {
+				clk_disable_unprepare(msm_hsl_port->clk);
+				goto err;
+			}
+		}
+	} else {
+
+		clk_disable_unprepare(msm_hsl_port->clk);
+		if (msm_hsl_port->pclk)
+			clk_disable_unprepare(msm_hsl_port->pclk);
+	}
+err:
+	return ret;
+}
+static int msm_hsl_loopback_enable_set(void *data, u64 val)
+{
+	struct msm_hsl_port *msm_hsl_port = data;
+	struct uart_port *port = &(msm_hsl_port->uart);
+	unsigned int vid;
+	unsigned long flags;
+	int ret = 0;
+
+	ret = clk_set_rate(msm_hsl_port->clk, 7372800);
+	if (!ret)
+		clk_en(port, 1);
+	else {
+		pr_err("%s(): Error: Setting the clock rate\n", __func__);
+		return -EINVAL;
+	}
+
+	vid = msm_hsl_port->ver_id;
+	if (val) {
+		spin_lock_irqsave(&port->lock, flags);
+		ret = msm_hsl_read(port, regmap[vid][UARTDM_MR2]);
+		ret |= UARTDM_MR2_LOOP_MODE_BMSK;
+		msm_hsl_write(port, ret, regmap[vid][UARTDM_MR2]);
+		spin_unlock_irqrestore(&port->lock, flags);
+	} else {
+		spin_lock_irqsave(&port->lock, flags);
+		ret = msm_hsl_read(port, regmap[vid][UARTDM_MR2]);
+		ret &= ~UARTDM_MR2_LOOP_MODE_BMSK;
+		msm_hsl_write(port, ret, regmap[vid][UARTDM_MR2]);
+		spin_unlock_irqrestore(&port->lock, flags);
+	}
+
+	clk_en(port, 0);
+	return 0;
+}
+static int msm_hsl_loopback_enable_get(void *data, u64 *val)
+{
+	struct msm_hsl_port *msm_hsl_port = data;
+	struct uart_port *port = &(msm_hsl_port->uart);
+	unsigned long flags;
+	int ret = 0;
+
+	ret = clk_set_rate(msm_hsl_port->clk, 7372800);
+	if (!ret)
+		clk_en(port, 1);
+	else {
+		pr_err("%s(): Error setting clk rate\n", __func__);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&port->lock, flags);
+	ret = msm_hsl_read(port, regmap[msm_hsl_port->ver_id][UARTDM_MR2]);
+	spin_unlock_irqrestore(&port->lock, flags);
+	clk_en(port, 0);
+
+	*val = (ret & UARTDM_MR2_LOOP_MODE_BMSK) ? 1 : 0;
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(loopback_enable_fops, msm_hsl_loopback_enable_get,
+			msm_hsl_loopback_enable_set, "%llu\n");
+/*
+ * msm_serial_hsl debugfs node: <debugfs_root>/msm_serial_hsl/loopback.<id>
+ * writing 1 turns on internal loopback mode in HW. Useful for automation
+ * test scripts.
+ * writing 0 disables the internal loopback mode. Default is disabled.
+ */
+static void msm_hsl_debugfs_init(struct msm_hsl_port *msm_uport,
+								int id)
+{
+	char node_name[15];
+
+	snprintf(node_name, sizeof(node_name), "loopback.%d", id);
+	msm_uport->loopback_dir = debugfs_create_file(node_name,
+					S_IRUGO | S_IWUSR,
+					debug_base,
+					msm_uport,
+					&loopback_enable_fops);
+
+	if (IS_ERR_OR_NULL(msm_uport->loopback_dir))
+		pr_err("%s(): Cannot create loopback.%d debug entry",
+							__func__, id);
+}
+static void msm_hsl_stop_tx(struct uart_port *port)
+{
+	struct msm_hsl_port *msm_hsl_port = UART_TO_MSM(port);
+
+	msm_hsl_port->imr &= ~UARTDM_ISR_TXLEV_BMSK;
+	msm_hsl_write(port, msm_hsl_port->imr,
+		regmap[msm_hsl_port->ver_id][UARTDM_IMR]);
+}
+
+static void msm_hsl_start_tx(struct uart_port *port)
+{
+	struct msm_hsl_port *msm_hsl_port = UART_TO_MSM(port);
+
+	msm_hsl_port->imr |= UARTDM_ISR_TXLEV_BMSK;
+	msm_hsl_write(port, msm_hsl_port->imr,
+		regmap[msm_hsl_port->ver_id][UARTDM_IMR]);
+}
+
+static void msm_hsl_stop_rx(struct uart_port *port)
+{
+	struct msm_hsl_port *msm_hsl_port = UART_TO_MSM(port);
+
+	msm_hsl_port->imr &= ~(UARTDM_ISR_RXLEV_BMSK |
+			       UARTDM_ISR_RXSTALE_BMSK);
+	msm_hsl_write(port, msm_hsl_port->imr,
+		regmap[msm_hsl_port->ver_id][UARTDM_IMR]);
+}
+
+static void msm_hsl_enable_ms(struct uart_port *port)
+{
+	struct msm_hsl_port *msm_hsl_port = UART_TO_MSM(port);
+
+	msm_hsl_port->imr |= UARTDM_ISR_DELTA_CTS_BMSK;
+	msm_hsl_write(port, msm_hsl_port->imr,
+		regmap[msm_hsl_port->ver_id][UARTDM_IMR]);
+}
+
+static void handle_rx(struct uart_port *port, unsigned int misr)
+{
+	struct tty_struct *tty = port->state->port.tty;
+	unsigned int vid;
+	unsigned int sr;
+	int count = 0;
+	struct msm_hsl_port *msm_hsl_port = UART_TO_MSM(port);
+
+	vid = msm_hsl_port->ver_id;
+	/*
+	 * Handle overrun. My understanding of the hardware is that overrun
+	 * is not tied to the RX buffer, so we handle the case out of band.
+	 */
+	if ((msm_hsl_read(port, regmap[vid][UARTDM_SR]) &
+				UARTDM_SR_OVERRUN_BMSK)) {
+		port->icount.overrun++;
+		tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+		msm_hsl_write(port, RESET_ERROR_STATUS,
+			regmap[vid][UARTDM_CR]);
+	}
+
+	if (misr & UARTDM_ISR_RXSTALE_BMSK) {
+		count = msm_hsl_read(port,
+			regmap[vid][UARTDM_RX_TOTAL_SNAP]) -
+			msm_hsl_port->old_snap_state;
+		msm_hsl_port->old_snap_state = 0;
+	} else {
+		count = 4 * (msm_hsl_read(port, regmap[vid][UARTDM_RFWR]));
+		msm_hsl_port->old_snap_state += count;
+	}
+
+	/* and now the main RX loop */
+	while (count > 0) {
+		unsigned int c;
+		char flag = TTY_NORMAL;
+
+		sr = msm_hsl_read(port, regmap[vid][UARTDM_SR]);
+		if ((sr & UARTDM_SR_RXRDY_BMSK) == 0) {
+			msm_hsl_port->old_snap_state -= count;
+			break;
+		}
+		c = msm_hsl_read(port, regmap[vid][UARTDM_RF]);
+		if (sr & UARTDM_SR_RX_BREAK_BMSK) {
+			port->icount.brk++;
+			if (uart_handle_break(port))
+				continue;
+		} else if (sr & UARTDM_SR_PAR_FRAME_BMSK) {
+			port->icount.frame++;
+		} else {
+			port->icount.rx++;
+		}
+
+		/* Mask conditions we're ignorning. */
+		sr &= port->read_status_mask;
+		if (sr & UARTDM_SR_RX_BREAK_BMSK)
+			flag = TTY_BREAK;
+		else if (sr & UARTDM_SR_PAR_FRAME_BMSK)
+			flag = TTY_FRAME;
+
+		/* TODO: handle sysrq */
+		/* if (!uart_handle_sysrq_char(port, c)) */
+		tty_insert_flip_string(tty, (char *) &c,
+				       (count > 4) ? 4 : count);
+		count -= 4;
+	}
+
+	tty_flip_buffer_push(tty);
+}
+
+static void handle_tx(struct uart_port *port)
+{
+	struct circ_buf *xmit = &port->state->xmit;
+	int sent_tx;
+	int tx_count;
+	int x;
+	unsigned int tf_pointer = 0;
+	unsigned int vid;
+
+	vid = UART_TO_MSM(port)->ver_id;
+	tx_count = uart_circ_chars_pending(xmit);
+
+	if (tx_count > (UART_XMIT_SIZE - xmit->tail))
+		tx_count = UART_XMIT_SIZE - xmit->tail;
+	if (tx_count >= port->fifosize)
+		tx_count = port->fifosize;
+
+	/* Handle x_char */
+	if (port->x_char) {
+		wait_for_xmitr(port);
+		msm_hsl_write(port, tx_count + 1, regmap[vid][UARTDM_NCF_TX]);
+		msm_hsl_read(port, regmap[vid][UARTDM_NCF_TX]);
+		msm_hsl_write(port, port->x_char, regmap[vid][UARTDM_TF]);
+		port->icount.tx++;
+		port->x_char = 0;
+	} else if (tx_count) {
+		wait_for_xmitr(port);
+		msm_hsl_write(port, tx_count, regmap[vid][UARTDM_NCF_TX]);
+		msm_hsl_read(port, regmap[vid][UARTDM_NCF_TX]);
+	}
+	if (!tx_count) {
+		msm_hsl_stop_tx(port);
+		return;
+	}
+
+	while (tf_pointer < tx_count)  {
+		if (unlikely(!(msm_hsl_read(port, regmap[vid][UARTDM_SR]) &
+			       UARTDM_SR_TXRDY_BMSK)))
+			continue;
+		switch (tx_count - tf_pointer) {
+		case 1: {
+			x = xmit->buf[xmit->tail];
+			port->icount.tx++;
+			break;
+		}
+		case 2: {
+			x = xmit->buf[xmit->tail]
+				| xmit->buf[xmit->tail+1] << 8;
+			port->icount.tx += 2;
+			break;
+		}
+		case 3: {
+			x = xmit->buf[xmit->tail]
+				| xmit->buf[xmit->tail+1] << 8
+				| xmit->buf[xmit->tail + 2] << 16;
+			port->icount.tx += 3;
+			break;
+		}
+		default: {
+			x = *((int *)&(xmit->buf[xmit->tail]));
+			port->icount.tx += 4;
+			break;
+		}
+		}
+		msm_hsl_write(port, x, regmap[vid][UARTDM_TF]);
+		xmit->tail = ((tx_count - tf_pointer < 4) ?
+			      (tx_count - tf_pointer + xmit->tail) :
+			      (xmit->tail + 4)) & (UART_XMIT_SIZE - 1);
+		tf_pointer += 4;
+		sent_tx = 1;
+	}
+
+	if (uart_circ_empty(xmit))
+		msm_hsl_stop_tx(port);
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+}
+
+static void handle_delta_cts(struct uart_port *port)
+{
+	unsigned int vid = UART_TO_MSM(port)->ver_id;
+
+	msm_hsl_write(port, RESET_CTS, regmap[vid][UARTDM_CR]);
+	port->icount.cts++;
+	wake_up_interruptible(&port->state->port.delta_msr_wait);
+}
+
+static irqreturn_t msm_hsl_irq(int irq, void *dev_id)
+{
+	struct uart_port *port = dev_id;
+	struct msm_hsl_port *msm_hsl_port = UART_TO_MSM(port);
+	unsigned int vid;
+	unsigned int misr;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+	vid = msm_hsl_port->ver_id;
+	misr = msm_hsl_read(port, regmap[vid][UARTDM_MISR]);
+	/* disable interrupt */
+	msm_hsl_write(port, 0, regmap[vid][UARTDM_IMR]);
+
+	if (misr & (UARTDM_ISR_RXSTALE_BMSK | UARTDM_ISR_RXLEV_BMSK)) {
+		handle_rx(port, misr);
+		if (misr & (UARTDM_ISR_RXSTALE_BMSK))
+			msm_hsl_write(port, RESET_STALE_INT,
+					regmap[vid][UARTDM_CR]);
+		msm_hsl_write(port, 6500, regmap[vid][UARTDM_DMRX]);
+		msm_hsl_write(port, STALE_EVENT_ENABLE, regmap[vid][UARTDM_CR]);
+	}
+	if (misr & UARTDM_ISR_TXLEV_BMSK)
+		handle_tx(port);
+
+	if (misr & UARTDM_ISR_DELTA_CTS_BMSK)
+		handle_delta_cts(port);
+
+	/* restore interrupt */
+	msm_hsl_write(port, msm_hsl_port->imr, regmap[vid][UARTDM_IMR]);
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	return IRQ_HANDLED;
+}
+
+static unsigned int msm_hsl_tx_empty(struct uart_port *port)
+{
+	unsigned int ret;
+	unsigned int vid = UART_TO_MSM(port)->ver_id;
+
+	ret = (msm_hsl_read(port, regmap[vid][UARTDM_SR]) &
+	       UARTDM_SR_TXEMT_BMSK) ? TIOCSER_TEMT : 0;
+	return ret;
+}
+
+static void msm_hsl_reset(struct uart_port *port)
+{
+	unsigned int vid = UART_TO_MSM(port)->ver_id;
+
+	/* reset everything */
+	msm_hsl_write(port, RESET_RX, regmap[vid][UARTDM_CR]);
+	msm_hsl_write(port, RESET_TX, regmap[vid][UARTDM_CR]);
+	msm_hsl_write(port, RESET_ERROR_STATUS, regmap[vid][UARTDM_CR]);
+	msm_hsl_write(port, RESET_BREAK_INT, regmap[vid][UARTDM_CR]);
+	msm_hsl_write(port, RESET_CTS, regmap[vid][UARTDM_CR]);
+	msm_hsl_write(port, RFR_LOW, regmap[vid][UARTDM_CR]);
+}
+
+static unsigned int msm_hsl_get_mctrl(struct uart_port *port)
+{
+	return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR | TIOCM_RTS;
+}
+
+static void msm_hsl_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	unsigned int vid = UART_TO_MSM(port)->ver_id;
+	unsigned int mr;
+	unsigned int loop_mode;
+
+	mr = msm_hsl_read(port, regmap[vid][UARTDM_MR1]);
+
+	if (!(mctrl & TIOCM_RTS)) {
+		mr &= ~UARTDM_MR1_RX_RDY_CTL_BMSK;
+		msm_hsl_write(port, mr, regmap[vid][UARTDM_MR1]);
+		msm_hsl_write(port, RFR_HIGH, regmap[vid][UARTDM_CR]);
+	} else {
+		mr |= UARTDM_MR1_RX_RDY_CTL_BMSK;
+		msm_hsl_write(port, mr, regmap[vid][UARTDM_MR1]);
+	}
+
+	loop_mode = TIOCM_LOOP & mctrl;
+	if (loop_mode) {
+		mr = msm_hsl_read(port, regmap[vid][UARTDM_MR2]);
+		mr |= UARTDM_MR2_LOOP_MODE_BMSK;
+		msm_hsl_write(port, mr, regmap[vid][UARTDM_MR2]);
+
+		/* Reset TX */
+		msm_hsl_reset(port);
+
+		/* Turn on Uart Receiver & Transmitter*/
+		msm_hsl_write(port, UARTDM_CR_RX_EN_BMSK
+		      | UARTDM_CR_TX_EN_BMSK, regmap[vid][UARTDM_CR]);
+	}
+}
+
+static void msm_hsl_break_ctl(struct uart_port *port, int break_ctl)
+{
+	unsigned int vid = UART_TO_MSM(port)->ver_id;
+
+	if (break_ctl)
+		msm_hsl_write(port, START_BREAK, regmap[vid][UARTDM_CR]);
+	else
+		msm_hsl_write(port, STOP_BREAK, regmap[vid][UARTDM_CR]);
+}
+
+static void msm_hsl_set_baud_rate(struct uart_port *port, unsigned int baud)
+{
+	unsigned int baud_code, rxstale, watermark;
+	unsigned int data;
+	unsigned int vid;
+	struct msm_hsl_port *msm_hsl_port = UART_TO_MSM(port);
+
+	switch (baud) {
+	case 300:
+		baud_code = UARTDM_CSR_75;
+		rxstale = 1;
+		break;
+	case 600:
+		baud_code = UARTDM_CSR_150;
+		rxstale = 1;
+		break;
+	case 1200:
+		baud_code = UARTDM_CSR_300;
+		rxstale = 1;
+		break;
+	case 2400:
+		baud_code = UARTDM_CSR_600;
+		rxstale = 1;
+		break;
+	case 4800:
+		baud_code = UARTDM_CSR_1200;
+		rxstale = 1;
+		break;
+	case 9600:
+		baud_code = UARTDM_CSR_2400;
+		rxstale = 2;
+		break;
+	case 14400:
+		baud_code = UARTDM_CSR_3600;
+		rxstale = 3;
+		break;
+	case 19200:
+		baud_code = UARTDM_CSR_4800;
+		rxstale = 4;
+		break;
+	case 28800:
+		baud_code = UARTDM_CSR_7200;
+		rxstale = 6;
+		break;
+	case 38400:
+		baud_code = UARTDM_CSR_9600;
+		rxstale = 8;
+		break;
+	case 57600:
+		baud_code = UARTDM_CSR_14400;
+		rxstale = 16;
+		break;
+	case 115200:
+		baud_code = UARTDM_CSR_28800;
+		rxstale = 31;
+		break;
+	case 230400:
+		baud_code = UARTDM_CSR_57600;
+		rxstale = 31;
+		break;
+	case 460800:
+		baud_code = UARTDM_CSR_115200;
+		rxstale = 31;
+		break;
+	default: /* 115200 baud rate */
+		baud_code = UARTDM_CSR_28800;
+		rxstale = 31;
+		break;
+	}
+
+	/* Set timeout to be ~100x the character transmit time */
+	msm_hsl_port->tx_timeout = 1000000000 / baud;
+
+	vid = msm_hsl_port->ver_id;
+	msm_hsl_write(port, baud_code, regmap[vid][UARTDM_CSR]);
+
+	/* RX stale watermark */
+	watermark = UARTDM_IPR_STALE_LSB_BMSK & rxstale;
+	watermark |= UARTDM_IPR_STALE_TIMEOUT_MSB_BMSK & (rxstale << 2);
+	msm_hsl_write(port, watermark, regmap[vid][UARTDM_IPR]);
+
+	/* Set RX watermark
+	 * Configure Rx Watermark as 3/4 size of Rx FIFO.
+	 * RFWR register takes value in Words for UARTDM Core
+	 * whereas it is consider to be in Bytes for UART Core.
+	 * Hence configuring Rx Watermark as 12 Words.
+	 */
+	watermark = (port->fifosize * 3) / (4*4);
+	msm_hsl_write(port, watermark, regmap[vid][UARTDM_RFWR]);
+
+	/* set TX watermark */
+	msm_hsl_write(port, 0, regmap[vid][UARTDM_TFWR]);
+
+	msm_hsl_write(port, CR_PROTECTION_EN, regmap[vid][UARTDM_CR]);
+	msm_hsl_reset(port);
+
+	data = UARTDM_CR_TX_EN_BMSK;
+	data |= UARTDM_CR_RX_EN_BMSK;
+	/* enable TX & RX */
+	msm_hsl_write(port, data, regmap[vid][UARTDM_CR]);
+
+	msm_hsl_write(port, RESET_STALE_INT, regmap[vid][UARTDM_CR]);
+	/* turn on RX and CTS interrupts */
+	msm_hsl_port->imr = UARTDM_ISR_RXSTALE_BMSK
+		| UARTDM_ISR_DELTA_CTS_BMSK | UARTDM_ISR_RXLEV_BMSK;
+	msm_hsl_write(port, msm_hsl_port->imr, regmap[vid][UARTDM_IMR]);
+	msm_hsl_write(port, 6500, regmap[vid][UARTDM_DMRX]);
+	msm_hsl_write(port, STALE_EVENT_ENABLE, regmap[vid][UARTDM_CR]);
+}
+
+static void msm_hsl_init_clock(struct uart_port *port)
+{
+	clk_en(port, 1);
+}
+
+static void msm_hsl_deinit_clock(struct uart_port *port)
+{
+	clk_en(port, 0);
+}
+
+static int msm_hsl_startup(struct uart_port *port)
+{
+	struct msm_hsl_port *msm_hsl_port = UART_TO_MSM(port);
+	struct platform_device *pdev = to_platform_device(port->dev);
+	const struct msm_serial_hslite_platform_data *pdata =
+					pdev->dev.platform_data;
+	unsigned int data, rfr_level;
+	unsigned int vid;
+	int ret;
+	unsigned long flags;
+
+	snprintf(msm_hsl_port->name, sizeof(msm_hsl_port->name),
+		 "msm_serial_hsl%d", port->line);
+
+	if (!(is_console(port)) || (!port->cons) ||
+		(port->cons && (!(port->cons->flags & CON_ENABLED)))) {
+
+		if (msm_serial_hsl_has_gsbi(port))
+			if ((ioread32(msm_hsl_port->mapped_gsbi +
+				GSBI_CONTROL_ADDR) & GSBI_PROTOCOL_I2C_UART)
+					!= GSBI_PROTOCOL_I2C_UART)
+				iowrite32(GSBI_PROTOCOL_I2C_UART,
+					msm_hsl_port->mapped_gsbi +
+						GSBI_CONTROL_ADDR);
+
+		if (pdata && pdata->config_gpio) {
+			ret = gpio_request(pdata->uart_tx_gpio,
+						"UART_TX_GPIO");
+			if (unlikely(ret)) {
+				pr_err("%s: gpio request failed for:%d\n",
+						__func__, pdata->uart_tx_gpio);
+				return ret;
+			}
+
+			ret = gpio_request(pdata->uart_rx_gpio, "UART_RX_GPIO");
+			if (unlikely(ret)) {
+				pr_err("%s: gpio request failed for:%d\n",
+						__func__, pdata->uart_rx_gpio);
+				gpio_free(pdata->uart_tx_gpio);
+				return ret;
+			}
+		}
+	}
+#ifndef CONFIG_PM_RUNTIME
+	msm_hsl_init_clock(port);
+#endif
+	pm_runtime_get_sync(port->dev);
+
+	/* Set RFR Level as 3/4 of UARTDM FIFO Size */
+	if (likely(port->fifosize > 48))
+		rfr_level = port->fifosize - 16;
+	else
+		rfr_level = port->fifosize;
+
+	/*
+	 * Use rfr_level value in Words to program
+	 * MR1 register for UARTDM Core.
+	 */
+	rfr_level = (rfr_level / 4);
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	vid = msm_hsl_port->ver_id;
+	/* set automatic RFR level */
+	data = msm_hsl_read(port, regmap[vid][UARTDM_MR1]);
+	data &= ~UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK;
+	data &= ~UARTDM_MR1_AUTO_RFR_LEVEL0_BMSK;
+	data |= UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK & (rfr_level << 2);
+	data |= UARTDM_MR1_AUTO_RFR_LEVEL0_BMSK & rfr_level;
+	msm_hsl_write(port, data, regmap[vid][UARTDM_MR1]);
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	ret = request_irq(port->irq, msm_hsl_irq, IRQF_TRIGGER_HIGH,
+			  msm_hsl_port->name, port);
+	if (unlikely(ret)) {
+		printk(KERN_ERR "%s: failed to request_irq\n", __func__);
+		return ret;
+	}
+	return 0;
+}
+
+static void msm_hsl_shutdown(struct uart_port *port)
+{
+	struct msm_hsl_port *msm_hsl_port = UART_TO_MSM(port);
+	struct platform_device *pdev = to_platform_device(port->dev);
+	const struct msm_serial_hslite_platform_data *pdata =
+					pdev->dev.platform_data;
+
+	msm_hsl_port->imr = 0;
+	/* disable interrupts */
+	msm_hsl_write(port, 0, regmap[msm_hsl_port->ver_id][UARTDM_IMR]);
+
+	free_irq(port->irq, port);
+
+#ifndef CONFIG_PM_RUNTIME
+	msm_hsl_deinit_clock(port);
+#endif
+	pm_runtime_put_sync(port->dev);
+	if (!(is_console(port)) || (!port->cons) ||
+		(port->cons && (!(port->cons->flags & CON_ENABLED)))) {
+		if (pdata && pdata->config_gpio) {
+			gpio_free(pdata->uart_tx_gpio);
+			gpio_free(pdata->uart_rx_gpio);
+		}
+	}
+}
+
+static void msm_hsl_set_termios(struct uart_port *port,
+				struct ktermios *termios,
+				struct ktermios *old)
+{
+	unsigned long flags;
+	unsigned int baud, mr;
+	unsigned int vid;
+
+	if (!termios->c_cflag)
+		return;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	/* calculate and set baud rate */
+	baud = uart_get_baud_rate(port, termios, old, 300, 460800);
+
+	msm_hsl_set_baud_rate(port, baud);
+
+	vid = UART_TO_MSM(port)->ver_id;
+	/* calculate parity */
+	mr = msm_hsl_read(port, regmap[vid][UARTDM_MR2]);
+	mr &= ~UARTDM_MR2_PARITY_MODE_BMSK;
+	if (termios->c_cflag & PARENB) {
+		if (termios->c_cflag & PARODD)
+			mr |= ODD_PARITY;
+		else if (termios->c_cflag & CMSPAR)
+			mr |= SPACE_PARITY;
+		else
+			mr |= EVEN_PARITY;
+	}
+
+	/* calculate bits per char */
+	mr &= ~UARTDM_MR2_BITS_PER_CHAR_BMSK;
+	switch (termios->c_cflag & CSIZE) {
+	case CS5:
+		mr |= FIVE_BPC;
+		break;
+	case CS6:
+		mr |= SIX_BPC;
+		break;
+	case CS7:
+		mr |= SEVEN_BPC;
+		break;
+	case CS8:
+	default:
+		mr |= EIGHT_BPC;
+		break;
+	}
+
+	/* calculate stop bits */
+	mr &= ~(STOP_BIT_ONE | STOP_BIT_TWO);
+	if (termios->c_cflag & CSTOPB)
+		mr |= STOP_BIT_TWO;
+	else
+		mr |= STOP_BIT_ONE;
+
+	/* set parity, bits per char, and stop bit */
+	msm_hsl_write(port, mr, regmap[vid][UARTDM_MR2]);
+
+	/* calculate and set hardware flow control */
+	mr = msm_hsl_read(port, regmap[vid][UARTDM_MR1]);
+	mr &= ~(UARTDM_MR1_CTS_CTL_BMSK | UARTDM_MR1_RX_RDY_CTL_BMSK);
+	if (termios->c_cflag & CRTSCTS) {
+		mr |= UARTDM_MR1_CTS_CTL_BMSK;
+		mr |= UARTDM_MR1_RX_RDY_CTL_BMSK;
+	}
+	msm_hsl_write(port, mr, regmap[vid][UARTDM_MR1]);
+
+	/* Configure status bits to ignore based on termio flags. */
+	port->read_status_mask = 0;
+	if (termios->c_iflag & INPCK)
+		port->read_status_mask |= UARTDM_SR_PAR_FRAME_BMSK;
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		port->read_status_mask |= UARTDM_SR_RX_BREAK_BMSK;
+
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *msm_hsl_type(struct uart_port *port)
+{
+	return "MSM";
+}
+
+static void msm_hsl_release_port(struct uart_port *port)
+{
+	struct msm_hsl_port *msm_hsl_port = UART_TO_MSM(port);
+	struct platform_device *pdev = to_platform_device(port->dev);
+	struct resource *uart_resource;
+	resource_size_t size;
+
+	uart_resource = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						     "uartdm_resource");
+	if (!uart_resource)
+		uart_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (unlikely(!uart_resource))
+		return;
+	size = uart_resource->end - uart_resource->start + 1;
+
+	release_mem_region(port->mapbase, size);
+	iounmap(port->membase);
+	port->membase = NULL;
+
+	if (msm_serial_hsl_has_gsbi(port)) {
+		iowrite32(GSBI_PROTOCOL_IDLE, msm_hsl_port->mapped_gsbi +
+			  GSBI_CONTROL_ADDR);
+		iounmap(msm_hsl_port->mapped_gsbi);
+		msm_hsl_port->mapped_gsbi = NULL;
+	}
+}
+
+static int msm_hsl_request_port(struct uart_port *port)
+{
+	struct msm_hsl_port *msm_hsl_port = UART_TO_MSM(port);
+	struct platform_device *pdev = to_platform_device(port->dev);
+	struct resource *uart_resource;
+	struct resource *gsbi_resource;
+	resource_size_t size;
+
+	uart_resource = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						     "uartdm_resource");
+	if (!uart_resource)
+		uart_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (unlikely(!uart_resource)) {
+		pr_err("%s: can't get uartdm resource\n", __func__);
+		return -ENXIO;
+	}
+	size = uart_resource->end - uart_resource->start + 1;
+
+	if (unlikely(!request_mem_region(port->mapbase, size,
+					 "msm_serial_hsl"))) {
+		pr_err("%s: can't get mem region for uartdm\n", __func__);
+		return -EBUSY;
+	}
+
+	port->membase = ioremap(port->mapbase, size);
+	if (!port->membase) {
+		release_mem_region(port->mapbase, size);
+		return -EBUSY;
+	}
+
+	if (msm_serial_hsl_has_gsbi(port)) {
+		gsbi_resource = platform_get_resource_byname(pdev,
+							     IORESOURCE_MEM,
+							     "gsbi_resource");
+		if (!gsbi_resource)
+			gsbi_resource = platform_get_resource(pdev,
+						IORESOURCE_MEM, 1);
+		if (unlikely(!gsbi_resource)) {
+			pr_err("%s: can't get gsbi resource\n", __func__);
+			return -ENXIO;
+		}
+
+		size = gsbi_resource->end - gsbi_resource->start + 1;
+		msm_hsl_port->mapped_gsbi = ioremap(gsbi_resource->start,
+						    size);
+		if (!msm_hsl_port->mapped_gsbi) {
+			return -EBUSY;
+		}
+	}
+
+	return 0;
+}
+
+static void msm_hsl_config_port(struct uart_port *port, int flags)
+{
+	struct msm_hsl_port *msm_hsl_port = UART_TO_MSM(port);
+	if (flags & UART_CONFIG_TYPE) {
+		port->type = PORT_MSM;
+		if (msm_hsl_request_port(port))
+			return;
+	}
+	if (msm_serial_hsl_has_gsbi(port)) {
+		if (msm_hsl_port->pclk)
+			clk_prepare_enable(msm_hsl_port->pclk);
+		if ((ioread32(msm_hsl_port->mapped_gsbi + GSBI_CONTROL_ADDR) &
+			GSBI_PROTOCOL_I2C_UART) != GSBI_PROTOCOL_I2C_UART)
+			iowrite32(GSBI_PROTOCOL_I2C_UART,
+				msm_hsl_port->mapped_gsbi + GSBI_CONTROL_ADDR);
+		if (msm_hsl_port->pclk)
+			clk_disable_unprepare(msm_hsl_port->pclk);
+	}
+}
+
+static int msm_hsl_verify_port(struct uart_port *port,
+			       struct serial_struct *ser)
+{
+	if (unlikely(ser->type != PORT_UNKNOWN && ser->type != PORT_MSM))
+		return -EINVAL;
+	if (unlikely(port->irq != ser->irq))
+		return -EINVAL;
+	return 0;
+}
+
+static void msm_hsl_power(struct uart_port *port, unsigned int state,
+			  unsigned int oldstate)
+{
+	int ret;
+	struct msm_hsl_port *msm_hsl_port = UART_TO_MSM(port);
+
+	switch (state) {
+	case 0:
+		ret = clk_set_rate(msm_hsl_port->clk, 7372800);
+		if (ret)
+			pr_err("%s(): Error setting UART clock rate\n",
+								__func__);
+		clk_en(port, 1);
+		break;
+	case 3:
+		clk_en(port, 0);
+		ret = clk_set_rate(msm_hsl_port->clk, 0);
+		if (ret)
+			pr_err("%s(): Error setting UART clock rate to zero.\n",
+								__func__);
+		break;
+	default:
+		pr_err("%s(): msm_serial_hsl: Unknown PM state %d\n",
+							__func__, state);
+	}
+}
+
+static struct uart_ops msm_hsl_uart_pops = {
+	.tx_empty = msm_hsl_tx_empty,
+	.set_mctrl = msm_hsl_set_mctrl,
+	.get_mctrl = msm_hsl_get_mctrl,
+	.stop_tx = msm_hsl_stop_tx,
+	.start_tx = msm_hsl_start_tx,
+	.stop_rx = msm_hsl_stop_rx,
+	.enable_ms = msm_hsl_enable_ms,
+	.break_ctl = msm_hsl_break_ctl,
+	.startup = msm_hsl_startup,
+	.shutdown = msm_hsl_shutdown,
+	.set_termios = msm_hsl_set_termios,
+	.type = msm_hsl_type,
+	.release_port = msm_hsl_release_port,
+	.request_port = msm_hsl_request_port,
+	.config_port = msm_hsl_config_port,
+	.verify_port = msm_hsl_verify_port,
+	.pm = msm_hsl_power,
+};
+
+static struct msm_hsl_port msm_hsl_uart_ports[] = {
+	{
+		.uart = {
+			.iotype = UPIO_MEM,
+			.ops = &msm_hsl_uart_pops,
+			.flags = UPF_BOOT_AUTOCONF,
+			.fifosize = 64,
+			.line = 0,
+		},
+	},
+	{
+		.uart = {
+			.iotype = UPIO_MEM,
+			.ops = &msm_hsl_uart_pops,
+			.flags = UPF_BOOT_AUTOCONF,
+			.fifosize = 64,
+			.line = 1,
+		},
+	},
+	{
+		.uart = {
+			.iotype = UPIO_MEM,
+			.ops = &msm_hsl_uart_pops,
+			.flags = UPF_BOOT_AUTOCONF,
+			.fifosize = 64,
+			.line = 2,
+		},
+	},
+};
+
+#define UART_NR	ARRAY_SIZE(msm_hsl_uart_ports)
+
+static inline struct uart_port *get_port_from_line(unsigned int line)
+{
+	return &msm_hsl_uart_ports[line].uart;
+}
+
+static unsigned int msm_hsl_console_state[8];
+
+static void dump_hsl_regs(struct uart_port *port)
+{
+	struct msm_hsl_port *msm_hsl_port = UART_TO_MSM(port);
+	unsigned int vid = msm_hsl_port->ver_id;
+	unsigned int sr, isr, mr1, mr2, ncf, txfs, rxfs, con_state;
+
+	sr = msm_hsl_read(port, regmap[vid][UARTDM_SR]);
+	isr = msm_hsl_read(port, regmap[vid][UARTDM_ISR]);
+	mr1 = msm_hsl_read(port, regmap[vid][UARTDM_MR1]);
+	mr2 = msm_hsl_read(port, regmap[vid][UARTDM_MR2]);
+	ncf = msm_hsl_read(port, regmap[vid][UARTDM_NCF_TX]);
+	txfs = msm_hsl_read(port, regmap[vid][UARTDM_TXFS]);
+	rxfs = msm_hsl_read(port, regmap[vid][UARTDM_RXFS]);
+	con_state = get_console_state(port);
+
+	msm_hsl_console_state[0] = sr;
+	msm_hsl_console_state[1] = isr;
+	msm_hsl_console_state[2] = mr1;
+	msm_hsl_console_state[3] = mr2;
+	msm_hsl_console_state[4] = ncf;
+	msm_hsl_console_state[5] = txfs;
+	msm_hsl_console_state[6] = rxfs;
+	msm_hsl_console_state[7] = con_state;
+
+	pr_info("%s(): Timeout: %d uS\n", __func__, msm_hsl_port->tx_timeout);
+	pr_info("%s(): SR:  %08x\n", __func__, sr);
+	pr_info("%s(): ISR: %08x\n", __func__, isr);
+	pr_info("%s(): MR1: %08x\n", __func__, mr1);
+	pr_info("%s(): MR2: %08x\n", __func__, mr2);
+	pr_info("%s(): NCF: %08x\n", __func__, ncf);
+	pr_info("%s(): TXFS: %08x\n", __func__, txfs);
+	pr_info("%s(): RXFS: %08x\n", __func__, rxfs);
+	pr_info("%s(): Console state: %d\n", __func__, con_state);
+}
+
+/*
+ *  Wait for transmitter & holding register to empty
+ *  Derived from wait_for_xmitr in 8250 serial driver by Russell King  */
+static void wait_for_xmitr(struct uart_port *port)
+{
+	struct msm_hsl_port *msm_hsl_port = UART_TO_MSM(port);
+	unsigned int vid = msm_hsl_port->ver_id;
+	int count = 0;
+
+	if (!(msm_hsl_read(port, regmap[vid][UARTDM_SR]) &
+			UARTDM_SR_TXEMT_BMSK)) {
+		while (!(msm_hsl_read(port, regmap[vid][UARTDM_ISR]) &
+			UARTDM_ISR_TX_READY_BMSK)) {
+			udelay(1);
+			touch_nmi_watchdog();
+			cpu_relax();
+			if (++count == msm_hsl_port->tx_timeout) {
+				dump_hsl_regs(port);
+				panic("MSM HSL wait_for_xmitr is stuck!");
+			}
+		}
+		msm_hsl_write(port, CLEAR_TX_READY, regmap[vid][UARTDM_CR]);
+	}
+}
+
+#ifdef CONFIG_SERIAL_MSM_HSL_CONSOLE
+static void msm_hsl_console_putchar(struct uart_port *port, int ch)
+{
+	unsigned int vid = UART_TO_MSM(port)->ver_id;
+
+	wait_for_xmitr(port);
+	msm_hsl_write(port, 1, regmap[vid][UARTDM_NCF_TX]);
+	/*
+	 * Dummy read to add 1 AHB clock delay to fix UART hardware bug.
+	 * Bug: Delay required on TX-transfer-init. after writing to
+	 * NO_CHARS_FOR_TX register.
+	 */
+	msm_hsl_read(port, regmap[vid][UARTDM_SR]);
+	msm_hsl_write(port, ch, regmap[vid][UARTDM_TF]);
+}
+
+static void msm_hsl_console_write(struct console *co, const char *s,
+				  unsigned int count)
+{
+	struct uart_port *port;
+	struct msm_hsl_port *msm_hsl_port;
+	unsigned int vid;
+	int locked;
+
+	BUG_ON(co->index < 0 || co->index >= UART_NR);
+
+	port = get_port_from_line(co->index);
+	msm_hsl_port = UART_TO_MSM(port);
+	vid = msm_hsl_port->ver_id;
+
+	/* not pretty, but we can end up here via various convoluted paths */
+	if (port->sysrq || oops_in_progress)
+		locked = spin_trylock(&port->lock);
+	else {
+		locked = 1;
+		spin_lock(&port->lock);
+	}
+	msm_hsl_write(port, 0, regmap[vid][UARTDM_IMR]);
+	uart_console_write(port, s, count, msm_hsl_console_putchar);
+	msm_hsl_write(port, msm_hsl_port->imr, regmap[vid][UARTDM_IMR]);
+	if (locked == 1)
+		spin_unlock(&port->lock);
+}
+
+static int msm_hsl_console_setup(struct console *co, char *options)
+{
+	struct uart_port *port;
+	unsigned int vid;
+	int baud = 0, flow, bits, parity, mr2;
+	int ret;
+
+	if (unlikely(co->index >= UART_NR || co->index < 0))
+		return -ENXIO;
+
+	port = get_port_from_line(co->index);
+	vid = UART_TO_MSM(port)->ver_id;
+
+	if (unlikely(!port->membase))
+		return -ENXIO;
+
+	port->cons = co;
+
+	pm_runtime_get_noresume(port->dev);
+
+#ifndef CONFIG_PM_RUNTIME
+	msm_hsl_init_clock(port);
+#endif
+	pm_runtime_resume(port->dev);
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	bits = 8;
+	parity = 'n';
+	flow = 'n';
+	msm_hsl_write(port, UARTDM_MR2_BITS_PER_CHAR_8 | STOP_BIT_ONE,
+		      regmap[vid][UARTDM_MR2]);	/* 8N1 */
+
+	if (baud < 300 || baud > 115200)
+		baud = 115200;
+	msm_hsl_set_baud_rate(port, baud);
+
+	ret = uart_set_options(port, co, baud, parity, bits, flow);
+
+	mr2 = msm_hsl_read(port, regmap[vid][UARTDM_MR2]);
+	mr2 |= UARTDM_MR2_RX_ERROR_CHAR_OFF;
+	mr2 |= UARTDM_MR2_RX_BREAK_ZERO_CHAR_OFF;
+	msm_hsl_write(port, mr2, regmap[vid][UARTDM_MR2]);
+
+	msm_hsl_reset(port);
+	/* Enable transmitter */
+	msm_hsl_write(port, CR_PROTECTION_EN, regmap[vid][UARTDM_CR]);
+	msm_hsl_write(port, UARTDM_CR_TX_EN_BMSK, regmap[vid][UARTDM_CR]);
+
+	printk(KERN_INFO "msm_serial_hsl: console setup on port #%d\n",
+	       port->line);
+
+	return ret;
+}
+
+static struct uart_driver msm_hsl_uart_driver;
+
+static struct console msm_hsl_console = {
+	.name = "ttyHSL",
+	.write = msm_hsl_console_write,
+	.device = uart_console_device,
+	.setup = msm_hsl_console_setup,
+	.flags = CON_PRINTBUFFER,
+	.index = -1,
+	.data = &msm_hsl_uart_driver,
+};
+
+#define MSM_HSL_CONSOLE	(&msm_hsl_console)
+/*
+ * get_console_state - check the per-port serial console state.
+ * @port: uart_port structure describing the port
+ *
+ * Return the state of serial console availability on port.
+ * return 1: If serial console is enabled on particular UART port.
+ * return 0: If serial console is disabled on particular UART port.
+ */
+static int get_console_state(struct uart_port *port)
+{
+	if (is_console(port) && (port->cons->flags & CON_ENABLED))
+		return 1;
+	else
+		return 0;
+}
+
+/* show_msm_console - provide per-port serial console state. */
+static ssize_t show_msm_console(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	int enable;
+	struct uart_port *port;
+
+	struct platform_device *pdev = to_platform_device(dev);
+	port = get_port_from_line(get_line(pdev));
+
+	enable = get_console_state(port);
+
+	return snprintf(buf, sizeof(enable), "%d\n", enable);
+}
+
+/*
+ * set_msm_console - allow to enable/disable serial console on port.
+ *
+ * writing 1 enables serial console on UART port.
+ * writing 0 disables serial console on UART port.
+ */
+static ssize_t set_msm_console(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	int enable, cur_state;
+	struct uart_port *port;
+
+	struct platform_device *pdev = to_platform_device(dev);
+	port = get_port_from_line(get_line(pdev));
+
+	cur_state = get_console_state(port);
+	enable = buf[0] - '0';
+
+	if (enable == cur_state)
+		return count;
+
+	switch (enable) {
+	case 0:
+		pr_debug("%s(): Calling stop_console\n", __func__);
+		console_stop(port->cons);
+		pr_debug("%s(): Calling unregister_console\n", __func__);
+		unregister_console(port->cons);
+		pm_runtime_put_sync(&pdev->dev);
+		pm_runtime_disable(&pdev->dev);
+		/*
+		 * Disable UART Core clk
+		 * 3 - to disable the UART clock
+		 * Thid parameter is not used here, but used in serial core.
+		 */
+		msm_hsl_power(port, 3, 1);
+		break;
+	case 1:
+		pr_debug("%s(): Calling register_console\n", __func__);
+		/*
+		 * Disable UART Core clk
+		 * 0 - to enable the UART clock
+		 * Thid parameter is not used here, but used in serial core.
+		 */
+		msm_hsl_power(port, 0, 1);
+		pm_runtime_enable(&pdev->dev);
+		register_console(port->cons);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return count;
+}
+static DEVICE_ATTR(console, S_IWUSR | S_IRUGO, show_msm_console,
+						set_msm_console);
+#else
+#define MSM_HSL_CONSOLE	NULL
+#endif
+
+static struct uart_driver msm_hsl_uart_driver = {
+	.owner = THIS_MODULE,
+	.driver_name = "msm_serial_hsl",
+	.dev_name = "ttyHSL",
+	.nr = UART_NR,
+	.cons = MSM_HSL_CONSOLE,
+};
+
+static atomic_t msm_serial_hsl_next_id = ATOMIC_INIT(0);
+
+static int __devinit msm_serial_hsl_probe(struct platform_device *pdev)
+{
+	struct msm_hsl_port *msm_hsl_port;
+	struct resource *uart_resource;
+	struct resource *gsbi_resource;
+	struct uart_port *port;
+	const struct of_device_id *match;
+	int ret;
+
+	if (pdev->id == -1)
+		pdev->id = atomic_inc_return(&msm_serial_hsl_next_id) - 1;
+
+	if (unlikely(get_line(pdev) < 0 || get_line(pdev) >= UART_NR))
+		return -ENXIO;
+
+	printk(KERN_INFO "msm_serial_hsl: detected port #%d\n", pdev->id);
+
+	port = get_port_from_line(get_line(pdev));
+	port->dev = &pdev->dev;
+	msm_hsl_port = UART_TO_MSM(port);
+
+	match = of_match_device(msm_hsl_match_table, &pdev->dev);
+	if (!match)
+		msm_hsl_port->ver_id = UARTDM_VERSION_11_13;
+	else
+		msm_hsl_port->ver_id = (unsigned int)match->data;
+
+	gsbi_resource =	platform_get_resource_byname(pdev,
+						     IORESOURCE_MEM,
+						     "gsbi_resource");
+	if (!gsbi_resource)
+		gsbi_resource = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	msm_hsl_port->clk = clk_get(&pdev->dev, "core_clk");
+	if (gsbi_resource) {
+		msm_hsl_port->is_uartdm = 1;
+		msm_hsl_port->pclk = clk_get(&pdev->dev, "iface_clk");
+	} else {
+		msm_hsl_port->is_uartdm = 0;
+		msm_hsl_port->pclk = NULL;
+	}
+
+	if (unlikely(IS_ERR(msm_hsl_port->clk))) {
+		printk(KERN_ERR "%s: Error getting clk\n", __func__);
+		return PTR_ERR(msm_hsl_port->clk);
+	}
+	if (unlikely(IS_ERR(msm_hsl_port->pclk))) {
+		printk(KERN_ERR "%s: Error getting pclk\n", __func__);
+		return PTR_ERR(msm_hsl_port->pclk);
+	}
+
+	uart_resource = platform_get_resource_byname(pdev,
+						     IORESOURCE_MEM,
+						     "uartdm_resource");
+	if (!uart_resource)
+		uart_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (unlikely(!uart_resource)) {
+		printk(KERN_ERR "getting uartdm_resource failed\n");
+		return -ENXIO;
+	}
+	port->mapbase = uart_resource->start;
+
+	port->irq = platform_get_irq(pdev, 0);
+	if (unlikely((int)port->irq < 0)) {
+		printk(KERN_ERR "%s: getting irq failed\n", __func__);
+		return -ENXIO;
+	}
+
+	device_set_wakeup_capable(&pdev->dev, 1);
+	platform_set_drvdata(pdev, port);
+	pm_runtime_enable(port->dev);
+#ifdef CONFIG_SERIAL_MSM_HSL_CONSOLE
+	ret = device_create_file(&pdev->dev, &dev_attr_console);
+	if (unlikely(ret))
+		pr_err("%s():Can't create console attribute\n", __func__);
+#endif
+	msm_hsl_debugfs_init(msm_hsl_port, get_line(pdev));
+
+	/* Temporarily increase the refcount on the GSBI clock to avoid a race
+	 * condition with the earlyprintk handover mechanism.
+	 */
+	if (msm_hsl_port->pclk)
+		clk_prepare_enable(msm_hsl_port->pclk);
+	ret = uart_add_one_port(&msm_hsl_uart_driver, port);
+	if (msm_hsl_port->pclk)
+		clk_disable_unprepare(msm_hsl_port->pclk);
+	return ret;
+}
+
+static int __devexit msm_serial_hsl_remove(struct platform_device *pdev)
+{
+	struct msm_hsl_port *msm_hsl_port = platform_get_drvdata(pdev);
+	struct uart_port *port;
+
+	port = get_port_from_line(get_line(pdev));
+#ifdef CONFIG_SERIAL_MSM_HSL_CONSOLE
+	device_remove_file(&pdev->dev, &dev_attr_console);
+#endif
+	pm_runtime_put_sync(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+
+	device_set_wakeup_capable(&pdev->dev, 0);
+	platform_set_drvdata(pdev, NULL);
+	uart_remove_one_port(&msm_hsl_uart_driver, port);
+
+	clk_put(msm_hsl_port->pclk);
+	clk_put(msm_hsl_port->clk);
+	debugfs_remove(msm_hsl_port->loopback_dir);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int msm_serial_hsl_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct uart_port *port;
+	port = get_port_from_line(get_line(pdev));
+
+	if (port) {
+
+		if (is_console(port))
+			msm_hsl_deinit_clock(port);
+
+		uart_suspend_port(&msm_hsl_uart_driver, port);
+		if (device_may_wakeup(dev))
+			enable_irq_wake(port->irq);
+	}
+
+	return 0;
+}
+
+static int msm_serial_hsl_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct uart_port *port;
+	port = get_port_from_line(get_line(pdev));
+
+	if (port) {
+
+		uart_resume_port(&msm_hsl_uart_driver, port);
+		if (device_may_wakeup(dev))
+			disable_irq_wake(port->irq);
+
+		if (is_console(port))
+			msm_hsl_init_clock(port);
+	}
+
+	return 0;
+}
+#else
+#define msm_serial_hsl_suspend NULL
+#define msm_serial_hsl_resume NULL
+#endif
+
+static int msm_hsl_runtime_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct uart_port *port;
+	port = get_port_from_line(get_line(pdev));
+
+	dev_dbg(dev, "pm_runtime: suspending\n");
+	msm_hsl_deinit_clock(port);
+	return 0;
+}
+
+static int msm_hsl_runtime_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct uart_port *port;
+	port = get_port_from_line(get_line(pdev));
+
+	dev_dbg(dev, "pm_runtime: resuming\n");
+	msm_hsl_init_clock(port);
+	return 0;
+}
+
+static struct dev_pm_ops msm_hsl_dev_pm_ops = {
+	.suspend = msm_serial_hsl_suspend,
+	.resume = msm_serial_hsl_resume,
+	.runtime_suspend = msm_hsl_runtime_suspend,
+	.runtime_resume = msm_hsl_runtime_resume,
+};
+
+static struct platform_driver msm_hsl_platform_driver = {
+	.probe = msm_serial_hsl_probe,
+	.remove = __devexit_p(msm_serial_hsl_remove),
+	.driver = {
+		.name = "msm_serial_hsl",
+		.owner = THIS_MODULE,
+		.pm = &msm_hsl_dev_pm_ops,
+		.of_match_table = msm_hsl_match_table,
+	},
+};
+
+static int __init msm_serial_hsl_init(void)
+{
+	int ret;
+
+	ret = uart_register_driver(&msm_hsl_uart_driver);
+	if (unlikely(ret))
+		return ret;
+
+	debug_base = debugfs_create_dir("msm_serial_hsl", NULL);
+	if (IS_ERR_OR_NULL(debug_base))
+		pr_err("%s():Cannot create debugfs dir\n", __func__);
+
+	ret = platform_driver_register(&msm_hsl_platform_driver);
+	if (unlikely(ret))
+		uart_unregister_driver(&msm_hsl_uart_driver);
+
+	printk(KERN_INFO "msm_serial_hsl: driver initialized\n");
+
+	return ret;
+}
+
+static void __exit msm_serial_hsl_exit(void)
+{
+	debugfs_remove_recursive(debug_base);
+#ifdef CONFIG_SERIAL_MSM_HSL_CONSOLE
+	unregister_console(&msm_hsl_console);
+#endif
+	platform_driver_unregister(&msm_hsl_platform_driver);
+	uart_unregister_driver(&msm_hsl_uart_driver);
+}
+
+module_init(msm_serial_hsl_init);
+module_exit(msm_serial_hsl_exit);
+
+MODULE_DESCRIPTION("Driver for msm HSUART serial device");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 7d9fbb8..9ca64ac 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -1965,7 +1965,11 @@
 		 */
 		if (port->tty && port->tty->termios && termios.c_cflag == 0)
 			termios = *(port->tty->termios);
-
+		/*
+		 * As we need to set the uart clock rate back to 7.3 MHz.
+		 * We need this change.
+		 *
+		 */
 		if (console_suspend_enabled)
 			uart_change_pm(state, 0);
 		uport->ops->set_termios(uport, &termios, NULL);
diff --git a/drivers/tty/smux_ctl.c b/drivers/tty/smux_ctl.c
new file mode 100644
index 0000000..69adbf3
--- /dev/null
+++ b/drivers/tty/smux_ctl.c
@@ -0,0 +1,937 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Serial Mux Control Driver -- Provides a binary serial muxed control
+ * port interface.
+ */
+
+#define DEBUG
+
+#include <linux/cdev.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+#include <linux/workqueue.h>
+#include <linux/atomic.h>
+#include <linux/platform_device.h>
+#include <linux/smux.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+
+#include <asm/ioctls.h>
+
+#define MAX_WRITE_RETRY 5
+#define MAGIC_NO_V1 0x33FC
+#define DEVICE_NAME "smuxctl"
+#define SMUX_CTL_MAX_BUF_SIZE 2048
+#define SMUX_CTL_MODULE_NAME "smux_ctl"
+#define DEBUG
+
+static int msm_smux_ctl_debug_mask;
+module_param_named(debug_mask, msm_smux_ctl_debug_mask,
+	int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+static uint32_t smux_ctl_ch_id[] = {
+	SMUX_DATA_CTL_0,
+	SMUX_DATA_CTL_1,
+	SMUX_DATA_CTL_2,
+	SMUX_DATA_CTL_3,
+	SMUX_DATA_CTL_4,
+	SMUX_DATA_CTL_5,
+	SMUX_DATA_CTL_6,
+	SMUX_DATA_CTL_7,
+	SMUX_USB_RMNET_CTL_0,
+	SMUX_CSVT_CTL_0
+};
+
+#define SMUX_CTL_NUM_CHANNELS ARRAY_SIZE(smux_ctl_ch_id)
+
+struct smux_ctl_dev {
+	int id;
+	char name[10];
+	struct cdev cdev;
+	struct device *devicep;
+	struct mutex dev_lock;
+	atomic_t ref_count;
+	int state;
+	int is_channel_reset;
+	int is_high_wm;
+	int write_pending;
+
+	struct mutex rx_lock;
+	uint32_t read_avail;
+	struct list_head rx_list;
+
+	wait_queue_head_t read_wait_queue;
+	wait_queue_head_t write_wait_queue;
+
+	struct {
+		uint32_t bytes_tx;
+		uint32_t bytes_rx;
+		uint32_t pkts_tx;
+		uint32_t pkts_rx;
+		uint32_t cnt_ssr;
+		uint32_t cnt_read_fail;
+		uint32_t cnt_write_fail;
+		uint32_t cnt_high_wm_hit;
+	} stats;
+
+} *smux_ctl_devp[SMUX_CTL_NUM_CHANNELS];
+
+struct smux_ctl_pkt {
+	int data_size;
+	void *data;
+};
+
+struct smux_ctl_list_elem {
+	struct list_head list;
+	struct smux_ctl_pkt ctl_pkt;
+};
+
+struct class *smux_ctl_classp;
+static dev_t smux_ctl_number;
+static uint32_t smux_ctl_inited;
+
+enum {
+	MSM_SMUX_CTL_DEBUG = 1U << 0,
+	MSM_SMUX_CTL_DUMP_BUFFER = 1U << 1,
+};
+
+#if defined(DEBUG)
+
+static const char *smux_ctl_event_str[] = {
+	"SMUX_CONNECTED",
+	"SMUX_DISCONNECTED",
+	"SMUX_READ_DONE",
+	"SMUX_READ_FAIL",
+	"SMUX_WRITE_DONE",
+	"SMUX_WRITE_FAIL",
+	"SMUX_TIOCM_UPDATE",
+	"SMUX_LOW_WM_HIT",
+	"SMUX_HIGH_WM_HIT",
+};
+
+#define SMUXCTL_DUMP_BUFFER(prestr, cnt, buf) \
+do { \
+	if (msm_smux_ctl_debug_mask & MSM_SMUX_CTL_DUMP_BUFFER) { \
+		int i; \
+		pr_err("%s", prestr); \
+		for (i = 0; i < cnt; i++) \
+			pr_err("%.2x", buf[i]); \
+		pr_err("\n"); \
+	} \
+} while (0)
+
+#define SMUXCTL_DBG(x...) \
+do { \
+	if (msm_smux_ctl_debug_mask & MSM_SMUX_CTL_DEBUG) \
+		pr_err(x); \
+} while (0)
+
+
+#else
+#define SMUXCTL_DUMP_BUFFER(prestr, cnt, buf) do {} while (0)
+#define SMUXCTL_DBG(x...) do {} while (0)
+#endif
+
+#if defined(DEBUG_LOOPBACK)
+#define SMUXCTL_SET_LOOPBACK(lcid) \
+	msm_smux_set_ch_option(lcid, SMUX_CH_OPTION_LOCAL_LOOPBACK, 0)
+#else
+#define SMUXCTL_SET_LOOPBACK(lcid) do {} while (0)
+#endif
+
+static int get_ctl_dev_index(int id)
+{
+	int dev_index;
+	for (dev_index = 0; dev_index < SMUX_CTL_NUM_CHANNELS; dev_index++) {
+		if (smux_ctl_ch_id[dev_index] == id)
+			return dev_index;
+	}
+	return -ENODEV;
+}
+
+static int smux_ctl_get_rx_buf_cb(void *priv, void **pkt_priv,
+		void **buffer, int size)
+{
+	void *buf = NULL;
+	int id = ((struct smux_ctl_dev *)(priv))->id;
+	int dev_index;
+
+	if (id < 0 || id > smux_ctl_ch_id[SMUX_CTL_NUM_CHANNELS - 1])
+		return -ENODEV;
+
+	if (!buffer || 0 >= size)
+		return -EINVAL;
+
+	dev_index = get_ctl_dev_index(id);
+	if (dev_index < 0) {
+		pr_err(SMUX_CTL_MODULE_NAME ": %s: Ch%d is not "
+					"exported to user-space\n",
+				__func__, id);
+		return -ENODEV;
+	}
+
+	SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s: Allocating Rx buf size %d "
+			"for ch%d\n",
+			__func__, size, smux_ctl_devp[dev_index]->id);
+
+	buf = kmalloc(size, GFP_KERNEL);
+	if (!buf) {
+		pr_err(SMUX_CTL_MODULE_NAME ": %s: buffer allocation failed: "
+				"Ch%d, size %d ", __func__, id, size);
+		return -ENOMEM;
+	}
+
+	*buffer = buf;
+	*pkt_priv = NULL;
+	return 0;
+
+}
+
+void smux_ctl_notify_cb(void *priv, int event_type, const void *metadata)
+{
+	int id = ((struct smux_ctl_dev *)(priv))->id;
+	struct smux_ctl_list_elem *list_elem = NULL;
+	int dev_index;
+	void *data;
+	int len;
+
+	if (id < 0 || id > smux_ctl_ch_id[SMUX_CTL_NUM_CHANNELS - 1])
+		return;
+
+	dev_index = get_ctl_dev_index(id);
+	if (dev_index < 0) {
+		pr_err(SMUX_CTL_MODULE_NAME ": %s: Ch%d is not exported "
+				"to user-space\n", __func__, id);
+		return;
+	}
+
+	SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s: Ch%d, Event %d (%s)\n",
+			__func__, smux_ctl_devp[dev_index]->id,
+				event_type, smux_ctl_event_str[event_type]);
+
+
+	switch (event_type) {
+	case SMUX_CONNECTED:
+		mutex_lock(&smux_ctl_devp[dev_index]->dev_lock);
+		smux_ctl_devp[dev_index]->state = SMUX_CONNECTED;
+		smux_ctl_devp[dev_index]->is_high_wm = 0;
+		smux_ctl_devp[dev_index]->is_channel_reset = 0;
+		smux_ctl_devp[dev_index]->read_avail = 0;
+		mutex_unlock(&smux_ctl_devp[dev_index]->dev_lock);
+		wake_up(&smux_ctl_devp[dev_index]->write_wait_queue);
+		break;
+
+	case SMUX_DISCONNECTED:
+		mutex_lock(&smux_ctl_devp[dev_index]->dev_lock);
+		smux_ctl_devp[dev_index]->state = SMUX_DISCONNECTED;
+		smux_ctl_devp[dev_index]->is_channel_reset =
+			((struct smux_meta_disconnected *)metadata)->is_ssr;
+		if (smux_ctl_devp[dev_index]->is_channel_reset)
+			smux_ctl_devp[dev_index]->stats.cnt_ssr++;
+		mutex_unlock(&smux_ctl_devp[dev_index]->dev_lock);
+		wake_up(&smux_ctl_devp[dev_index]->write_wait_queue);
+		wake_up(&smux_ctl_devp[dev_index]->read_wait_queue);
+		break;
+
+	case SMUX_READ_FAIL:
+		data = ((struct smux_meta_read *)metadata)->buffer;
+		kfree(data);
+		mutex_lock(&smux_ctl_devp[dev_index]->dev_lock);
+		smux_ctl_devp[dev_index]->stats.cnt_read_fail++;
+		mutex_unlock(&smux_ctl_devp[dev_index]->dev_lock);
+		wake_up(&smux_ctl_devp[dev_index]->read_wait_queue);
+		break;
+
+	case SMUX_READ_DONE:
+		data = ((struct smux_meta_read *)metadata)->buffer;
+		len = ((struct smux_meta_read *)metadata)->len;
+
+		if (data && len > 0) {
+			list_elem = kmalloc(sizeof(struct smux_ctl_list_elem),
+							GFP_KERNEL);
+			if (list_elem) {
+				list_elem->ctl_pkt.data = data;
+				list_elem->ctl_pkt.data_size = len;
+
+				mutex_lock(&smux_ctl_devp[dev_index]->rx_lock);
+				list_add_tail(&list_elem->list,
+					&smux_ctl_devp[dev_index]->rx_list);
+				smux_ctl_devp[dev_index]->read_avail += len;
+				mutex_unlock(
+					&smux_ctl_devp[dev_index]->rx_lock);
+			} else {
+				kfree(data);
+			}
+		}
+
+		wake_up(&smux_ctl_devp[dev_index]->read_wait_queue);
+		break;
+
+	case SMUX_WRITE_DONE:
+		mutex_lock(&smux_ctl_devp[dev_index]->dev_lock);
+		smux_ctl_devp[dev_index]->write_pending = 0;
+		mutex_unlock(&smux_ctl_devp[dev_index]->dev_lock);
+		data = ((struct smux_meta_write *)metadata)->buffer;
+		kfree(data);
+		wake_up(&smux_ctl_devp[dev_index]->write_wait_queue);
+		break;
+
+	case SMUX_WRITE_FAIL:
+		data = ((struct smux_meta_write *)metadata)->buffer;
+		kfree(data);
+		mutex_lock(&smux_ctl_devp[dev_index]->dev_lock);
+		smux_ctl_devp[dev_index]->stats.cnt_write_fail++;
+		mutex_unlock(&smux_ctl_devp[dev_index]->dev_lock);
+		wake_up(&smux_ctl_devp[dev_index]->write_wait_queue);
+		break;
+
+	case SMUX_LOW_WM_HIT:
+		mutex_lock(&smux_ctl_devp[dev_index]->dev_lock);
+		smux_ctl_devp[dev_index]->is_high_wm = 0;
+		mutex_unlock(&smux_ctl_devp[dev_index]->dev_lock);
+		wake_up(&smux_ctl_devp[dev_index]->write_wait_queue);
+		break;
+
+	case SMUX_HIGH_WM_HIT:
+		mutex_lock(&smux_ctl_devp[dev_index]->dev_lock);
+		smux_ctl_devp[dev_index]->is_high_wm = 1;
+		smux_ctl_devp[dev_index]->stats.cnt_high_wm_hit++;
+		mutex_unlock(&smux_ctl_devp[dev_index]->dev_lock);
+		break;
+
+	case SMUX_TIOCM_UPDATE:
+	default:
+		pr_err(SMUX_CTL_MODULE_NAME ": %s: Event %d not supported\n",
+				__func__, event_type);
+		break;
+
+	}
+
+}
+
+int smux_ctl_open(struct inode *inode, struct file *file)
+{
+	int r = 0;
+	struct smux_ctl_dev *devp;
+
+	if (!smux_ctl_inited)
+		return -EIO;
+
+	devp = container_of(inode->i_cdev, struct smux_ctl_dev, cdev);
+	if (!devp)
+		return -ENODEV;
+
+	SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s called on smuxctl%d device\n",
+			__func__, devp->id);
+
+	if (1 == atomic_add_return(1, &devp->ref_count)) {
+
+		SMUXCTL_SET_LOOPBACK(devp->id);
+		r = msm_smux_open(devp->id,
+				devp,
+				smux_ctl_notify_cb,
+				smux_ctl_get_rx_buf_cb);
+		if (r < 0) {
+			pr_err(SMUX_CTL_MODULE_NAME ": %s: smux_open failed "
+					"for smuxctl%d with rc %d\n",
+					__func__, devp->id, r);
+			atomic_dec(&devp->ref_count);
+			return r;
+		}
+
+		r = wait_event_interruptible_timeout(
+				devp->write_wait_queue,
+				(devp->state == SMUX_CONNECTED),
+				(5 * HZ));
+		if (r == 0)
+			r = -ETIMEDOUT;
+
+		if (r < 0) {
+			pr_err(SMUX_CTL_MODULE_NAME ": %s: "
+				"SMUX open timed out: %d, LCID %d\n",
+			       __func__, r, devp->id);
+			atomic_dec(&devp->ref_count);
+			msm_smux_close(devp->id);
+			return r;
+
+		} else if (devp->state != SMUX_CONNECTED) {
+			pr_err(SMUX_CTL_MODULE_NAME ": %s: "
+				"Invalid open notification\n", __func__);
+			r = -ENODEV;
+			atomic_dec(&devp->ref_count);
+			msm_smux_close(devp->id);
+			return r;
+		}
+	}
+
+	file->private_data = devp;
+	return 0;
+}
+
+int smux_ctl_release(struct inode *inode, struct file *file)
+{
+	struct smux_ctl_dev *devp;
+	struct smux_ctl_list_elem *list_elem = NULL;
+
+	devp = file->private_data;
+	if (!devp)
+		return -EINVAL;
+
+	SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s called on smuxctl%d device\n",
+			__func__, devp->id);
+
+	mutex_lock(&devp->dev_lock);
+	if (atomic_dec_and_test(&devp->ref_count)) {
+		mutex_lock(&devp->rx_lock);
+		while (!list_empty(&devp->rx_list)) {
+			list_elem = list_first_entry(
+					&devp->rx_list,
+					struct smux_ctl_list_elem,
+					list);
+			list_del(&list_elem->list);
+			kfree(list_elem->ctl_pkt.data);
+			kfree(list_elem);
+		}
+		devp->read_avail = 0;
+		mutex_unlock(&devp->rx_lock);
+		msm_smux_close(devp->id);
+	}
+	mutex_unlock(&devp->dev_lock);
+	file->private_data = NULL;
+
+	return 0;
+}
+
+static int smux_ctl_readable(int id)
+{
+	int r;
+	int dev_index;
+
+	if (id < 0 || id > smux_ctl_ch_id[SMUX_CTL_NUM_CHANNELS - 1])
+		return -ENODEV;
+
+	dev_index = get_ctl_dev_index(id);
+	if (dev_index < 0) {
+		pr_err(SMUX_CTL_MODULE_NAME ": %s: Ch%d "
+				"is not exported to user-space\n",
+			__func__, id);
+		return -ENODEV;
+	}
+
+	mutex_lock(&smux_ctl_devp[dev_index]->dev_lock);
+
+	if (signal_pending(current))
+		r = -ERESTARTSYS;
+
+	if (smux_ctl_devp[dev_index]->state == SMUX_DISCONNECTED &&
+	    smux_ctl_devp[dev_index]->is_channel_reset != 0)
+		r = -ENETRESET;
+
+	else if (smux_ctl_devp[dev_index]->state != SMUX_CONNECTED)
+		r = -ENODEV;
+
+	else
+		r = smux_ctl_devp[dev_index]->read_avail;
+
+
+	mutex_unlock(&smux_ctl_devp[dev_index]->dev_lock);
+
+	return r;
+
+}
+
+ssize_t smux_ctl_read(struct file *file,
+			char __user *buf,
+			size_t count,
+			loff_t *ppos)
+{
+	int r = 0, id, bytes_to_read, read_err;
+	struct smux_ctl_dev *devp;
+	struct smux_ctl_list_elem *list_elem = NULL;
+
+	devp = file->private_data;
+
+	if (!devp)
+		return -ENODEV;
+
+	SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s: read from ch%d\n",
+			__func__, devp->id);
+
+	id = devp->id;
+	mutex_lock(&devp->rx_lock);
+	while (devp->read_avail <= 0) {
+		mutex_unlock(&devp->rx_lock);
+		r = wait_event_interruptible(devp->read_wait_queue,
+				0 != (read_err = smux_ctl_readable(id)));
+
+		if (r < 0) {
+			pr_err(SMUX_CTL_MODULE_NAME ": %s:"
+					"wait_event_interruptible "
+					"ret %i\n", __func__, r);
+			return r;
+		}
+
+		if (read_err < 0) {
+			pr_err(SMUX_CTL_MODULE_NAME ": %s:"
+				" Read block failed for Ch%d, err %d\n",
+					__func__, devp->id, read_err);
+			return read_err;
+		}
+
+		mutex_lock(&devp->rx_lock);
+	}
+
+	if (list_empty(&devp->rx_list)) {
+		mutex_unlock(&devp->rx_lock);
+		SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s: "
+			"Nothing in ch%d's rx_list\n", __func__,
+			devp->id);
+		return -EAGAIN;
+	}
+
+	list_elem = list_first_entry(&devp->rx_list,
+			struct smux_ctl_list_elem, list);
+	bytes_to_read = (uint32_t)(list_elem->ctl_pkt.data_size);
+	if (bytes_to_read > count) {
+		mutex_unlock(&devp->rx_lock);
+		pr_err(SMUX_CTL_MODULE_NAME ": %s: "
+			"Packet size %d > buf size %d\n", __func__,
+			bytes_to_read, count);
+		return -ENOMEM;
+	}
+
+	if (copy_to_user(buf, list_elem->ctl_pkt.data, bytes_to_read)) {
+		mutex_unlock(&devp->rx_lock);
+		pr_err(SMUX_CTL_MODULE_NAME ": %s: "
+			"copy_to_user failed for ch%d\n", __func__,
+			devp->id);
+		return -EFAULT;
+	}
+
+	devp->read_avail -= bytes_to_read;
+	list_del(&list_elem->list);
+	kfree(list_elem->ctl_pkt.data);
+	kfree(list_elem);
+	devp->stats.pkts_rx++;
+	devp->stats.bytes_rx += bytes_to_read;
+	mutex_unlock(&devp->rx_lock);
+
+	SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s: "
+		"Returning %d bytes to ch%d\n", __func__,
+			bytes_to_read, devp->id);
+	return bytes_to_read;
+}
+
+static int smux_ctl_writeable(int id)
+{
+	int r;
+	int dev_index;
+
+	if (id < 0 || id > smux_ctl_ch_id[SMUX_CTL_NUM_CHANNELS - 1])
+		return -ENODEV;
+
+	dev_index = get_ctl_dev_index(id);
+	if (dev_index < 0) {
+		pr_err(SMUX_CTL_MODULE_NAME ": %s: "
+			"Ch%d is not exported to user-space\n",
+			__func__, id);
+		return -ENODEV;
+	}
+
+	mutex_lock(&smux_ctl_devp[dev_index]->dev_lock);
+
+	if (signal_pending(current))
+		r = -ERESTARTSYS;
+	else if (smux_ctl_devp[dev_index]->state == SMUX_DISCONNECTED &&
+	    smux_ctl_devp[dev_index]->is_channel_reset != 0)
+		r = -ENETRESET;
+	else if (smux_ctl_devp[dev_index]->state != SMUX_CONNECTED)
+		r = -ENODEV;
+	else if (smux_ctl_devp[dev_index]->is_high_wm ||
+			smux_ctl_devp[dev_index]->write_pending)
+		r = 0;
+	else
+		r = SMUX_CTL_MAX_BUF_SIZE;
+
+	mutex_unlock(&smux_ctl_devp[dev_index]->dev_lock);
+
+	return r;
+
+}
+
+ssize_t smux_ctl_write(struct file *file,
+		const char __user *buf,
+		size_t count,
+		loff_t *ppos)
+{
+	int r = 0, id, write_err;
+	char *temp_buf;
+	struct smux_ctl_dev *devp;
+
+	if (count <= 0)
+		return -EINVAL;
+
+	devp = file->private_data;
+	if (!devp)
+		return -ENODEV;
+
+	SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s: writing %i bytes on ch%d\n",
+			__func__, count, devp->id);
+
+	id = devp->id;
+	r = wait_event_interruptible(devp->write_wait_queue,
+			0 != (write_err = smux_ctl_writeable(id)));
+
+	if (r < 0) {
+		pr_err(SMUX_CTL_MODULE_NAME
+				": %s: wait_event_interruptible "
+				"ret %i\n", __func__, r);
+		return r;
+	}
+
+	if (write_err < 0) {
+		pr_err(SMUX_CTL_MODULE_NAME ": %s:"
+				"Write block failed for Ch%d, err %d\n",
+				__func__, devp->id, write_err);
+		return write_err;
+	}
+
+	temp_buf = kmalloc(count, GFP_KERNEL);
+	if (!temp_buf) {
+		pr_err(SMUX_CTL_MODULE_NAME
+				": %s: temp_buf alloc failed\n", __func__);
+		return -ENOMEM;
+	}
+
+	if (copy_from_user(temp_buf, buf, count)) {
+		pr_err(SMUX_CTL_MODULE_NAME
+				": %s: copy_from_user failed\n", __func__);
+		kfree(temp_buf);
+		return -EFAULT;
+	}
+
+	mutex_lock(&devp->dev_lock);
+	devp->write_pending = 1;
+	mutex_unlock(&devp->dev_lock);
+
+	r = msm_smux_write(id, NULL, (void *)temp_buf, count);
+	if (r < 0) {
+		pr_err(SMUX_CTL_MODULE_NAME
+			": %s: smux_write on Ch%dfailed, err %d\n",
+				__func__, id, r);
+		mutex_lock(&devp->dev_lock);
+		devp->write_pending = 0;
+		mutex_unlock(&devp->dev_lock);
+		return r;
+	}
+
+	r = wait_event_interruptible(devp->write_wait_queue,
+			0 != (write_err = smux_ctl_writeable(id)));
+	if (r < 0) {
+		pr_err(SMUX_CTL_MODULE_NAME " :%s: wait_event_interruptible "
+				"ret %i\n", __func__, r);
+		mutex_lock(&devp->dev_lock);
+		devp->write_pending = 0;
+		mutex_unlock(&devp->dev_lock);
+		return r;
+	}
+
+	mutex_lock(&devp->dev_lock);
+	devp->write_pending = 0;
+	devp->stats.pkts_tx++;
+	devp->stats.bytes_tx += count;
+	mutex_unlock(&devp->dev_lock);
+	return count;
+}
+
+static long smux_ctl_ioctl(struct file *file, unsigned int cmd,
+				unsigned long arg)
+{
+	int ret;
+	struct smux_ctl_dev *devp;
+
+	devp = file->private_data;
+	if (!devp)
+		return -ENODEV;
+
+	SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s called on smuxctl%d device\n",
+			__func__, devp->id);
+
+	switch (cmd) {
+	case TIOCMGET:
+		ret = msm_smux_tiocm_get(devp->id);
+		break;
+	case TIOCMSET:
+		ret = msm_smux_tiocm_set(devp->id, arg, ~arg);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static const struct file_operations smux_ctl_fops = {
+	.owner = THIS_MODULE,
+	.open = smux_ctl_open,
+	.release = smux_ctl_release,
+	.read = smux_ctl_read,
+	.write = smux_ctl_write,
+	.unlocked_ioctl = smux_ctl_ioctl,
+};
+
+static int smux_ctl_probe(struct platform_device *pdev)
+{
+	int i;
+	int r;
+
+	SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s Begins\n", __func__);
+
+	for (i = 0; i < SMUX_CTL_NUM_CHANNELS; ++i) {
+		smux_ctl_devp[i] = kzalloc(sizeof(struct smux_ctl_dev),
+							GFP_KERNEL);
+		if (IS_ERR(smux_ctl_devp[i])) {
+			pr_err(SMUX_CTL_MODULE_NAME
+				 ": %s kmalloc() ENOMEM\n", __func__);
+			r = -ENOMEM;
+			goto error0;
+		}
+
+		smux_ctl_devp[i]->id = smux_ctl_ch_id[i];
+		atomic_set(&smux_ctl_devp[i]->ref_count, 0);
+		smux_ctl_devp[i]->is_high_wm = 0;
+		smux_ctl_devp[i]->write_pending = 0;
+		smux_ctl_devp[i]->is_channel_reset = 0;
+		smux_ctl_devp[i]->state = SMUX_DISCONNECTED;
+		smux_ctl_devp[i]->read_avail = 0;
+
+		smux_ctl_devp[i]->stats.bytes_tx = 0;
+		smux_ctl_devp[i]->stats.bytes_rx = 0;
+		smux_ctl_devp[i]->stats.pkts_tx = 0;
+		smux_ctl_devp[i]->stats.pkts_rx = 0;
+		smux_ctl_devp[i]->stats.cnt_ssr = 0;
+		smux_ctl_devp[i]->stats.cnt_read_fail = 0;
+		smux_ctl_devp[i]->stats.cnt_write_fail = 0;
+		smux_ctl_devp[i]->stats.cnt_high_wm_hit = 0;
+
+		mutex_init(&smux_ctl_devp[i]->dev_lock);
+		init_waitqueue_head(&smux_ctl_devp[i]->read_wait_queue);
+		init_waitqueue_head(&smux_ctl_devp[i]->write_wait_queue);
+		mutex_init(&smux_ctl_devp[i]->rx_lock);
+		INIT_LIST_HEAD(&smux_ctl_devp[i]->rx_list);
+	}
+
+	r = alloc_chrdev_region(&smux_ctl_number, 0, SMUX_CTL_NUM_CHANNELS,
+							DEVICE_NAME);
+	if (IS_ERR_VALUE(r)) {
+		pr_err(SMUX_CTL_MODULE_NAME ": %s: "
+				"alloc_chrdev_region() ret %i.\n",
+					 __func__, r);
+		goto error0;
+	}
+
+	smux_ctl_classp = class_create(THIS_MODULE, DEVICE_NAME);
+	if (IS_ERR(smux_ctl_classp)) {
+		pr_err(SMUX_CTL_MODULE_NAME ": %s: "
+				"class_create() ENOMEM\n", __func__);
+		r = -ENOMEM;
+		goto error1;
+	}
+
+	for (i = 0; i < SMUX_CTL_NUM_CHANNELS; ++i) {
+		cdev_init(&smux_ctl_devp[i]->cdev, &smux_ctl_fops);
+		smux_ctl_devp[i]->cdev.owner = THIS_MODULE;
+
+		r = cdev_add(&smux_ctl_devp[i]->cdev, (smux_ctl_number + i), 1);
+
+		if (IS_ERR_VALUE(r)) {
+			pr_err(SMUX_CTL_MODULE_NAME ": %s: "
+					"cdev_add() ret %i\n", __func__, r);
+			kfree(smux_ctl_devp[i]);
+			goto error2;
+		}
+
+		smux_ctl_devp[i]->devicep =
+				device_create(smux_ctl_classp, NULL,
+					(smux_ctl_number + i), NULL,
+					DEVICE_NAME "%d", smux_ctl_ch_id[i]);
+
+		if (IS_ERR(smux_ctl_devp[i]->devicep)) {
+			pr_err(SMUX_CTL_MODULE_NAME ": %s: "
+					"device_create() ENOMEM\n", __func__);
+			r = -ENOMEM;
+			cdev_del(&smux_ctl_devp[i]->cdev);
+			kfree(smux_ctl_devp[i]);
+			goto error2;
+		}
+	}
+
+	smux_ctl_inited = 1;
+	SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s: "
+		"SMUX Control Port Driver Initialized.\n", __func__);
+	return 0;
+
+error2:
+	while (--i >= 0) {
+		cdev_del(&smux_ctl_devp[i]->cdev);
+		device_destroy(smux_ctl_classp,
+			MKDEV(MAJOR(smux_ctl_number), i));
+	}
+
+	class_destroy(smux_ctl_classp);
+	i = SMUX_CTL_NUM_CHANNELS;
+
+error1:
+	unregister_chrdev_region(MAJOR(smux_ctl_number),
+			SMUX_CTL_NUM_CHANNELS);
+
+error0:
+	while (--i >= 0)
+		kfree(smux_ctl_devp[i]);
+
+	return r;
+}
+
+static int smux_ctl_remove(struct platform_device *pdev)
+{
+	int i;
+
+	SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s Begins\n", __func__);
+
+	for (i = 0; i < SMUX_CTL_NUM_CHANNELS; ++i) {
+		cdev_del(&smux_ctl_devp[i]->cdev);
+		kfree(smux_ctl_devp[i]);
+		device_destroy(smux_ctl_classp,
+			MKDEV(MAJOR(smux_ctl_number), i));
+	}
+	class_destroy(smux_ctl_classp);
+	unregister_chrdev_region(MAJOR(smux_ctl_number),
+			SMUX_CTL_NUM_CHANNELS);
+
+	return 0;
+}
+
+static struct platform_driver smux_ctl_driver = {
+	.probe = smux_ctl_probe,
+	.remove = smux_ctl_remove,
+	.driver = {
+		.name = "SMUX_CTL",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init smux_ctl_init(void)
+{
+	msm_smux_ctl_debug_mask = MSM_SMUX_CTL_DEBUG | MSM_SMUX_CTL_DUMP_BUFFER;
+
+	SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s Begins\n", __func__);
+	return platform_driver_register(&smux_ctl_driver);
+}
+
+
+#if defined(CONFIG_DEBUG_FS)
+
+#define DEBUG_BUFMAX 4096
+static char debug_buffer[DEBUG_BUFMAX];
+
+static ssize_t debug_read(struct file *file, char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	int bsize = 0;
+	int i;
+	if (!smux_ctl_inited) {
+		pr_err(SMUX_CTL_MODULE_NAME ": %s: SMUX_CTL not yet inited\n",
+				__func__);
+		return -EIO;
+	}
+
+	bsize += scnprintf(debug_buffer + bsize, DEBUG_BUFMAX - bsize,
+				"SMUX_CTL Channel States:\n");
+
+	for (i = 0; i < SMUX_CTL_NUM_CHANNELS; ++i) {
+		bsize += scnprintf(debug_buffer + bsize, DEBUG_BUFMAX - bsize,
+		"Ch%02d %s RefCnt=%01d State=%02d "
+		"SSR=%02d HighWM=%02d ReadAvail=%04d WritePending=%02d\n",
+		smux_ctl_devp[i]->id,
+		smux_ctl_devp[i]->name,
+		atomic_read(&smux_ctl_devp[i]->ref_count),
+		smux_ctl_devp[i]->state,
+		smux_ctl_devp[i]->is_channel_reset,
+		smux_ctl_devp[i]->is_high_wm,
+		smux_ctl_devp[i]->read_avail,
+		smux_ctl_devp[i]->write_pending);
+	}
+
+	bsize += scnprintf(debug_buffer + bsize, DEBUG_BUFMAX - bsize,
+				"\nSMUX_CTL Channel Statistics:\n");
+	for (i = 0; i < SMUX_CTL_NUM_CHANNELS; ++i) {
+		bsize += scnprintf(debug_buffer + bsize, DEBUG_BUFMAX - bsize,
+			"Ch%02d %s BytesTX=%08d "
+				"BytesRx=%08d PktsTx=%04d PktsRx=%04d"
+			"CntSSR=%02d CntHighWM=%02d "
+				"CntReadFail%02d CntWriteFailed=%02d\n",
+			smux_ctl_devp[i]->id,
+			smux_ctl_devp[i]->name,
+			smux_ctl_devp[i]->stats.bytes_tx,
+			smux_ctl_devp[i]->stats.bytes_rx,
+			smux_ctl_devp[i]->stats.pkts_tx,
+			smux_ctl_devp[i]->stats.pkts_rx,
+			smux_ctl_devp[i]->stats.cnt_ssr,
+			smux_ctl_devp[i]->stats.cnt_high_wm_hit,
+			smux_ctl_devp[i]->stats.cnt_read_fail,
+			smux_ctl_devp[i]->stats.cnt_write_fail);
+	}
+
+	return simple_read_from_buffer(buf, count, ppos, debug_buffer, bsize);
+}
+
+static int debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static const struct file_operations debug_ops = {
+	.read = debug_read,
+	.open = debug_open,
+};
+
+static int __init smux_debugfs_init(void)
+{
+	struct dentry *dent;
+
+	dent = debugfs_create_dir("smux_ctl", 0);
+	if (!IS_ERR(dent))
+		debugfs_create_file("smux_ctl_state", 0444, dent,
+			NULL, &debug_ops);
+
+	return 0;
+}
+
+late_initcall(smux_debugfs_init);
+#endif
+
+module_init(smux_ctl_init);
+MODULE_DESCRIPTION("MSM SMUX Control Port");
+MODULE_LICENSE("GPL v2");
+
+
diff --git a/drivers/tty/smux_loopback.c b/drivers/tty/smux_loopback.c
new file mode 100644
index 0000000..52ce17f
--- /dev/null
+++ b/drivers/tty/smux_loopback.c
@@ -0,0 +1,289 @@
+/* drivers/tty/smux_loopback.c
+ *
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * 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/types.h>
+#include <linux/err.h>
+#include <linux/workqueue.h>
+#include <linux/kfifo.h>
+#include <linux/slab.h>
+#include <linux/smux.h>
+#include "smux_private.h"
+
+#define SMUX_LOOP_FIFO_SIZE	128
+
+static void smux_loopback_rx_worker(struct work_struct *work);
+static struct workqueue_struct *smux_loopback_wq;
+static DECLARE_WORK(smux_loopback_work, smux_loopback_rx_worker);
+static struct kfifo smux_loop_pkt_fifo;
+static DEFINE_SPINLOCK(hw_fn_lock);
+
+/**
+ * Initialize loopback framework (called by n_smux.c).
+ */
+int smux_loopback_init(void)
+{
+	int ret = 0;
+
+	spin_lock_init(&hw_fn_lock);
+	smux_loopback_wq = create_singlethread_workqueue("smux_loopback_wq");
+	if (IS_ERR(smux_loopback_wq)) {
+		pr_err("%s: failed to create workqueue\n", __func__);
+		return -ENOMEM;
+	}
+
+	ret |= kfifo_alloc(&smux_loop_pkt_fifo,
+			SMUX_LOOP_FIFO_SIZE * sizeof(struct smux_pkt_t *),
+			GFP_KERNEL);
+
+	return ret;
+}
+
+/**
+ * Simulate a write to the TTY hardware by duplicating
+ * the TX packet and putting it into the RX queue.
+ *
+ * @pkt     Packet to write
+ *
+ * @returns 0 on success
+ */
+int smux_tx_loopback(struct smux_pkt_t *pkt_ptr)
+{
+	struct smux_pkt_t *send_pkt;
+	unsigned long flags;
+	int i;
+	int ret;
+
+	/* duplicate packet */
+	send_pkt = smux_alloc_pkt();
+	send_pkt->hdr = pkt_ptr->hdr;
+	if (pkt_ptr->hdr.payload_len) {
+		ret = smux_alloc_pkt_payload(send_pkt);
+		if (ret) {
+			ret = -ENOMEM;
+			goto out;
+		}
+		memcpy(send_pkt->payload, pkt_ptr->payload,
+				pkt_ptr->hdr.payload_len);
+	}
+
+	/* queue duplicate as pseudo-RX data */
+	spin_lock_irqsave(&hw_fn_lock, flags);
+	i = kfifo_avail(&smux_loop_pkt_fifo);
+	if (i < sizeof(struct smux_pkt_t *)) {
+		pr_err("%s: no space in fifo\n", __func__);
+		ret = -ENOMEM;
+		goto unlock;
+	}
+
+	i = kfifo_in(&smux_loop_pkt_fifo,
+			&send_pkt,
+			sizeof(struct smux_pkt_t *));
+	if (i < 0) {
+		pr_err("%s: fifo error\n", __func__);
+		ret = -ENOMEM;
+		goto unlock;
+	}
+	queue_work(smux_loopback_wq, &smux_loopback_work);
+	ret = 0;
+
+unlock:
+	spin_unlock_irqrestore(&hw_fn_lock, flags);
+out:
+	return ret;
+}
+
+/**
+ * Receive loopback byte processor.
+ *
+ * @pkt  Incoming packet
+ */
+static void smux_loopback_rx_byte(struct smux_pkt_t *pkt)
+{
+	static int simulated_retry_cnt;
+	const char ack = SMUX_WAKEUP_ACK;
+
+	switch (pkt->hdr.flags) {
+	case SMUX_WAKEUP_REQ:
+		/* reply with ACK after appropriate delays */
+		++simulated_retry_cnt;
+		if (simulated_retry_cnt >= smux_simulate_wakeup_delay) {
+			pr_err("%s: completed %d of %d\n",
+				__func__, simulated_retry_cnt,
+				smux_simulate_wakeup_delay);
+			pr_err("%s: simulated wakeup\n", __func__);
+			simulated_retry_cnt = 0;
+			smux_rx_state_machine(&ack, 1, 0);
+		} else {
+			/* force retry */
+			pr_err("%s: dropping wakeup request %d of %d\n",
+					__func__, simulated_retry_cnt,
+					smux_simulate_wakeup_delay);
+		}
+		break;
+	case SMUX_WAKEUP_ACK:
+		/* this shouldn't happen since we don't send requests */
+		pr_err("%s: wakeup ACK unexpected\n", __func__);
+		break;
+
+	default:
+		/* invalid character */
+		pr_err("%s: invalid character 0x%x\n",
+				__func__, (unsigned)pkt->hdr.flags);
+		break;
+	}
+}
+
+/**
+ * Simulated remote hardware used for local loopback testing.
+ *
+ * @work Not used
+ */
+static void smux_loopback_rx_worker(struct work_struct *work)
+{
+	struct smux_pkt_t *pkt;
+	struct smux_pkt_t reply_pkt;
+	char *data;
+	int len;
+	int lcid;
+	int i;
+	unsigned long flags;
+
+	data = kzalloc(SMUX_MAX_PKT_SIZE, GFP_ATOMIC);
+
+	spin_lock_irqsave(&hw_fn_lock, flags);
+	while (kfifo_len(&smux_loop_pkt_fifo) >= sizeof(struct smux_pkt_t *)) {
+		i = kfifo_out(&smux_loop_pkt_fifo, &pkt,
+					sizeof(struct smux_pkt_t *));
+		spin_unlock_irqrestore(&hw_fn_lock, flags);
+
+		if (pkt->hdr.magic != SMUX_MAGIC) {
+			pr_err("%s: invalid magic %x\n", __func__,
+					pkt->hdr.magic);
+			return;
+		}
+
+		lcid = pkt->hdr.lcid;
+		if (smux_assert_lch_id(lcid)) {
+			pr_err("%s: invalid channel id %d\n", __func__, lcid);
+			return;
+		}
+
+		switch (pkt->hdr.cmd) {
+		case SMUX_CMD_OPEN_LCH:
+			if (pkt->hdr.flags & SMUX_CMD_OPEN_ACK)
+				break;
+
+			/* Reply with Open ACK */
+			smux_init_pkt(&reply_pkt);
+			reply_pkt.hdr.lcid = lcid;
+			reply_pkt.hdr.cmd = SMUX_CMD_OPEN_LCH;
+			reply_pkt.hdr.flags = SMUX_CMD_OPEN_ACK
+				| SMUX_CMD_OPEN_POWER_COLLAPSE;
+			reply_pkt.hdr.payload_len = 0;
+			reply_pkt.hdr.pad_len = 0;
+			smux_serialize(&reply_pkt, data, &len);
+			smux_rx_state_machine(data, len, 0);
+
+			/* Send Remote Open */
+			smux_init_pkt(&reply_pkt);
+			reply_pkt.hdr.lcid = lcid;
+			reply_pkt.hdr.cmd = SMUX_CMD_OPEN_LCH;
+			reply_pkt.hdr.flags = SMUX_CMD_OPEN_POWER_COLLAPSE;
+			reply_pkt.hdr.payload_len = 0;
+			reply_pkt.hdr.pad_len = 0;
+			smux_serialize(&reply_pkt, data, &len);
+			smux_rx_state_machine(data, len, 0);
+			break;
+
+		case SMUX_CMD_CLOSE_LCH:
+			if (pkt->hdr.flags == SMUX_CMD_CLOSE_ACK)
+				break;
+
+			/* Reply with Close ACK */
+			smux_init_pkt(&reply_pkt);
+			reply_pkt.hdr.lcid = lcid;
+			reply_pkt.hdr.cmd = SMUX_CMD_CLOSE_LCH;
+			reply_pkt.hdr.flags = SMUX_CMD_CLOSE_ACK;
+			reply_pkt.hdr.payload_len = 0;
+			reply_pkt.hdr.pad_len = 0;
+			smux_serialize(&reply_pkt, data, &len);
+			smux_rx_state_machine(data, len, 0);
+
+			/* Send Remote Close */
+			smux_init_pkt(&reply_pkt);
+			reply_pkt.hdr.lcid = lcid;
+			reply_pkt.hdr.cmd = SMUX_CMD_CLOSE_LCH;
+			reply_pkt.hdr.flags = 0;
+			reply_pkt.hdr.payload_len = 0;
+			reply_pkt.hdr.pad_len = 0;
+			smux_serialize(&reply_pkt, data, &len);
+			smux_rx_state_machine(data, len, 0);
+			break;
+
+		case SMUX_CMD_DATA:
+			/* Echo back received data */
+			smux_init_pkt(&reply_pkt);
+			reply_pkt.hdr.lcid = lcid;
+			reply_pkt.hdr.cmd = SMUX_CMD_DATA;
+			reply_pkt.hdr.flags = 0;
+			reply_pkt.hdr.payload_len = pkt->hdr.payload_len;
+			reply_pkt.payload = pkt->payload;
+			reply_pkt.hdr.pad_len = pkt->hdr.pad_len;
+			smux_serialize(&reply_pkt, data, &len);
+			smux_rx_state_machine(data, len, 0);
+			break;
+
+		case SMUX_CMD_STATUS:
+			/* Echo back received status */
+			smux_init_pkt(&reply_pkt);
+			reply_pkt.hdr.lcid = lcid;
+			reply_pkt.hdr.cmd = SMUX_CMD_STATUS;
+			reply_pkt.hdr.flags = pkt->hdr.flags;
+			reply_pkt.hdr.payload_len = 0;
+			reply_pkt.payload = NULL;
+			reply_pkt.hdr.pad_len = pkt->hdr.pad_len;
+			smux_serialize(&reply_pkt, data, &len);
+			smux_rx_state_machine(data, len, 0);
+			break;
+
+		case SMUX_CMD_PWR_CTL:
+			/* reply with ack */
+			smux_init_pkt(&reply_pkt);
+			reply_pkt.hdr.lcid = lcid;
+			reply_pkt.hdr.cmd = SMUX_CMD_PWR_CTL;
+			reply_pkt.hdr.flags = SMUX_CMD_PWR_CTL_SLEEP_REQ
+				| SMUX_CMD_PWR_CTL_ACK;
+			reply_pkt.hdr.payload_len = 0;
+			reply_pkt.payload = NULL;
+			reply_pkt.hdr.pad_len = pkt->hdr.pad_len;
+			smux_serialize(&reply_pkt, data, &len);
+			smux_rx_state_machine(data, len, 0);
+			break;
+
+		case SMUX_CMD_BYTE:
+			smux_loopback_rx_byte(pkt);
+			break;
+
+		default:
+			pr_err("%s: unknown command %d\n",
+					__func__, pkt->hdr.cmd);
+			break;
+		};
+
+		smux_free_pkt(pkt);
+		spin_lock_irqsave(&hw_fn_lock, flags);
+	}
+	spin_unlock_irqrestore(&hw_fn_lock, flags);
+	kfree(data);
+}
diff --git a/drivers/tty/smux_loopback.h b/drivers/tty/smux_loopback.h
new file mode 100644
index 0000000..85c6c23
--- /dev/null
+++ b/drivers/tty/smux_loopback.h
@@ -0,0 +1,39 @@
+/* drivers/tty/smux_loopback.h
+ *
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * 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.
+ *
+ */
+#ifndef SMUX_LOOPBACK_H
+#define SMUX_LOOPBACK_H
+
+#include "smux_private.h"
+
+#ifdef CONFIG_N_SMUX_LOOPBACK
+
+int smux_loopback_init(void);
+int smux_tx_loopback(struct smux_pkt_t *pkt_ptr);
+
+#else
+static inline int smux_loopback_init(void)
+{
+	return 0;
+}
+
+static inline int smux_tx_loopback(struct smux_pkt_t *pkt_ptr)
+{
+	return -ENODEV;
+}
+
+
+#endif /* CONFIG_N_SMUX_LOOPBACK */
+#endif /* SMUX_LOOPBACK_H */
+
diff --git a/drivers/tty/smux_private.h b/drivers/tty/smux_private.h
new file mode 100644
index 0000000..5ce8fb8
--- /dev/null
+++ b/drivers/tty/smux_private.h
@@ -0,0 +1,115 @@
+/* drivers/tty/smux_private.h
+ *
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * 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.
+ *
+ */
+#ifndef SMUX_PRIVATE_H
+#define SMUX_PRIVATE_H
+
+#define SMUX_MAX_PKT_SIZE   8192
+
+/* SMUX Protocol Characters */
+#define SMUX_MAGIC          0x33FC
+#define SMUX_MAGIC_WORD1    0xFC
+#define SMUX_MAGIC_WORD2    0x33
+#define SMUX_WAKEUP_REQ     0xFD
+#define SMUX_WAKEUP_ACK     0xFE
+
+/* Unit testing characters */
+#define SMUX_UT_ECHO_REQ    0xF0
+#define SMUX_UT_ECHO_ACK_OK 0xF1
+#define SMUX_UT_ECHO_ACK_FAIL 0xF2
+
+struct tty_struct;
+
+/* Packet header. */
+struct smux_hdr_t {
+	uint16_t magic;
+	uint8_t flags;
+	uint8_t cmd;
+	uint8_t pad_len;
+	uint8_t lcid;
+	uint16_t payload_len;
+};
+
+/* Internal packet structure. */
+struct smux_pkt_t {
+	struct smux_hdr_t hdr;
+	int allocated;
+	unsigned char *payload;
+	int free_payload;
+	struct list_head list;
+	void *priv;
+};
+
+/* SMUX Packet Commands */
+enum {
+	SMUX_CMD_DATA = 0x0,
+	SMUX_CMD_OPEN_LCH = 0x1,
+	SMUX_CMD_CLOSE_LCH = 0x2,
+	SMUX_CMD_STATUS = 0x3,
+	SMUX_CMD_PWR_CTL = 0x4,
+
+	SMUX_CMD_BYTE, /* for internal usage */
+	SMUX_NUM_COMMANDS
+};
+
+/* Open command flags */
+enum {
+	SMUX_CMD_OPEN_ACK = 1 << 0,
+	SMUX_CMD_OPEN_POWER_COLLAPSE = 1 << 1,
+	SMUX_CMD_OPEN_REMOTE_LOOPBACK = 1 << 2,
+};
+
+/* Close command flags */
+enum {
+	SMUX_CMD_CLOSE_ACK = 1 << 0,
+};
+
+/* Power command flags */
+enum {
+	SMUX_CMD_PWR_CTL_ACK =  1 << 0,
+	SMUX_CMD_PWR_CTL_SLEEP_REQ =  1 << 1,
+};
+
+/* Local logical channel states */
+enum {
+	SMUX_LCH_LOCAL_CLOSED,
+	SMUX_LCH_LOCAL_OPENING,
+	SMUX_LCH_LOCAL_OPENED,
+	SMUX_LCH_LOCAL_CLOSING,
+};
+
+/* Remote logical channel states */
+enum {
+	SMUX_LCH_REMOTE_CLOSED,
+	SMUX_LCH_REMOTE_OPENED,
+};
+
+
+int smux_assert_lch_id(uint32_t lcid);
+void smux_init_pkt(struct smux_pkt_t *pkt);
+struct smux_pkt_t *smux_alloc_pkt(void);
+int smux_alloc_pkt_payload(struct smux_pkt_t *pkt);
+void smux_free_pkt(struct smux_pkt_t *pkt);
+int smux_serialize(struct smux_pkt_t *pkt, char *out,
+					unsigned int *out_len);
+
+void smux_rx_state_machine(const unsigned char *data, int len, int flag);
+void smuxld_receive_buf(struct tty_struct *tty, const unsigned char *cp,
+			   char *fp, int count);
+
+/* testing parameters */
+extern int smux_byte_loopback;
+extern int smux_simulate_wakeup_delay;
+
+#endif /* SMUX_PRIVATE_H */
diff --git a/drivers/tty/smux_test.c b/drivers/tty/smux_test.c
new file mode 100644
index 0000000..242c66e
--- /dev/null
+++ b/drivers/tty/smux_test.c
@@ -0,0 +1,1222 @@
+/* drivers/tty/smux_test.c
+ *
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * 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/debugfs.h>
+#include <linux/list.h>
+#include <linux/ctype.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+#include <linux/termios.h>
+#include <linux/smux.h>
+#include "smux_private.h"
+
+#define DEBUG_BUFMAX 4096
+
+/**
+ * Unit test assertion for logging test cases.
+ *
+ * @a lval
+ * @b rval
+ * @cmp comparison operator
+ *
+ * Assertion fails if (@a cmp @b) is not true which then
+ * logs the function and line number where the error occurred
+ * along with the values of @a and @b.
+ *
+ * Assumes that the following local variables exist:
+ * @buf - buffer to write failure message to
+ * @i - number of bytes written to buffer
+ * @max - maximum size of the buffer
+ * @failed - set to true if test fails
+ */
+#define UT_ASSERT_INT(a, cmp, b) \
+	if (!((a)cmp(b))) { \
+		i += scnprintf(buf + i, max - i, \
+			"%s:%d Fail: " #a "(%d) " #cmp " " #b "(%d)\n", \
+				__func__, __LINE__, \
+				a, b); \
+		failed = 1; \
+		break; \
+	} \
+	do {} while (0)
+
+#define UT_ASSERT_PTR(a, cmp, b) \
+	if (!((a)cmp(b))) { \
+		i += scnprintf(buf + i, max - i, \
+			"%s:%d Fail: " #a "(%p) " #cmp " " #b "(%p)\n", \
+				__func__, __LINE__, \
+				a, b); \
+		failed = 1; \
+		break; \
+	} \
+	do {} while (0)
+
+#define UT_ASSERT_UINT(a, cmp, b) \
+	if (!((a)cmp(b))) { \
+		i += scnprintf(buf + i, max - i, \
+			"%s:%d Fail: " #a "(%u) " #cmp " " #b "(%u)\n", \
+				__func__, __LINE__, \
+				a, b); \
+		failed = 1; \
+		break; \
+	} \
+	do {} while (0)
+
+static unsigned char test_array[] = {1, 1, 2, 3, 5, 8, 13, 21, 34, 55,
+					89, 144, 233};
+
+/* Used for mapping local to remote TIOCM signals */
+struct tiocm_test_vector {
+	uint32_t input;
+	uint32_t set_old;
+	uint32_t set_new;
+	uint32_t clr_old;
+};
+
+/**
+ * Allocates a new buffer for SMUX for every call.
+ */
+int get_rx_buffer(void *priv, void **pkt_priv, void **buffer, int size)
+{
+	void *rx_buf;
+
+	rx_buf = kmalloc(size, GFP_ATOMIC);
+	*pkt_priv = (void *)0x1234;
+	*buffer = rx_buf;
+
+	return 0;
+}
+
+/* Test vector for packet tests. */
+struct test_vector {
+	const char *data;
+	const unsigned len;
+};
+
+/* Mock object metadata for SMUX_READ_DONE event */
+struct mock_read_event {
+	struct list_head list;
+	struct smux_meta_read meta;
+};
+
+/* Mock object metadata for SMUX_WRITE_DONE event */
+struct mock_write_event {
+	struct list_head list;
+	struct smux_meta_write meta;
+};
+
+/* Mock object for all SMUX callback events */
+struct smux_mock_callback {
+	int cb_count;
+	struct completion cb_completion;
+	spinlock_t lock;
+
+	/* status changes */
+	int event_connected;
+	int event_disconnected;
+	int event_disconnected_ssr;
+	int event_low_wm;
+	int event_high_wm;
+
+	/* TIOCM changes */
+	int event_tiocm;
+	struct smux_meta_tiocm tiocm_meta;
+
+	/* read event data */
+	int event_read_done;
+	int event_read_failed;
+	struct list_head read_events;
+
+	/* write event data */
+	int event_write_done;
+	int event_write_failed;
+	struct list_head write_events;
+};
+
+/**
+ * Initialize mock callback data. Only call once.
+ *
+ * @cb  Mock callback data
+ */
+void mock_cb_data_init(struct smux_mock_callback *cb)
+{
+	init_completion(&cb->cb_completion);
+	spin_lock_init(&cb->lock);
+	INIT_LIST_HEAD(&cb->read_events);
+	INIT_LIST_HEAD(&cb->write_events);
+}
+
+/**
+ * Reset mock callback data to default values.
+ *
+ * @cb  Mock callback data
+ *
+ * All packets are freed and counters reset to zero.
+ */
+void mock_cb_data_reset(struct smux_mock_callback *cb)
+{
+	cb->cb_count = 0;
+	INIT_COMPLETION(cb->cb_completion);
+	cb->event_connected = 0;
+	cb->event_disconnected = 0;
+	cb->event_disconnected_ssr = 0;
+	cb->event_low_wm = 0;
+	cb->event_high_wm = 0;
+	cb->event_tiocm = 0;
+	cb->tiocm_meta.tiocm_old = 0;
+	cb->tiocm_meta.tiocm_new = 0;
+
+	cb->event_read_done = 0;
+	cb->event_read_failed = 0;
+	while (!list_empty(&cb->read_events)) {
+		struct mock_read_event *meta;
+		meta = list_first_entry(&cb->read_events,
+				struct mock_read_event,
+				list);
+		kfree(meta->meta.buffer);
+		list_del(&meta->list);
+		kfree(meta);
+	}
+
+	cb->event_write_done = 0;
+	cb->event_write_failed = 0;
+	while (!list_empty(&cb->write_events)) {
+		struct mock_write_event *meta;
+		meta = list_first_entry(&cb->write_events,
+				struct mock_write_event,
+				list);
+		list_del(&meta->list);
+		kfree(meta);
+	}
+}
+
+/**
+ * Dump the values of the mock callback data for debug purposes.
+ *
+ * @cb  Mock callback data
+ * @buf Print buffer
+ * @max Maximum number of characters to print
+ *
+ * @returns Number of characters added to buffer
+ */
+static int mock_cb_data_print(const struct smux_mock_callback *cb,
+		char *buf, int max)
+{
+	int i = 0;
+
+	i += scnprintf(buf + i, max - i,
+		"\tcb_count=%d\n"
+		"\tcb_completion.done=%d\n"
+		"\tevent_connected=%d\n"
+		"\tevent_disconnected=%d\n"
+		"\tevent_disconnected_ssr=%d\n"
+		"\tevent_low_wm=%d\n"
+		"\tevent_high_wm=%d\n"
+		"\tevent_tiocm=%d\n"
+		"\tevent_read_done=%d\n"
+		"\tevent_read_failed=%d\n"
+		"\tread_events=%d\n"
+		"\tevent_write_done=%d\n"
+		"\tevent_write_failed=%d\n"
+		"\twrite_events=%d\n",
+		cb->cb_count,
+		cb->cb_completion.done,
+		cb->event_connected,
+		cb->event_disconnected,
+		cb->event_disconnected_ssr,
+		cb->event_low_wm,
+		cb->event_high_wm,
+		cb->event_tiocm,
+		cb->event_read_done,
+		cb->event_read_failed,
+		!list_empty(&cb->read_events),
+		cb->event_write_done,
+		cb->event_write_failed,
+		list_empty(&cb->write_events)
+		);
+
+	return i;
+}
+
+/**
+ * Mock object event callback.  Used to logs events for analysis in the unit
+ * tests.
+ */
+void smux_mock_cb(void *priv, int event, const void *metadata)
+{
+	struct smux_mock_callback *cb_data_ptr;
+	struct mock_write_event *write_event_meta;
+	struct mock_read_event *read_event_meta;
+	unsigned long flags;
+
+	cb_data_ptr = (struct smux_mock_callback *)priv;
+	if (cb_data_ptr == NULL) {
+		pr_err("%s: invalid private data\n", __func__);
+		return;
+	}
+
+	spin_lock_irqsave(&cb_data_ptr->lock, flags);
+	switch (event) {
+	case SMUX_CONNECTED:
+		++cb_data_ptr->event_connected;
+		break;
+
+	case SMUX_DISCONNECTED:
+		++cb_data_ptr->event_disconnected;
+		cb_data_ptr->event_disconnected_ssr =
+			((struct smux_meta_disconnected *)metadata)->is_ssr;
+		break;
+
+	case SMUX_READ_DONE:
+		++cb_data_ptr->event_read_done;
+		read_event_meta = kmalloc(sizeof(struct mock_read_event),
+						GFP_ATOMIC);
+		if (read_event_meta) {
+			read_event_meta->meta =
+				*(struct smux_meta_read *)metadata;
+			list_add_tail(&read_event_meta->list,
+						&cb_data_ptr->read_events);
+		}
+		break;
+
+	case SMUX_READ_FAIL:
+		++cb_data_ptr->event_read_failed;
+		read_event_meta = kmalloc(sizeof(struct mock_read_event),
+						GFP_ATOMIC);
+		if (read_event_meta) {
+			read_event_meta->meta =
+					*(struct smux_meta_read *)metadata;
+			list_add_tail(&read_event_meta->list,
+					&cb_data_ptr->read_events);
+		}
+		break;
+
+	case SMUX_WRITE_DONE:
+		++cb_data_ptr->event_write_done;
+		write_event_meta = kmalloc(sizeof(struct mock_write_event),
+						GFP_ATOMIC);
+		if (write_event_meta) {
+			write_event_meta->meta =
+					*(struct smux_meta_write *)metadata;
+			list_add_tail(&write_event_meta->list,
+					&cb_data_ptr->write_events);
+		}
+		break;
+
+	case SMUX_WRITE_FAIL:
+		++cb_data_ptr->event_write_failed;
+		write_event_meta = kmalloc(sizeof(struct mock_write_event),
+						GFP_ATOMIC);
+		if (write_event_meta) {
+			write_event_meta->meta =
+				*(struct smux_meta_write *)metadata;
+			list_add_tail(&write_event_meta->list,
+					&cb_data_ptr->write_events);
+		}
+		break;
+
+	case SMUX_LOW_WM_HIT:
+		++cb_data_ptr->event_low_wm;
+		break;
+
+	case SMUX_HIGH_WM_HIT:
+		++cb_data_ptr->event_high_wm;
+		break;
+
+	case SMUX_TIOCM_UPDATE:
+		++cb_data_ptr->event_tiocm;
+		cb_data_ptr->tiocm_meta = *(struct smux_meta_tiocm *)metadata;
+		break;
+
+	default:
+		pr_err("%s: unknown event %d\n", __func__, event);
+	};
+
+	++cb_data_ptr->cb_count;
+	complete(&cb_data_ptr->cb_completion);
+	spin_unlock_irqrestore(&cb_data_ptr->lock, flags);
+}
+
+/**
+ * Test Read/write usage.
+ *
+ * @buf       Output buffer for failure/status messages
+ * @max       Size of @buf
+ * @vectors   Test vector data (must end with NULL item)
+ * @name      Name of the test case for failure messages
+ *
+ * Perform a sanity test consisting of opening a port, writing test packet(s),
+ * reading the response(s), and closing the port.
+ *
+ * The port should already be configured to use either local or remote
+ * loopback.
+ */
+static int smux_ut_basic_core(char *buf, int max,
+	const struct test_vector *vectors,
+	const char *name)
+{
+	int i = 0;
+	int failed = 0;
+	static struct smux_mock_callback cb_data;
+	static int cb_initialized;
+	int ret;
+
+	if (!cb_initialized)
+		mock_cb_data_init(&cb_data);
+
+	mock_cb_data_reset(&cb_data);
+	while (!failed) {
+		struct mock_write_event *write_event;
+		struct mock_read_event *read_event;
+
+		/* open port */
+		ret = msm_smux_open(SMUX_TEST_LCID, &cb_data, smux_mock_cb,
+					get_rx_buffer);
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_INT(
+			(int)wait_for_completion_timeout(
+					&cb_data.cb_completion, HZ), >, 0);
+		UT_ASSERT_INT(cb_data.cb_count, ==, 1);
+		UT_ASSERT_INT(cb_data.event_connected, ==, 1);
+		mock_cb_data_reset(&cb_data);
+
+		/* write, read, and verify the test vector data */
+		for (; vectors->data != NULL; ++vectors) {
+			const char *test_data = vectors->data;
+			const unsigned test_len = vectors->len;
+
+			i += scnprintf(buf + i, max - i,
+					"Writing vector %p len %d\n",
+					test_data, test_len);
+
+			/* write data */
+			msm_smux_write(SMUX_TEST_LCID, (void *)0xCAFEFACE,
+					test_data, test_len);
+			UT_ASSERT_INT(ret, ==, 0);
+			UT_ASSERT_INT(
+					(int)wait_for_completion_timeout(
+					&cb_data.cb_completion, HZ), >, 0);
+
+			/* wait for write and echo'd read to complete */
+			INIT_COMPLETION(cb_data.cb_completion);
+			if (cb_data.cb_count < 2)
+				UT_ASSERT_INT(
+					(int)wait_for_completion_timeout(
+						&cb_data.cb_completion, HZ),
+					>, 0);
+
+			UT_ASSERT_INT(cb_data.cb_count, >=, 1);
+			UT_ASSERT_INT(cb_data.event_write_done, ==, 1);
+			UT_ASSERT_INT(list_empty(&cb_data.write_events), ==, 0);
+
+			write_event = list_first_entry(&cb_data.write_events,
+					struct mock_write_event, list);
+			UT_ASSERT_PTR(write_event->meta.pkt_priv, ==,
+							(void *)0xCAFEFACE);
+			UT_ASSERT_PTR(write_event->meta.buffer, ==,
+							(void *)test_data);
+			UT_ASSERT_INT(write_event->meta.len, ==, test_len);
+
+			/* verify read event */
+			UT_ASSERT_INT(cb_data.event_read_done, ==, 1);
+			UT_ASSERT_INT(list_empty(&cb_data.read_events), ==, 0);
+			read_event = list_first_entry(&cb_data.read_events,
+					struct mock_read_event, list);
+			UT_ASSERT_PTR(read_event->meta.pkt_priv, ==,
+							(void *)0x1234);
+			UT_ASSERT_PTR(read_event->meta.buffer, !=, NULL);
+
+			if (read_event->meta.len != test_len ||
+				memcmp(read_event->meta.buffer,
+						test_data, test_len)) {
+				/* data mismatch */
+				char linebuff[80];
+
+				hex_dump_to_buffer(test_data, test_len,
+					16, 1, linebuff, sizeof(linebuff), 1);
+				i += scnprintf(buf + i, max - i,
+						"Expected:\n%s\n\n", linebuff);
+
+				hex_dump_to_buffer(read_event->meta.buffer,
+					read_event->meta.len,
+					16, 1, linebuff, sizeof(linebuff), 1);
+				i += scnprintf(buf + i, max - i,
+						"Actual:\n%s\n", linebuff);
+				failed = 1;
+				break;
+			}
+			mock_cb_data_reset(&cb_data);
+		}
+
+		/* close port */
+		ret = msm_smux_close(SMUX_TEST_LCID);
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_INT(
+			(int)wait_for_completion_timeout(
+				&cb_data.cb_completion, HZ),
+			>, 0);
+		UT_ASSERT_INT(cb_data.cb_count, ==, 1);
+		UT_ASSERT_INT(cb_data.event_disconnected, ==, 1);
+		UT_ASSERT_INT(cb_data.event_disconnected_ssr, ==, 0);
+		break;
+	}
+
+	if (!failed) {
+		i += scnprintf(buf + i, max - i, "\tOK\n");
+	} else {
+		pr_err("%s: Failed\n", name);
+		i += scnprintf(buf + i, max - i, "\tFailed\n");
+		i += mock_cb_data_print(&cb_data, buf + i, max - i);
+		msm_smux_close(SMUX_TEST_LCID);
+	}
+
+	mock_cb_data_reset(&cb_data);
+	return i;
+}
+
+/**
+ * Verify Basic Local Loopback Support
+ *
+ * Perform a sanity test consisting of opening a port in local loopback
+ * mode and writing a packet and reading the echo'd packet back.
+ */
+static int smux_ut_basic(char *buf, int max)
+{
+	const struct test_vector test_data[] = {
+		{"hello\0world\n", sizeof("hello\0world\n")},
+		{0, 0},
+	};
+	int i = 0;
+	int failed = 0;
+	int ret;
+
+	i += scnprintf(buf + i, max - i, "Running %s\n", __func__);
+	while (!failed) {
+		/* enable loopback mode */
+		ret = msm_smux_set_ch_option(SMUX_TEST_LCID,
+				SMUX_CH_OPTION_LOCAL_LOOPBACK, 0);
+		UT_ASSERT_INT(ret, ==, 0);
+
+		i += smux_ut_basic_core(buf + i, max - i, test_data, __func__);
+		break;
+	}
+
+	if (failed) {
+		pr_err("%s: Failed\n", __func__);
+		i += scnprintf(buf + i, max - i, "\tFailed\n");
+	}
+	return i;
+}
+
+/**
+ * Verify Basic Remote Loopback Support
+ *
+ * Perform a sanity test consisting of opening a port in remote loopback
+ * mode and writing a packet and reading the echo'd packet back.
+ */
+static int smux_ut_remote_basic(char *buf, int max)
+{
+	const struct test_vector test_data[] = {
+		{"hello\0world\n", sizeof("hello\0world\n")},
+		{0, 0},
+	};
+	int i = 0;
+	int failed = 0;
+	int ret;
+
+	i += scnprintf(buf + i, max - i, "Running %s\n", __func__);
+	while (!failed) {
+		/* enable remote mode */
+		ret = msm_smux_set_ch_option(SMUX_TEST_LCID,
+				SMUX_CH_OPTION_REMOTE_LOOPBACK, 0);
+		UT_ASSERT_INT(ret, ==, 0);
+
+		i += smux_ut_basic_core(buf + i, max - i, test_data, __func__);
+		break;
+	}
+
+	if (failed) {
+		pr_err("%s: Failed\n", __func__);
+		i += scnprintf(buf + i, max - i, "\tFailed\n");
+	}
+	return i;
+}
+
+/**
+ * Fill test pattern into provided buffer including an optional
+ * redzone 16 bytes before and 16 bytes after the buffer.
+ *
+ * buf ---------
+ *      redzone
+ *     --------- <- returned pointer
+ *       data
+ *     --------- <- returned pointer + len
+ *      redzone
+ *     ---------
+ *
+ * @buf  Pointer to the buffer of size len or len+32 (redzone)
+ * @len  Length of the *data* buffer (excluding 32-byte redzone)
+ * @redzone If true, adds redzone data
+ *
+ * @returns pointer to buffer (buf + 16 if redzone enabled)
+ */
+uint8_t *test_pattern_fill(char *buf, int len, int redzone)
+{
+	void *ret;
+	uint8_t ch;
+
+	ret = buf;
+	if (redzone) {
+		memset((char *)buf, 0xAB, 16);
+		memset((char *)buf + len, 0xBA, 16);
+		ret += 16;
+	}
+
+	/* fill with test pattern */
+	for (ch = 0; len > 0; --len, ++ch)
+		*buf++ = (char)ch;
+
+	return ret;
+}
+
+/**
+ * Verify test pattern generated by test_pattern_fill.
+ *
+ * @buf_ptr    Pointer to buffer pointer
+ * @len        Length of the *data* buffer (excluding 32-byte redzone)
+ * @redzone    If true, verifies redzone and adjusts *buf_ptr
+ * @errmsg     Buffer for error message
+ * @errmsg_max Size of error message buffer
+ *
+ * @returns    0 for success; length of error message otherwise
+ */
+unsigned test_pattern_verify(char **buf_ptr, int len, int redzone,
+					char *errmsg, int errmsg_max)
+{
+	int n;
+	int i = 0;
+	char linebuff[80];
+
+	if (redzone) {
+		*buf_ptr -= 16;
+
+		/* verify prefix redzone */
+		for (n = 0; n < 16; ++n) {
+			if (*buf_ptr[n] != 0xAB) {
+				hex_dump_to_buffer(*buf_ptr, 16,
+					16, 1, linebuff, sizeof(linebuff), 1);
+				i += scnprintf(errmsg + i, errmsg_max - i,
+					"Redzone violation: %s\n", linebuff);
+				break;
+			}
+		}
+
+		/* verify postfix redzone */
+		for (n = 0; n < 16; ++n) {
+			if (*buf_ptr[len + n] != 0xBA) {
+				hex_dump_to_buffer(&(*buf_ptr)[len], 16,
+					16, 1, linebuff, sizeof(linebuff), 1);
+				i += scnprintf(errmsg + i, errmsg_max - i,
+					"Redzone violation: %s\n", linebuff);
+				break;
+			}
+		}
+	}
+	return i;
+}
+
+/**
+ * Write a multiple packets in ascending size and verify packet is received
+ * correctly.
+ *
+ * @buf  Buffer for status message
+ * @max  Size of buffer
+ * @name Name of the test for error reporting
+ *
+ * @returns Number of bytes written to @buf
+ *
+ * Requires that the port already be opened and loopback mode is
+ * configured correctly (if required).
+ */
+static int smux_ut_loopback_big_pkt(char *buf, int max, const char *name)
+{
+	struct test_vector test_data[] = {
+		{0, 64},
+		{0, 128},
+		{0, 256},
+		{0, 512},
+		{0, 1024},
+		{0, 2048},
+		{0, 4096},
+		{0, 0},
+	};
+	int i = 0;
+	int failed = 0;
+	struct test_vector *tv;
+
+	/* generate test data */
+	for (tv = test_data; tv->len > 0; ++tv) {
+		tv->data = kmalloc(tv->len + 32, GFP_KERNEL);
+		pr_err("%s: allocating %p len %d\n",
+				__func__, tv->data, tv->len);
+		if (!tv->data) {
+			i += scnprintf(buf + i, max - i,
+					"%s: Unable to allocate %d bytes\n",
+					__func__, tv->len);
+			failed = 1;
+			goto out;
+		}
+		test_pattern_fill((uint8_t *)tv->data, tv->len, 1);
+	}
+
+	/* run test */
+	i += scnprintf(buf + i, max - i, "Running %s\n", name);
+	while (!failed) {
+		i += smux_ut_basic_core(buf + i, max - i, test_data, name);
+		break;
+	}
+
+out:
+	if (failed) {
+		pr_err("%s: Failed\n", name);
+		i += scnprintf(buf + i, max - i, "\tFailed\n");
+	}
+
+	for (tv = test_data; tv->len > 0; ++tv) {
+		if (!tv->data) {
+			i += test_pattern_verify((char **)&tv->data,
+						tv->len, 1, buf + i, max - i);
+			pr_err("%s: freeing %p len %d\n", __func__,
+							tv->data, tv->len);
+			kfree(tv->data);
+		}
+	}
+
+	return i;
+}
+
+/**
+ * Verify Large-packet Local Loopback Support.
+ *
+ * @buf  Buffer for status message
+ * @max  Size of buffer
+ *
+ * @returns Number of bytes written to @buf
+ *
+ * Open port in local loopback mode and write a multiple packets in ascending
+ * size and verify packet is received correctly.
+ */
+static int smux_ut_local_big_pkt(char *buf, int max)
+{
+	int i = 0;
+	int ret;
+
+	ret = msm_smux_set_ch_option(SMUX_TEST_LCID,
+			SMUX_CH_OPTION_LOCAL_LOOPBACK, 0);
+
+	if (ret == 0) {
+		smux_byte_loopback = SMUX_TEST_LCID;
+		i += smux_ut_loopback_big_pkt(buf, max, __func__);
+		smux_byte_loopback = 0;
+	} else {
+		i += scnprintf(buf + i, max - i,
+				"%s: Unable to set loopback mode\n",
+				__func__);
+	}
+
+	return i;
+}
+
+/**
+ * Verify Large-packet Remote Loopback Support.
+ *
+ * @buf  Buffer for status message
+ * @max  Size of buffer
+ *
+ * @returns Number of bytes written to @buf
+ *
+ * Open port in remote loopback mode and write a multiple packets in ascending
+ * size and verify packet is received correctly.
+ */
+static int smux_ut_remote_big_pkt(char *buf, int max)
+{
+	int i = 0;
+	int ret;
+
+	ret = msm_smux_set_ch_option(SMUX_TEST_LCID,
+			SMUX_CH_OPTION_REMOTE_LOOPBACK, 0);
+	if (ret == 0) {
+		i += smux_ut_loopback_big_pkt(buf, max, __func__);
+	} else {
+		i += scnprintf(buf + i, max - i,
+				"%s: Unable to set loopback mode\n",
+				__func__);
+	}
+
+	return i;
+}
+
+/**
+ * Verify set and get operations for each TIOCM bit.
+ *
+ * @buf  Buffer for status message
+ * @max  Size of buffer
+ * @name Name of the test for error reporting
+ *
+ * @returns Number of bytes written to @buf
+ */
+static int smux_ut_tiocm(char *buf, int max, const char *name)
+{
+	static struct smux_mock_callback cb_data;
+	static int cb_initialized;
+	static const struct tiocm_test_vector tiocm_vectors[] = {
+		/* bit to set, set old, set new, clear old */
+		{TIOCM_DTR, TIOCM_DTR, TIOCM_DTR | TIOCM_DSR, TIOCM_DSR},
+		{TIOCM_RTS, TIOCM_RTS, TIOCM_RTS | TIOCM_CTS, TIOCM_CTS},
+		{TIOCM_RI, 0x0, TIOCM_RI, TIOCM_RI},
+		{TIOCM_CD, 0x0, TIOCM_CD, TIOCM_CD},
+	};
+	int i = 0;
+	int failed = 0;
+	int n;
+	int ret;
+
+	i += scnprintf(buf + i, max - i, "Running %s\n", name);
+
+	if (!cb_initialized)
+		mock_cb_data_init(&cb_data);
+
+	mock_cb_data_reset(&cb_data);
+	while (!failed) {
+		/* open port */
+		ret = msm_smux_open(SMUX_TEST_LCID, &cb_data, smux_mock_cb,
+								get_rx_buffer);
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_INT(
+				(int)wait_for_completion_timeout(
+					&cb_data.cb_completion, HZ), >, 0);
+		UT_ASSERT_INT(cb_data.cb_count, ==, 1);
+		UT_ASSERT_INT(cb_data.event_connected, ==, 1);
+		mock_cb_data_reset(&cb_data);
+
+		/* set and clear each TIOCM bit */
+		for (n = 0; n < ARRAY_SIZE(tiocm_vectors) && !failed; ++n) {
+			/* set signal and verify */
+			ret = msm_smux_tiocm_set(SMUX_TEST_LCID,
+						tiocm_vectors[n].input, 0x0);
+			UT_ASSERT_INT(ret, ==, 0);
+			UT_ASSERT_INT(
+				(int)wait_for_completion_timeout(
+					&cb_data.cb_completion, HZ), >, 0);
+			UT_ASSERT_INT(cb_data.cb_count, ==, 1);
+			UT_ASSERT_INT(cb_data.event_tiocm, ==, 1);
+			UT_ASSERT_INT(cb_data.tiocm_meta.tiocm_old, ==,
+						tiocm_vectors[n].set_old);
+			UT_ASSERT_INT(cb_data.tiocm_meta.tiocm_new, ==,
+						tiocm_vectors[n].set_new);
+			mock_cb_data_reset(&cb_data);
+
+			/* clear signal and verify */
+			ret = msm_smux_tiocm_set(SMUX_TEST_LCID, 0x0,
+						tiocm_vectors[n].input);
+			UT_ASSERT_INT(ret, ==, 0);
+			UT_ASSERT_INT(
+				(int)wait_for_completion_timeout(
+					&cb_data.cb_completion, HZ),
+				>, 0);
+			UT_ASSERT_INT(cb_data.cb_count, ==, 1);
+			UT_ASSERT_INT(cb_data.event_tiocm, ==, 1);
+			UT_ASSERT_INT(cb_data.tiocm_meta.tiocm_old, ==,
+						tiocm_vectors[n].clr_old);
+			UT_ASSERT_INT(cb_data.tiocm_meta.tiocm_new, ==, 0x0);
+			mock_cb_data_reset(&cb_data);
+		}
+		if (failed)
+			break;
+
+		/* close port */
+		ret = msm_smux_close(SMUX_TEST_LCID);
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_INT(
+			(int)wait_for_completion_timeout(
+				&cb_data.cb_completion, HZ),
+			>, 0);
+		UT_ASSERT_INT(cb_data.cb_count, ==, 1);
+		UT_ASSERT_INT(cb_data.event_disconnected, ==, 1);
+		UT_ASSERT_INT(cb_data.event_disconnected_ssr, ==, 0);
+		break;
+	}
+
+	if (!failed) {
+		i += scnprintf(buf + i, max - i, "\tOK\n");
+	} else {
+		pr_err("%s: Failed\n", name);
+		i += scnprintf(buf + i, max - i, "\tFailed\n");
+		i += mock_cb_data_print(&cb_data, buf + i, max - i);
+		msm_smux_close(SMUX_TEST_LCID);
+	}
+
+	mock_cb_data_reset(&cb_data);
+	return i;
+}
+
+/**
+ * Verify TIOCM Status Bits for local loopback.
+ *
+ * @buf  Buffer for status message
+ * @max  Size of buffer
+ *
+ * @returns Number of bytes written to @buf
+ */
+static int smux_ut_local_tiocm(char *buf, int max)
+{
+	int i = 0;
+	int ret;
+
+	ret = msm_smux_set_ch_option(SMUX_TEST_LCID,
+			SMUX_CH_OPTION_LOCAL_LOOPBACK, 0);
+
+	if (ret == 0) {
+		smux_byte_loopback = SMUX_TEST_LCID;
+		i += smux_ut_tiocm(buf, max, __func__);
+		smux_byte_loopback = 0;
+	} else {
+		i += scnprintf(buf + i, max - i,
+				"%s: Unable to set loopback mode\n",
+				__func__);
+	}
+
+	return i;
+}
+
+/**
+ * Verify TIOCM Status Bits for remote loopback.
+ *
+ * @buf  Buffer for status message
+ * @max  Size of buffer
+ *
+ * @returns Number of bytes written to @buf
+ */
+static int smux_ut_remote_tiocm(char *buf, int max)
+{
+	int i = 0;
+	int ret;
+
+	ret = msm_smux_set_ch_option(SMUX_TEST_LCID,
+			SMUX_CH_OPTION_REMOTE_LOOPBACK, 0);
+	if (ret == 0) {
+		i += smux_ut_tiocm(buf, max, __func__);
+	} else {
+		i += scnprintf(buf + i, max - i,
+				"%s: Unable to set loopback mode\n",
+				__func__);
+	}
+
+	return i;
+}
+
+/**
+ * Verify High/Low Watermark notifications.
+ *
+ * @buf  Buffer for status message
+ * @max  Size of buffer
+ *
+ * @returns Number of bytes written to @buf
+ */
+static int smux_ut_local_wm(char *buf, int max)
+{
+	static struct smux_mock_callback cb_data;
+	static int cb_initialized;
+	int i = 0;
+	int failed = 0;
+	int ret;
+
+	i += scnprintf(buf + i, max - i, "Running %s\n", __func__);
+	pr_err("%s", buf);
+
+	if (!cb_initialized)
+		mock_cb_data_init(&cb_data);
+
+	mock_cb_data_reset(&cb_data);
+	smux_byte_loopback = SMUX_TEST_LCID;
+	while (!failed) {
+		/* open port for loopback with TX disabled */
+		ret = msm_smux_set_ch_option(SMUX_TEST_LCID,
+				SMUX_CH_OPTION_LOCAL_LOOPBACK
+				| SMUX_CH_OPTION_REMOTE_TX_STOP,
+				0);
+		UT_ASSERT_INT(ret, ==, 0);
+
+		ret = msm_smux_open(SMUX_TEST_LCID, &cb_data, smux_mock_cb,
+								get_rx_buffer);
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_INT(
+				(int)wait_for_completion_timeout(
+					&cb_data.cb_completion, HZ), >, 0);
+		UT_ASSERT_INT(cb_data.cb_count, ==, 1);
+		UT_ASSERT_INT(cb_data.event_connected, ==, 1);
+		mock_cb_data_reset(&cb_data);
+
+		/* transmit 4 packets and verify high-watermark notification */
+		ret = 0;
+		ret |= msm_smux_write(SMUX_TEST_LCID, (void *)1,
+					test_array, sizeof(test_array));
+		ret |= msm_smux_write(SMUX_TEST_LCID, (void *)2,
+					test_array, sizeof(test_array));
+		ret |= msm_smux_write(SMUX_TEST_LCID, (void *)3,
+					test_array, sizeof(test_array));
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_INT(cb_data.cb_count, ==, 0);
+		UT_ASSERT_INT(cb_data.event_high_wm, ==, 0);
+
+		ret = msm_smux_write(SMUX_TEST_LCID, (void *)4,
+					test_array, sizeof(test_array));
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_INT(
+			(int)wait_for_completion_timeout(
+				&cb_data.cb_completion, HZ),
+			>, 0);
+		UT_ASSERT_INT(cb_data.event_high_wm, ==, 1);
+		UT_ASSERT_INT(cb_data.event_low_wm, ==, 0);
+		mock_cb_data_reset(&cb_data);
+
+		/* exceed watermark and verify failure return value */
+		ret = msm_smux_write(SMUX_TEST_LCID, (void *)5,
+					test_array, sizeof(test_array));
+		UT_ASSERT_INT(ret, ==, -EAGAIN);
+
+		/* re-enable TX and verify low-watermark notification */
+		ret = msm_smux_set_ch_option(SMUX_TEST_LCID,
+				0, SMUX_CH_OPTION_REMOTE_TX_STOP);
+		UT_ASSERT_INT(ret, ==, 0);
+		while (cb_data.cb_count < 9) {
+			UT_ASSERT_INT(
+				(int)wait_for_completion_timeout(
+					&cb_data.cb_completion, HZ),
+				>, 0);
+			INIT_COMPLETION(cb_data.cb_completion);
+		}
+		if (failed)
+			break;
+
+		UT_ASSERT_INT(cb_data.event_high_wm, ==, 0);
+		UT_ASSERT_INT(cb_data.event_low_wm, ==, 1);
+		UT_ASSERT_INT(cb_data.event_write_done, ==, 4);
+		mock_cb_data_reset(&cb_data);
+
+		/* close port */
+		ret = msm_smux_close(SMUX_TEST_LCID);
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_INT(
+			(int)wait_for_completion_timeout(
+				&cb_data.cb_completion, HZ),
+			>, 0);
+		UT_ASSERT_INT(cb_data.cb_count, ==, 1);
+		UT_ASSERT_INT(cb_data.event_disconnected, ==, 1);
+		UT_ASSERT_INT(cb_data.event_disconnected_ssr, ==, 0);
+		break;
+	}
+
+	if (!failed) {
+		i += scnprintf(buf + i, max - i, "\tOK\n");
+	} else {
+		pr_err("%s: Failed\n", __func__);
+		i += scnprintf(buf + i, max - i, "\tFailed\n");
+		i += mock_cb_data_print(&cb_data, buf + i, max - i);
+		msm_smux_close(SMUX_TEST_LCID);
+	}
+	smux_byte_loopback = 0;
+	mock_cb_data_reset(&cb_data);
+	return i;
+}
+
+/**
+ * Verify smuxld_receive_buf regular and error processing.
+ *
+ * @buf  Buffer for status message
+ * @max  Size of buffer
+ *
+ * @returns Number of bytes written to @buf
+ */
+static int smux_ut_local_smuxld_receive_buf(char *buf, int max)
+{
+	static struct smux_mock_callback cb_data;
+	static int cb_initialized;
+	struct mock_read_event *meta;
+	int i = 0;
+	int failed = 0;
+	int ret;
+	char data[] = {SMUX_UT_ECHO_REQ,
+		SMUX_UT_ECHO_REQ, SMUX_UT_ECHO_REQ,
+	};
+	char flags[] = {0x0, 0x1, 0x0,};
+
+
+	i += scnprintf(buf + i, max - i, "Running %s\n", __func__);
+
+	if (!cb_initialized)
+		mock_cb_data_init(&cb_data);
+
+	mock_cb_data_reset(&cb_data);
+	smux_byte_loopback = SMUX_TEST_LCID;
+	while (!failed) {
+		/* open port for loopback */
+		ret = msm_smux_set_ch_option(SMUX_TEST_LCID,
+				SMUX_CH_OPTION_LOCAL_LOOPBACK, 0);
+		UT_ASSERT_INT(ret, ==, 0);
+
+		ret = msm_smux_open(SMUX_TEST_LCID, &cb_data, smux_mock_cb,
+								get_rx_buffer);
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_INT(
+				(int)wait_for_completion_timeout(
+					&cb_data.cb_completion, HZ), >, 0);
+		UT_ASSERT_INT(cb_data.cb_count, ==, 1);
+		UT_ASSERT_INT(cb_data.event_connected, ==, 1);
+		mock_cb_data_reset(&cb_data);
+
+		/*
+		 * Verify RX error processing by sending 3 echo requests:
+		 *     one OK, one fail, and a final OK
+		 *
+		 * The parsing framework should process the requests
+		 * and send us three BYTE command packets with
+		 * ECHO ACK FAIL and ECHO ACK OK characters.
+		 */
+		smuxld_receive_buf(0, data, flags, sizeof(data));
+
+		/* verify response characters */
+		do {
+			UT_ASSERT_INT(
+				(int)wait_for_completion_timeout(
+					&cb_data.cb_completion, HZ), >, 0);
+			INIT_COMPLETION(cb_data.cb_completion);
+		} while (cb_data.cb_count < 3);
+		UT_ASSERT_INT(cb_data.cb_count, ==, 3);
+		UT_ASSERT_INT(cb_data.event_read_done, ==, 3);
+
+		meta = list_first_entry(&cb_data.read_events,
+				struct mock_read_event, list);
+		UT_ASSERT_INT((int)meta->meta.pkt_priv, ==,
+				SMUX_UT_ECHO_ACK_OK);
+		list_del(&meta->list);
+
+		meta = list_first_entry(&cb_data.read_events,
+				struct mock_read_event, list);
+		UT_ASSERT_INT((int)meta->meta.pkt_priv, ==,
+				SMUX_UT_ECHO_ACK_FAIL);
+		list_del(&meta->list);
+
+		meta = list_first_entry(&cb_data.read_events,
+				struct mock_read_event, list);
+		UT_ASSERT_INT((int)meta->meta.pkt_priv, ==,
+				SMUX_UT_ECHO_ACK_OK);
+		list_del(&meta->list);
+		mock_cb_data_reset(&cb_data);
+
+		/* close port */
+		ret = msm_smux_close(SMUX_TEST_LCID);
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_INT(
+			(int)wait_for_completion_timeout(
+				&cb_data.cb_completion, HZ),
+			>, 0);
+		UT_ASSERT_INT(cb_data.cb_count, ==, 1);
+		UT_ASSERT_INT(cb_data.event_disconnected, ==, 1);
+		UT_ASSERT_INT(cb_data.event_disconnected_ssr, ==, 0);
+		break;
+	}
+
+	if (!failed) {
+		i += scnprintf(buf + i, max - i, "\tOK\n");
+	} else {
+		pr_err("%s: Failed\n", __func__);
+		i += scnprintf(buf + i, max - i, "\tFailed\n");
+		i += mock_cb_data_print(&cb_data, buf + i, max - i);
+		msm_smux_close(SMUX_TEST_LCID);
+	}
+	smux_byte_loopback = 0;
+	mock_cb_data_reset(&cb_data);
+	return i;
+}
+
+static char debug_buffer[DEBUG_BUFMAX];
+
+static ssize_t debug_read(struct file *file, char __user *buf,
+			  size_t count, loff_t *ppos)
+{
+	int (*fill)(char *buf, int max) = file->private_data;
+	int bsize;
+
+	if (*ppos != 0)
+		return 0;
+
+	bsize = fill(debug_buffer, DEBUG_BUFMAX);
+	return simple_read_from_buffer(buf, count, ppos, debug_buffer, bsize);
+}
+
+static int debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static const struct file_operations debug_ops = {
+	.read = debug_read,
+	.open = debug_open,
+};
+
+static void debug_create(const char *name, mode_t mode,
+			 struct dentry *dent,
+			 int (*fill)(char *buf, int max))
+{
+	debugfs_create_file(name, mode, dent, fill, &debug_ops);
+}
+
+static int __init smux_debugfs_init(void)
+{
+	struct dentry *dent;
+
+	dent = debugfs_create_dir("n_smux", 0);
+	if (IS_ERR(dent))
+		return PTR_ERR(dent);
+
+	/*
+	 * Add Unit Test entries.
+	 *
+	 * The idea with unit tests is that you can run all of them
+	 * from ADB shell by doing:
+	 *  adb shell
+	 *	cat ut*
+	 *
+	 * And if particular tests fail, you can then repeatedly run the failing
+	 * tests as you debug and resolve the failing test.
+	 */
+	debug_create("ut_local_basic", 0444, dent, smux_ut_basic);
+	debug_create("ut_remote_basic", 0444, dent,	smux_ut_remote_basic);
+	debug_create("ut_local_big_pkt", 0444, dent, smux_ut_local_big_pkt);
+	debug_create("ut_remote_big_pkt", 0444, dent, smux_ut_remote_big_pkt);
+	debug_create("ut_local_tiocm", 0444, dent, smux_ut_local_tiocm);
+	debug_create("ut_remote_tiocm", 0444, dent,	smux_ut_remote_tiocm);
+	debug_create("ut_local_wm", 0444, dent, smux_ut_local_wm);
+	debug_create("ut_local_smuxld_receive_buf", 0444, dent,
+			smux_ut_local_smuxld_receive_buf);
+
+	return 0;
+}
+
+late_initcall(smux_debugfs_init);
+
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index d939bd7..a203472 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -2576,8 +2576,11 @@
 		clear = ~val;
 		break;
 	}
-	set &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP;
-	clear &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP;
+
+	set &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP|TIOCM_CD|
+		TIOCM_RI|TIOCM_DSR|TIOCM_CTS;
+	clear &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP|TIOCM_CD|
+		TIOCM_RI|TIOCM_DSR|TIOCM_CTS;
 	return tty->ops->tiocmset(tty, set, clear);
 }
 
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 76316a3..ceefb23 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -133,8 +133,6 @@
 
 source "drivers/usb/musb/Kconfig"
 
-source "drivers/usb/renesas_usbhs/Kconfig"
-
 source "drivers/usb/class/Kconfig"
 
 source "drivers/usb/storage/Kconfig"
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 53a7bc0..b1ec3fc 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -27,6 +27,7 @@
 obj-$(CONFIG_USB_ISP1760_HCD)	+= host/
 obj-$(CONFIG_USB_IMX21_HCD)	+= host/
 obj-$(CONFIG_USB_FSL_MPH_DR_OF)	+= host/
+obj-$(CONFIG_USB_PEHCI_HCD)	+= host/
 
 obj-$(CONFIG_USB_C67X00_HCD)	+= c67x00/
 
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 9a56635..19e1244 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1274,6 +1274,57 @@
 	return status;
 }
 
+#ifdef CONFIG_USB_OTG
+void usb_hnp_polling_work(struct work_struct *work)
+{
+	int ret;
+	struct usb_bus *bus =
+		container_of(work, struct usb_bus, hnp_polling.work);
+	struct usb_device *udev = bus->root_hub->children[bus->otg_port - 1];
+	u8 *status = NULL;
+
+	/*
+	 * The OTG-B device must hand back the host role to OTG PET
+	 * within 100 msec irrespective of host_request flag.
+	 */
+	if (bus->quick_hnp) {
+		bus->quick_hnp = 0;
+		goto start_hnp;
+	}
+
+	status = kmalloc(sizeof(*status), GFP_KERNEL);
+	if (!status)
+		goto reschedule;
+
+	ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+		USB_REQ_GET_STATUS, USB_DIR_IN | USB_RECIP_DEVICE,
+		0, OTG_STATUS_SELECTOR, status, sizeof(*status),
+		USB_CTRL_GET_TIMEOUT);
+	if (ret < 0) {
+		/* Peripheral may not be supporting HNP polling */
+		dev_info(&udev->dev, "HNP polling failed. status %d\n", ret);
+		goto out;
+	}
+
+	if (!(*status & (1 << HOST_REQUEST_FLAG)))
+		goto reschedule;
+
+start_hnp:
+	do_unbind_rebind(udev, DO_UNBIND);
+	udev->do_remote_wakeup = device_may_wakeup(&udev->dev);
+	ret = usb_suspend_both(udev, PMSG_USER_SUSPEND);
+	if (ret)
+		dev_info(&udev->dev, "suspend failed\n");
+	goto out;
+
+reschedule:
+	schedule_delayed_work(&bus->hnp_polling,
+		msecs_to_jiffies(THOST_REQ_POLL));
+out:
+	kfree(status);
+}
+#endif
+
 static void choose_wakeup(struct usb_device *udev, pm_message_t msg)
 {
 	int	w;
@@ -1342,6 +1393,7 @@
 	 * (This can't be done in usb_resume_interface()
 	 * above because it doesn't own the right set of locks.)
 	 */
+	pm_runtime_get_sync(dev->parent);
 	status = usb_resume_both(udev, msg);
 	if (status == 0) {
 		pm_runtime_disable(dev);
@@ -1349,6 +1401,7 @@
 		pm_runtime_enable(dev);
 		unbind_no_reset_resume_drivers_interfaces(udev);
 	}
+	pm_runtime_put_sync(dev->parent);
 
 	/* Avoid PM error messages for devices disconnected while suspended
 	 * as we'll display regular disconnect messages just a bit later.
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 140d3e1..7c37aff 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -897,6 +897,9 @@
 	bus->bandwidth_isoc_reqs = 0;
 
 	INIT_LIST_HEAD (&bus->bus_list);
+#ifdef CONFIG_USB_OTG
+	INIT_DELAYED_WORK(&bus->hnp_polling, usb_hnp_polling_work);
+#endif
 }
 
 /*-------------------------------------------------------------------------*/
@@ -926,6 +929,11 @@
 	/* Add it to the local list of buses */
 	list_add (&bus->bus_list, &usb_bus_list);
 	mutex_unlock(&usb_bus_list_lock);
+#ifdef CONFIG_USB_OTG
+	/* Obvioulsy HNP is supported on B-host */
+	if (bus->is_b_host)
+		bus->hnp_support = 1;
+#endif
 
 	usb_notify_add_bus(bus);
 
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index ec6c97d..5dceb41 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -24,12 +24,38 @@
 #include <linux/kthread.h>
 #include <linux/mutex.h>
 #include <linux/freezer.h>
+#include <linux/usb/otg.h>
 
 #include <asm/uaccess.h>
 #include <asm/byteorder.h>
 
 #include "usb.h"
 
+#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
+#include <linux/usb/hcd.h>
+#include <linux/usb/ch11.h>
+
+int portno;
+int No_Data_Phase;
+EXPORT_SYMBOL(No_Data_Phase);
+int No_Status_Phase;
+EXPORT_SYMBOL(No_Status_Phase);
+unsigned char hub_tier;
+
+#define PDC_HOST_NOTIFY		0x8001	/*completion from core */
+#define UNSUPPORTED_DEVICE	0x8099
+#define UNWANTED_SUSPEND	0x8098
+#define PDC_POWERMANAGEMENT	0x8097
+
+int Unwanted_SecondReset;
+EXPORT_SYMBOL(Unwanted_SecondReset);
+int HostComplianceTest;
+EXPORT_SYMBOL(HostComplianceTest);
+int HostTest;
+EXPORT_SYMBOL(HostTest);
+#endif
+
+
 /* if we are in debug mode, always announce new devices */
 #ifdef DEBUG
 #ifndef CONFIG_USB_ANNOUNCE_NEW_DEVICES
@@ -358,8 +384,11 @@
 {
 	int i, status = -ETIMEDOUT;
 
+	/* ISP1763A HUB sometimes returns 2 bytes instead of 4 bytes, retry
+	 * if this happens
+	 */
 	for (i = 0; i < USB_STS_RETRIES &&
-			(status == -ETIMEDOUT || status == -EPIPE); i++) {
+			(status == -ETIMEDOUT || status == -EPIPE || status == 2); i++) {
 		status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
 			USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port1,
 			data, sizeof(*data), USB_STS_TIMEOUT);
@@ -765,6 +794,10 @@
 		 */
 		if (type == HUB_INIT) {
 			delay = hub_power_on(hub, false);
+#ifdef CONFIG_USB_OTG
+			if (hdev->bus->is_b_host)
+				goto init2;
+#endif
 			PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func2);
 			schedule_delayed_work(&hub->init_work,
 					msecs_to_jiffies(delay));
@@ -906,6 +939,11 @@
 	 * will see them later and handle them normally.
 	 */
 	if (need_debounce_delay) {
+#ifdef CONFIG_USB_OTG
+		if (hdev->bus->is_b_host && type == HUB_INIT)
+			goto init3;
+#endif
+
 		delay = HUB_DEBOUNCE_STABLE;
 
 		/* Don't do a long sleep inside a workqueue routine */
@@ -1335,6 +1373,7 @@
 #ifdef	CONFIG_USB_OTG_BLACKLIST_HUB
 	if (hdev->parent) {
 		dev_warn(&intf->dev, "ignoring external hub\n");
+		otg_send_event(OTG_EVENT_HUB_NOT_SUPPORTED);
 		return -ENODEV;
 	}
 #endif
@@ -1676,6 +1715,13 @@
 	dev_info(&udev->dev, "USB disconnect, device number %d\n",
 			udev->devnum);
 
+#ifdef CONFIG_USB_OTG
+	if (udev->bus->hnp_support && udev->portnum == udev->bus->otg_port) {
+		cancel_delayed_work_sync(&udev->bus->hnp_polling);
+		udev->bus->hnp_support = 0;
+	}
+#endif
+
 	usb_lock_device(udev);
 
 	/* Free up all the children before we remove this device */
@@ -1757,6 +1803,7 @@
 	int err = 0;
 
 #ifdef	CONFIG_USB_OTG
+	bool old_otg = false;
 	/*
 	 * OTG-aware devices on OTG-capable root hubs may be able to use SRP,
 	 * to wake us after we've powered off VBUS; and HNP, switching roles
@@ -1780,15 +1827,34 @@
 					(port1 == bus->otg_port)
 						? "" : "non-");
 
+				/* a_alt_hnp_support is obsoleted */
+				if (port1 != bus->otg_port)
+					goto out;
+
+				bus->hnp_support = 1;
+
+				/* a_hnp_support is not required for devices
+				 * compliant to revision 2.0 or subsequent
+				 * versions.
+				 */
+
+				if ((le16_to_cpu(desc->bLength) ==
+						USB_DT_OTG_SIZE) &&
+					le16_to_cpu(desc->bcdOTG) >= 0x0200)
+					goto out;
+
+				/* Legacy B-device i.e compliant to spec
+				 * revision 1.3 expect A-device to set
+				 * a_hnp_support or b_hnp_enable before
+				 * selecting configuration.
+				 */
+				old_otg = true;
+
 				/* enable HNP before suspend, it's simpler */
-				if (port1 == bus->otg_port)
-					bus->b_hnp_enable = 1;
 				err = usb_control_msg(udev,
 					usb_sndctrlpipe(udev, 0),
 					USB_REQ_SET_FEATURE, 0,
-					bus->b_hnp_enable
-						? USB_DEVICE_B_HNP_ENABLE
-						: USB_DEVICE_A_ALT_HNP_SUPPORT,
+					USB_DEVICE_A_HNP_SUPPORT,
 					0, NULL, 0, USB_CTRL_SET_TIMEOUT);
 				if (err < 0) {
 					/* OTG MESSAGE: report errors here,
@@ -1797,26 +1863,47 @@
 					dev_info(&udev->dev,
 						"can't set HNP mode: %d\n",
 						err);
-					bus->b_hnp_enable = 0;
+					bus->hnp_support = 0;
 				}
 			}
 		}
 	}
+out:
+	if ((udev->quirks & USB_QUIRK_OTG_PET)) {
+		if (le16_to_cpu(udev->descriptor.bcdDevice) &
+			OTG_TTST_VBUS_OFF)
+			udev->bus->otg_vbus_off = 1;
+		if (udev->bus->is_b_host || old_otg)
+			udev->bus->quick_hnp = 1;
+	}
 
 	if (!is_targeted(udev)) {
 
+		otg_send_event(OTG_EVENT_DEV_NOT_SUPPORTED);
+
 		/* Maybe it can talk to us, though we can't talk to it.
 		 * (Includes HNP test device.)
 		 */
-		if (udev->bus->b_hnp_enable || udev->bus->is_b_host) {
+		if (udev->bus->hnp_support) {
 			err = usb_port_suspend(udev, PMSG_SUSPEND);
 			if (err < 0)
 				dev_dbg(&udev->dev, "HNP fail, %d\n", err);
 		}
 		err = -ENOTSUPP;
-		goto fail;
+	} else if (udev->bus->hnp_support &&
+		udev->portnum == udev->bus->otg_port) {
+		/* HNP polling is introduced in OTG supplement Rev 2.0
+		 * and older devices may not support. Work is not
+		 * re-armed if device returns STALL. B-Host also perform
+		 * HNP polling.
+		 */
+		if (udev->bus->quick_hnp)
+			schedule_delayed_work(&udev->bus->hnp_polling,
+				msecs_to_jiffies(OTG_TTST_SUSP));
+		else
+			schedule_delayed_work(&udev->bus->hnp_polling,
+				msecs_to_jiffies(THOST_REQ_POLL));
 	}
-fail:
 #endif
 	return err;
 }
@@ -2475,6 +2562,22 @@
 				return status;
 		}
 	}
+#ifdef CONFIG_USB_OTG
+	if (!udev->bus->is_b_host && udev->bus->hnp_support &&
+		udev->portnum == udev->bus->otg_port) {
+		status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+				USB_REQ_SET_FEATURE, 0,
+				USB_DEVICE_B_HNP_ENABLE,
+				0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+		if (status < 0) {
+			otg_send_event(OTG_EVENT_NO_RESP_FOR_HNP_ENABLE);
+			dev_dbg(&udev->dev, "can't enable HNP on port %d, "
+					"status %d\n", port1, status);
+		} else {
+			udev->bus->b_hnp_enable = 1;
+		}
+	}
+#endif
 
 	/* disable USB2 hardware LPM */
 	if (udev->usb2_hw_lpm_enabled == 1)
@@ -3091,14 +3194,22 @@
 					buf->bMaxPacketSize0;
 			kfree(buf);
 
-			retval = hub_port_reset(hub, port1, udev, delay, false);
-			if (retval < 0)		/* error or disconnect */
-				goto fail;
-			if (oldspeed != udev->speed) {
-				dev_dbg(&udev->dev,
-					"device reset changed speed!\n");
-				retval = -ENODEV;
-				goto fail;
+			/*
+			 * If it is a HSET Test device, we don't issue a
+			 * second reset which results in failure due to
+			 * speed change.
+			 */
+			if (le16_to_cpu(buf->idVendor) != 0x1a0a) {
+				retval = hub_port_reset(hub, port1, udev,
+							 delay, false);
+				if (retval < 0)	/* error or disconnect */
+					goto fail;
+				if (oldspeed != udev->speed) {
+					dev_dbg(&udev->dev,
+					       "device reset changed speed!\n");
+					retval = -ENODEV;
+					goto fail;
+				}
 			}
 			if (r) {
 				dev_err(&udev->dev,
@@ -3366,6 +3477,9 @@
 			(portchange & USB_PORT_STAT_C_CONNECTION))
 		clear_bit(port1, hub->removed_bits);
 
+#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
+	if (Unwanted_SecondReset == 0)   /*stericsson*/
+#endif
 	if (portchange & (USB_PORT_STAT_C_CONNECTION |
 				USB_PORT_STAT_C_ENABLE)) {
 		status = hub_port_debounce(hub, port1);
@@ -3504,7 +3618,32 @@
 		status = hub_power_remaining(hub);
 		if (status)
 			dev_dbg(hub_dev, "%dmA power budget left\n", status);
+#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
+		if (HostComplianceTest == 1 && udev->devnum > 1) {
+			if (HostTest == 7) {	/*SINGLE_STEP_GET_DEV_DESC */
+				dev_info(hub_dev, "Testing "
+						"SINGLE_STEP_GET_DEV_DESC\n");
+				/* Test the Single Step Get Device Descriptor ,
+				 * take care it should not get status phase
+				 */
+				No_Data_Phase = 1;
+				No_Status_Phase = 1;
 
+				usb_get_device_descriptor(udev, 8);
+				No_Data_Phase = 0;
+				No_Status_Phase = 0;
+			}
+
+			if (HostTest == 8) {
+				dev_info(hub_dev, "Testing "
+						"SINGLE_STEP_SET_FEATURE\n");
+				/* Test Single Step Set Feature */
+				No_Status_Phase = 1;
+				usb_get_device_descriptor(udev, 8);
+				No_Status_Phase = 0;
+			}
+		}
+#endif
 		return;
 
 loop_disable:
@@ -3581,6 +3720,11 @@
 	u16 portstatus;
 	u16 portchange;
 	int i, ret;
+#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
+	int j;
+	int otgport = 0;
+	struct usb_port_status port_status;
+#endif
 	int connect_change, wakeup_change;
 
 	/*
@@ -3657,6 +3801,171 @@
 
 		/* deal with port status changes */
 		for (i = 1; i <= hub->descriptor->bNbrPorts; i++) {
+#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
+			struct usb_port_status portsts;
+
+			/*if we have something to do on
+			 * otg port
+			 * */
+			if ((hdev->otgstate & USB_OTG_SUSPEND) ||
+			    (hdev->otgstate & USB_OTG_ENUMERATE) ||
+			    (hdev->otgstate & USB_OTG_DISCONNECT) ||
+			    (hdev->otgstate & USB_OTG_RESUME)) {
+				otgport = 1;
+			}
+
+
+			if (hdev->otgstate & USB_OTG_RESUME) {
+				ret = clear_port_feature(hdev, i,
+							 USB_PORT_FEAT_SUSPEND);
+				if (ret < 0) {
+					dev_err(hub_dev, "usb otg port Resume"
+						" fails, %d\n", ret);
+				}
+				hdev->otgstate &= ~USB_OTG_RESUME;
+			}
+			if ((hdev->otgstate & USB_OTG_SUSPEND)
+			    && (hdev->children[0])) {
+				hdev->otgstate &= ~USB_OTG_SUSPEND;
+
+				ret = set_port_feature(hdev, 1,
+						       USB_PORT_FEAT_SUSPEND);
+				if (ret < 0) {
+					dev_err(hub_dev, "usb otg port suspend"
+						" fails, %d\n", ret);
+					break;
+				}
+				msleep(1);
+				ret = get_port_status(hdev, i, &portsts);
+				if (ret < 0) {
+					dev_err(hub_dev, "usb otg get port"
+						" status fails, %d\n", ret);
+					break;
+				}
+				portchange = le16_to_cpu(portsts.wPortChange);
+				if (portchange & USB_PORT_STAT_C_SUSPEND) {
+					clear_port_feature(hdev, i,
+						USB_PORT_FEAT_C_SUSPEND);
+				}
+				break;
+			}
+
+			if (hdev->otgstate & USB_OTG_REMOTEWAKEUP) {
+
+				for (j = 1; j <= hub->descriptor->bNbrPorts;
+				     j++) {
+					if (hdev->children[j - 1]) {
+						dev_dbg(hub_dev, "child"
+						     " found at port %d\n", j);
+						ret = usb_control_msg(hdev->
+						      children[j - 1],
+						      usb_sndctrlpipe(hdev->
+								children[j - 1],
+								0),
+						      USB_REQ_SET_FEATURE,
+						      USB_RECIP_DEVICE,
+						      USB_DEVICE_REMOTE_WAKEUP,
+						      0, NULL,
+						      0,
+						      USB_CTRL_SET_TIMEOUT);
+						if (ret < 0) {
+							dev_err(hub_dev, "Port"
+							  " %d doesn't support"
+							  "remote wakeup\n", j);
+						} else {
+							dev_dbg(hub_dev, "Port"
+							  " %d supports"
+							  "remote wakeup\n", j);
+						}
+						ret = set_port_feature(hdev, j,
+							USB_PORT_FEAT_SUSPEND);
+						if (ret < 0) {
+							dev_err(hub_dev, "Port"
+							  " %d NOT ABLE TO"
+							  " SUSPEND\n", j);
+						} else {
+							dev_dbg(hub_dev, "Port"
+							  " %d is ABLE TO"
+							  " SUSPEND\n", j);
+						}
+					}
+				}
+				ret = usb_control_msg(hdev,
+						      usb_sndctrlpipe(hdev, 0),
+						      USB_REQ_SET_FEATURE,
+						      USB_RECIP_DEVICE,
+						      USB_DEVICE_REMOTE_WAKEUP,
+						      0, NULL, 0,
+						      USB_CTRL_SET_TIMEOUT);
+				if (ret < 0) {
+					dev_err(hub_dev, "HUB doesn't support"
+							" REMOTE WAKEUP\n");
+				} else {
+					dev_dbg(hub_dev, "HUB supports"
+							" REMOTE WAKEUP\n");
+				}
+				ret = 0;
+				msleep(10);
+				if (hdev->parent == hdev->bus->root_hub) {
+					if (hdev->hcd_suspend &&
+					    hdev->hcd_priv) {
+						dev_dbg(hub_dev, "calling"
+						  " suspend after remote wakeup"
+						  " command is issued\n");
+						hdev->hcd_suspend(hdev->
+								   hcd_priv);
+					}
+					if (hdev->otg_notif)
+						hdev->otg_notif(hdev->otgpriv,
+						       PDC_POWERMANAGEMENT, 10);
+				}
+			}
+
+			if (hdev->otgstate & USB_OTG_WAKEUP_ALL) {
+				(void) usb_control_msg(hdev,
+						       usb_sndctrlpipe(hdev, 0),
+						       USB_REQ_CLEAR_FEATURE,
+						       USB_RECIP_DEVICE,
+						       USB_DEVICE_REMOTE_WAKEUP,
+						       0, NULL, 0,
+						       USB_CTRL_SET_TIMEOUT);
+				dev_dbg(hub_dev, "Hub CLEARED REMOTE WAKEUP\n");
+				for (j = 1; j <= hub->descriptor->bNbrPorts;
+				     j++) {
+					if (hdev->children[j - 1]) {
+						dev_dbg(hub_dev, "PORT %d"
+						   " SUSPEND IS CLEARD\n", j);
+						clear_port_feature(hdev, j,
+						   USB_PORT_FEAT_C_SUSPEND);
+						msleep(50);
+						(void) usb_control_msg(hdev->
+						       children[j - 1],
+						       usb_sndctrlpipe(
+							  hdev->children[j - 1],
+							  0),
+						       USB_REQ_CLEAR_FEATURE,
+						       USB_RECIP_DEVICE,
+						       USB_DEVICE_REMOTE_WAKEUP,
+						       0, NULL,
+						       0,
+						       USB_CTRL_SET_TIMEOUT);
+						dev_dbg(hub_dev, "PORT %d "
+							"REMOTE WAKEUP IS "
+							"CLEARD\n", j);
+						msleep(10);
+					}
+				}
+
+
+			}
+
+
+			/*
+			 * reset the state of otg device,
+			 * regardless of otg device
+			 */
+			hdev->otgstate = 0;
+#endif
 			if (test_bit(i, hub->busy_bits))
 				continue;
 			connect_change = test_bit(i, hub->change_bits);
@@ -3761,9 +4070,19 @@
 						HUB_BH_RESET_TIME, true);
 			}
 
-			if (connect_change)
+			if (connect_change) {
+#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
+				if (hdev->parent == hdev->bus->root_hub)
+					if (hdev->otg_notif
+					    && (HostComplianceTest == 0))
+						hdev->otg_notif(hdev->otgpriv,
+								PDC_HOST_NOTIFY,
+								5);
+				portno = i;
+#endif
 				hub_port_connect_change(hub, i,
 						portstatus, portchange);
+				}
 		} /* end for i */
 
 		/* deal with hub status changes */
@@ -3795,7 +4114,105 @@
 						"condition\n");
 			}
 		}
+#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
+		/* if we have something on otg */
+		if (otgport) {
+			otgport = 0;
+			/* notify otg controller about it */
+			if (hdev->parent == hdev->bus->root_hub)
+				if (hdev->otg_notif)
+					hdev->otg_notif(hdev->otgpriv,
+							PDC_HOST_NOTIFY, 0);
+		}
 
+		if (HostComplianceTest && hdev->devnum > 1) {
+			/* TEST_SE0_NAK */
+			if (HostTest == 1) {
+				dev_info(hub_dev, "Testing for TEST_SE0_NAK\n");
+				ret = clear_port_feature(hdev, portno,
+						 USB_PORT_FEAT_C_CONNECTION);
+				ret = set_port_feature(hdev, portno,
+						       USB_PORT_FEAT_SUSPEND);
+				ret = set_port_feature(hdev, portno | 0x300,
+						       USB_PORT_FEAT_TEST);
+				ret = get_port_status(hdev, portno,
+						      &port_status);
+			}
+			/*TEST_J*/
+			if (HostTest == 2) {
+				dev_info(hub_dev, "Testing TEST_J\n");
+				ret = clear_port_feature(hdev, portno,
+						USB_PORT_FEAT_C_CONNECTION);
+				ret = set_port_feature(hdev, portno,
+						USB_PORT_FEAT_SUSPEND);
+				ret = set_port_feature(hdev, portno | 0x100,
+						       USB_PORT_FEAT_TEST);
+				ret = get_port_status(hdev, portno,
+						      &port_status);
+			}
+			if (HostTest == 3) {
+				dev_info(hub_dev, "Testing TEST_K\n");
+				ret = clear_port_feature(hdev, portno,
+						USB_PORT_FEAT_C_CONNECTION);
+				ret = set_port_feature(hdev, portno,
+						       USB_PORT_FEAT_SUSPEND);
+				ret = set_port_feature(hdev, portno | 0x200,
+						       USB_PORT_FEAT_TEST);
+				ret = get_port_status(hdev, portno,
+						      &port_status);
+			}
+			if (HostTest == 4) {
+				dev_info(hub_dev, "Testing TEST_PACKET at Port"
+						  " %d\n", portno);
+				ret = clear_port_feature(hdev, portno,
+						USB_PORT_FEAT_C_CONNECTION);
+				if (ret < 0)
+					dev_err(hub_dev, "Clear port feature"
+						" C_CONNECTION failed\n");
+
+				ret = set_port_feature(hdev, portno,
+						       USB_PORT_FEAT_SUSPEND);
+				if (ret < 0)
+					dev_err(hub_dev, "Clear port feature"
+						" SUSPEND failed\n");
+
+				ret = set_port_feature(hdev, portno | 0x400,
+						       USB_PORT_FEAT_TEST);
+				if (ret < 0)
+					dev_err(hub_dev, "Clear port feature"
+						" TEST failed\n");
+
+				ret = get_port_status(hdev, portno,
+						      &port_status);
+				if (ret < 0)
+					dev_err(hub_dev, "Get port status"
+						" failed\n");
+			}
+			if (HostTest == 5) {
+				dev_info(hub_dev, "Testing TEST_FORCE_ENBLE\n");
+				ret = clear_port_feature(hdev, portno,
+						 USB_PORT_FEAT_C_CONNECTION);
+				ret = set_port_feature(hdev, portno,
+						 USB_PORT_FEAT_SUSPEND);
+				ret = set_port_feature(hdev, portno | 0x500,
+						       USB_PORT_FEAT_TEST);
+				ret = get_port_status(hdev, portno,
+						      &port_status);
+			}
+			if (HostTest == 6) {
+				dev_info(hub_dev, "Testing "
+					 "HS_HOST_PORT_SUSPEND_RESUME\n");
+				ret = clear_port_feature(hdev, portno,
+						 USB_PORT_FEAT_C_CONNECTION);
+				ret = set_port_feature(hdev, portno,
+						     USB_PORT_FEAT_SUSPEND);
+				msleep(3000);
+				ret = clear_port_feature(hdev, portno,
+						 USB_PORT_FEAT_SUSPEND);
+				HostTest = 0;
+			}
+		}
+#endif
  loop_autopm:
 		/* Balance the usb_autopm_get_interface() above */
 		usb_autopm_put_interface_no_suspend(intf);
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index ca717da..b2da64c 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -1770,6 +1770,9 @@
 		goto free_interfaces;
 	}
 
+	dev->actconfig = cp;
+	if (cp)
+		usb_notify_config_device(dev);
 	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
 			      USB_REQ_SET_CONFIGURATION, 0, configuration, 0,
 			      NULL, 0, USB_CTRL_SET_TIMEOUT);
@@ -1777,11 +1780,11 @@
 		/* All the old state is gone, so what else can we do?
 		 * The device is probably useless now anyway.
 		 */
-		cp = NULL;
+		dev->actconfig = cp = NULL;
 	}
 
-	dev->actconfig = cp;
 	if (!cp) {
+		usb_notify_config_device(dev);
 		usb_set_device_state(dev, USB_STATE_ADDRESS);
 		usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL);
 		mutex_unlock(hcd->bandwidth_mutex);
diff --git a/drivers/usb/core/notify.c b/drivers/usb/core/notify.c
index 7728c91..617a364 100644
--- a/drivers/usb/core/notify.c
+++ b/drivers/usb/core/notify.c
@@ -58,6 +58,12 @@
 	mutex_unlock(&usbfs_mutex);
 }
 
+void usb_notify_config_device(struct usb_device *udev)
+{
+	blocking_notifier_call_chain(&usb_notifier_list,
+			USB_DEVICE_CONFIG, udev);
+}
+
 void usb_notify_add_bus(struct usb_bus *ubus)
 {
 	blocking_notifier_call_chain(&usb_notifier_list, USB_BUS_ADD, ubus);
diff --git a/drivers/usb/core/otg_whitelist.h b/drivers/usb/core/otg_whitelist.h
index e8cdce5..7bb6747 100644
--- a/drivers/usb/core/otg_whitelist.h
+++ b/drivers/usb/core/otg_whitelist.h
@@ -59,6 +59,11 @@
 	     le16_to_cpu(dev->descriptor.idProduct) == 0xbadd))
 		return 0;
 
+	/* OTG PET device is always targeted (see OTG 2.0 ECN 6.4.2) */
+	if ((le16_to_cpu(dev->descriptor.idVendor) == 0x1a0a &&
+	     le16_to_cpu(dev->descriptor.idProduct) == 0x0200))
+		return 1;
+
 	/* NOTE: can't use usb_match_id() since interface caches
 	 * aren't set up yet. this is cut/paste from that code.
 	 */
@@ -92,7 +97,30 @@
 		if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) &&
 		    (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol))
 			continue;
+#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
+		/*Hub is targeted device,so code execution should reach here */
+		if (USB_CLASS_HUB == dev->descriptor.bDeviceClass) {
+			/* count the tiers and if it is more than 6, return 0 */
+			unsigned char tier = 0;
+			struct usb_device *root_hub;
 
+			root_hub = dev->bus->root_hub;
+			while ((dev->parent != NULL) && /* root hub not count */
+				(dev->parent != root_hub) &&
+				(tier != 6))  {/* interal hub not count */
+				tier++;
+				dev = dev->parent;
+			}
+
+			if (tier == 6) {
+				dev_err(&dev->dev, "5 tier of hubs reached,"
+					" newly added hub will not be"
+					" supported!\n");
+				hub_tier = 1;
+				return 0;
+			}
+		}
+#endif
 		return 1;
 	}
 
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 4c65eb6..200dc9b 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -150,6 +150,9 @@
 	/* INTEL VALUE SSD */
 	{ USB_DEVICE(0x8086, 0xf1a5), .driver_info = USB_QUIRK_RESET_RESUME },
 
+	/* Protocol and OTG Electrical Test Device */
+	{ USB_DEVICE(0x1a0a, 0x0200), .driver_info = USB_QUIRK_OTG_PET },
+
 	{ }  /* terminating entry must be last */
 };
 
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index 71648dc..4f40547 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -104,6 +104,10 @@
 }
 #endif
 
+#ifdef CONFIG_USB_OTG
+extern void usb_hnp_polling_work(struct work_struct *work);
+#endif
+
 extern struct bus_type usb_bus_type;
 extern struct device_type usb_device_type;
 extern struct device_type usb_if_device_type;
@@ -153,6 +157,7 @@
 /* internal notify stuff */
 extern void usb_notify_add_device(struct usb_device *udev);
 extern void usb_notify_remove_device(struct usb_device *udev);
+extern void usb_notify_config_device(struct usb_device *udev);
 extern void usb_notify_add_bus(struct usb_bus *ubus);
 extern void usb_notify_remove_bus(struct usb_bus *ubus);
 
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index d441fe4..3227508 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -26,7 +26,9 @@
 # PCI doesn't provide nops if CONFIG_PCI isn't enabled.
 ##
 
-obj-$(CONFIG_USB_DWC3)		+= dwc3-omap.o
+
+obj-$(CONFIG_USB_DWC3_OMAP)	+= dwc3-omap.o
+obj-$(CONFIG_USB_DWC3_MSM)	+= dwc3-msm.o
 
 ##
 # REVISIT Samsung Exynos platform needs the clk API which isn't
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
new file mode 100644
index 0000000..4e6091e
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -0,0 +1,879 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/list.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/msm_hsusb.h>
+
+#include "core.h"
+#include "gadget.h"
+
+/**
+ *  USB DBM Hardware registers.
+ *
+ */
+#define DBM_EP_CFG(n)		(0x00 + 4 * (n))
+#define DBM_DATA_FIFO(n)	(0x10 + 4 * (n))
+#define DBM_DATA_FIFO_SIZE(n)	(0x20 + 4 * (n))
+#define DBM_DATA_FIFO_EN	(0x30)
+#define DBM_GEVNTADR		(0x34)
+#define DBM_GEVNTSIZ		(0x38)
+#define DBM_DBG_CNFG		(0x3C)
+#define DBM_HW_TRB0_EP(n)	(0x40 + 4 * (n))
+#define DBM_HW_TRB1_EP(n)	(0x50 + 4 * (n))
+#define DBM_HW_TRB2_EP(n)	(0x60 + 4 * (n))
+#define DBM_HW_TRB3_EP(n)	(0x70 + 4 * (n))
+#define DBM_PIPE_CFG		(0x80)
+#define DBM_SOFT_RESET		(0x84)
+
+/**
+ *  USB DBM  Hardware registers bitmask.
+ *
+ */
+/* DBM_EP_CFG */
+#define DBM_EN_EP		0x00000000
+#define DBM_USB3_EP_NUM		0x0000003E
+#define DBM_BAM_PIPE_NUM	0x000000C0
+#define DBM_PRODUCER		0x00000100
+#define DBM_DISABLE_WB		0x00000200
+#define DBM_INT_RAM_ACC		0x00000400
+
+/* DBM_DATA_FIFO_SIZE */
+#define DBM_DATA_FIFO_SIZE_MASK	0x0000ffff
+
+/* DBM_GEVNTSIZ */
+#define DBM_GEVNTSIZ_MASK	0x0000ffff
+
+/* DBM_DBG_CNFG */
+#define DBM_ENABLE_IOC_MASK	0x0000000f
+
+/* DBM_SOFT_RESET */
+#define DBM_SFT_RST_EP0		0x00000001
+#define DBM_SFT_RST_EP1		0x00000002
+#define DBM_SFT_RST_EP2		0x00000004
+#define DBM_SFT_RST_EP3		0x00000008
+#define DBM_SFT_RST_EPS		0x0000000F
+#define DBM_SFT_RST		0x80000000
+
+#define DBM_MAX_EPS		4
+
+struct dwc3_msm_req_complete {
+	struct list_head list_item;
+	struct usb_request *req;
+	void (*orig_complete)(struct usb_ep *ep,
+			      struct usb_request *req);
+};
+
+struct dwc3_msm {
+	struct platform_device *dwc3;
+	struct device *dev;
+	void __iomem *base;
+	u32 resource_size;
+	int dbm_num_eps;
+	u8 ep_num_mapping[DBM_MAX_EPS];
+	const struct usb_ep_ops *original_ep_ops[DWC3_ENDPOINTS_NUM];
+	struct list_head req_complete_list;
+};
+
+static struct dwc3_msm *context;
+
+/**
+ *
+ * Read register with debug info.
+ *
+ * @base - DWC3 base virtual address.
+ * @offset - register offset.
+ *
+ * @return u32
+ */
+static inline u32 dwc3_msm_read_reg(void *base, u32 offset)
+{
+	u32 val = ioread32(base + offset);
+	return val;
+}
+
+/**
+ * Read register masked field with debug info.
+ *
+ * @base - DWC3 base virtual address.
+ * @offset - register offset.
+ * @mask - register bitmask.
+ *
+ * @return u32
+ */
+static inline u32 dwc3_msm_read_reg_field(void *base,
+					  u32 offset,
+					  const u32 mask)
+{
+	u32 shift = find_first_bit((void *)&mask, 32);
+	u32 val = ioread32(base + offset);
+	val &= mask;		/* clear other bits */
+	val >>= shift;
+	return val;
+}
+
+/**
+ *
+ * Write register with debug info.
+ *
+ * @base - DWC3 base virtual address.
+ * @offset - register offset.
+ * @val - value to write.
+ *
+ */
+static inline void dwc3_msm_write_reg(void *base, u32 offset, u32 val)
+{
+	iowrite32(val, base + offset);
+}
+
+/**
+ * Write register masked field with debug info.
+ *
+ * @base - DWC3 base virtual address.
+ * @offset - register offset.
+ * @mask - register bitmask.
+ * @val - value to write.
+ *
+ */
+static inline void dwc3_msm_write_reg_field(void *base, u32 offset,
+					    const u32 mask, u32 val)
+{
+	u32 shift = find_first_bit((void *)&mask, 32);
+	u32 tmp = ioread32(base + offset);
+
+	tmp &= ~mask;		/* clear written bits */
+	val = tmp | (val << shift);
+	iowrite32(val, base + offset);
+}
+
+/**
+ * Return DBM EP number which is not already configured.
+ *
+ */
+static int dwc3_msm_find_avail_dbm_ep(void)
+{
+	int i;
+
+	for (i = 0; i < context->dbm_num_eps; i++)
+		if (!context->ep_num_mapping[i])
+			return i;
+
+	return -ENODEV; /* Not found */
+}
+
+/**
+ * Return DBM EP number according to usb endpoint number.
+ *
+ */
+static int dwc3_msm_find_matching_dbm_ep(u8 usb_ep)
+{
+	int i;
+
+	for (i = 0; i < context->dbm_num_eps; i++)
+		if (context->ep_num_mapping[i] == usb_ep)
+			return i;
+
+	return -ENODEV; /* Not found */
+}
+
+/**
+ * Return number of configured DBM endpoints.
+ *
+ */
+static int dwc3_msm_configured_dbm_ep_num(void)
+{
+	int i;
+	int count = 0;
+
+	for (i = 0; i < context->dbm_num_eps; i++)
+		if (context->ep_num_mapping[i])
+			count++;
+
+	return count;
+}
+
+/**
+ * Configure the DBM with the USB3 core event buffer.
+ * This function is called by the SNPS UDC upon initialization.
+ *
+ * @addr - address of the event buffer.
+ * @size - size of the event buffer.
+ *
+ */
+static int dwc3_msm_event_buffer_config(u32 addr, u16 size)
+{
+	dev_dbg(context->dev, "%s\n", __func__);
+
+	dwc3_msm_write_reg(context->base, DBM_GEVNTADR, addr);
+	dwc3_msm_write_reg_field(context->base, DBM_GEVNTSIZ,
+		DBM_GEVNTSIZ_MASK, size);
+
+	return 0;
+}
+
+/**
+ * Reset the DBM registers upon initialization.
+ *
+ */
+static int dwc3_msm_dbm_soft_reset(void)
+{
+	dev_dbg(context->dev, "%s\n", __func__);
+
+	dwc3_msm_write_reg_field(context->base, DBM_SOFT_RESET,
+		DBM_SFT_RST, 1);
+
+	return 0;
+}
+
+/**
+ * Soft reset specific DBM ep.
+ * This function is called by the function driver upon events
+ * such as transfer aborting, USB re-enumeration and USB
+ * disconnection.
+ *
+ * @dbm_ep - DBM ep number.
+ * @enter_reset - should we enter a reset state or get out of it.
+ *
+ */
+static int dwc3_msm_dbm_ep_soft_reset(u8 dbm_ep, bool enter_reset)
+{
+	dev_dbg(context->dev, "%s\n", __func__);
+
+	if (dbm_ep >= context->dbm_num_eps) {
+		dev_err(context->dev,
+				"%s: Invalid DBM ep index\n", __func__);
+		return -ENODEV;
+	}
+
+	if (enter_reset) {
+		dwc3_msm_write_reg_field(context->base, DBM_SOFT_RESET,
+			DBM_SFT_RST_EPS, 1 << dbm_ep);
+	} else {
+		dwc3_msm_write_reg_field(context->base, DBM_SOFT_RESET,
+			DBM_SFT_RST_EPS, 0);
+	}
+
+	return 0;
+}
+
+/**
+ * Configure a USB DBM ep to work in BAM mode.
+ *
+ *
+ * @usb_ep - USB physical EP number.
+ * @producer - producer/consumer.
+ * @disable_wb - disable write back to system memory.
+ * @internal_mem - use internal USB memory for data fifo.
+ * @ioc - enable interrupt on completion.
+ *
+ * @return int - DBM ep number.
+ */
+static int dwc3_msm_dbm_ep_config(u8 usb_ep, u8 bam_pipe,
+				  bool producer, bool disable_wb,
+				  bool internal_mem, bool ioc)
+{
+	u8 dbm_ep;
+	u8 ioc_mask;
+
+	dev_dbg(context->dev, "%s\n", __func__);
+
+	dbm_ep = dwc3_msm_find_avail_dbm_ep();
+	if (dbm_ep < 0) {
+		dev_err(context->dev, "%s: No more DBM eps\n", __func__);
+		return -ENODEV;
+	}
+
+	context->ep_num_mapping[dbm_ep] = usb_ep;
+
+	/* First, reset the dbm endpoint */
+	dwc3_msm_dbm_ep_soft_reset(dbm_ep, false);
+
+	ioc_mask = dwc3_msm_read_reg_field(context->base, DBM_DBG_CNFG,
+		DBM_ENABLE_IOC_MASK);
+	ioc_mask &= ~(ioc << dbm_ep); /* Clear ioc bit for dbm_ep */
+	/* Set ioc bit for dbm_ep if needed */
+	dwc3_msm_write_reg_field(context->base, DBM_DBG_CNFG,
+		DBM_ENABLE_IOC_MASK, ioc_mask | (ioc << dbm_ep));
+
+	dwc3_msm_write_reg(context->base, DBM_EP_CFG(dbm_ep),
+		producer | disable_wb | internal_mem);
+	dwc3_msm_write_reg_field(context->base, DBM_EP_CFG(dbm_ep),
+		DBM_USB3_EP_NUM, usb_ep);
+	dwc3_msm_write_reg_field(context->base, DBM_EP_CFG(dbm_ep),
+		DBM_BAM_PIPE_NUM, bam_pipe);
+	dwc3_msm_write_reg_field(context->base, DBM_EP_CFG(dbm_ep),
+		DBM_EN_EP, 1);
+
+	return dbm_ep;
+}
+
+/**
+ * Configure a USB DBM ep to work in normal mode.
+ *
+ * @usb_ep - USB ep number.
+ *
+ */
+static int dwc3_msm_dbm_ep_unconfig(u8 usb_ep)
+{
+	u8 dbm_ep;
+
+	dev_dbg(context->dev, "%s\n", __func__);
+
+	dbm_ep = dwc3_msm_find_matching_dbm_ep(usb_ep);
+
+	if (dbm_ep < 0) {
+		dev_err(context->dev,
+				"%s: Invalid usb ep index\n", __func__);
+		return -ENODEV;
+	}
+
+	context->ep_num_mapping[dbm_ep] = 0;
+
+	dwc3_msm_write_reg(context->base, DBM_EP_CFG(dbm_ep), 0);
+
+	/* Reset the dbm endpoint */
+	dwc3_msm_dbm_ep_soft_reset(dbm_ep, true);
+
+	return 0;
+}
+
+/**
+ * Configure the DBM with the BAM's data fifo.
+ * This function is called by the USB BAM Driver
+ * upon initialization.
+ *
+ * @ep - pointer to usb endpoint.
+ * @addr - address of data fifo.
+ * @size - size of data fifo.
+ *
+ */
+int msm_data_fifo_config(struct usb_ep *ep, u32 addr, u32 size)
+{
+	u8 dbm_ep;
+	struct dwc3_ep *dep = to_dwc3_ep(ep);
+
+	dev_dbg(context->dev, "%s\n", __func__);
+
+	dbm_ep = dwc3_msm_find_matching_dbm_ep(dep->number);
+
+	if (dbm_ep >= context->dbm_num_eps) {
+		dev_err(context->dev,
+				"%s: Invalid DBM ep index\n", __func__);
+		return -ENODEV;
+	}
+
+	dwc3_msm_write_reg(context->base, DBM_DATA_FIFO(dbm_ep), addr);
+	dwc3_msm_write_reg_field(context->base, DBM_DATA_FIFO_SIZE(dbm_ep),
+		DBM_DATA_FIFO_SIZE_MASK, size);
+
+	return 0;
+}
+
+/**
+* Cleanups for msm endpoint on request complete.
+*
+* Also call original request complete.
+*
+* @usb_ep - pointer to usb_ep instance.
+* @request - pointer to usb_request instance.
+*
+* @return int - 0 on success, negetive on error.
+*/
+static void dwc3_msm_req_complete_func(struct usb_ep *ep,
+				       struct usb_request *request)
+{
+	struct dwc3_request *req = to_dwc3_request(request);
+	struct dwc3_ep *dep = to_dwc3_ep(ep);
+	struct dwc3_msm_req_complete *req_complete = NULL;
+
+	/* Find original request complete function and remove it from list */
+	list_for_each_entry(req_complete,
+				&context->req_complete_list,
+				list_item) {
+		if (req_complete->req == request)
+			break;
+	}
+	if (!req_complete || req_complete->req != request) {
+		dev_err(dep->dwc->dev, "%s: could not find the request\n",
+					__func__);
+		return;
+	}
+	list_del(&req_complete->list_item);
+
+	/*
+	 * Release another one TRB to the pool since DBM queue took 2 TRBs
+	 * (normal and link), and the dwc3/gadget.c :: dwc3_gadget_giveback
+	 * released only one.
+	 */
+	if (req->queued)
+		dep->busy_slot++;
+
+	/* Unconfigure dbm ep */
+	dwc3_msm_dbm_ep_unconfig(dep->number);
+
+	/*
+	 * If this is the last endpoint we unconfigured, than reset also
+	 * the event buffers.
+	 */
+	if (0 == dwc3_msm_configured_dbm_ep_num())
+		dwc3_msm_event_buffer_config(0, 0);
+
+	/*
+	 * Call original complete function, notice that dwc->lock is already
+	 * taken by the caller of this function (dwc3_gadget_giveback()).
+	 */
+	request->complete = req_complete->orig_complete;
+	request->complete(ep, request);
+
+	kfree(req_complete);
+}
+
+/**
+* Helper function.
+* See the header of the dwc3_msm_ep_queue function.
+*
+* @dwc3_ep - pointer to dwc3_ep instance.
+* @req - pointer to dwc3_request instance.
+*
+* @return int - 0 on success, negetive on error.
+*/
+static int __dwc3_msm_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
+{
+	struct dwc3_trb_hw *trb_hw;
+	struct dwc3_trb_hw *trb_link_hw;
+	struct dwc3_trb trb;
+	struct dwc3_gadget_ep_cmd_params params;
+	u32 cmd;
+	int ret = 0;
+
+	if ((req->request.udc_priv & MSM_IS_FINITE_TRANSFER) &&
+	    (req->request.length > 0)) {
+		/* Map the request to a DMA. */
+		dwc3_map_buffer_to_dma(req);
+	}
+
+	/* We push the request to the dep->req_queued list to indicate that
+	 * this request is issued with start transfer. The request will be out
+	 * from this list in 2 cases. The first is that the transfer will be
+	 * completed (not if the transfer is endless using a circular TRBs with
+	 * with link TRB). The second case is an option to do stop stransfer,
+	 * this can be initiated by the function driver when calling dequeue.
+	 */
+	req->queued = true;
+	list_add_tail(&req->list, &dep->req_queued);
+
+	/* First, prepare a normal TRB, point to the fake buffer */
+	trb_hw = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK];
+	dep->free_slot++;
+	memset(&trb, 0, sizeof(trb));
+
+	req->trb = trb_hw;
+
+	trb.bplh = req->request.dma;
+	trb.lst = 0;
+	trb.trbctl = DWC3_TRBCTL_NORMAL;
+	trb.length = req->request.length;
+	trb.hwo = true;
+
+	dwc3_trb_to_hw(&trb, trb_hw);
+	req->trb_dma = dep->trb_pool_dma;
+
+	/* Second, prepare a Link TRB that points to the first TRB*/
+	trb_link_hw = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK];
+	dep->free_slot++;
+	memset(&trb, 0, sizeof(trb));
+
+	trb.bplh = dep->trb_pool_dma;
+	trb.trbctl = DWC3_TRBCTL_LINK_TRB;
+	trb.hwo = true;
+
+	dwc3_trb_to_hw(&trb, trb_link_hw);
+
+	/*
+	 * Now start the transfer
+	 */
+	memset(&params, 0, sizeof(params));
+	params.param0 = upper_32_bits(req->trb_dma);
+	params.param1 = lower_32_bits(req->trb_dma);
+	cmd = DWC3_DEPCMD_STARTTRANSFER;
+	ret = dwc3_send_gadget_ep_cmd(dep->dwc, dep->number, cmd, &params);
+	if (ret < 0) {
+		dev_dbg(dep->dwc->dev,
+			"%s: failed to send STARTTRANSFER command\n",
+			__func__);
+
+		dwc3_unmap_buffer_from_dma(req);
+		list_del(&req->list);
+		return ret;
+	}
+
+	return ret;
+}
+
+/**
+* Queue a usb request to the DBM endpoint.
+* This function should be called after the endpoint
+* was enabled by the ep_enable.
+*
+* This function prepares special structure of TRBs which
+* is familier with the DBM HW, so it will possible to use
+* this endpoint in DBM mode.
+*
+* The TRBs prepared by this function, is one normal TRB
+* which point to a fake buffer, followed by a link TRB
+* that points to the first TRB.
+*
+* The API of this function follow the regular API of
+* usb_ep_queue (see usb_ep_ops in include/linuk/usb/gadget.h).
+*
+* @usb_ep - pointer to usb_ep instance.
+* @request - pointer to usb_request instance.
+* @gfp_flags - possible flags.
+*
+* @return int - 0 on success, negetive on error.
+*/
+static int dwc3_msm_ep_queue(struct usb_ep *ep,
+			     struct usb_request *request, gfp_t gfp_flags)
+{
+	struct dwc3_request *req = to_dwc3_request(request);
+	struct dwc3_ep *dep = to_dwc3_ep(ep);
+	struct dwc3 *dwc = dep->dwc;
+	struct dwc3_msm_req_complete *req_complete;
+	unsigned long flags;
+	int ret = 0;
+	u8 bam_pipe;
+	bool producer;
+	bool disable_wb;
+	bool internal_mem;
+	bool ioc;
+
+	if (!(request->udc_priv & MSM_SPS_MODE)) {
+		/* Not SPS mode, call original queue */
+		dev_vdbg(dwc->dev, "%s: not sps mode, use regular queue\n",
+					__func__);
+
+		return (context->original_ep_ops[dep->number])->queue(ep,
+								request,
+								gfp_flags);
+	}
+
+	if (!dep->endpoint.desc) {
+		dev_err(dwc->dev,
+			"%s: trying to queue request %p to disabled ep %s\n",
+			__func__, request, ep->name);
+		return -EPERM;
+	}
+
+	if (dep->number == 0 || dep->number == 1) {
+		dev_err(dwc->dev,
+			"%s: trying to queue dbm request %p to control ep %s\n",
+			__func__, request, ep->name);
+		return -EPERM;
+	}
+
+	if (dep->free_slot > 0 || dep->busy_slot > 0 ||
+		!list_empty(&dep->request_list) ||
+		!list_empty(&dep->req_queued)) {
+
+		dev_err(dwc->dev,
+			"%s: trying to queue dbm request %p tp ep %s\n",
+			__func__, request, ep->name);
+		return -EPERM;
+	}
+
+	/*
+	 * Override req->complete function, but before doing that,
+	 * store it's original pointer in the req_complete_list.
+	 */
+	req_complete = kzalloc(sizeof(*req_complete), GFP_KERNEL);
+	if (!req_complete) {
+		dev_err(dep->dwc->dev, "%s: not enough memory\n", __func__);
+		return -ENOMEM;
+	}
+	req_complete->req = request;
+	req_complete->orig_complete = request->complete;
+	list_add_tail(&req_complete->list_item, &context->req_complete_list);
+	request->complete = dwc3_msm_req_complete_func;
+
+	/*
+	 * Configure dbm event buffers if this is the first
+	 * dbm endpoint we about to configure.
+	 */
+	if (0 == dwc3_msm_configured_dbm_ep_num())
+		dwc3_msm_event_buffer_config(dwc->ev_buffs[0]->dma,
+					     dwc->ev_buffs[0]->length);
+
+	/*
+	 * Configure the DBM endpoint
+	 */
+	bam_pipe = (request->udc_priv & MSM_PIPE_ID_MASK);
+	producer = ((request->udc_priv & MSM_PRODUCER) ? true : false);
+	disable_wb = ((request->udc_priv & MSM_DISABLE_WB) ? true : false);
+	internal_mem = ((request->udc_priv & MSM_INTERNAL_MEM) ? true : false);
+	ioc = ((request->udc_priv & MSM_ETD_IOC) ? true : false);
+
+	ret = dwc3_msm_dbm_ep_config(dep->number,
+					bam_pipe, producer,
+					disable_wb, internal_mem, ioc);
+	if (ret < 0) {
+		dev_err(context->dev,
+			"error %d after calling dwc3_msm_dbm_ep_config\n",
+			ret);
+		return ret;
+	}
+
+	dev_vdbg(dwc->dev, "%s: queing request %p to ep %s length %d\n",
+			__func__, request, ep->name, request->length);
+
+	/*
+	 * We must obtain the lock of the dwc3 core driver,
+	 * including disabling interrupts, so we will be sure
+	 * that we are the only ones that configure the HW device
+	 * core and ensure that we queuing the request will finish
+	 * as soon as possible so we will release back the lock.
+	 */
+	spin_lock_irqsave(&dwc->lock, flags);
+	ret = __dwc3_msm_ep_queue(dep, req);
+	spin_unlock_irqrestore(&dwc->lock, flags);
+	if (ret < 0) {
+		dev_err(context->dev,
+			"error %d after calling __dwc3_msm_ep_queue\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * Configure MSM endpoint.
+ * This function do specific configurations
+ * to an endpoint which need specific implementaion
+ * in the MSM architecture.
+ *
+ * This function should be called by usb function/class
+ * layer which need a support from the specific MSM HW
+ * which wrap the USB3 core. (like DBM specific endpoints)
+ *
+ * @ep - a pointer to some usb_ep instance
+ *
+ * @return int - 0 on success, negetive on error.
+ */
+int msm_ep_config(struct usb_ep *ep)
+{
+	struct dwc3_ep *dep = to_dwc3_ep(ep);
+	struct usb_ep_ops *new_ep_ops;
+
+	/* Save original ep ops for future restore*/
+	if (context->original_ep_ops[dep->number]) {
+		dev_err(context->dev,
+			"ep [%s,%d] already configured as msm endpoint\n",
+			ep->name, dep->number);
+		return -EPERM;
+	}
+	context->original_ep_ops[dep->number] = ep->ops;
+
+	/* Set new usb ops as we like */
+	new_ep_ops = kzalloc(sizeof(struct usb_ep_ops), GFP_KERNEL);
+	if (!new_ep_ops) {
+		dev_err(context->dev,
+			"%s: unable to allocate mem for new usb ep ops\n",
+			__func__);
+		return -ENOMEM;
+	}
+	(*new_ep_ops) = (*ep->ops);
+	new_ep_ops->queue = dwc3_msm_ep_queue;
+	ep->ops = new_ep_ops;
+
+	/*
+	 * Do HERE more usb endpoint configurations
+	 * which are specific to MSM.
+	 */
+
+	return 0;
+}
+EXPORT_SYMBOL(msm_ep_config);
+
+/**
+ * Un-configure MSM endpoint.
+ * Tear down configurations done in the
+ * dwc3_msm_ep_config function.
+ *
+ * @ep - a pointer to some usb_ep instance
+ *
+ * @return int - 0 on success, negetive on error.
+ */
+int msm_ep_unconfig(struct usb_ep *ep)
+{
+	struct dwc3_ep *dep = to_dwc3_ep(ep);
+	struct usb_ep_ops *old_ep_ops;
+
+	/* Restore original ep ops */
+	if (!context->original_ep_ops[dep->number]) {
+		dev_err(context->dev,
+			"ep [%s,%d] was not configured as msm endpoint\n",
+			ep->name, dep->number);
+		return -EINVAL;
+	}
+	old_ep_ops = (struct usb_ep_ops	*)ep->ops;
+	ep->ops = context->original_ep_ops[dep->number];
+	context->original_ep_ops[dep->number] = NULL;
+	kfree(old_ep_ops);
+
+	/*
+	 * Do HERE more usb endpoint un-configurations
+	 * which are specific to MSM.
+	 */
+
+	return 0;
+}
+EXPORT_SYMBOL(msm_ep_unconfig);
+
+static int __devinit dwc3_msm_probe(struct platform_device *pdev)
+{
+	struct device_node *node = pdev->dev.of_node;
+	struct platform_device *dwc3;
+	struct dwc3_msm *msm;
+	struct resource *res;
+	int ret = 0;
+
+	msm = devm_kzalloc(&pdev->dev, sizeof(*msm), GFP_KERNEL);
+	if (!msm) {
+		dev_err(&pdev->dev, "not enough memory\n");
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(pdev, msm);
+	context = msm;
+
+	INIT_LIST_HEAD(&msm->req_complete_list);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "missing memory base resource\n");
+		return -ENODEV;
+	}
+
+	msm->base = devm_ioremap_nocache(&pdev->dev, res->start,
+		resource_size(res));
+	if (!msm->base) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		return -ENODEV;
+	}
+
+	dwc3 = platform_device_alloc("dwc3-msm", -1);
+	if (!dwc3) {
+		dev_err(&pdev->dev, "couldn't allocate dwc3 device\n");
+		return -ENOMEM;
+	}
+
+	dma_set_coherent_mask(&dwc3->dev, pdev->dev.coherent_dma_mask);
+
+	dwc3->dev.parent = &pdev->dev;
+	dwc3->dev.dma_mask = pdev->dev.dma_mask;
+	dwc3->dev.dma_parms = pdev->dev.dma_parms;
+	msm->resource_size = resource_size(res);
+	msm->dev = &pdev->dev;
+	msm->dwc3 = dwc3;
+
+	if (of_property_read_u32(node, "qcom,dwc-usb3-msm-dbm-eps",
+				 &msm->dbm_num_eps)) {
+		dev_err(&pdev->dev,
+			"unable to read platform data num of dbm eps\n");
+		msm->dbm_num_eps = DBM_MAX_EPS;
+	}
+
+	if (msm->dbm_num_eps > DBM_MAX_EPS) {
+		dev_err(&pdev->dev,
+			"Driver doesn't support number of DBM EPs. "
+			"max: %d, dbm_num_eps: %d\n",
+			DBM_MAX_EPS, msm->dbm_num_eps);
+		ret = -ENODEV;
+		goto err1;
+	}
+
+	ret = platform_device_add_resources(dwc3, pdev->resource,
+		pdev->num_resources);
+	if (ret) {
+		dev_err(&pdev->dev, "couldn't add resources to dwc3 device\n");
+		goto err1;
+	}
+
+	ret = platform_device_add(dwc3);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register dwc3 device\n");
+		goto err1;
+	}
+
+	/* Reset the DBM */
+	dwc3_msm_dbm_soft_reset();
+
+	return 0;
+
+err1:
+	platform_device_put(dwc3);
+
+	return ret;
+}
+
+static int __devexit dwc3_msm_remove(struct platform_device *pdev)
+{
+	struct dwc3_msm	*msm = platform_get_drvdata(pdev);
+
+	platform_device_unregister(msm->dwc3);
+
+	return 0;
+}
+
+static const struct of_device_id of_dwc3_matach[] = {
+	{
+		.compatible = "qcom,dwc-usb3-msm",
+	},
+	{ },
+};
+MODULE_DEVICE_TABLE(of, of_dwc3_matach);
+
+static struct platform_driver dwc3_msm_driver = {
+	.probe		= dwc3_msm_probe,
+	.remove		= __devexit_p(dwc3_msm_remove),
+	.driver		= {
+		.name	= "msm-dwc3",
+		.of_match_table	= of_dwc3_matach,
+	},
+};
+
+MODULE_LICENSE("GPLV2");
+MODULE_DESCRIPTION("DesignWare USB3 MSM Glue Layer");
+
+static int __devinit dwc3_msm_init(void)
+{
+	return platform_driver_register(&dwc3_msm_driver);
+}
+module_init(dwc3_msm_init);
+
+static void __exit dwc3_msm_exit(void)
+{
+	platform_driver_unregister(&dwc3_msm_driver);
+}
+module_exit(dwc3_msm_exit);
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index a04b3f5..14b07e5 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -83,7 +83,7 @@
 config USB_GADGET_VBUS_DRAW
 	int "Maximum VBUS Power usage (2-500 mA)"
 	range 2 500
-	default 2
+	default 500
 	help
 	   Some devices need to draw power from USB when they are
 	   configured, perhaps to operate circuitry or to recharge
@@ -494,10 +494,33 @@
 	  dynamically linked module called "ci13xxx_msm" and force all
 	  gadget drivers to also be dynamically linked.
 
+config USB_CI13XXX_MSM_HSIC
+	tristate "MIPS HSIC CI13xxx for MSM"
+	depends on ARCH_MSM
+	select USB_GADGET_DUALSPEED
+	help
+	  MSM SoC has chipidea USB controller.  This driver uses
+	  ci13xxx_udc core. Support USB-HSIC core.
+
+	  Say "y" to link the driver statically, or "m" to build a
+	  dynamically linked module called "ci13xxx_msm_hsic" and force all
+	  gadget drivers to also be dynamically linked.
+
 #
 # LAST -- dummy/emulated controller
 #
 
+config USB_MSM_72K
+	tristate "MSM 72K Device Controller"
+	depends on ARCH_MSM
+	select USB_GADGET_DUALSPEED
+	help
+	   USB gadget driver for Qualcomm MSM 72K architecture.
+
+	   Say "y" to link the driver statically, or "m" to build a
+	   dynamically linked module called "msm72k" and force all
+	   gadget drivers to also be dynamically linked.
+
 config USB_DUMMY_HCD
 	tristate "Dummy HCD (DEVELOPMENT)"
 	depends on USB=y || (USB=m && USB_GADGET=m)
@@ -986,4 +1009,90 @@
 
 endchoice
 
+config USB_CSW_HACK
+	boolean "USB Mass storage csw hack Feature"
+	default y
+	help
+	 This csw hack feature is for increasing the performance of the mass
+	 storage
+
+config USB_MSC_PROFILING
+	bool "USB MSC performance profiling"
+	help
+	  If you say Y here, support will be added for collecting
+	  Mass-storage performance numbers at the VFS level.
+
+config MODEM_SUPPORT
+	boolean "modem support in generic serial function driver"
+	depends on USB_G_ANDROID
+	default y
+	help
+          This feature enables the modem functionality in the
+	  generic serial.
+	  adds interrupt endpoint support to send modem notifications
+	  to host.
+	  adds CDC descriptors to enumerate the generic serial as MODEM.
+	  adds CDC class requests to configure MODEM line settings.
+	  Say "y" to enable MODEM support in the generic serial driver.
+
+config RMNET_SMD_CTL_CHANNEL
+	string "RMNET control SMD channel name"
+	depends on USB_G_ANDROID && MSM_SMD
+	default ""
+	help
+	  Control SMD channel for transferring QMI messages
+
+config RMNET_SMD_DATA_CHANNEL
+	string "RMNET Data SMD channel name"
+	depends on USB_G_ANDROID && MSM_SMD
+	default ""
+	help
+	  Data SMD channel for transferring network data
+
+config RMNET_SDIO_CTL_CHANNEL
+       int "RMNET control SDIO channel id"
+       default 8
+       depends on MSM_SDIO_CMUX && MSM_SDIO_DMUX
+       help
+         Control SDIO channel for transferring RMNET QMI messages
+
+config RMNET_SDIO_DATA_CHANNEL
+       int "RMNET Data SDIO channel id"
+       default 8
+       depends on MSM_SDIO_CMUX && MSM_SDIO_DMUX
+       help
+         Data SDIO channel for transferring network data
+
+config RMNET_SMD_SDIO_CTL_CHANNEL
+       int "RMNET(sdio_smd) Control SDIO channel id"
+       depends on MSM_SDIO_CMUX && MSM_SDIO_DMUX
+       default 8
+       help
+         Control SDIO channel for transferring QMI messages
+
+config RMNET_SMD_SDIO_DATA_CHANNEL
+       int "RMNET(sdio_smd) Data SDIO channel id"
+       default 8
+       depends on MSM_SDIO_CMUX && MSM_SDIO_DMUX
+       help
+         Data SDIO channel for transferring network data
+
+config RMNET_SDIO_SMD_DATA_CHANNEL
+       string "RMNET(sdio_smd) Data SMD channel name"
+       depends on MSM_SDIO_CMUX && MSM_SDIO_DMUX
+       default "DATA40"
+       help
+	  Data SMD channel for transferring network data
+
+config USB_ANDROID_RMNET_CTRL_SMD
+       boolean "RmNet(BAM) control over SMD driver"
+       depends on MSM_SMD
+       help
+         Enabling this option adds rmnet control over SMD
+	 support to the android gadget. Rmnet is an
+	 alternative to CDC-ECM and Windows RNDIS.
+	 It uses QUALCOMM MSM Interface for control
+	 transfers. This option enables only control interface.
+	 Data interface used is BAM.
+
 endif # USB_GADGET
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index b91866a6..c646c9f 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -29,8 +29,10 @@
 obj-$(CONFIG_USB_EG20T)		+= pch_udc.o
 obj-$(CONFIG_USB_MV_UDC)	+= mv_udc.o
 mv_udc-y			:= mv_udc_core.o
+obj-$(CONFIG_USB_CI13XXX_MSM_HSIC)	+= ci13xxx_msm_hsic.o
 obj-$(CONFIG_USB_CI13XXX_MSM)	+= ci13xxx_msm.o
 obj-$(CONFIG_USB_FUSB300)	+= fusb300_udc.o
+obj-$(CONFIG_USB_MSM_72K)	+= msm72k_udc.o
 
 #
 # USB gadget drivers
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index 7a2af58..eefe95f 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -23,10 +23,12 @@
 #include <linux/kernel.h>
 #include <linux/utsname.h>
 #include <linux/platform_device.h>
+#include <linux/pm_qos.h>
 
 #include <linux/usb/ch9.h>
 #include <linux/usb/composite.h>
 #include <linux/usb/gadget.h>
+#include <linux/usb/android.h>
 
 #include "gadget_chips.h"
 
@@ -42,16 +44,33 @@
 #include "epautoconf.c"
 #include "composite.c"
 
+#include "f_diag.c"
+#include "f_rmnet_smd.c"
+#include "f_rmnet_sdio.c"
+#include "f_rmnet_smd_sdio.c"
+#include "f_rmnet.c"
 #include "f_mass_storage.c"
 #include "u_serial.c"
+#include "u_sdio.c"
+#include "u_smd.c"
+#include "u_bam.c"
+#include "u_rmnet_ctrl_smd.c"
+#include "u_ctrl_hsic.c"
+#include "u_data_hsic.c"
+#include "u_ctrl_hsuart.c"
+#include "u_data_hsuart.c"
+#include "f_serial.c"
 #include "f_acm.c"
 #include "f_adb.c"
+#include "f_ccid.c"
 #include "f_mtp.c"
 #include "f_accessory.c"
 #define USB_ETH_RNDIS y
 #include "f_rndis.c"
 #include "rndis.c"
 #include "u_ether.c"
+#include "u_bam_data.c"
+#include "f_mbim.c"
 
 MODULE_AUTHOR("Mike Lockwood");
 MODULE_DESCRIPTION("Android Composite USB Driver");
@@ -106,8 +125,12 @@
 	bool enabled;
 	int disable_depth;
 	struct mutex mutex;
+	struct android_usb_platform_data *pdata;
+
 	bool connected;
 	bool sw_connected;
+	char pm_qos[5];
+	struct pm_qos_request pm_qos_req_dma;
 	struct work_struct work;
 };
 
@@ -154,14 +177,50 @@
 	.bNumConfigurations   = 1,
 };
 
+static struct usb_otg_descriptor otg_descriptor = {
+	.bLength =		sizeof otg_descriptor,
+	.bDescriptorType =	USB_DT_OTG,
+	.bmAttributes =		USB_OTG_SRP | USB_OTG_HNP,
+	.bcdOTG               = __constant_cpu_to_le16(0x0200),
+};
+
+static const struct usb_descriptor_header *otg_desc[] = {
+	(struct usb_descriptor_header *) &otg_descriptor,
+	NULL,
+};
+
 static struct usb_configuration android_config_driver = {
 	.label		= "android",
 	.unbind		= android_unbind_config,
 	.bConfigurationValue = 1,
-	.bmAttributes	= USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
-	.bMaxPower	= 0xFA, /* 500ma */
 };
 
+enum android_device_state {
+	USB_DISCONNECTED,
+	USB_CONNECTED,
+	USB_CONFIGURED,
+};
+
+static void android_pm_qos_update_latency(struct android_dev *dev, int vote)
+{
+	struct android_usb_platform_data *pdata = dev->pdata;
+	u32 swfi_latency = 0;
+	static int last_vote = -1;
+
+	if (!pdata || vote == last_vote
+		|| !pdata->swfi_latency)
+		return;
+
+	swfi_latency = pdata->swfi_latency + 1;
+	if (vote)
+		pm_qos_update_request(&dev->pm_qos_req_dma,
+				swfi_latency);
+	else
+		pm_qos_update_request(&dev->pm_qos_req_dma,
+				PM_QOS_DEFAULT_VALUE);
+	last_vote = vote;
+}
+
 static void android_work(struct work_struct *data)
 {
 	struct android_dev *dev = container_of(data, struct android_dev, work);
@@ -170,18 +229,53 @@
 	char *connected[2]    = { "USB_STATE=CONNECTED", NULL };
 	char *configured[2]   = { "USB_STATE=CONFIGURED", NULL };
 	char **uevent_envp = NULL;
+	static enum android_device_state last_uevent, next_state;
 	unsigned long flags;
+	int pm_qos_vote = -1;
 
 	spin_lock_irqsave(&cdev->lock, flags);
-	if (cdev->config)
+	if (cdev->config) {
 		uevent_envp = configured;
-	else if (dev->connected != dev->sw_connected)
+		next_state = USB_CONFIGURED;
+	} else if (dev->connected != dev->sw_connected) {
 		uevent_envp = dev->connected ? connected : disconnected;
+		next_state = dev->connected ? USB_CONNECTED : USB_DISCONNECTED;
+		if (dev->connected && strncmp(dev->pm_qos, "low", 3))
+			pm_qos_vote = 1;
+		else if (!dev->connected || !strncmp(dev->pm_qos, "low", 3))
+			pm_qos_vote = 0;
+	}
 	dev->sw_connected = dev->connected;
 	spin_unlock_irqrestore(&cdev->lock, flags);
 
+	if (pm_qos_vote != -1)
+		android_pm_qos_update_latency(dev, pm_qos_vote);
+
 	if (uevent_envp) {
+		/*
+		 * Some userspace modules, e.g. MTP, work correctly only if
+		 * CONFIGURED uevent is preceded by DISCONNECT uevent.
+		 * Check if we missed sending out a DISCONNECT uevent. This can
+		 * happen if host PC resets and configures device really quick.
+		 */
+		if (((uevent_envp == connected) &&
+		      (last_uevent != USB_DISCONNECTED)) ||
+		    ((uevent_envp == configured) &&
+		      (last_uevent == USB_CONFIGURED))) {
+			pr_info("%s: sent missed DISCONNECT event\n", __func__);
+			kobject_uevent_env(&dev->dev->kobj, KOBJ_CHANGE,
+								disconnected);
+			msleep(20);
+		}
+		/*
+		 * Before sending out CONFIGURED uevent give function drivers
+		 * a chance to wakeup userspace threads and notify disconnect
+		 */
+		if (uevent_envp == configured)
+			msleep(50);
+
 		kobject_uevent_env(&dev->dev->kobj, KOBJ_CHANGE, uevent_envp);
+		last_uevent = next_state;
 		pr_info("%s: sent uevent %s\n", __func__, uevent_envp[0]);
 	} else {
 		pr_info("%s: did not send uevent (%d %d %p)\n", __func__,
@@ -311,88 +405,410 @@
 }
 
 
-#define MAX_ACM_INSTANCES 4
-struct acm_function_config {
-	int instances;
+/*-------------------------------------------------------------------------*/
+/* Supported functions initialization */
+
+/* RMNET_SMD */
+static int rmnet_smd_function_bind_config(struct android_usb_function *f,
+					  struct usb_configuration *c)
+{
+	return rmnet_smd_bind_config(c);
+}
+
+static struct android_usb_function rmnet_smd_function = {
+	.name		= "rmnet_smd",
+	.bind_config	= rmnet_smd_function_bind_config,
 };
 
-static int
-acm_function_init(struct android_usb_function *f,
-		struct usb_composite_dev *cdev)
+/* RMNET_SDIO */
+static int rmnet_sdio_function_bind_config(struct android_usb_function *f,
+					  struct usb_configuration *c)
 {
-	f->config = kzalloc(sizeof(struct acm_function_config), GFP_KERNEL);
-	if (!f->config)
-		return -ENOMEM;
-
-	return gserial_setup(cdev->gadget, MAX_ACM_INSTANCES);
+	return rmnet_sdio_function_add(c);
 }
 
+static struct android_usb_function rmnet_sdio_function = {
+	.name		= "rmnet_sdio",
+	.bind_config	= rmnet_sdio_function_bind_config,
+};
+
+/* RMNET_SMD_SDIO */
+static int rmnet_smd_sdio_function_init(struct android_usb_function *f,
+				 struct usb_composite_dev *cdev)
+{
+	return rmnet_smd_sdio_init();
+}
+
+static void rmnet_smd_sdio_function_cleanup(struct android_usb_function *f)
+{
+	rmnet_smd_sdio_cleanup();
+}
+
+static int rmnet_smd_sdio_bind_config(struct android_usb_function *f,
+					  struct usb_configuration *c)
+{
+	return rmnet_smd_sdio_function_add(c);
+}
+
+static struct device_attribute *rmnet_smd_sdio_attributes[] = {
+					&dev_attr_transport, NULL };
+
+static struct android_usb_function rmnet_smd_sdio_function = {
+	.name		= "rmnet_smd_sdio",
+	.init		= rmnet_smd_sdio_function_init,
+	.cleanup	= rmnet_smd_sdio_function_cleanup,
+	.bind_config	= rmnet_smd_sdio_bind_config,
+	.attributes	= rmnet_smd_sdio_attributes,
+};
+
+/*rmnet transport string format(per port):"ctrl0,data0,ctrl1,data1..." */
+#define MAX_XPORT_STR_LEN 50
+static char rmnet_transports[MAX_XPORT_STR_LEN];
+
+static void rmnet_function_cleanup(struct android_usb_function *f)
+{
+	frmnet_cleanup();
+}
+
+static int rmnet_function_bind_config(struct android_usb_function *f,
+					 struct usb_configuration *c)
+{
+	int i;
+	int err = 0;
+	char *ctrl_name;
+	char *data_name;
+	char buf[MAX_XPORT_STR_LEN], *b;
+	static int rmnet_initialized, ports;
+
+	if (!rmnet_initialized) {
+		rmnet_initialized = 1;
+		strlcpy(buf, rmnet_transports, sizeof(buf));
+		b = strim(buf);
+		while (b) {
+			ctrl_name = strsep(&b, ",");
+			data_name = strsep(&b, ",");
+			if (ctrl_name && data_name) {
+				err = frmnet_init_port(ctrl_name, data_name);
+				if (err) {
+					pr_err("rmnet: Cannot open ctrl port:"
+						"'%s' data port:'%s'\n",
+						ctrl_name, data_name);
+					goto out;
+				}
+				ports++;
+			}
+		}
+
+		err = rmnet_gport_setup();
+		if (err) {
+			pr_err("rmnet: Cannot setup transports");
+			goto out;
+		}
+	}
+
+	for (i = 0; i < ports; i++) {
+		err = frmnet_bind_config(c, i);
+		if (err) {
+			pr_err("Could not bind rmnet%u config\n", i);
+			break;
+		}
+	}
+out:
+	return err;
+}
+
+static ssize_t rmnet_transports_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", rmnet_transports);
+}
+
+static ssize_t rmnet_transports_store(
+		struct device *device, struct device_attribute *attr,
+		const char *buff, size_t size)
+{
+	strlcpy(rmnet_transports, buff, sizeof(rmnet_transports));
+
+	return size;
+}
+
+static struct device_attribute dev_attr_rmnet_transports =
+					__ATTR(transports, S_IRUGO | S_IWUSR,
+						rmnet_transports_show,
+						rmnet_transports_store);
+static struct device_attribute *rmnet_function_attributes[] = {
+					&dev_attr_rmnet_transports,
+					NULL };
+
+static struct android_usb_function rmnet_function = {
+	.name		= "rmnet",
+	.cleanup	= rmnet_function_cleanup,
+	.bind_config	= rmnet_function_bind_config,
+	.attributes	= rmnet_function_attributes,
+};
+
+
+/* MBIM - used with BAM */
+#define MAX_MBIM_INSTANCES 1
+
+static int mbim_function_init(struct android_usb_function *f,
+					 struct usb_composite_dev *cdev)
+{
+	return mbim_init(MAX_MBIM_INSTANCES);
+}
+
+static void mbim_function_cleanup(struct android_usb_function *f)
+{
+	fmbim_cleanup();
+}
+
+static int mbim_function_bind_config(struct android_usb_function *f,
+					  struct usb_configuration *c)
+{
+	return mbim_bind_config(c, 0);
+}
+
+static struct android_usb_function mbim_function = {
+	.name		= "usb_mbim",
+	.cleanup	= mbim_function_cleanup,
+	.bind_config	= mbim_function_bind_config,
+	.init		= mbim_function_init,
+};
+
+
+/* DIAG */
+static char diag_clients[32];	    /*enabled DIAG clients- "diag[,diag_mdm]" */
+static ssize_t clients_store(
+		struct device *device, struct device_attribute *attr,
+		const char *buff, size_t size)
+{
+	strlcpy(diag_clients, buff, sizeof(diag_clients));
+
+	return size;
+}
+
+static DEVICE_ATTR(clients, S_IWUSR, NULL, clients_store);
+static struct device_attribute *diag_function_attributes[] =
+					 { &dev_attr_clients, NULL };
+
+static int diag_function_init(struct android_usb_function *f,
+				 struct usb_composite_dev *cdev)
+{
+	return diag_setup();
+}
+
+static void diag_function_cleanup(struct android_usb_function *f)
+{
+	diag_cleanup();
+}
+
+static int diag_function_bind_config(struct android_usb_function *f,
+					struct usb_configuration *c)
+{
+	char *name;
+	char buf[32], *b;
+	int once = 0, err = -1;
+	int (*notify)(uint32_t, const char *);
+
+	strlcpy(buf, diag_clients, sizeof(buf));
+	b = strim(buf);
+
+	while (b) {
+		notify = NULL;
+		name = strsep(&b, ",");
+		/* Allow only first diag channel to update pid and serial no */
+		if (_android_dev->pdata && !once++)
+			notify = _android_dev->pdata->update_pid_and_serial_num;
+
+		if (name) {
+			err = diag_function_add(c, name, notify);
+			if (err)
+				pr_err("diag: Cannot open channel '%s'", name);
+		}
+	}
+
+	return err;
+}
+
+static struct android_usb_function diag_function = {
+	.name		= "diag",
+	.init		= diag_function_init,
+	.cleanup	= diag_function_cleanup,
+	.bind_config	= diag_function_bind_config,
+	.attributes	= diag_function_attributes,
+};
+
+/* SERIAL */
+static char serial_transports[32];	/*enabled FSERIAL ports - "tty[,sdio]"*/
+static ssize_t serial_transports_store(
+		struct device *device, struct device_attribute *attr,
+		const char *buff, size_t size)
+{
+	strlcpy(serial_transports, buff, sizeof(serial_transports));
+
+	return size;
+}
+
+static DEVICE_ATTR(transports, S_IWUSR, NULL, serial_transports_store);
+static struct device_attribute *serial_function_attributes[] =
+					 { &dev_attr_transports, NULL };
+
+static void serial_function_cleanup(struct android_usb_function *f)
+{
+	gserial_cleanup();
+}
+
+static int serial_function_bind_config(struct android_usb_function *f,
+					struct usb_configuration *c)
+{
+	char *name;
+	char buf[32], *b;
+	int err = -1, i;
+	static int serial_initialized = 0, ports = 0;
+
+	if (serial_initialized)
+		goto bind_config;
+
+	serial_initialized = 1;
+	strlcpy(buf, serial_transports, sizeof(buf));
+	b = strim(buf);
+
+	while (b) {
+		name = strsep(&b, ",");
+
+		if (name) {
+			err = gserial_init_port(ports, name);
+			if (err) {
+				pr_err("serial: Cannot open port '%s'", name);
+				goto out;
+			}
+			ports++;
+		}
+	}
+	err = gport_setup(c);
+	if (err) {
+		pr_err("serial: Cannot setup transports");
+		goto out;
+	}
+
+bind_config:
+	for (i = 0; i < ports; i++) {
+		err = gser_bind_config(c, i);
+		if (err) {
+			pr_err("serial: bind_config failed for port %d", i);
+			goto out;
+		}
+	}
+
+out:
+	return err;
+}
+
+static struct android_usb_function serial_function = {
+	.name		= "serial",
+	.cleanup	= serial_function_cleanup,
+	.bind_config	= serial_function_bind_config,
+	.attributes	= serial_function_attributes,
+};
+
+/* ACM */
+static char acm_transports[32];	/*enabled ACM ports - "tty[,sdio]"*/
+static ssize_t acm_transports_store(
+		struct device *device, struct device_attribute *attr,
+		const char *buff, size_t size)
+{
+	strlcpy(acm_transports, buff, sizeof(acm_transports));
+
+	return size;
+}
+
+static DEVICE_ATTR(acm_transports, S_IWUSR, NULL, acm_transports_store);
+static struct device_attribute *acm_function_attributes[] = {
+		&dev_attr_acm_transports, NULL };
+
 static void acm_function_cleanup(struct android_usb_function *f)
 {
 	gserial_cleanup();
-	kfree(f->config);
-	f->config = NULL;
 }
 
-static int
-acm_function_bind_config(struct android_usb_function *f,
-		struct usb_configuration *c)
+static int acm_function_bind_config(struct android_usb_function *f,
+					struct usb_configuration *c)
 {
-	int i;
-	int ret = 0;
-	struct acm_function_config *config = f->config;
+	char *name;
+	char buf[32], *b;
+	int err = -1, i;
+	static int acm_initialized, ports;
 
-	for (i = 0; i < config->instances; i++) {
-		ret = acm_bind_config(c, i);
-		if (ret) {
-			pr_err("Could not bind acm%u config\n", i);
-			break;
+	if (acm_initialized)
+		goto bind_config;
+
+	acm_initialized = 1;
+	strlcpy(buf, acm_transports, sizeof(buf));
+	b = strim(buf);
+
+	while (b) {
+		name = strsep(&b, ",");
+
+		if (name) {
+			err = acm_init_port(ports, name);
+			if (err) {
+				pr_err("acm: Cannot open port '%s'", name);
+				goto out;
+			}
+			ports++;
+		}
+	}
+	err = acm_port_setup(c);
+	if (err) {
+		pr_err("acm: Cannot setup transports");
+		goto out;
+	}
+
+bind_config:
+	for (i = 0; i < ports; i++) {
+		err = acm_bind_config(c, i);
+		if (err) {
+			pr_err("acm: bind_config failed for port %d", i);
+			goto out;
 		}
 	}
 
-	return ret;
+out:
+	return err;
 }
-
-static ssize_t acm_instances_show(struct device *dev,
-		struct device_attribute *attr, char *buf)
-{
-	struct android_usb_function *f = dev_get_drvdata(dev);
-	struct acm_function_config *config = f->config;
-	return sprintf(buf, "%d\n", config->instances);
-}
-
-static ssize_t acm_instances_store(struct device *dev,
-		struct device_attribute *attr, const char *buf, size_t size)
-{
-	struct android_usb_function *f = dev_get_drvdata(dev);
-	struct acm_function_config *config = f->config;
-	int value;
-
-	sscanf(buf, "%d", &value);
-	if (value > MAX_ACM_INSTANCES)
-		value = MAX_ACM_INSTANCES;
-	config->instances = value;
-	return size;
-}
-
-static DEVICE_ATTR(instances, S_IRUGO | S_IWUSR, acm_instances_show,
-						 acm_instances_store);
-static struct device_attribute *acm_function_attributes[] = {
-	&dev_attr_instances,
-	NULL
-};
-
 static struct android_usb_function acm_function = {
 	.name		= "acm",
-	.init		= acm_function_init,
 	.cleanup	= acm_function_cleanup,
 	.bind_config	= acm_function_bind_config,
 	.attributes	= acm_function_attributes,
 };
 
+/* CCID */
+static int ccid_function_init(struct android_usb_function *f,
+					struct usb_composite_dev *cdev)
+{
+	return ccid_setup();
+}
 
-static int
-mtp_function_init(struct android_usb_function *f,
+static void ccid_function_cleanup(struct android_usb_function *f)
+{
+	ccid_cleanup();
+}
+
+static int ccid_function_bind_config(struct android_usb_function *f,
+						struct usb_configuration *c)
+{
+	return ccid_bind_config(c);
+}
+
+static struct android_usb_function ccid_function = {
+	.name		= "ccid",
+	.init		= ccid_function_init,
+	.cleanup	= ccid_function_cleanup,
+	.bind_config	= ccid_function_bind_config,
+};
+
+static int mtp_function_init(struct android_usb_function *f,
 		struct usb_composite_dev *cdev)
 {
 	return mtp_setup();
@@ -403,16 +819,13 @@
 	mtp_cleanup();
 }
 
-static int
-mtp_function_bind_config(struct android_usb_function *f,
+static int mtp_function_bind_config(struct android_usb_function *f,
 		struct usb_configuration *c)
 {
 	return mtp_bind_config(c, false);
 }
 
-static int
-ptp_function_init(struct android_usb_function *f,
-		struct usb_composite_dev *cdev)
+static int ptp_function_init(struct android_usb_function *f, struct usb_composite_dev *cdev)
 {
 	/* nothing to do - initialization is handled by mtp_function_init */
 	return 0;
@@ -423,9 +836,7 @@
 	/* nothing to do - cleanup is handled by mtp_function_cleanup */
 }
 
-static int
-ptp_function_bind_config(struct android_usb_function *f,
-		struct usb_configuration *c)
+static int ptp_function_bind_config(struct android_usb_function *f, struct usb_configuration *c)
 {
 	return mtp_bind_config(c, true);
 }
@@ -527,7 +938,8 @@
 {
 	struct android_usb_function *f = dev_get_drvdata(dev);
 	struct rndis_function_config *config = f->config;
-	return sprintf(buf, "%s\n", config->manufacturer);
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", config->manufacturer);
 }
 
 static ssize_t rndis_manufacturer_store(struct device *dev,
@@ -538,7 +950,8 @@
 
 	if (size >= sizeof(config->manufacturer))
 		return -EINVAL;
-	if (sscanf(buf, "%s", config->manufacturer) == 1)
+
+	if (sscanf(buf, "%255s", config->manufacturer) == 1)
 		return size;
 	return -1;
 }
@@ -551,7 +964,8 @@
 {
 	struct android_usb_function *f = dev_get_drvdata(dev);
 	struct rndis_function_config *config = f->config;
-	return sprintf(buf, "%d\n", config->wceis);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", config->wceis);
 }
 
 static ssize_t rndis_wceis_store(struct device *dev,
@@ -576,7 +990,8 @@
 {
 	struct android_usb_function *f = dev_get_drvdata(dev);
 	struct rndis_function_config *rndis = f->config;
-	return sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n",
+
+	return snprintf(buf, PAGE_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x\n",
 		rndis->ethaddr[0], rndis->ethaddr[1], rndis->ethaddr[2],
 		rndis->ethaddr[3], rndis->ethaddr[4], rndis->ethaddr[5]);
 }
@@ -603,7 +1018,8 @@
 {
 	struct android_usb_function *f = dev_get_drvdata(dev);
 	struct rndis_function_config *config = f->config;
-	return sprintf(buf, "%04x\n", config->vendorID);
+
+	return snprintf(buf, PAGE_SIZE, "%04x\n", config->vendorID);
 }
 
 static ssize_t rndis_vendorID_store(struct device *dev,
@@ -671,6 +1087,7 @@
 				&common->luns[0].dev.kobj,
 				"lun");
 	if (err) {
+		fsg_common_release(&common->ref);
 		kfree(config);
 		return err;
 	}
@@ -698,7 +1115,7 @@
 {
 	struct android_usb_function *f = dev_get_drvdata(dev);
 	struct mass_storage_function_config *config = f->config;
-	return sprintf(buf, "%s\n", config->common->inquiry_string);
+	return snprintf(buf, PAGE_SIZE, "%s\n", config->common->inquiry_string);
 }
 
 static ssize_t mass_storage_inquiry_store(struct device *dev,
@@ -708,7 +1125,7 @@
 	struct mass_storage_function_config *config = f->config;
 	if (size >= sizeof(config->common->inquiry_string))
 		return -EINVAL;
-	if (sscanf(buf, "%s", config->common->inquiry_string) != 1)
+	if (sscanf(buf, "%28s", config->common->inquiry_string) != 1)
 		return -EINVAL;
 	return size;
 }
@@ -765,7 +1182,15 @@
 
 
 static struct android_usb_function *supported_functions[] = {
+	&mbim_function,
+	&rmnet_smd_function,
+	&rmnet_sdio_function,
+	&rmnet_smd_sdio_function,
+	&rmnet_function,
+	&diag_function,
+	&serial_function,
 	&adb_function,
+	&ccid_function,
 	&acm_function,
 	&mtp_function,
 	&ptp_function,
@@ -775,6 +1200,31 @@
 	NULL
 };
 
+static void android_cleanup_functions(struct android_usb_function **functions)
+{
+	struct android_usb_function *f;
+	struct device_attribute **attrs;
+	struct device_attribute *attr;
+
+	while (*functions) {
+		f = *functions++;
+
+		if (f->dev) {
+			device_destroy(android_class, f->dev->devt);
+			kfree(f->dev_name);
+		} else
+			continue;
+
+		if (f->cleanup)
+			f->cleanup(f);
+
+		attrs = f->attributes;
+		if (attrs) {
+			while ((attr = *attrs++))
+				device_remove_file(f->dev, attr);
+		}
+	}
+}
 
 static int android_init_functions(struct android_usb_function **functions,
 				  struct usb_composite_dev *cdev)
@@ -783,17 +1233,22 @@
 	struct android_usb_function *f;
 	struct device_attribute **attrs;
 	struct device_attribute *attr;
-	int err;
-	int index = 0;
+	int err = 0;
+	int index = 1; /* index 0 is for android0 device */
 
 	for (; (f = *functions++); index++) {
 		f->dev_name = kasprintf(GFP_KERNEL, "f_%s", f->name);
+		if (!f->dev_name) {
+			err = -ENOMEM;
+			goto err_out;
+		}
 		f->dev = device_create(android_class, dev->dev,
 				MKDEV(0, index), f, f->dev_name);
 		if (IS_ERR(f->dev)) {
 			pr_err("%s: Failed to create dev %s", __func__,
 							f->dev_name);
 			err = PTR_ERR(f->dev);
+			f->dev = NULL;
 			goto err_create;
 		}
 
@@ -802,7 +1257,7 @@
 			if (err) {
 				pr_err("%s: Failed to init %s", __func__,
 								f->name);
-				goto err_out;
+				goto err_init;
 			}
 		}
 
@@ -814,35 +1269,26 @@
 		if (err) {
 			pr_err("%s: Failed to create function %s attributes",
 					__func__, f->name);
-			goto err_out;
+			goto err_attrs;
 		}
 	}
 	return 0;
 
-err_out:
+err_attrs:
+	for (attr = *(attrs -= 2); attrs != f->attributes; attr = *(attrs--))
+		device_remove_file(f->dev, attr);
+	if (f->cleanup)
+		f->cleanup(f);
+err_init:
 	device_destroy(android_class, f->dev->devt);
 err_create:
+	f->dev = NULL;
 	kfree(f->dev_name);
+err_out:
+	android_cleanup_functions(dev->functions);
 	return err;
 }
 
-static void android_cleanup_functions(struct android_usb_function **functions)
-{
-	struct android_usb_function *f;
-
-	while (*functions) {
-		f = *functions++;
-
-		if (f->dev) {
-			device_destroy(android_class, f->dev->devt);
-			kfree(f->dev_name);
-		}
-
-		if (f->cleanup)
-			f->cleanup(f);
-	}
-}
-
 static int
 android_bind_enabled_functions(struct android_dev *dev,
 			       struct usb_configuration *c)
@@ -889,6 +1335,32 @@
 /*-------------------------------------------------------------------------*/
 /* /sys/class/android_usb/android%d/ interface */
 
+static ssize_t remote_wakeup_show(struct device *pdev,
+		struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			!!(android_config_driver.bmAttributes &
+				USB_CONFIG_ATT_WAKEUP));
+}
+
+static ssize_t remote_wakeup_store(struct device *pdev,
+		struct device_attribute *attr, const char *buff, size_t size)
+{
+	int enable = 0;
+
+	sscanf(buff, "%d", &enable);
+
+	pr_debug("android_usb: %s remote wakeup\n",
+			enable ? "enabling" : "disabling");
+
+	if (enable)
+		android_config_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+	else
+		android_config_driver.bmAttributes &= ~USB_CONFIG_ATT_WAKEUP;
+
+	return size;
+}
+
 static ssize_t
 functions_show(struct device *pdev, struct device_attribute *attr, char *buf)
 {
@@ -899,7 +1371,7 @@
 	mutex_lock(&dev->mutex);
 
 	list_for_each_entry(f, &dev->enabled_functions, enabled_list)
-		buff += sprintf(buff, "%s,", f->name);
+		buff += snprintf(buff, PAGE_SIZE, "%s,", f->name);
 
 	mutex_unlock(&dev->mutex);
 
@@ -926,7 +1398,7 @@
 
 	INIT_LIST_HEAD(&dev->enabled_functions);
 
-	strncpy(buf, buff, sizeof(buf));
+	strlcpy(buf, buff, sizeof(buf));
 	b = strim(buf);
 
 	while (b) {
@@ -947,7 +1419,8 @@
 			   char *buf)
 {
 	struct android_dev *dev = dev_get_drvdata(pdev);
-	return sprintf(buf, "%d\n", dev->enabled);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", dev->enabled);
 }
 
 static ssize_t enable_store(struct device *pdev, struct device_attribute *attr,
@@ -958,7 +1431,6 @@
 	struct android_usb_function *f;
 	int enabled = 0;
 
-
 	if (!cdev)
 		return -ENODEV;
 
@@ -996,6 +1468,26 @@
 	}
 
 	mutex_unlock(&dev->mutex);
+
+	return size;
+}
+
+static ssize_t pm_qos_show(struct device *pdev,
+			   struct device_attribute *attr, char *buf)
+{
+	struct android_dev *dev = dev_get_drvdata(pdev);
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", dev->pm_qos);
+}
+
+static ssize_t pm_qos_store(struct device *pdev,
+			   struct device_attribute *attr,
+			   const char *buff, size_t size)
+{
+	struct android_dev *dev = dev_get_drvdata(pdev);
+
+	strlcpy(dev->pm_qos, buff, sizeof(dev->pm_qos));
+
 	return size;
 }
 
@@ -1017,7 +1509,7 @@
 		state = "CONNECTED";
 	spin_unlock_irqrestore(&cdev->lock, flags);
 out:
-	return sprintf(buf, "%s\n", state);
+	return snprintf(buf, PAGE_SIZE, "%s\n", state);
 }
 
 #define DESCRIPTOR_ATTR(field, format_string)				\
@@ -1025,7 +1517,8 @@
 field ## _show(struct device *dev, struct device_attribute *attr,	\
 		char *buf)						\
 {									\
-	return sprintf(buf, format_string, device_desc.field);		\
+	return snprintf(buf, PAGE_SIZE,					\
+			format_string, device_desc.field);		\
 }									\
 static ssize_t								\
 field ## _store(struct device *dev, struct device_attribute *attr,	\
@@ -1045,7 +1538,7 @@
 field ## _show(struct device *dev, struct device_attribute *attr,	\
 		char *buf)						\
 {									\
-	return sprintf(buf, "%s", buffer);				\
+	return snprintf(buf, PAGE_SIZE, "%s", buffer);			\
 }									\
 static ssize_t								\
 field ## _store(struct device *dev, struct device_attribute *attr,	\
@@ -1071,7 +1564,11 @@
 static DEVICE_ATTR(functions, S_IRUGO | S_IWUSR, functions_show,
 						 functions_store);
 static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, enable_show, enable_store);
+static DEVICE_ATTR(pm_qos, S_IRUGO | S_IWUSR,
+		pm_qos_show, pm_qos_store);
 static DEVICE_ATTR(state, S_IRUGO, state_show, NULL);
+static DEVICE_ATTR(remote_wakeup, S_IRUGO | S_IWUSR,
+		remote_wakeup_show, remote_wakeup_store);
 
 static struct device_attribute *android_usb_attributes[] = {
 	&dev_attr_idVendor,
@@ -1085,7 +1582,9 @@
 	&dev_attr_iSerial,
 	&dev_attr_functions,
 	&dev_attr_enable,
+	&dev_attr_pm_qos,
 	&dev_attr_state,
+	&dev_attr_remote_wakeup,
 	NULL
 };
 
@@ -1143,9 +1642,10 @@
 	device_desc.iProduct = id;
 
 	/* Default strings - should be updated by userspace */
-	strncpy(manufacturer_string, "Android", sizeof(manufacturer_string)-1);
-	strncpy(product_string, "Android", sizeof(product_string) - 1);
-	strncpy(serial_string, "0123456789ABCDEF", sizeof(serial_string) - 1);
+	strlcpy(manufacturer_string, "Android",
+		sizeof(manufacturer_string) - 1);
+	strlcpy(product_string, "Android", sizeof(product_string) - 1);
+	strlcpy(serial_string, "0123456789ABCDEF", sizeof(serial_string) - 1);
 
 	id = usb_string_id(cdev);
 	if (id < 0)
@@ -1153,6 +1653,9 @@
 	strings_dev[STRING_SERIAL_IDX].id = id;
 	device_desc.iSerialNumber = id;
 
+	if (gadget_is_otg(cdev->gadget))
+		android_config_driver.descriptors = otg_desc;
+
 	gcnum = usb_gadget_controller_number(gadget);
 	if (gcnum >= 0)
 		device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
@@ -1162,7 +1665,6 @@
 		device_desc.bcdDevice = __constant_cpu_to_le16(0x9999);
 	}
 
-	usb_gadget_set_selfpowered(gadget);
 	dev->cdev = cdev;
 
 	return 0;
@@ -1172,6 +1674,9 @@
 {
 	struct android_dev *dev = _android_dev;
 
+	manufacturer_string[0] = '\0';
+	product_string[0] = '\0';
+	serial_string[0] = '0';
 	cancel_work_sync(&dev->work);
 	android_cleanup_functions(dev->functions);
 	return 0;
@@ -1182,7 +1687,7 @@
 	.dev		= &device_desc,
 	.strings	= dev_strings,
 	.unbind		= android_usb_unbind,
-	.max_speed	= USB_SPEED_HIGH,
+	.max_speed	= USB_SPEED_SUPER
 };
 
 static int
@@ -1267,19 +1772,86 @@
 	return 0;
 }
 
-
-static int __init init(void)
+static void android_destroy_device(struct android_dev *dev)
 {
-	struct android_dev *dev;
-	int err;
+	struct device_attribute **attrs = android_usb_attributes;
+	struct device_attribute *attr;
+
+	while ((attr = *attrs++))
+		device_remove_file(dev->dev, attr);
+	device_destroy(android_class, dev->dev->devt);
+}
+
+static int __devinit android_probe(struct platform_device *pdev)
+{
+	struct android_usb_platform_data *pdata = pdev->dev.platform_data;
+	struct android_dev *dev = _android_dev;
+	int ret = 0;
+
+	dev->pdata = pdata;
 
 	android_class = class_create(THIS_MODULE, "android_usb");
 	if (IS_ERR(android_class))
 		return PTR_ERR(android_class);
 
+	ret = android_create_device(dev);
+	if (ret) {
+		pr_err("%s(): android_create_device failed\n", __func__);
+		goto err_dev;
+	}
+
+	ret = usb_composite_probe(&android_usb_driver, android_bind);
+	if (ret) {
+		pr_err("%s(): Failed to register android "
+				 "composite driver\n", __func__);
+		goto err_probe;
+	}
+
+	/* pm qos request to prevent apps idle power collapse */
+	if (pdata && pdata->swfi_latency)
+		pm_qos_add_request(&dev->pm_qos_req_dma,
+			PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);
+	strlcpy(dev->pm_qos, "high", sizeof(dev->pm_qos));
+
+	return ret;
+err_probe:
+	android_destroy_device(dev);
+err_dev:
+	class_destroy(android_class);
+	return ret;
+}
+
+static int android_remove(struct platform_device *pdev)
+{
+	struct android_dev *dev = _android_dev;
+	struct android_usb_platform_data *pdata = pdev->dev.platform_data;
+
+	android_destroy_device(dev);
+	class_destroy(android_class);
+	usb_composite_unregister(&android_usb_driver);
+	if (pdata && pdata->swfi_latency)
+		pm_qos_remove_request(&dev->pm_qos_req_dma);
+
+	return 0;
+}
+
+static struct platform_driver android_platform_driver = {
+	.driver = { .name = "android_usb"},
+	.probe = android_probe,
+	.remove = android_remove,
+};
+
+static int __init init(void)
+{
+	struct android_dev *dev;
+	int ret;
+
 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
-	if (!dev)
+	if (!dev) {
+		pr_err("%s(): Failed to alloc memory for android_dev\n",
+				__func__);
 		return -ENOMEM;
+	}
 
 	dev->disable_depth = 1;
 	dev->functions = supported_functions;
@@ -1287,27 +1859,26 @@
 	INIT_WORK(&dev->work, android_work);
 	mutex_init(&dev->mutex);
 
-	err = android_create_device(dev);
-	if (err) {
-		class_destroy(android_class);
-		kfree(dev);
-		return err;
-	}
-
 	_android_dev = dev;
 
 	/* Override composite driver functions */
 	composite_driver.setup = android_setup;
 	composite_driver.disconnect = android_disconnect;
 
-	return usb_composite_probe(&android_usb_driver, android_bind);
+	ret = platform_driver_register(&android_platform_driver);
+	if (ret) {
+		pr_err("%s(): Failed to register android"
+				 "platform driver\n", __func__);
+		kfree(dev);
+	}
+
+	return ret;
 }
 module_init(init);
 
 static void __exit cleanup(void)
 {
-	usb_composite_unregister(&android_usb_driver);
-	class_destroy(android_class);
+	platform_driver_unregister(&android_platform_driver);
 	kfree(_android_dev);
 	_android_dev = NULL;
 }
diff --git a/drivers/usb/gadget/ci13xxx_msm.c b/drivers/usb/gadget/ci13xxx_msm.c
index d07e44c..1cbaa8e 100644
--- a/drivers/usb/gadget/ci13xxx_msm.c
+++ b/drivers/usb/gadget/ci13xxx_msm.c
@@ -1,8 +1,14 @@
-/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
  * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
  */
 
 #include <linux/module.h>
@@ -10,59 +16,153 @@
 #include <linux/pm_runtime.h>
 #include <linux/usb/msm_hsusb_hw.h>
 #include <linux/usb/ulpi.h>
+#include <linux/gpio.h>
 
 #include "ci13xxx_udc.c"
 
 #define MSM_USB_BASE	(udc->regs)
 
+struct ci13xxx_udc_context {
+	int irq;
+	void __iomem *regs;
+	int wake_gpio;
+	int wake_irq;
+	bool wake_irq_state;
+};
+
+static struct ci13xxx_udc_context _udc_ctxt;
+
 static irqreturn_t msm_udc_irq(int irq, void *data)
 {
 	return udc_irq();
 }
 
+static void ci13xxx_msm_suspend(void)
+{
+	struct device *dev = _udc->gadget.dev.parent;
+	dev_dbg(dev, "ci13xxx_msm_suspend\n");
+
+	if (_udc_ctxt.wake_irq && !_udc_ctxt.wake_irq_state) {
+		enable_irq_wake(_udc_ctxt.wake_irq);
+		enable_irq(_udc_ctxt.wake_irq);
+		_udc_ctxt.wake_irq_state = true;
+	}
+}
+
+static void ci13xxx_msm_resume(void)
+{
+	struct device *dev = _udc->gadget.dev.parent;
+	dev_dbg(dev, "ci13xxx_msm_resume\n");
+
+	if (_udc_ctxt.wake_irq && _udc_ctxt.wake_irq_state) {
+		disable_irq_wake(_udc_ctxt.wake_irq);
+		disable_irq(_udc_ctxt.wake_irq);
+		_udc_ctxt.wake_irq_state = false;
+	}
+}
+
 static void ci13xxx_msm_notify_event(struct ci13xxx *udc, unsigned event)
 {
 	struct device *dev = udc->gadget.dev.parent;
-	int val;
 
 	switch (event) {
 	case CI13XXX_CONTROLLER_RESET_EVENT:
-		dev_dbg(dev, "CI13XXX_CONTROLLER_RESET_EVENT received\n");
+		dev_info(dev, "CI13XXX_CONTROLLER_RESET_EVENT received\n");
 		writel(0, USB_AHBBURST);
-		writel(0, USB_AHBMODE);
+		writel_relaxed(0x08, USB_AHBMODE);
 		break;
-	case CI13XXX_CONTROLLER_STOPPED_EVENT:
-		dev_dbg(dev, "CI13XXX_CONTROLLER_STOPPED_EVENT received\n");
-		/*
-		 * Put the transceiver in non-driving mode. Otherwise host
-		 * may not detect soft-disconnection.
-		 */
-		val = usb_phy_io_read(udc->transceiver, ULPI_FUNC_CTRL);
-		val &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
-		val |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
-		usb_phy_io_write(udc->transceiver, val, ULPI_FUNC_CTRL);
+	case CI13XXX_CONTROLLER_DISCONNECT_EVENT:
+		dev_info(dev, "CI13XXX_CONTROLLER_DISCONNECT_EVENT received\n");
+		ci13xxx_msm_resume();
 		break;
+	case CI13XXX_CONTROLLER_SUSPEND_EVENT:
+		dev_info(dev, "CI13XXX_CONTROLLER_SUSPEND_EVENT received\n");
+		ci13xxx_msm_suspend();
+		break;
+	case CI13XXX_CONTROLLER_RESUME_EVENT:
+		dev_info(dev, "CI13XXX_CONTROLLER_RESUME_EVENT received\n");
+		ci13xxx_msm_resume();
+		break;
+
 	default:
 		dev_dbg(dev, "unknown ci13xxx_udc event\n");
 		break;
 	}
 }
 
+static irqreturn_t ci13xxx_msm_resume_irq(int irq, void *data)
+{
+	struct ci13xxx *udc = _udc;
+
+	if (udc->transceiver && udc->vbus_active && udc->suspended)
+		usb_phy_set_suspend(udc->transceiver, 0);
+	else if (!udc->suspended)
+		ci13xxx_msm_resume();
+
+	return IRQ_HANDLED;
+}
+
 static struct ci13xxx_udc_driver ci13xxx_msm_udc_driver = {
 	.name			= "ci13xxx_msm",
 	.flags			= CI13XXX_REGS_SHARED |
 				  CI13XXX_REQUIRE_TRANSCEIVER |
 				  CI13XXX_PULLUP_ON_VBUS |
-				  CI13XXX_DISABLE_STREAMING,
+				  CI13XXX_ZERO_ITC |
+				  CI13XXX_DISABLE_STREAMING |
+				  CI13XXX_IS_OTG,
 
 	.notify_event		= ci13xxx_msm_notify_event,
 };
 
+static int ci13xxx_msm_install_wake_gpio(struct platform_device *pdev,
+				struct resource *res)
+{
+	int wake_irq;
+	int ret;
+
+	dev_dbg(&pdev->dev, "ci13xxx_msm_install_wake_gpio\n");
+
+	_udc_ctxt.wake_gpio = res->start;
+	gpio_request(_udc_ctxt.wake_gpio, "USB_RESUME");
+	gpio_direction_input(_udc_ctxt.wake_gpio);
+	wake_irq = gpio_to_irq(_udc_ctxt.wake_gpio);
+	if (wake_irq < 0) {
+		dev_err(&pdev->dev, "could not register USB_RESUME GPIO.\n");
+		return -ENXIO;
+	}
+
+	dev_dbg(&pdev->dev, "_udc_ctxt.gpio_irq = %d and irq = %d\n",
+			_udc_ctxt.wake_gpio, wake_irq);
+	ret = request_irq(wake_irq, ci13xxx_msm_resume_irq,
+		IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "usb resume", NULL);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "could not register USB_RESUME IRQ.\n");
+		goto gpio_free;
+	}
+	disable_irq(wake_irq);
+	_udc_ctxt.wake_irq = wake_irq;
+
+	return 0;
+
+gpio_free:
+	gpio_free(_udc_ctxt.wake_gpio);
+	_udc_ctxt.wake_gpio = 0;
+	return ret;
+}
+
+static void ci13xxx_msm_uninstall_wake_gpio(struct platform_device *pdev)
+{
+	dev_dbg(&pdev->dev, "ci13xxx_msm_uninstall_wake_gpio\n");
+
+	if (_udc_ctxt.wake_gpio) {
+		gpio_free(_udc_ctxt.wake_gpio);
+		_udc_ctxt.wake_gpio = 0;
+	}
+}
+
 static int ci13xxx_msm_probe(struct platform_device *pdev)
 {
 	struct resource *res;
-	void __iomem *regs;
-	int irq;
 	int ret;
 
 	dev_dbg(&pdev->dev, "ci13xxx_msm_probe\n");
@@ -73,29 +173,39 @@
 		return -ENXIO;
 	}
 
-	regs = ioremap(res->start, resource_size(res));
-	if (!regs) {
+	_udc_ctxt.regs = ioremap(res->start, resource_size(res));
+	if (!_udc_ctxt.regs) {
 		dev_err(&pdev->dev, "ioremap failed\n");
 		return -ENOMEM;
 	}
 
-	ret = udc_probe(&ci13xxx_msm_udc_driver, &pdev->dev, regs);
+	ret = udc_probe(&ci13xxx_msm_udc_driver, &pdev->dev, _udc_ctxt.regs);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "udc_probe failed\n");
 		goto iounmap;
 	}
 
-	irq = platform_get_irq(pdev, 0);
-	if (irq < 0) {
+	_udc_ctxt.irq = platform_get_irq(pdev, 0);
+	if (_udc_ctxt.irq < 0) {
 		dev_err(&pdev->dev, "IRQ not found\n");
 		ret = -ENXIO;
 		goto udc_remove;
 	}
 
-	ret = request_irq(irq, msm_udc_irq, IRQF_SHARED, pdev->name, pdev);
+	res = platform_get_resource_byname(pdev, IORESOURCE_IO, "USB_RESUME");
+	if (res) {
+		ret = ci13xxx_msm_install_wake_gpio(pdev, res);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "gpio irq install failed\n");
+			goto udc_remove;
+		}
+	}
+
+	ret = request_irq(_udc_ctxt.irq, msm_udc_irq, IRQF_SHARED, pdev->name,
+					  pdev);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "request_irq failed\n");
-		goto udc_remove;
+		goto gpio_uninstall;
 	}
 
 	pm_runtime_no_callbacks(&pdev->dev);
@@ -103,17 +213,30 @@
 
 	return 0;
 
+gpio_uninstall:
+	ci13xxx_msm_uninstall_wake_gpio(pdev);
 udc_remove:
 	udc_remove();
 iounmap:
-	iounmap(regs);
+	iounmap(_udc_ctxt.regs);
 
 	return ret;
 }
 
+int ci13xxx_msm_remove(struct platform_device *pdev)
+{
+	pm_runtime_disable(&pdev->dev);
+	free_irq(_udc_ctxt.irq, pdev);
+	ci13xxx_msm_uninstall_wake_gpio(pdev);
+	udc_remove();
+	iounmap(_udc_ctxt.regs);
+	return 0;
+}
+
 static struct platform_driver ci13xxx_msm_driver = {
 	.probe = ci13xxx_msm_probe,
 	.driver = { .name = "msm_hsusb", },
+	.remove = ci13xxx_msm_remove,
 };
 MODULE_ALIAS("platform:msm_hsusb");
 
@@ -123,4 +246,10 @@
 }
 module_init(ci13xxx_msm_init);
 
+static void __exit ci13xxx_msm_exit(void)
+{
+	platform_driver_unregister(&ci13xxx_msm_driver);
+}
+module_exit(ci13xxx_msm_exit);
+
 MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/gadget/ci13xxx_msm_hsic.c b/drivers/usb/gadget/ci13xxx_msm_hsic.c
new file mode 100644
index 0000000..39d4720
--- /dev/null
+++ b/drivers/usb/gadget/ci13xxx_msm_hsic.c
@@ -0,0 +1,794 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/wakelock.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/usb.h>
+
+#include <linux/usb/gadget.h>
+#include <linux/usb/msm_hsusb_hw.h>
+#include <linux/usb/msm_hsusb.h>
+
+#include <mach/clk.h>
+#include <mach/msm_iomap.h>
+#include <mach/msm_xo.h>
+
+#include "ci13xxx_udc.c"
+
+#define MSM_USB_BASE	(mhsic->regs)
+
+#define ULPI_IO_TIMEOUT_USEC			(10 * 1000)
+#define USB_PHY_VDD_DIG_VOL_SUSP_MIN	500000 /* uV */
+#define USB_PHY_VDD_DIG_VOL_MIN			1045000 /* uV */
+#define USB_PHY_VDD_DIG_VOL_MAX			1320000 /* uV */
+#define USB_PHY_VDD_DIG_LOAD			49360	/* uA */
+#define LINK_RESET_TIMEOUT_USEC			(250 * 1000)
+#define PHY_SUSPEND_TIMEOUT_USEC		(500 * 1000)
+#define PHY_RESUME_TIMEOUT_USEC			(100 * 1000)
+#define HSIC_CFG_REG					0x30
+#define HSIC_IO_CAL_PER_REG				0x33
+#define HSIC_DBG1_REG					0x38
+
+struct msm_hsic_per *the_mhsic;
+
+struct msm_hsic_per {
+	struct device		*dev;
+	struct clk			*iface_clk;
+	struct clk			*core_clk;
+	struct clk			*alt_core_clk;
+	struct clk			*phy_clk;
+	struct clk			*cal_clk;
+	struct regulator	*hsic_vddcx;
+	bool				async_int;
+	void __iomem		*regs;
+	int					irq;
+	atomic_t			in_lpm;
+	struct wake_lock	wlock;
+	struct msm_xo_voter	*xo_handle;
+	struct workqueue_struct *wq;
+	struct work_struct	suspend_w;
+	struct msm_hsic_peripheral_platform_data *pdata;
+};
+
+static int msm_hsic_init_vddcx(struct msm_hsic_per *mhsic, int init)
+{
+	int ret = 0;
+
+	if (!init)
+		goto disable_reg;
+
+	mhsic->hsic_vddcx = regulator_get(mhsic->dev, "HSIC_VDDCX");
+	if (IS_ERR(mhsic->hsic_vddcx)) {
+		dev_err(mhsic->dev, "unable to get hsic vddcx\n");
+		return PTR_ERR(mhsic->hsic_vddcx);
+	}
+
+	ret = regulator_set_voltage(mhsic->hsic_vddcx,
+			USB_PHY_VDD_DIG_VOL_MIN,
+			USB_PHY_VDD_DIG_VOL_MAX);
+	if (ret) {
+		dev_err(mhsic->dev, "unable to set the voltage"
+				"for hsic vddcx\n");
+		goto reg_set_voltage_err;
+	}
+
+	ret = regulator_set_optimum_mode(mhsic->hsic_vddcx,
+				USB_PHY_VDD_DIG_LOAD);
+	if (ret < 0) {
+		pr_err("%s: Unable to set optimum mode of the regulator:"
+					"VDDCX\n", __func__);
+		goto reg_optimum_mode_err;
+	}
+
+	ret = regulator_enable(mhsic->hsic_vddcx);
+	if (ret) {
+		dev_err(mhsic->dev, "unable to enable hsic vddcx\n");
+		goto reg_enable_err;
+	}
+
+	return 0;
+
+disable_reg:
+	regulator_disable(mhsic->hsic_vddcx);
+reg_enable_err:
+	regulator_set_optimum_mode(mhsic->hsic_vddcx, 0);
+reg_optimum_mode_err:
+	regulator_set_voltage(mhsic->hsic_vddcx, 0,
+				USB_PHY_VDD_DIG_VOL_MIN);
+reg_set_voltage_err:
+	regulator_put(mhsic->hsic_vddcx);
+
+	return ret;
+
+}
+
+static int ulpi_write(struct msm_hsic_per *mhsic, u32 val, u32 reg)
+{
+	int cnt = 0;
+
+	/* initiate write operation */
+	writel_relaxed(ULPI_RUN | ULPI_WRITE |
+	       ULPI_ADDR(reg) | ULPI_DATA(val),
+	       USB_ULPI_VIEWPORT);
+
+	/* wait for completion */
+	while (cnt < ULPI_IO_TIMEOUT_USEC) {
+		if (!(readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_RUN))
+			break;
+		udelay(1);
+		cnt++;
+	}
+
+	if (cnt >= ULPI_IO_TIMEOUT_USEC) {
+		dev_err(mhsic->dev, "ulpi_write: timeout\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static int msm_hsic_phy_clk_reset(struct msm_hsic_per *mhsic)
+{
+	int ret;
+
+	ret = clk_reset(mhsic->core_clk, CLK_RESET_ASSERT);
+	if (ret) {
+		clk_disable(mhsic->alt_core_clk);
+		dev_err(mhsic->dev, "usb phy clk assert failed\n");
+		return ret;
+	}
+	usleep_range(10000, 12000);
+	clk_disable(mhsic->alt_core_clk);
+
+	ret = clk_reset(mhsic->core_clk, CLK_RESET_DEASSERT);
+	if (ret)
+		dev_err(mhsic->dev, "usb phy clk deassert failed\n");
+
+	return ret;
+}
+
+static int msm_hsic_phy_reset(struct msm_hsic_per *mhsic)
+{
+	u32 val;
+	int ret;
+
+	ret = msm_hsic_phy_clk_reset(mhsic);
+	if (ret)
+		return ret;
+
+	val = readl_relaxed(USB_PORTSC) & ~PORTSC_PTS_MASK;
+	writel_relaxed(val | PORTSC_PTS_ULPI, USB_PORTSC);
+
+	/*
+	 * Ensure that RESET operation is completed before
+	 * turning off clock.
+	 */
+	mb();
+	dev_dbg(mhsic->dev, "phy_reset: success\n");
+
+	return 0;
+}
+
+static int msm_hsic_enable_clocks(struct platform_device *pdev,
+				struct msm_hsic_per *mhsic, bool enable)
+{
+	int ret = 0;
+
+	if (!enable)
+		goto put_clocks;
+
+	mhsic->iface_clk = clk_get(&pdev->dev, "iface_clk");
+	if (IS_ERR(mhsic->iface_clk)) {
+		dev_err(mhsic->dev, "failed to get iface_clk\n");
+		ret = PTR_ERR(mhsic->iface_clk);
+		goto put_iface_clk;
+	}
+
+	mhsic->core_clk = clk_get(&pdev->dev, "core_clk");
+	if (IS_ERR(mhsic->core_clk)) {
+		dev_err(mhsic->dev, "failed to get core_clk\n");
+		ret = PTR_ERR(mhsic->core_clk);
+		goto put_core_clk;
+	}
+
+	mhsic->phy_clk = clk_get(&pdev->dev, "phy_clk");
+	if (IS_ERR(mhsic->phy_clk)) {
+		dev_err(mhsic->dev, "failed to get phy_clk\n");
+		ret = PTR_ERR(mhsic->phy_clk);
+		goto put_phy_clk;
+	}
+
+	mhsic->alt_core_clk = clk_get(&pdev->dev, "alt_core_clk");
+	if (IS_ERR(mhsic->alt_core_clk)) {
+		dev_err(mhsic->dev, "failed to get alt_core_clk\n");
+		ret = PTR_ERR(mhsic->alt_core_clk);
+		goto put_alt_core_clk;
+	}
+
+	mhsic->cal_clk = clk_get(&pdev->dev, "cal_clk");
+	if (IS_ERR(mhsic->cal_clk)) {
+		dev_err(mhsic->dev, "failed to get cal_clk\n");
+		ret = PTR_ERR(mhsic->cal_clk);
+		goto put_cal_clk;
+	}
+
+	clk_enable(mhsic->iface_clk);
+	clk_enable(mhsic->core_clk);
+	clk_enable(mhsic->phy_clk);
+	clk_enable(mhsic->alt_core_clk);
+	clk_enable(mhsic->cal_clk);
+
+	return 0;
+
+put_clocks:
+	clk_disable(mhsic->iface_clk);
+	clk_disable(mhsic->core_clk);
+	clk_disable(mhsic->phy_clk);
+	clk_disable(mhsic->alt_core_clk);
+	clk_disable(mhsic->cal_clk);
+put_cal_clk:
+	clk_put(mhsic->cal_clk);
+put_alt_core_clk:
+	clk_put(mhsic->alt_core_clk);
+put_phy_clk:
+	clk_put(mhsic->phy_clk);
+put_core_clk:
+	clk_put(mhsic->core_clk);
+put_iface_clk:
+	clk_put(mhsic->iface_clk);
+
+	return ret;
+}
+
+static int msm_hsic_reset(struct msm_hsic_per *mhsic)
+{
+	int cnt = 0;
+	int ret;
+
+	ret = msm_hsic_phy_reset(mhsic);
+	if (ret) {
+		dev_err(mhsic->dev, "phy_reset failed\n");
+		return ret;
+	}
+
+	writel_relaxed(USBCMD_RESET, USB_USBCMD);
+	while (cnt < LINK_RESET_TIMEOUT_USEC) {
+		if (!(readl_relaxed(USB_USBCMD) & USBCMD_RESET))
+			break;
+		udelay(1);
+		cnt++;
+	}
+	if (cnt >= LINK_RESET_TIMEOUT_USEC)
+		return -ETIMEDOUT;
+
+	/* Reset PORTSC and select ULPI phy */
+	writel_relaxed(0x80000000, USB_PORTSC);
+	return 0;
+}
+
+static void msm_hsic_wakeup(void)
+{
+	if (atomic_read(&the_mhsic->in_lpm))
+		pm_runtime_resume(the_mhsic->dev);
+}
+
+static void msm_hsic_start(void)
+{
+	int ret;
+
+	/* programmable length of connect signaling (33.2ns) */
+	ret = ulpi_write(the_mhsic, 3, HSIC_DBG1_REG);
+	if (ret) {
+		pr_err("%s: Unable to program length of connect signaling\n",
+			    __func__);
+	}
+
+	/*set periodic calibration interval to ~2.048sec in HSIC_IO_CAL_REG */
+	ret = ulpi_write(the_mhsic, 0xFF, HSIC_IO_CAL_PER_REG);
+
+	if (ret) {
+		pr_err("%s: Unable to set periodic calibration interval\n",
+			    __func__);
+	}
+
+	/* Enable periodic IO calibration in HSIC_CFG register */
+	ret = ulpi_write(the_mhsic, 0xE9, HSIC_CFG_REG);
+	if (ret) {
+		pr_err("%s: Unable to enable periodic IO calibration\n",
+			    __func__);
+	}
+}
+
+
+#ifdef CONFIG_PM_SLEEP
+static int msm_hsic_suspend(struct msm_hsic_per *mhsic)
+{
+	int cnt = 0, ret;
+	u32 val;
+
+	if (atomic_read(&mhsic->in_lpm)) {
+		dev_dbg(mhsic->dev, "%s called while in lpm\n", __func__);
+		return 0;
+	}
+	disable_irq(mhsic->irq);
+
+	/*
+	 * PHY may take some time or even fail to enter into low power
+	 * mode (LPM). Hence poll for 500 msec and reset the PHY and link
+	 * in failure case.
+	 */
+	val = readl_relaxed(USB_PORTSC) | PORTSC_PHCD;
+	writel_relaxed(val, USB_PORTSC);
+
+	while (cnt < PHY_SUSPEND_TIMEOUT_USEC) {
+		if (readl_relaxed(USB_PORTSC) & PORTSC_PHCD)
+			break;
+		udelay(1);
+		cnt++;
+	}
+
+	if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) {
+		dev_err(mhsic->dev, "Unable to suspend PHY\n");
+		msm_hsic_reset(mhsic);
+	}
+
+	/*
+	 * PHY has capability to generate interrupt asynchronously in low
+	 * power mode (LPM). This interrupt is level triggered. So USB IRQ
+	 * line must be disabled till async interrupt enable bit is cleared
+	 * in USBCMD register. Assert STP (ULPI interface STOP signal) to
+	 * block data communication from PHY.
+	 */
+	writel_relaxed(readl_relaxed(USB_USBCMD) | ASYNC_INTR_CTRL |
+				ULPI_STP_CTRL, USB_USBCMD);
+
+	/*
+	 * Ensure that hardware is put in low power mode before
+	 * clocks are turned OFF and VDD is allowed to minimize.
+	 */
+	mb();
+
+	if (!mhsic->pdata->keep_core_clk_on_suspend_workaround) {
+		clk_disable(mhsic->iface_clk);
+		clk_disable(mhsic->core_clk);
+	}
+	clk_disable(mhsic->phy_clk);
+	clk_disable(mhsic->cal_clk);
+
+	ret = msm_xo_mode_vote(mhsic->xo_handle, MSM_XO_MODE_OFF);
+	if (ret)
+		dev_err(mhsic->dev, "%s failed to devote for TCXO %d\n"
+				, __func__, ret);
+
+	ret = regulator_set_voltage(mhsic->hsic_vddcx,
+				USB_PHY_VDD_DIG_VOL_SUSP_MIN,
+				USB_PHY_VDD_DIG_VOL_MAX);
+	if (ret < 0)
+		dev_err(mhsic->dev, "unable to set vddcx voltage: min:0.5v max:1.32v\n");
+
+	if (device_may_wakeup(mhsic->dev))
+		enable_irq_wake(mhsic->irq);
+
+	atomic_set(&mhsic->in_lpm, 1);
+	enable_irq(mhsic->irq);
+	wake_unlock(&mhsic->wlock);
+
+	dev_info(mhsic->dev, "HSIC-USB in low power mode\n");
+
+	return 0;
+}
+
+static int msm_hsic_resume(struct msm_hsic_per *mhsic)
+{
+	int cnt = 0, ret;
+	unsigned temp;
+
+	if (!atomic_read(&mhsic->in_lpm)) {
+		dev_dbg(mhsic->dev, "%s called while not in lpm\n", __func__);
+		return 0;
+	}
+
+	wake_lock(&mhsic->wlock);
+	ret = regulator_set_voltage(mhsic->hsic_vddcx,
+				USB_PHY_VDD_DIG_VOL_MIN,
+				USB_PHY_VDD_DIG_VOL_MAX);
+	if (ret < 0)
+		dev_err(mhsic->dev,
+				"unable to set vddcx voltage: min:1.045v max:1.32v\n");
+
+	ret = msm_xo_mode_vote(mhsic->xo_handle, MSM_XO_MODE_ON);
+	if (ret)
+		dev_err(mhsic->dev, "%s failed to vote for TCXO %d\n",
+				__func__, ret);
+
+	if (!mhsic->pdata->keep_core_clk_on_suspend_workaround) {
+		clk_enable(mhsic->iface_clk);
+		clk_enable(mhsic->core_clk);
+	}
+	clk_enable(mhsic->phy_clk);
+	clk_enable(mhsic->cal_clk);
+
+	temp = readl_relaxed(USB_USBCMD);
+	temp &= ~ASYNC_INTR_CTRL;
+	temp &= ~ULPI_STP_CTRL;
+	writel_relaxed(temp, USB_USBCMD);
+
+	if (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD))
+		goto skip_phy_resume;
+
+	temp = readl_relaxed(USB_PORTSC) & ~PORTSC_PHCD;
+	writel_relaxed(temp, USB_PORTSC);
+	while (cnt < PHY_RESUME_TIMEOUT_USEC) {
+		if (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD) &&
+			(readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_SYNC_STATE))
+			break;
+		udelay(1);
+		cnt++;
+	}
+
+	if (cnt >= PHY_RESUME_TIMEOUT_USEC) {
+		/*
+		 * This is a fatal error. Reset the link and
+		 * PHY to make hsic working.
+		 */
+		dev_err(mhsic->dev, "Unable to resume USB. Reset the hsic\n");
+		msm_hsic_reset(mhsic);
+	}
+skip_phy_resume:
+	if (device_may_wakeup(mhsic->dev))
+		disable_irq_wake(mhsic->irq);
+
+	atomic_set(&mhsic->in_lpm, 0);
+
+	if (mhsic->async_int) {
+		mhsic->async_int = false;
+		enable_irq(mhsic->irq);
+	}
+
+	dev_info(mhsic->dev, "HSIC-USB exited from low power mode\n");
+
+	return 0;
+}
+
+static int msm_hsic_pm_suspend(struct device *dev)
+{
+	struct msm_hsic_per *mhsic = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "MSM HSIC Peripheral PM suspend\n");
+
+	return msm_hsic_suspend(mhsic);
+}
+
+#ifdef CONFIG_PM_RUNTIME
+static int msm_hsic_pm_resume(struct device *dev)
+{
+	dev_dbg(dev, "MSM HSIC Peripheral PM resume\n");
+
+	/*
+	 * Do not resume hardware as part of system resume,
+	 * rather, wait for the ASYNC INT from the h/w
+	 */
+	return 0;
+}
+#else
+static int msm_hsic_pm_resume(struct device *dev)
+{
+	struct msm_hsic_per *mhsic = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "MSM HSIC Peripheral PM resume\n");
+
+	return msm_hsic_resume(mhsic);
+}
+#endif
+
+static void msm_hsic_pm_suspend_work(struct work_struct *w)
+{
+	pm_runtime_put_noidle(the_mhsic->dev);
+	pm_runtime_suspend(the_mhsic->dev);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+#ifdef CONFIG_PM_RUNTIME
+static int msm_hsic_runtime_idle(struct device *dev)
+{
+	dev_dbg(dev, "MSM HSIC Peripheral runtime idle\n");
+
+	return 0;
+}
+
+static int msm_hsic_runtime_suspend(struct device *dev)
+{
+	struct msm_hsic_per *mhsic = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "MSM HSIC Peripheral runtime suspend\n");
+
+	return msm_hsic_suspend(mhsic);
+}
+
+static int msm_hsic_runtime_resume(struct device *dev)
+{
+	struct msm_hsic_per *mhsic = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "MSM HSIC Peripheral runtime resume\n");
+	pm_runtime_get_noresume(mhsic->dev);
+
+	return msm_hsic_resume(mhsic);
+}
+#endif
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops msm_hsic_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(msm_hsic_pm_suspend, msm_hsic_pm_resume)
+	SET_RUNTIME_PM_OPS(msm_hsic_runtime_suspend, msm_hsic_runtime_resume,
+				msm_hsic_runtime_idle)
+};
+#endif
+
+/**
+ * Dummy match function - will be called only for HSIC msm
+ * device (msm_device_gadget_hsic_peripheral).
+ */
+static inline int __match(struct device *dev, void *data) { return 1; }
+
+static void msm_hsic_connect_peripheral(struct device *msm_udc_dev)
+{
+	struct device *dev;
+	struct usb_gadget *gadget;
+
+	dev = device_find_child(msm_udc_dev, NULL, __match);
+	gadget = dev_to_usb_gadget(dev);
+	usb_gadget_vbus_connect(gadget);
+}
+
+static irqreturn_t msm_udc_hsic_irq(int irq, void *data)
+{
+	struct msm_hsic_per *mhsic = data;
+
+	if (atomic_read(&mhsic->in_lpm)) {
+		disable_irq_nosync(mhsic->irq);
+		mhsic->async_int = true;
+		pm_request_resume(mhsic->dev);
+		return IRQ_HANDLED;
+	}
+
+	return udc_irq();
+}
+
+static void ci13xxx_msm_hsic_notify_event(struct ci13xxx *udc, unsigned event)
+{
+	struct device *dev = udc->gadget.dev.parent;
+	struct msm_hsic_per *mhsic = the_mhsic;
+
+	switch (event) {
+	case CI13XXX_CONTROLLER_RESET_EVENT:
+		dev_dbg(dev, "CI13XXX_CONTROLLER_RESET_EVENT received\n");
+		writel_relaxed(0, USB_AHBBURST);
+		writel_relaxed(0x08, USB_AHBMODE);
+		break;
+	case CI13XXX_CONTROLLER_CONNECT_EVENT:
+		dev_dbg(dev, "CI13XXX_CONTROLLER_CONNECT_EVENT received\n");
+		msm_hsic_start();
+		break;
+	case CI13XXX_CONTROLLER_SUSPEND_EVENT:
+		dev_dbg(dev, "CI13XXX_CONTROLLER_SUSPEND_EVENT received\n");
+		queue_work(mhsic->wq, &mhsic->suspend_w);
+		break;
+	case CI13XXX_CONTROLLER_REMOTE_WAKEUP_EVENT:
+		dev_dbg(dev, "CI13XXX_CONTROLLER_REMOTE_WAKEUP_EVENT received\n");
+		msm_hsic_wakeup();
+		break;
+	default:
+		dev_dbg(dev, "unknown ci13xxx_udc event\n");
+		break;
+	}
+}
+
+static struct ci13xxx_udc_driver ci13xxx_msm_udc_hsic_driver = {
+	.name			= "ci13xxx_msm_hsic",
+	.flags			= CI13XXX_REGS_SHARED |
+				  CI13XXX_PULLUP_ON_VBUS |
+				  CI13XXX_DISABLE_STREAMING |
+				  CI13XXX_ZERO_ITC,
+
+	.notify_event		= ci13xxx_msm_hsic_notify_event,
+};
+
+static int __devinit msm_hsic_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct msm_hsic_per *mhsic;
+	int ret = 0;
+	struct msm_hsic_peripheral_platform_data *pdata;
+
+	dev_dbg(&pdev->dev, "msm-hsic probe\n");
+
+	if (!pdev->dev.platform_data) {
+		dev_err(&pdev->dev, "No platform data given. Bailing out\n");
+		return -ENODEV;
+	} else {
+		pdata = pdev->dev.platform_data;
+	}
+
+	mhsic = kzalloc(sizeof(struct msm_hsic_per), GFP_KERNEL);
+	if (!mhsic) {
+		dev_err(&pdev->dev, "unable to allocate msm_hsic\n");
+		return -ENOMEM;
+	}
+	the_mhsic = mhsic;
+	platform_set_drvdata(pdev, mhsic);
+	mhsic->dev = &pdev->dev;
+	mhsic->pdata = pdata;
+
+	mhsic->irq = platform_get_irq(pdev, 0);
+	if (mhsic->irq < 0) {
+		dev_err(&pdev->dev, "Unable to get IRQ resource\n");
+		ret = mhsic->irq;
+		goto error;
+	}
+
+	mhsic->wq = alloc_workqueue("mhsic_wq", WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
+	if (!mhsic->wq) {
+		pr_err("%s: Unable to create workqueue mhsic wq\n",
+				__func__);
+		ret = -ENOMEM;
+		goto error;
+	}
+	INIT_WORK(&mhsic->suspend_w, msm_hsic_pm_suspend_work);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "Unable to get memory resource\n");
+		ret = -ENODEV;
+		goto error;
+	}
+	mhsic->regs = ioremap(res->start, resource_size(res));
+	if (!mhsic->regs) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		ret = -ENOMEM;
+		goto unmap;
+	}
+	dev_info(&pdev->dev, "HSIC Peripheral regs = %p\n", mhsic->regs);
+
+	mhsic->xo_handle = msm_xo_get(MSM_XO_TCXO_D0, "hsic_peripheral");
+	if (IS_ERR(mhsic->xo_handle)) {
+		dev_err(&pdev->dev, "%s not able to get the handle "
+			"to vote for TCXO\n", __func__);
+		ret = PTR_ERR(mhsic->xo_handle);
+		goto unmap;
+	}
+
+	ret = msm_xo_mode_vote(mhsic->xo_handle, MSM_XO_MODE_ON);
+	if (ret) {
+		dev_err(&pdev->dev, "%s failed to vote for TCXO %d\n",
+				__func__, ret);
+		goto free_xo_handle;
+	}
+
+	ret = msm_hsic_enable_clocks(pdev, mhsic, true);
+
+	if (ret) {
+		dev_err(&pdev->dev, "msm_hsic_enable_clocks failed\n");
+		ret = -ENODEV;
+		goto deinit_clocks;
+	}
+	ret = msm_hsic_init_vddcx(mhsic, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to initialize VDDCX\n");
+		ret = -ENODEV;
+		goto deinit_vddcx;
+	}
+
+	ret = msm_hsic_reset(mhsic);
+	if (ret) {
+		dev_err(&pdev->dev, "msm_hsic_reset failed\n");
+		ret = -ENODEV;
+		goto deinit_vddcx;
+	}
+
+	ret = udc_probe(&ci13xxx_msm_udc_hsic_driver, &pdev->dev, mhsic->regs);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "udc_probe failed\n");
+		ret = -ENODEV;
+		goto deinit_vddcx;
+	}
+
+	msm_hsic_connect_peripheral(&pdev->dev);
+
+	device_init_wakeup(&pdev->dev, 1);
+	wake_lock_init(&mhsic->wlock, WAKE_LOCK_SUSPEND, dev_name(&pdev->dev));
+	wake_lock(&mhsic->wlock);
+
+	ret = request_irq(mhsic->irq, msm_udc_hsic_irq,
+					  IRQF_SHARED, pdev->name, mhsic);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "request_irq failed\n");
+		ret = -ENODEV;
+		goto udc_remove;
+	}
+
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+	pm_runtime_get_sync(&pdev->dev);
+
+	return 0;
+udc_remove:
+	udc_remove();
+deinit_vddcx:
+	msm_hsic_init_vddcx(mhsic, 0);
+deinit_clocks:
+	msm_hsic_enable_clocks(pdev, mhsic, 0);
+	msm_xo_mode_vote(mhsic->xo_handle, MSM_XO_MODE_OFF);
+free_xo_handle:
+	msm_xo_put(mhsic->xo_handle);
+unmap:
+	iounmap(mhsic->regs);
+error:
+	destroy_workqueue(mhsic->wq);
+	kfree(mhsic);
+	return ret;
+}
+
+static int __devexit hsic_msm_remove(struct platform_device *pdev)
+{
+	struct msm_hsic_per *mhsic = platform_get_drvdata(pdev);
+
+	device_init_wakeup(&pdev->dev, 0);
+	pm_runtime_disable(&pdev->dev);
+	pm_runtime_set_suspended(&pdev->dev);
+
+	msm_hsic_init_vddcx(mhsic, 0);
+	msm_hsic_enable_clocks(pdev, mhsic, 0);
+	msm_xo_put(mhsic->xo_handle);
+	wake_lock_destroy(&mhsic->wlock);
+	destroy_workqueue(mhsic->wq);
+	udc_remove();
+	iounmap(mhsic->regs);
+	kfree(mhsic);
+
+	return 0;
+}
+
+static struct platform_driver msm_hsic_peripheral_driver = {
+	.probe	= msm_hsic_probe,
+	.remove	= __devexit_p(hsic_msm_remove),
+	.driver = {
+		.name = "msm_hsic_peripheral",
+#ifdef CONFIG_PM
+		.pm = &msm_hsic_dev_pm_ops,
+#endif
+	},
+};
+
+static int __init msm_hsic_peripheral_init(void)
+{
+	return platform_driver_probe(&msm_hsic_peripheral_driver,
+								msm_hsic_probe);
+}
+
+static void __exit msm_hsic_peripheral_exit(void)
+{
+	platform_driver_unregister(&msm_hsic_peripheral_driver);
+}
+
+module_init(msm_hsic_peripheral_init);
+module_exit(msm_hsic_peripheral_exit);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c
index 243ef1a..466fa15 100644
--- a/drivers/usb/gadget/ci13xxx_udc.c
+++ b/drivers/usb/gadget/ci13xxx_udc.c
@@ -60,10 +60,12 @@
 #include <linux/irq.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
+#include <linux/module.h>
 #include <linux/pm_runtime.h>
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
 #include <linux/usb/otg.h>
+#include <linux/usb/msm_hsusb.h>
 
 #include "ci13xxx_udc.h"
 
@@ -158,6 +160,7 @@
 #define CAP_ENDPTLISTADDR   (0x018UL)
 #define CAP_PORTSC          (0x044UL)
 #define CAP_DEVLC           (0x084UL)
+#define CAP_ENDPTPIPEID     (0x0BCUL)
 #define CAP_USBMODE         (hw_bank.lpm ? 0x0C8UL : 0x068UL)
 #define CAP_ENDPTSETUPSTAT  (hw_bank.lpm ? 0x0D8UL : 0x06CUL)
 #define CAP_ENDPTPRIME      (hw_bank.lpm ? 0x0DCUL : 0x070UL)
@@ -331,6 +334,17 @@
 	hw_cwrite(CAP_USBMODE, USBMODE_CM, USBMODE_CM_DEVICE);
 	hw_cwrite(CAP_USBMODE, USBMODE_SLOM, USBMODE_SLOM);  /* HW >= 2.3 */
 
+	/*
+	 * ITC (Interrupt Threshold Control) field is to set the maximum
+	 * rate at which the device controller will issue interrupts.
+	 * The maximum interrupt interval measured in micro frames.
+	 * Valid values are 0, 1, 2, 4, 8, 16, 32, 64. The default value is
+	 * 8 micro frames. If CPU can handle interrupts at faster rate, ITC
+	 * can be set to lesser value to gain performance.
+	 */
+	if (udc->udc_driver->flags & CI13XXX_ZERO_ITC)
+		hw_cwrite(CAP_USBCMD, USBCMD_ITC_MASK, USBCMD_ITC(0));
+
 	if (hw_cread(CAP_USBMODE, USBMODE_CM) != USBMODE_CM_DEVICE) {
 		pr_err("cannot enter in device mode");
 		pr_err("lpm = %i", hw_bank.lpm);
@@ -430,6 +444,10 @@
 		data |= ENDPTCTRL_RXE;
 	}
 	hw_cwrite(CAP_ENDPTCTRL + num * sizeof(u32), mask, data);
+
+	/* make sure endpoint is enabled before returning */
+	mb();
+
 	return 0;
 }
 
@@ -497,13 +515,18 @@
  */
 static int hw_ep_set_halt(int num, int dir, int value)
 {
+	u32 addr, mask_xs, mask_xr;
+
 	if (value != 0 && value != 1)
 		return -EINVAL;
 
 	do {
-		u32 addr = CAP_ENDPTCTRL + num * sizeof(u32);
-		u32 mask_xs = dir ? ENDPTCTRL_TXS : ENDPTCTRL_RXS;
-		u32 mask_xr = dir ? ENDPTCTRL_TXR : ENDPTCTRL_RXR;
+		if (hw_cread(CAP_ENDPTSETUPSTAT, BIT(num)))
+			return 0;
+
+		addr = CAP_ENDPTCTRL + num * sizeof(u32);
+		mask_xs = dir ? ENDPTCTRL_TXS : ENDPTCTRL_RXS;
+		mask_xr = dir ? ENDPTCTRL_TXR : ENDPTCTRL_RXR;
 
 		/* data toggle - reserved for EP0 but it's in ESS */
 		hw_cwrite(addr, mask_xs|mask_xr, value ? mask_xs : mask_xr);
@@ -855,6 +878,32 @@
 	*idx = (*idx + 1) & (DBG_DATA_MAX-1);
 }
 
+
+static unsigned int ep_addr_txdbg_mask;
+module_param(ep_addr_txdbg_mask, uint, S_IRUGO | S_IWUSR);
+static unsigned int ep_addr_rxdbg_mask;
+module_param(ep_addr_rxdbg_mask, uint, S_IRUGO | S_IWUSR);
+
+static int allow_dbg_print(u8 addr)
+{
+	int dir, num;
+
+	/* allow bus wide events */
+	if (addr == 0xff)
+		return 1;
+
+	dir = addr & USB_ENDPOINT_DIR_MASK ? TX : RX;
+	num = addr & ~USB_ENDPOINT_DIR_MASK;
+	num = 1 << num;
+
+	if ((dir == TX) && (num & ep_addr_txdbg_mask))
+		return 1;
+	if ((dir == RX) && (num & ep_addr_rxdbg_mask))
+		return 1;
+
+	return 0;
+}
+
 /**
  * dbg_print:  prints the common part of the event
  * @addr:   endpoint address
@@ -868,6 +917,9 @@
 	unsigned int stamp;
 	unsigned long flags;
 
+	if (!allow_dbg_print(addr))
+		return;
+
 	write_lock_irqsave(&dbg_data.lck, flags);
 
 	do_gettimeofday(&tval);
@@ -1246,6 +1298,9 @@
 		dev_err(dev, "[%s] EINVAL\n", __func__);
 		return 0;
 	}
+	dump = kmalloc(2048, GFP_KERNEL);
+	if (dump == NULL)
+		return -ENOMEM;
 
 	dump = kmalloc(sizeof(u32) * DUMP_ENTRIES, GFP_KERNEL);
 	if (!dump) {
@@ -1343,6 +1398,142 @@
 }
 static DEVICE_ATTR(requests, S_IRUSR, show_requests, NULL);
 
+/* EP# and Direction */
+static ssize_t prime_ept(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
+	struct ci13xxx_ep *mEp;
+	unsigned int ep_num, dir;
+	int n;
+	struct ci13xxx_req *mReq = NULL;
+
+	if (sscanf(buf, "%u %u", &ep_num, &dir) != 2) {
+		dev_err(dev, "<ep_num> <dir>: prime the ep");
+		goto done;
+	}
+
+	if (dir)
+		mEp = &udc->ci13xxx_ep[ep_num + hw_ep_max/2];
+	else
+		mEp = &udc->ci13xxx_ep[ep_num];
+
+	n = hw_ep_bit(mEp->num, mEp->dir);
+	mReq =  list_entry(mEp->qh.queue.next, struct ci13xxx_req, queue);
+	mEp->qh.ptr->td.next   = mReq->dma;
+	mEp->qh.ptr->td.token &= ~TD_STATUS;
+
+	wmb();
+
+	hw_cwrite(CAP_ENDPTPRIME, BIT(n), BIT(n));
+	while (hw_cread(CAP_ENDPTPRIME, BIT(n)))
+		cpu_relax();
+
+	pr_info("%s: prime:%08x stat:%08x ep#%d dir:%s\n", __func__,
+			hw_cread(CAP_ENDPTPRIME, ~0),
+			hw_cread(CAP_ENDPTSTAT, ~0),
+			mEp->num, mEp->dir ? "IN" : "OUT");
+done:
+	return count;
+
+}
+static DEVICE_ATTR(prime, S_IWUSR, NULL, prime_ept);
+
+/* EP# and Direction */
+static ssize_t print_dtds(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
+	struct ci13xxx_ep *mEp;
+	unsigned int ep_num, dir;
+	int n;
+	struct list_head   *ptr = NULL;
+	struct ci13xxx_req *req = NULL;
+
+	if (sscanf(buf, "%u %u", &ep_num, &dir) != 2) {
+		dev_err(dev, "<ep_num> <dir>: to print dtds");
+		goto done;
+	}
+
+	if (dir)
+		mEp = &udc->ci13xxx_ep[ep_num + hw_ep_max/2];
+	else
+		mEp = &udc->ci13xxx_ep[ep_num];
+
+	n = hw_ep_bit(mEp->num, mEp->dir);
+	pr_info("%s: prime:%08x stat:%08x ep#%d dir:%s"
+			"dTD_update_fail_count: %lu "
+			"mEp->dTD_update_fail_count: %lu\n", __func__,
+			hw_cread(CAP_ENDPTPRIME, ~0),
+			hw_cread(CAP_ENDPTSTAT, ~0),
+			mEp->num, mEp->dir ? "IN" : "OUT",
+			udc->dTD_update_fail_count,
+			mEp->dTD_update_fail_count);
+
+	pr_info("QH: cap:%08x cur:%08x next:%08x token:%08x\n",
+			mEp->qh.ptr->cap, mEp->qh.ptr->curr,
+			mEp->qh.ptr->td.next, mEp->qh.ptr->td.token);
+
+	list_for_each(ptr, &mEp->qh.queue) {
+		req = list_entry(ptr, struct ci13xxx_req, queue);
+
+		pr_info("\treq:%08x next:%08x token:%08x page0:%08x status:%d\n",
+				req->dma, req->ptr->next, req->ptr->token,
+				req->ptr->page[0], req->req.status);
+	}
+done:
+	return count;
+
+}
+static DEVICE_ATTR(dtds, S_IWUSR, NULL, print_dtds);
+
+static int ci13xxx_wakeup(struct usb_gadget *_gadget)
+{
+	struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget);
+	unsigned long flags;
+	int ret = 0;
+
+	trace();
+
+	spin_lock_irqsave(udc->lock, flags);
+	if (!udc->remote_wakeup) {
+		ret = -EOPNOTSUPP;
+		dbg_trace("remote wakeup feature is not enabled\n");
+		goto out;
+	}
+	spin_unlock_irqrestore(udc->lock, flags);
+
+	udc->udc_driver->notify_event(udc,
+		CI13XXX_CONTROLLER_REMOTE_WAKEUP_EVENT);
+
+	if (udc->transceiver)
+		usb_phy_set_suspend(udc->transceiver, 0);
+
+	spin_lock_irqsave(udc->lock, flags);
+	if (!hw_cread(CAP_PORTSC, PORTSC_SUSP)) {
+		ret = -EINVAL;
+		dbg_trace("port is not suspended\n");
+		goto out;
+	}
+	hw_cwrite(CAP_PORTSC, PORTSC_FPR, PORTSC_FPR);
+out:
+	spin_unlock_irqrestore(udc->lock, flags);
+	return ret;
+}
+
+static ssize_t usb_remote_wakeup(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
+
+	ci13xxx_wakeup(&udc->gadget);
+
+	return count;
+}
+static DEVICE_ATTR(wakeup, S_IWUSR, 0, usb_remote_wakeup);
+
 /**
  * dbg_create_files: initializes the attribute interface
  * @dev: device
@@ -1379,8 +1570,24 @@
 	retval = device_create_file(dev, &dev_attr_requests);
 	if (retval)
 		goto rm_registers;
+	retval = device_create_file(dev, &dev_attr_wakeup);
+	if (retval)
+		goto rm_remote_wakeup;
+	retval = device_create_file(dev, &dev_attr_prime);
+	if (retval)
+		goto rm_prime;
+	retval = device_create_file(dev, &dev_attr_dtds);
+	if (retval)
+		goto rm_dtds;
+
 	return 0;
 
+rm_dtds:
+	device_remove_file(dev, &dev_attr_dtds);
+rm_prime:
+	device_remove_file(dev, &dev_attr_prime);
+rm_remote_wakeup:
+	device_remove_file(dev, &dev_attr_wakeup);
  rm_registers:
 	device_remove_file(dev, &dev_attr_registers);
  rm_qheads:
@@ -1417,6 +1624,7 @@
 	device_remove_file(dev, &dev_attr_events);
 	device_remove_file(dev, &dev_attr_driver);
 	device_remove_file(dev, &dev_attr_device);
+	device_remove_file(dev, &dev_attr_wakeup);
 	return 0;
 }
 
@@ -1497,6 +1705,23 @@
 		if (!mReq->req.no_interrupt)
 			mReq->ptr->token  |= TD_IOC;
 	}
+
+	/* MSM Specific: updating the request as required for
+	 * SPS mode. Enable MSM proprietary DMA engine acording
+	 * to the UDC private data in the request.
+	 */
+	if (CI13XX_REQ_VENDOR_ID(mReq->req.udc_priv) == MSM_VENDOR_ID) {
+		if (mReq->req.udc_priv & MSM_SPS_MODE) {
+			mReq->ptr->token = TD_STATUS_ACTIVE;
+			if (mReq->req.udc_priv & MSM_IS_FINITE_TRANSFER)
+				mReq->ptr->next = TD_TERMINATE;
+			else
+				mReq->ptr->next = MSM_ETD_TYPE | mReq->dma;
+			if (!mReq->req.no_interrupt)
+				mReq->ptr->token |= MSM_ETD_IOC;
+		}
+	}
+
 	mReq->ptr->page[0]  = mReq->req.dma;
 	for (i = 1; i < 5; i++)
 		mReq->ptr->page[i] =
@@ -1526,10 +1751,56 @@
 	}
 
 	/*  QH configuration */
+	if (!list_empty(&mEp->qh.queue)) {
+		struct ci13xxx_req *mReq = \
+			list_entry(mEp->qh.queue.next,
+				   struct ci13xxx_req, queue);
+
+		if (TD_STATUS_ACTIVE & mReq->ptr->token) {
+			mEp->qh.ptr->td.next   = mReq->dma;
+			mEp->qh.ptr->td.token &= ~TD_STATUS;
+			goto prime;
+		}
+	}
+
 	mEp->qh.ptr->td.next   = mReq->dma;    /* TERMINATE = 0 */
+
+	if (CI13XX_REQ_VENDOR_ID(mReq->req.udc_priv) == MSM_VENDOR_ID) {
+		if (mReq->req.udc_priv & MSM_SPS_MODE) {
+			mEp->qh.ptr->td.next   |= MSM_ETD_TYPE;
+			i = hw_cread(CAP_ENDPTPIPEID +
+						 mEp->num * sizeof(u32), ~0);
+			/* Read current value of this EPs pipe id */
+			i = (mEp->dir == TX) ?
+				((i >> MSM_TX_PIPE_ID_OFS) & MSM_PIPE_ID_MASK) :
+					(i & MSM_PIPE_ID_MASK);
+			/* If requested pipe id is different from current,
+			   then write it */
+			if (i != (mReq->req.udc_priv & MSM_PIPE_ID_MASK)) {
+				if (mEp->dir == TX)
+					hw_cwrite(
+						CAP_ENDPTPIPEID +
+							mEp->num * sizeof(u32),
+						MSM_PIPE_ID_MASK <<
+							MSM_TX_PIPE_ID_OFS,
+						(mReq->req.udc_priv &
+						 MSM_PIPE_ID_MASK)
+							<< MSM_TX_PIPE_ID_OFS);
+				else
+					hw_cwrite(
+						CAP_ENDPTPIPEID +
+							mEp->num * sizeof(u32),
+						MSM_PIPE_ID_MASK,
+						mReq->req.udc_priv &
+							MSM_PIPE_ID_MASK);
+			}
+		}
+	}
+
 	mEp->qh.ptr->td.token &= ~TD_STATUS;   /* clear status */
 	mEp->qh.ptr->cap |=  QH_ZLT;
 
+prime:
 	wmb();   /* synchronize before ep prime */
 
 	ret = hw_ep_prime(mEp->num, mEp->dir,
@@ -1552,9 +1823,16 @@
 	if (mReq->req.status != -EALREADY)
 		return -EINVAL;
 
+	/* clean speculative fetches on req->ptr->token */
+	mb();
+
 	if ((TD_STATUS_ACTIVE & mReq->ptr->token) != 0)
 		return -EBUSY;
 
+	if (CI13XX_REQ_VENDOR_ID(mReq->req.udc_priv) == MSM_VENDOR_ID)
+		if ((mReq->req.udc_priv & MSM_SPS_MODE) &&
+			(mReq->req.udc_priv & MSM_IS_FINITE_TRANSFER))
+			return -EBUSY;
 	if (mReq->zptr) {
 		if ((TD_STATUS_ACTIVE & mReq->zptr->token) != 0)
 			return -EBUSY;
@@ -1598,6 +1876,9 @@
 __releases(mEp->lock)
 __acquires(mEp->lock)
 {
+	struct ci13xxx_ep *mEpTemp = mEp;
+	unsigned val;
+
 	trace("%p", mEp);
 
 	if (mEp == NULL)
@@ -1612,11 +1893,37 @@
 			list_entry(mEp->qh.queue.next,
 				   struct ci13xxx_req, queue);
 		list_del_init(&mReq->queue);
+
+		/* MSM Specific: Clear end point proprietary register */
+		if (CI13XX_REQ_VENDOR_ID(mReq->req.udc_priv) == MSM_VENDOR_ID) {
+			if (mReq->req.udc_priv & MSM_SPS_MODE) {
+				val = hw_cread(CAP_ENDPTPIPEID +
+					mEp->num * sizeof(u32),
+					~0);
+
+				if (val != MSM_EP_PIPE_ID_RESET_VAL)
+					hw_cwrite(
+						CAP_ENDPTPIPEID +
+						 mEp->num * sizeof(u32),
+						~0, MSM_EP_PIPE_ID_RESET_VAL);
+			}
+		}
 		mReq->req.status = -ESHUTDOWN;
 
+		if (mReq->map) {
+			dma_unmap_single(mEp->device, mReq->req.dma,
+				mReq->req.length,
+				mEp->dir ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+			mReq->req.dma = 0;
+			mReq->map     = 0;
+		}
+
 		if (mReq->req.complete != NULL) {
 			spin_unlock(mEp->lock);
-			mReq->req.complete(&mEp->ep, &mReq->req);
+			if ((mEp->type == USB_ENDPOINT_XFER_CONTROL) &&
+				mReq->req.length)
+				mEpTemp = &_udc->ep0in;
+			mReq->req.complete(&mEpTemp->ep, &mReq->req);
 			spin_lock(mEp->lock);
 		}
 	}
@@ -1644,8 +1951,14 @@
 	udc->gadget.speed = USB_SPEED_UNKNOWN;
 	udc->remote_wakeup = 0;
 	udc->suspended = 0;
+	udc->configured = 0;
 	spin_unlock_irqrestore(udc->lock, flags);
 
+	gadget->b_hnp_enable = 0;
+	gadget->a_hnp_support = 0;
+	gadget->host_request = 0;
+	gadget->otg_srp_reqd = 0;
+
 	/* flush all endpoints */
 	gadget_for_each_ep(ep, gadget) {
 		usb_ep_fifo_flush(ep);
@@ -1693,6 +2006,11 @@
 	dbg_event(0xFF, "BUS RST", 0);
 
 	spin_unlock(udc->lock);
+
+	/*stop charging upon reset */
+	if (udc->transceiver)
+		usb_phy_set_power(udc->transceiver, 0);
+
 	retval = _gadget_stop_activity(&udc->gadget);
 	if (retval)
 		goto done;
@@ -1713,6 +2031,51 @@
 }
 
 /**
+ * isr_resume_handler: USB PCI interrupt handler
+ * @udc: UDC device
+ *
+ */
+static void isr_resume_handler(struct ci13xxx *udc)
+{
+	udc->gadget.speed = hw_port_is_high_speed() ?
+		USB_SPEED_HIGH : USB_SPEED_FULL;
+	if (udc->suspended) {
+		spin_unlock(udc->lock);
+		if (udc->udc_driver->notify_event)
+			udc->udc_driver->notify_event(udc,
+			  CI13XXX_CONTROLLER_RESUME_EVENT);
+		if (udc->transceiver)
+			usb_phy_set_suspend(udc->transceiver, 0);
+		udc->driver->resume(&udc->gadget);
+		spin_lock(udc->lock);
+		udc->suspended = 0;
+	}
+}
+
+/**
+ * isr_resume_handler: USB SLI interrupt handler
+ * @udc: UDC device
+ *
+ */
+static void isr_suspend_handler(struct ci13xxx *udc)
+{
+	if (udc->gadget.speed != USB_SPEED_UNKNOWN &&
+		udc->vbus_active) {
+		if (udc->suspended == 0) {
+			spin_unlock(udc->lock);
+			udc->driver->suspend(&udc->gadget);
+			if (udc->udc_driver->notify_event)
+				udc->udc_driver->notify_event(udc,
+				CI13XXX_CONTROLLER_SUSPEND_EVENT);
+			if (udc->transceiver)
+				usb_phy_set_suspend(udc->transceiver, 1);
+			spin_lock(udc->lock);
+			udc->suspended = 1;
+		}
+	}
+}
+
+/**
  * isr_get_status_complete: get_status request complete function
  * @ep:  endpoint
  * @req: request handled
@@ -1769,8 +2132,15 @@
 	}
 
 	if ((setup->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
-		/* Assume that device is bus powered for now. */
-		*((u16 *)req->buf) = _udc->remote_wakeup << 1;
+		if (setup->wIndex == OTG_STATUS_SELECTOR) {
+			*((u8 *)req->buf) = _udc->gadget.host_request <<
+						HOST_REQUEST_FLAG;
+			req->length = 1;
+		} else {
+			/* Assume that device is bus powered for now. */
+			*((u16 *)req->buf) = _udc->remote_wakeup << 1;
+		}
+		/* TODO: D1 - Remote Wakeup; D0 - Self Powered */
 		retval = 0;
 	} else if ((setup->bRequestType & USB_RECIP_MASK) \
 		   == USB_RECIP_ENDPOINT) {
@@ -1860,17 +2230,35 @@
 	struct ci13xxx_req *mReq, *mReqTemp;
 	struct ci13xxx_ep *mEpTemp = mEp;
 	int uninitialized_var(retval);
+	int req_dequeue = 1;
+	struct ci13xxx *udc = _udc;
 
 	trace("%p", mEp);
 
 	if (list_empty(&mEp->qh.queue))
-		return -EINVAL;
+		return 0;
 
 	list_for_each_entry_safe(mReq, mReqTemp, &mEp->qh.queue,
 			queue) {
+dequeue:
 		retval = _hardware_dequeue(mEp, mReq);
-		if (retval < 0)
+		if (retval < 0) {
+			/*
+			 * FIXME: don't know exact delay
+			 * required for HW to update dTD status
+			 * bits. This is a temporary workaround till
+			 * HW designers come back on this.
+			 */
+			if (retval == -EBUSY && req_dequeue && mEp->dir == 0) {
+				req_dequeue = 0;
+				udc->dTD_update_fail_count++;
+				mEp->dTD_update_fail_count++;
+				udelay(10);
+				goto dequeue;
+			}
 			break;
+		}
+		req_dequeue = 0;
 		list_del_init(&mReq->queue);
 		dbg_done(_usb_addr(mEp), mReq->ptr->token, retval);
 		if (mReq->req.complete != NULL) {
@@ -1955,6 +2343,8 @@
 		do {
 			hw_test_and_set_setup_guard();
 			memcpy(&req, &mEp->qh.ptr->setup, sizeof(req));
+			/* Ensure buffer is read before acknowledging to h/w */
+			mb();
 		} while (!hw_test_and_clear_setup_guard());
 
 		type = req.bRequestType;
@@ -2000,8 +2390,7 @@
 			    type != (USB_DIR_IN|USB_RECIP_ENDPOINT) &&
 			    type != (USB_DIR_IN|USB_RECIP_INTERFACE))
 				goto delegate;
-			if (le16_to_cpu(req.wLength) != 2 ||
-			    le16_to_cpu(req.wValue)  != 0)
+			if (le16_to_cpu(req.wValue)  != 0)
 				break;
 			err = isr_get_status_response(udc, &req);
 			break;
@@ -2016,6 +2405,10 @@
 				break;
 			err = isr_setup_status_phase(udc);
 			break;
+		case USB_REQ_SET_CONFIGURATION:
+			if (type == (USB_DIR_OUT|USB_TYPE_STANDARD))
+				udc->configured = !!req.wValue;
+			goto delegate;
 		case USB_REQ_SET_FEATURE:
 			if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
 					le16_to_cpu(req.wValue) ==
@@ -2041,6 +2434,16 @@
 					udc->remote_wakeup = 1;
 					err = isr_setup_status_phase(udc);
 					break;
+				case USB_DEVICE_B_HNP_ENABLE:
+					udc->gadget.b_hnp_enable = 1;
+					err = isr_setup_status_phase(udc);
+					break;
+				case USB_DEVICE_A_HNP_SUPPORT:
+					udc->gadget.a_hnp_support = 1;
+					err = isr_setup_status_phase(udc);
+					break;
+				case USB_DEVICE_A_ALT_HNP_SUPPORT:
+					break;
 				case USB_DEVICE_TEST_MODE:
 					tmode = le16_to_cpu(req.wIndex) >> 8;
 					switch (tmode) {
@@ -2053,11 +2456,21 @@
 						err = isr_setup_status_phase(
 								udc);
 						break;
+					case TEST_OTG_SRP_REQD:
+						udc->gadget.otg_srp_reqd = 1;
+						err = isr_setup_status_phase(
+								udc);
+						break;
+					case TEST_OTG_HNP_REQD:
+						udc->gadget.host_request = 1;
+						err = isr_setup_status_phase(
+								udc);
+						break;
 					default:
 						break;
 					}
 				default:
-					goto delegate;
+					break;
 				}
 			} else {
 				goto delegate;
@@ -2129,12 +2542,15 @@
 	else if (mEp->type == USB_ENDPOINT_XFER_ISOC)
 		mEp->qh.ptr->cap &= ~QH_MULT;
 	else
-		mEp->qh.ptr->cap &= ~QH_ZLT;
+		mEp->qh.ptr->cap |= QH_ZLT;
 
 	mEp->qh.ptr->cap |=
 		(mEp->ep.maxpacket << ffs_nr(QH_MAX_PKT)) & QH_MAX_PKT;
 	mEp->qh.ptr->td.next |= TD_TERMINATE;   /* needed? */
 
+	/* complete all the updates to ept->head before enabling endpoint*/
+	mb();
+
 	/*
 	 * Enable endpoints in the HW other than ep0 as ep0
 	 * is always enabled
@@ -2266,6 +2682,7 @@
 	struct ci13xxx_req *mReq = container_of(req, struct ci13xxx_req, req);
 	int retval = 0;
 	unsigned long flags;
+	struct ci13xxx *udc = _udc;
 
 	trace("%p, %p, %X", ep, req, gfp_flags);
 
@@ -2274,6 +2691,15 @@
 
 	spin_lock_irqsave(mEp->lock, flags);
 
+	if (!udc->configured && mEp->type !=
+		USB_ENDPOINT_XFER_CONTROL) {
+		spin_unlock_irqrestore(mEp->lock, flags);
+		trace("usb is not configured"
+			"ept #%d, ept name#%s\n",
+			mEp->num, mEp->ep.name);
+		return -ESHUTDOWN;
+	}
+
 	if (mEp->type == USB_ENDPOINT_XFER_CONTROL) {
 		if (req->length)
 			mEp = (_udc->ep0_dir == RX) ?
@@ -2326,6 +2752,7 @@
 static int ep_dequeue(struct usb_ep *ep, struct usb_request *req)
 {
 	struct ci13xxx_ep  *mEp  = container_of(ep,  struct ci13xxx_ep, ep);
+	struct ci13xxx_ep *mEpTemp = mEp;
 	struct ci13xxx_req *mReq = container_of(req, struct ci13xxx_req, req);
 	unsigned long flags;
 
@@ -2354,7 +2781,10 @@
 
 	if (mReq->req.complete != NULL) {
 		spin_unlock(mEp->lock);
-		mReq->req.complete(&mEp->ep, &mReq->req);
+		if ((mEp->type == USB_ENDPOINT_XFER_CONTROL) &&
+				mReq->req.length)
+			mEpTemp = &_udc->ep0in;
+		mReq->req.complete(&mEpTemp->ep, &mReq->req);
 		spin_lock(mEp->lock);
 	}
 
@@ -2362,6 +2792,12 @@
 	return 0;
 }
 
+static int is_sps_req(struct ci13xxx_req *mReq)
+{
+	return (CI13XX_REQ_VENDOR_ID(mReq->req.udc_priv) == MSM_VENDOR_ID &&
+			mReq->req.udc_priv & MSM_SPS_MODE);
+}
+
 /**
  * ep_set_halt: sets the endpoint halt feature
  *
@@ -2383,7 +2819,9 @@
 #ifndef STALL_IN
 	/* g_file_storage MS compliant but g_zero fails chapter 9 compliance */
 	if (value && mEp->type == USB_ENDPOINT_XFER_BULK && mEp->dir == TX &&
-	    !list_empty(&mEp->qh.queue)) {
+		!list_empty(&mEp->qh.queue) &&
+		!is_sps_req(list_entry(mEp->qh.queue.next, struct ci13xxx_req,
+							   queue))){
 		spin_unlock_irqrestore(mEp->lock, flags);
 		return -EAGAIN;
 	}
@@ -2494,13 +2932,14 @@
 		if (is_active) {
 			pm_runtime_get_sync(&_gadget->dev);
 			hw_device_reset(udc);
-			hw_device_state(udc->ep0out.qh.dma);
+			if (udc->softconnect)
+				hw_device_state(udc->ep0out.qh.dma);
 		} else {
 			hw_device_state(0);
+			_gadget_stop_activity(&udc->gadget);
 			if (udc->udc_driver->notify_event)
 				udc->udc_driver->notify_event(udc,
-				CI13XXX_CONTROLLER_STOPPED_EVENT);
-			_gadget_stop_activity(&udc->gadget);
+					CI13XXX_CONTROLLER_DISCONNECT_EVENT);
 			pm_runtime_put_sync(&_gadget->dev);
 		}
 	}
@@ -2508,31 +2947,6 @@
 	return 0;
 }
 
-static int ci13xxx_wakeup(struct usb_gadget *_gadget)
-{
-	struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget);
-	unsigned long flags;
-	int ret = 0;
-
-	trace();
-
-	spin_lock_irqsave(udc->lock, flags);
-	if (!udc->remote_wakeup) {
-		ret = -EOPNOTSUPP;
-		trace("remote wakeup feature is not enabled\n");
-		goto out;
-	}
-	if (!hw_cread(CAP_PORTSC, PORTSC_SUSP)) {
-		ret = -EINVAL;
-		trace("port is not suspended\n");
-		goto out;
-	}
-	hw_cwrite(CAP_PORTSC, PORTSC_FPR, PORTSC_FPR);
-out:
-	spin_unlock_irqrestore(udc->lock, flags);
-	return ret;
-}
-
 static int ci13xxx_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
 {
 	struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget);
@@ -2542,6 +2956,32 @@
 	return -ENOTSUPP;
 }
 
+static int ci13xxx_pullup(struct usb_gadget *_gadget, int is_active)
+{
+	struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget);
+	unsigned long flags;
+
+	spin_lock_irqsave(udc->lock, flags);
+	udc->softconnect = is_active;
+	if (((udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) &&
+			!udc->vbus_active) || !udc->driver) {
+		spin_unlock_irqrestore(udc->lock, flags);
+		return 0;
+	}
+	spin_unlock_irqrestore(udc->lock, flags);
+
+	if (is_active) {
+		hw_device_state(udc->ep0out.qh.dma);
+		if (udc->udc_driver->notify_event)
+			udc->udc_driver->notify_event(udc,
+				CI13XXX_CONTROLLER_CONNECT_EVENT);
+	}
+	else
+		hw_device_state(0);
+
+	return 0;
+}
+
 static int ci13xxx_start(struct usb_gadget_driver *driver,
 		int (*bind)(struct usb_gadget *));
 static int ci13xxx_stop(struct usb_gadget_driver *driver);
@@ -2554,6 +2994,7 @@
 	.vbus_session	= ci13xxx_vbus_session,
 	.wakeup		= ci13xxx_wakeup,
 	.vbus_draw	= ci13xxx_vbus_draw,
+	.pullup		= ci13xxx_pullup,
 	.start		= ci13xxx_start,
 	.stop		= ci13xxx_stop,
 };
@@ -2573,6 +3014,7 @@
 	unsigned long flags;
 	int i, j;
 	int retval = -ENOMEM;
+	bool put = false;
 
 	trace("%p", driver);
 
@@ -2660,8 +3102,10 @@
 	/* bind gadget */
 	driver->driver.bus     = NULL;
 	udc->gadget.dev.driver = &driver->driver;
+	udc->softconnect = 1;
 
 	spin_unlock_irqrestore(udc->lock, flags);
+	pm_runtime_get_sync(&udc->gadget.dev);
 	retval = bind(&udc->gadget);                /* MAY SLEEP */
 	spin_lock_irqsave(udc->lock, flags);
 
@@ -2671,23 +3115,27 @@
 	}
 
 	udc->driver = driver;
-	pm_runtime_get_sync(&udc->gadget.dev);
 	if (udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) {
 		if (udc->vbus_active) {
 			if (udc->udc_driver->flags & CI13XXX_REGS_SHARED)
 				hw_device_reset(udc);
 		} else {
-			pm_runtime_put_sync(&udc->gadget.dev);
+			put = true;
 			goto done;
 		}
 	}
 
+	if (!udc->softconnect) {
+		put = true;
+		goto done;
+	}
+
 	retval = hw_device_state(udc->ep0out.qh.dma);
-	if (retval)
-		pm_runtime_put_sync(&udc->gadget.dev);
 
  done:
 	spin_unlock_irqrestore(udc->lock, flags);
+	if (retval || put)
+		pm_runtime_put_sync(&udc->gadget.dev);
 	return retval;
 }
 
@@ -2715,9 +3163,6 @@
 	if (!(udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) ||
 			udc->vbus_active) {
 		hw_device_state(0);
-		if (udc->udc_driver->notify_event)
-			udc->udc_driver->notify_event(udc,
-			CI13XXX_CONTROLLER_STOPPED_EVENT);
 		spin_unlock_irqrestore(udc->lock, flags);
 		_gadget_stop_activity(&udc->gadget);
 		spin_lock_irqsave(udc->lock, flags);
@@ -2803,14 +3248,7 @@
 		}
 		if (USBi_PCI & intr) {
 			isr_statistics.pci++;
-			udc->gadget.speed = hw_port_is_high_speed() ?
-				USB_SPEED_HIGH : USB_SPEED_FULL;
-			if (udc->suspended && udc->driver->resume) {
-				spin_unlock(udc->lock);
-				udc->driver->resume(&udc->gadget);
-				spin_lock(udc->lock);
-				udc->suspended = 0;
-			}
+			isr_resume_handler(udc);
 		}
 		if (USBi_UEI & intr)
 			isr_statistics.uei++;
@@ -2819,13 +3257,7 @@
 			isr_tr_complete_handler(udc);
 		}
 		if (USBi_SLI & intr) {
-			if (udc->gadget.speed != USB_SPEED_UNKNOWN &&
-			    udc->driver->suspend) {
-				udc->suspended = 1;
-				spin_unlock(udc->lock);
-				udc->driver->suspend(&udc->gadget);
-				spin_lock(udc->lock);
-			}
+			isr_suspend_handler(udc);
 			isr_statistics.sli++;
 		}
 		retval = IRQ_HANDLED;
@@ -2866,7 +3298,7 @@
 		void __iomem *regs)
 {
 	struct ci13xxx *udc;
-	int retval = 0;
+	int retval = 0, i;
 
 	trace("%p, %p, %p", dev, regs, driver->name);
 
@@ -2885,7 +3317,10 @@
 	udc->gadget.ops          = &usb_gadget_ops;
 	udc->gadget.speed        = USB_SPEED_UNKNOWN;
 	udc->gadget.max_speed    = USB_SPEED_HIGH;
-	udc->gadget.is_otg       = 0;
+	if (udc->udc_driver->flags & CI13XXX_IS_OTG)
+		udc->gadget.is_otg       = 1;
+	else
+		udc->gadget.is_otg       = 0;
 	udc->gadget.name         = driver->name;
 
 	INIT_LIST_HEAD(&udc->gadget.ep_list);
@@ -2897,19 +3332,23 @@
 	udc->gadget.dev.parent   = dev;
 	udc->gadget.dev.release  = udc_release;
 
-	retval = hw_device_init(regs);
-	if (retval < 0)
-		goto free_udc;
-
-	udc->transceiver = usb_get_transceiver();
-
 	if (udc->udc_driver->flags & CI13XXX_REQUIRE_TRANSCEIVER) {
+		udc->transceiver = usb_get_transceiver();
 		if (udc->transceiver == NULL) {
 			retval = -ENODEV;
 			goto free_udc;
 		}
 	}
 
+	retval = hw_device_init(regs);
+	if (retval < 0)
+		goto put_transceiver;
+
+	for (i = 0; i < hw_ep_max; i++) {
+		struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i];
+		INIT_LIST_HEAD(&mEp->ep.ep_list);
+	}
+
 	if (!(udc->udc_driver->flags & CI13XXX_REGS_SHARED)) {
 		retval = hw_device_reset(udc);
 		if (retval)
diff --git a/drivers/usb/gadget/ci13xxx_udc.h b/drivers/usb/gadget/ci13xxx_udc.h
index 0d31af5..4376804 100644
--- a/drivers/usb/gadget/ci13xxx_udc.h
+++ b/drivers/usb/gadget/ci13xxx_udc.h
@@ -25,6 +25,14 @@
 #define RX        (0)  /* similar to USB_DIR_OUT but can be used as an index */
 #define TX        (1)  /* similar to USB_DIR_IN  but can be used as an index */
 
+/* UDC private data:
+ *  16MSb - Vendor ID | 16 LSb Vendor private data
+ */
+#define CI13XX_REQ_VENDOR_ID(id)  (id & 0xFFFF0000UL)
+
+#define MSM_ETD_TYPE			BIT(1)
+#define MSM_EP_PIPE_ID_RESET_VAL	0x1F001F
+
 /******************************************************************************
  * STRUCTURES
  *****************************************************************************/
@@ -98,6 +106,7 @@
 	spinlock_t                            *lock;
 	struct device                         *device;
 	struct dma_pool                       *td_pool;
+	unsigned long dTD_update_fail_count;
 };
 
 struct ci13xxx;
@@ -108,9 +117,15 @@
 #define CI13XXX_REQUIRE_TRANSCEIVER	BIT(1)
 #define CI13XXX_PULLUP_ON_VBUS		BIT(2)
 #define CI13XXX_DISABLE_STREAMING	BIT(3)
+#define CI13XXX_ZERO_ITC		BIT(4)
+#define CI13XXX_IS_OTG			BIT(5)
 
-#define CI13XXX_CONTROLLER_RESET_EVENT		0
-#define CI13XXX_CONTROLLER_STOPPED_EVENT	1
+#define CI13XXX_CONTROLLER_RESET_EVENT			0
+#define CI13XXX_CONTROLLER_CONNECT_EVENT		1
+#define CI13XXX_CONTROLLER_SUSPEND_EVENT		2
+#define CI13XXX_CONTROLLER_REMOTE_WAKEUP_EVENT	3
+#define CI13XXX_CONTROLLER_RESUME_EVENT	        4
+#define CI13XXX_CONTROLLER_DISCONNECT_EVENT	    5
 	void	(*notify_event) (struct ci13xxx *udc, unsigned event);
 };
 
@@ -131,11 +146,14 @@
 	u8                         remote_wakeup; /* Is remote wakeup feature
 							enabled by the host? */
 	u8                         suspended;  /* suspended by the host */
+	u8                         configured;  /* is device configured */
 	u8                         test_mode;  /* the selected test mode */
 
 	struct usb_gadget_driver  *driver;     /* 3rd party gadget driver */
 	struct ci13xxx_udc_driver *udc_driver; /* device controller driver */
 	int                        vbus_active; /* is VBUS active */
+	int                        softconnect; /* is pull-up enable allowed */
+	unsigned long dTD_update_fail_count;
 	struct usb_phy            *transceiver; /* Transceiver struct */
 };
 
@@ -189,6 +207,8 @@
 #define    USBMODE_CM_HOST    (0x03UL <<  0)
 #define USBMODE_SLOM          BIT(3)
 #define USBMODE_SDIS          BIT(4)
+#define USBCMD_ITC(n)         (n << 16) /* n = 0, 1, 2, 4, 8, 16, 32, 64 */
+#define USBCMD_ITC_MASK       (0xFF << 16)
 
 /* ENDPTCTRL */
 #define ENDPTCTRL_RXS         BIT(0)
@@ -212,7 +232,10 @@
 			   "[%s] " format "\n", __func__, ## args); \
 } while (0)
 
+#ifndef err
 #define err(format, args...)    ci13xxx_printk(KERN_ERR, format, ## args)
+#endif
+
 #define warn(format, args...)   ci13xxx_printk(KERN_WARNING, format, ## args)
 #define info(format, args...)   ci13xxx_printk(KERN_INFO, format, ## args)
 
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 8f82fc0..d35d861 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -29,7 +29,7 @@
  */
 
 /* big enough to hold our biggest descriptor */
-#define USB_BUFSIZ	1024
+#define USB_BUFSIZ	4096
 
 static struct usb_composite_driver *composite;
 static int (*composite_gadget_bind)(struct usb_composite_dev *cdev);
@@ -175,13 +175,14 @@
 	_ep->comp_desc = comp_desc;
 	if (g->speed == USB_SPEED_SUPER) {
 		switch (usb_endpoint_type(_ep->desc)) {
-		case USB_ENDPOINT_XFER_ISOC:
-			/* mult: bits 1:0 of bmAttributes */
-			_ep->mult = comp_desc->bmAttributes & 0x3;
 		case USB_ENDPOINT_XFER_BULK:
 		case USB_ENDPOINT_XFER_INT:
 			_ep->maxburst = comp_desc->bMaxBurst;
 			break;
+		case USB_ENDPOINT_XFER_ISOC:
+			/* mult: bits 1:0 of bmAttributes */
+			_ep->mult = comp_desc->bmAttributes & 0x3;
+			break;
 		default:
 			/* Do nothing for control endpoints */
 			break;
@@ -687,6 +688,7 @@
 	power = c->bMaxPower ? (2 * c->bMaxPower) : CONFIG_USB_GADGET_VBUS_DRAW;
 done:
 	usb_gadget_vbus_draw(gadget, power);
+
 	if (result >= 0 && cdev->delayed_status)
 		result = USB_GADGET_DELAYED_STATUS;
 	return result;
@@ -1080,6 +1082,11 @@
 	u16				w_length = le16_to_cpu(ctrl->wLength);
 	struct usb_function		*f = NULL;
 	u8				endp;
+	struct usb_configuration *c;
+
+
+	if (w_length > USB_BUFSIZ)
+		return value;
 
 	/* partial re-init of the response message; the function or the
 	 * gadget might need to intercept e.g. a control-OUT completion
@@ -1133,6 +1140,16 @@
 			if (value >= 0)
 				value = min(w_length, (u16) value);
 			break;
+		case USB_DT_OTG:
+			if (!gadget_is_otg(gadget))
+				break;
+			c = list_first_entry(&cdev->configs,
+				struct usb_configuration, list);
+			if (c && c->descriptors)
+				value = usb_find_descriptor_fillbuf(req->buf,
+						USB_BUFSIZ, c->descriptors,
+						USB_DT_OTG);
+			break;
 		case USB_DT_STRING:
 			value = get_string(cdev, req->buf,
 					w_index, w_value & 0xff);
@@ -1606,6 +1623,8 @@
 int usb_composite_probe(struct usb_composite_driver *driver,
 			       int (*bind)(struct usb_composite_dev *cdev))
 {
+	int retval;
+
 	if (!driver || !driver->dev || !bind || composite)
 		return -EINVAL;
 
@@ -1620,7 +1639,10 @@
 	composite = driver;
 	composite_gadget_bind = bind;
 
-	return usb_gadget_probe_driver(&composite_driver, composite_bind);
+	retval = usb_gadget_probe_driver(&composite_driver, composite_bind);
+	if (retval)
+		composite = NULL;
+	return retval;
 }
 
 /**
diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c
index 7542a72..c52d8b6 100644
--- a/drivers/usb/gadget/config.c
+++ b/drivers/usb/gadget/config.c
@@ -19,6 +19,40 @@
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
 
+/**
+ * usb_find_descriptor_fillbuf - fill buffer with the requested descriptor
+ * @buf: Buffer to be filled
+ * @buflen: Size of buf
+ * @src: Array of descriptor pointers, terminated by null pointer.
+ * @desc_type: bDescriptorType field of the requested descriptor.
+ *
+ * Copies the requested descriptor into the buffer, returning the length
+ * or a negative error code if it is not found or can't be copied.  Useful
+ * when DT_OTG descriptor is requested.
+ */
+int
+usb_find_descriptor_fillbuf(void *buf, unsigned buflen,
+		const struct usb_descriptor_header **src, u8 desc_type)
+{
+	if (!src)
+		return -EINVAL;
+
+	for (; NULL != *src; src++) {
+		unsigned len;
+
+		if ((*src)->bDescriptorType != desc_type)
+			continue;
+
+		len = (*src)->bLength;
+		if (len > buflen)
+			return -EINVAL;
+
+		memcpy(buf, *src, len);
+		return len;
+	}
+
+	return -ENOENT;
+}
 
 /**
  * usb_descriptor_fillbuf - fill buffer with descriptors
diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c
index a581822..108caf9 100644
--- a/drivers/usb/gadget/f_accessory.c
+++ b/drivers/usb/gadget/f_accessory.c
@@ -296,7 +296,7 @@
 	}
 }
 
-static int __init create_bulk_endpoints(struct acc_dev *dev,
+static int create_bulk_endpoints(struct acc_dev *dev,
 				struct usb_endpoint_descriptor *in_desc,
 				struct usb_endpoint_descriptor *out_desc)
 {
@@ -688,19 +688,31 @@
 	DBG(cdev, "acc_function_set_alt intf: %d alt: %d\n", intf, alt);
 
 	ret = config_ep_by_speed(cdev->gadget, f, dev->ep_in);
-	if (ret)
-		return ret;
-
+	if (ret) {
+		dev->ep_in->desc = NULL;
+		ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
+				dev->ep_in->name, ret);
+			return ret;
+	}
 	ret = usb_ep_enable(dev->ep_in);
-	if (ret)
+	if (ret) {
+		ERROR(cdev, "failed to enable ep %s, result %d\n",
+			dev->ep_in->name, ret);
 		return ret;
+	}
 
 	ret = config_ep_by_speed(cdev->gadget, f, dev->ep_out);
-	if (ret)
+	if (ret) {
+		dev->ep_out->desc = NULL;
+		ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
+			dev->ep_out->name, ret);
+		usb_ep_disable(dev->ep_in);
 		return ret;
-
+	}
 	ret = usb_ep_enable(dev->ep_out);
 	if (ret) {
+		ERROR(cdev, "failed to enable ep %s, result %d\n",
+				dev->ep_out->name, ret);
 		usb_ep_disable(dev->ep_in);
 		return ret;
 	}
diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c
index d672250a..cc151cb 100644
--- a/drivers/usb/gadget/f_acm.c
+++ b/drivers/usb/gadget/f_acm.c
@@ -5,6 +5,7 @@
  * Copyright (C) 2008 by David Brownell
  * Copyright (C) 2008 by Nokia Corporation
  * Copyright (C) 2009 by Samsung Electronics
+ * Copyright (c) 2011 Code Aurora Forum. All rights reserved.
  * Author: Michal Nazarewicz (mina86@mina86.com)
  *
  * This software is distributed under the terms of the GNU General
@@ -17,6 +18,8 @@
 #include <linux/slab.h>
 #include <linux/kernel.h>
 #include <linux/device.h>
+#include <linux/usb/android_composite.h>
+#include <mach/usb_gadget_xport.h>
 
 #include "u_serial.h"
 #include "gadget_chips.h"
@@ -43,6 +46,7 @@
 	struct gserial			port;
 	u8				ctrl_id, data_id;
 	u8				port_num;
+	enum transport_type		transport;
 
 	u8				pending;
 
@@ -73,6 +77,17 @@
 #define ACM_CTRL_DCD		(1 << 0)
 };
 
+static unsigned int no_acm_tty_ports;
+static unsigned int no_acm_sdio_ports;
+static unsigned int no_acm_smd_ports;
+static unsigned int nr_acm_ports;
+
+static struct acm_port_info {
+	enum transport_type	transport;
+	unsigned		port_num;
+	unsigned		client_port_num;
+} gacm_ports[GSERIAL_NO_PORTS];
+
 static inline struct f_acm *func_to_acm(struct usb_function *f)
 {
 	return container_of(f, struct f_acm, port.func);
@@ -83,6 +98,82 @@
 	return container_of(p, struct f_acm, port);
 }
 
+static int acm_port_setup(struct usb_configuration *c)
+{
+	int ret = 0;
+
+	pr_debug("%s: no_acm_tty_ports:%u no_acm_sdio_ports: %u nr_acm_ports:%u\n",
+			__func__, no_acm_tty_ports, no_acm_sdio_ports,
+				nr_acm_ports);
+
+	if (no_acm_tty_ports)
+		ret = gserial_setup(c->cdev->gadget, no_acm_tty_ports);
+	if (no_acm_sdio_ports)
+		ret = gsdio_setup(c->cdev->gadget, no_acm_sdio_ports);
+	if (no_acm_smd_ports)
+		ret = gsmd_setup(c->cdev->gadget, no_acm_smd_ports);
+
+	return ret;
+}
+
+static int acm_port_connect(struct f_acm *acm)
+{
+	unsigned port_num;
+
+	port_num = gacm_ports[acm->port_num].client_port_num;
+
+
+	pr_debug("%s: transport:%s f_acm:%p gserial:%p port_num:%d cl_port_no:%d\n",
+			__func__, xport_to_str(acm->transport),
+			acm, &acm->port, acm->port_num, port_num);
+
+	switch (acm->transport) {
+	case USB_GADGET_XPORT_TTY:
+		gserial_connect(&acm->port, port_num);
+		break;
+	case USB_GADGET_XPORT_SDIO:
+		gsdio_connect(&acm->port, port_num);
+		break;
+	case USB_GADGET_XPORT_SMD:
+		gsmd_connect(&acm->port, port_num);
+		break;
+	default:
+		pr_err("%s: Un-supported transport: %s\n", __func__,
+				xport_to_str(acm->transport));
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int acm_port_disconnect(struct f_acm *acm)
+{
+	unsigned port_num;
+
+	port_num = gacm_ports[acm->port_num].client_port_num;
+
+	pr_debug("%s: transport:%s f_acm:%p gserial:%p port_num:%d cl_pno:%d\n",
+			__func__, xport_to_str(acm->transport),
+			acm, &acm->port, acm->port_num, port_num);
+
+	switch (acm->transport) {
+	case USB_GADGET_XPORT_TTY:
+		gserial_disconnect(&acm->port);
+		break;
+	case USB_GADGET_XPORT_SDIO:
+		gsdio_disconnect(&acm->port, port_num);
+		break;
+	case USB_GADGET_XPORT_SMD:
+		gsmd_disconnect(&acm->port, port_num);
+		break;
+	default:
+		pr_err("%s: Un-supported transport:%s\n", __func__,
+				xport_to_str(acm->transport));
+		return -ENODEV;
+	}
+
+	return 0;
+}
 /*-------------------------------------------------------------------------*/
 
 /* notification endpoint uses smallish and infrequent fixed-size messages */
@@ -359,8 +450,7 @@
 	/* SET_LINE_CODING ... just read and save what the host sends */
 	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
 			| USB_CDC_REQ_SET_LINE_CODING:
-		if (w_length != sizeof(struct usb_cdc_line_coding)
-				|| w_index != acm->ctrl_id)
+		if (w_length != sizeof(struct usb_cdc_line_coding))
 			goto invalid;
 
 		value = w_length;
@@ -371,8 +461,6 @@
 	/* GET_LINE_CODING ... return what host sent, or initial value */
 	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
 			| USB_CDC_REQ_GET_LINE_CODING:
-		if (w_index != acm->ctrl_id)
-			goto invalid;
 
 		value = min_t(unsigned, w_length,
 				sizeof(struct usb_cdc_line_coding));
@@ -382,9 +470,6 @@
 	/* SET_CONTROL_LINE_STATE ... save what the host sent */
 	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
 			| USB_CDC_REQ_SET_CONTROL_LINE_STATE:
-		if (w_index != acm->ctrl_id)
-			goto invalid;
-
 		value = 0;
 
 		/* FIXME we should not allow data to flow until the
@@ -392,6 +477,12 @@
 		 * that bit, we should return to that no-flow state.
 		 */
 		acm->port_handshake_bits = w_value;
+		if (acm->port.notify_modem) {
+			unsigned port_num =
+				gacm_ports[acm->port_num].client_port_num;
+
+			acm->port.notify_modem(&acm->port, port_num, w_value);
+		}
 		break;
 
 	default:
@@ -431,16 +522,17 @@
 			usb_ep_disable(acm->notify);
 		} else {
 			VDBG(cdev, "init acm ctrl interface %d\n", intf);
-			if (config_ep_by_speed(cdev->gadget, f, acm->notify))
-				return -EINVAL;
 		}
+		if (config_ep_by_speed(cdev->gadget, f, acm->notify))
+			return -EINVAL;
+
 		usb_ep_enable(acm->notify);
 		acm->notify->driver_data = acm;
 
 	} else if (intf == acm->data_id) {
 		if (acm->port.in->driver_data) {
 			DBG(cdev, "reset acm ttyGS%d\n", acm->port_num);
-			gserial_disconnect(&acm->port);
+			acm_port_disconnect(acm);
 		}
 		if (!acm->port.in->desc || !acm->port.out->desc) {
 			DBG(cdev, "activate acm ttyGS%d\n", acm->port_num);
@@ -453,7 +545,16 @@
 				return -EINVAL;
 			}
 		}
-		gserial_connect(&acm->port, acm->port_num);
+		if (config_ep_by_speed(cdev->gadget, f,
+				acm->port.in) ||
+			config_ep_by_speed(cdev->gadget, f,
+				acm->port.out)) {
+			acm->port.in->desc = NULL;
+			acm->port.out->desc = NULL;
+			return -EINVAL;
+		}
+
+		acm_port_connect(acm);
 
 	} else
 		return -EINVAL;
@@ -467,7 +568,7 @@
 	struct usb_composite_dev *cdev = f->config->cdev;
 
 	DBG(cdev, "acm ttyGS%d deactivated\n", acm->port_num);
-	gserial_disconnect(&acm->port);
+	acm_port_disconnect(acm);
 	usb_ep_disable(acm->notify);
 	acm->notify->driver_data = NULL;
 }
@@ -598,6 +699,15 @@
 	return acm_notify_serial_state(acm);
 }
 
+static int acm_send_modem_ctrl_bits(struct gserial *port, int ctrl_bits)
+{
+	struct f_acm *acm = port_to_acm(port);
+
+	acm->serial_state = ctrl_bits;
+
+	return acm_notify_serial_state(acm);
+}
+
 /*-------------------------------------------------------------------------*/
 
 /* ACM function driver setup/binding */
@@ -678,6 +788,8 @@
 
 		/* copy descriptors */
 		f->hs_descriptors = usb_copy_descriptors(acm_hs_function);
+		if (!f->hs_descriptors)
+			goto fail;
 	}
 	if (gadget_is_superspeed(c->cdev->gadget)) {
 		acm_ss_in_desc.bEndpointAddress =
@@ -700,6 +812,11 @@
 	return 0;
 
 fail:
+	if (f->hs_descriptors)
+		usb_free_descriptors(f->hs_descriptors);
+	if (f->descriptors)
+		usb_free_descriptors(f->descriptors);
+
 	if (acm->notify_req)
 		gs_free_req(acm->notify, acm->notify_req);
 
@@ -727,6 +844,7 @@
 		usb_free_descriptors(f->ss_descriptors);
 	usb_free_descriptors(f->descriptors);
 	gs_free_req(acm->notify, acm->notify_req);
+	kfree(acm->port.func.name);
 	kfree(acm);
 }
 
@@ -793,12 +911,18 @@
 	spin_lock_init(&acm->lock);
 
 	acm->port_num = port_num;
+	acm->transport = gacm_ports[port_num].transport;
 
 	acm->port.connect = acm_connect;
 	acm->port.disconnect = acm_disconnect;
 	acm->port.send_break = acm_send_break;
+	acm->port.send_modem_ctrl_bits = acm_send_modem_ctrl_bits;
 
-	acm->port.func.name = "acm";
+	acm->port.func.name = kasprintf(GFP_KERNEL, "acm%u", port_num + 1);
+	if (!acm->port.func.name) {
+		kfree(acm);
+		return -ENOMEM;
+	}
 	acm->port.func.strings = acm_strings;
 	/* descriptors are per-instance copies */
 	acm->port.func.bind = acm_bind;
@@ -812,3 +936,44 @@
 		kfree(acm);
 	return status;
 }
+
+/**
+ * acm_init_port - bind a acm_port to its transport
+ */
+static int acm_init_port(int port_num, const char *name)
+{
+	enum transport_type transport;
+
+	if (port_num >= GSERIAL_NO_PORTS)
+		return -ENODEV;
+
+	transport = str_to_xport(name);
+	pr_debug("%s, port:%d, transport:%s\n", __func__,
+			port_num, xport_to_str(transport));
+
+	gacm_ports[port_num].transport = transport;
+	gacm_ports[port_num].port_num = port_num;
+
+	switch (transport) {
+	case USB_GADGET_XPORT_TTY:
+		gacm_ports[port_num].client_port_num = no_acm_tty_ports;
+		no_acm_tty_ports++;
+		break;
+	case USB_GADGET_XPORT_SDIO:
+		gacm_ports[port_num].client_port_num = no_acm_sdio_ports;
+		no_acm_sdio_ports++;
+		break;
+	case USB_GADGET_XPORT_SMD:
+		gacm_ports[port_num].client_port_num = no_acm_smd_ports;
+		no_acm_smd_ports++;
+		break;
+	default:
+		pr_err("%s: Un-supported transport transport: %u\n",
+				__func__, gacm_ports[port_num].transport);
+		return -ENODEV;
+	}
+
+	nr_acm_ports++;
+
+	return 0;
+}
diff --git a/drivers/usb/gadget/f_adb.c b/drivers/usb/gadget/f_adb.c
index 1629ffb..9778673 100644
--- a/drivers/usb/gadget/f_adb.c
+++ b/drivers/usb/gadget/f_adb.c
@@ -42,8 +42,8 @@
 	struct usb_ep *ep_in;
 	struct usb_ep *ep_out;
 
-	int online;
-	int error;
+	atomic_t online;
+	atomic_t error;
 
 	atomic_t read_excl;
 	atomic_t write_excl;
@@ -195,7 +195,7 @@
 	struct adb_dev *dev = _adb_dev;
 
 	if (req->status != 0)
-		dev->error = 1;
+		atomic_set(&dev->error, 1);
 
 	adb_req_put(dev, &dev->tx_idle, req);
 
@@ -208,7 +208,7 @@
 
 	dev->rx_done = 1;
 	if (req->status != 0 && req->status != -ECONNRESET)
-		dev->error = 1;
+		atomic_set(&dev->error, 1);
 
 	wake_up(&dev->read_wq);
 }
@@ -283,16 +283,17 @@
 		return -EBUSY;
 
 	/* we will block until we're online */
-	while (!(dev->online || dev->error)) {
+	while (!(atomic_read(&dev->online) || atomic_read(&dev->error))) {
 		pr_debug("adb_read: waiting for online state\n");
 		ret = wait_event_interruptible(dev->read_wq,
-				(dev->online || dev->error));
+			(atomic_read(&dev->online) ||
+			atomic_read(&dev->error)));
 		if (ret < 0) {
 			adb_unlock(&dev->read_excl);
 			return ret;
 		}
 	}
-	if (dev->error) {
+	if (atomic_read(&dev->error)) {
 		r = -EIO;
 		goto done;
 	}
@@ -306,7 +307,7 @@
 	if (ret < 0) {
 		pr_debug("adb_read: failed to queue req %p (%d)\n", req, ret);
 		r = -EIO;
-		dev->error = 1;
+		atomic_set(&dev->error, 1);
 		goto done;
 	} else {
 		pr_debug("rx %p queue\n", req);
@@ -316,12 +317,12 @@
 	ret = wait_event_interruptible(dev->read_wq, dev->rx_done);
 	if (ret < 0) {
 		if (ret != -ERESTARTSYS)
-			dev->error = 1;
+		atomic_set(&dev->error, 1);
 		r = ret;
 		usb_ep_dequeue(dev->ep_out, req);
 		goto done;
 	}
-	if (!dev->error) {
+	if (!atomic_read(&dev->error)) {
 		/* If we got a 0-len packet, throw it back and try again. */
 		if (req->actual == 0)
 			goto requeue_req;
@@ -356,7 +357,7 @@
 		return -EBUSY;
 
 	while (count > 0) {
-		if (dev->error) {
+		if (atomic_read(&dev->error)) {
 			pr_debug("adb_write dev->error\n");
 			r = -EIO;
 			break;
@@ -365,7 +366,8 @@
 		/* get an idle tx request to use */
 		req = 0;
 		ret = wait_event_interruptible(dev->write_wq,
-			(req = adb_req_get(dev, &dev->tx_idle)) || dev->error);
+			((req = adb_req_get(dev, &dev->tx_idle)) ||
+			 atomic_read(&dev->error)));
 
 		if (ret < 0) {
 			r = ret;
@@ -386,7 +388,7 @@
 			ret = usb_ep_queue(dev->ep_in, req, GFP_ATOMIC);
 			if (ret < 0) {
 				pr_debug("adb_write: xfer error %d\n", ret);
-				dev->error = 1;
+				atomic_set(&dev->error, 1);
 				r = -EIO;
 				break;
 			}
@@ -419,7 +421,7 @@
 	fp->private_data = _adb_dev;
 
 	/* clear the error latch */
-	_adb_dev->error = 0;
+	atomic_set(&_adb_dev->error, 0);
 
 	adb_ready_callback();
 
@@ -498,8 +500,8 @@
 	struct usb_request *req;
 
 
-	dev->online = 0;
-	dev->error = 1;
+	atomic_set(&dev->online, 0);
+	atomic_set(&dev->error, 1);
 
 	wake_up(&dev->read_wq);
 
@@ -518,23 +520,35 @@
 	DBG(cdev, "adb_function_set_alt intf: %d alt: %d\n", intf, alt);
 
 	ret = config_ep_by_speed(cdev->gadget, f, dev->ep_in);
-	if (ret)
+	if (ret) {
+		dev->ep_in->desc = NULL;
+		ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
+				dev->ep_in->name, ret);
 		return ret;
-
+	}
 	ret = usb_ep_enable(dev->ep_in);
-	if (ret)
+	if (ret) {
+		ERROR(cdev, "failed to enable ep %s, result %d\n",
+			dev->ep_in->name, ret);
 		return ret;
+	}
 
 	ret = config_ep_by_speed(cdev->gadget, f, dev->ep_out);
-	if (ret)
-		return ret;
-
-	ret = usb_ep_enable(dev->ep_out);
 	if (ret) {
+		dev->ep_out->desc = NULL;
+		ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
+			dev->ep_out->name, ret);
 		usb_ep_disable(dev->ep_in);
 		return ret;
 	}
-	dev->online = 1;
+	ret = usb_ep_enable(dev->ep_out);
+	if (ret) {
+		ERROR(cdev, "failed to enable ep %s, result %d\n",
+				dev->ep_out->name, ret);
+		usb_ep_disable(dev->ep_in);
+		return ret;
+	}
+	atomic_set(&dev->online, 1);
 
 	/* readers may be blocked waiting for us to go online */
 	wake_up(&dev->read_wq);
@@ -547,8 +561,8 @@
 	struct usb_composite_dev	*cdev = dev->cdev;
 
 	DBG(cdev, "adb_function_disable cdev %p\n", cdev);
-	dev->online = 0;
-	dev->error = 1;
+	atomic_set(&dev->online, 0);
+	atomic_set(&dev->error, 1);
 	usb_ep_disable(dev->ep_in);
 	usb_ep_disable(dev->ep_out);
 
diff --git a/drivers/usb/gadget/f_ccid.c b/drivers/usb/gadget/f_ccid.c
new file mode 100644
index 0000000..c8f144a
--- /dev/null
+++ b/drivers/usb/gadget/f_ccid.c
@@ -0,0 +1,999 @@
+/*
+ * f_ccid.c -- CCID function Driver
+ *
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/usb/android_composite.h>
+#include <linux/fs.h>
+#include <linux/usb/ccid_desc.h>
+#include <linux/miscdevice.h>
+
+#include "f_ccid.h"
+
+#define BULK_IN_BUFFER_SIZE sizeof(struct ccid_bulk_in_header)
+#define BULK_OUT_BUFFER_SIZE sizeof(struct ccid_bulk_out_header)
+#define CTRL_BUF_SIZE	4
+#define FUNCTION_NAME	"ccid"
+#define CCID_NOTIFY_INTERVAL	5
+#define CCID_NOTIFY_MAXPACKET	4
+
+/* number of tx requests to allocate */
+#define TX_REQ_MAX 4
+
+struct ccid_ctrl_dev {
+	atomic_t opened;
+	struct list_head tx_q;
+	wait_queue_head_t tx_wait_q;
+	unsigned char buf[CTRL_BUF_SIZE];
+	int tx_ctrl_done;
+};
+
+struct ccid_bulk_dev {
+	atomic_t error;
+	atomic_t opened;
+	atomic_t rx_req_busy;
+	wait_queue_head_t read_wq;
+	wait_queue_head_t write_wq;
+	struct usb_request *rx_req;
+	int rx_done;
+	struct list_head tx_idle;
+};
+
+struct f_ccid {
+	struct usb_function function;
+	struct usb_composite_dev *cdev;
+	int ifc_id;
+	spinlock_t lock;
+	atomic_t online;
+	/* usb eps*/
+	struct usb_ep *notify;
+	struct usb_ep *in;
+	struct usb_ep *out;
+	struct usb_request *notify_req;
+	struct ccid_ctrl_dev ctrl_dev;
+	struct ccid_bulk_dev bulk_dev;
+	int dtr_state;
+};
+
+static struct f_ccid *_ccid_dev;
+static struct miscdevice ccid_bulk_device;
+static struct miscdevice ccid_ctrl_device;
+
+/* Interface Descriptor: */
+static struct usb_interface_descriptor ccid_interface_desc = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	.bNumEndpoints =	3,
+	.bInterfaceClass =	USB_CLASS_CSCID,
+	.bInterfaceSubClass =	0,
+	.bInterfaceProtocol =	0,
+};
+/* CCID Class Descriptor */
+static struct usb_ccid_class_descriptor ccid_class_desc = {
+	.bLength =		sizeof(ccid_class_desc),
+	.bDescriptorType =	CCID_DECRIPTOR_TYPE,
+	.bcdCCID =		CCID1_10,
+	.bMaxSlotIndex =	0,
+	/* This value indicates what voltages the CCID can supply to slots */
+	.bVoltageSupport =	VOLTS_3_0,
+	.dwProtocols =		PROTOCOL_TO,
+	/* Default ICC clock frequency in KHz */
+	.dwDefaultClock =	3580,
+	/* Maximum supported ICC clock frequency in KHz */
+	.dwMaximumClock =	3580,
+	.bNumClockSupported =	0,
+	/* Default ICC I/O data rate in bps */
+	.dwDataRate =		9600,
+	/* Maximum supported ICC I/O data rate in bps */
+	.dwMaxDataRate =	9600,
+	.bNumDataRatesSupported = 0,
+	.dwMaxIFSD =		0,
+	.dwSynchProtocols =	0,
+	.dwMechanical =		0,
+	/* This value indicates what intelligent features the CCID has */
+	.dwFeatures =		CCID_FEATURES_EXC_SAPDU |
+				CCID_FEATURES_AUTO_PNEGO |
+				CCID_FEATURES_AUTO_BAUD |
+				CCID_FEATURES_AUTO_CLOCK |
+				CCID_FEATURES_AUTO_VOLT |
+				CCID_FEATURES_AUTO_ACTIV |
+				CCID_FEATURES_AUTO_PCONF,
+	/* extended APDU level Message Length */
+	.dwMaxCCIDMessageLength = 0x200,
+	.bClassGetResponse =	0x0,
+	.bClassEnvelope =	0x0,
+	.wLcdLayout =		0,
+	.bPINSupport =		0,
+	.bMaxCCIDBusySlots =	1
+};
+/* Full speed support: */
+static struct usb_endpoint_descriptor ccid_fs_notify_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	__constant_cpu_to_le16(CCID_NOTIFY_MAXPACKET),
+	.bInterval =		1 << CCID_NOTIFY_INTERVAL,
+};
+
+static struct usb_endpoint_descriptor ccid_fs_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize   =	__constant_cpu_to_le16(64),
+};
+
+static struct usb_endpoint_descriptor ccid_fs_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize   =	 __constant_cpu_to_le16(64),
+};
+
+static struct usb_descriptor_header *ccid_fs_descs[] = {
+	(struct usb_descriptor_header *) &ccid_interface_desc,
+	(struct usb_descriptor_header *) &ccid_class_desc,
+	(struct usb_descriptor_header *) &ccid_fs_notify_desc,
+	(struct usb_descriptor_header *) &ccid_fs_in_desc,
+	(struct usb_descriptor_header *) &ccid_fs_out_desc,
+	NULL,
+};
+
+/* High speed support: */
+static struct usb_endpoint_descriptor ccid_hs_notify_desc  = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	__constant_cpu_to_le16(CCID_NOTIFY_MAXPACKET),
+	.bInterval =		CCID_NOTIFY_INTERVAL + 4,
+};
+
+static struct usb_endpoint_descriptor ccid_hs_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	__constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor ccid_hs_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	__constant_cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *ccid_hs_descs[] = {
+	(struct usb_descriptor_header *) &ccid_interface_desc,
+	(struct usb_descriptor_header *) &ccid_class_desc,
+	(struct usb_descriptor_header *) &ccid_hs_notify_desc,
+	(struct usb_descriptor_header *) &ccid_hs_in_desc,
+	(struct usb_descriptor_header *) &ccid_hs_out_desc,
+	NULL,
+};
+
+static inline struct f_ccid *func_to_ccid(struct usb_function *f)
+{
+	return container_of(f, struct f_ccid, function);
+}
+
+static void ccid_req_put(struct f_ccid *ccid_dev, struct list_head *head,
+		struct usb_request *req)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&ccid_dev->lock, flags);
+	list_add_tail(&req->list, head);
+	spin_unlock_irqrestore(&ccid_dev->lock, flags);
+}
+
+static struct usb_request *ccid_req_get(struct f_ccid *ccid_dev,
+					struct list_head *head)
+{
+	unsigned long flags;
+	struct usb_request *req = NULL;
+
+	spin_lock_irqsave(&ccid_dev->lock, flags);
+	if (!list_empty(head)) {
+		req = list_first_entry(head, struct usb_request, list);
+		list_del(&req->list);
+	}
+	spin_unlock_irqrestore(&ccid_dev->lock, flags);
+	return req;
+}
+
+static void ccid_notify_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	switch (req->status) {
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+	case 0:
+		break;
+	default:
+		pr_err("CCID notify ep error %d\n", req->status);
+	}
+}
+
+static void ccid_bulk_complete_in(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_ccid *ccid_dev = _ccid_dev;
+	struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev;
+
+	if (req->status != 0)
+		atomic_set(&bulk_dev->error, 1);
+
+	ccid_req_put(ccid_dev, &bulk_dev->tx_idle, req);
+	wake_up(&bulk_dev->write_wq);
+}
+
+static void ccid_bulk_complete_out(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_ccid *ccid_dev = _ccid_dev;
+	struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev;
+	if (req->status != 0)
+		atomic_set(&bulk_dev->error, 1);
+
+	bulk_dev->rx_done = 1;
+	wake_up(&bulk_dev->read_wq);
+}
+
+static struct usb_request *
+ccid_request_alloc(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags)
+{
+	struct usb_request *req;
+
+	req = usb_ep_alloc_request(ep, kmalloc_flags);
+
+	if (req != NULL) {
+		req->length = len;
+		req->buf = kmalloc(len, kmalloc_flags);
+		if (req->buf == NULL) {
+			usb_ep_free_request(ep, req);
+			req = NULL;
+		}
+	}
+
+	return req ? req : ERR_PTR(-ENOMEM);
+}
+
+static void ccid_request_free(struct usb_request *req, struct usb_ep *ep)
+{
+	if (req) {
+		kfree(req->buf);
+		usb_ep_free_request(ep, req);
+	}
+}
+
+static int
+ccid_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+	struct f_ccid *ccid_dev = container_of(f, struct f_ccid, function);
+	struct ccid_ctrl_dev *ctrl_dev = &ccid_dev->ctrl_dev;
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct usb_request      *req = cdev->req;
+	int ret = -EOPNOTSUPP;
+	u16 w_index = le16_to_cpu(ctrl->wIndex);
+	u16 w_value = le16_to_cpu(ctrl->wValue);
+	u16 w_length = le16_to_cpu(ctrl->wLength);
+
+	if (!atomic_read(&ccid_dev->online))
+		return -ENOTCONN;
+
+	switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| CCIDGENERICREQ_ABORT:
+		if (w_length != 0)
+			goto invalid;
+		ctrl_dev->buf[0] = CCIDGENERICREQ_ABORT;
+		ctrl_dev->buf[1] = w_value & 0xFF;
+		ctrl_dev->buf[2] = (w_value >> 8) & 0xFF;
+		ctrl_dev->buf[3] = 0x00;
+		ctrl_dev->tx_ctrl_done = 1;
+		wake_up(&ctrl_dev->tx_wait_q);
+		return 0;
+
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| CCIDGENERICREQ_GET_CLOCK_FREQUENCIES:
+		if (w_length > req->length)
+			goto invalid;
+		*(u32 *) req->buf =
+				cpu_to_le32(ccid_class_desc.dwDefaultClock);
+		ret = min_t(u32, w_length,
+				sizeof(ccid_class_desc.dwDefaultClock));
+		break;
+
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| CCIDGENERICREQ_GET_DATA_RATES:
+		if (w_length > req->length)
+			goto invalid;
+		*(u32 *) req->buf = cpu_to_le32(ccid_class_desc.dwDataRate);
+		ret = min_t(u32, w_length, sizeof(ccid_class_desc.dwDataRate));
+		break;
+
+	default:
+invalid:
+	pr_debug("invalid control req%02x.%02x v%04x i%04x l%d\n",
+		ctrl->bRequestType, ctrl->bRequest,
+		w_value, w_index, w_length);
+	}
+
+	/* respond with data transfer or status phase? */
+	if (ret >= 0) {
+		pr_debug("ccid req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+		req->length = ret;
+		ret = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+		if (ret < 0)
+			pr_err("ccid ep0 enqueue err %d\n", ret);
+	}
+
+	return ret;
+}
+
+static void ccid_function_disable(struct usb_function *f)
+{
+	struct f_ccid *ccid_dev = func_to_ccid(f);
+	struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev;
+	struct ccid_ctrl_dev *ctrl_dev = &ccid_dev->ctrl_dev;
+	struct usb_request *req;
+
+	/* Disable endpoints */
+	usb_ep_disable(ccid_dev->notify);
+	usb_ep_disable(ccid_dev->in);
+	usb_ep_disable(ccid_dev->out);
+	/* Free endpoint related requests */
+	ccid_request_free(ccid_dev->notify_req, ccid_dev->notify);
+	if (!atomic_read(&bulk_dev->rx_req_busy))
+		ccid_request_free(bulk_dev->rx_req, ccid_dev->out);
+	while ((req = ccid_req_get(ccid_dev, &bulk_dev->tx_idle)))
+		ccid_request_free(req, ccid_dev->in);
+
+	ccid_dev->dtr_state = 0;
+	atomic_set(&ccid_dev->online, 0);
+	/* Wake up threads */
+	wake_up(&bulk_dev->write_wq);
+	wake_up(&bulk_dev->read_wq);
+	wake_up(&ctrl_dev->tx_wait_q);
+
+}
+
+static int
+ccid_function_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+	struct f_ccid *ccid_dev = func_to_ccid(f);
+	struct usb_composite_dev *cdev = ccid_dev->cdev;
+	struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev;
+	struct usb_request *req;
+	int ret = 0;
+	int i;
+
+	ccid_dev->notify_req = ccid_request_alloc(ccid_dev->notify,
+			sizeof(struct usb_ccid_notification), GFP_ATOMIC);
+	if (IS_ERR(ccid_dev->notify_req)) {
+		pr_err("%s: unable to allocate memory for notify req\n",
+				__func__);
+		return PTR_ERR(ccid_dev->notify_req);
+	}
+	ccid_dev->notify_req->complete = ccid_notify_complete;
+	ccid_dev->notify_req->context = ccid_dev;
+
+	/* now allocate requests for our endpoints */
+	req = ccid_request_alloc(ccid_dev->out, BULK_OUT_BUFFER_SIZE,
+							GFP_ATOMIC);
+	if (IS_ERR(req)) {
+		pr_err("%s: unable to allocate memory for out req\n",
+				__func__);
+		ret = PTR_ERR(req);
+		goto free_notify;
+	}
+	req->complete = ccid_bulk_complete_out;
+	req->context = ccid_dev;
+	bulk_dev->rx_req = req;
+
+	for (i = 0; i < TX_REQ_MAX; i++) {
+		req = ccid_request_alloc(ccid_dev->in, BULK_IN_BUFFER_SIZE,
+								GFP_ATOMIC);
+		if (IS_ERR(req)) {
+			pr_err("%s: unable to allocate memory for in req\n",
+					__func__);
+			ret = PTR_ERR(req);
+			goto free_bulk_out;
+		}
+		req->complete = ccid_bulk_complete_in;
+		req->context = ccid_dev;
+		ccid_req_put(ccid_dev, &bulk_dev->tx_idle, req);
+	}
+
+	/* choose the descriptors and enable endpoints */
+	ret = config_ep_by_speed(cdev->gadget, f, ccid_dev->notify);
+	if (ret) {
+		ccid_dev->notify->desc = NULL;
+		pr_err("%s: config_ep_by_speed failed for ep#%s, err#%d\n",
+				__func__, ccid_dev->notify->name, ret);
+		goto free_bulk_in;
+	}
+	ret = usb_ep_enable(ccid_dev->notify);
+	if (ret) {
+		pr_err("%s: usb ep#%s enable failed, err#%d\n",
+				__func__, ccid_dev->notify->name, ret);
+		goto free_bulk_in;
+	}
+	ccid_dev->notify->driver_data = ccid_dev;
+
+	ret = config_ep_by_speed(cdev->gadget, f, ccid_dev->in);
+	if (ret) {
+		ccid_dev->in->desc = NULL;
+		pr_err("%s: config_ep_by_speed failed for ep#%s, err#%d\n",
+				__func__, ccid_dev->in->name, ret);
+		goto disable_ep_notify;
+	}
+	ret = usb_ep_enable(ccid_dev->in);
+	if (ret) {
+		pr_err("%s: usb ep#%s enable failed, err#%d\n",
+				__func__, ccid_dev->in->name, ret);
+		goto disable_ep_notify;
+	}
+
+	ret = config_ep_by_speed(cdev->gadget, f, ccid_dev->out);
+	if (ret) {
+		ccid_dev->out->desc = NULL;
+		pr_err("%s: config_ep_by_speed failed for ep#%s, err#%d\n",
+				__func__, ccid_dev->out->name, ret);
+		goto disable_ep_in;
+	}
+	ret = usb_ep_enable(ccid_dev->out);
+	if (ret) {
+		pr_err("%s: usb ep#%s enable failed, err#%d\n",
+				__func__, ccid_dev->out->name, ret);
+		goto disable_ep_in;
+	}
+	ccid_dev->dtr_state = 1;
+	atomic_set(&ccid_dev->online, 1);
+	return ret;
+
+disable_ep_in:
+	usb_ep_disable(ccid_dev->in);
+disable_ep_notify:
+	usb_ep_disable(ccid_dev->notify);
+	ccid_dev->notify->driver_data = NULL;
+free_bulk_in:
+	while ((req = ccid_req_get(ccid_dev, &bulk_dev->tx_idle)))
+		ccid_request_free(req, ccid_dev->in);
+free_bulk_out:
+	ccid_request_free(bulk_dev->rx_req, ccid_dev->out);
+free_notify:
+	ccid_request_free(ccid_dev->notify_req, ccid_dev->notify);
+	return ret;
+}
+
+static void ccid_function_unbind(struct usb_configuration *c,
+					struct usb_function *f)
+{
+	if (gadget_is_dualspeed(c->cdev->gadget))
+		usb_free_descriptors(f->hs_descriptors);
+	usb_free_descriptors(f->descriptors);
+
+}
+
+static int ccid_function_bind(struct usb_configuration *c,
+					struct usb_function *f)
+{
+	struct f_ccid *ccid_dev = func_to_ccid(f);
+	struct usb_ep *ep;
+	struct usb_composite_dev *cdev = c->cdev;
+	int ret = -ENODEV;
+
+	ccid_dev->ifc_id = usb_interface_id(c, f);
+	if (ccid_dev->ifc_id < 0) {
+		pr_err("%s: unable to allocate ifc id, err:%d",
+				__func__, ccid_dev->ifc_id);
+		return ccid_dev->ifc_id;
+	}
+	ccid_interface_desc.bInterfaceNumber = ccid_dev->ifc_id;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &ccid_fs_notify_desc);
+	if (!ep) {
+		pr_err("%s: usb epnotify autoconfig failed\n", __func__);
+		return -ENODEV;
+	}
+	ccid_dev->notify = ep;
+	ep->driver_data = cdev;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &ccid_fs_in_desc);
+	if (!ep) {
+		pr_err("%s: usb epin autoconfig failed\n", __func__);
+		ret = -ENODEV;
+		goto ep_auto_in_fail;
+	}
+	ccid_dev->in = ep;
+	ep->driver_data = cdev;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &ccid_fs_out_desc);
+	if (!ep) {
+		pr_err("%s: usb epout autoconfig failed\n", __func__);
+		ret = -ENODEV;
+		goto ep_auto_out_fail;
+	}
+	ccid_dev->out = ep;
+	ep->driver_data = cdev;
+
+	f->descriptors = usb_copy_descriptors(ccid_fs_descs);
+	if (!f->descriptors)
+		goto ep_auto_out_fail;
+
+	if (gadget_is_dualspeed(cdev->gadget)) {
+		ccid_hs_in_desc.bEndpointAddress =
+				ccid_fs_in_desc.bEndpointAddress;
+		ccid_hs_out_desc.bEndpointAddress =
+				ccid_fs_out_desc.bEndpointAddress;
+		ccid_hs_notify_desc.bEndpointAddress =
+				ccid_fs_notify_desc.bEndpointAddress;
+
+		/* copy descriptors, and track endpoint copies */
+		f->hs_descriptors = usb_copy_descriptors(ccid_hs_descs);
+		if (!f->hs_descriptors)
+			goto ep_auto_out_fail;
+	}
+
+	pr_debug("%s: CCID %s Speed, IN:%s OUT:%s\n", __func__,
+			gadget_is_dualspeed(cdev->gadget) ? "dual" : "full",
+			ccid_dev->in->name, ccid_dev->out->name);
+
+	return 0;
+
+ep_auto_out_fail:
+	ccid_dev->out->driver_data = NULL;
+	ccid_dev->out = NULL;
+ep_auto_in_fail:
+	ccid_dev->in->driver_data = NULL;
+	ccid_dev->in = NULL;
+
+	return ret;
+}
+
+static int ccid_bulk_open(struct inode *ip, struct file *fp)
+{
+	struct f_ccid *ccid_dev = _ccid_dev;
+	struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev;
+	unsigned long flags;
+
+	pr_debug("ccid_bulk_open\n");
+	if (!atomic_read(&ccid_dev->online)) {
+		pr_debug("%s: USB cable not connected\n", __func__);
+		return -ENODEV;
+	}
+
+	if (atomic_read(&bulk_dev->opened)) {
+		pr_debug("%s: bulk device is already opened\n", __func__);
+		return -EBUSY;
+	}
+	atomic_set(&bulk_dev->opened, 1);
+	/* clear the error latch */
+	atomic_set(&bulk_dev->error, 0);
+	spin_lock_irqsave(&ccid_dev->lock, flags);
+	fp->private_data = ccid_dev;
+	spin_unlock_irqrestore(&ccid_dev->lock, flags);
+
+	return 0;
+}
+
+static int ccid_bulk_release(struct inode *ip, struct file *fp)
+{
+	struct f_ccid *ccid_dev =  fp->private_data;
+	struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev;
+
+	pr_debug("ccid_bulk_release\n");
+	atomic_set(&bulk_dev->opened, 0);
+	return 0;
+}
+
+static ssize_t ccid_bulk_read(struct file *fp, char __user *buf,
+				size_t count, loff_t *pos)
+{
+	struct f_ccid *ccid_dev =  fp->private_data;
+	struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev;
+	struct usb_request *req;
+	int r = count, xfer;
+	int ret;
+	unsigned long flags;
+
+	pr_debug("ccid_bulk_read(%d)\n", count);
+
+	if (count > BULK_OUT_BUFFER_SIZE) {
+		pr_err("%s: max_buffer_size:%d given_pkt_size:%d\n",
+				__func__, BULK_OUT_BUFFER_SIZE, count);
+		return -ENOMEM;
+	}
+
+	if (atomic_read(&bulk_dev->error)) {
+		r = -EIO;
+		pr_err("%s bulk_dev_error\n", __func__);
+		goto done;
+	}
+
+requeue_req:
+	spin_lock_irqsave(&ccid_dev->lock, flags);
+	if (!atomic_read(&ccid_dev->online)) {
+		pr_debug("%s: USB cable not connected\n", __func__);
+		return -ENODEV;
+	}
+	/* queue a request */
+	req = bulk_dev->rx_req;
+	req->length = count;
+	bulk_dev->rx_done = 0;
+	spin_unlock_irqrestore(&ccid_dev->lock, flags);
+	ret = usb_ep_queue(ccid_dev->out, req, GFP_KERNEL);
+	if (ret < 0) {
+		r = -EIO;
+		pr_err("%s usb ep queue failed\n", __func__);
+		atomic_set(&bulk_dev->error, 1);
+		goto done;
+	}
+	/* wait for a request to complete */
+	ret = wait_event_interruptible(bulk_dev->read_wq, bulk_dev->rx_done ||
+					atomic_read(&bulk_dev->error) ||
+					!atomic_read(&ccid_dev->online));
+	if (ret < 0) {
+		atomic_set(&bulk_dev->error, 1);
+		r = ret;
+		usb_ep_dequeue(ccid_dev->out, req);
+		goto done;
+	}
+	if (!atomic_read(&bulk_dev->error)) {
+		spin_lock_irqsave(&ccid_dev->lock, flags);
+		if (!atomic_read(&ccid_dev->online)) {
+			spin_unlock_irqrestore(&ccid_dev->lock, flags);
+			pr_debug("%s: USB cable not connected\n", __func__);
+			r = -ENODEV;
+			goto done;
+		}
+		/* If we got a 0-len packet, throw it back and try again. */
+		if (req->actual == 0) {
+			spin_unlock_irqrestore(&ccid_dev->lock, flags);
+			goto requeue_req;
+		}
+		xfer = (req->actual < count) ? req->actual : count;
+		atomic_set(&bulk_dev->rx_req_busy, 1);
+		spin_unlock_irqrestore(&ccid_dev->lock, flags);
+
+		if (copy_to_user(buf, req->buf, xfer))
+			r = -EFAULT;
+
+		spin_lock_irqsave(&ccid_dev->lock, flags);
+		atomic_set(&bulk_dev->rx_req_busy, 0);
+		if (!atomic_read(&ccid_dev->online)) {
+			ccid_request_free(bulk_dev->rx_req, ccid_dev->out);
+			spin_unlock_irqrestore(&ccid_dev->lock, flags);
+			pr_debug("%s: USB cable not connected\n", __func__);
+			r = -ENODEV;
+			goto done;
+		}
+		spin_unlock_irqrestore(&ccid_dev->lock, flags);
+	} else {
+		r = -EIO;
+	}
+done:
+	pr_debug("ccid_bulk_read returning %d\n", r);
+	return r;
+}
+
+static ssize_t ccid_bulk_write(struct file *fp, const char __user *buf,
+				 size_t count, loff_t *pos)
+{
+	struct f_ccid *ccid_dev =  fp->private_data;
+	struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev;
+	struct usb_request *req = 0;
+	int r = count;
+	int ret;
+	unsigned long flags;
+
+	pr_debug("ccid_bulk_write(%d)\n", count);
+
+	if (!atomic_read(&ccid_dev->online)) {
+		pr_debug("%s: USB cable not connected\n", __func__);
+		return -ENODEV;
+	}
+
+	if (!count) {
+		pr_err("%s: zero length ctrl pkt\n", __func__);
+		return -ENODEV;
+	}
+	if (count > BULK_IN_BUFFER_SIZE) {
+		pr_err("%s: max_buffer_size:%d given_pkt_size:%d\n",
+				__func__, BULK_IN_BUFFER_SIZE, count);
+		return -ENOMEM;
+	}
+
+
+	/* get an idle tx request to use */
+	ret = wait_event_interruptible(bulk_dev->write_wq,
+		((req = ccid_req_get(ccid_dev, &bulk_dev->tx_idle)) ||
+		 atomic_read(&bulk_dev->error)));
+
+	if (ret < 0) {
+		r = ret;
+		goto done;
+	}
+
+	if (atomic_read(&bulk_dev->error)) {
+		pr_err(" %s dev->error\n", __func__);
+		r = -EIO;
+		goto done;
+	}
+	if (copy_from_user(req->buf, buf, count)) {
+		if (!atomic_read(&ccid_dev->online)) {
+			pr_debug("%s: USB cable not connected\n",
+						__func__);
+			ccid_request_free(req, ccid_dev->in);
+			r = -ENODEV;
+		} else {
+			ccid_req_put(ccid_dev, &bulk_dev->tx_idle, req);
+			r = -EFAULT;
+		}
+		goto done;
+	}
+	req->length = count;
+	ret = usb_ep_queue(ccid_dev->in, req, GFP_KERNEL);
+	if (ret < 0) {
+		pr_debug("ccid_bulk_write: xfer error %d\n", ret);
+		atomic_set(&bulk_dev->error, 1);
+		ccid_req_put(ccid_dev, &bulk_dev->tx_idle, req);
+		r = -EIO;
+		spin_lock_irqsave(&ccid_dev->lock, flags);
+		if (!atomic_read(&ccid_dev->online)) {
+			spin_unlock_irqrestore(&ccid_dev->lock, flags);
+			pr_debug("%s: USB cable not connected\n",
+							__func__);
+			while ((req = ccid_req_get(ccid_dev,
+						&bulk_dev->tx_idle)))
+				ccid_request_free(req, ccid_dev->in);
+			r = -ENODEV;
+		}
+		spin_unlock_irqrestore(&ccid_dev->lock, flags);
+		goto done;
+	}
+done:
+	pr_debug("ccid_bulk_write returning %d\n", r);
+	return r;
+}
+
+static const struct file_operations ccid_bulk_fops = {
+	.owner = THIS_MODULE,
+	.read = ccid_bulk_read,
+	.write = ccid_bulk_write,
+	.open = ccid_bulk_open,
+	.release = ccid_bulk_release,
+};
+
+static struct miscdevice ccid_bulk_device = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "ccid_bulk",
+	.fops = &ccid_bulk_fops,
+};
+
+static int ccid_bulk_device_init(struct f_ccid *dev)
+{
+	int ret;
+	struct ccid_bulk_dev *bulk_dev = &dev->bulk_dev;
+
+	init_waitqueue_head(&bulk_dev->read_wq);
+	init_waitqueue_head(&bulk_dev->write_wq);
+	INIT_LIST_HEAD(&bulk_dev->tx_idle);
+
+	ret = misc_register(&ccid_bulk_device);
+	if (ret) {
+		pr_err("%s: failed to register misc device\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ccid_ctrl_open(struct inode *inode, struct file *fp)
+{
+	struct f_ccid *ccid_dev =  _ccid_dev;
+	struct ccid_ctrl_dev *ctrl_dev = &ccid_dev->ctrl_dev;
+	unsigned long flags;
+
+	if (!atomic_read(&ccid_dev->online)) {
+		pr_debug("%s: USB cable not connected\n", __func__);
+		return -ENODEV;
+	}
+	if (atomic_read(&ctrl_dev->opened)) {
+		pr_debug("%s: ctrl device is already opened\n", __func__);
+		return -EBUSY;
+	}
+	atomic_set(&ctrl_dev->opened, 1);
+	spin_lock_irqsave(&ccid_dev->lock, flags);
+	fp->private_data = ccid_dev;
+	spin_unlock_irqrestore(&ccid_dev->lock, flags);
+
+	return 0;
+}
+
+
+static int ccid_ctrl_release(struct inode *inode, struct file *fp)
+{
+	struct f_ccid *ccid_dev = fp->private_data;
+	struct ccid_ctrl_dev *ctrl_dev = &ccid_dev->ctrl_dev;
+
+	atomic_set(&ctrl_dev->opened, 0);
+
+	return 0;
+}
+
+static ssize_t ccid_ctrl_read(struct file *fp, char __user *buf,
+		      size_t count, loff_t *ppos)
+{
+	struct f_ccid *ccid_dev = fp->private_data;
+	struct ccid_ctrl_dev *ctrl_dev = &ccid_dev->ctrl_dev;
+	int ret = 0;
+
+	if (!atomic_read(&ccid_dev->online)) {
+		pr_debug("%s: USB cable not connected\n", __func__);
+		return -ENODEV;
+	}
+	if (count > CTRL_BUF_SIZE)
+		count = CTRL_BUF_SIZE;
+
+	ret = wait_event_interruptible(ctrl_dev->tx_wait_q,
+					 ctrl_dev->tx_ctrl_done);
+	if (ret < 0)
+		return ret;
+	ctrl_dev->tx_ctrl_done = 0;
+
+	if (!atomic_read(&ccid_dev->online)) {
+		pr_debug("%s: USB cable not connected\n", __func__);
+		return -ENODEV;
+	}
+	ret = copy_to_user(buf, ctrl_dev->buf, count);
+	if (ret)
+		return -EFAULT;
+
+	return count;
+}
+
+static long
+ccid_ctrl_ioctl(struct file *fp, unsigned cmd, u_long arg)
+{
+	struct f_ccid *ccid_dev = fp->private_data;
+	struct usb_request              *req = ccid_dev->notify_req;
+	struct usb_ccid_notification     *ccid_notify = req->buf;
+	void __user *argp = (void __user *)arg;
+	int ret = 0;
+
+	switch (cmd) {
+	case CCID_NOTIFY_CARD:
+		if (copy_from_user(ccid_notify, argp,
+				sizeof(struct usb_ccid_notification)))
+			return -EFAULT;
+		req->length = 2;
+		break;
+	case CCID_NOTIFY_HWERROR:
+		if (copy_from_user(ccid_notify, argp,
+				sizeof(struct usb_ccid_notification)))
+			return -EFAULT;
+		req->length = 4;
+		break;
+	case CCID_READ_DTR:
+		if (copy_to_user((int *)arg, &ccid_dev->dtr_state, sizeof(int)))
+			return -EFAULT;
+		return 0;
+	}
+	ret = usb_ep_queue(ccid_dev->notify, ccid_dev->notify_req, GFP_KERNEL);
+	if (ret < 0) {
+		pr_err("ccid notify ep enqueue error %d\n", ret);
+		return ret;
+	}
+	return 0;
+}
+
+static const struct file_operations ccid_ctrl_fops = {
+	.owner		= THIS_MODULE,
+	.open		= ccid_ctrl_open,
+	.release	= ccid_ctrl_release,
+	.read		= ccid_ctrl_read,
+	.unlocked_ioctl	= ccid_ctrl_ioctl,
+};
+
+static struct miscdevice ccid_ctrl_device = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "ccid_ctrl",
+	.fops = &ccid_ctrl_fops,
+};
+
+static int ccid_ctrl_device_init(struct f_ccid *dev)
+{
+	int ret;
+	struct ccid_ctrl_dev *ctrl_dev = &dev->ctrl_dev;
+
+	INIT_LIST_HEAD(&ctrl_dev->tx_q);
+	init_waitqueue_head(&ctrl_dev->tx_wait_q);
+
+	ret = misc_register(&ccid_ctrl_device);
+	if (ret) {
+		pr_err("%s: failed to register misc device\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ccid_bind_config(struct usb_configuration *c)
+{
+	struct f_ccid *ccid_dev = _ccid_dev;
+
+	pr_debug("ccid_bind_config\n");
+	ccid_dev->cdev = c->cdev;
+	ccid_dev->function.name = FUNCTION_NAME;
+	ccid_dev->function.descriptors = ccid_fs_descs;
+	ccid_dev->function.hs_descriptors = ccid_hs_descs;
+	ccid_dev->function.bind = ccid_function_bind;
+	ccid_dev->function.unbind = ccid_function_unbind;
+	ccid_dev->function.set_alt = ccid_function_set_alt;
+	ccid_dev->function.setup = ccid_function_setup;
+	ccid_dev->function.disable = ccid_function_disable;
+
+	return usb_add_function(c, &ccid_dev->function);
+
+}
+
+static int ccid_setup(void)
+{
+	struct f_ccid  *ccid_dev;
+	int ret;
+
+	ccid_dev = kzalloc(sizeof(*ccid_dev), GFP_KERNEL);
+	if (!ccid_dev)
+		return -ENOMEM;
+
+	_ccid_dev = ccid_dev;
+	spin_lock_init(&ccid_dev->lock);
+
+	ret = ccid_ctrl_device_init(ccid_dev);
+	if (ret) {
+		pr_err("%s: ccid_ctrl_device_init failed, err:%d\n",
+				__func__, ret);
+		goto err_ctrl_init;
+	}
+	ret = ccid_bulk_device_init(ccid_dev);
+	if (ret) {
+		pr_err("%s: ccid_bulk_device_init failed, err:%d\n",
+				__func__, ret);
+		goto err_bulk_init;
+	}
+
+	return 0;
+err_bulk_init:
+	misc_deregister(&ccid_ctrl_device);
+err_ctrl_init:
+	kfree(ccid_dev);
+	pr_err("ccid gadget driver failed to initialize\n");
+	return ret;
+}
+
+static void ccid_cleanup(void)
+{
+	misc_deregister(&ccid_bulk_device);
+	misc_deregister(&ccid_ctrl_device);
+	kfree(_ccid_dev);
+}
diff --git a/drivers/usb/gadget/f_ccid.h b/drivers/usb/gadget/f_ccid.h
new file mode 100644
index 0000000..4d6a0ea
--- /dev/null
+++ b/drivers/usb/gadget/f_ccid.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details
+ */
+
+#ifndef __F_CCID_H
+#define __F_CCID_H
+
+#define PROTOCOL_TO 0x01
+#define PROTOCOL_T1 0x02
+#define ABDATA_SIZE 512
+
+/* define for dwFeatures for Smart Card Device Class Descriptors */
+/* No special characteristics */
+#define CCID_FEATURES_NADA       0x00000000
+/* Automatic parameter configuration based on ATR data */
+#define CCID_FEATURES_AUTO_PCONF 0x00000002
+/* Automatic activation of ICC on inserting */
+#define CCID_FEATURES_AUTO_ACTIV 0x00000004
+/* Automatic ICC voltage selection */
+#define CCID_FEATURES_AUTO_VOLT  0x00000008
+/* Automatic ICC clock frequency change */
+#define CCID_FEATURES_AUTO_CLOCK 0x00000010
+/* Automatic baud rate change */
+#define CCID_FEATURES_AUTO_BAUD  0x00000020
+/*Automatic parameters negotiation made by the CCID */
+#define CCID_FEATURES_AUTO_PNEGO 0x00000040
+/* Automatic PPS made by the CCID according to the active parameters */
+#define CCID_FEATURES_AUTO_PPS   0x00000080
+/* CCID can set ICC in clock stop mode */
+#define CCID_FEATURES_ICCSTOP    0x00000100
+/* NAD value other than 00 accepted (T=1 protocol in use) */
+#define CCID_FEATURES_NAD        0x00000200
+/* Automatic IFSD exchange as first exchange (T=1 protocol in use) */
+#define CCID_FEATURES_AUTO_IFSD  0x00000400
+/* TPDU level exchanges with CCID */
+#define CCID_FEATURES_EXC_TPDU   0x00010000
+/* Short APDU level exchange with CCID */
+#define CCID_FEATURES_EXC_SAPDU  0x00020000
+/* Short and Extended APDU level exchange with CCID */
+#define CCID_FEATURES_EXC_APDU   0x00040000
+/* USB Wake up signaling supported on card insertion and removal */
+#define CCID_FEATURES_WAKEUP     0x00100000
+
+#define CCID_NOTIFY_CARD	_IOW('C', 1, struct usb_ccid_notification)
+#define CCID_NOTIFY_HWERROR	_IOW('C', 2, struct usb_ccid_notification)
+#define CCID_READ_DTR		_IOR('C', 3, int)
+
+struct usb_ccid_notification {
+	unsigned char buf[4];
+} __packed;
+
+struct ccid_bulk_in_header {
+	unsigned char bMessageType;
+	unsigned long wLength;
+	unsigned char bSlot;
+	unsigned char bSeq;
+	unsigned char bStatus;
+	unsigned char bError;
+	unsigned char bSpecific;
+	unsigned char abData[ABDATA_SIZE];
+	unsigned char bSizeToSend;
+} __packed;
+
+struct ccid_bulk_out_header {
+	unsigned char bMessageType;
+	unsigned long wLength;
+	unsigned char bSlot;
+	unsigned char bSeq;
+	unsigned char bSpecific_0;
+	unsigned char bSpecific_1;
+	unsigned char bSpecific_2;
+	unsigned char APDU[ABDATA_SIZE];
+} __packed;
+#endif
diff --git a/drivers/usb/gadget/f_diag.c b/drivers/usb/gadget/f_diag.c
new file mode 100644
index 0000000..72bff49
--- /dev/null
+++ b/drivers/usb/gadget/f_diag.c
@@ -0,0 +1,759 @@
+/* drivers/usb/gadget/f_diag.c
+ * Diag Function Device - Route ARM9 and ARM11 DIAG messages
+ * between HOST and DEVICE.
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ * Author: Brian Swetland <swetland@google.com>
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+
+#include <mach/usbdiag.h>
+#include <mach/rpc_hsusb.h>
+
+#include <linux/usb/composite.h>
+#include <linux/usb/gadget.h>
+#include <linux/workqueue.h>
+#include <linux/debugfs.h>
+
+static DEFINE_SPINLOCK(ch_lock);
+static LIST_HEAD(usb_diag_ch_list);
+
+static struct usb_interface_descriptor intf_desc = {
+	.bLength            =	sizeof intf_desc,
+	.bDescriptorType    =	USB_DT_INTERFACE,
+	.bNumEndpoints      =	2,
+	.bInterfaceClass    =	0xFF,
+	.bInterfaceSubClass =	0xFF,
+	.bInterfaceProtocol =	0xFF,
+};
+
+static struct usb_endpoint_descriptor hs_bulk_in_desc = {
+	.bLength 			=	USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType 	=	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes 		=	USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize 	=	__constant_cpu_to_le16(512),
+	.bInterval 			=	0,
+};
+static struct usb_endpoint_descriptor fs_bulk_in_desc = {
+	.bLength          =	USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType  =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes     =	USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize   = __constant_cpu_to_le16(64),
+	.bInterval        =	0,
+};
+
+static struct usb_endpoint_descriptor hs_bulk_out_desc = {
+	.bLength          =	USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType  =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes     =	USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize   = __constant_cpu_to_le16(512),
+	.bInterval        =	0,
+};
+
+static struct usb_endpoint_descriptor fs_bulk_out_desc = {
+	.bLength          =	USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType  =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes     =	USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize   = __constant_cpu_to_le16(64),
+	.bInterval        =	0,
+};
+
+static struct usb_descriptor_header *fs_diag_desc[] = {
+	(struct usb_descriptor_header *) &intf_desc,
+	(struct usb_descriptor_header *) &fs_bulk_in_desc,
+	(struct usb_descriptor_header *) &fs_bulk_out_desc,
+	NULL,
+	};
+static struct usb_descriptor_header *hs_diag_desc[] = {
+	(struct usb_descriptor_header *) &intf_desc,
+	(struct usb_descriptor_header *) &hs_bulk_in_desc,
+	(struct usb_descriptor_header *) &hs_bulk_out_desc,
+	NULL,
+};
+
+/**
+ * struct diag_context - USB diag function driver private structure
+ * @function: function structure for USB interface
+ * @out: USB OUT endpoint struct
+ * @in: USB IN endpoint struct
+ * @in_desc: USB IN endpoint descriptor struct
+ * @out_desc: USB OUT endpoint descriptor struct
+ * @read_pool: List of requests used for Rx (OUT ep)
+ * @write_pool: List of requests used for Tx (IN ep)
+ * @config_work: Work item schedule after interface is configured to notify
+ *               CONNECT event to diag char driver and updating product id
+ *               and serial number to MODEM/IMEM.
+ * @lock: Spinlock to proctect read_pool, write_pool lists
+ * @cdev: USB composite device struct
+ * @ch: USB diag channel
+ *
+ */
+struct diag_context {
+	struct usb_function function;
+	struct usb_ep *out;
+	struct usb_ep *in;
+	struct list_head read_pool;
+	struct list_head write_pool;
+	struct work_struct config_work;
+	spinlock_t lock;
+	unsigned configured;
+	struct usb_composite_dev *cdev;
+	int (*update_pid_and_serial_num)(uint32_t, const char *);
+	struct usb_diag_ch ch;
+
+	/* pkt counters */
+	unsigned long dpkts_tolaptop;
+	unsigned long dpkts_tomodem;
+	unsigned dpkts_tolaptop_pending;
+};
+
+static inline struct diag_context *func_to_diag(struct usb_function *f)
+{
+	return container_of(f, struct diag_context, function);
+}
+
+static void usb_config_work_func(struct work_struct *work)
+{
+	struct diag_context *ctxt = container_of(work,
+			struct diag_context, config_work);
+	struct usb_composite_dev *cdev = ctxt->cdev;
+	struct usb_gadget_strings *table;
+	struct usb_string *s;
+
+	if (ctxt->ch.notify)
+		ctxt->ch.notify(ctxt->ch.priv, USB_DIAG_CONNECT, NULL);
+
+	if (!ctxt->update_pid_and_serial_num)
+		return;
+
+	/* pass on product id and serial number to dload */
+	if (!cdev->desc.iSerialNumber) {
+		ctxt->update_pid_and_serial_num(
+					cdev->desc.idProduct, 0);
+		return;
+	}
+
+	/*
+	 * Serial number is filled by the composite driver. So
+	 * it is fair enough to assume that it will always be
+	 * found at first table of strings.
+	 */
+	table = *(cdev->driver->strings);
+	for (s = table->strings; s && s->s; s++)
+		if (s->id == cdev->desc.iSerialNumber) {
+			ctxt->update_pid_and_serial_num(
+					cdev->desc.idProduct, s->s);
+			break;
+		}
+}
+
+static void diag_write_complete(struct usb_ep *ep,
+		struct usb_request *req)
+{
+	struct diag_context *ctxt = ep->driver_data;
+	struct diag_request *d_req = req->context;
+	unsigned long flags;
+
+	ctxt->dpkts_tolaptop_pending--;
+
+	if (!req->status) {
+		if ((req->length >= ep->maxpacket) &&
+				((req->length % ep->maxpacket) == 0)) {
+			ctxt->dpkts_tolaptop_pending++;
+			req->length = 0;
+			d_req->actual = req->actual;
+			d_req->status = req->status;
+			/* Queue zero length packet */
+			usb_ep_queue(ctxt->in, req, GFP_ATOMIC);
+			return;
+		}
+	}
+
+	spin_lock_irqsave(&ctxt->lock, flags);
+	list_add_tail(&req->list, &ctxt->write_pool);
+	if (req->length != 0) {
+		d_req->actual = req->actual;
+		d_req->status = req->status;
+	}
+	spin_unlock_irqrestore(&ctxt->lock, flags);
+
+	if (ctxt->ch.notify)
+		ctxt->ch.notify(ctxt->ch.priv, USB_DIAG_WRITE_DONE, d_req);
+}
+
+static void diag_read_complete(struct usb_ep *ep,
+		struct usb_request *req)
+{
+	struct diag_context *ctxt = ep->driver_data;
+	struct diag_request *d_req = req->context;
+	unsigned long flags;
+
+	d_req->actual = req->actual;
+	d_req->status = req->status;
+
+	spin_lock_irqsave(&ctxt->lock, flags);
+	list_add_tail(&req->list, &ctxt->read_pool);
+	spin_unlock_irqrestore(&ctxt->lock, flags);
+
+	ctxt->dpkts_tomodem++;
+
+	if (ctxt->ch.notify)
+		ctxt->ch.notify(ctxt->ch.priv, USB_DIAG_READ_DONE, d_req);
+}
+
+/**
+ * usb_diag_open() - Open a diag channel over USB
+ * @name: Name of the channel
+ * @priv: Private structure pointer which will be passed in notify()
+ * @notify: Callback function to receive notifications
+ *
+ * This function iterates overs the available channels and returns
+ * the channel handler if the name matches. The notify callback is called
+ * for CONNECT, DISCONNECT, READ_DONE and WRITE_DONE events.
+ *
+ */
+struct usb_diag_ch *usb_diag_open(const char *name, void *priv,
+		void (*notify)(void *, unsigned, struct diag_request *))
+{
+	struct usb_diag_ch *ch;
+	struct diag_context *ctxt;
+	unsigned long flags;
+	int found = 0;
+
+	spin_lock_irqsave(&ch_lock, flags);
+	/* Check if we already have a channel with this name */
+	list_for_each_entry(ch, &usb_diag_ch_list, list) {
+		if (!strcmp(name, ch->name)) {
+			found = 1;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&ch_lock, flags);
+
+	if (!found) {
+		ctxt = kzalloc(sizeof(*ctxt), GFP_KERNEL);
+		if (!ctxt)
+			return ERR_PTR(-ENOMEM);
+
+		ch = &ctxt->ch;
+	}
+
+	ch->name = name;
+	ch->priv = priv;
+	ch->notify = notify;
+
+	spin_lock_irqsave(&ch_lock, flags);
+	list_add_tail(&ch->list, &usb_diag_ch_list);
+	spin_unlock_irqrestore(&ch_lock, flags);
+
+	return ch;
+}
+EXPORT_SYMBOL(usb_diag_open);
+
+/**
+ * usb_diag_close() - Close a diag channel over USB
+ * @ch: Channel handler
+ *
+ * This function closes the diag channel.
+ *
+ */
+void usb_diag_close(struct usb_diag_ch *ch)
+{
+	struct diag_context *dev = container_of(ch, struct diag_context, ch);
+	unsigned long flags;
+
+	spin_lock_irqsave(&ch_lock, flags);
+	ch->priv = NULL;
+	ch->notify = NULL;
+	/* Free-up the resources if channel is no more active */
+	if (!ch->priv_usb) {
+		list_del(&ch->list);
+		kfree(dev);
+	}
+
+	spin_unlock_irqrestore(&ch_lock, flags);
+}
+EXPORT_SYMBOL(usb_diag_close);
+
+/**
+ * usb_diag_free_req() - Free USB requests
+ * @ch: Channel handler
+ *
+ * This function free read and write USB requests for the interface
+ * associated with this channel.
+ *
+ */
+void usb_diag_free_req(struct usb_diag_ch *ch)
+{
+	struct diag_context *ctxt = ch->priv_usb;
+	struct usb_request *req;
+	struct list_head *act, *tmp;
+
+	if (!ctxt)
+		return;
+
+	list_for_each_safe(act, tmp, &ctxt->write_pool) {
+		req = list_entry(act, struct usb_request, list);
+		list_del(&req->list);
+		usb_ep_free_request(ctxt->in, req);
+	}
+
+	list_for_each_safe(act, tmp, &ctxt->read_pool) {
+		req = list_entry(act, struct usb_request, list);
+		list_del(&req->list);
+		usb_ep_free_request(ctxt->out, req);
+	}
+}
+EXPORT_SYMBOL(usb_diag_free_req);
+
+/**
+ * usb_diag_alloc_req() - Allocate USB requests
+ * @ch: Channel handler
+ * @n_write: Number of requests for Tx
+ * @n_read: Number of requests for Rx
+ *
+ * This function allocate read and write USB requests for the interface
+ * associated with this channel. The actual buffer is not allocated.
+ * The buffer is passed by diag char driver.
+ *
+ */
+int usb_diag_alloc_req(struct usb_diag_ch *ch, int n_write, int n_read)
+{
+	struct diag_context *ctxt = ch->priv_usb;
+	struct usb_request *req;
+	int i;
+
+	if (!ctxt)
+		return -ENODEV;
+
+	for (i = 0; i < n_write; i++) {
+		req = usb_ep_alloc_request(ctxt->in, GFP_ATOMIC);
+		if (!req)
+			goto fail;
+		req->complete = diag_write_complete;
+		list_add_tail(&req->list, &ctxt->write_pool);
+	}
+
+	for (i = 0; i < n_read; i++) {
+		req = usb_ep_alloc_request(ctxt->out, GFP_ATOMIC);
+		if (!req)
+			goto fail;
+		req->complete = diag_read_complete;
+		list_add_tail(&req->list, &ctxt->read_pool);
+	}
+
+	return 0;
+
+fail:
+	usb_diag_free_req(ch);
+	return -ENOMEM;
+
+}
+EXPORT_SYMBOL(usb_diag_alloc_req);
+
+/**
+ * usb_diag_read() - Read data from USB diag channel
+ * @ch: Channel handler
+ * @d_req: Diag request struct
+ *
+ * Enqueue a request on OUT endpoint of the interface corresponding to this
+ * channel. This function returns proper error code when interface is not
+ * in configured state, no Rx requests available and ep queue is failed.
+ *
+ * This function operates asynchronously. READ_DONE event is notified after
+ * completion of OUT request.
+ *
+ */
+int usb_diag_read(struct usb_diag_ch *ch, struct diag_request *d_req)
+{
+	struct diag_context *ctxt = ch->priv_usb;
+	unsigned long flags;
+	struct usb_request *req;
+
+	if (!ctxt)
+		return -ENODEV;
+
+	spin_lock_irqsave(&ctxt->lock, flags);
+
+	if (!ctxt->configured) {
+		spin_unlock_irqrestore(&ctxt->lock, flags);
+		return -EIO;
+	}
+
+	if (list_empty(&ctxt->read_pool)) {
+		spin_unlock_irqrestore(&ctxt->lock, flags);
+		ERROR(ctxt->cdev, "%s: no requests available\n", __func__);
+		return -EAGAIN;
+	}
+
+	req = list_first_entry(&ctxt->read_pool, struct usb_request, list);
+	list_del(&req->list);
+	spin_unlock_irqrestore(&ctxt->lock, flags);
+
+	req->buf = d_req->buf;
+	req->length = d_req->length;
+	req->context = d_req;
+	if (usb_ep_queue(ctxt->out, req, GFP_ATOMIC)) {
+		/* If error add the link to linked list again*/
+		spin_lock_irqsave(&ctxt->lock, flags);
+		list_add_tail(&req->list, &ctxt->read_pool);
+		spin_unlock_irqrestore(&ctxt->lock, flags);
+		ERROR(ctxt->cdev, "%s: cannot queue"
+				" read request\n", __func__);
+		return -EIO;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(usb_diag_read);
+
+/**
+ * usb_diag_write() - Write data from USB diag channel
+ * @ch: Channel handler
+ * @d_req: Diag request struct
+ *
+ * Enqueue a request on IN endpoint of the interface corresponding to this
+ * channel. This function returns proper error code when interface is not
+ * in configured state, no Tx requests available and ep queue is failed.
+ *
+ * This function operates asynchronously. WRITE_DONE event is notified after
+ * completion of IN request.
+ *
+ */
+int usb_diag_write(struct usb_diag_ch *ch, struct diag_request *d_req)
+{
+	struct diag_context *ctxt = ch->priv_usb;
+	unsigned long flags;
+	struct usb_request *req = NULL;
+
+	if (!ctxt)
+		return -ENODEV;
+
+	spin_lock_irqsave(&ctxt->lock, flags);
+
+	if (!ctxt->configured) {
+		spin_unlock_irqrestore(&ctxt->lock, flags);
+		return -EIO;
+	}
+
+	if (list_empty(&ctxt->write_pool)) {
+		spin_unlock_irqrestore(&ctxt->lock, flags);
+		ERROR(ctxt->cdev, "%s: no requests available\n", __func__);
+		return -EAGAIN;
+	}
+
+	req = list_first_entry(&ctxt->write_pool, struct usb_request, list);
+	list_del(&req->list);
+	spin_unlock_irqrestore(&ctxt->lock, flags);
+
+	req->buf = d_req->buf;
+	req->length = d_req->length;
+	req->context = d_req;
+	if (usb_ep_queue(ctxt->in, req, GFP_ATOMIC)) {
+		/* If error add the link to linked list again*/
+		spin_lock_irqsave(&ctxt->lock, flags);
+		list_add_tail(&req->list, &ctxt->write_pool);
+		spin_unlock_irqrestore(&ctxt->lock, flags);
+		ERROR(ctxt->cdev, "%s: cannot queue"
+				" read request\n", __func__);
+		return -EIO;
+	}
+
+	ctxt->dpkts_tolaptop++;
+	ctxt->dpkts_tolaptop_pending++;
+
+	return 0;
+}
+EXPORT_SYMBOL(usb_diag_write);
+
+static void diag_function_disable(struct usb_function *f)
+{
+	struct diag_context  *dev = func_to_diag(f);
+	unsigned long flags;
+
+	DBG(dev->cdev, "diag_function_disable\n");
+
+	spin_lock_irqsave(&dev->lock, flags);
+	dev->configured = 0;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	if (dev->ch.notify)
+		dev->ch.notify(dev->ch.priv, USB_DIAG_DISCONNECT, NULL);
+
+	usb_ep_disable(dev->in);
+	dev->in->driver_data = NULL;
+
+	usb_ep_disable(dev->out);
+	dev->out->driver_data = NULL;
+
+}
+
+static int diag_function_set_alt(struct usb_function *f,
+		unsigned intf, unsigned alt)
+{
+	struct diag_context  *dev = func_to_diag(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+	unsigned long flags;
+	int rc = 0;
+
+	if (config_ep_by_speed(cdev->gadget, f, dev->in) ||
+	    config_ep_by_speed(cdev->gadget, f, dev->out)) {
+		dev->in->desc = NULL;
+		dev->out->desc = NULL;
+		return -EINVAL;
+	}
+
+	dev->in->driver_data = dev;
+	rc = usb_ep_enable(dev->in);
+	if (rc) {
+		ERROR(dev->cdev, "can't enable %s, result %d\n",
+						dev->in->name, rc);
+		return rc;
+	}
+	dev->out->driver_data = dev;
+	rc = usb_ep_enable(dev->out);
+	if (rc) {
+		ERROR(dev->cdev, "can't enable %s, result %d\n",
+						dev->out->name, rc);
+		usb_ep_disable(dev->in);
+		return rc;
+	}
+	schedule_work(&dev->config_work);
+
+	dev->dpkts_tolaptop = 0;
+	dev->dpkts_tomodem = 0;
+	dev->dpkts_tolaptop_pending = 0;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	dev->configured = 1;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return rc;
+}
+
+static void diag_function_unbind(struct usb_configuration *c,
+		struct usb_function *f)
+{
+	struct diag_context *ctxt = func_to_diag(f);
+
+	if (gadget_is_dualspeed(c->cdev->gadget))
+		usb_free_descriptors(f->hs_descriptors);
+
+	usb_free_descriptors(f->descriptors);
+	ctxt->ch.priv_usb = NULL;
+}
+
+static int diag_function_bind(struct usb_configuration *c,
+		struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = c->cdev;
+	struct diag_context *ctxt = func_to_diag(f);
+	struct usb_ep *ep;
+	int status = -ENODEV;
+
+	intf_desc.bInterfaceNumber =  usb_interface_id(c, f);
+
+	ep = usb_ep_autoconfig(cdev->gadget, &fs_bulk_in_desc);
+	if (!ep)
+		goto fail;
+	ctxt->in = ep;
+	ep->driver_data = ctxt;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &fs_bulk_out_desc);
+	if (!ep)
+		goto fail;
+	ctxt->out = ep;
+	ep->driver_data = ctxt;
+
+	/* copy descriptors, and track endpoint copies */
+	f->descriptors = usb_copy_descriptors(fs_diag_desc);
+	if (!f->descriptors)
+		goto fail;
+
+	if (gadget_is_dualspeed(c->cdev->gadget)) {
+		hs_bulk_in_desc.bEndpointAddress =
+				fs_bulk_in_desc.bEndpointAddress;
+		hs_bulk_out_desc.bEndpointAddress =
+				fs_bulk_out_desc.bEndpointAddress;
+
+		/* copy descriptors, and track endpoint copies */
+		f->hs_descriptors = usb_copy_descriptors(hs_diag_desc);
+	}
+	return 0;
+fail:
+	if (ctxt->out)
+		ctxt->out->driver_data = NULL;
+	if (ctxt->in)
+		ctxt->in->driver_data = NULL;
+	return status;
+
+}
+
+int diag_function_add(struct usb_configuration *c, const char *name,
+			int (*update_pid)(uint32_t, const char *))
+{
+	struct diag_context *dev;
+	struct usb_diag_ch *_ch;
+	int found = 0, ret;
+
+	DBG(c->cdev, "diag_function_add\n");
+
+	list_for_each_entry(_ch, &usb_diag_ch_list, list) {
+		if (!strcmp(name, _ch->name)) {
+			found = 1;
+			break;
+		}
+	}
+	if (!found) {
+		ERROR(c->cdev, "unable to get diag usb channel\n");
+		return -ENODEV;
+	}
+
+	dev = container_of(_ch, struct diag_context, ch);
+	/* claim the channel for this USB interface */
+	_ch->priv_usb = dev;
+
+	dev->update_pid_and_serial_num = update_pid;
+	dev->cdev = c->cdev;
+	dev->function.name = _ch->name;
+	dev->function.descriptors = fs_diag_desc;
+	dev->function.hs_descriptors = hs_diag_desc;
+	dev->function.bind = diag_function_bind;
+	dev->function.unbind = diag_function_unbind;
+	dev->function.set_alt = diag_function_set_alt;
+	dev->function.disable = diag_function_disable;
+	spin_lock_init(&dev->lock);
+	INIT_LIST_HEAD(&dev->read_pool);
+	INIT_LIST_HEAD(&dev->write_pool);
+	INIT_WORK(&dev->config_work, usb_config_work_func);
+
+	ret = usb_add_function(c, &dev->function);
+	if (ret) {
+		INFO(c->cdev, "usb_add_function failed\n");
+		_ch->priv_usb = NULL;
+	}
+
+	return ret;
+}
+
+
+#if defined(CONFIG_DEBUG_FS)
+static char debug_buffer[PAGE_SIZE];
+
+static ssize_t debug_read_stats(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	char *buf = debug_buffer;
+	int temp = 0;
+	struct usb_diag_ch *ch;
+
+	list_for_each_entry(ch, &usb_diag_ch_list, list) {
+		struct diag_context *ctxt = ch->priv_usb;
+
+		if (ctxt)
+			temp += scnprintf(buf + temp, PAGE_SIZE - temp,
+					"---Name: %s---\n"
+					"endpoints: %s, %s\n"
+					"dpkts_tolaptop: %lu\n"
+					"dpkts_tomodem:  %lu\n"
+					"pkts_tolaptop_pending: %u\n",
+					ch->name,
+					ctxt->in->name, ctxt->out->name,
+					ctxt->dpkts_tolaptop,
+					ctxt->dpkts_tomodem,
+					ctxt->dpkts_tolaptop_pending);
+	}
+
+	return simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+}
+
+static ssize_t debug_reset_stats(struct file *file, const char __user *buf,
+				 size_t count, loff_t *ppos)
+{
+	struct usb_diag_ch *ch;
+
+	list_for_each_entry(ch, &usb_diag_ch_list, list) {
+		struct diag_context *ctxt = ch->priv_usb;
+
+		if (ctxt) {
+			ctxt->dpkts_tolaptop = 0;
+			ctxt->dpkts_tomodem = 0;
+			ctxt->dpkts_tolaptop_pending = 0;
+		}
+	}
+
+	return count;
+}
+
+static int debug_open(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static const struct file_operations debug_fdiag_ops = {
+	.open = debug_open,
+	.read = debug_read_stats,
+	.write = debug_reset_stats,
+};
+
+struct dentry *dent_diag;
+static void fdiag_debugfs_init(void)
+{
+	dent_diag = debugfs_create_dir("usb_diag", 0);
+	if (IS_ERR(dent_diag))
+		return;
+
+	debugfs_create_file("status", 0444, dent_diag, 0, &debug_fdiag_ops);
+}
+#else
+static void fdiag_debugfs_init(void)
+{
+	return;
+}
+#endif
+
+static void diag_cleanup(void)
+{
+	struct diag_context *dev;
+	struct list_head *act, *tmp;
+	struct usb_diag_ch *_ch;
+	unsigned long flags;
+
+	debugfs_remove_recursive(dent_diag);
+
+	list_for_each_safe(act, tmp, &usb_diag_ch_list) {
+		_ch = list_entry(act, struct usb_diag_ch, list);
+		dev = container_of(_ch, struct diag_context, ch);
+
+		spin_lock_irqsave(&ch_lock, flags);
+		/* Free if diagchar is not using the channel anymore */
+		if (!_ch->priv) {
+			list_del(&_ch->list);
+			kfree(dev);
+		}
+		spin_unlock_irqrestore(&ch_lock, flags);
+	}
+}
+
+static int diag_setup(void)
+{
+	fdiag_debugfs_init();
+
+	return 0;
+}
diff --git a/drivers/usb/gadget/f_diag.h b/drivers/usb/gadget/f_diag.h
new file mode 100644
index 0000000..82d9a25
--- /dev/null
+++ b/drivers/usb/gadget/f_diag.h
@@ -0,0 +1,24 @@
+/* drivers/usb/gadget/f_diag.h
+ *
+ * Diag Function Device - Route DIAG frames between SMD and USB
+ *
+ * Copyright (C) 2008-2009 Google, Inc.
+ * Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ * Author: Brian Swetland <swetland@google.com>
+ * 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.
+ *
+ */
+#ifndef __F_DIAG_H
+#define __F_DIAG_H
+
+int diag_function_add(struct usb_configuration *c, const char *);
+
+#endif /* __F_DIAG_H */
+
diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c
index cb8c162..278e04e 100644
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/drivers/usb/gadget/f_mass_storage.c
@@ -310,7 +310,10 @@
 
 #include "storage_common.c"
 
-
+#ifdef CONFIG_USB_CSW_HACK
+static int write_error_after_csw_sent;
+static int csw_hack_sent;
+#endif
 /*-------------------------------------------------------------------------*/
 
 struct fsg_dev;
@@ -467,6 +470,7 @@
 }
 
 typedef void (*fsg_routine_t)(struct fsg_dev *);
+static int send_status(struct fsg_common *common);
 
 static int exception_in_progress(struct fsg_common *common)
 {
@@ -746,6 +750,9 @@
 	loff_t			file_offset, file_offset_tmp;
 	unsigned int		amount;
 	ssize_t			nread;
+#ifdef CONFIG_USB_MSC_PROFILING
+	ktime_t			start, diff;
+#endif
 
 	/*
 	 * Get the starting Logical Block Address and check that it's
@@ -813,11 +820,20 @@
 
 		/* Perform the read */
 		file_offset_tmp = file_offset;
+
+#ifdef CONFIG_USB_MSC_PROFILING
+		start = ktime_get();
+#endif
 		nread = vfs_read(curlun->filp,
 				 (char __user *)bh->buf,
 				 amount, &file_offset_tmp);
 		VLDBG(curlun, "file read %u @ %llu -> %d\n", amount,
-		      (unsigned long long)file_offset, (int)nread);
+		     (unsigned long long) file_offset, (int) nread);
+#ifdef CONFIG_USB_MSC_PROFILING
+		diff = ktime_sub(ktime_get(), start);
+		curlun->perf.rbytes += nread;
+		curlun->perf.rtime = ktime_add(curlun->perf.rtime, diff);
+#endif
 		if (signal_pending(current))
 			return -EINTR;
 
@@ -879,6 +895,13 @@
 	ssize_t			nwritten;
 	int			rc;
 
+#ifdef CONFIG_USB_CSW_HACK
+	int			i;
+#endif
+
+#ifdef CONFIG_USB_MSC_PROFILING
+	ktime_t			start, diff;
+#endif
 	if (curlun->ro) {
 		curlun->sense_data = SS_WRITE_PROTECTED;
 		return -EINVAL;
@@ -971,7 +994,17 @@
 		bh = common->next_buffhd_to_drain;
 		if (bh->state == BUF_STATE_EMPTY && !get_some_more)
 			break;			/* We stopped early */
+#ifdef CONFIG_USB_CSW_HACK
+		/*
+		 * If the csw packet is already submmitted to the hardware,
+		 * by marking the state of buffer as full, then by checking
+		 * the residue, we make sure that this csw packet is not
+		 * written on to the storage media.
+		 */
+		if (bh->state == BUF_STATE_FULL && common->residue) {
+#else
 		if (bh->state == BUF_STATE_FULL) {
+#endif
 			smp_rmb();
 			common->next_buffhd_to_drain = bh->next;
 			bh->state = BUF_STATE_EMPTY;
@@ -1006,11 +1039,20 @@
 
 			/* Perform the write */
 			file_offset_tmp = file_offset;
+#ifdef CONFIG_USB_MSC_PROFILING
+			start = ktime_get();
+#endif
 			nwritten = vfs_write(curlun->filp,
 					     (char __user *)bh->buf,
 					     amount, &file_offset_tmp);
 			VLDBG(curlun, "file write %u @ %llu -> %d\n", amount,
 			      (unsigned long long)file_offset, (int)nwritten);
+#ifdef CONFIG_USB_MSC_PROFILING
+			diff = ktime_sub(ktime_get(), start);
+			curlun->perf.wbytes += nwritten;
+			curlun->perf.wtime =
+					ktime_add(curlun->perf.wtime, diff);
+#endif
 			if (signal_pending(current))
 				return -EINTR;		/* Interrupted! */
 
@@ -1033,9 +1075,37 @@
 				curlun->sense_data_info =
 					file_offset >> curlun->blkbits;
 				curlun->info_valid = 1;
+#ifdef CONFIG_USB_CSW_HACK
+				write_error_after_csw_sent = 1;
+				goto write_error;
+#endif
 				break;
 			}
 
+#ifdef CONFIG_USB_CSW_HACK
+write_error:
+			if ((nwritten == amount) && !csw_hack_sent) {
+				if (write_error_after_csw_sent)
+					break;
+				/*
+				 * Check if any of the buffer is in the
+				 * busy state, if any buffer is in busy state,
+				 * means the complete data is not received
+				 * yet from the host. So there is no point in
+				 * csw right away without the complete data.
+				 */
+				for (i = 0; i < fsg_num_buffers; i++) {
+					if (common->buffhds[i].state ==
+							BUF_STATE_BUSY)
+						break;
+				}
+				if (!amount_left_to_req && i == fsg_num_buffers) {
+					csw_hack_sent = 1;
+					send_status(common);
+				}
+			}
+#endif
+
  empty_write:
 			/* Did the host decide to stop early? */
 			if (bh->outreq->actual < bh->bulk_out_intended_length) {
@@ -1497,8 +1567,7 @@
 		curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
 		return -EINVAL;
 	}
-
-	if (curlun->prevent_medium_removal && !prevent)
+	if (!curlun->nofua && curlun->prevent_medium_removal && !prevent)
 		fsg_lun_fsync_sub(curlun);
 	curlun->prevent_medium_removal = prevent;
 	return 0;
@@ -1778,6 +1847,19 @@
 	csw->Signature = cpu_to_le32(US_BULK_CS_SIGN);
 	csw->Tag = common->tag;
 	csw->Residue = cpu_to_le32(common->residue);
+#ifdef CONFIG_USB_CSW_HACK
+	/* Since csw is being sent early, before
+	 * writing on to storage media, need to set
+	 * residue to zero,assuming that write will succeed.
+	 */
+	if (write_error_after_csw_sent) {
+		write_error_after_csw_sent = 0;
+		csw->Residue = cpu_to_le32(common->residue);
+	} else
+		csw->Residue = 0;
+#else
+	csw->Residue = cpu_to_le32(common->residue);
+#endif
 	csw->Status = status;
 
 	bh->inreq->length = US_BULK_CS_WRAP_LEN;
@@ -2366,15 +2448,6 @@
 			}
 		}
 
-		/* Disable the endpoints */
-		if (fsg->bulk_in_enabled) {
-			usb_ep_disable(fsg->bulk_in);
-			fsg->bulk_in_enabled = 0;
-		}
-		if (fsg->bulk_out_enabled) {
-			usb_ep_disable(fsg->bulk_out);
-			fsg->bulk_out_enabled = 0;
-		}
 
 		common->fsg = NULL;
 		wake_up(&common->fsg_wait);
@@ -2387,28 +2460,6 @@
 	common->fsg = new_fsg;
 	fsg = common->fsg;
 
-	/* Enable the endpoints */
-	rc = config_ep_by_speed(common->gadget, &(fsg->function), fsg->bulk_in);
-	if (rc)
-		goto reset;
-	rc = usb_ep_enable(fsg->bulk_in);
-	if (rc)
-		goto reset;
-	fsg->bulk_in->driver_data = common;
-	fsg->bulk_in_enabled = 1;
-
-	rc = config_ep_by_speed(common->gadget, &(fsg->function),
-				fsg->bulk_out);
-	if (rc)
-		goto reset;
-	rc = usb_ep_enable(fsg->bulk_out);
-	if (rc)
-		goto reset;
-	fsg->bulk_out->driver_data = common;
-	fsg->bulk_out_enabled = 1;
-	common->bulk_out_maxpacket = usb_endpoint_maxp(fsg->bulk_out->desc);
-	clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
-
 	/* Allocate the requests */
 	for (i = 0; i < fsg_num_buffers; ++i) {
 		struct fsg_buffhd	*bh = &common->buffhds[i];
@@ -2437,14 +2488,55 @@
 static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
 {
 	struct fsg_dev *fsg = fsg_from_func(f);
+	struct fsg_common *common = fsg->common;
+	int rc;
+
+	/* Enable the endpoints */
+	rc = config_ep_by_speed(common->gadget, &(fsg->function), fsg->bulk_in);
+	if (rc)
+		return rc;
+	rc = usb_ep_enable(fsg->bulk_in);
+	if (rc)
+		return rc;
+	fsg->bulk_in->driver_data = common;
+	fsg->bulk_in_enabled = 1;
+
+	rc = config_ep_by_speed(common->gadget, &(fsg->function),
+				fsg->bulk_out);
+	if (rc)
+		goto reset_bulk_int;
+	rc = usb_ep_enable(fsg->bulk_out);
+	if (rc)
+		goto reset_bulk_int;
+	fsg->bulk_out->driver_data = common;
+	fsg->bulk_out_enabled = 1;
+	common->bulk_out_maxpacket = le16_to_cpu(fsg->bulk_in->desc->wMaxPacketSize);
+	clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
 	fsg->common->new_fsg = fsg;
 	raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
 	return USB_GADGET_DELAYED_STATUS;
+
+reset_bulk_int:
+	usb_ep_disable(fsg->bulk_in);
+	fsg->bulk_in_enabled = 0;
+	return rc;
 }
 
 static void fsg_disable(struct usb_function *f)
 {
 	struct fsg_dev *fsg = fsg_from_func(f);
+
+	/* Disable the endpoints */
+	if (fsg->bulk_in_enabled) {
+		usb_ep_disable(fsg->bulk_in);
+		fsg->bulk_in_enabled = 0;
+		fsg->bulk_in->driver_data = NULL;
+	}
+	if (fsg->bulk_out_enabled) {
+		usb_ep_disable(fsg->bulk_out);
+		fsg->bulk_out_enabled = 0;
+		fsg->bulk_out->driver_data = NULL;
+	}
 	fsg->common->new_fsg = NULL;
 	raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
 }
@@ -2651,6 +2743,16 @@
 			common->state = FSG_STATE_STATUS_PHASE;
 		spin_unlock_irq(&common->lock);
 
+#ifdef CONFIG_USB_CSW_HACK
+		/* Since status is already sent for write scsi command,
+		 * need to skip sending status once again if it is a
+		 * write scsi command.
+		 */
+		if (csw_hack_sent) {
+			csw_hack_sent = 0;
+			continue;
+		}
+#endif
 		if (send_status(common))
 			continue;
 
@@ -2691,7 +2793,9 @@
 static DEVICE_ATTR(ro, 0644, fsg_show_ro, fsg_store_ro);
 static DEVICE_ATTR(nofua, 0644, fsg_show_nofua, fsg_store_nofua);
 static DEVICE_ATTR(file, 0644, fsg_show_file, fsg_store_file);
-
+#ifdef CONFIG_USB_MSC_PROFILING
+static DEVICE_ATTR(perf, 0644, fsg_show_perf, fsg_store_perf);
+#endif
 
 /****************************** FSG COMMON ******************************/
 
@@ -2788,6 +2892,7 @@
 		curlun->ro = lcfg->cdrom || lcfg->ro;
 		curlun->initially_ro = curlun->ro;
 		curlun->removable = lcfg->removable;
+		curlun->nofua = lcfg->nofua;
 		curlun->dev.release = fsg_lun_release;
 		curlun->dev.parent = &gadget->dev;
 		/* curlun->dev.driver = &fsg_driver.driver; XXX */
@@ -2815,7 +2920,12 @@
 		rc = device_create_file(&curlun->dev, &dev_attr_nofua);
 		if (rc)
 			goto error_luns;
-
+#ifdef CONFIG_USB_MSC_PROFILING
+		rc = device_create_file(&curlun->dev, &dev_attr_perf);
+		if (rc)
+			dev_err(&gadget->dev, "failed to create sysfs entry:"
+				"(dev_attr_perf) error: %d\n", rc);
+#endif
 		if (lcfg->filename) {
 			rc = fsg_lun_open(curlun, lcfg->filename);
 			if (rc)
@@ -2944,6 +3054,9 @@
 
 		/* In error recovery common->nluns may be zero. */
 		for (; i; --i, ++lun) {
+#ifdef CONFIG_USB_MSC_PROFILING
+			device_remove_file(&lun->dev, &dev_attr_perf);
+#endif
 			device_remove_file(&lun->dev, &dev_attr_nofua);
 			device_remove_file(&lun->dev, &dev_attr_ro);
 			device_remove_file(&lun->dev, &dev_attr_file);
@@ -3085,7 +3198,7 @@
 	if (unlikely(!fsg))
 		return -ENOMEM;
 
-	fsg->function.name        = FSG_DRIVER_DESC;
+	fsg->function.name        = "mass_storage";
 	fsg->function.strings     = fsg_strings_array;
 	fsg->function.bind        = fsg_bind;
 	fsg->function.unbind      = fsg_unbind;
diff --git a/drivers/usb/gadget/f_mbim.c b/drivers/usb/gadget/f_mbim.c
new file mode 100644
index 0000000..41a1777
--- /dev/null
+++ b/drivers/usb/gadget/f_mbim.c
@@ -0,0 +1,1804 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+
+#include <linux/usb/cdc.h>
+
+#include <linux/usb/composite.h>
+#include <linux/usb/android_composite.h>
+#include <linux/platform_device.h>
+
+#include <linux/spinlock.h>
+
+/*
+ * This function is a "Mobile Broadband Interface Model" (MBIM) link.
+ * MBIM is intended to be used with high-speed network attachments.
+ *
+ * Note that MBIM requires the use of "alternate settings" for its data
+ * interface.  This means that the set_alt() method has real work to do,
+ * and also means that a get_alt() method is required.
+ */
+
+#define MBIM_BULK_BUFFER_SIZE		4096
+
+#define MBIM_IOCTL_MAGIC		'o'
+#define MBIM_GET_NTB_SIZE		_IOR(MBIM_IOCTL_MAGIC, 2, u32)
+#define MBIM_GET_DATAGRAM_COUNT		_IOR(MBIM_IOCTL_MAGIC, 3, u16)
+
+#define NR_MBIM_PORTS			1
+
+struct ctrl_pkt {
+	void			*buf;
+	int			len;
+	struct list_head	list;
+};
+
+struct mbim_ep_descs {
+	struct usb_endpoint_descriptor	*in;
+	struct usb_endpoint_descriptor	*out;
+	struct usb_endpoint_descriptor	*notify;
+};
+
+struct mbim_notify_port {
+	struct usb_ep			*notify;
+	struct usb_request		*notify_req;
+	u8				notify_state;
+	atomic_t			notify_count;
+};
+
+enum mbim_notify_state {
+	NCM_NOTIFY_NONE,
+	NCM_NOTIFY_CONNECT,
+	NCM_NOTIFY_SPEED,
+};
+
+struct f_mbim {
+	struct usb_function function;
+	struct usb_composite_dev *cdev;
+
+	atomic_t	online;
+	bool		is_open;
+
+	atomic_t	open_excl;
+	atomic_t	ioctl_excl;
+	atomic_t	read_excl;
+	atomic_t	write_excl;
+
+	wait_queue_head_t read_wq;
+	wait_queue_head_t write_wq;
+
+	u8				port_num;
+	struct data_port		bam_port;
+	struct mbim_notify_port		not_port;
+
+	struct mbim_ep_descs		fs;
+	struct mbim_ep_descs		hs;
+
+	u8				ctrl_id, data_id;
+
+	struct ndp_parser_opts		*parser_opts;
+
+	spinlock_t			lock;
+
+	struct list_head	cpkt_req_q;
+	struct list_head	cpkt_resp_q;
+
+	u32			ntb_input_size;
+	u16			ntb_max_datagrams;
+
+	atomic_t		error;
+};
+
+struct mbim_ntb_input_size {
+	u32	ntb_input_size;
+	u16	ntb_max_datagrams;
+	u16	reserved;
+};
+
+/* temporary variable used between mbim_open() and mbim_gadget_bind() */
+static struct f_mbim *_mbim_dev;
+
+static unsigned int nr_mbim_ports;
+
+static struct mbim_ports {
+	struct f_mbim	*port;
+	unsigned	port_num;
+} mbim_ports[NR_MBIM_PORTS];
+
+static inline struct f_mbim *func_to_mbim(struct usb_function *f)
+{
+	return container_of(f, struct f_mbim, function);
+}
+
+/* peak (theoretical) bulk transfer rate in bits-per-second */
+static inline unsigned mbim_bitrate(struct usb_gadget *g)
+{
+	if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
+		return 13 * 512 * 8 * 1000 * 8;
+	else
+		return 19 *  64 * 1 * 1000 * 8;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define NTB_DEFAULT_IN_SIZE	(0x4000)
+#define NTB_OUT_SIZE		(0x1000)
+#define NDP_IN_DIVISOR		(0x4)
+
+#define FORMATS_SUPPORTED	USB_CDC_NCM_NTB16_SUPPORTED
+
+static struct usb_cdc_ncm_ntb_parameters ntb_parameters = {
+	.wLength = sizeof ntb_parameters,
+	.bmNtbFormatsSupported = cpu_to_le16(FORMATS_SUPPORTED),
+	.dwNtbInMaxSize = cpu_to_le32(NTB_DEFAULT_IN_SIZE),
+	.wNdpInDivisor = cpu_to_le16(NDP_IN_DIVISOR),
+	.wNdpInPayloadRemainder = cpu_to_le16(0),
+	.wNdpInAlignment = cpu_to_le16(4),
+
+	.dwNtbOutMaxSize = cpu_to_le32(NTB_OUT_SIZE),
+	.wNdpOutDivisor = cpu_to_le16(4),
+	.wNdpOutPayloadRemainder = cpu_to_le16(0),
+	.wNdpOutAlignment = cpu_to_le16(4),
+	.wNtbOutMaxDatagrams = 0,
+};
+
+/*
+ * Use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one
+ * packet, to simplify cancellation; and a big transfer interval, to
+ * waste less bandwidth.
+ */
+
+#define LOG2_STATUS_INTERVAL_MSEC	5	/* 1 << 5 == 32 msec */
+#define NCM_STATUS_BYTECOUNT		16	/* 8 byte header + data */
+
+static struct usb_interface_assoc_descriptor mbim_iad_desc = {
+	.bLength =		sizeof mbim_iad_desc,
+	.bDescriptorType =	USB_DT_INTERFACE_ASSOCIATION,
+
+	/* .bFirstInterface =	DYNAMIC, */
+	.bInterfaceCount =	2,	/* control + data */
+	.bFunctionClass =	2,
+	.bFunctionSubClass =	0x0e,
+	.bFunctionProtocol =	0,
+	/* .iFunction =		DYNAMIC */
+};
+
+/* interface descriptor: */
+static struct usb_interface_descriptor mbim_control_intf = {
+	.bLength =		sizeof mbim_control_intf,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	/* .bInterfaceNumber = DYNAMIC */
+	.bNumEndpoints =	1,
+	.bInterfaceClass =	0x02,
+	.bInterfaceSubClass =	0x0e,
+	.bInterfaceProtocol =	0,
+	/* .iInterface = DYNAMIC */
+};
+
+static struct usb_cdc_header_desc mbim_header_desc = {
+	.bLength =		sizeof mbim_header_desc,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_HEADER_TYPE,
+
+	.bcdCDC =		cpu_to_le16(0x0110),
+};
+
+static struct usb_cdc_union_desc mbim_union_desc = {
+	.bLength =		sizeof(mbim_union_desc),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_UNION_TYPE,
+	/* .bMasterInterface0 =	DYNAMIC */
+	/* .bSlaveInterface0 =	DYNAMIC */
+};
+
+static struct usb_cdc_mbb_desc mbb_desc = {
+	.bLength =		sizeof mbb_desc,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_MBB_TYPE,
+
+	.bcdMbbVersion =	cpu_to_le16(0x0100),
+
+	.wMaxControlMessage =	cpu_to_le16(0x1000),
+	.bNumberFilters =	0x10,
+	.bMaxFilterSize =	0x80,
+	.wMaxSegmentSize =	cpu_to_le16(0xfe0),
+	.bmNetworkCapabilities = 0x20,
+};
+
+/* the default data interface has no endpoints ... */
+static struct usb_interface_descriptor mbim_data_nop_intf = {
+	.bLength =		sizeof mbim_data_nop_intf,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	/* .bInterfaceNumber = DYNAMIC */
+	.bAlternateSetting =	0,
+	.bNumEndpoints =	0,
+	.bInterfaceClass =	0x0a,
+	.bInterfaceSubClass =	0,
+	.bInterfaceProtocol =	0x02,
+	/* .iInterface = DYNAMIC */
+};
+
+/* ... but the "real" data interface has two bulk endpoints */
+static struct usb_interface_descriptor mbim_data_intf = {
+	.bLength =		sizeof mbim_data_intf,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	/* .bInterfaceNumber = DYNAMIC */
+	.bAlternateSetting =	1,
+	.bNumEndpoints =	2,
+	.bInterfaceClass =	0x0a,
+	.bInterfaceSubClass =	0,
+	.bInterfaceProtocol =	0x02,
+	/* .iInterface = DYNAMIC */
+};
+
+/* full speed support: */
+
+static struct usb_endpoint_descriptor fs_mbim_notify_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	4*cpu_to_le16(NCM_STATUS_BYTECOUNT),
+	.bInterval =		1 << LOG2_STATUS_INTERVAL_MSEC,
+};
+
+static struct usb_endpoint_descriptor fs_mbim_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor fs_mbim_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *mbim_fs_function[] = {
+	(struct usb_descriptor_header *) &mbim_iad_desc,
+	/* MBIM control descriptors */
+	(struct usb_descriptor_header *) &mbim_control_intf,
+	(struct usb_descriptor_header *) &mbim_header_desc,
+	(struct usb_descriptor_header *) &mbb_desc,
+	(struct usb_descriptor_header *) &fs_mbim_notify_desc,
+	/* data interface, altsettings 0 and 1 */
+	(struct usb_descriptor_header *) &mbim_data_nop_intf,
+	(struct usb_descriptor_header *) &mbim_data_intf,
+	(struct usb_descriptor_header *) &fs_mbim_in_desc,
+	(struct usb_descriptor_header *) &fs_mbim_out_desc,
+	NULL,
+};
+
+/* high speed support: */
+
+static struct usb_endpoint_descriptor hs_mbim_notify_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	4*cpu_to_le16(NCM_STATUS_BYTECOUNT),
+	.bInterval =		LOG2_STATUS_INTERVAL_MSEC + 4,
+};
+static struct usb_endpoint_descriptor hs_mbim_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor hs_mbim_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *mbim_hs_function[] = {
+	(struct usb_descriptor_header *) &mbim_iad_desc,
+	/* MBIM control descriptors */
+	(struct usb_descriptor_header *) &mbim_control_intf,
+	(struct usb_descriptor_header *) &mbim_header_desc,
+	(struct usb_descriptor_header *) &mbb_desc,
+	(struct usb_descriptor_header *) &hs_mbim_notify_desc,
+	/* data interface, altsettings 0 and 1 */
+	(struct usb_descriptor_header *) &mbim_data_nop_intf,
+	(struct usb_descriptor_header *) &mbim_data_intf,
+	(struct usb_descriptor_header *) &hs_mbim_in_desc,
+	(struct usb_descriptor_header *) &hs_mbim_out_desc,
+	NULL,
+};
+
+/* string descriptors: */
+
+#define STRING_CTRL_IDX	0
+#define STRING_DATA_IDX	1
+
+static struct usb_string mbim_string_defs[] = {
+	[STRING_CTRL_IDX].s = "MBIM Control",
+	[STRING_DATA_IDX].s = "MBIM Data",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings mbim_string_table = {
+	.language =		0x0409,	/* en-us */
+	.strings =		mbim_string_defs,
+};
+
+static struct usb_gadget_strings *mbim_strings[] = {
+	&mbim_string_table,
+	NULL,
+};
+
+/*
+ * Here are options for the Datagram Pointer table (NDP) parser.
+ * There are 2 different formats: NDP16 and NDP32 in the spec (ch. 3),
+ * in NDP16 offsets and sizes fields are 1 16bit word wide,
+ * in NDP32 -- 2 16bit words wide. Also signatures are different.
+ * To make the parser code the same, put the differences in the structure,
+ * and switch pointers to the structures when the format is changed.
+ */
+
+struct ndp_parser_opts {
+	u32		nth_sign;
+	u32		ndp_sign;
+	unsigned	nth_size;
+	unsigned	ndp_size;
+	unsigned	ndplen_align;
+	/* sizes in u16 units */
+	unsigned	dgram_item_len; /* index or length */
+	unsigned	block_length;
+	unsigned	fp_index;
+	unsigned	reserved1;
+	unsigned	reserved2;
+	unsigned	next_fp_index;
+};
+
+#define INIT_NDP16_OPTS {				\
+	.nth_sign = USB_CDC_NCM_NTH16_SIGN,		\
+	.ndp_sign = USB_CDC_NCM_NDP16_NOCRC_SIGN,	\
+	.nth_size = sizeof(struct usb_cdc_ncm_nth16),	\
+	.ndp_size = sizeof(struct usb_cdc_ncm_ndp16),	\
+	.ndplen_align = 4,				\
+	.dgram_item_len = 1,				\
+	.block_length = 1,				\
+	.fp_index = 1,					\
+	.reserved1 = 0,					\
+	.reserved2 = 0,					\
+	.next_fp_index = 1,				\
+}
+
+#define INIT_NDP32_OPTS {				\
+	.nth_sign = USB_CDC_NCM_NTH32_SIGN,		\
+	.ndp_sign = USB_CDC_NCM_NDP32_NOCRC_SIGN,	\
+	.nth_size = sizeof(struct usb_cdc_ncm_nth32),	\
+	.ndp_size = sizeof(struct usb_cdc_ncm_ndp32),	\
+	.ndplen_align = 8,				\
+	.dgram_item_len = 2,				\
+	.block_length = 2,				\
+	.fp_index = 2,					\
+	.reserved1 = 1,					\
+	.reserved2 = 2,					\
+	.next_fp_index = 2,				\
+}
+
+static struct ndp_parser_opts ndp16_opts = INIT_NDP16_OPTS;
+static struct ndp_parser_opts ndp32_opts = INIT_NDP32_OPTS;
+
+static inline int mbim_lock(atomic_t *excl)
+{
+	if (atomic_inc_return(excl) == 1) {
+		return 0;
+	} else {
+		atomic_dec(excl);
+		return -EBUSY;
+	}
+}
+
+static inline void mbim_unlock(atomic_t *excl)
+{
+	atomic_dec(excl);
+}
+
+static struct ctrl_pkt *mbim_alloc_ctrl_pkt(unsigned len, gfp_t flags)
+{
+	struct ctrl_pkt *pkt;
+
+	pkt = kzalloc(sizeof(struct ctrl_pkt), flags);
+	if (!pkt)
+		return ERR_PTR(-ENOMEM);
+
+	pkt->buf = kmalloc(len, flags);
+	if (!pkt->buf) {
+		kfree(pkt);
+		return ERR_PTR(-ENOMEM);
+	}
+	pkt->len = len;
+
+	return pkt;
+}
+
+static void mbim_free_ctrl_pkt(struct ctrl_pkt *pkt)
+{
+	if (pkt) {
+		kfree(pkt->buf);
+		kfree(pkt);
+	}
+}
+
+static struct usb_request *mbim_alloc_req(struct usb_ep *ep, int buffer_size)
+{
+	struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL);
+	if (!req)
+		return NULL;
+
+	req->buf = kmalloc(buffer_size, GFP_KERNEL);
+	if (!req->buf) {
+		usb_ep_free_request(ep, req);
+		return NULL;
+	}
+	req->length = buffer_size;
+	return req;
+}
+
+void fmbim_free_req(struct usb_ep *ep, struct usb_request *req)
+{
+	if (req) {
+		kfree(req->buf);
+		usb_ep_free_request(ep, req);
+	}
+}
+
+static void fmbim_ctrl_response_available(struct f_mbim *dev)
+{
+	struct usb_request		*req = dev->not_port.notify_req;
+	struct usb_cdc_notification	*event = NULL;
+	unsigned long			flags;
+	int				ret;
+
+	int notif_c = 0;
+
+	pr_info("dev:%p portno#%d\n", dev, dev->port_num);
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	if (!atomic_read(&dev->online)) {
+		pr_info("dev:%p is not online\n", dev);
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return;
+	}
+
+	if (!req) {
+		pr_info("dev:%p req is NULL\n", dev);
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return;
+	}
+
+	if (!req->buf) {
+		pr_info("dev:%p req->buf is NULL\n", dev);
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return;
+	}
+
+	notif_c = atomic_inc_return(&dev->not_port.notify_count);
+	pr_info("atomic_inc_return[notif_c] = %d", notif_c);
+
+	event = req->buf;
+	event->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
+			| USB_RECIP_INTERFACE;
+	event->bNotificationType = USB_CDC_NOTIFY_RESPONSE_AVAILABLE;
+	event->wValue = cpu_to_le16(0);
+	event->wIndex = cpu_to_le16(dev->ctrl_id);
+	event->wLength = cpu_to_le16(0);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	pr_info("Call usb_ep_queue");
+
+	ret = usb_ep_queue(dev->not_port.notify,
+			   dev->not_port.notify_req, GFP_ATOMIC);
+	if (ret) {
+		atomic_dec(&dev->not_port.notify_count);
+		pr_err("ep enqueue error %d\n", ret);
+	}
+
+	pr_info("Succcessfull Exit");
+}
+
+static int
+fmbim_send_cpkt_response(struct f_mbim *gr, struct ctrl_pkt *cpkt)
+{
+	struct f_mbim	*dev = gr;
+	unsigned long	flags;
+
+	if (!gr || !cpkt) {
+		pr_err("Invalid cpkt, dev:%p cpkt:%p\n",
+				gr, cpkt);
+		return -ENODEV;
+	}
+
+	pr_info("dev:%p port_num#%d\n", dev, dev->port_num);
+
+	if (!atomic_read(&dev->online)) {
+		pr_info("dev:%p is not connected\n", dev);
+		mbim_free_ctrl_pkt(cpkt);
+		return 0;
+	}
+
+	spin_lock_irqsave(&dev->lock, flags);
+	list_add_tail(&cpkt->list, &dev->cpkt_resp_q);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	fmbim_ctrl_response_available(dev);
+
+	return 0;
+}
+
+/* ---------------------------- BAM INTERFACE ----------------------------- */
+
+static int mbim_bam_setup(int no_ports)
+{
+	int ret;
+
+	pr_info("no_ports:%d\n", no_ports);
+
+	ret = bam_data_setup(no_ports);
+	if (ret) {
+		pr_err("bam_data_setup failed err: %d\n", ret);
+		return ret;
+	}
+
+	pr_info("Initialized %d ports\n", no_ports);
+	return 0;
+}
+
+static int mbim_bam_connect(struct f_mbim *dev)
+{
+	int ret;
+
+	pr_info("dev:%p portno:%d\n", dev, dev->port_num);
+
+	ret = bam_data_connect(&dev->bam_port, dev->port_num, dev->port_num);
+	if (ret) {
+		pr_err("bam_data_setup failed: err:%d\n",
+				ret);
+		return ret;
+	} else {
+		pr_info("mbim bam connected\n");
+	}
+
+	return 0;
+}
+
+static int mbim_bam_disconnect(struct f_mbim *dev)
+{
+	pr_info("dev:%p port:%d. Do nothing.\n",
+			dev, dev->port_num);
+
+	/* bam_data_disconnect(&dev->bam_port, dev->port_num); */
+
+	return 0;
+}
+
+/* -------------------------------------------------------------------------*/
+
+static inline void mbim_reset_values(struct f_mbim *mbim)
+{
+	mbim->parser_opts = &ndp16_opts;
+
+	mbim->ntb_input_size = NTB_DEFAULT_IN_SIZE;
+
+	atomic_set(&mbim->not_port.notify_count, 0);
+	atomic_set(&mbim->online, 0);
+}
+
+static void mbim_reset_function_queue(struct f_mbim *dev)
+{
+	struct ctrl_pkt	*cpkt = NULL;
+
+	pr_debug("Queue empty packet for QBI");
+
+	spin_lock(&dev->lock);
+	if (!dev->is_open) {
+		pr_err("%s: mbim file handler %p is not open", __func__, dev);
+		spin_unlock(&dev->lock);
+		return;
+	}
+
+	cpkt = mbim_alloc_ctrl_pkt(0, GFP_ATOMIC);
+	if (!cpkt) {
+		pr_err("%s: Unable to allocate reset function pkt\n", __func__);
+		spin_unlock(&dev->lock);
+		return;
+	}
+
+	list_add_tail(&cpkt->list, &dev->cpkt_req_q);
+	spin_unlock(&dev->lock);
+
+	pr_debug("%s: Wake up read queue", __func__);
+	wake_up(&dev->read_wq);
+}
+
+static void fmbim_reset_cmd_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_mbim		*dev = req->context;
+
+	mbim_reset_function_queue(dev);
+}
+
+static void mbim_clear_queues(struct f_mbim *mbim)
+{
+	struct ctrl_pkt	*cpkt = NULL;
+	struct list_head *act, *tmp;
+
+	spin_lock(&mbim->lock);
+	list_for_each_safe(act, tmp, &mbim->cpkt_req_q) {
+		cpkt = list_entry(act, struct ctrl_pkt, list);
+		list_del(&cpkt->list);
+		mbim_free_ctrl_pkt(cpkt);
+	}
+	list_for_each_safe(act, tmp, &mbim->cpkt_resp_q) {
+		cpkt = list_entry(act, struct ctrl_pkt, list);
+		list_del(&cpkt->list);
+		mbim_free_ctrl_pkt(cpkt);
+	}
+	spin_unlock(&mbim->lock);
+}
+
+/*
+ * Context: mbim->lock held
+ */
+static void mbim_do_notify(struct f_mbim *mbim)
+{
+	struct usb_request		*req = mbim->not_port.notify_req;
+	struct usb_cdc_notification	*event;
+	struct usb_composite_dev	*cdev = mbim->cdev;
+	__le32				*data;
+	int				status;
+
+	pr_info("notify_state: %d", mbim->not_port.notify_state);
+
+	if (!req)
+		return;
+
+	event = req->buf;
+
+	switch (mbim->not_port.notify_state) {
+
+	case NCM_NOTIFY_NONE:
+		return;
+
+	case NCM_NOTIFY_CONNECT:
+		event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
+		if (mbim->is_open)
+			event->wValue = cpu_to_le16(1);
+		else
+			event->wValue = cpu_to_le16(0);
+		event->wLength = 0;
+		req->length = sizeof *event;
+
+		pr_info("notify connect %s\n",
+			mbim->is_open ? "true" : "false");
+		mbim->not_port.notify_state = NCM_NOTIFY_NONE;
+		break;
+
+	case NCM_NOTIFY_SPEED:
+		event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE;
+		event->wValue = cpu_to_le16(0);
+		event->wLength = cpu_to_le16(8);
+		req->length = NCM_STATUS_BYTECOUNT;
+
+		/* SPEED_CHANGE data is up/down speeds in bits/sec */
+		data = req->buf + sizeof *event;
+		data[0] = cpu_to_le32(mbim_bitrate(cdev->gadget));
+		data[1] = data[0];
+
+		pr_info("notify speed %d\n",
+			mbim_bitrate(cdev->gadget));
+		mbim->not_port.notify_state = NCM_NOTIFY_CONNECT;
+		break;
+	}
+	event->bmRequestType = 0xA1;
+	event->wIndex = cpu_to_le16(mbim->ctrl_id);
+
+	mbim->not_port.notify_req = NULL;
+	/*
+	 * In double buffering if there is a space in FIFO,
+	 * completion callback can be called right after the call,
+	 * so unlocking
+	 */
+	spin_unlock(&mbim->lock);
+	status = usb_ep_queue(mbim->not_port.notify, req, GFP_ATOMIC);
+	spin_lock(&mbim->lock);
+	if (status < 0) {
+		mbim->not_port.notify_req = req;
+		atomic_dec(&mbim->not_port.notify_count);
+		pr_err("usb_ep_queue failed, err: %d", status);
+	}
+}
+
+/*
+ * Context: mbim->lock held
+ */
+static void mbim_notify(struct f_mbim *mbim)
+{
+	/*
+	 * If mbim_notify() is called before the second (CONNECT)
+	 * notification is sent, then it will reset to send the SPEED
+	 * notificaion again (and again, and again), but it's not a problem
+	 */
+	pr_info("dev:%p\n", mbim);
+
+	mbim->not_port.notify_state = NCM_NOTIFY_SPEED;
+	mbim_do_notify(mbim);
+}
+
+static void mbim_notify_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_mbim			*mbim = req->context;
+	struct usb_cdc_notification	*event = req->buf;
+
+	int notif_c = 0;
+
+	pr_info("dev:%p\n", mbim);
+
+	spin_lock(&mbim->lock);
+	switch (req->status) {
+	case 0:
+		pr_info("Notification %02x sent\n",
+			event->bNotificationType);
+
+		notif_c = atomic_dec_return(&mbim->not_port.notify_count);
+
+		if (notif_c != 0) {
+			pr_info("Continue to mbim_do_notify()");
+			break;
+		} else {
+			pr_info("notify_count decreased to 0. Do not notify");
+			spin_unlock(&mbim->lock);
+			return;
+		}
+
+		break;
+
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		mbim->not_port.notify_state = NCM_NOTIFY_NONE;
+		atomic_set(&mbim->not_port.notify_count, 0);
+		pr_info("ESHUTDOWN/ECONNRESET, connection gone");
+		spin_unlock(&mbim->lock);
+		mbim_clear_queues(mbim);
+		mbim_reset_function_queue(mbim);
+		break;
+	default:
+		pr_err("Unknown event %02x --> %d\n",
+			event->bNotificationType, req->status);
+		break;
+	}
+
+	mbim->not_port.notify_req = req;
+	mbim_do_notify(mbim);
+
+	spin_unlock(&mbim->lock);
+
+	pr_info("dev:%p Exit\n", mbim);
+}
+
+static void mbim_ep0out_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	/* now for SET_NTB_INPUT_SIZE only */
+	unsigned		in_size = 0;
+	struct usb_function	*f = req->context;
+	struct f_mbim		*mbim = func_to_mbim(f);
+	struct mbim_ntb_input_size *ntb = NULL;
+
+	pr_info("dev:%p\n", mbim);
+
+	req->context = NULL;
+	if (req->status || req->actual != req->length) {
+		pr_err("Bad control-OUT transfer\n");
+		goto invalid;
+	}
+
+	if (req->length == 4) {
+		in_size = get_unaligned_le32(req->buf);
+		if (in_size < USB_CDC_NCM_NTB_MIN_IN_SIZE ||
+		    in_size > le32_to_cpu(ntb_parameters.dwNtbInMaxSize)) {
+			pr_err("Illegal INPUT SIZE (%d) from host\n", in_size);
+			goto invalid;
+		}
+	} else if (req->length == 8) {
+		ntb = (struct mbim_ntb_input_size *)req->buf;
+		in_size = get_unaligned_le32(&(ntb->ntb_input_size));
+		if (in_size < USB_CDC_NCM_NTB_MIN_IN_SIZE ||
+		    in_size > le32_to_cpu(ntb_parameters.dwNtbInMaxSize)) {
+			pr_err("Illegal INPUT SIZE (%d) from host\n", in_size);
+			goto invalid;
+		}
+		mbim->ntb_max_datagrams =
+			get_unaligned_le16(&(ntb->ntb_max_datagrams));
+	} else {
+		pr_err("Illegal NTB length %d\n", in_size);
+		goto invalid;
+	}
+
+	pr_info("Set NTB INPUT SIZE %d\n", in_size);
+
+	mbim->ntb_input_size = in_size;
+	return;
+
+invalid:
+	usb_ep_set_halt(ep);
+
+	pr_err("dev:%p Failed\n", mbim);
+
+	return;
+}
+
+static void
+fmbim_cmd_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_mbim		*dev = req->context;
+	struct ctrl_pkt		*cpkt = NULL;
+	int			len = req->actual;
+
+	if (!dev) {
+		pr_err("mbim dev is null\n");
+		return;
+	}
+
+	if (req->status < 0) {
+		pr_err("mbim command error %d\n", req->status);
+		return;
+	}
+
+	pr_info("dev:%p port#%d\n", dev, dev->port_num);
+
+	spin_lock(&dev->lock);
+	if (!dev->is_open) {
+		pr_err("mbim file handler %p is not open", dev);
+		spin_unlock(&dev->lock);
+		return;
+	}
+
+	cpkt = mbim_alloc_ctrl_pkt(len, GFP_ATOMIC);
+	if (!cpkt) {
+		pr_err("Unable to allocate ctrl pkt\n");
+		spin_unlock(&dev->lock);
+		return;
+	}
+
+	pr_info("Add to cpkt_req_q packet with len = %d\n", len);
+	memcpy(cpkt->buf, req->buf, len);
+	list_add_tail(&cpkt->list, &dev->cpkt_req_q);
+	spin_unlock(&dev->lock);
+
+	/* wakeup read thread */
+	pr_info("Wake up read queue");
+	wake_up(&dev->read_wq);
+
+	return;
+}
+
+static int
+mbim_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+	struct f_mbim			*mbim = func_to_mbim(f);
+	struct usb_composite_dev	*cdev = mbim->cdev;
+	struct usb_request		*req = cdev->req;
+	struct ctrl_pkt		*cpkt = NULL;
+	int	value = -EOPNOTSUPP;
+	u16	w_index = le16_to_cpu(ctrl->wIndex);
+	u16	w_value = le16_to_cpu(ctrl->wValue);
+	u16	w_length = le16_to_cpu(ctrl->wLength);
+
+	/*
+	 * composite driver infrastructure handles everything except
+	 * CDC class messages; interface activation uses set_alt().
+	 */
+
+	if (!atomic_read(&mbim->online)) {
+		pr_info("usb cable is not connected\n");
+		return -ENOTCONN;
+	}
+
+	switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_RESET_FUNCTION:
+
+		pr_info("USB_CDC_RESET_FUNCTION");
+		value = 0;
+		req->complete = fmbim_reset_cmd_complete;
+		req->context = mbim;
+		break;
+
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_SEND_ENCAPSULATED_COMMAND:
+
+		pr_info("USB_CDC_SEND_ENCAPSULATED_COMMAND");
+
+		if (w_length > req->length) {
+			pr_err("w_length > req->length: %d > %d",
+			w_length, req->length);
+		}
+		value = w_length;
+		req->complete = fmbim_cmd_complete;
+		req->context = mbim;
+		break;
+
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_GET_ENCAPSULATED_RESPONSE:
+
+		pr_info("USB_CDC_GET_ENCAPSULATED_RESPONSE");
+
+		if (w_value) {
+			pr_err("w_length > 0: %d", w_length);
+			break;
+		}
+
+		pr_info("req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+
+		spin_lock(&mbim->lock);
+		if (list_empty(&mbim->cpkt_resp_q)) {
+			pr_err("ctrl resp queue empty\n");
+			spin_unlock(&mbim->lock);
+			break;
+		}
+
+		cpkt = list_first_entry(&mbim->cpkt_resp_q,
+					struct ctrl_pkt, list);
+		list_del(&cpkt->list);
+		spin_unlock(&mbim->lock);
+
+		value = min_t(unsigned, w_length, cpkt->len);
+		memcpy(req->buf, cpkt->buf, value);
+		mbim_free_ctrl_pkt(cpkt);
+
+		pr_info("copied encapsulated_response %d bytes",
+			value);
+
+		break;
+
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_GET_NTB_PARAMETERS:
+
+		pr_info("USB_CDC_GET_NTB_PARAMETERS");
+
+		if (w_length == 0 || w_value != 0 || w_index != mbim->ctrl_id)
+			break;
+
+		value = w_length > sizeof ntb_parameters ?
+			sizeof ntb_parameters : w_length;
+		memcpy(req->buf, &ntb_parameters, value);
+		break;
+
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_GET_NTB_INPUT_SIZE:
+
+		pr_info("USB_CDC_GET_NTB_INPUT_SIZE");
+
+		if (w_length < 4 || w_value != 0 || w_index != mbim->ctrl_id)
+			break;
+
+		put_unaligned_le32(mbim->ntb_input_size, req->buf);
+		value = 4;
+		pr_info("Reply to host INPUT SIZE %d\n",
+		     mbim->ntb_input_size);
+		break;
+
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_SET_NTB_INPUT_SIZE:
+
+		pr_info("USB_CDC_SET_NTB_INPUT_SIZE");
+
+		if (w_length != 4 && w_length != 8) {
+			pr_err("wrong NTB length %d", w_length);
+			break;
+		}
+
+		if (w_value != 0 || w_index != mbim->ctrl_id)
+			break;
+
+		req->complete = mbim_ep0out_complete;
+		req->length = w_length;
+		req->context = f;
+
+		value = req->length;
+		break;
+
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_GET_NTB_FORMAT:
+	{
+		uint16_t format;
+
+		pr_info("USB_CDC_GET_NTB_FORMAT");
+
+		if (w_length < 2 || w_value != 0 || w_index != mbim->ctrl_id)
+			break;
+
+		format = (mbim->parser_opts == &ndp16_opts) ? 0x0000 : 0x0001;
+		put_unaligned_le16(format, req->buf);
+		value = 2;
+		pr_info("NTB FORMAT: sending %d\n", format);
+		break;
+	}
+
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_SET_NTB_FORMAT:
+	{
+		pr_info("USB_CDC_SET_NTB_FORMAT");
+
+		if (w_length != 0 || w_index != mbim->ctrl_id)
+			break;
+		switch (w_value) {
+		case 0x0000:
+			mbim->parser_opts = &ndp16_opts;
+			pr_info("NCM16 selected\n");
+			break;
+		case 0x0001:
+			mbim->parser_opts = &ndp32_opts;
+			pr_info("NCM32 selected\n");
+			break;
+		default:
+			break;
+		}
+		value = 0;
+		break;
+	}
+
+	/* optional in mbim descriptor: */
+	/* case USB_CDC_GET_MAX_DATAGRAM_SIZE: */
+	/* case USB_CDC_SET_MAX_DATAGRAM_SIZE: */
+
+	default:
+	pr_err("invalid control req: %02x.%02x v%04x i%04x l%d\n",
+		ctrl->bRequestType, ctrl->bRequest,
+		w_value, w_index, w_length);
+	}
+
+	 /* respond with data transfer or status phase? */
+	if (value >= 0) {
+		pr_info("control request: %02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+		req->zero = (value < w_length);
+		req->length = value;
+		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+		if (value < 0) {
+			pr_err("queueing req failed: %02x.%02x, err %d\n",
+				ctrl->bRequestType,
+			       ctrl->bRequest, value);
+		}
+	} else {
+		pr_err("ctrl req err %d: %02x.%02x v%04x i%04x l%d\n",
+			value, ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+	}
+
+	/* device either stalls (value < 0) or reports success */
+	return value;
+}
+
+static int mbim_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+	struct f_mbim		*mbim = func_to_mbim(f);
+	struct usb_composite_dev *cdev = mbim->cdev;
+	int ret = 0;
+
+	/* Control interface has only altsetting 0 */
+	if (intf == mbim->ctrl_id) {
+
+		pr_info("CONTROL_INTERFACE");
+
+		if (alt != 0)
+			goto fail;
+
+		if (mbim->not_port.notify->driver_data) {
+			pr_info("reset mbim control %d\n", intf);
+			usb_ep_disable(mbim->not_port.notify);
+		}
+
+		ret = config_ep_by_speed(cdev->gadget, f,
+					mbim->not_port.notify);
+		if (ret) {
+			mbim->not_port.notify->desc = NULL;
+			pr_err("Failed configuring notify ep %s: err %d\n",
+				mbim->not_port.notify->name, ret);
+			return ret;
+		}
+
+		ret = usb_ep_enable(mbim->not_port.notify);
+		if (ret) {
+			pr_err("usb ep#%s enable failed, err#%d\n",
+				mbim->not_port.notify->name, ret);
+			return ret;
+		}
+		mbim->not_port.notify->driver_data = mbim;
+
+	/* Data interface has two altsettings, 0 and 1 */
+	} else if (intf == mbim->data_id) {
+
+		pr_info("DATA_INTERFACE");
+
+		if (alt > 1)
+			goto fail;
+
+		if (mbim->bam_port.in->driver_data) {
+			pr_info("reset mbim\n");
+			mbim_reset_values(mbim);
+			mbim_bam_disconnect(mbim);
+		}
+
+		/*
+		 * CDC Network only sends data in non-default altsettings.
+		 * Changing altsettings resets filters, statistics, etc.
+		 */
+		if (alt == 1) {
+			pr_info("Alt set 1, initialize ports");
+
+			if (!mbim->bam_port.in->desc) {
+
+				pr_info("Choose endpoints");
+
+				ret = config_ep_by_speed(cdev->gadget, f,
+							mbim->bam_port.in);
+				if (ret) {
+					mbim->bam_port.in->desc = NULL;
+					pr_err("IN ep %s failed: %d\n",
+					mbim->bam_port.in->name, ret);
+					return ret;
+				}
+
+				pr_info("Set mbim port in_desc = 0x%p",
+					mbim->bam_port.in->desc);
+
+				ret = config_ep_by_speed(cdev->gadget, f,
+							mbim->bam_port.out);
+				if (ret) {
+					mbim->bam_port.out->desc = NULL;
+					pr_err("OUT ep %s failed: %d\n",
+					mbim->bam_port.out->name, ret);
+					return ret;
+				}
+
+				pr_info("Set mbim port out_desc = 0x%p",
+					mbim->bam_port.out->desc);
+			} else {
+				pr_info("PORTS already SET");
+			}
+
+			pr_info("Activate mbim\n");
+			mbim_bam_connect(mbim);
+		}
+
+		spin_lock(&mbim->lock);
+		mbim_notify(mbim);
+		spin_unlock(&mbim->lock);
+	} else {
+		goto fail;
+	}
+
+	atomic_set(&mbim->online, 1);
+
+	pr_info("SET DEVICE ONLINE");
+
+	/* wakeup file threads */
+	wake_up(&mbim->read_wq);
+	wake_up(&mbim->write_wq);
+
+	return 0;
+
+fail:
+	pr_err("ERROR: Illegal Interface");
+	return -EINVAL;
+}
+
+/*
+ * Because the data interface supports multiple altsettings,
+ * this MBIM function *MUST* implement a get_alt() method.
+ */
+static int mbim_get_alt(struct usb_function *f, unsigned intf)
+{
+	struct f_mbim	*mbim = func_to_mbim(f);
+
+	if (intf == mbim->ctrl_id)
+		return 0;
+	return mbim->bam_port.in->driver_data ? 1 : 0;
+}
+
+static void mbim_disable(struct usb_function *f)
+{
+	struct f_mbim	*mbim = func_to_mbim(f);
+
+	pr_info("SET DEVICE OFFLINE");
+	atomic_set(&mbim->online, 0);
+
+	mbim_clear_queues(mbim);
+	mbim_reset_function_queue(mbim);
+
+	mbim_bam_disconnect(mbim);
+
+	if (mbim->not_port.notify->driver_data) {
+		usb_ep_disable(mbim->not_port.notify);
+		mbim->not_port.notify->driver_data = NULL;
+	}
+
+	pr_info("mbim deactivated\n");
+}
+
+/*---------------------- function driver setup/binding ---------------------*/
+
+static int
+mbim_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = c->cdev;
+	struct f_mbim		*mbim = func_to_mbim(f);
+	int			status;
+	struct usb_ep		*ep;
+
+	pr_info("Enter");
+
+	mbim->cdev = cdev;
+
+	/* allocate instance-specific interface IDs */
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+	mbim->ctrl_id = status;
+	mbim_iad_desc.bFirstInterface = status;
+
+	mbim_control_intf.bInterfaceNumber = status;
+	mbim_union_desc.bMasterInterface0 = status;
+
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+	mbim->data_id = status;
+
+	mbim_data_nop_intf.bInterfaceNumber = status;
+	mbim_data_intf.bInterfaceNumber = status;
+	mbim_union_desc.bSlaveInterface0 = status;
+
+	status = -ENODEV;
+
+	/* allocate instance-specific endpoints */
+	ep = usb_ep_autoconfig(cdev->gadget, &fs_mbim_in_desc);
+	if (!ep) {
+		pr_err("usb epin autoconfig failed\n");
+		goto fail;
+	}
+	pr_info("usb epin autoconfig succeeded\n");
+	ep->driver_data = cdev;	/* claim */
+	mbim->bam_port.in = ep;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &fs_mbim_out_desc);
+	if (!ep) {
+		pr_err("usb epout autoconfig failed\n");
+		goto fail;
+	}
+	pr_info("usb epout autoconfig succeeded\n");
+	ep->driver_data = cdev;	/* claim */
+	mbim->bam_port.out = ep;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &fs_mbim_notify_desc);
+	if (!ep) {
+		pr_err("usb notify ep autoconfig failed\n");
+		goto fail;
+	}
+	pr_info("usb notify ep autoconfig succeeded\n");
+	mbim->not_port.notify = ep;
+	ep->driver_data = cdev;	/* claim */
+
+	status = -ENOMEM;
+
+	/* allocate notification request and buffer */
+	mbim->not_port.notify_req = mbim_alloc_req(ep, NCM_STATUS_BYTECOUNT);
+	if (!mbim->not_port.notify_req) {
+		pr_info("failed to allocate notify request\n");
+		goto fail;
+	}
+	pr_info("allocated notify ep request & request buffer\n");
+
+	mbim->not_port.notify_req->context = mbim;
+	mbim->not_port.notify_req->complete = mbim_notify_complete;
+
+	/* copy descriptors, and track endpoint copies */
+	f->descriptors = usb_copy_descriptors(mbim_fs_function);
+	if (!f->descriptors)
+		goto fail;
+
+	/*
+	 * support all relevant hardware speeds... we expect that when
+	 * hardware is dual speed, all bulk-capable endpoints work at
+	 * both speeds
+	 */
+	if (gadget_is_dualspeed(c->cdev->gadget)) {
+		hs_mbim_in_desc.bEndpointAddress =
+				fs_mbim_in_desc.bEndpointAddress;
+		hs_mbim_out_desc.bEndpointAddress =
+				fs_mbim_out_desc.bEndpointAddress;
+		hs_mbim_notify_desc.bEndpointAddress =
+				fs_mbim_notify_desc.bEndpointAddress;
+
+		/* copy descriptors, and track endpoint copies */
+		f->hs_descriptors = usb_copy_descriptors(mbim_hs_function);
+		if (!f->hs_descriptors)
+			goto fail;
+	}
+
+	pr_info("mbim(%d): %s speed IN/%s OUT/%s NOTIFY/%s\n",
+			mbim->port_num,
+			gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+			mbim->bam_port.in->name, mbim->bam_port.out->name,
+			mbim->not_port.notify->name);
+
+	return 0;
+
+fail:
+	pr_err("%s failed to bind, err %d\n", f->name, status);
+
+	if (f->descriptors)
+		usb_free_descriptors(f->descriptors);
+
+	if (mbim->not_port.notify_req) {
+		kfree(mbim->not_port.notify_req->buf);
+		usb_ep_free_request(mbim->not_port.notify,
+				    mbim->not_port.notify_req);
+	}
+
+	/* we might as well release our claims on endpoints */
+	if (mbim->not_port.notify)
+		mbim->not_port.notify->driver_data = NULL;
+	if (mbim->bam_port.out)
+		mbim->bam_port.out->driver_data = NULL;
+	if (mbim->bam_port.in)
+		mbim->bam_port.in->driver_data = NULL;
+
+	return status;
+}
+
+static void mbim_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct f_mbim	*mbim = func_to_mbim(f);
+
+	if (gadget_is_dualspeed(c->cdev->gadget))
+		usb_free_descriptors(f->hs_descriptors);
+	usb_free_descriptors(f->descriptors);
+
+	kfree(mbim->not_port.notify_req->buf);
+	usb_ep_free_request(mbim->not_port.notify, mbim->not_port.notify_req);
+}
+
+/**
+ * mbim_bind_config - add MBIM link to a configuration
+ * @c: the configuration to support the network link
+ * Context: single threaded during gadget setup
+ * Returns zero on success, else negative errno.
+ */
+int mbim_bind_config(struct usb_configuration *c, unsigned portno)
+{
+	struct f_mbim	*mbim = NULL;
+	int status = 0;
+
+	pr_info("port number %u", portno);
+
+	if (portno >= nr_mbim_ports) {
+		pr_err("Can not add port %u. Max ports = %d",
+		       portno, nr_mbim_ports);
+		return -ENODEV;
+	}
+
+	status = mbim_bam_setup(nr_mbim_ports);
+	if (status) {
+		pr_err("bam setup failed");
+		return status;
+	}
+
+	/* maybe allocate device-global string IDs */
+	if (mbim_string_defs[0].id == 0) {
+
+		/* control interface label */
+		status = usb_string_id(c->cdev);
+		if (status < 0)
+			return status;
+		mbim_string_defs[STRING_CTRL_IDX].id = status;
+		mbim_control_intf.iInterface = status;
+
+		/* data interface label */
+		status = usb_string_id(c->cdev);
+		if (status < 0)
+			return status;
+		mbim_string_defs[STRING_DATA_IDX].id = status;
+		mbim_data_nop_intf.iInterface = status;
+		mbim_data_intf.iInterface = status;
+	}
+
+	/* allocate and initialize one new instance */
+	mbim = mbim_ports[0].port;
+	if (!mbim) {
+		pr_info("mbim struct not allocated");
+		return -ENOMEM;
+	}
+
+	mbim->cdev = c->cdev;
+
+	spin_lock_init(&mbim->lock);
+
+	mbim_reset_values(mbim);
+
+	mbim->function.name = "usb_mbim";
+	mbim->function.strings = mbim_strings;
+	mbim->function.bind = mbim_bind;
+	mbim->function.unbind = mbim_unbind;
+	mbim->function.set_alt = mbim_set_alt;
+	mbim->function.get_alt = mbim_get_alt;
+	mbim->function.setup = mbim_setup;
+	mbim->function.disable = mbim_disable;
+
+	INIT_LIST_HEAD(&mbim->cpkt_req_q);
+	INIT_LIST_HEAD(&mbim->cpkt_resp_q);
+
+	status = usb_add_function(c, &mbim->function);
+
+	pr_info("Exit status %d", status);
+
+	return status;
+}
+
+/* ------------ MBIM DRIVER File Operations API for USER SPACE ------------ */
+
+static ssize_t
+mbim_read(struct file *fp, char __user *buf, size_t count, loff_t *pos)
+{
+	struct f_mbim *dev = fp->private_data;
+	struct ctrl_pkt *cpkt = NULL;
+	int ret = 0;
+
+	pr_debug("Enter(%d)\n", count);
+
+	if (!dev) {
+		pr_err("Received NULL mbim pointer\n");
+		return -ENODEV;
+	}
+
+	if (count > MBIM_BULK_BUFFER_SIZE) {
+		pr_err("Buffer size is too big %d, should be at most %d\n",
+			count, MBIM_BULK_BUFFER_SIZE);
+		return -EINVAL;
+	}
+
+	if (mbim_lock(&dev->read_excl)) {
+		pr_err("Previous reading is not finished yet\n");
+		return -EBUSY;
+	}
+
+	/* block until mbim online */
+	while (!(atomic_read(&dev->online) || atomic_read(&dev->error))) {
+		pr_err("USB cable not connected. Wait.\n");
+		ret = wait_event_interruptible(dev->read_wq,
+			(atomic_read(&dev->online) ||
+			atomic_read(&dev->error)));
+		if (ret < 0) {
+			mbim_unlock(&dev->read_excl);
+			return 0;
+		}
+	}
+
+	if (atomic_read(&dev->error)) {
+		mbim_unlock(&dev->read_excl);
+		return -EIO;
+	}
+
+	while (list_empty(&dev->cpkt_req_q)) {
+		pr_err("Requests list is empty. Wait.\n");
+		ret = wait_event_interruptible(dev->read_wq,
+			!list_empty(&dev->cpkt_req_q));
+		if (ret < 0) {
+			pr_err("Waiting failed\n");
+			mbim_unlock(&dev->read_excl);
+			return 0;
+		}
+		pr_debug("Received request packet\n");
+	}
+
+	cpkt = list_first_entry(&dev->cpkt_req_q, struct ctrl_pkt,
+							list);
+	if (cpkt->len > count) {
+		mbim_unlock(&dev->read_excl);
+		pr_err("cpkt size too big:%d > buf size:%d\n",
+				cpkt->len, count);
+		return -ENOMEM;
+	}
+
+	pr_debug("cpkt size:%d\n", cpkt->len);
+
+	list_del(&cpkt->list);
+	mbim_unlock(&dev->read_excl);
+
+	ret = copy_to_user(buf, cpkt->buf, cpkt->len);
+	if (ret) {
+		pr_err("copy_to_user failed: err %d\n", ret);
+		ret = 0;
+	} else {
+		pr_debug("copied %d bytes to user\n", cpkt->len);
+		ret = cpkt->len;
+	}
+
+	mbim_free_ctrl_pkt(cpkt);
+
+	return ret;
+}
+
+static ssize_t
+mbim_write(struct file *fp, const char __user *buf, size_t count, loff_t *pos)
+{
+	struct f_mbim *dev = fp->private_data;
+	struct ctrl_pkt *cpkt = NULL;
+	int ret = 0;
+
+	pr_debug("Enter(%d)", count);
+
+	if (!dev) {
+		pr_err("Received NULL mbim pointer\n");
+		return -ENODEV;
+	}
+
+	if (!count) {
+		pr_err("zero length ctrl pkt\n");
+		return -ENODEV;
+	}
+
+	if (count > MAX_CTRL_PKT_SIZE) {
+		pr_err("given pkt size too big:%d > max_pkt_size:%d\n",
+				count, MAX_CTRL_PKT_SIZE);
+		return -ENOMEM;
+	}
+
+	if (mbim_lock(&dev->write_excl)) {
+		pr_err("Previous writing not finished yet\n");
+		return -EBUSY;
+	}
+
+	if (!atomic_read(&dev->online)) {
+		pr_err("USB cable not connected\n");
+		mbim_unlock(&dev->write_excl);
+		return -EPIPE;
+	}
+
+	cpkt = mbim_alloc_ctrl_pkt(count, GFP_KERNEL);
+	if (!cpkt) {
+		pr_err("failed to allocate ctrl pkt\n");
+		mbim_unlock(&dev->write_excl);
+		return -ENOMEM;
+	}
+
+	ret = copy_from_user(cpkt->buf, buf, count);
+	if (ret) {
+		pr_err("copy_from_user failed err:%d\n", ret);
+		mbim_free_ctrl_pkt(cpkt);
+		mbim_unlock(&dev->write_excl);
+		return 0;
+	}
+
+	fmbim_send_cpkt_response(dev, cpkt);
+
+	mbim_unlock(&dev->write_excl);
+
+	pr_debug("Exit(%d)", count);
+
+	return count;
+}
+
+static int mbim_open(struct inode *ip, struct file *fp)
+{
+	pr_info("Open mbim driver\n");
+
+	while (!_mbim_dev) {
+		pr_err("mbim_dev not created yet\n");
+		return -ENODEV;
+	}
+
+	if (mbim_lock(&_mbim_dev->open_excl)) {
+		pr_err("Already opened\n");
+		return -EBUSY;
+	}
+
+	pr_info("Lock mbim_dev->open_excl for open\n");
+
+	if (!atomic_read(&_mbim_dev->online))
+		pr_err("USB cable not connected\n");
+
+	fp->private_data = _mbim_dev;
+
+	atomic_set(&_mbim_dev->error, 0);
+
+	spin_lock(&_mbim_dev->lock);
+	_mbim_dev->is_open = true;
+	mbim_notify(_mbim_dev);
+	spin_unlock(&_mbim_dev->lock);
+
+	pr_info("Exit, mbim file opened\n");
+
+	return 0;
+}
+
+static int mbim_release(struct inode *ip, struct file *fp)
+{
+	struct f_mbim *mbim = fp->private_data;
+
+	pr_info("Close mbim file");
+
+	spin_lock(&mbim->lock);
+	mbim->is_open = false;
+	mbim_notify(mbim);
+	spin_unlock(&mbim->lock);
+
+	mbim_unlock(&_mbim_dev->open_excl);
+
+	return 0;
+}
+
+static long mbim_ioctl(struct file *fp, unsigned cmd, unsigned long arg)
+{
+	struct f_mbim *mbim = fp->private_data;
+	int ret = 0;
+
+	pr_info("Received command %d", cmd);
+
+	if (mbim_lock(&mbim->ioctl_excl))
+		return -EBUSY;
+
+	switch (cmd) {
+	case MBIM_GET_NTB_SIZE:
+		ret = copy_to_user((void __user *)arg,
+			&mbim->ntb_input_size, sizeof(mbim->ntb_input_size));
+		if (ret) {
+			pr_err("copying to user space failed");
+			ret = -EFAULT;
+		}
+		pr_info("Sent NTB size %d", mbim->ntb_input_size);
+		break;
+	case MBIM_GET_DATAGRAM_COUNT:
+		ret = copy_to_user((void __user *)arg,
+			&mbim->ntb_max_datagrams,
+			sizeof(mbim->ntb_max_datagrams));
+		if (ret) {
+			pr_err("copying to user space failed");
+			ret = -EFAULT;
+		}
+		pr_info("Sent NTB datagrams count %d",
+			mbim->ntb_max_datagrams);
+		break;
+	default:
+		pr_err("wrong parameter");
+		ret = -EINVAL;
+	}
+
+	mbim_unlock(&mbim->ioctl_excl);
+
+	return ret;
+}
+
+/* file operations for MBIM device /dev/android_mbim */
+static const struct file_operations mbim_fops = {
+	.owner = THIS_MODULE,
+	.open = mbim_open,
+	.release = mbim_release,
+	.read = mbim_read,
+	.write = mbim_write,
+	.unlocked_ioctl	= mbim_ioctl,
+};
+
+static struct miscdevice mbim_device = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "android_mbim",
+	.fops = &mbim_fops,
+};
+
+static int mbim_init(int instances)
+{
+	int i;
+	struct f_mbim *dev = NULL;
+	int ret;
+
+	pr_info("initialize %d instances\n", instances);
+
+	if (instances > NR_MBIM_PORTS) {
+		pr_err("Max-%d instances supported\n", NR_MBIM_PORTS);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < instances; i++) {
+		dev = kzalloc(sizeof(struct f_mbim), GFP_KERNEL);
+		if (!dev) {
+			pr_err("Failed to allocate mbim dev\n");
+			ret = -ENOMEM;
+			goto fail_probe;
+		}
+
+		dev->port_num = i;
+		spin_lock_init(&dev->lock);
+		INIT_LIST_HEAD(&dev->cpkt_req_q);
+		INIT_LIST_HEAD(&dev->cpkt_resp_q);
+
+		mbim_ports[i].port = dev;
+		mbim_ports[i].port_num = i;
+
+		init_waitqueue_head(&dev->read_wq);
+		init_waitqueue_head(&dev->write_wq);
+
+		atomic_set(&dev->open_excl, 0);
+		atomic_set(&dev->ioctl_excl, 0);
+		atomic_set(&dev->read_excl, 0);
+		atomic_set(&dev->write_excl, 0);
+
+		nr_mbim_ports++;
+
+	}
+
+	_mbim_dev = dev;
+	ret = misc_register(&mbim_device);
+	if (ret) {
+		pr_err("mbim driver failed to register");
+		goto fail_probe;
+	}
+
+	pr_info("Initialized %d ports\n", nr_mbim_ports);
+
+	return ret;
+
+fail_probe:
+	pr_err("Failed");
+	for (i = 0; i < nr_mbim_ports; i++) {
+		kfree(mbim_ports[i].port);
+		mbim_ports[i].port = NULL;
+	}
+
+	return ret;
+}
+
+static void fmbim_cleanup(void)
+{
+	int i = 0;
+
+	pr_info("Enter");
+
+	for (i = 0; i < nr_mbim_ports; i++) {
+		kfree(mbim_ports[i].port);
+		mbim_ports[i].port = NULL;
+	}
+	nr_mbim_ports = 0;
+
+	misc_deregister(&mbim_device);
+
+	_mbim_dev = NULL;
+}
+
diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c
index 1638977..0394b0b 100644
--- a/drivers/usb/gadget/f_mtp.c
+++ b/drivers/usb/gadget/f_mtp.c
@@ -410,15 +410,6 @@
 	ep->driver_data = dev;		/* claim the endpoint */
 	dev->ep_out = ep;
 
-	ep = usb_ep_autoconfig(cdev->gadget, out_desc);
-	if (!ep) {
-		DBG(cdev, "usb_ep_autoconfig for ep_out failed\n");
-		return -ENODEV;
-	}
-	DBG(cdev, "usb_ep_autoconfig for mtp ep_out got %s\n", ep->name);
-	ep->driver_data = dev;		/* claim the endpoint */
-	dev->ep_out = ep;
-
 	ep = usb_ep_autoconfig(cdev->gadget, intr_desc);
 	if (!ep) {
 		DBG(cdev, "usb_ep_autoconfig for ep_intr failed\n");
@@ -504,7 +495,17 @@
 	}
 
 	/* wait for a request to complete */
-	ret = wait_event_interruptible(dev->read_wq, dev->rx_done);
+	ret = wait_event_interruptible(dev->read_wq,
+				dev->rx_done || dev->state != STATE_BUSY);
+	if (dev->state == STATE_CANCELED) {
+		r = -ECANCELED;
+		if (!dev->rx_done)
+			usb_ep_dequeue(dev->ep_out, req);
+		spin_lock_irq(&dev->lock);
+		dev->state = STATE_CANCELED;
+		spin_unlock_irq(&dev->lock);
+		goto done;
+	}
 	if (ret < 0) {
 		r = ret;
 		usb_ep_dequeue(dev->ep_out, req);
@@ -709,7 +710,8 @@
 		ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL);
 		if (ret < 0) {
 			DBG(cdev, "send_file_work: xfer error %d\n", ret);
-			dev->state = STATE_ERROR;
+			if (dev->state != STATE_OFFLINE)
+				dev->state = STATE_ERROR;
 			r = -EIO;
 			break;
 		}
@@ -762,7 +764,8 @@
 			ret = usb_ep_queue(dev->ep_out, read_req, GFP_KERNEL);
 			if (ret < 0) {
 				r = -EIO;
-				dev->state = STATE_ERROR;
+				if (dev->state != STATE_OFFLINE)
+					dev->state = STATE_ERROR;
 				break;
 			}
 		}
@@ -774,7 +777,8 @@
 			DBG(cdev, "vfs_write %d\n", ret);
 			if (ret != write_req->actual) {
 				r = -EIO;
-				dev->state = STATE_ERROR;
+				if (dev->state != STATE_OFFLINE)
+					dev->state = STATE_ERROR;
 				break;
 			}
 			write_req = NULL;
@@ -1141,27 +1145,35 @@
 	DBG(cdev, "mtp_function_set_alt intf: %d alt: %d\n", intf, alt);
 
 	ret = config_ep_by_speed(cdev->gadget, f, dev->ep_in);
-	if (ret)
-		return ret;
-
-	ret = usb_ep_enable(dev->ep_in);
-	if (ret)
-		return ret;
-
-	ret = config_ep_by_speed(cdev->gadget, f, dev->ep_out);
-	if (ret)
-		return ret;
-
-	ret = usb_ep_enable(dev->ep_out);
 	if (ret) {
-		usb_ep_disable(dev->ep_in);
+		dev->ep_in->desc = NULL;
+		ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
+			dev->ep_in->name, ret);
+		return ret;
+	}
+	ret = usb_ep_enable(dev->ep_in);
+	if (ret) {
+		ERROR(cdev, "failed to enable ep %s, result %d\n",
+			dev->ep_in->name, ret);
 		return ret;
 	}
 
-	ret = config_ep_by_speed(cdev->gadget, f, dev->ep_intr);
-	if (ret)
+	ret = config_ep_by_speed(cdev->gadget, f, dev->ep_out);
+	if (ret) {
+		dev->ep_out->desc = NULL;
+		ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
+			dev->ep_out->name, ret);
+		usb_ep_disable(dev->ep_in);
 		return ret;
-
+	}
+	ret = usb_ep_enable(dev->ep_out);
+	if (ret) {
+		ERROR(cdev, "failed to enable ep %s, result %d\n",
+			dev->ep_out->name, ret);
+		usb_ep_disable(dev->ep_in);
+		return ret;
+	}
+	dev->ep_intr->desc = &mtp_intr_desc;
 	ret = usb_ep_enable(dev->ep_intr);
 	if (ret) {
 		usb_ep_disable(dev->ep_out);
diff --git a/drivers/usb/gadget/f_rmnet.c b/drivers/usb/gadget/f_rmnet.c
new file mode 100644
index 0000000..414a7b9
--- /dev/null
+++ b/drivers/usb/gadget/f_rmnet.c
@@ -0,0 +1,1162 @@
+/*
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/usb/android_composite.h>
+#include <linux/spinlock.h>
+
+#include <mach/usb_gadget_xport.h>
+
+#include "u_rmnet.h"
+#include "gadget_chips.h"
+
+#define RMNET_NOTIFY_INTERVAL	5
+#define RMNET_MAX_NOTIFY_SIZE	sizeof(struct usb_cdc_notification)
+
+
+#define ACM_CTRL_DTR	(1 << 0)
+
+/* TODO: use separate structures for data and
+ * control paths
+ */
+struct f_rmnet {
+	struct grmnet			port;
+	int				ifc_id;
+	u8				port_num;
+	atomic_t			online;
+	atomic_t			ctrl_online;
+	struct usb_composite_dev	*cdev;
+
+	spinlock_t			lock;
+
+	/* usb eps*/
+	struct usb_ep			*notify;
+	struct usb_request		*notify_req;
+
+	/* control info */
+	struct list_head		cpkt_resp_q;
+	atomic_t			notify_count;
+	unsigned long			cpkts_len;
+};
+
+#define NR_RMNET_PORTS	3
+static unsigned int nr_rmnet_ports;
+static unsigned int no_ctrl_smd_ports;
+static unsigned int no_ctrl_hsic_ports;
+static unsigned int no_ctrl_hsuart_ports;
+static unsigned int no_data_bam_ports;
+static unsigned int no_data_bam2bam_ports;
+static unsigned int no_data_hsic_ports;
+static unsigned int no_data_hsuart_ports;
+static struct rmnet_ports {
+	enum transport_type		data_xport;
+	enum transport_type		ctrl_xport;
+	unsigned			data_xport_num;
+	unsigned			ctrl_xport_num;
+	unsigned			port_num;
+	struct f_rmnet			*port;
+} rmnet_ports[NR_RMNET_PORTS];
+
+static struct usb_interface_descriptor rmnet_interface_desc = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	.bNumEndpoints =	3,
+	.bInterfaceClass =	USB_CLASS_VENDOR_SPEC,
+	.bInterfaceSubClass =	USB_CLASS_VENDOR_SPEC,
+	.bInterfaceProtocol =	USB_CLASS_VENDOR_SPEC,
+	/* .iInterface = DYNAMIC */
+};
+
+/* Full speed support */
+static struct usb_endpoint_descriptor rmnet_fs_notify_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	__constant_cpu_to_le16(RMNET_MAX_NOTIFY_SIZE),
+	.bInterval =		1 << RMNET_NOTIFY_INTERVAL,
+};
+
+static struct usb_endpoint_descriptor rmnet_fs_in_desc  = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize   = __constant_cpu_to_le16(64),
+};
+
+static struct usb_endpoint_descriptor rmnet_fs_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize   = __constant_cpu_to_le16(64),
+};
+
+static struct usb_descriptor_header *rmnet_fs_function[] = {
+	(struct usb_descriptor_header *) &rmnet_interface_desc,
+	(struct usb_descriptor_header *) &rmnet_fs_notify_desc,
+	(struct usb_descriptor_header *) &rmnet_fs_in_desc,
+	(struct usb_descriptor_header *) &rmnet_fs_out_desc,
+	NULL,
+};
+
+/* High speed support */
+static struct usb_endpoint_descriptor rmnet_hs_notify_desc  = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	__constant_cpu_to_le16(RMNET_MAX_NOTIFY_SIZE),
+	.bInterval =		RMNET_NOTIFY_INTERVAL + 4,
+};
+
+static struct usb_endpoint_descriptor rmnet_hs_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	__constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor rmnet_hs_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	__constant_cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *rmnet_hs_function[] = {
+	(struct usb_descriptor_header *) &rmnet_interface_desc,
+	(struct usb_descriptor_header *) &rmnet_hs_notify_desc,
+	(struct usb_descriptor_header *) &rmnet_hs_in_desc,
+	(struct usb_descriptor_header *) &rmnet_hs_out_desc,
+	NULL,
+};
+
+/* String descriptors */
+
+static struct usb_string rmnet_string_defs[] = {
+	[0].s = "RmNet",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings rmnet_string_table = {
+	.language =		0x0409,	/* en-us */
+	.strings =		rmnet_string_defs,
+};
+
+static struct usb_gadget_strings *rmnet_strings[] = {
+	&rmnet_string_table,
+	NULL,
+};
+
+static void frmnet_ctrl_response_available(struct f_rmnet *dev);
+
+/* ------- misc functions --------------------*/
+
+static inline struct f_rmnet *func_to_rmnet(struct usb_function *f)
+{
+	return container_of(f, struct f_rmnet, port.func);
+}
+
+static inline struct f_rmnet *port_to_rmnet(struct grmnet *r)
+{
+	return container_of(r, struct f_rmnet, port);
+}
+
+static struct usb_request *
+frmnet_alloc_req(struct usb_ep *ep, unsigned len, gfp_t flags)
+{
+	struct usb_request *req;
+
+	req = usb_ep_alloc_request(ep, flags);
+	if (!req)
+		return ERR_PTR(-ENOMEM);
+
+	req->buf = kmalloc(len, flags);
+	if (!req->buf) {
+		usb_ep_free_request(ep, req);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	req->length = len;
+
+	return req;
+}
+
+void frmnet_free_req(struct usb_ep *ep, struct usb_request *req)
+{
+	kfree(req->buf);
+	usb_ep_free_request(ep, req);
+}
+
+static struct rmnet_ctrl_pkt *rmnet_alloc_ctrl_pkt(unsigned len, gfp_t flags)
+{
+	struct rmnet_ctrl_pkt *pkt;
+
+	pkt = kzalloc(sizeof(struct rmnet_ctrl_pkt), flags);
+	if (!pkt)
+		return ERR_PTR(-ENOMEM);
+
+	pkt->buf = kmalloc(len, flags);
+	if (!pkt->buf) {
+		kfree(pkt);
+		return ERR_PTR(-ENOMEM);
+	}
+	pkt->len = len;
+
+	return pkt;
+}
+
+static void rmnet_free_ctrl_pkt(struct rmnet_ctrl_pkt *pkt)
+{
+	kfree(pkt->buf);
+	kfree(pkt);
+}
+
+/* -------------------------------------------*/
+
+static int rmnet_gport_setup(void)
+{
+	int	ret;
+	int	port_idx;
+	int	i;
+
+	pr_debug("%s: bam ports: %u bam2bam ports: %u data hsic ports: %u data hsuart ports: %u"
+		" smd ports: %u ctrl hsic ports: %u ctrl hsuart ports: %u"
+	" nr_rmnet_ports: %u\n",
+		__func__, no_data_bam_ports, no_data_bam2bam_ports,
+		no_data_hsic_ports, no_data_hsuart_ports, no_ctrl_smd_ports,
+		no_ctrl_hsic_ports, no_ctrl_hsuart_ports, nr_rmnet_ports);
+
+	if (no_data_bam_ports || no_data_bam2bam_ports) {
+		ret = gbam_setup(no_data_bam_ports,
+						 no_data_bam2bam_ports);
+		if (ret)
+			return ret;
+	}
+
+	if (no_ctrl_smd_ports) {
+		ret = gsmd_ctrl_setup(no_ctrl_smd_ports);
+		if (ret)
+			return ret;
+	}
+
+	if (no_data_hsic_ports) {
+		port_idx = ghsic_data_setup(no_data_hsic_ports,
+				USB_GADGET_RMNET);
+		if (port_idx < 0)
+			return port_idx;
+		for (i = 0; i < nr_rmnet_ports; i++) {
+			if (rmnet_ports[i].data_xport ==
+					USB_GADGET_XPORT_HSIC) {
+				rmnet_ports[i].data_xport_num = port_idx;
+				port_idx++;
+			}
+		}
+	}
+
+	if (no_ctrl_hsic_ports) {
+		port_idx = ghsic_ctrl_setup(no_ctrl_hsic_ports,
+				USB_GADGET_RMNET);
+		if (port_idx < 0)
+			return port_idx;
+		for (i = 0; i < nr_rmnet_ports; i++) {
+			if (rmnet_ports[i].ctrl_xport ==
+					USB_GADGET_XPORT_HSIC) {
+				rmnet_ports[i].ctrl_xport_num = port_idx;
+				port_idx++;
+			}
+		}
+	}
+
+	if (no_data_hsuart_ports) {
+		port_idx = ghsuart_data_setup(no_data_hsuart_ports,
+				USB_GADGET_RMNET);
+		if (port_idx < 0)
+			return port_idx;
+		for (i = 0; i < nr_rmnet_ports; i++) {
+			if (rmnet_ports[i].data_xport ==
+					USB_GADGET_XPORT_HSUART) {
+				rmnet_ports[i].data_xport_num = port_idx;
+				port_idx++;
+			}
+		}
+	}
+
+	if (no_ctrl_hsuart_ports) {
+		port_idx = ghsuart_ctrl_setup(no_ctrl_hsuart_ports,
+				USB_GADGET_RMNET);
+		if (port_idx < 0)
+			return port_idx;
+		for (i = 0; i < nr_rmnet_ports; i++) {
+			if (rmnet_ports[i].ctrl_xport ==
+					USB_GADGET_XPORT_HSUART) {
+				rmnet_ports[i].ctrl_xport_num = port_idx;
+				port_idx++;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int gport_rmnet_connect(struct f_rmnet *dev)
+{
+	int			ret;
+	unsigned		port_num;
+	enum transport_type	cxport = rmnet_ports[dev->port_num].ctrl_xport;
+	enum transport_type	dxport = rmnet_ports[dev->port_num].data_xport;
+
+	pr_debug("%s: ctrl xport: %s data xport: %s dev: %p portno: %d\n",
+			__func__, xport_to_str(cxport), xport_to_str(dxport),
+			dev, dev->port_num);
+
+	port_num = rmnet_ports[dev->port_num].ctrl_xport_num;
+	switch (cxport) {
+	case USB_GADGET_XPORT_SMD:
+		ret = gsmd_ctrl_connect(&dev->port, port_num);
+		if (ret) {
+			pr_err("%s: gsmd_ctrl_connect failed: err:%d\n",
+					__func__, ret);
+			return ret;
+		}
+		break;
+	case USB_GADGET_XPORT_HSIC:
+		ret = ghsic_ctrl_connect(&dev->port, port_num);
+		if (ret) {
+			pr_err("%s: ghsic_ctrl_connect failed: err:%d\n",
+					__func__, ret);
+			return ret;
+		}
+		break;
+	case USB_GADGET_XPORT_HSUART:
+		ret = ghsuart_ctrl_connect(&dev->port, port_num);
+		if (ret) {
+			pr_err("%s: ghsuart_ctrl_connect failed: err:%d\n",
+					__func__, ret);
+			return ret;
+		}
+		break;
+	case USB_GADGET_XPORT_NONE:
+		break;
+	default:
+		pr_err("%s: Un-supported transport: %s\n", __func__,
+				xport_to_str(cxport));
+		return -ENODEV;
+	}
+
+	port_num = rmnet_ports[dev->port_num].data_xport_num;
+	switch (dxport) {
+	case USB_GADGET_XPORT_BAM:
+	case USB_GADGET_XPORT_BAM2BAM:
+		ret = gbam_connect(&dev->port, port_num,
+						   dxport, port_num);
+		if (ret) {
+			pr_err("%s: gbam_connect failed: err:%d\n",
+					__func__, ret);
+			gsmd_ctrl_disconnect(&dev->port, port_num);
+			return ret;
+		}
+		break;
+	case USB_GADGET_XPORT_HSIC:
+		ret = ghsic_data_connect(&dev->port, port_num);
+		if (ret) {
+			pr_err("%s: ghsic_data_connect failed: err:%d\n",
+					__func__, ret);
+			ghsic_ctrl_disconnect(&dev->port, port_num);
+			return ret;
+		}
+		break;
+	case USB_GADGET_XPORT_HSUART:
+		ret = ghsuart_data_connect(&dev->port, port_num);
+		if (ret) {
+			pr_err("%s: ghsuart_data_connect failed: err:%d\n",
+					__func__, ret);
+			ghsuart_ctrl_disconnect(&dev->port, port_num);
+			return ret;
+		}
+		break;
+	case USB_GADGET_XPORT_NONE:
+		 break;
+	default:
+		pr_err("%s: Un-supported transport: %s\n", __func__,
+				xport_to_str(dxport));
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int gport_rmnet_disconnect(struct f_rmnet *dev)
+{
+	unsigned		port_num;
+	enum transport_type	cxport = rmnet_ports[dev->port_num].ctrl_xport;
+	enum transport_type	dxport = rmnet_ports[dev->port_num].data_xport;
+
+	pr_debug("%s: ctrl xport: %s data xport: %s dev: %p portno: %d\n",
+			__func__, xport_to_str(cxport), xport_to_str(dxport),
+			dev, dev->port_num);
+
+	port_num = rmnet_ports[dev->port_num].ctrl_xport_num;
+	switch (cxport) {
+	case USB_GADGET_XPORT_SMD:
+		gsmd_ctrl_disconnect(&dev->port, port_num);
+		break;
+	case USB_GADGET_XPORT_HSIC:
+		ghsic_ctrl_disconnect(&dev->port, port_num);
+		break;
+	case USB_GADGET_XPORT_HSUART:
+		ghsuart_ctrl_disconnect(&dev->port, port_num);
+		break;
+	case USB_GADGET_XPORT_NONE:
+		break;
+	default:
+		pr_err("%s: Un-supported transport: %s\n", __func__,
+				xport_to_str(cxport));
+		return -ENODEV;
+	}
+
+	port_num = rmnet_ports[dev->port_num].data_xport_num;
+	switch (dxport) {
+	case USB_GADGET_XPORT_BAM:
+	case USB_GADGET_XPORT_BAM2BAM:
+		gbam_disconnect(&dev->port, port_num, dxport);
+		break;
+	case USB_GADGET_XPORT_HSIC:
+		ghsic_data_disconnect(&dev->port, port_num);
+		break;
+	case USB_GADGET_XPORT_HSUART:
+		ghsuart_data_disconnect(&dev->port, port_num);
+		break;
+	case USB_GADGET_XPORT_NONE:
+		break;
+	default:
+		pr_err("%s: Un-supported transport: %s\n", __func__,
+				xport_to_str(dxport));
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void frmnet_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct f_rmnet *dev = func_to_rmnet(f);
+
+	pr_debug("%s: portno:%d\n", __func__, dev->port_num);
+
+	if (gadget_is_dualspeed(c->cdev->gadget))
+		usb_free_descriptors(f->hs_descriptors);
+	usb_free_descriptors(f->descriptors);
+
+	frmnet_free_req(dev->notify, dev->notify_req);
+
+	kfree(f->name);
+}
+
+static void frmnet_suspend(struct usb_function *f)
+{
+	struct f_rmnet *dev = func_to_rmnet(f);
+	unsigned		port_num;
+	enum transport_type	dxport = rmnet_ports[dev->port_num].data_xport;
+
+	pr_debug("%s: data xport: %s dev: %p portno: %d\n",
+		__func__, xport_to_str(dxport),
+		dev, dev->port_num);
+
+	port_num = rmnet_ports[dev->port_num].data_xport_num;
+	switch (dxport) {
+	case USB_GADGET_XPORT_BAM:
+		break;
+	case USB_GADGET_XPORT_BAM2BAM:
+		gbam_suspend(&dev->port, port_num, dxport);
+		break;
+	case USB_GADGET_XPORT_HSIC:
+		break;
+	case USB_GADGET_XPORT_NONE:
+		break;
+	default:
+		pr_err("%s: Un-supported transport: %s\n", __func__,
+				xport_to_str(dxport));
+	}
+}
+
+static void frmnet_resume(struct usb_function *f)
+{
+	struct f_rmnet *dev = func_to_rmnet(f);
+	unsigned		port_num;
+	enum transport_type	dxport = rmnet_ports[dev->port_num].data_xport;
+
+	pr_debug("%s: data xport: %s dev: %p portno: %d\n",
+		__func__, xport_to_str(dxport),
+		dev, dev->port_num);
+
+	port_num = rmnet_ports[dev->port_num].data_xport_num;
+	switch (dxport) {
+	case USB_GADGET_XPORT_BAM:
+		break;
+	case USB_GADGET_XPORT_BAM2BAM:
+		gbam_resume(&dev->port, port_num, dxport);
+		break;
+	case USB_GADGET_XPORT_HSIC:
+		break;
+	case USB_GADGET_XPORT_NONE:
+		break;
+	default:
+		pr_err("%s: Un-supported transport: %s\n", __func__,
+				xport_to_str(dxport));
+	}
+}
+
+static void frmnet_disable(struct usb_function *f)
+{
+	struct f_rmnet *dev = func_to_rmnet(f);
+	unsigned long flags;
+	struct rmnet_ctrl_pkt *cpkt;
+
+	pr_debug("%s: port#%d\n", __func__, dev->port_num);
+
+	usb_ep_disable(dev->notify);
+	dev->notify->driver_data = NULL;
+
+	atomic_set(&dev->online, 0);
+
+	spin_lock_irqsave(&dev->lock, flags);
+	while (!list_empty(&dev->cpkt_resp_q)) {
+		cpkt = list_first_entry(&dev->cpkt_resp_q,
+				struct rmnet_ctrl_pkt, list);
+
+		list_del(&cpkt->list);
+		rmnet_free_ctrl_pkt(cpkt);
+	}
+	atomic_set(&dev->notify_count, 0);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	gport_rmnet_disconnect(dev);
+}
+
+static int
+frmnet_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+	struct f_rmnet			*dev = func_to_rmnet(f);
+	struct usb_composite_dev	*cdev = dev->cdev;
+	int				ret;
+	struct list_head *cpkt;
+
+	pr_debug("%s:dev:%p port#%d\n", __func__, dev, dev->port_num);
+
+	if (dev->notify->driver_data) {
+		pr_debug("%s: reset port:%d\n", __func__, dev->port_num);
+		usb_ep_disable(dev->notify);
+	}
+
+	ret = config_ep_by_speed(cdev->gadget, f, dev->notify);
+	if (ret) {
+		dev->notify->desc = NULL;
+		ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
+					dev->notify->name, ret);
+		return ret;
+	}
+	ret = usb_ep_enable(dev->notify);
+
+	if (ret) {
+		pr_err("%s: usb ep#%s enable failed, err#%d\n",
+				__func__, dev->notify->name, ret);
+		return ret;
+	}
+	dev->notify->driver_data = dev;
+
+	if (!dev->port.in->desc || !dev->port.out->desc) {
+		if (config_ep_by_speed(cdev->gadget, f, dev->port.in) ||
+			config_ep_by_speed(cdev->gadget, f, dev->port.out)) {
+				dev->port.in->desc = NULL;
+				dev->port.out->desc = NULL;
+				return -EINVAL;
+		}
+		ret = gport_rmnet_connect(dev);
+	}
+
+	atomic_set(&dev->online, 1);
+
+	/* In case notifications were aborted, but there are pending control
+	   packets in the response queue, re-add the notifications */
+	list_for_each(cpkt, &dev->cpkt_resp_q)
+		frmnet_ctrl_response_available(dev);
+
+	return ret;
+}
+
+static void frmnet_ctrl_response_available(struct f_rmnet *dev)
+{
+	struct usb_request		*req = dev->notify_req;
+	struct usb_cdc_notification	*event;
+	unsigned long			flags;
+	int				ret;
+
+	pr_debug("%s:dev:%p portno#%d\n", __func__, dev, dev->port_num);
+
+	spin_lock_irqsave(&dev->lock, flags);
+	if (!atomic_read(&dev->online) || !req || !req->buf) {
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return;
+	}
+
+	if (atomic_inc_return(&dev->notify_count) != 1) {
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return;
+	}
+
+	event = req->buf;
+	event->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
+			| USB_RECIP_INTERFACE;
+	event->bNotificationType = USB_CDC_NOTIFY_RESPONSE_AVAILABLE;
+	event->wValue = cpu_to_le16(0);
+	event->wIndex = cpu_to_le16(dev->ifc_id);
+	event->wLength = cpu_to_le16(0);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	ret = usb_ep_queue(dev->notify, dev->notify_req, GFP_ATOMIC);
+	if (ret) {
+		atomic_dec(&dev->notify_count);
+		pr_debug("ep enqueue error %d\n", ret);
+	}
+}
+
+static void frmnet_connect(struct grmnet *gr)
+{
+	struct f_rmnet			*dev;
+
+	if (!gr) {
+		pr_err("%s: Invalid grmnet:%p\n", __func__, gr);
+		return;
+	}
+
+	dev = port_to_rmnet(gr);
+
+	atomic_set(&dev->ctrl_online, 1);
+}
+
+static void frmnet_disconnect(struct grmnet *gr)
+{
+	struct f_rmnet			*dev;
+	unsigned long			flags;
+	struct usb_cdc_notification	*event;
+	int				status;
+	struct rmnet_ctrl_pkt		*cpkt;
+
+	if (!gr) {
+		pr_err("%s: Invalid grmnet:%p\n", __func__, gr);
+		return;
+	}
+
+	dev = port_to_rmnet(gr);
+
+	atomic_set(&dev->ctrl_online, 0);
+
+	if (!atomic_read(&dev->online)) {
+		pr_debug("%s: nothing to do\n", __func__);
+		return;
+	}
+
+	usb_ep_fifo_flush(dev->notify);
+
+	event = dev->notify_req->buf;
+	event->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
+			| USB_RECIP_INTERFACE;
+	event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
+	event->wValue = cpu_to_le16(0);
+	event->wIndex = cpu_to_le16(dev->ifc_id);
+	event->wLength = cpu_to_le16(0);
+
+	status = usb_ep_queue(dev->notify, dev->notify_req, GFP_ATOMIC);
+	if (status < 0) {
+		if (!atomic_read(&dev->online))
+			return;
+		pr_err("%s: rmnet notify ep enqueue error %d\n",
+				__func__, status);
+	}
+
+	spin_lock_irqsave(&dev->lock, flags);
+	while (!list_empty(&dev->cpkt_resp_q)) {
+		cpkt = list_first_entry(&dev->cpkt_resp_q,
+				struct rmnet_ctrl_pkt, list);
+
+		list_del(&cpkt->list);
+		rmnet_free_ctrl_pkt(cpkt);
+	}
+	atomic_set(&dev->notify_count, 0);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+}
+
+static int
+frmnet_send_cpkt_response(void *gr, void *buf, size_t len)
+{
+	struct f_rmnet		*dev;
+	struct rmnet_ctrl_pkt	*cpkt;
+	unsigned long		flags;
+
+	if (!gr || !buf) {
+		pr_err("%s: Invalid grmnet/buf, grmnet:%p buf:%p\n",
+				__func__, gr, buf);
+		return -ENODEV;
+	}
+	cpkt = rmnet_alloc_ctrl_pkt(len, GFP_ATOMIC);
+	if (IS_ERR(cpkt)) {
+		pr_err("%s: Unable to allocate ctrl pkt\n", __func__);
+		return -ENOMEM;
+	}
+	memcpy(cpkt->buf, buf, len);
+	cpkt->len = len;
+
+	dev = port_to_rmnet(gr);
+
+	pr_debug("%s: dev:%p port#%d\n", __func__, dev, dev->port_num);
+
+	if (!atomic_read(&dev->online) || !atomic_read(&dev->ctrl_online)) {
+		rmnet_free_ctrl_pkt(cpkt);
+		return 0;
+	}
+
+	spin_lock_irqsave(&dev->lock, flags);
+	list_add_tail(&cpkt->list, &dev->cpkt_resp_q);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	frmnet_ctrl_response_available(dev);
+
+	return 0;
+}
+
+static void
+frmnet_cmd_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_rmnet			*dev = req->context;
+	struct usb_composite_dev	*cdev;
+	unsigned			port_num;
+
+	if (!dev) {
+		pr_err("%s: rmnet dev is null\n", __func__);
+		return;
+	}
+
+	pr_debug("%s: dev:%p port#%d\n", __func__, dev, dev->port_num);
+
+	cdev = dev->cdev;
+
+	if (dev->port.send_encap_cmd) {
+		port_num = rmnet_ports[dev->port_num].ctrl_xport_num;
+		dev->port.send_encap_cmd(port_num, req->buf, req->actual);
+	}
+}
+
+static void frmnet_notify_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_rmnet *dev = req->context;
+	int status = req->status;
+
+	pr_debug("%s: dev:%p port#%d\n", __func__, dev, dev->port_num);
+
+	switch (status) {
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		atomic_set(&dev->notify_count, 0);
+		break;
+	default:
+		pr_err("rmnet notify ep error %d\n", status);
+		/* FALLTHROUGH */
+	case 0:
+		if (!atomic_read(&dev->ctrl_online))
+			break;
+
+		if (atomic_dec_and_test(&dev->notify_count))
+			break;
+
+		status = usb_ep_queue(dev->notify, req, GFP_ATOMIC);
+		if (status) {
+			atomic_dec(&dev->notify_count);
+			pr_debug("ep enqueue error %d\n", status);
+		}
+		break;
+	}
+}
+
+static int
+frmnet_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+	struct f_rmnet			*dev = func_to_rmnet(f);
+	struct usb_composite_dev	*cdev = dev->cdev;
+	struct usb_request		*req = cdev->req;
+	unsigned			port_num;
+	u16				w_index = le16_to_cpu(ctrl->wIndex);
+	u16				w_value = le16_to_cpu(ctrl->wValue);
+	u16				w_length = le16_to_cpu(ctrl->wLength);
+	int				ret = -EOPNOTSUPP;
+
+	pr_debug("%s:dev:%p port#%d\n", __func__, dev, dev->port_num);
+
+	if (!atomic_read(&dev->online)) {
+		pr_debug("%s: usb cable is not connected\n", __func__);
+		return -ENOTCONN;
+	}
+
+	switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_SEND_ENCAPSULATED_COMMAND:
+		ret = w_length;
+		req->complete = frmnet_cmd_complete;
+		req->context = dev;
+		break;
+
+
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_GET_ENCAPSULATED_RESPONSE:
+		if (w_value)
+			goto invalid;
+		else {
+			unsigned len;
+			struct rmnet_ctrl_pkt *cpkt;
+
+			spin_lock(&dev->lock);
+			if (list_empty(&dev->cpkt_resp_q)) {
+				pr_err("ctrl resp queue empty "
+					" req%02x.%02x v%04x i%04x l%d\n",
+					ctrl->bRequestType, ctrl->bRequest,
+					w_value, w_index, w_length);
+				spin_unlock(&dev->lock);
+				goto invalid;
+			}
+
+			cpkt = list_first_entry(&dev->cpkt_resp_q,
+					struct rmnet_ctrl_pkt, list);
+			list_del(&cpkt->list);
+			spin_unlock(&dev->lock);
+
+			len = min_t(unsigned, w_length, cpkt->len);
+			memcpy(req->buf, cpkt->buf, len);
+			ret = len;
+
+			rmnet_free_ctrl_pkt(cpkt);
+		}
+		break;
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_REQ_SET_CONTROL_LINE_STATE:
+		if (dev->port.notify_modem) {
+			port_num = rmnet_ports[dev->port_num].ctrl_xport_num;
+			dev->port.notify_modem(&dev->port, port_num, w_value);
+		}
+		ret = 0;
+
+		break;
+	default:
+
+invalid:
+		DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+	}
+
+	/* respond with data transfer or status phase? */
+	if (ret >= 0) {
+		VDBG(cdev, "rmnet req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+		req->zero = (ret < w_length);
+		req->length = ret;
+		ret = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+		if (ret < 0)
+			ERROR(cdev, "rmnet ep0 enqueue err %d\n", ret);
+	}
+
+	return ret;
+}
+
+static int frmnet_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct f_rmnet			*dev = func_to_rmnet(f);
+	struct usb_ep			*ep;
+	struct usb_composite_dev	*cdev = c->cdev;
+	int				ret = -ENODEV;
+
+	dev->ifc_id = usb_interface_id(c, f);
+	if (dev->ifc_id < 0) {
+		pr_err("%s: unable to allocate ifc id, err:%d",
+				__func__, dev->ifc_id);
+		return dev->ifc_id;
+	}
+	rmnet_interface_desc.bInterfaceNumber = dev->ifc_id;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &rmnet_fs_in_desc);
+	if (!ep) {
+		pr_err("%s: usb epin autoconfig failed\n", __func__);
+		return -ENODEV;
+	}
+	dev->port.in = ep;
+	ep->driver_data = cdev;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &rmnet_fs_out_desc);
+	if (!ep) {
+		pr_err("%s: usb epout autoconfig failed\n", __func__);
+		ret = -ENODEV;
+		goto ep_auto_out_fail;
+	}
+	dev->port.out = ep;
+	ep->driver_data = cdev;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &rmnet_fs_notify_desc);
+	if (!ep) {
+		pr_err("%s: usb epnotify autoconfig failed\n", __func__);
+		ret = -ENODEV;
+		goto ep_auto_notify_fail;
+	}
+	dev->notify = ep;
+	ep->driver_data = cdev;
+
+	dev->notify_req = frmnet_alloc_req(ep,
+				sizeof(struct usb_cdc_notification),
+				GFP_KERNEL);
+	if (IS_ERR(dev->notify_req)) {
+		pr_err("%s: unable to allocate memory for notify req\n",
+				__func__);
+		ret = -ENOMEM;
+		goto ep_notify_alloc_fail;
+	}
+
+	dev->notify_req->complete = frmnet_notify_complete;
+	dev->notify_req->context = dev;
+
+	f->descriptors = usb_copy_descriptors(rmnet_fs_function);
+
+	if (!f->descriptors)
+		goto fail;
+
+	if (gadget_is_dualspeed(cdev->gadget)) {
+		rmnet_hs_in_desc.bEndpointAddress =
+				rmnet_fs_in_desc.bEndpointAddress;
+		rmnet_hs_out_desc.bEndpointAddress =
+				rmnet_fs_out_desc.bEndpointAddress;
+		rmnet_hs_notify_desc.bEndpointAddress =
+				rmnet_fs_notify_desc.bEndpointAddress;
+
+		/* copy descriptors, and track endpoint copies */
+		f->hs_descriptors = usb_copy_descriptors(rmnet_hs_function);
+
+		if (!f->hs_descriptors)
+			goto fail;
+	}
+
+	pr_info("%s: RmNet(%d) %s Speed, IN:%s OUT:%s\n",
+			__func__, dev->port_num,
+			gadget_is_dualspeed(cdev->gadget) ? "dual" : "full",
+			dev->port.in->name, dev->port.out->name);
+
+	return 0;
+
+fail:
+	if (f->descriptors)
+		usb_free_descriptors(f->descriptors);
+ep_notify_alloc_fail:
+	dev->notify->driver_data = NULL;
+	dev->notify = NULL;
+ep_auto_notify_fail:
+	dev->port.out->driver_data = NULL;
+	dev->port.out = NULL;
+ep_auto_out_fail:
+	dev->port.in->driver_data = NULL;
+	dev->port.in = NULL;
+
+	return ret;
+}
+
+static int frmnet_bind_config(struct usb_configuration *c, unsigned portno)
+{
+	int			status;
+	struct f_rmnet		*dev;
+	struct usb_function	*f;
+	unsigned long		flags;
+
+	pr_debug("%s: usb config:%p\n", __func__, c);
+
+	if (portno >= nr_rmnet_ports) {
+		pr_err("%s: supporting ports#%u port_id:%u", __func__,
+				nr_rmnet_ports, portno);
+		return -ENODEV;
+	}
+
+	if (rmnet_string_defs[0].id == 0) {
+		status = usb_string_id(c->cdev);
+		if (status < 0) {
+			pr_err("%s: failed to get string id, err:%d\n",
+					__func__, status);
+			return status;
+		}
+		rmnet_string_defs[0].id = status;
+	}
+
+	dev = rmnet_ports[portno].port;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	dev->cdev = c->cdev;
+	f = &dev->port.func;
+	f->name = kasprintf(GFP_ATOMIC, "rmnet%d", portno);
+	spin_unlock_irqrestore(&dev->lock, flags);
+	if (!f->name) {
+		pr_err("%s: cannot allocate memory for name\n", __func__);
+		return -ENOMEM;
+	}
+
+	f->strings = rmnet_strings;
+	f->bind = frmnet_bind;
+	f->unbind = frmnet_unbind;
+	f->disable = frmnet_disable;
+	f->set_alt = frmnet_set_alt;
+	f->setup = frmnet_setup;
+	f->suspend = frmnet_suspend;
+	f->resume = frmnet_resume;
+	dev->port.send_cpkt_response = frmnet_send_cpkt_response;
+	dev->port.disconnect = frmnet_disconnect;
+	dev->port.connect = frmnet_connect;
+
+	status = usb_add_function(c, f);
+	if (status) {
+		pr_err("%s: usb add function failed: %d\n",
+				__func__, status);
+		kfree(f->name);
+		return status;
+	}
+
+	pr_debug("%s: complete\n", __func__);
+
+	return status;
+}
+
+static void frmnet_cleanup(void)
+{
+	int i;
+
+	for (i = 0; i < nr_rmnet_ports; i++)
+		kfree(rmnet_ports[i].port);
+
+	nr_rmnet_ports = 0;
+	no_ctrl_smd_ports = 0;
+	no_data_bam_ports = 0;
+	no_data_bam2bam_ports = 0;
+	no_ctrl_hsic_ports = 0;
+	no_data_hsic_ports = 0;
+	no_ctrl_hsuart_ports = 0;
+	no_data_hsuart_ports = 0;
+}
+
+static int frmnet_init_port(const char *ctrl_name, const char *data_name)
+{
+	struct f_rmnet			*dev;
+	struct rmnet_ports		*rmnet_port;
+	int				ret;
+	int				i;
+
+	if (nr_rmnet_ports >= NR_RMNET_PORTS) {
+		pr_err("%s: Max-%d instances supported\n",
+				__func__, NR_RMNET_PORTS);
+		return -EINVAL;
+	}
+
+	pr_debug("%s: port#:%d, ctrl port: %s data port: %s\n",
+		__func__, nr_rmnet_ports, ctrl_name, data_name);
+
+	dev = kzalloc(sizeof(struct f_rmnet), GFP_KERNEL);
+	if (!dev) {
+		pr_err("%s: Unable to allocate rmnet device\n", __func__);
+		return -ENOMEM;
+	}
+
+	dev->port_num = nr_rmnet_ports;
+	spin_lock_init(&dev->lock);
+	INIT_LIST_HEAD(&dev->cpkt_resp_q);
+
+	rmnet_port = &rmnet_ports[nr_rmnet_ports];
+	rmnet_port->port = dev;
+	rmnet_port->port_num = nr_rmnet_ports;
+	rmnet_port->ctrl_xport = str_to_xport(ctrl_name);
+	rmnet_port->data_xport = str_to_xport(data_name);
+
+	switch (rmnet_port->ctrl_xport) {
+	case USB_GADGET_XPORT_SMD:
+		rmnet_port->ctrl_xport_num = no_ctrl_smd_ports;
+		no_ctrl_smd_ports++;
+		break;
+	case USB_GADGET_XPORT_HSIC:
+		rmnet_port->ctrl_xport_num = no_ctrl_hsic_ports;
+		no_ctrl_hsic_ports++;
+		break;
+	case USB_GADGET_XPORT_HSUART:
+		rmnet_port->ctrl_xport_num = no_ctrl_hsuart_ports;
+		no_ctrl_hsuart_ports++;
+		break;
+	case USB_GADGET_XPORT_NONE:
+		break;
+	default:
+		pr_err("%s: Un-supported transport: %u\n", __func__,
+				rmnet_port->ctrl_xport);
+		ret = -ENODEV;
+		goto fail_probe;
+	}
+
+	switch (rmnet_port->data_xport) {
+	case USB_GADGET_XPORT_BAM:
+		rmnet_port->data_xport_num = no_data_bam_ports;
+		no_data_bam_ports++;
+		break;
+	case USB_GADGET_XPORT_BAM2BAM:
+		rmnet_port->data_xport_num = no_data_bam2bam_ports;
+		no_data_bam2bam_ports++;
+		break;
+	case USB_GADGET_XPORT_HSIC:
+		rmnet_port->data_xport_num = no_data_hsic_ports;
+		no_data_hsic_ports++;
+		break;
+	case USB_GADGET_XPORT_HSUART:
+		rmnet_port->data_xport_num = no_data_hsuart_ports;
+		no_data_hsuart_ports++;
+		break;
+	case USB_GADGET_XPORT_NONE:
+		break;
+	default:
+		pr_err("%s: Un-supported transport: %u\n", __func__,
+				rmnet_port->data_xport);
+		ret = -ENODEV;
+		goto fail_probe;
+	}
+	nr_rmnet_ports++;
+
+	return 0;
+
+fail_probe:
+	for (i = 0; i < nr_rmnet_ports; i++)
+		kfree(rmnet_ports[i].port);
+
+	nr_rmnet_ports = 0;
+	no_ctrl_smd_ports = 0;
+	no_data_bam_ports = 0;
+	no_ctrl_hsic_ports = 0;
+	no_data_hsic_ports = 0;
+	no_ctrl_hsuart_ports = 0;
+	no_data_hsuart_ports = 0;
+
+	return ret;
+}
diff --git a/drivers/usb/gadget/f_rmnet.h b/drivers/usb/gadget/f_rmnet.h
new file mode 100644
index 0000000..2d816c6
--- /dev/null
+++ b/drivers/usb/gadget/f_rmnet.h
@@ -0,0 +1,19 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __F_RMNET_H
+#define __F_RMNET_H
+
+int rmnet_function_add(struct usb_configuration *c);
+
+#endif /* __F_RMNET_H */
diff --git a/drivers/usb/gadget/f_rmnet_sdio.c b/drivers/usb/gadget/f_rmnet_sdio.c
new file mode 100644
index 0000000..8019356
--- /dev/null
+++ b/drivers/usb/gadget/f_rmnet_sdio.c
@@ -0,0 +1,1576 @@
+/*
+ * f_rmnet_sdio.c -- RmNet SDIO function driver
+ *
+ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
+ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
+ * Copyright (C) 2008 Nokia Corporation
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/workqueue.h>
+#include <linux/netdevice.h>
+
+#include <linux/usb/cdc.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/ch9.h>
+#include <linux/termios.h>
+#include <linux/debugfs.h>
+
+#include <mach/sdio_cmux.h>
+#include <mach/sdio_dmux.h>
+
+#ifdef CONFIG_RMNET_SDIO_CTL_CHANNEL
+static uint32_t rmnet_sdio_ctl_ch = CONFIG_RMNET_SDIO_CTL_CHANNEL;
+#else
+static uint32_t rmnet_sdio_ctl_ch;
+#endif
+module_param(rmnet_sdio_ctl_ch, uint, S_IRUGO);
+MODULE_PARM_DESC(rmnet_sdio_ctl_ch, "RmNet control SDIO channel ID");
+
+#ifdef CONFIG_RMNET_SDIO_DATA_CHANNEL
+static uint32_t rmnet_sdio_data_ch = CONFIG_RMNET_SDIO_DATA_CHANNEL;
+#else
+static uint32_t rmnet_sdio_data_ch;
+#endif
+module_param(rmnet_sdio_data_ch, uint, S_IRUGO);
+MODULE_PARM_DESC(rmnet_sdio_data_ch, "RmNet data SDIO channel ID");
+
+#define ACM_CTRL_DTR				(1 << 0)
+
+#define SDIO_MUX_HDR				8
+#define RMNET_SDIO_NOTIFY_INTERVAL		5
+#define RMNET_SDIO_MAX_NFY_SZE  sizeof(struct usb_cdc_notification)
+
+#define RMNET_SDIO_RX_REQ_MAX			16
+#define RMNET_SDIO_RX_REQ_SIZE			2048
+#define RMNET_SDIO_TX_REQ_MAX			200
+
+#define TX_PKT_DROP_THRESHOLD			1000
+#define RX_PKT_FLOW_CTRL_EN_THRESHOLD		1000
+#define RX_PKT_FLOW_CTRL_DISABLE		500
+
+unsigned int sdio_tx_pkt_drop_thld = TX_PKT_DROP_THRESHOLD;
+module_param(sdio_tx_pkt_drop_thld, uint, S_IRUGO | S_IWUSR);
+
+unsigned int sdio_rx_fctrl_en_thld = RX_PKT_FLOW_CTRL_EN_THRESHOLD;
+module_param(sdio_rx_fctrl_en_thld, uint, S_IRUGO | S_IWUSR);
+
+unsigned int sdio_rx_fctrl_dis_thld = RX_PKT_FLOW_CTRL_DISABLE;
+module_param(sdio_rx_fctrl_dis_thld, uint, S_IRUGO | S_IWUSR);
+
+/* QMI requests & responses buffer*/
+struct rmnet_sdio_qmi_buf {
+	void *buf;
+	int len;
+	struct list_head list;
+};
+
+struct rmnet_sdio_dev {
+	struct usb_function function;
+	struct usb_composite_dev *cdev;
+
+	struct usb_ep           *epout;
+	struct usb_ep           *epin;
+	struct usb_ep           *epnotify;
+	struct usb_request      *notify_req;
+
+	u8                      ifc_id;
+	/* QMI lists */
+	struct list_head        qmi_req_q;
+	unsigned int		qreq_q_len;
+	struct list_head        qmi_resp_q;
+	unsigned int		qresp_q_len;
+	/* Tx/Rx lists */
+	struct list_head        tx_idle;
+	unsigned int		tx_idle_len;
+	struct sk_buff_head	tx_skb_queue;
+	struct list_head        rx_idle;
+	unsigned int		rx_idle_len;
+	struct sk_buff_head	rx_skb_queue;
+
+	spinlock_t              lock;
+	atomic_t                online;
+	atomic_t                notify_count;
+
+	struct workqueue_struct *wq;
+	struct work_struct disconnect_work;
+
+	struct work_struct ctl_rx_work;
+	struct work_struct data_rx_work;
+
+	struct delayed_work sdio_open_work;
+	struct work_struct sdio_close_work;
+#define RMNET_SDIO_CH_OPEN	1
+	unsigned long	data_ch_status;
+	unsigned long	ctrl_ch_status;
+
+	unsigned int dpkts_pending_atdmux;
+	int cbits_to_modem;
+	struct work_struct set_modem_ctl_bits_work;
+
+	/* pkt logging dpkt - data pkt; cpkt - control pkt*/
+	struct dentry *dent;
+	unsigned long dpkt_tolaptop;
+	unsigned long dpkt_tomodem;
+	unsigned long tx_drp_cnt;
+	unsigned long cpkt_tolaptop;
+	unsigned long cpkt_tomodem;
+};
+
+static struct usb_interface_descriptor rmnet_sdio_interface_desc = {
+	.bLength =              USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =      USB_DT_INTERFACE,
+	/* .bInterfaceNumber = DYNAMIC */
+	.bNumEndpoints =        3,
+	.bInterfaceClass =      USB_CLASS_VENDOR_SPEC,
+	.bInterfaceSubClass =   USB_CLASS_VENDOR_SPEC,
+	.bInterfaceProtocol =   USB_CLASS_VENDOR_SPEC,
+	/* .iInterface = DYNAMIC */
+};
+
+/* Full speed support */
+static struct usb_endpoint_descriptor rmnet_sdio_fs_notify_desc = {
+	.bLength =              USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =      USB_DT_ENDPOINT,
+	.bEndpointAddress =     USB_DIR_IN,
+	.bmAttributes =         USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	__constant_cpu_to_le16(RMNET_SDIO_MAX_NFY_SZE),
+	.bInterval =            1 << RMNET_SDIO_NOTIFY_INTERVAL,
+};
+
+static struct usb_endpoint_descriptor rmnet_sdio_fs_in_desc  = {
+	.bLength =              USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =      USB_DT_ENDPOINT,
+	.bEndpointAddress =     USB_DIR_IN,
+	.bmAttributes =         USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize   = __constant_cpu_to_le16(64),
+};
+
+static struct usb_endpoint_descriptor rmnet_sdio_fs_out_desc = {
+	.bLength =              USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =      USB_DT_ENDPOINT,
+	.bEndpointAddress =     USB_DIR_OUT,
+	.bmAttributes =         USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize = __constant_cpu_to_le16(64),
+};
+
+static struct usb_descriptor_header *rmnet_sdio_fs_function[] = {
+	(struct usb_descriptor_header *) &rmnet_sdio_interface_desc,
+	(struct usb_descriptor_header *) &rmnet_sdio_fs_notify_desc,
+	(struct usb_descriptor_header *) &rmnet_sdio_fs_in_desc,
+	(struct usb_descriptor_header *) &rmnet_sdio_fs_out_desc,
+	NULL,
+};
+
+/* High speed support */
+static struct usb_endpoint_descriptor rmnet_sdio_hs_notify_desc  = {
+	.bLength =              USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =      USB_DT_ENDPOINT,
+	.bEndpointAddress =     USB_DIR_IN,
+	.bmAttributes =         USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	__constant_cpu_to_le16(RMNET_SDIO_MAX_NFY_SZE),
+	.bInterval =            RMNET_SDIO_NOTIFY_INTERVAL + 4,
+};
+
+static struct usb_endpoint_descriptor rmnet_sdio_hs_in_desc = {
+	.bLength =              USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =      USB_DT_ENDPOINT,
+	.bEndpointAddress =     USB_DIR_IN,
+	.bmAttributes =         USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	__constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor rmnet_sdio_hs_out_desc = {
+	.bLength =              USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =      USB_DT_ENDPOINT,
+	.bEndpointAddress =     USB_DIR_OUT,
+	.bmAttributes =         USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	__constant_cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *rmnet_sdio_hs_function[] = {
+	(struct usb_descriptor_header *) &rmnet_sdio_interface_desc,
+	(struct usb_descriptor_header *) &rmnet_sdio_hs_notify_desc,
+	(struct usb_descriptor_header *) &rmnet_sdio_hs_in_desc,
+	(struct usb_descriptor_header *) &rmnet_sdio_hs_out_desc,
+	NULL,
+};
+
+/* String descriptors */
+
+static struct usb_string rmnet_sdio_string_defs[] = {
+	[0].s = "QMI RmNet",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings rmnet_sdio_string_table = {
+	.language =             0x0409, /* en-us */
+	.strings =              rmnet_sdio_string_defs,
+};
+
+static struct usb_gadget_strings *rmnet_sdio_strings[] = {
+	&rmnet_sdio_string_table,
+	NULL,
+};
+
+static struct rmnet_sdio_qmi_buf *
+rmnet_sdio_alloc_qmi(unsigned len, gfp_t kmalloc_flags)
+
+{
+	struct rmnet_sdio_qmi_buf *qmi;
+
+	qmi = kmalloc(sizeof(struct rmnet_sdio_qmi_buf), kmalloc_flags);
+	if (qmi != NULL) {
+		qmi->buf = kmalloc(len, kmalloc_flags);
+		if (qmi->buf == NULL) {
+			kfree(qmi);
+			qmi = NULL;
+		}
+	}
+
+	return qmi ? qmi : ERR_PTR(-ENOMEM);
+}
+
+static void rmnet_sdio_free_qmi(struct rmnet_sdio_qmi_buf *qmi)
+{
+	kfree(qmi->buf);
+	kfree(qmi);
+}
+/*
+ * Allocate a usb_request and its buffer.  Returns a pointer to the
+ * usb_request or a pointer with an error code if there is an error.
+ */
+static struct usb_request *
+rmnet_sdio_alloc_req(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags)
+{
+	struct usb_request *req;
+
+	req = usb_ep_alloc_request(ep, kmalloc_flags);
+
+	if (len && req != NULL) {
+		req->length = len;
+		req->buf = kmalloc(len, kmalloc_flags);
+		if (req->buf == NULL) {
+			usb_ep_free_request(ep, req);
+			req = NULL;
+		}
+	}
+
+	return req ? req : ERR_PTR(-ENOMEM);
+}
+
+/*
+ * Free a usb_request and its buffer.
+ */
+static void rmnet_sdio_free_req(struct usb_ep *ep, struct usb_request *req)
+{
+	kfree(req->buf);
+	usb_ep_free_request(ep, req);
+}
+
+static void rmnet_sdio_notify_complete(struct usb_ep *ep,
+					struct usb_request *req)
+{
+	struct rmnet_sdio_dev *dev = req->context;
+	struct usb_composite_dev *cdev = dev->cdev;
+	int status = req->status;
+
+	switch (status) {
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		atomic_set(&dev->notify_count, 0);
+		break;
+	default:
+		ERROR(cdev, "rmnet notifyep error %d\n", status);
+		/* FALLTHROUGH */
+	case 0:
+
+		if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status))
+			return;
+
+		/* handle multiple pending QMI_RESPONSE_AVAILABLE
+		 * notifications by resending until we're done
+		 */
+		if (atomic_dec_and_test(&dev->notify_count))
+			break;
+
+		status = usb_ep_queue(dev->epnotify, req, GFP_ATOMIC);
+		if (status) {
+			atomic_dec(&dev->notify_count);
+			ERROR(cdev, "rmnet notify ep enq error %d\n", status);
+		}
+		break;
+	}
+}
+
+static void rmnet_sdio_qmi_resp_available(struct rmnet_sdio_dev *dev)
+{
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct usb_cdc_notification     *event;
+	int status;
+	unsigned long flags;
+
+	/* Response will be sent later */
+	if (atomic_inc_return(&dev->notify_count) != 1)
+		return;
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	if (!atomic_read(&dev->online)) {
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return;
+	}
+
+	event = dev->notify_req->buf;
+
+	event->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
+			| USB_RECIP_INTERFACE;
+	event->bNotificationType = USB_CDC_NOTIFY_RESPONSE_AVAILABLE;
+	event->wValue = cpu_to_le16(0);
+	event->wIndex = cpu_to_le16(dev->ifc_id);
+	event->wLength = cpu_to_le16(0);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	status = usb_ep_queue(dev->epnotify, dev->notify_req, GFP_ATOMIC);
+	if (status < 0) {
+		if (atomic_read(&dev->online))
+			atomic_dec(&dev->notify_count);
+		ERROR(cdev, "rmnet notify ep enqueue error %d\n", status);
+	}
+}
+
+#define SDIO_MAX_CTRL_PKT_SIZE	4096
+static void rmnet_sdio_ctl_receive_cb(void *data, int size, void *priv)
+{
+	struct rmnet_sdio_dev *dev = priv;
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct rmnet_sdio_qmi_buf *qmi_resp;
+	unsigned long flags;
+
+	if (!data) {
+		pr_info("%s: cmux_ch close event\n", __func__);
+		if (test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status) &&
+		    test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status)) {
+			clear_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status);
+			clear_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status);
+			queue_work(dev->wq, &dev->sdio_close_work);
+		}
+		return;
+	}
+
+	if (!size || !test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status))
+		return;
+
+
+	if (size > SDIO_MAX_CTRL_PKT_SIZE) {
+		ERROR(cdev, "ctrl pkt size:%d exceeds max pkt size:%d\n",
+				size, SDIO_MAX_CTRL_PKT_SIZE);
+		return;
+	}
+
+	if (!atomic_read(&dev->online)) {
+		DBG(cdev, "USB disconnected\n");
+		return;
+	}
+
+	qmi_resp = rmnet_sdio_alloc_qmi(size, GFP_KERNEL);
+	if (IS_ERR(qmi_resp)) {
+		DBG(cdev, "unable to allocate memory for QMI resp\n");
+		return;
+	}
+	memcpy(qmi_resp->buf, data, size);
+	qmi_resp->len = size;
+	spin_lock_irqsave(&dev->lock, flags);
+	list_add_tail(&qmi_resp->list, &dev->qmi_resp_q);
+	dev->qresp_q_len++;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	rmnet_sdio_qmi_resp_available(dev);
+}
+
+static void rmnet_sdio_ctl_write_done(void *data, int size, void *priv)
+{
+	struct rmnet_sdio_dev *dev = priv;
+	struct usb_composite_dev *cdev = dev->cdev;
+
+	VDBG(cdev, "rmnet control write done = %d bytes\n", size);
+}
+
+static void rmnet_sdio_sts_callback(int id, void *priv)
+{
+	struct rmnet_sdio_dev *dev = priv;
+	struct usb_composite_dev *cdev = dev->cdev;
+
+	DBG(cdev, "rmnet_sdio_sts_callback: id: %d\n", id);
+}
+
+static void rmnet_sdio_control_rx_work(struct work_struct *w)
+{
+	struct rmnet_sdio_dev *dev = container_of(w, struct rmnet_sdio_dev,
+						 ctl_rx_work);
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct rmnet_sdio_qmi_buf *qmi_req;
+	unsigned long flags;
+	int ret;
+
+	while (1) {
+		spin_lock_irqsave(&dev->lock, flags);
+		if (list_empty(&dev->qmi_req_q))
+			goto unlock;
+
+		qmi_req = list_first_entry(&dev->qmi_req_q,
+					struct rmnet_sdio_qmi_buf, list);
+		list_del(&qmi_req->list);
+		dev->qreq_q_len--;
+		spin_unlock_irqrestore(&dev->lock, flags);
+
+		ret = sdio_cmux_write(rmnet_sdio_ctl_ch, qmi_req->buf,
+					qmi_req->len);
+		if (ret != qmi_req->len) {
+			ERROR(cdev, "rmnet control SDIO write failed\n");
+			return;
+		}
+
+		dev->cpkt_tomodem++;
+
+		/*
+		 * cmux_write API copies the buffer and gives it to sdio_al.
+		 * Hence freeing the memory before write is completed.
+		 */
+		rmnet_sdio_free_qmi(qmi_req);
+	}
+unlock:
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static void rmnet_sdio_response_complete(struct usb_ep *ep,
+					 struct usb_request *req)
+{
+	struct rmnet_sdio_dev *dev = req->context;
+	struct usb_composite_dev *cdev = dev->cdev;
+
+	switch (req->status) {
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+	case 0:
+		return;
+	default:
+		INFO(cdev, "rmnet %s response error %d, %d/%d\n",
+			ep->name, req->status,
+			req->actual, req->length);
+	}
+}
+
+static void rmnet_sdio_command_complete(struct usb_ep *ep,
+					struct usb_request *req)
+{
+	struct rmnet_sdio_dev *dev = req->context;
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct rmnet_sdio_qmi_buf *qmi_req;
+	int len = req->actual;
+
+	if (req->status < 0) {
+		ERROR(cdev, "rmnet command error %d\n", req->status);
+		return;
+	}
+
+	/* discard the packet if sdio is not available */
+	if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status))
+		return;
+
+	qmi_req = rmnet_sdio_alloc_qmi(len, GFP_ATOMIC);
+	if (IS_ERR(qmi_req)) {
+		ERROR(cdev, "unable to allocate memory for QMI req\n");
+		return;
+	}
+	memcpy(qmi_req->buf, req->buf, len);
+	qmi_req->len = len;
+	spin_lock(&dev->lock);
+	list_add_tail(&qmi_req->list, &dev->qmi_req_q);
+	dev->qreq_q_len++;
+	spin_unlock(&dev->lock);
+	queue_work(dev->wq, &dev->ctl_rx_work);
+}
+
+static int
+rmnet_sdio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+	struct rmnet_sdio_dev *dev = container_of(f, struct rmnet_sdio_dev,
+							 function);
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct usb_request      *req = cdev->req;
+	int                     ret = -EOPNOTSUPP;
+	u16                     w_index = le16_to_cpu(ctrl->wIndex);
+	u16                     w_value = le16_to_cpu(ctrl->wValue);
+	u16                     w_length = le16_to_cpu(ctrl->wLength);
+	struct rmnet_sdio_qmi_buf *resp;
+
+	if (!atomic_read(&dev->online))
+		return -ENOTCONN;
+
+	switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_SEND_ENCAPSULATED_COMMAND:
+		ret = w_length;
+		req->complete = rmnet_sdio_command_complete;
+		req->context = dev;
+		break;
+
+
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_GET_ENCAPSULATED_RESPONSE:
+		if (w_value)
+			goto invalid;
+		else {
+			unsigned len;
+
+			spin_lock(&dev->lock);
+
+			if (list_empty(&dev->qmi_resp_q)) {
+				INFO(cdev, "qmi resp empty "
+					" req%02x.%02x v%04x i%04x l%d\n",
+					ctrl->bRequestType, ctrl->bRequest,
+					w_value, w_index, w_length);
+				spin_unlock(&dev->lock);
+				goto invalid;
+			}
+
+			resp = list_first_entry(&dev->qmi_resp_q,
+				struct rmnet_sdio_qmi_buf, list);
+			list_del(&resp->list);
+			dev->qresp_q_len--;
+			spin_unlock(&dev->lock);
+
+			len = min_t(unsigned, w_length, resp->len);
+			memcpy(req->buf, resp->buf, len);
+			ret = len;
+			req->context = dev;
+			req->complete = rmnet_sdio_response_complete;
+			rmnet_sdio_free_qmi(resp);
+
+			/* check if its the right place to add */
+			dev->cpkt_tolaptop++;
+		}
+		break;
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_REQ_SET_CONTROL_LINE_STATE:
+		/* This is a workaround for RmNet and is borrowed from the
+		 * CDC/ACM standard. The host driver will issue the above ACM
+		 * standard request to the RmNet interface in the following
+		 * scenario: Once the network adapter is disabled from device
+		 * manager, the above request will be sent from the qcusbnet
+		 * host driver, with DTR being '0'. Once network adapter is
+		 * enabled from device manager (or during enumeration), the
+		 * request will be sent with DTR being '1'.
+		 */
+		if (w_value & ACM_CTRL_DTR)
+			dev->cbits_to_modem |= TIOCM_DTR;
+		else
+			dev->cbits_to_modem &= ~TIOCM_DTR;
+		queue_work(dev->wq, &dev->set_modem_ctl_bits_work);
+
+		ret = 0;
+
+		break;
+	default:
+
+invalid:
+	DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
+		ctrl->bRequestType, ctrl->bRequest,
+		w_value, w_index, w_length);
+	}
+
+	/* respond with data transfer or status phase? */
+	if (ret >= 0) {
+		VDBG(cdev, "rmnet req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+		req->zero = (ret < w_length);
+		req->length = ret;
+		ret = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+		if (ret < 0)
+			ERROR(cdev, "rmnet ep0 enqueue err %d\n", ret);
+	}
+
+	return ret;
+}
+
+static int
+rmnet_sdio_rx_submit(struct rmnet_sdio_dev *dev, struct usb_request *req,
+						 gfp_t gfp_flags)
+{
+	struct sk_buff *skb;
+	int retval;
+
+	skb = alloc_skb(RMNET_SDIO_RX_REQ_SIZE + SDIO_MUX_HDR, gfp_flags);
+	if (skb == NULL)
+		return -ENOMEM;
+	skb_reserve(skb, SDIO_MUX_HDR);
+
+	req->buf = skb->data;
+	req->length = RMNET_SDIO_RX_REQ_SIZE;
+	req->context = skb;
+
+	retval = usb_ep_queue(dev->epout, req, gfp_flags);
+	if (retval)
+		dev_kfree_skb_any(skb);
+
+	return retval;
+}
+
+static void rmnet_sdio_start_rx(struct rmnet_sdio_dev *dev)
+{
+	struct usb_composite_dev *cdev = dev->cdev;
+	int status;
+	struct usb_request *req;
+	unsigned long flags;
+
+	if (!atomic_read(&dev->online)) {
+		pr_err("%s: USB not connected\n", __func__);
+		return;
+	}
+
+	spin_lock_irqsave(&dev->lock, flags);
+	while (!list_empty(&dev->rx_idle)) {
+		req = list_first_entry(&dev->rx_idle, struct usb_request, list);
+		list_del(&req->list);
+		dev->rx_idle_len--;
+
+		spin_unlock_irqrestore(&dev->lock, flags);
+		status = rmnet_sdio_rx_submit(dev, req, GFP_ATOMIC);
+		spin_lock_irqsave(&dev->lock, flags);
+
+		if (status) {
+			ERROR(cdev, "rmnet data rx enqueue err %d\n", status);
+			list_add_tail(&req->list, &dev->rx_idle);
+			dev->rx_idle_len++;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static void rmnet_sdio_start_tx(struct rmnet_sdio_dev *dev)
+{
+	unsigned long			flags;
+	int				status;
+	struct sk_buff			*skb;
+	struct usb_request		*req;
+	struct usb_composite_dev	*cdev = dev->cdev;
+
+	if (!atomic_read(&dev->online))
+		return;
+
+	if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status))
+		return;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	while (!list_empty(&dev->tx_idle)) {
+		skb = __skb_dequeue(&dev->tx_skb_queue);
+		if (!skb) {
+			spin_unlock_irqrestore(&dev->lock, flags);
+			return;
+		}
+
+		req = list_first_entry(&dev->tx_idle, struct usb_request, list);
+		req->context = skb;
+		req->buf = skb->data;
+		req->length = skb->len;
+
+		list_del(&req->list);
+		dev->tx_idle_len--;
+		spin_unlock(&dev->lock);
+		status = usb_ep_queue(dev->epin, req, GFP_ATOMIC);
+		spin_lock(&dev->lock);
+		if (status) {
+			/* USB still online, queue requests back */
+			if (atomic_read(&dev->online)) {
+				ERROR(cdev, "rmnet tx data enqueue err %d\n",
+						status);
+				list_add_tail(&req->list, &dev->tx_idle);
+				dev->tx_idle_len++;
+				__skb_queue_head(&dev->tx_skb_queue, skb);
+			} else {
+				req->buf = 0;
+				rmnet_sdio_free_req(dev->epin, req);
+				dev_kfree_skb_any(skb);
+			}
+			break;
+		}
+		dev->dpkt_tolaptop++;
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static void rmnet_sdio_data_receive_cb(void *priv, struct sk_buff *skb)
+{
+	struct rmnet_sdio_dev *dev = priv;
+	unsigned long flags;
+
+	/* SDIO mux sends NULL SKB when link state changes */
+	if (!skb) {
+		pr_info("%s: dmux_ch close event\n", __func__);
+		if (test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status) &&
+		    test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status)) {
+			clear_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status);
+			clear_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status);
+			queue_work(dev->wq, &dev->sdio_close_work);
+		}
+		return;
+	}
+
+	if (!atomic_read(&dev->online)) {
+		dev_kfree_skb_any(skb);
+		return;
+	}
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	if (dev->tx_skb_queue.qlen > sdio_tx_pkt_drop_thld) {
+		if (printk_ratelimit())
+			pr_err("%s: tx pkt dropped: tx_drop_cnt:%lu\n",
+					__func__, dev->tx_drp_cnt);
+		dev->tx_drp_cnt++;
+		spin_unlock_irqrestore(&dev->lock, flags);
+		dev_kfree_skb_any(skb);
+		return;
+	}
+
+	__skb_queue_tail(&dev->tx_skb_queue, skb);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	rmnet_sdio_start_tx(dev);
+}
+
+static void rmnet_sdio_data_write_done(void *priv, struct sk_buff *skb)
+{
+	struct rmnet_sdio_dev *dev = priv;
+
+	/* SDIO mux sends NULL SKB when link state changes */
+	if (!skb) {
+		pr_info("%s: dmux_ch open event\n", __func__);
+		queue_delayed_work(dev->wq, &dev->sdio_open_work, 0);
+		return;
+	}
+
+	dev_kfree_skb_any(skb);
+	/* this function is called from
+	 * sdio mux from spin_lock_irqsave
+	 */
+	spin_lock(&dev->lock);
+	dev->dpkts_pending_atdmux--;
+
+	if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status) ||
+			dev->dpkts_pending_atdmux >= sdio_rx_fctrl_dis_thld) {
+		spin_unlock(&dev->lock);
+		return;
+	}
+	spin_unlock(&dev->lock);
+
+	rmnet_sdio_start_rx(dev);
+}
+
+static void rmnet_sdio_data_rx_work(struct work_struct *w)
+{
+	struct rmnet_sdio_dev *dev = container_of(w, struct rmnet_sdio_dev,
+							data_rx_work);
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct sk_buff *skb;
+	int ret;
+	unsigned long flags;
+
+	if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status)) {
+		pr_info("%s: sdio data ch not open\n", __func__);
+		return;
+	}
+
+	spin_lock_irqsave(&dev->lock, flags);
+	while ((skb = __skb_dequeue(&dev->rx_skb_queue))) {
+		spin_unlock_irqrestore(&dev->lock, flags);
+		ret = msm_sdio_dmux_write(rmnet_sdio_data_ch, skb);
+		spin_lock_irqsave(&dev->lock, flags);
+		if (ret < 0) {
+			ERROR(cdev, "rmnet SDIO data write failed\n");
+			dev_kfree_skb_any(skb);
+			break;
+		} else {
+			dev->dpkt_tomodem++;
+			dev->dpkts_pending_atdmux++;
+		}
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static void rmnet_sdio_complete_epout(struct usb_ep *ep,
+					struct usb_request *req)
+{
+	struct rmnet_sdio_dev *dev = ep->driver_data;
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct sk_buff *skb = req->context;
+	int status = req->status;
+	int queue = 0;
+
+	switch (status) {
+	case 0:
+		/* successful completion */
+		skb_put(skb, req->actual);
+		queue = 1;
+		break;
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		dev_kfree_skb_any(skb);
+		req->buf = 0;
+		rmnet_sdio_free_req(ep, req);
+		return;
+	default:
+		/* unexpected failure */
+		ERROR(cdev, "RMNET %s response error %d, %d/%d\n",
+			ep->name, status,
+			req->actual, req->length);
+		dev_kfree_skb_any(skb);
+		break;
+	}
+
+	if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status)) {
+		pr_info("%s: sdio data ch not open\n", __func__);
+		dev_kfree_skb_any(skb);
+		req->buf = 0;
+		rmnet_sdio_free_req(ep, req);
+		return;
+	}
+
+	spin_lock(&dev->lock);
+	if (queue) {
+		__skb_queue_tail(&dev->rx_skb_queue, skb);
+		queue_work(dev->wq, &dev->data_rx_work);
+	}
+
+	if (dev->dpkts_pending_atdmux >= sdio_rx_fctrl_en_thld) {
+		list_add_tail(&req->list, &dev->rx_idle);
+		dev->rx_idle_len++;
+		spin_unlock(&dev->lock);
+		return;
+	}
+	spin_unlock(&dev->lock);
+
+	status = rmnet_sdio_rx_submit(dev, req, GFP_ATOMIC);
+	if (status) {
+		ERROR(cdev, "rmnet data rx enqueue err %d\n", status);
+		list_add_tail(&req->list, &dev->rx_idle);
+		dev->rx_idle_len++;
+	}
+}
+
+static void rmnet_sdio_complete_epin(struct usb_ep *ep, struct usb_request *req)
+{
+	struct rmnet_sdio_dev *dev = ep->driver_data;
+	struct sk_buff  *skb = req->context;
+	struct usb_composite_dev *cdev = dev->cdev;
+	int status = req->status;
+
+	switch (status) {
+	case 0:
+		/* successful completion */
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		break;
+	default:
+		ERROR(cdev, "rmnet data tx ep error %d\n", status);
+		break;
+	}
+
+	spin_lock(&dev->lock);
+	list_add_tail(&req->list, &dev->tx_idle);
+	dev->tx_idle_len++;
+	spin_unlock(&dev->lock);
+	dev_kfree_skb_any(skb);
+
+	rmnet_sdio_start_tx(dev);
+}
+
+static void rmnet_sdio_free_buf(struct rmnet_sdio_dev *dev)
+{
+	struct rmnet_sdio_qmi_buf *qmi;
+	struct usb_request *req;
+	struct list_head *act, *tmp;
+	struct sk_buff *skb;
+	unsigned long flags;
+
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	dev->dpkt_tolaptop = 0;
+	dev->dpkt_tomodem = 0;
+	dev->cpkt_tolaptop = 0;
+	dev->cpkt_tomodem = 0;
+	dev->dpkts_pending_atdmux = 0;
+	dev->tx_drp_cnt = 0;
+
+	/* free all usb requests in tx pool */
+	list_for_each_safe(act, tmp, &dev->tx_idle) {
+		req = list_entry(act, struct usb_request, list);
+		list_del(&req->list);
+		dev->tx_idle_len--;
+		req->buf = NULL;
+		rmnet_sdio_free_req(dev->epout, req);
+	}
+
+	/* free all usb requests in rx pool */
+	list_for_each_safe(act, tmp, &dev->rx_idle) {
+		req = list_entry(act, struct usb_request, list);
+		list_del(&req->list);
+		dev->rx_idle_len--;
+		req->buf = NULL;
+		rmnet_sdio_free_req(dev->epin, req);
+	}
+
+	/* free all buffers in qmi request pool */
+	list_for_each_safe(act, tmp, &dev->qmi_req_q) {
+		qmi = list_entry(act, struct rmnet_sdio_qmi_buf, list);
+		list_del(&qmi->list);
+		dev->qreq_q_len--;
+		rmnet_sdio_free_qmi(qmi);
+	}
+
+	/* free all buffers in qmi request pool */
+	list_for_each_safe(act, tmp, &dev->qmi_resp_q) {
+		qmi = list_entry(act, struct rmnet_sdio_qmi_buf, list);
+		list_del(&qmi->list);
+		dev->qresp_q_len--;
+		rmnet_sdio_free_qmi(qmi);
+	}
+
+	while ((skb = __skb_dequeue(&dev->tx_skb_queue)))
+		dev_kfree_skb_any(skb);
+
+	while ((skb = __skb_dequeue(&dev->rx_skb_queue)))
+		dev_kfree_skb_any(skb);
+
+	rmnet_sdio_free_req(dev->epnotify, dev->notify_req);
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static void rmnet_sdio_set_modem_cbits_w(struct work_struct *w)
+{
+	struct rmnet_sdio_dev *dev;
+
+	dev = container_of(w, struct rmnet_sdio_dev, set_modem_ctl_bits_work);
+
+	if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status))
+		return;
+
+	pr_debug("%s: cbits_to_modem:%d\n",
+			__func__, dev->cbits_to_modem);
+
+	sdio_cmux_tiocmset(rmnet_sdio_ctl_ch,
+			dev->cbits_to_modem,
+			~dev->cbits_to_modem);
+}
+
+static void rmnet_sdio_disconnect_work(struct work_struct *w)
+{
+	/* REVISIT: Push all the data to sdio if anythign is pending */
+}
+static void rmnet_sdio_suspend(struct usb_function *f)
+{
+	struct rmnet_sdio_dev *dev = container_of(f, struct rmnet_sdio_dev,
+								function);
+
+	if (!atomic_read(&dev->online))
+		return;
+	/* This is a workaround for Windows Host bug during suspend.
+	 * Windows 7/xp Hosts are suppose to drop DTR, when Host suspended.
+	 * Since it is not beind done, Hence exclusively dropping the DTR
+	 * from function driver suspend.
+	 */
+	dev->cbits_to_modem &= ~TIOCM_DTR;
+	queue_work(dev->wq, &dev->set_modem_ctl_bits_work);
+}
+static void rmnet_sdio_disable(struct usb_function *f)
+{
+	struct rmnet_sdio_dev *dev = container_of(f, struct rmnet_sdio_dev,
+								 function);
+
+	if (!atomic_read(&dev->online))
+		return;
+
+	usb_ep_disable(dev->epnotify);
+	usb_ep_disable(dev->epout);
+	usb_ep_disable(dev->epin);
+
+	atomic_set(&dev->online, 0);
+	atomic_set(&dev->notify_count, 0);
+	rmnet_sdio_free_buf(dev);
+
+	/* cleanup work */
+	queue_work(dev->wq, &dev->disconnect_work);
+	dev->cbits_to_modem = 0;
+	queue_work(dev->wq, &dev->set_modem_ctl_bits_work);
+}
+
+static void rmnet_close_sdio_work(struct work_struct *w)
+{
+	struct rmnet_sdio_dev		*dev;
+	unsigned long			flags;
+	struct usb_cdc_notification     *event;
+	int				status;
+	struct rmnet_sdio_qmi_buf	*qmi;
+	struct usb_request		*req;
+	struct sk_buff			*skb;
+
+	pr_debug("%s:\n", __func__);
+
+	dev = container_of(w, struct rmnet_sdio_dev, sdio_close_work);
+
+	if (!atomic_read(&dev->online))
+		return;
+
+	usb_ep_fifo_flush(dev->epnotify);
+
+	spin_lock_irqsave(&dev->lock, flags);
+	event = dev->notify_req->buf;
+
+	event->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
+			| USB_RECIP_INTERFACE;
+	event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
+	event->wValue = cpu_to_le16(0);
+	event->wIndex = cpu_to_le16(dev->ifc_id);
+	event->wLength = cpu_to_le16(0);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	status = usb_ep_queue(dev->epnotify, dev->notify_req, GFP_KERNEL);
+	if (status < 0) {
+		if (!atomic_read(&dev->online))
+			return;
+		pr_err("%s: rmnet notify ep enqueue error %d\n",
+				__func__, status);
+	}
+
+	usb_ep_fifo_flush(dev->epout);
+	usb_ep_fifo_flush(dev->epin);
+	cancel_work_sync(&dev->data_rx_work);
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	if (!atomic_read(&dev->online)) {
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return;
+	}
+
+	/* free all usb requests in tx pool */
+	while (!list_empty(&dev->tx_idle)) {
+		req = list_first_entry(&dev->tx_idle, struct usb_request, list);
+		list_del(&req->list);
+		dev->tx_idle_len--;
+		req->buf = NULL;
+		rmnet_sdio_free_req(dev->epout, req);
+	}
+
+	/* free all usb requests in rx pool */
+	while (!list_empty(&dev->rx_idle)) {
+		req = list_first_entry(&dev->rx_idle, struct usb_request, list);
+		list_del(&req->list);
+		dev->rx_idle_len--;
+		req->buf = NULL;
+		rmnet_sdio_free_req(dev->epin, req);
+	}
+
+	/* free all buffers in qmi request pool */
+	while (!list_empty(&dev->qmi_req_q)) {
+		qmi = list_first_entry(&dev->qmi_req_q,
+				struct rmnet_sdio_qmi_buf, list);
+		list_del(&qmi->list);
+		dev->qreq_q_len--;
+		rmnet_sdio_free_qmi(qmi);
+	}
+
+	/* free all buffers in qmi response pool */
+	while (!list_empty(&dev->qmi_resp_q)) {
+		qmi = list_first_entry(&dev->qmi_resp_q,
+				struct rmnet_sdio_qmi_buf, list);
+		list_del(&qmi->list);
+		dev->qresp_q_len--;
+		rmnet_sdio_free_qmi(qmi);
+	}
+	atomic_set(&dev->notify_count, 0);
+
+	pr_info("%s: setting notify count to zero\n", __func__);
+
+
+	while ((skb = __skb_dequeue(&dev->tx_skb_queue)))
+		dev_kfree_skb_any(skb);
+
+	while ((skb = __skb_dequeue(&dev->rx_skb_queue)))
+		dev_kfree_skb_any(skb);
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static int rmnet_sdio_start_io(struct rmnet_sdio_dev *dev)
+{
+	struct usb_request *req;
+	int ret, i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	if (!atomic_read(&dev->online)) {
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return 0;
+	}
+
+	if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status) ||
+			!test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status)) {
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return 0;
+	}
+
+	for (i = 0; i < RMNET_SDIO_RX_REQ_MAX; i++) {
+		req = rmnet_sdio_alloc_req(dev->epout, 0, GFP_ATOMIC);
+		if (IS_ERR(req)) {
+			ret = PTR_ERR(req);
+			spin_unlock_irqrestore(&dev->lock, flags);
+			goto free_buf;
+		}
+		req->complete = rmnet_sdio_complete_epout;
+		list_add_tail(&req->list, &dev->rx_idle);
+		dev->rx_idle_len++;
+	}
+	for (i = 0; i < RMNET_SDIO_TX_REQ_MAX; i++) {
+		req = rmnet_sdio_alloc_req(dev->epin, 0, GFP_ATOMIC);
+		if (IS_ERR(req)) {
+			ret = PTR_ERR(req);
+			spin_unlock_irqrestore(&dev->lock, flags);
+			goto free_buf;
+		}
+		req->complete = rmnet_sdio_complete_epin;
+		list_add_tail(&req->list, &dev->tx_idle);
+		dev->tx_idle_len++;
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	/* Queue Rx data requests */
+	rmnet_sdio_start_rx(dev);
+
+	return 0;
+
+free_buf:
+	rmnet_sdio_free_buf(dev);
+	dev->epout = dev->epin = dev->epnotify = NULL; /* release endpoints */
+	return ret;
+}
+
+
+#define RMNET_SDIO_OPEN_RETRY_DELAY	msecs_to_jiffies(2000)
+#define SDIO_SDIO_OPEN_MAX_RETRY	90
+static void rmnet_open_sdio_work(struct work_struct *w)
+{
+	struct rmnet_sdio_dev *dev =
+			container_of(w, struct rmnet_sdio_dev,
+						sdio_open_work.work);
+	struct usb_composite_dev *cdev = dev->cdev;
+	int ret;
+	static int retry_cnt;
+
+	if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status)) {
+		/* Control channel for QMI messages */
+		ret = sdio_cmux_open(rmnet_sdio_ctl_ch,
+				rmnet_sdio_ctl_receive_cb,
+				rmnet_sdio_ctl_write_done,
+				rmnet_sdio_sts_callback, dev);
+		if (!ret)
+			set_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status);
+	}
+
+	if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status)) {
+		/* Data channel for network packets */
+		ret = msm_sdio_dmux_open(rmnet_sdio_data_ch, dev,
+				rmnet_sdio_data_receive_cb,
+				rmnet_sdio_data_write_done);
+		if (!ret)
+			set_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status);
+	}
+
+	if (test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status) &&
+			test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status)) {
+
+		rmnet_sdio_start_io(dev);
+
+		/* if usb cable is connected, update DTR status to modem */
+		if (atomic_read(&dev->online))
+			queue_work(dev->wq, &dev->set_modem_ctl_bits_work);
+
+		pr_info("%s: usb rmnet sdio channels are open retry_cnt:%d\n",
+				__func__, retry_cnt);
+		retry_cnt = 0;
+		return;
+	}
+
+	retry_cnt++;
+	pr_debug("%s: usb rmnet sdio open retry_cnt:%d\n",
+			__func__, retry_cnt);
+
+	if (retry_cnt > SDIO_SDIO_OPEN_MAX_RETRY) {
+		if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status))
+			ERROR(cdev, "Unable to open control SDIO channel\n");
+
+		if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status))
+			ERROR(cdev, "Unable to open DATA SDIO channel\n");
+
+	} else {
+		queue_delayed_work(dev->wq, &dev->sdio_open_work,
+				RMNET_SDIO_OPEN_RETRY_DELAY);
+	}
+}
+
+static int rmnet_sdio_set_alt(struct usb_function *f,
+			unsigned intf, unsigned alt)
+{
+	struct rmnet_sdio_dev *dev = container_of(f, struct rmnet_sdio_dev,
+								function);
+	struct usb_composite_dev *cdev = dev->cdev;
+	int ret = 0;
+
+	/* Enable epin */
+	dev->epin->driver_data = dev;
+	ret = config_ep_by_speed(cdev->gadget, f, dev->epin);
+	if (ret) {
+		dev->epin->desc = NULL;
+		ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
+			dev->epin->name, ret);
+		return ret;
+	}
+	ret = usb_ep_enable(dev->epin);
+	if (ret) {
+		ERROR(cdev, "can't enable %s, result %d\n",
+			dev->epin->name, ret);
+		return ret;
+	}
+
+	/* Enable epout */
+	dev->epout->driver_data = dev;
+	ret = config_ep_by_speed(cdev->gadget, f, dev->epout);
+	if (ret) {
+		dev->epout->desc = NULL;
+		ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
+			dev->epout->name, ret);
+		usb_ep_disable(dev->epin);
+		return ret;
+	}
+	ret = usb_ep_enable(dev->epout);
+	if (ret) {
+		ERROR(cdev, "can't enable %s, result %d\n",
+			dev->epout->name, ret);
+		usb_ep_disable(dev->epin);
+		return ret;
+	}
+
+	/* Enable epnotify */
+	ret = config_ep_by_speed(cdev->gadget, f, dev->epnotify);
+	if (ret) {
+		dev->epnotify->desc = NULL;
+		ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
+			dev->epnotify->name, ret);
+		usb_ep_disable(dev->epin);
+		usb_ep_disable(dev->epout);
+		return ret;
+	}
+	ret = usb_ep_enable(dev->epnotify);
+	if (ret) {
+		ERROR(cdev, "can't enable %s, result %d\n",
+			dev->epnotify->name, ret);
+		usb_ep_disable(dev->epin);
+		usb_ep_disable(dev->epout);
+		return ret;
+	}
+
+	/* allocate notification */
+	dev->notify_req = rmnet_sdio_alloc_req(dev->epnotify,
+				RMNET_SDIO_MAX_NFY_SZE, GFP_ATOMIC);
+
+	if (IS_ERR(dev->notify_req)) {
+		ret = PTR_ERR(dev->notify_req);
+		pr_err("%s: unable to allocate memory for notify ep\n",
+				__func__);
+		return ret;
+	}
+	dev->notify_req->complete = rmnet_sdio_notify_complete;
+	dev->notify_req->context = dev;
+	dev->notify_req->length = RMNET_SDIO_MAX_NFY_SZE;
+
+	atomic_set(&dev->online, 1);
+
+	ret = rmnet_sdio_start_io(dev);
+
+	return ret;
+
+}
+
+static int rmnet_sdio_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = c->cdev;
+	struct rmnet_sdio_dev *dev = container_of(f, struct rmnet_sdio_dev,
+								function);
+	int id;
+	struct usb_ep *ep;
+
+	dev->cdev = cdev;
+
+	/* allocate interface ID */
+	id = usb_interface_id(c, f);
+	if (id < 0)
+		return id;
+	dev->ifc_id = id;
+	rmnet_sdio_interface_desc.bInterfaceNumber = id;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &rmnet_sdio_fs_in_desc);
+	if (!ep)
+		goto out;
+	ep->driver_data = cdev; /* claim endpoint */
+	dev->epin = ep;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &rmnet_sdio_fs_out_desc);
+	if (!ep)
+		goto out;
+	ep->driver_data = cdev; /* claim endpoint */
+	dev->epout = ep;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &rmnet_sdio_fs_notify_desc);
+	if (!ep)
+		goto out;
+	ep->driver_data = cdev; /* claim endpoint */
+	dev->epnotify = ep;
+
+	/* support all relevant hardware speeds... we expect that when
+	 * hardware is dual speed, all bulk-capable endpoints work at
+	 * both speeds
+	 */
+	if (gadget_is_dualspeed(c->cdev->gadget)) {
+		rmnet_sdio_hs_in_desc.bEndpointAddress =
+			rmnet_sdio_fs_in_desc.bEndpointAddress;
+		rmnet_sdio_hs_out_desc.bEndpointAddress =
+			rmnet_sdio_fs_out_desc.bEndpointAddress;
+		rmnet_sdio_hs_notify_desc.bEndpointAddress =
+			rmnet_sdio_fs_notify_desc.bEndpointAddress;
+	}
+
+	queue_delayed_work(dev->wq, &dev->sdio_open_work, 0);
+
+	return 0;
+
+out:
+	if (dev->epnotify)
+		dev->epnotify->driver_data = NULL;
+	if (dev->epout)
+		dev->epout->driver_data = NULL;
+	if (dev->epin)
+		dev->epin->driver_data = NULL;
+
+	return -ENODEV;
+}
+
+static void
+rmnet_sdio_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct rmnet_sdio_dev *dev = container_of(f, struct rmnet_sdio_dev,
+								function);
+
+	cancel_delayed_work_sync(&dev->sdio_open_work);
+	destroy_workqueue(dev->wq);
+
+	dev->epout = dev->epin = dev->epnotify = NULL; /* release endpoints */
+
+	if (test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status)) {
+		msm_sdio_dmux_close(rmnet_sdio_data_ch);
+		clear_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status);
+	}
+
+	if (test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status)) {
+		sdio_cmux_close(rmnet_sdio_ctl_ch);
+		clear_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status);
+	}
+
+	debugfs_remove_recursive(dev->dent);
+
+	kfree(dev);
+}
+
+#if defined(CONFIG_DEBUG_FS)
+static ssize_t rmnet_sdio_read_stats(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	struct rmnet_sdio_dev *dev = file->private_data;
+	char *buf;
+	unsigned long flags;
+	int ret;
+
+	buf = kzalloc(sizeof(char) * 1024, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	ret = scnprintf(buf, PAGE_SIZE,
+			"-*-DATA-*-\n"
+			"dpkts_tohost:%lu epInPool:%u tx_size:%u drp_cnt:%lu\n"
+			"dpkts_tomodem:%lu epOutPool:%u rx_size:%u pending:%u\n"
+			"-*-QMI-*-\n"
+			"cpkts_tomodem:%lu  qmi_req_q:%u cbits:%d\n"
+			"cpkts_tolaptop:%lu qmi_resp_q:%u notify_cnt:%d\n"
+			"-*-MISC-*-\n"
+			"data_ch_status: %lu ctrl_ch_status: %lu\n",
+			/* data */
+			dev->dpkt_tolaptop, dev->tx_idle_len,
+			dev->tx_skb_queue.qlen, dev->tx_drp_cnt,
+			dev->dpkt_tomodem, dev->rx_idle_len,
+			dev->rx_skb_queue.qlen, dev->dpkts_pending_atdmux,
+			/* qmi */
+			dev->cpkt_tomodem, dev->qreq_q_len,
+			dev->cbits_to_modem,
+			dev->cpkt_tolaptop, dev->qresp_q_len,
+			atomic_read(&dev->notify_count),
+			/* misc */
+			dev->data_ch_status, dev->ctrl_ch_status);
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, ret);
+
+	kfree(buf);
+
+	return ret;
+}
+
+static ssize_t rmnet_sdio_reset_stats(struct file *file, const char __user *buf,
+				 size_t count, loff_t *ppos)
+{
+	struct rmnet_sdio_dev *dev = file->private_data;
+
+	dev->dpkt_tolaptop = 0;
+	dev->dpkt_tomodem = 0;
+	dev->cpkt_tolaptop = 0;
+	dev->cpkt_tomodem = 0;
+	dev->dpkts_pending_atdmux = 0;
+	dev->tx_drp_cnt = 0;
+
+	/* TBD: How do we reset skb qlen
+	 * it might have side effects
+	 */
+
+	return count;
+}
+
+static int debug_rmnet_sdio_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+
+	return 0;
+}
+
+const struct file_operations debug_rmnet_sdio_stats_ops = {
+	.open  = debug_rmnet_sdio_open,
+	.read  = rmnet_sdio_read_stats,
+	.write = rmnet_sdio_reset_stats,
+};
+
+static void rmnet_sdio_debugfs_init(struct rmnet_sdio_dev *dev)
+{
+	dev->dent = debugfs_create_dir("usb_rmnet_sdio", 0);
+	if (IS_ERR(dev->dent))
+		return;
+
+	debugfs_create_file("status", 0444, dev->dent, dev,
+					&debug_rmnet_sdio_stats_ops);
+}
+#else
+static void rmnet_sdio_debugfs_init(struct rmnet_sdio_dev *dev)
+{
+	return;
+}
+#endif
+
+int rmnet_sdio_function_add(struct usb_configuration *c)
+{
+	struct rmnet_sdio_dev *dev;
+	int ret;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	dev->wq = create_singlethread_workqueue("k_rmnet_work");
+	if (!dev->wq) {
+		ret = -ENOMEM;
+		goto free_dev;
+	}
+
+	spin_lock_init(&dev->lock);
+	atomic_set(&dev->notify_count, 0);
+	atomic_set(&dev->online, 0);
+
+	INIT_WORK(&dev->disconnect_work, rmnet_sdio_disconnect_work);
+	INIT_WORK(&dev->set_modem_ctl_bits_work, rmnet_sdio_set_modem_cbits_w);
+
+	INIT_WORK(&dev->ctl_rx_work, rmnet_sdio_control_rx_work);
+	INIT_WORK(&dev->data_rx_work, rmnet_sdio_data_rx_work);
+
+	INIT_DELAYED_WORK(&dev->sdio_open_work, rmnet_open_sdio_work);
+	INIT_WORK(&dev->sdio_close_work, rmnet_close_sdio_work);
+
+	INIT_LIST_HEAD(&dev->qmi_req_q);
+	INIT_LIST_HEAD(&dev->qmi_resp_q);
+
+	INIT_LIST_HEAD(&dev->rx_idle);
+	INIT_LIST_HEAD(&dev->tx_idle);
+	skb_queue_head_init(&dev->tx_skb_queue);
+	skb_queue_head_init(&dev->rx_skb_queue);
+
+	dev->function.name = "rmnet_sdio";
+	dev->function.strings = rmnet_sdio_strings;
+	dev->function.descriptors = rmnet_sdio_fs_function;
+	dev->function.hs_descriptors = rmnet_sdio_hs_function;
+	dev->function.bind = rmnet_sdio_bind;
+	dev->function.unbind = rmnet_sdio_unbind;
+	dev->function.setup = rmnet_sdio_setup;
+	dev->function.set_alt = rmnet_sdio_set_alt;
+	dev->function.disable = rmnet_sdio_disable;
+	dev->function.suspend = rmnet_sdio_suspend;
+
+	ret = usb_add_function(c, &dev->function);
+	if (ret)
+		goto free_wq;
+
+	rmnet_sdio_debugfs_init(dev);
+
+       return 0;
+
+free_wq:
+       destroy_workqueue(dev->wq);
+free_dev:
+       kfree(dev);
+
+       return ret;
+}
diff --git a/drivers/usb/gadget/f_rmnet_smd.c b/drivers/usb/gadget/f_rmnet_smd.c
new file mode 100644
index 0000000..b71f646
--- /dev/null
+++ b/drivers/usb/gadget/f_rmnet_smd.c
@@ -0,0 +1,1391 @@
+/*
+ * f_rmnet.c -- RmNet function driver
+ *
+ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
+ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
+ * Copyright (C) 2008 Nokia Corporation
+ * Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <linux/bitops.h>
+#include <linux/termios.h>
+#include <linux/debugfs.h>
+
+#include <mach/msm_smd.h>
+#include <linux/usb/cdc.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/ch9.h>
+
+#include "gadget_chips.h"
+
+#ifndef CONFIG_MSM_SMD
+#define CONFIG_RMNET_SMD_CTL_CHANNEL	""
+#define CONFIG_RMNET_SMD_DATA_CHANNEL	""
+#endif
+
+static char *rmnet_ctl_ch = CONFIG_RMNET_SMD_CTL_CHANNEL;
+module_param(rmnet_ctl_ch, charp, S_IRUGO);
+MODULE_PARM_DESC(rmnet_ctl_ch, "RmNet control SMD channel");
+
+static char *rmnet_data_ch = CONFIG_RMNET_SMD_DATA_CHANNEL;
+module_param(rmnet_data_ch, charp, S_IRUGO);
+MODULE_PARM_DESC(rmnet_data_ch, "RmNet data SMD channel");
+
+#define RMNET_SMD_ACM_CTRL_DTR	(1 << 0)
+
+#define RMNET_SMD_NOTIFY_INTERVAL	5
+#define RMNET_SMD_MAX_NOTIFY_SIZE	sizeof(struct usb_cdc_notification)
+
+#define QMI_REQ_MAX			4
+#define QMI_REQ_SIZE			2048
+#define QMI_RESP_MAX			8
+#define QMI_RESP_SIZE			2048
+
+#define RMNET_RX_REQ_MAX		8
+#define RMNET_RX_REQ_SIZE		2048
+#define RMNET_TX_REQ_MAX		8
+#define RMNET_TX_REQ_SIZE		2048
+
+#define RMNET_TXN_MAX	 		2048
+
+/* QMI requests & responses buffer*/
+struct qmi_buf {
+	void *buf;
+	int len;
+	struct list_head list;
+};
+
+/* Control & data SMD channel private data */
+struct rmnet_smd_ch_info {
+	struct smd_channel 	*ch;
+	struct tasklet_struct	tx_tlet;
+	struct tasklet_struct	rx_tlet;
+#define CH_OPENED	0
+	unsigned long		flags;
+	/* pending rx packet length */
+	atomic_t		rx_pkt;
+	/* wait for smd open event*/
+	wait_queue_head_t	wait;
+};
+
+struct rmnet_smd_dev {
+	struct usb_function function;
+	struct usb_composite_dev *cdev;
+
+	struct usb_ep		*epout;
+	struct usb_ep		*epin;
+	struct usb_ep		*epnotify;
+	struct usb_request 	*notify_req;
+
+	u8			ifc_id;
+	/* QMI lists */
+	struct list_head	qmi_req_pool;
+	struct list_head	qmi_resp_pool;
+	struct list_head	qmi_req_q;
+	struct list_head	qmi_resp_q;
+	/* Tx/Rx lists */
+	struct list_head 	tx_idle;
+	struct list_head 	rx_idle;
+	struct list_head	rx_queue;
+
+	spinlock_t		lock;
+	atomic_t		online;
+	atomic_t		notify_count;
+
+	struct platform_driver		pdrv;
+	u8				is_pdrv_used;
+	struct rmnet_smd_ch_info	smd_ctl;
+	struct rmnet_smd_ch_info	smd_data;
+
+	struct workqueue_struct *wq;
+	struct work_struct connect_work;
+	struct work_struct disconnect_work;
+
+	unsigned long	dpkts_to_host;
+	unsigned long	dpkts_from_modem;
+	unsigned long	dpkts_from_host;
+	unsigned long	dpkts_to_modem;
+
+	unsigned long	cpkts_to_host;
+	unsigned long	cpkts_from_modem;
+	unsigned long	cpkts_from_host;
+	unsigned long	cpkts_to_modem;
+};
+
+static struct rmnet_smd_dev *rmnet_smd;
+
+static struct usb_interface_descriptor rmnet_smd_interface_desc = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	/* .bInterfaceNumber = DYNAMIC */
+	.bNumEndpoints =	3,
+	.bInterfaceClass =	USB_CLASS_VENDOR_SPEC,
+	.bInterfaceSubClass =	USB_CLASS_VENDOR_SPEC,
+	.bInterfaceProtocol =	USB_CLASS_VENDOR_SPEC,
+	/* .iInterface = DYNAMIC */
+};
+
+/* Full speed support */
+static struct usb_endpoint_descriptor rmnet_smd_fs_notify_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	__constant_cpu_to_le16(
+						RMNET_SMD_MAX_NOTIFY_SIZE),
+	.bInterval =		1 << RMNET_SMD_NOTIFY_INTERVAL,
+};
+
+static struct usb_endpoint_descriptor rmnet_smd_fs_in_desc  = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize   = __constant_cpu_to_le16(64),
+};
+
+static struct usb_endpoint_descriptor rmnet_smd_fs_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize   = __constant_cpu_to_le16(64),
+};
+
+static struct usb_descriptor_header *rmnet_smd_fs_function[] = {
+	(struct usb_descriptor_header *) &rmnet_smd_interface_desc,
+	(struct usb_descriptor_header *) &rmnet_smd_fs_notify_desc,
+	(struct usb_descriptor_header *) &rmnet_smd_fs_in_desc,
+	(struct usb_descriptor_header *) &rmnet_smd_fs_out_desc,
+	NULL,
+};
+
+/* High speed support */
+static struct usb_endpoint_descriptor rmnet_smd_hs_notify_desc  = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	__constant_cpu_to_le16(
+						RMNET_SMD_MAX_NOTIFY_SIZE),
+	.bInterval =		RMNET_SMD_NOTIFY_INTERVAL + 4,
+};
+
+static struct usb_endpoint_descriptor rmnet_smd_hs_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	__constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor rmnet_smd_hs_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	__constant_cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *rmnet_smd_hs_function[] = {
+	(struct usb_descriptor_header *) &rmnet_smd_interface_desc,
+	(struct usb_descriptor_header *) &rmnet_smd_hs_notify_desc,
+	(struct usb_descriptor_header *) &rmnet_smd_hs_in_desc,
+	(struct usb_descriptor_header *) &rmnet_smd_hs_out_desc,
+	NULL,
+};
+
+/* String descriptors */
+
+static struct usb_string rmnet_smd_string_defs[] = {
+	[0].s = "QMI RmNet",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings rmnet_smd_string_table = {
+	.language =		0x0409,	/* en-us */
+	.strings =		rmnet_smd_string_defs,
+};
+
+static struct usb_gadget_strings *rmnet_smd_strings[] = {
+	&rmnet_smd_string_table,
+	NULL,
+};
+
+static struct qmi_buf *
+rmnet_smd_alloc_qmi(unsigned len, gfp_t kmalloc_flags)
+{
+	struct qmi_buf *qmi;
+
+	qmi = kmalloc(sizeof(struct qmi_buf), kmalloc_flags);
+	if (qmi != NULL) {
+		qmi->buf = kmalloc(len, kmalloc_flags);
+		if (qmi->buf == NULL) {
+			kfree(qmi);
+			qmi = NULL;
+		}
+	}
+
+	return qmi ? qmi : ERR_PTR(-ENOMEM);
+}
+
+static void rmnet_smd_free_qmi(struct qmi_buf *qmi)
+{
+	kfree(qmi->buf);
+	kfree(qmi);
+}
+/*
+ * Allocate a usb_request and its buffer.  Returns a pointer to the
+ * usb_request or a error code if there is an error.
+ */
+static struct usb_request *
+rmnet_smd_alloc_req(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags)
+{
+	struct usb_request *req;
+
+	req = usb_ep_alloc_request(ep, kmalloc_flags);
+
+	if (req != NULL) {
+		req->length = len;
+		req->buf = kmalloc(len, kmalloc_flags);
+		if (req->buf == NULL) {
+			usb_ep_free_request(ep, req);
+			req = NULL;
+		}
+	}
+
+	return req ? req : ERR_PTR(-ENOMEM);
+}
+
+/*
+ * Free a usb_request and its buffer.
+ */
+static void rmnet_smd_free_req(struct usb_ep *ep, struct usb_request *req)
+{
+	kfree(req->buf);
+	usb_ep_free_request(ep, req);
+}
+
+static void rmnet_smd_notify_complete(struct usb_ep *ep,
+					 struct usb_request *req)
+{
+	struct rmnet_smd_dev *dev = req->context;
+	struct usb_composite_dev *cdev = dev->cdev;
+	int status = req->status;
+
+	switch (status) {
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		atomic_set(&dev->notify_count, 0);
+		break;
+	default:
+		ERROR(cdev, "rmnet notify ep error %d\n", status);
+		/* FALLTHROUGH */
+	case 0:
+		if (ep != dev->epnotify)
+			break;
+
+		/* handle multiple pending QMI_RESPONSE_AVAILABLE
+		 * notifications by resending until we're done
+		 */
+		if (atomic_dec_and_test(&dev->notify_count))
+			break;
+
+		status = usb_ep_queue(dev->epnotify, req, GFP_ATOMIC);
+		if (status) {
+			atomic_dec(&dev->notify_count);
+			ERROR(cdev, "rmnet notify ep enqueue error %d\n",
+					status);
+		}
+		break;
+	}
+}
+
+static void qmi_smd_response_available(struct rmnet_smd_dev *dev)
+{
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct usb_request		*req = dev->notify_req;
+	struct usb_cdc_notification	*event = req->buf;
+	int status;
+
+	/* Response will be sent later */
+	if (atomic_inc_return(&dev->notify_count) != 1)
+		return;
+
+	event->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
+			| USB_RECIP_INTERFACE;
+	event->bNotificationType = USB_CDC_NOTIFY_RESPONSE_AVAILABLE;
+	event->wValue = cpu_to_le16(0);
+	event->wIndex = cpu_to_le16(dev->ifc_id);
+	event->wLength = cpu_to_le16(0);
+
+	status = usb_ep_queue(dev->epnotify, dev->notify_req, GFP_ATOMIC);
+	if (status < 0) {
+		atomic_dec(&dev->notify_count);
+		ERROR(cdev, "rmnet notify ep enqueue error %d\n", status);
+	}
+}
+
+/* TODO
+ * handle modem restart events
+ */
+static void rmnet_smd_event_notify(void *priv, unsigned event)
+{
+	struct rmnet_smd_ch_info *smd_info = priv;
+	int len = atomic_read(&smd_info->rx_pkt);
+	struct rmnet_smd_dev *dev =
+				(struct rmnet_smd_dev *) smd_info->tx_tlet.data;
+
+	switch (event) {
+	case SMD_EVENT_DATA: {
+		if (!atomic_read(&dev->online))
+			break;
+		if (len && (smd_write_avail(smd_info->ch) >= len))
+			tasklet_schedule(&smd_info->rx_tlet);
+
+		if (smd_read_avail(smd_info->ch))
+			tasklet_schedule(&smd_info->tx_tlet);
+
+		break;
+	}
+	case SMD_EVENT_OPEN:
+		/* usb endpoints are not enabled untill smd channels
+		 * are opened. wake up worker thread to continue
+		 * connection processing
+		 */
+		set_bit(CH_OPENED, &smd_info->flags);
+		wake_up(&smd_info->wait);
+		break;
+	case SMD_EVENT_CLOSE:
+		/* We will never come here.
+		 * reset flags after closing smd channel
+		 * */
+		clear_bit(CH_OPENED, &smd_info->flags);
+		break;
+	}
+}
+
+static void rmnet_control_tx_tlet(unsigned long arg)
+{
+	struct rmnet_smd_dev *dev = (struct rmnet_smd_dev *) arg;
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct qmi_buf *qmi_resp;
+	int sz;
+	unsigned long flags;
+
+	while (1) {
+		sz = smd_cur_packet_size(dev->smd_ctl.ch);
+		if (sz == 0)
+			break;
+		if (smd_read_avail(dev->smd_ctl.ch) < sz)
+			break;
+
+		spin_lock_irqsave(&dev->lock, flags);
+		if (list_empty(&dev->qmi_resp_pool)) {
+			ERROR(cdev, "rmnet QMI Tx buffers full\n");
+			spin_unlock_irqrestore(&dev->lock, flags);
+			break;
+		}
+		qmi_resp = list_first_entry(&dev->qmi_resp_pool,
+				struct qmi_buf, list);
+		list_del(&qmi_resp->list);
+		spin_unlock_irqrestore(&dev->lock, flags);
+
+		qmi_resp->len = smd_read(dev->smd_ctl.ch, qmi_resp->buf, sz);
+
+		spin_lock_irqsave(&dev->lock, flags);
+		dev->cpkts_from_modem++;
+		list_add_tail(&qmi_resp->list, &dev->qmi_resp_q);
+		spin_unlock_irqrestore(&dev->lock, flags);
+
+		qmi_smd_response_available(dev);
+	}
+
+}
+
+static void rmnet_control_rx_tlet(unsigned long arg)
+{
+	struct rmnet_smd_dev *dev = (struct rmnet_smd_dev *) arg;
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct qmi_buf *qmi_req;
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	while (1) {
+
+		if (list_empty(&dev->qmi_req_q)) {
+			atomic_set(&dev->smd_ctl.rx_pkt, 0);
+			break;
+		}
+		qmi_req = list_first_entry(&dev->qmi_req_q,
+				struct qmi_buf, list);
+		if (smd_write_avail(dev->smd_ctl.ch) < qmi_req->len) {
+			atomic_set(&dev->smd_ctl.rx_pkt, qmi_req->len);
+			DBG(cdev, "rmnet control smd channel full\n");
+			break;
+		}
+
+		list_del(&qmi_req->list);
+		dev->cpkts_from_host++;
+		spin_unlock_irqrestore(&dev->lock, flags);
+		ret = smd_write(dev->smd_ctl.ch, qmi_req->buf, qmi_req->len);
+		spin_lock_irqsave(&dev->lock, flags);
+		if (ret != qmi_req->len) {
+			ERROR(cdev, "rmnet control smd write failed\n");
+			break;
+		}
+		dev->cpkts_to_modem++;
+		list_add_tail(&qmi_req->list, &dev->qmi_req_pool);
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static void rmnet_smd_command_complete(struct usb_ep *ep,
+					struct usb_request *req)
+{
+	struct rmnet_smd_dev *dev = req->context;
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct qmi_buf *qmi_req;
+	int ret;
+
+	if (req->status < 0) {
+		ERROR(cdev, "rmnet command error %d\n", req->status);
+		return;
+	}
+
+	spin_lock(&dev->lock);
+	dev->cpkts_from_host++;
+	/* no pending control rx packet */
+	if (!atomic_read(&dev->smd_ctl.rx_pkt)) {
+		if (smd_write_avail(dev->smd_ctl.ch) < req->actual) {
+			atomic_set(&dev->smd_ctl.rx_pkt, req->actual);
+			goto queue_req;
+		}
+		spin_unlock(&dev->lock);
+		ret = smd_write(dev->smd_ctl.ch, req->buf, req->actual);
+		/* This should never happen */
+		if (ret != req->actual)
+			ERROR(cdev, "rmnet control smd write failed\n");
+		spin_lock(&dev->lock);
+		dev->cpkts_to_modem++;
+		spin_unlock(&dev->lock);
+		return;
+	}
+queue_req:
+	if (list_empty(&dev->qmi_req_pool)) {
+		spin_unlock(&dev->lock);
+		ERROR(cdev, "rmnet QMI pool is empty\n");
+		return;
+	}
+
+	qmi_req = list_first_entry(&dev->qmi_req_pool, struct qmi_buf, list);
+	list_del(&qmi_req->list);
+	spin_unlock(&dev->lock);
+	memcpy(qmi_req->buf, req->buf, req->actual);
+	qmi_req->len = req->actual;
+	spin_lock(&dev->lock);
+	list_add_tail(&qmi_req->list, &dev->qmi_req_q);
+	spin_unlock(&dev->lock);
+}
+static void rmnet_txcommand_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct rmnet_smd_dev *dev = req->context;
+
+	spin_lock(&dev->lock);
+	dev->cpkts_to_host++;
+	spin_unlock(&dev->lock);
+}
+
+static int
+rmnet_smd_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+	struct rmnet_smd_dev *dev = container_of(f, struct rmnet_smd_dev,
+								function);
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct usb_request	*req = cdev->req;
+	int			ret = -EOPNOTSUPP;
+	u16			w_index = le16_to_cpu(ctrl->wIndex);
+	u16			w_value = le16_to_cpu(ctrl->wValue);
+	u16			w_length = le16_to_cpu(ctrl->wLength);
+	struct qmi_buf *resp;
+	int schedule = 0;
+
+	if (!atomic_read(&dev->online))
+		return -ENOTCONN;
+
+	switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_SEND_ENCAPSULATED_COMMAND:
+		ret = w_length;
+		req->complete = rmnet_smd_command_complete;
+		req->context = dev;
+		break;
+
+
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_GET_ENCAPSULATED_RESPONSE:
+		if (w_value)
+			goto invalid;
+		else {
+			spin_lock(&dev->lock);
+			if (list_empty(&dev->qmi_resp_q)) {
+				INFO(cdev, "qmi resp empty "
+					" req%02x.%02x v%04x i%04x l%d\n",
+					ctrl->bRequestType, ctrl->bRequest,
+					w_value, w_index, w_length);
+				spin_unlock(&dev->lock);
+				goto invalid;
+			}
+			resp = list_first_entry(&dev->qmi_resp_q,
+					struct qmi_buf, list);
+			list_del(&resp->list);
+			spin_unlock(&dev->lock);
+			memcpy(req->buf, resp->buf, resp->len);
+			ret = resp->len;
+			spin_lock(&dev->lock);
+
+			if (list_empty(&dev->qmi_resp_pool))
+				schedule = 1;
+			list_add_tail(&resp->list, &dev->qmi_resp_pool);
+
+			if (schedule)
+				tasklet_schedule(&dev->smd_ctl.tx_tlet);
+			spin_unlock(&dev->lock);
+			req->complete = rmnet_txcommand_complete;
+			req->context = dev;
+		}
+		break;
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_REQ_SET_CONTROL_LINE_STATE:
+		/* This is a workaround for RmNet and is borrowed from the
+		 * CDC/ACM standard. The host driver will issue the above ACM
+		 * standard request to the RmNet interface in the following
+		 * scenario: Once the network adapter is disabled from device
+		 * manager, the above request will be sent from the qcusbnet
+		 * host driver, with DTR being '0'. Once network adapter is
+		 * enabled from device manager (or during enumeration), the
+		 * request will be sent with DTR being '1'.
+		 */
+		if (w_value & RMNET_SMD_ACM_CTRL_DTR)
+			ret = smd_tiocmset(dev->smd_ctl.ch, TIOCM_DTR, 0);
+		else
+			ret = smd_tiocmset(dev->smd_ctl.ch, 0, TIOCM_DTR);
+
+		break;
+	default:
+
+invalid:
+		DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+	}
+
+	/* respond with data transfer or status phase? */
+	if (ret >= 0) {
+		VDBG(cdev, "rmnet req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+		req->zero = 0;
+		req->length = ret;
+		ret = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+		if (ret < 0)
+			ERROR(cdev, "rmnet ep0 enqueue err %d\n", ret);
+	}
+
+	return ret;
+}
+
+static void rmnet_smd_start_rx(struct rmnet_smd_dev *dev)
+{
+	struct usb_composite_dev *cdev = dev->cdev;
+	int status;
+	struct usb_request *req;
+	struct list_head *pool = &dev->rx_idle;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	while (!list_empty(pool)) {
+		req = list_entry(pool->next, struct usb_request, list);
+		list_del(&req->list);
+
+		spin_unlock_irqrestore(&dev->lock, flags);
+		status = usb_ep_queue(dev->epout, req, GFP_ATOMIC);
+		spin_lock_irqsave(&dev->lock, flags);
+
+		if (status) {
+			ERROR(cdev, "rmnet data rx enqueue err %d\n", status);
+			list_add_tail(&req->list, pool);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static void rmnet_data_tx_tlet(unsigned long arg)
+{
+	struct rmnet_smd_dev *dev = (struct rmnet_smd_dev *) arg;
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct usb_request *req;
+	int status;
+	int sz;
+	unsigned long flags;
+
+	while (1) {
+
+		sz = smd_cur_packet_size(dev->smd_data.ch);
+		if (sz == 0)
+			break;
+		if (smd_read_avail(dev->smd_data.ch) < sz)
+			break;
+
+		spin_lock_irqsave(&dev->lock, flags);
+		if (list_empty(&dev->tx_idle)) {
+			spin_unlock_irqrestore(&dev->lock, flags);
+			DBG(cdev, "rmnet data Tx buffers full\n");
+			break;
+		}
+		req = list_first_entry(&dev->tx_idle, struct usb_request, list);
+		list_del(&req->list);
+		spin_unlock_irqrestore(&dev->lock, flags);
+
+		req->length = smd_read(dev->smd_data.ch, req->buf, sz);
+		status = usb_ep_queue(dev->epin, req, GFP_ATOMIC);
+		if (status) {
+			ERROR(cdev, "rmnet tx data enqueue err %d\n", status);
+			spin_lock_irqsave(&dev->lock, flags);
+			list_add_tail(&req->list, &dev->tx_idle);
+			spin_unlock_irqrestore(&dev->lock, flags);
+			break;
+		}
+		spin_lock_irqsave(&dev->lock, flags);
+		dev->dpkts_from_modem++;
+		spin_unlock_irqrestore(&dev->lock, flags);
+	}
+
+}
+
+static void rmnet_data_rx_tlet(unsigned long arg)
+{
+	struct rmnet_smd_dev *dev = (struct rmnet_smd_dev *) arg;
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct usb_request *req;
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	while (1) {
+		if (list_empty(&dev->rx_queue)) {
+			atomic_set(&dev->smd_data.rx_pkt, 0);
+			break;
+		}
+		req = list_first_entry(&dev->rx_queue,
+			struct usb_request, list);
+		if (smd_write_avail(dev->smd_data.ch) < req->actual) {
+			atomic_set(&dev->smd_data.rx_pkt, req->actual);
+			DBG(cdev, "rmnet SMD data channel full\n");
+			break;
+		}
+
+		list_del(&req->list);
+		spin_unlock_irqrestore(&dev->lock, flags);
+		ret = smd_write(dev->smd_data.ch, req->buf, req->actual);
+		spin_lock_irqsave(&dev->lock, flags);
+		if (ret != req->actual) {
+			ERROR(cdev, "rmnet SMD data write failed\n");
+			break;
+		}
+		dev->dpkts_to_modem++;
+		list_add_tail(&req->list, &dev->rx_idle);
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	/* We have free rx data requests. */
+	rmnet_smd_start_rx(dev);
+}
+
+/* If SMD has enough room to accommodate a data rx packet,
+ * write into SMD directly. Otherwise enqueue to rx_queue.
+ * We will not write into SMD directly untill rx_queue is
+ * empty to strictly follow the ordering requests.
+ */
+static void rmnet_smd_complete_epout(struct usb_ep *ep, struct usb_request *req)
+{
+	struct rmnet_smd_dev *dev = req->context;
+	struct usb_composite_dev *cdev = dev->cdev;
+	int status = req->status;
+	int ret;
+
+	switch (status) {
+	case 0:
+		/* normal completion */
+		break;
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		spin_lock(&dev->lock);
+		list_add_tail(&req->list, &dev->rx_idle);
+		spin_unlock(&dev->lock);
+		return;
+	default:
+		/* unexpected failure */
+		ERROR(cdev, "RMNET %s response error %d, %d/%d\n",
+			ep->name, status,
+			req->actual, req->length);
+		spin_lock(&dev->lock);
+		list_add_tail(&req->list, &dev->rx_idle);
+		spin_unlock(&dev->lock);
+		return;
+	}
+
+	spin_lock(&dev->lock);
+	dev->dpkts_from_host++;
+	if (!atomic_read(&dev->smd_data.rx_pkt)) {
+		if (smd_write_avail(dev->smd_data.ch) < req->actual) {
+			atomic_set(&dev->smd_data.rx_pkt, req->actual);
+			goto queue_req;
+		}
+		spin_unlock(&dev->lock);
+		ret = smd_write(dev->smd_data.ch, req->buf, req->actual);
+		/* This should never happen */
+		if (ret != req->actual)
+			ERROR(cdev, "rmnet data smd write failed\n");
+		/* Restart Rx */
+		spin_lock(&dev->lock);
+		dev->dpkts_to_modem++;
+		list_add_tail(&req->list, &dev->rx_idle);
+		spin_unlock(&dev->lock);
+		rmnet_smd_start_rx(dev);
+		return;
+	}
+queue_req:
+	list_add_tail(&req->list, &dev->rx_queue);
+	spin_unlock(&dev->lock);
+}
+
+static void rmnet_smd_complete_epin(struct usb_ep *ep, struct usb_request *req)
+{
+	struct rmnet_smd_dev *dev = req->context;
+	struct usb_composite_dev *cdev = dev->cdev;
+	int status = req->status;
+	int schedule = 0;
+
+	switch (status) {
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		spin_lock(&dev->lock);
+		list_add_tail(&req->list, &dev->tx_idle);
+		spin_unlock(&dev->lock);
+		break;
+	default:
+		ERROR(cdev, "rmnet data tx ep error %d\n", status);
+		/* FALLTHROUGH */
+	case 0:
+		spin_lock(&dev->lock);
+		if (list_empty(&dev->tx_idle))
+			schedule = 1;
+		list_add_tail(&req->list, &dev->tx_idle);
+		dev->dpkts_to_host++;
+		if (schedule)
+			tasklet_schedule(&dev->smd_data.tx_tlet);
+		spin_unlock(&dev->lock);
+		break;
+	}
+
+}
+
+static void rmnet_smd_disconnect_work(struct work_struct *w)
+{
+	struct qmi_buf *qmi;
+	struct usb_request *req;
+	struct list_head *act, *tmp;
+	struct rmnet_smd_dev *dev = container_of(w, struct rmnet_smd_dev,
+					disconnect_work);
+
+	tasklet_kill(&dev->smd_ctl.rx_tlet);
+	tasklet_kill(&dev->smd_ctl.tx_tlet);
+	tasklet_kill(&dev->smd_data.rx_tlet);
+	tasklet_kill(&dev->smd_data.tx_tlet);
+
+	smd_close(dev->smd_ctl.ch);
+	dev->smd_ctl.flags = 0;
+
+	smd_close(dev->smd_data.ch);
+	dev->smd_data.flags = 0;
+
+	atomic_set(&dev->notify_count, 0);
+
+	list_for_each_safe(act, tmp, &dev->rx_queue) {
+		req = list_entry(act, struct usb_request, list);
+		list_del(&req->list);
+		list_add_tail(&req->list, &dev->rx_idle);
+	}
+
+	list_for_each_safe(act, tmp, &dev->qmi_req_q) {
+		qmi = list_entry(act, struct qmi_buf, list);
+		list_del(&qmi->list);
+		list_add_tail(&qmi->list, &dev->qmi_req_pool);
+	}
+
+	list_for_each_safe(act, tmp, &dev->qmi_resp_q) {
+		qmi = list_entry(act, struct qmi_buf, list);
+		list_del(&qmi->list);
+		list_add_tail(&qmi->list, &dev->qmi_resp_pool);
+	}
+
+	if (dev->is_pdrv_used) {
+		platform_driver_unregister(&dev->pdrv);
+		dev->is_pdrv_used = 0;
+	}
+}
+
+/* SMD close may sleep
+ * schedule a work to close smd channels
+ */
+static void rmnet_smd_disable(struct usb_function *f)
+{
+	struct rmnet_smd_dev *dev = container_of(f, struct rmnet_smd_dev,
+								function);
+
+	if (!atomic_read(&dev->online))
+		return;
+
+	atomic_set(&dev->online, 0);
+
+	usb_ep_fifo_flush(dev->epnotify);
+	usb_ep_disable(dev->epnotify);
+	usb_ep_fifo_flush(dev->epout);
+	usb_ep_disable(dev->epout);
+
+	usb_ep_fifo_flush(dev->epin);
+	usb_ep_disable(dev->epin);
+
+	/* cleanup work */
+	queue_work(dev->wq, &dev->disconnect_work);
+}
+
+static void rmnet_smd_connect_work(struct work_struct *w)
+{
+	struct rmnet_smd_dev *dev = container_of(w, struct rmnet_smd_dev,
+								connect_work);
+	struct usb_composite_dev *cdev = dev->cdev;
+	int ret = 0;
+
+	/* Control channel for QMI messages */
+	ret = smd_open(rmnet_ctl_ch, &dev->smd_ctl.ch,
+			&dev->smd_ctl, rmnet_smd_event_notify);
+	if (ret) {
+		ERROR(cdev, "Unable to open control smd channel: %d\n", ret);
+		/*
+		 * Register platform driver to be notified in case SMD channels
+		 * later becomes ready to be opened.
+		 */
+		ret = platform_driver_register(&dev->pdrv);
+		if (ret)
+			ERROR(cdev, "Platform driver %s register failed %d\n",
+					dev->pdrv.driver.name, ret);
+		else
+			dev->is_pdrv_used = 1;
+
+		return;
+	}
+	wait_event(dev->smd_ctl.wait, test_bit(CH_OPENED,
+				&dev->smd_ctl.flags));
+
+	/* Data channel for network packets */
+	ret = smd_open(rmnet_data_ch, &dev->smd_data.ch,
+			&dev->smd_data, rmnet_smd_event_notify);
+	if (ret) {
+		ERROR(cdev, "Unable to open data smd channel\n");
+		smd_close(dev->smd_ctl.ch);
+		return;
+	}
+	wait_event(dev->smd_data.wait, test_bit(CH_OPENED,
+				&dev->smd_data.flags));
+
+	atomic_set(&dev->online, 1);
+	/* Queue Rx data requests */
+	rmnet_smd_start_rx(dev);
+}
+
+static int rmnet_smd_ch_probe(struct platform_device *pdev)
+{
+	DBG(rmnet_smd->cdev, "Probe called for device: %s\n", pdev->name);
+
+	queue_work(rmnet_smd->wq, &rmnet_smd->connect_work);
+
+	return 0;
+}
+
+/* SMD open may sleep.
+ * Schedule a work to open smd channels and enable
+ * endpoints if smd channels are opened successfully.
+ */
+static int rmnet_smd_set_alt(struct usb_function *f,
+		unsigned intf, unsigned alt)
+{
+	struct rmnet_smd_dev *dev = container_of(f, struct rmnet_smd_dev,
+								function);
+	struct usb_composite_dev *cdev = dev->cdev;
+	int ret = 0;
+
+	/* Enable epin endpoint */
+	ret = config_ep_by_speed(cdev->gadget, f, dev->epin);
+	if (ret) {
+		dev->epin->desc = NULL;
+		ERROR(cdev, "config_ep_by_speed failed for ep %s, result %d\n",
+			dev->epin->name, ret);
+		return ret;
+	}
+	ret = usb_ep_enable(dev->epin);
+	if (ret) {
+		ERROR(cdev, "can't enable %s, result %d\n",
+					dev->epin->name, ret);
+		return ret;
+	}
+
+	/* Enable epout endpoint */
+	ret = config_ep_by_speed(cdev->gadget, f, dev->epout);
+	if (ret) {
+		dev->epout->desc = NULL;
+		ERROR(cdev, "config_ep_by_speed failed for ep %s, result %d\n",
+					dev->epout->name, ret);
+		usb_ep_disable(dev->epin);
+		return ret;
+	}
+	ret = usb_ep_enable(dev->epout);
+
+	if (ret) {
+		ERROR(cdev, "can't enable %s, result %d\n",
+					dev->epout->name, ret);
+		usb_ep_disable(dev->epin);
+		return ret;
+	}
+
+	/* Enable epnotify endpoint */
+	ret = config_ep_by_speed(cdev->gadget, f, dev->epnotify);
+	if (ret) {
+		dev->epnotify->desc = NULL;
+		ERROR(cdev, "config_ep_by_speed failed for ep %s, result %d\n",
+			dev->epnotify->name, ret);
+		usb_ep_disable(dev->epin);
+		usb_ep_disable(dev->epout);
+		return ret;
+	}
+	ret = usb_ep_enable(dev->epnotify);
+	if (ret) {
+		ERROR(cdev, "can't enable %s, result %d\n",
+					dev->epnotify->name, ret);
+		usb_ep_disable(dev->epin);
+		usb_ep_disable(dev->epout);
+		return ret;
+	}
+
+	queue_work(dev->wq, &dev->connect_work);
+	return 0;
+}
+
+static void rmnet_smd_free_buf(struct rmnet_smd_dev *dev)
+{
+	struct qmi_buf *qmi;
+	struct usb_request *req;
+	struct list_head *act, *tmp;
+
+	dev->dpkts_to_host = 0;
+	dev->dpkts_from_modem = 0;
+	dev->dpkts_from_host = 0;
+	dev->dpkts_to_modem = 0;
+
+	dev->cpkts_to_host = 0;
+	dev->cpkts_from_modem = 0;
+	dev->cpkts_from_host = 0;
+	dev->cpkts_to_modem = 0;
+	/* free all usb requests in tx pool */
+	list_for_each_safe(act, tmp, &dev->tx_idle) {
+		req = list_entry(act, struct usb_request, list);
+		list_del(&req->list);
+		rmnet_smd_free_req(dev->epout, req);
+	}
+
+	/* free all usb requests in rx pool */
+	list_for_each_safe(act, tmp, &dev->rx_idle) {
+		req = list_entry(act, struct usb_request, list);
+		list_del(&req->list);
+		rmnet_smd_free_req(dev->epin, req);
+	}
+
+	/* free all buffers in qmi request pool */
+	list_for_each_safe(act, tmp, &dev->qmi_req_pool) {
+		qmi = list_entry(act, struct qmi_buf, list);
+		list_del(&qmi->list);
+		rmnet_smd_free_qmi(qmi);
+	}
+
+	/* free all buffers in qmi request pool */
+	list_for_each_safe(act, tmp, &dev->qmi_resp_pool) {
+		qmi = list_entry(act, struct qmi_buf, list);
+		list_del(&qmi->list);
+		rmnet_smd_free_qmi(qmi);
+	}
+
+	rmnet_smd_free_req(dev->epnotify, dev->notify_req);
+}
+static int rmnet_smd_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = c->cdev;
+	struct rmnet_smd_dev *dev = container_of(f, struct rmnet_smd_dev,
+								function);
+	int i, id, ret;
+	struct qmi_buf *qmi;
+	struct usb_request *req;
+	struct usb_ep *ep;
+
+	dev->cdev = cdev;
+
+	/* allocate interface ID */
+	id = usb_interface_id(c, f);
+	if (id < 0)
+		return id;
+	dev->ifc_id = id;
+	rmnet_smd_interface_desc.bInterfaceNumber = id;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &rmnet_smd_fs_in_desc);
+	if (!ep)
+		return -ENODEV;
+	ep->driver_data = cdev; /* claim endpoint */
+	dev->epin = ep;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &rmnet_smd_fs_out_desc);
+	if (!ep)
+		return -ENODEV;
+	ep->driver_data = cdev; /* claim endpoint */
+	dev->epout = ep;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &rmnet_smd_fs_notify_desc);
+	if (!ep)
+		return -ENODEV;
+	ep->driver_data = cdev; /* clain endpoint */
+	dev->epnotify = ep;
+
+	/* support all relevant hardware speeds... we expect that when
+	 * hardware is dual speed, all bulk-capable endpoints work at
+	 * both speeds
+	 */
+	if (gadget_is_dualspeed(c->cdev->gadget)) {
+		rmnet_smd_hs_in_desc.bEndpointAddress =
+				rmnet_smd_fs_in_desc.bEndpointAddress;
+		rmnet_smd_hs_out_desc.bEndpointAddress =
+				rmnet_smd_fs_out_desc.bEndpointAddress;
+		rmnet_smd_hs_notify_desc.bEndpointAddress =
+				rmnet_smd_fs_notify_desc.bEndpointAddress;
+
+	}
+
+	/* allocate notification */
+	dev->notify_req = rmnet_smd_alloc_req(dev->epnotify,
+					RMNET_SMD_MAX_NOTIFY_SIZE, GFP_KERNEL);
+	if (IS_ERR(dev->notify_req))
+		return PTR_ERR(dev->notify_req);
+
+	dev->notify_req->complete = rmnet_smd_notify_complete;
+	dev->notify_req->context = dev;
+	dev->notify_req->length = RMNET_SMD_MAX_NOTIFY_SIZE;
+
+	/* Allocate the qmi request and response buffers */
+	for (i = 0; i < QMI_REQ_MAX; i++) {
+		qmi = rmnet_smd_alloc_qmi(QMI_REQ_SIZE, GFP_KERNEL);
+		if (IS_ERR(qmi)) {
+			ret = PTR_ERR(qmi);
+			goto free_buf;
+		}
+		list_add_tail(&qmi->list, &dev->qmi_req_pool);
+	}
+
+	for (i = 0; i < QMI_RESP_MAX; i++) {
+		qmi = rmnet_smd_alloc_qmi(QMI_RESP_SIZE, GFP_KERNEL);
+		if (IS_ERR(qmi)) {
+			ret = PTR_ERR(qmi);
+			goto free_buf;
+		}
+		list_add_tail(&qmi->list, &dev->qmi_resp_pool);
+	}
+
+	/* Allocate bulk in/out requests for data transfer */
+	for (i = 0; i < RMNET_RX_REQ_MAX; i++) {
+		req = rmnet_smd_alloc_req(dev->epout, RMNET_RX_REQ_SIZE,
+								 GFP_KERNEL);
+		if (IS_ERR(req)) {
+			ret = PTR_ERR(req);
+			goto free_buf;
+		}
+		req->length = RMNET_TXN_MAX;
+		req->context = dev;
+		req->complete = rmnet_smd_complete_epout;
+		list_add_tail(&req->list, &dev->rx_idle);
+	}
+
+	for (i = 0; i < RMNET_TX_REQ_MAX; i++) {
+		req = rmnet_smd_alloc_req(dev->epin, RMNET_TX_REQ_SIZE,
+							GFP_KERNEL);
+		if (IS_ERR(req)) {
+			ret = PTR_ERR(req);
+			goto free_buf;
+		}
+		req->context = dev;
+		req->complete = rmnet_smd_complete_epin;
+		list_add_tail(&req->list, &dev->tx_idle);
+	}
+
+	return 0;
+
+free_buf:
+	rmnet_smd_free_buf(dev);
+	dev->epout = dev->epin = dev->epnotify = NULL; /* release endpoints */
+	return ret;
+}
+
+#if defined(CONFIG_DEBUG_FS)
+static ssize_t rmnet_smd_debug_read_stats(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	struct rmnet_smd_dev *dev = file->private_data;
+	struct rmnet_smd_ch_info smd_ctl_info = dev->smd_ctl;
+	struct rmnet_smd_ch_info smd_data_info = dev->smd_data;
+	char *buf;
+	unsigned long flags;
+	int ret;
+
+	buf = kzalloc(sizeof(char) * 512, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	ret = scnprintf(buf, 512,
+			"smd_control_ch_opened: %lu\n"
+			"smd_data_ch_opened: %lu\n"
+			"usb online : %d\n"
+			"dpkts_from_modem: %lu\n"
+			"dpkts_to_host: %lu\n"
+			"pending_dpkts_to_host: %lu\n"
+			"dpkts_from_host: %lu\n"
+			"dpkts_to_modem: %lu\n"
+			"pending_dpkts_to_modem: %lu\n"
+			"cpkts_from_modem: %lu\n"
+			"cpkts_to_host: %lu\n"
+			"pending_cpkts_to_host: %lu\n"
+			"cpkts_from_host: %lu\n"
+			"cpkts_to_modem: %lu\n"
+			"pending_cpkts_to_modem: %lu\n"
+			"smd_read_avail_ctrl: %d\n"
+			"smd_write_avail_ctrl: %d\n"
+			"smd_read_avail_data: %d\n"
+			"smd_write_avail_data: %d\n",
+			smd_ctl_info.flags, smd_data_info.flags,
+			atomic_read(&dev->online),
+			dev->dpkts_from_modem, dev->dpkts_to_host,
+			(dev->dpkts_from_modem - dev->dpkts_to_host),
+			dev->dpkts_from_host, dev->dpkts_to_modem,
+			(dev->dpkts_from_host - dev->dpkts_to_modem),
+			dev->cpkts_from_modem, dev->cpkts_to_host,
+			(dev->cpkts_from_modem - dev->cpkts_to_host),
+			dev->cpkts_from_host, dev->cpkts_to_modem,
+			(dev->cpkts_from_host - dev->cpkts_to_modem),
+			smd_read_avail(dev->smd_ctl.ch),
+			smd_write_avail(dev->smd_ctl.ch),
+			smd_read_avail(dev->smd_data.ch),
+			smd_write_avail(dev->smd_data.ch));
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, ret);
+
+	kfree(buf);
+
+	return ret;
+}
+
+static ssize_t rmnet_smd_debug_reset_stats(struct file *file,
+					const char __user *buf,
+					size_t count, loff_t *ppos)
+{
+	struct rmnet_smd_dev *dev = file->private_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	dev->dpkts_to_host = 0;
+	dev->dpkts_from_modem = 0;
+	dev->dpkts_from_host = 0;
+	dev->dpkts_to_modem = 0;
+
+	dev->cpkts_to_host = 0;
+	dev->cpkts_from_modem = 0;
+	dev->cpkts_from_host = 0;
+	dev->cpkts_to_modem = 0;
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return count;
+}
+
+static int rmnet_smd_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+
+	return 0;
+}
+
+const struct file_operations rmnet_smd_debug_stats_ops = {
+	.open = rmnet_smd_debug_open,
+	.read = rmnet_smd_debug_read_stats,
+	.write = rmnet_smd_debug_reset_stats,
+};
+
+struct dentry *dent_smd;
+struct dentry *dent_smd_status;
+
+static void rmnet_smd_debugfs_init(struct rmnet_smd_dev *dev)
+{
+
+	dent_smd = debugfs_create_dir("usb_rmnet_smd", 0);
+	if (IS_ERR(dent_smd))
+		return;
+
+	dent_smd_status = debugfs_create_file("status", 0444, dent_smd, dev,
+			&rmnet_smd_debug_stats_ops);
+
+	if (!dent_smd_status) {
+		debugfs_remove(dent_smd);
+		dent_smd = NULL;
+		return;
+	}
+
+	return;
+}
+#else
+static void rmnet_smd_debugfs_init(struct rmnet_smd_dev *dev) {}
+#endif
+
+static void
+rmnet_smd_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct rmnet_smd_dev *dev = container_of(f, struct rmnet_smd_dev,
+								function);
+
+	tasklet_kill(&dev->smd_ctl.rx_tlet);
+	tasklet_kill(&dev->smd_ctl.tx_tlet);
+	tasklet_kill(&dev->smd_data.rx_tlet);
+	tasklet_kill(&dev->smd_data.tx_tlet);
+
+	flush_workqueue(dev->wq);
+	rmnet_smd_free_buf(dev);
+	dev->epout = dev->epin = dev->epnotify = NULL; /* release endpoints */
+
+	destroy_workqueue(dev->wq);
+	debugfs_remove_recursive(dent_smd);
+	kfree(dev);
+
+}
+
+int rmnet_smd_bind_config(struct usb_configuration *c)
+{
+	struct rmnet_smd_dev *dev;
+	int ret;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	rmnet_smd = dev;
+
+	dev->wq = create_singlethread_workqueue("k_rmnet_work");
+	if (!dev->wq) {
+		ret = -ENOMEM;
+		goto free_dev;
+	}
+
+	spin_lock_init(&dev->lock);
+	atomic_set(&dev->notify_count, 0);
+	atomic_set(&dev->online, 0);
+	atomic_set(&dev->smd_ctl.rx_pkt, 0);
+	atomic_set(&dev->smd_data.rx_pkt, 0);
+
+	INIT_WORK(&dev->connect_work, rmnet_smd_connect_work);
+	INIT_WORK(&dev->disconnect_work, rmnet_smd_disconnect_work);
+
+	tasklet_init(&dev->smd_ctl.rx_tlet, rmnet_control_rx_tlet,
+					(unsigned long) dev);
+	tasklet_init(&dev->smd_ctl.tx_tlet, rmnet_control_tx_tlet,
+					(unsigned long) dev);
+	tasklet_init(&dev->smd_data.rx_tlet, rmnet_data_rx_tlet,
+					(unsigned long) dev);
+	tasklet_init(&dev->smd_data.tx_tlet, rmnet_data_tx_tlet,
+					(unsigned long) dev);
+
+	init_waitqueue_head(&dev->smd_ctl.wait);
+	init_waitqueue_head(&dev->smd_data.wait);
+
+	dev->pdrv.probe = rmnet_smd_ch_probe;
+	dev->pdrv.driver.name = CONFIG_RMNET_SMD_CTL_CHANNEL;
+	dev->pdrv.driver.owner = THIS_MODULE;
+
+	INIT_LIST_HEAD(&dev->qmi_req_pool);
+	INIT_LIST_HEAD(&dev->qmi_req_q);
+	INIT_LIST_HEAD(&dev->qmi_resp_pool);
+	INIT_LIST_HEAD(&dev->qmi_resp_q);
+	INIT_LIST_HEAD(&dev->rx_idle);
+	INIT_LIST_HEAD(&dev->rx_queue);
+	INIT_LIST_HEAD(&dev->tx_idle);
+
+	dev->function.name = "rmnet";
+	dev->function.strings = rmnet_smd_strings;
+	dev->function.descriptors = rmnet_smd_fs_function;
+	dev->function.hs_descriptors = rmnet_smd_hs_function;
+	dev->function.bind = rmnet_smd_bind;
+	dev->function.unbind = rmnet_smd_unbind;
+	dev->function.setup = rmnet_smd_setup;
+	dev->function.set_alt = rmnet_smd_set_alt;
+	dev->function.disable = rmnet_smd_disable;
+
+	ret = usb_add_function(c, &dev->function);
+	if (ret)
+		goto free_wq;
+
+	rmnet_smd_debugfs_init(dev);
+
+	return 0;
+
+free_wq:
+	destroy_workqueue(dev->wq);
+free_dev:
+	kfree(dev);
+
+	return ret;
+}
diff --git a/drivers/usb/gadget/f_rmnet_smd_sdio.c b/drivers/usb/gadget/f_rmnet_smd_sdio.c
new file mode 100644
index 0000000..175afe3
--- /dev/null
+++ b/drivers/usb/gadget/f_rmnet_smd_sdio.c
@@ -0,0 +1,2045 @@
+/*
+ * f_rmnet_smd_sdio.c -- RmNet SMD & SDIO function driver
+ *
+ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
+ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
+ * Copyright (C) 2008 Nokia Corporation
+ * Copyright (c) 2011 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/workqueue.h>
+#include <linux/netdevice.h>
+#include <linux/interrupt.h>
+#include <linux/ratelimit.h>
+
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <asm/ioctls.h>
+
+#include <linux/usb/cdc.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/ch9.h>
+#include <linux/termios.h>
+#include <linux/debugfs.h>
+
+#include <mach/msm_smd.h>
+#include <mach/sdio_cmux.h>
+#include <mach/sdio_dmux.h>
+#include <mach/usb_gadget_xport.h>
+
+#ifdef CONFIG_RMNET_SMD_SDIO_CTL_CHANNEL
+static uint32_t rmnet_mux_sdio_ctl_ch = CONFIG_RMNET_SMD_SDIO_CTL_CHANNEL;
+#else
+static uint32_t rmnet_mux_sdio_ctl_ch;
+#endif
+module_param(rmnet_mux_sdio_ctl_ch, uint, S_IRUGO);
+MODULE_PARM_DESC(rmnet_mux_sdio_ctl_ch, "RmNetMUX control SDIO channel ID");
+
+#ifdef CONFIG_RMNET_SMD_SDIO_DATA_CHANNEL
+static uint32_t rmnet_mux_sdio_data_ch = CONFIG_RMNET_SMD_SDIO_DATA_CHANNEL;
+#else
+static uint32_t rmnet_mux_sdio_data_ch;
+#endif
+module_param(rmnet_mux_sdio_data_ch, uint, S_IRUGO);
+MODULE_PARM_DESC(rmnet_mux_sdio_data_ch, "RmNetMUX data SDIO channel ID");
+
+#ifdef CONFIG_RMNET_SDIO_SMD_DATA_CHANNEL
+static char *rmnet_mux_smd_data_ch = CONFIG_RMNET_SDIO_SMD_DATA_CHANNEL;
+#else
+static char *rmnet_mux_smd_data_ch;
+#endif
+module_param(rmnet_mux_smd_data_ch, charp, S_IRUGO);
+MODULE_PARM_DESC(rmnet_mux_smd_data_ch, "RmNetMUX data SMD channel");
+
+#define RMNET_MUX_ACM_CTRL_DTR			(1 << 0)
+
+#define RMNET_MUX_SDIO_HDR			8
+#define RMNET_MUX_SDIO_NOTIFY_INTERVAL		5
+#define RMNET_MUX_SDIO_MAX_NFY_SZE	sizeof(struct usb_cdc_notification)
+
+#define RMNET_MUX_SDIO_RX_REQ_MAX		16
+#define RMNET_MUX_SDIO_RX_REQ_SIZE		2048
+#define RMNET_MUX_SDIO_TX_REQ_MAX		100
+
+#define RMNET_MUX_SDIO_TX_LIMIT			1000
+#define RMNET_MUX_SDIO_RX_ENABLE_LIMIT		1000
+#define RMNET_MUX_SDIO_RX_DISABLE_LIMIT		500
+
+static uint32_t mux_sdio_tx_pkt_drop_thld = RMNET_MUX_SDIO_TX_LIMIT;
+module_param(mux_sdio_tx_pkt_drop_thld, uint, S_IRUGO | S_IWUSR);
+
+static uint32_t mux_sdio_rx_fctrl_en_thld =
+		RMNET_MUX_SDIO_RX_ENABLE_LIMIT;
+module_param(mux_sdio_rx_fctrl_en_thld, uint, S_IRUGO | S_IWUSR);
+
+static uint32_t mux_sdio_rx_fctrl_dis_thld = RMNET_MUX_SDIO_RX_DISABLE_LIMIT;
+module_param(mux_sdio_rx_fctrl_dis_thld, uint, S_IRUGO | S_IWUSR);
+
+
+#define RMNET_MUX_SMD_RX_REQ_MAX		8
+#define RMNET_MUX_SMD_RX_REQ_SIZE		2048
+#define RMNET_MUX_SMD_TX_REQ_MAX		8
+#define RMNET_MUX_SMD_TX_REQ_SIZE		2048
+#define RMNET_MUX_SMD_TXN_MAX			2048
+
+struct rmnet_mux_ctrl_pkt {
+	void *buf;
+	int len;
+	struct list_head list;
+};
+
+struct rmnet_mux_ctrl_dev {
+	struct list_head tx_q;
+	wait_queue_head_t tx_wait_q;
+	unsigned long tx_len;
+
+	struct list_head rx_q;
+	unsigned long rx_len;
+
+	unsigned long cbits_to_modem;
+
+	unsigned	opened;
+};
+
+struct rmnet_mux_sdio_dev {
+	/* Tx/Rx lists */
+	struct list_head tx_idle;
+	struct sk_buff_head    tx_skb_queue;
+	struct list_head rx_idle;
+	struct sk_buff_head    rx_skb_queue;
+
+
+
+	struct work_struct data_rx_work;
+
+	struct delayed_work open_work;
+	atomic_t sdio_open;
+
+	unsigned int dpkts_pending_atdmux;
+};
+
+/* Data SMD channel */
+struct rmnet_mux_smd_info {
+	struct smd_channel *ch;
+	struct tasklet_struct tx_tlet;
+	struct tasklet_struct rx_tlet;
+#define RMNET_MUX_CH_OPENED 0
+	unsigned long flags;
+	/* pending rx packet length */
+	atomic_t rx_pkt;
+	/* wait for smd open event*/
+	wait_queue_head_t wait;
+};
+
+struct rmnet_mux_smd_dev {
+	/* Tx/Rx lists */
+	struct list_head tx_idle;
+	struct list_head rx_idle;
+	struct list_head rx_queue;
+
+	struct rmnet_mux_smd_info smd_data;
+};
+
+struct rmnet_mux_dev {
+	struct usb_function function;
+	struct usb_composite_dev *cdev;
+
+	struct usb_ep *epout;
+	struct usb_ep *epin;
+	struct usb_ep *epnotify;
+	struct usb_request *notify_req;
+
+	struct rmnet_mux_smd_dev smd_dev;
+	struct rmnet_mux_sdio_dev sdio_dev;
+	struct rmnet_mux_ctrl_dev ctrl_dev;
+
+	u8 ifc_id;
+	enum transport_type xport;
+	spinlock_t lock;
+	atomic_t online;
+	atomic_t notify_count;
+	struct workqueue_struct *wq;
+	struct work_struct disconnect_work;
+
+	/* pkt counters */
+	unsigned long dpkts_tomsm;
+	unsigned long dpkts_tomdm;
+	unsigned long dpkts_tolaptop;
+	unsigned long tx_drp_cnt;
+	unsigned long cpkts_tolaptop;
+	unsigned long cpkts_tomdm;
+	unsigned long cpkts_drp_cnt;
+};
+
+static struct rmnet_mux_dev *rmux_dev;
+
+static struct usb_interface_descriptor rmnet_mux_interface_desc = {
+	.bLength =              USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =      USB_DT_INTERFACE,
+	.bNumEndpoints =        3,
+	.bInterfaceClass =      USB_CLASS_VENDOR_SPEC,
+	.bInterfaceSubClass =   USB_CLASS_VENDOR_SPEC,
+	.bInterfaceProtocol =   USB_CLASS_VENDOR_SPEC,
+};
+
+/* Full speed support */
+static struct usb_endpoint_descriptor rmnet_mux_fs_notify_desc = {
+	.bLength =              USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =      USB_DT_ENDPOINT,
+	.bEndpointAddress =     USB_DIR_IN,
+	.bmAttributes =         USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	__constant_cpu_to_le16(
+						RMNET_MUX_SDIO_MAX_NFY_SZE),
+	.bInterval =            1 << RMNET_MUX_SDIO_NOTIFY_INTERVAL,
+};
+
+static struct usb_endpoint_descriptor rmnet_mux_fs_in_desc  = {
+	.bLength =              USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =      USB_DT_ENDPOINT,
+	.bEndpointAddress =     USB_DIR_IN,
+	.bmAttributes =         USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize   = __constant_cpu_to_le16(64),
+};
+
+static struct usb_endpoint_descriptor rmnet_mux_fs_out_desc = {
+	.bLength =              USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =      USB_DT_ENDPOINT,
+	.bEndpointAddress =     USB_DIR_OUT,
+	.bmAttributes =         USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize = __constant_cpu_to_le16(64),
+};
+
+static struct usb_descriptor_header *rmnet_mux_fs_function[] = {
+	(struct usb_descriptor_header *) &rmnet_mux_interface_desc,
+	(struct usb_descriptor_header *) &rmnet_mux_fs_notify_desc,
+	(struct usb_descriptor_header *) &rmnet_mux_fs_in_desc,
+	(struct usb_descriptor_header *) &rmnet_mux_fs_out_desc,
+	NULL,
+};
+
+/* High speed support */
+static struct usb_endpoint_descriptor rmnet_mux_hs_notify_desc  = {
+	.bLength =              USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =      USB_DT_ENDPOINT,
+	.bEndpointAddress =     USB_DIR_IN,
+	.bmAttributes =         USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	__constant_cpu_to_le16(
+						RMNET_MUX_SDIO_MAX_NFY_SZE),
+	.bInterval =            RMNET_MUX_SDIO_NOTIFY_INTERVAL + 4,
+};
+
+static struct usb_endpoint_descriptor rmnet_mux_hs_in_desc = {
+	.bLength =              USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =      USB_DT_ENDPOINT,
+	.bEndpointAddress =     USB_DIR_IN,
+	.bmAttributes =         USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	__constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor rmnet_mux_hs_out_desc = {
+	.bLength =              USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =      USB_DT_ENDPOINT,
+	.bEndpointAddress =     USB_DIR_OUT,
+	.bmAttributes =         USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	__constant_cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *rmnet_mux_hs_function[] = {
+	(struct usb_descriptor_header *) &rmnet_mux_interface_desc,
+	(struct usb_descriptor_header *) &rmnet_mux_hs_notify_desc,
+	(struct usb_descriptor_header *) &rmnet_mux_hs_in_desc,
+	(struct usb_descriptor_header *) &rmnet_mux_hs_out_desc,
+	NULL,
+};
+
+/* String descriptors */
+
+static struct usb_string rmnet_mux_string_defs[] = {
+	[0].s = "RmNet",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings rmnet_mux_string_table = {
+	.language =             0x0409, /* en-us */
+	.strings =              rmnet_mux_string_defs,
+};
+
+static struct usb_gadget_strings *rmnet_mux_strings[] = {
+	&rmnet_mux_string_table,
+	NULL,
+};
+
+static struct rmnet_mux_ctrl_pkt *rmnet_mux_alloc_ctrl_pkt(unsigned len,
+							   gfp_t flags)
+{
+	struct rmnet_mux_ctrl_pkt *cpkt;
+
+	cpkt = kzalloc(sizeof(struct rmnet_mux_ctrl_pkt), flags);
+	if (!cpkt)
+		return 0;
+
+	cpkt->buf = kzalloc(len, flags);
+	if (!cpkt->buf) {
+		kfree(cpkt);
+		return 0;
+	}
+
+	cpkt->len = len;
+
+	return cpkt;
+
+}
+
+static void rmnet_mux_free_ctrl_pkt(struct rmnet_mux_ctrl_pkt *cpkt)
+{
+	kfree(cpkt->buf);
+	kfree(cpkt);
+}
+
+/*
+ * Allocate a usb_request and its buffer.  Returns a pointer to the
+ * usb_request or a pointer with an error code if there is an error.
+ */
+static struct usb_request *
+rmnet_mux_alloc_req(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags)
+{
+	struct usb_request *req;
+
+	req = usb_ep_alloc_request(ep, kmalloc_flags);
+
+	if (len && req != NULL) {
+		req->length = len;
+		req->buf = kmalloc(len, kmalloc_flags);
+		if (req->buf == NULL) {
+			usb_ep_free_request(ep, req);
+			req = NULL;
+		}
+	}
+
+	return req ? req : ERR_PTR(-ENOMEM);
+}
+
+/*
+ * Free a usb_request and its buffer.
+ */
+static void rmnet_mux_free_req(struct usb_ep *ep, struct usb_request *req)
+{
+	kfree(req->buf);
+	usb_ep_free_request(ep, req);
+}
+
+static int rmnet_mux_sdio_rx_submit(struct rmnet_mux_dev *dev,
+				    struct usb_request *req, gfp_t gfp_flags)
+{
+	struct sk_buff *skb;
+	int retval;
+
+	skb = alloc_skb(RMNET_MUX_SDIO_RX_REQ_SIZE + RMNET_MUX_SDIO_HDR,
+								gfp_flags);
+	if (skb == NULL)
+		return -ENOMEM;
+	skb_reserve(skb, RMNET_MUX_SDIO_HDR);
+
+	req->buf = skb->data;
+	req->length = RMNET_MUX_SDIO_RX_REQ_SIZE;
+	req->context = skb;
+
+	retval = usb_ep_queue(dev->epout, req, gfp_flags);
+	if (retval)
+		dev_kfree_skb_any(skb);
+
+	return retval;
+}
+
+static void rmnet_mux_sdio_start_rx(struct rmnet_mux_dev *dev)
+{
+	struct rmnet_mux_sdio_dev *sdio_dev = &dev->sdio_dev;
+	struct usb_composite_dev *cdev = dev->cdev;
+	int status;
+	struct usb_request *req;
+	struct list_head *pool;
+	unsigned long flags;
+
+	if (!atomic_read(&dev->online)) {
+		pr_debug("%s: USB not connected\n", __func__);
+		return;
+	}
+
+	spin_lock_irqsave(&dev->lock, flags);
+	pool = &sdio_dev->rx_idle;
+	while (!list_empty(pool)) {
+		req = list_first_entry(pool, struct usb_request, list);
+		list_del(&req->list);
+
+		spin_unlock_irqrestore(&dev->lock, flags);
+		status = rmnet_mux_sdio_rx_submit(dev, req, GFP_KERNEL);
+		spin_lock_irqsave(&dev->lock, flags);
+
+		if (status) {
+			ERROR(cdev, "rmnet_mux data rx enqueue err %d\n",
+								status);
+			list_add_tail(&req->list, &sdio_dev->rx_idle);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static void rmnet_mux_sdio_start_tx(struct rmnet_mux_dev *dev)
+{
+	unsigned long			flags;
+	int				status;
+	struct sk_buff			*skb;
+	struct usb_request		*req;
+	struct rmnet_mux_sdio_dev *sdio_dev = &dev->sdio_dev;
+	struct usb_composite_dev	*cdev = dev->cdev;
+
+
+	if (!atomic_read(&dev->online))
+		return;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	while (!list_empty(&sdio_dev->tx_idle)) {
+		skb = __skb_dequeue(&sdio_dev->tx_skb_queue);
+		if (!skb) {
+			spin_unlock_irqrestore(&dev->lock, flags);
+			return;
+		}
+
+		req = list_first_entry(&sdio_dev->tx_idle,
+				struct usb_request, list);
+		req->context = skb;
+		req->buf = skb->data;
+		req->length = skb->len;
+
+		list_del(&req->list);
+		spin_unlock(&dev->lock);
+		status = usb_ep_queue(dev->epin, req, GFP_ATOMIC);
+		spin_lock(&dev->lock);
+		if (status) {
+			/* USB still online, queue requests back */
+			if (atomic_read(&dev->online)) {
+				ERROR(cdev, "rmnet tx data enqueue err %d\n",
+						status);
+				list_add_tail(&req->list, &sdio_dev->tx_idle);
+				__skb_queue_head(&sdio_dev->tx_skb_queue, skb);
+			} else {
+				req->buf = 0;
+				rmnet_mux_free_req(dev->epin, req);
+				dev_kfree_skb_any(skb);
+			}
+			break;
+		}
+		dev->dpkts_tolaptop++;
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static void rmnet_mux_sdio_data_receive_cb(void *priv, struct sk_buff *skb)
+{
+	struct rmnet_mux_dev *dev = priv;
+	struct rmnet_mux_sdio_dev *sdio_dev = &dev->sdio_dev;
+	unsigned long flags;
+
+	if (!skb)
+		return;
+	if (!atomic_read(&dev->online)) {
+		dev_kfree_skb_any(skb);
+		return;
+	}
+	spin_lock_irqsave(&dev->lock, flags);
+	if (sdio_dev->tx_skb_queue.qlen > mux_sdio_tx_pkt_drop_thld) {
+		pr_err_ratelimited("%s: tx pkt dropped: tx_drop_cnt:%lu\n",
+			__func__, dev->tx_drp_cnt);
+		dev->tx_drp_cnt++;
+		spin_unlock_irqrestore(&dev->lock, flags);
+		dev_kfree_skb_any(skb);
+		return;
+	}
+	__skb_queue_tail(&sdio_dev->tx_skb_queue, skb);
+	spin_unlock_irqrestore(&dev->lock, flags);
+	rmnet_mux_sdio_start_tx(dev);
+}
+
+static void rmnet_mux_sdio_data_write_done(void *priv, struct sk_buff *skb)
+{
+	struct rmnet_mux_dev *dev = priv;
+	struct rmnet_mux_sdio_dev *sdio_dev = &dev->sdio_dev;
+
+	if (!skb)
+		return;
+
+	dev_kfree_skb_any(skb);
+	/* this function is called from
+	 * sdio mux from spin_lock_irqsave
+	 */
+	spin_lock(&dev->lock);
+	sdio_dev->dpkts_pending_atdmux--;
+
+	if (sdio_dev->dpkts_pending_atdmux >= mux_sdio_rx_fctrl_dis_thld) {
+		spin_unlock(&dev->lock);
+		return;
+	}
+	spin_unlock(&dev->lock);
+
+	rmnet_mux_sdio_start_rx(dev);
+}
+
+static void rmnet_mux_sdio_data_rx_work(struct work_struct *w)
+{
+	struct rmnet_mux_dev *dev = container_of(w, struct rmnet_mux_dev,
+			sdio_dev.data_rx_work);
+	struct rmnet_mux_sdio_dev *sdio_dev = &dev->sdio_dev;
+	struct usb_composite_dev *cdev = dev->cdev;
+
+	struct sk_buff *skb;
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	while ((skb = __skb_dequeue(&sdio_dev->rx_skb_queue))) {
+		spin_unlock_irqrestore(&dev->lock, flags);
+		ret = msm_sdio_dmux_write(rmnet_mux_sdio_data_ch, skb);
+		spin_lock_irqsave(&dev->lock, flags);
+		if (ret < 0) {
+			ERROR(cdev, "rmnet_mux SDIO data write failed\n");
+			dev_kfree_skb_any(skb);
+		} else {
+			dev->dpkts_tomdm++;
+			sdio_dev->dpkts_pending_atdmux++;
+		}
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static void
+rmnet_mux_sdio_complete_epout(struct usb_ep *ep, struct usb_request *req)
+{
+	struct rmnet_mux_dev *dev = ep->driver_data;
+	struct rmnet_mux_sdio_dev *sdio_dev = &dev->sdio_dev;
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct sk_buff *skb = req->context;
+	int status = req->status;
+	int queue = 0;
+
+	if (dev->xport == USB_GADGET_XPORT_UNDEF) {
+		dev_kfree_skb_any(skb);
+		req->buf = 0;
+		rmnet_mux_free_req(ep, req);
+		return;
+	}
+
+	switch (status) {
+	case 0:
+		/* successful completion */
+		skb_put(skb, req->actual);
+		queue = 1;
+		break;
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		dev_kfree_skb_any(skb);
+		req->buf = 0;
+		rmnet_mux_free_req(ep, req);
+		return;
+	default:
+		/* unexpected failure */
+		ERROR(cdev, "RMNET_MUX %s response error %d, %d/%d\n",
+			ep->name, status,
+			req->actual, req->length);
+		dev_kfree_skb_any(skb);
+		break;
+	}
+
+	spin_lock(&dev->lock);
+	if (queue) {
+		__skb_queue_tail(&sdio_dev->rx_skb_queue, skb);
+		queue_work(dev->wq, &sdio_dev->data_rx_work);
+	}
+
+	if (sdio_dev->dpkts_pending_atdmux >= mux_sdio_rx_fctrl_en_thld) {
+		list_add_tail(&req->list, &sdio_dev->rx_idle);
+		spin_unlock(&dev->lock);
+		return;
+	}
+	spin_unlock(&dev->lock);
+
+	status = rmnet_mux_sdio_rx_submit(dev, req, GFP_ATOMIC);
+	if (status) {
+		ERROR(cdev, "rmnet_mux data rx enqueue err %d\n", status);
+		list_add_tail(&req->list, &sdio_dev->rx_idle);
+	}
+}
+
+static void
+rmnet_mux_sdio_complete_epin(struct usb_ep *ep, struct usb_request *req)
+{
+	struct rmnet_mux_dev *dev = ep->driver_data;
+	struct rmnet_mux_sdio_dev *sdio_dev = &dev->sdio_dev;
+	struct sk_buff  *skb = req->context;
+	struct usb_composite_dev *cdev = dev->cdev;
+	int status = req->status;
+
+	if (dev->xport == USB_GADGET_XPORT_UNDEF) {
+		dev_kfree_skb_any(skb);
+		req->buf = 0;
+		rmnet_mux_free_req(ep, req);
+		return;
+	}
+
+	switch (status) {
+	case 0:
+		/* successful completion */
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		break;
+	default:
+		ERROR(cdev, "rmnet_mux data tx ep error %d\n", status);
+		break;
+	}
+
+	spin_lock(&dev->lock);
+	list_add_tail(&req->list, &sdio_dev->tx_idle);
+	spin_unlock(&dev->lock);
+	dev_kfree_skb_any(skb);
+
+	rmnet_mux_sdio_start_tx(dev);
+}
+
+static int rmnet_mux_sdio_enable(struct rmnet_mux_dev *dev)
+{
+	struct rmnet_mux_sdio_dev *sdio_dev = &dev->sdio_dev;
+	int i;
+	struct usb_request *req;
+
+	/*
+	 * If the memory allocation fails, all the allocated
+	 * requests will be freed upon cable disconnect.
+	 */
+	for (i = 0; i < RMNET_MUX_SDIO_RX_REQ_MAX; i++) {
+		req = rmnet_mux_alloc_req(dev->epout, 0, GFP_KERNEL);
+		if (IS_ERR(req))
+			return PTR_ERR(req);
+		req->complete = rmnet_mux_sdio_complete_epout;
+		list_add_tail(&req->list, &sdio_dev->rx_idle);
+	}
+	for (i = 0; i < RMNET_MUX_SDIO_TX_REQ_MAX; i++) {
+		req = rmnet_mux_alloc_req(dev->epin, 0, GFP_KERNEL);
+		if (IS_ERR(req))
+			return PTR_ERR(req);
+		req->complete = rmnet_mux_sdio_complete_epin;
+		list_add_tail(&req->list, &sdio_dev->tx_idle);
+	}
+
+	rmnet_mux_sdio_start_rx(dev);
+	return 0;
+}
+
+static void rmnet_mux_smd_start_rx(struct rmnet_mux_dev *dev)
+{
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct rmnet_mux_smd_dev *smd_dev = &dev->smd_dev;
+	int status;
+	struct usb_request *req;
+	struct list_head *pool = &smd_dev->rx_idle;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	while (!list_empty(pool)) {
+		req = list_entry(pool->next, struct usb_request, list);
+		list_del(&req->list);
+
+		spin_unlock_irqrestore(&dev->lock, flags);
+		status = usb_ep_queue(dev->epout, req, GFP_ATOMIC);
+		spin_lock_irqsave(&dev->lock, flags);
+
+		if (status) {
+			ERROR(cdev, "rmnet data rx enqueue err %d\n", status);
+			list_add_tail(&req->list, pool);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static void rmnet_mux_smd_data_tx_tlet(unsigned long arg)
+{
+	struct rmnet_mux_dev *dev = (struct rmnet_mux_dev *) arg;
+	struct rmnet_mux_smd_dev *smd_dev = &dev->smd_dev;
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct usb_request *req;
+	int status;
+	int sz;
+	unsigned long flags;
+
+	while (1) {
+		if (!atomic_read(&dev->online))
+			break;
+		sz = smd_cur_packet_size(smd_dev->smd_data.ch);
+		if (sz == 0)
+			break;
+		if (smd_read_avail(smd_dev->smd_data.ch) < sz)
+			break;
+
+		spin_lock_irqsave(&dev->lock, flags);
+		if (list_empty(&smd_dev->tx_idle)) {
+			spin_unlock_irqrestore(&dev->lock, flags);
+			DBG(cdev, "rmnet_mux data Tx buffers full\n");
+			break;
+		}
+		req = list_first_entry(&smd_dev->tx_idle,
+				struct usb_request, list);
+		list_del(&req->list);
+		spin_unlock_irqrestore(&dev->lock, flags);
+
+		req->length = smd_read(smd_dev->smd_data.ch, req->buf, sz);
+		status = usb_ep_queue(dev->epin, req, GFP_ATOMIC);
+		if (status) {
+			ERROR(cdev, "rmnet tx data enqueue err %d\n", status);
+			spin_lock_irqsave(&dev->lock, flags);
+			list_add_tail(&req->list, &smd_dev->tx_idle);
+			spin_unlock_irqrestore(&dev->lock, flags);
+			break;
+		}
+		dev->dpkts_tolaptop++;
+	}
+
+}
+
+static void rmnet_mux_smd_data_rx_tlet(unsigned long arg)
+{
+	struct rmnet_mux_dev *dev = (struct rmnet_mux_dev *) arg;
+	struct rmnet_mux_smd_dev *smd_dev = &dev->smd_dev;
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct usb_request *req;
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	while (1) {
+		if (!atomic_read(&dev->online))
+			break;
+		if (list_empty(&smd_dev->rx_queue)) {
+			atomic_set(&smd_dev->smd_data.rx_pkt, 0);
+			break;
+		}
+		req = list_first_entry(&smd_dev->rx_queue,
+			struct usb_request, list);
+		if (smd_write_avail(smd_dev->smd_data.ch) < req->actual) {
+			atomic_set(&smd_dev->smd_data.rx_pkt, req->actual);
+			DBG(cdev, "rmnet_mux SMD data channel full\n");
+			break;
+		}
+
+		list_del(&req->list);
+		spin_unlock_irqrestore(&dev->lock, flags);
+		ret = smd_write(smd_dev->smd_data.ch, req->buf, req->actual);
+		spin_lock_irqsave(&dev->lock, flags);
+		if (ret != req->actual) {
+			ERROR(cdev, "rmnet_mux SMD data write failed\n");
+			break;
+		}
+		dev->dpkts_tomsm++;
+		list_add_tail(&req->list, &smd_dev->rx_idle);
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	/* We have free rx data requests. */
+	rmnet_mux_smd_start_rx(dev);
+}
+
+/* If SMD has enough room to accommodate a data rx packet,
+ * write into SMD directly. Otherwise enqueue to rx_queue.
+ * We will not write into SMD directly untill rx_queue is
+ * empty to strictly follow the ordering requests.
+ */
+static void
+rmnet_mux_smd_complete_epout(struct usb_ep *ep, struct usb_request *req)
+{
+	struct rmnet_mux_dev *dev = req->context;
+	struct rmnet_mux_smd_dev *smd_dev = &dev->smd_dev;
+	struct usb_composite_dev *cdev = dev->cdev;
+	int status = req->status;
+	int ret;
+
+	if (dev->xport == USB_GADGET_XPORT_UNDEF) {
+		rmnet_mux_free_req(ep, req);
+		return;
+	}
+
+	switch (status) {
+	case 0:
+		/* normal completion */
+		break;
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		spin_lock(&dev->lock);
+		list_add_tail(&req->list, &smd_dev->rx_idle);
+		spin_unlock(&dev->lock);
+		return;
+	default:
+		/* unexpected failure */
+		ERROR(cdev, "RMNET_MUX %s response error %d, %d/%d\n",
+			ep->name, status,
+			req->actual, req->length);
+		spin_lock(&dev->lock);
+		list_add_tail(&req->list, &smd_dev->rx_idle);
+		spin_unlock(&dev->lock);
+		return;
+	}
+
+	spin_lock(&dev->lock);
+	if (!atomic_read(&smd_dev->smd_data.rx_pkt)) {
+		if (smd_write_avail(smd_dev->smd_data.ch) < req->actual) {
+			atomic_set(&smd_dev->smd_data.rx_pkt, req->actual);
+			goto queue_req;
+		}
+		spin_unlock(&dev->lock);
+		ret = smd_write(smd_dev->smd_data.ch, req->buf, req->actual);
+		/* This should never happen */
+		if (ret != req->actual)
+			ERROR(cdev, "rmnet_mux data smd write failed\n");
+		/* Restart Rx */
+		dev->dpkts_tomsm++;
+		spin_lock(&dev->lock);
+		list_add_tail(&req->list, &smd_dev->rx_idle);
+		spin_unlock(&dev->lock);
+		rmnet_mux_smd_start_rx(dev);
+		return;
+	}
+queue_req:
+	list_add_tail(&req->list, &smd_dev->rx_queue);
+	spin_unlock(&dev->lock);
+}
+
+static void rmnet_mux_smd_complete_epin(struct usb_ep *ep,
+					struct usb_request *req)
+{
+	struct rmnet_mux_dev *dev = req->context;
+	struct rmnet_mux_smd_dev *smd_dev = &dev->smd_dev;
+	struct usb_composite_dev *cdev = dev->cdev;
+	int status = req->status;
+	int schedule = 0;
+
+	if (dev->xport == USB_GADGET_XPORT_UNDEF) {
+		rmnet_mux_free_req(ep, req);
+		return;
+	}
+
+	switch (status) {
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		spin_lock(&dev->lock);
+		list_add_tail(&req->list, &smd_dev->tx_idle);
+		spin_unlock(&dev->lock);
+		break;
+	default:
+		ERROR(cdev, "rmnet_mux data tx ep error %d\n", status);
+		/* FALLTHROUGH */
+	case 0:
+		spin_lock(&dev->lock);
+		if (list_empty(&smd_dev->tx_idle))
+			schedule = 1;
+		list_add_tail(&req->list, &smd_dev->tx_idle);
+
+		if (schedule)
+			tasklet_schedule(&smd_dev->smd_data.tx_tlet);
+		spin_unlock(&dev->lock);
+		break;
+	}
+
+}
+
+
+static void rmnet_mux_smd_notify(void *priv, unsigned event)
+{
+	struct rmnet_mux_dev *dev = priv;
+	struct rmnet_mux_smd_info *smd_info = &dev->smd_dev.smd_data;
+	int len = atomic_read(&smd_info->rx_pkt);
+
+	switch (event) {
+	case SMD_EVENT_DATA: {
+		if (!atomic_read(&dev->online))
+			break;
+		if (len && (smd_write_avail(smd_info->ch) >= len))
+			tasklet_schedule(&smd_info->rx_tlet);
+
+		if (smd_read_avail(smd_info->ch))
+			tasklet_schedule(&smd_info->tx_tlet);
+
+		break;
+	}
+	case SMD_EVENT_OPEN:
+		/* usb endpoints are not enabled untill smd channels
+		 * are opened. wake up worker thread to continue
+		 * connection processing
+		 */
+		set_bit(RMNET_MUX_CH_OPENED, &smd_info->flags);
+		wake_up(&smd_info->wait);
+		break;
+	case SMD_EVENT_CLOSE:
+		/* We will never come here.
+		 * reset flags after closing smd channel
+		 * */
+		clear_bit(RMNET_MUX_CH_OPENED, &smd_info->flags);
+		break;
+	}
+}
+
+static int rmnet_mux_smd_enable(struct rmnet_mux_dev *dev)
+{
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct rmnet_mux_smd_dev *smd_dev = &dev->smd_dev;
+	int i, ret;
+	struct usb_request *req;
+
+	if (test_bit(RMNET_MUX_CH_OPENED, &smd_dev->smd_data.flags))
+		goto smd_alloc_req;
+
+	ret = smd_open(rmnet_mux_smd_data_ch, &smd_dev->smd_data.ch,
+			dev, rmnet_mux_smd_notify);
+	if (ret) {
+		ERROR(cdev, "Unable to open data smd channel\n");
+		return ret;
+	}
+
+	wait_event(smd_dev->smd_data.wait, test_bit(RMNET_MUX_CH_OPENED,
+				&smd_dev->smd_data.flags));
+
+	/* Allocate bulk in/out requests for data transfer.
+	 * If the memory allocation fails, all the allocated
+	 * requests will be freed upon cable disconnect.
+	 */
+smd_alloc_req:
+	for (i = 0; i < RMNET_MUX_SMD_RX_REQ_MAX; i++) {
+		req = rmnet_mux_alloc_req(dev->epout, RMNET_MUX_SMD_RX_REQ_SIZE,
+				GFP_KERNEL);
+		if (IS_ERR(req))
+			return PTR_ERR(req);
+		req->length = RMNET_MUX_SMD_TXN_MAX;
+		req->context = dev;
+		req->complete = rmnet_mux_smd_complete_epout;
+		list_add_tail(&req->list, &smd_dev->rx_idle);
+	}
+
+	for (i = 0; i < RMNET_MUX_SMD_TX_REQ_MAX; i++) {
+		req = rmnet_mux_alloc_req(dev->epin, RMNET_MUX_SMD_TX_REQ_SIZE,
+				GFP_KERNEL);
+		if (IS_ERR(req))
+			return PTR_ERR(req);
+		req->context = dev;
+		req->complete = rmnet_mux_smd_complete_epin;
+		list_add_tail(&req->list, &smd_dev->tx_idle);
+	}
+
+	rmnet_mux_smd_start_rx(dev);
+	return 0;
+}
+
+static void rmnet_mux_notify_complete(struct usb_ep *ep,
+					 struct usb_request *req)
+{
+	struct rmnet_mux_dev *dev = req->context;
+	struct usb_composite_dev *cdev = dev->cdev;
+	int status = req->status;
+
+	switch (status) {
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		atomic_set(&dev->notify_count, 0);
+		break;
+	default:
+		ERROR(cdev, "rmnet_mux notifyep error %d\n", status);
+		/* FALLTHROUGH */
+	case 0:
+
+		if (atomic_dec_and_test(&dev->notify_count))
+			break;
+
+		status = usb_ep_queue(dev->epnotify, req, GFP_ATOMIC);
+		if (status) {
+			atomic_dec(&dev->notify_count);
+			ERROR(cdev, "rmnet notify ep enq error %d\n", status);
+		}
+		break;
+	}
+}
+
+static void ctrl_response_available(struct rmnet_mux_dev *dev)
+{
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct usb_request              *req = dev->notify_req;
+	struct usb_cdc_notification     *event = req->buf;
+	int status;
+
+	/* Response will be sent later */
+	if (atomic_inc_return(&dev->notify_count) != 1)
+		return;
+
+	event->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
+			| USB_RECIP_INTERFACE;
+	event->bNotificationType = USB_CDC_NOTIFY_RESPONSE_AVAILABLE;
+	event->wValue = cpu_to_le16(0);
+	event->wIndex = cpu_to_le16(dev->ifc_id);
+	event->wLength = cpu_to_le16(0);
+
+	status = usb_ep_queue(dev->epnotify, dev->notify_req, GFP_ATOMIC);
+	if (status < 0) {
+		atomic_dec(&dev->notify_count);
+		ERROR(cdev, "rmnet_mux notify ep enqueue error %d\n", status);
+	}
+}
+
+#define MAX_CTRL_PKT_SIZE	4096
+
+static void rmnet_mux_response_complete(struct usb_ep *ep,
+					struct usb_request *req)
+{
+	struct rmnet_mux_dev *dev = req->context;
+	struct usb_composite_dev *cdev = dev->cdev;
+
+	switch (req->status) {
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+	case 0:
+		return;
+	default:
+		INFO(cdev, "rmnet_mux %s response error %d, %d/%d\n",
+			ep->name, req->status,
+			req->actual, req->length);
+	}
+}
+
+static void rmnet_mux_command_complete(struct usb_ep *ep,
+					struct usb_request *req)
+{
+	struct rmnet_mux_dev		*dev = req->context;
+	struct usb_composite_dev	*cdev = dev->cdev;
+	struct rmnet_mux_ctrl_dev		*ctrl_dev = &dev->ctrl_dev;
+	struct rmnet_mux_ctrl_pkt		*cpkt;
+	int				len = req->actual;
+
+	if (req->status < 0) {
+		ERROR(cdev, "rmnet_mux command error %d\n", req->status);
+		return;
+	}
+
+	cpkt = rmnet_mux_alloc_ctrl_pkt(len, GFP_ATOMIC);
+	if (!cpkt) {
+		ERROR(cdev, "unable to allocate memory for ctrl req\n");
+		return;
+	}
+
+	spin_lock(&dev->lock);
+	if (!ctrl_dev->opened) {
+		spin_unlock(&dev->lock);
+		rmnet_mux_free_ctrl_pkt(cpkt);
+		dev->cpkts_drp_cnt++;
+		pr_err_ratelimited(
+			"%s: ctrl pkts dropped: cpkts_drp_cnt: %lu\n",
+			__func__, dev->cpkts_drp_cnt);
+		return;
+	}
+
+	memcpy(cpkt->buf, req->buf, len);
+
+	list_add_tail(&cpkt->list, &ctrl_dev->tx_q);
+	ctrl_dev->tx_len++;
+	spin_unlock(&dev->lock);
+
+	/* wakeup read thread */
+	wake_up(&ctrl_dev->tx_wait_q);
+}
+
+static int
+rmnet_mux_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+	struct rmnet_mux_dev *dev = container_of(f, struct rmnet_mux_dev,
+								 function);
+	struct rmnet_mux_ctrl_dev *ctrl_dev = &dev->ctrl_dev;
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct usb_request      *req = cdev->req;
+	int                     ret = -EOPNOTSUPP;
+	u16                     w_index = le16_to_cpu(ctrl->wIndex);
+	u16                     w_value = le16_to_cpu(ctrl->wValue);
+	u16                     w_length = le16_to_cpu(ctrl->wLength);
+	struct rmnet_mux_ctrl_pkt	*cpkt;
+
+	if (!atomic_read(&dev->online))
+		return -ENOTCONN;
+
+	switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_SEND_ENCAPSULATED_COMMAND:
+		ret = w_length;
+		req->complete = rmnet_mux_command_complete;
+		req->context = dev;
+		break;
+
+
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_GET_ENCAPSULATED_RESPONSE:
+		if (w_value)
+			goto invalid;
+		else {
+			unsigned len;
+
+			spin_lock(&dev->lock);
+			if (list_empty(&ctrl_dev->rx_q)) {
+				DBG(cdev, "ctrl resp queue empty"
+					" %02x.%02x v%04x i%04x l%d\n",
+					ctrl->bRequestType, ctrl->bRequest,
+					w_value, w_index, w_length);
+				spin_unlock(&dev->lock);
+				goto invalid;
+
+			}
+			cpkt = list_first_entry(&ctrl_dev->rx_q,
+					struct rmnet_mux_ctrl_pkt, list);
+			list_del(&cpkt->list);
+			ctrl_dev->rx_len--;
+			spin_unlock(&dev->lock);
+
+			len = min_t(unsigned, w_length, cpkt->len);
+			memcpy(req->buf, cpkt->buf, len);
+			ret = len;
+			req->complete = rmnet_mux_response_complete;
+			req->context = dev;
+			rmnet_mux_free_ctrl_pkt(cpkt);
+
+			dev->cpkts_tolaptop++;
+		}
+		break;
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_REQ_SET_CONTROL_LINE_STATE:
+		/* This is a workaround for RmNet and is borrowed from the
+		 * CDC/ACM standard. The host driver will issue the above ACM
+		 * standard request to the RmNet interface in the following
+		 * scenario: Once the network adapter is disabled from device
+		 * manager, the above request will be sent from the qcusbnet
+		 * host driver, with DTR being '0'. Once network adapter is
+		 * enabled from device manager (or during enumeration), the
+		 * request will be sent with DTR being '1'.
+		 */
+		if (w_value & RMNET_MUX_ACM_CTRL_DTR)
+			ctrl_dev->cbits_to_modem |= TIOCM_DTR;
+		else
+			ctrl_dev->cbits_to_modem &= ~TIOCM_DTR;
+
+		ret = 0;
+
+		break;
+	default:
+
+invalid:
+	DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
+		ctrl->bRequestType, ctrl->bRequest,
+		w_value, w_index, w_length);
+	}
+
+	/* respond with data transfer or status phase? */
+	if (ret >= 0) {
+		VDBG(cdev, "rmnet_mux req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+		req->zero = (ret < w_length);
+		req->length = ret;
+		ret = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+		if (ret < 0)
+			ERROR(cdev, "rmnet_mux ep0 enqueue err %d\n", ret);
+	}
+
+	return ret;
+}
+
+static void rmnet_mux_free_buf(struct rmnet_mux_dev *dev)
+{
+	struct rmnet_mux_sdio_dev *sdio_dev = &dev->sdio_dev;
+	struct rmnet_mux_ctrl_dev *ctrl_dev = &dev->ctrl_dev;
+	struct rmnet_mux_smd_dev *smd_dev = &dev->smd_dev;
+	struct rmnet_mux_ctrl_pkt *cpkt;
+	struct usb_request *req;
+	struct list_head *pool;
+	struct sk_buff *skb;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	/* free all usb requests in SDIO tx pool */
+	pool = &sdio_dev->tx_idle;
+	while (!list_empty(pool)) {
+		req = list_first_entry(pool, struct usb_request, list);
+		list_del(&req->list);
+		req->buf = NULL;
+		rmnet_mux_free_req(dev->epout, req);
+	}
+
+	pool = &sdio_dev->rx_idle;
+	/* free all usb requests in SDIO rx pool */
+	while (!list_empty(pool)) {
+		req = list_first_entry(pool, struct usb_request, list);
+		list_del(&req->list);
+		req->buf = NULL;
+		rmnet_mux_free_req(dev->epin, req);
+	}
+
+	while ((skb = __skb_dequeue(&sdio_dev->tx_skb_queue)))
+		dev_kfree_skb_any(skb);
+
+	while ((skb = __skb_dequeue(&sdio_dev->rx_skb_queue)))
+		dev_kfree_skb_any(skb);
+
+	/* free all usb requests in SMD tx pool */
+	pool = &smd_dev->tx_idle;
+	while (!list_empty(pool)) {
+		req = list_first_entry(pool, struct usb_request, list);
+		list_del(&req->list);
+		rmnet_mux_free_req(dev->epout, req);
+	}
+
+	pool = &smd_dev->rx_idle;
+	/* free all usb requests in SMD rx pool */
+	while (!list_empty(pool)) {
+		req = list_first_entry(pool, struct usb_request, list);
+		list_del(&req->list);
+		rmnet_mux_free_req(dev->epin, req);
+	}
+
+	/* free all usb requests in SMD rx queue */
+	pool = &smd_dev->rx_queue;
+	while (!list_empty(pool)) {
+		req = list_first_entry(pool, struct usb_request, list);
+		list_del(&req->list);
+		rmnet_mux_free_req(dev->epin, req);
+	}
+
+	pool = &ctrl_dev->tx_q;
+	while (!list_empty(pool)) {
+		cpkt = list_first_entry(pool, struct rmnet_mux_ctrl_pkt, list);
+		list_del(&cpkt->list);
+		rmnet_mux_free_ctrl_pkt(cpkt);
+		ctrl_dev->tx_len--;
+	}
+
+	pool = &ctrl_dev->rx_q;
+	while (!list_empty(pool)) {
+		cpkt = list_first_entry(pool, struct rmnet_mux_ctrl_pkt, list);
+		list_del(&cpkt->list);
+		rmnet_mux_free_ctrl_pkt(cpkt);
+		ctrl_dev->rx_len--;
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static void rmnet_mux_disconnect_work(struct work_struct *w)
+{
+	struct rmnet_mux_dev *dev = container_of(w, struct rmnet_mux_dev,
+			disconnect_work);
+	struct rmnet_mux_smd_dev *smd_dev = &dev->smd_dev;
+	struct rmnet_mux_ctrl_dev *ctrl_dev = &dev->ctrl_dev;
+
+	if (dev->xport == USB_GADGET_XPORT_SMD) {
+		tasklet_kill(&smd_dev->smd_data.rx_tlet);
+		tasklet_kill(&smd_dev->smd_data.tx_tlet);
+	}
+
+	rmnet_mux_free_buf(dev);
+	dev->xport = 0;
+
+	/* wakeup read thread */
+	wake_up(&ctrl_dev->tx_wait_q);
+}
+
+static void rmnet_mux_suspend(struct usb_function *f)
+{
+	struct rmnet_mux_dev *dev = container_of(f, struct rmnet_mux_dev,
+								function);
+	struct rmnet_mux_ctrl_dev *ctrl_dev = &dev->ctrl_dev;
+
+	if (!atomic_read(&dev->online))
+		return;
+	/* This is a workaround for Windows Host bug during suspend.
+	 * Windows 7/xp Hosts are suppose to drop DTR, when Host suspended.
+	 * Since it is not being done, Hence exclusively dropping the DTR
+	 * from function driver suspend.
+	 */
+	ctrl_dev->cbits_to_modem &= ~TIOCM_DTR;
+}
+
+static void rmnet_mux_disable(struct usb_function *f)
+{
+	struct rmnet_mux_dev *dev = container_of(f, struct rmnet_mux_dev,
+								function);
+	struct rmnet_mux_ctrl_dev *ctrl_dev = &dev->ctrl_dev;
+
+	if (!atomic_read(&dev->online))
+		return;
+
+	atomic_set(&dev->online, 0);
+
+	usb_ep_fifo_flush(dev->epnotify);
+	usb_ep_disable(dev->epnotify);
+	rmnet_mux_free_req(dev->epnotify, dev->notify_req);
+
+	usb_ep_fifo_flush(dev->epout);
+	usb_ep_disable(dev->epout);
+
+	usb_ep_fifo_flush(dev->epin);
+	usb_ep_disable(dev->epin);
+
+	/* cleanup work */
+	ctrl_dev->cbits_to_modem = 0;
+	queue_work(dev->wq, &dev->disconnect_work);
+}
+
+#define SDIO_OPEN_RETRY_DELAY	msecs_to_jiffies(2000)
+#define SDIO_OPEN_MAX_RETRY	90
+static void rmnet_mux_open_sdio_work(struct work_struct *w)
+{
+	struct rmnet_mux_dev *dev =
+		container_of(w, struct rmnet_mux_dev, sdio_dev.open_work.work);
+	struct rmnet_mux_sdio_dev *sdio_dev = &dev->sdio_dev;
+	struct usb_composite_dev *cdev = dev->cdev;
+	int ret;
+	static int retry_cnt;
+
+	/* Data channel for network packets */
+	ret = msm_sdio_dmux_open(rmnet_mux_sdio_data_ch, dev,
+				rmnet_mux_sdio_data_receive_cb,
+				rmnet_mux_sdio_data_write_done);
+	if (ret) {
+		if (retry_cnt > SDIO_OPEN_MAX_RETRY) {
+			ERROR(cdev, "Unable to open SDIO DATA channel\n");
+			return;
+		}
+		retry_cnt++;
+		queue_delayed_work(dev->wq, &sdio_dev->open_work,
+					SDIO_OPEN_RETRY_DELAY);
+		return;
+	}
+
+
+	atomic_set(&sdio_dev->sdio_open, 1);
+	pr_info("%s: usb rmnet_mux sdio channels are open retry_cnt:%d\n",
+				__func__, retry_cnt);
+	retry_cnt = 0;
+	return;
+}
+
+static int rmnet_mux_set_alt(struct usb_function *f,
+			unsigned intf, unsigned alt)
+{
+	struct rmnet_mux_dev *dev = container_of(f, struct rmnet_mux_dev,
+								function);
+	struct rmnet_mux_sdio_dev *sdio_dev = &dev->sdio_dev;
+	struct usb_composite_dev *cdev = dev->cdev;
+	int ret = 0;
+
+	/* allocate notification */
+	dev->notify_req = rmnet_mux_alloc_req(dev->epnotify,
+				RMNET_MUX_SDIO_MAX_NFY_SZE, GFP_ATOMIC);
+
+	if (IS_ERR(dev->notify_req))
+		return PTR_ERR(dev->notify_req);
+
+	dev->notify_req->complete = rmnet_mux_notify_complete;
+	dev->notify_req->context = dev;
+	dev->notify_req->length = RMNET_MUX_SDIO_MAX_NFY_SZE;
+
+	/* Enable epin */
+	dev->epin->driver_data = dev;
+	ret = config_ep_by_speed(cdev->gadget, f, dev->epin);
+	if (ret) {
+			dev->epin->desc = NULL;
+			ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
+				dev->epin->name, ret);
+			return ret;
+	}
+	ret = usb_ep_enable(dev->epin);
+	if (ret) {
+		ERROR(cdev, "can't enable %s, result %d\n",
+		dev->epin->name, ret);
+		return ret;
+	}
+
+	/* Enable epout */
+	dev->epout->driver_data = dev;
+	ret = config_ep_by_speed(cdev->gadget, f, dev->epout);
+	if (ret) {
+		dev->epout->desc = NULL;
+		ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
+				dev->epout->name, ret);
+		usb_ep_disable(dev->epin);
+		return ret;
+	}
+	ret = usb_ep_enable(dev->epout);
+	if (ret) {
+		ERROR(cdev, "can't enable %s, result %d\n",
+			dev->epout->name, ret);
+		usb_ep_disable(dev->epin);
+		return ret;
+	}
+
+	/* Enable epnotify */
+	ret = config_ep_by_speed(cdev->gadget, f, dev->epnotify);
+	if (ret) {
+		dev->epnotify->desc = NULL;
+		ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
+			dev->epnotify->name, ret);
+		usb_ep_disable(dev->epin);
+		usb_ep_disable(dev->epout);
+		return ret;
+	}
+	ret = usb_ep_enable(dev->epnotify);
+	if (ret) {
+		ERROR(cdev, "can't enable %s, result %d\n",
+			dev->epnotify->name, ret);
+		usb_ep_disable(dev->epin);
+		usb_ep_disable(dev->epout);
+		return ret;
+	}
+
+	dev->dpkts_tolaptop = 0;
+	dev->cpkts_tolaptop = 0;
+	dev->cpkts_tomdm = 0;
+	dev->dpkts_tomdm = 0;
+	dev->dpkts_tomsm = 0;
+	dev->tx_drp_cnt = 0;
+	dev->cpkts_drp_cnt = 0;
+	sdio_dev->dpkts_pending_atdmux = 0;
+	atomic_set(&dev->online, 1);
+
+	return 0;
+}
+
+static ssize_t transport_store(
+		struct device *device, struct device_attribute *attr,
+		const char *buf, size_t size)
+{
+	struct rmnet_mux_dev *dev =  rmux_dev;
+	int value;
+	enum transport_type given_xport;
+	enum transport_type t;
+	struct rmnet_mux_smd_dev *smd_dev = &dev->smd_dev;
+	struct rmnet_mux_sdio_dev *sdio_dev = &dev->sdio_dev;
+	struct list_head *pool;
+	struct sk_buff_head *skb_pool;
+	struct sk_buff *skb;
+	struct usb_request *req;
+	unsigned long flags;
+
+	if (!atomic_read(&dev->online)) {
+		pr_err("%s: usb cable is not connected\n", __func__);
+		return -EINVAL;
+	}
+
+	sscanf(buf, "%d", &value);
+	if (value)
+		given_xport = USB_GADGET_XPORT_SDIO;
+	else
+		given_xport = USB_GADGET_XPORT_SMD;
+
+	if (given_xport == dev->xport) {
+		pr_err("%s: given_xport:%s cur_xport:%s doing nothing\n",
+				__func__, xport_to_str(given_xport),
+				xport_to_str(dev->xport));
+		return 0;
+	}
+
+	pr_debug("usb_rmnet_mux: TransportRequested: %s\n",
+			xport_to_str(given_xport));
+
+	/* prevent any other pkts to/from usb  */
+	t = dev->xport;
+	dev->xport = USB_GADGET_XPORT_UNDEF;
+	if (t != USB_GADGET_XPORT_UNDEF) {
+		usb_ep_fifo_flush(dev->epin);
+		usb_ep_fifo_flush(dev->epout);
+	}
+
+	switch (t) {
+	case USB_GADGET_XPORT_SDIO:
+		spin_lock_irqsave(&dev->lock, flags);
+		/* tx_idle */
+
+		sdio_dev->dpkts_pending_atdmux = 0;
+
+		pool = &sdio_dev->tx_idle;
+		while (!list_empty(pool)) {
+			req = list_first_entry(pool, struct usb_request, list);
+			list_del(&req->list);
+			req->buf = NULL;
+			rmnet_mux_free_req(dev->epout, req);
+		}
+
+		/* rx_idle */
+		pool = &sdio_dev->rx_idle;
+		/* free all usb requests in SDIO rx pool */
+		while (!list_empty(pool)) {
+			req = list_first_entry(pool, struct usb_request, list);
+			list_del(&req->list);
+			req->buf = NULL;
+			rmnet_mux_free_req(dev->epin, req);
+		}
+
+		/* tx_skb_queue */
+		skb_pool = &sdio_dev->tx_skb_queue;
+		while ((skb = __skb_dequeue(skb_pool)))
+			dev_kfree_skb_any(skb);
+		/* rx_skb_queue */
+		skb_pool = &sdio_dev->rx_skb_queue;
+		while ((skb = __skb_dequeue(skb_pool)))
+			dev_kfree_skb_any(skb);
+
+		spin_unlock_irqrestore(&dev->lock, flags);
+		break;
+	case USB_GADGET_XPORT_SMD:
+		/* close smd xport */
+		tasklet_kill(&smd_dev->smd_data.rx_tlet);
+		tasklet_kill(&smd_dev->smd_data.tx_tlet);
+
+		spin_lock_irqsave(&dev->lock, flags);
+		/* free all usb requests in SMD tx pool */
+		pool = &smd_dev->tx_idle;
+		while (!list_empty(pool)) {
+			req = list_first_entry(pool, struct usb_request, list);
+			list_del(&req->list);
+			rmnet_mux_free_req(dev->epout, req);
+		}
+
+		pool = &smd_dev->rx_idle;
+		/* free all usb requests in SMD rx pool */
+		while (!list_empty(pool)) {
+			req = list_first_entry(pool, struct usb_request, list);
+			list_del(&req->list);
+			rmnet_mux_free_req(dev->epin, req);
+		}
+
+		/* free all usb requests in SMD rx queue */
+		pool = &smd_dev->rx_queue;
+		while (!list_empty(pool)) {
+			req = list_first_entry(pool, struct usb_request, list);
+			list_del(&req->list);
+			rmnet_mux_free_req(dev->epin, req);
+		}
+
+		spin_unlock_irqrestore(&dev->lock, flags);
+		break;
+	default:
+		pr_debug("%s: undefined xport, do nothing\n", __func__);
+	}
+
+	dev->xport = given_xport;
+
+	switch (dev->xport) {
+	case USB_GADGET_XPORT_SDIO:
+		rmnet_mux_sdio_enable(dev);
+		break;
+	case USB_GADGET_XPORT_SMD:
+		rmnet_mux_smd_enable(dev);
+		break;
+	default:
+		/* we should never come here */
+		pr_err("%s: undefined transport\n", __func__);
+	}
+
+	return size;
+}
+static DEVICE_ATTR(transport, S_IRUGO | S_IWUSR, NULL, transport_store);
+
+static int rmnet_mux_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = c->cdev;
+	struct rmnet_mux_dev *dev = container_of(f, struct rmnet_mux_dev,
+								function);
+	struct rmnet_mux_sdio_dev *sdio_dev = &dev->sdio_dev;
+	int id;
+	struct usb_ep *ep;
+
+	dev->cdev = cdev;
+
+	/* allocate interface ID */
+	id = usb_interface_id(c, f);
+	if (id < 0)
+		return id;
+	dev->ifc_id = id;
+	rmnet_mux_interface_desc.bInterfaceNumber = id;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &rmnet_mux_fs_in_desc);
+	if (!ep)
+		goto out;
+	ep->driver_data = cdev; /* claim endpoint */
+	dev->epin = ep;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &rmnet_mux_fs_out_desc);
+	if (!ep)
+		goto out;
+	ep->driver_data = cdev; /* claim endpoint */
+	dev->epout = ep;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &rmnet_mux_fs_notify_desc);
+	if (!ep)
+		goto out;
+	ep->driver_data = cdev; /* claim endpoint */
+	dev->epnotify = ep;
+
+	/* support all relevant hardware speeds... we expect that when
+	 * hardware is dual speed, all bulk-capable endpoints work at
+	 * both speeds
+	 */
+	if (gadget_is_dualspeed(c->cdev->gadget)) {
+		rmnet_mux_hs_in_desc.bEndpointAddress =
+			rmnet_mux_fs_in_desc.bEndpointAddress;
+		rmnet_mux_hs_out_desc.bEndpointAddress =
+			rmnet_mux_fs_out_desc.bEndpointAddress;
+		rmnet_mux_hs_notify_desc.bEndpointAddress =
+			rmnet_mux_fs_notify_desc.bEndpointAddress;
+	}
+
+	queue_delayed_work(dev->wq, &sdio_dev->open_work, 0);
+
+	return 0;
+
+out:
+	if (dev->epnotify)
+		dev->epnotify->driver_data = NULL;
+	if (dev->epout)
+		dev->epout->driver_data = NULL;
+	if (dev->epin)
+		dev->epin->driver_data = NULL;
+
+	return -ENODEV;
+}
+
+static void rmnet_mux_smd_init(struct rmnet_mux_smd_dev *smd_dev)
+{
+	struct rmnet_mux_dev *dev = container_of(smd_dev,
+			struct rmnet_mux_dev, smd_dev);
+
+	atomic_set(&smd_dev->smd_data.rx_pkt, 0);
+	tasklet_init(&smd_dev->smd_data.rx_tlet, rmnet_mux_smd_data_rx_tlet,
+					(unsigned long) dev);
+	tasklet_init(&smd_dev->smd_data.tx_tlet, rmnet_mux_smd_data_tx_tlet,
+					(unsigned long) dev);
+
+	init_waitqueue_head(&smd_dev->smd_data.wait);
+
+	INIT_LIST_HEAD(&smd_dev->rx_idle);
+	INIT_LIST_HEAD(&smd_dev->rx_queue);
+	INIT_LIST_HEAD(&smd_dev->tx_idle);
+}
+
+static void rmnet_mux_sdio_init(struct rmnet_mux_sdio_dev *sdio_dev)
+{
+	INIT_WORK(&sdio_dev->data_rx_work, rmnet_mux_sdio_data_rx_work);
+
+	INIT_DELAYED_WORK(&sdio_dev->open_work, rmnet_mux_open_sdio_work);
+
+	INIT_LIST_HEAD(&sdio_dev->rx_idle);
+	INIT_LIST_HEAD(&sdio_dev->tx_idle);
+	skb_queue_head_init(&sdio_dev->tx_skb_queue);
+	skb_queue_head_init(&sdio_dev->rx_skb_queue);
+}
+
+static void
+rmnet_mux_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct rmnet_mux_dev *dev = container_of(f, struct rmnet_mux_dev,
+								function);
+	struct rmnet_mux_smd_dev *smd_dev = &dev->smd_dev;
+
+	smd_dev->smd_data.flags = 0;
+}
+
+#if defined(CONFIG_DEBUG_FS)
+#define DEBUG_BUF_SIZE	1024
+static ssize_t rmnet_mux_read_stats(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	struct rmnet_mux_dev *dev = file->private_data;
+	struct rmnet_mux_sdio_dev *sdio_dev = &dev->sdio_dev;
+	struct rmnet_mux_ctrl_dev *ctrl_dev = &dev->ctrl_dev;
+	char *debug_buf;
+	unsigned long flags;
+	int ret;
+
+	debug_buf = kmalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+	if (!debug_buf)
+		return -ENOMEM;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	ret = scnprintf(debug_buf, DEBUG_BUF_SIZE,
+			"dpkts_tomsm:  %lu\n"
+			"dpkts_tomdm: %lu\n"
+			"cpkts_tomdm: %lu\n"
+			"dpkts_tolaptop: %lu\n"
+			"cpkts_tolaptop:  %lu\n"
+			"cbits_to_modem: %lu\n"
+			"tx skb size:     %u\n"
+			"rx_skb_size:     %u\n"
+			"dpkts_pending_at_dmux: %u\n"
+			"tx drp cnt: %lu\n"
+			"cpkts_drp_cnt: %lu\n"
+			"cpkt_tx_qlen: %lu\n"
+			"cpkt_rx_qlen_to_modem: %lu\n"
+			"xport: %s\n"
+			"ctr_ch_opened:	%d\n",
+			dev->dpkts_tomsm, dev->dpkts_tomdm,
+			dev->cpkts_tomdm, dev->dpkts_tolaptop,
+			dev->cpkts_tolaptop, ctrl_dev->cbits_to_modem,
+			sdio_dev->tx_skb_queue.qlen,
+			sdio_dev->rx_skb_queue.qlen,
+			sdio_dev->dpkts_pending_atdmux, dev->tx_drp_cnt,
+			dev->cpkts_drp_cnt,
+			ctrl_dev->tx_len, ctrl_dev->rx_len,
+			xport_to_str(dev->xport), ctrl_dev->opened);
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, debug_buf, ret);
+
+	kfree(debug_buf);
+
+	return ret;
+}
+
+static ssize_t rmnet_mux_reset_stats(struct file *file, const char __user *buf,
+				 size_t count, loff_t *ppos)
+{
+	struct rmnet_mux_dev *dev = file->private_data;
+	struct rmnet_mux_sdio_dev *sdio_dev = &dev->sdio_dev;
+
+	dev->dpkts_tolaptop = 0;
+	dev->cpkts_tolaptop = 0;
+	dev->cpkts_tomdm = 0;
+	dev->dpkts_tomdm = 0;
+	dev->dpkts_tomsm = 0;
+	sdio_dev->dpkts_pending_atdmux = 0;
+	dev->tx_drp_cnt = 0;
+	dev->cpkts_drp_cnt = 0;
+	return count;
+}
+
+static int dbg_rmnet_mux_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+
+	return 0;
+}
+
+const struct file_operations rmnet_mux_svlte_debug_stats_ops = {
+	.open = dbg_rmnet_mux_open,
+	.read = rmnet_mux_read_stats,
+	.write = rmnet_mux_reset_stats,
+};
+
+struct dentry *dent_rmnet_mux;
+
+static void rmnet_mux_debugfs_init(struct rmnet_mux_dev *dev)
+{
+
+	dent_rmnet_mux = debugfs_create_dir("usb_rmnet_mux", 0);
+	if (IS_ERR(dent_rmnet_mux))
+		return;
+
+	debugfs_create_file("status", 0444, dent_rmnet_mux, dev,
+			&rmnet_mux_svlte_debug_stats_ops);
+}
+#else
+static void rmnet_mux_debugfs_init(struct rmnet_mux_dev *dev) {}
+#endif
+
+int usb_rmnet_mux_ctrl_open(struct inode *inode, struct file *fp)
+{
+	struct rmnet_mux_dev *dev =  rmux_dev;
+	struct rmnet_mux_ctrl_dev *ctrl_dev = &dev->ctrl_dev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	if (ctrl_dev->opened) {
+		spin_unlock_irqrestore(&dev->lock, flags);
+		pr_err("%s: device is already opened\n", __func__);
+		return -EBUSY;
+	}
+
+	ctrl_dev->opened = 1;
+	fp->private_data = dev;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return 0;
+}
+
+
+int usb_rmnet_mux_ctrl_release(struct inode *inode, struct file *fp)
+{
+	struct rmnet_mux_dev *dev = fp->private_data;
+	struct rmnet_mux_ctrl_dev *ctrl_dev = &dev->ctrl_dev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	ctrl_dev->opened = 0;
+	fp->private_data = 0;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return 0;
+}
+
+ssize_t usb_rmnet_mux_ctrl_read(struct file *fp,
+		      char __user *buf,
+		      size_t count,
+		      loff_t *ppos)
+{
+	struct rmnet_mux_dev *dev = fp->private_data;
+	struct rmnet_mux_ctrl_dev *ctrl_dev = &dev->ctrl_dev;
+	struct rmnet_mux_ctrl_pkt *cpkt;
+	unsigned long flags;
+	int ret = 0;
+
+ctrl_read:
+	if (!atomic_read(&dev->online)) {
+		pr_debug("%s: USB cable not connected\n", __func__);
+		return -ENODEV;
+	}
+
+	spin_lock_irqsave(&dev->lock, flags);
+	if (list_empty(&ctrl_dev->tx_q)) {
+		spin_unlock_irqrestore(&dev->lock, flags);
+		/* Implement sleep and wakeup here */
+		ret = wait_event_interruptible(ctrl_dev->tx_wait_q,
+					!list_empty(&ctrl_dev->tx_q) ||
+					!atomic_read(&dev->online));
+		if (ret < 0)
+			return ret;
+
+		goto ctrl_read;
+	}
+
+	cpkt = list_first_entry(&ctrl_dev->tx_q, struct rmnet_mux_ctrl_pkt,
+							list);
+	if (cpkt->len > count) {
+		spin_unlock_irqrestore(&dev->lock, flags);
+		pr_err("%s: cpkt size:%d > buf size:%d\n",
+				__func__, cpkt->len, count);
+		return -ENOMEM;
+	}
+	list_del(&cpkt->list);
+	ctrl_dev->tx_len--;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	count = cpkt->len;
+
+	ret = copy_to_user(buf, cpkt->buf, count);
+	dev->cpkts_tomdm++;
+
+	rmnet_mux_free_ctrl_pkt(cpkt);
+
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+ssize_t usb_rmnet_mux_ctrl_write(struct file *fp,
+		       const char __user *buf,
+		       size_t count,
+		       loff_t *ppos)
+{
+	struct rmnet_mux_dev *dev = fp->private_data;
+	struct rmnet_mux_ctrl_dev *ctrl_dev = &dev->ctrl_dev;
+	struct rmnet_mux_ctrl_pkt *cpkt;
+	unsigned long flags;
+	int ret = 0;
+
+	if (!atomic_read(&dev->online)) {
+		pr_debug("%s: USB cable not connected\n", __func__);
+		return -ENODEV;
+	}
+
+	if (!count) {
+		pr_err("%s: zero length ctrl pkt\n", __func__);
+		return -ENODEV;
+	}
+
+	if (count > MAX_CTRL_PKT_SIZE) {
+		pr_err("%s: max_pkt_size:%d given_pkt_size:%d\n",
+				__func__, MAX_CTRL_PKT_SIZE, count);
+		return -ENOMEM;
+	}
+
+	cpkt = rmnet_mux_alloc_ctrl_pkt(count, GFP_KERNEL);
+	if (!cpkt) {
+		pr_err("%s: cannot allocate rmnet_mux ctrl pkt\n", __func__);
+		return -ENOMEM;
+	}
+
+	ret = copy_from_user(cpkt->buf, buf, count);
+	if (ret) {
+		pr_err("%s: copy_from_user failed err:%d\n",
+				__func__, ret);
+		rmnet_mux_free_ctrl_pkt(cpkt);
+		return ret;
+	}
+
+	spin_lock_irqsave(&dev->lock, flags);
+	ctrl_dev->rx_len++;
+	list_add(&cpkt->list, &ctrl_dev->rx_q);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	ctrl_response_available(dev);
+
+	return count;
+}
+
+
+#define RMNET_MUX_CTRL_GET_DTR	_IOR(0xFE, 0, int)
+static long
+usb_rmnet_mux_ctrl_ioctl(struct file *fp, unsigned c, unsigned long value)
+{
+	struct rmnet_mux_dev *dev = fp->private_data;
+	struct rmnet_mux_ctrl_dev *ctrl_dev = &dev->ctrl_dev;
+	unsigned long *temp = (unsigned long *)value;
+	int ret = 0;
+
+	if (c != RMNET_MUX_CTRL_GET_DTR)
+		return -ENODEV;
+
+	ret = copy_to_user(temp,
+			&ctrl_dev->cbits_to_modem,
+			sizeof(*temp));
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static const struct file_operations rmnet_mux_ctrl_fops = {
+	.owner		= THIS_MODULE,
+	.open		= usb_rmnet_mux_ctrl_open,
+	.release	= usb_rmnet_mux_ctrl_release,
+	.read		= usb_rmnet_mux_ctrl_read,
+	.write		= usb_rmnet_mux_ctrl_write,
+	.unlocked_ioctl	= usb_rmnet_mux_ctrl_ioctl,
+};
+
+static struct miscdevice rmnet_mux_ctrl_dev = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "rmnet_mux_ctrl",
+	.fops = &rmnet_mux_ctrl_fops,
+};
+
+static int rmnet_mux_ctrl_device_init(struct rmnet_mux_dev *dev)
+{
+	int ret;
+	struct rmnet_mux_ctrl_dev *ctrl_dev = &dev->ctrl_dev;
+
+	INIT_LIST_HEAD(&ctrl_dev->tx_q);
+	INIT_LIST_HEAD(&ctrl_dev->rx_q);
+	init_waitqueue_head(&ctrl_dev->tx_wait_q);
+
+	ret = misc_register(&rmnet_mux_ctrl_dev);
+	if (ret) {
+		pr_err("%s: failed to register misc device\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int rmnet_smd_sdio_function_add(struct usb_configuration *c)
+{
+	struct rmnet_mux_dev *dev = rmux_dev;
+
+	if (!dev)
+		return -ENODEV;
+
+	pr_debug("rmnet_smd_sdio_function_add\n");
+
+	dev->function.name = "rmnet_smd_sdio";
+	dev->function.strings = rmnet_mux_strings;
+	dev->function.descriptors = rmnet_mux_fs_function;
+	dev->function.hs_descriptors = rmnet_mux_hs_function;
+	dev->function.bind = rmnet_mux_bind;
+	dev->function.unbind = rmnet_mux_unbind;
+	dev->function.setup = rmnet_mux_setup;
+	dev->function.set_alt = rmnet_mux_set_alt;
+	dev->function.disable = rmnet_mux_disable;
+	dev->function.suspend = rmnet_mux_suspend;
+
+	return usb_add_function(c, &dev->function);
+}
+
+static int rmnet_smd_sdio_init(void)
+{
+	struct rmnet_mux_dev *dev;
+	int ret;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	rmux_dev = dev;
+
+	dev->wq = create_singlethread_workqueue("k_rmnet_mux_work");
+	if (!dev->wq) {
+		ret = -ENOMEM;
+		goto free_dev;
+	}
+
+	spin_lock_init(&dev->lock);
+	atomic_set(&dev->notify_count, 0);
+	atomic_set(&dev->online, 0);
+	INIT_WORK(&dev->disconnect_work, rmnet_mux_disconnect_work);
+	rmnet_mux_smd_init(&dev->smd_dev);
+	rmnet_mux_sdio_init(&dev->sdio_dev);
+
+	ret = rmnet_mux_ctrl_device_init(dev);
+	if (ret) {
+		pr_debug("%s: rmnet_mux_ctrl_device_init failed, err:%d\n",
+				__func__, ret);
+		goto free_wq;
+	}
+
+	rmnet_mux_debugfs_init(dev);
+
+	return 0;
+
+free_wq:
+	destroy_workqueue(dev->wq);
+free_dev:
+	kfree(dev);
+
+	return ret;
+}
+
+static void rmnet_smd_sdio_cleanup(void)
+{
+	struct rmnet_mux_dev *dev = rmux_dev;
+	struct rmnet_mux_smd_dev *smd_dev = &dev->smd_dev;
+
+	debugfs_remove_recursive(dent_rmnet_mux);
+	misc_deregister(&rmnet_mux_ctrl_dev);
+	smd_close(smd_dev->smd_data.ch);
+	destroy_workqueue(dev->wq);
+	kfree(dev);
+}
diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c
index 1413df9..6e807cb 100644
--- a/drivers/usb/gadget/f_rndis.c
+++ b/drivers/usb/gadget/f_rndis.c
@@ -869,14 +869,14 @@
 	if (!can_support_rndis(c) || !ethaddr)
 		return -EINVAL;
 
+	/* setup RNDIS itself */
+	status = rndis_init();
+	if (status < 0)
+		return status;
+
 	/* maybe allocate device-global string IDs */
 	if (rndis_string_defs[0].id == 0) {
 
-		/* ... and setup RNDIS itself */
-		status = rndis_init();
-		if (status < 0)
-			return status;
-
 		/* control interface label */
 		status = usb_string_id(c->cdev);
 		if (status < 0)
diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c
index 07197d6..7b6acc6 100644
--- a/drivers/usb/gadget/f_serial.c
+++ b/drivers/usb/gadget/f_serial.c
@@ -13,6 +13,7 @@
 #include <linux/slab.h>
 #include <linux/kernel.h>
 #include <linux/device.h>
+#include <mach/usb_gadget_xport.h>
 
 #include "u_serial.h"
 #include "gadget_chips.h"
@@ -26,74 +27,199 @@
  * CDC ACM driver.  However, for many purposes it's just as functional
  * if you can arrange appropriate host side drivers.
  */
+#define GSERIAL_NO_PORTS 3
 
 struct f_gser {
 	struct gserial			port;
 	u8				data_id;
 	u8				port_num;
+
+	u8				online;
+	enum transport_type		transport;
+
+#ifdef CONFIG_MODEM_SUPPORT
+	u8				pending;
+	spinlock_t			lock;
+	struct usb_ep			*notify;
+	struct usb_request		*notify_req;
+
+	struct usb_cdc_line_coding	port_line_coding;
+
+	/* SetControlLineState request */
+	u16				port_handshake_bits;
+#define ACM_CTRL_RTS	(1 << 1)	/* unused with full duplex */
+#define ACM_CTRL_DTR	(1 << 0)	/* host is ready for data r/w */
+
+	/* SerialState notification */
+	u16				serial_state;
+#define ACM_CTRL_OVERRUN	(1 << 6)
+#define ACM_CTRL_PARITY		(1 << 5)
+#define ACM_CTRL_FRAMING	(1 << 4)
+#define ACM_CTRL_RI		(1 << 3)
+#define ACM_CTRL_BRK		(1 << 2)
+#define ACM_CTRL_DSR		(1 << 1)
+#define ACM_CTRL_DCD		(1 << 0)
+#endif
 };
 
+static unsigned int no_tty_ports;
+static unsigned int no_sdio_ports;
+static unsigned int no_smd_ports;
+static unsigned int no_hsic_sports;
+static unsigned int no_hsuart_sports;
+static unsigned int nr_ports;
+
+static struct port_info {
+	enum transport_type	transport;
+	unsigned		port_num;
+	unsigned		client_port_num;
+} gserial_ports[GSERIAL_NO_PORTS];
+
+static inline bool is_transport_sdio(enum transport_type t)
+{
+	if (t == USB_GADGET_XPORT_SDIO)
+		return 1;
+	return 0;
+}
+
 static inline struct f_gser *func_to_gser(struct usb_function *f)
 {
 	return container_of(f, struct f_gser, port.func);
 }
 
+#ifdef CONFIG_MODEM_SUPPORT
+static inline struct f_gser *port_to_gser(struct gserial *p)
+{
+	return container_of(p, struct f_gser, port);
+}
+#define GS_LOG2_NOTIFY_INTERVAL		5	/* 1 << 5 == 32 msec */
+#define GS_NOTIFY_MAXPACKET		10	/* notification + 2 bytes */
+#endif
 /*-------------------------------------------------------------------------*/
 
 /* interface descriptor: */
 
-static struct usb_interface_descriptor gser_interface_desc __initdata = {
+static struct usb_interface_descriptor gser_interface_desc = {
 	.bLength =		USB_DT_INTERFACE_SIZE,
 	.bDescriptorType =	USB_DT_INTERFACE,
 	/* .bInterfaceNumber = DYNAMIC */
+#ifdef CONFIG_MODEM_SUPPORT
+	.bNumEndpoints =	3,
+#else
 	.bNumEndpoints =	2,
+#endif
 	.bInterfaceClass =	USB_CLASS_VENDOR_SPEC,
 	.bInterfaceSubClass =	0,
 	.bInterfaceProtocol =	0,
 	/* .iInterface = DYNAMIC */
 };
+#ifdef CONFIG_MODEM_SUPPORT
+static struct usb_cdc_header_desc gser_header_desc  = {
+	.bLength =		sizeof(gser_header_desc),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_HEADER_TYPE,
+	.bcdCDC =		__constant_cpu_to_le16(0x0110),
+};
 
+static struct usb_cdc_call_mgmt_descriptor
+gser_call_mgmt_descriptor  = {
+	.bLength =		sizeof(gser_call_mgmt_descriptor),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_CALL_MANAGEMENT_TYPE,
+	.bmCapabilities =	0,
+	/* .bDataInterface = DYNAMIC */
+};
+
+static struct usb_cdc_acm_descriptor gser_descriptor  = {
+	.bLength =		sizeof(gser_descriptor),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_ACM_TYPE,
+	.bmCapabilities =	USB_CDC_CAP_LINE,
+};
+
+static struct usb_cdc_union_desc gser_union_desc  = {
+	.bLength =		sizeof(gser_union_desc),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_UNION_TYPE,
+	/* .bMasterInterface0 =	DYNAMIC */
+	/* .bSlaveInterface0 =	DYNAMIC */
+};
+#endif
 /* full speed support: */
+#ifdef CONFIG_MODEM_SUPPORT
+static struct usb_endpoint_descriptor gser_fs_notify_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	__constant_cpu_to_le16(GS_NOTIFY_MAXPACKET),
+	.bInterval =		1 << GS_LOG2_NOTIFY_INTERVAL,
+};
+#endif
 
-static struct usb_endpoint_descriptor gser_fs_in_desc __initdata = {
+static struct usb_endpoint_descriptor gser_fs_in_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 	.bEndpointAddress =	USB_DIR_IN,
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
 };
 
-static struct usb_endpoint_descriptor gser_fs_out_desc __initdata = {
+static struct usb_endpoint_descriptor gser_fs_out_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 	.bEndpointAddress =	USB_DIR_OUT,
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
 };
 
-static struct usb_descriptor_header *gser_fs_function[] __initdata = {
+static struct usb_descriptor_header *gser_fs_function[] = {
 	(struct usb_descriptor_header *) &gser_interface_desc,
+#ifdef CONFIG_MODEM_SUPPORT
+	(struct usb_descriptor_header *) &gser_header_desc,
+	(struct usb_descriptor_header *) &gser_call_mgmt_descriptor,
+	(struct usb_descriptor_header *) &gser_descriptor,
+	(struct usb_descriptor_header *) &gser_union_desc,
+	(struct usb_descriptor_header *) &gser_fs_notify_desc,
+#endif
 	(struct usb_descriptor_header *) &gser_fs_in_desc,
 	(struct usb_descriptor_header *) &gser_fs_out_desc,
 	NULL,
 };
 
 /* high speed support: */
+#ifdef CONFIG_MODEM_SUPPORT
+static struct usb_endpoint_descriptor gser_hs_notify_desc  = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	__constant_cpu_to_le16(GS_NOTIFY_MAXPACKET),
+	.bInterval =		GS_LOG2_NOTIFY_INTERVAL+4,
+};
+#endif
 
-static struct usb_endpoint_descriptor gser_hs_in_desc __initdata = {
+static struct usb_endpoint_descriptor gser_hs_in_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
-	.wMaxPacketSize =	cpu_to_le16(512),
+	.wMaxPacketSize =	__constant_cpu_to_le16(512),
 };
 
-static struct usb_endpoint_descriptor gser_hs_out_desc __initdata = {
+static struct usb_endpoint_descriptor gser_hs_out_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
-	.wMaxPacketSize =	cpu_to_le16(512),
+	.wMaxPacketSize =	__constant_cpu_to_le16(512),
 };
 
-static struct usb_descriptor_header *gser_hs_function[] __initdata = {
+static struct usb_descriptor_header *gser_hs_function[] = {
 	(struct usb_descriptor_header *) &gser_interface_desc,
+#ifdef CONFIG_MODEM_SUPPORT
+	(struct usb_descriptor_header *) &gser_header_desc,
+	(struct usb_descriptor_header *) &gser_call_mgmt_descriptor,
+	(struct usb_descriptor_header *) &gser_descriptor,
+	(struct usb_descriptor_header *) &gser_union_desc,
+	(struct usb_descriptor_header *) &gser_hs_notify_desc,
+#endif
 	(struct usb_descriptor_header *) &gser_hs_in_desc,
 	(struct usb_descriptor_header *) &gser_hs_out_desc,
 	NULL,
@@ -144,18 +270,279 @@
 	NULL,
 };
 
+static int gport_setup(struct usb_configuration *c)
+{
+	int ret = 0;
+	int port_idx;
+	int i;
+
+	pr_debug("%s: no_tty_ports: %u no_sdio_ports: %u"
+		" no_smd_ports: %u no_hsic_sports: %u no_hsuart_ports: %u nr_ports: %u\n",
+			__func__, no_tty_ports, no_sdio_ports, no_smd_ports,
+			no_hsic_sports, no_hsuart_sports, nr_ports);
+
+	if (no_tty_ports)
+		ret = gserial_setup(c->cdev->gadget, no_tty_ports);
+	if (no_sdio_ports)
+		ret = gsdio_setup(c->cdev->gadget, no_sdio_ports);
+	if (no_smd_ports)
+		ret = gsmd_setup(c->cdev->gadget, no_smd_ports);
+	if (no_hsic_sports) {
+		port_idx = ghsic_data_setup(no_hsic_sports, USB_GADGET_SERIAL);
+		if (port_idx < 0)
+			return port_idx;
+
+		for (i = 0; i < nr_ports; i++) {
+			if (gserial_ports[i].transport ==
+					USB_GADGET_XPORT_HSIC) {
+				gserial_ports[i].client_port_num = port_idx;
+				port_idx++;
+			}
+		}
+
+		/*clinet port num is same for data setup and ctrl setup*/
+		ret = ghsic_ctrl_setup(no_hsic_sports, USB_GADGET_SERIAL);
+		if (ret < 0)
+			return ret;
+		return 0;
+	}
+	if (no_hsuart_sports) {
+		port_idx = ghsuart_data_setup(no_hsuart_sports,
+					USB_GADGET_SERIAL);
+		if (port_idx < 0)
+			return port_idx;
+
+		for (i = 0; i < nr_ports; i++) {
+			if (gserial_ports[i].transport ==
+					USB_GADGET_XPORT_HSUART) {
+				gserial_ports[i].client_port_num = port_idx;
+				port_idx++;
+			}
+		}
+
+		return 0;
+	}
+	return ret;
+}
+
+static int gport_connect(struct f_gser *gser)
+{
+	unsigned	port_num;
+	int		ret;
+
+	pr_debug("%s: transport: %s f_gser: %p gserial: %p port_num: %d\n",
+			__func__, xport_to_str(gser->transport),
+			gser, &gser->port, gser->port_num);
+
+	port_num = gserial_ports[gser->port_num].client_port_num;
+
+	switch (gser->transport) {
+	case USB_GADGET_XPORT_TTY:
+		gserial_connect(&gser->port, port_num);
+		break;
+	case USB_GADGET_XPORT_SDIO:
+		gsdio_connect(&gser->port, port_num);
+		break;
+	case USB_GADGET_XPORT_SMD:
+		gsmd_connect(&gser->port, port_num);
+		break;
+	case USB_GADGET_XPORT_HSIC:
+		ret = ghsic_ctrl_connect(&gser->port, port_num);
+		if (ret) {
+			pr_err("%s: ghsic_ctrl_connect failed: err:%d\n",
+					__func__, ret);
+			return ret;
+		}
+		ret = ghsic_data_connect(&gser->port, port_num);
+		if (ret) {
+			pr_err("%s: ghsic_data_connect failed: err:%d\n",
+					__func__, ret);
+			ghsic_ctrl_disconnect(&gser->port, port_num);
+			return ret;
+		}
+		break;
+	case USB_GADGET_XPORT_HSUART:
+		ret = ghsuart_data_connect(&gser->port, port_num);
+		if (ret) {
+			pr_err("%s: ghsuart_data_connect failed: err:%d\n",
+					__func__, ret);
+			return ret;
+		}
+		break;
+	default:
+		pr_err("%s: Un-supported transport: %s\n", __func__,
+				xport_to_str(gser->transport));
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int gport_disconnect(struct f_gser *gser)
+{
+	unsigned port_num;
+
+	pr_debug("%s: transport: %s f_gser: %p gserial: %p port_num: %d\n",
+			__func__, xport_to_str(gser->transport),
+			gser, &gser->port, gser->port_num);
+
+	port_num = gserial_ports[gser->port_num].client_port_num;
+
+	switch (gser->transport) {
+	case USB_GADGET_XPORT_TTY:
+		gserial_disconnect(&gser->port);
+		break;
+	case USB_GADGET_XPORT_SDIO:
+		gsdio_disconnect(&gser->port, port_num);
+		break;
+	case USB_GADGET_XPORT_SMD:
+		gsmd_disconnect(&gser->port, port_num);
+		break;
+	case USB_GADGET_XPORT_HSIC:
+		ghsic_ctrl_disconnect(&gser->port, port_num);
+		ghsic_data_disconnect(&gser->port, port_num);
+		break;
+	case USB_GADGET_XPORT_HSUART:
+		ghsuart_data_disconnect(&gser->port, port_num);
+		break;
+	default:
+		pr_err("%s: Un-supported transport:%s\n", __func__,
+				xport_to_str(gser->transport));
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_MODEM_SUPPORT
+static void gser_complete_set_line_coding(struct usb_ep *ep,
+		struct usb_request *req)
+{
+	struct f_gser            *gser = ep->driver_data;
+	struct usb_composite_dev *cdev = gser->port.func.config->cdev;
+
+	if (req->status != 0) {
+		DBG(cdev, "gser ttyGS%d completion, err %d\n",
+				gser->port_num, req->status);
+		return;
+	}
+
+	/* normal completion */
+	if (req->actual != sizeof(gser->port_line_coding)) {
+		DBG(cdev, "gser ttyGS%d short resp, len %d\n",
+				gser->port_num, req->actual);
+		usb_ep_set_halt(ep);
+	} else {
+		struct usb_cdc_line_coding	*value = req->buf;
+		gser->port_line_coding = *value;
+	}
+}
 /*-------------------------------------------------------------------------*/
 
+static int
+gser_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+	struct f_gser            *gser = func_to_gser(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct usb_request	 *req = cdev->req;
+	int			 value = -EOPNOTSUPP;
+	u16			 w_index = le16_to_cpu(ctrl->wIndex);
+	u16			 w_value = le16_to_cpu(ctrl->wValue);
+	u16			 w_length = le16_to_cpu(ctrl->wLength);
+
+	switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+
+	/* SET_LINE_CODING ... just read and save what the host sends */
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_REQ_SET_LINE_CODING:
+		if (w_length != sizeof(struct usb_cdc_line_coding))
+			goto invalid;
+
+		value = w_length;
+		cdev->gadget->ep0->driver_data = gser;
+		req->complete = gser_complete_set_line_coding;
+		break;
+
+	/* GET_LINE_CODING ... return what host sent, or initial value */
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_REQ_GET_LINE_CODING:
+		value = min_t(unsigned, w_length,
+				sizeof(struct usb_cdc_line_coding));
+		memcpy(req->buf, &gser->port_line_coding, value);
+		break;
+
+	/* SET_CONTROL_LINE_STATE ... save what the host sent */
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_REQ_SET_CONTROL_LINE_STATE:
+
+		value = 0;
+		gser->port_handshake_bits = w_value;
+		if (gser->port.notify_modem) {
+			unsigned port_num =
+				gserial_ports[gser->port_num].client_port_num;
+
+			gser->port.notify_modem(&gser->port,
+					port_num, w_value);
+		}
+		break;
+
+	default:
+invalid:
+		DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+	}
+
+	/* respond with data transfer or status phase? */
+	if (value >= 0) {
+		DBG(cdev, "gser ttyGS%d req%02x.%02x v%04x i%04x l%d\n",
+			gser->port_num, ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+		req->zero = 0;
+		req->length = value;
+		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+		if (value < 0)
+			ERROR(cdev, "gser response on ttyGS%d, err %d\n",
+					gser->port_num, value);
+	}
+
+	/* device either stalls (value < 0) or reports success */
+	return value;
+}
+#endif
 static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
 {
 	struct f_gser		*gser = func_to_gser(f);
 	struct usb_composite_dev *cdev = f->config->cdev;
+	int rc = 0;
 
 	/* we know alt == 0, so this is an activation or a reset */
 
+#ifdef CONFIG_MODEM_SUPPORT
+	if (gser->notify->driver_data) {
+		DBG(cdev, "reset generic ctl ttyGS%d\n", gser->port_num);
+		usb_ep_disable(gser->notify);
+	}
+
+	if (!gser->notify->desc) {
+		if (config_ep_by_speed(cdev->gadget, f, gser->notify)) {
+			gser->notify->desc = NULL;
+			return -EINVAL;
+		}
+	}
+	rc = usb_ep_enable(gser->notify);
+
+	if (rc) {
+		ERROR(cdev, "can't enable %s, result %d\n",
+					gser->notify->name, rc);
+		return rc;
+	}
+	gser->notify->driver_data = gser;
+#endif
+
 	if (gser->port.in->driver_data) {
-		DBG(cdev, "reset generic ttyGS%d\n", gser->port_num);
-		gserial_disconnect(&gser->port);
+		DBG(cdev, "reset generic data ttyGS%d\n", gser->port_num);
+		gport_disconnect(gser);
 	}
 	if (!gser->port.in->desc || !gser->port.out->desc) {
 		DBG(cdev, "activate generic ttyGS%d\n", gser->port_num);
@@ -166,8 +553,11 @@
 			return -EINVAL;
 		}
 	}
-	gserial_connect(&gser->port, gser->port_num);
-	return 0;
+
+	gport_connect(gser);
+
+	gser->online = 1;
+	return rc;
 }
 
 static void gser_disable(struct usb_function *f)
@@ -176,14 +566,185 @@
 	struct usb_composite_dev *cdev = f->config->cdev;
 
 	DBG(cdev, "generic ttyGS%d deactivated\n", gser->port_num);
-	gserial_disconnect(&gser->port);
+
+	gport_disconnect(gser);
+
+#ifdef CONFIG_MODEM_SUPPORT
+	usb_ep_fifo_flush(gser->notify);
+	usb_ep_disable(gser->notify);
+#endif
+	gser->online = 0;
+}
+#ifdef CONFIG_MODEM_SUPPORT
+static int gser_notify(struct f_gser *gser, u8 type, u16 value,
+		void *data, unsigned length)
+{
+	struct usb_ep			*ep = gser->notify;
+	struct usb_request		*req;
+	struct usb_cdc_notification	*notify;
+	const unsigned			len = sizeof(*notify) + length;
+	void				*buf;
+	int				status;
+	struct usb_composite_dev *cdev = gser->port.func.config->cdev;
+
+	req = gser->notify_req;
+	gser->notify_req = NULL;
+	gser->pending = false;
+
+	req->length = len;
+	notify = req->buf;
+	buf = notify + 1;
+
+	notify->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
+			| USB_RECIP_INTERFACE;
+	notify->bNotificationType = type;
+	notify->wValue = cpu_to_le16(value);
+	notify->wIndex = cpu_to_le16(gser->data_id);
+	notify->wLength = cpu_to_le16(length);
+	memcpy(buf, data, length);
+
+	status = usb_ep_queue(ep, req, GFP_ATOMIC);
+	if (status < 0) {
+		ERROR(cdev, "gser ttyGS%d can't notify serial state, %d\n",
+				gser->port_num, status);
+		gser->notify_req = req;
+	}
+
+	return status;
 }
 
+static int gser_notify_serial_state(struct f_gser *gser)
+{
+	int			 status;
+	unsigned long flags;
+	struct usb_composite_dev *cdev = gser->port.func.config->cdev;
+
+	spin_lock_irqsave(&gser->lock, flags);
+	if (gser->notify_req) {
+		DBG(cdev, "gser ttyGS%d serial state %04x\n",
+				gser->port_num, gser->serial_state);
+		status = gser_notify(gser, USB_CDC_NOTIFY_SERIAL_STATE,
+				0, &gser->serial_state,
+					sizeof(gser->serial_state));
+	} else {
+		gser->pending = true;
+		status = 0;
+	}
+	spin_unlock_irqrestore(&gser->lock, flags);
+	return status;
+}
+
+static void gser_notify_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_gser *gser = req->context;
+	u8	      doit = false;
+	unsigned long flags;
+
+	/* on this call path we do NOT hold the port spinlock,
+	 * which is why ACM needs its own spinlock
+	 */
+	spin_lock_irqsave(&gser->lock, flags);
+	if (req->status != -ESHUTDOWN)
+		doit = gser->pending;
+	gser->notify_req = req;
+	spin_unlock_irqrestore(&gser->lock, flags);
+
+	if (doit && gser->online)
+		gser_notify_serial_state(gser);
+}
+static void gser_connect(struct gserial *port)
+{
+	struct f_gser *gser = port_to_gser(port);
+
+	gser->serial_state |= ACM_CTRL_DSR | ACM_CTRL_DCD;
+	gser_notify_serial_state(gser);
+}
+
+unsigned int gser_get_dtr(struct gserial *port)
+{
+	struct f_gser *gser = port_to_gser(port);
+
+	if (gser->port_handshake_bits & ACM_CTRL_DTR)
+		return 1;
+	else
+		return 0;
+}
+
+unsigned int gser_get_rts(struct gserial *port)
+{
+	struct f_gser *gser = port_to_gser(port);
+
+	if (gser->port_handshake_bits & ACM_CTRL_RTS)
+		return 1;
+	else
+		return 0;
+}
+
+unsigned int gser_send_carrier_detect(struct gserial *port, unsigned int yes)
+{
+	struct f_gser *gser = port_to_gser(port);
+	u16			state;
+
+	state = gser->serial_state;
+	state &= ~ACM_CTRL_DCD;
+	if (yes)
+		state |= ACM_CTRL_DCD;
+
+	gser->serial_state = state;
+	return gser_notify_serial_state(gser);
+
+}
+
+unsigned int gser_send_ring_indicator(struct gserial *port, unsigned int yes)
+{
+	struct f_gser *gser = port_to_gser(port);
+	u16			state;
+
+	state = gser->serial_state;
+	state &= ~ACM_CTRL_RI;
+	if (yes)
+		state |= ACM_CTRL_RI;
+
+	gser->serial_state = state;
+	return gser_notify_serial_state(gser);
+
+}
+static void gser_disconnect(struct gserial *port)
+{
+	struct f_gser *gser = port_to_gser(port);
+
+	gser->serial_state &= ~(ACM_CTRL_DSR | ACM_CTRL_DCD);
+	gser_notify_serial_state(gser);
+}
+
+static int gser_send_break(struct gserial *port, int duration)
+{
+	struct f_gser *gser = port_to_gser(port);
+	u16			state;
+
+	state = gser->serial_state;
+	state &= ~ACM_CTRL_BRK;
+	if (duration)
+		state |= ACM_CTRL_BRK;
+
+	gser->serial_state = state;
+	return gser_notify_serial_state(gser);
+}
+
+static int gser_send_modem_ctrl_bits(struct gserial *port, int ctrl_bits)
+{
+	struct f_gser *gser = port_to_gser(port);
+
+	gser->serial_state = ctrl_bits;
+
+	return gser_notify_serial_state(gser);
+}
+#endif
 /*-------------------------------------------------------------------------*/
 
 /* serial function driver setup/binding */
 
-static int __init
+static int
 gser_bind(struct usb_configuration *c, struct usb_function *f)
 {
 	struct usb_composite_dev *cdev = c->cdev;
@@ -213,9 +774,29 @@
 	gser->port.out = ep;
 	ep->driver_data = cdev;	/* claim */
 
+#ifdef CONFIG_MODEM_SUPPORT
+	ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_notify_desc);
+	if (!ep)
+		goto fail;
+	gser->notify = ep;
+	ep->driver_data = cdev;	/* claim */
+	/* allocate notification */
+	gser->notify_req = gs_alloc_req(ep,
+			sizeof(struct usb_cdc_notification) + 2,
+			GFP_KERNEL);
+	if (!gser->notify_req)
+		goto fail;
+
+	gser->notify_req->complete = gser_notify_complete;
+	gser->notify_req->context = gser;
+#endif
+
 	/* copy descriptors, and track endpoint copies */
 	f->descriptors = usb_copy_descriptors(gser_fs_function);
 
+	if (!f->descriptors)
+		goto fail;
+
 	/* support all relevant hardware speeds... we expect that when
 	 * hardware is dual speed, all bulk-capable endpoints work at
 	 * both speeds
@@ -225,9 +806,17 @@
 				gser_fs_in_desc.bEndpointAddress;
 		gser_hs_out_desc.bEndpointAddress =
 				gser_fs_out_desc.bEndpointAddress;
+#ifdef CONFIG_MODEM_SUPPORT
+		gser_hs_notify_desc.bEndpointAddress =
+				gser_fs_notify_desc.bEndpointAddress;
+#endif
 
 		/* copy descriptors, and track endpoint copies */
 		f->hs_descriptors = usb_copy_descriptors(gser_hs_function);
+
+		if (!f->hs_descriptors)
+			goto fail;
+
 	}
 	if (gadget_is_superspeed(c->cdev->gadget)) {
 		gser_ss_in_desc.bEndpointAddress =
@@ -249,6 +838,16 @@
 	return 0;
 
 fail:
+	if (f->descriptors)
+		usb_free_descriptors(f->descriptors);
+#ifdef CONFIG_MODEM_SUPPORT
+	if (gser->notify_req)
+		gs_free_req(gser->notify, gser->notify_req);
+
+	/* we might as well release our claims on endpoints */
+	if (gser->notify)
+		gser->notify->driver_data = NULL;
+#endif
 	/* we might as well release our claims on endpoints */
 	if (gser->port.out)
 		gser->port.out->driver_data = NULL;
@@ -263,11 +862,17 @@
 static void
 gser_unbind(struct usb_configuration *c, struct usb_function *f)
 {
+#ifdef CONFIG_MODEM_SUPPORT
+	struct f_gser *gser = func_to_gser(f);
+#endif
 	if (gadget_is_dualspeed(c->cdev->gadget))
 		usb_free_descriptors(f->hs_descriptors);
 	if (gadget_is_superspeed(c->cdev->gadget))
 		usb_free_descriptors(f->ss_descriptors);
 	usb_free_descriptors(f->descriptors);
+#ifdef CONFIG_MODEM_SUPPORT
+	gs_free_req(gser->notify, gser->notify_req);
+#endif
 	kfree(func_to_gser(f));
 }
 
@@ -283,7 +888,7 @@
  * handle all the ones it binds.  Caller is also responsible
  * for calling @gserial_cleanup() before module unload.
  */
-int __init gser_bind_config(struct usb_configuration *c, u8 port_num)
+int gser_bind_config(struct usb_configuration *c, u8 port_num)
 {
 	struct f_gser	*gser;
 	int		status;
@@ -305,6 +910,9 @@
 	if (!gser)
 		return -ENOMEM;
 
+#ifdef CONFIG_MODEM_SUPPORT
+	spin_lock_init(&gser->lock);
+#endif
 	gser->port_num = port_num;
 
 	gser->port.func.name = "gser";
@@ -313,9 +921,77 @@
 	gser->port.func.unbind = gser_unbind;
 	gser->port.func.set_alt = gser_set_alt;
 	gser->port.func.disable = gser_disable;
+	gser->transport		= gserial_ports[port_num].transport;
+#ifdef CONFIG_MODEM_SUPPORT
+	/* We support only three ports for now */
+	if (port_num == 0)
+		gser->port.func.name = "modem";
+	else if (port_num == 1)
+		gser->port.func.name = "nmea";
+	else
+		gser->port.func.name = "modem2";
+	gser->port.func.setup = gser_setup;
+	gser->port.connect = gser_connect;
+	gser->port.get_dtr = gser_get_dtr;
+	gser->port.get_rts = gser_get_rts;
+	gser->port.send_carrier_detect = gser_send_carrier_detect;
+	gser->port.send_ring_indicator = gser_send_ring_indicator;
+	gser->port.send_modem_ctrl_bits = gser_send_modem_ctrl_bits;
+	gser->port.disconnect = gser_disconnect;
+	gser->port.send_break = gser_send_break;
+#endif
 
 	status = usb_add_function(c, &gser->port.func);
 	if (status)
 		kfree(gser);
 	return status;
 }
+
+/**
+ * gserial_init_port - bind a gserial_port to its transport
+ */
+static int gserial_init_port(int port_num, const char *name)
+{
+	enum transport_type transport;
+
+	if (port_num >= GSERIAL_NO_PORTS)
+		return -ENODEV;
+
+	transport = str_to_xport(name);
+	pr_debug("%s, port:%d, transport:%s\n", __func__,
+			port_num, xport_to_str(transport));
+
+	gserial_ports[port_num].transport = transport;
+	gserial_ports[port_num].port_num = port_num;
+
+	switch (transport) {
+	case USB_GADGET_XPORT_TTY:
+		gserial_ports[port_num].client_port_num = no_tty_ports;
+		no_tty_ports++;
+		break;
+	case USB_GADGET_XPORT_SDIO:
+		gserial_ports[port_num].client_port_num = no_sdio_ports;
+		no_sdio_ports++;
+		break;
+	case USB_GADGET_XPORT_SMD:
+		gserial_ports[port_num].client_port_num = no_smd_ports;
+		no_smd_ports++;
+		break;
+	case USB_GADGET_XPORT_HSIC:
+		/*client port number will be updated in gport_setup*/
+		no_hsic_sports++;
+		break;
+	case USB_GADGET_XPORT_HSUART:
+		/*client port number will be updated in gport_setup*/
+		no_hsuart_sports++;
+		break;
+	default:
+		pr_err("%s: Un-supported transport transport: %u\n",
+				__func__, gserial_ports[port_num].transport);
+		return -ENODEV;
+	}
+
+	nr_ports++;
+
+	return 0;
+}
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index a8855d0..14873a0 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -29,6 +29,7 @@
 #define gadget_is_at91(g)		(!strcmp("at91_udc", (g)->name))
 #define gadget_is_atmel_usba(g)		(!strcmp("atmel_usba_udc", (g)->name))
 #define gadget_is_ci13xxx_msm(g)	(!strcmp("ci13xxx_msm", (g)->name))
+#define gadget_is_ci13xxx_msm_hsic(g)	(!strcmp("ci13xxx_msm_hsic", (g)->name))
 #define gadget_is_ci13xxx_pci(g)	(!strcmp("ci13xxx_pci", (g)->name))
 #define gadget_is_dummy(g)		(!strcmp("dummy_udc", (g)->name))
 #define gadget_is_dwc3(g)		(!strcmp("dwc3-gadget", (g)->name))
@@ -38,6 +39,7 @@
 #define gadget_is_imx(g)		(!strcmp("imx_udc", (g)->name))
 #define gadget_is_langwell(g)		(!strcmp("langwell_udc", (g)->name))
 #define gadget_is_m66592(g)		(!strcmp("m66592_udc", (g)->name))
+#define gadget_is_msm72k(g)		(!strcmp("msm72k_udc", (g)->name))
 #define gadget_is_musbhdrc(g)		(!strcmp("musb-hdrc", (g)->name))
 #define gadget_is_net2272(g)		(!strcmp("net2272", (g)->name))
 #define gadget_is_net2280(g)		(!strcmp("net2280", (g)->name))
@@ -118,6 +120,10 @@
 		return 0x31;
 	else if (gadget_is_dwc3(gadget))
 		return 0x32;
+	else if (gadget_is_msm72k(gadget))
+		return 0x33;
+	else if (gadget_is_ci13xxx_msm_hsic(gadget))
+		return 0x34;
 
 	return -ENOENT;
 }
diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c
new file mode 100644
index 0000000..a025d95
--- /dev/null
+++ b/drivers/usb/gadget/msm72k_udc.c
@@ -0,0 +1,2834 @@
+/*
+ * Driver for HighSpeed USB Client Controller in MSM7K
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *         Brian Swetland <swetland@google.com>
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/workqueue.h>
+#include <linux/switch.h>
+#include <linux/pm_runtime.h>
+
+#include <mach/msm72k_otg.h>
+#include <linux/io.h>
+
+#include <asm/mach-types.h>
+
+#include <mach/board.h>
+#include <mach/msm_hsusb.h>
+#include <linux/device.h>
+#include <mach/msm_hsusb_hw.h>
+#include <mach/clk.h>
+#include <linux/uaccess.h>
+#include <linux/wakelock.h>
+
+static const char driver_name[] = "msm72k_udc";
+
+/* #define DEBUG */
+/* #define VERBOSE */
+
+#define MSM_USB_BASE ((unsigned) ui->addr)
+
+#define	DRIVER_DESC		"MSM 72K USB Peripheral Controller"
+#define	DRIVER_NAME		"MSM72K_UDC"
+
+#define EPT_FLAG_IN        0x0001
+
+#define SETUP_BUF_SIZE     8
+
+
+static const char *const ep_name[] = {
+	"ep0out", "ep1out", "ep2out", "ep3out",
+	"ep4out", "ep5out", "ep6out", "ep7out",
+	"ep8out", "ep9out", "ep10out", "ep11out",
+	"ep12out", "ep13out", "ep14out", "ep15out",
+	"ep0in", "ep1in", "ep2in", "ep3in",
+	"ep4in", "ep5in", "ep6in", "ep7in",
+	"ep8in", "ep9in", "ep10in", "ep11in",
+	"ep12in", "ep13in", "ep14in", "ep15in"
+};
+
+/*To release the wakelock from debugfs*/
+static int release_wlocks;
+
+struct msm_request {
+	struct usb_request req;
+
+	/* saved copy of req.complete */
+	void	(*gadget_complete)(struct usb_ep *ep,
+					struct usb_request *req);
+
+
+	struct usb_info *ui;
+	struct msm_request *next;
+	struct msm_request *prev;
+
+	unsigned busy:1;
+	unsigned live:1;
+	unsigned alloced:1;
+
+	dma_addr_t dma;
+	dma_addr_t item_dma;
+
+	struct ept_queue_item *item;
+};
+
+#define to_msm_request(r) container_of(r, struct msm_request, req)
+#define to_msm_endpoint(r) container_of(r, struct msm_endpoint, ep)
+#define to_msm_otg(xceiv)  container_of(xceiv, struct msm_otg, phy)
+#define is_b_sess_vld()	((OTGSC_BSV & readl(USB_OTGSC)) ? 1 : 0)
+#define is_usb_online(ui) (ui->usb_state != USB_STATE_NOTATTACHED)
+
+struct msm_endpoint {
+	struct usb_ep ep;
+	struct usb_info *ui;
+	struct msm_request *req; /* head of pending requests */
+	struct msm_request *last;
+	unsigned flags;
+
+	/* bit number (0-31) in various status registers
+	** as well as the index into the usb_info's array
+	** of all endpoints
+	*/
+	unsigned char bit;
+	unsigned char num;
+	unsigned long dTD_update_fail_count;
+	unsigned long false_prime_fail_count;
+	unsigned actual_prime_fail_count;
+
+	unsigned wedged:1;
+	/* pointers to DMA transfer list area */
+	/* these are allocated from the usb_info dma space */
+	struct ept_queue_head *head;
+	struct timer_list prime_timer;
+};
+
+/* PHY status check timer to monitor phy stuck up on reset */
+static struct timer_list phy_status_timer;
+
+static void ept_prime_timer_func(unsigned long data);
+static void usb_do_work(struct work_struct *w);
+static void usb_do_remote_wakeup(struct work_struct *w);
+
+
+#define USB_STATE_IDLE    0
+#define USB_STATE_ONLINE  1
+#define USB_STATE_OFFLINE 2
+
+#define USB_FLAG_START          0x0001
+#define USB_FLAG_VBUS_ONLINE    0x0002
+#define USB_FLAG_VBUS_OFFLINE   0x0004
+#define USB_FLAG_RESET          0x0008
+#define USB_FLAG_SUSPEND        0x0010
+#define USB_FLAG_CONFIGURED     0x0020
+
+#define USB_CHG_DET_DELAY	msecs_to_jiffies(1000)
+#define REMOTE_WAKEUP_DELAY	msecs_to_jiffies(1000)
+#define PHY_STATUS_CHECK_DELAY	(jiffies + msecs_to_jiffies(1000))
+#define EPT_PRIME_CHECK_DELAY	(jiffies + msecs_to_jiffies(1000))
+
+struct usb_info {
+	/* lock for register/queue/device state changes */
+	spinlock_t lock;
+
+	/* single request used for handling setup transactions */
+	struct usb_request *setup_req;
+
+	struct platform_device *pdev;
+	int irq;
+	void *addr;
+
+	unsigned state;
+	unsigned flags;
+
+	atomic_t configured;
+	atomic_t running;
+
+	struct dma_pool *pool;
+
+	/* dma page to back the queue heads and items */
+	unsigned char *buf;
+	dma_addr_t dma;
+
+	struct ept_queue_head *head;
+
+	/* used for allocation */
+	unsigned next_item;
+	unsigned next_ifc_num;
+
+	/* endpoints are ordered based on their status bits,
+	** so they are OUT0, OUT1, ... OUT15, IN0, IN1, ... IN15
+	*/
+	struct msm_endpoint ept[32];
+
+
+	/* max power requested by selected configuration */
+	unsigned b_max_pow;
+	unsigned chg_current;
+	struct delayed_work chg_det;
+	struct delayed_work chg_stop;
+	struct msm_hsusb_gadget_platform_data *pdata;
+	struct work_struct phy_status_check;
+
+	struct work_struct work;
+	unsigned phy_status;
+	unsigned phy_fail_count;
+	unsigned prime_fail_count;
+	unsigned long dTD_update_fail_count;
+
+	struct usb_gadget		gadget;
+	struct usb_gadget_driver	*driver;
+	struct switch_dev sdev;
+
+#define ep0out ept[0]
+#define ep0in  ept[16]
+
+	atomic_t ep0_dir;
+	atomic_t test_mode;
+	atomic_t offline_pending;
+	atomic_t softconnect;
+#ifdef CONFIG_USB_OTG
+	u8 hnp_avail;
+#endif
+
+	atomic_t remote_wakeup;
+	atomic_t self_powered;
+	struct delayed_work rw_work;
+
+	struct usb_phy *xceiv;
+	enum usb_device_state usb_state;
+	struct wake_lock	wlock;
+};
+
+static const struct usb_ep_ops msm72k_ep_ops;
+static struct usb_info *the_usb_info;
+
+static int msm72k_wakeup(struct usb_gadget *_gadget);
+static int msm72k_pullup_internal(struct usb_gadget *_gadget, int is_active);
+static int msm72k_set_halt(struct usb_ep *_ep, int value);
+static void flush_endpoint(struct msm_endpoint *ept);
+static void usb_reset(struct usb_info *ui);
+static int usb_ept_set_halt(struct usb_ep *_ep, int value);
+
+static void msm_hsusb_set_speed(struct usb_info *ui)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&ui->lock, flags);
+	switch (readl(USB_PORTSC) & PORTSC_PSPD_MASK) {
+	case PORTSC_PSPD_FS:
+		dev_dbg(&ui->pdev->dev, "portchange USB_SPEED_FULL\n");
+		ui->gadget.speed = USB_SPEED_FULL;
+		break;
+	case PORTSC_PSPD_LS:
+		dev_dbg(&ui->pdev->dev, "portchange USB_SPEED_LOW\n");
+		ui->gadget.speed = USB_SPEED_LOW;
+		break;
+	case PORTSC_PSPD_HS:
+		dev_dbg(&ui->pdev->dev, "portchange USB_SPEED_HIGH\n");
+		ui->gadget.speed = USB_SPEED_HIGH;
+		break;
+	}
+	spin_unlock_irqrestore(&ui->lock, flags);
+}
+
+static void msm_hsusb_set_state(enum usb_device_state state)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&the_usb_info->lock, flags);
+	the_usb_info->usb_state = state;
+	spin_unlock_irqrestore(&the_usb_info->lock, flags);
+}
+
+static enum usb_device_state msm_hsusb_get_state(void)
+{
+	unsigned long flags;
+	enum usb_device_state state;
+
+	spin_lock_irqsave(&the_usb_info->lock, flags);
+	state = the_usb_info->usb_state;
+	spin_unlock_irqrestore(&the_usb_info->lock, flags);
+
+	return state;
+}
+
+static ssize_t print_switch_name(struct switch_dev *sdev, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", DRIVER_NAME);
+}
+
+static ssize_t print_switch_state(struct switch_dev *sdev, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n",
+		sdev->state ? "online" : "offline");
+}
+
+static inline enum chg_type usb_get_chg_type(struct usb_info *ui)
+{
+	if ((readl(USB_PORTSC) & PORTSC_LS) == PORTSC_LS)
+		return USB_CHG_TYPE__WALLCHARGER;
+	else
+		return USB_CHG_TYPE__SDP;
+}
+
+#define USB_WALLCHARGER_CHG_CURRENT 1800
+static int usb_get_max_power(struct usb_info *ui)
+{
+	struct msm_otg *otg = to_msm_otg(ui->xceiv);
+	unsigned long flags;
+	enum chg_type temp;
+	int suspended;
+	int configured;
+	unsigned bmaxpow;
+
+	if (ui->gadget.is_a_peripheral)
+		return -EINVAL;
+
+	temp = atomic_read(&otg->chg_type);
+	spin_lock_irqsave(&ui->lock, flags);
+	suspended = ui->usb_state == USB_STATE_SUSPENDED ? 1 : 0;
+	configured = atomic_read(&ui->configured);
+	bmaxpow = ui->b_max_pow;
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	if (temp == USB_CHG_TYPE__INVALID)
+		return -ENODEV;
+
+	if (temp == USB_CHG_TYPE__WALLCHARGER)
+		return USB_WALLCHARGER_CHG_CURRENT;
+
+	if (suspended || !configured)
+		return 0;
+
+	return bmaxpow;
+}
+
+static int usb_phy_stuck_check(struct usb_info *ui)
+{
+	/*
+	 * write some value (0xAA) into scratch reg (0x16) and read it back,
+	 * If the read value is same as written value, means PHY is normal
+	 * otherwise, PHY seems to have stuck.
+	 */
+
+	if (usb_phy_io_write(ui->xceiv, 0xAA, 0x16) == -1) {
+		dev_dbg(&ui->pdev->dev,
+				"%s(): ulpi write timeout\n", __func__);
+		return -EIO;
+	}
+
+	if (usb_phy_io_read(ui->xceiv, 0x16) != 0xAA) {
+		dev_dbg(&ui->pdev->dev,
+				"%s(): read value is incorrect\n", __func__);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/*
+ * This function checks the phy status by reading/writing to the
+ * phy scratch register. If the phy is stuck resets the HW
+ * */
+static void usb_phy_stuck_recover(struct work_struct *w)
+{
+	struct usb_info *ui = the_usb_info;
+	struct msm_otg *otg = to_msm_otg(ui->xceiv);
+	unsigned long flags;
+
+	spin_lock_irqsave(&ui->lock, flags);
+	if (ui->gadget.speed != USB_SPEED_UNKNOWN ||
+			ui->usb_state == USB_STATE_NOTATTACHED ||
+			ui->driver == NULL) {
+		spin_unlock_irqrestore(&ui->lock, flags);
+		return;
+	}
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	disable_irq(otg->irq);
+	if (usb_phy_stuck_check(ui)) {
+#ifdef CONFIG_USB_MSM_ACA
+		del_timer_sync(&otg->id_timer);
+#endif
+		ui->phy_fail_count++;
+		dev_err(&ui->pdev->dev,
+				"%s():PHY stuck, resetting HW\n", __func__);
+		/*
+		 * PHY seems to have stuck,
+		 * reset the PHY and HW link to recover the PHY
+		 */
+		usb_reset(ui);
+#ifdef CONFIG_USB_MSM_ACA
+		mod_timer(&otg->id_timer, jiffies +
+				 msecs_to_jiffies(OTG_ID_POLL_MS));
+#endif
+		msm72k_pullup_internal(&ui->gadget, 1);
+	}
+	enable_irq(otg->irq);
+}
+
+static void usb_phy_status_check_timer(unsigned long data)
+{
+	struct usb_info *ui = the_usb_info;
+
+	schedule_work(&ui->phy_status_check);
+}
+
+static void usb_chg_stop(struct work_struct *w)
+{
+	struct usb_info *ui = container_of(w, struct usb_info, chg_stop.work);
+	struct msm_otg *otg = to_msm_otg(ui->xceiv);
+	enum chg_type temp;
+
+	temp = atomic_read(&otg->chg_type);
+
+	if (temp == USB_CHG_TYPE__SDP)
+		usb_phy_set_power(ui->xceiv, 0);
+}
+
+static void usb_chg_detect(struct work_struct *w)
+{
+	struct usb_info *ui = container_of(w, struct usb_info, chg_det.work);
+	struct msm_otg *otg = to_msm_otg(ui->xceiv);
+	enum chg_type temp = USB_CHG_TYPE__INVALID;
+	unsigned long flags;
+	int maxpower;
+
+	spin_lock_irqsave(&ui->lock, flags);
+	if (ui->usb_state == USB_STATE_NOTATTACHED) {
+		spin_unlock_irqrestore(&ui->lock, flags);
+		return;
+	}
+
+	temp = usb_get_chg_type(ui);
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	atomic_set(&otg->chg_type, temp);
+	maxpower = usb_get_max_power(ui);
+	if (maxpower > 0)
+		usb_phy_set_power(ui->xceiv, maxpower);
+
+	/* USB driver prevents idle and suspend power collapse(pc)
+	 * while USB cable is connected. But when dedicated charger is
+	 * connected, driver can vote for idle and suspend pc.
+	 * OTG driver handles idle pc as part of above usb_phy_set_power call
+	 * when wallcharger is attached. To allow suspend pc, release the
+	 * wakelock which will be re-acquired for any sub-sequent usb interrupts
+	 * */
+	if (temp == USB_CHG_TYPE__WALLCHARGER) {
+		pm_runtime_put_sync(&ui->pdev->dev);
+		wake_unlock(&ui->wlock);
+	}
+}
+
+static int usb_ep_get_stall(struct msm_endpoint *ept)
+{
+	unsigned int n;
+	struct usb_info *ui = ept->ui;
+
+	n = readl(USB_ENDPTCTRL(ept->num));
+	if (ept->flags & EPT_FLAG_IN)
+		return (CTRL_TXS & n) ? 1 : 0;
+	else
+		return (CTRL_RXS & n) ? 1 : 0;
+}
+
+static void init_endpoints(struct usb_info *ui)
+{
+	unsigned n;
+
+	for (n = 0; n < 32; n++) {
+		struct msm_endpoint *ept = ui->ept + n;
+
+		ept->ui = ui;
+		ept->bit = n;
+		ept->num = n & 15;
+		ept->ep.name = ep_name[n];
+		ept->ep.ops = &msm72k_ep_ops;
+
+		if (ept->bit > 15) {
+			/* IN endpoint */
+			ept->head = ui->head + (ept->num << 1) + 1;
+			ept->flags = EPT_FLAG_IN;
+		} else {
+			/* OUT endpoint */
+			ept->head = ui->head + (ept->num << 1);
+			ept->flags = 0;
+		}
+		setup_timer(&ept->prime_timer, ept_prime_timer_func,
+			(unsigned long) ept);
+
+	}
+}
+
+static void config_ept(struct msm_endpoint *ept)
+{
+	struct usb_info *ui = ept->ui;
+	unsigned cfg = CONFIG_MAX_PKT(ept->ep.maxpacket) | CONFIG_ZLT;
+
+	/* ep0 out needs interrupt-on-setup */
+	if (ept->bit == 0)
+		cfg |= CONFIG_IOS;
+
+	ept->head->config = cfg;
+	ept->head->next = TERMINATE;
+
+	if (ept->ep.maxpacket)
+		dev_dbg(&ui->pdev->dev,
+			"ept #%d %s max:%d head:%p bit:%d\n",
+		       ept->num,
+		       (ept->flags & EPT_FLAG_IN) ? "in" : "out",
+		       ept->ep.maxpacket, ept->head, ept->bit);
+}
+
+static void configure_endpoints(struct usb_info *ui)
+{
+	unsigned n;
+
+	for (n = 0; n < 32; n++)
+		config_ept(ui->ept + n);
+}
+
+struct usb_request *usb_ept_alloc_req(struct msm_endpoint *ept,
+			unsigned bufsize, gfp_t gfp_flags)
+{
+	struct usb_info *ui = ept->ui;
+	struct msm_request *req;
+
+	req = kzalloc(sizeof(*req), gfp_flags);
+	if (!req)
+		goto fail1;
+
+	req->item = dma_pool_alloc(ui->pool, gfp_flags, &req->item_dma);
+	if (!req->item)
+		goto fail2;
+
+	if (bufsize) {
+		req->req.buf = kmalloc(bufsize, gfp_flags);
+		if (!req->req.buf)
+			goto fail3;
+		req->alloced = 1;
+	}
+
+	return &req->req;
+
+fail3:
+	dma_pool_free(ui->pool, req->item, req->item_dma);
+fail2:
+	kfree(req);
+fail1:
+	return 0;
+}
+
+static void usb_ept_enable(struct msm_endpoint *ept, int yes,
+		unsigned char ep_type)
+{
+	struct usb_info *ui = ept->ui;
+	int in = ept->flags & EPT_FLAG_IN;
+	unsigned n;
+
+	n = readl(USB_ENDPTCTRL(ept->num));
+
+	if (in) {
+		if (yes) {
+			n = (n & (~CTRL_TXT_MASK)) |
+				(ep_type << CTRL_TXT_EP_TYPE_SHIFT);
+			n |= CTRL_TXE | CTRL_TXR;
+		} else
+			n &= (~CTRL_TXE);
+	} else {
+		if (yes) {
+			n = (n & (~CTRL_RXT_MASK)) |
+				(ep_type << CTRL_RXT_EP_TYPE_SHIFT);
+			n |= CTRL_RXE | CTRL_RXR;
+		} else
+			n &= ~(CTRL_RXE);
+	}
+	/* complete all the updates to ept->head before enabling endpoint*/
+	mb();
+	writel(n, USB_ENDPTCTRL(ept->num));
+
+	/* Ensure endpoint is enabled before returning */
+	mb();
+
+	dev_dbg(&ui->pdev->dev, "ept %d %s %s\n",
+	       ept->num, in ? "in" : "out", yes ? "enabled" : "disabled");
+}
+
+static void ept_prime_timer_func(unsigned long data)
+{
+	struct msm_endpoint *ept = (struct msm_endpoint *)data;
+	struct usb_info *ui = ept->ui;
+	unsigned n = 1 << ept->bit;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ui->lock, flags);
+
+	ept->false_prime_fail_count++;
+	if ((readl_relaxed(USB_ENDPTPRIME) & n)) {
+		/*
+		 * ---- UNLIKELY ---
+		 * May be hardware is taking long time to process the
+		 * prime request. Or could be intermittent priming and
+		 * previous dTD is not fired yet.
+		 */
+		mod_timer(&ept->prime_timer, EPT_PRIME_CHECK_DELAY);
+		goto out;
+	}
+	if (readl_relaxed(USB_ENDPTSTAT) & n)
+		goto out;
+
+	/* clear speculative loads on item->info */
+	rmb();
+	if (ept->req && (ept->req->item->info & INFO_ACTIVE)) {
+		ui->prime_fail_count++;
+		ept->actual_prime_fail_count++;
+		pr_err("%s(): ept%d%s prime failed. ept: config: %x"
+				"active: %x next: %x info: %x\n",
+				__func__, ept->num,
+				ept->flags & EPT_FLAG_IN ? "in" : "out",
+				ept->head->config, ept->head->active,
+				ept->head->next, ept->head->info);
+		writel_relaxed(n, USB_ENDPTPRIME);
+		mod_timer(&ept->prime_timer, EPT_PRIME_CHECK_DELAY);
+	}
+out:
+	spin_unlock_irqrestore(&ui->lock, flags);
+}
+
+static void usb_ept_start(struct msm_endpoint *ept)
+{
+	struct usb_info *ui = ept->ui;
+	struct msm_request *req = ept->req;
+	unsigned n = 1 << ept->bit;
+
+	BUG_ON(req->live);
+
+	while (req) {
+		req->live = 1;
+		/* prepare the transaction descriptor item for the hardware */
+		req->item->info =
+			INFO_BYTES(req->req.length) | INFO_IOC | INFO_ACTIVE;
+		req->item->page0 = req->dma;
+		req->item->page1 = (req->dma + 0x1000) & 0xfffff000;
+		req->item->page2 = (req->dma + 0x2000) & 0xfffff000;
+		req->item->page3 = (req->dma + 0x3000) & 0xfffff000;
+
+		if (req->next == NULL) {
+			req->item->next = TERMINATE;
+			break;
+		}
+		req->item->next = req->next->item_dma;
+		req = req->next;
+	}
+
+	rmb();
+	/* link the hw queue head to the request's transaction item */
+	ept->head->next = ept->req->item_dma;
+	ept->head->info = 0;
+
+	/* flush buffers before priming ept */
+	mb();
+	/* during high throughput testing it is observed that
+	 * ept stat bit is not set even though all the data
+	 * structures are updated properly and ept prime bit
+	 * is set. To workaround the issue, kick a timer and
+	 * make decision on re-prime. We can do a busy loop here
+	 * but it leads to high cpu usage.
+	 */
+	writel_relaxed(n, USB_ENDPTPRIME);
+	mod_timer(&ept->prime_timer, EPT_PRIME_CHECK_DELAY);
+}
+
+int usb_ept_queue_xfer(struct msm_endpoint *ept, struct usb_request *_req)
+{
+	unsigned long flags;
+	struct msm_request *req = to_msm_request(_req);
+	struct msm_request *last;
+	struct usb_info *ui = ept->ui;
+	unsigned length = req->req.length;
+
+	if (length > 0x4000)
+		return -EMSGSIZE;
+
+	spin_lock_irqsave(&ui->lock, flags);
+
+	if (req->busy) {
+		req->req.status = -EBUSY;
+		spin_unlock_irqrestore(&ui->lock, flags);
+		dev_err(&ui->pdev->dev,
+			"usb_ept_queue_xfer() tried to queue busy request\n");
+		return -EBUSY;
+	}
+
+	if (!atomic_read(&ui->configured) && (ept->num != 0)) {
+		req->req.status = -ESHUTDOWN;
+		spin_unlock_irqrestore(&ui->lock, flags);
+		if (printk_ratelimit())
+			dev_err(&ui->pdev->dev,
+				"%s: called while offline\n", __func__);
+		return -ESHUTDOWN;
+	}
+
+	if (ui->usb_state == USB_STATE_SUSPENDED) {
+		if (!atomic_read(&ui->remote_wakeup)) {
+			req->req.status = -EAGAIN;
+			spin_unlock_irqrestore(&ui->lock, flags);
+			if (printk_ratelimit())
+				dev_err(&ui->pdev->dev,
+				"%s: cannot queue as bus is suspended "
+				"ept #%d %s max:%d head:%p bit:%d\n",
+				__func__, ept->num,
+				(ept->flags & EPT_FLAG_IN) ? "in" : "out",
+				ept->ep.maxpacket, ept->head, ept->bit);
+
+			return -EAGAIN;
+		}
+
+		wake_lock(&ui->wlock);
+		usb_phy_set_suspend(ui->xceiv, 0);
+		schedule_delayed_work(&ui->rw_work, REMOTE_WAKEUP_DELAY);
+	}
+
+	req->busy = 1;
+	req->live = 0;
+	req->next = 0;
+	req->req.status = -EBUSY;
+
+	req->dma = dma_map_single(NULL, req->req.buf, length,
+				  (ept->flags & EPT_FLAG_IN) ?
+				  DMA_TO_DEVICE : DMA_FROM_DEVICE);
+
+
+	/* Add the new request to the end of the queue */
+	last = ept->last;
+	if (last) {
+		/* Already requests in the queue. add us to the
+		 * end, but let the completion interrupt actually
+		 * start things going, to avoid hw issues
+		 */
+		last->next = req;
+		req->prev = last;
+
+	} else {
+		/* queue was empty -- kick the hardware */
+		ept->req = req;
+		req->prev = NULL;
+		usb_ept_start(ept);
+	}
+	ept->last = req;
+
+	spin_unlock_irqrestore(&ui->lock, flags);
+	return 0;
+}
+
+/* --- endpoint 0 handling --- */
+
+static void ep0_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct msm_request *r = to_msm_request(req);
+	struct msm_endpoint *ept = to_msm_endpoint(ep);
+	struct usb_info *ui = ept->ui;
+
+	req->complete = r->gadget_complete;
+	r->gadget_complete = 0;
+	if	(req->complete)
+		req->complete(&ui->ep0in.ep, req);
+}
+
+static void ep0_status_complete(struct usb_ep *ep, struct usb_request *_req)
+{
+	struct usb_request *req = _req->context;
+	struct msm_request *r;
+	struct msm_endpoint *ept;
+	struct usb_info *ui;
+
+	pr_debug("%s:\n", __func__);
+	if (!req)
+		return;
+
+	r = to_msm_request(req);
+	ept = to_msm_endpoint(ep);
+	ui = ept->ui;
+	_req->context = 0;
+
+	req->complete = r->gadget_complete;
+	req->zero = 0;
+	r->gadget_complete = 0;
+	if (req->complete)
+		req->complete(&ui->ep0in.ep, req);
+
+}
+
+static void ep0_status_phase(struct usb_ep *ep, struct usb_request *req)
+{
+	struct msm_endpoint *ept = to_msm_endpoint(ep);
+	struct usb_info *ui = ept->ui;
+
+	pr_debug("%s:\n", __func__);
+
+	req->length = 0;
+	req->complete = ep0_status_complete;
+
+	/* status phase */
+	if (atomic_read(&ui->ep0_dir) == USB_DIR_IN)
+		usb_ept_queue_xfer(&ui->ep0out, req);
+	else
+		usb_ept_queue_xfer(&ui->ep0in, req);
+}
+
+static void ep0in_send_zero_leng_pkt(struct msm_endpoint *ept)
+{
+	struct usb_info *ui = ept->ui;
+	struct usb_request *req = ui->setup_req;
+
+	pr_debug("%s:\n", __func__);
+
+	req->length = 0;
+	req->complete = ep0_status_phase;
+	usb_ept_queue_xfer(&ui->ep0in, req);
+}
+
+static void ep0_queue_ack_complete(struct usb_ep *ep,
+	struct usb_request *_req)
+{
+	struct msm_endpoint *ept = to_msm_endpoint(ep);
+	struct usb_info *ui = ept->ui;
+	struct usb_request *req = ui->setup_req;
+
+	pr_debug("%s: _req:%p actual:%d length:%d zero:%d\n",
+			__func__, _req, _req->actual,
+			_req->length, _req->zero);
+
+	/* queue up the receive of the ACK response from the host */
+	if (_req->status == 0 && _req->actual == _req->length) {
+		req->context = _req;
+		if (atomic_read(&ui->ep0_dir) == USB_DIR_IN) {
+			if (_req->zero && _req->length &&
+					!(_req->length % ep->maxpacket)) {
+				ep0in_send_zero_leng_pkt(&ui->ep0in);
+				return;
+			}
+		}
+		ep0_status_phase(ep, req);
+	} else
+		ep0_complete(ep, _req);
+}
+
+static void ep0_setup_ack_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct msm_endpoint *ept = to_msm_endpoint(ep);
+	struct usb_info *ui = ept->ui;
+	unsigned int temp;
+	int test_mode = atomic_read(&ui->test_mode);
+
+	if (!test_mode)
+		return;
+
+	switch (test_mode) {
+	case J_TEST:
+		dev_info(&ui->pdev->dev, "usb electrical test mode: (J)\n");
+		temp = readl(USB_PORTSC) & (~PORTSC_PTC);
+		writel(temp | PORTSC_PTC_J_STATE, USB_PORTSC);
+		break;
+
+	case K_TEST:
+		dev_info(&ui->pdev->dev, "usb electrical test mode: (K)\n");
+		temp = readl(USB_PORTSC) & (~PORTSC_PTC);
+		writel(temp | PORTSC_PTC_K_STATE, USB_PORTSC);
+		break;
+
+	case SE0_NAK_TEST:
+		dev_info(&ui->pdev->dev,
+			"usb electrical test mode: (SE0-NAK)\n");
+		temp = readl(USB_PORTSC) & (~PORTSC_PTC);
+		writel(temp | PORTSC_PTC_SE0_NAK, USB_PORTSC);
+		break;
+
+	case TST_PKT_TEST:
+		dev_info(&ui->pdev->dev,
+			"usb electrical test mode: (TEST_PKT)\n");
+		temp = readl(USB_PORTSC) & (~PORTSC_PTC);
+		writel(temp | PORTSC_PTC_TST_PKT, USB_PORTSC);
+		break;
+	}
+}
+
+static void ep0_setup_ack(struct usb_info *ui)
+{
+	struct usb_request *req = ui->setup_req;
+	req->length = 0;
+	req->complete = ep0_setup_ack_complete;
+	usb_ept_queue_xfer(&ui->ep0in, req);
+}
+
+static void ep0_setup_stall(struct usb_info *ui)
+{
+	writel((1<<16) | (1<<0), USB_ENDPTCTRL(0));
+}
+
+static void ep0_setup_send(struct usb_info *ui, unsigned length)
+{
+	struct usb_request *req = ui->setup_req;
+	struct msm_request *r = to_msm_request(req);
+	struct msm_endpoint *ept = &ui->ep0in;
+
+	req->length = length;
+	req->complete = ep0_queue_ack_complete;
+	r->gadget_complete = 0;
+	usb_ept_queue_xfer(ept, req);
+}
+
+static void handle_setup(struct usb_info *ui)
+{
+	struct usb_ctrlrequest ctl;
+	struct usb_request *req = ui->setup_req;
+	int ret;
+#ifdef CONFIG_USB_OTG
+	u8 hnp;
+	unsigned long flags;
+#endif
+	/* USB hardware sometimes generate interrupt before
+	 * 8 bytes of SETUP packet are written to system memory.
+	 * This results in fetching wrong setup_data sometimes.
+	 * TODO: Remove below workaround of adding 1us delay once
+	 * it gets fixed in hardware.
+	 */
+	udelay(10);
+
+	memcpy(&ctl, ui->ep0out.head->setup_data, sizeof(ctl));
+	/* Ensure buffer is read before acknowledging to h/w */
+	mb();
+
+	writel(EPT_RX(0), USB_ENDPTSETUPSTAT);
+
+	if (ctl.bRequestType & USB_DIR_IN)
+		atomic_set(&ui->ep0_dir, USB_DIR_IN);
+	else
+		atomic_set(&ui->ep0_dir, USB_DIR_OUT);
+
+	/* any pending ep0 transactions must be canceled */
+	flush_endpoint(&ui->ep0out);
+	flush_endpoint(&ui->ep0in);
+
+	dev_dbg(&ui->pdev->dev,
+		"setup: type=%02x req=%02x val=%04x idx=%04x len=%04x\n",
+	       ctl.bRequestType, ctl.bRequest, ctl.wValue,
+	       ctl.wIndex, ctl.wLength);
+
+	if ((ctl.bRequestType & (USB_DIR_IN | USB_TYPE_MASK)) ==
+					(USB_DIR_IN | USB_TYPE_STANDARD)) {
+		if (ctl.bRequest == USB_REQ_GET_STATUS) {
+			/* OTG supplement Rev 2.0 introduces another device
+			 * GET_STATUS request for HNP polling with length = 1.
+			 */
+			u8 len = 2;
+			switch (ctl.bRequestType & USB_RECIP_MASK) {
+			case USB_RECIP_ENDPOINT:
+			{
+				struct msm_endpoint *ept;
+				unsigned num =
+					ctl.wIndex & USB_ENDPOINT_NUMBER_MASK;
+				u16 temp = 0;
+
+				if (num == 0) {
+					memset(req->buf, 0, 2);
+					break;
+				}
+				if (ctl.wIndex & USB_ENDPOINT_DIR_MASK)
+					num += 16;
+				ept = &ui->ep0out + num;
+				temp = usb_ep_get_stall(ept);
+				temp = temp << USB_ENDPOINT_HALT;
+				memcpy(req->buf, &temp, 2);
+				break;
+			}
+			case USB_RECIP_DEVICE:
+			{
+				u16 temp = 0;
+
+				if (ctl.wIndex == OTG_STATUS_SELECTOR) {
+#ifdef CONFIG_USB_OTG
+					spin_lock_irqsave(&ui->lock, flags);
+					hnp = (ui->gadget.host_request <<
+							HOST_REQUEST_FLAG);
+					ui->hnp_avail = 1;
+					spin_unlock_irqrestore(&ui->lock,
+							flags);
+					memcpy(req->buf, &hnp, 1);
+					len = 1;
+#else
+					goto stall;
+#endif
+				} else {
+					temp = (atomic_read(&ui->self_powered)
+						<< USB_DEVICE_SELF_POWERED);
+					temp |= (atomic_read(&ui->remote_wakeup)
+						<< USB_DEVICE_REMOTE_WAKEUP);
+					memcpy(req->buf, &temp, 2);
+				}
+				break;
+			}
+			case USB_RECIP_INTERFACE:
+				memset(req->buf, 0, 2);
+				break;
+			default:
+				goto stall;
+			}
+			ep0_setup_send(ui, len);
+			return;
+		}
+	}
+	if (ctl.bRequestType ==
+		    (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT)) {
+		if ((ctl.bRequest == USB_REQ_CLEAR_FEATURE) ||
+				(ctl.bRequest == USB_REQ_SET_FEATURE)) {
+			if ((ctl.wValue == 0) && (ctl.wLength == 0)) {
+				unsigned num = ctl.wIndex & 0x0f;
+
+				if (num != 0) {
+					struct msm_endpoint *ept;
+
+					if (ctl.wIndex & 0x80)
+						num += 16;
+					ept = &ui->ep0out + num;
+
+					if (ept->wedged)
+						goto ack;
+					if (ctl.bRequest == USB_REQ_SET_FEATURE)
+						usb_ept_set_halt(&ept->ep, 1);
+					else
+						usb_ept_set_halt(&ept->ep, 0);
+				}
+				goto ack;
+			}
+		}
+	}
+	if (ctl.bRequestType == (USB_DIR_OUT | USB_TYPE_STANDARD)) {
+		if (ctl.bRequest == USB_REQ_SET_CONFIGURATION) {
+			atomic_set(&ui->configured, !!ctl.wValue);
+			msm_hsusb_set_state(USB_STATE_CONFIGURED);
+		} else if (ctl.bRequest == USB_REQ_SET_ADDRESS) {
+			/*
+			 * Gadget speed should be set when PCI interrupt
+			 * occurs. But sometimes, PCI interrupt is not
+			 * occuring after reset. Hence update the gadget
+			 * speed here.
+			 */
+			if (ui->gadget.speed == USB_SPEED_UNKNOWN) {
+				dev_info(&ui->pdev->dev,
+					"PCI intr missed"
+					"set speed explictly\n");
+				msm_hsusb_set_speed(ui);
+			}
+			msm_hsusb_set_state(USB_STATE_ADDRESS);
+
+			/* write address delayed (will take effect
+			** after the next IN txn)
+			*/
+			writel((ctl.wValue << 25) | (1 << 24), USB_DEVICEADDR);
+			goto ack;
+		} else if (ctl.bRequest == USB_REQ_SET_FEATURE) {
+			switch (ctl.wValue) {
+			case USB_DEVICE_TEST_MODE:
+				switch (ctl.wIndex) {
+				case J_TEST:
+				case K_TEST:
+				case SE0_NAK_TEST:
+				case TST_PKT_TEST:
+					atomic_set(&ui->test_mode, ctl.wIndex);
+					goto ack;
+				}
+				goto stall;
+			case USB_DEVICE_REMOTE_WAKEUP:
+				atomic_set(&ui->remote_wakeup, 1);
+				goto ack;
+#ifdef CONFIG_USB_OTG
+			case USB_DEVICE_B_HNP_ENABLE:
+				ui->gadget.b_hnp_enable = 1;
+				goto ack;
+			case USB_DEVICE_A_HNP_SUPPORT:
+			case USB_DEVICE_A_ALT_HNP_SUPPORT:
+				/* B-devices compliant to OTG spec
+				 * Rev 2.0 are not required to
+				 * suppport these features.
+				 */
+				goto stall;
+#endif
+			}
+		} else if ((ctl.bRequest == USB_REQ_CLEAR_FEATURE) &&
+				(ctl.wValue == USB_DEVICE_REMOTE_WAKEUP)) {
+			atomic_set(&ui->remote_wakeup, 0);
+			goto ack;
+		}
+	}
+
+	/* delegate if we get here */
+	if (ui->driver) {
+		ret = ui->driver->setup(&ui->gadget, &ctl);
+		if (ret >= 0)
+			return;
+	}
+
+stall:
+	/* stall ep0 on error */
+	ep0_setup_stall(ui);
+	return;
+
+ack:
+	ep0_setup_ack(ui);
+}
+
+static void handle_endpoint(struct usb_info *ui, unsigned bit)
+{
+	struct msm_endpoint *ept = ui->ept + bit;
+	struct msm_request *req;
+	unsigned long flags;
+	int req_dequeue = 1;
+	unsigned info;
+
+	/*
+	INFO("handle_endpoint() %d %s req=%p(%08x)\n",
+		ept->num, (ept->flags & EPT_FLAG_IN) ? "in" : "out",
+		ept->req, ept->req ? ept->req->item_dma : 0);
+	*/
+
+	/* expire all requests that are no longer active */
+	spin_lock_irqsave(&ui->lock, flags);
+	while ((req = ept->req)) {
+		/* if we've processed all live requests, time to
+		 * restart the hardware on the next non-live request
+		 */
+		if (!req->live) {
+			usb_ept_start(ept);
+			break;
+		}
+
+dequeue:
+		/* clean speculative fetches on req->item->info */
+		dma_coherent_post_ops();
+		info = req->item->info;
+		/* if the transaction is still in-flight, stop here */
+		if (info & INFO_ACTIVE) {
+			if (req_dequeue) {
+				req_dequeue = 0;
+				ui->dTD_update_fail_count++;
+				ept->dTD_update_fail_count++;
+				udelay(10);
+				goto dequeue;
+			} else {
+				break;
+			}
+		}
+		req_dequeue = 0;
+
+		del_timer(&ept->prime_timer);
+		/* advance ept queue to the next request */
+		ept->req = req->next;
+		if (ept->req == 0)
+			ept->last = 0;
+
+		dma_unmap_single(NULL, req->dma, req->req.length,
+				 (ept->flags & EPT_FLAG_IN) ?
+				 DMA_TO_DEVICE : DMA_FROM_DEVICE);
+
+		if (info & (INFO_HALTED | INFO_BUFFER_ERROR | INFO_TXN_ERROR)) {
+			/* XXX pass on more specific error code */
+			req->req.status = -EIO;
+			req->req.actual = 0;
+			dev_err(&ui->pdev->dev,
+				"ept %d %s error. info=%08x\n",
+			       ept->num,
+			       (ept->flags & EPT_FLAG_IN) ? "in" : "out",
+			       info);
+		} else {
+			req->req.status = 0;
+			req->req.actual =
+				req->req.length - ((info >> 16) & 0x7FFF);
+		}
+		req->busy = 0;
+		req->live = 0;
+
+		if (req->req.complete) {
+			spin_unlock_irqrestore(&ui->lock, flags);
+			req->req.complete(&ept->ep, &req->req);
+			spin_lock_irqsave(&ui->lock, flags);
+		}
+	}
+	spin_unlock_irqrestore(&ui->lock, flags);
+}
+
+static void flush_endpoint_hw(struct usb_info *ui, unsigned bits)
+{
+	/* flush endpoint, canceling transactions
+	** - this can take a "large amount of time" (per databook)
+	** - the flush can fail in some cases, thus we check STAT
+	**   and repeat if we're still operating
+	**   (does the fact that this doesn't use the tripwire matter?!)
+	*/
+	do {
+		writel(bits, USB_ENDPTFLUSH);
+		while (readl(USB_ENDPTFLUSH) & bits)
+			udelay(100);
+	} while (readl(USB_ENDPTSTAT) & bits);
+}
+
+static void flush_endpoint_sw(struct msm_endpoint *ept)
+{
+	struct usb_info *ui = ept->ui;
+	struct msm_request *req, *next_req = NULL;
+	unsigned long flags;
+
+	/* inactive endpoints have nothing to do here */
+	if (ept->ep.maxpacket == 0)
+		return;
+
+	/* put the queue head in a sane state */
+	ept->head->info = 0;
+	ept->head->next = TERMINATE;
+
+	/* cancel any pending requests */
+	spin_lock_irqsave(&ui->lock, flags);
+	req = ept->req;
+	ept->req = 0;
+	ept->last = 0;
+	while (req != 0) {
+		req->busy = 0;
+		req->live = 0;
+		req->req.status = -ESHUTDOWN;
+		req->req.actual = 0;
+
+		/* Gadget driver may free the request in completion
+		 * handler. So keep a copy of next req pointer
+		 * before calling completion handler.
+		 */
+		next_req = req->next;
+		if (req->req.complete) {
+			spin_unlock_irqrestore(&ui->lock, flags);
+			req->req.complete(&ept->ep, &req->req);
+			spin_lock_irqsave(&ui->lock, flags);
+		}
+		req = next_req;
+	}
+	spin_unlock_irqrestore(&ui->lock, flags);
+}
+
+static void flush_endpoint(struct msm_endpoint *ept)
+{
+	del_timer(&ept->prime_timer);
+	flush_endpoint_hw(ept->ui, (1 << ept->bit));
+	flush_endpoint_sw(ept);
+}
+
+static irqreturn_t usb_interrupt(int irq, void *data)
+{
+	struct usb_info *ui = data;
+	unsigned n;
+	unsigned long flags;
+
+	n = readl(USB_USBSTS);
+	writel(n, USB_USBSTS);
+
+	/* somehow we got an IRQ while in the reset sequence: ignore it */
+	if (!atomic_read(&ui->running))
+		return IRQ_HANDLED;
+
+	if (n & STS_PCI) {
+		msm_hsusb_set_speed(ui);
+		if (atomic_read(&ui->configured)) {
+			wake_lock(&ui->wlock);
+
+			spin_lock_irqsave(&ui->lock, flags);
+			ui->usb_state = USB_STATE_CONFIGURED;
+			ui->flags = USB_FLAG_CONFIGURED;
+			spin_unlock_irqrestore(&ui->lock, flags);
+
+			ui->driver->resume(&ui->gadget);
+			schedule_work(&ui->work);
+		} else {
+			msm_hsusb_set_state(USB_STATE_DEFAULT);
+		}
+
+#ifdef CONFIG_USB_OTG
+		/* notify otg to clear A_BIDL_ADIS timer */
+		if (ui->gadget.is_a_peripheral)
+			usb_phy_set_suspend(ui->xceiv, 0);
+#endif
+	}
+
+	if (n & STS_URI) {
+		dev_dbg(&ui->pdev->dev, "reset\n");
+		spin_lock_irqsave(&ui->lock, flags);
+		ui->gadget.speed = USB_SPEED_UNKNOWN;
+		spin_unlock_irqrestore(&ui->lock, flags);
+#ifdef CONFIG_USB_OTG
+		/* notify otg to clear A_BIDL_ADIS timer */
+		if (ui->gadget.is_a_peripheral)
+			usb_phy_set_suspend(ui->xceiv, 0);
+		spin_lock_irqsave(&ui->lock, flags);
+		/* Host request is persistent across reset */
+		ui->gadget.b_hnp_enable = 0;
+		ui->hnp_avail = 0;
+		spin_unlock_irqrestore(&ui->lock, flags);
+#endif
+		msm_hsusb_set_state(USB_STATE_DEFAULT);
+		atomic_set(&ui->remote_wakeup, 0);
+		if (!ui->gadget.is_a_peripheral)
+			schedule_delayed_work(&ui->chg_stop, 0);
+
+		writel(readl(USB_ENDPTSETUPSTAT), USB_ENDPTSETUPSTAT);
+		writel(readl(USB_ENDPTCOMPLETE), USB_ENDPTCOMPLETE);
+		writel(0xffffffff, USB_ENDPTFLUSH);
+		writel(0, USB_ENDPTCTRL(1));
+
+		wake_lock(&ui->wlock);
+		if (atomic_read(&ui->configured)) {
+			/* marking us offline will cause ept queue attempts
+			** to fail
+			*/
+			atomic_set(&ui->configured, 0);
+			/* Defer sending offline uevent to userspace */
+			atomic_set(&ui->offline_pending, 1);
+
+			/* XXX: we can't seem to detect going offline,
+			 * XXX:  so deconfigure on reset for the time being
+			 */
+			dev_dbg(&ui->pdev->dev,
+					"usb: notify offline\n");
+			ui->driver->disconnect(&ui->gadget);
+			/* cancel pending ep0 transactions */
+			flush_endpoint(&ui->ep0out);
+			flush_endpoint(&ui->ep0in);
+
+		}
+		/* Start phy stuck timer */
+		if (ui->pdata && ui->pdata->is_phy_status_timer_on)
+			mod_timer(&phy_status_timer, PHY_STATUS_CHECK_DELAY);
+	}
+
+	if (n & STS_SLI) {
+		dev_dbg(&ui->pdev->dev, "suspend\n");
+
+		spin_lock_irqsave(&ui->lock, flags);
+		ui->usb_state = USB_STATE_SUSPENDED;
+		ui->flags = USB_FLAG_SUSPEND;
+		spin_unlock_irqrestore(&ui->lock, flags);
+
+		ui->driver->suspend(&ui->gadget);
+		schedule_work(&ui->work);
+#ifdef CONFIG_USB_OTG
+		/* notify otg for
+		 * 1. kicking A_BIDL_ADIS timer in case of A-peripheral
+		 * 2. disabling pull-up and kicking B_ASE0_RST timer
+		 */
+		if (ui->gadget.b_hnp_enable || ui->gadget.is_a_peripheral)
+			usb_phy_set_suspend(ui->xceiv, 1);
+#endif
+	}
+
+	if (n & STS_UI) {
+		n = readl(USB_ENDPTSETUPSTAT);
+		if (n & EPT_RX(0))
+			handle_setup(ui);
+
+		n = readl(USB_ENDPTCOMPLETE);
+		writel(n, USB_ENDPTCOMPLETE);
+		while (n) {
+			unsigned bit = __ffs(n);
+			handle_endpoint(ui, bit);
+			n = n & (~(1 << bit));
+		}
+	}
+	return IRQ_HANDLED;
+}
+
+static void usb_prepare(struct usb_info *ui)
+{
+	spin_lock_init(&ui->lock);
+
+	memset(ui->buf, 0, 4096);
+	ui->head = (void *) (ui->buf + 0);
+
+	/* only important for reset/reinit */
+	memset(ui->ept, 0, sizeof(ui->ept));
+	ui->next_item = 0;
+	ui->next_ifc_num = 0;
+
+	init_endpoints(ui);
+
+	ui->ep0in.ep.maxpacket = 64;
+	ui->ep0out.ep.maxpacket = 64;
+
+	ui->setup_req =
+		usb_ept_alloc_req(&ui->ep0in, SETUP_BUF_SIZE, GFP_KERNEL);
+
+	INIT_WORK(&ui->work, usb_do_work);
+	INIT_DELAYED_WORK(&ui->chg_det, usb_chg_detect);
+	INIT_DELAYED_WORK(&ui->chg_stop, usb_chg_stop);
+	INIT_DELAYED_WORK(&ui->rw_work, usb_do_remote_wakeup);
+	if (ui->pdata && ui->pdata->is_phy_status_timer_on)
+		INIT_WORK(&ui->phy_status_check, usb_phy_stuck_recover);
+}
+
+static void usb_reset(struct usb_info *ui)
+{
+	struct msm_otg *otg = to_msm_otg(ui->xceiv);
+
+	dev_dbg(&ui->pdev->dev, "reset controller\n");
+
+	atomic_set(&ui->running, 0);
+
+	/*
+	 * PHY reset takes minimum 100 msec. Hence reset only link
+	 * during HNP. Reset PHY and link in B-peripheral mode.
+	 */
+	if (ui->gadget.is_a_peripheral)
+		otg->reset(ui->xceiv, 0);
+	else
+		otg->reset(ui->xceiv, 1);
+
+	/* set usb controller interrupt threshold to zero*/
+	writel((readl(USB_USBCMD) & ~USBCMD_ITC_MASK) | USBCMD_ITC(0),
+							USB_USBCMD);
+
+	writel(ui->dma, USB_ENDPOINTLISTADDR);
+
+	configure_endpoints(ui);
+
+	/* marking us offline will cause ept queue attempts to fail */
+	atomic_set(&ui->configured, 0);
+
+	if (ui->driver) {
+		dev_dbg(&ui->pdev->dev, "usb: notify offline\n");
+		ui->driver->disconnect(&ui->gadget);
+	}
+
+	/* cancel pending ep0 transactions */
+	flush_endpoint(&ui->ep0out);
+	flush_endpoint(&ui->ep0in);
+
+	/* enable interrupts */
+	writel(STS_URI | STS_SLI | STS_UI | STS_PCI, USB_USBINTR);
+
+	/* Ensure that h/w RESET is completed before returning */
+	mb();
+
+	atomic_set(&ui->running, 1);
+}
+
+static void usb_start(struct usb_info *ui)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&ui->lock, flags);
+	ui->flags |= USB_FLAG_START;
+	schedule_work(&ui->work);
+	spin_unlock_irqrestore(&ui->lock, flags);
+}
+
+static int usb_free(struct usb_info *ui, int ret)
+{
+	if (ret)
+		dev_dbg(&ui->pdev->dev, "usb_free(%d)\n", ret);
+
+	usb_del_gadget_udc(&ui->gadget);
+
+	if (ui->xceiv)
+		usb_put_transceiver(ui->xceiv);
+
+	if (ui->irq)
+		free_irq(ui->irq, 0);
+	if (ui->pool)
+		dma_pool_destroy(ui->pool);
+	if (ui->dma)
+		dma_free_coherent(&ui->pdev->dev, 4096, ui->buf, ui->dma);
+	kfree(ui);
+	return ret;
+}
+
+static void usb_do_work_check_vbus(struct usb_info *ui)
+{
+	unsigned long iflags;
+
+	spin_lock_irqsave(&ui->lock, iflags);
+	if (is_usb_online(ui))
+		ui->flags |= USB_FLAG_VBUS_ONLINE;
+	else
+		ui->flags |= USB_FLAG_VBUS_OFFLINE;
+	spin_unlock_irqrestore(&ui->lock, iflags);
+}
+
+static void usb_do_work(struct work_struct *w)
+{
+	struct usb_info *ui = container_of(w, struct usb_info, work);
+	struct msm_otg *otg = to_msm_otg(ui->xceiv);
+	unsigned long iflags;
+	unsigned flags, _vbus;
+
+	for (;;) {
+		spin_lock_irqsave(&ui->lock, iflags);
+		flags = ui->flags;
+		ui->flags = 0;
+		_vbus = is_usb_online(ui);
+		spin_unlock_irqrestore(&ui->lock, iflags);
+
+		/* give up if we have nothing to do */
+		if (flags == 0)
+			break;
+
+		switch (ui->state) {
+		case USB_STATE_IDLE:
+			if (flags & USB_FLAG_START) {
+				int ret;
+
+				if (!_vbus) {
+					ui->state = USB_STATE_OFFLINE;
+					break;
+				}
+
+				pm_runtime_get_noresume(&ui->pdev->dev);
+				pm_runtime_resume(&ui->pdev->dev);
+				dev_dbg(&ui->pdev->dev,
+					"msm72k_udc: IDLE -> ONLINE\n");
+				usb_reset(ui);
+				ret = request_irq(otg->irq, usb_interrupt,
+							IRQF_SHARED,
+							ui->pdev->name, ui);
+				/* FIXME: should we call BUG_ON when
+				 * requst irq fails
+				 */
+				if (ret) {
+					dev_err(&ui->pdev->dev,
+						"hsusb: peripheral: request irq"
+						" failed:(%d)", ret);
+					break;
+				}
+				ui->irq = otg->irq;
+				ui->state = USB_STATE_ONLINE;
+				usb_do_work_check_vbus(ui);
+
+				if (!atomic_read(&ui->softconnect))
+					break;
+
+				msm72k_pullup_internal(&ui->gadget, 1);
+
+				if (!ui->gadget.is_a_peripheral)
+					schedule_delayed_work(
+							&ui->chg_det,
+							USB_CHG_DET_DELAY);
+
+			}
+			break;
+		case USB_STATE_ONLINE:
+			if (atomic_read(&ui->offline_pending)) {
+				switch_set_state(&ui->sdev, 0);
+				atomic_set(&ui->offline_pending, 0);
+			}
+
+			/* If at any point when we were online, we received
+			 * the signal to go offline, we must honor it
+			 */
+			if (flags & USB_FLAG_VBUS_OFFLINE) {
+
+				ui->chg_current = 0;
+				/* wait incase chg_detect is running */
+				if (!ui->gadget.is_a_peripheral)
+					cancel_delayed_work_sync(&ui->chg_det);
+
+				dev_dbg(&ui->pdev->dev,
+					"msm72k_udc: ONLINE -> OFFLINE\n");
+
+				atomic_set(&ui->running, 0);
+				atomic_set(&ui->remote_wakeup, 0);
+				atomic_set(&ui->configured, 0);
+
+				if (ui->driver) {
+					dev_dbg(&ui->pdev->dev,
+						"usb: notify offline\n");
+					ui->driver->disconnect(&ui->gadget);
+				}
+				/* cancel pending ep0 transactions */
+				flush_endpoint(&ui->ep0out);
+				flush_endpoint(&ui->ep0in);
+
+				/* synchronize with irq context */
+				spin_lock_irqsave(&ui->lock, iflags);
+#ifdef CONFIG_USB_OTG
+				ui->gadget.host_request = 0;
+				ui->gadget.b_hnp_enable = 0;
+				ui->hnp_avail = 0;
+#endif
+				msm72k_pullup_internal(&ui->gadget, 0);
+				spin_unlock_irqrestore(&ui->lock, iflags);
+
+
+				/* if charger is initialized to known type
+				 * we must let modem know about charger
+				 * disconnection
+				 */
+				usb_phy_set_power(ui->xceiv, 0);
+
+				if (ui->irq) {
+					free_irq(ui->irq, ui);
+					ui->irq = 0;
+				}
+
+
+				switch_set_state(&ui->sdev, 0);
+
+				ui->state = USB_STATE_OFFLINE;
+				usb_do_work_check_vbus(ui);
+				pm_runtime_put_noidle(&ui->pdev->dev);
+				pm_runtime_suspend(&ui->pdev->dev);
+				wake_unlock(&ui->wlock);
+				break;
+			}
+			if (flags & USB_FLAG_SUSPEND) {
+				int maxpower = usb_get_max_power(ui);
+
+				if (maxpower < 0)
+					break;
+
+				usb_phy_set_power(ui->xceiv, 0);
+				/* To support TCXO during bus suspend
+				 * This might be dummy check since bus suspend
+				 * is not implemented as of now
+				 * */
+				if (release_wlocks)
+					wake_unlock(&ui->wlock);
+
+				/* TBD: Initiate LPM at usb bus suspend */
+				break;
+			}
+			if (flags & USB_FLAG_CONFIGURED) {
+				int maxpower = usb_get_max_power(ui);
+
+				/* We may come here even when no configuration
+				 * is selected. Send online/offline event
+				 * accordingly.
+				 */
+				switch_set_state(&ui->sdev,
+						atomic_read(&ui->configured));
+
+				if (maxpower < 0)
+					break;
+
+				ui->chg_current = maxpower;
+				usb_phy_set_power(ui->xceiv, maxpower);
+				break;
+			}
+			if (flags & USB_FLAG_RESET) {
+				dev_dbg(&ui->pdev->dev,
+					"msm72k_udc: ONLINE -> RESET\n");
+				msm72k_pullup_internal(&ui->gadget, 0);
+				usb_reset(ui);
+				msm72k_pullup_internal(&ui->gadget, 1);
+				dev_dbg(&ui->pdev->dev,
+					"msm72k_udc: RESET -> ONLINE\n");
+				break;
+			}
+			break;
+		case USB_STATE_OFFLINE:
+			/* If we were signaled to go online and vbus is still
+			 * present when we received the signal, go online.
+			 */
+			if ((flags & USB_FLAG_VBUS_ONLINE) && _vbus) {
+				int ret;
+
+				pm_runtime_get_noresume(&ui->pdev->dev);
+				pm_runtime_resume(&ui->pdev->dev);
+				dev_dbg(&ui->pdev->dev,
+					"msm72k_udc: OFFLINE -> ONLINE\n");
+
+				usb_reset(ui);
+				ui->state = USB_STATE_ONLINE;
+				usb_do_work_check_vbus(ui);
+				ret = request_irq(otg->irq, usb_interrupt,
+							IRQF_SHARED,
+							ui->pdev->name, ui);
+				/* FIXME: should we call BUG_ON when
+				 * requst irq fails
+				 */
+				if (ret) {
+					dev_err(&ui->pdev->dev,
+						"hsusb: peripheral: request irq"
+						" failed:(%d)", ret);
+					break;
+				}
+				ui->irq = otg->irq;
+				enable_irq_wake(otg->irq);
+
+				if (!atomic_read(&ui->softconnect))
+					break;
+				msm72k_pullup_internal(&ui->gadget, 1);
+
+				if (!ui->gadget.is_a_peripheral)
+					schedule_delayed_work(
+							&ui->chg_det,
+							USB_CHG_DET_DELAY);
+			}
+			break;
+		}
+	}
+}
+
+/* FIXME - the callers of this function should use a gadget API instead.
+ * This is called from htc_battery.c and board-halibut.c
+ * WARNING - this can get called before this driver is initialized.
+ */
+void msm_hsusb_set_vbus_state(int online)
+{
+	unsigned long flags;
+	struct usb_info *ui = the_usb_info;
+
+	if (!ui) {
+		pr_err("%s called before driver initialized\n", __func__);
+		return;
+	}
+
+	spin_lock_irqsave(&ui->lock, flags);
+
+	if (is_usb_online(ui) ==  online)
+		goto out;
+
+	if (online) {
+		ui->usb_state = USB_STATE_POWERED;
+		ui->flags |= USB_FLAG_VBUS_ONLINE;
+	} else {
+		ui->gadget.speed = USB_SPEED_UNKNOWN;
+		ui->usb_state = USB_STATE_NOTATTACHED;
+		ui->flags |= USB_FLAG_VBUS_OFFLINE;
+	}
+	if (in_interrupt()) {
+		schedule_work(&ui->work);
+	} else {
+		spin_unlock_irqrestore(&ui->lock, flags);
+		usb_do_work(&ui->work);
+		return;
+	}
+out:
+	spin_unlock_irqrestore(&ui->lock, flags);
+}
+
+#if defined(CONFIG_DEBUG_FS)
+
+void usb_function_reenumerate(void)
+{
+	struct usb_info *ui = the_usb_info;
+
+	/* disable and re-enable the D+ pullup */
+	dev_dbg(&ui->pdev->dev, "disable pullup\n");
+	writel(readl(USB_USBCMD) & ~USBCMD_RS, USB_USBCMD);
+
+	msleep(10);
+
+	dev_dbg(&ui->pdev->dev, "enable pullup\n");
+	writel(readl(USB_USBCMD) | USBCMD_RS, USB_USBCMD);
+}
+
+static char debug_buffer[PAGE_SIZE];
+
+static ssize_t debug_read_status(struct file *file, char __user *ubuf,
+				 size_t count, loff_t *ppos)
+{
+	struct usb_info *ui = file->private_data;
+	char *buf = debug_buffer;
+	unsigned long flags;
+	struct msm_endpoint *ept;
+	struct msm_request *req;
+	int n;
+	int i = 0;
+
+	spin_lock_irqsave(&ui->lock, flags);
+
+	i += scnprintf(buf + i, PAGE_SIZE - i,
+		   "regs: setup=%08x prime=%08x stat=%08x done=%08x\n",
+		   readl(USB_ENDPTSETUPSTAT),
+		   readl(USB_ENDPTPRIME),
+		   readl(USB_ENDPTSTAT),
+		   readl(USB_ENDPTCOMPLETE));
+	i += scnprintf(buf + i, PAGE_SIZE - i,
+		   "regs:   cmd=%08x   sts=%08x intr=%08x port=%08x\n\n",
+		   readl(USB_USBCMD),
+		   readl(USB_USBSTS),
+		   readl(USB_USBINTR),
+		   readl(USB_PORTSC));
+
+
+	for (n = 0; n < 32; n++) {
+		ept = ui->ept + n;
+		if (ept->ep.maxpacket == 0)
+			continue;
+
+		i += scnprintf(buf + i, PAGE_SIZE - i,
+			"ept%d %s cfg=%08x active=%08x next=%08x info=%08x\n",
+			ept->num, (ept->flags & EPT_FLAG_IN) ? "in " : "out",
+			ept->head->config, ept->head->active,
+			ept->head->next, ept->head->info);
+
+		for (req = ept->req; req; req = req->next)
+			i += scnprintf(buf + i, PAGE_SIZE - i,
+			"  req @%08x next=%08x info=%08x page0=%08x %c %c\n",
+				req->item_dma, req->item->next,
+				req->item->info, req->item->page0,
+				req->busy ? 'B' : ' ',
+				req->live ? 'L' : ' ');
+	}
+
+	i += scnprintf(buf + i, PAGE_SIZE - i,
+			   "phy failure count: %d\n", ui->phy_fail_count);
+
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	return simple_read_from_buffer(ubuf, count, ppos, buf, i);
+}
+
+static ssize_t debug_write_reset(struct file *file, const char __user *buf,
+				 size_t count, loff_t *ppos)
+{
+	struct usb_info *ui = file->private_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ui->lock, flags);
+	ui->flags |= USB_FLAG_RESET;
+	schedule_work(&ui->work);
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	return count;
+}
+
+static ssize_t debug_write_cycle(struct file *file, const char __user *buf,
+				 size_t count, loff_t *ppos)
+{
+	usb_function_reenumerate();
+	return count;
+}
+
+static int debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+const struct file_operations debug_stat_ops = {
+	.open = debug_open,
+	.read = debug_read_status,
+};
+
+const struct file_operations debug_reset_ops = {
+	.open = debug_open,
+	.write = debug_write_reset,
+};
+
+const struct file_operations debug_cycle_ops = {
+	.open = debug_open,
+	.write = debug_write_cycle,
+};
+
+static ssize_t debug_read_release_wlocks(struct file *file, char __user *ubuf,
+				 size_t count, loff_t *ppos)
+{
+	char kbuf[10];
+	size_t c = 0;
+
+	memset(kbuf, 0, 10);
+
+	c = scnprintf(kbuf, 10, "%d", release_wlocks);
+
+	if (copy_to_user(ubuf, kbuf, c))
+		return -EFAULT;
+
+	return c;
+}
+static ssize_t debug_write_release_wlocks(struct file *file,
+		const char __user *buf, size_t count, loff_t *ppos)
+{
+	char kbuf[10];
+	long temp;
+
+	memset(kbuf, 0, 10);
+
+	if (copy_from_user(kbuf, buf, count > 10 ? 10 : count))
+		return -EFAULT;
+
+	if (strict_strtol(kbuf, 10, &temp))
+		return -EINVAL;
+
+	if (temp)
+		release_wlocks = 1;
+	else
+		release_wlocks = 0;
+
+	return count;
+}
+static int debug_wake_lock_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+const struct file_operations debug_wlocks_ops = {
+	.open = debug_wake_lock_open,
+	.read = debug_read_release_wlocks,
+	.write = debug_write_release_wlocks,
+};
+
+static ssize_t debug_reprime_ep(struct file *file, const char __user *ubuf,
+				 size_t count, loff_t *ppos)
+{
+	struct usb_info *ui = file->private_data;
+	struct msm_endpoint *ept;
+	char kbuf[10];
+	unsigned int ep_num, dir;
+	unsigned long flags;
+	unsigned n, i;
+
+	memset(kbuf, 0, 10);
+
+	if (copy_from_user(kbuf, ubuf, count > 10 ? 10 : count))
+		return -EFAULT;
+
+	if (sscanf(kbuf, "%u %u", &ep_num, &dir) != 2)
+		return -EINVAL;
+
+	if (dir)
+		i = ep_num + 16;
+	else
+		i = ep_num;
+
+	spin_lock_irqsave(&ui->lock, flags);
+	ept = ui->ept + i;
+	n = 1 << ept->bit;
+
+	if ((readl_relaxed(USB_ENDPTPRIME) & n))
+		goto out;
+
+	if (readl_relaxed(USB_ENDPTSTAT) & n)
+		goto out;
+
+	/* clear speculative loads on item->info */
+	rmb();
+	if (ept->req && (ept->req->item->info & INFO_ACTIVE)) {
+		pr_err("%s(): ept%d%s prime failed. ept: config: %x"
+				"active: %x next: %x info: %x\n",
+				__func__, ept->num,
+				ept->flags & EPT_FLAG_IN ? "in" : "out",
+				ept->head->config, ept->head->active,
+				ept->head->next, ept->head->info);
+		writel_relaxed(n, USB_ENDPTPRIME);
+	}
+out:
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	return count;
+}
+
+static char buffer[512];
+static ssize_t debug_prime_fail_read(struct file *file, char __user *ubuf,
+				 size_t count, loff_t *ppos)
+{
+	struct usb_info *ui = file->private_data;
+	char *buf = buffer;
+	unsigned long flags;
+	struct msm_endpoint *ept;
+	int n;
+	int i = 0;
+
+	spin_lock_irqsave(&ui->lock, flags);
+	for (n = 0; n < 32; n++) {
+		ept = ui->ept + n;
+		if (ept->ep.maxpacket == 0)
+			continue;
+
+		i += scnprintf(buf + i, PAGE_SIZE - i,
+			"ept%d %s false_prime_count=%lu prime_fail_count=%d dtd_fail_count=%lu\n",
+			ept->num, (ept->flags & EPT_FLAG_IN) ? "in " : "out",
+			ept->false_prime_fail_count,
+			ept->actual_prime_fail_count,
+			ept->dTD_update_fail_count);
+	}
+
+	i += scnprintf(buf + i, PAGE_SIZE - i,
+			   "dTD_update_fail count: %lu\n",
+			    ui->dTD_update_fail_count);
+
+	i += scnprintf(buf + i, PAGE_SIZE - i,
+			   "prime_fail count: %d\n", ui->prime_fail_count);
+
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	return simple_read_from_buffer(ubuf, count, ppos, buf, i);
+}
+
+static int debug_prime_fail_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+const struct file_operations prime_fail_ops = {
+	.open = debug_prime_fail_open,
+	.read = debug_prime_fail_read,
+	.write = debug_reprime_ep,
+};
+
+static void usb_debugfs_init(struct usb_info *ui)
+{
+	struct dentry *dent;
+	dent = debugfs_create_dir(dev_name(&ui->pdev->dev), 0);
+	if (IS_ERR(dent))
+		return;
+
+	debugfs_create_file("status", 0444, dent, ui, &debug_stat_ops);
+	debugfs_create_file("reset", 0222, dent, ui, &debug_reset_ops);
+	debugfs_create_file("cycle", 0222, dent, ui, &debug_cycle_ops);
+	debugfs_create_file("release_wlocks", 0666, dent, ui,
+						&debug_wlocks_ops);
+	debugfs_create_file("prime_fail_countt", 0666, dent, ui,
+						&prime_fail_ops);
+}
+#else
+static void usb_debugfs_init(struct usb_info *ui) {}
+#endif
+
+static int
+msm72k_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
+{
+	struct msm_endpoint *ept;
+	unsigned char ep_type;
+
+	if (_ep == NULL || desc == NULL)
+		return -EINVAL;
+
+	ept = to_msm_endpoint(_ep);
+	ep_type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+	_ep->maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+	config_ept(ept);
+	ept->wedged = 0;
+	usb_ept_enable(ept, 1, ep_type);
+	return 0;
+}
+
+static int msm72k_disable(struct usb_ep *_ep)
+{
+	struct msm_endpoint *ept = to_msm_endpoint(_ep);
+
+	usb_ept_enable(ept, 0, 0);
+	flush_endpoint(ept);
+	/*
+	 * Clear descriptors here. Otherwise previous descriptors
+	 * will be used by function drivers upon next enumeration.
+	 */
+	_ep->desc = NULL;
+	return 0;
+}
+
+static struct usb_request *
+msm72k_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
+{
+	return usb_ept_alloc_req(to_msm_endpoint(_ep), 0, gfp_flags);
+}
+
+static void
+msm72k_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct msm_request *req = to_msm_request(_req);
+	struct msm_endpoint *ept = to_msm_endpoint(_ep);
+	struct usb_info *ui = ept->ui;
+
+	/* request should not be busy */
+	BUG_ON(req->busy);
+	if (req->alloced)
+		kfree(req->req.buf);
+	dma_pool_free(ui->pool, req->item, req->item_dma);
+	kfree(req);
+}
+
+static int
+msm72k_queue(struct usb_ep *_ep, struct usb_request *req, gfp_t gfp_flags)
+{
+	struct msm_endpoint *ep = to_msm_endpoint(_ep);
+	struct usb_info *ui = ep->ui;
+
+	if (ep == &ui->ep0in) {
+		struct msm_request *r = to_msm_request(req);
+		if (!req->length)
+			goto ep_queue_done;
+		r->gadget_complete = req->complete;
+		/* ep0_queue_ack_complete queue a receive for ACK before
+		** calling req->complete
+		*/
+		req->complete = ep0_queue_ack_complete;
+		if (atomic_read(&ui->ep0_dir) == USB_DIR_OUT)
+			ep = &ui->ep0out;
+		goto ep_queue_done;
+	}
+
+ep_queue_done:
+	return usb_ept_queue_xfer(ep, req);
+}
+
+static int msm72k_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct msm_endpoint *ep = to_msm_endpoint(_ep);
+	struct msm_request *req = to_msm_request(_req);
+	struct usb_info *ui = ep->ui;
+
+	struct msm_request *temp_req;
+	unsigned long flags;
+
+	if (!(ui && req && ep->req))
+		return -EINVAL;
+
+	spin_lock_irqsave(&ui->lock, flags);
+	if (!req->busy) {
+		dev_dbg(&ui->pdev->dev, "%s: !req->busy\n", __func__);
+		spin_unlock_irqrestore(&ui->lock, flags);
+		return -EINVAL;
+	}
+	del_timer(&ep->prime_timer);
+	/* Stop the transfer */
+	do {
+		writel((1 << ep->bit), USB_ENDPTFLUSH);
+		while (readl(USB_ENDPTFLUSH) & (1 << ep->bit))
+			udelay(100);
+	} while (readl(USB_ENDPTSTAT) & (1 << ep->bit));
+
+	req->req.status = 0;
+	req->busy = 0;
+
+	if (ep->req == req) {
+		ep->req = req->next;
+		ep->head->next = req->item->next;
+	} else {
+		req->prev->next = req->next;
+		if (req->next)
+			req->next->prev = req->prev;
+		req->prev->item->next = req->item->next;
+	}
+
+	if (!req->next)
+		ep->last = req->prev;
+
+	/* initialize request to default */
+	req->item->next = TERMINATE;
+	req->item->info = 0;
+	req->live = 0;
+	dma_unmap_single(NULL, req->dma, req->req.length,
+		(ep->flags & EPT_FLAG_IN) ?
+		DMA_TO_DEVICE : DMA_FROM_DEVICE);
+
+	if (req->req.complete) {
+		req->req.status = -ECONNRESET;
+		spin_unlock_irqrestore(&ui->lock, flags);
+		req->req.complete(&ep->ep, &req->req);
+		spin_lock_irqsave(&ui->lock, flags);
+	}
+
+	if (!req->live) {
+		/* Reprime the endpoint for the remaining transfers */
+		for (temp_req = ep->req ; temp_req ; temp_req = temp_req->next)
+			temp_req->live = 0;
+		if (ep->req)
+			usb_ept_start(ep);
+		spin_unlock_irqrestore(&ui->lock, flags);
+		return 0;
+	}
+	spin_unlock_irqrestore(&ui->lock, flags);
+	return 0;
+}
+
+static int
+usb_ept_set_halt(struct usb_ep *_ep, int value)
+{
+	struct msm_endpoint *ept = to_msm_endpoint(_ep);
+	struct usb_info *ui = ept->ui;
+	unsigned int in = ept->flags & EPT_FLAG_IN;
+	unsigned int n;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ui->lock, flags);
+
+	n = readl(USB_ENDPTCTRL(ept->num));
+
+	if (in) {
+		if (value)
+			n |= CTRL_TXS;
+		else {
+			n &= ~CTRL_TXS;
+			n |= CTRL_TXR;
+		}
+	} else {
+		if (value)
+			n |= CTRL_RXS;
+		else {
+			n &= ~CTRL_RXS;
+			n |= CTRL_RXR;
+		}
+	}
+	writel(n, USB_ENDPTCTRL(ept->num));
+	if (!value)
+		ept->wedged = 0;
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	return 0;
+}
+
+static int
+msm72k_set_halt(struct usb_ep *_ep, int value)
+{
+	struct msm_endpoint *ept = to_msm_endpoint(_ep);
+	unsigned int in = ept->flags & EPT_FLAG_IN;
+
+	if (value && in && ept->req)
+		return -EAGAIN;
+
+	usb_ept_set_halt(_ep, value);
+
+	return 0;
+}
+
+static int
+msm72k_fifo_status(struct usb_ep *_ep)
+{
+	return -EOPNOTSUPP;
+}
+
+static void
+msm72k_fifo_flush(struct usb_ep *_ep)
+{
+	flush_endpoint(to_msm_endpoint(_ep));
+}
+static int msm72k_set_wedge(struct usb_ep *_ep)
+{
+	struct msm_endpoint *ept = to_msm_endpoint(_ep);
+
+	if (ept->num == 0)
+		return -EINVAL;
+
+	ept->wedged = 1;
+
+	return msm72k_set_halt(_ep, 1);
+}
+
+static const struct usb_ep_ops msm72k_ep_ops = {
+	.enable		= msm72k_enable,
+	.disable	= msm72k_disable,
+
+	.alloc_request	= msm72k_alloc_request,
+	.free_request	= msm72k_free_request,
+
+	.queue		= msm72k_queue,
+	.dequeue	= msm72k_dequeue,
+
+	.set_halt	= msm72k_set_halt,
+	.set_wedge	= msm72k_set_wedge,
+	.fifo_status	= msm72k_fifo_status,
+	.fifo_flush	= msm72k_fifo_flush,
+};
+
+static int msm72k_get_frame(struct usb_gadget *_gadget)
+{
+	struct usb_info *ui = container_of(_gadget, struct usb_info, gadget);
+
+	/* frame number is in bits 13:3 */
+	return (readl(USB_FRINDEX) >> 3) & 0x000007FF;
+}
+
+/* VBUS reporting logically comes from a transceiver */
+static int msm72k_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
+{
+	struct usb_info *ui = container_of(_gadget, struct usb_info, gadget);
+	struct msm_otg *otg = to_msm_otg(ui->xceiv);
+
+	if (is_active || atomic_read(&otg->chg_type)
+					 == USB_CHG_TYPE__WALLCHARGER)
+		wake_lock(&ui->wlock);
+
+	msm_hsusb_set_vbus_state(is_active);
+	return 0;
+}
+
+/* SW workarounds
+Issue #1	- USB Spoof Disconnect Failure
+Symptom	- Writing 0 to run/stop bit of USBCMD doesn't cause disconnect
+SW workaround	- Making opmode non-driving and SuspendM set in function
+		register of SMSC phy
+*/
+/* drivers may have software control over D+ pullup */
+static int msm72k_pullup_internal(struct usb_gadget *_gadget, int is_active)
+{
+	struct usb_info *ui = container_of(_gadget, struct usb_info, gadget);
+	unsigned long flags;
+
+	if (is_active) {
+		spin_lock_irqsave(&ui->lock, flags);
+		if (is_usb_online(ui) && ui->driver)
+			writel(readl(USB_USBCMD) | USBCMD_RS, USB_USBCMD);
+		spin_unlock_irqrestore(&ui->lock, flags);
+	} else {
+		writel(readl(USB_USBCMD) & ~USBCMD_RS, USB_USBCMD);
+		/* S/W workaround, Issue#1 */
+		usb_phy_io_write(ui->xceiv, 0x48, 0x04);
+	}
+
+	/* Ensure pull-up operation is completed before returning */
+	mb();
+
+	return 0;
+}
+
+static int msm72k_pullup(struct usb_gadget *_gadget, int is_active)
+{
+	struct usb_info *ui = container_of(_gadget, struct usb_info, gadget);
+	struct msm_otg *otg = to_msm_otg(ui->xceiv);
+	unsigned long flags;
+
+	atomic_set(&ui->softconnect, is_active);
+
+	spin_lock_irqsave(&ui->lock, flags);
+	if (ui->usb_state == USB_STATE_NOTATTACHED || ui->driver == NULL ||
+		atomic_read(&otg->chg_type) == USB_CHG_TYPE__WALLCHARGER) {
+		spin_unlock_irqrestore(&ui->lock, flags);
+		return 0;
+	}
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	msm72k_pullup_internal(_gadget, is_active);
+
+	if (is_active && !ui->gadget.is_a_peripheral)
+		schedule_delayed_work(&ui->chg_det, USB_CHG_DET_DELAY);
+
+	return 0;
+}
+
+static int msm72k_wakeup(struct usb_gadget *_gadget)
+{
+	struct usb_info *ui = container_of(_gadget, struct usb_info, gadget);
+	struct msm_otg *otg = to_msm_otg(ui->xceiv);
+
+	if (!atomic_read(&ui->remote_wakeup)) {
+		dev_err(&ui->pdev->dev,
+			"%s: remote wakeup not supported\n", __func__);
+		return -ENOTSUPP;
+	}
+
+	if (!atomic_read(&ui->configured)) {
+		dev_err(&ui->pdev->dev,
+			"%s: device is not configured\n", __func__);
+		return -ENODEV;
+	}
+	usb_phy_set_suspend(ui->xceiv, 0);
+
+	disable_irq(otg->irq);
+
+	if (!is_usb_active())
+		writel(readl(USB_PORTSC) | PORTSC_FPR, USB_PORTSC);
+
+	/* Ensure that USB port is resumed before enabling the IRQ */
+	mb();
+
+	enable_irq(otg->irq);
+
+	return 0;
+}
+
+/* when Gadget is configured, it will indicate how much power
+ * can be pulled from vbus, as specified in configuiration descriptor
+ */
+static int msm72k_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
+{
+	struct usb_info *ui = container_of(_gadget, struct usb_info, gadget);
+	unsigned long flags;
+
+
+	spin_lock_irqsave(&ui->lock, flags);
+	ui->b_max_pow = mA;
+	ui->flags = USB_FLAG_CONFIGURED;
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	schedule_work(&ui->work);
+
+	return 0;
+}
+
+static int msm72k_set_selfpowered(struct usb_gadget *_gadget, int set)
+{
+	struct usb_info *ui = container_of(_gadget, struct usb_info, gadget);
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&ui->lock, flags);
+	if (set) {
+		if (ui->pdata && ui->pdata->self_powered)
+			atomic_set(&ui->self_powered, 1);
+		else
+			ret = -EOPNOTSUPP;
+	} else {
+		/* We can always work as a bus powered device */
+		atomic_set(&ui->self_powered, 0);
+	}
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	return ret;
+
+}
+
+static int msm72k_gadget_start(struct usb_gadget_driver *driver,
+		int (*bind)(struct usb_gadget *));
+static int msm72k_gadget_stop(struct usb_gadget_driver *driver);
+
+
+static const struct usb_gadget_ops msm72k_ops = {
+	.get_frame	= msm72k_get_frame,
+	.vbus_session	= msm72k_udc_vbus_session,
+	.vbus_draw	= msm72k_udc_vbus_draw,
+	.pullup		= msm72k_pullup,
+	.wakeup		= msm72k_wakeup,
+	.set_selfpowered = msm72k_set_selfpowered,
+	.start		= msm72k_gadget_start,
+	.stop		= msm72k_gadget_stop
+};
+
+static void usb_do_remote_wakeup(struct work_struct *w)
+{
+	struct usb_info *ui = the_usb_info;
+
+	msm72k_wakeup(&ui->gadget);
+}
+
+static ssize_t usb_remote_wakeup(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct usb_info *ui = the_usb_info;
+
+	msm72k_wakeup(&ui->gadget);
+
+	return count;
+}
+
+static ssize_t show_usb_state(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	size_t i;
+	char *state[] = {"USB_STATE_NOTATTACHED", "USB_STATE_ATTACHED",
+			"USB_STATE_POWERED", "USB_STATE_UNAUTHENTICATED",
+			"USB_STATE_RECONNECTING", "USB_STATE_DEFAULT",
+			"USB_STATE_ADDRESS", "USB_STATE_CONFIGURED",
+			"USB_STATE_SUSPENDED"
+	};
+
+	i = scnprintf(buf, PAGE_SIZE, "%s\n", state[msm_hsusb_get_state()]);
+	return i;
+}
+
+static ssize_t show_usb_speed(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	struct usb_info *ui = the_usb_info;
+	size_t i;
+	char *speed[] = {"USB_SPEED_UNKNOWN", "USB_SPEED_LOW",
+			"USB_SPEED_FULL", "USB_SPEED_HIGH"};
+
+	i = scnprintf(buf, PAGE_SIZE, "%s\n", speed[ui->gadget.speed]);
+	return i;
+}
+
+static ssize_t store_usb_chg_current(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct usb_info *ui = the_usb_info;
+	unsigned long mA;
+
+	if (ui->gadget.is_a_peripheral)
+		return -EINVAL;
+
+	if (strict_strtoul(buf, 10, &mA))
+		return -EINVAL;
+
+	ui->chg_current = mA;
+	usb_phy_set_power(ui->xceiv, mA);
+
+	return count;
+}
+
+static ssize_t show_usb_chg_current(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct usb_info *ui = the_usb_info;
+	size_t count;
+
+	count = snprintf(buf, PAGE_SIZE, "%d", ui->chg_current);
+
+	return count;
+}
+
+static ssize_t show_usb_chg_type(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct usb_info *ui = the_usb_info;
+	struct msm_otg *otg = to_msm_otg(ui->xceiv);
+	size_t count;
+	char *chg_type[] = {"STD DOWNSTREAM PORT",
+			"CARKIT",
+			"DEDICATED CHARGER",
+			"INVALID"};
+
+	count = snprintf(buf, PAGE_SIZE, "%s",
+			chg_type[atomic_read(&otg->chg_type)]);
+
+	return count;
+}
+static DEVICE_ATTR(wakeup, S_IWUSR, 0, usb_remote_wakeup);
+static DEVICE_ATTR(usb_state, S_IRUSR, show_usb_state, 0);
+static DEVICE_ATTR(usb_speed, S_IRUSR, show_usb_speed, 0);
+static DEVICE_ATTR(chg_type, S_IRUSR, show_usb_chg_type, 0);
+static DEVICE_ATTR(chg_current, S_IWUSR | S_IRUSR,
+		show_usb_chg_current, store_usb_chg_current);
+
+#ifdef CONFIG_USB_OTG
+static ssize_t store_host_req(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct usb_info *ui = the_usb_info;
+	unsigned long val, flags;
+
+	if (strict_strtoul(buf, 10, &val))
+		return -EINVAL;
+
+	dev_dbg(&ui->pdev->dev, "%s host request\n",
+			val ? "set" : "clear");
+
+	spin_lock_irqsave(&ui->lock, flags);
+	if (ui->hnp_avail)
+		ui->gadget.host_request = !!val;
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	return count;
+}
+static DEVICE_ATTR(host_request, S_IWUSR, NULL, store_host_req);
+
+/* How do we notify user space about HNP availability?
+ * As we are compliant to Rev 2.0, Host will not set a_hnp_support.
+ * Introduce hnp_avail flag and set when HNP polling request arrives.
+ * The expectation is that user space checks hnp availability before
+ * requesting host role via above sysfs node.
+ */
+static ssize_t show_host_avail(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct usb_info *ui = the_usb_info;
+	size_t count;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ui->lock, flags);
+	count = snprintf(buf, PAGE_SIZE, "%d\n", ui->hnp_avail);
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	return count;
+}
+static DEVICE_ATTR(host_avail, S_IRUSR, show_host_avail, NULL);
+
+static struct attribute *otg_attrs[] = {
+	&dev_attr_host_request.attr,
+	&dev_attr_host_avail.attr,
+	NULL,
+};
+
+static struct attribute_group otg_attr_grp = {
+	.name  = "otg",
+	.attrs = otg_attrs,
+};
+#endif
+
+static int msm72k_probe(struct platform_device *pdev)
+{
+	struct usb_info *ui;
+	struct msm_otg *otg;
+	int retval;
+
+	dev_dbg(&pdev->dev, "msm72k_probe\n");
+	ui = kzalloc(sizeof(struct usb_info), GFP_KERNEL);
+	if (!ui)
+		return -ENOMEM;
+
+	ui->pdev = pdev;
+	ui->pdata = pdev->dev.platform_data;
+
+	ui->buf = dma_alloc_coherent(&pdev->dev, 4096, &ui->dma, GFP_KERNEL);
+	if (!ui->buf)
+		return usb_free(ui, -ENOMEM);
+
+	ui->pool = dma_pool_create("msm72k_udc", NULL, 32, 32, 0);
+	if (!ui->pool)
+		return usb_free(ui, -ENOMEM);
+
+	ui->xceiv = usb_get_transceiver();
+	if (!ui->xceiv)
+		return usb_free(ui, -ENODEV);
+
+	otg = to_msm_otg(ui->xceiv);
+	ui->addr = otg->regs;
+
+	ui->gadget.ops = &msm72k_ops;
+	ui->gadget.max_speed = USB_SPEED_HIGH;
+	device_initialize(&ui->gadget.dev);
+	dev_set_name(&ui->gadget.dev, "gadget");
+	ui->gadget.dev.parent = &pdev->dev;
+	ui->gadget.dev.dma_mask = pdev->dev.dma_mask;
+
+#ifdef CONFIG_USB_OTG
+	ui->gadget.is_otg = 1;
+#endif
+
+	retval = usb_add_gadget_udc(&pdev->dev, &ui->gadget);
+	if (retval)
+		return usb_free(ui, retval);
+
+	ui->sdev.name = DRIVER_NAME;
+	ui->sdev.print_name = print_switch_name;
+	ui->sdev.print_state = print_switch_state;
+
+	retval = switch_dev_register(&ui->sdev);
+	if (retval)
+		return usb_free(ui, retval);
+
+	the_usb_info = ui;
+
+	wake_lock_init(&ui->wlock,
+			WAKE_LOCK_SUSPEND, "usb_bus_active");
+
+	usb_debugfs_init(ui);
+
+	usb_prepare(ui);
+
+#ifdef CONFIG_USB_OTG
+	retval = sysfs_create_group(&pdev->dev.kobj, &otg_attr_grp);
+	if (retval) {
+		dev_err(&ui->pdev->dev,
+			"failed to create otg sysfs directory:"
+			"err:(%d)\n", retval);
+	}
+#endif
+
+	retval = otg_set_peripheral(ui->xceiv->otg, &ui->gadget);
+	if (retval) {
+		dev_err(&ui->pdev->dev,
+			"%s: Cannot bind the transceiver, retval:(%d)\n",
+			__func__, retval);
+		switch_dev_unregister(&ui->sdev);
+		wake_lock_destroy(&ui->wlock);
+		return usb_free(ui, retval);
+	}
+
+	pm_runtime_enable(&pdev->dev);
+
+	/* Setup phy stuck timer */
+	if (ui->pdata && ui->pdata->is_phy_status_timer_on)
+		setup_timer(&phy_status_timer, usb_phy_status_check_timer, 0);
+	return 0;
+}
+
+static int msm72k_gadget_start(struct usb_gadget_driver *driver,
+			    int (*bind)(struct usb_gadget *))
+{
+	struct usb_info *ui = the_usb_info;
+	int			retval, n;
+
+	if (!driver
+			|| driver->max_speed < USB_SPEED_FULL
+			|| !bind
+			|| !driver->disconnect
+			|| !driver->setup)
+		return -EINVAL;
+	if (!ui)
+		return -ENODEV;
+	if (ui->driver)
+		return -EBUSY;
+
+	/* first hook up the driver ... */
+	ui->driver = driver;
+	ui->gadget.dev.driver = &driver->driver;
+	ui->gadget.name = driver_name;
+	INIT_LIST_HEAD(&ui->gadget.ep_list);
+	ui->gadget.ep0 = &ui->ep0in.ep;
+	INIT_LIST_HEAD(&ui->gadget.ep0->ep_list);
+	ui->gadget.speed = USB_SPEED_UNKNOWN;
+	atomic_set(&ui->softconnect, 1);
+
+	for (n = 1; n < 16; n++) {
+		struct msm_endpoint *ept = ui->ept + n;
+		list_add_tail(&ept->ep.ep_list, &ui->gadget.ep_list);
+		ept->ep.maxpacket = 512;
+	}
+	for (n = 17; n < 32; n++) {
+		struct msm_endpoint *ept = ui->ept + n;
+		list_add_tail(&ept->ep.ep_list, &ui->gadget.ep_list);
+		ept->ep.maxpacket = 512;
+	}
+
+	retval = device_add(&ui->gadget.dev);
+	if (retval)
+		goto fail;
+
+	retval = bind(&ui->gadget);
+	if (retval) {
+		dev_err(&ui->pdev->dev, "bind to driver %s --> error %d\n",
+				driver->driver.name, retval);
+		device_del(&ui->gadget.dev);
+		goto fail;
+	}
+
+	retval = device_create_file(&ui->gadget.dev, &dev_attr_wakeup);
+	if (retval != 0)
+		dev_err(&ui->pdev->dev, "failed to create sysfs entry:"
+			"(wakeup) error: (%d)\n", retval);
+	retval = device_create_file(&ui->gadget.dev, &dev_attr_usb_state);
+	if (retval != 0)
+		dev_err(&ui->pdev->dev, "failed to create sysfs entry:"
+			" (usb_state) error: (%d)\n", retval);
+
+	retval = device_create_file(&ui->gadget.dev, &dev_attr_usb_speed);
+	if (retval != 0)
+		dev_err(&ui->pdev->dev, "failed to create sysfs entry:"
+			" (usb_speed) error: (%d)\n", retval);
+
+	retval = device_create_file(&ui->gadget.dev, &dev_attr_chg_type);
+	if (retval != 0)
+		dev_err(&ui->pdev->dev,
+			"failed to create sysfs entry(chg_type): err:(%d)\n",
+					retval);
+	retval = device_create_file(&ui->gadget.dev, &dev_attr_chg_current);
+	if (retval != 0)
+		dev_err(&ui->pdev->dev,
+			"failed to create sysfs entry(chg_current):"
+			"err:(%d)\n", retval);
+
+	dev_dbg(&ui->pdev->dev, "registered gadget driver '%s'\n",
+			driver->driver.name);
+	usb_start(ui);
+
+	return 0;
+
+fail:
+	ui->driver = NULL;
+	ui->gadget.dev.driver = NULL;
+	return retval;
+}
+
+static int msm72k_gadget_stop(struct usb_gadget_driver *driver)
+{
+	struct usb_info *dev = the_usb_info;
+
+	if (!dev)
+		return -ENODEV;
+	if (!driver || driver != dev->driver || !driver->unbind)
+		return -EINVAL;
+
+	msm72k_pullup_internal(&dev->gadget, 0);
+	if (dev->irq) {
+		free_irq(dev->irq, dev);
+		dev->irq = 0;
+	}
+	dev->state = USB_STATE_IDLE;
+	atomic_set(&dev->configured, 0);
+	switch_set_state(&dev->sdev, 0);
+	/* cancel pending ep0 transactions */
+	flush_endpoint(&dev->ep0out);
+	flush_endpoint(&dev->ep0in);
+
+	device_remove_file(&dev->gadget.dev, &dev_attr_wakeup);
+	device_remove_file(&dev->gadget.dev, &dev_attr_usb_state);
+	device_remove_file(&dev->gadget.dev, &dev_attr_usb_speed);
+	device_remove_file(&dev->gadget.dev, &dev_attr_chg_type);
+	device_remove_file(&dev->gadget.dev, &dev_attr_chg_current);
+	driver->disconnect(&dev->gadget);
+	driver->unbind(&dev->gadget);
+	dev->gadget.dev.driver = NULL;
+	dev->driver = NULL;
+
+	device_del(&dev->gadget.dev);
+
+	dev_dbg(&dev->pdev->dev,
+		"unregistered gadget driver '%s'\n", driver->driver.name);
+	return 0;
+}
+
+
+static int msm72k_udc_runtime_suspend(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: suspending...\n");
+	return 0;
+}
+
+static int msm72k_udc_runtime_resume(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: resuming...\n");
+	return 0;
+}
+
+static int msm72k_udc_runtime_idle(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: idling...\n");
+	return 0;
+}
+
+static struct dev_pm_ops msm72k_udc_dev_pm_ops = {
+	.runtime_suspend = msm72k_udc_runtime_suspend,
+	.runtime_resume = msm72k_udc_runtime_resume,
+	.runtime_idle = msm72k_udc_runtime_idle
+};
+
+static int __exit msm72k_remove(struct platform_device *pdev)
+{
+	struct usb_info *ui = container_of(&pdev, struct usb_info, pdev);
+
+	return usb_free(ui, 0);
+}
+
+static struct platform_driver usb_driver = {
+	.probe = msm72k_probe,
+	.remove = msm72k_remove,
+	.driver = { .name = "msm_hsusb",
+		    .pm = &msm72k_udc_dev_pm_ops, },
+};
+
+static int __init init(void)
+{
+	return platform_driver_register(&usb_driver);
+}
+module_init(init);
+
+static void __exit cleanup(void)
+{
+	platform_driver_unregister(&usb_driver);
+}
+module_exit(cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Mike Lockwood, Brian Swetland");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/qcom_maemo.c b/drivers/usb/gadget/qcom_maemo.c
new file mode 100644
index 0000000..39686c4
--- /dev/null
+++ b/drivers/usb/gadget/qcom_maemo.c
@@ -0,0 +1,304 @@
+/*
+ * Qualcomm Maemo Composite driver
+ *
+ * Copyright (C) 2008 David Brownell
+ * Copyright (C) 2008 Nokia Corporation
+ * Copyright (C) 2009 Samsung Electronics
+ * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program from the Code Aurora Forum is free software; you can
+ * redistribute it and/or modify it under the GNU General Public License
+ * version 2 and only version 2 as published by the Free Software Foundation.
+ * The original work available from [git.kernel.org ] is subject to the
+ * notice below.
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/utsname.h>
+#include <linux/kdev_t.h>
+#include <linux/delay.h>
+
+
+#define DRIVER_DESC		"Qcom Maemo Composite Gadget"
+#define VENDOR_ID		0x05c6
+#define PRODUCT_ID		0x902E
+
+/*
+ * kbuild is not very cooperative with respect to linking separately
+ * compiled library objects into one module.  So for now we won't use
+ * separate compilation ... ensuring init/exit sections work to shrink
+ * the runtime footprint, and giving us at least some parts of what
+ * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
+ */
+
+#include "composite.c"
+#include "usbstring.c"
+#include "config.c"
+#include "epautoconf.c"
+
+#define USB_ETH
+
+#define USB_ETH_RNDIS
+#ifdef USB_ETH_RNDIS
+#  include "f_rndis.c"
+#  include "rndis.c"
+#endif
+
+
+#include "u_serial.c"
+#include "f_serial.c"
+
+#include "u_ether.c"
+
+#undef DBG     /* u_ether.c has broken idea about macros */
+#undef VDBG    /* so clean up after it */
+#undef ERROR
+#undef INFO
+
+#include "f_mass_storage.c"
+#include "f_diag.c"
+#include "f_rmnet.c"
+
+/*-------------------------------------------------------------------------*/
+/* string IDs are assigned dynamically */
+
+#define STRING_MANUFACTURER_IDX         0
+#define STRING_PRODUCT_IDX              1
+#define STRING_SERIAL_IDX               2
+
+/* String Table */
+static struct usb_string strings_dev[] = {
+	/* These dummy values should be overridden by platform data */
+	[STRING_MANUFACTURER_IDX].s = "Qualcomm Incorporated",
+	[STRING_PRODUCT_IDX].s = "Usb composition",
+	[STRING_SERIAL_IDX].s = "0123456789ABCDEF",
+	{  }                    /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+	.language       = 0x0409,       /* en-us */
+	.strings        = strings_dev,
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+	&stringtab_dev,
+	NULL,
+};
+
+static struct usb_device_descriptor device_desc = {
+	.bLength              = sizeof(device_desc),
+	.bDescriptorType      = USB_DT_DEVICE,
+	.bcdUSB               = __constant_cpu_to_le16(0x0200),
+	.bDeviceClass         = USB_CLASS_PER_INTERFACE,
+	.bDeviceSubClass      =      0,
+	.bDeviceProtocol      =      0,
+	.idVendor             = __constant_cpu_to_le16(VENDOR_ID),
+	.idProduct            = __constant_cpu_to_le16(PRODUCT_ID),
+	.bcdDevice            = __constant_cpu_to_le16(0xffff),
+	.bNumConfigurations   = 1,
+};
+
+static u8 hostaddr[ETH_ALEN];
+static struct usb_diag_ch *diag_ch;
+static struct usb_diag_platform_data usb_diag_pdata = {
+	.ch_name = DIAG_LEGACY,
+};
+
+/****************************** Configurations ******************************/
+static struct fsg_module_parameters mod_data = {
+	.stall = 0
+};
+FSG_MODULE_PARAMETERS(/* no prefix */, mod_data);
+
+static struct fsg_common *fsg_common;
+static int maemo_setup_config(struct usb_configuration *c,
+			const struct usb_ctrlrequest *ctrl);
+
+static int maemo_do_config(struct usb_configuration *c)
+{
+	int ret;
+
+	ret = rndis_bind_config(c, hostaddr);
+	if (ret < 0)
+		return ret;
+
+	ret = diag_function_add(c);
+	if (ret < 0)
+		return ret;
+
+	ret = gser_bind_config(c, 0);
+	if (ret < 0)
+		return ret;
+
+	ret = gser_bind_config(c, 1);
+	if (ret < 0)
+		return ret;
+
+	ret = rmnet_function_add(c);
+	if (ret < 0)
+		return ret;
+
+	ret = fsg_add(c->cdev, c, fsg_common);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static struct usb_configuration maemo_config_driver = {
+	.label			= "Qcom Maemo Gadget",
+	.bind			= maemo_do_config,
+	.setup			= maemo_setup_config,
+	.bConfigurationValue	= 1,
+	.bMaxPower		= 0xFA,
+};
+static int maemo_setup_config(struct usb_configuration *c,
+		const struct usb_ctrlrequest *ctrl)
+{
+	int i;
+	int ret = -EOPNOTSUPP;
+
+	for (i = 0; i < maemo_config_driver.next_interface_id; i++) {
+		if (maemo_config_driver.interface[i]->setup) {
+			ret = maemo_config_driver.interface[i]->setup(
+				maemo_config_driver.interface[i], ctrl);
+			if (ret >= 0)
+				return ret;
+		}
+	}
+
+	return ret;
+}
+
+static int maemo_bind(struct usb_composite_dev *cdev)
+{
+	struct usb_gadget *gadget = cdev->gadget;
+	int status, gcnum;
+
+	/* set up diag channel */
+	diag_ch = diag_setup(&usb_diag_pdata);
+	if (IS_ERR(diag_ch))
+		return PTR_ERR(diag_ch);
+
+	/* set up network link layer */
+	status = gether_setup(cdev->gadget, hostaddr);
+	if (status < 0)
+		goto diag_clean;
+
+	/* set up serial link layer */
+	status = gserial_setup(cdev->gadget, 2);
+	if (status < 0)
+		goto fail0;
+
+	/* set up mass storage function */
+	fsg_common = fsg_common_from_params(0, cdev, &mod_data);
+	if (IS_ERR(fsg_common)) {
+		status = PTR_ERR(fsg_common);
+		goto fail1;
+	}
+
+	gcnum = usb_gadget_controller_number(gadget);
+	if (gcnum >= 0)
+		device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
+	else {
+		/* gadget zero is so simple (for now, no altsettings) that
+		 * it SHOULD NOT have problems with bulk-capable hardware.
+		 * so just warn about unrcognized controllers -- don't panic.
+		 *
+		 * things like configuration and altsetting numbering
+		 * can need hardware-specific attention though.
+		 */
+		WARNING(cdev, "controller '%s' not recognized\n",
+			gadget->name);
+		device_desc.bcdDevice = __constant_cpu_to_le16(0x9999);
+	}
+
+	/* Allocate string descriptor numbers ... note that string
+	 * contents can be overridden by the composite_dev glue.
+	*/
+
+	status = usb_string_id(cdev);
+	if (status < 0)
+		goto fail2;
+	strings_dev[STRING_MANUFACTURER_IDX].id = status;
+	device_desc.iManufacturer = status;
+
+	status = usb_string_id(cdev);
+	if (status < 0)
+		goto fail2;
+	strings_dev[STRING_PRODUCT_IDX].id = status;
+	device_desc.iProduct = status;
+
+	if (!usb_gadget_set_selfpowered(gadget))
+		maemo_config_driver.bmAttributes |= USB_CONFIG_ATT_SELFPOWER;
+
+	if (gadget->ops->wakeup)
+		maemo_config_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+
+	/* register our first configuration */
+	status = usb_add_config(cdev, &maemo_config_driver);
+	if (status < 0)
+		goto fail2;
+
+	usb_gadget_set_selfpowered(gadget);
+	dev_info(&gadget->dev, DRIVER_DESC "\n");
+	fsg_common_put(fsg_common);
+	return 0;
+
+fail2:
+	fsg_common_put(fsg_common);
+fail1:
+	gserial_cleanup();
+fail0:
+	gether_cleanup();
+diag_clean:
+	diag_cleanup(diag_ch);
+
+	return status;
+}
+
+static int __exit maemo_unbind(struct usb_composite_dev *cdev)
+{
+	gserial_cleanup();
+	gether_cleanup();
+	diag_cleanup(diag_ch);
+	return 0;
+}
+
+static struct usb_composite_driver qcom_maemo_driver = {
+	.name		= "Qcom Maemo Gadget",
+	.dev		= &device_desc,
+	.strings	= dev_strings,
+	.bind		= maemo_bind,
+	.unbind		= __exit_p(maemo_unbind),
+};
+
+static int __init qcom_maemo_usb_init(void)
+{
+	return usb_composite_register(&qcom_maemo_driver);
+}
+module_init(qcom_maemo_usb_init);
+
+static void __exit qcom_maemo_usb_cleanup(void)
+{
+	usb_composite_unregister(&qcom_maemo_driver);
+}
+module_exit(qcom_maemo_usb_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("1.0");
diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c
index 73a934a..0cb2121 100644
--- a/drivers/usb/gadget/rndis.c
+++ b/drivers/usb/gadget/rndis.c
@@ -1146,11 +1146,15 @@
 
 #endif /* CONFIG_USB_GADGET_DEBUG_FILES */
 
+static bool rndis_initialized;
 
 int rndis_init(void)
 {
 	u8 i;
 
+	if (rndis_initialized)
+		return 0;
+
 	for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
 #ifdef	CONFIG_USB_GADGET_DEBUG_FILES
 		char name [20];
@@ -1177,6 +1181,7 @@
 		INIT_LIST_HEAD(&(rndis_per_dev_params[i].resp_queue));
 	}
 
+	rndis_initialized = true;
 	return 0;
 }
 
@@ -1185,7 +1190,13 @@
 #ifdef CONFIG_USB_GADGET_DEBUG_FILES
 	u8 i;
 	char name[20];
+#endif
 
+	if (!rndis_initialized)
+		return;
+	rndis_initialized = false;
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
 	for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
 		sprintf(name, NAME_TEMPLATE, i);
 		remove_proc_entry(name, NULL);
diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c
index 8081ca3..3c57df4 100644
--- a/drivers/usb/gadget/storage_common.c
+++ b/drivers/usb/gadget/storage_common.c
@@ -207,6 +207,17 @@
 	unsigned int	blkbits;	/* Bits of logical block size of bound block device */
 	unsigned int	blksize;	/* logical block size of bound block device */
 	struct device	dev;
+#ifdef CONFIG_USB_MSC_PROFILING
+	spinlock_t	lock;
+	struct {
+
+		unsigned long rbytes;
+		unsigned long wbytes;
+		ktime_t rtime;
+		ktime_t wtime;
+	} perf;
+
+#endif
 };
 
 #define fsg_lun_is_open(curlun)	((curlun)->filp != NULL)
@@ -221,6 +232,9 @@
 #define EP0_BUFSIZE	256
 #define DELAYED_STATUS	(EP0_BUFSIZE + 999)	/* An impossibly large value */
 
+#ifdef CONFIG_USB_CSW_HACK
+#define fsg_num_buffers		4
+#else
 #ifdef CONFIG_USB_GADGET_DEBUG_FILES
 
 static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
@@ -236,6 +250,7 @@
 #define fsg_num_buffers	CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
 
 #endif /* CONFIG_USB_DEBUG */
+#endif /* CONFIG_USB_CSW_HACK */
 
 /* check if fsg_num_buffers is within a valid range */
 static inline int fsg_num_buffers_validate(void)
@@ -786,6 +801,43 @@
 	return sprintf(buf, "%u\n", curlun->nofua);
 }
 
+#ifdef CONFIG_USB_MSC_PROFILING
+static ssize_t fsg_show_perf(struct device *dev, struct device_attribute *attr,
+			      char *buf)
+{
+	struct fsg_lun	*curlun = fsg_lun_from_dev(dev);
+	unsigned long rbytes, wbytes;
+	int64_t rtime, wtime;
+
+	spin_lock(&curlun->lock);
+	rbytes = curlun->perf.rbytes;
+	wbytes = curlun->perf.wbytes;
+	rtime = ktime_to_us(curlun->perf.rtime);
+	wtime = ktime_to_us(curlun->perf.wtime);
+	spin_unlock(&curlun->lock);
+
+	return snprintf(buf, PAGE_SIZE, "Write performance :"
+					"%lu bytes in %lld microseconds\n"
+					"Read performance :"
+					"%lu bytes in %lld microseconds\n",
+					wbytes, wtime, rbytes, rtime);
+}
+static ssize_t fsg_store_perf(struct device *dev, struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	struct fsg_lun	*curlun = fsg_lun_from_dev(dev);
+	int value;
+
+	sscanf(buf, "%d", &value);
+	if (!value) {
+		spin_lock(&curlun->lock);
+		memset(&curlun->perf, 0, sizeof(curlun->perf));
+		spin_unlock(&curlun->lock);
+	}
+
+	return count;
+}
+#endif
 static ssize_t fsg_show_file(struct device *dev, struct device_attribute *attr,
 			     char *buf)
 {
@@ -872,10 +924,16 @@
 	struct rw_semaphore	*filesem = dev_get_drvdata(dev);
 	int		rc = 0;
 
+
+#ifndef CONFIG_USB_ANDROID_MASS_STORAGE
+	/* disabled in android because we need to allow closing the backing file
+	 * if the media was removed
+	 */
 	if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) {
 		LDBG(curlun, "eject attempt prevented\n");
 		return -EBUSY;				/* "Door is locked" */
 	}
+#endif
 
 	/* Remove a trailing newline */
 	if (count > 0 && buf[count-1] == '\n')
diff --git a/drivers/usb/gadget/u_bam.c b/drivers/usb/gadget/u_bam.c
new file mode 100644
index 0000000..d379c66
--- /dev/null
+++ b/drivers/usb/gadget/u_bam.c
@@ -0,0 +1,1262 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/termios.h>
+#include <mach/msm_smd.h>
+#include <linux/netdevice.h>
+#include <mach/bam_dmux.h>
+#include <linux/debugfs.h>
+#include <linux/bitops.h>
+#include <linux/termios.h>
+
+#include <mach/usb_gadget_xport.h>
+#include <mach/usb_bam.h>
+
+#include "u_rmnet.h"
+
+#define BAM_N_PORTS	1
+#define BAM2BAM_N_PORTS	3
+
+static struct workqueue_struct *gbam_wq;
+static int n_bam_ports;
+static int n_bam2bam_ports;
+static unsigned n_tx_req_queued;
+static unsigned bam_ch_ids[] = { 8 };
+
+static const char *bam_ch_names[] = { "bam_dmux_ch_8" };
+
+#define BAM_PENDING_LIMIT			220
+#define BAM_MUX_TX_PKT_DROP_THRESHOLD		1000
+#define BAM_MUX_RX_PKT_FCTRL_EN_TSHOLD		500
+#define BAM_MUX_RX_PKT_FCTRL_DIS_TSHOLD		300
+#define BAM_MUX_RX_PKT_FLOW_CTRL_SUPPORT	1
+
+#define BAM_MUX_HDR				8
+
+#define BAM_MUX_RX_Q_SIZE			16
+#define BAM_MUX_TX_Q_SIZE			200
+#define BAM_MUX_RX_REQ_SIZE			(2048 - BAM_MUX_HDR)
+
+#define DL_INTR_THRESHOLD			20
+
+unsigned int bam_mux_tx_pkt_drop_thld = BAM_MUX_TX_PKT_DROP_THRESHOLD;
+module_param(bam_mux_tx_pkt_drop_thld, uint, S_IRUGO | S_IWUSR);
+
+unsigned int bam_mux_rx_fctrl_en_thld = BAM_MUX_RX_PKT_FCTRL_EN_TSHOLD;
+module_param(bam_mux_rx_fctrl_en_thld, uint, S_IRUGO | S_IWUSR);
+
+unsigned int bam_mux_rx_fctrl_support = BAM_MUX_RX_PKT_FLOW_CTRL_SUPPORT;
+module_param(bam_mux_rx_fctrl_support, uint, S_IRUGO | S_IWUSR);
+
+unsigned int bam_mux_rx_fctrl_dis_thld = BAM_MUX_RX_PKT_FCTRL_DIS_TSHOLD;
+module_param(bam_mux_rx_fctrl_dis_thld, uint, S_IRUGO | S_IWUSR);
+
+unsigned int bam_mux_tx_q_size = BAM_MUX_TX_Q_SIZE;
+module_param(bam_mux_tx_q_size, uint, S_IRUGO | S_IWUSR);
+
+unsigned int bam_mux_rx_q_size = BAM_MUX_RX_Q_SIZE;
+module_param(bam_mux_rx_q_size, uint, S_IRUGO | S_IWUSR);
+
+unsigned int bam_mux_rx_req_size = BAM_MUX_RX_REQ_SIZE;
+module_param(bam_mux_rx_req_size, uint, S_IRUGO | S_IWUSR);
+
+unsigned int dl_intr_threshold = DL_INTR_THRESHOLD;
+module_param(dl_intr_threshold, uint, S_IRUGO | S_IWUSR);
+
+#define BAM_CH_OPENED	BIT(0)
+#define BAM_CH_READY	BIT(1)
+
+struct bam_ch_info {
+	unsigned long		flags;
+	unsigned		id;
+
+	struct list_head        tx_idle;
+	struct sk_buff_head	tx_skb_q;
+
+	struct list_head        rx_idle;
+	struct sk_buff_head	rx_skb_q;
+
+	struct gbam_port	*port;
+	struct work_struct	write_tobam_w;
+	struct work_struct	write_tohost_w;
+
+	struct usb_request	*rx_req;
+	struct usb_request	*tx_req;
+
+	u8					src_pipe_idx;
+	u8					dst_pipe_idx;
+	u8					connection_idx;
+
+	/* stats */
+	unsigned int		pending_with_bam;
+	unsigned int		tohost_drp_cnt;
+	unsigned int		tomodem_drp_cnt;
+	unsigned int		tx_len;
+	unsigned int		rx_len;
+	unsigned long		to_modem;
+	unsigned long		to_host;
+};
+
+struct gbam_port {
+	unsigned		port_num;
+	spinlock_t		port_lock_ul;
+	spinlock_t		port_lock_dl;
+
+	struct grmnet		*port_usb;
+	struct grmnet		*gr;
+
+	struct bam_ch_info	data_ch;
+
+	struct work_struct	connect_w;
+	struct work_struct	disconnect_w;
+};
+
+static struct bam_portmaster {
+	struct gbam_port *port;
+	struct platform_driver pdrv;
+} bam_ports[BAM_N_PORTS];
+
+struct gbam_port *bam2bam_ports[BAM2BAM_N_PORTS];
+static void gbam_start_rx(struct gbam_port *port);
+static void gbam_start_endless_rx(struct gbam_port *port);
+static void gbam_start_endless_tx(struct gbam_port *port);
+
+/*---------------misc functions---------------- */
+static void gbam_free_requests(struct usb_ep *ep, struct list_head *head)
+{
+	struct usb_request	*req;
+
+	while (!list_empty(head)) {
+		req = list_entry(head->next, struct usb_request, list);
+		list_del(&req->list);
+		usb_ep_free_request(ep, req);
+	}
+}
+
+static int gbam_alloc_requests(struct usb_ep *ep, struct list_head *head,
+		int num,
+		void (*cb)(struct usb_ep *ep, struct usb_request *),
+		gfp_t flags)
+{
+	int i;
+	struct usb_request *req;
+
+	pr_debug("%s: ep:%p head:%p num:%d cb:%p", __func__,
+			ep, head, num, cb);
+
+	for (i = 0; i < num; i++) {
+		req = usb_ep_alloc_request(ep, flags);
+		if (!req) {
+			pr_debug("%s: req allocated:%d\n", __func__, i);
+			return list_empty(head) ? -ENOMEM : 0;
+		}
+		req->complete = cb;
+		list_add(&req->list, head);
+	}
+
+	return 0;
+}
+/*--------------------------------------------- */
+
+/*------------data_path----------------------------*/
+static void gbam_write_data_tohost(struct gbam_port *port)
+{
+	unsigned long			flags;
+	struct bam_ch_info		*d = &port->data_ch;
+	struct sk_buff			*skb;
+	int				ret;
+	struct usb_request		*req;
+	struct usb_ep			*ep;
+
+	spin_lock_irqsave(&port->port_lock_dl, flags);
+	if (!port->port_usb) {
+		spin_unlock_irqrestore(&port->port_lock_dl, flags);
+		return;
+	}
+
+	ep = port->port_usb->in;
+
+	while (!list_empty(&d->tx_idle)) {
+		skb = __skb_dequeue(&d->tx_skb_q);
+		if (!skb) {
+			spin_unlock_irqrestore(&port->port_lock_dl, flags);
+			return;
+		}
+		req = list_first_entry(&d->tx_idle,
+				struct usb_request,
+				list);
+		req->context = skb;
+		req->buf = skb->data;
+		req->length = skb->len;
+		n_tx_req_queued++;
+		if (n_tx_req_queued == dl_intr_threshold) {
+			req->no_interrupt = 0;
+			n_tx_req_queued = 0;
+		} else {
+			req->no_interrupt = 1;
+		}
+
+		list_del(&req->list);
+
+		spin_unlock(&port->port_lock_dl);
+		ret = usb_ep_queue(ep, req, GFP_ATOMIC);
+		spin_lock(&port->port_lock_dl);
+		if (ret) {
+			pr_err("%s: usb epIn failed\n", __func__);
+			list_add(&req->list, &d->tx_idle);
+			dev_kfree_skb_any(skb);
+			break;
+		}
+		d->to_host++;
+	}
+	spin_unlock_irqrestore(&port->port_lock_dl, flags);
+}
+
+static void gbam_write_data_tohost_w(struct work_struct *w)
+{
+	struct bam_ch_info	*d;
+	struct gbam_port	*port;
+
+	d = container_of(w, struct bam_ch_info, write_tohost_w);
+	port = d->port;
+
+	gbam_write_data_tohost(port);
+}
+
+void gbam_data_recv_cb(void *p, struct sk_buff *skb)
+{
+	struct gbam_port	*port = p;
+	struct bam_ch_info	*d = &port->data_ch;
+	unsigned long		flags;
+
+	if (!skb)
+		return;
+
+	pr_debug("%s: p:%p#%d d:%p skb_len:%d\n", __func__,
+			port, port->port_num, d, skb->len);
+
+	spin_lock_irqsave(&port->port_lock_dl, flags);
+	if (!port->port_usb) {
+		spin_unlock_irqrestore(&port->port_lock_dl, flags);
+		dev_kfree_skb_any(skb);
+		return;
+	}
+
+	if (d->tx_skb_q.qlen > bam_mux_tx_pkt_drop_thld) {
+		d->tohost_drp_cnt++;
+		if (printk_ratelimit())
+			pr_err("%s: tx pkt dropped: tx_drop_cnt:%u\n",
+					__func__, d->tohost_drp_cnt);
+		spin_unlock_irqrestore(&port->port_lock_dl, flags);
+		dev_kfree_skb_any(skb);
+		return;
+	}
+
+	__skb_queue_tail(&d->tx_skb_q, skb);
+	spin_unlock_irqrestore(&port->port_lock_dl, flags);
+
+	gbam_write_data_tohost(port);
+}
+
+void gbam_data_write_done(void *p, struct sk_buff *skb)
+{
+	struct gbam_port	*port = p;
+	struct bam_ch_info	*d = &port->data_ch;
+	unsigned long		flags;
+
+	if (!skb)
+		return;
+
+	dev_kfree_skb_any(skb);
+
+	spin_lock_irqsave(&port->port_lock_ul, flags);
+
+	d->pending_with_bam--;
+
+	pr_debug("%s: port:%p d:%p tom:%lu pbam:%u, pno:%d\n", __func__,
+			port, d, d->to_modem,
+			d->pending_with_bam, port->port_num);
+
+	spin_unlock_irqrestore(&port->port_lock_ul, flags);
+
+	queue_work(gbam_wq, &d->write_tobam_w);
+}
+
+static void gbam_data_write_tobam(struct work_struct *w)
+{
+	struct gbam_port	*port;
+	struct bam_ch_info	*d;
+	struct sk_buff		*skb;
+	unsigned long		flags;
+	int			ret;
+	int			qlen;
+
+	d = container_of(w, struct bam_ch_info, write_tobam_w);
+	port = d->port;
+
+	spin_lock_irqsave(&port->port_lock_ul, flags);
+	if (!port->port_usb) {
+		spin_unlock_irqrestore(&port->port_lock_ul, flags);
+		return;
+	}
+
+	while (d->pending_with_bam < BAM_PENDING_LIMIT) {
+		skb =  __skb_dequeue(&d->rx_skb_q);
+		if (!skb)
+			break;
+
+		d->pending_with_bam++;
+		d->to_modem++;
+
+		pr_debug("%s: port:%p d:%p tom:%lu pbam:%u pno:%d\n", __func__,
+				port, d, d->to_modem, d->pending_with_bam,
+				port->port_num);
+
+		spin_unlock_irqrestore(&port->port_lock_ul, flags);
+		ret = msm_bam_dmux_write(d->id, skb);
+		spin_lock_irqsave(&port->port_lock_ul, flags);
+		if (ret) {
+			pr_debug("%s: write error:%d\n", __func__, ret);
+			d->pending_with_bam--;
+			d->to_modem--;
+			d->tomodem_drp_cnt++;
+			dev_kfree_skb_any(skb);
+			break;
+		}
+	}
+
+	qlen = d->rx_skb_q.qlen;
+
+	spin_unlock_irqrestore(&port->port_lock_ul, flags);
+
+	if (qlen < BAM_MUX_RX_PKT_FCTRL_DIS_TSHOLD)
+		gbam_start_rx(port);
+}
+/*-------------------------------------------------------------*/
+
+static void gbam_epin_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct gbam_port	*port = ep->driver_data;
+	struct bam_ch_info	*d;
+	struct sk_buff		*skb = req->context;
+	int			status = req->status;
+
+	switch (status) {
+	case 0:
+		/* successful completion */
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		break;
+	default:
+		pr_err("%s: data tx ep error %d\n",
+				__func__, status);
+		break;
+	}
+
+	dev_kfree_skb_any(skb);
+
+	if (!port)
+		return;
+
+	spin_lock(&port->port_lock_dl);
+	d = &port->data_ch;
+	list_add_tail(&req->list, &d->tx_idle);
+	spin_unlock(&port->port_lock_dl);
+
+	queue_work(gbam_wq, &d->write_tohost_w);
+}
+
+static void
+gbam_epout_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct gbam_port	*port = ep->driver_data;
+	struct bam_ch_info	*d = &port->data_ch;
+	struct sk_buff		*skb = req->context;
+	int			status = req->status;
+	int			queue = 0;
+
+	switch (status) {
+	case 0:
+		skb_put(skb, req->actual);
+		queue = 1;
+		break;
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* cable disconnection */
+		dev_kfree_skb_any(skb);
+		req->buf = 0;
+		usb_ep_free_request(ep, req);
+		return;
+	default:
+		if (printk_ratelimit())
+			pr_err("%s: %s response error %d, %d/%d\n",
+				__func__, ep->name, status,
+				req->actual, req->length);
+		dev_kfree_skb_any(skb);
+		break;
+	}
+
+	spin_lock(&port->port_lock_ul);
+	if (queue) {
+		__skb_queue_tail(&d->rx_skb_q, skb);
+		queue_work(gbam_wq, &d->write_tobam_w);
+	}
+
+	/* TODO: Handle flow control gracefully by having
+	 * having call back mechanism from bam driver
+	 */
+	if (bam_mux_rx_fctrl_support &&
+		d->rx_skb_q.qlen >= bam_mux_rx_fctrl_en_thld) {
+
+		list_add_tail(&req->list, &d->rx_idle);
+		spin_unlock(&port->port_lock_ul);
+		return;
+	}
+	spin_unlock(&port->port_lock_ul);
+
+	skb = alloc_skb(bam_mux_rx_req_size + BAM_MUX_HDR, GFP_ATOMIC);
+	if (!skb) {
+		spin_lock(&port->port_lock_ul);
+		list_add_tail(&req->list, &d->rx_idle);
+		spin_unlock(&port->port_lock_ul);
+		return;
+	}
+	skb_reserve(skb, BAM_MUX_HDR);
+
+	req->buf = skb->data;
+	req->length = bam_mux_rx_req_size;
+	req->context = skb;
+
+	status = usb_ep_queue(ep, req, GFP_ATOMIC);
+	if (status) {
+		dev_kfree_skb_any(skb);
+
+		if (printk_ratelimit())
+			pr_err("%s: data rx enqueue err %d\n",
+					__func__, status);
+
+		spin_lock(&port->port_lock_ul);
+		list_add_tail(&req->list, &d->rx_idle);
+		spin_unlock(&port->port_lock_ul);
+	}
+}
+
+static void gbam_endless_rx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	int status = req->status;
+
+	pr_debug("%s status: %d\n", __func__, status);
+}
+
+static void gbam_endless_tx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	int status = req->status;
+
+	pr_debug("%s status: %d\n", __func__, status);
+}
+
+static void gbam_start_rx(struct gbam_port *port)
+{
+	struct usb_request		*req;
+	struct bam_ch_info		*d;
+	struct usb_ep			*ep;
+	unsigned long			flags;
+	int				ret;
+	struct sk_buff			*skb;
+
+	spin_lock_irqsave(&port->port_lock_ul, flags);
+	if (!port->port_usb) {
+		spin_unlock_irqrestore(&port->port_lock_ul, flags);
+		return;
+	}
+
+	d = &port->data_ch;
+	ep = port->port_usb->out;
+
+	while (port->port_usb && !list_empty(&d->rx_idle)) {
+
+		if (bam_mux_rx_fctrl_support &&
+			d->rx_skb_q.qlen >= bam_mux_rx_fctrl_en_thld)
+			break;
+
+		req = list_first_entry(&d->rx_idle, struct usb_request, list);
+
+		skb = alloc_skb(bam_mux_rx_req_size + BAM_MUX_HDR, GFP_ATOMIC);
+		if (!skb)
+			break;
+		skb_reserve(skb, BAM_MUX_HDR);
+
+		list_del(&req->list);
+		req->buf = skb->data;
+		req->length = bam_mux_rx_req_size;
+		req->context = skb;
+
+		spin_unlock_irqrestore(&port->port_lock_ul, flags);
+		ret = usb_ep_queue(ep, req, GFP_ATOMIC);
+		spin_lock_irqsave(&port->port_lock_ul, flags);
+		if (ret) {
+			dev_kfree_skb_any(skb);
+
+			if (printk_ratelimit())
+				pr_err("%s: rx queue failed\n", __func__);
+
+			if (port->port_usb)
+				list_add(&req->list, &d->rx_idle);
+			else
+				usb_ep_free_request(ep, req);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&port->port_lock_ul, flags);
+}
+
+static void gbam_start_endless_rx(struct gbam_port *port)
+{
+	struct bam_ch_info *d = &port->data_ch;
+	int status;
+
+	status = usb_ep_queue(port->port_usb->out, d->rx_req, GFP_ATOMIC);
+	if (status)
+		pr_err("%s: error enqueuing transfer, %d\n", __func__, status);
+}
+
+static void gbam_start_endless_tx(struct gbam_port *port)
+{
+	struct bam_ch_info *d = &port->data_ch;
+	int status;
+
+	status = usb_ep_queue(port->port_usb->in, d->tx_req, GFP_ATOMIC);
+	if (status)
+		pr_err("%s: error enqueuing transfer, %d\n", __func__, status);
+}
+
+static void gbam_start_io(struct gbam_port *port)
+{
+	unsigned long		flags;
+	struct usb_ep		*ep;
+	int			ret;
+	struct bam_ch_info	*d;
+
+	pr_debug("%s: port:%p\n", __func__, port);
+
+	spin_lock_irqsave(&port->port_lock_ul, flags);
+	if (!port->port_usb) {
+		spin_unlock_irqrestore(&port->port_lock_ul, flags);
+		return;
+	}
+
+	d = &port->data_ch;
+	ep = port->port_usb->out;
+	ret = gbam_alloc_requests(ep, &d->rx_idle, bam_mux_rx_q_size,
+			gbam_epout_complete, GFP_ATOMIC);
+	if (ret) {
+		pr_err("%s: rx req allocation failed\n", __func__);
+		return;
+	}
+
+	spin_unlock_irqrestore(&port->port_lock_ul, flags);
+	spin_lock_irqsave(&port->port_lock_dl, flags);
+	ep = port->port_usb->in;
+	ret = gbam_alloc_requests(ep, &d->tx_idle, bam_mux_tx_q_size,
+			gbam_epin_complete, GFP_ATOMIC);
+	if (ret) {
+		pr_err("%s: tx req allocation failed\n", __func__);
+		gbam_free_requests(ep, &d->rx_idle);
+		return;
+	}
+
+	spin_unlock_irqrestore(&port->port_lock_dl, flags);
+
+	/* queue out requests */
+	gbam_start_rx(port);
+}
+
+static void gbam_notify(void *p, int event, unsigned long data)
+{
+	switch (event) {
+	case BAM_DMUX_RECEIVE:
+		gbam_data_recv_cb(p, (struct sk_buff *)(data));
+		break;
+	case BAM_DMUX_WRITE_DONE:
+		gbam_data_write_done(p, (struct sk_buff *)(data));
+		break;
+	}
+}
+
+static void gbam_free_buffers(struct gbam_port *port)
+{
+	struct sk_buff		*skb;
+	unsigned long		flags;
+	struct bam_ch_info	*d;
+
+	spin_lock_irqsave(&port->port_lock_ul, flags);
+	spin_lock(&port->port_lock_dl);
+
+	if (!port || !port->port_usb)
+		goto free_buf_out;
+
+	d = &port->data_ch;
+
+	gbam_free_requests(port->port_usb->in, &d->tx_idle);
+	gbam_free_requests(port->port_usb->out, &d->rx_idle);
+
+	while ((skb = __skb_dequeue(&d->tx_skb_q)))
+		dev_kfree_skb_any(skb);
+
+	while ((skb = __skb_dequeue(&d->rx_skb_q)))
+		dev_kfree_skb_any(skb);
+
+free_buf_out:
+	spin_unlock(&port->port_lock_dl);
+	spin_unlock_irqrestore(&port->port_lock_ul, flags);
+}
+
+static void gbam_disconnect_work(struct work_struct *w)
+{
+	struct gbam_port *port =
+			container_of(w, struct gbam_port, disconnect_w);
+	struct bam_ch_info *d = &port->data_ch;
+
+	if (!test_bit(BAM_CH_OPENED, &d->flags))
+		return;
+
+	msm_bam_dmux_close(d->id);
+	clear_bit(BAM_CH_OPENED, &d->flags);
+}
+
+static void gbam2bam_disconnect_work(struct work_struct *w)
+{
+	struct gbam_port *port =
+			container_of(w, struct gbam_port, disconnect_w);
+	unsigned long		flags;
+
+	spin_lock_irqsave(&port->port_lock_ul, flags);
+	spin_lock(&port->port_lock_dl);
+	port->port_usb = 0;
+	spin_unlock(&port->port_lock_dl);
+	spin_unlock_irqrestore(&port->port_lock_ul, flags);
+
+	/* disable endpoints */
+	usb_ep_disable(port->gr->out);
+	usb_ep_disable(port->gr->in);
+
+	port->gr->in->driver_data = NULL;
+	port->gr->out->driver_data = NULL;
+}
+
+static void gbam_connect_work(struct work_struct *w)
+{
+	struct gbam_port *port = container_of(w, struct gbam_port, connect_w);
+	struct bam_ch_info *d = &port->data_ch;
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->port_lock_ul, flags);
+	spin_lock(&port->port_lock_dl);
+	if (!port->port_usb) {
+		spin_unlock(&port->port_lock_dl);
+		spin_unlock_irqrestore(&port->port_lock_ul, flags);
+		return;
+	}
+	spin_unlock(&port->port_lock_dl);
+	spin_unlock_irqrestore(&port->port_lock_ul, flags);
+
+	if (!test_bit(BAM_CH_READY, &d->flags))
+		return;
+
+	ret = msm_bam_dmux_open(d->id, port, gbam_notify);
+	if (ret) {
+		pr_err("%s: unable open bam ch:%d err:%d\n",
+				__func__, d->id, ret);
+		return;
+	}
+	set_bit(BAM_CH_OPENED, &d->flags);
+
+	gbam_start_io(port);
+
+	pr_debug("%s: done\n", __func__);
+}
+
+static void gbam2bam_connect_work(struct work_struct *w)
+{
+	struct gbam_port *port = container_of(w, struct gbam_port, connect_w);
+	struct bam_ch_info *d = &port->data_ch;
+	u32 sps_params;
+	int ret;
+	unsigned long flags;
+
+	ret = usb_ep_enable(port->gr->in);
+	if (ret) {
+		pr_err("%s: usb_ep_enable failed eptype:IN ep:%p",
+				__func__, port->gr->in);
+		return;
+	}
+	port->gr->in->driver_data = port;
+
+	ret = usb_ep_enable(port->gr->out);
+	if (ret) {
+		pr_err("%s: usb_ep_enable failed eptype:OUT ep:%p",
+				__func__, port->gr->out);
+		port->gr->in->driver_data = 0;
+		return;
+	}
+	port->gr->out->driver_data = port;
+	spin_lock_irqsave(&port->port_lock_ul, flags);
+	spin_lock(&port->port_lock_dl);
+	port->port_usb = port->gr;
+	spin_unlock(&port->port_lock_dl);
+	spin_unlock_irqrestore(&port->port_lock_ul, flags);
+
+	ret = usb_bam_connect(d->connection_idx, &d->src_pipe_idx,
+						  &d->dst_pipe_idx);
+	if (ret) {
+		pr_err("%s: usb_bam_connect failed: err:%d\n",
+			__func__, ret);
+		return;
+	}
+
+	d->rx_req = usb_ep_alloc_request(port->port_usb->out, GFP_KERNEL);
+	if (!d->rx_req)
+		return;
+
+	d->rx_req->context = port;
+	d->rx_req->complete = gbam_endless_rx_complete;
+	d->rx_req->length = 0;
+	sps_params = (MSM_SPS_MODE | d->src_pipe_idx |
+				 MSM_VENDOR_ID) & ~MSM_IS_FINITE_TRANSFER;
+	d->rx_req->udc_priv = sps_params;
+	d->tx_req = usb_ep_alloc_request(port->port_usb->in, GFP_KERNEL);
+	if (!d->tx_req)
+		return;
+
+	d->tx_req->context = port;
+	d->tx_req->complete = gbam_endless_tx_complete;
+	d->tx_req->length = 0;
+	sps_params = (MSM_SPS_MODE | d->dst_pipe_idx |
+				 MSM_VENDOR_ID) & ~MSM_IS_FINITE_TRANSFER;
+	d->tx_req->udc_priv = sps_params;
+
+	/* queue in & out requests */
+	gbam_start_endless_rx(port);
+	gbam_start_endless_tx(port);
+
+	pr_debug("%s: done\n", __func__);
+}
+
+/* BAM data channel ready, allow attempt to open */
+static int gbam_data_ch_probe(struct platform_device *pdev)
+{
+	struct gbam_port	*port;
+	struct bam_ch_info	*d;
+	int			i;
+	unsigned long		flags;
+
+	pr_debug("%s: name:%s\n", __func__, pdev->name);
+
+	for (i = 0; i < n_bam_ports; i++) {
+		port = bam_ports[i].port;
+		d = &port->data_ch;
+
+		if (!strncmp(bam_ch_names[i], pdev->name,
+					BAM_DMUX_CH_NAME_MAX_LEN)) {
+			set_bit(BAM_CH_READY, &d->flags);
+
+			/* if usb is online, try opening bam_ch */
+			spin_lock_irqsave(&port->port_lock_ul, flags);
+			spin_lock(&port->port_lock_dl);
+			if (port->port_usb)
+				queue_work(gbam_wq, &port->connect_w);
+			spin_unlock(&port->port_lock_dl);
+			spin_unlock_irqrestore(&port->port_lock_ul, flags);
+
+			break;
+		}
+	}
+
+	return 0;
+}
+
+/* BAM data channel went inactive, so close it */
+static int gbam_data_ch_remove(struct platform_device *pdev)
+{
+	struct gbam_port	*port;
+	struct bam_ch_info	*d;
+	struct usb_ep		*ep_in = NULL;
+	struct usb_ep		*ep_out = NULL;
+	unsigned long		flags;
+	int			i;
+
+	pr_debug("%s: name:%s\n", __func__, pdev->name);
+
+	for (i = 0; i < n_bam_ports; i++) {
+		if (!strncmp(bam_ch_names[i], pdev->name,
+					BAM_DMUX_CH_NAME_MAX_LEN)) {
+			port = bam_ports[i].port;
+			d = &port->data_ch;
+
+			spin_lock_irqsave(&port->port_lock_ul, flags);
+			spin_lock(&port->port_lock_dl);
+			if (port->port_usb) {
+				ep_in = port->port_usb->in;
+				ep_out = port->port_usb->out;
+			}
+			spin_unlock(&port->port_lock_dl);
+			spin_unlock_irqrestore(&port->port_lock_ul, flags);
+
+			if (ep_in)
+				usb_ep_fifo_flush(ep_in);
+			if (ep_out)
+				usb_ep_fifo_flush(ep_out);
+
+			gbam_free_buffers(port);
+
+			msm_bam_dmux_close(d->id);
+
+			/* bam dmux will free all pending skbs */
+			d->pending_with_bam = 0;
+
+			clear_bit(BAM_CH_READY, &d->flags);
+			clear_bit(BAM_CH_OPENED, &d->flags);
+		}
+	}
+
+	return 0;
+}
+
+static void gbam_port_free(int portno)
+{
+	struct gbam_port *port = bam_ports[portno].port;
+	struct platform_driver *pdrv = &bam_ports[portno].pdrv;
+
+	if (port) {
+		kfree(port);
+		platform_driver_unregister(pdrv);
+	}
+}
+
+static void gbam2bam_port_free(int portno)
+{
+	struct gbam_port *port = bam2bam_ports[portno];
+
+	kfree(port);
+}
+
+static int gbam_port_alloc(int portno)
+{
+	struct gbam_port	*port;
+	struct bam_ch_info	*d;
+	struct platform_driver	*pdrv;
+
+	port = kzalloc(sizeof(struct gbam_port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	port->port_num = portno;
+
+	/* port initialization */
+	spin_lock_init(&port->port_lock_ul);
+	spin_lock_init(&port->port_lock_dl);
+	INIT_WORK(&port->connect_w, gbam_connect_work);
+	INIT_WORK(&port->disconnect_w, gbam_disconnect_work);
+
+	/* data ch */
+	d = &port->data_ch;
+	d->port = port;
+	INIT_LIST_HEAD(&d->tx_idle);
+	INIT_LIST_HEAD(&d->rx_idle);
+	INIT_WORK(&d->write_tobam_w, gbam_data_write_tobam);
+	INIT_WORK(&d->write_tohost_w, gbam_write_data_tohost_w);
+	skb_queue_head_init(&d->tx_skb_q);
+	skb_queue_head_init(&d->rx_skb_q);
+	d->id = bam_ch_ids[portno];
+
+	bam_ports[portno].port = port;
+
+	pdrv = &bam_ports[portno].pdrv;
+	pdrv->probe = gbam_data_ch_probe;
+	pdrv->remove = gbam_data_ch_remove;
+	pdrv->driver.name = bam_ch_names[portno];
+	pdrv->driver.owner = THIS_MODULE;
+
+	platform_driver_register(pdrv);
+	pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
+
+	return 0;
+}
+
+static int gbam2bam_port_alloc(int portno)
+{
+	struct gbam_port	*port;
+	struct bam_ch_info	*d;
+
+	port = kzalloc(sizeof(struct gbam_port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	port->port_num = portno;
+
+	/* port initialization */
+	spin_lock_init(&port->port_lock_ul);
+	spin_lock_init(&port->port_lock_dl);
+
+	INIT_WORK(&port->connect_w, gbam2bam_connect_work);
+	INIT_WORK(&port->disconnect_w, gbam2bam_disconnect_work);
+
+	/* data ch */
+	d = &port->data_ch;
+	d->port = port;
+	bam2bam_ports[portno] = port;
+
+	pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
+
+	return 0;
+}
+
+#if defined(CONFIG_DEBUG_FS)
+#define DEBUG_BUF_SIZE	1024
+static ssize_t gbam_read_stats(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	struct gbam_port	*port;
+	struct bam_ch_info	*d;
+	char			*buf;
+	unsigned long		flags;
+	int			ret;
+	int			i;
+	int			temp = 0;
+
+	buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	for (i = 0; i < n_bam_ports; i++) {
+		port = bam_ports[i].port;
+		if (!port)
+			continue;
+		spin_lock_irqsave(&port->port_lock_ul, flags);
+		spin_lock(&port->port_lock_dl);
+
+		d = &port->data_ch;
+
+		temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+				"#PORT:%d port:%p data_ch:%p#\n"
+				"dpkts_to_usbhost: %lu\n"
+				"dpkts_to_modem:  %lu\n"
+				"dpkts_pwith_bam: %u\n"
+				"to_usbhost_dcnt:  %u\n"
+				"tomodem__dcnt:  %u\n"
+				"tx_buf_len:	 %u\n"
+				"rx_buf_len:	 %u\n"
+				"data_ch_open:   %d\n"
+				"data_ch_ready:  %d\n",
+				i, port, &port->data_ch,
+				d->to_host, d->to_modem,
+				d->pending_with_bam,
+				d->tohost_drp_cnt, d->tomodem_drp_cnt,
+				d->tx_skb_q.qlen, d->rx_skb_q.qlen,
+				test_bit(BAM_CH_OPENED, &d->flags),
+				test_bit(BAM_CH_READY, &d->flags));
+
+		spin_unlock(&port->port_lock_dl);
+		spin_unlock_irqrestore(&port->port_lock_ul, flags);
+	}
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+	kfree(buf);
+
+	return ret;
+}
+
+static ssize_t gbam_reset_stats(struct file *file, const char __user *buf,
+				 size_t count, loff_t *ppos)
+{
+	struct gbam_port	*port;
+	struct bam_ch_info	*d;
+	int			i;
+	unsigned long		flags;
+
+	for (i = 0; i < n_bam_ports; i++) {
+		port = bam_ports[i].port;
+		if (!port)
+			continue;
+
+		spin_lock_irqsave(&port->port_lock_ul, flags);
+		spin_lock(&port->port_lock_dl);
+
+		d = &port->data_ch;
+
+		d->to_host = 0;
+		d->to_modem = 0;
+		d->pending_with_bam = 0;
+		d->tohost_drp_cnt = 0;
+		d->tomodem_drp_cnt = 0;
+
+		spin_unlock(&port->port_lock_dl);
+		spin_unlock_irqrestore(&port->port_lock_ul, flags);
+	}
+	return count;
+}
+
+const struct file_operations gbam_stats_ops = {
+	.read = gbam_read_stats,
+	.write = gbam_reset_stats,
+};
+
+static void gbam_debugfs_init(void)
+{
+	struct dentry *dent;
+	struct dentry *dfile;
+
+	dent = debugfs_create_dir("usb_rmnet", 0);
+	if (IS_ERR(dent))
+		return;
+
+	/* TODO: Implement cleanup function to remove created file */
+	dfile = debugfs_create_file("status", 0444, dent, 0, &gbam_stats_ops);
+	if (!dfile || IS_ERR(dfile))
+		debugfs_remove(dent);
+}
+#else
+static void gam_debugfs_init(void) { }
+#endif
+
+void gbam_disconnect(struct grmnet *gr, u8 port_num, enum transport_type trans)
+{
+	struct gbam_port	*port;
+	unsigned long		flags;
+	struct bam_ch_info	*d;
+
+	pr_debug("%s: grmnet:%p port#%d\n", __func__, gr, port_num);
+
+	if (trans == USB_GADGET_XPORT_BAM &&
+		port_num >= n_bam_ports) {
+		pr_err("%s: invalid bam portno#%d\n",
+			   __func__, port_num);
+		return;
+	}
+
+	if (trans == USB_GADGET_XPORT_BAM2BAM &&
+		port_num >= n_bam2bam_ports) {
+		pr_err("%s: invalid bam2bam portno#%d\n",
+			   __func__, port_num);
+		return;
+	}
+
+	if (!gr) {
+		pr_err("%s: grmnet port is null\n", __func__);
+		return;
+	}
+	if (trans == USB_GADGET_XPORT_BAM)
+		port = bam_ports[port_num].port;
+	else
+		port = bam2bam_ports[port_num];
+
+	d = &port->data_ch;
+	port->gr = gr;
+
+	if (trans == USB_GADGET_XPORT_BAM) {
+		gbam_free_buffers(port);
+
+	spin_lock_irqsave(&port->port_lock_ul, flags);
+	spin_lock(&port->port_lock_dl);
+	port->port_usb = 0;
+	n_tx_req_queued = 0;
+	spin_unlock(&port->port_lock_dl);
+	spin_unlock_irqrestore(&port->port_lock_ul, flags);
+
+		/* disable endpoints */
+		usb_ep_disable(gr->out);
+		usb_ep_disable(gr->in);
+	}
+
+	queue_work(gbam_wq, &port->disconnect_w);
+}
+
+int gbam_connect(struct grmnet *gr, u8 port_num,
+				 enum transport_type trans, u8 connection_idx)
+{
+	struct gbam_port	*port;
+	struct bam_ch_info	*d;
+	int			ret;
+	unsigned long		flags;
+
+	pr_debug("%s: grmnet:%p port#%d\n", __func__, gr, port_num);
+
+	if (trans == USB_GADGET_XPORT_BAM && port_num >= n_bam_ports) {
+		pr_err("%s: invalid portno#%d\n", __func__, port_num);
+		return -ENODEV;
+	}
+
+	if (trans == USB_GADGET_XPORT_BAM2BAM && port_num >= n_bam2bam_ports) {
+		pr_err("%s: invalid portno#%d\n", __func__, port_num);
+		return -ENODEV;
+	}
+
+	if (!gr) {
+		pr_err("%s: grmnet port is null\n", __func__);
+		return -ENODEV;
+	}
+
+	if (trans == USB_GADGET_XPORT_BAM)
+		port = bam_ports[port_num].port;
+	else
+		port = bam2bam_ports[port_num];
+
+	d = &port->data_ch;
+
+	if (trans == USB_GADGET_XPORT_BAM) {
+		ret = usb_ep_enable(gr->in);
+		if (ret) {
+			pr_err("%s: usb_ep_enable failed eptype:IN ep:%p",
+					__func__, gr->in);
+			return ret;
+		}
+		gr->in->driver_data = port;
+
+		ret = usb_ep_enable(gr->out);
+		if (ret) {
+			pr_err("%s: usb_ep_enable failed eptype:OUT ep:%p",
+					__func__, gr->out);
+			gr->in->driver_data = 0;
+			return ret;
+		}
+		gr->out->driver_data = port;
+
+	spin_lock_irqsave(&port->port_lock_ul, flags);
+	spin_lock(&port->port_lock_dl);
+	port->port_usb = gr;
+
+		d->to_host = 0;
+		d->to_modem = 0;
+		d->pending_with_bam = 0;
+		d->tohost_drp_cnt = 0;
+		d->tomodem_drp_cnt = 0;
+	spin_unlock(&port->port_lock_dl);
+	spin_unlock_irqrestore(&port->port_lock_ul, flags);
+	}
+
+	if (trans == USB_GADGET_XPORT_BAM2BAM) {
+		port->gr = gr;
+		d->connection_idx = connection_idx;
+	}
+
+	queue_work(gbam_wq, &port->connect_w);
+
+	return 0;
+}
+
+int gbam_setup(unsigned int no_bam_port, unsigned int no_bam2bam_port)
+{
+	int	i;
+	int	ret;
+
+	pr_debug("%s: requested BAM ports:%d and BAM2BAM ports:%d\n",
+			  __func__, no_bam_port, no_bam2bam_port);
+
+	if ((!no_bam_port && !no_bam2bam_port) || no_bam_port > BAM_N_PORTS
+		|| no_bam2bam_port > BAM2BAM_N_PORTS) {
+		pr_err("%s: Invalid num of ports count:%d,%d\n",
+				__func__, no_bam_port, no_bam2bam_port);
+		return -EINVAL;
+	}
+
+	gbam_wq = alloc_workqueue("k_gbam", WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
+	if (!gbam_wq) {
+		pr_err("%s: Unable to create workqueue gbam_wq\n",
+				__func__);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < no_bam_port; i++) {
+		n_bam_ports++;
+		ret = gbam_port_alloc(i);
+		if (ret) {
+			n_bam_ports--;
+			pr_err("%s: Unable to alloc port:%d\n", __func__, i);
+			goto free_bam_ports;
+		}
+	}
+
+	for (i = 0; i < no_bam2bam_port; i++) {
+		n_bam2bam_ports++;
+		ret = gbam2bam_port_alloc(i);
+		if (ret) {
+			n_bam2bam_ports--;
+			pr_err("%s: Unable to alloc port:%d\n", __func__, i);
+			goto free_bam_ports;
+		}
+	}
+	gbam_debugfs_init();
+	return 0;
+
+free_bam_ports:
+	for (i = 0; i < n_bam_ports; i++)
+		gbam_port_free(i);
+	for (i = 0; i < n_bam2bam_ports; i++)
+		gbam2bam_port_free(i);
+	destroy_workqueue(gbam_wq);
+
+	return ret;
+}
+
+static int gbam_wake_cb(void *param)
+{
+	struct gbam_port	*port = (struct gbam_port *)param;
+	struct bam_ch_info *d;
+	struct f_rmnet		*dev;
+
+	dev = port_to_rmnet(port->gr);
+	d = &port->data_ch;
+
+	pr_debug("%s: woken up by peer\n", __func__);
+
+	return usb_gadget_wakeup(dev->cdev->gadget);
+}
+
+void gbam_suspend(struct grmnet *gr, u8 port_num, enum transport_type trans)
+{
+	struct gbam_port	*port;
+	struct bam_ch_info *d;
+
+	if (trans != USB_GADGET_XPORT_BAM2BAM)
+		return;
+
+	port = bam2bam_ports[port_num];
+	d = &port->data_ch;
+
+	pr_debug("%s: suspended port %d\n", __func__, port_num);
+
+	usb_bam_register_wake_cb(d->connection_idx, gbam_wake_cb, port);
+}
+
+void gbam_resume(struct grmnet *gr, u8 port_num, enum transport_type trans)
+{
+	struct gbam_port	*port;
+	struct bam_ch_info *d;
+
+	if (trans != USB_GADGET_XPORT_BAM2BAM)
+		return;
+
+	port = bam2bam_ports[port_num];
+	d = &port->data_ch;
+
+	pr_debug("%s: resumed port %d\n", __func__, port_num);
+
+	usb_bam_register_wake_cb(d->connection_idx, NULL, NULL);
+}
diff --git a/drivers/usb/gadget/u_bam_data.c b/drivers/usb/gadget/u_bam_data.c
new file mode 100644
index 0000000..73b4e75
--- /dev/null
+++ b/drivers/usb/gadget/u_bam_data.c
@@ -0,0 +1,328 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifdef pr_fmt
+#undef pr_fmt
+#endif
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/bitops.h>
+#include <linux/usb/gadget.h>
+
+#include <mach/bam_dmux.h>
+#include <mach/usb_gadget_xport.h>
+#include <mach/usb_bam.h>
+
+#define BAM2BAM_DATA_N_PORTS	1
+
+static struct workqueue_struct *bam_data_wq;
+static int n_bam2bam_data_ports;
+
+#define SPS_PARAMS_SPS_MODE		BIT(5)
+#define SPS_PARAMS_TBE		        BIT(6)
+#define MSM_VENDOR_ID			BIT(16)
+
+struct data_port {
+	struct usb_function		func;
+	struct usb_ep			*in;
+	struct usb_ep			*out;
+};
+
+struct bam_data_ch_info {
+	unsigned long		flags;
+	unsigned		id;
+
+	struct bam_data_port	*port;
+	struct work_struct	write_tobam_w;
+
+	struct usb_request	*rx_req;
+	struct usb_request	*tx_req;
+
+	u8			src_pipe_idx;
+	u8			dst_pipe_idx;
+	u8			connection_idx;
+};
+
+struct bam_data_port {
+	unsigned			port_num;
+	struct data_port		*port_usb;
+	struct bam_data_ch_info		data_ch;
+
+	struct work_struct		connect_w;
+	struct work_struct		disconnect_w;
+};
+
+struct bam_data_port *bam2bam_data_ports[BAM2BAM_DATA_N_PORTS];
+
+/*------------data_path----------------------------*/
+
+static void bam_data_endless_rx_complete(struct usb_ep *ep,
+					 struct usb_request *req)
+{
+	int status = req->status;
+
+	pr_info("status: %d\n", status);
+}
+
+static void bam_data_endless_tx_complete(struct usb_ep *ep,
+					 struct usb_request *req)
+{
+	int status = req->status;
+
+	pr_info("status: %d\n", status);
+}
+
+static void bam_data_start_endless_rx(struct bam_data_port *port)
+{
+	struct bam_data_ch_info *d = &port->data_ch;
+	int status;
+
+	status = usb_ep_queue(port->port_usb->out, d->rx_req, GFP_ATOMIC);
+	if (status)
+		pr_err("error enqueuing transfer, %d\n", status);
+}
+
+static void bam_data_start_endless_tx(struct bam_data_port *port)
+{
+	struct bam_data_ch_info *d = &port->data_ch;
+	int status;
+
+	status = usb_ep_queue(port->port_usb->in, d->tx_req, GFP_ATOMIC);
+	if (status)
+		pr_err("error enqueuing transfer, %d\n", status);
+}
+
+static void bam2bam_data_disconnect_work(struct work_struct *w)
+{
+	struct bam_data_port *port =
+			container_of(w, struct bam_data_port, disconnect_w);
+
+	pr_info("Enter");
+
+	/* disable endpoints */
+	if (!port->port_usb || !port->port_usb->out || !port->port_usb->in) {
+		pr_err("port_usb->out/in == NULL. Exit");
+		return;
+	}
+	usb_ep_disable(port->port_usb->out);
+	usb_ep_disable(port->port_usb->in);
+
+	port->port_usb->in->driver_data = NULL;
+	port->port_usb->out->driver_data = NULL;
+
+	port->port_usb = 0;
+
+	pr_info("Exit");
+}
+
+static void bam2bam_data_connect_work(struct work_struct *w)
+{
+	struct bam_data_port *port = container_of(w, struct bam_data_port,
+						  connect_w);
+	struct bam_data_ch_info *d = &port->data_ch;
+	u32 sps_params;
+	int ret;
+
+	pr_info("Enter");
+
+	ret = usb_bam_connect(d->connection_idx, &d->src_pipe_idx,
+						  &d->dst_pipe_idx);
+	d->src_pipe_idx = 11;
+	d->dst_pipe_idx = 10;
+
+	if (ret) {
+		pr_err("usb_bam_connect failed: err:%d\n", ret);
+		return;
+	}
+
+	if (!port->port_usb) {
+		pr_err("port_usb is NULL");
+		return;
+	}
+
+	if (!port->port_usb->out) {
+		pr_err("port_usb->out (bulk out ep) is NULL");
+		return;
+	}
+
+	d->rx_req = usb_ep_alloc_request(port->port_usb->out, GFP_KERNEL);
+	if (!d->rx_req)
+		return;
+
+	d->rx_req->context = port;
+	d->rx_req->complete = bam_data_endless_rx_complete;
+	d->rx_req->length = 0;
+	sps_params = (SPS_PARAMS_SPS_MODE | d->src_pipe_idx |
+				 MSM_VENDOR_ID) & ~SPS_PARAMS_TBE;
+	d->rx_req->udc_priv = sps_params;
+	d->tx_req = usb_ep_alloc_request(port->port_usb->in, GFP_KERNEL);
+	if (!d->tx_req)
+		return;
+
+	d->tx_req->context = port;
+	d->tx_req->complete = bam_data_endless_tx_complete;
+	d->tx_req->length = 0;
+	sps_params = (SPS_PARAMS_SPS_MODE | d->dst_pipe_idx |
+				 MSM_VENDOR_ID) & ~SPS_PARAMS_TBE;
+	d->tx_req->udc_priv = sps_params;
+
+	/* queue in & out requests */
+	bam_data_start_endless_rx(port);
+	bam_data_start_endless_tx(port);
+
+	pr_info("Done\n");
+}
+
+static void bam2bam_data_port_free(int portno)
+{
+	kfree(bam2bam_data_ports[portno]);
+	bam2bam_data_ports[portno] = NULL;
+}
+
+static int bam2bam_data_port_alloc(int portno)
+{
+	struct bam_data_port	*port = NULL;
+	struct bam_data_ch_info	*d = NULL;
+
+	port = kzalloc(sizeof(struct bam_data_port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	port->port_num = portno;
+
+	INIT_WORK(&port->connect_w, bam2bam_data_connect_work);
+	INIT_WORK(&port->disconnect_w, bam2bam_data_disconnect_work);
+
+	/* data ch */
+	d = &port->data_ch;
+	d->port = port;
+	bam2bam_data_ports[portno] = port;
+
+	pr_info("port:%p portno:%d\n", port, portno);
+
+	return 0;
+}
+
+void bam_data_disconnect(struct data_port *gr, u8 port_num)
+{
+	struct bam_data_port	*port;
+	struct bam_data_ch_info	*d;
+
+	pr_info("dev:%p port#%d\n", gr, port_num);
+
+	if (port_num >= n_bam2bam_data_ports) {
+		pr_err("invalid bam2bam portno#%d\n", port_num);
+		return;
+	}
+
+	if (!gr) {
+		pr_err("mbim data port is null\n");
+		return;
+	}
+
+	port = bam2bam_data_ports[port_num];
+
+	d = &port->data_ch;
+	port->port_usb = gr;
+
+	queue_work(bam_data_wq, &port->disconnect_w);
+}
+
+int bam_data_connect(struct data_port *gr, u8 port_num,
+				 u8 connection_idx)
+{
+	struct bam_data_port	*port;
+	struct bam_data_ch_info	*d;
+	int			ret;
+
+	pr_info("dev:%p port#%d\n", gr, port_num);
+
+	if (port_num >= n_bam2bam_data_ports) {
+		pr_err("invalid portno#%d\n", port_num);
+		return -ENODEV;
+	}
+
+	if (!gr) {
+		pr_err("mbim data port is null\n");
+		return -ENODEV;
+	}
+
+	port = bam2bam_data_ports[port_num];
+
+	d = &port->data_ch;
+
+	ret = usb_ep_enable(gr->in);
+	if (ret) {
+		pr_err("usb_ep_enable failed eptype:IN ep:%p", gr->in);
+		return ret;
+	}
+	gr->in->driver_data = port;
+
+	ret = usb_ep_enable(gr->out);
+	if (ret) {
+		pr_err("usb_ep_enable failed eptype:OUT ep:%p", gr->out);
+		gr->in->driver_data = 0;
+		return ret;
+	}
+	gr->out->driver_data = port;
+
+	port->port_usb = gr;
+
+	d->connection_idx = connection_idx;
+
+	queue_work(bam_data_wq, &port->connect_w);
+
+	return 0;
+}
+
+int bam_data_setup(unsigned int no_bam2bam_port)
+{
+	int	i;
+	int	ret;
+
+	pr_info("requested %d BAM2BAM ports", no_bam2bam_port);
+
+	if (!no_bam2bam_port || no_bam2bam_port > BAM2BAM_DATA_N_PORTS) {
+		pr_err("Invalid num of ports count:%d\n", no_bam2bam_port);
+		return -EINVAL;
+	}
+
+	bam_data_wq = alloc_workqueue("k_bam_data",
+				      WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
+	if (!bam_data_wq) {
+		pr_err("Failed to create workqueue\n");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < no_bam2bam_port; i++) {
+		n_bam2bam_data_ports++;
+		ret = bam2bam_data_port_alloc(i);
+		if (ret) {
+			n_bam2bam_data_ports--;
+			pr_err("Failed to alloc port:%d\n", i);
+			goto free_bam_ports;
+		}
+	}
+
+	return 0;
+
+free_bam_ports:
+	for (i = 0; i < n_bam2bam_data_ports; i++)
+		bam2bam_data_port_free(i);
+	destroy_workqueue(bam_data_wq);
+
+	return ret;
+}
+
diff --git a/drivers/usb/gadget/u_ctrl_hsic.c b/drivers/usb/gadget/u_ctrl_hsic.c
new file mode 100644
index 0000000..fdfab96
--- /dev/null
+++ b/drivers/usb/gadget/u_ctrl_hsic.c
@@ -0,0 +1,617 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/termios.h>
+#include <linux/debugfs.h>
+#include <linux/bitops.h>
+#include <linux/termios.h>
+#include <mach/usb_bridge.h>
+#include <mach/usb_gadget_xport.h>
+
+/* from cdc-acm.h */
+#define ACM_CTRL_RTS		(1 << 1)	/* unused with full duplex */
+#define ACM_CTRL_DTR		(1 << 0)	/* host is ready for data r/w */
+#define ACM_CTRL_OVERRUN	(1 << 6)
+#define ACM_CTRL_PARITY		(1 << 5)
+#define ACM_CTRL_FRAMING	(1 << 4)
+#define ACM_CTRL_RI		(1 << 3)
+#define ACM_CTRL_BRK		(1 << 2)
+#define ACM_CTRL_DSR		(1 << 1)
+#define ACM_CTRL_DCD		(1 << 0)
+
+
+static unsigned int	no_ctrl_ports;
+
+static const char	*ctrl_bridge_names[] = {
+	"dun_ctrl_hsic0",
+	"rmnet_ctrl_hsic0"
+};
+
+#define CTRL_BRIDGE_NAME_MAX_LEN	20
+#define READ_BUF_LEN			1024
+
+#define CH_OPENED 0
+#define CH_READY 1
+
+struct gctrl_port {
+	/* port */
+	unsigned		port_num;
+
+	/* gadget */
+	spinlock_t		port_lock;
+	void			*port_usb;
+
+	/* work queue*/
+	struct workqueue_struct	*wq;
+	struct work_struct	connect_w;
+	struct work_struct	disconnect_w;
+
+	enum gadget_type	gtype;
+
+	/*ctrl pkt response cb*/
+	int (*send_cpkt_response)(void *g, void *buf, size_t len);
+
+	struct bridge		brdg;
+
+	/* bridge status */
+	unsigned long		bridge_sts;
+
+	/* control bits */
+	unsigned		cbits_tomodem;
+	unsigned		cbits_tohost;
+
+	/* counters */
+	unsigned long		to_modem;
+	unsigned long		to_host;
+	unsigned long		drp_cpkt_cnt;
+};
+
+static struct {
+	struct gctrl_port	*port;
+	struct platform_driver	pdrv;
+} gctrl_ports[NUM_PORTS];
+
+static int ghsic_ctrl_receive(void *dev, void *buf, size_t actual)
+{
+	struct gctrl_port	*port = dev;
+	int retval = 0;
+
+	pr_debug_ratelimited("%s: read complete bytes read: %d\n",
+			__func__, actual);
+
+	/* send it to USB here */
+	if (port && port->send_cpkt_response) {
+		retval = port->send_cpkt_response(port->port_usb, buf, actual);
+		port->to_host++;
+	}
+
+	return retval;
+}
+
+static int
+ghsic_send_cpkt_tomodem(u8 portno, void *buf, size_t len)
+{
+	void			*cbuf;
+	struct gctrl_port	*port;
+
+	if (portno >= no_ctrl_ports) {
+		pr_err("%s: Invalid portno#%d\n", __func__, portno);
+		return -ENODEV;
+	}
+
+	port = gctrl_ports[portno].port;
+	if (!port) {
+		pr_err("%s: port is null\n", __func__);
+		return -ENODEV;
+	}
+
+	cbuf = kmalloc(len, GFP_ATOMIC);
+	if (!cbuf)
+		return -ENOMEM;
+
+	memcpy(cbuf, buf, len);
+
+	/* drop cpkt if ch is not open */
+	if (!test_bit(CH_OPENED, &port->bridge_sts)) {
+		port->drp_cpkt_cnt++;
+		kfree(cbuf);
+		return 0;
+	}
+
+	pr_debug("%s: ctrl_pkt:%d bytes\n", __func__, len);
+
+	ctrl_bridge_write(port->brdg.ch_id, cbuf, len);
+
+	port->to_modem++;
+
+	return 0;
+}
+
+static void
+ghsic_send_cbits_tomodem(void *gptr, u8 portno, int cbits)
+{
+	struct gctrl_port	*port;
+
+	if (portno >= no_ctrl_ports || !gptr) {
+		pr_err("%s: Invalid portno#%d\n", __func__, portno);
+		return;
+	}
+
+	port = gctrl_ports[portno].port;
+	if (!port) {
+		pr_err("%s: port is null\n", __func__);
+		return;
+	}
+
+	if (cbits == port->cbits_tomodem)
+		return;
+
+	port->cbits_tomodem = cbits;
+
+	if (!test_bit(CH_OPENED, &port->bridge_sts))
+		return;
+
+	pr_debug("%s: ctrl_tomodem:%d\n", __func__, cbits);
+
+	ctrl_bridge_set_cbits(port->brdg.ch_id, cbits);
+}
+
+static void ghsic_ctrl_connect_w(struct work_struct *w)
+{
+	struct gserial		*gser = NULL;
+	struct grmnet		*gr = NULL;
+	struct gctrl_port	*port =
+			container_of(w, struct gctrl_port, connect_w);
+	unsigned long		flags;
+	int			retval;
+	unsigned		cbits;
+
+	if (!port || !test_bit(CH_READY, &port->bridge_sts))
+		return;
+
+	pr_debug("%s: port:%p\n", __func__, port);
+
+	retval = ctrl_bridge_open(&port->brdg);
+	if (retval) {
+		pr_err("%s: ctrl bridge open failed :%d\n", __func__, retval);
+		return;
+	}
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	if (!port->port_usb) {
+		ctrl_bridge_close(port->brdg.ch_id);
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		return;
+	}
+	set_bit(CH_OPENED, &port->bridge_sts);
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	cbits = ctrl_bridge_get_cbits_tohost(port->brdg.ch_id);
+
+	if (port->gtype == USB_GADGET_SERIAL && (cbits & ACM_CTRL_DCD)) {
+		gser = port->port_usb;
+		if (gser && gser->connect)
+			gser->connect(gser);
+		return;
+	}
+
+	if (port->gtype == USB_GADGET_RMNET) {
+		gr = port->port_usb;
+		if (gr && gr->connect)
+			gr->connect(gr);
+	}
+}
+
+int ghsic_ctrl_connect(void *gptr, int port_num)
+{
+	struct gctrl_port	*port;
+	struct gserial		*gser;
+	struct grmnet		*gr;
+	unsigned long		flags;
+
+	pr_debug("%s: port#%d\n", __func__, port_num);
+
+	if (port_num > no_ctrl_ports || !gptr) {
+		pr_err("%s: invalid portno#%d\n", __func__, port_num);
+		return -ENODEV;
+	}
+
+	port = gctrl_ports[port_num].port;
+	if (!port) {
+		pr_err("%s: port is null\n", __func__);
+		return -ENODEV;
+	}
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	if (port->gtype == USB_GADGET_SERIAL) {
+		gser = gptr;
+		gser->notify_modem = ghsic_send_cbits_tomodem;
+	}
+
+	if (port->gtype == USB_GADGET_RMNET) {
+		gr = gptr;
+		port->send_cpkt_response = gr->send_cpkt_response;
+		gr->send_encap_cmd = ghsic_send_cpkt_tomodem;
+		gr->notify_modem = ghsic_send_cbits_tomodem;
+	}
+
+	port->port_usb = gptr;
+	port->to_host = 0;
+	port->to_modem = 0;
+	port->drp_cpkt_cnt = 0;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	queue_work(port->wq, &port->connect_w);
+
+	return 0;
+}
+
+static void gctrl_disconnect_w(struct work_struct *w)
+{
+	struct gctrl_port	*port =
+			container_of(w, struct gctrl_port, disconnect_w);
+
+	if (!test_bit(CH_OPENED, &port->bridge_sts))
+		return;
+
+	/* send the dtr zero */
+	ctrl_bridge_close(port->brdg.ch_id);
+	clear_bit(CH_OPENED, &port->bridge_sts);
+}
+
+void ghsic_ctrl_disconnect(void *gptr, int port_num)
+{
+	struct gctrl_port	*port;
+	struct gserial		*gser = NULL;
+	struct grmnet		*gr = NULL;
+	unsigned long		flags;
+
+	pr_debug("%s: port#%d\n", __func__, port_num);
+
+	port = gctrl_ports[port_num].port;
+
+	if (port_num > no_ctrl_ports) {
+		pr_err("%s: invalid portno#%d\n", __func__, port_num);
+		return;
+	}
+
+	if (!gptr || !port) {
+		pr_err("%s: grmnet port is null\n", __func__);
+		return;
+	}
+
+	if (port->gtype == USB_GADGET_SERIAL)
+		gser = gptr;
+	 else
+		gr = gptr;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	if (gr) {
+		gr->send_encap_cmd = 0;
+		gr->notify_modem = 0;
+	}
+
+	if (gser)
+		gser->notify_modem = 0;
+	port->cbits_tomodem = 0;
+	port->port_usb = 0;
+	port->send_cpkt_response = 0;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	queue_work(port->wq, &port->disconnect_w);
+}
+
+static void ghsic_ctrl_status(void *ctxt, unsigned int ctrl_bits)
+{
+	struct gctrl_port	*port = ctxt;
+	struct gserial		*gser;
+
+	pr_debug("%s - input control lines: dcd%c dsr%c break%c "
+		 "ring%c framing%c parity%c overrun%c\n", __func__,
+		 ctrl_bits & ACM_CTRL_DCD ? '+' : '-',
+		 ctrl_bits & ACM_CTRL_DSR ? '+' : '-',
+		 ctrl_bits & ACM_CTRL_BRK ? '+' : '-',
+		 ctrl_bits & ACM_CTRL_RI  ? '+' : '-',
+		 ctrl_bits & ACM_CTRL_FRAMING ? '+' : '-',
+		 ctrl_bits & ACM_CTRL_PARITY ? '+' : '-',
+		 ctrl_bits & ACM_CTRL_OVERRUN ? '+' : '-');
+
+	port->cbits_tohost = ctrl_bits;
+	gser = port->port_usb;
+	if (gser && gser->send_modem_ctrl_bits)
+		gser->send_modem_ctrl_bits(gser, ctrl_bits);
+}
+
+static int ghsic_ctrl_probe(struct platform_device *pdev)
+{
+	struct gctrl_port	*port;
+	unsigned long		flags;
+
+	pr_debug("%s: name:%s\n", __func__, pdev->name);
+
+	if (pdev->id >= no_ctrl_ports) {
+		pr_err("%s: invalid port: %d\n", __func__, pdev->id);
+		return -EINVAL;
+	}
+
+	port = gctrl_ports[pdev->id].port;
+	set_bit(CH_READY, &port->bridge_sts);
+
+	/* if usb is online, start read */
+	spin_lock_irqsave(&port->port_lock, flags);
+	if (port->port_usb)
+		queue_work(port->wq, &port->connect_w);
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	return 0;
+}
+
+static int ghsic_ctrl_remove(struct platform_device *pdev)
+{
+	struct gctrl_port	*port;
+	struct gserial		*gser = NULL;
+	struct grmnet		*gr = NULL;
+	unsigned long		flags;
+
+	pr_debug("%s: name:%s\n", __func__, pdev->name);
+
+	if (pdev->id >= no_ctrl_ports) {
+		pr_err("%s: invalid port: %d\n", __func__, pdev->id);
+		return -EINVAL;
+	}
+
+	port = gctrl_ports[pdev->id].port;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	if (!port->port_usb) {
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		goto not_ready;
+	}
+
+	if (port->gtype == USB_GADGET_SERIAL)
+		gser = port->port_usb;
+	else
+		gr = port->port_usb;
+
+	port->cbits_tohost = 0;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	if (gr && gr->disconnect)
+		gr->disconnect(gr);
+
+	if (gser && gser->disconnect)
+		gser->disconnect(gser);
+
+	ctrl_bridge_close(port->brdg.ch_id);
+
+	clear_bit(CH_OPENED, &port->bridge_sts);
+not_ready:
+	clear_bit(CH_READY, &port->bridge_sts);
+
+	return 0;
+}
+
+static void ghsic_ctrl_port_free(int portno)
+{
+	struct gctrl_port	*port = gctrl_ports[portno].port;
+	struct platform_driver	*pdrv = &gctrl_ports[portno].pdrv;
+
+	destroy_workqueue(port->wq);
+	kfree(port);
+
+	if (pdrv)
+		platform_driver_unregister(pdrv);
+}
+
+static int gctrl_port_alloc(int portno, enum gadget_type gtype)
+{
+	struct gctrl_port	*port;
+	struct platform_driver	*pdrv;
+
+	port = kzalloc(sizeof(struct gctrl_port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	port->wq = create_singlethread_workqueue(ctrl_bridge_names[portno]);
+	if (!port->wq) {
+		pr_err("%s: Unable to create workqueue:%s\n",
+			__func__, ctrl_bridge_names[portno]);
+		return -ENOMEM;
+	}
+
+	port->port_num = portno;
+	port->gtype = gtype;
+
+	spin_lock_init(&port->port_lock);
+
+	INIT_WORK(&port->connect_w, ghsic_ctrl_connect_w);
+	INIT_WORK(&port->disconnect_w, gctrl_disconnect_w);
+
+	port->brdg.ch_id = portno;
+	port->brdg.ctx = port;
+	port->brdg.ops.send_pkt = ghsic_ctrl_receive;
+	if (port->gtype == USB_GADGET_SERIAL)
+		port->brdg.ops.send_cbits = ghsic_ctrl_status;
+	gctrl_ports[portno].port = port;
+
+	pdrv = &gctrl_ports[portno].pdrv;
+	pdrv->probe = ghsic_ctrl_probe;
+	pdrv->remove = ghsic_ctrl_remove;
+	pdrv->driver.name = ctrl_bridge_names[portno];
+	pdrv->driver.owner = THIS_MODULE;
+
+	platform_driver_register(pdrv);
+
+	pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
+
+	return 0;
+}
+
+int ghsic_ctrl_setup(unsigned int num_ports, enum gadget_type gtype)
+{
+	int		first_port_id = no_ctrl_ports;
+	int		total_num_ports = num_ports + no_ctrl_ports;
+	int		i;
+	int		ret = 0;
+
+	if (!num_ports || total_num_ports > NUM_PORTS) {
+		pr_err("%s: Invalid num of ports count:%d\n",
+				__func__, num_ports);
+		return -EINVAL;
+	}
+
+	pr_debug("%s: requested ports:%d\n", __func__, num_ports);
+
+	for (i = first_port_id; i < (first_port_id + num_ports); i++) {
+
+		/*probe can be called while port_alloc,so update no_ctrl_ports*/
+		no_ctrl_ports++;
+		ret = gctrl_port_alloc(i, gtype);
+		if (ret) {
+			no_ctrl_ports--;
+			pr_err("%s: Unable to alloc port:%d\n", __func__, i);
+			goto free_ports;
+		}
+	}
+
+	return first_port_id;
+
+free_ports:
+	for (i = first_port_id; i < no_ctrl_ports; i++)
+		ghsic_ctrl_port_free(i);
+		no_ctrl_ports = first_port_id;
+	return ret;
+}
+
+#if defined(CONFIG_DEBUG_FS)
+#define DEBUG_BUF_SIZE	1024
+static ssize_t gctrl_read_stats(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	struct gctrl_port	*port;
+	struct platform_driver	*pdrv;
+	char			*buf;
+	unsigned long		flags;
+	int			ret;
+	int			i;
+	int			temp = 0;
+
+	buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	for (i = 0; i < no_ctrl_ports; i++) {
+		port = gctrl_ports[i].port;
+		if (!port)
+			continue;
+		pdrv = &gctrl_ports[i].pdrv;
+		spin_lock_irqsave(&port->port_lock, flags);
+
+		temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+				"\nName:        %s\n"
+				"#PORT:%d port: %p\n"
+				"to_usbhost:    %lu\n"
+				"to_modem:      %lu\n"
+				"cpkt_drp_cnt:  %lu\n"
+				"DTR:           %s\n"
+				"ch_open:       %d\n"
+				"ch_ready:      %d\n",
+				pdrv->driver.name,
+				i, port,
+				port->to_host, port->to_modem,
+				port->drp_cpkt_cnt,
+				port->cbits_tomodem ? "HIGH" : "LOW",
+				test_bit(CH_OPENED, &port->bridge_sts),
+				test_bit(CH_READY, &port->bridge_sts));
+
+		spin_unlock_irqrestore(&port->port_lock, flags);
+	}
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+	kfree(buf);
+
+	return ret;
+}
+
+static ssize_t gctrl_reset_stats(struct file *file,
+	const char __user *buf, size_t count, loff_t *ppos)
+{
+	struct gctrl_port	*port;
+	int			i;
+	unsigned long		flags;
+
+	for (i = 0; i < no_ctrl_ports; i++) {
+		port = gctrl_ports[i].port;
+		if (!port)
+			continue;
+
+		spin_lock_irqsave(&port->port_lock, flags);
+		port->to_host = 0;
+		port->to_modem = 0;
+		port->drp_cpkt_cnt = 0;
+		spin_unlock_irqrestore(&port->port_lock, flags);
+	}
+	return count;
+}
+
+const struct file_operations gctrl_stats_ops = {
+	.read = gctrl_read_stats,
+	.write = gctrl_reset_stats,
+};
+
+struct dentry	*gctrl_dent;
+struct dentry	*gctrl_dfile;
+static void gctrl_debugfs_init(void)
+{
+	gctrl_dent = debugfs_create_dir("ghsic_ctrl_xport", 0);
+	if (IS_ERR(gctrl_dent))
+		return;
+
+	gctrl_dfile =
+		debugfs_create_file("status", 0444, gctrl_dent, 0,
+			&gctrl_stats_ops);
+	if (!gctrl_dfile || IS_ERR(gctrl_dfile))
+		debugfs_remove(gctrl_dent);
+}
+
+static void gctrl_debugfs_exit(void)
+{
+	debugfs_remove(gctrl_dfile);
+	debugfs_remove(gctrl_dent);
+}
+
+#else
+static void gctrl_debugfs_init(void) { }
+static void gctrl_debugfs_exit(void) { }
+#endif
+
+static int __init gctrl_init(void)
+{
+	gctrl_debugfs_init();
+
+	return 0;
+}
+module_init(gctrl_init);
+
+static void __exit gctrl_exit(void)
+{
+	gctrl_debugfs_exit();
+}
+module_exit(gctrl_exit);
+MODULE_DESCRIPTION("hsic control xport driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/gadget/u_ctrl_hsuart.c b/drivers/usb/gadget/u_ctrl_hsuart.c
new file mode 100644
index 0000000..7102d81
--- /dev/null
+++ b/drivers/usb/gadget/u_ctrl_hsuart.c
@@ -0,0 +1,576 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/termios.h>
+#include <linux/debugfs.h>
+#include <linux/smux.h>
+
+#include <mach/usb_gadget_xport.h>
+
+#define CH_OPENED 0
+#define CH_READY 1
+
+static unsigned int num_ctrl_ports;
+
+static const char *ghsuart_ctrl_names[] = {
+	"SMUX_RMNET_CTL_HSUART"
+};
+
+struct ghsuart_ctrl_port {
+	/* port */
+	unsigned port_num;
+	/* gadget */
+	enum gadget_type gtype;
+	spinlock_t port_lock;
+	void *port_usb;
+	/* work queue*/
+	struct workqueue_struct	*wq;
+	struct work_struct connect_w;
+	struct work_struct disconnect_w;
+	/*ctrl pkt response cb*/
+	int (*send_cpkt_response)(void *g, void *buf, size_t len);
+	void *ctxt;
+	unsigned int ch_id;
+	/* flow control bits */
+	unsigned long flags;
+	int (*send_pkt)(void *, void *, size_t actual);
+	/* Channel status */
+	unsigned long channel_sts;
+	/* control bits */
+	unsigned cbits_tomodem;
+	/* counters */
+	unsigned long to_modem;
+	unsigned long to_host;
+	unsigned long drp_cpkt_cnt;
+};
+
+static struct {
+	struct ghsuart_ctrl_port	*port;
+	struct platform_driver	pdrv;
+} ghsuart_ctrl_ports[NUM_HSUART_PORTS];
+
+static int ghsuart_ctrl_receive(void *dev, void *buf, size_t actual);
+
+static void smux_control_event(void *priv, int event_type, const void *metadata)
+{
+	struct grmnet		*gr = NULL;
+	struct ghsuart_ctrl_port	*port = priv;
+	void			*buf;
+	unsigned long		flags;
+	size_t			len;
+
+	switch (event_type) {
+	case SMUX_CONNECTED:
+		spin_lock_irqsave(&port->port_lock, flags);
+		if (!port->port_usb) {
+			spin_unlock_irqrestore(&port->port_lock, flags);
+			return;
+		}
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		set_bit(CH_OPENED, &port->channel_sts);
+		if (port->gtype == USB_GADGET_RMNET) {
+			gr = port->port_usb;
+			if (gr && gr->connect)
+				gr->connect(gr);
+		}
+		break;
+	case SMUX_DISCONNECTED:
+		clear_bit(CH_OPENED, &port->channel_sts);
+		break;
+	case SMUX_READ_DONE:
+		len = ((struct smux_meta_read *)metadata)->len;
+		buf = ((struct smux_meta_read *)metadata)->buffer;
+		ghsuart_ctrl_receive(port, buf, len);
+		break;
+	case SMUX_READ_FAIL:
+		buf = ((struct smux_meta_read *)metadata)->buffer;
+		kfree(buf);
+		break;
+	case SMUX_WRITE_DONE:
+	case SMUX_WRITE_FAIL:
+		buf = ((struct smux_meta_write *)metadata)->buffer;
+		kfree(buf);
+		break;
+	case SMUX_LOW_WM_HIT:
+	case SMUX_HIGH_WM_HIT:
+	case SMUX_TIOCM_UPDATE:
+		break;
+	default:
+		pr_err("%s Event %d not supported\n", __func__, event_type);
+	};
+}
+
+static int rx_control_buffer(void *priv, void **pkt_priv, void **buffer,
+			int size)
+{
+	void *rx_buf;
+
+	rx_buf = kmalloc(size, GFP_KERNEL);
+	if (!rx_buf)
+		return -EAGAIN;
+	*buffer = rx_buf;
+	*pkt_priv = NULL;
+
+	return 0;
+}
+
+static int ghsuart_ctrl_receive(void *dev, void *buf, size_t actual)
+{
+	struct ghsuart_ctrl_port	*port = dev;
+	int retval = 0;
+
+	pr_debug_ratelimited("%s: read complete bytes read: %d\n",
+			__func__, actual);
+
+	/* send it to USB here */
+	if (port && port->send_cpkt_response) {
+		retval = port->send_cpkt_response(port->port_usb, buf, actual);
+		port->to_host++;
+	}
+	kfree(buf);
+	return retval;
+}
+
+static int
+ghsuart_send_cpkt_tomodem(u8 portno, void *buf, size_t len)
+{
+	void			*cbuf;
+	struct ghsuart_ctrl_port	*port;
+	int			ret;
+
+	if (portno >= num_ctrl_ports) {
+		pr_err("%s: Invalid portno#%d\n", __func__, portno);
+		return -ENODEV;
+	}
+
+	port = ghsuart_ctrl_ports[portno].port;
+	if (!port) {
+		pr_err("%s: port is null\n", __func__);
+		return -ENODEV;
+	}
+	/* drop cpkt if ch is not open */
+	if (!test_bit(CH_OPENED, &port->channel_sts)) {
+		port->drp_cpkt_cnt++;
+		return 0;
+	}
+	cbuf = kmalloc(len, GFP_ATOMIC);
+	if (!cbuf)
+		return -ENOMEM;
+
+	memcpy(cbuf, buf, len);
+
+	pr_debug("%s: ctrl_pkt:%d bytes\n", __func__, len);
+
+	ret = msm_smux_write(port->ch_id, port, (void *)cbuf, len);
+	if (ret < 0) {
+		pr_err_ratelimited("%s: write error:%d\n",
+				__func__, ret);
+		port->drp_cpkt_cnt++;
+		kfree(cbuf);
+		return ret;
+	}
+	port->to_modem++;
+
+	return 0;
+}
+
+static void
+ghsuart_send_cbits_tomodem(void *gptr, u8 portno, int cbits)
+{
+	struct ghsuart_ctrl_port	*port;
+
+	if (portno >= num_ctrl_ports || !gptr) {
+		pr_err("%s: Invalid portno#%d\n", __func__, portno);
+		return;
+	}
+
+	port = ghsuart_ctrl_ports[portno].port;
+	if (!port) {
+		pr_err("%s: port is null\n", __func__);
+		return;
+	}
+
+	if (cbits == port->cbits_tomodem)
+		return;
+
+	port->cbits_tomodem = cbits;
+
+	if (!test_bit(CH_OPENED, &port->channel_sts))
+		return;
+
+	pr_debug("%s: ctrl_tomodem:%d\n", __func__, cbits);
+	/* Send the control bits to the Modem */
+	msm_smux_tiocm_set(port->ch_id, cbits, ~cbits);
+}
+
+static void ghsuart_ctrl_connect_w(struct work_struct *w)
+{
+	struct ghsuart_ctrl_port	*port =
+			container_of(w, struct ghsuart_ctrl_port, connect_w);
+	int			retval;
+
+	if (!port || !test_bit(CH_READY, &port->channel_sts))
+		return;
+
+	pr_debug("%s: port:%p\n", __func__, port);
+
+	retval = msm_smux_open(port->ch_id, port->ctxt, smux_control_event,
+				rx_control_buffer);
+	if (retval < 0) {
+		pr_err(" %s smux_open failed\n", __func__);
+		return;
+	}
+
+}
+
+int ghsuart_ctrl_connect(void *gptr, int port_num)
+{
+	struct ghsuart_ctrl_port	*port;
+	struct grmnet		*gr;
+	unsigned long		flags;
+
+	pr_debug("%s: port#%d\n", __func__, port_num);
+
+	if (port_num > num_ctrl_ports || !gptr) {
+		pr_err("%s: invalid portno#%d\n", __func__, port_num);
+		return -ENODEV;
+	}
+
+	port = ghsuart_ctrl_ports[port_num].port;
+	if (!port) {
+		pr_err("%s: port is null\n", __func__);
+		return -ENODEV;
+	}
+
+	spin_lock_irqsave(&port->port_lock, flags);
+
+	gr = gptr;
+	port->send_cpkt_response = gr->send_cpkt_response;
+	gr->send_encap_cmd = ghsuart_send_cpkt_tomodem;
+	gr->notify_modem = ghsuart_send_cbits_tomodem;
+
+	port->port_usb = gptr;
+	port->to_host = 0;
+	port->to_modem = 0;
+	port->drp_cpkt_cnt = 0;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	if (test_bit(CH_READY, &port->channel_sts))
+		queue_work(port->wq, &port->connect_w);
+
+	return 0;
+}
+
+static void ghsuart_ctrl_disconnect_w(struct work_struct *w)
+{
+	struct ghsuart_ctrl_port	*port =
+			container_of(w, struct ghsuart_ctrl_port, disconnect_w);
+
+	if (!test_bit(CH_OPENED, &port->channel_sts))
+		return;
+
+	msm_smux_close(port->ch_id);
+	clear_bit(CH_OPENED, &port->channel_sts);
+}
+
+void ghsuart_ctrl_disconnect(void *gptr, int port_num)
+{
+	struct gctrl_port	*port;
+	struct grmnet		*gr = NULL;
+	unsigned long		flags;
+
+	pr_debug("%s: port#%d\n", __func__, port_num);
+
+	if (port_num > num_ctrl_ports) {
+		pr_err("%s: invalid portno#%d\n", __func__, port_num);
+		return;
+	}
+
+	port = gctrl_ports[port_num].port;
+
+	if (!gptr || !port) {
+		pr_err("%s: grmnet port is null\n", __func__);
+		return;
+	}
+
+	gr = gptr;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	gr->send_encap_cmd = 0;
+	gr->notify_modem = 0;
+	port->cbits_tomodem = 0;
+	port->port_usb = 0;
+	port->send_cpkt_response = 0;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	queue_work(port->wq, &port->disconnect_w);
+}
+
+static int ghsuart_ctrl_probe(struct platform_device *pdev)
+{
+	struct ghsuart_ctrl_port	*port;
+	unsigned long		flags;
+
+	pr_debug("%s: name:%s\n", __func__, pdev->name);
+
+	port = ghsuart_ctrl_ports[pdev->id].port;
+	set_bit(CH_READY, &port->channel_sts);
+
+	/* if usb is online, start read */
+	spin_lock_irqsave(&port->port_lock, flags);
+	if (port->port_usb)
+		queue_work(port->wq, &port->connect_w);
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	return 0;
+}
+
+static int ghsuart_ctrl_remove(struct platform_device *pdev)
+{
+	struct ghsuart_ctrl_port	*port;
+	struct grmnet		*gr = NULL;
+	unsigned long		flags;
+
+	pr_debug("%s: name:%s\n", __func__, pdev->name);
+
+	port = ghsuart_ctrl_ports[pdev->id].port;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	if (!port->port_usb) {
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		goto not_ready;
+	}
+
+	gr = port->port_usb;
+
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	if (gr && gr->disconnect)
+		gr->disconnect(gr);
+
+	clear_bit(CH_OPENED, &port->channel_sts);
+not_ready:
+	clear_bit(CH_READY, &port->channel_sts);
+
+	return 0;
+}
+
+static void ghsuart_ctrl_port_free(int portno)
+{
+	struct ghsuart_ctrl_port	*port = ghsuart_ctrl_ports[portno].port;
+	struct platform_driver	*pdrv = &gctrl_ports[portno].pdrv;
+
+	destroy_workqueue(port->wq);
+	if (pdrv)
+		platform_driver_unregister(pdrv);
+	kfree(port);
+}
+
+static int ghsuart_ctrl_port_alloc(int portno, enum gadget_type gtype)
+{
+	struct ghsuart_ctrl_port	*port;
+	struct platform_driver	*pdrv;
+	int err;
+
+	port = kzalloc(sizeof(struct ghsuart_ctrl_port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	port->wq = create_singlethread_workqueue(ghsuart_ctrl_names[portno]);
+	if (!port->wq) {
+		pr_err("%s: Unable to create workqueue:%s\n",
+			__func__, ghsuart_ctrl_names[portno]);
+		kfree(port);
+		return -ENOMEM;
+	}
+
+	port->port_num = portno;
+	port->gtype = gtype;
+
+	spin_lock_init(&port->port_lock);
+
+	INIT_WORK(&port->connect_w, ghsuart_ctrl_connect_w);
+	INIT_WORK(&port->disconnect_w, ghsuart_ctrl_disconnect_w);
+
+	port->ch_id = SMUX_USB_RMNET_CTL_0;
+	port->ctxt = port;
+	port->send_pkt = ghsuart_ctrl_receive;
+	ghsuart_ctrl_ports[portno].port = port;
+
+	pdrv = &ghsuart_ctrl_ports[portno].pdrv;
+	pdrv->probe = ghsuart_ctrl_probe;
+	pdrv->remove = ghsuart_ctrl_remove;
+	pdrv->driver.name = ghsuart_ctrl_names[portno];
+	pdrv->driver.owner = THIS_MODULE;
+
+	err = platform_driver_register(pdrv);
+	if (unlikely(err < 0))
+		return err;
+	pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
+
+	return 0;
+}
+
+int ghsuart_ctrl_setup(unsigned int num_ports, enum gadget_type gtype)
+{
+	int	first_port_id = num_ctrl_ports;
+	int	total_num_ports = num_ports + num_ctrl_ports;
+	int	i;
+	int	ret = 0;
+
+	if (!num_ports || total_num_ports > NUM_HSUART_PORTS) {
+		pr_err("%s: Invalid num of ports count:%d\n",
+				__func__, num_ports);
+		return -EINVAL;
+	}
+
+	pr_debug("%s: requested ports:%d\n", __func__, num_ports);
+
+	for (i = first_port_id; i < (first_port_id + num_ports); i++) {
+
+		num_ctrl_ports++;
+		ret = ghsuart_ctrl_port_alloc(i, gtype);
+		if (ret) {
+			num_ctrl_ports--;
+			pr_err("%s: Unable to alloc port:%d\n", __func__, i);
+			goto free_ports;
+		}
+	}
+
+	return first_port_id;
+
+free_ports:
+	for (i = first_port_id; i < num_ctrl_ports; i++)
+		ghsuart_ctrl_port_free(i);
+		num_ctrl_ports = first_port_id;
+	return ret;
+}
+
+#define DEBUG_BUF_SIZE	1024
+static ssize_t ghsuart_ctrl_read_stats(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	struct ghsuart_ctrl_port	*port;
+	char			*buf;
+	unsigned long		flags;
+	int			ret;
+	int			i;
+	int			temp = 0;
+
+	buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	for (i = 0; i < num_ctrl_ports; i++) {
+		port = ghsuart_ctrl_ports[i].port;
+		if (!port)
+			continue;
+		spin_lock_irqsave(&port->port_lock, flags);
+
+		temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+				"#PORT:%d port: %p\n"
+				"to_usbhost:    %lu\n"
+				"to_modem:      %lu\n"
+				"cpkt_drp_cnt:  %lu\n"
+				"DTR:           %s\n",
+				i, port,
+				port->to_host, port->to_modem,
+				port->drp_cpkt_cnt,
+				port->cbits_tomodem ? "HIGH" : "LOW");
+
+		spin_unlock_irqrestore(&port->port_lock, flags);
+	}
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+	kfree(buf);
+
+	return ret;
+}
+
+static ssize_t ghsuart_ctrl_reset_stats(struct file *file,
+	const char __user *buf, size_t count, loff_t *ppos)
+{
+	struct ghsuart_ctrl_port	*port;
+	int			i;
+	unsigned long		flags;
+
+	for (i = 0; i < num_ctrl_ports; i++) {
+		port = ghsuart_ctrl_ports[i].port;
+		if (!port)
+			continue;
+
+		spin_lock_irqsave(&port->port_lock, flags);
+		port->to_host = 0;
+		port->to_modem = 0;
+		port->drp_cpkt_cnt = 0;
+		spin_unlock_irqrestore(&port->port_lock, flags);
+	}
+	return count;
+}
+
+static const struct file_operations ghsuart_ctrl_stats_ops = {
+	.read = ghsuart_ctrl_read_stats,
+	.write = ghsuart_ctrl_reset_stats,
+};
+
+static struct dentry	*ghsuart_ctrl_dent;
+static int ghsuart_ctrl_debugfs_init(void)
+{
+	struct dentry	*ghsuart_ctrl_dfile;
+
+	ghsuart_ctrl_dent = debugfs_create_dir("ghsuart_ctrl_xport", 0);
+	if (!ghsuart_ctrl_dent || IS_ERR(ghsuart_ctrl_dent))
+		return -ENODEV;
+
+	ghsuart_ctrl_dfile =
+		debugfs_create_file("status", S_IRUGO | S_IWUSR,
+				ghsuart_ctrl_dent, 0, &gctrl_stats_ops);
+	if (!ghsuart_ctrl_dfile || IS_ERR(ghsuart_ctrl_dfile)) {
+		debugfs_remove(ghsuart_ctrl_dent);
+		ghsuart_ctrl_dent = NULL;
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void ghsuart_ctrl_debugfs_exit(void)
+{
+	debugfs_remove_recursive(ghsuart_ctrl_dent);
+}
+
+static int __init ghsuart_ctrl_init(void)
+{
+	int ret;
+
+	ret = ghsuart_ctrl_debugfs_init();
+	if (ret) {
+		pr_debug("mode debugfs file is not available\n");
+		return ret;
+	}
+	return 0;
+}
+module_init(ghsuart_ctrl_init);
+
+static void __exit ghsuart_ctrl_exit(void)
+{
+	ghsuart_ctrl_debugfs_exit();
+}
+module_exit(ghsuart_ctrl_exit);
+
+MODULE_DESCRIPTION("HSUART control xport for RmNet");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/gadget/u_data_hsic.c b/drivers/usb/gadget/u_data_hsic.c
new file mode 100644
index 0000000..89d2887
--- /dev/null
+++ b/drivers/usb/gadget/u_data_hsic.c
@@ -0,0 +1,1143 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/termios.h>
+#include <linux/netdevice.h>
+#include <linux/debugfs.h>
+#include <linux/bitops.h>
+#include <linux/termios.h>
+#include <mach/usb_bridge.h>
+#include <mach/usb_gadget_xport.h>
+
+static unsigned int no_data_ports;
+
+static const char *data_bridge_names[] = {
+	"dun_data_hsic0",
+	"rmnet_data_hsic0"
+};
+
+#define DATA_BRIDGE_NAME_MAX_LEN		20
+
+#define GHSIC_DATA_RMNET_RX_Q_SIZE		50
+#define GHSIC_DATA_RMNET_TX_Q_SIZE		300
+#define GHSIC_DATA_SERIAL_RX_Q_SIZE		10
+#define GHSIC_DATA_SERIAL_TX_Q_SIZE		20
+#define GHSIC_DATA_RX_REQ_SIZE			2048
+#define GHSIC_DATA_TX_INTR_THRESHOLD		20
+
+static unsigned int ghsic_data_rmnet_tx_q_size = GHSIC_DATA_RMNET_TX_Q_SIZE;
+module_param(ghsic_data_rmnet_tx_q_size, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsic_data_rmnet_rx_q_size = GHSIC_DATA_RMNET_RX_Q_SIZE;
+module_param(ghsic_data_rmnet_rx_q_size, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsic_data_serial_tx_q_size = GHSIC_DATA_SERIAL_TX_Q_SIZE;
+module_param(ghsic_data_serial_tx_q_size, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsic_data_serial_rx_q_size = GHSIC_DATA_SERIAL_RX_Q_SIZE;
+module_param(ghsic_data_serial_rx_q_size, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsic_data_rx_req_size = GHSIC_DATA_RX_REQ_SIZE;
+module_param(ghsic_data_rx_req_size, uint, S_IRUGO | S_IWUSR);
+
+unsigned int ghsic_data_tx_intr_thld = GHSIC_DATA_TX_INTR_THRESHOLD;
+module_param(ghsic_data_tx_intr_thld, uint, S_IRUGO | S_IWUSR);
+
+/*flow ctrl*/
+#define GHSIC_DATA_FLOW_CTRL_EN_THRESHOLD	500
+#define GHSIC_DATA_FLOW_CTRL_DISABLE		300
+#define GHSIC_DATA_FLOW_CTRL_SUPPORT		1
+#define GHSIC_DATA_PENDLIMIT_WITH_BRIDGE	500
+
+static unsigned int ghsic_data_fctrl_support = GHSIC_DATA_FLOW_CTRL_SUPPORT;
+module_param(ghsic_data_fctrl_support, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsic_data_fctrl_en_thld =
+		GHSIC_DATA_FLOW_CTRL_EN_THRESHOLD;
+module_param(ghsic_data_fctrl_en_thld, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsic_data_fctrl_dis_thld = GHSIC_DATA_FLOW_CTRL_DISABLE;
+module_param(ghsic_data_fctrl_dis_thld, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsic_data_pend_limit_with_bridge =
+		GHSIC_DATA_PENDLIMIT_WITH_BRIDGE;
+module_param(ghsic_data_pend_limit_with_bridge, uint, S_IRUGO | S_IWUSR);
+
+#define CH_OPENED 0
+#define CH_READY 1
+
+struct gdata_port {
+	/* port */
+	unsigned		port_num;
+
+	/* gadget */
+	atomic_t		connected;
+	struct usb_ep		*in;
+	struct usb_ep		*out;
+
+	enum gadget_type	gtype;
+
+	/* data transfer queues */
+	unsigned int		tx_q_size;
+	struct list_head	tx_idle;
+	struct sk_buff_head	tx_skb_q;
+	spinlock_t		tx_lock;
+
+	unsigned int		rx_q_size;
+	struct list_head	rx_idle;
+	struct sk_buff_head	rx_skb_q;
+	spinlock_t		rx_lock;
+
+	/* work */
+	struct workqueue_struct	*wq;
+	struct work_struct	connect_w;
+	struct work_struct	disconnect_w;
+	struct work_struct	write_tomdm_w;
+	struct work_struct	write_tohost_w;
+
+	struct bridge		brdg;
+
+	/*bridge status*/
+	unsigned long		bridge_sts;
+
+	unsigned int		n_tx_req_queued;
+
+	/*counters*/
+	unsigned long		to_modem;
+	unsigned long		to_host;
+	unsigned int		rx_throttled_cnt;
+	unsigned int		rx_unthrottled_cnt;
+	unsigned int		tx_throttled_cnt;
+	unsigned int		tx_unthrottled_cnt;
+	unsigned int		tomodem_drp_cnt;
+	unsigned int		unthrottled_pnd_skbs;
+};
+
+static struct {
+	struct gdata_port	*port;
+	struct platform_driver	pdrv;
+} gdata_ports[NUM_PORTS];
+
+static unsigned int get_timestamp(void);
+static void dbg_timestamp(char *, struct sk_buff *);
+static void ghsic_data_start_rx(struct gdata_port *port);
+
+static void ghsic_data_free_requests(struct usb_ep *ep, struct list_head *head)
+{
+	struct usb_request	*req;
+
+	while (!list_empty(head)) {
+		req = list_entry(head->next, struct usb_request, list);
+		list_del(&req->list);
+		usb_ep_free_request(ep, req);
+	}
+}
+
+static int ghsic_data_alloc_requests(struct usb_ep *ep, struct list_head *head,
+		int num,
+		void (*cb)(struct usb_ep *ep, struct usb_request *),
+		gfp_t flags)
+{
+	int			i;
+	struct usb_request	*req;
+
+	pr_debug("%s: ep:%s head:%p num:%d cb:%p", __func__,
+			ep->name, head, num, cb);
+
+	for (i = 0; i < num; i++) {
+		req = usb_ep_alloc_request(ep, flags);
+		if (!req) {
+			pr_debug("%s: req allocated:%d\n", __func__, i);
+			return list_empty(head) ? -ENOMEM : 0;
+		}
+		req->complete = cb;
+		list_add(&req->list, head);
+	}
+
+	return 0;
+}
+
+static void ghsic_data_unthrottle_tx(void *ctx)
+{
+	struct gdata_port	*port = ctx;
+	unsigned long		flags;
+
+	if (!port || !atomic_read(&port->connected))
+		return;
+
+	spin_lock_irqsave(&port->rx_lock, flags);
+	port->tx_unthrottled_cnt++;
+	spin_unlock_irqrestore(&port->rx_lock, flags);
+
+	queue_work(port->wq, &port->write_tomdm_w);
+	pr_debug("%s: port num =%d unthrottled\n", __func__,
+		port->port_num);
+}
+
+static void ghsic_data_write_tohost(struct work_struct *w)
+{
+	unsigned long		flags;
+	struct sk_buff		*skb;
+	int			ret;
+	struct usb_request	*req;
+	struct usb_ep		*ep;
+	struct gdata_port	*port;
+	struct timestamp_info	*info;
+
+	port = container_of(w, struct gdata_port, write_tohost_w);
+
+	if (!port)
+		return;
+
+	spin_lock_irqsave(&port->tx_lock, flags);
+	ep = port->in;
+	if (!ep) {
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+		return;
+	}
+
+	while (!list_empty(&port->tx_idle)) {
+		skb = __skb_dequeue(&port->tx_skb_q);
+		if (!skb)
+			break;
+
+		req = list_first_entry(&port->tx_idle, struct usb_request,
+				list);
+		req->context = skb;
+		req->buf = skb->data;
+		req->length = skb->len;
+
+		port->n_tx_req_queued++;
+		if (port->n_tx_req_queued == ghsic_data_tx_intr_thld) {
+			req->no_interrupt = 0;
+			port->n_tx_req_queued = 0;
+		} else {
+			req->no_interrupt = 1;
+		}
+
+		list_del(&req->list);
+
+		info = (struct timestamp_info *)skb->cb;
+		info->tx_queued = get_timestamp();
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+		ret = usb_ep_queue(ep, req, GFP_KERNEL);
+		spin_lock_irqsave(&port->tx_lock, flags);
+		if (ret) {
+			pr_err("%s: usb epIn failed\n", __func__);
+			list_add(&req->list, &port->tx_idle);
+			dev_kfree_skb_any(skb);
+			break;
+		}
+		port->to_host++;
+		if (ghsic_data_fctrl_support &&
+			port->tx_skb_q.qlen <= ghsic_data_fctrl_dis_thld &&
+			test_and_clear_bit(RX_THROTTLED, &port->brdg.flags)) {
+			port->rx_unthrottled_cnt++;
+			port->unthrottled_pnd_skbs = port->tx_skb_q.qlen;
+			pr_debug_ratelimited("%s: disable flow ctrl:"
+					" tx skbq len: %u\n",
+					__func__, port->tx_skb_q.qlen);
+			data_bridge_unthrottle_rx(port->brdg.ch_id);
+		}
+	}
+	spin_unlock_irqrestore(&port->tx_lock, flags);
+}
+
+static int ghsic_data_receive(void *p, void *data, size_t len)
+{
+	struct gdata_port	*port = p;
+	unsigned long		flags;
+	struct sk_buff		*skb = data;
+
+	if (!port || !atomic_read(&port->connected)) {
+		dev_kfree_skb_any(skb);
+		return -ENOTCONN;
+	}
+
+	pr_debug("%s: p:%p#%d skb_len:%d\n", __func__,
+			port, port->port_num, skb->len);
+
+	spin_lock_irqsave(&port->tx_lock, flags);
+	__skb_queue_tail(&port->tx_skb_q, skb);
+
+	if (ghsic_data_fctrl_support &&
+			port->tx_skb_q.qlen >= ghsic_data_fctrl_en_thld) {
+		set_bit(RX_THROTTLED, &port->brdg.flags);
+		port->rx_throttled_cnt++;
+		pr_debug_ratelimited("%s: flow ctrl enabled: tx skbq len: %u\n",
+					__func__, port->tx_skb_q.qlen);
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+		queue_work(port->wq, &port->write_tohost_w);
+		return -EBUSY;
+	}
+
+	spin_unlock_irqrestore(&port->tx_lock, flags);
+
+	queue_work(port->wq, &port->write_tohost_w);
+
+	return 0;
+}
+
+static void ghsic_data_write_tomdm(struct work_struct *w)
+{
+	struct gdata_port	*port;
+	struct sk_buff		*skb;
+	struct timestamp_info	*info;
+	unsigned long		flags;
+	int			ret;
+
+	port = container_of(w, struct gdata_port, write_tomdm_w);
+
+	if (!port || !atomic_read(&port->connected))
+		return;
+
+	spin_lock_irqsave(&port->rx_lock, flags);
+	if (test_bit(TX_THROTTLED, &port->brdg.flags)) {
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+		goto start_rx;
+	}
+
+	while ((skb = __skb_dequeue(&port->rx_skb_q))) {
+		pr_debug("%s: port:%p tom:%lu pno:%d\n", __func__,
+				port, port->to_modem, port->port_num);
+
+		info = (struct timestamp_info *)skb->cb;
+		info->rx_done_sent = get_timestamp();
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+		ret = data_bridge_write(port->brdg.ch_id, skb);
+		spin_lock_irqsave(&port->rx_lock, flags);
+		if (ret < 0) {
+			if (ret == -EBUSY) {
+				/*flow control*/
+				port->tx_throttled_cnt++;
+				break;
+			}
+			pr_err_ratelimited("%s: write error:%d\n",
+					__func__, ret);
+			port->tomodem_drp_cnt++;
+			dev_kfree_skb_any(skb);
+			break;
+		}
+		port->to_modem++;
+	}
+	spin_unlock_irqrestore(&port->rx_lock, flags);
+start_rx:
+	ghsic_data_start_rx(port);
+}
+
+static void ghsic_data_epin_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct gdata_port	*port = ep->driver_data;
+	struct sk_buff		*skb = req->context;
+	int			status = req->status;
+
+	switch (status) {
+	case 0:
+		/* successful completion */
+		dbg_timestamp("DL", skb);
+		break;
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		dev_kfree_skb_any(skb);
+		req->buf = 0;
+		usb_ep_free_request(ep, req);
+		return;
+	default:
+		pr_err("%s: data tx ep error %d\n", __func__, status);
+		break;
+	}
+
+	dev_kfree_skb_any(skb);
+
+	spin_lock(&port->tx_lock);
+	list_add_tail(&req->list, &port->tx_idle);
+	spin_unlock(&port->tx_lock);
+
+	queue_work(port->wq, &port->write_tohost_w);
+}
+
+static void
+ghsic_data_epout_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct gdata_port	*port = ep->driver_data;
+	struct sk_buff		*skb = req->context;
+	struct timestamp_info	*info = (struct timestamp_info *)skb->cb;
+	int			status = req->status;
+	int			queue = 0;
+
+	switch (status) {
+	case 0:
+		skb_put(skb, req->actual);
+		queue = 1;
+		break;
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* cable disconnection */
+		dev_kfree_skb_any(skb);
+		req->buf = 0;
+		usb_ep_free_request(ep, req);
+		return;
+	default:
+		pr_err_ratelimited("%s: %s response error %d, %d/%d\n",
+					__func__, ep->name, status,
+				req->actual, req->length);
+		dev_kfree_skb_any(skb);
+		break;
+	}
+
+	spin_lock(&port->rx_lock);
+	if (queue) {
+		info->rx_done = get_timestamp();
+		__skb_queue_tail(&port->rx_skb_q, skb);
+		list_add_tail(&req->list, &port->rx_idle);
+		queue_work(port->wq, &port->write_tomdm_w);
+	}
+	spin_unlock(&port->rx_lock);
+}
+
+static void ghsic_data_start_rx(struct gdata_port *port)
+{
+	struct usb_request	*req;
+	struct usb_ep		*ep;
+	unsigned long		flags;
+	int			ret;
+	struct sk_buff		*skb;
+	struct timestamp_info	*info;
+	unsigned int		created;
+
+	pr_debug("%s: port:%p\n", __func__, port);
+	if (!port)
+		return;
+
+	spin_lock_irqsave(&port->rx_lock, flags);
+	ep = port->out;
+	if (!ep) {
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+		return;
+	}
+
+	while (atomic_read(&port->connected) && !list_empty(&port->rx_idle)) {
+		if (port->rx_skb_q.qlen > ghsic_data_pend_limit_with_bridge)
+			break;
+
+		req = list_first_entry(&port->rx_idle,
+					struct usb_request, list);
+
+		created = get_timestamp();
+		skb = alloc_skb(ghsic_data_rx_req_size, GFP_ATOMIC);
+		if (!skb)
+			break;
+		info = (struct timestamp_info *)skb->cb;
+		info->created = created;
+		list_del(&req->list);
+		req->buf = skb->data;
+		req->length = ghsic_data_rx_req_size;
+		req->context = skb;
+
+		info->rx_queued = get_timestamp();
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+		ret = usb_ep_queue(ep, req, GFP_KERNEL);
+		spin_lock_irqsave(&port->rx_lock, flags);
+		if (ret) {
+			dev_kfree_skb_any(skb);
+
+			pr_err_ratelimited("%s: rx queue failed\n", __func__);
+
+			if (atomic_read(&port->connected))
+				list_add(&req->list, &port->rx_idle);
+			else
+				usb_ep_free_request(ep, req);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&port->rx_lock, flags);
+}
+
+static void ghsic_data_start_io(struct gdata_port *port)
+{
+	unsigned long	flags;
+	struct usb_ep	*ep;
+	int		ret;
+
+	pr_debug("%s: port:%p\n", __func__, port);
+
+	if (!port)
+		return;
+
+	spin_lock_irqsave(&port->rx_lock, flags);
+	ep = port->out;
+	if (!ep) {
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+		return;
+	}
+
+	ret = ghsic_data_alloc_requests(ep, &port->rx_idle,
+		port->rx_q_size, ghsic_data_epout_complete, GFP_ATOMIC);
+	if (ret) {
+		pr_err("%s: rx req allocation failed\n", __func__);
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+		return;
+	}
+	spin_unlock_irqrestore(&port->rx_lock, flags);
+
+	spin_lock_irqsave(&port->tx_lock, flags);
+	ep = port->in;
+	if (!ep) {
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+		return;
+	}
+
+	ret = ghsic_data_alloc_requests(ep, &port->tx_idle,
+		port->tx_q_size, ghsic_data_epin_complete, GFP_ATOMIC);
+	if (ret) {
+		pr_err("%s: tx req allocation failed\n", __func__);
+		ghsic_data_free_requests(ep, &port->rx_idle);
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+		return;
+	}
+	spin_unlock_irqrestore(&port->tx_lock, flags);
+
+	/* queue out requests */
+	ghsic_data_start_rx(port);
+}
+
+static void ghsic_data_connect_w(struct work_struct *w)
+{
+	struct gdata_port	*port =
+		container_of(w, struct gdata_port, connect_w);
+	int			ret;
+
+	if (!port || !atomic_read(&port->connected) ||
+		!test_bit(CH_READY, &port->bridge_sts))
+		return;
+
+	pr_debug("%s: port:%p\n", __func__, port);
+
+	ret = data_bridge_open(&port->brdg);
+	if (ret) {
+		pr_err("%s: unable open bridge ch:%d err:%d\n",
+				__func__, port->brdg.ch_id, ret);
+		return;
+	}
+
+	set_bit(CH_OPENED, &port->bridge_sts);
+
+	ghsic_data_start_io(port);
+}
+
+static void ghsic_data_disconnect_w(struct work_struct *w)
+{
+	struct gdata_port	*port =
+		container_of(w, struct gdata_port, disconnect_w);
+
+	if (!test_bit(CH_OPENED, &port->bridge_sts))
+		return;
+
+	data_bridge_close(port->brdg.ch_id);
+	clear_bit(CH_OPENED, &port->bridge_sts);
+}
+
+static void ghsic_data_free_buffers(struct gdata_port *port)
+{
+	struct sk_buff	*skb;
+	unsigned long	flags;
+
+	if (!port)
+		return;
+
+	spin_lock_irqsave(&port->tx_lock, flags);
+	if (!port->in) {
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+		return;
+	}
+
+	ghsic_data_free_requests(port->in, &port->tx_idle);
+
+	while ((skb = __skb_dequeue(&port->tx_skb_q)))
+		dev_kfree_skb_any(skb);
+	spin_unlock_irqrestore(&port->tx_lock, flags);
+
+	spin_lock_irqsave(&port->rx_lock, flags);
+	if (!port->out) {
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+		return;
+	}
+
+	ghsic_data_free_requests(port->out, &port->rx_idle);
+
+	while ((skb = __skb_dequeue(&port->rx_skb_q)))
+		dev_kfree_skb_any(skb);
+	spin_unlock_irqrestore(&port->rx_lock, flags);
+}
+
+static int ghsic_data_probe(struct platform_device *pdev)
+{
+	struct gdata_port *port;
+
+	pr_debug("%s: name:%s no_data_ports= %d\n",
+		__func__, pdev->name, no_data_ports);
+
+	if (pdev->id >= no_data_ports) {
+		pr_err("%s: invalid port: %d\n", __func__, pdev->id);
+		return -EINVAL;
+	}
+
+	port = gdata_ports[pdev->id].port;
+	set_bit(CH_READY, &port->bridge_sts);
+
+	/* if usb is online, try opening bridge */
+	if (atomic_read(&port->connected))
+		queue_work(port->wq, &port->connect_w);
+
+	return 0;
+}
+
+/* mdm disconnect */
+static int ghsic_data_remove(struct platform_device *pdev)
+{
+	struct gdata_port *port;
+	struct usb_ep	*ep_in;
+	struct usb_ep	*ep_out;
+
+	pr_debug("%s: name:%s\n", __func__, pdev->name);
+
+	if (pdev->id >= no_data_ports) {
+		pr_err("%s: invalid port: %d\n", __func__, pdev->id);
+		return -EINVAL;
+	}
+
+	port = gdata_ports[pdev->id].port;
+
+	ep_in = port->in;
+	if (ep_in)
+		usb_ep_fifo_flush(ep_in);
+
+	ep_out = port->out;
+	if (ep_out)
+		usb_ep_fifo_flush(ep_out);
+
+	ghsic_data_free_buffers(port);
+
+	data_bridge_close(port->brdg.ch_id);
+
+	clear_bit(CH_READY, &port->bridge_sts);
+	clear_bit(CH_OPENED, &port->bridge_sts);
+
+	return 0;
+}
+
+static void ghsic_data_port_free(int portno)
+{
+	struct gdata_port	*port = gdata_ports[portno].port;
+	struct platform_driver	*pdrv = &gdata_ports[portno].pdrv;
+
+	destroy_workqueue(port->wq);
+	kfree(port);
+
+	if (pdrv)
+		platform_driver_unregister(pdrv);
+}
+
+static int ghsic_data_port_alloc(unsigned port_num, enum gadget_type gtype)
+{
+	struct gdata_port	*port;
+	struct platform_driver	*pdrv;
+
+	port = kzalloc(sizeof(struct gdata_port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	port->wq = create_singlethread_workqueue(data_bridge_names[port_num]);
+	if (!port->wq) {
+		pr_err("%s: Unable to create workqueue:%s\n",
+			__func__, data_bridge_names[port_num]);
+		kfree(port);
+		return -ENOMEM;
+	}
+	port->port_num = port_num;
+
+	/* port initialization */
+	spin_lock_init(&port->rx_lock);
+	spin_lock_init(&port->tx_lock);
+
+	INIT_WORK(&port->connect_w, ghsic_data_connect_w);
+	INIT_WORK(&port->disconnect_w, ghsic_data_disconnect_w);
+	INIT_WORK(&port->write_tohost_w, ghsic_data_write_tohost);
+	INIT_WORK(&port->write_tomdm_w, ghsic_data_write_tomdm);
+
+	INIT_LIST_HEAD(&port->tx_idle);
+	INIT_LIST_HEAD(&port->rx_idle);
+
+	skb_queue_head_init(&port->tx_skb_q);
+	skb_queue_head_init(&port->rx_skb_q);
+
+	port->gtype = gtype;
+	port->brdg.ch_id = port_num;
+	port->brdg.ctx = port;
+	port->brdg.ops.send_pkt = ghsic_data_receive;
+	port->brdg.ops.unthrottle_tx = ghsic_data_unthrottle_tx;
+	gdata_ports[port_num].port = port;
+
+	pdrv = &gdata_ports[port_num].pdrv;
+	pdrv->probe = ghsic_data_probe;
+	pdrv->remove = ghsic_data_remove;
+	pdrv->driver.name = data_bridge_names[port_num];
+	pdrv->driver.owner = THIS_MODULE;
+
+	platform_driver_register(pdrv);
+
+	pr_debug("%s: port:%p portno:%d\n", __func__, port, port_num);
+
+	return 0;
+}
+
+void ghsic_data_disconnect(void *gptr, int port_num)
+{
+	struct gdata_port	*port;
+	unsigned long		flags;
+
+	pr_debug("%s: port#%d\n", __func__, port_num);
+
+	port = gdata_ports[port_num].port;
+
+	if (port_num > no_data_ports) {
+		pr_err("%s: invalid portno#%d\n", __func__, port_num);
+		return;
+	}
+
+	if (!gptr || !port) {
+		pr_err("%s: port is null\n", __func__);
+		return;
+	}
+
+	ghsic_data_free_buffers(port);
+
+	/* disable endpoints */
+	if (port->in)
+		usb_ep_disable(port->out);
+
+	if (port->out)
+		usb_ep_disable(port->in);
+
+	atomic_set(&port->connected, 0);
+
+	spin_lock_irqsave(&port->tx_lock, flags);
+	port->in = NULL;
+	port->n_tx_req_queued = 0;
+	clear_bit(RX_THROTTLED, &port->brdg.flags);
+	spin_unlock_irqrestore(&port->tx_lock, flags);
+
+	spin_lock_irqsave(&port->rx_lock, flags);
+	port->out = NULL;
+	clear_bit(TX_THROTTLED, &port->brdg.flags);
+	spin_unlock_irqrestore(&port->rx_lock, flags);
+
+	queue_work(port->wq, &port->disconnect_w);
+}
+
+int ghsic_data_connect(void *gptr, int port_num)
+{
+	struct gdata_port		*port;
+	struct gserial			*gser;
+	struct grmnet			*gr;
+	unsigned long			flags;
+	int				ret = 0;
+
+	pr_debug("%s: port#%d\n", __func__, port_num);
+
+	port = gdata_ports[port_num].port;
+
+	if (port_num > no_data_ports) {
+		pr_err("%s: invalid portno#%d\n", __func__, port_num);
+		return -ENODEV;
+	}
+
+	if (!gptr || !port) {
+		pr_err("%s: port is null\n", __func__);
+		return -ENODEV;
+	}
+
+	if (port->gtype == USB_GADGET_SERIAL) {
+		gser = gptr;
+
+		spin_lock_irqsave(&port->tx_lock, flags);
+		port->in = gser->in;
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+
+		spin_lock_irqsave(&port->rx_lock, flags);
+		port->out = gser->out;
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+
+		port->tx_q_size = ghsic_data_serial_tx_q_size;
+		port->rx_q_size = ghsic_data_serial_rx_q_size;
+		gser->in->driver_data = port;
+		gser->out->driver_data = port;
+	} else {
+		gr = gptr;
+
+		spin_lock_irqsave(&port->tx_lock, flags);
+		port->in = gr->in;
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+
+		spin_lock_irqsave(&port->rx_lock, flags);
+		port->out = gr->out;
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+
+		port->tx_q_size = ghsic_data_rmnet_tx_q_size;
+		port->rx_q_size = ghsic_data_rmnet_rx_q_size;
+		gr->in->driver_data = port;
+		gr->out->driver_data = port;
+	}
+
+	ret = usb_ep_enable(port->in);
+	if (ret) {
+		pr_err("%s: usb_ep_enable failed eptype:IN ep:%p",
+				__func__, port->in);
+		goto fail;
+	}
+
+	ret = usb_ep_enable(port->out);
+	if (ret) {
+		pr_err("%s: usb_ep_enable failed eptype:OUT ep:%p",
+				__func__, port->out);
+		usb_ep_disable(port->in);
+		goto fail;
+	}
+
+	atomic_set(&port->connected, 1);
+
+	spin_lock_irqsave(&port->tx_lock, flags);
+	port->to_host = 0;
+	port->rx_throttled_cnt = 0;
+	port->rx_unthrottled_cnt = 0;
+	port->unthrottled_pnd_skbs = 0;
+	spin_unlock_irqrestore(&port->tx_lock, flags);
+
+	spin_lock_irqsave(&port->rx_lock, flags);
+	port->to_modem = 0;
+	port->tomodem_drp_cnt = 0;
+	port->tx_throttled_cnt = 0;
+	port->tx_unthrottled_cnt = 0;
+	spin_unlock_irqrestore(&port->rx_lock, flags);
+
+	queue_work(port->wq, &port->connect_w);
+fail:
+	return ret;
+}
+
+#if defined(CONFIG_DEBUG_FS)
+#define DEBUG_BUF_SIZE 1024
+
+static unsigned int	record_timestamp;
+module_param(record_timestamp, uint, S_IRUGO | S_IWUSR);
+
+static struct timestamp_buf dbg_data = {
+	.idx = 0,
+	.lck = __RW_LOCK_UNLOCKED(lck)
+};
+
+/*get_timestamp - returns time of day in us */
+static unsigned int get_timestamp(void)
+{
+	struct timeval	tval;
+	unsigned int	stamp;
+
+	if (!record_timestamp)
+		return 0;
+
+	do_gettimeofday(&tval);
+	/* 2^32 = 4294967296. Limit to 4096s. */
+	stamp = tval.tv_sec & 0xFFF;
+	stamp = stamp * 1000000 + tval.tv_usec;
+	return stamp;
+}
+
+static void dbg_inc(unsigned *idx)
+{
+	*idx = (*idx + 1) & (DBG_DATA_MAX-1);
+}
+
+/**
+* dbg_timestamp - Stores timestamp values of a SKB life cycle
+*	to debug buffer
+* @event: "DL": Downlink Data
+* @skb: SKB used to store timestamp values to debug buffer
+*/
+static void dbg_timestamp(char *event, struct sk_buff * skb)
+{
+	unsigned long		flags;
+	struct timestamp_info	*info = (struct timestamp_info *)skb->cb;
+
+	if (!record_timestamp)
+		return;
+
+	write_lock_irqsave(&dbg_data.lck, flags);
+
+	scnprintf(dbg_data.buf[dbg_data.idx], DBG_DATA_MSG,
+		  "%p %u[%s] %u %u %u %u %u %u\n",
+		  skb, skb->len, event, info->created, info->rx_queued,
+		  info->rx_done, info->rx_done_sent, info->tx_queued,
+		  get_timestamp());
+
+	dbg_inc(&dbg_data.idx);
+
+	write_unlock_irqrestore(&dbg_data.lck, flags);
+}
+
+/* show_timestamp: displays the timestamp buffer */
+static ssize_t show_timestamp(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	unsigned long	flags;
+	unsigned	i;
+	unsigned	j = 0;
+	char		*buf;
+	int		ret = 0;
+
+	if (!record_timestamp)
+		return 0;
+
+	buf = kzalloc(sizeof(char) * 4 * DEBUG_BUF_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	read_lock_irqsave(&dbg_data.lck, flags);
+
+	i = dbg_data.idx;
+	for (dbg_inc(&i); i != dbg_data.idx; dbg_inc(&i)) {
+		if (!strnlen(dbg_data.buf[i], DBG_DATA_MSG))
+			continue;
+		j += scnprintf(buf + j, (4 * DEBUG_BUF_SIZE) - j,
+			       "%s\n", dbg_data.buf[i]);
+	}
+
+	read_unlock_irqrestore(&dbg_data.lck, flags);
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, j);
+
+	kfree(buf);
+
+	return ret;
+}
+
+const struct file_operations gdata_timestamp_ops = {
+	.read = show_timestamp,
+};
+
+static ssize_t ghsic_data_read_stats(struct file *file,
+	char __user *ubuf, size_t count, loff_t *ppos)
+{
+	struct gdata_port	*port;
+	struct platform_driver	*pdrv;
+	char			*buf;
+	unsigned long		flags;
+	int			ret;
+	int			i;
+	int			temp = 0;
+
+	buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	for (i = 0; i < no_data_ports; i++) {
+		port = gdata_ports[i].port;
+		if (!port)
+			continue;
+		pdrv = &gdata_ports[i].pdrv;
+
+		spin_lock_irqsave(&port->rx_lock, flags);
+		temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+				"\nName:           %s\n"
+				"#PORT:%d port#:   %p\n"
+				"data_ch_open:	   %d\n"
+				"data_ch_ready:    %d\n"
+				"\n******UL INFO*****\n\n"
+				"dpkts_to_modem:   %lu\n"
+				"tomodem_drp_cnt:  %u\n"
+				"rx_buf_len:       %u\n"
+				"tx thld cnt       %u\n"
+				"tx unthld cnt     %u\n"
+				"TX_THROTTLED      %d\n",
+				pdrv->driver.name,
+				i, port,
+				test_bit(CH_OPENED, &port->bridge_sts),
+				test_bit(CH_READY, &port->bridge_sts),
+				port->to_modem,
+				port->tomodem_drp_cnt,
+				port->rx_skb_q.qlen,
+				port->tx_throttled_cnt,
+				port->tx_unthrottled_cnt,
+				test_bit(TX_THROTTLED, &port->brdg.flags));
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+
+		spin_lock_irqsave(&port->tx_lock, flags);
+		temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+				"\n******DL INFO******\n\n"
+				"dpkts_to_usbhost: %lu\n"
+				"tx_buf_len:	   %u\n"
+				"rx thld cnt	   %u\n"
+				"rx unthld cnt	   %u\n"
+				"uthld pnd skbs    %u\n"
+				"RX_THROTTLED	   %d\n",
+				port->to_host,
+				port->tx_skb_q.qlen,
+				port->rx_throttled_cnt,
+				port->rx_unthrottled_cnt,
+				port->unthrottled_pnd_skbs,
+				test_bit(RX_THROTTLED, &port->brdg.flags));
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+
+	}
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+	kfree(buf);
+
+	return ret;
+}
+
+static ssize_t ghsic_data_reset_stats(struct file *file,
+	const char __user *buf, size_t count, loff_t *ppos)
+{
+	struct gdata_port	*port;
+	int			i;
+	unsigned long		flags;
+
+	for (i = 0; i < no_data_ports; i++) {
+		port = gdata_ports[i].port;
+		if (!port)
+			continue;
+
+		spin_lock_irqsave(&port->rx_lock, flags);
+		port->to_modem = 0;
+		port->tomodem_drp_cnt = 0;
+		port->tx_throttled_cnt = 0;
+		port->tx_unthrottled_cnt = 0;
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+
+		spin_lock_irqsave(&port->tx_lock, flags);
+		port->to_host = 0;
+		port->rx_throttled_cnt = 0;
+		port->rx_unthrottled_cnt = 0;
+		port->unthrottled_pnd_skbs = 0;
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+	}
+	return count;
+}
+
+const struct file_operations ghsic_stats_ops = {
+	.read = ghsic_data_read_stats,
+	.write = ghsic_data_reset_stats,
+};
+
+static struct dentry	*gdata_dent;
+static struct dentry	*gdata_dfile_stats;
+static struct dentry	*gdata_dfile_tstamp;
+
+static void ghsic_data_debugfs_init(void)
+{
+	gdata_dent = debugfs_create_dir("ghsic_data_xport", 0);
+	if (IS_ERR(gdata_dent))
+		return;
+
+	gdata_dfile_stats = debugfs_create_file("status", 0444, gdata_dent, 0,
+			&ghsic_stats_ops);
+	if (!gdata_dfile_stats || IS_ERR(gdata_dfile_stats)) {
+		debugfs_remove(gdata_dent);
+		return;
+	}
+
+	gdata_dfile_tstamp = debugfs_create_file("timestamp", 0644, gdata_dent,
+				0, &gdata_timestamp_ops);
+		if (!gdata_dfile_tstamp || IS_ERR(gdata_dfile_tstamp))
+			debugfs_remove(gdata_dent);
+}
+
+static void ghsic_data_debugfs_exit(void)
+{
+	debugfs_remove(gdata_dfile_stats);
+	debugfs_remove(gdata_dfile_tstamp);
+	debugfs_remove(gdata_dent);
+}
+
+#else
+static void ghsic_data_debugfs_init(void) { }
+static void ghsic_data_debugfs_exit(void) { }
+static void dbg_timestamp(char *event, struct sk_buff * skb)
+{
+	return;
+}
+static unsigned int get_timestamp(void)
+{
+	return 0;
+}
+
+#endif
+
+int ghsic_data_setup(unsigned num_ports, enum gadget_type gtype)
+{
+	int		first_port_id = no_data_ports;
+	int		total_num_ports = num_ports + no_data_ports;
+	int		ret = 0;
+	int		i;
+
+	if (!num_ports || total_num_ports > NUM_PORTS) {
+		pr_err("%s: Invalid num of ports count:%d\n",
+				__func__, num_ports);
+		return -EINVAL;
+	}
+	pr_debug("%s: count: %d\n", __func__, num_ports);
+
+	for (i = first_port_id; i < (num_ports + first_port_id); i++) {
+
+		/*probe can be called while port_alloc,so update no_data_ports*/
+		no_data_ports++;
+		ret = ghsic_data_port_alloc(i, gtype);
+		if (ret) {
+			no_data_ports--;
+			pr_err("%s: Unable to alloc port:%d\n", __func__, i);
+			goto free_ports;
+		}
+	}
+
+	/*return the starting index*/
+	return first_port_id;
+
+free_ports:
+	for (i = first_port_id; i < no_data_ports; i++)
+		ghsic_data_port_free(i);
+		no_data_ports = first_port_id;
+
+	return ret;
+}
+
+static int __init ghsic_data_init(void)
+{
+	ghsic_data_debugfs_init();
+
+	return 0;
+}
+module_init(ghsic_data_init);
+
+static void __exit ghsic_data_exit(void)
+{
+	ghsic_data_debugfs_exit();
+}
+module_exit(ghsic_data_exit);
+MODULE_DESCRIPTION("hsic data xport driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/gadget/u_data_hsuart.c b/drivers/usb/gadget/u_data_hsuart.c
new file mode 100644
index 0000000..b2c57c4
--- /dev/null
+++ b/drivers/usb/gadget/u_data_hsuart.c
@@ -0,0 +1,1142 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/termios.h>
+#include <linux/netdevice.h>
+#include <linux/debugfs.h>
+#include <linux/bitops.h>
+#include <linux/smux.h>
+
+#include <mach/usb_gadget_xport.h>
+
+static unsigned int num_data_ports;
+
+static const char *ghsuart_data_names[] = {
+	"SMUX_DUN_DATA_HSUART",
+	"SMUX_RMNET_DATA_HSUART"
+};
+
+#define DATA_BRIDGE_NAME_MAX_LEN		20
+
+#define GHSUART_DATA_RMNET_RX_Q_SIZE		10
+#define GHSUART_DATA_RMNET_TX_Q_SIZE		20
+#define GHSUART_DATA_SERIAL_RX_Q_SIZE		5
+#define GHSUART_DATA_SERIAL_TX_Q_SIZE		5
+#define GHSUART_DATA_RX_REQ_SIZE		2048
+#define GHSUART_DATA_TX_INTR_THRESHOLD		1
+
+/* from cdc-acm.h */
+#define ACM_CTRL_RTS		(1 << 1)	/* unused with full duplex */
+#define ACM_CTRL_DTR		(1 << 0)	/* host is ready for data r/w */
+#define ACM_CTRL_OVERRUN	(1 << 6)
+#define ACM_CTRL_PARITY		(1 << 5)
+#define ACM_CTRL_FRAMING	(1 << 4)
+#define ACM_CTRL_RI		(1 << 3)
+#define ACM_CTRL_BRK		(1 << 2)
+#define ACM_CTRL_DSR		(1 << 1)
+#define ACM_CTRL_DCD		(1 << 0)
+
+static unsigned int ghsuart_data_rmnet_tx_q_size = GHSUART_DATA_RMNET_TX_Q_SIZE;
+module_param(ghsuart_data_rmnet_tx_q_size, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsuart_data_rmnet_rx_q_size = GHSUART_DATA_RMNET_RX_Q_SIZE;
+module_param(ghsuart_data_rmnet_rx_q_size, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsuart_data_serial_tx_q_size =
+					GHSUART_DATA_SERIAL_TX_Q_SIZE;
+module_param(ghsuart_data_serial_tx_q_size, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsuart_data_serial_rx_q_size =
+				GHSUART_DATA_SERIAL_RX_Q_SIZE;
+module_param(ghsuart_data_serial_rx_q_size, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsuart_data_rx_req_size = GHSUART_DATA_RX_REQ_SIZE;
+module_param(ghsuart_data_rx_req_size, uint, S_IRUGO | S_IWUSR);
+
+unsigned int ghsuart_data_tx_intr_thld = GHSUART_DATA_TX_INTR_THRESHOLD;
+module_param(ghsuart_data_tx_intr_thld, uint, S_IRUGO | S_IWUSR);
+
+#define CH_OPENED 0
+#define CH_READY 1
+
+struct ghsuart_data_port {
+	/* port */
+	unsigned		port_num;
+
+	/* gadget */
+	atomic_t		connected;
+	struct usb_ep		*in;
+	struct usb_ep		*out;
+
+	enum gadget_type	gtype;
+	spinlock_t		port_lock;
+	void *port_usb;
+
+	/* data transfer queues */
+	unsigned int		tx_q_size;
+	struct list_head	tx_idle;
+	struct sk_buff_head	tx_skb_q;
+	spinlock_t		tx_lock;
+
+	unsigned int		rx_q_size;
+	struct list_head	rx_idle;
+	struct sk_buff_head	rx_skb_q;
+	spinlock_t		rx_lock;
+
+	/* work */
+	struct workqueue_struct	*wq;
+	struct work_struct	connect_w;
+	struct work_struct	disconnect_w;
+	struct work_struct	write_tomdm_w;
+	struct work_struct	write_tohost_w;
+	void *ctx;
+	unsigned int ch_id;
+	/* flow control bits */
+	unsigned long flags;
+	/* channel status */
+	unsigned long		channel_sts;
+
+	unsigned int		n_tx_req_queued;
+
+	/* control bits */
+	unsigned		cbits_tomodem;
+	unsigned		cbits_tohost;
+
+	/* counters */
+	unsigned long		to_modem;
+	unsigned long		to_host;
+	unsigned int		tomodem_drp_cnt;
+};
+
+static struct {
+	struct ghsuart_data_port	*port;
+	struct platform_driver	pdrv;
+} ghsuart_data_ports[NUM_HSUART_PORTS];
+
+static void ghsuart_data_start_rx(struct ghsuart_data_port *port);
+
+static void ghsuart_data_free_requests(struct usb_ep *ep,
+				 struct list_head *head)
+{
+	struct usb_request	*req;
+
+	while (!list_empty(head)) {
+		req = list_entry(head->next, struct usb_request, list);
+		list_del(&req->list);
+		usb_ep_free_request(ep, req);
+	}
+}
+
+static int ghsuart_data_alloc_requests(struct usb_ep *ep,
+		struct list_head *head,
+		int num,
+		void (*cb)(struct usb_ep *ep, struct usb_request *),
+		gfp_t flags)
+{
+	int			i;
+	struct usb_request	*req;
+
+	pr_debug("%s: ep:%s head:%p num:%d cb:%p", __func__,
+			ep->name, head, num, cb);
+
+	for (i = 0; i < num; i++) {
+		req = usb_ep_alloc_request(ep, flags);
+		if (!req) {
+			pr_err("%s: req allocated:%d\n", __func__, i);
+			return list_empty(head) ? -ENOMEM : 0;
+		}
+		req->complete = cb;
+		list_add(&req->list, head);
+	}
+
+	return 0;
+}
+
+static void ghsuart_data_write_tohost(struct work_struct *w)
+{
+	unsigned long		flags;
+	struct sk_buff		*skb;
+	int			ret;
+	struct usb_request	*req;
+	struct usb_ep		*ep;
+	struct ghsuart_data_port	*port;
+
+	port = container_of(w, struct ghsuart_data_port, write_tohost_w);
+
+	if (!port || !atomic_read(&port->connected))
+		return;
+
+	spin_lock_irqsave(&port->tx_lock, flags);
+	ep = port->in;
+	if (!ep) {
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+		return;
+	}
+
+	while (!list_empty(&port->tx_idle)) {
+		skb = __skb_dequeue(&port->tx_skb_q);
+		if (!skb)
+			break;
+
+		req = list_first_entry(&port->tx_idle, struct usb_request,
+				list);
+		req->context = skb;
+		req->buf = skb->data;
+		req->length = skb->len;
+
+		port->n_tx_req_queued++;
+		if (port->n_tx_req_queued == ghsuart_data_tx_intr_thld) {
+			req->no_interrupt = 0;
+			port->n_tx_req_queued = 0;
+		} else {
+			req->no_interrupt = 1;
+		}
+
+		list_del(&req->list);
+
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+		ret = usb_ep_queue(ep, req, GFP_KERNEL);
+		spin_lock_irqsave(&port->tx_lock, flags);
+		if (ret) {
+			pr_err("%s: usb epIn failed\n", __func__);
+			list_add(&req->list, &port->tx_idle);
+			dev_kfree_skb_any(skb);
+			break;
+		}
+		port->to_host++;
+	}
+	spin_unlock_irqrestore(&port->tx_lock, flags);
+}
+
+static void ghsuart_data_write_tomdm(struct work_struct *w)
+{
+	struct ghsuart_data_port	*port;
+	struct sk_buff		*skb;
+	unsigned long		flags;
+	int			ret;
+
+	port = container_of(w, struct ghsuart_data_port, write_tomdm_w);
+
+	if (!port || !atomic_read(&port->connected))
+		return;
+
+	spin_lock_irqsave(&port->rx_lock, flags);
+	if (test_bit(TX_THROTTLED, &port->flags)) {
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+		return;
+	}
+
+	while ((skb = __skb_dequeue(&port->rx_skb_q))) {
+		pr_debug("%s: port:%p tom:%lu pno:%d\n", __func__,
+				port, port->to_modem, port->port_num);
+
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+		ret = msm_smux_write(port->ch_id, skb, skb->data, skb->len);
+		spin_lock_irqsave(&port->rx_lock, flags);
+		if (ret < 0) {
+			if (ret == -EAGAIN) {
+				/*flow control*/
+				set_bit(TX_THROTTLED, &port->flags);
+				__skb_queue_head(&port->rx_skb_q, skb);
+				break;
+			}
+			pr_err_ratelimited("%s: write error:%d\n",
+					__func__, ret);
+			port->tomodem_drp_cnt++;
+			dev_kfree_skb_any(skb);
+			break;
+		}
+		port->to_modem++;
+	}
+	spin_unlock_irqrestore(&port->rx_lock, flags);
+	ghsuart_data_start_rx(port);
+}
+
+static void ghsuart_data_epin_complete(struct usb_ep *ep,
+				struct usb_request *req)
+{
+	struct ghsuart_data_port	*port = ep->driver_data;
+	struct sk_buff		*skb = req->context;
+	int			status = req->status;
+
+	switch (status) {
+	case 0:
+		/* successful completion */
+		break;
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		dev_kfree_skb_any(skb);
+		req->buf = 0;
+		usb_ep_free_request(ep, req);
+		return;
+	default:
+		pr_err("%s: data tx ep error %d\n", __func__, status);
+		break;
+	}
+
+	dev_kfree_skb_any(skb);
+
+	spin_lock(&port->tx_lock);
+	list_add_tail(&req->list, &port->tx_idle);
+	spin_unlock(&port->tx_lock);
+
+	queue_work(port->wq, &port->write_tohost_w);
+}
+
+static void
+ghsuart_data_epout_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct ghsuart_data_port	*port = ep->driver_data;
+	struct sk_buff		*skb = req->context;
+	int			status = req->status;
+	int			queue = 0;
+
+	switch (status) {
+	case 0:
+		skb_put(skb, req->actual);
+		queue = 1;
+		break;
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* cable disconnection */
+		dev_kfree_skb_any(skb);
+		req->buf = 0;
+		usb_ep_free_request(ep, req);
+		return;
+	default:
+		pr_err_ratelimited("%s: %s response error %d, %d/%d\n",
+					__func__, ep->name, status,
+				req->actual, req->length);
+		dev_kfree_skb_any(skb);
+		list_add_tail(&req->list, &port->rx_idle);
+		return;
+	}
+
+	spin_lock(&port->rx_lock);
+	if (queue) {
+		__skb_queue_tail(&port->rx_skb_q, skb);
+		list_add_tail(&req->list, &port->rx_idle);
+		queue_work(port->wq, &port->write_tomdm_w);
+	}
+	spin_unlock(&port->rx_lock);
+}
+
+static void ghsuart_data_start_rx(struct ghsuart_data_port *port)
+{
+	struct usb_request	*req;
+	struct usb_ep		*ep;
+	unsigned long		flags;
+	int			ret;
+	struct sk_buff		*skb;
+
+	pr_debug("%s: port:%p\n", __func__, port);
+	if (!port)
+		return;
+
+	spin_lock_irqsave(&port->rx_lock, flags);
+	ep = port->out;
+	if (!ep) {
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+		return;
+	}
+
+	if (test_bit(TX_THROTTLED, &port->flags)) {
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+		return;
+	}
+
+	while (atomic_read(&port->connected) && !list_empty(&port->rx_idle)) {
+
+		req = list_first_entry(&port->rx_idle,
+					struct usb_request, list);
+
+		skb = alloc_skb(ghsuart_data_rx_req_size, GFP_ATOMIC);
+		if (!skb)
+			break;
+		list_del(&req->list);
+		req->buf = skb->data;
+		req->length = ghsuart_data_rx_req_size;
+		req->context = skb;
+
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+		ret = usb_ep_queue(ep, req, GFP_KERNEL);
+		spin_lock_irqsave(&port->rx_lock, flags);
+		if (ret) {
+			dev_kfree_skb_any(skb);
+
+			pr_err_ratelimited("%s: rx queue failed\n", __func__);
+
+			if (atomic_read(&port->connected))
+				list_add(&req->list, &port->rx_idle);
+			else
+				usb_ep_free_request(ep, req);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&port->rx_lock, flags);
+}
+
+static void ghsuart_data_start_io(struct ghsuart_data_port *port)
+{
+	unsigned long	flags;
+	struct usb_ep	*ep;
+	int		ret;
+
+	pr_debug("%s: port:%p\n", __func__, port);
+
+	if (!port)
+		return;
+
+	spin_lock_irqsave(&port->rx_lock, flags);
+	ep = port->out;
+	if (!ep) {
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+		return;
+	}
+
+	ret = ghsuart_data_alloc_requests(ep, &port->rx_idle,
+		port->rx_q_size, ghsuart_data_epout_complete, GFP_ATOMIC);
+	if (ret) {
+		pr_err("%s: rx req allocation failed\n", __func__);
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+		return;
+	}
+	spin_unlock_irqrestore(&port->rx_lock, flags);
+
+	spin_lock_irqsave(&port->tx_lock, flags);
+	ep = port->in;
+	if (!ep) {
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+		return;
+	}
+
+	ret = ghsuart_data_alloc_requests(ep, &port->tx_idle,
+		port->tx_q_size, ghsuart_data_epin_complete, GFP_ATOMIC);
+	if (ret) {
+		pr_err("%s: tx req allocation failed\n", __func__);
+		ghsuart_data_free_requests(ep, &port->rx_idle);
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+		return;
+	}
+	spin_unlock_irqrestore(&port->tx_lock, flags);
+
+	/* queue out requests */
+	ghsuart_data_start_rx(port);
+}
+
+static void ghsuart_dunctrl_status(void *ctxt, unsigned int ctrl_bits)
+{
+	struct ghsuart_data_port  *port = ctxt;
+	struct gserial          *gser;
+	unsigned long	flags;
+
+	pr_debug("%s - input control lines: dcd%c dsr%c break%c "
+	"ring%c framing%c parity%c overrun%c\n", __func__,
+	ctrl_bits & ACM_CTRL_DCD ? '+' : '-',
+	ctrl_bits & ACM_CTRL_DSR ? '+' : '-',
+	ctrl_bits & ACM_CTRL_BRK ? '+' : '-',
+	ctrl_bits & ACM_CTRL_RI  ? '+' : '-',
+	ctrl_bits & ACM_CTRL_FRAMING ? '+' : '-',
+	ctrl_bits & ACM_CTRL_PARITY ? '+' : '-',
+	ctrl_bits & ACM_CTRL_OVERRUN ? '+' : '-');
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	port->cbits_tohost = ctrl_bits;
+	gser = port->port_usb;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+	if (gser && gser->send_modem_ctrl_bits)
+		gser->send_modem_ctrl_bits(gser, ctrl_bits);
+}
+
+const char *event_string(int event_type)
+{
+	switch (event_type) {
+	case SMUX_CONNECTED:
+		return "SMUX_CONNECTED";
+	case SMUX_DISCONNECTED:
+		return "SMUX_DISCONNECTED";
+	case SMUX_READ_DONE:
+		return "SMUX_READ_DONE";
+	case SMUX_READ_FAIL:
+		return "SMUX_READ_FAIL";
+	case SMUX_WRITE_DONE:
+		return "SMUX_WRITE_DONE";
+	case SMUX_WRITE_FAIL:
+		return "SMUX_WRITE_FAIL";
+	case SMUX_HIGH_WM_HIT:
+		return "SMUX_HIGH_WM_HIT";
+	case SMUX_LOW_WM_HIT:
+		return "SMUX_LOW_WM_HIT";
+	case SMUX_TIOCM_UPDATE:
+		return "SMUX_TIOCM_UPDATE";
+	default:
+		return "UNDEFINED";
+	}
+}
+
+static void ghsuart_notify_event(void *priv, int event_type,
+				const void *metadata)
+{
+	struct ghsuart_data_port	*port = priv;
+	struct smux_meta_write *meta_write =
+				(struct smux_meta_write *) metadata;
+	struct smux_meta_read *meta_read =
+				(struct smux_meta_read *) metadata;
+	struct sk_buff		*skb;
+	unsigned long		flags;
+	unsigned int		cbits;
+	struct gserial		*gser;
+
+	pr_debug("%s: event type: %s ", __func__, event_string(event_type));
+	switch (event_type) {
+	case SMUX_CONNECTED:
+		set_bit(CH_OPENED, &port->channel_sts);
+		if (port->gtype == USB_GADGET_SERIAL) {
+			cbits = msm_smux_tiocm_get(port->ch_id);
+			if (cbits & ACM_CTRL_DCD) {
+				gser = port->port_usb;
+				if (gser && gser->connect)
+					gser->connect(gser);
+			}
+		}
+		ghsuart_data_start_io(port);
+		break;
+	case SMUX_DISCONNECTED:
+		clear_bit(CH_OPENED, &port->channel_sts);
+		break;
+	case SMUX_READ_DONE:
+		skb = meta_read->pkt_priv;
+		skb->data = meta_read->buffer;
+		skb->len = meta_read->len;
+		spin_lock_irqsave(&port->tx_lock, flags);
+		__skb_queue_tail(&port->tx_skb_q, skb);
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+		queue_work(port->wq, &port->write_tohost_w);
+		break;
+	case SMUX_WRITE_DONE:
+		skb = meta_write->pkt_priv;
+		skb->data = meta_write->buffer;
+		dev_kfree_skb_any(skb);
+		queue_work(port->wq, &port->write_tomdm_w);
+		break;
+	case SMUX_READ_FAIL:
+		skb = meta_read->pkt_priv;
+		skb->data = meta_read->buffer;
+		dev_kfree_skb_any(skb);
+		break;
+	case SMUX_WRITE_FAIL:
+		skb = meta_write->pkt_priv;
+		skb->data = meta_write->buffer;
+		dev_kfree_skb_any(skb);
+		break;
+	case SMUX_HIGH_WM_HIT:
+		spin_lock_irqsave(&port->rx_lock, flags);
+		set_bit(TX_THROTTLED, &port->flags);
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+	case SMUX_LOW_WM_HIT:
+		spin_lock_irqsave(&port->rx_lock, flags);
+		clear_bit(TX_THROTTLED, &port->flags);
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+		queue_work(port->wq, &port->write_tomdm_w);
+		break;
+	case SMUX_TIOCM_UPDATE:
+		if (port->gtype == USB_GADGET_SERIAL) {
+			cbits = msm_smux_tiocm_get(port->ch_id);
+			ghsuart_dunctrl_status(port, cbits);
+		}
+		break;
+	default:
+		pr_err("%s:wrong event recieved\n", __func__);
+	}
+}
+
+static int ghsuart_get_rx_buffer(void *priv, void **pkt_priv,
+			void **buffer, int size)
+{
+	struct sk_buff		*skb;
+
+	skb = alloc_skb(size, GFP_ATOMIC);
+	if (!skb)
+		return -ENOMEM;
+	*pkt_priv = skb;
+	*buffer = skb->data;
+
+	return 0;
+}
+
+static void ghsuart_data_connect_w(struct work_struct *w)
+{
+	struct ghsuart_data_port	*port =
+		container_of(w, struct ghsuart_data_port, connect_w);
+	int			ret;
+
+	if (!port || !atomic_read(&port->connected) ||
+		!test_bit(CH_READY, &port->channel_sts))
+		return;
+
+	pr_debug("%s: port:%p\n", __func__, port);
+
+	ret = msm_smux_open(port->ch_id, port, &ghsuart_notify_event,
+				&ghsuart_get_rx_buffer);
+	if (ret) {
+		pr_err("%s: unable to open smux ch:%d err:%d\n",
+				__func__, port->ch_id, ret);
+		return;
+	}
+}
+
+static void ghsuart_data_disconnect_w(struct work_struct *w)
+{
+	struct ghsuart_data_port	*port =
+		container_of(w, struct ghsuart_data_port, disconnect_w);
+
+	if (!test_bit(CH_OPENED, &port->channel_sts))
+		return;
+
+	msm_smux_close(port->ch_id);
+	clear_bit(CH_OPENED, &port->channel_sts);
+}
+
+static void ghsuart_data_free_buffers(struct ghsuart_data_port *port)
+{
+	struct sk_buff	*skb;
+	unsigned long	flags;
+
+	if (!port)
+		return;
+
+	spin_lock_irqsave(&port->tx_lock, flags);
+	if (!port->in) {
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+		return;
+	}
+
+	ghsuart_data_free_requests(port->in, &port->tx_idle);
+
+	while ((skb = __skb_dequeue(&port->tx_skb_q)))
+		dev_kfree_skb_any(skb);
+	spin_unlock_irqrestore(&port->tx_lock, flags);
+
+	spin_lock_irqsave(&port->rx_lock, flags);
+	if (!port->out) {
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+		return;
+	}
+
+	ghsuart_data_free_requests(port->out, &port->rx_idle);
+
+	while ((skb = __skb_dequeue(&port->rx_skb_q)))
+		dev_kfree_skb_any(skb);
+	spin_unlock_irqrestore(&port->rx_lock, flags);
+}
+
+static int ghsuart_data_probe(struct platform_device *pdev)
+{
+	struct ghsuart_data_port *port;
+
+	pr_debug("%s: name:%s num_data_ports= %d\n",
+		__func__, pdev->name, num_data_ports);
+
+	if (pdev->id >= num_data_ports) {
+		pr_err("%s: invalid port: %d\n", __func__, pdev->id);
+		return -EINVAL;
+	}
+
+	port = ghsuart_data_ports[pdev->id].port;
+	set_bit(CH_READY, &port->channel_sts);
+
+	/* if usb is online, try opening bridge */
+	if (atomic_read(&port->connected))
+		queue_work(port->wq, &port->connect_w);
+
+	return 0;
+}
+
+/* mdm disconnect */
+static int ghsuart_data_remove(struct platform_device *pdev)
+{
+	struct ghsuart_data_port *port;
+	struct usb_ep	*ep_in;
+	struct usb_ep	*ep_out;
+	int ret;
+	struct gserial		*gser = NULL;
+	unsigned long	flags;
+
+	pr_debug("%s: name:%s\n", __func__, pdev->name);
+
+	if (pdev->id >= num_data_ports) {
+		pr_err("%s: invalid port: %d\n", __func__, pdev->id);
+		return -EINVAL;
+	}
+
+	port = ghsuart_data_ports[pdev->id].port;
+
+	ep_in = port->in;
+	if (ep_in)
+		usb_ep_fifo_flush(ep_in);
+
+	ep_out = port->out;
+	if (ep_out)
+		usb_ep_fifo_flush(ep_out);
+
+	ghsuart_data_free_buffers(port);
+
+	if (port->gtype == USB_GADGET_SERIAL) {
+		spin_lock_irqsave(&port->port_lock, flags);
+		gser = port->port_usb;
+		port->cbits_tohost = 0;
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		if (gser && gser->disconnect)
+			gser->disconnect(gser);
+	}
+
+	ret = msm_smux_close(port->ch_id);
+	if (ret < 0)
+		pr_err("%s:Unable to close smux channel: %d\n",
+				__func__, port->ch_id);
+
+	clear_bit(CH_READY, &port->channel_sts);
+	clear_bit(CH_OPENED, &port->channel_sts);
+
+	return 0;
+}
+
+static void ghsuart_data_port_free(int portno)
+{
+	struct ghsuart_data_port	*port = ghsuart_data_ports[portno].port;
+	struct platform_driver	*pdrv = &ghsuart_data_ports[portno].pdrv;
+
+	destroy_workqueue(port->wq);
+	kfree(port);
+
+	if (pdrv)
+		platform_driver_unregister(pdrv);
+}
+
+static void
+ghsuart_send_controlbits_tomodem(void *gptr, u8 portno, int cbits)
+{
+	struct ghsuart_data_port	*port;
+
+	if (portno >= num_ctrl_ports || !gptr) {
+		pr_err("%s: Invalid portno#%d\n", __func__, portno);
+		return;
+	}
+
+	port = ghsuart_data_ports[portno].port;
+	if (!port) {
+		pr_err("%s: port is null\n", __func__);
+		return;
+	}
+
+	if (cbits == port->cbits_tomodem)
+		return;
+
+	port->cbits_tomodem = cbits;
+
+	if (!test_bit(CH_OPENED, &port->channel_sts))
+		return;
+
+	/* if DTR is high, update latest modem info to Host */
+	if (port->cbits_tomodem & ACM_CTRL_DTR) {
+		unsigned int i;
+
+		i = msm_smux_tiocm_get(port->ch_id);
+		ghsuart_dunctrl_status(port, i);
+	}
+
+	pr_debug("%s: ctrl_tomodem:%d\n", __func__, cbits);
+	/* Send the control bits to the Modem */
+	msm_smux_tiocm_set(port->ch_id, cbits, ~cbits);
+}
+
+static int ghsuart_data_port_alloc(unsigned port_num, enum gadget_type gtype)
+{
+	struct ghsuart_data_port	*port;
+	struct platform_driver	*pdrv;
+
+	port = kzalloc(sizeof(struct ghsuart_data_port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	port->wq = create_singlethread_workqueue(ghsuart_data_names[port_num]);
+	if (!port->wq) {
+		pr_err("%s: Unable to create workqueue:%s\n",
+			__func__, ghsuart_data_names[port_num]);
+		kfree(port);
+		return -ENOMEM;
+	}
+	port->port_num = port_num;
+
+	/* port initialization */
+	spin_lock_init(&port->port_lock);
+	spin_lock_init(&port->rx_lock);
+	spin_lock_init(&port->tx_lock);
+
+	INIT_WORK(&port->connect_w, ghsuart_data_connect_w);
+	INIT_WORK(&port->disconnect_w, ghsuart_data_disconnect_w);
+	INIT_WORK(&port->write_tohost_w, ghsuart_data_write_tohost);
+	INIT_WORK(&port->write_tomdm_w, ghsuart_data_write_tomdm);
+
+	INIT_LIST_HEAD(&port->tx_idle);
+	INIT_LIST_HEAD(&port->rx_idle);
+
+	skb_queue_head_init(&port->tx_skb_q);
+	skb_queue_head_init(&port->rx_skb_q);
+
+	port->gtype = gtype;
+	if (port->gtype == USB_GADGET_SERIAL)
+		port->ch_id = SMUX_USB_DUN_0;
+	else
+		port->ch_id = SMUX_USB_RMNET_DATA_0;
+	port->ctx = port;
+	ghsuart_data_ports[port_num].port = port;
+
+	pdrv = &ghsuart_data_ports[port_num].pdrv;
+	pdrv->probe = ghsuart_data_probe;
+	pdrv->remove = ghsuart_data_remove;
+	pdrv->driver.name = ghsuart_data_names[port_num];
+	pdrv->driver.owner = THIS_MODULE;
+
+	platform_driver_register(pdrv);
+
+	pr_debug("%s: port:%p portno:%d\n", __func__, port, port_num);
+
+	return 0;
+}
+
+void ghsuart_data_disconnect(void *gptr, int port_num)
+{
+	struct ghsuart_data_port	*port;
+	unsigned long		flags;
+	struct gserial		*gser = NULL;
+
+	pr_debug("%s: port#%d\n", __func__, port_num);
+
+	port = ghsuart_data_ports[port_num].port;
+
+	if (port_num > num_data_ports) {
+		pr_err("%s: invalid portno#%d\n", __func__, port_num);
+		return;
+	}
+
+	if (!gptr || !port) {
+		pr_err("%s: port is null\n", __func__);
+		return;
+	}
+
+	ghsuart_data_free_buffers(port);
+
+	/* disable endpoints */
+	if (port->in)
+		usb_ep_disable(port->in);
+
+	if (port->out)
+		usb_ep_disable(port->out);
+
+	atomic_set(&port->connected, 0);
+
+	if (port->gtype == USB_GADGET_SERIAL) {
+		gser = gptr;
+		spin_lock_irqsave(&port->port_lock, flags);
+		gser->notify_modem = 0;
+		port->cbits_tomodem = 0;
+		port->port_usb = 0;
+		spin_unlock_irqrestore(&port->port_lock, flags);
+	}
+
+	spin_lock_irqsave(&port->tx_lock, flags);
+	port->in = NULL;
+	port->n_tx_req_queued = 0;
+	clear_bit(RX_THROTTLED, &port->flags);
+	spin_unlock_irqrestore(&port->tx_lock, flags);
+
+	spin_lock_irqsave(&port->rx_lock, flags);
+	port->out = NULL;
+	clear_bit(TX_THROTTLED, &port->flags);
+	spin_unlock_irqrestore(&port->rx_lock, flags);
+
+	queue_work(port->wq, &port->disconnect_w);
+}
+
+int ghsuart_data_connect(void *gptr, int port_num)
+{
+	struct ghsuart_data_port		*port;
+	struct gserial			*gser;
+	struct grmnet			*gr;
+	unsigned long			flags;
+	int				ret = 0;
+
+	pr_debug("%s: port#%d\n", __func__, port_num);
+
+	port = ghsuart_data_ports[port_num].port;
+
+	if (port_num > num_data_ports) {
+		pr_err("%s: invalid portno#%d\n", __func__, port_num);
+		return -ENODEV;
+	}
+
+	if (!gptr || !port) {
+		pr_err("%s: port is null\n", __func__);
+		return -ENODEV;
+	}
+
+	if (port->gtype == USB_GADGET_SERIAL) {
+		gser = gptr;
+
+		spin_lock_irqsave(&port->tx_lock, flags);
+		port->in = gser->in;
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+
+		spin_lock_irqsave(&port->rx_lock, flags);
+		port->out = gser->out;
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+
+
+		port->tx_q_size = ghsuart_data_serial_tx_q_size;
+		port->rx_q_size = ghsuart_data_serial_rx_q_size;
+		gser->in->driver_data = port;
+		gser->out->driver_data = port;
+
+		spin_lock_irqsave(&port->port_lock, flags);
+		gser->notify_modem = ghsuart_send_controlbits_tomodem;
+		port->port_usb = gptr;
+		spin_unlock_irqrestore(&port->port_lock, flags);
+	} else {
+		gr = gptr;
+
+		spin_lock_irqsave(&port->tx_lock, flags);
+		port->in = gr->in;
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+
+		spin_lock_irqsave(&port->rx_lock, flags);
+		port->out = gr->out;
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+
+		port->tx_q_size = ghsuart_data_rmnet_tx_q_size;
+		port->rx_q_size = ghsuart_data_rmnet_rx_q_size;
+		gr->in->driver_data = port;
+		gr->out->driver_data = port;
+	}
+
+	ret = usb_ep_enable(port->in);
+	if (ret) {
+		pr_err("%s: usb_ep_enable failed eptype:IN ep:%p",
+				__func__, port->in);
+		goto fail;
+	}
+
+	ret = usb_ep_enable(port->out);
+	if (ret) {
+		pr_err("%s: usb_ep_enable failed eptype:OUT ep:%p",
+				__func__, port->out);
+		usb_ep_disable(port->in);
+		goto fail;
+	}
+
+	atomic_set(&port->connected, 1);
+
+	spin_lock_irqsave(&port->tx_lock, flags);
+	port->to_host = 0;
+	spin_unlock_irqrestore(&port->tx_lock, flags);
+
+	spin_lock_irqsave(&port->rx_lock, flags);
+	port->to_modem = 0;
+	port->tomodem_drp_cnt = 0;
+	spin_unlock_irqrestore(&port->rx_lock, flags);
+
+	queue_work(port->wq, &port->connect_w);
+fail:
+	return ret;
+}
+
+#define DEBUG_BUF_SIZE 1024
+static ssize_t ghsuart_data_read_stats(struct file *file,
+	char __user *ubuf, size_t count, loff_t *ppos)
+{
+	struct ghsuart_data_port	*port;
+	struct platform_driver	*pdrv;
+	char			*buf;
+	unsigned long		flags;
+	int			ret;
+	int			i;
+	int			temp = 0;
+
+	buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	for (i = 0; i < num_data_ports; i++) {
+		port = ghsuart_data_ports[i].port;
+		if (!port)
+			continue;
+		pdrv = &ghsuart_data_ports[i].pdrv;
+
+		spin_lock_irqsave(&port->rx_lock, flags);
+		temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+				"\nName:           %s\n"
+				"#PORT:%d port#:   %p\n"
+				"data_ch_open:	   %d\n"
+				"data_ch_ready:    %d\n"
+				"\n******UL INFO*****\n\n"
+				"dpkts_to_modem:   %lu\n"
+				"tomodem_drp_cnt:  %u\n"
+				"rx_buf_len:       %u\n"
+				"TX_THROTTLED      %d\n",
+				pdrv->driver.name,
+				i, port,
+				test_bit(CH_OPENED, &port->channel_sts),
+				test_bit(CH_READY, &port->channel_sts),
+				port->to_modem,
+				port->tomodem_drp_cnt,
+				port->rx_skb_q.qlen,
+				test_bit(TX_THROTTLED, &port->flags));
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+
+		spin_lock_irqsave(&port->tx_lock, flags);
+		temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+				"\n******DL INFO******\n\n"
+				"dpkts_to_usbhost: %lu\n"
+				"tx_buf_len:	   %u\n"
+				"RX_THROTTLED	   %d\n",
+				port->to_host,
+				port->tx_skb_q.qlen,
+				test_bit(RX_THROTTLED, &port->flags));
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+
+	}
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+	kfree(buf);
+
+	return ret;
+}
+
+static ssize_t ghsuart_data_reset_stats(struct file *file,
+	const char __user *buf, size_t count, loff_t *ppos)
+{
+	struct ghsuart_data_port	*port;
+	int			i;
+	unsigned long		flags;
+
+	for (i = 0; i < num_data_ports; i++) {
+		port = ghsuart_data_ports[i].port;
+		if (!port)
+			continue;
+
+		spin_lock_irqsave(&port->rx_lock, flags);
+		port->to_modem = 0;
+		port->tomodem_drp_cnt = 0;
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+
+		spin_lock_irqsave(&port->tx_lock, flags);
+		port->to_host = 0;
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+	}
+	return count;
+}
+
+const struct file_operations ghsuart_data_stats_ops = {
+	.read = ghsuart_data_read_stats,
+	.write = ghsuart_data_reset_stats,
+};
+
+static struct dentry	*ghsuart_data_dent;
+static int ghsuart_data_debugfs_init(void)
+{
+	struct dentry	 *ghsuart_data_dfile;
+
+	ghsuart_data_dent = debugfs_create_dir("ghsic_data_xport", 0);
+	if (!ghsuart_data_dent || IS_ERR(ghsuart_data_dent))
+		return -ENODEV;
+
+	ghsuart_data_dfile = debugfs_create_file("status", S_IRUGO | S_IWUSR,
+				 ghsuart_data_dent, 0, &ghsuart_data_stats_ops);
+	if (!ghsuart_data_dfile || IS_ERR(ghsuart_data_dfile)) {
+		debugfs_remove(ghsuart_data_dent);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void ghsuart_data_debugfs_exit(void)
+{
+	debugfs_remove_recursive(ghsuart_data_dent);
+}
+
+int ghsuart_data_setup(unsigned num_ports, enum gadget_type gtype)
+{
+	int		first_port_id = num_data_ports;
+	int		total_num_ports = num_ports + num_data_ports;
+	int		ret = 0;
+	int		i;
+
+	if (!num_ports || total_num_ports > NUM_PORTS) {
+		pr_err("%s: Invalid num of ports count:%d\n",
+				__func__, num_ports);
+		return -EINVAL;
+	}
+	pr_debug("%s: count: %d\n", __func__, num_ports);
+
+	for (i = first_port_id; i < total_num_ports; i++) {
+
+		/*probe can be called while port_alloc,so update no_data_ports*/
+		num_data_ports++;
+		ret = ghsuart_data_port_alloc(i, gtype);
+		if (ret) {
+			num_data_ports--;
+			pr_err("%s: Unable to alloc port:%d\n", __func__, i);
+			goto free_ports;
+		}
+	}
+
+	/*return the starting index*/
+	return first_port_id;
+
+free_ports:
+	for (i = first_port_id; i < num_data_ports; i++)
+		ghsuart_data_port_free(i);
+		num_data_ports = first_port_id;
+
+	return ret;
+}
+
+static int __init ghsuart_data_init(void)
+{
+	int ret;
+
+	ret = ghsuart_data_debugfs_init();
+	if (ret) {
+		pr_debug("mode debugfs file is not available");
+		return ret;
+	}
+
+	return 0;
+}
+module_init(ghsuart_data_init);
+
+static void __exit ghsuart_data_exit(void)
+{
+	ghsuart_data_debugfs_exit();
+}
+module_exit(ghsuart_data_exit);
+
+MODULE_DESCRIPTION("hsuart data xport driver for DUN and RMNET");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c
index c8702c8..9e3301f 100644
--- a/drivers/usb/gadget/u_ether.c
+++ b/drivers/usb/gadget/u_ether.c
@@ -58,7 +58,7 @@
 
 	spinlock_t		req_lock;	/* guard {rx,tx}_reqs */
 	struct list_head	tx_reqs, rx_reqs;
-	atomic_t		tx_qlen;
+	unsigned		tx_qlen;
 
 	struct sk_buff_head	rx_frames;
 
@@ -476,7 +476,6 @@
 	spin_unlock(&dev->req_lock);
 	dev_kfree_skb_any(skb);
 
-	atomic_dec(&dev->tx_qlen);
 	if (netif_carrier_ok(dev->net))
 		netif_wake_queue(dev->net);
 }
@@ -590,12 +589,19 @@
 
 	req->length = length;
 
-	/* throttle high/super speed IRQ rate back slightly */
-	if (gadget_is_dualspeed(dev->gadget))
-		req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH ||
-				     dev->gadget->speed == USB_SPEED_SUPER)
-			? ((atomic_read(&dev->tx_qlen) % qmult) != 0)
-			: 0;
+	/* throttle highspeed IRQ rate back slightly */
+	if (gadget_is_dualspeed(dev->gadget) &&
+			 (dev->gadget->speed == USB_SPEED_HIGH)) {
+		dev->tx_qlen++;
+		if (dev->tx_qlen == qmult) {
+			req->no_interrupt = 0;
+			dev->tx_qlen = 0;
+		} else {
+			req->no_interrupt = 1;
+		}
+	} else {
+		req->no_interrupt = 0;
+	}
 
 	retval = usb_ep_queue(in, req, GFP_ATOMIC);
 	switch (retval) {
@@ -604,7 +610,6 @@
 		break;
 	case 0:
 		net->trans_start = jiffies;
-		atomic_inc(&dev->tx_qlen);
 	}
 
 	if (retval) {
@@ -630,7 +635,7 @@
 	rx_fill(dev, gfp_flags);
 
 	/* and open the tx floodgates */
-	atomic_set(&dev->tx_qlen, 0);
+	dev->tx_qlen = 0;
 	netif_wake_queue(dev->net);
 }
 
@@ -956,7 +961,6 @@
 	struct eth_dev		*dev = link->ioport;
 	struct usb_request	*req;
 
-	WARN_ON(!dev);
 	if (!dev)
 		return;
 
diff --git a/drivers/usb/gadget/u_rmnet.h b/drivers/usb/gadget/u_rmnet.h
new file mode 100644
index 0000000..0f7c4fb
--- /dev/null
+++ b/drivers/usb/gadget/u_rmnet.h
@@ -0,0 +1,59 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __U_RMNET_H
+#define __U_RMNET_H
+
+#include <linux/usb/composite.h>
+#include <linux/usb/cdc.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+
+struct rmnet_ctrl_pkt {
+	void			*buf;
+	int			len;
+	struct list_head	list;
+};
+
+struct grmnet {
+	struct usb_function		func;
+
+	struct usb_ep			*in;
+	struct usb_ep			*out;
+
+	/* to usb host, aka laptop, windows pc etc. Will
+	 * be filled by usb driver of rmnet functionality
+	 */
+	int (*send_cpkt_response)(void *g, void *buf, size_t len);
+
+	/* to modem, and to be filled by driver implementing
+	 * control function
+	 */
+	int (*send_encap_cmd)(u8 port_num, void *buf, size_t len);
+
+	void (*notify_modem)(void *g, u8 port_num, int cbits);
+
+	void (*disconnect)(struct grmnet *g);
+	void (*connect)(struct grmnet *g);
+};
+
+int gbam_setup(unsigned int no_bam_port, unsigned int no_bam2bam_port);
+int gbam_connect(struct grmnet *gr, u8 port_num,
+				 enum transport_type trans, u8 connection_idx);
+void gbam_disconnect(struct grmnet *gr, u8 port_num, enum transport_type trans);
+void gbam_suspend(struct grmnet *gr, u8 port_num, enum transport_type trans);
+void gbam_resume(struct grmnet *gr, u8 port_num, enum transport_type trans);
+int gsmd_ctrl_connect(struct grmnet *gr, int port_num);
+void gsmd_ctrl_disconnect(struct grmnet *gr, u8 port_num);
+int gsmd_ctrl_setup(unsigned int count);
+
+#endif /* __U_RMNET_H*/
diff --git a/drivers/usb/gadget/u_rmnet_ctrl_smd.c b/drivers/usb/gadget/u_rmnet_ctrl_smd.c
new file mode 100644
index 0000000..0256a75
--- /dev/null
+++ b/drivers/usb/gadget/u_rmnet_ctrl_smd.c
@@ -0,0 +1,706 @@
+/*
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/termios.h>
+#include <mach/msm_smd.h>
+#include <linux/debugfs.h>
+#include <linux/bitops.h>
+#include <linux/termios.h>
+
+#include "u_rmnet.h"
+
+#define NR_CTRL_SMD_PORTS	3
+static int n_rmnet_ctrl_ports;
+static char *rmnet_ctrl_names[] = {"DATA40_CNTL", "DATA39_CNTL", "DATA38_CNTL"};
+static struct workqueue_struct *grmnet_ctrl_wq;
+
+#define SMD_CH_MAX_LEN	20
+#define CH_OPENED	0
+#define CH_READY	1
+struct smd_ch_info {
+	struct smd_channel	*ch;
+	char			*name;
+	unsigned long		flags;
+	wait_queue_head_t	wait;
+	unsigned		dtr;
+
+	struct list_head	tx_q;
+	unsigned long		tx_len;
+
+	struct work_struct	read_w;
+	struct work_struct	write_w;
+
+	struct rmnet_ctrl_port	*port;
+
+	int			cbits_tomodem;
+	/* stats */
+	unsigned long		to_modem;
+	unsigned long		to_host;
+};
+
+struct rmnet_ctrl_port {
+	struct smd_ch_info	ctrl_ch;
+	unsigned int		port_num;
+	struct grmnet		*port_usb;
+
+	spinlock_t		port_lock;
+	struct delayed_work	connect_w;
+};
+
+static struct rmnet_ctrl_ports {
+	struct rmnet_ctrl_port *port;
+	struct platform_driver pdrv;
+} ctrl_smd_ports[NR_CTRL_SMD_PORTS];
+
+
+/*---------------misc functions---------------- */
+
+static struct rmnet_ctrl_pkt *alloc_rmnet_ctrl_pkt(unsigned len, gfp_t flags)
+{
+	struct rmnet_ctrl_pkt *pkt;
+
+	pkt = kzalloc(sizeof(struct rmnet_ctrl_pkt), flags);
+	if (!pkt)
+		return ERR_PTR(-ENOMEM);
+
+	pkt->buf = kmalloc(len, flags);
+	if (!pkt->buf) {
+		kfree(pkt);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	pkt->len = len;
+
+	return pkt;
+}
+
+static void free_rmnet_ctrl_pkt(struct rmnet_ctrl_pkt *pkt)
+{
+	kfree(pkt->buf);
+	kfree(pkt);
+}
+
+/*--------------------------------------------- */
+
+/*---------------control/smd channel functions---------------- */
+
+static void grmnet_ctrl_smd_read_w(struct work_struct *w)
+{
+	struct smd_ch_info *c = container_of(w, struct smd_ch_info, read_w);
+	struct rmnet_ctrl_port *port = c->port;
+	int sz;
+	size_t len;
+	void *buf;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	while (c->ch) {
+		sz = smd_cur_packet_size(c->ch);
+		if (sz <= 0)
+			break;
+
+		if (smd_read_avail(c->ch) < sz)
+			break;
+
+		spin_unlock_irqrestore(&port->port_lock, flags);
+
+		buf = kmalloc(sz, GFP_KERNEL);
+		if (!buf)
+			return;
+
+		len = smd_read(c->ch, buf, sz);
+
+		/* send it to USB here */
+		spin_lock_irqsave(&port->port_lock, flags);
+		if (port->port_usb && port->port_usb->send_cpkt_response) {
+			port->port_usb->send_cpkt_response(port->port_usb,
+							buf, len);
+			c->to_host++;
+		}
+		kfree(buf);
+	}
+	spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+static void grmnet_ctrl_smd_write_w(struct work_struct *w)
+{
+	struct smd_ch_info *c = container_of(w, struct smd_ch_info, write_w);
+	struct rmnet_ctrl_port *port = c->port;
+	unsigned long flags;
+	struct rmnet_ctrl_pkt *cpkt;
+	int ret;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	while (c->ch) {
+		if (list_empty(&c->tx_q))
+			break;
+
+		cpkt = list_first_entry(&c->tx_q, struct rmnet_ctrl_pkt, list);
+
+		if (smd_write_avail(c->ch) < cpkt->len)
+			break;
+
+		list_del(&cpkt->list);
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		ret = smd_write(c->ch, cpkt->buf, cpkt->len);
+		spin_lock_irqsave(&port->port_lock, flags);
+		if (ret != cpkt->len) {
+			pr_err("%s: smd_write failed err:%d\n", __func__, ret);
+			free_rmnet_ctrl_pkt(cpkt);
+			break;
+		}
+		free_rmnet_ctrl_pkt(cpkt);
+		c->to_modem++;
+	}
+	spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+static int
+grmnet_ctrl_smd_send_cpkt_tomodem(u8 portno,
+	void *buf, size_t len)
+{
+	unsigned long		flags;
+	struct rmnet_ctrl_port	*port;
+	struct smd_ch_info	*c;
+	struct rmnet_ctrl_pkt *cpkt;
+
+	if (portno >= n_rmnet_ctrl_ports) {
+		pr_err("%s: Invalid portno#%d\n", __func__, portno);
+		return -ENODEV;
+	}
+
+	port = ctrl_smd_ports[portno].port;
+
+	cpkt = alloc_rmnet_ctrl_pkt(len, GFP_ATOMIC);
+	if (IS_ERR(cpkt)) {
+		pr_err("%s: Unable to allocate ctrl pkt\n", __func__);
+		return -ENOMEM;
+	}
+
+	memcpy(cpkt->buf, buf, len);
+	cpkt->len = len;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	c = &port->ctrl_ch;
+
+	/* drop cpkt if ch is not open */
+	if (!test_bit(CH_OPENED, &c->flags)) {
+		free_rmnet_ctrl_pkt(cpkt);
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		return 0;
+	}
+
+	list_add_tail(&cpkt->list, &c->tx_q);
+	queue_work(grmnet_ctrl_wq, &c->write_w);
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	return 0;
+}
+
+#define RMNET_CTRL_DTR		0x01
+static void
+gsmd_ctrl_send_cbits_tomodem(void *gptr, u8 portno, int cbits)
+{
+	struct rmnet_ctrl_port	*port;
+	struct smd_ch_info	*c;
+	int			set_bits = 0;
+	int			clear_bits = 0;
+	int			temp = 0;
+
+	if (portno >= n_rmnet_ctrl_ports) {
+		pr_err("%s: Invalid portno#%d\n", __func__, portno);
+		return;
+	}
+
+	if (!gptr) {
+		pr_err("%s: grmnet is null\n", __func__);
+		return;
+	}
+
+	port = ctrl_smd_ports[portno].port;
+	cbits = cbits & RMNET_CTRL_DTR;
+	c = &port->ctrl_ch;
+
+	/* host driver will only send DTR, but to have generic
+	 * set and clear bit implementation using two separate
+	 * checks
+	 */
+	if (cbits & RMNET_CTRL_DTR)
+		set_bits |= TIOCM_DTR;
+	else
+		clear_bits |= TIOCM_DTR;
+
+	temp |= set_bits;
+	temp &= ~clear_bits;
+
+	if (temp == c->cbits_tomodem)
+		return;
+
+	c->cbits_tomodem = temp;
+
+	if (!test_bit(CH_OPENED, &c->flags))
+		return;
+
+	pr_debug("%s: ctrl_tomodem:%d ctrl_bits:%d setbits:%d clearbits:%d\n",
+			__func__, temp, cbits, set_bits, clear_bits);
+
+	smd_tiocmset(c->ch, set_bits, clear_bits);
+}
+
+static char *get_smd_event(unsigned event)
+{
+	switch (event) {
+	case SMD_EVENT_DATA:
+		return "DATA";
+	case SMD_EVENT_OPEN:
+		return "OPEN";
+	case SMD_EVENT_CLOSE:
+		return "CLOSE";
+	}
+
+	return "UNDEFINED";
+}
+
+static void grmnet_ctrl_smd_notify(void *p, unsigned event)
+{
+	struct rmnet_ctrl_port	*port = p;
+	struct smd_ch_info	*c = &port->ctrl_ch;
+	struct rmnet_ctrl_pkt	*cpkt;
+	unsigned long		flags;
+
+	pr_debug("%s: EVENT_(%s)\n", __func__, get_smd_event(event));
+
+	switch (event) {
+	case SMD_EVENT_DATA:
+		if (smd_read_avail(c->ch))
+			queue_work(grmnet_ctrl_wq, &c->read_w);
+		if (smd_write_avail(c->ch))
+			queue_work(grmnet_ctrl_wq, &c->write_w);
+		break;
+	case SMD_EVENT_OPEN:
+		set_bit(CH_OPENED, &c->flags);
+
+		if (port && port->port_usb && port->port_usb->connect)
+			port->port_usb->connect(port->port_usb);
+
+		break;
+	case SMD_EVENT_CLOSE:
+		clear_bit(CH_OPENED, &c->flags);
+
+		if (port && port->port_usb && port->port_usb->disconnect)
+			port->port_usb->disconnect(port->port_usb);
+
+		spin_lock_irqsave(&port->port_lock, flags);
+		while (!list_empty(&c->tx_q)) {
+			cpkt = list_first_entry(&c->tx_q,
+					struct rmnet_ctrl_pkt, list);
+
+			list_del(&cpkt->list);
+			free_rmnet_ctrl_pkt(cpkt);
+		}
+		spin_unlock_irqrestore(&port->port_lock, flags);
+
+		break;
+	}
+}
+/*------------------------------------------------------------ */
+
+static void grmnet_ctrl_smd_connect_w(struct work_struct *w)
+{
+	struct rmnet_ctrl_port *port =
+			container_of(w, struct rmnet_ctrl_port, connect_w.work);
+	struct smd_ch_info *c = &port->ctrl_ch;
+	unsigned long flags;
+	int ret;
+
+	pr_debug("%s:\n", __func__);
+
+	if (!test_bit(CH_READY, &c->flags))
+		return;
+
+	ret = smd_open(c->name, &c->ch, port, grmnet_ctrl_smd_notify);
+	if (ret) {
+		if (ret == -EAGAIN) {
+			/* port not ready  - retry */
+			pr_debug("%s: SMD port not ready - rescheduling:%s err:%d\n",
+					__func__, c->name, ret);
+			queue_delayed_work(grmnet_ctrl_wq, &port->connect_w,
+				msecs_to_jiffies(250));
+		} else {
+			pr_err("%s: unable to open smd port:%s err:%d\n",
+					__func__, c->name, ret);
+		}
+		return;
+	}
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	if (port->port_usb)
+		smd_tiocmset(c->ch, c->cbits_tomodem, ~c->cbits_tomodem);
+	spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+int gsmd_ctrl_connect(struct grmnet *gr, int port_num)
+{
+	struct rmnet_ctrl_port	*port;
+	struct smd_ch_info	*c;
+	unsigned long		flags;
+
+	pr_debug("%s: grmnet:%p port#%d\n", __func__, gr, port_num);
+
+	if (port_num >= n_rmnet_ctrl_ports) {
+		pr_err("%s: invalid portno#%d\n", __func__, port_num);
+		return -ENODEV;
+	}
+
+	if (!gr) {
+		pr_err("%s: grmnet port is null\n", __func__);
+		return -ENODEV;
+	}
+
+	port = ctrl_smd_ports[port_num].port;
+	c = &port->ctrl_ch;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	port->port_usb = gr;
+	gr->send_encap_cmd = grmnet_ctrl_smd_send_cpkt_tomodem;
+	gr->notify_modem = gsmd_ctrl_send_cbits_tomodem;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	queue_delayed_work(grmnet_ctrl_wq, &port->connect_w, 0);
+
+	return 0;
+}
+
+void gsmd_ctrl_disconnect(struct grmnet *gr, u8 port_num)
+{
+	struct rmnet_ctrl_port	*port;
+	unsigned long		flags;
+	struct smd_ch_info	*c;
+	struct rmnet_ctrl_pkt	*cpkt;
+
+	pr_debug("%s: grmnet:%p port#%d\n", __func__, gr, port_num);
+
+	if (port_num >= n_rmnet_ctrl_ports) {
+		pr_err("%s: invalid portno#%d\n", __func__, port_num);
+		return;
+	}
+
+	if (!gr) {
+		pr_err("%s: grmnet port is null\n", __func__);
+		return;
+	}
+
+	port = ctrl_smd_ports[port_num].port;
+	c = &port->ctrl_ch;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	port->port_usb = 0;
+	gr->send_encap_cmd = 0;
+	gr->notify_modem = 0;
+	c->cbits_tomodem = 0;
+
+	while (!list_empty(&c->tx_q)) {
+		cpkt = list_first_entry(&c->tx_q, struct rmnet_ctrl_pkt, list);
+
+		list_del(&cpkt->list);
+		free_rmnet_ctrl_pkt(cpkt);
+	}
+
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	if (test_and_clear_bit(CH_OPENED, &c->flags))
+		/* send dtr zero */
+		smd_tiocmset(c->ch, c->cbits_tomodem, ~c->cbits_tomodem);
+
+	if (c->ch) {
+		smd_close(c->ch);
+		c->ch = NULL;
+	}
+}
+
+#define SMD_CH_MAX_LEN	20
+static int grmnet_ctrl_smd_ch_probe(struct platform_device *pdev)
+{
+	struct rmnet_ctrl_port	*port;
+	struct smd_ch_info	*c;
+	int			i;
+	unsigned long		flags;
+
+	pr_debug("%s: name:%s\n", __func__, pdev->name);
+
+	for (i = 0; i < n_rmnet_ctrl_ports; i++) {
+		port = ctrl_smd_ports[i].port;
+		c = &port->ctrl_ch;
+
+		if (!strncmp(c->name, pdev->name, SMD_CH_MAX_LEN)) {
+			set_bit(CH_READY, &c->flags);
+
+			/* if usb is online, try opening smd_ch */
+			spin_lock_irqsave(&port->port_lock, flags);
+			if (port->port_usb)
+				queue_delayed_work(grmnet_ctrl_wq,
+							&port->connect_w, 0);
+			spin_unlock_irqrestore(&port->port_lock, flags);
+
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static int grmnet_ctrl_smd_ch_remove(struct platform_device *pdev)
+{
+	struct rmnet_ctrl_port	*port;
+	struct smd_ch_info	*c;
+	int			i;
+
+	pr_debug("%s: name:%s\n", __func__, pdev->name);
+
+	for (i = 0; i < n_rmnet_ctrl_ports; i++) {
+		port = ctrl_smd_ports[i].port;
+		c = &port->ctrl_ch;
+
+		if (!strncmp(c->name, pdev->name, SMD_CH_MAX_LEN)) {
+			clear_bit(CH_READY, &c->flags);
+			clear_bit(CH_OPENED, &c->flags);
+			if (c->ch) {
+				smd_close(c->ch);
+				c->ch = NULL;
+			}
+			break;
+		}
+	}
+
+	return 0;
+}
+
+
+static void grmnet_ctrl_smd_port_free(int portno)
+{
+	struct rmnet_ctrl_port	*port = ctrl_smd_ports[portno].port;
+	struct platform_driver *pdrv = &ctrl_smd_ports[portno].pdrv;
+
+	if (port) {
+		kfree(port);
+		platform_driver_unregister(pdrv);
+	}
+}
+
+static int grmnet_ctrl_smd_port_alloc(int portno)
+{
+	struct rmnet_ctrl_port	*port;
+	struct smd_ch_info	*c;
+	struct platform_driver	*pdrv;
+
+	port = kzalloc(sizeof(struct rmnet_ctrl_port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	port->port_num = portno;
+
+	spin_lock_init(&port->port_lock);
+	INIT_DELAYED_WORK(&port->connect_w, grmnet_ctrl_smd_connect_w);
+
+	c = &port->ctrl_ch;
+	c->name = rmnet_ctrl_names[portno];
+	c->port = port;
+	init_waitqueue_head(&c->wait);
+	INIT_LIST_HEAD(&c->tx_q);
+	INIT_WORK(&c->read_w, grmnet_ctrl_smd_read_w);
+	INIT_WORK(&c->write_w, grmnet_ctrl_smd_write_w);
+
+	ctrl_smd_ports[portno].port = port;
+
+	pdrv = &ctrl_smd_ports[portno].pdrv;
+	pdrv->probe = grmnet_ctrl_smd_ch_probe;
+	pdrv->remove = grmnet_ctrl_smd_ch_remove;
+	pdrv->driver.name = c->name;
+	pdrv->driver.owner = THIS_MODULE;
+
+	platform_driver_register(pdrv);
+
+	pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
+
+	return 0;
+}
+
+int gsmd_ctrl_setup(unsigned int count)
+{
+	int	i;
+	int	ret;
+
+	pr_debug("%s: requested ports:%d\n", __func__, count);
+
+	if (!count || count > NR_CTRL_SMD_PORTS) {
+		pr_err("%s: Invalid num of ports count:%d\n",
+				__func__, count);
+		return -EINVAL;
+	}
+
+	grmnet_ctrl_wq = alloc_workqueue("gsmd_ctrl",
+				WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
+	if (!grmnet_ctrl_wq) {
+		pr_err("%s: Unable to create workqueue grmnet_ctrl\n",
+				__func__);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < count; i++) {
+		n_rmnet_ctrl_ports++;
+		ret = grmnet_ctrl_smd_port_alloc(i);
+		if (ret) {
+			pr_err("%s: Unable to alloc port:%d\n", __func__, i);
+			n_rmnet_ctrl_ports--;
+			goto free_ctrl_smd_ports;
+		}
+	}
+
+	return 0;
+
+free_ctrl_smd_ports:
+	for (i = 0; i < n_rmnet_ctrl_ports; i++)
+		grmnet_ctrl_smd_port_free(i);
+
+	destroy_workqueue(grmnet_ctrl_wq);
+
+	return ret;
+}
+
+#if defined(CONFIG_DEBUG_FS)
+#define DEBUG_BUF_SIZE	1024
+static ssize_t gsmd_ctrl_read_stats(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	struct rmnet_ctrl_port	*port;
+	struct smd_ch_info	*c;
+	char			*buf;
+	unsigned long		flags;
+	int			ret;
+	int			i;
+	int			temp = 0;
+
+	buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	for (i = 0; i < n_rmnet_ctrl_ports; i++) {
+		port = ctrl_smd_ports[i].port;
+		if (!port)
+			continue;
+		spin_lock_irqsave(&port->port_lock, flags);
+
+		c = &port->ctrl_ch;
+
+		temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+				"#PORT:%d port:%p ctrl_ch:%p#\n"
+				"to_usbhost: %lu\n"
+				"to_modem:   %lu\n"
+				"DTR:        %s\n"
+				"ch_open:    %d\n"
+				"ch_ready:   %d\n"
+				"read_avail: %d\n"
+				"write_avail:%d\n",
+				i, port, &port->ctrl_ch,
+				c->to_host, c->to_modem,
+				c->cbits_tomodem ? "HIGH" : "LOW",
+				test_bit(CH_OPENED, &c->flags),
+				test_bit(CH_READY, &c->flags),
+				c->ch ? smd_read_avail(c->ch) : 0,
+				c->ch ? smd_write_avail(c->ch) : 0);
+
+		spin_unlock_irqrestore(&port->port_lock, flags);
+	}
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+	kfree(buf);
+
+	return ret;
+}
+
+static ssize_t gsmd_ctrl_reset_stats(struct file *file, const char __user *buf,
+				 size_t count, loff_t *ppos)
+{
+	struct rmnet_ctrl_port	*port;
+	struct smd_ch_info	*c;
+	int			i;
+	unsigned long		flags;
+
+	for (i = 0; i < n_rmnet_ctrl_ports; i++) {
+		port = ctrl_smd_ports[i].port;
+		if (!port)
+			continue;
+
+		spin_lock_irqsave(&port->port_lock, flags);
+
+		c = &port->ctrl_ch;
+
+		c->to_host = 0;
+		c->to_modem = 0;
+
+		spin_unlock_irqrestore(&port->port_lock, flags);
+	}
+	return count;
+}
+
+const struct file_operations gsmd_ctrl_stats_ops = {
+	.read = gsmd_ctrl_read_stats,
+	.write = gsmd_ctrl_reset_stats,
+};
+
+struct dentry *smd_ctrl_dent;
+struct dentry *smd_ctrl_dfile;
+static void gsmd_ctrl_debugfs_init(void)
+{
+	smd_ctrl_dent = debugfs_create_dir("usb_rmnet_ctrl_smd", 0);
+	if (IS_ERR(smd_ctrl_dent))
+		return;
+
+	smd_ctrl_dfile = debugfs_create_file("status", 0444, smd_ctrl_dent, 0,
+			&gsmd_ctrl_stats_ops);
+	if (!smd_ctrl_dfile || IS_ERR(smd_ctrl_dfile))
+		debugfs_remove(smd_ctrl_dent);
+}
+
+static void gsmd_ctrl_debugfs_exit(void)
+{
+	debugfs_remove(smd_ctrl_dfile);
+	debugfs_remove(smd_ctrl_dent);
+}
+
+#else
+static void gsmd_ctrl_debugfs_init(void) { }
+static void gsmd_ctrl_debugfs_exit(void) { }
+#endif
+
+static int __init gsmd_ctrl_init(void)
+{
+	gsmd_ctrl_debugfs_init();
+
+	return 0;
+}
+module_init(gsmd_ctrl_init);
+
+static void __exit gsmd_ctrl_exit(void)
+{
+	gsmd_ctrl_debugfs_exit();
+}
+module_exit(gsmd_ctrl_exit);
+MODULE_DESCRIPTION("smd control driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/gadget/u_sdio.c b/drivers/usb/gadget/u_sdio.c
new file mode 100644
index 0000000..8c4b4c7
--- /dev/null
+++ b/drivers/usb/gadget/u_sdio.c
@@ -0,0 +1,1169 @@
+/*
+ * u_sdio.c - utilities for USB gadget serial over sdio
+ *
+ * This code also borrows from drivers/usb/gadget/u_serial.c, which is
+ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
+ * Copyright (C) 2008 David Brownell
+ * Copyright (C) 2008 by Nokia Corporation
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program from the Code Aurora Forum is free software; you can
+ * redistribute it and/or modify it under the GNU General Public License
+ * version 2 and only version 2 as published by the Free Software Foundation.
+ * The original work available from [kernel.org] is subject to the notice below.
+ *
+ * This software is distributed under the terms of the GNU General
+ * Public License ("GPL") as published by the Free Software Foundation,
+ * either version 2 of that License or (at your option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/termios.h>
+#include <linux/debugfs.h>
+
+#include <mach/sdio_al.h>
+#include <mach/sdio_cmux.h>
+#include "u_serial.h"
+
+#define SDIO_RX_QUEUE_SIZE		8
+#define SDIO_RX_BUF_SIZE		2048
+
+#define SDIO_TX_QUEUE_SIZE		8
+#define SDIO_TX_BUF_SIZE		2048
+
+/* 1 - DUN, 2-NMEA/GPS */
+#define SDIO_N_PORTS	2
+static struct sdio_portmaster {
+	struct mutex lock;
+	struct gsdio_port *port;
+	struct platform_driver gsdio_ch;
+} sdio_ports[SDIO_N_PORTS];
+static unsigned n_sdio_ports;
+
+struct sdio_port_info {
+	/* data channel info */
+	char *data_ch_name;
+	struct sdio_channel *ch;
+
+	/* control channel info */
+	int ctrl_ch_id;
+};
+
+struct sdio_port_info sport_info[SDIO_N_PORTS] = {
+	{
+		.data_ch_name = "SDIO_DUN",
+		.ctrl_ch_id = 9,
+	},
+	{
+		.data_ch_name = "SDIO_NMEA",
+		.ctrl_ch_id = 10,
+	},
+};
+
+static struct workqueue_struct *gsdio_wq;
+
+struct gsdio_port {
+	unsigned			port_num;
+	spinlock_t			port_lock;
+
+	unsigned			n_read;
+	struct list_head		read_pool;
+	struct list_head		read_queue;
+	struct work_struct		push;
+	unsigned long			rp_len;
+	unsigned long			rq_len;
+
+	struct list_head		write_pool;
+	struct work_struct		pull;
+	unsigned long			wp_len;
+
+	struct work_struct		notify_modem;
+
+	struct gserial			*port_usb;
+	struct usb_cdc_line_coding	line_coding;
+
+	int				sdio_open;
+	int				sdio_probe;
+	int				ctrl_ch_err;
+	struct sdio_port_info		*sport_info;
+	struct delayed_work		sdio_open_work;
+
+#define SDIO_ACM_CTRL_RI		(1 << 3)
+#define SDIO_ACM_CTRL_DSR		(1 << 1)
+#define SDIO_ACM_CTRL_DCD		(1 << 0)
+	int				cbits_to_laptop;
+
+#define SDIO_ACM_CTRL_RTS	(1 << 1)	/* unused with full duplex */
+#define SDIO_ACM_CTRL_DTR	(1 << 0)	/* host is ready for data r/w */
+	int				cbits_to_modem;
+
+	/* pkt logging */
+	unsigned long			nbytes_tolaptop;
+	unsigned long			nbytes_tomodem;
+};
+
+void gsdio_free_req(struct usb_ep *ep, struct usb_request *req)
+{
+	kfree(req->buf);
+	usb_ep_free_request(ep, req);
+}
+
+struct usb_request *
+gsdio_alloc_req(struct usb_ep *ep, unsigned len, gfp_t flags)
+{
+	struct usb_request *req;
+
+	req = usb_ep_alloc_request(ep, flags);
+	if (!req) {
+		pr_err("%s: usb alloc request failed\n", __func__);
+		return NULL;
+	}
+
+	req->length = len;
+	req->buf = kmalloc(len, flags);
+	if (!req->buf) {
+		pr_err("%s: request buf allocation failed\n", __func__);
+		usb_ep_free_request(ep, req);
+		return NULL;
+	}
+
+	return req;
+}
+
+void gsdio_free_requests(struct usb_ep *ep, struct list_head *head)
+{
+	struct usb_request	*req;
+
+	while (!list_empty(head)) {
+		req = list_entry(head->next, struct usb_request, list);
+		list_del(&req->list);
+		gsdio_free_req(ep, req);
+	}
+}
+
+int gsdio_alloc_requests(struct usb_ep *ep, struct list_head *head,
+		int num, int size,
+		void (*cb)(struct usb_ep *ep, struct usb_request *))
+{
+	int i;
+	struct usb_request *req;
+
+	pr_debug("%s: ep:%p head:%p num:%d size:%d cb:%p", __func__,
+			ep, head, num, size, cb);
+
+	for (i = 0; i < num; i++) {
+		req = gsdio_alloc_req(ep, size, GFP_ATOMIC);
+		if (!req) {
+			pr_debug("%s: req allocated:%d\n", __func__, i);
+			return list_empty(head) ? -ENOMEM : 0;
+		}
+		req->complete = cb;
+		list_add(&req->list, head);
+	}
+
+	return 0;
+}
+
+void gsdio_start_rx(struct gsdio_port *port)
+{
+	struct list_head	*pool;
+	struct usb_ep		*out;
+	int ret;
+
+	if (!port) {
+		pr_err("%s: port is null\n", __func__);
+		return;
+	}
+
+	pr_debug("%s: port:%p port#%d\n", __func__, port, port->port_num);
+
+	spin_lock_irq(&port->port_lock);
+
+	if (!port->port_usb) {
+		pr_debug("%s: usb is disconnected\n", __func__);
+		goto start_rx_end;
+	}
+
+	if (!port->sdio_open) {
+		pr_debug("%s: sdio is not open\n", __func__);
+		goto start_rx_end;
+	}
+
+	pool = &port->read_pool;
+	out = port->port_usb->out;
+
+	while (!list_empty(pool)) {
+		struct usb_request	*req;
+
+		req = list_entry(pool->next, struct usb_request, list);
+		list_del(&req->list);
+		req->length = SDIO_RX_BUF_SIZE;
+		port->rp_len--;
+
+		spin_unlock_irq(&port->port_lock);
+		ret = usb_ep_queue(out, req, GFP_ATOMIC);
+		spin_lock_irq(&port->port_lock);
+		if (ret) {
+			pr_err("%s: usb ep out queue failed"
+					"port:%p, port#%d\n",
+					__func__, port, port->port_num);
+			list_add_tail(&req->list, pool);
+			port->rp_len++;
+			break;
+		}
+
+		/* usb could have disconnected while we released spin lock */
+		if (!port->port_usb) {
+			pr_debug("%s: usb is disconnected\n", __func__);
+			goto start_rx_end;
+		}
+	}
+
+start_rx_end:
+	spin_unlock_irq(&port->port_lock);
+}
+
+int gsdio_write(struct gsdio_port *port, struct usb_request *req)
+{
+	unsigned	avail;
+	char		*packet;
+	unsigned	size = req->actual;
+	unsigned	n;
+	int		ret = 0;
+
+
+	if (!port) {
+		pr_err("%s: port is null\n", __func__);
+		return -ENODEV;
+	}
+
+	if (!req) {
+		pr_err("%s: usb request is null port#%d\n",
+				__func__, port->port_num);
+		return -ENODEV;
+	}
+
+	pr_debug("%s: port:%p port#%d req:%p actual:%d n_read:%d\n",
+			__func__, port, port->port_num, req,
+			req->actual, port->n_read);
+
+	if (!port->sdio_open) {
+		pr_debug("%s: SDIO IO is not supported\n", __func__);
+		return -ENODEV;
+	}
+
+	avail = sdio_write_avail(port->sport_info->ch);
+
+	pr_debug("%s: sdio_write_avail:%d", __func__, avail);
+
+	if (!avail)
+		return -EBUSY;
+
+	if (!req->actual) {
+		pr_debug("%s: req->actual is already zero,update bytes read\n",
+				__func__);
+		port->n_read = 0;
+		return -ENODEV;
+	}
+
+	packet = req->buf;
+	n = port->n_read;
+	if (n) {
+		packet += n;
+		size -= n;
+	}
+
+	if (size > avail)
+		size = avail;
+
+	spin_unlock_irq(&port->port_lock);
+	ret = sdio_write(port->sport_info->ch, packet, size);
+	spin_lock_irq(&port->port_lock);
+	if (ret) {
+		pr_err("%s: port#%d sdio write failed err:%d",
+				__func__, port->port_num, ret);
+		/* try again later */
+		return ret;
+	}
+
+	port->nbytes_tomodem += size;
+
+	if (size + n == req->actual)
+		port->n_read = 0;
+	else
+		port->n_read += size;
+
+	return ret;
+}
+
+void gsdio_rx_push(struct work_struct *w)
+{
+	struct gsdio_port *port = container_of(w, struct gsdio_port, push);
+	struct list_head *q = &port->read_queue;
+	struct usb_ep		*out;
+	int ret;
+
+	pr_debug("%s: port:%p port#%d read_queue:%p", __func__,
+			port, port->port_num, q);
+
+	spin_lock_irq(&port->port_lock);
+
+	if (!port->port_usb) {
+		pr_debug("%s: usb cable is disconencted\n", __func__);
+		spin_unlock_irq(&port->port_lock);
+		return;
+	}
+
+	out = port->port_usb->out;
+
+	while (!list_empty(q)) {
+		struct usb_request *req;
+
+		req = list_first_entry(q, struct usb_request, list);
+
+		switch (req->status) {
+		case -ESHUTDOWN:
+			pr_debug("%s: req status shutdown portno#%d port:%p",
+					__func__, port->port_num, port);
+			goto rx_push_end;
+		default:
+			pr_warning("%s: port:%p port#%d"
+					" Unexpected Rx Status:%d\n", __func__,
+					port, port->port_num, req->status);
+			/* FALL THROUGH */
+		case 0:
+			/* normal completion */
+			break;
+		}
+
+		if (!port->sdio_open) {
+			pr_err("%s: sio channel is not open\n", __func__);
+			list_move(&req->list, &port->read_pool);
+			port->rp_len++;
+			port->rq_len--;
+			goto rx_push_end;
+		}
+
+
+		list_del(&req->list);
+		port->rq_len--;
+
+		ret = gsdio_write(port, req);
+		/* as gsdio_write drops spin_lock while writing data
+		 * to sdio usb cable may have been disconnected
+		 */
+		if (!port->port_usb) {
+			port->n_read = 0;
+			gsdio_free_req(out, req);
+			spin_unlock_irq(&port->port_lock);
+			return;
+		}
+
+		if (ret || port->n_read) {
+			list_add(&req->list, &port->read_queue);
+			port->rq_len++;
+			goto rx_push_end;
+		}
+
+		list_add(&req->list, &port->read_pool);
+		port->rp_len++;
+	}
+
+	if (port->sdio_open && !list_empty(q)) {
+		if (sdio_write_avail(port->sport_info->ch))
+			queue_work(gsdio_wq, &port->push);
+	}
+rx_push_end:
+	spin_unlock_irq(&port->port_lock);
+
+	/* start queuing out requests again to host */
+	gsdio_start_rx(port);
+}
+
+void gsdio_read_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct gsdio_port *port = ep->driver_data;
+	unsigned long flags;
+
+	pr_debug("%s: ep:%p port:%p\n", __func__, ep, port);
+
+	if (!port) {
+		pr_err("%s: port is null\n", __func__);
+		return;
+	}
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	list_add_tail(&req->list, &port->read_queue);
+	port->rq_len++;
+	queue_work(gsdio_wq, &port->push);
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	return;
+}
+
+void gsdio_write_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct gsdio_port *port = ep->driver_data;
+	unsigned long flags;
+
+	pr_debug("%s: ep:%p port:%p\n", __func__, ep, port);
+
+	if (!port) {
+		pr_err("%s: port is null\n", __func__);
+		return;
+	}
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	list_add(&req->list, &port->write_pool);
+	port->wp_len++;
+
+	switch (req->status) {
+	default:
+		pr_warning("%s: port:%p port#%d unexpected %s status %d\n",
+				__func__, port, port->port_num,
+				ep->name, req->status);
+		/* FALL THROUGH */
+	case 0:
+		queue_work(gsdio_wq, &port->pull);
+		break;
+
+	case -ESHUTDOWN:
+		/* disconnect */
+		pr_debug("%s: %s shutdown\n", __func__, ep->name);
+		break;
+	}
+
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	return;
+}
+
+void gsdio_read_pending(struct gsdio_port *port)
+{
+	struct sdio_channel *ch;
+	char buf[1024];
+	int avail;
+
+	if (!port) {
+		pr_err("%s: port is null\n", __func__);
+		return;
+	}
+
+	ch = port->sport_info->ch;
+
+	if (!ch)
+		return;
+
+	while ((avail = sdio_read_avail(ch))) {
+		if (avail > 1024)
+			avail = 1024;
+		sdio_read(ch, buf, avail);
+
+		pr_debug("%s: flushed out %d bytes\n", __func__, avail);
+	}
+}
+
+void gsdio_tx_pull(struct work_struct *w)
+{
+	struct gsdio_port *port = container_of(w, struct gsdio_port, pull);
+	struct list_head *pool = &port->write_pool;
+
+	pr_debug("%s: port:%p port#%d pool:%p\n", __func__,
+			port, port->port_num, pool);
+
+	if (!port->port_usb) {
+		pr_err("%s: usb disconnected\n", __func__);
+
+		/* take out all the pending data from sdio */
+		gsdio_read_pending(port);
+
+		return;
+	}
+
+	spin_lock_irq(&port->port_lock);
+
+	while (!list_empty(pool)) {
+		int avail;
+		struct usb_ep *in = port->port_usb->in;
+		struct sdio_channel *ch = port->sport_info->ch;
+		struct usb_request *req;
+		unsigned len = SDIO_TX_BUF_SIZE;
+		int ret;
+
+
+		req = list_entry(pool->next, struct usb_request, list);
+
+		if (!port->sdio_open) {
+			pr_debug("%s: SDIO channel is not open\n", __func__);
+			goto tx_pull_end;
+		}
+
+		avail = sdio_read_avail(ch);
+		if (!avail) {
+			/* REVISIT: for ZLP */
+			pr_debug("%s: read_avail:%d port:%p port#%d\n",
+					__func__, avail, port, port->port_num);
+			goto tx_pull_end;
+		}
+
+		if (avail > len)
+			avail = len;
+
+		list_del(&req->list);
+		port->wp_len--;
+
+		spin_unlock_irq(&port->port_lock);
+		ret = sdio_read(ch, req->buf, avail);
+		spin_lock_irq(&port->port_lock);
+		if (ret) {
+			pr_err("%s: port:%p port#%d sdio read failed err:%d",
+					__func__, port, port->port_num, ret);
+
+			/* check if usb is still active */
+			if (!port->port_usb) {
+				gsdio_free_req(in, req);
+			} else {
+				list_add(&req->list, pool);
+				port->wp_len++;
+			}
+			goto tx_pull_end;
+		}
+
+		req->length = avail;
+
+		spin_unlock_irq(&port->port_lock);
+		ret = usb_ep_queue(in, req, GFP_KERNEL);
+		spin_lock_irq(&port->port_lock);
+		if (ret) {
+			pr_err("%s: usb ep out queue failed"
+					"port:%p, port#%d err:%d\n",
+					__func__, port, port->port_num, ret);
+
+			/* could be usb disconnected */
+			if (!port->port_usb) {
+				gsdio_free_req(in, req);
+			} else {
+				list_add(&req->list, pool);
+				port->wp_len++;
+			}
+			goto tx_pull_end;
+		}
+
+		port->nbytes_tolaptop += avail;
+	}
+tx_pull_end:
+	spin_unlock_irq(&port->port_lock);
+}
+
+int gsdio_start_io(struct gsdio_port *port)
+{
+	int			ret;
+	unsigned long		flags;
+
+	pr_debug("%s:\n", __func__);
+
+	spin_lock_irqsave(&port->port_lock, flags);
+
+	if (!port->port_usb) {
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		return -ENODEV;
+	}
+
+	/* start usb out queue */
+	ret = gsdio_alloc_requests(port->port_usb->out,
+				&port->read_pool,
+				SDIO_RX_QUEUE_SIZE, SDIO_RX_BUF_SIZE,
+				gsdio_read_complete);
+	if (ret) {
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		pr_err("%s: unable to allocate out reqs\n", __func__);
+		return ret;
+	}
+	port->rp_len = SDIO_RX_QUEUE_SIZE;
+
+	ret = gsdio_alloc_requests(port->port_usb->in,
+				&port->write_pool,
+				SDIO_TX_QUEUE_SIZE, SDIO_TX_BUF_SIZE,
+				gsdio_write_complete);
+	if (ret) {
+		gsdio_free_requests(port->port_usb->out, &port->read_pool);
+		port->rp_len = 0;
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		pr_err("%s: unable to allocate in reqs\n", __func__);
+		return ret;
+	}
+	port->wp_len = SDIO_TX_QUEUE_SIZE;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	gsdio_start_rx(port);
+	queue_work(gsdio_wq, &port->pull);
+
+	return 0;
+}
+
+void gsdio_port_free(unsigned portno)
+{
+	struct gsdio_port *port = sdio_ports[portno].port;
+	struct platform_driver *pdriver = &sdio_ports[portno].gsdio_ch;
+
+	if (!port) {
+		pr_err("%s: invalid portno#%d\n", __func__, portno);
+		return;
+	}
+
+	platform_driver_unregister(pdriver);
+
+	kfree(port);
+}
+
+void gsdio_ctrl_wq(struct work_struct *w)
+{
+	struct gsdio_port *port;
+
+	port = container_of(w, struct gsdio_port, notify_modem);
+
+	if (!port) {
+		pr_err("%s: port is null\n", __func__);
+		return;
+	}
+
+	if (!port->sdio_open || port->ctrl_ch_err)
+		return;
+
+	sdio_cmux_tiocmset(port->sport_info->ctrl_ch_id,
+			port->cbits_to_modem, ~(port->cbits_to_modem));
+}
+
+void gsdio_ctrl_notify_modem(void *gptr, u8 portno, int ctrl_bits)
+{
+	struct gsdio_port *port;
+	int temp;
+	struct gserial *gser = gptr;
+
+	if (portno >= n_sdio_ports) {
+		pr_err("%s: invalid portno#%d\n", __func__, portno);
+		return;
+	}
+
+	if (!gser) {
+		pr_err("%s: gser is null\n", __func__);
+		return;
+	}
+
+	port = sdio_ports[portno].port;
+
+	temp = ctrl_bits & SDIO_ACM_CTRL_DTR ? TIOCM_DTR : 0;
+
+	if (port->cbits_to_modem == temp)
+		return;
+
+	 port->cbits_to_modem = temp;
+
+	/* TIOCM_DTR - 0x002 - bit(1) */
+	pr_debug("%s: port:%p port#%d ctrl_bits:%08x\n", __func__,
+		port, port->port_num, ctrl_bits);
+
+	if (!port->sdio_open) {
+		pr_err("%s: port:%p port#%d sdio not connected\n",
+				__func__, port, port->port_num);
+		return;
+	}
+
+	/* whenever DTR is high let laptop know that modem status */
+	if (port->cbits_to_modem && gser->send_modem_ctrl_bits)
+		gser->send_modem_ctrl_bits(gser, port->cbits_to_laptop);
+
+	queue_work(gsdio_wq, &port->notify_modem);
+}
+
+void gsdio_ctrl_modem_status(int ctrl_bits, void *_dev)
+{
+	struct gsdio_port *port = _dev;
+
+	/* TIOCM_CD - 0x040 - bit(6)
+	 * TIOCM_RI - 0x080 - bit(7)
+	 * TIOCM_DSR- 0x100 - bit(8)
+	 */
+	pr_debug("%s: port:%p port#%d event:%08x\n", __func__,
+		port, port->port_num, ctrl_bits);
+
+	port->cbits_to_laptop = 0;
+	ctrl_bits &= TIOCM_RI | TIOCM_CD | TIOCM_DSR;
+	if (ctrl_bits & TIOCM_RI)
+		port->cbits_to_laptop |= SDIO_ACM_CTRL_RI;
+	if (ctrl_bits & TIOCM_CD)
+		port->cbits_to_laptop |= SDIO_ACM_CTRL_DCD;
+	if (ctrl_bits & TIOCM_DSR)
+		port->cbits_to_laptop |= SDIO_ACM_CTRL_DSR;
+
+	if (port->port_usb && port->port_usb->send_modem_ctrl_bits)
+		port->port_usb->send_modem_ctrl_bits(port->port_usb,
+					port->cbits_to_laptop);
+}
+
+void gsdio_ch_notify(void *_dev, unsigned event)
+{
+	struct gsdio_port *port = _dev;
+
+	pr_debug("%s: port:%p port#%d event:%s\n", __func__,
+		port, port->port_num,
+		event == 1 ? "READ AVAIL" : "WRITE_AVAIL");
+
+	if (event == SDIO_EVENT_DATA_WRITE_AVAIL)
+		queue_work(gsdio_wq, &port->push);
+	if (event == SDIO_EVENT_DATA_READ_AVAIL)
+		queue_work(gsdio_wq, &port->pull);
+}
+
+static void gsdio_open_work(struct work_struct *w)
+{
+	struct gsdio_port *port =
+			container_of(w, struct gsdio_port, sdio_open_work.work);
+	struct sdio_port_info *pi = port->sport_info;
+	struct gserial *gser;
+	int ret;
+	int ctrl_bits;
+	int startio;
+
+	ret = sdio_open(pi->data_ch_name, &pi->ch, port, gsdio_ch_notify);
+	if (ret) {
+		pr_err("%s: port:%p port#%d unable to open sdio ch:%s\n",
+				__func__, port, port->port_num,
+				pi->data_ch_name);
+		return;
+	}
+
+	port->ctrl_ch_err = 0;
+	ret = sdio_cmux_open(pi->ctrl_ch_id, 0, 0,
+			gsdio_ctrl_modem_status, port);
+	if (ret) {
+		pr_err("%s: port:%p port#%d unable to open ctrl ch:%d\n",
+				__func__, port, port->port_num, pi->ctrl_ch_id);
+		port->ctrl_ch_err = 1;
+	}
+
+	/* check for latest status update from modem */
+	if (!port->ctrl_ch_err) {
+		ctrl_bits = sdio_cmux_tiocmget(pi->ctrl_ch_id);
+		gsdio_ctrl_modem_status(ctrl_bits, port);
+	}
+
+	pr_debug("%s: SDIO data:%s ctrl:%d are open\n", __func__,
+					pi->data_ch_name,
+					pi->ctrl_ch_id);
+
+	port->sdio_open = 1;
+
+	/* start tx if usb is open already */
+	spin_lock_irq(&port->port_lock);
+	startio = port->port_usb ? 1 : 0;
+	gser = port->port_usb;
+	spin_unlock_irq(&port->port_lock);
+
+	if (startio) {
+		pr_debug("%s: USB is already open, start io\n", __func__);
+		gsdio_start_io(port);
+		 if (gser->send_modem_ctrl_bits)
+			gser->send_modem_ctrl_bits(gser, port->cbits_to_laptop);
+	}
+}
+
+#define SDIO_CH_NAME_MAX_LEN	9
+#define SDIO_OPEN_DELAY		msecs_to_jiffies(10000)
+static int gsdio_ch_remove(struct platform_device *dev)
+{
+	struct gsdio_port	*port;
+	struct sdio_port_info	*pi;
+	int i;
+	unsigned long		flags;
+
+	pr_debug("%s: name:%s\n", __func__, dev->name);
+
+	for (i = 0; i < n_sdio_ports; i++) {
+		port = sdio_ports[i].port;
+		pi = port->sport_info;
+
+		if (!strncmp(pi->data_ch_name, dev->name,
+					SDIO_CH_NAME_MAX_LEN)) {
+			struct gserial *gser = port->port_usb;
+
+			port->sdio_open = 0;
+			port->sdio_probe = 0;
+			port->ctrl_ch_err = 1;
+
+			/* check if usb cable is connected */
+			if (!gser)
+				continue;
+
+			/* indicated call status to usb host */
+			gsdio_ctrl_modem_status(0, port);
+
+			usb_ep_fifo_flush(gser->in);
+			usb_ep_fifo_flush(gser->out);
+
+			cancel_work_sync(&port->push);
+			cancel_work_sync(&port->pull);
+
+			spin_lock_irqsave(&port->port_lock, flags);
+			gsdio_free_requests(gser->out, &port->read_pool);
+			gsdio_free_requests(gser->out, &port->read_queue);
+			gsdio_free_requests(gser->in, &port->write_pool);
+
+			port->rp_len = 0;
+			port->rq_len = 0;
+			port->wp_len = 0;
+			port->n_read = 0;
+			spin_unlock_irqrestore(&port->port_lock, flags);
+
+		}
+	}
+
+	return 0;
+}
+
+static int gsdio_ch_probe(struct platform_device *dev)
+{
+	struct gsdio_port	*port;
+	struct sdio_port_info	*pi;
+	int i;
+
+	pr_debug("%s: name:%s\n", __func__, dev->name);
+
+	for (i = 0; i < n_sdio_ports; i++) {
+		port = sdio_ports[i].port;
+		pi = port->sport_info;
+
+		pr_debug("%s: sdio_ch_name:%s dev_name:%s\n", __func__,
+				pi->data_ch_name, dev->name);
+
+		/* unfortunately cmux channle might not be ready even if
+		 * sdio channel is ready. as we dont have good notification
+		 * mechanism schedule a delayed work
+		 */
+		if (!strncmp(pi->data_ch_name, dev->name,
+					SDIO_CH_NAME_MAX_LEN)) {
+			port->sdio_probe = 1;
+			queue_delayed_work(gsdio_wq,
+				&port->sdio_open_work, SDIO_OPEN_DELAY);
+			return 0;
+		}
+	}
+
+	pr_info("%s: name:%s is not found\n", __func__, dev->name);
+
+	return -ENODEV;
+}
+
+int gsdio_port_alloc(unsigned portno,
+		struct usb_cdc_line_coding *coding,
+		struct sdio_port_info *pi)
+{
+	struct gsdio_port *port;
+	struct platform_driver *pdriver;
+
+	port = kzalloc(sizeof(struct gsdio_port), GFP_KERNEL);
+	if (!port) {
+		pr_err("%s: port allocation failed\n", __func__);
+		return -ENOMEM;
+	}
+
+	port->port_num = portno;
+	spin_lock_init(&port->port_lock);
+	port->line_coding = *coding;
+
+	/* READ: read from usb and write into sdio */
+	INIT_LIST_HEAD(&port->read_pool);
+	INIT_LIST_HEAD(&port->read_queue);
+	INIT_WORK(&port->push, gsdio_rx_push);
+
+	INIT_LIST_HEAD(&port->write_pool);
+	INIT_WORK(&port->pull, gsdio_tx_pull);
+
+	INIT_WORK(&port->notify_modem, gsdio_ctrl_wq);
+
+	INIT_DELAYED_WORK(&port->sdio_open_work, gsdio_open_work);
+
+	sdio_ports[portno].port = port;
+
+	port->sport_info = pi;
+	pdriver = &sdio_ports[portno].gsdio_ch;
+
+	pdriver->probe = gsdio_ch_probe;
+	pdriver->remove = gsdio_ch_remove;
+	pdriver->driver.name = pi->data_ch_name;
+	pdriver->driver.owner = THIS_MODULE;
+
+	pr_debug("%s: port:%p port#%d sdio_name: %s\n", __func__,
+			port, port->port_num, pi->data_ch_name);
+
+	platform_driver_register(pdriver);
+
+	pr_debug("%s: port:%p port#%d\n", __func__, port, port->port_num);
+
+	return 0;
+}
+
+int gsdio_connect(struct gserial *gser, u8 portno)
+{
+	struct gsdio_port *port;
+	int ret = 0;
+	unsigned long flags;
+
+	if (portno >= n_sdio_ports) {
+		pr_err("%s: invalid portno#%d\n", __func__, portno);
+		return -EINVAL;
+	}
+
+	if (!gser) {
+		pr_err("%s: gser is null\n", __func__);
+		return -EINVAL;
+	}
+
+	port = sdio_ports[portno].port;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	port->port_usb = gser;
+	gser->notify_modem = gsdio_ctrl_notify_modem;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	ret = usb_ep_enable(gser->in);
+	if (ret) {
+		pr_err("%s: failed to enable in ep w/ err:%d\n",
+					__func__, ret);
+		port->port_usb = 0;
+		return ret;
+	}
+	gser->in->driver_data = port;
+
+	ret = usb_ep_enable(gser->out);
+	if (ret) {
+		pr_err("%s: failed to enable in ep w/ err:%d\n",
+					__func__, ret);
+		usb_ep_disable(gser->in);
+		port->port_usb = 0;
+		gser->in->driver_data = 0;
+		return ret;
+	}
+	gser->out->driver_data = port;
+
+	if (port->sdio_open) {
+		pr_debug("%s: sdio is already open, start io\n", __func__);
+		gsdio_start_io(port);
+		 if (gser->send_modem_ctrl_bits)
+			gser->send_modem_ctrl_bits(gser, port->cbits_to_laptop);
+	}
+
+	return 0;
+}
+
+void gsdio_disconnect(struct gserial *gser, u8 portno)
+{
+	unsigned long flags;
+	struct gsdio_port *port;
+
+	if (portno >= n_sdio_ports) {
+		pr_err("%s: invalid portno#%d\n", __func__, portno);
+		return;
+	}
+
+	if (!gser) {
+		pr_err("%s: gser is null\n", __func__);
+		return;
+	}
+
+	port = sdio_ports[portno].port;
+
+	/* send dtr zero to modem to notify disconnect */
+	port->cbits_to_modem = 0;
+	queue_work(gsdio_wq, &port->notify_modem);
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	port->port_usb = 0;
+	port->nbytes_tomodem = 0;
+	port->nbytes_tolaptop = 0;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	/* disable endpoints, aborting down any active I/O */
+	usb_ep_disable(gser->out);
+
+	usb_ep_disable(gser->in);
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	gsdio_free_requests(gser->out, &port->read_pool);
+	gsdio_free_requests(gser->out, &port->read_queue);
+	gsdio_free_requests(gser->in, &port->write_pool);
+
+	port->rp_len = 0;
+	port->rq_len = 0;
+	port->wp_len = 0;
+	port->n_read = 0;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+#if defined(CONFIG_DEBUG_FS)
+static char debug_buffer[PAGE_SIZE];
+
+static ssize_t debug_sdio_read_stats(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	struct gsdio_port *port;
+	char *buf;
+	unsigned long flags;
+	int i = 0;
+	int temp = 0;
+	int ret;
+
+	buf = kzalloc(sizeof(char) * 1024, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	while (i < n_sdio_ports) {
+		port = sdio_ports[i].port;
+		spin_lock_irqsave(&port->port_lock, flags);
+		temp += scnprintf(buf + temp, PAGE_SIZE - temp,
+				"###PORT:%d port:%p###\n"
+				"nbytes_tolaptop: %lu\n"
+				"nbytes_tomodem:  %lu\n"
+				"cbits_to_modem:  %u\n"
+				"cbits_to_laptop: %u\n"
+				"read_pool_len:   %lu\n"
+				"read_queue_len:  %lu\n"
+				"write_pool_len:  %lu\n"
+				"n_read:          %u\n"
+				"sdio_open:       %d\n"
+				"sdio_probe:      %d\n",
+				i, port,
+				port->nbytes_tolaptop, port->nbytes_tomodem,
+				port->cbits_to_modem, port->cbits_to_laptop,
+				port->rp_len, port->rq_len, port->wp_len,
+				port->n_read,
+				port->sdio_open, port->sdio_probe);
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		i++;
+	}
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+	kfree(buf);
+
+	return ret;
+}
+
+static ssize_t debug_sdio_reset_stats(struct file *file, const char __user *buf,
+				 size_t count, loff_t *ppos)
+{
+	struct gsdio_port *port;
+	unsigned long flags;
+	int i = 0;
+
+	while (i < n_sdio_ports) {
+		port = sdio_ports[i].port;
+
+		spin_lock_irqsave(&port->port_lock, flags);
+		port->nbytes_tolaptop = 0;
+		port->nbytes_tomodem = 0;
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		i++;
+	}
+
+	return count;
+}
+
+static int debug_sdio_open(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static const struct file_operations debug_gsdio_ops = {
+	.open = debug_sdio_open,
+	.read = debug_sdio_read_stats,
+	.write = debug_sdio_reset_stats,
+};
+
+static void gsdio_debugfs_init(void)
+{
+	struct dentry *dent;
+
+	dent = debugfs_create_dir("usb_gsdio", 0);
+	if (IS_ERR(dent))
+		return;
+
+	debugfs_create_file("status", 0444, dent, 0, &debug_gsdio_ops);
+}
+#else
+static void gsdio_debugfs_init(void)
+{
+	return;
+}
+#endif
+
+/* connect, disconnect, alloc_requests, free_requests */
+int gsdio_setup(struct usb_gadget *g, unsigned count)
+{
+	struct usb_cdc_line_coding	coding;
+	int i;
+	int ret = 0;
+
+	pr_debug("%s: gadget:(%p) count:%d\n", __func__, g, count);
+
+	if (count == 0 || count > SDIO_N_PORTS) {
+		pr_err("%s: invalid number of ports count:%d max_ports:%d\n",
+				__func__, count, SDIO_N_PORTS);
+		return -EINVAL;
+	}
+
+	coding.dwDTERate = cpu_to_le32(9600);
+	coding.bCharFormat = 8;
+	coding.bParityType = USB_CDC_NO_PARITY;
+	coding.bDataBits = USB_CDC_1_STOP_BITS;
+
+	gsdio_wq = create_singlethread_workqueue("k_gserial");
+	if (!gsdio_wq) {
+		pr_err("%s: unable to create workqueue gsdio_wq\n",
+				__func__);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < count; i++) {
+		mutex_init(&sdio_ports[i].lock);
+		ret = gsdio_port_alloc(i, &coding, sport_info + i);
+		n_sdio_ports++;
+		if (ret) {
+			n_sdio_ports--;
+			pr_err("%s: sdio logical port allocation failed\n",
+					__func__);
+			goto free_sdio_ports;
+		}
+
+#ifdef DEBUG
+		/* REVISIT: create one file per port
+		 * or do not create any file
+		 */
+		if (i == 0) {
+			ret = device_create_file(&g->dev, &dev_attr_input);
+			if (ret)
+				pr_err("%s: unable to create device file\n",
+						__func__);
+		}
+#endif
+
+	}
+
+	gsdio_debugfs_init();
+
+	return 0;
+
+free_sdio_ports:
+	for (i = 0; i < n_sdio_ports; i++)
+		gsdio_port_free(i);
+	destroy_workqueue(gsdio_wq);
+
+	return ret;
+}
+
+/* TODO: Add gserial_cleanup */
diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c
index 380a87f..de93049 100644
--- a/drivers/usb/gadget/u_serial.c
+++ b/drivers/usb/gadget/u_serial.c
@@ -26,6 +26,7 @@
 #include <linux/tty_flip.h>
 #include <linux/slab.h>
 #include <linux/export.h>
+#include <linux/debugfs.h>
 
 #include "u_serial.h"
 
@@ -78,9 +79,14 @@
  * next layer of buffering.  For TX that's a circular buffer; for RX
  * consider it a NOP.  A third layer is provided by the TTY code.
  */
-#define QUEUE_SIZE		16
+#define TX_QUEUE_SIZE		8
+#define TX_BUF_SIZE		4096
 #define WRITE_BUF_SIZE		8192		/* TX only */
 
+#define RX_QUEUE_SIZE		8
+#define RX_BUF_SIZE		4096
+
+
 /* circular buffer */
 struct gs_buf {
 	unsigned		buf_size;
@@ -110,7 +116,7 @@
 	int read_allocated;
 	struct list_head	read_queue;
 	unsigned		n_read;
-	struct tasklet_struct	push;
+	struct work_struct	push;
 
 	struct list_head	write_pool;
 	int write_started;
@@ -120,16 +126,22 @@
 
 	/* REVISIT this state ... */
 	struct usb_cdc_line_coding port_line_coding;	/* 8-N-1 etc */
+	unsigned long           nbytes_from_host;
+	unsigned long           nbytes_to_tty;
+	unsigned long           nbytes_from_tty;
+	unsigned long           nbytes_to_host;
 };
 
 /* increase N_PORTS if you need more */
-#define N_PORTS		4
+#define N_PORTS		8
 static struct portmaster {
 	struct mutex	lock;			/* protect open/close */
 	struct gs_port	*port;
 } ports[N_PORTS];
 static unsigned	n_ports;
 
+static struct workqueue_struct *gserial_wq;
+
 #define GS_CLOSE_TIMEOUT		15		/* seconds */
 
 
@@ -362,18 +374,39 @@
 	struct list_head	*pool = &port->write_pool;
 	struct usb_ep		*in = port->port_usb->in;
 	int			status = 0;
+	static long 		prev_len;
 	bool			do_tty_wake = false;
 
 	while (!list_empty(pool)) {
 		struct usb_request	*req;
 		int			len;
 
-		if (port->write_started >= QUEUE_SIZE)
+		if (port->write_started >= TX_QUEUE_SIZE)
 			break;
 
 		req = list_entry(pool->next, struct usb_request, list);
-		len = gs_send_packet(port, req->buf, in->maxpacket);
+		len = gs_send_packet(port, req->buf, TX_BUF_SIZE);
 		if (len == 0) {
+			/* Queue zero length packet explicitly to make it
+			 * work with UDCs which don't support req->zero flag
+			 */
+			if (prev_len && (prev_len % in->maxpacket == 0)) {
+				req->length = 0;
+				list_del(&req->list);
+				spin_unlock(&port->port_lock);
+				status = usb_ep_queue(in, req, GFP_ATOMIC);
+				spin_lock(&port->port_lock);
+				if (!port->port_usb) {
+					gs_free_req(in, req);
+					break;
+				}
+				if (status) {
+					printk(KERN_ERR "%s: %s err %d\n",
+					__func__, "queue", status);
+					list_add(&req->list, pool);
+				}
+				prev_len = 0;
+			}
 			wake_up_interruptible(&port->drain_wait);
 			break;
 		}
@@ -381,7 +414,6 @@
 
 		req->length = len;
 		list_del(&req->list);
-		req->zero = (gs_buf_data_avail(&port->port_write_buf) == 0);
 
 		pr_vdebug(PREFIX "%d: tx len=%d, 0x%02x 0x%02x 0x%02x ...\n",
 				port->port_num, len, *((u8 *)req->buf),
@@ -397,19 +429,25 @@
 		spin_unlock(&port->port_lock);
 		status = usb_ep_queue(in, req, GFP_ATOMIC);
 		spin_lock(&port->port_lock);
-
+		/*
+		 * If port_usb is NULL, gserial disconnect is called
+		 * while the spinlock is dropped and all requests are
+		 * freed. Free the current request here.
+		 */
+		if (!port->port_usb) {
+			do_tty_wake = false;
+			gs_free_req(in, req);
+			break;
+		}
 		if (status) {
 			pr_debug("%s: %s %s err %d\n",
 					__func__, "queue", in->name, status);
 			list_add(&req->list, pool);
 			break;
 		}
+		prev_len = req->length;
+		port->nbytes_from_tty += req->length;
 
-		port->write_started++;
-
-		/* abort immediately after disconnect */
-		if (!port->port_usb)
-			break;
 	}
 
 	if (do_tty_wake && port->port_tty)
@@ -428,6 +466,7 @@
 {
 	struct list_head	*pool = &port->read_pool;
 	struct usb_ep		*out = port->port_usb->out;
+	unsigned		started = 0;
 
 	while (!list_empty(pool)) {
 		struct usb_request	*req;
@@ -439,12 +478,12 @@
 		if (!tty)
 			break;
 
-		if (port->read_started >= QUEUE_SIZE)
+		if (port->read_started >= RX_QUEUE_SIZE)
 			break;
 
 		req = list_entry(pool->next, struct usb_request, list);
 		list_del(&req->list);
-		req->length = out->maxpacket;
+		req->length = RX_BUF_SIZE;
 
 		/* drop lock while we call out; the controller driver
 		 * may need to call us back (e.g. for disconnect)
@@ -452,7 +491,16 @@
 		spin_unlock(&port->port_lock);
 		status = usb_ep_queue(out, req, GFP_ATOMIC);
 		spin_lock(&port->port_lock);
-
+		/*
+		 * If port_usb is NULL, gserial disconnect is called
+		 * while the spinlock is dropped and all requests are
+		 * freed. Free the current request here.
+		 */
+		if (!port->port_usb) {
+			started = 0;
+			gs_free_req(out, req);
+			break;
+		}
 		if (status) {
 			pr_debug("%s: %s %s err %d\n",
 					__func__, "queue", out->name, status);
@@ -461,9 +509,6 @@
 		}
 		port->read_started++;
 
-		/* abort immediately after disconnect */
-		if (!port->port_usb)
-			break;
 	}
 	return port->read_started;
 }
@@ -478,9 +523,9 @@
  * So QUEUE_SIZE packets plus however many the FIFO holds (usually two)
  * can be buffered before the TTY layer's buffers (currently 64 KB).
  */
-static void gs_rx_push(unsigned long _port)
+static void gs_rx_push(struct work_struct *w)
 {
-	struct gs_port		*port = (void *)_port;
+	struct gs_port		*port = container_of(w, struct gs_port, push);
 	struct tty_struct	*tty;
 	struct list_head	*queue = &port->read_queue;
 	bool			disconnect = false;
@@ -533,6 +578,7 @@
 			}
 
 			count = tty_insert_flip_string(tty, packet, size);
+			port->nbytes_to_tty += count;
 			if (count)
 				do_push = true;
 			if (count != size) {
@@ -556,19 +602,18 @@
 	if (tty && do_push)
 		tty_flip_buffer_push(tty);
 
-
 	/* We want our data queue to become empty ASAP, keeping data
 	 * in the tty and ldisc (not here).  If we couldn't push any
 	 * this time around, there may be trouble unless there's an
 	 * implicit tty_unthrottle() call on its way...
 	 *
-	 * REVISIT we should probably add a timer to keep the tasklet
+	 * REVISIT we should probably add a timer to keep the work queue
 	 * from starving ... but it's not clear that case ever happens.
 	 */
 	if (!list_empty(queue) && tty) {
 		if (!test_bit(TTY_THROTTLED, &tty->flags)) {
 			if (do_push)
-				tasklet_schedule(&port->push);
+				queue_work(gserial_wq, &port->push);
 			else
 				pr_warning(PREFIX "%d: RX not scheduled?\n",
 					port->port_num);
@@ -585,19 +630,23 @@
 static void gs_read_complete(struct usb_ep *ep, struct usb_request *req)
 {
 	struct gs_port	*port = ep->driver_data;
+	unsigned long flags;
 
 	/* Queue all received data until the tty layer is ready for it. */
-	spin_lock(&port->port_lock);
+	spin_lock_irqsave(&port->port_lock, flags);
+	port->nbytes_from_host += req->actual;
 	list_add_tail(&req->list, &port->read_queue);
-	tasklet_schedule(&port->push);
-	spin_unlock(&port->port_lock);
+	queue_work(gserial_wq, &port->push);
+	spin_unlock_irqrestore(&port->port_lock, flags);
 }
 
 static void gs_write_complete(struct usb_ep *ep, struct usb_request *req)
 {
 	struct gs_port	*port = ep->driver_data;
+	unsigned long flags;
 
-	spin_lock(&port->port_lock);
+	spin_lock_irqsave(&port->port_lock, flags);
+	port->nbytes_to_host += req->actual;
 	list_add(&req->list, &port->write_pool);
 	port->write_started--;
 
@@ -609,7 +658,8 @@
 		/* FALL THROUGH */
 	case 0:
 		/* normal completion */
-		gs_start_tx(port);
+		if (port->port_usb)
+			gs_start_tx(port);
 		break;
 
 	case -ESHUTDOWN:
@@ -618,7 +668,7 @@
 		break;
 	}
 
-	spin_unlock(&port->port_lock);
+	spin_unlock_irqrestore(&port->port_lock, flags);
 }
 
 static void gs_free_requests(struct usb_ep *ep, struct list_head *head,
@@ -636,19 +686,18 @@
 }
 
 static int gs_alloc_requests(struct usb_ep *ep, struct list_head *head,
-		void (*fn)(struct usb_ep *, struct usb_request *),
+		int num, int size, void (*fn)(struct usb_ep *, struct usb_request *),
 		int *allocated)
 {
 	int			i;
 	struct usb_request	*req;
-	int n = allocated ? QUEUE_SIZE - *allocated : QUEUE_SIZE;
 
 	/* Pre-allocate up to QUEUE_SIZE transfers, but if we can't
 	 * do quite that many this time, don't fail ... we just won't
 	 * be as speedy as we might otherwise be.
 	 */
-	for (i = 0; i < n; i++) {
-		req = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC);
+	for (i = 0; i < num; i++) {
+		req = gs_alloc_req(ep, size, GFP_ATOMIC);
 		if (!req)
 			return list_empty(head) ? -ENOMEM : 0;
 		req->complete = fn;
@@ -681,13 +730,13 @@
 	 * configurations may use different endpoints with a given port;
 	 * and high speed vs full speed changes packet sizes too.
 	 */
-	status = gs_alloc_requests(ep, head, gs_read_complete,
-		&port->read_allocated);
+	status = gs_alloc_requests(ep, head, RX_QUEUE_SIZE, RX_BUF_SIZE,
+			 gs_read_complete, &port->read_allocated);
 	if (status)
 		return status;
 
 	status = gs_alloc_requests(port->port_usb->in, &port->write_pool,
-			gs_write_complete, &port->write_allocated);
+			TX_QUEUE_SIZE, TX_BUF_SIZE, gs_write_complete, &port->write_allocated);
 	if (status) {
 		gs_free_requests(ep, head, &port->read_allocated);
 		return status;
@@ -697,6 +746,8 @@
 	port->n_read = 0;
 	started = gs_start_rx(port);
 
+	if (!port->port_usb)
+		return -EIO;
 	/* unblock any pending writes into our circular buffer */
 	if (started) {
 		tty_wakeup(port->port_tty);
@@ -798,6 +849,13 @@
 	port->open_count = 1;
 	port->openclose = false;
 
+	/* low_latency means ldiscs work is carried in the same context
+	 * of tty_flip_buffer_push. The same can be called from IRQ with
+	 * low_latency = 0. But better to use a dedicated worker thread
+	 * to push the data.
+	 */
+	tty->low_latency = 1;
+
 	/* if connected, start the I/O stream */
 	if (port->port_usb) {
 		struct gserial	*gser = port->port_usb;
@@ -871,7 +929,7 @@
 
 	/* Iff we're disconnected, there can be no I/O in flight so it's
 	 * ok to free the circular buffer; else just scrub it.  And don't
-	 * let the push tasklet fire again until we're re-opened.
+	 * let the push work queue fire again until we're re-opened.
 	 */
 	if (gser == NULL)
 		gs_buf_free(&port->port_write_buf);
@@ -887,6 +945,22 @@
 			port->port_num, tty, file);
 
 	wake_up_interruptible(&port->close_wait);
+
+	/*
+	 * Freeing the previously queued requests as they are
+	 * allocated again as a part of gs_open()
+	 */
+	if (port->port_usb) {
+		spin_unlock_irq(&port->port_lock);
+		usb_ep_fifo_flush(gser->out);
+		usb_ep_fifo_flush(gser->in);
+		spin_lock_irq(&port->port_lock);
+		gs_free_requests(gser->out, &port->read_queue, NULL);
+		gs_free_requests(gser->out, &port->read_pool, NULL);
+		gs_free_requests(gser->in, &port->write_pool, NULL);
+	}
+	port->read_allocated = port->read_started =
+		port->write_allocated = port->write_started = 0;
 exit:
 	spin_unlock_irq(&port->port_lock);
 }
@@ -985,7 +1059,7 @@
 		 * rts/cts, or other handshaking with the host, but if the
 		 * read queue backs up enough we'll be NAKing OUT packets.
 		 */
-		tasklet_schedule(&port->push);
+		queue_work(gserial_wq, &port->push);
 		pr_vdebug(PREFIX "%d: unthrottle\n", port->port_num);
 	}
 	spin_unlock_irqrestore(&port->port_lock, flags);
@@ -1009,6 +1083,77 @@
 	return status;
 }
 
+static int gs_tiocmget(struct tty_struct *tty)
+{
+	struct gs_port	*port = tty->driver_data;
+	struct gserial	*gser;
+	unsigned int result = 0;
+
+	spin_lock_irq(&port->port_lock);
+	gser = port->port_usb;
+	if (!gser) {
+		result = -ENODEV;
+		goto fail;
+	}
+
+	if (gser->get_dtr)
+		result |= (gser->get_dtr(gser) ? TIOCM_DTR : 0);
+
+	if (gser->get_rts)
+		result |= (gser->get_rts(gser) ? TIOCM_RTS : 0);
+
+	if (gser->serial_state & TIOCM_CD)
+		result |= TIOCM_CD;
+
+	if (gser->serial_state & TIOCM_RI)
+		result |= TIOCM_RI;
+fail:
+	spin_unlock_irq(&port->port_lock);
+	return result;
+}
+
+static int gs_tiocmset(struct tty_struct *tty,
+	unsigned int set, unsigned int clear)
+{
+	struct gs_port	*port = tty->driver_data;
+	struct gserial *gser;
+	int	status = 0;
+
+	spin_lock_irq(&port->port_lock);
+	gser = port->port_usb;
+	if (!gser) {
+		status = -ENODEV;
+		goto fail;
+	}
+
+	if (set & TIOCM_RI) {
+		if (gser->send_ring_indicator) {
+			gser->serial_state |= TIOCM_RI;
+			status = gser->send_ring_indicator(gser, 1);
+		}
+	}
+	if (clear & TIOCM_RI) {
+		if (gser->send_ring_indicator) {
+			gser->serial_state &= ~TIOCM_RI;
+			status = gser->send_ring_indicator(gser, 0);
+		}
+	}
+	if (set & TIOCM_CD) {
+		if (gser->send_carrier_detect) {
+			gser->serial_state |= TIOCM_CD;
+			status = gser->send_carrier_detect(gser, 1);
+		}
+	}
+	if (clear & TIOCM_CD) {
+		if (gser->send_carrier_detect) {
+			gser->serial_state &= ~TIOCM_CD;
+			status = gser->send_carrier_detect(gser, 0);
+		}
+	}
+fail:
+	spin_unlock_irq(&port->port_lock);
+	return status;
+}
 static const struct tty_operations gs_tty_ops = {
 	.open =			gs_open,
 	.close =		gs_close,
@@ -1019,6 +1164,8 @@
 	.chars_in_buffer =	gs_chars_in_buffer,
 	.unthrottle =		gs_unthrottle,
 	.break_ctl =		gs_break_ctl,
+	.tiocmget  =		gs_tiocmget,
+	.tiocmset  =		gs_tiocmset,
 };
 
 /*-------------------------------------------------------------------------*/
@@ -1038,7 +1185,7 @@
 	init_waitqueue_head(&port->close_wait);
 	init_waitqueue_head(&port->drain_wait);
 
-	tasklet_init(&port->push, gs_rx_push, (unsigned long) port);
+	INIT_WORK(&port->push, gs_rx_push);
 
 	INIT_LIST_HEAD(&port->read_pool);
 	INIT_LIST_HEAD(&port->read_queue);
@@ -1052,6 +1199,116 @@
 	return 0;
 }
 
+
+#if defined(CONFIG_DEBUG_FS)
+
+#define BUF_SIZE	512
+
+static ssize_t debug_read_status(struct file *file, char __user *ubuf,
+					size_t count, loff_t *ppos)
+{
+	struct gs_port *ui_dev = file->private_data;
+	struct tty_struct       *tty;
+	struct gserial		*gser;
+	char *buf;
+	unsigned long flags;
+	int i = 0;
+	int ret;
+	int result = 0;
+
+	tty = ui_dev->port_tty;
+	gser = ui_dev->port_usb;
+
+	buf = kzalloc(sizeof(char) * BUF_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	spin_lock_irqsave(&ui_dev->port_lock, flags);
+
+	i += scnprintf(buf + i, BUF_SIZE - i,
+		"nbytes_from_host: %lu\n", ui_dev->nbytes_from_host);
+
+	i += scnprintf(buf + i, BUF_SIZE - i,
+		"nbytes_to_tty: %lu\n", ui_dev->nbytes_to_tty);
+
+	i += scnprintf(buf + i, BUF_SIZE - i, "nbytes_with_usb_OUT_txr: %lu\n",
+			(ui_dev->nbytes_from_host - ui_dev->nbytes_to_tty));
+
+	i += scnprintf(buf + i, BUF_SIZE - i,
+		"nbytes_from_tty: %lu\n", ui_dev->nbytes_from_tty);
+
+	i += scnprintf(buf + i, BUF_SIZE - i,
+		"nbytes_to_host: %lu\n", ui_dev->nbytes_to_host);
+
+	i += scnprintf(buf + i, BUF_SIZE - i, "nbytes_with_usb_IN_txr: %lu\n",
+			(ui_dev->nbytes_from_tty - ui_dev->nbytes_to_host));
+
+	if (tty)
+		i += scnprintf(buf + i, BUF_SIZE - i,
+			"tty_flags: %lu\n", tty->flags);
+
+	if (gser->get_dtr) {
+		result |= (gser->get_dtr(gser) ? TIOCM_DTR : 0);
+		i += scnprintf(buf + i, BUF_SIZE - i,
+			"DTR_status: %d\n", result);
+	}
+
+	spin_unlock_irqrestore(&ui_dev->port_lock, flags);
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, i);
+
+	kfree(buf);
+
+	return ret;
+}
+
+static ssize_t debug_write_reset(struct file *file, const char __user *buf,
+			size_t count, loff_t *ppos)
+{
+	struct gs_port *ui_dev = file->private_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ui_dev->port_lock, flags);
+	ui_dev->nbytes_from_host = ui_dev->nbytes_to_tty =
+			ui_dev->nbytes_from_tty = ui_dev->nbytes_to_host = 0;
+	spin_unlock_irqrestore(&ui_dev->port_lock, flags);
+
+	return count;
+}
+
+static int serial_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+const struct file_operations debug_rst_ops = {
+	.open = serial_debug_open,
+	.write = debug_write_reset,
+};
+
+const struct file_operations debug_adb_ops = {
+	.open = serial_debug_open,
+	.read = debug_read_status,
+};
+
+static void usb_debugfs_init(struct gs_port *ui_dev, int port_num)
+{
+	struct dentry *dent;
+	char buf[48];
+
+	snprintf(buf, 48, "usb_serial%d", port_num);
+	dent = debugfs_create_dir(buf, 0);
+	if (IS_ERR(dent))
+		return;
+
+	debugfs_create_file("readstatus", 0444, dent, ui_dev, &debug_adb_ops);
+	debugfs_create_file("reset", 0222, dent, ui_dev, &debug_rst_ops);
+}
+#else
+static void usb_debugfs_init(struct gs_port *ui_dev) {}
+#endif
+
 /**
  * gserial_setup - initialize TTY driver for one or more ports
  * @g: gadget to associate with these ports
@@ -1090,7 +1347,8 @@
 
 	gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
 	gs_tty_driver->subtype = SERIAL_TYPE_NORMAL;
-	gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+	gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV
+				| TTY_DRIVER_RESET_TERMIOS;
 	gs_tty_driver->init_termios = tty_std_termios;
 
 	/* 9600-8-N-1 ... matches defaults expected by "usbser.sys" on
@@ -1109,6 +1367,12 @@
 
 	tty_set_operations(gs_tty_driver, &gs_tty_ops);
 
+	gserial_wq = create_singlethread_workqueue("k_gserial");
+	if (!gserial_wq) {
+		status = -ENOMEM;
+		goto fail;
+	}
+
 	/* make devices be openable */
 	for (i = 0; i < count; i++) {
 		mutex_init(&ports[i].lock);
@@ -1123,6 +1387,7 @@
 	/* export the driver ... */
 	status = tty_register_driver(gs_tty_driver);
 	if (status) {
+		put_tty_driver(gs_tty_driver);
 		pr_err("%s: cannot register, err %d\n",
 				__func__, status);
 		goto fail;
@@ -1138,6 +1403,9 @@
 				__func__, i, PTR_ERR(tty_dev));
 	}
 
+	for (i = 0; i < count; i++)
+		usb_debugfs_init(ports[i].port, i);
+
 	pr_debug("%s: registered %d ttyGS* device%s\n", __func__,
 			count, (count == 1) ? "" : "s");
 
@@ -1145,6 +1413,8 @@
 fail:
 	while (count--)
 		kfree(ports[count].port);
+	if (gserial_wq)
+		destroy_workqueue(gserial_wq);
 	put_tty_driver(gs_tty_driver);
 	gs_tty_driver = NULL;
 	return status;
@@ -1191,7 +1461,7 @@
 		ports[i].port = NULL;
 		mutex_unlock(&ports[i].lock);
 
-		tasklet_kill(&port->push);
+		cancel_work_sync(&port->push);
 
 		/* wait for old opens to finish */
 		wait_event(port->close_wait, gs_closed(port));
@@ -1202,6 +1472,7 @@
 	}
 	n_ports = 0;
 
+	destroy_workqueue(gserial_wq);
 	tty_unregister_driver(gs_tty_driver);
 	put_tty_driver(gs_tty_driver);
 	gs_tty_driver = NULL;
@@ -1340,5 +1611,8 @@
 	port->read_allocated = port->read_started =
 		port->write_allocated = port->write_started = 0;
 
+	port->nbytes_from_host = port->nbytes_to_tty =
+		port->nbytes_from_tty = port->nbytes_to_host = 0;
+
 	spin_unlock_irqrestore(&port->port_lock, flags);
 }
diff --git a/drivers/usb/gadget/u_serial.h b/drivers/usb/gadget/u_serial.h
index 9b0fe64..dadc507 100644
--- a/drivers/usb/gadget/u_serial.h
+++ b/drivers/usb/gadget/u_serial.h
@@ -38,11 +38,22 @@
 
 	/* REVISIT avoid this CDC-ACM support harder ... */
 	struct usb_cdc_line_coding port_line_coding;	/* 9600-8-N-1 etc */
+	u16				serial_state;
+
+	/* control signal callbacks*/
+	unsigned int (*get_dtr)(struct gserial *p);
+	unsigned int (*get_rts)(struct gserial *p);
 
 	/* notification callbacks */
 	void (*connect)(struct gserial *p);
 	void (*disconnect)(struct gserial *p);
 	int (*send_break)(struct gserial *p, int duration);
+	unsigned int (*send_carrier_detect)(struct gserial *p, unsigned int);
+	unsigned int (*send_ring_indicator)(struct gserial *p, unsigned int);
+	int (*send_modem_ctrl_bits)(struct gserial *p, int ctrl_bits);
+
+	/* notification changes to modem */
+	void (*notify_modem)(void *gser, u8 portno, int ctrl_bits);
 };
 
 /* utilities to allocate/free request and buffer */
@@ -57,6 +68,15 @@
 int gserial_connect(struct gserial *, u8 port_num);
 void gserial_disconnect(struct gserial *);
 
+/* sdio related functions */
+int gsdio_setup(struct usb_gadget *g, unsigned n_ports);
+int gsdio_connect(struct gserial *, u8 port_num);
+void gsdio_disconnect(struct gserial *, u8 portno);
+
+int gsmd_setup(struct usb_gadget *g, unsigned n_ports);
+int gsmd_connect(struct gserial *, u8 port_num);
+void gsmd_disconnect(struct gserial *, u8 portno);
+
 /* functions are bound to configurations by a config or gadget driver */
 int acm_bind_config(struct usb_configuration *c, u8 port_num);
 int gser_bind_config(struct usb_configuration *c, u8 port_num);
diff --git a/drivers/usb/gadget/u_smd.c b/drivers/usb/gadget/u_smd.c
new file mode 100644
index 0000000..a5ceaff
--- /dev/null
+++ b/drivers/usb/gadget/u_smd.c
@@ -0,0 +1,979 @@
+/*
+ * u_smd.c - utilities for USB gadget serial over smd
+ *
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This code also borrows from drivers/usb/gadget/u_serial.c, which is
+ * Copyright (C) 2000 - 2003 Al Borchers (alborchers@steinerpoint.com)
+ * Copyright (C) 2008 David Brownell
+ * Copyright (C) 2008 by Nokia Corporation
+ * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2000 Peter Berger (pberger@brimson.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/termios.h>
+#include <mach/msm_smd.h>
+#include <linux/debugfs.h>
+
+#include "u_serial.h"
+
+#define SMD_RX_QUEUE_SIZE		8
+#define SMD_RX_BUF_SIZE			2048
+
+#define SMD_TX_QUEUE_SIZE		8
+#define SMD_TX_BUF_SIZE			2048
+
+static struct workqueue_struct *gsmd_wq;
+
+#define SMD_N_PORTS	2
+#define CH_OPENED	0
+#define CH_READY	1
+struct smd_port_info {
+	struct smd_channel	*ch;
+	char			*name;
+	unsigned long		flags;
+};
+
+struct smd_port_info smd_pi[SMD_N_PORTS] = {
+	{
+		.name = "DS",
+	},
+	{
+		.name = "UNUSED",
+	},
+};
+
+struct gsmd_port {
+	unsigned		port_num;
+	spinlock_t		port_lock;
+
+	unsigned		n_read;
+	struct list_head	read_pool;
+	struct list_head	read_queue;
+	struct work_struct	push;
+
+	struct list_head	write_pool;
+	struct work_struct	pull;
+
+	struct gserial		*port_usb;
+
+	struct smd_port_info	*pi;
+	struct delayed_work	connect_work;
+
+	/* At present, smd does not notify
+	 * control bit change info from modem
+	 */
+	struct work_struct	update_modem_ctrl_sig;
+
+#define SMD_ACM_CTRL_DTR		0x01
+#define SMD_ACM_CTRL_RTS		0x02
+	unsigned		cbits_to_modem;
+
+#define SMD_ACM_CTRL_DCD		0x01
+#define SMD_ACM_CTRL_DSR		0x02
+#define SMD_ACM_CTRL_BRK		0x04
+#define SMD_ACM_CTRL_RI		0x08
+	unsigned		cbits_to_laptop;
+
+	/* pkt counters */
+	unsigned long		nbytes_tomodem;
+	unsigned long		nbytes_tolaptop;
+};
+
+static struct smd_portmaster {
+	struct mutex lock;
+	struct gsmd_port *port;
+	struct platform_driver pdrv;
+} smd_ports[SMD_N_PORTS];
+static unsigned n_smd_ports;
+
+static void gsmd_free_req(struct usb_ep *ep, struct usb_request *req)
+{
+	kfree(req->buf);
+	usb_ep_free_request(ep, req);
+}
+
+static void gsmd_free_requests(struct usb_ep *ep, struct list_head *head)
+{
+	struct usb_request	*req;
+
+	while (!list_empty(head)) {
+		req = list_entry(head->next, struct usb_request, list);
+		list_del(&req->list);
+		gsmd_free_req(ep, req);
+	}
+}
+
+static struct usb_request *
+gsmd_alloc_req(struct usb_ep *ep, unsigned len, gfp_t flags)
+{
+	struct usb_request *req;
+
+	req = usb_ep_alloc_request(ep, flags);
+	if (!req) {
+		pr_err("%s: usb alloc request failed\n", __func__);
+		return 0;
+	}
+
+	req->length = len;
+	req->buf = kmalloc(len, flags);
+	if (!req->buf) {
+		pr_err("%s: request buf allocation failed\n", __func__);
+		usb_ep_free_request(ep, req);
+		return 0;
+	}
+
+	return req;
+}
+
+static int gsmd_alloc_requests(struct usb_ep *ep, struct list_head *head,
+		int num, int size,
+		void (*cb)(struct usb_ep *ep, struct usb_request *))
+{
+	int i;
+	struct usb_request *req;
+
+	pr_debug("%s: ep:%p head:%p num:%d size:%d cb:%p", __func__,
+			ep, head, num, size, cb);
+
+	for (i = 0; i < num; i++) {
+		req = gsmd_alloc_req(ep, size, GFP_ATOMIC);
+		if (!req) {
+			pr_debug("%s: req allocated:%d\n", __func__, i);
+			return list_empty(head) ? -ENOMEM : 0;
+		}
+		req->complete = cb;
+		list_add(&req->list, head);
+	}
+
+	return 0;
+}
+
+static void gsmd_start_rx(struct gsmd_port *port)
+{
+	struct list_head	*pool;
+	struct usb_ep		*out;
+	unsigned long	flags;
+	int ret;
+
+	if (!port) {
+		pr_err("%s: port is null\n", __func__);
+		return;
+	}
+
+	spin_lock_irqsave(&port->port_lock, flags);
+
+	if (!port->port_usb) {
+		pr_debug("%s: USB disconnected\n", __func__);
+		goto start_rx_end;
+	}
+
+	pool = &port->read_pool;
+	out = port->port_usb->out;
+
+	while (test_bit(CH_OPENED, &port->pi->flags) && !list_empty(pool)) {
+		struct usb_request	*req;
+
+		req = list_entry(pool->next, struct usb_request, list);
+		list_del(&req->list);
+		req->length = SMD_RX_BUF_SIZE;
+
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		ret = usb_ep_queue(out, req, GFP_KERNEL);
+		spin_lock_irqsave(&port->port_lock, flags);
+		if (ret) {
+			pr_err("%s: usb ep out queue failed"
+					"port:%p, port#%d\n",
+					 __func__, port, port->port_num);
+			list_add_tail(&req->list, pool);
+			break;
+		}
+	}
+start_rx_end:
+	spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+static void gsmd_rx_push(struct work_struct *w)
+{
+	struct gsmd_port *port = container_of(w, struct gsmd_port, push);
+	struct smd_port_info *pi = port->pi;
+	struct list_head *q;
+
+	pr_debug("%s: port:%p port#%d", __func__, port, port->port_num);
+
+	spin_lock_irq(&port->port_lock);
+
+	q = &port->read_queue;
+	while (pi->ch && !list_empty(q)) {
+		struct usb_request *req;
+		int avail;
+
+		req = list_first_entry(q, struct usb_request, list);
+
+		switch (req->status) {
+		case -ESHUTDOWN:
+			pr_debug("%s: req status shutdown portno#%d port:%p\n",
+					__func__, port->port_num, port);
+			goto rx_push_end;
+		default:
+			pr_warning("%s: port:%p port#%d"
+					" Unexpected Rx Status:%d\n", __func__,
+					port, port->port_num, req->status);
+		case 0:
+			/* normal completion */
+			break;
+		}
+
+		avail = smd_write_avail(pi->ch);
+		if (!avail)
+			goto rx_push_end;
+
+		if (req->actual) {
+			char		*packet = req->buf;
+			unsigned	size = req->actual;
+			unsigned	n;
+			int		count;
+
+			n = port->n_read;
+			if (n) {
+				packet += n;
+				size -= n;
+			}
+
+			count = smd_write(pi->ch, packet, size);
+			if (count < 0) {
+				pr_err("%s: smd write failed err:%d\n",
+						__func__, count);
+				goto rx_push_end;
+			}
+
+			if (count != size) {
+				port->n_read += count;
+				goto rx_push_end;
+			}
+
+			port->nbytes_tomodem += count;
+		}
+
+		port->n_read = 0;
+		list_move(&req->list, &port->read_pool);
+	}
+
+rx_push_end:
+	spin_unlock_irq(&port->port_lock);
+
+	gsmd_start_rx(port);
+}
+
+static void gsmd_read_pending(struct gsmd_port *port)
+{
+	int avail;
+
+	if (!port || !port->pi->ch)
+		return;
+
+	/* passing null buffer discards the data */
+	while ((avail = smd_read_avail(port->pi->ch)))
+		smd_read(port->pi->ch, 0, avail);
+
+	return;
+}
+
+static void gsmd_tx_pull(struct work_struct *w)
+{
+	struct gsmd_port *port = container_of(w, struct gsmd_port, pull);
+	struct list_head *pool = &port->write_pool;
+	struct smd_port_info *pi = port->pi;
+	struct usb_ep *in;
+
+	pr_debug("%s: port:%p port#%d pool:%p\n", __func__,
+			port, port->port_num, pool);
+
+	spin_lock_irq(&port->port_lock);
+
+	if (!port->port_usb) {
+		pr_debug("%s: usb is disconnected\n", __func__);
+		spin_unlock_irq(&port->port_lock);
+		gsmd_read_pending(port);
+		return;
+	}
+
+	in = port->port_usb->in;
+	while (pi->ch && !list_empty(pool)) {
+		struct usb_request *req;
+		int avail;
+		int ret;
+
+		avail = smd_read_avail(pi->ch);
+		if (!avail)
+			break;
+
+		avail = avail > SMD_TX_BUF_SIZE ? SMD_TX_BUF_SIZE : avail;
+
+		req = list_entry(pool->next, struct usb_request, list);
+		list_del(&req->list);
+		req->length = smd_read(pi->ch, req->buf, avail);
+
+		spin_unlock_irq(&port->port_lock);
+		ret = usb_ep_queue(in, req, GFP_KERNEL);
+		spin_lock_irq(&port->port_lock);
+		if (ret) {
+			pr_err("%s: usb ep out queue failed"
+					"port:%p, port#%d err:%d\n",
+					__func__, port, port->port_num, ret);
+			/* could be usb disconnected */
+			if (!port->port_usb)
+				gsmd_free_req(in, req);
+			else
+				list_add(&req->list, pool);
+			goto tx_pull_end;
+		}
+
+		port->nbytes_tolaptop += req->length;
+	}
+
+tx_pull_end:
+	/* TBD: Check how code behaves on USB bus suspend */
+	if (port->port_usb && smd_read_avail(port->pi->ch) && !list_empty(pool))
+		queue_work(gsmd_wq, &port->pull);
+
+	spin_unlock_irq(&port->port_lock);
+
+	return;
+}
+
+static void gsmd_read_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct gsmd_port *port = ep->driver_data;
+
+	pr_debug("%s: ep:%p port:%p\n", __func__, ep, port);
+
+	if (!port) {
+		pr_err("%s: port is null\n", __func__);
+		return;
+	}
+
+	spin_lock(&port->port_lock);
+	if (!test_bit(CH_OPENED, &port->pi->flags) ||
+			req->status == -ESHUTDOWN) {
+		spin_unlock(&port->port_lock);
+		gsmd_free_req(ep, req);
+		return;
+	}
+
+	list_add_tail(&req->list, &port->read_queue);
+	queue_work(gsmd_wq, &port->push);
+	spin_unlock(&port->port_lock);
+
+	return;
+}
+
+static void gsmd_write_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct gsmd_port *port = ep->driver_data;
+
+	pr_debug("%s: ep:%p port:%p\n", __func__, ep, port);
+
+	if (!port) {
+		pr_err("%s: port is null\n", __func__);
+		return;
+	}
+
+	spin_lock(&port->port_lock);
+	if (!test_bit(CH_OPENED, &port->pi->flags) ||
+			req->status == -ESHUTDOWN) {
+		spin_unlock(&port->port_lock);
+		gsmd_free_req(ep, req);
+		return;
+	}
+
+	if (req->status)
+		pr_warning("%s: port:%p port#%d unexpected %s status %d\n",
+				__func__, port, port->port_num,
+				ep->name, req->status);
+
+	list_add(&req->list, &port->write_pool);
+	queue_work(gsmd_wq, &port->pull);
+	spin_unlock(&port->port_lock);
+
+	return;
+}
+
+static void gsmd_start_io(struct gsmd_port *port)
+{
+	int		ret = -ENODEV;
+
+	pr_debug("%s: port: %p\n", __func__, port);
+
+	spin_lock(&port->port_lock);
+
+	if (!port->port_usb)
+		goto start_io_out;
+
+	smd_tiocmset_from_cb(port->pi->ch,
+			port->cbits_to_modem,
+			~port->cbits_to_modem);
+
+	ret = gsmd_alloc_requests(port->port_usb->out,
+				&port->read_pool,
+				SMD_RX_QUEUE_SIZE, SMD_RX_BUF_SIZE,
+				gsmd_read_complete);
+	if (ret) {
+		pr_err("%s: unable to allocate out requests\n",
+				__func__);
+		goto start_io_out;
+	}
+
+	ret = gsmd_alloc_requests(port->port_usb->in,
+				&port->write_pool,
+				SMD_TX_QUEUE_SIZE, SMD_TX_BUF_SIZE,
+				gsmd_write_complete);
+	if (ret) {
+		gsmd_free_requests(port->port_usb->out, &port->read_pool);
+		pr_err("%s: unable to allocate IN requests\n",
+				__func__);
+		goto start_io_out;
+	}
+
+start_io_out:
+	spin_unlock(&port->port_lock);
+
+	if (ret)
+		return;
+
+	gsmd_start_rx(port);
+}
+
+static unsigned int convert_uart_sigs_to_acm(unsigned uart_sig)
+{
+	unsigned int acm_sig = 0;
+
+	/* should this needs to be in calling functions ??? */
+	uart_sig &= (TIOCM_RI | TIOCM_CD | TIOCM_DSR);
+
+	if (uart_sig & TIOCM_RI)
+		acm_sig |= SMD_ACM_CTRL_RI;
+	if (uart_sig & TIOCM_CD)
+		acm_sig |= SMD_ACM_CTRL_DCD;
+	if (uart_sig & TIOCM_DSR)
+		acm_sig |= SMD_ACM_CTRL_DSR;
+
+	return acm_sig;
+}
+
+static unsigned int convert_acm_sigs_to_uart(unsigned acm_sig)
+{
+	unsigned int uart_sig = 0;
+
+	/* should this needs to be in calling functions ??? */
+	acm_sig &= (SMD_ACM_CTRL_DTR | SMD_ACM_CTRL_RTS);
+
+	if (acm_sig & SMD_ACM_CTRL_DTR)
+		uart_sig |= TIOCM_DTR;
+	if (acm_sig & SMD_ACM_CTRL_RTS)
+		uart_sig |= TIOCM_RTS;
+
+	return uart_sig;
+}
+
+
+static void gsmd_stop_io(struct gsmd_port *port)
+{
+	struct usb_ep	*in;
+	struct usb_ep	*out;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	if (!port->port_usb) {
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		return;
+	}
+	in = port->port_usb->in;
+	out = port->port_usb->out;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	usb_ep_fifo_flush(in);
+	usb_ep_fifo_flush(out);
+
+	spin_lock(&port->port_lock);
+	if (port->port_usb) {
+		gsmd_free_requests(out, &port->read_pool);
+		gsmd_free_requests(out, &port->read_queue);
+		gsmd_free_requests(in, &port->write_pool);
+		port->n_read = 0;
+		port->cbits_to_laptop = 0;
+	}
+
+	if (port->port_usb->send_modem_ctrl_bits)
+		port->port_usb->send_modem_ctrl_bits(
+					port->port_usb,
+					port->cbits_to_laptop);
+	spin_unlock(&port->port_lock);
+
+}
+
+static void gsmd_notify(void *priv, unsigned event)
+{
+	struct gsmd_port *port = priv;
+	struct smd_port_info *pi = port->pi;
+	int i;
+
+	switch (event) {
+	case SMD_EVENT_DATA:
+		pr_debug("%s: Event data\n", __func__);
+		if (smd_read_avail(pi->ch))
+			queue_work(gsmd_wq, &port->pull);
+		if (smd_write_avail(pi->ch))
+			queue_work(gsmd_wq, &port->push);
+		break;
+	case SMD_EVENT_OPEN:
+		pr_debug("%s: Event Open\n", __func__);
+		set_bit(CH_OPENED, &pi->flags);
+		gsmd_start_io(port);
+		break;
+	case SMD_EVENT_CLOSE:
+		pr_debug("%s: Event Close\n", __func__);
+		clear_bit(CH_OPENED, &pi->flags);
+		gsmd_stop_io(port);
+		break;
+	case SMD_EVENT_STATUS:
+		i = smd_tiocmget(port->pi->ch);
+		port->cbits_to_laptop = convert_uart_sigs_to_acm(i);
+		if (port->port_usb && port->port_usb->send_modem_ctrl_bits)
+			port->port_usb->send_modem_ctrl_bits(port->port_usb,
+						port->cbits_to_laptop);
+		break;
+	}
+}
+
+static void gsmd_connect_work(struct work_struct *w)
+{
+	struct gsmd_port *port;
+	struct smd_port_info *pi;
+	int ret;
+
+	port = container_of(w, struct gsmd_port, connect_work.work);
+	pi = port->pi;
+
+	pr_debug("%s: port:%p port#%d\n", __func__, port, port->port_num);
+
+	if (!test_bit(CH_READY, &pi->flags))
+		return;
+
+	ret = smd_named_open_on_edge(pi->name, SMD_APPS_MODEM,
+				&pi->ch, port, gsmd_notify);
+	if (ret) {
+		if (ret == -EAGAIN) {
+			/* port not ready  - retry */
+			pr_debug("%s: SMD port not ready - rescheduling:%s err:%d\n",
+					__func__, pi->name, ret);
+			queue_delayed_work(gsmd_wq, &port->connect_work,
+				msecs_to_jiffies(250));
+		} else {
+			pr_err("%s: unable to open smd port:%s err:%d\n",
+					__func__, pi->name, ret);
+		}
+	}
+}
+
+static void gsmd_notify_modem(void *gptr, u8 portno, int ctrl_bits)
+{
+	struct gsmd_port *port;
+	int temp;
+	struct gserial *gser = gptr;
+
+	if (portno >= n_smd_ports) {
+		pr_err("%s: invalid portno#%d\n", __func__, portno);
+		return;
+	}
+
+	if (!gser) {
+		pr_err("%s: gser is null\n", __func__);
+		return;
+	}
+
+	port = smd_ports[portno].port;
+
+	temp = convert_acm_sigs_to_uart(ctrl_bits);
+
+	if (temp == port->cbits_to_modem)
+		return;
+
+	port->cbits_to_modem = temp;
+
+	/* usb could send control signal before smd is ready */
+	if (!test_bit(CH_OPENED, &port->pi->flags))
+		return;
+
+	/* if DTR is high, update latest modem info to laptop */
+	if (port->cbits_to_modem & TIOCM_DTR) {
+		unsigned i;
+
+		i = smd_tiocmget(port->pi->ch);
+		port->cbits_to_laptop = convert_uart_sigs_to_acm(i);
+
+		if (gser->send_modem_ctrl_bits)
+			gser->send_modem_ctrl_bits(
+					port->port_usb,
+					port->cbits_to_laptop);
+	}
+
+	smd_tiocmset(port->pi->ch,
+			port->cbits_to_modem,
+			~port->cbits_to_modem);
+}
+
+int gsmd_connect(struct gserial *gser, u8 portno)
+{
+	unsigned long flags;
+	int ret;
+	struct gsmd_port *port;
+
+	pr_debug("%s: gserial:%p portno:%u\n", __func__, gser, portno);
+
+	if (portno >= n_smd_ports) {
+		pr_err("%s: Invalid port no#%d", __func__, portno);
+		return -EINVAL;
+	}
+
+	if (!gser) {
+		pr_err("%s: gser is null\n", __func__);
+		return -EINVAL;
+	}
+
+	port = smd_ports[portno].port;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	port->port_usb = gser;
+	gser->notify_modem = gsmd_notify_modem;
+	port->nbytes_tomodem = 0;
+	port->nbytes_tolaptop = 0;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	ret = usb_ep_enable(gser->in);
+	if (ret) {
+		pr_err("%s: usb_ep_enable failed eptype:IN ep:%p",
+				__func__, gser->in);
+		port->port_usb = 0;
+		return ret;
+	}
+	gser->in->driver_data = port;
+
+	ret = usb_ep_enable(gser->out);
+	if (ret) {
+		pr_err("%s: usb_ep_enable failed eptype:OUT ep:%p",
+				__func__, gser->out);
+		port->port_usb = 0;
+		gser->in->driver_data = 0;
+		return ret;
+	}
+	gser->out->driver_data = port;
+
+	queue_delayed_work(gsmd_wq, &port->connect_work, msecs_to_jiffies(0));
+
+	return 0;
+}
+
+void gsmd_disconnect(struct gserial *gser, u8 portno)
+{
+	unsigned long flags;
+	struct gsmd_port *port;
+
+	pr_debug("%s: gserial:%p portno:%u\n", __func__, gser, portno);
+
+	if (portno >= n_smd_ports) {
+		pr_err("%s: invalid portno#%d\n", __func__, portno);
+		return;
+	}
+
+	if (!gser) {
+		pr_err("%s: gser is null\n", __func__);
+		return;
+	}
+
+	port = smd_ports[portno].port;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	port->port_usb = 0;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	/* disable endpoints, aborting down any active I/O */
+	usb_ep_disable(gser->out);
+	usb_ep_disable(gser->in);
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	gsmd_free_requests(gser->out, &port->read_pool);
+	gsmd_free_requests(gser->out, &port->read_queue);
+	gsmd_free_requests(gser->in, &port->write_pool);
+	port->n_read = 0;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	if (test_and_clear_bit(CH_OPENED, &port->pi->flags)) {
+		/* lower the dtr */
+		port->cbits_to_modem = 0;
+		smd_tiocmset(port->pi->ch,
+				port->cbits_to_modem,
+				~port->cbits_to_modem);
+	}
+
+	if (port->pi->ch) {
+		smd_close(port->pi->ch);
+		port->pi->ch = NULL;
+	}
+}
+
+#define SMD_CH_MAX_LEN	20
+static int gsmd_ch_probe(struct platform_device *pdev)
+{
+	struct gsmd_port *port;
+	struct smd_port_info *pi;
+	int i;
+	unsigned long flags;
+
+	pr_debug("%s: name:%s\n", __func__, pdev->name);
+
+	for (i = 0; i < n_smd_ports; i++) {
+		port = smd_ports[i].port;
+		pi = port->pi;
+
+		if (!strncmp(pi->name, pdev->name, SMD_CH_MAX_LEN)) {
+			set_bit(CH_READY, &pi->flags);
+			spin_lock_irqsave(&port->port_lock, flags);
+			if (port->port_usb)
+				queue_delayed_work(gsmd_wq, &port->connect_work,
+					msecs_to_jiffies(0));
+			spin_unlock_irqrestore(&port->port_lock, flags);
+			break;
+		}
+	}
+	return 0;
+}
+
+static int gsmd_ch_remove(struct platform_device *pdev)
+{
+	struct gsmd_port *port;
+	struct smd_port_info *pi;
+	int i;
+
+	pr_debug("%s: name:%s\n", __func__, pdev->name);
+
+	for (i = 0; i < n_smd_ports; i++) {
+		port = smd_ports[i].port;
+		pi = port->pi;
+
+		if (!strncmp(pi->name, pdev->name, SMD_CH_MAX_LEN)) {
+			clear_bit(CH_READY, &pi->flags);
+			clear_bit(CH_OPENED, &pi->flags);
+			if (pi->ch) {
+				smd_close(pi->ch);
+				pi->ch = NULL;
+			}
+			break;
+		}
+	}
+	return 0;
+}
+
+static void gsmd_port_free(int portno)
+{
+	struct gsmd_port *port = smd_ports[portno].port;
+
+	if (!port)
+		kfree(port);
+}
+
+static int gsmd_port_alloc(int portno, struct usb_cdc_line_coding *coding)
+{
+	struct gsmd_port *port;
+	struct platform_driver *pdrv;
+
+	port = kzalloc(sizeof(struct gsmd_port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	port->port_num = portno;
+	port->pi = &smd_pi[portno];
+
+	spin_lock_init(&port->port_lock);
+
+	INIT_LIST_HEAD(&port->read_pool);
+	INIT_LIST_HEAD(&port->read_queue);
+	INIT_WORK(&port->push, gsmd_rx_push);
+
+	INIT_LIST_HEAD(&port->write_pool);
+	INIT_WORK(&port->pull, gsmd_tx_pull);
+
+	INIT_DELAYED_WORK(&port->connect_work, gsmd_connect_work);
+
+	smd_ports[portno].port = port;
+	pdrv = &smd_ports[portno].pdrv;
+	pdrv->probe = gsmd_ch_probe;
+	pdrv->remove = gsmd_ch_remove;
+	pdrv->driver.name = port->pi->name;
+	pdrv->driver.owner = THIS_MODULE;
+	platform_driver_register(pdrv);
+
+	pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
+
+	return 0;
+}
+
+#if defined(CONFIG_DEBUG_FS)
+static ssize_t debug_smd_read_stats(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	struct gsmd_port *port;
+	struct smd_port_info *pi;
+	char *buf;
+	unsigned long flags;
+	int temp = 0;
+	int i;
+	int ret;
+
+	buf = kzalloc(sizeof(char) * 512, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	for (i = 0; i < n_smd_ports; i++) {
+		port = smd_ports[i].port;
+		pi = port->pi;
+		spin_lock_irqsave(&port->port_lock, flags);
+		temp += scnprintf(buf + temp, 512 - temp,
+				"###PORT:%d###\n"
+				"nbytes_tolaptop: %lu\n"
+				"nbytes_tomodem:  %lu\n"
+				"cbits_to_modem:  %u\n"
+				"cbits_to_laptop: %u\n"
+				"n_read: %u\n"
+				"smd_read_avail: %d\n"
+				"smd_write_avail: %d\n"
+				"CH_OPENED: %d\n"
+				"CH_READY: %d\n",
+				i, port->nbytes_tolaptop, port->nbytes_tomodem,
+				port->cbits_to_modem, port->cbits_to_laptop,
+				port->n_read,
+				pi->ch ? smd_read_avail(pi->ch) : 0,
+				pi->ch ? smd_write_avail(pi->ch) : 0,
+				test_bit(CH_OPENED, &pi->flags),
+				test_bit(CH_READY, &pi->flags));
+		spin_unlock_irqrestore(&port->port_lock, flags);
+	}
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+	kfree(buf);
+
+	return ret;
+
+}
+
+static ssize_t debug_smd_reset_stats(struct file *file, const char __user *buf,
+				 size_t count, loff_t *ppos)
+{
+	struct gsmd_port *port;
+	unsigned long flags;
+	int i;
+
+	for (i = 0; i < n_smd_ports; i++) {
+		port = smd_ports[i].port;
+
+		spin_lock_irqsave(&port->port_lock, flags);
+		port->nbytes_tolaptop = 0;
+		port->nbytes_tomodem = 0;
+		spin_unlock_irqrestore(&port->port_lock, flags);
+	}
+
+	return count;
+}
+
+static int debug_smd_open(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static const struct file_operations debug_gsmd_ops = {
+	.open = debug_smd_open,
+	.read = debug_smd_read_stats,
+	.write = debug_smd_reset_stats,
+};
+
+static void gsmd_debugfs_init(void)
+{
+	struct dentry *dent;
+
+	dent = debugfs_create_dir("usb_gsmd", 0);
+	if (IS_ERR(dent))
+		return;
+
+	debugfs_create_file("status", 0444, dent, 0, &debug_gsmd_ops);
+}
+#else
+static void gsmd_debugfs_init(void) {}
+#endif
+
+int gsmd_setup(struct usb_gadget *g, unsigned count)
+{
+	struct usb_cdc_line_coding	coding;
+	int ret;
+	int i;
+
+	pr_debug("%s: g:%p count: %d\n", __func__, g, count);
+
+	if (!count || count > SMD_N_PORTS) {
+		pr_err("%s: Invalid num of ports count:%d gadget:%p\n",
+				__func__, count, g);
+		return -EINVAL;
+	}
+
+	coding.dwDTERate = cpu_to_le32(9600);
+	coding.bCharFormat = 8;
+	coding.bParityType = USB_CDC_NO_PARITY;
+	coding.bDataBits = USB_CDC_1_STOP_BITS;
+
+	gsmd_wq = create_singlethread_workqueue("k_gsmd");
+	if (!gsmd_wq) {
+		pr_err("%s: Unable to create workqueue gsmd_wq\n",
+				__func__);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < count; i++) {
+		mutex_init(&smd_ports[i].lock);
+		n_smd_ports++;
+		ret = gsmd_port_alloc(i, &coding);
+		if (ret) {
+			n_smd_ports--;
+			pr_err("%s: Unable to alloc port:%d\n", __func__, i);
+			goto free_smd_ports;
+		}
+	}
+
+	gsmd_debugfs_init();
+
+	return 0;
+free_smd_ports:
+	for (i = 0; i < n_smd_ports; i++)
+		gsmd_port_free(i);
+
+	destroy_workqueue(gsmd_wq);
+
+	return ret;
+}
+
+void gsmd_cleanup(struct usb_gadget *g, unsigned count)
+{
+	/* TBD */
+}
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index f788eb8..4357867 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -63,6 +63,19 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called ehci-hcd.
 
+config USB_EHCI_EHSET
+	bool "Embedded High-speed Host Electrical Test Support"
+	depends on USB_EHCI_HCD
+	---help---
+	  This option is required for EHSET Host Compliance Tests support on an
+	  embedded Hi-speed USB Host or OTG port.
+
+	  This enables the software support for the "Single Step Set Featue" test.
+	  Apart from this test, other EHSET tests TEST_SE0/J/K/PACKET are part
+	  of EHCI specification and their support already exists in the EHCI driver.
+
+	  If unsure, say N.
+
 config USB_EHCI_ROOT_HUB_TT
 	bool "Root Hub Transaction Translators"
 	depends on USB_EHCI_HCD
@@ -169,6 +182,24 @@
 	  This driver is not supported on boards like trout which
 	  has an external PHY.
 
+config USB_EHCI_MSM_HSIC
+	bool "Support for HSIC based MSM on-chip EHCI USB controller"
+	depends on USB_EHCI_HCD && ARCH_MSM
+	---help---
+	  Enables support for the HSIC (High Speed Inter-Chip) based
+	  USB Host controller present on the Qualcomm chipsets.
+
+	  HSIC is a supplement to USB 2.0 specification and is preferred
+	  for chip-to-chip interconnect (having maximum circuit length of
+	  10cm) as it removes the analog transceivers.
+
+config USB_EHCI_MSM_HOST4
+	bool "Support for MSM on-chip EHCI USB controller# 4"
+	depends on USB_EHCI_HCD && ARCH_MSM && ARCH_APQ8064
+	---help---
+	  Enables support for the EHCI Compliant USB Host controller# 4
+	  present on the Qualcomm chipsets.
+
 config USB_EHCI_TEGRA
        boolean "NVIDIA Tegra HCD support"
        depends on USB_EHCI_HCD && ARCH_TEGRA
@@ -247,6 +278,21 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called oxu210hp-hcd.
 
+config USB_EHCI_MSM_72K
+	bool "Support for Legacy Qualcomm on-chip EHCI USB controller"
+	depends on USB_EHCI_HCD && USB_MSM_OTG_72K && ARCH_MSM
+	---help---
+	  This driver enables support for USB host controller
+	  in pre 8660 qualcomm chipsets(8660, 7X30, 8X50 and 7X27).
+
+config USB_FS_HOST
+	bool "Support for Full Speed Host Mode"
+	depends on USB_EHCI_MSM_72K && ARCH_QSD8X50
+	default n
+	---help---
+	  Enables support for the full speed USB controller core present
+	  on the Qualcomm chipsets
+
 config USB_ISP116X_HCD
 	tristate "ISP116X HCD support"
 	depends on USB
@@ -588,6 +634,15 @@
 	  To compile this driver a module, choose M here: the module
 	  will be called "whci-hcd".
 
+config USB_PEHCI_HCD
+	tristate "ST-E ISP1763A Host Controller"
+	depends on USB
+	help
+	  Driver for ST-E isp1763A USB Host 2.0 Controllers.
+
+	  To compile this driver a module, choose M here: the module
+	  will be called "pehci".
+
 config USB_HWA_HCD
 	tristate "Host Wire Adapter (HWA) driver (EXPERIMENTAL)"
 	depends on EXPERIMENTAL
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 0982bcc..7d35f5b 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -20,6 +20,7 @@
 endif
 
 obj-$(CONFIG_USB_WHCI_HCD)	+= whci/
+obj-$(CONFIG_USB_PEHCI_HCD)	+= pehci/
 
 obj-$(CONFIG_PCI)		+= pci-quirks.o
 
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
index 680e1a3..87bf3df 100644
--- a/drivers/usb/host/ehci-dbg.c
+++ b/drivers/usb/host/ehci-dbg.c
@@ -39,7 +39,7 @@
  * (host controller _Structural_ parameters)
  * see EHCI spec, Table 2-4 for each value
  */
-static void dbg_hcs_params (struct ehci_hcd *ehci, char *label)
+static void __maybe_unused dbg_hcs_params (struct ehci_hcd *ehci, char *label)
 {
 	u32	params = ehci_readl(ehci, &ehci->caps->hcs_params);
 
@@ -83,7 +83,7 @@
  * (host controller _Capability_ parameters)
  * see EHCI Spec, Table 2-5 for each value
  * */
-static void dbg_hcc_params (struct ehci_hcd *ehci, char *label)
+static void __maybe_unused dbg_hcc_params (struct ehci_hcd *ehci, char *label)
 {
 	u32	params = ehci_readl(ehci, &ehci->caps->hcc_params);
 
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 4a3bc5b..bdde862 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -503,7 +503,7 @@
 	spin_unlock_irq(&ehci->lock);
 }
 
-static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
+static void __maybe_unused ehci_port_power (struct ehci_hcd *ehci, int is_on)
 {
 	unsigned port;
 
@@ -579,8 +579,21 @@
 
 	/* root hub is shut down separately (first, when possible) */
 	spin_lock_irq (&ehci->lock);
-	if (ehci->async)
+	if (ehci->async) {
+		/*
+		 * TODO: Observed that ehci->async next ptr is not
+		 * NULL sometimes which leads to crash in mem_cleanup.
+		 * Root cause is not yet known why this messup is
+		 * happenning.
+		 * The follwing workaround fixes the crash caused
+		 * by this temporarily.
+		 * check if async next ptr is not NULL and unlink
+		 * explictly.
+		 */
+		if (ehci->async->qh_next.ptr != NULL)
+			start_unlink_async(ehci, ehci->async->qh_next.qh);
 		ehci_work (ehci);
+	}
 	spin_unlock_irq (&ehci->lock);
 	ehci_mem_cleanup (ehci);
 
@@ -726,7 +739,7 @@
 }
 
 /* start HC running; it's halted, ehci_init() has been run (once) */
-static int ehci_run (struct usb_hcd *hcd)
+static int __maybe_unused ehci_run (struct usb_hcd *hcd)
 {
 	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
 	u32			temp;
@@ -931,6 +944,12 @@
 			pstatus = ehci_readl(ehci,
 					 &ehci->regs->port_status[i]);
 
+			/*set RS bit in case of remote wakeup*/
+			if (ehci_is_TDI(ehci) && !(cmd & CMD_RUN) &&
+					(pstatus & PORT_SUSPEND))
+				ehci_writel(ehci, cmd | CMD_RUN,
+						&ehci->regs->command);
+
 			if (pstatus & PORT_OWNER)
 				continue;
 			if (!(test_bit(i, &ehci->suspended_ports) &&
@@ -1200,7 +1219,7 @@
 	spin_unlock_irqrestore (&ehci->lock, flags);
 }
 
-static void
+static void __maybe_unused
 ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
 {
 	struct ehci_hcd		*ehci = hcd_to_ehci(hcd);
@@ -1258,31 +1277,6 @@
 #define	PCI_DRIVER		ehci_pci_driver
 #endif
 
-#ifdef CONFIG_USB_EHCI_FSL
-#include "ehci-fsl.c"
-#define	PLATFORM_DRIVER		ehci_fsl_driver
-#endif
-
-#ifdef CONFIG_USB_EHCI_MXC
-#include "ehci-mxc.c"
-#define PLATFORM_DRIVER		ehci_mxc_driver
-#endif
-
-#ifdef CONFIG_USB_EHCI_SH
-#include "ehci-sh.c"
-#define PLATFORM_DRIVER		ehci_hcd_sh_driver
-#endif
-
-#ifdef CONFIG_MIPS_ALCHEMY
-#include "ehci-au1xxx.c"
-#define	PLATFORM_DRIVER		ehci_hcd_au1xxx_driver
-#endif
-
-#ifdef CONFIG_USB_EHCI_HCD_OMAP
-#include "ehci-omap.c"
-#define        PLATFORM_DRIVER         ehci_hcd_omap_driver
-#endif
-
 #ifdef CONFIG_PPC_PS3
 #include "ehci-ps3.c"
 #define	PS3_SYSTEM_BUS_DRIVER	ps3_ehci_driver
@@ -1298,100 +1292,253 @@
 #define XILINX_OF_PLATFORM_DRIVER	ehci_hcd_xilinx_of_driver
 #endif
 
+#ifdef CONFIG_USB_EHCI_FSL
+#include "ehci-fsl.c"
+#define PLATFORM_DRIVER_PRESENT
+#endif
+
+#ifdef CONFIG_USB_EHCI_MXC
+#include "ehci-mxc.c"
+#define PLATFORM_DRIVER_PRESENT
+#endif
+
+#ifdef CONFIG_USB_EHCI_SH
+#include "ehci-sh.c"
+#define PLATFORM_DRIVER_PRESENT
+#endif
+
+#ifdef CONFIG_MIPS_ALCHEMY
+#include "ehci-au1xxx.c"
+#define PLATFORM_DRIVER_PRESENT
+#endif
+
+#ifdef CONFIG_USB_EHCI_HCD_OMAP
+#include "ehci-omap.c"
+#define PLATFORM_DRIVER_PRESENT
+#endif
+
 #ifdef CONFIG_PLAT_ORION
 #include "ehci-orion.c"
-#define	PLATFORM_DRIVER		ehci_orion_driver
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
 #ifdef CONFIG_ARCH_IXP4XX
 #include "ehci-ixp4xx.c"
-#define	PLATFORM_DRIVER		ixp4xx_ehci_driver
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
 #ifdef CONFIG_USB_W90X900_EHCI
 #include "ehci-w90x900.c"
-#define	PLATFORM_DRIVER		ehci_hcd_w90x900_driver
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
 #ifdef CONFIG_ARCH_AT91
 #include "ehci-atmel.c"
-#define	PLATFORM_DRIVER		ehci_atmel_driver
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
 #ifdef CONFIG_USB_OCTEON_EHCI
 #include "ehci-octeon.c"
-#define PLATFORM_DRIVER		ehci_octeon_driver
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
 #ifdef CONFIG_USB_CNS3XXX_EHCI
 #include "ehci-cns3xxx.c"
-#define PLATFORM_DRIVER		cns3xxx_ehci_driver
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
 #ifdef CONFIG_ARCH_VT8500
 #include "ehci-vt8500.c"
-#define	PLATFORM_DRIVER		vt8500_ehci_driver
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
 #ifdef CONFIG_PLAT_SPEAR
 #include "ehci-spear.c"
-#define PLATFORM_DRIVER		spear_ehci_hcd_driver
+#define PLATFORM_DRIVER_PRESENT
+#endif
+
+#ifdef CONFIG_USB_EHCI_MSM_72K
+#include "ehci-msm72k.c"
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
 #ifdef CONFIG_USB_EHCI_MSM
 #include "ehci-msm.c"
-#define PLATFORM_DRIVER		ehci_msm_driver
+#include "ehci-msm2.c"
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
 #ifdef CONFIG_USB_EHCI_HCD_PMC_MSP
 #include "ehci-pmcmsp.c"
-#define	PLATFORM_DRIVER		ehci_hcd_msp_driver
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
 #ifdef CONFIG_USB_EHCI_TEGRA
 #include "ehci-tegra.c"
-#define PLATFORM_DRIVER		tegra_ehci_driver
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
 #ifdef CONFIG_USB_EHCI_S5P
 #include "ehci-s5p.c"
-#define PLATFORM_DRIVER		s5p_ehci_driver
+#define PLATFORM_DRIVER_PRESENT
+#endif
+
+#ifdef CONFIG_USB_EHCI_ATH79
+#include "ehci-ath79.c"
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
 #ifdef CONFIG_SPARC_LEON
 #include "ehci-grlib.c"
-#define PLATFORM_DRIVER		ehci_grlib_driver
+#define PLATFORM_DRIVER_PRESENT
+#endif
+
+#ifdef CONFIG_USB_EHCI_MSM_HSIC
+#include "ehci-msm-hsic.c"
+#define PLATFORM_DRIVER_PRESENT
+#endif
+
+#ifdef CONFIG_USB_PXA168_EHCI
+#include "ehci-pxa168.c"
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
 #ifdef CONFIG_CPU_XLR
 #include "ehci-xls.c"
-#define PLATFORM_DRIVER		ehci_xls_driver
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
 #ifdef CONFIG_USB_EHCI_MV
 #include "ehci-mv.c"
-#define        PLATFORM_DRIVER         ehci_mv_driver
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
 #ifdef CONFIG_MACH_LOONGSON1
 #include "ehci-ls1x.c"
-#define PLATFORM_DRIVER		ehci_ls1x_driver
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
 #ifdef CONFIG_USB_EHCI_HCD_PLATFORM
 #include "ehci-platform.c"
-#define PLATFORM_DRIVER		ehci_platform_driver
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
-#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
+#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER_PRESENT) && \
     !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \
     !defined(XILINX_OF_PLATFORM_DRIVER)
 #error "missing bus glue for ehci-hcd"
 #endif
 
+static struct platform_driver *plat_drivers[]  = {
+#ifdef CONFIG_USB_EHCI_FSL
+	&ehci_fsl_driver,
+#endif
+
+#ifdef CONFIG_USB_EHCI_MXC
+	&ehci_mxc_driver,
+#endif
+
+#ifdef CONFIG_CPU_SUBTYPE_SH7786
+	&ehci_hcd_sh_driver,
+#endif
+
+#ifdef CONFIG_SOC_AU1200
+	&ehci_hcd_au1xxx_driver,
+#endif
+
+#ifdef CONFIG_USB_EHCI_HCD_OMAP
+	&ehci_hcd_omap_driver,
+#endif
+
+#ifdef CONFIG_PLAT_ORION
+	&ehci_orion_driver,
+#endif
+
+#ifdef CONFIG_ARCH_IXP4XX
+	&ixp4xx_ehci_driver,
+#endif
+
+#ifdef CONFIG_USB_W90X900_EHCI
+	&ehci_hcd_w90x900_driver,
+#endif
+
+#ifdef CONFIG_ARCH_AT91
+	&ehci_atmel_driver,
+#endif
+
+#ifdef CONFIG_USB_OCTEON_EHCI
+	&ehci_octeon_driver,
+#endif
+
+#ifdef CONFIG_USB_CNS3XXX_EHCI
+	&cns3xxx_ehci_driver,
+#endif
+
+#ifdef CONFIG_ARCH_VT8500
+	&vt8500_ehci_driver,
+#endif
+
+#ifdef CONFIG_PLAT_SPEAR
+	&spear_ehci_hcd_driver,
+#endif
+
+#ifdef CONFIG_USB_EHCI_HCD_PMC_MSP
+	&ehci_hcd_msp_driver
+#endif
+
+#ifdef CONFIG_USB_EHCI_TEGRA
+	&tegra_ehci_driver
+#endif
+
+#ifdef CONFIG_USB_EHCI_S5P
+	&s5p_ehci_driver
+#endif
+
+#ifdef CONFIG_USB_EHCI_ATH79
+	&ehci_ath79_driver
+#endif
+
+#ifdef CONFIG_SPARC_LEON
+	&ehci_grlib_driver
+#endif
+
+#if defined(CONFIG_USB_EHCI_MSM_72K) || defined(CONFIG_USB_EHCI_MSM)
+	&ehci_msm_driver,
+#endif
+
+#ifdef CONFIG_USB_EHCI_MSM_HSIC
+	&ehci_msm_hsic_driver,
+#endif
+
+#ifdef CONFIG_USB_EHCI_MSM
+	&ehci_msm2_driver,
+#endif
+
+#ifdef CONFIG_USB_PXA168_EHCI
+	&ehci_pxa168_driver,
+#endif
+
+#ifdef CONFIG_CPU_XLR
+	&ehci_xls_driver,
+#endif
+
+#ifdef CONFIG_USB_EHCI_MV
+	&ehci_mv_driver,
+#endif
+
+#ifdef CONFIG_MACH_LOONGSON1
+	&ehci_ls1x_driver,
+#endif
+
+#ifdef CONFIG_USB_EHCI_HCD_PLATFORM
+	&ehci_platform_driver,
+#endif
+};
+
+
 static int __init ehci_hcd_init(void)
 {
-	int retval = 0;
+	int i, retval = 0;
 
 	if (usb_disabled())
 		return -ENODEV;
@@ -1416,11 +1563,14 @@
 	}
 #endif
 
-#ifdef PLATFORM_DRIVER
-	retval = platform_driver_register(&PLATFORM_DRIVER);
-	if (retval < 0)
-		goto clean0;
-#endif
+	for (i = 0; i < ARRAY_SIZE(plat_drivers); i++) {
+		retval = platform_driver_register(plat_drivers[i]);
+		if (retval) {
+			while (--i >= 0)
+				platform_driver_unregister(plat_drivers[i]);
+			goto clean0;
+		}
+	}
 
 #ifdef PCI_DRIVER
 	retval = pci_register_driver(&PCI_DRIVER);
@@ -1463,10 +1613,9 @@
 	pci_unregister_driver(&PCI_DRIVER);
 clean1:
 #endif
-#ifdef PLATFORM_DRIVER
-	platform_driver_unregister(&PLATFORM_DRIVER);
+	for (i = 0; i < ARRAY_SIZE(plat_drivers); i++)
+		platform_driver_unregister(plat_drivers[i]);
 clean0:
-#endif
 #ifdef DEBUG
 	debugfs_remove(ehci_debug_root);
 	ehci_debug_root = NULL;
@@ -1479,15 +1628,17 @@
 
 static void __exit ehci_hcd_cleanup(void)
 {
+	int i;
 #ifdef XILINX_OF_PLATFORM_DRIVER
 	platform_driver_unregister(&XILINX_OF_PLATFORM_DRIVER);
 #endif
 #ifdef OF_PLATFORM_DRIVER
 	platform_driver_unregister(&OF_PLATFORM_DRIVER);
 #endif
-#ifdef PLATFORM_DRIVER
-	platform_driver_unregister(&PLATFORM_DRIVER);
-#endif
+
+	for (i = 0; i < ARRAY_SIZE(plat_drivers); i++)
+		platform_driver_unregister(plat_drivers[i]);
+
 #ifdef PCI_DRIVER
 	pci_unregister_driver(&PCI_DRIVER);
 #endif
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 38fe076..5cc70e0 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -254,6 +254,11 @@
 		if (t1 & PORT_OWNER)
 			set_bit(port, &ehci->owned_ports);
 		else if ((t1 & PORT_PE) && !(t1 & PORT_SUSPEND)) {
+			/*clear RS bit before setting SUSP bit
+			* and wait for HCH to get set*/
+			if (ehci->susp_sof_bug)
+				ehci_halt(ehci);
+
 			t2 |= PORT_SUSPEND;
 			set_bit(port, &ehci->bus_suspended);
 		}
@@ -304,9 +309,11 @@
 	if (ehci->bus_suspended)
 		udelay(150);
 
-	/* turn off now-idle HC */
-	ehci_halt (ehci);
-	ehci->rh_state = EHCI_RH_SUSPENDED;
+	/*if this bit is set, controller is already haled*/
+	if (!ehci->susp_sof_bug)
+		ehci_halt(ehci); /* turn off now-idle HC */
+
+	hcd->state = HC_STATE_SUSPENDED;
 
 	if (ehci->reclaim)
 		end_unlink_async(ehci);
@@ -653,6 +660,151 @@
 }
 
 /*-------------------------------------------------------------------------*/
+#ifdef CONFIG_USB_EHCI_EHSET
+
+#define EHSET_TEST_SINGLE_STEP_SET_FEATURE 0x06
+
+static void usb_ehset_completion(struct urb *urb)
+{
+	struct completion  *done = urb->context;
+
+	complete(done);
+}
+static int submit_single_step_set_feature(
+	struct usb_hcd  *hcd,
+	struct urb      *urb,
+	int 		is_setup
+);
+
+/* Allocate a URB and initialize the various fields of it.
+ * This API is used by the single_step_set_feature test of
+ * EHSET where IN packet of the GetDescriptor request is
+ * sent after 15secs of the SETUP packet.
+ * Return NULL if failed.
+ */
+static struct urb *
+request_single_step_set_feature_urb(
+	struct usb_device 	*udev,
+	void 			*dr,
+	void 			*buf,
+	struct completion 	*done
+) {
+	struct urb *urb;
+	struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+	struct usb_host_endpoint	*ep;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		return NULL;
+
+	urb->pipe = usb_rcvctrlpipe(udev, 0);
+	ep = (usb_pipein(urb->pipe) ? udev->ep_in : udev->ep_out)
+			[usb_pipeendpoint(urb->pipe)];
+	if (!ep) {
+		usb_free_urb(urb);
+		return NULL;
+	}
+
+	/* Initialize the various URB fields as these are used
+	 * by the HCD driver to queue it and as well as
+	 * when completion happens.
+	 */
+	urb->ep = ep;
+	urb->dev = udev;
+	urb->setup_packet = (void *)dr;
+	urb->transfer_buffer = buf;
+	urb->transfer_buffer_length = USB_DT_DEVICE_SIZE;
+	urb->complete = usb_ehset_completion;
+	urb->status = -EINPROGRESS;
+	urb->actual_length = 0;
+	urb->transfer_flags = (urb->transfer_flags & ~URB_DIR_MASK)
+				| URB_DIR_IN ;
+	usb_get_urb(urb);
+	atomic_inc(&urb->use_count);
+	atomic_inc(&urb->dev->urbnum);
+	urb->setup_dma = dma_map_single(
+			hcd->self.controller,
+			urb->setup_packet,
+			sizeof(struct usb_ctrlrequest),
+			DMA_TO_DEVICE);
+	urb->transfer_dma = dma_map_single(
+			hcd->self.controller,
+			urb->transfer_buffer,
+			urb->transfer_buffer_length,
+			DMA_FROM_DEVICE);
+	urb->context = done;
+	return urb;
+}
+
+static int ehset_single_step_set_feature(struct usb_hcd *hcd, int port)
+{
+	int retval = -ENOMEM;
+	struct usb_ctrlrequest *dr;
+	struct urb *urb;
+	struct usb_device *udev ;
+	struct ehci_hcd	*ehci = hcd_to_ehci(hcd);
+	struct usb_device_descriptor *buf;
+	DECLARE_COMPLETION_ONSTACK(done);
+
+	/*Obtain udev of the rhub's child port */
+	udev = hcd->self.root_hub->children[port];
+	if (!udev) {
+		ehci_err(ehci, "No device attached to the RootHub\n");
+		return -ENODEV;
+	}
+	buf = kmalloc(USB_DT_DEVICE_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
+	if (!dr) {
+		kfree(buf);
+		return -ENOMEM;
+	}
+
+	/* Fill Setup packet for GetDescriptor */
+	dr->bRequestType = USB_DIR_IN;
+	dr->bRequest = USB_REQ_GET_DESCRIPTOR;
+	dr->wValue = cpu_to_le16(USB_DT_DEVICE << 8);
+	dr->wIndex = 0;
+	dr->wLength = cpu_to_le16(USB_DT_DEVICE_SIZE);
+	urb = request_single_step_set_feature_urb(udev, dr, buf, &done);
+	if (!urb)
+		goto cleanup;
+
+	/* Now complete just the SETUP stage */
+	retval = submit_single_step_set_feature(hcd, urb, 1);
+	if (retval)
+		goto out1;
+	if (!wait_for_completion_timeout(&done, msecs_to_jiffies(2000))) {
+		usb_kill_urb(urb);
+		retval = -ETIMEDOUT;
+		ehci_err(ehci, "%s SETUP stage timed out on ep0\n", __func__);
+		goto out1;
+	}
+	msleep(15 * 1000);
+	/* Complete remaining DATA and status stages */
+	/* No need to free the URB, we can reuse the same */
+	urb->status = -EINPROGRESS;
+	usb_get_urb(urb);
+	atomic_inc(&urb->use_count);
+	atomic_inc(&urb->dev->urbnum);
+	retval = submit_single_step_set_feature(hcd, urb, 0);
+	if (!retval && !wait_for_completion_timeout(&done,
+						msecs_to_jiffies(2000))) {
+		usb_kill_urb(urb);
+		retval = -ETIMEDOUT;
+		ehci_err(ehci, "%s IN stage timed out on ep0\n", __func__);
+	}
+out1:
+	usb_free_urb(urb);
+cleanup:
+	kfree(dr);
+	kfree(buf);
+	return retval;
+}
+#endif
+/*-------------------------------------------------------------------------*/
 
 static int ehci_hub_control (
 	struct usb_hcd	*hcd,
@@ -978,14 +1130,29 @@
 			if ((temp & PORT_PE) == 0
 					|| (temp & PORT_RESET) != 0)
 				goto error;
-
+			/*port gets suspended as part of bus suspend routine*/
+			if (!ehci->susp_sof_bug)
+				ehci_writel(ehci, temp | PORT_SUSPEND,
+						status_reg);
+#ifdef	CONFIG_USB_OTG
+			if (hcd->self.otg_port == (wIndex + 1) &&
+					hcd->self.b_hnp_enable) {
+				set_bit(wIndex, &ehci->suspended_ports);
+				otg_start_hnp(ehci->transceiver->otg);
+				break;
+			}
+#endif
 			/* After above check the port must be connected.
 			 * Set appropriate bit thus could put phy into low power
 			 * mode if we have hostpc feature
 			 */
 			temp &= ~PORT_WKCONN_E;
 			temp |= PORT_WKDISC_E | PORT_WKOC_E;
-			ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
+			if (ehci->susp_sof_bug)
+				ehci_writel(ehci, temp, status_reg);
+			else
+				ehci_writel(ehci, temp | PORT_SUSPEND,
+						status_reg);
 			if (hostpc_reg) {
 				spin_unlock_irqrestore(&ehci->lock, flags);
 				msleep(5);/* 5ms for HCD enter low pwr mode */
@@ -1041,26 +1208,38 @@
 		 * about the EHCI-specific stuff.
 		 */
 		case USB_PORT_FEAT_TEST:
-			if (!selector || selector > 5)
-				goto error;
-			ehci_quiesce(ehci);
+			if (selector && selector <= 5) {
+				ehci_quiesce(ehci);
 
 			/* Put all enabled ports into suspend */
-			while (ports--) {
-				u32 __iomem *sreg =
+				while (ports--) {
+					u32 __iomem *sreg =
 						&ehci->regs->port_status[ports];
 
-				temp = ehci_readl(ehci, sreg) & ~PORT_RWC_BITS;
-				if (temp & PORT_PE)
-					ehci_writel(ehci, temp | PORT_SUSPEND,
+					temp = ehci_readl(ehci, sreg)
+					       	& ~PORT_RWC_BITS;
+					if (temp & PORT_PE)
+						ehci_writel(ehci,
+							temp | PORT_SUSPEND,
 							sreg);
+				}
+				ehci_halt(ehci);
+				temp = ehci_readl(ehci, status_reg);
+				temp |= selector << 16;
+				ehci_writel(ehci, temp, status_reg);
 			}
-			ehci_halt(ehci);
-			temp = ehci_readl(ehci, status_reg);
-			temp |= selector << 16;
-			ehci_writel(ehci, temp, status_reg);
+#ifdef CONFIG_USB_EHCI_EHSET
+			else if (selector
+				  == EHSET_TEST_SINGLE_STEP_SET_FEATURE) {
+				spin_unlock_irqrestore(&ehci->lock, flags);
+				retval = ehset_single_step_set_feature(hcd,
+								   wIndex);
+				spin_lock_irqsave(&ehci->lock, flags);
+			}
+#endif
+			else
+				goto error;
 			break;
-
 		default:
 			goto error;
 		}
diff --git a/drivers/usb/host/ehci-msm-hsic.c b/drivers/usb/host/ehci-msm-hsic.c
new file mode 100644
index 0000000..fd3d1ca
--- /dev/null
+++ b/drivers/usb/host/ehci-msm-hsic.c
@@ -0,0 +1,1154 @@
+/* ehci-msm-hsic.c - HSUSB Host Controller Driver Implementation
+ *
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * Partly derived from ehci-fsl.c and ehci-hcd.c
+ * Copyright (c) 2000-2004 by David Brownell
+ * Copyright (c) 2005 MontaVista Software
+ *
+ * All source code in this file is licensed under the following license except
+ * where indicated.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org
+ */
+
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/wakelock.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <mach/msm_bus.h>
+
+#include <linux/usb/msm_hsusb_hw.h>
+#include <linux/usb/msm_hsusb.h>
+#include <linux/gpio.h>
+#include <mach/clk.h>
+#include <mach/msm_iomap.h>
+#include <mach/msm_xo.h>
+#include <linux/spinlock.h>
+
+#define MSM_USB_BASE (hcd->regs)
+
+struct msm_hsic_hcd {
+	struct ehci_hcd		ehci;
+	struct device		*dev;
+	struct clk		*ahb_clk;
+	struct clk		*core_clk;
+	struct clk		*alt_core_clk;
+	struct clk		*phy_clk;
+	struct clk		*cal_clk;
+	struct regulator	*hsic_vddcx;
+	bool			async_int;
+	atomic_t                in_lpm;
+	struct wake_lock	wlock;
+	int			peripheral_status_irq;
+	int			wakeup_irq;
+	int			wakeup_gpio;
+	bool			wakeup_irq_enabled;
+	atomic_t		pm_usage_cnt;
+	uint32_t		bus_perf_client;
+	uint32_t		wakeup_int_cnt;
+};
+
+static bool debug_bus_voting_enabled = true;
+static inline struct msm_hsic_hcd *hcd_to_hsic(struct usb_hcd *hcd)
+{
+	return (struct msm_hsic_hcd *) (hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *hsic_to_hcd(struct msm_hsic_hcd *mehci)
+{
+	return container_of((void *) mehci, struct usb_hcd, hcd_priv);
+}
+
+#define ULPI_IO_TIMEOUT_USEC	(10 * 1000)
+
+#define USB_PHY_VDD_DIG_VOL_SUSP_MIN	500000 /* uV */
+#define USB_PHY_VDD_DIG_VOL_MIN		1000000 /* uV */
+#define USB_PHY_VDD_DIG_VOL_MAX		1320000 /* uV */
+#define USB_PHY_VDD_DIG_LOAD		49360	/* uA */
+
+#define HSIC_DBG1_REG		0x38
+
+static int msm_hsic_init_vddcx(struct msm_hsic_hcd *mehci, int init)
+{
+	int ret = 0;
+
+	if (!init)
+		goto disable_reg;
+
+	mehci->hsic_vddcx = devm_regulator_get(mehci->dev, "HSIC_VDDCX");
+	if (IS_ERR(mehci->hsic_vddcx)) {
+		dev_err(mehci->dev, "unable to get hsic vddcx\n");
+		return PTR_ERR(mehci->hsic_vddcx);
+	}
+
+	ret = regulator_set_voltage(mehci->hsic_vddcx,
+			USB_PHY_VDD_DIG_VOL_MIN,
+			USB_PHY_VDD_DIG_VOL_MAX);
+	if (ret) {
+		dev_err(mehci->dev, "unable to set the voltage"
+				"for hsic vddcx\n");
+		return ret;
+	}
+
+	ret = regulator_set_optimum_mode(mehci->hsic_vddcx,
+				USB_PHY_VDD_DIG_LOAD);
+	if (ret < 0) {
+		pr_err("%s: Unable to set optimum mode of the regulator:"
+					"VDDCX\n", __func__);
+		goto reg_optimum_mode_err;
+	}
+
+	ret = regulator_enable(mehci->hsic_vddcx);
+	if (ret) {
+		dev_err(mehci->dev, "unable to enable hsic vddcx\n");
+		goto reg_enable_err;
+	}
+
+	return 0;
+
+disable_reg:
+	regulator_disable(mehci->hsic_vddcx);
+reg_enable_err:
+	regulator_set_optimum_mode(mehci->hsic_vddcx, 0);
+reg_optimum_mode_err:
+	regulator_set_voltage(mehci->hsic_vddcx, 0,
+				USB_PHY_VDD_DIG_VOL_MIN);
+	return ret;
+
+}
+
+static int ulpi_write(struct msm_hsic_hcd *mehci, u32 val, u32 reg)
+{
+	struct usb_hcd *hcd = hsic_to_hcd(mehci);
+	int cnt = 0;
+
+	/* initiate write operation */
+	writel_relaxed(ULPI_RUN | ULPI_WRITE |
+	       ULPI_ADDR(reg) | ULPI_DATA(val),
+	       USB_ULPI_VIEWPORT);
+
+	/* wait for completion */
+	while (cnt < ULPI_IO_TIMEOUT_USEC) {
+		if (!(readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_RUN))
+			break;
+		udelay(1);
+		cnt++;
+	}
+
+	if (cnt >= ULPI_IO_TIMEOUT_USEC) {
+		dev_err(mehci->dev, "ulpi_write: timeout\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static int msm_hsic_config_gpios(struct msm_hsic_hcd *mehci, int gpio_en)
+{
+	int rc = 0;
+	struct msm_hsic_host_platform_data *pdata;
+	static int gpio_status;
+
+	pdata = mehci->dev->platform_data;
+
+	if (!pdata || !pdata->strobe || !pdata->data)
+		return rc;
+
+	if (gpio_status == gpio_en)
+		return 0;
+
+	gpio_status = gpio_en;
+
+	if (!gpio_en)
+		goto free_gpio;
+
+	rc = gpio_request(pdata->strobe, "HSIC_STROBE_GPIO");
+	if (rc < 0) {
+		dev_err(mehci->dev, "gpio request failed for HSIC STROBE\n");
+		return rc;
+	}
+
+	rc = gpio_request(pdata->data, "HSIC_DATA_GPIO");
+	if (rc < 0) {
+		dev_err(mehci->dev, "gpio request failed for HSIC DATA\n");
+		goto free_strobe;
+		}
+
+	if (mehci->wakeup_gpio) {
+		rc = gpio_request(mehci->wakeup_gpio, "HSIC_WAKEUP_GPIO");
+		if (rc < 0) {
+			dev_err(mehci->dev, "gpio request failed for HSIC WAKEUP\n");
+			goto free_data;
+		}
+	}
+
+	return 0;
+
+free_gpio:
+	if (mehci->wakeup_gpio)
+		gpio_free(mehci->wakeup_gpio);
+free_data:
+	gpio_free(pdata->data);
+free_strobe:
+	gpio_free(pdata->strobe);
+
+	return rc;
+}
+
+static int msm_hsic_phy_clk_reset(struct msm_hsic_hcd *mehci)
+{
+	int ret;
+
+	clk_prepare_enable(mehci->alt_core_clk);
+
+	ret = clk_reset(mehci->core_clk, CLK_RESET_ASSERT);
+	if (ret) {
+		clk_disable_unprepare(mehci->alt_core_clk);
+		dev_err(mehci->dev, "usb phy clk assert failed\n");
+		return ret;
+	}
+	usleep_range(10000, 12000);
+	clk_disable_unprepare(mehci->alt_core_clk);
+
+	ret = clk_reset(mehci->core_clk, CLK_RESET_DEASSERT);
+	if (ret)
+		dev_err(mehci->dev, "usb phy clk deassert failed\n");
+
+	return ret;
+}
+
+static int msm_hsic_phy_reset(struct msm_hsic_hcd *mehci)
+{
+	struct usb_hcd *hcd = hsic_to_hcd(mehci);
+	u32 val;
+	int ret;
+
+	ret = msm_hsic_phy_clk_reset(mehci);
+	if (ret)
+		return ret;
+
+	val = readl_relaxed(USB_PORTSC) & ~PORTSC_PTS_MASK;
+	writel_relaxed(val | PORTSC_PTS_ULPI, USB_PORTSC);
+
+	/* Ensure that RESET operation is completed before turning off clock */
+	mb();
+	dev_dbg(mehci->dev, "phy_reset: success\n");
+
+	return 0;
+}
+
+#define HSIC_GPIO150_PAD_CTL   (MSM_TLMM_BASE+0x20C0)
+#define HSIC_GPIO151_PAD_CTL   (MSM_TLMM_BASE+0x20C4)
+#define HSIC_CAL_PAD_CTL       (MSM_TLMM_BASE+0x20C8)
+#define HSIC_LV_MODE		0x04
+#define HSIC_PAD_CALIBRATION	0xA8
+#define HSIC_GPIO_PAD_VAL	0x0A0AAA10
+#define LINK_RESET_TIMEOUT_USEC		(250 * 1000)
+static int msm_hsic_reset(struct msm_hsic_hcd *mehci)
+{
+	struct usb_hcd *hcd = hsic_to_hcd(mehci);
+	int cnt = 0;
+	int ret;
+	struct msm_hsic_host_platform_data *pdata = mehci->dev->platform_data;
+
+	ret = msm_hsic_phy_reset(mehci);
+	if (ret) {
+		dev_err(mehci->dev, "phy_reset failed\n");
+		return ret;
+	}
+
+	writel_relaxed(USBCMD_RESET, USB_USBCMD);
+	while (cnt < LINK_RESET_TIMEOUT_USEC) {
+		if (!(readl_relaxed(USB_USBCMD) & USBCMD_RESET))
+			break;
+		udelay(1);
+		cnt++;
+	}
+	if (cnt >= LINK_RESET_TIMEOUT_USEC)
+		return -ETIMEDOUT;
+
+	/* Reset PORTSC and select ULPI phy */
+	writel_relaxed(0x80000000, USB_PORTSC);
+
+	/* TODO: Need to confirm if HSIC PHY also requires delay after RESET */
+	msleep(100);
+
+	/* HSIC PHY Initialization */
+
+	/* HSIC init sequence when HSIC signals (Strobe/Data) are
+	routed via GPIOs */
+	if (pdata && pdata->strobe && pdata->data) {
+
+		/* Enable LV_MODE in HSIC_CAL_PAD_CTL register */
+		writel_relaxed(HSIC_LV_MODE, HSIC_CAL_PAD_CTL);
+
+		/*set periodic calibration interval to ~2.048sec in
+		  HSIC_IO_CAL_REG */
+		ulpi_write(mehci, 0xFF, 0x33);
+
+		/* Enable periodic IO calibration in HSIC_CFG register */
+		ulpi_write(mehci, HSIC_PAD_CALIBRATION, 0x30);
+
+		/* Configure GPIO 150/151 pins for HSIC functionality mode */
+		ret = msm_hsic_config_gpios(mehci, 1);
+		if (ret) {
+			dev_err(mehci->dev, " gpio configuarion failed\n");
+			return ret;
+		}
+		/* Set LV_MODE=0x1 and DCC=0x2 in HSIC_GPIO150/151_PAD_CTL
+		   register */
+		writel_relaxed(HSIC_GPIO_PAD_VAL, HSIC_GPIO150_PAD_CTL);
+		writel_relaxed(HSIC_GPIO_PAD_VAL, HSIC_GPIO151_PAD_CTL);
+		/* Enable HSIC mode in HSIC_CFG register */
+		ulpi_write(mehci, 0x01, 0x31);
+	} else {
+		/* HSIC init sequence when HSIC signals (Strobe/Data) are routed
+		via dedicated I/O */
+
+		/* programmable length of connect signaling (33.2ns) */
+		ret = ulpi_write(mehci, 3, HSIC_DBG1_REG);
+		if (ret) {
+			pr_err("%s: Unable to program length of connect "
+			      "signaling\n", __func__);
+		}
+
+		/*set periodic calibration interval to ~2.048sec in
+		  HSIC_IO_CAL_REG */
+		ulpi_write(mehci, 0xFF, 0x33);
+
+		/* Enable HSIC mode in HSIC_CFG register */
+		ulpi_write(mehci, 0xA9, 0x30);
+	}
+
+	/*disable auto resume*/
+	ulpi_write(mehci, ULPI_IFC_CTRL_AUTORESUME, ULPI_CLR(ULPI_IFC_CTRL));
+
+	return 0;
+}
+
+#define PHY_SUSPEND_TIMEOUT_USEC	(500 * 1000)
+#define PHY_RESUME_TIMEOUT_USEC		(100 * 1000)
+
+#ifdef CONFIG_PM_SLEEP
+static int msm_hsic_suspend(struct msm_hsic_hcd *mehci)
+{
+	struct usb_hcd *hcd = hsic_to_hcd(mehci);
+	int cnt = 0, ret;
+	u32 val;
+
+	if (atomic_read(&mehci->in_lpm)) {
+		dev_dbg(mehci->dev, "%s called in lpm\n", __func__);
+		return 0;
+	}
+
+	if (!(readl_relaxed(USB_PORTSC) & PORT_PE)) {
+		dev_dbg(mehci->dev, "%s:port is not enabled skip suspend\n",
+				__func__);
+		return -EAGAIN;
+	}
+
+	disable_irq(hcd->irq);
+
+	/* make sure we don't race against a remote wakeup */
+	if (test_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags) ||
+	    readl_relaxed(USB_PORTSC) & PORT_RESUME) {
+		dev_dbg(mehci->dev, "wakeup pending, aborting suspend\n");
+		enable_irq(hcd->irq);
+		return -EBUSY;
+	}
+
+	/*
+	 * PHY may take some time or even fail to enter into low power
+	 * mode (LPM). Hence poll for 500 msec and reset the PHY and link
+	 * in failure case.
+	 */
+	val = readl_relaxed(USB_PORTSC);
+	val &= ~PORT_RWC_BITS;
+	val |= PORTSC_PHCD;
+	writel_relaxed(val, USB_PORTSC);
+	while (cnt < PHY_SUSPEND_TIMEOUT_USEC) {
+		if (readl_relaxed(USB_PORTSC) & PORTSC_PHCD)
+			break;
+		udelay(1);
+		cnt++;
+	}
+
+	if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) {
+		dev_err(mehci->dev, "Unable to suspend PHY\n");
+		msm_hsic_config_gpios(mehci, 0);
+		msm_hsic_reset(mehci);
+	}
+
+	/*
+	 * PHY has capability to generate interrupt asynchronously in low
+	 * power mode (LPM). This interrupt is level triggered. So USB IRQ
+	 * line must be disabled till async interrupt enable bit is cleared
+	 * in USBCMD register. Assert STP (ULPI interface STOP signal) to
+	 * block data communication from PHY.
+	 */
+	writel_relaxed(readl_relaxed(USB_USBCMD) | ASYNC_INTR_CTRL |
+				ULPI_STP_CTRL, USB_USBCMD);
+
+	/*
+	 * Ensure that hardware is put in low power mode before
+	 * clocks are turned OFF and VDD is allowed to minimize.
+	 */
+	mb();
+
+	clk_disable_unprepare(mehci->core_clk);
+	clk_disable_unprepare(mehci->phy_clk);
+	clk_disable_unprepare(mehci->cal_clk);
+	clk_disable_unprepare(mehci->ahb_clk);
+
+	ret = regulator_set_voltage(mehci->hsic_vddcx,
+				USB_PHY_VDD_DIG_VOL_SUSP_MIN,
+				USB_PHY_VDD_DIG_VOL_MAX);
+	if (ret < 0)
+		dev_err(mehci->dev, "unable to set vddcx voltage: min:0.5v max:1.3v\n");
+
+	if (mehci->bus_perf_client && debug_bus_voting_enabled) {
+		ret = msm_bus_scale_client_update_request(
+				mehci->bus_perf_client, 0);
+		if (ret)
+			dev_err(mehci->dev, "%s: Failed to dvote for "
+				   "bus bandwidth %d\n", __func__, ret);
+	}
+
+	atomic_set(&mehci->in_lpm, 1);
+	enable_irq(hcd->irq);
+
+	mehci->wakeup_irq_enabled = 1;
+	enable_irq_wake(mehci->wakeup_irq);
+	enable_irq(mehci->wakeup_irq);
+
+	wake_unlock(&mehci->wlock);
+
+	dev_info(mehci->dev, "HSIC-USB in low power mode\n");
+
+	return 0;
+}
+
+static int msm_hsic_resume(struct msm_hsic_hcd *mehci)
+{
+	struct usb_hcd *hcd = hsic_to_hcd(mehci);
+	int cnt = 0, ret;
+	unsigned temp;
+
+	if (!atomic_read(&mehci->in_lpm)) {
+		dev_dbg(mehci->dev, "%s called in !in_lpm\n", __func__);
+		return 0;
+	}
+
+	if (mehci->wakeup_irq_enabled) {
+		disable_irq_wake(mehci->wakeup_irq);
+		disable_irq_nosync(mehci->wakeup_irq);
+		mehci->wakeup_irq_enabled = 0;
+	}
+
+	wake_lock(&mehci->wlock);
+
+	if (mehci->bus_perf_client && debug_bus_voting_enabled) {
+		ret = msm_bus_scale_client_update_request(
+				mehci->bus_perf_client, 1);
+		if (ret)
+			dev_err(mehci->dev, "%s: Failed to vote for "
+				   "bus bandwidth %d\n", __func__, ret);
+	}
+
+	ret = regulator_set_voltage(mehci->hsic_vddcx,
+				USB_PHY_VDD_DIG_VOL_MIN,
+				USB_PHY_VDD_DIG_VOL_MAX);
+	if (ret < 0)
+		dev_err(mehci->dev, "unable to set vddcx voltage: min:1v max:1.3v\n");
+
+	clk_prepare_enable(mehci->core_clk);
+	clk_prepare_enable(mehci->phy_clk);
+	clk_prepare_enable(mehci->cal_clk);
+	clk_prepare_enable(mehci->ahb_clk);
+
+	temp = readl_relaxed(USB_USBCMD);
+	temp &= ~ASYNC_INTR_CTRL;
+	temp &= ~ULPI_STP_CTRL;
+	writel_relaxed(temp, USB_USBCMD);
+
+	if (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD))
+		goto skip_phy_resume;
+
+	temp = readl_relaxed(USB_PORTSC);
+	temp &= ~(PORT_RWC_BITS | PORTSC_PHCD);
+	writel_relaxed(temp, USB_PORTSC);
+	while (cnt < PHY_RESUME_TIMEOUT_USEC) {
+		if (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD) &&
+			(readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_SYNC_STATE))
+			break;
+		udelay(1);
+		cnt++;
+	}
+
+	if (cnt >= PHY_RESUME_TIMEOUT_USEC) {
+		/*
+		 * This is a fatal error. Reset the link and
+		 * PHY to make hsic working.
+		 */
+		dev_err(mehci->dev, "Unable to resume USB. Reset the hsic\n");
+		msm_hsic_config_gpios(mehci, 0);
+		msm_hsic_reset(mehci);
+	}
+
+skip_phy_resume:
+
+	usb_hcd_resume_root_hub(hcd);
+
+	atomic_set(&mehci->in_lpm, 0);
+
+	if (mehci->async_int) {
+		mehci->async_int = false;
+		pm_runtime_put_noidle(mehci->dev);
+		enable_irq(hcd->irq);
+	}
+
+	if (atomic_read(&mehci->pm_usage_cnt)) {
+		atomic_set(&mehci->pm_usage_cnt, 0);
+		pm_runtime_put_noidle(mehci->dev);
+	}
+
+	dev_info(mehci->dev, "HSIC-USB exited from low power mode\n");
+
+	return 0;
+}
+#endif
+
+static irqreturn_t msm_hsic_irq(struct usb_hcd *hcd)
+{
+	struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
+
+	if (atomic_read(&mehci->in_lpm)) {
+		disable_irq_nosync(hcd->irq);
+		dev_dbg(mehci->dev, "phy async intr\n");
+		mehci->async_int = true;
+		pm_runtime_get(mehci->dev);
+		return IRQ_HANDLED;
+	}
+
+	return ehci_irq(hcd);
+}
+
+static int ehci_hsic_reset(struct usb_hcd *hcd)
+{
+	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+	int retval;
+
+	ehci->caps = USB_CAPLENGTH;
+	ehci->regs = USB_CAPLENGTH +
+		HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
+	dbg_hcs_params(ehci, "reset");
+	dbg_hcc_params(ehci, "reset");
+
+	/* cache the data to minimize the chip reads*/
+	ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
+
+	hcd->has_tt = 1;
+	ehci->sbrn = HCD_USB2;
+
+	retval = ehci_halt(ehci);
+	if (retval)
+		return retval;
+
+	/* data structure init */
+	retval = ehci_init(hcd);
+	if (retval)
+		return retval;
+
+	retval = ehci_reset(ehci);
+	if (retval)
+		return retval;
+
+	/* bursts of unspecified length. */
+	writel_relaxed(0, USB_AHBBURST);
+	/* Use the AHB transactor */
+	writel_relaxed(0x08, USB_AHBMODE);
+	/* Disable streaming mode and select host mode */
+	writel_relaxed(0x13, USB_USBMODE);
+
+	ehci_port_power(ehci, 1);
+	return 0;
+}
+
+static struct hc_driver msm_hsic_driver = {
+	.description		= hcd_name,
+	.product_desc		= "Qualcomm EHCI Host Controller using HSIC",
+	.hcd_priv_size		= sizeof(struct msm_hsic_hcd),
+
+	/*
+	 * generic hardware linkage
+	 */
+	.irq			= msm_hsic_irq,
+	.flags			= HCD_USB2 | HCD_MEMORY,
+
+	.reset			= ehci_hsic_reset,
+	.start			= ehci_run,
+
+	.stop			= ehci_stop,
+	.shutdown		= ehci_shutdown,
+
+	/*
+	 * managing i/o requests and associated device resources
+	 */
+	.urb_enqueue		= ehci_urb_enqueue,
+	.urb_dequeue		= ehci_urb_dequeue,
+	.endpoint_disable	= ehci_endpoint_disable,
+	.endpoint_reset		= ehci_endpoint_reset,
+	.clear_tt_buffer_complete	 = ehci_clear_tt_buffer_complete,
+
+	/*
+	 * scheduling support
+	 */
+	.get_frame_number	= ehci_get_frame,
+
+	/*
+	 * root hub support
+	 */
+	.hub_status_data	= ehci_hub_status_data,
+	.hub_control		= ehci_hub_control,
+	.relinquish_port	= ehci_relinquish_port,
+	.port_handed_over	= ehci_port_handed_over,
+
+	/*
+	 * PM support
+	 */
+	.bus_suspend		= ehci_bus_suspend,
+	.bus_resume		= ehci_bus_resume,
+};
+
+static int msm_hsic_init_clocks(struct msm_hsic_hcd *mehci, u32 init)
+{
+	int ret = 0;
+
+	if (!init)
+		goto put_clocks;
+
+	/*core_clk is required for LINK protocol engine
+	 *clock rate appropriately set by target specific clock driver */
+	mehci->core_clk = clk_get(mehci->dev, "core_clk");
+	if (IS_ERR(mehci->core_clk)) {
+		dev_err(mehci->dev, "failed to get core_clk\n");
+		ret = PTR_ERR(mehci->core_clk);
+		return ret;
+	}
+
+	/* alt_core_clk is for LINK to be used during PHY RESET
+	 * clock rate appropriately set by target specific clock driver */
+	mehci->alt_core_clk = clk_get(mehci->dev, "alt_core_clk");
+	if (IS_ERR(mehci->alt_core_clk)) {
+		dev_err(mehci->dev, "failed to core_clk\n");
+		ret = PTR_ERR(mehci->alt_core_clk);
+		goto put_core_clk;
+	}
+
+	/* phy_clk is required for HSIC PHY operation
+	 * clock rate appropriately set by target specific clock driver */
+	mehci->phy_clk = clk_get(mehci->dev, "phy_clk");
+	if (IS_ERR(mehci->phy_clk)) {
+		dev_err(mehci->dev, "failed to get phy_clk\n");
+		ret = PTR_ERR(mehci->phy_clk);
+		goto put_alt_core_clk;
+	}
+
+	/* 10MHz cal_clk is required for calibration of I/O pads */
+	mehci->cal_clk = clk_get(mehci->dev, "cal_clk");
+	if (IS_ERR(mehci->cal_clk)) {
+		dev_err(mehci->dev, "failed to get cal_clk\n");
+		ret = PTR_ERR(mehci->cal_clk);
+		goto put_phy_clk;
+	}
+	clk_set_rate(mehci->cal_clk, 10000000);
+
+	/* ahb_clk is required for data transfers */
+	mehci->ahb_clk = clk_get(mehci->dev, "iface_clk");
+	if (IS_ERR(mehci->ahb_clk)) {
+		dev_err(mehci->dev, "failed to get iface_clk\n");
+		ret = PTR_ERR(mehci->ahb_clk);
+		goto put_cal_clk;
+	}
+
+	clk_prepare_enable(mehci->core_clk);
+	clk_prepare_enable(mehci->phy_clk);
+	clk_prepare_enable(mehci->cal_clk);
+	clk_prepare_enable(mehci->ahb_clk);
+
+	return 0;
+
+put_clocks:
+	if (!atomic_read(&mehci->in_lpm)) {
+		clk_disable_unprepare(mehci->core_clk);
+		clk_disable_unprepare(mehci->phy_clk);
+		clk_disable_unprepare(mehci->cal_clk);
+		clk_disable_unprepare(mehci->ahb_clk);
+	}
+	clk_put(mehci->ahb_clk);
+put_cal_clk:
+	clk_put(mehci->cal_clk);
+put_phy_clk:
+	clk_put(mehci->phy_clk);
+put_alt_core_clk:
+	clk_put(mehci->alt_core_clk);
+put_core_clk:
+	clk_put(mehci->core_clk);
+
+	return ret;
+}
+static irqreturn_t hsic_peripheral_status_change(int irq, void *dev_id)
+{
+	struct msm_hsic_hcd *mehci = dev_id;
+
+	pr_debug("%s: mehci:%p dev_id:%p\n", __func__, mehci, dev_id);
+
+	if (mehci)
+		msm_hsic_config_gpios(mehci, 0);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t msm_hsic_wakeup_irq(int irq, void *data)
+{
+	struct msm_hsic_hcd *mehci = data;
+
+	mehci->wakeup_int_cnt++;
+	dev_dbg(mehci->dev, "%s: hsic remote wakeup interrupt cnt: %u\n",
+			__func__, mehci->wakeup_int_cnt);
+
+	wake_lock(&mehci->wlock);
+
+	if (mehci->wakeup_irq_enabled) {
+		mehci->wakeup_irq_enabled = 0;
+		disable_irq_wake(irq);
+		disable_irq_nosync(irq);
+	}
+
+	if (!atomic_read(&mehci->pm_usage_cnt)) {
+		atomic_set(&mehci->pm_usage_cnt, 1);
+		pm_runtime_get(mehci->dev);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int ehci_hsic_msm_bus_show(struct seq_file *s, void *unused)
+{
+	if (debug_bus_voting_enabled)
+		seq_printf(s, "enabled\n");
+	else
+		seq_printf(s, "disabled\n");
+
+	return 0;
+}
+
+static int ehci_hsic_msm_bus_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, ehci_hsic_msm_bus_show, inode->i_private);
+}
+
+static ssize_t ehci_hsic_msm_bus_write(struct file *file,
+	const char __user *ubuf, size_t count, loff_t *ppos)
+{
+	char buf[8];
+	int ret;
+	struct seq_file *s = file->private_data;
+	struct msm_hsic_hcd *mehci = s->private;
+
+	memset(buf, 0x00, sizeof(buf));
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	if (!strncmp(buf, "enable", 6)) {
+		/* Do not vote here. Let hsic driver decide when to vote */
+		debug_bus_voting_enabled = true;
+	} else {
+		debug_bus_voting_enabled = false;
+		if (mehci->bus_perf_client) {
+			ret = msm_bus_scale_client_update_request(
+					mehci->bus_perf_client, 0);
+			if (ret)
+				dev_err(mehci->dev, "%s: Failed to devote "
+					   "for bus bw %d\n", __func__, ret);
+		}
+	}
+
+	return count;
+}
+
+const struct file_operations ehci_hsic_msm_bus_fops = {
+	.open = ehci_hsic_msm_bus_open,
+	.read = seq_read,
+	.write = ehci_hsic_msm_bus_write,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static int ehci_hsic_msm_wakeup_cnt_show(struct seq_file *s, void *unused)
+{
+	struct msm_hsic_hcd *mehci = s->private;
+
+	seq_printf(s, "%u\n", mehci->wakeup_int_cnt);
+
+	return 0;
+}
+
+static int ehci_hsic_msm_wakeup_cnt_open(struct inode *inode, struct file *f)
+{
+	return single_open(f, ehci_hsic_msm_wakeup_cnt_show, inode->i_private);
+}
+
+const struct file_operations ehci_hsic_msm_wakeup_cnt_fops = {
+	.open = ehci_hsic_msm_wakeup_cnt_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static struct dentry *ehci_hsic_msm_dbg_root;
+static int ehci_hsic_msm_debugfs_init(struct msm_hsic_hcd *mehci)
+{
+	struct dentry *ehci_hsic_msm_dentry;
+
+	ehci_hsic_msm_dbg_root = debugfs_create_dir("ehci_hsic_msm_dbg", NULL);
+
+	if (!ehci_hsic_msm_dbg_root || IS_ERR(ehci_hsic_msm_dbg_root))
+		return -ENODEV;
+
+	ehci_hsic_msm_dentry = debugfs_create_file("bus_voting",
+		S_IRUGO | S_IWUSR,
+		ehci_hsic_msm_dbg_root, mehci,
+		&ehci_hsic_msm_bus_fops);
+
+	if (!ehci_hsic_msm_dentry) {
+		debugfs_remove_recursive(ehci_hsic_msm_dbg_root);
+		return -ENODEV;
+	}
+
+	ehci_hsic_msm_dentry = debugfs_create_file("wakeup_cnt",
+		S_IRUGO,
+		ehci_hsic_msm_dbg_root, mehci,
+		&ehci_hsic_msm_wakeup_cnt_fops);
+
+	if (!ehci_hsic_msm_dentry) {
+		debugfs_remove_recursive(ehci_hsic_msm_dbg_root);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void ehci_hsic_msm_debugfs_cleanup(void)
+{
+	debugfs_remove_recursive(ehci_hsic_msm_dbg_root);
+}
+
+static int __devinit ehci_hsic_msm_probe(struct platform_device *pdev)
+{
+	struct usb_hcd *hcd;
+	struct resource *res;
+	struct msm_hsic_hcd *mehci;
+	struct msm_hsic_host_platform_data *pdata;
+	int ret;
+
+	dev_dbg(&pdev->dev, "ehci_msm-hsic probe\n");
+
+	/* After parent device's probe is executed, it will be put in suspend
+	 * mode. When child device's probe is called, driver core is not
+	 * resuming parent device due to which parent will be in suspend even
+	 * though child is active. Hence resume the parent device explicitly.
+	 */
+	if (pdev->dev.parent)
+		pm_runtime_get_sync(pdev->dev.parent);
+
+	hcd = usb_create_hcd(&msm_hsic_driver, &pdev->dev,
+				dev_name(&pdev->dev));
+	if (!hcd) {
+		dev_err(&pdev->dev, "Unable to create HCD\n");
+		return  -ENOMEM;
+	}
+
+	hcd->irq = platform_get_irq(pdev, 0);
+	if (hcd->irq < 0) {
+		dev_err(&pdev->dev, "Unable to get IRQ resource\n");
+		ret = hcd->irq;
+		goto put_hcd;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "Unable to get memory resource\n");
+		ret = -ENODEV;
+		goto put_hcd;
+	}
+
+	hcd->rsrc_start = res->start;
+	hcd->rsrc_len = resource_size(res);
+	hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+	if (!hcd->regs) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		ret = -ENOMEM;
+		goto put_hcd;
+	}
+
+	mehci = hcd_to_hsic(hcd);
+	mehci->dev = &pdev->dev;
+
+	mehci->ehci.susp_sof_bug = 1;
+
+	res = platform_get_resource_byname(pdev,
+			IORESOURCE_IRQ,
+			"peripheral_status_irq");
+	if (res)
+		mehci->peripheral_status_irq = res->start;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IO, "wakeup");
+	if (res) {
+		mehci->wakeup_gpio = res->start;
+		mehci->wakeup_irq = MSM_GPIO_TO_INT(res->start);
+		dev_dbg(mehci->dev, "wakeup_irq: %d\n", mehci->wakeup_irq);
+	}
+
+	ret = msm_hsic_init_clocks(mehci, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to initialize clocks\n");
+		ret = -ENODEV;
+		goto unmap;
+	}
+
+	ret = msm_hsic_init_vddcx(mehci, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to initialize VDDCX\n");
+		ret = -ENODEV;
+		goto deinit_clocks;
+	}
+
+	ret = msm_hsic_reset(mehci);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to initialize PHY\n");
+		goto deinit_vddcx;
+	}
+
+	ret = usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to register HCD\n");
+		goto unconfig_gpio;
+	}
+
+	device_init_wakeup(&pdev->dev, 1);
+	wake_lock_init(&mehci->wlock, WAKE_LOCK_SUSPEND, dev_name(&pdev->dev));
+	wake_lock(&mehci->wlock);
+
+	if (mehci->peripheral_status_irq) {
+		ret = request_threaded_irq(mehci->peripheral_status_irq,
+			NULL, hsic_peripheral_status_change,
+			IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
+						| IRQF_SHARED,
+			"hsic_peripheral_status", mehci);
+		if (ret)
+			dev_err(&pdev->dev, "%s:request_irq:%d failed:%d",
+				__func__, mehci->peripheral_status_irq, ret);
+	}
+
+	/* configure wakeup irq */
+	if (mehci->wakeup_irq) {
+		ret = request_irq(mehci->wakeup_irq, msm_hsic_wakeup_irq,
+				IRQF_TRIGGER_HIGH,
+				"msm_hsic_wakeup", mehci);
+		if (!ret) {
+			disable_irq_nosync(mehci->wakeup_irq);
+		} else {
+			dev_err(&pdev->dev, "request_irq(%d) failed: %d\n",
+					mehci->wakeup_irq, ret);
+			mehci->wakeup_irq = 0;
+		}
+	}
+
+	ret = ehci_hsic_msm_debugfs_init(mehci);
+	if (ret)
+		dev_dbg(&pdev->dev, "mode debugfs file is"
+			"not available\n");
+
+	pdata = mehci->dev->platform_data;
+	if (pdata && pdata->bus_scale_table) {
+		mehci->bus_perf_client =
+		    msm_bus_scale_register_client(pdata->bus_scale_table);
+		/* Configure BUS performance parameters for MAX bandwidth */
+		if (mehci->bus_perf_client) {
+			ret = msm_bus_scale_client_update_request(
+					mehci->bus_perf_client, 1);
+			if (ret)
+				dev_err(&pdev->dev, "%s: Failed to vote for "
+					   "bus bandwidth %d\n", __func__, ret);
+		} else {
+			dev_err(&pdev->dev, "%s: Failed to register BUS "
+						"scaling client!!\n", __func__);
+		}
+	}
+
+	/*
+	 * This pdev->dev is assigned parent of root-hub by USB core,
+	 * hence, runtime framework automatically calls this driver's
+	 * runtime APIs based on root-hub's state.
+	 */
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+	/* Decrement the parent device's counter after probe.
+	 * As child is active, parent will not be put into
+	 * suspend mode.
+	 */
+	if (pdev->dev.parent)
+		pm_runtime_put_sync(pdev->dev.parent);
+
+	return 0;
+
+unconfig_gpio:
+	msm_hsic_config_gpios(mehci, 0);
+deinit_vddcx:
+	msm_hsic_init_vddcx(mehci, 0);
+deinit_clocks:
+	msm_hsic_init_clocks(mehci, 0);
+unmap:
+	iounmap(hcd->regs);
+put_hcd:
+	usb_put_hcd(hcd);
+
+	return ret;
+}
+
+static int __devexit ehci_hsic_msm_remove(struct platform_device *pdev)
+{
+	struct usb_hcd *hcd = platform_get_drvdata(pdev);
+	struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
+
+	if (mehci->peripheral_status_irq)
+		free_irq(mehci->peripheral_status_irq, mehci);
+
+	if (mehci->wakeup_irq) {
+		if (mehci->wakeup_irq_enabled)
+			disable_irq_wake(mehci->wakeup_irq);
+		free_irq(mehci->wakeup_irq, mehci);
+	}
+
+	if (mehci->bus_perf_client)
+		msm_bus_scale_unregister_client(mehci->bus_perf_client);
+
+	ehci_hsic_msm_debugfs_cleanup();
+	device_init_wakeup(&pdev->dev, 0);
+	pm_runtime_set_suspended(&pdev->dev);
+
+	usb_remove_hcd(hcd);
+	msm_hsic_config_gpios(mehci, 0);
+	msm_hsic_init_vddcx(mehci, 0);
+
+	msm_hsic_init_clocks(mehci, 0);
+	wake_lock_destroy(&mehci->wlock);
+	iounmap(hcd->regs);
+	usb_put_hcd(hcd);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int msm_hsic_pm_suspend(struct device *dev)
+{
+	int ret;
+	struct usb_hcd *hcd = dev_get_drvdata(dev);
+	struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
+
+	dev_dbg(dev, "ehci-msm-hsic PM suspend\n");
+
+	if (device_may_wakeup(dev))
+		enable_irq_wake(hcd->irq);
+
+	ret = msm_hsic_suspend(mehci);
+
+	if (ret && device_may_wakeup(dev))
+		disable_irq_wake(hcd->irq);
+
+	return ret;
+}
+
+static int msm_hsic_pm_resume(struct device *dev)
+{
+	int ret;
+	struct usb_hcd *hcd = dev_get_drvdata(dev);
+	struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
+
+	if (device_may_wakeup(dev))
+		disable_irq_wake(hcd->irq);
+
+	ret = msm_hsic_resume(mehci);
+	if (ret)
+		return ret;
+
+	/* Bring the device to full powered state upon system resume */
+	pm_runtime_disable(dev);
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+static int msm_hsic_runtime_idle(struct device *dev)
+{
+	dev_dbg(dev, "EHCI runtime idle\n");
+	return 0;
+}
+
+static int msm_hsic_runtime_suspend(struct device *dev)
+{
+	struct usb_hcd *hcd = dev_get_drvdata(dev);
+	struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
+
+	dev_dbg(dev, "EHCI runtime suspend\n");
+	return msm_hsic_suspend(mehci);
+}
+
+static int msm_hsic_runtime_resume(struct device *dev)
+{
+	struct usb_hcd *hcd = dev_get_drvdata(dev);
+	struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
+
+	dev_dbg(dev, "EHCI runtime resume\n");
+	return msm_hsic_resume(mehci);
+}
+#endif
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops msm_hsic_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(msm_hsic_pm_suspend, msm_hsic_pm_resume)
+	SET_RUNTIME_PM_OPS(msm_hsic_runtime_suspend, msm_hsic_runtime_resume,
+				msm_hsic_runtime_idle)
+};
+#endif
+
+static struct platform_driver ehci_msm_hsic_driver = {
+	.probe	= ehci_hsic_msm_probe,
+	.remove	= __devexit_p(ehci_hsic_msm_remove),
+	.driver = {
+		.name = "msm_hsic_host",
+#ifdef CONFIG_PM
+		.pm = &msm_hsic_dev_pm_ops,
+#endif
+	},
+};
diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c
index 9803a55..e8e4e10 100644
--- a/drivers/usb/host/ehci-msm.c
+++ b/drivers/usb/host/ehci-msm.c
@@ -1,6 +1,6 @@
 /* ehci-msm.c - HSUSB Host Controller Driver Implementation
  *
- * Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
  *
  * Partly derived from ehci-fsl.c and ehci-hcd.c
  * Copyright (c) 2000-2004 by David Brownell
@@ -49,7 +49,7 @@
 	/* bursts of unspecified length. */
 	writel(0, USB_AHBBURST);
 	/* Use the AHB transactor */
-	writel(0, USB_AHBMODE);
+	writel_relaxed(0x08, USB_AHBMODE);
 	/* Disable streaming mode and select host mode */
 	writel(0x13, USB_USBMODE);
 
@@ -158,12 +158,8 @@
 		goto put_transceiver;
 	}
 
+	hcd_to_ehci(hcd)->transceiver = phy;
 	device_init_wakeup(&pdev->dev, 1);
-	/*
-	 * OTG device parent of HCD takes care of putting
-	 * hardware into low power mode.
-	 */
-	pm_runtime_no_callbacks(&pdev->dev);
 	pm_runtime_enable(&pdev->dev);
 
 	return 0;
@@ -186,6 +182,7 @@
 	pm_runtime_disable(&pdev->dev);
 	pm_runtime_set_suspended(&pdev->dev);
 
+	hcd_to_ehci(hcd)->transceiver = NULL;
 	otg_set_host(phy->otg, NULL);
 	usb_put_transceiver(phy);
 
@@ -194,7 +191,31 @@
 	return 0;
 }
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_RUNTIME
+static int ehci_msm_runtime_idle(struct device *dev)
+{
+	dev_dbg(dev, "ehci runtime idle\n");
+	return 0;
+}
+
+static int ehci_msm_runtime_suspend(struct device *dev)
+{
+	dev_dbg(dev, "ehci runtime suspend\n");
+	/*
+	 * Notify OTG about suspend.  It takes care of
+	 * putting the hardware in LPM.
+	 */
+	return usb_phy_set_suspend(phy, 1);
+}
+
+static int ehci_msm_runtime_resume(struct device *dev)
+{
+	dev_dbg(dev, "ehci runtime resume\n");
+	return usb_phy_set_suspend(phy, 0);
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
 static int ehci_msm_pm_suspend(struct device *dev)
 {
 	struct usb_hcd *hcd = dev_get_drvdata(dev);
@@ -202,6 +223,9 @@
 
 	dev_dbg(dev, "ehci-msm PM suspend\n");
 
+	if (!hcd->rh_registered)
+		return 0;
+
 	/*
 	 * EHCI helper function has also the same check before manipulating
 	 * port wakeup flags.  We do check here the same condition before
@@ -215,7 +239,7 @@
 				wakeup);
 	}
 
-	return 0;
+	return usb_phy_set_suspend(phy, 1);
 }
 
 static int ehci_msm_pm_resume(struct device *dev)
@@ -223,18 +247,20 @@
 	struct usb_hcd *hcd = dev_get_drvdata(dev);
 
 	dev_dbg(dev, "ehci-msm PM resume\n");
+
+	if (!hcd->rh_registered)
+		return 0;
+
 	ehci_prepare_ports_for_controller_resume(hcd_to_ehci(hcd));
 
-	return 0;
+	return usb_phy_set_suspend(phy, 0);
 }
-#else
-#define ehci_msm_pm_suspend	NULL
-#define ehci_msm_pm_resume	NULL
 #endif
 
 static const struct dev_pm_ops ehci_msm_dev_pm_ops = {
-	.suspend         = ehci_msm_pm_suspend,
-	.resume          = ehci_msm_pm_resume,
+	SET_SYSTEM_SLEEP_PM_OPS(ehci_msm_pm_suspend, ehci_msm_pm_resume)
+	SET_RUNTIME_PM_OPS(ehci_msm_runtime_suspend, ehci_msm_runtime_resume,
+				ehci_msm_runtime_idle)
 };
 
 static struct platform_driver ehci_msm_driver = {
diff --git a/drivers/usb/host/ehci-msm2.c b/drivers/usb/host/ehci-msm2.c
new file mode 100644
index 0000000..4657283
--- /dev/null
+++ b/drivers/usb/host/ehci-msm2.c
@@ -0,0 +1,1090 @@
+/* ehci-msm2.c - HSUSB Host Controller Driver Implementation
+ *
+ * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ *
+ * Partly derived from ehci-fsl.c and ehci-hcd.c
+ * Copyright (c) 2000-2004 by David Brownell
+ * Copyright (c) 2005 MontaVista Software
+ *
+ * All source code in this file is licensed under the following license except
+ * where indicated.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org
+ */
+
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/wakelock.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/usb/ulpi.h>
+#include <linux/usb/msm_hsusb_hw.h>
+#include <linux/usb/msm_hsusb.h>
+#include <mach/clk.h>
+#include <mach/msm_xo.h>
+#include <mach/msm_iomap.h>
+
+#define MSM_USB_BASE (hcd->regs)
+
+#define PDEV_NAME_LEN 20
+
+struct msm_hcd {
+	struct ehci_hcd				ehci;
+	struct device				*dev;
+	struct clk				*iface_clk;
+	struct clk				*core_clk;
+	struct clk				*alt_core_clk;
+	struct regulator			*hsusb_vddcx;
+	struct regulator			*hsusb_3p3;
+	struct regulator			*hsusb_1p8;
+	struct regulator			*vbus;
+	struct msm_xo_voter			*xo_handle;
+	bool					async_int;
+	bool					vbus_on;
+	atomic_t				in_lpm;
+	struct wake_lock			wlock;
+};
+
+static inline struct msm_hcd *hcd_to_mhcd(struct usb_hcd *hcd)
+{
+	return (struct msm_hcd *) (hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *mhcd_to_hcd(struct msm_hcd *mhcd)
+{
+	return container_of((void *) mhcd, struct usb_hcd, hcd_priv);
+}
+
+#define HSUSB_PHY_3P3_VOL_MIN		3050000 /* uV */
+#define HSUSB_PHY_3P3_VOL_MAX		3300000 /* uV */
+#define HSUSB_PHY_3P3_HPM_LOAD		50000	/* uA */
+
+#define HSUSB_PHY_1P8_VOL_MIN		1800000 /* uV */
+#define HSUSB_PHY_1P8_VOL_MAX		1800000 /* uV */
+#define HSUSB_PHY_1P8_HPM_LOAD		50000	/* uA */
+
+#define HSUSB_PHY_VDD_DIG_VOL_MIN	1045000	/* uV */
+#define HSUSB_PHY_VDD_DIG_VOL_MAX	1320000	/* uV */
+#define HSUSB_PHY_VDD_DIG_LOAD		49360	/* uA */
+
+static int msm_ehci_init_vddcx(struct msm_hcd *mhcd, int init)
+{
+	int ret = 0;
+
+	if (!init)
+		goto disable_reg;
+
+	mhcd->hsusb_vddcx = devm_regulator_get(mhcd->dev, "HSUSB_VDDCX");
+	if (IS_ERR(mhcd->hsusb_vddcx)) {
+		dev_err(mhcd->dev, "unable to get ehci vddcx\n");
+		return PTR_ERR(mhcd->hsusb_vddcx);
+	}
+
+	ret = regulator_set_voltage(mhcd->hsusb_vddcx,
+			HSUSB_PHY_VDD_DIG_VOL_MIN,
+			HSUSB_PHY_VDD_DIG_VOL_MAX);
+	if (ret) {
+		dev_err(mhcd->dev, "unable to set the voltage"
+				"for ehci vddcx\n");
+		return ret;
+	}
+
+	ret = regulator_set_optimum_mode(mhcd->hsusb_vddcx,
+				HSUSB_PHY_VDD_DIG_LOAD);
+	if (ret < 0) {
+		dev_err(mhcd->dev, "%s: Unable to set optimum mode of the"
+				" regulator: VDDCX\n", __func__);
+		goto reg_optimum_mode_err;
+	}
+
+	ret = regulator_enable(mhcd->hsusb_vddcx);
+	if (ret) {
+		dev_err(mhcd->dev, "unable to enable ehci vddcx\n");
+		goto reg_enable_err;
+	}
+
+	return 0;
+
+disable_reg:
+	regulator_disable(mhcd->hsusb_vddcx);
+reg_enable_err:
+	regulator_set_optimum_mode(mhcd->hsusb_vddcx, 0);
+reg_optimum_mode_err:
+	regulator_set_voltage(mhcd->hsusb_vddcx, 0,
+				HSUSB_PHY_VDD_DIG_VOL_MIN);
+	return ret;
+
+}
+
+static int msm_ehci_ldo_init(struct msm_hcd *mhcd, int init)
+{
+	int rc = 0;
+
+	if (!init)
+		goto put_1p8;
+
+	mhcd->hsusb_3p3 = devm_regulator_get(mhcd->dev, "HSUSB_3p3");
+	if (IS_ERR(mhcd->hsusb_3p3)) {
+		dev_err(mhcd->dev, "unable to get hsusb 3p3\n");
+		return PTR_ERR(mhcd->hsusb_3p3);
+	}
+
+	rc = regulator_set_voltage(mhcd->hsusb_3p3,
+			HSUSB_PHY_3P3_VOL_MIN, HSUSB_PHY_3P3_VOL_MAX);
+	if (rc) {
+		dev_err(mhcd->dev, "unable to set voltage level for"
+				"hsusb 3p3\n");
+		return rc;
+	}
+	mhcd->hsusb_1p8 = devm_regulator_get(mhcd->dev, "HSUSB_1p8");
+	if (IS_ERR(mhcd->hsusb_1p8)) {
+		dev_err(mhcd->dev, "unable to get hsusb 1p8\n");
+		rc = PTR_ERR(mhcd->hsusb_1p8);
+		goto put_3p3_lpm;
+	}
+	rc = regulator_set_voltage(mhcd->hsusb_1p8,
+			HSUSB_PHY_1P8_VOL_MIN, HSUSB_PHY_1P8_VOL_MAX);
+	if (rc) {
+		dev_err(mhcd->dev, "unable to set voltage level for"
+				"hsusb 1p8\n");
+		goto put_1p8;
+	}
+
+	return 0;
+
+put_1p8:
+	regulator_set_voltage(mhcd->hsusb_1p8, 0, HSUSB_PHY_1P8_VOL_MAX);
+put_3p3_lpm:
+	regulator_set_voltage(mhcd->hsusb_3p3, 0, HSUSB_PHY_3P3_VOL_MAX);
+
+	return rc;
+}
+
+#ifdef CONFIG_PM_SLEEP
+#define HSUSB_PHY_SUSP_DIG_VOL_P50  500000
+#define HSUSB_PHY_SUSP_DIG_VOL_P75  750000
+static int msm_ehci_config_vddcx(struct msm_hcd *mhcd, int high)
+{
+	struct msm_usb_host_platform_data *pdata;
+	int max_vol = HSUSB_PHY_VDD_DIG_VOL_MAX;
+	int min_vol;
+	int ret;
+
+	pdata = mhcd->dev->platform_data;
+
+	if (high)
+		min_vol = HSUSB_PHY_VDD_DIG_VOL_MIN;
+	else if (pdata && pdata->dock_connect_irq &&
+			!irq_read_line(pdata->dock_connect_irq))
+		min_vol = HSUSB_PHY_SUSP_DIG_VOL_P75;
+	else
+		min_vol = HSUSB_PHY_SUSP_DIG_VOL_P50;
+
+	ret = regulator_set_voltage(mhcd->hsusb_vddcx, min_vol, max_vol);
+	if (ret) {
+		dev_err(mhcd->dev, "%s: unable to set the voltage of regulator"
+			" HSUSB_VDDCX\n", __func__);
+		return ret;
+	}
+
+	dev_dbg(mhcd->dev, "%s: min_vol:%d max_vol:%d\n", __func__, min_vol,
+								max_vol);
+
+	return ret;
+}
+#else
+static int msm_ehci_config_vddcx(struct msm_hcd *mhcd, int high)
+{
+	return 0;
+}
+#endif
+
+static void msm_ehci_vbus_power(struct msm_hcd *mhcd, bool on)
+{
+	int ret;
+
+	if (!mhcd->vbus) {
+		pr_err("vbus is NULL.");
+		return;
+	}
+
+	if (mhcd->vbus_on == on)
+		return;
+
+	if (on) {
+		ret = regulator_enable(mhcd->vbus);
+		if (ret) {
+			pr_err("unable to enable vbus\n");
+			return;
+		}
+		mhcd->vbus_on = true;
+	} else {
+		ret = regulator_disable(mhcd->vbus);
+		if (ret) {
+			pr_err("unable to disable vbus\n");
+			return;
+		}
+		mhcd->vbus_on = false;
+	}
+}
+
+static irqreturn_t msm_ehci_dock_connect_irq(int irq, void *data)
+{
+	const struct msm_usb_host_platform_data *pdata;
+	struct msm_hcd *mhcd = data;
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+
+	pdata = mhcd->dev->platform_data;
+
+	if (atomic_read(&mhcd->in_lpm))
+		usb_hcd_resume_root_hub(hcd);
+
+	if (irq_read_line(pdata->dock_connect_irq)) {
+		dev_dbg(mhcd->dev, "%s:Dock removed disable vbus\n", __func__);
+		msm_ehci_vbus_power(mhcd, 0);
+	} else {
+		dev_dbg(mhcd->dev, "%s:Dock connected enable vbus\n", __func__);
+		msm_ehci_vbus_power(mhcd, 1);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int msm_ehci_init_vbus(struct msm_hcd *mhcd, int init)
+{
+	int rc = 0;
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+	const struct msm_usb_host_platform_data *pdata;
+
+	pdata = mhcd->dev->platform_data;
+
+	if (!init) {
+		if (pdata && pdata->dock_connect_irq)
+			free_irq(pdata->dock_connect_irq, mhcd);
+		return rc;
+	}
+
+	mhcd->vbus = devm_regulator_get(mhcd->dev, "vbus");
+	if (IS_ERR(mhcd->vbus)) {
+		pr_err("Unable to get vbus\n");
+		return -ENODEV;
+	}
+
+	if (pdata) {
+		hcd->power_budget = pdata->power_budget;
+
+		if (pdata->dock_connect_irq) {
+			rc = request_threaded_irq(pdata->dock_connect_irq, NULL,
+					msm_ehci_dock_connect_irq,
+					IRQF_TRIGGER_FALLING |
+					IRQF_TRIGGER_RISING |
+					IRQF_ONESHOT, "msm_ehci_host", mhcd);
+			if (!rc)
+				enable_irq_wake(pdata->dock_connect_irq);
+		}
+	}
+	return rc;
+}
+
+static int msm_ehci_ldo_enable(struct msm_hcd *mhcd, int on)
+{
+	int ret = 0;
+
+	if (IS_ERR(mhcd->hsusb_1p8)) {
+		dev_err(mhcd->dev, "%s: HSUSB_1p8 is not initialized\n",
+								__func__);
+		return -ENODEV;
+	}
+
+	if (IS_ERR(mhcd->hsusb_3p3)) {
+		dev_err(mhcd->dev, "%s: HSUSB_3p3 is not initialized\n",
+								__func__);
+		return -ENODEV;
+	}
+
+	if (on) {
+		ret = regulator_set_optimum_mode(mhcd->hsusb_1p8,
+						HSUSB_PHY_1P8_HPM_LOAD);
+		if (ret < 0) {
+			dev_err(mhcd->dev, "%s: Unable to set HPM of the"
+				" regulator: HSUSB_1p8\n", __func__);
+			return ret;
+		}
+
+		ret = regulator_enable(mhcd->hsusb_1p8);
+		if (ret) {
+			dev_err(mhcd->dev, "%s: unable to enable the hsusb"
+						" 1p8\n", __func__);
+			regulator_set_optimum_mode(mhcd->hsusb_1p8, 0);
+			return ret;
+		}
+
+		ret = regulator_set_optimum_mode(mhcd->hsusb_3p3,
+						HSUSB_PHY_3P3_HPM_LOAD);
+		if (ret < 0) {
+			dev_err(mhcd->dev, "%s: Unable to set HPM of the "
+				"regulator: HSUSB_3p3\n", __func__);
+			regulator_set_optimum_mode(mhcd->hsusb_1p8, 0);
+			regulator_disable(mhcd->hsusb_1p8);
+			return ret;
+		}
+
+		ret = regulator_enable(mhcd->hsusb_3p3);
+		if (ret) {
+			dev_err(mhcd->dev, "%s: unable to enable the "
+					"hsusb 3p3\n", __func__);
+			regulator_set_optimum_mode(mhcd->hsusb_3p3, 0);
+			regulator_set_optimum_mode(mhcd->hsusb_1p8, 0);
+			regulator_disable(mhcd->hsusb_1p8);
+			return ret;
+		}
+
+	} else {
+		ret = regulator_disable(mhcd->hsusb_1p8);
+		if (ret) {
+			dev_err(mhcd->dev, "%s: unable to disable the "
+					"hsusb 1p8\n", __func__);
+			return ret;
+		}
+
+		ret = regulator_set_optimum_mode(mhcd->hsusb_1p8, 0);
+		if (ret < 0)
+			dev_err(mhcd->dev, "%s: Unable to set LPM of the "
+				"regulator: HSUSB_1p8\n", __func__);
+
+		ret = regulator_disable(mhcd->hsusb_3p3);
+		if (ret) {
+			dev_err(mhcd->dev, "%s: unable to disable the "
+					"hsusb 3p3\n", __func__);
+			return ret;
+		}
+		ret = regulator_set_optimum_mode(mhcd->hsusb_3p3, 0);
+		if (ret < 0)
+			dev_err(mhcd->dev, "%s: Unable to set LPM of the "
+					"regulator: HSUSB_3p3\n", __func__);
+	}
+
+	dev_dbg(mhcd->dev, "reg (%s)\n", on ? "HPM" : "LPM");
+
+	return ret < 0 ? ret : 0;
+}
+
+
+#define ULPI_IO_TIMEOUT_USECS	(10 * 1000)
+static int msm_ulpi_read(struct msm_hcd *mhcd, u32 reg)
+{
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+	unsigned long timeout;
+
+	/* initiate read operation */
+	writel_relaxed(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg),
+	       USB_ULPI_VIEWPORT);
+
+	/* wait for completion */
+	timeout = jiffies + usecs_to_jiffies(ULPI_IO_TIMEOUT_USECS);
+	while (readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_RUN) {
+		if (time_after(jiffies, timeout)) {
+			dev_err(mhcd->dev, "msm_ulpi_read: timeout %08x\n",
+				readl_relaxed(USB_ULPI_VIEWPORT));
+			return -ETIMEDOUT;
+		}
+		udelay(1);
+	}
+
+	return ULPI_DATA_READ(readl_relaxed(USB_ULPI_VIEWPORT));
+}
+
+
+static int msm_ulpi_write(struct msm_hcd *mhcd, u32 val, u32 reg)
+{
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+	unsigned long timeout;
+
+	/* initiate write operation */
+	writel_relaxed(ULPI_RUN | ULPI_WRITE |
+	       ULPI_ADDR(reg) | ULPI_DATA(val),
+	       USB_ULPI_VIEWPORT);
+
+	/* wait for completion */
+	timeout = jiffies + usecs_to_jiffies(ULPI_IO_TIMEOUT_USECS);
+	while (readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_RUN) {
+		if (time_after(jiffies, timeout)) {
+			dev_err(mhcd->dev, "msm_ulpi_write: timeout\n");
+			return -ETIMEDOUT;
+		}
+		udelay(1);
+	}
+
+	return 0;
+}
+
+static int msm_ehci_link_clk_reset(struct msm_hcd *mhcd, bool assert)
+{
+	int ret;
+
+	if (assert) {
+		ret = clk_reset(mhcd->alt_core_clk, CLK_RESET_ASSERT);
+		if (ret)
+			dev_err(mhcd->dev, "usb alt_core_clk assert failed\n");
+	} else {
+		ret = clk_reset(mhcd->alt_core_clk, CLK_RESET_DEASSERT);
+		if (ret)
+			dev_err(mhcd->dev, "usb alt_core_clk deassert failed\n");
+	}
+
+	return ret;
+}
+
+static int msm_ehci_phy_reset(struct msm_hcd *mhcd)
+{
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+	u32 val;
+	int ret;
+	int retries;
+
+	ret = msm_ehci_link_clk_reset(mhcd, 1);
+	if (ret)
+		return ret;
+
+	udelay(1);
+
+	ret = msm_ehci_link_clk_reset(mhcd, 0);
+	if (ret)
+		return ret;
+
+	val = readl_relaxed(USB_PORTSC) & ~PORTSC_PTS_MASK;
+	writel_relaxed(val | PORTSC_PTS_ULPI, USB_PORTSC);
+
+	for (retries = 3; retries > 0; retries--) {
+		ret = msm_ulpi_write(mhcd, ULPI_FUNC_CTRL_SUSPENDM,
+				ULPI_CLR(ULPI_FUNC_CTRL));
+		if (!ret)
+			break;
+	}
+	if (!retries)
+		return -ETIMEDOUT;
+
+	/* Wakeup the PHY with a reg-access for calibration */
+	for (retries = 3; retries > 0; retries--) {
+		ret = msm_ulpi_read(mhcd, ULPI_DEBUG);
+		if (ret != -ETIMEDOUT)
+			break;
+	}
+	if (!retries)
+		return -ETIMEDOUT;
+
+	dev_info(mhcd->dev, "phy_reset: success\n");
+
+	return 0;
+}
+
+#define LINK_RESET_TIMEOUT_USEC		(250 * 1000)
+static int msm_hsusb_reset(struct msm_hcd *mhcd)
+{
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+	unsigned long timeout;
+	int ret;
+
+	clk_prepare_enable(mhcd->alt_core_clk);
+	ret = msm_ehci_phy_reset(mhcd);
+	if (ret) {
+		dev_err(mhcd->dev, "phy_reset failed\n");
+		return ret;
+	}
+
+	writel_relaxed(USBCMD_RESET, USB_USBCMD);
+
+	timeout = jiffies + usecs_to_jiffies(LINK_RESET_TIMEOUT_USEC);
+	while (readl_relaxed(USB_USBCMD) & USBCMD_RESET) {
+		if (time_after(jiffies, timeout))
+			return -ETIMEDOUT;
+		udelay(1);
+	}
+
+	/* select ULPI phy */
+	writel_relaxed(0x80000000, USB_PORTSC);
+
+	msleep(100);
+
+	writel_relaxed(0x0, USB_AHBBURST);
+	writel_relaxed(0x08, USB_AHBMODE);
+
+	/* Ensure that RESET operation is completed before turning off clock */
+	mb();
+	clk_disable_unprepare(mhcd->alt_core_clk);
+
+	/*rising edge interrupts with Dp rise and fall enabled*/
+	msm_ulpi_write(mhcd, ULPI_INT_DP, ULPI_USB_INT_EN_RISE);
+	msm_ulpi_write(mhcd, ULPI_INT_DP, ULPI_USB_INT_EN_FALL);
+
+	/*Clear the PHY interrupts by reading the PHY interrupt latch register*/
+	msm_ulpi_read(mhcd, ULPI_USB_INT_LATCH);
+
+	return 0;
+}
+
+#define PHY_SUSPEND_TIMEOUT_USEC	(500 * 1000)
+#define PHY_RESUME_TIMEOUT_USEC		(100 * 1000)
+
+#ifdef CONFIG_PM_SLEEP
+static int msm_ehci_suspend(struct msm_hcd *mhcd)
+{
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+	unsigned long timeout;
+	int ret;
+	u32 portsc;
+
+	if (atomic_read(&mhcd->in_lpm)) {
+		dev_dbg(mhcd->dev, "%s called in lpm\n", __func__);
+		return 0;
+	}
+
+	disable_irq(hcd->irq);
+
+	/* Set the PHCD bit, only if it is not set by the controller.
+	 * PHY may take some time or even fail to enter into low power
+	 * mode (LPM). Hence poll for 500 msec and reset the PHY and link
+	 * in failure case.
+	 */
+	portsc = readl_relaxed(USB_PORTSC);
+	if (!(portsc & PORTSC_PHCD)) {
+		writel_relaxed(portsc | PORTSC_PHCD,
+				USB_PORTSC);
+
+		timeout = jiffies + usecs_to_jiffies(PHY_SUSPEND_TIMEOUT_USEC);
+		while (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD)) {
+			if (time_after(jiffies, timeout)) {
+				dev_err(mhcd->dev, "Unable to suspend PHY\n");
+				msm_hsusb_reset(mhcd);
+				break;
+			}
+			udelay(1);
+		}
+	}
+
+	/*
+	 * PHY has capability to generate interrupt asynchronously in low
+	 * power mode (LPM). This interrupt is level triggered. So USB IRQ
+	 * line must be disabled till async interrupt enable bit is cleared
+	 * in USBCMD register. Assert STP (ULPI interface STOP signal) to
+	 * block data communication from PHY.
+	 */
+	writel_relaxed(readl_relaxed(USB_USBCMD) | ASYNC_INTR_CTRL |
+				ULPI_STP_CTRL, USB_USBCMD);
+
+	/*
+	 * Ensure that hardware is put in low power mode before
+	 * clocks are turned OFF and VDD is allowed to minimize.
+	 */
+	mb();
+
+	clk_disable_unprepare(mhcd->iface_clk);
+	clk_disable_unprepare(mhcd->core_clk);
+
+	/* usb phy does not require TCXO clock, hence vote for TCXO disable */
+	ret = msm_xo_mode_vote(mhcd->xo_handle, MSM_XO_MODE_OFF);
+	if (ret)
+		dev_err(mhcd->dev, "%s failed to devote for "
+			"TCXO D0 buffer%d\n", __func__, ret);
+
+	msm_ehci_config_vddcx(mhcd, 0);
+
+	atomic_set(&mhcd->in_lpm, 1);
+	enable_irq(hcd->irq);
+	wake_unlock(&mhcd->wlock);
+
+	dev_info(mhcd->dev, "EHCI USB in low power mode\n");
+
+	return 0;
+}
+
+static int msm_ehci_resume(struct msm_hcd *mhcd)
+{
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+	unsigned long timeout;
+	unsigned temp;
+	int ret;
+
+	if (!atomic_read(&mhcd->in_lpm)) {
+		dev_dbg(mhcd->dev, "%s called in !in_lpm\n", __func__);
+		return 0;
+	}
+
+	wake_lock(&mhcd->wlock);
+
+	/* Vote for TCXO when waking up the phy */
+	ret = msm_xo_mode_vote(mhcd->xo_handle, MSM_XO_MODE_ON);
+	if (ret)
+		dev_err(mhcd->dev, "%s failed to vote for "
+			"TCXO D0 buffer%d\n", __func__, ret);
+
+	clk_prepare_enable(mhcd->core_clk);
+	clk_prepare_enable(mhcd->iface_clk);
+
+	msm_ehci_config_vddcx(mhcd, 1);
+
+	temp = readl_relaxed(USB_USBCMD);
+	temp &= ~ASYNC_INTR_CTRL;
+	temp &= ~ULPI_STP_CTRL;
+	writel_relaxed(temp, USB_USBCMD);
+
+	if (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD))
+		goto skip_phy_resume;
+
+	temp = readl_relaxed(USB_PORTSC) & ~PORTSC_PHCD;
+	writel_relaxed(temp, USB_PORTSC);
+
+	timeout = jiffies + usecs_to_jiffies(PHY_RESUME_TIMEOUT_USEC);
+	while ((readl_relaxed(USB_PORTSC) & PORTSC_PHCD) ||
+			!(readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_SYNC_STATE)) {
+		if (time_after(jiffies, timeout)) {
+			/*This is a fatal error. Reset the link and PHY*/
+			dev_err(mhcd->dev, "Unable to resume USB. Resetting the h/w\n");
+			msm_hsusb_reset(mhcd);
+			break;
+		}
+		udelay(1);
+	}
+
+skip_phy_resume:
+
+	atomic_set(&mhcd->in_lpm, 0);
+
+	if (mhcd->async_int) {
+		mhcd->async_int = false;
+		pm_runtime_put_noidle(mhcd->dev);
+		enable_irq(hcd->irq);
+	}
+
+	dev_info(mhcd->dev, "EHCI USB exited from low power mode\n");
+
+	return 0;
+}
+#endif
+
+static irqreturn_t msm_ehci_irq(struct usb_hcd *hcd)
+{
+	struct msm_hcd *mhcd = hcd_to_mhcd(hcd);
+
+	if (atomic_read(&mhcd->in_lpm)) {
+		disable_irq_nosync(hcd->irq);
+		mhcd->async_int = true;
+		pm_runtime_get(mhcd->dev);
+		return IRQ_HANDLED;
+	}
+
+	return ehci_irq(hcd);
+}
+
+static int msm_ehci_reset(struct usb_hcd *hcd)
+{
+	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+	int retval;
+
+	ehci->caps = USB_CAPLENGTH;
+	ehci->regs = USB_CAPLENGTH +
+		HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
+	dbg_hcs_params(ehci, "reset");
+	dbg_hcc_params(ehci, "reset");
+
+	/* cache the data to minimize the chip reads*/
+	ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
+
+	hcd->has_tt = 1;
+	ehci->sbrn = HCD_USB2;
+
+	retval = ehci_halt(ehci);
+	if (retval)
+		return retval;
+
+	/* data structure init */
+	retval = ehci_init(hcd);
+	if (retval)
+		return retval;
+
+	retval = ehci_reset(ehci);
+	if (retval)
+		return retval;
+
+	/* bursts of unspecified length. */
+	writel_relaxed(0, USB_AHBBURST);
+	/* Use the AHB transactor */
+	writel_relaxed(0x08, USB_AHBMODE);
+	/* Disable streaming mode and select host mode */
+	writel_relaxed(0x13, USB_USBMODE);
+
+	ehci_port_power(ehci, 1);
+	return 0;
+}
+
+static struct hc_driver msm_hc2_driver = {
+	.description		= hcd_name,
+	.product_desc		= "Qualcomm EHCI Host Controller",
+	.hcd_priv_size		= sizeof(struct msm_hcd),
+
+	/*
+	 * generic hardware linkage
+	 */
+	.irq			= msm_ehci_irq,
+	.flags			= HCD_USB2 | HCD_MEMORY,
+
+	.reset			= msm_ehci_reset,
+	.start			= ehci_run,
+
+	.stop			= ehci_stop,
+	.shutdown		= ehci_shutdown,
+
+	/*
+	 * managing i/o requests and associated device resources
+	 */
+	.urb_enqueue		= ehci_urb_enqueue,
+	.urb_dequeue		= ehci_urb_dequeue,
+	.endpoint_disable	= ehci_endpoint_disable,
+	.endpoint_reset		= ehci_endpoint_reset,
+	.clear_tt_buffer_complete	 = ehci_clear_tt_buffer_complete,
+
+	/*
+	 * scheduling support
+	 */
+	.get_frame_number	= ehci_get_frame,
+
+	/*
+	 * root hub support
+	 */
+	.hub_status_data	= ehci_hub_status_data,
+	.hub_control		= ehci_hub_control,
+	.relinquish_port	= ehci_relinquish_port,
+	.port_handed_over	= ehci_port_handed_over,
+
+	/*
+	 * PM support
+	 */
+	.bus_suspend		= ehci_bus_suspend,
+	.bus_resume		= ehci_bus_resume,
+};
+
+static int msm_ehci_init_clocks(struct msm_hcd *mhcd, u32 init)
+{
+	int ret = 0;
+
+	if (!init)
+		goto put_clocks;
+
+	/* 60MHz alt_core_clk is for LINK to be used during PHY RESET  */
+	mhcd->alt_core_clk = clk_get(mhcd->dev, "alt_core_clk");
+	if (IS_ERR(mhcd->alt_core_clk)) {
+		dev_err(mhcd->dev, "failed to get alt_core_clk\n");
+		ret = PTR_ERR(mhcd->alt_core_clk);
+		return ret;
+	}
+	clk_set_rate(mhcd->alt_core_clk, 60000000);
+
+	/* iface_clk is required for data transfers */
+	mhcd->iface_clk = clk_get(mhcd->dev, "iface_clk");
+	if (IS_ERR(mhcd->iface_clk)) {
+		dev_err(mhcd->dev, "failed to get iface_clk\n");
+		ret = PTR_ERR(mhcd->iface_clk);
+		goto put_alt_core_clk;
+	}
+
+	/* Link's protocol engine is based on pclk which must
+	 * be running >55Mhz and frequency should also not change.
+	 * Hence, vote for maximum clk frequency on its source
+	 */
+	mhcd->core_clk = clk_get(mhcd->dev, "core_clk");
+	if (IS_ERR(mhcd->core_clk)) {
+		dev_err(mhcd->dev, "failed to get core_clk\n");
+		ret = PTR_ERR(mhcd->core_clk);
+		goto put_iface_clk;
+	}
+	clk_set_rate(mhcd->core_clk, INT_MAX);
+
+	clk_prepare_enable(mhcd->core_clk);
+	clk_prepare_enable(mhcd->iface_clk);
+
+	return 0;
+
+put_clocks:
+	clk_disable_unprepare(mhcd->iface_clk);
+	clk_disable_unprepare(mhcd->core_clk);
+	clk_put(mhcd->core_clk);
+put_iface_clk:
+	clk_put(mhcd->iface_clk);
+put_alt_core_clk:
+	clk_put(mhcd->alt_core_clk);
+
+	return ret;
+}
+
+static int __devinit ehci_msm2_probe(struct platform_device *pdev)
+{
+	struct usb_hcd *hcd;
+	struct resource *res;
+	struct msm_hcd *mhcd;
+	const struct msm_usb_host_platform_data *pdata;
+	char pdev_name[PDEV_NAME_LEN];
+	int ret;
+
+	dev_dbg(&pdev->dev, "ehci_msm2 probe\n");
+
+	hcd = usb_create_hcd(&msm_hc2_driver, &pdev->dev,
+				dev_name(&pdev->dev));
+	if (!hcd) {
+		dev_err(&pdev->dev, "Unable to create HCD\n");
+		return  -ENOMEM;
+	}
+
+	hcd->irq = platform_get_irq(pdev, 0);
+	if (hcd->irq < 0) {
+		dev_err(&pdev->dev, "Unable to get IRQ resource\n");
+		ret = hcd->irq;
+		goto put_hcd;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "Unable to get memory resource\n");
+		ret = -ENODEV;
+		goto put_hcd;
+	}
+
+	hcd->rsrc_start = res->start;
+	hcd->rsrc_len = resource_size(res);
+	hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+	if (!hcd->regs) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		ret = -ENOMEM;
+		goto put_hcd;
+	}
+
+	mhcd = hcd_to_mhcd(hcd);
+	mhcd->dev = &pdev->dev;
+
+	snprintf(pdev_name, PDEV_NAME_LEN, "%s.%d", pdev->name, pdev->id);
+	mhcd->xo_handle = msm_xo_get(MSM_XO_TCXO_D0, pdev_name);
+	if (IS_ERR(mhcd->xo_handle)) {
+		dev_err(&pdev->dev, "%s not able to get the handle "
+			"to vote for TCXO D0 buffer\n", __func__);
+		ret = PTR_ERR(mhcd->xo_handle);
+		goto unmap;
+	}
+
+	ret = msm_xo_mode_vote(mhcd->xo_handle, MSM_XO_MODE_ON);
+	if (ret) {
+		dev_err(&pdev->dev, "%s failed to vote for TCXO "
+			"D0 buffer%d\n", __func__, ret);
+		goto free_xo_handle;
+	}
+
+	ret = msm_ehci_init_clocks(mhcd, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to initialize clocks\n");
+		ret = -ENODEV;
+		goto devote_xo_handle;
+	}
+
+	ret = msm_ehci_init_vddcx(mhcd, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to initialize VDDCX\n");
+		ret = -ENODEV;
+		goto deinit_clocks;
+	}
+
+	ret = msm_ehci_config_vddcx(mhcd, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "hsusb vddcx configuration failed\n");
+		goto deinit_vddcx;
+	}
+
+	ret = msm_ehci_ldo_init(mhcd, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "hsusb vreg configuration failed\n");
+		goto deinit_vddcx;
+	}
+
+	ret = msm_ehci_ldo_enable(mhcd, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "hsusb vreg enable failed\n");
+		goto deinit_ldo;
+	}
+
+	ret = msm_ehci_init_vbus(mhcd, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to get vbus\n");
+		goto disable_ldo;
+	}
+
+	ret = msm_hsusb_reset(mhcd);
+	if (ret) {
+		dev_err(&pdev->dev, "hsusb PHY initialization failed\n");
+		goto vbus_deinit;
+	}
+
+	ret = usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to register HCD\n");
+		goto vbus_deinit;
+	}
+
+	pdata = mhcd->dev->platform_data;
+	if (pdata && (!pdata->dock_connect_irq ||
+				!irq_read_line(pdata->dock_connect_irq)))
+		msm_ehci_vbus_power(mhcd, 1);
+
+	device_init_wakeup(&pdev->dev, 1);
+	wake_lock_init(&mhcd->wlock, WAKE_LOCK_SUSPEND, dev_name(&pdev->dev));
+	wake_lock(&mhcd->wlock);
+	/*
+	 * This pdev->dev is assigned parent of root-hub by USB core,
+	 * hence, runtime framework automatically calls this driver's
+	 * runtime APIs based on root-hub's state.
+	 */
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+
+	return 0;
+
+vbus_deinit:
+	msm_ehci_init_vbus(mhcd, 0);
+disable_ldo:
+	msm_ehci_ldo_enable(mhcd, 0);
+deinit_ldo:
+	msm_ehci_ldo_init(mhcd, 0);
+deinit_vddcx:
+	msm_ehci_init_vddcx(mhcd, 0);
+deinit_clocks:
+	msm_ehci_init_clocks(mhcd, 0);
+devote_xo_handle:
+	msm_xo_mode_vote(mhcd->xo_handle, MSM_XO_MODE_OFF);
+free_xo_handle:
+	msm_xo_put(mhcd->xo_handle);
+unmap:
+	iounmap(hcd->regs);
+put_hcd:
+	usb_put_hcd(hcd);
+
+	return ret;
+}
+
+static int __devexit ehci_msm2_remove(struct platform_device *pdev)
+{
+	struct usb_hcd *hcd = platform_get_drvdata(pdev);
+	struct msm_hcd *mhcd = hcd_to_mhcd(hcd);
+
+	device_init_wakeup(&pdev->dev, 0);
+	pm_runtime_disable(&pdev->dev);
+	pm_runtime_set_suspended(&pdev->dev);
+
+	usb_remove_hcd(hcd);
+
+	msm_xo_put(mhcd->xo_handle);
+	msm_ehci_vbus_power(mhcd, 0);
+	msm_ehci_init_vbus(mhcd, 0);
+	msm_ehci_ldo_enable(mhcd, 0);
+	msm_ehci_ldo_init(mhcd, 0);
+	msm_ehci_init_vddcx(mhcd, 0);
+
+	msm_ehci_init_clocks(mhcd, 0);
+	wake_lock_destroy(&mhcd->wlock);
+	iounmap(hcd->regs);
+	usb_put_hcd(hcd);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int ehci_msm2_pm_suspend(struct device *dev)
+{
+	struct usb_hcd *hcd = dev_get_drvdata(dev);
+	struct msm_hcd *mhcd = hcd_to_mhcd(hcd);
+
+	dev_dbg(dev, "ehci-msm2 PM suspend\n");
+
+	if (device_may_wakeup(dev))
+		enable_irq_wake(hcd->irq);
+
+	return msm_ehci_suspend(mhcd);
+
+}
+
+static int ehci_msm2_pm_resume(struct device *dev)
+{
+	int ret;
+	struct usb_hcd *hcd = dev_get_drvdata(dev);
+	struct msm_hcd *mhcd = hcd_to_mhcd(hcd);
+
+	dev_dbg(dev, "ehci-msm2 PM resume\n");
+
+	if (device_may_wakeup(dev))
+		disable_irq_wake(hcd->irq);
+
+	ret = msm_ehci_resume(mhcd);
+	if (ret)
+		return ret;
+
+	/* Bring the device to full powered state upon system resume */
+	pm_runtime_disable(dev);
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+static int ehci_msm2_runtime_idle(struct device *dev)
+{
+	dev_dbg(dev, "EHCI runtime idle\n");
+
+	return 0;
+}
+
+static int ehci_msm2_runtime_suspend(struct device *dev)
+{
+	struct usb_hcd *hcd = dev_get_drvdata(dev);
+	struct msm_hcd *mhcd = hcd_to_mhcd(hcd);
+
+	dev_dbg(dev, "EHCI runtime suspend\n");
+	return msm_ehci_suspend(mhcd);
+}
+
+static int ehci_msm2_runtime_resume(struct device *dev)
+{
+	struct usb_hcd *hcd = dev_get_drvdata(dev);
+	struct msm_hcd *mhcd = hcd_to_mhcd(hcd);
+
+	dev_dbg(dev, "EHCI runtime resume\n");
+	return msm_ehci_resume(mhcd);
+}
+#endif
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops ehci_msm2_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(ehci_msm2_pm_suspend, ehci_msm2_pm_resume)
+	SET_RUNTIME_PM_OPS(ehci_msm2_runtime_suspend, ehci_msm2_runtime_resume,
+				ehci_msm2_runtime_idle)
+};
+#endif
+
+static struct platform_driver ehci_msm2_driver = {
+	.probe	= ehci_msm2_probe,
+	.remove	= __devexit_p(ehci_msm2_remove),
+	.driver = {
+		.name = "msm_ehci_host",
+#ifdef CONFIG_PM
+		.pm = &ehci_msm2_dev_pm_ops,
+#endif
+	},
+};
diff --git a/drivers/usb/host/ehci-msm72k.c b/drivers/usb/host/ehci-msm72k.c
new file mode 100644
index 0000000..816e408
--- /dev/null
+++ b/drivers/usb/host/ehci-msm72k.c
@@ -0,0 +1,812 @@
+/* ehci-msm.c - HSUSB Host Controller Driver Implementation
+ *
+ * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ *
+ * Partly derived from ehci-fsl.c and ehci-hcd.c
+ * Copyright (c) 2000-2004 by David Brownell
+ * Copyright (c) 2005 MontaVista Software
+ *
+ * All source code in this file is licensed under the following license except
+ * where indicated.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org
+ */
+
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/clk.h>
+#include <linux/spinlock.h>
+
+#include <mach/board.h>
+#include <mach/rpc_hsusb.h>
+#include <mach/msm_hsusb.h>
+#include <mach/msm_hsusb_hw.h>
+#include <mach/msm_otg.h>
+#include <mach/clk.h>
+#include <linux/wakelock.h>
+#include <linux/pm_runtime.h>
+
+#include <mach/msm72k_otg.h>
+
+#define MSM_USB_BASE (hcd->regs)
+
+struct msmusb_hcd {
+	struct ehci_hcd ehci;
+	struct clk *alt_core_clk;
+	struct clk *iface_clk;
+	unsigned in_lpm;
+	struct work_struct lpm_exit_work;
+	spinlock_t lock;
+	struct wake_lock wlock;
+	unsigned int clk_enabled;
+	struct msm_usb_host_platform_data *pdata;
+	unsigned running;
+	struct usb_phy *xceiv;
+	struct work_struct otg_work;
+	unsigned flags;
+	struct msm_otg_ops otg_ops;
+};
+
+static inline struct msmusb_hcd *hcd_to_mhcd(struct usb_hcd *hcd)
+{
+	return (struct msmusb_hcd *) (hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *mhcd_to_hcd(struct msmusb_hcd *mhcd)
+{
+	return container_of((void *) mhcd, struct usb_hcd, hcd_priv);
+}
+
+static void msm_xusb_pm_qos_update(struct msmusb_hcd *mhcd, int vote)
+{
+	struct msm_usb_host_platform_data *pdata = mhcd->pdata;
+
+	/* if otg driver is available, it would take
+	 * care of voting for appropriate pclk source
+	 */
+	if (mhcd->xceiv)
+		return;
+
+	if (vote)
+		clk_prepare_enable(pdata->ebi1_clk);
+	else
+		clk_disable_unprepare(pdata->ebi1_clk);
+}
+
+static void msm_xusb_enable_clks(struct msmusb_hcd *mhcd)
+{
+	struct msm_usb_host_platform_data *pdata = mhcd->pdata;
+
+	if (mhcd->clk_enabled)
+		return;
+
+	switch (PHY_TYPE(pdata->phy_info)) {
+	case USB_PHY_INTEGRATED:
+		/* OTG driver takes care of clock management */
+		break;
+	case USB_PHY_SERIAL_PMIC:
+		clk_prepare_enable(mhcd->alt_core_clk);
+		clk_prepare_enable(mhcd->iface_clk);
+		break;
+	default:
+		pr_err("%s: undefined phy type ( %X )\n", __func__,
+						pdata->phy_info);
+		return;
+	}
+	mhcd->clk_enabled = 1;
+}
+
+static void msm_xusb_disable_clks(struct msmusb_hcd *mhcd)
+{
+	struct msm_usb_host_platform_data *pdata = mhcd->pdata;
+
+	if (!mhcd->clk_enabled)
+		return;
+
+	switch (PHY_TYPE(pdata->phy_info)) {
+	case USB_PHY_INTEGRATED:
+		/* OTG driver takes care of clock management */
+		break;
+	case USB_PHY_SERIAL_PMIC:
+		clk_disable_unprepare(mhcd->alt_core_clk);
+		clk_disable_unprepare(mhcd->iface_clk);
+		break;
+	default:
+		pr_err("%s: undefined phy type ( %X )\n", __func__,
+						pdata->phy_info);
+		return;
+	}
+	mhcd->clk_enabled = 0;
+
+}
+
+static int usb_wakeup_phy(struct usb_hcd *hcd)
+{
+	struct msmusb_hcd *mhcd = hcd_to_mhcd(hcd);
+	struct msm_usb_host_platform_data *pdata = mhcd->pdata;
+	int ret = -ENODEV;
+
+	switch (PHY_TYPE(pdata->phy_info)) {
+	case USB_PHY_INTEGRATED:
+		break;
+	case USB_PHY_SERIAL_PMIC:
+		ret = msm_fsusb_resume_phy();
+		break;
+	default:
+		pr_err("%s: undefined phy type ( %X ) \n", __func__,
+						pdata->phy_info);
+	}
+
+	return ret;
+}
+
+#ifdef CONFIG_PM
+static int usb_suspend_phy(struct usb_hcd *hcd)
+{
+	int ret = 0;
+	struct msmusb_hcd *mhcd = hcd_to_mhcd(hcd);
+	struct msm_usb_host_platform_data *pdata = mhcd->pdata;
+
+	switch (PHY_TYPE(pdata->phy_info)) {
+	case USB_PHY_INTEGRATED:
+		break;
+	case USB_PHY_SERIAL_PMIC:
+		ret = msm_fsusb_set_remote_wakeup();
+		ret = msm_fsusb_suspend_phy();
+		break;
+	default:
+		pr_err("%s: undefined phy type ( %X ) \n", __func__,
+						pdata->phy_info);
+		ret = -ENODEV;
+		break;
+	}
+
+	return ret;
+}
+
+static int usb_lpm_enter(struct usb_hcd *hcd)
+{
+	struct device *dev = container_of((void *)hcd, struct device,
+							platform_data);
+	struct msmusb_hcd *mhcd = hcd_to_mhcd(hcd);
+
+	disable_irq(hcd->irq);
+	if (mhcd->in_lpm) {
+		pr_info("%s: already in lpm. nothing to do\n", __func__);
+		enable_irq(hcd->irq);
+		return 0;
+	}
+
+	if (HC_IS_RUNNING(hcd->state)) {
+		pr_info("%s: can't enter into lpm. controller is runnning\n",
+			__func__);
+		enable_irq(hcd->irq);
+		return -1;
+	}
+
+	pr_info("%s: lpm enter procedure started\n", __func__);
+
+	mhcd->in_lpm = 1;
+
+	if (usb_suspend_phy(hcd)) {
+		mhcd->in_lpm = 0;
+		enable_irq(hcd->irq);
+		pr_info("phy suspend failed\n");
+		pr_info("%s: lpm enter procedure end\n", __func__);
+		return -1;
+	}
+
+	msm_xusb_disable_clks(mhcd);
+
+	if (mhcd->xceiv && mhcd->xceiv->set_suspend)
+		mhcd->xceiv->set_suspend(mhcd->xceiv, 1);
+
+	if (device_may_wakeup(dev))
+		enable_irq_wake(hcd->irq);
+	enable_irq(hcd->irq);
+	pr_info("%s: lpm enter procedure end\n", __func__);
+	return 0;
+}
+#endif
+
+void usb_lpm_exit_w(struct work_struct *work)
+{
+	struct msmusb_hcd *mhcd = container_of((void *) work,
+			struct msmusb_hcd, lpm_exit_work);
+
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+
+	struct device *dev = container_of((void *)hcd, struct device,
+							platform_data);
+	msm_xusb_enable_clks(mhcd);
+
+
+	if (usb_wakeup_phy(hcd)) {
+		pr_err("fatal error: cannot bring phy out of lpm\n");
+		return;
+	}
+
+	/* If resume signalling finishes before lpm exit, PCD is not set in
+	 * USBSTS register. Drive resume signal to the downstream device now
+	 * so that EHCI can process the upcoming port change interrupt.*/
+
+	writel(readl(USB_PORTSC) | PORTSC_FPR, USB_PORTSC);
+
+	if (mhcd->xceiv && mhcd->xceiv->set_suspend)
+		mhcd->xceiv->set_suspend(mhcd->xceiv, 0);
+
+	if (device_may_wakeup(dev))
+		disable_irq_wake(hcd->irq);
+	enable_irq(hcd->irq);
+}
+
+static void usb_lpm_exit(struct usb_hcd *hcd)
+{
+	unsigned long flags;
+	struct msmusb_hcd *mhcd = hcd_to_mhcd(hcd);
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+	if (!mhcd->in_lpm) {
+		spin_unlock_irqrestore(&mhcd->lock, flags);
+		return;
+	}
+	mhcd->in_lpm = 0;
+	disable_irq_nosync(hcd->irq);
+	schedule_work(&mhcd->lpm_exit_work);
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+}
+
+static irqreturn_t ehci_msm_irq(struct usb_hcd *hcd)
+{
+	struct msmusb_hcd *mhcd = hcd_to_mhcd(hcd);
+	struct msm_otg *otg = container_of(mhcd->xceiv, struct msm_otg, phy);
+
+	/*
+	 * OTG scheduled a work to get Integrated PHY out of LPM,
+	 * WAIT till then */
+	if (PHY_TYPE(mhcd->pdata->phy_info) == USB_PHY_INTEGRATED)
+		if (atomic_read(&otg->in_lpm))
+			return IRQ_HANDLED;
+
+	return ehci_irq(hcd);
+}
+
+#ifdef CONFIG_PM
+
+static int ehci_msm_bus_suspend(struct usb_hcd *hcd)
+{
+	int ret;
+	struct msmusb_hcd *mhcd = hcd_to_mhcd(hcd);
+	struct device *dev = hcd->self.controller;
+
+	ret = ehci_bus_suspend(hcd);
+	if (ret) {
+		pr_err("ehci_bus suspend faield\n");
+		return ret;
+	}
+	if (PHY_TYPE(mhcd->pdata->phy_info) == USB_PHY_INTEGRATED)
+		ret = usb_phy_set_suspend(mhcd->xceiv, 1);
+	else
+		ret = usb_lpm_enter(hcd);
+
+	pm_runtime_put_noidle(dev);
+	pm_runtime_suspend(dev);
+	wake_unlock(&mhcd->wlock);
+	return ret;
+}
+
+static int ehci_msm_bus_resume(struct usb_hcd *hcd)
+{
+	struct msmusb_hcd *mhcd = hcd_to_mhcd(hcd);
+	struct device *dev = hcd->self.controller;
+
+	wake_lock(&mhcd->wlock);
+	pm_runtime_get_noresume(dev);
+	pm_runtime_resume(dev);
+
+	if (PHY_TYPE(mhcd->pdata->phy_info) == USB_PHY_INTEGRATED) {
+		usb_phy_set_suspend(mhcd->xceiv, 0);
+	} else { /* PMIC serial phy */
+		usb_lpm_exit(hcd);
+		if (cancel_work_sync(&(mhcd->lpm_exit_work)))
+			usb_lpm_exit_w(&mhcd->lpm_exit_work);
+	}
+
+	return ehci_bus_resume(hcd);
+
+}
+
+#else
+
+#define ehci_msm_bus_suspend NULL
+#define ehci_msm_bus_resume NULL
+
+#endif	/* CONFIG_PM */
+
+static int ehci_msm_reset(struct usb_hcd *hcd)
+{
+	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+	int retval;
+
+	ehci->caps = USB_CAPLENGTH;
+	ehci->regs = USB_CAPLENGTH +
+		HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
+
+	/* cache the data to minimize the chip reads*/
+	ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
+
+	retval = ehci_init(hcd);
+	if (retval)
+		return retval;
+
+	hcd->has_tt = 1;
+	ehci->sbrn = HCD_USB2;
+
+	retval = ehci_reset(ehci);
+
+	/* SW workaround for USB stability issues*/
+	writel(0x0, USB_AHB_MODE);
+	writel(0x0, USB_AHB_BURST);
+
+	return retval;
+}
+
+#define PTS_VAL(x) (PHY_TYPE(x) == USB_PHY_SERIAL_PMIC) ? PORTSC_PTS_SERIAL : \
+							PORTSC_PTS_ULPI
+
+static int ehci_msm_run(struct usb_hcd *hcd)
+{
+	struct ehci_hcd *ehci  = hcd_to_ehci(hcd);
+	struct msmusb_hcd *mhcd = hcd_to_mhcd(hcd);
+	int             retval = 0;
+	int     	port   = HCS_N_PORTS(ehci->hcs_params);
+	u32 __iomem     *reg_ptr;
+	u32             hcc_params;
+	struct msm_usb_host_platform_data *pdata = mhcd->pdata;
+
+	hcd->uses_new_polling = 1;
+	set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+
+	/* set hostmode */
+	reg_ptr = (u32 __iomem *)(((u8 __iomem *)ehci->regs) + USBMODE);
+	ehci_writel(ehci, (USBMODE_VBUS | USBMODE_SDIS), reg_ptr);
+
+	/* port configuration - phy, port speed, port power, port enable */
+	while (port--)
+		ehci_writel(ehci, (PTS_VAL(pdata->phy_info) | PORT_POWER |
+				PORT_PE), &ehci->regs->port_status[port]);
+
+	ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list);
+	ehci_writel(ehci, (u32)ehci->async->qh_dma, &ehci->regs->async_next);
+
+	hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params);
+	if (HCC_64BIT_ADDR(hcc_params))
+		ehci_writel(ehci, 0, &ehci->regs->segment);
+
+	ehci->command &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET);
+	ehci->command |= CMD_RUN;
+	ehci_writel(ehci, ehci->command, &ehci->regs->command);
+	ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
+
+	hcd->state = HC_STATE_RUNNING;
+
+	/*Enable appropriate Interrupts*/
+	ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable);
+
+	return retval;
+}
+
+static struct hc_driver msm_hc_driver = {
+	.description		= hcd_name,
+	.product_desc 		= "Qualcomm On-Chip EHCI Host Controller",
+	.hcd_priv_size 		= sizeof(struct msmusb_hcd),
+
+	/*
+	 * generic hardware linkage
+	 */
+	.irq 			= ehci_msm_irq,
+	.flags 			= HCD_USB2,
+
+	.reset 			= ehci_msm_reset,
+	.start 			= ehci_msm_run,
+
+	.stop			= ehci_stop,
+	.shutdown		= ehci_shutdown,
+
+	/*
+	 * managing i/o requests and associated device resources
+	 */
+	.urb_enqueue		= ehci_urb_enqueue,
+	.urb_dequeue		= ehci_urb_dequeue,
+	.endpoint_disable	= ehci_endpoint_disable,
+
+	/*
+	 * scheduling support
+	 */
+	.get_frame_number	= ehci_get_frame,
+
+	/*
+	 * root hub support
+	 */
+	.hub_status_data	= ehci_hub_status_data,
+	.hub_control		= ehci_hub_control,
+	.bus_suspend		= ehci_msm_bus_suspend,
+	.bus_resume		= ehci_msm_bus_resume,
+	.relinquish_port	= ehci_relinquish_port,
+
+	.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
+};
+
+static void msm_hsusb_request_host(void *handle, int request)
+{
+	struct msmusb_hcd *mhcd = handle;
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+	struct msm_usb_host_platform_data *pdata = mhcd->pdata;
+	struct msm_otg *otg = container_of(mhcd->xceiv, struct msm_otg, phy);
+#ifdef CONFIG_USB_OTG
+	struct usb_device *udev = hcd->self.root_hub;
+#endif
+	struct device *dev = hcd->self.controller;
+
+	switch (request) {
+#ifdef CONFIG_USB_OTG
+	case REQUEST_HNP_SUSPEND:
+		/* disable Root hub auto suspend. As hardware is configured
+		 * for peripheral mode, mark hardware is not available.
+		 */
+		if (PHY_TYPE(pdata->phy_info) == USB_PHY_INTEGRATED) {
+			pm_runtime_disable(&udev->dev);
+			/* Mark root hub as disconnected. This would
+			 * protect suspend/resume via sysfs.
+			 */
+			udev->state = USB_STATE_NOTATTACHED;
+			clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+			hcd->state = HC_STATE_HALT;
+			pm_runtime_put_noidle(dev);
+			pm_runtime_suspend(dev);
+		}
+		break;
+	case REQUEST_HNP_RESUME:
+		if (PHY_TYPE(pdata->phy_info) == USB_PHY_INTEGRATED) {
+			pm_runtime_get_noresume(dev);
+			pm_runtime_resume(dev);
+			disable_irq(hcd->irq);
+			ehci_msm_reset(hcd);
+			ehci_msm_run(hcd);
+			set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+			pm_runtime_enable(&udev->dev);
+			udev->state = USB_STATE_CONFIGURED;
+			enable_irq(hcd->irq);
+		}
+		break;
+#endif
+	case REQUEST_RESUME:
+		usb_hcd_resume_root_hub(hcd);
+		break;
+	case REQUEST_START:
+		if (mhcd->running)
+			break;
+		pm_runtime_get_noresume(dev);
+		pm_runtime_resume(dev);
+		wake_lock(&mhcd->wlock);
+		msm_xusb_pm_qos_update(mhcd, 1);
+		msm_xusb_enable_clks(mhcd);
+		if (PHY_TYPE(pdata->phy_info) == USB_PHY_INTEGRATED)
+			if (otg->set_clk)
+				otg->set_clk(mhcd->xceiv, 1);
+		if (pdata->vbus_power)
+			pdata->vbus_power(pdata->phy_info, 1);
+		if (pdata->config_gpio)
+			pdata->config_gpio(1);
+		usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
+		mhcd->running = 1;
+		if (PHY_TYPE(pdata->phy_info) == USB_PHY_INTEGRATED)
+			if (otg->set_clk)
+				otg->set_clk(mhcd->xceiv, 0);
+		break;
+	case REQUEST_STOP:
+		if (!mhcd->running)
+			break;
+		mhcd->running = 0;
+		/* come out of lpm before deregistration */
+		if (PHY_TYPE(pdata->phy_info) == USB_PHY_SERIAL_PMIC) {
+			usb_lpm_exit(hcd);
+			if (cancel_work_sync(&(mhcd->lpm_exit_work)))
+				usb_lpm_exit_w(&mhcd->lpm_exit_work);
+		}
+		usb_remove_hcd(hcd);
+		if (pdata->config_gpio)
+			pdata->config_gpio(0);
+		if (pdata->vbus_power)
+			pdata->vbus_power(pdata->phy_info, 0);
+		msm_xusb_disable_clks(mhcd);
+		wake_lock_timeout(&mhcd->wlock, HZ/2);
+		msm_xusb_pm_qos_update(mhcd, 0);
+		pm_runtime_put_noidle(dev);
+		pm_runtime_suspend(dev);
+		break;
+	}
+}
+
+static void msm_hsusb_otg_work(struct work_struct *work)
+{
+	struct msmusb_hcd *mhcd;
+
+	mhcd = container_of(work, struct msmusb_hcd, otg_work);
+	msm_hsusb_request_host((void *)mhcd, mhcd->flags);
+}
+static void msm_hsusb_start_host(struct usb_bus *bus, int start)
+{
+	struct usb_hcd *hcd = bus_to_hcd(bus);
+	struct msmusb_hcd *mhcd = hcd_to_mhcd(hcd);
+
+	mhcd->flags = start;
+	if (in_interrupt())
+		schedule_work(&mhcd->otg_work);
+	else
+		msm_hsusb_request_host((void *)mhcd, mhcd->flags);
+
+}
+
+static int msm_xusb_init_phy(struct msmusb_hcd *mhcd)
+{
+	int ret = -ENODEV;
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+	struct msm_usb_host_platform_data *pdata = mhcd->pdata;
+
+	switch (PHY_TYPE(pdata->phy_info)) {
+	case USB_PHY_INTEGRATED:
+		ret = 0;
+	case USB_PHY_SERIAL_PMIC:
+		msm_xusb_enable_clks(mhcd);
+		writel(0, USB_USBINTR);
+		ret = msm_fsusb_rpc_init(&mhcd->otg_ops);
+		if (!ret)
+			msm_fsusb_init_phy();
+		msm_xusb_disable_clks(mhcd);
+		break;
+	default:
+		pr_err("%s: undefined phy type ( %X ) \n", __func__,
+						pdata->phy_info);
+	}
+
+	return ret;
+}
+
+static int msm_xusb_rpc_close(struct msmusb_hcd *mhcd)
+{
+	int retval = -ENODEV;
+	struct msm_usb_host_platform_data *pdata = mhcd->pdata;
+
+	switch (PHY_TYPE(pdata->phy_info)) {
+	case USB_PHY_INTEGRATED:
+		if (!mhcd->xceiv)
+			retval = msm_hsusb_rpc_close();
+		break;
+	case USB_PHY_SERIAL_PMIC:
+		retval = msm_fsusb_reset_phy();
+		msm_fsusb_rpc_deinit();
+		break;
+	default:
+		pr_err("%s: undefined phy type ( %X ) \n", __func__,
+						pdata->phy_info);
+	}
+	return retval;
+}
+
+static int msm_xusb_init_host(struct platform_device *pdev,
+			      struct msmusb_hcd *mhcd)
+{
+	int ret = 0;
+	struct msm_otg *otg;
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+	struct msm_usb_host_platform_data *pdata = mhcd->pdata;
+
+	switch (PHY_TYPE(pdata->phy_info)) {
+	case USB_PHY_INTEGRATED:
+		msm_hsusb_rpc_connect();
+
+		if (pdata->vbus_init)
+			pdata->vbus_init(1);
+
+		/* VBUS might be present. Turn off vbus */
+		if (pdata->vbus_power)
+			pdata->vbus_power(pdata->phy_info, 0);
+
+		INIT_WORK(&mhcd->otg_work, msm_hsusb_otg_work);
+		mhcd->xceiv = usb_get_transceiver();
+		if (!mhcd->xceiv)
+			return -ENODEV;
+		otg = container_of(mhcd->xceiv, struct msm_otg, phy);
+		hcd->regs = otg->regs;
+		otg->start_host = msm_hsusb_start_host;
+
+		ret = otg_set_host(mhcd->xceiv->otg, &hcd->self);
+		ehci->transceiver = mhcd->xceiv;
+		break;
+	case USB_PHY_SERIAL_PMIC:
+		hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+
+		if (!hcd->regs)
+			return -EFAULT;
+		/* get usb clocks */
+		mhcd->alt_core_clk = clk_get(&pdev->dev, "alt_core_clk");
+		if (IS_ERR(mhcd->alt_core_clk)) {
+			iounmap(hcd->regs);
+			return PTR_ERR(mhcd->alt_core_clk);
+		}
+
+		mhcd->iface_clk = clk_get(&pdev->dev, "iface_clk");
+		if (IS_ERR(mhcd->iface_clk)) {
+			iounmap(hcd->regs);
+			clk_put(mhcd->alt_core_clk);
+			return PTR_ERR(mhcd->iface_clk);
+		}
+		mhcd->otg_ops.request = msm_hsusb_request_host;
+		mhcd->otg_ops.handle = (void *) mhcd;
+		ret = msm_xusb_init_phy(mhcd);
+		if (ret < 0) {
+			iounmap(hcd->regs);
+			clk_put(mhcd->alt_core_clk);
+			clk_put(mhcd->iface_clk);
+		}
+		break;
+	default:
+		pr_err("phy type is bad\n");
+	}
+	return ret;
+}
+
+static int __devinit ehci_msm_probe(struct platform_device *pdev)
+{
+	struct usb_hcd *hcd;
+	struct resource *res;
+	struct msm_usb_host_platform_data *pdata;
+	int retval;
+	struct msmusb_hcd *mhcd;
+
+	hcd = usb_create_hcd(&msm_hc_driver, &pdev->dev, dev_name(&pdev->dev));
+	if (!hcd)
+		return  -ENOMEM;
+
+	hcd->irq = platform_get_irq(pdev, 0);
+	if (hcd->irq < 0) {
+		usb_put_hcd(hcd);
+		return hcd->irq;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		usb_put_hcd(hcd);
+		return -ENODEV;
+	}
+
+	hcd->rsrc_start = res->start;
+	hcd->rsrc_len = resource_size(res);
+
+	mhcd = hcd_to_mhcd(hcd);
+	spin_lock_init(&mhcd->lock);
+	mhcd->in_lpm = 0;
+	mhcd->running = 0;
+	device_init_wakeup(&pdev->dev, 1);
+
+	pdata = pdev->dev.platform_data;
+	if (PHY_TYPE(pdata->phy_info) == USB_PHY_UNDEFINED) {
+		usb_put_hcd(hcd);
+		return -ENODEV;
+	}
+	hcd->power_budget = pdata->power_budget;
+	mhcd->pdata = pdata;
+	INIT_WORK(&mhcd->lpm_exit_work, usb_lpm_exit_w);
+
+	wake_lock_init(&mhcd->wlock, WAKE_LOCK_SUSPEND, dev_name(&pdev->dev));
+	pdata->ebi1_clk = clk_get(&pdev->dev, "core_clk");
+	if (IS_ERR(pdata->ebi1_clk))
+		pdata->ebi1_clk = NULL;
+	else
+		clk_set_rate(pdata->ebi1_clk, INT_MAX);
+
+	retval = msm_xusb_init_host(pdev, mhcd);
+
+	if (retval < 0) {
+		wake_lock_destroy(&mhcd->wlock);
+		usb_put_hcd(hcd);
+		clk_put(pdata->ebi1_clk);
+	}
+
+	pm_runtime_enable(&pdev->dev);
+
+	return retval;
+}
+
+static void msm_xusb_uninit_host(struct msmusb_hcd *mhcd)
+{
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+	struct msm_usb_host_platform_data *pdata = mhcd->pdata;
+
+	switch (PHY_TYPE(pdata->phy_info)) {
+	case USB_PHY_INTEGRATED:
+		if (pdata->vbus_init)
+			pdata->vbus_init(0);
+		hcd_to_ehci(hcd)->transceiver = NULL;
+		otg_set_host(mhcd->xceiv->otg, NULL);
+		usb_put_transceiver(mhcd->xceiv);
+		cancel_work_sync(&mhcd->otg_work);
+		break;
+	case USB_PHY_SERIAL_PMIC:
+		iounmap(hcd->regs);
+		clk_put(mhcd->alt_core_clk);
+		clk_put(mhcd->iface_clk);
+		msm_fsusb_reset_phy();
+		msm_fsusb_rpc_deinit();
+		break;
+	default:
+		pr_err("phy type is bad\n");
+	}
+}
+static int __exit ehci_msm_remove(struct platform_device *pdev)
+{
+	struct usb_hcd *hcd = platform_get_drvdata(pdev);
+	struct msmusb_hcd *mhcd = hcd_to_mhcd(hcd);
+	struct msm_usb_host_platform_data *pdata;
+	int retval = 0;
+
+	pdata = pdev->dev.platform_data;
+	device_init_wakeup(&pdev->dev, 0);
+
+	msm_hsusb_request_host((void *)mhcd, REQUEST_STOP);
+	msm_xusb_uninit_host(mhcd);
+	retval = msm_xusb_rpc_close(mhcd);
+
+	wake_lock_destroy(&mhcd->wlock);
+	usb_put_hcd(hcd);
+	clk_put(pdata->ebi1_clk);
+
+	pm_runtime_disable(&pdev->dev);
+	pm_runtime_set_suspended(&pdev->dev);
+
+	return retval;
+}
+
+static int ehci_msm_runtime_suspend(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: suspending...\n");
+	return 0;
+}
+
+static int ehci_msm_runtime_resume(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: resuming...\n");
+	return 0;
+}
+
+static int ehci_msm_runtime_idle(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: idling...\n");
+	return 0;
+}
+
+static const struct dev_pm_ops ehci_msm_dev_pm_ops = {
+	.runtime_suspend = ehci_msm_runtime_suspend,
+	.runtime_resume = ehci_msm_runtime_resume,
+	.runtime_idle = ehci_msm_runtime_idle
+};
+
+static struct platform_driver ehci_msm_driver = {
+	.probe	= ehci_msm_probe,
+	.remove	= __exit_p(ehci_msm_remove),
+	.driver	= {.name = "msm_hsusb_host",
+		    .pm = &ehci_msm_dev_pm_ops, },
+};
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 36ca507..4c59eab 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -1155,6 +1155,111 @@
 }
 
 /*-------------------------------------------------------------------------*/
+/* This function creates the qtds and submits them for the
+ * SINGLE_STEP_SET_FEATURE Test.
+ * This is done in two parts: first SETUP req for GetDesc is sent then
+ * 15 seconds later, the IN stage for GetDesc starts to req data from dev
+ *
+ * is_setup : i/p arguement decides which of the two stage needs to be
+ * performed; TRUE - SETUP and FALSE - IN+STATUS
+ * Returns 0 if success
+ */
+#ifdef CONFIG_USB_EHCI_EHSET
+static int
+submit_single_step_set_feature(
+	struct usb_hcd  *hcd,
+	struct urb      *urb,
+	int 		is_setup
+) {
+	struct ehci_hcd		*ehci = hcd_to_ehci(hcd);
+	struct list_head	qtd_list;
+	struct list_head	*head ;
+
+	struct ehci_qtd		*qtd, *qtd_prev;
+	dma_addr_t		buf;
+	int			len, maxpacket;
+	u32			token;
+
+	INIT_LIST_HEAD(&qtd_list);
+	head = &qtd_list;
+
+	/*
+	 * URBs map to sequences of QTDs:  one logical transaction
+	 */
+	qtd = ehci_qtd_alloc(ehci, GFP_KERNEL);
+	if (unlikely(!qtd))
+		return -1;
+	list_add_tail(&qtd->qtd_list, head);
+	qtd->urb = urb;
+
+	token = QTD_STS_ACTIVE;
+	token |= (EHCI_TUNE_CERR << 10);
+
+	len = urb->transfer_buffer_length;
+	/* Check if the request is to perform just the SETUP stage (getDesc)
+	 * as in SINGLE_STEP_SET_FEATURE test, DATA stage (IN) happens
+	 * 15 secs after the setup
+	 */
+	if (is_setup) {
+		/* SETUP pid */
+		qtd_fill(ehci, qtd, urb->setup_dma,
+				sizeof(struct usb_ctrlrequest),
+				token | (2 /* "setup" */ << 8), 8);
+
+		submit_async(ehci, urb, &qtd_list, GFP_ATOMIC);
+		return 0; /*Return now; we shall come back after 15 seconds*/
+	}
+
+	/*---------------------------------------------------------------------
+	 * IN: data transfer stage:  buffer setup : start the IN txn phase for
+	 * the get_Desc SETUP which was sent 15seconds back
+	 */
+	token ^= QTD_TOGGLE;   /*We need to start IN with DATA-1 Pid-sequence*/
+	buf = urb->transfer_dma;
+
+	token |= (1 /* "in" */ << 8);  /*This is IN stage*/
+
+	maxpacket = max_packet(usb_maxpacket(urb->dev, urb->pipe, 0));
+
+	qtd_fill(ehci, qtd, buf, len, token, maxpacket);
+
+	/* Our IN phase shall always be a short read; so keep the queue running
+	* and let it advance to the next qtd which zero length OUT status */
+
+	qtd->hw_alt_next = EHCI_LIST_END(ehci);
+
+	/*----------------------------------------------------------------------
+	 * STATUS stage for GetDesc control request
+	 */
+	token ^= 0x0100;	/* "in" <--> "out"  */
+	token |= QTD_TOGGLE;	/* force DATA1 */
+
+	qtd_prev = qtd;
+	qtd = ehci_qtd_alloc(ehci, GFP_ATOMIC);
+	if (unlikely(!qtd))
+		goto cleanup;
+	qtd->urb = urb;
+	qtd_prev->hw_next = QTD_NEXT(ehci, qtd->qtd_dma);
+	list_add_tail(&qtd->qtd_list, head);
+
+	/* dont fill any data in such packets */
+	qtd_fill(ehci, qtd, 0, 0, token, 0);
+
+	/* by default, enable interrupt on urb completion */
+	if (likely(!(urb->transfer_flags & URB_NO_INTERRUPT)))
+		qtd->hw_token |= cpu_to_hc32(ehci, QTD_IOC);
+
+	submit_async(ehci, urb, &qtd_list, GFP_KERNEL);
+
+	return 0;
+
+cleanup:
+	qtd_list_free(ehci, urb, head);
+	return -1;
+}
+#endif
+
+/*-------------------------------------------------------------------------*/
 
 /* the async qh for the qtds being reclaimed are now unlinked from the HC */
 
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 2694ed6..c69104d 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -149,6 +149,7 @@
 	unsigned		use_dummy_qh:1;	/* AMD Frame List table quirk*/
 	unsigned		has_synopsys_hc_bug:1; /* Synopsys HC */
 	unsigned		frame_index_bug:1; /* MosChip (AKA NetMos) */
+	unsigned		susp_sof_bug:1; /*Chip Idea HC*/
 
 	/* required for usb32 quirk */
 	#define OHCI_CTRL_HCFS          (3 << 6)
@@ -748,6 +749,23 @@
 
 #endif
 
+/*
+ * Writing to dma coherent memory on ARM may be delayed via L2
+ * writing buffer, so introduce the helper which can flush L2 writing
+ * buffer into memory immediately, especially used to flush ehci
+ * descriptor to memory.
+ * */
+#ifdef	CONFIG_ARM_DMA_MEM_BUFFERABLE
+static inline void ehci_sync_mem(void)
+{
+	mb();
+}
+#else
+static inline void ehci_sync_mem(void)
+{
+}
+#endif
+
 /*-------------------------------------------------------------------------*/
 
 #ifdef CONFIG_PCI
diff --git a/drivers/usb/host/pehci/Makefile b/drivers/usb/host/pehci/Makefile
new file mode 100644
index 0000000..8c0d17f
--- /dev/null
+++ b/drivers/usb/host/pehci/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the pehci driver (if driver is inside kernel tree).
+#
+
+obj-$(CONFIG_USB_PEHCI_HCD) += hal/ host/
+
diff --git a/drivers/usb/host/pehci/hal/Makefile b/drivers/usb/host/pehci/hal/Makefile
new file mode 100644
index 0000000..91408e5
--- /dev/null
+++ b/drivers/usb/host/pehci/hal/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the pehci driver (if driver is inside kernel tree).
+#
+
+obj-$(CONFIG_USB_PEHCI_HCD) += hal_msm.o
+
diff --git a/drivers/usb/host/pehci/hal/hal_intf.h b/drivers/usb/host/pehci/hal/hal_intf.h
new file mode 100644
index 0000000..2d66e57
--- /dev/null
+++ b/drivers/usb/host/pehci/hal/hal_intf.h
@@ -0,0 +1,313 @@
+/* 
+* Copyright (C) ST-Ericsson AP Pte Ltd 2010 
+*
+* ISP1763 Linux OTG Controller driver : hal
+* 
+* 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 Street, Fifth Floor, Boston, MA  02110-1301, USA. 
+* 
+* This is a hardware abstraction layer header file.
+* 
+* Author : wired support <wired.support@stericsson.com>
+*
+*/
+
+#ifndef HAL_INTF_H
+#define HAL_INTF_H
+
+
+/* Specify package here instead of including package.h */
+/* #include "package.h" */
+#define HCD_PACKAGE
+
+#define NON_PCI
+//#define PXA300
+
+//#define MSEC_INT_BASED
+#ifdef MSEC_INT_BASED
+#define THREAD_BASED 
+#endif
+
+#ifndef DATABUS_WIDTH_16
+#define DATABUS_WIDTH_16
+#endif
+
+#ifdef	DATABUS_WIDTH_16
+/*DMA SUPPORT */
+/* #define	ENABLE_PLX_DMA */
+//#undef	ENABLE_PLX_DMA//PXA300
+#endif
+
+//#define	EDGE_INTERRUPT
+//#define 	POL_HIGH_INTERRUPT
+
+#define	DMA_BUF_SIZE	(4096 * 2)
+
+#define ISP1763_CHIPID  0x176320
+
+/* Values for id_flags filed of isp1763_driver_t */
+#define ISP1763_HC				0	/* Host Controller Driver */
+#define ISP1763_DC				1	/* Device Controller Driver */
+#define ISP1763_OTG				2	/* Otg Controller Driver */
+#define ISP1763_LAST_DEV			(ISP1763_OTG + 1)
+#define ISP1763_1ST_DEV				(ISP1763_HC)
+
+#ifdef PXA300
+#define HC_SPARAMS_REG					(0x04<<1)	/* Structural Parameters Register */
+#define HC_CPARAMS_REG					(0x08<<1)	/* Capability Parameters Register */
+
+#define HC_USBCMD_REG						(0x8C<<1)	/* USB Command Register */
+#define HC_USBSTS_REG						(0x90<<1)	/* USB Status Register */
+#define HC_INTERRUPT_REG_EHCI				(0x94<<1)	/* INterrupt Enable Register */
+#define HC_FRINDEX_REG						(0x98<<1)	/* Frame Index Register */
+
+#define HC_CONFIGFLAG_REG					(0x9C<<1)	/* Conigured Flag  Register */
+#define HC_PORTSC1_REG					(0xA0<<1)	/* Port Status Control for Port1 */
+
+/*ISO Transfer Registers */
+#define HC_ISO_PTD_DONEMAP_REG			(0xA4<<1)	/* ISO PTD Done Map Register */
+#define HC_ISO_PTD_SKIPMAP_REG			(0xA6<<1)	/* ISO PTD Skip Map Register */
+#define HC_ISO_PTD_LASTPTD_REG				(0xA8<<1)	/* ISO PTD Last PTD Register */
+
+/*INT Transfer Registers */
+#define HC_INT_PTD_DONEMAP_REG			(0xAA<<1)	/* INT PTD Done Map Register */
+#define HC_INT_PTD_SKIPMAP_REG				(0xAC<<1)	/* INT PTD Skip Map Register */
+#define HC_INT_PTD_LASTPTD_REG				(0xAE<<1)	/* INT PTD Last PTD Register  */
+
+/*ATL Transfer Registers */
+#define HC_ATL_PTD_DONEMAP_REG			(0xB0<<1)	/* ATL PTD Last PTD Register  */
+#define HC_ATL_PTD_SKIPMAP_REG				(0xB2<<1)	/* ATL PTD Last PTD Register  */
+#define HC_ATL_PTD_LASTPTD_REG				(0xB4<<1)	/* ATL PTD Last PTD Register  */
+
+/*General Purpose Registers */
+#define HC_HW_MODE_REG					(0x0C<<1)	/* H/W Mode Register  */
+#define HC_CHIP_ID_REG						(0x70<<1)	/* Chip ID Register */
+#define HC_SCRATCH_REG					(0x78<<1)	/* Scratch Register */
+#define HC_RESET_REG						(0xB8<<1)	/* HC Reset Register */
+#define HC_HWMODECTRL_REG				(0xB6<<1)
+#define HC_UNLOCK_DEVICE					(0x7C<<1)
+
+/* Interrupt Registers */
+#define HC_INTERRUPT_REG					(0xD4<<1)	/* Interrupt Register */
+#define HC_INTENABLE_REG					(0xD6<<1)	/* Interrupt enable Register */
+#define HC_ISO_IRQ_MASK_OR_REG			(0xD8<<1)	/* ISO Mask OR Register */
+#define HC_INT_IRQ_MASK_OR_REG			(0xDA<<1)	/* INT Mask OR Register */
+#define HC_ATL_IRQ_MASK_OR_REG			(0xDC<<1)	/* ATL Mask OR Register */
+#define HC_ISO_IRQ_MASK_AND_REG			(0xDE<<1)	/* ISO Mask AND Register */
+#define HC_INT_IRQ_MASK_AND_REG			(0xE0<<1)	/* INT Mask AND Register */
+#define HC_ATL_IRQ_MASK_AND_REG			(0xE2<<1)	/* ATL Mask AND Register */
+
+/*power control reg */
+#define HC_POWER_DOWN_CONTROL_REG		(0xD0<<1)
+
+/*RAM Registers */
+#define HC_DMACONFIG_REG					(0xBC<<1)	/* DMA Config Register */
+#define HC_MEM_READ_REG					(0xC4<<1)	/* Memory Register */
+#define HC_DATA_REG						(0xC6<<1)	/* Data Register */
+
+#define OTG_CTRL_SET_REG					(0xE4<<1)
+#define OTG_CTRL_CLEAR_REG					(0xE6<<1)
+#define OTG_SOURCE_REG					(0xE8<<1)
+
+#define OTG_INTR_EN_F_SET_REG				(0xF0<<1)
+#define OTG_INTR_EN_R_SET_REG				(0xF4<<1)	/* OTG Interrupt Enable Rise register */
+
+#else
+#define HC_SPARAMS_REG					0x04	/* Structural Parameters Register */
+#define HC_CPARAMS_REG					0x08	/* Capability Parameters Register */
+
+#define HC_USBCMD_REG					0x8C	/* USB Command Register */
+#define HC_USBSTS_REG					0x90	/* USB Status Register */
+#define HC_INTERRUPT_REG_EHCI			0x94	/* INterrupt Enable Register */
+#define HC_FRINDEX_REG					0x98	/* Frame Index Register */
+
+#define HC_CONFIGFLAG_REG				0x9C	/* Conigured Flag  Register */
+#define HC_PORTSC1_REG					0xA0	/* Port Status Control for Port1 */
+
+/*ISO Transfer Registers */
+#define HC_ISO_PTD_DONEMAP_REG			0xA4	/* ISO PTD Done Map Register */
+#define HC_ISO_PTD_SKIPMAP_REG			0xA6	/* ISO PTD Skip Map Register */
+#define HC_ISO_PTD_LASTPTD_REG			0xA8	/* ISO PTD Last PTD Register */
+
+/*INT Transfer Registers */
+#define HC_INT_PTD_DONEMAP_REG			0xAA	/* INT PTD Done Map Register */
+#define HC_INT_PTD_SKIPMAP_REG			0xAC	/* INT PTD Skip Map Register */
+#define HC_INT_PTD_LASTPTD_REG			0xAE	/* INT PTD Last PTD Register  */
+
+/*ATL Transfer Registers */
+#define HC_ATL_PTD_DONEMAP_REG			0xB0	/* ATL PTD Last PTD Register  */
+#define HC_ATL_PTD_SKIPMAP_REG			0xB2	/* ATL PTD Last PTD Register  */
+#define HC_ATL_PTD_LASTPTD_REG			0xB4	/* ATL PTD Last PTD Register  */
+
+/*General Purpose Registers */
+#define HC_HW_MODE_REG					0x0C //0xB6	/* H/W Mode Register  */
+#define HC_CHIP_ID_REG					0x70	/* Chip ID Register */
+#define HC_SCRATCH_REG					0x78	/* Scratch Register */
+#define HC_RESET_REG					0xB8	/* HC Reset Register */
+#define HC_HWMODECTRL_REG				0xB6 //0x0C /* H/W Mode control Register  */
+#define HC_UNLOCK_DEVICE				0x7C
+
+/* Interrupt Registers */
+#define HC_INTERRUPT_REG				0xD4	/* Interrupt Register */
+#define HC_INTENABLE_REG				0xD6	/* Interrupt enable Register */
+#define HC_ISO_IRQ_MASK_OR_REG			0xD8	/* ISO Mask OR Register */
+#define HC_INT_IRQ_MASK_OR_REG			0xDA	/* INT Mask OR Register */
+#define HC_ATL_IRQ_MASK_OR_REG			0xDC	/* ATL Mask OR Register */
+#define HC_ISO_IRQ_MASK_AND_REG			0xDE	/* ISO Mask AND Register */
+#define HC_INT_IRQ_MASK_AND_REG			0xE0	/* INT Mask AND Register */
+#define HC_ATL_IRQ_MASK_AND_REG			0xE2	/* ATL Mask AND Register */
+
+/*power control reg */
+#define HC_POWER_DOWN_CONTROL_REG		0xD0
+
+/*RAM Registers */
+#define HC_DMACONFIG_REG				0xBC	/* DMA Config Register */
+#define HC_MEM_READ_REG					0xC4	/* Memory Register */
+#define HC_DATA_REG						0xC6	/* Data Register */
+
+#define OTG_CTRL_SET_REG				0xE4
+#define OTG_CTRL_CLEAR_REG				0xE6
+#define OTG_SOURCE_REG					0xE8
+
+#define OTG_INTR_EN_F_SET_REG			0xF0	/* OTG Interrupt Enable Fall register */
+#define OTG_INTR_EN_R_SET_REG			0xF4	/* OTG Interrupt Enable Rise register */
+
+#endif
+
+#define	OTG_CTRL_DPPULLUP				0x0001
+#define	OTG_CTRL_DPPULLDOWN				0x0002
+#define	OTG_CTRL_DMPULLDOWN				0x0004
+#define	OTG_CTRL_VBUS_DRV				0x0010
+#define	OTG_CTRL_VBUS_DISCHRG			0x0020
+#define	OTG_CTRL_VBUS_CHRG				0x0040
+#define	OTG_CTRL_SW_SEL_HC_DC			0x0080
+#define	OTG_CTRL_BDIS_ACON_EN			0x0100
+#define	OTG_CTRL_OTG_SE0_EN				0x0200
+#define	OTG_CTRL_OTG_DISABLE			0x0400
+#define	OTG_CTRL_VBUS_DRV_PORT2			0x1000
+#define	OTG_CTRL_SW_SEL_HC_2			0x8000
+
+/*interrupt count and buffer status register*/
+
+
+#ifdef PXA300
+#define HC_BUFFER_STATUS_REG			(0xBA<<1)
+#define HC_INT_THRESHOLD_REG			(0xC8<<1)
+#else
+#define HC_BUFFER_STATUS_REG			0xBA
+#define HC_INT_THRESHOLD_REG			0xC8
+#endif
+
+#define HC_OTG_INTERRUPT				0x400
+
+#ifdef PXA300
+#define DC_CHIPID						(0x70<<1)
+#else
+#define DC_CHIPID						0x70
+#endif
+
+
+#ifdef PXA300
+#define FPGA_CONFIG_REG				(0x100<<1)
+#else
+#define FPGA_CONFIG_REG					0x100
+#endif
+
+#define HC_HW_MODE_GOBAL_INTR_ENABLE	0x01
+#define HC_HW_MODE_INTR_EDGE			0x02
+#define HC_HW_MODE_INTR_POLARITY_HIGH	0x04
+#define HC_HW_MODE_LOCK				0x08
+#define HC_HW_MODE_DATABUSWIDTH_8	0x10
+#define HC_HW_MODE_DREQ_POL_HIGH		0x20
+#define HC_HW_MODE_DACK_POL_HIGH		0x40
+#define HC_HW_MODE_COMN_INT			0x80
+
+struct isp1763_driver;
+typedef struct _isp1763_id {
+	u16 idVendor;
+	u16 idProduct;
+	u32 driver_info;
+} isp1763_id;
+
+typedef struct isp1763_dev {
+	/*added for pci device */
+#ifdef  NON_PCI 
+		struct platform_device *dev;
+#else /*PCI*/
+	struct pci_dev *pcidev;
+#endif
+	struct isp1763_driver *driver;	/* which driver has allocated this device */
+	void *driver_data;	/* data private to the host controller driver */
+	void *otg_driver_data;	/*data private for otg controler */
+	unsigned char index;	/* local controller (HC/DC/OTG) */
+	unsigned int irq;	/*Interrupt Channel allocated for this device */
+	void (*handler) (struct isp1763_dev * dev, void *isr_data);	/* Interrupt Serrvice Routine */
+	void *isr_data;		/* isr data of the driver */
+	unsigned long int_reg;	/* Interrupt register */
+	unsigned long alt_int_reg;	/* Interrupt register 2 */
+	unsigned long start;
+	unsigned long length;
+	struct resource *mem_res;
+	unsigned long io_base;	/* Start Io address space for this device */
+	unsigned long io_len;	/* IO address space length for this device */
+
+	unsigned long chip_id;	/* Chip Id */
+
+	char name[80];		/* device name */
+	int active;		/* device status */
+
+	/* DMA resources should come here */
+	unsigned long dma;
+	u8 *baseaddress;	/*base address for i/o ops */
+	u8 *dmabase;
+	isp1763_id *id;
+} isp1763_dev_t;
+
+
+typedef struct isp1763_driver {
+	char *name;
+	unsigned long index;	/* HC or DC or OTG */
+	isp1763_id *id;		/*device ids */
+	int (*probe) (struct isp1763_dev * dev, isp1763_id * id);	/* New device inserted */
+	void (*remove) (struct isp1763_dev * dev);	/* Device removed (NULL if not a hot-plug capable driver) */
+	
+	void (*suspend) (struct isp1763_dev * dev);	/* Device suspended */
+	void (*resume) (struct isp1763_dev * dev);	/* Device woken up */
+	void (*remotewakeup) (struct isp1763_dev *dev);  /* Remote Wakeup */
+	void (*powerup) (struct isp1763_dev *dev);  /* Device poweup mode */
+	void (*powerdown)	(struct isp1763_dev *dev); /* Device power down mode */
+} isp_1763_driver_t;
+
+struct usb_device *phci_register_otg_device(struct isp1763_dev *dev);
+
+/*otg exported function from host*/
+int phci_suspend_otg_port(struct isp1763_dev *dev, u32 command);
+int phci_enumerate_otg_port(struct isp1763_dev *dev, u32 command);
+
+extern int isp1763_register_driver(struct isp1763_driver *drv);
+extern void isp1763_unregister_driver(struct isp1763_driver *drv);
+extern int isp1763_request_irq(void (*handler)(struct isp1763_dev * dev, void *isr_data),
+		      struct isp1763_dev *dev, void *isr_data);
+extern void isp1763_free_irq(struct isp1763_dev *dev, void *isr_data);
+
+extern u32 isp1763_reg_read32(isp1763_dev_t * dev, u16 reg, u32 data);
+extern u16 isp1763_reg_read16(isp1763_dev_t * dev, u16 reg, u16 data);
+extern u8 isp1763_reg_read8(struct isp1763_dev *dev, u16 reg, u8 data);
+extern void isp1763_reg_write32(isp1763_dev_t * dev, u16 reg, u32 data);
+extern void isp1763_reg_write16(isp1763_dev_t * dev, u16 reg, u16 data);
+extern void isp1763_reg_write8(struct isp1763_dev *dev, u16 reg, u8 data);
+extern int isp1763_mem_read(isp1763_dev_t * dev, u32 start_add,
+		     u32 end_add, u32 * buffer, u32 length, u16 dir);
+extern int isp1763_mem_write(isp1763_dev_t * dev, u32 start_add,
+		      u32 end_add, u32 * buffer, u32 length, u16 dir);
+#endif /* __HAL_INTF_H__ */
diff --git a/drivers/usb/host/pehci/hal/hal_msm.c b/drivers/usb/host/pehci/hal/hal_msm.c
new file mode 100644
index 0000000..35c0203
--- /dev/null
+++ b/drivers/usb/host/pehci/hal/hal_msm.c
@@ -0,0 +1,748 @@
+/* 
+* Copyright (C) ST-Ericsson AP Pte Ltd 2010 
+*
+* ISP1763 Linux HCD Controller driver : hal
+* 
+* 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 Street, Fifth Floor, Boston, MA  02110-1301, USA. 
+* 
+* This is the main hardware abstraction layer file. Hardware initialization, interupt
+* processing and read/write routines are handled here.
+* 
+* Author : wired support <wired.support@stericsson.com>
+*
+*/
+
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/completion.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/usb.h>
+#include <linux/gpio.h>
+#include <mach/board.h>
+#include <linux/poll.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+
+
+/*--------------------------------------------------------------*
+ *               linux system include files
+ *--------------------------------------------------------------*/
+#include "hal_msm.h"
+#include "../hal/hal_intf.h"
+#include "../hal/isp1763.h"
+
+
+/*--------------------------------------------------------------*
+ *               Local variable Definitions
+ *--------------------------------------------------------------*/
+struct isp1763_dev isp1763_loc_dev[ISP1763_LAST_DEV];
+
+
+/*--------------------------------------------------------------*
+ *               Local # Definitions
+ *--------------------------------------------------------------*/
+#define         PCI_ACCESS_RETRY_COUNT  20
+#define         ISP1763_DRIVER_NAME     "isp1763_usb"
+
+/*--------------------------------------------------------------*
+ *               Local Function
+ *--------------------------------------------------------------*/
+
+static int __devexit isp1763_remove(struct platform_device *pdev);
+static int __devinit isp1763_probe(struct platform_device *pdev);
+
+
+/*--------------------------------------------------------------*
+ *               Platform Driver Interface Functions
+ *--------------------------------------------------------------*/
+
+static struct platform_driver isp1763_usb_driver = {
+	.remove = __exit_p(isp1763_remove),
+	.driver = {
+		.name = ISP1763_DRIVER_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+
+/*--------------------------------------------------------------*
+ *               ISP1763 Read write routine
+ *--------------------------------------------------------------*/
+/*
+ * EBI2 on 8660 ignores the first bit and shifts the address by
+ * one bit to the right.
+ * Hence, shift left all the register addresses before accessing
+ * them over EBI2.
+ * This logic applies only for the register read/writes, for
+ * read/write from ISP memory this conversion is not needed
+ * as the ISP obtains the memory address from 'memory' register
+ */
+
+/* Write a 32 bit Register of isp1763 */
+void
+isp1763_reg_write32(struct isp1763_dev *dev, u16 reg, u32 data)
+{
+	/* Write the 32bit to the register address given to us */
+
+	reg <<= 1;
+#ifdef DATABUS_WIDTH_16
+	writew((u16) data, dev->baseaddress + ((reg)));
+	writew((u16) (data >> 16), dev->baseaddress + (((reg + 4))));
+#else
+	writeb((u8) data, dev->baseaddress + (reg));
+	writeb((u8) (data >> 8), dev->baseaddress + ((reg + 1)));
+	writeb((u8) (data >> 16), dev->baseaddress + ((reg + 2)));
+	writeb((u8) (data >> 24), dev->baseaddress + ((reg + 3)));
+#endif
+
+}
+EXPORT_SYMBOL(isp1763_reg_write32);
+
+
+/* Read a 32 bit Register of isp1763 */
+u32
+isp1763_reg_read32(struct isp1763_dev *dev, u16 reg, u32 data)
+{
+
+#ifdef DATABUS_WIDTH_16
+	u16 wvalue1, wvalue2;
+#else
+	u8 bval1, bval2, bval3, bval4;
+#endif
+	data = 0;
+	reg <<= 1;
+#ifdef DATABUS_WIDTH_16
+	wvalue1 = readw(dev->baseaddress + ((reg)));
+	wvalue2 = readw(dev->baseaddress + (((reg + 4))));
+	data |= wvalue2;
+	data <<= 16;
+	data |= wvalue1;
+#else
+
+	bval1 = readb(dev->baseaddress + (reg));
+	bval2 = readb(dev->baseaddress + (reg + 1));
+	bval3 = readb(dev->baseaddress + (reg + 2));
+	bval4 = readb(dev->baseaddress + (reg + 3));
+	data = 0;
+	data |= bval4;
+	data <<= 8;
+	data |= bval3;
+	data <<= 8;
+	data |= bval2;
+	data <<= 8;
+	data |= bval1;
+
+#endif
+
+	return data;
+}
+EXPORT_SYMBOL(isp1763_reg_read32);
+
+
+/* Read a 16 bit Register of isp1763 */
+u16
+isp1763_reg_read16(struct isp1763_dev * dev, u16 reg, u16 data)
+{
+	reg <<= 1;
+#ifdef DATABUS_WIDTH_16
+	data = readw(dev->baseaddress + ((reg)));
+#else
+	u8 bval1, bval2;
+	bval1 = readb(dev->baseaddress + (reg));
+	if (reg == HC_DATA_REG){
+		bval2 = readb(dev->baseaddress + (reg));
+	} else {
+		bval2 = readb(dev->baseaddress + ((reg + 1)));
+	}
+	data = 0;
+	data |= bval2;
+	data <<= 8;
+	data |= bval1;
+
+#endif
+	return data;
+}
+EXPORT_SYMBOL(isp1763_reg_read16);
+
+/* Write a 16 bit Register of isp1763 */
+void
+isp1763_reg_write16(struct isp1763_dev *dev, u16 reg, u16 data)
+{
+	reg <<= 1;
+#ifdef DATABUS_WIDTH_16
+	writew(data, dev->baseaddress + ((reg)));
+#else
+	writeb((u8) data, dev->baseaddress + (reg));
+	if (reg == HC_DATA_REG){
+		writeb((u8) (data >> 8), dev->baseaddress + (reg));
+	}else{
+		writeb((u8) (data >> 8), dev->baseaddress + ((reg + 1)));
+	}
+
+#endif
+}
+EXPORT_SYMBOL(isp1763_reg_write16);
+
+/* Read a 8 bit Register of isp1763 */
+u8
+isp1763_reg_read8(struct isp1763_dev *dev, u16 reg, u8 data)
+{
+	reg <<= 1;
+	data = readb((dev->baseaddress + (reg)));
+	return data;
+}
+EXPORT_SYMBOL(isp1763_reg_read8);
+
+/* Write a 8 bit Register of isp1763 */
+void
+isp1763_reg_write8(struct isp1763_dev *dev, u16 reg, u8 data)
+{
+	reg <<= 1;
+	writeb(data, (dev->baseaddress + (reg)));
+}
+EXPORT_SYMBOL(isp1763_reg_write8);
+
+
+/*--------------------------------------------------------------*
+ *
+ * Module dtatils: isp1763_mem_read
+ *
+ * Memory read using PIO method.
+ *
+ *  Input: struct isp1763_driver *drv  -->  Driver structure.
+ *                      u32 start_add     --> Starting address of memory
+ *              u32 end_add     ---> End address
+ *
+ *              u32 * buffer      --> Buffer pointer.
+ *              u32 length       ---> Length
+ *              u16 dir          ---> Direction ( Inc or Dec)
+ *
+ *  Output     int Length  ----> Number of bytes read
+ *
+ *  Called by: system function
+ *
+ *
+ *--------------------------------------------------------------*/
+/* Memory read function PIO */
+
+int
+isp1763_mem_read(struct isp1763_dev *dev, u32 start_add,
+	u32 end_add, u32 * buffer, u32 length, u16 dir)
+{
+	u8 *one = (u8 *) buffer;
+	u16 *two = (u16 *) buffer;
+	u32 a = (u32) length;
+	u32 w;
+	u32 w2;
+
+	if (buffer == 0) {
+		printk("Buffer address zero\n");
+		return 0;
+	}
+
+
+	isp1763_reg_write16(dev, HC_MEM_READ_REG, start_add);
+	/* This delay requirement comes from the ISP1763A programming guide */
+	ndelay(100);
+last:
+	w = isp1763_reg_read16(dev, HC_DATA_REG, w);
+	w2 = isp1763_reg_read16(dev, HC_DATA_REG, w);
+	w2 <<= 16;
+	w = w | w2;
+	if (a == 1) {
+		*one = (u8) w;
+		return 0;
+	}
+	if (a == 2) {
+		*two = (u16) w;
+		return 0;
+	}
+
+	if (a == 3) {
+		*two = (u16) w;
+		two += 1;
+		w >>= 16;
+		*two = (u8) (w);
+		return 0;
+
+	}
+	while (a > 0) {
+		*buffer = w;
+		a -= 4;
+		if (a <= 0) {
+			break;
+		}
+		if (a < 4) {
+			buffer += 1;
+			one = (u8 *) buffer;
+			two = (u16 *) buffer;
+			goto last;
+		}
+		buffer += 1;
+		w = isp1763_reg_read16(dev, HC_DATA_REG, w);
+		w2 = isp1763_reg_read16(dev, HC_DATA_REG, w);
+		w2 <<= 16;
+		w = w | w2;
+	}
+	return ((a < 0) || (a == 0)) ? 0 : (-1);
+
+}
+EXPORT_SYMBOL(isp1763_mem_read);
+
+
+/*--------------------------------------------------------------*
+ *
+ * Module dtatils: isp1763_mem_write
+ *
+ * Memory write using PIO method.
+ *
+ *  Input: struct isp1763_driver *drv  -->  Driver structure.
+ *                      u32 start_add     --> Starting address of memory
+ *              u32 end_add     ---> End address
+ *
+ *              u32 * buffer      --> Buffer pointer.
+ *              u32 length       ---> Length
+ *              u16 dir          ---> Direction ( Inc or Dec)
+ *
+ *  Output     int Length  ----> Number of bytes read
+ *
+ *  Called by: system function
+ *
+ *
+ *--------------------------------------------------------------*/
+
+/* Memory read function IO */
+
+int
+isp1763_mem_write(struct isp1763_dev *dev,
+	u32 start_add, u32 end_add, u32 * buffer, u32 length, u16 dir)
+{
+	int a = length;
+	u8 one = (u8) (*buffer);
+	u16 two = (u16) (*buffer);
+
+
+	isp1763_reg_write16(dev, HC_MEM_READ_REG, start_add);
+	/* This delay requirement comes from the ISP1763A programming guide */
+	ndelay(100);
+
+	if (a == 1) {
+		isp1763_reg_write16(dev, HC_DATA_REG, one);
+		return 0;
+	}
+	if (a == 2) {
+		isp1763_reg_write16(dev, HC_DATA_REG, two);
+		return 0;
+	}
+
+	while (a > 0) {
+		isp1763_reg_write16(dev, HC_DATA_REG, (u16) (*buffer));
+		if (a >= 3)
+			isp1763_reg_write16(dev, HC_DATA_REG,
+					    (u16) ((*buffer) >> 16));
+		start_add += 4;
+		a -= 4;
+		if (a <= 0)
+			break;
+		buffer += 1;
+
+	}
+
+	return ((a < 0) || (a == 0)) ? 0 : (-1);
+
+}
+EXPORT_SYMBOL(isp1763_mem_write);
+
+
+/*--------------------------------------------------------------*
+ *
+ * Module dtatils: isp1763_register_driver
+ *
+ * This function is used by top driver (OTG, HCD, DCD) to register
+ * their communication functions (probe, remove, suspend, resume) using
+ * the drv data structure.
+ * This function will call the probe function of the driver if the ISP1763
+ * corresponding to the driver is enabled
+ *
+ *  Input: struct isp1763_driver *drv  --> Driver structure.
+ *  Output result
+ *         0= complete
+ *         1= error.
+ *
+ *  Called by: system function module_init
+ *
+ *
+ *--------------------------------------------------------------*/
+
+int
+isp1763_register_driver(struct isp1763_driver *drv)
+{
+	struct isp1763_dev *dev;
+	int result = -EINVAL;
+
+	hal_entry("%s: Entered\n", __FUNCTION__);
+	info("isp1763_register_driver(drv=%p)\n", drv);
+
+	if (!drv) {
+		return -EINVAL;
+	}
+
+	dev = &isp1763_loc_dev[drv->index];
+	if (!dev->baseaddress)
+		return -EINVAL;
+
+	dev->active = 1;	/* set the driver as active*/
+
+	if (drv->probe) {
+		result = drv->probe(dev, drv->id);
+	} else {
+		printk("%s no probe function for indes %d \n", __FUNCTION__,
+			(int)drv->index);
+	}
+
+	if (result >= 0) {
+		pr_debug(KERN_INFO __FILE__ ": Registered Driver %s\n",
+			drv->name);
+		dev->driver = drv;
+	}
+	hal_entry("%s: Exit\n", __FUNCTION__);
+	return result;
+}				/* End of isp1763_register_driver */
+EXPORT_SYMBOL(isp1763_register_driver);
+
+
+/*--------------------------------------------------------------*
+ *
+ * Module dtatils: isp1763_unregister_driver
+ *
+ * This function is used by top driver (OTG, HCD, DCD) to de-register
+ * their communication functions (probe, remove, suspend, resume) using
+ * the drv data structure.
+ * This function will check whether the driver is registered or not and
+ * call the remove function of the driver if registered
+ *
+ *  Input: struct isp1763_driver *drv  --> Driver structure.
+ *  Output result
+ *         0= complete
+ *         1= error.
+ *
+ *  Called by: system function module_init
+ *
+ *
+ *--------------------------------------------------------------*/
+
+void
+isp1763_unregister_driver(struct isp1763_driver *drv)
+{
+	struct isp1763_dev *dev;
+	hal_entry("%s: Entered\n", __FUNCTION__);
+
+	info("isp1763_unregister_driver(drv=%p)\n", drv);
+	dev = &isp1763_loc_dev[drv->index];
+	if (dev->driver == drv) {
+		/* driver registered is same as the requestig driver */
+		drv->remove(dev);
+		dev->driver = NULL;
+		info(": De-registered Driver %s\n", drv->name);
+		return;
+	}
+	hal_entry("%s: Exit\n", __FUNCTION__);
+}				/* End of isp1763_unregister_driver */
+EXPORT_SYMBOL(isp1763_unregister_driver);
+
+
+/*--------------------------------------------------------------*
+ *               ISP1763 Platform driver interface routine.
+ *--------------------------------------------------------------*/
+
+
+/*--------------------------------------------------------------*
+ *
+ *  Module dtatils: isp1763_module_init
+ *
+ *  This  is the module initialization function. It registers to
+ *  driver for a isp1763 platform device. And also resets the
+ *  internal data structures.
+ *
+ *  Input: void
+ *  Output result
+ *         0= complete
+ *         1= error.
+ *
+ *  Called by: system function module_init
+ *
+ *
+ *
+ -------------------------------------------------------------------*/
+static int __init
+isp1763_module_init(void)
+{
+	int result = 0;
+	hal_entry("%s: Entered\n", __FUNCTION__);
+	pr_debug(KERN_NOTICE "+isp1763_module_init\n");
+	memset(isp1763_loc_dev, 0, sizeof(isp1763_loc_dev));
+
+	result = platform_driver_probe(&isp1763_usb_driver, isp1763_probe);
+
+	pr_debug(KERN_NOTICE "-isp1763_module_init\n");
+	hal_entry("%s: Exit\n", __FUNCTION__);
+	return result;
+}
+
+/*--------------------------------------------------------------*
+ *
+ *  Module dtatils: isp1763_module_cleanup
+ *
+ * This  is the module cleanup function. It de-registers the
+ * Platform driver and resets the internal data structures.
+ *
+ *  Input: void
+ *  Output void
+ *
+ *  Called by: system function module_cleanup
+ *
+ *
+ *
+ --------------------------------------------------------------*/
+
+static void __exit
+isp1763_module_cleanup(void)
+{
+	pr_debug("Hal Module Cleanup\n");
+	platform_driver_unregister(&isp1763_usb_driver);
+
+	memset(isp1763_loc_dev, 0, sizeof(isp1763_loc_dev));
+}
+
+void dummy_mem_read(struct isp1763_dev *dev)
+{
+	u32 w = 0;
+	isp1763_reg_write16(dev, HC_MEM_READ_REG, 0x0400);
+	w = isp1763_reg_read16(dev, HC_DATA_REG, w);
+
+	pr_debug("dummy_read DONE: %x\n", w);
+	msleep(10);
+}
+/*--------------------------------------------------------------*
+ *
+ *  Module dtatils: isp1763_probe
+ *
+ * probe function of ISP1763
+ * This function is called from module_init if the corresponding platform
+ * device is present. This function initializes the information
+ * for the Host Controller with the assigned resources and tests the register
+ * access to the controller and do a software reset and makes it ready
+ * for the driver to play with. It also calls setup_gpio passed from pdata
+ * to setup GPIOs (e.g. used for IRQ and RST lines).
+ *
+ *  Input:
+ *              struct platform_device *dev   ----> Platform Device structure
+ *  Output void
+ *
+ *  Called by: system function module_cleanup
+ *
+ *
+ *
+ --------------------------------------------------------------**/
+
+static int __devinit
+isp1763_probe(struct platform_device *pdev)
+{
+	u32 reg_data = 0;
+	struct isp1763_dev *loc_dev;
+	int status = 1;
+	u32 hwmodectrl = 0;
+	u16 us_reset_hc = 0;
+	u32 chipid = 0;
+	struct isp1763_platform_data *pdata = pdev->dev.platform_data;
+
+	hal_entry("%s: Entered\n", __FUNCTION__);
+
+	hal_init(("isp1763_probe(dev=%p)\n", dev));
+
+	loc_dev = &(isp1763_loc_dev[ISP1763_HC]);
+	loc_dev->dev = pdev;
+
+	/* Get the Host Controller IO and INT resources */
+	loc_dev->mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!loc_dev->mem_res) {
+		pr_err("%s: failed to get platform resource mem\n", __func__);
+		return -ENODEV;
+	}
+
+	loc_dev->baseaddress = ioremap_nocache(loc_dev->mem_res->start,
+					resource_size(loc_dev->mem_res));
+	if (!loc_dev->baseaddress) {
+		pr_err("%s: ioremap failed\n", __func__);
+		status = -ENOMEM;
+		goto put_mem_res;
+	}
+	pr_info("%s: ioremap done at: %x\n", __func__,
+					(int)loc_dev->baseaddress);
+	loc_dev->irq = platform_get_irq(pdev, 0);
+	if (!loc_dev->irq) {
+		pr_err("%s: platform_get_irq failed\n", __func__);
+		status = -ENODEV;
+		goto free_regs;
+	}
+
+	loc_dev->index = ISP1763_HC;	/*zero */
+	loc_dev->length = resource_size(loc_dev->mem_res);
+
+	hal_init(("isp1763 HC MEM Base= %p irq = %d\n",
+		loc_dev->baseaddress, loc_dev->irq));
+
+	/* Setup GPIOs and isssue RESET_N to Controller */
+	if (pdata->setup_gpio)
+		if (pdata->setup_gpio(1))
+			pr_err("%s: Failed to setup GPIOs for isp1763\n",
+								 __func__);
+	if (pdata->reset_gpio) {
+		gpio_set_value(pdata->reset_gpio, 0);
+		msleep(10);
+		gpio_set_value(pdata->reset_gpio, 1);
+	} else {
+		pr_err("%s: Failed to issue RESET_N to isp1763\n", __func__);
+	}
+
+	dummy_mem_read(loc_dev);
+
+	chipid = isp1763_reg_read32(loc_dev, DC_CHIPID, chipid);
+	pr_info("START: chip id:%x\n", chipid);
+
+	/*reset the host controller  */
+	pr_debug("RESETTING\n");
+	us_reset_hc |= 0x1;
+	isp1763_reg_write16(loc_dev, 0xB8, us_reset_hc);
+	msleep(20);
+	us_reset_hc = 0;
+	us_reset_hc |= 0x2;
+	isp1763_reg_write16(loc_dev, 0xB8, us_reset_hc);
+
+	chipid = isp1763_reg_read32(loc_dev, DC_CHIPID, chipid);
+	pr_info("after HC reset, chipid:%x\n", chipid);
+
+	msleep(20);
+	hwmodectrl = isp1763_reg_read16(loc_dev, HC_HWMODECTRL_REG, hwmodectrl);
+	pr_debug("Mode Ctrl Value b4 setting buswidth: %x\n", hwmodectrl);
+#ifdef DATABUS_WIDTH_16
+	hwmodectrl &= 0xFFEF;	/*enable the 16 bit bus */
+#else
+	pr_debug("Setting 8-BIT mode\n");
+	hwmodectrl |= 0x0010;	/*enable the 8 bit bus */
+#endif
+	isp1763_reg_write16(loc_dev, HC_HWMODECTRL_REG, hwmodectrl);
+	pr_debug("writing 0x%x to hw mode reg\n", hwmodectrl);
+
+	hwmodectrl = isp1763_reg_read16(loc_dev, HC_HWMODECTRL_REG, hwmodectrl);
+	msleep(100);
+
+	pr_debug("Mode Ctrl Value after setting buswidth: %x\n", hwmodectrl);
+
+
+	chipid = isp1763_reg_read32(loc_dev, DC_CHIPID, chipid);
+	pr_debug("after setting HW MODE to 8bit, chipid:%x\n", chipid);
+
+
+
+	hal_init(("isp1763 DC MEM Base= %lx irq = %d\n",
+		loc_dev->io_base, loc_dev->irq));
+	reg_data = isp1763_reg_read16(loc_dev, HC_SCRATCH_REG, reg_data);
+	pr_debug("Scratch register is 0x%x\n", reg_data);
+	reg_data = 0xABCD;
+	isp1763_reg_write16(loc_dev, HC_SCRATCH_REG, reg_data);
+	reg_data = isp1763_reg_read16(loc_dev, HC_SCRATCH_REG, reg_data);
+	pr_debug("After write, Scratch register is 0x%x\n", reg_data);
+
+	if (reg_data != 0xABCD) {
+		pr_err("%s: Scratch register write mismatch!!\n", __func__);
+		status = -ENODEV;
+		goto free_gpios;
+	}
+
+	memcpy(loc_dev->name, ISP1763_DRIVER_NAME, sizeof(ISP1763_DRIVER_NAME));
+	loc_dev->name[sizeof(ISP1763_DRIVER_NAME)] = 0;
+
+	pr_debug(KERN_NOTICE "-isp1763_pci_probe\n");
+	hal_entry("%s: Exit\n", __FUNCTION__);
+	return 0;
+
+free_gpios:
+	if (pdata->setup_gpio)
+		pdata->setup_gpio(0);
+free_regs:
+	iounmap(loc_dev->baseaddress);
+put_mem_res:
+	loc_dev->baseaddress = NULL;
+	hal_entry("%s: Exit\n", __FUNCTION__);
+	return status;
+}				/* End of isp1763_probe */
+
+
+/*--------------------------------------------------------------*
+ *
+ *  Module details: isp1763_remove
+ *
+ * cleanup function of ISP1763
+ * This functions de-initializes the local variables, frees GPIOs
+ * and releases memory resource.
+ *
+ *  Input:
+ *              struct platform_device *dev    ----> Platform Device structure
+ *
+ *  Output void
+ *
+ *  Called by: system function module_cleanup
+ *
+ *
+ *
+ --------------------------------------------------------------*/
+static int __devexit
+isp1763_remove(struct platform_device *pdev)
+{
+	struct isp1763_dev *loc_dev;
+	struct isp1763_platform_data *pdata = pdev->dev.platform_data;
+
+	hal_init(("isp1763_pci_remove(dev=%p)\n", dev));
+
+	loc_dev = &isp1763_loc_dev[ISP1763_HC];
+	iounmap(loc_dev->baseaddress);
+	loc_dev->baseaddress = NULL;
+	if (pdata->setup_gpio)
+		return pdata->setup_gpio(0);
+
+	return 0;
+}				/* End of isp1763_remove */
+
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_init(isp1763_module_init);
+module_exit(isp1763_module_cleanup);
diff --git a/drivers/usb/host/pehci/hal/hal_msm.h b/drivers/usb/host/pehci/hal/hal_msm.h
new file mode 100644
index 0000000..a7a65b7
--- /dev/null
+++ b/drivers/usb/host/pehci/hal/hal_msm.h
@@ -0,0 +1,85 @@
+/* 
+* Copyright (C) ST-Ericsson AP Pte Ltd 2010 
+*
+* ISP1763 Linux OTG Controller driver : hal
+* 
+* 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 Street, Fifth Floor, Boston, MA  02110-1301, USA. 
+* 
+* This is a hardware abstraction layer header file.
+* 
+* Author : wired support <wired.support@stericsson.com>
+*
+*/
+
+#ifndef	HAL_X86_H
+#define	HAL_X86_H
+
+#define	DRIVER_AUTHOR	"ST-ERICSSON	  "
+#define	DRIVER_DESC	"ISP1763 bus driver"
+
+/* Driver tuning, per ST-ERICSSON requirements:	*/
+
+#define	MEM_TO_CHECK		4096	/*bytes, must be multiple of 2 */
+
+/* BIT defines */
+#define	BIT0	(1 << 0)
+#define	BIT1	(1 << 1)
+#define	BIT2	(1 << 2)
+#define	BIT3	(1 << 3)
+#define	BIT4	(1 << 4)
+#define	BIT5	(1 << 5)
+#define	BIT6	(1 << 6)
+#define	BIT7	(1 << 7)
+#define	BIT8	(1 << 8)
+#define	BIT9	(1 << 9)
+#define	BIT10	(1 << 10)
+#define	BIT11	(1 << 11)
+#define	BIT12	(1 << 12)
+#define	BIT13	(1 << 13)
+#define	BIT14	(1 << 14)
+#define	BIT15	(1 << 15)
+#define	BIT16	(1 << 16)
+#define	BIT17	(1 << 17)
+#define	BIT18	(1 << 18)
+#define	BIT19	(1 << 19)
+#define	BIT20	(1 << 20)
+#define	BIT21	(1 << 21)
+#define	BIT22	(1 << 22)
+#define	BIT23	(1 << 23)
+#define	BIT24	(1 << 24)
+#define	BIT25	(1 << 26)
+#define	BIT27	(1 << 27)
+#define	BIT28	(1 << 28)
+#define	BIT29	(1 << 29)
+#define	BIT30	(1 << 30)
+#define	BIT31	(1 << 31)
+
+/* Definitions Related to Chip Address and CPU Physical	Address
+ * cpu_phy_add:	CPU Physical Address , it uses 32 bit data per address
+ * chip_add   :	Chip Address, it uses double word(64) bit data per address
+ */
+#define	chip_add(cpu_phy_add)		(((cpu_phy_add)	- 0x400) / 8)
+#define	cpu_phy_add(chip_add)		((8 * (chip_add)) + 0x400)
+
+/* for getting end add,	and start add, provided	we have	one address with us */
+/* IMPORTANT length  hex(base16) and dec(base10) works fine*/
+#define	end_add(start_add, length)	(start_add + (length - 4))
+#define	start_add(end_add, length)	(end_add - (length - 4))
+
+/* Device Registers*/
+#define	DEV_UNLOCK_REGISTER		0x7C
+#define	DEV_INTERRUPT_REGISTER		0x18
+#define	INT_ENABLE_REGISTER		0x14
+
+#endif /*_HAL_X86_H_ */
diff --git a/drivers/usb/host/pehci/hal/isp1763.h b/drivers/usb/host/pehci/hal/isp1763.h
new file mode 100644
index 0000000..7355185
--- /dev/null
+++ b/drivers/usb/host/pehci/hal/isp1763.h
@@ -0,0 +1,227 @@
+/* 
+* Copyright (C) ST-Ericsson AP Pte Ltd 2010 
+*
+* ISP1763 Linux OTG Controller driver : hal
+* 
+* 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 Street, Fifth Floor, Boston, MA  02110-1301, USA. 
+* 
+* This is a hardware abstraction layer header file.
+* 
+* Author : wired support <wired.support@stericsson.com>
+*
+*/
+
+#ifndef	ISP1763_H
+#define	ISP1763_H
+
+
+
+/* For debugging option: ------------------- */
+#define PTD_DUMP_SCHEDULE
+#undef  PTD_DUMP_SCHEDULE
+
+#define PTD_DUMP_COMPLETE
+#undef  PTD_DUMP_COMPLETE
+/* ------------------------------------*/
+#define CONFIG_ISO_SUPPORT 
+
+#ifdef CONFIG_ISO_SUPPORT
+
+#define	ISO_DBG_ENTRY 1
+#define	ISO_DBG_EXIT  1
+#define	ISO_DBG_ADDR 1
+#define	ISO_DBG_DATA 1
+#define	ISO_DBG_ERR  1
+#define	ISO_DBG_INFO 1
+
+#if 0				/* Set to 1 to enable isochronous debugging */
+#define	iso_dbg(category, format, arg...) \
+do \
+{ \
+	if(category) \
+	{ \
+		printk(format, ## arg);	\
+	} \
+} while(0)
+#else
+#define	iso_dbg(category, format, arg...) while(0)
+#endif
+
+#endif /* CONFIG_ISO_SUPPORT */
+
+/*Debug	For Entry/Exit of the functions	*/
+//#define HCD_DEBUG_LEVEL1 
+#ifdef HCD_DEBUG_LEVEL1
+#define	pehci_entry(format, args... ) printk(format, ##args)
+#else
+#define	pehci_entry(format, args...) do	{ } while(0)
+#endif
+
+/*Debug	for Port Info and Errors */
+//#define HCD_DEBUG_LEVEL2 
+#ifdef HCD_DEBUG_LEVEL2
+#define	pehci_print(format, args... ) printk(format, ##args)
+#else
+#define	pehci_print(format, args...) do	{ } while(0)
+#endif
+
+/*Debug	For the	Port changes and Enumeration */
+//#define HCD_DEBUG_LEVEL3 
+#ifdef HCD_DEBUG_LEVEL3
+#define	pehci_info(format,arg...) printk(format, ##arg)
+#else
+#define	pehci_info(format,arg...) do {}	while (0)
+#endif
+
+/*Debug	For Transfer flow  */
+// #define HCD_DEBUG_LEVEL4 
+#ifdef HCD_DEBUG_LEVEL4
+#define	pehci_check(format,args...) printk(format, ##args)
+#else
+#define	pehci_check(format,args...)
+#endif
+/*******************END	HOST CONTROLLER**********************************/
+
+
+
+/*******************START DEVICE CONTROLLER******************************/
+
+/* For MTP support */
+#undef MTP_ENABLE		/* Enable to add MTP support; But requires MTP class driver to be present to work */
+/*For CHAPTER8 TEST */
+#undef	CHAPTER8_TEST		/* Enable to Pass Chapter 8 Test */
+
+/* Debug Entery/Exit of	Function as well as some other Info */
+//#define DEV_DEBUG_LEVEL2
+#ifdef DEV_DEBUG_LEVEL2
+#define	dev_print(format,arg...) printk(format,	##arg)
+#else
+#define	dev_print(format,arg...) do {} while (0)
+#endif
+
+/*Debug	for Interrupt ,	Registers , device Enable/Disable and some other info */
+//#define DEV_DEBUG_LEVEL3
+#undef dev_info
+#ifdef DEV_DEBUG_LEVEL3
+#define	dev_info(format,arg...)	printk(format, ##arg)
+#else
+#define	dev_info(format,arg...)	do {} while (0)
+#endif
+
+/*Debug	for Tranffer flow , Enumeration	and Packet info	*/
+//#define DEV_DEBUG_LEVEL4
+#ifdef DEV_DEBUG_LEVEL4
+#define	dev_check(format,args...) printk(format, ##args)
+#else
+#define	dev_check(format,args...) do{}while(0)
+#endif
+/*******************END	DEVICE CONTROLLER********************************/
+
+
+/*******************START MSCD*******************************************/
+/*Debug	Entery/Exit of Function	as well	as some	other Information*/
+//#define MSCD_DEBUG_LEVEL2
+#ifdef MSCD_DEBUG_LEVEL2
+#define	mscd_print(format,arg...) printk(format, ##arg)
+#else
+#define	mscd_print(format,arg...) do {}	while (0)
+#endif
+
+/*Debug	for Info */
+//#define MSCD_DEBUG_LEVEL3
+#ifdef MSCD_DEBUG_LEVEL3
+#define	mscd_info(format,arg...) printk(format,	##arg)
+#else
+#define	mscd_info(format,arg...) do {} while (0)
+#endif
+/*******************END	MSCD*********************************************/
+
+
+/*******************START OTG CONTROLLER*********************************/
+/*#define	OTG */			/*undef	for Device only	and Host only */
+#define	ALL_FSM_FLAGS
+/*Debug	for Entry/Exit and Info	*/
+/* #define OTG_DEBUG_LEVEL1 */
+#ifdef OTG_DEBUG_LEVEL1
+#define	otg_entry(format, args... ) printk(format, ##args)
+#else
+#define	otg_entry(format, args...) do {	} while(0)
+#endif
+
+/*Debug	for State Machine Flow */
+/* #define OTG_DEBUG_LEVEL2 */
+#ifdef OTG_DEBUG_LEVEL2
+#define	otg_print(format,arg...) printk(format,	##arg)
+#else
+#define	otg_print(format,arg...) do {} while (0)
+#endif
+/*Debug	for Info */
+/* #define OTG_DEBUG_LEVEL3 */
+#ifdef OTG_DEBUG_LEVEL3
+#define	otg_info(format,arg...)	printk(format, ##arg)
+#else
+#define	otg_info(format,arg...)	do {} while (0)
+#endif
+
+/* #define OTG_DEBUG_LEVEL4 */
+#ifdef OTG_DEBUG_LEVEL4
+#define	otg_printB(format,arg...) printk(format, ##arg)
+#else
+#define	otg_printB(format,arg...) do {}	while (0)
+#endif
+/*******************END	OTG CONTROLLER***********************************/
+
+
+
+/*******************START FOR HAL ***************************************/
+#define info pr_debug
+#define warn pr_warn
+/*Debug For Entry and Exit of the functions */
+#undef HAL_DEBUG_LEVEL1
+#ifdef HAL_DEBUG_LEVEL1
+#define	hal_entry(format, args... ) printk(format, ##args)
+#else
+#define	hal_entry(format, args...) do {	} while(0)
+#endif
+
+/*Debug	For Interrupt information */
+#undef HAL_DEBUG_LEVEL2
+#ifdef HAL_DEBUG_LEVEL2
+#define	hal_int(format,	args...	) printk(format, ##args)
+#else
+#define	hal_int(format,	args...) do { }	while(0)
+#endif
+
+/*Debug	For HAL	Initialisation and Mem Initialisation */
+#undef HAL_DEBUG_LEVEL3
+#ifdef HAL_DEBUG_LEVEL3
+#define	hal_init(format, args... ) printk(format, ##args)
+#else
+#define	hal_init(format, args...) do { } while(0)
+#endif
+/*******************END	FOR HAL*******************************************/
+
+
+
+/*******************START FOR ALL CONTROLLERS*****************************/
+/*#define	CONFIG_USB_OTG */	/*undef	for Device only	and Host only */
+/*#define	ISP1763_DEVICE */
+
+#ifdef CONFIG_USB_DEBUG
+#define	DEBUG
+#else
+#undef DEBUG
+#endif
+/*******************END	FOR ALL	CONTROLLERS*******************************/
+#endif
diff --git a/drivers/usb/host/pehci/host/Makefile b/drivers/usb/host/pehci/host/Makefile
new file mode 100644
index 0000000..0c8552e
--- /dev/null
+++ b/drivers/usb/host/pehci/host/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the pehci driver (if driver is inside kernel tree).
+#
+
+obj-$(CONFIG_USB_PEHCI_HCD) += pehci.o
+
diff --git a/drivers/usb/host/pehci/host/itdptd.c b/drivers/usb/host/pehci/host/itdptd.c
new file mode 100644
index 0000000..6699c3a
--- /dev/null
+++ b/drivers/usb/host/pehci/host/itdptd.c
@@ -0,0 +1,2156 @@
+/* 
+* Copyright (C) ST-Ericsson AP Pte Ltd 2010 
+*
+* ISP1763 Linux OTG Controller driver : host
+* 
+* 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 Street, Fifth Floor, Boston, MA  02110-1301, USA. 
+* 
+* This is a host controller driver file. Isochronous event processing is handled here.
+* 
+* Author : wired support <wired.support@stericsson.com>
+*
+*/
+#ifdef CONFIG_ISO_SUPPORT
+void phcd_clean_periodic_ep(void);
+#endif
+
+#ifdef CONFIG_ISO_SUPPORT
+
+#define MAX_URBS		8
+#define MAX_EPS			2/*maximum 2 endpoints supported in ISO transfers.*/
+/*number of microframe per frame which is scheduled, for high speed device
+* actually , NUMMICROFRAME should be 8 , but the micro frame #7 is fail , so
+* there's just 4 microframe is used (#0 -> #4)
+* Writer : LyNguyen - 25Nov09
+*/
+#define NUMMICROFRAME		8
+struct urb *gstUrb_pending[MAX_URBS] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+struct usb_host_endpoint *periodic_ep[MAX_EPS];
+
+int giUrbCount = 0;		/* count the pending urb*/
+int giUrbIndex = 0;		/*the index of urb need to be scheduled next*/
+/*
+ * phcd_iso_sitd_to_ptd - convert an SITD into a PTD
+ *
+ * phci_hcd *hcd
+ *      - Main host controller driver structure
+ * struct ehci_sitd *sitd
+ *  - Isochronous Transfer Descriptor, contains elements as defined by the
+ *        EHCI standard plus a few more specific elements.
+ * struct urb *urb
+ *  - USB Request Block, contains information regarding the type and how much data
+ *    is requested to be transferred.
+ * void  * ptd
+ *  - Points to the ISO ptd structure that needs to be initialized
+ *
+ * API Description
+ * This is mainly responsible for:
+ *  -Initializing the PTD that will be used for the ISO transfer
+ */
+void *
+phcd_iso_sitd_to_ptd(phci_hcd * hcd,
+	struct ehci_sitd *sitd, struct urb *urb, void *ptd)
+{
+	struct _isp1763_isoptd *iso_ptd;
+	struct isp1763_mem_addr *mem_addr;
+
+	unsigned long max_packet, mult, length, td_info1, td_info3;
+	unsigned long token, port_num, hub_num, data_addr;
+	unsigned long frame_number;
+
+	iso_dbg(ISO_DBG_ENTRY, "phcd_iso_sitd_to_ptd entry\n");
+
+	/* Variable initialization */
+	iso_ptd = (struct _isp1763_isoptd *) ptd;
+	mem_addr = &sitd->mem_addr;
+
+	/*
+	 * For both ISO and INT endpoints descriptors, new bit fields we added to
+	 * specify whether or not the endpoint supports high bandwidth, and if so
+	 * the number of additional packets that the endpoint can support during a
+	 * single microframe.
+	 * Bits 12:11 specify whether the endpoint supports high-bandwidth transfers
+	 * Valid values:
+	 *             00 None (1 transaction/uFrame)
+	 *             01 1 additional transaction
+	 *             10 2 additional transactions
+	 *             11 reserved
+	 */
+	max_packet = usb_maxpacket(urb->dev, urb->pipe,usb_pipeout(urb->pipe));
+
+	/*
+	 * We need to add 1 since our Multi starts with 1 instead of the USB specs defined
+	 * zero (0).
+	 */
+	mult = 1 + ((max_packet >> 11) & 0x3);
+	max_packet &= 0x7ff;
+
+	/* This is the size of the request (bytes to write or bytes to read) */
+	length = sitd->length;
+
+	/*
+	 * Set V bit to indicate that there is payload to be sent or received. And
+	 * indicate that the current PTD is active.
+	 */
+	td_info1 = QHA_VALID;
+
+	/*
+	 * Set the number of bytes that can be transferred by this PTD. This indicates
+	 * the depth of the data field.
+	 */
+	td_info1 |= (length << 3);
+
+	/*
+	 * Set the maximum packet length which indicates the maximum number of bytes that
+	 * can be sent to or received from the endpoint in a single data packet.
+	 */
+	if (urb->dev->speed != USB_SPEED_HIGH) {
+		/*
+		 * According to the ISP1763 specs for sITDs, OUT token max packet should
+		 * not be more  than 188 bytes, while IN token max packet not more than
+		 * 192 bytes (ISP1763 Rev 3.01, Table 72, page 79
+		 */
+		if (usb_pipein(urb->pipe) && (max_packet > 192)) {
+			iso_dbg(ISO_DBG_INFO,
+				"IN Max packet over maximum\n");
+			max_packet = 192;
+		}
+
+		if ((!usb_pipein(urb->pipe)) && (max_packet > 188)) {
+			iso_dbg(ISO_DBG_INFO,
+				"OUT Max packet over maximum\n");
+			max_packet = 188;
+		}
+	}
+	td_info1 |= (max_packet << 18);
+
+	/*
+	 * Place the FIRST BIT of the endpoint number here.
+	 */
+	td_info1 |= (usb_pipeendpoint(urb->pipe) << 31);
+
+	/*
+	 * Set the number of successive packets the HC can submit to the endpoint.
+	 */
+	if (urb->dev->speed == USB_SPEED_HIGH) {
+		td_info1 |= MULTI(mult);
+	}
+
+	/* Set the first DWORD */
+	iso_ptd->td_info1 = td_info1;
+	iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD0 = 0x%08x\n",
+		iso_ptd->td_info1);
+
+	/*
+	 * Since the first bit have already been added on the first DWORD of the PTD
+	 * we only need to add the last 3-bits of the endpoint number.
+	 */
+	token = (usb_pipeendpoint(urb->pipe) & 0xE) >> 1;
+
+	/*
+	 * Get the device address and set it accordingly to its assigned bits of the 2nd
+	 * DWORD.
+	 */
+	token |= usb_pipedevice(urb->pipe) << 3;
+
+	/* See a split transaction is needed */
+	if (urb->dev->speed != USB_SPEED_HIGH) {
+		/*
+		 * If we are performing a SPLIT transaction indicate that it is so by setting
+		 * the S bit of the second DWORD.
+		 */
+		token |= 1 << 14;
+
+		port_num = urb->dev->ttport;
+		hub_num = urb->dev->tt->hub->devnum;
+
+		/* Set the the port number of the hub or embedded TT */
+		token |= port_num << 18;
+
+		/*
+		 * Set the hub address, this should be zero for the internal or
+		 * embedded hub
+		 */
+		token |= hub_num << 25;
+	}
+
+	/* if(urb->dev->speed != USB_SPEED_HIGH) */
+	/*
+	 * Determine if the direction of this pipe is IN, if so set the Token bit of
+	 * the second DWORD to indicate it as IN. Since it is initialized to zero and
+	 * zero indicates an OUT token, then we do not need anything to the Token bit
+	 * if it is an OUT token.
+	 */
+	if (usb_pipein(urb->pipe)) {
+		token |= (IN_PID << 10);
+	}
+
+	/* Set endpoint type to Isochronous */
+	token |= EPTYPE_ISO;
+
+	/* Set the second DWORD */
+	iso_ptd->td_info2 = token;
+	iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD1 = 0x%08x\n",
+		iso_ptd->td_info2);
+
+	/*
+	 * Get the physical address of the memory location that was allocated for this PTD
+	 * in the PAYLOAD region, using the formula indicated in sectin 7.2.2 of the ISP1763 specs
+	 * rev 3.01 page 17 to 18.
+	 */
+	data_addr = ((unsigned long) (mem_addr->phy_addr) & 0xffff) - 0x400;
+	data_addr >>= 3;
+
+	/*  Set it to its location in the third DWORD */
+	td_info3 =( 0xffff&data_addr) << 8;
+
+	/*
+	 * Set the frame number when this PTD will be sent for ISO OUT or IN
+	 * Bits 0 to 2 are don't care, only bits 3 to 7.
+	 */
+	frame_number = sitd->framenumber;
+	frame_number = sitd->start_frame;
+	td_info3 |= (0xff& ((frame_number) << 3));
+
+	/* Set the third DWORD */
+	iso_ptd->td_info3 = td_info3;
+	iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD2 = 0x%08x\n",
+		iso_ptd->td_info3);
+
+	/*
+	 * Set the A bit of the fourth DWORD to 1 to indicate that this PTD is active.
+	 * This have the same functionality with the V bit of DWORD0
+	 */
+	iso_ptd->td_info4 = QHA_ACTIVE;
+	iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD3 = 0x%08x\n",
+		iso_ptd->td_info4);
+
+	/* Set the fourth DWORD to specify which uSOFs the start split needs to be placed */
+	if (usb_pipein(urb->pipe)){
+		iso_ptd->td_info5 = (sitd->ssplit);
+	}else{
+		iso_ptd->td_info5 = (sitd->ssplit << 2);
+	}
+	iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD4 = 0x%08x\n",
+		iso_ptd->td_info5);
+
+	/*
+	 * Set the fifth DWORD to specify which uSOFs the complete split needs to be sent.
+	 * This is VALID only for IN (since ISO transfers don't have handshake stages)
+	 */
+	iso_ptd->td_info6 = sitd->csplit;
+	iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD5 = 0x%08x\n",
+		iso_ptd->td_info6);
+
+	/*printk(" [phcd_iso_itd_to_ptd]: DWORD0 = 0x%08x\n",iso_ptd->td_info1);
+	printk(" [phcd_iso_itd_to_ptd]: DWORD1 = 0x%08x\n",iso_ptd->td_info2);
+	printk(" [phcd_iso_itd_to_ptd]: DWORD2 = 0x%08x\n",iso_ptd->td_info3);
+	printk(" [phcd_iso_itd_to_ptd]: DWORD3 = 0x%08x\n",iso_ptd->td_info4);
+	printk(" [phcd_iso_itd_to_ptd]: DWORD4 = 0x%08x\n",iso_ptd->td_info5);
+	printk(" [phcd_iso_itd_to_ptd]: DWORD5 = 0x%08x\n",iso_ptd->td_info6);*/
+	iso_dbg(ISO_DBG_EXIT, "phcd_iso_itd_to_ptd exit\n");
+	return iso_ptd;
+}
+
+
+/*
+ * phcd_iso_itd_to_ptd - convert an ITD into a PTD
+ *
+ * phci_hcd *hcd
+ *      - Main host controller driver structure
+ * struct ehci_itd *itd
+ *  - Isochronous Transfer Descriptor, contains elements as defined by the
+ *        EHCI standard plus a few more ST-ERICSSON specific elements.
+ * struct urb *urb
+ *  - USB Request Block, contains information regarding the type and how much data
+ *    is requested to be transferred.
+ * void  * ptd
+ *  - Points to the ISO ptd structure that needs to be initialized
+ *
+ * API Description
+ * This is mainly responsible for:
+ *  -Initializing the PTD that will be used for the ISO transfer
+ */
+void *
+phcd_iso_itd_to_ptd(phci_hcd * hcd,
+	struct ehci_itd *itd, struct urb *urb, void *ptd)
+{
+	struct _isp1763_isoptd *iso_ptd;
+	struct isp1763_mem_addr *mem_addr;
+
+	unsigned long max_packet, mult, length, td_info1, td_info3;
+	unsigned long token, port_num, hub_num, data_addr;
+	unsigned long frame_number;
+	int maxpacket;
+	iso_dbg(ISO_DBG_ENTRY, "phcd_iso_itd_to_ptd entry\n");
+
+	/* Variable initialization */
+	iso_ptd = (struct _isp1763_isoptd *) ptd;
+	mem_addr = &itd->mem_addr;
+
+	/*
+	 * For both ISO and INT endpoints descriptors, new bit fields we added to
+	 * specify whether or not the endpoint supports high bandwidth, and if so
+	 * the number of additional packets that the endpoint can support during a
+	 * single microframe.
+	 * Bits 12:11 specify whether the endpoint supports high-bandwidth transfers
+	 * Valid values:
+	 *             00 None (1 transaction/uFrame)
+	 *             01 1 additional transaction
+	 *             10 2 additional transactions
+	 *             11 reserved
+	 */
+	max_packet = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+
+	maxpacket = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));	
+
+	/*
+	 * We need to add 1 since our Multi starts with 1 instead of the USB specs defined
+	 * zero (0).
+	 */
+	maxpacket &= 0x7ff;
+	mult = 1 + ((max_packet >> 11) & 0x3);
+
+
+	max_packet &= 0x7ff;
+
+	/* This is the size of the request (bytes to write or bytes to read) */
+	length = itd->length;
+
+	/*
+	 * Set V bit to indicate that there is payload to be sent or received. And
+	 * indicate that the current PTD is active.
+	 */
+	td_info1 = QHA_VALID;
+
+	/*
+	 * Set the number of bytes that can be transferred by this PTD. This indicates
+	 * the depth of the data field.
+	 */
+	td_info1 |= (length << 3);
+
+	/*
+	 * Set the maximum packet length which indicates the maximum number of bytes that
+	 * can be sent to or received from the endpoint in a single data packet.
+	 */
+	if (urb->dev->speed != USB_SPEED_HIGH) {
+		/*
+		 * According to the ISP1763 specs for sITDs, OUT token max packet should
+		 * not be more  than 188 bytes, while IN token max packet not more than
+		 * 192 bytes (ISP1763 Rev 3.01, Table 72, page 79
+		 */
+		if (usb_pipein(urb->pipe) && (max_packet > 192)) {
+			iso_dbg(ISO_DBG_INFO,
+				"[phcd_iso_itd_to_ptd]: IN Max packet over maximum\n");
+			max_packet = 192;
+		}
+
+		if ((!usb_pipein(urb->pipe)) && (max_packet > 188)) {
+			iso_dbg(ISO_DBG_INFO,
+				"[phcd_iso_itd_to_ptd]: OUT Max packet over maximum\n");
+			max_packet = 188;
+		}
+	} else {		/*HIGH SPEED */
+
+		if (max_packet > 1024){
+			max_packet = 1024;
+		}
+	}
+	td_info1 |= (max_packet << 18);
+
+	/*
+	 * Place the FIRST BIT of the endpoint number here.
+	 */
+	td_info1 |= (usb_pipeendpoint(urb->pipe) << 31);
+
+	/*
+	 * Set the number of successive packets the HC can submit to the endpoint.
+	 */
+	if (urb->dev->speed == USB_SPEED_HIGH) {
+		td_info1 |= MULTI(mult);
+	}
+
+	/* Set the first DWORD */
+	iso_ptd->td_info1 = td_info1;
+	iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD0 = 0x%08x\n",
+		iso_ptd->td_info1);
+
+	/*
+	 * Since the first bit have already been added on the first DWORD of the PTD
+	 * we only need to add the last 3-bits of the endpoint number.
+	 */
+	token = (usb_pipeendpoint(urb->pipe) & 0xE) >> 1;
+
+	/*
+	 * Get the device address and set it accordingly to its assigned bits of the 2nd
+	 * DWORD.
+	 */
+	token |= usb_pipedevice(urb->pipe) << 3;
+
+	/* See a split transaction is needed */
+	if (urb->dev->speed != USB_SPEED_HIGH) {
+		/*
+		 * If we are performing a SPLIT transaction indicate that it is so by setting
+		 * the S bit of the second DWORD.
+		 */
+		token |= 1 << 14;
+
+		port_num = urb->dev->ttport;
+		hub_num = urb->dev->tt->hub->devnum;
+
+		/* Set the the port number of the hub or embedded TT */
+		token |= port_num << 18;
+
+		/*
+		 * Set the hub address, this should be zero for the internal or
+		 * embedded hub
+		 */
+		token |= hub_num << 25;
+	}
+
+	/* if(urb->dev->speed != USB_SPEED_HIGH) */
+	/*
+	 * Determine if the direction of this pipe is IN, if so set the Token bit of
+	 * the second DWORD to indicate it as IN. Since it is initialized to zero and
+	 * zero indicates an OUT token, then we do not need anything to the Token bit
+	 * if it is an OUT token.
+	 */
+	if (usb_pipein(urb->pipe)){
+		token |= (IN_PID << 10);
+	}
+
+	/* Set endpoint type to Isochronous */
+	token |= EPTYPE_ISO;
+
+	/* Set the second DWORD */
+	iso_ptd->td_info2 = token;
+	iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD1 = 0x%08x\n",
+		iso_ptd->td_info2);
+
+	/*
+	 * Get the physical address of the memory location that was allocated for this PTD
+	 * in the PAYLOAD region, using the formula indicated in sectin 7.2.2 of the ISP1763 specs
+	 * rev 3.01 page 17 to 18.
+	 */
+	data_addr = ((unsigned long) (mem_addr->phy_addr) & 0xffff) - 0x400;
+	data_addr >>= 3;
+
+	/*  Set it to its location in the third DWORD */
+	td_info3 = (data_addr&0xffff) << 8;
+
+	/*
+	 * Set the frame number when this PTD will be sent for ISO OUT or IN
+	 * Bits 0 to 2 are don't care, only bits 3 to 7.
+	 */
+	frame_number = itd->framenumber;
+	td_info3 |= (0xff&(frame_number << 3));
+
+	/* Set the third DWORD */
+	iso_ptd->td_info3 = td_info3;
+	iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD2 = 0x%08x\n",
+		iso_ptd->td_info3);
+
+	/*
+	 * Set the A bit of the fourth DWORD to 1 to indicate that this PTD is active.
+	 * This have the same functionality with the V bit of DWORD0
+	 */
+	iso_ptd->td_info4 = QHA_ACTIVE;
+	iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD3 = 0x%08x\n",
+		iso_ptd->td_info4);
+
+	/* Set the fourth DWORD to specify which uSOFs the start split needs to be placed */
+	iso_ptd->td_info5 = itd->ssplit;
+	iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD4 = 0x%08x\n",
+		iso_ptd->td_info5);
+
+	/*
+	 * Set the fifth DWORD to specify which uSOFs the complete split needs to be sent.
+	 * This is VALID only for IN (since ISO transfers don't have handshake stages)
+	 */
+	iso_ptd->td_info6 = itd->csplit;
+	iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD5 = 0x%08x\n",
+		iso_ptd->td_info6);
+
+	iso_dbg(ISO_DBG_EXIT, "phcd_iso_itd_to_ptd exit\n");
+	return iso_ptd;
+}				/* phcd_iso_itd_to_ptd */
+
+/*
+ * phcd_iso_scheduling_info - Initializing the start split and complete split.
+ *
+ * phci_hcd *hcd
+ *      - Main host controller driver structure
+ * struct ehci_qh *qhead
+ *  - Contains information about the endpoint.
+ * unsigned long max_pkt
+ *  - Maximum packet size that the endpoint in capable of handling
+ * unsigned long high_speed
+ *  - Indicates if the bus is a high speed bus
+ * unsigned long ep_in
+ *  - Inidcates if the endpoint is an IN endpoint
+ *
+ * API Description
+ * This is mainly responsible for:
+ *  - Determining the number of start split needed during an OUT transaction or
+ *    the number of complete splits needed during an IN transaction.
+ */
+unsigned long
+phcd_iso_scheduling_info(phci_hcd * hcd,
+	struct ehci_qh *qhead,
+	unsigned long max_pkt,
+	unsigned long high_speed, unsigned long ep_in)
+{
+	unsigned long count, usof, temp;
+
+	/* Local variable initialization */
+	usof = 0x1;
+
+	if (high_speed) {
+		qhead->csplit = 0;
+
+		/* Always send high speed transfers in first uframes */
+		qhead->ssplit = 0x1;
+		return 0;
+	}
+
+	/* Determine how many 188 byte-transfers are needed to send all data */
+	count = max_pkt / 188;
+
+	/*
+	 * Check is the data is not a factor of 188, if it is not then we need
+	 * one more 188 transfer to move the last set of data less than 188.
+	 */
+	if (max_pkt % 188){
+		count += 1;
+	}
+
+	/*
+	 * Remember that usof was initialized to 0x1 so that means
+	 * that usof is always guranteed a value of 0x1 and then
+	 * depending on the maxp, other bits of usof will also be set.
+	 */
+	for (temp = 0; temp < count; temp++){
+		usof |= (0x1 << temp);
+	}
+
+	if (ep_in) {
+		/*
+		 * Send start split into first frame.
+		 */
+		qhead->ssplit = 0x1;
+
+		/*
+		 * Inidicate that we can send a complete split starting from
+		 * the third uFrame to how much complete split is needed to
+		 * retrieve all data.
+		 *
+		 * Of course, the first uFrame is reserved for the start split, the
+		 * second is reserved for the TT to send the request and get some
+		 * data.
+		 */
+		qhead->csplit = (usof << 2);
+	} else {
+		/*
+		 * For ISO OUT we don't need to send out a complete split
+		 * since we do not require and data coming in to us (since ISO
+		 * do not have integrity checking/handshake).
+		 *
+		 * For start split we indicate that we send a start split from the
+		 * first uFrame up to the the last uFrame needed to retrieve all
+		 * data
+		 */
+		qhead->ssplit = usof;
+		qhead->csplit = 0;
+	}	/* else for if(ep_in) */
+	return 0;
+}				/* phcd_iso_scheduling_info */
+
+/*
+ * phcd_iso_sitd_fill - Allocate memory from the PAYLOAD memory region
+ *
+ * phci_hcd *pHcd_st
+ *  - Main host controller driver structure
+ * struct ehci_sitd *sitd
+ *  - Isochronous Transfer Descriptor, contains elements as defined by the
+ *        EHCI standard plus a few more  specific elements.
+ * struct urb *urb
+ *  - USB Request Block, contains information regarding the type and how much data
+ *    is requested to be transferred.
+ * unsigned long packets
+ *  - Total number of packets to completely transfer this ISO transfer request.
+ *
+ * API Description
+ * This is mainly responsible for:
+ * - Initialize the following elements of the ITS structure
+ *       > sitd->length = length;        -- the size of the request
+ *       > sitd->multi = multi;          -- the number of transactions for
+ *                                         this EP per micro frame
+ *       > sitd->hw_bufp[0] = buf_dma;   -- The base address of the buffer where
+ *                                         to put the data (this base address was
+ *                                         the buffer provided plus the offset)
+ * - Allocating memory from the PAYLOAD memory area, where the data coming from
+ *   the requesting party will be placed or data requested by the requesting party will
+ *   be retrieved when it is available.
+ */
+unsigned long
+phcd_iso_sitd_fill(phci_hcd * hcd,
+	struct ehci_sitd *sitd,
+	struct urb *urb, unsigned long packets)
+{
+	unsigned long length, offset, pipe;
+	unsigned long max_pkt;
+	dma_addr_t buff_dma;
+	struct isp1763_mem_addr *mem_addr;
+
+#ifdef COMMON_MEMORY
+	struct ehci_qh *qhead = NULL;
+#endif
+
+	iso_dbg(ISO_DBG_ENTRY, "phcd_iso_itd_fill entry\n");
+	/*
+	 * The value for both these variables are supplied by the one
+	 * who submitted the URB.
+	 */
+	length = urb->iso_frame_desc[packets].length;
+	offset = urb->iso_frame_desc[packets].offset;
+
+	/* Initialize the status and actual length of this packet */
+	urb->iso_frame_desc[packets].actual_length = 0;
+	urb->iso_frame_desc[packets].status = -EXDEV;
+
+	/* Buffer for this packet */
+	buff_dma = (u32) ((unsigned char *) urb->transfer_buffer + offset);
+
+	/* Memory for this packet */
+	mem_addr = &sitd->mem_addr;
+
+	pipe = urb->pipe;
+	max_pkt = usb_maxpacket(urb->dev, pipe, usb_pipeout(pipe));
+
+	max_pkt = max_pkt & 0x7FF;
+
+	if ((length < 0) || (max_pkt < length)) {
+		iso_dbg(ISO_DBG_ERR,
+			"[phcd_iso_itd_fill Error]: No available memory.\n");
+		return -ENOSPC;
+	}
+	sitd->buf_dma = buff_dma;
+
+
+#ifndef COMMON_MEMORY
+	/*
+	 * Allocate memory in the PAYLOAD memory region for the
+	 * data buffer for this SITD
+	 */
+	phci_hcd_mem_alloc(length, mem_addr, 0);
+	if (length && ((mem_addr->phy_addr == 0) || (mem_addr->virt_addr == 0))) {
+		mem_addr = 0;
+		iso_dbg(ISO_DBG_ERR,
+			"[phcd_iso_itd_fill Error]: No payload memory available\n");
+		return -ENOMEM;
+	}
+#else
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	qhead=urb->hcpriv;
+#else
+	qhead = urb->ep->hcpriv;
+#endif
+	if (qhead) {
+
+		mem_addr->phy_addr = qhead->memory_addr.phy_addr + offset;
+
+		mem_addr->virt_addr = qhead->memory_addr.phy_addr + offset;
+	} else {
+		iso_dbg(ISO_DBG_ERR,
+			"[phcd_iso_itd_fill Error]: No payload memory available\n");
+		return -ENOMEM;
+	}
+
+
+#endif
+	/* Length of this packet */
+	sitd->length = length;
+
+	/* Buffer address, one ptd per packet */
+	sitd->hw_bufp[0] = buff_dma;
+
+	iso_dbg(ISO_DBG_EXIT, "phcd_iso_sitd_fill exit\n");
+	return 0;
+}
+
+/*
+ * phcd_iso_itd_fill - Allocate memory from the PAYLOAD memory region
+ *
+ * phci_hcd *pHcd_st
+ *  - Main host controller driver structure
+ * struct ehci_itd *itd
+ *  - Isochronous Transfer Descriptor, contains elements as defined by the
+ *        EHCI standard plus a few more IC specific elements.
+ * struct urb *urb
+ *  - USB Request Block, contains information regarding the type and how much data
+ *    is requested to be transferred.
+ * unsigned long packets
+ *  - Total number of packets to completely transfer this ISO transfer request.
+ *
+ * API Description
+ * This is mainly responsible for:
+ * - Initialize the following elements of the ITS structure
+ *       > itd->length = length;        -- the size of the request
+ *       > itd->multi = multi;          -- the number of transactions for
+ *                                         this EP per micro frame
+ *       > itd->hw_bufp[0] = buf_dma;   -- The base address of the buffer where
+ *                                         to put the data (this base address was
+ *                                         the buffer provided plus the offset)
+ * - Allocating memory from the PAYLOAD memory area, where the data coming from
+ *   the requesting party will be placed or data requested by the requesting party will
+ *   be retrieved when it is available.
+ */
+unsigned long
+phcd_iso_itd_fill(phci_hcd * hcd,
+	struct ehci_itd *itd,
+	struct urb *urb,
+	unsigned long packets, unsigned char numofPkts)
+{
+	unsigned long length, offset, pipe;
+	unsigned long max_pkt, mult;
+	dma_addr_t buff_dma;
+	struct isp1763_mem_addr *mem_addr;
+#ifdef COMMON_MEMORY
+	struct ehci_qh *qhead = NULL;
+#endif
+	int i = 0;
+
+	iso_dbg(ISO_DBG_ENTRY, "phcd_iso_itd_fill entry\n");
+	for (i = 0; i < 8; i++){
+		itd->hw_transaction[i] = 0;
+	}
+	/*
+	 * The value for both these variables are supplied by the one
+	 * who submitted the URB.
+	 */
+	length = urb->iso_frame_desc[packets].length;
+	offset = urb->iso_frame_desc[packets].offset;
+
+	/* Initialize the status and actual length of this packet */
+	urb->iso_frame_desc[packets].actual_length = 0;
+	urb->iso_frame_desc[packets].status = -EXDEV;
+
+	/* Buffer for this packet */
+	buff_dma = cpu_to_le32((unsigned char *) urb->transfer_buffer + offset);
+
+	/* Memory for this packet */
+	mem_addr = &itd->mem_addr;
+
+	pipe = urb->pipe;
+	max_pkt = usb_maxpacket(urb->dev, pipe, usb_pipeout(pipe));
+
+	mult = 1 + ((max_pkt >> 11) & 0x3);
+	max_pkt = max_pkt & 0x7FF;
+	max_pkt *= mult;
+
+	if ((length < 0) || (max_pkt < length)) {
+		iso_dbg(ISO_DBG_ERR,
+			"[phcd_iso_itd_fill Error]: No available memory.\n");
+		return -ENOSPC;
+	}
+	itd->buf_dma = buff_dma;
+	for (i = packets + 1; i < numofPkts + packets; i++)
+		length += urb->iso_frame_desc[i].length;
+
+	/*
+	 * Allocate memory in the PAYLOAD memory region for the
+	 * data buffer for this ITD
+	 */
+#ifndef COMMON_MEMORY
+
+	phci_hcd_mem_alloc(length, mem_addr, 0);
+	if (length && ((mem_addr->phy_addr == 0) || (mem_addr->virt_addr == 0))) {
+		mem_addr = 0;
+		iso_dbg(ISO_DBG_ERR,
+			"[phcd_iso_itd_fill Error]: No payload memory available\n");
+		return -ENOMEM;
+	}
+#else
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+	qhead = urb->ep->hcpriv;
+#else
+	qhead=urb->hcpriv;
+#endif
+	if (qhead) {
+
+		mem_addr->phy_addr = qhead->memory_addr.phy_addr + offset;
+
+		mem_addr->virt_addr = qhead->memory_addr.phy_addr + offset;
+	} else {
+		iso_dbg(ISO_DBG_ERR,
+			"[phcd_iso_itd_fill Error]: No payload memory available\n");
+		return -ENOMEM;
+	}
+
+
+#endif
+	/* Length of this packet */
+	itd->length = length;
+
+	/* Number of transaction per uframe */
+	itd->multi = mult;
+
+	/* Buffer address, one ptd per packet */
+	itd->hw_bufp[0] = buff_dma;
+
+	iso_dbg(ISO_DBG_EXIT, "phcd_iso_itd_fill exit\n");
+	return 0;
+}				/* phcd_iso_itd_fill */
+
+/*
+ * phcd_iso_get_sitd_ptd_index - Allocate an ISO PTD from the ISO PTD map list
+ *
+ * phci_hcd *hcd
+ *      - Main host controller driver structure
+ * struct ehci_sitd *sitd
+ *  - Isochronous Transfer Descriptor, contains elements as defined by the
+ *        EHCI standard plus a few more  specific elements.
+ *
+ * API Description
+ * This is mainly responsible for:
+ * - Allocating an ISO PTD from the ISO PTD map list
+ * - Set the equivalent bit of the allocated PTD to active
+ *   in the bitmap so that this PTD will be included into
+ *   the periodic schedule
+ */
+void
+phcd_iso_get_sitd_ptd_index(phci_hcd * hcd, struct ehci_sitd *sitd)
+{
+	td_ptd_map_buff_t *ptd_map_buff;
+	unsigned long buff_type, max_ptds;
+	unsigned char sitd_index, bitmap;
+
+	/* Local variable initialization */
+	bitmap = 0x1;
+	buff_type = td_ptd_pipe_x_buff_type[TD_PTD_BUFF_TYPE_ISTL];
+	ptd_map_buff = (td_ptd_map_buff_t *) & (td_ptd_map_buff[buff_type]);
+	max_ptds = ptd_map_buff->max_ptds;
+	sitd->sitd_index = TD_PTD_INV_PTD_INDEX;
+
+	for (sitd_index = 0; sitd_index < max_ptds; sitd_index++) {
+		/*
+		 * ISO have 32 PTDs, the first thing to do is look for a free PTD.
+		 */
+		if (ptd_map_buff->map_list[sitd_index].state == TD_PTD_NEW) {
+			iso_dbg(ISO_DBG_INFO,
+				"[phcd_iso_get_itd_ptd_index] There's a free PTD No. %d\n",
+				sitd_index);
+			/*
+			 * Determine if this is a newly allocated SITD by checking the
+			 * itd_index, since it was set to TD_PTD_INV_PTD_INDEX during
+			 * initialization
+			 */
+			if (sitd->sitd_index == TD_PTD_INV_PTD_INDEX) {
+				sitd->sitd_index = sitd_index;
+			}
+
+			/* Once there is a free slot, indicate that it is already taken */
+			ptd_map_buff->map_list[sitd_index].datatoggle = 0;
+			ptd_map_buff->map_list[sitd_index].state =
+				TD_PTD_ACTIVE;
+			ptd_map_buff->map_list[sitd_index].qtd = NULL;
+
+			/* Put a connection to the SITD with the PTD maplist */
+			ptd_map_buff->map_list[sitd_index].sitd = sitd;
+			ptd_map_buff->map_list[sitd_index].itd = NULL;
+			ptd_map_buff->map_list[sitd_index].qh = NULL;
+
+			/* ptd_bitmap just holds the bit assigned to this PTD. */
+			ptd_map_buff->map_list[sitd_index].ptd_bitmap =
+				bitmap << sitd_index;
+
+			phci_hcd_fill_ptd_addresses(&ptd_map_buff->
+				map_list[sitd_index], sitd->sitd_index,
+				buff_type);
+
+			/*
+			 * Indicate that this SITD is the last in the list and update
+			 * the number of active PTDs
+			 */
+			ptd_map_buff->map_list[sitd_index].lasttd = 0;
+			ptd_map_buff->total_ptds++;
+
+
+			ptd_map_buff->active_ptd_bitmap |=
+				(bitmap << sitd_index);
+			ptd_map_buff->pending_ptd_bitmap |= (bitmap << sitd_index);	
+			break;
+		}		/* if(ptd_map_buff->map_list[sitd_index].state == TD_PTD_NEW) */
+	}			/* for(itd_index = 0; itd_index < max_ptds; itd_index++) */
+	return;
+}
+
+/*
+ * phcd_iso_get_itd_ptd_index - Allocate an ISO PTD from the ISO PTD map list
+ *
+ * phci_hcd *hcd
+ *      - Main host controller driver structure
+ * struct ehci_itd *itd
+ *  - Isochronous Transfer Descriptor, contains elements as defined by the
+ *        EHCI standard plus a few more IC specific elements.
+ *
+ * API Description
+ * This is mainly responsible for:
+ * - Allocating an ISO PTD from the ISO PTD map list
+ * - Set the equivalent bit of the allocated PTD to active
+ *   in the bitmap so that this PTD will be included into
+ *   the periodic schedule
+ */
+void
+phcd_iso_get_itd_ptd_index(phci_hcd * hcd, struct ehci_itd *itd)
+{
+	td_ptd_map_buff_t *ptd_map_buff;
+	unsigned long buff_type, max_ptds;
+	unsigned char itd_index, bitmap;
+
+	/* Local variable initialization */
+	bitmap = 0x1;
+	buff_type = td_ptd_pipe_x_buff_type[TD_PTD_BUFF_TYPE_ISTL];
+	ptd_map_buff = (td_ptd_map_buff_t *) & (td_ptd_map_buff[buff_type]);
+	max_ptds = ptd_map_buff->max_ptds;
+
+	itd->itd_index = TD_PTD_INV_PTD_INDEX;
+
+	for (itd_index = 0; itd_index < max_ptds; itd_index++) {
+		/*
+		 * ISO have 32 PTDs, the first thing to do is look for a free PTD.
+		 */
+		if (ptd_map_buff->map_list[itd_index].state == TD_PTD_NEW) {
+			/*
+			 * Determine if this is a newly allocated ITD by checking the
+			 * itd_index, since it was set to TD_PTD_INV_PTD_INDEX during
+			 * initialization
+			 */
+			if (itd->itd_index == TD_PTD_INV_PTD_INDEX) {
+				itd->itd_index = itd_index;
+			}
+
+			/* Once there is a free slot, indicate that it is already taken */
+			ptd_map_buff->map_list[itd_index].datatoggle = 0;
+			ptd_map_buff->map_list[itd_index].state = TD_PTD_ACTIVE;
+			ptd_map_buff->map_list[itd_index].qtd = NULL;
+
+			/* Put a connection to the ITD with the PTD maplist */
+			ptd_map_buff->map_list[itd_index].itd = itd;
+			ptd_map_buff->map_list[itd_index].qh = NULL;
+
+			/* ptd_bitmap just holds the bit assigned to this PTD. */
+			ptd_map_buff->map_list[itd_index].ptd_bitmap =
+				bitmap << itd_index;
+
+			phci_hcd_fill_ptd_addresses(&ptd_map_buff->
+				map_list[itd_index],
+				itd->itd_index, buff_type);
+
+			/*
+			 * Indicate that this ITD is the last in the list and update
+			 * the number of active PTDs
+			 */
+			ptd_map_buff->map_list[itd_index].lasttd = 0;
+			ptd_map_buff->total_ptds++;
+
+			ptd_map_buff->active_ptd_bitmap |=
+				(bitmap << itd_index);
+			ptd_map_buff->pending_ptd_bitmap |= (bitmap << itd_index);	
+			break;
+		}		/* if(ptd_map_buff->map_list[itd_index].state == TD_PTD_NEW) */
+	}			/* for(itd_index = 0; itd_index < max_ptds; itd_index++) */
+	return;
+}				/* phcd_iso_get_itd_ptd_index */
+
+/*
+ * phcd_iso_sitd_free_list - Free memory used by SITDs in SITD list
+ *
+ * phci_hcd *hcd
+ *      - Main host controller driver structure
+ * struct urb *urb
+ *  - USB Request Block, contains information regarding the type and how much data
+ *    is requested to be transferred.
+ * unsigned long status
+ *  - Variable provided by the calling routine that contain the status of the
+ *        SITD list.
+ *
+ * API Description
+ * This is mainly responsible for:
+ *  - Cleaning up memory used by each SITD in the SITD list
+ */
+void
+phcd_iso_sitd_free_list(phci_hcd * hcd, struct urb *urb, unsigned long status)
+{
+	td_ptd_map_buff_t *ptd_map_buff;
+	struct ehci_sitd *first_sitd, *next_sitd, *sitd;
+	td_ptd_map_t *td_ptd_map;
+
+	/* Local variable initialization */
+	ptd_map_buff = &(td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL]);
+	first_sitd = (struct ehci_sitd *) urb->hcpriv;
+	sitd = first_sitd;
+
+	/*
+	 * Check if there is only one SITD, if so immediately
+	 * go and clean it up.
+	 */
+	if (sitd->hw_next == EHCI_LIST_END) {
+		if (sitd->sitd_index != TD_PTD_INV_PTD_INDEX) {
+			td_ptd_map = &ptd_map_buff->map_list[sitd->sitd_index];
+			td_ptd_map->state = TD_PTD_NEW;
+		}
+
+		if (status != -ENOMEM) {
+			phci_hcd_mem_free(&sitd->mem_addr);
+		}
+
+		list_del(&sitd->sitd_list);
+		qha_free(qha_cache, sitd);
+
+		urb->hcpriv = 0;
+		return;
+	}
+	/* if(sitd->hw_next == EHCI_LIST_END) */
+	while (1) {
+		/* Get the SITD following the head SITD */
+		next_sitd = (struct ehci_sitd *) (sitd->hw_next);
+		if (next_sitd->hw_next == EHCI_LIST_END) {
+			/*
+			 * If the next SITD is the end of the list, check if space have
+			 * already been allocated in the PTD array.
+			 */
+			if (next_sitd->sitd_index != TD_PTD_INV_PTD_INDEX) {
+				/* Free up its allocation */
+				td_ptd_map =
+					&ptd_map_buff->map_list[next_sitd->
+					sitd_index];
+				td_ptd_map->state = TD_PTD_NEW;
+			}
+
+			/*
+			 * If the error is not about memory allocation problems, then
+			 * free up the memory used.
+			 */
+			if (status != -ENOMEM) {
+				iso_dbg(ISO_DBG_ERR,
+					"[phcd_iso_itd_free_list Error]: Memory not available\n");
+				phci_hcd_mem_free(&next_sitd->mem_addr);
+			}
+
+			/* Remove from the SITD list and free up space allocated for SITD structure */
+			list_del(&next_sitd->sitd_list);
+			qha_free(qha_cache, next_sitd);
+			break;
+		}
+
+		/* if(next_itd->hw_next == EHCI_LIST_END) */
+		/*
+		 * If SITD is not the end of the list, it only means that it already have everything allocated
+		 * and there is no need to check which procedure failed. So just free all resourcs immediately
+		 */
+		sitd->hw_next = next_sitd->hw_next;
+
+		td_ptd_map = &ptd_map_buff->map_list[next_sitd->sitd_index];
+		td_ptd_map->state = TD_PTD_NEW;
+		phci_hcd_mem_free(&next_sitd->mem_addr);
+		list_del(&next_sitd->sitd_list);
+		qha_free(qha_cache, next_sitd);
+	}			/*  while(1) */
+
+	/* Now work on the head SITD, it is the last one processed. */
+	if (first_sitd->sitd_index != TD_PTD_INV_PTD_INDEX) {
+		td_ptd_map = &ptd_map_buff->map_list[first_sitd->sitd_index];
+		td_ptd_map->state = TD_PTD_NEW;
+	}
+
+	if (status != -ENOMEM) {
+		iso_dbg(ISO_DBG_ERR,
+			"[phcd_iso_itd_free_list Error]: No memory\n");
+		phci_hcd_mem_free(&first_sitd->mem_addr);
+	}
+
+	list_del(&first_sitd->sitd_list);
+	qha_free(qha_cache, first_sitd);
+	urb->hcpriv = 0;
+	return;
+}
+
+/*
+ * phcd_iso_itd_free_list - Free memory used by ITDs in ITD list
+ *
+ * phci_hcd *hcd
+ *      - Main host controller driver structure
+ * struct urb *urb
+ *  - USB Request Block, contains information regarding the type and how much data
+ *    is requested to be transferred.
+ * unsigned long status
+ *  - Variable provided by the calling routine that contain the status of the
+ *        ITD list.
+ *
+ * API Description
+ * This is mainly responsible for:
+ *  - Cleaning up memory used by each ITD in the ITD list
+ */
+void
+phcd_iso_itd_free_list(phci_hcd * hcd, struct urb *urb, unsigned long status)
+{
+	td_ptd_map_buff_t *ptd_map_buff;
+	struct ehci_itd *first_itd, *next_itd, *itd;
+	td_ptd_map_t *td_ptd_map;
+
+	/* Local variable initialization */
+	ptd_map_buff = &(td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL]);
+	first_itd = (struct ehci_itd *) urb->hcpriv;
+	itd = first_itd;
+
+	/*
+	 * Check if there is only one ITD, if so immediately
+	 * go and clean it up.
+	 */
+	if (itd->hw_next == EHCI_LIST_END) {
+		if (itd->itd_index != TD_PTD_INV_PTD_INDEX) {
+			td_ptd_map = &ptd_map_buff->map_list[itd->itd_index];
+			td_ptd_map->state = TD_PTD_NEW;
+		}
+
+		if (status != -ENOMEM) {
+			phci_hcd_mem_free(&itd->mem_addr);
+		}
+
+		list_del(&itd->itd_list);
+		qha_free(qha_cache, itd);
+
+		urb->hcpriv = 0;
+		return;
+	}
+	/* if(itd->hw_next == EHCI_LIST_END) */
+	while (1) {
+		/* Get the ITD following the head ITD */
+		next_itd = (struct ehci_itd *) le32_to_cpu(itd->hw_next);
+		if (next_itd->hw_next == EHCI_LIST_END) {
+			/*
+			 * If the next ITD is the end of the list, check if space have
+			 * already been allocated in the PTD array.
+			 */
+			if (next_itd->itd_index != TD_PTD_INV_PTD_INDEX) {
+				/* Free up its allocation */
+				td_ptd_map =
+					&ptd_map_buff->map_list[next_itd->
+					itd_index];
+				td_ptd_map->state = TD_PTD_NEW;
+			}
+
+			/*
+			 * If the error is not about memory allocation problems, then
+			 * free up the memory used.
+			 */
+			if (status != -ENOMEM) {
+				iso_dbg(ISO_DBG_ERR,
+					"[phcd_iso_itd_free_list Error]: Memory not available\n");
+				phci_hcd_mem_free(&next_itd->mem_addr);
+			}
+
+			/* Remove from the ITD list and free up space allocated for ITD structure */
+			list_del(&next_itd->itd_list);
+			qha_free(qha_cache, next_itd);
+			break;
+		}
+
+		/* if(next_itd->hw_next == EHCI_LIST_END) */
+		/*
+		 * If ITD is not the end of the list, it only means that it already have everything allocated
+		 * and there is no need to check which procedure failed. So just free all resourcs immediately
+		 */
+		itd->hw_next = next_itd->hw_next;
+
+		td_ptd_map = &ptd_map_buff->map_list[next_itd->itd_index];
+		td_ptd_map->state = TD_PTD_NEW;
+		phci_hcd_mem_free(&next_itd->mem_addr);
+		list_del(&next_itd->itd_list);
+		qha_free(qha_cache, next_itd);
+	}			/*  while(1) */
+
+	/* Now work on the head ITD, it is the last one processed. */
+	if (first_itd->itd_index != TD_PTD_INV_PTD_INDEX) {
+		td_ptd_map = &ptd_map_buff->map_list[first_itd->itd_index];
+		td_ptd_map->state = TD_PTD_NEW;
+	}
+
+	if (status != -ENOMEM) {
+		iso_dbg(ISO_DBG_ERR,
+			"[phcd_iso_itd_free_list Error]: No memory\n");
+		phci_hcd_mem_free(&first_itd->mem_addr);
+	}
+
+	list_del(&first_itd->itd_list);
+	qha_free(qha_cache, first_itd);
+	urb->hcpriv = 0;
+	return;
+}				/* phcd_iso_itd_free_list */
+
+void
+phcd_clean_iso_qh(phci_hcd * hcd, struct ehci_qh *qh)
+{
+	unsigned int i = 0;
+	u16 skipmap=0;
+	struct ehci_sitd *sitd;
+	struct ehci_itd *itd;
+
+	iso_dbg(ISO_DBG_ERR, "phcd_clean_iso_qh \n");
+	if (!qh){
+		return;
+	}
+	skipmap = isp1763_reg_read16(hcd->dev, hcd->regs.isotdskipmap, skipmap);
+	skipmap |= qh->periodic_list.ptdlocation;
+	isp1763_reg_write16(hcd->dev, hcd->regs.isotdskipmap, skipmap);
+#ifdef COMMON_MEMORY
+	phci_hcd_mem_free(&qh->memory_addr);
+#endif
+	for (i = 0; i < 16 && qh->periodic_list.ptdlocation; i++) {
+		if (qh->periodic_list.ptdlocation & (0x1 << i)) {
+			printk("[phcd_clean_iso_qh] : %x \n",
+				qh->periodic_list.high_speed);
+
+			qh->periodic_list.ptdlocation &= ~(0x1 << i);
+
+			if (qh->periodic_list.high_speed == 0) {
+				if (td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+					map_list[i].sitd) {
+
+					printk("SITD found \n");
+					sitd = td_ptd_map_buff
+						[TD_PTD_BUFF_TYPE_ISTL].
+						map_list[i].sitd;
+#ifndef COMMON_MEMORY
+					phci_hcd_mem_free(&sitd->mem_addr);
+#endif
+					/*
+					if(sitd->urb)
+						urb=sitd->urb;
+					*/
+					sitd->urb = NULL;
+					td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+						map_list[i].state = TD_PTD_NEW;
+					td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+						map_list[i].sitd = NULL;
+					qha_free(qha_cache, sitd);
+				}
+			} else {
+				if (td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+					map_list[i].itd) {
+
+					printk("ITD found \n");
+					itd = td_ptd_map_buff
+						[TD_PTD_BUFF_TYPE_ISTL].
+						map_list[i].itd;
+#ifdef COMMON_MEMORY
+					phci_hcd_mem_free(&itd->mem_addr);
+#endif
+
+					/*
+					if(itd->urb)
+					urb=itd->urb;
+					*/
+					itd->urb = NULL;
+					td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+						map_list[i].state = TD_PTD_NEW;
+					td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+						map_list[i].itd = NULL;
+					qha_free(qha_cache, itd);
+				}
+			}
+
+		}
+	}
+
+
+}
+
+
+/*
+ * phcd_store_urb_pending - store requested URB into a queue
+ *
+ * phci_hcd *hcd
+ *      - Main host controller driver structure
+ * struct urb *urb
+ *  - USB Request Block, contains information regarding the type and how much data
+ *    is requested to be transferred.
+ * unsigned long *status
+ *  - Variable provided by the calling routine that will contain the status of the
+ *        phcd_submit_iso actions
+ *
+ * API Description
+ * This is mainly responsible for:
+ *  - Store URB into a queue
+ *  - If ther's enough free PTD slots , repairing the PTDs
+ */
+void phcd_clean_periodic_ep(void){
+	periodic_ep[0] = NULL;
+	periodic_ep[1] = NULL;
+}
+
+int
+phcd_clean_urb_pending(phci_hcd * hcd, struct urb *urb)
+{
+	unsigned int i = 0;
+	struct ehci_qh *qhead;
+	struct ehci_sitd *sitd;
+	struct ehci_itd *itd;
+	u16 skipmap=0;;
+
+	iso_dbg(ISO_DBG_ENTRY, "[phcd_clean_urb_pending] : Enter\n");
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	qhead=urb->hcpriv;
+	if (periodic_ep[0] == qhead->ep) {
+		periodic_ep[0] = NULL;
+
+	}
+
+	if (periodic_ep[1] == qhead->ep) {
+		periodic_ep[1] = NULL;
+	}
+#else	
+	qhead = urb->ep->hcpriv;
+	if (periodic_ep[0] == urb->ep) {
+		periodic_ep[0] = NULL;
+
+	}
+
+	if (periodic_ep[1] == urb->ep) {
+		periodic_ep[1] = NULL;
+	}
+#endif	
+	if (!qhead) {
+		return 0;
+	}
+	skipmap = isp1763_reg_read16(hcd->dev, hcd->regs.isotdskipmap, skipmap);
+	skipmap |= qhead->periodic_list.ptdlocation;
+	isp1763_reg_write16(hcd->dev, hcd->regs.isotdskipmap, skipmap);
+#ifdef COMMON_MEMORY
+	phci_hcd_mem_free(&qhead->memory_addr);
+#endif
+
+	for (i = 0; i < 16 && qhead->periodic_list.ptdlocation; i++) {
+
+		qhead->periodic_list.ptdlocation &= ~(0x1 << i);
+
+		if (qhead->periodic_list.ptdlocation & (0x1 << i)) {
+
+			printk("[phcd_clean_urb_pending] : %x \n",
+				qhead->periodic_list.high_speed);
+
+			if (qhead->periodic_list.high_speed == 0) {
+
+				if (td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+					map_list[i].sitd) {
+
+					sitd = td_ptd_map_buff
+						[TD_PTD_BUFF_TYPE_ISTL].
+						map_list[i].sitd;
+#ifndef COMMON_MEMORY
+					phci_hcd_mem_free(&sitd->mem_addr);
+#endif
+					td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+						map_list[i].state = TD_PTD_NEW;
+					td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+						map_list[i].sitd = NULL;
+					qha_free(qha_cache, sitd);
+				}
+			} else {
+
+				if (td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+					map_list[i].itd) {
+
+					itd = td_ptd_map_buff
+						[TD_PTD_BUFF_TYPE_ISTL].
+						map_list[i].itd;
+#ifdef COMMON_MEMORY
+					phci_hcd_mem_free(&itd->mem_addr);
+#endif
+					td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+						map_list[i].state = TD_PTD_NEW;
+					td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+						map_list[i].itd = NULL;
+					qha_free(qha_cache, itd);
+				}
+			}
+
+		}
+
+	}
+	INIT_LIST_HEAD(&qhead->periodic_list.sitd_itd_head);
+	iso_dbg(ISO_DBG_ENTRY, "[phcd_clean_urb_pending] : Exit\n");
+	return 0;
+}
+
+
+
+int
+phcd_store_urb_pending(phci_hcd * hcd, int index, struct urb *urb, int *status)
+{
+	unsigned int uiNumofPTDs = 0;
+	unsigned int uiNumofSlots = 0;
+	unsigned int uiMult = 0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+	iso_dbg(ISO_DBG_ENTRY, "[phcd_store_urb_pending] : Enter\n");
+	if (urb != NULL) {
+		if (periodic_ep[0] != urb->ep && periodic_ep[1] != urb->ep) {
+			if (periodic_ep[0] == NULL) {
+			//	printk("storing in 0 %x %x\n",urb,urb->pipe);
+				periodic_ep[0] = urb->ep;
+			} else if (periodic_ep[1] == NULL) {
+				printk("storing in 1\n");
+				periodic_ep[1] = urb->ep;
+				usb_hcd_link_urb_to_ep(&(hcd->usb_hcd), urb);
+				return -1;
+			} else {
+				iso_dbg(ISO_DBG_ERR,
+					"Support only 2 ISO endpoints simultaneously \n");
+				*status = -1;
+				return -1;
+			}
+		}
+		usb_hcd_link_urb_to_ep(&(hcd->usb_hcd), urb);
+		iso_dbg(ISO_DBG_DATA,
+			"[phcd_store_urb_pending] : Add an urb into gstUrb_pending array at index : %d\n",
+			giUrbCount);
+		giUrbCount++;
+	} else {
+
+		iso_dbg(ISO_DBG_ENTRY,
+			"[phcd_store_urb_pending] : getting urb from list \n");
+		if (index > 0 && index < 2) {
+			if (periodic_ep[index - 1]){
+				urb = container_of(periodic_ep[index - 1]->
+					urb_list.next, struct urb,
+					urb_list);
+			}
+		} else {
+			iso_dbg(ISO_DBG_ERR, " Unknown enpoints Error \n");
+			*status = -1;
+			return -1;
+		}
+
+	}
+
+
+	if ((urb != NULL && (urb->ep->urb_list.next == &urb->urb_list))){
+		iso_dbg(ISO_DBG_DATA,
+			"[phcd_store_urb_pending] : periodic_sched : %d\n",
+			hcd->periodic_sched);
+		iso_dbg(ISO_DBG_DATA,
+			"[phcd_store_urb_pending] : number_of_packets : %d\n",
+			urb->number_of_packets);
+		iso_dbg(ISO_DBG_DATA,
+			"[phcd_store_urb_pending] : Maximum PacketSize : %d\n",
+			usb_maxpacket(urb->dev,urb->pipe, usb_pipeout(urb->pipe)));
+		/*if enough free slots */
+		if (urb->dev->speed == USB_SPEED_FULL) {	/*for FULL SPEED */
+	//		if (hcd->periodic_sched < 
+		//		MAX_PERIODIC_SIZE - urb->number_of_packets) {
+			if(1){
+				if (phcd_submit_iso(hcd, 
+					#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+						struct usb_host_endpoint *ep,
+					#endif
+						urb,
+						( unsigned long *) &status) == 0) {
+					pehci_hcd_iso_schedule(hcd, urb);
+				} else{
+				//*status = 0;
+				}
+			}
+		} else if (urb->dev->speed == USB_SPEED_HIGH) {	/*for HIGH SPEED */
+			/*number of slots for 1 PTD */
+			uiNumofSlots = NUMMICROFRAME / urb->interval;
+			/*max packets size */
+			uiMult = usb_maxpacket(urb->dev, urb->pipe,
+					usb_pipeout(urb->pipe));
+			/*mult */
+			uiMult = 1 + ((uiMult >> 11) & 0x3);
+			/*number of PTDs need to schedule for this PTD */
+			uiNumofPTDs =
+				(urb->number_of_packets / uiMult) /
+				uiNumofSlots;
+			if ((urb->number_of_packets / uiMult) % uiNumofSlots != 0){
+				uiNumofPTDs += 1;
+			}
+
+			iso_dbg(ISO_DBG_DATA,
+				"[phcd_store_urb_pending] : interval : %d\n",
+				urb->interval);
+			iso_dbg(ISO_DBG_DATA,
+				"[phcd_store_urb_pending] : uiMult : %d\n",
+				uiMult);
+			iso_dbg(ISO_DBG_DATA,
+				"[phcd_store_urb_pending] : uiNumofPTDs : %d\n",
+				uiNumofPTDs);
+
+			if (hcd->periodic_sched <=
+				MAX_PERIODIC_SIZE - uiNumofPTDs) {
+
+				if (phcd_submit_iso(hcd,
+					#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+						struct usb_host_endpoint *ep,
+					#endif
+					urb, (unsigned long *) &status)== 0) {
+
+					pehci_hcd_iso_schedule(hcd, urb);
+				}
+			} else{
+				*status = 0;
+			}
+		}
+	} else{
+		iso_dbg(ISO_DBG_DATA,
+			"[phcd_store_urb_pending] : nextUrb is NULL\n");
+	}
+#endif
+	iso_dbg(ISO_DBG_ENTRY, "[phcd_store_urb_pending] : Exit\n");
+	return 0;
+}
+
+/*
+ * phcd_submit_iso - ISO transfer URB submit routine
+ *
+ * phci_hcd *hcd
+ *      - Main host controller driver structure
+ * struct urb *urb
+ *  - USB Request Block, contains information regarding the type and how much data
+ *    is requested to be transferred.
+ * unsigned long *status
+ *  - Variable provided by the calling routine that will contain the status of the
+ *        phcd_submit_iso actions
+ *
+ * API Description
+ * This is mainly responsible for:
+ *  - Allocating memory for the endpoint information structure (pQHead_st)
+ *  - Requesting for bus bandwidth from the USB core
+ *  - Allocating and initializing Payload and PTD memory
+ */
+unsigned long
+phcd_submit_iso(phci_hcd * hcd,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	struct usb_host_endpoint *ep,
+#else
+#endif
+		struct urb *urb, unsigned long *status)
+{
+	struct _periodic_list *periodic_list;
+	struct hcd_dev *dev;
+	struct ehci_qh *qhead;
+	struct ehci_itd *itd, *prev_itd;
+	struct ehci_sitd *sitd, *prev_sitd;
+	struct list_head *sitd_itd_list;
+	unsigned long ep_in, max_pkt, mult;
+	unsigned long bus_time, high_speed, start_frame;
+	unsigned long temp;
+	unsigned long packets;
+	/*for high speed device */
+	unsigned int iMicroIndex = 0;
+	unsigned int iNumofSlots = 0;
+	unsigned int iNumofPTDs = 0;
+	unsigned int iPTDIndex = 0;
+	unsigned int iNumofPks = 0;
+	int iPG = 0;
+	dma_addr_t buff_dma;
+	unsigned long length, offset;
+	int i = 0;
+
+	iso_dbg(ISO_DBG_ENTRY, "phcd_submit_iso Entry\n");
+
+	*status = 0;
+	/* Local variable initialization */
+	high_speed = 0;
+	periodic_list = &hcd->periodic_list[0];
+	dev = (struct hcd_dev *) urb->hcpriv;
+	urb->hcpriv = (void *) 0;
+	prev_itd = (struct ehci_itd *) 0;
+	itd = (struct ehci_itd *) 0;
+	prev_sitd = (struct ehci_sitd *) 0;
+	sitd = (struct ehci_sitd *) 0;
+	start_frame = 0;
+
+	ep_in = usb_pipein(urb->pipe);
+
+	/*
+	 * Take the endpoint, if there is still no memory allocated
+	 * for it allocate some and indicate this is for ISO.
+	 */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	qhead = ep->hcpriv;
+#else
+	qhead = urb->ep->hcpriv;
+#endif
+	if (!qhead) {
+
+		qhead = phci_hcd_qh_alloc(hcd);
+		if (qhead == 0) {
+			iso_dbg(ISO_DBG_ERR,
+				"[phcd_submit_iso Error]: Not enough memory\n");
+			return -ENOMEM;
+		}
+
+		qhead->type = TD_PTD_BUFF_TYPE_ISTL;
+		INIT_LIST_HEAD(&qhead->periodic_list.sitd_itd_head);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+		qhead->ep=ep;
+		ep->hcpriv = qhead;
+		urb->hcpriv=qhead;
+#else
+		urb->ep->hcpriv = qhead;
+#endif
+	}
+
+		urb->hcpriv=qhead;
+
+	/* if(!qhead) */
+	/*
+	 * Get the number of additional packets that the endpoint can support during a
+	 * single microframe.
+	 */
+	max_pkt = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+
+	/*
+	 * We need to add 1 since our Multi starts with 1 instead of the USB specs defined
+	 * zero (0).
+	 */
+	mult = 1 + ((max_pkt >> 11) & 0x3);
+
+	/* This is the actual length per for the whole transaction */
+	max_pkt *= mult;
+
+	/* Check bandwidth */
+	bus_time = 0;
+
+	if (urb->dev->speed == USB_SPEED_FULL) {
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+		if (urb->bandwidth == 0) {
+			bus_time = usb_check_bandwidth(urb->dev, urb);
+			if (bus_time < 0) {
+				usb_dec_dev_use(urb->dev);
+				*status = bus_time;
+				return *status;
+			}
+		}
+#else
+#endif
+	} else {			/*HIGH SPEED */
+
+		high_speed = 1;
+
+		/*
+		 * Calculate bustime as dictated by the USB Specs Section 5.11.3
+		 * for high speed ISO
+		 */
+		bus_time = 633232L;
+		bus_time +=
+			(2083L * ((3167L + BitTime(max_pkt) * 1000L) / 1000L));
+		bus_time = bus_time / 1000L;
+		bus_time += BW_HOST_DELAY;
+		bus_time = NS_TO_US(bus_time);
+	}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	usb_claim_bandwidth(urb->dev, urb, bus_time, 1);
+#else
+#endif
+
+	qhead->periodic_list.ptdlocation = 0;
+	/* Initialize the start split (ssplit) and complete split (csplit) variables of qhead */
+	if (phcd_iso_scheduling_info(hcd, qhead, max_pkt, high_speed, ep_in) <
+		0) {
+
+		iso_dbg(ISO_DBG_ERR,
+			"[phcd_submit_iso Error]: No space available\n");
+		return -ENOSPC;
+	}
+
+	if (urb->dev->speed == USB_SPEED_HIGH) {
+		iNumofSlots = NUMMICROFRAME / urb->interval;
+		/*number of PTDs need to schedule for this PTD */
+		iNumofPTDs = (urb->number_of_packets / mult) / iNumofSlots;
+		if ((urb->number_of_packets / mult) % iNumofSlots != 0){	
+			/*get remainder */
+			iNumofPTDs += 1;
+		}
+	}
+	if (urb->iso_frame_desc[0].offset != 0) {
+		*status = -EINVAL;
+		iso_dbg(ISO_DBG_ERR,
+			"[phcd_submit_iso Error]: Invalid value\n");
+		return *status;
+	}
+	if (1) {
+		/* Calculate the current frame number */
+		if (0){
+			if (urb->transfer_flags & URB_ISO_ASAP){
+				start_frame =
+					isp1763_reg_read16(hcd->dev,
+						hcd->regs.frameindex,
+						start_frame);
+			} else {
+				start_frame = urb->start_frame;
+			}
+		}
+
+		start_frame =
+			isp1763_reg_read16(hcd->dev, hcd->regs.frameindex,
+				start_frame);
+
+		/* The only valid bits of the frame index is the lower 14 bits. */
+
+		/*
+		 * Remove the count for the micro frame (uSOF) and just leave the
+		 * count for the frame (SOF). Since 1 SOF is equal to 8 uSOF then
+		 * shift right by three is like dividing it by 8 (each shift is divide by two)
+		 */
+		start_frame >>= 3;
+		if (urb->dev->speed != USB_SPEED_HIGH){
+			start_frame += 1;
+		}else{
+			start_frame += 2;
+		}
+		start_frame = start_frame & PTD_FRAME_MASK;
+		temp = start_frame;
+		if (urb->dev->speed != USB_SPEED_HIGH) {
+			qhead->next_uframe =
+				start_frame + urb->number_of_packets;
+		} else {
+			qhead->next_uframe = start_frame + iNumofPTDs;
+		}
+		qhead->next_uframe %= PTD_FRAME_MASK;
+		iso_dbg(ISO_DBG_DATA, "[phcd_submit_iso]: startframe = %ld\n",
+			start_frame);
+	} else {
+		/*
+		 * The periodic frame list size is only 32 elements deep, so we need
+		 * the frame index to be less than or equal to 32 (actually 31 if we
+		 * start from 0)
+		 */
+		start_frame = (qhead->next_uframe) % PTD_FRAME_MASK;
+		if (urb->dev->speed != USB_SPEED_HIGH){
+			qhead->next_uframe =
+				start_frame + urb->number_of_packets;
+				iNumofPTDs=urb->number_of_packets;
+		} else {
+			qhead->next_uframe = start_frame + iNumofPTDs;
+		}
+
+		qhead->next_uframe %= PTD_FRAME_MASK;
+	}
+
+
+	iso_dbg(ISO_DBG_DATA, "[phcd_submit_iso]: Start frame index: %ld\n",
+		start_frame);
+	iso_dbg(ISO_DBG_DATA, "[phcd_submit_iso]: Max packet: %d\n",
+		(int) max_pkt);
+
+#ifdef COMMON_MEMORY
+	if(urb->number_of_packets>8 && urb->dev->speed!=USB_SPEED_HIGH)
+		phci_hcd_mem_alloc(8*max_pkt, &qhead->memory_addr, 0);
+	else
+	phci_hcd_mem_alloc(urb->transfer_buffer_length, &qhead->memory_addr, 0);
+	if (urb->transfer_buffer_length && ((qhead->memory_addr.phy_addr == 0)
+		|| (qhead->memory_addr.virt_addr ==0))) {
+		iso_dbg(ISO_DBG_ERR,
+			"[URB FILL MEMORY Error]: No payload memory available\n");
+		return -ENOMEM;
+	}
+#endif
+
+	if (urb->dev->speed != USB_SPEED_HIGH) {
+		iNumofPks = urb->number_of_packets;
+		qhead->totalptds=urb->number_of_packets;
+		qhead->actualptds=0;
+
+		/* Make as many tds as number of packets */
+		for (packets = 0; packets < urb->number_of_packets; packets++) {
+			/*
+			 * Allocate memory for the SITD data structure and initialize it.
+			 *
+			 * This data structure follows the format of the SITD
+			 * structure defined by the EHCI standard on the top part
+			 * but also contains specific elements in the bottom
+			 * part
+			 */
+			sitd = kmalloc(sizeof(*sitd), GFP_ATOMIC);
+			if (!sitd) {
+				*status = -ENOMEM;
+				if (((int)(qhead->next_uframe -
+					urb->number_of_packets)) < 0){
+					/*plus max PTDs*/
+					qhead->next_uframe = qhead->next_uframe + PTD_PERIODIC_SIZE;	
+					
+				}
+				qhead->next_uframe -= urb->number_of_packets;
+
+				/* Handle SITD list cleanup */
+				if (urb->hcpriv) {
+					phcd_iso_sitd_free_list(hcd, urb, 
+						*status);
+				}
+				iso_dbg(ISO_DBG_ERR,
+					"[phcd_submit_iso Error]: No memory available\n");
+				return *status;
+			}
+
+			memset(sitd, 0, sizeof(struct ehci_sitd));
+
+			INIT_LIST_HEAD(&sitd->sitd_list);
+
+			sitd->sitd_dma = (u32) (sitd);
+			sitd->urb = urb;
+
+			/*
+			 * Indicate that this SITD is the last in the list.
+			 *
+			 * Also set the itd_index to TD_PTD_INV_PTD_INDEX
+			 * (0xFFFFFFFF). This would indicate when we allocate
+			 * a PTD that this SITD did not have a PTD allocated
+			 * before.
+			 */
+
+			sitd->hw_next = EHCI_LIST_END;
+			sitd->sitd_index = TD_PTD_INV_PTD_INDEX;
+
+			/* This SITD will go into this frame */
+			sitd->framenumber = start_frame + packets;
+			sitd->start_frame = temp + packets;
+
+			/* Number of the packet */
+			sitd->index = packets;
+
+			sitd->framenumber = sitd->framenumber & PTD_FRAME_MASK;
+			sitd->ssplit = qhead->ssplit;
+			sitd->csplit = qhead->csplit;
+
+			/* Initialize the following elements of the ITS structure
+			 *      > sitd->length = length;                 -- the size of the request
+			 *      > sitd->multi = multi;                   -- the number of transactions for
+			 *                                         this EP per micro frame
+			 *      > sitd->hw_bufp[0] = buf_dma;    -- The base address of the buffer where
+			 *                                         to put the data (this base address was
+			 *                                         the buffer provided plus the offset)
+			 * And then, allocating memory from the PAYLOAD memory area, where the data
+			 * coming from the requesting party will be placed or data requested by the
+			 * requesting party will be retrieved when it is available.
+			 */
+			*status = phcd_iso_sitd_fill(hcd, sitd, urb, packets);
+
+			if (*status != 0) {
+				if (((int)(qhead->next_uframe - 
+					urb->number_of_packets)) < 0){
+					/*plus max PTDs*/
+					qhead->next_uframe = qhead->next_uframe + 
+						PTD_PERIODIC_SIZE;	
+				}
+				qhead->next_uframe -= urb->number_of_packets;
+
+				/* Handle SITD list cleanup */
+				if (urb->hcpriv) {
+					phcd_iso_sitd_free_list(hcd, urb,
+						*status);
+				}
+				iso_dbg(ISO_DBG_ERR,
+					"[phcd_submit_iso Error]: Error in filling up SITD\n");
+				return *status;
+			}
+
+			/*
+			 * If this SITD is not the head/root SITD, link this SITD to the SITD
+			 * that came before it.
+			 */
+			if (prev_sitd) {
+				prev_sitd->hw_next = (u32) (sitd);
+			}
+
+			prev_sitd = sitd;
+
+			if(packets<8){  //bcs of memory constraint , we use only first 8 PTDs if number_of_packets is more than 8.
+			/*
+			 * Allocate an ISO PTD from the ISO PTD map list and
+			 * set the equivalent bit of the allocated PTD to active
+			 * in the bitmap so that this PTD will be included into
+			 * the periodic schedule
+			 */
+			phcd_iso_get_sitd_ptd_index(hcd, sitd);
+			iso_dbg(ISO_DBG_DATA,
+				"[phcd_submit_iso]: SITD index %d\n",
+				sitd->sitd_index);
+
+			/*if we dont have any space left */
+			if (sitd->sitd_index == TD_PTD_INV_PTD_INDEX) {
+				*status = -ENOSPC;
+				if (((int) (qhead->next_uframe -
+					urb->number_of_packets)) < 0){
+					/*plus max PTDs*/
+					qhead->next_uframe = qhead->next_uframe + PTD_PERIODIC_SIZE;	
+				}
+				qhead->next_uframe -= urb->number_of_packets;
+
+				/* Handle SITD list cleanup */
+				if (urb->hcpriv) {
+					phcd_iso_sitd_free_list(hcd, urb,
+						*status);
+				}
+				return *status;
+			}
+					qhead->actualptds++;
+			}
+			/* Insert this td into the periodic list */
+
+			sitd_itd_list = &qhead->periodic_list.sitd_itd_head;
+			list_add_tail(&sitd->sitd_list, sitd_itd_list);
+			qhead->periodic_list.high_speed = 0;
+			if(sitd->sitd_index!=TD_PTD_INV_PTD_INDEX)
+			qhead->periodic_list.ptdlocation |=
+				0x1 << sitd->sitd_index;
+			/* Inidcate that a new SITD have been scheduled */
+			hcd->periodic_sched++;
+
+			/* Determine if there are any SITD scheduled before this one. */
+			if (urb->hcpriv == 0){
+				urb->hcpriv = sitd;
+			}
+		}	/* for(packets = 0; packets... */
+	} else if (urb->dev->speed == USB_SPEED_HIGH) {	
+		iNumofPks = iNumofPTDs;
+
+		packets = 0;
+		iPTDIndex = 0;
+		while (packets < urb->number_of_packets) {
+			iNumofSlots = NUMMICROFRAME / urb->interval;
+			/*
+			 * Allocate memory for the ITD data structure and initialize it.
+			 *
+			 * This data structure follows the format of the ITD
+			 * structure defined by the EHCI standard on the top part
+			 * but also contains specific elements in the bottom
+			 * part
+			 */
+			itd = kmalloc(sizeof(*itd), GFP_ATOMIC);
+			if (!itd) {
+				*status = -ENOMEM;
+				if(((int) (qhead->next_uframe - iNumofPTDs))<0){
+					/*plus max PTDs*/
+					qhead->next_uframe = qhead->next_uframe + 
+						PTD_PERIODIC_SIZE;	
+				}
+				qhead->next_uframe -= iNumofPTDs;
+
+				/* Handle ITD list cleanup */
+				if (urb->hcpriv) {
+					phcd_iso_itd_free_list(hcd, urb,
+							       *status);
+				}
+				iso_dbg(ISO_DBG_ERR,
+					"[phcd_submit_iso Error]: No memory available\n");
+				return *status;
+			}
+			memset(itd, 0, sizeof(struct ehci_itd));
+
+			INIT_LIST_HEAD(&itd->itd_list);
+
+			itd->itd_dma = (u32) (itd);
+			itd->urb = urb;
+			/*
+			 * Indicate that this ITD is the last in the list.
+			 *
+			 * Also set the itd_index to TD_PTD_INV_PTD_INDEX
+			 * (0xFFFFFFFF). This would indicate when we allocate
+			 * a PTD that this SITD did not have a PTD allocated
+			 * before.
+			 */
+
+			itd->hw_next = EHCI_LIST_END;
+			itd->itd_index = TD_PTD_INV_PTD_INDEX;
+			/* This ITD will go into this frame */
+			itd->framenumber = start_frame + iPTDIndex;
+			/* Number of the packet */
+			itd->index = packets;
+
+			itd->framenumber = itd->framenumber & 0x1F;
+
+			itd->ssplit = qhead->ssplit;
+			itd->csplit = qhead->csplit;
+
+			/*caculate the number of packets for this itd */
+			itd->num_of_pkts = iNumofSlots * mult;
+			/*for the case , urb number_of_packets is less than (number of slot*mult*x times) */
+			if (itd->num_of_pkts >= urb->number_of_packets)
+			{
+				itd->num_of_pkts = urb->number_of_packets;
+			}
+			else {
+				if (itd->num_of_pkts >
+					urb->number_of_packets - packets){
+					itd->num_of_pkts =
+						urb->number_of_packets -
+						packets;
+				}
+			}
+
+			/* Initialize the following elements of the ITS structure
+			 *      > itd->length = length;                 -- the size of the request
+			 *      > itd->multi = multi;                   -- the number of transactions for
+			 *                                         this EP per micro frame
+			 *      > itd->hw_bufp[0] = buf_dma;    -- The base address of the buffer where
+			 *                                         to put the data (this base address was
+			 *                                         the buffer provided plus the offset)
+			 * And then, allocating memory from the PAYLOAD memory area, where the data
+			 * coming from the requesting party will be placed or data requested by the
+			 * requesting party will be retrieved when it is available.
+			 */
+			iso_dbg(ISO_DBG_DATA,
+				"[phcd_submit_iso] packets index = %ld itd->num_of_pkts = %d\n",
+				packets, itd->num_of_pkts);
+			*status =
+				phcd_iso_itd_fill(hcd, itd, urb, packets,
+						itd->num_of_pkts);
+			if (*status != 0) {
+				if (((int) (qhead->next_uframe - iNumofPTDs)) <
+					0) {
+					qhead->next_uframe = qhead->next_uframe + PTD_PERIODIC_SIZE;	/*plus max PTDs*/
+				}
+				qhead->next_uframe -= iNumofPTDs;
+
+				/* Handle SITD list cleanup */
+				if (urb->hcpriv) {
+					phcd_iso_itd_free_list(hcd, urb,
+						*status);
+				}
+				iso_dbg(ISO_DBG_ERR,
+					"[phcd_submit_iso Error]: Error in filling up ITD\n");
+				return *status;
+			}
+
+			iPG = 0;
+			iMicroIndex = 0;
+			while (iNumofSlots > 0) {
+				offset = urb->iso_frame_desc[packets].offset;
+				/* Buffer for this packet */
+				buff_dma =
+					(u32) ((unsigned char *) urb->
+						transfer_buffer + offset);
+
+				/*for the case mult is 2 or 3 */
+				length = 0;
+				for (i = packets; i < packets + mult; i++) {
+					length += urb->iso_frame_desc[i].length;
+				}
+				itd->hw_transaction[iMicroIndex] =
+					EHCI_ISOC_ACTIVE | (length & 
+					EHCI_ITD_TRANLENGTH)
+					<< 16 | iPG << 12 | buff_dma;
+					
+				if (itd->hw_bufp[iPG] != buff_dma){
+					itd->hw_bufp[++iPG] = buff_dma;
+				}
+
+				iso_dbg(ISO_DBG_DATA,
+					"[%s] offset : %ld buff_dma : 0x%08x length : %ld\n",
+					__FUNCTION__, offset,
+					(unsigned int) buff_dma, length);
+
+				itd->ssplit |= 1 << iMicroIndex;
+				packets++;
+				iMicroIndex += urb->interval;
+				iNumofSlots--;
+
+				/*last packets or last slot */
+				if (packets == urb->number_of_packets
+					|| iNumofSlots == 0) {
+
+					itd->hw_transaction[iMicroIndex] |=
+						EHCI_ITD_IOC;
+
+					break;
+					
+				}
+			}
+
+			/*
+			 * If this SITD is not the head/root SITD, link this SITD to the SITD
+			 * that came before it.
+			 */
+			if (prev_itd) {
+				prev_itd->hw_next = (u32) (itd);
+			}
+
+			prev_itd = itd;
+
+			/*
+			 * Allocate an ISO PTD from the ISO PTD map list and
+			 * set the equivalent bit of the allocated PTD to active
+			 * in the bitmap so that this PTD will be included into
+			 * the periodic schedule
+			 */
+
+
+			iso_dbg(ISO_DBG_DATA,
+				"[phcd_submit_iso]: ITD index %d\n",
+				itd->framenumber);
+			phcd_iso_get_itd_ptd_index(hcd, itd);
+			iso_dbg(ISO_DBG_DATA,
+				"[phcd_submit_iso]: ITD index %d\n",
+				itd->itd_index);
+
+			/*if we dont have any space left */
+			if (itd->itd_index == TD_PTD_INV_PTD_INDEX) {
+				*status = -ENOSPC;
+				if (((int) (qhead->next_uframe - iNumofPTDs)) <
+					0){
+					/*plus max PTDs*/
+					qhead->next_uframe = qhead->next_uframe + PTD_PERIODIC_SIZE;	
+				}
+				qhead->next_uframe -= iNumofPTDs;
+
+				/* Handle SITD list cleanup */
+				if (urb->hcpriv) {
+					phcd_iso_itd_free_list(hcd, urb,
+							       *status);
+				}
+				return *status;
+			}
+
+			sitd_itd_list = &qhead->periodic_list.sitd_itd_head;
+			list_add_tail(&itd->itd_list, sitd_itd_list);
+			qhead->periodic_list.high_speed = 1;
+			qhead->periodic_list.ptdlocation |=
+				0x1 << itd->itd_index;
+
+			/* Inidcate that a new SITD have been scheduled */
+			hcd->periodic_sched++;
+
+			/* Determine if there are any ITD scheduled before this one. */
+			if (urb->hcpriv == 0){
+				urb->hcpriv = itd;
+			}
+			iPTDIndex++;
+
+		}		/*end of while */
+	}
+
+	/*end of HIGH SPEED */
+	/* Last td of current transaction */
+	if (high_speed == 0){
+		sitd->hw_next = EHCI_LIST_END;
+	}
+	urb->error_count = 0;
+	return *status;
+}				/* phcd_submit_iso */
+#endif /* CONFIG_ISO_SUPPORT */
diff --git a/drivers/usb/host/pehci/host/mem.c b/drivers/usb/host/pehci/host/mem.c
new file mode 100644
index 0000000..dbf28a9
--- /dev/null
+++ b/drivers/usb/host/pehci/host/mem.c
@@ -0,0 +1,355 @@
+/* 
+* Copyright (C) ST-Ericsson AP Pte Ltd 2010 
+*
+* ISP1763 Linux OTG Controller driver : host
+* 
+* 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 Street, Fifth Floor, Boston, MA  02110-1301, USA. 
+* 
+* This is a host controller driver file. Memory initialization, allocation, and 
+* deallocation are handled here.
+* 
+* Author : wired support <wired.support@stericsson.com>
+*
+*/
+
+#ifdef CONFIG_ISO_SUPPORT
+
+/*memory utilization fuctions*/
+void
+phci_hcd_mem_init(void)
+{
+	int i = 0;
+	u32 start_addr = 0x1000;
+	struct isp1763_mem_addr *memaddr;
+	for (i = 0; i < BLK_TOTAL; i++) {
+		memaddr = &memalloc[i];
+		memset(memaddr, 0, sizeof *memaddr);
+	}
+	/*initialize block of 128bytes */
+	for (i = 0; i < BLK_128_; i++) {
+		memaddr = &memalloc[i];
+		memaddr->blk_num = i;
+		memaddr->used = 0;
+		memaddr->blk_size = BLK_SIZE_128;
+		memaddr->phy_addr = start_addr;
+		start_addr += BLK_SIZE_128;
+	}
+	/*initialize block of 256bytes */
+	for (i = BLK_128_; i < BLK_256_; i++) {
+		memaddr = &memalloc[i];
+		memaddr->blk_num = i;
+		memaddr->used = 0;
+		memaddr->blk_size = BLK_SIZE_256;
+		memaddr->phy_addr = start_addr;
+		start_addr += BLK_SIZE_256;
+	}
+	/*initialize block of 1024bytes */
+	for (i = BLK_128_ + BLK_256_; i < (BLK_128_ + BLK_256_ + BLK_1024_);
+		i++) {
+		memaddr = &memalloc[i];
+		memaddr->blk_num = i;
+		memaddr->used = 0;
+		memaddr->blk_size = BLK_SIZE_1024;
+		memaddr->phy_addr = start_addr;
+		start_addr += BLK_SIZE_1024;
+	}
+
+	/*initialize block of  2kbytes */
+	for (i = (BLK_128_ + BLK_256_ + BLK_1024_);
+		i < (BLK_128_ + BLK_256_ + BLK_1024_ + BLK_2048_); i++) {
+		memaddr = &memalloc[i];
+		memaddr->blk_num = i;
+		memaddr->used = 0;
+		memaddr->blk_size = BLK_SIZE_2048;
+		memaddr->phy_addr = start_addr;
+		start_addr += BLK_SIZE_2048;
+	}
+	/* initialize block of 4kbytes */
+	for (i = (BLK_128_ + BLK_256_ + BLK_1024_ + BLK_2048_);
+		i < (BLK_128_ + BLK_256_ + BLK_1024_ + BLK_2048_ + BLK_4096_); 
+		i++){
+		memaddr = &memalloc[i];
+		memaddr->blk_num = i;
+		memaddr->used = 0;
+		memaddr->blk_size = BLK_SIZE_4096;
+		memaddr->phy_addr = start_addr;
+		start_addr += BLK_SIZE_4096;
+	}
+	/* initialize block of 8kbytes */
+	for (i = (BLK_128_ + BLK_256_ + BLK_1024_ + BLK_2048_); i <
+		(BLK_128_ + BLK_256_ + BLK_1024_ + BLK_2048_ + BLK_4096_ +
+		BLK_8196_); i++) {
+		memaddr = &memalloc[i];
+		memaddr->blk_num = i;
+		memaddr->used = 0;
+		memaddr->blk_size = BLK_SIZE_8192;
+		memaddr->phy_addr = start_addr;
+		start_addr += BLK_SIZE_8192;
+	}
+
+}
+
+
+/*free memory*/
+static void
+phci_hcd_mem_free(struct isp1763_mem_addr *memptr)
+{
+	/*block number to be freed */
+	int block = memptr->blk_num;
+
+	if (block < BLK_TOTAL){
+		if ((memptr->blk_size) && (memalloc[block].used != 0)) {
+			memalloc[block].used = 0;
+			memptr->used = 0;
+		}
+	}
+}
+
+
+/*allocate memory*/
+static void
+phci_hcd_mem_alloc(u32 size, struct isp1763_mem_addr *memptr, u32 flag)
+{
+	u32 blk_size = size;
+	u16 i;
+	u32 nextblk1 = 0, nextblk4 = 0;
+	u32 start = 0, end = 0;
+	struct isp1763_mem_addr *memaddr = 0;
+
+	memset(memptr, 0, sizeof *memptr);
+
+	pehci_print("phci_hcd_mem_alloc(size = %d)\n", size);
+
+	if (blk_size == 0) {
+		memptr->phy_addr = 0;
+		memptr->virt_addr = 0;
+		memptr->blk_size = 0;
+		memptr->num_alloc = 0;
+		memptr->blk_num = 0;
+		return;
+	}
+
+	for (i = 0; i < BLK_TOTAL; i++) {
+		memaddr = &memalloc[i];
+		if (!memaddr->used && size <= memaddr->blk_size) {
+			memaddr->used = 1;
+			memptr->used = 1;
+			memptr->blk_num = i;
+			memptr->blk_size = memaddr->blk_size;
+			memptr->phy_addr = memaddr->phy_addr;
+			memptr->virt_addr = memptr->phy_addr;
+			return;
+		}
+	}
+
+	return;
+	/*end of the 1k blocks */
+	nextblk1 = BLK_256_ + BLK_1024_;
+	/*end of the 4k blocks */
+	nextblk4 = nextblk1 + BLK_4096_;
+
+	if (blk_size <= BLK_SIZE_128) {
+		blk_size = BLK_SIZE_128;
+		start = 0;
+		end = BLK_256_;
+	}
+	if (blk_size <= BLK_SIZE_256) {
+		blk_size = BLK_SIZE_256;
+		start = 0;
+		end = BLK_256_;
+	} else if (blk_size <= BLK_SIZE_1024) {
+		blk_size = BLK_SIZE_1024;
+		start = BLK_256_;
+		end = start + BLK_1024_;
+	} else if (blk_size > BLK_SIZE_1024) {
+		blk_size = BLK_SIZE_4096;
+		start = BLK_256_ + BLK_1024_;
+		end = start + BLK_4096_;
+	}
+
+	for (i = start; i < end; i++) {
+		memaddr = &memalloc[i];
+		if (!memaddr->used) {
+			memaddr->used = 1;
+			memptr->blk_num = i;
+			memptr->used = 1;
+			memptr->blk_size = blk_size;
+			memptr->phy_addr = memaddr->phy_addr;
+			memptr->virt_addr = memptr->phy_addr;
+			return;
+		}
+	}
+
+	/*look for in the next block if memory is free */
+	/*start from the first place of the next block */
+	start = end;
+
+	/*for 1k and 256 size request only 4k can be returned */
+	end = nextblk4;
+
+	for (i = start; i < end; i++) {
+		memaddr = &memalloc[i];
+		if (!memaddr->used) {
+			memaddr->used = 1;
+			memptr->used = 1;
+			memptr->blk_num = i;
+			memptr->blk_size = blk_size;
+			memptr->phy_addr = memaddr->phy_addr;
+			memptr->virt_addr = memptr->phy_addr;
+			return;
+		}
+	}
+
+}
+
+#else
+
+void
+phci_hcd_mem_init(void)
+{
+	int i = 0;
+	u32 start_addr = 0x1000;
+	struct isp1763_mem_addr *memaddr;
+	for (i = 0; i < BLK_TOTAL; i++) {
+		memaddr = &memalloc[i];
+		memset(memaddr, 0, sizeof *memaddr);
+	}
+
+	/*initialize block of 256bytes */
+	for (i = 0; i < BLK_256_; i++) {
+		memaddr = &memalloc[i];
+		memaddr->blk_num = i;
+		memaddr->used = 0;
+		memaddr->blk_size = BLK_SIZE_256;
+		memaddr->phy_addr = start_addr;
+		start_addr += BLK_SIZE_256;
+	}
+	/*initialize block of 1024bytes */
+	for (i = BLK_256_; i < (BLK_256_ + BLK_1024_); i++) {
+		memaddr = &memalloc[i];
+		memaddr->blk_num = i;
+		memaddr->used = 0;
+		memaddr->blk_size = BLK_SIZE_1024;
+		memaddr->phy_addr = start_addr;
+		start_addr += BLK_SIZE_1024;
+	}
+
+	/*initialize block of  4kbytes */
+	for (i = (BLK_256_ + BLK_1024_); i < (BLK_256_ + BLK_1024_ + BLK_4096_);
+		i++) {
+		memaddr = &memalloc[i];
+		memaddr->blk_num = i;
+		memaddr->used = 0;
+		memaddr->blk_size = BLK_SIZE_4096;
+		memaddr->phy_addr = start_addr;
+		start_addr += BLK_SIZE_4096;
+	}
+
+}
+
+
+/*free memory*/
+static void
+phci_hcd_mem_free(struct isp1763_mem_addr *memptr)
+{
+	/*block number to be freed */
+	int block = memptr->blk_num;
+
+	if (block < BLK_TOTAL)
+		if ((memptr->blk_size) && (memalloc[block].used != 0)) {
+			memalloc[block].used = 0;
+			memptr->used = 0;
+		}
+}
+
+
+/*allocate memory*/
+static void
+phci_hcd_mem_alloc(u32 size, struct isp1763_mem_addr *memptr, u32 flag)
+{
+	u32 blk_size = size;
+	u16 i;
+	u32 nextblk1 = 0, nextblk4 = 0;
+	u32 start = 0, end = 0;
+	struct isp1763_mem_addr *memaddr = 0;
+
+	memset(memptr, 0, sizeof *memptr);
+
+	pehci_print("phci_hcd_mem_alloc(size = %d)\n", size);
+
+	if (blk_size == 0) {
+		memptr->phy_addr = 0;
+		memptr->virt_addr = 0;
+		memptr->blk_size = 0;
+		memptr->num_alloc = 0;
+		memptr->blk_num = 0;
+		return;
+	}
+
+	/*end of the 1k blocks */
+	nextblk1 = BLK_256_ + BLK_1024_;
+	/*end of the 4k blocks */
+	nextblk4 = nextblk1 + BLK_4096_;
+
+
+	if (blk_size <= BLK_SIZE_256) {
+		blk_size = BLK_SIZE_256;
+		start = 0;
+		end = BLK_256_;
+	} else if (blk_size <= BLK_SIZE_1024) {
+		blk_size = BLK_SIZE_1024;
+		start = BLK_256_;
+		end = start + BLK_1024_;
+	} else if (blk_size > BLK_SIZE_1024) {
+		blk_size = BLK_SIZE_4096;
+		start = BLK_256_ + BLK_1024_;
+		end = start + BLK_4096_;
+	}
+
+	for (i = start; i < end; i++) {
+		memaddr = &memalloc[i];
+		if (!memaddr->used) {
+			memaddr->used = 1;
+			memptr->blk_num = i;
+			memptr->used = 1;
+			memptr->blk_size = blk_size;
+			memptr->phy_addr = memaddr->phy_addr;
+			memptr->virt_addr = memptr->phy_addr;
+			return;
+		}
+	}
+
+	/*look for in the next block if memory is free */
+	/*start from the first place of the next block */
+	start = end;
+
+	/*for 1k and 256 size request only 4k can be returned */
+	end = nextblk4;
+
+	for (i = start; i < end; i++) {
+		memaddr = &memalloc[i];
+		if (!memaddr->used) {
+			memaddr->used = 1;
+			memptr->used = 1;
+			memptr->blk_num = i;
+			memptr->blk_size = blk_size;
+			memptr->phy_addr = memaddr->phy_addr;
+			memptr->virt_addr = memptr->phy_addr;
+			return;
+		}
+	}
+
+}
+
+#endif
diff --git a/drivers/usb/host/pehci/host/otg.c b/drivers/usb/host/pehci/host/otg.c
new file mode 100644
index 0000000..546d9e9
--- /dev/null
+++ b/drivers/usb/host/pehci/host/otg.c
@@ -0,0 +1,189 @@
+/* 
+* Copyright (C) ST-Ericsson AP Pte Ltd 2010 
+*
+* ISP1763 Linux OTG Controller driver : host
+* 
+* 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 Street, Fifth Floor, Boston, MA  02110-1301, USA. 
+* 
+* This is a host controller driver file. OTG related events are handled here.
+* 
+* Author : wired support <wired.support@stericsson.com>
+*
+*/
+
+
+/*hub device which connected with root port*/
+struct usb_device *hubdev = 0;
+/* hub interrupt urb*/
+struct urb *huburb;
+
+/*return otghub from here*/
+struct usb_device *
+phci_register_otg_device(struct isp1763_dev *dev)
+{
+	printk("OTG dev %x %d\n",(u32) hubdev, hubdev->devnum);
+	if (hubdev && hubdev->devnum >= 0x2) {
+		return hubdev;
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL(phci_register_otg_device);
+
+/*suspend the otg port(0)
+ * needed when port is switching
+ * from host to device
+ * */
+int
+phci_suspend_otg_port(struct isp1763_dev *dev, u32 command)
+{
+	int status = 0;
+	hubdev->otgstate = USB_OTG_SUSPEND;
+	if (huburb->status == -EINPROGRESS) {
+		huburb->status = 0;
+	}
+
+	huburb->status = 0;
+	huburb->complete(huburb);
+	return status;
+}
+EXPORT_SYMBOL(phci_suspend_otg_port);
+
+/*set the flag to enumerate the device*/
+int
+phci_enumerate_otg_port(struct isp1763_dev *dev, u32 command)
+{
+	/*set the flag to enumerate */
+	/*connect change interrupt will happen from
+	 * phci_intl_worker only
+	 * */
+	hubdev->otgstate = USB_OTG_ENUMERATE;
+	if (huburb->status == -EINPROGRESS) {
+		huburb->status = 0;
+	}
+	/*complete the urb */
+
+	huburb->complete(huburb);
+
+	/*reset the otghub urb status */
+	huburb->status = -EINPROGRESS;
+	return 0;
+}
+EXPORT_SYMBOL(phci_enumerate_otg_port);
+
+/*host controller resume sequence at otg port*/
+int
+phci_resume_otg_port(struct isp1763_dev *dev, u32 command)
+{
+	printk("Resume is called\n");
+	hubdev->otgstate = USB_OTG_RESUME;
+	if (huburb->status == -EINPROGRESS) {
+		huburb->status = 0;
+	}
+	/*complete the urb */
+
+	huburb->complete(huburb);
+
+	/*reset the otghub urb status */
+	huburb->status = -EINPROGRESS;
+	return 0;
+}
+EXPORT_SYMBOL(phci_resume_otg_port);
+/*host controller remote wakeup sequence at otg port*/
+int
+phci_remotewakeup(struct isp1763_dev *dev)
+{
+    printk("phci_remotewakeup_otg_port is called\n");
+    hubdev->otgstate = USB_OTG_REMOTEWAKEUP;
+    if(huburb->status == -EINPROGRESS)
+        huburb->status = 0;
+    /*complete the urb*/
+#if ((defined LINUX_269) || defined (LINUX_2611))
+    huburb->complete(huburb,NULL);      
+#else
+	 huburb->complete(huburb);
+#endif
+    /*reset the otghub urb status*/
+    huburb->status = -EINPROGRESS;
+    return 0;
+}
+EXPORT_SYMBOL(phci_remotewakeup);
+
+/*host controller wakeup sequence at otg port*/
+int
+phci_resume_wakeup(struct isp1763_dev *dev)
+{
+    printk("phci_wakeup_otg_port is called\n");
+#if 0
+    hubdev->otgstate = USB_OTG_WAKEUP_ALL;
+    if(huburb->status == -EINPROGRESS)
+#endif
+        huburb->status = 0;
+    /*complete the urb*/
+#if ((defined LINUX_269) || defined (LINUX_2611))
+    huburb->complete(huburb,NULL);      
+#else
+	 huburb->complete(huburb);
+#endif
+    /*reset the otghub urb status*/
+    huburb->status = -EINPROGRESS;
+    return 0;
+}
+EXPORT_SYMBOL(phci_resume_wakeup);
+
+struct isp1763_driver *host_driver;
+struct isp1763_driver *device_driver;
+
+void
+pehci_delrhtimer(struct isp1763_dev *dev)
+{
+
+	struct usb_hcd *usb_hcd =
+		container_of(huburb->dev->parent->bus, struct usb_hcd, self);
+	del_timer_sync(&usb_hcd->rh_timer);
+	del_timer(&usb_hcd->rh_timer);
+
+}
+EXPORT_SYMBOL(pehci_delrhtimer);
+
+int
+pehci_Deinitialize(struct isp1763_dev *dev)
+{
+	dev -= 2;
+	if (dev->index == 0) {
+		if (dev->driver) {
+			if (dev->driver->powerdown) {
+				dev->driver->powerdown(dev);
+			}
+		}
+	}
+return 0;
+}
+EXPORT_SYMBOL(pehci_Deinitialize);
+
+int
+pehci_Reinitialize(struct isp1763_dev *dev)
+{
+
+	dev -= 2;
+	if (dev->index == 0) {
+		if(dev->driver->powerup){
+			dev->driver->powerup(dev);
+		}
+	}
+return 0;
+}
+EXPORT_SYMBOL(pehci_Reinitialize);
+
+
diff --git a/drivers/usb/host/pehci/host/pehci.c b/drivers/usb/host/pehci/host/pehci.c
new file mode 100644
index 0000000..19e9441
--- /dev/null
+++ b/drivers/usb/host/pehci/host/pehci.c
@@ -0,0 +1,6567 @@
+/* 
+* Copyright (C) ST-Ericsson AP Pte Ltd 2010 
+*
+* ISP1763 Linux OTG Controller driver : host
+* 
+* 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 Street, Fifth Floor, Boston, MA  02110-1301, USA. 
+* 
+* Refer to the follwing files in ~/drivers/usb/host for copyright owners:
+* ehci-dbg.c, ehci-hcd.c, ehci-hub.c, ehci-mem.c, ehci-q.c and ehic-sched.c (kernel version 2.6.9)
+* Code is modified for ST-Ericsson product 
+* 
+* Author : wired support <wired.support@stericsson.com>
+*
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/usb.h>
+#include <linux/version.h>
+#include <stdarg.h>
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+#include <linux/version.h>
+
+#include "../hal/isp1763.h"
+#include "pehci.h"
+#include "../hal/hal_intf.h"
+#include <linux/platform_device.h>
+#include <linux/wakelock.h>
+
+extern int HostComplianceTest;
+extern int HostTest;
+extern int No_Data_Phase;
+extern int No_Status_Phase;
+#define	EHCI_TUNE_CERR		3
+#define	URB_NO_INTERRUPT	0x0080
+#define	EHCI_TUNE_RL_TT		0
+#define	EHCI_TUNE_MULT_TT	1
+#define	EHCI_TUNE_RL_HS		0
+#define	EHCI_TUNE_MULT_HS	1
+
+
+#define POWER_DOWN_CTRL_NORMAL_VALUE	0xffff1ba0
+#define POWER_DOWN_CTRL_SUSPEND_VALUE	0xffff08b0
+
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
+//This macro is not supported in linux-2.6.35
+#define	USB_PORT_FEAT_HIGHSPEED 10
+#endif
+
+#ifdef CONFIG_ISO_SUPPORT
+
+#define	FALSE 0
+#define	TRUE (!FALSE)
+extern void *phcd_iso_sitd_to_ptd(phci_hcd * hcd,
+	struct ehci_sitd *sitd,
+	struct urb *urb, void *ptd);
+extern void *phcd_iso_itd_to_ptd(phci_hcd * hcd,
+	struct	ehci_itd *itd,
+	struct	urb *urb, void *ptd);
+
+extern unsigned	long phcd_submit_iso(phci_hcd *	hcd,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	struct usb_host_endpoint *ep,
+#else
+#endif
+	struct urb *urb, unsigned long *status);
+void pehci_hcd_iso_schedule(phci_hcd * hcd, struct urb *);
+unsigned long lgFrameIndex = 0;
+unsigned long lgScheduledPTDIndex = 0;
+int igNumOfPkts = 0;
+#endif /* CONFIG_ISO_SUPPORT */
+
+struct isp1763_dev *isp1763_hcd;
+
+#ifdef HCD_PACKAGE
+/*file operation*/
+struct fasync_struct *fasync_q;
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+static void
+pehci_hcd_urb_complete(phci_hcd * hcd, struct ehci_qh *qh, struct urb *urb,
+	td_ptd_map_t * td_ptd_map, struct pt_regs *regs);
+#else
+static void
+pehci_hcd_urb_complete(phci_hcd * hcd, struct ehci_qh *qh, struct urb *urb,
+	td_ptd_map_t * td_ptd_map);
+#endif
+
+#include "otg.c"  /*OTG and HCD package needs it */
+
+
+int hcdpowerdown = 0;
+int portchange=0; //for remotewakeup
+EXPORT_SYMBOL(hcdpowerdown);
+unsigned char otg_se0_enable;
+EXPORT_SYMBOL(otg_se0_enable);
+
+
+/*Enable all other interrupt.*/
+
+#ifdef MSEC_INT_BASED
+#ifdef THREAD_BASED//This is to test interrupt mapping problem
+//#define	INTR_ENABLE_MASK (HC_OPR_REG_INT|HC_CLK_RDY_INT )
+#define INTR_ENABLE_MASK (/*HC_MSEC_INT |*/ HC_INTL_INT | HC_ATL_INT| HC_ISO_INT /*| HC_EOT_INT | HC_ISO_INT*/)
+#else
+#define	INTR_ENABLE_MASK (HC_MSEC_INT|HC_OPR_REG_INT|HC_CLK_RDY_INT )
+#endif
+#else
+#define	INTR_ENABLE_MASK ( HC_INTL_INT | HC_ATL_INT |HC_ISO_INT| HC_EOT_INT|HC_OPR_REG_INT|HC_CLK_RDY_INT)
+#endif
+
+
+
+#ifdef THREAD_BASED
+
+#define NO_SOF_REQ_IN_TSK 		0x1
+#define NO_SOF_REQ_IN_ISR 		0x2
+#define NO_SOF_REQ_IN_REQ 	0x3
+#define MSEC_INTERVAL_CHECKING 5
+
+typedef struct _st_UsbIt_Msg_Struc {
+	struct usb_hcd 		*usb_hcd;
+	u8				uIntStatus;
+	struct list_head 		list;
+} st_UsbIt_Msg_Struc, *pst_UsbIt_Msg_Struc ;
+
+typedef struct _st_UsbIt_Thread {
+    wait_queue_head_t       	ulThrdWaitQhead;
+    int                           		lThrdWakeUpNeeded;
+    struct task_struct           	*phThreadTask;
+    spinlock_t              lock;
+} st_UsbIt_Thread, *pst_UsbIt_Thread;
+
+st_UsbIt_Thread g_stUsbItThreadHandler;
+
+st_UsbIt_Msg_Struc 	g_messList;
+st_UsbIt_Msg_Struc 	g_enqueueMessList;
+spinlock_t              	enqueue_lock;
+
+int pehci_hcd_process_irq_it_handle(struct usb_hcd* usb_hcd_);
+int pehci_hcd_process_irq_in_thread(struct usb_hcd *usb_hcd_);
+
+#endif /*THREAD_BASED*/
+
+#ifdef THREAD_BASED
+phci_hcd *g_pehci_hcd;
+#endif
+
+
+struct wake_lock pehci_wake_lock;
+
+/*---------------------------------------------------
+ *    Globals for EHCI
+ -----------------------------------------------------*/
+
+/* used	when updating hcd data */
+static spinlock_t hcd_data_lock	= SPIN_LOCK_UNLOCKED;
+
+static const char hcd_name[] = "ST-Ericsson ISP1763";
+static td_ptd_map_buff_t td_ptd_map_buff[TD_PTD_TOTAL_BUFF_TYPES];	/* td-ptd map buffer for all 1362 buffers */
+
+static u8 td_ptd_pipe_x_buff_type[TD_PTD_TOTAL_BUFF_TYPES] = {
+	TD_PTD_BUFF_TYPE_ATL,
+	TD_PTD_BUFF_TYPE_INTL,
+	TD_PTD_BUFF_TYPE_ISTL
+};
+
+
+/*global memory	blocks*/
+isp1763_mem_addr_t memalloc[BLK_TOTAL];
+#include "mem.c"
+#include "qtdptd.c"
+
+#ifdef CONFIG_ISO_SUPPORT
+#include "itdptd.c"
+#endif /* CONFIG_ISO_SUPPORT */
+
+static int
+pehci_rh_control(struct	usb_hcd	*usb_hcd, u16 typeReq,
+		 u16 wValue, u16 wIndex, char *buf, u16	wLength);
+
+static int pehci_bus_suspend(struct usb_hcd *usb_hcd);
+static int pehci_bus_resume(struct usb_hcd *usb_hcd);
+/*----------------------------------------------------*/
+static void
+pehci_complete_device_removal(phci_hcd * hcd, struct ehci_qh *qh)
+{
+	td_ptd_map_t *td_ptd_map;
+	td_ptd_map_buff_t *td_ptd_buff;
+	struct urb * urb;
+	urb_priv_t *urb_priv;
+	struct ehci_qtd	*qtd = 0;
+//	struct usb_hcd *usb_hcd=&hcd->usb_hcd;
+	u16 skipmap=0;
+
+	if (qh->type ==	TD_PTD_BUFF_TYPE_ISTL) {
+#ifdef COMMON_MEMORY
+		phci_hcd_mem_free(&qh->memory_addr);
+#endif
+		return;
+	}
+
+	td_ptd_buff = &td_ptd_map_buff[qh->type];
+	td_ptd_map = &td_ptd_buff->map_list[qh->qtd_ptd_index];
+
+	/*this flag should only	be set when device is going */
+	td_ptd_map->state = TD_PTD_REMOVE;
+	/*if nothing there */
+	if (list_empty(&qh->qtd_list)) {
+		if (td_ptd_map->state != TD_PTD_NEW) {
+			phci_hcd_release_td_ptd_index(qh);
+		}
+		qha_free(qha_cache, qh);
+		qh = 0;
+		return;
+	} else {
+	
+		if(!list_empty(&qh->qtd_list)){
+				qtd=NULL;
+				qtd = list_entry(qh->qtd_list.next, struct ehci_qtd, qtd_list);
+				if(qtd){
+					urb=qtd->urb;
+					urb_priv= urb->hcpriv;
+					
+					if(urb)
+					switch (usb_pipetype(urb->pipe)) {
+						case PIPE_CONTROL:
+						case PIPE_BULK:
+							break;
+						case PIPE_INTERRUPT:
+							td_ptd_buff = &td_ptd_map_buff[TD_PTD_BUFF_TYPE_INTL];
+							td_ptd_map = &td_ptd_buff->map_list[qh->qtd_ptd_index];
+
+							/*urb is already been removed */
+						//	if (td_ptd_map->state == TD_PTD_NEW) {
+						//		kfree(urb_priv);
+						//		break;
+						//	}
+
+							/* These TDs are not pending anymore */
+							td_ptd_buff->pending_ptd_bitmap &= ~td_ptd_map->ptd_bitmap;
+
+							td_ptd_map->state = TD_PTD_REMOVE;
+							urb_priv->state	|= DELETE_URB;
+
+							/*read the skipmap, to see if this transfer has	to be rescheduled */
+							skipmap	=
+							isp1763_reg_read16(hcd->dev, hcd->regs.inttdskipmap,
+							skipmap);
+
+							isp1763_reg_write16(hcd->dev, hcd->regs.inttdskipmap,
+							skipmap | td_ptd_map->ptd_bitmap);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+							pehci_hcd_urb_complete(hcd, qh, urb, td_ptd_map, NULL);
+#else
+							pehci_hcd_urb_complete(hcd, qh, urb, td_ptd_map);
+#endif
+							break;
+					}
+
+					
+				}else{
+					//break;
+				}
+		}
+		qha_free(qha_cache, qh);
+		qh = 0;
+		return;
+	}
+	/*MUST not come	down below this	*/
+	err("Never Error: Should not come to this portion of code\n");
+
+	return;
+}
+
+/*functions looks for the values in register
+  specified in ptr, if register	values masked
+  with the mask	and result is equal to done,
+  operation is successful else fails with timeout*/
+static int
+pehci_hcd_handshake(phci_hcd * hcd, u32	ptr, u32 mask, u32 done, int usec)
+{
+	u32 result = 0;
+	do {
+		result = isp1763_reg_read16(hcd->dev, ptr, result);
+		printk(KERN_NOTICE "Registr %x val is %x\n", ptr, result);
+		if (result == ~(u32) 0)	{/* card removed */
+			return -ENODEV;
+		}
+		result &= mask;
+		if (result == done) {
+			return 0;
+		}
+		udelay(1);
+		usec--;
+	} while	(usec >	0);
+
+	return -ETIMEDOUT;
+}
+
+#ifndef	MSEC_INT_BASED
+/*schedule atl and interrupt tds,
+  only when we are not running on sof interrupt
+ */
+static void
+pehci_hcd_td_ptd_submit_urb(phci_hcd * hcd, struct ehci_qh *qh,	u8 bufftype)
+{
+	unsigned long flags=0;
+	struct ehci_qtd	*qtd = 0;
+	struct urb *urb	= 0;
+	struct _isp1763_qha *qha = 0;
+	u16 location = 0;
+	u16 skipmap = 0;
+	u16 buffstatus = 0;
+	u16 ormask = 0;
+	u16 intormask =	0;
+	u32 length = 0;
+	struct list_head *head;
+
+	td_ptd_map_t *td_ptd_map;
+	td_ptd_map_buff_t *ptd_map_buff;
+	struct isp1763_mem_addr	*mem_addr = 0;
+
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+	pehci_print("Buuffer type %d\n", bufftype);
+
+	spin_lock_irqsave(&hcd->lock, flags);
+	ptd_map_buff = &td_ptd_map_buff[bufftype];
+
+	qha = &hcd->qha;
+
+	switch (bufftype) {
+	case TD_PTD_BUFF_TYPE_ATL:
+
+		skipmap	=
+			isp1763_reg_read16(hcd->dev, hcd->regs.atltdskipmap,
+					   skipmap);
+
+		ormask = isp1763_reg_read16(hcd->dev, hcd->regs.atl_irq_mask_or,
+					    ormask);
+		break;
+	case TD_PTD_BUFF_TYPE_INTL:
+
+		skipmap	=
+			isp1763_reg_read16(hcd->dev, hcd->regs.inttdskipmap,
+					   skipmap);
+
+		intormask =
+			isp1763_reg_read16(hcd->dev, hcd->regs.int_irq_mask_or,
+					   intormask);
+		break;
+	default:
+
+		skipmap	=
+			isp1763_reg_read16(hcd->dev, hcd->regs.isotdskipmap,
+					   skipmap);
+		break;
+
+	}
+
+
+	buffstatus =
+		isp1763_reg_read16(hcd->dev, hcd->regs.buffer_status,
+				   buffstatus);
+
+	/*header, qtd, and urb of current transfer */
+	location = qh->qtd_ptd_index;
+	td_ptd_map = &ptd_map_buff->map_list[location];
+
+	if (!(qh->qh_state & QH_STATE_TAKE_NEXT)) {
+		pehci_check("qh	will schdule from interrupt routine,map	%x\n",
+			    td_ptd_map->ptd_bitmap);
+		spin_unlock_irqrestore(&hcd->lock, flags);
+		return;
+	}
+	head = &qh->qtd_list;
+	qtd = list_entry(head->next, struct ehci_qtd, qtd_list);
+
+	/*already scheduled, may be from interrupt */
+	if (!(qtd->state & QTD_STATE_NEW)) {
+		pehci_check("qtd already in, state %x\n", qtd->state);
+		spin_unlock_irqrestore(&hcd->lock, flags);
+		return;
+	}
+
+	qtd->state &= ~QTD_STATE_NEW;
+	qtd->state |= QTD_STATE_SCHEDULED;
+
+	qh->qh_state &=	~QH_STATE_TAKE_NEXT;
+	/*take the first td */
+	td_ptd_map->qtd	= qtd;
+	/*take the urb */
+	urb = qtd->urb;
+	ptd_map_buff->active_ptds++;
+
+	/*trust	the atl	worker,	at this	location there wont be any td */
+	/*if this td is	the last one */
+	if (qtd->state & QTD_STATE_LAST) {
+		qh->hw_current = cpu_to_le32(0);
+		/*else update the hw_next of qh	to the next td */
+	} else {
+		qh->hw_current = qtd->hw_next;
+	}
+	memset(qha, 0, sizeof(isp1763_qha));
+
+	pehci_check("td	being scheduled	: length: %d, device: %d, map: %x\n",
+		    qtd->length, urb->dev->devnum, td_ptd_map->ptd_bitmap);
+	/*NEW, now need	to get the memory for this transfer */
+	length = qtd->length;
+	mem_addr = &qtd->mem_addr;
+	phci_hcd_mem_alloc(length, mem_addr, 0);
+	if (length && ((mem_addr->phy_addr == 0) || (mem_addr->virt_addr == 0))) {
+		err("Never Error: Can not allocate memory for the current td,length %d\n", length);
+		/*should not happen */
+		/*can happen only when we exceed the limit of devices we support
+		   MAX 4 mass storage at a time	*/
+	}
+	phci_hcd_qha_from_qtd(hcd, qtd, qtd->urb, (void *) qha,
+		td_ptd_map->ptd_ram_data_addr, qh);
+	if (qh->type ==	TD_PTD_BUFF_TYPE_INTL) {
+		phci_hcd_qhint_schedule(hcd, qh, qtd, (isp1763_qhint *)	qha,
+					qtd->urb);
+	}
+	/*write	qha into the header of the host	controller */
+	isp1763_mem_write(hcd->dev, td_ptd_map->ptd_header_addr, 0,
+			  (u32 *) (qha), PHCI_QHA_LENGTH, 0);
+
+	/*if this is SETUP/OUT token , then need to write into the buffer */
+	/*length should	be valid and supported by the ptd */
+	if (qtd->length && (qtd->length <= HC_ATL_PL_SIZE)){
+		switch (PTD_PID(qha->td_info2))	{
+		case OUT_PID:
+		case SETUP_PID:
+
+			isp1763_mem_write(hcd->dev, (u32) mem_addr->phy_addr, 0,
+					  (void	*) qtd->hw_buf[0], length, 0);
+
+
+#if 0
+					int i=0;
+					int *data_addr= qtd->hw_buf[0];
+					printk("\n");
+					for(i=0;i<length;i+=4) printk("[0x%X] ",*data_addr++);
+					printk("\n");
+#endif
+
+			
+
+			break;
+		}
+	}
+
+	/*unskip the tds at this location */
+	switch (bufftype) {
+	case TD_PTD_BUFF_TYPE_ATL:
+		skipmap	&= ~td_ptd_map->ptd_bitmap;
+		/*enable atl interrupts	on donemap */
+		ormask |= td_ptd_map->ptd_bitmap;
+
+		isp1763_reg_write16(hcd->dev, hcd->regs.atl_irq_mask_or,
+				    ormask);
+		break;
+
+	case TD_PTD_BUFF_TYPE_INTL:
+		skipmap	&= ~td_ptd_map->ptd_bitmap;
+		intormask |= td_ptd_map->ptd_bitmap;
+
+		isp1763_reg_write16(hcd->dev, hcd->regs.int_irq_mask_or,
+				    intormask);
+		break;
+
+	case TD_PTD_BUFF_TYPE_ISTL:
+		skipmap	&= ~td_ptd_map->ptd_bitmap;
+
+		isp1763_reg_write16(hcd->dev, hcd->regs.isotdskipmap, skipmap);
+		break;
+	}
+
+	/*if any new schedule, enable the atl buffer */
+	switch (bufftype) {
+	case TD_PTD_BUFF_TYPE_ATL:
+
+		isp1763_reg_write16(hcd->dev, hcd->regs.buffer_status,
+				    buffstatus | ATL_BUFFER);
+
+		isp1763_reg_write16(hcd->dev, hcd->regs.atltdskipmap, skipmap);
+		buffstatus |= ATL_BUFFER;
+		break;
+	case TD_PTD_BUFF_TYPE_INTL:
+
+		isp1763_reg_write16(hcd->dev, hcd->regs.buffer_status,
+				    buffstatus | INT_BUFFER);
+
+		isp1763_reg_write16(hcd->dev, hcd->regs.inttdskipmap, skipmap);
+		break;
+	case TD_PTD_BUFF_TYPE_ISTL:
+		/*not supposed to be seen here */
+
+		isp1763_reg_write16(hcd->dev, hcd->regs.buffer_status,
+				    buffstatus | ISO_BUFFER);
+		break;
+	}
+	spin_unlock_irqrestore(&hcd->lock, flags);
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+	return;
+
+}
+#endif
+
+
+
+#ifdef MSEC_INT_BASED
+/*schedule next	(atl/int)tds and any pending tds*/
+static void
+pehci_hcd_schedule_pending_ptds(phci_hcd * hcd, u16 donemap, u8 bufftype,
+				u16 only)
+{
+	struct ehci_qtd	*qtd = 0;
+	struct ehci_qh *qh = 0;
+	struct list_head *qtd_list = 0;
+	struct _isp1763_qha allqha;
+	struct _isp1763_qha *qha = 0;
+	u16 mask = 0x1,	index =	0;
+	u16 location = 0;
+	u16 skipmap = 0;
+	u32 newschedule	= 0;
+	u16 buffstatus = 0;
+	u16 schedulemap	= 0;
+#ifndef	CONFIG_ISO_SUPPORT
+	u16 lasttd = 1;
+#endif
+	u16 lastmap = 0;
+	struct urb *urb	= 0;
+	urb_priv_t *urbpriv = 0;
+	int length = 0;
+	u16 ormask = 0,	andmask	= 0;
+	u16 intormask =	0;
+	td_ptd_map_t *td_ptd_map;
+	td_ptd_map_buff_t *ptd_map_buff;
+	struct isp1763_mem_addr	*mem_addr = 0;
+
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+	pehci_print("Buffer type %d\n",	bufftype);
+
+	/*need to hold this lock if another interrupt is comming
+	   for previously scheduled transfer, while scheduling new tds
+	 */
+	spin_lock(&hcd_data_lock);
+	ptd_map_buff = &td_ptd_map_buff[bufftype];
+	qha = &allqha;
+	switch (bufftype) {
+	case TD_PTD_BUFF_TYPE_ATL:
+
+		skipmap	=
+			isp1763_reg_read16(hcd->dev, hcd->regs.atltdskipmap,
+					   skipmap);
+		rmb();
+
+		ormask = isp1763_reg_read16(hcd->dev, hcd->regs.atl_irq_mask_or,
+					    ormask);
+
+		andmask	=
+			isp1763_reg_read16(hcd->dev, hcd->regs.atl_irq_mask_and,
+					   andmask);
+		break;
+	case TD_PTD_BUFF_TYPE_INTL:
+
+		skipmap	=
+			isp1763_reg_read16(hcd->dev, hcd->regs.inttdskipmap,
+					   skipmap);
+		/*read the interrupt mask registers */
+
+		intormask =
+			isp1763_reg_read16(hcd->dev, hcd->regs.int_irq_mask_or,
+					   intormask);
+		break;
+	default:
+		err("Never Error: Bogus	type of	bufer\n");
+		return;
+	}
+
+	buffstatus =
+		isp1763_reg_read16(hcd->dev, hcd->regs.buffer_status,
+				   buffstatus);
+	/*td headers need attention */
+	schedulemap = donemap;
+	while (schedulemap) {
+		index =	schedulemap & mask;
+		schedulemap &= ~mask;
+		mask <<= 1;
+
+		if (!index) {
+			location++;
+			continue;
+		}
+
+		td_ptd_map = &ptd_map_buff->map_list[location];
+		/*	can happen if donemap comes after
+		   removal of the urb and associated tds
+		 */
+		if ((td_ptd_map->state == TD_PTD_NEW) ||
+			(td_ptd_map->state == TD_PTD_REMOVE)) {
+			qh = td_ptd_map->qh;
+			pehci_check
+				("should not come here,	map %x,pending map %x\n",
+				 td_ptd_map->ptd_bitmap,
+				 ptd_map_buff->pending_ptd_bitmap);
+
+			pehci_check("buffer type %s\n",
+				(bufftype == 0) ? "ATL" : "INTL");
+			donemap	&= ~td_ptd_map->ptd_bitmap;
+			/*clear	the pending map	*/
+			ptd_map_buff->pending_ptd_bitmap &=
+				~td_ptd_map->ptd_bitmap;
+			location++;
+			continue;
+		}
+
+		/*no endpoint at this location */
+		if (!(td_ptd_map->qh)) {
+			err("queue head	can not	be null	here\n");
+			/*move to the next location */
+			ptd_map_buff->pending_ptd_bitmap &=
+				~td_ptd_map->ptd_bitmap;
+			location++;
+			continue;
+		}
+
+		/*current endpoint */
+		qh = td_ptd_map->qh;
+		if (!(skipmap &	td_ptd_map->ptd_bitmap)) {
+			/*should not happen, if	happening, then	*/
+			pehci_check("buffertype	%d,td_ptd_map %x,skipnap %x\n",
+				    bufftype, td_ptd_map->ptd_bitmap, skipmap);
+			lastmap	= td_ptd_map->ptd_bitmap;
+			donemap	&= ~td_ptd_map->ptd_bitmap;
+			ptd_map_buff->pending_ptd_bitmap &=
+				~td_ptd_map->ptd_bitmap;
+			location++;
+			continue;
+		}
+
+		/*if we	processed all the tds in ths transfer */
+		if (td_ptd_map->lasttd)	{
+			err("should not	show  map %x,qtd %p\n",
+			td_ptd_map->ptd_bitmap, td_ptd_map->qtd);
+			/*this can happen in case the transfer is not being
+			 * procesed by the host	, tho the transfer is there
+			 * */
+			qh->hw_current = cpu_to_le32(td_ptd_map->qtd);
+			ptd_map_buff->pending_ptd_bitmap &=
+				~td_ptd_map->ptd_bitmap;
+			location++;
+			continue;
+		}
+
+		/*if we	have ptd that is going for reload */
+		if ((td_ptd_map->qtd) && (td_ptd_map->state & TD_PTD_RELOAD)) {
+			warn("%s: reload td\n",	__FUNCTION__);
+			td_ptd_map->state &= ~TD_PTD_RELOAD;
+			qtd = td_ptd_map->qtd;
+			goto loadtd;
+		}
+
+		/* qh is there but no qtd so it	means fresh transfer */
+		if ((td_ptd_map->qh) &&	!(td_ptd_map->qtd)) {
+			if (list_empty(&qh->qtd_list)) {
+				/*should not hapen again, as it	comes here
+				   when	it has td in its map
+				 */
+				pehci_check
+					("must not come	here any more, td map %x\n",
+					 td_ptd_map->ptd_bitmap);
+				/*this location	is idle	and can	be free	next time if
+				   no new transfers are	comming	for this */
+				donemap	&= ~td_ptd_map->ptd_bitmap;
+				td_ptd_map->state |= TD_PTD_IDLE;
+				ptd_map_buff->pending_ptd_bitmap &=
+					~td_ptd_map->ptd_bitmap;
+				location++;
+				continue;
+			}
+			qtd_list = &qh->qtd_list;
+			qtd = td_ptd_map->qtd =
+				list_entry(qtd_list->next, struct ehci_qtd,
+					   qtd_list);
+			/*got the td, now goto reload */
+			goto loadtd;
+		}
+
+		/*if there is already one qtd there in the transfer */
+		if (td_ptd_map->qtd) {
+			/*new schedule */
+			qtd = td_ptd_map->qtd;
+		}
+		loadtd:
+		/*should not happen */
+		if (!qtd) {
+			err("this piece	of code	should not be executed\n");
+			ptd_map_buff->pending_ptd_bitmap &=
+				~td_ptd_map->ptd_bitmap;
+			location++;
+			continue;
+		}
+
+		ptd_map_buff->active_ptds++;
+		/*clear	the pending map	here */
+		ptd_map_buff->pending_ptd_bitmap &= ~td_ptd_map->ptd_bitmap;
+
+
+
+		/*if this td is	the last one */
+		if (qtd->state & QTD_STATE_LAST) {
+			/*no qtd anymore */
+			qh->hw_current = cpu_to_le32(0);
+
+			/*else update the hw_next of qh	to the next td */
+		} else {
+			qh->hw_current = qtd->hw_next;
+		}
+
+		if (location !=	qh->qtd_ptd_index) {
+			err("Never Error: Endpoint header location and scheduling information are not same\n");
+		}
+
+		/*next location	*/
+		location++;
+		/*found	new transfer */
+		newschedule = 1;
+		/*take the urb */
+		urb = qtd->urb;
+		/*sometimes we miss due	to skipmap
+		   so to make sure that	we dont	put again the
+		   same	stuff
+		 */
+		if (!(qtd->state & QTD_STATE_NEW)) {
+			err("Never Error: We should not	put the	same stuff\n");
+			continue;
+		}
+
+		urbpriv	= (urb_priv_t *) urb->hcpriv;
+		urbpriv->timeout = 0;
+
+		/*no more new */
+		qtd->state &= ~QTD_STATE_NEW;
+		qtd->state |= QTD_STATE_SCHEDULED;
+
+
+
+		/*NEW, now need	to get the memory for this transfer */
+		length = qtd->length;
+		mem_addr = &qtd->mem_addr;
+		phci_hcd_mem_alloc(length, mem_addr, 0);
+		if (length && ((mem_addr->phy_addr == 0)
+			       || (mem_addr->virt_addr == 0))) {
+
+			err("Never Error: Can not allocate memory for the current td,length %d\n", length);
+			location++;
+			continue;
+		}
+
+		pehci_check("qtd being scheduled %p, device %d,map %x\n", qtd,
+			    urb->dev->devnum, td_ptd_map->ptd_bitmap);
+
+
+		memset(qha, 0, sizeof(isp1763_qha));
+		/*convert qtd to qha */
+		phci_hcd_qha_from_qtd(hcd, qtd,	qtd->urb, (void	*) qha,
+			td_ptd_map->ptd_ram_data_addr, qh);
+
+		if (qh->type ==	TD_PTD_BUFF_TYPE_INTL) {
+			phci_hcd_qhint_schedule(hcd, qh, qtd,
+				(isp1763_qhint *) qha,
+				qtd->urb);
+
+		}
+
+
+		length = PTD_XFERRED_LENGTH(qha->td_info1 >> 3);
+		if (length > HC_ATL_PL_SIZE) {
+			err("Never Error: Bogus	length,length %d(max %d)\n",
+			qtd->length, HC_ATL_PL_SIZE);
+		}
+
+		/*write	qha into the header of the host	controller */
+		isp1763_mem_write(hcd->dev, td_ptd_map->ptd_header_addr, 0,
+			(u32 *) (qha), PHCI_QHA_LENGTH, 0);
+
+#ifdef PTD_DUMP_SCHEDULE
+		printk("SCHEDULE next (atl/int)tds PTD header\n");
+		printk("DW0: 0x%08X\n", qha->td_info1);
+		printk("DW1: 0x%08X\n", qha->td_info2);
+		printk("DW2: 0x%08X\n", qha->td_info3);
+		printk("DW3: 0x%08X\n", qha->td_info4);
+#endif
+		
+		/*if this is SETUP/OUT token , then need to write into the buffer */
+		/*length should	be valid */
+		if (qtd->length && (length <= HC_ATL_PL_SIZE)){
+			switch (PTD_PID(qha->td_info2))	{
+			case OUT_PID:
+			case SETUP_PID:
+
+				isp1763_mem_write(hcd->dev,
+					(u32)	mem_addr->phy_addr, 0,
+					(void	*) qtd->hw_buf[0],
+					length, 0);
+#if 0
+					int i=0;
+					int *data_addr= qtd->hw_buf[0];
+					printk("\n");
+					for(i=0;i<length;i+=4) printk("[0x%X] ",*data_addr++);
+					printk("\n");
+#endif
+
+
+
+				break;
+			}
+		}
+
+		/*unskip the tds at this location */
+		switch (bufftype) {
+		case TD_PTD_BUFF_TYPE_ATL:
+			skipmap	&= ~td_ptd_map->ptd_bitmap;
+			lastmap	= td_ptd_map->ptd_bitmap;
+			/*try to reduce	the interrupts */
+			ormask |= td_ptd_map->ptd_bitmap;
+
+			isp1763_reg_write16(hcd->dev, hcd->regs.atl_irq_mask_or,
+					    ormask);
+			break;
+
+		case TD_PTD_BUFF_TYPE_INTL:
+			skipmap	&= ~td_ptd_map->ptd_bitmap;
+			lastmap	= td_ptd_map->ptd_bitmap;
+			intormask |= td_ptd_map->ptd_bitmap;
+			;
+			isp1763_reg_write16(hcd->dev, hcd->regs.int_irq_mask_or,
+					    intormask);
+			break;
+
+		case TD_PTD_BUFF_TYPE_ISTL:
+#ifdef CONFIG_ISO_SUPPORT
+			iso_dbg(ISO_DBG_INFO,
+				"Never Error: Should not come here\n");
+#else
+			skipmap	&= ~td_ptd_map->ptd_bitmap;
+
+			isp1763_reg_write16(hcd->dev, hcd->regs.isotdskipmap,
+					    skipmap);
+
+			isp1763_reg_write16(hcd->dev, hcd->regs.isotdlastmap,
+				lasttd);
+#endif /* CONFIG_ISO_SUPPORT */
+			break;
+		}
+
+
+	}
+	/*if any new schedule, enable the atl buffer */
+
+	if (newschedule) {
+		switch (bufftype) {
+		case TD_PTD_BUFF_TYPE_ATL:
+
+			isp1763_reg_write16(hcd->dev, hcd->regs.buffer_status,
+					    buffstatus | ATL_BUFFER);
+			/*i am comming here to only those tds that has to be scheduled */
+			/*so skip map must be in place */
+			if (skipmap & donemap) {
+				pehci_check
+					("must be both ones compliment of each other\n");
+				pehci_check
+					("problem, skipmap %x, donemap %x,\n",
+					 skipmap, donemap);
+
+			}
+			skipmap	&= ~donemap;
+
+			isp1763_reg_write16(hcd->dev, hcd->regs.atltdskipmap,
+					    skipmap);
+
+			break;
+		case TD_PTD_BUFF_TYPE_INTL:
+
+			isp1763_reg_write16(hcd->dev, hcd->regs.buffer_status,
+					    buffstatus | INT_BUFFER);
+			skipmap	&= ~donemap;
+
+			isp1763_reg_write16(hcd->dev, hcd->regs.inttdskipmap,
+					    skipmap);
+			break;
+		case TD_PTD_BUFF_TYPE_ISTL:
+#ifndef	CONFIG_ISO_SUPPORT
+
+			isp1763_reg_write16(hcd->dev, hcd->regs.buffer_status,
+					    buffstatus | ISO_BUFFER);
+#endif
+			break;
+		}
+	}
+	spin_unlock(&hcd_data_lock);
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+}
+#endif
+
+
+
+static void
+pehci_hcd_qtd_schedule(phci_hcd	* hcd, struct ehci_qtd *qtd,
+		       struct ehci_qh *qh, td_ptd_map_t	* td_ptd_map)
+{
+	struct urb *urb;
+	urb_priv_t *urbpriv = 0;
+	u32 length=0;
+	struct isp1763_mem_addr	*mem_addr = 0;
+	struct _isp1763_qha *qha, qhtemp;
+
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+
+	if (qtd->state & QTD_STATE_SCHEDULED) {
+		return;
+	}
+	/*redundant */
+	qha = &qhtemp;
+
+	/*if this td is	the last one */
+	if (qtd->state & QTD_STATE_LAST) {
+		/*no qtd anymore */
+		qh->hw_current = cpu_to_le32(0);
+
+		/*else update the hw_next of qh	to the next td */
+	} else {
+		qh->hw_current = qtd->hw_next;
+	}
+
+	urb = qtd->urb;
+	urbpriv	= (urb_priv_t *) urb->hcpriv;
+	urbpriv->timeout = 0;
+
+	/*NEW, now need	to get the memory for this transfer */
+	length = qtd->length;
+	mem_addr = &qtd->mem_addr;
+	phci_hcd_mem_alloc(length, mem_addr, 0);
+	if (length && ((mem_addr->phy_addr == 0) || (mem_addr->virt_addr == 0))) {
+		err("Never Error: Cannot allocate memory for the current td,length %d\n", length);
+		return;
+	}
+
+	pehci_check("newqtd being scheduled, device: %d,map: %x\n",
+		    urb->dev->devnum, td_ptd_map->ptd_bitmap);
+
+	//udelay(100);
+
+	memset(qha, 0, sizeof(isp1763_qha));
+	/*convert qtd to qha */
+	phci_hcd_qha_from_qtd(hcd, qtd,	qtd->urb, (void	*) qha,
+			      td_ptd_map->ptd_ram_data_addr, qh
+			      /*td_ptd_map->datatoggle */ );
+
+	if (qh->type ==	TD_PTD_BUFF_TYPE_INTL) {
+		phci_hcd_qhint_schedule(hcd, qh, qtd, (isp1763_qhint *)	qha,
+					qtd->urb);
+	}
+
+
+	length = PTD_XFERRED_LENGTH(qha->td_info1 >> 3);
+	if (length > HC_ATL_PL_SIZE) {
+		err("Never Error: Bogus	length,length %d(max %d)\n",
+		qtd->length, HC_ATL_PL_SIZE);
+	}
+
+	/*write	qha into the header of the host	controller */
+	isp1763_mem_write(hcd->dev, td_ptd_map->ptd_header_addr, 0,
+			  (u32 *) (qha), PHCI_QHA_LENGTH, 0);
+	
+#if 0 //def PTD_DUMP_SCHEDULE
+		printk("SCHEDULE Next qtd\n");
+		printk("DW0: 0x%08X\n", qha->td_info1);
+		printk("DW1: 0x%08X\n", qha->td_info2);
+		printk("DW2: 0x%08X\n", qha->td_info3);
+		printk("DW3: 0x%08X\n", qha->td_info4);
+#endif
+	
+	/*if this is SETUP/OUT token , then need to write into the buffer */
+	/*length should	be valid */
+	if (qtd->length && (length <= HC_ATL_PL_SIZE)){
+		switch (PTD_PID(qha->td_info2))	{
+		case OUT_PID:
+		case SETUP_PID:
+
+			isp1763_mem_write(hcd->dev, (u32) mem_addr->phy_addr, 0,
+				(void	*) qtd->hw_buf[0], length, 0);
+
+#if 0
+					int i=0;
+					int *data_addr= qtd->hw_buf[0];
+					printk("\n");
+					for(i=0;i<length;i+=4) printk("[0x%X] ",*data_addr++);
+					printk("\n");
+#endif
+
+
+			break;
+		}
+	}
+	/*qtd is scheduled */
+	qtd->state &= ~QTD_STATE_NEW;
+	qtd->state |= QTD_STATE_SCHEDULED;
+
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+	return;
+}
+#ifdef USBNET 
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+static void
+pehci_hcd_urb_delayed_complete(phci_hcd * hcd, struct ehci_qh *qh, struct urb *urb,
+	td_ptd_map_t * td_ptd_map, struct pt_regs *regs)
+#else
+static void
+pehci_hcd_urb_delayed_complete(phci_hcd * hcd, struct ehci_qh *qh, struct urb *urb,
+	td_ptd_map_t * td_ptd_map)
+#endif
+{
+	static u32 remove = 0;
+	static u32 qh_state = 0;
+
+	urb_priv_t *urb_priv = (urb_priv_t *) urb->hcpriv;
+
+#ifdef USBNET 
+	struct isp1763_async_cleanup_urb *urb_st = 0;
+#endif
+
+
+
+	urb_priv->timeout = 0;
+
+	if((td_ptd_map->state == TD_PTD_REMOVE	) ||
+		  (urb_priv->state == DELETE_URB) ||
+		     !HCD_IS_RUNNING(hcd->state)){
+	remove=1;
+	}
+	qh_state=qh->qh_state;
+	qh->qh_state = QH_STATE_COMPLETING;
+	/*remove the done tds */
+	spin_lock(&hcd_data_lock);
+	phci_hcd_urb_free_priv(hcd, urb_priv, qh);
+	spin_unlock(&hcd_data_lock);
+
+	urb_priv->timeout = 0;
+	kfree(urb_priv);
+	urb->hcpriv = 0;
+
+
+	/*if normal completion */
+	if (urb->status	== -EINPROGRESS) {
+		urb->status = 0;
+	}
+
+	if(remove)
+	if (list_empty(&qh->qtd_list)) {
+		phci_hcd_release_td_ptd_index(qh);
+	}
+	remove=0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+		if(!usb_hcd_check_unlink_urb(&hcd->usb_hcd, urb,0))
+					usb_hcd_unlink_urb_from_ep(&hcd->usb_hcd,urb);
+#endif
+
+//if(qh_state!=QH_STATE_COMPLETING)
+{
+//	spin_unlock(&hcd->lock);
+	/* assume interrupt has been disabled and has acquired hcd->lock */
+	urb_st = (struct isp1763_async_cleanup_urb *)kmalloc(sizeof(struct isp1763_async_cleanup_urb), GFP_ATOMIC);
+	urb_st->urb = urb;
+	list_add_tail(&urb_st->urb_list, &(hcd->cleanup_urb.urb_list));
+
+//	isp1763_reg_write16(hcd->dev, hcd->regs.interruptenable, INTR_ENABLE_MASK | HC_SOF_INT);
+	isp1763_reg_write16(hcd->dev, hcd->regs.interruptenable, HC_MSOF_INT);
+//	spin_lock(&hcd->lock);
+}
+
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+}
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+static void
+pehci_hcd_urb_complete(phci_hcd * hcd, struct ehci_qh *qh, struct urb *urb,
+	td_ptd_map_t * td_ptd_map, struct pt_regs *regs)
+#else
+static void
+pehci_hcd_urb_complete(phci_hcd * hcd, struct ehci_qh *qh, struct urb *urb,
+	td_ptd_map_t * td_ptd_map)
+#endif
+{
+	static u32 remove = 0;
+	static u32 qh_state = 0;
+	urb_priv_t *urb_priv = (urb_priv_t *) urb->hcpriv;
+	
+	if(urb_priv==NULL){
+	printk("***************urb_priv is NULL ************ %s: Entered\n",	__FUNCTION__);
+	goto exit;
+	}
+	pehci_check("complete the td , length: %d\n", td_ptd_map->qtd->length);
+	urb_priv->timeout = 0;
+
+	if((td_ptd_map->state == TD_PTD_REMOVE	) ||
+		  (urb_priv->state == DELETE_URB) ||
+		     !HCD_IS_RUNNING(hcd->state)){
+	remove=1;
+	}
+
+
+	qh_state=qh->qh_state;
+
+	qh->qh_state = QH_STATE_COMPLETING;
+	/*remove the done tds */
+	spin_lock(&hcd_data_lock);
+	phci_hcd_urb_free_priv(hcd, urb_priv, qh);
+	spin_unlock(&hcd_data_lock);
+
+	urb_priv->timeout = 0;
+	kfree(urb_priv);
+	urb->hcpriv = 0;
+
+
+	/*if normal completion */
+	if (urb->status	== -EINPROGRESS) {
+		urb->status = 0;
+	}
+
+	if(remove)
+	if (list_empty(&qh->qtd_list)) {
+		phci_hcd_release_td_ptd_index(qh);
+	}
+	remove=0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+	if(!usb_hcd_check_unlink_urb(&hcd->usb_hcd, urb,0))
+	{
+		usb_hcd_unlink_urb_from_ep(&hcd->usb_hcd,urb);
+	}
+#endif
+	spin_unlock(&hcd->lock);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	usb_hcd_giveback_urb(&hcd->usb_hcd, urb);
+#else
+	usb_hcd_giveback_urb(&hcd->usb_hcd, urb, urb->status);
+#endif
+	spin_lock(&hcd->lock);
+exit:
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+	
+}
+
+/*update the error status of the td*/
+static void
+pehci_hcd_update_error_status(u32 ptdstatus, struct urb	*urb)
+{
+	/*if ptd status	is halted */
+	if (ptdstatus &	PTD_STATUS_HALTED) {
+		if (ptdstatus &	PTD_XACT_ERROR)	{
+			/*transaction error results due	to retry count goes to zero */
+			if (PTD_RETRY(ptdstatus)) {
+				/*halt the endpoint */
+				printk("transaction error , retries %d\n",
+					PTD_RETRY(ptdstatus));
+				urb->status = -EPIPE;
+			} else {
+				printk("transaction error , retries %d\n",
+					PTD_RETRY(ptdstatus));
+				/*protocol error */
+				urb->status = -EPROTO;
+			}
+		} else if (ptdstatus & PTD_BABBLE) {
+			printk("babble error, qha %x\n", ptdstatus);
+			/*babble error */
+			urb->status = -EOVERFLOW;
+		} else if (PTD_RETRY(ptdstatus)) {
+			printk("endpoint halted with retrie remaining %d\n",
+				PTD_RETRY(ptdstatus));
+			urb->status = -EPIPE;
+		} else {	/*unknown error, i will	report it as halted, as	i will never see xact error bit	set */
+			printk("protocol error, qha %x\n", ptdstatus);
+			urb->status = -EPIPE;
+		}
+
+		/*if halted need to recover */
+		if (urb->status	== -EPIPE) {
+		}
+	}
+}
+
+#ifdef CONFIG_ISO_SUPPORT	/* New code for	ISO support */
+
+/*******************************************************************
+ * phcd_iso_handler - ISOCHRONOUS Transfer handler
+ *
+ * phci_hcd *hcd,
+ *	Host controller	driver structure which contains	almost all data
+ *	needed by the host controller driver to	process	data and interact
+ *	with the host controller.
+ *
+ * struct pt_regs *regs
+ *
+ * API Description
+ * This	is the ISOCHRONOUS Transfer handler, mainly responsible	for:
+ *  - Checking the periodic list if there are any ITDs for scheduling or
+ *    removal.
+ *  - For ITD scheduling, converting an	ITD into a PTD,	which is the data
+ *    structure	that the host contrtoller can understand and process.
+ *  - For ITD completion, checking the transfer	status and performing the
+ *    required actions depending on status.
+ *  - Freeing up memory	used by	an ITDs	once it	is not needed anymore.
+ ************************************************************************/
+void 
+pehci_hcd_iso_sitd_schedule(phci_hcd *hcd,struct urb* urb,struct ehci_sitd* sitd){
+		td_ptd_map_t *td_ptd_map;
+		td_ptd_map_buff_t *ptd_map_buff;
+		struct _isp1763_isoptd *iso_ptd;
+		u32 ormask = 0, skip_map = 0,last_map=0,buff_stat=0;
+		struct isp1763_mem_addr *mem_addr;
+		ptd_map_buff = &(td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL]);
+		
+		/* Get the PTD allocated for this SITD. */
+		td_ptd_map =
+				&ptd_map_buff->map_list[sitd->
+					sitd_index];
+		iso_ptd = &hcd->isotd;
+		
+		memset(iso_ptd, 0,	sizeof(struct _isp1763_isoptd));
+		/* Read buffer status register to check later if the ISO buffer is
+		filled or not */
+		buff_stat =
+			isp1763_reg_read16(hcd->dev, hcd->regs.buffer_status,buff_stat);
+
+		/* Read the contents of the ISO skipmap register */
+		skip_map =
+			isp1763_reg_read16(hcd->dev, hcd->regs.isotdskipmap,
+				skip_map);
+		iso_dbg(ISO_DBG_DATA,
+			"[pehci_hcd_iso_sitd_schedule]: Read skip map: 0x%08x\n",
+			(unsigned int) skip_map);
+
+		/* Read the contents of the ISO lastmap  register */
+		last_map =
+			isp1763_reg_read16(hcd->dev, hcd->regs.isotdlastmap,
+			last_map);
+
+		/* Read the contents of the ISO ormask  register */
+		ormask = isp1763_reg_read16(hcd->dev, hcd->regs.iso_irq_mask_or,
+			ormask);
+		
+		/* Create a PTD from an SITD */
+		phcd_iso_sitd_to_ptd(hcd, sitd, sitd->urb,
+				(void *) iso_ptd);	
+		/* Indicate that this SITD's PTD have been
+		filled up */
+		ptd_map_buff->pending_ptd_bitmap &=
+			~td_ptd_map->ptd_bitmap;		
+
+				/*
+				 * Place the newly initialized ISO PTD structure into
+				 the location allocated for this PTD in the ISO PTD
+				 memory region.
+				 */
+#ifdef SWAP
+				isp1763_mem_write(hcd->dev,
+					td_ptd_map->ptd_header_addr, 0,
+					(u32 *) iso_ptd, PHCI_QHA_LENGTH, 0,
+					PTD_HED);
+#else /* NO_SWAP */
+				isp1763_mem_write(hcd->dev,
+					td_ptd_map->ptd_header_addr, 0,
+					(u32 *) iso_ptd,PHCI_QHA_LENGTH, 0);
+#endif
+
+				/*
+ 				* Set this flag to avoid unlinking before
+ 				schedule at particular frame number
+				 */
+				td_ptd_map->state = TD_PTD_IN_SCHEDULE;
+
+				/*
+				 * If the length is not zero and the direction is
+				 OUT then  copy the  data to be transferred
+				 into the PAYLOAD memory area.
+				 */
+				if (sitd->length) {
+					switch (PTD_PID(iso_ptd->td_info2)) {
+					case OUT_PID:
+						/* Get the Payload memory
+						allocated for this PTD */
+						mem_addr = &sitd->mem_addr;
+#ifdef SWAP
+						isp1763_mem_write(hcd->dev,
+							(unsigned long)
+							mem_addr-> phy_addr,
+							0, (u32*)
+							((sitd->hw_bufp[0])),
+							sitd->length, 0,
+							PTD_PAY);
+#else /* NO_SWAP */
+						isp1763_mem_write(hcd->dev,
+							(unsigned long)
+							mem_addr->phy_addr,
+							0, (u32 *)
+							sitd->hw_bufp[0],
+							sitd->length, 0);
+#endif
+						break;
+					}
+					/* switch(PTD_PID(iso_ptd->td_info2))*/
+				}
+
+				/* if(sitd->length) */
+				/* If this is the last td, indicate to complete
+				the URB */
+				if (sitd->hw_next == EHCI_LIST_END) {
+					td_ptd_map->lasttd = 1;
+				}
+
+				/*
+				 * Clear the bit corresponding to this PTD in
+				 the skip map so that it will be processed on
+				 the next schedule traversal.
+				 */
+				skip_map &= ~td_ptd_map->ptd_bitmap;
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_sitd_schedule]: Skip map:0x%08x\n",(unsigned int) skip_map);
+
+				/*
+				 * Update the last map register to indicate
+				 that the newly created PTD is the last PTD
+				 added only if it is larger than the previous
+				 bitmap.
+				 */
+				if (last_map < td_ptd_map->ptd_bitmap) {
+					isp1763_reg_write16(hcd->dev,
+						hcd->regs.isotdlastmap,
+						td_ptd_map->ptd_bitmap);
+					iso_dbg(ISO_DBG_DATA,
+						"[pehci_hcd_iso_sitd_schedule]:Last Map: 0x%08x\n",
+						td_ptd_map->ptd_bitmap);
+				}
+
+				/*
+				 * Set the ISO_BUF_FILL bit to 1 to indicate
+				 that there is a PTD for ISO that needs to
+				 * be processed.
+				 */
+				isp1763_reg_write16(hcd->dev,
+					hcd->regs.buffer_status,
+					(buff_stat | ISO_BUFFER));
+				
+				isp1763_reg_write16(hcd->dev, hcd->regs.isotdskipmap,skip_map);
+		
+}
+
+/*******************************************************************
+ * phcd_iso_handler - ISOCHRONOUS Transfer handler
+ *
+ * phci_hcd *hcd,
+ *	Host controller	driver structure which contains	almost all data
+ *	needed by the host controller driver to	process	data and interact
+ *	with the host controller.
+ *
+ * struct pt_regs *regs
+ *
+ * API Description
+ * This	is the ISOCHRONOUS Transfer handler, mainly responsible	for:
+ *  - Checking the periodic list if there are any ITDs for scheduling or
+ *    removal.
+ *  - For ITD scheduling, converting an	ITD into a PTD,	which is the data
+ *    structure	that the host contrtoller can understand and process.
+ *  - For ITD completion, checking the transfer	status and performing the
+ *    required actions depending on status.
+ *  - Freeing up memory	used by	an ITDs	once it	is not needed anymore.
+ ************************************************************************/
+void
+pehci_hcd_iso_schedule(phci_hcd * hcd, struct urb *urb)
+{
+	struct list_head *sitd_itd_sched, *position;
+	struct ehci_itd *itd;
+	struct ehci_sitd *sitd;
+	td_ptd_map_t *td_ptd_map;
+	unsigned long last_map;
+	td_ptd_map_buff_t *ptd_map_buff;
+	struct _isp1763_isoptd *iso_ptd;
+	unsigned long buff_stat;
+	struct isp1763_mem_addr *mem_addr;
+	u32 ormask = 0, skip_map = 0;
+	u32 iNumofPkts;
+	unsigned int iNumofSlots = 0, mult = 0;
+	struct ehci_qh *qhead;
+
+	buff_stat = 0;
+	iso_dbg(ISO_DBG_ENTRY, "[pehci_hcd_iso_schedule]: Enter\n");
+	iso_ptd = &hcd->isotd;
+
+	last_map = 0;
+	/* Check if there are any ITDs scheduled  for processing */
+	if (hcd->periodic_sched == 0) {
+		return;
+	}
+	if (urb->dev->speed == USB_SPEED_HIGH) {
+		mult = usb_maxpacket(urb->dev, urb->pipe,
+				usb_pipeout(urb->pipe));
+		mult = 1 + ((mult >> 11) & 0x3);
+		iNumofSlots = NUMMICROFRAME / urb->interval;
+		/*number of PTDs need to schedule for this PTD */
+		iNumofPkts = (urb->number_of_packets / mult) / iNumofSlots;
+		if ((urb->number_of_packets / mult) % iNumofSlots != 0){
+			/*get remainder */
+			iNumofPkts += 1;
+		}
+	} else{
+		iNumofPkts = urb->number_of_packets;
+	}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	qhead = urb->hcpriv;
+#else
+	qhead = urb->ep->hcpriv;
+#endif
+	if (!qhead) {
+		iso_dbg(ISO_DBG_ENTRY,
+			"[pehci_hcd_iso_schedule]: Qhead==NULL\n");
+		return ;
+	}
+	ptd_map_buff = &(td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL]);
+
+	while (iNumofPkts > 0) {
+	/* Read buffer status register to check later if the ISO buffer is
+	filled or not */
+	buff_stat =
+		isp1763_reg_read16(hcd->dev, hcd->regs.buffer_status,buff_stat);
+
+		/* Read the contents of the ISO skipmap register */
+		skip_map =
+			isp1763_reg_read16(hcd->dev, hcd->regs.isotdskipmap,
+				skip_map);
+		iso_dbg(ISO_DBG_DATA,
+			"[pehci_hcd_iso_schedule]: Read skip map: 0x%08x\n",
+			(unsigned int) skip_map);
+
+		/* Read the contents of the ISO lastmap  register */
+		last_map =
+			isp1763_reg_read16(hcd->dev, hcd->regs.isotdlastmap,
+			last_map);
+
+		/* Read the contents of the ISO ormask  register */
+		ormask = isp1763_reg_read16(hcd->dev, hcd->regs.iso_irq_mask_or,
+			ormask);
+
+		/* Process ITDs linked to this frame, checking if there are any that needs to
+		be scheduled */
+		sitd_itd_sched = &qhead->periodic_list.sitd_itd_head;
+		if (list_empty(sitd_itd_sched)) {
+			iso_dbg(ISO_DBG_INFO,
+				"[pehci_hcd_iso_schedule]: ISO schedule list's empty. Nothing to schedule.\n");
+			return;
+		}
+
+		list_for_each(position, sitd_itd_sched) {
+			if (qhead->periodic_list.high_speed == 0){
+				/* Get an SITD in the list for processing */
+				sitd = list_entry(position, struct ehci_sitd,
+					sitd_list);
+				iNumofPkts--;
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_schedule]: SITD Index:%d\n", sitd->sitd_index);
+				if(sitd->sitd_index==TD_PTD_INV_PTD_INDEX)
+					continue;
+				/* Get the PTD allocated for this SITD. */
+				td_ptd_map =
+					&ptd_map_buff->map_list[sitd->
+					sitd_index];
+				memset(iso_ptd, 0,
+					sizeof(struct _isp1763_isoptd));
+
+				/* Create a PTD from an SITD */
+				phcd_iso_sitd_to_ptd(hcd, sitd, sitd->urb,
+					(void *) iso_ptd);
+
+				/* Indicate that this SITD's PTD have been
+				filled up */
+				ptd_map_buff->pending_ptd_bitmap &=
+					~td_ptd_map->ptd_bitmap;
+
+				/*
+				 * Place the newly initialized ISO PTD structure into
+				 the location allocated for this PTD in the ISO PTD
+				 memory region.
+				 */
+#ifdef SWAP
+				isp1763_mem_write(hcd->dev,
+					td_ptd_map->ptd_header_addr, 0,
+					(u32 *) iso_ptd, PHCI_QHA_LENGTH, 0,
+					PTD_HED);
+#else /* NO_SWAP */
+				isp1763_mem_write(hcd->dev,
+					td_ptd_map->ptd_header_addr, 0,
+					(u32 *) iso_ptd,PHCI_QHA_LENGTH, 0);
+#endif
+
+				/*
+ 				* Set this flag to avoid unlinking before
+ 				schedule at particular frame number
+				 */
+				td_ptd_map->state = TD_PTD_IN_SCHEDULE;
+
+				/*
+				 * If the length is not zero and the direction is
+				 OUT then  copy the  data to be transferred
+				 into the PAYLOAD memory area.
+				 */
+				if (sitd->length) {
+					switch (PTD_PID(iso_ptd->td_info2)) {
+					case OUT_PID:
+						/* Get the Payload memory
+						allocated for this PTD */
+						mem_addr = &sitd->mem_addr;
+#ifdef SWAP
+						isp1763_mem_write(hcd->dev,
+							(unsigned long)
+							mem_addr-> phy_addr,
+							0, (u32*)
+							((sitd->hw_bufp[0])),
+							sitd->length, 0,
+							PTD_PAY);
+#else /* NO_SWAP */
+						isp1763_mem_write(hcd->dev,
+							(unsigned long)
+							mem_addr->phy_addr,
+							0, (u32 *)
+							sitd->hw_bufp[0],
+							sitd->length, 0);
+#endif
+						break;
+					}
+					/* switch(PTD_PID(iso_ptd->td_info2))*/
+				}
+
+				/* if(sitd->length) */
+				/* If this is the last td, indicate to complete
+				the URB */
+				if (sitd->hw_next == EHCI_LIST_END) {
+					td_ptd_map->lasttd = 1;
+				}
+
+				/*
+				 * Clear the bit corresponding to this PTD in
+				 the skip map so that it will be processed on
+				 the next schedule traversal.
+				 */
+				skip_map &= ~td_ptd_map->ptd_bitmap;
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_schedule]: Skip map:0x%08x\n",(unsigned int) skip_map);
+
+				/*
+				 * Update the last map register to indicate
+				 that the newly created PTD is the last PTD
+				 added only if it is larger than the previous
+				 bitmap.
+				 */
+				if (last_map < td_ptd_map->ptd_bitmap) {
+					isp1763_reg_write16(hcd->dev,
+						hcd->regs.isotdlastmap,
+						td_ptd_map->ptd_bitmap);
+					iso_dbg(ISO_DBG_DATA,
+						"[pehci_hcd_iso_schedule]:Last Map: 0x%08x\n",
+						td_ptd_map->ptd_bitmap);
+				}
+
+				/*
+				 * Set the ISO_BUF_FILL bit to 1 to indicate
+				 that there is a PTD for ISO that needs to
+				 * be processed.
+				 */
+				isp1763_reg_write16(hcd->dev,
+					hcd->regs.buffer_status,
+					(buff_stat | ISO_BUFFER));
+
+			} else {	/*HIGH SPEED */
+
+				/* Get an ITD in the list for processing */
+				itd = list_entry(position, struct ehci_itd,
+					itd_list);
+				iNumofPkts--;
+
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_schedule]: ITD Index: %d\n",	itd->itd_index);
+				/* Get the PTD allocated for this ITD. */
+				td_ptd_map =
+					&ptd_map_buff->map_list[itd->itd_index];
+				memset(iso_ptd, 0,
+					sizeof(struct _isp1763_isoptd));
+
+				/* Create a PTD from an ITD */
+				phcd_iso_itd_to_ptd(hcd, itd, itd->urb,
+					(void *) iso_ptd);
+
+				/* Indicate that this SITD's PTD have been
+				filled up */
+				ptd_map_buff->pending_ptd_bitmap &=
+					~td_ptd_map->ptd_bitmap;
+
+				/*
+				 * Place the newly initialized ISO PTD
+				 structure into the location allocated
+				 * for this PTD in the ISO PTD memory region.
+				 */
+#ifdef SWAP
+				isp1763_mem_write(hcd->dev,
+					td_ptd_map->ptd_header_addr, 0,
+					(u32 *) iso_ptd,PHCI_QHA_LENGTH, 0,
+					PTD_HED);
+#else /* NO_SWAP */
+				isp1763_mem_write(hcd->dev,
+					td_ptd_map->ptd_header_addr, 0,
+					(u32 *) iso_ptd,PHCI_QHA_LENGTH, 0);
+#endif
+				/*
+				 * Set this flag to avoid unlinking before schedule
+				 * at particular frame number
+				 */
+				td_ptd_map->state = TD_PTD_IN_SCHEDULE;
+
+				/*
+				 * If the length is not zero and the direction
+				 is OUT then copy the data to be transferred
+				 into the PAYLOAD memory area.
+				 */
+				if (itd->length) {
+					switch (PTD_PID(iso_ptd->td_info2)) {
+					case OUT_PID:
+						/* Get the Payload memory
+						allocated for this PTD */
+						mem_addr = &itd->mem_addr;
+#ifdef SWAP
+						isp1763_mem_write(hcd->dev,
+							(unsigned long)
+							mem_addr->phy_addr, 0,
+							(u32*)
+							((itd->hw_bufp[0])),
+							itd->length, 0,
+							PTD_PAY);
+#else /* NO_SWAP */
+						isp1763_mem_write(hcd->dev,
+							(unsigned long)
+							mem_addr->phy_addr, 0,
+							(u32 *)itd->hw_bufp[0],
+							itd->length, 0);
+#endif
+						break;
+					}
+					/* switch(PTD_PID(iso_ptd->td_info2)) */
+				}
+
+				
+				/* If this is the last td, indicate to
+				complete the URB */
+				if (itd->hw_next == EHCI_LIST_END) {
+					td_ptd_map->lasttd = 1;
+				}
+
+				/*
+				 * Clear the bit corresponding to this PT D
+				 in the skip map so that it will be processed
+				 on the next schedule traversal.
+				 */
+				skip_map &= ~td_ptd_map->ptd_bitmap;
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_schedule]: Skip map:0x%08x\n",(unsigned int) skip_map);
+				isp1763_reg_write16(hcd->dev,
+					hcd->regs.isotdskipmap,
+					skip_map);
+
+				/*
+				 * Update the last map register to indicate
+				 that the newly created PTD is the last PTD
+				 added only if it is larger than the previous
+				 bitmap.
+				 */
+				if (last_map < td_ptd_map->ptd_bitmap) {
+					isp1763_reg_write16(hcd->dev,
+						hcd->regs.isotdlastmap,
+						td_ptd_map->ptd_bitmap);
+					iso_dbg(ISO_DBG_DATA,
+						"[pehci_hcd_iso_schedule]:Last Map: 0x%08x\n",
+						td_ptd_map->ptd_bitmap);
+				}
+
+				/*
+				 * Set the ISO_BUF_FILL bit to 1 to indicate
+				 that there is a PTD for ISO that needs to
+				 * be processed.
+				 */
+				isp1763_reg_write16(hcd->dev,
+					hcd->regs.buffer_status,
+					(buff_stat | ISO_BUFFER));
+			}
+		}		/* list_for_each(position, itd_sched) */
+		isp1763_reg_write16(hcd->dev, hcd->regs.isotdskipmap,skip_map);
+	}/*end of while (igNumOfPkts) */
+
+	iso_dbg(ISO_DBG_INFO,
+		"[pehci_hcd_iso_schedule]: ISO-Frame scheduling done\n");
+	iso_dbg(ISO_DBG_ENTRY, "[pehci_hcd_iso_schedule]: Exit\n");
+}
+
+/*******************************************************************
+ * phcd_iso_handler - ISOCHRONOUS Transfer handler
+ *
+ * phci_hcd *hcd,
+ *      Host controller driver structure which contains almost all data
+ *      needed by the host controller driver to process data and interact
+ *      with the host controller.
+ *
+ * struct pt_regs *regs
+ *
+ * API Description
+ * This is the ISOCHRONOUS Transfer handler, mainly responsible for:
+ *  - Checking the periodic list if there are any ITDs for scheduling or
+ *    removal.
+ *  - For ITD scheduling, converting an ITD into a PTD, which is the data
+ *    structure that the host contrtoller can understand and process.
+ *  - For ITD completion, checking the transfer status and performing the
+ *    required actions depending on status.
+ *  - Freeing up memory used by an ITDs once it is not needed anymore.
+ ************************************************************************/
+
+int debugiso = 0;
+
+void
+pehci_hcd_iso_worker(phci_hcd * hcd)
+{
+	u32 donemap = 0, skipmap = 0; /*ormask = 0,  buff_stat = 0;*/
+	u32 pendingmap = 0;
+	u32 mask = 0x1, index = 0, donetoclear = 0;
+	u32 uFrIndex = 0;
+	unsigned char last_td = FALSE, iReject = 0;
+	struct isp1763_mem_addr *mem_addr;
+	struct _isp1763_isoptd *iso_ptd;
+	unsigned long length = 0, uframe_cnt, usof_stat;
+	struct ehci_qh *qhead;
+	struct ehci_itd *itd, *current_itd;
+	struct ehci_sitd *sitd=0, *current_sitd=0;
+	td_ptd_map_t *td_ptd_map;
+	td_ptd_map_buff_t *ptd_map_buff;
+	struct list_head *sitd_itd_remove, *position;// *lst_temp;	
+	struct urb *urb;
+	u8 i = 0;
+	unsigned long startAdd = 0;
+	int ret = 0;
+
+
+	iso_ptd = &hcd->isotd;
+
+	/* Check if there are any ITDs scheduled  for processing */
+	if (hcd->periodic_sched == 0) {
+		goto exit;
+	}
+	ptd_map_buff = &(td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL]);
+	pendingmap = ptd_map_buff->pending_ptd_bitmap;
+
+
+	/*read the done map for interrupt transfers */
+	donemap = isp1763_reg_read16(hcd->dev, hcd->regs.isotddonemap, donemap);
+
+	iso_dbg(ISO_DBG_ENTRY, "[pehci_hcd_iso_worker]: Enter %x \n", donemap);
+	if (!donemap) {		/*there isnt any completed PTD */
+		goto exit;
+	}
+	donetoclear = donemap;
+	uFrIndex = 0;
+	while (donetoclear) {
+		mask = 0x1 << uFrIndex;
+		index = uFrIndex;
+		uFrIndex++;
+		if (!(donetoclear & mask))
+			continue;
+		donetoclear &= ~mask;
+		iso_dbg(ISO_DBG_DATA, "[pehci_hcd_iso_worker]: uFrIndex = %d\n", index);
+		iso_dbg(ISO_DBG_DATA,
+			"[pehci_hcd_iso_worker]:donetoclear = 0x%x mask = 0x%x\n",
+			donetoclear, mask);
+
+
+		if (ptd_map_buff->map_list[index].sitd) {
+			urb = ptd_map_buff->map_list[index].sitd->urb;
+			if (!urb) {
+				printk("ERROR : URB is NULL \n");
+				continue;
+			}
+			sitd = ptd_map_buff->map_list[index].sitd;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+			qhead=urb->hcpriv;
+#else
+			qhead = urb->ep->hcpriv;
+#endif
+			if (!qhead) {
+				printk("ERROR : Qhead is NULL \n");
+				continue;
+			}
+
+			sitd_itd_remove = &qhead->periodic_list.sitd_itd_head;
+		} else if (ptd_map_buff->map_list[index].itd) {
+			urb = ptd_map_buff->map_list[index].itd->urb;
+			if (!urb) {
+				printk("ERROR : URB is NULL \n");
+				continue;
+			}
+			itd = ptd_map_buff->map_list[index].itd;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+			qhead=urb->hcpriv;
+#else
+			qhead = urb->ep->hcpriv;
+#endif
+			if (!qhead) {
+				printk("ERROR : Qhead is NULL \n");
+				continue;
+			}
+
+			sitd_itd_remove = &qhead->periodic_list.sitd_itd_head;
+
+		} else {
+			printk("ERROR : NO sitd in that PTD location : \n");
+			continue;
+		}
+		/* Process ITDs linked to this frame, checking for completed ITDs */
+		iso_dbg(ISO_DBG_DATA,
+			"[pehci_hcd_iso_worker]: Removal Frame number: %d\n",
+			(int) index);
+		if (list_empty(sitd_itd_remove)) {
+			continue;
+		}
+
+		if (urb) {
+			last_td = FALSE;
+			if (qhead->periodic_list.high_speed == 0)/*FULL SPEED*/
+			{
+
+				/* Get the PTD that was allocated for this
+				particular SITD*/
+				td_ptd_map =
+					&ptd_map_buff->map_list[sitd->
+								sitd_index];
+
+				iso_dbg(ISO_DBG_INFO,
+					"[pehci_hcd_iso_worker]: PTD is done,%d\n",index);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: SITD Index: %d\n",sitd->sitd_index);
+				urb = sitd->urb;
+
+				/*
+				 * Get the base address of the memory allocated
+				 in the PAYLOAD region for this SITD
+				 */
+				mem_addr = &sitd->mem_addr;
+				memset(iso_ptd, 0,
+					sizeof(struct _isp1763_isoptd));
+
+				/*
+				 * Read this ptd from the ram address,
+				 address is in the td_ptd_map->ptd_header_addr
+				 */
+
+				isp1763_mem_read(hcd->dev,
+					td_ptd_map->ptd_header_addr,
+					0, (u32 *) iso_ptd,
+					PHCI_QHA_LENGTH, 0);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: DWORD0 = 0x%08x\n", iso_ptd->td_info1);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: DWORD1 = 0x%08x\n", iso_ptd->td_info2);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: DWORD2 = 0x%08x\n", iso_ptd->td_info3);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: DWORD3 = 0x%08x\n", iso_ptd->td_info4);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: DWORD4 = 0x%08x\n", iso_ptd->td_info5);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: DWORD5 = 0x%08x\n", iso_ptd->td_info6);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: DWORD6 = 0x%08x\n", iso_ptd->td_info7);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: DWORD7 = 0x%08x\n", iso_ptd->td_info8);
+
+				/* Go over the status of each of the 8 Micro Frames */
+				for (uframe_cnt = 0; uframe_cnt < 8;
+					uframe_cnt++) {
+					/*
+					 * We go over the status one at a time. The status bits and their
+					 * equivalent status are:
+					 * Bit 0 - Transaction Error (IN and OUT)
+					 * Bit 1 - Babble (IN token only)
+					 * Bit 2 - Underrun (OUT token only)
+					 */
+					usof_stat =
+						iso_ptd->td_info5 >> (8 +
+						(uframe_cnt * 3));
+
+					switch (usof_stat & 0x7) {
+					case INT_UNDERRUN:
+						iso_dbg(ISO_DBG_ERR,
+							"[pehci_hcd_iso_worker Error]: Buffer underrun\n");
+							urb->error_count++;
+						break;
+					case INT_EXACT:
+						iso_dbg(ISO_DBG_ERR,
+							"[pehci_hcd_iso_worker Error]: Transaction error\n");
+							printk("[pehci_hcd_iso_worker Error]: Transaction error\n");
+							urb->error_count++;
+						break;
+					case INT_BABBLE:
+						iso_dbg(ISO_DBG_ERR,
+							"[pehci_hcd_iso_worker Error]: Babble error\n");
+							printk("[pehci_hcd_iso_worker Error]: Babble error\n");
+						urb->iso_frame_desc[sitd->sitd_index].status
+							= -EOVERFLOW;
+						urb->error_count++;
+						break;
+					}	/* switch(usof_stat & 0x7) */
+				}	/* end of for( ulMicroFrmCnt = 0; ulMicroFrmCnt < 8; ulMicroFrmCnt++) */
+
+				/*
+				 * Get the number of bytes transferred. This indicates the number of
+				 * bytes sent or received for this transaction.
+				 */
+				if (urb->dev->speed != USB_SPEED_HIGH) {
+					/* Length is 1K for full/low speed device */
+					length = PTD_XFERRED_NONHSLENGTH
+						(iso_ptd->td_info4);
+				} else {
+					/* Length is 32K for high speed device */
+					length = PTD_XFERRED_LENGTH(iso_ptd->
+						td_info4);
+				}
+
+				/* Halted, need to finish all the transfer on this endpoint */
+				if (iso_ptd->td_info4 & PTD_STATUS_HALTED) {
+					iso_dbg(ISO_DBG_ERR,
+						"[pehci_hcd_iso_worker Error] PTD Halted\n");
+						printk("[pehci_hcd_iso_worker Error] PTD Halted\n");
+					/*
+					 * When there is an error, do not process the other PTDs.
+					 * Stop at the PTD with the error and remove all other PTDs.
+					 */
+					td_ptd_map->lasttd = 1;
+
+					/*
+					 * In case of halt, next transfer will start with toggle zero,
+					 * USB specs, 5.8.5
+					 */
+					td_ptd_map->datatoggle = 0;
+				}
+
+				/* if(iso_ptd->td_info4 & PTD_STATUS_HALTED) */
+				/* Update the actual length of the transfer from the data we got earlier */
+				urb->iso_frame_desc[sitd->index].actual_length =
+					length;
+
+				/* If the PTD have been executed properly the V bit should be cleared */
+				if (iso_ptd->td_info1 & QHA_VALID) {
+					iso_dbg(ISO_DBG_ERR,
+						"[pehci_hcd_iso_worker Error]: Valid bit not cleared\n");
+						printk("[pehci_hcd_iso_worker Error]: Valid bit not cleared\n");
+					urb->iso_frame_desc[sitd->index].
+						status = -ENOSPC;
+				} else {
+					urb->iso_frame_desc[sitd->index].
+						status = 0;
+				}
+
+				/* Check if this is the last SITD either due to some error or normal completion */
+				if ((td_ptd_map->lasttd)
+					|| (sitd->hw_next == EHCI_LIST_END)) {
+					last_td = TRUE;
+				}
+
+				/* Copy data to/from */
+				if (length && (length <= MAX_PTD_BUFFER_SIZE)) {
+					switch (PTD_PID(iso_ptd->td_info2)) {
+					case IN_PID:
+						/*
+						 * Get the data from the PAYLOAD area and place it into
+						 * the buffer provided by the requestor.
+						 */
+
+						isp1763_mem_read(hcd->dev,
+							(unsigned long)mem_addr->
+							phy_addr, 0,(u32 *)
+							sitd->hw_bufp[0],
+							length, 0);
+
+					case OUT_PID:
+						/*
+						 * urb->actual length was initialized to zero, so for the first
+						 * uFrame having it incremented immediately is not a problem.
+						 */
+						urb->actual_length += length;
+						break;
+					}/* switch(PTD_PID(iso_ptd->td_info2)) */
+				}
+				/* if(length && (length <= MAX_PTD_BUFFER_SIZE)) */
+//				removesitd:
+				/*read skip-map */
+				skipmap =
+					isp1763_reg_read16(hcd->dev,
+						hcd->regs.isotdskipmap,
+						skipmap);
+				iso_dbg(ISO_DBG_DATA,
+					"[%s] : read skipmap =0x%x\n",
+					__FUNCTION__, skipmap);
+				if (last_td == TRUE) {
+					/* Start removing the ITDs in the list */
+					while (1) {
+						/*
+						 * This indicates that we are processing the tail PTD.
+						 * Perform cleanup procedure on this last PTD
+						 */
+						if (sitd->hw_next == EHCI_LIST_END) {
+							td_ptd_map =
+								&ptd_map_buff->
+								map_list[sitd->
+								sitd_index];
+
+							/*
+							 * Free up our allocation in the PAYLOAD area so that others can use
+							 * it.
+							 */
+#ifndef COMMON_MEMORY
+							phci_hcd_mem_free
+								(&sitd->
+								mem_addr);
+#endif
+							/* Remove this SITD entry in the SITD list */
+							list_del(&sitd->
+								sitd_list);
+
+							/* Free up the memory allocated for the SITD structure */
+							qha_free(qha_cache,
+								sitd);
+
+							/* Indicate that the PTD we have used is now free */
+							td_ptd_map->state =
+								TD_PTD_NEW;
+							td_ptd_map->sitd = NULL;
+							td_ptd_map->itd = NULL;
+
+							/* Decrease the number of active PTDs scheduled */
+							hcd->periodic_sched--;
+
+							/* Skip this PTD during the next PTD processing. */
+							skipmap |=
+								td_ptd_map->ptd_bitmap;
+							isp1763_reg_write16
+								(hcd->dev,
+								hcd->regs.
+								isotdskipmap,
+								skipmap);
+
+							/* All ITDs in this list have been successfully removed. */
+							break;
+						} else {
+						/*
+						* This indicates that we stopped due to an error on a PTD that is
+						* not the last in the list. We need to free up this PTD as well as
+						* the PTDs after it.
+						*/
+						/*
+						 * Put the current SITD error onto this variable.
+						 * We will be unlinking this from the list and free up its
+						 * resources later.
+						 */
+							current_sitd = sitd;
+
+							td_ptd_map =
+								&ptd_map_buff->
+								map_list[sitd->
+								sitd_index];
+
+							/*
+							 * Get the next SITD, and place it to the sitd variable.
+							 * In a way we are moving forward in the SITD list.
+							 */
+							sitd = (struct ehci_sitd
+								*)
+								(current_sitd->
+								hw_next);
+							/* Free up the current SITD's resources */
+#ifndef COMMON_MEMORY
+							phci_hcd_mem_free
+								(&current_sitd->
+								 mem_addr);
+#endif
+							/* Remove this SITD entry in the SITD list */
+							list_del(&current_sitd->
+								sitd_list);
+
+							/* Free up the memory allocated for the SITD structure */
+							qha_free(qha_cache,
+								current_sitd);
+
+							/* Inidicate that the PTD we have used is now free */
+							td_ptd_map->state =
+								TD_PTD_NEW;
+							td_ptd_map->sitd = NULL;
+							td_ptd_map->itd = NULL;
+
+							/* Decrease the number of active PTDs scheduled */
+							hcd->periodic_sched--;
+
+							/* Sine it is done, skip this PTD during the next PTD processing. */
+							skipmap |=
+								td_ptd_map->
+								ptd_bitmap;
+							isp1763_reg_write16
+								(hcd->dev,
+								hcd->regs.
+								isotdskipmap,
+								skipmap);
+							/*
+							 * Start all over again until it gets to the tail of the
+							 * list of PTDs/ITDs
+							 */
+							continue;
+						}	/* else of if(sitd->hw_next == EHCI_LIST_END) */
+
+						/* It should never get here, but I put this as a precaution */
+						break;
+					}	/*end of while(1) */
+
+					/* Check if there were ITDs that were not processed due to the error */
+					if (urb->status == -EINPROGRESS) {
+						if ((urb->actual_length !=
+							urb->transfer_buffer_length)
+							&& (urb->transfer_flags &
+							URB_SHORT_NOT_OK)) {
+							iso_dbg(ISO_DBG_ERR,
+								"[pehci_hcd_iso_worker Error]: Short Packet\n");
+							urb->status =
+								-EREMOTEIO;
+						} else {
+							urb->status = 0;
+						}
+					}
+
+					urb->hcpriv = 0;
+					iso_dbg(ISO_DBG_DATA,
+						"[%s] : remain skipmap =0x%x\n",
+						__FUNCTION__, skipmap);
+#ifdef COMMON_MEMORY
+					phci_hcd_mem_free(&qhead->memory_addr);
+#endif
+					/* We need to unlock this here, since this was locked when we are called
+					 * from the interrupt handler */
+					spin_unlock(&hcd->lock);
+					/* Perform URB cleanup */
+					iso_dbg(ISO_DBG_INFO,
+						"[pehci_hcd_iso_worker] Complete a URB\n");
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+		if(!usb_hcd_check_unlink_urb(&hcd->usb_hcd, urb,0))
+					usb_hcd_unlink_urb_from_ep(&hcd->usb_hcd,
+						urb);
+#endif
+					hcd->periodic_more_urb = 0;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+						qhead=urb->hcpriv;
+					if (!list_empty(&qhead->ep->urb_list))
+#else
+					if (!list_empty(&urb->ep->urb_list))
+#endif
+						hcd->periodic_more_urb = 1;
+					
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+				usb_hcd_giveback_urb(&hcd->usb_hcd, urb);
+#else
+				usb_hcd_giveback_urb(&hcd->usb_hcd, urb, urb->status);
+#endif
+
+					spin_lock(&hcd->lock);
+					continue;
+				}
+
+				/* if( last_td == TRUE ) */
+				/*
+				 * If the last_td is not set then we do not need to check for errors and directly
+				 * proceed with the cleaning sequence.
+				 */
+				iso_dbg(ISO_DBG_INFO,
+					"[pehci_hcd_iso_worker]: last_td is not set\n");
+				/*update skipmap */
+				skipmap |= td_ptd_map->ptd_bitmap;
+				isp1763_reg_write16(hcd->dev,
+					hcd->regs.isotdskipmap,
+					skipmap);
+				iso_dbg(ISO_DBG_DATA,
+					"%s : remain skipmap =0x%x\n",
+					__FUNCTION__, skipmap);
+
+				/* Decrement the count of active PTDs */
+				hcd->periodic_sched--;
+				/*schedule next PTD for this URB */
+				if(qhead->actualptds<qhead->totalptds)
+				{
+					sitd_itd_remove = &qhead->periodic_list.sitd_itd_head;
+					/* find sitd to schedule */
+					list_for_each(position, sitd_itd_remove) {
+						
+						if (qhead->periodic_list.high_speed == 0){
+						/* Get an SITD in the list for processing */
+							current_sitd= list_entry(position, struct ehci_sitd,
+									sitd_list);		
+							if(current_sitd->sitd_index==TD_PTD_INV_PTD_INDEX)
+								break;
+						}	
+					}
+				      if(current_sitd->sitd_index==TD_PTD_INV_PTD_INDEX){
+					  	qhead->actualptds++;
+					/*allocate memory and PTD index */
+						memcpy(&current_sitd->mem_addr,&sitd->mem_addr,sizeof(struct isp1763_mem_addr));
+//				printk("current %x\n",sitd->sitd_index);
+						current_sitd->sitd_index=sitd->sitd_index;
+					/*schedule PTD */
+						td_ptd_map->sitd = current_sitd;
+						hcd->periodic_sched++;
+						pehci_hcd_iso_sitd_schedule(hcd, urb,current_sitd);
+				      }
+
+				/* Remove this SITD from the list of active ITDs */
+				list_del(&sitd->sitd_list);
+
+				/* Free up the memory we allocated for the SITD structure */
+				qha_free(qha_cache, sitd);
+
+					
+				}else{
+#ifndef COMMON_MEMORY
+				phci_hcd_mem_free(&sitd->mem_addr);
+#endif
+				/* Remove this SITD from the list of active ITDs */
+				list_del(&sitd->sitd_list);
+
+				/* Free up the memory we allocated for the SITD structure */
+				qha_free(qha_cache, sitd);
+
+				/*
+				 * Clear the bit associated with this PTD from the grouptdmap and
+				 * make this PTD available for other transfers
+				 */
+				td_ptd_map->state = TD_PTD_NEW;
+				td_ptd_map->sitd = NULL;
+				td_ptd_map->itd = NULL;
+
+				}		
+
+				
+				
+			}	else {	/*HIGH SPEED */
+
+				/* Get an ITD in the list for processing */
+				itd = ptd_map_buff->map_list[index].itd;
+
+				/* Get the PTD that was allocated for this particular ITD. */
+				td_ptd_map =
+					&ptd_map_buff->map_list[itd->itd_index];
+
+				iso_dbg(ISO_DBG_INFO,
+					"[pehci_hcd_iso_worker]: PTD is done , %d\n",
+					index);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: ITD Index: %d\n",
+					itd->itd_index);
+
+				urb = itd->urb;
+
+				/*
+				 * Get the base address of the memory allocated in the
+				 * PAYLOAD region for this ITD
+				 */
+				mem_addr = &itd->mem_addr;
+				memset(iso_ptd, 0,
+					sizeof(struct _isp1763_isoptd));
+
+				/*
+				 * Read this ptd from the ram address,address is in the
+				 * td_ptd_map->ptd_header_addr
+				 */
+
+				isp1763_mem_read(hcd->dev,
+					td_ptd_map->ptd_header_addr,
+					0, (u32 *) iso_ptd,
+					PHCI_QHA_LENGTH, 0);
+
+				/* 
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: DWORD0 =
+					0x%08x\n", iso_ptd->td_info1);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: DWORD1 =
+					0x%08x\n", iso_ptd->td_info2);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: DWORD2 =
+					0x%08x\n", iso_ptd->td_info3);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: DWORD3 =
+					0x%08x\n", iso_ptd->td_info4);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: DWORD4 =
+					0x%08x\n",iso_ptd->td_info5);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: DWORD5 =
+					0x%08x\n", iso_ptd->td_info6);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: DWORD6 =
+					0x%08x\n", iso_ptd->td_info7);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: DWORD7 =
+					0x%08x\n", iso_ptd->td_info8);
+				*/
+
+
+				/* If the PTD have been executed properly,
+				the V bit should be cleared */
+				if (iso_ptd->td_info1 & QHA_VALID) {
+					iso_dbg(ISO_DBG_ERR,
+						"[pehci_hcd_iso_worker Error]: Valid bit not cleared\n");
+					for(i = 0; i<itd->num_of_pkts; i++){
+						urb->iso_frame_desc[itd->index
+							+ i].status = -ENOSPC;
+					}
+				} else {
+					for (i = 0; i<itd->num_of_pkts; i++){
+						urb->iso_frame_desc[itd->index
+							+i].status = 0;
+					}
+				}
+
+				/* Go over the status of each of the 8 Micro Frames */
+				for (uframe_cnt = 0; (uframe_cnt < 8)
+					&& (uframe_cnt < itd->num_of_pkts);
+					uframe_cnt++) {
+					/*
+					 * We go over the status one at a time. The status bits and their
+					 * equivalent status are:
+					 * Bit 0 - Transaction Error (IN and OUT)
+					 * Bit 1 - Babble (IN token only)
+					 * Bit 2 - Underrun (OUT token only)
+					 */
+					usof_stat =
+						iso_ptd->td_info5 >> (8 +
+						(uframe_cnt * 3));
+
+					switch (usof_stat & 0x7) {
+					case INT_UNDERRUN:
+						iso_dbg(ISO_DBG_ERR,
+							"[pehci_hcd_iso_worker Error]: Buffer underrun\n");
+						urb->iso_frame_desc[itd->index +
+							uframe_cnt].
+						status = -ECOMM;
+						urb->error_count++;
+						break;
+					case INT_EXACT:
+						iso_dbg(ISO_DBG_ERR,
+							"[pehci_hcd_iso_worker Error]: %p Transaction error\n",
+							urb);
+						urb->iso_frame_desc[itd->index +
+							uframe_cnt].
+							status = -EPROTO;
+						urb->error_count++;
+						debugiso = 25;
+						break;
+					case INT_BABBLE:
+						iso_dbg(ISO_DBG_ERR,
+							"[pehci_hcd_iso_worker Error]: Babble error\n");
+						urb->iso_frame_desc[itd->index +
+							uframe_cnt].
+							status = -EOVERFLOW;
+						urb->error_count++;
+						break;
+					}/* switch(usof_stat & 0x7) */
+				}/* end of for( ulMicroFrmCnt = 0; ulMicroFrmCnt < 8; ulMicroFrmCnt++) */
+
+				/*
+				 * Get the number of bytes transferred. This indicates the number of
+				 * bytes sent or received for this transaction.
+				 */
+
+				/* Length is 32K for high speed device */
+				length = PTD_XFERRED_LENGTH(iso_ptd->td_info4);
+
+				/* Halted, need to finish all the transfer on this endpoint */
+				if (iso_ptd->td_info4 & PTD_STATUS_HALTED) {
+
+					iso_dbg(ISO_DBG_ERR,
+						"[pehci_hcd_iso_worker Error] PTD Halted\n");
+					printk("[pehci_hcd_iso_worker Error] PTD Halted===============\n");
+					/*
+					 * When there is an error, do not process the other PTDs.
+					 * Stop at the PTD with the error and remove all other PTDs.
+					 */
+					td_ptd_map->lasttd = 1;
+
+					/*
+					 * In case of halt, next transfer will start with toggle zero,
+					 * USB specs, 5.8.5
+					 */
+					td_ptd_map->datatoggle = 0;
+				}
+				/* if(iso_ptd->td_info4 & PTD_STATUS_HALTED) */
+				/* Update the actual length of the transfer from the data we got earlier */
+				if (PTD_PID(iso_ptd->td_info2) == OUT_PID) {
+					for (i = 0; i < itd->num_of_pkts; i++){
+						urb->iso_frame_desc[itd->index +
+						i].actual_length =(unsigned int)
+						length / itd->num_of_pkts;
+					}
+				} else{
+					iso_dbg(ISO_DBG_DATA,
+						"itd->num_of_pkts = %d, itd->ssplit = %x\n",
+						itd->num_of_pkts, itd->ssplit);
+					urb->iso_frame_desc[itd->index +
+						0].actual_length =
+						iso_ptd->td_info6 & 0x00000FFF;
+					iso_dbg(ISO_DBG_DATA,
+						"actual length[0] = %d\n",
+						urb->iso_frame_desc[itd->index +0].
+						actual_length);
+
+					if((itd->num_of_pkts > 1)
+						&& ((itd->ssplit & 0x2) == 0x2)
+						&& (urb->iso_frame_desc[itd->index +
+						1].status ==0)) {
+						
+						urb->iso_frame_desc[itd->index +1].
+							actual_length =	(iso_ptd->
+							td_info6 & 0x00FFF000)>> 12;
+
+						iso_dbg(ISO_DBG_DATA,
+							"actual length[1] = %d\n",
+							urb->
+							iso_frame_desc[itd->
+							index + 1].
+							actual_length);
+					}else{
+						urb->iso_frame_desc[itd->index +1].
+							actual_length = 0;
+					}
+
+					if ((itd->num_of_pkts > 2)
+						&& ((itd->ssplit & 0x4) == 0x4)
+						&& (urb->
+						iso_frame_desc[itd->index +
+						2].status ==0)) {
+						
+						urb->iso_frame_desc[itd->index +
+							2].actual_length =
+							((iso_ptd->td_info6 &
+							0xFF000000 )>> 24)
+							| ((iso_ptd->td_info7
+							& 0x0000000F)<< 8);
+						
+						iso_dbg(ISO_DBG_DATA,
+							"actual length[2] = %d\n",
+							urb->iso_frame_desc[itd->
+							index + 2].actual_length);
+					} else{
+						urb->iso_frame_desc[itd->index +2].
+							actual_length = 0;
+					}
+
+					if ((itd->num_of_pkts > 3)
+						&& ((itd->ssplit & 0x8) == 0x8)
+						&& (urb->iso_frame_desc[itd->index +
+						3].status == 0)) {
+
+						urb->iso_frame_desc[itd->index + 3].
+							actual_length =(iso_ptd->
+							td_info7 & 0x0000FFF0)>> 4;
+
+						iso_dbg(ISO_DBG_DATA,
+							"actual length[3] = %d\n",
+							urb->iso_frame_desc[itd->
+							index + 3].actual_length);
+					} else {
+						urb->iso_frame_desc[itd->index +3].
+							actual_length = 0;
+					}
+
+					if ((itd->num_of_pkts > 4)
+						&& ((itd->ssplit & 0x10) == 0x10)
+						&& (urb->
+						iso_frame_desc[itd->index +
+						4].status ==0)) {
+
+						urb->iso_frame_desc[itd->index +
+							4].actual_length =
+							(iso_ptd->
+							td_info7 & 0x0FFF0000) >> 16;
+
+						iso_dbg(ISO_DBG_DATA,
+							"actual length[4] = %d\n",
+							urb->iso_frame_desc[itd->index +
+							4].actual_length);
+					} else {
+						urb->iso_frame_desc[itd->index +
+							4].actual_length = 0;
+					}
+
+					if ((itd->num_of_pkts > 5)
+						&& ((itd->ssplit & 0x20) == 0x20)
+						&& (urb->
+						iso_frame_desc[itd->index +
+						5].status ==
+						0)) {
+
+						urb->iso_frame_desc[itd->index +
+							5].actual_length =
+							((iso_ptd->
+							td_info7 & 0xF0000000) >> 28) | 
+							((iso_ptd->td_info8 &
+							0x000000FF)
+							<< 4);
+
+						iso_dbg(ISO_DBG_DATA,
+							"actual length[5] = %d\n",
+							urb->
+							iso_frame_desc[itd->
+							index +
+							5].actual_length);
+					} else {
+						urb->iso_frame_desc[itd->index +
+							5].actual_length = 0;
+					}
+
+					if ((itd->num_of_pkts > 6)
+						&& ((itd->ssplit & 0x40) == 0x40)
+						&& (urb->
+						iso_frame_desc[itd->index +
+						6].status ==0)) {
+
+						urb->iso_frame_desc[itd->index +
+							6].actual_length =
+							(iso_ptd->
+							td_info8 & 0x000FFF00)
+							>> 8;
+						
+						iso_dbg(ISO_DBG_DATA,
+							"actual length[6] = %d\n",
+							urb->
+							iso_frame_desc[itd->
+							index +
+							6].actual_length);
+					} else {
+						urb->iso_frame_desc[itd->index +
+							6].actual_length = 0;
+					}
+
+					if ((itd->num_of_pkts > 7)
+						&& ((itd->ssplit & 0x80) == 0x80)
+						&& (urb->
+						iso_frame_desc[itd->index +
+						7].status ==
+						0)) {
+
+						urb->iso_frame_desc[itd->index +
+							7].actual_length =
+							(iso_ptd->
+							td_info8 & 0xFFF00000) >> 20;
+
+						iso_dbg(ISO_DBG_DATA,
+							"actual length[7] = %d\n",
+							urb->
+							iso_frame_desc[itd->
+							index +
+							7].actual_length);
+					} else {
+						urb->iso_frame_desc[itd->index +
+							7].actual_length = 0;
+					}
+				}
+				/* Check if this is the last ITD either due to some error or normal completion */
+				if ((td_ptd_map->lasttd)
+					|| (itd->hw_next == EHCI_LIST_END)) {
+
+					last_td = TRUE;
+
+				}
+
+				/* Copy data to/from */
+				if (length && (length <= MAX_PTD_BUFFER_SIZE)) {
+					switch (PTD_PID(iso_ptd->td_info2)) {
+					case IN_PID:
+						/*
+						 * Get the data from the PAYLOAD area and place it into
+						 * the buffer provided by the requestor.
+						 */
+						/*for first packet*/
+						startAdd = mem_addr->phy_addr;
+						iso_dbg(ISO_DBG_DATA,
+							"start add = %ld hw_bufp[0] = 0x%08x length = %d\n",
+							startAdd,
+							itd->hw_bufp[0],
+							urb->
+							iso_frame_desc[itd->
+							index].actual_length);
+						if (urb->
+							iso_frame_desc[itd->index].
+							status == 0) {
+
+							if (itd->hw_bufp[0] ==0) {
+								dma_addr_t
+									buff_dma;
+
+								buff_dma =
+									(u32) ((unsigned char *) urb->transfer_buffer +
+									urb->iso_frame_desc[itd->index].offset);
+								itd->buf_dma =
+									buff_dma;
+								itd->hw_bufp[0]
+									=
+									buff_dma;
+							}
+							if (itd->hw_bufp[0] !=0) {
+
+								ret = isp1763_mem_read(hcd->dev, (unsigned long)
+									startAdd,
+									0,(u32*)itd->
+									hw_bufp[0],
+									urb->
+									iso_frame_desc
+									[itd->
+									index].
+									actual_length,
+									0);
+
+							} else {
+								printk("isp1763_mem_read data payload fail\n");
+								printk("start add = %ld hw_bufp[0] = 0x%08x length = %d\n",
+									startAdd, itd->hw_bufp[0],
+									urb->iso_frame_desc[itd->index].actual_length);
+								urb->iso_frame_desc[itd->index].status = -EPROTO;
+								urb->error_count++;
+							}
+						}
+
+
+						for (i = 1;
+							i < itd->num_of_pkts;
+							i++) {
+							startAdd +=
+								(unsigned
+								long) (urb->
+								iso_frame_desc
+								[itd->
+								index +
+								i - 1].
+								actual_length);
+
+							iso_dbg(ISO_DBG_DATA,
+								"start add = %ld hw_bufp[%d] = 0x%08x length = %d\n",
+								startAdd, i,
+								itd->hw_bufp[i],
+								urb->
+								iso_frame_desc
+								[itd->index +
+								i].
+								actual_length);
+							if (urb->
+								iso_frame_desc[itd->
+								index + i].
+								status == 0) {
+
+								isp1763_mem_read
+									(hcd->dev,
+									startAdd,
+									0,(u32*)
+									itd->
+									hw_bufp
+									[i],urb->
+									iso_frame_desc
+									[itd->
+									index + i].
+									actual_length,
+									0);
+
+								if (ret == -EINVAL){
+									printk("isp1763_mem_read data payload fail %d\n", i);
+								}
+							}
+						}
+
+					case OUT_PID:
+						/*
+						 * urb->actual length was initialized to zero, so for the first
+						 * uFrame having it incremented immediately is not a problem.
+						 */
+						urb->actual_length += length;
+						break;
+					}	/* switch(PTD_PID(iso_ptd->td_info2)) */
+				}
+
+				/* if(length && (length <= MAX_PTD_BUFFER_SIZE)) */
+//				removeitd:
+				/*read skip-map */
+				skipmap =
+					isp1763_reg_read16(hcd->dev,
+						hcd->regs.isotdskipmap,
+						skipmap);
+
+				iso_dbg(ISO_DBG_DATA,
+					"[%s] : read skipmap =0x%x\n",
+					__FUNCTION__, skipmap);
+				if (last_td == TRUE) {
+					/* Start removing the ITDs in the list */
+					while (1) {
+						/*
+						 * This indicates that we are processing the tail PTD.
+						 * Perform cleanup procedure on this last PTD
+						 */
+						if (itd->hw_next ==
+							EHCI_LIST_END) {
+							td_ptd_map =
+							&ptd_map_buff->
+							map_list[itd->
+							itd_index];
+
+							/*
+							 * Free up our allocation in the PAYLOAD area so that others can use
+							 * it.
+							 */
+#ifndef COMMON_MEMORY
+							phci_hcd_mem_free(&itd->
+								mem_addr);
+#endif
+
+							/* Remove this ITD entry in the ITD list */
+							list_del(&itd->
+								itd_list);
+
+							/* Free up the memory allocated for the ITD structure */
+							qha_free(qha_cache,
+								itd);
+
+							/* Indicate that the PTD we have used is now free */
+							td_ptd_map->state =
+								TD_PTD_NEW;
+							td_ptd_map->sitd = NULL;
+							td_ptd_map->itd = NULL;
+
+							/* Decrease the number of active PTDs scheduled */
+							hcd->periodic_sched--;
+
+							/* Skip this PTD during the next PTD processing. */
+							skipmap |=
+								td_ptd_map->
+								ptd_bitmap;
+
+							isp1763_reg_write16
+								(hcd->dev,
+								hcd->regs.
+								isotdskipmap,
+								skipmap);
+
+							/* All ITDs in this list have been successfully removed. */
+							break;
+						}
+						/* if(itd->hw_next == EHCI_LIST_END) */
+						/*
+						 * This indicates that we stopped due to an error on a PTD that is
+						 * not the last in the list. We need to free up this PTD as well as
+						 * the PTDs after it.
+						 */
+						else {
+							/*
+							 * Put the current ITD error onto this variable.
+							 * We will be unlinking this from the list and free up its
+							 * resources later.
+							 */
+							current_itd = itd;
+
+							td_ptd_map =
+								&ptd_map_buff->
+								map_list[itd->
+								itd_index];
+
+							/*
+							 * Get the next ITD, and place it to the itd variable.
+							 * In a way we are moving forward in the ITD list.
+							 */
+							itd = (struct ehci_itd
+								*) (current_itd->
+								hw_next);
+#ifndef COMMON_MEMORY
+							/* Free up the current ITD's resources */
+							phci_hcd_mem_free
+								(&current_itd->
+								mem_addr);
+#endif
+
+							/* Remove this ITD entry in the ITD list */
+							list_del(&current_itd->
+								itd_list);
+
+							/* Free up the memory allocated for the ITD structure */
+							qha_free(qha_cache,
+								current_itd);
+
+							/* Inidicate that the PTD we have used is now free */
+							td_ptd_map->state =
+								TD_PTD_NEW;
+							td_ptd_map->sitd = NULL;
+							td_ptd_map->itd = NULL;
+
+							/* Decrease the number of active PTDs scheduled */
+							hcd->periodic_sched--;
+
+							/* Sine it is done, skip this PTD during the next PTD processing. */
+							skipmap |=
+								td_ptd_map->
+								ptd_bitmap;
+							isp1763_reg_write16
+								(hcd->dev,
+								hcd->regs.
+								isotdskipmap,
+								skipmap);
+							/*
+							 * Start all over again until it gets to the tail of the
+							 * list of PTDs/ITDs
+							 */
+							continue;
+						}/* else of if(itd->hw_next == EHCI_LIST_END) */
+						/* It should never get here, but I put this as a precaution */
+						break;
+					}	/*end of while(1) */
+					/* Check if there were ITDs that were not processed due to the error */
+					if (urb->status == -EINPROGRESS) {
+						if ((urb->actual_length !=
+							urb->transfer_buffer_length)
+							&& (urb->
+							transfer_flags &
+							URB_SHORT_NOT_OK)) {
+
+							iso_dbg(ISO_DBG_ERR,
+							"[pehci_hcd_iso_worker Error]: Short Packet\n");
+
+							urb->status =
+								-EREMOTEIO;
+						} else {
+							urb->status = 0;
+						}
+					}
+
+					urb->hcpriv = 0;
+					iso_dbg(ISO_DBG_DATA,
+						"[%s] : remain skipmap =0x%x\n",
+						__FUNCTION__, skipmap);
+
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
+//					if (urb->reject.counter) {
+					if (unlikely(atomic_read(&urb->reject))) {// kernel reference code hcd.c
+						iso_dbg("ISO_DBG_INFO, [%s] urb reject\n", __FUNCTION__);
+						iReject = 1;
+					}
+#else
+					if (unlikely(urb->reject)) {
+						iso_dbg("ISO_DBG_INFO, [%s] urb reject\n", __FUNCTION__);
+						iReject = 1;
+					}
+#endif
+
+/*
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28)
+
+					if (urb->reject.counter) {
+						iso_dbg("ISO_DBG_INFO, [%s] urb reject\n", __FUNCTION__);
+						iReject = 1;
+					}
+#else
+				        if (unlikely(urb->reject)) {					       
+				
+					
+						iso_dbg("ISO_DBG_INFO, [%s] urb reject\n", __FUNCTION__);
+						iReject = 1;
+					}
+#endif
+*/
+
+#ifdef COMMON_MEMORY
+					phci_hcd_mem_free(&qhead->memory_addr);
+#endif
+					/* We need to unlock this here, since this was locked when we are called */
+					/* from the interrupt handler */
+					spin_unlock(&hcd->lock);
+					/* Perform URB cleanup */
+					iso_dbg(ISO_DBG_INFO,
+						"[pehci_hcd_iso_worker] Complete a URB\n");
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+		if(!usb_hcd_check_unlink_urb(&hcd->usb_hcd, urb,0))
+					usb_hcd_unlink_urb_from_ep(&hcd->usb_hcd, urb);
+#endif
+					hcd->periodic_more_urb = 0;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+						qhead=urb->hcpriv;
+					if (!list_empty(&qhead->ep->urb_list)){
+
+#else
+					if (!list_empty(&urb->ep->urb_list)){
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+						if (urb->hcpriv== periodic_ep[0]){
+#else
+						if (urb->ep == periodic_ep[0]){
+#endif
+							hcd->periodic_more_urb =
+							1;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+						} else if (urb->hcpriv==
+							 periodic_ep[1]){
+#else
+						} else if (urb->ep ==
+							 periodic_ep[1]){
+#endif							 
+							hcd->periodic_more_urb =
+							2;
+						} else {
+							hcd->periodic_more_urb =
+							0;
+						}
+
+
+					}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+				usb_hcd_giveback_urb(&hcd->usb_hcd, urb);
+#else
+				usb_hcd_giveback_urb(&hcd->usb_hcd, urb, 
+										urb->status);
+#endif
+
+					spin_lock(&hcd->lock);
+					continue;
+				}
+				/* if( last_td == TRUE ) */
+				/*
+				 * If the last_td is not set then we do not need to check for errors and directly
+				 * proceed with the cleaning sequence.
+				 */
+				iso_dbg(ISO_DBG_INFO,
+					"[pehci_hcd_iso_worker]: last_td is not set\n");
+				/*update skipmap */
+				skipmap |= td_ptd_map->ptd_bitmap;
+				isp1763_reg_write16(hcd->dev,
+					hcd->regs.isotdskipmap,
+					skipmap);
+				iso_dbg(ISO_DBG_DATA,
+					"%s : remain skipmap =0x%x\n",
+					__FUNCTION__, skipmap);
+
+				/* Decrement the count of active PTDs */
+				hcd->periodic_sched--;
+#ifndef COMMON_MEMORY
+				/* Free up the memory we allocated in the PAYLOAD area */
+				phci_hcd_mem_free(&itd->mem_addr);
+#endif
+				/* Remove this ITD from the list of active ITDs */
+				list_del(&itd->itd_list);
+
+				/* Free up the memory we allocated for the ITD structure */
+				qha_free(qha_cache, itd);
+				/*
+				 * Clear the bit associated with this PTD from the grouptdmap and
+				 * make this PTD available for other transfers
+				 */
+				td_ptd_map->state = TD_PTD_NEW;
+				td_ptd_map->sitd = NULL;
+				td_ptd_map->itd = NULL;
+			}	/*end of HIGH SPEED */
+		}		/* end of list_for_each_safe(position, lst_temp, itd_remove) */
+		iso_dbg(ISO_DBG_INFO,
+			"[pehci_hcd_iso_worker]: ISO-Frame removal done\n");
+
+
+	}			/* while donetoclear */
+
+
+	if (iReject) {
+		spin_unlock(&hcd->lock);
+		if (hcd->periodic_more_urb) {
+
+			if(periodic_ep[hcd->periodic_more_urb])
+			while (&periodic_ep[hcd->periodic_more_urb - 1]->
+				urb_list) {
+
+				urb = container_of(periodic_ep
+					[hcd->periodic_more_urb -
+					1]->urb_list.next,
+					struct urb, urb_list);
+				
+				if (urb) {
+					urb->status = -ENOENT;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+		if(!usb_hcd_check_unlink_urb(&hcd->usb_hcd, urb,0))
+					usb_hcd_unlink_urb_from_ep(&hcd->
+					usb_hcd,urb);
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+					usb_hcd_giveback_urb(&hcd->usb_hcd, urb);
+#else
+					usb_hcd_giveback_urb(&hcd->usb_hcd, urb,
+						urb->status);
+#endif
+				}
+			}
+		}
+
+		spin_lock(&hcd->lock);
+	}
+
+	/* When there is no more PTDs queued for scheduling or removal
+	 * clear the buffer status to indicate there are no more PTDs for
+	 * processing and set the skip map to 1 to indicate that the first
+	 * PTD is also the last PTD.
+	 */
+
+	if (hcd->periodic_more_urb) {
+		int status = 0;
+		iso_dbg(ISO_DBG_INFO,
+			"[phcd_iso_handler]: No more PTDs queued\n");
+		hcd->periodic_sched = 0;
+		phcd_store_urb_pending(hcd, hcd->periodic_more_urb, NULL,
+				       &status);
+		hcd->periodic_more_urb = 0;
+	}
+exit:
+	iso_dbg(ISO_DBG_ENTRY, "-- %s: Exit\n", __FUNCTION__);
+}				/* end of pehci_hcd_iso_worker */
+
+#endif /* CONFIG_ISO_SUPPORT */
+
+/*interrupt transfer handler*/
+/********************************************************
+  1. read done map
+  2. read the ptd to see any errors
+  3. copy the payload to and from
+  4. update ehci td
+  5. make new ptd if transfer there and earlier done
+  6. schedule
+ *********************************************************/
+static void
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+pehci_hcd_intl_worker(phci_hcd * hcd, struct pt_regs *regs)
+#else
+pehci_hcd_intl_worker(phci_hcd * hcd)
+#endif
+{
+	int i =	0;
+	u16 donemap = 0, donetoclear;
+	u16 mask = 0x1,	index =	0;
+	u16 pendingmap = 0;
+	u16 location = 0;
+	u32 length = 0;
+	u16 skipmap = 0;
+	u16 ormask = 0;
+	u32 usofstatus = 0;
+	struct urb *urb;
+	struct ehci_qtd	*qtd = 0;
+	struct ehci_qh *qh = 0;
+
+	struct _isp1763_qhint *qhint = &hcd->qhint;
+
+	td_ptd_map_t *td_ptd_map;
+	td_ptd_map_buff_t *ptd_map_buff;
+	struct isp1763_mem_addr	*mem_addr = 0;
+	u16 dontschedule = 0;
+
+	ptd_map_buff = &(td_ptd_map_buff[TD_PTD_BUFF_TYPE_INTL]);
+	pendingmap = ptd_map_buff->pending_ptd_bitmap;
+
+	/*read the done	map for	interrupt transfers */
+	donetoclear = donemap =
+		isp1763_reg_read16(hcd->dev, hcd->regs.inttddonemap, donemap);
+	if (donemap) {
+		/*skip done tds	*/
+		skipmap	=
+			isp1763_reg_read16(hcd->dev, hcd->regs.inttdskipmap,
+			skipmap);
+		skipmap	|= donemap;
+		isp1763_reg_write16(hcd->dev, hcd->regs.inttdskipmap, skipmap);
+		donemap	|= pendingmap;
+	}
+	/*if sof interrupt is enabled */
+#ifdef MSEC_INT_BASED
+	else {
+		/*if there is something	pending	, put this transfer in */
+		if (ptd_map_buff->pending_ptd_bitmap) {
+			pehci_hcd_schedule_pending_ptds(hcd, pendingmap, (u8)
+				TD_PTD_BUFF_TYPE_INTL,
+				1);
+		}
+		//return 0;
+		goto exit;
+	}
+#else
+	else {
+	goto exit;	
+	//return 0;
+	}
+
+#endif
+
+
+	ormask = isp1763_reg_read16(hcd->dev, hcd->regs.int_irq_mask_or,
+		ormask);
+	/*process all the endpoints first those	are done */
+	donetoclear = donemap;
+	while (donetoclear) {
+		/*index	is the number of endpoints open	currently */
+		index =	donetoclear & mask;
+		donetoclear &= ~mask;
+		mask <<= 1;
+		/*what if we are in the	middle of schedule
+		   where nothing is done */
+		if (!index) {
+			location++;
+			continue;
+		}
+
+		/*read our td_ptd_map */
+		td_ptd_map = &ptd_map_buff->map_list[location];
+
+		/*if this one is already in the	removal	*/
+		if (td_ptd_map->state == TD_PTD_REMOVE ||
+			td_ptd_map->state == TD_PTD_NEW) {
+			pehci_check("interrupt td is being removed\n");
+			/*this will be handled by urb_remove */
+			/*if this is last urb no need to complete it again */
+			donemap	&= ~td_ptd_map->ptd_bitmap;
+			/*if there is something	pending	*/
+			ptd_map_buff->pending_ptd_bitmap &=
+				~td_ptd_map->ptd_bitmap;
+			continue;
+		}
+
+
+		/*if we	found something	already	in */
+		if (!(skipmap &	td_ptd_map->ptd_bitmap)) {
+			pehci_check("intr td_ptd_map %x,skipnap	%x\n",
+			td_ptd_map->ptd_bitmap, skipmap);
+			donemap	&= ~td_ptd_map->ptd_bitmap;
+			/*in case pending */
+			ptd_map_buff->pending_ptd_bitmap &=
+				~td_ptd_map->ptd_bitmap;;
+			location++;
+			continue;
+		}
+
+
+		if (td_ptd_map->state == TD_PTD_NEW) {
+			pehci_check
+				("interrupt not	come here, map %x,location %d\n",
+				 td_ptd_map->ptd_bitmap, location);
+			donemap	&= ~td_ptd_map->ptd_bitmap;
+			/*in case pending */
+			ptd_map_buff->pending_ptd_bitmap &=
+				~td_ptd_map->ptd_bitmap;
+			donemap	&= ~td_ptd_map->ptd_bitmap;
+			location++;
+			continue;
+		}
+
+		/*move to the next schedule */
+		location++;
+		/*endpoint, td,	urb and	memory
+		 * for current transfer*/
+		qh = td_ptd_map->qh;
+		qtd = td_ptd_map->qtd;
+		if (qtd->state & QTD_STATE_NEW)	{
+			/*we need to schedule it */
+			goto schedule;
+		}
+		urb = qtd->urb;
+		mem_addr = &qtd->mem_addr;
+
+		/*clear	the irq	mask for this transfer */
+		ormask &= ~td_ptd_map->ptd_bitmap;
+		isp1763_reg_write16(hcd->dev, hcd->regs.int_irq_mask_or,
+			ormask);
+
+		ptd_map_buff->active_ptds--;
+		memset(qhint, 0, sizeof(struct _isp1763_qhint));
+
+		/*read this ptd	from the ram address,address is	in the
+		   td_ptd_map->ptd_header_addr */
+		isp1763_mem_read(hcd->dev, td_ptd_map->ptd_header_addr,	0,
+				 (u32 *) (qhint), PHCI_QHA_LENGTH, 0);
+
+#ifdef PTD_DUMP_COMPLETE
+		printk("INTL PTD header after COMPLETION\n");
+		printk("CDW0: 0x%08X\n", qhint->td_info1);
+		printk("CDW1: 0x%08X\n", qhint->td_info2);
+		printk("CDW2: 0x%08X\n", qhint->td_info3);
+		printk("CDW3: 0x%08X\n", qhint->td_info4);
+#endif
+
+		/*statuc of 8 uframes */
+		for (i = 0; i <	8; i++)	{
+			/*take care of errors */
+			usofstatus = qhint->td_info5 >>	(8 + i * 3);
+			switch (usofstatus & 0x7) {
+			case INT_UNDERRUN:
+				pehci_print("under run , %x\n",	usofstatus);
+				break;
+			case INT_EXACT:
+				pehci_print("transaction error,	%x\n",
+					    usofstatus);
+				break;
+			case INT_BABBLE:
+				pehci_print("babble error, %x\n", usofstatus);
+				break;
+			}
+		}
+
+		if (urb->dev->speed != USB_SPEED_HIGH) {
+			/*length is 1K for full/low speed device */
+			length = PTD_XFERRED_NONHSLENGTH(qhint->td_info4);
+		} else {
+			/*length is 32K	for high speed device */
+			length = PTD_XFERRED_LENGTH(qhint->td_info4);
+		}
+
+		pehci_hcd_update_error_status(qhint->td_info4, urb);
+		/*halted, need to finish all the transfer on this endpoint */
+		if (qhint->td_info4 & PTD_STATUS_HALTED) {
+			qtd->state |= QTD_STATE_LAST;
+			/*in case of halt, next	transfer will start with toggle	zero,
+			 *USB speck, 5.8.5*/
+			qh->datatoggle = td_ptd_map->datatoggle	= 0;
+			donemap	&= ~td_ptd_map->ptd_bitmap;
+			ptd_map_buff->pending_ptd_bitmap &=
+				~td_ptd_map->ptd_bitmap;
+			dontschedule = 1;
+			goto copylength;
+		}
+
+
+		copylength:
+		/*preserve the current data toggle */
+		qh->datatoggle = td_ptd_map->datatoggle	=
+			PTD_NEXTTOGGLE(qhint->td_info4);
+		/*copy data from the host */
+		switch (PTD_PID(qhint->td_info2)) {
+		case IN_PID:
+			if (length && (length <= MAX_PTD_BUFFER_SIZE))
+				/*do read only when there is somedata */
+				isp1763_mem_read(hcd->dev,
+					(u32) mem_addr->phy_addr, 0,
+					urb->transfer_buffer +
+					urb->actual_length, length, 0);
+
+		case OUT_PID:
+			urb->actual_length += length;
+			qh->hw_current = qtd->hw_next;
+			phci_hcd_mem_free(&qtd->mem_addr);
+			qtd->state &= ~QTD_STATE_NEW;
+			qtd->state |= QTD_STATE_DONE;
+			break;
+		}
+
+		if (qtd->state & QTD_STATE_LAST) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+			pehci_hcd_urb_complete(hcd, qh, urb, td_ptd_map, regs);
+#else
+			pehci_hcd_urb_complete(hcd, qh, urb, td_ptd_map);
+#endif
+			if (dontschedule) {	/*cleanup will start from drivers */
+				dontschedule = 0;
+				continue;
+			}
+
+			/*take the next	if in the queue	*/
+			if (!list_empty(&qh->qtd_list))	{
+				struct list_head *head;
+				/*last td of previous urb */
+				head = &qh->qtd_list;
+				qtd = list_entry(head->next, struct ehci_qtd,
+					qtd_list);
+				td_ptd_map->qtd	= qtd;
+				qh->hw_current = cpu_to_le32(qtd);
+				qh->qh_state = QH_STATE_LINKED;
+
+			} else {
+				td_ptd_map->qtd	=
+						 (struct ehci_qtd *) le32_to_cpu(0);
+				qh->hw_current = cpu_to_le32(0);
+				qh->qh_state = QH_STATE_IDLE;
+				donemap	&= ~td_ptd_map->ptd_bitmap;
+				ptd_map_buff->pending_ptd_bitmap &= 
+						~td_ptd_map->ptd_bitmap;
+	       			td_ptd_map->state=TD_PTD_NEW;
+				continue;
+			}
+
+		}
+
+		schedule:
+		{
+			/*current td comes from	qh->hw_current */
+			ptd_map_buff->pending_ptd_bitmap &=
+				~td_ptd_map->ptd_bitmap;
+			ormask |= td_ptd_map->ptd_bitmap;
+			ptd_map_buff->active_ptds++;
+			pehci_check
+				("inter	schedule next qtd %p, active tds %d\n",
+				 qtd, ptd_map_buff->active_ptds);
+			pehci_hcd_qtd_schedule(hcd, qtd, qh, td_ptd_map);
+		}
+
+	}			/*end of while */
+
+
+	/*clear	all the	tds inside this	routine	*/
+	skipmap	&= ~donemap;
+	isp1763_reg_write16(hcd->dev, hcd->regs.inttdskipmap, skipmap);
+	ormask |= donemap;
+	isp1763_reg_write16(hcd->dev, hcd->regs.int_irq_mask_or, ormask);
+exit:
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+	
+//	return (int)0;
+}
+
+/*atl(bulk/control) transfer handler*/
+/*1. read done map
+  2. read the ptd to see any errors
+  3. copy the payload to and from
+  4. update ehci td
+  5. make new ptd if transfer there and	earlier	done
+  6. schedule
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+static void
+pehci_hcd_atl_worker(phci_hcd * hcd, struct pt_regs *regs)
+#else
+static void
+pehci_hcd_atl_worker(phci_hcd * hcd)
+#endif
+{
+	u16 donemap = 0, donetoclear = 0;
+	u16 pendingmap = 0;
+	u32 rl = 0;
+	u16 mask = 0x1,	index =	0;
+	u16 location = 0;
+	u32 nakcount = 0;
+	u32 active = 0;
+	u32 length = 0;
+	u16 skipmap = 0;
+	u16 tempskipmap	= 0;
+	u16 ormask = 0;
+	struct urb *urb;
+	struct ehci_qtd	*qtd = 0;
+	struct ehci_qh *qh;
+	struct _isp1763_qha atlqha;
+	struct _isp1763_qha *qha;
+	td_ptd_map_t *td_ptd_map;
+	td_ptd_map_buff_t *ptd_map_buff;
+	urb_priv_t *urbpriv = 0;
+	struct isp1763_mem_addr	*mem_addr = 0;
+	u16 dontschedule = 0;
+	ptd_map_buff = &(td_ptd_map_buff[TD_PTD_BUFF_TYPE_ATL]);
+	pendingmap = ptd_map_buff->pending_ptd_bitmap;
+
+#ifdef MSEC_INT_BASED
+	/*running on skipmap rather donemap,
+	   some	cases donemap may not be set
+	   for complete	transfer
+	 */
+	skipmap	= isp1763_reg_read16(hcd->dev, hcd->regs.atltdskipmap, skipmap);
+	tempskipmap = ~skipmap;
+	tempskipmap &= 0xffff;
+
+	if (tempskipmap) {
+		donemap	=
+			isp1763_reg_read16(hcd->dev, hcd->regs.atltddonemap,
+					   donemap);
+		skipmap	|= donemap;
+		isp1763_reg_write16(hcd->dev, hcd->regs.atltdskipmap, skipmap);
+		qha = &atlqha;
+		donemap	|= pendingmap;
+		tempskipmap &= ~donemap;
+	}  else {
+
+	/*if sof interrupt enabled */
+
+		/*if there is something	pending	, put this transfer in */
+		if (pendingmap)	{
+			pehci_hcd_schedule_pending_ptds(hcd, pendingmap, (u8)
+				TD_PTD_BUFF_TYPE_ATL,
+				1);
+		}
+		goto exit;
+	}
+#else
+
+	donemap	= isp1763_reg_read16(hcd->dev, hcd->regs.atltddonemap, donemap);
+	if (donemap) {
+
+
+		pehci_info("DoneMap Value in ATL Worker	%x\n", donemap);
+		skipmap	=
+			isp1763_reg_read16(hcd->dev, hcd->regs.atltdskipmap,
+					   skipmap);
+		skipmap	|= donemap;
+		isp1763_reg_write16(hcd->dev, hcd->regs.atltdskipmap, skipmap);
+		qha = &atlqha;
+	} else {
+		pehci_info("Done Map Value is 0x%X \n",	donemap);
+		pehci_entry("--	%s: Exit abnormally with DoneMap all zero \n",
+			    __FUNCTION__);
+		goto exit;
+
+	}
+#endif
+
+	/*read the interrupt mask registers */
+	ormask = isp1763_reg_read16(hcd->dev, hcd->regs.atl_irq_mask_or,
+				    ormask);
+
+
+	/*this map is used only	to update and
+	 * scheduling for the tds who are not
+	 * complete. the tds those are complete
+	 * new schedule	will happen from
+	 * td_ptd_submit_urb routine
+	 * */
+	donetoclear = donemap;
+	/*we will be processing	skipped	tds also */
+	donetoclear |= tempskipmap;
+	/*process all the endpoints first those	are done */
+	while (donetoclear) {
+		/*index	is the number of endpoint open currently */
+		index =	donetoclear & mask;
+		donetoclear &= ~mask;
+		mask <<= 1;
+		/*what if we are in the	middle of schedule
+		   where nothing is done
+		 */
+		if (!index) {
+			location++;
+			continue;
+		}
+
+		/*read our td_ptd_map */
+		td_ptd_map = &ptd_map_buff->map_list[location];
+
+		/*urb is in remove */
+		if (td_ptd_map->state == TD_PTD_NEW ||
+			td_ptd_map->state == TD_PTD_REMOVE)	{
+			pehci_check
+				("atl td is being removed,map %x, skipmap %x\n",
+				 td_ptd_map->ptd_bitmap, skipmap);
+			pehci_check("temp skipmap %x, pendign map %x,done %x\n",
+				    tempskipmap, pendingmap, donemap);
+
+			/*unlink urb will take care of this */
+			donemap	&= ((~td_ptd_map->ptd_bitmap) &	0xffff);
+			/*in case pending */
+			ptd_map_buff->pending_ptd_bitmap &=
+				((~td_ptd_map->ptd_bitmap) & 0xffff);
+			location++;
+			continue;
+		}
+
+
+		/*move to the next endpoint */
+		location++;
+		/*endpoint, td,	urb and	memory
+		 * for current endpoint*/
+		qh = td_ptd_map->qh;
+		qtd = td_ptd_map->qtd;
+		if (!qh	|| !qtd) {
+			donemap	&= ((~td_ptd_map->ptd_bitmap) &	0xffff);
+			/*in case pending */
+			ptd_map_buff->pending_ptd_bitmap &=
+				((~td_ptd_map->ptd_bitmap) & 0xffff);
+			continue;
+		}
+#ifdef MSEC_INT_BASED
+		/*new td must be scheduled */
+		if ((qtd->state	& QTD_STATE_NEW)	/*&&
+							   (pendingmap & td_ptd_map->ptd_bitmap) */ ) {
+			/*this td will come here first time from
+			 *pending tds, so its qh->hw_current needs to
+			 * adjusted
+			 */
+			qh->hw_current = QTD_NEXT(qtd->qtd_dma);
+			goto schedule;
+		}
+#endif
+		urb = qtd->urb;
+		if (urb	== NULL) {
+			donemap	&= ((~td_ptd_map->ptd_bitmap) &	0xffff);
+			/*in case pending */
+			ptd_map_buff->pending_ptd_bitmap &=
+				((~td_ptd_map->ptd_bitmap) & 0xffff);
+			continue;
+		}
+		urbpriv	= (urb_priv_t *) urb->hcpriv;
+		mem_addr = &qtd->mem_addr;
+
+#ifdef MSEC_INT_BASED
+		/*check	here for the td	if its done */
+		if (donemap & td_ptd_map->ptd_bitmap) {
+			/*nothing to do	*/
+			;
+		} else {
+			/*if td	is not done, lets check	how long
+			   its been scheduled
+			 */
+			if (tempskipmap	& td_ptd_map->ptd_bitmap) {
+				/*i will give 20 msec to complete */
+				if (urbpriv->timeout < 20) {
+					urbpriv->timeout++;
+					continue;
+				}
+				urbpriv->timeout++;
+				/*otherwise check its status */
+			}
+
+		}
+#endif
+		memset(qha, 0, sizeof(struct _isp1763_qha));
+
+		/*read this ptd	from the ram address,address is	in the
+		   td_ptd_map->ptd_header_addr */
+		isp1763_mem_read(hcd->dev, td_ptd_map->ptd_header_addr,	0,
+				 (u32 *) (qha),	PHCI_QHA_LENGTH, 0);
+
+#ifdef PTD_DUMP_COMPLETE
+		printk("ATL PTD header after COMPLETION\n");
+		printk("CDW0: 0x%08X\n", qha->td_info1);
+		printk("CDW1: 0x%08X\n", qha->td_info2);
+		printk("CDW2: 0x%08X\n", qha->td_info3);
+		printk("CDW3: 0x%08X\n", qha->td_info4);
+#endif
+
+#ifdef MSEC_INT_BASED
+		/*since	we are running on skipmap
+		   tds will be checked for completion state
+		 */
+		if ((qha->td_info1 & QHA_VALID)) {
+
+			pehci_check
+				("pendign map %x, donemap %x, tempskipmap %x\n",
+				 pendingmap, donemap, tempskipmap);
+			/*this could be	one of the unprotected urbs, clear it */
+			ptd_map_buff->pending_ptd_bitmap &=
+				((~td_ptd_map->ptd_bitmap) & 0xffff);
+			/*here also we need to increment the tds timeout count */
+			urbpriv->timeout++;
+			continue;
+		} else {
+			/*this td is going to be done,
+			   this	td could be the	one un-skipped but no donemap or
+			   maybe it could be one of those where	we get unprotected urbs,
+			   so checking against tempskipmap may not give	us correct td
+			 */
+
+			skipmap	|= td_ptd_map->ptd_bitmap;
+			isp1763_reg_write16(hcd->dev, hcd->regs.atltdskipmap,
+					    skipmap);
+
+			/*of course this is going to be	as good
+			   as td that is done and donemap is set
+			   also	skipmap	is set
+			 */
+			donemap	|= td_ptd_map->ptd_bitmap;
+		}
+#endif
+		/*clear	the corrosponding mask register	*/
+		ormask &= ((~td_ptd_map->ptd_bitmap) & 0xffff);
+		isp1763_reg_write16(hcd->dev, hcd->regs.atl_irq_mask_or,
+			ormask);
+
+		ptd_map_buff->active_ptds--;
+
+		urbpriv->timeout = 0;
+
+		/*take care of errors */
+		pehci_hcd_update_error_status(qha->td_info4, urb);
+		/*halted, need to finish all the transfer on this endpoint */
+		if (qha->td_info4 & PTD_STATUS_HALTED) {
+
+			printk(KERN_NOTICE "Endpoint is	halted\n");
+			qtd->state |= QTD_STATE_LAST;
+
+			donemap	&= ((~td_ptd_map->ptd_bitmap) &	0xffff);
+			/*in case pending */
+			ptd_map_buff->pending_ptd_bitmap &=
+				((~td_ptd_map->ptd_bitmap) & 0xffff);
+			/*in case of halt, next	transfer will start with toggle
+			   zero,USB speck, 5.8.5 */
+			qh->datatoggle = td_ptd_map->datatoggle	= 0;
+			/*cleanup the ping */
+			qh->ping = 0;
+			/*force	cleanup	after this */
+			dontschedule = 1;
+			goto copylength;
+		}
+
+
+
+		/*read the reload count	*/
+		rl = (qha->td_info3 >> 23);
+		rl &= 0xf;
+
+
+
+		/*if there is a	transaction error and the status is not	halted,
+		 * process whatever the	length we got.if the length is what we
+		 * expected complete the transfer*/
+		if ((qha->td_info4 & PTD_XACT_ERROR) &&
+			!(qha->td_info4 & PTD_STATUS_HALTED) &&
+			(qha->td_info4 & QHA_ACTIVE)) {
+
+			if (PTD_XFERRED_LENGTH(qha->td_info4) == qtd->length) {
+				;	/*nothing to do	its fake */
+			} else {
+
+				pehci_print
+					("xact error, info1 0x%08x,info4 0x%08x\n",
+					 qha->td_info1,	qha->td_info4);
+
+				/*if this is the case then we need to
+				   resubmit the	td again */
+				qha->td_info1 |= QHA_VALID;
+				skipmap	&= ~td_ptd_map->ptd_bitmap;
+				ormask |= td_ptd_map->ptd_bitmap;
+				donemap	&= ((~td_ptd_map->ptd_bitmap) &	0xffff);
+
+				/*set the retry	count to 3 again */
+				qha->td_info4 |= (rl <<	19);
+				/*set the active bit, if cleared, will be cleared if we	have some length */
+				qha->td_info4 |= QHA_ACTIVE;
+
+				/*clear	the xact error */
+				qha->td_info4 &= ~PTD_XACT_ERROR;
+				isp1763_reg_write16(hcd->dev,
+						    hcd->regs.atl_irq_mask_or,
+						    ormask);
+
+				/*copy back into the header, payload is	already
+				 * present no need to write again
+				 */
+				isp1763_mem_write(hcd->dev,
+						  td_ptd_map->ptd_header_addr,
+						  0, (u32 *) (qha),
+						  PHCI_QHA_LENGTH, 0);
+				/*unskip this td */
+				isp1763_reg_write16(hcd->dev,
+						    hcd->regs.atltdskipmap,
+						    skipmap);
+				continue;
+			}
+			goto copylength;
+		}
+
+		/*check	for the	nak count and active condition
+		 * to reload the ptd if	needed*/
+		nakcount = qha->td_info4 >> 19;
+		nakcount &= 0xf;
+		active = qha->td_info4 & QHA_ACTIVE;
+		/*if nak count is zero and active bit is set , it
+		 *means	that device is naking and need to reload
+		 *the same td*/
+		if (!nakcount && active) {
+			pehci_info("%s:	ptd is going for reload,length %d\n",
+				   __FUNCTION__, length);
+			/*make this td valid */
+			qha->td_info1 |= QHA_VALID;
+			donemap	&= ((~td_ptd_map->ptd_bitmap & 0xffff));
+			/*just like fresh td */
+
+			/*set the retry	count to 3 again */
+			qha->td_info4 |= (rl <<	19);
+			qha->td_info4 &= ~0x3;
+			qha->td_info4 |= (0x2 << 23);
+			ptd_map_buff->active_ptds++;
+			skipmap	&= ((~td_ptd_map->ptd_bitmap) &	0xffff);
+			ormask |= td_ptd_map->ptd_bitmap;
+			isp1763_reg_write16(hcd->dev, hcd->regs.atl_irq_mask_or,
+					    ormask);
+			/*copy back into the header, payload is	already
+			 * present no need to write again */
+			isp1763_mem_write(hcd->dev, td_ptd_map->ptd_header_addr,
+					  0, (u32 *) (qha), PHCI_QHA_LENGTH, 0);
+			/*unskip this td */
+			isp1763_reg_write16(hcd->dev, hcd->regs.atltdskipmap,
+					    skipmap);
+			continue;
+		}
+
+		copylength:
+		/*read the length transferred */
+		length = PTD_XFERRED_LENGTH(qha->td_info4);
+
+
+		/*short	complete in case of BULK only */
+		if ((length < qtd->length) && usb_pipebulk(urb->pipe)) {
+
+			/*if current ptd is not	able to	fetech enough data as
+			 * been	asked then device has no data, so complete this	transfer
+			 * */
+			/*can we complete our transfer here */
+			if ((urb->transfer_flags & URB_SHORT_NOT_OK)) {
+				pehci_check
+					("short	read, length %d(expected %d)\n",
+					 length, qtd->length);
+				urb->status = -EREMOTEIO;
+				/*if this is the only td,donemap will be cleared
+				   at completion, otherwise take the next one
+				 */
+				donemap	&= ((~td_ptd_map->ptd_bitmap) &	0xffff);
+				ptd_map_buff->pending_ptd_bitmap &=
+					((~td_ptd_map->ptd_bitmap) & 0xffff);
+				/*force	the cleanup from here */
+				dontschedule = 1;
+			}
+
+			/*this will be the last	td,in case of short read/write */
+			/*donemap, pending maps	will be	handled	at the while scheduling	or completion */
+			qtd->state |= QTD_STATE_LAST;
+
+		}
+		/*preserve the current data toggle */
+		qh->datatoggle = td_ptd_map->datatoggle	=
+			PTD_NEXTTOGGLE(qha->td_info4);
+		qh->ping = PTD_PING_STATE(qha->td_info4);
+		/*copy data from */
+		switch (PTD_PID(qha->td_info2))	{
+		case IN_PID:
+			qh->ping = 0;
+			/*do read only when there is some data */
+			if (length && (length <= HC_ATL_PL_SIZE)) {
+				isp1763_mem_read(hcd->dev,
+						 (u32) mem_addr->phy_addr, 0,
+						 (u32*) (le32_to_cpu(qtd->hw_buf[0])), length, 0);
+#if 0
+			//	printk("IN PayLoad length:%d\n", length); 
+			if(length<=4)	{
+					int i=0;
+					int *data_addr= qtd->hw_buf[0];
+					printk("\n");
+					for(i=0;i<length;i+=4) printk("[0x%X] ",*data_addr++);
+					printk("\n");
+				}
+#endif
+			}
+
+		case OUT_PID:
+			urb->actual_length += length;
+			qh->hw_current = qtd->hw_next;
+			phci_hcd_mem_free(&qtd->mem_addr);
+			qtd->state |= QTD_STATE_DONE;
+
+			break;
+		case SETUP_PID:
+			qh->hw_current = qtd->hw_next;
+			phci_hcd_mem_free(&qtd->mem_addr);
+			qtd->state |= QTD_STATE_DONE;
+			break;
+		}
+
+		if (qtd->state & QTD_STATE_LAST) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+			pehci_hcd_urb_complete(hcd, qh, urb, td_ptd_map, regs);
+#else
+			pehci_hcd_urb_complete(hcd, qh, urb, td_ptd_map);
+#endif
+			if (dontschedule) {	/*cleanup will start from drivers */
+				dontschedule = 0;
+				/*so that we can take next one */
+				qh->qh_state = QH_STATE_TAKE_NEXT;
+				continue;
+			}
+			/*take the next	if in the queue	*/
+			if (!list_empty(&qh->qtd_list))	{
+				struct list_head *head;
+				/*last td of previous urb */
+				head = &qh->qtd_list;
+				qtd = list_entry(head->next, struct ehci_qtd,
+						 qtd_list);
+				td_ptd_map->qtd	= qtd;
+				qh->hw_current = cpu_to_le32(qtd);
+				qh->qh_state = QH_STATE_LINKED;
+
+			} else {
+				td_ptd_map->qtd	=
+					(struct	ehci_qtd *) le32_to_cpu(0);
+				qh->hw_current = cpu_to_le32(0);
+				qh->qh_state = QH_STATE_TAKE_NEXT;
+				donemap	&= ((~td_ptd_map->ptd_bitmap & 0xffff));
+				ptd_map_buff->pending_ptd_bitmap &=
+					((~td_ptd_map->ptd_bitmap) & 0xffff);
+				continue;
+			}
+		}
+
+#ifdef MSEC_INT_BASED
+		schedule:
+#endif
+		{
+			/*current td comes from	qh->hw_current */
+			ptd_map_buff->pending_ptd_bitmap &=
+				((~td_ptd_map->ptd_bitmap) & 0xffff);
+			td_ptd_map->qtd	=
+				(struct	ehci_qtd
+				 *) (le32_to_cpu(qh->hw_current));
+			qtd = td_ptd_map->qtd;
+			ormask |= td_ptd_map->ptd_bitmap;
+			ptd_map_buff->active_ptds++;
+			pehci_hcd_qtd_schedule(hcd, qtd, qh, td_ptd_map);
+		}
+
+	}			/*end of while */
+
+/*clear	all the	tds inside this	routine*/
+	skipmap	&= ((~donemap) & 0xffff);
+	isp1763_reg_write16(hcd->dev, hcd->regs.atltdskipmap, skipmap);
+	ormask |= donemap;
+	isp1763_reg_write16(hcd->dev, hcd->regs.atl_irq_mask_or, ormask);
+exit:
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+}
+
+/*--------------------------------------------------------*
+  root hub functions
+ *--------------------------------------------------------*/
+
+/*return root hub descriptor, can not fail*/
+static void
+pehci_hub_descriptor(phci_hcd *	hcd, struct usb_hub_descriptor *desc)
+{
+	u32 ports = 0;
+	u16 temp = 0;
+
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+
+	ports =	0x11;
+	ports =	ports &	0xf;
+
+	pehci_info("%s:	number of ports	%d\n", __FUNCTION__, ports);
+
+	desc->bDescriptorType =	0x29;
+	desc->bPwrOn2PwrGood = 10;
+
+	desc->bHubContrCurrent = 0;
+
+	desc->bNbrPorts	= ports;
+	temp = 1 + (ports / 8);
+	desc->bDescLength = 7 +	2 * temp;
+	/* two bitmaps:	 ports removable, and usb 1.0 legacy PortPwrCtrlMask */
+
+	memset(&desc->DeviceRemovable[0], 0, temp);
+	memset(&desc->PortPwrCtrlMask[temp], 0xff, temp);
+
+	temp = 0x0008;		/* per-port overcurrent	reporting */
+	temp |=	0x0001;		/* per-port power control */
+	temp |=	0x0080;		/* per-port indicators (LEDs) */
+	desc->wHubCharacteristics = cpu_to_le16(temp);
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+}
+
+/*after	reset on root hub,
+ * device high speed or	non-high speed
+ * */
+static int
+phci_check_reset_complete(phci_hcd * hcd, int index, int port_status)
+{
+	pehci_print("check reset complete\n");
+	if (!(port_status & PORT_CONNECT)) {
+		hcd->reset_done[index] = 0;
+		return port_status;
+	}
+
+	/* if reset finished and it's still not	enabled	-- handoff */
+	if (!(port_status & PORT_PE)) {
+		printk("port %d	full speed --> companion\n", index + 1);
+		port_status |= PORT_OWNER;
+		isp1763_reg_write32(hcd->dev, hcd->regs.ports[index],
+				    port_status);
+
+	} else {
+		pehci_print("port %d high speed\n", index + 1);
+	}
+
+	return port_status;
+
+}
+
+/*----------------------------------------------*
+  host controller initialization, removal functions
+ *----------------------------------------------*/
+
+
+/*initialize all three buffer(iso/atl/int) type	headers*/
+static void
+pehci_hcd_init_map_buffers(phci_hcd * phci)
+{
+	td_ptd_map_buff_t *ptd_map_buff;
+	u8 buff_type, ptd_index;
+	u32 bitmap;
+
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+	pehci_print("phci_init_map_buffers(phci	= 0x%p)\n", phci);
+	/* initialize for each buffer type */
+	for (buff_type = 0; buff_type <	TD_PTD_TOTAL_BUFF_TYPES; buff_type++) {
+		ptd_map_buff = &(td_ptd_map_buff[buff_type]);
+		ptd_map_buff->buffer_type = buff_type;
+		ptd_map_buff->active_ptds = 0;
+		ptd_map_buff->total_ptds = 0;
+		/*each bufer type can have atleast 32 ptds */
+		ptd_map_buff->max_ptds = 16;
+		ptd_map_buff->active_ptd_bitmap	= 0;
+		/*everything skipped */
+		/*nothing is pending */
+		ptd_map_buff->pending_ptd_bitmap = 0x00000000;
+
+		/* For each ptd	index of this buffer, set the fiedls */
+		bitmap = 0x00000001;
+		for (ptd_index = 0; ptd_index <	TD_PTD_MAX_BUFF_TDS;
+			ptd_index++) {
+			/*datatoggle zero */
+			ptd_map_buff->map_list[ptd_index].datatoggle = 0;
+			/*td state is not used */
+			ptd_map_buff->map_list[ptd_index].state	= TD_PTD_NEW;
+			/*no endpoint, no qtd */
+			ptd_map_buff->map_list[ptd_index].qh = NULL;
+			ptd_map_buff->map_list[ptd_index].qtd =	NULL;
+			ptd_map_buff->map_list[ptd_index].ptd_header_addr =
+				0xFFFF;
+		}		/* for(	ptd_index */
+	}			/* for(buff_type */
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+}				/* phci_init_map_buffers */
+
+
+/*put the host controller into operational mode
+ * called phci_hcd_start routine,
+ * return 0, success else
+ * timeout, fails*/
+
+static int
+pehci_hcd_start_controller(phci_hcd * hcd)
+{
+	u32 temp = 0;
+	u32 command = 0;
+	int retval = 0;
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+	printk(KERN_NOTICE "++ %s: Entered\n", __FUNCTION__);
+
+
+	command	= isp1763_reg_read16(hcd->dev, hcd->regs.command, command);
+	printk(KERN_NOTICE "HC Command Reg val ...1 %x\n", command);
+
+	/*initialize the host controller */
+	command	|= CMD_RUN;
+
+	isp1763_reg_write16(hcd->dev, hcd->regs.command, command);
+
+
+	command	&= 0;
+
+	command	= isp1763_reg_read16(hcd->dev, hcd->regs.command, command);
+	printk(KERN_NOTICE "HC Command Reg val ...2 %x\n", command);
+
+	/*should be in operation in 1000 usecs */
+	if ((retval =
+		pehci_hcd_handshake(hcd, hcd->regs.command, CMD_RUN, CMD_RUN,
+		100000))) {
+		err("Host is not up(CMD_RUN) in	1000 usecs\n");
+		return retval;
+	}
+
+	printk(KERN_NOTICE "ISP1763 HC is running \n");
+
+
+	/*put the host controller to ehci mode */
+	command	&= 0;
+	command	|= 1;
+
+	isp1763_reg_write16(hcd->dev, hcd->regs.configflag, command);
+	mdelay(5);
+
+	temp = isp1763_reg_read16(hcd->dev, hcd->regs.configflag, temp);
+	pehci_print("%s: Config	Flag reg value:	0x%08x\n", __FUNCTION__, temp);
+
+	/*check	if ehci	mode switching is correct or not */
+	if ((retval =
+		pehci_hcd_handshake(hcd, hcd->regs.configflag, 1, 1, 100))) {
+		err("Host is not into ehci mode	in 100 usecs\n");
+		return retval;
+	}
+
+	mdelay(5);
+
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+	printk(KERN_NOTICE "-- %s: Exit\n", __FUNCTION__);
+	return retval;
+}
+
+
+/*enable the interrupts
+ *called phci_1763_start routine
+ * return void*/
+static void
+pehci_hcd_enable_interrupts(phci_hcd * hcd)
+{
+	u32 temp = 0;
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+	printk(KERN_NOTICE "++ %s: Entered\n", __FUNCTION__);
+	/*disable the interrupt	source */
+	temp &=	0;
+	/*clear	all the	interrupts that	may be there */
+	temp |=	INTR_ENABLE_MASK;
+	isp1763_reg_write16(hcd->dev, hcd->regs.interrupt, temp);
+
+	/*enable interrupts */
+	temp = 0;
+	
+#ifdef OTG_PACKAGE
+	temp |= INTR_ENABLE_MASK | HC_OTG_INT;
+#else
+	temp |= INTR_ENABLE_MASK;
+#endif	
+	pehci_print("%s: enabled mask 0x%08x\n", __FUNCTION__, temp);
+	isp1763_reg_write16(hcd->dev, hcd->regs.interruptenable, temp);
+
+	temp = isp1763_reg_read16(hcd->dev, hcd->regs.interruptenable, temp);
+	pehci_print("%s: Intr enable reg value:	0x%08x\n", __FUNCTION__, temp);
+	
+#ifdef HCD_PACKAGE
+	temp = 0;
+	temp = isp1763_reg_read32(hcd->dev, HC_INT_THRESHOLD_REG, temp);
+//	temp |= 0x0800000F;
+	temp |= 0x0100000F;//125 micro second minimum width between two edge interrupts, 500ns int will remain low
+	//	15/30MHz=500 ns
+	isp1763_reg_write32(hcd->dev, HC_INT_THRESHOLD_REG, temp);
+#endif
+	/*enable the global interrupt */
+	temp &=	0;
+	temp = isp1763_reg_read16(hcd->dev, hcd->regs.hwmodecontrol, temp);
+	temp |=	0x01;		/*enable the global interrupt */
+#ifdef EDGE_INTERRUPT
+	temp |=	0x02;		/*enable the edge interrupt */
+#endif
+
+#ifdef POL_HIGH_INTERRUPT
+	temp |=	0x04;		/* enable interrupt polarity high */
+#endif
+
+	isp1763_reg_write16(hcd->dev, hcd->regs.hwmodecontrol, temp);
+
+	/*maximum rate is one msec */
+	/*enable the atl interrupts OR and AND mask */
+	temp = 0;
+	isp1763_reg_write16(hcd->dev, hcd->regs.atl_irq_mask_and, temp);
+	temp = 0;
+	isp1763_reg_write16(hcd->dev, hcd->regs.atl_irq_mask_or, temp);
+	temp = 0;
+	isp1763_reg_write16(hcd->dev, hcd->regs.int_irq_mask_and, temp);
+	temp = 0x0;
+	isp1763_reg_write16(hcd->dev, hcd->regs.int_irq_mask_or, temp);
+	temp = 0;
+	isp1763_reg_write16(hcd->dev, hcd->regs.iso_irq_mask_and, temp);
+	temp = 0xffff;
+	isp1763_reg_write16(hcd->dev, hcd->regs.iso_irq_mask_or, temp);
+
+	temp = isp1763_reg_read16(hcd->dev, hcd->regs.iso_irq_mask_or, temp);
+	pehci_print("%s:Iso irq	mask reg value:	0x%08x\n", __FUNCTION__, temp);
+
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+}
+
+/*initialize the host controller register map from Isp1763 to EHCI */
+static void
+pehci_hcd_init_reg(phci_hcd * hcd)
+{
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+	/* scratch pad for the test */
+	hcd->regs.scratch = HC_SCRATCH_REG;
+
+	/*make a copy of our interrupt locations */
+	hcd->regs.command = HC_USBCMD_REG;
+	hcd->regs.usbstatus = HC_USBSTS_REG;
+	hcd->regs.usbinterrupt = HC_INTERRUPT_REG_EHCI;
+
+	hcd->regs.hcsparams = HC_SPARAMS_REG;
+	hcd->regs.frameindex = HC_FRINDEX_REG;
+
+	/*transfer specific registers */
+	hcd->regs.hwmodecontrol	= HC_HWMODECTRL_REG;
+	hcd->regs.interrupt = HC_INTERRUPT_REG;
+	hcd->regs.interruptenable = HC_INTENABLE_REG;
+	hcd->regs.atl_irq_mask_and = HC_ATL_IRQ_MASK_AND_REG;
+	hcd->regs.atl_irq_mask_or = HC_ATL_IRQ_MASK_OR_REG;
+
+	hcd->regs.int_irq_mask_and = HC_INT_IRQ_MASK_AND_REG;
+	hcd->regs.int_irq_mask_or = HC_INT_IRQ_MASK_OR_REG;
+	hcd->regs.iso_irq_mask_and = HC_ISO_IRQ_MASK_AND_REG;
+	hcd->regs.iso_irq_mask_or = HC_ISO_IRQ_MASK_OR_REG;
+	hcd->regs.buffer_status	= HC_BUFFER_STATUS_REG;
+	hcd->regs.interruptthreshold = HC_INT_THRESHOLD_REG;
+	/*initialization specific */
+	hcd->regs.reset	= HC_RESET_REG;
+	hcd->regs.configflag = HC_CONFIGFLAG_REG;
+	hcd->regs.ports[0] = HC_PORTSC1_REG;
+	hcd->regs.ports[1] = 0;	/*port1,port2,port3 status reg are removed */
+	hcd->regs.ports[2] = 0;
+	hcd->regs.ports[3] = 0;
+	hcd->regs.pwrdwn_ctrl =	HC_POWER_DOWN_CONTROL_REG;
+	/*transfer registers */
+	hcd->regs.isotddonemap = HC_ISO_PTD_DONEMAP_REG;
+	hcd->regs.isotdskipmap = HC_ISO_PTD_SKIPMAP_REG;
+	hcd->regs.isotdlastmap = HC_ISO_PTD_LASTPTD_REG;
+
+	hcd->regs.inttddonemap = HC_INT_PTD_DONEMAP_REG;
+
+	hcd->regs.inttdskipmap = HC_INT_PTD_SKIPMAP_REG;
+	hcd->regs.inttdlastmap = HC_INT_PTD_LASTPTD_REG;
+
+	hcd->regs.atltddonemap = HC_ATL_PTD_DONEMAP_REG;
+	hcd->regs.atltdskipmap = HC_ATL_PTD_SKIPMAP_REG;
+	hcd->regs.atltdlastmap = HC_ATL_PTD_LASTPTD_REG;
+
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+}
+
+
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+static void
+pehci_interrupt_handler(phci_hcd * hcd, struct pt_regs *regs)
+{
+	spin_lock(&hcd->lock);
+#ifdef CONFIG_ISO_SUPPORT
+	phcd_iso_handler(hcd, regs);
+#endif
+	pehci_hcd_intl_worker(hcd, regs);
+	pehci_hcd_atl_worker(hcd, regs);
+	spin_unlock(&hcd->lock);
+	return;
+}
+#else
+static void
+pehci_interrupt_handler(phci_hcd * hcd)
+{
+	spin_lock(&hcd->lock);
+#ifdef CONFIG_ISO_SUPPORT
+	pehci_hcd_iso_worker(hcd);
+#endif
+	pehci_hcd_intl_worker(hcd);
+	pehci_hcd_atl_worker(hcd);
+	spin_unlock(&hcd->lock);
+	return;
+}
+#endif
+irqreturn_t pehci_hcd_irq(struct usb_hcd *usb_hcd)
+{
+
+	int work = 0;
+	phci_hcd *pehci_hcd;
+	struct isp1763_dev *dev;
+	u32 intr = 0;
+	u32 resume=0;
+	u32 temp=0;
+	u32 irq_mask = 0;
+
+	if (!(usb_hcd->state & USB_STATE_READY)) {
+		info("interrupt	handler	state not ready	yet\n");
+	usb_hcd->state=USB_STATE_READY;
+	//	return IRQ_NONE;
+	}
+
+	/*our host */
+	pehci_hcd = usb_hcd_to_pehci_hcd(usb_hcd);
+	dev = pehci_hcd->dev;
+
+	spin_lock(&pehci_hcd->lock);
+	dev->int_reg = isp1763_reg_read16(dev, HC_INTERRUPT_REG, dev->int_reg);
+	/*Clear the interrupt*/
+	isp1763_reg_write16(dev, HC_INTERRUPT_REG, dev->int_reg);
+
+	irq_mask = isp1763_reg_read16(dev, HC_INTENABLE_REG, irq_mask);
+	dev->int_reg &= irq_mask;
+
+	intr = dev->int_reg;
+
+
+	if (atomic_read(&pehci_hcd->nuofsofs)) {
+		spin_unlock(&pehci_hcd->lock);
+		return IRQ_HANDLED;
+	}
+	atomic_inc(&pehci_hcd->nuofsofs);
+
+	irq_mask=isp1763_reg_read32(dev,HC_USBSTS_REG,0);
+	isp1763_reg_write32(dev,HC_USBSTS_REG,irq_mask);
+	if(irq_mask & 0x4){  // port status register.
+		if(intr & 0x50) {   // OPR register change
+			temp=isp1763_reg_read32(dev,HC_PORTSC1_REG,0);
+			if(temp & 0x4){   // Force resume bit is set
+				if (dev) {
+					if (dev->driver) {
+						if (dev->driver->resume) {
+						dev->driver->resume(dev);
+							resume=1;
+						}
+					}
+				}
+			}
+		}
+	}
+
+	set_bit(HCD_FLAG_SAW_IRQ, &usb_hcd->flags);
+
+#ifndef THREAD_BASED
+/*-----------------------------------------------------------*/
+#ifdef MSEC_INT_BASED
+	work = 1;
+#else
+	if (intr & (HC_MSEC_INT	& INTR_ENABLE_MASK)) {
+		work = 1;	/* phci_iso_worker(hcd); */
+	}
+
+#ifdef USBNET 
+	if (intr & HC_MSOF_INT ) {
+		struct list_head *pos, *q;
+	
+		list_for_each_safe(pos, q, &pehci_hcd->cleanup_urb.urb_list) {
+		struct isp1763_async_cleanup_urb *tmp;
+		
+			tmp = list_entry(pos, struct isp1763_async_cleanup_urb, urb_list);
+			if (tmp) {
+				spin_unlock(&pehci_hcd->lock);
+				usb_hcd_giveback_urb(usb_hcd, tmp->urb, tmp->urb->status);
+				spin_lock(&pehci_hcd->lock);
+
+				list_del(pos);
+				if(tmp)
+				kfree(tmp);
+			}
+		}
+		isp1763_reg_write16(dev, HC_INTENABLE_REG, INTR_ENABLE_MASK );
+	}
+#endif
+
+
+	if (intr & (HC_INTL_INT	& INTR_ENABLE_MASK)) {
+	//	spin_lock(&pehci_hcd->lock);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+		pehci_hcd_intl_worker(pehci_hcd, regs);
+#else
+		pehci_hcd_intl_worker(pehci_hcd);
+#endif
+	//	spin_unlock(&pehci_hcd->lock);
+		work = 0;	/*phci_intl_worker(hcd); */
+	}
+	
+	if (intr & (HC_ATL_INT & INTR_ENABLE_MASK)) {
+	//	spin_lock(&pehci_hcd->lock);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+		pehci_hcd_atl_worker(pehci_hcd, regs);
+#else
+		pehci_hcd_atl_worker(pehci_hcd);
+#endif
+	//	spin_unlock(&pehci_hcd->lock);
+		work = 0;	/*phci_atl_worker(hcd);	*/
+	}
+#ifdef CONFIG_ISO_SUPPORT
+	if (intr & (HC_ISO_INT & INTR_ENABLE_MASK)) {
+	//	spin_lock(&pehci_hcd->lock);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+		pehci_hcd_iso_worker(pehci_hcd);
+#else
+		pehci_hcd_iso_worker(pehci_hcd);
+#endif
+	//	spin_unlock(&pehci_hcd->lock);
+		work = 0;	/*phci_atl_worker(hcd); */
+	}
+#endif
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	if (work){
+		spin_unlock(&pehci_hcd->lock);
+		pehci_interrupt_handler(pehci_hcd, regs);
+		spin_lock(&pehci_hcd->lock);
+	}
+#else
+	if (work){
+		spin_unlock(&pehci_hcd->lock);
+		pehci_interrupt_handler(pehci_hcd);
+		spin_lock(&pehci_hcd->lock);
+	}
+#endif
+
+/*-----------------------------------------------------------*/
+#else
+	if ((intr & (HC_INTL_INT & INTR_ENABLE_MASK)) ||(intr & (HC_ATL_INT & INTR_ENABLE_MASK)))
+	{ //send
+		st_UsbIt_Msg_Struc *stUsbItMsgSnd ;
+		
+		stUsbItMsgSnd = (st_UsbIt_Msg_Struc *)kmalloc(sizeof(st_UsbIt_Msg_Struc), GFP_ATOMIC);
+		if (!stUsbItMsgSnd) return -ENOMEM;
+		
+		memset(stUsbItMsgSnd, 0, sizeof(stUsbItMsgSnd));
+		
+		stUsbItMsgSnd->usb_hcd = usb_hcd;
+		stUsbItMsgSnd->uIntStatus = NO_SOF_REQ_IN_ISR;
+		list_add_tail(&(stUsbItMsgSnd->list), &(g_messList.list));
+
+		pehci_print("\n------------- send mess : %d------------\n",stUsbItMsgSnd->uIntStatus);
+		if ((g_stUsbItThreadHandler.phThreadTask != NULL) && (g_stUsbItThreadHandler.lThrdWakeUpNeeded == 0))
+		{
+			pehci_print("\n------- wake up thread : %d-----\n",stUsbItMsgSnd->uIntStatus);
+			g_stUsbItThreadHandler.lThrdWakeUpNeeded = 1;
+			wake_up(&(g_stUsbItThreadHandler.ulThrdWaitQhead));
+		}
+	}
+/*-----------------------------------------------------------*/
+#endif
+
+	atomic_dec(&pehci_hcd->nuofsofs);
+	spin_unlock(&pehci_hcd->lock);
+		if(resume){
+			usb_hcd_poll_rh_status(usb_hcd);
+	}
+	return IRQ_HANDLED;
+}
+
+/*reset	the host controller
+ *called phci_hcd_start	routine
+ *return 0, success else
+ *timeout, fails*/
+static int
+pehci_hcd_reset(struct usb_hcd *usb_hcd)
+{
+	u32 command = 0;
+	u32 temp = 0;
+	phci_hcd *hcd =	usb_hcd_to_pehci_hcd(usb_hcd);
+	printk(KERN_NOTICE "++ %s: Entered\n", __FUNCTION__);
+	pehci_hcd_init_reg(hcd);
+	printk("chipid %x \n", isp1763_reg_read32(hcd->dev, HC_CHIP_ID_REG, temp)); //0x70
+
+	/*reset	the atx controller */
+	temp &=	0;
+	temp |=	8;
+	isp1763_reg_write16(hcd->dev, hcd->regs.reset, temp);
+	mdelay(10);
+	
+	/*reset	the host controller */
+	temp &=	0;
+	temp |=	1;
+	isp1763_reg_write16(hcd->dev, hcd->regs.reset, temp);
+
+	command	= 0;
+	do {
+
+		temp = isp1763_reg_read16(hcd->dev, hcd->regs.reset, temp);
+		mdelay(10);
+		command++;
+		if (command > 100) {
+			printk("not able to reset\n");
+			break;
+		}
+	} while	(temp &	0x01);
+
+
+	/*reset	the ehci controller registers */
+	temp = 0;
+	temp |=	(1 << 1);
+	isp1763_reg_write16(hcd->dev, hcd->regs.reset, temp);
+	command	= 0;
+	do {
+		temp = isp1763_reg_read16(hcd->dev, hcd->regs.reset, temp);
+		mdelay(10);
+		command++;
+		if (command > 100) {
+			printk("not able to reset\n");
+			break;
+		}
+	} while	(temp &	0x02);
+
+	/*read the command register */
+	command	= isp1763_reg_read16(hcd->dev, hcd->regs.command, command);
+
+	command	|= CMD_RESET;
+	/*write	back and wait for, 250 msec */
+	isp1763_reg_write16(hcd->dev, hcd->regs.command, command);
+	/*wait for maximum 250 msecs */
+	mdelay(200);
+	printk("command	%x\n",
+		isp1763_reg_read16(hcd->dev, hcd->regs.command, command));
+	printk(KERN_NOTICE "-- %s: Exit	\n", __FUNCTION__);
+	return 0;
+}
+
+/*host controller initialize routine,
+ *called by phci_hcd_probe
+ * */
+static int
+pehci_hcd_start(struct usb_hcd *usb_hcd)
+{
+
+	int retval;
+	int count = 0;
+	phci_hcd *pehci_hcd = NULL;
+	u32 temp = 0;
+	u32 hwmodectrl = 0;
+	u32 ul_scratchval = 0;
+
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+	pehci_hcd = usb_hcd_to_pehci_hcd(usb_hcd);
+
+	spin_lock_init(&pehci_hcd->lock);
+	atomic_set(&pehci_hcd->nuofsofs, 0);
+	atomic_set(&pehci_hcd->missedsofs, 0);
+
+	/*Initialize host controller registers */
+	pehci_hcd_init_reg(pehci_hcd);
+
+	/*reset	the host controller */
+	retval = pehci_hcd_reset(usb_hcd);
+	if (retval) {
+		err("phci_1763_start: error failing with status	%x\n", retval);
+		return retval;
+	}
+
+	hwmodectrl =
+		isp1763_reg_read16(pehci_hcd->dev,
+				   pehci_hcd->regs.hwmodecontrol, hwmodectrl);
+#ifdef DATABUS_WIDTH_16
+	printk(KERN_NOTICE "Mode Ctrl Value before 16width: %x\n", hwmodectrl);
+	hwmodectrl &= 0xFFEF;	/*enable the 16	bit bus	*/
+	hwmodectrl |= 0x0400;	/*enable common	int */
+#else
+	printk(KERN_NOTICE "Mode Ctrl Value before 8width : %x\n", hwmodectrl);
+	hwmodectrl |= 0x0010;	/*enable the 8 bit bus */
+	hwmodectrl |= 0x0400;	/*enable common	int */
+#endif
+	isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.hwmodecontrol,
+			    hwmodectrl);
+
+	hwmodectrl =
+		isp1763_reg_read16(pehci_hcd->dev,
+				   pehci_hcd->regs.hwmodecontrol, hwmodectrl);
+	hwmodectrl |=0x9;  //lock interface and enable global interrupt.
+	isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.hwmodecontrol,
+		hwmodectrl);	
+	printk(KERN_NOTICE "Mode Ctrl Value after buswidth: %x\n", hwmodectrl);
+
+	isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.scratch, 0x3344);
+
+	ul_scratchval =
+		isp1763_reg_read16(pehci_hcd->dev, pehci_hcd->regs.scratch,
+				   ul_scratchval);
+	printk(KERN_NOTICE "Scratch Reg	Value :	%x\n", ul_scratchval);
+	if (ul_scratchval != 0x3344) {
+		printk(KERN_NOTICE "Scratch Reg	Value Mismatch:	%x\n",
+		       ul_scratchval);
+
+	}
+
+
+	/*initialize the host controller initial values	*/
+	/*disable all the buffer */
+	isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.buffer_status, 0);
+	/*skip all the transfers */
+	isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.atltdskipmap,
+			    NO_TRANSFER_ACTIVE);
+	isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.inttdskipmap,
+			    NO_TRANSFER_ACTIVE);
+	isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.isotdskipmap,
+			    NO_TRANSFER_ACTIVE);
+	/*clear	done map */
+	isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.atltddonemap,
+			    NO_TRANSFER_DONE);
+	isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.inttddonemap,
+			    NO_TRANSFER_DONE);
+	isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.isotddonemap,
+			    NO_TRANSFER_DONE);
+	
+#ifdef HCD_PACKAGE
+	/*port1 as Host */
+	isp1763_reg_write16(pehci_hcd->dev, OTG_CTRL_SET_REG, 0x0400);
+	isp1763_reg_write16(pehci_hcd->dev, OTG_CTRL_CLEAR_REG, 0x0080);
+	/*port2 as Host */
+	isp1763_reg_write16(pehci_hcd->dev, OTG_CTRL_SET_REG, 0x0000);
+	isp1763_reg_write16(pehci_hcd->dev, OTG_CTRL_CLEAR_REG, 0x8000);
+	
+	#if 0 /* do not use bit 1&2 for pure host application */
+	ul_scratchval =	isp1763_reg_read32(pehci_hcd->dev, HC_POWER_DOWN_CONTROL_REG,0);
+	ul_scratchval |= 0x006;	
+	isp1763_reg_write32(pehci_hcd->dev, HC_POWER_DOWN_CONTROL_REG,ul_scratchval);
+	#endif
+	
+#elif defined(HCD_DCD_PACKAGE)
+
+	/*port1 as device */
+	isp1763_reg_write16(pehci_hcd->dev,OTG_CTRL_SET_REG, 
+			OTG_CTRL_DMPULLDOWN |OTG_CTRL_DPPULLDOWN | 
+			OTG_CTRL_SW_SEL_HC_DC |OTG_CTRL_OTG_DISABLE);	/* pure	Device Mode and	OTG disabled */
+
+	isp1763_reg_write16(pehci_hcd->dev, OTG_CTRL_SET_REG, 0x0480);
+	/*port2 as host */
+	isp1763_reg_write16(pehci_hcd->dev, OTG_CTRL_SET_REG, 0x0000);
+	isp1763_reg_write16(pehci_hcd->dev, OTG_CTRL_CLEAR_REG, 0x8000);
+	ul_scratchval =
+		isp1763_reg_read32(pehci_hcd->dev, HC_POWER_DOWN_CONTROL_REG,
+		0);
+#endif
+
+	/*enable interrupts */
+	pehci_hcd_enable_interrupts(pehci_hcd);
+
+	/*put controller into operational mode */
+	retval = pehci_hcd_start_controller(pehci_hcd);
+	if (retval) {
+		err("phci_1763_start: error failing with status	%x\n", retval);
+		return retval;
+	}
+
+	/*Init the phci	qtd <->	ptd map	buffers	*/
+	pehci_hcd_init_map_buffers(pehci_hcd);
+
+	/*set last maps, for iso its only 1, else 32 tds bitmap	*/
+	isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.atltdlastmap,
+			    0x8000);
+	isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.inttdlastmap, 0x80);
+	isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.isotdlastmap, 0x01);
+	/*iso transfers	are not	active */
+	pehci_hcd->next_uframe = -1;
+	pehci_hcd->periodic_sched = 0;
+	hwmodectrl =
+		isp1763_reg_read16(pehci_hcd->dev,
+				   pehci_hcd->regs.hwmodecontrol, hwmodectrl);
+
+	/*initialize the periodic list */
+	for (count = 0; count < PTD_PERIODIC_SIZE; count++) {
+		pehci_hcd->periodic_list[count].framenumber = 0;
+		INIT_LIST_HEAD(&pehci_hcd->periodic_list[count].sitd_itd_head);
+	}
+
+
+	/*set the state	of the host to ready,
+	 * start processing interrupts
+	 * */
+
+	usb_hcd->state = HC_STATE_RUNNING;
+	pehci_hcd->state = HC_STATE_RUNNING;
+
+
+	/*initialize root hub timer */
+	init_timer(&pehci_hcd->rh_timer);
+	/*initialize watchdog */
+	init_timer(&pehci_hcd->watchdog);
+
+	temp = isp1763_reg_read32(pehci_hcd->dev, HC_POWER_DOWN_CONTROL_REG,
+				  temp);
+	
+	temp = 0x3e81bA0;
+#if 0
+	temp |=	0x306;
+#endif
+	isp1763_reg_write32(pehci_hcd->dev, HC_POWER_DOWN_CONTROL_REG, temp);
+	temp = isp1763_reg_read32(pehci_hcd->dev, HC_POWER_DOWN_CONTROL_REG,
+				  temp);
+	printk(" Powerdown Reg Val: %x\n", temp);
+
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+
+	return 0;
+}
+
+static void
+pehci_hcd_stop(struct usb_hcd *usb_hcd)
+{
+
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+
+	/* no more interrupts ... */
+	if (usb_hcd->state == USB_STATE_RUNNING) {
+		mdelay(2);
+	}
+	if (in_interrupt()) {	/* must	not happen!! */
+		pehci_info("stopped in_interrupt!\n");
+
+		return;
+	}
+
+	/*power	off our	root hub */
+	pehci_rh_control(usb_hcd, ClearPortFeature, USB_PORT_FEAT_POWER,
+			 1, NULL, 0);
+
+	/*let the roothub power	go off */
+	mdelay(20);
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+
+	return;
+}
+
+
+/*submit urb , other than root hub*/
+static int
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+pehci_hcd_urb_enqueue(struct usb_hcd *usb_hcd, struct usb_host_endpoint *ep,
+		struct urb *urb, gfp_t mem_flags)
+#else
+pehci_hcd_urb_enqueue(struct usb_hcd *usb_hcd, struct urb *urb, gfp_t mem_flags)
+#endif
+{
+
+	struct list_head qtd_list;
+	struct ehci_qh *qh = 0;
+	phci_hcd *pehci_hcd = usb_hcd_to_pehci_hcd(usb_hcd);
+	int status = 0;
+	int temp = 0, max = 0, num_tds = 0, mult = 0;
+	urb_priv_t *urb_priv = NULL;
+	unsigned long  flags;
+	
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+
+	
+	if (unlikely(atomic_read(&urb->reject))) 
+		return -EINVAL;
+	
+	INIT_LIST_HEAD(&qtd_list);
+	urb->transfer_flags &= ~EHCI_STATE_UNLINK;
+
+
+	temp = usb_pipetype(urb->pipe);
+	max = usb_maxpacket(urb->dev, urb->pipe, !usb_pipein(urb->pipe));
+	
+
+	if (hcdpowerdown == 1) {
+		printk("Enqueue	hcd power down\n");
+		return -EINVAL;
+	}
+
+
+	/*pathch to get	the otg	device */
+	if (!hubdev || 
+		(urb->dev->parent==usb_hcd->self.root_hub && 
+		hubdev!=urb->dev)) {
+		if(urb->dev->parent== usb_hcd->self.root_hub) {
+			hubdev = urb->dev;
+		}
+	}
+
+	switch (temp) {
+	case PIPE_INTERRUPT:
+		/*only one td */
+		num_tds	= 1;
+		mult = 1 + ((max >> 11)	& 0x03);
+		max &= 0x07ff;
+		max *= mult;
+
+		if (urb->transfer_buffer_length	> max) {
+			err("interrupt urb length is greater then %d\n", max);
+			return -EINVAL;
+		}
+
+		if (hubdev && urb->dev->parent == usb_hcd->self.root_hub) {
+			huburb = urb;
+		}
+
+		break;
+
+	case PIPE_CONTROL:
+		/*calculate the	number of tds, follow 1	pattern	*/
+		if (No_Data_Phase && No_Status_Phase) {
+			printk("Only SetUP Phase\n");
+			num_tds	= (urb->transfer_buffer_length == 0) ? 1 :
+				((urb->transfer_buffer_length -
+				  1) / HC_ATL_PL_SIZE +	1);
+		} else if (!No_Data_Phase && No_Status_Phase) {
+			printk("SetUP Phase and	Data Phase\n");
+			num_tds	= (urb->transfer_buffer_length == 0) ? 2 :
+				((urb->transfer_buffer_length -
+				  1) / HC_ATL_PL_SIZE +	3);
+		} else if (!No_Data_Phase && !No_Status_Phase) {
+			num_tds	= (urb->transfer_buffer_length == 0) ? 2 :
+				((urb->transfer_buffer_length -
+				  1) / HC_ATL_PL_SIZE +	3);
+		}
+		
+		break;
+		
+	case PIPE_BULK:
+		num_tds	=
+			(urb->transfer_buffer_length - 1) / HC_ATL_PL_SIZE + 1;
+		if ((urb->transfer_flags & URB_ZERO_PACKET)
+			&& !(urb->transfer_buffer_length % max)) {
+			num_tds++;
+		}
+		
+		break;
+		
+#ifdef CONFIG_ISO_SUPPORT
+	case PIPE_ISOCHRONOUS:
+		/* Don't need to do anything here */
+		break;
+#endif
+	default:
+		return -EINVAL;	/*not supported	isoc transfers */
+
+
+	}
+
+#ifdef CONFIG_ISO_SUPPORT
+	if (temp != PIPE_ISOCHRONOUS) {
+#endif
+		/*make number of tds required */
+		urb_priv = kmalloc(sizeof(urb_priv_t) +
+				   num_tds * sizeof(struct ehci_qtd),
+				   mem_flags);
+		if (!urb_priv) {
+			err("memory   allocation error\n");
+			return -ENOMEM;
+		}
+
+		memset(urb_priv, 0, sizeof(urb_priv_t) +
+			num_tds * sizeof(struct ehci_qtd));
+		INIT_LIST_HEAD(&urb_priv->qtd_list);
+		urb_priv->qtd[0] = NULL;
+		urb_priv->length = num_tds;
+		{
+			int i =	0;
+			/*allocate number of tds here. better to do this in qtd_make routine */
+			for (i = 0; i <	num_tds; i++) {
+				urb_priv->qtd[i] =
+					phci_hcd_qtd_allocate(mem_flags);
+				if (!urb_priv->qtd[i]) {
+					phci_hcd_urb_free_priv(pehci_hcd,
+							       urb_priv, NULL);
+					return -ENOMEM;
+				}
+			}
+		}
+		/*keep a copy of this */
+		urb->hcpriv = urb_priv;
+#ifdef CONFIG_ISO_SUPPORT
+	}
+#endif
+
+	switch (temp) {
+	case PIPE_INTERRUPT:
+		phci_hcd_make_qtd(pehci_hcd, &urb_priv->qtd_list,	urb, &status);
+		if (status < 0)	{
+			return status;
+		}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+		qh = phci_hcd_submit_interrupt(pehci_hcd, ep, &urb_priv->qtd_list, urb,
+			&status);
+#else
+		qh = phci_hcd_submit_interrupt(pehci_hcd, &urb_priv->qtd_list, urb,
+			&status);
+#endif
+		if (status < 0)
+			return status;
+		break;
+
+	case PIPE_CONTROL:
+	case PIPE_BULK:
+
+#ifdef THREAD_BASED
+	spin_lock_irqsave (&pehci_hcd->lock, flags);
+#endif
+		phci_hcd_make_qtd(pehci_hcd, &qtd_list,	urb, &status);
+		if (status < 0) {
+			return status;
+		}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+		qh = phci_hcd_submit_async(pehci_hcd, ep, &qtd_list, urb,
+			&status);
+#else
+		qh = phci_hcd_submit_async(pehci_hcd, &qtd_list, urb, &status);
+#endif
+
+#ifdef THREAD_BASED
+	spin_unlock_irqrestore (&pehci_hcd->lock, flags);
+#endif
+
+		if (status < 0) {
+			return status;
+		}
+		break;
+#ifdef CONFIG_ISO_SUPPORT
+	case PIPE_ISOCHRONOUS:
+		iso_dbg(ISO_DBG_DATA,
+			"[pehci_hcd_urb_enqueue]: URB Transfer buffer: 0x%08x\n",
+			(long) urb->transfer_buffer);
+		iso_dbg(ISO_DBG_DATA,
+			"[pehci_hcd_urb_enqueue]: URB Buffer Length: %d\n",
+			(long) urb->transfer_buffer_length);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+		phcd_submit_iso(pehci_hcd, ep, urb, (unsigned long *) &status);
+#else
+		spin_lock_irqsave(&pehci_hcd->lock, flags);
+		phcd_store_urb_pending(pehci_hcd, 0, urb, (int *) &status);
+		spin_unlock_irqrestore(&pehci_hcd->lock, flags);
+#endif
+
+		return status;
+
+		break;
+#endif
+	default:
+		return -ENODEV;
+	}			/*end of switch	*/
+
+#if (defined MSEC_INT_BASED)
+	return 0;
+#elif (defined THREAD_BASED)
+{ //send
+		st_UsbIt_Msg_Struc *stUsbItMsgSnd ;
+		unsigned long flags;
+		spin_lock_irqsave(&pehci_hcd->lock,flags);
+	
+		//local_irq_save(flags); /*disable interrupt*/
+		stUsbItMsgSnd = (st_UsbIt_Msg_Struc *)kmalloc(sizeof(st_UsbIt_Msg_Struc), GFP_ATOMIC);
+		if (!stUsbItMsgSnd)
+		{
+			return -ENOMEM;
+		}
+		
+		memset(stUsbItMsgSnd, 0, sizeof(stUsbItMsgSnd));
+		
+		stUsbItMsgSnd->usb_hcd = usb_hcd;
+		stUsbItMsgSnd->uIntStatus = NO_SOF_REQ_IN_REQ;
+		spin_lock(&enqueue_lock);
+		if(list_empty(&g_enqueueMessList.list))
+			list_add_tail(&(stUsbItMsgSnd->list), &(g_enqueueMessList.list));
+		spin_unlock(&enqueue_lock);
+		
+		pehci_print("\n------------- send mess : %d------------\n",stUsbItMsgSnd->uIntStatus);
+
+		//local_irq_restore(flags); /*disable interrupt*/
+		
+		spin_lock(&g_stUsbItThreadHandler.lock);
+		if ((g_stUsbItThreadHandler.phThreadTask != NULL) && (g_stUsbItThreadHandler.lThrdWakeUpNeeded == 0))
+		{
+			pehci_print("\n------- wake up thread : %d-----\n",stUsbItMsgSnd->uIntStatus);
+			g_stUsbItThreadHandler.lThrdWakeUpNeeded = 1;
+			wake_up(&(g_stUsbItThreadHandler.ulThrdWaitQhead));
+		}
+		spin_unlock(&g_stUsbItThreadHandler.lock);
+
+		spin_unlock_irqrestore(&pehci_hcd->lock,flags);
+	}
+	pehci_entry("-- %s: Exit\n",__FUNCTION__);
+    return 0;
+#else
+	/*submit tds but iso */
+    if (temp != PIPE_ISOCHRONOUS)
+	pehci_hcd_td_ptd_submit_urb(pehci_hcd, qh, qh->type);
+#endif
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+	return 0;
+
+}
+
+/*---------------------------------------------*
+  io request handlers
+ *---------------------------------------------*/
+
+/*unlink urb*/
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+static int
+pehci_hcd_urb_dequeue(struct usb_hcd *usb_hcd, struct urb *urb)
+#else
+static int
+pehci_hcd_urb_dequeue(struct usb_hcd *usb_hcd, struct urb *urb, int status)
+#endif
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	int status = 0;
+#endif
+	int retval = 0;
+	td_ptd_map_buff_t *td_ptd_buf;
+	td_ptd_map_t *td_ptd_map;
+	struct ehci_qh *qh = 0;
+	u32 skipmap = 0;
+	u32 buffstatus = 0;
+	unsigned long flags;
+	struct ehci_qtd	*qtd = 0;
+	struct usb_host_endpoint *ep;
+
+	struct ehci_qtd	*cancel_qtd = 0;	/*added	for stopping ptd*/
+	struct urb *cancel_urb = 0;	/*added	for stopping ptd*/
+	urb_priv_t *cancel_urb_priv = 0;	/* added for stopping ptd */
+	struct _isp1763_qha atlqha;
+	struct _isp1763_qha *qha;
+	struct isp1763_mem_addr	*mem_addr = 0;
+	u32 ormask = 0;
+	struct list_head *qtd_list = 0;
+	urb_priv_t *urb_priv = (urb_priv_t *) urb->hcpriv;
+	phci_hcd *hcd =	usb_hcd_to_pehci_hcd(usb_hcd);
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+
+	pehci_info("device %d\n", urb->dev->devnum);
+
+	if(urb_priv==NULL){
+		printk("*******urb_priv is NULL*******	%s: Entered\n",	__FUNCTION__);
+		return 0;
+		}
+	spin_lock_irqsave(&hcd->lock, flags);
+
+
+	switch (usb_pipetype(urb->pipe)) {
+	case PIPE_CONTROL:
+	case PIPE_BULK:
+	//	status = 0;
+		qh = urb_priv->qh;
+		if(qh==NULL)
+			break;
+
+		td_ptd_buf = &td_ptd_map_buff[TD_PTD_BUFF_TYPE_ATL];
+		td_ptd_map = &td_ptd_buf->map_list[qh->qtd_ptd_index];
+
+		/*if its already been removed */
+		if (td_ptd_map->state == TD_PTD_NEW) {
+			break;
+		}
+/* patch added for stopping Full speed PTD */
+/* patch starts	ere */
+		if (urb->dev->speed != USB_SPEED_HIGH) {
+
+			cancel_qtd = td_ptd_map->qtd;
+			if (!qh	|| !cancel_qtd)	{
+				err("Never Error:QH and	QTD must not be	zero\n");
+			} else {
+				cancel_urb = cancel_qtd->urb;
+				cancel_urb_priv	=
+					(urb_priv_t *) cancel_urb->hcpriv;
+				mem_addr = &cancel_qtd->mem_addr;
+				qha = &atlqha;
+				memset(qha, 0, sizeof(struct _isp1763_qha));
+
+				skipmap	=
+					isp1763_reg_read16(hcd->dev,
+							   hcd->regs.
+							   atltdskipmap,
+							   skipmap);
+				skipmap	|= td_ptd_map->ptd_bitmap;
+				isp1763_reg_write16(hcd->dev,
+						    hcd->regs.atltdskipmap,
+						    skipmap);
+
+				/*read this ptd	from the ram address,address is	in the
+				   td_ptd_map->ptd_header_addr */
+				isp1763_mem_read(hcd->dev,
+						 td_ptd_map->ptd_header_addr, 0,
+						 (u32 *) (qha),	PHCI_QHA_LENGTH,
+						 0);
+				if ((qha->td_info1 & QHA_VALID)
+					|| (qha->td_info4 &	QHA_ACTIVE)) {
+
+					qha->td_info2 |= 0x00008000;
+					qha->td_info1 |= QHA_VALID;
+					qha->td_info4 |= QHA_ACTIVE;
+					skipmap	&= ~td_ptd_map->ptd_bitmap;
+					ormask |= td_ptd_map->ptd_bitmap;
+					isp1763_reg_write16(hcd->dev,
+						hcd->regs.
+						atl_irq_mask_or,
+						ormask);
+					/* copy back into the header, payload is	already
+					 * present no need to write again */
+					isp1763_mem_write(hcd->dev,
+						td_ptd_map->
+						ptd_header_addr, 0,
+						(u32 *) (qha),
+						PHCI_QHA_LENGTH, 0);
+					/*unskip this td */
+					isp1763_reg_write16(hcd->dev,
+						hcd->regs.
+						atltdskipmap,
+						skipmap);
+					udelay(100);
+				}
+
+				isp1763_mem_read(hcd->dev,
+					td_ptd_map->ptd_header_addr, 0,
+					(u32 *) (qha),	PHCI_QHA_LENGTH,
+					0);
+				if (!(qha->td_info1 & QHA_VALID)
+					&& !(qha->td_info4 & QHA_ACTIVE)) {
+					printk(KERN_NOTICE
+					"ptd has	been retired \n");
+				}
+
+			}
+		}
+
+/*   Patch Ends	*/
+		/* These TDs are not pending anymore */
+		td_ptd_buf->pending_ptd_bitmap &= ~td_ptd_map->ptd_bitmap;
+
+		/*tell atl worker this urb is going to be removed */
+		td_ptd_map->state = TD_PTD_REMOVE;
+		/* These TDs are not pending anymore */
+		td_ptd_buf->pending_ptd_bitmap &= ~td_ptd_map->ptd_bitmap;
+		/*tell atl worker this urb is going to be removed */
+		td_ptd_map->state = TD_PTD_REMOVE;
+		urb_priv->state	|= DELETE_URB;
+
+		/*read the skipmap, to see if this transfer has	to be rescheduled */
+		skipmap	=
+			isp1763_reg_read16(hcd->dev, hcd->regs.atltdskipmap,
+			skipmap);
+		pehci_check("remove skip map %x, ptd map %x\n",	skipmap,
+			td_ptd_map->ptd_bitmap);
+
+		buffstatus =
+			isp1763_reg_read16(hcd->dev, hcd->regs.buffer_status,
+			buffstatus);
+
+
+		isp1763_reg_write16(hcd->dev, hcd->regs.atltdskipmap,
+			skipmap | td_ptd_map->ptd_bitmap);
+
+		while (!(skipmap & td_ptd_map->ptd_bitmap)) {
+			udelay(125);
+
+			skipmap	= isp1763_reg_read16(hcd->dev,
+				hcd->regs.atltdskipmap,
+				skipmap);
+		}
+
+		/* if all  transfers skipped,
+		 * then	disable	the atl	buffer,
+		 * so that new transfer	can come in
+		 * need	to see the side	effects
+		 * */
+		if (skipmap == NO_TRANSFER_ACTIVE) {
+			/*disable the buffer */
+			pehci_info("disable the	atl buffer\n");
+			buffstatus &= ~ATL_BUFFER;
+			isp1763_reg_write16(hcd->dev, hcd->regs.buffer_status,
+				buffstatus);
+		}
+
+		qtd_list = &qh->qtd_list;
+		/*this should remove all pending transfers */
+		pehci_check("num tds %d, urb length %d,device %d\n",
+			urb_priv->length, urb->transfer_buffer_length,
+			urb->dev->devnum);
+
+		pehci_check("remove first qtd address %p\n", urb_priv->qtd[0]);
+		pehci_check("length of the urb %d, completed %d\n",
+			urb->transfer_buffer_length, urb->actual_length);
+		qtd = urb_priv->qtd[urb_priv->length - 1];
+		pehci_check("qtd state is %x\n", qtd->state);
+
+
+		urb->status=status;
+		status = 0;
+#ifdef USBNET 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+		pehci_hcd_urb_delayed_complete(hcd, qh, urb, td_ptd_map, NULL);
+#else
+		pehci_hcd_urb_delayed_complete(hcd, qh, urb, td_ptd_map);
+#endif
+#else
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+		pehci_hcd_urb_complete(hcd, qh, urb, td_ptd_map, NULL);
+#else
+		pehci_hcd_urb_complete(hcd, qh, urb, td_ptd_map);
+#endif
+
+#endif
+		break;
+
+	case PIPE_INTERRUPT:
+		pehci_check("phci_1763_urb_dequeue: INTR needs to be done\n");
+		urb->status = status; //-ENOENT;//This will allow to suspend the system. in auto suspend mode
+		status = 0;
+		qh = urb_priv->qh;
+		if(qh==NULL)
+			break;
+
+		td_ptd_buf = &td_ptd_map_buff[TD_PTD_BUFF_TYPE_INTL];
+		td_ptd_map = &td_ptd_buf->map_list[qh->qtd_ptd_index];
+
+		/*urb is already been removed */
+		if (td_ptd_map->state == TD_PTD_NEW) {
+			kfree(urb_priv);
+			break;
+		}
+
+		/* These TDs are not pending anymore */
+		td_ptd_buf->pending_ptd_bitmap &= ~td_ptd_map->ptd_bitmap;
+
+		td_ptd_map->state = TD_PTD_REMOVE;
+		urb_priv->state	|= DELETE_URB;
+
+		/*read the skipmap, to see if this transfer has	to be rescheduled */
+		skipmap	=
+			isp1763_reg_read16(hcd->dev, hcd->regs.inttdskipmap,
+			skipmap);
+
+		isp1763_reg_write16(hcd->dev, hcd->regs.inttdskipmap,
+			skipmap | td_ptd_map->ptd_bitmap);
+		qtd_list = &qh->qtd_list;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+		pehci_hcd_urb_complete(hcd, qh, urb, td_ptd_map, NULL);
+#else
+		pehci_hcd_urb_complete(hcd, qh, urb, td_ptd_map);
+#endif
+		break;
+#ifdef CONFIG_ISO_SUPPORT
+	case PIPE_ISOCHRONOUS:
+		pehci_info("urb dequeue %x %x\n", urb,urb->pipe);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+	if(urb->dev->speed==USB_SPEED_HIGH){
+		retval = usb_hcd_check_unlink_urb(usb_hcd, urb, status);
+		if (!retval) {
+			pehci_info("[pehci_hcd_urb_dequeue] usb_hcd_unlink_urb_from_ep with status = %d\n", status);
+			usb_hcd_unlink_urb_from_ep(usb_hcd, urb);
+
+
+		}
+	}
+#endif
+
+		
+		status = 0;
+		ep=urb->ep;
+		spin_unlock_irqrestore(&hcd->lock, flags);
+		mdelay(100);
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+						if (urb->hcpriv!= periodic_ep[0]){
+#else
+						if (urb->ep != periodic_ep[0]){
+#endif
+	if(!list_empty(&ep->urb_list)){	
+		while(!list_empty(&ep->urb_list)){
+			urb=container_of(ep->urb_list.next,struct urb,urb_list);
+			pehci_info("list is not empty %x %x\n",urb,urb->dev->state);
+			if(urb){
+		retval = usb_hcd_check_unlink_urb(usb_hcd, urb,0);
+		if (!retval) {
+			pehci_info("[pehci_hcd_urb_dequeue] usb_hcd_unlink_urb_from_ep with status = %d\n", status);
+			usb_hcd_unlink_urb_from_ep(usb_hcd, urb);
+		}
+			urb->status=-ESHUTDOWN;
+	#if LINUX_VERSION_CODE <KERNEL_VERSION(2,6,24)
+			usb_hcd_giveback_urb(usb_hcd,urb);
+	#else
+			usb_hcd_giveback_urb(usb_hcd,urb,urb->status);
+	#endif
+				
+			}
+		}
+		}else{
+	if(urb){
+		pehci_info("list empty %x\n",urb->dev->state);
+		phcd_clean_urb_pending(hcd, urb);
+		retval = usb_hcd_check_unlink_urb(usb_hcd, urb,0);
+		if (!retval) {
+			pehci_info("[pehci_hcd_urb_dequeue] usb_hcd_unlink_urb_from_ep with status = %d\n", status);
+			usb_hcd_unlink_urb_from_ep(usb_hcd, urb);
+		}
+			urb->status=-ESHUTDOWN;
+	#if LINUX_VERSION_CODE <KERNEL_VERSION(2,6,24)
+			usb_hcd_giveback_urb(usb_hcd,urb);
+	#else
+			usb_hcd_giveback_urb(usb_hcd,urb,urb->status);
+	#endif
+				
+			}
+			
+		}
+	}	
+#endif
+		return 0;
+		/*nothing to do	here, wait till	all transfers are done in iso worker */
+		break;
+	}
+
+	spin_unlock_irqrestore(&hcd->lock, flags);
+	pehci_info("status %d\n", status);
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+	return status;
+}
+
+/* bulk	qh holds the data toggle */
+
+static void
+pehci_hcd_endpoint_disable(struct usb_hcd *usb_hcd,
+			   struct usb_host_endpoint *ep)
+{
+	phci_hcd *ehci = usb_hcd_to_pehci_hcd(usb_hcd);
+	struct urb *urb;
+
+	unsigned long flags;
+	struct ehci_qh *qh;
+
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+	/* ASSERT:  any	requests/urbs are being	unlinked */
+	/* ASSERT:  nobody can be submitting urbs for this any more */
+	
+#ifdef CONFIG_ISO_SUPPORT
+	mdelay(100);  //delay for ISO
+#endif
+	spin_lock_irqsave(&ehci->lock, flags);
+
+	qh = ep->hcpriv;
+
+	if (!qh) {
+		goto done;
+	} else {
+#ifdef CONFIG_ISO_SUPPORT
+		pehci_info("disable endpoint %x %x\n", ep->desc.bEndpointAddress,qh->type);
+
+		
+		if (qh->type == TD_PTD_BUFF_TYPE_ISTL) {
+
+			/*wait for urb to get complete*/
+			pehci_info("disable %x \n", list_empty(&ep->urb_list));
+			while (!list_empty(&ep->urb_list)) {
+			
+				urb = container_of(ep->urb_list.next,
+					struct urb, urb_list);
+				if (urb) {
+					phcd_clean_urb_pending(ehci, urb);
+					spin_unlock_irqrestore(&ehci->lock,
+						flags);
+
+					urb->status = -ESHUTDOWN;
+					
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+					usb_hcd_giveback_urb(usb_hcd, urb);
+#else
+					usb_hcd_giveback_urb(usb_hcd, urb,
+						urb->status);
+#endif
+					spin_lock_irqsave(&ehci->lock, flags);
+
+				}
+
+			}
+		}
+#endif
+		/*i will complete whatever left	on this	endpoint */
+		pehci_complete_device_removal(ehci, qh);
+#ifdef CONFIG_ISO_SUPPORT
+		phcd_clean_periodic_ep();
+#endif
+		ep->hcpriv = NULL;
+
+		goto done;
+	}
+	done:
+
+	ep->hcpriv = NULL;
+
+	spin_unlock_irqrestore(&ehci->lock, flags);
+	printk("disable endpoint exit\n");
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+	return;
+}
+
+/*called by core, for current frame number*/
+static int
+pehci_hcd_get_frame_number(struct usb_hcd *usb_hcd)
+{
+	u32 framenumber	= 0;
+	phci_hcd *pehci_hcd = usb_hcd_to_pehci_hcd(usb_hcd);
+	framenumber =
+		isp1763_reg_read16(pehci_hcd->dev, pehci_hcd->regs.frameindex,
+		framenumber);
+	return framenumber;
+}
+
+/*root hub status data,	called by root hub timer
+ *return 0, if no change, else
+ *	1, incase of high speed	device
+ */
+static int
+pehci_rh_status_data(struct usb_hcd *usb_hcd, char *buf)
+{
+
+	u32 temp = 0, status = 0;
+	u32 ports = 0, i, retval = 1;
+	unsigned long flags;
+	phci_hcd *hcd =	usb_hcd_to_pehci_hcd(usb_hcd);
+
+	if (hcdpowerdown == 1)
+		return 0;
+
+	buf[0] = 0;
+	if(portchange==1){
+		printk("Remotewakeup-enumerate again \n");
+		buf[0] |= 2;
+		hcd->reset_done[0] = 0;
+		return 1;
+	}
+	/* init	status to no-changes */
+	buf[0] = 0;
+	/*number of ports */
+	ports =	0x1;
+	spin_lock_irqsave(&hcd->lock, flags);
+	/*read the port	status registers */
+	for (i = 0; i <	ports; i++) {
+		temp = isp1763_reg_read32(hcd->dev, hcd->regs.ports[i],	temp);
+		if (temp & PORT_OWNER) {
+			/* dont	report the port	status change in case of CC HCD
+			 * but clear the port status , if there	is any*/
+			if (temp & PORT_CSC) {
+				temp &=	~PORT_CSC;
+				isp1763_reg_write32(hcd->dev,
+						    hcd->regs.ports[i],	temp);
+				continue;
+			}
+		}
+
+		if (!(temp & PORT_CONNECT)) {
+			hcd->reset_done[i] = 0;
+		}
+		if ((temp & (PORT_CSC |	PORT_PEC | PORT_OCC)) != 0) {
+			if (i <	7) {
+				buf[0] |= 1 << (i + 1);
+			} else {
+				buf[1] |= 1 << (i - 7);
+			}
+			status = STS_PCD;
+		}
+	}
+
+	spin_unlock_irqrestore(&hcd->lock, flags);
+	return status ?	retval : 0;
+}
+
+/*root hub control requests*/
+static int
+pehci_rh_control(struct	usb_hcd	*usb_hcd, u16 typeReq, u16 wValue,
+		 u16 wIndex, char *buf,	u16 wLength)
+{
+	u32 ports = 0;
+	u32 temp = 0, status;
+	unsigned long flags;
+	int retval = 0;
+	phci_hcd *hcd =	usb_hcd_to_pehci_hcd(usb_hcd);
+
+	ports =	0x11;
+
+	printk("%s: request %x,wValuse:0x%x, wIndex:0x%x \n",__func__, typeReq,wValue,wIndex);
+	
+	spin_lock_irqsave(&hcd->lock, flags);
+	switch (typeReq) {
+	case ClearHubFeature:
+		switch (wValue)	{
+		case C_HUB_LOCAL_POWER:
+		case C_HUB_OVER_CURRENT:
+			/* no hub-wide feature/status flags */
+			break;
+		default:
+			goto error;
+		}
+		break;
+	case ClearPortFeature:
+		pehci_print("ClearPortFeature:0x%x\n", ClearPortFeature);
+		if (!wIndex || wIndex >	(ports & 0xf)) {
+			pehci_info
+				("ClearPortFeature not valid port number %d, should be %d\n",
+				 wIndex, (ports	& 0xf));
+			goto error;
+		}
+		wIndex--;
+		temp = isp1763_reg_read32(hcd->dev, hcd->regs.ports[wIndex],
+					  temp);
+		if (temp & PORT_OWNER) {
+			printk("port is	owned by the CC	host\n");
+			break;
+		}
+
+		switch (wValue)	{
+		case USB_PORT_FEAT_ENABLE:
+			pehci_print("enable the	port\n");
+			isp1763_reg_write32(hcd->dev, hcd->regs.ports[wIndex],
+					    temp & ~PORT_PE);
+
+			break;
+		case USB_PORT_FEAT_C_ENABLE:
+			printk("disable	the port\n");
+			isp1763_reg_write32(hcd->dev, hcd->regs.ports[wIndex],
+					    temp | PORT_PEC);
+			break;
+		case USB_PORT_FEAT_SUSPEND:
+		case USB_PORT_FEAT_C_SUSPEND:
+			printk("clear feature suspend  \n");
+			break;
+		case USB_PORT_FEAT_POWER:
+			if (ports & 0x10) {	/*port has has power control switches */
+				isp1763_reg_write32(hcd->dev,
+						    hcd->regs.ports[wIndex],
+						    temp & ~PORT_POWER);
+			}
+			break;
+		case USB_PORT_FEAT_C_CONNECTION:
+			pehci_print("connect change, status is 0x%08x\n", temp);
+			isp1763_reg_write32(hcd->dev, hcd->regs.ports[wIndex],
+					    temp | PORT_CSC);
+			break;
+		case USB_PORT_FEAT_C_OVER_CURRENT:
+			isp1763_reg_write32(hcd->dev, hcd->regs.ports[wIndex],
+					    temp | PORT_OCC);
+			break;
+		default:
+			goto error;
+
+		}
+		break;
+
+	case GetHubDescriptor:
+		pehci_hub_descriptor(hcd, (struct usb_hub_descriptor *)	buf);
+		break;
+
+	case GetHubStatus:
+		pehci_print("GetHubStatus:0x%x\n", GetHubStatus);
+		/* no hub-wide feature/status flags */
+		memset(buf, 0, 4);
+		break;
+	case GetPortStatus:
+		pehci_print("GetPortStatus:0x%x\n", GetPortStatus);
+		if (!wIndex || wIndex >	(ports & 0xf)) {
+			pehci_info
+				("GetPortStatus,not valid port number %d, should be %d\n",
+				 wIndex, (ports	& 0xf));
+			goto error;
+		}
+		wIndex--;
+		status = 0;
+		temp = isp1763_reg_read32(hcd->dev, hcd->regs.ports[wIndex],
+					  temp);
+		printk("root port status:0x%x\n", temp);
+		/*connect status chnage	*/
+		if (temp & PORT_CSC) {
+			status |= 1 << USB_PORT_FEAT_C_CONNECTION;
+			pehci_print("feature CSC 0x%08x	and status 0x%08x  \n",
+				    temp, status);
+		}
+		if(portchange){
+			portchange=0;
+			status |= 1 << USB_PORT_FEAT_C_CONNECTION;
+		}
+		/*port enable change */
+		if (temp & PORT_PEC) {
+			status |= 1 << USB_PORT_FEAT_C_ENABLE;
+			pehci_print("feature PEC  0x%08x and status 0x%08x  \n",
+				    temp, status);
+		}
+		/*port over-current */
+		if (temp & PORT_OCC) {
+			status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT;
+			pehci_print("feature OCC 0x%08x	and status 0x%08x  \n",
+				    temp, status);
+		}
+
+		/* whoever resets must GetPortStatus to	complete it!! */
+		if ((temp & PORT_RESET)	&& jiffies > hcd->reset_done[wIndex]) {
+			status |= 1 << USB_PORT_FEAT_C_RESET;
+			pehci_print("feature reset 0x%08x and status 0x%08x\n",
+				temp, status);
+			printk(KERN_NOTICE
+				"feature	reset 0x%08x and status	0x%08x\n", temp,
+				status);
+			/* force reset to complete */
+			isp1763_reg_write32(hcd->dev, hcd->regs.ports[wIndex],
+					    temp & ~PORT_RESET);
+			do {
+				mdelay(20);
+				temp = isp1763_reg_read32(hcd->dev,
+							  hcd->regs.
+							  ports[wIndex], temp);
+			} while	(temp &	PORT_RESET);
+
+			/* see what we found out */
+			printk(KERN_NOTICE "after portreset: %x\n", temp);
+
+			temp = phci_check_reset_complete(hcd, wIndex, temp);
+			printk(KERN_NOTICE "after checkportreset: %x\n", temp);
+		}
+
+		/* don't show wPortStatus if it's owned	by a companion hc */
+
+		if (!(temp & PORT_OWNER)) {
+
+			if (temp & PORT_CONNECT) {
+				status |= 1 << USB_PORT_FEAT_CONNECTION;
+				status |= 1 << USB_PORT_FEAT_HIGHSPEED;
+			}
+			if (temp & PORT_PE) {
+				status |= 1 << USB_PORT_FEAT_ENABLE;
+			}
+			if (temp & PORT_SUSPEND) {
+				status |= 1 << USB_PORT_FEAT_SUSPEND;
+			}
+			if (temp & PORT_OC) {
+				status |= 1 << USB_PORT_FEAT_OVER_CURRENT;
+			}
+			if (temp & PORT_RESET) {
+				status |= 1 << USB_PORT_FEAT_RESET;
+			}
+			if (temp & PORT_POWER) {
+				status |= 1 << USB_PORT_FEAT_POWER;
+			}
+		}
+
+		/* This	alignment is good, caller used kmalloc() */
+		*((u32 *) buf) = cpu_to_le32(status);
+		break;
+
+	case SetHubFeature:
+		pehci_print("SetHubFeature:0x%x\n", SetHubFeature);
+		switch (wValue)	{
+		case C_HUB_LOCAL_POWER:
+		case C_HUB_OVER_CURRENT:
+			/* no hub-wide feature/status flags */
+			break;
+		default:
+			goto error;
+		}
+		break;
+	case SetPortFeature:
+		pehci_print("SetPortFeature:%x\n", SetPortFeature);
+		if (!wIndex || wIndex >	(ports & 0xf)) {
+			pehci_info
+				("SetPortFeature not valid port	number %d, should be %d\n",
+				 wIndex, (ports	& 0xf));
+			goto error;
+		}
+		wIndex--;
+		temp = isp1763_reg_read32(hcd->dev, hcd->regs.ports[wIndex],
+					  temp);
+		pehci_print("SetPortFeature:PortSc Val 0x%x\n",	temp);
+		if (temp & PORT_OWNER) {
+			break;
+		}
+		switch (wValue)	{
+		case USB_PORT_FEAT_ENABLE:
+			/*enable the port */
+			isp1763_reg_write32(hcd->dev, hcd->regs.ports[wIndex],
+				temp | PORT_PE);
+			break;
+		case USB_PORT_FEAT_SUSPEND:
+			
+			#if 0 /* Port suspend will be added in suspend function */
+			isp1763_reg_write32(hcd->dev, hcd->regs.ports[wIndex],
+				temp | PORT_SUSPEND);
+			#endif
+			
+			break;
+		case USB_PORT_FEAT_POWER:
+			pehci_print("Set Port Power 0x%x and Ports %x\n",
+				USB_PORT_FEAT_POWER, ports);
+			if (ports & 0x10) {
+				printk(KERN_NOTICE
+					"PortSc Reg %x an Value %x\n",
+					hcd->regs.ports[wIndex],
+					(temp | PORT_POWER));
+
+				isp1763_reg_write32(hcd->dev,
+					hcd->regs.ports[wIndex],
+					temp | PORT_POWER);
+			}
+			break;
+		case USB_PORT_FEAT_RESET:
+			pehci_print("Set Port Reset 0x%x\n",
+				USB_PORT_FEAT_RESET);
+			if ((temp & (PORT_PE | PORT_CONNECT)) == PORT_CONNECT
+				&& PORT_USB11(temp)) {
+				printk("error:port %d low speed	--> companion\n", wIndex + 1);
+				temp |=	PORT_OWNER;
+			} else {
+				temp |=	PORT_RESET;
+				temp &=	~PORT_PE;
+
+				/*
+				 * caller must wait, then call GetPortStatus
+				 * usb 2.0 spec	says 50	ms resets on root
+				 */
+				hcd->reset_done[wIndex]	= jiffies
+					+ ((50 /* msec */  * HZ) / 1000);
+			}
+			isp1763_reg_write32(hcd->dev, hcd->regs.ports[wIndex],
+				temp);
+			break;
+		default:
+			goto error;
+		}
+		break;
+	default:
+		pehci_print("this request doesnt fit anywhere\n");
+	error:
+		/* "stall" on error */
+		pehci_info
+			("unhandled root hub request: typereq 0x%08x, wValue %d, wIndex	%d\n",
+			 typeReq, wValue, wIndex);
+		retval = -EPIPE;
+	}
+
+	pehci_info("rh_control:exit\n");
+	spin_unlock_irqrestore(&hcd->lock, flags);
+	return retval;
+}
+
+
+
+/*-------------------------------------------------------------------------*/
+
+static const struct hc_driver pehci_driver = {
+	.description = hcd_name,
+	.product_desc =	"ST-ERICSSON ISP1763",
+	.hcd_priv_size = sizeof(phci_hcd),
+#ifdef LINUX_2620
+	.irq = NULL,
+#else
+	.irq = pehci_hcd_irq,
+#endif
+	/*
+	 * generic hardware linkage
+	 */
+	.flags = HCD_USB2 | HCD_MEMORY,
+
+	/*
+	 * basic lifecycle operations
+	 */
+	.reset = pehci_hcd_reset,
+	.start = pehci_hcd_start,
+	.bus_suspend = pehci_bus_suspend,
+	.bus_resume  = pehci_bus_resume,
+	.stop =	pehci_hcd_stop,
+	/*
+	 * managing i/o	requests and associated	device resources
+	 */
+	.urb_enqueue = pehci_hcd_urb_enqueue,
+	.urb_dequeue = pehci_hcd_urb_dequeue,
+	.endpoint_disable = pehci_hcd_endpoint_disable,
+
+	/*
+	 * scheduling support
+	 */
+	.get_frame_number = pehci_hcd_get_frame_number,
+
+	/*
+	 * root	hub support
+	 */
+	.hub_status_data = pehci_rh_status_data,
+	.hub_control = pehci_rh_control,
+};
+
+/*probe the PCI host*/
+
+#ifdef THREAD_BASED
+int pehci_hcd_process_irq_it_handle(struct usb_hcd* usb_hcd_)
+{
+	int istatus;
+	
+	struct usb_hcd 		*usb_hcd;
+	char					uIntStatus;
+	phci_hcd    *pehci_hcd;
+
+	struct list_head *pos, *lst_tmp;
+	st_UsbIt_Msg_Struc *mess;
+	unsigned long flags;
+	
+	g_stUsbItThreadHandler.phThreadTask = current;
+	siginitsetinv(&((g_stUsbItThreadHandler.phThreadTask)->blocked), sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM));		
+	pehci_info("pehci_hcd_process_irq_it_thread ID : %d\n", g_stUsbItThreadHandler.phThreadTask->pid);
+	
+	while (1)
+	{
+		if (signal_pending(g_stUsbItThreadHandler.phThreadTask))
+		{
+	       	printk("thread handler:  Thread received signal\n");
+	       	break;
+		}
+
+		spin_lock(&g_stUsbItThreadHandler.lock);
+		g_stUsbItThreadHandler.lThrdWakeUpNeeded = 0;
+		spin_unlock(&g_stUsbItThreadHandler.lock);
+		
+		/* Wait until a signal arrives or we are woken up or timeout (5second)*/
+		istatus = wait_event_interruptible_timeout(g_stUsbItThreadHandler.ulThrdWaitQhead, (g_stUsbItThreadHandler.lThrdWakeUpNeeded== 1), msecs_to_jiffies(MSEC_INTERVAL_CHECKING));
+
+		local_irq_save(flags); /*disable interrupt*/
+		spin_lock(&g_stUsbItThreadHandler.lock);
+		g_stUsbItThreadHandler.lThrdWakeUpNeeded = 1;
+		spin_unlock(&g_stUsbItThreadHandler.lock);
+		//receive mess	
+		if (!list_empty(&g_messList.list)) //mess list not empty
+		{
+
+			list_for_each_safe(pos, lst_tmp, &(g_messList.list))
+			{
+				mess = list_entry(pos, st_UsbIt_Msg_Struc, list);
+
+				usb_hcd = mess->usb_hcd;
+				uIntStatus = mess->uIntStatus;
+				//pehci_print("-------------receive mess : %d------------\n",uIntStatus);
+				pehci_hcd = usb_hcd_to_pehci_hcd(usb_hcd);
+				if((uIntStatus & NO_SOF_REQ_IN_TSK)  || (uIntStatus & NO_SOF_REQ_IN_ISR) || (uIntStatus & NO_SOF_REQ_IN_REQ))
+					pehci_interrupt_handler(pehci_hcd);
+				spin_lock(&g_stUsbItThreadHandler.lock);
+				list_del(pos);
+				kfree(mess);
+				spin_unlock(&g_stUsbItThreadHandler.lock);				
+			}
+		}
+		else if(!list_empty(&g_enqueueMessList.list))
+		{
+			mess = list_first_entry(&(g_enqueueMessList.list), st_UsbIt_Msg_Struc, list);
+			usb_hcd = mess->usb_hcd;
+			uIntStatus = mess->uIntStatus;
+
+			pehci_print("-------------receive mess : %d------------\n",uIntStatus);
+			pehci_hcd = usb_hcd_to_pehci_hcd(usb_hcd);
+			if((uIntStatus & NO_SOF_REQ_IN_REQ))
+			{
+				pehci_interrupt_handler(pehci_hcd);
+			}	
+
+			{
+				spin_lock(&enqueue_lock);
+				list_del((g_enqueueMessList.list).next);
+				kfree(mess);
+				spin_unlock(&enqueue_lock);
+			}	
+		}
+		else if(istatus == 0) //timeout
+		{
+			pehci_hcd = NULL;
+			pehci_hcd = usb_hcd_to_pehci_hcd(usb_hcd_);
+			pehci_interrupt_handler(pehci_hcd);
+
+		}
+		local_irq_restore(flags);  /*enable interrupt*/
+	}
+
+	flush_signals(g_stUsbItThreadHandler.phThreadTask);
+	g_stUsbItThreadHandler.phThreadTask = NULL;
+	return 0;
+	
+}
+
+int pehci_hcd_process_irq_in_thread(struct usb_hcd* usb_hcd_)
+{
+	
+	//status = msgq_create("usb_it_queue", 10, sizeof(st_UsbIt_Msg_Struc), &uUsbIt_MsgQueId);
+	INIT_LIST_HEAD(&g_messList.list);
+	INIT_LIST_HEAD(&g_enqueueMessList.list);
+	spin_lock_init(&enqueue_lock);
+
+	memset(&g_stUsbItThreadHandler, 0, sizeof(st_UsbIt_Thread));
+	init_waitqueue_head(&(g_stUsbItThreadHandler.ulThrdWaitQhead));
+	g_stUsbItThreadHandler.lThrdWakeUpNeeded = 0;
+	spin_lock_init(&g_stUsbItThreadHandler.lock);
+	kernel_thread(pehci_hcd_process_irq_it_handle, usb_hcd_, 0);
+	
+    return 0;
+}
+#endif
+
+
+/*probe	the PCI	host*/
+int
+pehci_hcd_probe(struct isp1763_dev *isp1763_dev, isp1763_id * ids)
+{
+#ifdef NON_PCI
+    struct platform_device *dev = isp1763_dev->dev;
+#else /* PCI */
+	struct pci_dev *dev = isp1763_dev->pcidev;
+#endif
+	struct usb_hcd *usb_hcd;
+	phci_hcd *pehci_hcd;
+	int status = 0;
+
+#ifndef NON_PCI
+	u32 intcsr=0;
+#endif
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+	if (usb_disabled()) {
+		return -ENODEV;
+	}
+
+	usb_hcd	= usb_create_hcd(&pehci_driver,&dev->dev, "ISP1763");
+
+	if (usb_hcd == NULL) {
+		status = -ENOMEM;
+		goto clean;
+	}
+
+	/* this	is our host */
+	pehci_hcd = usb_hcd_to_pehci_hcd(usb_hcd);
+	pehci_hcd->dev = isp1763_dev;
+	pehci_hcd->iobase = (u8	*) isp1763_dev->baseaddress;
+	pehci_hcd->iolength = isp1763_dev->length;
+
+
+	/* lets	keep our host here */
+	isp1763_dev->driver_data = usb_hcd;
+#ifdef NON_PCI
+//Do nothing
+#else
+	/* Enable the interrupts from PLX to PCI */
+	/* CONFIGURE PCI/PLX interrupt */
+#ifdef DATABUS_WIDTH_16
+	wvalue1	= readw(pehci_hcd->plxiobase + 0x68);
+	wvalue2	= readw(pehci_hcd->plxiobase + 0x68 + 2);
+	intcsr |= wvalue2;
+	intcsr <<= 16;
+	intcsr |= wvalue1;
+	printk(KERN_NOTICE "Enable PCI Intr: %x	\n", intcsr);
+	intcsr |= 0x900;
+	writew((u16) intcsr, pehci_hcd->plxiobase + 0x68);
+	writew((u16) (intcsr >>	16), pehci_hcd->plxiobase + 0x68 + 2);
+#else
+	bvalue1	= readb(pehci_hcd->plxiobase + 0x68);
+	bvalue2	= readb(pehci_hcd->plxiobase + 0x68 + 1);
+	bvalue3	= readb(pehci_hcd->plxiobase + 0x68 + 2);
+	bvalue4	= readb(pehci_hcd->plxiobase + 0x68 + 3);
+	intcsr |= bvalue4;
+	intcsr <<= 8;
+	intcsr |= bvalue3;
+	intcsr <<= 8;
+	intcsr |= bvalue2;
+	intcsr <<= 8;
+	intcsr |= bvalue1;
+	writeb((u8) intcsr, pehci_hcd->plxiobase + 0x68);
+	writeb((u8) (intcsr >> 8), pehci_hcd->plxiobase	+ 0x68 + 1);
+	writeb((u8) (intcsr >> 16), pehci_hcd->plxiobase + 0x68	+ 2);
+	writeb((u8) (intcsr >> 24), pehci_hcd->plxiobase + 0x68	+ 3);
+#endif
+#endif
+
+	No_Data_Phase =	0;
+	No_Status_Phase	= 0;
+	usb_hcd->self.controller->dma_mask = 0;
+	usb_hcd->self.otg_port = 1;
+#if 0
+#ifndef THREAD_BASED 	
+	status = isp1763_request_irq(pehci_hcd_irq, isp1763_dev, usb_hcd);
+#endif
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	if (status == 0) {
+		status = usb_add_hcd(usb_hcd, isp1763_dev->irq, SA_SHIRQ);
+	}
+#else /* Linux 2.6.28*/
+	usb_hcd->self.uses_dma = 0;
+	if (status == 0){
+		status = usb_add_hcd(usb_hcd, isp1763_dev->irq,
+		IRQF_SHARED | IRQF_DISABLED | IRQF_TRIGGER_LOW);
+	}
+#endif
+
+#ifdef THREAD_BASED 	
+	g_pehci_hcd = pehci_hcd;
+#endif
+
+#ifdef USBNET 
+	// initialize clean up urb list
+	INIT_LIST_HEAD(&(pehci_hcd->cleanup_urb.urb_list));
+#endif
+	enable_irq_wake(isp1763_dev->irq);
+	wake_lock_init(&pehci_wake_lock, WAKE_LOCK_SUSPEND,
+						dev_name(&dev->dev));
+	wake_lock(&pehci_wake_lock);
+
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+	isp1763_hcd=isp1763_dev;
+	return status;
+
+	clean:
+	return status;
+
+}
+/*--------------------------------------------------------------*
+ *
+ *  Module details: pehci_hcd_powerup
+ *
+ *  This function powerdown the chip completely, which make chip works in minimal power
+ *
+ *  Input: struct isp1763_Dev *
+ *
+ *
+ *  
+ *
+ *  Called by: IOCTL function
+ *
+ *
+ --------------------------------------------------------------*/
+void 
+pehci_hcd_powerup(struct	isp1763_dev *dev)
+{
+	printk("%s\n", __FUNCTION__);
+	hcdpowerdown = 0;
+	dev->driver->probe(dev,dev->driver->id);
+
+	
+}
+void
+pehci_hcd_powerdown(struct	isp1763_dev *dev)
+{
+	struct usb_hcd *usb_hcd;
+
+	phci_hcd *hcd = NULL;
+	u32 temp;
+	usb_hcd = (struct usb_hcd *) dev->driver_data;
+	if (!usb_hcd) {
+		return;
+	}
+	
+	printk("%s\n", __FUNCTION__);
+	hcd = usb_hcd_to_pehci_hcd(usb_hcd);
+
+	temp = isp1763_reg_read16(dev, HC_USBCMD_REG, 0);
+	temp &= ~0x01;		/* stop the controller first */
+	isp1763_reg_write16(dev, HC_USBCMD_REG, temp);
+	printk("++ %s: Entered\n", __FUNCTION__);
+
+//	isp1763_free_irq(dev,usb_hcd);
+	usb_remove_hcd(usb_hcd);
+	dev->driver_data = NULL;
+	
+
+	temp = isp1763_reg_read16(dev, HC_INTENABLE_REG, temp); //0xD6
+	temp &= ~0x400;		/*disable otg interrupt*/
+	isp1763_reg_write16(dev, HC_INTENABLE_REG, temp); //0xD6
+
+	isp1763_reg_write16(dev, HC_UNLOCK_DEVICE, 0xAA37);	/*unlock the device 0x7c*/
+	mdelay(1);
+	temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0);
+
+
+	if ((temp & 0x1005) == 0x1005) {
+		isp1763_reg_write32(dev, HC_PORTSC1_REG, 0x1000);
+		temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0);
+		mdelay(10);
+		isp1763_reg_write32(dev, HC_PORTSC1_REG, 0x1104);
+		mdelay(10);
+		isp1763_reg_write32(dev, HC_PORTSC1_REG, 0x1007);
+		temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0);
+		mdelay(10);
+		isp1763_reg_write32(dev, HC_PORTSC1_REG, 0x1005);
+
+		temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0);
+	}
+	
+	printk("port status %x\n ", temp);
+	temp &= ~0x2;
+	temp &= ~0x40;		/*force port resume*/
+	temp |= 0x80;		/*suspend*/
+
+	isp1763_reg_write32(dev, HC_PORTSC1_REG, temp);
+	printk("port status %x\n ", temp);
+	mdelay(200);
+
+	temp = isp1763_reg_read16(dev, HC_HW_MODE_REG, 0);	/*suspend the device first 0xc*/
+	temp |= 0x2c;
+	isp1763_reg_write16(dev, HC_HW_MODE_REG, temp); //0xc
+	mdelay(20);
+
+	temp = isp1763_reg_read16(dev, HC_HW_MODE_REG, 0); //0xc
+	temp = 0xc;
+	isp1763_reg_write16(dev, HC_HW_MODE_REG, temp); //0xc
+
+	isp1763_reg_write32(dev, HC_POWER_DOWN_CONTROL_REG, 0xffff0800);
+
+	wake_unlock(&pehci_wake_lock);
+	wake_lock_destroy(&pehci_wake_lock);
+
+	hcdpowerdown = 1;
+	
+}
+
+static int pehci_bus_suspend(struct usb_hcd *usb_hcd)
+{
+	u32 temp=0;
+	unsigned long flags;
+	phci_hcd *pehci_hcd = NULL;
+	struct isp1763_dev *dev = NULL;
+
+	
+	if (!usb_hcd) {
+		return -EBUSY;
+	}
+	
+	printk("++ %s \n",__FUNCTION__);
+	pehci_hcd = usb_hcd_to_pehci_hcd(usb_hcd);
+
+	dev = pehci_hcd->dev;
+	
+	spin_lock_irqsave(&pehci_hcd->lock, flags);
+	if(hcdpowerdown){
+		spin_unlock_irqrestore(&pehci_hcd->lock, flags);
+		return 0;
+	}
+
+
+	isp1763_reg_write32(dev, HC_USBSTS_REG, 0x4); //0x90
+	isp1763_reg_write32(dev, HC_INTERRUPT_REG_EHCI, 0x4); //0x94
+	isp1763_reg_write16(dev, HC_INTERRUPT_REG, INTR_ENABLE_MASK); //0xd4
+
+	temp=isp1763_reg_read16(dev, HC_INTERRUPT_REG, 0); //0xd4
+
+	isp1763_reg_write16(dev,HC_INTENABLE_REG,INTR_ENABLE_MASK);
+	temp=isp1763_reg_read16(dev,HC_INTENABLE_REG,0);
+
+	hcdpowerdown = 1;
+	
+	/* stop the controller first */
+	temp = isp1763_reg_read16(dev, HC_USBCMD_REG, 0);
+	temp &= ~0x01;		
+	isp1763_reg_write16(dev, HC_USBCMD_REG, temp);
+
+	/* suspend root port which will suspend host controller of the ISP1763A */
+	temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0);
+	temp |= (PORT_SUSPEND);//0x80
+	isp1763_reg_write32(dev, HC_PORTSC1_REG, temp);
+	
+	/* suspend device controller of the ISP1763a*/
+	temp = isp1763_reg_read16(dev, HC_HW_MODE_REG, 0);
+	temp |= 0x20;
+	isp1763_reg_write16(dev, HC_HW_MODE_REG, temp);
+	mdelay(1); // make sure there will not be huge delay here max is 1 ms
+	temp &= ~0x20;
+	isp1763_reg_write16(dev, HC_HW_MODE_REG, temp);
+	/* put host controoler into low power mode */
+	isp1763_reg_write32(dev, HC_POWER_DOWN_CONTROL_REG, POWER_DOWN_CTRL_SUSPEND_VALUE);
+
+//	usb_hcd->state = HC_STATE_SUSPENDED;
+
+	spin_unlock_irqrestore(&pehci_hcd->lock, flags);
+
+	printk("-- %s \n",__FUNCTION__);
+
+	wake_unlock(&pehci_wake_lock);
+
+	return 0;
+	
+
+}
+
+static int pehci_bus_resume(struct usb_hcd *usb_hcd)
+{
+	u32 temp,i;
+	phci_hcd *pehci_hcd = NULL;
+	struct isp1763_dev *dev = NULL;
+	unsigned long flags;
+	u32 portsc1;
+
+	printk("%s Enter \n",__func__);
+
+	if (!usb_hcd) {
+		return -EBUSY;
+	}
+
+	if(hcdpowerdown ==0){
+		printk("%s already executed\n ",__func__);
+		return 0;
+	}
+
+	pehci_hcd = usb_hcd_to_pehci_hcd(usb_hcd);
+	dev = pehci_hcd->dev;
+	spin_lock_irqsave(&pehci_hcd->lock, flags);
+
+	for (temp = 0; temp < 100; temp++)
+	{
+		i = isp1763_reg_read32(dev, HC_CHIP_ID_REG, 0);
+		if(i==0x176320)
+			break;
+		mdelay(2);
+	}
+	printk("temp=%d, chipid:0x%x \n",temp,i);
+	mdelay(10);
+	isp1763_reg_write16(dev, HC_UNLOCK_DEVICE, 0xAA37);	/*unlock the device 0x7c*/
+	i = isp1763_reg_read32(dev, HC_POWER_DOWN_CONTROL_REG, 0);
+	printk("POWER DOWN CTRL REG value during suspend =0x%x\n", i);
+	for (temp = 0; temp < 100; temp++) {
+		mdelay(1);
+		isp1763_reg_write32(dev, HC_POWER_DOWN_CONTROL_REG, POWER_DOWN_CTRL_NORMAL_VALUE);
+		mdelay(1);
+		i = isp1763_reg_read32(dev, HC_POWER_DOWN_CONTROL_REG, 0);
+		if(i==POWER_DOWN_CTRL_NORMAL_VALUE)
+			break;
+	}
+	if (temp == 100) {
+		spin_unlock_irqrestore(&pehci_hcd->lock, flags);
+		pr_err("%s:isp1763a failed to resume\n", __func__);
+		return -1;
+	}
+
+	wake_lock(&pehci_wake_lock);
+
+	printk("%s: Powerdown Reg Val: 0x%08x -- %d\n", __func__, i, temp);
+
+	isp1763_reg_write32(dev, HC_USBSTS_REG,0x0); //0x90
+	isp1763_reg_write32(dev, HC_INTERRUPT_REG_EHCI, 0x0); //0x94
+	isp1763_reg_write16(dev, HC_INTENABLE_REG,0); //0xD6
+
+	portsc1 = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0);
+	printk("%s PORTSC1: 0x%x\n", __func__, portsc1);
+
+	temp = isp1763_reg_read16(dev, HC_USBCMD_REG, 0);
+	temp |= 0x01;		/* Start the controller */
+	isp1763_reg_write16(dev, HC_USBCMD_REG, temp);
+	mdelay(10);
+
+	temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0);
+	if (temp & PORT_SUSPEND)
+		pr_err("%s: HC_PORTSC1_REG: 0x%08x\n", __func__, temp);
+	temp |= PORT_SUSPEND;    //0x80;
+	isp1763_reg_write32(dev, HC_PORTSC1_REG, temp);
+	mdelay(50);
+	temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0);
+	temp |= PORT_RESUME;     //0x40;
+	temp &= ~(PORT_SUSPEND); //0x80;		/*suspend*/
+	isp1763_reg_write32(dev, HC_PORTSC1_REG, temp);
+	temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0);
+	temp &= ~(PORT_RESUME);  //0x40;
+	isp1763_reg_write32(dev, HC_PORTSC1_REG, temp);
+
+	temp = INTR_ENABLE_MASK;
+	isp1763_reg_write16(dev, HC_INTENABLE_REG, temp); //0xD6
+	temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0);
+	printk("%s resume port status: 0x%x\n", __func__, temp);
+	if(!(temp & 0x4)){ //port is disabled
+		isp1763_reg_write16(dev, HC_INTENABLE_REG, 0x1005); //0xD6
+		mdelay(10);
+	}
+//	phci_resume_wakeup(dev);
+
+	hcdpowerdown = 0;
+	if(hubdev){
+		hubdev->hcd_priv    = NULL;
+		hubdev->hcd_suspend = NULL;
+	}
+
+	spin_unlock_irqrestore(&pehci_hcd->lock, flags);
+	printk("%s Leave\n",__func__);
+
+	return 0;
+}
+
+void
+pehci_hcd_resume(struct	isp1763_dev *dev)
+{
+	struct usb_hcd *usb_hcd;
+	u32 temp,i;
+	usb_hcd = (struct usb_hcd *) dev->driver_data;
+	if (!usb_hcd) {
+		return;
+	}
+
+	if(hcdpowerdown ==0){
+		return ;
+	}
+
+	printk("%s \n",__FUNCTION__);
+
+	for (temp = 0; temp < 10; temp++)
+	{
+	i = isp1763_reg_read32(dev, HC_CHIP_ID_REG, 0);
+	printk("temp=%d, chipid:0x%x \n",temp,i);
+	if(i==0x176320)
+	break;
+	mdelay(1);
+	}
+
+	/* Start the controller */
+	temp = 0x01;		
+	isp1763_reg_write16(dev, HC_USBCMD_REG, temp);
+
+	/* update power down control reg value */
+	for (temp = 0; temp < 100; temp++) {
+		isp1763_reg_write32(dev, HC_POWER_DOWN_CONTROL_REG, POWER_DOWN_CTRL_NORMAL_VALUE);
+		i = isp1763_reg_read32(dev, HC_POWER_DOWN_CONTROL_REG, 0);
+		if(i==POWER_DOWN_CTRL_NORMAL_VALUE)
+		break;
+	}
+	
+	if (temp == 100) {
+		pr_err("%s:isp1763a failed to resume\n", __func__);
+		return;
+	}
+
+	wake_lock(&pehci_wake_lock);
+
+	isp1763_reg_write16(dev, HC_INTENABLE_REG,0); //0xD6
+	isp1763_reg_write32(dev,HC_INTERRUPT_REG_EHCI,0x4); //0x94 
+	isp1763_reg_write32(dev,HC_INTERRUPT_REG,0xFFFF); //0x94 
+	/* clear suspend bit and resume bit */	
+	temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0);
+	temp &= ~(PORT_SUSPEND); //0x80;		/*suspend*/
+	temp &= ~(PORT_RESUME);  // 0x40;
+	isp1763_reg_write32(dev, HC_PORTSC1_REG, temp);
+	
+	isp1763_reg_write16(dev, HC_INTENABLE_REG, INTR_ENABLE_MASK); //0xD6
+	/*this is just make sure port is resumed back */	
+	mdelay(1);
+	temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0);
+	printk("after hcd resume :port status %x\n ", temp);
+	
+	hcdpowerdown = 0;	
+
+	phci_resume_wakeup(dev);
+
+	if(hubdev){
+		hubdev->hcd_priv=NULL;
+		hubdev->hcd_suspend=NULL;
+	}
+//	usb_hcd->state = HC_STATE_RUNNING;
+
+}
+
+
+void
+pehci_hcd_suspend(struct isp1763_dev *dev)
+{
+	struct usb_hcd *usb_hcd;
+	u32 temp;
+	usb_hcd = (struct usb_hcd *) dev->driver_data;
+	if (!usb_hcd) {
+		return;
+	}
+	printk("%s \n",__FUNCTION__);
+	if(hcdpowerdown){
+		return ;
+	}
+
+	temp = isp1763_reg_read16(dev, HC_USBCMD_REG, 0);
+	temp &= ~0x01;		/* stop the controller first */
+	isp1763_reg_write16(dev, HC_USBCMD_REG, temp);
+
+	isp1763_reg_write32(dev, HC_USBSTS_REG, 0x4); //0x90
+	isp1763_reg_write32(dev, HC_INTERRUPT_REG_EHCI, 0x4); //0x94
+	isp1763_reg_write16(dev, HC_INTERRUPT_REG, INTR_ENABLE_MASK); //0xd4
+	
+	temp=isp1763_reg_read16(dev, HC_INTERRUPT_REG, 0); //0xd4
+
+	printk("suspend :Interrupt Status %x\n",temp);
+	isp1763_reg_write16(dev,HC_INTENABLE_REG,INTR_ENABLE_MASK);
+	temp=isp1763_reg_read16(dev,HC_INTENABLE_REG,0);
+	printk("suspend :Interrupt Enable %x\n",temp);
+	temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0);
+	
+	printk("suspend :port status %x\n ", temp);
+	temp &= ~0x2;
+	temp &= ~0x40;		/*force port resume*/
+	temp |= 0x80;		/*suspend*/
+//	temp |= 0x700000;	/*WKCNNT_E,WKDSCNNT_E,WKOC_E*/
+	isp1763_reg_write32(dev, HC_PORTSC1_REG, temp);
+  //  mdelay(10);
+	temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0);
+	printk("suspend :port status %x\n ", temp);
+	hcdpowerdown = 1;
+
+
+	temp = isp1763_reg_read16(dev,HC_HW_MODE_REG, 0);	/*suspend the device first 0xc*/
+	temp&=0xff7b;
+	isp1763_reg_write16(dev, HC_HW_MODE_REG, temp); //0xc
+
+
+	temp = isp1763_reg_read16(dev, HC_HW_MODE_REG, 0);	/*suspend the device first 0xc*/
+	temp |= 0x20;
+	isp1763_reg_write16(dev, HC_HW_MODE_REG, temp);//0xc
+	mdelay(2);
+	temp = isp1763_reg_read16(dev, HC_HW_MODE_REG, 0);//0xc
+	temp &= 0xffdf;
+	temp &= ~0x20;
+	isp1763_reg_write16(dev, HC_HW_MODE_REG, temp);//0xc
+
+	isp1763_reg_write32(dev, HC_POWER_DOWN_CONTROL_REG, 0xffff0830);
+
+	wake_unlock(&pehci_wake_lock);
+	
+}
+
+void 
+pehci_hcd_remotewakeup(struct isp1763_dev *dev){
+	if(hubdev){
+		hubdev->hcd_priv=dev;
+		hubdev->hcd_suspend=(void *)pehci_hcd_suspend;
+		}
+	phci_remotewakeup(dev);
+}
+
+/*remove the host controller*/
+static void
+pehci_hcd_remove(struct	isp1763_dev *isp1763_dev)
+{
+
+	struct usb_hcd *usb_hcd;
+	
+#ifdef NON_PCI
+#else	/* PCI */
+//	struct pci_dev *dev = isp1763_dev->pcidev;
+#endif
+
+	phci_hcd *hcd =	NULL;
+	u32 temp;
+	usb_hcd	= (struct usb_hcd *) isp1763_dev->driver_data;
+	if (!usb_hcd) {
+		return;
+	}
+	hcd=usb_hcd_to_pehci_hcd(usb_hcd);
+	isp1763_reg_write32(hcd->dev,hcd->regs.hwmodecontrol,0);
+	isp1763_reg_write32(hcd->dev,hcd->regs.interruptenable,0);
+	hubdev=0;
+	huburb=0;
+	temp = isp1763_reg_read16(hcd->dev, HC_USBCMD_REG, 0);
+	temp &= ~0x01;		/* stop the controller first */
+	isp1763_reg_write16(hcd->dev, HC_USBCMD_REG, temp);
+//	isp1763_free_irq(isp1763_dev,usb_hcd);
+	usb_remove_hcd(usb_hcd);
+
+	wake_unlock(&pehci_wake_lock);
+	wake_lock_destroy(&pehci_wake_lock);
+
+	return ;
+}
+
+
+static isp1763_id ids =	{
+	.idVendor = 0x04CC,	/*st ericsson isp1763 vendor_id	*/
+	.idProduct = 0x1A64,	/*st ericsson isp1763 product_id */
+	.driver_info = (unsigned long) &pehci_driver,
+};
+
+/* pci driver glue; this is a "new style" PCI driver module */
+static struct isp1763_driver pehci_hcd_pci_driver = {
+	.name =	(char *) hcd_name,
+	.index = 0,
+	.id = &ids,
+	.probe = pehci_hcd_probe,
+	.remove	= pehci_hcd_remove,
+	.suspend = pehci_hcd_suspend,
+	.resume	= pehci_hcd_resume,
+	.remotewakeup=pehci_hcd_remotewakeup,
+	.powerup	=	pehci_hcd_powerup,
+	.powerdown	=	pehci_hcd_powerdown,
+};
+
+#ifdef HCD_PACKAGE
+int
+usb_hcddev_open(struct inode *inode, struct file *fp)
+{
+
+	return 0;
+}
+
+int
+usb_hcddev_close(struct inode *inode, struct file *fp)
+{
+
+	return 0;
+}
+
+int
+usb_hcddev_fasync(int fd, struct file *fp, int mode)
+{
+
+	return fasync_helper(fd, fp, mode, &fasync_q);
+}
+
+long
+usb_hcddev_ioctl(struct file *fp,
+		 unsigned int cmd, unsigned long arg)
+{
+
+	switch (cmd) {
+	case HCD_IOC_POWERDOWN:	/* SET HCD DEEP SUSPEND MODE */
+		printk("HCD IOC POWERDOWN MODE\n");
+		if(isp1763_hcd->driver->powerdown)
+			isp1763_hcd->driver->powerdown(isp1763_hcd);
+
+		break;
+
+	case HCD_IOC_POWERUP:	/* Set HCD POWER UP */
+		printk("HCD IOC POWERUP MODE\n");
+		if(isp1763_hcd->driver->powerup)
+			isp1763_hcd->driver->powerup(isp1763_hcd);
+
+		break;
+	case HCD_IOC_TESTSE0_NACK:
+		HostComplianceTest = HOST_COMPILANCE_TEST_ENABLE;
+		HostTest = HOST_COMP_TEST_SE0_NAK;
+		break;
+	case   HCD_IOC_TEST_J:		
+		HostComplianceTest = HOST_COMPILANCE_TEST_ENABLE;
+		HostTest = HOST_COMP_TEST_J;
+		break;
+	case    HCD_IOC_TEST_K:
+		HostComplianceTest = HOST_COMPILANCE_TEST_ENABLE;
+		HostTest = HOST_COMP_TEST_K;
+		break;
+		
+	case   HCD_IOC_TEST_TESTPACKET:
+		HostComplianceTest = HOST_COMPILANCE_TEST_ENABLE;
+		HostTest = HOST_COMP_TEST_PACKET;
+		break;
+	case HCD_IOC_TEST_FORCE_ENABLE:
+		HostComplianceTest = HOST_COMPILANCE_TEST_ENABLE;
+		HostTest = HOST_COMP_TEST_FORCE_ENABLE;
+		break;
+	case	HCD_IOC_TEST_SUSPEND_RESUME:
+		HostComplianceTest = HOST_COMPILANCE_TEST_ENABLE;
+		HostTest = HOST_COMP_HS_HOST_PORT_SUSPEND_RESUME;
+		break;
+	case HCD_IOC_TEST_SINGLE_STEP_GET_DEV_DESC:
+		HostComplianceTest = HOST_COMPILANCE_TEST_ENABLE;
+		HostTest = HOST_COMP_SINGLE_STEP_GET_DEV_DESC;		
+		break;
+	case HCD_IOC_TEST_SINGLE_STEP_SET_FEATURE:
+		HostComplianceTest = HOST_COMPILANCE_TEST_ENABLE;
+		HostTest = HOST_COMP_SINGLE_STEP_SET_FEATURE;		
+		break;
+	case HCD_IOC_TEST_STOP:
+		HostComplianceTest = 0;
+		HostTest = 0;		
+		break;
+	case     HCD_IOC_SUSPEND_BUS:
+		printk("isp1763:SUSPEND bus\n");
+		if(isp1763_hcd->driver->suspend)
+			isp1763_hcd->driver->suspend(isp1763_hcd);
+		break;
+	case	HCD_IOC_RESUME_BUS:
+		printk("isp1763:RESUME bus\n");
+		if(isp1763_hcd->driver->resume)
+			isp1763_hcd->driver->resume(isp1763_hcd);		
+		break;
+	case     HCD_IOC_REMOTEWAKEUP_BUS:
+		printk("isp1763:SUSPEND bus\n");
+		if(isp1763_hcd->driver->remotewakeup)
+			isp1763_hcd->driver->remotewakeup(isp1763_hcd);
+		break;		
+	default:
+
+		break;
+
+	}
+	return 0;
+}
+
+
+/* HCD file operations */
+static struct file_operations usb_hcddev_fops = {
+	owner:THIS_MODULE,
+	read:NULL,
+	write:NULL,
+	poll:NULL,
+	unlocked_ioctl:usb_hcddev_ioctl,
+	open:usb_hcddev_open,
+	release:usb_hcddev_close,
+	fasync:usb_hcddev_fasync,
+};
+
+#endif
+
+
+static int __init
+pehci_module_init(void)
+{
+	int result = 0;
+	phci_hcd_mem_init();
+
+	/*register driver */
+	result = isp1763_register_driver(&pehci_hcd_pci_driver);
+	if (!result) {
+		info("Host Driver has been Registered");
+	} else {
+		err("Host Driver has not been Registered with errors : %x",
+			result);
+	}
+
+#ifdef THREAD_BASED 	
+	pehci_hcd_process_irq_in_thread(&(g_pehci_hcd->usb_hcd));
+   	printk("kernel_thread() Enter\n"); 
+#endif
+	
+#ifdef HCD_PACKAGE
+	printk("Register Char Driver for HCD\n");
+	result = register_chrdev(USB_HCD_MAJOR, USB_HCD_MODULE_NAME,
+		&usb_hcddev_fops);
+	
+#endif
+	return result;
+
+}
+
+static void __exit
+pehci_module_cleanup(void)
+{
+#ifdef THREAD_BASED	
+	printk("module exit:  Sending signal to stop thread\n");
+	if (g_stUsbItThreadHandler.phThreadTask != NULL)
+	{
+		send_sig(SIGKILL, g_stUsbItThreadHandler.phThreadTask, 1);
+		mdelay(6);
+	}
+#endif
+
+#ifdef HCD_PACKAGE
+	unregister_chrdev(USB_HCD_MAJOR, USB_HCD_MODULE_NAME);
+#endif
+	isp1763_unregister_driver(&pehci_hcd_pci_driver);
+}
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
+module_init(pehci_module_init);
+module_exit(pehci_module_cleanup);
diff --git a/drivers/usb/host/pehci/host/pehci.h b/drivers/usb/host/pehci/host/pehci.h
new file mode 100644
index 0000000..cc6a06b
--- /dev/null
+++ b/drivers/usb/host/pehci/host/pehci.h
@@ -0,0 +1,752 @@
+/* 
+* Copyright (C) ST-Ericsson AP Pte Ltd 2010 
+*
+* ISP1763 Linux OTG Controller driver : host
+* 
+* 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 Street, Fifth Floor, Boston, MA  02110-1301, USA. 
+* 
+* Refer to file ~/drivers/usb/host/ehci-dbg.h for copyright owners (kernel version 2.6.9)
+* Code is modified for ST-Ericsson product 
+* 
+* Author : wired support <wired.support@stericsson.com>
+*
+*/
+
+#ifndef	__PEHCI_H__
+#define	__PEHCI_H__
+
+
+#define	DRIVER_AUTHOR	"ST-ERICSSON	  "
+#define	DRIVER_DESC "ISP1763 'Enhanced'	Host Controller	(EHCI) Driver"
+
+/*    bus related stuff	*/
+#define	__ACTIVE		0x01
+#define	__SLEEPY		0x02
+#define	__SUSPEND		0x04
+#define	__TRANSIENT		0x80
+
+#define	USB_STATE_HALT		0
+#define	USB_STATE_RUNNING	(__ACTIVE)
+#define	USB_STATE_READY		(__ACTIVE|__SLEEPY)
+#define	USB_STATE_QUIESCING	(__SUSPEND|__TRANSIENT|__ACTIVE)
+#define	USB_STATE_RESUMING	(__SUSPEND|__TRANSIENT)
+#define	USB_STATE_SUSPENDED	(__SUSPEND)
+
+/* System flags	 */
+#define	HCD_MEMORY		0x0001
+#define	HCD_USB2		0x0020
+#define	HCD_USB11		0x0010
+
+#define	HCD_IS_RUNNING(state) ((state) & __ACTIVE)
+#define	HCD_IS_SUSPENDED(state)	((state) & __SUSPEND)
+
+
+/*---------------------------------------------------
+ *    Host controller related
+ -----------------------------------------------------*/
+/* IRQ line for	the ISP1763 */
+#define	HCD_IRQ			IRQ_GPIO(25)
+#define	CMD_RESET		(1<<1)	/* reset HC not	bus */
+#define	CMD_RUN			(1<<0)	/* start/stop HC */
+#define	STS_PCD			(1<<2)	/* port	change detect */
+/* NOTE:  urb->transfer_flags expected to not use this bit !!! */
+#define	EHCI_STATE_UNLINK	0x8000	/* urb being unlinked */
+
+/*  Bits definations for qha*/
+/* Bits	PID*/
+#define	SETUP_PID		(2)
+#define	OUT_PID			(0)
+#define	IN_PID			(1)
+
+/* Bits	MULTI*/
+#define	MULTI(x)		((x)<< 29)
+#define	XFER_PER_UFRAME(x)	(((x) >> 29) & 0x3)
+
+/*Active, EP type and speed bits */
+#define	QHA_VALID		(1<<0)
+#define	QHA_ACTIVE		(1<<31)
+
+/*1763 error bit maps*/
+#define	HC_MSOF_INT		(1<< 0)
+#define	HC_MSEC_INT		(1 << 1)
+#define	HC_EOT_INT		(1 << 3)
+#define     HC_OPR_REG_INT	(1<<4)
+#define     HC_CLK_RDY_INT	(1<<6)
+#define	HC_INTL_INT		(1 << 7)
+#define	HC_ATL_INT		(1 << 8)
+#define	HC_ISO_INT		(1 << 9)
+#define	HC_OTG_INT		(1 << 10)
+
+/*PTD error codes*/
+#define	PTD_STATUS_HALTED	(1 << 30)
+#define	PTD_XACT_ERROR		(1 << 28)
+#define	PTD_BABBLE		(1 << 29)
+#define PTD_ERROR		(PTD_STATUS_HALTED | PTD_XACT_ERROR | PTD_BABBLE)
+/*ep types*/
+#define	EPTYPE_BULK		(2 << 12)
+#define	EPTYPE_CONTROL		(0 << 12)
+#define	EPTYPE_INT		(3 << 12)
+#define	EPTYPE_ISO		(1 << 12)
+
+#define	PHCI_QHA_LENGTH		32
+
+#define usb_inc_dev_use		usb_get_dev
+#define usb_dec_dev_use		usb_put_dev
+#define usb_free_dev		usb_put_dev
+/*1763 host controller periodic	size*/
+#define PTD_PERIODIC_SIZE	16
+#define MAX_PERIODIC_SIZE	16
+#define PTD_FRAME_MASK		0x1f
+/*periodic list*/
+struct _periodic_list {
+	int framenumber;
+	struct list_head sitd_itd_head;
+	char high_speed;	/*1 - HS ; 0 - FS*/
+	u16 ptdlocation;
+};
+typedef	struct _periodic_list periodic_list;
+
+
+/*iso ptd*/
+struct _isp1763_isoptd {
+	u32 td_info1;
+	u32 td_info2;
+	u32 td_info3;
+	u32 td_info4;
+	u32 td_info5;
+	u32 td_info6;
+	u32 td_info7;
+	u32 td_info8;
+} __attribute__	((aligned(32)));
+
+typedef	struct _isp1763_isoptd isp1763_isoptd;
+
+struct _isp1763_qhint {
+	u32 td_info1;
+	u32 td_info2;
+	u32 td_info3;
+	u32 td_info4;
+	u32 td_info5;
+#define	INT_UNDERRUN (1	<< 2)
+#define	INT_BABBLE    (1 << 1)
+#define	INT_EXACT     (1 << 0)
+	u32 td_info6;
+	u32 td_info7;
+	u32 td_info8;
+} __attribute__	((aligned(32)));
+
+typedef	struct _isp1763_qhint isp1763_qhint;
+
+
+struct _isp1763_qha {
+	u32 td_info1;		/* First 32 bit	*/
+	u32 td_info2;		/* Second 32 bit */
+	u32 td_info3;		/* third 32 bit	*/
+	u32 td_info4;		/* fourth 32 bit */
+	u32 reserved[4];
+};
+typedef	struct _isp1763_qha isp1763_qha, *pisp1763_qha;
+
+
+
+
+/*this does not	cover all interrupts in	1763 chip*/
+typedef	struct _ehci_regs {
+
+	/*standard ehci	registers */
+	u32 command;
+	u32 usbinterrupt;
+	u32 usbstatus;
+	u32 hcsparams;
+	u32 frameindex;
+
+	/*isp1763 interrupt specific registers */
+	u16 hwmodecontrol;
+	u16 interrupt;
+	u16 interruptenable;
+	u32 interruptthreshold;
+	u16 iso_irq_mask_or;
+	u16 int_irq_mask_or;
+	u16 atl_irq_mask_or;
+	u16 iso_irq_mask_and;
+	u16 int_irq_mask_and;
+	u16 atl_irq_mask_and;
+	u16 buffer_status;
+
+	/*isp1763 initialization registers */
+	u32 reset;
+	u32 configflag;
+	u32 ports[4];
+	u32 pwrdwn_ctrl;
+
+	/*isp1763 transfer specific registers */
+	u16 isotddonemap;
+	u16 inttddonemap;
+	u16 atltddonemap;
+	u16 isotdskipmap;
+	u16 inttdskipmap;
+	u16 atltdskipmap;
+	u16 isotdlastmap;
+	u16 inttdlastmap;
+	u16 atltdlastmap;
+	u16 scratch;
+
+} ehci_regs, *pehci_regs;
+
+/*memory management structures*/
+#define MEM_KV
+#ifdef MEM_KV
+typedef struct isp1763_mem_addr {
+	u32 phy_addr;		/* Physical address of the memory */
+	u32 virt_addr;		/* after ioremap() function call */
+	u8 num_alloc;		/* In case n*smaller size is allocated then for clearing purpose */
+	u32 blk_size;		/*block size */
+	u8 blk_num;		/* number of the block */
+	u8 used;		/*used/free */
+} isp1763_mem_addr_t;
+#else
+typedef struct isp1763_mem_addr {
+	void *phy_addr;		/* Physical address of the memory */
+	void *virt_addr;	/* after ioremap() function call */
+	u8 usage;
+	u32 blk_size;		/*block size */
+} isp1763_mem_addr_t;
+
+#endif
+/* type	tag from {qh,itd,sitd,fstn}->hw_next */
+#define	Q_NEXT_TYPE(dma) ((dma)	& __constant_cpu_to_le32 (3 << 1))
+
+/* values for that type	tag */
+#define	Q_TYPE_ITD	__constant_cpu_to_le32 (0 << 1)
+#define	Q_TYPE_QH	__constant_cpu_to_le32 (1 << 1)
+#define	Q_TYPE_SITD	__constant_cpu_to_le32 (2 << 1)
+#define	Q_TYPE_FSTN	__constant_cpu_to_le32 (3 << 1)
+
+/*next queuehead in execution*/
+#define	QH_NEXT(dma)	cpu_to_le32((u32)dma)
+
+struct ehci_qh {
+	/* first part defined by EHCI spec */
+	u32 hw_next;		/* see EHCI 3.6.1 */
+	u32 hw_info1;		/* see EHCI 3.6.2 */
+
+	u32 hw_info2;		/* see EHCI 3.6.2 */
+	u32 hw_current;		/* qtd list - see EHCI 3.6.4 */
+
+	/* qtd overlay (hardware parts of a struct ehci_qtd) */
+	u32 hw_qtd_next;
+	u32 hw_alt_next;
+	u32 hw_token;
+	u32 hw_buf[5];
+	u32 hw_buf_hi[5];
+	
+	/* the rest is HCD-private */
+	dma_addr_t qh_dma;	/* address of qh */
+	struct list_head qtd_list;	/* sw qtd list */
+	struct ehci_qtd	*dummy;
+	struct ehci_qh *reclaim;	/* next	to reclaim */
+
+	atomic_t refcount;
+	wait_queue_head_t waitforcomplete;
+	unsigned stamp;
+
+	u8 qh_state;
+
+	/* periodic schedule info */
+	u8 usecs;		/* intr	bandwidth */
+	u8 gap_uf;		/* uframes split/csplit	gap */
+	u8 c_usecs;		/* ... split completion	bw */
+	unsigned short period;	/* polling interval */
+	unsigned short start;	/* where polling starts	*/
+	u8 datatoggle;		/*data toggle */
+
+	/*handling the ping stuffs */
+	u8 ping;		/*ping bit */
+
+	/*qtd <-> ptd management */
+
+	u32 qtd_ptd_index;	/* Td-PTD map index for	this ptd */
+	u32 type;		/* endpoint type */
+
+	/*iso stuffs */
+	struct usb_host_endpoint *ep;
+	int next_uframe;	/*next uframe for this endpoint	*/
+	struct list_head itd_list;	/*list of tds to this endpoint */
+	isp1763_mem_addr_t memory_addr;
+	struct _periodic_list periodic_list;
+	/*scheduling requirements for this endpoint */
+	u32 ssplit;
+	u32 csplit;
+	u8 totalptds;   // total number of PTDs needed for current URB
+	u8 actualptds;	// scheduled PTDs until now for current URB
+};
+
+/* urb private part for	the driver. */
+typedef	struct {
+	struct ehci_qh *qh;
+	u16 length;		/* number of tds associated with this request */
+	u16 td_cnt;		/* number of tds already serviced */
+	int state;		/* State machine state when URB	is deleted  */
+	int timeout;		/* timeout for bulk transfers */
+	wait_queue_head_t wait;	/* wait	State machine state when URB is	deleted	*/
+	/*FIX solve the	full speed dying */
+	struct timer_list urb_timer;
+	struct list_head qtd_list;
+	struct ehci_qtd	*qtd[0];	/* list	pointer	to all corresponding TDs associated with this request */
+
+} urb_priv_t;
+
+/*
+ * EHCI	Specification 0.95 Section 3.6
+ * QH: describes control/bulk/interrupt	endpoints
+ * See Fig 3-7 "Queue Head Structure Layout".
+ *
+ * These appear	in both	the async and (for interrupt) periodic schedules.
+ */
+
+
+/*Defination required for the ehci Queuehead */
+#define	QH_HEAD			0x00008000
+#define	QH_STATE_LINKED		1	/* HC sees this	*/
+#define	QH_STATE_UNLINK		2	/* HC may still	see this */
+#define	QH_STATE_IDLE		3	/* HC doesn't see this */
+#define	QH_STATE_UNLINK_WAIT	4	/* LINKED and on reclaim q */
+#define	QH_STATE_COMPLETING	5	/* don't touch token.HALT */
+#define	QH_STATE_TAKE_NEXT	8	/*take the new transfer	from */
+#define	NO_FRAME ((unsigned short)~0)	/* pick	new start */
+
+
+#define EHCI_ITD_TRANLENGTH	0x0fff0000	/*transaction length */
+#define EHCI_ITD_PG		0x00007000	/*page select */
+#define EHCI_ITD_TRANOFFSET	0x00000fff	/*transaction offset */
+#define EHCI_ITD_BUFFPTR	0xfffff000	/*buffer pointer */
+
+struct ehci_sitd {
+	/* first part defined by EHCI spec */
+	u32 hw_next;		/* see EHCI 3.3.1 */
+	u32 hw_transaction[8];	/* see EHCI 3.3.2 */
+#define EHCI_ISOC_ACTIVE	(1<<31)	/* activate transfer this slot */
+#define EHCI_ISOC_BUF_ERR	(1<<30)	/* Data buffer error */
+#define EHCI_ISOC_BABBLE	(1<<29)	/* babble detected */
+#define EHCI_ISOC_XACTERR	(1<<28)	/* XactErr - transaction error */
+
+#define EHCI_ITD_LENGTH(tok)	(((tok)>>16) & 0x7fff)
+#define EHCI_ITD_IOC		(1 << 15)	/* interrupt on complete */
+
+	u32 hw_bufp[7];		/* see EHCI 3.3.3 */
+	u32 hw_bufp_hi[7];	/* Appendix B */
+
+	/* the rest is HCD-private */
+	dma_addr_t sitd_dma;	/* for this itd */
+	struct urb *urb;
+	struct list_head sitd_list;	/* list of urb frames' itds */
+	dma_addr_t buf_dma;	/* frame's buffer address */
+
+	/* for now, only one hw_transaction per itd */
+	u32 transaction;
+	u16 index;		/* in urb->iso_frame_desc */
+	u16 uframe;		/* in periodic schedule */
+	u16 usecs;
+	/*memory address */
+	struct isp1763_mem_addr mem_addr;
+	int length;
+	u32 framenumber;
+	u32 ptdframe;
+	int sitd_index;
+	/*scheduling fields */
+	u32 ssplit;
+	u32 csplit;
+	u32 start_frame;
+};
+
+struct ehci_itd	{
+	/* first part defined by EHCI spec */
+	u32 hw_next;		/* see EHCI 3.3.1 */
+	u32 hw_transaction[8];	/* see EHCI 3.3.2 */
+#define	EHCI_ISOC_ACTIVE	(1<<31)	/* activate transfer this slot */
+#define	EHCI_ISOC_BUF_ERR	(1<<30)	/* Data	buffer error */
+#define	EHCI_ISOC_BABBLE	(1<<29)	/* babble detected */
+#define	EHCI_ISOC_XACTERR	(1<<28)	/* XactErr - transaction error */
+
+#define	EHCI_ITD_LENGTH(tok)	(((tok)>>16) & 0x7fff)
+#define	EHCI_ITD_IOC		(1 << 15)	/* interrupt on	complete */
+
+	u32 hw_bufp[7];		/* see EHCI 3.3.3 */
+	u32 hw_bufp_hi[7];	/* Appendix B */
+
+	/* the rest is HCD-private */
+	dma_addr_t itd_dma;	/* for this itd	*/
+	struct urb *urb;
+	struct list_head itd_list;	/* list	of urb frames' itds */
+	dma_addr_t buf_dma;	/* frame's buffer address */
+	u8 num_of_pkts;		/*number of packets for this ITD */
+	/* for now, only one hw_transaction per	itd */
+	u32 transaction;
+	u16 index;		/* in urb->iso_frame_desc */
+	u16 uframe;		/* in periodic schedule	*/
+	u16 usecs;
+	/*memory address */
+	struct isp1763_mem_addr	mem_addr;
+	int length;
+	u32 multi;
+	u32 framenumber;
+	u32 ptdframe;
+	int itd_index;
+	/*scheduling fields */
+	u32 ssplit;
+	u32 csplit;
+};
+
+/*
+ * EHCI	Specification 0.95 Section 3.5
+ * QTD:	describe data transfer components (buffer, direction, ...)
+ * See Fig 3-6 "Queue Element Transfer Descriptor Block	Diagram".
+ *
+ * These are associated	only with "QH" (Queue Head) structures,
+ * used	with control, bulk, and	interrupt transfers.
+ */
+struct ehci_qtd	{
+	/* first part defined by EHCI spec */
+	u32 hw_next;		/* see EHCI 3.5.1 */
+	u32 hw_alt_next;	/* see EHCI 3.5.2 */
+	u32 hw_token;		/* see EHCI 3.5.3 */
+
+	u32 hw_buf[5];		/* see EHCI 3.5.4 */
+	u32 hw_buf_hi[5];	/* Appendix B */
+
+	/* the rest is HCD-private */
+	dma_addr_t qtd_dma;	/* qtd address */
+	struct list_head qtd_list;	/* sw qtd list */
+	struct urb *urb;	/* qtd's urb */
+	size_t length;		/* length of buffer */
+	u32 state;		/*state	of the qtd */
+#define	QTD_STATE_NEW			0x100
+#define	QTD_STATE_DONE			0x200
+#define	QTD_STATE_SCHEDULED		0x400
+#define	QTD_STATE_LAST			0x800
+	struct isp1763_mem_addr	mem_addr;
+};
+
+#define	QTD_TOGGLE			(1 << 31)	/* data	toggle */
+#define	QTD_LENGTH(tok)			(((tok)>>16) & 0x7fff)
+#define	QTD_IOC				(1 << 15)	/* interrupt on	complete */
+#define	QTD_CERR(tok)			(((tok)>>10) & 0x3)
+#define	QTD_PID(tok)			(((tok)>>8) & 0x3)
+#define	QTD_STS_ACTIVE			(1 << 7)	/* HC may execute this */
+#define	QTD_STS_HALT			(1 << 6)	/* halted on error */
+#define	QTD_STS_DBE			(1 << 5)	/* data	buffer error (in HC) */
+#define	QTD_STS_BABBLE			(1 << 4)	/* device was babbling (qtd halted) */
+#define	QTD_STS_XACT			(1 << 3)	/* device gave illegal response	*/
+#define	QTD_STS_MMF			(1 << 2)	/* incomplete split transaction	*/
+#define	QTD_STS_STS			(1 << 1)	/* split transaction state */
+#define	QTD_STS_PING			(1 << 0)	/* issue PING? */
+
+/* for periodic/async schedules	and qtd	lists, mark end	of list	*/
+#define	EHCI_LIST_END	__constant_cpu_to_le32(1)	/* "null pointer" to hw	*/
+#define	QTD_NEXT(dma)	cpu_to_le32((u32)dma)
+
+struct _phci_driver;
+struct _isp1763_hcd;
+#define	EHCI_MAX_ROOT_PORTS 1
+
+#include <linux/usb/hcd.h>
+
+#define USBNET
+#ifdef USBNET 
+struct isp1763_async_cleanup_urb {
+        struct list_head urb_list;
+        struct urb *urb;
+};
+#endif
+
+
+/*host controller*/
+typedef	struct _phci_hcd {
+
+	struct usb_hcd usb_hcd;
+	spinlock_t lock;
+
+	/* async schedule support */
+	struct ehci_qh *async;
+	struct ehci_qh *reclaim;
+	/* periodic schedule support */
+	unsigned periodic_size;
+	int next_uframe;	/* scan	periodic, start	here */
+	int periodic_sched;	/* periodic activity count */
+	int periodic_more_urb;
+	struct usb_device *otgdev;	/*otg deice, with address 2 */
+	struct timer_list rh_timer;	/* drives root hub */
+	struct list_head dev_list;	/* devices on this bus */
+	struct list_head urb_list;	/*iso testing */
+
+	/*msec break in	interrupts */
+	atomic_t nuofsofs;
+	atomic_t missedsofs;
+
+	struct isp1763_dev *dev;
+	/*hw info */
+	u8 *iobase;
+	u32 iolength;
+	u8 *plxiobase;
+	u32 plxiolength;
+
+	int irq;		/* irq allocated */
+	int state;		/*state	of the host controller */
+	unsigned long reset_done[EHCI_MAX_ROOT_PORTS];
+	ehci_regs regs;
+
+	struct _isp1763_qha qha;
+	struct _isp1763_qhint qhint;
+	struct _isp1763_isoptd isotd;
+
+	struct tasklet_struct tasklet;
+	/*this timer is	going to run every 20 msec */
+	struct timer_list watchdog;
+	void (*worker_function)	(struct	_phci_hcd * hcd);
+	struct _periodic_list periodic_list[PTD_PERIODIC_SIZE];
+#ifdef USBNET 
+	struct isp1763_async_cleanup_urb cleanup_urb;
+#endif
+} phci_hcd, *pphci_hcd;
+
+/*usb_device->hcpriv, points to	this structure*/
+typedef	struct hcd_dev {
+	struct list_head dev_list;
+	struct list_head urb_list;
+} hcd_dev;
+
+#define	usb_hcd_to_pehci_hcd(hcd)   container_of(hcd, struct _phci_hcd,	usb_hcd)
+
+/*td allocation*/
+#ifdef CONFIG_PHCI_MEM_SLAB
+
+#define	qha_alloc(t,c) kmem_cache_alloc(c,ALLOC_FLAGS)
+#define	qha_free(c,x) kmem_cache_free(c,x)
+static kmem_cache_t *qha_cache,	*qh_cache, *qtd_cache;
+static int
+phci_hcd_mem_init(void)
+{
+	/* qha TDs accessed by controllers and host */
+	qha_cache = kmem_cache_create("phci_ptd", sizeof(isp1763_qha), 0,
+				      SLAB_HWCACHE_ALIGN, NULL,	NULL);
+	if (!qha_cache)	{
+		printk("no TD cache?");
+		return -ENOMEM;
+	}
+
+	/* qh TDs accessed by controllers and host */
+	qh_cache = kmem_cache_create("phci_ptd", sizeof(isp1763_qha), 0,
+				     SLAB_HWCACHE_ALIGN, NULL, NULL);
+	if (!qh_cache) {
+		printk("no TD cache?");
+		return -ENOMEM;
+	}
+
+	/* qtd	accessed by controllers	and host */
+	qtd_cache = kmem_cache_create("phci_ptd", sizeof(isp1763_qha), 0,
+				      SLAB_HWCACHE_ALIGN, NULL,	NULL);
+	if (!qtd_cache)	{
+		printk("no TD cache?");
+		return -ENOMEM;
+	}
+	return 0;
+}
+static void
+phci_mem_cleanup(void)
+{
+	if (qha_cache && kmem_cache_destroy(qha_cache))
+		err("td_cache remained");
+	qha_cache = 0;
+}
+#else
+
+#define	qha_alloc(t,c)			kmalloc(t,ALLOC_FLAGS)
+#define	qha_free(c,x)			kfree(x)
+#define	qha_cache			0
+
+
+#ifdef CONFIG_ISO_SUPPORT
+/*memory constants*/
+#define BLK_128_	2
+#define BLK_256_	3
+#define BLK_1024_	1
+#define BLK_2048_	3
+#define BLK_4096_	3 //1
+#define BLK_8196_	0 //1
+#define BLK_TOTAL	(BLK_128_+BLK_256_ + BLK_1024_ +BLK_2048_+ BLK_4096_+BLK_8196_)
+
+#define BLK_SIZE_128	128
+#define BLK_SIZE_256	256
+#define BLK_SIZE_1024	1024
+#define BLK_SIZE_2048	2048
+#define BLK_SIZE_4096	4096
+#define BLK_SIZE_8192	8192
+
+#define  COMMON_MEMORY	1
+
+#else
+#define BLK_256_	8
+#define BLK_1024_	6
+#define BLK_4096_	3
+#define BLK_TOTAL	(BLK_256_ + BLK_1024_ + BLK_4096_)
+#define BLK_SIZE_256	256
+#define BLK_SIZE_1024	1024
+#define BLK_SIZE_4096	4096
+#endif
+static void phci_hcd_mem_init(void);
+static inline void
+phci_mem_cleanup(void)
+{
+	return;
+}
+
+#endif
+
+#define	PORT_WKOC_E			(1<<22)	/* wake	on overcurrent (enable)	*/
+#define	PORT_WKDISC_E			(1<<21)	/* wake	on disconnect (enable) */
+#define	PORT_WKCONN_E			(1<<20)	/* wake	on connect (enable) */
+/* 19:16 for port testing */
+/* 15:14 for using port	indicator leds (if HCS_INDICATOR allows) */
+#define	PORT_OWNER			(1<<13)	/* true: companion hc owns this	port */
+#define	PORT_POWER			(1<<12)	/* true: has power (see	PPC) */
+#define	PORT_USB11(x)			(((x)&(3<<10))==(1<<10))	/* USB 1.1 device */
+/* 11:10 for detecting lowspeed	devices	(reset vs release ownership) */
+/* 9 reserved */
+#define	PORT_RESET			(1<<8)	/* reset port */
+#define	PORT_SUSPEND			(1<<7)	/* suspend port	*/
+#define	PORT_RESUME			(1<<6)	/* resume it */
+#define	PORT_OCC			(1<<5)	/* over	current	change */
+
+#define	PORT_OC				(1<<4)	/* over	current	active */
+#define	PORT_PEC			(1<<3)	/* port	enable change */
+#define	PORT_PE				(1<<2)	/* port	enable */
+#define	PORT_CSC			(1<<1)	/* connect status change */
+#define	PORT_CONNECT			(1<<0)	/* device connected */
+#define PORT_RWC_BITS	(PORT_CSC | PORT_PEC | PORT_OCC)	
+/*Legends,
+ * ATL	  control, bulk	transfer
+ * INTL	  interrupt transfer
+ * ISTL	  iso transfer
+ * */
+
+/*buffer(transfer) bitmaps*/
+#define	ATL_BUFFER			0x1
+#define	INT_BUFFER			0x2
+#define	ISO_BUFFER			0x4
+#define	BUFFER_MAP			0x7
+
+/* buffer type for ST-ERICSSON HC */
+#define	TD_PTD_BUFF_TYPE_ATL		0	/* ATL buffer */
+#define	TD_PTD_BUFF_TYPE_INTL		1	/* INTL	buffer */
+#define	TD_PTD_BUFF_TYPE_ISTL		2	/* ISO buffer */
+#define	TD_PTD_TOTAL_BUFF_TYPES		(TD_PTD_BUFF_TYPE_ISTL +1)
+/*maximum number of tds	per transfer type*/
+#define	TD_PTD_MAX_BUFF_TDS		16
+
+/*invalid td index in the headers*/
+#define	TD_PTD_INV_PTD_INDEX		0xFFFF
+/*Host controller buffer defination*/
+#define	INVALID_FRAME_NUMBER		0xFFFFFFFF
+/*per td transfer size*/
+#define	HC_ATL_PL_SIZE			4096
+#define	HC_ISTL_PL_SIZE			1024
+#define	HC_INTL_PL_SIZE			1024
+
+/*TD_PTD_MAP states*/
+#define	TD_PTD_NEW			0x0000
+#define	TD_PTD_ACTIVE			0x0001
+#define	TD_PTD_IDLE			0x0002
+#define	TD_PTD_REMOVE			0x0004
+#define	TD_PTD_RELOAD			0x0008
+#define	TD_PTD_IN_SCHEDULE		0x0010
+#define	TD_PTD_DONE			0x0020
+
+#define	PTD_RETRY(x)			(((x) >> 23) & 0x3)
+#define	PTD_PID(x)			(((x) >> 10) & (0x3))
+#define	PTD_NEXTTOGGLE(x)		(((x) >> 25) & (0x1))
+#define	PTD_XFERRED_LENGTH(x)		((x) & 0x7fff)
+#define	PTD_XFERRED_NONHSLENGTH(x)	((x) & 0x7ff)
+#define	PTD_PING_STATE(x)		(((x) >> 26) & (0x1))
+
+/* urb state*/
+#define	DELETE_URB			0x0008
+#define	NO_TRANSFER_ACTIVE		0xFFFF
+#define	NO_TRANSFER_DONE		0x0000
+#define	MAX_PTD_BUFFER_SIZE		4096	/*max ptd size */
+
+/*information of the td	in headers of host memory*/
+typedef	struct td_ptd_map {
+	u32 state;		/* ACTIVE, NEW,	TO_BE_REMOVED */
+	u8 datatoggle;		/*to preserve the data toggle for ATL/ISTL transfers */
+	u32 ptd_bitmap;		/* Bitmap of this ptd in HC headers */
+	u32 ptd_header_addr;	/* headers address of  this td */
+	u32 ptd_data_addr;	/*data address of this td to write in and read from */
+	/*this is address is actual RAM	address	not the	CPU address
+	 * RAM address = (CPU ADDRESS-0x400) >>	3
+	 * */
+	u32 ptd_ram_data_addr;
+	u8 lasttd;		/*last td , complete the transfer */
+	struct ehci_qh *qh;	/* endpoint */
+	struct ehci_qtd	*qtd;	/* qtds	for this endpoint */
+	struct ehci_itd	*itd;	/*itd pointer */
+	struct ehci_sitd *sitd;	/*itd pointer */
+	/*iso specific only */
+	u32 grouptdmap;		/*if td	need to	complete with error, then process all the tds
+				   in the groupmap    */
+} td_ptd_map_t;
+
+/*buffer(ATL/ISTL/INTL)	managemnet*/
+typedef	struct td_ptd_map_buff {
+	u8 buffer_type;		/* Buffer type:	BUFF_TYPE_ATL/INTL/ISTL0/ISTL1 */
+	u8 active_ptds;		/* number of active td's in the	buffer */
+	u8 total_ptds;		/* Total number	of td's	present	in the buffer (active +	tobe removed + skip) */
+	u8 max_ptds;		/* Maximum number of ptd's(32) this buffer can withstand */
+	u16 active_ptd_bitmap;	/* Active PTD's	bitmap */
+	u16 pending_ptd_bitmap;	/* skip	PTD's bitmap */
+	td_ptd_map_t map_list[TD_PTD_MAX_BUFF_TDS];	/* td_ptd_map list */
+} td_ptd_map_buff_t;
+
+
+#define     USB_HCD_MAJOR           0
+#define     USB_HCD_MODULE_NAME     "isp1763hcd"
+/* static char devpath[] = "/dev/isp1763hcd"; */
+
+#define HCD_IOC_MAGIC	'h'
+
+#define     HCD_IOC_POWERDOWN							_IO(HCD_IOC_MAGIC, 1)
+#define     HCD_IOC_POWERUP								_IO(HCD_IOC_MAGIC, 2)
+#define     HCD_IOC_TESTSE0_NACK						_IO(HCD_IOC_MAGIC, 3)
+#define     HCD_IOC_TEST_J								_IO(HCD_IOC_MAGIC,4)
+#define     HCD_IOC_TEST_K								_IO(HCD_IOC_MAGIC,5)
+#define     HCD_IOC_TEST_TESTPACKET						_IO(HCD_IOC_MAGIC,6)
+#define     HCD_IOC_TEST_FORCE_ENABLE					_IO(HCD_IOC_MAGIC,7)
+#define	  HCD_IOC_TEST_SUSPEND_RESUME				_IO(HCD_IOC_MAGIC,8)
+#define     HCD_IOC_TEST_SINGLE_STEP_GET_DEV_DESC		_IO(HCD_IOC_MAGIC,9)
+#define     HCD_IOC_TEST_SINGLE_STEP_SET_FEATURE		_IO(HCD_IOC_MAGIC,10)
+#define     HCD_IOC_TEST_STOP							_IO(HCD_IOC_MAGIC,11)
+#define     HCD_IOC_SUSPEND_BUS							_IO(HCD_IOC_MAGIC,12)
+#define     HCD_IOC_RESUME_BUS							_IO(HCD_IOC_MAGIC,13)
+#define     HCD_IOC_REMOTEWAKEUP_BUS					_IO(HCD_IOC_MAGIC,14)
+
+#define HOST_COMPILANCE_TEST_ENABLE	1
+#define HOST_COMP_TEST_SE0_NAK	1
+#define HOST_COMP_TEST_J	2
+#define HOST_COMP_TEST_K	3
+#define HOST_COMP_TEST_PACKET		4
+#define HOST_COMP_TEST_FORCE_ENABLE	5
+#define HOST_COMP_HS_HOST_PORT_SUSPEND_RESUME	6
+#define HOST_COMP_SINGLE_STEP_GET_DEV_DESC	7
+#define HOST_COMP_SINGLE_STEP_SET_FEATURE	8
+
+#endif
diff --git a/drivers/usb/host/pehci/host/qtdptd.c b/drivers/usb/host/pehci/host/qtdptd.c
new file mode 100644
index 0000000..093800e
--- /dev/null
+++ b/drivers/usb/host/pehci/host/qtdptd.c
@@ -0,0 +1,1315 @@
+/* 
+* Copyright (C) ST-Ericsson AP Pte Ltd 2010 
+*
+* ISP1763 Linux OTG Controller driver : host
+* 
+* 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 Street, Fifth Floor, Boston, MA  02110-1301, USA. 
+* 
+* This is a host controller driver file.  QTD processing is handled here.
+* 
+* Author : wired support <wired.support@stericsson.com>
+*
+*/
+
+
+/*   Td	managenment routines  */
+
+#define	QUEUE_HEAD_NOT_EMPTY	0x001
+
+
+/*free the location used by removed urb/endpoint*/
+static void
+phci_hcd_release_td_ptd_index(struct ehci_qh *qh)
+{
+	td_ptd_map_buff_t *td_ptd_buff = &td_ptd_map_buff[qh->type];
+	td_ptd_map_t *td_ptd_map = &td_ptd_buff->map_list[qh->qtd_ptd_index];
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+	/*hold the global lock here */
+	td_ptd_map->state = TD_PTD_NEW;
+	qh->qh_state = QH_STATE_IDLE;
+	/*
+	   set these values to NULL as schedule
+	   is based on these values,
+	   rather td_ptd_map state
+	 */
+	td_ptd_map->qh = NULL;
+	td_ptd_map->qtd	= NULL;
+
+	td_ptd_buff->active_ptd_bitmap &= ~td_ptd_map->ptd_bitmap;
+
+	/* Only	pending	transfers on current QH	must be	cleared	*/
+	td_ptd_buff->pending_ptd_bitmap	&= ~td_ptd_map->ptd_bitmap;
+
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+
+}
+
+/*print	ehciqtd*/
+static void
+print_ehci_qtd(struct ehci_qtd *qtd)
+{
+	pehci_print("hwnext 0x%08x, altnext 0x%08x,token 0x%08x, length	%d\n",
+		    qtd->hw_next, qtd->hw_alt_next,
+		    le32_to_cpu(qtd->hw_token),	qtd->length);
+
+	pehci_print("buf[0] 0x%08x\n", qtd->hw_buf[0]);
+
+}
+
+/*delete all qtds linked with this urb*/
+static void
+phci_hcd_qtd_list_free(phci_hcd	* ehci,
+		       struct urb *urb,	struct list_head *qtd_list)
+{
+	struct list_head *entry, *temp;
+
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+
+	list_for_each_safe(entry, temp,	qtd_list) {
+		struct ehci_qtd	*qtd;
+		qtd = list_entry(entry,	struct ehci_qtd, qtd_list);
+	if(!list_empty(&qtd->qtd_list))
+		list_del_init(&qtd->qtd_list);
+		qha_free(qha_cache, qtd);
+	}
+
+	pehci_entry("--	%s: Exit \n", __FUNCTION__);
+}
+
+
+/*
+ * free	all the	qtds for this transfer,	also
+ * free	the Host memory	to be reused
+ */
+static void
+phci_hcd_urb_free_priv(phci_hcd	* hcd,
+		       urb_priv_t * urb_priv_to_remove,	struct ehci_qh *qh)
+{
+	int i =	0;
+	struct ehci_qtd	*qtd;
+	for (i = 0; i <	urb_priv_to_remove->length; i++) {
+		if (urb_priv_to_remove->qtd[i])	{
+			qtd = urb_priv_to_remove->qtd[i];
+
+			if(!list_empty(&qtd->qtd_list))
+				list_del_init(&qtd->qtd_list);
+
+			/* This	is required when the device is abruptly	disconnected and the
+			 * PTDs	are not	completely processed
+			 */
+			if (qtd->length)
+				phci_hcd_mem_free(&qtd->mem_addr);
+
+			qha_free(qha_cache, qtd);
+			urb_priv_to_remove->qtd[i] = 0;
+			qtd = 0;
+		}
+
+	}
+	
+	return;
+}
+
+
+/*allocate the qtd*/
+struct ehci_qtd	*
+phci_hcd_qtd_allocate(int mem_flags)
+{
+
+	struct ehci_qtd	*qtd = 0;
+	qtd = kmalloc(sizeof *qtd, mem_flags);
+	if (!qtd)
+	{
+		return 0;
+	}
+	
+	memset(qtd, 0, sizeof *qtd);
+	qtd->qtd_dma = cpu_to_le32(qtd);
+	qtd->hw_next = EHCI_LIST_END;
+	qtd->hw_alt_next = EHCI_LIST_END;
+	qtd->state = QTD_STATE_NEW;
+	INIT_LIST_HEAD(&qtd->qtd_list);
+	return qtd;
+}
+
+/*
+ * calculates host memory for current length transfer td,
+ * maximum td length is	4K(custom made)
+ * */
+static int
+phci_hcd_qtd_fill(struct urb *urb,
+		  struct ehci_qtd *qtd,
+		  dma_addr_t buf, size_t len, int token, int *status)
+{
+	int count = 0;
+
+	qtd->hw_buf[0] = (u32) buf;
+	/*max lenggth is HC_ATL_PL_SIZE	*/
+	if (len	> HC_ATL_PL_SIZE) {
+		count =	HC_ATL_PL_SIZE;
+	} else {
+		count =	len;
+	}
+	qtd->hw_token =	cpu_to_le32((count << 16) | token);
+	qtd->length = count;
+
+	pehci_print("%s:qtd %p,	token %8x bytes	%d dma %x\n",
+		__FUNCTION__, qtd, le32_to_cpu(qtd->hw_token), count,
+		qtd->hw_buf[0]);
+
+	return count;
+}
+
+
+/*
+ * makes number	of qtds	required for
+ * interrupt/bulk/control transfer length
+ * and initilize qtds
+ * */
+struct list_head *
+phci_hcd_make_qtd(phci_hcd * hcd,
+		  struct list_head *head, struct urb *urb, int *status)
+{
+
+	struct ehci_qtd	*qtd, *qtd_prev;
+	dma_addr_t buf,	map_buf;
+	int len, maxpacket;
+	int is_input;
+	u32 token;
+	int cnt	= 0;
+	urb_priv_t *urb_priv = (urb_priv_t *) urb->hcpriv;
+
+	pehci_entry("++	%s, Entered\n",	__FUNCTION__);
+
+	/*take the qtd from already allocated
+	   structure from hcd_submit_urb
+	 */
+	qtd = urb_priv->qtd[cnt];
+	if (unlikely(!qtd)) {
+		*status	= -ENOMEM;
+		return 0;
+	}
+
+	qtd_prev = 0;
+	list_add_tail(&qtd->qtd_list, head);
+
+	qtd->urb = urb;
+
+	token =	QTD_STS_ACTIVE;
+	token |= (EHCI_TUNE_CERR << 10);
+
+	len = urb->transfer_buffer_length;
+
+	is_input = usb_pipein(urb->pipe);
+
+	if (usb_pipecontrol(urb->pipe))	{
+		/* SETUP pid */
+		if (phci_hcd_qtd_fill(urb, qtd,	cpu_to_le32(urb->setup_packet),
+			sizeof(struct usb_ctrlrequest),
+			token |	(2 /* "setup" */	<< 8),
+			status)	<	0) {
+			goto cleanup;
+		}
+
+		cnt++;		/* increment the index */
+		print_ehci_qtd(qtd);
+		/* ... and always at least one more pid	*/
+		token ^= QTD_TOGGLE;
+		qtd_prev = qtd;
+		qtd = urb_priv->qtd[cnt];
+		if (unlikely(!qtd)) {
+			*status	= -ENOMEM;
+			goto cleanup;
+		}
+		qtd->urb = urb;
+		qtd_prev->hw_next = QTD_NEXT(qtd->qtd_dma);
+		list_add_tail(&qtd->qtd_list, head);
+	}
+
+	/*
+	 * data	transfer stage:	 buffer	setup
+	 */
+	len = urb->transfer_buffer_length;
+	if (likely(len > 0)) {
+		/*update the buffer address */
+		buf = cpu_to_le32(urb->transfer_buffer);
+	} else {
+		buf = map_buf =	cpu_to_le32(0);	/*set-up stage has no data. */
+	}
+
+	/* So are we waiting for the ack only or there is a data stage with out. */
+	if (!buf || usb_pipein(urb->pipe)) {
+		token |= (1 /* "in" */	<< 8);
+	}
+	/* else	it's already initted to	"out" pid (0 <<	8) */
+	maxpacket = usb_maxpacket(urb->dev, urb->pipe,
+				  usb_pipeout(urb->pipe)) & 0x07ff;
+
+
+	/*
+	 * buffer gets wrapped in one or more qtds;
+	 * last	one may	be "short" (including zero len)
+	 * and may serve as a control status ack
+	 */
+
+	for (;;) {
+		int this_qtd_len;
+		this_qtd_len =
+			phci_hcd_qtd_fill(urb, qtd, buf, len, token, status);
+		if (this_qtd_len < 0)
+			goto cleanup;
+		print_ehci_qtd(qtd);
+		len -= this_qtd_len;
+		buf += this_qtd_len;
+		cnt++;
+		/* qh makes control packets use	qtd toggle; maybe switch it */
+		if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0) {
+			token ^= QTD_TOGGLE;
+		}
+
+		if (likely(len <= 0)) {
+			break;
+		}
+		qtd_prev = qtd;
+		qtd = urb_priv->qtd[cnt];
+		if (unlikely(!qtd)) {
+			goto cleanup;
+		}
+		qtd->urb = urb;
+		qtd_prev->hw_next = QTD_NEXT(qtd->qtd_dma);
+		list_add_tail(&qtd->qtd_list, head);
+	}
+
+	/*
+	 * control requests may	need a terminating data	"status" ack;
+	 * bulk	ones may need a	terminating short packet (zero length).
+	 */
+	if (likely(buf != 0)) {
+		int one_more = 0;
+		if (usb_pipecontrol(urb->pipe))	{
+			one_more = 1;
+			token ^= 0x0100;	/* "in"	<--> "out"  */
+			token |= QTD_TOGGLE;	/* force DATA1 */
+
+		} else if (usb_pipebulk(urb->pipe)	/* bulk	data exactly terminated	on zero	lenth */
+			&&(urb->transfer_flags & URB_ZERO_PACKET)
+			&& !(urb->transfer_buffer_length % maxpacket)) {
+			one_more = 1;
+		}
+		if (one_more) {
+			qtd_prev = qtd;
+			qtd = urb_priv->qtd[cnt];
+			if (unlikely(!qtd)) {
+				goto cleanup;
+			}
+
+			qtd->urb = urb;
+			qtd_prev->hw_next = QTD_NEXT(qtd->qtd_dma);
+			list_add_tail(&qtd->qtd_list, head);
+			phci_hcd_qtd_fill(urb, qtd, 0, 0, token, status);
+			print_ehci_qtd(qtd);
+			cnt++;
+		}
+	}
+
+	/*this is our last td for current transfer */
+	qtd->state |= QTD_STATE_LAST;
+
+	/*number of tds	*/
+	if (urb_priv->length !=	cnt) {
+		err("Never Error: number of tds	allocated %d exceeding %d\n",
+		    urb_priv->length, cnt);
+	}
+	/* by default, enable interrupt	on urb completion */
+	if (likely(!(urb->transfer_flags & URB_NO_INTERRUPT))) {
+		qtd->hw_token |= __constant_cpu_to_le32(QTD_IOC);
+	}
+
+	pehci_entry("--	%s, Exit\n", __FUNCTION__);
+	return head;
+
+	cleanup:
+	phci_hcd_qtd_list_free(hcd, urb, head);
+	return 0;
+}
+
+/*allocates a queue head(endpoint*/
+struct ehci_qh *
+phci_hcd_qh_alloc(phci_hcd * hcd)
+{
+
+	struct ehci_qh *qh = kmalloc(sizeof(struct ehci_qh), GFP_ATOMIC);
+	if (!qh)
+	{
+		return qh;
+	}
+	
+	memset(qh, 0, sizeof *qh);
+	atomic_set(&qh->refcount, 1);
+	init_waitqueue_head(&qh->waitforcomplete);
+	qh->qh_dma = (u32) qh;
+	INIT_LIST_HEAD(&qh->qtd_list);
+	INIT_LIST_HEAD(&qh->itd_list);
+	qh->next_uframe	= -1;
+	return qh;
+}
+
+/* calculates header address for the tds*/
+static int
+phci_hcd_fill_ptd_addresses(td_ptd_map_t * td_ptd_map, int index, int bufftype)
+{
+	int i =	0;
+	unsigned long tdlocation = 0;
+	/*
+	 * the below payloadlocation and
+	 * payloadsize are redundant
+	 * */
+	unsigned long payloadlocation =	0;
+	unsigned long payloadsize = 0;
+	pehci_entry("++	%s: enter\n", __FUNCTION__);
+	switch (bufftype) {
+		/*atl header starts at 0xc00 */
+	case TD_PTD_BUFF_TYPE_ATL:
+		tdlocation = 0x0c00;
+		/*redundant */
+		payloadsize = 0x1000;
+		payloadlocation	= 0x1000;
+		break;
+	case TD_PTD_BUFF_TYPE_INTL:
+		/*interrupt header
+		 * starts at 0x800
+		 * */
+		tdlocation = 0x0800;
+		/*redundant */
+		payloadlocation	= 0x1000;
+		payloadsize = 0x1000;
+		break;
+
+	case TD_PTD_BUFF_TYPE_ISTL:
+		/*iso header starts
+		 * at 0x400*/
+
+		tdlocation = 0x0400;
+		/*redunndant */
+		payloadlocation	= 0x1000;
+		payloadsize = 0x1000;
+
+		break;
+	}
+
+
+	i = index;
+	payloadlocation	+= (i) * payloadsize;	/*each payload is of 4096 bytes	*/
+	tdlocation += (i) * PHCI_QHA_LENGTH;	/*each td is of	32 bytes */
+	td_ptd_map->ptd_header_addr = tdlocation;
+	td_ptd_map->ptd_data_addr = payloadlocation;
+	td_ptd_map->ptd_ram_data_addr =	((payloadlocation - 0x0400) >> 3);
+	pehci_print
+		("Index: %d, Header: 0x%08x, Payload: 0x%08x,Data start	address: 0x%08x\n",
+		 index,	td_ptd_map->ptd_header_addr, td_ptd_map->ptd_data_addr,
+		 td_ptd_map->ptd_ram_data_addr);
+	pehci_entry("--	%s: Exit", __FUNCTION__);
+	return payloadlocation;
+}
+
+
+/*--------------------------------------------------------------*
+ * calculate the header	location for the current
+ * endpoint, if	found returns a	valid index
+ * else	invalid
+ -----------------------------------------------------------*/
+static void
+phci_hcd_get_qtd_ptd_index(struct ehci_qh *qh,
+			   struct ehci_qtd *qtd, struct	ehci_itd *itd)
+{
+	u8 buff_type = td_ptd_pipe_x_buff_type[qh->type];
+	u8 qtd_ptd_index;	/*, index; */
+	/*this is the location of the ptd's skip map/done map, also
+	   calculating the td header, payload, data start address
+	   location */
+	u8 bitmap = 0x1;
+	u8 max_ptds;
+
+	td_ptd_map_buff_t *ptd_map_buff	= &(td_ptd_map_buff[buff_type]);
+	pehci_entry("++	%s, Entered, buffer type %d\n",	__FUNCTION__,
+		    buff_type);
+
+	/* ATL PTDs can	wait */
+	max_ptds = (buff_type == TD_PTD_BUFF_TYPE_ATL)
+		? TD_PTD_MAX_BUFF_TDS :	ptd_map_buff->max_ptds;
+
+	for (qtd_ptd_index = 0;	qtd_ptd_index <	max_ptds; qtd_ptd_index++) {	/* Find	the first free slot */
+		if (ptd_map_buff->map_list[qtd_ptd_index].state	== TD_PTD_NEW) {
+			/* Found a free	slot */
+			if (qh->qtd_ptd_index == TD_PTD_INV_PTD_INDEX) {
+				qh->qtd_ptd_index = qtd_ptd_index;
+			}
+			ptd_map_buff->map_list[qtd_ptd_index].datatoggle = 0;
+			/*put the ptd_index into operational state */
+			ptd_map_buff->map_list[qtd_ptd_index].state =
+				TD_PTD_ACTIVE;
+			ptd_map_buff->map_list[qtd_ptd_index].qtd = qtd;
+			/* No td transfer is in	progress */
+			ptd_map_buff->map_list[qtd_ptd_index].itd = itd;
+			/*initialize endpoint(queuehead) */
+			ptd_map_buff->map_list[qtd_ptd_index].qh = qh;
+			ptd_map_buff->map_list[qtd_ptd_index].ptd_bitmap =
+				bitmap << qtd_ptd_index;
+			phci_hcd_fill_ptd_addresses(&ptd_map_buff->
+				map_list[qtd_ptd_index],
+				qh->qtd_ptd_index,
+				buff_type);
+			ptd_map_buff->map_list[qtd_ptd_index].lasttd = 0;
+			ptd_map_buff->total_ptds++;	/* update # of total td's */
+			/*make the queuehead map, to process in	the phci_schedule_ptds */
+			ptd_map_buff->active_ptd_bitmap	|=
+				(bitmap	<< qtd_ptd_index);
+			break;
+		}
+	}
+	pehci_entry("--	%s, Exit\n", __FUNCTION__);
+	return;
+
+}				/* phci_get_td_ptd_index */
+
+
+
+/*
+ * calculate the header	location for the endpoint and
+ * all tds on this endpoint will use the same
+ * header location for all transfers on	this endpoint.
+ * also	puts the endpoint into the linked state
+ * */
+static void
+phci_hcd_qh_link_async(phci_hcd	* hcd, struct ehci_qh *qh, int *status)
+{
+	struct ehci_qtd	*qtd = 0;
+	struct list_head *qtd_list = &qh->qtd_list;
+
+#ifdef MSEC_INT_BASED
+	td_ptd_map_buff_t *ptd_map_buff;
+	td_ptd_map_t *td_ptd_map;
+#endif
+
+	/*  take the first td, in case we are not able to schedule the new td
+	   and this is going for remove
+	 */
+	qtd = list_entry(qtd_list->next, struct	ehci_qtd, qtd_list);
+
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+
+	/* Assign a td-ptd index for this ed so	that we	can put	ptd's in the HC	buffers	*/
+
+	qh->qtd_ptd_index = TD_PTD_INV_PTD_INDEX;
+	phci_hcd_get_qtd_ptd_index(qh, qtd, NULL);	/* Get a td-ptd	index */
+	if (qh->qtd_ptd_index == TD_PTD_INV_PTD_INDEX) {
+		err("can not find the location in our buffer\n");
+		*status	= -ENOSPC;
+		return;
+	}
+#ifdef MSEC_INT_BASED
+	/*first	transfers in sof interrupt goes	into pending */
+	ptd_map_buff = &(td_ptd_map_buff[qh->type]);
+	td_ptd_map = &ptd_map_buff->map_list[qh->qtd_ptd_index];
+	ptd_map_buff->pending_ptd_bitmap |= td_ptd_map->ptd_bitmap;
+
+#endif
+	/* open	the halt so that it acessed */
+	qh->hw_token &=	~__constant_cpu_to_le32(QTD_STS_HALT);
+	qh->qh_state = QH_STATE_LINKED;
+	qh->qh_state |=	QH_STATE_TAKE_NEXT;
+	pehci_entry("--	%s: Exit , qh %p\n", __FUNCTION__, qh);
+
+
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * mainly used for setting up current td on current
+ * endpoint(queuehead),	endpoint may be	new or
+ * halted one
+ * */
+
+static inline void
+phci_hcd_qh_update(phci_hcd * ehci, struct ehci_qh *qh,	struct ehci_qtd	*qtd)
+{
+	/*make this current td */
+	qh->hw_current = QTD_NEXT(qtd->qtd_dma);
+	qh->hw_qtd_next	= QTD_NEXT(qtd->qtd_dma);
+	qh->hw_alt_next	= EHCI_LIST_END;
+	/* HC must see latest qtd and qh data before we	clear ACTIVE+HALT */
+	wmb();
+	qh->hw_token &=	__constant_cpu_to_le32(QTD_TOGGLE | QTD_STS_PING);
+}
+
+/*
+ * used	for ATL, INT transfers
+ * function creates new	endpoint,
+ * calculates bandwidth	for interrupt transfers,
+ * and initialize the qh based on endpoint type/speed
+ * */
+struct ehci_qh *
+phci_hcd_make_qh(phci_hcd * hcd,
+		 struct	urb *urb, struct list_head *qtd_list, int *status)
+{
+	struct ehci_qh *qh = 0;
+	u32 info1 = 0, info2 = 0;
+	int is_input, type;
+	int maxp = 0;
+	int mult = 0;
+	int bustime = 0;
+	struct ehci_qtd	*qtd =
+		list_entry(qtd_list->next, struct ehci_qtd, qtd_list);
+
+
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+
+	qh = phci_hcd_qh_alloc(hcd);
+	if (!qh) {
+		*status	= -ENOMEM;
+		return 0;
+	}
+
+	/*
+	 * init	endpoint/device	data for this QH
+	 */
+	info1 |= usb_pipeendpoint(urb->pipe) <<	8;
+	info1 |= usb_pipedevice(urb->pipe) << 0;
+
+	is_input = usb_pipein(urb->pipe);
+	type = usb_pipetype(urb->pipe);
+	maxp = usb_maxpacket(urb->dev, urb->pipe, !is_input);
+	mult = 1 + ((maxp >> 11) & 0x3);
+
+	/*set this queueheads index to invalid */
+	qh->qtd_ptd_index = TD_PTD_INV_PTD_INDEX;
+
+	switch (type) {
+	case PIPE_CONTROL:
+	case PIPE_BULK:
+		qh->type = TD_PTD_BUFF_TYPE_ATL;
+		break;
+
+	case PIPE_INTERRUPT:
+		qh->type = TD_PTD_BUFF_TYPE_INTL;
+		break;
+	case PIPE_ISOCHRONOUS:
+		qh->type = TD_PTD_BUFF_TYPE_ISTL;
+		break;
+
+	}
+
+
+
+	if (type == PIPE_INTERRUPT) {
+		/*for this interrupt transfer check how	much bustime in	usecs required */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+		bustime = usb_check_bandwidth(urb->dev, urb);
+
+		if (bustime < 0) {
+			*status = -ENOSPC;
+			goto done;
+		}
+
+		usb_claim_bandwidth(urb->dev, urb, bustime,
+			usb_pipeisoc(urb->pipe));
+#else
+#endif
+		qh->usecs = bustime;
+
+		qh->start = NO_FRAME;
+
+		if (urb->dev->speed == USB_SPEED_HIGH) {
+			qh->c_usecs = 0;
+			qh->gap_uf = 0;
+			/*after	how many uframes this interrupt	is to be executed */
+			qh->period = urb->interval >> 3;
+			if (qh->period < 1) {
+				printk("intr period %d uframes,\n",
+				urb->interval);
+			}
+			/*restore the original urb->interval in	qh->period */
+			qh->period = urb->interval;
+
+		} else {
+			/* gap is f(FS/LS transfer times) */
+			qh->gap_uf = 1 + 7;	/*usb_calc_bus_time (urb->dev->speed,
+						   is_input, 0,	maxp) /	(125 * 1000); */
+
+			if (is_input) {	/* SPLIT, gap, CSPLIT+DATA */
+
+				qh->c_usecs = qh->usecs	+ 1;	/*HS_USECS (0);	*/
+				qh->usecs = 10;	/*HS_USECS (1);	*/
+			} else {	/* SPLIT+DATA, gap, CSPLIT */
+				qh->usecs += 10;	/*HS_USECS (1);	*/
+				qh->c_usecs = 1;	/*HS_USECS (0);	*/
+			}
+
+
+			/*take the period ss/cs	scheduling will	be
+			   handled by submit urb
+			 */
+			qh->period = urb->interval;
+		}
+	}
+
+	/* using TT? */
+	switch (urb->dev->speed) {
+	case USB_SPEED_LOW:
+		info1 |= (1 << 12);	/* EPS "low" */
+		/* FALL	THROUGH	*/
+
+	case USB_SPEED_FULL:
+		/* EPS 0 means "full" */
+		if (type != PIPE_INTERRUPT) {
+			info1 |= (EHCI_TUNE_RL_TT << 28);
+		}
+		if (type == PIPE_CONTROL) {
+			info1 |= (1 << 27);	/* for TT */
+			info1 |= 1 << 14;	/* toggle from qtd */
+		}
+		info1 |= maxp << 16;
+
+		info2 |= (EHCI_TUNE_MULT_TT << 30);
+		info2 |= urb->dev->ttport << 23;
+		info2 |= urb->dev->tt->hub->devnum << 16;
+		break;
+
+
+	case USB_SPEED_HIGH:	/* no TT involved */
+		info1 |= (2 << 12);	/* EPS "high" */
+		if (type == PIPE_CONTROL) {
+			info1 |= (EHCI_TUNE_RL_HS << 28);
+			info1 |= 64 << 16;	/* usb2	fixed maxpacket	*/
+
+			info1 |= 1 << 14;	/* toggle from qtd */
+			info2 |= (EHCI_TUNE_MULT_HS << 30);
+		} else if (type	== PIPE_BULK) {
+			info1 |= (EHCI_TUNE_RL_HS << 28);
+			info1 |= 512 <<	16;	/* usb2	fixed maxpacket	*/
+			info2 |= (EHCI_TUNE_MULT_HS << 30);
+		} else {	/* PIPE_INTERRUPT */
+			info1 |= (maxp & 0x7ff)	/*max_packet (maxp) */ <<16;
+			info2 |= mult /*hb_mult	(maxp) */  << 30;
+		}
+		break;
+
+	default:
+		pehci_print("bogus dev %p speed	%d", urb->dev, urb->dev->speed);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	done:
+#else
+#endif
+		qha_free(qha_cache, qh);
+		return 0;
+	}			/*end of switch	*/
+
+	/* NOTE:  if (PIPE_INTERRUPT) {	scheduler sets s-mask }	*/
+
+	/* init	as halted, toggle clear, advance to dummy */
+	qh->qh_state = QH_STATE_IDLE;
+	qh->hw_info1 = cpu_to_le32(info1);
+	qh->hw_info2 = cpu_to_le32(info2);
+	/*link the tds here */
+	list_splice(qtd_list, &qh->qtd_list);
+	phci_hcd_qh_update(hcd,	qh, qtd);
+	qh->hw_token = cpu_to_le32(QTD_STS_HALT);
+	if (!usb_pipecontrol(urb->pipe)) {
+		usb_settoggle(urb->dev,	usb_pipeendpoint(urb->pipe), !is_input,
+			1);
+	}
+	pehci_entry("--	%s: Exit, qh %p\n", __FUNCTION__, qh);
+	return qh;
+}
+
+
+/*-----------------------------------------------------------*/
+/*
+ * Hardware maintains data toggle (like	OHCI) ... here we (re)initialize
+ * the hardware	data toggle in the QH, and set the pseudo-toggle in udev
+ * so we can see if usb_clear_halt() was called.  NOP for control, since
+ * we set up qh->hw_info1 to always use	the QTD	toggle bits.
+ */
+static inline void
+phci_hcd_clear_toggle(struct usb_device	*udev, int ep, int is_out,
+		      struct ehci_qh *qh)
+{
+	pehci_print("clear toggle, dev %d ep 0x%x-%s\n",
+		    udev->devnum, ep, is_out ? "out" : "in");
+	qh->hw_token &=	~__constant_cpu_to_le32(QTD_TOGGLE);
+	usb_settoggle(udev, ep,	is_out,	1);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * For control/bulk/interrupt, return QH with these TDs	appended.
+ * Allocates and initializes the QH if necessary.
+ * Returns null	if it can't allocate a QH it needs to.
+ * If the QH has TDs (urbs) already, that's great.
+ */
+struct ehci_qh *
+phci_hcd_qh_append_tds(phci_hcd	* hcd,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	struct usb_host_endpoint *ep,
+#else
+#endif
+	struct urb *urb,	struct list_head *qtd_list,
+	void **ptr, int *status)
+{
+
+	int epnum;
+
+	struct ehci_qh *qh = 0;
+	struct ehci_qtd	*qtd =
+		list_entry(qtd_list->next, struct ehci_qtd, qtd_list);
+	td_ptd_map_buff_t *ptd_map_buff;
+	td_ptd_map_t *td_ptd_map;
+
+
+
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	epnum = ep->desc.bEndpointAddress;
+#else
+	epnum = urb->ep->desc.bEndpointAddress;
+#endif
+
+	qh = (struct ehci_qh *)	*ptr;
+	if (likely(qh != 0)) {
+		u32 hw_next = QTD_NEXT(qtd->qtd_dma);
+		pehci_print("%Queue head already %p\n",	qh);
+
+		ptd_map_buff = &(td_ptd_map_buff[qh->type]);
+		td_ptd_map = &ptd_map_buff->map_list[qh->qtd_ptd_index];
+
+		/* maybe patch the qh used for set_address */
+		if (unlikely
+			(epnum == 0	&& le32_to_cpu(qh->hw_info1 & 0x7f) == 0)) {
+			qh->hw_info1 |=	cpu_to_le32(usb_pipedevice(urb->pipe));
+		}
+
+		/* is an URB is	queued to this qh already? */
+		if (unlikely(!list_empty(&qh->qtd_list))) {
+			struct ehci_qtd	*last_qtd;
+			/* update the last qtd's "next"	pointer	*/
+			last_qtd = list_entry(qh->qtd_list.prev,
+				struct ehci_qtd, qtd_list);
+
+			/*queue	head is	not empty just add the
+			   td at the end of it , and return from here
+			 */
+			last_qtd->hw_next = hw_next;
+
+			/*set the status as positive */
+			*status	= (u32)	QUEUE_HEAD_NOT_EMPTY;
+
+			/* no URB queued */
+		} else {
+
+	//		qh->qh_state = QH_STATE_IDLE;
+
+
+			/* usb_clear_halt() means qh data toggle gets reset */
+			if (usb_pipebulk(urb->pipe)
+				&& unlikely(!usb_gettoggle(urb->dev, (epnum	& 0x0f),
+				!(epnum & 0x80)))) {
+
+				phci_hcd_clear_toggle(urb->dev,
+					epnum & 0x0f,
+					!(epnum &	0x80), qh);
+
+				/*reset	our data toggle	*/
+
+				qh->datatoggle = 0;
+				qh->ping = 0;
+
+			}
+			phci_hcd_qh_update(hcd,	qh, qtd);
+		}
+		/*put everything in pedning, will be cleared during scheduling */
+		ptd_map_buff->pending_ptd_bitmap |= td_ptd_map->ptd_bitmap;
+		list_splice(qtd_list, qh->qtd_list.prev);
+	} else {
+		qh = phci_hcd_make_qh(hcd, urb,	qtd_list, status);
+		*ptr = qh;
+	}
+	pehci_entry("--	%s: Exit qh %p\n", __FUNCTION__, qh);
+	return qh;
+}
+
+/*link qtds to endpoint(qh)*/
+struct ehci_qh *
+phci_hcd_submit_async(phci_hcd * hcd,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	struct usb_host_endpoint *ep,
+#else
+#endif
+		      struct list_head *qtd_list, struct urb *urb, int *status)
+{
+	struct ehci_qtd	*qtd;
+	struct hcd_dev *dev;
+	int epnum;
+
+#ifndef THREAD_BASED
+	unsigned long flags;
+#endif
+
+	
+	struct ehci_qh *qh = 0;
+
+	urb_priv_t *urb_priv = urb->hcpriv;
+
+	qtd = list_entry(qtd_list->next, struct	ehci_qtd, qtd_list);
+	dev = (struct hcd_dev *) urb->hcpriv;
+	epnum =	usb_pipeendpoint(urb->pipe);
+	if (usb_pipein(urb->pipe) && !usb_pipecontrol(urb->pipe)) {
+		epnum |= 0x10;
+	}
+
+	pehci_entry("++	%s, enter\n", __FUNCTION__);
+
+	/* ehci_hcd->lock guards shared	data against other CPUs:
+	 *   ehci_hcd:	    async, reclaim, periodic (and shadow), ...
+	 *   hcd_dev:	    ep[]
+
+	 *   ehci_qh:	    qh_next, qtd_list
+
+	 *   ehci_qtd:	    qtd_list
+	 *
+	 * Also, hold this lock	when talking to	HC registers or
+	 * when	updating hw_* fields in	shared qh/qtd/... structures.
+	 */
+#ifndef THREAD_BASED
+	spin_lock_irqsave(&hcd->lock, flags);
+#endif
+
+	spin_lock(&hcd_data_lock);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+	usb_hcd_link_urb_to_ep(&hcd->usb_hcd, urb);
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	qh = phci_hcd_qh_append_tds(hcd, ep, urb, qtd_list, &ep->hcpriv,
+		status);
+#else
+	qh = phci_hcd_qh_append_tds(hcd, urb, qtd_list, &urb->ep->hcpriv,
+		status);
+#endif
+	if (!qh	|| *status < 0) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+		usb_hcd_unlink_urb_from_ep(&hcd->usb_hcd, urb);
+#endif
+		goto cleanup;
+	}
+	/* Control/bulk	operations through TTs don't need scheduling,
+	 * the HC and TT handle	it when	the TT has a buffer ready.
+	 */
+
+	/* now the quehead can not be in the unlink state */
+
+//	printk("qh->qh_state:0x%x \n",qh->qh_state);
+	if (qh->qh_state == QH_STATE_UNLINK) {
+		pehci_info("%s:	free the urb,qh->state %x\n", __FUNCTION__,
+			   qh->qh_state);
+		phci_hcd_qtd_list_free(hcd, urb, &qh->qtd_list);
+		spin_unlock(&hcd_data_lock);
+		
+#ifndef THREAD_BASED			
+		spin_unlock_irqrestore(&hcd->lock, flags);
+#endif
+		*status	= -ENODEV;
+		return 0;
+	}
+
+	if (likely(qh != 0)) {
+		urb_priv->qh = qh;
+		if (likely(qh->qh_state	== QH_STATE_IDLE))
+			phci_hcd_qh_link_async(hcd, qh,	status);
+	}
+
+	cleanup:
+	spin_unlock(&hcd_data_lock);
+
+#ifndef THREAD_BASED			
+	/* free	it from	lock systme can	sleep now */
+	spin_unlock_irqrestore(&hcd->lock, flags);
+#endif
+	
+	/* could not get the QH	terminate and clean. */
+	if (unlikely(qh	== 0) || *status < 0) {
+		phci_hcd_qtd_list_free(hcd, urb, qtd_list);
+		return qh;
+	}
+	return qh;
+}
+
+/*
+ * initilaize the s-mask c-mask	for
+ * interrupt transfers.
+ */
+static int
+phci_hcd_qhint_schedule(phci_hcd * hcd,
+			struct ehci_qh *qh,
+			struct ehci_qtd	*qtd,
+			struct _isp1763_qhint *qha, struct urb *urb)
+{
+	int i =	0;
+	u32 td_info3 = 0;
+	u32 td_info5 = 0;
+	u32 period = 0;
+	u32 usofmask = 1;
+	u32 usof = 0;
+	u32 ssplit = 0,	csplit = 0xFF;
+	int maxpacket;
+	u32 numberofusofs = 0;
+
+	/*and since whol msec frame is empty, i	can schedule in	any uframe */
+	maxpacket = usb_maxpacket(urb->dev, urb->pipe, !usb_pipein(urb->pipe));
+	maxpacket &= 0x7ff;
+	/*length of the	data per uframe	*/
+	maxpacket = XFER_PER_UFRAME(qha->td_info1) * maxpacket;
+
+	/*caculate the number of uframes are required */
+	numberofusofs =	urb->transfer_buffer_length / maxpacket;
+	/*if something left */
+	if (urb->transfer_buffer_length	% maxpacket) {
+		numberofusofs += 1;
+	}
+
+	for (i = 0; i <	numberofusofs; i++) {
+		usofmask <<= i;
+		usof |=	usofmask;
+
+	}
+
+	/*
+	   for full/low	speed devices, as we
+	   have	seperate location for all the endpoints
+	   let the start split goto the	first uframe, means 0 uframe
+	 */
+	if (urb->dev->speed != USB_SPEED_HIGH && usb_pipeint(urb->pipe)) {
+		/*set the complete splits */
+		/*set all the bits and lets see	whats happening	*/
+		/*but this will	be set based on	the maximum packet size	*/
+		ssplit = usof;
+		/*  need to fix	it */
+		csplit = 0x1C;
+		qha->td_info6 =	csplit;
+		period = qh->period;
+		if (period >= 32) {
+			period = qh->period / 2;
+		}
+		td_info3 = period;
+		goto done;
+
+	} else {
+		if (qh->period >= 8) {
+			period = qh->period / 8;
+		} else {
+			period = qh->period;
+		}
+	}
+	/*our limitaion	is maximum of 32 ie 31,	5 bits */
+	if (period >= 32) {
+		period = 32;
+		/*devide by 2 */
+		period >>= 1;
+	}
+	if (qh->period >= 8) {
+		/*millisecond period */
+		td_info3 = (period << 3);
+	} else {
+		/*usof based tranmsfers	*/
+		/*minimum 4 usofs */
+		td_info3 = period;
+		usof = 0x11;
+	}
+
+	done:
+	td_info5 = usof;
+	qha->td_info3 |= td_info3;
+	qha->td_info5 |= usof;
+	return numberofusofs;
+}
+
+/*link interrupts qtds to endpoint*/
+struct ehci_qh *
+phci_hcd_submit_interrupt(phci_hcd * hcd,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	struct usb_host_endpoint *ep,
+#else
+#endif
+			  struct list_head *qtd_list,
+			  struct urb *urb, int *status)
+{
+	struct ehci_qtd	*qtd;
+	struct _hcd_dev	*dev;
+	int epnum;
+	unsigned long flags;
+	struct ehci_qh *qh = 0;
+	urb_priv_t *urb_priv = (urb_priv_t *) urb->hcpriv;
+
+	qtd = list_entry(qtd_list->next, struct	ehci_qtd, qtd_list);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	dev = (struct hcd_dev *) urb->hcpriv;
+	epnum = ep->desc.bEndpointAddress;
+
+	pehci_entry("++ %s, enter\n", __FUNCTION__);
+
+
+	/*check for more than one urb queued for this endpoint */
+	qh = ep->hcpriv;
+#else
+	dev = (struct _hcd_dev *) (urb->hcpriv);
+	epnum = urb->ep->desc.bEndpointAddress;
+
+	pehci_entry("++ %s, enter\n", __FUNCTION__);
+
+
+	/*check for more than one urb queued for this endpoint */
+	qh = (struct ehci_qh *) urb->ep->hcpriv;
+#endif
+
+	spin_lock_irqsave(&hcd->lock, flags);
+	if (unlikely(qh	!= 0)) {
+		if (!list_empty(&qh->qtd_list))	{
+			*status	= -EBUSY;
+			goto done;
+		} else {
+			td_ptd_map_buff_t *ptd_map_buff;
+			td_ptd_map_t *td_ptd_map;
+			ptd_map_buff = &(td_ptd_map_buff[qh->type]);
+			td_ptd_map = &ptd_map_buff->map_list[qh->qtd_ptd_index];
+			ptd_map_buff->pending_ptd_bitmap |=
+				td_ptd_map->ptd_bitmap;
+			 /*NEW*/ td_ptd_map->qtd = qtd;
+			/* maybe reset hardware's data toggle in the qh	*/
+			if (unlikely(!usb_gettoggle(urb->dev, epnum & 0x0f,
+				!(epnum & 0x80)))) {
+
+				/*reset	our data toggle	*/
+				td_ptd_map->datatoggle = 0;
+				usb_settoggle(urb->dev,	epnum &	0x0f,
+					!(epnum &	0x80), 1);
+				qh->datatoggle = 0;
+			}
+			/* trust the QH	was set	up as interrupt	... */
+			list_splice(qtd_list, &qh->qtd_list);
+		}
+	}
+
+
+	if (!qh) {
+		qh = phci_hcd_make_qh(hcd, urb,	qtd_list, status);
+		if (likely(qh == 0)) {
+			*status	= -ENOMEM;
+			goto done;
+		}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+		ep->hcpriv = qh;
+#else
+		urb->ep->hcpriv = qh;
+#endif
+	}
+
+	if (likely(qh != 0)) {
+		urb_priv->qh = qh;
+		if (likely(qh->qh_state	== QH_STATE_IDLE)) {
+			phci_hcd_qh_link_async(hcd, qh,	status);
+		}
+	}
+
+
+	done:
+	/* free	it from	lock systme can	sleep now */
+	spin_unlock_irqrestore(&hcd->lock, flags);
+	/* could not get the QH	terminate and clean. */
+	if (unlikely(qh	== 0) || *status < 0) {
+		phci_hcd_qtd_list_free(hcd, urb, qtd_list);
+		return qh;
+	}
+	return qh;
+}
+
+
+
+
+/*
+ * converts original EHCI QTD into PTD(Proprietary transfer descriptor)
+ * we call PTD as qha also for atl transfers
+ * for ATL and INT transfers
+ */
+void *
+phci_hcd_qha_from_qtd(phci_hcd * hcd,
+	struct ehci_qtd *qtd,
+	struct urb *urb,
+	void *ptd, u32 ptd_data_addr, struct ehci_qh *qh)
+{
+	u8 toggle = qh->datatoggle;
+	u32 token = 0;
+	u32 td_info1 = 0;
+	u32 td_info3 = 0;
+	u32 td_info4 = 0;
+	int maxpacket =	0;
+	u32 length = 0,	temp = 0;
+	/*for non high speed devices */
+	u32 portnum = 0;
+	u32 hubnum = 0;
+	u32 se = 0, rl = 0x0, nk = 0x0;
+	u8 datatoggle =	0;
+	struct isp1763_mem_addr	*mem_addr = &qtd->mem_addr;
+	u32 data_addr =	0;
+	u32 multi = 0;
+	struct _isp1763_qha *qha = (isp1763_qha	*) ptd;
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+
+	maxpacket = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+
+	multi =	1 + ((maxpacket	>> 11) & 0x3);
+
+	maxpacket &= 0x7ff;
+
+	/************************first word*********************************/
+	length = qtd->length;
+	td_info1 = QHA_VALID;
+	td_info1 |= (length << 3);
+	td_info1 |= (maxpacket << 18);
+	td_info1 |= (usb_pipeendpoint(urb->pipe) << 31);
+	td_info1 |= MULTI(multi);
+	/*set the first	dword */
+	qha->td_info1 =	td_info1;
+
+	pehci_print("%s: length	%d, 1st	word 0x%08x\n",	__FUNCTION__, length,
+		    qha->td_info1);
+
+	/*******************second word***************************************/
+	temp = qtd->hw_token;
+
+	/*take the pid,	thats of only interest to me from qtd,
+	 */
+
+	temp = temp & 0x0300;
+	temp = temp >> 8;
+	/*take the endpoint and	its 3 bits */
+	token =	(usb_pipeendpoint(urb->pipe) & 0xE) >> 1;
+	token |= usb_pipedevice(urb->pipe) << 3;
+
+	if (urb->dev->speed != USB_SPEED_HIGH) {
+		pehci_print("device is full/low	speed, %d\n", urb->dev->speed);
+		token |= 1 << 14;
+		portnum	= urb->dev->ttport;
+		 /*IMMED*/ hubnum = urb->dev->tt->hub->devnum;
+		token |= portnum << 18;
+		token |= hubnum	<< 25;
+		/*for non-high speed transfer
+		   reload and nak counts are zero
+		 */
+		rl = 0x0;
+		nk = 0x0;
+
+	}
+
+	/*se should be 0x2 for only low	speed devices */
+	if (urb->dev->speed == USB_SPEED_LOW) {
+		se = 0x2;
+	}
+
+	if (usb_pipeint(urb->pipe)) {
+		/*	reload count and nakcount is
+		   required for	only async transfers
+		 */
+		rl = 0x0;
+	}
+
+	/*set the se field, should be zero for all
+	   but low speed devices
+	 */
+	token |= se << 16;
+	/*take the pid */
+	token |= temp << 10;
+
+	if (usb_pipebulk(urb->pipe)) {
+		token |= EPTYPE_BULK;
+	} else if (usb_pipeint(urb->pipe)) {
+		token |= EPTYPE_INT;
+	} else if (usb_pipeisoc(urb->pipe)) {
+		token |= EPTYPE_ISO;
+	}
+
+
+	qha->td_info2 =	token;
+
+	pehci_print("%s: second	word 0x%08x, qtd token 0x%08x\n",
+		    __FUNCTION__, qha->td_info2, temp);
+
+	/***********************Third word*************************************/
+
+	/*calculate the	data start address from	mem_addr for qha */
+
+	data_addr = ((u32) (mem_addr->phy_addr)	& 0xffff) - 0x400;
+	data_addr >>= 3;
+	pehci_print("data start	address	%x\n", data_addr);
+	/*use this field only if there
+	 * is something	to transfer
+	 * */
+	if (length) {
+		td_info3 = data_addr <<	8;
+	}
+	/*RL Count, 16 */
+	td_info3 |= (rl	<< 25);
+	qha->td_info3 =	td_info3;
+
+	pehci_print("%s: third word 0x%08x, tdinfo 0x%08x\n",
+		__FUNCTION__, qha->td_info3, td_info3);
+
+
+	/**************************fourt word*************************************/
+
+	if (usb_pipecontrol(urb->pipe))	{
+		datatoggle = qtd->hw_token >> 31;
+	} else {
+		/*take the data	toggle from the	previous completed transfer
+		   or zero in case of fresh */
+		datatoggle = toggle;
+	}
+
+	td_info4 = QHA_ACTIVE;
+	/*dt */
+	td_info4 |= datatoggle << 25;	/*QHA_DATA_TOGGLE; */
+	/*3 retry count	for setup else forever */
+	if (PTD_PID(qha->td_info2) == SETUP_PID) {
+		td_info4 |= (3 << 23);
+	} else {
+		td_info4 |= (0 << 23);
+	}
+	
+	/*nak count */
+	td_info4 |= (nk	<< 19);
+
+	td_info4 |= (qh->ping << 26);
+	qha->td_info4 =	td_info4;
+#ifdef PTD_DUMP_SCHEDULE
+	printk("SCHEDULE PTD DUMPE\n") ;
+	printk("SDW0: 0x%08x\n",qha->td_info1);
+	printk("SDW1: 0x%08x\n",qha->td_info2);
+	printk("SDW2: 0x%08x\n",qha->td_info3);
+	printk("SDW3: 0x%08x\n",qha->td_info4);
+#endif
+	pehci_print("%s: fourt word 0x%08x\n", __FUNCTION__, qha->td_info4);
+	pehci_entry("--	%s: Exit, qha %p\n", __FUNCTION__, qha);
+	return qha;
+
+}
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
index 1bfcd02..13828e0 100644
--- a/drivers/usb/misc/Kconfig
+++ b/drivers/usb/misc/Kconfig
@@ -218,6 +218,21 @@
 	  See <http://www.linux-usb.org/usbtest/> for more information,
 	  including sample test device firmware and "how to use it".
 
+config USB_EHSET_TEST_FIXTURE
+	tristate "USB EHSET Test Fixture Driver"
+	depends on USB && USB_EHCI_EHSET
+	default n
+	help
+	  Say Y here if you want to use EHSET Test Fixture device for host
+	  compliance testing.
+
+	  This driver initiates test modes on the downstream port to which the
+	  test fixture is attached.
+
+	  See <http://www.usb.org/developers/onthego/EHSET_v1.01.pdf>
+	  for more information.
+
+
 config USB_ISIGHTFW
 	tristate "iSight firmware loading support"
 	depends on USB
@@ -244,3 +259,37 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called yurex.
 
+config USB_QCOM_DIAG_BRIDGE
+	tristate "USB Qualcomm diagnostic bridge driver"
+	depends on USB
+	help
+	  Say Y here if you have a Qualcomm modem device connected via USB that
+	  will be bridged in kernel space. This driver communicates with the
+	  diagnostic interface and allows for bridging with the diag forwarding
+	  driver.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called diag_bridge.  If unsure, choose N.
+
+config USB_QCOM_DIAG_BRIDGE_TEST
+	tristate "USB Qualcomm diagnostic bridge driver test"
+	depends on USB && USB_QCOM_DIAG_BRIDGE
+	help
+	  Say Y here if you want to enable the test hook for the
+	  Qualcomm diag bridge driver. When enabled, this will create
+	  a debugfs file entry named "diag_bridge_test" which can be used
+	  to send a ping command to the diag port of the modem over USB.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called diag_bridge_test.  If unsure, choose N.
+
+config USB_QCOM_MDM_BRIDGE
+	tristate "USB Qualcomm modem bridge driver for DUN and RMNET"
+	depends on USB
+	help
+	  Say Y here if you have a Qualcomm modem device connected via USB that
+	  will be bridged in kernel space. This driver works as a bridge to pass
+	  control and data packets between the modem and peripheral usb gadget
+	  driver for dial up network and RMNET.
+	  To compile this driver as a module, choose M here: the module
+	  will be called mdm_bridge. If unsure, choose N.
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
index 796ce7e..b4aee65 100644
--- a/drivers/usb/misc/Makefile
+++ b/drivers/usb/misc/Makefile
@@ -21,9 +21,15 @@
 obj-$(CONFIG_USB_LEGOTOWER)		+= legousbtower.o
 obj-$(CONFIG_USB_RIO500)		+= rio500.o
 obj-$(CONFIG_USB_TEST)			+= usbtest.o
+obj-$(CONFIG_USB_EHSET_TEST_FIXTURE)	+= ehset.o
 obj-$(CONFIG_USB_TRANCEVIBRATOR)	+= trancevibrator.o
 obj-$(CONFIG_USB_USS720)		+= uss720.o
 obj-$(CONFIG_USB_SEVSEG)		+= usbsevseg.o
 obj-$(CONFIG_USB_YUREX)			+= yurex.o
 
 obj-$(CONFIG_USB_SISUSBVGA)		+= sisusbvga/
+
+obj-$(CONFIG_USB_QCOM_DIAG_BRIDGE)	+= diag_bridge.o
+obj-$(CONFIG_USB_QCOM_DIAG_BRIDGE_TEST)	+= diag_bridge_test.o
+mdm_bridge-y				:= mdm_ctrl_bridge.o mdm_data_bridge.o
+obj-$(CONFIG_USB_QCOM_MDM_BRIDGE) 	+= mdm_bridge.o
diff --git a/drivers/usb/misc/diag_bridge.c b/drivers/usb/misc/diag_bridge.c
new file mode 100644
index 0000000..96e5a90
--- /dev/null
+++ b/drivers/usb/misc/diag_bridge.c
@@ -0,0 +1,487 @@
+/*
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/debugfs.h>
+#include <mach/diag_bridge.h>
+
+#define DRIVER_DESC	"USB host diag bridge driver"
+#define DRIVER_VERSION	"1.0"
+
+struct diag_bridge {
+	struct usb_device	*udev;
+	struct usb_interface	*ifc;
+	struct usb_anchor	submitted;
+	__u8			in_epAddr;
+	__u8			out_epAddr;
+	int			err;
+	struct kref		kref;
+	struct diag_bridge_ops	*ops;
+	struct platform_device	*pdev;
+
+	/* debugging counters */
+	unsigned long		bytes_to_host;
+	unsigned long		bytes_to_mdm;
+	unsigned		pending_reads;
+	unsigned		pending_writes;
+};
+struct diag_bridge *__dev;
+
+int diag_bridge_open(struct diag_bridge_ops *ops)
+{
+	struct diag_bridge	*dev = __dev;
+
+	if (!dev) {
+		err("dev is null");
+		return -ENODEV;
+	}
+
+	dev->ops = ops;
+	dev->err = 0;
+
+	return 0;
+}
+EXPORT_SYMBOL(diag_bridge_open);
+
+void diag_bridge_close(void)
+{
+	struct diag_bridge	*dev = __dev;
+
+	dev_dbg(&dev->udev->dev, "%s:\n", __func__);
+
+	usb_kill_anchored_urbs(&dev->submitted);
+
+	dev->ops = 0;
+}
+EXPORT_SYMBOL(diag_bridge_close);
+
+static void diag_bridge_read_cb(struct urb *urb)
+{
+	struct diag_bridge	*dev = urb->context;
+	struct diag_bridge_ops	*cbs = dev->ops;
+
+	dev_dbg(&dev->udev->dev, "%s: status:%d actual:%d\n", __func__,
+			urb->status, urb->actual_length);
+
+	if (urb->status == -EPROTO) {
+		dev_err(&dev->udev->dev, "%s: proto error\n", __func__);
+		/* save error so that subsequent read/write returns ESHUTDOWN */
+		dev->err = urb->status;
+		return;
+	}
+
+	if (cbs && cbs->read_complete_cb)
+		cbs->read_complete_cb(cbs->ctxt,
+			urb->transfer_buffer,
+			urb->transfer_buffer_length,
+			urb->status < 0 ? urb->status : urb->actual_length);
+
+	dev->bytes_to_host += urb->actual_length;
+	dev->pending_reads--;
+}
+
+int diag_bridge_read(char *data, int size)
+{
+	struct urb		*urb = NULL;
+	unsigned int		pipe;
+	struct diag_bridge	*dev = __dev;
+	int			ret;
+
+	dev_dbg(&dev->udev->dev, "%s:\n", __func__);
+
+	if (!size) {
+		dev_err(&dev->udev->dev, "invalid size:%d\n", size);
+		return -EINVAL;
+	}
+
+	if (!dev->ifc) {
+		dev_err(&dev->udev->dev, "device is disconnected\n");
+		return -ENODEV;
+	}
+
+	/* if there was a previous unrecoverable error, just quit */
+	if (dev->err)
+		return -ESHUTDOWN;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb) {
+		dev_err(&dev->udev->dev, "unable to allocate urb\n");
+		return -ENOMEM;
+	}
+
+	ret = usb_autopm_get_interface(dev->ifc);
+	if (ret < 0) {
+		dev_err(&dev->udev->dev, "autopm_get failed:%d\n", ret);
+		usb_free_urb(urb);
+		return ret;
+	}
+
+	pipe = usb_rcvbulkpipe(dev->udev, dev->in_epAddr);
+	usb_fill_bulk_urb(urb, dev->udev, pipe, data, size,
+				diag_bridge_read_cb, dev);
+	usb_anchor_urb(urb, &dev->submitted);
+	dev->pending_reads++;
+
+	ret = usb_submit_urb(urb, GFP_KERNEL);
+	if (ret) {
+		dev_err(&dev->udev->dev, "submitting urb failed err:%d\n", ret);
+		dev->pending_reads--;
+		usb_unanchor_urb(urb);
+		usb_free_urb(urb);
+		usb_autopm_put_interface(dev->ifc);
+		return ret;
+	}
+
+	usb_autopm_put_interface(dev->ifc);
+	usb_free_urb(urb);
+
+	return 0;
+}
+EXPORT_SYMBOL(diag_bridge_read);
+
+static void diag_bridge_write_cb(struct urb *urb)
+{
+	struct diag_bridge	*dev = urb->context;
+	struct diag_bridge_ops	*cbs = dev->ops;
+
+	dev_dbg(&dev->udev->dev, "%s:\n", __func__);
+
+	usb_autopm_put_interface_async(dev->ifc);
+
+	if (urb->status == -EPROTO) {
+		dev_err(&dev->udev->dev, "%s: proto error\n", __func__);
+		/* save error so that subsequent read/write returns ESHUTDOWN */
+		dev->err = urb->status;
+		return;
+	}
+
+	if (cbs && cbs->write_complete_cb)
+		cbs->write_complete_cb(cbs->ctxt,
+			urb->transfer_buffer,
+			urb->transfer_buffer_length,
+			urb->status < 0 ? urb->status : urb->actual_length);
+
+	dev->bytes_to_mdm += urb->actual_length;
+	dev->pending_writes--;
+}
+
+int diag_bridge_write(char *data, int size)
+{
+	struct urb		*urb = NULL;
+	unsigned int		pipe;
+	struct diag_bridge	*dev = __dev;
+	int			ret;
+
+	dev_dbg(&dev->udev->dev, "%s:\n", __func__);
+
+	if (!size) {
+		dev_err(&dev->udev->dev, "invalid size:%d\n", size);
+		return -EINVAL;
+	}
+
+	if (!dev->ifc) {
+		dev_err(&dev->udev->dev, "device is disconnected\n");
+		return -ENODEV;
+	}
+
+	/* if there was a previous unrecoverable error, just quit */
+	if (dev->err)
+		return -ESHUTDOWN;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb) {
+		err("unable to allocate urb");
+		return -ENOMEM;
+	}
+
+	ret = usb_autopm_get_interface(dev->ifc);
+	if (ret < 0) {
+		dev_err(&dev->udev->dev, "autopm_get failed:%d\n", ret);
+		usb_free_urb(urb);
+		return ret;
+	}
+
+	pipe = usb_sndbulkpipe(dev->udev, dev->out_epAddr);
+	usb_fill_bulk_urb(urb, dev->udev, pipe, data, size,
+				diag_bridge_write_cb, dev);
+	usb_anchor_urb(urb, &dev->submitted);
+	dev->pending_writes++;
+
+	ret = usb_submit_urb(urb, GFP_KERNEL);
+	if (ret) {
+		dev_err(&dev->udev->dev, "submitting urb failed err:%d\n", ret);
+		dev->pending_writes--;
+		usb_unanchor_urb(urb);
+		usb_free_urb(urb);
+		usb_autopm_put_interface(dev->ifc);
+		return ret;
+	}
+
+	usb_free_urb(urb);
+
+	return 0;
+}
+EXPORT_SYMBOL(diag_bridge_write);
+
+static void diag_bridge_delete(struct kref *kref)
+{
+	struct diag_bridge *dev =
+		container_of(kref, struct diag_bridge, kref);
+
+	usb_put_dev(dev->udev);
+	__dev = 0;
+	kfree(dev);
+}
+
+#if defined(CONFIG_DEBUG_FS)
+#define DEBUG_BUF_SIZE	512
+static ssize_t diag_read_stats(struct file *file, char __user *ubuf,
+				size_t count, loff_t *ppos)
+{
+	struct diag_bridge	*dev = __dev;
+	char			*buf;
+	int			ret;
+
+	buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	ret = scnprintf(buf, DEBUG_BUF_SIZE,
+			"epin:%d, epout:%d\n"
+			"bytes to host: %lu\n"
+			"bytes to mdm: %lu\n"
+			"pending reads: %u\n"
+			"pending writes: %u\n"
+			"last error: %d\n",
+			dev->in_epAddr, dev->out_epAddr,
+			dev->bytes_to_host, dev->bytes_to_mdm,
+			dev->pending_reads, dev->pending_writes,
+			dev->err);
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, ret);
+	kfree(buf);
+	return ret;
+}
+
+static ssize_t diag_reset_stats(struct file *file, const char __user *buf,
+				 size_t count, loff_t *ppos)
+{
+	struct diag_bridge	*dev = __dev;
+
+	dev->bytes_to_host = dev->bytes_to_mdm = 0;
+	dev->pending_reads = dev->pending_writes = 0;
+
+	return count;
+}
+
+const struct file_operations diag_stats_ops = {
+	.read = diag_read_stats,
+	.write = diag_reset_stats,
+};
+
+static struct dentry *dent;
+
+static void diag_bridge_debugfs_init(void)
+{
+	struct dentry *dfile;
+
+	dent = debugfs_create_dir("diag_bridge", 0);
+	if (IS_ERR(dent))
+		return;
+
+	dfile = debugfs_create_file("status", 0444, dent, 0, &diag_stats_ops);
+	if (!dfile || IS_ERR(dfile))
+		debugfs_remove(dent);
+}
+
+static void diag_bridge_debugfs_cleanup(void)
+{
+	if (dent) {
+		debugfs_remove_recursive(dent);
+		dent = NULL;
+	}
+}
+#else
+static inline void diag_bridge_debugfs_init(void) { }
+static inline void diag_bridge_debugfs_cleanup(void) { }
+#endif
+
+static int
+diag_bridge_probe(struct usb_interface *ifc, const struct usb_device_id *id)
+{
+	struct diag_bridge		*dev;
+	struct usb_host_interface	*ifc_desc;
+	struct usb_endpoint_descriptor	*ep_desc;
+	int				i;
+	int				ret = -ENOMEM;
+	__u8				ifc_num;
+
+	dbg("%s: id:%lu", __func__, id->driver_info);
+
+	ifc_num = ifc->cur_altsetting->desc.bInterfaceNumber;
+
+	/* is this interface supported ? */
+	if (ifc_num != id->driver_info)
+		return -ENODEV;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev) {
+		pr_err("%s: unable to allocate dev\n", __func__);
+		return -ENOMEM;
+	}
+	dev->pdev = platform_device_alloc("diag_bridge", -1);
+	if (!dev->pdev) {
+		pr_err("%s: unable to allocate platform device\n", __func__);
+		kfree(dev);
+		return -ENOMEM;
+	}
+	__dev = dev;
+
+	dev->udev = usb_get_dev(interface_to_usbdev(ifc));
+	dev->ifc = ifc;
+	kref_init(&dev->kref);
+	init_usb_anchor(&dev->submitted);
+
+	ifc_desc = ifc->cur_altsetting;
+	for (i = 0; i < ifc_desc->desc.bNumEndpoints; i++) {
+		ep_desc = &ifc_desc->endpoint[i].desc;
+
+		if (!dev->in_epAddr && usb_endpoint_is_bulk_in(ep_desc))
+			dev->in_epAddr = ep_desc->bEndpointAddress;
+
+		if (!dev->out_epAddr && usb_endpoint_is_bulk_out(ep_desc))
+			dev->out_epAddr = ep_desc->bEndpointAddress;
+	}
+
+	if (!(dev->in_epAddr && dev->out_epAddr)) {
+		err("could not find bulk in and bulk out endpoints");
+		ret = -ENODEV;
+		goto error;
+	}
+
+	usb_set_intfdata(ifc, dev);
+	diag_bridge_debugfs_init();
+	platform_device_add(dev->pdev);
+
+	dev_dbg(&dev->udev->dev, "%s: complete\n", __func__);
+
+	return 0;
+
+error:
+	if (dev)
+		kref_put(&dev->kref, diag_bridge_delete);
+
+	return ret;
+}
+
+static void diag_bridge_disconnect(struct usb_interface *ifc)
+{
+	struct diag_bridge	*dev = usb_get_intfdata(ifc);
+
+	dev_dbg(&dev->udev->dev, "%s:\n", __func__);
+
+	platform_device_del(dev->pdev);
+	diag_bridge_debugfs_cleanup();
+	kref_put(&dev->kref, diag_bridge_delete);
+	usb_set_intfdata(ifc, NULL);
+}
+
+static int diag_bridge_suspend(struct usb_interface *ifc, pm_message_t message)
+{
+	struct diag_bridge	*dev = usb_get_intfdata(ifc);
+	struct diag_bridge_ops	*cbs = dev->ops;
+	int ret = 0;
+
+	if (cbs && cbs->suspend) {
+		ret = cbs->suspend(cbs->ctxt);
+		if (ret) {
+			dev_dbg(&dev->udev->dev,
+				"%s: diag veto'd suspend\n", __func__);
+			return ret;
+		}
+
+		usb_kill_anchored_urbs(&dev->submitted);
+	}
+
+	return ret;
+}
+
+static int diag_bridge_resume(struct usb_interface *ifc)
+{
+	struct diag_bridge	*dev = usb_get_intfdata(ifc);
+	struct diag_bridge_ops	*cbs = dev->ops;
+
+
+	if (cbs && cbs->resume)
+		cbs->resume(cbs->ctxt);
+
+	return 0;
+}
+
+#define VALID_INTERFACE_NUM	0
+static const struct usb_device_id diag_bridge_ids[] = {
+	{ USB_DEVICE(0x5c6, 0x9001),
+	.driver_info = VALID_INTERFACE_NUM, },
+	{ USB_DEVICE(0x5c6, 0x9034),
+	.driver_info = VALID_INTERFACE_NUM, },
+	{ USB_DEVICE(0x5c6, 0x9048),
+	.driver_info = VALID_INTERFACE_NUM, },
+	{ USB_DEVICE(0x5c6, 0x904C),
+	.driver_info = VALID_INTERFACE_NUM, },
+
+	{} /* terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, diag_bridge_ids);
+
+static struct usb_driver diag_bridge_driver = {
+	.name =		"diag_bridge",
+	.probe =	diag_bridge_probe,
+	.disconnect =	diag_bridge_disconnect,
+	.suspend =	diag_bridge_suspend,
+	.resume =	diag_bridge_resume,
+	.id_table =	diag_bridge_ids,
+	.supports_autosuspend = 1,
+};
+
+static int __init diag_bridge_init(void)
+{
+	int ret;
+
+	ret = usb_register(&diag_bridge_driver);
+	if (ret) {
+		err("%s: unable to register diag driver", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void __exit diag_bridge_exit(void)
+{
+	usb_deregister(&diag_bridge_driver);
+}
+
+module_init(diag_bridge_init);
+module_exit(diag_bridge_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/misc/diag_bridge_test.c b/drivers/usb/misc/diag_bridge_test.c
new file mode 100644
index 0000000..5bc0828
--- /dev/null
+++ b/drivers/usb/misc/diag_bridge_test.c
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/crc-ccitt.h>
+#include <mach/diag_bridge.h>
+
+#define DRIVER_DESC	"USB host diag bridge driver test"
+#define DRIVER_VERSION	"1.0"
+
+#define RD_BUF_SIZE	2048
+#define DIAG_TEST_CONNECTED	0
+
+struct diag_test_dev {
+	char *read_buf;
+	struct work_struct read_w;
+	unsigned long	flags;
+
+	struct diag_bridge_ops	ops;
+};
+static struct diag_test_dev *__dev;
+static struct dentry *dent;
+
+static void
+diag_test_read_complete_cb(void *d, char *buf, size_t size, size_t actual)
+{
+	if (actual < 0) {
+		pr_err("%s: read complete err\n", __func__);
+		return;
+	}
+
+	print_hex_dump(KERN_INFO, "to_host:", 0, 1, 1, buf, actual, false);
+}
+static void diag_test_read_work(struct work_struct *w)
+{
+	struct diag_test_dev *dev =
+		container_of(w, struct diag_test_dev, read_w);
+
+	memset(dev->read_buf, 0, RD_BUF_SIZE);
+	diag_bridge_read(dev->read_buf, RD_BUF_SIZE);
+}
+
+static void
+diag_test_write_complete_cb(void *d, char *buf, size_t size, size_t actual)
+{
+	struct diag_test_dev *dev = d;
+
+	if (actual > 0)
+		schedule_work(&dev->read_w);
+}
+
+#if defined(CONFIG_DEBUG_FS)
+#define DEBUG_BUF_SIZE	1024
+static ssize_t send_ping_cmd(struct file *file, const char __user *ubuf,
+				 size_t count, loff_t *ppos)
+{
+	struct diag_test_dev	*dev = __dev;
+	unsigned char		*buf;
+	int			temp = sizeof(unsigned char) * 4;
+
+	if (!dev)
+		return -ENODEV;
+
+	buf = kmalloc(temp, GFP_KERNEL);
+	if (!buf) {
+		pr_err("%s: unable to allocate mem for ping cmd\n",
+				__func__);
+		return -ENOMEM;
+	}
+
+	/* hdlc encoded ping command */
+	buf[0] = 0x0C;
+	buf[1] = 0x14;
+	buf[2] = 0x3A;
+	buf[3] = 0x7E;
+
+	diag_bridge_write(buf, temp);
+
+	return count;
+}
+
+const struct file_operations diag_test_ping_ops = {
+	.write = send_ping_cmd,
+};
+
+static void diag_test_debug_init(void)
+{
+	struct dentry *dfile;
+
+	dent = debugfs_create_dir("diag_test", 0);
+	if (IS_ERR(dent))
+		return;
+
+	dfile = debugfs_create_file("send_ping", 0444, dent,
+			0, &diag_test_ping_ops);
+	if (!dfile || IS_ERR(dfile))
+		debugfs_remove(dent);
+}
+#else
+static void diag_test_debug_init(void) { }
+#endif
+
+static int diag_test_remove(struct platform_device *pdev)
+{
+	diag_bridge_close();
+
+	if (dent) {
+		debugfs_remove_recursive(dent);
+		dent = NULL;
+	}
+
+	return 0;
+}
+
+static int diag_test_probe(struct platform_device *pdev)
+{
+	struct diag_test_dev	*dev = __dev;
+	int			ret = 0;
+
+	pr_info("%s:\n", __func__);
+
+	ret = diag_bridge_open(&dev->ops);
+	if (ret)
+		pr_err("diag open failed: %d", ret);
+
+
+	diag_test_debug_init();
+
+	return ret;
+}
+
+static struct platform_driver diag_test = {
+	.remove = diag_test_remove,
+	.probe	= diag_test_probe,
+	.driver = {
+		.name = "diag_bridge",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init diag_test_init(void)
+{
+	struct diag_test_dev	*dev;
+	int ret = 0;
+
+	pr_info("%s\n", __func__);
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	__dev = dev;
+
+	dev->ops.read_complete_cb = diag_test_read_complete_cb;
+	dev->ops.write_complete_cb = diag_test_write_complete_cb;
+	dev->read_buf = kmalloc(RD_BUF_SIZE, GFP_KERNEL);
+	if (!dev->read_buf) {
+		pr_err("%s: unable to allocate read buffer\n", __func__);
+		kfree(dev);
+		return -ENOMEM;
+	}
+
+	dev->ops.ctxt = dev;
+	INIT_WORK(&dev->read_w, diag_test_read_work);
+
+	ret = platform_driver_register(&diag_test);
+	if (ret)
+		pr_err("%s: platform driver %s register failed %d\n",
+				__func__, diag_test.driver.name, ret);
+
+	return ret;
+}
+
+static void __exit diag_test_exit(void)
+{
+	struct diag_test_dev *dev = __dev;
+
+	pr_info("%s:\n", __func__);
+
+	if (test_bit(DIAG_TEST_CONNECTED, &dev->flags))
+		diag_bridge_close();
+
+	kfree(dev->read_buf);
+	kfree(dev);
+
+}
+
+module_init(diag_test_init);
+module_exit(diag_test_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/misc/ehset.c b/drivers/usb/misc/ehset.c
new file mode 100644
index 0000000..30879e0
--- /dev/null
+++ b/drivers/usb/misc/ehset.c
@@ -0,0 +1,147 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/slab.h>
+#include <linux/usb/ch11.h>
+#include <linux/usb/hcd.h>
+
+#define TEST_SE0_NAK_PID		0x0101
+#define TEST_J_PID			0x0102
+#define TEST_K_PID			0x0103
+#define TEST_PACKET_PID			0x0104
+#define TEST_HS_HOST_PORT_SUSPEND_RESUME 0x0106
+#define TEST_SINGLE_STEP_GET_DEV_DESC	0x0107
+#define TEST_SINGLE_STEP_SET_FEATURE	0x0108
+
+static int ehset_probe(struct usb_interface *intf,
+		       const struct usb_device_id *id)
+{
+	int status = -1;
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct usb_device *rh_udev = dev->bus->root_hub;
+	struct usb_device *hub_udev = dev->parent;
+	int port1 = dev->portnum;
+	int test_mode = le16_to_cpu(dev->descriptor.idProduct);
+
+	switch (test_mode) {
+	case TEST_SE0_NAK_PID:
+		status = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0),
+			USB_REQ_SET_FEATURE, USB_RT_PORT, USB_PORT_FEAT_TEST,
+			(3 << 8) | port1, NULL, 0, 1000);
+		break;
+	case TEST_J_PID:
+		status = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0),
+			USB_REQ_SET_FEATURE, USB_RT_PORT, USB_PORT_FEAT_TEST,
+			(1 << 8) | port1, NULL, 0, 1000);
+		break;
+	case TEST_K_PID:
+		status = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0),
+			USB_REQ_SET_FEATURE, USB_RT_PORT, USB_PORT_FEAT_TEST,
+			(2 << 8) | port1, NULL, 0, 1000);
+		break;
+	case TEST_PACKET_PID:
+		status = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0),
+			USB_REQ_SET_FEATURE, USB_RT_PORT, USB_PORT_FEAT_TEST,
+			(4 << 8) | port1, NULL, 0, 1000);
+		break;
+	case TEST_HS_HOST_PORT_SUSPEND_RESUME:
+		/* Test: wait for 15secs -> suspend -> 15secs delay -> resume */
+		msleep(15 * 1000);
+		status = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0),
+			USB_REQ_SET_FEATURE, USB_RT_PORT,
+			USB_PORT_FEAT_SUSPEND, port1, NULL, 0, 1000);
+		if (status < 0)
+			break;
+		msleep(15 * 1000);
+		status = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0),
+			USB_REQ_CLEAR_FEATURE, USB_RT_PORT,
+			USB_PORT_FEAT_SUSPEND, port1, NULL, 0, 1000);
+		break;
+	case TEST_SINGLE_STEP_GET_DEV_DESC:
+		/* Test: wait for 15secs -> GetDescriptor request */
+		msleep(15 * 1000);
+		{
+			struct usb_device_descriptor *buf;
+			buf = kmalloc(USB_DT_DEVICE_SIZE, GFP_KERNEL);
+			if (!buf)
+				return -ENOMEM;
+
+			status = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+				USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
+				USB_DT_DEVICE << 8, 0,
+				buf, USB_DT_DEVICE_SIZE,
+				USB_CTRL_GET_TIMEOUT);
+			kfree(buf);
+		}
+		break;
+	case TEST_SINGLE_STEP_SET_FEATURE:
+		/* GetDescriptor's SETUP request -> 15secs delay -> IN & STATUS
+		 * Issue request to ehci root hub driver with portnum = 1
+		 */
+		status = usb_control_msg(rh_udev, usb_sndctrlpipe(rh_udev, 0),
+			USB_REQ_SET_FEATURE, USB_RT_PORT, USB_PORT_FEAT_TEST,
+			(6 << 8) | 1, NULL, 0, 60 * 1000);
+
+		break;
+	default:
+		pr_err("%s: undefined test mode ( %X )\n", __func__, test_mode);
+		return -EINVAL;
+	}
+
+	return (status < 0) ? status : 0;
+}
+
+static void ehset_disconnect(struct usb_interface *intf)
+{
+}
+
+static struct usb_device_id ehset_id_table[] = {
+	{ USB_DEVICE(0x1a0a, TEST_SE0_NAK_PID) },
+	{ USB_DEVICE(0x1a0a, TEST_J_PID) },
+	{ USB_DEVICE(0x1a0a, TEST_K_PID) },
+	{ USB_DEVICE(0x1a0a, TEST_PACKET_PID) },
+	{ USB_DEVICE(0x1a0a, TEST_HS_HOST_PORT_SUSPEND_RESUME) },
+	{ USB_DEVICE(0x1a0a, TEST_SINGLE_STEP_GET_DEV_DESC) },
+	{ USB_DEVICE(0x1a0a, TEST_SINGLE_STEP_SET_FEATURE) },
+	{ }			/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, ehset_id_table);
+
+static struct usb_driver ehset_driver = {
+	.name =		"usb_ehset_test",
+	.probe =	ehset_probe,
+	.disconnect =	ehset_disconnect,
+	.id_table =	ehset_id_table,
+};
+
+static int __init ehset_init(void)
+{
+	return usb_register(&ehset_driver);
+}
+
+static void __exit ehset_exit(void)
+{
+	usb_deregister(&ehset_driver);
+}
+
+module_init(ehset_init);
+module_exit(ehset_exit);
+
+MODULE_DESCRIPTION("USB Driver for EHSET Test Fixture");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/misc/mdm_ctrl_bridge.c b/drivers/usb/misc/mdm_ctrl_bridge.c
new file mode 100644
index 0000000..49591cd
--- /dev/null
+++ b/drivers/usb/misc/mdm_ctrl_bridge.c
@@ -0,0 +1,757 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <linux/debugfs.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/ratelimit.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/cdc.h>
+#include <linux/termios.h>
+#include <asm/unaligned.h>
+#include <mach/usb_bridge.h>
+
+static const char *ctrl_bridge_names[] = {
+	"dun_ctrl_hsic0",
+	"rmnet_ctrl_hsic0"
+};
+
+/* polling interval for Interrupt ep */
+#define HS_INTERVAL		7
+#define FS_LS_INTERVAL		3
+
+#define ACM_CTRL_DTR		(1 << 0)
+#define DEFAULT_READ_URB_LENGTH	4096
+
+#define SUSPENDED		BIT(0)
+
+struct ctrl_bridge {
+	struct usb_device	*udev;
+	struct usb_interface	*intf;
+
+	unsigned int		int_pipe;
+	struct urb		*inturb;
+	void			*intbuf;
+
+	struct urb		*readurb;
+	void			*readbuf;
+
+	struct usb_anchor	tx_submitted;
+	struct usb_anchor	tx_deferred;
+	struct usb_ctrlrequest	*in_ctlreq;
+
+	struct bridge		*brdg;
+	struct platform_device	*pdev;
+
+	unsigned long		flags;
+
+	/* input control lines (DSR, CTS, CD, RI) */
+	unsigned int		cbits_tohost;
+
+	/* output control lines (DTR, RTS) */
+	unsigned int		cbits_tomdm;
+
+	/* counters */
+	unsigned int		snd_encap_cmd;
+	unsigned int		get_encap_res;
+	unsigned int		resp_avail;
+	unsigned int		set_ctrl_line_sts;
+	unsigned int		notify_ser_state;
+};
+
+static struct ctrl_bridge	*__dev[MAX_BRIDGE_DEVICES];
+
+/* counter used for indexing ctrl bridge devices */
+static int	ch_id;
+
+unsigned int ctrl_bridge_get_cbits_tohost(unsigned int id)
+{
+	struct ctrl_bridge	*dev;
+
+	if (id >= MAX_BRIDGE_DEVICES)
+		return -EINVAL;
+
+	dev = __dev[id];
+	if (!dev)
+		return -ENODEV;
+
+	return dev->cbits_tohost;
+}
+EXPORT_SYMBOL(ctrl_bridge_get_cbits_tohost);
+
+int ctrl_bridge_set_cbits(unsigned int id, unsigned int cbits)
+{
+	struct ctrl_bridge	*dev;
+	struct bridge		*brdg;
+	int			retval;
+
+	if (id >= MAX_BRIDGE_DEVICES)
+		return -EINVAL;
+
+	dev = __dev[id];
+	if (!dev)
+		return -ENODEV;
+
+	pr_debug("%s: dev[id] =%u cbits : %u\n", __func__, id, cbits);
+
+	brdg = dev->brdg;
+	if (!brdg)
+		return -ENODEV;
+
+	dev->cbits_tomdm = cbits;
+
+	retval = ctrl_bridge_write(id, NULL, 0);
+
+	/* if DTR is high, update latest modem info to host */
+	if (brdg && (cbits & ACM_CTRL_DTR) && brdg->ops.send_cbits)
+		brdg->ops.send_cbits(brdg->ctx, dev->cbits_tohost);
+
+	return retval;
+}
+EXPORT_SYMBOL(ctrl_bridge_set_cbits);
+
+static void resp_avail_cb(struct urb *urb)
+{
+	struct ctrl_bridge	*dev = urb->context;
+	struct usb_device	*udev;
+	int			status = 0;
+	int			resubmit_urb = 1;
+	struct bridge		*brdg = dev->brdg;
+
+	udev = interface_to_usbdev(dev->intf);
+	switch (urb->status) {
+	case 0:
+		/*success*/
+		dev->get_encap_res++;
+		if (brdg && brdg->ops.send_pkt)
+			brdg->ops.send_pkt(brdg->ctx, urb->transfer_buffer,
+				urb->actual_length);
+		break;
+
+	/*do not resubmit*/
+	case -ESHUTDOWN:
+	case -ENOENT:
+	case -ECONNRESET:
+		/* unplug */
+	case -EPROTO:
+		/*babble error*/
+		resubmit_urb = 0;
+	/*resubmit*/
+	case -EOVERFLOW:
+	default:
+		dev_dbg(&udev->dev, "%s: non zero urb status = %d\n",
+			__func__, urb->status);
+	}
+
+	if (resubmit_urb) {
+		/*re- submit int urb to check response available*/
+		usb_anchor_urb(dev->inturb, &dev->tx_submitted);
+		status = usb_submit_urb(dev->inturb, GFP_ATOMIC);
+		if (status) {
+			dev_err(&udev->dev,
+				"%s: Error re-submitting Int URB %d\n",
+				__func__, status);
+			usb_unanchor_urb(dev->inturb);
+		}
+	}
+}
+
+static void notification_available_cb(struct urb *urb)
+{
+	int				status;
+	struct usb_cdc_notification	*ctrl;
+	struct usb_device		*udev;
+	struct ctrl_bridge		*dev = urb->context;
+	struct bridge			*brdg = dev->brdg;
+	unsigned int			ctrl_bits;
+	unsigned char			*data;
+
+	udev = interface_to_usbdev(dev->intf);
+
+	switch (urb->status) {
+	case 0:
+		/*success*/
+		break;
+	case -ESHUTDOWN:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPROTO:
+		 /* unplug */
+		 return;
+	case -EPIPE:
+		dev_err(&udev->dev, "%s: stall on int endpoint\n", __func__);
+		/* TBD : halt to be cleared in work */
+	case -EOVERFLOW:
+	default:
+		pr_debug_ratelimited("%s: non zero urb status = %d\n",
+					__func__, urb->status);
+		goto resubmit_int_urb;
+	}
+
+	ctrl = (struct usb_cdc_notification *)urb->transfer_buffer;
+	data = (unsigned char *)(ctrl + 1);
+
+	switch (ctrl->bNotificationType) {
+	case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:
+		dev->resp_avail++;
+		usb_fill_control_urb(dev->readurb, udev,
+					usb_rcvctrlpipe(udev, 0),
+					(unsigned char *)dev->in_ctlreq,
+					dev->readbuf,
+					DEFAULT_READ_URB_LENGTH,
+					resp_avail_cb, dev);
+
+		usb_anchor_urb(dev->readurb, &dev->tx_submitted);
+		status = usb_submit_urb(dev->readurb, GFP_ATOMIC);
+		if (status) {
+			dev_err(&udev->dev,
+				"%s: Error submitting Read URB %d\n",
+				__func__, status);
+			usb_unanchor_urb(dev->readurb);
+			goto resubmit_int_urb;
+		}
+		return;
+	case USB_CDC_NOTIFY_NETWORK_CONNECTION:
+		dev_dbg(&udev->dev, "%s network\n", ctrl->wValue ?
+					"connected to" : "disconnected from");
+		break;
+	case USB_CDC_NOTIFY_SERIAL_STATE:
+		dev->notify_ser_state++;
+		ctrl_bits = get_unaligned_le16(data);
+		dev_dbg(&udev->dev, "serial state: %d\n", ctrl_bits);
+		dev->cbits_tohost = ctrl_bits;
+		if (brdg && brdg->ops.send_cbits)
+			brdg->ops.send_cbits(brdg->ctx, ctrl_bits);
+		break;
+	default:
+		dev_err(&udev->dev, "%s: unknown notification %d received:"
+			"index %d len %d data0 %d data1 %d",
+			__func__, ctrl->bNotificationType, ctrl->wIndex,
+			ctrl->wLength, data[0], data[1]);
+	}
+
+resubmit_int_urb:
+	usb_anchor_urb(urb, &dev->tx_submitted);
+	status = usb_submit_urb(urb, GFP_ATOMIC);
+	if (status) {
+		dev_err(&udev->dev, "%s: Error re-submitting Int URB %d\n",
+		__func__, status);
+		usb_unanchor_urb(urb);
+	}
+}
+
+static int ctrl_bridge_start_read(struct ctrl_bridge *dev)
+{
+	int	retval = 0;
+
+	if (!dev->inturb) {
+		dev_err(&dev->udev->dev, "%s: inturb is NULL\n", __func__);
+		return -ENODEV;
+	}
+
+	if (!dev->inturb->anchor) {
+		usb_anchor_urb(dev->inturb, &dev->tx_submitted);
+		retval = usb_submit_urb(dev->inturb, GFP_KERNEL);
+		if (retval < 0) {
+			dev_err(&dev->udev->dev,
+				"%s error submitting int urb %d\n",
+				__func__, retval);
+			usb_unanchor_urb(dev->inturb);
+		}
+	}
+
+	return retval;
+}
+
+int ctrl_bridge_open(struct bridge *brdg)
+{
+	struct ctrl_bridge	*dev;
+
+	if (!brdg) {
+		err("bridge is null\n");
+		return -EINVAL;
+	}
+
+	if (brdg->ch_id >= MAX_BRIDGE_DEVICES)
+		return -EINVAL;
+
+	dev = __dev[brdg->ch_id];
+	if (!dev) {
+		err("dev is null\n");
+		return -ENODEV;
+	}
+
+	dev->brdg = brdg;
+	dev->snd_encap_cmd = 0;
+	dev->get_encap_res = 0;
+	dev->resp_avail = 0;
+	dev->set_ctrl_line_sts = 0;
+	dev->notify_ser_state = 0;
+
+	if (brdg->ops.send_cbits)
+		brdg->ops.send_cbits(brdg->ctx, dev->cbits_tohost);
+
+	return 0;
+}
+EXPORT_SYMBOL(ctrl_bridge_open);
+
+void ctrl_bridge_close(unsigned int id)
+{
+	struct ctrl_bridge	*dev;
+
+	if (id >= MAX_BRIDGE_DEVICES)
+		return;
+
+	dev  = __dev[id];
+	if (!dev || !dev->brdg)
+		return;
+
+	dev_dbg(&dev->udev->dev, "%s:\n", __func__);
+
+	ctrl_bridge_set_cbits(dev->brdg->ch_id, 0);
+	usb_unlink_anchored_urbs(&dev->tx_submitted);
+
+	dev->brdg = NULL;
+}
+EXPORT_SYMBOL(ctrl_bridge_close);
+
+static void ctrl_write_callback(struct urb *urb)
+{
+	struct ctrl_bridge	*dev = urb->context;
+
+	if (urb->status) {
+		pr_debug("Write status/size %d/%d\n",
+			urb->status, urb->actual_length);
+	}
+
+	kfree(urb->transfer_buffer);
+	kfree(urb->setup_packet);
+	usb_free_urb(urb);
+	usb_autopm_put_interface_async(dev->intf);
+}
+
+int ctrl_bridge_write(unsigned int id, char *data, size_t size)
+{
+	int			result;
+	struct urb		*writeurb;
+	struct usb_ctrlrequest	*out_ctlreq;
+	struct usb_device	*udev;
+	struct ctrl_bridge	*dev;
+
+	if (id >= MAX_BRIDGE_DEVICES) {
+		result = -EINVAL;
+		goto free_data;
+	}
+
+	dev = __dev[id];
+
+	if (!dev) {
+		result = -ENODEV;
+		goto free_data;
+	}
+
+	udev = interface_to_usbdev(dev->intf);
+
+	dev_dbg(&udev->dev, "%s:[id]:%u: write (%d bytes)\n",
+		__func__, id, size);
+
+	writeurb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!writeurb) {
+		dev_err(&udev->dev, "%s: error allocating read urb\n",
+			__func__);
+		result = -ENOMEM;
+		goto free_data;
+	}
+
+	out_ctlreq = kmalloc(sizeof(*out_ctlreq), GFP_ATOMIC);
+	if (!out_ctlreq) {
+		dev_err(&udev->dev,
+			"%s: error allocating setup packet buffer\n",
+			__func__);
+		result = -ENOMEM;
+		goto free_urb;
+	}
+
+	/* CDC Send Encapsulated Request packet */
+	out_ctlreq->bRequestType = (USB_DIR_OUT | USB_TYPE_CLASS |
+				 USB_RECIP_INTERFACE);
+	if (!data && !size) {
+		out_ctlreq->bRequest = USB_CDC_REQ_SET_CONTROL_LINE_STATE;
+		out_ctlreq->wValue = dev->cbits_tomdm;
+		dev->set_ctrl_line_sts++;
+	} else {
+		out_ctlreq->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND;
+		out_ctlreq->wValue = 0;
+		dev->snd_encap_cmd++;
+	}
+	out_ctlreq->wIndex =
+		dev->intf->cur_altsetting->desc.bInterfaceNumber;
+	out_ctlreq->wLength = cpu_to_le16(size);
+
+	usb_fill_control_urb(writeurb, udev,
+				 usb_sndctrlpipe(udev, 0),
+				 (unsigned char *)out_ctlreq,
+				 (void *)data, size,
+				 ctrl_write_callback, dev);
+
+	result = usb_autopm_get_interface_async(dev->intf);
+	if (result < 0) {
+		dev_err(&udev->dev, "%s: unable to resume interface: %d\n",
+			__func__, result);
+
+		/*
+		  * Revisit: if (result == -EPERM)
+		  * bridge_suspend(dev->intf, PMSG_SUSPEND);
+		  */
+
+		goto free_ctrlreq;
+	}
+
+	if (test_bit(SUSPENDED, &dev->flags)) {
+		usb_anchor_urb(writeurb, &dev->tx_deferred);
+		goto deferred;
+	}
+
+	usb_anchor_urb(writeurb, &dev->tx_submitted);
+	result = usb_submit_urb(writeurb, GFP_ATOMIC);
+	if (result < 0) {
+		dev_err(&udev->dev, "%s: submit URB error %d\n",
+			__func__, result);
+		usb_autopm_put_interface_async(dev->intf);
+		goto unanchor_urb;
+	}
+deferred:
+	return size;
+
+unanchor_urb:
+	usb_unanchor_urb(writeurb);
+free_ctrlreq:
+	kfree(out_ctlreq);
+free_urb:
+	usb_free_urb(writeurb);
+free_data:
+	kfree(data);
+
+	return result;
+}
+EXPORT_SYMBOL(ctrl_bridge_write);
+
+int ctrl_bridge_suspend(unsigned int id)
+{
+	struct ctrl_bridge	*dev;
+
+	if (id >= MAX_BRIDGE_DEVICES)
+		return -EINVAL;
+
+	dev = __dev[id];
+	if (!dev)
+		return -ENODEV;
+
+	set_bit(SUSPENDED, &dev->flags);
+	usb_kill_anchored_urbs(&dev->tx_submitted);
+
+	return 0;
+}
+
+int ctrl_bridge_resume(unsigned int id)
+{
+	struct ctrl_bridge	*dev;
+	struct urb		*urb;
+
+	if (id >= MAX_BRIDGE_DEVICES)
+		return -EINVAL;
+
+	dev = __dev[id];
+	if (!dev)
+		return -ENODEV;
+
+	if (!test_and_clear_bit(SUSPENDED, &dev->flags))
+		return 0;
+
+	/* submit pending write requests */
+	while ((urb = usb_get_from_anchor(&dev->tx_deferred))) {
+		int ret;
+		usb_anchor_urb(urb, &dev->tx_submitted);
+		ret = usb_submit_urb(urb, GFP_ATOMIC);
+		if (ret < 0) {
+			usb_unanchor_urb(urb);
+			kfree(urb->setup_packet);
+			kfree(urb->transfer_buffer);
+			usb_free_urb(urb);
+			usb_autopm_put_interface_async(dev->intf);
+		}
+	}
+
+	return ctrl_bridge_start_read(dev);
+}
+
+#if defined(CONFIG_DEBUG_FS)
+#define DEBUG_BUF_SIZE	1024
+static ssize_t ctrl_bridge_read_stats(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	struct ctrl_bridge	*dev;
+	char			*buf;
+	int			ret;
+	int			i;
+	int			temp = 0;
+
+	buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	for (i = 0; i < ch_id; i++) {
+		dev = __dev[i];
+		if (!dev)
+			continue;
+
+		temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+				"\nName#%s dev %p\n"
+				"snd encap cmd cnt: %u\n"
+				"get encap res cnt: %u\n"
+				"res available cnt: %u\n"
+				"set ctrlline sts cnt: %u\n"
+				"notify ser state cnt: %u\n"
+				"cbits_tomdm: %d\n"
+				"cbits_tohost: %d\n"
+				"suspended: %d\n",
+				dev->pdev->name, dev,
+				dev->snd_encap_cmd,
+				dev->get_encap_res,
+				dev->resp_avail,
+				dev->set_ctrl_line_sts,
+				dev->notify_ser_state,
+				dev->cbits_tomdm,
+				dev->cbits_tohost,
+				test_bit(SUSPENDED, &dev->flags));
+	}
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+	kfree(buf);
+
+	return ret;
+}
+
+static ssize_t ctrl_bridge_reset_stats(struct file *file,
+	const char __user *buf, size_t count, loff_t *ppos)
+{
+	struct ctrl_bridge	*dev;
+	int			i;
+
+	for (i = 0; i < ch_id; i++) {
+		dev = __dev[i];
+		if (!dev)
+			continue;
+
+		dev->snd_encap_cmd = 0;
+		dev->get_encap_res = 0;
+		dev->resp_avail = 0;
+		dev->set_ctrl_line_sts = 0;
+		dev->notify_ser_state = 0;
+	}
+	return count;
+}
+
+const struct file_operations ctrl_stats_ops = {
+	.read = ctrl_bridge_read_stats,
+	.write = ctrl_bridge_reset_stats,
+};
+
+struct dentry	*ctrl_dent;
+struct dentry	*ctrl_dfile;
+static void ctrl_bridge_debugfs_init(void)
+{
+	ctrl_dent = debugfs_create_dir("ctrl_hsic_bridge", 0);
+	if (IS_ERR(ctrl_dent))
+		return;
+
+	ctrl_dfile =
+		debugfs_create_file("status", 0644, ctrl_dent, 0,
+			&ctrl_stats_ops);
+	if (!ctrl_dfile || IS_ERR(ctrl_dfile))
+		debugfs_remove(ctrl_dent);
+}
+
+static void ctrl_bridge_debugfs_exit(void)
+{
+	debugfs_remove(ctrl_dfile);
+	debugfs_remove(ctrl_dent);
+}
+
+#else
+static void ctrl_bridge_debugfs_init(void) { }
+static void ctrl_bridge_debugfs_exit(void) { }
+#endif
+
+int
+ctrl_bridge_probe(struct usb_interface *ifc, struct usb_host_endpoint *int_in,
+		int id)
+{
+	struct ctrl_bridge		*dev;
+	struct usb_device		*udev;
+	struct usb_endpoint_descriptor	*ep;
+	u16				wMaxPacketSize;
+	int				retval = 0;
+	int				interval;
+
+	udev = interface_to_usbdev(ifc);
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev) {
+		dev_err(&udev->dev, "%s: unable to allocate dev\n",
+			__func__);
+		return -ENOMEM;
+	}
+	dev->pdev = platform_device_alloc(ctrl_bridge_names[id], id);
+	if (!dev->pdev) {
+		dev_err(&dev->udev->dev,
+			"%s: unable to allocate platform device\n", __func__);
+		retval = -ENOMEM;
+		goto nomem;
+	}
+
+	dev->udev = udev;
+	dev->int_pipe = usb_rcvintpipe(udev,
+		int_in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+	dev->intf = ifc;
+
+	init_usb_anchor(&dev->tx_submitted);
+	init_usb_anchor(&dev->tx_deferred);
+
+	/*use max pkt size from ep desc*/
+	ep = &dev->intf->cur_altsetting->endpoint[0].desc;
+
+	dev->inturb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->inturb) {
+		dev_err(&udev->dev, "%s: error allocating int urb\n", __func__);
+		retval = -ENOMEM;
+		goto pdev_del;
+	}
+
+	wMaxPacketSize = le16_to_cpu(ep->wMaxPacketSize);
+
+	dev->intbuf = kmalloc(wMaxPacketSize, GFP_KERNEL);
+	if (!dev->intbuf) {
+		dev_err(&udev->dev, "%s: error allocating int buffer\n",
+			__func__);
+		retval = -ENOMEM;
+		goto free_inturb;
+	}
+
+	interval =
+		(udev->speed == USB_SPEED_HIGH) ? HS_INTERVAL : FS_LS_INTERVAL;
+
+	usb_fill_int_urb(dev->inturb, udev, dev->int_pipe,
+				dev->intbuf, wMaxPacketSize,
+				notification_available_cb, dev, interval);
+
+	dev->readurb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->readurb) {
+		dev_err(&udev->dev, "%s: error allocating read urb\n",
+			__func__);
+		retval = -ENOMEM;
+		goto free_intbuf;
+	}
+
+	dev->readbuf = kmalloc(DEFAULT_READ_URB_LENGTH, GFP_KERNEL);
+	if (!dev->readbuf) {
+		dev_err(&udev->dev, "%s: error allocating read buffer\n",
+			__func__);
+		retval = -ENOMEM;
+		goto free_rurb;
+	}
+
+	dev->in_ctlreq = kmalloc(sizeof(*dev->in_ctlreq), GFP_KERNEL);
+	if (!dev->in_ctlreq) {
+		dev_err(&udev->dev,
+			"%s:error allocating setup packet buffer\n",
+			__func__);
+		retval = -ENOMEM;
+		goto free_rbuf;
+	}
+
+	dev->in_ctlreq->bRequestType =
+			(USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
+	dev->in_ctlreq->bRequest  = USB_CDC_GET_ENCAPSULATED_RESPONSE;
+	dev->in_ctlreq->wValue = 0;
+	dev->in_ctlreq->wIndex =
+		dev->intf->cur_altsetting->desc.bInterfaceNumber;
+	dev->in_ctlreq->wLength = cpu_to_le16(DEFAULT_READ_URB_LENGTH);
+
+	__dev[id] = dev;
+
+	platform_device_add(dev->pdev);
+
+	ch_id++;
+
+	return ctrl_bridge_start_read(dev);
+
+free_rbuf:
+	kfree(dev->readbuf);
+free_rurb:
+	usb_free_urb(dev->readurb);
+free_intbuf:
+	kfree(dev->intbuf);
+free_inturb:
+	usb_free_urb(dev->inturb);
+pdev_del:
+	platform_device_del(dev->pdev);
+nomem:
+	kfree(dev);
+
+	return retval;
+}
+
+void ctrl_bridge_disconnect(unsigned int id)
+{
+	struct ctrl_bridge	*dev = __dev[id];
+
+	dev_dbg(&dev->udev->dev, "%s:\n", __func__);
+
+	platform_device_del(dev->pdev);
+
+	kfree(dev->in_ctlreq);
+	kfree(dev->readbuf);
+	kfree(dev->intbuf);
+
+	usb_free_urb(dev->readurb);
+	usb_free_urb(dev->inturb);
+
+	__dev[id] = NULL;
+	ch_id--;
+
+	kfree(dev);
+}
+
+static int __init ctrl_bridge_init(void)
+{
+	ctrl_bridge_debugfs_init();
+
+	return 0;
+}
+module_init(ctrl_bridge_init);
+
+static void __exit ctrl_bridge_exit(void)
+{
+	ctrl_bridge_debugfs_exit();
+}
+module_exit(ctrl_bridge_exit);
+
+MODULE_DESCRIPTION("Qualcomm modem control bridge driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/misc/mdm_data_bridge.c b/drivers/usb/misc/mdm_data_bridge.c
new file mode 100644
index 0000000..bf8e5f4
--- /dev/null
+++ b/drivers/usb/misc/mdm_data_bridge.c
@@ -0,0 +1,1080 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/ratelimit.h>
+#include <mach/usb_bridge.h>
+
+#define MAX_RX_URBS			50
+#define RMNET_RX_BUFSIZE		2048
+
+#define STOP_SUBMIT_URB_LIMIT		500
+#define FLOW_CTRL_EN_THRESHOLD		500
+#define FLOW_CTRL_DISABLE		300
+#define FLOW_CTRL_SUPPORT		1
+
+static const char	*data_bridge_names[] = {
+	"dun_data_hsic0",
+	"rmnet_data_hsic0"
+};
+
+static struct workqueue_struct	*bridge_wq;
+
+static unsigned int	fctrl_support = FLOW_CTRL_SUPPORT;
+module_param(fctrl_support, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int	fctrl_en_thld = FLOW_CTRL_EN_THRESHOLD;
+module_param(fctrl_en_thld, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int	fctrl_dis_thld = FLOW_CTRL_DISABLE;
+module_param(fctrl_dis_thld, uint, S_IRUGO | S_IWUSR);
+
+unsigned int	max_rx_urbs = MAX_RX_URBS;
+module_param(max_rx_urbs, uint, S_IRUGO | S_IWUSR);
+
+unsigned int	stop_submit_urb_limit = STOP_SUBMIT_URB_LIMIT;
+module_param(stop_submit_urb_limit, uint, S_IRUGO | S_IWUSR);
+
+static unsigned tx_urb_mult = 20;
+module_param(tx_urb_mult, uint, S_IRUGO|S_IWUSR);
+
+#define TX_HALT   BIT(0)
+#define RX_HALT   BIT(1)
+#define SUSPENDED BIT(2)
+
+struct data_bridge {
+	struct usb_interface		*intf;
+	struct usb_device		*udev;
+	int				id;
+
+	unsigned int			bulk_in;
+	unsigned int			bulk_out;
+	int				err;
+
+	/* keep track of in-flight URBs */
+	struct usb_anchor		tx_active;
+	struct usb_anchor		rx_active;
+
+	/* keep track of outgoing URBs during suspend */
+	struct usb_anchor		delayed;
+
+	struct list_head		rx_idle;
+	struct sk_buff_head		rx_done;
+
+	struct workqueue_struct		*wq;
+	struct work_struct		process_rx_w;
+
+	struct bridge			*brdg;
+
+	/* work queue function for handling halt conditions */
+	struct work_struct		kevent;
+
+	unsigned long			flags;
+
+	struct platform_device		*pdev;
+
+	/* counters */
+	atomic_t			pending_txurbs;
+	unsigned int			txurb_drp_cnt;
+	unsigned long			to_host;
+	unsigned long			to_modem;
+	unsigned int			tx_throttled_cnt;
+	unsigned int			tx_unthrottled_cnt;
+	unsigned int			rx_throttled_cnt;
+	unsigned int			rx_unthrottled_cnt;
+};
+
+static struct data_bridge	*__dev[MAX_BRIDGE_DEVICES];
+
+/* counter used for indexing data bridge devices */
+static int	ch_id;
+
+static unsigned int get_timestamp(void);
+static void dbg_timestamp(char *, struct sk_buff *);
+static int submit_rx_urb(struct data_bridge *dev, struct urb *urb,
+		gfp_t flags);
+
+static inline  bool rx_halted(struct data_bridge *dev)
+{
+	return test_bit(RX_HALT, &dev->flags);
+}
+
+static inline bool rx_throttled(struct bridge *brdg)
+{
+	return test_bit(RX_THROTTLED, &brdg->flags);
+}
+
+int data_bridge_unthrottle_rx(unsigned int id)
+{
+	struct data_bridge	*dev;
+
+	if (id >= MAX_BRIDGE_DEVICES)
+		return -EINVAL;
+
+	dev = __dev[id];
+	if (!dev || !dev->brdg)
+		return -ENODEV;
+
+	dev->rx_unthrottled_cnt++;
+	queue_work(dev->wq, &dev->process_rx_w);
+
+	return 0;
+}
+EXPORT_SYMBOL(data_bridge_unthrottle_rx);
+
+static void data_bridge_process_rx(struct work_struct *work)
+{
+	int			retval;
+	unsigned long		flags;
+	struct urb		*rx_idle;
+	struct sk_buff		*skb;
+	struct timestamp_info	*info;
+	struct data_bridge	*dev =
+		container_of(work, struct data_bridge, process_rx_w);
+
+	struct bridge		*brdg = dev->brdg;
+
+	if (!brdg || !brdg->ops.send_pkt || rx_halted(dev))
+		return;
+
+	while (!rx_throttled(brdg) && (skb = skb_dequeue(&dev->rx_done))) {
+		dev->to_host++;
+		info = (struct timestamp_info *)skb->cb;
+		info->rx_done_sent = get_timestamp();
+		/* hand off sk_buff to client,they'll need to free it */
+		retval = brdg->ops.send_pkt(brdg->ctx, skb, skb->len);
+		if (retval == -ENOTCONN || retval == -EINVAL) {
+			return;
+		} else if (retval == -EBUSY) {
+			dev->rx_throttled_cnt++;
+			break;
+		}
+	}
+
+	spin_lock_irqsave(&dev->rx_done.lock, flags);
+	while (!list_empty(&dev->rx_idle)) {
+		if (dev->rx_done.qlen > stop_submit_urb_limit)
+			break;
+
+		rx_idle = list_first_entry(&dev->rx_idle, struct urb, urb_list);
+		list_del(&rx_idle->urb_list);
+		spin_unlock_irqrestore(&dev->rx_done.lock, flags);
+		retval = submit_rx_urb(dev, rx_idle, GFP_KERNEL);
+		spin_lock_irqsave(&dev->rx_done.lock, flags);
+		if (retval) {
+			list_add_tail(&rx_idle->urb_list, &dev->rx_idle);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&dev->rx_done.lock, flags);
+}
+
+static void data_bridge_read_cb(struct urb *urb)
+{
+	struct bridge		*brdg;
+	struct sk_buff		*skb = urb->context;
+	struct timestamp_info	*info = (struct timestamp_info *)skb->cb;
+	struct data_bridge	*dev = info->dev;
+	bool			queue = 0;
+
+	brdg = dev->brdg;
+	skb_put(skb, urb->actual_length);
+
+	switch (urb->status) {
+	case 0: /* success */
+		queue = 1;
+		info->rx_done = get_timestamp();
+		spin_lock(&dev->rx_done.lock);
+		__skb_queue_tail(&dev->rx_done, skb);
+		spin_unlock(&dev->rx_done.lock);
+		break;
+
+	/*do not resubmit*/
+	case -EPIPE:
+		set_bit(RX_HALT, &dev->flags);
+		dev_err(&dev->udev->dev, "%s: epout halted\n", __func__);
+		schedule_work(&dev->kevent);
+		/* FALLTHROUGH */
+	case -ESHUTDOWN:
+	case -ENOENT: /* suspended */
+	case -ECONNRESET: /* unplug */
+	case -EPROTO:
+		dev_kfree_skb_any(skb);
+		break;
+
+	/*resubmit */
+	case -EOVERFLOW: /*babble error*/
+	default:
+		queue = 1;
+		dev_kfree_skb_any(skb);
+		pr_debug_ratelimited("%s: non zero urb status = %d\n",
+			__func__, urb->status);
+		break;
+	}
+
+	spin_lock(&dev->rx_done.lock);
+	list_add_tail(&urb->urb_list, &dev->rx_idle);
+	spin_unlock(&dev->rx_done.lock);
+
+	if (queue)
+		queue_work(dev->wq, &dev->process_rx_w);
+}
+
+static int submit_rx_urb(struct data_bridge *dev, struct urb *rx_urb,
+	gfp_t flags)
+{
+	struct sk_buff		*skb;
+	struct timestamp_info	*info;
+	int			retval = -EINVAL;
+	unsigned int		created;
+
+	created = get_timestamp();
+	skb = alloc_skb(RMNET_RX_BUFSIZE, flags);
+	if (!skb)
+		return -ENOMEM;
+
+	info = (struct timestamp_info *)skb->cb;
+	info->dev = dev;
+	info->created = created;
+
+	usb_fill_bulk_urb(rx_urb, dev->udev, dev->bulk_in,
+			  skb->data, RMNET_RX_BUFSIZE,
+			  data_bridge_read_cb, skb);
+
+	if (test_bit(SUSPENDED, &dev->flags))
+		goto suspended;
+
+	usb_anchor_urb(rx_urb, &dev->rx_active);
+	info->rx_queued = get_timestamp();
+	retval = usb_submit_urb(rx_urb, flags);
+	if (retval)
+		goto fail;
+
+	usb_mark_last_busy(dev->udev);
+	return 0;
+fail:
+	usb_unanchor_urb(rx_urb);
+suspended:
+	dev_kfree_skb_any(skb);
+
+	return retval;
+}
+
+static int data_bridge_prepare_rx(struct data_bridge *dev)
+{
+	int		i;
+	struct urb	*rx_urb;
+
+	for (i = 0; i < max_rx_urbs; i++) {
+		rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!rx_urb)
+			return -ENOMEM;
+
+		list_add_tail(&rx_urb->urb_list, &dev->rx_idle);
+	}
+	 return 0;
+}
+
+int data_bridge_open(struct bridge *brdg)
+{
+	struct data_bridge	*dev;
+
+	if (!brdg) {
+		err("bridge is null\n");
+		return -EINVAL;
+	}
+
+	if (brdg->ch_id >= MAX_BRIDGE_DEVICES)
+		return -EINVAL;
+
+	dev = __dev[brdg->ch_id];
+	if (!dev) {
+		err("dev is null\n");
+		return -ENODEV;
+	}
+
+	dev_dbg(&dev->udev->dev, "%s: dev:%p\n", __func__, dev);
+
+	dev->brdg = brdg;
+	dev->err = 0;
+	atomic_set(&dev->pending_txurbs, 0);
+	dev->to_host = 0;
+	dev->to_modem = 0;
+	dev->txurb_drp_cnt = 0;
+	dev->tx_throttled_cnt = 0;
+	dev->tx_unthrottled_cnt = 0;
+	dev->rx_throttled_cnt = 0;
+	dev->rx_unthrottled_cnt = 0;
+
+	queue_work(dev->wq, &dev->process_rx_w);
+
+	return 0;
+}
+EXPORT_SYMBOL(data_bridge_open);
+
+void data_bridge_close(unsigned int id)
+{
+	struct data_bridge	*dev;
+	struct sk_buff		*skb;
+	unsigned long		flags;
+
+	if (id >= MAX_BRIDGE_DEVICES)
+		return;
+
+	dev  = __dev[id];
+	if (!dev || !dev->brdg)
+		return;
+
+	dev_dbg(&dev->udev->dev, "%s:\n", __func__);
+
+	usb_unlink_anchored_urbs(&dev->tx_active);
+	usb_unlink_anchored_urbs(&dev->rx_active);
+	usb_unlink_anchored_urbs(&dev->delayed);
+
+	spin_lock_irqsave(&dev->rx_done.lock, flags);
+	while ((skb = __skb_dequeue(&dev->rx_done)))
+		dev_kfree_skb_any(skb);
+	spin_unlock_irqrestore(&dev->rx_done.lock, flags);
+
+	dev->brdg = NULL;
+}
+EXPORT_SYMBOL(data_bridge_close);
+
+static void defer_kevent(struct work_struct *work)
+{
+	int			status;
+	struct data_bridge	*dev =
+		container_of(work, struct data_bridge, kevent);
+
+	if (!dev)
+		return;
+
+	if (test_bit(TX_HALT, &dev->flags)) {
+		usb_unlink_anchored_urbs(&dev->tx_active);
+
+		status = usb_autopm_get_interface(dev->intf);
+		if (status < 0) {
+			dev_err(&dev->udev->dev,
+				"can't acquire interface, status %d\n", status);
+			return;
+		}
+
+		status = usb_clear_halt(dev->udev, dev->bulk_out);
+		usb_autopm_put_interface(dev->intf);
+		if (status < 0 && status != -EPIPE && status != -ESHUTDOWN)
+			dev_err(&dev->udev->dev,
+				"can't clear tx halt, status %d\n", status);
+		else
+			clear_bit(TX_HALT, &dev->flags);
+	}
+
+	if (test_bit(RX_HALT, &dev->flags)) {
+		usb_unlink_anchored_urbs(&dev->rx_active);
+
+		status = usb_autopm_get_interface(dev->intf);
+		if (status < 0) {
+			dev_err(&dev->udev->dev,
+				"can't acquire interface, status %d\n", status);
+			return;
+		}
+
+		status = usb_clear_halt(dev->udev, dev->bulk_in);
+		usb_autopm_put_interface(dev->intf);
+		if (status < 0 && status != -EPIPE && status != -ESHUTDOWN)
+			dev_err(&dev->udev->dev,
+				"can't clear rx halt, status %d\n", status);
+		else {
+			clear_bit(RX_HALT, &dev->flags);
+			if (dev->brdg)
+				queue_work(dev->wq, &dev->process_rx_w);
+		}
+	}
+}
+
+static void data_bridge_write_cb(struct urb *urb)
+{
+	struct sk_buff		*skb = urb->context;
+	struct timestamp_info	*info = (struct timestamp_info *)skb->cb;
+	struct data_bridge	*dev = info->dev;
+	struct bridge		*brdg = dev->brdg;
+	int			pending;
+
+	pr_debug("%s: dev:%p\n", __func__, dev);
+
+	switch (urb->status) {
+	case 0: /*success*/
+		dbg_timestamp("UL", skb);
+		break;
+	case -EPROTO:
+		dev->err = -EPROTO;
+		break;
+	case -EPIPE:
+		set_bit(TX_HALT, &dev->flags);
+		dev_err(&dev->udev->dev, "%s: epout halted\n", __func__);
+		schedule_work(&dev->kevent);
+		/* FALLTHROUGH */
+	case -ESHUTDOWN:
+	case -ENOENT: /* suspended */
+	case -ECONNRESET: /* unplug */
+	case -EOVERFLOW: /*babble error*/
+		/* FALLTHROUGH */
+	default:
+		pr_debug_ratelimited("%s: non zero urb status = %d\n",
+					__func__, urb->status);
+	}
+
+	usb_free_urb(urb);
+	dev_kfree_skb_any(skb);
+
+	pending = atomic_dec_return(&dev->pending_txurbs);
+
+	/*flow ctrl*/
+	if (brdg && fctrl_support && pending <= fctrl_dis_thld &&
+		test_and_clear_bit(TX_THROTTLED, &brdg->flags)) {
+		pr_debug_ratelimited("%s: disable flow ctrl: pend urbs:%u\n",
+			__func__, pending);
+		dev->tx_unthrottled_cnt++;
+		if (brdg->ops.unthrottle_tx)
+			brdg->ops.unthrottle_tx(brdg->ctx);
+	}
+
+	usb_autopm_put_interface_async(dev->intf);
+}
+
+int data_bridge_write(unsigned int id, struct sk_buff *skb)
+{
+	int			result;
+	int			size = skb->len;
+	int			pending;
+	struct urb		*txurb;
+	struct timestamp_info	*info = (struct timestamp_info *)skb->cb;
+	struct data_bridge	*dev = __dev[id];
+	struct bridge		*brdg;
+
+	if (!dev || !dev->brdg || dev->err || !usb_get_intfdata(dev->intf))
+		return -ENODEV;
+
+	brdg = dev->brdg;
+	if (!brdg)
+		return -ENODEV;
+
+	dev_dbg(&dev->udev->dev, "%s: write (%d bytes)\n", __func__, skb->len);
+
+	result = usb_autopm_get_interface(dev->intf);
+	if (result < 0) {
+		dev_err(&dev->udev->dev, "%s: resume failure\n", __func__);
+		goto error;
+	}
+
+	txurb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!txurb) {
+		dev_err(&dev->udev->dev, "%s: error allocating read urb\n",
+			__func__);
+		result = -ENOMEM;
+		goto error;
+	}
+
+	/* store dev pointer in skb */
+	info->dev = dev;
+	info->tx_queued = get_timestamp();
+
+	usb_fill_bulk_urb(txurb, dev->udev, dev->bulk_out,
+			skb->data, skb->len, data_bridge_write_cb, skb);
+
+	if (test_bit(SUSPENDED, &dev->flags)) {
+		usb_anchor_urb(txurb, &dev->delayed);
+		goto free_urb;
+	}
+
+	pending = atomic_inc_return(&dev->pending_txurbs);
+	usb_anchor_urb(txurb, &dev->tx_active);
+
+	if (atomic_read(&dev->pending_txurbs) % tx_urb_mult)
+		txurb->transfer_flags |= URB_NO_INTERRUPT;
+
+	result = usb_submit_urb(txurb, GFP_KERNEL);
+	if (result < 0) {
+		usb_unanchor_urb(txurb);
+		atomic_dec(&dev->pending_txurbs);
+		dev_err(&dev->udev->dev, "%s: submit URB error %d\n",
+			__func__, result);
+		goto free_urb;
+	}
+
+	dev->to_modem++;
+	dev_dbg(&dev->udev->dev, "%s: pending_txurbs: %u\n", __func__, pending);
+
+	/* flow control: last urb submitted but return -EBUSY */
+	if (fctrl_support && pending > fctrl_en_thld) {
+		set_bit(TX_THROTTLED, &brdg->flags);
+		dev->tx_throttled_cnt++;
+		pr_debug_ratelimited("%s: enable flow ctrl pend txurbs:%u\n",
+					__func__, pending);
+		return -EBUSY;
+	}
+
+	return size;
+
+free_urb:
+	usb_free_urb(txurb);
+error:
+	dev->txurb_drp_cnt++;
+	usb_autopm_put_interface(dev->intf);
+
+	return result;
+}
+EXPORT_SYMBOL(data_bridge_write);
+
+static int data_bridge_resume(struct data_bridge *dev)
+{
+	struct urb	*urb;
+	int		retval;
+
+	if (!test_and_clear_bit(SUSPENDED, &dev->flags))
+		return 0;
+
+	while ((urb = usb_get_from_anchor(&dev->delayed))) {
+		usb_anchor_urb(urb, &dev->tx_active);
+		atomic_inc(&dev->pending_txurbs);
+		retval = usb_submit_urb(urb, GFP_ATOMIC);
+		if (retval < 0) {
+			atomic_dec(&dev->pending_txurbs);
+			usb_unanchor_urb(urb);
+
+			/* TODO: need to free urb data */
+			usb_scuttle_anchored_urbs(&dev->delayed);
+			break;
+		}
+		dev->to_modem++;
+		dev->txurb_drp_cnt--;
+	}
+
+	if (dev->brdg)
+		queue_work(dev->wq, &dev->process_rx_w);
+
+	return 0;
+}
+
+static int bridge_resume(struct usb_interface *iface)
+{
+	int			retval = 0;
+	int			oldstate;
+	struct data_bridge	*dev = usb_get_intfdata(iface);
+
+	oldstate = iface->dev.power.power_state.event;
+	iface->dev.power.power_state.event = PM_EVENT_ON;
+
+	if (oldstate & PM_EVENT_SUSPEND) {
+		retval = data_bridge_resume(dev);
+		if (!retval)
+			retval = ctrl_bridge_resume(dev->id);
+	}
+
+	return retval;
+}
+
+static int data_bridge_suspend(struct data_bridge *dev, pm_message_t message)
+{
+	if (atomic_read(&dev->pending_txurbs) &&
+		(message.event & PM_EVENT_AUTO))
+		return -EBUSY;
+
+	set_bit(SUSPENDED, &dev->flags);
+
+	usb_kill_anchored_urbs(&dev->tx_active);
+	usb_kill_anchored_urbs(&dev->rx_active);
+
+	return 0;
+}
+
+static int bridge_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	int			retval;
+	struct data_bridge	*dev = usb_get_intfdata(intf);
+
+	retval = data_bridge_suspend(dev, message);
+	if (!retval) {
+		retval = ctrl_bridge_suspend(dev->id);
+		intf->dev.power.power_state.event = message.event;
+	}
+
+	return retval;
+}
+
+static int data_bridge_probe(struct usb_interface *iface,
+		struct usb_host_endpoint *bulk_in,
+		struct usb_host_endpoint *bulk_out, int id)
+{
+	struct data_bridge	*dev;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev) {
+		err("%s: unable to allocate dev\n", __func__);
+		return -ENOMEM;
+	}
+
+	dev->pdev = platform_device_alloc(data_bridge_names[id], id);
+	if (!dev->pdev) {
+		err("%s: unable to allocate platform device\n", __func__);
+		kfree(dev);
+		return -ENOMEM;
+	}
+
+	init_usb_anchor(&dev->tx_active);
+	init_usb_anchor(&dev->rx_active);
+	init_usb_anchor(&dev->delayed);
+
+	INIT_LIST_HEAD(&dev->rx_idle);
+	skb_queue_head_init(&dev->rx_done);
+
+	dev->wq = bridge_wq;
+	dev->id = id;
+	dev->udev = interface_to_usbdev(iface);
+	dev->intf = iface;
+
+	dev->bulk_in = usb_rcvbulkpipe(dev->udev,
+		bulk_in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+
+	dev->bulk_out = usb_sndbulkpipe(dev->udev,
+		bulk_out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+
+	usb_set_intfdata(iface, dev);
+
+	INIT_WORK(&dev->kevent, defer_kevent);
+	INIT_WORK(&dev->process_rx_w, data_bridge_process_rx);
+
+	__dev[id] = dev;
+
+	/*allocate list of rx urbs*/
+	data_bridge_prepare_rx(dev);
+
+	platform_device_add(dev->pdev);
+
+	return 0;
+}
+
+#if defined(CONFIG_DEBUG_FS)
+#define DEBUG_BUF_SIZE	1024
+
+static unsigned int	record_timestamp;
+module_param(record_timestamp, uint, S_IRUGO | S_IWUSR);
+
+static struct timestamp_buf dbg_data = {
+	.idx = 0,
+	.lck = __RW_LOCK_UNLOCKED(lck)
+};
+
+/*get_timestamp - returns time of day in us */
+static unsigned int get_timestamp(void)
+{
+	struct timeval	tval;
+	unsigned int	stamp;
+
+	if (!record_timestamp)
+		return 0;
+
+	do_gettimeofday(&tval);
+	/* 2^32 = 4294967296. Limit to 4096s. */
+	stamp = tval.tv_sec & 0xFFF;
+	stamp = stamp * 1000000 + tval.tv_usec;
+	return stamp;
+}
+
+static void dbg_inc(unsigned *idx)
+{
+	*idx = (*idx + 1) & (DBG_DATA_MAX-1);
+}
+
+/**
+* dbg_timestamp - Stores timestamp values of a SKB life cycle
+*	to debug buffer
+* @event: "UL": Uplink Data
+* @skb: SKB used to store timestamp values to debug buffer
+*/
+static void dbg_timestamp(char *event, struct sk_buff * skb)
+{
+	unsigned long		flags;
+	struct timestamp_info	*info = (struct timestamp_info *)skb->cb;
+
+	if (!record_timestamp)
+		return;
+
+	write_lock_irqsave(&dbg_data.lck, flags);
+
+	scnprintf(dbg_data.buf[dbg_data.idx], DBG_DATA_MSG,
+		  "%p %u[%s] %u %u %u %u %u %u\n",
+		  skb, skb->len, event, info->created, info->rx_queued,
+		  info->rx_done, info->rx_done_sent, info->tx_queued,
+		  get_timestamp());
+
+	dbg_inc(&dbg_data.idx);
+
+	write_unlock_irqrestore(&dbg_data.lck, flags);
+}
+
+/* show_timestamp: displays the timestamp buffer */
+static ssize_t show_timestamp(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	unsigned long	flags;
+	unsigned	i;
+	unsigned	j = 0;
+	char		*buf;
+	int		ret = 0;
+
+	if (!record_timestamp)
+		return 0;
+
+	buf = kzalloc(sizeof(char) * 4 * DEBUG_BUF_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	read_lock_irqsave(&dbg_data.lck, flags);
+
+	i = dbg_data.idx;
+	for (dbg_inc(&i); i != dbg_data.idx; dbg_inc(&i)) {
+		if (!strnlen(dbg_data.buf[i], DBG_DATA_MSG))
+			continue;
+		j += scnprintf(buf + j, (4 * DEBUG_BUF_SIZE) - j,
+			       "%s\n", dbg_data.buf[i]);
+	}
+
+	read_unlock_irqrestore(&dbg_data.lck, flags);
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, j);
+
+	kfree(buf);
+
+	return ret;
+}
+
+const struct file_operations data_timestamp_ops = {
+	.read = show_timestamp,
+};
+
+static ssize_t data_bridge_read_stats(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	struct data_bridge	*dev;
+	char			*buf;
+	int			ret;
+	int			i;
+	int			temp = 0;
+
+	buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	for (i = 0; i < ch_id; i++) {
+		dev = __dev[i];
+		if (!dev)
+			continue;
+
+		temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+				"\nName#%s dev %p\n"
+				"pending tx urbs:    %u\n"
+				"tx urb drp cnt:     %u\n"
+				"to host:            %lu\n"
+				"to mdm:             %lu\n"
+				"tx throttled cnt:   %u\n"
+				"tx unthrottled cnt: %u\n"
+				"rx throttled cnt:   %u\n"
+				"rx unthrottled cnt: %u\n"
+				"rx done skb qlen:   %u\n"
+				"dev err:            %d\n"
+				"suspended:          %d\n"
+				"TX_HALT:            %d\n"
+				"RX_HALT:            %d\n",
+				dev->pdev->name, dev,
+				atomic_read(&dev->pending_txurbs),
+				dev->txurb_drp_cnt,
+				dev->to_host,
+				dev->to_modem,
+				dev->tx_throttled_cnt,
+				dev->tx_unthrottled_cnt,
+				dev->rx_throttled_cnt,
+				dev->rx_unthrottled_cnt,
+				dev->rx_done.qlen,
+				dev->err,
+				test_bit(SUSPENDED, &dev->flags),
+				test_bit(TX_HALT, &dev->flags),
+				test_bit(RX_HALT, &dev->flags));
+
+	}
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+	kfree(buf);
+
+	return ret;
+}
+
+static ssize_t data_bridge_reset_stats(struct file *file,
+	const char __user *buf, size_t count, loff_t *ppos)
+{
+	struct data_bridge	*dev;
+	int			i;
+
+	for (i = 0; i < ch_id; i++) {
+		dev = __dev[i];
+		if (!dev)
+			continue;
+
+		dev->to_host = 0;
+		dev->to_modem = 0;
+		dev->txurb_drp_cnt = 0;
+		dev->tx_throttled_cnt = 0;
+		dev->tx_unthrottled_cnt = 0;
+		dev->rx_throttled_cnt = 0;
+		dev->rx_unthrottled_cnt = 0;
+	}
+	return count;
+}
+
+const struct file_operations data_stats_ops = {
+	.read = data_bridge_read_stats,
+	.write = data_bridge_reset_stats,
+};
+
+static struct dentry	*data_dent;
+static struct dentry	*data_dfile_stats;
+static struct dentry	*data_dfile_tstamp;
+
+static void data_bridge_debugfs_init(void)
+{
+	data_dent = debugfs_create_dir("data_hsic_bridge", 0);
+	if (IS_ERR(data_dent))
+		return;
+
+	data_dfile_stats = debugfs_create_file("status", 0644, data_dent, 0,
+				&data_stats_ops);
+	if (!data_dfile_stats || IS_ERR(data_dfile_stats)) {
+		debugfs_remove(data_dent);
+		return;
+	}
+
+	data_dfile_tstamp = debugfs_create_file("timestamp", 0644, data_dent,
+				0, &data_timestamp_ops);
+	if (!data_dfile_tstamp || IS_ERR(data_dfile_tstamp))
+		debugfs_remove(data_dent);
+}
+
+static void data_bridge_debugfs_exit(void)
+{
+	debugfs_remove(data_dfile_stats);
+	debugfs_remove(data_dfile_tstamp);
+	debugfs_remove(data_dent);
+}
+
+#else
+static void data_bridge_debugfs_init(void) { }
+static void data_bridge_debugfs_exit(void) { }
+static void dbg_timestamp(char *event, struct sk_buff * skb)
+{
+	return;
+}
+
+static unsigned int get_timestamp(void)
+{
+	return 0;
+}
+
+#endif
+
+static int __devinit
+bridge_probe(struct usb_interface *iface, const struct usb_device_id *id)
+{
+	struct usb_host_endpoint	*endpoint = NULL;
+	struct usb_host_endpoint	*bulk_in = NULL;
+	struct usb_host_endpoint	*bulk_out = NULL;
+	struct usb_host_endpoint	*int_in = NULL;
+	struct usb_device		*udev;
+	int				i;
+	int				status = 0;
+	int				numends;
+	unsigned int			iface_num;
+
+	iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
+
+	if (iface->num_altsetting != 1) {
+		err("%s invalid num_altsetting %u\n",
+				__func__, iface->num_altsetting);
+		return -EINVAL;
+	}
+
+	udev = interface_to_usbdev(iface);
+	usb_get_dev(udev);
+
+	if (!test_bit(iface_num, &id->driver_info))
+		return -ENODEV;
+
+	numends = iface->cur_altsetting->desc.bNumEndpoints;
+	for (i = 0; i < numends; i++) {
+		endpoint = iface->cur_altsetting->endpoint + i;
+		if (!endpoint) {
+			dev_err(&udev->dev, "%s: invalid endpoint %u\n",
+					__func__, i);
+			status = -EINVAL;
+			goto out;
+		}
+
+		if (usb_endpoint_is_bulk_in(&endpoint->desc))
+			bulk_in = endpoint;
+		else if (usb_endpoint_is_bulk_out(&endpoint->desc))
+			bulk_out = endpoint;
+		else if (usb_endpoint_is_int_in(&endpoint->desc))
+			int_in = endpoint;
+	}
+
+	if (!bulk_in || !bulk_out || !int_in) {
+		dev_err(&udev->dev, "%s: invalid endpoints\n", __func__);
+		status = -EINVAL;
+		goto out;
+	}
+
+	status = data_bridge_probe(iface, bulk_in, bulk_out, ch_id);
+	if (status < 0) {
+		dev_err(&udev->dev, "data_bridge_probe failed %d\n", status);
+		goto out;
+	}
+
+	status = ctrl_bridge_probe(iface, int_in, ch_id);
+	if (status < 0) {
+		dev_err(&udev->dev, "ctrl_bridge_probe failed %d\n", status);
+		goto free_data_bridge;
+	}
+
+	ch_id++;
+
+	return 0;
+
+free_data_bridge:
+	platform_device_del(__dev[ch_id]->pdev);
+	usb_set_intfdata(iface, NULL);
+	kfree(__dev[ch_id]);
+	__dev[ch_id] = NULL;
+out:
+	usb_put_dev(udev);
+
+	return status;
+}
+
+static void bridge_disconnect(struct usb_interface *intf)
+{
+	struct data_bridge	*dev = usb_get_intfdata(intf);
+	struct list_head	*head;
+	struct urb		*rx_urb;
+	unsigned long		flags;
+
+	if (!dev) {
+		err("%s: data device not found\n", __func__);
+		return;
+	}
+
+	ch_id--;
+	ctrl_bridge_disconnect(ch_id);
+	platform_device_del(dev->pdev);
+	usb_set_intfdata(intf, NULL);
+	__dev[ch_id] = NULL;
+
+	cancel_work_sync(&dev->process_rx_w);
+	cancel_work_sync(&dev->kevent);
+
+	/*free rx urbs*/
+	head = &dev->rx_idle;
+	spin_lock_irqsave(&dev->rx_done.lock, flags);
+	while (!list_empty(head)) {
+		rx_urb = list_entry(head->next, struct urb, urb_list);
+		list_del(&rx_urb->urb_list);
+		usb_free_urb(rx_urb);
+	}
+	spin_unlock_irqrestore(&dev->rx_done.lock, flags);
+
+	usb_put_dev(dev->udev);
+	kfree(dev);
+}
+
+/*bit position represents interface number*/
+#define PID9001_IFACE_MASK	0xC
+#define PID9034_IFACE_MASK	0xC
+#define PID9048_IFACE_MASK	0x18
+#define PID904C_IFACE_MASK	0x28
+
+static const struct usb_device_id bridge_ids[] = {
+	{ USB_DEVICE(0x5c6, 0x9001),
+	.driver_info = PID9001_IFACE_MASK,
+	},
+	{ USB_DEVICE(0x5c6, 0x9034),
+	.driver_info = PID9034_IFACE_MASK,
+	},
+	{ USB_DEVICE(0x5c6, 0x9048),
+	.driver_info = PID9048_IFACE_MASK,
+	},
+	{ USB_DEVICE(0x5c6, 0x904c),
+	.driver_info = PID904C_IFACE_MASK,
+	},
+
+	{ } /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, bridge_ids);
+
+static struct usb_driver bridge_driver = {
+	.name =			"mdm_bridge",
+	.probe =		bridge_probe,
+	.disconnect =		bridge_disconnect,
+	.id_table =		bridge_ids,
+	.suspend =		bridge_suspend,
+	.resume =		bridge_resume,
+	.supports_autosuspend =	1,
+};
+
+static int __init bridge_init(void)
+{
+	int	ret;
+
+	ret = usb_register(&bridge_driver);
+	if (ret) {
+		err("%s: unable to register mdm_bridge driver", __func__);
+		return ret;
+	}
+
+	bridge_wq  = create_singlethread_workqueue("mdm_bridge");
+	if (!bridge_wq) {
+		usb_deregister(&bridge_driver);
+		pr_err("%s: Unable to create workqueue:bridge\n", __func__);
+		return -ENOMEM;
+	}
+
+	data_bridge_debugfs_init();
+
+	return 0;
+}
+
+static void __exit bridge_exit(void)
+{
+	data_bridge_debugfs_exit();
+	destroy_workqueue(bridge_wq);
+	usb_deregister(&bridge_driver);
+}
+
+module_init(bridge_init);
+module_exit(bridge_exit);
+
+MODULE_DESCRIPTION("Qualcomm modem data bridge driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig
index c2902a8..e6c823d 100644
--- a/drivers/usb/otg/Kconfig
+++ b/drivers/usb/otg/Kconfig
@@ -74,6 +74,34 @@
 	  This transceiver supports high and full speed devices plus,
 	  in host mode, low speed.
 
+config USB_MSM_OTG_72K
+	bool "OTG support for Legcay Qualcomm on-chip USB controller"
+	depends on ARCH_MSM
+	select USB_OTG_UTILS
+	default USB_MSM_72K
+	help
+	  Enable this to support the USB OTG transceiver on MSM chips. It
+	  handles PHY initialization, clock management, low power mode and
+	  workarounds required after resetting the hardware. This driver is
+	  required for even peripheral only or host only mode configuration.
+	  Supports SRP and HNP when both gadget and Host are selected.
+
+config MSM_OTG_ENABLE_A_WAIT_BCON_TIMEOUT
+	bool "Enable A-device timeout for B-device connection"
+	depends on USB_MSM_OTG_72K
+	default n
+	help
+	   OTG specification allows A-device to turn off VBUS if B-device
+	   fails to signal connect event before TA_WAIT_BCON (1.1 - 30 sec).
+	   SRP detection is enabled and hardware is put into low power mode
+	   upon this timeout.
+
+	   If you say yes, VBUS will be turned off if B-device does not signal
+	   connect in 30 sec. Otherwise VBUS is not turned off when Micro-A
+	   cable is connected. But hardware is put into LPM. Say no if leakage
+	   currents in your system are minimum.
+
+
 config TWL6030_USB
 	tristate "TWL6030 USB Transceiver Driver"
 	depends on TWL4030_CORE
@@ -107,6 +135,25 @@
 	  This driver is not supported on boards like trout which
 	  has an external PHY.
 
+config USB_MSM_ACA
+	bool "Support for Accessory Charger Adapter (ACA)"
+	depends on (USB_MSM_OTG || USB_MSM_OTG_72K) && ARCH_MSM
+	default n
+	help
+	  Accesory Charger Adapter is a charger specified in USB Battery
+	  Charging Specification(1.1). It enables OTG devices to charge
+	  while operating as a host or peripheral at the same time.
+
+config USB_MSM_STANDARD_ACA
+	bool "Support for Standard ACA"
+	depends on USB_MSM_ACA
+	default USB_MSM_OTG_72K
+	help
+	  A Standard ACA has a Standard-A receptacle on the Accessory Port,
+	  and can only be attached to a B-device.  RID_A and RID_GND states
+	  are only possible with Standard ACA.  Select this feature if the
+	  board is intended to support only Standard ACA.
+
 config AB8500_USB
 	tristate "AB8500 USB Transceiver Driver"
 	depends on AB8500_CORE
diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile
index 638d040..5afb02e 100644
--- a/drivers/usb/otg/Makefile
+++ b/drivers/usb/otg/Makefile
@@ -17,6 +17,7 @@
 obj-$(CONFIG_NOP_USB_XCEIV)	+= nop-usb-xceiv.o
 obj-$(CONFIG_USB_ULPI)		+= ulpi.o
 obj-$(CONFIG_USB_ULPI_VIEWPORT)	+= ulpi_viewport.o
+obj-$(CONFIG_USB_MSM_OTG_72K)	+= msm72k_otg.o
 obj-$(CONFIG_USB_MSM_OTG)	+= msm_otg.o
 obj-$(CONFIG_AB8500_USB)	+= ab8500-usb.o
 fsl_usb2_otg-objs		:= fsl_otg.o otg_fsm.o
diff --git a/drivers/usb/otg/msm72k_otg.c b/drivers/usb/otg/msm72k_otg.c
new file mode 100644
index 0000000..f62ae76
--- /dev/null
+++ b/drivers/usb/otg/msm72k_otg.c
@@ -0,0 +1,3036 @@
+/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/pm_runtime.h>
+
+#include <linux/device.h>
+#include <linux/pm_qos.h>
+#include <mach/msm_hsusb_hw.h>
+#include <mach/msm72k_otg.h>
+#include <mach/msm_hsusb.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <mach/clk.h>
+#include <mach/msm_xo.h>
+
+#define MSM_USB_BASE	(dev->regs)
+#define USB_LINK_RESET_TIMEOUT	(msecs_to_jiffies(10))
+#define DRIVER_NAME	"msm_otg"
+static void otg_reset(struct usb_phy *phy, int phy_reset);
+static void msm_otg_set_vbus_state(int online);
+#ifdef CONFIG_USB_EHCI_MSM_72K
+static void msm_otg_set_id_state(int id);
+#else
+static void msm_otg_set_id_state(int id)
+{
+}
+#endif
+
+struct msm_otg *the_msm_otg;
+
+static int is_host(void)
+{
+	struct msm_otg *dev = the_msm_otg;
+
+	if (dev->pdata->otg_mode == OTG_ID)
+		return (OTGSC_ID & readl(USB_OTGSC)) ? 0 : 1;
+	else
+		return !test_bit(ID, &dev->inputs);
+}
+
+static int is_b_sess_vld(void)
+{
+	struct msm_otg *dev = the_msm_otg;
+
+	if (dev->pdata->otg_mode == OTG_ID)
+		return (OTGSC_BSV & readl(USB_OTGSC)) ? 1 : 0;
+	else
+		return test_bit(B_SESS_VLD, &dev->inputs);
+}
+
+static unsigned ulpi_read(struct msm_otg *dev, unsigned reg)
+{
+	unsigned ret, timeout = 100000;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	/* initiate read operation */
+	writel(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg),
+	       USB_ULPI_VIEWPORT);
+
+	/* wait for completion */
+	while ((readl(USB_ULPI_VIEWPORT) & ULPI_RUN) && (--timeout))
+		cpu_relax();
+
+	if (timeout == 0) {
+		pr_err("%s: timeout %08x\n", __func__,
+				 readl(USB_ULPI_VIEWPORT));
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return 0xffffffff;
+	}
+	ret = ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT));
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return ret;
+}
+
+static int ulpi_write(struct msm_otg *dev, unsigned val, unsigned reg)
+{
+	unsigned timeout = 10000;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	/* initiate write operation */
+	writel(ULPI_RUN | ULPI_WRITE |
+	       ULPI_ADDR(reg) | ULPI_DATA(val),
+	       USB_ULPI_VIEWPORT);
+
+	/* wait for completion */
+	while ((readl(USB_ULPI_VIEWPORT) & ULPI_RUN) && (--timeout))
+		;
+
+	if (timeout == 0) {
+		pr_err("%s: timeout\n", __func__);
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return -1;
+	}
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return 0;
+}
+
+static int usb_ulpi_write(struct usb_phy *xceiv, u32 val, u32 reg)
+{
+	struct msm_otg *dev = container_of(xceiv, struct msm_otg, phy);
+
+	return ulpi_write(dev, val, reg);
+}
+
+static int usb_ulpi_read(struct usb_phy *xceiv, u32 reg)
+{
+	struct msm_otg *dev = container_of(xceiv, struct msm_otg, phy);
+
+	return ulpi_read(dev, reg);
+}
+
+#ifdef CONFIG_USB_EHCI_MSM_72K
+static void enable_idgnd(struct msm_otg *dev)
+{
+	unsigned temp;
+
+	/* Do nothing if instead of ID pin, USER controls mode switch */
+	if (dev->pdata->otg_mode == OTG_USER_CONTROL)
+		return;
+
+	ulpi_write(dev, (1<<4), 0x0E);
+	ulpi_write(dev, (1<<4), 0x11);
+	ulpi_write(dev, (1<<0), 0x0B);
+	temp = OTGSC_IDIE | OTGSC_IDPU;
+	writel_relaxed(readl_relaxed(USB_OTGSC) | temp, USB_OTGSC);
+}
+
+static void disable_idgnd(struct msm_otg *dev)
+{
+	unsigned temp;
+
+	/* Do nothing if instead of ID pin, USER controls mode switch */
+	if (dev->pdata->otg_mode == OTG_USER_CONTROL)
+		return;
+	temp = OTGSC_IDIE | OTGSC_IDPU;
+	writel_relaxed(readl_relaxed(USB_OTGSC) & ~temp, USB_OTGSC);
+	ulpi_write(dev, (1<<4), 0x0F);
+	ulpi_write(dev, (1<<4), 0x12);
+	ulpi_write(dev, (1<<0), 0x0C);
+}
+#else
+static void enable_idgnd(struct msm_otg *dev)
+{
+}
+static void disable_idgnd(struct msm_otg *dev)
+{
+}
+#endif
+
+static void enable_idabc(struct msm_otg *dev)
+{
+#ifdef CONFIG_USB_MSM_ACA
+	ulpi_write(dev, (1<<5), 0x0E);
+	ulpi_write(dev, (1<<5), 0x11);
+#endif
+}
+static void disable_idabc(struct msm_otg *dev)
+{
+#ifdef CONFIG_USB_MSM_ACA
+	ulpi_write(dev, (1<<5), 0x0F);
+	ulpi_write(dev, (1<<5), 0x12);
+#endif
+}
+
+static void enable_sess_valid(struct msm_otg *dev)
+{
+	/* Do nothing if instead of ID pin, USER controls mode switch */
+	if (dev->pdata->otg_mode == OTG_USER_CONTROL)
+		return;
+
+	ulpi_write(dev, (1<<2), 0x0E);
+	ulpi_write(dev, (1<<2), 0x11);
+	writel(readl(USB_OTGSC) | OTGSC_BSVIE, USB_OTGSC);
+}
+
+static void disable_sess_valid(struct msm_otg *dev)
+{
+	/* Do nothing if instead of ID pin, USER controls mode switch */
+	if (dev->pdata->otg_mode == OTG_USER_CONTROL)
+		return;
+
+	ulpi_write(dev, (1<<2), 0x0F);
+	ulpi_write(dev, (1<<2), 0x12);
+	writel(readl(USB_OTGSC) & ~OTGSC_BSVIE, USB_OTGSC);
+}
+#ifdef CONFIG_USB_MSM_ACA
+static void set_aca_id_inputs(struct msm_otg *dev)
+{
+	u8		phy_ints;
+
+	phy_ints = ulpi_read(dev, 0x13);
+	if (phy_ints == -ETIMEDOUT)
+		return;
+
+	pr_debug("phy_ints = %x\n", phy_ints);
+	clear_bit(ID_A, &dev->inputs);
+	clear_bit(ID_B, &dev->inputs);
+	clear_bit(ID_C, &dev->inputs);
+	if (phy_id_state_a(phy_ints)) {
+		pr_debug("ID_A set\n");
+		set_bit(ID_A, &dev->inputs);
+		set_bit(A_BUS_REQ, &dev->inputs);
+	} else if (phy_id_state_b(phy_ints)) {
+		pr_debug("ID_B set\n");
+		set_bit(ID_B, &dev->inputs);
+	} else if (phy_id_state_c(phy_ints)) {
+		pr_debug("ID_C set\n");
+		set_bit(ID_C, &dev->inputs);
+	}
+	if (is_b_sess_vld())
+		set_bit(B_SESS_VLD, &dev->inputs);
+	else
+		clear_bit(B_SESS_VLD, &dev->inputs);
+}
+#define get_aca_bmaxpower(dev)		(dev->b_max_power)
+#define set_aca_bmaxpower(dev, power)	(dev->b_max_power = power)
+#else
+static void set_aca_id_inputs(struct msm_otg *dev)
+{
+}
+#define get_aca_bmaxpower(dev)		0
+#define set_aca_bmaxpower(dev, power)
+#endif
+static inline void set_pre_emphasis_level(struct msm_otg *dev)
+{
+	unsigned res = 0;
+
+	if (!dev->pdata || dev->pdata->pemp_level == PRE_EMPHASIS_DEFAULT)
+		return;
+
+	res = ulpi_read(dev, ULPI_CONFIG_REG3);
+	res &= ~(ULPI_PRE_EMPHASIS_MASK);
+	if (dev->pdata->pemp_level != PRE_EMPHASIS_DISABLE)
+		res |= dev->pdata->pemp_level;
+	ulpi_write(dev, res, ULPI_CONFIG_REG3);
+}
+
+static inline void set_hsdrv_slope(struct msm_otg *dev)
+{
+	unsigned res = 0;
+
+	if (!dev->pdata || dev->pdata->hsdrvslope == HS_DRV_SLOPE_DEFAULT)
+		return;
+
+	res = ulpi_read(dev, ULPI_CONFIG_REG3);
+	res &= ~(ULPI_HSDRVSLOPE_MASK);
+	res |= (dev->pdata->hsdrvslope & ULPI_HSDRVSLOPE_MASK);
+	ulpi_write(dev, res, ULPI_CONFIG_REG3);
+}
+
+static inline void set_cdr_auto_reset(struct msm_otg *dev)
+{
+	unsigned res = 0;
+
+	if (!dev->pdata || dev->pdata->cdr_autoreset == CDR_AUTO_RESET_DEFAULT)
+		return;
+
+	res = ulpi_read(dev, ULPI_DIGOUT_CTRL);
+	if (dev->pdata->cdr_autoreset == CDR_AUTO_RESET_ENABLE)
+		res &=  ~ULPI_CDR_AUTORESET;
+	else
+		res |=  ULPI_CDR_AUTORESET;
+	ulpi_write(dev, res, ULPI_DIGOUT_CTRL);
+}
+
+static inline void set_se1_gating(struct msm_otg *dev)
+{
+	unsigned res = 0;
+
+	if (!dev->pdata || dev->pdata->se1_gating == SE1_GATING_DEFAULT)
+		return;
+
+	res = ulpi_read(dev, ULPI_DIGOUT_CTRL);
+	if (dev->pdata->se1_gating == SE1_GATING_ENABLE)
+		res &=  ~ULPI_SE1_GATE;
+	else
+		res |=  ULPI_SE1_GATE;
+	ulpi_write(dev, res, ULPI_DIGOUT_CTRL);
+}
+static inline void set_driver_amplitude(struct msm_otg *dev)
+{
+	unsigned res = 0;
+
+	if (!dev->pdata || dev->pdata->drv_ampl == HS_DRV_AMPLITUDE_DEFAULT)
+		return;
+
+	res = ulpi_read(dev, ULPI_CONFIG_REG2);
+	res &= ~ULPI_DRV_AMPL_MASK;
+	if (dev->pdata->drv_ampl != HS_DRV_AMPLITUDE_ZERO_PERCENT)
+		res |= dev->pdata->drv_ampl;
+	ulpi_write(dev, res, ULPI_CONFIG_REG2);
+}
+
+static const char *state_string(enum usb_otg_state state)
+{
+	switch (state) {
+	case OTG_STATE_A_IDLE:		return "a_idle";
+	case OTG_STATE_A_WAIT_VRISE:	return "a_wait_vrise";
+	case OTG_STATE_A_WAIT_BCON:	return "a_wait_bcon";
+	case OTG_STATE_A_HOST:		return "a_host";
+	case OTG_STATE_A_SUSPEND:	return "a_suspend";
+	case OTG_STATE_A_PERIPHERAL:	return "a_peripheral";
+	case OTG_STATE_A_WAIT_VFALL:	return "a_wait_vfall";
+	case OTG_STATE_A_VBUS_ERR:	return "a_vbus_err";
+	case OTG_STATE_B_IDLE:		return "b_idle";
+	case OTG_STATE_B_SRP_INIT:	return "b_srp_init";
+	case OTG_STATE_B_PERIPHERAL:	return "b_peripheral";
+	case OTG_STATE_B_WAIT_ACON:	return "b_wait_acon";
+	case OTG_STATE_B_HOST:		return "b_host";
+	default:			return "UNDEFINED";
+	}
+}
+
+static const char *timer_string(int bit)
+{
+	switch (bit) {
+	case A_WAIT_VRISE:		return "a_wait_vrise";
+	case A_WAIT_VFALL:		return "a_wait_vfall";
+	case B_SRP_FAIL:		return "b_srp_fail";
+	case A_WAIT_BCON:		return "a_wait_bcon";
+	case A_AIDL_BDIS:		return "a_aidl_bdis";
+	case A_BIDL_ADIS:		return "a_bidl_adis";
+	case B_ASE0_BRST:		return "b_ase0_brst";
+	default:			return "UNDEFINED";
+	}
+}
+
+/* Prevent idle power collapse(pc) while operating in peripheral mode */
+static void otg_pm_qos_update_latency(struct msm_otg *dev, int vote)
+{
+	struct msm_otg_platform_data *pdata = dev->pdata;
+	u32 swfi_latency = 0;
+
+	if (pdata)
+		swfi_latency = pdata->swfi_latency + 1;
+
+	if (vote)
+		pm_qos_update_request(&pdata->pm_qos_req_dma,
+				swfi_latency);
+	else
+		pm_qos_update_request(&pdata->pm_qos_req_dma,
+				PM_QOS_DEFAULT_VALUE);
+}
+
+/* Controller gives interrupt for every 1 mesc if 1MSIE is set in OTGSC.
+ * This interrupt can be used as a timer source and OTG timers can be
+ * implemented. But hrtimers on MSM hardware can give atleast 1/32 KHZ
+ * precision. This precision is more than enough for OTG timers.
+ */
+static enum hrtimer_restart msm_otg_timer_func(struct hrtimer *_timer)
+{
+	struct msm_otg *dev = container_of(_timer, struct msm_otg, timer);
+
+	/* Phy lockup issues are observed when VBUS Valid interrupt is
+	 * enabled. Hence set A_VBUS_VLD upon timer exipration.
+	 */
+	if (dev->active_tmout == A_WAIT_VRISE)
+		set_bit(A_VBUS_VLD, &dev->inputs);
+	else
+		set_bit(dev->active_tmout, &dev->tmouts);
+
+	pr_debug("expired %s timer\n", timer_string(dev->active_tmout));
+	queue_work(dev->wq, &dev->sm_work);
+	return HRTIMER_NORESTART;
+}
+
+static void msm_otg_del_timer(struct msm_otg *dev)
+{
+	int bit = dev->active_tmout;
+
+	pr_debug("deleting %s timer. remaining %lld msec \n", timer_string(bit),
+			div_s64(ktime_to_us(hrtimer_get_remaining(&dev->timer)),
+					1000));
+	hrtimer_cancel(&dev->timer);
+	clear_bit(bit, &dev->tmouts);
+}
+
+static void msm_otg_start_timer(struct msm_otg *dev, int time, int bit)
+{
+	clear_bit(bit, &dev->tmouts);
+	dev->active_tmout = bit;
+	pr_debug("starting %s timer\n", timer_string(bit));
+	hrtimer_start(&dev->timer,
+			ktime_set(time / 1000, (time % 1000) * 1000000),
+			HRTIMER_MODE_REL);
+}
+
+/* No two otg timers run in parallel. So one hrtimer is sufficient */
+static void msm_otg_init_timer(struct msm_otg *dev)
+{
+	hrtimer_init(&dev->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	dev->timer.function = msm_otg_timer_func;
+}
+
+static const char *event_string(enum usb_otg_event event)
+{
+	switch (event) {
+	case OTG_EVENT_DEV_CONN_TMOUT:
+		return "DEV_CONN_TMOUT";
+	case OTG_EVENT_NO_RESP_FOR_HNP_ENABLE:
+		return "NO_RESP_FOR_HNP_ENABLE";
+	case OTG_EVENT_HUB_NOT_SUPPORTED:
+		return "HUB_NOT_SUPPORTED";
+	case OTG_EVENT_DEV_NOT_SUPPORTED:
+		return "DEV_NOT_SUPPORTED,";
+	case OTG_EVENT_HNP_FAILED:
+		return "HNP_FAILED";
+	case OTG_EVENT_NO_RESP_FOR_SRP:
+		return "NO_RESP_FOR_SRP";
+	default:
+		return "UNDEFINED";
+	}
+}
+
+static int msm_otg_send_event(struct usb_otg *otg,
+				enum usb_otg_event event)
+{
+	char module_name[16];
+	char udev_event[128];
+	char *envp[] = { module_name, udev_event, NULL };
+	int ret;
+
+	pr_debug("sending %s event\n", event_string(event));
+
+	snprintf(module_name, 16, "MODULE=%s", DRIVER_NAME);
+	snprintf(udev_event, 128, "EVENT=%s", event_string(event));
+	ret = kobject_uevent_env(&otg->phy->dev->kobj, KOBJ_CHANGE, envp);
+	if (ret < 0)
+		pr_info("uevent sending failed with ret = %d\n", ret);
+	return ret;
+}
+
+static int msm_otg_start_hnp(struct usb_otg *otg)
+{
+	struct msm_otg *dev = container_of(otg->phy, struct msm_otg, phy);
+	enum usb_otg_state state;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	state = dev->phy.state;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	if (state != OTG_STATE_A_HOST) {
+		pr_err("HNP can not be initiated in %s state\n",
+				state_string(state));
+		return -EINVAL;
+	}
+
+	pr_debug("A-Host: HNP initiated\n");
+	clear_bit(A_BUS_REQ, &dev->inputs);
+	wake_lock(&dev->wlock);
+	queue_work(dev->wq, &dev->sm_work);
+	return 0;
+}
+
+static int msm_otg_start_srp(struct usb_otg *otg)
+{
+	struct msm_otg *dev = container_of(otg->phy, struct msm_otg, phy);
+	u32	val;
+	int ret = 0;
+	enum usb_otg_state state;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	state = dev->phy.state;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	if (state != OTG_STATE_B_IDLE) {
+		pr_err("SRP can not be initiated in %s state\n",
+				state_string(state));
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if ((jiffies - dev->b_last_se0_sess) < msecs_to_jiffies(TB_SRP_INIT)) {
+		pr_debug("initial conditions of SRP are not met. Try again"
+				"after some time\n");
+		ret = -EAGAIN;
+		goto out;
+	}
+
+	/* Harware auto assist data pulsing: Data pulse is given
+	 * for 7msec; wait for vbus
+	 */
+	val = readl(USB_OTGSC);
+	writel((val & ~OTGSC_INTR_STS_MASK) | OTGSC_HADP, USB_OTGSC);
+
+	/* VBUS plusing is obsoleted in OTG 2.0 supplement */
+out:
+	return ret;
+}
+
+static int msm_otg_set_power(struct usb_phy *xceiv, unsigned mA)
+{
+	static enum chg_type 	curr_chg = USB_CHG_TYPE__INVALID;
+	struct msm_otg		*dev = container_of(xceiv, struct msm_otg, phy);
+	struct msm_otg_platform_data *pdata = dev->pdata;
+	enum chg_type 		new_chg = atomic_read(&dev->chg_type);
+	unsigned 		charge = mA;
+
+	/* Call chg_connected only if the charger has changed */
+	if (new_chg != curr_chg && pdata->chg_connected) {
+		curr_chg = new_chg;
+		pdata->chg_connected(new_chg);
+	}
+
+	/* Always use USB_IDCHG_MAX for charging in ID_B and ID_C */
+	if (test_bit(ID_C, &dev->inputs) ||
+				test_bit(ID_B, &dev->inputs))
+		charge = USB_IDCHG_MAX;
+
+	pr_debug("Charging with %dmA current\n", charge);
+	/* Call vbus_draw only if the charger is of known type and also
+	 * ignore request to stop charging as a result of suspend interrupt
+	 * when wall-charger is used.
+	 */
+	if (pdata->chg_vbus_draw && new_chg != USB_CHG_TYPE__INVALID &&
+		(charge || new_chg != USB_CHG_TYPE__WALLCHARGER))
+			pdata->chg_vbus_draw(charge);
+
+	if (new_chg == USB_CHG_TYPE__WALLCHARGER) {
+		wake_lock(&dev->wlock);
+		queue_work(dev->wq, &dev->sm_work);
+	}
+
+	return 0;
+}
+
+static int msm_otg_set_clk(struct usb_phy *xceiv, int on)
+{
+	struct msm_otg *dev = container_of(xceiv, struct msm_otg, phy);
+
+	if (!dev || (dev != the_msm_otg))
+		return -ENODEV;
+
+	if (on)
+		/* enable clocks */
+		clk_prepare_enable(dev->alt_core_clk);
+	else
+		clk_disable_unprepare(dev->alt_core_clk);
+
+	return 0;
+}
+static void msm_otg_start_peripheral(struct usb_otg *otg, int on)
+{
+	struct msm_otg *dev = container_of(otg->phy, struct msm_otg, phy);
+	struct msm_otg_platform_data *pdata = dev->pdata;
+
+	if (!otg->gadget)
+		return;
+
+	if (on) {
+		if (pdata->setup_gpio)
+			pdata->setup_gpio(USB_SWITCH_PERIPHERAL);
+		/* vote for minimum dma_latency to prevent idle
+		 * power collapse(pc) while running in peripheral mode.
+		 */
+		otg_pm_qos_update_latency(dev, 1);
+
+		/* increment the clk reference count so that
+		 * it would be still on when disabled from
+		 * low power mode routine
+		 */
+		if (dev->pdata->pclk_required_during_lpm)
+			clk_prepare_enable(dev->iface_clk);
+
+		usb_gadget_vbus_connect(otg->gadget);
+	} else {
+		atomic_set(&dev->chg_type, USB_CHG_TYPE__INVALID);
+		usb_gadget_vbus_disconnect(otg->gadget);
+
+		/* decrement the clk reference count so that
+		 * it would be off when disabled from
+		 * low power mode routine
+		 */
+		if (dev->pdata->pclk_required_during_lpm)
+			clk_disable_unprepare(dev->iface_clk);
+
+		otg_pm_qos_update_latency(dev, 0);
+		if (pdata->setup_gpio)
+			pdata->setup_gpio(USB_SWITCH_DISABLE);
+	}
+}
+
+static void msm_otg_start_host(struct usb_otg *otg, int on)
+{
+	struct msm_otg *dev = container_of(otg->phy, struct msm_otg, phy);
+	struct msm_otg_platform_data *pdata = dev->pdata;
+
+	if (!otg->host)
+		return;
+
+	if (dev->start_host) {
+		/* Some targets, e.g. ST1.5, use GPIO to choose b/w connector */
+		if (on && pdata->setup_gpio)
+			pdata->setup_gpio(USB_SWITCH_HOST);
+
+		/* increment or decrement the clk reference count
+		 * to avoid usb h/w lockup issues when low power
+		 * mode is initiated and vbus is on.
+		 */
+		if (dev->pdata->pclk_required_during_lpm) {
+			if (on)
+				clk_prepare_enable(dev->iface_clk);
+			else
+				clk_disable_unprepare(dev->iface_clk);
+		}
+
+		dev->start_host(otg->host, on);
+
+		if (!on && pdata->setup_gpio)
+			pdata->setup_gpio(USB_SWITCH_DISABLE);
+	}
+}
+
+static int msm_otg_suspend(struct msm_otg *dev)
+{
+	unsigned long timeout;
+	bool host_bus_suspend;
+	unsigned ret;
+	enum chg_type chg_type = atomic_read(&dev->chg_type);
+	unsigned long flags;
+
+	disable_irq(dev->irq);
+	if (atomic_read(&dev->in_lpm))
+		goto out;
+#ifdef CONFIG_USB_MSM_ACA
+	/*
+	 * ACA interrupts are disabled before entering into LPM.
+	 * If LPM is allowed in host mode with accessory charger
+	 * connected or only accessory charger is connected,
+	 * there is a chance that charger is removed and we will
+	 * not know about it.
+	 *
+	 * REVISIT
+	 *
+	 * Allowing LPM in case of gadget bus suspend is tricky.
+	 * Bus suspend can happen in two states.
+	 * 1. ID_float:  Allowing LPM has pros and cons. If LPM is allowed
+	 * and accessory charger is connected, we miss ID_float --> ID_C
+	 * transition where we could draw large amount of current
+	 * compared to the suspend current.
+	 * 2. ID_C: We can not allow LPM. If accessory charger is removed
+	 * we should not draw more than what host could supply which will
+	 * be less compared to accessory charger.
+	 *
+	 * For simplicity, LPM is not allowed in bus suspend.
+	 */
+#ifndef CONFIG_USB_MSM_STANDARD_ACA
+	/*
+	 * RID_A and IdGnd states are only possible with standard ACA.  We can
+	 * exit from low power mode with !BSV or IdGnd interrupt.  Hence LPM
+	 * is allowed.
+	 */
+	if ((test_bit(ID, &dev->inputs) && test_bit(B_SESS_VLD, &dev->inputs) &&
+			chg_type != USB_CHG_TYPE__WALLCHARGER) ||
+			test_bit(ID_A, &dev->inputs))
+		goto out;
+#endif
+	/* Disable ID_abc interrupts else it causes spurious interrupt */
+	disable_idabc(dev);
+#endif
+	ulpi_read(dev, 0x14);/* clear PHY interrupt latch register */
+
+	/*
+	 * Turn on PHY comparators if,
+	 * 1. USB wall charger is connected (bus suspend is not supported)
+	 * 2. Host bus suspend
+	 * 3. host is supported, but, id is not routed to pmic
+	 * 4. peripheral is supported, but, vbus is not routed to pmic
+	 */
+	host_bus_suspend = dev->phy.otg->host && is_host();
+
+	/*
+	 *  Configure the PMIC ID only in case of cable disconnect.
+	 *  PMIC doesn't generate interrupt for ID_GND to ID_A
+	 *  transistion. hence use the PHY ID cricuit.
+	 */
+	if (dev->pdata->pmic_id_notif_init && !host_bus_suspend &&
+		!test_bit(ID_A, &dev->inputs)) {
+		disable_idgnd(dev);
+		ret = dev->pdata->pmic_id_notif_init(
+			&msm_otg_set_id_state, 1);
+		if (!ret) {
+			dev->pmic_id_notif_supp = 1;
+			if (dev->pdata->pmic_id_irq)
+				dev->id_irq = dev->pdata->pmic_id_irq;
+		} else if (ret == -ENOTSUPP) {
+			pr_debug("%s:USB ID is not routed to pmic",
+			__func__);
+			enable_idgnd(dev);
+		} else {
+			pr_err("%s: pmic_id_ notif_init failed err:%d",
+				__func__, ret);
+		}
+	}
+
+	if ((dev->phy.otg->gadget && chg_type == USB_CHG_TYPE__WALLCHARGER) ||
+		host_bus_suspend ||
+		(dev->phy.otg->host && !dev->pmic_id_notif_supp) ||
+		(dev->phy.otg->gadget && !dev->pmic_vbus_notif_supp)) {
+		ulpi_write(dev, 0x01, 0x30);
+	}
+
+	ulpi_write(dev, 0x08, 0x09);/* turn off PLL on integrated phy */
+
+	timeout = jiffies + msecs_to_jiffies(500);
+	disable_phy_clk();
+	while (!is_phy_clk_disabled()) {
+		if (time_after(jiffies, timeout)) {
+			pr_err("%s: Unable to suspend phy\n", __func__);
+			/*
+			 * Start otg state machine in default state upon
+			 * phy suspend failure*/
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_UNDEFINED;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			queue_work(dev->wq, &dev->sm_work);
+			goto out;
+		}
+		msleep(1);
+		/* check if there are any pending interrupts*/
+		if (((readl(USB_OTGSC) & OTGSC_INTR_MASK) >> 8) &
+				readl(USB_OTGSC)) {
+			enable_idabc(dev);
+			goto out;
+		}
+	}
+
+	writel(readl(USB_USBCMD) | ASYNC_INTR_CTRL | ULPI_STP_CTRL, USB_USBCMD);
+	/* Ensure that above operation is completed before turning off clocks */
+	mb();
+
+	if (dev->iface_clk)
+		clk_disable_unprepare(dev->iface_clk);
+
+	clk_disable_unprepare(dev->core_clk);
+	/* usb phy no more require TCXO clock, hence vote for TCXO disable*/
+	ret = msm_xo_mode_vote(dev->xo_handle, MSM_XO_MODE_OFF);
+	if (ret)
+		pr_err("%s failed to devote for"
+			"TCXO D1 buffer%d\n", __func__, ret);
+
+	if (device_may_wakeup(dev->phy.dev)) {
+		enable_irq_wake(dev->irq);
+		if (dev->vbus_on_irq)
+			enable_irq_wake(dev->vbus_on_irq);
+		if (dev->id_irq)
+			enable_irq_wake(dev->id_irq);
+	}
+
+	atomic_set(&dev->in_lpm, 1);
+
+	/*
+	 * TODO: put regulators in low power mode by assuming that
+	 * regulators are brought back to active state before PHY
+	 * becomes active. But this assumption becomes wrong in case of
+	 * ACA charger where PHY itself will generate the wakeup
+	 * interrupt. This creates a small window where PHY regulators
+	 * are in LPM but PHY is in active state and this patch assumes
+	 * that there is no harm with this. Till hw folks confirms this
+	 * put regulators in lpm.
+	 */
+	 if (!host_bus_suspend && dev->pmic_vbus_notif_supp &&
+		!test_bit(ID_A, &dev->inputs)) {
+		pr_debug("phy can power collapse: (%d)\n",
+			can_phy_power_collapse(dev));
+		if (can_phy_power_collapse(dev) && dev->pdata->ldo_enable) {
+			pr_debug("disabling the regulators\n");
+			dev->pdata->ldo_enable(0);
+		}
+	}
+
+	/* phy can interrupts when vddcx is at 0.75, so irrespective
+	 * of pmic notification support, configure vddcx @0.75
+	 */
+	if (dev->pdata->config_vddcx)
+		dev->pdata->config_vddcx(0);
+	pr_info("%s: usb in low power mode\n", __func__);
+
+out:
+	enable_irq(dev->irq);
+
+	return 0;
+}
+
+static int msm_otg_resume(struct msm_otg *dev)
+{
+	unsigned temp;
+	unsigned ret;
+
+	if (!atomic_read(&dev->in_lpm))
+		return 0;
+	/* vote for vddcx, as PHY cannot tolerate vddcx below 1.0V */
+	if (dev->pdata->config_vddcx) {
+		ret = dev->pdata->config_vddcx(1);
+		if (ret) {
+			pr_err("%s: unable to enable vddcx digital core:%d\n",
+				__func__, ret);
+		}
+	}
+	if (dev->pdata->ldo_set_voltage)
+		dev->pdata->ldo_set_voltage(3400);
+
+	/* Vote for TCXO when waking up the phy */
+	ret = msm_xo_mode_vote(dev->xo_handle, MSM_XO_MODE_ON);
+	if (ret)
+		pr_err("%s failed to vote for"
+			"TCXO D1 buffer%d\n", __func__, ret);
+
+	clk_prepare_enable(dev->core_clk);
+
+	if (dev->iface_clk)
+		clk_prepare_enable(dev->iface_clk);
+
+	temp = readl(USB_USBCMD);
+	temp &= ~ASYNC_INTR_CTRL;
+	temp &= ~ULPI_STP_CTRL;
+	writel(temp, USB_USBCMD);
+
+	if (device_may_wakeup(dev->phy.dev)) {
+		disable_irq_wake(dev->irq);
+		if (dev->vbus_on_irq)
+			disable_irq_wake(dev->vbus_on_irq);
+		if (dev->id_irq)
+			disable_irq_wake(dev->id_irq);
+	}
+
+	atomic_set(&dev->in_lpm, 0);
+
+	pr_info("%s: usb exited from low power mode\n", __func__);
+
+	return 0;
+}
+
+static void msm_otg_get_resume(struct msm_otg *dev)
+{
+#ifdef CONFIG_PM_RUNTIME
+	pm_runtime_get_noresume(dev->phy.dev);
+	pm_runtime_resume(dev->phy.dev);
+#else
+	msm_otg_resume(dev);
+#endif
+}
+
+static void msm_otg_put_suspend(struct msm_otg *dev)
+{
+#ifdef CONFIG_PM_RUNTIME
+	pm_runtime_put_sync(dev->phy.dev);
+	if (!atomic_read(&dev->in_lpm))
+		pm_runtime_get_sync(dev->phy.dev);
+#else
+	msm_otg_suspend(dev);
+#endif
+}
+
+static void msm_otg_resume_w(struct work_struct *w)
+{
+	struct msm_otg	*dev = container_of(w, struct msm_otg, otg_resume_work);
+	unsigned long timeout;
+
+	if (can_phy_power_collapse(dev) && dev->pdata->ldo_enable)
+		dev->pdata->ldo_enable(1);
+
+	if (pm_runtime_enabled(dev->phy.dev)) {
+		msm_otg_get_resume(dev);
+	} else {
+		pm_runtime_get_noresume(dev->phy.dev);
+		msm_otg_resume(dev);
+		pm_runtime_set_active(dev->phy.dev);
+	}
+
+	if (!is_phy_clk_disabled())
+		goto phy_resumed;
+
+	timeout = jiffies + usecs_to_jiffies(100);
+	enable_phy_clk();
+	while (is_phy_clk_disabled() || !is_phy_active()) {
+		if (time_after(jiffies, timeout)) {
+			pr_err("%s: Unable to wakeup phy. is_phy_active: %x\n",
+				 __func__, !!is_phy_active());
+			/* Reset both phy and link */
+			otg_reset(&dev->phy, 1);
+			break;
+		}
+		udelay(10);
+	}
+
+phy_resumed:
+	/*
+	 * It is observed that BSVIS may get set immediatly
+	 * after PHY becomes active upon micro-B cable connect.
+	 * But BSVIS might get cleared by below enable_idgnd
+	 * function which causes hw to not generate the BSV interrupt.
+	 * Hence check for BSV interrupt explictly and schedule the
+	 * work.
+	 */
+	if (readl_relaxed(USB_OTGSC) & OTGSC_BSVIS) {
+		set_bit(B_SESS_VLD, &dev->inputs);
+		queue_work(dev->wq, &dev->sm_work);
+	}
+	if (dev->pmic_id_notif_supp) {
+		dev->pdata->pmic_id_notif_init(&msm_otg_set_id_state, 0);
+		dev->pmic_id_notif_supp = 0;
+		enable_idgnd(dev);
+	}
+
+	/* Enable Idabc interrupts as these were disabled before entering LPM */
+	enable_idabc(dev);
+
+	/*
+	 * There is corner case where host won't be resumed
+	 * while transitioning from ID_GND to ID_A. In that
+	 * IDGND might have cleared and ID_A might not have updated
+	 * yet. Hence update the ACA states explicitly.
+	 */
+	set_aca_id_inputs(dev);
+
+	/* If resume signalling finishes before lpm exit, PCD is not set in
+	 * USBSTS register. Drive resume signal to the downstream device now
+	 * so that host driver can process the upcoming port change interrupt.*/
+	if (is_host() || test_bit(ID_A, &dev->inputs)) {
+		writel(readl(USB_PORTSC) | PORTSC_FPR, USB_PORTSC);
+		msm_otg_start_host(dev->phy.otg, REQUEST_RESUME);
+	}
+
+	/* Enable irq which was disabled before scheduling this work.
+	 * But don't release wake_lock, as we got async interrupt and
+	 * there will be some work pending for OTG state machine.
+	 */
+	enable_irq(dev->irq);
+}
+
+static int msm_otg_set_suspend(struct usb_phy *xceiv, int suspend)
+{
+	struct msm_otg *dev = container_of(xceiv, struct msm_otg, phy);
+	enum usb_otg_state state;
+	unsigned long flags;
+
+	if (!dev || (dev != the_msm_otg))
+		return -ENODEV;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	state = dev->phy.state;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	pr_debug("suspend request in state: %s\n",
+			state_string(state));
+
+	if (suspend) {
+		switch (state) {
+#ifndef CONFIG_MSM_OTG_ENABLE_A_WAIT_BCON_TIMEOUT
+		case OTG_STATE_A_WAIT_BCON:
+			if (test_bit(ID_A, &dev->inputs))
+				msm_otg_set_power(xceiv, USB_IDCHG_MIN - 100);
+			msm_otg_put_suspend(dev);
+			break;
+#endif
+		case OTG_STATE_A_HOST:
+			clear_bit(A_BUS_REQ, &dev->inputs);
+			wake_lock(&dev->wlock);
+			queue_work(dev->wq, &dev->sm_work);
+			break;
+		case OTG_STATE_B_PERIPHERAL:
+			if (xceiv->otg->gadget->b_hnp_enable) {
+				set_bit(A_BUS_SUSPEND, &dev->inputs);
+				set_bit(B_BUS_REQ, &dev->inputs);
+				wake_lock(&dev->wlock);
+				queue_work(dev->wq, &dev->sm_work);
+			}
+			break;
+		case OTG_STATE_A_PERIPHERAL:
+			msm_otg_start_timer(dev, TA_BIDL_ADIS,
+					A_BIDL_ADIS);
+			break;
+		default:
+			break;
+		}
+	} else {
+		unsigned long timeout;
+
+		switch (state) {
+		case OTG_STATE_A_PERIPHERAL:
+			/* A-peripheral observed activity on bus.
+			 * clear A_BIDL_ADIS timer.
+			 */
+			msm_otg_del_timer(dev);
+			break;
+		case OTG_STATE_A_SUSPEND:
+			/* Remote wakeup or resume */
+			set_bit(A_BUS_REQ, &dev->inputs);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_HOST;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			if (test_bit(ID_A, &dev->inputs) &&
+				(get_aca_bmaxpower(dev) < USB_IDCHG_MIN))
+				msm_otg_set_power(xceiv,
+					USB_IDCHG_MIN - get_aca_bmaxpower(dev));
+			break;
+		default:
+			break;
+		}
+
+		if (suspend == atomic_read(&dev->in_lpm))
+			return 0;
+
+		disable_irq(dev->irq);
+		if (dev->pmic_vbus_notif_supp)
+			if (can_phy_power_collapse(dev) &&
+					dev->pdata->ldo_enable)
+				dev->pdata->ldo_enable(1);
+
+		msm_otg_get_resume(dev);
+
+		if (!is_phy_clk_disabled())
+			goto out;
+
+		timeout = jiffies + usecs_to_jiffies(100);
+		enable_phy_clk();
+		while (is_phy_clk_disabled() || !is_phy_active()) {
+			if (time_after(jiffies, timeout)) {
+				pr_err("%s: Unable to wakeup phy. "
+					"is_phy_active: %x\n",
+					__func__, !!is_phy_active());
+				/* Reset both phy and link */
+				otg_reset(&dev->phy, 1);
+				break;
+			}
+			udelay(10);
+		}
+		if (dev->pmic_id_notif_supp) {
+			dev->pdata->pmic_id_notif_init(
+				&msm_otg_set_id_state, 0);
+			dev->pmic_id_notif_supp = 0;
+			enable_idgnd(dev);
+		}
+out:
+		enable_idabc(dev);
+		enable_irq(dev->irq);
+
+	}
+
+	return 0;
+}
+
+static int msm_otg_set_peripheral(struct usb_otg *otg,
+			struct usb_gadget *gadget)
+{
+	struct msm_otg *dev = container_of(otg->phy, struct msm_otg, phy);
+
+	if (!dev || (dev != the_msm_otg))
+		return -ENODEV;
+
+	if (!gadget) {
+		msm_otg_start_peripheral(otg, 0);
+		otg->gadget = 0;
+		disable_sess_valid(dev);
+		if (!otg->host)
+			disable_idabc(dev);
+		return 0;
+	}
+	otg->gadget = gadget;
+	pr_info("peripheral driver registered w/ tranceiver\n");
+
+	wake_lock(&dev->wlock);
+	queue_work(dev->wq, &dev->sm_work);
+	return 0;
+}
+
+#ifdef CONFIG_USB_EHCI_MSM_72K
+static int usbdev_notify(struct notifier_block *self,
+			unsigned long action, void *device)
+{
+	enum usb_otg_state state;
+	struct msm_otg *dev = container_of(self, struct msm_otg, usbdev_nb);
+	struct usb_device *udev = device;
+	int work = 1;
+	unsigned long flags;
+
+	/* Interested in only devices directly connected
+	 * to root hub directly.
+	 */
+	if (!udev->parent || udev->parent->parent)
+		goto out;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	state = dev->phy.state;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	switch (state) {
+	case OTG_STATE_A_WAIT_BCON:
+		if (action == USB_DEVICE_ADD) {
+			pr_debug("B_CONN set\n");
+			set_bit(B_CONN, &dev->inputs);
+			if (udev->actconfig) {
+				set_aca_bmaxpower(dev,
+					udev->actconfig->desc.bMaxPower * 2);
+				goto do_work;
+			}
+			if (udev->portnum == udev->bus->otg_port)
+				set_aca_bmaxpower(dev, USB_IB_UNCFG);
+			else
+				set_aca_bmaxpower(dev, 100);
+		}
+		break;
+	case OTG_STATE_A_HOST:
+		if (action == USB_DEVICE_REMOVE) {
+			pr_debug("B_CONN clear\n");
+			clear_bit(B_CONN, &dev->inputs);
+			set_aca_bmaxpower(dev, 0);
+		}
+		break;
+	default:
+		work = 0;
+		break;
+	}
+do_work:
+	if (work) {
+		wake_lock(&dev->wlock);
+		queue_work(dev->wq, &dev->sm_work);
+	}
+out:
+	return NOTIFY_OK;
+}
+
+static int msm_otg_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+	struct msm_otg *dev = container_of(otg->phy, struct msm_otg, phy);
+
+	if (!dev || (dev != the_msm_otg))
+		return -ENODEV;
+
+	if (!dev->start_host)
+		return -ENODEV;
+
+	if (!host) {
+		msm_otg_start_host(otg, REQUEST_STOP);
+		usb_unregister_notify(&dev->usbdev_nb);
+		otg->host = 0;
+		dev->start_host = 0;
+		disable_idgnd(dev);
+		if (!otg->gadget)
+			disable_idabc(dev);
+		return 0;
+	}
+#ifdef CONFIG_USB_OTG
+	host->otg_port = 1;
+#endif
+	dev->usbdev_nb.notifier_call = usbdev_notify;
+	usb_register_notify(&dev->usbdev_nb);
+	otg->host = host;
+	pr_info("host driver registered w/ tranceiver\n");
+
+#ifndef CONFIG_USB_MSM_72K
+	wake_lock(&dev->wlock);
+	queue_work(dev->wq, &dev->sm_work);
+#endif
+	return 0;
+}
+
+static void msm_otg_set_id_state(int id)
+{
+	struct msm_otg *dev = the_msm_otg;
+	unsigned long flags;
+
+	if (!atomic_read(&dev->in_lpm))
+		return;
+
+	if (id) {
+		set_bit(ID, &dev->inputs);
+	} else {
+		clear_bit(ID, &dev->inputs);
+		set_bit(A_BUS_REQ, &dev->inputs);
+	}
+	spin_lock_irqsave(&dev->lock, flags);
+	if (dev->phy.state != OTG_STATE_UNDEFINED) {
+		wake_lock(&dev->wlock);
+		queue_work(dev->wq, &dev->sm_work);
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+#endif
+
+void msm_otg_set_vbus_state(int online)
+{
+	struct msm_otg *dev = the_msm_otg;
+
+	/*
+	 * Process disconnect only for wallcharger
+	 * during fast plug-out plug-in at the
+	 * AC source side.
+	 */
+	if (online)
+		set_bit(B_SESS_VLD, &dev->inputs);
+	else
+		clear_bit(B_SESS_VLD, &dev->inputs);
+
+	wake_lock(&dev->wlock);
+	queue_work(dev->wq, &dev->sm_work);
+}
+
+static irqreturn_t msm_otg_irq(int irq, void *data)
+{
+	struct msm_otg *dev = data;
+	u32 otgsc, sts, pc, sts_mask;
+	irqreturn_t ret = IRQ_HANDLED;
+	int work = 0;
+	enum usb_otg_state state;
+	unsigned long flags;
+
+	if (atomic_read(&dev->in_lpm)) {
+		disable_irq_nosync(dev->irq);
+		wake_lock(&dev->wlock);
+		queue_work(dev->wq, &dev->otg_resume_work);
+		goto out;
+	}
+
+	/* Return immediately if instead of ID pin, USER controls mode switch */
+	if (dev->pdata->otg_mode == OTG_USER_CONTROL)
+		return IRQ_NONE;
+
+
+	otgsc = readl(USB_OTGSC);
+	sts = readl(USB_USBSTS);
+
+	sts_mask = (otgsc & OTGSC_INTR_MASK) >> 8;
+
+	if (!((otgsc & sts_mask) || (sts & STS_PCI))) {
+		ret = IRQ_NONE;
+		goto out;
+	}
+
+	spin_lock_irqsave(&dev->lock, flags);
+	state = dev->phy.state;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	pr_debug("IRQ state: %s\n", state_string(state));
+	pr_debug("otgsc = %x\n", otgsc);
+
+	if ((otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) {
+		if (otgsc & OTGSC_ID) {
+			pr_debug("Id set\n");
+			set_bit(ID, &dev->inputs);
+		} else {
+			pr_debug("Id clear\n");
+			/* Assert a_bus_req to supply power on
+			 * VBUS when Micro/Mini-A cable is connected
+			 * with out user intervention.
+			 */
+			set_bit(A_BUS_REQ, &dev->inputs);
+			clear_bit(ID, &dev->inputs);
+		}
+		writel(otgsc, USB_OTGSC);
+		work = 1;
+	} else if (otgsc & OTGSC_BSVIS) {
+		writel(otgsc, USB_OTGSC);
+		/* BSV interrupt comes when operating as an A-device
+		 * (VBUS on/off).
+		 * But, handle BSV when charger is removed from ACA in ID_A
+		 */
+		if ((state >= OTG_STATE_A_IDLE) &&
+			!test_bit(ID_A, &dev->inputs))
+			goto out;
+		if (otgsc & OTGSC_BSV) {
+			pr_debug("BSV set\n");
+			set_bit(B_SESS_VLD, &dev->inputs);
+		} else {
+			pr_debug("BSV clear\n");
+			clear_bit(B_SESS_VLD, &dev->inputs);
+		}
+		work = 1;
+	} else if (otgsc & OTGSC_DPIS) {
+		pr_debug("DPIS detected\n");
+		writel(otgsc, USB_OTGSC);
+		set_bit(A_SRP_DET, &dev->inputs);
+		set_bit(A_BUS_REQ, &dev->inputs);
+		work = 1;
+	} else if (sts & STS_PCI) {
+		pc = readl(USB_PORTSC);
+		pr_debug("portsc = %x\n", pc);
+		ret = IRQ_NONE;
+		/* HCD Acks PCI interrupt. We use this to switch
+		 * between different OTG states.
+		 */
+		work = 1;
+		switch (state) {
+		case OTG_STATE_A_SUSPEND:
+			if (dev->phy.otg->host->b_hnp_enable &&
+					(pc & PORTSC_CSC) &&
+					!(pc & PORTSC_CCS)) {
+				pr_debug("B_CONN clear\n");
+				clear_bit(B_CONN, &dev->inputs);
+			}
+			break;
+		case OTG_STATE_B_WAIT_ACON:
+			if ((pc & PORTSC_CSC) && (pc & PORTSC_CCS)) {
+				pr_debug("A_CONN set\n");
+				set_bit(A_CONN, &dev->inputs);
+				/* Clear ASE0_BRST timer */
+				msm_otg_del_timer(dev);
+			}
+			break;
+		case OTG_STATE_B_HOST:
+			if ((pc & PORTSC_CSC) && !(pc & PORTSC_CCS)) {
+				pr_debug("A_CONN clear\n");
+				clear_bit(A_CONN, &dev->inputs);
+			}
+			break;
+		default:
+			work = 0;
+			break;
+		}
+	}
+	if (work) {
+#ifdef CONFIG_USB_MSM_ACA
+		/* With ACA, ID can change bcoz of BSVIS as well, so update */
+		if ((otgsc & OTGSC_IDIS) || (otgsc & OTGSC_BSVIS))
+			set_aca_id_inputs(dev);
+#endif
+		wake_lock(&dev->wlock);
+		queue_work(dev->wq, &dev->sm_work);
+	}
+out:
+	return ret;
+}
+
+#define ULPI_VERIFY_MAX_LOOP_COUNT  5
+#define PHY_CALIB_RETRY_COUNT 10
+static void phy_clk_reset(struct msm_otg *dev)
+{
+	unsigned rc;
+	enum clk_reset_action assert = CLK_RESET_ASSERT;
+
+	if (dev->pdata->phy_reset_sig_inverted)
+		assert = CLK_RESET_DEASSERT;
+
+	rc = clk_reset(dev->phy_reset_clk, assert);
+	if (rc) {
+		pr_err("%s: phy clk assert failed\n", __func__);
+		return;
+	}
+
+	msleep(1);
+
+	rc = clk_reset(dev->phy_reset_clk, !assert);
+	if (rc) {
+		pr_err("%s: phy clk deassert failed\n", __func__);
+		return;
+	}
+
+	msleep(1);
+}
+
+static unsigned ulpi_read_with_reset(struct msm_otg *dev, unsigned reg)
+{
+	int temp;
+	unsigned res;
+
+	for (temp = 0; temp < ULPI_VERIFY_MAX_LOOP_COUNT; temp++) {
+		res = ulpi_read(dev, reg);
+		if (res != 0xffffffff)
+			return res;
+
+		phy_clk_reset(dev);
+	}
+
+	pr_err("%s: ulpi read failed for %d times\n",
+			__func__, ULPI_VERIFY_MAX_LOOP_COUNT);
+
+	return -1;
+}
+
+static int ulpi_write_with_reset(struct msm_otg *dev,
+unsigned val, unsigned reg)
+{
+	int temp, res;
+
+	for (temp = 0; temp < ULPI_VERIFY_MAX_LOOP_COUNT; temp++) {
+		res = ulpi_write(dev, val, reg);
+		if (!res)
+			return 0;
+		phy_clk_reset(dev);
+	}
+	pr_err("%s: ulpi write failed for %d times\n",
+		__func__, ULPI_VERIFY_MAX_LOOP_COUNT);
+
+	return -1;
+}
+
+/* some of the older targets does not turn off the PLL
+ * if onclock bit is set and clocksuspendM bit is on,
+ * hence clear them too and initiate the suspend mode
+ * by clearing SupendM bit.
+ */
+static inline int turn_off_phy_pll(struct msm_otg *dev)
+{
+	unsigned res;
+
+	res = ulpi_read_with_reset(dev, ULPI_CONFIG_REG1);
+	if (res == 0xffffffff)
+		return -ETIMEDOUT;
+
+	res = ulpi_write_with_reset(dev,
+		res & ~(ULPI_ONCLOCK), ULPI_CONFIG_REG1);
+	if (res)
+		return -ETIMEDOUT;
+
+	res = ulpi_write_with_reset(dev,
+		ULPI_CLOCK_SUSPENDM, ULPI_IFC_CTRL_CLR);
+	if (res)
+		return -ETIMEDOUT;
+
+	/*Clear SuspendM bit to initiate suspend mode */
+	res = ulpi_write_with_reset(dev,
+		ULPI_SUSPENDM, ULPI_FUNC_CTRL_CLR);
+	if (res)
+		return -ETIMEDOUT;
+
+	return res;
+}
+
+static inline int check_phy_caliberation(struct msm_otg *dev)
+{
+	unsigned res;
+
+	res = ulpi_read_with_reset(dev, ULPI_DEBUG);
+
+	if (res == 0xffffffff)
+		return -ETIMEDOUT;
+
+	if (!(res & ULPI_CALIB_STS) && ULPI_CALIB_VAL(res))
+		return 0;
+
+	return -1;
+}
+
+static int msm_otg_phy_caliberate(struct msm_otg *dev)
+{
+	int i = 0;
+	unsigned long res;
+
+	do {
+		res = turn_off_phy_pll(dev);
+		if (res)
+			return -ETIMEDOUT;
+
+		/* bring phy out of suspend */
+		phy_clk_reset(dev);
+
+		res = check_phy_caliberation(dev);
+		if (!res)
+			return res;
+		i++;
+
+	} while (i < PHY_CALIB_RETRY_COUNT);
+
+	return res;
+}
+
+static int msm_otg_phy_reset(struct msm_otg *dev)
+{
+	unsigned rc;
+	unsigned temp;
+	unsigned long timeout;
+
+	rc = clk_reset(dev->alt_core_clk, CLK_RESET_ASSERT);
+	if (rc) {
+		pr_err("%s: usb hs clk assert failed\n", __func__);
+		return -1;
+	}
+
+	phy_clk_reset(dev);
+
+	rc = clk_reset(dev->alt_core_clk, CLK_RESET_DEASSERT);
+	if (rc) {
+		pr_err("%s: usb hs clk deassert failed\n", __func__);
+		return -1;
+	}
+	/* Observing ulpi timeouts as part of PHY calibration. On resetting
+	 * the HW link explicity by setting the RESET bit in the USBCMD
+	 * register before PHY calibration fixes the ulpi timeout issue.
+	 * This workaround is required for unicorn target
+	 */
+	writel_relaxed(USBCMD_RESET, USB_USBCMD);
+	timeout = jiffies + USB_LINK_RESET_TIMEOUT;
+	do {
+		if (time_after(jiffies, timeout)) {
+			pr_err("msm_otg: usb link reset timeout\n");
+			break;
+		}
+		usleep_range(1000, 1200);
+	} while (readl_relaxed(USB_USBCMD) & USBCMD_RESET);
+
+	/* select ULPI phy */
+	temp = (readl(USB_PORTSC) & ~PORTSC_PTS);
+	writel(temp | PORTSC_PTS_ULPI, USB_PORTSC);
+
+	if (atomic_read(&dev->chg_type) !=
+				USB_CHG_TYPE__WALLCHARGER) {
+		rc = msm_otg_phy_caliberate(dev);
+		if (rc)
+			return rc;
+	}
+
+	/* TBD: There are two link resets. One is below and other one
+	 * is done immediately after this function. See if we can
+	 * eliminate one of these.
+	 */
+	writel(USBCMD_RESET, USB_USBCMD);
+	timeout = jiffies + USB_LINK_RESET_TIMEOUT;
+	do {
+		if (time_after(jiffies, timeout)) {
+			pr_err("msm_otg: usb link reset timeout\n");
+			break;
+		}
+		msleep(1);
+	} while (readl(USB_USBCMD) & USBCMD_RESET);
+
+	if (readl(USB_USBCMD) & USBCMD_RESET) {
+		pr_err("%s: usb core reset failed\n", __func__);
+		return -1;
+	}
+
+	return 0;
+}
+
+static void otg_reset(struct usb_phy *xceiv, int phy_reset)
+{
+	struct msm_otg *dev = container_of(xceiv, struct msm_otg, phy);
+	unsigned long timeout;
+	u32 mode, work = 0;
+
+	clk_prepare_enable(dev->alt_core_clk);
+
+	if (!phy_reset)
+		goto reset_link;
+
+	if (dev->pdata->phy_reset)
+		dev->pdata->phy_reset(dev->regs);
+	else
+		msm_otg_phy_reset(dev);
+
+	/*disable all phy interrupts*/
+	ulpi_write(dev, 0xFF, 0x0F);
+	ulpi_write(dev, 0xFF, 0x12);
+	msleep(100);
+
+reset_link:
+	writel(USBCMD_RESET, USB_USBCMD);
+	timeout = jiffies + USB_LINK_RESET_TIMEOUT;
+	do {
+		if (time_after(jiffies, timeout)) {
+			pr_err("msm_otg: usb link reset timeout\n");
+			break;
+		}
+		msleep(1);
+	} while (readl(USB_USBCMD) & USBCMD_RESET);
+
+	/* select ULPI phy */
+	writel(0x80000000, USB_PORTSC);
+
+	set_pre_emphasis_level(dev);
+	set_hsdrv_slope(dev);
+	set_cdr_auto_reset(dev);
+	set_driver_amplitude(dev);
+	set_se1_gating(dev);
+
+	writel(0x0, USB_AHB_BURST);
+	writel(0x00, USB_AHB_MODE);
+	if (dev->pdata->bam_disable) {
+		writel_relaxed((readl_relaxed(USB_GEN_CONFIG) |
+					USB_BAM_DISABLE), USB_GEN_CONFIG);
+		pr_debug("%s(): USB_GEN_CONFIG = %x\n",
+				__func__, readl_relaxed(USB_GEN_CONFIG));
+	}
+	/* Ensure that RESET operation is completed before turning off clock */
+	mb();
+
+	clk_disable_unprepare(dev->alt_core_clk);
+
+	if ((xceiv->otg->gadget && xceiv->otg->gadget->is_a_peripheral) ||
+			test_bit(ID, &dev->inputs))
+		mode = USBMODE_SDIS | USBMODE_DEVICE;
+	else
+		mode = USBMODE_SDIS | USBMODE_HOST;
+	writel(mode, USB_USBMODE);
+
+	writel_relaxed((readl_relaxed(USB_OTGSC) | OTGSC_IDPU), USB_OTGSC);
+	if (dev->phy.otg->gadget) {
+		enable_sess_valid(dev);
+		/* Due to the above 100ms delay, interrupts from PHY are
+		 * sometimes missed during fast plug-in/plug-out of cable.
+		 * Check for such cases here.
+		 */
+		if (is_b_sess_vld() && !test_bit(B_SESS_VLD, &dev->inputs)) {
+			pr_debug("%s: handle missing BSV event\n", __func__);
+			set_bit(B_SESS_VLD, &dev->inputs);
+			work = 1;
+		} else if (!is_b_sess_vld() && test_bit(B_SESS_VLD,
+				&dev->inputs)) {
+			pr_debug("%s: handle missing !BSV event\n", __func__);
+			clear_bit(B_SESS_VLD, &dev->inputs);
+			work = 1;
+		}
+	}
+
+#ifdef CONFIG_USB_EHCI_MSM_72K
+	if (dev->phy.otg->host && !dev->pmic_id_notif_supp) {
+		enable_idgnd(dev);
+		/* Handle missing ID_GND interrupts during fast PIPO */
+		if (is_host() && test_bit(ID, &dev->inputs)) {
+			pr_debug("%s: handle missing ID_GND event\n", __func__);
+			clear_bit(ID, &dev->inputs);
+			work = 1;
+		} else if (!is_host() && !test_bit(ID, &dev->inputs)) {
+			pr_debug("%s: handle missing !ID_GND event\n",
+						__func__);
+			set_bit(ID, &dev->inputs);
+			work = 1;
+		}
+	} else {
+		disable_idgnd(dev);
+	}
+#endif
+
+	enable_idabc(dev);
+
+	if (work) {
+		wake_lock(&dev->wlock);
+		queue_work(dev->wq, &dev->sm_work);
+	}
+}
+
+static void msm_otg_sm_work(struct work_struct *w)
+{
+	struct msm_otg	*dev = container_of(w, struct msm_otg, sm_work);
+	enum chg_type	chg_type = atomic_read(&dev->chg_type);
+	int ret;
+	int work = 0;
+	enum usb_otg_state state;
+	unsigned long flags;
+
+	if (atomic_read(&dev->in_lpm))
+		msm_otg_set_suspend(&dev->phy, 0);
+
+	spin_lock_irqsave(&dev->lock, flags);
+	state = dev->phy.state;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	switch (state) {
+	case OTG_STATE_UNDEFINED:
+
+		/*
+		 * We can come here when LPM fails with wall charger
+		 * connected. Change the state to B_PERIPHERAL and
+		 * schedule the work which takes care of resetting the
+		 * PHY and putting the hardware in low power mode.
+		 */
+		if (atomic_read(&dev->chg_type) ==
+				USB_CHG_TYPE__WALLCHARGER) {
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_B_PERIPHERAL;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			work = 1;
+			break;
+		}
+
+		/* Reset both phy and link */
+		otg_reset(&dev->phy, 1);
+
+#ifdef CONFIG_USB_MSM_ACA
+		set_aca_id_inputs(dev);
+#endif
+		if (dev->pdata->otg_mode == OTG_USER_CONTROL) {
+			if ((dev->pdata->usb_mode == USB_PERIPHERAL_MODE) ||
+					!dev->phy.otg->host) {
+				set_bit(ID, &dev->inputs);
+				set_bit(B_SESS_VLD, &dev->inputs);
+			}
+		} else {
+			if (!dev->phy.otg->host || !is_host())
+				set_bit(ID, &dev->inputs);
+
+			if (dev->phy.otg->gadget && is_b_sess_vld())
+				set_bit(B_SESS_VLD, &dev->inputs);
+		}
+		spin_lock_irqsave(&dev->lock, flags);
+		if ((test_bit(ID, &dev->inputs)) &&
+				!test_bit(ID_A, &dev->inputs)) {
+			dev->phy.state = OTG_STATE_B_IDLE;
+		} else {
+			set_bit(A_BUS_REQ, &dev->inputs);
+			dev->phy.state = OTG_STATE_A_IDLE;
+		}
+		spin_unlock_irqrestore(&dev->lock, flags);
+
+		work = 1;
+		break;
+	case OTG_STATE_B_IDLE:
+		dev->phy.otg->default_a = 0;
+		if (!test_bit(ID, &dev->inputs) ||
+				test_bit(ID_A, &dev->inputs)) {
+			pr_debug("!id || id_A\n");
+			clear_bit(B_BUS_REQ, &dev->inputs);
+			otg_reset(&dev->phy, 0);
+
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_IDLE;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			msm_otg_set_power(&dev->phy, 0);
+			work = 1;
+		} else if (test_bit(B_SESS_VLD, &dev->inputs) &&
+				!test_bit(ID_B, &dev->inputs)) {
+			pr_debug("b_sess_vld\n");
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_B_PERIPHERAL;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			msm_otg_set_power(&dev->phy, 0);
+			msm_otg_start_peripheral(dev->phy.otg, 1);
+		} else if (test_bit(B_BUS_REQ, &dev->inputs)) {
+			pr_debug("b_sess_end && b_bus_req\n");
+			ret = msm_otg_start_srp(dev->phy.otg);
+			if (ret < 0) {
+				/* notify user space */
+				clear_bit(B_BUS_REQ, &dev->inputs);
+				work = 1;
+				break;
+			}
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_B_SRP_INIT;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			msm_otg_start_timer(dev, TB_SRP_FAIL, B_SRP_FAIL);
+			break;
+		} else if (test_bit(ID_B, &dev->inputs)) {
+			atomic_set(&dev->chg_type, USB_CHG_TYPE__SDP);
+			msm_otg_set_power(&dev->phy, USB_IDCHG_MAX);
+		} else {
+			msm_otg_set_power(&dev->phy, 0);
+			pr_debug("entering into lpm\n");
+			msm_otg_put_suspend(dev);
+
+			if (dev->pdata->ldo_set_voltage)
+				dev->pdata->ldo_set_voltage(3075);
+		}
+		break;
+	case OTG_STATE_B_SRP_INIT:
+		if (!test_bit(ID, &dev->inputs) ||
+				test_bit(ID_A, &dev->inputs) ||
+				test_bit(ID_C, &dev->inputs) ||
+				(test_bit(B_SESS_VLD, &dev->inputs) &&
+				!test_bit(ID_B, &dev->inputs))) {
+			pr_debug("!id || id_a/c || b_sess_vld+!id_b\n");
+			msm_otg_del_timer(dev);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_B_IDLE;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			work = 1;
+		} else if (test_bit(B_SRP_FAIL, &dev->tmouts)) {
+			pr_debug("b_srp_fail\n");
+			/* notify user space */
+			msm_otg_send_event(dev->phy.otg,
+				OTG_EVENT_NO_RESP_FOR_SRP);
+			clear_bit(B_BUS_REQ, &dev->inputs);
+			clear_bit(B_SRP_FAIL, &dev->tmouts);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_B_IDLE;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			dev->b_last_se0_sess = jiffies;
+			work = 1;
+		}
+		break;
+	case OTG_STATE_B_PERIPHERAL:
+		if (!test_bit(ID, &dev->inputs) ||
+				test_bit(ID_A, &dev->inputs) ||
+				test_bit(ID_B, &dev->inputs) ||
+				!test_bit(B_SESS_VLD, &dev->inputs)) {
+			pr_debug("!id  || id_a/b || !b_sess_vld\n");
+			clear_bit(B_BUS_REQ, &dev->inputs);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_B_IDLE;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			msm_otg_start_peripheral(dev->phy.otg, 0);
+			dev->b_last_se0_sess = jiffies;
+
+			/* Workaround: Reset phy after session */
+			otg_reset(&dev->phy, 1);
+			work = 1;
+		} else if (test_bit(B_BUS_REQ, &dev->inputs) &&
+				dev->phy.otg->gadget->b_hnp_enable &&
+				test_bit(A_BUS_SUSPEND, &dev->inputs)) {
+			pr_debug("b_bus_req && b_hnp_en && a_bus_suspend\n");
+			msm_otg_start_timer(dev, TB_ASE0_BRST, B_ASE0_BRST);
+			msm_otg_start_peripheral(dev->phy.otg, 0);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_B_WAIT_ACON;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			/* start HCD even before A-device enable
+			 * pull-up to meet HNP timings.
+			 */
+			dev->phy.otg->host->is_b_host = 1;
+			msm_otg_start_host(dev->phy.otg, REQUEST_START);
+
+		} else if (test_bit(ID_C, &dev->inputs)) {
+			atomic_set(&dev->chg_type, USB_CHG_TYPE__SDP);
+			msm_otg_set_power(&dev->phy, USB_IDCHG_MAX);
+		} else if (chg_type == USB_CHG_TYPE__WALLCHARGER) {
+#ifdef CONFIG_USB_MSM_ACA
+			del_timer_sync(&dev->id_timer);
+#endif
+			/* Workaround: Reset PHY in SE1 state */
+			otg_reset(&dev->phy, 1);
+			pr_debug("entering into lpm with wall-charger\n");
+			msm_otg_put_suspend(dev);
+			/* Allow idle power collapse */
+			otg_pm_qos_update_latency(dev, 0);
+		}
+		break;
+	case OTG_STATE_B_WAIT_ACON:
+		if (!test_bit(ID, &dev->inputs) ||
+				test_bit(ID_A, &dev->inputs) ||
+				test_bit(ID_B, &dev->inputs) ||
+				!test_bit(B_SESS_VLD, &dev->inputs)) {
+			pr_debug("!id || id_a/b || !b_sess_vld\n");
+			msm_otg_del_timer(dev);
+			/* A-device is physically disconnected during
+			 * HNP. Remove HCD.
+			 */
+			msm_otg_start_host(dev->phy.otg, REQUEST_STOP);
+			dev->phy.otg->host->is_b_host = 0;
+
+			clear_bit(B_BUS_REQ, &dev->inputs);
+			clear_bit(A_BUS_SUSPEND, &dev->inputs);
+			dev->b_last_se0_sess = jiffies;
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_B_IDLE;
+			spin_unlock_irqrestore(&dev->lock, flags);
+
+			/* Workaround: Reset phy after session */
+			otg_reset(&dev->phy, 1);
+			work = 1;
+		} else if (test_bit(A_CONN, &dev->inputs)) {
+			pr_debug("a_conn\n");
+			clear_bit(A_BUS_SUSPEND, &dev->inputs);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_B_HOST;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			if (test_bit(ID_C, &dev->inputs)) {
+				atomic_set(&dev->chg_type, USB_CHG_TYPE__SDP);
+				msm_otg_set_power(&dev->phy, USB_IDCHG_MAX);
+			}
+		} else if (test_bit(B_ASE0_BRST, &dev->tmouts)) {
+			/* TODO: A-device may send reset after
+			 * enabling HNP; a_bus_resume case is
+			 * not handled for now.
+			 */
+			pr_debug("b_ase0_brst_tmout\n");
+			msm_otg_send_event(dev->phy.otg,
+				OTG_EVENT_HNP_FAILED);
+			msm_otg_start_host(dev->phy.otg, REQUEST_STOP);
+			dev->phy.otg->host->is_b_host = 0;
+			clear_bit(B_ASE0_BRST, &dev->tmouts);
+			clear_bit(A_BUS_SUSPEND, &dev->inputs);
+			clear_bit(B_BUS_REQ, &dev->inputs);
+
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_B_PERIPHERAL;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			msm_otg_start_peripheral(dev->phy.otg, 1);
+		} else if (test_bit(ID_C, &dev->inputs)) {
+			atomic_set(&dev->chg_type, USB_CHG_TYPE__SDP);
+			msm_otg_set_power(&dev->phy, USB_IDCHG_MAX);
+		}
+		break;
+	case OTG_STATE_B_HOST:
+		/* B_BUS_REQ is not exposed to user space. So
+		 * it must be A_CONN for now.
+		 */
+		if (!test_bit(B_BUS_REQ, &dev->inputs) ||
+				!test_bit(A_CONN, &dev->inputs)) {
+			pr_debug("!b_bus_req || !a_conn\n");
+			clear_bit(A_CONN, &dev->inputs);
+			clear_bit(B_BUS_REQ, &dev->inputs);
+
+			msm_otg_start_host(dev->phy.otg, REQUEST_STOP);
+			dev->phy.otg->host->is_b_host = 0;
+
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_B_IDLE;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			/* Workaround: Reset phy after session */
+			otg_reset(&dev->phy, 1);
+			work = 1;
+		} else if (test_bit(ID_C, &dev->inputs)) {
+			atomic_set(&dev->chg_type, USB_CHG_TYPE__SDP);
+			msm_otg_set_power(&dev->phy, USB_IDCHG_MAX);
+		}
+		break;
+	case OTG_STATE_A_IDLE:
+		dev->phy.otg->default_a = 1;
+		if (test_bit(ID, &dev->inputs) &&
+				!test_bit(ID_A, &dev->inputs)) {
+			pr_debug("id && !id_a\n");
+			dev->phy.otg->default_a = 0;
+			otg_reset(&dev->phy, 0);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_B_IDLE;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			msm_otg_set_power(&dev->phy, 0);
+			work = 1;
+		} else if (!test_bit(A_BUS_DROP, &dev->inputs) &&
+				(test_bit(A_SRP_DET, &dev->inputs) ||
+				 test_bit(A_BUS_REQ, &dev->inputs))) {
+			pr_debug("!a_bus_drop && (a_srp_det || a_bus_req)\n");
+
+			clear_bit(A_SRP_DET, &dev->inputs);
+			/* Disable SRP detection */
+			writel((readl(USB_OTGSC) & ~OTGSC_INTR_STS_MASK) &
+					~OTGSC_DPIE, USB_OTGSC);
+
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_WAIT_VRISE;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			/* ACA: ID_A: Stop charging untill enumeration */
+			if (test_bit(ID_A, &dev->inputs))
+				msm_otg_set_power(&dev->phy, 0);
+			else
+				dev->pdata->vbus_power(USB_PHY_INTEGRATED, 1);
+			msm_otg_start_timer(dev, TA_WAIT_VRISE, A_WAIT_VRISE);
+			/* no need to schedule work now */
+		} else {
+			pr_debug("No session requested\n");
+
+			/* A-device is not providing power on VBUS.
+			 * Enable SRP detection.
+			 */
+			writel((readl(USB_OTGSC) & ~OTGSC_INTR_STS_MASK) |
+					OTGSC_DPIE, USB_OTGSC);
+			msm_otg_put_suspend(dev);
+
+		}
+		break;
+	case OTG_STATE_A_WAIT_VRISE:
+		if ((test_bit(ID, &dev->inputs) &&
+				!test_bit(ID_A, &dev->inputs)) ||
+				test_bit(A_BUS_DROP, &dev->inputs) ||
+				test_bit(A_WAIT_VRISE, &dev->tmouts)) {
+			pr_debug("id || a_bus_drop || a_wait_vrise_tmout\n");
+			clear_bit(A_BUS_REQ, &dev->inputs);
+			msm_otg_del_timer(dev);
+			dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_WAIT_VFALL;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			msm_otg_start_timer(dev, TA_WAIT_VFALL, A_WAIT_VFALL);
+		} else if (test_bit(A_VBUS_VLD, &dev->inputs)) {
+			pr_debug("a_vbus_vld\n");
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_WAIT_BCON;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			if (TA_WAIT_BCON > 0)
+				msm_otg_start_timer(dev, TA_WAIT_BCON,
+					A_WAIT_BCON);
+			/* Start HCD to detect peripherals. */
+			msm_otg_start_host(dev->phy.otg, REQUEST_START);
+		}
+		break;
+	case OTG_STATE_A_WAIT_BCON:
+		if ((test_bit(ID, &dev->inputs) &&
+				!test_bit(ID_A, &dev->inputs)) ||
+				test_bit(A_BUS_DROP, &dev->inputs) ||
+				test_bit(A_WAIT_BCON, &dev->tmouts)) {
+			pr_debug("id_f/b/c || a_bus_drop ||"
+					"a_wait_bcon_tmout\n");
+			if (test_bit(A_WAIT_BCON, &dev->tmouts))
+				msm_otg_send_event(dev->phy.otg,
+					OTG_EVENT_DEV_CONN_TMOUT);
+			msm_otg_del_timer(dev);
+			clear_bit(A_BUS_REQ, &dev->inputs);
+			msm_otg_start_host(dev->phy.otg, REQUEST_STOP);
+			/* Reset both phy and link */
+			otg_reset(&dev->phy, 1);
+			/* ACA: ID_A with NO accessory, just the A plug is
+			 * attached to ACA: Use IDCHG_MAX for charging
+			 */
+			if (test_bit(ID_A, &dev->inputs))
+				msm_otg_set_power(&dev->phy, USB_IDCHG_MAX);
+			else
+				dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_WAIT_VFALL;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			msm_otg_start_timer(dev, TA_WAIT_VFALL, A_WAIT_VFALL);
+		} else if (test_bit(B_CONN, &dev->inputs)) {
+			pr_debug("b_conn\n");
+			msm_otg_del_timer(dev);
+			/* HCD is added already. just move to
+			 * A_HOST state.
+			 */
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_HOST;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			if (test_bit(ID_A, &dev->inputs)) {
+				atomic_set(&dev->chg_type, USB_CHG_TYPE__SDP);
+				msm_otg_set_power(&dev->phy,
+					USB_IDCHG_MIN - get_aca_bmaxpower(dev));
+			}
+		} else if (!test_bit(A_VBUS_VLD, &dev->inputs)) {
+			pr_debug("!a_vbus_vld\n");
+			msm_otg_del_timer(dev);
+			msm_otg_start_host(dev->phy.otg, REQUEST_STOP);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_VBUS_ERR;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			/* Reset both phy and link */
+			otg_reset(&dev->phy, 1);
+		} else if (test_bit(ID_A, &dev->inputs)) {
+			dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0);
+		} else if (!test_bit(ID, &dev->inputs)) {
+			msm_otg_set_power(&dev->phy, 0);
+			dev->pdata->vbus_power(USB_PHY_INTEGRATED, 1);
+		}
+		break;
+	case OTG_STATE_A_HOST:
+		if ((test_bit(ID, &dev->inputs) &&
+				!test_bit(ID_A, &dev->inputs)) ||
+				test_bit(A_BUS_DROP, &dev->inputs)) {
+			pr_debug("id_f/b/c || a_bus_drop\n");
+			clear_bit(B_CONN, &dev->inputs);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_WAIT_VFALL;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			msm_otg_start_host(dev->phy.otg, REQUEST_STOP);
+			/* Reset both phy and link */
+			otg_reset(&dev->phy, 1);
+			if (!test_bit(ID_A, &dev->inputs))
+				dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0);
+			msm_otg_start_timer(dev, TA_WAIT_VFALL, A_WAIT_VFALL);
+			msm_otg_set_power(&dev->phy, 0);
+		} else if (!test_bit(A_VBUS_VLD, &dev->inputs)) {
+			pr_debug("!a_vbus_vld\n");
+			clear_bit(B_CONN, &dev->inputs);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_VBUS_ERR;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			msm_otg_start_host(dev->phy.otg, REQUEST_STOP);
+			/* Reset both phy and link */
+			otg_reset(&dev->phy, 1);
+			/* no work */
+		} else if (!test_bit(A_BUS_REQ, &dev->inputs)) {
+			/* a_bus_req is de-asserted when root hub is
+			 * suspended or HNP is in progress.
+			 */
+			pr_debug("!a_bus_req\n");
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_SUSPEND;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			if (dev->phy.otg->host->b_hnp_enable) {
+				msm_otg_start_timer(dev, TA_AIDL_BDIS,
+						A_AIDL_BDIS);
+			} else {
+				/* No HNP. Root hub suspended */
+				msm_otg_put_suspend(dev);
+			}
+			if (test_bit(ID_A, &dev->inputs))
+				msm_otg_set_power(&dev->phy,
+						USB_IDCHG_MIN - USB_IB_UNCFG);
+		} else if (!test_bit(B_CONN, &dev->inputs)) {
+			pr_debug("!b_conn\n");
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_WAIT_BCON;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			if (TA_WAIT_BCON > 0)
+				msm_otg_start_timer(dev, TA_WAIT_BCON,
+					A_WAIT_BCON);
+		} else if (test_bit(ID_A, &dev->inputs)) {
+			atomic_set(&dev->chg_type, USB_CHG_TYPE__SDP);
+			dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0);
+			msm_otg_set_power(&dev->phy,
+					USB_IDCHG_MIN - get_aca_bmaxpower(dev));
+		} else if (!test_bit(ID, &dev->inputs)) {
+			atomic_set(&dev->chg_type, USB_CHG_TYPE__INVALID);
+			msm_otg_set_power(&dev->phy, 0);
+			dev->pdata->vbus_power(USB_PHY_INTEGRATED, 1);
+		}
+		break;
+	case OTG_STATE_A_SUSPEND:
+		if ((test_bit(ID, &dev->inputs) &&
+				!test_bit(ID_A, &dev->inputs)) ||
+				test_bit(A_BUS_DROP, &dev->inputs) ||
+				test_bit(A_AIDL_BDIS, &dev->tmouts)) {
+			pr_debug("id_f/b/c || a_bus_drop ||"
+					"a_aidl_bdis_tmout\n");
+			if (test_bit(A_AIDL_BDIS, &dev->tmouts))
+				msm_otg_send_event(dev->phy.otg,
+					OTG_EVENT_HNP_FAILED);
+			msm_otg_del_timer(dev);
+			clear_bit(B_CONN, &dev->inputs);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_WAIT_VFALL;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			msm_otg_start_host(dev->phy.otg, REQUEST_STOP);
+			dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0);
+			/* Reset both phy and link */
+			otg_reset(&dev->phy, 1);
+			if (!test_bit(ID_A, &dev->inputs))
+				dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0);
+			msm_otg_start_timer(dev, TA_WAIT_VFALL, A_WAIT_VFALL);
+			msm_otg_set_power(&dev->phy, 0);
+		} else if (!test_bit(A_VBUS_VLD, &dev->inputs)) {
+			pr_debug("!a_vbus_vld\n");
+			msm_otg_del_timer(dev);
+			clear_bit(B_CONN, &dev->inputs);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_VBUS_ERR;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			msm_otg_start_host(dev->phy.otg, REQUEST_STOP);
+			/* Reset both phy and link */
+			otg_reset(&dev->phy, 1);
+		} else if (!test_bit(B_CONN, &dev->inputs) &&
+				dev->phy.otg->host->b_hnp_enable) {
+			pr_debug("!b_conn && b_hnp_enable");
+			/* Clear AIDL_BDIS timer */
+			msm_otg_del_timer(dev);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_PERIPHERAL;
+			spin_unlock_irqrestore(&dev->lock, flags);
+
+			msm_otg_start_host(dev->phy.otg, REQUEST_HNP_SUSPEND);
+
+			/* We may come here even when B-dev is physically
+			 * disconnected during HNP. We go back to host
+			 * role if bus is idle for BIDL_ADIS time.
+			 */
+			dev->phy.otg->gadget->is_a_peripheral = 1;
+			msm_otg_start_peripheral(dev->phy.otg, 1);
+			/* If ID_A: we can charge in a_peripheral as well */
+			if (test_bit(ID_A, &dev->inputs)) {
+				atomic_set(&dev->chg_type, USB_CHG_TYPE__SDP);
+				msm_otg_set_power(&dev->phy,
+					 USB_IDCHG_MIN - USB_IB_UNCFG);
+			}
+		} else if (!test_bit(B_CONN, &dev->inputs) &&
+				!dev->phy.otg->host->b_hnp_enable) {
+			pr_debug("!b_conn && !b_hnp_enable");
+			/* bus request is dropped during suspend.
+			 * acquire again for next device.
+			 */
+			set_bit(A_BUS_REQ, &dev->inputs);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_WAIT_BCON;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			if (TA_WAIT_BCON > 0)
+				msm_otg_start_timer(dev, TA_WAIT_BCON,
+					A_WAIT_BCON);
+			msm_otg_set_power(&dev->phy, 0);
+		} else if (test_bit(ID_A, &dev->inputs)) {
+			dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0);
+			atomic_set(&dev->chg_type, USB_CHG_TYPE__SDP);
+			msm_otg_set_power(&dev->phy,
+					 USB_IDCHG_MIN - USB_IB_UNCFG);
+		} else if (!test_bit(ID, &dev->inputs)) {
+			msm_otg_set_power(&dev->phy, 0);
+			dev->pdata->vbus_power(USB_PHY_INTEGRATED, 1);
+		}
+		break;
+	case OTG_STATE_A_PERIPHERAL:
+		if ((test_bit(ID, &dev->inputs) &&
+				!test_bit(ID_A, &dev->inputs)) ||
+				test_bit(A_BUS_DROP, &dev->inputs)) {
+			pr_debug("id _f/b/c || a_bus_drop\n");
+			/* Clear BIDL_ADIS timer */
+			msm_otg_del_timer(dev);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_WAIT_VFALL;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			msm_otg_start_peripheral(dev->phy.otg, 0);
+			dev->phy.otg->gadget->is_a_peripheral = 0;
+			/* HCD was suspended before. Stop it now */
+			msm_otg_start_host(dev->phy.otg, REQUEST_STOP);
+
+			/* Reset both phy and link */
+			otg_reset(&dev->phy, 1);
+			if (!test_bit(ID_A, &dev->inputs))
+				dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0);
+			msm_otg_start_timer(dev, TA_WAIT_VFALL, A_WAIT_VFALL);
+			msm_otg_set_power(&dev->phy, 0);
+		} else if (!test_bit(A_VBUS_VLD, &dev->inputs)) {
+			pr_debug("!a_vbus_vld\n");
+			/* Clear BIDL_ADIS timer */
+			msm_otg_del_timer(dev);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_VBUS_ERR;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			msm_otg_start_peripheral(dev->phy.otg, 0);
+			dev->phy.otg->gadget->is_a_peripheral = 0;
+			/* HCD was suspended before. Stop it now */
+			msm_otg_start_host(dev->phy.otg, REQUEST_STOP);
+		} else if (test_bit(A_BIDL_ADIS, &dev->tmouts)) {
+			pr_debug("a_bidl_adis_tmout\n");
+			msm_otg_start_peripheral(dev->phy.otg, 0);
+			dev->phy.otg->gadget->is_a_peripheral = 0;
+
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_WAIT_BCON;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			set_bit(A_BUS_REQ, &dev->inputs);
+			msm_otg_start_host(dev->phy.otg, REQUEST_HNP_RESUME);
+			if (TA_WAIT_BCON > 0)
+				msm_otg_start_timer(dev, TA_WAIT_BCON,
+					A_WAIT_BCON);
+			msm_otg_set_power(&dev->phy, 0);
+		} else if (test_bit(ID_A, &dev->inputs)) {
+			dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0);
+			atomic_set(&dev->chg_type, USB_CHG_TYPE__SDP);
+			msm_otg_set_power(&dev->phy,
+					 USB_IDCHG_MIN - USB_IB_UNCFG);
+		} else if (!test_bit(ID, &dev->inputs)) {
+			msm_otg_set_power(&dev->phy, 0);
+			dev->pdata->vbus_power(USB_PHY_INTEGRATED, 1);
+		}
+		break;
+	case OTG_STATE_A_WAIT_VFALL:
+		if (test_bit(A_WAIT_VFALL, &dev->tmouts)) {
+			clear_bit(A_VBUS_VLD, &dev->inputs);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_IDLE;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			work = 1;
+		}
+		break;
+	case OTG_STATE_A_VBUS_ERR:
+		if ((test_bit(ID, &dev->inputs) &&
+				!test_bit(ID_A, &dev->inputs)) ||
+				test_bit(A_BUS_DROP, &dev->inputs) ||
+				test_bit(A_CLR_ERR, &dev->inputs)) {
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_WAIT_VFALL;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			if (!test_bit(ID_A, &dev->inputs))
+				dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0);
+			msm_otg_start_timer(dev, TA_WAIT_VFALL, A_WAIT_VFALL);
+			msm_otg_set_power(&dev->phy, 0);
+		}
+		break;
+	default:
+		pr_err("invalid OTG state\n");
+	}
+
+	if (work)
+		queue_work(dev->wq, &dev->sm_work);
+
+#ifdef CONFIG_USB_MSM_ACA
+	/* Start id_polling if (ID_FLOAT&BSV) || ID_A/B/C */
+	if ((test_bit(ID, &dev->inputs) &&
+			test_bit(B_SESS_VLD, &dev->inputs) &&
+			chg_type != USB_CHG_TYPE__WALLCHARGER) ||
+			test_bit(ID_A, &dev->inputs)) {
+		mod_timer(&dev->id_timer, jiffies +
+				 msecs_to_jiffies(OTG_ID_POLL_MS));
+		return;
+	}
+	del_timer(&dev->id_timer);
+#endif
+	/* IRQ/sysfs may queue work. Check work_pending. otherwise
+	 * we might endup releasing wakelock after it is acquired
+	 * in IRQ/sysfs.
+	 */
+	if (!work_pending(&dev->sm_work) && !hrtimer_active(&dev->timer) &&
+			!work_pending(&dev->otg_resume_work))
+		wake_unlock(&dev->wlock);
+}
+
+#ifdef CONFIG_USB_MSM_ACA
+static void msm_otg_id_func(unsigned long _dev)
+{
+	struct msm_otg	*dev = (struct msm_otg *) _dev;
+	u8		phy_ints;
+
+#ifdef CONFIG_USB_MSM_STANDARD_ACA
+	/*
+	 * When standard ACA is attached RID_A and RID_GND states are only
+	 * possible.  RID_A-->RID_GND transition generates IdGnd interrupt
+	 * from PHY.  Hence polling is disabled.
+	 */
+	if (test_bit(ID_A, &dev->inputs))
+		goto out;
+#endif
+
+	if (atomic_read(&dev->in_lpm))
+		msm_otg_set_suspend(&dev->phy, 0);
+
+	phy_ints = ulpi_read(dev, 0x13);
+
+	/*
+	 * ACA timer will be kicked again after the PHY
+	 * state is recovered.
+	 */
+	if (phy_ints == -ETIMEDOUT)
+		return;
+
+
+	/* If id_gnd happened then stop and let isr take care of this */
+	if (phy_id_state_gnd(phy_ints))
+		goto out;
+
+	if ((test_bit(ID_A, &dev->inputs) == phy_id_state_a(phy_ints)) &&
+	    (test_bit(ID_B, &dev->inputs) == phy_id_state_b(phy_ints)) &&
+	    (test_bit(ID_C, &dev->inputs) == phy_id_state_c(phy_ints))) {
+		mod_timer(&dev->id_timer,
+				jiffies + msecs_to_jiffies(OTG_ID_POLL_MS));
+		goto out;
+	} else {
+		set_aca_id_inputs(dev);
+	}
+	wake_lock(&dev->wlock);
+	queue_work(dev->wq, &dev->sm_work);
+out:
+	/* OOPS: runing while !BSV, schedule work to initiate LPM */
+	if (!is_b_sess_vld()) {
+		clear_bit(B_SESS_VLD, &dev->inputs);
+		wake_lock(&dev->wlock);
+		queue_work(dev->wq, &dev->sm_work);
+	}
+	return;
+}
+#endif
+#ifdef CONFIG_USB_OTG
+static ssize_t
+set_pwr_down(struct device *_dev, struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	struct msm_otg *dev = the_msm_otg;
+	int value;
+	enum usb_otg_state state;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	state = dev->phy.state;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	/* Applicable for only A-Device */
+	if (state <= OTG_STATE_A_IDLE)
+		return -EINVAL;
+
+	sscanf(buf, "%d", &value);
+
+	if (test_bit(A_BUS_DROP, &dev->inputs) != !!value) {
+		change_bit(A_BUS_DROP, &dev->inputs);
+		wake_lock(&dev->wlock);
+		queue_work(dev->wq, &dev->sm_work);
+	}
+
+	return count;
+}
+static DEVICE_ATTR(pwr_down, S_IRUGO | S_IWUSR, NULL, set_pwr_down);
+
+static ssize_t
+set_srp_req(struct device *_dev, struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	struct msm_otg *dev = the_msm_otg;
+	enum usb_otg_state state;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	state = dev->phy.state;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	if (state != OTG_STATE_B_IDLE)
+		return -EINVAL;
+
+	set_bit(B_BUS_REQ, &dev->inputs);
+	wake_lock(&dev->wlock);
+	queue_work(dev->wq, &dev->sm_work);
+
+	return count;
+}
+static DEVICE_ATTR(srp_req, S_IRUGO | S_IWUSR, NULL, set_srp_req);
+
+static ssize_t
+set_clr_err(struct device *_dev, struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	struct msm_otg *dev = the_msm_otg;
+	enum usb_otg_state state;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	state = dev->phy.state;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	if (state == OTG_STATE_A_VBUS_ERR) {
+		set_bit(A_CLR_ERR, &dev->inputs);
+		wake_lock(&dev->wlock);
+		queue_work(dev->wq, &dev->sm_work);
+	}
+
+	return count;
+}
+static DEVICE_ATTR(clr_err, S_IRUGO | S_IWUSR, NULL, set_clr_err);
+
+static struct attribute *msm_otg_attrs[] = {
+	&dev_attr_pwr_down.attr,
+	&dev_attr_srp_req.attr,
+	&dev_attr_clr_err.attr,
+	NULL,
+};
+
+static struct attribute_group msm_otg_attr_grp = {
+	.attrs = msm_otg_attrs,
+};
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+static int otg_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+static ssize_t otg_mode_write(struct file *file, const char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	struct msm_otg *dev = file->private_data;
+	int ret = count;
+	int work = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	dev->pdata->otg_mode = OTG_USER_CONTROL;
+	if (!memcmp(buf, "none", 4)) {
+		clear_bit(B_SESS_VLD, &dev->inputs);
+		set_bit(ID, &dev->inputs);
+		work = 1;
+	} else if (!memcmp(buf, "peripheral", 10)) {
+		set_bit(B_SESS_VLD, &dev->inputs);
+		set_bit(ID, &dev->inputs);
+		work = 1;
+	} else if (!memcmp(buf, "host", 4)) {
+		clear_bit(B_SESS_VLD, &dev->inputs);
+		clear_bit(ID, &dev->inputs);
+		set_bit(A_BUS_REQ, &dev->inputs);
+		work = 1;
+	} else {
+		pr_info("%s: unknown mode specified\n", __func__);
+		ret = -EINVAL;
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	if (work) {
+		wake_lock(&dev->wlock);
+		queue_work(dev->wq, &dev->sm_work);
+	}
+
+	return ret;
+}
+const struct file_operations otgfs_fops = {
+	.open	= otg_open,
+	.write	= otg_mode_write,
+};
+
+#define OTG_INFO_SIZE 512
+static ssize_t otg_info_read(struct file *file, char __user *ubuf,
+				size_t count, loff_t *ppos)
+{
+	char *buf;
+	int temp = 0;
+	int ret;
+	struct msm_otg *dev = file->private_data;
+
+	buf = kzalloc(sizeof(char) * OTG_INFO_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	temp += scnprintf(buf + temp, OTG_INFO_SIZE - temp,
+			"OTG State:             %s\n"
+			"OTG Mode:              %d\n"
+			"OTG Inputs:            0x%lx\n"
+			"Charger Type:          %d\n"
+			"PMIC VBUS Support:     %u\n"
+			"PMIC ID Support:       %u\n"
+			"USB In SPS:            %d\n"
+			"pre_emphasis_level:    0x%x\n"
+			"cdr_auto_reset:        0x%x\n"
+			"hs_drv_amplitude:      0x%x\n"
+			"se1_gate_state:        0x%x\n"
+			"swfi_latency:          0x%x\n"
+			"PHY Powercollapse:     0x%x\n",
+			state_string(dev->phy.state),
+			dev->pdata->otg_mode,
+			dev->inputs,
+			atomic_read(&dev->chg_type),
+			dev->pmic_vbus_notif_supp,
+			dev->pmic_id_notif_supp,
+			dev->pdata->usb_in_sps,
+			dev->pdata->pemp_level,
+			dev->pdata->cdr_autoreset,
+			dev->pdata->drv_ampl,
+			dev->pdata->se1_gating,
+			dev->pdata->swfi_latency,
+			dev->pdata->phy_can_powercollapse);
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+	kfree(buf);
+
+	return ret;
+}
+
+const struct file_operations otgfs_info_fops = {
+	.open	= otg_open,
+	.read	= otg_info_read,
+};
+
+struct dentry *otg_debug_root;
+struct dentry *otg_debug_mode;
+struct dentry *otg_debug_info;
+#endif
+
+static int otg_debugfs_init(struct msm_otg *dev)
+{
+#ifdef CONFIG_DEBUG_FS
+	otg_debug_root = debugfs_create_dir("otg", NULL);
+	if (!otg_debug_root)
+		return -ENOENT;
+
+	otg_debug_mode = debugfs_create_file("mode", 0222,
+						otg_debug_root, dev,
+						&otgfs_fops);
+	if (!otg_debug_mode)
+		goto free_root;
+
+	otg_debug_info = debugfs_create_file("info", 0444,
+						otg_debug_root, dev,
+						&otgfs_info_fops);
+	if (!otg_debug_info)
+		goto free_mode;
+
+	return 0;
+
+free_mode:
+	debugfs_remove(otg_debug_mode);
+	otg_debug_mode = NULL;
+
+free_root:
+	debugfs_remove(otg_debug_root);
+	otg_debug_root = NULL;
+	return -ENOENT;
+#endif
+	return 0;
+}
+
+static void otg_debugfs_cleanup(void)
+{
+#ifdef CONFIG_DEBUG_FS
+	debugfs_remove(otg_debug_info);
+	debugfs_remove(otg_debug_mode);
+	debugfs_remove(otg_debug_root);
+#endif
+}
+
+struct usb_phy_io_ops msm_otg_io_ops = {
+	.read = usb_ulpi_read,
+	.write = usb_ulpi_write,
+};
+
+static int __init msm_otg_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct resource *res;
+	struct msm_otg *dev;
+
+	dev = kzalloc(sizeof(struct msm_otg), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	dev->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL);
+	if (!dev->phy.otg) {
+		kfree(dev);
+		return -ENOMEM;
+	}
+
+	the_msm_otg = dev;
+	dev->phy.dev = &pdev->dev;
+	dev->phy.otg->phy = &dev->phy;
+	dev->pdata = pdev->dev.platform_data;
+
+	if (!dev->pdata) {
+		ret = -ENODEV;
+		goto free_dev;
+	}
+
+#ifdef CONFIG_USB_EHCI_MSM_72K
+	if (!dev->pdata->vbus_power) {
+		ret = -ENODEV;
+		goto free_dev;
+	} else
+		dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0);
+
+#endif
+
+	if (dev->pdata->rpc_connect) {
+		ret = dev->pdata->rpc_connect(1);
+		pr_debug("%s: rpc_connect(%d)\n", __func__, ret);
+		if (ret) {
+			pr_err("%s: rpc connect failed\n", __func__);
+			ret = -ENODEV;
+			goto free_dev;
+		}
+	}
+
+	dev->alt_core_clk = clk_get(&pdev->dev, "alt_core_clk");
+	if (IS_ERR(dev->alt_core_clk)) {
+		pr_err("%s: failed to get alt_core_clk\n", __func__);
+		ret = PTR_ERR(dev->alt_core_clk);
+		goto rpc_fail;
+	}
+	clk_set_rate(dev->alt_core_clk, 60000000);
+
+	/* pm qos request to prevent apps idle power collapse */
+	pm_qos_add_request(&dev->pdata->pm_qos_req_dma, PM_QOS_CPU_DMA_LATENCY,
+			   PM_QOS_DEFAULT_VALUE);
+
+	dev->core_clk = clk_get(&pdev->dev, "core_clk");
+	if (IS_ERR(dev->core_clk)) {
+		pr_err("%s: failed to get core_clk\n", __func__);
+		ret = PTR_ERR(dev->core_clk);
+		goto put_alt_core_clk;
+	}
+	/* CORE clk must be running at >60Mhz for correct HSUSB operation
+	 * and USB core cannot tolerate frequency changes on CORE CLK.
+	 * Vote for maximum clk frequency for CORE clock.
+	 */
+	clk_set_rate(dev->core_clk, INT_MAX);
+
+	clk_prepare_enable(dev->core_clk);
+
+	if (!dev->pdata->pclk_is_hw_gated) {
+		dev->iface_clk = clk_get(&pdev->dev, "iface_clk");
+		if (IS_ERR(dev->iface_clk)) {
+			pr_err("%s: failed to get abh_clk\n", __func__);
+			ret = PTR_ERR(dev->iface_clk);
+			goto put_core_clk;
+		}
+		clk_prepare_enable(dev->iface_clk);
+	}
+
+	if (!dev->pdata->phy_reset) {
+		dev->phy_reset_clk = clk_get(&pdev->dev, "phy_clk");
+		if (IS_ERR(dev->phy_reset_clk)) {
+			pr_err("%s: failed to get phy_clk\n", __func__);
+			ret = PTR_ERR(dev->phy_reset_clk);
+			goto put_iface_clk;
+		}
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		pr_err("%s: failed to get platform resource mem\n", __func__);
+		ret = -ENODEV;
+		goto put_phy_clk;
+	}
+
+	dev->regs = ioremap(res->start, resource_size(res));
+	if (!dev->regs) {
+		pr_err("%s: ioremap failed\n", __func__);
+		ret = -ENOMEM;
+		goto put_phy_clk;
+	}
+	dev->irq = platform_get_irq(pdev, 0);
+	if (!dev->irq) {
+		pr_err("%s: platform_get_irq failed\n", __func__);
+		ret = -ENODEV;
+		goto free_regs;
+	}
+	dev->xo_handle = msm_xo_get(MSM_XO_TCXO_D1, "usb");
+	if (IS_ERR(dev->xo_handle)) {
+		pr_err(" %s not able to get the handle"
+			"to vote for TCXO D1 buffer\n", __func__);
+		ret = PTR_ERR(dev->xo_handle);
+		goto free_regs;
+	}
+
+	ret = msm_xo_mode_vote(dev->xo_handle, MSM_XO_MODE_ON);
+	if (ret) {
+		pr_err("%s failed to vote for TCXO"
+			"D1 buffer%d\n", __func__, ret);
+		goto free_xo_handle;
+	}
+
+
+	msm_otg_init_timer(dev);
+	INIT_WORK(&dev->sm_work, msm_otg_sm_work);
+	INIT_WORK(&dev->otg_resume_work, msm_otg_resume_w);
+	spin_lock_init(&dev->lock);
+	wake_lock_init(&dev->wlock, WAKE_LOCK_SUSPEND, "msm_otg");
+
+	dev->wq = alloc_workqueue("k_otg", WQ_NON_REENTRANT, 0);
+	if (!dev->wq) {
+		ret = -ENOMEM;
+		goto free_wlock;
+	}
+
+	if (dev->pdata->init_gpio) {
+		ret = dev->pdata->init_gpio(1);
+		if (ret) {
+			pr_err("%s: gpio init failed with err:%d\n",
+					__func__, ret);
+			goto free_wq;
+		}
+	}
+	/* To reduce phy power consumption and to avoid external LDO
+	 * on the board, PMIC comparators can be used to detect VBUS
+	 * session change.
+	 */
+	if (dev->pdata->pmic_vbus_notif_init) {
+		ret = dev->pdata->pmic_vbus_notif_init
+			(&msm_otg_set_vbus_state, 1);
+		if (!ret) {
+			dev->pmic_vbus_notif_supp = 1;
+		} else if (ret != -ENOTSUPP) {
+			pr_err("%s: pmic_vbus_notif_init() failed, err:%d\n",
+					__func__, ret);
+			goto free_gpio;
+		}
+	}
+
+	if (dev->pdata->phy_id_setup_init) {
+		ret = dev->pdata->phy_id_setup_init(1);
+		if (ret) {
+			pr_err("%s: phy_id_setup_init failed err:%d",
+					__func__, ret);
+			goto free_pmic_vbus_notif;
+		}
+	}
+
+	if (dev->pdata->pmic_vbus_irq)
+		dev->vbus_on_irq = dev->pdata->pmic_vbus_irq;
+
+	/* vote for vddcx, as PHY cannot tolerate vddcx below 1.0V */
+	if (dev->pdata->init_vddcx) {
+		ret = dev->pdata->init_vddcx(1);
+		if (ret) {
+			pr_err("%s: unable to enable vddcx digital core:%d\n",
+				__func__, ret);
+			goto free_phy_id_setup;
+		}
+	}
+
+	if (dev->pdata->ldo_init) {
+		ret = dev->pdata->ldo_init(1);
+		if (ret) {
+			pr_err("%s: ldo_init failed with err:%d\n",
+					__func__, ret);
+			goto free_config_vddcx;
+		}
+	}
+
+	if (dev->pdata->ldo_enable) {
+		ret = dev->pdata->ldo_enable(1);
+		if (ret) {
+			pr_err("%s: ldo_enable failed with err:%d\n",
+					__func__, ret);
+			goto free_ldo_init;
+		}
+	}
+
+
+	/* ACk all pending interrupts and clear interrupt enable registers */
+	writel((readl(USB_OTGSC) & ~OTGSC_INTR_MASK), USB_OTGSC);
+	writel(readl(USB_USBSTS), USB_USBSTS);
+	writel(0, USB_USBINTR);
+	/* Ensure that above STOREs are completed before enabling interrupts */
+	mb();
+
+	ret = request_irq(dev->irq, msm_otg_irq, IRQF_SHARED,
+					"msm_otg", dev);
+	if (ret) {
+		pr_err("%s: request irq failed\n", __func__);
+		goto free_ldo_enable;
+	}
+
+	dev->phy.set_suspend = msm_otg_set_suspend;
+	dev->phy.set_power = msm_otg_set_power;
+
+	dev->phy.otg->set_peripheral = msm_otg_set_peripheral;
+#ifdef CONFIG_USB_EHCI_MSM_72K
+	dev->phy.otg->set_host = msm_otg_set_host;
+#endif
+	dev->phy.otg->start_hnp = msm_otg_start_hnp;
+	dev->phy.otg->send_event = msm_otg_send_event;
+	dev->set_clk = msm_otg_set_clk;
+	dev->reset = otg_reset;
+	dev->phy.io_ops = &msm_otg_io_ops;
+	if (usb_set_transceiver(&dev->phy)) {
+		WARN_ON(1);
+		goto free_otg_irq;
+	}
+#ifdef CONFIG_USB_MSM_ACA
+	/* Link doesnt support id_a/b/c interrupts, hence polling
+	 * needs to be done to support ACA charger
+	 */
+	init_timer(&dev->id_timer);
+	dev->id_timer.function = msm_otg_id_func;
+	dev->id_timer.data = (unsigned long) dev;
+#endif
+
+	atomic_set(&dev->chg_type, USB_CHG_TYPE__INVALID);
+	if (dev->pdata->chg_init && dev->pdata->chg_init(1))
+		pr_err("%s: chg_init failed\n", __func__);
+
+	device_init_wakeup(&pdev->dev, 1);
+
+	ret = pm_runtime_set_active(&pdev->dev);
+	if (ret < 0)
+		pr_err("%s: pm_runtime: Fail to set active\n", __func__);
+
+	ret = 0;
+	pm_runtime_enable(&pdev->dev);
+	pm_runtime_get(&pdev->dev);
+
+
+	ret = otg_debugfs_init(dev);
+	if (ret) {
+		pr_err("%s: otg_debugfs_init failed\n", __func__);
+		goto chg_deinit;
+	}
+
+#ifdef CONFIG_USB_OTG
+	ret = sysfs_create_group(&pdev->dev.kobj, &msm_otg_attr_grp);
+	if (ret < 0) {
+		pr_err("%s: Failed to create the sysfs entry\n", __func__);
+		otg_debugfs_cleanup();
+		goto chg_deinit;
+	}
+#endif
+
+
+	return 0;
+
+chg_deinit:
+	if (dev->pdata->chg_init)
+		dev->pdata->chg_init(0);
+free_otg_irq:
+	free_irq(dev->irq, dev);
+free_ldo_enable:
+	if (dev->pdata->ldo_enable)
+		dev->pdata->ldo_enable(0);
+	if (dev->pdata->setup_gpio)
+		dev->pdata->setup_gpio(USB_SWITCH_DISABLE);
+free_ldo_init:
+	if (dev->pdata->ldo_init)
+		dev->pdata->ldo_init(0);
+free_config_vddcx:
+	if (dev->pdata->init_vddcx)
+		dev->pdata->init_vddcx(0);
+free_phy_id_setup:
+	if (dev->pdata->phy_id_setup_init)
+		dev->pdata->phy_id_setup_init(0);
+free_pmic_vbus_notif:
+	if (dev->pdata->pmic_vbus_notif_init && dev->pmic_vbus_notif_supp)
+		dev->pdata->pmic_vbus_notif_init(&msm_otg_set_vbus_state, 0);
+free_gpio:
+	if (dev->pdata->init_gpio)
+		dev->pdata->init_gpio(0);
+free_wq:
+	destroy_workqueue(dev->wq);
+free_wlock:
+	wake_lock_destroy(&dev->wlock);
+free_xo_handle:
+	msm_xo_put(dev->xo_handle);
+free_regs:
+	iounmap(dev->regs);
+put_phy_clk:
+	if (dev->phy_reset_clk)
+		clk_put(dev->phy_reset_clk);
+put_iface_clk:
+	if (dev->iface_clk) {
+		clk_disable_unprepare(dev->iface_clk);
+		clk_put(dev->iface_clk);
+	}
+put_core_clk:
+	clk_disable_unprepare(dev->core_clk);
+	clk_put(dev->core_clk);
+put_alt_core_clk:
+	clk_put(dev->alt_core_clk);
+rpc_fail:
+	if (dev->pdata->rpc_connect)
+		dev->pdata->rpc_connect(0);
+free_dev:
+	kfree(dev->phy.otg);
+	kfree(dev);
+	return ret;
+}
+
+static int __exit msm_otg_remove(struct platform_device *pdev)
+{
+	struct msm_otg *dev = the_msm_otg;
+
+	otg_debugfs_cleanup();
+#ifdef CONFIG_USB_OTG
+	sysfs_remove_group(&pdev->dev.kobj, &msm_otg_attr_grp);
+#endif
+	destroy_workqueue(dev->wq);
+	wake_lock_destroy(&dev->wlock);
+
+	if (dev->pdata->setup_gpio)
+		dev->pdata->setup_gpio(USB_SWITCH_DISABLE);
+
+	if (dev->pdata->init_vddcx)
+		dev->pdata->init_vddcx(0);
+	if (dev->pdata->ldo_enable)
+		dev->pdata->ldo_enable(0);
+
+	if (dev->pdata->ldo_init)
+		dev->pdata->ldo_init(0);
+
+	if (dev->pmic_vbus_notif_supp)
+		dev->pdata->pmic_vbus_notif_init(&msm_otg_set_vbus_state, 0);
+
+	if (dev->pdata->phy_id_setup_init)
+		dev->pdata->phy_id_setup_init(0);
+
+	if (dev->pmic_id_notif_supp)
+		dev->pdata->pmic_id_notif_init(&msm_otg_set_id_state, 0);
+
+#ifdef CONFIG_USB_MSM_ACA
+	del_timer_sync(&dev->id_timer);
+#endif
+	if (dev->pdata->chg_init)
+		dev->pdata->chg_init(0);
+	free_irq(dev->irq, pdev);
+	iounmap(dev->regs);
+	clk_disable_unprepare(dev->core_clk);
+	clk_put(dev->core_clk);
+	if (dev->iface_clk) {
+		clk_disable_unprepare(dev->iface_clk);
+		clk_put(dev->iface_clk);
+	}
+	if (dev->alt_core_clk)
+		clk_put(dev->alt_core_clk);
+	if (dev->phy_reset_clk)
+		clk_put(dev->phy_reset_clk);
+	if (dev->pdata->rpc_connect)
+		dev->pdata->rpc_connect(0);
+	msm_xo_put(dev->xo_handle);
+	pm_qos_remove_request(&dev->pdata->pm_qos_req_dma);
+
+	pm_runtime_put(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+	kfree(dev->phy.otg);
+	kfree(dev);
+	return 0;
+}
+
+static int msm_otg_runtime_suspend(struct device *dev)
+{
+	struct msm_otg *otg = the_msm_otg;
+
+	dev_dbg(dev, "pm_runtime: suspending...\n");
+	msm_otg_suspend(otg);
+	return  0;
+}
+
+static int msm_otg_runtime_resume(struct device *dev)
+{
+	struct msm_otg *otg = the_msm_otg;
+
+	dev_dbg(dev, "pm_runtime: resuming...\n");
+	msm_otg_resume(otg);
+	return  0;
+}
+
+static int msm_otg_runtime_idle(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: idling...\n");
+	return  0;
+}
+
+static struct dev_pm_ops msm_otg_dev_pm_ops = {
+	.runtime_suspend = msm_otg_runtime_suspend,
+	.runtime_resume = msm_otg_runtime_resume,
+	.runtime_idle = msm_otg_runtime_idle,
+};
+
+static struct platform_driver msm_otg_driver = {
+	.remove = __exit_p(msm_otg_remove),
+	.driver = {
+		.name = DRIVER_NAME,
+		.owner = THIS_MODULE,
+		.pm = &msm_otg_dev_pm_ops,
+	},
+};
+
+static int __init msm_otg_init(void)
+{
+	return platform_driver_probe(&msm_otg_driver, msm_otg_probe);
+}
+
+static void __exit msm_otg_exit(void)
+{
+	platform_driver_unregister(&msm_otg_driver);
+}
+
+module_init(msm_otg_init);
+module_exit(msm_otg_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MSM usb transceiver driver");
+MODULE_VERSION("1.00");
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index 1d0347c..59f01f6 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -9,11 +9,6 @@
  * 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 Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
  */
 
 #include <linux/module.h>
@@ -30,23 +25,32 @@
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
 #include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/dma-mapping.h>
 
 #include <linux/usb.h>
 #include <linux/usb/otg.h>
 #include <linux/usb/ulpi.h>
 #include <linux/usb/gadget.h>
 #include <linux/usb/hcd.h>
+#include <linux/usb/quirks.h>
 #include <linux/usb/msm_hsusb.h>
 #include <linux/usb/msm_hsusb_hw.h>
 #include <linux/regulator/consumer.h>
+#include <linux/mfd/pm8xxx/pm8921-charger.h>
+#include <linux/mfd/pm8xxx/misc.h>
+#include <linux/power_supply.h>
 
 #include <mach/clk.h>
+#include <mach/msm_xo.h>
+#include <mach/msm_bus.h>
+#include <mach/rpm-regulator.h>
 
 #define MSM_USB_BASE	(motg->regs)
 #define DRIVER_NAME	"msm_otg"
 
+#define ID_TIMER_FREQ		(jiffies + msecs_to_jiffies(500))
 #define ULPI_IO_TIMEOUT_USEC	(10 * 1000)
-
 #define USB_PHY_3P3_VOL_MIN	3050000 /* uV */
 #define USB_PHY_3P3_VOL_MAX	3300000 /* uV */
 #define USB_PHY_3P3_HPM_LOAD	50000	/* uA */
@@ -57,61 +61,58 @@
 #define USB_PHY_1P8_HPM_LOAD	50000	/* uA */
 #define USB_PHY_1P8_LPM_LOAD	4000	/* uA */
 
-#define USB_PHY_VDD_DIG_VOL_MIN	1000000 /* uV */
+#define USB_PHY_VDD_DIG_VOL_NONE	0 /*uV */
+#define USB_PHY_VDD_DIG_VOL_MIN	1045000 /* uV */
 #define USB_PHY_VDD_DIG_VOL_MAX	1320000 /* uV */
 
+static DECLARE_COMPLETION(pmic_vbus_init);
+static struct msm_otg *the_msm_otg;
+static bool debug_aca_enabled;
+static bool debug_bus_voting_enabled;
+
 static struct regulator *hsusb_3p3;
 static struct regulator *hsusb_1p8;
 static struct regulator *hsusb_vddcx;
+static struct regulator *vbus_otg;
+static struct regulator *mhl_analog_switch;
+static struct power_supply *psy;
 
-static int msm_hsusb_init_vddcx(struct msm_otg *motg, int init)
+static bool aca_id_turned_on;
+static inline bool aca_enabled(void)
 {
-	int ret = 0;
-
-	if (init) {
-		hsusb_vddcx = regulator_get(motg->phy.dev, "HSUSB_VDDCX");
-		if (IS_ERR(hsusb_vddcx)) {
-			dev_err(motg->phy.dev, "unable to get hsusb vddcx\n");
-			return PTR_ERR(hsusb_vddcx);
-		}
-
-		ret = regulator_set_voltage(hsusb_vddcx,
-				USB_PHY_VDD_DIG_VOL_MIN,
-				USB_PHY_VDD_DIG_VOL_MAX);
-		if (ret) {
-			dev_err(motg->phy.dev, "unable to set the voltage "
-					"for hsusb vddcx\n");
-			regulator_put(hsusb_vddcx);
-			return ret;
-		}
-
-		ret = regulator_enable(hsusb_vddcx);
-		if (ret) {
-			dev_err(motg->phy.dev, "unable to enable hsusb vddcx\n");
-			regulator_put(hsusb_vddcx);
-		}
-	} else {
-		ret = regulator_set_voltage(hsusb_vddcx, 0,
-			USB_PHY_VDD_DIG_VOL_MAX);
-		if (ret)
-			dev_err(motg->phy.dev, "unable to set the voltage "
-					"for hsusb vddcx\n");
-		ret = regulator_disable(hsusb_vddcx);
-		if (ret)
-			dev_err(motg->phy.dev, "unable to disable hsusb vddcx\n");
-
-		regulator_put(hsusb_vddcx);
-	}
-
-	return ret;
+#ifdef CONFIG_USB_MSM_ACA
+	return true;
+#else
+	return debug_aca_enabled;
+#endif
 }
 
+enum usb_vdd_value {
+	VDD_NONE = 0,
+	VDD_MIN,
+	VDD_MAX,
+	VDD_VAL_MAX,
+};
+
+static const int vdd_val[VDD_TYPE_MAX][VDD_VAL_MAX] = {
+		{  /* VDD_CX CORNER Voting */
+			[VDD_NONE]	= RPM_VREG_CORNER_NONE,
+			[VDD_MIN]	= RPM_VREG_CORNER_NOMINAL,
+			[VDD_MAX]	= RPM_VREG_CORNER_HIGH,
+		},
+		{ /* VDD_CX Voltage Voting */
+			[VDD_NONE]	= USB_PHY_VDD_DIG_VOL_NONE,
+			[VDD_MIN]	= USB_PHY_VDD_DIG_VOL_MIN,
+			[VDD_MAX]	= USB_PHY_VDD_DIG_VOL_MAX,
+		},
+};
+
 static int msm_hsusb_ldo_init(struct msm_otg *motg, int init)
 {
 	int rc = 0;
 
 	if (init) {
-		hsusb_3p3 = regulator_get(motg->phy.dev, "HSUSB_3p3");
+		hsusb_3p3 = devm_regulator_get(motg->phy.dev, "HSUSB_3p3");
 		if (IS_ERR(hsusb_3p3)) {
 			dev_err(motg->phy.dev, "unable to get hsusb 3p3\n");
 			return PTR_ERR(hsusb_3p3);
@@ -120,60 +121,43 @@
 		rc = regulator_set_voltage(hsusb_3p3, USB_PHY_3P3_VOL_MIN,
 				USB_PHY_3P3_VOL_MAX);
 		if (rc) {
-			dev_err(motg->phy.dev, "unable to set voltage level "
-					"for hsusb 3p3\n");
-			goto put_3p3;
+			dev_err(motg->phy.dev, "unable to set voltage level for"
+					"hsusb 3p3\n");
+			return rc;
 		}
-		rc = regulator_enable(hsusb_3p3);
-		if (rc) {
-			dev_err(motg->phy.dev, "unable to enable the hsusb 3p3\n");
-			goto put_3p3;
-		}
-		hsusb_1p8 = regulator_get(motg->phy.dev, "HSUSB_1p8");
+		hsusb_1p8 = devm_regulator_get(motg->phy.dev, "HSUSB_1p8");
 		if (IS_ERR(hsusb_1p8)) {
 			dev_err(motg->phy.dev, "unable to get hsusb 1p8\n");
 			rc = PTR_ERR(hsusb_1p8);
-			goto disable_3p3;
+			goto put_3p3_lpm;
 		}
 		rc = regulator_set_voltage(hsusb_1p8, USB_PHY_1P8_VOL_MIN,
 				USB_PHY_1P8_VOL_MAX);
 		if (rc) {
-			dev_err(motg->phy.dev, "unable to set voltage level "
-					"for hsusb 1p8\n");
-			goto put_1p8;
-		}
-		rc = regulator_enable(hsusb_1p8);
-		if (rc) {
-			dev_err(motg->phy.dev, "unable to enable the hsusb 1p8\n");
+			dev_err(motg->phy.dev, "unable to set voltage level for"
+					"hsusb 1p8\n");
 			goto put_1p8;
 		}
 
 		return 0;
 	}
 
-	regulator_disable(hsusb_1p8);
 put_1p8:
-	regulator_put(hsusb_1p8);
-disable_3p3:
-	regulator_disable(hsusb_3p3);
-put_3p3:
-	regulator_put(hsusb_3p3);
+	regulator_set_voltage(hsusb_1p8, 0, USB_PHY_1P8_VOL_MAX);
+put_3p3_lpm:
+	regulator_set_voltage(hsusb_3p3, 0, USB_PHY_3P3_VOL_MAX);
 	return rc;
 }
 
-#ifdef CONFIG_PM_SLEEP
-#define USB_PHY_SUSP_DIG_VOL  500000
 static int msm_hsusb_config_vddcx(int high)
 {
-	int max_vol = USB_PHY_VDD_DIG_VOL_MAX;
+	struct msm_otg *motg = the_msm_otg;
+	enum usb_vdd_type vdd_type = motg->vdd_type;
+	int max_vol = vdd_val[vdd_type][VDD_MAX];
 	int min_vol;
 	int ret;
 
-	if (high)
-		min_vol = USB_PHY_VDD_DIG_VOL_MIN;
-	else
-		min_vol = USB_PHY_SUSP_DIG_VOL;
-
+	min_vol = vdd_val[vdd_type][!!high];
 	ret = regulator_set_voltage(hsusb_vddcx, min_vol, max_vol);
 	if (ret) {
 		pr_err("%s: unable to set the voltage for regulator "
@@ -185,18 +169,17 @@
 
 	return ret;
 }
-#endif
 
-static int msm_hsusb_ldo_set_mode(int on)
+static int msm_hsusb_ldo_enable(struct msm_otg *motg, int on)
 {
 	int ret = 0;
 
-	if (!hsusb_1p8 || IS_ERR(hsusb_1p8)) {
+	if (IS_ERR(hsusb_1p8)) {
 		pr_err("%s: HSUSB_1p8 is not initialized\n", __func__);
 		return -ENODEV;
 	}
 
-	if (!hsusb_3p3 || IS_ERR(hsusb_3p3)) {
+	if (IS_ERR(hsusb_3p3)) {
 		pr_err("%s: HSUSB_3p3 is not initialized\n", __func__);
 		return -ENODEV;
 	}
@@ -205,29 +188,61 @@
 		ret = regulator_set_optimum_mode(hsusb_1p8,
 				USB_PHY_1P8_HPM_LOAD);
 		if (ret < 0) {
-			pr_err("%s: Unable to set HPM of the regulator "
+			pr_err("%s: Unable to set HPM of the regulator:"
 				"HSUSB_1p8\n", __func__);
 			return ret;
 		}
+
+		ret = regulator_enable(hsusb_1p8);
+		if (ret) {
+			dev_err(motg->phy.dev, "%s: unable to enable the hsusb 1p8\n",
+				__func__);
+			regulator_set_optimum_mode(hsusb_1p8, 0);
+			return ret;
+		}
+
 		ret = regulator_set_optimum_mode(hsusb_3p3,
 				USB_PHY_3P3_HPM_LOAD);
 		if (ret < 0) {
-			pr_err("%s: Unable to set HPM of the regulator "
+			pr_err("%s: Unable to set HPM of the regulator:"
 				"HSUSB_3p3\n", __func__);
-			regulator_set_optimum_mode(hsusb_1p8,
-				USB_PHY_1P8_LPM_LOAD);
+			regulator_set_optimum_mode(hsusb_1p8, 0);
+			regulator_disable(hsusb_1p8);
 			return ret;
 		}
+
+		ret = regulator_enable(hsusb_3p3);
+		if (ret) {
+			dev_err(motg->phy.dev, "%s: unable to enable the hsusb 3p3\n",
+				__func__);
+			regulator_set_optimum_mode(hsusb_3p3, 0);
+			regulator_set_optimum_mode(hsusb_1p8, 0);
+			regulator_disable(hsusb_1p8);
+			return ret;
+		}
+
 	} else {
-		ret = regulator_set_optimum_mode(hsusb_1p8,
-				USB_PHY_1P8_LPM_LOAD);
+		ret = regulator_disable(hsusb_1p8);
+		if (ret) {
+			dev_err(motg->phy.dev, "%s: unable to disable the hsusb 1p8\n",
+				__func__);
+			return ret;
+		}
+
+		ret = regulator_set_optimum_mode(hsusb_1p8, 0);
 		if (ret < 0)
-			pr_err("%s: Unable to set LPM of the regulator "
+			pr_err("%s: Unable to set LPM of the regulator:"
 				"HSUSB_1p8\n", __func__);
-		ret = regulator_set_optimum_mode(hsusb_3p3,
-				USB_PHY_3P3_LPM_LOAD);
+
+		ret = regulator_disable(hsusb_3p3);
+		if (ret) {
+			dev_err(motg->phy.dev, "%s: unable to disable the hsusb 3p3\n",
+				 __func__);
+			return ret;
+		}
+		ret = regulator_set_optimum_mode(hsusb_3p3, 0);
 		if (ret < 0)
-			pr_err("%s: Unable to set LPM of the regulator "
+			pr_err("%s: Unable to set LPM of the regulator:"
 				"HSUSB_3p3\n", __func__);
 	}
 
@@ -235,6 +250,26 @@
 	return ret < 0 ? ret : 0;
 }
 
+static void msm_hsusb_mhl_switch_enable(struct msm_otg *motg, bool on)
+{
+	struct msm_otg_platform_data *pdata = motg->pdata;
+
+	if (!pdata->mhl_enable)
+		return;
+
+	if (!mhl_analog_switch) {
+		pr_err("%s: mhl_analog_switch is NULL.\n", __func__);
+		return;
+	}
+
+	if (on) {
+		if (regulator_enable(mhl_analog_switch))
+			pr_err("unable to enable mhl_analog_switch\n");
+	} else {
+		regulator_disable(mhl_analog_switch);
+	}
+}
+
 static int ulpi_read(struct usb_phy *phy, u32 reg)
 {
 	struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
@@ -310,6 +345,9 @@
 {
 	int ret;
 
+	if (IS_ERR(motg->clk))
+		return 0;
+
 	if (assert) {
 		ret = clk_reset(motg->clk, CLK_RESET_ASSERT);
 		if (ret)
@@ -326,6 +364,9 @@
 {
 	int ret;
 
+	if (IS_ERR(motg->phy_reset_clk))
+		return 0;
+
 	ret = clk_reset(motg->phy_reset_clk, CLK_RESET_ASSERT);
 	if (ret) {
 		dev_err(motg->phy.dev, "usb phy clk assert failed\n");
@@ -390,26 +431,13 @@
 }
 
 #define LINK_RESET_TIMEOUT_USEC		(250 * 1000)
-static int msm_otg_reset(struct usb_phy *phy)
+static int msm_otg_link_reset(struct msm_otg *motg)
 {
-	struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
-	struct msm_otg_platform_data *pdata = motg->pdata;
 	int cnt = 0;
-	int ret;
-	u32 val = 0;
-	u32 ulpi_val = 0;
 
-	ret = msm_otg_phy_reset(motg);
-	if (ret) {
-		dev_err(phy->dev, "phy_reset failed\n");
-		return ret;
-	}
-
-	ulpi_init(motg);
-
-	writel(USBCMD_RESET, USB_USBCMD);
+	writel_relaxed(USBCMD_RESET, USB_USBCMD);
 	while (cnt < LINK_RESET_TIMEOUT_USEC) {
-		if (!(readl(USB_USBCMD) & USBCMD_RESET))
+		if (!(readl_relaxed(USB_USBCMD) & USBCMD_RESET))
 			break;
 		udelay(1);
 		cnt++;
@@ -418,15 +446,59 @@
 		return -ETIMEDOUT;
 
 	/* select ULPI phy */
-	writel(0x80000000, USB_PORTSC);
+	writel_relaxed(0x80000000, USB_PORTSC);
+	writel_relaxed(0x0, USB_AHBBURST);
+	writel_relaxed(0x08, USB_AHBMODE);
 
+	return 0;
+}
+
+static int msm_otg_reset(struct usb_phy *phy)
+{
+	struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
+	struct msm_otg_platform_data *pdata = motg->pdata;
+	int ret;
+	u32 val = 0;
+	u32 ulpi_val = 0;
+
+	/*
+	 * USB PHY and Link reset also reset the USB BAM.
+	 * Thus perform reset operation only once to avoid
+	 * USB BAM reset on other cases e.g. USB cable disconnections.
+	 */
+	if (pdata->disable_reset_on_disconnect) {
+		if (motg->reset_counter)
+			return 0;
+		else
+			motg->reset_counter++;
+	}
+
+	if (!IS_ERR(motg->clk))
+		clk_prepare_enable(motg->clk);
+	ret = msm_otg_phy_reset(motg);
+	if (ret) {
+		dev_err(phy->dev, "phy_reset failed\n");
+		return ret;
+	}
+
+	aca_id_turned_on = false;
+	ret = msm_otg_link_reset(motg);
+	if (ret) {
+		dev_err(phy->dev, "link reset failed\n");
+		return ret;
+	}
 	msleep(100);
 
-	writel(0x0, USB_AHBBURST);
-	writel(0x00, USB_AHBMODE);
+	ulpi_init(motg);
+
+	/* Ensure that RESET operation is completed before turning off clock */
+	mb();
+
+	if (!IS_ERR(motg->clk))
+		clk_disable_unprepare(motg->clk);
 
 	if (pdata->otg_control == OTG_PHY_CONTROL) {
-		val = readl(USB_OTGSC);
+		val = readl_relaxed(USB_OTGSC);
 		if (pdata->mode == USB_OTG) {
 			ulpi_val = ULPI_INT_IDGRD | ULPI_INT_SESS_VALID;
 			val |= OTGSC_IDIE | OTGSC_BSVIE;
@@ -434,14 +506,223 @@
 			ulpi_val = ULPI_INT_SESS_VALID;
 			val |= OTGSC_BSVIE;
 		}
-		writel(val, USB_OTGSC);
+		writel_relaxed(val, USB_OTGSC);
 		ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_RISE);
 		ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_FALL);
+	} else if (pdata->otg_control == OTG_PMIC_CONTROL) {
+		ulpi_write(phy, OTG_COMP_DISABLE,
+			ULPI_SET(ULPI_PWR_CLK_MNG_REG));
+		/* Enable PMIC pull-up */
+		pm8xxx_usb_id_pullup(1);
 	}
 
 	return 0;
 }
 
+static const char *timer_string(int bit)
+{
+	switch (bit) {
+	case A_WAIT_VRISE:		return "a_wait_vrise";
+	case A_WAIT_VFALL:		return "a_wait_vfall";
+	case B_SRP_FAIL:		return "b_srp_fail";
+	case A_WAIT_BCON:		return "a_wait_bcon";
+	case A_AIDL_BDIS:		return "a_aidl_bdis";
+	case A_BIDL_ADIS:		return "a_bidl_adis";
+	case B_ASE0_BRST:		return "b_ase0_brst";
+	case A_TST_MAINT:		return "a_tst_maint";
+	case B_TST_SRP:			return "b_tst_srp";
+	case B_TST_CONFIG:		return "b_tst_config";
+	default:			return "UNDEFINED";
+	}
+}
+
+static enum hrtimer_restart msm_otg_timer_func(struct hrtimer *hrtimer)
+{
+	struct msm_otg *motg = container_of(hrtimer, struct msm_otg, timer);
+
+	switch (motg->active_tmout) {
+	case A_WAIT_VRISE:
+		/* TODO: use vbus_vld interrupt */
+		set_bit(A_VBUS_VLD, &motg->inputs);
+		break;
+	case A_TST_MAINT:
+		/* OTG PET: End session after TA_TST_MAINT */
+		set_bit(A_BUS_DROP, &motg->inputs);
+		break;
+	case B_TST_SRP:
+		/*
+		 * OTG PET: Initiate SRP after TB_TST_SRP of
+		 * previous session end.
+		 */
+		set_bit(B_BUS_REQ, &motg->inputs);
+		break;
+	case B_TST_CONFIG:
+		clear_bit(A_CONN, &motg->inputs);
+		break;
+	default:
+		set_bit(motg->active_tmout, &motg->tmouts);
+	}
+
+	pr_debug("expired %s timer\n", timer_string(motg->active_tmout));
+	queue_work(system_nrt_wq, &motg->sm_work);
+	return HRTIMER_NORESTART;
+}
+
+static void msm_otg_del_timer(struct msm_otg *motg)
+{
+	int bit = motg->active_tmout;
+
+	pr_debug("deleting %s timer. remaining %lld msec\n", timer_string(bit),
+			div_s64(ktime_to_us(hrtimer_get_remaining(
+					&motg->timer)), 1000));
+	hrtimer_cancel(&motg->timer);
+	clear_bit(bit, &motg->tmouts);
+}
+
+static void msm_otg_start_timer(struct msm_otg *motg, int time, int bit)
+{
+	clear_bit(bit, &motg->tmouts);
+	motg->active_tmout = bit;
+	pr_debug("starting %s timer\n", timer_string(bit));
+	hrtimer_start(&motg->timer,
+			ktime_set(time / 1000, (time % 1000) * 1000000),
+			HRTIMER_MODE_REL);
+}
+
+static void msm_otg_init_timer(struct msm_otg *motg)
+{
+	hrtimer_init(&motg->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	motg->timer.function = msm_otg_timer_func;
+}
+
+static int msm_otg_start_hnp(struct usb_otg *otg)
+{
+	struct msm_otg *motg = container_of(otg->phy, struct msm_otg, phy);
+
+	if (otg->phy->state != OTG_STATE_A_HOST) {
+		pr_err("HNP can not be initiated in %s state\n",
+				otg_state_string(otg->phy->state));
+		return -EINVAL;
+	}
+
+	pr_debug("A-Host: HNP initiated\n");
+	clear_bit(A_BUS_REQ, &motg->inputs);
+	queue_work(system_nrt_wq, &motg->sm_work);
+	return 0;
+}
+
+static int msm_otg_start_srp(struct usb_otg *otg)
+{
+	struct msm_otg *motg = container_of(otg->phy, struct msm_otg, phy);
+	u32 val;
+	int ret = 0;
+
+	if (otg->phy->state != OTG_STATE_B_IDLE) {
+		pr_err("SRP can not be initiated in %s state\n",
+				otg_state_string(otg->phy->state));
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if ((jiffies - motg->b_last_se0_sess) < msecs_to_jiffies(TB_SRP_INIT)) {
+		pr_debug("initial conditions of SRP are not met. Try again"
+				"after some time\n");
+		ret = -EAGAIN;
+		goto out;
+	}
+
+	pr_debug("B-Device SRP started\n");
+
+	/*
+	 * PHY won't pull D+ high unless it detects Vbus valid.
+	 * Since by definition, SRP is only done when Vbus is not valid,
+	 * software work-around needs to be used to spoof the PHY into
+	 * thinking it is valid. This can be done using the VBUSVLDEXTSEL and
+	 * VBUSVLDEXT register bits.
+	 */
+	ulpi_write(otg->phy, 0x03, 0x97);
+	/*
+	 * Harware auto assist data pulsing: Data pulse is given
+	 * for 7msec; wait for vbus
+	 */
+	val = readl_relaxed(USB_OTGSC);
+	writel_relaxed((val & ~OTGSC_INTSTS_MASK) | OTGSC_HADP, USB_OTGSC);
+
+	/* VBUS plusing is obsoleted in OTG 2.0 supplement */
+out:
+	return ret;
+}
+
+static void msm_otg_host_hnp_enable(struct usb_otg *otg, bool enable)
+{
+	struct usb_hcd *hcd = bus_to_hcd(otg->host);
+	struct usb_device *rhub = otg->host->root_hub;
+
+	if (enable) {
+		pm_runtime_disable(&rhub->dev);
+		rhub->state = USB_STATE_NOTATTACHED;
+		hcd->driver->bus_suspend(hcd);
+		clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+	} else {
+		usb_remove_hcd(hcd);
+		msm_otg_reset(otg->phy);
+		usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
+	}
+}
+
+static int msm_otg_set_suspend(struct usb_phy *phy, int suspend)
+{
+	struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
+
+	if (aca_enabled())
+		return 0;
+
+	if (suspend) {
+		switch (phy->state) {
+		case OTG_STATE_A_WAIT_BCON:
+			if (TA_WAIT_BCON > 0)
+				break;
+			/* fall through */
+		case OTG_STATE_A_HOST:
+			pr_debug("host bus suspend\n");
+			clear_bit(A_BUS_REQ, &motg->inputs);
+			queue_work(system_nrt_wq, &motg->sm_work);
+			break;
+		case OTG_STATE_B_PERIPHERAL:
+			pr_debug("peripheral bus suspend\n");
+			if (!(motg->caps & ALLOW_LPM_ON_DEV_SUSPEND))
+				break;
+			set_bit(A_BUS_SUSPEND, &motg->inputs);
+			queue_work(system_nrt_wq, &motg->sm_work);
+			break;
+
+		default:
+			break;
+		}
+	} else {
+		switch (phy->state) {
+		case OTG_STATE_A_SUSPEND:
+			/* Remote wakeup or resume */
+			set_bit(A_BUS_REQ, &motg->inputs);
+			phy->state = OTG_STATE_A_HOST;
+
+			/* ensure hardware is not in low power mode */
+			pm_runtime_resume(phy->dev);
+			break;
+		case OTG_STATE_B_PERIPHERAL:
+			pr_debug("peripheral bus resume\n");
+			if (!(motg->caps & ALLOW_LPM_ON_DEV_SUSPEND))
+				break;
+			clear_bit(A_BUS_SUSPEND, &motg->inputs);
+			queue_work(system_nrt_wq, &motg->sm_work);
+			break;
+		default:
+			break;
+		}
+	}
+	return 0;
+}
+
 #define PHY_SUSPEND_TIMEOUT_USEC	(500 * 1000)
 #define PHY_RESUME_TIMEOUT_USEC	(100 * 1000)
 
@@ -452,11 +733,20 @@
 	struct usb_bus *bus = phy->otg->host;
 	struct msm_otg_platform_data *pdata = motg->pdata;
 	int cnt = 0;
+	bool host_bus_suspend, device_bus_suspend, dcp;
+	u32 phy_ctrl_val = 0, cmd_val;
+	unsigned ret;
+	u32 portsc;
 
 	if (atomic_read(&motg->in_lpm))
 		return 0;
 
 	disable_irq(motg->irq);
+	host_bus_suspend = phy->otg->host && !test_bit(ID, &motg->inputs);
+	device_bus_suspend = phy->otg->gadget && test_bit(ID, &motg->inputs) &&
+		test_bit(A_BUS_SUSPEND, &motg->inputs) &&
+		motg->caps & ALLOW_LPM_ON_DEV_SUSPEND;
+	dcp = motg->chg_type == USB_DCP_CHARGER;
 	/*
 	 * Chipidea 45-nm PHY suspend sequence:
 	 *
@@ -481,17 +771,22 @@
 		ulpi_write(phy, 0x08, 0x09);
 	}
 
-	/*
+
+	/* Set the PHCD bit, only if it is not set by the controller.
 	 * PHY may take some time or even fail to enter into low power
 	 * mode (LPM). Hence poll for 500 msec and reset the PHY and link
 	 * in failure case.
 	 */
-	writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC);
-	while (cnt < PHY_SUSPEND_TIMEOUT_USEC) {
-		if (readl(USB_PORTSC) & PORTSC_PHCD)
-			break;
-		udelay(1);
-		cnt++;
+	portsc = readl_relaxed(USB_PORTSC);
+	if (!(portsc & PORTSC_PHCD)) {
+		writel_relaxed(portsc | PORTSC_PHCD,
+				USB_PORTSC);
+		while (cnt < PHY_SUSPEND_TIMEOUT_USEC) {
+			if (readl_relaxed(USB_PORTSC) & PORTSC_PHCD)
+				break;
+			udelay(1);
+			cnt++;
+		}
 	}
 
 	if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) {
@@ -507,34 +802,69 @@
 	 * line must be disabled till async interrupt enable bit is cleared
 	 * in USBCMD register. Assert STP (ULPI interface STOP signal) to
 	 * block data communication from PHY.
+	 *
+	 * PHY retention mode is disallowed while entering to LPM with wall
+	 * charger connected.  But PHY is put into suspend mode. Hence
+	 * enable asynchronous interrupt to detect charger disconnection when
+	 * PMIC notifications are unavailable.
 	 */
-	writel(readl(USB_USBCMD) | ASYNC_INTR_CTRL | ULPI_STP_CTRL, USB_USBCMD);
+	cmd_val = readl_relaxed(USB_USBCMD);
+	if (host_bus_suspend || device_bus_suspend ||
+		(motg->pdata->otg_control == OTG_PHY_CONTROL && dcp))
+		cmd_val |= ASYNC_INTR_CTRL | ULPI_STP_CTRL;
+	else
+		cmd_val |= ULPI_STP_CTRL;
+	writel_relaxed(cmd_val, USB_USBCMD);
 
-	if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY &&
-			motg->pdata->otg_control == OTG_PMIC_CONTROL)
-		writel(readl(USB_PHY_CTRL) | PHY_RETEN, USB_PHY_CTRL);
+	/*
+	 * BC1.2 spec mandates PD to enable VDP_SRC when charging from DCP.
+	 * PHY retention and collapse can not happen with VDP_SRC enabled.
+	 */
+	if (motg->caps & ALLOW_PHY_RETENTION && !host_bus_suspend &&
+		!device_bus_suspend && !dcp) {
+		phy_ctrl_val = readl_relaxed(USB_PHY_CTRL);
+		if (motg->pdata->otg_control == OTG_PHY_CONTROL)
+			/* Enable PHY HV interrupts to wake MPM/Link */
+			phy_ctrl_val |=
+				(PHY_IDHV_INTEN | PHY_OTGSESSVLDHV_INTEN);
 
-	clk_disable(motg->pclk);
-	clk_disable(motg->clk);
-	if (motg->core_clk)
-		clk_disable(motg->core_clk);
-
-	if (!IS_ERR(motg->pclk_src))
-		clk_disable(motg->pclk_src);
-
-	if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY &&
-			motg->pdata->otg_control == OTG_PMIC_CONTROL) {
-		msm_hsusb_ldo_set_mode(0);
-		msm_hsusb_config_vddcx(0);
+		writel_relaxed(phy_ctrl_val & ~PHY_RETEN, USB_PHY_CTRL);
+		motg->lpm_flags |= PHY_RETENTIONED;
 	}
 
-	if (device_may_wakeup(phy->dev))
+	/* Ensure that above operation is completed before turning off clocks */
+	mb();
+	clk_disable_unprepare(motg->pclk);
+	clk_disable_unprepare(motg->core_clk);
+
+	/* usb phy no more require TCXO clock, hence vote for TCXO disable */
+	ret = msm_xo_mode_vote(motg->xo_handle, MSM_XO_MODE_OFF);
+	if (ret)
+		dev_err(phy->dev, "%s failed to devote for "
+			"TCXO D0 buffer%d\n", __func__, ret);
+
+	if (motg->caps & ALLOW_PHY_POWER_COLLAPSE &&
+			!host_bus_suspend && !dcp) {
+		msm_hsusb_ldo_enable(motg, 0);
+		motg->lpm_flags |= PHY_PWR_COLLAPSED;
+	}
+
+	if (motg->lpm_flags & PHY_RETENTIONED) {
+		msm_hsusb_config_vddcx(0);
+		msm_hsusb_mhl_switch_enable(motg, 0);
+	}
+
+	if (device_may_wakeup(phy->dev)) {
 		enable_irq_wake(motg->irq);
+		if (motg->pdata->pmic_id_irq)
+			enable_irq_wake(motg->pdata->pmic_id_irq);
+	}
 	if (bus)
 		clear_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags);
 
 	atomic_set(&motg->in_lpm, 1);
 	enable_irq(motg->irq);
+	wake_unlock(&motg->wlock);
 
 	dev_info(phy->dev, "USB in low power mode\n");
 
@@ -547,23 +877,40 @@
 	struct usb_bus *bus = phy->otg->host;
 	int cnt = 0;
 	unsigned temp;
+	u32 phy_ctrl_val = 0;
+	unsigned ret;
 
 	if (!atomic_read(&motg->in_lpm))
 		return 0;
 
-	if (!IS_ERR(motg->pclk_src))
-		clk_enable(motg->pclk_src);
+	wake_lock(&motg->wlock);
 
-	clk_enable(motg->pclk);
-	clk_enable(motg->clk);
-	if (motg->core_clk)
-		clk_enable(motg->core_clk);
+	/* Vote for TCXO when waking up the phy */
+	ret = msm_xo_mode_vote(motg->xo_handle, MSM_XO_MODE_ON);
+	if (ret)
+		dev_err(phy->dev, "%s failed to vote for "
+			"TCXO D0 buffer%d\n", __func__, ret);
 
-	if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY &&
-			motg->pdata->otg_control == OTG_PMIC_CONTROL) {
-		msm_hsusb_ldo_set_mode(1);
+	clk_prepare_enable(motg->core_clk);
+
+	clk_prepare_enable(motg->pclk);
+
+	if (motg->lpm_flags & PHY_PWR_COLLAPSED) {
+		msm_hsusb_ldo_enable(motg, 1);
+		motg->lpm_flags &= ~PHY_PWR_COLLAPSED;
+	}
+
+	if (motg->lpm_flags & PHY_RETENTIONED) {
+		msm_hsusb_mhl_switch_enable(motg, 1);
 		msm_hsusb_config_vddcx(1);
-		writel(readl(USB_PHY_CTRL) & ~PHY_RETEN, USB_PHY_CTRL);
+		phy_ctrl_val = readl_relaxed(USB_PHY_CTRL);
+		phy_ctrl_val |= PHY_RETEN;
+		if (motg->pdata->otg_control == OTG_PHY_CONTROL)
+			/* Disable PHY HV interrupts */
+			phy_ctrl_val &=
+				~(PHY_IDHV_INTEN | PHY_OTGSESSVLDHV_INTEN);
+		writel_relaxed(phy_ctrl_val, USB_PHY_CTRL);
+		motg->lpm_flags &= ~PHY_RETENTIONED;
 	}
 
 	temp = readl(USB_USBCMD);
@@ -598,8 +945,11 @@
 	}
 
 skip_phy_resume:
-	if (device_may_wakeup(phy->dev))
+	if (device_may_wakeup(phy->dev)) {
 		disable_irq_wake(motg->irq);
+		if (motg->pdata->pmic_id_irq)
+			disable_irq_wake(motg->pdata->pmic_id_irq);
+	}
 	if (bus)
 		set_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags);
 
@@ -607,7 +957,6 @@
 
 	if (motg->async_int) {
 		motg->async_int = 0;
-		pm_runtime_put(phy->dev);
 		enable_irq(motg->irq);
 	}
 
@@ -617,13 +966,91 @@
 }
 #endif
 
+static int msm_otg_notify_chg_type(struct msm_otg *motg)
+{
+	static int charger_type;
+	/*
+	 * TODO
+	 * Unify OTG driver charger types and power supply charger types
+	 */
+	if (charger_type == motg->chg_type)
+		return 0;
+
+	if (motg->chg_type == USB_SDP_CHARGER)
+		charger_type = POWER_SUPPLY_TYPE_USB;
+	else if (motg->chg_type == USB_CDP_CHARGER)
+		charger_type = POWER_SUPPLY_TYPE_USB_CDP;
+	else if (motg->chg_type == USB_DCP_CHARGER)
+		charger_type = POWER_SUPPLY_TYPE_USB_DCP;
+	else if ((motg->chg_type == USB_ACA_DOCK_CHARGER ||
+		motg->chg_type == USB_ACA_A_CHARGER ||
+		motg->chg_type == USB_ACA_B_CHARGER ||
+		motg->chg_type == USB_ACA_C_CHARGER))
+		charger_type = POWER_SUPPLY_TYPE_USB_ACA;
+	else
+		charger_type = POWER_SUPPLY_TYPE_BATTERY;
+
+	return pm8921_set_usb_power_supply_type(charger_type);
+}
+
+static int msm_otg_notify_power_supply(struct msm_otg *motg, unsigned mA)
+{
+
+	if (!psy)
+		goto psy_not_supported;
+
+	if (motg->cur_power == 0 && mA > 0) {
+		/* Enable charging */
+		if (power_supply_set_online(psy, true))
+			goto psy_not_supported;
+	} else if (motg->cur_power > 0 && mA == 0) {
+		/* Disable charging */
+		if (power_supply_set_online(psy, false))
+			goto psy_not_supported;
+		return 0;
+	}
+	/* Set max current limit */
+	if (power_supply_set_current_limit(psy, 1000*mA))
+		goto psy_not_supported;
+
+	return 0;
+
+psy_not_supported:
+	dev_dbg(motg->phy.dev, "Power Supply doesn't support USB charger\n");
+	return -ENXIO;
+}
+
 static void msm_otg_notify_charger(struct msm_otg *motg, unsigned mA)
 {
+	struct usb_gadget *g = motg->phy.otg->gadget;
+
+	if (g && g->is_a_peripheral)
+		return;
+
+	if ((motg->chg_type == USB_ACA_DOCK_CHARGER ||
+		motg->chg_type == USB_ACA_A_CHARGER ||
+		motg->chg_type == USB_ACA_B_CHARGER ||
+		motg->chg_type == USB_ACA_C_CHARGER) &&
+			mA > IDEV_ACA_CHG_LIMIT)
+		mA = IDEV_ACA_CHG_LIMIT;
+
+	if (msm_otg_notify_chg_type(motg))
+		dev_err(motg->phy.dev,
+			"Failed notifying %d charger type to PMIC\n",
+							motg->chg_type);
+
 	if (motg->cur_power == mA)
 		return;
 
-	/* TODO: Notify PMIC about available current */
 	dev_info(motg->phy.dev, "Avail curr from USB = %u\n", mA);
+
+	/*
+	 *  Use Power Supply API if supported, otherwise fallback
+	 *  to legacy pm8921 API.
+	 */
+	if (msm_otg_notify_power_supply(motg, mA))
+		pm8921_charger_vbus_draw(mA);
+
 	motg->cur_power = mA;
 }
 
@@ -644,22 +1071,24 @@
 	return 0;
 }
 
-static void msm_otg_start_host(struct usb_phy *phy, int on)
+static void msm_otg_start_host(struct usb_otg *otg, int on)
 {
-	struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
+	struct msm_otg *motg = container_of(otg->phy, struct msm_otg, phy);
 	struct msm_otg_platform_data *pdata = motg->pdata;
 	struct usb_hcd *hcd;
 
-	if (!phy->otg->host)
+	if (!otg->host)
 		return;
 
-	hcd = bus_to_hcd(phy->otg->host);
+	hcd = bus_to_hcd(otg->host);
 
 	if (on) {
-		dev_dbg(phy->dev, "host on\n");
+		dev_dbg(otg->phy->dev, "host on\n");
 
-		if (pdata->vbus_power)
-			pdata->vbus_power(1);
+		if (pdata->otg_control == OTG_PHY_CONTROL)
+			ulpi_write(otg->phy, OTG_COMP_DISABLE,
+				ULPI_SET(ULPI_PWR_CLK_MNG_REG));
+
 		/*
 		 * Some boards have a switch cotrolled by gpio
 		 * to enable/disable internal HUB. Enable internal
@@ -667,19 +1096,139 @@
 		 */
 		if (pdata->setup_gpio)
 			pdata->setup_gpio(OTG_STATE_A_HOST);
-#ifdef CONFIG_USB
 		usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
-#endif
 	} else {
-		dev_dbg(phy->dev, "host off\n");
+		dev_dbg(otg->phy->dev, "host off\n");
 
-#ifdef CONFIG_USB
 		usb_remove_hcd(hcd);
-#endif
+		/* HCD core reset all bits of PORTSC. select ULPI phy */
+		writel_relaxed(0x80000000, USB_PORTSC);
+
 		if (pdata->setup_gpio)
 			pdata->setup_gpio(OTG_STATE_UNDEFINED);
-		if (pdata->vbus_power)
-			pdata->vbus_power(0);
+
+		if (pdata->otg_control == OTG_PHY_CONTROL)
+			ulpi_write(otg->phy, OTG_COMP_DISABLE,
+				ULPI_CLR(ULPI_PWR_CLK_MNG_REG));
+	}
+}
+
+static int msm_otg_usbdev_notify(struct notifier_block *self,
+			unsigned long action, void *priv)
+{
+	struct msm_otg *motg = container_of(self, struct msm_otg, usbdev_nb);
+	struct usb_otg *otg = motg->phy.otg;
+	struct usb_device *udev = priv;
+
+	if (action == USB_BUS_ADD || action == USB_BUS_REMOVE)
+		goto out;
+
+	if (udev->bus != otg->host)
+		goto out;
+	/*
+	 * Interested in devices connected directly to the root hub.
+	 * ACA dock can supply IDEV_CHG irrespective devices connected
+	 * on the accessory port.
+	 */
+	if (!udev->parent || udev->parent->parent ||
+			motg->chg_type == USB_ACA_DOCK_CHARGER)
+		goto out;
+
+	switch (action) {
+	case USB_DEVICE_ADD:
+		if (aca_enabled())
+			usb_disable_autosuspend(udev);
+		if (otg->phy->state == OTG_STATE_A_WAIT_BCON) {
+			pr_debug("B_CONN set\n");
+			set_bit(B_CONN, &motg->inputs);
+			msm_otg_del_timer(motg);
+			otg->phy->state = OTG_STATE_A_HOST;
+			/*
+			 * OTG PET: A-device must end session within
+			 * 10 sec after PET enumeration.
+			 */
+			if (udev->quirks & USB_QUIRK_OTG_PET)
+				msm_otg_start_timer(motg, TA_TST_MAINT,
+						A_TST_MAINT);
+		}
+		/* fall through */
+	case USB_DEVICE_CONFIG:
+		if (udev->actconfig)
+			motg->mA_port = udev->actconfig->desc.bMaxPower * 2;
+		else
+			motg->mA_port = IUNIT;
+		if (otg->phy->state == OTG_STATE_B_HOST)
+			msm_otg_del_timer(motg);
+		break;
+	case USB_DEVICE_REMOVE:
+		if ((otg->phy->state == OTG_STATE_A_HOST) ||
+			(otg->phy->state == OTG_STATE_A_SUSPEND)) {
+			pr_debug("B_CONN clear\n");
+			clear_bit(B_CONN, &motg->inputs);
+			/*
+			 * OTG PET: A-device must end session after
+			 * PET disconnection if it is enumerated
+			 * with bcdDevice[0] = 1. USB core sets
+			 * bus->otg_vbus_off for us. clear it here.
+			 */
+			if (udev->bus->otg_vbus_off) {
+				udev->bus->otg_vbus_off = 0;
+				set_bit(A_BUS_DROP, &motg->inputs);
+			}
+			queue_work(system_nrt_wq, &motg->sm_work);
+		}
+	default:
+		break;
+	}
+	if (test_bit(ID_A, &motg->inputs))
+		msm_otg_notify_charger(motg, IDEV_ACA_CHG_MAX -
+				motg->mA_port);
+out:
+	return NOTIFY_OK;
+}
+
+static void msm_hsusb_vbus_power(struct msm_otg *motg, bool on)
+{
+	int ret;
+	static bool vbus_is_on;
+
+	if (vbus_is_on == on)
+		return;
+
+	if (motg->pdata->vbus_power) {
+		ret = motg->pdata->vbus_power(on);
+		if (!ret)
+			vbus_is_on = on;
+		return;
+	}
+
+	if (!vbus_otg) {
+		pr_err("vbus_otg is NULL.");
+		return;
+	}
+
+	/*
+	 * if entering host mode tell the charger to not draw any current
+	 * from usb before turning on the boost.
+	 * if exiting host mode disable the boost before enabling to draw
+	 * current from the source.
+	 */
+	if (on) {
+		pm8921_disable_source_current(on);
+		ret = regulator_enable(vbus_otg);
+		if (ret) {
+			pr_err("unable to enable vbus_otg\n");
+			return;
+		}
+		vbus_is_on = true;
+	} else {
+		ret = regulator_disable(vbus_otg);
+		if (ret) {
+			pr_err("unable to disable vbus_otg\n");
+			return;
+		}
+		pm8921_disable_source_current(on);
+		vbus_is_on = false;
 	}
 }
 
@@ -697,13 +1246,23 @@
 		return -ENODEV;
 	}
 
+	if (!motg->pdata->vbus_power && host) {
+		vbus_otg = devm_regulator_get(motg->phy.dev, "vbus_otg");
+		if (IS_ERR(vbus_otg)) {
+			pr_err("Unable to get vbus_otg\n");
+			return -ENODEV;
+		}
+	}
+
 	if (!host) {
 		if (otg->phy->state == OTG_STATE_A_HOST) {
 			pm_runtime_get_sync(otg->phy->dev);
-			msm_otg_start_host(otg->phy, 0);
+			usb_unregister_notify(&motg->usbdev_nb);
+			msm_otg_start_host(otg, 0);
+			msm_hsusb_vbus_power(motg, 0);
 			otg->host = NULL;
 			otg->phy->state = OTG_STATE_UNDEFINED;
-			schedule_work(&motg->sm_work);
+			queue_work(system_nrt_wq, &motg->sm_work);
 		} else {
 			otg->host = NULL;
 		}
@@ -714,6 +1273,11 @@
 	hcd = bus_to_hcd(host);
 	hcd->power_budget = motg->pdata->power_budget;
 
+#ifdef CONFIG_USB_OTG
+	host->otg_port = 1;
+#endif
+	motg->usbdev_nb.notifier_call = msm_otg_usbdev_notify;
+	usb_register_notify(&motg->usbdev_nb);
 	otg->host = host;
 	dev_dbg(otg->phy->dev, "host driver registered w/ tranceiver\n");
 
@@ -723,22 +1287,23 @@
 	 */
 	if (motg->pdata->mode == USB_HOST || otg->gadget) {
 		pm_runtime_get_sync(otg->phy->dev);
-		schedule_work(&motg->sm_work);
+		queue_work(system_nrt_wq, &motg->sm_work);
 	}
 
 	return 0;
 }
 
-static void msm_otg_start_peripheral(struct usb_phy *phy, int on)
+static void msm_otg_start_peripheral(struct usb_otg *otg, int on)
 {
-	struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
+	int ret;
+	struct msm_otg *motg = container_of(otg->phy, struct msm_otg, phy);
 	struct msm_otg_platform_data *pdata = motg->pdata;
 
-	if (!phy->otg->gadget)
+	if (!otg->gadget)
 		return;
 
 	if (on) {
-		dev_dbg(phy->dev, "gadget on\n");
+		dev_dbg(otg->phy->dev, "gadget on\n");
 		/*
 		 * Some boards have a switch cotrolled by gpio
 		 * to enable/disable internal HUB. Disable internal
@@ -746,10 +1311,27 @@
 		 */
 		if (pdata->setup_gpio)
 			pdata->setup_gpio(OTG_STATE_B_PERIPHERAL);
-		usb_gadget_vbus_connect(phy->otg->gadget);
+
+		/* Configure BUS performance parameters for MAX bandwidth */
+		if (motg->bus_perf_client && debug_bus_voting_enabled) {
+			ret = msm_bus_scale_client_update_request(
+					motg->bus_perf_client, 1);
+			if (ret)
+				dev_err(motg->phy.dev, "%s: Failed to vote for "
+					   "bus bandwidth %d\n", __func__, ret);
+		}
+		usb_gadget_vbus_connect(otg->gadget);
 	} else {
-		dev_dbg(phy->dev, "gadget off\n");
-		usb_gadget_vbus_disconnect(phy->otg->gadget);
+		dev_dbg(otg->phy->dev, "gadget off\n");
+		usb_gadget_vbus_disconnect(otg->gadget);
+		/* Configure BUS performance parameters to default */
+		if (motg->bus_perf_client) {
+			ret = msm_bus_scale_client_update_request(
+					motg->bus_perf_client, 0);
+			if (ret)
+				dev_err(motg->phy.dev, "%s: Failed to devote "
+					   "for bus bw %d\n", __func__, ret);
+		}
 		if (pdata->setup_gpio)
 			pdata->setup_gpio(OTG_STATE_UNDEFINED);
 	}
@@ -757,7 +1339,7 @@
 }
 
 static int msm_otg_set_peripheral(struct usb_otg *otg,
-					struct usb_gadget *gadget)
+			struct usb_gadget *gadget)
 {
 	struct msm_otg *motg = container_of(otg->phy, struct msm_otg, phy);
 
@@ -773,10 +1355,10 @@
 	if (!gadget) {
 		if (otg->phy->state == OTG_STATE_B_PERIPHERAL) {
 			pm_runtime_get_sync(otg->phy->dev);
-			msm_otg_start_peripheral(otg->phy, 0);
+			msm_otg_start_peripheral(otg, 0);
 			otg->gadget = NULL;
 			otg->phy->state = OTG_STATE_UNDEFINED;
-			schedule_work(&motg->sm_work);
+			queue_work(system_nrt_wq, &motg->sm_work);
 		} else {
 			otg->gadget = NULL;
 		}
@@ -792,12 +1374,190 @@
 	 */
 	if (motg->pdata->mode == USB_PERIPHERAL || otg->host) {
 		pm_runtime_get_sync(otg->phy->dev);
-		schedule_work(&motg->sm_work);
+		queue_work(system_nrt_wq, &motg->sm_work);
 	}
 
 	return 0;
 }
 
+static bool msm_chg_aca_detect(struct msm_otg *motg)
+{
+	struct usb_phy *phy = &motg->phy;
+	u32 int_sts;
+	bool ret = false;
+
+	if (!aca_enabled())
+		goto out;
+
+	if (motg->pdata->phy_type == CI_45NM_INTEGRATED_PHY)
+		goto out;
+
+	int_sts = ulpi_read(phy, 0x87);
+	switch (int_sts & 0x1C) {
+	case 0x08:
+		if (!test_and_set_bit(ID_A, &motg->inputs)) {
+			dev_dbg(phy->dev, "ID_A\n");
+			motg->chg_type = USB_ACA_A_CHARGER;
+			motg->chg_state = USB_CHG_STATE_DETECTED;
+			clear_bit(ID_B, &motg->inputs);
+			clear_bit(ID_C, &motg->inputs);
+			set_bit(ID, &motg->inputs);
+			ret = true;
+		}
+		break;
+	case 0x0C:
+		if (!test_and_set_bit(ID_B, &motg->inputs)) {
+			dev_dbg(phy->dev, "ID_B\n");
+			motg->chg_type = USB_ACA_B_CHARGER;
+			motg->chg_state = USB_CHG_STATE_DETECTED;
+			clear_bit(ID_A, &motg->inputs);
+			clear_bit(ID_C, &motg->inputs);
+			set_bit(ID, &motg->inputs);
+			ret = true;
+		}
+		break;
+	case 0x10:
+		if (!test_and_set_bit(ID_C, &motg->inputs)) {
+			dev_dbg(phy->dev, "ID_C\n");
+			motg->chg_type = USB_ACA_C_CHARGER;
+			motg->chg_state = USB_CHG_STATE_DETECTED;
+			clear_bit(ID_A, &motg->inputs);
+			clear_bit(ID_B, &motg->inputs);
+			set_bit(ID, &motg->inputs);
+			ret = true;
+		}
+		break;
+	case 0x04:
+		if (test_and_clear_bit(ID, &motg->inputs)) {
+			dev_dbg(phy->dev, "ID_GND\n");
+			motg->chg_type = USB_INVALID_CHARGER;
+			motg->chg_state = USB_CHG_STATE_UNDEFINED;
+			clear_bit(ID_A, &motg->inputs);
+			clear_bit(ID_B, &motg->inputs);
+			clear_bit(ID_C, &motg->inputs);
+			ret = true;
+		}
+		break;
+	default:
+		ret = test_and_clear_bit(ID_A, &motg->inputs) |
+			test_and_clear_bit(ID_B, &motg->inputs) |
+			test_and_clear_bit(ID_C, &motg->inputs) |
+			!test_and_set_bit(ID, &motg->inputs);
+		if (ret) {
+			dev_dbg(phy->dev, "ID A/B/C/GND is no more\n");
+			motg->chg_type = USB_INVALID_CHARGER;
+			motg->chg_state = USB_CHG_STATE_UNDEFINED;
+		}
+	}
+out:
+	return ret;
+}
+
+static void msm_chg_enable_aca_det(struct msm_otg *motg)
+{
+	struct usb_phy *phy = &motg->phy;
+
+	if (!aca_enabled())
+		return;
+
+	switch (motg->pdata->phy_type) {
+	case SNPS_28NM_INTEGRATED_PHY:
+		/* Disable ID_GND in link and PHY */
+		writel_relaxed(readl_relaxed(USB_OTGSC) & ~(OTGSC_IDPU |
+				OTGSC_IDIE), USB_OTGSC);
+		ulpi_write(phy, 0x01, 0x0C);
+		ulpi_write(phy, 0x10, 0x0F);
+		ulpi_write(phy, 0x10, 0x12);
+		/* Disable PMIC ID pull-up */
+		pm8xxx_usb_id_pullup(0);
+		/* Enable ACA ID detection */
+		ulpi_write(phy, 0x20, 0x85);
+		aca_id_turned_on = true;
+		break;
+	default:
+		break;
+	}
+}
+
+static void msm_chg_enable_aca_intr(struct msm_otg *motg)
+{
+	struct usb_phy *phy = &motg->phy;
+
+	if (!aca_enabled())
+		return;
+
+	switch (motg->pdata->phy_type) {
+	case SNPS_28NM_INTEGRATED_PHY:
+		/* Enable ACA Detection interrupt (on any RID change) */
+		ulpi_write(phy, 0x01, 0x94);
+		break;
+	default:
+		break;
+	}
+}
+
+static void msm_chg_disable_aca_intr(struct msm_otg *motg)
+{
+	struct usb_phy *phy = &motg->phy;
+
+	if (!aca_enabled())
+		return;
+
+	switch (motg->pdata->phy_type) {
+	case SNPS_28NM_INTEGRATED_PHY:
+		ulpi_write(phy, 0x01, 0x95);
+		break;
+	default:
+		break;
+	}
+}
+
+static bool msm_chg_check_aca_intr(struct msm_otg *motg)
+{
+	struct usb_phy *phy = &motg->phy;
+	bool ret = false;
+
+	if (!aca_enabled())
+		return ret;
+
+	switch (motg->pdata->phy_type) {
+	case SNPS_28NM_INTEGRATED_PHY:
+		if (ulpi_read(phy, 0x91) & 1) {
+			dev_dbg(phy->dev, "RID change\n");
+			ulpi_write(phy, 0x01, 0x92);
+			ret = msm_chg_aca_detect(motg);
+		}
+	default:
+		break;
+	}
+	return ret;
+}
+
+static void msm_otg_id_timer_func(unsigned long data)
+{
+	struct msm_otg *motg = (struct msm_otg *) data;
+
+	if (!aca_enabled())
+		return;
+
+	if (atomic_read(&motg->in_lpm)) {
+		dev_dbg(motg->phy.dev, "timer: in lpm\n");
+		return;
+	}
+
+	if (motg->phy.state == OTG_STATE_A_SUSPEND)
+		goto out;
+
+	if (msm_chg_check_aca_intr(motg)) {
+		dev_dbg(motg->phy.dev, "timer: aca work\n");
+		queue_work(system_nrt_wq, &motg->sm_work);
+	}
+
+out:
+	if (!test_bit(ID, &motg->inputs) || test_bit(ID_A, &motg->inputs))
+		mod_timer(&motg->id_timer, ID_TIMER_FREQ);
+}
+
 static bool msm_chg_check_secondary_det(struct msm_otg *motg)
 {
 	struct usb_phy *phy = &motg->phy;
@@ -846,6 +1606,9 @@
 		ulpi_write(phy, chg_det, 0x34);
 		break;
 	case SNPS_28NM_INTEGRATED_PHY:
+		/* Turn off VDP_SRC */
+		ulpi_write(phy, 0x3, 0x86);
+		msleep(20);
 		/*
 		 * Configure DM as current source, DP as current sink
 		 * and enable battery charging comparators.
@@ -990,7 +1753,7 @@
 		break;
 	case SNPS_28NM_INTEGRATED_PHY:
 		/* Clear charger detecting control bits */
-		ulpi_write(phy, 0x3F, 0x86);
+		ulpi_write(phy, 0x1F, 0x86);
 		/* Clear alt interrupt latch and enable bits */
 		ulpi_write(phy, 0x1F, 0x92);
 		ulpi_write(phy, 0x1F, 0x95);
@@ -1031,32 +1794,62 @@
 	ulpi_write(phy, func_ctrl, ULPI_FUNC_CTRL);
 }
 
+static const char *chg_to_string(enum usb_chg_type chg_type)
+{
+	switch (chg_type) {
+	case USB_SDP_CHARGER:		return "USB_SDP_CHARGER";
+	case USB_DCP_CHARGER:		return "USB_DCP_CHARGER";
+	case USB_CDP_CHARGER:		return "USB_CDP_CHARGER";
+	case USB_ACA_A_CHARGER:		return "USB_ACA_A_CHARGER";
+	case USB_ACA_B_CHARGER:		return "USB_ACA_B_CHARGER";
+	case USB_ACA_C_CHARGER:		return "USB_ACA_C_CHARGER";
+	case USB_ACA_DOCK_CHARGER:	return "USB_ACA_DOCK_CHARGER";
+	default:			return "INVALID_CHARGER";
+	}
+}
+
 #define MSM_CHG_DCD_POLL_TIME		(100 * HZ/1000) /* 100 msec */
 #define MSM_CHG_DCD_MAX_RETRIES		6 /* Tdcd_tmout = 6 * 100 msec */
-#define MSM_CHG_PRIMARY_DET_TIME	(40 * HZ/1000) /* TVDPSRC_ON */
-#define MSM_CHG_SECONDARY_DET_TIME	(40 * HZ/1000) /* TVDMSRC_ON */
+#define MSM_CHG_PRIMARY_DET_TIME	(50 * HZ/1000) /* TVDPSRC_ON */
+#define MSM_CHG_SECONDARY_DET_TIME	(50 * HZ/1000) /* TVDMSRC_ON */
 static void msm_chg_detect_work(struct work_struct *w)
 {
 	struct msm_otg *motg = container_of(w, struct msm_otg, chg_work.work);
 	struct usb_phy *phy = &motg->phy;
-	bool is_dcd, tmout, vout;
+	bool is_dcd = false, tmout, vout, is_aca;
 	unsigned long delay;
 
 	dev_dbg(phy->dev, "chg detection work\n");
 	switch (motg->chg_state) {
 	case USB_CHG_STATE_UNDEFINED:
-		pm_runtime_get_sync(phy->dev);
 		msm_chg_block_on(motg);
-		msm_chg_enable_dcd(motg);
+		if (motg->pdata->enable_dcd)
+			msm_chg_enable_dcd(motg);
+		msm_chg_enable_aca_det(motg);
 		motg->chg_state = USB_CHG_STATE_WAIT_FOR_DCD;
 		motg->dcd_retries = 0;
 		delay = MSM_CHG_DCD_POLL_TIME;
 		break;
 	case USB_CHG_STATE_WAIT_FOR_DCD:
-		is_dcd = msm_chg_check_dcd(motg);
+		is_aca = msm_chg_aca_detect(motg);
+		if (is_aca) {
+			/*
+			 * ID_A can be ACA dock too. continue
+			 * primary detection after DCD.
+			 */
+			if (test_bit(ID_A, &motg->inputs)) {
+				motg->chg_state = USB_CHG_STATE_WAIT_FOR_DCD;
+			} else {
+				delay = 0;
+				break;
+			}
+		}
+		if (motg->pdata->enable_dcd)
+			is_dcd = msm_chg_check_dcd(motg);
 		tmout = ++motg->dcd_retries == MSM_CHG_DCD_MAX_RETRIES;
 		if (is_dcd || tmout) {
-			msm_chg_disable_dcd(motg);
+			if (motg->pdata->enable_dcd)
+				msm_chg_disable_dcd(motg);
 			msm_chg_enable_primary_det(motg);
 			delay = MSM_CHG_PRIMARY_DET_TIME;
 			motg->chg_state = USB_CHG_STATE_DCD_DONE;
@@ -1067,10 +1860,22 @@
 	case USB_CHG_STATE_DCD_DONE:
 		vout = msm_chg_check_primary_det(motg);
 		if (vout) {
+			if (test_bit(ID_A, &motg->inputs)) {
+				motg->chg_type = USB_ACA_DOCK_CHARGER;
+				motg->chg_state = USB_CHG_STATE_DETECTED;
+				delay = 0;
+				break;
+			}
 			msm_chg_enable_secondary_det(motg);
 			delay = MSM_CHG_SECONDARY_DET_TIME;
 			motg->chg_state = USB_CHG_STATE_PRIMARY_DONE;
 		} else {
+			if (test_bit(ID_A, &motg->inputs)) {
+				motg->chg_type = USB_ACA_A_CHARGER;
+				motg->chg_state = USB_CHG_STATE_DETECTED;
+				delay = 0;
+				break;
+			}
 			motg->chg_type = USB_SDP_CHARGER;
 			motg->chg_state = USB_CHG_STATE_DETECTED;
 			delay = 0;
@@ -1088,14 +1893,23 @@
 		motg->chg_state = USB_CHG_STATE_DETECTED;
 	case USB_CHG_STATE_DETECTED:
 		msm_chg_block_off(motg);
-		dev_dbg(phy->dev, "charger = %d\n", motg->chg_type);
-		schedule_work(&motg->sm_work);
+		msm_chg_enable_aca_det(motg);
+		/*
+		 * Spurious interrupt is seen after enabling ACA detection
+		 * due to which charger detection fails in case of PET.
+		 * Add delay of 100 microsec to avoid that.
+		 */
+		udelay(100);
+		msm_chg_enable_aca_intr(motg);
+		dev_dbg(phy->dev, "chg_type = %s\n",
+			chg_to_string(motg->chg_type));
+		queue_work(system_nrt_wq, &motg->sm_work);
 		return;
 	default:
 		return;
 	}
 
-	schedule_delayed_work(&motg->chg_work, delay);
+	queue_delayed_work(system_nrt_wq, &motg->chg_work, delay);
 }
 
 /*
@@ -1112,17 +1926,7 @@
 
 	switch (pdata->mode) {
 	case USB_OTG:
-		if (pdata->otg_control == OTG_PHY_CONTROL) {
-			if (otgsc & OTGSC_ID)
-				set_bit(ID, &motg->inputs);
-			else
-				clear_bit(ID, &motg->inputs);
-
-			if (otgsc & OTGSC_BSV)
-				set_bit(B_SESS_VLD, &motg->inputs);
-			else
-				clear_bit(B_SESS_VLD, &motg->inputs);
-		} else if (pdata->otg_control == OTG_USER_CONTROL) {
+		if (pdata->otg_control == OTG_USER_CONTROL) {
 			if (pdata->default_mode == USB_HOST) {
 				clear_bit(ID, &motg->inputs);
 			} else if (pdata->default_mode == USB_PERIPHERAL) {
@@ -1132,6 +1936,32 @@
 				set_bit(ID, &motg->inputs);
 				clear_bit(B_SESS_VLD, &motg->inputs);
 			}
+		} else if (pdata->otg_control == OTG_PHY_CONTROL) {
+			if (otgsc & OTGSC_ID) {
+				set_bit(ID, &motg->inputs);
+			} else {
+				clear_bit(ID, &motg->inputs);
+				set_bit(A_BUS_REQ, &motg->inputs);
+			}
+			if (otgsc & OTGSC_BSV)
+				set_bit(B_SESS_VLD, &motg->inputs);
+			else
+				clear_bit(B_SESS_VLD, &motg->inputs);
+		} else if (pdata->otg_control == OTG_PMIC_CONTROL) {
+			if (pdata->pmic_id_irq) {
+				unsigned long flags;
+				local_irq_save(flags);
+				if (irq_read_line(pdata->pmic_id_irq))
+					set_bit(ID, &motg->inputs);
+				else
+					clear_bit(ID, &motg->inputs);
+				local_irq_restore(flags);
+			}
+			/*
+			 * VBUS initial state is reported after PMIC
+			 * driver initialization. Wait for it.
+			 */
+			wait_for_completion(&pmic_vbus_init);
 		}
 		break;
 	case USB_HOST:
@@ -1139,10 +1969,18 @@
 		break;
 	case USB_PERIPHERAL:
 		set_bit(ID, &motg->inputs);
-		if (otgsc & OTGSC_BSV)
-			set_bit(B_SESS_VLD, &motg->inputs);
-		else
-			clear_bit(B_SESS_VLD, &motg->inputs);
+		if (pdata->otg_control == OTG_PHY_CONTROL) {
+			if (otgsc & OTGSC_BSV)
+				set_bit(B_SESS_VLD, &motg->inputs);
+			else
+				clear_bit(B_SESS_VLD, &motg->inputs);
+		} else if (pdata->otg_control == OTG_PMIC_CONTROL) {
+			/*
+			 * VBUS initial state is reported after PMIC
+			 * driver initialization. Wait for it.
+			 */
+			wait_for_completion(&pmic_vbus_init);
+		}
 		break;
 	default:
 		break;
@@ -1153,22 +1991,35 @@
 {
 	struct msm_otg *motg = container_of(w, struct msm_otg, sm_work);
 	struct usb_otg *otg = motg->phy.otg;
+	bool work = 0, srp_reqd;
 
+	pm_runtime_resume(otg->phy->dev);
+	pr_debug("%s work\n", otg_state_string(otg->phy->state));
 	switch (otg->phy->state) {
 	case OTG_STATE_UNDEFINED:
-		dev_dbg(otg->phy->dev, "OTG_STATE_UNDEFINED state\n");
 		msm_otg_reset(otg->phy);
 		msm_otg_init_sm(motg);
+		psy = power_supply_get_by_name("usb");
+		if (!psy)
+			pr_err("couldn't get usb power supply\n");
 		otg->phy->state = OTG_STATE_B_IDLE;
+		if (!test_bit(B_SESS_VLD, &motg->inputs) &&
+				test_bit(ID, &motg->inputs)) {
+			pm_runtime_put_noidle(otg->phy->dev);
+			pm_runtime_suspend(otg->phy->dev);
+			break;
+		}
 		/* FALL THROUGH */
 	case OTG_STATE_B_IDLE:
-		dev_dbg(otg->phy->dev, "OTG_STATE_B_IDLE state\n");
-		if (!test_bit(ID, &motg->inputs) && otg->host) {
-			/* disable BSV bit */
-			writel(readl(USB_OTGSC) & ~OTGSC_BSVIE, USB_OTGSC);
-			msm_otg_start_host(otg->phy, 1);
-			otg->phy->state = OTG_STATE_A_HOST;
+		if ((!test_bit(ID, &motg->inputs) ||
+				test_bit(ID_A, &motg->inputs)) && otg->host) {
+			pr_debug("!id || id_A\n");
+			clear_bit(B_BUS_REQ, &motg->inputs);
+			set_bit(A_BUS_REQ, &motg->inputs);
+			otg->phy->state = OTG_STATE_A_IDLE;
+			work = 1;
 		} else if (test_bit(B_SESS_VLD, &motg->inputs)) {
+			pr_debug("b_sess_vld\n");
 			switch (motg->chg_state) {
 			case USB_CHG_STATE_UNDEFINED:
 				msm_chg_detect_work(&motg->chg_work.work);
@@ -1176,21 +2027,39 @@
 			case USB_CHG_STATE_DETECTED:
 				switch (motg->chg_type) {
 				case USB_DCP_CHARGER:
+					/* Enable VDP_SRC */
+					ulpi_write(otg->phy, 0x2, 0x85);
 					msm_otg_notify_charger(motg,
 							IDEV_CHG_MAX);
+					pm_runtime_put_noidle(otg->phy->dev);
+					pm_runtime_suspend(otg->phy->dev);
+					break;
+				case USB_ACA_B_CHARGER:
+					msm_otg_notify_charger(motg,
+							IDEV_ACA_CHG_MAX);
+					/*
+					 * (ID_B --> ID_C) PHY_ALT interrupt can
+					 * not be detected in LPM.
+					 */
 					break;
 				case USB_CDP_CHARGER:
 					msm_otg_notify_charger(motg,
 							IDEV_CHG_MAX);
-					msm_otg_start_peripheral(otg->phy, 1);
-					otg->phy->state
-						= OTG_STATE_B_PERIPHERAL;
+					msm_otg_start_peripheral(otg, 1);
+					otg->phy->state =
+						OTG_STATE_B_PERIPHERAL;
+					break;
+				case USB_ACA_C_CHARGER:
+					msm_otg_notify_charger(motg,
+							IDEV_ACA_CHG_MAX);
+					msm_otg_start_peripheral(otg, 1);
+					otg->phy->state =
+						OTG_STATE_B_PERIPHERAL;
 					break;
 				case USB_SDP_CHARGER:
-					msm_otg_notify_charger(motg, IUNIT);
-					msm_otg_start_peripheral(otg->phy, 1);
-					otg->phy->state
-						= OTG_STATE_B_PERIPHERAL;
+					msm_otg_start_peripheral(otg, 1);
+					otg->phy->state =
+						OTG_STATE_B_PERIPHERAL;
 					break;
 				default:
 					break;
@@ -1199,93 +2068,692 @@
 			default:
 				break;
 			}
-		} else {
-			/*
-			 * If charger detection work is pending, decrement
-			 * the pm usage counter to balance with the one that
-			 * is incremented in charger detection work.
-			 */
-			if (cancel_delayed_work_sync(&motg->chg_work)) {
-				pm_runtime_put_sync(otg->phy->dev);
-				msm_otg_reset(otg->phy);
+		} else if (test_bit(B_BUS_REQ, &motg->inputs)) {
+			pr_debug("b_sess_end && b_bus_req\n");
+			if (msm_otg_start_srp(otg) < 0) {
+				clear_bit(B_BUS_REQ, &motg->inputs);
+				work = 1;
+				break;
 			}
-			msm_otg_notify_charger(motg, 0);
+			otg->phy->state = OTG_STATE_B_SRP_INIT;
+			msm_otg_start_timer(motg, TB_SRP_FAIL, B_SRP_FAIL);
+			break;
+		} else {
+			pr_debug("chg_work cancel");
+			cancel_delayed_work_sync(&motg->chg_work);
 			motg->chg_state = USB_CHG_STATE_UNDEFINED;
 			motg->chg_type = USB_INVALID_CHARGER;
+			msm_otg_notify_charger(motg, 0);
+			msm_otg_reset(otg->phy);
+			pm_runtime_put_noidle(otg->phy->dev);
+			pm_runtime_suspend(otg->phy->dev);
 		}
-		pm_runtime_put_sync(otg->phy->dev);
+		break;
+	case OTG_STATE_B_SRP_INIT:
+		if (!test_bit(ID, &motg->inputs) ||
+				test_bit(ID_A, &motg->inputs) ||
+				test_bit(ID_C, &motg->inputs) ||
+				(test_bit(B_SESS_VLD, &motg->inputs) &&
+				!test_bit(ID_B, &motg->inputs))) {
+			pr_debug("!id || id_a/c || b_sess_vld+!id_b\n");
+			msm_otg_del_timer(motg);
+			otg->phy->state = OTG_STATE_B_IDLE;
+			/*
+			 * clear VBUSVLDEXTSEL and VBUSVLDEXT register
+			 * bits after SRP initiation.
+			 */
+			ulpi_write(otg->phy, 0x0, 0x98);
+			work = 1;
+		} else if (test_bit(B_SRP_FAIL, &motg->tmouts)) {
+			pr_debug("b_srp_fail\n");
+			pr_info("A-device did not respond to SRP\n");
+			clear_bit(B_BUS_REQ, &motg->inputs);
+			clear_bit(B_SRP_FAIL, &motg->tmouts);
+			otg_send_event(OTG_EVENT_NO_RESP_FOR_SRP);
+			ulpi_write(otg->phy, 0x0, 0x98);
+			otg->phy->state = OTG_STATE_B_IDLE;
+			motg->b_last_se0_sess = jiffies;
+			work = 1;
+		}
 		break;
 	case OTG_STATE_B_PERIPHERAL:
-		dev_dbg(otg->phy->dev, "OTG_STATE_B_PERIPHERAL state\n");
-		if (!test_bit(B_SESS_VLD, &motg->inputs) ||
-				!test_bit(ID, &motg->inputs)) {
-			msm_otg_notify_charger(motg, 0);
-			msm_otg_start_peripheral(otg->phy, 0);
+		if (!test_bit(ID, &motg->inputs) ||
+				test_bit(ID_A, &motg->inputs) ||
+				test_bit(ID_B, &motg->inputs) ||
+				!test_bit(B_SESS_VLD, &motg->inputs)) {
+			pr_debug("!id  || id_a/b || !b_sess_vld\n");
 			motg->chg_state = USB_CHG_STATE_UNDEFINED;
 			motg->chg_type = USB_INVALID_CHARGER;
+			msm_otg_notify_charger(motg, 0);
+			srp_reqd = otg->gadget->otg_srp_reqd;
+			msm_otg_start_peripheral(otg, 0);
+			if (test_bit(ID_B, &motg->inputs))
+				clear_bit(ID_B, &motg->inputs);
+			clear_bit(B_BUS_REQ, &motg->inputs);
+			otg->phy->state = OTG_STATE_B_IDLE;
+			motg->b_last_se0_sess = jiffies;
+			if (srp_reqd)
+				msm_otg_start_timer(motg,
+					TB_TST_SRP, B_TST_SRP);
+			else
+				work = 1;
+		} else if (test_bit(B_BUS_REQ, &motg->inputs) &&
+				otg->gadget->b_hnp_enable &&
+				test_bit(A_BUS_SUSPEND, &motg->inputs)) {
+			pr_debug("b_bus_req && b_hnp_en && a_bus_suspend\n");
+			msm_otg_start_timer(motg, TB_ASE0_BRST, B_ASE0_BRST);
+			/* D+ pullup should not be disconnected within 4msec
+			 * after A device suspends the bus. Otherwise PET will
+			 * fail the compliance test.
+			 */
+			udelay(1000);
+			msm_otg_start_peripheral(otg, 0);
+			otg->phy->state = OTG_STATE_B_WAIT_ACON;
+			/*
+			 * start HCD even before A-device enable
+			 * pull-up to meet HNP timings.
+			 */
+			otg->host->is_b_host = 1;
+			msm_otg_start_host(otg, 1);
+		} else if (test_bit(A_BUS_SUSPEND, &motg->inputs) &&
+				   test_bit(B_SESS_VLD, &motg->inputs)) {
+			pr_debug("a_bus_suspend && b_sess_vld\n");
+			if (motg->caps & ALLOW_LPM_ON_DEV_SUSPEND) {
+				pm_runtime_put_noidle(otg->phy->dev);
+				pm_runtime_suspend(otg->phy->dev);
+			}
+		} else if (test_bit(ID_C, &motg->inputs)) {
+			msm_otg_notify_charger(motg, IDEV_ACA_CHG_MAX);
+		}
+		break;
+	case OTG_STATE_B_WAIT_ACON:
+		if (!test_bit(ID, &motg->inputs) ||
+				test_bit(ID_A, &motg->inputs) ||
+				test_bit(ID_B, &motg->inputs) ||
+				!test_bit(B_SESS_VLD, &motg->inputs)) {
+			pr_debug("!id || id_a/b || !b_sess_vld\n");
+			msm_otg_del_timer(motg);
+			/*
+			 * A-device is physically disconnected during
+			 * HNP. Remove HCD.
+			 */
+			msm_otg_start_host(otg, 0);
+			otg->host->is_b_host = 0;
+
+			clear_bit(B_BUS_REQ, &motg->inputs);
+			clear_bit(A_BUS_SUSPEND, &motg->inputs);
+			motg->b_last_se0_sess = jiffies;
 			otg->phy->state = OTG_STATE_B_IDLE;
 			msm_otg_reset(otg->phy);
-			schedule_work(w);
+			work = 1;
+		} else if (test_bit(A_CONN, &motg->inputs)) {
+			pr_debug("a_conn\n");
+			clear_bit(A_BUS_SUSPEND, &motg->inputs);
+			otg->phy->state = OTG_STATE_B_HOST;
+			/*
+			 * PET disconnects D+ pullup after reset is generated
+			 * by B device in B_HOST role which is not detected by
+			 * B device. As workaorund , start timer of 300msec
+			 * and stop timer if A device is enumerated else clear
+			 * A_CONN.
+			 */
+			msm_otg_start_timer(motg, TB_TST_CONFIG,
+						B_TST_CONFIG);
+		} else if (test_bit(B_ASE0_BRST, &motg->tmouts)) {
+			pr_debug("b_ase0_brst_tmout\n");
+			pr_info("B HNP fail:No response from A device\n");
+			msm_otg_start_host(otg, 0);
+			msm_otg_reset(otg->phy);
+			otg->host->is_b_host = 0;
+			clear_bit(B_ASE0_BRST, &motg->tmouts);
+			clear_bit(A_BUS_SUSPEND, &motg->inputs);
+			clear_bit(B_BUS_REQ, &motg->inputs);
+			otg_send_event(OTG_EVENT_HNP_FAILED);
+			otg->phy->state = OTG_STATE_B_IDLE;
+			work = 1;
+		} else if (test_bit(ID_C, &motg->inputs)) {
+			msm_otg_notify_charger(motg, IDEV_ACA_CHG_MAX);
+		}
+		break;
+	case OTG_STATE_B_HOST:
+		if (!test_bit(B_BUS_REQ, &motg->inputs) ||
+				!test_bit(A_CONN, &motg->inputs) ||
+				!test_bit(B_SESS_VLD, &motg->inputs)) {
+			pr_debug("!b_bus_req || !a_conn || !b_sess_vld\n");
+			clear_bit(A_CONN, &motg->inputs);
+			clear_bit(B_BUS_REQ, &motg->inputs);
+			msm_otg_start_host(otg, 0);
+			otg->host->is_b_host = 0;
+			otg->phy->state = OTG_STATE_B_IDLE;
+			msm_otg_reset(otg->phy);
+			work = 1;
+		} else if (test_bit(ID_C, &motg->inputs)) {
+			msm_otg_notify_charger(motg, IDEV_ACA_CHG_MAX);
+		}
+		break;
+	case OTG_STATE_A_IDLE:
+		otg->default_a = 1;
+		if (test_bit(ID, &motg->inputs) &&
+			!test_bit(ID_A, &motg->inputs)) {
+			pr_debug("id && !id_a\n");
+			otg->default_a = 0;
+			clear_bit(A_BUS_DROP, &motg->inputs);
+			otg->phy->state = OTG_STATE_B_IDLE;
+			del_timer_sync(&motg->id_timer);
+			msm_otg_link_reset(motg);
+			msm_chg_enable_aca_intr(motg);
+			msm_otg_notify_charger(motg, 0);
+			work = 1;
+		} else if (!test_bit(A_BUS_DROP, &motg->inputs) &&
+				(test_bit(A_SRP_DET, &motg->inputs) ||
+				 test_bit(A_BUS_REQ, &motg->inputs))) {
+			pr_debug("!a_bus_drop && (a_srp_det || a_bus_req)\n");
+
+			clear_bit(A_SRP_DET, &motg->inputs);
+			/* Disable SRP detection */
+			writel_relaxed((readl_relaxed(USB_OTGSC) &
+					~OTGSC_INTSTS_MASK) &
+					~OTGSC_DPIE, USB_OTGSC);
+
+			otg->phy->state = OTG_STATE_A_WAIT_VRISE;
+			/* VBUS should not be supplied before end of SRP pulse
+			 * generated by PET, if not complaince test fail.
+			 */
+			usleep_range(10000, 12000);
+			/* ACA: ID_A: Stop charging untill enumeration */
+			if (test_bit(ID_A, &motg->inputs))
+				msm_otg_notify_charger(motg, 0);
+			else
+				msm_hsusb_vbus_power(motg, 1);
+			msm_otg_start_timer(motg, TA_WAIT_VRISE, A_WAIT_VRISE);
+		} else {
+			pr_debug("No session requested\n");
+			clear_bit(A_BUS_DROP, &motg->inputs);
+			if (test_bit(ID_A, &motg->inputs)) {
+					msm_otg_notify_charger(motg,
+							IDEV_ACA_CHG_MAX);
+			} else if (!test_bit(ID, &motg->inputs)) {
+				msm_otg_notify_charger(motg, 0);
+				/*
+				 * A-device is not providing power on VBUS.
+				 * Enable SRP detection.
+				 */
+				writel_relaxed(0x13, USB_USBMODE);
+				writel_relaxed((readl_relaxed(USB_OTGSC) &
+						~OTGSC_INTSTS_MASK) |
+						OTGSC_DPIE, USB_OTGSC);
+				mb();
+			}
+		}
+		break;
+	case OTG_STATE_A_WAIT_VRISE:
+		if ((test_bit(ID, &motg->inputs) &&
+				!test_bit(ID_A, &motg->inputs)) ||
+				test_bit(A_BUS_DROP, &motg->inputs) ||
+				test_bit(A_WAIT_VRISE, &motg->tmouts)) {
+			pr_debug("id || a_bus_drop || a_wait_vrise_tmout\n");
+			clear_bit(A_BUS_REQ, &motg->inputs);
+			msm_otg_del_timer(motg);
+			msm_hsusb_vbus_power(motg, 0);
+			otg->phy->state = OTG_STATE_A_WAIT_VFALL;
+			msm_otg_start_timer(motg, TA_WAIT_VFALL, A_WAIT_VFALL);
+		} else if (test_bit(A_VBUS_VLD, &motg->inputs)) {
+			pr_debug("a_vbus_vld\n");
+			otg->phy->state = OTG_STATE_A_WAIT_BCON;
+			if (TA_WAIT_BCON > 0)
+				msm_otg_start_timer(motg, TA_WAIT_BCON,
+					A_WAIT_BCON);
+			msm_otg_start_host(otg, 1);
+			msm_chg_enable_aca_det(motg);
+			msm_chg_disable_aca_intr(motg);
+			mod_timer(&motg->id_timer, ID_TIMER_FREQ);
+			if (msm_chg_check_aca_intr(motg))
+				work = 1;
+		}
+		break;
+	case OTG_STATE_A_WAIT_BCON:
+		if ((test_bit(ID, &motg->inputs) &&
+				!test_bit(ID_A, &motg->inputs)) ||
+				test_bit(A_BUS_DROP, &motg->inputs) ||
+				test_bit(A_WAIT_BCON, &motg->tmouts)) {
+			pr_debug("(id && id_a/b/c) || a_bus_drop ||"
+					"a_wait_bcon_tmout\n");
+			if (test_bit(A_WAIT_BCON, &motg->tmouts)) {
+				pr_info("Device No Response\n");
+				otg_send_event(OTG_EVENT_DEV_CONN_TMOUT);
+			}
+			msm_otg_del_timer(motg);
+			clear_bit(A_BUS_REQ, &motg->inputs);
+			clear_bit(B_CONN, &motg->inputs);
+			msm_otg_start_host(otg, 0);
+			/*
+			 * ACA: ID_A with NO accessory, just the A plug is
+			 * attached to ACA: Use IDCHG_MAX for charging
+			 */
+			if (test_bit(ID_A, &motg->inputs))
+				msm_otg_notify_charger(motg, IDEV_CHG_MIN);
+			else
+				msm_hsusb_vbus_power(motg, 0);
+			otg->phy->state = OTG_STATE_A_WAIT_VFALL;
+			msm_otg_start_timer(motg, TA_WAIT_VFALL, A_WAIT_VFALL);
+		} else if (!test_bit(A_VBUS_VLD, &motg->inputs)) {
+			pr_debug("!a_vbus_vld\n");
+			clear_bit(B_CONN, &motg->inputs);
+			msm_otg_del_timer(motg);
+			msm_otg_start_host(otg, 0);
+			otg->phy->state = OTG_STATE_A_VBUS_ERR;
+			msm_otg_reset(otg->phy);
+		} else if (test_bit(ID_A, &motg->inputs)) {
+			msm_hsusb_vbus_power(motg, 0);
+		} else if (!test_bit(A_BUS_REQ, &motg->inputs)) {
+			/*
+			 * If TA_WAIT_BCON is infinite, we don;t
+			 * turn off VBUS. Enter low power mode.
+			 */
+			if (TA_WAIT_BCON < 0)
+				pm_runtime_put_sync(otg->phy->dev);
+		} else if (!test_bit(ID, &motg->inputs)) {
+			msm_hsusb_vbus_power(motg, 1);
 		}
 		break;
 	case OTG_STATE_A_HOST:
-		dev_dbg(otg->phy->dev, "OTG_STATE_A_HOST state\n");
-		if (test_bit(ID, &motg->inputs)) {
-			msm_otg_start_host(otg->phy, 0);
-			otg->phy->state = OTG_STATE_B_IDLE;
+		if ((test_bit(ID, &motg->inputs) &&
+				!test_bit(ID_A, &motg->inputs)) ||
+				test_bit(A_BUS_DROP, &motg->inputs)) {
+			pr_debug("id_a/b/c || a_bus_drop\n");
+			clear_bit(B_CONN, &motg->inputs);
+			clear_bit(A_BUS_REQ, &motg->inputs);
+			msm_otg_del_timer(motg);
+			otg->phy->state = OTG_STATE_A_WAIT_VFALL;
+			msm_otg_start_host(otg, 0);
+			if (!test_bit(ID_A, &motg->inputs))
+				msm_hsusb_vbus_power(motg, 0);
+			msm_otg_start_timer(motg, TA_WAIT_VFALL, A_WAIT_VFALL);
+		} else if (!test_bit(A_VBUS_VLD, &motg->inputs)) {
+			pr_debug("!a_vbus_vld\n");
+			clear_bit(B_CONN, &motg->inputs);
+			msm_otg_del_timer(motg);
+			otg->phy->state = OTG_STATE_A_VBUS_ERR;
+			msm_otg_start_host(otg, 0);
 			msm_otg_reset(otg->phy);
-			schedule_work(w);
+		} else if (!test_bit(A_BUS_REQ, &motg->inputs)) {
+			/*
+			 * a_bus_req is de-asserted when root hub is
+			 * suspended or HNP is in progress.
+			 */
+			pr_debug("!a_bus_req\n");
+			msm_otg_del_timer(motg);
+			otg->phy->state = OTG_STATE_A_SUSPEND;
+			if (otg->host->b_hnp_enable)
+				msm_otg_start_timer(motg, TA_AIDL_BDIS,
+						A_AIDL_BDIS);
+			else
+				pm_runtime_put_sync(otg->phy->dev);
+		} else if (!test_bit(B_CONN, &motg->inputs)) {
+			pr_debug("!b_conn\n");
+			msm_otg_del_timer(motg);
+			otg->phy->state = OTG_STATE_A_WAIT_BCON;
+			if (TA_WAIT_BCON > 0)
+				msm_otg_start_timer(motg, TA_WAIT_BCON,
+					A_WAIT_BCON);
+			if (msm_chg_check_aca_intr(motg))
+				work = 1;
+		} else if (test_bit(ID_A, &motg->inputs)) {
+			msm_otg_del_timer(motg);
+			msm_hsusb_vbus_power(motg, 0);
+			if (motg->chg_type == USB_ACA_DOCK_CHARGER)
+				msm_otg_notify_charger(motg,
+						IDEV_ACA_CHG_MAX);
+			else
+				msm_otg_notify_charger(motg,
+						IDEV_CHG_MIN - motg->mA_port);
+		} else if (!test_bit(ID, &motg->inputs)) {
+			motg->chg_state = USB_CHG_STATE_UNDEFINED;
+			motg->chg_type = USB_INVALID_CHARGER;
+			msm_otg_notify_charger(motg, 0);
+			msm_hsusb_vbus_power(motg, 1);
+		}
+		break;
+	case OTG_STATE_A_SUSPEND:
+		if ((test_bit(ID, &motg->inputs) &&
+				!test_bit(ID_A, &motg->inputs)) ||
+				test_bit(A_BUS_DROP, &motg->inputs) ||
+				test_bit(A_AIDL_BDIS, &motg->tmouts)) {
+			pr_debug("id_a/b/c || a_bus_drop ||"
+					"a_aidl_bdis_tmout\n");
+			msm_otg_del_timer(motg);
+			clear_bit(B_CONN, &motg->inputs);
+			otg->phy->state = OTG_STATE_A_WAIT_VFALL;
+			msm_otg_start_host(otg, 0);
+			msm_otg_reset(otg->phy);
+			if (!test_bit(ID_A, &motg->inputs))
+				msm_hsusb_vbus_power(motg, 0);
+			msm_otg_start_timer(motg, TA_WAIT_VFALL, A_WAIT_VFALL);
+		} else if (!test_bit(A_VBUS_VLD, &motg->inputs)) {
+			pr_debug("!a_vbus_vld\n");
+			msm_otg_del_timer(motg);
+			clear_bit(B_CONN, &motg->inputs);
+			otg->phy->state = OTG_STATE_A_VBUS_ERR;
+			msm_otg_start_host(otg, 0);
+			msm_otg_reset(otg->phy);
+		} else if (!test_bit(B_CONN, &motg->inputs) &&
+				otg->host->b_hnp_enable) {
+			pr_debug("!b_conn && b_hnp_enable");
+			otg->phy->state = OTG_STATE_A_PERIPHERAL;
+			msm_otg_host_hnp_enable(otg, 1);
+			otg->gadget->is_a_peripheral = 1;
+			msm_otg_start_peripheral(otg, 1);
+		} else if (!test_bit(B_CONN, &motg->inputs) &&
+				!otg->host->b_hnp_enable) {
+			pr_debug("!b_conn && !b_hnp_enable");
+			/*
+			 * bus request is dropped during suspend.
+			 * acquire again for next device.
+			 */
+			set_bit(A_BUS_REQ, &motg->inputs);
+			otg->phy->state = OTG_STATE_A_WAIT_BCON;
+			if (TA_WAIT_BCON > 0)
+				msm_otg_start_timer(motg, TA_WAIT_BCON,
+					A_WAIT_BCON);
+		} else if (test_bit(ID_A, &motg->inputs)) {
+			msm_hsusb_vbus_power(motg, 0);
+			msm_otg_notify_charger(motg,
+					IDEV_CHG_MIN - motg->mA_port);
+		} else if (!test_bit(ID, &motg->inputs)) {
+			msm_otg_notify_charger(motg, 0);
+			msm_hsusb_vbus_power(motg, 1);
+		}
+		break;
+	case OTG_STATE_A_PERIPHERAL:
+		if ((test_bit(ID, &motg->inputs) &&
+				!test_bit(ID_A, &motg->inputs)) ||
+				test_bit(A_BUS_DROP, &motg->inputs)) {
+			pr_debug("id _f/b/c || a_bus_drop\n");
+			/* Clear BIDL_ADIS timer */
+			msm_otg_del_timer(motg);
+			otg->phy->state = OTG_STATE_A_WAIT_VFALL;
+			msm_otg_start_peripheral(otg, 0);
+			otg->gadget->is_a_peripheral = 0;
+			msm_otg_start_host(otg, 0);
+			msm_otg_reset(otg->phy);
+			if (!test_bit(ID_A, &motg->inputs))
+				msm_hsusb_vbus_power(motg, 0);
+			msm_otg_start_timer(motg, TA_WAIT_VFALL, A_WAIT_VFALL);
+		} else if (!test_bit(A_VBUS_VLD, &motg->inputs)) {
+			pr_debug("!a_vbus_vld\n");
+			/* Clear BIDL_ADIS timer */
+			msm_otg_del_timer(motg);
+			otg->phy->state = OTG_STATE_A_VBUS_ERR;
+			msm_otg_start_peripheral(otg, 0);
+			otg->gadget->is_a_peripheral = 0;
+			msm_otg_start_host(otg, 0);
+		} else if (test_bit(A_BIDL_ADIS, &motg->tmouts)) {
+			pr_debug("a_bidl_adis_tmout\n");
+			msm_otg_start_peripheral(otg, 0);
+			otg->gadget->is_a_peripheral = 0;
+			otg->phy->state = OTG_STATE_A_WAIT_BCON;
+			set_bit(A_BUS_REQ, &motg->inputs);
+			msm_otg_host_hnp_enable(otg, 0);
+			if (TA_WAIT_BCON > 0)
+				msm_otg_start_timer(motg, TA_WAIT_BCON,
+					A_WAIT_BCON);
+		} else if (test_bit(ID_A, &motg->inputs)) {
+			msm_hsusb_vbus_power(motg, 0);
+			msm_otg_notify_charger(motg,
+					IDEV_CHG_MIN - motg->mA_port);
+		} else if (!test_bit(ID, &motg->inputs)) {
+			msm_otg_notify_charger(motg, 0);
+			msm_hsusb_vbus_power(motg, 1);
+		}
+		break;
+	case OTG_STATE_A_WAIT_VFALL:
+		if (test_bit(A_WAIT_VFALL, &motg->tmouts)) {
+			clear_bit(A_VBUS_VLD, &motg->inputs);
+			otg->phy->state = OTG_STATE_A_IDLE;
+			work = 1;
+		}
+		break;
+	case OTG_STATE_A_VBUS_ERR:
+		if ((test_bit(ID, &motg->inputs) &&
+				!test_bit(ID_A, &motg->inputs)) ||
+				test_bit(A_BUS_DROP, &motg->inputs) ||
+				test_bit(A_CLR_ERR, &motg->inputs)) {
+			otg->phy->state = OTG_STATE_A_WAIT_VFALL;
+			if (!test_bit(ID_A, &motg->inputs))
+				msm_hsusb_vbus_power(motg, 0);
+			msm_otg_start_timer(motg, TA_WAIT_VFALL, A_WAIT_VFALL);
+			motg->chg_state = USB_CHG_STATE_UNDEFINED;
+			motg->chg_type = USB_INVALID_CHARGER;
+			msm_otg_notify_charger(motg, 0);
 		}
 		break;
 	default:
 		break;
 	}
+	if (work)
+		queue_work(system_nrt_wq, &motg->sm_work);
 }
 
 static irqreturn_t msm_otg_irq(int irq, void *data)
 {
 	struct msm_otg *motg = data;
-	struct usb_phy *phy = &motg->phy;
-	u32 otgsc = 0;
+	struct usb_otg *otg = motg->phy.otg;
+	u32 otgsc = 0, usbsts, pc;
+	bool work = 0;
+	irqreturn_t ret = IRQ_HANDLED;
 
 	if (atomic_read(&motg->in_lpm)) {
+		pr_debug("OTG IRQ: in LPM\n");
 		disable_irq_nosync(irq);
 		motg->async_int = 1;
-		pm_runtime_get(phy->dev);
+		if (atomic_read(&motg->pm_suspended))
+			motg->sm_work_pending = true;
+		else
+			pm_request_resume(otg->phy->dev);
 		return IRQ_HANDLED;
 	}
 
+	usbsts = readl(USB_USBSTS);
 	otgsc = readl(USB_OTGSC);
-	if (!(otgsc & (OTGSC_IDIS | OTGSC_BSVIS)))
+
+	if (!(otgsc & OTG_OTGSTS_MASK) && !(usbsts & OTG_USBSTS_MASK))
 		return IRQ_NONE;
 
 	if ((otgsc & OTGSC_IDIS) && (otgsc & OTGSC_IDIE)) {
-		if (otgsc & OTGSC_ID)
+		if (otgsc & OTGSC_ID) {
+			pr_debug("Id set\n");
 			set_bit(ID, &motg->inputs);
-		else
+		} else {
+			pr_debug("Id clear\n");
+			/*
+			 * Assert a_bus_req to supply power on
+			 * VBUS when Micro/Mini-A cable is connected
+			 * with out user intervention.
+			 */
+			set_bit(A_BUS_REQ, &motg->inputs);
 			clear_bit(ID, &motg->inputs);
-		dev_dbg(phy->dev, "ID set/clear\n");
-		pm_runtime_get_noresume(phy->dev);
-	} else if ((otgsc & OTGSC_BSVIS) && (otgsc & OTGSC_BSVIE)) {
-		if (otgsc & OTGSC_BSV)
+			msm_chg_enable_aca_det(motg);
+		}
+		writel_relaxed(otgsc, USB_OTGSC);
+		work = 1;
+	} else if (otgsc & OTGSC_DPIS) {
+		pr_debug("DPIS detected\n");
+		writel_relaxed(otgsc, USB_OTGSC);
+		set_bit(A_SRP_DET, &motg->inputs);
+		set_bit(A_BUS_REQ, &motg->inputs);
+		work = 1;
+	} else if (otgsc & OTGSC_BSVIS) {
+		writel_relaxed(otgsc, USB_OTGSC);
+		/*
+		 * BSV interrupt comes when operating as an A-device
+		 * (VBUS on/off).
+		 * But, handle BSV when charger is removed from ACA in ID_A
+		 */
+		if ((otg->phy->state >= OTG_STATE_A_IDLE) &&
+			!test_bit(ID_A, &motg->inputs))
+			return IRQ_HANDLED;
+		if (otgsc & OTGSC_BSV) {
+			pr_debug("BSV set\n");
 			set_bit(B_SESS_VLD, &motg->inputs);
-		else
+		} else {
+			pr_debug("BSV clear\n");
 			clear_bit(B_SESS_VLD, &motg->inputs);
-		dev_dbg(phy->dev, "BSV set/clear\n");
-		pm_runtime_get_noresume(phy->dev);
+			clear_bit(A_BUS_SUSPEND, &motg->inputs);
+
+			msm_chg_check_aca_intr(motg);
+		}
+		work = 1;
+	} else if (usbsts & STS_PCI) {
+		pc = readl_relaxed(USB_PORTSC);
+		pr_debug("portsc = %x\n", pc);
+		ret = IRQ_NONE;
+		/*
+		 * HCD Acks PCI interrupt. We use this to switch
+		 * between different OTG states.
+		 */
+		work = 1;
+		switch (otg->phy->state) {
+		case OTG_STATE_A_SUSPEND:
+			if (otg->host->b_hnp_enable && (pc & PORTSC_CSC) &&
+					!(pc & PORTSC_CCS)) {
+				pr_debug("B_CONN clear\n");
+				clear_bit(B_CONN, &motg->inputs);
+				msm_otg_del_timer(motg);
+			}
+			break;
+		case OTG_STATE_A_PERIPHERAL:
+			/*
+			 * A-peripheral observed activity on bus.
+			 * clear A_BIDL_ADIS timer.
+			 */
+			msm_otg_del_timer(motg);
+			work = 0;
+			break;
+		case OTG_STATE_B_WAIT_ACON:
+			if ((pc & PORTSC_CSC) && (pc & PORTSC_CCS)) {
+				pr_debug("A_CONN set\n");
+				set_bit(A_CONN, &motg->inputs);
+				/* Clear ASE0_BRST timer */
+				msm_otg_del_timer(motg);
+			}
+			break;
+		case OTG_STATE_B_HOST:
+			if ((pc & PORTSC_CSC) && !(pc & PORTSC_CCS)) {
+				pr_debug("A_CONN clear\n");
+				clear_bit(A_CONN, &motg->inputs);
+				msm_otg_del_timer(motg);
+			}
+			break;
+		case OTG_STATE_A_WAIT_BCON:
+			if (TA_WAIT_BCON < 0)
+				set_bit(A_BUS_REQ, &motg->inputs);
+		default:
+			work = 0;
+			break;
+		}
+	} else if (usbsts & STS_URI) {
+		ret = IRQ_NONE;
+		switch (otg->phy->state) {
+		case OTG_STATE_A_PERIPHERAL:
+			/*
+			 * A-peripheral observed activity on bus.
+			 * clear A_BIDL_ADIS timer.
+			 */
+			msm_otg_del_timer(motg);
+			work = 0;
+			break;
+		default:
+			work = 0;
+			break;
+		}
+	} else if (usbsts & STS_SLI) {
+		ret = IRQ_NONE;
+		work = 0;
+		switch (otg->phy->state) {
+		case OTG_STATE_B_PERIPHERAL:
+			if (otg->gadget->b_hnp_enable) {
+				set_bit(A_BUS_SUSPEND, &motg->inputs);
+				set_bit(B_BUS_REQ, &motg->inputs);
+				work = 1;
+			}
+			break;
+		case OTG_STATE_A_PERIPHERAL:
+			msm_otg_start_timer(motg, TA_BIDL_ADIS,
+					A_BIDL_ADIS);
+			break;
+		default:
+			break;
+		}
+	} else if ((usbsts & PHY_ALT_INT)) {
+		writel_relaxed(PHY_ALT_INT, USB_USBSTS);
+		if (msm_chg_check_aca_intr(motg))
+			work = 1;
+		ret = IRQ_HANDLED;
+	}
+	if (work)
+		queue_work(system_nrt_wq, &motg->sm_work);
+
+	return ret;
+}
+
+static void msm_otg_set_vbus_state(int online)
+{
+	static bool init;
+	struct msm_otg *motg = the_msm_otg;
+
+	if (online) {
+		pr_debug("PMIC: BSV set\n");
+		set_bit(B_SESS_VLD, &motg->inputs);
+	} else {
+		pr_debug("PMIC: BSV clear\n");
+		clear_bit(B_SESS_VLD, &motg->inputs);
 	}
 
-	writel(otgsc, USB_OTGSC);
-	schedule_work(&motg->sm_work);
+	if (!init) {
+		init = true;
+		complete(&pmic_vbus_init);
+		pr_debug("PMIC: BSV init complete\n");
+		return;
+	}
+
+	if (atomic_read(&motg->pm_suspended))
+		motg->sm_work_pending = true;
+	else
+		queue_work(system_nrt_wq, &motg->sm_work);
+}
+
+static irqreturn_t msm_pmic_id_irq(int irq, void *data)
+{
+	struct msm_otg *motg = data;
+
+	if (aca_id_turned_on)
+		return IRQ_HANDLED;
+
+	if (irq_read_line(motg->pdata->pmic_id_irq)) {
+		pr_debug("PMIC: ID set\n");
+		set_bit(ID, &motg->inputs);
+	} else {
+		pr_debug("PMIC: ID clear\n");
+		clear_bit(ID, &motg->inputs);
+		set_bit(A_BUS_REQ, &motg->inputs);
+	}
+
+	if (motg->phy.state != OTG_STATE_UNDEFINED) {
+		if (atomic_read(&motg->pm_suspended))
+			motg->sm_work_pending = true;
+		else
+			queue_work(system_nrt_wq, &motg->sm_work);
+	}
+
 	return IRQ_HANDLED;
 }
 
 static int msm_otg_mode_show(struct seq_file *s, void *unused)
 {
 	struct msm_otg *motg = s->private;
-	struct usb_otg *otg = motg->phy.otg;
+	struct usb_phy *phy = &motg->phy;
 
-	switch (otg->phy->state) {
+	switch (phy->state) {
 	case OTG_STATE_A_HOST:
 		seq_printf(s, "host\n");
 		break;
@@ -1311,7 +2779,7 @@
 	struct seq_file *s = file->private_data;
 	struct msm_otg *motg = s->private;
 	char buf[16];
-	struct usb_otg *otg = motg->phy.otg;
+	struct usb_phy *phy = &motg->phy;
 	int status = count;
 	enum usb_mode_type req_mode;
 
@@ -1335,7 +2803,7 @@
 
 	switch (req_mode) {
 	case USB_NONE:
-		switch (otg->phy->state) {
+		switch (phy->state) {
 		case OTG_STATE_A_HOST:
 		case OTG_STATE_B_PERIPHERAL:
 			set_bit(ID, &motg->inputs);
@@ -1346,7 +2814,7 @@
 		}
 		break;
 	case USB_PERIPHERAL:
-		switch (otg->phy->state) {
+		switch (phy->state) {
 		case OTG_STATE_B_IDLE:
 		case OTG_STATE_A_HOST:
 			set_bit(ID, &motg->inputs);
@@ -1357,7 +2825,7 @@
 		}
 		break;
 	case USB_HOST:
-		switch (otg->phy->state) {
+		switch (phy->state) {
 		case OTG_STATE_B_IDLE:
 		case OTG_STATE_B_PERIPHERAL:
 			clear_bit(ID, &motg->inputs);
@@ -1370,8 +2838,8 @@
 		goto out;
 	}
 
-	pm_runtime_get_sync(otg->phy->dev);
-	schedule_work(&motg->sm_work);
+	pm_runtime_resume(phy->dev);
+	queue_work(system_nrt_wq, &motg->sm_work);
 out:
 	return status;
 }
@@ -1384,31 +2852,320 @@
 	.release = single_release,
 };
 
+static int msm_otg_show_otg_state(struct seq_file *s, void *unused)
+{
+	struct msm_otg *motg = s->private;
+	struct usb_phy *phy = &motg->phy;
+
+	seq_printf(s, "%s\n", otg_state_string(phy->state));
+	return 0;
+}
+
+static int msm_otg_otg_state_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, msm_otg_show_otg_state, inode->i_private);
+}
+
+const struct file_operations msm_otg_state_fops = {
+	.open = msm_otg_otg_state_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static int msm_otg_show_chg_type(struct seq_file *s, void *unused)
+{
+	struct msm_otg *motg = s->private;
+
+	seq_printf(s, "%s\n", chg_to_string(motg->chg_type));
+	return 0;
+}
+
+static int msm_otg_chg_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, msm_otg_show_chg_type, inode->i_private);
+}
+
+const struct file_operations msm_otg_chg_fops = {
+	.open = msm_otg_chg_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static int msm_otg_aca_show(struct seq_file *s, void *unused)
+{
+	if (debug_aca_enabled)
+		seq_printf(s, "enabled\n");
+	else
+		seq_printf(s, "disabled\n");
+
+	return 0;
+}
+
+static int msm_otg_aca_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, msm_otg_aca_show, inode->i_private);
+}
+
+static ssize_t msm_otg_aca_write(struct file *file, const char __user *ubuf,
+				size_t count, loff_t *ppos)
+{
+	char buf[8];
+
+	memset(buf, 0x00, sizeof(buf));
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	if (!strncmp(buf, "enable", 6))
+		debug_aca_enabled = true;
+	else
+		debug_aca_enabled = false;
+
+	return count;
+}
+
+const struct file_operations msm_otg_aca_fops = {
+	.open = msm_otg_aca_open,
+	.read = seq_read,
+	.write = msm_otg_aca_write,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static int msm_otg_bus_show(struct seq_file *s, void *unused)
+{
+	if (debug_bus_voting_enabled)
+		seq_printf(s, "enabled\n");
+	else
+		seq_printf(s, "disabled\n");
+
+	return 0;
+}
+
+static int msm_otg_bus_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, msm_otg_bus_show, inode->i_private);
+}
+
+static ssize_t msm_otg_bus_write(struct file *file, const char __user *ubuf,
+				size_t count, loff_t *ppos)
+{
+	char buf[8];
+	int ret;
+	struct seq_file *s = file->private_data;
+	struct msm_otg *motg = s->private;
+
+	memset(buf, 0x00, sizeof(buf));
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	if (!strncmp(buf, "enable", 6)) {
+		/* Do not vote here. Let OTG statemachine decide when to vote */
+		debug_bus_voting_enabled = true;
+	} else {
+		debug_bus_voting_enabled = false;
+		if (motg->bus_perf_client) {
+			ret = msm_bus_scale_client_update_request(
+					motg->bus_perf_client, 0);
+			if (ret)
+				dev_err(motg->phy.dev, "%s: Failed to devote "
+					   "for bus bw %d\n", __func__, ret);
+		}
+	}
+
+	return count;
+}
+
+const struct file_operations msm_otg_bus_fops = {
+	.open = msm_otg_bus_open,
+	.read = seq_read,
+	.write = msm_otg_bus_write,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
 static struct dentry *msm_otg_dbg_root;
-static struct dentry *msm_otg_dbg_mode;
 
 static int msm_otg_debugfs_init(struct msm_otg *motg)
 {
+	struct dentry *msm_otg_dentry;
+
 	msm_otg_dbg_root = debugfs_create_dir("msm_otg", NULL);
 
 	if (!msm_otg_dbg_root || IS_ERR(msm_otg_dbg_root))
 		return -ENODEV;
 
-	msm_otg_dbg_mode = debugfs_create_file("mode", S_IRUGO | S_IWUSR,
-				msm_otg_dbg_root, motg, &msm_otg_mode_fops);
-	if (!msm_otg_dbg_mode) {
-		debugfs_remove(msm_otg_dbg_root);
-		msm_otg_dbg_root = NULL;
+	if (motg->pdata->mode == USB_OTG &&
+		motg->pdata->otg_control == OTG_USER_CONTROL) {
+
+		msm_otg_dentry = debugfs_create_file("mode", S_IRUGO |
+			S_IWUSR, msm_otg_dbg_root, motg,
+			&msm_otg_mode_fops);
+
+		if (!msm_otg_dentry) {
+			debugfs_remove(msm_otg_dbg_root);
+			msm_otg_dbg_root = NULL;
+			return -ENODEV;
+		}
+	}
+
+	msm_otg_dentry = debugfs_create_file("chg_type", S_IRUGO,
+		msm_otg_dbg_root, motg,
+		&msm_otg_chg_fops);
+
+	if (!msm_otg_dentry) {
+		debugfs_remove_recursive(msm_otg_dbg_root);
 		return -ENODEV;
 	}
 
+	msm_otg_dentry = debugfs_create_file("aca", S_IRUGO | S_IWUSR,
+		msm_otg_dbg_root, motg,
+		&msm_otg_aca_fops);
+
+	if (!msm_otg_dentry) {
+		debugfs_remove_recursive(msm_otg_dbg_root);
+		return -ENODEV;
+	}
+
+	msm_otg_dentry = debugfs_create_file("bus_voting", S_IRUGO | S_IWUSR,
+		msm_otg_dbg_root, motg,
+		&msm_otg_bus_fops);
+
+	if (!msm_otg_dentry) {
+		debugfs_remove_recursive(msm_otg_dbg_root);
+		return -ENODEV;
+	}
+
+	msm_otg_dentry = debugfs_create_file("otg_state", S_IRUGO,
+				msm_otg_dbg_root, motg, &msm_otg_state_fops);
+
+	if (!msm_otg_dentry) {
+		debugfs_remove_recursive(msm_otg_dbg_root);
+		return -ENODEV;
+	}
 	return 0;
 }
 
 static void msm_otg_debugfs_cleanup(void)
 {
-	debugfs_remove(msm_otg_dbg_mode);
-	debugfs_remove(msm_otg_dbg_root);
+	debugfs_remove_recursive(msm_otg_dbg_root);
+}
+
+static u64 msm_otg_dma_mask = DMA_BIT_MASK(64);
+static struct platform_device *msm_otg_add_pdev(
+		struct platform_device *ofdev, const char *name)
+{
+	struct platform_device *pdev;
+	const struct resource *res = ofdev->resource;
+	unsigned int num = ofdev->num_resources;
+	int retval;
+
+	pdev = platform_device_alloc(name, -1);
+	if (!pdev) {
+		retval = -ENOMEM;
+		goto error;
+	}
+
+	pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+	pdev->dev.dma_mask = &msm_otg_dma_mask;
+
+	if (num) {
+		retval = platform_device_add_resources(pdev, res, num);
+		if (retval)
+			goto error;
+	}
+
+	retval = platform_device_add(pdev);
+	if (retval)
+		goto error;
+
+	return pdev;
+
+error:
+	platform_device_put(pdev);
+	return ERR_PTR(retval);
+}
+
+static int msm_otg_setup_devices(struct platform_device *ofdev,
+		enum usb_mode_type mode, bool init)
+{
+	const char *gadget_name = "msm_hsusb";
+	const char *host_name = "msm_hsusb_host";
+	static struct platform_device *gadget_pdev;
+	static struct platform_device *host_pdev;
+	int retval = 0;
+
+	if (!init) {
+		if (gadget_pdev)
+			platform_device_unregister(gadget_pdev);
+		if (host_pdev)
+			platform_device_unregister(host_pdev);
+		return 0;
+	}
+
+	switch (mode) {
+	case USB_OTG:
+		/* fall through */
+	case USB_PERIPHERAL:
+		gadget_pdev = msm_otg_add_pdev(ofdev, gadget_name);
+		if (IS_ERR(gadget_pdev)) {
+			retval = PTR_ERR(gadget_pdev);
+			break;
+		}
+		if (mode == USB_PERIPHERAL)
+			break;
+		/* fall through */
+	case USB_HOST:
+		host_pdev = msm_otg_add_pdev(ofdev, host_name);
+		if (IS_ERR(host_pdev)) {
+			retval = PTR_ERR(host_pdev);
+			if (mode == USB_OTG)
+				platform_device_unregister(gadget_pdev);
+		}
+		break;
+	default:
+		break;
+	}
+
+	return retval;
+}
+
+struct msm_otg_platform_data *msm_otg_dt_to_pdata(struct platform_device *pdev)
+{
+	struct device_node *node = pdev->dev.of_node;
+	struct msm_otg_platform_data *pdata;
+	int len = 0;
+
+	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata) {
+		pr_err("unable to allocate platform data\n");
+		return NULL;
+	}
+	of_get_property(node, "qcom,hsusb-otg-phy-init-seq", &len);
+	if (len) {
+		pdata->phy_init_seq = devm_kzalloc(&pdev->dev, len, GFP_KERNEL);
+		if (!pdata->phy_init_seq)
+			return NULL;
+		of_property_read_u32_array(node, "qcom,hsusb-otg-phy-init-seq",
+				pdata->phy_init_seq,
+				len/sizeof(*pdata->phy_init_seq));
+	}
+	of_property_read_u32(node, "qcom,hsusb-otg-power-budget",
+				&pdata->power_budget);
+	of_property_read_u32(node, "qcom,hsusb-otg-mode",
+				&pdata->mode);
+	of_property_read_u32(node, "qcom,hsusb-otg-otg-control",
+				&pdata->otg_control);
+	of_property_read_u32(node, "qcom,hsusb-otg-default-mode",
+				&pdata->default_mode);
+	of_property_read_u32(node, "qcom,hsusb-otg-phy-type",
+				&pdata->phy_type);
+	of_property_read_u32(node, "qcom,hsusb-otg-pmic-id-irq",
+				&pdata->pmic_id_irq);
+	return pdata;
 }
 
 static int __init msm_otg_probe(struct platform_device *pdev)
@@ -1417,11 +3174,25 @@
 	struct resource *res;
 	struct msm_otg *motg;
 	struct usb_phy *phy;
+	struct msm_otg_platform_data *pdata;
 
 	dev_info(&pdev->dev, "msm_otg probe\n");
-	if (!pdev->dev.platform_data) {
+
+	if (pdev->dev.of_node) {
+		dev_dbg(&pdev->dev, "device tree enabled\n");
+		pdata = msm_otg_dt_to_pdata(pdev);
+		if (!pdata)
+			return -ENOMEM;
+		ret = msm_otg_setup_devices(pdev, pdata->mode, true);
+		if (ret) {
+			dev_err(&pdev->dev, "devices setup failed\n");
+			return ret;
+		}
+	} else if (!pdev->dev.platform_data) {
 		dev_err(&pdev->dev, "No platform data given. Bailing out\n");
 		return -ENODEV;
+	} else {
+		pdata = pdev->dev.platform_data;
 	}
 
 	motg = kzalloc(sizeof(struct msm_otg), GFP_KERNEL);
@@ -1432,75 +3203,80 @@
 
 	motg->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL);
 	if (!motg->phy.otg) {
-		dev_err(&pdev->dev, "unable to allocate msm_otg\n");
-		return -ENOMEM;
-	}
-
-	motg->pdata = pdev->dev.platform_data;
-	phy = &motg->phy;
-	phy->dev = &pdev->dev;
-
-	motg->phy_reset_clk = clk_get(&pdev->dev, "usb_phy_clk");
-	if (IS_ERR(motg->phy_reset_clk)) {
-		dev_err(&pdev->dev, "failed to get usb_phy_clk\n");
-		ret = PTR_ERR(motg->phy_reset_clk);
+		dev_err(&pdev->dev, "unable to allocate usb_otg\n");
+		ret = -ENOMEM;
 		goto free_motg;
 	}
 
-	motg->clk = clk_get(&pdev->dev, "usb_hs_clk");
-	if (IS_ERR(motg->clk)) {
-		dev_err(&pdev->dev, "failed to get usb_hs_clk\n");
-		ret = PTR_ERR(motg->clk);
-		goto put_phy_reset_clk;
-	}
-	clk_set_rate(motg->clk, 60000000);
+	the_msm_otg = motg;
+	motg->pdata = pdata;
+	phy = &motg->phy;
+	phy->dev = &pdev->dev;
 
 	/*
-	 * If USB Core is running its protocol engine based on CORE CLK,
+	 * ACA ID_GND threshold range is overlapped with OTG ID_FLOAT.  Hence
+	 * PHY treat ACA ID_GND as float and no interrupt is generated.  But
+	 * PMIC can detect ACA ID_GND and generate an interrupt.
+	 */
+	if (aca_enabled() && motg->pdata->otg_control != OTG_PMIC_CONTROL) {
+		dev_err(&pdev->dev, "ACA can not be enabled without PMIC\n");
+		ret = -EINVAL;
+		goto free_otg;
+	}
+
+	/* initialize reset counter */
+	motg->reset_counter = 0;
+
+	/* Some targets don't support PHY clock. */
+	motg->phy_reset_clk = clk_get(&pdev->dev, "phy_clk");
+	if (IS_ERR(motg->phy_reset_clk))
+		dev_err(&pdev->dev, "failed to get phy_clk\n");
+
+	/*
+	 * Targets on which link uses asynchronous reset methodology,
+	 * free running clock is not required during the reset.
+	 */
+	motg->clk = clk_get(&pdev->dev, "alt_core_clk");
+	if (IS_ERR(motg->clk))
+		dev_dbg(&pdev->dev, "alt_core_clk is not present\n");
+	else
+		clk_set_rate(motg->clk, 60000000);
+
+	/*
+	 * USB Core is running its protocol engine based on CORE CLK,
 	 * CORE CLK  must be running at >55Mhz for correct HSUSB
 	 * operation and USB core cannot tolerate frequency changes on
 	 * CORE CLK. For such USB cores, vote for maximum clk frequency
 	 * on pclk source
 	 */
-	 if (motg->pdata->pclk_src_name) {
-		motg->pclk_src = clk_get(&pdev->dev,
-			motg->pdata->pclk_src_name);
-		if (IS_ERR(motg->pclk_src))
-			goto put_clk;
-		clk_set_rate(motg->pclk_src, INT_MAX);
-		clk_enable(motg->pclk_src);
-	} else
-		motg->pclk_src = ERR_PTR(-ENOENT);
-
-
-	motg->pclk = clk_get(&pdev->dev, "usb_hs_pclk");
-	if (IS_ERR(motg->pclk)) {
-		dev_err(&pdev->dev, "failed to get usb_hs_pclk\n");
-		ret = PTR_ERR(motg->pclk);
-		goto put_pclk_src;
-	}
-
-	/*
-	 * USB core clock is not present on all MSM chips. This
-	 * clock is introduced to remove the dependency on AXI
-	 * bus frequency.
-	 */
-	motg->core_clk = clk_get(&pdev->dev, "usb_hs_core_clk");
-	if (IS_ERR(motg->core_clk))
+	motg->core_clk = clk_get(&pdev->dev, "core_clk");
+	if (IS_ERR(motg->core_clk)) {
 		motg->core_clk = NULL;
+		dev_err(&pdev->dev, "failed to get core_clk\n");
+		ret = PTR_ERR(motg->core_clk);
+		goto put_clk;
+	}
+	clk_set_rate(motg->core_clk, INT_MAX);
+
+	motg->pclk = clk_get(&pdev->dev, "iface_clk");
+	if (IS_ERR(motg->pclk)) {
+		dev_err(&pdev->dev, "failed to get iface_clk\n");
+		ret = PTR_ERR(motg->pclk);
+		goto put_core_clk;
+	}
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!res) {
 		dev_err(&pdev->dev, "failed to get platform resource mem\n");
 		ret = -ENODEV;
-		goto put_core_clk;
+		goto put_pclk;
 	}
 
 	motg->regs = ioremap(res->start, resource_size(res));
 	if (!motg->regs) {
 		dev_err(&pdev->dev, "ioremap failed\n");
 		ret = -ENOMEM;
-		goto put_core_clk;
+		goto put_pclk;
 	}
 	dev_info(&pdev->dev, "OTG regs = %p\n", motg->regs);
 
@@ -1511,49 +3287,97 @@
 		goto free_regs;
 	}
 
-	clk_enable(motg->clk);
-	clk_enable(motg->pclk);
+	motg->xo_handle = msm_xo_get(MSM_XO_TCXO_D0, "usb");
+	if (IS_ERR(motg->xo_handle)) {
+		dev_err(&pdev->dev, "%s not able to get the handle "
+			"to vote for TCXO D0 buffer\n", __func__);
+		ret = PTR_ERR(motg->xo_handle);
+		goto free_regs;
+	}
 
-	ret = msm_hsusb_init_vddcx(motg, 1);
+	ret = msm_xo_mode_vote(motg->xo_handle, MSM_XO_MODE_ON);
+	if (ret) {
+		dev_err(&pdev->dev, "%s failed to vote for TCXO "
+			"D0 buffer%d\n", __func__, ret);
+		goto free_xo_handle;
+	}
+
+	clk_prepare_enable(motg->pclk);
+
+	motg->vdd_type = VDDCX_CORNER;
+	hsusb_vddcx = devm_regulator_get(motg->phy.dev, "hsusb_vdd_dig");
+	if (IS_ERR(hsusb_vddcx)) {
+		hsusb_vddcx = devm_regulator_get(motg->phy.dev, "HSUSB_VDDCX");
+		if (IS_ERR(hsusb_vddcx)) {
+			dev_err(motg->phy.dev, "unable to get hsusb vddcx\n");
+			goto devote_xo_handle;
+		}
+		motg->vdd_type = VDDCX;
+	}
+
+	ret = msm_hsusb_config_vddcx(1);
 	if (ret) {
 		dev_err(&pdev->dev, "hsusb vddcx configuration failed\n");
-		goto free_regs;
+		goto devote_xo_handle;
+	}
+
+	ret = regulator_enable(hsusb_vddcx);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to enable the hsusb vddcx\n");
+		goto free_config_vddcx;
 	}
 
 	ret = msm_hsusb_ldo_init(motg, 1);
 	if (ret) {
 		dev_err(&pdev->dev, "hsusb vreg configuration failed\n");
-		goto vddcx_exit;
-	}
-	ret = msm_hsusb_ldo_set_mode(1);
-	if (ret) {
-		dev_err(&pdev->dev, "hsusb vreg enable failed\n");
-		goto ldo_exit;
+		goto free_hsusb_vddcx;
 	}
 
-	if (motg->core_clk)
-		clk_enable(motg->core_clk);
+	if (pdata->mhl_enable) {
+		mhl_analog_switch = devm_regulator_get(motg->phy.dev,
+							"mhl_ext_3p3v");
+		if (IS_ERR(mhl_analog_switch)) {
+			dev_err(&pdev->dev, "Unable to get mhl_analog_switch\n");
+			goto free_ldo_init;
+		}
+	}
+
+	ret = msm_hsusb_ldo_enable(motg, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "hsusb vreg enable failed\n");
+		goto free_ldo_init;
+	}
+	clk_prepare_enable(motg->core_clk);
 
 	writel(0, USB_USBINTR);
 	writel(0, USB_OTGSC);
+	/* Ensure that above STOREs are completed before enabling interrupts */
+	mb();
 
+	wake_lock_init(&motg->wlock, WAKE_LOCK_SUSPEND, "msm_otg");
+	msm_otg_init_timer(motg);
 	INIT_WORK(&motg->sm_work, msm_otg_sm_work);
 	INIT_DELAYED_WORK(&motg->chg_work, msm_chg_detect_work);
+	setup_timer(&motg->id_timer, msm_otg_id_timer_func,
+				(unsigned long) motg);
 	ret = request_irq(motg->irq, msm_otg_irq, IRQF_SHARED,
 					"msm_otg", motg);
 	if (ret) {
 		dev_err(&pdev->dev, "request irq failed\n");
-		goto disable_clks;
+		goto destroy_wlock;
 	}
 
 	phy->init = msm_otg_reset;
 	phy->set_power = msm_otg_set_power;
+	phy->set_suspend = msm_otg_set_suspend;
 
 	phy->io_ops = &msm_otg_io_ops;
 
 	phy->otg->phy = &motg->phy;
 	phy->otg->set_host = msm_otg_set_host;
 	phy->otg->set_peripheral = msm_otg_set_peripheral;
+	phy->otg->start_hnp = msm_otg_start_hnp;
+	phy->otg->start_srp = msm_otg_start_srp;
 
 	ret = usb_set_transceiver(&motg->phy);
 	if (ret) {
@@ -1561,47 +3385,104 @@
 		goto free_irq;
 	}
 
-	platform_set_drvdata(pdev, motg);
-	device_init_wakeup(&pdev->dev, 1);
-
 	if (motg->pdata->mode == USB_OTG &&
-			motg->pdata->otg_control == OTG_USER_CONTROL) {
-		ret = msm_otg_debugfs_init(motg);
-		if (ret)
-			dev_dbg(&pdev->dev, "mode debugfs file is"
-					"not available\n");
+		motg->pdata->otg_control == OTG_PMIC_CONTROL) {
+		if (motg->pdata->pmic_id_irq) {
+			ret = request_irq(motg->pdata->pmic_id_irq,
+						msm_pmic_id_irq,
+						IRQF_TRIGGER_RISING |
+						IRQF_TRIGGER_FALLING,
+						"msm_otg", motg);
+			if (ret) {
+				dev_err(&pdev->dev, "request irq failed for PMIC ID\n");
+				goto remove_phy;
+			}
+		} else {
+			ret = -ENODEV;
+			dev_err(&pdev->dev, "PMIC IRQ for ID notifications doesn't exist\n");
+			goto remove_phy;
+		}
 	}
 
+	msm_hsusb_mhl_switch_enable(motg, 1);
+
+	platform_set_drvdata(pdev, motg);
+	device_init_wakeup(&pdev->dev, 1);
+	motg->mA_port = IUNIT;
+
+	ret = msm_otg_debugfs_init(motg);
+	if (ret)
+		dev_dbg(&pdev->dev, "mode debugfs file is"
+			"not available\n");
+
+	if (motg->pdata->otg_control == OTG_PMIC_CONTROL)
+		pm8921_charger_register_vbus_sn(&msm_otg_set_vbus_state);
+
+	if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY) {
+		if (motg->pdata->otg_control == OTG_PMIC_CONTROL &&
+			(!(motg->pdata->mode == USB_OTG) ||
+			 motg->pdata->pmic_id_irq))
+			motg->caps = ALLOW_PHY_POWER_COLLAPSE |
+				ALLOW_PHY_RETENTION;
+
+		if (motg->pdata->otg_control == OTG_PHY_CONTROL)
+			motg->caps = ALLOW_PHY_RETENTION;
+	}
+
+	if (motg->pdata->enable_lpm_on_dev_suspend)
+		motg->caps |= ALLOW_LPM_ON_DEV_SUSPEND;
+
+	wake_lock(&motg->wlock);
 	pm_runtime_set_active(&pdev->dev);
 	pm_runtime_enable(&pdev->dev);
 
+	if (motg->pdata->bus_scale_table) {
+		motg->bus_perf_client =
+		    msm_bus_scale_register_client(motg->pdata->bus_scale_table);
+		if (!motg->bus_perf_client)
+			dev_err(motg->phy.dev, "%s: Failed to register BUS "
+						"scaling client!!\n", __func__);
+		else
+			debug_bus_voting_enabled = true;
+	}
+
 	return 0;
+
+remove_phy:
+	usb_set_transceiver(NULL);
 free_irq:
 	free_irq(motg->irq, motg);
-disable_clks:
-	clk_disable(motg->pclk);
-	clk_disable(motg->clk);
-ldo_exit:
+destroy_wlock:
+	wake_lock_destroy(&motg->wlock);
+	clk_disable_unprepare(motg->core_clk);
+	msm_hsusb_ldo_enable(motg, 0);
+free_ldo_init:
 	msm_hsusb_ldo_init(motg, 0);
-vddcx_exit:
-	msm_hsusb_init_vddcx(motg, 0);
+free_hsusb_vddcx:
+	regulator_disable(hsusb_vddcx);
+free_config_vddcx:
+	regulator_set_voltage(hsusb_vddcx,
+		vdd_val[motg->vdd_type][VDD_NONE],
+		vdd_val[motg->vdd_type][VDD_MAX]);
+devote_xo_handle:
+	clk_disable_unprepare(motg->pclk);
+	msm_xo_mode_vote(motg->xo_handle, MSM_XO_MODE_OFF);
+free_xo_handle:
+	msm_xo_put(motg->xo_handle);
 free_regs:
 	iounmap(motg->regs);
-put_core_clk:
-	if (motg->core_clk)
-		clk_put(motg->core_clk);
+put_pclk:
 	clk_put(motg->pclk);
-put_pclk_src:
-	if (!IS_ERR(motg->pclk_src)) {
-		clk_disable(motg->pclk_src);
-		clk_put(motg->pclk_src);
-	}
+put_core_clk:
+	clk_put(motg->core_clk);
 put_clk:
-	clk_put(motg->clk);
-put_phy_reset_clk:
-	clk_put(motg->phy_reset_clk);
-free_motg:
+	if (!IS_ERR(motg->clk))
+		clk_put(motg->clk);
+	if (!IS_ERR(motg->phy_reset_clk))
+		clk_put(motg->phy_reset_clk);
+free_otg:
 	kfree(motg->phy.otg);
+free_motg:
 	kfree(motg);
 	return ret;
 }
@@ -1609,12 +3490,16 @@
 static int __devexit msm_otg_remove(struct platform_device *pdev)
 {
 	struct msm_otg *motg = platform_get_drvdata(pdev);
-	struct usb_phy *phy = &motg->phy;
+	struct usb_otg *otg = motg->phy.otg;
 	int cnt = 0;
 
-	if (phy->otg->host || phy->otg->gadget)
+	if (otg->host || otg->gadget)
 		return -EBUSY;
 
+	if (pdev->dev.of_node)
+		msm_otg_setup_devices(pdev, motg->pdata->mode, false);
+	if (motg->pdata->otg_control == OTG_PMIC_CONTROL)
+		pm8921_charger_unregister_vbus_sn(0);
 	msm_otg_debugfs_cleanup();
 	cancel_delayed_work_sync(&motg->chg_work);
 	cancel_work_sync(&motg->sm_work);
@@ -1623,15 +3508,19 @@
 
 	device_init_wakeup(&pdev->dev, 0);
 	pm_runtime_disable(&pdev->dev);
+	wake_lock_destroy(&motg->wlock);
 
+	msm_hsusb_mhl_switch_enable(motg, 0);
+	if (motg->pdata->pmic_id_irq)
+		free_irq(motg->pdata->pmic_id_irq, motg);
 	usb_set_transceiver(NULL);
 	free_irq(motg->irq, motg);
 
 	/*
 	 * Put PHY in low power mode.
 	 */
-	ulpi_read(phy, 0x14);
-	ulpi_write(phy, 0x08, 0x09);
+	ulpi_read(otg->phy, 0x14);
+	ulpi_write(otg->phy, 0x08, 0x09);
 
 	writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC);
 	while (cnt < PHY_SUSPEND_TIMEOUT_USEC) {
@@ -1641,30 +3530,33 @@
 		cnt++;
 	}
 	if (cnt >= PHY_SUSPEND_TIMEOUT_USEC)
-		dev_err(phy->dev, "Unable to suspend PHY\n");
+		dev_err(otg->phy->dev, "Unable to suspend PHY\n");
 
-	clk_disable(motg->pclk);
-	clk_disable(motg->clk);
-	if (motg->core_clk)
-		clk_disable(motg->core_clk);
-	if (!IS_ERR(motg->pclk_src)) {
-		clk_disable(motg->pclk_src);
-		clk_put(motg->pclk_src);
-	}
+	clk_disable_unprepare(motg->pclk);
+	clk_disable_unprepare(motg->core_clk);
+	msm_xo_put(motg->xo_handle);
+	msm_hsusb_ldo_enable(motg, 0);
 	msm_hsusb_ldo_init(motg, 0);
+	regulator_disable(hsusb_vddcx);
+	regulator_set_voltage(hsusb_vddcx,
+		vdd_val[motg->vdd_type][VDD_NONE],
+		vdd_val[motg->vdd_type][VDD_MAX]);
 
 	iounmap(motg->regs);
 	pm_runtime_set_suspended(&pdev->dev);
 
-	clk_put(motg->phy_reset_clk);
+	if (!IS_ERR(motg->phy_reset_clk))
+		clk_put(motg->phy_reset_clk);
 	clk_put(motg->pclk);
-	clk_put(motg->clk);
-	if (motg->core_clk)
-		clk_put(motg->core_clk);
+	if (!IS_ERR(motg->clk))
+		clk_put(motg->clk);
+	clk_put(motg->core_clk);
+
+	if (motg->bus_perf_client)
+		msm_bus_scale_unregister_client(motg->bus_perf_client);
 
 	kfree(motg->phy.otg);
 	kfree(motg);
-
 	return 0;
 }
 
@@ -1672,20 +3564,14 @@
 static int msm_otg_runtime_idle(struct device *dev)
 {
 	struct msm_otg *motg = dev_get_drvdata(dev);
-	struct usb_otg *otg = motg->phy.otg;
+	struct usb_phy *phy = &motg->phy;
 
 	dev_dbg(dev, "OTG runtime idle\n");
 
-	/*
-	 * It is observed some times that a spurious interrupt
-	 * comes when PHY is put into LPM immediately after PHY reset.
-	 * This 1 sec delay also prevents entering into LPM immediately
-	 * after asynchronous interrupt.
-	 */
-	if (otg->phy->state != OTG_STATE_UNDEFINED)
-		pm_schedule_suspend(dev, 1000);
-
-	return -EAGAIN;
+	if (phy->state == OTG_STATE_UNDEFINED)
+		return -EAGAIN;
+	else
+		return 0;
 }
 
 static int msm_otg_runtime_suspend(struct device *dev)
@@ -1701,6 +3587,7 @@
 	struct msm_otg *motg = dev_get_drvdata(dev);
 
 	dev_dbg(dev, "OTG runtime resume\n");
+	pm_runtime_get_noresume(dev);
 	return msm_otg_resume(motg);
 }
 #endif
@@ -1708,32 +3595,42 @@
 #ifdef CONFIG_PM_SLEEP
 static int msm_otg_pm_suspend(struct device *dev)
 {
+	int ret = 0;
 	struct msm_otg *motg = dev_get_drvdata(dev);
 
 	dev_dbg(dev, "OTG PM suspend\n");
-	return msm_otg_suspend(motg);
+
+	atomic_set(&motg->pm_suspended, 1);
+	ret = msm_otg_suspend(motg);
+	if (ret)
+		atomic_set(&motg->pm_suspended, 0);
+
+	return ret;
 }
 
 static int msm_otg_pm_resume(struct device *dev)
 {
+	int ret = 0;
 	struct msm_otg *motg = dev_get_drvdata(dev);
-	int ret;
 
 	dev_dbg(dev, "OTG PM resume\n");
 
-	ret = msm_otg_resume(motg);
-	if (ret)
-		return ret;
+	atomic_set(&motg->pm_suspended, 0);
+	if (motg->sm_work_pending) {
+		motg->sm_work_pending = false;
 
-	/*
-	 * Runtime PM Documentation recommends bringing the
-	 * device to full powered state upon resume.
-	 */
-	pm_runtime_disable(dev);
-	pm_runtime_set_active(dev);
-	pm_runtime_enable(dev);
+		pm_runtime_get_noresume(dev);
+		ret = msm_otg_resume(motg);
 
-	return 0;
+		/* Update runtime PM status */
+		pm_runtime_disable(dev);
+		pm_runtime_set_active(dev);
+		pm_runtime_enable(dev);
+
+		queue_work(system_nrt_wq, &motg->sm_work);
+	}
+
+	return ret;
 }
 #endif
 
@@ -1745,6 +3642,12 @@
 };
 #endif
 
+static struct of_device_id msm_otg_dt_match[] = {
+	{	.compatible = "qcom,hsusb-otg",
+	},
+	{}
+};
+
 static struct platform_driver msm_otg_driver = {
 	.remove = __devexit_p(msm_otg_remove),
 	.driver = {
@@ -1753,6 +3656,7 @@
 #ifdef CONFIG_PM
 		.pm = &msm_otg_dev_pm_ops,
 #endif
+		.of_match_table = msm_otg_dt_match,
 	},
 };
 
diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c
index 801e597..cf8676f 100644
--- a/drivers/usb/otg/otg.c
+++ b/drivers/usb/otg/otg.c
@@ -100,3 +100,18 @@
 	}
 }
 EXPORT_SYMBOL(otg_state_string);
+
+int otg_send_event(enum usb_otg_event event)
+{
+	struct usb_phy *phy = usb_get_transceiver();
+	int ret = -ENOTSUPP;
+
+	if (phy && phy->otg && phy->otg->send_event)
+		ret = phy->otg->send_event(phy->otg, event);
+
+	if (phy)
+		usb_put_transceiver(phy);
+
+	return ret;
+}
+EXPORT_SYMBOL(otg_send_event);
diff --git a/drivers/usb/otg/otg_id.c b/drivers/usb/otg/otg_id.c
new file mode 100644
index 0000000..7c38390
--- /dev/null
+++ b/drivers/usb/otg/otg_id.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Author:
+ *	Colin Cross <ccross@android.com>
+ *
+ * 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/kernel.h>
+#include <linux/mutex.h>
+#include <linux/notifier.h>
+#include <linux/usb/otg_id.h>
+
+static DEFINE_MUTEX(otg_id_lock);
+static struct plist_head otg_id_plist =
+	PLIST_HEAD_INIT(otg_id_plist);
+static struct otg_id_notifier_block *otg_id_active;
+static bool otg_id_cancelling;
+static bool otg_id_inited;
+static int otg_id_suspended;
+static bool otg_id_pending;
+
+static void otg_id_cancel(void)
+{
+	if (otg_id_active) {
+		otg_id_cancelling = true;
+		mutex_unlock(&otg_id_lock);
+
+		otg_id_active->cancel(otg_id_active);
+
+		mutex_lock(&otg_id_lock);
+		otg_id_cancelling = false;
+	}
+}
+
+static void __otg_id_notify(void)
+{
+	int ret = 0;
+	struct otg_id_notifier_block *otg_id_nb;
+	bool proxy_wait = false;
+	if (plist_head_empty(&otg_id_plist))
+		return;
+
+	plist_for_each_entry(otg_id_nb, &otg_id_plist, p) {
+		if (proxy_wait) {
+			if (otg_id_nb->proxy_wait)
+				ret = otg_id_nb->proxy_wait(otg_id_nb);
+		} else {
+			ret = otg_id_nb->detect(otg_id_nb);
+		}
+		if (ret == OTG_ID_HANDLED) {
+			otg_id_active = otg_id_nb;
+			return;
+		}
+		if (ret == OTG_ID_PROXY_WAIT)
+			proxy_wait = true;
+
+	}
+
+	WARN(1, "otg id event not handled");
+	otg_id_active = NULL;
+}
+
+int otg_id_init(void)
+{
+	mutex_lock(&otg_id_lock);
+
+	otg_id_inited = true;
+	__otg_id_notify();
+
+	mutex_unlock(&otg_id_lock);
+	return 0;
+}
+late_initcall(otg_id_init);
+
+/**
+ * otg_id_register_notifier
+ * @otg_id_nb: notifier block containing priority and callback function
+ *
+ * Register a notifier that will be called on any USB cable state change.
+ * The priority determines the order the callback will be called in, a higher
+ * number will be called first.  A callback function needs to determine the
+ * type of USB cable that is connected.  If it can determine the type, it
+ * should notify the appropriate drivers (for example, call an otg notifier
+ * with USB_EVENT_VBUS), and return OTG_ID_HANDLED.  Once a callback has
+ * returned OTG_ID_HANDLED, it is responsible for calling otg_id_notify() when
+ * the detected USB cable is disconnected.
+ */
+int otg_id_register_notifier(struct otg_id_notifier_block *otg_id_nb)
+{
+	plist_node_init(&otg_id_nb->p, otg_id_nb->priority);
+
+	mutex_lock(&otg_id_lock);
+	plist_add(&otg_id_nb->p, &otg_id_plist);
+
+	if (otg_id_inited) {
+		otg_id_cancel();
+		__otg_id_notify();
+	}
+
+	mutex_unlock(&otg_id_lock);
+
+	return 0;
+}
+
+void otg_id_unregister_notifier(struct otg_id_notifier_block *otg_id_nb)
+{
+	mutex_lock(&otg_id_lock);
+
+	plist_del(&otg_id_nb->p, &otg_id_plist);
+
+	if (otg_id_inited && (otg_id_active == otg_id_nb)) {
+		otg_id_cancel();
+		__otg_id_notify();
+	}
+
+	mutex_unlock(&otg_id_lock);
+}
+
+/**
+ * otg_id_notify
+ *
+ * Notify listeners on any USB cable state change.
+ *
+ * A driver may only call otg_id_notify if it returned OTG_ID_HANDLED the last
+ * time it's notifier was called, and it's cancel function has not been called.
+ */
+void otg_id_notify(void)
+{
+	mutex_lock(&otg_id_lock);
+
+	if (otg_id_cancelling)
+		goto out;
+
+	if (otg_id_suspended != 0) {
+		otg_id_pending = true;
+		goto out;
+	}
+
+	__otg_id_notify();
+out:
+	mutex_unlock(&otg_id_lock);
+}
+
+/**
+ * otg_id_suspend
+ *
+ * Mark the otg_id subsystem as going into suspend. From here on out,
+ * any notifications will be deferred until the last otg_id client resumes.
+ * If there is a pending notification when calling this function, it will
+ * return a negative errno and expects that the caller will abort suspend.
+ * Returs 0 on success.
+ */
+int otg_id_suspend(void)
+{
+	int ret = 0;
+
+	mutex_lock(&otg_id_lock);
+
+	/*
+	 * if there's a pending notification, tell the caller to abort suspend
+	 */
+	if (otg_id_suspended != 0 && otg_id_pending) {
+		pr_info("otg_id: pending notification, should abort suspend\n");
+		ret = -EBUSY;
+		goto out;
+	}
+
+	otg_id_suspended++;
+out:
+	mutex_unlock(&otg_id_lock);
+	return ret;
+}
+
+/**
+ * otg_id_resume
+ *
+ * Inform the otg_id subsystem that a client is resuming. If this is the
+ * last client to be resumed and there's a pending notification,
+ * otg_id_notify() is called.
+ */
+void otg_id_resume(void)
+{
+	mutex_lock(&otg_id_lock);
+	if (WARN(!otg_id_suspended, "unbalanced otg_id_resume\n"))
+		goto out;
+	if (--otg_id_suspended == 0) {
+		if (otg_id_pending) {
+			pr_info("otg_id: had pending notification\n");
+			otg_id_pending = false;
+			__otg_id_notify();
+		}
+	}
+out:
+	mutex_unlock(&otg_id_lock);
+}
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
index 7141d65..90b68d1 100644
--- a/drivers/usb/serial/Kconfig
+++ b/drivers/usb/serial/Kconfig
@@ -669,6 +669,15 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called ssu100.
 
+config USB_SERIAL_CSVT
+	tristate "USB serial driver for Circuit-Switched Video Telephony"
+	help
+	  Say Y here if you want to use usb serial driver for Circuit-Switched
+	  Video Telephony
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called csvt.
+
 config USB_SERIAL_DEBUG
 	tristate "USB Debugging Device"
 	help
diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile
index 07f198e..708bdbb 100644
--- a/drivers/usb/serial/Makefile
+++ b/drivers/usb/serial/Makefile
@@ -62,3 +62,4 @@
 obj-$(CONFIG_USB_SERIAL_XIRCOM)			+= keyspan_pda.o
 obj-$(CONFIG_USB_SERIAL_VIVOPAY_SERIAL)		+= vivopay-serial.o
 obj-$(CONFIG_USB_SERIAL_ZIO)			+= zio.o
+obj-$(CONFIG_USB_SERIAL_CSVT)			+= csvt.o
diff --git a/drivers/usb/serial/csvt.c b/drivers/usb/serial/csvt.c
new file mode 100644
index 0000000..3efdd77
--- /dev/null
+++ b/drivers/usb/serial/csvt.c
@@ -0,0 +1,443 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/tty_flip.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/cdc.h>
+#include <linux/usb/serial.h>
+#include <asm/unaligned.h>
+
+
+/* output control lines*/
+#define CSVT_CTRL_DTR		0x01
+#define CSVT_CTRL_RTS		0x02
+
+/* input control lines*/
+#define CSVT_CTRL_CTS		0x01
+#define CSVT_CTRL_DSR		0x02
+#define CSVT_CTRL_RI		0x04
+#define CSVT_CTRL_CD		0x08
+
+static int debug;
+module_param(debug, int, S_IRUGO | S_IWUSR);
+
+struct csvt_ctrl_dev {
+	struct mutex		dev_lock;
+
+	/* input control lines (DSR, CTS, CD, RI) */
+	unsigned int		cbits_tolocal;
+
+	/* output control lines (DTR, RTS) */
+	unsigned int		cbits_tomdm;
+};
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x05c6 , 0x904c, 0xff, 0xfe, 0xff)},
+	{}, /* terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct usb_driver csvt_driver = {
+	.name			= "qc_csvt",
+	.probe			= usb_serial_probe,
+	.disconnect		= usb_serial_disconnect,
+	.id_table		= id_table,
+	.suspend		= usb_serial_suspend,
+	.resume			= usb_serial_resume,
+	.supports_autosuspend	= true,
+};
+
+#define CSVT_IFC_NUM	4
+
+static int csvt_probe(struct usb_serial *serial, const struct usb_device_id *id)
+{
+	struct usb_host_interface	*intf =
+		serial->interface->cur_altsetting;
+
+	pr_debug("%s:\n", __func__);
+
+	if (intf->desc.bInterfaceNumber != CSVT_IFC_NUM)
+		return -ENODEV;
+
+	usb_enable_autosuspend(serial->dev);
+
+	return 0;
+}
+
+static int csvt_ctrl_write_cmd(struct csvt_ctrl_dev	*dev,
+	struct usb_serial_port *port)
+{
+	struct usb_device	*udev = port->serial->dev;
+	struct usb_interface	*iface = port->serial->interface;
+	unsigned int		iface_num;
+	int			retval = 0;
+
+	retval = usb_autopm_get_interface(iface);
+	if (retval < 0) {
+		dev_err(&port->dev, "%s: Unable to resume interface: %d\n",
+			__func__, retval);
+		return retval;
+	}
+
+	dev_dbg(&port->dev, "%s: cbits to mdm 0x%x\n", __func__,
+		dev->cbits_tomdm);
+
+	iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
+
+	retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+		USB_CDC_REQ_SET_CONTROL_LINE_STATE,
+		(USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE),
+		dev->cbits_tomdm,
+		iface_num,
+		NULL, 0, USB_CTRL_SET_TIMEOUT);
+	usb_autopm_put_interface(iface);
+
+	return retval;
+}
+
+static void csvt_ctrl_dtr_rts(struct usb_serial_port *port, int on)
+{
+	struct csvt_ctrl_dev	*dev = usb_get_serial_port_data(port);
+
+	if (!dev)
+		return;
+
+	dev_dbg(&port->dev, "%s", __func__);
+
+	mutex_lock(&dev->dev_lock);
+	if (on) {
+		dev->cbits_tomdm |= CSVT_CTRL_DTR;
+		dev->cbits_tomdm |= CSVT_CTRL_RTS;
+	} else {
+		dev->cbits_tomdm &= ~CSVT_CTRL_DTR;
+		dev->cbits_tomdm &= ~CSVT_CTRL_RTS;
+	}
+	mutex_unlock(&dev->dev_lock);
+
+	csvt_ctrl_write_cmd(dev, port);
+}
+
+static int get_serial_info(struct usb_serial_port *port,
+			   struct serial_struct __user *retinfo)
+{
+	struct serial_struct	tmp;
+
+	if (!retinfo)
+		return -EFAULT;
+
+	memset(&tmp, 0, sizeof(tmp));
+	tmp.line            = port->serial->minor;
+	tmp.port            = port->number;
+	tmp.baud_base       = tty_get_baud_rate(port->port.tty);
+	tmp.close_delay	    = port->port.close_delay / 10;
+	tmp.closing_wait    =
+		port->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
+				 ASYNC_CLOSING_WAIT_NONE :
+				 port->port.closing_wait / 10;
+
+	if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+		return -EFAULT;
+	return 0;
+}
+
+static int set_serial_info(struct usb_serial_port *port,
+			   struct serial_struct __user *newinfo)
+{
+	struct serial_struct	new_serial;
+	unsigned int		closing_wait;
+	unsigned int		close_delay;
+	int			retval = 0;
+
+	if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
+		return -EFAULT;
+
+	close_delay = new_serial.close_delay * 10;
+	closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
+			ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10;
+
+	mutex_lock(&port->port.mutex);
+
+	if (!capable(CAP_SYS_ADMIN)) {
+		if ((close_delay != port->port.close_delay) ||
+		    (closing_wait != port->port.closing_wait))
+			retval = -EPERM;
+		else
+			retval = -EOPNOTSUPP;
+	} else {
+		port->port.close_delay  = close_delay;
+		port->port.closing_wait = closing_wait;
+	}
+
+	mutex_unlock(&port->port.mutex);
+	return retval;
+}
+
+static int csvt_ctrl_ioctl(struct tty_struct *tty, unsigned int cmd,
+	unsigned long arg)
+{
+	struct usb_serial_port	*port = tty->driver_data;
+
+	dev_dbg(&port->dev, "%s cmd 0x%04x", __func__, cmd);
+
+	switch (cmd) {
+	case TIOCGSERIAL:
+		return get_serial_info(port,
+				       (struct serial_struct __user *) arg);
+	case TIOCSSERIAL:
+		return set_serial_info(port,
+				       (struct serial_struct __user *) arg);
+	default:
+		break;
+	}
+
+	dev_err(&port->dev, "%s arg not supported", __func__);
+
+	return -ENOIOCTLCMD;
+}
+
+static int csvt_ctrl_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port	*port = tty->driver_data;
+	struct csvt_ctrl_dev	*dev = usb_get_serial_port_data(port);
+	unsigned int		control_state = 0;
+
+	if (!dev)
+		return -ENODEV;
+
+	mutex_lock(&dev->dev_lock);
+	control_state = (dev->cbits_tomdm & CSVT_CTRL_DTR ? TIOCM_DTR : 0) |
+		(dev->cbits_tomdm & CSVT_CTRL_RTS ? TIOCM_RTS : 0) |
+		(dev->cbits_tolocal & CSVT_CTRL_DSR ? TIOCM_DSR : 0) |
+		(dev->cbits_tolocal & CSVT_CTRL_RI ? TIOCM_RI : 0) |
+		(dev->cbits_tolocal & CSVT_CTRL_CD ? TIOCM_CD : 0) |
+		(dev->cbits_tolocal & CSVT_CTRL_CTS ? TIOCM_CTS : 0);
+	mutex_unlock(&dev->dev_lock);
+
+	dev_dbg(&port->dev, "%s -- %x", __func__, control_state);
+
+	return control_state;
+}
+
+static int csvt_ctrl_tiocmset(struct tty_struct *tty,
+			       unsigned int set, unsigned int clear)
+{
+	struct usb_serial_port	*port = tty->driver_data;
+	struct csvt_ctrl_dev	*dev = usb_get_serial_port_data(port);
+
+	if (!dev)
+		return -ENODEV;
+
+	dev_dbg(&port->dev, "%s\n", __func__);
+
+	mutex_lock(&dev->dev_lock);
+	if (set & CSVT_CTRL_DTR)
+		dev->cbits_tomdm |= TIOCM_DTR;
+	if (set & CSVT_CTRL_RTS)
+		dev->cbits_tomdm |= TIOCM_RTS;
+
+	if (clear & CSVT_CTRL_DTR)
+		dev->cbits_tomdm &= ~TIOCM_DTR;
+	if (clear & CSVT_CTRL_RTS)
+		dev->cbits_tomdm &= ~TIOCM_RTS;
+	mutex_unlock(&dev->dev_lock);
+
+	return csvt_ctrl_write_cmd(dev, port);
+}
+
+static void csvt_ctrl_set_termios(struct tty_struct *tty,
+			  struct usb_serial_port *port,
+			  struct ktermios *old_termios)
+{
+	struct csvt_ctrl_dev	*dev = usb_get_serial_port_data(port);
+
+	if (!dev)
+		return;
+
+	dev_dbg(&port->dev, "%s", __func__);
+
+	/* Doesn't support option setting */
+	tty_termios_copy_hw(tty->termios, old_termios);
+
+	csvt_ctrl_write_cmd(dev, port);
+}
+
+static void csvt_ctrl_int_cb(struct urb *urb)
+{
+	int				status;
+	struct usb_cdc_notification	*ctrl;
+	struct usb_serial_port		*port = urb->context;
+	struct csvt_ctrl_dev		*dev;
+	unsigned int			ctrl_bits;
+	unsigned char			*data;
+
+	switch (urb->status) {
+	case 0:
+		/*success*/
+		break;
+	case -ESHUTDOWN:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPROTO:
+		 /* unplug */
+		 return;
+	case -EPIPE:
+		dev_err(&port->dev, "%s: stall on int endpoint\n", __func__);
+		/* TBD : halt to be cleared in work */
+	case -EOVERFLOW:
+	default:
+		pr_debug_ratelimited("%s: non zero urb status = %d\n",
+					__func__, urb->status);
+		goto resubmit_int_urb;
+	}
+
+	dev = usb_get_serial_port_data(port);
+	if (!dev)
+		return;
+
+	ctrl = urb->transfer_buffer;
+	data = (unsigned char *)(ctrl + 1);
+
+	usb_serial_debug_data(debug, &port->dev, __func__,
+					urb->actual_length, data);
+
+	switch (ctrl->bNotificationType) {
+	case USB_CDC_NOTIFY_NETWORK_CONNECTION:
+		dev_dbg(&port->dev, "%s network\n", ctrl->wValue ?
+					"connected to" : "disconnected from");
+		break;
+	case USB_CDC_NOTIFY_SERIAL_STATE:
+		ctrl_bits = get_unaligned_le16(data);
+		dev_dbg(&port->dev, "serial state: %d\n", ctrl_bits);
+		dev->cbits_tolocal = ctrl_bits;
+		break;
+	default:
+		dev_err(&port->dev, "%s: unknown notification %d received:"
+			"index %d len %d data0 %d data1 %d",
+			__func__, ctrl->bNotificationType, ctrl->wIndex,
+			ctrl->wLength, data[0], data[1]);
+	}
+
+resubmit_int_urb:
+	status = usb_submit_urb(urb, GFP_ATOMIC);
+	if (status)
+		dev_err(&port->dev, "%s: Error re-submitting Int URB %d\n",
+		__func__, status);
+
+}
+
+static int csvt_ctrl_open(struct tty_struct *tty,
+					struct usb_serial_port *port)
+{
+	int	retval;
+
+	dev_dbg(&port->dev, "%s port %d", __func__, port->number);
+
+	retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+	if (retval) {
+		dev_err(&port->dev, "usb_submit_urb(read int) failed\n");
+		return retval;
+	}
+
+	retval = usb_serial_generic_open(tty, port);
+	if (retval)
+		usb_kill_urb(port->interrupt_in_urb);
+
+	return retval;
+}
+
+static void csvt_ctrl_close(struct usb_serial_port *port)
+{
+	dev_dbg(&port->dev, "%s port %d", __func__, port->number);
+
+	usb_serial_generic_close(port);
+	usb_kill_urb(port->interrupt_in_urb);
+}
+
+static int csvt_ctrl_attach(struct usb_serial *serial)
+{
+	struct csvt_ctrl_dev	*dev;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	mutex_init(&dev->dev_lock);
+	usb_set_serial_port_data(serial->port[0], dev);
+
+	return 0;
+}
+
+static void csvt_ctrl_release(struct usb_serial *serial)
+{
+	struct usb_serial_port	*port = serial->port[0];
+	struct csvt_ctrl_dev	*dev = usb_get_serial_port_data(port);
+
+	dev_dbg(&port->dev, "%s", __func__);
+
+	kfree(dev);
+	usb_set_serial_port_data(port, NULL);
+}
+
+static struct usb_serial_driver csvt_device = {
+	.driver			= {
+		.owner	= THIS_MODULE,
+		.name	= "qc_csvt",
+	},
+	.description		= "qc_csvt",
+	.id_table		= id_table,
+	.num_ports		= 1,
+	.open			= csvt_ctrl_open,
+	.close			= csvt_ctrl_close,
+	.probe			= csvt_probe,
+	.dtr_rts		= csvt_ctrl_dtr_rts,
+	.tiocmget		= csvt_ctrl_tiocmget,
+	.tiocmset		= csvt_ctrl_tiocmset,
+	.ioctl			= csvt_ctrl_ioctl,
+	.set_termios		= csvt_ctrl_set_termios,
+	.read_int_callback	= csvt_ctrl_int_cb,
+	.attach			= csvt_ctrl_attach,
+	.release		= csvt_ctrl_release,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&csvt_device,
+	NULL,
+};
+
+static int __init csvt_init(void)
+{
+	int	retval;
+
+	retval = usb_serial_register_drivers(&csvt_driver, serial_drivers);
+	if (retval) {
+		err("%s: usb serial register failed\n", __func__);
+		return retval;
+	}
+
+	return 0;
+}
+
+static void __exit csvt_exit(void)
+{
+	usb_serial_deregister_drivers(&csvt_driver, serial_drivers);
+}
+
+module_init(csvt_init);
+module_exit(csvt_exit);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
index 0206b10..1f6d915 100644
--- a/drivers/usb/serial/qcserial.c
+++ b/drivers/usb/serial/qcserial.c
@@ -1,7 +1,7 @@
 /*
  * Qualcomm Serial USB driver
  *
- *	Copyright (c) 2008 QUALCOMM Incorporated.
+ *	Copyright (c) 2008, 2012 Code Aurora Forum. All rights reserved.
  *	Copyright (c) 2009 Greg Kroah-Hartman <gregkh@suse.de>
  *	Copyright (c) 2009 Novell Inc.
  *
@@ -108,10 +108,14 @@
 	{USB_DEVICE(0x1199, 0x9013)},	/* Sierra Wireless Gobi 3000 Modem device (MC8355) */
 	{USB_DEVICE(0x12D1, 0x14F0)},	/* Sony Gobi 3000 QDL */
 	{USB_DEVICE(0x12D1, 0x14F1)},	/* Sony Gobi 3000 Composite */
+	{USB_DEVICE(0x05c6, 0x9048)},	/* MDM9x15 device */
+	{USB_DEVICE(0x05c6, 0x904C)},	/* MDM9x15 device */
 	{ }				/* Terminating entry */
 };
 MODULE_DEVICE_TABLE(usb, id_table);
 
+#define EFS_SYNC_IFC_NUM	2
+
 static struct usb_driver qcdriver = {
 	.name			= "qcserial",
 	.probe			= usb_serial_probe,
@@ -233,6 +237,14 @@
 		}
 		break;
 
+	case 9:
+		if (ifnum != EFS_SYNC_IFC_NUM) {
+			kfree(data);
+			break;
+		}
+
+		retval = 0;
+		break;
 	default:
 		dev_err(&serial->dev->dev,
 			"unknown number of interfaces: %d\n", nintf);
diff --git a/drivers/usb/serial/usb-wwan.h b/drivers/usb/serial/usb-wwan.h
index c47b6ec..de8d490 100644
--- a/drivers/usb/serial/usb-wwan.h
+++ b/drivers/usb/serial/usb-wwan.h
@@ -31,10 +31,10 @@
 
 /* per port private data */
 
-#define N_IN_URB 4
-#define N_OUT_URB 4
-#define IN_BUFLEN 4096
-#define OUT_BUFLEN 4096
+#define N_IN_URB 5
+#define N_OUT_URB 5
+#define IN_BUFLEN 65536
+#define OUT_BUFLEN 65536
 
 struct usb_wwan_intf_private {
 	spinlock_t susp_lock;
diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c
index c88657d..519af39 100644
--- a/drivers/usb/serial/usb_wwan.c
+++ b/drivers/usb/serial/usb_wwan.c
@@ -406,6 +406,11 @@
 	portdata = usb_get_serial_port_data(port);
 	intfdata = serial->private;
 
+	/* explicitly set the driver mode to raw */
+	tty->raw = 1;
+	tty->real_raw = 1;
+
+	set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
 	dbg("%s", __func__);
 
 	/* Start reading from the IN endpoint */
@@ -548,7 +553,7 @@
 		init_usb_anchor(&portdata->delayed);
 
 		for (j = 0; j < N_IN_URB; j++) {
-			buffer = (u8 *) __get_free_page(GFP_KERNEL);
+			buffer = kmalloc(IN_BUFLEN, GFP_KERNEL);
 			if (!buffer)
 				goto bail_out_error;
 			portdata->in_buffer[j] = buffer;
@@ -577,8 +582,7 @@
 		kfree(portdata->out_buffer[j]);
 bail_out_error:
 	for (j = 0; j < N_IN_URB; j++)
-		if (portdata->in_buffer[j])
-			free_page((unsigned long)portdata->in_buffer[j]);
+		kfree(portdata->in_buffer[j]);
 	kfree(portdata);
 	return 1;
 }
@@ -624,8 +628,7 @@
 
 		for (j = 0; j < N_IN_URB; j++) {
 			usb_free_urb(portdata->in_urbs[j]);
-			free_page((unsigned long)
-				  portdata->in_buffer[j]);
+			kfree(portdata->in_buffer[j]);
 			portdata->in_urbs[j] = NULL;
 		}
 		for (j = 0; j < N_OUT_URB; j++) {
@@ -731,6 +734,10 @@
 		}
 	}
 
+	spin_lock_irq(&intfdata->susp_lock);
+	intfdata->suspended = 0;
+	spin_unlock_irq(&intfdata->susp_lock);
+
 	for (i = 0; i < serial->num_ports; i++) {
 		/* walk all ports */
 		port = serial->port[i];
@@ -756,9 +763,6 @@
 		play_delayed(port);
 		spin_unlock_irq(&intfdata->susp_lock);
 	}
-	spin_lock_irq(&intfdata->susp_lock);
-	intfdata->suspended = 0;
-	spin_unlock_irq(&intfdata->susp_lock);
 err_out:
 	return err;
 }
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 65b07f4..903d421 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -25,6 +25,8 @@
 
 source "drivers/gpu/ion/Kconfig"
 
+source "drivers/gpu/msm/Kconfig"
+
 config VGASTATE
        tristate
        default n
@@ -2351,13 +2353,6 @@
 	  Select this option if display contents should be inherited as set by
 	  the bootloader.
 
-config FB_MSM
-	tristate "MSM Framebuffer support"
-	depends on FB && ARCH_MSM
-	select FB_CFB_FILLRECT
-	select FB_CFB_COPYAREA
-	select FB_CFB_IMAGEBLIT
-
 config FB_MX3
 	tristate "MX3 Framebuffer support"
 	depends on FB && MX3_IPU
@@ -2413,6 +2408,8 @@
 	  Choose this option if you want to use the Unigfx device as a
 	  framebuffer device. Without the support of PCI & AGP.
 
+source "drivers/video/msm/Kconfig"
+
 source "drivers/video/omap/Kconfig"
 source "drivers/video/omap2/Kconfig"
 source "drivers/video/exynos/Kconfig"
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c
index c6ce416..d6a664a 100644
--- a/drivers/video/fbmem.c
+++ b/drivers/video/fbmem.c
@@ -1183,14 +1183,11 @@
 		unlock_fb_info(info);
 		break;
 	default:
-		if (!lock_fb_info(info))
-			return -ENODEV;
 		fb = info->fbops;
 		if (fb->fb_ioctl)
 			ret = fb->fb_ioctl(info, cmd, arg);
 		else
 			ret = -ENOTTY;
-		unlock_fb_info(info);
 	}
 	return ret;
 }
diff --git a/drivers/video/msm/Kconfig b/drivers/video/msm/Kconfig
new file mode 100644
index 0000000..b8d1df8
--- /dev/null
+++ b/drivers/video/msm/Kconfig
@@ -0,0 +1,924 @@
+
+source "drivers/video/msm/vidc/Kconfig"
+
+config FB_MSM
+	tristate "MSM Framebuffer support"
+	depends on FB && ARCH_MSM
+	select FB_BACKLIGHT if FB_MSM_BACKLIGHT
+	select NEW_LEDS
+	select LEDS_CLASS
+	select FB_CFB_FILLRECT
+	select FB_CFB_COPYAREA
+	select FB_CFB_IMAGEBLIT
+	---help---
+	  Support for MSM Framebuffer.
+
+if FB_MSM
+
+config FB_MSM_BACKLIGHT
+	bool "Support for backlight control"
+	default y
+	---help---
+	  Say Y here if you want to control the backlight of your display.
+
+config FB_MSM_LOGO
+	bool "MSM Frame Buffer Logo"
+	default n
+	---help---
+	  Show /initlogo.rle during boot.
+
+config FB_MSM_LCDC_HW
+	bool
+	default n
+
+config FB_MSM_TRIPLE_BUFFER
+	bool "Support for triple frame buffer"
+	default n
+
+config FB_MSM_MDP_HW
+	bool
+	default n
+
+choice
+	prompt "MDP HW version"
+	default FB_MSM_MDP22
+
+config FB_MSM_MDP22
+	select FB_MSM_MDP_HW
+	bool "MDP HW ver2.2"
+	---help---
+	  Support for MSM MDP HW revision 2.2
+	  Say Y here if this is msm7201 variant platform.
+
+config FB_MSM_MDP30
+	select FB_MSM_LCDC_HW
+	bool "MDP HW ver3.0"
+	---help---
+	  Support for MSM MDP HW revision 3.0
+	  Say Y here if this is msm7x25 variant platform.
+
+config FB_MSM_MDP303
+	depends on FB_MSM_MDP30
+	select FB_MSM_MDP_HW
+	bool "MDP HW ver3.03"
+	default n
+	---help---
+	  Support for MSM MDP HW revision 3.03. This is a new version of
+	  MDP3.0 which has the required functionality to support the features
+	  required for msm7x2xA platform.
+	  Say Y here if this is msm7x2xA variant platform.
+
+config FB_MSM_MDP31
+	select FB_MSM_LCDC_HW
+	select FB_MSM_MDP_HW
+	bool "MDP HW ver3.1"
+	---help---
+	  Support for MSM MDP HW revision 3.1
+	  Say Y here if this is msm8x50 variant platform.
+
+config FB_MSM_MDP40
+	select FB_MSM_LCDC_HW
+	select FB_MSM_MDP_HW
+	bool "MDP HW ver4.0"
+	---help---
+	  Support for MSM MDP HW revision 4.0
+	  Say Y here if this is msm7x30 variant platform.
+
+config FB_MSM_MDP_NONE
+	bool "MDP HW None"
+	---help---
+	  Say Y here if this is mdm platform.
+
+endchoice
+
+config FB_MSM_EBI2
+	bool
+	default n
+
+config FB_MSM_MDDI
+	bool
+	default n
+
+config FB_MSM_MIPI_DSI
+	bool
+	default n
+
+config FB_MSM_LCDC
+	bool
+	default n
+
+config FB_MSM_LVDS
+	bool
+	default n
+
+config FB_MSM_OVERLAY
+	depends on FB_MSM_MDP40 && ANDROID_PMEM
+	bool "MDP4 overlay support"
+	default n
+
+config FB_MSM_DTV
+	depends on FB_MSM_OVERLAY
+	bool
+	default n
+
+config FB_MSM_EXTMDDI
+	bool
+	default n
+
+config FB_MSM_TVOUT
+	bool
+	default n
+
+config FB_MSM_MDDI_TOSHIBA_COMMON
+	bool
+	select FB_MSM_MDDI
+	default n
+
+config FB_MSM_MDDI_TOSHIBA_COMMON_VGA
+	bool
+	select FB_MSM_MDDI_TOSHIBA_COMMON
+	default n
+
+config FB_MSM_MDDI_ORISE
+	bool
+	select FB_MSM_MDDI
+	default n
+
+config FB_MSM_MDDI_QUICKVX
+	bool
+	select FB_MSM_MDDI
+	default n
+
+config FB_MSM_MDDI_AUTO_DETECT
+	bool
+	select FB_MSM_MDDI
+	default n
+
+config FB_MSM_LCDC_AUTO_DETECT
+	bool
+	select FB_MSM_LCDC
+	default n
+
+config FB_MSM_LCDC_PANEL
+	bool
+	select FB_MSM_LCDC
+	default n
+
+config FB_MSM_MIPI_DSI_TOSHIBA
+	bool
+	select FB_MSM_MIPI_DSI
+	default n
+
+config FB_MSM_MIPI_DSI_RENESAS
+	bool
+	select FB_MSM_MIPI_DSI
+	default n
+
+config FB_MSM_MIPI_DSI_TRULY
+	bool
+	select FB_MSM_MIPI_DSI
+
+config FB_MSM_MIPI_DSI_SIMULATOR
+	bool
+	select FB_MSM_MIPI_DSI
+	default n
+
+config FB_MSM_MIPI_DSI_NOVATEK
+        bool
+        select FB_MSM_MIPI_DSI
+        default n
+
+config FB_MSM_MIPI_DSI_NT35510
+	bool
+	select FB_MSM_MIPI_DSI
+
+config FB_MSM_MIPI_DSI_ORISE
+        bool
+        select FB_MSM_MIPI_DSI
+        default n
+
+config FB_MSM_MIPI_DSI_NT35516
+	bool
+	select FB_MSM_MIPI_DSI
+
+config FB_MSM_MIPI_DSI_TC358764_DSI2LVDS
+	bool
+	select FB_MSM_MIPI_DSI
+	---help---
+	  Support for Toshiba MIPI DSI-to-LVDS bridge.
+	  The chip supports 1366x768 24-bit
+	  using a single LVDS link
+	  and up to WUXGA 1920x1200 18-bit
+	  using a dual LVDS link.
+
+config FB_MSM_LCDC_ST15_WXGA
+    bool
+    select FB_MSM_LCDC_PANEL
+    default n
+
+config FB_MSM_LCDC_ST15_PANEL
+    depends on FB_MSM_LCDC_HW
+    bool "LCDC ST1.5 Panel"
+    select FB_MSM_LCDC_ST15_WXGA
+    ---help---
+      Support for ST1.5 WXGA (1366x768) panel
+
+config FB_MSM_LCDC_PRISM_WVGA
+	bool
+	select FB_MSM_LCDC_PANEL
+	default n
+
+config FB_MSM_LCDC_SAMSUNG_WSVGA
+	bool
+	select FB_MSM_LCDC_PANEL
+	default n
+
+config FB_MSM_LCDC_CHIMEI_WXGA
+	bool
+	select FB_MSM_LCDC_PANEL
+	default n
+
+config FB_MSM_LCDC_GORDON_VGA
+	bool
+	select FB_MSM_LCDC_PANEL
+	default n
+
+config FB_MSM_LCDC_TOSHIBA_WVGA_PT
+	bool
+	select FB_MSM_LCDC_PANEL
+	default n
+
+config FB_MSM_LCDC_TOSHIBA_FWVGA_PT
+	bool
+	select FB_MSM_LCDC_PANEL
+	default n
+
+config FB_MSM_LCDC_SHARP_WVGA_PT
+	bool
+	select FB_MSM_LCDC_PANEL
+	default n
+
+config FB_MSM_LCDC_AUO_WVGA
+	bool
+	select FB_MSM_LCDC_PANEL
+	default n
+
+config FB_MSM_LCDC_TRULY_HVGA_IPS3P2335
+        bool
+        select FB_MSM_LCDC_PANEL
+        default n
+
+config FB_MSM_LCDC_TRULY_HVGA_IPS3P2335_PT_PANEL
+        depends on FB_MSM_LCDC_HW
+        bool "LCDC Truly HVGA PT Panel"
+        select FB_MSM_LCDC_TRULY_HVGA_IPS3P2335
+        default n
+        ---help---
+        Support for LCDC Truly HVGA PT panel
+
+
+config FB_MSM_LCDC_SAMSUNG_OLED_PT
+	bool
+	select FB_MSM_LCDC_PANEL
+	default n
+
+config FB_MSM_LCDC_NT35582_WVGA
+	bool
+	select FB_MSM_LCDC_PANEL
+	default n
+
+config FB_MSM_LCDC_WXGA
+	bool
+	select FB_MSM_LCDC_PANEL
+	default n
+
+config FB_MSM_LVDS_CHIMEI_WXGA
+	bool
+	select FB_MSM_LVDS
+	default n
+
+config FB_MSM_MIPI_TOSHIBA_VIDEO_WVGA_PT
+	bool
+	select FB_MSM_MIPI_DSI_TOSHIBA
+	default n
+
+config FB_MSM_MIPI_TOSHIBA_VIDEO_WSVGA_PT
+	bool
+	select FB_MSM_MIPI_DSI_TOSHIBA
+	default n
+
+config FB_MSM_MIPI_TOSHIBA_VIDEO_WUXGA
+	bool
+	select FB_MSM_MIPI_DSI_TOSHIBA
+	default n
+
+config FB_MSM_MIPI_NOVATEK_VIDEO_QHD_PT
+	bool
+	select FB_MSM_MIPI_DSI_NOVATEK
+	default n
+
+config FB_MSM_MIPI_NOVATEK_CMD_QHD_PT
+	bool
+	select FB_MSM_MIPI_DSI_NOVATEK
+	default n
+
+config FB_MSM_MIPI_ORISE_VIDEO_720P_PT
+	bool
+	select FB_MSM_MIPI_DSI_ORISE
+	default n
+
+config FB_MSM_MIPI_ORISE_CMD_720P_PT
+	bool
+	select FB_MSM_MIPI_DSI_ORISE
+	default n
+
+config FB_MSM_MIPI_RENESAS_VIDEO_FWVGA_PT
+	bool
+	select FB_MSM_MIPI_DSI_RENESAS
+	default n
+
+config FB_MSM_MIPI_RENESAS_CMD_FWVGA_PT
+	bool
+	select FB_MSM_MIPI_DSI_RENESAS
+	default n
+
+config FB_MSM_MIPI_NT35510_VIDEO_WVGA_PT
+	bool
+	select FB_MSM_MIPI_DSI_NT35510
+	default n
+
+config FB_MSM_MIPI_NT35510_CMD_WVGA_PT
+	bool
+	select FB_MSM_MIPI_DSI_NT35510
+	default n
+
+config FB_MSM_MIPI_NT35516_VIDEO_QHD_PT
+        bool
+        select FB_MSM_MIPI_DSI_NT35516
+        default n
+
+config FB_MSM_MIPI_NT35516_CMD_QHD_PT
+        bool
+        select FB_MSM_MIPI_DSI_NT35516
+        default n
+
+
+config FB_MSM_MIPI_CHIMEI_WXGA
+	bool "LVDS Chimei WXGA Panel using Toshiba MIPI DSI-to-LVDS bridge."
+	select FB_MSM_MIPI_DSI_TC358764_DSI2LVDS
+	---help---
+	  Support for Chimei WXGA (1366x768) panel.
+	  The panel is using a serial LVDS input.
+	  The panel is connected to the host
+	  via Toshiba DSI-to-LVDS bridge.
+
+config FB_MSM_MIPI_CHIMEI_WUXGA
+	bool "LVDS Chimei WUXGA Panel using Toshiba MIPI DSI-to-LVDS bridge."
+	select FB_MSM_MIPI_DSI_TC358764_DSI2LVDS
+	---help---
+	  Support for Chimei WUXGA (1920x1200) panel.
+	  The panel is using a serial LVDS input.
+	  The panel is connected to the host
+	  via Toshiba DSI-to-LVDS bridge.
+
+config FB_MSM_MIPI_TRULY_VIDEO_WVGA_PT
+	bool
+	select FB_MSM_MIPI_DSI_TRULY
+
+config FB_MSM_MIPI_SIMULATOR_VIDEO
+	bool
+	select FB_MSM_MIPI_DSI_SIMULATOR
+	default n
+
+config FB_MSM_OVERLAY0_WRITEBACK
+	depends on FB_MSM_OVERLAY
+        bool "MDP overlay0 write back mode enable"
+	---help---
+	  Support for MDP4 OVERLAY0 write back mode
+
+
+config FB_MSM_OVERLAY1_WRITEBACK
+        depends on FB_MSM_OVERLAY
+        bool "MDP overlay1 write back mode enable"
+        ---help---
+          Support for MDP4 OVERLAY1 write back mode
+
+config FB_MSM_WRITEBACK_MSM_PANEL
+	depends on FB_MSM_OVERLAY
+        bool "MDP overlay write back panel enable"
+	---help---
+	  Support for MDP4 OVERLAY write back mode
+choice
+	prompt "LCD Panel"
+	default FB_MSM_MDDI_AUTO_DETECT
+
+config FB_MSM_LCDC_PRISM_WVGA_PANEL
+	depends on FB_MSM_LCDC_HW
+	bool "LCDC Prism WVGA Panel"
+	select FB_MSM_LCDC_PRISM_WVGA
+	---help---
+	  Support for LCDC Prism WVGA (800x480) panel
+
+config FB_MSM_LCDC_SAMSUNG_WSVGA_PANEL
+	depends on FB_MSM_LCDC_HW
+	bool "LCDC Samsung WSVGA Panel"
+	select FB_MSM_LCDC_SAMSUNG_WSVGA
+	---help---
+	  Support for LCDC Samsung WSVGA (1024x600) panel
+
+config FB_MSM_LCDC_CHIMEI_WXGA_PANEL
+	depends on FB_MSM_LCDC_HW
+	bool "LCDC Chimei WXGA Panel"
+	select FB_MSM_LCDC_CHIMEI_WXGA
+	---help---
+	  Support for LCDC Chimei WXGA (1366x768) panel
+
+config FB_MSM_LCDC_GORDON_VGA_PANEL
+	depends on FB_MSM_LCDC_HW
+	bool "LCDC Gordon VGA Panel"
+	select FB_MSM_LCDC_GORDON_VGA
+	---help---
+	  Support for LCDC Gordon VGA (480x640) panel
+
+config FB_MSM_LCDC_TOSHIBA_WVGA_PT_PANEL
+	depends on FB_MSM_LCDC_HW
+	bool "LCDC Toshiba WVGA PT Panel"
+	select FB_MSM_LCDC_TOSHIBA_WVGA_PT
+	---help---
+	  Support for LCDC Toshiba WVGA PT (480x800) panel
+
+config FB_MSM_LCDC_TOSHIBA_FWVGA_PT_PANEL
+	depends on FB_MSM_LCDC_HW
+	bool "LCDC Toshiba FWVGA PT Panel"
+	select FB_MSM_LCDC_TOSHIBA_FWVGA_PT
+	---help---
+	  Support for LCDC Toshiba FWVGA PT (480x864) panel. This
+	  configuration has to be selected to support the Toshiba
+	  FWVGA (480x864) portrait panel.
+
+config FB_MSM_LCDC_SHARP_WVGA_PT_PANEL
+	depends on FB_MSM_LCDC_HW
+	bool "LCDC Sharp WVGA PT Panel"
+	select FB_MSM_LCDC_SHARP_WVGA_PT
+	---help---
+	  Support for LCDC Sharp WVGA PT (480x800) panel
+
+config FB_MSM_LCDC_AUO_WVGA_PANEL
+	depends on FB_MSM_LCDC_HW
+	bool "LCDC AUO WVGA Panel"
+	select FB_MSM_LCDC_AUO_WVGA
+	---help---
+	  Support for LCDC AUO WVGA(480x800) panel
+
+config FB_MSM_LCDC_NT35582_PANEL
+	depends on FB_MSM_LCDC_HW
+	bool "LCDC NT35582 WVGA Panel"
+	select FB_MSM_LCDC_NT35582_WVGA
+	---help---
+	  Support for LCDC NT35582 WVGA(480x800) panel
+
+config FB_MSM_LCDC_SAMSUNG_OLED_PT_PANEL
+	depends on FB_MSM_LCDC_HW
+	bool "LCDC Samsung OLED PT Panel"
+	select FB_MSM_LCDC_SAMSUNG_OLED_PT
+	---help---
+	  Support for LCDC Samsung OLED PT (480x800) panel
+
+config FB_MSM_LVDS_CHIMEI_WXGA_PANEL
+        bool "LVDS Chimei WXGA Panel"
+        select FB_MSM_LVDS_CHIMEI_WXGA
+        ---help---
+          Support for LVDS Chimei WXGA(1366x768) panel
+
+config FB_MSM_TRY_MDDI_CATCH_LCDC_PRISM
+	depends on FB_MSM_LCDC_HW
+	bool "MDDI Panel Auto Detect + LCDC Prism WVGA"
+	select FB_MSM_MDDI_AUTO_DETECT
+	select FB_MSM_LCDC_PRISM_WVGA
+	select FB_MSM_LCDC_GORDON_VGA
+	select FB_MSM_LCDC_WXGA
+	select FB_MSM_LCDC_TOSHIBA_WVGA_PT
+	select FB_MSM_LCDC_TOSHIBA_FWVGA_PT
+	select FB_MSM_LCDC_SHARP_WVGA_PT
+	select FB_MSM_LCDC_ST15_WXGA
+	---help---
+	  Support for MDDI panel auto detect.
+	  If it can't find any MDDI panel, it will load an LCDC panel.
+
+config FB_MSM_MIPI_PANEL_DETECT
+	bool "MIPI Panel Detect"
+	select FB_MSM_MIPI_TOSHIBA_VIDEO_WVGA_PT
+	select FB_MSM_MIPI_TOSHIBA_VIDEO_WSVGA_PT
+	select FB_MSM_MIPI_TOSHIBA_VIDEO_WUXGA
+	select FB_MSM_MIPI_RENESAS_VIDEO_FWVGA_PT
+	select FB_MSM_MIPI_RENESAS_CMD_FWVGA_PT
+	select FB_MSM_MIPI_TRULY_VIDEO_WVGA_PT
+	select FB_MSM_MIPI_NOVATEK_VIDEO_QHD_PT
+	select FB_MSM_MIPI_NOVATEK_CMD_QHD_PT
+	select FB_MSM_MIPI_NT35510_VIDEO_WVGA_PT
+	select FB_MSM_MIPI_NT35510_CMD_WVGA_PT
+	select FB_MSM_MIPI_ORISE_VIDEO_720P_PT
+	select FB_MSM_MIPI_ORISE_CMD_720P_PT
+	select FB_MSM_MIPI_NT35516_VIDEO_QHD_PT
+	select FB_MSM_MIPI_NT35516_CMD_QHD_PT
+	select FB_MSM_MIPI_SIMULATOR_VIDEO
+	select FB_MSM_MIPI_CHIMEI_WXGA
+	select FB_MSM_MIPI_CHIMEI_WUXGA
+	---help---
+	  Support for MIPI panel auto detect
+
+config FB_MSM_MDDI_PANEL_AUTO_DETECT
+	bool "MDDI Panel Auto Detect"
+	select FB_MSM_MDDI_AUTO_DETECT
+	---help---
+	  Support for MDDI panel auto detect
+
+config FB_MSM_LCDC_PANEL_AUTO_DETECT
+	bool "LCDC Panel Auto Detect"
+	select FB_MSM_LCDC_AUTO_DETECT
+	select FB_MSM_LCDC_SAMSUNG_WSVGA
+	select FB_MSM_LCDC_AUO_WVGA
+	select FB_MSM_LCDC_NT35582_WVGA
+	select FB_MSM_LCDC_SAMSUNG_OLED_PT
+	---help---
+	  Support for LCDC panel auto detect
+
+config FB_MSM_LCDC_MIPI_PANEL_AUTO_DETECT
+	bool "LCDC + MIPI Panel Auto Detect"
+	select FB_MSM_LCDC_AUTO_DETECT
+	select FB_MSM_LCDC_SAMSUNG_WSVGA
+	select FB_MSM_LCDC_AUO_WVGA
+	select FB_MSM_LCDC_SAMSUNG_OLED_PT
+	select FB_MSM_LCDC_NT35582_WVGA
+	select FB_MSM_LCDC_TOSHIBA_FWVGA_PT
+	select FB_MSM_MIPI_TOSHIBA_VIDEO_WVGA_PT
+	select FB_MSM_MIPI_TOSHIBA_VIDEO_WSVGA_PT
+	select FB_MSM_MIPI_RENESAS_VIDEO_FWVGA_PT
+	select FB_MSM_MIPI_RENESAS_CMD_FWVGA_PT
+	select FB_MSM_MIPI_NOVATEK_VIDEO_QHD_PT
+	select FB_MSM_MIPI_NOVATEK_CMD_QHD_PT
+	select FB_MSM_MIPI_NT35510_VIDEO_WVGA_PT
+	select FB_MSM_MIPI_NT35510_CMD_WVGA_PT
+	select FB_MSM_MIPI_NT35516_VIDEO_QHD_PT
+	select FM_MSM_MIPI_NT35516_CMD_QHD_PT
+	select FB_MSM_MIPI_SIMULATOR_VIDEO
+	---help---
+	  Support for LCDC + MIPI panel auto detect
+
+config FB_MSM_LVDS_MIPI_PANEL_DETECT
+	bool "LVDS + MIPI Panel Auto Detect"
+	select FB_MSM_LVDS_CHIMEI_WXGA
+	select FB_MSM_MIPI_TOSHIBA_VIDEO_WVGA_PT
+	select FB_MSM_MIPI_TOSHIBA_VIDEO_WSVGA_PT
+	select FB_MSM_MIPI_TOSHIBA_VIDEO_WUXGA
+	select FB_MSM_MIPI_RENESAS_VIDEO_FWVGA_PT
+	select FB_MSM_MIPI_RENESAS_CMD_FWVGA_PT
+	select FB_MSM_MIPI_TRULY_VIDEO_WVGA_PT
+	select FB_MSM_MIPI_NOVATEK_VIDEO_QHD_PT
+	select FB_MSM_MIPI_NOVATEK_CMD_QHD_PT
+	select FB_MSM_MIPI_NT35510_VIDEO_WVGA_PT
+	select FB_MSM_MIPI_NT35510_CMD_WVGA_PT
+	select FB_MSM_MIPI_ORISE_VIDEO_720P_PT
+	select FB_MSM_MIPI_ORISE_CMD_720P_PT
+	select FB_MSM_MIPI_SIMULATOR_VIDEO
+	select FB_MSM_MIPI_CHIMEI_WXGA
+	select FB_MSM_MIPI_CHIMEI_WUXGA
+	---help---
+	  Support for LVDS + MIPI panel auto detect
+
+config FB_MSM_MDDI_PRISM_WVGA
+	bool "MDDI Prism WVGA Panel"
+	select FB_MSM_MDDI
+	---help---
+	  Support for MDDI Prism WVGA (800x480) panel
+
+config FB_MSM_MDDI_TOSHIBA_WVGA_PORTRAIT
+	bool "MDDI Toshiba WVGA Portrait Panel"
+	select FB_MSM_MDDI_TOSHIBA_COMMON
+	---help---
+	  Support for MDDI Toshiba WVGA (480x800) panel
+
+config FB_MSM_MDDI_TOSHIBA_VGA
+	bool "MDDI Toshiba VGA Panel"
+	select FB_MSM_MDDI_TOSHIBA_COMMON_VGA
+	---help---
+	  Support for MDDI Toshiba VGA (480x640) and QCIF (176x220) panel
+
+config FB_MSM_MDDI_TOSHIBA_WVGA
+	bool "MDDI Toshiba WVGA panel"
+	select FB_MSM_MDDI_TOSHIBA_COMMON
+	---help---
+	  Support for MDDI Toshiba (800x480) WVGA panel
+
+config FB_MSM_MDDI_SHARP_QVGA_128x128
+	bool "MDDI Sharp QVGA Dual Panel"
+	select FB_MSM_MDDI
+	---help---
+	  Support for MDDI Sharp QVGA (240x320) and 128x128 dual panel
+
+config FB_MSM_MIPI_TOSHIBA_VIDEO_WVGA_PT_PANEL
+	bool "MIPI Toshiba WVGA PT Panel"
+	select FB_MSM_MIPI_TOSHIBA_VIDEO_WVGA_PT
+
+config FB_MSM_MIPI_TOSHIBA_VIDEO_WSVGA_PT_PANEL
+	bool "MIPI Toshiba WSVGA PT Panel"
+	select FB_MSM_MIPI_TOSHIBA_VIDEO_WSVGA_PT
+
+config FB_MSM_MIPI_TOSHIBA_VIDEO_WUXGA_PANEL
+	bool "MIPI Toshiba WUXGA (1920x1200) Panel"
+	select FB_MSM_MIPI_TOSHIBA_VIDEO_WUXGA
+
+config FB_MSM_MIPI_NOVATEK_VIDEO_QHD_PT_PANEL
+	bool "MIPI NOVATEK VIDEO QHD PT Panel"
+	select FB_MSM_MIPI_NOVATEK_VIDEO_QHD_PT
+
+config FB_MSM_MIPI_NOVATEK_CMD_QHD_PT_PANEL
+	bool "MIPI NOVATEK CMD QHD PT Panel"
+	select FB_MSM_MIPI_NOVATEK_CMD_QHD_PT
+
+config FB_MSM_MIPI_ORISE_VIDEO_720P_PT_PANEL
+	bool "MIPI ORISE VIDEO 720P PT Panel"
+	select FB_MSM_MIPI_ORISE_VIDEO_720P_PT
+
+config FB_MSM_MIPI_ORISE_CMD_720P_PT_PANEL
+	bool "MIPI ORISE CMD 720P PT Panel"
+	select FB_MSM_MIPI_ORISE_CMD_720P_PT
+
+config FB_MSM_MIPI_RENESAS_VIDEO_FWVGA_PT_PANEL
+	bool "MIPI Renesas Video FWVGA PT Panel"
+	select FB_MSM_MIPI_RENESAS_VIDEO_FWVGA_PT
+
+config FB_MSM_MIPI_RENESAS_CMD_FWVGA_PT_PANEL
+	bool "MIPI Renesas Command FWVGA PT Panel"
+	select FB_MSM_MIPI_RENESAS_CMD_FWVGA_PT
+
+config FB_MSM_MIPI_CHIMEI_WXGA_PANEL
+	bool "MIPI Chimei WXGA PT Panel"
+	select FB_MSM_MIPI_CHIMEI_WXGA
+
+config FB_MSM_MIPI_CHIMEI_WUXGA_PANEL
+	bool "MIPI Chimei WUXGA Panel"
+	select FB_MSM_MIPI_CHIMEI_WUXGA
+
+config FB_MSM_MIPI_TRULY_VIDEO_WVGA_PT_PANEL
+	bool "MIPI Truly Video WVGA PT Panel"
+	select FB_MSM_MIPI_TRULY_VIDEO_WVGA_PT
+
+config FB_MSM_MIPI_NT35510_VIDEO_WVGA_PT_PANEL
+	bool "MIPI NT35510 Video WVGA PT Panel"
+	select FB_MSM_MIPI_NT35510_VIDEO_WVGA_PT
+
+config FB_MSM_MIPI_NT35510_CMD_WVGA_PT_PANEL
+	bool "MIPI NT35510 Command WVGA PT Panel"
+	select FB_MSM_MIPI_NT35510_CMD_WVGA_PT
+
+config FB_MSM_MIPI_NT35516_VIDEO_QHD_PT_PANEL
+        bool "MIPI NT35516 Video qHD PT Panel"
+        select FB_MSM_MIPI_NT35516_VIDEO_QHD_PT
+
+config FB_MSM_MIPI_NT35516_CMD_QHD_PT_PANEL
+        bool "MIPI NT35516 Command qHD PT Panel"
+        select FB_MSM_MIPI_NT35516_CMD_QHD_PT
+
+config FB_MSM_MIPI_SIMULATOR_VIDEO_PANEL
+	bool "MIPI Simulator Video Panel"
+	select FB_MSM_MIPI_SIMULATOR_VIDEO
+
+config FB_MSM_EBI2_TMD_QVGA_EPSON_QCIF
+	bool "EBI2 TMD QVGA Epson QCIF Dual Panel"
+	select FB_MSM_EBI2
+	---help---
+	  Support for EBI2 TMD QVGA (240x320) and Epson QCIF (176x220) panel
+
+config FB_MSM_HDMI_AS_PRIMARY
+	depends on FB_MSM_HDMI_COMMON
+	bool "Use HDMI as primary panel"
+	---help---
+	Support for using HDMI as primary
+
+config FB_MSM_PANEL_NONE
+	bool "NONE"
+	---help---
+	  This will disable LCD panel
+endchoice
+
+choice
+	prompt "Secondary LCD Panel"
+	depends on  FB_MSM_MDP31
+	default FB_MSM_SECONDARY_PANEL_NONE
+
+config FB_MSM_LCDC_EXTERNAL_WXGA
+	depends on FB_MSM_MDP31
+	bool "External WXGA on LCDC"
+	select FB_MSM_LCDC_PANEL
+	---help---
+	  Support for external WXGA display (1280x720)
+
+config FB_MSM_HDMI_SII_EXTERNAL_720P
+	depends on FB_MSM_MDP31
+	bool "External SiI9022 HDMI 720P"
+	select FB_MSM_LCDC_PANEL
+	---help---
+	  Support for external HDMI 720p display (1280x720p)
+	  Using SiI9022 chipset
+
+config FB_MSM_SECONDARY_PANEL_NONE
+	bool "NONE"
+	---help---
+	  No secondary panel
+endchoice
+
+config FB_MSM_LCDC_DSUB
+	depends on FB_MSM_LCDC_SAMSUNG_WSVGA && FB_MSM_MDP40 && FB_MSM_LCDC_HW
+	bool "External DSUB support"
+	default n
+	---help---
+	  Support for external DSUB (VGA) display up to 1440x900.  The DSUB
+	  display shares the same video bus as the primary LCDC attached display.
+	  Typically only one of the two displays can be used at one time.
+
+config FB_MSM_EXT_INTERFACE_COMMON
+	bool
+	default n
+
+config FB_MSM_HDMI_COMMON
+	bool
+	default n
+
+config FB_MSM_HDMI_3D
+	bool
+	default n
+
+config FB_MSM_HDMI_ADV7520_PANEL
+	depends on FB_MSM_MDP40 && FB_MSM_OVERLAY
+        bool "LCDC HDMI ADV7520 720p Panel"
+        select FB_MSM_DTV
+        select FB_MSM_EXT_INTERFACE_COMMON
+	select FB_MSM_HDMI_COMMON
+	default n
+        ---help---
+        Support for LCDC 720p HDMI panel attached to ADV7520
+
+config FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT
+        depends on FB_MSM_HDMI_ADV7520_PANEL
+        bool "Use HDCP mode"
+        default y
+        ---help---
+          Support for HDCP mode for ADV7520 HDMI 720p Panel
+          Choose to enable HDCP
+
+
+config FB_MSM_HDMI_MSM_PANEL
+	depends on FB_MSM_MDP40
+	bool "MSM HDMI 1080p Panel"
+	select FB_MSM_DTV
+        select FB_MSM_EXT_INTERFACE_COMMON
+	select FB_MSM_HDMI_COMMON
+	select FB_MSM_HDMI_3D
+	default n
+	---help---
+	  Support for 480p/720p/1080i/1080p output through MSM HDMI
+
+config FB_MSM_HDMI_MSM_PANEL_DVI_SUPPORT
+	depends on FB_MSM_HDMI_MSM_PANEL
+	bool "Use DVI mode"
+	default n
+	---help---
+	  Support for DVI mode for MSM HDMI 1080p Panel
+
+config FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
+	depends on FB_MSM_HDMI_MSM_PANEL
+	bool "Use HDCP mode"
+	default y
+	---help---
+	  Support for HDCP mode for MSM HDMI 1080p Panel
+	  Choose to enable HDCP
+
+config FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
+	depends on FB_MSM_HDMI_MSM_PANEL
+	bool "Enable CEC"
+	default n
+	---help---
+	  Support for HDMI CEC Feature
+	  Choose to enable CEC
+
+config FB_MSM_HDMI_MHL_9244
+	depends on FB_MSM_HDMI_MSM_PANEL
+	bool 'SI_MHL 9244 support'
+	default n
+	---help---
+	  Support the HDMI to MHL conversion.
+	  MHL (Mobile High-Definition Link) technology
+	  uses USB connector to output HDMI content
+
+config FB_MSM_HDMI_MHL_8334
+	depends on FB_MSM_HDMI_MSM_PANEL
+	bool 'SI_MHL 8334 support '
+	default n
+	---help---
+	  Support the HDMI to MHL conversion.
+	  MHL (Mobile High-Definition Link) technology
+	  uses USB connector to output HDMI content
+
+choice
+	depends on  (FB_MSM_MDP22 || FB_MSM_MDP31 || FB_MSM_MDP40)
+	prompt "TVOut Region"
+	default FB_MSM_TVOUT_NONE
+
+config FB_MSM_TVOUT_NTSC_M
+	bool "NTSC M"
+	select FB_MSM_TVOUT
+        select FB_MSM_EXT_INTERFACE_COMMON
+	---help---
+	  Support for NTSC M region (North American and Korea)
+
+config FB_MSM_TVOUT_NTSC_J
+	bool "NTSC J"
+	select FB_MSM_TVOUT
+        select FB_MSM_EXT_INTERFACE_COMMON
+	---help---
+	  Support for NTSC J region (Japan)
+
+config FB_MSM_TVOUT_PAL_BDGHIN
+	bool "PAL BDGHIN"
+	select FB_MSM_TVOUT
+        select FB_MSM_EXT_INTERFACE_COMMON
+	---help---
+	  Support for PAL BDGHIN region (Non-argentina PAL-N)
+
+config FB_MSM_TVOUT_PAL_M
+	bool "PAL M"
+	select FB_MSM_TVOUT
+        select FB_MSM_EXT_INTERFACE_COMMON
+	---help---
+	  Support for PAL M region
+
+config FB_MSM_TVOUT_PAL_N
+	bool "PAL N"
+	select FB_MSM_TVOUT
+        select FB_MSM_EXT_INTERFACE_COMMON
+	---help---
+	  Support for PAL N region (Argentina PAL-N)
+
+config FB_MSM_TVOUT_NONE
+	bool "NONE"
+	---help---
+	  This will disable TV Out functionality.
+endchoice
+
+config FB_MSM_TVOUT_SVIDEO
+	bool "TVOut on S-video"
+	depends on FB_MSM_TVOUT
+	default n
+	---help---
+	  Selects whether the TVOut signal uses S-video.
+	  Choose n for composite output.
+
+choice
+	depends on  FB_MSM_MDP22
+	prompt "External MDDI"
+	default FB_MSM_EXTMDDI_SVGA
+
+config FB_MSM_EXTMDDI_SVGA
+	bool "External MDDI SVGA"
+	select FB_MSM_MDDI
+	select FB_MSM_EXTMDDI
+	---help---
+	  Support for MSM SVGA (800x600) external MDDI panel
+
+config FB_MSM_EXTMDDI_NONE
+	bool "NONE"
+	---help---
+	  This will disable External MDDI functionality.
+endchoice
+
+choice
+	prompt "Default framebuffer color depth"
+	depends on FB_MSM_MDP40 || FB_MSM_MDP31 || FB_MSM_MDP303
+	default FB_MSM_DEFAULT_DEPTH_RGBA8888
+
+config FB_MSM_DEFAULT_DEPTH_RGB565
+	bool "16 bits per pixel (RGB565)"
+
+config FB_MSM_DEFAULT_DEPTH_ARGB8888
+	bool "32 bits per pixel (ARGB8888)"
+
+config FB_MSM_DEFAULT_DEPTH_RGBA8888
+	bool "32 bits per pixel (RGBA8888)"
+
+endchoice
+
+config FB_MSM_EBI2_EPSON_S1D_QVGA_PANEL
+	bool "EBI2 Epson QVGA Panel"
+	select FB_MSM_EBI2
+	default n
+	---help---
+	  Support for EBI2 Epson QVGA (240x320) panel
+
+config FB_MSM_EBI2_PANEL_DETECT
+	bool "EBI2 Panel Detect"
+	select FB_MSM_EBI2_EPSON_S1D_QVGA_PANEL
+	default n
+	---help---
+	  Support for EBI2 panel auto detect
+endif
diff --git a/drivers/video/msm/Makefile b/drivers/video/msm/Makefile
index 802d6ae..b2ecb08 100644
--- a/drivers/video/msm/Makefile
+++ b/drivers/video/msm/Makefile
@@ -1,19 +1,195 @@
-
-# core framebuffer
-#
 obj-y := msm_fb.o
 
-# MDP DMA/PPP engine
-#
-obj-y += mdp.o mdp_scale_tables.o mdp_ppp.o
+obj-$(CONFIG_FB_MSM_LOGO) += logo.o
+obj-$(CONFIG_FB_BACKLIGHT) += msm_fb_bl.o
 
-# MDDI interface
-#
-obj-y += mddi.o
+ifeq ($(CONFIG_FB_MSM_MDP_HW),y)
 
-# MDDI client/panel drivers
-#
-obj-y += mddi_client_dummy.o
-obj-y += mddi_client_toshiba.o
-obj-y += mddi_client_nt35399.o
+# MDP
+obj-y += mdp.o
 
+obj-$(CONFIG_DEBUG_FS) += mdp_debugfs.o
+
+ifeq ($(CONFIG_FB_MSM_MDP40),y)
+obj-y += mdp4_util.o
+obj-y += mdp4_hsic.o
+else
+obj-y += mdp_hw_init.o
+obj-y += mdp_ppp.o
+ifeq ($(CONFIG_FB_MSM_MDP31),y)
+obj-y += mdp_ppp_v31.o
+else
+obj-y += mdp_ppp_v20.o
+endif
+endif
+
+ifeq ($(CONFIG_FB_MSM_OVERLAY),y)
+obj-y += mdp4_overlay.o
+obj-y += mdp4_overlay_lcdc.o
+ifeq ($(CONFIG_FB_MSM_MIPI_DSI),y)
+obj-y += mdp4_overlay_dsi_video.o
+obj-y += mdp4_overlay_dsi_cmd.o
+else
+obj-y += mdp4_overlay_mddi.o
+endif
+else
+obj-y += mdp_dma_lcdc.o
+endif
+
+obj-$(CONFIG_FB_MSM_MDP303) += mdp_dma_dsi_video.o
+
+ifeq ($(CONFIG_FB_MSM_DTV),y)
+obj-y += mdp4_dtv.o
+obj-y += mdp4_overlay_dtv.o
+endif
+
+obj-y += mdp_dma.o
+obj-y += mdp_dma_s.o
+obj-y += mdp_vsync.o
+obj-y += mdp_cursor.o
+obj-y += mdp_dma_tv.o
+obj-$(CONFIG_ARCH_MSM7X27A) += msm_dss_io_7x27a.o
+obj-$(CONFIG_ARCH_MSM8X60) += msm_dss_io_8x60.o
+obj-$(CONFIG_ARCH_MSM8960) += msm_dss_io_8960.o
+
+# EBI2
+obj-$(CONFIG_FB_MSM_EBI2) += ebi2_lcd.o
+
+# LCDC
+obj-$(CONFIG_FB_MSM_LCDC) += lcdc.o
+
+# LVDS
+obj-$(CONFIG_FB_MSM_LVDS) += lvds.o
+
+# MDDI
+msm_mddi-objs := mddi.o mddihost.o mddihosti.o
+obj-$(CONFIG_FB_MSM_MDDI) += msm_mddi.o
+
+# External MDDI
+msm_mddi_ext-objs := mddihost_e.o mddi_ext.o
+obj-$(CONFIG_FB_MSM_EXTMDDI) += msm_mddi_ext.o
+
+# MIPI gereric
+msm_mipi-objs := mipi_dsi.o mipi_dsi_host.o
+obj-$(CONFIG_FB_MSM_MIPI_DSI) += msm_mipi.o
+
+# MIPI manufacture
+obj-$(CONFIG_FB_MSM_MIPI_DSI_TOSHIBA) += mipi_toshiba.o
+obj-$(CONFIG_FB_MSM_MIPI_DSI_NOVATEK) += mipi_novatek.o
+obj-$(CONFIG_FB_MSM_MIPI_DSI_ORISE) += mipi_orise.o
+obj-$(CONFIG_FB_MSM_MIPI_DSI_RENESAS) += mipi_renesas.o
+obj-$(CONFIG_FB_MSM_MIPI_DSI_TRULY) += mipi_truly.o
+obj-$(CONFIG_FB_MSM_MIPI_DSI_NT35510) += mipi_NT35510.o
+obj-$(CONFIG_FB_MSM_MIPI_DSI_NT35516) += mipi_truly_tft540960_1_e.o
+obj-$(CONFIG_FB_MSM_MIPI_DSI_SIMULATOR) += mipi_simulator.o
+
+# MIPI Bridge
+obj-$(CONFIG_FB_MSM_MIPI_DSI_TC358764_DSI2LVDS) += mipi_tc358764_dsi2lvds.o
+
+# TVEnc
+obj-$(CONFIG_FB_MSM_TVOUT) += tvenc.o
+ifeq ($(CONFIG_FB_MSM_OVERLAY),y)
+obj-$(CONFIG_FB_MSM_TVOUT) += mdp4_overlay_atv.o
+endif
+
+# MSM FB Panel
+obj-y += msm_fb_panel.o
+obj-$(CONFIG_FB_MSM_EBI2_TMD_QVGA_EPSON_QCIF) += ebi2_tmd20.o
+obj-$(CONFIG_FB_MSM_EBI2_TMD_QVGA_EPSON_QCIF) += ebi2_l2f.o
+
+ifeq ($(CONFIG_FB_MSM_MDDI_AUTO_DETECT),y)
+obj-y += mddi_prism.o
+obj-y += mddi_toshiba.o
+obj-y += mddi_toshiba_vga.o
+obj-y += mddi_toshiba_wvga_pt.o
+obj-y += mddi_toshiba_wvga.o
+obj-y += mddi_sharp.o
+obj-y += mddi_orise.o
+obj-y += mddi_quickvx.o
+else
+obj-$(CONFIG_FB_MSM_MDDI_PRISM_WVGA) += mddi_prism.o
+obj-$(CONFIG_FB_MSM_MDDI_TOSHIBA_COMMON) += mddi_toshiba.o
+obj-$(CONFIG_FB_MSM_MDDI_TOSHIBA_COMMON_VGA) += mddi_toshiba_vga.o
+obj-$(CONFIG_FB_MSM_MDDI_TOSHIBA_WVGA_PORTRAIT) += mddi_toshiba_wvga_pt.o
+obj-$(CONFIG_FB_MSM_MDDI_TOSHIBA_WVGA) += mddi_toshiba_wvga.o
+obj-$(CONFIG_FB_MSM_MDDI_SHARP_QVGA_128x128) += mddi_sharp.o
+obj-$(CONFIG_FB_MSM_MDDI_ORISE) += mddi_orise.o
+obj-$(CONFIG_FB_MSM_MDDI_QUICKVX) += mddi_quickvx.o
+endif
+
+ifeq ($(CONFIG_FB_MSM_MIPI_PANEL_DETECT),y)
+obj-y += mipi_toshiba_video_wvga_pt.o mipi_toshiba_video_wsvga_pt.o mipi_toshiba_video_wuxga.o
+obj-y += mipi_novatek_video_qhd_pt.o mipi_novatek_cmd_qhd_pt.o
+obj-y += mipi_orise_video_720p_pt.o mipi_orise_cmd_720p_pt.o
+obj-y += mipi_renesas_video_fwvga_pt.o mipi_renesas_cmd_fwvga_pt.o
+obj-y += mipi_NT35510_video_wvga_pt.o mipi_NT35510_cmd_wvga_pt.o
+obj-y += mipi_truly_tft540960_1_e_video_qhd_pt.o mipi_truly_tft540960_1_e_cmd_qhd_pt.o
+obj-y += mipi_chimei_wxga_pt.o
+obj-y += mipi_chimei_wuxga.o
+obj-y += mipi_truly_video_wvga_pt.o
+else
+obj-$(CONFIG_FB_MSM_MIPI_TOSHIBA_VIDEO_WVGA_PT) += mipi_toshiba_video_wvga_pt.o
+obj-$(CONFIG_FB_MSM_MIPI_TOSHIBA_VIDEO_WSVGA_PT) += mipi_toshiba_video_wsvga_pt.o
+obj-$(CONFIG_FB_MSM_MIPI_TOSHIBA_VIDEO_WUXGA) += mipi_toshiba_video_wuxga.o
+obj-$(CONFIG_FB_MSM_MIPI_NOVATEK_VIDEO_QHD_PT) += mipi_novatek_video_qhd_pt.o
+obj-$(CONFIG_FB_MSM_MIPI_ORISE_VIDEO_720P_PT) += mipi_orise_video_720p_pt.o
+obj-$(CONFIG_FB_MSM_MIPI_ORISE_CMD_720P_PT) += mipi_orise_cmd_720p_pt.o
+obj-$(CONFIG_FB_MSM_MIPI_NOVATEK_CMD_QHD_PT) += mipi_novatek_cmd_qhd_pt.o
+obj-$(CONFIG_FB_MSM_MIPI_RENESAS_VIDEO_FWVGA_PT) += mipi_renesas_video_fwvga_pt.o
+obj-$(CONFIG_FB_MSM_MIPI_RENESAS_CMD_FWVGA_PT) += mipi_renesas_cmd_fwvga_pt.o
+obj-$(CONFIG_FB_MSM_MIPI_RENESAS_VIDEO_FWVGA_PT) += mipi_renesas_video_fwvga_pt.o
+obj-$(CONFIG_FB_MSM_MIPI_TRULY_VIDEO_WVGA_PT) += mipi_truly_video_wvga_pt.o
+obj-$(CONFIG_FB_MSM_MIPI_NT35510_CMD_WVGA_PT) += mipi_NT35510_cmd_wvga_pt.o
+obj-$(CONFIG_FB_MSM_MIPI_NT35510_VIDEO_WVGA_PT) += mipi_NT35510_video_wvga_pt.o
+obj-$(CONFIG_FB_MSM_MIPI_NT35516_CMD_QHD_PT) += mipi_truly_tft540960_1_e_cmd_qhd_pt.o
+obj-$(CONFIG_FB_MSM_MIPI_NT35516_VIDEO_QHD_PT) += mipi_truly_tft540960_1_e_video_qhd_pt.o
+obj-$(CONFIG_FB_MSM_MIPI_SIMULATOR_VIDEO) += mipi_simulator_video.o
+obj-$(CONFIG_FB_MSM_MIPI_CHIMEI_WXGA) += mipi_chimei_wxga_pt.o
+obj-$(CONFIG_FB_MSM_MIPI_CHIMEI_WUXGA) += mipi_chimei_wuxga.o
+endif
+
+obj-$(CONFIG_FB_MSM_LCDC_PANEL) += lcdc_panel.o
+obj-$(CONFIG_FB_MSM_LCDC_PRISM_WVGA) += lcdc_prism.o
+obj-$(CONFIG_FB_MSM_LCDC_SAMSUNG_WSVGA) += lcdc_samsung_wsvga.o
+obj-$(CONFIG_FB_MSM_LCDC_CHIMEI_WXGA) += lcdc_chimei_wxga.o
+obj-$(CONFIG_FB_MSM_LCDC_NT35582_WVGA) += lcdc_nt35582_wvga.o
+obj-$(CONFIG_FB_MSM_LCDC_EXTERNAL_WXGA) += lcdc_external.o
+obj-$(CONFIG_FB_MSM_HDMI_SII_EXTERNAL_720P) += hdmi_sii9022.o
+obj-$(CONFIG_FB_MSM_LCDC_GORDON_VGA) += lcdc_gordon.o
+obj-$(CONFIG_FB_MSM_LCDC_WXGA) += lcdc_wxga.o
+obj-$(CONFIG_FB_MSM_LCDC_TOSHIBA_WVGA_PT) += lcdc_toshiba_wvga_pt.o
+obj-$(CONFIG_FB_MSM_LCDC_TOSHIBA_FWVGA_PT) += lcdc_toshiba_fwvga_pt.o
+obj-$(CONFIG_FB_MSM_LCDC_SHARP_WVGA_PT) += lcdc_sharp_wvga_pt.o
+obj-$(CONFIG_FB_MSM_LCDC_AUO_WVGA) += lcdc_auo_wvga.o
+obj-$(CONFIG_FB_MSM_LCDC_SAMSUNG_OLED_PT) += lcdc_samsung_oled_pt.o
+obj-$(CONFIG_FB_MSM_HDMI_ADV7520_PANEL) += adv7520.o
+obj-$(CONFIG_FB_MSM_LCDC_ST15_WXGA) += lcdc_st15.o
+obj-$(CONFIG_FB_MSM_LVDS_CHIMEI_WXGA) += lvds_chimei_wxga.o
+obj-$(CONFIG_FB_MSM_HDMI_MSM_PANEL) += hdmi_msm.o
+obj-$(CONFIG_FB_MSM_EXT_INTERFACE_COMMON) += external_common.o
+obj-$(CONFIG_FB_MSM_LCDC_TRULY_HVGA_IPS3P2335) += lcdc_truly_ips3p2335.o
+
+obj-$(CONFIG_FB_MSM_TVOUT) += tvout_msm.o
+
+ccflags-y := -I$(src)/mhl
+obj-$(CONFIG_FB_MSM_HDMI_MHL_8334) += mhl-8334.o
+mhl-8334-objs  += mhl/mhl_8334.o
+mhl-8334-objs  += mhl/mhl_i2c_utils.o
+
+obj-$(CONFIG_FB_MSM_EXTMDDI_SVGA) += mddi_ext_lcd.o
+
+obj-$(CONFIG_FB_MSM_WRITEBACK_MSM_PANEL) += mdp4_wfd_writeback_panel.o
+obj-$(CONFIG_FB_MSM_WRITEBACK_MSM_PANEL) += mdp4_wfd_writeback.o
+obj-$(CONFIG_FB_MSM_WRITEBACK_MSM_PANEL) += mdp4_overlay_writeback.o
+
+obj-$(CONFIG_MSM_VIDC_1080P) += vidc/
+obj-$(CONFIG_MSM_VIDC_720P) += vidc/
+else
+obj-$(CONFIG_FB_MSM_EBI2) += ebi2_host.o
+obj-$(CONFIG_FB_MSM_EBI2) += ebi2_lcd.o
+obj-y += msm_fb_panel.o
+obj-$(CONFIG_FB_MSM_EBI2_EPSON_S1D_QVGA_PANEL) += ebi2_epson_s1d_qvga.o
+endif
+
+clean:
+	rm *.o .*cmd
diff --git a/drivers/video/msm/adv7520.c b/drivers/video/msm/adv7520.c
new file mode 100644
index 0000000..c0fb370
--- /dev/null
+++ b/drivers/video/msm/adv7520.c
@@ -0,0 +1,1029 @@
+/* Copyright (c) 2010,2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/i2c.h>
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/adv7520.h>
+#include <linux/time.h>
+#include <linux/completion.h>
+#include <linux/wakelock.h>
+#include <linux/clk.h>
+#include <asm/atomic.h>
+#include "msm_fb.h"
+
+#define DEBUG
+#define DEV_DBG_PREFIX "HDMI: "
+
+#include "external_common.h"
+
+/* #define PORT_DEBUG */
+/* #define TESTING_FORCE_480p */
+
+#define HPD_DUTY_CYCLE	4 /*secs*/
+
+static struct external_common_state_type hdmi_common;
+
+static struct i2c_client *hclient;
+static struct clk *tv_enc_clk;
+
+static bool chip_power_on = FALSE;	/* For chip power on/off */
+static bool enable_5v_on = FALSE;
+static bool hpd_power_on = FALSE;
+static atomic_t comm_power_on;	/* For dtv power on/off (I2C) */
+static int suspend_count;
+
+static u8 reg[256];	/* HDMI panel registers */
+
+struct hdmi_data {
+	struct msm_hdmi_platform_data *pd;
+	struct work_struct isr_work;
+};
+static struct hdmi_data *dd;
+static struct work_struct hpd_timer_work;
+
+#ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT
+static struct work_struct hdcp_handle_work;
+static int hdcp_activating;
+static DEFINE_MUTEX(hdcp_state_mutex);
+static int has_hdcp_hw_support = true;
+#endif
+
+static struct timer_list hpd_timer;
+static struct timer_list hpd_duty_timer;
+static struct work_struct hpd_duty_work;
+static unsigned int monitor_sense;
+static boolean hpd_cable_chg_detected;
+
+struct wake_lock wlock;
+
+/* Change HDMI state */
+static void change_hdmi_state(int online)
+{
+	if (!external_common_state)
+		return;
+
+	mutex_lock(&external_common_state_hpd_mutex);
+	external_common_state->hpd_state = online;
+	mutex_unlock(&external_common_state_hpd_mutex);
+
+	if (!external_common_state->uevent_kobj)
+		return;
+
+	if (online) {
+		kobject_uevent(external_common_state->uevent_kobj,
+			KOBJ_ONLINE);
+		switch_set_state(&external_common_state->sdev, 1);
+	} else {
+		kobject_uevent(external_common_state->uevent_kobj,
+			KOBJ_OFFLINE);
+		switch_set_state(&external_common_state->sdev, 0);
+	}
+	DEV_INFO("adv7520_uevent: %d [suspend# %d]\n", online, suspend_count);
+}
+
+
+/*
+ * Read a value from a register on ADV7520 device
+ * If sucessfull returns value read , otherwise error.
+ */
+static u8 adv7520_read_reg(struct i2c_client *client, u8 reg)
+{
+	int err;
+	struct i2c_msg msg[2];
+	u8 reg_buf[] = { reg };
+	u8 data_buf[] = { 0 };
+
+	if (!client->adapter)
+		return -ENODEV;
+	if (!atomic_read(&comm_power_on)) {
+		DEV_WARN("%s: WARN: missing GPIO power\n", __func__);
+		return -ENODEV;
+	}
+
+	msg[0].addr = client->addr;
+	msg[0].flags = 0;
+	msg[0].len = 1;
+	msg[0].buf = reg_buf;
+
+	msg[1].addr = client->addr;
+	msg[1].flags = I2C_M_RD;
+	msg[1].len = 1;
+	msg[1].buf = data_buf;
+
+	err = i2c_transfer(client->adapter, msg, 2);
+
+	if (err < 0) {
+		DEV_INFO("%s: I2C err: %d\n", __func__, err);
+		return err;
+	}
+
+#ifdef PORT_DEBUG
+	DEV_INFO("HDMI[%02x] [R] %02x\n", reg, data);
+#endif
+	return *data_buf;
+}
+
+/*
+ * Write a value to a register on adv7520 device.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int adv7520_write_reg(struct i2c_client *client, u8 reg, u8 val)
+{
+	int err;
+	struct i2c_msg msg[1];
+	unsigned char data[2];
+
+	if (!client->adapter)
+		return -ENODEV;
+	if (!atomic_read(&comm_power_on)) {
+		DEV_WARN("%s: WARN: missing GPIO power\n", __func__);
+		return -ENODEV;
+	}
+
+	msg->addr = client->addr;
+	msg->flags = 0;
+	msg->len = 2;
+	msg->buf = data;
+	data[0] = reg;
+	data[1] = val;
+
+	err = i2c_transfer(client->adapter, msg, 1);
+	if (err >= 0)
+		return 0;
+#ifdef PORT_DEBUG
+	DEV_INFO("HDMI[%02x] [W] %02x [%d]\n", reg, val, err);
+#endif
+	return err;
+}
+
+#ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT
+static void adv7520_close_hdcp_link(void)
+{
+	if (!external_common_state->hdcp_active && !hdcp_activating)
+		return;
+
+	DEV_INFO("HDCP: Close link\n");
+
+	reg[0xD5] = adv7520_read_reg(hclient, 0xD5);
+	reg[0xD5] &= 0xFE;
+	adv7520_write_reg(hclient, 0xD5, (u8)reg[0xD5]);
+
+	reg[0x16] = adv7520_read_reg(hclient, 0x16);
+	reg[0x16] &= 0xFE;
+	adv7520_write_reg(hclient, 0x16, (u8)reg[0x16]);
+
+	/* UnMute Audio */
+	adv7520_write_reg(hclient, 0x0C, (u8)0x84);
+
+	external_common_state->hdcp_active = FALSE;
+	mutex_lock(&hdcp_state_mutex);
+	hdcp_activating = FALSE;
+	mutex_unlock(&hdcp_state_mutex);
+}
+
+static void adv7520_comm_power(int on, int show);
+static void adv7520_hdcp_enable(struct work_struct *work)
+{
+	DEV_INFO("HDCP: Start reg[0xaf]=%02x (mute audio)\n", reg[0xaf]);
+
+	adv7520_comm_power(1, 1);
+
+	/* Mute Audio */
+	adv7520_write_reg(hclient, 0x0C, (u8)0xC3);
+
+	msleep(200);
+	/* Wait for BKSV ready interrupt */
+	/* Read BKSV's keys from HDTV */
+	reg[0xBF] = adv7520_read_reg(hclient, 0xBF);
+	reg[0xC0] = adv7520_read_reg(hclient, 0xC0);
+	reg[0xC1] = adv7520_read_reg(hclient, 0xC1);
+	reg[0xC2] = adv7520_read_reg(hclient, 0xC2);
+	reg[0xc3] = adv7520_read_reg(hclient, 0xC3);
+
+	DEV_DBG("HDCP: BKSV={%02x,%02x,%02x,%02x,%02x}\n", reg[0xbf], reg[0xc0],
+		reg[0xc1], reg[0xc2], reg[0xc3]);
+
+	/* Is SINK repeater */
+	reg[0xBE] = adv7520_read_reg(hclient, 0xBE);
+	if (~(reg[0xBE] & 0x40)) {
+		; /* compare with revocation list */
+		/* Check 20 1's and 20 zero's */
+	} else {
+		/* Don't implement HDCP if sink as a repeater */
+		adv7520_write_reg(hclient, 0x0C, (u8)0x84);
+		mutex_lock(&hdcp_state_mutex);
+		hdcp_activating = FALSE;
+		mutex_unlock(&hdcp_state_mutex);
+		DEV_WARN("HDCP: Sink Repeater (%02x), (unmute audio)\n",
+			reg[0xbe]);
+
+		adv7520_comm_power(0, 1);
+		return;
+	}
+
+	msleep(200);
+	reg[0xB8] = adv7520_read_reg(hclient, 0xB8);
+	DEV_INFO("HDCP: Status reg[0xB8] is %02x\n", reg[0xb8]);
+	if (reg[0xb8] & 0x40) {
+		/* UnMute Audio */
+		adv7520_write_reg(hclient, 0x0C, (u8)0x84);
+		DEV_INFO("HDCP: A/V content Encrypted (unmute audio)\n");
+		external_common_state->hdcp_active = TRUE;
+	}
+	adv7520_comm_power(0, 1);
+
+	mutex_lock(&hdcp_state_mutex);
+	hdcp_activating = FALSE;
+	mutex_unlock(&hdcp_state_mutex);
+}
+#endif
+
+static int adv7520_read_edid_block(int block, uint8 *edid_buf)
+{
+	u8 r = 0;
+	int ret;
+	struct i2c_msg msg[] = {
+		{ .addr = reg[0x43] >> 1,
+		  .flags = 0,
+		  .len = 1,
+		  .buf = &r },
+		{ .addr = reg[0x43] >> 1,
+		  .flags = I2C_M_RD,
+		  .len = 0x100,
+		  .buf = edid_buf } };
+
+	if (block > 0)
+		return 0;
+	ret = i2c_transfer(hclient->adapter, msg, 2);
+	DEV_DBG("EDID block: addr=%02x, ret=%d\n", reg[0x43] >> 1, ret);
+	return (ret < 2) ? -ENODEV : 0;
+}
+
+static void adv7520_read_edid(void)
+{
+	external_common_state->read_edid_block = adv7520_read_edid_block;
+	if (hdmi_common_read_edid()) {
+		u8 timeout;
+		DEV_INFO("%s: retry\n", __func__);
+		adv7520_write_reg(hclient, 0xc9, 0x13);
+		msleep(500);
+		timeout = (adv7520_read_reg(hclient, 0x96) & (1 << 2));
+		if (timeout) {
+			hdmi_common_read_edid();
+		}
+	}
+}
+
+static void adv7520_chip_on(void)
+{
+	if (!chip_power_on) {
+		/* Get the current register holding the power bit. */
+		unsigned long reg0xaf = adv7520_read_reg(hclient, 0xaf);
+
+		dd->pd->core_power(1, 1);
+
+		/* Set the HDMI select bit. */
+		set_bit(1, &reg0xaf);
+		DEV_INFO("%s: turn on chip power\n", __func__);
+		adv7520_write_reg(hclient, 0x41, 0x10);
+		adv7520_write_reg(hclient, 0xaf, (u8)reg0xaf);
+		chip_power_on = TRUE;
+	} else
+		DEV_INFO("%s: chip already has power\n", __func__);
+}
+
+static void adv7520_chip_off(void)
+{
+	if (chip_power_on) {
+#ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT
+		if (has_hdcp_hw_support)
+			adv7520_close_hdcp_link();
+#endif
+
+		DEV_INFO("%s: turn off chip power\n", __func__);
+		adv7520_write_reg(hclient, 0x41, 0x50);
+		dd->pd->core_power(0, 1);
+		chip_power_on = FALSE;
+	} else
+		DEV_INFO("%s: chip is already off\n", __func__);
+
+	monitor_sense = 0;
+	hpd_cable_chg_detected = FALSE;
+
+	if (enable_5v_on) {
+		dd->pd->enable_5v(0);
+		enable_5v_on = FALSE;
+	}
+}
+
+/*  Power ON/OFF  ADV7520 chip */
+static void adv7520_isr_w(struct work_struct *work);
+static void adv7520_comm_power(int on, int show)
+{
+	if (!on)
+		atomic_dec(&comm_power_on);
+	dd->pd->comm_power(on, 0/*show*/);
+	if (on)
+		atomic_inc(&comm_power_on);
+}
+
+#ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT
+static void adv7520_start_hdcp(void);
+#endif
+static int adv7520_power_on(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd = platform_get_drvdata(pdev);
+
+	clk_prepare_enable(tv_enc_clk);
+	external_common_state->dev = &pdev->dev;
+	if (mfd != NULL) {
+		DEV_INFO("adv7520_power: ON (%dx%d %d)\n",
+			mfd->var_xres, mfd->var_yres, mfd->var_pixclock);
+		hdmi_common_get_video_format_from_drv_data(mfd);
+	}
+
+	adv7520_comm_power(1, 1);
+	/* Check if HPD is signaled */
+	if (adv7520_read_reg(hclient, 0x42) & (1 << 6)) {
+		DEV_INFO("power_on: cable detected\n");
+		monitor_sense = adv7520_read_reg(hclient, 0xC6);
+#ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT
+		if (has_hdcp_hw_support) {
+			if (!hdcp_activating)
+				adv7520_start_hdcp();
+		}
+#endif
+	} else
+		DEV_INFO("power_on: cable NOT detected\n");
+	adv7520_comm_power(0, 1);
+	wake_lock(&wlock);
+
+	return 0;
+}
+
+static int adv7520_power_off(struct platform_device *pdev)
+{
+	DEV_INFO("power_off\n");
+	adv7520_comm_power(1, 1);
+	adv7520_chip_off();
+	wake_unlock(&wlock);
+	adv7520_comm_power(0, 1);
+	clk_disable_unprepare(tv_enc_clk);
+	return 0;
+}
+
+
+/* AV7520 chip specific initialization */
+static void adv7520_chip_init(void)
+{
+	/* Initialize the variables used to read/write the ADV7520 chip. */
+	memset(&reg, 0xff, sizeof(reg));
+
+	/* Get the values from the "Fixed Registers That Must Be Set". */
+	reg[0x98] = adv7520_read_reg(hclient, 0x98);
+	reg[0x9c] = adv7520_read_reg(hclient, 0x9c);
+	reg[0x9d] = adv7520_read_reg(hclient, 0x9d);
+	reg[0xa2] = adv7520_read_reg(hclient, 0xa2);
+	reg[0xa3] = adv7520_read_reg(hclient, 0xa3);
+	reg[0xde] = adv7520_read_reg(hclient, 0xde);
+
+	/* Get the "HDMI/DVI Selection" register. */
+	reg[0xaf] = adv7520_read_reg(hclient, 0xaf);
+
+	/* Read Packet Memory I2C Address */
+	reg[0x45] = adv7520_read_reg(hclient, 0x45);
+
+	/* Hard coded values provided by ADV7520 data sheet. */
+	reg[0x98] = 0x03;
+	reg[0x9c] = 0x38;
+	reg[0x9d] = 0x61;
+	reg[0xa2] = 0x94;
+	reg[0xa3] = 0x94;
+	reg[0xde] = 0x88;
+
+	/* Set the HDMI select bit. */
+	reg[0xaf] |= 0x16;
+
+	/* Set the audio related registers. */
+	reg[0x01] = 0x00;
+	reg[0x02] = 0x2d;
+	reg[0x03] = 0x80;
+	reg[0x0a] = 0x4d;
+	reg[0x0b] = 0x0e;
+	reg[0x0c] = 0x84;
+	reg[0x0d] = 0x10;
+	reg[0x12] = 0x00;
+	reg[0x14] = 0x00;
+	reg[0x15] = 0x20;
+	reg[0x44] = 0x79;
+	reg[0x73] = 0x01;
+	reg[0x76] = 0x00;
+
+	/* Set 720p display related registers */
+	reg[0x16] = 0x00;
+
+	reg[0x18] = 0x46;
+	reg[0x55] = 0x00;
+	reg[0x3c] = 0x04;
+
+	/* Set Interrupt Mask register for HPD/HDCP */
+	reg[0x94] = 0xC0;
+#ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT
+	if (has_hdcp_hw_support)
+		reg[0x95] = 0xC0;
+	else
+		reg[0x95] = 0x00;
+#else
+	reg[0x95] = 0x00;
+#endif
+	adv7520_write_reg(hclient, 0x94, reg[0x94]);
+	adv7520_write_reg(hclient, 0x95, reg[0x95]);
+
+	/* Set Packet Memory I2C Address */
+	reg[0x45] = 0x74;
+
+	/* Set the values from the "Fixed Registers That Must Be Set". */
+	adv7520_write_reg(hclient, 0x98, reg[0x98]);
+	adv7520_write_reg(hclient, 0x9c, reg[0x9c]);
+	adv7520_write_reg(hclient, 0x9d, reg[0x9d]);
+	adv7520_write_reg(hclient, 0xa2, reg[0xa2]);
+	adv7520_write_reg(hclient, 0xa3, reg[0xa3]);
+	adv7520_write_reg(hclient, 0xde, reg[0xde]);
+
+	/* Set the "HDMI/DVI Selection" register. */
+	adv7520_write_reg(hclient, 0xaf, reg[0xaf]);
+
+	/* Set EDID Monitor address */
+	reg[0x43] = 0x7E;
+	adv7520_write_reg(hclient, 0x43, reg[0x43]);
+
+	/* Enable the i2s audio input. */
+	adv7520_write_reg(hclient, 0x01, reg[0x01]);
+	adv7520_write_reg(hclient, 0x02, reg[0x02]);
+	adv7520_write_reg(hclient, 0x03, reg[0x03]);
+	adv7520_write_reg(hclient, 0x0a, reg[0x0a]);
+	adv7520_write_reg(hclient, 0x0b, reg[0x0b]);
+	adv7520_write_reg(hclient, 0x0c, reg[0x0c]);
+	adv7520_write_reg(hclient, 0x0d, reg[0x0d]);
+	adv7520_write_reg(hclient, 0x12, reg[0x12]);
+	adv7520_write_reg(hclient, 0x14, reg[0x14]);
+	adv7520_write_reg(hclient, 0x15, reg[0x15]);
+	adv7520_write_reg(hclient, 0x44, reg[0x44]);
+	adv7520_write_reg(hclient, 0x73, reg[0x73]);
+	adv7520_write_reg(hclient, 0x76, reg[0x76]);
+
+	/* Enable 720p display */
+	adv7520_write_reg(hclient, 0x16, reg[0x16]);
+	adv7520_write_reg(hclient, 0x18, reg[0x18]);
+	adv7520_write_reg(hclient, 0x55, reg[0x55]);
+	adv7520_write_reg(hclient, 0x3c, reg[0x3c]);
+
+	/* Set Packet Memory address to avoid conflict
+	with Bosch Accelerometer */
+	adv7520_write_reg(hclient, 0x45, reg[0x45]);
+
+	/* Ensure chip is in low-power state */
+	adv7520_write_reg(hclient, 0x41, 0x50);
+}
+
+#ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT
+static void adv7520_start_hdcp(void)
+{
+	mutex_lock(&hdcp_state_mutex);
+	if (hdcp_activating) {
+		DEV_WARN("adv7520_timer: HDCP already"
+			" activating, skipping\n");
+		mutex_unlock(&hdcp_state_mutex);
+		return;
+	}
+	hdcp_activating = TRUE;
+	mutex_unlock(&hdcp_state_mutex);
+
+	del_timer(&hpd_duty_timer);
+
+	adv7520_comm_power(1, 1);
+
+	if (!enable_5v_on) {
+		dd->pd->enable_5v(1);
+		enable_5v_on = TRUE;
+		adv7520_chip_on();
+	}
+
+	/* request for HDCP */
+	reg[0xaf] = adv7520_read_reg(hclient, 0xaf);
+	reg[0xaf] |= 0x90;
+	adv7520_write_reg(hclient, 0xaf, reg[0xaf]);
+	reg[0xaf] = adv7520_read_reg(hclient, 0xaf);
+
+	reg[0xba] = adv7520_read_reg(hclient, 0xba);
+	reg[0xba] |= 0x10;
+	adv7520_write_reg(hclient, 0xba, reg[0xba]);
+	reg[0xba] = adv7520_read_reg(hclient, 0xba);
+	adv7520_comm_power(0, 1);
+
+	DEV_INFO("HDCP: reg[0xaf]=0x%02x, reg[0xba]=0x%02x, waiting for BKSV\n",
+				reg[0xaf], reg[0xba]);
+
+	/* will check for HDCP Error or BKSV ready */
+	mod_timer(&hpd_duty_timer, jiffies + HZ/2);
+}
+#endif
+
+static void adv7520_hpd_timer_w(struct work_struct *work)
+{
+	if (!external_common_state->hpd_feature_on) {
+		DEV_INFO("adv7520_timer: skipping, feature off\n");
+		return;
+	}
+
+	if ((monitor_sense & 0x4) && !external_common_state->hpd_state) {
+		int timeout;
+		DEV_DBG("adv7520_timer: Cable Detected\n");
+		adv7520_comm_power(1, 1);
+		adv7520_chip_on();
+
+		if (hpd_cable_chg_detected) {
+			hpd_cable_chg_detected = FALSE;
+			/* Ensure 5V to read EDID */
+			if (!enable_5v_on) {
+				dd->pd->enable_5v(1);
+				enable_5v_on = TRUE;
+			}
+			msleep(500);
+			timeout = (adv7520_read_reg(hclient, 0x96) & (1 << 2));
+			if (timeout) {
+				DEV_DBG("adv7520_timer: EDID-Ready..\n");
+				adv7520_read_edid();
+			} else
+				DEV_DBG("adv7520_timer: EDID TIMEOUT (C9=%02x)"
+					"\n", adv7520_read_reg(hclient, 0xC9));
+		}
+#ifdef TESTING_FORCE_480p
+		external_common_state->disp_mode_list.num_of_elements = 1;
+		external_common_state->disp_mode_list.disp_mode_list[0] =
+			HDMI_VFRMT_720x480p60_16_9;
+#endif
+		adv7520_comm_power(0, 1);
+#ifndef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT
+		/* HDMI_5V_EN not needed anymore */
+		if (enable_5v_on) {
+			DEV_DBG("adv7520_timer: EDID done, no HDCP, 5V not "
+				"needed anymore\n");
+			dd->pd->enable_5v(0);
+			enable_5v_on = FALSE;
+		}
+#endif
+		change_hdmi_state(1);
+	} else if (external_common_state->hpd_state) {
+		adv7520_comm_power(1, 1);
+		adv7520_chip_off();
+		adv7520_comm_power(0, 1);
+		DEV_DBG("adv7520_timer: Cable Removed\n");
+		change_hdmi_state(0);
+	}
+}
+
+static void adv7520_hpd_timer_f(unsigned long data)
+{
+	schedule_work(&hpd_timer_work);
+}
+
+static void adv7520_isr_w(struct work_struct *work)
+{
+	static int state_count;
+	static u8 last_reg0x96;
+	u8 reg0xc8;
+	u8 reg0x96;
+#ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT
+	static u8 last_reg0x97;
+	u8 reg0x97 = 0;
+#endif
+	if (!external_common_state->hpd_feature_on) {
+		DEV_DBG("adv7520_irq: skipping, hpd off\n");
+		return;
+	}
+
+	adv7520_comm_power(1, 1);
+	reg0x96 = adv7520_read_reg(hclient, 0x96);
+#ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT
+	if (has_hdcp_hw_support) {
+		reg0x97 = adv7520_read_reg(hclient, 0x97);
+		/* Clearing the Interrupts */
+		adv7520_write_reg(hclient, 0x97, reg0x97);
+	}
+#endif
+	/* Clearing the Interrupts */
+	adv7520_write_reg(hclient, 0x96, reg0x96);
+
+	if ((reg0x96 == 0xC0) || (reg0x96 & 0x40)) {
+#ifdef DEBUG
+		unsigned int hpd_state = adv7520_read_reg(hclient, 0x42);
+#endif
+		monitor_sense = adv7520_read_reg(hclient, 0xC6);
+		DEV_DBG("adv7520_irq: reg[0x42]=%02x && reg[0xC6]=%02x\n",
+			hpd_state, monitor_sense);
+
+		if (!enable_5v_on) {
+			dd->pd->enable_5v(1);
+			enable_5v_on = TRUE;
+		}
+		if (!hpd_power_on) {
+			dd->pd->core_power(1, 1);
+			hpd_power_on = TRUE;
+		}
+
+		/* Timer for catching interrupt debouning */
+		DEV_DBG("adv7520_irq: Timer in .5sec\n");
+		hpd_cable_chg_detected = TRUE;
+		mod_timer(&hpd_timer, jiffies + HZ/2);
+	}
+#ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT
+	if (has_hdcp_hw_support) {
+		if (hdcp_activating) {
+			/* HDCP controller error Interrupt */
+			if (reg0x97 & 0x80) {
+				DEV_ERR("adv7520_irq: HDCP_ERROR\n");
+				state_count = 0;
+				adv7520_close_hdcp_link();
+			/* BKSV Ready interrupts */
+			} else if (reg0x97 & 0x40) {
+				DEV_INFO("adv7520_irq: BKSV keys ready, Begin"
+					" HDCP encryption\n");
+				state_count = 0;
+				schedule_work(&hdcp_handle_work);
+			} else if (++state_count > 2 && (monitor_sense & 0x4)) {
+				DEV_INFO("adv7520_irq: Still waiting for BKSV,"
+				"restart HDCP\n");
+				hdcp_activating = FALSE;
+				state_count = 0;
+				adv7520_chip_off();
+				adv7520_start_hdcp();
+			}
+			reg0xc8 = adv7520_read_reg(hclient, 0xc8);
+			DEV_INFO("adv7520_irq: DDC controller reg[0xC8]=0x%02x,"
+				"state_count=%d, monitor_sense=%x\n",
+				reg0xc8, state_count, monitor_sense);
+		} else if (!external_common_state->hdcp_active
+			&& (monitor_sense & 0x4)) {
+			DEV_INFO("adv7520_irq: start HDCP with"
+				" monitor sense\n");
+			state_count = 0;
+			adv7520_start_hdcp();
+		} else
+			state_count = 0;
+		if (last_reg0x97 != reg0x97 || last_reg0x96 != reg0x96)
+			DEV_DBG("adv7520_irq: reg[0x96]=%02x "
+				"reg[0x97]=%02x: HDCP: %d\n", reg0x96, reg0x97,
+				external_common_state->hdcp_active);
+		last_reg0x97 = reg0x97;
+	} else {
+		if (last_reg0x96 != reg0x96)
+			DEV_DBG("adv7520_irq: reg[0x96]=%02x\n", reg0x96);
+	}
+#else
+	if (last_reg0x96 != reg0x96)
+		DEV_DBG("adv7520_irq: reg[0x96]=%02x\n", reg0x96);
+#endif
+	last_reg0x96 = reg0x96;
+	adv7520_comm_power(0, 1);
+}
+
+static void adv7520_hpd_duty_work(struct work_struct *work)
+{
+	if (!external_common_state->hpd_feature_on) {
+		DEV_WARN("%s: hpd feature is off, skipping\n", __func__);
+		return;
+	}
+
+	dd->pd->core_power(1, 0);
+	msleep(10);
+	adv7520_isr_w(NULL);
+	dd->pd->core_power(0, 0);
+}
+
+static void adv7520_hpd_duty_timer_f(unsigned long data)
+{
+	if (!external_common_state->hpd_feature_on) {
+		DEV_WARN("%s: hpd feature is off, skipping\n", __func__);
+		return;
+	}
+
+	mod_timer(&hpd_duty_timer, jiffies + HPD_DUTY_CYCLE*HZ);
+	schedule_work(&hpd_duty_work);
+}
+
+static const struct i2c_device_id adv7520_id[] = {
+	{ ADV7520_DRV_NAME , 0},
+	{}
+};
+
+static struct msm_fb_panel_data hdmi_panel_data = {
+	.on  = adv7520_power_on,
+	.off = adv7520_power_off,
+};
+
+static struct platform_device hdmi_device = {
+	.name = ADV7520_DRV_NAME ,
+	.id   = 2,
+	.dev  = {
+		.platform_data = &hdmi_panel_data,
+		}
+};
+
+static void adv7520_ensure_init(void)
+{
+	static boolean init_done;
+	if (!init_done) {
+		int rc = dd->pd->init_irq();
+		if (rc) {
+			DEV_ERR("adv7520_init: init_irq: %d\n", rc);
+			return;
+		}
+
+		init_done = TRUE;
+	}
+	DEV_INFO("adv7520_init: chip init\n");
+	adv7520_comm_power(1, 1);
+	adv7520_chip_init();
+	adv7520_comm_power(0, 1);
+}
+
+static int adv7520_hpd_feature(int on)
+{
+	int rc = 0;
+
+	if (!on) {
+		if (enable_5v_on) {
+			dd->pd->enable_5v(0);
+			enable_5v_on = FALSE;
+		}
+		if (hpd_power_on) {
+			dd->pd->core_power(0, 1);
+			hpd_power_on = FALSE;
+		}
+
+		DEV_DBG("adv7520_hpd: %d: stop duty timer\n", on);
+		del_timer(&hpd_timer);
+		del_timer(&hpd_duty_timer);
+		external_common_state->hpd_state = 0;
+	}
+
+	if (on) {
+		dd->pd->core_power(1, 0);
+		adv7520_ensure_init();
+
+		adv7520_comm_power(1, 1);
+		monitor_sense = adv7520_read_reg(hclient, 0xC6);
+		DEV_DBG("adv7520_irq: reg[0xC6]=%02x\n", monitor_sense);
+		adv7520_comm_power(0, 1);
+		dd->pd->core_power(0, 0);
+
+		if (monitor_sense & 0x4) {
+			if (!enable_5v_on) {
+				dd->pd->enable_5v(1);
+				enable_5v_on = TRUE;
+			}
+			if (!hpd_power_on) {
+				dd->pd->core_power(1, 1);
+				hpd_power_on = TRUE;
+			}
+
+			hpd_cable_chg_detected = TRUE;
+			mod_timer(&hpd_timer, jiffies + HZ/2);
+		}
+
+		DEV_DBG("adv7520_hpd: %d start duty timer\n", on);
+		mod_timer(&hpd_duty_timer, jiffies + HZ/100);
+	}
+
+	DEV_INFO("adv7520_hpd: %d\n", on);
+	return rc;
+}
+
+static int __devinit
+	adv7520_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+	int rc;
+	struct platform_device *fb_dev;
+
+	dd = kzalloc(sizeof *dd, GFP_KERNEL);
+	if (!dd) {
+		rc = -ENOMEM;
+		goto probe_exit;
+	}
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+		return -ENODEV;
+
+	external_common_state->dev = &client->dev;
+
+	/* Init real i2c_client */
+	hclient = client;
+
+	i2c_set_clientdata(client, dd);
+	dd->pd = client->dev.platform_data;
+	if (!dd->pd) {
+		rc = -ENODEV;
+		goto probe_free;
+	}
+
+	INIT_WORK(&dd->isr_work, adv7520_isr_w);
+	INIT_WORK(&hpd_timer_work, adv7520_hpd_timer_w);
+#ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT
+	if (dd->pd->check_hdcp_hw_support)
+		has_hdcp_hw_support = dd->pd->check_hdcp_hw_support();
+
+	if (has_hdcp_hw_support)
+		INIT_WORK(&hdcp_handle_work, adv7520_hdcp_enable);
+	else
+		DEV_INFO("%s: no hdcp hw support.\n", __func__);
+#endif
+
+	init_timer(&hpd_timer);
+	hpd_timer.function = adv7520_hpd_timer_f;
+	hpd_timer.data = (unsigned long)NULL;
+	hpd_timer.expires = 0xffffffff;
+	add_timer(&hpd_timer);
+
+	external_common_state->hpd_feature = adv7520_hpd_feature;
+	DEV_INFO("adv7520_probe: HPD detection on request\n");
+	init_timer(&hpd_duty_timer);
+	hpd_duty_timer.function = adv7520_hpd_duty_timer_f;
+	hpd_duty_timer.data = (unsigned long)NULL;
+	hpd_duty_timer.expires = 0xffffffff;
+	add_timer(&hpd_duty_timer);
+	INIT_WORK(&hpd_duty_work, adv7520_hpd_duty_work);
+	DEV_INFO("adv7520_probe: HPD detection ON (duty)\n");
+
+	fb_dev = msm_fb_add_device(&hdmi_device);
+
+	if (fb_dev) {
+		rc = external_common_state_create(fb_dev);
+		if (rc)
+			goto probe_free;
+	} else
+		DEV_ERR("adv7520_probe: failed to add fb device\n");
+
+	if (hdmi_prim_display)
+		external_common_state->sdev.name = "hdmi_as_primary";
+	else
+		external_common_state->sdev.name = "hdmi";
+
+	if (switch_dev_register(&external_common_state->sdev) < 0)
+		DEV_ERR("Hdmi switch registration failed\n");
+
+	return 0;
+
+probe_free:
+	kfree(dd);
+	dd = NULL;
+probe_exit:
+	return rc;
+
+}
+
+static int __devexit adv7520_remove(struct i2c_client *client)
+{
+	if (!client->adapter) {
+		DEV_ERR("%s: No HDMI Device\n", __func__);
+		return -ENODEV;
+	}
+	switch_dev_unregister(&external_common_state->sdev);
+	wake_lock_destroy(&wlock);
+	kfree(dd);
+	dd = NULL;
+	return 0;
+}
+
+#ifdef CONFIG_SUSPEND
+static int adv7520_i2c_suspend(struct device *dev)
+{
+	DEV_INFO("%s\n", __func__);
+
+	++suspend_count;
+
+	if (external_common_state->hpd_feature_on) {
+		DEV_DBG("%s: stop duty timer\n", __func__);
+		del_timer(&hpd_duty_timer);
+		del_timer(&hpd_timer);
+	}
+
+	/* Turn off LDO8 and go into low-power state */
+	if (chip_power_on) {
+		DEV_DBG("%s: turn off power\n", __func__);
+		adv7520_comm_power(1, 1);
+		adv7520_write_reg(hclient, 0x41, 0x50);
+		adv7520_comm_power(0, 1);
+		dd->pd->core_power(0, 1);
+	}
+
+	return 0;
+}
+
+static int adv7520_i2c_resume(struct device *dev)
+{
+	DEV_INFO("%s\n", __func__);
+
+	/* Turn on LDO8 and go into normal-power state */
+	if (chip_power_on) {
+		DEV_DBG("%s: turn on power\n", __func__);
+		dd->pd->core_power(1, 1);
+		adv7520_comm_power(1, 1);
+		adv7520_write_reg(hclient, 0x41, 0x10);
+		adv7520_comm_power(0, 1);
+	}
+
+	if (external_common_state->hpd_feature_on) {
+		DEV_DBG("%s: start duty timer\n", __func__);
+		mod_timer(&hpd_duty_timer, jiffies + HPD_DUTY_CYCLE*HZ);
+	}
+
+	return 0;
+}
+#else
+#define adv7520_i2c_suspend	NULL
+#define adv7520_i2c_resume	NULL
+#endif
+
+static const struct dev_pm_ops adv7520_device_pm_ops = {
+	.suspend = adv7520_i2c_suspend,
+	.resume = adv7520_i2c_resume,
+};
+
+static struct i2c_driver hdmi_i2c_driver = {
+	.driver		= {
+		.name   = ADV7520_DRV_NAME,
+		.owner  = THIS_MODULE,
+		.pm     = &adv7520_device_pm_ops,
+	},
+	.probe		= adv7520_probe,
+	.id_table	= adv7520_id,
+	.remove		= __devexit_p(adv7520_remove),
+};
+
+static int __init adv7520_init(void)
+{
+	int rc;
+
+	pr_info("%s\n", __func__);
+	external_common_state = &hdmi_common;
+	external_common_state->video_resolution = HDMI_VFRMT_1280x720p60_16_9;
+
+	tv_enc_clk = clk_get(NULL, "tv_enc_clk");
+	if (IS_ERR(tv_enc_clk)) {
+		printk(KERN_ERR "error: can't get tv_enc_clk!\n");
+		return IS_ERR(tv_enc_clk);
+	}
+
+	HDMI_SETUP_LUT(640x480p60_4_3);		/* 25.20MHz */
+	HDMI_SETUP_LUT(720x480p60_16_9);	/* 27.03MHz */
+	HDMI_SETUP_LUT(1280x720p60_16_9);	/* 74.25MHz */
+
+	HDMI_SETUP_LUT(720x576p50_16_9);	/* 27.00MHz */
+	HDMI_SETUP_LUT(1280x720p50_16_9);	/* 74.25MHz */
+
+	hdmi_common_init_panel_info(&hdmi_panel_data.panel_info);
+
+	rc = i2c_add_driver(&hdmi_i2c_driver);
+	if (rc) {
+		pr_err("hdmi_init FAILED: i2c_add_driver rc=%d\n", rc);
+		goto init_exit;
+	}
+
+	if (machine_is_msm7x30_surf() || machine_is_msm8x55_surf()) {
+		short *hdtv_mux = (short *)ioremap(0x8e000170 , 0x100);
+		*hdtv_mux++ = 0x020b;
+		*hdtv_mux = 0x8000;
+		iounmap(hdtv_mux);
+	}
+	wake_lock_init(&wlock, WAKE_LOCK_IDLE, "hdmi_active");
+
+	return 0;
+
+init_exit:
+	if (tv_enc_clk)
+		clk_put(tv_enc_clk);
+	return rc;
+}
+
+static void __exit adv7520_exit(void)
+{
+	i2c_del_driver(&hdmi_i2c_driver);
+}
+
+module_init(adv7520_init);
+module_exit(adv7520_exit);
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.1");
+MODULE_AUTHOR("Qualcomm Innovation Center, Inc.");
+MODULE_DESCRIPTION("ADV7520 HDMI driver");
diff --git a/drivers/video/msm/ebi2_epson_s1d_qvga.c b/drivers/video/msm/ebi2_epson_s1d_qvga.c
new file mode 100644
index 0000000..8821eab
--- /dev/null
+++ b/drivers/video/msm/ebi2_epson_s1d_qvga.c
@@ -0,0 +1,374 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_fb.h"
+
+#include <linux/memory.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include "linux/proc_fs.h"
+
+#include <linux/delay.h>
+
+#include <mach/hardware.h>
+#include <linux/io.h>
+
+#include <asm/system.h>
+#include <asm/mach-types.h>
+
+#define CMD_NOP_C							0x00
+#define CMD_SOFT_RESET_C					0x99
+#define CMD_DISPLAY_ON_C					0xAF
+#define CMD_DISPLAY_OFF_C					0xAE
+#define CMD_SET_DISPLAY_C					0xCA
+#define CMD_SET_DISPLAY_TIMING_C			0xA1
+#define CMD_SET_DATA_C						0xBC
+#define CMD_SET_START_ADDRESS_C				0x15
+#define CMD_SET_END_ADDRESS_C				0x75
+#define CMD_RAM_WRITE_C						0x5C
+#define CMD_RAM_READ_C						0x5D
+#define CMD_SET_AREA_SCROLLING_C			0xAA
+#define CMD_SET_DISPLAY_START_LINE_C		0xAB
+#define CMD_PARTIAL_DISPLAY_IN_C			0xA8
+#define CMD_PARTIAL_DISPLAY_OUT_C			0xA9
+#define CMD_SET_DISPLAY_DATA_INTERFACE_C	0x31
+#define CMD_SET_DISPLAY_COLOR_MODE_C		0x8B
+#define CMD_SELECT_MTP_ROM_MODE_C			0x65
+#define CMD_MTP_ROM_MODE_IN_C				0x67
+#define CMD_MTP_ROM_MODE_OUT_C				0x68
+#define CMD_MTP_ROM_OPERATION_IN_C			0x69
+#define CMD_MTP_ROM_OPERATION_OUT_C			0x70
+#define CMD_GATE_LINE_SCAN_MODE_C			0x6F
+#define CMD_SET_AC_OPERATION_DRIVE_C		0x8C
+#define CMD_SET_ELECTRONIC_CONTROL_C		0x20
+#define CMD_SET_POSITIVE_CORRECTION_CHARS_C	0x22
+#define CMD_SET_NEGATIVE_CORRECTION_CHARS_C	0x25
+#define CMD_SET_POWER_CONTROL_C				0x21
+#define CMD_SET_PARTIAL_POWER_CONTROL_C		0x23
+#define CMD_SET_8_COLOR_CONTROL_C			0x24
+#define CMD_SLEEP_IN_C						0x95
+#define CMD_SLEEP_OUT_C						0x94
+#define CMD_VDD_OFF_C						0x97
+#define CMD_VDD_ON_C						0x96
+#define CMD_STOP_OSCILLATION_C				0x93
+#define CMD_START_OSCILLATION_C				0x92
+#define CMD_TEST_SOURCE_C					0xFD
+#define CMD_TEST_FUSE_C						0xFE
+#define CMD_TEST_C							0xFF
+#define CMD_STATUS_READ_C					0xE8
+#define CMD_REVISION_READ_C					0xE9
+
+#define PANEL_WIDTH			240
+#define PANEL_HEIGHT		320
+#define ACTIVE_WIN_WIDTH	PANEL_WIDTH
+#define ACTIVE_WIN_HEIGHT	PANEL_HEIGHT
+
+#define ACTIVE_WIN_H_START	0
+#define ACTIVE_WIN_V_START	0
+
+#define DISP_CMD_OUT(cmd) outpw(DISP_CMD_PORT, (cmd << 1));
+#define DISP_DATA_OUT(data) outpw(DISP_DATA_PORT, (data << 1));
+#define DISP_DATA_IN() inpw(DISP_DATA_PORT);
+
+static void *DISP_CMD_PORT;
+static void *DISP_DATA_PORT;
+static boolean disp_initialized;
+static boolean display_on;
+static struct msm_panel_common_pdata *ebi2_epson_pdata;
+
+static void epson_s1d_disp_init(struct platform_device *pdev);
+static int epson_s1d_disp_off(struct platform_device *pdev);
+static int epson_s1d_disp_on(struct platform_device *pdev);
+static void epson_s1d_disp_set_rect(int x, int y, int xres, int yres);
+
+static void epson_s1d_disp_set_rect(int x, int y, int xres, int yres)
+{
+	int right, bottom;
+
+	if (!disp_initialized)
+		return;
+
+	right = x + xres - 1;
+	bottom = y + yres - 1;
+
+	x += ACTIVE_WIN_H_START;
+	y += ACTIVE_WIN_V_START;
+	right += ACTIVE_WIN_H_START;
+	bottom += ACTIVE_WIN_V_START;
+
+	if ((PANEL_WIDTH  > x) &&
+		(PANEL_HEIGHT > y) &&
+		(PANEL_WIDTH > right) &&
+		(PANEL_HEIGHT > bottom)) {
+		DISP_CMD_OUT(CMD_SET_START_ADDRESS_C);
+		DISP_DATA_OUT((uint8)x);
+		DISP_DATA_OUT((uint8)(y>>8));
+		DISP_DATA_OUT((uint8)y);
+
+		DISP_CMD_OUT(CMD_SET_END_ADDRESS_C);
+		DISP_DATA_OUT((uint8)right);
+		DISP_DATA_OUT((uint8)(bottom>>8));
+		DISP_DATA_OUT((uint8)bottom);
+		DISP_CMD_OUT(CMD_RAM_WRITE_C);
+	}
+}
+
+static void epson_s1d_disp_init(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+
+	if (disp_initialized)
+		return;
+
+	mfd = platform_get_drvdata(pdev);
+
+	DISP_CMD_PORT = mfd->cmd_port;
+	DISP_DATA_PORT = mfd->data_port;
+
+	disp_initialized = TRUE;
+}
+
+static int epson_s1d_disp_off(struct platform_device *pdev)
+{
+	if (!disp_initialized)
+		epson_s1d_disp_init(pdev);
+
+	if (display_on) {
+		DISP_CMD_OUT(CMD_SOFT_RESET_C);
+		DISP_CMD_OUT(CMD_VDD_OFF_C);
+		display_on = FALSE;
+	}
+
+	return 0;
+}
+
+static int epson_s1d_disp_on(struct platform_device *pdev)
+{
+	int i;
+	if (!disp_initialized)
+		epson_s1d_disp_init(pdev);
+
+	if (!display_on) {
+		/* Enable Vdd regulator */
+		DISP_CMD_OUT(CMD_VDD_ON_C);
+		msleep(20);
+
+		/* Soft Reset before configuring display */
+		DISP_CMD_OUT(CMD_SOFT_RESET_C);
+		msleep(20);
+
+		/* Set display attributes */
+
+		/* GATESCAN */
+		DISP_CMD_OUT(CMD_GATE_LINE_SCAN_MODE_C);
+		DISP_DATA_OUT(0x0);
+
+		/* DISSET */
+		DISP_CMD_OUT(CMD_SET_DISPLAY_C);
+		DISP_DATA_OUT(0x31);
+		DISP_DATA_OUT(0x00);
+		DISP_DATA_OUT((uint8)((PANEL_HEIGHT - 1)>>8));
+		DISP_DATA_OUT((uint8)(PANEL_HEIGHT - 1));
+		DISP_DATA_OUT(0x03);
+		DISP_DATA_OUT(0x00);
+		DISP_DATA_OUT(0x08);
+
+		/* VOLSET */
+		DISP_CMD_OUT(
+				   CMD_SET_ELECTRONIC_CONTROL_C);
+		DISP_DATA_OUT(0x10);
+		DISP_DATA_OUT(0x80);
+		DISP_DATA_OUT(0x11);
+		DISP_DATA_OUT(0x1B);
+		DISP_DATA_OUT(0x02);
+		DISP_DATA_OUT(0x0D);
+		DISP_DATA_OUT(0x00);
+
+		/* PWRCTL */
+		DISP_CMD_OUT(CMD_SET_POWER_CONTROL_C);
+		DISP_DATA_OUT(0x01);
+		DISP_DATA_OUT(0x24);
+		DISP_DATA_OUT(0x0F);
+		DISP_DATA_OUT(0xFE);
+		DISP_DATA_OUT(0x33);
+		DISP_DATA_OUT(0x31);
+		DISP_DATA_OUT(0xFF);
+		DISP_DATA_OUT(0x03);
+		DISP_DATA_OUT(0x00);
+		DISP_DATA_OUT(0x77);
+		DISP_DATA_OUT(0x33);
+		DISP_DATA_OUT(0x11);
+		DISP_DATA_OUT(0x44);
+		DISP_DATA_OUT(0x00);
+
+		/* PPWRCTL */
+		DISP_CMD_OUT(CMD_SET_PARTIAL_POWER_CONTROL_C);
+		DISP_DATA_OUT(0x33);
+		DISP_DATA_OUT(0xFF);
+		DISP_DATA_OUT(0x03);
+		DISP_DATA_OUT(0x00);
+		DISP_DATA_OUT(0x44);
+		DISP_DATA_OUT(0x00);
+
+		/* SPLOUT */
+		DISP_CMD_OUT(CMD_SLEEP_OUT_C);
+		msleep(100);
+
+		/* DATSET */
+		DISP_CMD_OUT(CMD_SET_DATA_C);
+		DISP_DATA_OUT(0x00);
+
+		/* DISTMEMSET */
+		DISP_CMD_OUT(CMD_SET_DISPLAY_TIMING_C);
+		DISP_DATA_OUT(0x01);
+		DISP_DATA_OUT(0x2E);
+		DISP_DATA_OUT(0x0A);
+		DISP_DATA_OUT(0x2C);
+		DISP_DATA_OUT(0x23);
+		DISP_DATA_OUT(0x2F);
+		DISP_DATA_OUT(0x00);
+
+		/* GAMSETP */
+		DISP_CMD_OUT(CMD_SET_POSITIVE_CORRECTION_CHARS_C);
+		DISP_DATA_OUT(0x37);
+		DISP_DATA_OUT(0xFF);
+		DISP_DATA_OUT(0x7F);
+		DISP_DATA_OUT(0x15);
+		DISP_DATA_OUT(0x37);
+		DISP_DATA_OUT(0x05);
+
+		/* GAMSETN */
+		DISP_CMD_OUT(CMD_SET_NEGATIVE_CORRECTION_CHARS_C);
+		DISP_DATA_OUT(0x37);
+		DISP_DATA_OUT(0xFF);
+		DISP_DATA_OUT(0x7F);
+		DISP_DATA_OUT(0x15);
+		DISP_DATA_OUT(0x37);
+		DISP_DATA_OUT(0x05);
+
+		/* ACDRIVE */
+		DISP_CMD_OUT(CMD_SET_AC_OPERATION_DRIVE_C);
+		DISP_DATA_OUT(0x00);
+
+		/* TEST */
+		DISP_CMD_OUT(CMD_TEST_C);
+		DISP_DATA_OUT(0x00);
+		DISP_DATA_OUT(0x00);
+		DISP_DATA_OUT(0x00);
+		DISP_DATA_OUT(0x01);
+
+		/* COLMOD */
+		DISP_CMD_OUT(CMD_SET_DISPLAY_COLOR_MODE_C);
+		DISP_DATA_OUT(0x00);
+
+		/* STADDSET */
+		DISP_CMD_OUT(CMD_SET_START_ADDRESS_C);
+		DISP_DATA_OUT(0x00);
+		DISP_DATA_OUT(0x00);
+		DISP_DATA_OUT(0x00);
+
+		/* EDADDSET */
+		DISP_CMD_OUT(CMD_SET_END_ADDRESS_C);
+		DISP_DATA_OUT(0xEF);
+		DISP_DATA_OUT(0x01);
+		DISP_DATA_OUT(0x3F);
+
+		/* Set Display Start Line */
+		DISP_CMD_OUT(CMD_SET_DISPLAY_START_LINE_C);
+		DISP_DATA_OUT(0x00);
+
+		/* Set Display Data Interface */
+		DISP_CMD_OUT(CMD_SET_DISPLAY_DATA_INTERFACE_C);
+		DISP_DATA_OUT(0x00);
+		DISP_DATA_OUT(0x04);
+
+		epson_s1d_disp_set_rect(0,
+						 0,
+						 ACTIVE_WIN_WIDTH,
+						 ACTIVE_WIN_HEIGHT);
+
+		for (i = 0; i < (ACTIVE_WIN_WIDTH * ACTIVE_WIN_HEIGHT); i++)
+			outpdw(DISP_DATA_PORT, 0);
+
+		/* DISON */
+		DISP_CMD_OUT(CMD_DISPLAY_ON_C);
+		msleep(60);
+
+		display_on = TRUE;
+	}
+
+	return 0;
+}
+
+static int epson_s1d_probe(struct platform_device *pdev)
+{
+	if (pdev->id == 0) {
+		ebi2_epson_pdata = pdev->dev.platform_data;
+		return 0;
+	}
+
+	msm_fb_add_device(pdev);
+	return 0;
+}
+
+static struct platform_driver this_driver = {
+	.probe  = epson_s1d_probe,
+	.driver = {
+		.name   = "ebi2_epson_s1d_qvga",
+	},
+};
+
+static struct msm_fb_panel_data epson_s1d_panel_data = {
+	.on = epson_s1d_disp_on,
+	.off = epson_s1d_disp_off,
+	.set_rect = epson_s1d_disp_set_rect,
+};
+
+static struct platform_device this_device = {
+	.name   = "ebi2_epson_s1d_qvga",
+	.id	= 1,
+	.dev	= {
+		.platform_data = &epson_s1d_panel_data,
+	}
+};
+
+static int __init epson_s1d_init(void)
+{
+	int ret;
+	struct msm_panel_info *pinfo;
+
+	ret = platform_driver_register(&this_driver);
+	if (!ret) {
+		pinfo = &epson_s1d_panel_data.panel_info;
+		pinfo->xres = PANEL_WIDTH;
+		pinfo->yres = PANEL_HEIGHT;
+		MSM_FB_SINGLE_MODE_PANEL(pinfo);
+		pinfo->type = EBI2_PANEL;
+		pinfo->pdest = DISPLAY_1;
+		pinfo->wait_cycle = 0x048423E8;
+		pinfo->bpp = 18;
+		pinfo->fb_num = 2;
+		pinfo->lcd.vsync_enable = FALSE;
+
+		ret = platform_device_register(&this_device);
+		if (ret)
+			platform_driver_unregister(&this_driver);
+	}
+
+	return ret;
+}
+
+module_init(epson_s1d_init);
diff --git a/drivers/video/msm/ebi2_host.c b/drivers/video/msm/ebi2_host.c
new file mode 100644
index 0000000..8ba5506
--- /dev/null
+++ b/drivers/video/msm/ebi2_host.c
@@ -0,0 +1,307 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * 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/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/hrtimer.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/semaphore.h>
+#include <linux/uaccess.h>
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <mach/clk.h>
+#include <mach/hardware.h>
+#include "msm_fb.h"
+
+struct mdp_ccs mdp_ccs_rgb2yuv;
+struct mdp_ccs mdp_ccs_yuv2rgb;
+
+static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST];
+static int pdev_list_cnt;
+static int ebi2_host_resource_initialized;
+static struct msm_panel_common_pdata *ebi2_host_pdata;
+
+static int ebi2_host_probe(struct platform_device *pdev);
+static int ebi2_host_remove(struct platform_device *pdev);
+
+static int ebi2_host_runtime_suspend(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: suspending...\n");
+	return 0;
+}
+
+static int ebi2_host_runtime_resume(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: resuming...\n");
+	return 0;
+}
+
+static const struct dev_pm_ops ebi2_host_dev_pm_ops = {
+	.runtime_suspend = ebi2_host_runtime_suspend,
+	.runtime_resume = ebi2_host_runtime_resume,
+};
+
+
+static struct platform_driver ebi2_host_driver = {
+	.probe = ebi2_host_probe,
+	.remove = ebi2_host_remove,
+	.shutdown = NULL,
+	.driver = {
+		/*
+		 * Simulate mdp hw
+		 */
+		.name = "mdp",
+		.pm = &ebi2_host_dev_pm_ops,
+	},
+};
+
+void mdp_pipe_ctrl(MDP_BLOCK_TYPE block, MDP_BLOCK_POWER_STATE state,
+		   boolean isr)
+{
+	return;
+}
+int mdp_ppp_blit(struct fb_info *info, struct mdp_blit_req *req)
+{
+	return 0;
+}
+int mdp_start_histogram(struct fb_info *info)
+{
+	return 0;
+}
+int mdp_stop_histogram(struct fb_info *info)
+{
+	return 0;
+}
+void mdp_refresh_screen(unsigned long data)
+{
+	return;
+}
+
+static int ebi2_host_off(struct platform_device *pdev)
+{
+	int ret;
+	ret = panel_next_off(pdev);
+	return ret;
+}
+
+static int ebi2_host_on(struct platform_device *pdev)
+{
+	int ret;
+	ret = panel_next_on(pdev);
+	return ret;
+}
+
+
+static int ebi2_host_probe(struct platform_device *pdev)
+{
+	struct platform_device *msm_fb_dev = NULL;
+	struct msm_fb_data_type *mfd;
+	struct msm_fb_panel_data *pdata = NULL;
+	int rc;
+
+	if ((pdev->id == 0) && (pdev->num_resources > 0)) {
+
+		ebi2_host_pdata = pdev->dev.platform_data;
+
+		ebi2_host_resource_initialized = 1;
+		return 0;
+	}
+
+	ebi2_host_resource_initialized = 1;
+	if (!ebi2_host_resource_initialized)
+		return -EPERM;
+
+	mfd = platform_get_drvdata(pdev);
+
+	if (!mfd)
+		return -ENODEV;
+
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST)
+		return -ENOMEM;
+
+	msm_fb_dev = platform_device_alloc("msm_fb", pdev->id);
+	if (!msm_fb_dev)
+		return -ENOMEM;
+
+	/* link to the latest pdev */
+	mfd->pdev = msm_fb_dev;
+
+	if (ebi2_host_pdata) {
+		mfd->mdp_rev = ebi2_host_pdata->mdp_rev;
+		mfd->mem_hid = ebi2_host_pdata->mem_hid;
+	}
+
+	/* add panel data */
+	if (platform_device_add_data
+	    (msm_fb_dev, pdev->dev.platform_data,
+	     sizeof(struct msm_fb_panel_data))) {
+		pr_err("ebi2_host_probe: platform_device_add_data failed!\n");
+		rc = -ENOMEM;
+		goto ebi2_host_probe_err;
+	}
+	/* data chain */
+	pdata = msm_fb_dev->dev.platform_data;
+	pdata->on = ebi2_host_on;
+	pdata->off = ebi2_host_off;
+	pdata->next = pdev;
+
+	/* set driver data */
+	platform_set_drvdata(msm_fb_dev, mfd);
+
+	rc = platform_device_add(msm_fb_dev);
+	if (rc)
+		goto ebi2_host_probe_err;
+
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+
+	pdev_list[pdev_list_cnt++] = pdev;
+	return 0;
+
+ebi2_host_probe_err:
+	platform_device_put(msm_fb_dev);
+	return rc;
+}
+
+void mdp_set_dma_pan_info(struct fb_info *info, struct mdp_dirty_region *dirty,
+			  boolean sync)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	struct fb_info *fbi = mfd->fbi;
+	struct msm_panel_info *panel_info = &mfd->panel_info;
+	MDPIBUF *iBuf;
+	int bpp = info->var.bits_per_pixel / 8;
+	int yres, remainder;
+
+	if (panel_info->mode2_yres != 0) {
+		yres = panel_info->mode2_yres;
+		remainder = (fbi->fix.line_length*yres)%PAGE_SIZE;
+	} else {
+		yres = panel_info->yres;
+		remainder = (fbi->fix.line_length*yres)%PAGE_SIZE;
+	}
+
+	if (!remainder)
+		remainder = PAGE_SIZE;
+
+	down(&mfd->sem);
+
+	iBuf = &mfd->ibuf;
+	/* use virtual address */
+	iBuf->buf = (uint8 *) fbi->screen_base;
+
+	if (fbi->var.yoffset < yres) {
+		iBuf->buf += fbi->var.xoffset * bpp;
+	} else if (fbi->var.yoffset >= yres && fbi->var.yoffset < 2 * yres) {
+		iBuf->buf += fbi->var.xoffset * bpp + yres *
+		fbi->fix.line_length + PAGE_SIZE - remainder;
+	} else {
+		iBuf->buf += fbi->var.xoffset * bpp + 2 * yres *
+		fbi->fix.line_length + 2 * (PAGE_SIZE - remainder);
+	}
+
+	iBuf->ibuf_width = info->var.xres_virtual;
+	iBuf->bpp = bpp;
+
+	iBuf->vsync_enable = sync;
+
+	if (dirty) {
+		/*
+		 * ToDo: dirty region check inside var.xoffset+xres
+		 * <-> var.yoffset+yres
+		 */
+		iBuf->dma_x = dirty->xoffset % info->var.xres;
+		iBuf->dma_y = dirty->yoffset % info->var.yres;
+		iBuf->dma_w = dirty->width;
+		iBuf->dma_h = dirty->height;
+	} else {
+		iBuf->dma_x = 0;
+		iBuf->dma_y = 0;
+		iBuf->dma_w = info->var.xres;
+		iBuf->dma_h = info->var.yres;
+	}
+	mfd->ibuf_flushed = FALSE;
+	up(&mfd->sem);
+}
+
+void mdp_dma_pan_update(struct fb_info *info)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	MDPIBUF *iBuf;
+	int i, j;
+	uint32 data;
+	uint8 *src;
+	struct msm_fb_panel_data *pdata =
+	    (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data;
+	struct fb_info *fbi = mfd->fbi;
+
+	iBuf = &mfd->ibuf;
+
+	invalidate_caches((unsigned long)fbi->screen_base,
+		(unsigned long)info->fix.smem_len,
+		(unsigned long)info->fix.smem_start);
+
+	pdata->set_rect(iBuf->dma_x, iBuf->dma_y, iBuf->dma_w,
+			iBuf->dma_h);
+	for (i = 0; i < iBuf->dma_h; i++) {
+		src = iBuf->buf + (fbi->fix.line_length * (iBuf->dma_y + i))
+			+ (iBuf->dma_x * iBuf->bpp);
+		for (j = 0; j < iBuf->dma_w; j++) {
+			data = (uint32)(*src++ >> 2) << 12;
+			data |= (uint32)(*src++ >> 2) << 6;
+			data |= (uint32)(*src++ >> 2);
+			data = ((data&0x1FF)<<16) | ((data&0x3FE00)>>9);
+			outpdw(mfd->data_port, data);
+		}
+	}
+}
+
+static int ebi2_host_remove(struct platform_device *pdev)
+{
+	pm_runtime_disable(&pdev->dev);
+
+	return 0;
+}
+
+static int ebi2_host_register_driver(void)
+{
+	return platform_driver_register(&ebi2_host_driver);
+}
+
+static int __init ebi2_host_driver_init(void)
+{
+	int ret;
+
+	ret = ebi2_host_register_driver();
+	if (ret) {
+		pr_err("ebi2_host_register_driver() failed!\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+module_init(ebi2_host_driver_init);
diff --git a/drivers/video/msm/ebi2_l2f.c b/drivers/video/msm/ebi2_l2f.c
new file mode 100644
index 0000000..767b802
--- /dev/null
+++ b/drivers/video/msm/ebi2_l2f.c
@@ -0,0 +1,566 @@
+/* Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_fb.h"
+
+#include <linux/memory.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include "linux/proc_fs.h"
+
+#include <linux/delay.h>
+
+#include <mach/hardware.h>
+#include <linux/io.h>
+
+#include <asm/system.h>
+#include <asm/mach-types.h>
+
+/* The following are for MSM5100 on Gator
+*/
+#ifdef FEATURE_PM1000
+#include "pm1000.h"
+#endif /* FEATURE_PM1000 */
+/* The following are for MSM6050 on Bambi
+*/
+#ifdef FEATURE_PMIC_LCDKBD_LED_DRIVER
+#include "pm.h"
+#endif /* FEATURE_PMIC_LCDKBD_LED_DRIVER */
+
+#ifdef DISP_DEVICE_18BPP
+#undef DISP_DEVICE_18BPP
+#define DISP_DEVICE_16BPP
+#endif
+
+#define QCIF_WIDTH        176
+#define QCIF_HEIGHT       220
+
+static void *DISP_CMD_PORT;
+static void *DISP_DATA_PORT;
+
+#define DISP_CMD_DISON    0xaf
+#define DISP_CMD_DISOFF   0xae
+#define DISP_CMD_DISNOR   0xa6
+#define DISP_CMD_DISINV   0xa7
+#define DISP_CMD_DISCTL   0xca
+#define DISP_CMD_GCP64    0xcb
+#define DISP_CMD_GCP16    0xcc
+#define DISP_CMD_GSSET    0xcd
+#define DISP_GS_2       0x02
+#define DISP_GS_16      0x01
+#define DISP_GS_64      0x00
+#define DISP_CMD_SLPIN    0x95
+#define DISP_CMD_SLPOUT   0x94
+#define DISP_CMD_SD_PSET  0x75
+#define DISP_CMD_MD_PSET  0x76
+#define DISP_CMD_SD_CSET  0x15
+#define DISP_CMD_MD_CSET  0x16
+#define DISP_CMD_DATCTL   0xbc
+#define DISP_DATCTL_666 0x08
+#define DISP_DATCTL_565 0x28
+#define DISP_DATCTL_444 0x38
+#define DISP_CMD_RAMWR    0x5c
+#define DISP_CMD_RAMRD    0x5d
+#define DISP_CMD_PTLIN    0xa8
+#define DISP_CMD_PTLOUT   0xa9
+#define DISP_CMD_ASCSET   0xaa
+#define DISP_CMD_SCSTART  0xab
+#define DISP_CMD_VOLCTL   0xc6
+#define DISP_VOLCTL_TONE 0x80
+#define DISP_CMD_NOp      0x25
+#define DISP_CMD_OSSEL    0xd0
+#define DISP_CMD_3500KSET 0xd1
+#define DISP_CMD_3500KEND 0xd2
+#define DISP_CMD_14MSET   0xd3
+#define DISP_CMD_14MEND   0xd4
+
+#define DISP_CMD_OUT(cmd) outpw(DISP_CMD_PORT, cmd);
+
+#define DISP_DATA_OUT(data) outpw(DISP_DATA_PORT, data);
+
+#define DISP_DATA_IN() inpw(DISP_DATA_PORT);
+
+/* Epson device column number starts at 2
+*/
+#define DISP_SET_RECT(ulhc_row, lrhc_row, ulhc_col, lrhc_col) \
+	  DISP_CMD_OUT(DISP_CMD_SD_PSET) \
+	  DISP_DATA_OUT((ulhc_row) & 0xFF) \
+	  DISP_DATA_OUT((ulhc_row) >> 8) \
+	  DISP_DATA_OUT((lrhc_row) & 0xFF) \
+	  DISP_DATA_OUT((lrhc_row) >> 8) \
+	  DISP_CMD_OUT(DISP_CMD_SD_CSET) \
+	  DISP_DATA_OUT(((ulhc_col)+2) & 0xFF) \
+	  DISP_DATA_OUT(((ulhc_col)+2) >> 8) \
+	  DISP_DATA_OUT(((lrhc_col)+2) & 0xFF) \
+	  DISP_DATA_OUT(((lrhc_col)+2) >> 8)
+
+#define DISP_MIN_CONTRAST      0
+#define DISP_MAX_CONTRAST      127
+#define DISP_DEFAULT_CONTRAST  80
+
+#define DISP_MIN_BACKLIGHT     0
+#define DISP_MAX_BACKLIGHT     15
+#define DISP_DEFAULT_BACKLIGHT 2
+
+#define WAIT_SEC(sec) mdelay((sec)/1000)
+
+static word disp_area_start_row;
+static word disp_area_end_row;
+static byte disp_contrast = DISP_DEFAULT_CONTRAST;
+static boolean disp_powered_up;
+static boolean disp_initialized = FALSE;
+/* For some reason the contrast set at init time is not good. Need to do
+ * it again
+ */
+static boolean display_on = FALSE;
+static void epsonQcif_disp_init(struct platform_device *pdev);
+static void epsonQcif_disp_set_contrast(word contrast);
+static void epsonQcif_disp_set_display_area(word start_row, word end_row);
+static int epsonQcif_disp_off(struct platform_device *pdev);
+static int epsonQcif_disp_on(struct platform_device *pdev);
+static void epsonQcif_disp_set_rect(int x, int y, int xres, int yres);
+
+volatile word databack;
+static void epsonQcif_disp_init(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+
+	int i;
+
+	if (disp_initialized)
+		return;
+
+	mfd = platform_get_drvdata(pdev);
+
+	DISP_CMD_PORT = mfd->cmd_port;
+	DISP_DATA_PORT = mfd->data_port;
+
+	/* Sleep in */
+	DISP_CMD_OUT(DISP_CMD_SLPIN);
+
+	/* Display off */
+	DISP_CMD_OUT(DISP_CMD_DISOFF);
+
+	/* Display normal */
+	DISP_CMD_OUT(DISP_CMD_DISNOR);
+
+	/* Set data mode */
+	DISP_CMD_OUT(DISP_CMD_DATCTL);
+	DISP_DATA_OUT(DISP_DATCTL_565);
+
+	/* Set display timing */
+	DISP_CMD_OUT(DISP_CMD_DISCTL);
+	DISP_DATA_OUT(0x1c);	/* p1 */
+	DISP_DATA_OUT(0x02);	/* p1 */
+	DISP_DATA_OUT(0x82);	/* p2 */
+	DISP_DATA_OUT(0x00);	/* p3 */
+	DISP_DATA_OUT(0x00);	/* p4 */
+	DISP_DATA_OUT(0xe0);	/* p5 */
+	DISP_DATA_OUT(0x00);	/* p5 */
+	DISP_DATA_OUT(0xdc);	/* p6 */
+	DISP_DATA_OUT(0x00);	/* p6 */
+	DISP_DATA_OUT(0x02);	/* p7 */
+	DISP_DATA_OUT(0x00);	/* p8 */
+
+	/* Set 64 gray scale level */
+	DISP_CMD_OUT(DISP_CMD_GCP64);
+	DISP_DATA_OUT(0x08);	/* p01 */
+	DISP_DATA_OUT(0x00);
+	DISP_DATA_OUT(0x2a);	/* p02 */
+	DISP_DATA_OUT(0x00);
+	DISP_DATA_OUT(0x4e);	/* p03 */
+	DISP_DATA_OUT(0x00);
+	DISP_DATA_OUT(0x6b);	/* p04 */
+	DISP_DATA_OUT(0x00);
+	DISP_DATA_OUT(0x88);	/* p05 */
+	DISP_DATA_OUT(0x00);
+	DISP_DATA_OUT(0xa3);	/* p06 */
+	DISP_DATA_OUT(0x00);
+	DISP_DATA_OUT(0xba);	/* p07 */
+	DISP_DATA_OUT(0x00);
+	DISP_DATA_OUT(0xd1);	/* p08 */
+	DISP_DATA_OUT(0x00);
+	DISP_DATA_OUT(0xe5);	/* p09 */
+	DISP_DATA_OUT(0x00);
+	DISP_DATA_OUT(0xf3);	/* p10 */
+	DISP_DATA_OUT(0x00);
+	DISP_DATA_OUT(0x03);	/* p11 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0x13);	/* p12 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0x22);	/* p13 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0x2f);	/* p14 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0x3b);	/* p15 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0x46);	/* p16 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0x51);	/* p17 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0x5b);	/* p18 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0x64);	/* p19 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0x6c);	/* p20 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0x74);	/* p21 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0x7c);	/* p22 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0x83);	/* p23 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0x8a);	/* p24 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0x91);	/* p25 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0x98);	/* p26 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0x9f);	/* p27 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0xa6);	/* p28 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0xac);	/* p29 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0xb2);	/* p30 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0xb7);	/* p31 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0xbc);	/* p32 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0xc1);	/* p33 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0xc6);	/* p34 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0xcb);	/* p35 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0xd0);	/* p36 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0xd4);	/* p37 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0xd8);	/* p38 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0xdc);	/* p39 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0xe0);	/* p40 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0xe4);	/* p41 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0xe8);	/* p42 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0xec);	/* p43 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0xf0);	/* p44 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0xf4);	/* p45 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0xf8);	/* p46 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0xfb);	/* p47 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0xfe);	/* p48 */
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0x01);	/* p49 */
+	DISP_DATA_OUT(0x02);
+	DISP_DATA_OUT(0x03);	/* p50 */
+	DISP_DATA_OUT(0x02);
+	DISP_DATA_OUT(0x05);	/* p51 */
+	DISP_DATA_OUT(0x02);
+	DISP_DATA_OUT(0x07);	/* p52 */
+	DISP_DATA_OUT(0x02);
+	DISP_DATA_OUT(0x09);	/* p53 */
+	DISP_DATA_OUT(0x02);
+	DISP_DATA_OUT(0x0b);	/* p54 */
+	DISP_DATA_OUT(0x02);
+	DISP_DATA_OUT(0x0d);	/* p55 */
+	DISP_DATA_OUT(0x02);
+	DISP_DATA_OUT(0x0f);	/* p56 */
+	DISP_DATA_OUT(0x02);
+	DISP_DATA_OUT(0x11);	/* p57 */
+	DISP_DATA_OUT(0x02);
+	DISP_DATA_OUT(0x13);	/* p58 */
+	DISP_DATA_OUT(0x02);
+	DISP_DATA_OUT(0x15);	/* p59 */
+	DISP_DATA_OUT(0x02);
+	DISP_DATA_OUT(0x17);	/* p60 */
+	DISP_DATA_OUT(0x02);
+	DISP_DATA_OUT(0x19);	/* p61 */
+	DISP_DATA_OUT(0x02);
+	DISP_DATA_OUT(0x1b);	/* p62 */
+	DISP_DATA_OUT(0x02);
+	DISP_DATA_OUT(0x1c);	/* p63 */
+	DISP_DATA_OUT(0x02);
+
+	/* Set 16 gray scale level */
+	DISP_CMD_OUT(DISP_CMD_GCP16);
+	DISP_DATA_OUT(0x1a);	/* p01 */
+	DISP_DATA_OUT(0x32);	/* p02 */
+	DISP_DATA_OUT(0x42);	/* p03 */
+	DISP_DATA_OUT(0x4c);	/* p04 */
+	DISP_DATA_OUT(0x58);	/* p05 */
+	DISP_DATA_OUT(0x5f);	/* p06 */
+	DISP_DATA_OUT(0x66);	/* p07 */
+	DISP_DATA_OUT(0x6b);	/* p08 */
+	DISP_DATA_OUT(0x70);	/* p09 */
+	DISP_DATA_OUT(0x74);	/* p10 */
+	DISP_DATA_OUT(0x78);	/* p11 */
+	DISP_DATA_OUT(0x7b);	/* p12 */
+	DISP_DATA_OUT(0x7e);	/* p13 */
+	DISP_DATA_OUT(0x80);	/* p14 */
+	DISP_DATA_OUT(0x82);	/* p15 */
+
+	/* Set DSP column */
+	DISP_CMD_OUT(DISP_CMD_MD_CSET);
+	DISP_DATA_OUT(0xff);
+	DISP_DATA_OUT(0x03);
+	DISP_DATA_OUT(0xff);
+	DISP_DATA_OUT(0x03);
+
+	/* Set DSP page */
+	DISP_CMD_OUT(DISP_CMD_MD_PSET);
+	DISP_DATA_OUT(0xff);
+	DISP_DATA_OUT(0x01);
+	DISP_DATA_OUT(0xff);
+	DISP_DATA_OUT(0x01);
+
+	/* Set ARM column */
+	DISP_CMD_OUT(DISP_CMD_SD_CSET);
+	DISP_DATA_OUT(0x02);
+	DISP_DATA_OUT(0x00);
+	DISP_DATA_OUT((QCIF_WIDTH + 1) & 0xFF);
+	DISP_DATA_OUT((QCIF_WIDTH + 1) >> 8);
+
+	/* Set ARM page */
+	DISP_CMD_OUT(DISP_CMD_SD_PSET);
+	DISP_DATA_OUT(0x00);
+	DISP_DATA_OUT(0x00);
+	DISP_DATA_OUT((QCIF_HEIGHT - 1) & 0xFF);
+	DISP_DATA_OUT((QCIF_HEIGHT - 1) >> 8);
+
+	/* Set 64 gray scales */
+	DISP_CMD_OUT(DISP_CMD_GSSET);
+	DISP_DATA_OUT(DISP_GS_64);
+
+	DISP_CMD_OUT(DISP_CMD_OSSEL);
+	DISP_DATA_OUT(0);
+
+	/* Sleep out */
+	DISP_CMD_OUT(DISP_CMD_SLPOUT);
+
+	WAIT_SEC(40000);
+
+	/* Initialize power IC */
+	DISP_CMD_OUT(DISP_CMD_VOLCTL);
+	DISP_DATA_OUT(DISP_VOLCTL_TONE);
+
+	WAIT_SEC(40000);
+
+	/* Set electronic volume, d'xx */
+	DISP_CMD_OUT(DISP_CMD_VOLCTL);
+	DISP_DATA_OUT(DISP_DEFAULT_CONTRAST);	/* value from 0 to 127 */
+
+	/* Initialize display data */
+	DISP_SET_RECT(0, (QCIF_HEIGHT - 1), 0, (QCIF_WIDTH - 1));
+	DISP_CMD_OUT(DISP_CMD_RAMWR);
+	for (i = 0; i < QCIF_HEIGHT * QCIF_WIDTH; i++)
+		DISP_DATA_OUT(0xffff);
+
+	DISP_CMD_OUT(DISP_CMD_RAMRD);
+	databack = DISP_DATA_IN();
+	databack = DISP_DATA_IN();
+	databack = DISP_DATA_IN();
+	databack = DISP_DATA_IN();
+
+	WAIT_SEC(80000);
+
+	DISP_CMD_OUT(DISP_CMD_DISON);
+
+	disp_area_start_row = 0;
+	disp_area_end_row = QCIF_HEIGHT - 1;
+	disp_powered_up = TRUE;
+	disp_initialized = TRUE;
+	epsonQcif_disp_set_display_area(0, QCIF_HEIGHT - 1);
+	display_on = TRUE;
+}
+
+static void epsonQcif_disp_set_rect(int x, int y, int xres, int yres)
+{
+	if (!disp_initialized)
+		return;
+
+	DISP_SET_RECT(y, y + yres - 1, x, x + xres - 1);
+	DISP_CMD_OUT(DISP_CMD_RAMWR);
+}
+
+static void epsonQcif_disp_set_display_area(word start_row, word end_row)
+{
+	if (!disp_initialized)
+		return;
+
+	if ((start_row == disp_area_start_row)
+	    && (end_row == disp_area_end_row))
+		return;
+	disp_area_start_row = start_row;
+	disp_area_end_row = end_row;
+
+	/* Range checking
+	 */
+	if (end_row >= QCIF_HEIGHT)
+		end_row = QCIF_HEIGHT - 1;
+	if (start_row > end_row)
+		start_row = end_row;
+
+	/* When display is not the full screen, gray scale is set to
+	 ** 2; otherwise it is set to 64.
+	 */
+	if ((start_row == 0) && (end_row == (QCIF_HEIGHT - 1))) {
+		/* The whole screen */
+		DISP_CMD_OUT(DISP_CMD_PTLOUT);
+		WAIT_SEC(10000);
+		DISP_CMD_OUT(DISP_CMD_DISOFF);
+		WAIT_SEC(100000);
+		DISP_CMD_OUT(DISP_CMD_GSSET);
+		DISP_DATA_OUT(DISP_GS_64);
+		WAIT_SEC(100000);
+		DISP_CMD_OUT(DISP_CMD_DISON);
+	} else {
+		/* partial screen */
+		DISP_CMD_OUT(DISP_CMD_PTLIN);
+		DISP_DATA_OUT(start_row);
+		DISP_DATA_OUT(start_row >> 8);
+		DISP_DATA_OUT(end_row);
+		DISP_DATA_OUT(end_row >> 8);
+		DISP_CMD_OUT(DISP_CMD_GSSET);
+		DISP_DATA_OUT(DISP_GS_2);
+	}
+}
+
+static int epsonQcif_disp_off(struct platform_device *pdev)
+{
+	if (!disp_initialized)
+		epsonQcif_disp_init(pdev);
+
+	if (display_on) {
+		DISP_CMD_OUT(DISP_CMD_DISOFF);
+		DISP_CMD_OUT(DISP_CMD_SLPIN);
+		display_on = FALSE;
+	}
+
+	return 0;
+}
+
+static int epsonQcif_disp_on(struct platform_device *pdev)
+{
+	if (!disp_initialized)
+		epsonQcif_disp_init(pdev);
+
+	if (!display_on) {
+		DISP_CMD_OUT(DISP_CMD_SLPOUT);
+		WAIT_SEC(40000);
+		DISP_CMD_OUT(DISP_CMD_DISON);
+		epsonQcif_disp_set_contrast(disp_contrast);
+		display_on = TRUE;
+	}
+
+	return 0;
+}
+
+static void epsonQcif_disp_set_contrast(word contrast)
+{
+	if (!disp_initialized)
+		return;
+
+	/* Initialize power IC, d'24 */
+	DISP_CMD_OUT(DISP_CMD_VOLCTL);
+	DISP_DATA_OUT(DISP_VOLCTL_TONE);
+
+	WAIT_SEC(40000);
+
+	/* Set electronic volume, d'xx */
+	DISP_CMD_OUT(DISP_CMD_VOLCTL);
+	if (contrast > 127)
+		contrast = 127;
+	DISP_DATA_OUT(contrast);	/* value from 0 to 127 */
+	disp_contrast = (byte) contrast;
+}				/* End disp_set_contrast */
+
+static void epsonQcif_disp_clear_screen_area(
+	word start_row, word end_row, word start_column, word end_column) {
+	int32 i;
+
+	/* Clear the display screen */
+	DISP_SET_RECT(start_row, end_row, start_column, end_column);
+	DISP_CMD_OUT(DISP_CMD_RAMWR);
+	i = (end_row - start_row + 1) * (end_column - start_column + 1);
+	for (; i > 0; i--)
+		DISP_DATA_OUT(0xffff);
+}
+
+static int __init epsonQcif_probe(struct platform_device *pdev)
+{
+	msm_fb_add_device(pdev);
+
+	return 0;
+}
+
+static struct platform_driver this_driver = {
+	.probe  = epsonQcif_probe,
+	.driver = {
+		.name   = "ebi2_epson_qcif",
+	},
+};
+
+static struct msm_fb_panel_data epsonQcif_panel_data = {
+	.on = epsonQcif_disp_on,
+	.off = epsonQcif_disp_off,
+	.set_rect = epsonQcif_disp_set_rect,
+};
+
+static struct platform_device this_device = {
+	.name   = "ebi2_epson_qcif",
+	.id	= 0,
+	.dev	= {
+		.platform_data = &epsonQcif_panel_data,
+	}
+};
+
+static int __init epsonQcif_init(void)
+{
+	int ret;
+	struct msm_panel_info *pinfo;
+
+	ret = platform_driver_register(&this_driver);
+	if (!ret) {
+		pinfo = &epsonQcif_panel_data.panel_info;
+		pinfo->xres = QCIF_WIDTH;
+		pinfo->yres = QCIF_HEIGHT;
+		MSM_FB_SINGLE_MODE_PANEL(pinfo);
+		pinfo->type = EBI2_PANEL;
+		pinfo->pdest = DISPLAY_2;
+		pinfo->wait_cycle = 0x808000;
+		pinfo->bpp = 16;
+		pinfo->fb_num = 2;
+		pinfo->lcd.vsync_enable = FALSE;
+
+		ret = platform_device_register(&this_device);
+		if (ret)
+			platform_driver_unregister(&this_driver);
+	}
+
+	return ret;
+}
+
+module_init(epsonQcif_init);
diff --git a/drivers/video/msm/ebi2_lcd.c b/drivers/video/msm/ebi2_lcd.c
new file mode 100644
index 0000000..966f974
--- /dev/null
+++ b/drivers/video/msm/ebi2_lcd.c
@@ -0,0 +1,308 @@
+/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/uaccess.h>
+#include <linux/workqueue.h>
+#include <linux/string.h>
+#include <linux/version.h>
+#include <linux/proc_fs.h>
+#include <linux/vmalloc.h>
+#include <linux/debugfs.h>
+
+#include "msm_fb.h"
+
+static int ebi2_lcd_probe(struct platform_device *pdev);
+static int ebi2_lcd_remove(struct platform_device *pdev);
+
+static int ebi2_lcd_runtime_suspend(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: suspending...\n");
+	return 0;
+}
+
+static int ebi2_lcd_runtime_resume(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: resuming...\n");
+	return 0;
+}
+
+static struct dev_pm_ops ebi2_lcd_dev_pm_ops = {
+	.runtime_suspend = ebi2_lcd_runtime_suspend,
+	.runtime_resume = ebi2_lcd_runtime_resume,
+};
+
+static struct platform_driver ebi2_lcd_driver = {
+	.probe = ebi2_lcd_probe,
+	.remove = ebi2_lcd_remove,
+	.suspend = NULL,
+	.resume = NULL,
+	.shutdown = NULL,
+	.driver = {
+		   .name = "ebi2_lcd",
+		   .pm = &ebi2_lcd_dev_pm_ops,
+		   },
+};
+
+static void *ebi2_base;
+static void *ebi2_lcd_cfg0;
+static void *ebi2_lcd_cfg1;
+static void __iomem *lcd01_base;
+static void __iomem *lcd02_base;
+static int lcd01_base_phys;
+static int ebi2_lcd_resource_initialized;
+
+static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST];
+static int pdev_list_cnt;
+static struct lcdc_platform_data *ebi2_pdata;
+
+static int ebi2_lcd_on(struct platform_device *pdev)
+{
+	int ret;
+
+	if (ebi2_pdata && ebi2_pdata->lcdc_power_save)
+		ebi2_pdata->lcdc_power_save(1);
+
+	ret = panel_next_on(pdev);
+	return ret;
+}
+
+static int ebi2_lcd_off(struct platform_device *pdev)
+{
+	int ret;
+
+	ret = panel_next_off(pdev);
+
+	if (ebi2_pdata && ebi2_pdata->lcdc_power_save)
+		ebi2_pdata->lcdc_power_save(0);
+
+	return ret;
+}
+
+static int ebi2_lcd_probe(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+	struct platform_device *mdp_dev = NULL;
+	struct msm_fb_panel_data *pdata = NULL;
+	int rc, i, hw_version;
+
+	if (pdev->id == 0) {
+		for (i = 0; i < pdev->num_resources; i++) {
+			if (!strncmp(pdev->resource[i].name, "base", 4)) {
+				ebi2_base = ioremap(pdev->resource[i].start,
+						pdev->resource[i].end -
+						pdev->resource[i].start + 1);
+				if (!ebi2_base) {
+					printk(KERN_ERR
+						"ebi2_base ioremap failed!\n");
+					return -ENOMEM;
+				}
+				ebi2_lcd_cfg0 = (void *)(ebi2_base + 0x20);
+				ebi2_lcd_cfg1 = (void *)(ebi2_base + 0x24);
+			} else if (!strncmp(pdev->resource[i].name,
+						"lcd01", 5)) {
+				lcd01_base_phys = pdev->resource[i].start;
+				lcd01_base = ioremap(pdev->resource[i].start,
+						pdev->resource[i].end -
+						pdev->resource[i].start + 1);
+				if (!lcd01_base) {
+					printk(KERN_ERR
+						"lcd01_base ioremap failed!\n");
+					return -ENOMEM;
+				}
+			} else if (!strncmp(pdev->resource[i].name,
+						"lcd02", 5)) {
+				lcd02_base = ioremap(pdev->resource[i].start,
+						pdev->resource[i].end -
+						pdev->resource[i].start + 1);
+				if (!lcd02_base) {
+					printk(KERN_ERR
+						"lcd02_base ioremap failed!\n");
+					return -ENOMEM;
+				}
+			}
+		}
+		ebi2_pdata = pdev->dev.platform_data;
+		ebi2_lcd_resource_initialized = 1;
+
+		return 0;
+	}
+
+	if (!ebi2_lcd_resource_initialized)
+		return -EPERM;
+
+	mfd = platform_get_drvdata(pdev);
+
+	if (!mfd)
+		return -ENODEV;
+
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST)
+		return -ENOMEM;
+
+	if (ebi2_base == NULL)
+		return -ENOMEM;
+
+	mdp_dev = platform_device_alloc("mdp", pdev->id);
+	if (!mdp_dev)
+		return -ENOMEM;
+
+	/* link to the latest pdev */
+	mfd->pdev = mdp_dev;
+	mfd->dest = DISPLAY_LCD;
+
+	/* add panel data */
+	if (platform_device_add_data
+	    (mdp_dev, pdev->dev.platform_data,
+	     sizeof(struct msm_fb_panel_data))) {
+		printk(KERN_ERR "ebi2_lcd_probe: platform_device_add_data failed!\n");
+		platform_device_put(mdp_dev);
+		return -ENOMEM;
+	}
+
+	/* data chain */
+	pdata = mdp_dev->dev.platform_data;
+	pdata->on = ebi2_lcd_on;
+	pdata->off = ebi2_lcd_off;
+	pdata->next = pdev;
+
+	/* get/set panel specific fb info */
+	mfd->panel_info = pdata->panel_info;
+
+	hw_version = inp32((int)ebi2_base + 8);
+
+	if (mfd->panel_info.bpp == 24)
+		mfd->fb_imgType = MDP_RGB_888;
+	else if (mfd->panel_info.bpp == 18)
+		mfd->fb_imgType = MDP_RGB_888;
+	else
+		mfd->fb_imgType = MDP_RGB_565;
+
+	/* config msm ebi2 lcd register */
+	if (mfd->panel_info.pdest == DISPLAY_1) {
+		outp32(ebi2_base,
+		       (inp32(ebi2_base) & (~(EBI2_PRIM_LCD_CLR))) |
+		       EBI2_PRIM_LCD_SEL);
+		/*
+		 * current design has one set of cfg0/1 register to control
+		 * both EBI2 channels. so, we're using the PRIM channel to
+		 * configure both.
+		 */
+		outp32(ebi2_lcd_cfg0, mfd->panel_info.wait_cycle);
+		if (hw_version < 0x2020) {
+			if (mfd->panel_info.bpp == 18)
+				outp32(ebi2_lcd_cfg1, 0x01000000);
+			else
+				outp32(ebi2_lcd_cfg1, 0x0);
+		}
+	} else {
+#ifdef DEBUG_EBI2_LCD
+		/*
+		 * confliting with QCOM SURF FPGA CS.
+		 * OEM should enable below for their CS mapping
+		 */
+		 outp32(ebi2_base, (inp32(ebi2_base)&(~(EBI2_SECD_LCD_CLR)))
+					|EBI2_SECD_LCD_SEL);
+#endif
+	}
+
+	/*
+	 * map cs (chip select) address
+	 */
+	if (mfd->panel_info.pdest == DISPLAY_1) {
+		mfd->cmd_port = lcd01_base;
+		if (hw_version >= 0x2020) {
+			mfd->data_port =
+				(void *)((uint32) mfd->cmd_port + 0x80);
+			mfd->data_port_phys =
+				(void *)(lcd01_base_phys + 0x80);
+		} else {
+			mfd->data_port =
+			    (void *)((uint32) mfd->cmd_port +
+				    EBI2_PRIM_LCD_RS_PIN);
+			mfd->data_port_phys =
+			    (void *)(LCD_PRIM_BASE_PHYS + EBI2_PRIM_LCD_RS_PIN);
+		}
+	} else {
+		mfd->cmd_port = lcd01_base;
+		mfd->data_port =
+		    (void *)((uint32) mfd->cmd_port + EBI2_SECD_LCD_RS_PIN);
+		mfd->data_port_phys =
+		    (void *)(LCD_SECD_BASE_PHYS + EBI2_SECD_LCD_RS_PIN);
+	}
+
+	/*
+	 * set driver data
+	 */
+	platform_set_drvdata(mdp_dev, mfd);
+
+	/*
+	 * register in mdp driver
+	 */
+	rc = platform_device_add(mdp_dev);
+	if (rc) {
+		goto ebi2_lcd_probe_err;
+	}
+
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+
+
+	pdev_list[pdev_list_cnt++] = pdev;
+	return 0;
+
+      ebi2_lcd_probe_err:
+	platform_device_put(mdp_dev);
+	return rc;
+}
+
+static int ebi2_lcd_remove(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+
+	mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
+
+	if (!mfd)
+		return 0;
+
+	if (mfd->key != MFD_KEY)
+		return 0;
+
+	iounmap(mfd->cmd_port);
+	pm_runtime_disable(&pdev->dev);
+	return 0;
+}
+
+static int ebi2_lcd_register_driver(void)
+{
+	return platform_driver_register(&ebi2_lcd_driver);
+}
+
+static int __init ebi2_lcd_driver_init(void)
+{
+	return ebi2_lcd_register_driver();
+}
+
+module_init(ebi2_lcd_driver_init);
diff --git a/drivers/video/msm/ebi2_tmd20.c b/drivers/video/msm/ebi2_tmd20.c
new file mode 100644
index 0000000..280373f
--- /dev/null
+++ b/drivers/video/msm/ebi2_tmd20.c
@@ -0,0 +1,1120 @@
+/* Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_fb.h"
+
+#include <linux/memory.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include "linux/proc_fs.h"
+
+#include <linux/delay.h>
+
+#include <mach/hardware.h>
+#include <linux/io.h>
+
+#include <asm/system.h>
+#include <asm/mach-types.h>
+
+/* #define TMD20QVGA_LCD_18BPP */
+#define QVGA_WIDTH        240
+#define QVGA_HEIGHT       320
+
+#ifdef TMD20QVGA_LCD_18BPP
+#define DISP_QVGA_18BPP(x)  ((((x)<<2) & 0x3FC00)|(( (x)<<1)& 0x1FE))
+#define DISP_REG(name)  uint32 register_##name;
+#define OUTPORT(x, y)  outpdw(x, y)
+#define INPORT(x)   inpdw(x)
+#else
+#define DISP_QVGA_18BPP(x)  (x)
+#define DISP_REG(name)  uint16 register_##name;
+#define OUTPORT(x, y)  outpw(x, y)
+#define INPORT(x)   intpw(x)
+#endif
+
+static void *DISP_CMD_PORT;
+static void *DISP_DATA_PORT;
+
+#define DISP_RNTI         0x10
+
+#define DISP_CMD_OUT(cmd) OUTPORT(DISP_CMD_PORT, DISP_QVGA_18BPP(cmd))
+#define DISP_DATA_OUT(data) OUTPORT(DISP_DATA_PORT, data)
+#define DISP_DATA_IN() INPORT(DISP_DATA_PORT)
+
+#if (defined(TMD20QVGA_LCD_18BPP))
+#define DISP_DATA_OUT_16TO18BPP(x) \
+	DISP_DATA_OUT((((x)&0xf800)<<2|((x)&0x80000)>>3) \
+		     | (((x)&0x7e0)<<1) \
+		     | (((x)&0x1F)<<1|((x)&0x10)>>4))
+#else
+#define DISP_DATA_OUT_16TO18BPP(x) \
+	DISP_DATA_OUT(x)
+#endif
+
+#define DISP_WRITE_OUT(addr, data) \
+   register_##addr = DISP_QVGA_18BPP(data); \
+   DISP_CMD_OUT(addr); \
+   DISP_DATA_OUT(register_##addr);
+
+#define DISP_UPDATE_VALUE(addr, bitmask, data) \
+   DISP_WRITE_OUT(##addr, (register_##addr & ~(bitmask)) | (data));
+
+#define DISP_VAL_IF(bitvalue, bitmask) \
+   ((bitvalue) ? (bitmask) : 0)
+
+/* QVGA = 256 x 320 */
+/* actual display is 240 x 320...offset by 0x10 */
+#define DISP_ROW_COL_TO_ADDR(row, col) ((row) * 0x100 + col)
+#define DISP_SET_RECT(ulhc_row, lrhc_row, ulhc_col, lrhc_col) \
+   { \
+   DISP_WRITE_OUT(DISP_HORZ_RAM_ADDR_POS_1_ADDR, (ulhc_col) + tmd20qvga_panel_offset); \
+   DISP_WRITE_OUT(DISP_HORZ_RAM_ADDR_POS_2_ADDR, (lrhc_col) + tmd20qvga_panel_offset); \
+   DISP_WRITE_OUT(DISP_VERT_RAM_ADDR_POS_1_ADDR, (ulhc_row)); \
+   DISP_WRITE_OUT(DISP_VERT_RAM_ADDR_POS_2_ADDR, (lrhc_row)); \
+   DISP_WRITE_OUT(DISP_RAM_ADDR_SET_1_ADDR, (ulhc_col) + tmd20qvga_panel_offset); \
+   DISP_WRITE_OUT(DISP_RAM_ADDR_SET_2_ADDR, (ulhc_row)); \
+   }
+
+#define WAIT_MSEC(msec) mdelay(msec)
+
+/*
+ * TMD QVGA Address
+ */
+/* Display Control */
+#define DISP_START_OSCILLATION_ADDR     0x000
+DISP_REG(DISP_START_OSCILLATION_ADDR)
+#define DISP_DRIVER_OUTPUT_CTL_ADDR     0x001
+    DISP_REG(DISP_DRIVER_OUTPUT_CTL_ADDR)
+#define DISP_LCD_DRIVING_SIG_ADDR     0x002
+    DISP_REG(DISP_LCD_DRIVING_SIG_ADDR)
+#define DISP_ENTRY_MODE_ADDR            0x003
+    DISP_REG(DISP_ENTRY_MODE_ADDR)
+#define DISP_DISPLAY_CTL_1_ADDR         0x007
+    DISP_REG(DISP_DISPLAY_CTL_1_ADDR)
+#define DISP_DISPLAY_CTL_2_ADDR         0x008
+    DISP_REG(DISP_DISPLAY_CTL_2_ADDR)
+
+/* DISPLAY MODE 0x009 partial display not supported */
+#define DISP_POWER_SUPPLY_INTF_ADDR     0x00A
+    DISP_REG(DISP_POWER_SUPPLY_INTF_ADDR)
+
+/* DISPLAY MODE 0x00B xZoom feature is not supported */
+#define DISP_EXT_DISPLAY_CTL_1_ADDR     0x00C
+    DISP_REG(DISP_EXT_DISPLAY_CTL_1_ADDR)
+
+#define DISP_FRAME_CYCLE_CTL_ADDR       0x00D
+    DISP_REG(DISP_FRAME_CYCLE_CTL_ADDR)
+
+#define DISP_EXT_DISPLAY_CTL_2_ADDR     0x00E
+    DISP_REG(DISP_EXT_DISPLAY_CTL_2_ADDR)
+
+#define DISP_EXT_DISPLAY_CTL_3_ADDR     0x00F
+    DISP_REG(DISP_EXT_DISPLAY_CTL_3_ADDR)
+
+#define DISP_LTPS_CTL_1_ADDR            0x012
+    DISP_REG(DISP_LTPS_CTL_1_ADDR)
+#define DISP_LTPS_CTL_2_ADDR            0x013
+    DISP_REG(DISP_LTPS_CTL_2_ADDR)
+#define DISP_LTPS_CTL_3_ADDR            0x014
+    DISP_REG(DISP_LTPS_CTL_3_ADDR)
+#define DISP_LTPS_CTL_4_ADDR            0x018
+    DISP_REG(DISP_LTPS_CTL_4_ADDR)
+#define DISP_LTPS_CTL_5_ADDR            0x019
+    DISP_REG(DISP_LTPS_CTL_5_ADDR)
+#define DISP_LTPS_CTL_6_ADDR            0x01A
+    DISP_REG(DISP_LTPS_CTL_6_ADDR)
+#define DISP_AMP_SETTING_ADDR           0x01C
+    DISP_REG(DISP_AMP_SETTING_ADDR)
+#define DISP_MODE_SETTING_ADDR          0x01D
+    DISP_REG(DISP_MODE_SETTING_ADDR)
+#define DISP_POFF_LN_SETTING_ADDR       0x01E
+    DISP_REG(DISP_POFF_LN_SETTING_ADDR)
+/* Power Contol */
+#define DISP_POWER_CTL_1_ADDR           0x100
+    DISP_REG(DISP_POWER_CTL_1_ADDR)
+#define DISP_POWER_CTL_2_ADDR           0x101
+    DISP_REG(DISP_POWER_CTL_2_ADDR)
+#define DISP_POWER_CTL_3_ADDR           0x102
+    DISP_REG(DISP_POWER_CTL_3_ADDR)
+#define DISP_POWER_CTL_4_ADDR           0x103
+    DISP_REG(DISP_POWER_CTL_4_ADDR)
+#define DISP_POWER_CTL_5_ADDR           0x104
+    DISP_REG(DISP_POWER_CTL_5_ADDR)
+#define DISP_POWER_CTL_6_ADDR           0x105
+    DISP_REG(DISP_POWER_CTL_6_ADDR)
+#define DISP_POWER_CTL_7_ADDR           0x106
+    DISP_REG(DISP_POWER_CTL_7_ADDR)
+/* RAM Access */
+#define DISP_RAM_ADDR_SET_1_ADDR        0x200
+    DISP_REG(DISP_RAM_ADDR_SET_1_ADDR)
+#define DISP_RAM_ADDR_SET_2_ADDR        0x201
+    DISP_REG(DISP_RAM_ADDR_SET_2_ADDR)
+#define DISP_CMD_RAMRD                  DISP_CMD_RAMWR
+#define DISP_CMD_RAMWR                  0x202
+    DISP_REG(DISP_CMD_RAMWR)
+#define DISP_RAM_DATA_MASK_1_ADDR       0x203
+    DISP_REG(DISP_RAM_DATA_MASK_1_ADDR)
+#define DISP_RAM_DATA_MASK_2_ADDR       0x204
+    DISP_REG(DISP_RAM_DATA_MASK_2_ADDR)
+/* Gamma Control, Contrast, Gray Scale Setting */
+#define DISP_GAMMA_CONTROL_1_ADDR       0x300
+    DISP_REG(DISP_GAMMA_CONTROL_1_ADDR)
+#define DISP_GAMMA_CONTROL_2_ADDR       0x301
+    DISP_REG(DISP_GAMMA_CONTROL_2_ADDR)
+#define DISP_GAMMA_CONTROL_3_ADDR       0x302
+    DISP_REG(DISP_GAMMA_CONTROL_3_ADDR)
+#define DISP_GAMMA_CONTROL_4_ADDR       0x303
+    DISP_REG(DISP_GAMMA_CONTROL_4_ADDR)
+#define DISP_GAMMA_CONTROL_5_ADDR       0x304
+    DISP_REG(DISP_GAMMA_CONTROL_5_ADDR)
+/* Coordinate Control */
+#define DISP_VERT_SCROLL_CTL_1_ADDR     0x400
+    DISP_REG(DISP_VERT_SCROLL_CTL_1_ADDR)
+#define DISP_VERT_SCROLL_CTL_2_ADDR     0x401
+    DISP_REG(DISP_VERT_SCROLL_CTL_2_ADDR)
+#define DISP_SCREEN_1_DRV_POS_1_ADDR    0x402
+    DISP_REG(DISP_SCREEN_1_DRV_POS_1_ADDR)
+#define DISP_SCREEN_1_DRV_POS_2_ADDR    0x403
+    DISP_REG(DISP_SCREEN_1_DRV_POS_2_ADDR)
+#define DISP_SCREEN_2_DRV_POS_1_ADDR    0x404
+    DISP_REG(DISP_SCREEN_2_DRV_POS_1_ADDR)
+#define DISP_SCREEN_2_DRV_POS_2_ADDR    0x405
+    DISP_REG(DISP_SCREEN_2_DRV_POS_2_ADDR)
+#define DISP_HORZ_RAM_ADDR_POS_1_ADDR   0x406
+    DISP_REG(DISP_HORZ_RAM_ADDR_POS_1_ADDR)
+#define DISP_HORZ_RAM_ADDR_POS_2_ADDR   0x407
+    DISP_REG(DISP_HORZ_RAM_ADDR_POS_2_ADDR)
+#define DISP_VERT_RAM_ADDR_POS_1_ADDR   0x408
+    DISP_REG(DISP_VERT_RAM_ADDR_POS_1_ADDR)
+#define DISP_VERT_RAM_ADDR_POS_2_ADDR   0x409
+    DISP_REG(DISP_VERT_RAM_ADDR_POS_2_ADDR)
+#define DISP_TMD_700_ADDR               0x700	/*  0x700 */
+    DISP_REG(DISP_TMD_700_ADDR)
+#define DISP_TMD_015_ADDR               0x015	/*  0x700 */
+    DISP_REG(DISP_TMD_015_ADDR)
+#define DISP_TMD_305_ADDR               0x305	/*  0x700 */
+    DISP_REG(DISP_TMD_305_ADDR)
+
+/*
+ * TMD QVGA Bit Definations
+ */
+
+#define DISP_BIT_IB15              0x8000
+#define DISP_BIT_IB14              0x4000
+#define DISP_BIT_IB13              0x2000
+#define DISP_BIT_IB12              0x1000
+#define DISP_BIT_IB11              0x0800
+#define DISP_BIT_IB10              0x0400
+#define DISP_BIT_IB09              0x0200
+#define DISP_BIT_IB08              0x0100
+#define DISP_BIT_IB07              0x0080
+#define DISP_BIT_IB06              0x0040
+#define DISP_BIT_IB05              0x0020
+#define DISP_BIT_IB04              0x0010
+#define DISP_BIT_IB03              0x0008
+#define DISP_BIT_IB02              0x0004
+#define DISP_BIT_IB01              0x0002
+#define DISP_BIT_IB00              0x0001
+/*
+ * Display Control
+ * DISP_START_OSCILLATION_ADDR     Start Oscillation
+ * DISP_DRIVER_OUTPUT_CTL_ADDR     Driver Output Control
+ */
+#define DISP_BITMASK_SS            DISP_BIT_IB08
+#define DISP_BITMASK_NL5           DISP_BIT_IB05
+#define DISP_BITMASK_NL4           DISP_BIT_IB04
+#define DISP_BITMASK_NL3           DISP_BIT_IB03
+#define DISP_BITMASK_NL2           DISP_BIT_IB02
+#define DISP_BITMASK_NL1           DISP_BIT_IB01
+#define DISP_BITMASK_NL0           DISP_BIT_IB00
+/* DISP_LCD_DRIVING_SIG_ADDR       LCD Driving Signal Setting */
+#define DISP_BITMASK_BC            DISP_BIT_IB09
+/* DISP_ENTRY_MODE_ADDR            Entry Mode */
+#define DISP_BITMASK_TRI           DISP_BIT_IB15
+#define DISP_BITMASK_DFM1          DISP_BIT_IB14
+#define DISP_BITMASK_DFM0          DISP_BIT_IB13
+#define DISP_BITMASK_BGR           DISP_BIT_IB12
+#define DISP_BITMASK_HWM0          DISP_BIT_IB08
+#define DISP_BITMASK_ID1           DISP_BIT_IB05
+#define DISP_BITMASK_ID0           DISP_BIT_IB04
+#define DISP_BITMASK_AM            DISP_BIT_IB03
+/* DISP_DISPLAY_CTL_1_ADDR         Display Control (1) */
+#define DISP_BITMASK_COL1          DISP_BIT_IB15
+#define DISP_BITMASK_COL0          DISP_BIT_IB14
+#define DISP_BITMASK_VLE2          DISP_BIT_IB10
+#define DISP_BITMASK_VLE1          DISP_BIT_IB09
+#define DISP_BITMASK_SPT           DISP_BIT_IB08
+#define DISP_BITMASK_PT1           DISP_BIT_IB07
+#define DISP_BITMASK_PT0           DISP_BIT_IB06
+#define DISP_BITMASK_REV           DISP_BIT_IB02
+/* DISP_DISPLAY_CTL_2_ADDR         Display Control (2) */
+#define DISP_BITMASK_FP3           DISP_BIT_IB11
+#define DISP_BITMASK_FP2           DISP_BIT_IB10
+#define DISP_BITMASK_FP1           DISP_BIT_IB09
+#define DISP_BITMASK_FP0           DISP_BIT_IB08
+#define DISP_BITMASK_BP3           DISP_BIT_IB03
+#define DISP_BITMASK_BP2           DISP_BIT_IB02
+#define DISP_BITMASK_BP1           DISP_BIT_IB01
+#define DISP_BITMASK_BP0           DISP_BIT_IB00
+/* DISP_POWER_SUPPLY_INTF_ADDR     Power Supply IC Interface Control */
+#define DISP_BITMASK_CSE           DISP_BIT_IB12
+#define DISP_BITMASK_TE            DISP_BIT_IB08
+#define DISP_BITMASK_IX3           DISP_BIT_IB03
+#define DISP_BITMASK_IX2           DISP_BIT_IB02
+#define DISP_BITMASK_IX1           DISP_BIT_IB01
+#define DISP_BITMASK_IX0           DISP_BIT_IB00
+/* DISP_EXT_DISPLAY_CTL_1_ADDR     External Display Interface Control (1) */
+#define DISP_BITMASK_RM            DISP_BIT_IB08
+#define DISP_BITMASK_DM1           DISP_BIT_IB05
+#define DISP_BITMASK_DM0           DISP_BIT_IB04
+#define DISP_BITMASK_RIM1          DISP_BIT_IB01
+#define DISP_BITMASK_RIM0          DISP_BIT_IB00
+/* DISP_FRAME_CYCLE_CTL_ADDR       Frame Frequency Adjustment Control */
+#define DISP_BITMASK_DIVI1         DISP_BIT_IB09
+#define DISP_BITMASK_DIVI0         DISP_BIT_IB08
+#define DISP_BITMASK_RTNI4         DISP_BIT_IB04
+#define DISP_BITMASK_RTNI3         DISP_BIT_IB03
+#define DISP_BITMASK_RTNI2         DISP_BIT_IB02
+#define DISP_BITMASK_RTNI1         DISP_BIT_IB01
+#define DISP_BITMASK_RTNI0         DISP_BIT_IB00
+/* DISP_EXT_DISPLAY_CTL_2_ADDR     External Display Interface Control (2) */
+#define DISP_BITMASK_DIVE1         DISP_BIT_IB09
+#define DISP_BITMASK_DIVE0         DISP_BIT_IB08
+#define DISP_BITMASK_RTNE7         DISP_BIT_IB07
+#define DISP_BITMASK_RTNE6         DISP_BIT_IB06
+#define DISP_BITMASK_RTNE5         DISP_BIT_IB05
+#define DISP_BITMASK_RTNE4         DISP_BIT_IB04
+#define DISP_BITMASK_RTNE3         DISP_BIT_IB03
+#define DISP_BITMASK_RTNE2         DISP_BIT_IB02
+#define DISP_BITMASK_RTNE1         DISP_BIT_IB01
+#define DISP_BITMASK_RTNE0         DISP_BIT_IB00
+/* DISP_EXT_DISPLAY_CTL_3_ADDR     External Display Interface Control (3) */
+#define DISP_BITMASK_VSPL          DISP_BIT_IB04
+#define DISP_BITMASK_HSPL          DISP_BIT_IB03
+#define DISP_BITMASK_VPL           DISP_BIT_IB02
+#define DISP_BITMASK_EPL           DISP_BIT_IB01
+#define DISP_BITMASK_DPL           DISP_BIT_IB00
+/* DISP_LTPS_CTL_1_ADDR            LTPS Interface Control (1) */
+#define DISP_BITMASK_CLWI3         DISP_BIT_IB11
+#define DISP_BITMASK_CLWI2         DISP_BIT_IB10
+#define DISP_BITMASK_CLWI1         DISP_BIT_IB09
+#define DISP_BITMASK_CLWI0         DISP_BIT_IB08
+#define DISP_BITMASK_CLTI1         DISP_BIT_IB01
+#define DISP_BITMASK_CLTI0         DISP_BIT_IB00
+/* DISP_LTPS_CTL_2_ADDR            LTPS Interface Control (2) */
+#define DISP_BITMASK_OEVBI1        DISP_BIT_IB09
+#define DISP_BITMASK_OEVBI0        DISP_BIT_IB08
+#define DISP_BITMASK_OEVFI1        DISP_BIT_IB01
+#define DISP_BITMASK_OEVFI0        DISP_BIT_IB00
+/* DISP_LTPS_CTL_3_ADDR            LTPS Interface Control (3) */
+#define DISP_BITMASK_SHI1          DISP_BIT_IB01
+#define DISP_BITMASK_SHI0          DISP_BIT_IB00
+/* DISP_LTPS_CTL_4_ADDR            LTPS Interface Control (4) */
+#define DISP_BITMASK_CLWE5         DISP_BIT_IB13
+#define DISP_BITMASK_CLWE4         DISP_BIT_IB12
+#define DISP_BITMASK_CLWE3         DISP_BIT_IB11
+#define DISP_BITMASK_CLWE2         DISP_BIT_IB10
+#define DISP_BITMASK_CLWE1         DISP_BIT_IB09
+#define DISP_BITMASK_CLWE0         DISP_BIT_IB08
+#define DISP_BITMASK_CLTE3         DISP_BIT_IB03
+#define DISP_BITMASK_CLTE2         DISP_BIT_IB02
+#define DISP_BITMASK_CLTE1         DISP_BIT_IB01
+#define DISP_BITMASK_CLTE0         DISP_BIT_IB00
+/* DISP_LTPS_CTL_5_ADDR            LTPS Interface Control (5) */
+#define DISP_BITMASK_OEVBE3        DISP_BIT_IB11
+#define DISP_BITMASK_OEVBE2        DISP_BIT_IB10
+#define DISP_BITMASK_OEVBE1        DISP_BIT_IB09
+#define DISP_BITMASK_OEVBE0        DISP_BIT_IB08
+#define DISP_BITMASK_OEVFE3        DISP_BIT_IB03
+#define DISP_BITMASK_OEVFE2        DISP_BIT_IB02
+#define DISP_BITMASK_OEVFE1        DISP_BIT_IB01
+#define DISP_BITMASK_OEVFE0        DISP_BIT_IB00
+/* DISP_LTPS_CTL_6_ADDR            LTPS Interface Control (6) */
+#define DISP_BITMASK_SHE3          DISP_BIT_IB03
+#define DISP_BITMASK_SHE2          DISP_BIT_IB02
+#define DISP_BITMASK_SHE1          DISP_BIT_IB01
+#define DISP_BITMASK_SHE0          DISP_BIT_IB00
+/* DISP_AMP_SETTING_ADDR           Amplify Setting */
+#define DISP_BITMASK_ABSW1         DISP_BIT_IB01
+#define DISP_BITMASK_ABSW0         DISP_BIT_IB00
+/* DISP_MODE_SETTING_ADDR          Mode Setting */
+#define DISP_BITMASK_DSTB          DISP_BIT_IB02
+#define DISP_BITMASK_STB           DISP_BIT_IB00
+/* DISP_POFF_LN_SETTING_ADDR       Power Off Line Setting */
+#define DISP_BITMASK_POFH3         DISP_BIT_IB03
+#define DISP_BITMASK_POFH2         DISP_BIT_IB02
+#define DISP_BITMASK_POFH1         DISP_BIT_IB01
+#define DISP_BITMASK_POFH0         DISP_BIT_IB00
+
+/* Power Contol */
+/* DISP_POWER_CTL_1_ADDR           Power Control (1) */
+#define DISP_BITMASK_PO            DISP_BIT_IB11
+#define DISP_BITMASK_VCD           DISP_BIT_IB09
+#define DISP_BITMASK_VSC           DISP_BIT_IB08
+#define DISP_BITMASK_CON           DISP_BIT_IB07
+#define DISP_BITMASK_ASW1          DISP_BIT_IB06
+#define DISP_BITMASK_ASW0          DISP_BIT_IB05
+#define DISP_BITMASK_OEV           DISP_BIT_IB04
+#define DISP_BITMASK_OEVE          DISP_BIT_IB03
+#define DISP_BITMASK_FR            DISP_BIT_IB02
+#define DISP_BITMASK_D1            DISP_BIT_IB01
+#define DISP_BITMASK_D0            DISP_BIT_IB00
+/* DISP_POWER_CTL_2_ADDR           Power Control (2) */
+#define DISP_BITMASK_DC4           DISP_BIT_IB15
+#define DISP_BITMASK_DC3           DISP_BIT_IB14
+#define DISP_BITMASK_SAP2          DISP_BIT_IB13
+#define DISP_BITMASK_SAP1          DISP_BIT_IB12
+#define DISP_BITMASK_SAP0          DISP_BIT_IB11
+#define DISP_BITMASK_BT2           DISP_BIT_IB10
+#define DISP_BITMASK_BT1           DISP_BIT_IB09
+#define DISP_BITMASK_BT0           DISP_BIT_IB08
+#define DISP_BITMASK_DC2           DISP_BIT_IB07
+#define DISP_BITMASK_DC1           DISP_BIT_IB06
+#define DISP_BITMASK_DC0           DISP_BIT_IB05
+#define DISP_BITMASK_AP2           DISP_BIT_IB04
+#define DISP_BITMASK_AP1           DISP_BIT_IB03
+#define DISP_BITMASK_AP0           DISP_BIT_IB02
+/* DISP_POWER_CTL_3_ADDR           Power Control (3) */
+#define DISP_BITMASK_VGL4          DISP_BIT_IB10
+#define DISP_BITMASK_VGL3          DISP_BIT_IB09
+#define DISP_BITMASK_VGL2          DISP_BIT_IB08
+#define DISP_BITMASK_VGL1          DISP_BIT_IB07
+#define DISP_BITMASK_VGL0          DISP_BIT_IB06
+#define DISP_BITMASK_VGH4          DISP_BIT_IB04
+#define DISP_BITMASK_VGH3          DISP_BIT_IB03
+#define DISP_BITMASK_VGH2          DISP_BIT_IB02
+#define DISP_BITMASK_VGH1          DISP_BIT_IB01
+#define DISP_BITMASK_VGH0          DISP_BIT_IB00
+/* DISP_POWER_CTL_4_ADDR           Power Control (4) */
+#define DISP_BITMASK_VC2           DISP_BIT_IB02
+#define DISP_BITMASK_VC1           DISP_BIT_IB01
+#define DISP_BITMASK_VC0           DISP_BIT_IB00
+/* DISP_POWER_CTL_5_ADDR           Power Control (5) */
+#define DISP_BITMASK_VRL3          DISP_BIT_IB11
+#define DISP_BITMASK_VRL2          DISP_BIT_IB10
+#define DISP_BITMASK_VRL1          DISP_BIT_IB09
+#define DISP_BITMASK_VRL0          DISP_BIT_IB08
+#define DISP_BITMASK_PON           DISP_BIT_IB04
+#define DISP_BITMASK_VRH3          DISP_BIT_IB03
+#define DISP_BITMASK_VRH2          DISP_BIT_IB02
+#define DISP_BITMASK_VRH1          DISP_BIT_IB01
+#define DISP_BITMASK_VRH0          DISP_BIT_IB00
+/* DISP_POWER_CTL_6_ADDR           Power Control (6) */
+#define DISP_BITMASK_VCOMG         DISP_BIT_IB13
+#define DISP_BITMASK_VDV4          DISP_BIT_IB12
+#define DISP_BITMASK_VDV3          DISP_BIT_IB11
+#define DISP_BITMASK_VDV2          DISP_BIT_IB10
+#define DISP_BITMASK_VDV1          DISP_BIT_IB09
+#define DISP_BITMASK_VDV0          DISP_BIT_IB08
+#define DISP_BITMASK_VCM4          DISP_BIT_IB04
+#define DISP_BITMASK_VCM3          DISP_BIT_IB03
+#define DISP_BITMASK_VCM2          DISP_BIT_IB02
+#define DISP_BITMASK_VCM1          DISP_BIT_IB01
+#define DISP_BITMASK_VCM0          DISP_BIT_IB00
+/* RAM Access */
+/* DISP_RAM_ADDR_SET_1_ADDR        RAM Address Set (1) */
+#define DISP_BITMASK_AD7           DISP_BIT_IB07
+#define DISP_BITMASK_AD6           DISP_BIT_IB06
+#define DISP_BITMASK_AD5           DISP_BIT_IB05
+#define DISP_BITMASK_AD4           DISP_BIT_IB04
+#define DISP_BITMASK_AD3           DISP_BIT_IB03
+#define DISP_BITMASK_AD2           DISP_BIT_IB02
+#define DISP_BITMASK_AD1           DISP_BIT_IB01
+#define DISP_BITMASK_AD0           DISP_BIT_IB00
+/* DISP_RAM_ADDR_SET_2_ADDR        RAM Address Set (2) */
+#define DISP_BITMASK_AD16          DISP_BIT_IB08
+#define DISP_BITMASK_AD15          DISP_BIT_IB07
+#define DISP_BITMASK_AD14          DISP_BIT_IB06
+#define DISP_BITMASK_AD13          DISP_BIT_IB05
+#define DISP_BITMASK_AD12          DISP_BIT_IB04
+#define DISP_BITMASK_AD11          DISP_BIT_IB03
+#define DISP_BITMASK_AD10          DISP_BIT_IB02
+#define DISP_BITMASK_AD9           DISP_BIT_IB01
+#define DISP_BITMASK_AD8           DISP_BIT_IB00
+/*
+ * DISP_CMD_RAMWR       RAM Data Read/Write
+ * Use Data Bit Configuration
+ */
+/* DISP_RAM_DATA_MASK_1_ADDR       RAM Write Data Mask (1) */
+#define DISP_BITMASK_WM11          DISP_BIT_IB13
+#define DISP_BITMASK_WM10          DISP_BIT_IB12
+#define DISP_BITMASK_WM9           DISP_BIT_IB11
+#define DISP_BITMASK_WM8           DISP_BIT_IB10
+#define DISP_BITMASK_WM7           DISP_BIT_IB09
+#define DISP_BITMASK_WM6           DISP_BIT_IB08
+#define DISP_BITMASK_WM5           DISP_BIT_IB05
+#define DISP_BITMASK_WM4           DISP_BIT_IB04
+#define DISP_BITMASK_WM3           DISP_BIT_IB03
+#define DISP_BITMASK_WM2           DISP_BIT_IB02
+#define DISP_BITMASK_WM1           DISP_BIT_IB01
+#define DISP_BITMASK_WM0           DISP_BIT_IB00
+/* DISP_RAM_DATA_MASK_2_ADDR       RAM Write Data Mask (2) */
+#define DISP_BITMASK_WM17          DISP_BIT_IB05
+#define DISP_BITMASK_WM16          DISP_BIT_IB04
+#define DISP_BITMASK_WM15          DISP_BIT_IB03
+#define DISP_BITMASK_WM14          DISP_BIT_IB02
+#define DISP_BITMASK_WM13          DISP_BIT_IB01
+#define DISP_BITMASK_WM12          DISP_BIT_IB00
+/*Gamma Control */
+/* DISP_GAMMA_CONTROL_1_ADDR       Gamma Control (1) */
+#define DISP_BITMASK_PKP12         DISP_BIT_IB10
+#define DISP_BITMASK_PKP11         DISP_BIT_IB08
+#define DISP_BITMASK_PKP10         DISP_BIT_IB09
+#define DISP_BITMASK_PKP02         DISP_BIT_IB02
+#define DISP_BITMASK_PKP01         DISP_BIT_IB01
+#define DISP_BITMASK_PKP00         DISP_BIT_IB00
+/* DISP_GAMMA_CONTROL_2_ADDR       Gamma Control (2) */
+#define DISP_BITMASK_PKP32         DISP_BIT_IB10
+#define DISP_BITMASK_PKP31         DISP_BIT_IB09
+#define DISP_BITMASK_PKP30         DISP_BIT_IB08
+#define DISP_BITMASK_PKP22         DISP_BIT_IB02
+#define DISP_BITMASK_PKP21         DISP_BIT_IB01
+#define DISP_BITMASK_PKP20         DISP_BIT_IB00
+/* DISP_GAMMA_CONTROL_3_ADDR       Gamma Control (3) */
+#define DISP_BITMASK_PKP52         DISP_BIT_IB10
+#define DISP_BITMASK_PKP51         DISP_BIT_IB09
+#define DISP_BITMASK_PKP50         DISP_BIT_IB08
+#define DISP_BITMASK_PKP42         DISP_BIT_IB02
+#define DISP_BITMASK_PKP41         DISP_BIT_IB01
+#define DISP_BITMASK_PKP40         DISP_BIT_IB00
+/* DISP_GAMMA_CONTROL_4_ADDR       Gamma Control (4) */
+#define DISP_BITMASK_PRP12         DISP_BIT_IB10
+#define DISP_BITMASK_PRP11         DISP_BIT_IB08
+#define DISP_BITMASK_PRP10         DISP_BIT_IB09
+#define DISP_BITMASK_PRP02         DISP_BIT_IB02
+#define DISP_BITMASK_PRP01         DISP_BIT_IB01
+#define DISP_BITMASK_PRP00         DISP_BIT_IB00
+/* DISP_GAMMA_CONTROL_5_ADDR       Gamma Control (5) */
+#define DISP_BITMASK_VRP14         DISP_BIT_IB12
+#define DISP_BITMASK_VRP13         DISP_BIT_IB11
+#define DISP_BITMASK_VRP12         DISP_BIT_IB10
+#define DISP_BITMASK_VRP11         DISP_BIT_IB08
+#define DISP_BITMASK_VRP10         DISP_BIT_IB09
+#define DISP_BITMASK_VRP03         DISP_BIT_IB03
+#define DISP_BITMASK_VRP02         DISP_BIT_IB02
+#define DISP_BITMASK_VRP01         DISP_BIT_IB01
+#define DISP_BITMASK_VRP00         DISP_BIT_IB00
+/* DISP_GAMMA_CONTROL_6_ADDR       Gamma Control (6) */
+#define DISP_BITMASK_PKN12         DISP_BIT_IB10
+#define DISP_BITMASK_PKN11         DISP_BIT_IB08
+#define DISP_BITMASK_PKN10         DISP_BIT_IB09
+#define DISP_BITMASK_PKN02         DISP_BIT_IB02
+#define DISP_BITMASK_PKN01         DISP_BIT_IB01
+#define DISP_BITMASK_PKN00         DISP_BIT_IB00
+/* DISP_GAMMA_CONTROL_7_ADDR       Gamma Control (7) */
+#define DISP_BITMASK_PKN32         DISP_BIT_IB10
+#define DISP_BITMASK_PKN31         DISP_BIT_IB08
+#define DISP_BITMASK_PKN30         DISP_BIT_IB09
+#define DISP_BITMASK_PKN22         DISP_BIT_IB02
+#define DISP_BITMASK_PKN21         DISP_BIT_IB01
+#define DISP_BITMASK_PKN20         DISP_BIT_IB00
+/* DISP_GAMMA_CONTROL_8_ADDR       Gamma Control (8) */
+#define DISP_BITMASK_PKN52         DISP_BIT_IB10
+#define DISP_BITMASK_PKN51         DISP_BIT_IB08
+#define DISP_BITMASK_PKN50         DISP_BIT_IB09
+#define DISP_BITMASK_PKN42         DISP_BIT_IB02
+#define DISP_BITMASK_PKN41         DISP_BIT_IB01
+#define DISP_BITMASK_PKN40         DISP_BIT_IB00
+/* DISP_GAMMA_CONTROL_9_ADDR       Gamma Control (9) */
+#define DISP_BITMASK_PRN12         DISP_BIT_IB10
+#define DISP_BITMASK_PRN11         DISP_BIT_IB08
+#define DISP_BITMASK_PRN10         DISP_BIT_IB09
+#define DISP_BITMASK_PRN02         DISP_BIT_IB02
+#define DISP_BITMASK_PRN01         DISP_BIT_IB01
+#define DISP_BITMASK_PRN00         DISP_BIT_IB00
+/* DISP_GAMMA_CONTROL_10_ADDR      Gamma Control (10) */
+#define DISP_BITMASK_VRN14         DISP_BIT_IB12
+#define DISP_BITMASK_VRN13         DISP_BIT_IB11
+#define DISP_BITMASK_VRN12         DISP_BIT_IB10
+#define DISP_BITMASK_VRN11         DISP_BIT_IB08
+#define DISP_BITMASK_VRN10         DISP_BIT_IB09
+#define DISP_BITMASK_VRN03         DISP_BIT_IB03
+#define DISP_BITMASK_VRN02         DISP_BIT_IB02
+#define DISP_BITMASK_VRN01         DISP_BIT_IB01
+#define DISP_BITMASK_VRN00         DISP_BIT_IB00
+/* Coordinate Control */
+/* DISP_VERT_SCROLL_CTL_1_ADDR     Vertical Scroll Control (1) */
+#define DISP_BITMASK_VL18          DISP_BIT_IB08
+#define DISP_BITMASK_VL17          DISP_BIT_IB07
+#define DISP_BITMASK_VL16          DISP_BIT_IB06
+#define DISP_BITMASK_VL15          DISP_BIT_IB05
+#define DISP_BITMASK_VL14          DISP_BIT_IB04
+#define DISP_BITMASK_VL13          DISP_BIT_IB03
+#define DISP_BITMASK_VL12          DISP_BIT_IB02
+#define DISP_BITMASK_VL11          DISP_BIT_IB01
+#define DISP_BITMASK_VL10          DISP_BIT_IB00
+/* DISP_VERT_SCROLL_CTL_2_ADDR     Vertical Scroll Control (2) */
+#define DISP_BITMASK_VL28          DISP_BIT_IB08
+#define DISP_BITMASK_VL27          DISP_BIT_IB07
+#define DISP_BITMASK_VL26          DISP_BIT_IB06
+#define DISP_BITMASK_VL25          DISP_BIT_IB05
+#define DISP_BITMASK_VL24          DISP_BIT_IB04
+#define DISP_BITMASK_VL23          DISP_BIT_IB03
+#define DISP_BITMASK_VL22          DISP_BIT_IB02
+#define DISP_BITMASK_VL21          DISP_BIT_IB01
+#define DISP_BITMASK_VL20          DISP_BIT_IB00
+/* DISP_SCREEN_1_DRV_POS_1_ADDR    First Screen Driving Position (1) */
+#define DISP_BITMASK_SS18          DISP_BIT_IB08
+#define DISP_BITMASK_SS17          DISP_BIT_IB07
+#define DISP_BITMASK_SS16          DISP_BIT_IB06
+#define DISP_BITMASK_SS15          DISP_BIT_IB05
+#define DISP_BITMASK_SS14          DISP_BIT_IB04
+#define DISP_BITMASK_SS13          DISP_BIT_IB03
+#define DISP_BITMASK_SS12          DISP_BIT_IB02
+#define DISP_BITMASK_SS11          DISP_BIT_IB01
+#define DISP_BITMASK_SS10          DISP_BIT_IB00
+/* DISP_SCREEN_1_DRV_POS_2_ADDR    First Screen Driving Position (2) */
+#define DISP_BITMASK_SE18          DISP_BIT_IB08
+#define DISP_BITMASK_SE17          DISP_BIT_IB07
+#define DISP_BITMASK_SE16          DISP_BIT_IB06
+#define DISP_BITMASK_SE15          DISP_BIT_IB05
+#define DISP_BITMASK_SE14          DISP_BIT_IB04
+#define DISP_BITMASK_SE13          DISP_BIT_IB03
+#define DISP_BITMASK_SE12          DISP_BIT_IB02
+#define DISP_BITMASK_SE11          DISP_BIT_IB01
+#define DISP_BITMASK_SE10          DISP_BIT_IB00
+/* DISP_SCREEN_2_DRV_POS_1_ADDR    Second Screen Driving Position (1) */
+#define DISP_BITMASK_SS28          DISP_BIT_IB08
+#define DISP_BITMASK_SS27          DISP_BIT_IB07
+#define DISP_BITMASK_SS26          DISP_BIT_IB06
+#define DISP_BITMASK_SS25          DISP_BIT_IB05
+#define DISP_BITMASK_SS24          DISP_BIT_IB04
+#define DISP_BITMASK_SS23          DISP_BIT_IB03
+#define DISP_BITMASK_SS22          DISP_BIT_IB02
+#define DISP_BITMASK_SS21          DISP_BIT_IB01
+#define DISP_BITMASK_SS20          DISP_BIT_IB00
+/* DISP_SCREEN_3_DRV_POS_2_ADDR    Second Screen Driving Position (2) */
+#define DISP_BITMASK_SE28          DISP_BIT_IB08
+#define DISP_BITMASK_SE27          DISP_BIT_IB07
+#define DISP_BITMASK_SE26          DISP_BIT_IB06
+#define DISP_BITMASK_SE25          DISP_BIT_IB05
+#define DISP_BITMASK_SE24          DISP_BIT_IB04
+#define DISP_BITMASK_SE23          DISP_BIT_IB03
+#define DISP_BITMASK_SE22          DISP_BIT_IB02
+#define DISP_BITMASK_SE21          DISP_BIT_IB01
+#define DISP_BITMASK_SE20          DISP_BIT_IB00
+/* DISP_HORZ_RAM_ADDR_POS_1_ADDR   Horizontal RAM Address Position (1) */
+#define DISP_BITMASK_HSA7          DISP_BIT_IB07
+#define DISP_BITMASK_HSA6          DISP_BIT_IB06
+#define DISP_BITMASK_HSA5          DISP_BIT_IB05
+#define DISP_BITMASK_HSA4          DISP_BIT_IB04
+#define DISP_BITMASK_HSA3          DISP_BIT_IB03
+#define DISP_BITMASK_HSA2          DISP_BIT_IB02
+#define DISP_BITMASK_HSA1          DISP_BIT_IB01
+#define DISP_BITMASK_HSA0          DISP_BIT_IB00
+/* DISP_HORZ_RAM_ADDR_POS_2_ADDR   Horizontal RAM Address Position (2) */
+#define DISP_BITMASK_HEA7          DISP_BIT_IB07
+#define DISP_BITMASK_HEA6          DISP_BIT_IB06
+#define DISP_BITMASK_HEA5          DISP_BIT_IB05
+#define DISP_BITMASK_HEA4          DISP_BIT_IB04
+#define DISP_BITMASK_HEA3          DISP_BIT_IB03
+#define DISP_BITMASK_HEA2          DISP_BIT_IB02
+#define DISP_BITMASK_HEA1          DISP_BIT_IB01
+#define DISP_BITMASK_HEA0          DISP_BIT_IB00
+/* DISP_VERT_RAM_ADDR_POS_1_ADDR   Vertical RAM Address Position (1) */
+#define DISP_BITMASK_VSA8          DISP_BIT_IB08
+#define DISP_BITMASK_VSA7          DISP_BIT_IB07
+#define DISP_BITMASK_VSA6          DISP_BIT_IB06
+#define DISP_BITMASK_VSA5          DISP_BIT_IB05
+#define DISP_BITMASK_VSA4          DISP_BIT_IB04
+#define DISP_BITMASK_VSA3          DISP_BIT_IB03
+#define DISP_BITMASK_VSA2          DISP_BIT_IB02
+#define DISP_BITMASK_VSA1          DISP_BIT_IB01
+#define DISP_BITMASK_VSA0          DISP_BIT_IB00
+/* DISP_VERT_RAM_ADDR_POS_2_ADDR   Vertical RAM Address Position (2) */
+#define DISP_BITMASK_VEA8          DISP_BIT_IB08
+#define DISP_BITMASK_VEA7          DISP_BIT_IB07
+#define DISP_BITMASK_VEA6          DISP_BIT_IB06
+#define DISP_BITMASK_VEA5          DISP_BIT_IB05
+#define DISP_BITMASK_VEA4          DISP_BIT_IB04
+#define DISP_BITMASK_VEA3          DISP_BIT_IB03
+#define DISP_BITMASK_VEA2          DISP_BIT_IB02
+#define DISP_BITMASK_VEA1          DISP_BIT_IB01
+#define DISP_BITMASK_VEA0          DISP_BIT_IB00
+static word disp_area_start_row;
+static word disp_area_end_row;
+static boolean disp_initialized = FALSE;
+/* For some reason the contrast set at init time is not good. Need to do
+* it again
+*/
+static boolean display_on = FALSE;
+
+static uint32 tmd20qvga_lcd_rev;
+uint16 tmd20qvga_panel_offset;
+
+#ifdef DISP_DEVICE_8BPP
+static word convert_8_to_16_tbl[256] = {
+	0x0000, 0x2000, 0x4000, 0x6000, 0x8000, 0xA000, 0xC000, 0xE000,
+	0x0100, 0x2100, 0x4100, 0x6100, 0x8100, 0xA100, 0xC100, 0xE100,
+	0x0200, 0x2200, 0x4200, 0x6200, 0x8200, 0xA200, 0xC200, 0xE200,
+	0x0300, 0x2300, 0x4300, 0x6300, 0x8300, 0xA300, 0xC300, 0xE300,
+	0x0400, 0x2400, 0x4400, 0x6400, 0x8400, 0xA400, 0xC400, 0xE400,
+	0x0500, 0x2500, 0x4500, 0x6500, 0x8500, 0xA500, 0xC500, 0xE500,
+	0x0600, 0x2600, 0x4600, 0x6600, 0x8600, 0xA600, 0xC600, 0xE600,
+	0x0700, 0x2700, 0x4700, 0x6700, 0x8700, 0xA700, 0xC700, 0xE700,
+	0x0008, 0x2008, 0x4008, 0x6008, 0x8008, 0xA008, 0xC008, 0xE008,
+	0x0108, 0x2108, 0x4108, 0x6108, 0x8108, 0xA108, 0xC108, 0xE108,
+	0x0208, 0x2208, 0x4208, 0x6208, 0x8208, 0xA208, 0xC208, 0xE208,
+	0x0308, 0x2308, 0x4308, 0x6308, 0x8308, 0xA308, 0xC308, 0xE308,
+	0x0408, 0x2408, 0x4408, 0x6408, 0x8408, 0xA408, 0xC408, 0xE408,
+	0x0508, 0x2508, 0x4508, 0x6508, 0x8508, 0xA508, 0xC508, 0xE508,
+	0x0608, 0x2608, 0x4608, 0x6608, 0x8608, 0xA608, 0xC608, 0xE608,
+	0x0708, 0x2708, 0x4708, 0x6708, 0x8708, 0xA708, 0xC708, 0xE708,
+	0x0010, 0x2010, 0x4010, 0x6010, 0x8010, 0xA010, 0xC010, 0xE010,
+	0x0110, 0x2110, 0x4110, 0x6110, 0x8110, 0xA110, 0xC110, 0xE110,
+	0x0210, 0x2210, 0x4210, 0x6210, 0x8210, 0xA210, 0xC210, 0xE210,
+	0x0310, 0x2310, 0x4310, 0x6310, 0x8310, 0xA310, 0xC310, 0xE310,
+	0x0410, 0x2410, 0x4410, 0x6410, 0x8410, 0xA410, 0xC410, 0xE410,
+	0x0510, 0x2510, 0x4510, 0x6510, 0x8510, 0xA510, 0xC510, 0xE510,
+	0x0610, 0x2610, 0x4610, 0x6610, 0x8610, 0xA610, 0xC610, 0xE610,
+	0x0710, 0x2710, 0x4710, 0x6710, 0x8710, 0xA710, 0xC710, 0xE710,
+	0x0018, 0x2018, 0x4018, 0x6018, 0x8018, 0xA018, 0xC018, 0xE018,
+	0x0118, 0x2118, 0x4118, 0x6118, 0x8118, 0xA118, 0xC118, 0xE118,
+	0x0218, 0x2218, 0x4218, 0x6218, 0x8218, 0xA218, 0xC218, 0xE218,
+	0x0318, 0x2318, 0x4318, 0x6318, 0x8318, 0xA318, 0xC318, 0xE318,
+	0x0418, 0x2418, 0x4418, 0x6418, 0x8418, 0xA418, 0xC418, 0xE418,
+	0x0518, 0x2518, 0x4518, 0x6518, 0x8518, 0xA518, 0xC518, 0xE518,
+	0x0618, 0x2618, 0x4618, 0x6618, 0x8618, 0xA618, 0xC618, 0xE618,
+	0x0718, 0x2718, 0x4718, 0x6718, 0x8718, 0xA718, 0xC718, 0xE718
+};
+#endif /* DISP_DEVICE_8BPP */
+
+static void tmd20qvga_disp_set_rect(int x, int y, int xres, int yres);
+static void tmd20qvga_disp_init(struct platform_device *pdev);
+static void tmd20qvga_disp_set_contrast(void);
+static void tmd20qvga_disp_set_display_area(word start_row, word end_row);
+static int tmd20qvga_disp_off(struct platform_device *pdev);
+static int tmd20qvga_disp_on(struct platform_device *pdev);
+static void tmd20qvga_set_revId(int);
+
+/* future use */
+void tmd20qvga_disp_clear_screen_area(word start_row, word end_row,
+				      word start_column, word end_column);
+
+static void tmd20qvga_set_revId(int id)
+{
+
+	tmd20qvga_lcd_rev = id;
+
+	if (tmd20qvga_lcd_rev == 1)
+		tmd20qvga_panel_offset = 0x10;
+	else
+		tmd20qvga_panel_offset = 0;
+}
+
+static void tmd20qvga_disp_init(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+
+	if (disp_initialized)
+		return;
+
+	mfd = platform_get_drvdata(pdev);
+
+	DISP_CMD_PORT = mfd->cmd_port;
+	DISP_DATA_PORT = mfd->data_port;
+
+#ifdef TMD20QVGA_LCD_18BPP
+	tmd20qvga_set_revId(2);
+#else
+	tmd20qvga_set_revId(1);
+#endif
+
+	disp_initialized = TRUE;
+	tmd20qvga_disp_set_contrast();
+	tmd20qvga_disp_set_display_area(0, QVGA_HEIGHT - 1);
+}
+
+static void tmd20qvga_disp_set_rect(int x, int y, int xres, int yres)
+{
+	if (!disp_initialized)
+		return;
+
+	DISP_SET_RECT(y, y + yres - 1, x, x + xres - 1);
+
+	DISP_CMD_OUT(DISP_CMD_RAMWR);
+}
+
+static void tmd20qvga_disp_set_display_area(word start_row, word end_row)
+{
+	word start_driving = start_row;
+	word end_driving = end_row;
+
+	if (!disp_initialized)
+		return;
+
+	/* Range checking
+	 */
+	if (end_driving >= QVGA_HEIGHT)
+		end_driving = QVGA_HEIGHT - 1;
+	if (start_driving > end_driving) {
+		/* Probably Backwards Switch */
+		start_driving = end_driving;
+		end_driving = start_row;	/* Has not changed */
+		if (end_driving >= QVGA_HEIGHT)
+			end_driving = QVGA_HEIGHT - 1;
+	}
+
+	if ((start_driving == disp_area_start_row)
+	    && (end_driving == disp_area_end_row))
+		return;
+
+	disp_area_start_row = start_driving;
+	disp_area_end_row = end_driving;
+
+	DISP_WRITE_OUT(DISP_SCREEN_1_DRV_POS_1_ADDR,
+		       DISP_VAL_IF(start_driving & 0x100,
+				   DISP_BITMASK_SS18) |
+		       DISP_VAL_IF(start_driving & 0x080,
+				   DISP_BITMASK_SS17) |
+		       DISP_VAL_IF(start_driving & 0x040,
+				   DISP_BITMASK_SS16) |
+		       DISP_VAL_IF(start_driving & 0x020,
+				   DISP_BITMASK_SS15) |
+		       DISP_VAL_IF(start_driving & 0x010,
+				   DISP_BITMASK_SS14) |
+		       DISP_VAL_IF(start_driving & 0x008,
+				   DISP_BITMASK_SS13) |
+		       DISP_VAL_IF(start_driving & 0x004,
+				   DISP_BITMASK_SS12) |
+		       DISP_VAL_IF(start_driving & 0x002,
+				   DISP_BITMASK_SS11) |
+		       DISP_VAL_IF(start_driving & 0x001, DISP_BITMASK_SS10));
+
+	DISP_WRITE_OUT(DISP_SCREEN_1_DRV_POS_2_ADDR,
+			DISP_VAL_IF(end_driving & 0x100, DISP_BITMASK_SE18) |
+			DISP_VAL_IF(end_driving & 0x080, DISP_BITMASK_SE17) |
+			DISP_VAL_IF(end_driving & 0x040, DISP_BITMASK_SE16) |
+			DISP_VAL_IF(end_driving & 0x020, DISP_BITMASK_SE15) |
+			DISP_VAL_IF(end_driving & 0x010, DISP_BITMASK_SE14) |
+			DISP_VAL_IF(end_driving & 0x008, DISP_BITMASK_SE13) |
+			DISP_VAL_IF(end_driving & 0x004, DISP_BITMASK_SE12) |
+			DISP_VAL_IF(end_driving & 0x002, DISP_BITMASK_SE11) |
+			DISP_VAL_IF(end_driving & 0x001, DISP_BITMASK_SE10));
+}
+
+static int tmd20qvga_disp_off(struct platform_device *pdev)
+{
+	if (!disp_initialized)
+		tmd20qvga_disp_init(pdev);
+
+	if (display_on) {
+		if (tmd20qvga_lcd_rev == 2) {
+			DISP_WRITE_OUT(DISP_POFF_LN_SETTING_ADDR, 0x000A);
+			DISP_WRITE_OUT(DISP_POWER_CTL_1_ADDR, 0xFFEE);
+			WAIT_MSEC(40);
+			DISP_WRITE_OUT(DISP_POWER_CTL_1_ADDR, 0xF812);
+			WAIT_MSEC(40);
+			DISP_WRITE_OUT(DISP_POWER_CTL_1_ADDR, 0xE811);
+			WAIT_MSEC(40);
+			DISP_WRITE_OUT(DISP_POWER_CTL_1_ADDR, 0xC011);
+			WAIT_MSEC(40);
+			DISP_WRITE_OUT(DISP_POWER_CTL_1_ADDR, 0x4011);
+			WAIT_MSEC(20);
+			DISP_WRITE_OUT(DISP_POWER_CTL_1_ADDR, 0x0010);
+
+		} else {
+			DISP_WRITE_OUT(DISP_POFF_LN_SETTING_ADDR, 0x000F);
+			DISP_WRITE_OUT(DISP_POWER_CTL_1_ADDR, 0x0BFE);
+			DISP_WRITE_OUT(DISP_POWER_SUPPLY_INTF_ADDR, 0x0100);
+			WAIT_MSEC(40);
+			DISP_WRITE_OUT(DISP_POWER_CTL_1_ADDR, 0x0BED);
+			DISP_WRITE_OUT(DISP_POWER_SUPPLY_INTF_ADDR, 0x0100);
+			WAIT_MSEC(40);
+			DISP_WRITE_OUT(DISP_POWER_CTL_1_ADDR, 0x00CD);
+			DISP_WRITE_OUT(DISP_POWER_SUPPLY_INTF_ADDR, 0x0100);
+			WAIT_MSEC(20);
+			DISP_WRITE_OUT(DISP_START_OSCILLATION_ADDR, 0x0);
+		}
+
+		DISP_WRITE_OUT(DISP_MODE_SETTING_ADDR, 0x0004);
+		DISP_WRITE_OUT(DISP_MODE_SETTING_ADDR, 0x0000);
+
+		display_on = FALSE;
+	}
+
+	return 0;
+}
+
+static int tmd20qvga_disp_on(struct platform_device *pdev)
+{
+	if (!disp_initialized)
+		tmd20qvga_disp_init(pdev);
+
+	if (!display_on) {
+		/* Deep Stand-by -> Stand-by */
+		DISP_CMD_OUT(DISP_START_OSCILLATION_ADDR);
+		WAIT_MSEC(1);
+		DISP_CMD_OUT(DISP_START_OSCILLATION_ADDR);
+		WAIT_MSEC(1);
+		DISP_CMD_OUT(DISP_START_OSCILLATION_ADDR);
+		WAIT_MSEC(1);
+
+		/* OFF -> Deep Stan-By -> Stand-by */
+		/* let's change the state from "Stand-by" to "Sleep" */
+		DISP_WRITE_OUT(DISP_MODE_SETTING_ADDR, 0x0005);
+		WAIT_MSEC(1);
+
+		/* Sleep -> Displaying */
+		DISP_WRITE_OUT(DISP_START_OSCILLATION_ADDR, 0x0001);
+		DISP_WRITE_OUT(DISP_DRIVER_OUTPUT_CTL_ADDR, 0x0127);
+		DISP_WRITE_OUT(DISP_LCD_DRIVING_SIG_ADDR, 0x200);
+		/* fast write mode */
+		DISP_WRITE_OUT(DISP_ENTRY_MODE_ADDR, 0x0130);
+		if (tmd20qvga_lcd_rev == 2)
+			DISP_WRITE_OUT(DISP_TMD_700_ADDR, 0x0003);
+		/* back porch = 14 + front porch = 2 --> 16 lines */
+		if (tmd20qvga_lcd_rev == 2) {
+#ifdef TMD20QVGA_LCD_18BPP
+			/* 256k color */
+			DISP_WRITE_OUT(DISP_DISPLAY_CTL_1_ADDR, 0x0000);
+#else
+			/* 65k color */
+			DISP_WRITE_OUT(DISP_DISPLAY_CTL_1_ADDR, 0x4000);
+#endif
+			DISP_WRITE_OUT(DISP_DISPLAY_CTL_2_ADDR, 0x0302);
+		} else {
+#ifdef TMD20QVGA_LCD_18BPP
+			/* 256k color */
+			DISP_WRITE_OUT(DISP_DISPLAY_CTL_1_ADDR, 0x0004);
+#else
+			/* 65k color */
+			DISP_WRITE_OUT(DISP_DISPLAY_CTL_1_ADDR, 0x4004);
+#endif
+			DISP_WRITE_OUT(DISP_DISPLAY_CTL_2_ADDR, 0x020E);
+		}
+		/* 16 bit one transfer */
+		if (tmd20qvga_lcd_rev == 2) {
+			DISP_WRITE_OUT(DISP_EXT_DISPLAY_CTL_1_ADDR, 0x0000);
+			DISP_WRITE_OUT(DISP_FRAME_CYCLE_CTL_ADDR, 0x0010);
+			DISP_WRITE_OUT(DISP_LTPS_CTL_1_ADDR, 0x0302);
+			DISP_WRITE_OUT(DISP_LTPS_CTL_2_ADDR, 0x0102);
+			DISP_WRITE_OUT(DISP_LTPS_CTL_3_ADDR, 0x0000);
+			DISP_WRITE_OUT(DISP_TMD_015_ADDR, 0x2000);
+
+			DISP_WRITE_OUT(DISP_AMP_SETTING_ADDR, 0x0000);
+			DISP_WRITE_OUT(DISP_GAMMA_CONTROL_1_ADDR, 0x0403);
+			DISP_WRITE_OUT(DISP_GAMMA_CONTROL_2_ADDR, 0x0304);
+			DISP_WRITE_OUT(DISP_GAMMA_CONTROL_3_ADDR, 0x0403);
+			DISP_WRITE_OUT(DISP_GAMMA_CONTROL_4_ADDR, 0x0303);
+			DISP_WRITE_OUT(DISP_GAMMA_CONTROL_5_ADDR, 0x0101);
+			DISP_WRITE_OUT(DISP_TMD_305_ADDR, 0);
+
+			DISP_WRITE_OUT(DISP_SCREEN_1_DRV_POS_1_ADDR, 0x0000);
+			DISP_WRITE_OUT(DISP_SCREEN_1_DRV_POS_2_ADDR, 0x013F);
+
+			DISP_WRITE_OUT(DISP_POWER_CTL_3_ADDR, 0x077D);
+
+			DISP_WRITE_OUT(DISP_POWER_CTL_4_ADDR, 0x0005);
+			DISP_WRITE_OUT(DISP_POWER_CTL_5_ADDR, 0x0000);
+			DISP_WRITE_OUT(DISP_POWER_CTL_6_ADDR, 0x0015);
+			DISP_WRITE_OUT(DISP_POWER_CTL_1_ADDR, 0xC010);
+			WAIT_MSEC(1);
+
+			DISP_WRITE_OUT(DISP_POWER_CTL_2_ADDR, 0x0001);
+			DISP_WRITE_OUT(DISP_POWER_CTL_1_ADDR, 0xFFFE);
+			WAIT_MSEC(60);
+		} else {
+			DISP_WRITE_OUT(DISP_EXT_DISPLAY_CTL_1_ADDR, 0x0001);
+			DISP_WRITE_OUT(DISP_FRAME_CYCLE_CTL_ADDR, 0x0010);
+			DISP_WRITE_OUT(DISP_LTPS_CTL_1_ADDR, 0x0301);
+			DISP_WRITE_OUT(DISP_LTPS_CTL_2_ADDR, 0x0001);
+			DISP_WRITE_OUT(DISP_LTPS_CTL_3_ADDR, 0x0000);
+			DISP_WRITE_OUT(DISP_AMP_SETTING_ADDR, 0x0000);
+			DISP_WRITE_OUT(DISP_GAMMA_CONTROL_1_ADDR, 0x0507);
+			DISP_WRITE_OUT(DISP_GAMMA_CONTROL_2_ADDR, 0x0405);
+			DISP_WRITE_OUT(DISP_GAMMA_CONTROL_3_ADDR, 0x0607);
+			DISP_WRITE_OUT(DISP_GAMMA_CONTROL_4_ADDR, 0x0502);
+			DISP_WRITE_OUT(DISP_GAMMA_CONTROL_5_ADDR, 0x0301);
+			DISP_WRITE_OUT(DISP_SCREEN_1_DRV_POS_1_ADDR, 0x0000);
+			DISP_WRITE_OUT(DISP_SCREEN_1_DRV_POS_2_ADDR, 0x013F);
+			DISP_WRITE_OUT(DISP_POWER_CTL_3_ADDR, 0x0795);
+
+			DISP_WRITE_OUT(DISP_POWER_SUPPLY_INTF_ADDR, 0x0102);
+			WAIT_MSEC(1);
+
+			DISP_WRITE_OUT(DISP_POWER_CTL_4_ADDR, 0x0450);
+			DISP_WRITE_OUT(DISP_POWER_SUPPLY_INTF_ADDR, 0x0103);
+			WAIT_MSEC(1);
+
+			DISP_WRITE_OUT(DISP_POWER_CTL_5_ADDR, 0x0008);
+			DISP_WRITE_OUT(DISP_POWER_SUPPLY_INTF_ADDR, 0x0104);
+			WAIT_MSEC(1);
+
+			DISP_WRITE_OUT(DISP_POWER_CTL_6_ADDR, 0x0C00);
+			DISP_WRITE_OUT(DISP_POWER_SUPPLY_INTF_ADDR, 0x0105);
+			WAIT_MSEC(1);
+
+			DISP_WRITE_OUT(DISP_POWER_CTL_7_ADDR, 0x0000);
+			DISP_WRITE_OUT(DISP_POWER_SUPPLY_INTF_ADDR, 0x0106);
+			WAIT_MSEC(1);
+
+			DISP_WRITE_OUT(DISP_POWER_CTL_1_ADDR, 0x0801);
+			DISP_WRITE_OUT(DISP_POWER_SUPPLY_INTF_ADDR, 0x0100);
+			WAIT_MSEC(1);
+
+			DISP_WRITE_OUT(DISP_POWER_CTL_2_ADDR, 0x001F);
+			DISP_WRITE_OUT(DISP_POWER_SUPPLY_INTF_ADDR, 0x0101);
+			WAIT_MSEC(60);
+
+			DISP_WRITE_OUT(DISP_POWER_CTL_2_ADDR, 0x009F);
+			DISP_WRITE_OUT(DISP_POWER_SUPPLY_INTF_ADDR, 0x0101);
+			WAIT_MSEC(10);
+
+			DISP_WRITE_OUT(DISP_HORZ_RAM_ADDR_POS_1_ADDR, 0x0010);
+			DISP_WRITE_OUT(DISP_HORZ_RAM_ADDR_POS_2_ADDR, 0x00FF);
+			DISP_WRITE_OUT(DISP_VERT_RAM_ADDR_POS_1_ADDR, 0x0000);
+			DISP_WRITE_OUT(DISP_VERT_RAM_ADDR_POS_2_ADDR, 0x013F);
+			/* RAM starts at address 0x10 */
+			DISP_WRITE_OUT(DISP_RAM_ADDR_SET_1_ADDR, 0x0010);
+			DISP_WRITE_OUT(DISP_RAM_ADDR_SET_2_ADDR, 0x0000);
+
+			/* lcd controller uses internal clock, not ext. vsync */
+			DISP_CMD_OUT(DISP_CMD_RAMWR);
+
+			DISP_WRITE_OUT(DISP_POWER_CTL_1_ADDR, 0x0881);
+			DISP_WRITE_OUT(DISP_POWER_SUPPLY_INTF_ADDR, 0x0100);
+			WAIT_MSEC(40);
+
+			DISP_WRITE_OUT(DISP_POWER_CTL_1_ADDR, 0x0BE1);
+			DISP_WRITE_OUT(DISP_POWER_SUPPLY_INTF_ADDR, 0x0100);
+			WAIT_MSEC(40);
+
+			DISP_WRITE_OUT(DISP_POWER_CTL_1_ADDR, 0x0BFF);
+			DISP_WRITE_OUT(DISP_POWER_SUPPLY_INTF_ADDR, 0x0100);
+		}
+		display_on = TRUE;
+	}
+
+	return 0;
+}
+
+static void tmd20qvga_disp_set_contrast(void)
+{
+#if (defined(TMD20QVGA_LCD_18BPP))
+
+	DISP_WRITE_OUT(DISP_GAMMA_CONTROL_1_ADDR, 0x0403);
+	DISP_WRITE_OUT(DISP_GAMMA_CONTROL_2_ADDR, 0x0302);
+	DISP_WRITE_OUT(DISP_GAMMA_CONTROL_3_ADDR, 0x0403);
+	DISP_WRITE_OUT(DISP_GAMMA_CONTROL_4_ADDR, 0x0303);
+	DISP_WRITE_OUT(DISP_GAMMA_CONTROL_5_ADDR, 0x0F07);
+
+#else
+	int newcontrast = 0x46;
+
+	DISP_WRITE_OUT(DISP_GAMMA_CONTROL_1_ADDR, 0x0403);
+
+	DISP_WRITE_OUT(DISP_GAMMA_CONTROL_2_ADDR,
+			DISP_VAL_IF(newcontrast & 0x0001, DISP_BITMASK_PKP20) |
+			DISP_VAL_IF(newcontrast & 0x0002, DISP_BITMASK_PKP21) |
+			DISP_VAL_IF(newcontrast & 0x0004, DISP_BITMASK_PKP22) |
+			DISP_VAL_IF(newcontrast & 0x0010, DISP_BITMASK_PKP30) |
+			DISP_VAL_IF(newcontrast & 0x0020, DISP_BITMASK_PKP31) |
+			DISP_VAL_IF(newcontrast & 0x0040, DISP_BITMASK_PKP32));
+
+	DISP_WRITE_OUT(DISP_GAMMA_CONTROL_3_ADDR,
+			DISP_VAL_IF(newcontrast & 0x0010, DISP_BITMASK_PKP40) |
+			DISP_VAL_IF(newcontrast & 0x0020, DISP_BITMASK_PKP41) |
+			DISP_VAL_IF(newcontrast & 0x0040, DISP_BITMASK_PKP42) |
+			DISP_VAL_IF(newcontrast & 0x0001, DISP_BITMASK_PKP50) |
+			DISP_VAL_IF(newcontrast & 0x0002, DISP_BITMASK_PKP51) |
+			DISP_VAL_IF(newcontrast & 0x0004, DISP_BITMASK_PKP52));
+
+	DISP_WRITE_OUT(DISP_GAMMA_CONTROL_4_ADDR, 0x0303);
+	DISP_WRITE_OUT(DISP_GAMMA_CONTROL_5_ADDR, 0x0F07);
+
+#endif /* defined(TMD20QVGA_LCD_18BPP) */
+
+}	/* End disp_set_contrast */
+
+void tmd20qvga_disp_clear_screen_area
+    (word start_row, word end_row, word start_column, word end_column) {
+	int32 i;
+
+	/* Clear the display screen */
+	DISP_SET_RECT(start_row, end_row, start_column, end_column);
+	DISP_CMD_OUT(DISP_CMD_RAMWR);
+	i = (end_row - start_row + 1) * (end_column - start_column + 1);
+	for (; i > 0; i--)
+		DISP_DATA_OUT_16TO18BPP(0x0);
+}
+
+static int __init tmd20qvga_probe(struct platform_device *pdev)
+{
+	msm_fb_add_device(pdev);
+
+	return 0;
+}
+
+static struct platform_driver this_driver = {
+	.probe  = tmd20qvga_probe,
+	.driver = {
+		.name   = "ebi2_tmd_qvga",
+	},
+};
+
+static struct msm_fb_panel_data tmd20qvga_panel_data = {
+	.on = tmd20qvga_disp_on,
+	.off = tmd20qvga_disp_off,
+	.set_rect = tmd20qvga_disp_set_rect,
+};
+
+static struct platform_device this_device = {
+	.name   = "ebi2_tmd_qvga",
+	.id	= 0,
+	.dev	= {
+		.platform_data = &tmd20qvga_panel_data,
+	}
+};
+
+static int __init tmd20qvga_init(void)
+{
+	int ret;
+	struct msm_panel_info *pinfo;
+
+	ret = platform_driver_register(&this_driver);
+	if (!ret) {
+		pinfo = &tmd20qvga_panel_data.panel_info;
+		pinfo->xres = 240;
+		pinfo->yres = 320;
+		MSM_FB_SINGLE_MODE_PANEL(pinfo);
+		pinfo->type = EBI2_PANEL;
+		pinfo->pdest = DISPLAY_1;
+		pinfo->wait_cycle = 0x808000;
+#ifdef TMD20QVGA_LCD_18BPP
+		pinfo->bpp = 18;
+#else
+		pinfo->bpp = 16;
+#endif
+		pinfo->fb_num = 2;
+		pinfo->lcd.vsync_enable = TRUE;
+		pinfo->lcd.refx100 = 6000;
+		pinfo->lcd.v_back_porch = 16;
+		pinfo->lcd.v_front_porch = 4;
+		pinfo->lcd.v_pulse_width = 0;
+		pinfo->lcd.hw_vsync_mode = FALSE;
+		pinfo->lcd.vsync_notifier_period = 0;
+
+		ret = platform_device_register(&this_device);
+		if (ret)
+			platform_driver_unregister(&this_driver);
+	}
+
+	return ret;
+}
+
+module_init(tmd20qvga_init);
+
diff --git a/drivers/video/msm/external_common.c b/drivers/video/msm/external_common.c
new file mode 100644
index 0000000..b6bf47d
--- /dev/null
+++ b/drivers/video/msm/external_common.c
@@ -0,0 +1,2103 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/mutex.h>
+
+/* #define DEBUG */
+#define DEV_DBG_PREFIX "EXT_COMMON: "
+
+/* The start of the data block collection within the CEA Extension Version 3 */
+#define DBC_START_OFFSET 4
+
+#include "msm_fb.h"
+#include "hdmi_msm.h"
+#include "external_common.h"
+#include "mhl_api.h"
+
+#include "mdp.h"
+
+struct external_common_state_type *external_common_state;
+EXPORT_SYMBOL(external_common_state);
+DEFINE_MUTEX(external_common_state_hpd_mutex);
+EXPORT_SYMBOL(external_common_state_hpd_mutex);
+
+
+static int atoi(const char *name)
+{
+	int val = 0;
+
+	for (;; name++) {
+		switch (*name) {
+		case '0' ... '9':
+			val = 10*val+(*name-'0');
+			break;
+		default:
+			return val;
+		}
+	}
+}
+
+#ifdef DEBUG_EDID
+/*
+ * Block 0 - 1920x1080p, 1360x768p
+ * Block 1 - 1280x720p, 1920x540i, 720x480p
+ */
+const char edid_blk0[0x100] = {
+0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x4C, 0x2D, 0x03, 0x05, 0x00,
+0x00, 0x00, 0x00, 0x30, 0x12, 0x01, 0x03, 0x80, 0x10, 0x09, 0x78, 0x0A, 0xEE,
+0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0x0F, 0x50, 0x54, 0xBD, 0xEF, 0x80, 0x71,
+0x4F, 0x81, 0x00, 0x81, 0x40, 0x81, 0x80, 0x95, 0x00, 0x95, 0x0F, 0xB3, 0x00,
+0xA9, 0x40, 0x02, 0x3A, 0x80, 0x18, 0x71, 0x38, 0x2D, 0x40, 0x58, 0x2C, 0x45,
+0x00, 0xA0, 0x5A, 0x00, 0x00, 0x00, 0x1E, 0x66, 0x21, 0x50, 0xB0, 0x51, 0x00,
+0x1B, 0x30, 0x40, 0x70, 0x36, 0x00, 0xA0, 0x5A, 0x00, 0x00, 0x00, 0x1E, 0x00,
+0x00, 0x00, 0xFD, 0x00, 0x18, 0x4B, 0x1A, 0x51, 0x17, 0x00, 0x0A, 0x20, 0x20,
+0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x53, 0x41, 0x4D, 0x53,
+0x55, 0x4E, 0x47, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0x8F};
+
+const char edid_blk1[0x100] = {
+0x02, 0x03, 0x1E, 0xF1, 0x46, 0x90, 0x04, 0x05, 0x03, 0x20, 0x22, 0x23, 0x09,
+0x07, 0x07, 0x83, 0x01, 0x00, 0x00, 0xE2, 0x00, 0x0F, 0x67, 0x03, 0x0C, 0x00,
+0x10, 0x00, 0xB8, 0x2D, 0x01, 0x1D, 0x00, 0x72, 0x51, 0xD0, 0x1E, 0x20, 0x6E,
+0x28, 0x55, 0x00, 0xA0, 0x5A, 0x00, 0x00, 0x00, 0x1E, 0x01, 0x1D, 0x80, 0x18,
+0x71, 0x1C, 0x16, 0x20, 0x58, 0x2C, 0x25, 0x00, 0xA0, 0x5A, 0x00, 0x00, 0x00,
+0x9E, 0x8C, 0x0A, 0xD0, 0x8A, 0x20, 0xE0, 0x2D, 0x10, 0x10, 0x3E, 0x96, 0x00,
+0xA0, 0x5A, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDF};
+#endif /* DEBUG_EDID */
+
+#define DMA_E_BASE 0xB0000
+void mdp_vid_quant_set(void)
+{
+	if ((external_common_state->video_resolution == \
+		HDMI_VFRMT_720x480p60_4_3) || \
+		(external_common_state->video_resolution == \
+		HDMI_VFRMT_720x480p60_16_9)) {
+		MDP_OUTP(MDP_BASE + DMA_E_BASE + 0x70, 0x00EB0010);
+		MDP_OUTP(MDP_BASE + DMA_E_BASE + 0x74, 0x00EB0010);
+		MDP_OUTP(MDP_BASE + DMA_E_BASE + 0x78, 0x00EB0010);
+	} else {
+		MDP_OUTP(MDP_BASE + DMA_E_BASE + 0x70, 0x00FF0000);
+		MDP_OUTP(MDP_BASE + DMA_E_BASE + 0x74, 0x00FF0000);
+		MDP_OUTP(MDP_BASE + DMA_E_BASE + 0x78, 0x00FF0000);
+	}
+}
+
+const char *video_format_2string(uint32 format)
+{
+	switch (format) {
+	default:
+#ifdef CONFIG_FB_MSM_HDMI_COMMON
+	case HDMI_VFRMT_640x480p60_4_3:    return " 640x 480 p60  4/3";
+	case HDMI_VFRMT_720x480p60_4_3:    return " 720x 480 p60  4/3";
+	case HDMI_VFRMT_720x480p60_16_9:   return " 720x 480 p60 16/9";
+	case HDMI_VFRMT_1280x720p60_16_9:  return "1280x 720 p60 16/9";
+	case HDMI_VFRMT_1920x1080i60_16_9: return "1920x1080 i60 16/9";
+	case HDMI_VFRMT_1440x480i60_4_3:   return "1440x 480 i60  4/3";
+	case HDMI_VFRMT_1440x480i60_16_9:  return "1440x 480 i60 16/9";
+	case HDMI_VFRMT_1440x240p60_4_3:   return "1440x 240 p60  4/3";
+	case HDMI_VFRMT_1440x240p60_16_9:  return "1440x 240 p60 16/9";
+	case HDMI_VFRMT_2880x480i60_4_3:   return "2880x 480 i60  4/3";
+	case HDMI_VFRMT_2880x480i60_16_9:  return "2880x 480 i60 16/9";
+	case HDMI_VFRMT_2880x240p60_4_3:   return "2880x 240 p60  4/3";
+	case HDMI_VFRMT_2880x240p60_16_9:  return "2880x 240 p60 16/9";
+	case HDMI_VFRMT_1440x480p60_4_3:   return "1440x 480 p60  4/3";
+	case HDMI_VFRMT_1440x480p60_16_9:  return "1440x 480 p60 16/9";
+	case HDMI_VFRMT_1920x1080p60_16_9: return "1920x1080 p60 16/9";
+	case HDMI_VFRMT_720x576p50_4_3:    return " 720x 576 p50  4/3";
+	case HDMI_VFRMT_720x576p50_16_9:   return " 720x 576 p50 16/9";
+	case HDMI_VFRMT_1280x720p50_16_9:  return "1280x 720 p50 16/9";
+	case HDMI_VFRMT_1920x1080i50_16_9: return "1920x1080 i50 16/9";
+	case HDMI_VFRMT_1440x576i50_4_3:   return "1440x 576 i50  4/3";
+	case HDMI_VFRMT_1440x576i50_16_9:  return "1440x 576 i50 16/9";
+	case HDMI_VFRMT_1440x288p50_4_3:   return "1440x 288 p50  4/3";
+	case HDMI_VFRMT_1440x288p50_16_9:  return "1440x 288 p50 16/9";
+	case HDMI_VFRMT_2880x576i50_4_3:   return "2880x 576 i50  4/3";
+	case HDMI_VFRMT_2880x576i50_16_9:  return "2880x 576 i50 16/9";
+	case HDMI_VFRMT_2880x288p50_4_3:   return "2880x 288 p50  4/3";
+	case HDMI_VFRMT_2880x288p50_16_9:  return "2880x 288 p50 16/9";
+	case HDMI_VFRMT_1440x576p50_4_3:   return "1440x 576 p50  4/3";
+	case HDMI_VFRMT_1440x576p50_16_9:  return "1440x 576 p50 16/9";
+	case HDMI_VFRMT_1920x1080p50_16_9: return "1920x1080 p50 16/9";
+	case HDMI_VFRMT_1920x1080p24_16_9: return "1920x1080 p24 16/9";
+	case HDMI_VFRMT_1920x1080p25_16_9: return "1920x1080 p25 16/9";
+	case HDMI_VFRMT_1920x1080p30_16_9: return "1920x1080 p30 16/9";
+	case HDMI_VFRMT_2880x480p60_4_3:   return "2880x 480 p60  4/3";
+	case HDMI_VFRMT_2880x480p60_16_9:  return "2880x 480 p60 16/9";
+	case HDMI_VFRMT_2880x576p50_4_3:   return "2880x 576 p50  4/3";
+	case HDMI_VFRMT_2880x576p50_16_9:  return "2880x 576 p50 16/9";
+	case HDMI_VFRMT_1920x1250i50_16_9: return "1920x1250 i50 16/9";
+	case HDMI_VFRMT_1920x1080i100_16_9:return "1920x1080 i100 16/9";
+	case HDMI_VFRMT_1280x720p100_16_9: return "1280x 720 p100 16/9";
+	case HDMI_VFRMT_720x576p100_4_3:   return " 720x 576 p100  4/3";
+	case HDMI_VFRMT_720x576p100_16_9:  return " 720x 576 p100 16/9";
+	case HDMI_VFRMT_1440x576i100_4_3:  return "1440x 576 i100  4/3";
+	case HDMI_VFRMT_1440x576i100_16_9: return "1440x 576 i100 16/9";
+	case HDMI_VFRMT_1920x1080i120_16_9:return "1920x1080 i120 16/9";
+	case HDMI_VFRMT_1280x720p120_16_9: return "1280x 720 p120 16/9";
+	case HDMI_VFRMT_720x480p120_4_3:   return " 720x 480 p120  4/3";
+	case HDMI_VFRMT_720x480p120_16_9:  return " 720x 480 p120 16/9";
+	case HDMI_VFRMT_1440x480i120_4_3:  return "1440x 480 i120  4/3";
+	case HDMI_VFRMT_1440x480i120_16_9: return "1440x 480 i120 16/9";
+	case HDMI_VFRMT_720x576p200_4_3:   return " 720x 576 p200  4/3";
+	case HDMI_VFRMT_720x576p200_16_9:  return " 720x 576 p200 16/9";
+	case HDMI_VFRMT_1440x576i200_4_3:  return "1440x 576 i200  4/3";
+	case HDMI_VFRMT_1440x576i200_16_9: return "1440x 576 i200 16/9";
+	case HDMI_VFRMT_720x480p240_4_3:   return " 720x 480 p240  4/3";
+	case HDMI_VFRMT_720x480p240_16_9:  return " 720x 480 p240 16/9";
+	case HDMI_VFRMT_1440x480i240_4_3:  return "1440x 480 i240  4/3";
+	case HDMI_VFRMT_1440x480i240_16_9: return "1440x 480 i240 16/9";
+#elif defined(CONFIG_FB_MSM_TVOUT)
+	case TVOUT_VFRMT_NTSC_M_720x480i:     return "NTSC_M_720x480i";
+	case TVOUT_VFRMT_NTSC_J_720x480i:     return "NTSC_J_720x480i";
+	case TVOUT_VFRMT_PAL_BDGHIN_720x576i: return "PAL_BDGHIN_720x576i";
+	case TVOUT_VFRMT_PAL_M_720x480i:      return "PAL_M_720x480i";
+	case TVOUT_VFRMT_PAL_N_720x480i:      return "PAL_N_720x480i";
+#endif
+
+	}
+}
+EXPORT_SYMBOL(video_format_2string);
+
+static ssize_t external_common_rda_video_mode_str(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	ssize_t ret = snprintf(buf, PAGE_SIZE, "%s\n",
+		video_format_2string(external_common_state->video_resolution));
+	DEV_DBG("%s: '%s'\n", __func__,
+		video_format_2string(external_common_state->video_resolution));
+	return ret;
+}
+
+#ifdef CONFIG_FB_MSM_HDMI_COMMON
+struct hdmi_disp_mode_timing_type
+	hdmi_common_supported_video_mode_lut[HDMI_VFRMT_MAX] = {
+	HDMI_SETTINGS_640x480p60_4_3,
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x480p60_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x480p60_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1280x720p60_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1080i60_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480i60_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480i60_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x240p60_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x240p60_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x480i60_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x480i60_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x240p60_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x240p60_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480p60_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480p60_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1080p60_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x576p50_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x576p50_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1280x720p50_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1080i50_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x576i50_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x576i50_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x288p50_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x288p50_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x576i50_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x576i50_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x288p50_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x288p50_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x576p50_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x576p50_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1080p50_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1080p24_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1080p25_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1080p30_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x480p60_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x480p60_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x576p50_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x576p50_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1250i50_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1080i100_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1280x720p100_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x576p100_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x576p100_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x576i100_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x576i100_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1080i120_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1280x720p120_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x480p120_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x480p120_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480i120_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480i120_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x576p200_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x576p200_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x576i200_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x576i200_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x480p240_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x480p240_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480i240_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480i240_16_9),
+};
+EXPORT_SYMBOL(hdmi_common_supported_video_mode_lut);
+
+struct hdmi_disp_mode_timing_type
+	hdmi_mhl_supported_video_mode_lut[HDMI_VFRMT_MAX] = {
+	HDMI_SETTINGS_640x480p60_4_3,
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x480p60_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x480p60_16_9),
+	HDMI_SETTINGS_1280x720p60_16_9,
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1080i60_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480i60_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480i60_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x240p60_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x240p60_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x480i60_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x480i60_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x240p60_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x240p60_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480p60_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480p60_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1080p60_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x576p50_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x576p50_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1280x720p50_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1080i50_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x576i50_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x576i50_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x288p50_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x288p50_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x576i50_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x576i50_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x288p50_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x288p50_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x576p50_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x576p50_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1080p50_16_9),
+	HDMI_SETTINGS_1920x1080p24_16_9,
+	HDMI_SETTINGS_1920x1080p25_16_9,
+	HDMI_SETTINGS_1920x1080p30_16_9,
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x480p60_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x480p60_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x576p50_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x576p50_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1250i50_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1080i100_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1280x720p100_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x576p100_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x576p100_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x576i100_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x576i100_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1080i120_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1280x720p120_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x480p120_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x480p120_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480i120_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480i120_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x576p200_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x576p200_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x576i200_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x576i200_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x480p240_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x480p240_16_9),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480i240_4_3),
+	VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480i240_16_9),
+};
+EXPORT_SYMBOL(hdmi_mhl_supported_video_mode_lut);
+
+static ssize_t hdmi_common_rda_edid_modes(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	ssize_t ret = 0;
+	int i;
+
+	buf[0] = 0;
+	if (external_common_state->disp_mode_list.num_of_elements) {
+		uint32 *video_mode = external_common_state->disp_mode_list
+			.disp_mode_list;
+		for (i = 0; i < external_common_state->disp_mode_list
+			.num_of_elements; ++i) {
+			if (ret > 0)
+				ret += snprintf(buf+ret, PAGE_SIZE-ret, ",%d",
+					*video_mode++ + 1);
+			else
+				ret += snprintf(buf+ret, PAGE_SIZE-ret, "%d",
+					*video_mode++ + 1);
+		}
+	} else
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "%d",
+			external_common_state->video_resolution+1);
+
+	DEV_DBG("%s: '%s'\n", __func__, buf);
+	ret += snprintf(buf+ret, PAGE_SIZE-ret, "\n");
+	return ret;
+}
+
+static ssize_t hdmi_common_rda_edid_physical_address(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	ssize_t ret = snprintf(buf, PAGE_SIZE, "%d\n",
+		external_common_state->physical_address);
+
+	DEV_DBG("%s: '%d'\n", __func__,
+			external_common_state->physical_address);
+	return ret;
+}
+
+
+static ssize_t hdmi_common_rda_edid_scan_info(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	ssize_t ret = snprintf(buf, PAGE_SIZE, "%d, %d, %d\n",
+		external_common_state->pt_scan_info,
+		external_common_state->it_scan_info,
+		external_common_state->ce_scan_info);
+	DEV_DBG("%s: '%s'\n", __func__, buf);
+	return ret;
+}
+
+static ssize_t hdmi_common_wta_vendor_name(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	uint8 *s = (uint8 *) buf;
+	uint8 *d = external_common_state->spd_vendor_name;
+	ssize_t ret = strnlen(buf, PAGE_SIZE);
+	ret = (ret > 8) ? 8 : ret;
+
+	memset(external_common_state->spd_vendor_name, 0, 8);
+	while (*s) {
+		if (*s & 0x60 && *s ^ 0x7f) {
+			*d = *s;
+		} else {
+			/* stop copying if control character found */
+			break;
+		}
+
+		if (++s > (uint8 *) (buf + ret))
+			break;
+
+		d++;
+	}
+
+	DEV_DBG("%s: '%s'\n", __func__,
+			external_common_state->spd_vendor_name);
+
+	return ret;
+}
+
+static ssize_t hdmi_common_rda_vendor_name(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	ssize_t ret = snprintf(buf, PAGE_SIZE, "%s\n",
+		external_common_state->spd_vendor_name);
+	DEV_DBG("%s: '%s'\n", __func__,
+			external_common_state->spd_vendor_name);
+
+	return ret;
+}
+
+static ssize_t hdmi_common_wta_product_description(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	uint8 *s = (uint8 *) buf;
+	uint8 *d = external_common_state->spd_product_description;
+	ssize_t ret = strnlen(buf, PAGE_SIZE);
+	ret = (ret > 16) ? 16 : ret;
+
+	memset(external_common_state->spd_product_description, 0, 16);
+	while (*s) {
+		if (*s & 0x60 && *s ^ 0x7f) {
+			*d = *s;
+		} else {
+			/* stop copying if control character found */
+			break;
+		}
+
+		if (++s > (uint8 *) (buf + ret))
+			break;
+
+		d++;
+	}
+
+	DEV_DBG("%s: '%s'\n", __func__,
+			external_common_state->spd_product_description);
+
+	return ret;
+}
+
+static ssize_t hdmi_common_rda_product_description(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	ssize_t ret = snprintf(buf, PAGE_SIZE, "%s\n",
+		external_common_state->spd_product_description);
+	DEV_DBG("%s: '%s'\n", __func__,
+			external_common_state->spd_product_description);
+
+	return ret;
+}
+
+static ssize_t hdmi_common_rda_edid_3d_modes(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	ssize_t ret = 0;
+	int i;
+	char buff_3d[128];
+
+	buf[0] = 0;
+	if (external_common_state->disp_mode_list.num_of_elements) {
+		uint32 *video_mode = external_common_state->disp_mode_list
+			.disp_mode_list;
+		uint32 *video_3d_mode = external_common_state->disp_mode_list
+			.disp_3d_mode_list;
+		for (i = 0; i < external_common_state->disp_mode_list
+			.num_of_elements; ++i) {
+			video_3d_format_2string(*video_3d_mode++, buff_3d);
+			if (ret > 0)
+				ret += snprintf(buf+ret, PAGE_SIZE-ret,
+					",%d=%s",
+					*video_mode++ + 1, buff_3d);
+			else
+				ret += snprintf(buf+ret, PAGE_SIZE-ret,
+					"%d=%s",
+					*video_mode++ + 1, buff_3d);
+		}
+	} else
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "%d",
+			external_common_state->video_resolution+1);
+
+	DEV_DBG("%s: '%s'\n", __func__, buf);
+	ret += snprintf(buf+ret, PAGE_SIZE-ret, "\n");
+	return ret;
+}
+
+static ssize_t hdmi_common_rda_hdcp(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	ssize_t ret = snprintf(buf, PAGE_SIZE, "%d\n",
+		external_common_state->hdcp_active);
+	DEV_DBG("%s: '%d'\n", __func__,
+		external_common_state->hdcp_active);
+	return ret;
+}
+
+static ssize_t hdmi_common_rda_hpd(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	ssize_t ret;
+	if (external_common_state->hpd_feature) {
+		ret = snprintf(buf, PAGE_SIZE, "%d\n",
+			external_common_state->hpd_feature_on);
+		DEV_DBG("%s: '%d'\n", __func__,
+			external_common_state->hpd_feature_on);
+	} else {
+		ret = snprintf(buf, PAGE_SIZE, "-1\n");
+		DEV_DBG("%s: 'not supported'\n", __func__);
+	}
+	return ret;
+}
+
+static ssize_t hdmi_common_wta_hpd(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	ssize_t ret = strnlen(buf, PAGE_SIZE);
+	int hpd;
+	if (hdmi_prim_display)
+		hpd = 1;
+	else
+		hpd = atoi(buf);
+
+	if (external_common_state->hpd_feature) {
+		if (hpd == 0 && external_common_state->hpd_feature_on) {
+			external_common_state->hpd_feature(0);
+			external_common_state->hpd_feature_on = 0;
+			DEV_DBG("%s: '%d'\n", __func__,
+				external_common_state->hpd_feature_on);
+		} else if (hpd == 1 && !external_common_state->hpd_feature_on) {
+			external_common_state->hpd_feature(1);
+			external_common_state->hpd_feature_on = 1;
+			DEV_DBG("%s: '%d'\n", __func__,
+				external_common_state->hpd_feature_on);
+		} else {
+			DEV_DBG("%s: '%d' (unchanged)\n", __func__,
+				external_common_state->hpd_feature_on);
+		}
+	} else {
+		DEV_DBG("%s: 'not supported'\n", __func__);
+	}
+
+	return ret;
+}
+
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
+/*
+ * This interface for CEC feature is defined to suit
+ * the current requirements. However, the actual functionality is
+ * added to accommodate different interfaces
+ */
+static ssize_t hdmi_msm_rda_cec(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	/* 0x028C CEC_CTRL */
+	ssize_t ret = snprintf(buf, PAGE_SIZE, "%d\n",
+		(HDMI_INP(0x028C) & BIT(0)));
+	return ret;
+}
+
+static ssize_t hdmi_msm_wta_cec(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	ssize_t ret = strnlen(buf, PAGE_SIZE);
+	int cec = atoi(buf);
+
+	if (cec != 0) {
+		mutex_lock(&hdmi_msm_state_mutex);
+		hdmi_msm_state->cec_enabled = true;
+		hdmi_msm_state->cec_logical_addr = 4;
+
+		/* flush CEC queue */
+		hdmi_msm_state->cec_queue_wr = hdmi_msm_state->cec_queue_start;
+		hdmi_msm_state->cec_queue_rd = hdmi_msm_state->cec_queue_start;
+		hdmi_msm_state->cec_queue_full = false;
+		memset(hdmi_msm_state->cec_queue_rd, 0,
+			sizeof(struct hdmi_msm_cec_msg)*CEC_QUEUE_SIZE);
+
+		mutex_unlock(&hdmi_msm_state_mutex);
+		hdmi_msm_cec_init();
+		hdmi_msm_cec_write_logical_addr(
+			hdmi_msm_state->cec_logical_addr);
+		DEV_DBG("CEC enabled\n");
+	} else {
+		mutex_lock(&hdmi_msm_state_mutex);
+		hdmi_msm_state->cec_enabled = false;
+		hdmi_msm_state->cec_logical_addr = 15;
+		mutex_unlock(&hdmi_msm_state_mutex);
+		hdmi_msm_cec_write_logical_addr(
+			hdmi_msm_state->cec_logical_addr);
+		/* 0x028C CEC_CTRL */
+		HDMI_OUTP(0x028C, 0);
+		DEV_DBG("CEC disabled\n");
+	}
+	return ret;
+}
+
+static ssize_t hdmi_msm_rda_cec_logical_addr(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	ssize_t ret;
+
+	mutex_lock(&hdmi_msm_state_mutex);
+	ret = snprintf(buf, PAGE_SIZE, "%d\n",
+		hdmi_msm_state->cec_logical_addr);
+	mutex_unlock(&hdmi_msm_state_mutex);
+	return ret;
+}
+
+static ssize_t hdmi_msm_wta_cec_logical_addr(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+
+#ifdef DRVR_ONLY_CECT_NO_DAEMON
+	/*
+	 * Only for testing
+	 */
+	hdmi_msm_cec_one_touch_play();
+	return 0;
+#else
+	ssize_t ret = strnlen(buf, PAGE_SIZE);
+	int logical_addr = atoi(buf);
+
+	if (logical_addr < 0 || logical_addr > 15)
+		return -EINVAL;
+
+	mutex_lock(&hdmi_msm_state_mutex);
+	hdmi_msm_state->cec_logical_addr = logical_addr;
+	mutex_unlock(&hdmi_msm_state_mutex);
+
+	hdmi_msm_cec_write_logical_addr(logical_addr);
+
+	return ret;
+#endif
+}
+
+static ssize_t hdmi_msm_rda_cec_frame(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	mutex_lock(&hdmi_msm_state_mutex);
+	if (hdmi_msm_state->cec_queue_rd == hdmi_msm_state->cec_queue_wr
+	    && !hdmi_msm_state->cec_queue_full) {
+		mutex_unlock(&hdmi_msm_state_mutex);
+		DEV_ERR("CEC message queue is empty\n");
+		return -EBUSY;
+	}
+	memcpy(buf, hdmi_msm_state->cec_queue_rd++,
+		sizeof(struct hdmi_msm_cec_msg));
+	hdmi_msm_state->cec_queue_full = false;
+	if (hdmi_msm_state->cec_queue_rd == CEC_QUEUE_END)
+		hdmi_msm_state->cec_queue_rd = hdmi_msm_state->cec_queue_start;
+	mutex_unlock(&hdmi_msm_state_mutex);
+
+	return sizeof(struct hdmi_msm_cec_msg);
+}
+
+static ssize_t hdmi_msm_wta_cec_frame(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	int i;
+	int retry = ((struct hdmi_msm_cec_msg *) buf)->retransmit;
+
+	for (i = 0; i < RETRANSMIT_MAX_NUM; i++) {
+		hdmi_msm_cec_msg_send((struct hdmi_msm_cec_msg *) buf);
+		if (hdmi_msm_state->cec_frame_wr_status
+		    & CEC_STATUS_WR_ERROR && retry--) {
+			mutex_lock(&hdmi_msm_state_mutex);
+			if (hdmi_msm_state->fsm_reset_done)
+				retry++;
+			mutex_unlock(&hdmi_msm_state_mutex);
+			msleep(20);
+		} else
+			break;
+	}
+
+	if (hdmi_msm_state->cec_frame_wr_status & CEC_STATUS_WR_DONE)
+		return sizeof(struct hdmi_msm_cec_msg);
+	else
+		return -EINVAL;
+}
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT */
+
+static ssize_t hdmi_common_rda_3d_present(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	ssize_t ret = snprintf(buf, PAGE_SIZE, "%d\n",
+		external_common_state->present_3d);
+	DEV_DBG("%s: '%d'\n", __func__,
+			external_common_state->present_3d);
+	return ret;
+}
+
+static ssize_t hdmi_common_rda_hdcp_present(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	ssize_t ret = snprintf(buf, PAGE_SIZE, "%d\n",
+		external_common_state->present_hdcp);
+	DEV_DBG("%s: '%d'\n", __func__,
+			external_common_state->present_hdcp);
+	return ret;
+}
+#endif
+
+#ifdef CONFIG_FB_MSM_HDMI_3D
+static ssize_t hdmi_3d_rda_format_3d(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	ssize_t ret = snprintf(buf, PAGE_SIZE, "%d\n",
+		external_common_state->format_3d);
+	DEV_DBG("%s: '%d'\n", __func__,
+		external_common_state->format_3d);
+	return ret;
+}
+
+static ssize_t hdmi_3d_wta_format_3d(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	ssize_t ret = strnlen(buf, PAGE_SIZE);
+	int format_3d = atoi(buf);
+
+	if (format_3d >= 0 && format_3d <= 2) {
+		if (format_3d != external_common_state->format_3d) {
+			external_common_state->format_3d = format_3d;
+			if (external_common_state->switch_3d)
+				external_common_state->switch_3d(format_3d);
+			DEV_DBG("%s: '%d'\n", __func__,
+				external_common_state->format_3d);
+		} else {
+			DEV_DBG("%s: '%d' (unchanged)\n", __func__,
+				external_common_state->format_3d);
+		}
+	} else {
+		DEV_DBG("%s: '%d' (unknown)\n", __func__, format_3d);
+	}
+
+	return ret;
+}
+#endif
+
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
+static DEVICE_ATTR(cec, S_IRUGO | S_IWUSR,
+	hdmi_msm_rda_cec,
+	hdmi_msm_wta_cec);
+
+static DEVICE_ATTR(cec_logical_addr, S_IRUGO | S_IWUSR,
+	hdmi_msm_rda_cec_logical_addr,
+	hdmi_msm_wta_cec_logical_addr);
+
+static DEVICE_ATTR(cec_rd_frame, S_IRUGO,
+	hdmi_msm_rda_cec_frame,	NULL);
+
+static DEVICE_ATTR(cec_wr_frame, S_IWUSR,
+	NULL, hdmi_msm_wta_cec_frame);
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT */
+
+
+static ssize_t external_common_rda_video_mode(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	ssize_t ret = snprintf(buf, PAGE_SIZE, "%d\n",
+		external_common_state->video_resolution+1);
+	DEV_DBG("%s: '%d'\n", __func__,
+			external_common_state->video_resolution+1);
+	return ret;
+}
+
+static ssize_t external_common_wta_video_mode(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	ssize_t ret = strnlen(buf, PAGE_SIZE);
+	uint32 video_mode;
+#ifdef CONFIG_FB_MSM_HDMI_COMMON
+	const struct hdmi_disp_mode_timing_type *disp_mode;
+#endif
+	mutex_lock(&external_common_state_hpd_mutex);
+	if (!external_common_state->hpd_state) {
+		mutex_unlock(&external_common_state_hpd_mutex);
+		DEV_INFO("%s: FAILED: display off or cable disconnected\n",
+			__func__);
+		return ret;
+	}
+	mutex_unlock(&external_common_state_hpd_mutex);
+
+	video_mode = atoi(buf)-1;
+	DEV_INFO("%s: video_mode is %d\n", __func__, video_mode);
+	kobject_uevent(external_common_state->uevent_kobj, KOBJ_OFFLINE);
+#ifdef CONFIG_FB_MSM_HDMI_COMMON
+	disp_mode = hdmi_common_get_supported_mode(video_mode);
+	if (!disp_mode) {
+		DEV_INFO("%s: FAILED: mode not supported (%d)\n",
+			__func__, video_mode);
+		return ret;
+	}
+	external_common_state->disp_mode_list.num_of_elements = 1;
+	external_common_state->disp_mode_list.disp_mode_list[0] = video_mode;
+#elif defined(CONFIG_FB_MSM_TVOUT)
+	external_common_state->video_resolution = video_mode;
+#endif
+	DEV_DBG("%s: 'mode=%d %s' successful (sending OFF/ONLINE)\n", __func__,
+		video_mode, video_format_2string(video_mode));
+	kobject_uevent(external_common_state->uevent_kobj, KOBJ_ONLINE);
+	return ret;
+}
+
+static ssize_t external_common_rda_connected(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	ssize_t ret;
+	mutex_lock(&external_common_state_hpd_mutex);
+	ret = snprintf(buf, PAGE_SIZE, "%d\n",
+		external_common_state->hpd_state);
+	DEV_DBG("%s: '%d'\n", __func__,
+		external_common_state->hpd_state);
+	mutex_unlock(&external_common_state_hpd_mutex);
+	return ret;
+}
+
+static ssize_t external_common_rda_hdmi_mode(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	ssize_t ret;
+
+	ret = snprintf(buf, PAGE_SIZE, "%d\n",
+		external_common_state->hdmi_sink);
+
+	DEV_DBG("%s: '%d'\n", __func__,
+		external_common_state->hdmi_sink);
+
+	return ret;
+}
+
+static ssize_t hdmi_common_rda_hdmi_primary(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	ssize_t ret = snprintf(buf, PAGE_SIZE, "%d\n",
+		hdmi_prim_display);
+	DEV_DBG("%s: '%d'\n", __func__,	hdmi_prim_display);
+	return ret;
+}
+
+static DEVICE_ATTR(video_mode, S_IRUGO | S_IWUGO,
+	external_common_rda_video_mode, external_common_wta_video_mode);
+static DEVICE_ATTR(video_mode_str, S_IRUGO, external_common_rda_video_mode_str,
+	NULL);
+static DEVICE_ATTR(connected, S_IRUGO, external_common_rda_connected, NULL);
+static DEVICE_ATTR(hdmi_mode, S_IRUGO, external_common_rda_hdmi_mode, NULL);
+#ifdef CONFIG_FB_MSM_HDMI_COMMON
+static DEVICE_ATTR(edid_modes, S_IRUGO, hdmi_common_rda_edid_modes, NULL);
+static DEVICE_ATTR(hpd, S_IRUGO | S_IWUGO, hdmi_common_rda_hpd,
+	hdmi_common_wta_hpd);
+static DEVICE_ATTR(hdcp, S_IRUGO, hdmi_common_rda_hdcp, NULL);
+static DEVICE_ATTR(pa, S_IRUGO,
+	hdmi_common_rda_edid_physical_address, NULL);
+static DEVICE_ATTR(scan_info, S_IRUGO,
+	hdmi_common_rda_edid_scan_info, NULL);
+static DEVICE_ATTR(vendor_name, S_IRUGO | S_IWUSR, hdmi_common_rda_vendor_name,
+	hdmi_common_wta_vendor_name);
+static DEVICE_ATTR(product_description, S_IRUGO | S_IWUSR,
+	hdmi_common_rda_product_description,
+	hdmi_common_wta_product_description);
+static DEVICE_ATTR(edid_3d_modes, S_IRUGO,
+	hdmi_common_rda_edid_3d_modes, NULL);
+static DEVICE_ATTR(3d_present, S_IRUGO, hdmi_common_rda_3d_present, NULL);
+static DEVICE_ATTR(hdcp_present, S_IRUGO, hdmi_common_rda_hdcp_present, NULL);
+#endif
+#ifdef CONFIG_FB_MSM_HDMI_3D
+static DEVICE_ATTR(format_3d, S_IRUGO | S_IWUGO, hdmi_3d_rda_format_3d,
+	hdmi_3d_wta_format_3d);
+#endif
+static DEVICE_ATTR(hdmi_primary, S_IRUGO, hdmi_common_rda_hdmi_primary, NULL);
+
+static struct attribute *external_common_fs_attrs[] = {
+	&dev_attr_video_mode.attr,
+	&dev_attr_video_mode_str.attr,
+	&dev_attr_connected.attr,
+	&dev_attr_hdmi_mode.attr,
+#ifdef CONFIG_FB_MSM_HDMI_COMMON
+	&dev_attr_edid_modes.attr,
+	&dev_attr_hdcp.attr,
+	&dev_attr_hpd.attr,
+	&dev_attr_pa.attr,
+	&dev_attr_scan_info.attr,
+	&dev_attr_vendor_name.attr,
+	&dev_attr_product_description.attr,
+	&dev_attr_edid_3d_modes.attr,
+	&dev_attr_3d_present.attr,
+	&dev_attr_hdcp_present.attr,
+#endif
+#ifdef CONFIG_FB_MSM_HDMI_3D
+	&dev_attr_format_3d.attr,
+#endif
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
+	&dev_attr_cec.attr,
+	&dev_attr_cec_logical_addr.attr,
+	&dev_attr_cec_rd_frame.attr,
+	&dev_attr_cec_wr_frame.attr,
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT */
+	&dev_attr_hdmi_primary.attr,
+	NULL,
+};
+static struct attribute_group external_common_fs_attr_group = {
+	.attrs = external_common_fs_attrs,
+};
+
+/* create external interface kobject and initialize */
+int external_common_state_create(struct platform_device *pdev)
+{
+	int rc;
+	struct msm_fb_data_type *mfd = platform_get_drvdata(pdev);
+	if (!mfd) {
+		DEV_ERR("%s: mfd not found\n", __func__);
+		return -ENODEV;
+	}
+	if (!mfd->fbi) {
+		DEV_ERR("%s: mfd->fbi not found\n", __func__);
+		return -ENODEV;
+	}
+	if (!mfd->fbi->dev) {
+		DEV_ERR("%s: mfd->fbi->dev not found\n", __func__);
+		return -ENODEV;
+	}
+	rc = sysfs_create_group(&mfd->fbi->dev->kobj,
+		&external_common_fs_attr_group);
+	if (rc) {
+		DEV_ERR("%s: sysfs group creation failed, rc=%d\n", __func__,
+			rc);
+		return rc;
+	}
+	external_common_state->uevent_kobj = &mfd->fbi->dev->kobj;
+	DEV_ERR("%s: sysfs group %p\n", __func__,
+		external_common_state->uevent_kobj);
+
+	kobject_uevent(external_common_state->uevent_kobj, KOBJ_ADD);
+	DEV_DBG("%s: kobject_uevent(KOBJ_ADD)\n", __func__);
+	return 0;
+}
+EXPORT_SYMBOL(external_common_state_create);
+
+void external_common_state_remove(void)
+{
+	if (external_common_state->uevent_kobj)
+		sysfs_remove_group(external_common_state->uevent_kobj,
+			&external_common_fs_attr_group);
+	external_common_state->uevent_kobj = NULL;
+}
+EXPORT_SYMBOL(external_common_state_remove);
+
+#ifdef CONFIG_FB_MSM_HDMI_COMMON
+/* The Logic ID for HDMI TX Core. Currently only support 1 HDMI TX Core. */
+struct hdmi_edid_video_mode_property_type {
+	uint32	video_code;
+	uint32	active_h;
+	uint32	active_v;
+	boolean	interlaced;
+	uint32	total_h;
+	uint32	total_blank_h;
+	uint32	total_v;
+	uint32	total_blank_v;
+	/* Must divide by 1000 to get the frequency */
+	uint32	freq_h;
+	/* Must divide by 1000 to get the frequency */
+	uint32	freq_v;
+	/* Must divide by 1000 to get the frequency */
+	uint32	pixel_freq;
+	/* Must divide by 1000 to get the frequency */
+	uint32	refresh_rate;
+	boolean	aspect_ratio_4_3;
+};
+
+/* LUT is sorted from lowest Active H to highest Active H - ease searching */
+static struct hdmi_edid_video_mode_property_type
+	hdmi_edid_disp_mode_lut[] = {
+
+	/* All 640 H Active */
+	{HDMI_VFRMT_640x480p60_4_3, 640, 480, FALSE, 800, 160, 525, 45,
+	 31465, 59940, 25175, 59940, TRUE},
+	{HDMI_VFRMT_640x480p60_4_3, 640, 480, FALSE, 800, 160, 525, 45,
+	 31500, 60000, 25200, 60000, TRUE},
+
+	/* All 720 H Active */
+	{HDMI_VFRMT_720x576p50_4_3,  720, 576, FALSE, 864, 144, 625, 49,
+	 31250, 50000, 27000, 50000, TRUE},
+	{HDMI_VFRMT_720x480p60_4_3,  720, 480, FALSE, 858, 138, 525, 45,
+	 31465, 59940, 27000, 59940, TRUE},
+	{HDMI_VFRMT_720x480p60_4_3,  720, 480, FALSE, 858, 138, 525, 45,
+	 31500, 60000, 27030, 60000, TRUE},
+	{HDMI_VFRMT_720x576p100_4_3, 720, 576, FALSE, 864, 144, 625, 49,
+	 62500, 100000, 54000, 100000, TRUE},
+	{HDMI_VFRMT_720x480p120_4_3, 720, 480, FALSE, 858, 138, 525, 45,
+	 62937, 119880, 54000, 119880, TRUE},
+	{HDMI_VFRMT_720x480p120_4_3, 720, 480, FALSE, 858, 138, 525, 45,
+	 63000, 120000, 54054, 120000, TRUE},
+	{HDMI_VFRMT_720x576p200_4_3, 720, 576, FALSE, 864, 144, 625, 49,
+	 125000, 200000, 108000, 200000, TRUE},
+	{HDMI_VFRMT_720x480p240_4_3, 720, 480, FALSE, 858, 138, 525, 45,
+	 125874, 239760, 108000, 239000, TRUE},
+	{HDMI_VFRMT_720x480p240_4_3, 720, 480, FALSE, 858, 138, 525, 45,
+	 126000, 240000, 108108, 240000, TRUE},
+
+	/* All 1280 H Active */
+	{HDMI_VFRMT_1280x720p50_16_9,  1280, 720, FALSE, 1980, 700, 750, 30,
+	 37500, 50000, 74250, 50000, FALSE},
+	{HDMI_VFRMT_1280x720p60_16_9,  1280, 720, FALSE, 1650, 370, 750, 30,
+	 44955, 59940, 74176, 59940, FALSE},
+	{HDMI_VFRMT_1280x720p60_16_9,  1280, 720, FALSE, 1650, 370, 750, 30,
+	 45000, 60000, 74250, 60000, FALSE},
+	{HDMI_VFRMT_1280x720p100_16_9, 1280, 720, FALSE, 1980, 700, 750, 30,
+	 75000, 100000, 148500, 100000, FALSE},
+	{HDMI_VFRMT_1280x720p120_16_9, 1280, 720, FALSE, 1650, 370, 750, 30,
+	 89909, 119880, 148352, 119880, FALSE},
+	{HDMI_VFRMT_1280x720p120_16_9, 1280, 720, FALSE, 1650, 370, 750, 30,
+	 90000, 120000, 148500, 120000, FALSE},
+
+	/* All 1440 H Active */
+	{HDMI_VFRMT_1440x576i50_4_3, 1440, 576, TRUE,  1728, 288, 625, 24,
+	 15625, 50000, 27000, 50000, TRUE},
+	{HDMI_VFRMT_720x288p50_4_3,  1440, 288, FALSE, 1728, 288, 312, 24,
+	 15625, 50080, 27000, 50000, TRUE},
+	{HDMI_VFRMT_720x288p50_4_3,  1440, 288, FALSE, 1728, 288, 313, 25,
+	 15625, 49920, 27000, 50000, TRUE},
+	{HDMI_VFRMT_720x288p50_4_3,  1440, 288, FALSE, 1728, 288, 314, 26,
+	 15625, 49761, 27000, 50000, TRUE},
+	{HDMI_VFRMT_1440x576p50_4_3, 1440, 576, FALSE, 1728, 288, 625, 49,
+	 31250, 50000, 54000, 50000, TRUE},
+	{HDMI_VFRMT_1440x480i60_4_3, 1440, 480, TRUE,  1716, 276, 525, 22,
+	 15734, 59940, 27000, 59940, TRUE},
+	{HDMI_VFRMT_1440x240p60_4_3, 1440, 240, FALSE, 1716, 276, 262, 22,
+	 15734, 60054, 27000, 59940, TRUE},
+	{HDMI_VFRMT_1440x240p60_4_3, 1440, 240, FALSE, 1716, 276, 263, 23,
+	 15734, 59826, 27000, 59940, TRUE},
+	{HDMI_VFRMT_1440x480p60_4_3, 1440, 480, FALSE, 1716, 276, 525, 45,
+	 31469, 59940, 54000, 59940, TRUE},
+	{HDMI_VFRMT_1440x480i60_4_3, 1440, 480, TRUE,  1716, 276, 525, 22,
+	 15750, 60000, 27027, 60000, TRUE},
+	{HDMI_VFRMT_1440x240p60_4_3, 1440, 240, FALSE, 1716, 276, 262, 22,
+	 15750, 60115, 27027, 60000, TRUE},
+	{HDMI_VFRMT_1440x240p60_4_3, 1440, 240, FALSE, 1716, 276, 263, 23,
+	 15750, 59886, 27027, 60000, TRUE},
+	{HDMI_VFRMT_1440x480p60_4_3, 1440, 480, FALSE, 1716, 276, 525, 45,
+	 31500, 60000, 54054, 60000, TRUE},
+	{HDMI_VFRMT_1440x576i100_4_3, 1440, 576, TRUE,  1728, 288, 625, 24,
+	 31250, 100000, 54000, 100000, TRUE},
+	{HDMI_VFRMT_1440x480i120_4_3, 1440, 480, TRUE,  1716, 276, 525, 22,
+	 31469, 119880, 54000, 119880, TRUE},
+	{HDMI_VFRMT_1440x480i120_4_3, 1440, 480, TRUE,  1716, 276, 525, 22,
+	 31500, 120000, 54054, 120000, TRUE},
+	{HDMI_VFRMT_1440x576i200_4_3, 1440, 576, TRUE,  1728, 288, 625, 24,
+	 62500, 200000, 108000, 200000, TRUE},
+	{HDMI_VFRMT_1440x480i240_4_3, 1440, 480, TRUE,  1716, 276, 525, 22,
+	 62937, 239760, 108000, 239000, TRUE},
+	{HDMI_VFRMT_1440x480i240_4_3, 1440, 480, TRUE,  1716, 276, 525, 22,
+	 63000, 240000, 108108, 240000, TRUE},
+
+	/* All 1920 H Active */
+	{HDMI_VFRMT_1920x1080p60_16_9, 1920, 1080, FALSE, 2200, 280, 1125,
+	 45, 67433, 59940, 148352, 59940, FALSE},
+	{HDMI_VFRMT_1920x1080p60_16_9, 1920, 1080, TRUE,  2200, 280, 1125,
+	 45, 67500, 60000, 148500, 60000, FALSE},
+	{HDMI_VFRMT_1920x1080p50_16_9, 1920, 1080, FALSE, 2640, 720, 1125,
+	 45, 56250, 50000, 148500, 50000, FALSE},
+	{HDMI_VFRMT_1920x1080p24_16_9, 1920, 1080, FALSE, 2750, 830, 1125,
+	 45, 26973, 23976, 74176, 24000, FALSE},
+	{HDMI_VFRMT_1920x1080p24_16_9, 1920, 1080, FALSE, 2750, 830, 1125,
+	 45, 27000, 24000, 74250, 24000, FALSE},
+	{HDMI_VFRMT_1920x1080p25_16_9, 1920, 1080, FALSE, 2640, 720, 1125,
+	 45, 28125, 25000, 74250, 25000, FALSE},
+	{HDMI_VFRMT_1920x1080p30_16_9, 1920, 1080, FALSE, 2200, 280, 1125,
+	 45, 33716, 29970, 74176, 30000, FALSE},
+	{HDMI_VFRMT_1920x1080p30_16_9, 1920, 1080, FALSE, 2200, 280, 1125,
+	 45, 33750, 30000, 74250, 30000, FALSE},
+	{HDMI_VFRMT_1920x1080i50_16_9, 1920, 1080, TRUE,  2304, 384, 1250,
+	 85, 31250, 50000, 72000, 50000, FALSE},
+	{HDMI_VFRMT_1920x1080i60_16_9, 1920, 1080, TRUE,  2200, 280, 1125,
+	 22, 33716, 59940, 74176, 59940, FALSE},
+	{HDMI_VFRMT_1920x1080i60_16_9, 1920, 1080, TRUE,  2200, 280, 1125,
+	 22, 33750, 60000, 74250, 60000, FALSE},
+	{HDMI_VFRMT_1920x1080i100_16_9, 1920, 1080, TRUE,  2640, 720, 1125,
+	 22, 56250, 100000, 148500, 100000, FALSE},
+	{HDMI_VFRMT_1920x1080i120_16_9, 1920, 1080, TRUE,  2200, 280, 1125,
+	 22, 67432, 119880, 148352, 119980, FALSE},
+	{HDMI_VFRMT_1920x1080i120_16_9, 1920, 1080, TRUE,  2200, 280, 1125,
+	 22, 67500, 120000, 148500, 120000, FALSE},
+
+	/* All 2880 H Active */
+	{HDMI_VFRMT_2880x576i50_4_3, 2880, 576, TRUE,  3456, 576, 625, 24,
+	 15625, 50000, 54000, 50000, TRUE},
+	{HDMI_VFRMT_2880x288p50_4_3, 2880, 576, FALSE, 3456, 576, 312, 24,
+	 15625, 50080, 54000, 50000, TRUE},
+	{HDMI_VFRMT_2880x288p50_4_3, 2880, 576, FALSE, 3456, 576, 313, 25,
+	 15625, 49920, 54000, 50000, TRUE},
+	{HDMI_VFRMT_2880x288p50_4_3, 2880, 576, FALSE, 3456, 576, 314, 26,
+	 15625, 49761, 54000, 50000, TRUE},
+	{HDMI_VFRMT_2880x576p50_4_3, 2880, 576, FALSE, 3456, 576, 625, 49,
+	 31250, 50000, 108000, 50000, TRUE},
+	{HDMI_VFRMT_2880x480i60_4_3, 2880, 480, TRUE,  3432, 552, 525, 22,
+	 15734, 59940, 54000, 59940, TRUE},
+	{HDMI_VFRMT_2880x240p60_4_3, 2880, 480, FALSE, 3432, 552, 262, 22,
+	 15734, 60054, 54000, 59940, TRUE},
+	{HDMI_VFRMT_2880x240p60_4_3, 2880, 480, FALSE, 3432, 552, 263, 23,
+	 15734, 59940, 54000, 59940, TRUE},
+	{HDMI_VFRMT_2880x480p60_4_3, 2880, 480, FALSE, 3432, 552, 525, 45,
+	 31469, 59940, 108000, 59940, TRUE},
+	{HDMI_VFRMT_2880x480i60_4_3, 2880, 480, TRUE,  3432, 552, 525, 22,
+	 15750, 60000, 54054, 60000, TRUE},
+	{HDMI_VFRMT_2880x240p60_4_3, 2880, 240, FALSE, 3432, 552, 262, 22,
+	 15750, 60115, 54054, 60000, TRUE},
+	{HDMI_VFRMT_2880x240p60_4_3, 2880, 240, FALSE, 3432, 552, 262, 23,
+	 15750, 59886, 54054, 60000, TRUE},
+	{HDMI_VFRMT_2880x480p60_4_3, 2880, 480, FALSE, 3432, 552, 525, 45,
+	 31500, 60000, 108108, 60000, TRUE},
+};
+
+static const uint8 *hdmi_edid_find_block(const uint8 *in_buf,
+		uint32 start_offset, uint8 type, uint8 *len)
+{
+	/* the start of data block collection, start of Video Data Block */
+	uint32 offset = start_offset;
+	uint32 end_dbc_offset = in_buf[2];
+
+	*len = 0;
+
+	/*edid buffer 1, byte 2 being 4 means no non-DTD/Data block collection
+	  present.
+	  edid buffer 1, byte 2 being 0 menas no non-DTD/DATA block collection
+	  present and no DTD data present.*/
+	if ((end_dbc_offset == 0) || (end_dbc_offset == 4)) {
+		DEV_WARN("EDID: no DTD or non-DTD data present\n");
+		return NULL;
+	}
+	while (offset < end_dbc_offset) {
+		uint8 block_len = in_buf[offset] & 0x1F;
+		if ((in_buf[offset] >> 5) == type) {
+			*len = block_len;
+			DEV_DBG("EDID: block=%d found @ %d with length=%d\n",
+				type, offset, block_len);
+			return in_buf+offset;
+		}
+		offset += 1 + block_len;
+	}
+	DEV_WARN("EDID: type=%d block not found in EDID block\n", type);
+	return NULL;
+}
+
+static void hdmi_edid_extract_vendor_id(const uint8 *in_buf,
+	char *vendor_id)
+{
+	uint32 id_codes = ((uint32)in_buf[8] << 8) + in_buf[9];
+
+	vendor_id[0] = 'A' - 1 + ((id_codes >> 10) & 0x1F);
+	vendor_id[1] = 'A' - 1 + ((id_codes >> 5) & 0x1F);
+	vendor_id[2] = 'A' - 1 + (id_codes & 0x1F);
+	vendor_id[3] = 0;
+}
+
+static uint32 hdmi_edid_extract_ieee_reg_id(const uint8 *in_buf)
+{
+	uint8 len;
+	const uint8 *vsd = hdmi_edid_find_block(in_buf, DBC_START_OFFSET, 3,
+			&len);
+
+	if (vsd == NULL)
+		return 0;
+
+	DEV_DBG("EDID: VSD PhyAddr=%04x, MaxTMDS=%dMHz\n",
+		((uint32)vsd[4] << 8) + (uint32)vsd[5], (uint32)vsd[7] * 5);
+	external_common_state->physical_address =
+		((uint16)vsd[4] << 8) + (uint16)vsd[5];
+	return ((uint32)vsd[3] << 16) + ((uint32)vsd[2] << 8) + (uint32)vsd[1];
+}
+
+#define HDMI_VSDB_3D_DATA_OFFSET(vsd) \
+	(!((vsd)[8] & BIT(7)) ? 9 : (!((vsd)[8] & BIT(6)) ? 11 : 13))
+
+static void hdmi_edid_extract_3d_present(const uint8 *in_buf)
+{
+	uint8 len, offset;
+	const uint8 *vsd = hdmi_edid_find_block(in_buf, DBC_START_OFFSET, 3,
+			&len);
+
+	external_common_state->present_3d = 0;
+	if (vsd == NULL || len < 9) {
+		DEV_DBG("EDID[3D]: block-id 3 not found or not long enough\n");
+		return;
+	}
+
+	offset = HDMI_VSDB_3D_DATA_OFFSET(vsd);
+	DEV_DBG("EDID: 3D present @ %d = %02x\n", offset, vsd[offset]);
+	if (vsd[offset] >> 7) { /* 3D format indication present */
+		DEV_INFO("EDID: 3D present, 3D-len=%d\n", vsd[offset+1] & 0x1F);
+		external_common_state->present_3d = 1;
+	}
+}
+
+
+static void hdmi_edid_extract_latency_fields(const uint8 *in_buf)
+{
+	uint8 len;
+	const uint8 *vsd = hdmi_edid_find_block(in_buf, DBC_START_OFFSET, 3,
+			&len);
+
+	if (vsd == NULL || len < 12 || !(vsd[8] & BIT(7))) {
+		external_common_state->video_latency = (uint16)-1;
+		external_common_state->audio_latency = (uint16)-1;
+		DEV_DBG("EDID: No audio/video latency present\n");
+	} else {
+		external_common_state->video_latency = vsd[9];
+		external_common_state->audio_latency = vsd[10];
+		DEV_DBG("EDID: video-latency=%04x, audio-latency=%04x\n",
+			external_common_state->video_latency,
+			external_common_state->audio_latency);
+	}
+}
+
+static void hdmi_edid_extract_speaker_allocation_data(const uint8 *in_buf)
+{
+	uint8 len;
+	const uint8 *sad = hdmi_edid_find_block(in_buf, DBC_START_OFFSET, 4,
+			&len);
+
+	if (sad == NULL)
+		return;
+
+	external_common_state->speaker_allocation_block = sad[1];
+	DEV_DBG("EDID: speaker allocation data SP byte = %08x %s%s%s%s%s%s%s\n",
+		sad[1],
+		(sad[1] & BIT(0)) ? "FL/FR," : "",
+		(sad[1] & BIT(1)) ? "LFE," : "",
+		(sad[1] & BIT(2)) ? "FC," : "",
+		(sad[1] & BIT(3)) ? "RL/RR," : "",
+		(sad[1] & BIT(4)) ? "RC," : "",
+		(sad[1] & BIT(5)) ? "FLC/FRC," : "",
+		(sad[1] & BIT(6)) ? "RLC/RRC," : "");
+}
+
+static void hdmi_edid_extract_audio_data_blocks(const uint8 *in_buf)
+{
+	uint8 len;
+	const uint8 *sad = hdmi_edid_find_block(in_buf, DBC_START_OFFSET, 1,
+			&len);
+	uint32 *adb = external_common_state->audio_data_blocks;
+
+	if (sad == NULL)
+		return;
+
+	external_common_state->audio_data_block_cnt = 0;
+	while (len >= 3 && external_common_state->audio_data_block_cnt < 16) {
+		DEV_DBG("EDID: Audio Data Block=<ch=%d, format=%d "
+			"sampling=0x%02x bit-depth=0x%02x>\n",
+			(sad[1] & 0x7)+1, sad[1] >> 3, sad[2], sad[3]);
+		*adb++ = (uint32)sad[1] + ((uint32)sad[2] << 8)
+			+ ((uint32)sad[2] << 16);
+		++external_common_state->audio_data_block_cnt;
+		len -= 3;
+		sad += 3;
+	}
+}
+
+static void hdmi_edid_extract_extended_data_blocks(const uint8 *in_buf)
+{
+	uint8 len = 0;
+	uint32 start_offset = DBC_START_OFFSET;
+
+	/* A Tage code of 7 identifies extended data blocks */
+	uint8 const *etag = hdmi_edid_find_block(in_buf, start_offset, 7, &len);
+
+	while (etag != NULL) {
+		/* The extended data block should at least be 2 bytes long */
+		if (len < 2) {
+			DEV_DBG("EDID: Found an extended data block of length"
+				"less than 2 bytes. Ignoring ...\n");
+		} else {
+			/*
+			 * The second byte of the extended data block has the
+			 * extended tag code
+			 */
+			switch (etag[1]) {
+			case 0:
+				/* Video Capability Data Block */
+				DEV_DBG("EDID: VCDB=%02X %02X\n", etag[1],
+						etag[2]);
+
+				/*
+				 * Check if the sink specifies underscan
+				 * support for:
+				 * BIT 5: preferred video format
+				 * BIT 3: IT video format
+				 * BIT 1: CE video format
+				 */
+				external_common_state->pt_scan_info = (etag[2] &
+							(BIT(4) | BIT(5))) >> 4;
+				external_common_state->it_scan_info = (etag[2] &
+							(BIT(3) | BIT(2))) >> 2;
+				external_common_state->ce_scan_info = etag[2] &
+							(BIT(1) | BIT(0));
+				DEV_DBG("EDID: Scan Information (pt|it|ce): "
+					"(%d|%d|%d)",
+					external_common_state->pt_scan_info,
+					external_common_state->it_scan_info,
+					external_common_state->ce_scan_info);
+				break;
+			default:
+				DEV_DBG("EDID: Extend Tag Code %d not"
+						"supported\n", etag[1]);
+				break;
+			}
+		}
+
+		/* There could be more that one extended data block */
+		start_offset = etag - in_buf + len + 1;
+		etag = hdmi_edid_find_block(in_buf, start_offset, 7, &len);
+	}
+}
+
+static void hdmi_edid_detail_desc(const uint8 *data_buf, uint32 *disp_mode)
+{
+	boolean	aspect_ratio_4_3    = FALSE;
+	boolean	interlaced          = FALSE;
+	uint32	active_h            = 0;
+	uint32	active_v            = 0;
+	uint32	blank_h             = 0;
+	uint32	blank_v             = 0;
+	uint32	ndx                 = 0;
+	uint32	max_num_of_elements = 0;
+	uint32	img_size_h          = 0;
+	uint32	img_size_v          = 0;
+
+	/* See VESA Spec */
+	/* EDID_TIMING_DESC_UPPER_H_NIBBLE[0x4]: Relative Offset to the EDID
+	 *   detailed timing descriptors - Upper 4 bit for each H active/blank
+	 *   field */
+	/* EDID_TIMING_DESC_H_ACTIVE[0x2]: Relative Offset to the EDID detailed
+	 *   timing descriptors - H active */
+	active_h = ((((uint32)data_buf[0x4] >> 0x4) & 0xF) << 8)
+		| data_buf[0x2];
+
+	/* EDID_TIMING_DESC_H_BLANK[0x3]: Relative Offset to the EDID detailed
+	 *   timing descriptors - H blank */
+	blank_h = (((uint32)data_buf[0x4] & 0xF) << 8)
+		| data_buf[0x3];
+
+	/* EDID_TIMING_DESC_UPPER_V_NIBBLE[0x7]: Relative Offset to the EDID
+	 *   detailed timing descriptors - Upper 4 bit for each V active/blank
+	 *   field */
+	/* EDID_TIMING_DESC_V_ACTIVE[0x5]: Relative Offset to the EDID detailed
+	 *   timing descriptors - V active */
+	active_v = ((((uint32)data_buf[0x7] >> 0x4) & 0xF) << 8)
+		| data_buf[0x5];
+
+	/* EDID_TIMING_DESC_V_BLANK[0x6]: Relative Offset to the EDID detailed
+	 *   timing descriptors - V blank */
+	blank_v = (((uint32)data_buf[0x7] & 0xF) << 8)
+		| data_buf[0x6];
+
+	/* EDID_TIMING_DESC_IMAGE_SIZE_UPPER_NIBBLE[0xE]: Relative Offset to the
+	 *   EDID detailed timing descriptors - Image Size upper nibble
+	 *   V and H */
+	/* EDID_TIMING_DESC_H_IMAGE_SIZE[0xC]: Relative Offset to the EDID
+	 *   detailed timing descriptors - H image size */
+	/* EDID_TIMING_DESC_V_IMAGE_SIZE[0xD]: Relative Offset to the EDID
+	 *   detailed timing descriptors - V image size */
+	img_size_h = ((((uint32)data_buf[0xE] >> 0x4) & 0xF) << 8)
+		| data_buf[0xC];
+	img_size_v = (((uint32)data_buf[0xE] & 0xF) << 8)
+		| data_buf[0xD];
+
+	/*
+	 * aspect ratio as 4:3 if within specificed range , rathaer than being
+	 * absolute value
+	 */
+	aspect_ratio_4_3 = (abs(img_size_h * 3 - img_size_v * 4) < 5) ? 1 : 0;
+
+	max_num_of_elements  = sizeof(hdmi_edid_disp_mode_lut)
+		/ sizeof(*hdmi_edid_disp_mode_lut);
+
+	/* EDID_TIMING_DESC_INTERLACE[0x11:7]: Relative Offset to the EDID
+	 *   detailed timing descriptors - Interlace flag */
+	DEV_DBG("Interlaced mode byte data_buf[0x11]=[%x]\n", data_buf[0x11]);
+	/*
+	 * CEA 861-D: interlaced bit is bit[7] of byte[0x11]
+	 */
+	interlaced = (data_buf[0x11] & 0x80) >> 7;
+
+	DEV_DBG("%s: A[%ux%u] B[%ux%u] V[%ux%u] %s\n", __func__,
+		active_h, active_v, blank_h, blank_v, img_size_h, img_size_v,
+		interlaced ? "i" : "p");
+
+	*disp_mode = HDMI_VFRMT_FORCE_32BIT;
+	while (ndx < max_num_of_elements) {
+		const struct hdmi_edid_video_mode_property_type *edid =
+			hdmi_edid_disp_mode_lut+ndx;
+
+		if ((interlaced    == edid->interlaced)    &&
+			(active_h  == edid->active_h)      &&
+			(blank_h   == edid->total_blank_h) &&
+			(blank_v   == edid->total_blank_v) &&
+			((active_v == edid->active_v) ||
+			 (active_v == (edid->active_v + 1)))
+		) {
+			if (edid->aspect_ratio_4_3 && !aspect_ratio_4_3)
+				/* Aspect ratio 16:9 */
+				*disp_mode = edid->video_code + 1;
+			else
+				/* Aspect ratio 4:3 */
+				*disp_mode = edid->video_code;
+
+			DEV_DBG("%s: mode found:%d\n", __func__, *disp_mode);
+			break;
+		}
+		++ndx;
+	}
+	if (ndx == max_num_of_elements)
+		DEV_INFO("%s: *no mode* found\n", __func__);
+}
+
+static void add_supported_video_format(
+	struct hdmi_disp_mode_list_type *disp_mode_list,
+	uint32 video_format)
+{
+	const struct hdmi_disp_mode_timing_type *timing =
+		hdmi_common_get_supported_mode(video_format);
+	boolean supported = timing != NULL;
+
+	if (video_format >= HDMI_VFRMT_MAX)
+		return;
+
+	DEV_DBG("EDID: format: %d [%s], %s\n",
+		video_format, video_format_2string(video_format),
+		supported ? "Supported" : "Not-Supported");
+	if (supported) {
+		if (mhl_is_connected()) {
+			const struct hdmi_disp_mode_timing_type *mhl_timing =
+				hdmi_mhl_get_supported_mode(video_format);
+			boolean mhl_supported = mhl_timing != NULL;
+			DEV_DBG("EDID: format: %d [%s], %s by MHL\n",
+			video_format, video_format_2string(video_format),
+				mhl_supported ? "Supported" : "Not-Supported");
+			if (mhl_supported)
+				disp_mode_list->disp_mode_list[
+			disp_mode_list->num_of_elements++] = video_format;
+		} else
+			disp_mode_list->disp_mode_list[
+			disp_mode_list->num_of_elements++] = video_format;
+	}
+}
+
+const char *single_video_3d_format_2string(uint32 format)
+{
+	switch (format) {
+	case TOP_AND_BOTTOM: return "TAB";
+	case FRAME_PACKING: return "FP";
+	case SIDE_BY_SIDE_HALF: return "SSH";
+	}
+	return "";
+}
+
+ssize_t video_3d_format_2string(uint32 format, char *buf)
+{
+	ssize_t ret, len = 0;
+	ret = snprintf(buf, PAGE_SIZE, "%s",
+		single_video_3d_format_2string(format & FRAME_PACKING));
+	len += ret;
+
+	if (len && (format & TOP_AND_BOTTOM))
+		ret = snprintf(buf + len, PAGE_SIZE, ":%s",
+			single_video_3d_format_2string(
+				format & TOP_AND_BOTTOM));
+	else
+		ret = snprintf(buf + len, PAGE_SIZE, "%s",
+			single_video_3d_format_2string(
+				format & TOP_AND_BOTTOM));
+	len += ret;
+
+	if (len && (format & SIDE_BY_SIDE_HALF))
+		ret = snprintf(buf + len, PAGE_SIZE, ":%s",
+			single_video_3d_format_2string(
+				format & SIDE_BY_SIDE_HALF));
+	else
+		ret = snprintf(buf + len, PAGE_SIZE, "%s",
+			single_video_3d_format_2string(
+				format & SIDE_BY_SIDE_HALF));
+	len += ret;
+
+	return len;
+}
+
+static void add_supported_3d_format(
+	struct hdmi_disp_mode_list_type *disp_mode_list,
+	uint32 video_format,
+	uint32 video_3d_format)
+{
+	char string[128];
+	boolean added = FALSE;
+	int i;
+	for (i = 0; i < disp_mode_list->num_of_elements; ++i) {
+		if (disp_mode_list->disp_mode_list[i] == video_format) {
+			disp_mode_list->disp_3d_mode_list[i] |=
+				video_3d_format;
+			added = TRUE;
+			break;
+		}
+	}
+	video_3d_format_2string(video_3d_format, string);
+	DEV_DBG("EDID[3D]: format: %d [%s], %s %s\n",
+		video_format, video_format_2string(video_format),
+		string, added ? "added" : "NOT added");
+}
+
+static void hdmi_edid_get_display_vsd_3d_mode(const uint8 *data_buf,
+	struct hdmi_disp_mode_list_type *disp_mode_list,
+	uint32 num_og_cea_blocks)
+{
+	uint8 len, offset, present_multi_3d, hdmi_vic_len, hdmi_3d_len;
+	uint16 structure_all, structure_mask;
+	const uint8 *vsd = num_og_cea_blocks ?
+		hdmi_edid_find_block(data_buf+0x80, DBC_START_OFFSET,
+				3, &len) : NULL;
+	int i;
+
+	offset = HDMI_VSDB_3D_DATA_OFFSET(vsd);
+	present_multi_3d = (vsd[offset] & 0x60) >> 5;
+
+	offset += 1;
+	hdmi_vic_len = (vsd[offset] >> 5) & 0x7;
+	hdmi_3d_len = vsd[offset] & 0x1F;
+	DEV_DBG("EDID[3D]: HDMI_VIC_LEN = %d, HDMI_3D_LEN = %d\n",
+		hdmi_vic_len, hdmi_3d_len);
+
+	offset += (hdmi_vic_len + 1);
+	if (present_multi_3d == 1 || present_multi_3d == 2) {
+		DEV_DBG("EDID[3D]: multi 3D present (%d)\n", present_multi_3d);
+		/* 3d_structure_all */
+		structure_all = (vsd[offset] << 8) | vsd[offset + 1];
+		offset += 2;
+		hdmi_3d_len -= 2;
+		if (present_multi_3d == 2) {
+			/* 3d_structure_mask */
+			structure_mask = (vsd[offset] << 8) | vsd[offset + 1];
+			offset += 2;
+			hdmi_3d_len -= 2;
+		} else
+			structure_mask = 0xffff;
+
+		i = 0;
+		while (i < 16) {
+			if (i >= disp_mode_list->disp_multi_3d_mode_list_cnt)
+				break;
+
+			if (!(structure_mask & BIT(i))) {
+				++i;
+				continue;
+			}
+
+			/* BIT0: FRAME PACKING */
+			if (structure_all & BIT(0))
+				add_supported_3d_format(disp_mode_list,
+					disp_mode_list->
+						disp_multi_3d_mode_list[i],
+					FRAME_PACKING);
+
+			/* BIT6: TOP AND BOTTOM */
+			if (structure_all & BIT(6))
+				add_supported_3d_format(disp_mode_list,
+					disp_mode_list->
+						disp_multi_3d_mode_list[i],
+					TOP_AND_BOTTOM);
+
+			/* BIT8: SIDE BY SIDE HALF */
+			if (structure_all & BIT(8))
+				add_supported_3d_format(disp_mode_list,
+					disp_mode_list->
+						disp_multi_3d_mode_list[i],
+					SIDE_BY_SIDE_HALF);
+
+			++i;
+		}
+	}
+
+	i = 0;
+	while (hdmi_3d_len > 0) {
+		DEV_DBG("EDID[3D]: 3D_Structure_%d @ %d: %02x\n",
+			i + 1, offset, vsd[offset]);
+
+		if ((vsd[offset] >> 4) >=
+			disp_mode_list->disp_multi_3d_mode_list_cnt) {
+			if ((vsd[offset] & 0x0F) >= 8) {
+				offset += 1;
+				hdmi_3d_len -= 1;
+				DEV_DBG("EDID[3D]: 3D_Detail_%d @ %d: %02x\n",
+					i + 1, offset, vsd[offset]);
+			}
+			i += 1;
+			offset += 1;
+			hdmi_3d_len -= 1;
+			continue;
+		}
+
+		switch (vsd[offset] & 0x0F) {
+		case 0:
+			/* 0000b: FRAME PACKING */
+			add_supported_3d_format(disp_mode_list,
+				disp_mode_list->disp_multi_3d_mode_list
+					[vsd[offset] >> 4],
+				FRAME_PACKING);
+			break;
+		case 6:
+			/* 0110b: TOP AND BOTTOM */
+			add_supported_3d_format(disp_mode_list,
+				disp_mode_list->disp_multi_3d_mode_list
+					[vsd[offset] >> 4],
+				TOP_AND_BOTTOM);
+			break;
+		case 8:
+			/* 1000b: SIDE BY SIDE HALF */
+			add_supported_3d_format(disp_mode_list,
+				disp_mode_list->disp_multi_3d_mode_list
+					[vsd[offset] >> 4],
+				SIDE_BY_SIDE_HALF);
+			break;
+		}
+		if ((vsd[offset] & 0x0F) >= 8) {
+			offset += 1;
+			hdmi_3d_len -= 1;
+			DEV_DBG("EDID[3D]: 3D_Detail_%d @ %d: %02x\n",
+				i + 1, offset, vsd[offset]);
+		}
+		i += 1;
+		offset += 1;
+		hdmi_3d_len -= 1;
+	}
+}
+
+static void hdmi_edid_get_display_mode(const uint8 *data_buf,
+	struct hdmi_disp_mode_list_type *disp_mode_list,
+	uint32 num_og_cea_blocks)
+{
+	uint8 i			= 0;
+	uint32 video_format	= HDMI_VFRMT_640x480p60_4_3;
+	boolean has480p		= FALSE;
+	uint8 len;
+	const uint8 *edid_blk0 = &data_buf[0x0];
+	const uint8 *edid_blk1 = &data_buf[0x80];
+	const uint8 *svd = num_og_cea_blocks ?
+		hdmi_edid_find_block(data_buf+0x80, DBC_START_OFFSET,
+				2, &len) : NULL;
+	boolean has60hz_mode	= FALSE;
+	boolean has50hz_mode	= FALSE;
+
+
+	disp_mode_list->num_of_elements = 0;
+	disp_mode_list->disp_multi_3d_mode_list_cnt = 0;
+	if (svd != NULL) {
+		++svd;
+		for (i = 0; i < len; ++i, ++svd) {
+			/* Subtract 1 because it is zero based in the driver,
+			 * while the Video identification code is 1 based in the
+			 * CEA_861D spec */
+			video_format = (*svd & 0x7F) - 1;
+			add_supported_video_format(disp_mode_list,
+				video_format);
+			/* Make a note of the preferred video format */
+			if (i == 0) {
+				external_common_state->preferred_video_format =
+					video_format;
+			}
+			if (i < 16) {
+				disp_mode_list->disp_multi_3d_mode_list[i]
+					= video_format;
+				disp_mode_list->disp_multi_3d_mode_list_cnt++;
+			}
+
+			if (video_format <= HDMI_VFRMT_1920x1080p60_16_9 ||
+				video_format == HDMI_VFRMT_2880x480p60_4_3 ||
+				video_format == HDMI_VFRMT_2880x480p60_16_9)
+				has60hz_mode = TRUE;
+
+			if ((video_format >= HDMI_VFRMT_720x576p50_4_3 &&
+				video_format <= HDMI_VFRMT_1920x1080p50_16_9) ||
+				video_format == HDMI_VFRMT_2880x576p50_4_3 ||
+				video_format == HDMI_VFRMT_2880x576p50_16_9 ||
+				video_format == HDMI_VFRMT_1920x1250i50_16_9)
+				has50hz_mode = TRUE;
+			if (video_format == HDMI_VFRMT_640x480p60_4_3)
+				has480p = TRUE;
+		}
+	} else if (!num_og_cea_blocks) {
+		/* Detailed timing descriptors */
+		uint32 desc_offset = 0;
+		/* Maximum 4 timing descriptor in block 0 - No CEA
+		 * extension in this case */
+		/* EDID_FIRST_TIMING_DESC[0x36] - 1st detailed timing
+		 *   descriptor */
+		/* EDID_DETAIL_TIMING_DESC_BLCK_SZ[0x12] - Each detailed timing
+		 *   descriptor has block size of 18 */
+		while (4 > i && 0 != edid_blk0[0x36+desc_offset]) {
+			hdmi_edid_detail_desc(edid_blk0+0x36+desc_offset,
+				&video_format);
+			DEV_DBG("[%s:%d] Block-0 Adding vid fmt = [%s]\n",
+				__func__, __LINE__,
+				video_format_2string(video_format));
+			add_supported_video_format(disp_mode_list,
+				video_format);
+			if (video_format == HDMI_VFRMT_640x480p60_4_3)
+				has480p = TRUE;
+			/* Make a note of the preferred video format */
+			if (i == 0) {
+				external_common_state->preferred_video_format =
+					video_format;
+			}
+			desc_offset += 0x12;
+			++i;
+		}
+	} else if (1 == num_og_cea_blocks) {
+		uint32 desc_offset = 0;
+
+		/*
+		 * Read from both block 0 and block 1
+		 * Read EDID block[0] as above
+		 */
+		while (4 > i && 0 != edid_blk0[0x36+desc_offset]) {
+			hdmi_edid_detail_desc(edid_blk0+0x36+desc_offset,
+				&video_format);
+			DEV_DBG("[%s:%d] Block-0 Adding vid fmt = [%s]\n",
+				__func__, __LINE__,
+				video_format_2string(video_format));
+			add_supported_video_format(disp_mode_list,
+				video_format);
+			if (video_format == HDMI_VFRMT_640x480p60_4_3)
+				has480p = TRUE;
+			/* Make a note of the preferred video format */
+			if (i == 0) {
+				external_common_state->preferred_video_format =
+					video_format;
+			}
+			desc_offset += 0x12;
+			++i;
+		}
+
+		/* Parse block 1 - CEA extension byte offset of first
+		 * detailed timing generation - offset is relevant to
+		 * the offset of block 1 */
+
+		/* EDID_CEA_EXTENSION_FIRST_DESC[0x82]: Offset to CEA
+		 * extension first timing desc - indicate the offset of
+		 * the first detailed timing descriptor */
+		 /* EDID_BLOCK_SIZE = 0x80  Each page size in the EDID ROM */
+		desc_offset = edid_blk1[0x02];
+		while (0 != edid_blk1[desc_offset]) {
+			hdmi_edid_detail_desc(edid_blk1+desc_offset,
+				&video_format);
+			DEV_DBG("[%s:%d] Block-1 Adding vid fmt = [%s]\n",
+				__func__, __LINE__,
+				video_format_2string(video_format));
+			add_supported_video_format(disp_mode_list,
+				video_format);
+			if (video_format == HDMI_VFRMT_640x480p60_4_3)
+				has480p = TRUE;
+			/* Make a note of the preferred video format */
+			if (i == 0) {
+				external_common_state->preferred_video_format =
+					video_format;
+			}
+			desc_offset += 0x12;
+			++i;
+		}
+	}
+
+	/* mandaroty 3d format */
+	if (external_common_state->present_3d) {
+		if (has60hz_mode) {
+			add_supported_3d_format(disp_mode_list,
+				HDMI_VFRMT_1920x1080p24_16_9,
+				FRAME_PACKING | TOP_AND_BOTTOM);
+			add_supported_3d_format(disp_mode_list,
+				HDMI_VFRMT_1280x720p60_16_9,
+				FRAME_PACKING | TOP_AND_BOTTOM);
+			add_supported_3d_format(disp_mode_list,
+				HDMI_VFRMT_1920x1080i60_16_9,
+				SIDE_BY_SIDE_HALF);
+		}
+		if (has50hz_mode) {
+			add_supported_3d_format(disp_mode_list,
+				HDMI_VFRMT_1920x1080p24_16_9,
+				FRAME_PACKING | TOP_AND_BOTTOM);
+			add_supported_3d_format(disp_mode_list,
+				HDMI_VFRMT_1280x720p50_16_9,
+				FRAME_PACKING | TOP_AND_BOTTOM);
+			add_supported_3d_format(disp_mode_list,
+				HDMI_VFRMT_1920x1080i50_16_9,
+				SIDE_BY_SIDE_HALF);
+		}
+
+		/* 3d format described in Vendor Specific Data */
+		hdmi_edid_get_display_vsd_3d_mode(data_buf, disp_mode_list,
+			num_og_cea_blocks);
+	}
+
+	if (!has480p)
+		/* Need to add default 640 by 480 timings, in case not described
+		 * in the EDID structure.
+		 * All DTV sink devices should support this mode */
+		add_supported_video_format(disp_mode_list,
+			HDMI_VFRMT_640x480p60_4_3);
+}
+
+static int hdmi_common_read_edid_block(int block, uint8 *edid_buf)
+{
+	uint32 ndx, check_sum, print_len;
+#ifdef DEBUG
+	const u8 *b = edid_buf;
+#endif
+	int status = external_common_state->read_edid_block(block, edid_buf);
+	if (status)
+		goto error;
+
+	/* Calculate checksum */
+	check_sum = 0;
+	for (ndx = 0; ndx < 0x80; ++ndx)
+		check_sum += edid_buf[ndx];
+
+	if (check_sum & 0xFF) {
+		DEV_ERR("%s: failed CHECKSUM (read:%x, expected:%x)\n",
+			__func__, (uint8)edid_buf[0x7F], (uint8)check_sum);
+#ifdef DEBUG
+		for (ndx = 0; ndx < 0x100; ndx += 16)
+			DEV_DBG("EDID[%02x-%02x] %02x %02x %02x %02x  "
+				"%02x %02x %02x %02x    %02x %02x %02x %02x  "
+				"%02x %02x %02x %02x\n", ndx, ndx+15,
+				b[ndx+0], b[ndx+1], b[ndx+2], b[ndx+3],
+				b[ndx+4], b[ndx+5], b[ndx+6], b[ndx+7],
+				b[ndx+8], b[ndx+9], b[ndx+10], b[ndx+11],
+				b[ndx+12], b[ndx+13], b[ndx+14], b[ndx+15]);
+#endif
+		status = -EPROTO;
+		goto error;
+	}
+	print_len = 0x80;
+	for (ndx = 0; ndx < print_len; ndx += 16)
+		DEV_DBG("EDID[%02x-%02x] %02x %02x %02x %02x  "
+			"%02x %02x %02x %02x    %02x %02x %02x %02x  "
+			"%02x %02x %02x %02x\n", ndx, ndx+15,
+			b[ndx+0], b[ndx+1], b[ndx+2], b[ndx+3],
+			b[ndx+4], b[ndx+5], b[ndx+6], b[ndx+7],
+			b[ndx+8], b[ndx+9], b[ndx+10], b[ndx+11],
+			b[ndx+12], b[ndx+13], b[ndx+14], b[ndx+15]);
+
+
+error:
+	return status;
+}
+
+static boolean check_edid_header(const uint8 *edid_buf)
+{
+	return (edid_buf[0] == 0x00) && (edid_buf[1] == 0xff)
+		&& (edid_buf[2] == 0xff) && (edid_buf[3] == 0xff)
+		&& (edid_buf[4] == 0xff) && (edid_buf[5] == 0xff)
+		&& (edid_buf[6] == 0xff) && (edid_buf[7] == 0x00);
+}
+
+int hdmi_common_read_edid(void)
+{
+	int status = 0;
+	uint32 cea_extension_ver = 0;
+	uint32 num_og_cea_blocks  = 0;
+	uint32 ieee_reg_id = 0;
+	uint32 i = 1;
+	char vendor_id[5];
+	/* EDID_BLOCK_SIZE[0x80] Each page size in the EDID ROM */
+	uint8 edid_buf[0x80 * 4];
+
+	external_common_state->preferred_video_format = 0;
+	external_common_state->present_3d = 0;
+	memset(&external_common_state->disp_mode_list, 0,
+		sizeof(external_common_state->disp_mode_list));
+	memset(edid_buf, 0, sizeof(edid_buf));
+
+	status = hdmi_common_read_edid_block(0, edid_buf);
+	if (status || !check_edid_header(edid_buf)) {
+		if (!status)
+			status = -EPROTO;
+		DEV_ERR("%s: edid read block(0) failed: %d "
+			"[%02x%02x%02x%02x%02x%02x%02x%02x]\n", __func__,
+			status,
+			edid_buf[0], edid_buf[1], edid_buf[2], edid_buf[3],
+			edid_buf[4], edid_buf[5], edid_buf[6], edid_buf[7]);
+		goto error;
+	}
+	hdmi_edid_extract_vendor_id(edid_buf, vendor_id);
+
+	/* EDID_CEA_EXTENSION_FLAG[0x7E] - CEC extension byte */
+	num_og_cea_blocks = edid_buf[0x7E];
+
+	DEV_DBG("[JSR] (%s): No. of CEA blocks is  [%u]\n", __func__,
+		num_og_cea_blocks);
+	/* Find out any CEA extension blocks following block 0 */
+	switch (num_og_cea_blocks) {
+	case 0: /* No CEA extension */
+		external_common_state->hdmi_sink = false;
+		DEV_DBG("HDMI DVI mode: %s\n",
+			external_common_state->hdmi_sink ? "no" : "yes");
+		break;
+	case 1: /* Read block 1 */
+		status = hdmi_common_read_edid_block(1, &edid_buf[0x80]);
+		if (status) {
+			DEV_ERR("%s: ddc read block(1) failed: %d\n", __func__,
+				status);
+			goto error;
+		}
+		if (edid_buf[0x80] != 2)
+			num_og_cea_blocks = 0;
+		if (num_og_cea_blocks) {
+			ieee_reg_id =
+				hdmi_edid_extract_ieee_reg_id(edid_buf+0x80);
+			if (ieee_reg_id == 0x0c03)
+				external_common_state->hdmi_sink = TRUE ;
+			else
+				external_common_state->hdmi_sink = FALSE ;
+			hdmi_edid_extract_latency_fields(edid_buf+0x80);
+			hdmi_edid_extract_speaker_allocation_data(
+				edid_buf+0x80);
+			hdmi_edid_extract_audio_data_blocks(edid_buf+0x80);
+			hdmi_edid_extract_3d_present(edid_buf+0x80);
+			hdmi_edid_extract_extended_data_blocks(edid_buf+0x80);
+		}
+		break;
+	case 2:
+	case 3:
+	case 4:
+		for (i = 1; i <= num_og_cea_blocks; i++) {
+			if (!(i % 2)) {
+					status = hdmi_common_read_edid_block(i,
+								edid_buf+0x00);
+					if (status) {
+						DEV_ERR("%s: ddc read block(%d)"
+						"failed: %d\n", __func__, i,
+							status);
+						goto error;
+					}
+			} else {
+				status = hdmi_common_read_edid_block(i,
+							edid_buf+0x80);
+				if (status) {
+					DEV_ERR("%s: ddc read block(%d)"
+					"failed:%d\n", __func__, i,
+						status);
+					goto error;
+				}
+			}
+		}
+		break;
+	default:
+		DEV_ERR("%s: ddc read failed, not supported multi-blocks: %d\n",
+			__func__, num_og_cea_blocks);
+		status = -EPROTO;
+		goto error;
+	}
+
+	if (num_og_cea_blocks) {
+		/* EDID_CEA_EXTENSION_VERSION[0x81]: Offset to CEA extension
+		 * version number - v1,v2,v3 (v1 is seldom, v2 is obsolete,
+		 * v3 most common) */
+		cea_extension_ver = edid_buf[0x81];
+	}
+
+	/* EDID_VERSION[0x12] - EDID Version */
+	/* EDID_REVISION[0x13] - EDID Revision */
+	DEV_INFO("EDID (V=%d.%d, #CEABlocks=%d[V%d], ID=%s, IEEE=%04x, "
+		"EDID-Ext=0x%02x)\n", edid_buf[0x12], edid_buf[0x13],
+		num_og_cea_blocks, cea_extension_ver, vendor_id, ieee_reg_id,
+		edid_buf[0x80]);
+
+	hdmi_edid_get_display_mode(edid_buf,
+		&external_common_state->disp_mode_list, num_og_cea_blocks);
+
+	return 0;
+
+error:
+	external_common_state->disp_mode_list.num_of_elements = 1;
+	external_common_state->disp_mode_list.disp_mode_list[0] =
+		external_common_state->video_resolution;
+	return status;
+}
+EXPORT_SYMBOL(hdmi_common_read_edid);
+
+bool hdmi_common_get_video_format_from_drv_data(struct msm_fb_data_type *mfd)
+{
+	uint32 format;
+	struct fb_var_screeninfo *var = &mfd->fbi->var;
+	bool changed = TRUE;
+
+	if (var->reserved[2]) {
+		format = var->reserved[2]-1;
+		DEV_DBG("reserved format is %d\n", format);
+	} else {
+		DEV_DBG("detecting resolution from %dx%d use var->reserved[3]"
+			" to specify mode", mfd->var_xres, mfd->var_yres);
+		switch (mfd->var_xres) {
+		default:
+		case  640:
+			format = HDMI_VFRMT_640x480p60_4_3;
+			break;
+		case  720:
+			format = (mfd->var_yres == 480)
+				? HDMI_VFRMT_720x480p60_16_9
+				: HDMI_VFRMT_720x576p50_16_9;
+			break;
+		case 1280:
+			format = HDMI_VFRMT_1280x720p60_16_9;
+			break;
+		case 1440:
+			format = (mfd->var_yres == 480)
+				? HDMI_VFRMT_1440x480i60_16_9
+				: HDMI_VFRMT_1440x576i50_16_9;
+			break;
+		case 1920:
+			format = HDMI_VFRMT_1920x1080p60_16_9;
+			break;
+		}
+	}
+
+	changed = external_common_state->video_resolution != format;
+	if (external_common_state->video_resolution != format)
+		DEV_DBG("switching %s => %s", video_format_2string(
+			external_common_state->video_resolution),
+			video_format_2string(format));
+	else
+		DEV_DBG("resolution %s", video_format_2string(
+			external_common_state->video_resolution));
+	external_common_state->video_resolution = format;
+	return changed;
+}
+EXPORT_SYMBOL(hdmi_common_get_video_format_from_drv_data);
+
+const struct hdmi_disp_mode_timing_type *hdmi_common_get_mode(uint32 mode)
+{
+	if (mode >= HDMI_VFRMT_MAX)
+		return NULL;
+
+	return &hdmi_common_supported_video_mode_lut[mode];
+}
+EXPORT_SYMBOL(hdmi_common_get_mode);
+
+const struct hdmi_disp_mode_timing_type *hdmi_common_get_supported_mode(
+	uint32 mode)
+{
+	const struct hdmi_disp_mode_timing_type *ret
+		= hdmi_common_get_mode(mode);
+
+	if (ret == NULL || !ret->supported)
+		return NULL;
+	return ret;
+}
+EXPORT_SYMBOL(hdmi_common_get_supported_mode);
+
+const struct hdmi_disp_mode_timing_type *hdmi_mhl_get_mode(uint32 mode)
+{
+	if (mode >= HDMI_VFRMT_MAX)
+		return NULL;
+
+	return &hdmi_mhl_supported_video_mode_lut[mode];
+}
+EXPORT_SYMBOL(hdmi_mhl_get_mode);
+
+const struct hdmi_disp_mode_timing_type *hdmi_mhl_get_supported_mode(
+	uint32 mode)
+{
+	const struct hdmi_disp_mode_timing_type *ret
+		= hdmi_mhl_get_mode(mode);
+
+	if (ret == NULL || !ret->supported)
+		return NULL;
+	return ret;
+}
+EXPORT_SYMBOL(hdmi_mhl_get_supported_mode);
+
+void hdmi_common_init_panel_info(struct msm_panel_info *pinfo)
+{
+	const struct hdmi_disp_mode_timing_type *timing =
+		hdmi_common_get_supported_mode(
+		external_common_state->video_resolution);
+
+	if (timing == NULL)
+		return;
+
+	pinfo->xres = timing->active_h;
+	pinfo->yres = timing->active_v;
+	pinfo->clk_rate = timing->pixel_freq*1000;
+	pinfo->frame_rate = 60;
+
+	pinfo->lcdc.h_back_porch = timing->back_porch_h;
+	pinfo->lcdc.h_front_porch = timing->front_porch_h;
+	pinfo->lcdc.h_pulse_width = timing->pulse_width_h;
+	pinfo->lcdc.v_back_porch = timing->back_porch_v;
+	pinfo->lcdc.v_front_porch = timing->front_porch_v;
+	pinfo->lcdc.v_pulse_width = timing->pulse_width_v;
+
+	pinfo->type = DTV_PANEL;
+	pinfo->pdest = DISPLAY_2;
+	pinfo->wait_cycle = 0;
+	pinfo->bpp = 24;
+	if (hdmi_prim_display)
+		pinfo->fb_num = 2;
+	else
+		pinfo->fb_num = 1;
+
+	/* blk */
+	pinfo->lcdc.border_clr = 0;
+	/* blue */
+	pinfo->lcdc.underflow_clr = 0xff;
+	pinfo->lcdc.hsync_skew = 0;
+}
+EXPORT_SYMBOL(hdmi_common_init_panel_info);
+#endif
diff --git a/drivers/video/msm/external_common.h b/drivers/video/msm/external_common.h
new file mode 100644
index 0000000..57c0804
--- /dev/null
+++ b/drivers/video/msm/external_common.h
@@ -0,0 +1,272 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __EXTERNAL_COMMON_H__
+#define __EXTERNAL_COMMON_H__
+#include <linux/switch.h>
+
+#ifdef DEBUG
+#ifndef DEV_DBG_PREFIX
+#define DEV_DBG_PREFIX "EXT_INTERFACE: "
+#endif
+#define DEV_DBG(args...)	pr_debug(DEV_DBG_PREFIX args)
+#else
+#define DEV_DBG(args...)	(void)0
+#endif /* DEBUG */
+#define DEV_INFO(args...)	dev_info(external_common_state->dev, args)
+#define DEV_WARN(args...)	dev_warn(external_common_state->dev, args)
+#define DEV_ERR(args...)	dev_err(external_common_state->dev, args)
+
+#ifdef CONFIG_FB_MSM_TVOUT
+#define TVOUT_VFRMT_NTSC_M_720x480i		0
+#define TVOUT_VFRMT_NTSC_J_720x480i		1
+#define TVOUT_VFRMT_PAL_BDGHIN_720x576i		2
+#define TVOUT_VFRMT_PAL_M_720x480i		3
+#define TVOUT_VFRMT_PAL_N_720x480i		4
+#elif defined(CONFIG_FB_MSM_HDMI_COMMON)
+/* all video formats defined by EIA CEA 861D */
+#define HDMI_VFRMT_640x480p60_4_3	0
+#define HDMI_VFRMT_720x480p60_4_3	1
+#define HDMI_VFRMT_720x480p60_16_9	2
+#define HDMI_VFRMT_1280x720p60_16_9	3
+#define HDMI_VFRMT_1920x1080i60_16_9	4
+#define HDMI_VFRMT_720x480i60_4_3	5
+#define HDMI_VFRMT_1440x480i60_4_3	HDMI_VFRMT_720x480i60_4_3
+#define HDMI_VFRMT_720x480i60_16_9	6
+#define HDMI_VFRMT_1440x480i60_16_9	HDMI_VFRMT_720x480i60_16_9
+#define HDMI_VFRMT_720x240p60_4_3	7
+#define HDMI_VFRMT_1440x240p60_4_3	HDMI_VFRMT_720x240p60_4_3
+#define HDMI_VFRMT_720x240p60_16_9	8
+#define HDMI_VFRMT_1440x240p60_16_9	HDMI_VFRMT_720x240p60_16_9
+#define HDMI_VFRMT_2880x480i60_4_3	9
+#define HDMI_VFRMT_2880x480i60_16_9	10
+#define HDMI_VFRMT_2880x240p60_4_3	11
+#define HDMI_VFRMT_2880x240p60_16_9	12
+#define HDMI_VFRMT_1440x480p60_4_3	13
+#define HDMI_VFRMT_1440x480p60_16_9	14
+#define HDMI_VFRMT_1920x1080p60_16_9	15
+#define HDMI_VFRMT_720x576p50_4_3	16
+#define HDMI_VFRMT_720x576p50_16_9	17
+#define HDMI_VFRMT_1280x720p50_16_9	18
+#define HDMI_VFRMT_1920x1080i50_16_9	19
+#define HDMI_VFRMT_720x576i50_4_3	20
+#define HDMI_VFRMT_1440x576i50_4_3	HDMI_VFRMT_720x576i50_4_3
+#define HDMI_VFRMT_720x576i50_16_9	21
+#define HDMI_VFRMT_1440x576i50_16_9	HDMI_VFRMT_720x576i50_16_9
+#define HDMI_VFRMT_720x288p50_4_3	22
+#define HDMI_VFRMT_1440x288p50_4_3	HDMI_VFRMT_720x288p50_4_3
+#define HDMI_VFRMT_720x288p50_16_9	23
+#define HDMI_VFRMT_1440x288p50_16_9	HDMI_VFRMT_720x288p50_16_9
+#define HDMI_VFRMT_2880x576i50_4_3	24
+#define HDMI_VFRMT_2880x576i50_16_9	25
+#define HDMI_VFRMT_2880x288p50_4_3	26
+#define HDMI_VFRMT_2880x288p50_16_9	27
+#define HDMI_VFRMT_1440x576p50_4_3	28
+#define HDMI_VFRMT_1440x576p50_16_9	29
+#define HDMI_VFRMT_1920x1080p50_16_9	30
+#define HDMI_VFRMT_1920x1080p24_16_9	31
+#define HDMI_VFRMT_1920x1080p25_16_9	32
+#define HDMI_VFRMT_1920x1080p30_16_9	33
+#define HDMI_VFRMT_2880x480p60_4_3	34
+#define HDMI_VFRMT_2880x480p60_16_9	35
+#define HDMI_VFRMT_2880x576p50_4_3	36
+#define HDMI_VFRMT_2880x576p50_16_9	37
+#define HDMI_VFRMT_1920x1250i50_16_9	38
+#define HDMI_VFRMT_1920x1080i100_16_9	39
+#define HDMI_VFRMT_1280x720p100_16_9	40
+#define HDMI_VFRMT_720x576p100_4_3	41
+#define HDMI_VFRMT_720x576p100_16_9	42
+#define HDMI_VFRMT_720x576i100_4_3	43
+#define HDMI_VFRMT_1440x576i100_4_3	HDMI_VFRMT_720x576i100_4_3
+#define HDMI_VFRMT_720x576i100_16_9	44
+#define HDMI_VFRMT_1440x576i100_16_9	HDMI_VFRMT_720x576i100_16_9
+#define HDMI_VFRMT_1920x1080i120_16_9	45
+#define HDMI_VFRMT_1280x720p120_16_9	46
+#define HDMI_VFRMT_720x480p120_4_3	47
+#define HDMI_VFRMT_720x480p120_16_9	48
+#define HDMI_VFRMT_720x480i120_4_3	49
+#define HDMI_VFRMT_1440x480i120_4_3	HDMI_VFRMT_720x480i120_4_3
+#define HDMI_VFRMT_720x480i120_16_9	50
+#define HDMI_VFRMT_1440x480i120_16_9	HDMI_VFRMT_720x480i120_16_9
+#define HDMI_VFRMT_720x576p200_4_3	51
+#define HDMI_VFRMT_720x576p200_16_9	52
+#define HDMI_VFRMT_720x576i200_4_3	53
+#define HDMI_VFRMT_1440x576i200_4_3	HDMI_VFRMT_720x576i200_4_3
+#define HDMI_VFRMT_720x576i200_16_9	54
+#define HDMI_VFRMT_1440x576i200_16_9	HDMI_VFRMT_720x576i200_16_9
+#define HDMI_VFRMT_720x480p240_4_3	55
+#define HDMI_VFRMT_720x480p240_16_9	56
+#define HDMI_VFRMT_720x480i240_4_3	57
+#define HDMI_VFRMT_1440x480i240_4_3	HDMI_VFRMT_720x480i240_4_3
+#define HDMI_VFRMT_720x480i240_16_9	58
+#define HDMI_VFRMT_1440x480i240_16_9	HDMI_VFRMT_720x480i240_16_9
+#define HDMI_VFRMT_MAX			59
+#define HDMI_VFRMT_FORCE_32BIT		0x7FFFFFFF
+
+struct hdmi_disp_mode_timing_type {
+	uint32	video_format;
+	uint32	active_h;
+	uint32	front_porch_h;
+	uint32	pulse_width_h;
+	uint32	back_porch_h;
+	boolean	active_low_h;
+	uint32	active_v;
+	uint32	front_porch_v;
+	uint32	pulse_width_v;
+	uint32	back_porch_v;
+	boolean	active_low_v;
+	/* Must divide by 1000 to get the actual frequency in MHZ */
+	uint32	pixel_freq;
+	/* Must divide by 1000 to get the actual frequency in HZ */
+	uint32	refresh_rate;
+	boolean	interlaced;
+	boolean	supported;
+};
+
+#define HDMI_SETTINGS_640x480p60_4_3					\
+	{HDMI_VFRMT_640x480p60_4_3,      640,  16,  96,  48,  TRUE,	\
+	 480, 10, 2, 33, TRUE, 25200, 60000, FALSE, TRUE}
+#define HDMI_SETTINGS_720x480p60_4_3					\
+	{HDMI_VFRMT_720x480p60_4_3,      720,  16,  62,  60,  TRUE,	\
+	 480, 9, 6, 30,  TRUE, 27030, 60000, FALSE, TRUE}
+#define HDMI_SETTINGS_720x480p60_16_9					\
+	{HDMI_VFRMT_720x480p60_16_9,     720,  16,  62,  60,  TRUE,	\
+	 480, 9, 6, 30,  TRUE, 27030, 60000, FALSE, TRUE}
+#define HDMI_SETTINGS_1280x720p60_16_9					\
+	{HDMI_VFRMT_1280x720p60_16_9,    1280, 110, 40,  220, FALSE,	\
+	 720, 5, 5, 20, FALSE, 74250, 60000, FALSE, TRUE}
+#define HDMI_SETTINGS_1920x1080i60_16_9					\
+	{HDMI_VFRMT_1920x1080i60_16_9,   1920, 88,  44,  148, FALSE,	\
+	 540, 2, 5, 5, FALSE, 74250, 60000, FALSE, TRUE}
+#define HDMI_SETTINGS_1440x480i60_4_3					\
+	{HDMI_VFRMT_1440x480i60_4_3,     1440, 38,  124, 114, TRUE,	\
+	 240, 4, 3, 15, TRUE, 27000, 60000, TRUE, TRUE}
+#define HDMI_SETTINGS_1440x480i60_16_9					\
+	{HDMI_VFRMT_1440x480i60_16_9,    1440, 38,  124, 114, TRUE,	\
+	 240, 4, 3, 15, TRUE, 27000, 60000, TRUE, TRUE}
+#define HDMI_SETTINGS_1920x1080p60_16_9					\
+	{HDMI_VFRMT_1920x1080p60_16_9,   1920, 88,  44,  148,  FALSE,	\
+	 1080, 4, 5, 36, FALSE, 148500, 60000, FALSE, TRUE}
+#define HDMI_SETTINGS_720x576p50_4_3					\
+	{HDMI_VFRMT_720x576p50_4_3,      720,  12,  64,  68,   TRUE,	\
+	 576,  5, 5, 39, TRUE, 27000, 50000, FALSE, TRUE}
+#define HDMI_SETTINGS_720x576p50_16_9					\
+	{HDMI_VFRMT_720x576p50_16_9,     720,  12,  64,  68,   TRUE,	\
+	 576,  5, 5, 39, TRUE, 27000, 50000, FALSE, TRUE}
+#define HDMI_SETTINGS_1280x720p50_16_9					\
+	{HDMI_VFRMT_1280x720p50_16_9,    1280, 440, 40,  220,  FALSE,	\
+	 720,  5, 5, 20, FALSE, 74250, 50000, FALSE, TRUE}
+#define HDMI_SETTINGS_1440x576i50_4_3					\
+	{HDMI_VFRMT_1440x576i50_4_3,     1440, 24,  126, 138,  TRUE,	\
+	 288,  2, 3, 19, TRUE, 27000, 50000, TRUE, TRUE}
+#define HDMI_SETTINGS_1440x576i50_16_9					\
+	{HDMI_VFRMT_1440x576i50_16_9,    1440, 24,  126, 138,  TRUE,	\
+	 288,  2, 3, 19, TRUE, 27000, 50000, TRUE, TRUE}
+#define HDMI_SETTINGS_1920x1080p50_16_9					\
+	{HDMI_VFRMT_1920x1080p50_16_9,   1920,  528,  44,  148,  FALSE,	\
+	 1080, 4, 5, 36, FALSE, 148500, 50000, FALSE, TRUE}
+#define HDMI_SETTINGS_1920x1080p24_16_9					\
+	{HDMI_VFRMT_1920x1080p24_16_9,   1920,  638,  44,  148,  FALSE,	\
+	 1080, 4, 5, 36, FALSE, 74250, 24000, FALSE, TRUE}
+#define HDMI_SETTINGS_1920x1080p25_16_9					\
+	{HDMI_VFRMT_1920x1080p25_16_9,   1920,  528,  44,  148,  FALSE,	\
+	 1080, 4, 5, 36, FALSE, 74250, 25000, FALSE, TRUE}
+#define HDMI_SETTINGS_1920x1080p30_16_9					\
+	{HDMI_VFRMT_1920x1080p30_16_9,   1920,  88,   44,  148,  FALSE,	\
+	 1080, 4, 5, 36, FALSE, 74250, 30000, FALSE, TRUE}
+
+/* A lookup table for all the supported display modes by the HDMI
+ * hardware and driver.  Use HDMI_SETUP_LUT in the module init to
+ * setup the LUT with the supported modes. */
+extern struct hdmi_disp_mode_timing_type
+	hdmi_common_supported_video_mode_lut[HDMI_VFRMT_MAX];
+
+/* Structure that encapsulates all the supported display modes by the HDMI sink
+ * device */
+struct hdmi_disp_mode_list_type {
+	uint32	disp_mode_list[HDMI_VFRMT_MAX];
+#define TOP_AND_BOTTOM		0x10
+#define FRAME_PACKING		0x20
+#define SIDE_BY_SIDE_HALF	0x40
+	uint32	disp_3d_mode_list[HDMI_VFRMT_MAX];
+	uint32	disp_multi_3d_mode_list[16];
+	uint32	disp_multi_3d_mode_list_cnt;
+	uint32	num_of_elements;
+};
+#endif
+
+struct external_common_state_type {
+	boolean hpd_state;
+	struct kobject *uevent_kobj;
+	uint32 video_resolution;
+	struct device *dev;
+	struct switch_dev sdev;
+#ifdef CONFIG_FB_MSM_HDMI_3D
+	boolean format_3d;
+	void (*switch_3d)(boolean on);
+#endif
+#ifdef CONFIG_FB_MSM_HDMI_COMMON
+	boolean hdcp_active;
+	boolean hpd_feature_on;
+	boolean hdmi_sink;
+	struct hdmi_disp_mode_list_type disp_mode_list;
+	uint8 speaker_allocation_block;
+	uint16 video_latency, audio_latency;
+	uint8 audio_data_block_cnt;
+	uint16 physical_address;
+	uint32 preferred_video_format;
+	uint8 pt_scan_info;
+	uint8 it_scan_info;
+	uint8 ce_scan_info;
+	uint8 spd_vendor_name[8];
+	uint8 spd_product_description[16];
+	boolean present_3d;
+	boolean present_hdcp;
+	uint32 audio_data_blocks[16];
+	int (*read_edid_block)(int block, uint8 *edid_buf);
+	int (*hpd_feature)(int on);
+#endif
+};
+
+/* The external interface driver needs to initialize the common state. */
+extern struct external_common_state_type *external_common_state;
+extern struct mutex external_common_state_hpd_mutex;
+extern struct mutex hdmi_msm_state_mutex;
+
+#ifdef CONFIG_FB_MSM_HDMI_COMMON
+#define VFRMT_NOT_SUPPORTED(VFRMT) \
+	{VFRMT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, FALSE}
+#define HDMI_SETUP_LUT(MODE) do {					\
+		struct hdmi_disp_mode_timing_type mode			\
+			= HDMI_SETTINGS_ ## MODE;			\
+		hdmi_common_supported_video_mode_lut[mode.video_format]	\
+			= mode;						\
+	} while (0)
+
+int hdmi_common_read_edid(void);
+const char *video_format_2string(uint32 format);
+bool hdmi_common_get_video_format_from_drv_data(struct msm_fb_data_type *mfd);
+const struct hdmi_disp_mode_timing_type *hdmi_common_get_mode(uint32 mode);
+const struct hdmi_disp_mode_timing_type *hdmi_common_get_supported_mode(
+	uint32 mode);
+const struct hdmi_disp_mode_timing_type *hdmi_mhl_get_mode(uint32 mode);
+const struct hdmi_disp_mode_timing_type *hdmi_mhl_get_supported_mode(
+	uint32 mode);
+void hdmi_common_init_panel_info(struct msm_panel_info *pinfo);
+
+ssize_t video_3d_format_2string(uint32 format, char *buf);
+#endif
+
+int external_common_state_create(struct platform_device *pdev);
+void external_common_state_remove(void);
+
+#endif /* __EXTERNAL_COMMON_H__ */
diff --git a/drivers/video/msm/hdmi_msm.c b/drivers/video/msm/hdmi_msm.c
new file mode 100644
index 0000000..a372016
--- /dev/null
+++ b/drivers/video/msm/hdmi_msm.c
@@ -0,0 +1,4817 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/* #define DEBUG */
+#define DEV_DBG_PREFIX "HDMI: "
+/* #define REG_DUMP */
+
+#define CEC_MSG_PRINT
+#define TOGGLE_CEC_HARDWARE_FSM
+
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/mutex.h>
+#include <mach/msm_hdmi_audio.h>
+#include <mach/clk.h>
+#include <mach/msm_iomap.h>
+#include <mach/socinfo.h>
+
+#include "msm_fb.h"
+#include "hdmi_msm.h"
+
+/* Supported HDMI Audio channels */
+#define MSM_HDMI_AUDIO_CHANNEL_2		0
+#define MSM_HDMI_AUDIO_CHANNEL_4		1
+#define MSM_HDMI_AUDIO_CHANNEL_6		2
+#define MSM_HDMI_AUDIO_CHANNEL_8		3
+#define MSM_HDMI_AUDIO_CHANNEL_MAX		4
+#define MSM_HDMI_AUDIO_CHANNEL_FORCE_32BIT	0x7FFFFFFF
+
+/* Supported HDMI Audio sample rates */
+#define MSM_HDMI_SAMPLE_RATE_32KHZ		0
+#define MSM_HDMI_SAMPLE_RATE_44_1KHZ		1
+#define MSM_HDMI_SAMPLE_RATE_48KHZ		2
+#define MSM_HDMI_SAMPLE_RATE_88_2KHZ		3
+#define MSM_HDMI_SAMPLE_RATE_96KHZ		4
+#define MSM_HDMI_SAMPLE_RATE_176_4KHZ		5
+#define MSM_HDMI_SAMPLE_RATE_192KHZ		6
+#define MSM_HDMI_SAMPLE_RATE_MAX		7
+#define MSM_HDMI_SAMPLE_RATE_FORCE_32BIT	0x7FFFFFFF
+
+static int msm_hdmi_sample_rate = MSM_HDMI_SAMPLE_RATE_48KHZ;
+
+/* HDMI/HDCP Registers */
+#define HDCP_DDC_STATUS		0x0128
+#define HDCP_DDC_CTRL_0		0x0120
+#define HDCP_DDC_CTRL_1		0x0124
+#define HDMI_DDC_CTRL		0x020C
+
+struct workqueue_struct *hdmi_work_queue;
+struct hdmi_msm_state_type *hdmi_msm_state;
+
+DEFINE_MUTEX(hdmi_msm_state_mutex);
+EXPORT_SYMBOL(hdmi_msm_state_mutex);
+static DEFINE_MUTEX(hdcp_auth_state_mutex);
+
+static void hdmi_msm_dump_regs(const char *prefix);
+
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
+static void hdmi_msm_hdcp_enable(void);
+#else
+static inline void hdmi_msm_hdcp_enable(void) {}
+#endif
+
+static void hdmi_msm_turn_on(void);
+static int hdmi_msm_audio_off(void);
+static int hdmi_msm_read_edid(void);
+static void hdmi_msm_hpd_off(void);
+
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
+
+static void hdmi_msm_cec_line_latch_detect(void);
+
+#ifdef TOGGLE_CEC_HARDWARE_FSM
+static boolean msg_send_complete = TRUE;
+static boolean msg_recv_complete = TRUE;
+#endif
+
+#define HDMI_MSM_CEC_REFTIMER_REFTIMER_ENABLE	BIT(16)
+#define HDMI_MSM_CEC_REFTIMER_REFTIMER(___t)	(((___t)&0xFFFF) << 0)
+
+#define HDMI_MSM_CEC_TIME_SIGNAL_FREE_TIME(___t)	(((___t)&0x1FF) << 7)
+#define HDMI_MSM_CEC_TIME_ENABLE			BIT(0)
+
+#define HDMI_MSM_CEC_ADDR_LOGICAL_ADDR(___la)	(((___la)&0xFF) << 0)
+
+#define HDMI_MSM_CEC_CTRL_LINE_OE			BIT(9)
+#define HDMI_MSM_CEC_CTRL_FRAME_SIZE(___sz)		(((___sz)&0x1F) << 4)
+#define HDMI_MSM_CEC_CTRL_SOFT_RESET		BIT(2)
+#define HDMI_MSM_CEC_CTRL_SEND_TRIG			BIT(1)
+#define HDMI_MSM_CEC_CTRL_ENABLE			BIT(0)
+
+#define HDMI_MSM_CEC_INT_FRAME_RD_DONE_MASK		BIT(7)
+#define HDMI_MSM_CEC_INT_FRAME_RD_DONE_ACK		BIT(6)
+#define HDMI_MSM_CEC_INT_FRAME_RD_DONE_INT		BIT(6)
+#define HDMI_MSM_CEC_INT_MONITOR_MASK		BIT(5)
+#define HDMI_MSM_CEC_INT_MONITOR_ACK		BIT(4)
+#define HDMI_MSM_CEC_INT_MONITOR_INT		BIT(4)
+#define HDMI_MSM_CEC_INT_FRAME_ERROR_MASK		BIT(3)
+#define HDMI_MSM_CEC_INT_FRAME_ERROR_ACK		BIT(2)
+#define HDMI_MSM_CEC_INT_FRAME_ERROR_INT		BIT(2)
+#define HDMI_MSM_CEC_INT_FRAME_WR_DONE_MASK		BIT(1)
+#define HDMI_MSM_CEC_INT_FRAME_WR_DONE_ACK		BIT(0)
+#define HDMI_MSM_CEC_INT_FRAME_WR_DONE_INT		BIT(0)
+
+#define HDMI_MSM_CEC_FRAME_WR_SUCCESS(___st)         (((___st)&0xB) ==\
+		(HDMI_MSM_CEC_INT_FRAME_WR_DONE_INT |\
+			HDMI_MSM_CEC_INT_FRAME_WR_DONE_MASK |\
+			HDMI_MSM_CEC_INT_FRAME_ERROR_MASK))
+
+#define HDMI_MSM_CEC_RETRANSMIT_NUM(___num)		(((___num)&0xF) << 4)
+#define HDMI_MSM_CEC_RETRANSMIT_ENABLE		BIT(0)
+
+#define HDMI_MSM_CEC_WR_DATA_DATA(___d)		(((___d)&0xFF) << 8)
+
+
+void hdmi_msm_cec_init(void)
+{
+	/* 0x02A8 CEC_REFTIMER */
+	HDMI_OUTP(0x02A8,
+		HDMI_MSM_CEC_REFTIMER_REFTIMER_ENABLE
+		| HDMI_MSM_CEC_REFTIMER_REFTIMER(27 * 50)
+		);
+
+	/*
+	 * 0x02A0 CEC_ADDR
+	 * Starting with a default address of 4
+	 */
+	HDMI_OUTP(0x02A0, HDMI_MSM_CEC_ADDR_LOGICAL_ADDR(4));
+
+	hdmi_msm_state->first_monitor = 0;
+	hdmi_msm_state->fsm_reset_done = false;
+
+	/* 0x029C CEC_INT */
+	/* Enable CEC interrupts */
+	HDMI_OUTP(0x029C,					\
+		  HDMI_MSM_CEC_INT_FRAME_WR_DONE_MASK		\
+		  | HDMI_MSM_CEC_INT_FRAME_ERROR_MASK		\
+		  | HDMI_MSM_CEC_INT_MONITOR_MASK		\
+		  | HDMI_MSM_CEC_INT_FRAME_RD_DONE_MASK);
+
+	HDMI_OUTP(0x02B0, 0x7FF << 4 | 1);
+
+	/*
+	 * Slight adjustment to logic 1 low periods on read,
+	 * CEC Test 8.2-3 was failing, 8 for the
+	 * BIT_1_ERR_RANGE_HI = 8 => 750us, the test used 775us,
+	 * so increased this to 9 which => 800us.
+	 */
+	/*
+	 * CEC latch up issue - To fire monitor interrupt
+	 * for every start of message
+	 */
+	HDMI_OUTP(0x02E0, 0x880000);
+
+	/*
+	 * Slight adjustment to logic 0 low period on write
+	 */
+	HDMI_OUTP(0x02DC, 0x8888A888);
+
+	/*
+	 * Enable Signal Free Time counter and set to 7 bit periods
+	 */
+	HDMI_OUTP(0x02A4, 0x1 | (7 * 0x30) << 7);
+
+	/* 0x028C CEC_CTRL */
+	HDMI_OUTP(0x028C, HDMI_MSM_CEC_CTRL_ENABLE);
+}
+
+void hdmi_msm_cec_write_logical_addr(int addr)
+{
+	/* 0x02A0 CEC_ADDR
+	 *   LOGICAL_ADDR       7:0  NUM
+	 */
+	HDMI_OUTP(0x02A0, addr & 0xFF);
+}
+
+void hdmi_msm_dump_cec_msg(struct hdmi_msm_cec_msg *msg)
+{
+#ifdef CEC_MSG_PRINT
+	int i;
+	DEV_DBG("sender_id     : %d", msg->sender_id);
+	DEV_DBG("recvr_id     : %d", msg->recvr_id);
+	if (msg->frame_size < 2) {
+		DEV_DBG("polling message");
+		return;
+	}
+	DEV_DBG("opcode      : %02x", msg->opcode);
+	for (i = 0; i < msg->frame_size - 2; i++)
+		DEV_DBG("operand(%2d) : %02x", i + 1, msg->operand[i]);
+#endif /* CEC_MSG_PRINT */
+}
+
+void hdmi_msm_cec_msg_send(struct hdmi_msm_cec_msg *msg)
+{
+	int i;
+	uint32 timeout_count = 1;
+	int retry = 10;
+
+	boolean frameType = (msg->recvr_id == 15 ? BIT(0) : 0);
+
+	mutex_lock(&hdmi_msm_state_mutex);
+	hdmi_msm_state->fsm_reset_done = false;
+	mutex_unlock(&hdmi_msm_state_mutex);
+#ifdef TOGGLE_CEC_HARDWARE_FSM
+	msg_send_complete = FALSE;
+#endif
+
+	INIT_COMPLETION(hdmi_msm_state->cec_frame_wr_done);
+	hdmi_msm_state->cec_frame_wr_status = 0;
+
+	/* 0x0294 HDMI_MSM_CEC_RETRANSMIT */
+	HDMI_OUTP(0x0294,
+#ifdef DRVR_ONLY_CECT_NO_DAEMON
+		HDMI_MSM_CEC_RETRANSMIT_NUM(msg->retransmit)
+		| (msg->retransmit > 0) ? HDMI_MSM_CEC_RETRANSMIT_ENABLE : 0);
+#else
+		HDMI_MSM_CEC_RETRANSMIT_NUM(0) |
+			HDMI_MSM_CEC_RETRANSMIT_ENABLE);
+#endif
+
+	/* 0x028C CEC_CTRL */
+	HDMI_OUTP(0x028C, 0x1 | msg->frame_size << 4);
+
+	/* 0x0290 CEC_WR_DATA */
+
+	/* header block */
+	HDMI_OUTP(0x0290,
+		HDMI_MSM_CEC_WR_DATA_DATA(msg->sender_id << 4 | msg->recvr_id)
+		| frameType);
+
+	/* data block 0 : opcode */
+	HDMI_OUTP(0x0290,
+		HDMI_MSM_CEC_WR_DATA_DATA(msg->frame_size < 2 ? 0 : msg->opcode)
+		| frameType);
+
+	/* data block 1-14 : operand 0-13 */
+	for (i = 0; i < msg->frame_size - 1; i++)
+		HDMI_OUTP(0x0290,
+			HDMI_MSM_CEC_WR_DATA_DATA(msg->operand[i])
+			| (msg->recvr_id == 15 ? BIT(0) : 0));
+
+	for (; i < 14; i++)
+		HDMI_OUTP(0x0290,
+			HDMI_MSM_CEC_WR_DATA_DATA(0)
+			| (msg->recvr_id == 15 ? BIT(0) : 0));
+
+	while ((HDMI_INP(0x0298) & 1) && retry--) {
+		DEV_DBG("CEC line is busy(%d)\n", retry);
+		schedule();
+	}
+
+	/* 0x028C CEC_CTRL */
+	HDMI_OUTP(0x028C,
+		  HDMI_MSM_CEC_CTRL_LINE_OE
+		  | HDMI_MSM_CEC_CTRL_FRAME_SIZE(msg->frame_size)
+		  | HDMI_MSM_CEC_CTRL_SEND_TRIG
+		  | HDMI_MSM_CEC_CTRL_ENABLE);
+
+	timeout_count = wait_for_completion_interruptible_timeout(
+		&hdmi_msm_state->cec_frame_wr_done, HZ);
+
+	if (!timeout_count) {
+		hdmi_msm_state->cec_frame_wr_status |= CEC_STATUS_WR_TMOUT;
+		DEV_ERR("%s: timedout", __func__);
+		hdmi_msm_dump_cec_msg(msg);
+	} else {
+		DEV_DBG("CEC write frame done (frame len=%d)",
+			msg->frame_size);
+		hdmi_msm_dump_cec_msg(msg);
+	}
+
+#ifdef TOGGLE_CEC_HARDWARE_FSM
+	if (!msg_recv_complete) {
+		/* Toggle CEC hardware FSM */
+		HDMI_OUTP(0x028C, 0x0);
+		HDMI_OUTP(0x028C, HDMI_MSM_CEC_CTRL_ENABLE);
+		msg_recv_complete = TRUE;
+	}
+	msg_send_complete = TRUE;
+#else
+	HDMI_OUTP(0x028C, 0x0);
+	HDMI_OUTP(0x028C, HDMI_MSM_CEC_CTRL_ENABLE);
+#endif
+}
+
+void hdmi_msm_cec_line_latch_detect(void)
+{
+	/*
+	 * CECT 9-5-1
+	 * The timer period needs to be changed to appropriate value
+	 */
+	/*
+	 * Timedout without RD_DONE, WR_DONE or ERR_INT
+	 * Toggle CEC hardware FSM
+	 */
+	mutex_lock(&hdmi_msm_state_mutex);
+	if (hdmi_msm_state->first_monitor == 1) {
+		DEV_WARN("CEC line is probably latched up - CECT 9-5-1");
+		if (!msg_recv_complete)
+			hdmi_msm_state->fsm_reset_done = true;
+		HDMI_OUTP(0x028C, 0x0);
+		HDMI_OUTP(0x028C, HDMI_MSM_CEC_CTRL_ENABLE);
+		hdmi_msm_state->first_monitor = 0;
+	}
+	mutex_unlock(&hdmi_msm_state_mutex);
+}
+
+void hdmi_msm_cec_msg_recv(void)
+{
+	uint32 data;
+	int i;
+#ifdef DRVR_ONLY_CECT_NO_DAEMON
+	struct hdmi_msm_cec_msg temp_msg;
+#endif
+	mutex_lock(&hdmi_msm_state_mutex);
+	if (hdmi_msm_state->cec_queue_wr == hdmi_msm_state->cec_queue_rd
+		&& hdmi_msm_state->cec_queue_full) {
+		mutex_unlock(&hdmi_msm_state_mutex);
+		DEV_ERR("CEC message queue is overflowing\n");
+#ifdef DRVR_ONLY_CECT_NO_DAEMON
+		/*
+		 * Without CEC daemon:
+		 * Compliance tests fail once the queue gets filled up.
+		 * so reset the pointers to the start of the queue.
+		 */
+		hdmi_msm_state->cec_queue_wr = hdmi_msm_state->cec_queue_start;
+		hdmi_msm_state->cec_queue_rd = hdmi_msm_state->cec_queue_start;
+		hdmi_msm_state->cec_queue_full = false;
+#else
+		return;
+#endif
+	}
+	if (hdmi_msm_state->cec_queue_wr == NULL) {
+		DEV_ERR("%s: wp is NULL\n", __func__);
+		return;
+	}
+	mutex_unlock(&hdmi_msm_state_mutex);
+
+	/* 0x02AC CEC_RD_DATA */
+	data = HDMI_INP(0x02AC);
+
+	hdmi_msm_state->cec_queue_wr->sender_id = (data & 0xF0) >> 4;
+	hdmi_msm_state->cec_queue_wr->recvr_id = (data & 0x0F);
+	hdmi_msm_state->cec_queue_wr->frame_size = (data & 0x1F00) >> 8;
+	DEV_DBG("Recvd init=[%u] dest=[%u] size=[%u]\n",
+		hdmi_msm_state->cec_queue_wr->sender_id,
+		hdmi_msm_state->cec_queue_wr->recvr_id,
+		hdmi_msm_state->cec_queue_wr->frame_size);
+
+	if (hdmi_msm_state->cec_queue_wr->frame_size < 1) {
+		DEV_ERR("%s: invalid message (frame length = %d)",
+			__func__, hdmi_msm_state->cec_queue_wr->frame_size);
+		return;
+	} else if (hdmi_msm_state->cec_queue_wr->frame_size == 1) {
+		DEV_DBG("%s: polling message (dest[%x] <- init[%x])",
+			__func__,
+			hdmi_msm_state->cec_queue_wr->recvr_id,
+			hdmi_msm_state->cec_queue_wr->sender_id);
+		return;
+	}
+
+	/* data block 0 : opcode */
+	data = HDMI_INP(0x02AC);
+	hdmi_msm_state->cec_queue_wr->opcode = data & 0xFF;
+
+	/* data block 1-14 : operand 0-13 */
+	for (i = 0; i < hdmi_msm_state->cec_queue_wr->frame_size - 2; i++) {
+		data = HDMI_INP(0x02AC);
+		hdmi_msm_state->cec_queue_wr->operand[i] = data & 0xFF;
+	}
+
+	for (; i < 14; i++)
+		hdmi_msm_state->cec_queue_wr->operand[i] = 0;
+
+	DEV_DBG("CEC read frame done\n");
+	DEV_DBG("=======================================\n");
+	hdmi_msm_dump_cec_msg(hdmi_msm_state->cec_queue_wr);
+	DEV_DBG("=======================================\n");
+
+#ifdef DRVR_ONLY_CECT_NO_DAEMON
+	switch (hdmi_msm_state->cec_queue_wr->opcode) {
+	case 0x64:
+		/* Set OSD String */
+		DEV_INFO("Recvd OSD Str=[%x]\n",\
+			hdmi_msm_state->cec_queue_wr->operand[3]);
+		break;
+	case 0x83:
+		/* Give Phy Addr */
+		DEV_INFO("Recvd a Give Phy Addr cmd\n");
+		memset(&temp_msg, 0x00, sizeof(struct hdmi_msm_cec_msg));
+		/* Setup a frame for sending out phy addr */
+		temp_msg.sender_id = 0x4;
+
+		/* Broadcast */
+		temp_msg.recvr_id = 0xf;
+		temp_msg.opcode = 0x84;
+		i = 0;
+		temp_msg.operand[i++] = 0x10;
+		temp_msg.operand[i++] = 0x00;
+		temp_msg.operand[i++] = 0x04;
+		temp_msg.frame_size = i + 2;
+		hdmi_msm_cec_msg_send(&temp_msg);
+		break;
+	case 0xFF:
+		/* Abort */
+		DEV_INFO("Recvd an abort cmd 0xFF\n");
+		memset(&temp_msg, 0x00, sizeof(struct hdmi_msm_cec_msg));
+		temp_msg.sender_id = 0x4;
+		temp_msg.recvr_id = hdmi_msm_state->cec_queue_wr->sender_id;
+		i = 0;
+
+		/*feature abort */
+		temp_msg.opcode = 0x00;
+		temp_msg.operand[i++] =
+			hdmi_msm_state->cec_queue_wr->opcode;
+
+		/*reason for abort = "Refused" */
+		temp_msg.operand[i++] = 0x04;
+		temp_msg.frame_size = i + 2;
+		hdmi_msm_dump_cec_msg(&temp_msg);
+		hdmi_msm_cec_msg_send(&temp_msg);
+		break;
+	case 0x046:
+		/* Give OSD name */
+		DEV_INFO("Recvd cmd 0x046\n");
+		memset(&temp_msg, 0x00, sizeof(struct hdmi_msm_cec_msg));
+		temp_msg.sender_id = 0x4;
+		temp_msg.recvr_id = hdmi_msm_state->cec_queue_wr->sender_id;
+		i = 0;
+
+		/* OSD Name */
+		temp_msg.opcode = 0x47;
+
+		/* Display control byte */
+		temp_msg.operand[i++] = 0x00;
+		temp_msg.operand[i++] = 'H';
+		temp_msg.operand[i++] = 'e';
+		temp_msg.operand[i++] = 'l';
+		temp_msg.operand[i++] = 'l';
+		temp_msg.operand[i++] = 'o';
+		temp_msg.operand[i++] = ' ';
+		temp_msg.operand[i++] = 'W';
+		temp_msg.operand[i++] = 'o';
+		temp_msg.operand[i++] = 'r';
+		temp_msg.operand[i++] = 'l';
+		temp_msg.operand[i++] = 'd';
+		temp_msg.frame_size = i + 2;
+		hdmi_msm_cec_msg_send(&temp_msg);
+		break;
+	case 0x08F:
+		/* Give Device Power status */
+		DEV_INFO("Recvd a Power status message\n");
+		memset(&temp_msg, 0x00, sizeof(struct hdmi_msm_cec_msg));
+		temp_msg.sender_id = 0x4;
+		temp_msg.recvr_id = hdmi_msm_state->cec_queue_wr->sender_id;
+		i = 0;
+
+		/* OSD String */
+		temp_msg.opcode = 0x90;
+		temp_msg.operand[i++] = 'H';
+		temp_msg.operand[i++] = 'e';
+		temp_msg.operand[i++] = 'l';
+		temp_msg.operand[i++] = 'l';
+		temp_msg.operand[i++] = 'o';
+		temp_msg.operand[i++] = ' ';
+		temp_msg.operand[i++] = 'W';
+		temp_msg.operand[i++] = 'o';
+		temp_msg.operand[i++] = 'r';
+		temp_msg.operand[i++] = 'l';
+		temp_msg.operand[i++] = 'd';
+		temp_msg.frame_size = i + 2;
+		hdmi_msm_cec_msg_send(&temp_msg);
+		break;
+	case 0x080:
+		/* Routing Change cmd */
+	case 0x086:
+		/* Set Stream Path */
+		DEV_INFO("Recvd Set Stream\n");
+		memset(&temp_msg, 0x00, sizeof(struct hdmi_msm_cec_msg));
+		temp_msg.sender_id = 0x4;
+
+		/*Broadcast this message*/
+		temp_msg.recvr_id = 0xf;
+		i = 0;
+		temp_msg.opcode = 0x82; /* Active Source */
+		temp_msg.operand[i++] = 0x10;
+		temp_msg.operand[i++] = 0x00;
+		temp_msg.frame_size = i + 2;
+		hdmi_msm_cec_msg_send(&temp_msg);
+
+		/*
+		 * sending <Image View On> message
+		 */
+		memset(&temp_msg, 0x00, sizeof(struct hdmi_msm_cec_msg));
+		temp_msg.sender_id = 0x4;
+		temp_msg.recvr_id = hdmi_msm_state->cec_queue_wr->sender_id;
+		i = 0;
+		/* opcode for Image View On */
+		temp_msg.opcode = 0x04;
+		temp_msg.frame_size = i + 2;
+		hdmi_msm_cec_msg_send(&temp_msg);
+		break;
+	case 0x44:
+		/* User Control Pressed */
+		DEV_INFO("User Control Pressed\n");
+		break;
+	case 0x45:
+		/* User Control Released */
+		DEV_INFO("User Control Released\n");
+		break;
+	default:
+		DEV_INFO("Recvd an unknown cmd = [%u]\n",
+			hdmi_msm_state->cec_queue_wr->opcode);
+#ifdef __SEND_ABORT__
+		memset(&temp_msg, 0x00, sizeof(struct hdmi_msm_cec_msg));
+		temp_msg.sender_id = 0x4;
+		temp_msg.recvr_id = hdmi_msm_state->cec_queue_wr->sender_id;
+		i = 0;
+		/* opcode for feature abort */
+		temp_msg.opcode = 0x00;
+		temp_msg.operand[i++] =
+			hdmi_msm_state->cec_queue_wr->opcode;
+		/*reason for abort = "Unrecognized opcode" */
+		temp_msg.operand[i++] = 0x00;
+		temp_msg.frame_size = i + 2;
+		hdmi_msm_cec_msg_send(&temp_msg);
+		break;
+#else
+		memset(&temp_msg, 0x00, sizeof(struct hdmi_msm_cec_msg));
+		temp_msg.sender_id = 0x4;
+		temp_msg.recvr_id = hdmi_msm_state->cec_queue_wr->sender_id;
+		i = 0;
+		/* OSD String */
+		temp_msg.opcode = 0x64;
+		temp_msg.operand[i++] = 0x0;
+		temp_msg.operand[i++] = 'H';
+		temp_msg.operand[i++] = 'e';
+		temp_msg.operand[i++] = 'l';
+		temp_msg.operand[i++] = 'l';
+		temp_msg.operand[i++] = 'o';
+		temp_msg.operand[i++] = ' ';
+		temp_msg.operand[i++] = 'W';
+		temp_msg.operand[i++] = 'o';
+		temp_msg.operand[i++] = 'r';
+		temp_msg.operand[i++] = 'l';
+		temp_msg.operand[i++] = 'd';
+		temp_msg.frame_size = i + 2;
+		hdmi_msm_cec_msg_send(&temp_msg);
+		break;
+#endif /* __SEND_ABORT__ */
+	}
+
+#endif /* DRVR_ONLY_CECT_NO_DAEMON */
+	mutex_lock(&hdmi_msm_state_mutex);
+	hdmi_msm_state->cec_queue_wr++;
+	if (hdmi_msm_state->cec_queue_wr == CEC_QUEUE_END)
+		hdmi_msm_state->cec_queue_wr = hdmi_msm_state->cec_queue_start;
+	if (hdmi_msm_state->cec_queue_wr == hdmi_msm_state->cec_queue_rd)
+		hdmi_msm_state->cec_queue_full = true;
+	mutex_unlock(&hdmi_msm_state_mutex);
+	DEV_DBG("Exiting %s()\n", __func__);
+}
+
+void hdmi_msm_cec_one_touch_play(void)
+{
+	struct hdmi_msm_cec_msg temp_msg;
+	uint32 i = 0;
+	memset(&temp_msg, 0x00, sizeof(struct hdmi_msm_cec_msg));
+	temp_msg.sender_id = 0x4;
+	/*
+	 * Broadcast this message
+	 */
+	temp_msg.recvr_id = 0xf;
+	i = 0;
+	/* Active Source */
+	temp_msg.opcode = 0x82;
+	temp_msg.operand[i++] = 0x10;
+	temp_msg.operand[i++] = 0x00;
+	/*temp_msg.operand[i++] = 0x04;*/
+	temp_msg.frame_size = i + 2;
+	hdmi_msm_cec_msg_send(&temp_msg);
+	/*
+	 * sending <Image View On> message
+	 */
+	memset(&temp_msg, 0x00, sizeof(struct hdmi_msm_cec_msg));
+	temp_msg.sender_id = 0x4;
+	temp_msg.recvr_id = hdmi_msm_state->cec_queue_wr->sender_id;
+	i = 0;
+	/* Image View On */
+	temp_msg.opcode = 0x04;
+	temp_msg.frame_size = i + 2;
+	hdmi_msm_cec_msg_send(&temp_msg);
+
+}
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT */
+
+uint32 hdmi_msm_get_io_base(void)
+{
+	return (uint32)MSM_HDMI_BASE;
+}
+EXPORT_SYMBOL(hdmi_msm_get_io_base);
+
+/* Table indicating the video format supported by the HDMI TX Core v1.0 */
+/* Valid Pixel-Clock rates: 25.2MHz, 27MHz, 27.03MHz, 74.25MHz, 148.5MHz */
+static void hdmi_msm_setup_video_mode_lut(void)
+{
+	HDMI_SETUP_LUT(640x480p60_4_3);
+	HDMI_SETUP_LUT(720x480p60_4_3);
+	HDMI_SETUP_LUT(720x480p60_16_9);
+	HDMI_SETUP_LUT(1280x720p60_16_9);
+	HDMI_SETUP_LUT(1920x1080i60_16_9);
+	HDMI_SETUP_LUT(1440x480i60_4_3);
+	HDMI_SETUP_LUT(1440x480i60_16_9);
+	HDMI_SETUP_LUT(1920x1080p60_16_9);
+	HDMI_SETUP_LUT(720x576p50_4_3);
+	HDMI_SETUP_LUT(720x576p50_16_9);
+	HDMI_SETUP_LUT(1280x720p50_16_9);
+	HDMI_SETUP_LUT(1440x576i50_4_3);
+	HDMI_SETUP_LUT(1440x576i50_16_9);
+	HDMI_SETUP_LUT(1920x1080p50_16_9);
+	HDMI_SETUP_LUT(1920x1080p24_16_9);
+	HDMI_SETUP_LUT(1920x1080p25_16_9);
+	HDMI_SETUP_LUT(1920x1080p30_16_9);
+}
+
+#ifdef PORT_DEBUG
+const char *hdmi_msm_name(uint32 offset)
+{
+	switch (offset) {
+	case 0x0000: return "CTRL";
+	case 0x0020: return "AUDIO_PKT_CTRL1";
+	case 0x0024: return "ACR_PKT_CTRL";
+	case 0x0028: return "VBI_PKT_CTRL";
+	case 0x002C: return "INFOFRAME_CTRL0";
+#ifdef CONFIG_FB_MSM_HDMI_3D
+	case 0x0034: return "GEN_PKT_CTRL";
+#endif
+	case 0x003C: return "ACP";
+	case 0x0040: return "GC";
+	case 0x0044: return "AUDIO_PKT_CTRL2";
+	case 0x0048: return "ISRC1_0";
+	case 0x004C: return "ISRC1_1";
+	case 0x0050: return "ISRC1_2";
+	case 0x0054: return "ISRC1_3";
+	case 0x0058: return "ISRC1_4";
+	case 0x005C: return "ISRC2_0";
+	case 0x0060: return "ISRC2_1";
+	case 0x0064: return "ISRC2_2";
+	case 0x0068: return "ISRC2_3";
+	case 0x006C: return "AVI_INFO0";
+	case 0x0070: return "AVI_INFO1";
+	case 0x0074: return "AVI_INFO2";
+	case 0x0078: return "AVI_INFO3";
+#ifdef CONFIG_FB_MSM_HDMI_3D
+	case 0x0084: return "GENERIC0_HDR";
+	case 0x0088: return "GENERIC0_0";
+	case 0x008C: return "GENERIC0_1";
+#endif
+	case 0x00C4: return "ACR_32_0";
+	case 0x00C8: return "ACR_32_1";
+	case 0x00CC: return "ACR_44_0";
+	case 0x00D0: return "ACR_44_1";
+	case 0x00D4: return "ACR_48_0";
+	case 0x00D8: return "ACR_48_1";
+	case 0x00E4: return "AUDIO_INFO0";
+	case 0x00E8: return "AUDIO_INFO1";
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
+	case 0x0110: return "HDCP_CTRL";
+	case 0x0114: return "HDCP_DEBUG_CTRL";
+	case 0x0118: return "HDCP_INT_CTRL";
+	case 0x011C: return "HDCP_LINK0_STATUS";
+	case 0x012C: return "HDCP_ENTROPY_CTRL0";
+	case 0x0130: return "HDCP_RESET";
+	case 0x0134: return "HDCP_RCVPORT_DATA0";
+	case 0x0138: return "HDCP_RCVPORT_DATA1";
+	case 0x013C: return "HDCP_RCVPORT_DATA2";
+	case 0x0144: return "HDCP_RCVPORT_DATA3";
+	case 0x0148: return "HDCP_RCVPORT_DATA4";
+	case 0x014C: return "HDCP_RCVPORT_DATA5";
+	case 0x0150: return "HDCP_RCVPORT_DATA6";
+	case 0x0168: return "HDCP_RCVPORT_DATA12";
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
+	case 0x01D0: return "AUDIO_CFG";
+	case 0x0208: return "USEC_REFTIMER";
+	case 0x020C: return "DDC_CTRL";
+	case 0x0214: return "DDC_INT_CTRL";
+	case 0x0218: return "DDC_SW_STATUS";
+	case 0x021C: return "DDC_HW_STATUS";
+	case 0x0220: return "DDC_SPEED";
+	case 0x0224: return "DDC_SETUP";
+	case 0x0228: return "DDC_TRANS0";
+	case 0x022C: return "DDC_TRANS1";
+	case 0x0238: return "DDC_DATA";
+	case 0x0250: return "HPD_INT_STATUS";
+	case 0x0254: return "HPD_INT_CTRL";
+	case 0x0258: return "HPD_CTRL";
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
+	case 0x025C: return "HDCP_ENTROPY_CTRL1";
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
+	case 0x027C: return "DDC_REF";
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
+	case 0x0284: return "HDCP_SW_UPPER_AKSV";
+	case 0x0288: return "HDCP_SW_LOWER_AKSV";
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
+	case 0x02B4: return "ACTIVE_H";
+	case 0x02B8: return "ACTIVE_V";
+	case 0x02BC: return "ACTIVE_V_F2";
+	case 0x02C0: return "TOTAL";
+	case 0x02C4: return "V_TOTAL_F2";
+	case 0x02C8: return "FRAME_CTRL";
+	case 0x02CC: return "AUD_INT";
+	case 0x0300: return "PHY_REG0";
+	case 0x0304: return "PHY_REG1";
+	case 0x0308: return "PHY_REG2";
+	case 0x030C: return "PHY_REG3";
+	case 0x0310: return "PHY_REG4";
+	case 0x0314: return "PHY_REG5";
+	case 0x0318: return "PHY_REG6";
+	case 0x031C: return "PHY_REG7";
+	case 0x0320: return "PHY_REG8";
+	case 0x0324: return "PHY_REG9";
+	case 0x0328: return "PHY_REG10";
+	case 0x032C: return "PHY_REG11";
+	case 0x0330: return "PHY_REG12";
+	default: return "???";
+	}
+}
+
+void hdmi_outp(uint32 offset, uint32 value)
+{
+	uint32 in_val;
+
+	outpdw(MSM_HDMI_BASE+offset, value);
+	in_val = inpdw(MSM_HDMI_BASE+offset);
+	DEV_DBG("HDMI[%04x] => %08x [%08x] %s\n",
+		offset, value, in_val, hdmi_msm_name(offset));
+}
+
+uint32 hdmi_inp(uint32 offset)
+{
+	uint32 value = inpdw(MSM_HDMI_BASE+offset);
+	DEV_DBG("HDMI[%04x] <= %08x %s\n",
+		offset, value, hdmi_msm_name(offset));
+	return value;
+}
+#endif /* DEBUG */
+
+static void hdmi_msm_turn_on(void);
+static int hdmi_msm_audio_off(void);
+static int hdmi_msm_read_edid(void);
+static void hdmi_msm_hpd_off(void);
+
+static void hdmi_msm_hpd_state_work(struct work_struct *work)
+{
+	boolean hpd_state;
+	char *envp[2];
+
+	if (!hdmi_msm_state || !hdmi_msm_state->hpd_initialized ||
+		!MSM_HDMI_BASE) {
+		DEV_DBG("%s: ignored, probe failed\n", __func__);
+		return;
+	}
+
+	DEV_DBG("%s:Got interrupt\n", __func__);
+	/* HPD_INT_STATUS[0x0250] */
+	hpd_state = (HDMI_INP(0x0250) & 0x2) >> 1;
+	mutex_lock(&external_common_state_hpd_mutex);
+	mutex_lock(&hdmi_msm_state_mutex);
+	if ((external_common_state->hpd_state != hpd_state) || (hdmi_msm_state->
+			hpd_prev_state != external_common_state->hpd_state)) {
+		external_common_state->hpd_state = hpd_state;
+		hdmi_msm_state->hpd_prev_state =
+				external_common_state->hpd_state;
+		DEV_DBG("%s: state not stable yet, wait again (%d|%d|%d)\n",
+			__func__, hdmi_msm_state->hpd_prev_state,
+			external_common_state->hpd_state, hpd_state);
+		mutex_unlock(&external_common_state_hpd_mutex);
+		hdmi_msm_state->hpd_stable = 0;
+		mutex_unlock(&hdmi_msm_state_mutex);
+		mod_timer(&hdmi_msm_state->hpd_state_timer, jiffies + HZ/2);
+		return;
+	}
+	mutex_unlock(&external_common_state_hpd_mutex);
+
+	if (hdmi_msm_state->hpd_stable++) {
+		mutex_unlock(&hdmi_msm_state_mutex);
+		DEV_DBG("%s: no more timer, depending for IRQ now\n",
+			__func__);
+		return;
+	}
+
+	hdmi_msm_state->hpd_stable = 1;
+	DEV_INFO("HDMI HPD: event detected\n");
+
+	if (!hdmi_msm_state->hpd_cable_chg_detected) {
+		mutex_unlock(&hdmi_msm_state_mutex);
+		if (hpd_state) {
+			if (!external_common_state->
+					disp_mode_list.num_of_elements)
+				hdmi_msm_read_edid();
+			hdmi_msm_turn_on();
+		}
+	} else {
+		hdmi_msm_state->hpd_cable_chg_detected = FALSE;
+		mutex_unlock(&hdmi_msm_state_mutex);
+		/* QDSP OFF preceding the HPD event notification */
+		envp[0] = "HDCP_STATE=FAIL";
+		envp[1] = NULL;
+		DEV_INFO("HDMI HPD: QDSP OFF\n");
+		kobject_uevent_env(external_common_state->uevent_kobj,
+				   KOBJ_CHANGE, envp);
+		switch_set_state(&external_common_state->sdev, 0);
+		DEV_INFO("Hdmi state switch to %d: %s\n",
+			external_common_state->sdev.state,  __func__);
+		if (hpd_state) {
+			hdmi_msm_read_edid();
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
+			hdmi_msm_state->reauth = FALSE ;
+#endif
+			/* Build EDID table */
+			hdmi_msm_turn_on();
+			DEV_INFO("HDMI HPD: sense CONNECTED: send ONLINE\n");
+			kobject_uevent(external_common_state->uevent_kobj,
+				KOBJ_ONLINE);
+			hdmi_msm_hdcp_enable();
+#ifndef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
+			/* Send Audio for HDMI Compliance Cases*/
+			envp[0] = "HDCP_STATE=PASS";
+			envp[1] = NULL;
+			DEV_INFO("HDMI HPD: sense : send HDCP_PASS\n");
+			kobject_uevent_env(external_common_state->uevent_kobj,
+				KOBJ_CHANGE, envp);
+			switch_set_state(&external_common_state->sdev, 1);
+			DEV_INFO("Hdmi state switch to %d: %s\n",
+				external_common_state->sdev.state, __func__);
+#endif
+		} else {
+			DEV_INFO("HDMI HPD: sense DISCONNECTED: send OFFLINE\n"
+				);
+			kobject_uevent(external_common_state->uevent_kobj,
+				KOBJ_OFFLINE);
+			switch_set_state(&external_common_state->sdev, 0);
+			DEV_INFO("Hdmi state switch to %d: %s\n",
+				external_common_state->sdev.state,  __func__);
+		}
+	}
+
+	/* HPD_INT_CTRL[0x0254]
+	 *   31:10 Reserved
+	 *   9     RCV_PLUGIN_DET_MASK	receiver plug in interrupt mask.
+	 *                              When programmed to 1,
+	 *                              RCV_PLUGIN_DET_INT will toggle
+	 *                              the interrupt line
+	 *   8:6   Reserved
+	 *   5     RX_INT_EN		Panel RX interrupt enable
+	 *         0: Disable
+	 *         1: Enable
+	 *   4     RX_INT_ACK		WRITE ONLY. Panel RX interrupt
+	 *                              ack
+	 *   3     Reserved
+	 *   2     INT_EN		Panel interrupt control
+	 *         0: Disable
+	 *         1: Enable
+	 *   1     INT_POLARITY		Panel interrupt polarity
+	 *         0: generate interrupt on disconnect
+	 *         1: generate interrupt on connect
+	 *   0     INT_ACK		WRITE ONLY. Panel interrupt ack */
+	/* Set IRQ for HPD */
+	HDMI_OUTP(0x0254, 4 | (hpd_state ? 0 : 2));
+}
+
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
+static void hdmi_msm_cec_latch_work(struct work_struct *work)
+{
+	hdmi_msm_cec_line_latch_detect();
+}
+#endif
+
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
+static void hdcp_deauthenticate(void);
+static void hdmi_msm_hdcp_reauth_work(struct work_struct *work)
+{
+
+	/* Don't process recursive actions */
+	mutex_lock(&hdmi_msm_state_mutex);
+	if (hdmi_msm_state->hdcp_activating) {
+		mutex_unlock(&hdmi_msm_state_mutex);
+		return;
+	}
+	mutex_unlock(&hdmi_msm_state_mutex);
+
+	/*
+	 * Reauth=>deauth, hdcp_auth
+	 * hdcp_auth=>turn_on() which calls
+	 * HDMI Core reset without informing the Audio QDSP
+	 * this can do bad things to video playback on the HDTV
+	 * Therefore, as surprising as it may sound do reauth
+	 * only if the device is HDCP-capable
+	 */
+	if (external_common_state->present_hdcp) {
+		hdcp_deauthenticate();
+		mod_timer(&hdmi_msm_state->hdcp_timer, jiffies + HZ/2);
+	}
+}
+
+static void hdmi_msm_hdcp_work(struct work_struct *work)
+{
+
+	/* Only re-enable if cable still connected */
+	mutex_lock(&external_common_state_hpd_mutex);
+	if (external_common_state->hpd_state &&
+	    !(hdmi_msm_state->full_auth_done)) {
+		mutex_unlock(&external_common_state_hpd_mutex);
+		hdmi_msm_state->reauth = TRUE;
+		hdmi_msm_turn_on();
+	} else
+		mutex_unlock(&external_common_state_hpd_mutex);
+}
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
+
+static irqreturn_t hdmi_msm_isr(int irq, void *dev_id)
+{
+	uint32 hpd_int_status;
+	uint32 hpd_int_ctrl;
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
+	uint32 cec_intr_status;
+#endif
+	uint32 ddc_int_ctrl;
+	uint32 audio_int_val;
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
+	uint32 hdcp_int_val;
+	char *envp[2];
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
+	static uint32 fifo_urun_int_occurred;
+	static uint32 sample_drop_int_occurred;
+	const uint32 occurrence_limit = 5;
+
+	if (!hdmi_msm_state || !hdmi_msm_state->hpd_initialized ||
+		!MSM_HDMI_BASE) {
+		DEV_DBG("ISR ignored, probe failed\n");
+		return IRQ_HANDLED;
+	}
+
+	/* Process HPD Interrupt */
+	/* HDMI_HPD_INT_STATUS[0x0250] */
+	hpd_int_status = HDMI_INP_ND(0x0250);
+	/* HDMI_HPD_INT_CTRL[0x0254] */
+	hpd_int_ctrl = HDMI_INP_ND(0x0254);
+	if ((hpd_int_ctrl & (1 << 2)) && (hpd_int_status & (1 << 0))) {
+		boolean cable_detected = (hpd_int_status & 2) >> 1;
+
+		/* HDMI_HPD_INT_CTRL[0x0254] */
+		/* Clear all interrupts, timer will turn IRQ back on
+		 * Leaving the bit[2] on, else core goes off
+		 * on getting HPD during power off
+		 */
+		HDMI_OUTP(0x0254, (1 << 2) | (1 << 0));
+
+		DEV_DBG("%s: HPD IRQ, Ctrl=%04x, State=%04x\n", __func__,
+			hpd_int_ctrl, hpd_int_status);
+		mutex_lock(&hdmi_msm_state_mutex);
+		hdmi_msm_state->hpd_cable_chg_detected = TRUE;
+
+		/* ensure 2 readouts */
+		hdmi_msm_state->hpd_prev_state = cable_detected ? 0 : 1;
+		external_common_state->hpd_state = cable_detected ? 1 : 0;
+		hdmi_msm_state->hpd_stable = 0;
+		mod_timer(&hdmi_msm_state->hpd_state_timer, jiffies + HZ/2);
+		mutex_unlock(&hdmi_msm_state_mutex);
+		/*
+		 * HDCP Compliance 1A-01:
+		 * The Quantum Data Box 882 triggers two consecutive
+		 * HPD events very close to each other as a part of this
+		 * test which can trigger two parallel HDCP auth threads
+		 * if HDCP authentication is going on and we get ISR
+		 * then stop the authentication , rather than
+		 * reauthenticating it again
+		 */
+		if (!(hdmi_msm_state->full_auth_done)) {
+			DEV_DBG("%s getting hpd while authenticating\n",\
+			    __func__);
+			mutex_lock(&hdcp_auth_state_mutex);
+			hdmi_msm_state->hpd_during_auth = TRUE;
+			mutex_unlock(&hdcp_auth_state_mutex);
+		}
+		return IRQ_HANDLED;
+	}
+
+	/* Process DDC Interrupts */
+	/* HDMI_DDC_INT_CTRL[0x0214] */
+	ddc_int_ctrl = HDMI_INP_ND(0x0214);
+	if ((ddc_int_ctrl & (1 << 2)) && (ddc_int_ctrl & (1 << 0))) {
+		/* SW_DONE INT occured, clr it */
+		HDMI_OUTP_ND(0x0214, ddc_int_ctrl | (1 << 1));
+		complete(&hdmi_msm_state->ddc_sw_done);
+		return IRQ_HANDLED;
+	}
+
+	/* FIFO Underrun Int is enabled */
+	/* HDMI_AUD_INT[0x02CC]
+	 *   [3] AUD_SAM_DROP_MASK [R/W]
+	 *   [2] AUD_SAM_DROP_ACK [W], AUD_SAM_DROP_INT [R]
+	 *   [1] AUD_FIFO_URUN_MASK [R/W]
+	 *   [0] AUD_FIFO_URUN_ACK [W], AUD_FIFO_URUN_INT [R] */
+	audio_int_val = HDMI_INP_ND(0x02CC);
+	if ((audio_int_val & (1 << 1)) && (audio_int_val & (1 << 0))) {
+		/* FIFO Underrun occured, clr it */
+		HDMI_OUTP(0x02CC, audio_int_val | (1 << 0));
+
+		++fifo_urun_int_occurred;
+		DEV_INFO("HDMI AUD_FIFO_URUN: %d\n", fifo_urun_int_occurred);
+
+		if (fifo_urun_int_occurred >= occurrence_limit) {
+			HDMI_OUTP(0x02CC, HDMI_INP(0x02CC) & ~(1 << 1));
+			DEV_INFO("HDMI AUD_FIFO_URUN: INT has been disabled "
+				"by the ISR after %d occurences...\n",
+				fifo_urun_int_occurred);
+		}
+		return IRQ_HANDLED;
+	}
+
+	/* Audio Sample Drop int is enabled */
+	if ((audio_int_val & (1 << 3)) && (audio_int_val & (1 << 2))) {
+		/* Audio Sample Drop occured, clr it */
+		HDMI_OUTP(0x02CC, audio_int_val | (1 << 2));
+		DEV_DBG("%s: AUD_SAM_DROP", __func__);
+
+		++sample_drop_int_occurred;
+		if (sample_drop_int_occurred >= occurrence_limit) {
+			HDMI_OUTP(0x02CC, HDMI_INP(0x02CC) & ~(1 << 3));
+			DEV_INFO("HDMI AUD_SAM_DROP: INT has been disabled "
+				"by the ISR after %d occurences...\n",
+				sample_drop_int_occurred);
+		}
+		return IRQ_HANDLED;
+	}
+
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
+	/* HDCP_INT_CTRL[0x0118]
+	 *    [0] AUTH_SUCCESS_INT	[R]	HDCP Authentication Success
+	 *		interrupt status
+	 *    [1] AUTH_SUCCESS_ACK	[W]	Acknowledge bit for HDCP
+	 *		Authentication Success bit - write 1 to clear
+	 *    [2] AUTH_SUCCESS_MASK	[R/W]	Mask bit for HDCP Authentication
+	 *		Success interrupt - set to 1 to enable interrupt */
+	hdcp_int_val = HDMI_INP_ND(0x0118);
+	if ((hdcp_int_val & (1 << 2)) && (hdcp_int_val & (1 << 0))) {
+		/* AUTH_SUCCESS_INT */
+		HDMI_OUTP(0x0118, (hdcp_int_val | (1 << 1)) & ~(1 << 0));
+		DEV_INFO("HDCP: AUTH_SUCCESS_INT received\n");
+		complete_all(&hdmi_msm_state->hdcp_success_done);
+		return IRQ_HANDLED;
+	}
+	/*    [4] AUTH_FAIL_INT		[R]	HDCP Authentication Lost
+	 *		interrupt Status
+	 *    [5] AUTH_FAIL_ACK		[W]	Acknowledge bit for HDCP
+	 *		Authentication Lost bit - write 1 to clear
+	 *    [6] AUTH_FAIL_MASK	[R/W]	Mask bit fo HDCP Authentication
+	 *		Lost interrupt set to 1 to enable interrupt
+	 *    [7] AUTH_FAIL_INFO_ACK	[W]	Acknowledge bit for HDCP
+	 *		Authentication Failure Info field - write 1 to clear */
+	if ((hdcp_int_val & (1 << 6)) && (hdcp_int_val & (1 << 4))) {
+		/* AUTH_FAIL_INT */
+		/* Clear and Disable */
+		HDMI_OUTP(0x0118, (hdcp_int_val | (1 << 5))
+			& ~((1 << 6) | (1 << 4)));
+		DEV_INFO("HDCP: AUTH_FAIL_INT received, LINK0_STATUS=0x%08x\n",
+			HDMI_INP_ND(0x011C));
+		if (hdmi_msm_state->full_auth_done) {
+			envp[0] = "HDCP_STATE=FAIL";
+			envp[1] = NULL;
+			DEV_INFO("HDMI HPD:QDSP OFF\n");
+			kobject_uevent_env(external_common_state->uevent_kobj,
+			KOBJ_CHANGE, envp);
+			switch_set_state(&external_common_state->sdev, 0);
+			DEV_INFO("Hdmi state switch to %d: %s\n",
+				external_common_state->sdev.state,  __func__);
+			mutex_lock(&hdcp_auth_state_mutex);
+			hdmi_msm_state->full_auth_done = FALSE;
+			mutex_unlock(&hdcp_auth_state_mutex);
+			/* Calling reauth only when authentication
+			 * is sucessful or else we always go into
+			 * the reauth loop
+			 */
+			queue_work(hdmi_work_queue,
+			    &hdmi_msm_state->hdcp_reauth_work);
+		}
+		mutex_lock(&hdcp_auth_state_mutex);
+		/* This flag prevents other threads from re-authenticating
+		 * after we've just authenticated (i.e., finished part3)
+		 */
+		hdmi_msm_state->full_auth_done = FALSE;
+
+		mutex_unlock(&hdcp_auth_state_mutex);
+		DEV_DBG("calling reauthenticate from %s HDCP FAIL INT ",
+		    __func__);
+
+		/* Clear AUTH_FAIL_INFO as well */
+		HDMI_OUTP(0x0118, (hdcp_int_val | (1 << 7)));
+		return IRQ_HANDLED;
+	}
+	/*    [8] DDC_XFER_REQ_INT	[R]	HDCP DDC Transfer Request
+	 *		interrupt status
+	 *    [9] DDC_XFER_REQ_ACK	[W]	Acknowledge bit for HDCP DDC
+	 *		Transfer Request bit - write 1 to clear
+	 *   [10] DDC_XFER_REQ_MASK	[R/W]	Mask bit for HDCP DDC Transfer
+	 *		Request interrupt - set to 1 to enable interrupt */
+	if ((hdcp_int_val & (1 << 10)) && (hdcp_int_val & (1 << 8))) {
+		/* DDC_XFER_REQ_INT */
+		HDMI_OUTP_ND(0x0118, (hdcp_int_val | (1 << 9)) & ~(1 << 8));
+		if (!(hdcp_int_val & (1 << 12)))
+			return IRQ_HANDLED;
+	}
+	/*   [12] DDC_XFER_DONE_INT	[R]	HDCP DDC Transfer done interrupt
+	 *		status
+	 *   [13] DDC_XFER_DONE_ACK	[W]	Acknowledge bit for HDCP DDC
+	 *		Transfer done bit - write 1 to clear
+	 *   [14] DDC_XFER_DONE_MASK	[R/W]	Mask bit for HDCP DDC Transfer
+	 *		done interrupt - set to 1 to enable interrupt */
+	if ((hdcp_int_val & (1 << 14)) && (hdcp_int_val & (1 << 12))) {
+		/* DDC_XFER_DONE_INT */
+		HDMI_OUTP_ND(0x0118, (hdcp_int_val | (1 << 13)) & ~(1 << 12));
+		DEV_INFO("HDCP: DDC_XFER_DONE received\n");
+		return IRQ_HANDLED;
+	}
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
+
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
+	/* Process CEC Interrupt */
+	/* HDMI_MSM_CEC_INT[0x029C] */
+	cec_intr_status = HDMI_INP_ND(0x029C);
+
+	DEV_DBG("cec interrupt status is [%u]\n", cec_intr_status);
+
+	if (HDMI_MSM_CEC_FRAME_WR_SUCCESS(cec_intr_status)) {
+		DEV_DBG("CEC_IRQ_FRAME_WR_DONE\n");
+		HDMI_OUTP(0x029C, cec_intr_status |
+			HDMI_MSM_CEC_INT_FRAME_WR_DONE_ACK);
+		mutex_lock(&hdmi_msm_state_mutex);
+		hdmi_msm_state->cec_frame_wr_status |= CEC_STATUS_WR_DONE;
+		hdmi_msm_state->first_monitor = 0;
+		del_timer(&hdmi_msm_state->cec_read_timer);
+		mutex_unlock(&hdmi_msm_state_mutex);
+		complete(&hdmi_msm_state->cec_frame_wr_done);
+		return IRQ_HANDLED;
+	}
+	if ((cec_intr_status & (1 << 2)) && (cec_intr_status & (1 << 3))) {
+		DEV_DBG("CEC_IRQ_FRAME_ERROR\n");
+#ifdef TOGGLE_CEC_HARDWARE_FSM
+		/* Toggle CEC hardware FSM */
+		HDMI_OUTP(0x028C, 0x0);
+		HDMI_OUTP(0x028C, HDMI_MSM_CEC_CTRL_ENABLE);
+#endif
+		HDMI_OUTP(0x029C, cec_intr_status);
+		mutex_lock(&hdmi_msm_state_mutex);
+		hdmi_msm_state->first_monitor = 0;
+		del_timer(&hdmi_msm_state->cec_read_timer);
+		hdmi_msm_state->cec_frame_wr_status |= CEC_STATUS_WR_ERROR;
+		mutex_unlock(&hdmi_msm_state_mutex);
+		complete(&hdmi_msm_state->cec_frame_wr_done);
+		return IRQ_HANDLED;
+	}
+
+	if ((cec_intr_status & (1 << 4)) && (cec_intr_status & (1 << 5))) {
+		DEV_DBG("CEC_IRQ_MONITOR\n");
+		HDMI_OUTP(0x029C, cec_intr_status |
+			  HDMI_MSM_CEC_INT_MONITOR_ACK);
+
+		/*
+		 * CECT 9-5-1
+		 * On the first occassion start a timer
+		 * for few hundred ms, if it expires then
+		 * reset the CEC block else go on with
+		 * frame transactions as usual.
+		 * Below adds hdmi_msm_cec_msg_recv() as an
+		 * item into the work queue instead of running in
+		 * interrupt context
+		 */
+		mutex_lock(&hdmi_msm_state_mutex);
+		if (hdmi_msm_state->first_monitor == 0) {
+			/* This timer might have to be changed
+			 * worst case theoritical =
+			 * 16 bytes * 8 * 2.7msec = 346 msec
+			 */
+			mod_timer(&hdmi_msm_state->cec_read_timer,
+					jiffies + HZ/2);
+			hdmi_msm_state->first_monitor = 1;
+		}
+		mutex_unlock(&hdmi_msm_state_mutex);
+		return IRQ_HANDLED;
+	}
+
+	if ((cec_intr_status & (1 << 6)) && (cec_intr_status & (1 << 7))) {
+		DEV_DBG("CEC_IRQ_FRAME_RD_DONE\n");
+
+		mutex_lock(&hdmi_msm_state_mutex);
+		hdmi_msm_state->first_monitor = 0;
+		del_timer(&hdmi_msm_state->cec_read_timer);
+		mutex_unlock(&hdmi_msm_state_mutex);
+		HDMI_OUTP(0x029C, cec_intr_status |
+			HDMI_MSM_CEC_INT_FRAME_RD_DONE_ACK);
+		hdmi_msm_cec_msg_recv();
+
+#ifdef TOGGLE_CEC_HARDWARE_FSM
+		if (!msg_send_complete)
+			msg_recv_complete = FALSE;
+		else {
+			/* Toggle CEC hardware FSM */
+			HDMI_OUTP(0x028C, 0x0);
+			HDMI_OUTP(0x028C, HDMI_MSM_CEC_CTRL_ENABLE);
+		}
+#else
+		HDMI_OUTP(0x028C, 0x0);
+		HDMI_OUTP(0x028C, HDMI_MSM_CEC_CTRL_ENABLE);
+#endif
+
+		return IRQ_HANDLED;
+	}
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT */
+
+	DEV_DBG("%s: HPD<Ctrl=%04x, State=%04x>, ddc_int_ctrl=%04x, "
+		"aud_int=%04x, cec_intr_status=%04x\n", __func__, hpd_int_ctrl,
+		hpd_int_status, ddc_int_ctrl, audio_int_val,
+		HDMI_INP_ND(0x029C));
+
+	return IRQ_HANDLED;
+}
+
+static int check_hdmi_features(void)
+{
+	/* RAW_FEAT_CONFIG_ROW0_LSB */
+	uint32 val = inpdw(QFPROM_BASE + 0x0238);
+	/* HDMI_DISABLE */
+	boolean hdmi_disabled = (val & 0x00200000) >> 21;
+	/* HDCP_DISABLE */
+	boolean hdcp_disabled = (val & 0x00400000) >> 22;
+
+	DEV_DBG("Features <val:0x%08x, HDMI:%s, HDCP:%s>\n", val,
+		hdmi_disabled ? "OFF" : "ON", hdcp_disabled ? "OFF" : "ON");
+	if (hdmi_disabled) {
+		DEV_ERR("ERROR: HDMI disabled\n");
+		return -ENODEV;
+	}
+
+	if (hdcp_disabled)
+		DEV_WARN("WARNING: HDCP disabled\n");
+
+	return 0;
+}
+
+static boolean hdmi_msm_has_hdcp(void)
+{
+	/* RAW_FEAT_CONFIG_ROW0_LSB, HDCP_DISABLE */
+	return (inpdw(QFPROM_BASE + 0x0238) & 0x00400000) ? FALSE : TRUE;
+}
+
+static boolean hdmi_msm_is_power_on(void)
+{
+	/* HDMI_CTRL, ENABLE */
+	return (HDMI_INP_ND(0x0000) & 0x00000001) ? TRUE : FALSE;
+}
+
+/* 1.2.1.2.1 DVI Operation
+ * HDMI compliance requires the HDMI core to support DVI as well. The
+ * HDMI core also supports DVI. In DVI operation there are no preambles
+ * and guardbands transmitted. THe TMDS encoding of video data remains
+ * the same as HDMI. There are no VBI or audio packets transmitted. In
+ * order to enable DVI mode in HDMI core, HDMI_DVI_SEL field of
+ * HDMI_CTRL register needs to be programmed to 0. */
+static boolean hdmi_msm_is_dvi_mode(void)
+{
+	/* HDMI_CTRL, HDMI_DVI_SEL */
+	return (HDMI_INP_ND(0x0000) & 0x00000002) ? FALSE : TRUE;
+}
+
+void hdmi_msm_set_mode(boolean power_on)
+{
+	uint32 reg_val = 0;
+	if (power_on) {
+		/* ENABLE */
+		reg_val |= 0x00000001; /* Enable the block */
+		if (external_common_state->hdmi_sink == 0) {
+			/* HDMI_DVI_SEL */
+			reg_val |= 0x00000002;
+			if (external_common_state->present_hdcp)
+				/* HDMI Encryption */
+				reg_val |= 0x00000004;
+			/* HDMI_CTRL */
+			HDMI_OUTP(0x0000, reg_val);
+			/* HDMI_DVI_SEL */
+			reg_val &= ~0x00000002;
+		} else {
+			if (external_common_state->present_hdcp)
+				/* HDMI_Encryption_ON */
+				reg_val |= 0x00000006;
+			else
+				reg_val |= 0x00000002;
+		}
+	} else
+		reg_val = 0x00000002;
+
+	/* HDMI_CTRL */
+	HDMI_OUTP(0x0000, reg_val);
+	DEV_DBG("HDMI Core: %s\n", power_on ? "Enable" : "Disable");
+}
+
+static void msm_hdmi_init_ddc(void)
+{
+	/* 0x0220 HDMI_DDC_SPEED
+	   [31:16] PRESCALE prescale = (m * xtal_frequency) /
+		(desired_i2c_speed), where m is multiply
+		factor, default: m = 1
+	   [1:0]   THRESHOLD Select threshold to use to determine whether value
+		sampled on SDA is a 1 or 0. Specified in terms of the ratio
+		between the number of sampled ones and the total number of times
+		SDA is sampled.
+		* 0x0: >0
+		* 0x1: 1/4 of total samples
+		* 0x2: 1/2 of total samples
+		* 0x3: 3/4 of total samples */
+	/* Configure the Pre-Scale multiplier
+	 * Configure the Threshold */
+	HDMI_OUTP_ND(0x0220, (10 << 16) | (2 << 0));
+
+	/*
+	 * 0x0224 HDMI_DDC_SETUP
+	 * Setting 31:24 bits : Time units to wait before timeout
+	 * when clock is being stalled by external sink device
+	 */
+	HDMI_OUTP_ND(0x0224, 0xff000000);
+
+	/* 0x027C HDMI_DDC_REF
+	   [6] REFTIMER_ENABLE	Enable the timer
+		* 0: Disable
+		* 1: Enable
+	   [15:0] REFTIMER	Value to set the register in order to generate
+		DDC strobe. This register counts on HDCP application clock */
+	/* Enable reference timer
+	 * 27 micro-seconds */
+	HDMI_OUTP_ND(0x027C, (1 << 16) | (27 << 0));
+}
+
+static int hdmi_msm_ddc_clear_irq(const char *what)
+{
+	const uint32 time_out = 0xFFFF;
+	uint32 time_out_count, reg_val;
+
+	/* clear pending and enable interrupt */
+	time_out_count = time_out;
+	do {
+		--time_out_count;
+		/* HDMI_DDC_INT_CTRL[0x0214]
+		   [2] SW_DONE_MK Mask bit for SW_DONE_INT. Set to 1 to enable
+		       interrupt.
+		   [1] SW_DONE_ACK WRITE ONLY. Acknowledge bit for SW_DONE_INT.
+		       Write 1 to clear interrupt.
+		   [0] SW_DONE_INT READ ONLY. SW_DONE interrupt status */
+		/* Clear and Enable DDC interrupt */
+		/* Write */
+		HDMI_OUTP_ND(0x0214, (1 << 2) | (1 << 1));
+		/* Read back */
+		reg_val = HDMI_INP_ND(0x0214);
+	} while ((reg_val & 0x1) && time_out_count);
+	if (!time_out_count) {
+		DEV_ERR("%s[%s]: timedout\n", __func__, what);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
+static int hdmi_msm_ddc_write(uint32 dev_addr, uint32 offset,
+	const uint8 *data_buf, uint32 data_len, const char *what)
+{
+	uint32 reg_val, ndx;
+	int status = 0, retry = 10;
+	uint32 time_out_count;
+
+	if (NULL == data_buf) {
+		status = -EINVAL;
+		DEV_ERR("%s[%s]: invalid input paramter\n", __func__, what);
+		goto error;
+	}
+
+again:
+	status = hdmi_msm_ddc_clear_irq(what);
+	if (status)
+		goto error;
+
+	/* Ensure Device Address has LSB set to 0 to indicate Slave addr read */
+	dev_addr &= 0xFE;
+
+	/* 0x0238 HDMI_DDC_DATA
+	   [31] INDEX_WRITE WRITE ONLY. To write index field, set this bit to
+		1 while writing HDMI_DDC_DATA.
+	   [23:16] INDEX Use to set index into DDC buffer for next read or
+		current write, or to read index of current read or next write.
+		Writable only when INDEX_WRITE=1.
+	   [15:8] DATA Use to fill or read the DDC buffer
+	   [0] DATA_RW Select whether buffer access will be a read or write.
+		For writes, address auto-increments on write to HDMI_DDC_DATA.
+		For reads, address autoincrements on reads to HDMI_DDC_DATA.
+		* 0: Write
+		* 1: Read */
+
+	/* 1. Write to HDMI_I2C_DATA with the following fields set in order to
+	 *    handle portion #1
+	 *    DATA_RW = 0x1 (write)
+	 *    DATA = linkAddress (primary link address and writing)
+	 *    INDEX = 0x0 (initial offset into buffer)
+	 *    INDEX_WRITE = 0x1 (setting initial offset) */
+	HDMI_OUTP_ND(0x0238, (0x1UL << 31) | (dev_addr << 8));
+
+	/* 2. Write to HDMI_I2C_DATA with the following fields set in order to
+	 *    handle portion #2
+	 *    DATA_RW = 0x0 (write)
+	 *    DATA = offsetAddress
+	 *    INDEX = 0x0
+	 *    INDEX_WRITE = 0x0 (auto-increment by hardware) */
+	HDMI_OUTP_ND(0x0238, offset << 8);
+
+	/* 3. Write to HDMI_I2C_DATA with the following fields set in order to
+	 *    handle portion #3
+	 *    DATA_RW = 0x0 (write)
+	 *    DATA = data_buf[ndx]
+	 *    INDEX = 0x0
+	 *    INDEX_WRITE = 0x0 (auto-increment by hardware) */
+	for (ndx = 0; ndx < data_len; ++ndx)
+		HDMI_OUTP_ND(0x0238, ((uint32)data_buf[ndx]) << 8);
+
+	/* Data setup is complete, now setup the transaction characteristics */
+
+	/* 0x0228 HDMI_DDC_TRANS0
+	   [23:16] CNT0 Byte count for first transaction (excluding the first
+		byte, which is usually the address).
+	   [13] STOP0 Determines whether a stop bit will be sent after the first
+		transaction
+		* 0: NO STOP
+		* 1: STOP
+	   [12] START0 Determines whether a start bit will be sent before the
+		first transaction
+		* 0: NO START
+		* 1: START
+	   [8] STOP_ON_NACK0 Determines whether the current transfer will stop
+		if a NACK is received during the first transaction (current
+		transaction always stops).
+		* 0: STOP CURRENT TRANSACTION, GO TO NEXT TRANSACTION
+		* 1: STOP ALL TRANSACTIONS, SEND STOP BIT
+	   [0] RW0 Read/write indicator for first transaction - set to 0 for
+		write, 1 for read. This bit only controls HDMI_DDC behaviour -
+		the R/W bit in the transaction is programmed into the DDC buffer
+		as the LSB of the address byte.
+		* 0: WRITE
+		* 1: READ */
+
+	/* 4. Write to HDMI_I2C_TRANSACTION0 with the following fields set in
+	      order to handle characteristics of portion #1 and portion #2
+	 *    RW0 = 0x0 (write)
+	 *    START0 = 0x1 (insert START bit)
+	 *    STOP0 = 0x0 (do NOT insert STOP bit)
+	 *    CNT0 = 0x1 (single byte transaction excluding address) */
+	HDMI_OUTP_ND(0x0228, (1 << 12) | (1 << 16));
+
+	/* 0x022C HDMI_DDC_TRANS1
+	  [23:16] CNT1 Byte count for second transaction (excluding the first
+		byte, which is usually the address).
+	  [13] STOP1 Determines whether a stop bit will be sent after the second
+		transaction
+		* 0: NO STOP
+		* 1: STOP
+	  [12] START1 Determines whether a start bit will be sent before the
+		second transaction
+		* 0: NO START
+		* 1: START
+	  [8] STOP_ON_NACK1 Determines whether the current transfer will stop if
+		a NACK is received during the second transaction (current
+		transaction always stops).
+		* 0: STOP CURRENT TRANSACTION, GO TO NEXT TRANSACTION
+		* 1: STOP ALL TRANSACTIONS, SEND STOP BIT
+	  [0] RW1 Read/write indicator for second transaction - set to 0 for
+		write, 1 for read. This bit only controls HDMI_DDC behaviour -
+		the R/W bit in the transaction is programmed into the DDC buffer
+		as the LSB of the address byte.
+		* 0: WRITE
+		* 1: READ */
+
+	/* 5. Write to HDMI_I2C_TRANSACTION1 with the following fields set in
+	      order to handle characteristics of portion #3
+	 *    RW1 = 0x1 (read)
+	 *    START1 = 0x1 (insert START bit)
+	 *    STOP1 = 0x1 (insert STOP bit)
+	 *    CNT1 = data_len   (0xN (write N bytes of data))
+	 *    Byte count for second transition (excluding the first
+	 *    Byte which is usually the address) */
+	HDMI_OUTP_ND(0x022C, (1 << 13) | ((data_len-1) << 16));
+
+	/* Trigger the I2C transfer */
+	/* 0x020C HDMI_DDC_CTRL
+	   [21:20] TRANSACTION_CNT
+		Number of transactions to be done in current transfer.
+		* 0x0: transaction0 only
+		* 0x1: transaction0, transaction1
+		* 0x2: transaction0, transaction1, transaction2
+		* 0x3: transaction0, transaction1, transaction2, transaction3
+	   [3] SW_STATUS_RESET
+		Write 1 to reset HDMI_DDC_SW_STATUS flags, will reset SW_DONE,
+		ABORTED, TIMEOUT, SW_INTERRUPTED, BUFFER_OVERFLOW,
+		STOPPED_ON_NACK, NACK0, NACK1, NACK2, NACK3
+	   [2] SEND_RESET Set to 1 to send reset sequence (9 clocks with no
+		data) at start of transfer.  This sequence is sent after GO is
+		written to 1, before the first transaction only.
+	   [1] SOFT_RESET Write 1 to reset DDC controller
+	   [0] GO WRITE ONLY. Write 1 to start DDC transfer. */
+
+	/* 6. Write to HDMI_I2C_CONTROL to kick off the hardware.
+	 *    Note that NOTHING has been transmitted on the DDC lines up to this
+	 *    point.
+	 *    TRANSACTION_CNT = 0x1 (execute transaction0 followed by
+	 *    transaction1)
+	 *    GO = 0x1 (kicks off hardware) */
+	INIT_COMPLETION(hdmi_msm_state->ddc_sw_done);
+	HDMI_OUTP_ND(0x020C, (1 << 0) | (1 << 20));
+
+	time_out_count = wait_for_completion_interruptible_timeout(
+		&hdmi_msm_state->ddc_sw_done, HZ/2);
+	HDMI_OUTP_ND(0x0214, 0x2);
+	if (!time_out_count) {
+		if (retry-- > 0) {
+			DEV_INFO("%s[%s]: failed timout, retry=%d\n", __func__,
+				what, retry);
+			goto again;
+		}
+		status = -ETIMEDOUT;
+		DEV_ERR("%s[%s]: timedout, DDC SW Status=%08x, HW "
+			"Status=%08x, Int Ctrl=%08x\n", __func__, what,
+			HDMI_INP_ND(0x0218), HDMI_INP_ND(0x021C),
+			HDMI_INP_ND(0x0214));
+		goto error;
+	}
+
+	/* Read DDC status */
+	reg_val = HDMI_INP_ND(0x0218);
+	reg_val &= 0x00001000 | 0x00002000 | 0x00004000 | 0x00008000;
+
+	/* Check if any NACK occurred */
+	if (reg_val) {
+		if (retry > 1)
+			HDMI_OUTP_ND(0x020C, BIT(3)); /* SW_STATUS_RESET */
+		else
+			HDMI_OUTP_ND(0x020C, BIT(1)); /* SOFT_RESET */
+		if (retry-- > 0) {
+			DEV_DBG("%s[%s]: failed NACK=%08x, retry=%d\n",
+				__func__, what, reg_val, retry);
+			msleep(100);
+			goto again;
+		}
+		status = -EIO;
+		DEV_ERR("%s[%s]: failed NACK: %08x\n", __func__, what, reg_val);
+		goto error;
+	}
+
+	DEV_DBG("%s[%s] success\n", __func__, what);
+
+error:
+	return status;
+}
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
+
+static int hdmi_msm_ddc_read_retry(uint32 dev_addr, uint32 offset,
+	uint8 *data_buf, uint32 data_len, uint32 request_len, int retry,
+	const char *what)
+{
+	uint32 reg_val, ndx;
+	int status = 0;
+	uint32 time_out_count;
+	int log_retry_fail = retry != 1;
+
+	if (NULL == data_buf) {
+		status = -EINVAL;
+		DEV_ERR("%s: invalid input paramter\n", __func__);
+		goto error;
+	}
+
+again:
+	status = hdmi_msm_ddc_clear_irq(what);
+	if (status)
+		goto error;
+
+	/* Ensure Device Address has LSB set to 0 to indicate Slave addr read */
+	dev_addr &= 0xFE;
+
+	/* 0x0238 HDMI_DDC_DATA
+	   [31] INDEX_WRITE WRITE ONLY. To write index field, set this bit to
+		1 while writing HDMI_DDC_DATA.
+	   [23:16] INDEX Use to set index into DDC buffer for next read or
+		current write, or to read index of current read or next write.
+		Writable only when INDEX_WRITE=1.
+	   [15:8] DATA Use to fill or read the DDC buffer
+	   [0] DATA_RW Select whether buffer access will be a read or write.
+		For writes, address auto-increments on write to HDMI_DDC_DATA.
+		For reads, address autoincrements on reads to HDMI_DDC_DATA.
+		* 0: Write
+		* 1: Read */
+
+	/* 1. Write to HDMI_I2C_DATA with the following fields set in order to
+	 *    handle portion #1
+	 *    DATA_RW = 0x0 (write)
+	 *    DATA = linkAddress (primary link address and writing)
+	 *    INDEX = 0x0 (initial offset into buffer)
+	 *    INDEX_WRITE = 0x1 (setting initial offset) */
+	HDMI_OUTP_ND(0x0238, (0x1UL << 31) | (dev_addr << 8));
+
+	/* 2. Write to HDMI_I2C_DATA with the following fields set in order to
+	 *    handle portion #2
+	 *    DATA_RW = 0x0 (write)
+	 *    DATA = offsetAddress
+	 *    INDEX = 0x0
+	 *    INDEX_WRITE = 0x0 (auto-increment by hardware) */
+	HDMI_OUTP_ND(0x0238, offset << 8);
+
+	/* 3. Write to HDMI_I2C_DATA with the following fields set in order to
+	 *    handle portion #3
+	 *    DATA_RW = 0x0 (write)
+	 *    DATA = linkAddress + 1 (primary link address 0x74 and reading)
+	 *    INDEX = 0x0
+	 *    INDEX_WRITE = 0x0 (auto-increment by hardware) */
+	HDMI_OUTP_ND(0x0238, (dev_addr | 1) << 8);
+
+	/* Data setup is complete, now setup the transaction characteristics */
+
+	/* 0x0228 HDMI_DDC_TRANS0
+	   [23:16] CNT0 Byte count for first transaction (excluding the first
+		byte, which is usually the address).
+	   [13] STOP0 Determines whether a stop bit will be sent after the first
+		transaction
+		* 0: NO STOP
+		* 1: STOP
+	   [12] START0 Determines whether a start bit will be sent before the
+		first transaction
+		* 0: NO START
+		* 1: START
+	   [8] STOP_ON_NACK0 Determines whether the current transfer will stop
+		if a NACK is received during the first transaction (current
+		transaction always stops).
+		* 0: STOP CURRENT TRANSACTION, GO TO NEXT TRANSACTION
+		* 1: STOP ALL TRANSACTIONS, SEND STOP BIT
+	   [0] RW0 Read/write indicator for first transaction - set to 0 for
+		write, 1 for read. This bit only controls HDMI_DDC behaviour -
+		the R/W bit in the transaction is programmed into the DDC buffer
+		as the LSB of the address byte.
+		* 0: WRITE
+		* 1: READ */
+
+	/* 4. Write to HDMI_I2C_TRANSACTION0 with the following fields set in
+	      order to handle characteristics of portion #1 and portion #2
+	 *    RW0 = 0x0 (write)
+	 *    START0 = 0x1 (insert START bit)
+	 *    STOP0 = 0x0 (do NOT insert STOP bit)
+	 *    CNT0 = 0x1 (single byte transaction excluding address) */
+	HDMI_OUTP_ND(0x0228, (1 << 12) | (1 << 16));
+
+	/* 0x022C HDMI_DDC_TRANS1
+	  [23:16] CNT1 Byte count for second transaction (excluding the first
+		byte, which is usually the address).
+	  [13] STOP1 Determines whether a stop bit will be sent after the second
+		transaction
+		* 0: NO STOP
+		* 1: STOP
+	  [12] START1 Determines whether a start bit will be sent before the
+		second transaction
+		* 0: NO START
+		* 1: START
+	  [8] STOP_ON_NACK1 Determines whether the current transfer will stop if
+		a NACK is received during the second transaction (current
+		transaction always stops).
+		* 0: STOP CURRENT TRANSACTION, GO TO NEXT TRANSACTION
+		* 1: STOP ALL TRANSACTIONS, SEND STOP BIT
+	  [0] RW1 Read/write indicator for second transaction - set to 0 for
+		write, 1 for read. This bit only controls HDMI_DDC behaviour -
+		the R/W bit in the transaction is programmed into the DDC buffer
+		as the LSB of the address byte.
+		* 0: WRITE
+		* 1: READ */
+
+	/* 5. Write to HDMI_I2C_TRANSACTION1 with the following fields set in
+	      order to handle characteristics of portion #3
+	 *    RW1 = 0x1 (read)
+	 *    START1 = 0x1 (insert START bit)
+	 *    STOP1 = 0x1 (insert STOP bit)
+	 *    CNT1 = data_len   (it's 128 (0x80) for a blk read) */
+	HDMI_OUTP_ND(0x022C, 1 | (1 << 12) | (1 << 13) | (request_len << 16));
+
+	/* Trigger the I2C transfer */
+	/* 0x020C HDMI_DDC_CTRL
+	   [21:20] TRANSACTION_CNT
+		Number of transactions to be done in current transfer.
+		* 0x0: transaction0 only
+		* 0x1: transaction0, transaction1
+		* 0x2: transaction0, transaction1, transaction2
+		* 0x3: transaction0, transaction1, transaction2, transaction3
+	   [3] SW_STATUS_RESET
+		Write 1 to reset HDMI_DDC_SW_STATUS flags, will reset SW_DONE,
+		ABORTED, TIMEOUT, SW_INTERRUPTED, BUFFER_OVERFLOW,
+		STOPPED_ON_NACK, NACK0, NACK1, NACK2, NACK3
+	   [2] SEND_RESET Set to 1 to send reset sequence (9 clocks with no
+		data) at start of transfer.  This sequence is sent after GO is
+		written to 1, before the first transaction only.
+	   [1] SOFT_RESET Write 1 to reset DDC controller
+	   [0] GO WRITE ONLY. Write 1 to start DDC transfer. */
+
+	/* 6. Write to HDMI_I2C_CONTROL to kick off the hardware.
+	 *    Note that NOTHING has been transmitted on the DDC lines up to this
+	 *    point.
+	 *    TRANSACTION_CNT = 0x1 (execute transaction0 followed by
+	 *    transaction1)
+	 *    SEND_RESET = Set to 1 to send reset sequence
+	 *    GO = 0x1 (kicks off hardware) */
+	INIT_COMPLETION(hdmi_msm_state->ddc_sw_done);
+	HDMI_OUTP_ND(0x020C, (1 << 0) | (1 << 20));
+
+	time_out_count = wait_for_completion_interruptible_timeout(
+		&hdmi_msm_state->ddc_sw_done, HZ/2);
+	HDMI_OUTP_ND(0x0214, 0x2);
+	if (!time_out_count) {
+		if (retry-- > 0) {
+			DEV_INFO("%s: failed timout, retry=%d\n", __func__,
+				retry);
+			goto again;
+		}
+		status = -ETIMEDOUT;
+		DEV_ERR("%s: timedout(7), DDC SW Status=%08x, HW "
+			"Status=%08x, Int Ctrl=%08x\n", __func__,
+			HDMI_INP(0x0218), HDMI_INP(0x021C), HDMI_INP(0x0214));
+		goto error;
+	}
+
+	/* Read DDC status */
+	reg_val = HDMI_INP_ND(0x0218);
+	reg_val &= 0x00001000 | 0x00002000 | 0x00004000 | 0x00008000;
+
+	/* Check if any NACK occurred */
+	if (reg_val) {
+		HDMI_OUTP_ND(0x020C, BIT(3)); /* SW_STATUS_RESET */
+		if (retry == 1)
+			HDMI_OUTP_ND(0x020C, BIT(1)); /* SOFT_RESET */
+		if (retry-- > 0) {
+			DEV_DBG("%s(%s): failed NACK=0x%08x, retry=%d, "
+				"dev-addr=0x%02x, offset=0x%02x, "
+				"length=%d\n", __func__, what,
+				reg_val, retry, dev_addr,
+				offset, data_len);
+			goto again;
+		}
+		status = -EIO;
+		if (log_retry_fail)
+			DEV_ERR("%s(%s): failed NACK=0x%08x, dev-addr=0x%02x, "
+				"offset=0x%02x, length=%d\n", __func__, what,
+				reg_val, dev_addr, offset, data_len);
+		goto error;
+	}
+
+	/* 0x0238 HDMI_DDC_DATA
+	   [31] INDEX_WRITE WRITE ONLY. To write index field, set this bit to 1
+		while writing HDMI_DDC_DATA.
+	   [23:16] INDEX Use to set index into DDC buffer for next read or
+		current write, or to read index of current read or next write.
+		Writable only when INDEX_WRITE=1.
+	   [15:8] DATA Use to fill or read the DDC buffer
+	   [0] DATA_RW Select whether buffer access will be a read or write.
+		For writes, address auto-increments on write to HDMI_DDC_DATA.
+		For reads, address autoincrements on reads to HDMI_DDC_DATA.
+		* 0: Write
+		* 1: Read */
+
+	/* 8. ALL data is now available and waiting in the DDC buffer.
+	 *    Read HDMI_I2C_DATA with the following fields set
+	 *    RW = 0x1 (read)
+	 *    DATA = BCAPS (this is field where data is pulled from)
+	 *    INDEX = 0x3 (where the data has been placed in buffer by hardware)
+	 *    INDEX_WRITE = 0x1 (explicitly define offset) */
+	/* Write this data to DDC buffer */
+	HDMI_OUTP_ND(0x0238, 0x1 | (3 << 16) | (1 << 31));
+
+	/* Discard first byte */
+	HDMI_INP_ND(0x0238);
+	for (ndx = 0; ndx < data_len; ++ndx) {
+		reg_val = HDMI_INP_ND(0x0238);
+		data_buf[ndx] = (uint8) ((reg_val & 0x0000FF00) >> 8);
+	}
+
+	DEV_DBG("%s[%s] success\n", __func__, what);
+
+error:
+	return status;
+}
+
+static int hdmi_msm_ddc_read_edid_seg(uint32 dev_addr, uint32 offset,
+	uint8 *data_buf, uint32 data_len, uint32 request_len, int retry,
+	const char *what)
+{
+	uint32 reg_val, ndx;
+	int status = 0;
+	uint32 time_out_count;
+	int log_retry_fail = retry != 1;
+	int seg_addr = 0x60, seg_num = 0x01;
+
+	if (NULL == data_buf) {
+		status = -EINVAL;
+		DEV_ERR("%s: invalid input paramter\n", __func__);
+		goto error;
+	}
+
+again:
+	status = hdmi_msm_ddc_clear_irq(what);
+	if (status)
+		goto error;
+
+	/* Ensure Device Address has LSB set to 0 to indicate Slave addr read */
+	dev_addr &= 0xFE;
+
+	/* 0x0238 HDMI_DDC_DATA
+	   [31] INDEX_WRITE WRITE ONLY. To write index field, set this bit to
+		1 while writing HDMI_DDC_DATA.
+	   [23:16] INDEX Use to set index into DDC buffer for next read or
+		current write, or to read index of current read or next write.
+		Writable only when INDEX_WRITE=1.
+	   [15:8] DATA Use to fill or read the DDC buffer
+	   [0] DATA_RW Select whether buffer access will be a read or write.
+		For writes, address auto-increments on write to HDMI_DDC_DATA.
+		For reads, address autoincrements on reads to HDMI_DDC_DATA.
+		* 0: Write
+		* 1: Read */
+
+	/* 1. Write to HDMI_I2C_DATA with the following fields set in order to
+	 *    handle portion #1
+	 *    DATA_RW = 0x0 (write)
+	 *    DATA = linkAddress (primary link address and writing)
+	 *    INDEX = 0x0 (initial offset into buffer)
+	 *    INDEX_WRITE = 0x1 (setting initial offset) */
+	HDMI_OUTP_ND(0x0238, (0x1UL << 31) | (seg_addr << 8));
+
+	/* 2. Write to HDMI_I2C_DATA with the following fields set in order to
+	 *    handle portion #2
+	 *    DATA_RW = 0x0 (write)
+	 *    DATA = offsetAddress
+	 *    INDEX = 0x0
+	 *    INDEX_WRITE = 0x0 (auto-increment by hardware) */
+	HDMI_OUTP_ND(0x0238, seg_num << 8);
+
+	/* 3. Write to HDMI_I2C_DATA with the following fields set in order to
+	 *    handle portion #3
+	 *    DATA_RW = 0x0 (write)
+	 *    DATA = linkAddress + 1 (primary link address 0x74 and reading)
+	 *    INDEX = 0x0
+	 *    INDEX_WRITE = 0x0 (auto-increment by hardware) */
+	HDMI_OUTP_ND(0x0238, dev_addr << 8);
+	HDMI_OUTP_ND(0x0238, offset << 8);
+	HDMI_OUTP_ND(0x0238, (dev_addr | 1) << 8);
+
+	/* Data setup is complete, now setup the transaction characteristics */
+
+	/* 0x0228 HDMI_DDC_TRANS0
+	   [23:16] CNT0 Byte count for first transaction (excluding the first
+		byte, which is usually the address).
+	   [13] STOP0 Determines whether a stop bit will be sent after the first
+		transaction
+		* 0: NO STOP
+		* 1: STOP
+	   [12] START0 Determines whether a start bit will be sent before the
+		first transaction
+		* 0: NO START
+		* 1: START
+	   [8] STOP_ON_NACK0 Determines whether the current transfer will stop
+		if a NACK is received during the first transaction (current
+		transaction always stops).
+		* 0: STOP CURRENT TRANSACTION, GO TO NEXT TRANSACTION
+		* 1: STOP ALL TRANSACTIONS, SEND STOP BIT
+	   [0] RW0 Read/write indicator for first transaction - set to 0 for
+		write, 1 for read. This bit only controls HDMI_DDC behaviour -
+		the R/W bit in the transaction is programmed into the DDC buffer
+		as the LSB of the address byte.
+		* 0: WRITE
+		* 1: READ */
+
+	/* 4. Write to HDMI_I2C_TRANSACTION0 with the following fields set in
+	      order to handle characteristics of portion #1 and portion #2
+	 *    RW0 = 0x0 (write)
+	 *    START0 = 0x1 (insert START bit)
+	 *    STOP0 = 0x0 (do NOT insert STOP bit)
+	 *    CNT0 = 0x1 (single byte transaction excluding address) */
+	HDMI_OUTP_ND(0x0228, (1 << 12) | (1 << 16));
+
+	/* 0x022C HDMI_DDC_TRANS1
+	  [23:16] CNT1 Byte count for second transaction (excluding the first
+		byte, which is usually the address).
+	  [13] STOP1 Determines whether a stop bit will be sent after the second
+		transaction
+		* 0: NO STOP
+		* 1: STOP
+	  [12] START1 Determines whether a start bit will be sent before the
+		second transaction
+		* 0: NO START
+		* 1: START
+	  [8] STOP_ON_NACK1 Determines whether the current transfer will stop if
+		a NACK is received during the second transaction (current
+		transaction always stops).
+		* 0: STOP CURRENT TRANSACTION, GO TO NEXT TRANSACTION
+		* 1: STOP ALL TRANSACTIONS, SEND STOP BIT
+	  [0] RW1 Read/write indicator for second transaction - set to 0 for
+		write, 1 for read. This bit only controls HDMI_DDC behaviour -
+		the R/W bit in the transaction is programmed into the DDC buffer
+		as the LSB of the address byte.
+		* 0: WRITE
+		* 1: READ */
+
+	/* 5. Write to HDMI_I2C_TRANSACTION1 with the following fields set in
+	      order to handle characteristics of portion #3
+	 *    RW1 = 0x1 (read)
+	 *    START1 = 0x1 (insert START bit)
+	 *    STOP1 = 0x1 (insert STOP bit)
+	 *    CNT1 = data_len   (it's 128 (0x80) for a blk read) */
+	HDMI_OUTP_ND(0x022C, (1 << 12) | (1 << 16));
+
+	/* 0x022C HDMI_DDC_TRANS2
+	  [23:16] CNT1 Byte count for second transaction (excluding the first
+		byte, which is usually the address).
+	  [13] STOP1 Determines whether a stop bit will be sent after the second
+		transaction
+		* 0: NO STOP
+		* 1: STOP
+	  [12] START1 Determines whether a start bit will be sent before the
+		second transaction
+		* 0: NO START
+		* 1: START
+	  [8] STOP_ON_NACK1 Determines whether the current transfer will stop if
+		a NACK is received during the second transaction (current
+		transaction always stops).
+		* 0: STOP CURRENT TRANSACTION, GO TO NEXT TRANSACTION
+		* 1: STOP ALL TRANSACTIONS, SEND STOP BIT
+	  [0] RW1 Read/write indicator for second transaction - set to 0 for
+		write, 1 for read. This bit only controls HDMI_DDC behaviour -
+		the R/W bit in the transaction is programmed into the DDC buffer
+		as the LSB of the address byte.
+		* 0: WRITE
+		* 1: READ */
+
+	/* 5. Write to HDMI_I2C_TRANSACTION1 with the following fields set in
+	      order to handle characteristics of portion #3
+	 *    RW1 = 0x1 (read)
+	 *    START1 = 0x1 (insert START bit)
+	 *    STOP1 = 0x1 (insert STOP bit)
+	 *    CNT1 = data_len   (it's 128 (0x80) for a blk read) */
+	HDMI_OUTP_ND(0x0230, 1 | (1 << 12) | (1 << 13) | (request_len << 16));
+
+	/* Trigger the I2C transfer */
+	/* 0x020C HDMI_DDC_CTRL
+	   [21:20] TRANSACTION_CNT
+		Number of transactions to be done in current transfer.
+		* 0x0: transaction0 only
+		* 0x1: transaction0, transaction1
+		* 0x2: transaction0, transaction1, transaction2
+		* 0x3: transaction0, transaction1, transaction2, transaction3
+	   [3] SW_STATUS_RESET
+		Write 1 to reset HDMI_DDC_SW_STATUS flags, will reset SW_DONE,
+		ABORTED, TIMEOUT, SW_INTERRUPTED, BUFFER_OVERFLOW,
+		STOPPED_ON_NACK, NACK0, NACK1, NACK2, NACK3
+	   [2] SEND_RESET Set to 1 to send reset sequence (9 clocks with no
+		data) at start of transfer.  This sequence is sent after GO is
+		written to 1, before the first transaction only.
+	   [1] SOFT_RESET Write 1 to reset DDC controller
+	   [0] GO WRITE ONLY. Write 1 to start DDC transfer. */
+
+	/* 6. Write to HDMI_I2C_CONTROL to kick off the hardware.
+	 *    Note that NOTHING has been transmitted on the DDC lines up to this
+	 *    point.
+	 *    TRANSACTION_CNT = 0x2 (execute transaction0 followed by
+	 *    transaction1)
+	 *    GO = 0x1 (kicks off hardware) */
+	INIT_COMPLETION(hdmi_msm_state->ddc_sw_done);
+	HDMI_OUTP_ND(0x020C, (1 << 0) | (2 << 20));
+
+	time_out_count = wait_for_completion_interruptible_timeout(
+		&hdmi_msm_state->ddc_sw_done, HZ/2);
+	HDMI_OUTP_ND(0x0214, 0x2);
+	if (!time_out_count) {
+		if (retry-- > 0) {
+			DEV_INFO("%s: failed timout, retry=%d\n", __func__,
+				retry);
+			goto again;
+		}
+		status = -ETIMEDOUT;
+		DEV_ERR("%s: timedout(7), DDC SW Status=%08x, HW "
+			"Status=%08x, Int Ctrl=%08x\n", __func__,
+			HDMI_INP(0x0218), HDMI_INP(0x021C), HDMI_INP(0x0214));
+		goto error;
+	}
+
+	/* Read DDC status */
+	reg_val = HDMI_INP_ND(0x0218);
+	reg_val &= 0x00001000 | 0x00002000 | 0x00004000 | 0x00008000;
+
+	/* Check if any NACK occurred */
+	if (reg_val) {
+		HDMI_OUTP_ND(0x020C, BIT(3)); /* SW_STATUS_RESET */
+		if (retry == 1)
+			HDMI_OUTP_ND(0x020C, BIT(1)); /* SOFT_RESET */
+		if (retry-- > 0) {
+			DEV_DBG("%s(%s): failed NACK=0x%08x, retry=%d, "
+				"dev-addr=0x%02x, offset=0x%02x, "
+				"length=%d\n", __func__, what,
+				reg_val, retry, dev_addr,
+				offset, data_len);
+			goto again;
+		}
+		status = -EIO;
+		if (log_retry_fail)
+			DEV_ERR("%s(%s): failed NACK=0x%08x, dev-addr=0x%02x, "
+				"offset=0x%02x, length=%d\n", __func__, what,
+				reg_val, dev_addr, offset, data_len);
+		goto error;
+	}
+
+	/* 0x0238 HDMI_DDC_DATA
+	   [31] INDEX_WRITE WRITE ONLY. To write index field, set this bit to 1
+		while writing HDMI_DDC_DATA.
+	   [23:16] INDEX Use to set index into DDC buffer for next read or
+		current write, or to read index of current read or next write.
+		Writable only when INDEX_WRITE=1.
+	   [15:8] DATA Use to fill or read the DDC buffer
+	   [0] DATA_RW Select whether buffer access will be a read or write.
+		For writes, address auto-increments on write to HDMI_DDC_DATA.
+		For reads, address autoincrements on reads to HDMI_DDC_DATA.
+		* 0: Write
+		* 1: Read */
+
+	/* 8. ALL data is now available and waiting in the DDC buffer.
+	 *    Read HDMI_I2C_DATA with the following fields set
+	 *    RW = 0x1 (read)
+	 *    DATA = BCAPS (this is field where data is pulled from)
+	 *    INDEX = 0x5 (where the data has been placed in buffer by hardware)
+	 *    INDEX_WRITE = 0x1 (explicitly define offset) */
+	/* Write this data to DDC buffer */
+	HDMI_OUTP_ND(0x0238, 0x1 | (5 << 16) | (1 << 31));
+
+	/* Discard first byte */
+	HDMI_INP_ND(0x0238);
+
+	for (ndx = 0; ndx < data_len; ++ndx) {
+		reg_val = HDMI_INP_ND(0x0238);
+		data_buf[ndx] = (uint8) ((reg_val & 0x0000FF00) >> 8);
+	}
+
+	DEV_DBG("%s[%s] success\n", __func__, what);
+
+error:
+	return status;
+}
+
+
+static int hdmi_msm_ddc_read(uint32 dev_addr, uint32 offset, uint8 *data_buf,
+	uint32 data_len, int retry, const char *what, boolean no_align)
+{
+	int ret = hdmi_msm_ddc_read_retry(dev_addr, offset, data_buf, data_len,
+		data_len, retry, what);
+	if (!ret)
+		return 0;
+	if (no_align) {
+		return hdmi_msm_ddc_read_retry(dev_addr, offset, data_buf,
+			data_len, data_len, retry, what);
+	} else {
+		return hdmi_msm_ddc_read_retry(dev_addr, offset, data_buf,
+			data_len, 32 * ((data_len + 31) / 32), retry, what);
+	}
+}
+
+
+static int hdmi_msm_read_edid_block(int block, uint8 *edid_buf)
+{
+	int i, rc = 0;
+	int block_size = 0x80;
+
+	do {
+		DEV_DBG("EDID: reading block(%d) with block-size=%d\n",
+			block, block_size);
+		for (i = 0; i < 0x80; i += block_size) {
+			/*Read EDID twice with 32bit alighnment too */
+			if (block < 2) {
+				rc = hdmi_msm_ddc_read(0xA0, block*0x80 + i,
+					edid_buf+i, block_size, 1,
+					"EDID", FALSE);
+			} else {
+				rc = hdmi_msm_ddc_read_edid_seg(0xA0,
+				block*0x80 + i, edid_buf+i, block_size,
+				block_size, 1, "EDID");
+			}
+			if (rc)
+				break;
+		}
+
+		block_size /= 2;
+	} while (rc && (block_size >= 16));
+
+	return rc;
+}
+
+static int hdmi_msm_read_edid(void)
+{
+	int status;
+
+	msm_hdmi_init_ddc();
+	/* Looks like we need to turn on HDMI engine before any
+	 * DDC transaction */
+	if (!hdmi_msm_is_power_on()) {
+		DEV_ERR("%s: failed: HDMI power is off", __func__);
+		status = -ENXIO;
+		goto error;
+	}
+
+	external_common_state->read_edid_block = hdmi_msm_read_edid_block;
+	status = hdmi_common_read_edid();
+	if (!status)
+		DEV_DBG("EDID: successfully read\n");
+
+error:
+	return status;
+}
+
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
+static void hdcp_auth_info(uint32 auth_info)
+{
+	switch (auth_info) {
+	case 0:
+		DEV_INFO("%s: None", __func__);
+		break;
+	case 1:
+		DEV_INFO("%s: Software Disabled Authentication", __func__);
+		break;
+	case 2:
+		DEV_INFO("%s: An Written", __func__);
+		break;
+	case 3:
+		DEV_INFO("%s: Invalid Aksv", __func__);
+		break;
+	case 4:
+		DEV_INFO("%s: Invalid Bksv", __func__);
+		break;
+	case 5:
+		DEV_INFO("%s: RI Mismatch (including RO)", __func__);
+		break;
+	case 6:
+		DEV_INFO("%s: consecutive Pj Mismatches", __func__);
+		break;
+	case 7:
+		DEV_INFO("%s: HPD Disconnect", __func__);
+		break;
+	case 8:
+	default:
+		DEV_INFO("%s: Reserved", __func__);
+		break;
+	}
+}
+
+static void hdcp_key_state(uint32 key_state)
+{
+	switch (key_state) {
+	case 0:
+		DEV_WARN("%s: No HDCP Keys", __func__);
+		break;
+	case 1:
+		DEV_WARN("%s: Not Checked", __func__);
+		break;
+	case 2:
+		DEV_DBG("%s: Checking", __func__);
+		break;
+	case 3:
+		DEV_DBG("%s: HDCP Keys Valid", __func__);
+		break;
+	case 4:
+		DEV_WARN("%s: AKSV not valid", __func__);
+		break;
+	case 5:
+		DEV_WARN("%s: Checksum Mismatch", __func__);
+		break;
+	case 6:
+		DEV_DBG("%s: Production AKSV"
+			"with ENABLE_USER_DEFINED_AN=1", __func__);
+		break;
+	case 7:
+	default:
+		DEV_INFO("%s: Reserved", __func__);
+		break;
+	}
+}
+
+static int hdmi_msm_count_one(uint8 *array, uint8 len)
+{
+	int i, j, count = 0;
+	for (i = 0; i < len; i++)
+		for (j = 0; j < 8; j++)
+			count += (((array[i] >> j) & 0x1) ? 1 : 0);
+	return count;
+}
+
+static void hdcp_deauthenticate(void)
+{
+	int hdcp_link_status = HDMI_INP(0x011C);
+
+	/* Disable HDCP interrupts */
+	HDMI_OUTP(0x0118, 0x0);
+
+	external_common_state->hdcp_active = FALSE;
+	/* 0x0130 HDCP_RESET
+	  [0] LINK0_DEAUTHENTICATE */
+	HDMI_OUTP(0x0130, 0x1);
+
+	/* 0x0110 HDCP_CTRL
+	  [8] ENCRYPTION_ENABLE
+	  [0] ENABLE */
+	/* encryption_enable = 0 | hdcp block enable = 1 */
+	HDMI_OUTP(0x0110, 0x0);
+
+	if (hdcp_link_status & 0x00000004)
+		hdcp_auth_info((hdcp_link_status & 0x000000F0) >> 4);
+}
+
+static void check_and_clear_HDCP_DDC_Failure(void)
+{
+	int hdcp_ddc_ctrl1_reg;
+	int hdcp_ddc_status;
+	int failure;
+	int nack0;
+
+	/*
+	 * Check for any DDC transfer failures
+	 * 0x0128 HDCP_DDC_STATUS
+	 * [16] FAILED		Indicates that the last HDCP HW DDC transer
+	 *			failed. This occurs when a transfer is
+	 *			attempted with HDCP DDC disabled
+	 *			(HDCP_DDC_DISABLE=1) or the number of retries
+	 *			match HDCP_DDC_RETRY_CNT
+	 *
+	 * [14] NACK0		Indicates that the last HDCP HW DDC transfer
+	 *			was aborted due to a NACK on the first
+	 *			transaction - cleared by writing 0 to GO bit
+	 */
+	hdcp_ddc_status = HDMI_INP(HDCP_DDC_STATUS);
+	failure = (hdcp_ddc_status >> 16) & 0x1;
+	nack0 = (hdcp_ddc_status >> 14) & 0x1;
+	DEV_DBG("%s: On Entry: HDCP_DDC_STATUS = 0x%x, FAILURE = %d,"
+		"NACK0 = %d\n", __func__ , hdcp_ddc_status, failure, nack0);
+
+	if (failure == 0x1) {
+		/*
+		 * Indicates that the last HDCP HW DDC transfer failed.
+		 * This occurs when a transfer is attempted with HDCP DDC
+		 * disabled (HDCP_DDC_DISABLE=1) or the number of retries
+		 * matches HDCP_DDC_RETRY_CNT.
+		 * Failure occured,  let's clear it.
+		 */
+		DEV_INFO("%s: DDC failure detected. HDCP_DDC_STATUS=0x%08x\n",
+			 __func__, hdcp_ddc_status);
+		/*
+		 * First, Disable DDC
+		 * 0x0120 HDCP_DDC_CTRL_0
+		 * [0] DDC_DISABLE	Determines whether HDCP Ri and Pj reads
+		 *			are done unassisted by hardware or by
+		 *			software via HDMI_DDC (HDCP provides
+		 *			interrupts to request software
+		 *			transfers)
+		 *     0 : Use Hardware DDC
+		 *     1 : Use Software DDC
+		 */
+		HDMI_OUTP(HDCP_DDC_CTRL_0, 0x1);
+
+		/*
+		 * ACK the Failure to Clear it
+		 * 0x0124 HDCP_DDC_CTRL_1
+		 * [0] DDC_FAILED_ACK	Write 1 to clear
+		 *			HDCP_STATUS.HDCP_DDC_FAILED
+		 */
+		hdcp_ddc_ctrl1_reg = HDMI_INP(HDCP_DDC_CTRL_1);
+		HDMI_OUTP(HDCP_DDC_CTRL_1, hdcp_ddc_ctrl1_reg | 0x1);
+
+		/* Check if the FAILURE got Cleared */
+		hdcp_ddc_status = HDMI_INP(HDCP_DDC_STATUS);
+		hdcp_ddc_status = (hdcp_ddc_status >> 16) & 0x1;
+		if (hdcp_ddc_status == 0x0) {
+			DEV_INFO("%s: HDCP DDC Failure has been cleared\n",
+				  __func__);
+		} else {
+			DEV_WARN("%s: Error: HDCP DDC Failure DID NOT get"
+				 "cleared\n", __func__);
+		}
+
+		/* Re-Enable HDCP DDC */
+		HDMI_OUTP(HDCP_DDC_CTRL_0, 0x0);
+	}
+
+	if (nack0 == 0x1) {
+		/*
+		 * 0x020C HDMI_DDC_CTRL
+		 * [3] SW_STATUS_RESET	Write 1 to reset HDMI_DDC_SW_STATUS
+		 *			flags, will reset SW_DONE, ABORTED,
+		 *			TIMEOUT, SW_INTERRUPTED,
+		 *			BUFFER_OVERFLOW, STOPPED_ON_NACK, NACK0,
+		 *			NACK1, NACK2, NACK3
+		 */
+		HDMI_OUTP_ND(HDMI_DDC_CTRL,
+			     HDMI_INP(HDMI_DDC_CTRL) | (0x1 << 3));
+		msleep(20);
+		HDMI_OUTP_ND(HDMI_DDC_CTRL,
+			     HDMI_INP(HDMI_DDC_CTRL) & ~(0x1 << 3));
+	}
+
+	hdcp_ddc_status = HDMI_INP(HDCP_DDC_STATUS);
+
+	failure = (hdcp_ddc_status >> 16) & 0x1;
+	nack0 = (hdcp_ddc_status >> 14) & 0x1;
+	DEV_DBG("%s: On Exit: HDCP_DDC_STATUS = 0x%x, FAILURE = %d,"
+		"NACK0 = %d\n", __func__ , hdcp_ddc_status, failure, nack0);
+}
+
+
+static int hdcp_authentication_part1(void)
+{
+	int ret = 0;
+	boolean is_match;
+	boolean is_part1_done = FALSE;
+	uint32 timeout_count;
+	uint8 bcaps;
+	uint8 aksv[5];
+	uint32 qfprom_aksv_0, qfprom_aksv_1, link0_aksv_0, link0_aksv_1;
+	uint8 bksv[5];
+	uint32 link0_bksv_0, link0_bksv_1;
+	uint8 an[8];
+	uint32 link0_an_0, link0_an_1;
+	uint32 hpd_int_status, hpd_int_ctrl;
+
+
+	static uint8 buf[0xFF];
+	memset(buf, 0, sizeof(buf));
+
+	if (!is_part1_done) {
+		is_part1_done = TRUE;
+
+		/* Fetch aksv from QFprom, this info should be public. */
+		qfprom_aksv_0 = inpdw(QFPROM_BASE + 0x000060D8);
+		qfprom_aksv_1 = inpdw(QFPROM_BASE + 0x000060DC);
+
+		/* copy an and aksv to byte arrays for transmission */
+		aksv[0] =  qfprom_aksv_0        & 0xFF;
+		aksv[1] = (qfprom_aksv_0 >> 8)  & 0xFF;
+		aksv[2] = (qfprom_aksv_0 >> 16) & 0xFF;
+		aksv[3] = (qfprom_aksv_0 >> 24) & 0xFF;
+		aksv[4] =  qfprom_aksv_1        & 0xFF;
+		/* check there are 20 ones in AKSV */
+		if (hdmi_msm_count_one(aksv, 5) != 20) {
+			DEV_ERR("HDCP: AKSV read from QFPROM doesn't have "
+				"20 1's and 20 0's, FAIL (AKSV=%02x%08x)\n",
+			qfprom_aksv_1, qfprom_aksv_0);
+			ret = -EINVAL;
+			goto error;
+		}
+		DEV_DBG("HDCP: AKSV=%02x%08x\n", qfprom_aksv_1, qfprom_aksv_0);
+
+		/* 0x0288 HDCP_SW_LOWER_AKSV
+			[31:0] LOWER_AKSV */
+		/* 0x0284 HDCP_SW_UPPER_AKSV
+			[7:0] UPPER_AKSV */
+
+		/* This is the lower 32 bits of the SW
+		 * injected AKSV value(AKSV[31:0]) read
+		 * from the EFUSE. It is needed for HDCP
+		 * authentication and must be written
+		 * before enabling HDCP. */
+		HDMI_OUTP(0x0288, qfprom_aksv_0);
+		HDMI_OUTP(0x0284, qfprom_aksv_1);
+
+		msm_hdmi_init_ddc();
+
+		/* read Bcaps at 0x40 in HDCP Port */
+		ret = hdmi_msm_ddc_read(0x74, 0x40, &bcaps, 1, 5, "Bcaps",
+			TRUE);
+		if (ret) {
+			DEV_ERR("%s(%d): Read Bcaps failed", __func__,
+			    __LINE__);
+			goto error;
+		}
+		DEV_DBG("HDCP: Bcaps=%02x\n", bcaps);
+
+		/* HDCP setup prior to HDCP enabled */
+
+		/* 0x0148 HDCP_RCVPORT_DATA4
+			[15:8] LINK0_AINFO
+			[7:0] LINK0_AKSV_1 */
+		/* LINK0_AINFO	= 0x2 FEATURE 1.1 on.
+		 *		= 0x0 FEATURE 1.1 off*/
+		HDMI_OUTP(0x0148, 0x0);
+
+		/* 0x012C HDCP_ENTROPY_CTRL0
+			[31:0] BITS_OF_INFLUENCE_0 */
+		/* 0x025C HDCP_ENTROPY_CTRL1
+			[31:0] BITS_OF_INFLUENCE_1 */
+		HDMI_OUTP(0x012C, 0xB1FFB0FF);
+		HDMI_OUTP(0x025C, 0xF00DFACE);
+
+		/* 0x0114 HDCP_DEBUG_CTRL
+			[2]	DEBUG_RNG_CIPHER
+			else default 0 */
+		HDMI_OUTP(0x0114, HDMI_INP(0x0114) & 0xFFFFFFFB);
+
+		/* 0x0110 HDCP_CTRL
+			[8] ENCRYPTION_ENABLE
+			[0] ENABLE */
+		/* encryption_enable | enable  */
+		HDMI_OUTP(0x0110, (1 << 8) | (1 << 0));
+
+		/*
+		 * Check to see if a HDCP DDC Failure is indicated in
+		 * HDCP_DDC_STATUS. If yes, clear it.
+		 */
+		check_and_clear_HDCP_DDC_Failure();
+
+		/* 0x0118 HDCP_INT_CTRL
+		 *    [2] AUTH_SUCCESS_MASK	[R/W]	Mask bit for\
+		 *					HDCP Authentication
+		 *		Success interrupt - set to 1 to enable interrupt
+		 *
+		 *    [6] AUTH_FAIL_MASK	[R/W]	Mask bit for HDCP
+		 *					Authentication
+		 *		Lost interrupt set to 1 to enable interrupt
+		 *
+		 *    [7] AUTH_FAIL_INFO_ACK	[W]	Acknwledge bit for HDCP
+		 *		Auth Failure Info field - write 1 to clear
+		 *
+		 *   [10] DDC_XFER_REQ_MASK	[R/W]	Mask bit for HDCP\
+		 *					DDC Transfer
+		 *		Request interrupt - set to 1 to enable interrupt
+		 *
+		 *   [14] DDC_XFER_DONE_MASK	[R/W]	Mask bit for HDCP\
+		 *					DDC Transfer
+		 *		done interrupt - set to 1 to enable interrupt */
+		/* enable all HDCP ints */
+		HDMI_OUTP(0x0118, (1 << 2) | (1 << 6) | (1 << 7));
+
+		/* 0x011C HDCP_LINK0_STATUS
+		[8] AN_0_READY
+		[9] AN_1_READY */
+		/* wait for an0 and an1 ready bits to be set in LINK0_STATUS */
+
+		mutex_lock(&hdcp_auth_state_mutex);
+		timeout_count = 100;
+		while (((HDMI_INP_ND(0x011C) & (0x3 << 8)) != (0x3 << 8))
+			&& timeout_count--)
+			msleep(20);
+		if (!timeout_count) {
+			ret = -ETIMEDOUT;
+			DEV_ERR("%s(%d): timedout, An0=%d, An1=%d\n",
+				__func__, __LINE__,
+			(HDMI_INP_ND(0x011C) & BIT(8)) >> 8,
+			(HDMI_INP_ND(0x011C) & BIT(9)) >> 9);
+			mutex_unlock(&hdcp_auth_state_mutex);
+			goto error;
+		}
+
+		/* 0x0168 HDCP_RCVPORT_DATA12
+		   [23:8] BSTATUS
+		   [7:0] BCAPS */
+		HDMI_OUTP(0x0168, bcaps);
+
+		/* 0x014C HDCP_RCVPORT_DATA5
+		   [31:0] LINK0_AN_0 */
+		/* read an0 calculation */
+		link0_an_0 = HDMI_INP(0x014C);
+
+		/* 0x0150 HDCP_RCVPORT_DATA6
+		   [31:0] LINK0_AN_1 */
+		/* read an1 calculation */
+		link0_an_1 = HDMI_INP(0x0150);
+		mutex_unlock(&hdcp_auth_state_mutex);
+
+		/* three bits 28..30 */
+		hdcp_key_state((HDMI_INP(0x011C) >> 28) & 0x7);
+
+		/* 0x0144 HDCP_RCVPORT_DATA3
+		[31:0] LINK0_AKSV_0 public key
+		0x0148 HDCP_RCVPORT_DATA4
+		[15:8] LINK0_AINFO
+		[7:0]  LINK0_AKSV_1 public key */
+		link0_aksv_0 = HDMI_INP(0x0144);
+		link0_aksv_1 = HDMI_INP(0x0148);
+
+		/* copy an and aksv to byte arrays for transmission */
+		aksv[0] =  link0_aksv_0        & 0xFF;
+		aksv[1] = (link0_aksv_0 >> 8)  & 0xFF;
+		aksv[2] = (link0_aksv_0 >> 16) & 0xFF;
+		aksv[3] = (link0_aksv_0 >> 24) & 0xFF;
+		aksv[4] =  link0_aksv_1        & 0xFF;
+
+		an[0] =  link0_an_0        & 0xFF;
+		an[1] = (link0_an_0 >> 8)  & 0xFF;
+		an[2] = (link0_an_0 >> 16) & 0xFF;
+		an[3] = (link0_an_0 >> 24) & 0xFF;
+		an[4] =  link0_an_1        & 0xFF;
+		an[5] = (link0_an_1 >> 8)  & 0xFF;
+		an[6] = (link0_an_1 >> 16) & 0xFF;
+		an[7] = (link0_an_1 >> 24) & 0xFF;
+
+		/* Write An 8 bytes to offset 0x18 */
+		ret = hdmi_msm_ddc_write(0x74, 0x18, an, 8, "An");
+		if (ret) {
+			DEV_ERR("%s(%d): Write An failed", __func__, __LINE__);
+			goto error;
+		}
+
+		/* Write Aksv 5 bytes to offset 0x10 */
+		ret = hdmi_msm_ddc_write(0x74, 0x10, aksv, 5, "Aksv");
+		if (ret) {
+			DEV_ERR("%s(%d): Write Aksv failed", __func__,
+			    __LINE__);
+			goto error;
+		}
+		DEV_DBG("HDCP: Link0-AKSV=%02x%08x\n",
+			link0_aksv_1 & 0xFF, link0_aksv_0);
+
+		/* Read Bksv 5 bytes at 0x00 in HDCP port */
+		ret = hdmi_msm_ddc_read(0x74, 0x00, bksv, 5, 5, "Bksv", TRUE);
+		if (ret) {
+			DEV_ERR("%s(%d): Read BKSV failed", __func__, __LINE__);
+			goto error;
+		}
+		/* check there are 20 ones in BKSV */
+		if (hdmi_msm_count_one(bksv, 5) != 20) {
+			DEV_ERR("HDCP: BKSV read from Sink doesn't have "
+				"20 1's and 20 0's, FAIL (BKSV="
+				"%02x%02x%02x%02x%02x)\n",
+				bksv[4], bksv[3], bksv[2], bksv[1], bksv[0]);
+			ret = -EINVAL;
+			goto error;
+		}
+
+		link0_bksv_0 = bksv[3];
+		link0_bksv_0 = (link0_bksv_0 << 8) | bksv[2];
+		link0_bksv_0 = (link0_bksv_0 << 8) | bksv[1];
+		link0_bksv_0 = (link0_bksv_0 << 8) | bksv[0];
+		link0_bksv_1 = bksv[4];
+		DEV_DBG("HDCP: BKSV=%02x%08x\n", link0_bksv_1, link0_bksv_0);
+
+		/* 0x0134 HDCP_RCVPORT_DATA0
+		   [31:0] LINK0_BKSV_0 */
+		HDMI_OUTP(0x0134, link0_bksv_0);
+		/* 0x0138 HDCP_RCVPORT_DATA1
+		   [31:0] LINK0_BKSV_1 */
+		HDMI_OUTP(0x0138, link0_bksv_1);
+		DEV_DBG("HDCP: Link0-BKSV=%02x%08x\n", link0_bksv_1,
+		    link0_bksv_0);
+
+		/* HDMI_HPD_INT_STATUS[0x0250] */
+		hpd_int_status = HDMI_INP_ND(0x0250);
+		/* HDMI_HPD_INT_CTRL[0x0254] */
+		hpd_int_ctrl = HDMI_INP_ND(0x0254);
+		DEV_DBG("[SR-DEUG]: HPD_INTR_CTRL=[%u] HPD_INTR_STATUS=[%u] "
+			"before reading R0'\n", hpd_int_ctrl, hpd_int_status);
+
+		/*
+		* HDCP Compliace Test case 1B-01:
+		* Wait here until all the ksv bytes have been
+		* read from the KSV FIFO register.
+		*/
+		msleep(125);
+
+		/* Reading R0' 2 bytes at offset 0x08 */
+		ret = hdmi_msm_ddc_read(0x74, 0x08, buf, 2, 5, "RO'", TRUE);
+		if (ret) {
+			DEV_ERR("%s(%d): Read RO's failed", __func__,
+			    __LINE__);
+			goto error;
+		}
+
+		DEV_DBG("HDCP: R0'=%02x%02x\n", buf[1], buf[0]);
+		INIT_COMPLETION(hdmi_msm_state->hdcp_success_done);
+		/* 0x013C HDCP_RCVPORT_DATA2_0
+		[15:0] LINK0_RI */
+		HDMI_OUTP(0x013C, (((uint32)buf[1]) << 8) | buf[0]);
+
+		timeout_count = wait_for_completion_interruptible_timeout(
+			&hdmi_msm_state->hdcp_success_done, HZ*2);
+
+		if (!timeout_count) {
+			ret = -ETIMEDOUT;
+			is_match = HDMI_INP(0x011C) & BIT(12);
+			DEV_ERR("%s(%d): timedout, Link0=<%s>\n", __func__,
+			  __LINE__,
+			  is_match ? "RI_MATCH" : "No RI Match INTR in time");
+			if (!is_match)
+				goto error;
+		}
+
+		/* 0x011C HDCP_LINK0_STATUS
+		[12] RI_MATCHES	[0] MISMATCH, [1] MATCH
+		[0] AUTH_SUCCESS */
+		/* Checking for RI, R0 Match */
+		/* RI_MATCHES */
+		if ((HDMI_INP(0x011C) & BIT(12)) != BIT(12)) {
+			ret = -EINVAL;
+			DEV_ERR("%s: HDCP_LINK0_STATUS[RI_MATCHES]: MISMATCH\n",
+			    __func__);
+			goto error;
+		}
+
+		DEV_INFO("HDCP: authentication part I, successful\n");
+		is_part1_done = FALSE;
+		return 0;
+error:
+		DEV_ERR("[%s]: HDCP Reauthentication\n", __func__);
+		is_part1_done = FALSE;
+		return ret;
+	} else {
+		return 1;
+	}
+}
+
+static int hdmi_msm_transfer_v_h(void)
+{
+	/* Read V'.HO 4 Byte at offset 0x20 */
+	char what[20];
+	int ret;
+	uint8 buf[4];
+
+	snprintf(what, sizeof(what), "V' H0");
+	ret = hdmi_msm_ddc_read(0x74, 0x20, buf, 4, 5, what, TRUE);
+	if (ret) {
+		DEV_ERR("%s: Read %s failed", __func__, what);
+		return ret;
+	}
+	DEV_DBG("buf[0]= %x , buf[1] = %x , buf[2] = %x , buf[3] = %x\n ",
+			buf[0] , buf[1] , buf[2] , buf[3]);
+
+	/* 0x0154 HDCP_RCVPORT_DATA7
+	   [31:0] V_HO */
+	HDMI_OUTP(0x0154 ,
+		(buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0]));
+
+	snprintf(what, sizeof(what), "V' H1");
+	ret = hdmi_msm_ddc_read(0x74, 0x24, buf, 4, 5, what, TRUE);
+	if (ret) {
+		DEV_ERR("%s: Read %s failed", __func__, what);
+		return ret;
+	}
+	DEV_DBG("buf[0]= %x , buf[1] = %x , buf[2] = %x , buf[3] = %x\n ",
+			buf[0] , buf[1] , buf[2] , buf[3]);
+
+	/* 0x0158 HDCP_RCVPORT_ DATA8
+	   [31:0] V_H1 */
+	HDMI_OUTP(0x0158,
+		(buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0]));
+
+
+	snprintf(what, sizeof(what), "V' H2");
+	ret = hdmi_msm_ddc_read(0x74, 0x28, buf, 4, 5, what, TRUE);
+	if (ret) {
+		DEV_ERR("%s: Read %s failed", __func__, what);
+		return ret;
+	}
+	DEV_DBG("buf[0]= %x , buf[1] = %x , buf[2] = %x , buf[3] = %x\n ",
+			buf[0] , buf[1] , buf[2] , buf[3]);
+
+	/* 0x015c HDCP_RCVPORT_DATA9
+	   [31:0] V_H2 */
+	HDMI_OUTP(0x015c ,
+		(buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0]));
+
+	snprintf(what, sizeof(what), "V' H3");
+	ret = hdmi_msm_ddc_read(0x74, 0x2c, buf, 4, 5, what, TRUE);
+	if (ret) {
+		DEV_ERR("%s: Read %s failed", __func__, what);
+		return ret;
+	}
+	DEV_DBG("buf[0]= %x , buf[1] = %x , buf[2] = %x , buf[3] = %x\n ",
+			buf[0] , buf[1] , buf[2] , buf[3]);
+
+	/* 0x0160 HDCP_RCVPORT_DATA10
+	   [31:0] V_H3 */
+	HDMI_OUTP(0x0160,
+		(buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0]));
+
+	snprintf(what, sizeof(what), "V' H4");
+	ret = hdmi_msm_ddc_read(0x74, 0x30, buf, 4, 5, what, TRUE);
+	if (ret) {
+		DEV_ERR("%s: Read %s failed", __func__, what);
+		return ret;
+	}
+	DEV_DBG("buf[0]= %x , buf[1] = %x , buf[2] = %x , buf[3] = %x\n ",
+			buf[0] , buf[1] , buf[2] , buf[3]);
+	/* 0x0164 HDCP_RCVPORT_DATA11
+	   [31:0] V_H4 */
+	HDMI_OUTP(0x0164,
+		(buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0]));
+
+	return 0;
+}
+
+static int hdcp_authentication_part2(void)
+{
+	int ret = 0;
+	uint32 timeout_count;
+	int i = 0;
+	int cnt = 0;
+	uint bstatus;
+	uint8 bcaps;
+	uint32 down_stream_devices;
+	uint32 ksv_bytes;
+
+	static uint8 buf[0xFF];
+	static uint8 kvs_fifo[5 * 127];
+
+	boolean max_devs_exceeded = 0;
+	boolean max_cascade_exceeded = 0;
+
+	boolean ksv_done = FALSE;
+
+	memset(buf, 0, sizeof(buf));
+	memset(kvs_fifo, 0, sizeof(kvs_fifo));
+
+	/* wait until READY bit is set in bcaps */
+	timeout_count = 50;
+	do {
+		timeout_count--;
+		/* read bcaps 1 Byte at offset 0x40 */
+		ret = hdmi_msm_ddc_read(0x74, 0x40, &bcaps, 1, 1,
+		    "Bcaps", FALSE);
+		if (ret) {
+			DEV_ERR("%s(%d): Read Bcaps failed", __func__,
+			    __LINE__);
+			goto error;
+		}
+		msleep(100);
+	} while ((0 == (bcaps & 0x20)) && timeout_count); /* READY (Bit 5) */
+	if (!timeout_count) {
+		ret = -ETIMEDOUT;
+		DEV_ERR("%s:timedout(1)", __func__);
+		goto error;
+	}
+
+	/* read bstatus 2 bytes at offset 0x41 */
+
+	ret = hdmi_msm_ddc_read(0x74, 0x41, buf, 2, 5, "Bstatus", FALSE);
+	if (ret) {
+		DEV_ERR("%s(%d): Read Bstatus failed", __func__, __LINE__);
+		goto error;
+	}
+	bstatus = buf[1];
+	bstatus = (bstatus << 8) | buf[0];
+	/* 0x0168 DCP_RCVPORT_DATA12
+	[7:0] BCAPS
+	[23:8 BSTATUS */
+	HDMI_OUTP(0x0168, bcaps | (bstatus << 8));
+	/* BSTATUS [6:0] DEVICE_COUNT Number of HDMI device attached to repeater
+	* - see HDCP spec */
+	down_stream_devices = bstatus & 0x7F;
+
+	if (down_stream_devices == 0x0) {
+		/* There isn't any devices attaced to the Repeater */
+		DEV_ERR("%s: there isn't any devices attached to the "
+		    "Repeater\n", __func__);
+		ret = -EINVAL;
+		goto error;
+	}
+
+	/*
+	* HDCP Compliance 1B-05:
+	* Check if no. of devices connected to repeater
+	* exceed max_devices_connected from bit 7 of Bstatus.
+	*/
+	max_devs_exceeded = (bstatus & 0x80) >> 7;
+	if (max_devs_exceeded == 0x01) {
+		DEV_ERR("%s: Number of devs connected to repeater "
+		    "exceeds max_devs\n", __func__);
+		ret = -EINVAL;
+		goto hdcp_error;
+	}
+
+	/*
+	* HDCP Compliance 1B-06:
+	* Check if no. of cascade connected to repeater
+	* exceed max_cascade_connected from bit 11 of Bstatus.
+	*/
+	max_cascade_exceeded = (bstatus & 0x800) >> 11;
+	if (max_cascade_exceeded == 0x01) {
+		DEV_ERR("%s: Number of cascade connected to repeater "
+		    "exceeds max_cascade\n", __func__);
+		ret = -EINVAL;
+		goto hdcp_error;
+	}
+
+	/* Read KSV FIFO over DDC
+	* Key Slection vector FIFO
+	* Used to pull downstream KSVs from HDCP Repeaters.
+	* All bytes (DEVICE_COUNT * 5) must be read in a single,
+	*   auto incrementing access.
+	* All bytes read as 0x00 for HDCP Receivers that are not
+	*   HDCP Repeaters (REPEATER == 0). */
+	ksv_bytes = 5 * down_stream_devices;
+	/* Reading KSV FIFO / KSV FIFO */
+	ksv_done = FALSE;
+
+	ret = hdmi_msm_ddc_read(0x74, 0x43, kvs_fifo, ksv_bytes, 5,
+	"KSV FIFO", TRUE);
+	do {
+		if (ret) {
+			DEV_ERR("%s(%d): Read KSV FIFO failed",
+			    __func__, __LINE__);
+			/*
+			* HDCP Compliace Test case 1B-01:
+			* Wait here until all the ksv bytes have been
+			* read from the KSV FIFO register.
+			*/
+			msleep(25);
+		} else {
+			ksv_done = TRUE;
+		}
+		cnt++;
+	} while (!ksv_done && cnt != 20);
+
+	if (ksv_done == FALSE)
+		goto error;
+
+	ret = hdmi_msm_transfer_v_h();
+	if (ret)
+		goto error;
+
+	/* Next: Write KSV FIFO to HDCP_SHA_DATA.
+	* This is done 1 byte at time starting with the LSB.
+	* On the very last byte write,
+	* the HDCP_SHA_DATA_DONE bit[0]
+	*/
+
+	/* 0x023C HDCP_SHA_CTRL
+	[0] RESET	[0] Enable, [1] Reset
+	[4] SELECT	[0] DIGA_HDCP, [1] DIGB_HDCP */
+	/* reset SHA engine */
+	HDMI_OUTP(0x023C, 1);
+	/* enable SHA engine, SEL=DIGA_HDCP */
+	HDMI_OUTP(0x023C, 0);
+
+	for (i = 0; i < ksv_bytes - 1; i++) {
+		/* Write KSV byte and do not set DONE bit[0] */
+		HDMI_OUTP_ND(0x0244, kvs_fifo[i] << 16);
+
+		/* Once 64 bytes have been written, we need to poll for
+		 * HDCP_SHA_BLOCK_DONE before writing any further
+		 */
+		if (i && !((i+1)%64)) {
+			timeout_count = 100;
+			while (!(HDMI_INP_ND(0x0240) & 0x1)
+					&& (--timeout_count)) {
+				DEV_DBG("HDCP Auth Part II: Waiting for the "
+					"computation of the current 64 byte to "
+					"complete. HDCP_SHA_STATUS=%08x. "
+					"timeout_count=%d\n",
+					 HDMI_INP_ND(0x0240), timeout_count);
+				msleep(20);
+			}
+			if (!timeout_count) {
+				ret = -ETIMEDOUT;
+				DEV_ERR("%s(%d): timedout", __func__, __LINE__);
+				goto error;
+			}
+		}
+
+	}
+
+	/* Write l to DONE bit[0] */
+	HDMI_OUTP_ND(0x0244, (kvs_fifo[ksv_bytes - 1] << 16) | 0x1);
+
+	/* 0x0240 HDCP_SHA_STATUS
+	[4] COMP_DONE */
+	/* Now wait for HDCP_SHA_COMP_DONE */
+	timeout_count = 100;
+	while ((0x10 != (HDMI_INP_ND(0x0240) & 0xFFFFFF10)) && --timeout_count)
+		msleep(20);
+
+	if (!timeout_count) {
+		ret = -ETIMEDOUT;
+		DEV_ERR("%s(%d): timedout", __func__, __LINE__);
+		goto error;
+	}
+
+	/* 0x011C HDCP_LINK0_STATUS
+	[20] V_MATCHES */
+	timeout_count = 100;
+	while (((HDMI_INP_ND(0x011C) & (1 << 20)) != (1 << 20))
+	    && --timeout_count) {
+		msleep(20);
+	}
+
+	if (!timeout_count) {
+		ret = -ETIMEDOUT;
+		DEV_ERR("%s(%d): timedout", __func__, __LINE__);
+		goto error;
+	}
+
+	DEV_INFO("HDCP: authentication part II, successful\n");
+
+hdcp_error:
+error:
+	return ret;
+}
+
+static int hdcp_authentication_part3(uint32 found_repeater)
+{
+	int ret = 0;
+	int poll = 3000;
+	while (poll) {
+		/* 0x011C HDCP_LINK0_STATUS
+		    [30:28]  KEYS_STATE = 3 = "Valid"
+		    [24] RO_COMPUTATION_DONE	[0] Not Done, [1] Done
+		    [20] V_MATCHES		[0] Mismtach, [1] Match
+		    [12] RI_MATCHES		[0] Mismatch, [1] Match
+		    [0] AUTH_SUCCESS */
+		if (HDMI_INP_ND(0x011C) != (0x31001001 |
+		    (found_repeater << 20))) {
+			DEV_ERR("HDCP: autentication part III, FAILED, "
+			    "Link Status=%08x\n", HDMI_INP(0x011C));
+			ret = -EINVAL;
+			goto error;
+		}
+		poll--;
+	}
+
+	DEV_INFO("HDCP: authentication part III, successful\n");
+
+error:
+	return ret;
+}
+
+static void hdmi_msm_hdcp_enable(void)
+{
+	int ret = 0;
+	uint8 bcaps;
+	uint32 found_repeater = 0x0;
+	char *envp[2];
+
+	if (!hdmi_msm_has_hdcp()) {
+		switch_set_state(&external_common_state->sdev, 1);
+		DEV_INFO("Hdmi state switch to %d: %s\n",
+			external_common_state->sdev.state, __func__);
+		return;
+	}
+
+	mutex_lock(&hdmi_msm_state_mutex);
+	hdmi_msm_state->hdcp_activating = TRUE;
+	mutex_unlock(&hdmi_msm_state_mutex);
+
+	fill_black_screen();
+
+	mutex_lock(&hdcp_auth_state_mutex);
+	/*
+	 * Initialize this to zero here to make
+	 * sure HPD has not happened yet
+	 */
+	hdmi_msm_state->hpd_during_auth = FALSE;
+	/* This flag prevents other threads from re-authenticating
+	* after we've just authenticated (i.e., finished part3)
+	* We probably need to protect this in a mutex lock */
+	hdmi_msm_state->full_auth_done = FALSE;
+	mutex_unlock(&hdcp_auth_state_mutex);
+
+	/* PART I Authentication*/
+	ret = hdcp_authentication_part1();
+	if (ret)
+		goto error;
+
+	/* PART II Authentication*/
+	/* read Bcaps at 0x40 in HDCP Port */
+	ret = hdmi_msm_ddc_read(0x74, 0x40, &bcaps, 1, 5, "Bcaps", FALSE);
+	if (ret) {
+		DEV_ERR("%s(%d): Read Bcaps failed\n", __func__, __LINE__);
+		goto error;
+	}
+	DEV_DBG("HDCP: Bcaps=0x%02x (%s)\n", bcaps,
+		(bcaps & BIT(6)) ? "repeater" : "no repeater");
+
+	/* if REPEATER (Bit 6), perform Part2 Authentication */
+	if (bcaps & BIT(6)) {
+		found_repeater = 0x1;
+		ret = hdcp_authentication_part2();
+		if (ret)
+			goto error;
+	} else
+		DEV_INFO("HDCP: authentication part II skipped, no repeater\n");
+
+	/* PART III Authentication*/
+	ret = hdcp_authentication_part3(found_repeater);
+	if (ret)
+		goto error;
+
+	unfill_black_screen();
+
+	external_common_state->hdcp_active = TRUE;
+	mutex_lock(&hdmi_msm_state_mutex);
+	hdmi_msm_state->hdcp_activating = FALSE;
+	mutex_unlock(&hdmi_msm_state_mutex);
+
+	mutex_lock(&hdcp_auth_state_mutex);
+	/*
+	 * This flag prevents other threads from re-authenticating
+	 * after we've just authenticated (i.e., finished part3)
+	 */
+	hdmi_msm_state->full_auth_done = TRUE;
+	mutex_unlock(&hdcp_auth_state_mutex);
+
+	if (!hdmi_msm_is_dvi_mode()) {
+		DEV_INFO("HDMI HPD: sense : send HDCP_PASS\n");
+		envp[0] = "HDCP_STATE=PASS";
+		envp[1] = NULL;
+		kobject_uevent_env(external_common_state->uevent_kobj,
+		    KOBJ_CHANGE, envp);
+	}
+	switch_set_state(&external_common_state->sdev, 1);
+	DEV_INFO("Hdmi state switch to %d: %s\n",
+		external_common_state->sdev.state, __func__);
+	return;
+
+error:
+	mutex_lock(&hdmi_msm_state_mutex);
+	hdmi_msm_state->hdcp_activating = FALSE;
+	mutex_unlock(&hdmi_msm_state_mutex);
+	if (hdmi_msm_state->hpd_during_auth) {
+		DEV_WARN("Calling Deauthentication: HPD occured during "
+			 "authentication  from [%s]\n", __func__);
+		hdcp_deauthenticate();
+		mutex_lock(&hdcp_auth_state_mutex);
+		hdmi_msm_state->hpd_during_auth = FALSE;
+		mutex_unlock(&hdcp_auth_state_mutex);
+	} else {
+		DEV_WARN("[DEV_DBG]: Calling reauth from [%s]\n", __func__);
+		if (hdmi_msm_state->panel_power_on)
+			queue_work(hdmi_work_queue,
+			    &hdmi_msm_state->hdcp_reauth_work);
+	}
+	switch_set_state(&external_common_state->sdev, 0);
+	DEV_INFO("Hdmi state switch to %d: %s\n",
+		external_common_state->sdev.state, __func__);
+}
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
+
+static void hdmi_msm_video_setup(int video_format)
+{
+	uint32 total_v   = 0;
+	uint32 total_h   = 0;
+	uint32 start_h   = 0;
+	uint32 end_h     = 0;
+	uint32 start_v   = 0;
+	uint32 end_v     = 0;
+	const struct hdmi_disp_mode_timing_type *timing =
+		hdmi_common_get_supported_mode(video_format);
+
+	/* timing register setup */
+	if (timing == NULL) {
+		DEV_ERR("video format not supported: %d\n", video_format);
+		return;
+	}
+
+	/* Hsync Total and Vsync Total */
+	total_h = timing->active_h + timing->front_porch_h
+		+ timing->back_porch_h + timing->pulse_width_h - 1;
+	total_v = timing->active_v + timing->front_porch_v
+		+ timing->back_porch_v + timing->pulse_width_v - 1;
+	/* 0x02C0 HDMI_TOTAL
+	   [27:16] V_TOTAL Vertical Total
+	   [11:0]  H_TOTAL Horizontal Total */
+	HDMI_OUTP(0x02C0, ((total_v << 16) & 0x0FFF0000)
+		| ((total_h << 0) & 0x00000FFF));
+
+	/* Hsync Start and Hsync End */
+	start_h = timing->back_porch_h + timing->pulse_width_h;
+	end_h   = (total_h + 1) - timing->front_porch_h;
+	/* 0x02B4 HDMI_ACTIVE_H
+	   [27:16] END Horizontal end
+	   [11:0]  START Horizontal start */
+	HDMI_OUTP(0x02B4, ((end_h << 16) & 0x0FFF0000)
+		| ((start_h << 0) & 0x00000FFF));
+
+	start_v = timing->back_porch_v + timing->pulse_width_v - 1;
+	end_v   = total_v - timing->front_porch_v;
+	/* 0x02B8 HDMI_ACTIVE_V
+	   [27:16] END Vertical end
+	   [11:0]  START Vertical start */
+	HDMI_OUTP(0x02B8, ((end_v << 16) & 0x0FFF0000)
+		| ((start_v << 0) & 0x00000FFF));
+
+	if (timing->interlaced) {
+		/* 0x02C4 HDMI_V_TOTAL_F2
+		   [11:0] V_TOTAL_F2 Vertical total for field2 */
+		HDMI_OUTP(0x02C4, ((total_v + 1) << 0) & 0x00000FFF);
+
+		/* 0x02BC HDMI_ACTIVE_V_F2
+		   [27:16] END_F2 Vertical end for field2
+		   [11:0]  START_F2 Vertical start for Field2 */
+		HDMI_OUTP(0x02BC,
+			  (((start_v + 1) << 0) & 0x00000FFF)
+			| (((end_v + 1) << 16) & 0x0FFF0000));
+	} else {
+		/* HDMI_V_TOTAL_F2 */
+		HDMI_OUTP(0x02C4, 0);
+		/* HDMI_ACTIVE_V_F2 */
+		HDMI_OUTP(0x02BC, 0);
+	}
+
+	hdmi_frame_ctrl_cfg(timing);
+}
+
+struct hdmi_msm_audio_acr {
+	uint32 n;	/* N parameter for clock regeneration */
+	uint32 cts;	/* CTS parameter for clock regeneration */
+};
+
+struct hdmi_msm_audio_arcs {
+	uint32 pclk;
+	struct hdmi_msm_audio_acr lut[MSM_HDMI_SAMPLE_RATE_MAX];
+};
+
+#define HDMI_MSM_AUDIO_ARCS(pclk, ...) { pclk, __VA_ARGS__ }
+
+/* Audio constants lookup table for hdmi_msm_audio_acr_setup */
+/* Valid Pixel-Clock rates: 25.2MHz, 27MHz, 27.03MHz, 74.25MHz, 148.5MHz */
+static const struct hdmi_msm_audio_arcs hdmi_msm_audio_acr_lut[] = {
+	/*  25.200MHz  */
+	HDMI_MSM_AUDIO_ARCS(25200, {
+		{4096, 25200}, {6272, 28000}, {6144, 25200}, {12544, 28000},
+		{12288, 25200}, {25088, 28000}, {24576, 25200} }),
+	/*  27.000MHz  */
+	HDMI_MSM_AUDIO_ARCS(27000, {
+		{4096, 27000}, {6272, 30000}, {6144, 27000}, {12544, 30000},
+		{12288, 27000}, {25088, 30000}, {24576, 27000} }),
+	/*  27.027MHz */
+	HDMI_MSM_AUDIO_ARCS(27030, {
+		{4096, 27027}, {6272, 30030}, {6144, 27027}, {12544, 30030},
+		{12288, 27027}, {25088, 30030}, {24576, 27027} }),
+	/*  74.250MHz */
+	HDMI_MSM_AUDIO_ARCS(74250, {
+		{4096, 74250}, {6272, 82500}, {6144, 74250}, {12544, 82500},
+		{12288, 74250}, {25088, 82500}, {24576, 74250} }),
+	/* 148.500MHz */
+	HDMI_MSM_AUDIO_ARCS(148500, {
+		{4096, 148500}, {6272, 165000}, {6144, 148500}, {12544, 165000},
+		{12288, 148500}, {25088, 165000}, {24576, 148500} }),
+};
+
+static void hdmi_msm_audio_acr_setup(boolean enabled, int video_format,
+	int audio_sample_rate, int num_of_channels)
+{
+	/* Read first before writing */
+	/* HDMI_ACR_PKT_CTRL[0x0024] */
+	uint32 acr_pck_ctrl_reg = HDMI_INP(0x0024);
+
+	if (enabled) {
+		const struct hdmi_disp_mode_timing_type *timing =
+			hdmi_common_get_supported_mode(video_format);
+		const struct hdmi_msm_audio_arcs *audio_arc =
+			&hdmi_msm_audio_acr_lut[0];
+		const int lut_size = sizeof(hdmi_msm_audio_acr_lut)
+			/sizeof(*hdmi_msm_audio_acr_lut);
+		uint32 i, n, cts, layout, multiplier, aud_pck_ctrl_2_reg;
+
+		if (timing == NULL) {
+			DEV_WARN("%s: video format %d not supported\n",
+				__func__, video_format);
+			return;
+		}
+
+		for (i = 0; i < lut_size;
+			audio_arc = &hdmi_msm_audio_acr_lut[++i]) {
+			if (audio_arc->pclk == timing->pixel_freq)
+				break;
+		}
+		if (i >= lut_size) {
+			DEV_WARN("%s: pixel clock %d not supported\n", __func__,
+				timing->pixel_freq);
+			return;
+		}
+
+		n = audio_arc->lut[audio_sample_rate].n;
+		cts = audio_arc->lut[audio_sample_rate].cts;
+		layout = (MSM_HDMI_AUDIO_CHANNEL_2 == num_of_channels) ? 0 : 1;
+
+		if ((MSM_HDMI_SAMPLE_RATE_192KHZ == audio_sample_rate) ||
+		    (MSM_HDMI_SAMPLE_RATE_176_4KHZ == audio_sample_rate)) {
+			multiplier = 4;
+			n >>= 2; /* divide N by 4 and use multiplier */
+		} else if ((MSM_HDMI_SAMPLE_RATE_96KHZ == audio_sample_rate) ||
+			  (MSM_HDMI_SAMPLE_RATE_88_2KHZ == audio_sample_rate)) {
+			multiplier = 2;
+			n >>= 1; /* divide N by 2 and use multiplier */
+		} else {
+			multiplier = 1;
+		}
+		DEV_DBG("%s: n=%u, cts=%u, layout=%u\n", __func__, n, cts,
+			layout);
+
+		/* AUDIO_PRIORITY | SOURCE */
+		acr_pck_ctrl_reg |= 0x80000100;
+		/* N_MULTIPLE(multiplier) */
+		acr_pck_ctrl_reg |= (multiplier & 7) << 16;
+
+		if ((MSM_HDMI_SAMPLE_RATE_48KHZ == audio_sample_rate) ||
+		    (MSM_HDMI_SAMPLE_RATE_96KHZ == audio_sample_rate) ||
+		    (MSM_HDMI_SAMPLE_RATE_192KHZ == audio_sample_rate)) {
+			/* SELECT(3) */
+			acr_pck_ctrl_reg |= 3 << 4;
+			/* CTS_48 */
+			cts <<= 12;
+
+			/* CTS: need to determine how many fractional bits */
+			/* HDMI_ACR_48_0 */
+			HDMI_OUTP(0x00D4, cts);
+			/* N */
+			/* HDMI_ACR_48_1 */
+			HDMI_OUTP(0x00D8, n);
+		} else if ((MSM_HDMI_SAMPLE_RATE_44_1KHZ == audio_sample_rate)
+			   || (MSM_HDMI_SAMPLE_RATE_88_2KHZ ==
+			       audio_sample_rate)
+			   || (MSM_HDMI_SAMPLE_RATE_176_4KHZ ==
+			       audio_sample_rate)) {
+			/* SELECT(2) */
+			acr_pck_ctrl_reg |= 2 << 4;
+			/* CTS_44 */
+			cts <<= 12;
+
+			/* CTS: need to determine how many fractional bits */
+			/* HDMI_ACR_44_0 */
+			HDMI_OUTP(0x00CC, cts);
+			/* N */
+			/* HDMI_ACR_44_1 */
+			HDMI_OUTP(0x00D0, n);
+		} else {	/* default to 32k */
+			/* SELECT(1) */
+			acr_pck_ctrl_reg |= 1 << 4;
+			/* CTS_32 */
+			cts <<= 12;
+
+			/* CTS: need to determine how many fractional bits */
+			/* HDMI_ACR_32_0 */
+			HDMI_OUTP(0x00C4, cts);
+			/* N */
+			/* HDMI_ACR_32_1 */
+			HDMI_OUTP(0x00C8, n);
+		}
+		/* Payload layout depends on number of audio channels */
+		/* LAYOUT_SEL(layout) */
+		aud_pck_ctrl_2_reg = 1 | (layout << 1);
+		/* override | layout */
+		/* HDMI_AUDIO_PKT_CTRL2[0x00044] */
+		HDMI_OUTP(0x00044, aud_pck_ctrl_2_reg);
+
+		/* SEND | CONT */
+		acr_pck_ctrl_reg |= 0x00000003;
+	} else {
+		/* ~(SEND | CONT) */
+		acr_pck_ctrl_reg &= ~0x00000003;
+	}
+	/* HDMI_ACR_PKT_CTRL[0x0024] */
+	HDMI_OUTP(0x0024, acr_pck_ctrl_reg);
+}
+
+static void hdmi_msm_outpdw_chk(uint32 offset, uint32 data)
+{
+	uint32 check, i = 0;
+
+#ifdef DEBUG
+	HDMI_OUTP(offset, data);
+#endif
+	do {
+		outpdw(MSM_HDMI_BASE+offset, data);
+		check = inpdw(MSM_HDMI_BASE+offset);
+	} while (check != data && i++ < 10);
+
+	if (check != data)
+		DEV_ERR("%s: failed addr=%08x, data=%x, check=%x",
+			__func__, offset, data, check);
+}
+
+static void hdmi_msm_rmw32or(uint32 offset, uint32 data)
+{
+	uint32 reg_data;
+	reg_data = inpdw(MSM_HDMI_BASE+offset);
+	reg_data = inpdw(MSM_HDMI_BASE+offset);
+	hdmi_msm_outpdw_chk(offset, reg_data | data);
+}
+
+
+#define HDMI_AUDIO_CFG				0x01D0
+#define HDMI_AUDIO_ENGINE_ENABLE		1
+#define HDMI_AUDIO_FIFO_MASK			0x000000F0
+#define HDMI_AUDIO_FIFO_WATERMARK_SHIFT		4
+#define HDMI_AUDIO_FIFO_MAX_WATER_MARK		8
+
+
+int hdmi_audio_enable(bool on , u32 fifo_water_mark)
+{
+	u32 hdmi_audio_config;
+
+	hdmi_audio_config = HDMI_INP(HDMI_AUDIO_CFG);
+
+	if (on) {
+
+		if (fifo_water_mark > HDMI_AUDIO_FIFO_MAX_WATER_MARK) {
+			pr_err("%s : HDMI audio fifo water mark can not be more"
+				" than %u\n", __func__,
+				HDMI_AUDIO_FIFO_MAX_WATER_MARK);
+			return -EINVAL;
+		}
+
+		/*
+		 *  Enable HDMI Audio engine.
+		 *  MUST be enabled after Audio DMA is enabled.
+		*/
+		hdmi_audio_config &= ~(HDMI_AUDIO_FIFO_MASK);
+
+		hdmi_audio_config |= (HDMI_AUDIO_ENGINE_ENABLE |
+			 (fifo_water_mark << HDMI_AUDIO_FIFO_WATERMARK_SHIFT));
+
+	} else
+		 hdmi_audio_config &= ~(HDMI_AUDIO_ENGINE_ENABLE);
+
+	HDMI_OUTP(HDMI_AUDIO_CFG, hdmi_audio_config);
+
+	mb();
+	pr_info("%s :HDMI_AUDIO_CFG 0x%08x\n", __func__,
+		HDMI_INP(HDMI_AUDIO_CFG));
+
+	return 0;
+}
+EXPORT_SYMBOL(hdmi_audio_enable);
+
+#define HDMI_AUDIO_PKT_CTRL			0x0020
+#define HDMI_AUDIO_SAMPLE_SEND_ENABLE		1
+
+int hdmi_audio_packet_enable(bool on)
+{
+	u32 hdmi_audio_pkt_ctrl;
+	hdmi_audio_pkt_ctrl = HDMI_INP(HDMI_AUDIO_PKT_CTRL);
+
+	if (on)
+		hdmi_audio_pkt_ctrl |= HDMI_AUDIO_SAMPLE_SEND_ENABLE;
+	else
+		hdmi_audio_pkt_ctrl &= ~(HDMI_AUDIO_SAMPLE_SEND_ENABLE);
+
+	HDMI_OUTP(HDMI_AUDIO_PKT_CTRL, hdmi_audio_pkt_ctrl);
+
+	mb();
+	pr_info("%s : HDMI_AUDIO_PKT_CTRL 0x%08x\n", __func__,
+	HDMI_INP(HDMI_AUDIO_PKT_CTRL));
+	return 0;
+}
+EXPORT_SYMBOL(hdmi_audio_packet_enable);
+
+
+/* TO-DO: return -EINVAL when num_of_channels and channel_allocation
+ *  does not match CEA 861-D spec.
+*/
+int hdmi_msm_audio_info_setup(bool enabled, u32 num_of_channels,
+	u32 channel_allocation, u32 level_shift, bool down_mix)
+{
+	uint32 channel_count = 1;	/* Default to 2 channels
+					   -> See Table 17 in CEA-D spec */
+	uint32 check_sum, audio_info_0_reg, audio_info_1_reg;
+	uint32 audio_info_ctrl_reg;
+	u32 aud_pck_ctrl_2_reg;
+	u32 layout;
+
+	layout = (MSM_HDMI_AUDIO_CHANNEL_2 == num_of_channels) ? 0 : 1;
+	aud_pck_ctrl_2_reg = 1 | (layout << 1);
+	HDMI_OUTP(0x00044, aud_pck_ctrl_2_reg);
+
+	/* Please see table 20 Audio InfoFrame in HDMI spec
+	   FL  = front left
+	   FC  = front Center
+	   FR  = front right
+	   FLC = front left center
+	   FRC = front right center
+	   RL  = rear left
+	   RC  = rear center
+	   RR  = rear right
+	   RLC = rear left center
+	   RRC = rear right center
+	   LFE = low frequency effect
+	 */
+
+	/* Read first then write because it is bundled with other controls */
+	/* HDMI_INFOFRAME_CTRL0[0x002C] */
+	audio_info_ctrl_reg = HDMI_INP(0x002C);
+
+	if (enabled) {
+		switch (num_of_channels) {
+		case MSM_HDMI_AUDIO_CHANNEL_2:
+			channel_allocation = 0;	/* Default to FR,FL */
+			break;
+		case MSM_HDMI_AUDIO_CHANNEL_4:
+			channel_count = 3;
+			/* FC,LFE,FR,FL */
+			channel_allocation = 0x3;
+			break;
+		case MSM_HDMI_AUDIO_CHANNEL_6:
+			channel_count = 5;
+			/* RR,RL,FC,LFE,FR,FL */
+			channel_allocation = 0xB;
+			break;
+		case MSM_HDMI_AUDIO_CHANNEL_8:
+			channel_count = 7;
+			/* FRC,FLC,RR,RL,FC,LFE,FR,FL */
+			channel_allocation = 0x1f;
+			break;
+		default:
+			pr_err("%s(): Unsupported num_of_channels = %u\n",
+					__func__, num_of_channels);
+			return -EINVAL;
+			break;
+		}
+
+		/* Program the Channel-Speaker allocation */
+		audio_info_1_reg = 0;
+		/* CA(channel_allocation) */
+		audio_info_1_reg |= channel_allocation & 0xff;
+		/* Program the Level shifter */
+		/* LSV(level_shift) */
+		audio_info_1_reg |= (level_shift << 11) & 0x00007800;
+		/* Program the Down-mix Inhibit Flag */
+		/* DM_INH(down_mix) */
+		audio_info_1_reg |= (down_mix << 15) & 0x00008000;
+
+		/* HDMI_AUDIO_INFO1[0x00E8] */
+		HDMI_OUTP(0x00E8, audio_info_1_reg);
+
+		/* Calculate CheckSum
+		   Sum of all the bytes in the Audio Info Packet bytes
+		   (See table 8.4 in HDMI spec) */
+		check_sum = 0;
+		/* HDMI_AUDIO_INFO_FRAME_PACKET_HEADER_TYPE[0x84] */
+		check_sum += 0x84;
+		/* HDMI_AUDIO_INFO_FRAME_PACKET_HEADER_VERSION[0x01] */
+		check_sum += 1;
+		/* HDMI_AUDIO_INFO_FRAME_PACKET_LENGTH[0x0A] */
+		check_sum += 0x0A;
+		check_sum += channel_count;
+		check_sum += channel_allocation;
+		/* See Table 8.5 in HDMI spec */
+		check_sum += (level_shift & 0xF) << 3 | (down_mix & 0x1) << 7;
+		check_sum &= 0xFF;
+		check_sum = (uint8) (256 - check_sum);
+
+		audio_info_0_reg = 0;
+		/* CHECKSUM(check_sum) */
+		audio_info_0_reg |= check_sum & 0xff;
+		/* CC(channel_count) */
+		audio_info_0_reg |= (channel_count << 8) & 0x00000700;
+
+		/* HDMI_AUDIO_INFO0[0x00E4] */
+		HDMI_OUTP(0x00E4, audio_info_0_reg);
+
+		/* Set these flags */
+		/* AUDIO_INFO_UPDATE | AUDIO_INFO_SOURCE | AUDIO_INFO_CONT
+		 | AUDIO_INFO_SEND */
+		audio_info_ctrl_reg |= 0x000000F0;
+	} else {
+		/* Clear these flags */
+		/* ~(AUDIO_INFO_UPDATE | AUDIO_INFO_SOURCE | AUDIO_INFO_CONT
+		   | AUDIO_INFO_SEND) */
+		audio_info_ctrl_reg &= ~0x000000F0;
+	}
+	/* HDMI_INFOFRAME_CTRL0[0x002C] */
+	HDMI_OUTP(0x002C, audio_info_ctrl_reg);
+
+
+	hdmi_msm_dump_regs("HDMI-AUDIO-ON: ");
+
+	return 0;
+
+}
+EXPORT_SYMBOL(hdmi_msm_audio_info_setup);
+
+static void hdmi_msm_en_gc_packet(boolean av_mute_is_requested)
+{
+	/* HDMI_GC[0x0040] */
+	HDMI_OUTP(0x0040, av_mute_is_requested ? 1 : 0);
+
+	/* GC packet enable (every frame) */
+	/* HDMI_VBI_PKT_CTRL[0x0028] */
+	hdmi_msm_rmw32or(0x0028, 3 << 4);
+}
+
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_ISRC_ACP_SUPPORT
+static void hdmi_msm_en_isrc_packet(boolean isrc_is_continued)
+{
+	static const char isrc_psuedo_data[] =
+					"ISRC1:0123456789isrc2=ABCDEFGHIJ";
+	const uint32 * isrc_data = (const uint32 *) isrc_psuedo_data;
+
+	/* ISRC_STATUS =0b010 | ISRC_CONTINUE | ISRC_VALID */
+	/* HDMI_ISRC1_0[0x00048] */
+	HDMI_OUTP(0x00048, 2 | (isrc_is_continued ? 1 : 0) << 6 | 0 << 7);
+
+	/* HDMI_ISRC1_1[0x004C] */
+	HDMI_OUTP(0x004C, *isrc_data++);
+	/* HDMI_ISRC1_2[0x0050] */
+	HDMI_OUTP(0x0050, *isrc_data++);
+	/* HDMI_ISRC1_3[0x0054] */
+	HDMI_OUTP(0x0054, *isrc_data++);
+	/* HDMI_ISRC1_4[0x0058] */
+	HDMI_OUTP(0x0058, *isrc_data++);
+
+	/* HDMI_ISRC2_0[0x005C] */
+	HDMI_OUTP(0x005C, *isrc_data++);
+	/* HDMI_ISRC2_1[0x0060] */
+	HDMI_OUTP(0x0060, *isrc_data++);
+	/* HDMI_ISRC2_2[0x0064] */
+	HDMI_OUTP(0x0064, *isrc_data++);
+	/* HDMI_ISRC2_3[0x0068] */
+	HDMI_OUTP(0x0068, *isrc_data);
+
+	/* HDMI_VBI_PKT_CTRL[0x0028] */
+	/* ISRC Send + Continuous */
+	hdmi_msm_rmw32or(0x0028, 3 << 8);
+}
+#else
+static void hdmi_msm_en_isrc_packet(boolean isrc_is_continued)
+{
+	/*
+	 * Until end-to-end support for various audio packets
+	 */
+}
+#endif
+
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_ISRC_ACP_SUPPORT
+static void hdmi_msm_en_acp_packet(uint32 byte1)
+{
+	/* HDMI_ACP[0x003C] */
+	HDMI_OUTP(0x003C, 2 | 1 << 8 | byte1 << 16);
+
+	/* HDMI_VBI_PKT_CTRL[0x0028] */
+	/* ACP send, s/w source */
+	hdmi_msm_rmw32or(0x0028, 3 << 12);
+}
+#else
+static void hdmi_msm_en_acp_packet(uint32 byte1)
+{
+	/*
+	 * Until end-to-end support for various audio packets
+	 */
+}
+#endif
+
+int hdmi_msm_audio_get_sample_rate(void)
+{
+	return msm_hdmi_sample_rate;
+}
+EXPORT_SYMBOL(hdmi_msm_audio_get_sample_rate);
+
+void hdmi_msm_audio_sample_rate_reset(int rate)
+{
+	msm_hdmi_sample_rate = rate;
+
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
+	if (hdmi_msm_has_hdcp())
+		hdcp_deauthenticate();
+	else
+#endif
+		hdmi_msm_turn_on();
+}
+EXPORT_SYMBOL(hdmi_msm_audio_sample_rate_reset);
+
+static void hdmi_msm_audio_setup(void)
+{
+	const int channels = MSM_HDMI_AUDIO_CHANNEL_2;
+
+	/* (0) for clr_avmute, (1) for set_avmute */
+	hdmi_msm_en_gc_packet(0);
+	/* (0) for isrc1 only, (1) for isrc1 and isrc2 */
+	hdmi_msm_en_isrc_packet(1);
+	/* arbitrary bit pattern for byte1 */
+	hdmi_msm_en_acp_packet(0x5a);
+	DEV_DBG("Not setting ACP, ISRC1, ISRC2 packets\n");
+	hdmi_msm_audio_acr_setup(TRUE,
+		external_common_state->video_resolution,
+		msm_hdmi_sample_rate, channels);
+	hdmi_msm_audio_info_setup(TRUE, channels, 0, 0, FALSE);
+
+	/* Turn on Audio FIFO and SAM DROP ISR */
+	HDMI_OUTP(0x02CC, HDMI_INP(0x02CC) | BIT(1) | BIT(3));
+	DEV_INFO("HDMI Audio: Enabled\n");
+}
+
+static int hdmi_msm_audio_off(void)
+{
+	uint32 audio_pkt_ctrl, audio_cfg;
+	 /* Number of wait iterations */
+	int i = 10;
+	audio_pkt_ctrl = HDMI_INP_ND(0x0020);
+	audio_cfg = HDMI_INP_ND(0x01D0);
+
+	/* Checking BIT[0] of AUDIO PACKET CONTROL and */
+	/* AUDIO CONFIGURATION register */
+	while (((audio_pkt_ctrl & 0x00000001) || (audio_cfg & 0x00000001))
+		&& (i--)) {
+		audio_pkt_ctrl = HDMI_INP_ND(0x0020);
+		audio_cfg = HDMI_INP_ND(0x01D0);
+		DEV_DBG("%d times :: HDMI AUDIO PACKET is %08x and "
+		"AUDIO CFG is %08x", i, audio_pkt_ctrl, audio_cfg);
+		msleep(100);
+		if (!i) {
+			DEV_ERR("%s:failed to set BIT[0] AUDIO PACKET"
+			"CONTROL or AUDIO CONFIGURATION REGISTER\n",
+				__func__);
+			return -ETIMEDOUT;
+		}
+	}
+	hdmi_msm_audio_info_setup(FALSE, 0, 0, 0, FALSE);
+	hdmi_msm_audio_acr_setup(FALSE, 0, 0, 0);
+	DEV_INFO("HDMI Audio: Disabled\n");
+	return 0;
+}
+
+
+static uint8 hdmi_msm_avi_iframe_lut[][16] = {
+/*	480p60	480i60	576p50	576i50	720p60	 720p50	1080p60	1080i60	1080p50
+	1080i50	1080p24	1080p30	1080p25	640x480p 480p60_16_9 576p50_4_3 */
+	{0x10,	0x10,	0x10,	0x10,	0x10,	 0x10,	0x10,	0x10,	0x10,
+	 0x10,	0x10,	0x10,	0x10,	0x10, 0x10, 0x10}, /*00*/
+	{0x18,	0x18,	0x28,	0x28,	0x28,	 0x28,	0x28,	0x28,	0x28,
+	 0x28,	0x28,	0x28,	0x28,	0x18, 0x28, 0x18}, /*01*/
+	{0x00,	0x04,	0x04,	0x04,	0x04,	 0x04,	0x04,	0x04,	0x04,
+	 0x04,	0x04,	0x04,	0x04,	0x88, 0x00, 0x04}, /*02*/
+	{0x02,	0x06,	0x11,	0x15,	0x04,	 0x13,	0x10,	0x05,	0x1F,
+	 0x14,	0x20,	0x22,	0x21,	0x01, 0x03, 0x11}, /*03*/
+	{0x00,	0x01,	0x00,	0x01,	0x00,	 0x00,	0x00,	0x00,	0x00,
+	 0x00,	0x00,	0x00,	0x00,	0x00, 0x00, 0x00}, /*04*/
+	{0x00,	0x00,	0x00,	0x00,	0x00,	 0x00,	0x00,	0x00,	0x00,
+	 0x00,	0x00,	0x00,	0x00,	0x00, 0x00, 0x00}, /*05*/
+	{0x00,	0x00,	0x00,	0x00,	0x00,	 0x00,	0x00,	0x00,	0x00,
+	 0x00,	0x00,	0x00,	0x00,	0x00, 0x00, 0x00}, /*06*/
+	{0xE1,	0xE1,	0x41,	0x41,	0xD1,	 0xd1,	0x39,	0x39,	0x39,
+	 0x39,	0x39,	0x39,	0x39,	0xe1, 0xE1, 0x41}, /*07*/
+	{0x01,	0x01,	0x02,	0x02,	0x02,	 0x02,	0x04,	0x04,	0x04,
+	 0x04,	0x04,	0x04,	0x04,	0x01, 0x01, 0x02}, /*08*/
+	{0x00,	0x00,	0x00,	0x00,	0x00,	 0x00,	0x00,	0x00,	0x00,
+	 0x00,	0x00,	0x00,	0x00,	0x00, 0x00, 0x00}, /*09*/
+	{0x00,	0x00,	0x00,	0x00,	0x00,	 0x00,	0x00,	0x00,	0x00,
+	 0x00,	0x00,	0x00,	0x00,	0x00, 0x00, 0x00}, /*10*/
+	{0xD1,	0xD1,	0xD1,	0xD1,	0x01,	 0x01,	0x81,	0x81,	0x81,
+	 0x81,	0x81,	0x81,	0x81,	0x81, 0xD1, 0xD1}, /*11*/
+	{0x02,	0x02,	0x02,	0x02,	0x05,	 0x05,	0x07,	0x07,	0x07,
+	 0x07,	0x07,	0x07,	0x07,	0x02, 0x02, 0x02}  /*12*/
+};
+
+static void hdmi_msm_avi_info_frame(void)
+{
+	/* two header + length + 13 data */
+	uint8 aviInfoFrame[16];
+	uint8 checksum;
+	uint32 sum;
+	uint32 regVal;
+	int i;
+	int mode = 0;
+	boolean use_ce_scan_info = TRUE;
+
+	switch (external_common_state->video_resolution) {
+	case HDMI_VFRMT_720x480p60_4_3:
+		mode = 0;
+		break;
+	case HDMI_VFRMT_720x480i60_16_9:
+		mode = 1;
+		break;
+	case HDMI_VFRMT_720x576p50_16_9:
+		mode = 2;
+		break;
+	case HDMI_VFRMT_720x576i50_16_9:
+		mode = 3;
+		break;
+	case HDMI_VFRMT_1280x720p60_16_9:
+		mode = 4;
+		break;
+	case HDMI_VFRMT_1280x720p50_16_9:
+		mode = 5;
+		break;
+	case HDMI_VFRMT_1920x1080p60_16_9:
+		mode = 6;
+		break;
+	case HDMI_VFRMT_1920x1080i60_16_9:
+		mode = 7;
+		break;
+	case HDMI_VFRMT_1920x1080p50_16_9:
+		mode = 8;
+		break;
+	case HDMI_VFRMT_1920x1080i50_16_9:
+		mode = 9;
+		break;
+	case HDMI_VFRMT_1920x1080p24_16_9:
+		mode = 10;
+		break;
+	case HDMI_VFRMT_1920x1080p30_16_9:
+		mode = 11;
+		break;
+	case HDMI_VFRMT_1920x1080p25_16_9:
+		mode = 12;
+		break;
+	case HDMI_VFRMT_640x480p60_4_3:
+		mode = 13;
+		break;
+	case HDMI_VFRMT_720x480p60_16_9:
+		mode = 14;
+		break;
+	case HDMI_VFRMT_720x576p50_4_3:
+		mode = 15;
+		break;
+	default:
+		DEV_INFO("%s: mode %d not supported\n", __func__,
+			external_common_state->video_resolution);
+		return;
+	}
+
+	/* InfoFrame Type = 82 */
+	aviInfoFrame[0]  = 0x82;
+	/* Version = 2 */
+	aviInfoFrame[1]  = 2;
+	/* Length of AVI InfoFrame = 13 */
+	aviInfoFrame[2]  = 13;
+
+	/* Data Byte 01: 0 Y1 Y0 A0 B1 B0 S1 S0 */
+	aviInfoFrame[3]  = hdmi_msm_avi_iframe_lut[0][mode];
+
+	/*
+	 * If the sink specified support for both underscan/overscan
+	 * then, by default, set the underscan bit.
+	 * Only checking underscan support for preferred format and cea formats
+	 */
+	if ((external_common_state->video_resolution ==
+			external_common_state->preferred_video_format)) {
+		use_ce_scan_info = FALSE;
+		switch (external_common_state->pt_scan_info) {
+		case 0:
+			/*
+			 * Need to use the info specified for the corresponding
+			 * IT or CE format
+			 */
+			DEV_DBG("%s: No underscan information specified for the"
+				" preferred video format\n", __func__);
+			use_ce_scan_info = TRUE;
+			break;
+		case 3:
+			DEV_DBG("%s: Setting underscan bit for the preferred"
+				" video format\n", __func__);
+			aviInfoFrame[3] |= 0x02;
+			break;
+		default:
+			DEV_DBG("%s: Underscan information not set for the"
+				" preferred video format\n", __func__);
+			break;
+		}
+	}
+
+	if (use_ce_scan_info) {
+		if (3 == external_common_state->ce_scan_info) {
+			DEV_DBG("%s: Setting underscan bit for the CE video"
+					" format\n", __func__);
+			aviInfoFrame[3] |= 0x02;
+		} else {
+			DEV_DBG("%s: Not setting underscan bit for the CE video"
+				       " format\n", __func__);
+		}
+	}
+
+	/* Data Byte 02: C1 C0 M1 M0 R3 R2 R1 R0 */
+	aviInfoFrame[4]  = hdmi_msm_avi_iframe_lut[1][mode];
+	/* Data Byte 03: ITC EC2 EC1 EC0 Q1 Q0 SC1 SC0 */
+	aviInfoFrame[5]  = hdmi_msm_avi_iframe_lut[2][mode];
+	/* Data Byte 04: 0 VIC6 VIC5 VIC4 VIC3 VIC2 VIC1 VIC0 */
+	aviInfoFrame[6]  = hdmi_msm_avi_iframe_lut[3][mode];
+	/* Data Byte 05: 0 0 0 0 PR3 PR2 PR1 PR0 */
+	aviInfoFrame[7]  = hdmi_msm_avi_iframe_lut[4][mode];
+	/* Data Byte 06: LSB Line No of End of Top Bar */
+	aviInfoFrame[8]  = hdmi_msm_avi_iframe_lut[5][mode];
+	/* Data Byte 07: MSB Line No of End of Top Bar */
+	aviInfoFrame[9]  = hdmi_msm_avi_iframe_lut[6][mode];
+	/* Data Byte 08: LSB Line No of Start of Bottom Bar */
+	aviInfoFrame[10] = hdmi_msm_avi_iframe_lut[7][mode];
+	/* Data Byte 09: MSB Line No of Start of Bottom Bar */
+	aviInfoFrame[11] = hdmi_msm_avi_iframe_lut[8][mode];
+	/* Data Byte 10: LSB Pixel Number of End of Left Bar */
+	aviInfoFrame[12] = hdmi_msm_avi_iframe_lut[9][mode];
+	/* Data Byte 11: MSB Pixel Number of End of Left Bar */
+	aviInfoFrame[13] = hdmi_msm_avi_iframe_lut[10][mode];
+	/* Data Byte 12: LSB Pixel Number of Start of Right Bar */
+	aviInfoFrame[14] = hdmi_msm_avi_iframe_lut[11][mode];
+	/* Data Byte 13: MSB Pixel Number of Start of Right Bar */
+	aviInfoFrame[15] = hdmi_msm_avi_iframe_lut[12][mode];
+
+	sum = 0;
+	for (i = 0; i < 16; i++)
+		sum += aviInfoFrame[i];
+	sum &= 0xFF;
+	sum = 256 - sum;
+	checksum = (uint8) sum;
+
+	regVal = aviInfoFrame[5];
+	regVal = regVal << 8 | aviInfoFrame[4];
+	regVal = regVal << 8 | aviInfoFrame[3];
+	regVal = regVal << 8 | checksum;
+	HDMI_OUTP(0x006C, regVal);
+
+	regVal = aviInfoFrame[9];
+	regVal = regVal << 8 | aviInfoFrame[8];
+	regVal = regVal << 8 | aviInfoFrame[7];
+	regVal = regVal << 8 | aviInfoFrame[6];
+	HDMI_OUTP(0x0070, regVal);
+
+	regVal = aviInfoFrame[13];
+	regVal = regVal << 8 | aviInfoFrame[12];
+	regVal = regVal << 8 | aviInfoFrame[11];
+	regVal = regVal << 8 | aviInfoFrame[10];
+	HDMI_OUTP(0x0074, regVal);
+
+	regVal = aviInfoFrame[1];
+	regVal = regVal << 16 | aviInfoFrame[15];
+	regVal = regVal << 8 | aviInfoFrame[14];
+	HDMI_OUTP(0x0078, regVal);
+
+	/* INFOFRAME_CTRL0[0x002C] */
+	/* 0x3 for AVI InfFrame enable (every frame) */
+	HDMI_OUTP(0x002C, HDMI_INP(0x002C) | 0x00000003L);
+}
+
+#ifdef CONFIG_FB_MSM_HDMI_3D
+static void hdmi_msm_vendor_infoframe_packetsetup(void)
+{
+	uint32 packet_header      = 0;
+	uint32 check_sum          = 0;
+	uint32 packet_payload     = 0;
+
+	if (!external_common_state->format_3d) {
+		HDMI_OUTP(0x0034, 0);
+		return;
+	}
+
+	/* 0x0084 GENERIC0_HDR
+	 *   HB0             7:0  NUM
+	 *   HB1            15:8  NUM
+	 *   HB2           23:16  NUM */
+	/* Setup Packet header and payload */
+	/* 0x81 VS_INFO_FRAME_ID
+	   0x01 VS_INFO_FRAME_VERSION
+	   0x1B VS_INFO_FRAME_PAYLOAD_LENGTH */
+	packet_header  = 0x81 | (0x01 << 8) | (0x1B << 16);
+	HDMI_OUTP(0x0084, packet_header);
+
+	check_sum  = packet_header & 0xff;
+	check_sum += (packet_header >> 8) & 0xff;
+	check_sum += (packet_header >> 16) & 0xff;
+
+	/* 0x008C GENERIC0_1
+	 *   BYTE4           7:0  NUM
+	 *   BYTE5          15:8  NUM
+	 *   BYTE6         23:16  NUM
+	 *   BYTE7         31:24  NUM */
+	/* 0x02 VS_INFO_FRAME_3D_PRESENT */
+	packet_payload  = 0x02 << 5;
+	switch (external_common_state->format_3d) {
+	case 1:
+		/* 0b1000 VIDEO_3D_FORMAT_SIDE_BY_SIDE_HALF */
+		packet_payload |= (0x08 << 8) << 4;
+		break;
+	case 2:
+		/* 0b0110 VIDEO_3D_FORMAT_TOP_AND_BOTTOM_HALF */
+		packet_payload |= (0x06 << 8) << 4;
+		break;
+	}
+	HDMI_OUTP(0x008C, packet_payload);
+
+	check_sum += packet_payload & 0xff;
+	check_sum += (packet_payload >> 8) & 0xff;
+
+	#define IEEE_REGISTRATION_ID	0xC03
+	/* Next 3 bytes are IEEE Registration Identifcation */
+	/* 0x0088 GENERIC0_0
+	 *   BYTE0           7:0  NUM (checksum)
+	 *   BYTE1          15:8  NUM
+	 *   BYTE2         23:16  NUM
+	 *   BYTE3         31:24  NUM */
+	check_sum += IEEE_REGISTRATION_ID & 0xff;
+	check_sum += (IEEE_REGISTRATION_ID >> 8) & 0xff;
+	check_sum += (IEEE_REGISTRATION_ID >> 16) & 0xff;
+
+	HDMI_OUTP(0x0088, (0x100 - (0xff & check_sum))
+		| ((IEEE_REGISTRATION_ID & 0xff) << 8)
+		| (((IEEE_REGISTRATION_ID >> 8) & 0xff) << 16)
+		| (((IEEE_REGISTRATION_ID >> 16) & 0xff) << 24));
+
+	/* 0x0034 GEN_PKT_CTRL
+	 *   GENERIC0_SEND   0      0 = Disable Generic0 Packet Transmission
+	 *                          1 = Enable Generic0 Packet Transmission
+	 *   GENERIC0_CONT   1      0 = Send Generic0 Packet on next frame only
+	 *                          1 = Send Generic0 Packet on every frame
+	 *   GENERIC0_UPDATE 2      NUM
+	 *   GENERIC1_SEND   4      0 = Disable Generic1 Packet Transmission
+	 *                          1 = Enable Generic1 Packet Transmission
+	 *   GENERIC1_CONT   5      0 = Send Generic1 Packet on next frame only
+	 *                          1 = Send Generic1 Packet on every frame
+	 *   GENERIC0_LINE   21:16  NUM
+	 *   GENERIC1_LINE   29:24  NUM
+	 */
+	/* GENERIC0_LINE | GENERIC0_UPDATE | GENERIC0_CONT | GENERIC0_SEND
+	 * Setup HDMI TX generic packet control
+	 * Enable this packet to transmit every frame
+	 * Enable this packet to transmit every frame
+	 * Enable HDMI TX engine to transmit Generic packet 0 */
+	HDMI_OUTP(0x0034, (1 << 16) | (1 << 2) | BIT(1) | BIT(0));
+}
+
+static void hdmi_msm_switch_3d(boolean on)
+{
+	mutex_lock(&external_common_state_hpd_mutex);
+	if (external_common_state->hpd_state)
+		hdmi_msm_vendor_infoframe_packetsetup();
+	mutex_unlock(&external_common_state_hpd_mutex);
+}
+#endif
+
+#define IFRAME_CHECKSUM_32(d) \
+	((d & 0xff) + ((d >> 8) & 0xff) + \
+	((d >> 16) & 0xff) + ((d >> 24) & 0xff))
+
+static void hdmi_msm_spd_infoframe_packetsetup(void)
+{
+	uint32 packet_header  = 0;
+	uint32 check_sum      = 0;
+	uint32 packet_payload = 0;
+	uint32 packet_control = 0;
+
+	uint8 *vendor_name = external_common_state->spd_vendor_name;
+	uint8 *product_description =
+		external_common_state->spd_product_description;
+
+	/* 0x00A4 GENERIC1_HDR
+	 *   HB0             7:0  NUM
+	 *   HB1            15:8  NUM
+	 *   HB2           23:16  NUM */
+	/* Setup Packet header and payload */
+	/* 0x83 InfoFrame Type Code
+	   0x01 InfoFrame Version Number
+	   0x19 Length of Source Product Description InfoFrame
+	*/
+	packet_header  = 0x83 | (0x01 << 8) | (0x19 << 16);
+	HDMI_OUTP(0x00A4, packet_header);
+	check_sum += IFRAME_CHECKSUM_32(packet_header);
+
+	/* Vendor Name (7bit ASCII code) */
+	/* 0x00A8 GENERIC1_0
+	 *   BYTE0           7:0  CheckSum
+	 *   BYTE1          15:8  VENDOR_NAME[0]
+	 *   BYTE2         23:16  VENDOR_NAME[1]
+	 *   BYTE3         31:24  VENDOR_NAME[2] */
+	packet_payload = ((vendor_name[0] & 0x7f) << 8)
+		| ((vendor_name[1] & 0x7f) << 16)
+		| ((vendor_name[2] & 0x7f) << 24);
+	check_sum += IFRAME_CHECKSUM_32(packet_payload);
+	packet_payload |= ((0x100 - (0xff & check_sum)) & 0xff);
+	HDMI_OUTP(0x00A8, packet_payload);
+
+	/* 0x00AC GENERIC1_1
+	 *   BYTE4           7:0  VENDOR_NAME[3]
+	 *   BYTE5          15:8  VENDOR_NAME[4]
+	 *   BYTE6         23:16  VENDOR_NAME[5]
+	 *   BYTE7         31:24  VENDOR_NAME[6] */
+	packet_payload = (vendor_name[3] & 0x7f)
+		| ((vendor_name[4] & 0x7f) << 8)
+		| ((vendor_name[5] & 0x7f) << 16)
+		| ((vendor_name[6] & 0x7f) << 24);
+	HDMI_OUTP(0x00AC, packet_payload);
+	check_sum += IFRAME_CHECKSUM_32(packet_payload);
+
+	/* Product Description (7-bit ASCII code) */
+	/* 0x00B0 GENERIC1_2
+	 *   BYTE8           7:0  VENDOR_NAME[7]
+	 *   BYTE9          15:8  PRODUCT_NAME[ 0]
+	 *   BYTE10        23:16  PRODUCT_NAME[ 1]
+	 *   BYTE11        31:24  PRODUCT_NAME[ 2] */
+	packet_payload = (vendor_name[7] & 0x7f)
+		| ((product_description[0] & 0x7f) << 8)
+		| ((product_description[1] & 0x7f) << 16)
+		| ((product_description[2] & 0x7f) << 24);
+	HDMI_OUTP(0x00B0, packet_payload);
+	check_sum += IFRAME_CHECKSUM_32(packet_payload);
+
+	/* 0x00B4 GENERIC1_3
+	 *   BYTE12          7:0  PRODUCT_NAME[ 3]
+	 *   BYTE13         15:8  PRODUCT_NAME[ 4]
+	 *   BYTE14        23:16  PRODUCT_NAME[ 5]
+	 *   BYTE15        31:24  PRODUCT_NAME[ 6] */
+	packet_payload = (product_description[3] & 0x7f)
+		| ((product_description[4] & 0x7f) << 8)
+		| ((product_description[5] & 0x7f) << 16)
+		| ((product_description[6] & 0x7f) << 24);
+	HDMI_OUTP(0x00B4, packet_payload);
+	check_sum += IFRAME_CHECKSUM_32(packet_payload);
+
+	/* 0x00B8 GENERIC1_4
+	 *   BYTE16          7:0  PRODUCT_NAME[ 7]
+	 *   BYTE17         15:8  PRODUCT_NAME[ 8]
+	 *   BYTE18        23:16  PRODUCT_NAME[ 9]
+	 *   BYTE19        31:24  PRODUCT_NAME[10] */
+	packet_payload = (product_description[7] & 0x7f)
+		| ((product_description[8] & 0x7f) << 8)
+		| ((product_description[9] & 0x7f) << 16)
+		| ((product_description[10] & 0x7f) << 24);
+	HDMI_OUTP(0x00B8, packet_payload);
+	check_sum += IFRAME_CHECKSUM_32(packet_payload);
+
+	/* 0x00BC GENERIC1_5
+	 *   BYTE20          7:0  PRODUCT_NAME[11]
+	 *   BYTE21         15:8  PRODUCT_NAME[12]
+	 *   BYTE22        23:16  PRODUCT_NAME[13]
+	 *   BYTE23        31:24  PRODUCT_NAME[14] */
+	packet_payload = (product_description[11] & 0x7f)
+		| ((product_description[12] & 0x7f) << 8)
+		| ((product_description[13] & 0x7f) << 16)
+		| ((product_description[14] & 0x7f) << 24);
+	HDMI_OUTP(0x00BC, packet_payload);
+	check_sum += IFRAME_CHECKSUM_32(packet_payload);
+
+	/* 0x00C0 GENERIC1_6
+	 *   BYTE24          7:0  PRODUCT_NAME[15]
+	 *   BYTE25         15:8  Source Device Information
+	 *   BYTE26        23:16  NUM
+	 *   BYTE27        31:24  NUM */
+	/* Source Device Information
+	 * 00h unknown
+	 * 01h Digital STB
+	 * 02h DVD
+	 * 03h D-VHS
+	 * 04h HDD Video
+	 * 05h DVC
+	 * 06h DSC
+	 * 07h Video CD
+	 * 08h Game
+	 * 09h PC general */
+	packet_payload = (product_description[15] & 0x7f) | 0x00 << 8;
+	HDMI_OUTP(0x00C0, packet_payload);
+	check_sum += IFRAME_CHECKSUM_32(packet_payload);
+
+	/* GENERIC1_LINE | GENERIC1_CONT | GENERIC1_SEND
+	 * Setup HDMI TX generic packet control
+	 * Enable this packet to transmit every frame
+	 * Enable HDMI TX engine to transmit Generic packet 1 */
+	packet_control = HDMI_INP_ND(0x0034);
+	packet_control |= ((0x1 << 24) | (1 << 5) | (1 << 4));
+	HDMI_OUTP(0x0034, packet_control);
+}
+
+int hdmi_msm_clk(int on)
+{
+	int rc;
+
+	DEV_DBG("HDMI Clk: %s\n", on ? "Enable" : "Disable");
+	if (on) {
+		rc = clk_prepare_enable(hdmi_msm_state->hdmi_app_clk);
+		if (rc) {
+			DEV_ERR("'hdmi_app_clk' clock enable failed, rc=%d\n",
+				rc);
+			return rc;
+		}
+
+		rc = clk_prepare_enable(hdmi_msm_state->hdmi_m_pclk);
+		if (rc) {
+			DEV_ERR("'hdmi_m_pclk' clock enable failed, rc=%d\n",
+				rc);
+			return rc;
+		}
+
+		rc = clk_prepare_enable(hdmi_msm_state->hdmi_s_pclk);
+		if (rc) {
+			DEV_ERR("'hdmi_s_pclk' clock enable failed, rc=%d\n",
+				rc);
+			return rc;
+		}
+	} else {
+		clk_disable_unprepare(hdmi_msm_state->hdmi_app_clk);
+		clk_disable_unprepare(hdmi_msm_state->hdmi_m_pclk);
+		clk_disable_unprepare(hdmi_msm_state->hdmi_s_pclk);
+	}
+
+	return 0;
+}
+
+static void hdmi_msm_turn_on(void)
+{
+	uint32 hpd_ctrl;
+	uint32 audio_pkt_ctrl, audio_cfg;
+	/*
+	 * Number of wait iterations for QDSP to disable Audio Engine
+	 * before resetting HDMI core
+	 */
+	int i = 10;
+	audio_pkt_ctrl = HDMI_INP_ND(0x0020);
+	audio_cfg = HDMI_INP_ND(0x01D0);
+
+	/*
+	 * Checking BIT[0] of AUDIO PACKET CONTROL and
+	 * AUDIO CONFIGURATION register
+	 */
+	while (((audio_pkt_ctrl & 0x00000001) || (audio_cfg & 0x00000001))
+		&& (i--)) {
+		audio_pkt_ctrl = HDMI_INP_ND(0x0020);
+		audio_cfg = HDMI_INP_ND(0x01D0);
+		DEV_DBG("%d times :: HDMI AUDIO PACKET is %08x and "
+			"AUDIO CFG is %08x", i, audio_pkt_ctrl, audio_cfg);
+		msleep(20);
+	}
+
+	mutex_lock(&hdcp_auth_state_mutex);
+	hdmi_msm_reset_core();
+	mutex_unlock(&hdcp_auth_state_mutex);
+
+	hdmi_msm_init_phy(external_common_state->video_resolution);
+	/* HDMI_USEC_REFTIMER[0x0208] */
+	HDMI_OUTP(0x0208, 0x0001001B);
+
+	hdmi_msm_set_mode(TRUE);
+
+	hdmi_msm_video_setup(external_common_state->video_resolution);
+	if (!hdmi_msm_is_dvi_mode())
+		hdmi_msm_audio_setup();
+	hdmi_msm_avi_info_frame();
+#ifdef CONFIG_FB_MSM_HDMI_3D
+	hdmi_msm_vendor_infoframe_packetsetup();
+#endif
+	hdmi_msm_spd_infoframe_packetsetup();
+
+	/* set timeout to 4.1ms (max) for hardware debounce */
+	hpd_ctrl = (HDMI_INP(0x0258) & ~0xFFF) | 0xFFF;
+
+	/* Toggle HPD circuit to trigger HPD sense */
+	HDMI_OUTP(0x0258, ~(1 << 28) & hpd_ctrl);
+	HDMI_OUTP(0x0258, (1 << 28) | hpd_ctrl);
+
+	/* Setup HPD IRQ */
+	HDMI_OUTP(0x0254, 4 | (external_common_state->hpd_state ? 0 : 2));
+
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
+	if (hdmi_msm_state->reauth) {
+		hdmi_msm_hdcp_enable();
+		hdmi_msm_state->reauth = FALSE ;
+	}
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
+
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
+	/* re-initialize CEC if enabled */
+	mutex_lock(&hdmi_msm_state_mutex);
+	if (hdmi_msm_state->cec_enabled == true) {
+		hdmi_msm_cec_init();
+		hdmi_msm_cec_write_logical_addr(
+			hdmi_msm_state->cec_logical_addr);
+	}
+	mutex_unlock(&hdmi_msm_state_mutex);
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT */
+	DEV_INFO("HDMI Core: Initialized\n");
+}
+
+static void hdmi_msm_hpd_state_timer(unsigned long data)
+{
+	queue_work(hdmi_work_queue, &hdmi_msm_state->hpd_state_work);
+}
+
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
+static void hdmi_msm_hdcp_timer(unsigned long data)
+{
+	queue_work(hdmi_work_queue, &hdmi_msm_state->hdcp_work);
+}
+#endif
+
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
+static void hdmi_msm_cec_read_timer_func(unsigned long data)
+{
+	queue_work(hdmi_work_queue, &hdmi_msm_state->cec_latch_detect_work);
+}
+#endif
+
+static void hdmi_msm_hpd_read_work(struct work_struct *work)
+{
+	uint32 hpd_ctrl;
+
+	clk_prepare_enable(hdmi_msm_state->hdmi_app_clk);
+	hdmi_msm_state->pd->core_power(1, 1);
+	hdmi_msm_state->pd->enable_5v(1);
+	hdmi_msm_set_mode(FALSE);
+	hdmi_msm_init_phy(external_common_state->video_resolution);
+	/* HDMI_USEC_REFTIMER[0x0208] */
+	HDMI_OUTP(0x0208, 0x0001001B);
+	hpd_ctrl = (HDMI_INP(0x0258) & ~0xFFF) | 0xFFF;
+
+	/* Toggle HPD circuit to trigger HPD sense */
+	HDMI_OUTP(0x0258, ~(1 << 28) & hpd_ctrl);
+	HDMI_OUTP(0x0258, (1 << 28) | hpd_ctrl);
+
+	hdmi_msm_set_mode(TRUE);
+	msleep(1000);
+	external_common_state->hpd_state = (HDMI_INP(0x0250) & 0x2) >> 1;
+	if (external_common_state->hpd_state) {
+		hdmi_msm_read_edid();
+		DEV_DBG("%s: sense CONNECTED: send ONLINE\n", __func__);
+		kobject_uevent(external_common_state->uevent_kobj,
+			KOBJ_ONLINE);
+	}
+	hdmi_msm_hpd_off();
+	hdmi_msm_set_mode(FALSE);
+	hdmi_msm_state->pd->core_power(0, 1);
+	hdmi_msm_state->pd->enable_5v(0);
+	clk_disable_unprepare(hdmi_msm_state->hdmi_app_clk);
+}
+
+static void hdmi_msm_hpd_off(void)
+{
+	if (!hdmi_msm_state->hpd_initialized) {
+		DEV_DBG("%s: HPD is already OFF, returning\n", __func__);
+		return;
+	}
+
+	DEV_DBG("%s: (timer, clk, 5V, core, IRQ off)\n", __func__);
+	del_timer(&hdmi_msm_state->hpd_state_timer);
+	disable_irq(hdmi_msm_state->irq);
+
+	hdmi_msm_set_mode(FALSE);
+	hdmi_msm_state->hpd_initialized = FALSE;
+	hdmi_msm_powerdown_phy();
+	hdmi_msm_state->pd->cec_power(0);
+	hdmi_msm_state->pd->enable_5v(0);
+	hdmi_msm_state->pd->core_power(0, 1);
+	hdmi_msm_clk(0);
+	hdmi_msm_state->hpd_initialized = FALSE;
+}
+
+static void hdmi_msm_dump_regs(const char *prefix)
+{
+#ifdef REG_DUMP
+	print_hex_dump(KERN_INFO, prefix, DUMP_PREFIX_OFFSET, 32, 4,
+		(void *)MSM_HDMI_BASE, 0x0334, false);
+#endif
+}
+
+static int hdmi_msm_hpd_on(bool trigger_handler)
+{
+	static int phy_reset_done;
+	uint32 hpd_ctrl;
+
+	if (hdmi_msm_state->hpd_initialized) {
+		DEV_DBG("%s: HPD is already ON, returning\n", __func__);
+		return 0;
+	}
+
+	hdmi_msm_clk(1);
+	hdmi_msm_state->pd->core_power(1, 1);
+	hdmi_msm_state->pd->enable_5v(1);
+	hdmi_msm_state->pd->cec_power(1);
+	hdmi_msm_dump_regs("HDMI-INIT: ");
+	hdmi_msm_set_mode(FALSE);
+
+	if (!phy_reset_done) {
+		hdmi_phy_reset();
+		phy_reset_done = 1;
+	}
+
+	/* HDMI_USEC_REFTIMER[0x0208] */
+	HDMI_OUTP(0x0208, 0x0001001B);
+
+	/* Check HPD State */
+	enable_irq(hdmi_msm_state->irq);
+
+	/* set timeout to 4.1ms (max) for hardware debounce */
+	hpd_ctrl = (HDMI_INP(0x0258) & ~0xFFF) | 0xFFF;
+
+	/* Toggle HPD circuit to trigger HPD sense */
+	HDMI_OUTP(0x0258, ~(1 << 28) & hpd_ctrl);
+	HDMI_OUTP(0x0258, (1 << 28) | hpd_ctrl);
+
+	DEV_DBG("%s: (clk, 5V, core, IRQ on) <trigger:%s>\n", __func__,
+		trigger_handler ? "true" : "false");
+
+	if (trigger_handler) {
+		/* Set HPD state machine: ensure at least 2 readouts */
+		mutex_lock(&hdmi_msm_state_mutex);
+		hdmi_msm_state->hpd_stable = 0;
+		hdmi_msm_state->hpd_prev_state = TRUE;
+		mutex_lock(&external_common_state_hpd_mutex);
+		external_common_state->hpd_state = FALSE;
+		mutex_unlock(&external_common_state_hpd_mutex);
+		hdmi_msm_state->hpd_cable_chg_detected = TRUE;
+		mutex_unlock(&hdmi_msm_state_mutex);
+		mod_timer(&hdmi_msm_state->hpd_state_timer,
+			jiffies + HZ/2);
+	}
+
+	hdmi_msm_state->hpd_initialized = TRUE;
+
+	hdmi_msm_set_mode(TRUE);
+
+	return 0;
+}
+
+static int hdmi_msm_power_ctrl(boolean enable)
+{
+	if (!external_common_state->hpd_feature_on)
+		return 0;
+
+	if (enable)
+		hdmi_msm_hpd_on(true);
+	else
+		hdmi_msm_hpd_off();
+
+	return 0;
+}
+
+static int hdmi_msm_power_on(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd = platform_get_drvdata(pdev);
+	bool changed;
+
+	if (!hdmi_msm_state || !hdmi_msm_state->hdmi_app_clk || !MSM_HDMI_BASE)
+		return -ENODEV;
+
+	DEV_INFO("power: ON (%dx%d %d)\n", mfd->var_xres, mfd->var_yres,
+		mfd->var_pixclock);
+
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
+	mutex_lock(&hdmi_msm_state_mutex);
+	if (hdmi_msm_state->hdcp_activating) {
+		hdmi_msm_state->panel_power_on = TRUE;
+		DEV_INFO("HDCP: activating, returning\n");
+	}
+	mutex_unlock(&hdmi_msm_state_mutex);
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
+
+	changed = hdmi_common_get_video_format_from_drv_data(mfd);
+	if (!external_common_state->hpd_feature_on || mfd->ref_cnt) {
+		int rc = hdmi_msm_hpd_on(true);
+		DEV_INFO("HPD: panel power without 'hpd' feature on\n");
+		if (rc) {
+			DEV_WARN("HPD: activation failed: rc=%d\n", rc);
+			return rc;
+		}
+	}
+	hdmi_msm_audio_info_setup(TRUE, 0, 0, 0, FALSE);
+
+	mutex_lock(&external_common_state_hpd_mutex);
+	hdmi_msm_state->panel_power_on = TRUE;
+	if ((external_common_state->hpd_state && !hdmi_msm_is_power_on())
+		|| changed) {
+		mutex_unlock(&external_common_state_hpd_mutex);
+		hdmi_msm_turn_on();
+	} else
+		mutex_unlock(&external_common_state_hpd_mutex);
+
+	hdmi_msm_dump_regs("HDMI-ON: ");
+
+	DEV_INFO("power=%s DVI= %s\n",
+		hdmi_msm_is_power_on() ? "ON" : "OFF" ,
+		hdmi_msm_is_dvi_mode() ? "ON" : "OFF");
+	return 0;
+}
+
+/* Note that power-off will also be called when the cable-remove event is
+ * processed on the user-space and as a result the framebuffer is powered
+ * down.  However, we are still required to be able to detect a cable-insert
+ * event; so for now leave the HDMI engine running; so that the HPD IRQ is
+ * still being processed.
+ */
+static int hdmi_msm_power_off(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd = platform_get_drvdata(pdev);
+
+	if (!hdmi_msm_state->hdmi_app_clk)
+		return -ENODEV;
+
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
+	mutex_lock(&hdmi_msm_state_mutex);
+	if (hdmi_msm_state->hdcp_activating) {
+		hdmi_msm_state->panel_power_on = FALSE;
+		mutex_unlock(&hdmi_msm_state_mutex);
+		DEV_INFO("HDCP: activating, returning\n");
+		return 0;
+	}
+	mutex_unlock(&hdmi_msm_state_mutex);
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
+
+	DEV_INFO("power: OFF (audio off, Reset Core)\n");
+	hdmi_msm_audio_off();
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
+	hdcp_deauthenticate();
+#endif
+	hdmi_msm_hpd_off();
+	hdmi_msm_powerdown_phy();
+	hdmi_msm_dump_regs("HDMI-OFF: ");
+	hdmi_msm_hpd_on(true);
+
+	mutex_lock(&external_common_state_hpd_mutex);
+	if (!external_common_state->hpd_feature_on || mfd->ref_cnt)
+		hdmi_msm_hpd_off();
+	mutex_unlock(&external_common_state_hpd_mutex);
+
+	hdmi_msm_state->panel_power_on = FALSE;
+	return 0;
+}
+
+static int __devinit hdmi_msm_probe(struct platform_device *pdev)
+{
+	int rc;
+	struct platform_device *fb_dev;
+
+	if (!hdmi_msm_state) {
+		pr_err("%s: hdmi_msm_state is NULL\n", __func__);
+		return -ENOMEM;
+	}
+
+	external_common_state->dev = &pdev->dev;
+	DEV_DBG("probe\n");
+	if (pdev->id == 0) {
+		struct resource *res;
+
+		#define GET_RES(name, mode) do {			\
+			res = platform_get_resource_byname(pdev, mode, name); \
+			if (!res) {					\
+				DEV_ERR("'" name "' resource not found\n"); \
+				rc = -ENODEV;				\
+				goto error;				\
+			}						\
+		} while (0)
+
+		#define IO_REMAP(var, name) do {			\
+			GET_RES(name, IORESOURCE_MEM);			\
+			var = ioremap(res->start, resource_size(res));	\
+			if (!var) {					\
+				DEV_ERR("'" name "' ioremap failed\n");	\
+				rc = -ENOMEM;				\
+				goto error;				\
+			}						\
+		} while (0)
+
+		#define GET_IRQ(var, name) do {				\
+			GET_RES(name, IORESOURCE_IRQ);			\
+			var = res->start;				\
+		} while (0)
+
+		IO_REMAP(hdmi_msm_state->qfprom_io, "hdmi_msm_qfprom_addr");
+		hdmi_msm_state->hdmi_io = MSM_HDMI_BASE;
+		GET_IRQ(hdmi_msm_state->irq, "hdmi_msm_irq");
+
+		hdmi_msm_state->pd = pdev->dev.platform_data;
+
+		#undef GET_RES
+		#undef IO_REMAP
+		#undef GET_IRQ
+		return 0;
+	}
+
+	hdmi_msm_state->hdmi_app_clk = clk_get(&pdev->dev, "core_clk");
+	if (IS_ERR(hdmi_msm_state->hdmi_app_clk)) {
+		DEV_ERR("'core_clk' clk not found\n");
+		rc = IS_ERR(hdmi_msm_state->hdmi_app_clk);
+		goto error;
+	}
+
+	hdmi_msm_state->hdmi_m_pclk = clk_get(&pdev->dev, "master_iface_clk");
+	if (IS_ERR(hdmi_msm_state->hdmi_m_pclk)) {
+		DEV_ERR("'master_iface_clk' clk not found\n");
+		rc = IS_ERR(hdmi_msm_state->hdmi_m_pclk);
+		goto error;
+	}
+
+	hdmi_msm_state->hdmi_s_pclk = clk_get(&pdev->dev, "slave_iface_clk");
+	if (IS_ERR(hdmi_msm_state->hdmi_s_pclk)) {
+		DEV_ERR("'slave_iface_clk' clk not found\n");
+		rc = IS_ERR(hdmi_msm_state->hdmi_s_pclk);
+		goto error;
+	}
+
+	rc = check_hdmi_features();
+	if (rc) {
+		DEV_ERR("Init FAILED: check_hdmi_features rc=%d\n", rc);
+		goto error;
+	}
+
+	if (!hdmi_msm_state->pd->core_power) {
+		DEV_ERR("Init FAILED: core_power function missing\n");
+		rc = -ENODEV;
+		goto error;
+	}
+	if (!hdmi_msm_state->pd->enable_5v) {
+		DEV_ERR("Init FAILED: enable_5v function missing\n");
+		rc = -ENODEV;
+		goto error;
+	}
+
+	if (!hdmi_msm_state->pd->cec_power) {
+		DEV_ERR("Init FAILED: cec_power function missing\n");
+		rc = -ENODEV;
+		goto error;
+	}
+
+	rc = request_threaded_irq(hdmi_msm_state->irq, NULL, &hdmi_msm_isr,
+		IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "hdmi_msm_isr", NULL);
+	if (rc) {
+		DEV_ERR("Init FAILED: IRQ request, rc=%d\n", rc);
+		goto error;
+	}
+	disable_irq(hdmi_msm_state->irq);
+
+	init_timer(&hdmi_msm_state->hpd_state_timer);
+	hdmi_msm_state->hpd_state_timer.function =
+		hdmi_msm_hpd_state_timer;
+	hdmi_msm_state->hpd_state_timer.data = (uint32)NULL;
+
+	hdmi_msm_state->hpd_state_timer.expires = 0xffffffffL;
+	add_timer(&hdmi_msm_state->hpd_state_timer);
+
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
+	init_timer(&hdmi_msm_state->hdcp_timer);
+	hdmi_msm_state->hdcp_timer.function =
+		hdmi_msm_hdcp_timer;
+	hdmi_msm_state->hdcp_timer.data = (uint32)NULL;
+
+	hdmi_msm_state->hdcp_timer.expires = 0xffffffffL;
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
+
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
+	init_timer(&hdmi_msm_state->cec_read_timer);
+	hdmi_msm_state->cec_read_timer.function =
+		hdmi_msm_cec_read_timer_func;
+	hdmi_msm_state->cec_read_timer.data = (uint32)NULL;
+
+	hdmi_msm_state->cec_read_timer.expires = 0xffffffffL;
+ #endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT */
+
+	fb_dev = msm_fb_add_device(pdev);
+	if (fb_dev) {
+		rc = external_common_state_create(fb_dev);
+		if (rc) {
+			DEV_ERR("Init FAILED: hdmi_msm_state_create, rc=%d\n",
+				rc);
+			goto error;
+		}
+	} else
+		DEV_ERR("Init FAILED: failed to add fb device\n");
+
+	DEV_INFO("HDMI HPD: ON\n");
+
+	rc = hdmi_msm_hpd_on(true);
+	if (rc)
+		goto error;
+
+	if (hdmi_msm_has_hdcp()) {
+		/* Don't Set Encryption in case of non HDCP builds */
+		external_common_state->present_hdcp = FALSE;
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
+		external_common_state->present_hdcp = TRUE;
+#endif
+	} else {
+		external_common_state->present_hdcp = FALSE;
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
+		/*
+		 * If the device is not hdcp capable do
+		 * not start hdcp timer.
+		 */
+		del_timer(&hdmi_msm_state->hdcp_timer);
+#endif
+	}
+
+	queue_work(hdmi_work_queue, &hdmi_msm_state->hpd_read_work);
+
+	/* Initialize hdmi node and register with switch driver */
+	if (hdmi_prim_display)
+		external_common_state->sdev.name = "hdmi_as_primary";
+	else
+		external_common_state->sdev.name = "hdmi";
+	if (switch_dev_register(&external_common_state->sdev) < 0)
+		DEV_ERR("Hdmi switch registration failed\n");
+
+	return 0;
+
+error:
+	if (hdmi_msm_state->qfprom_io)
+		iounmap(hdmi_msm_state->qfprom_io);
+	hdmi_msm_state->qfprom_io = NULL;
+
+	if (hdmi_msm_state->hdmi_io)
+		iounmap(hdmi_msm_state->hdmi_io);
+	hdmi_msm_state->hdmi_io = NULL;
+
+	external_common_state_remove();
+
+	if (hdmi_msm_state->hdmi_app_clk)
+		clk_put(hdmi_msm_state->hdmi_app_clk);
+	if (hdmi_msm_state->hdmi_m_pclk)
+		clk_put(hdmi_msm_state->hdmi_m_pclk);
+	if (hdmi_msm_state->hdmi_s_pclk)
+		clk_put(hdmi_msm_state->hdmi_s_pclk);
+
+	hdmi_msm_state->hdmi_app_clk = NULL;
+	hdmi_msm_state->hdmi_m_pclk = NULL;
+	hdmi_msm_state->hdmi_s_pclk = NULL;
+
+	return rc;
+}
+
+static int __devexit hdmi_msm_remove(struct platform_device *pdev)
+{
+	DEV_INFO("HDMI device: remove\n");
+
+	DEV_INFO("HDMI HPD: OFF\n");
+
+	/* Unregister hdmi node from switch driver */
+	switch_dev_unregister(&external_common_state->sdev);
+
+	hdmi_msm_hpd_off();
+	free_irq(hdmi_msm_state->irq, NULL);
+
+	if (hdmi_msm_state->qfprom_io)
+		iounmap(hdmi_msm_state->qfprom_io);
+	hdmi_msm_state->qfprom_io = NULL;
+
+	if (hdmi_msm_state->hdmi_io)
+		iounmap(hdmi_msm_state->hdmi_io);
+	hdmi_msm_state->hdmi_io = NULL;
+
+	external_common_state_remove();
+
+	if (hdmi_msm_state->hdmi_app_clk)
+		clk_put(hdmi_msm_state->hdmi_app_clk);
+	if (hdmi_msm_state->hdmi_m_pclk)
+		clk_put(hdmi_msm_state->hdmi_m_pclk);
+	if (hdmi_msm_state->hdmi_s_pclk)
+		clk_put(hdmi_msm_state->hdmi_s_pclk);
+
+	hdmi_msm_state->hdmi_app_clk = NULL;
+	hdmi_msm_state->hdmi_m_pclk = NULL;
+	hdmi_msm_state->hdmi_s_pclk = NULL;
+
+	kfree(hdmi_msm_state);
+	hdmi_msm_state = NULL;
+
+	return 0;
+}
+
+static int hdmi_msm_hpd_feature(int on)
+{
+	int rc = 0;
+
+	DEV_INFO("%s: %d\n", __func__, on);
+	if (on) {
+		rc = hdmi_msm_hpd_on(true);
+	} else {
+		hdmi_msm_hpd_off();
+		/* Set HDMI switch node to 0 on HPD feature disable */
+		switch_set_state(&external_common_state->sdev, 0);
+	}
+
+	return rc;
+}
+
+static struct platform_driver this_driver = {
+	.probe = hdmi_msm_probe,
+	.remove = hdmi_msm_remove,
+	.driver.name = "hdmi_msm",
+};
+
+static struct msm_fb_panel_data hdmi_msm_panel_data = {
+	.on = hdmi_msm_power_on,
+	.off = hdmi_msm_power_off,
+	.power_ctrl = hdmi_msm_power_ctrl,
+};
+
+static struct platform_device this_device = {
+	.name = "hdmi_msm",
+	.id = 1,
+	.dev.platform_data = &hdmi_msm_panel_data,
+};
+
+static int __init hdmi_msm_init(void)
+{
+	int rc;
+
+	if (msm_fb_detect_client("hdmi_msm"))
+		return 0;
+
+#ifdef CONFIG_FB_MSM_HDMI_AS_PRIMARY
+	hdmi_prim_display = 1;
+#endif
+
+	hdmi_msm_setup_video_mode_lut();
+	hdmi_msm_state = kzalloc(sizeof(*hdmi_msm_state), GFP_KERNEL);
+	if (!hdmi_msm_state) {
+		pr_err("hdmi_msm_init FAILED: out of memory\n");
+		rc = -ENOMEM;
+		goto init_exit;
+	}
+
+	external_common_state = &hdmi_msm_state->common;
+	external_common_state->video_resolution = HDMI_VFRMT_1920x1080p60_16_9;
+#ifdef CONFIG_FB_MSM_HDMI_3D
+	external_common_state->switch_3d = hdmi_msm_switch_3d;
+#endif
+	memset(external_common_state->spd_vendor_name, 0,
+			sizeof(external_common_state->spd_vendor_name));
+	memset(external_common_state->spd_product_description, 0,
+			sizeof(external_common_state->spd_product_description));
+
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
+	hdmi_msm_state->cec_queue_start =
+		kzalloc(sizeof(struct hdmi_msm_cec_msg)*CEC_QUEUE_SIZE,
+			GFP_KERNEL);
+	if (!hdmi_msm_state->cec_queue_start) {
+		pr_err("hdmi_msm_init FAILED: CEC queue out of memory\n");
+		rc = -ENOMEM;
+		goto init_exit;
+	}
+
+	hdmi_msm_state->cec_queue_wr = hdmi_msm_state->cec_queue_start;
+	hdmi_msm_state->cec_queue_rd = hdmi_msm_state->cec_queue_start;
+	hdmi_msm_state->cec_queue_full = false;
+#endif
+
+	/*
+	 * Create your work queue
+	 * allocs and returns ptr
+	*/
+	hdmi_work_queue = create_workqueue("hdmi_hdcp");
+	external_common_state->hpd_feature = hdmi_msm_hpd_feature;
+
+	rc = platform_driver_register(&this_driver);
+	if (rc) {
+		pr_err("hdmi_msm_init FAILED: platform_driver_register rc=%d\n",
+		       rc);
+		goto init_exit;
+	}
+
+	hdmi_common_init_panel_info(&hdmi_msm_panel_data.panel_info);
+	init_completion(&hdmi_msm_state->ddc_sw_done);
+	INIT_WORK(&hdmi_msm_state->hpd_state_work, hdmi_msm_hpd_state_work);
+	INIT_WORK(&hdmi_msm_state->hpd_read_work, hdmi_msm_hpd_read_work);
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
+	init_completion(&hdmi_msm_state->hdcp_success_done);
+	INIT_WORK(&hdmi_msm_state->hdcp_reauth_work, hdmi_msm_hdcp_reauth_work);
+	INIT_WORK(&hdmi_msm_state->hdcp_work, hdmi_msm_hdcp_work);
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
+
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
+	INIT_WORK(&hdmi_msm_state->cec_latch_detect_work,
+		  hdmi_msm_cec_latch_work);
+	init_completion(&hdmi_msm_state->cec_frame_wr_done);
+	init_completion(&hdmi_msm_state->cec_line_latch_wait);
+#endif
+
+	rc = platform_device_register(&this_device);
+	if (rc) {
+		pr_err("hdmi_msm_init FAILED: platform_device_register rc=%d\n",
+		       rc);
+		platform_driver_unregister(&this_driver);
+		goto init_exit;
+	}
+
+	pr_debug("%s: success:"
+#ifdef DEBUG
+		" DEBUG"
+#else
+		" RELEASE"
+#endif
+		" AUDIO EDID HPD HDCP"
+#ifndef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
+		":0"
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
+		" DVI"
+#ifndef CONFIG_FB_MSM_HDMI_MSM_PANEL_DVI_SUPPORT
+		":0"
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_DVI_SUPPORT */
+		"\n", __func__);
+
+	return 0;
+
+init_exit:
+	kfree(hdmi_msm_state);
+	hdmi_msm_state = NULL;
+
+	return rc;
+}
+
+static void __exit hdmi_msm_exit(void)
+{
+	platform_device_unregister(&this_device);
+	platform_driver_unregister(&this_driver);
+}
+
+module_init(hdmi_msm_init);
+module_exit(hdmi_msm_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.3");
+MODULE_AUTHOR("Qualcomm Innovation Center, Inc.");
+MODULE_DESCRIPTION("HDMI MSM TX driver");
diff --git a/drivers/video/msm/hdmi_msm.h b/drivers/video/msm/hdmi_msm.h
new file mode 100644
index 0000000..5195f2c
--- /dev/null
+++ b/drivers/video/msm/hdmi_msm.h
@@ -0,0 +1,138 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __HDMI_MSM_H__
+#define __HDMI_MSM_H__
+
+#include <mach/msm_iomap.h>
+#include "external_common.h"
+/* #define PORT_DEBUG */
+
+#ifdef PORT_DEBUG
+const char *hdmi_msm_name(uint32 offset);
+void hdmi_outp(uint32 offset, uint32 value);
+uint32 hdmi_inp(uint32 offset);
+
+#define HDMI_OUTP_ND(offset, value)	outpdw(MSM_HDMI_BASE+(offset), (value))
+#define HDMI_OUTP(offset, value)	hdmi_outp((offset), (value))
+#define HDMI_INP_ND(offset)		inpdw(MSM_HDMI_BASE+(offset))
+#define HDMI_INP(offset)		hdmi_inp((offset))
+#else
+#define HDMI_OUTP_ND(offset, value)	outpdw(MSM_HDMI_BASE+(offset), (value))
+#define HDMI_OUTP(offset, value)	outpdw(MSM_HDMI_BASE+(offset), (value))
+#define HDMI_INP_ND(offset)		inpdw(MSM_HDMI_BASE+(offset))
+#define HDMI_INP(offset)		inpdw(MSM_HDMI_BASE+(offset))
+#endif
+
+
+/*
+ * Ref. HDMI 1.4a
+ * Supplement-1 CEC Section 6, 7
+ */
+struct hdmi_msm_cec_msg {
+	uint8 sender_id;
+	uint8 recvr_id;
+	uint8 opcode;
+	uint8 operand[15];
+	uint8 frame_size;
+	uint8 retransmit;
+};
+
+#define QFPROM_BASE		((uint32)hdmi_msm_state->qfprom_io)
+#define HDMI_BASE		((uint32)hdmi_msm_state->hdmi_io)
+
+struct hdmi_msm_state_type {
+	boolean panel_power_on;
+	boolean hpd_initialized;
+#ifdef CONFIG_SUSPEND
+	boolean pm_suspended;
+#endif
+	int hpd_stable;
+	boolean hpd_prev_state;
+	boolean hpd_cable_chg_detected;
+	boolean full_auth_done;
+	boolean hpd_during_auth;
+	struct work_struct hpd_state_work, hpd_read_work;
+	struct timer_list hpd_state_timer;
+	struct completion ddc_sw_done;
+
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
+	boolean hdcp_activating;
+	boolean reauth ;
+	struct work_struct hdcp_reauth_work, hdcp_work;
+	struct completion hdcp_success_done;
+	struct timer_list hdcp_timer;
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
+
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
+	boolean cec_enabled;
+	unsigned int first_monitor;
+	int cec_logical_addr;
+	struct completion cec_frame_wr_done;
+	struct timer_list cec_read_timer;
+#define CEC_STATUS_WR_ERROR	0x0001
+#define CEC_STATUS_WR_DONE	0x0002
+#define CEC_STATUS_WR_TMOUT	0x0004
+	uint32 cec_frame_wr_status;
+
+	struct hdmi_msm_cec_msg *cec_queue_start;
+	struct hdmi_msm_cec_msg *cec_queue_wr;
+	struct hdmi_msm_cec_msg *cec_queue_rd;
+	boolean cec_queue_full;
+	boolean fsm_reset_done;
+
+	/*
+	 * CECT 9-5-1
+	 */
+	struct completion cec_line_latch_wait;
+	struct work_struct cec_latch_detect_work;
+
+#define CEC_QUEUE_SIZE		16
+#define CEC_QUEUE_END	 (hdmi_msm_state->cec_queue_start + CEC_QUEUE_SIZE)
+#define RETRANSMIT_MAX_NUM	5
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT */
+
+	int irq;
+	struct msm_hdmi_platform_data *pd;
+	struct clk *hdmi_app_clk;
+	struct clk *hdmi_m_pclk;
+	struct clk *hdmi_s_pclk;
+	void __iomem *qfprom_io;
+	void __iomem *hdmi_io;
+
+	struct external_common_state_type common;
+};
+
+extern struct hdmi_msm_state_type *hdmi_msm_state;
+
+uint32 hdmi_msm_get_io_base(void);
+
+#ifdef CONFIG_FB_MSM_HDMI_COMMON
+void hdmi_msm_set_mode(boolean power_on);
+int hdmi_msm_clk(int on);
+void hdmi_phy_reset(void);
+void hdmi_msm_reset_core(void);
+void hdmi_msm_init_phy(int video_format);
+void hdmi_msm_powerdown_phy(void);
+void hdmi_frame_ctrl_cfg(const struct hdmi_disp_mode_timing_type *timing);
+void hdmi_msm_phy_status_poll(void);
+#endif
+
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
+void hdmi_msm_cec_init(void);
+void hdmi_msm_cec_write_logical_addr(int addr);
+void hdmi_msm_cec_msg_recv(void);
+void hdmi_msm_cec_one_touch_play(void);
+void hdmi_msm_cec_msg_send(struct hdmi_msm_cec_msg *msg);
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT */
+
+#endif /* __HDMI_MSM_H__ */
diff --git a/drivers/video/msm/hdmi_sii9022.c b/drivers/video/msm/hdmi_sii9022.c
new file mode 100644
index 0000000..3d27488
--- /dev/null
+++ b/drivers/video/msm/hdmi_sii9022.c
@@ -0,0 +1,245 @@
+/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include "msm_fb.h"
+
+#define DEVICE_NAME "sii9022"
+#define SII9022_DEVICE_ID   0xB0
+
+struct sii9022_i2c_addr_data{
+	u8 addr;
+	u8 data;
+};
+
+/* video mode data */
+static u8 video_mode_data[] = {
+	0x00,
+	0xF9, 0x1C, 0x70, 0x17, 0x72, 0x06, 0xEE, 0x02,
+};
+
+static u8 avi_io_format[] = {
+	0x09,
+	0x00, 0x00,
+};
+
+/* power state */
+static struct sii9022_i2c_addr_data regset0[] = {
+	{ 0x60, 0x04 },
+	{ 0x63, 0x00 },
+	{ 0x1E, 0x00 },
+};
+
+static u8 video_infoframe[] = {
+	0x0C,
+	0xF0, 0x00, 0x68, 0x00, 0x04, 0x00, 0x19, 0x00,
+	0xE9, 0x02, 0x04, 0x01, 0x04, 0x06,
+};
+
+/* configure audio */
+static struct sii9022_i2c_addr_data regset1[] = {
+	{ 0x26, 0x90 },
+	{ 0x20, 0x90 },
+	{ 0x1F, 0x80 },
+	{ 0x26, 0x80 },
+	{ 0x24, 0x02 },
+	{ 0x25, 0x0B },
+	{ 0xBC, 0x02 },
+	{ 0xBD, 0x24 },
+	{ 0xBE, 0x02 },
+};
+
+/* enable audio */
+static u8 misc_infoframe[] = {
+	0xBF,
+	0xC2, 0x84, 0x01, 0x0A, 0x6F, 0x02, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+/* set HDMI, active */
+static struct sii9022_i2c_addr_data regset2[] = {
+	{ 0x1A, 0x01 },
+	{ 0x3D, 0x00 },
+};
+
+static int send_i2c_data(struct i2c_client *client,
+			 struct sii9022_i2c_addr_data *regset,
+			 int size)
+{
+	int i;
+	int rc = 0;
+
+	for (i = 0; i < size; i++) {
+		rc = i2c_smbus_write_byte_data(
+			client,
+			regset[i].addr, regset[i].data);
+		if (rc)
+			break;
+	}
+	return rc;
+}
+
+static int hdmi_sii_enable(struct i2c_client *client)
+{
+	int rc;
+	int retries = 10;
+	int count;
+
+	rc = i2c_smbus_write_byte_data(client, 0xC7, 0x00);
+	if (rc)
+		goto enable_exit;
+
+	do {
+		msleep(1);
+		rc = i2c_smbus_read_byte_data(client, 0x1B);
+	} while ((rc != SII9022_DEVICE_ID) && retries--);
+
+	if (rc != SII9022_DEVICE_ID)
+		return -ENODEV;
+
+	rc = i2c_smbus_write_byte_data(client, 0x1A, 0x11);
+	if (rc)
+		goto enable_exit;
+
+	count = ARRAY_SIZE(video_mode_data);
+	rc = i2c_master_send(client, video_mode_data, count);
+	if (rc != count) {
+		rc = -EIO;
+		goto enable_exit;
+	}
+
+	rc = i2c_smbus_write_byte_data(client, 0x08, 0x20);
+	if (rc)
+		goto enable_exit;
+	count = ARRAY_SIZE(avi_io_format);
+	rc = i2c_master_send(client, avi_io_format, count);
+	if (rc != count) {
+		rc = -EIO;
+		goto enable_exit;
+	}
+
+	rc = send_i2c_data(client, regset0, ARRAY_SIZE(regset0));
+	if (rc)
+		goto enable_exit;
+
+	count = ARRAY_SIZE(video_infoframe);
+	rc = i2c_master_send(client, video_infoframe, count);
+	if (rc != count) {
+		rc = -EIO;
+		goto enable_exit;
+	}
+
+	rc = send_i2c_data(client, regset1, ARRAY_SIZE(regset1));
+	if (rc)
+		goto enable_exit;
+
+	count = ARRAY_SIZE(misc_infoframe);
+	rc = i2c_master_send(client, misc_infoframe, count);
+	if (rc != count) {
+		rc = -EIO;
+		goto enable_exit;
+	}
+
+	rc = send_i2c_data(client, regset2, ARRAY_SIZE(regset2));
+	if (rc)
+		goto enable_exit;
+
+	return 0;
+enable_exit:
+	printk(KERN_ERR "%s: exited rc=%d\n", __func__, rc);
+	return rc;
+}
+
+static const struct i2c_device_id hmdi_sii_id[] = {
+	{ DEVICE_NAME, 0 },
+	{ }
+};
+
+static int hdmi_sii_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	int rc;
+
+	if (!i2c_check_functionality(client->adapter,
+				     I2C_FUNC_SMBUS_BYTE | I2C_FUNC_I2C))
+		return -ENODEV;
+	rc = hdmi_sii_enable(client);
+	return rc;
+}
+
+
+static struct i2c_driver hdmi_sii_i2c_driver = {
+	.driver = {
+		.name = DEVICE_NAME,
+		.owner = THIS_MODULE,
+	},
+	.probe = hdmi_sii_probe,
+	.remove =  __exit_p(hdmi_sii_remove),
+	.id_table = hmdi_sii_id,
+};
+
+static int __init hdmi_sii_init(void)
+{
+	int ret;
+	struct msm_panel_info pinfo;
+
+	if (msm_fb_detect_client("hdmi_sii9022"))
+		return 0;
+
+	pinfo.xres = 1280;
+	pinfo.yres = 720;
+	MSM_FB_SINGLE_MODE_PANEL(&pinfo);
+	pinfo.type = HDMI_PANEL;
+	pinfo.pdest = DISPLAY_1;
+	pinfo.wait_cycle = 0;
+	pinfo.bpp = 18;
+	pinfo.fb_num = 2;
+	pinfo.clk_rate = 74250000;
+
+	pinfo.lcdc.h_back_porch = 124;
+	pinfo.lcdc.h_front_porch = 110;
+	pinfo.lcdc.h_pulse_width = 136;
+	pinfo.lcdc.v_back_porch = 19;
+	pinfo.lcdc.v_front_porch = 5;
+	pinfo.lcdc.v_pulse_width = 6;
+	pinfo.lcdc.border_clr = 0;
+	pinfo.lcdc.underflow_clr = 0xff;
+	pinfo.lcdc.hsync_skew = 0;
+
+	ret = lcdc_device_register(&pinfo);
+	if (ret) {
+		printk(KERN_ERR "%s: failed to register device\n", __func__);
+		goto init_exit;
+	}
+
+	ret = i2c_add_driver(&hdmi_sii_i2c_driver);
+	if (ret)
+		printk(KERN_ERR "%s: failed to add i2c driver\n", __func__);
+
+init_exit:
+	return ret;
+}
+
+static void __exit hdmi_sii_exit(void)
+{
+	i2c_del_driver(&hdmi_sii_i2c_driver);
+}
+
+module_init(hdmi_sii_init);
+module_exit(hdmi_sii_exit);
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.1");
+MODULE_AUTHOR("Qualcomm Innovation Center, Inc.");
+MODULE_DESCRIPTION("SiI9022 HDMI driver");
+MODULE_ALIAS("platform:hdmi-sii9022");
diff --git a/drivers/video/msm/lcdc.c b/drivers/video/msm/lcdc.c
new file mode 100644
index 0000000..863d59d
--- /dev/null
+++ b/drivers/video/msm/lcdc.c
@@ -0,0 +1,289 @@
+/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <mach/hardware.h>
+#include <linux/io.h>
+
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <linux/semaphore.h>
+#include <linux/uaccess.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+#include "msm_fb.h"
+
+static int lcdc_probe(struct platform_device *pdev);
+static int lcdc_remove(struct platform_device *pdev);
+
+static int lcdc_off(struct platform_device *pdev);
+static int lcdc_on(struct platform_device *pdev);
+
+static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST];
+static int pdev_list_cnt;
+
+static struct clk *pixel_mdp_clk; /* drives the lcdc block in mdp */
+static struct clk *pixel_lcdc_clk; /* drives the lcdc interface */
+
+static struct platform_driver lcdc_driver = {
+	.probe = lcdc_probe,
+	.remove = lcdc_remove,
+	.suspend = NULL,
+	.resume = NULL,
+	.shutdown = NULL,
+	.driver = {
+		   .name = "lcdc",
+		   },
+};
+
+static struct lcdc_platform_data *lcdc_pdata;
+
+static int lcdc_off(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct msm_fb_data_type *mfd;
+
+	mfd = platform_get_drvdata(pdev);
+	ret = panel_next_off(pdev);
+
+	clk_disable_unprepare(pixel_mdp_clk);
+	clk_disable_unprepare(pixel_lcdc_clk);
+
+	if (lcdc_pdata && lcdc_pdata->lcdc_power_save)
+		lcdc_pdata->lcdc_power_save(0);
+
+	if (lcdc_pdata && lcdc_pdata->lcdc_gpio_config)
+		ret = lcdc_pdata->lcdc_gpio_config(0);
+
+#ifndef CONFIG_MSM_BUS_SCALING
+	if (mfd->ebi1_clk) {
+		if (mdp_rev == MDP_REV_303) {
+			if (clk_set_rate(mfd->ebi1_clk, 0))
+				pr_err("%s: ebi1_lcdc_clk set rate failed\n",
+					__func__);
+		}
+		clk_disable_unprepare(mfd->ebi1_clk);
+	}
+#else
+	mdp_bus_scale_update_request(0);
+#endif
+
+	return ret;
+}
+
+static int lcdc_on(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct msm_fb_data_type *mfd;
+	unsigned long panel_pixclock_freq = 0;
+#ifndef CONFIG_MSM_BUS_SCALING
+	unsigned long pm_qos_rate;
+#endif
+	mfd = platform_get_drvdata(pdev);
+
+	if (lcdc_pdata && lcdc_pdata->lcdc_get_clk)
+		panel_pixclock_freq = lcdc_pdata->lcdc_get_clk();
+
+	if (!panel_pixclock_freq)
+		panel_pixclock_freq = mfd->fbi->var.pixclock;
+#ifdef CONFIG_MSM_BUS_SCALING
+	mdp_bus_scale_update_request(2);
+#else
+	if (panel_pixclock_freq > 65000000)
+		/* pm_qos_rate should be in Khz */
+		pm_qos_rate = panel_pixclock_freq / 1000 ;
+	else
+		pm_qos_rate = 65000;
+
+	if (mfd->ebi1_clk) {
+		if (mdp_rev == MDP_REV_303) {
+			if (clk_set_rate(mfd->ebi1_clk, 65000000))
+				pr_err("%s: ebi1_lcdc_clk set rate failed\n",
+					__func__);
+		} else {
+			clk_set_rate(mfd->ebi1_clk, pm_qos_rate * 1000);
+		}
+		clk_prepare_enable(mfd->ebi1_clk);
+	}
+
+#endif
+	mfd = platform_get_drvdata(pdev);
+
+	mfd->fbi->var.pixclock = clk_round_rate(pixel_mdp_clk,
+					mfd->fbi->var.pixclock);
+	ret = clk_set_rate(pixel_mdp_clk, mfd->fbi->var.pixclock);
+	if (ret) {
+		pr_err("%s: Can't set MDP LCDC pixel clock to rate %u\n",
+			__func__, mfd->fbi->var.pixclock);
+		goto out;
+	}
+
+	clk_prepare_enable(pixel_mdp_clk);
+	clk_prepare_enable(pixel_lcdc_clk);
+
+	if (lcdc_pdata && lcdc_pdata->lcdc_power_save)
+		lcdc_pdata->lcdc_power_save(1);
+	if (lcdc_pdata && lcdc_pdata->lcdc_gpio_config)
+		ret = lcdc_pdata->lcdc_gpio_config(1);
+
+	ret = panel_next_on(pdev);
+
+out:
+	return ret;
+}
+
+static int lcdc_probe(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+	struct fb_info *fbi;
+	struct platform_device *mdp_dev = NULL;
+	struct msm_fb_panel_data *pdata = NULL;
+	int rc;
+	struct clk *ebi1_clk = NULL;
+
+	if (pdev->id == 0) {
+		lcdc_pdata = pdev->dev.platform_data;
+		pixel_mdp_clk = clk_get(&pdev->dev, "mdp_clk");
+		if (IS_ERR(pixel_mdp_clk)) {
+			pr_err("Couldnt find pixel_mdp_clk\n");
+			return -EINVAL;
+		}
+
+		pixel_lcdc_clk = clk_get(&pdev->dev, "lcdc_clk");
+		if (IS_ERR(pixel_lcdc_clk)) {
+			pr_err("Couldnt find pixel_lcdc_clk\n");
+			return -EINVAL;
+		}
+
+#ifndef CONFIG_MSM_BUS_SCALING
+		ebi1_clk = clk_get(&pdev->dev, "mem_clk");
+		if (IS_ERR(ebi1_clk))
+			return PTR_ERR(ebi1_clk);
+#endif
+
+		return 0;
+	}
+
+	mfd = platform_get_drvdata(pdev);
+	mfd->ebi1_clk = ebi1_clk;
+
+	if (!mfd)
+		return -ENODEV;
+
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST)
+		return -ENOMEM;
+
+	mdp_dev = platform_device_alloc("mdp", pdev->id);
+	if (!mdp_dev)
+		return -ENOMEM;
+
+	/*
+	 * link to the latest pdev
+	 */
+	mfd->pdev = mdp_dev;
+	mfd->dest = DISPLAY_LCDC;
+
+	/*
+	 * alloc panel device data
+	 */
+	if (platform_device_add_data
+	    (mdp_dev, pdev->dev.platform_data,
+	     sizeof(struct msm_fb_panel_data))) {
+		pr_err("lcdc_probe: platform_device_add_data failed!\n");
+		platform_device_put(mdp_dev);
+		return -ENOMEM;
+	}
+	/*
+	 * data chain
+	 */
+	pdata = (struct msm_fb_panel_data *)mdp_dev->dev.platform_data;
+	pdata->on = lcdc_on;
+	pdata->off = lcdc_off;
+	pdata->next = pdev;
+
+	/*
+	 * get/set panel specific fb info
+	 */
+	mfd->panel_info = pdata->panel_info;
+
+	if (mfd->index == 0)
+		mfd->fb_imgType = MSMFB_DEFAULT_TYPE;
+	else
+		mfd->fb_imgType = MDP_RGB_565;
+
+	fbi = mfd->fbi;
+	fbi->var.pixclock = clk_round_rate(pixel_mdp_clk,
+					mfd->panel_info.clk_rate);
+	fbi->var.left_margin = mfd->panel_info.lcdc.h_back_porch;
+	fbi->var.right_margin = mfd->panel_info.lcdc.h_front_porch;
+	fbi->var.upper_margin = mfd->panel_info.lcdc.v_back_porch;
+	fbi->var.lower_margin = mfd->panel_info.lcdc.v_front_porch;
+	fbi->var.hsync_len = mfd->panel_info.lcdc.h_pulse_width;
+	fbi->var.vsync_len = mfd->panel_info.lcdc.v_pulse_width;
+
+	/*
+	 * set driver data
+	 */
+	platform_set_drvdata(mdp_dev, mfd);
+	/*
+	 * register in mdp driver
+	 */
+	rc = platform_device_add(mdp_dev);
+	if (rc)
+		goto lcdc_probe_err;
+
+	pdev_list[pdev_list_cnt++] = pdev;
+
+	return 0;
+
+lcdc_probe_err:
+	platform_device_put(mdp_dev);
+	return rc;
+}
+
+static int lcdc_remove(struct platform_device *pdev)
+{
+#ifndef CONFIG_MSM_BUS_SCALING
+	struct msm_fb_data_type *mfd;
+
+	mfd = platform_get_drvdata(pdev);
+
+	clk_put(mfd->ebi1_clk);
+#endif
+	return 0;
+}
+
+static int lcdc_register_driver(void)
+{
+	return platform_driver_register(&lcdc_driver);
+}
+
+static int __init lcdc_driver_init(void)
+{
+
+	return lcdc_register_driver();
+}
+
+module_init(lcdc_driver_init);
diff --git a/drivers/video/msm/lcdc_auo_wvga.c b/drivers/video/msm/lcdc_auo_wvga.c
new file mode 100644
index 0000000..6b0733f
--- /dev/null
+++ b/drivers/video/msm/lcdc_auo_wvga.c
@@ -0,0 +1,411 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/pwm.h>
+#ifdef CONFIG_SPI_QUP
+#include <linux/spi/spi.h>
+#else
+#include <mach/gpio.h>
+#endif
+#include "msm_fb.h"
+
+#define MAX_BACKLIGHT_LEVEL			15
+#define PANEL_CMD_BACKLIGHT_LEVEL	0x6A18
+#define PANEL_CMD_FORMAT			0x3A00
+#define PANEL_CMD_RGBCTRL			0x3B00
+#define PANEL_CMD_BCTRL				0x5300
+#define PANEL_CMD_PWM_EN			0x6A17
+
+#define PANEL_CMD_SLEEP_OUT			0x1100
+#define PANEL_CMD_DISP_ON			0x2900
+#define PANEL_CMD_DISP_OFF			0x2800
+#define PANEL_CMD_SLEEP_IN			0x1000
+
+#define LCDC_AUO_PANEL_NAME			"lcdc_auo_wvga"
+
+#ifdef CONFIG_SPI_QUP
+#define LCDC_AUO_SPI_DEVICE_NAME	"lcdc_auo_nt35582"
+static struct spi_device *lcdc_spi_client;
+#else
+static int spi_cs;
+static int spi_sclk;
+static int spi_mosi;
+#endif
+
+struct auo_state_type {
+	boolean display_on;
+	int bl_level;
+};
+
+
+static struct auo_state_type auo_state = { .bl_level = 10 };
+static struct msm_panel_common_pdata *lcdc_auo_pdata;
+
+#ifndef CONFIG_SPI_QUP
+static void auo_spi_write_byte(u8 data)
+{
+	uint32 bit;
+	int bnum;
+
+	bnum = 8;			/* 8 data bits */
+	bit = 0x80;
+	while (bnum--) {
+		gpio_set_value(spi_sclk, 0); /* clk low */
+		gpio_set_value(spi_mosi, (data & bit) ? 1 : 0);
+		udelay(1);
+		gpio_set_value(spi_sclk, 1); /* clk high */
+		udelay(1);
+		bit >>= 1;
+	}
+	gpio_set_value(spi_mosi, 0);
+}
+
+static void auo_spi_read_byte(u16 cmd_16, u8 *data)
+{
+	int bnum;
+	u8 cmd_hi = (u8)(cmd_16 >> 8);
+	u8 cmd_low = (u8)(cmd_16);
+
+	/* Chip Select - low */
+	gpio_set_value(spi_cs, 0);
+	udelay(2);
+
+	/* command byte first */
+	auo_spi_write_byte(0x20);
+	udelay(2);
+	auo_spi_write_byte(cmd_hi);
+	udelay(2);
+	auo_spi_write_byte(0x00);
+	udelay(2);
+	auo_spi_write_byte(cmd_low);
+	udelay(2);
+	auo_spi_write_byte(0xc0);
+	udelay(2);
+
+	gpio_direction_input(spi_mosi);
+
+	/* followed by data bytes */
+	bnum = 1 * 8;	/* number of bits */
+	*data = 0;
+	while (bnum) {
+		gpio_set_value(spi_sclk, 0); /* clk low */
+		udelay(1);
+		*data <<= 1;
+		*data |= gpio_get_value(spi_mosi) ? 1 : 0;
+		gpio_set_value(spi_sclk, 1); /* clk high */
+		udelay(1);
+		--bnum;
+		if ((bnum % 8) == 0)
+			++data;
+	}
+
+	gpio_direction_output(spi_mosi, 0);
+
+	/* Chip Select - high */
+	udelay(2);
+	gpio_set_value(spi_cs, 1);
+}
+#endif
+
+static int auo_serigo(u8 *input_data, int input_len)
+{
+#ifdef CONFIG_SPI_QUP
+	int                 rc;
+	struct spi_message  m;
+	struct spi_transfer t;
+
+	if (!lcdc_spi_client) {
+		pr_err("%s lcdc_spi_client is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	memset(&t, 0, sizeof t);
+
+	t.tx_buf = input_data;
+	t.len = input_len;
+	t.bits_per_word = 16;
+
+	spi_setup(lcdc_spi_client);
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+
+	rc = spi_sync(lcdc_spi_client, &m);
+
+	return rc;
+#else
+	int i;
+
+	/* Chip Select - low */
+	gpio_set_value(spi_cs, 0);
+	udelay(2);
+
+	for (i = 0; i < input_len; ++i) {
+		auo_spi_write_byte(input_data[i]);
+		udelay(2);
+	}
+
+	/* Chip Select - high */
+	gpio_set_value(spi_cs, 1);
+
+	return 0;
+#endif
+}
+
+#ifndef CONFIG_SPI_QUP
+static void auo_spi_init(void)
+{
+	spi_sclk = *(lcdc_auo_pdata->gpio_num);
+	spi_cs   = *(lcdc_auo_pdata->gpio_num + 1);
+	spi_mosi = *(lcdc_auo_pdata->gpio_num + 2);
+
+	/* Set the output so that we don't disturb the slave device */
+	gpio_set_value(spi_sclk, 1);
+	gpio_set_value(spi_mosi, 0);
+
+	/* Set the Chip Select deasserted (active low) */
+	gpio_set_value(spi_cs, 1);
+}
+#endif
+
+static struct work_struct disp_on_delayed_work;
+static void auo_write_cmd(u16  cmd)
+{
+	u8  local_data[4];
+
+	local_data[0] = 0x20;
+	local_data[1] = (u8)(cmd >> 8);
+	local_data[2] = 0;
+	local_data[3] = (u8)cmd;
+	auo_serigo(local_data, 4);
+}
+static void auo_write_cmd_1param(u16  cmd, u8  para1)
+{
+	u8  local_data[6];
+
+	local_data[0] = 0x20;
+	local_data[1] = (u8)(cmd >> 8);
+	local_data[2] = 0;
+	local_data[3] = (u8)cmd;
+	local_data[4] = 0x40;
+	local_data[5] = para1;
+	auo_serigo(local_data, 6);
+}
+static void lcdc_auo_set_backlight(struct msm_fb_data_type *mfd)
+{
+	int bl_level;
+
+	bl_level = mfd->bl_level;
+	if (auo_state.display_on) {
+		auo_write_cmd_1param(PANEL_CMD_BACKLIGHT_LEVEL,
+			bl_level * 255 / MAX_BACKLIGHT_LEVEL);
+		auo_state.bl_level = bl_level;
+	}
+
+}
+static void auo_disp_on_delayed_work(struct work_struct *work_ptr)
+{
+	/* 0x1100: Sleep Out */
+	auo_write_cmd(PANEL_CMD_SLEEP_OUT);
+
+	msleep(180);
+
+	/* SET_PIXEL_FORMAT: Set how many bits per pixel are used (3A00h)*/
+	auo_write_cmd_1param(PANEL_CMD_FORMAT, 0x66); /* 18 bits */
+
+	/* RGBCTRL: RGB Interface Signal Control (3B00h) */
+	auo_write_cmd_1param(PANEL_CMD_RGBCTRL, 0x2B);
+
+	/* Display ON command */
+	auo_write_cmd(PANEL_CMD_DISP_ON);
+	msleep(20);
+
+	/*Backlight on */
+	auo_write_cmd_1param(PANEL_CMD_BCTRL, 0x24); /*BCTRL, BL */
+	auo_write_cmd_1param(PANEL_CMD_PWM_EN, 0x01); /*Enable PWM Level */
+
+	msleep(20);
+}
+
+static void auo_disp_on(void)
+{
+	if (!auo_state.display_on) {
+		INIT_WORK(&disp_on_delayed_work, auo_disp_on_delayed_work);
+#ifdef CONFIG_SPI_QUP
+		if (lcdc_spi_client)
+#endif
+			schedule_work(&disp_on_delayed_work);
+		auo_state.display_on = TRUE;
+	}
+}
+
+static int lcdc_auo_panel_on(struct platform_device *pdev)
+{
+	pr_info("%s\n", __func__);
+	if (!auo_state.display_on) {
+#ifndef CONFIG_SPI_QUP
+		lcdc_auo_pdata->panel_config_gpio(1);
+		auo_spi_init();
+#endif
+		auo_disp_on();
+	}
+	return 0;
+}
+
+static int lcdc_auo_panel_off(struct platform_device *pdev)
+{
+	pr_info("%s\n", __func__);
+	if (auo_state.display_on) {
+		/* 0x2800: Display Off */
+		auo_write_cmd(PANEL_CMD_DISP_OFF);
+		msleep(120);
+		/* 0x1000: Sleep In */
+		auo_write_cmd(PANEL_CMD_SLEEP_IN);
+		msleep(120);
+
+		auo_state.display_on = FALSE;
+	}
+	return 0;
+}
+
+static int auo_probe(struct platform_device *pdev)
+{
+	pr_info("%s: id=%d\n", __func__, pdev->id);
+	if (pdev->id == 0) {
+		lcdc_auo_pdata = pdev->dev.platform_data;
+		return 0;
+	}
+
+	msm_fb_add_device(pdev);
+
+	return 0;
+}
+
+#ifdef CONFIG_SPI_QUP
+static int __devinit lcdc_auo_spi_probe(struct spi_device *spi)
+{
+	pr_info("%s\n", __func__);
+	lcdc_spi_client = spi;
+	lcdc_spi_client->bits_per_word = 32;
+	if (auo_state.display_on)
+		schedule_work(&disp_on_delayed_work);
+	return 0;
+}
+static int __devexit lcdc_auo_spi_remove(struct spi_device *spi)
+{
+	lcdc_spi_client = NULL;
+	return 0;
+}
+static struct spi_driver lcdc_auo_spi_driver = {
+	.driver.name   = LCDC_AUO_SPI_DEVICE_NAME,
+	.driver.owner  = THIS_MODULE,
+	.probe         = lcdc_auo_spi_probe,
+	.remove        = __devexit_p(lcdc_auo_spi_remove),
+};
+#endif
+
+static struct platform_driver this_driver = {
+	.probe		= auo_probe,
+	.driver.name	= LCDC_AUO_PANEL_NAME,
+};
+
+static struct msm_fb_panel_data auo_panel_data = {
+	.on = lcdc_auo_panel_on,
+	.off = lcdc_auo_panel_off,
+	.set_backlight = lcdc_auo_set_backlight,
+};
+
+static struct platform_device this_device = {
+	.name	= LCDC_AUO_PANEL_NAME,
+	.id	= 1,
+	.dev.platform_data = &auo_panel_data,
+};
+
+static int __init lcdc_auo_panel_init(void)
+{
+	int ret;
+	struct msm_panel_info *pinfo;
+
+	if (msm_fb_detect_client(LCDC_AUO_PANEL_NAME)) {
+		pr_err("%s: detect failed\n", __func__);
+		return 0;
+	}
+
+	ret = platform_driver_register(&this_driver);
+	if (ret) {
+		pr_err("%s: driver register failed, rc=%d\n", __func__, ret);
+		return ret;
+	}
+
+	pinfo = &auo_panel_data.panel_info;
+	pinfo->xres = 480;
+	pinfo->yres = 800;
+	pinfo->type = LCDC_PANEL;
+	pinfo->pdest = DISPLAY_1;
+	pinfo->wait_cycle = 0;
+	pinfo->bpp = 18;
+	pinfo->fb_num = 2;
+	pinfo->clk_rate = 25600000;
+	pinfo->bl_max = MAX_BACKLIGHT_LEVEL;
+	pinfo->bl_min = 1;
+
+	pinfo->lcdc.h_back_porch = 16-2;	/* HBP-HLW */
+	pinfo->lcdc.h_front_porch = 16;
+	pinfo->lcdc.h_pulse_width = 2;
+
+	pinfo->lcdc.v_back_porch = 3-2;		/* VBP-VLW */
+	pinfo->lcdc.v_front_porch = 28;
+	pinfo->lcdc.v_pulse_width = 2;
+
+	pinfo->lcdc.border_clr = 0;
+	pinfo->lcdc.underflow_clr = 0xff;
+	pinfo->lcdc.hsync_skew = 0;
+
+	ret = platform_device_register(&this_device);
+	if (ret) {
+		pr_err("%s: device register failed, rc=%d\n", __func__, ret);
+		goto fail_driver;
+	}
+#ifdef CONFIG_SPI_QUP
+	ret = spi_register_driver(&lcdc_auo_spi_driver);
+
+	if (ret) {
+		pr_err("%s: spi register failed: rc=%d\n", __func__, ret);
+		goto fail_device;
+	}
+	pr_info("%s: SUCCESS (SPI)\n", __func__);
+#else
+	pr_info("%s: SUCCESS (BitBang)\n", __func__);
+#endif
+	return ret;
+
+#ifdef CONFIG_SPI_QUP
+fail_device:
+	platform_device_unregister(&this_device);
+#endif
+fail_driver:
+	platform_driver_unregister(&this_driver);
+
+	return ret;
+}
+
+module_init(lcdc_auo_panel_init);
+static void __exit lcdc_auo_panel_exit(void)
+{
+	pr_info("%s\n", __func__);
+	platform_device_unregister(&this_device);
+	platform_driver_unregister(&this_driver);
+#ifdef CONFIG_SPI_QUP
+	spi_unregister_driver(&lcdc_auo_spi_driver);
+#endif
+}
+module_exit(lcdc_auo_panel_exit);
diff --git a/drivers/video/msm/lcdc_chimei_wxga.c b/drivers/video/msm/lcdc_chimei_wxga.c
new file mode 100644
index 0000000..7453ecb
--- /dev/null
+++ b/drivers/video/msm/lcdc_chimei_wxga.c
@@ -0,0 +1,231 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/pwm.h>
+#ifdef CONFIG_PMIC8058_PWM
+#include <linux/mfd/pmic8058.h>
+#include <linux/pmic8058-pwm.h>
+#endif
+#include <mach/gpio.h>
+#include "msm_fb.h"
+
+
+
+
+static struct pwm_device *bl_pwm;
+
+#define PWM_FREQ_HZ 210
+#define PWM_PERIOD_USEC (USEC_PER_SEC / PWM_FREQ_HZ)
+#define PWM_DUTY_LEVEL (PWM_PERIOD_USEC / PWM_LEVEL)
+#define PWM_LEVEL 15
+
+static struct msm_panel_common_pdata *cm_pdata;
+static struct platform_device *cm_fbpdev;
+static int led_pwm;		/* pm8058 gpio 24, channel 0 */
+static int led_en;		/* pm8058 gpio 1 */
+static int lvds_pwr_down;	/* msm gpio 30 */
+static int chimei_bl_level = 1;
+
+
+static void lcdc_chimei_set_backlight(int level)
+{
+	int ret;
+
+	if (bl_pwm) {
+		ret = pwm_config(bl_pwm, PWM_DUTY_LEVEL * level,
+			PWM_PERIOD_USEC);
+		if (ret) {
+			pr_err("%s: pwm_config on pwm failed %d\n",
+					__func__, ret);
+			return;
+		}
+
+		ret = pwm_enable(bl_pwm);
+		if (ret) {
+			pr_err("%s: pwm_enable on pwm failed %d\n",
+					__func__, ret);
+			return;
+		}
+	}
+
+	chimei_bl_level = level;
+}
+
+static int lcdc_chimei_panel_on(struct platform_device *pdev)
+{
+	int ret;
+
+	/* panel powered on here */
+
+	ret = gpio_request(lvds_pwr_down, "lvds_pwr_down");
+	if (ret == 0) {
+		/* output, pull high to enable */
+		gpio_direction_output(lvds_pwr_down, 1);
+	} else {
+		pr_err("%s: lvds_pwr_down=%d, gpio_request failed\n",
+			__func__, lvds_pwr_down);
+	}
+
+	msleep(200);
+	/* power on led pwm power >= 200 ms */
+
+	if (chimei_bl_level == 0)
+		chimei_bl_level = 1;
+	lcdc_chimei_set_backlight(chimei_bl_level);
+
+	msleep(10);
+
+	ret = gpio_request(led_en, "led_en");
+	if (ret == 0) {
+		/* output, pull high */
+		gpio_direction_output(led_en, 1);
+	} else {
+		pr_err("%s: led_en=%d, gpio_request failed\n",
+			__func__, led_en);
+	}
+	return ret;
+}
+
+static int lcdc_chimei_panel_off(struct platform_device *pdev)
+{
+	/* pull low to disable */
+	gpio_set_value_cansleep(led_en, 0);
+	gpio_free(led_en);
+
+	msleep(10);
+
+	lcdc_chimei_set_backlight(0);
+
+	msleep(200);
+	/* power off led pwm power >= 200 ms */
+
+	/* pull low to shut down lvds */
+	gpio_set_value_cansleep(lvds_pwr_down, 0);
+	gpio_free(lvds_pwr_down);
+
+	/* panel power off here */
+
+	return 0;
+}
+
+static void lcdc_chimei_panel_backlight(struct msm_fb_data_type *mfd)
+{
+	lcdc_chimei_set_backlight(mfd->bl_level);
+}
+
+static int __devinit chimei_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+
+	if (pdev->id == 0) {
+		cm_pdata = pdev->dev.platform_data;
+		if (cm_pdata == NULL) {
+			pr_err("%s: no PWM gpio specified\n", __func__);
+			return 0;
+		}
+		led_pwm = cm_pdata->gpio_num[0];
+		led_en = cm_pdata->gpio_num[1];
+		lvds_pwr_down = cm_pdata->gpio_num[2];
+		pr_info("%s: led_pwm=%d led_en=%d lvds_pwr_down=%d\n",
+			__func__, led_pwm, led_en, lvds_pwr_down);
+		return 0;
+	}
+
+	if (cm_pdata == NULL)
+		return -ENODEV;
+
+	bl_pwm = pwm_request(led_pwm, "backlight");
+	if (bl_pwm == NULL || IS_ERR(bl_pwm)) {
+		pr_err("%s pwm_request() failed\n", __func__);
+		bl_pwm = NULL;
+	}
+
+	cm_fbpdev = msm_fb_add_device(pdev);
+	if (!cm_fbpdev) {
+		dev_err(&pdev->dev, "failed to add msm_fb device\n");
+		rc = -ENODEV;
+		goto probe_exit;
+	}
+
+probe_exit:
+	return rc;
+}
+
+static struct platform_driver this_driver = {
+	.probe  = chimei_probe,
+	.driver = {
+		.name   = "lcdc_chimei_lvds_wxga",
+	},
+};
+
+static struct msm_fb_panel_data chimei_panel_data = {
+	.on = lcdc_chimei_panel_on,
+	.off = lcdc_chimei_panel_off,
+	.set_backlight = lcdc_chimei_panel_backlight,
+};
+
+static struct platform_device this_device = {
+	.name   = "lcdc_chimei_lvds_wxga",
+	.id	= 1,
+	.dev	= {
+		.platform_data = &chimei_panel_data,
+	}
+};
+
+static int __init lcdc_chimei_lvds_panel_init(void)
+{
+	int ret;
+	struct msm_panel_info *pinfo;
+
+	if (msm_fb_detect_client("lcdc_chimei_wxga"))
+		return 0;
+
+	ret = platform_driver_register(&this_driver);
+	if (ret)
+		return ret;
+
+	pinfo = &chimei_panel_data.panel_info;
+	pinfo->xres = 1366;
+	pinfo->yres = 768;
+	MSM_FB_SINGLE_MODE_PANEL(pinfo);
+	pinfo->type = LCDC_PANEL;
+	pinfo->pdest = DISPLAY_1;
+	pinfo->wait_cycle = 0;
+	pinfo->bpp = 18;
+	pinfo->fb_num = 2;
+	pinfo->clk_rate = 69300000;
+	pinfo->bl_max = PWM_LEVEL;
+	pinfo->bl_min = 1;
+
+	/*
+	 * this panel is operated by de,
+	 * vsycn and hsync are ignored
+	 */
+	pinfo->lcdc.h_back_porch = 108;
+	pinfo->lcdc.h_front_porch = 0;
+	pinfo->lcdc.h_pulse_width = 1;
+	pinfo->lcdc.v_back_porch = 0;
+	pinfo->lcdc.v_front_porch = 16;
+	pinfo->lcdc.v_pulse_width = 1;
+	pinfo->lcdc.border_clr = 0;
+	pinfo->lcdc.underflow_clr = 0xff;
+	pinfo->lcdc.hsync_skew = 0;
+
+	ret = platform_device_register(&this_device);
+	if (ret)
+		platform_driver_unregister(&this_driver);
+
+	return ret;
+}
+
+module_init(lcdc_chimei_lvds_panel_init);
diff --git a/drivers/video/msm/lcdc_external.c b/drivers/video/msm/lcdc_external.c
new file mode 100644
index 0000000..ca82def
--- /dev/null
+++ b/drivers/video/msm/lcdc_external.c
@@ -0,0 +1,51 @@
+/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_fb.h"
+
+static int __init lcdc_external_init(void)
+{
+	int ret;
+	struct msm_panel_info pinfo;
+
+	if (msm_fb_detect_client("lcdc_external"))
+		return 0;
+
+	pinfo.xres = 1280;
+	pinfo.yres = 720;
+	MSM_FB_SINGLE_MODE_PANEL(&pinfo);
+	pinfo.type = LCDC_PANEL;
+	pinfo.pdest = DISPLAY_1;
+	pinfo.wait_cycle = 0;
+	pinfo.bpp = 24;
+	pinfo.fb_num = 2;
+	pinfo.clk_rate = 74250000;
+
+	pinfo.lcdc.h_back_porch = 124;
+	pinfo.lcdc.h_front_porch = 110;
+	pinfo.lcdc.h_pulse_width = 136;
+	pinfo.lcdc.v_back_porch = 19;
+	pinfo.lcdc.v_front_porch = 5;
+	pinfo.lcdc.v_pulse_width = 6;
+	pinfo.lcdc.border_clr = 0;	/* blk */
+	pinfo.lcdc.underflow_clr = 0xff;	/* blue */
+	pinfo.lcdc.hsync_skew = 0;
+
+	ret = lcdc_device_register(&pinfo);
+	if (ret)
+		printk(KERN_ERR "%s: failed to register device!\n", __func__);
+
+	return ret;
+}
+
+module_init(lcdc_external_init);
diff --git a/drivers/video/msm/lcdc_gordon.c b/drivers/video/msm/lcdc_gordon.c
new file mode 100644
index 0000000..b675787
--- /dev/null
+++ b/drivers/video/msm/lcdc_gordon.c
@@ -0,0 +1,457 @@
+/* Copyright (c) 2009-2010, 2012 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include "msm_fb.h"
+
+/* registers */
+#define GORDON_REG_NOP          0x00
+#define GORDON_REG_IMGCTL1      0x10
+#define GORDON_REG_IMGCTL2      0x11
+#define GORDON_REG_IMGSET1      0x12
+#define GORDON_REG_IMGSET2      0x13
+#define GORDON_REG_IVBP1        0x14
+#define GORDON_REG_IHBP1        0x15
+#define GORDON_REG_IVNUM1       0x16
+#define GORDON_REG_IHNUM1       0x17
+#define GORDON_REG_IVBP2        0x18
+#define GORDON_REG_IHBP2        0x19
+#define GORDON_REG_IVNUM2       0x1A
+#define GORDON_REG_IHNUM2       0x1B
+#define GORDON_REG_LCDIFCTL1    0x30
+#define GORDON_REG_VALTRAN      0x31
+#define GORDON_REG_AVCTL        0x33
+#define GORDON_REG_LCDIFCTL2    0x34
+#define GORDON_REG_LCDIFCTL3    0x35
+#define GORDON_REG_LCDIFSET1    0x36
+#define GORDON_REG_PCCTL        0x3C
+#define GORDON_REG_TPARAM1      0x40
+#define GORDON_REG_TLCDIF1      0x41
+#define GORDON_REG_TSSPB_ST1    0x42
+#define GORDON_REG_TSSPB_ED1    0x43
+#define GORDON_REG_TSCK_ST1     0x44
+#define GORDON_REG_TSCK_WD1     0x45
+#define GORDON_REG_TGSPB_VST1   0x46
+#define GORDON_REG_TGSPB_VED1   0x47
+#define GORDON_REG_TGSPB_CH1    0x48
+#define GORDON_REG_TGCK_ST1     0x49
+#define GORDON_REG_TGCK_ED1     0x4A
+#define GORDON_REG_TPCTL_ST1    0x4B
+#define GORDON_REG_TPCTL_ED1    0x4C
+#define GORDON_REG_TPCHG_ED1    0x4D
+#define GORDON_REG_TCOM_CH1     0x4E
+#define GORDON_REG_THBP1        0x4F
+#define GORDON_REG_TPHCTL1      0x50
+#define GORDON_REG_EVPH1        0x51
+#define GORDON_REG_EVPL1        0x52
+#define GORDON_REG_EVNH1        0x53
+#define GORDON_REG_EVNL1        0x54
+#define GORDON_REG_TBIAS1       0x55
+#define GORDON_REG_TPARAM2      0x56
+#define GORDON_REG_TLCDIF2      0x57
+#define GORDON_REG_TSSPB_ST2    0x58
+#define GORDON_REG_TSSPB_ED2    0x59
+#define GORDON_REG_TSCK_ST2     0x5A
+#define GORDON_REG_TSCK_WD2     0x5B
+#define GORDON_REG_TGSPB_VST2   0x5C
+#define GORDON_REG_TGSPB_VED2   0x5D
+#define GORDON_REG_TGSPB_CH2    0x5E
+#define GORDON_REG_TGCK_ST2     0x5F
+#define GORDON_REG_TGCK_ED2     0x60
+#define GORDON_REG_TPCTL_ST2    0x61
+#define GORDON_REG_TPCTL_ED2    0x62
+#define GORDON_REG_TPCHG_ED2    0x63
+#define GORDON_REG_TCOM_CH2     0x64
+#define GORDON_REG_THBP2        0x65
+#define GORDON_REG_TPHCTL2      0x66
+#define GORDON_REG_POWCTL       0x80
+
+static int lcdc_gordon_panel_off(struct platform_device *pdev);
+
+static int spi_cs;
+static int spi_sclk;
+static int spi_sdo;
+static int spi_sdi;
+static int spi_dac;
+static int bl_level;
+static unsigned char bit_shift[8] = { (1 << 7),	/* MSB */
+	(1 << 6),
+	(1 << 5),
+	(1 << 4),
+	(1 << 3),
+	(1 << 2),
+	(1 << 1),
+	(1 << 0)		               /* LSB */
+};
+
+struct gordon_state_type{
+	boolean disp_initialized;
+	boolean display_on;
+	boolean disp_powered_up;
+};
+
+static struct gordon_state_type gordon_state = { 0 };
+static struct msm_panel_common_pdata *lcdc_gordon_pdata;
+
+static void serigo(uint16 reg, uint8 data)
+{
+	unsigned int tx_val = ((0x00FF & reg) << 8) | data;
+	unsigned char i, val = 0;
+
+	/* Enable the Chip Select */
+	gpio_set_value(spi_cs, 1);
+	udelay(33);
+
+	/* Transmit it in two parts, Higher Byte first, then Lower Byte */
+	val = (unsigned char)((tx_val & 0xFF00) >> 8);
+
+	/* Clock should be Low before entering ! */
+	for (i = 0; i < 8; i++) {
+		/* #1: Drive the Data (High or Low) */
+		if (val & bit_shift[i])
+			gpio_set_value(spi_sdi, 1);
+		else
+			gpio_set_value(spi_sdi, 0);
+
+		/* #2: Drive the Clk High and then Low */
+		udelay(33);
+		gpio_set_value(spi_sclk, 1);
+		udelay(33);
+		gpio_set_value(spi_sclk, 0);
+	}
+
+	/* Idle state of SDO (MOSI) is Low */
+	gpio_set_value(spi_sdi, 0);
+	/* ..then Lower Byte */
+	val = (uint8) (tx_val & 0x00FF);
+	/* Before we enter here the Clock should be Low ! */
+
+	for (i = 0; i < 8; i++) {
+		/* #1: Drive the Data (High or Low) */
+		if (val & bit_shift[i])
+			gpio_set_value(spi_sdi, 1);
+		else
+			gpio_set_value(spi_sdi, 0);
+
+		/* #2: Drive the Clk High and then Low */
+		udelay(33);
+
+		gpio_set_value(spi_sclk, 1);
+		udelay(33);
+		gpio_set_value(spi_sclk, 0);
+	}
+
+	/* Idle state of SDO (MOSI) is Low */
+	gpio_set_value(spi_sdi, 0);
+
+	/* Now Disable the Chip Select */
+	udelay(33);
+	gpio_set_value(spi_cs, 0);
+}
+
+static void spi_init(void)
+{
+	/* Setting the Default GPIO's */
+	spi_sclk = *(lcdc_gordon_pdata->gpio_num);
+	spi_cs   = *(lcdc_gordon_pdata->gpio_num + 1);
+	spi_sdi  = *(lcdc_gordon_pdata->gpio_num + 2);
+	spi_sdo  = *(lcdc_gordon_pdata->gpio_num + 3);
+
+	/* Set the output so that we dont disturb the slave device */
+	gpio_set_value(spi_sclk, 0);
+	gpio_set_value(spi_sdi, 0);
+
+	/* Set the Chip Select De-asserted */
+	gpio_set_value(spi_cs, 0);
+
+}
+
+static void gordon_disp_powerup(void)
+{
+	if (!gordon_state.disp_powered_up && !gordon_state.display_on) {
+		/* Reset the hardware first */
+		/* Include DAC power up implementation here */
+	      gordon_state.disp_powered_up = TRUE;
+	}
+}
+
+static void gordon_init(void)
+{
+	/* Image interface settings */
+	serigo(GORDON_REG_IMGCTL2, 0x00);
+	serigo(GORDON_REG_IMGSET1, 0x00);
+
+	/* Exchange the RGB signal for J510(Softbank mobile) */
+	serigo(GORDON_REG_IMGSET2, 0x12);
+	serigo(GORDON_REG_LCDIFSET1, 0x00);
+
+	/* Pre-charge settings */
+	serigo(GORDON_REG_PCCTL, 0x09);
+	serigo(GORDON_REG_LCDIFCTL2, 0x7B);
+
+	mdelay(1);
+}
+
+static void gordon_disp_on(void)
+{
+	if (gordon_state.disp_powered_up && !gordon_state.display_on) {
+		gordon_init();
+		mdelay(20);
+		/* gordon_dispmode setting */
+		serigo(GORDON_REG_TPARAM1, 0x30);
+		serigo(GORDON_REG_TLCDIF1, 0x00);
+		serigo(GORDON_REG_TSSPB_ST1, 0x8B);
+		serigo(GORDON_REG_TSSPB_ED1, 0x93);
+		serigo(GORDON_REG_TSCK_ST1, 0x88);
+		serigo(GORDON_REG_TSCK_WD1, 0x00);
+		serigo(GORDON_REG_TGSPB_VST1, 0x01);
+		serigo(GORDON_REG_TGSPB_VED1, 0x02);
+		serigo(GORDON_REG_TGSPB_CH1, 0x5E);
+		serigo(GORDON_REG_TGCK_ST1, 0x80);
+		serigo(GORDON_REG_TGCK_ED1, 0x3C);
+		serigo(GORDON_REG_TPCTL_ST1, 0x50);
+		serigo(GORDON_REG_TPCTL_ED1, 0x74);
+		serigo(GORDON_REG_TPCHG_ED1, 0x78);
+		serigo(GORDON_REG_TCOM_CH1, 0x50);
+		serigo(GORDON_REG_THBP1, 0x84);
+		serigo(GORDON_REG_TPHCTL1, 0x00);
+		serigo(GORDON_REG_EVPH1, 0x70);
+		serigo(GORDON_REG_EVPL1, 0x64);
+		serigo(GORDON_REG_EVNH1, 0x56);
+		serigo(GORDON_REG_EVNL1, 0x48);
+		serigo(GORDON_REG_TBIAS1, 0x88);
+
+		/* QVGA settings */
+		serigo(GORDON_REG_TPARAM2, 0x28);
+		serigo(GORDON_REG_TLCDIF2, 0x14);
+		serigo(GORDON_REG_TSSPB_ST2, 0x49);
+		serigo(GORDON_REG_TSSPB_ED2, 0x4B);
+		serigo(GORDON_REG_TSCK_ST2, 0x4A);
+		serigo(GORDON_REG_TSCK_WD2, 0x02);
+		serigo(GORDON_REG_TGSPB_VST2, 0x02);
+		serigo(GORDON_REG_TGSPB_VED2, 0x03);
+		serigo(GORDON_REG_TGSPB_CH2, 0x2F);
+		serigo(GORDON_REG_TGCK_ST2, 0x40);
+		serigo(GORDON_REG_TGCK_ED2, 0x1E);
+		serigo(GORDON_REG_TPCTL_ST2, 0x2C);
+		serigo(GORDON_REG_TPCTL_ED2, 0x3A);
+		serigo(GORDON_REG_TPCHG_ED2, 0x3C);
+		serigo(GORDON_REG_TCOM_CH2, 0x28);
+		serigo(GORDON_REG_THBP2, 0x4D);
+		serigo(GORDON_REG_TPHCTL2, 0x1A);
+
+		/* VGA settings */
+		serigo(GORDON_REG_IVBP1, 0x02);
+		serigo(GORDON_REG_IHBP1, 0x90);
+		serigo(GORDON_REG_IVNUM1, 0xA0);
+		serigo(GORDON_REG_IHNUM1, 0x78);
+
+		/* QVGA settings */
+		serigo(GORDON_REG_IVBP2, 0x02);
+		serigo(GORDON_REG_IHBP2, 0x48);
+		serigo(GORDON_REG_IVNUM2, 0x50);
+		serigo(GORDON_REG_IHNUM2, 0x3C);
+
+		/* Gordon Charge pump settings and ON */
+		serigo(GORDON_REG_POWCTL, 0x03);
+		mdelay(15);
+		serigo(GORDON_REG_POWCTL, 0x07);
+		mdelay(15);
+
+		serigo(GORDON_REG_POWCTL, 0x0F);
+		mdelay(15);
+
+		serigo(GORDON_REG_AVCTL, 0x03);
+		mdelay(15);
+
+		serigo(GORDON_REG_POWCTL, 0x1F);
+		mdelay(15);
+
+		serigo(GORDON_REG_POWCTL, 0x5F);
+		mdelay(15);
+
+		serigo(GORDON_REG_POWCTL, 0x7F);
+		mdelay(15);
+
+		serigo(GORDON_REG_LCDIFCTL1, 0x02);
+		mdelay(15);
+
+		serigo(GORDON_REG_IMGCTL1, 0x00);
+		mdelay(15);
+
+		serigo(GORDON_REG_LCDIFCTL3, 0x00);
+		mdelay(15);
+
+		serigo(GORDON_REG_VALTRAN, 0x01);
+		mdelay(15);
+
+		serigo(GORDON_REG_LCDIFCTL1, 0x03);
+		mdelay(1);
+		gordon_state.display_on = TRUE;
+	}
+}
+
+static int lcdc_gordon_panel_on(struct platform_device *pdev)
+{
+	if (!gordon_state.disp_initialized) {
+		/* Configure reset GPIO that drives DAC */
+		lcdc_gordon_pdata->panel_config_gpio(1);
+		spi_dac = *(lcdc_gordon_pdata->gpio_num + 4);
+		gpio_set_value(spi_dac, 0);
+		udelay(15);
+		gpio_set_value(spi_dac, 1);
+		spi_init();	/* LCD needs SPI */
+		gordon_disp_powerup();
+		gordon_disp_on();
+		if (bl_level <= 1) {
+			/* keep back light OFF */
+			serigo(GORDON_REG_LCDIFCTL2, 0x0B);
+			udelay(15);
+			serigo(GORDON_REG_VALTRAN, 0x01);
+		} else {
+			/* keep back light ON */
+			serigo(GORDON_REG_LCDIFCTL2, 0x7B);
+			udelay(15);
+			serigo(GORDON_REG_VALTRAN, 0x01);
+		}
+		gordon_state.disp_initialized = TRUE;
+	}
+	return 0;
+}
+
+static int lcdc_gordon_panel_off(struct platform_device *pdev)
+{
+	if (gordon_state.disp_powered_up && gordon_state.display_on) {
+		serigo(GORDON_REG_LCDIFCTL2, 0x7B);
+		serigo(GORDON_REG_VALTRAN, 0x01);
+		serigo(GORDON_REG_LCDIFCTL1, 0x02);
+		serigo(GORDON_REG_LCDIFCTL3, 0x01);
+		mdelay(20);
+		serigo(GORDON_REG_VALTRAN, 0x01);
+		serigo(GORDON_REG_IMGCTL1, 0x01);
+		serigo(GORDON_REG_LCDIFCTL1, 0x00);
+		mdelay(20);
+
+		serigo(GORDON_REG_POWCTL, 0x1F);
+		mdelay(40);
+
+		serigo(GORDON_REG_POWCTL, 0x07);
+		mdelay(40);
+
+		serigo(GORDON_REG_POWCTL, 0x03);
+		mdelay(40);
+
+		serigo(GORDON_REG_POWCTL, 0x00);
+		mdelay(40);
+		lcdc_gordon_pdata->panel_config_gpio(0);
+		gordon_state.display_on = FALSE;
+		gordon_state.disp_initialized = FALSE;
+	}
+	return 0;
+}
+
+static void lcdc_gordon_set_backlight(struct msm_fb_data_type *mfd)
+{
+		bl_level = mfd->bl_level;
+
+		if (gordon_state.disp_initialized) {
+			if (bl_level <= 1) {
+				/* keep back light OFF */
+				serigo(GORDON_REG_LCDIFCTL2, 0x0B);
+				udelay(15);
+				serigo(GORDON_REG_VALTRAN, 0x01);
+			} else {
+				/* keep back light ON */
+				serigo(GORDON_REG_LCDIFCTL2, 0x7B);
+				udelay(15);
+				serigo(GORDON_REG_VALTRAN, 0x01);
+			}
+		}
+}
+
+static int __devinit gordon_probe(struct platform_device *pdev)
+{
+	if (pdev->id == 0) {
+		lcdc_gordon_pdata = pdev->dev.platform_data;
+		return 0;
+	}
+	msm_fb_add_device(pdev);
+	return 0;
+}
+
+static struct platform_driver this_driver = {
+	.probe  = gordon_probe,
+	.driver = {
+		.name   = "lcdc_gordon_vga",
+	},
+};
+
+static struct msm_fb_panel_data gordon_panel_data = {
+	.on = lcdc_gordon_panel_on,
+	.off = lcdc_gordon_panel_off,
+	.set_backlight = lcdc_gordon_set_backlight,
+};
+
+static struct platform_device this_device = {
+	.name   = "lcdc_gordon_vga",
+	.id	= 1,
+	.dev	= {
+		.platform_data = &gordon_panel_data,
+	}
+};
+
+static int __init lcdc_gordon_panel_init(void)
+{
+	int ret;
+	struct msm_panel_info *pinfo;
+
+#ifdef CONFIG_FB_MSM_TRY_MDDI_CATCH_LCDC_PRISM
+	if (msm_fb_detect_client("lcdc_gordon_vga"))
+		return 0;
+#endif
+	ret = platform_driver_register(&this_driver);
+	if (ret)
+		return ret;
+
+	pinfo = &gordon_panel_data.panel_info;
+	pinfo->xres = 480;
+	pinfo->yres = 640;
+	MSM_FB_SINGLE_MODE_PANEL(pinfo);
+	pinfo->type = LCDC_PANEL;
+	pinfo->pdest = DISPLAY_1;
+	pinfo->wait_cycle = 0;
+	pinfo->bpp = 24;
+	pinfo->fb_num = 2;
+	pinfo->clk_rate = 24500000;
+	pinfo->bl_max = 4;
+	pinfo->bl_min = 1;
+
+	pinfo->lcdc.h_back_porch = 84;
+	pinfo->lcdc.h_front_porch = 33;
+	pinfo->lcdc.h_pulse_width = 60;
+	pinfo->lcdc.v_back_porch = 0;
+	pinfo->lcdc.v_front_porch = 2;
+	pinfo->lcdc.v_pulse_width = 2;
+	pinfo->lcdc.border_clr = 0;     /* blk */
+	pinfo->lcdc.underflow_clr = 0xff;       /* blue */
+	pinfo->lcdc.hsync_skew = 0;
+
+	ret = platform_device_register(&this_device);
+	if (ret)
+		platform_driver_unregister(&this_driver);
+
+	return ret;
+}
+
+module_init(lcdc_gordon_panel_init);
diff --git a/drivers/video/msm/lcdc_nt35582_wvga.c b/drivers/video/msm/lcdc_nt35582_wvga.c
new file mode 100644
index 0000000..9ecf4b9
--- /dev/null
+++ b/drivers/video/msm/lcdc_nt35582_wvga.c
@@ -0,0 +1,472 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#ifdef CONFIG_SPI_QUP
+#include <linux/spi/spi.h>
+#endif
+#include <mach/gpio.h>
+#include <mach/pmic.h>
+#include "msm_fb.h"
+
+#define LCDC_NT35582_PANEL_NAME		"lcdc_nt35582_wvga"
+
+#define WRITE_FIRST_TRANS	0x20
+#define WRITE_SECOND_TRANS	0x00
+#define WRITE_THIRD_TRANS	0x40
+#define READ_FIRST_TRANS	0x20
+#define READ_SECOND_TRANS	0x00
+#define READ_THIRD_TRANS	0xC0
+
+#ifdef CONFIG_SPI_QUP
+#define LCDC_NT35582_SPI_DEVICE_NAME		"lcdc_nt35582_spi"
+static struct spi_device *spi_client;
+#endif
+
+struct nt35582_state_type {
+	boolean display_on;
+	int bl_level;
+};
+
+static struct nt35582_state_type nt35582_state = { 0 };
+static int gpio_backlight_en;
+static struct msm_panel_common_pdata *lcdc_nt35582_pdata;
+
+static int spi_write_2bytes(struct spi_device *spi,
+	unsigned char reg_high_addr, unsigned char reg_low_addr)
+{
+	char tx_buf[4];
+	int rc;
+	struct spi_message m;
+	struct spi_transfer t;
+
+	memset(&t, 0, sizeof t);
+	t.tx_buf = tx_buf;
+
+	spi_setup(spi);
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+
+	tx_buf[0] = WRITE_FIRST_TRANS;
+	tx_buf[1] = reg_high_addr;
+	tx_buf[2] = WRITE_SECOND_TRANS;
+	tx_buf[3] = reg_low_addr;
+	t.rx_buf = NULL;
+	t.len = 4;
+	t.bits_per_word = 16;
+	rc = spi_sync(spi, &m);
+	if (rc)
+		pr_err("write spi command failed!\n");
+
+	return rc;
+}
+
+static int spi_write_3bytes(struct spi_device *spi, unsigned char reg_high_addr,
+	unsigned char reg_low_addr, unsigned char write_data)
+{
+	char tx_buf[6];
+	int rc;
+	struct spi_message  m;
+	struct spi_transfer t;
+
+	memset(&t, 0, sizeof t);
+	t.tx_buf = tx_buf;
+
+	spi_setup(spi);
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+
+	tx_buf[0] = WRITE_FIRST_TRANS;
+	tx_buf[1] = reg_high_addr;
+	tx_buf[2] = WRITE_SECOND_TRANS;
+	tx_buf[3] = reg_low_addr;
+	tx_buf[4] = WRITE_THIRD_TRANS;
+	tx_buf[5] = write_data;
+	t.rx_buf = NULL;
+	t.len = 6;
+	t.bits_per_word = 16;
+	rc = spi_sync(spi, &m);
+
+	if (rc)
+		pr_err("write spi command failed!\n");
+
+	return rc;
+}
+
+static int spi_read_bytes(struct spi_device *spi, unsigned char reg_high_addr,
+	unsigned char reg_low_addr, unsigned char *read_value)
+{
+	char tx_buf[6];
+	char rx_buf[6];
+	int rc;
+	struct spi_message m;
+	struct spi_transfer t;
+
+	memset(&t, 0, sizeof t);
+	t.tx_buf = tx_buf;
+
+	spi_setup(spi);
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+
+	tx_buf[0] = READ_FIRST_TRANS;
+	tx_buf[1] = reg_high_addr;
+	tx_buf[2] = READ_SECOND_TRANS;
+	tx_buf[3] = reg_low_addr;
+	tx_buf[4] = READ_THIRD_TRANS;
+	tx_buf[5] = 0x00;
+
+	t.rx_buf = rx_buf;
+	t.len = 6;
+	t.bits_per_word = 16;
+	rc = spi_sync(spi, &m);
+
+	if (rc)
+		pr_err("write spi command failed!\n");
+	else
+		*read_value = rx_buf[5];
+
+	return rc;
+}
+
+static void nt35582_disp_on(void)
+{
+	uint32 panel_id1 = 0, panel_id2 = 0;
+
+	if (!nt35582_state.display_on) {
+
+		/* GVDD setting */
+		spi_write_3bytes(spi_client, 0xC0, 0x00, 0xC0);
+		spi_write_3bytes(spi_client, 0xC0, 0x01, 0x00);
+		spi_write_3bytes(spi_client, 0xC0, 0x02, 0xC0);
+		spi_write_3bytes(spi_client, 0xC0, 0x03, 0x00);
+		/* Power setting */
+		spi_write_3bytes(spi_client, 0xC1, 0x00, 0x40);
+		spi_write_3bytes(spi_client, 0xC2, 0x00, 0x21);
+		spi_write_3bytes(spi_client, 0xC2, 0x02, 0x02);
+
+		/* Gamma setting */
+		spi_write_3bytes(spi_client, 0xE0, 0x00, 0x0E);
+		spi_write_3bytes(spi_client, 0xE0, 0x01, 0x54);
+		spi_write_3bytes(spi_client, 0xE0, 0x02, 0x63);
+		spi_write_3bytes(spi_client, 0xE0, 0x03, 0x76);
+		spi_write_3bytes(spi_client, 0xE0, 0x04, 0x1F);
+		spi_write_3bytes(spi_client, 0xE0, 0x05, 0x31);
+		spi_write_3bytes(spi_client, 0xE0, 0x06, 0x62);
+		spi_write_3bytes(spi_client, 0xE0, 0x07, 0x78);
+		spi_write_3bytes(spi_client, 0xE0, 0x08, 0x1F);
+		spi_write_3bytes(spi_client, 0xE0, 0x09, 0x25);
+		spi_write_3bytes(spi_client, 0xE0, 0x0A, 0xB3);
+		spi_write_3bytes(spi_client, 0xE0, 0x0B, 0x17);
+		spi_write_3bytes(spi_client, 0xE0, 0x0C, 0x38);
+		spi_write_3bytes(spi_client, 0xE0, 0x0D, 0x5A);
+		spi_write_3bytes(spi_client, 0xE0, 0x0E, 0xA2);
+		spi_write_3bytes(spi_client, 0xE0, 0x0F, 0xA2);
+		spi_write_3bytes(spi_client, 0xE0, 0x10, 0x24);
+		spi_write_3bytes(spi_client, 0xE0, 0x11, 0x57);
+
+		spi_write_3bytes(spi_client, 0xE1, 0x00, 0x0E);
+		spi_write_3bytes(spi_client, 0xE1, 0x01, 0x54);
+		spi_write_3bytes(spi_client, 0xE1, 0x02, 0x63);
+		spi_write_3bytes(spi_client, 0xE1, 0x03, 0x76);
+		spi_write_3bytes(spi_client, 0xE1, 0x04, 0x1F);
+		spi_write_3bytes(spi_client, 0xE1, 0x05, 0x31);
+		spi_write_3bytes(spi_client, 0xE1, 0x06, 0X62);
+		spi_write_3bytes(spi_client, 0xE1, 0x07, 0x78);
+		spi_write_3bytes(spi_client, 0xE1, 0x08, 0x1F);
+		spi_write_3bytes(spi_client, 0xE1, 0x09, 0x25);
+		spi_write_3bytes(spi_client, 0xE1, 0x0A, 0xB3);
+		spi_write_3bytes(spi_client, 0xE1, 0x0B, 0x17);
+		spi_write_3bytes(spi_client, 0xE1, 0x0C, 0x38);
+		spi_write_3bytes(spi_client, 0xE1, 0x0D, 0x5A);
+		spi_write_3bytes(spi_client, 0xE1, 0x0E, 0xA2);
+		spi_write_3bytes(spi_client, 0xE1, 0x0F, 0xA2);
+		spi_write_3bytes(spi_client, 0xE1, 0x10, 0x24);
+		spi_write_3bytes(spi_client, 0xE1, 0x11, 0x57);
+
+		spi_write_3bytes(spi_client, 0xE2, 0x00, 0x0E);
+		spi_write_3bytes(spi_client, 0xE2, 0x01, 0x54);
+		spi_write_3bytes(spi_client, 0xE2, 0x02, 0x63);
+		spi_write_3bytes(spi_client, 0xE2, 0x03, 0x76);
+		spi_write_3bytes(spi_client, 0xE2, 0x04, 0x1F);
+		spi_write_3bytes(spi_client, 0xE2, 0x05, 0x31);
+		spi_write_3bytes(spi_client, 0xE2, 0x06, 0x62);
+		spi_write_3bytes(spi_client, 0xE2, 0x07, 0x78);
+		spi_write_3bytes(spi_client, 0xE2, 0x08, 0x1F);
+		spi_write_3bytes(spi_client, 0xE2, 0x09, 0x25);
+		spi_write_3bytes(spi_client, 0xE2, 0x0A, 0xB3);
+		spi_write_3bytes(spi_client, 0xE2, 0x0B, 0x17);
+		spi_write_3bytes(spi_client, 0xE2, 0x0C, 0x38);
+		spi_write_3bytes(spi_client, 0xE2, 0x0D, 0x5A);
+		spi_write_3bytes(spi_client, 0xE2, 0x0E, 0xA2);
+		spi_write_3bytes(spi_client, 0xE2, 0x0F, 0xA2);
+		spi_write_3bytes(spi_client, 0xE2, 0x10, 0x24);
+		spi_write_3bytes(spi_client, 0xE2, 0x11, 0x57);
+
+		spi_write_3bytes(spi_client, 0xE3, 0x00, 0x0E);
+		spi_write_3bytes(spi_client, 0xE3, 0x01, 0x54);
+		spi_write_3bytes(spi_client, 0xE3, 0x02, 0x63);
+		spi_write_3bytes(spi_client, 0xE3, 0x03, 0x76);
+		spi_write_3bytes(spi_client, 0xE3, 0x04, 0x1F);
+		spi_write_3bytes(spi_client, 0xE3, 0x05, 0x31);
+		spi_write_3bytes(spi_client, 0xE3, 0x06, 0x62);
+		spi_write_3bytes(spi_client, 0xE3, 0x07, 0x78);
+		spi_write_3bytes(spi_client, 0xE3, 0x08, 0x1F);
+		spi_write_3bytes(spi_client, 0xE3, 0x09, 0x25);
+		spi_write_3bytes(spi_client, 0xE3, 0x0A, 0xB3);
+		spi_write_3bytes(spi_client, 0xE3, 0x0B, 0x17);
+		spi_write_3bytes(spi_client, 0xE3, 0x0C, 0x38);
+		spi_write_3bytes(spi_client, 0xE3, 0x0D, 0x5A);
+		spi_write_3bytes(spi_client, 0xE3, 0x0E, 0xA2);
+		spi_write_3bytes(spi_client, 0xE3, 0x0F, 0xA2);
+		spi_write_3bytes(spi_client, 0xE3, 0x10, 0x24);
+		spi_write_3bytes(spi_client, 0xE3, 0x11, 0x57);
+
+		spi_write_3bytes(spi_client, 0xE4, 0x00, 0x48);
+		spi_write_3bytes(spi_client, 0xE4, 0x01, 0x6B);
+		spi_write_3bytes(spi_client, 0xE4, 0x02, 0x84);
+		spi_write_3bytes(spi_client, 0xE4, 0x03, 0x9B);
+		spi_write_3bytes(spi_client, 0xE4, 0x04, 0x1F);
+		spi_write_3bytes(spi_client, 0xE4, 0x05, 0x31);
+		spi_write_3bytes(spi_client, 0xE4, 0x06, 0x62);
+		spi_write_3bytes(spi_client, 0xE4, 0x07, 0x78);
+		spi_write_3bytes(spi_client, 0xE4, 0x08, 0x1F);
+		spi_write_3bytes(spi_client, 0xE4, 0x09, 0x25);
+		spi_write_3bytes(spi_client, 0xE4, 0x0A, 0xB3);
+		spi_write_3bytes(spi_client, 0xE4, 0x0B, 0x17);
+		spi_write_3bytes(spi_client, 0xE4, 0x0C, 0x38);
+		spi_write_3bytes(spi_client, 0xE4, 0x0D, 0x5A);
+		spi_write_3bytes(spi_client, 0xE4, 0x0E, 0xA2);
+		spi_write_3bytes(spi_client, 0xE4, 0x0F, 0xA2);
+		spi_write_3bytes(spi_client, 0xE4, 0x10, 0x24);
+		spi_write_3bytes(spi_client, 0xE4, 0x11, 0x57);
+
+		spi_write_3bytes(spi_client, 0xE5, 0x00, 0x48);
+		spi_write_3bytes(spi_client, 0xE5, 0x01, 0x6B);
+		spi_write_3bytes(spi_client, 0xE5, 0x02, 0x84);
+		spi_write_3bytes(spi_client, 0xE5, 0x03, 0x9B);
+		spi_write_3bytes(spi_client, 0xE5, 0x04, 0x1F);
+		spi_write_3bytes(spi_client, 0xE5, 0x05, 0x31);
+		spi_write_3bytes(spi_client, 0xE5, 0x06, 0x62);
+		spi_write_3bytes(spi_client, 0xE5, 0x07, 0x78);
+		spi_write_3bytes(spi_client, 0xE5, 0x08, 0x1F);
+		spi_write_3bytes(spi_client, 0xE5, 0x09, 0x25);
+		spi_write_3bytes(spi_client, 0xE5, 0x0A, 0xB3);
+		spi_write_3bytes(spi_client, 0xE5, 0x0B, 0x17);
+		spi_write_3bytes(spi_client, 0xE5, 0x0C, 0x38);
+		spi_write_3bytes(spi_client, 0xE5, 0x0D, 0x5A);
+		spi_write_3bytes(spi_client, 0xE5, 0x0E, 0xA2);
+		spi_write_3bytes(spi_client, 0xE5, 0x0F, 0xA2);
+		spi_write_3bytes(spi_client, 0xE5, 0x10, 0x24);
+		spi_write_3bytes(spi_client, 0xE5, 0x11, 0x57);
+
+		/* Data format setting */
+		spi_write_3bytes(spi_client, 0x3A, 0x00, 0x70);
+
+		/* Reverse PCLK signal of LCM to meet Qualcomm's platform */
+		spi_write_3bytes(spi_client, 0x3B, 0x00, 0x2B);
+
+		/* Scan direstion setting */
+		spi_write_3bytes(spi_client, 0x36, 0x00, 0x00);
+
+		/* Sleep out */
+		spi_write_2bytes(spi_client, 0x11, 0x00);
+
+		msleep(120);
+
+		/* Display on */
+		spi_write_2bytes(spi_client, 0x29, 0x00);
+
+		pr_info("%s: LCM SPI display on CMD finished...\n", __func__);
+
+		msleep(200);
+
+		nt35582_state.display_on = TRUE;
+	}
+
+	/* Test to read RDDID. It should be 0x0055h and 0x0082h */
+	spi_read_bytes(spi_client, 0x10, 0x80, (unsigned char *)&panel_id1);
+	spi_read_bytes(spi_client, 0x11, 0x80, (unsigned char *)&panel_id2);
+
+	pr_info(KERN_INFO "nt35582_disp_on: LCM_ID=[0x%x, 0x%x]\n",
+		panel_id1, panel_id2);
+}
+
+static int lcdc_nt35582_panel_on(struct platform_device *pdev)
+{
+	nt35582_disp_on();
+	return 0;
+}
+
+static int lcdc_nt35582_panel_off(struct platform_device *pdev)
+{
+	nt35582_state.display_on = FALSE;
+	return 0;
+}
+
+static void lcdc_nt35582_set_backlight(struct msm_fb_data_type *mfd)
+{
+	int bl_level;
+	int i = 0, step = 0;
+
+	bl_level = mfd->bl_level;
+	if (bl_level == nt35582_state.bl_level)
+		return;
+	else
+		nt35582_state.bl_level = bl_level;
+
+	if (bl_level == 0) {
+		gpio_set_value_cansleep(gpio_backlight_en, 0);
+		return;
+	}
+
+	/* Level:0~31 mapping to step 32~1 */
+	step = 32 - bl_level;
+	for (i = 0; i < step; i++) {
+		gpio_set_value_cansleep(gpio_backlight_en, 0);
+		ndelay(5);
+		gpio_set_value_cansleep(gpio_backlight_en, 1);
+		ndelay(5);
+	}
+}
+
+static int __devinit nt35582_probe(struct platform_device *pdev)
+{
+	if (pdev->id == 0) {
+		lcdc_nt35582_pdata = pdev->dev.platform_data;
+		return 0;
+	}
+
+	gpio_backlight_en = *(lcdc_nt35582_pdata->gpio_num);
+
+	msm_fb_add_device(pdev);
+	return 0;
+}
+
+#ifdef CONFIG_SPI_QUP
+static int __devinit lcdc_nt35582_spi_probe(struct spi_device *spi)
+{
+	spi_client = spi;
+	spi_client->bits_per_word = 16;
+	spi_client->chip_select = 0;
+	spi_client->max_speed_hz = 1100000;
+	spi_client->mode = SPI_MODE_0;
+	spi_setup(spi_client);
+
+	return 0;
+}
+
+static int __devexit lcdc_nt35582_spi_remove(struct spi_device *spi)
+{
+	spi_client = NULL;
+	return 0;
+}
+
+static struct spi_driver lcdc_nt35582_spi_driver = {
+	.driver = {
+		.name = LCDC_NT35582_SPI_DEVICE_NAME,
+		.owner = THIS_MODULE,
+	},
+	.probe = lcdc_nt35582_spi_probe,
+	.remove = __devexit_p(lcdc_nt35582_spi_remove),
+};
+#endif
+static struct platform_driver this_driver = {
+	.probe = nt35582_probe,
+	.driver = {
+		.name = LCDC_NT35582_PANEL_NAME,
+	},
+};
+
+static struct msm_fb_panel_data nt35582_panel_data = {
+	.on = lcdc_nt35582_panel_on,
+	.off = lcdc_nt35582_panel_off,
+	.set_backlight = lcdc_nt35582_set_backlight,
+};
+
+static struct platform_device this_device = {
+	.name = LCDC_NT35582_PANEL_NAME,
+	.id = 1,
+	.dev = {
+		.platform_data = &nt35582_panel_data,
+	}
+};
+
+static int __init lcdc_nt35582_panel_init(void)
+{
+	int ret;
+	struct msm_panel_info *pinfo;
+
+#ifdef CONFIG_FB_MSM_LCDC_AUTO_DETECT
+	if (msm_fb_detect_client(LCDC_NT35582_PANEL_NAME)) {
+		pr_err("detect failed\n");
+		return 0;
+	}
+#endif
+	ret = platform_driver_register(&this_driver);
+	if (ret) {
+		pr_err("Fails to platform_driver_register...\n");
+		return ret;
+	}
+
+	pinfo = &nt35582_panel_data.panel_info;
+	pinfo->xres = 480;
+	pinfo->yres = 800;
+	MSM_FB_SINGLE_MODE_PANEL(pinfo);
+	pinfo->type = LCDC_PANEL;
+	pinfo->pdest = DISPLAY_1;
+	pinfo->wait_cycle = 0;
+	pinfo->bpp = 24;
+	pinfo->fb_num = 2;
+	pinfo->clk_rate = 25600000;
+	pinfo->bl_max = 31;
+	pinfo->bl_min = 1;
+
+	pinfo->lcdc.h_back_porch = 10;	/* hsw = 8 + hbp=184 */
+	pinfo->lcdc.h_front_porch = 10;
+	pinfo->lcdc.h_pulse_width = 2;
+	pinfo->lcdc.v_back_porch = 4;	/* vsw=1 + vbp = 2 */
+	pinfo->lcdc.v_front_porch = 10;
+	pinfo->lcdc.v_pulse_width = 2;
+	pinfo->lcdc.border_clr = 0;	/* blk */
+	pinfo->lcdc.underflow_clr = 0xff;	/* blue */
+	pinfo->lcdc.hsync_skew = 0;
+
+	ret = platform_device_register(&this_device);
+	if (ret) {
+		pr_err("not able to register the device\n");
+		goto fail_driver;
+	}
+#ifdef CONFIG_SPI_QUP
+	ret = spi_register_driver(&lcdc_nt35582_spi_driver);
+
+	if (ret) {
+		pr_err("not able to register spi\n");
+		goto fail_device;
+	}
+#endif
+	return ret;
+
+#ifdef CONFIG_SPI_QUP
+fail_device:
+	platform_device_unregister(&this_device);
+#endif
+fail_driver:
+	platform_driver_unregister(&this_driver);
+	return ret;
+}
+
+device_initcall(lcdc_nt35582_panel_init);
diff --git a/drivers/video/msm/lcdc_panel.c b/drivers/video/msm/lcdc_panel.c
new file mode 100644
index 0000000..5705325
--- /dev/null
+++ b/drivers/video/msm/lcdc_panel.c
@@ -0,0 +1,84 @@
+/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_fb.h"
+
+static int lcdc_panel_on(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static int lcdc_panel_off(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static int __devinit lcdc_panel_probe(struct platform_device *pdev)
+{
+	msm_fb_add_device(pdev);
+
+	return 0;
+}
+
+static struct platform_driver this_driver = {
+	.probe  = lcdc_panel_probe,
+	.driver = {
+		.name   = "lcdc_panel",
+	},
+};
+
+static struct msm_fb_panel_data lcdc_panel_data = {
+	.on = lcdc_panel_on,
+	.off = lcdc_panel_off,
+};
+
+static int lcdc_dev_id;
+
+int lcdc_device_register(struct msm_panel_info *pinfo)
+{
+	struct platform_device *pdev = NULL;
+	int ret;
+
+	pdev = platform_device_alloc("lcdc_panel", ++lcdc_dev_id);
+	if (!pdev)
+		return -ENOMEM;
+
+	lcdc_panel_data.panel_info = *pinfo;
+	ret = platform_device_add_data(pdev, &lcdc_panel_data,
+		sizeof(lcdc_panel_data));
+	if (ret) {
+		printk(KERN_ERR
+		  "%s: platform_device_add_data failed!\n", __func__);
+		goto err_device_put;
+	}
+
+	ret = platform_device_add(pdev);
+	if (ret) {
+		printk(KERN_ERR
+		  "%s: platform_device_register failed!\n", __func__);
+		goto err_device_put;
+	}
+
+	return 0;
+
+err_device_put:
+	platform_device_put(pdev);
+	return ret;
+}
+
+static int __init lcdc_panel_init(void)
+{
+	return platform_driver_register(&this_driver);
+}
+
+module_init(lcdc_panel_init);
diff --git a/drivers/video/msm/lcdc_prism.c b/drivers/video/msm/lcdc_prism.c
new file mode 100644
index 0000000..d127f63
--- /dev/null
+++ b/drivers/video/msm/lcdc_prism.c
@@ -0,0 +1,61 @@
+/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_fb.h"
+
+#ifdef CONFIG_FB_MSM_TRY_MDDI_CATCH_LCDC_PRISM
+#include "mddihosti.h"
+#endif
+
+static int __init lcdc_prism_init(void)
+{
+	int ret;
+	struct msm_panel_info pinfo;
+
+#ifdef CONFIG_FB_MSM_TRY_MDDI_CATCH_LCDC_PRISM
+	ret = msm_fb_detect_client("lcdc_prism_wvga");
+	if (ret == -ENODEV)
+		return 0;
+
+	if (ret && (mddi_get_client_id() != 0))
+		return 0;
+#endif
+
+	pinfo.xres = 800;
+	pinfo.yres = 480;
+	MSM_FB_SINGLE_MODE_PANEL(&pinfo);
+	pinfo.type = LCDC_PANEL;
+	pinfo.pdest = DISPLAY_1;
+	pinfo.wait_cycle = 0;
+	pinfo.bpp = 24;
+	pinfo.fb_num = 2;
+	pinfo.clk_rate = 30720000;
+
+	pinfo.lcdc.h_back_porch = 21;
+	pinfo.lcdc.h_front_porch = 81;
+	pinfo.lcdc.h_pulse_width = 60;
+	pinfo.lcdc.v_back_porch = 18;
+	pinfo.lcdc.v_front_porch = 27;
+	pinfo.lcdc.v_pulse_width = 2;
+	pinfo.lcdc.border_clr = 0;	/* blk */
+	pinfo.lcdc.underflow_clr = 0xff;	/* blue */
+	pinfo.lcdc.hsync_skew = 0;
+
+	ret = lcdc_device_register(&pinfo);
+	if (ret)
+		printk(KERN_ERR "%s: failed to register device!\n", __func__);
+
+	return ret;
+}
+
+module_init(lcdc_prism_init);
diff --git a/drivers/video/msm/lcdc_samsung_oled_pt.c b/drivers/video/msm/lcdc_samsung_oled_pt.c
new file mode 100644
index 0000000..16790f3
--- /dev/null
+++ b/drivers/video/msm/lcdc_samsung_oled_pt.c
@@ -0,0 +1,588 @@
+/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/pwm.h>
+#ifdef CONFIG_SPI_QUP
+#include <linux/spi/spi.h>
+#else
+#include <mach/gpio.h>
+#endif
+#include "msm_fb.h"
+
+#define DEBUG
+/* #define SYSFS_DEBUG_CMD */
+
+#ifdef CONFIG_SPI_QUP
+#define LCDC_SAMSUNG_SPI_DEVICE_NAME	"lcdc_samsung_ams367pe02"
+static struct spi_device *lcdc_spi_client;
+#else
+static int spi_cs;
+static int spi_sclk;
+static int spi_mosi;
+#endif
+
+struct samsung_state_type {
+	boolean disp_initialized;
+	boolean display_on;
+	boolean disp_powered_up;
+	int brightness;
+};
+
+struct samsung_spi_data {
+	u8 addr;
+	u8 len;
+	u8 data[22];
+};
+
+static struct samsung_spi_data panel_sequence[] = {
+	{ .addr = 0xf8, .len = 14, .data = { 0x01, 0x27, 0x27, 0x07, 0x07,
+	 0x54, 0x9f, 0x63, 0x86, 0x1a, 0x33, 0x0d, 0x00, 0x00 } },
+};
+static struct samsung_spi_data display_sequence[] = {
+	{ .addr = 0xf2, .len = 5, .data = { 0x02, 0x03, 0x1c, 0x10, 0x10 } },
+	{ .addr = 0xf7, .len = 3, .data = { 0x00, 0x00, 0x30 } },
+};
+
+/* lum=300 cd/m2 */
+static struct samsung_spi_data gamma_sequence_300[] = {
+	{ .addr = 0xfa, .len = 22, .data = { 0x02, 0x18, 0x08, 0x24, 0x7d, 0x77,
+	 0x5b, 0xbe, 0xc1, 0xb1, 0xb3, 0xb7, 0xa6, 0xc3, 0xc5, 0xb9, 0x00, 0xb3,
+	 0x00, 0xaf, 0x00, 0xe8 } },
+	{ .addr = 0xFA, .len = 1, .data = { 0x03 } },
+};
+/* lum = 180 cd/m2*/
+static struct samsung_spi_data gamma_sequence_180[] = {
+	{ .addr = 0xfa, .len = 22, .data = { 0x02, 0x18, 0x08, 0x24, 0x83, 0x78,
+	 0x60, 0xc5, 0xc6, 0xb8, 0xba, 0xbe, 0xad, 0xcb, 0xcd, 0xc2, 0x00, 0x92,
+	 0x00, 0x8e, 0x00, 0xbc } },
+	{ .addr = 0xFA, .len = 1, .data = { 0x03 } },
+};
+/* lum = 80 cd/m2*/
+static struct samsung_spi_data gamma_sequence_80[] = {
+	{ .addr = 0xfa, .len = 22, .data = { 0x02, 0x18, 0x08, 0x24, 0x94, 0x73,
+	 0x6c, 0xcb, 0xca, 0xbe, 0xc4, 0xc7, 0xb8, 0xd3, 0xd5, 0xcb, 0x00, 0x6d,
+	 0x00, 0x69, 0x00, 0x8b } },
+	{ .addr = 0xFA, .len = 1, .data = { 0x03 } },
+};
+
+static struct samsung_spi_data etc_sequence[] = {
+	{ .addr = 0xF6, .len = 3, .data = { 0x00, 0x8e, 0x07 } },
+	{ .addr = 0xB3, .len = 1, .data = { 0x0C } },
+};
+
+static struct samsung_state_type samsung_state = { .brightness = 180 };
+static struct msm_panel_common_pdata *lcdc_samsung_pdata;
+
+#ifndef CONFIG_SPI_QUP
+static void samsung_spi_write_byte(boolean dc, u8 data)
+{
+	uint32 bit;
+	int bnum;
+
+	gpio_set_value(spi_sclk, 0);
+	gpio_set_value(spi_mosi, dc ? 1 : 0);
+	udelay(1);			/* at least 20 ns */
+	gpio_set_value(spi_sclk, 1);	/* clk high */
+	udelay(1);			/* at least 20 ns */
+
+	bnum = 8;			/* 8 data bits */
+	bit = 0x80;
+	while (bnum--) {
+		gpio_set_value(spi_sclk, 0); /* clk low */
+		gpio_set_value(spi_mosi, (data & bit) ? 1 : 0);
+		udelay(1);
+		gpio_set_value(spi_sclk, 1); /* clk high */
+		udelay(1);
+		bit >>= 1;
+	}
+	gpio_set_value(spi_mosi, 0);
+
+}
+
+static void samsung_spi_read_bytes(u8 cmd, u8 *data, int num)
+{
+	int bnum;
+
+	/* Chip Select - low */
+	gpio_set_value(spi_cs, 0);
+	udelay(2);
+
+	/* command byte first */
+	samsung_spi_write_byte(0, cmd);
+	udelay(2);
+
+	gpio_direction_input(spi_mosi);
+
+	if (num > 1) {
+		/* extra dummy clock */
+		gpio_set_value(spi_sclk, 0);
+		udelay(1);
+		gpio_set_value(spi_sclk, 1);
+		udelay(1);
+	}
+
+	/* followed by data bytes */
+	bnum = num * 8;	/* number of bits */
+	*data = 0;
+	while (bnum) {
+		gpio_set_value(spi_sclk, 0); /* clk low */
+		udelay(1);
+		*data <<= 1;
+		*data |= gpio_get_value(spi_mosi) ? 1 : 0;
+		gpio_set_value(spi_sclk, 1); /* clk high */
+		udelay(1);
+		--bnum;
+		if ((bnum % 8) == 0)
+			++data;
+	}
+
+	gpio_direction_output(spi_mosi, 0);
+
+	/* Chip Select - high */
+	udelay(2);
+	gpio_set_value(spi_cs, 1);
+}
+#endif
+
+#ifdef DEBUG
+static const char *byte_to_binary(const u8 *buf, int len)
+{
+	static char b[32*8+1];
+	char *p = b;
+	int i, z;
+
+	for (i = 0; i < len; ++i) {
+		u8 val = *buf++;
+		for (z = 1 << 7; z > 0; z >>= 1)
+			*p++ = (val & z) ? '1' : '0';
+	}
+	*p = 0;
+
+	return b;
+}
+#endif
+
+#define BIT_OFFSET	(bit_size % 8)
+#define ADD_BIT(val) do { \
+		tx_buf[bit_size / 8] |= \
+			(u8)((val ? 1 : 0) << (7 - BIT_OFFSET)); \
+		++bit_size; \
+	} while (0)
+
+#define ADD_BYTE(data) do { \
+		tx_buf[bit_size / 8] |= (u8)(data >> BIT_OFFSET); \
+		bit_size += 8; \
+		if (BIT_OFFSET != 0) \
+			tx_buf[bit_size / 8] |= (u8)(data << (8 - BIT_OFFSET));\
+	} while (0)
+
+static int samsung_serigo(struct samsung_spi_data data)
+{
+#ifdef CONFIG_SPI_QUP
+	char                tx_buf[32];
+	int                 bit_size = 0, i, rc;
+	struct spi_message  m;
+	struct spi_transfer t;
+
+	if (!lcdc_spi_client) {
+		pr_err("%s lcdc_spi_client is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	memset(&t, 0, sizeof t);
+	memset(tx_buf, 0, sizeof tx_buf);
+	t.tx_buf = tx_buf;
+	spi_setup(lcdc_spi_client);
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+
+	ADD_BIT(FALSE);
+	ADD_BYTE(data.addr);
+	for (i = 0; i < data.len; ++i) {
+		ADD_BIT(TRUE);
+		ADD_BYTE(data.data[i]);
+	}
+
+	/* add padding bits so we round to next byte */
+	t.len = (bit_size+7) / 8;
+	if (t.len <= 4)
+		t.bits_per_word = bit_size;
+
+	rc = spi_sync(lcdc_spi_client, &m);
+#ifdef DEBUG
+	pr_info("%s: addr=0x%02x, #args=%d[%d] [%s], rc=%d\n",
+		__func__, data.addr, t.len, t.bits_per_word,
+		byte_to_binary(tx_buf, t.len), rc);
+#endif
+	return rc;
+#else
+	int i;
+
+	/* Chip Select - low */
+	gpio_set_value(spi_cs, 0);
+	udelay(2);
+
+	samsung_spi_write_byte(FALSE, data.addr);
+	udelay(2);
+
+	for (i = 0; i < data.len; ++i) {
+		samsung_spi_write_byte(TRUE, data.data[i]);
+		udelay(2);
+	}
+
+	/* Chip Select - high */
+	gpio_set_value(spi_cs, 1);
+#ifdef DEBUG
+	pr_info("%s: cmd=0x%02x, #args=%d\n", __func__, data.addr, data.len);
+#endif
+	return 0;
+#endif
+}
+
+static int samsung_write_cmd(u8 cmd)
+{
+#ifdef CONFIG_SPI_QUP
+	char                tx_buf[2];
+	int                 bit_size = 0, rc;
+	struct spi_message  m;
+	struct spi_transfer t;
+
+	if (!lcdc_spi_client) {
+		pr_err("%s lcdc_spi_client is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	memset(&t, 0, sizeof t);
+	memset(tx_buf, 0, sizeof tx_buf);
+	t.tx_buf = tx_buf;
+	spi_setup(lcdc_spi_client);
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+
+	ADD_BIT(FALSE);
+	ADD_BYTE(cmd);
+
+	t.len = 2;
+	t.bits_per_word = 9;
+
+	rc = spi_sync(lcdc_spi_client, &m);
+#ifdef DEBUG
+	pr_info("%s: addr=0x%02x, #args=%d[%d] [%s], rc=%d\n",
+		__func__, cmd, t.len, t.bits_per_word,
+		byte_to_binary(tx_buf, t.len), rc);
+#endif
+	return rc;
+#else
+	/* Chip Select - low */
+	gpio_set_value(spi_cs, 0);
+	udelay(2);
+
+	samsung_spi_write_byte(FALSE, cmd);
+
+	/* Chip Select - high */
+	udelay(2);
+	gpio_set_value(spi_cs, 1);
+#ifdef DEBUG
+	pr_info("%s: cmd=0x%02x\n", __func__, cmd);
+#endif
+	return 0;
+#endif
+}
+
+static int samsung_serigo_list(struct samsung_spi_data *data, int count)
+{
+	int i, rc;
+	for (i = 0; i < count; ++i, ++data) {
+		rc = samsung_serigo(*data);
+		if (rc)
+			return rc;
+		msleep(10);
+	}
+	return 0;
+}
+
+#ifndef CONFIG_SPI_QUP
+static void samsung_spi_init(void)
+{
+	spi_sclk = *(lcdc_samsung_pdata->gpio_num);
+	spi_cs   = *(lcdc_samsung_pdata->gpio_num + 1);
+	spi_mosi = *(lcdc_samsung_pdata->gpio_num + 2);
+
+	/* Set the output so that we don't disturb the slave device */
+	gpio_set_value(spi_sclk, 1);
+	gpio_set_value(spi_mosi, 0);
+
+	/* Set the Chip Select deasserted (active low) */
+	gpio_set_value(spi_cs, 1);
+}
+#endif
+
+static void samsung_disp_powerup(void)
+{
+	if (!samsung_state.disp_powered_up && !samsung_state.display_on)
+		samsung_state.disp_powered_up = TRUE;
+}
+
+static struct work_struct disp_on_delayed_work;
+static void samsung_disp_on_delayed_work(struct work_struct *work_ptr)
+{
+	/* 0x01: Software Reset */
+	samsung_write_cmd(0x01);
+	msleep(120);
+
+	msleep(300);
+	samsung_serigo_list(panel_sequence,
+		sizeof(panel_sequence)/sizeof(*panel_sequence));
+	samsung_serigo_list(display_sequence,
+		sizeof(display_sequence)/sizeof(*display_sequence));
+
+	switch (samsung_state.brightness) {
+	case 300:
+		samsung_serigo_list(gamma_sequence_300,
+			sizeof(gamma_sequence_300)/sizeof(*gamma_sequence_300));
+		break;
+	case 180:
+	default:
+		samsung_serigo_list(gamma_sequence_180,
+			sizeof(gamma_sequence_180)/sizeof(*gamma_sequence_180));
+		break;
+	case 80:
+		samsung_serigo_list(gamma_sequence_80,
+			sizeof(gamma_sequence_80)/sizeof(*gamma_sequence_80));
+		break;
+	}
+
+	samsung_serigo_list(etc_sequence,
+		sizeof(etc_sequence)/sizeof(*etc_sequence));
+
+	/* 0x11: Sleep Out */
+	samsung_write_cmd(0x11);
+	msleep(120);
+	/* 0x13: Normal Mode On */
+	samsung_write_cmd(0x13);
+
+#ifndef CONFIG_SPI_QUP
+	{
+		u8 data;
+
+		msleep(120);
+		/* 0x0A: Read Display Power Mode */
+		samsung_spi_read_bytes(0x0A, &data, 1);
+		pr_info("%s: power=[%s]\n", __func__,
+			byte_to_binary(&data, 1));
+
+		msleep(120);
+		/* 0x0C: Read Display Pixel Format */
+		samsung_spi_read_bytes(0x0C, &data, 1);
+		pr_info("%s: pixel-format=[%s]\n", __func__,
+			byte_to_binary(&data, 1));
+	}
+#endif
+	msleep(120);
+	/* 0x29: Display On */
+	samsung_write_cmd(0x29);
+}
+
+static void samsung_disp_on(void)
+{
+	if (samsung_state.disp_powered_up && !samsung_state.display_on) {
+		INIT_WORK(&disp_on_delayed_work, samsung_disp_on_delayed_work);
+		schedule_work(&disp_on_delayed_work);
+
+		samsung_state.display_on = TRUE;
+	}
+}
+
+static int lcdc_samsung_panel_on(struct platform_device *pdev)
+{
+	pr_info("%s\n", __func__);
+	if (!samsung_state.disp_initialized) {
+#ifndef CONFIG_SPI_QUP
+		lcdc_samsung_pdata->panel_config_gpio(1);
+		samsung_spi_init();
+#endif
+		samsung_disp_powerup();
+		samsung_disp_on();
+		samsung_state.disp_initialized = TRUE;
+	}
+	return 0;
+}
+
+static int lcdc_samsung_panel_off(struct platform_device *pdev)
+{
+	pr_info("%s\n", __func__);
+	if (samsung_state.disp_powered_up && samsung_state.display_on) {
+		/* 0x10: Sleep In */
+		samsung_write_cmd(0x10);
+		msleep(120);
+
+		samsung_state.display_on = FALSE;
+		samsung_state.disp_initialized = FALSE;
+	}
+	return 0;
+}
+
+#ifdef SYSFS_DEBUG_CMD
+static ssize_t samsung_rda_cmd(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	ssize_t ret = snprintf(buf, PAGE_SIZE, "n/a\n");
+	pr_info("%s: 'n/a'\n", __func__);
+	return ret;
+}
+
+static ssize_t samsung_wta_cmd(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	ssize_t ret = strnlen(buf, PAGE_SIZE);
+	uint32 cmd;
+
+	sscanf(buf, "%x", &cmd);
+	samsung_write_cmd((u8)cmd);
+
+	return ret;
+}
+
+static DEVICE_ATTR(cmd, S_IRUGO | S_IWUGO, samsung_rda_cmd, samsung_wta_cmd);
+static struct attribute *fs_attrs[] = {
+	&dev_attr_cmd.attr,
+	NULL,
+};
+static struct attribute_group fs_attr_group = {
+	.attrs = fs_attrs,
+};
+#endif
+
+static struct msm_fb_panel_data samsung_panel_data = {
+	.on = lcdc_samsung_panel_on,
+	.off = lcdc_samsung_panel_off,
+};
+
+static int __devinit samsung_probe(struct platform_device *pdev)
+{
+	struct msm_panel_info *pinfo;
+#ifdef SYSFS_DEBUG_CMD
+	struct platform_device *fb_dev;
+	struct msm_fb_data_type *mfd;
+	int rc;
+#endif
+
+	pr_info("%s: id=%d\n", __func__, pdev->id);
+	lcdc_samsung_pdata = pdev->dev.platform_data;
+
+	pinfo = &samsung_panel_data.panel_info;
+	pinfo->xres = 480;
+	pinfo->yres = 800;
+	pinfo->type = LCDC_PANEL;
+	pinfo->pdest = DISPLAY_1;
+	pinfo->wait_cycle = 0;
+	pinfo->bpp = 24;
+	pinfo->fb_num = 2;
+	pinfo->clk_rate = 25600000; /* Max 27.77MHz */
+	pinfo->bl_max = 15;
+	pinfo->bl_min = 1;
+
+	/* AMS367PE02 Operation Manual, Page 7 */
+	pinfo->lcdc.h_back_porch = 16-2;	/* HBP-HLW */
+	pinfo->lcdc.h_front_porch = 16;
+	pinfo->lcdc.h_pulse_width = 2;
+	/* AMS367PE02 Operation Manual, Page 6 */
+	pinfo->lcdc.v_back_porch = 3-2;		/* VBP-VLW */
+	pinfo->lcdc.v_front_porch = 28;
+	pinfo->lcdc.v_pulse_width = 2;
+
+	pinfo->lcdc.border_clr = 0;
+	pinfo->lcdc.underflow_clr = 0xff;
+	pinfo->lcdc.hsync_skew = 0;
+	pdev->dev.platform_data = &samsung_panel_data;
+
+#ifndef SYSFS_DEBUG_CMD
+	msm_fb_add_device(pdev);
+#else
+	fb_dev = msm_fb_add_device(pdev);
+	mfd = platform_get_drvdata(fb_dev);
+	rc = sysfs_create_group(&mfd->fbi->dev->kobj, &fs_attr_group);
+	if (rc) {
+		pr_err("%s: sysfs group creation failed, rc=%d\n", __func__,
+			rc);
+		return rc;
+	}
+#endif
+	return 0;
+}
+
+#ifdef CONFIG_SPI_QUP
+static int __devinit lcdc_samsung_spi_probe(struct spi_device *spi)
+{
+	pr_info("%s\n", __func__);
+	lcdc_spi_client = spi;
+	lcdc_spi_client->bits_per_word = 32;
+	return 0;
+}
+static int __devexit lcdc_samsung_spi_remove(struct spi_device *spi)
+{
+	lcdc_spi_client = NULL;
+	return 0;
+}
+static struct spi_driver lcdc_samsung_spi_driver = {
+	.driver.name   = LCDC_SAMSUNG_SPI_DEVICE_NAME,
+	.driver.owner  = THIS_MODULE,
+	.probe         = lcdc_samsung_spi_probe,
+	.remove        = __devexit_p(lcdc_samsung_spi_remove),
+};
+#endif
+
+static struct platform_driver this_driver = {
+	.probe		= samsung_probe,
+	.driver.name	= "lcdc_samsung_oled",
+};
+
+static int __init lcdc_samsung_panel_init(void)
+{
+	int ret;
+
+	if (msm_fb_detect_client("lcdc_samsung_oled")) {
+		pr_err("%s: detect failed\n", __func__);
+		return 0;
+	}
+
+	ret = platform_driver_register(&this_driver);
+	if (ret) {
+		pr_err("%s: driver register failed, rc=%d\n", __func__, ret);
+		return ret;
+	}
+
+#ifdef CONFIG_SPI_QUP
+	ret = spi_register_driver(&lcdc_samsung_spi_driver);
+
+	if (ret) {
+		pr_err("%s: spi register failed: rc=%d\n", __func__, ret);
+		platform_driver_unregister(&this_driver);
+	} else
+		pr_info("%s: SUCCESS (SPI)\n", __func__);
+#else
+	pr_info("%s: SUCCESS (BitBang)\n", __func__);
+#endif
+	return ret;
+}
+
+module_init(lcdc_samsung_panel_init);
+static void __exit lcdc_samsung_panel_exit(void)
+{
+	pr_info("%s\n", __func__);
+#ifdef CONFIG_SPI_QUP
+	spi_unregister_driver(&lcdc_samsung_spi_driver);
+#endif
+	platform_driver_unregister(&this_driver);
+}
+module_exit(lcdc_samsung_panel_exit);
diff --git a/drivers/video/msm/lcdc_samsung_wsvga.c b/drivers/video/msm/lcdc_samsung_wsvga.c
new file mode 100644
index 0000000..6b35e52
--- /dev/null
+++ b/drivers/video/msm/lcdc_samsung_wsvga.c
@@ -0,0 +1,270 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/pwm.h>
+#ifdef CONFIG_PMIC8058_PWM
+#include <linux/mfd/pmic8058.h>
+#include <linux/pmic8058-pwm.h>
+#endif
+#include <mach/gpio.h>
+#include "msm_fb.h"
+
+
+
+#ifdef CONFIG_PMIC8058_PWM
+static struct pwm_device *bl_pwm0;
+static struct pwm_device *bl_pwm1;
+
+/* for samsung panel 300hz was the minimum freq where flickering wasnt
+ * observed as the screen was dimmed
+ */
+
+#define PWM_FREQ_HZ 300
+#define PWM_PERIOD_USEC (USEC_PER_SEC / PWM_FREQ_HZ)
+#define PWM_LEVEL 100
+#define PWM_DUTY_LEVEL (PWM_PERIOD_USEC / PWM_LEVEL)
+#endif
+
+struct lcdc_samsung_data {
+	struct msm_panel_common_pdata *pdata;
+#ifdef CONFIG_FB_MSM_LCDC_DSUB
+	int vga_enabled;
+#endif
+	struct platform_device *fbpdev;
+};
+
+static struct lcdc_samsung_data *dd;
+
+
+static void lcdc_samsung_panel_set_backlight(struct msm_fb_data_type *mfd)
+{
+#ifdef CONFIG_PMIC8058_PWM
+	int bl_level;
+	int ret;
+
+	bl_level = mfd->bl_level;
+
+	if (bl_pwm0) {
+		ret = pwm_config(bl_pwm0, PWM_DUTY_LEVEL * bl_level,
+			PWM_PERIOD_USEC);
+		if (ret)
+			printk(KERN_ERR "pwm_config on pwm 0 failed %d\n", ret);
+	}
+
+	if (bl_pwm1) {
+		ret = pwm_config(bl_pwm1,
+			PWM_PERIOD_USEC - (PWM_DUTY_LEVEL * bl_level),
+			PWM_PERIOD_USEC);
+		if (ret)
+			printk(KERN_ERR "pwm_config on pwm 1 failed %d\n", ret);
+	}
+
+	if (bl_pwm0) {
+		ret = pwm_enable(bl_pwm0);
+		if (ret)
+			printk(KERN_ERR "pwm_enable on pwm 0 failed %d\n", ret);
+	}
+
+	if (bl_pwm1) {
+		ret = pwm_enable(bl_pwm1);
+		if (ret)
+			printk(KERN_ERR "pwm_enable on pwm 1 failed %d\n", ret);
+	}
+#endif
+
+}
+
+#ifdef CONFIG_FB_MSM_LCDC_DSUB
+static ssize_t show_vga_enable(struct device *device,
+			       struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", dd->vga_enabled);
+}
+
+static ssize_t store_vga_enable(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	unsigned long enable;
+	int rc;
+
+	rc = strict_strtoul(buf, 10, &enable);
+	if (rc)
+		return -EINVAL;
+
+	if (dd->pdata && dd->pdata->vga_switch)
+		rc = dd->pdata->vga_switch(enable);
+	else
+		rc = -ENODEV;
+	if (!rc) {
+		dd->vga_enabled = enable;
+		rc = count;
+	}
+	return rc;
+}
+
+static DEVICE_ATTR(vga_enable, S_IRUGO|S_IWUSR, show_vga_enable,
+		   store_vga_enable);
+static struct attribute *attrs[] = {
+	&dev_attr_vga_enable.attr,
+	NULL,
+};
+static struct attribute_group attr_group = {
+	.attrs = attrs,
+};
+#endif
+
+static int __devinit samsung_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+#ifdef CONFIG_FB_MSM_LCDC_DSUB
+	struct msm_fb_data_type *mfd;
+#endif
+
+	if (pdev->id == 0) {
+		dd = kzalloc(sizeof *dd, GFP_KERNEL);
+		if (!dd)
+			return -ENOMEM;
+#ifdef CONFIG_FB_MSM_LCDC_DSUB
+		dd->vga_enabled = 0;
+#endif
+		dd->pdata = pdev->dev.platform_data;
+		return 0;
+	} else if (!dd)
+		return -ENODEV;
+
+#ifdef CONFIG_PMIC8058_PWM
+	bl_pwm0 = pwm_request(dd->pdata->gpio_num[0], "backlight1");
+	if (bl_pwm0 == NULL || IS_ERR(bl_pwm0)) {
+		pr_err("%s pwm_request() failed\n", __func__);
+		bl_pwm0 = NULL;
+	}
+
+	bl_pwm1 = pwm_request(dd->pdata->gpio_num[1], "backlight2");
+	if (bl_pwm1 == NULL || IS_ERR(bl_pwm1)) {
+		pr_err("%s pwm_request() failed\n", __func__);
+		bl_pwm1 = NULL;
+	}
+
+	pr_debug("samsung_probe: bl_pwm0=%p LPG_chan0=%d "
+			"bl_pwm1=%p LPG_chan1=%d\n",
+			bl_pwm0, (int)dd->pdata->gpio_num[0],
+			bl_pwm1, (int)dd->pdata->gpio_num[1]
+			);
+#endif
+
+
+	dd->fbpdev = msm_fb_add_device(pdev);
+	if (!dd->fbpdev) {
+		dev_err(&pdev->dev, "failed to add msm_fb device\n");
+		rc = -ENODEV;
+		goto probe_exit;
+	}
+
+#ifdef CONFIG_FB_MSM_LCDC_DSUB
+	mfd = platform_get_drvdata(dd->fbpdev);
+	if (mfd && mfd->fbi && mfd->fbi->dev) {
+		rc = sysfs_create_group(&mfd->fbi->dev->kobj, &attr_group);
+		if (rc)
+			dev_err(&pdev->dev, "failed to create sysfs group\n");
+	} else {
+		dev_err(&pdev->dev, "no dev to create sysfs group\n");
+		rc = -ENODEV;
+	}
+#endif
+
+probe_exit:
+	return rc;
+}
+
+#ifdef CONFIG_FB_MSM_LCDC_DSUB
+static int __devexit samsung_remove(struct platform_device *pdev)
+{
+	sysfs_remove_group(&dd->fbpdev->dev.kobj, &attr_group);
+	return 0;
+}
+#endif
+
+static struct platform_driver this_driver = {
+	.probe  = samsung_probe,
+#ifdef CONFIG_FB_MSM_LCDC_DSUB
+	.remove = samsung_remove,
+#endif
+	.driver = {
+		.name   = "lcdc_samsung_wsvga",
+	},
+};
+
+static struct msm_fb_panel_data samsung_panel_data = {
+	.set_backlight = lcdc_samsung_panel_set_backlight,
+};
+
+static struct platform_device this_device = {
+	.name   = "lcdc_samsung_wsvga",
+	.id	= 1,
+	.dev	= {
+		.platform_data = &samsung_panel_data,
+	}
+};
+
+static int __init lcdc_samsung_panel_init(void)
+{
+	int ret;
+	struct msm_panel_info *pinfo;
+
+	if (msm_fb_detect_client("lcdc_samsung_wsvga"))
+		return 0;
+
+	ret = platform_driver_register(&this_driver);
+	if (ret)
+		return ret;
+
+	pinfo = &samsung_panel_data.panel_info;
+	pinfo->xres = 1024;
+	pinfo->yres = 600;
+#ifdef CONFIG_FB_MSM_LCDC_DSUB
+	/* DSUB (VGA) is on the same bus, this allows us to allocate for the
+	 * max resolution of the DSUB display */
+	pinfo->mode2_xres = 1440;
+	pinfo->mode2_yres = 900;
+	pinfo->mode2_bpp = 16;
+#else
+	MSM_FB_SINGLE_MODE_PANEL(pinfo);
+#endif
+	pinfo->type = LCDC_PANEL;
+	pinfo->pdest = DISPLAY_1;
+	pinfo->wait_cycle = 0;
+	pinfo->bpp = 18;
+	pinfo->fb_num = 2;
+	pinfo->clk_rate = 43192000;
+	pinfo->bl_max = PWM_LEVEL;
+	pinfo->bl_min = 1;
+
+	pinfo->lcdc.h_back_porch = 80;
+	pinfo->lcdc.h_front_porch = 48;
+	pinfo->lcdc.h_pulse_width = 32;
+	pinfo->lcdc.v_back_porch = 4;
+	pinfo->lcdc.v_front_porch = 3;
+	pinfo->lcdc.v_pulse_width = 1;
+	pinfo->lcdc.border_clr = 0;
+	pinfo->lcdc.underflow_clr = 0xff;
+	pinfo->lcdc.hsync_skew = 0;
+
+	ret = platform_device_register(&this_device);
+	if (ret)
+		platform_driver_unregister(&this_driver);
+
+	return ret;
+}
+
+module_init(lcdc_samsung_panel_init);
diff --git a/drivers/video/msm/lcdc_sharp_wvga_pt.c b/drivers/video/msm/lcdc_sharp_wvga_pt.c
new file mode 100644
index 0000000..2ba2618
--- /dev/null
+++ b/drivers/video/msm/lcdc_sharp_wvga_pt.c
@@ -0,0 +1,414 @@
+/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/pwm.h>
+#ifdef CONFIG_PMIC8058_PWM
+#include <linux/mfd/pmic8058.h>
+#include <linux/pmic8058-pwm.h>
+#endif
+#ifdef CONFIG_SPI_QSD
+#include <linux/spi/spi.h>
+#endif
+#include <mach/gpio.h>
+#include "msm_fb.h"
+
+#ifdef CONFIG_SPI_QSD
+#define LCDC_SHARP_SPI_DEVICE_NAME	"lcdc_sharp_ls038y7dx01"
+static struct spi_device *lcdc_spi_client;
+#endif
+static int lcdc_sharp_panel_off(struct platform_device *pdev);
+
+#define BL_MAX		16
+
+#ifdef CONFIG_PMIC8058_PWM
+static struct pwm_device *bl_pwm;
+
+#define PWM_PERIOD	1000	/* us, period of 1Khz */
+#define DUTY_LEVEL	(PWM_PERIOD / BL_MAX)
+#endif
+
+#ifndef CONFIG_SPI_QSD
+static int spi_cs;
+static int spi_sclk;
+static int spi_mosi;
+static int spi_miso;
+static unsigned char bit_shift[8] = { (1 << 7),	/* MSB */
+	(1 << 6),
+	(1 << 5),
+	(1 << 4),
+	(1 << 3),
+	(1 << 2),
+	(1 << 1),
+	(1 << 0)		               /* LSB */
+};
+#endif
+
+struct sharp_state_type {
+	boolean disp_initialized;
+	boolean display_on;
+	boolean disp_powered_up;
+};
+
+struct sharp_spi_data {
+	u8 addr;
+	u8 data;
+};
+
+static struct sharp_spi_data init_sequence[] = {
+	{  15, 0x01 },
+	{   5, 0x01 },
+	{   7, 0x10 },
+	{   9, 0x1E },
+	{  10, 0x04 },
+	{  17, 0xFF },
+	{  21, 0x8A },
+	{  22, 0x00 },
+	{  23, 0x82 },
+	{  24, 0x24 },
+	{  25, 0x22 },
+	{  26, 0x6D },
+	{  27, 0xEB },
+	{  28, 0xB9 },
+	{  29, 0x3A },
+	{  49, 0x1A },
+	{  50, 0x16 },
+	{  51, 0x05 },
+	{  55, 0x7F },
+	{  56, 0x15 },
+	{  57, 0x7B },
+	{  60, 0x05 },
+	{  61, 0x0C },
+	{  62, 0x80 },
+	{  63, 0x00 },
+	{  92, 0x90 },
+	{  97, 0x01 },
+	{  98, 0xFF },
+	{ 113, 0x11 },
+	{ 114, 0x02 },
+	{ 115, 0x08 },
+	{ 123, 0xAB },
+	{ 124, 0x04 },
+	{   6, 0x02 },
+	{ 133, 0x00 },
+	{ 134, 0xFE },
+	{ 135, 0x22 },
+	{ 136, 0x0B },
+	{ 137, 0xFF },
+	{ 138, 0x0F },
+	{ 139, 0x00 },
+	{ 140, 0xFE },
+	{ 141, 0x22 },
+	{ 142, 0x0B },
+	{ 143, 0xFF },
+	{ 144, 0x0F },
+	{ 145, 0x00 },
+	{ 146, 0xFE },
+	{ 147, 0x22 },
+	{ 148, 0x0B },
+	{ 149, 0xFF },
+	{ 150, 0x0F },
+	{ 202, 0x30 },
+	{  30, 0x01 },
+	{   4, 0x01 },
+	{  31, 0x41 },
+};
+
+static struct sharp_state_type sharp_state = { 0 };
+static struct msm_panel_common_pdata *lcdc_sharp_pdata;
+
+#ifndef CONFIG_SPI_QSD
+static void sharp_spi_write_byte(u8 val)
+{
+	int i;
+
+	/* Clock should be Low before entering */
+	for (i = 0; i < 8; i++) {
+		/* #1: Drive the Data (High or Low) */
+		if (val & bit_shift[i])
+			gpio_set_value(spi_mosi, 1);
+		else
+			gpio_set_value(spi_mosi, 0);
+
+		/* #2: Drive the Clk High and then Low */
+		gpio_set_value(spi_sclk, 1);
+		gpio_set_value(spi_sclk, 0);
+	}
+}
+#endif
+
+static int serigo(u8 reg, u8 data)
+{
+#ifdef CONFIG_SPI_QSD
+	char                tx_buf[2];
+	int                 rc;
+	struct spi_message  m;
+	struct spi_transfer t;
+
+	if (!lcdc_spi_client) {
+		printk(KERN_ERR "%s lcdc_spi_client is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	memset(&t, 0, sizeof t);
+	t.tx_buf = tx_buf;
+	spi_setup(lcdc_spi_client);
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+
+	tx_buf[0] = reg;
+	tx_buf[1] = data;
+	t.rx_buf = NULL;
+	t.len = 2;
+	rc = spi_sync(lcdc_spi_client, &m);
+	return rc;
+#else
+	/* Enable the Chip Select - low */
+	gpio_set_value(spi_cs, 0);
+	udelay(1);
+
+	/* Transmit register address first, then data */
+	sharp_spi_write_byte(reg);
+
+	/* Idle state of MOSI is Low */
+	gpio_set_value(spi_mosi, 0);
+	udelay(1);
+	sharp_spi_write_byte(data);
+
+	gpio_set_value(spi_mosi, 0);
+	gpio_set_value(spi_cs, 1);
+	return 0;
+#endif
+}
+
+#ifndef CONFIG_SPI_QSD
+static void sharp_spi_init(void)
+{
+	spi_sclk = *(lcdc_sharp_pdata->gpio_num);
+	spi_cs   = *(lcdc_sharp_pdata->gpio_num + 1);
+	spi_mosi = *(lcdc_sharp_pdata->gpio_num + 2);
+	spi_miso = *(lcdc_sharp_pdata->gpio_num + 3);
+
+	/* Set the output so that we don't disturb the slave device */
+	gpio_set_value(spi_sclk, 0);
+	gpio_set_value(spi_mosi, 0);
+
+	/* Set the Chip Select deasserted (active low) */
+	gpio_set_value(spi_cs, 1);
+}
+#endif
+
+static void sharp_disp_powerup(void)
+{
+	if (!sharp_state.disp_powered_up && !sharp_state.display_on)
+		sharp_state.disp_powered_up = TRUE;
+}
+
+static void sharp_disp_on(void)
+{
+	int i;
+
+	if (sharp_state.disp_powered_up && !sharp_state.display_on) {
+		for (i = 0; i < ARRAY_SIZE(init_sequence); i++) {
+			serigo(init_sequence[i].addr,
+			       init_sequence[i].data);
+		}
+		mdelay(10);
+		serigo(31, 0xC1);
+		mdelay(10);
+		serigo(31, 0xD9);
+		serigo(31, 0xDF);
+
+		sharp_state.display_on = TRUE;
+	}
+}
+
+static int lcdc_sharp_panel_on(struct platform_device *pdev)
+{
+	if (!sharp_state.disp_initialized) {
+#ifndef CONFIG_SPI_QSD
+		lcdc_sharp_pdata->panel_config_gpio(1);
+		sharp_spi_init();
+#endif
+		sharp_disp_powerup();
+		sharp_disp_on();
+		sharp_state.disp_initialized = TRUE;
+	}
+	return 0;
+}
+
+static int lcdc_sharp_panel_off(struct platform_device *pdev)
+{
+	if (sharp_state.disp_powered_up && sharp_state.display_on) {
+		serigo(4, 0x00);
+		mdelay(40);
+		serigo(31, 0xC1);
+		mdelay(40);
+		serigo(31, 0x00);
+		msleep(16);
+		sharp_state.display_on = FALSE;
+		sharp_state.disp_initialized = FALSE;
+	}
+	return 0;
+}
+
+static void lcdc_sharp_panel_set_backlight(struct msm_fb_data_type *mfd)
+{
+	int bl_level;
+
+	bl_level = mfd->bl_level;
+
+#ifdef CONFIG_PMIC8058_PWM
+	if (bl_pwm) {
+		pwm_config(bl_pwm, DUTY_LEVEL * bl_level, PWM_PERIOD);
+		pwm_enable(bl_pwm);
+	}
+#endif
+}
+
+static int __devinit sharp_probe(struct platform_device *pdev)
+{
+	if (pdev->id == 0) {
+		lcdc_sharp_pdata = pdev->dev.platform_data;
+		return 0;
+	}
+
+#ifdef CONFIG_PMIC8058_PWM
+	bl_pwm = pwm_request(lcdc_sharp_pdata->gpio, "backlight");
+	if (bl_pwm == NULL || IS_ERR(bl_pwm)) {
+		pr_err("%s pwm_request() failed\n", __func__);
+		bl_pwm = NULL;
+	}
+
+	printk(KERN_INFO "sharp_probe: bl_pwm=%x LPG_chan=%d\n",
+			(int) bl_pwm, (int)lcdc_sharp_pdata->gpio);
+#endif
+
+	msm_fb_add_device(pdev);
+
+	return 0;
+}
+
+#ifdef CONFIG_SPI_QSD
+static int __devinit lcdc_sharp_spi_probe(struct spi_device *spi)
+{
+	lcdc_spi_client = spi;
+	lcdc_spi_client->bits_per_word = 32;
+	return 0;
+}
+static int __devexit lcdc_sharp_spi_remove(struct spi_device *spi)
+{
+	lcdc_spi_client = NULL;
+	return 0;
+}
+static struct spi_driver lcdc_sharp_spi_driver = {
+	.driver = {
+		.name  = LCDC_SHARP_SPI_DEVICE_NAME,
+		.owner = THIS_MODULE,
+	},
+	.probe         = lcdc_sharp_spi_probe,
+	.remove        = __devexit_p(lcdc_sharp_spi_remove),
+};
+#endif
+static struct platform_driver this_driver = {
+	.probe  = sharp_probe,
+	.driver = {
+		.name   = "lcdc_sharp_wvga",
+	},
+};
+
+static struct msm_fb_panel_data sharp_panel_data = {
+	.on = lcdc_sharp_panel_on,
+	.off = lcdc_sharp_panel_off,
+	.set_backlight = lcdc_sharp_panel_set_backlight,
+};
+
+static struct platform_device this_device = {
+	.name   = "lcdc_sharp_wvga",
+	.id	= 1,
+	.dev	= {
+		.platform_data = &sharp_panel_data,
+	}
+};
+
+static int __init lcdc_sharp_panel_init(void)
+{
+	int ret;
+	struct msm_panel_info *pinfo;
+
+#ifdef CONFIG_FB_MSM_MDDI_AUTO_DETECT
+	if (msm_fb_detect_client("lcdc_sharp_wvga_pt"))
+		return 0;
+#endif
+
+	ret = platform_driver_register(&this_driver);
+	if (ret)
+		return ret;
+
+	pinfo = &sharp_panel_data.panel_info;
+	pinfo->xres = 480;
+	pinfo->yres = 800;
+	MSM_FB_SINGLE_MODE_PANEL(pinfo);
+	pinfo->type = LCDC_PANEL;
+	pinfo->pdest = DISPLAY_1;
+	pinfo->wait_cycle = 0;
+	pinfo->bpp = 18;
+	pinfo->fb_num = 2;
+	pinfo->clk_rate = 24500000;
+	pinfo->bl_max = BL_MAX;
+	pinfo->bl_min = 1;
+
+	pinfo->lcdc.h_back_porch = 20;
+	pinfo->lcdc.h_front_porch = 10;
+	pinfo->lcdc.h_pulse_width = 10;
+	pinfo->lcdc.v_back_porch = 2;
+	pinfo->lcdc.v_front_porch = 2;
+	pinfo->lcdc.v_pulse_width = 2;
+	pinfo->lcdc.border_clr = 0;
+	pinfo->lcdc.underflow_clr = 0xff;
+	pinfo->lcdc.hsync_skew = 0;
+
+	ret = platform_device_register(&this_device);
+	if (ret) {
+		printk(KERN_ERR "%s not able to register the device\n",
+			__func__);
+		goto fail_driver;
+	}
+#ifdef CONFIG_SPI_QSD
+	ret = spi_register_driver(&lcdc_sharp_spi_driver);
+
+	if (ret) {
+		printk(KERN_ERR "%s not able to register spi\n", __func__);
+		goto fail_device;
+	}
+#endif
+	return ret;
+#ifdef CONFIG_SPI_QSD
+fail_device:
+	platform_device_unregister(&this_device);
+#endif
+fail_driver:
+		platform_driver_unregister(&this_driver);
+
+	return ret;
+}
+
+module_init(lcdc_sharp_panel_init);
+#ifdef CONFIG_SPI_QSD
+static void __exit lcdc_sharp_panel_exit(void)
+{
+	spi_unregister_driver(&lcdc_sharp_spi_driver);
+}
+module_exit(lcdc_sharp_panel_exit);
+#endif
+
diff --git a/drivers/video/msm/lcdc_st15.c b/drivers/video/msm/lcdc_st15.c
new file mode 100644
index 0000000..cdae358
--- /dev/null
+++ b/drivers/video/msm/lcdc_st15.c
@@ -0,0 +1,413 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include "msm_fb.h"
+
+#define DEVICE_NAME "sii9022"
+#define SII9022_DEVICE_ID   0xB0
+#define SII9022_ISR                   0x3D
+#define SII9022_ISR_RXS_STATUS        0x08
+
+static int lcdc_sii9022_panel_on(struct platform_device *pdev);
+static int lcdc_sii9022_panel_off(struct platform_device *pdev);
+
+static struct i2c_client *sii9022_i2c_client;
+
+struct sii9022_data {
+	struct msm_hdmi_platform_data *pd;
+	struct platform_device *pdev;
+	struct work_struct work;
+	int x_res;
+	int y_res;
+	int sysfs_entry_created;
+	int hdmi_attached;
+};
+static struct sii9022_data *dd;
+
+struct sii9022_i2c_addr_data{
+	u8 addr;
+	u8 data;
+};
+
+/* video mode data */
+static u8 video_mode_data[] = {
+	0x00,
+	0xF9, 0x1C, 0x70, 0x17, 0x72, 0x06, 0xEE, 0x02,
+};
+
+static u8 avi_io_format[] = {
+	0x09,
+	0x00, 0x00,
+};
+
+/* power state */
+static struct sii9022_i2c_addr_data regset0[] = {
+	{ 0x60, 0x04 },
+	{ 0x63, 0x00 },
+	{ 0x1E, 0x00 },
+};
+
+static u8 video_infoframe[] = {
+	0x0C,
+	0xF0, 0x00, 0x68, 0x00, 0x04, 0x00, 0x19, 0x00,
+	0xE9, 0x02, 0x04, 0x01, 0x04, 0x06,
+};
+
+/* configure audio */
+static struct sii9022_i2c_addr_data regset1[] = {
+	{ 0x26, 0x90 },
+	{ 0x20, 0x90 },
+	{ 0x1F, 0x80 },
+	{ 0x26, 0x80 },
+	{ 0x24, 0x02 },
+	{ 0x25, 0x0B },
+	{ 0xBC, 0x02 },
+	{ 0xBD, 0x24 },
+	{ 0xBE, 0x02 },
+};
+
+/* enable audio */
+static u8 misc_infoframe[] = {
+	0xBF,
+	0xC2, 0x84, 0x01, 0x0A, 0x6F, 0x02, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+/* set HDMI, active */
+static struct sii9022_i2c_addr_data regset2[] = {
+	{ 0x1A, 0x01 },
+	{ 0x3D, 0x00 },
+	{ 0x3C, 0x02 },
+};
+
+static struct msm_fb_panel_data sii9022_panel_data = {
+	.on = lcdc_sii9022_panel_on,
+	.off = lcdc_sii9022_panel_off,
+};
+
+static struct platform_device sii9022_device = {
+	.name   = DEVICE_NAME,
+	.id	= 1,
+	.dev	= {
+		.platform_data = &sii9022_panel_data,
+	}
+};
+
+static int send_i2c_data(struct i2c_client *client,
+			 struct sii9022_i2c_addr_data *regset,
+			 int size)
+{
+	int i;
+	int rc = 0;
+
+	for (i = 0; i < size; i++) {
+		rc = i2c_smbus_write_byte_data(
+			client,
+			regset[i].addr, regset[i].data);
+		if (rc)
+			break;
+	}
+	return rc;
+}
+
+static void sii9022_work_f(struct work_struct *work)
+{
+	int isr;
+
+	isr = i2c_smbus_read_byte_data(sii9022_i2c_client, SII9022_ISR);
+	if (isr < 0) {
+		dev_err(&sii9022_i2c_client->dev,
+			"i2c read of isr failed rc = 0x%x\n", isr);
+		return;
+	}
+	if (isr == 0)
+		return;
+
+	/* reset any set bits */
+	i2c_smbus_write_byte_data(sii9022_i2c_client, SII9022_ISR, isr);
+	dd->hdmi_attached = isr & SII9022_ISR_RXS_STATUS;
+	if (dd->pd->cable_detect)
+		dd->pd->cable_detect(dd->hdmi_attached);
+	if (dd->hdmi_attached) {
+		dd->x_res = 1280;
+		dd->y_res = 720;
+	} else {
+		dd->x_res = sii9022_panel_data.panel_info.xres;
+		dd->y_res = sii9022_panel_data.panel_info.yres;
+	}
+}
+
+static irqreturn_t sii9022_interrupt(int irq, void *dev_id)
+{
+	struct sii9022_data *dd = dev_id;
+
+	schedule_work(&dd->work);
+	return IRQ_HANDLED;
+}
+
+static int hdmi_sii_enable(struct i2c_client *client)
+{
+	int rc;
+	int retries = 10;
+	int count;
+
+	rc = i2c_smbus_write_byte_data(client, 0xC7, 0x00);
+	if (rc)
+		goto enable_exit;
+
+	do {
+		msleep(1);
+		rc = i2c_smbus_read_byte_data(client, 0x1B);
+	} while ((rc != SII9022_DEVICE_ID) && retries--);
+
+	if (rc != SII9022_DEVICE_ID)
+		return -ENODEV;
+
+	rc = i2c_smbus_write_byte_data(client, 0x1A, 0x11);
+	if (rc)
+		goto enable_exit;
+
+	count = ARRAY_SIZE(video_mode_data);
+	rc = i2c_master_send(client, video_mode_data, count);
+	if (rc != count) {
+		rc = -EIO;
+		goto enable_exit;
+	}
+
+	rc = i2c_smbus_write_byte_data(client, 0x08, 0x20);
+	if (rc)
+		goto enable_exit;
+	count = ARRAY_SIZE(avi_io_format);
+	rc = i2c_master_send(client, avi_io_format, count);
+	if (rc != count) {
+		rc = -EIO;
+		goto enable_exit;
+	}
+
+	rc = send_i2c_data(client, regset0, ARRAY_SIZE(regset0));
+	if (rc)
+		goto enable_exit;
+
+	count = ARRAY_SIZE(video_infoframe);
+	rc = i2c_master_send(client, video_infoframe, count);
+	if (rc != count) {
+		rc = -EIO;
+		goto enable_exit;
+	}
+
+	rc = send_i2c_data(client, regset1, ARRAY_SIZE(regset1));
+	if (rc)
+		goto enable_exit;
+
+	count = ARRAY_SIZE(misc_infoframe);
+	rc = i2c_master_send(client, misc_infoframe, count);
+	if (rc != count) {
+		rc = -EIO;
+		goto enable_exit;
+	}
+
+	rc = send_i2c_data(client, regset2, ARRAY_SIZE(regset2));
+	if (rc)
+		goto enable_exit;
+
+	return 0;
+enable_exit:
+	printk(KERN_ERR "%s: exited rc=%d\n", __func__, rc);
+	return rc;
+}
+
+static ssize_t show_res(struct device *device,
+			 struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%dx%d\n", dd->x_res, dd->y_res);
+}
+
+static struct device_attribute device_attrs[] = {
+	__ATTR(screen_resolution, S_IRUGO|S_IWUSR, show_res, NULL),
+};
+
+static int lcdc_sii9022_panel_on(struct platform_device *pdev)
+{
+	int rc;
+	if (!dd->sysfs_entry_created) {
+		dd->pdev = pdev;
+		rc = device_create_file(&pdev->dev, &device_attrs[0]);
+		if (!rc)
+			dd->sysfs_entry_created = 1;
+	}
+
+	rc = hdmi_sii_enable(sii9022_i2c_client);
+	if (rc) {
+		dd->hdmi_attached = 0;
+		dd->x_res = sii9022_panel_data.panel_info.xres;
+		dd->y_res = sii9022_panel_data.panel_info.yres;
+	}
+	if (dd->pd->irq)
+		enable_irq(dd->pd->irq);
+	/* Don't return the value from hdmi_sii_enable().
+	 * It may fail on some ST1.5s, but we must return 0 from this
+	 * function in order for the on-board display to turn on.
+	 */
+	return 0;
+}
+
+static int lcdc_sii9022_panel_off(struct platform_device *pdev)
+{
+	if (dd->pd->irq)
+		disable_irq(dd->pd->irq);
+	return 0;
+}
+
+static const struct i2c_device_id hmdi_sii_id[] = {
+	{ DEVICE_NAME, 0 },
+	{ }
+};
+
+static int hdmi_sii_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	int rc;
+
+	if (!i2c_check_functionality(client->adapter,
+				     I2C_FUNC_SMBUS_BYTE | I2C_FUNC_I2C))
+		return -ENODEV;
+
+	dd = kzalloc(sizeof *dd, GFP_KERNEL);
+	if (!dd) {
+		rc = -ENOMEM;
+		goto probe_exit;
+	}
+	sii9022_i2c_client = client;
+	i2c_set_clientdata(client, dd);
+	dd->pd = client->dev.platform_data;
+	if (!dd->pd) {
+		rc = -ENODEV;
+		goto probe_free;
+	}
+	if (dd->pd->irq) {
+		INIT_WORK(&dd->work, sii9022_work_f);
+		rc = request_irq(dd->pd->irq,
+				 &sii9022_interrupt,
+				 IRQF_TRIGGER_FALLING,
+				 "sii9022_cable", dd);
+		if (rc)
+			goto probe_free;
+		disable_irq(dd->pd->irq);
+	}
+	msm_fb_add_device(&sii9022_device);
+	dd->x_res = sii9022_panel_data.panel_info.xres;
+	dd->y_res = sii9022_panel_data.panel_info.yres;
+
+	return 0;
+
+probe_free:
+	i2c_set_clientdata(client, NULL);
+	kfree(dd);
+probe_exit:
+	return rc;
+}
+
+static int __devexit hdmi_sii_remove(struct i2c_client *client)
+{
+	int err = 0 ;
+	struct msm_hdmi_platform_data *pd;
+
+	if (dd->sysfs_entry_created)
+		device_remove_file(&dd->pdev->dev, &device_attrs[0]);
+	pd = client->dev.platform_data;
+	if (pd && pd->irq)
+		free_irq(pd->irq, dd);
+	i2c_set_clientdata(client, NULL);
+	kfree(dd);
+
+	return err ;
+}
+
+#ifdef CONFIG_PM
+static int sii9022_suspend(struct device *dev)
+{
+	if (dd && dd->pd && dd->pd->irq)
+		disable_irq(dd->pd->irq);
+	return 0;
+}
+
+static int sii9022_resume(struct device *dev)
+{
+	if (dd && dd->pd && dd->pd->irq)
+		enable_irq(dd->pd->irq);
+	return 0;
+}
+
+static struct dev_pm_ops sii9022_pm_ops = {
+	.suspend = sii9022_suspend,
+	.resume = sii9022_resume,
+};
+#endif
+
+static struct i2c_driver hdmi_sii_i2c_driver = {
+	.driver = {
+		.name = DEVICE_NAME,
+		.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm     = &sii9022_pm_ops,
+#endif
+	},
+	.probe = hdmi_sii_probe,
+	.remove =  __exit_p(hdmi_sii_remove),
+	.id_table = hmdi_sii_id,
+};
+
+static int __init lcdc_st15_init(void)
+{
+	int ret;
+	struct msm_panel_info *pinfo;
+
+	if (msm_fb_detect_client("lcdc_st15"))
+		return 0;
+
+	pinfo = &sii9022_panel_data.panel_info;
+	pinfo->xres = 1366;
+	pinfo->yres = 768;
+	MSM_FB_SINGLE_MODE_PANEL(pinfo);
+	pinfo->type = LCDC_PANEL;
+	pinfo->pdest = DISPLAY_1;
+	pinfo->wait_cycle = 0;
+	pinfo->bpp = 24;
+	pinfo->fb_num = 2;
+	pinfo->clk_rate = 74250000;
+
+	pinfo->lcdc.h_back_porch = 120;
+	pinfo->lcdc.h_front_porch = 20;
+	pinfo->lcdc.h_pulse_width = 40;
+	pinfo->lcdc.v_back_porch = 25;
+	pinfo->lcdc.v_front_porch = 1;
+	pinfo->lcdc.v_pulse_width = 7;
+	pinfo->lcdc.border_clr = 0;      /* blk */
+	pinfo->lcdc.underflow_clr = 0xff;        /* blue */
+	pinfo->lcdc.hsync_skew = 0;
+
+	ret = i2c_add_driver(&hdmi_sii_i2c_driver);
+	if (ret)
+		printk(KERN_ERR "%s: failed to add i2c driver\n", __func__);
+
+	return ret;
+}
+
+static void __exit hdmi_sii_exit(void)
+{
+	i2c_del_driver(&hdmi_sii_i2c_driver);
+}
+
+module_init(lcdc_st15_init);
+module_exit(hdmi_sii_exit);
diff --git a/drivers/video/msm/lcdc_toshiba_fwvga_pt.c b/drivers/video/msm/lcdc_toshiba_fwvga_pt.c
new file mode 100644
index 0000000..77606cf
--- /dev/null
+++ b/drivers/video/msm/lcdc_toshiba_fwvga_pt.c
@@ -0,0 +1,471 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <mach/gpio.h>
+#include <mach/pmic.h>
+#include <mach/socinfo.h>
+#include "msm_fb.h"
+
+static int spi_cs0_N;
+static int spi_sclk;
+static int spi_mosi;
+static int spi_miso;
+
+struct toshiba_state_type {
+	boolean disp_initialized;
+	boolean display_on;
+	boolean disp_powered_up;
+};
+
+static struct toshiba_state_type toshiba_state = { 0 };
+static struct msm_panel_common_pdata *lcdc_toshiba_pdata;
+
+static int toshiba_spi_write(char data1, char data2, int rs)
+{
+	uint32 bitdata = 0, bnum = 24, bmask = 0x800000;
+
+	gpio_set_value_cansleep(spi_cs0_N, 0);	/* cs* low */
+	udelay(1);
+
+	if (rs)
+		bitdata = (0x72 << 16);
+	else
+		bitdata = (0x70 << 16);
+
+	bitdata |= ((data1 << 8) | data2);
+
+	while (bnum) {
+		gpio_set_value_cansleep(spi_sclk, 0); /* clk low */
+		udelay(1);
+
+		if (bitdata & bmask)
+			gpio_set_value_cansleep(spi_mosi, 1);
+		else
+			gpio_set_value_cansleep(spi_mosi, 0);
+
+		udelay(1);
+		gpio_set_value_cansleep(spi_sclk, 1); /* clk high */
+		udelay(1);
+		bmask >>= 1;
+		bnum--;
+	}
+
+	gpio_set_value_cansleep(spi_cs0_N, 1);	/* cs* high */
+	udelay(1);
+	return 0;
+}
+
+static void spi_pin_assign(void)
+{
+	/* Setting the Default GPIO's */
+	spi_mosi  = *(lcdc_toshiba_pdata->gpio_num);
+	spi_miso  = *(lcdc_toshiba_pdata->gpio_num + 1);
+	spi_sclk  = *(lcdc_toshiba_pdata->gpio_num + 2);
+	spi_cs0_N = *(lcdc_toshiba_pdata->gpio_num + 3);
+}
+
+static void toshiba_disp_powerup(void)
+{
+	if (!toshiba_state.disp_powered_up && !toshiba_state.display_on) {
+		/* Reset the hardware first */
+		/* Include DAC power up implementation here */
+	      toshiba_state.disp_powered_up = TRUE;
+	}
+}
+
+static void toshiba_disp_on(void)
+{
+	if (toshiba_state.disp_powered_up && !toshiba_state.display_on) {
+		toshiba_spi_write(0x01, 0x00, 0);
+		toshiba_spi_write(0x30, 0x00, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x01, 0x01, 0);
+		toshiba_spi_write(0x40, 0x10, 1);
+
+#ifdef TOSHIBA_FWVGA_FULL_INIT
+		udelay(500);
+		toshiba_spi_write(0x01, 0x06, 0);
+		toshiba_spi_write(0x00, 0x00, 1);
+		msleep(20);
+
+		toshiba_spi_write(0x00, 0x01, 0);
+		toshiba_spi_write(0x03, 0x10, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x00, 0x02, 0);
+		toshiba_spi_write(0x01, 0x00, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x00, 0x03, 0);
+		toshiba_spi_write(0x00, 0x00, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x00, 0x07, 0);
+		toshiba_spi_write(0x00, 0x00, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x00, 0x08, 0);
+		toshiba_spi_write(0x00, 0x04, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x00, 0x09, 0);
+		toshiba_spi_write(0x00, 0x0c, 1);
+#endif
+		udelay(500);
+		toshiba_spi_write(0x00, 0x0c, 0);
+		toshiba_spi_write(0x40, 0x10, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x00, 0x0e, 0);
+		toshiba_spi_write(0x00, 0x00, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x00, 0x20, 0);
+		toshiba_spi_write(0x01, 0x3f, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x00, 0x22, 0);
+		toshiba_spi_write(0x76, 0x00, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x00, 0x23, 0);
+		toshiba_spi_write(0x1c, 0x0a, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x00, 0x24, 0);
+		toshiba_spi_write(0x1c, 0x2c, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x00, 0x25, 0);
+		toshiba_spi_write(0x1c, 0x4e, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x00, 0x27, 0);
+		toshiba_spi_write(0x00, 0x00, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x00, 0x28, 0);
+		toshiba_spi_write(0x76, 0x0c, 1);
+
+#ifdef TOSHIBA_FWVGA_FULL_INIT
+		udelay(500);
+		toshiba_spi_write(0x03, 0x00, 0);
+		toshiba_spi_write(0x00, 0x00, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x03, 0x01, 0);
+		toshiba_spi_write(0x05, 0x02, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x03, 0x02, 0);
+		toshiba_spi_write(0x07, 0x05, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x03, 0x03, 0);
+		toshiba_spi_write(0x00, 0x00, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x03, 0x04, 0);
+		toshiba_spi_write(0x02, 0x00, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x03, 0x05, 0);
+		toshiba_spi_write(0x07, 0x07, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x03, 0x06, 0);
+		toshiba_spi_write(0x10, 0x10, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x03, 0x07, 0);
+		toshiba_spi_write(0x02, 0x02, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x03, 0x08, 0);
+		toshiba_spi_write(0x07, 0x04, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x03, 0x09, 0);
+		toshiba_spi_write(0x07, 0x07, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x03, 0x0a, 0);
+		toshiba_spi_write(0x00, 0x00, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x03, 0x0b, 0);
+		toshiba_spi_write(0x00, 0x00, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x03, 0x0c, 0);
+		toshiba_spi_write(0x07, 0x07, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x03, 0x0d, 0);
+		toshiba_spi_write(0x10, 0x10, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x03, 0x10, 0);
+		toshiba_spi_write(0x01, 0x04, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x03, 0x11, 0);
+		toshiba_spi_write(0x05, 0x03, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x03, 0x12, 0);
+		toshiba_spi_write(0x03, 0x04, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x03, 0x15, 0);
+		toshiba_spi_write(0x03, 0x04, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x03, 0x16, 0);
+		toshiba_spi_write(0x03, 0x1c, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x03, 0x17, 0);
+		toshiba_spi_write(0x02, 0x04, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x03, 0x18, 0);
+		toshiba_spi_write(0x04, 0x02, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x03, 0x19, 0);
+		toshiba_spi_write(0x03, 0x05, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x03, 0x1c, 0);
+		toshiba_spi_write(0x07, 0x07, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x03, 0x1d, 0);
+		toshiba_spi_write(0x02, 0x1f, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x03, 0x20, 0);
+		toshiba_spi_write(0x05, 0x07, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x03, 0x21, 0);
+		toshiba_spi_write(0x06, 0x04, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x03, 0x22, 0);
+		toshiba_spi_write(0x04, 0x05, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x03, 0x27, 0);
+		toshiba_spi_write(0x02, 0x03, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x03, 0x28, 0);
+		toshiba_spi_write(0x03, 0x00, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x03, 0x29, 0);
+		toshiba_spi_write(0x00, 0x02, 1);
+
+#endif
+		udelay(500);
+		toshiba_spi_write(0x01, 0x00, 0);
+		toshiba_spi_write(0x36, 0x3c, 1);
+		udelay(500);
+
+		toshiba_spi_write(0x01, 0x01, 0);
+		toshiba_spi_write(0x40, 0x03, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x01, 0x02, 0);
+		toshiba_spi_write(0x00, 0x01, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x01, 0x03, 0);
+		toshiba_spi_write(0x3c, 0x58, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x01, 0x0c, 0);
+		toshiba_spi_write(0x01, 0x35, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x01, 0x06, 0);
+		toshiba_spi_write(0x00, 0x02, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x00, 0x29, 0);
+		toshiba_spi_write(0x03, 0xbf, 1);
+
+		udelay(500);
+		toshiba_spi_write(0x01, 0x06, 0);
+		toshiba_spi_write(0x00, 0x03, 1);
+		msleep(32);
+
+		toshiba_spi_write(0x01, 0x01, 0);
+		toshiba_spi_write(0x40, 0x10, 1);
+		msleep(80);
+
+		toshiba_state.display_on = TRUE;
+	}
+}
+
+static int lcdc_toshiba_panel_on(struct platform_device *pdev)
+{
+	if (!toshiba_state.disp_initialized) {
+		/* Configure reset GPIO that drives DAC */
+		if (lcdc_toshiba_pdata->panel_config_gpio)
+			lcdc_toshiba_pdata->panel_config_gpio(1);
+		toshiba_disp_powerup();
+		toshiba_disp_on();
+		toshiba_state.disp_initialized = TRUE;
+	}
+	return 0;
+}
+
+static int lcdc_toshiba_panel_off(struct platform_device *pdev)
+{
+	if (toshiba_state.disp_powered_up && toshiba_state.display_on) {
+		toshiba_spi_write(0x01, 0x06, 1);
+		toshiba_spi_write(0x00, 0x02, 1);
+		msleep(80);
+
+		toshiba_spi_write(0x01, 0x06, 1);
+		toshiba_spi_write(0x00, 0x00, 1);
+
+		toshiba_spi_write(0x00, 0x29, 1);
+		toshiba_spi_write(0x00, 0x02, 1);
+
+		toshiba_spi_write(0x01, 0x00, 1);
+		toshiba_spi_write(0x30, 0x00, 1);
+
+		if (lcdc_toshiba_pdata->panel_config_gpio)
+			lcdc_toshiba_pdata->panel_config_gpio(0);
+		toshiba_state.display_on = FALSE;
+		toshiba_state.disp_initialized = FALSE;
+	}
+
+	return 0;
+}
+
+static void lcdc_toshiba_set_backlight(struct msm_fb_data_type *mfd)
+{
+	int ret;
+	int bl_level;
+
+	bl_level = mfd->bl_level;
+
+	if (lcdc_toshiba_pdata && lcdc_toshiba_pdata->pmic_backlight)
+		ret = lcdc_toshiba_pdata->pmic_backlight(bl_level);
+	else
+		pr_err("%s(): Backlight level set failed", __func__);
+
+	return;
+}
+
+static int __devinit toshiba_probe(struct platform_device *pdev)
+{
+	if (pdev->id == 0) {
+		lcdc_toshiba_pdata = pdev->dev.platform_data;
+		spi_pin_assign();
+		return 0;
+	}
+	msm_fb_add_device(pdev);
+	return 0;
+}
+
+static struct platform_driver this_driver = {
+	.probe  = toshiba_probe,
+	.driver = {
+		.name   = "lcdc_toshiba_fwvga_pt",
+	},
+};
+
+static struct msm_fb_panel_data toshiba_panel_data = {
+	.on = lcdc_toshiba_panel_on,
+	.off = lcdc_toshiba_panel_off,
+	.set_backlight = lcdc_toshiba_set_backlight,
+};
+
+static struct platform_device this_device = {
+	.name   = "lcdc_toshiba_fwvga_pt",
+	.id	= 1,
+	.dev	= {
+		.platform_data = &toshiba_panel_data,
+	}
+};
+
+static int __init lcdc_toshiba_panel_init(void)
+{
+	int ret;
+	struct msm_panel_info *pinfo;
+
+	ret = msm_fb_detect_client("lcdc_toshiba_fwvga_pt");
+	if (ret)
+		return 0;
+
+
+	ret = platform_driver_register(&this_driver);
+	if (ret)
+		return ret;
+
+	pinfo = &toshiba_panel_data.panel_info;
+	pinfo->xres = 480;
+	pinfo->yres = 864;
+	MSM_FB_SINGLE_MODE_PANEL(pinfo);
+	pinfo->type = LCDC_PANEL;
+	pinfo->pdest = DISPLAY_1;
+	pinfo->wait_cycle = 0;
+	pinfo->bpp = 18;
+	pinfo->fb_num = 2;
+	/* 30Mhz mdp_lcdc_pclk and mdp_lcdc_pad_pcl */
+	pinfo->clk_rate = 30720000;
+	pinfo->bl_max = 100;
+	pinfo->bl_min = 1;
+
+	if (cpu_is_msm7x25a() || cpu_is_msm7x25aa() || cpu_is_msm7x25ab()) {
+		pinfo->yres = 320;
+		pinfo->lcdc.h_back_porch = 10;
+		pinfo->lcdc.h_front_porch = 21;
+		pinfo->lcdc.h_pulse_width = 5;
+		pinfo->lcdc.v_back_porch = 8;
+		pinfo->lcdc.v_front_porch = 540;
+		pinfo->lcdc.v_pulse_width = 42;
+		pinfo->lcdc.border_clr = 0;     /* blk */
+		pinfo->lcdc.underflow_clr = 0xff;       /* blue */
+		pinfo->lcdc.hsync_skew = 0;
+	} else {
+		pinfo->lcdc.h_back_porch = 8;
+		pinfo->lcdc.h_front_porch = 16;
+		pinfo->lcdc.h_pulse_width = 8;
+		pinfo->lcdc.v_back_porch = 2;
+		pinfo->lcdc.v_front_porch = 2;
+		pinfo->lcdc.v_pulse_width = 2;
+		pinfo->lcdc.border_clr = 0;     /* blk */
+		pinfo->lcdc.underflow_clr = 0xff;       /* blue */
+		pinfo->lcdc.hsync_skew = 0;
+	}
+
+	ret = platform_device_register(&this_device);
+	if (ret) {
+		printk(KERN_ERR "%s not able to register the device\n",
+			 __func__);
+		platform_driver_unregister(&this_driver);
+	}
+	return ret;
+}
+
+device_initcall(lcdc_toshiba_panel_init);
diff --git a/drivers/video/msm/lcdc_toshiba_wvga_pt.c b/drivers/video/msm/lcdc_toshiba_wvga_pt.c
new file mode 100644
index 0000000..f0aa8f5
--- /dev/null
+++ b/drivers/video/msm/lcdc_toshiba_wvga_pt.c
@@ -0,0 +1,519 @@
+/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#ifdef CONFIG_SPI_QSD
+#include <linux/spi/spi.h>
+#endif
+#include <mach/gpio.h>
+#include <mach/pmic.h>
+#include "msm_fb.h"
+
+#ifdef CONFIG_FB_MSM_TRY_MDDI_CATCH_LCDC_PRISM
+#include "mddihosti.h"
+#endif
+
+#ifdef CONFIG_SPI_QSD
+#define LCDC_TOSHIBA_SPI_DEVICE_NAME "lcdc_toshiba_ltm030dd40"
+static struct spi_device *lcdc_toshiba_spi_client;
+#else
+static int spi_cs;
+static int spi_sclk;
+static int spi_mosi;
+static int spi_miso;
+#endif
+struct toshiba_state_type{
+	boolean disp_initialized;
+	boolean display_on;
+	boolean disp_powered_up;
+};
+
+static struct toshiba_state_type toshiba_state = { 0 };
+static struct msm_panel_common_pdata *lcdc_toshiba_pdata;
+
+#ifndef CONFIG_SPI_QSD
+static void toshiba_spi_write_byte(char dc, uint8 data)
+{
+	uint32 bit;
+	int bnum;
+
+	gpio_set_value(spi_sclk, 0); /* clk low */
+	/* dc: 0 for command, 1 for parameter */
+	gpio_set_value(spi_mosi, dc);
+	udelay(1);	/* at least 20 ns */
+	gpio_set_value(spi_sclk, 1); /* clk high */
+	udelay(1);	/* at least 20 ns */
+	bnum = 8;	/* 8 data bits */
+	bit = 0x80;
+	while (bnum) {
+		gpio_set_value(spi_sclk, 0); /* clk low */
+		if (data & bit)
+			gpio_set_value(spi_mosi, 1);
+		else
+			gpio_set_value(spi_mosi, 0);
+		udelay(1);
+		gpio_set_value(spi_sclk, 1); /* clk high */
+		udelay(1);
+		bit >>= 1;
+		bnum--;
+	}
+}
+#endif
+
+static int toshiba_spi_write(char cmd, uint32 data, int num)
+{
+	char *bp;
+#ifdef CONFIG_SPI_QSD
+	char                tx_buf[4];
+	int                 rc, i;
+	struct spi_message  m;
+	struct spi_transfer t;
+	uint32 final_data = 0;
+
+	if (!lcdc_toshiba_spi_client) {
+		printk(KERN_ERR "%s lcdc_toshiba_spi_client is NULL\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	memset(&t, 0, sizeof t);
+	t.tx_buf = tx_buf;
+	spi_setup(lcdc_toshiba_spi_client);
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+
+	/* command byte first */
+	final_data |= cmd << 23;
+	t.len = num + 2;
+	if (t.len < 4)
+		t.bits_per_word = 8 * t.len;
+	/* followed by parameter bytes */
+	if (num) {
+		bp = (char *)&data;;
+		bp += (num - 1);
+		i = 1;
+		while (num) {
+			final_data |= 1 << (((4 - i) << 3) - i - 1);
+			final_data |= *bp << (((4 - i - 1) << 3) - i - 1);
+			num--;
+			bp--;
+			i++;
+		}
+	}
+
+	bp = (char *)&final_data;
+	for (i = 0; i < t.len; i++)
+		tx_buf[i] = bp[3 - i];
+	t.rx_buf = NULL;
+	rc = spi_sync(lcdc_toshiba_spi_client, &m);
+	if (rc)
+		printk(KERN_ERR "spi_sync _write failed %d\n", rc);
+	return rc;
+#else
+	gpio_set_value(spi_cs, 1);	/* cs high */
+
+	/* command byte first */
+	toshiba_spi_write_byte(0, cmd);
+
+	/* followed by parameter bytes */
+	if (num) {
+		bp = (char *)&data;;
+		bp += (num - 1);
+		while (num) {
+			toshiba_spi_write_byte(1, *bp);
+			num--;
+			bp--;
+		}
+	}
+
+	gpio_set_value(spi_cs, 0);	/* cs low */
+	udelay(1);
+	return 0;
+#endif
+}
+
+static int toshiba_spi_read_bytes(char cmd, uint32 *data, int num)
+{
+#ifdef CONFIG_SPI_QSD
+	char            tx_buf[5];
+	char		    rx_buf[5];
+	int                 rc;
+	struct spi_message  m;
+	struct spi_transfer t;
+
+	if (!lcdc_toshiba_spi_client) {
+		printk(KERN_ERR "%s lcdc_toshiba_spi_client is NULL\n",
+			 __func__);
+		return -EINVAL;
+	}
+
+	memset(&t, 0, sizeof t);
+	t.tx_buf = tx_buf;
+	t.rx_buf = rx_buf;
+	spi_setup(lcdc_toshiba_spi_client);
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+
+	/* command byte first */
+	tx_buf[0] = 0 | ((cmd >> 1) & 0x7f);
+	tx_buf[1] = (cmd & 0x01) << 7;
+	tx_buf[2] = 0;
+	tx_buf[3] = 0;
+	tx_buf[4] = 0;
+
+	t.len = 5;
+
+	rc = spi_sync(lcdc_toshiba_spi_client, &m);
+	*data = 0;
+	*data = ((rx_buf[1] & 0x1f) << 19) | (rx_buf[2] << 11) |
+		(rx_buf[3] << 3) | ((rx_buf[4] & 0xe0) >> 5);
+	if (rc)
+		printk(KERN_ERR "spi_sync _read failed %d\n", rc);
+	return rc;
+#else
+	uint32 dbit, bits;
+	int bnum;
+
+	gpio_set_value(spi_cs, 1);	/* cs high */
+
+	/* command byte first */
+	toshiba_spi_write_byte(0, cmd);
+
+	if (num > 1) {
+		/* extra dc bit */
+		gpio_set_value(spi_sclk, 0); /* clk low */
+		udelay(1);
+		dbit = gpio_get_value(spi_miso);/* dc bit */
+		udelay(1);
+		gpio_set_value(spi_sclk, 1); /* clk high */
+	}
+
+	/* followed by data bytes */
+	bnum = num * 8;	/* number of bits */
+	bits = 0;
+	while (bnum) {
+		bits <<= 1;
+		gpio_set_value(spi_sclk, 0); /* clk low */
+		udelay(1);
+		dbit = gpio_get_value(spi_miso);
+		udelay(1);
+		gpio_set_value(spi_sclk, 1); /* clk high */
+		bits |= dbit;
+		bnum--;
+	}
+
+	*data = bits;
+
+	udelay(1);
+	gpio_set_value(spi_cs, 0);	/* cs low */
+	udelay(1);
+	return 0;
+#endif
+}
+
+#ifndef CONFIG_SPI_QSD
+static void spi_pin_assign(void)
+{
+	/* Setting the Default GPIO's */
+	spi_sclk = *(lcdc_toshiba_pdata->gpio_num);
+	spi_cs   = *(lcdc_toshiba_pdata->gpio_num + 1);
+	spi_mosi  = *(lcdc_toshiba_pdata->gpio_num + 2);
+	spi_miso  = *(lcdc_toshiba_pdata->gpio_num + 3);
+}
+#endif
+
+static void toshiba_disp_powerup(void)
+{
+	if (!toshiba_state.disp_powered_up && !toshiba_state.display_on) {
+		/* Reset the hardware first */
+		/* Include DAC power up implementation here */
+	      toshiba_state.disp_powered_up = TRUE;
+	}
+}
+
+static void toshiba_disp_on(void)
+{
+	uint32	data;
+
+#ifndef CONFIG_SPI_QSD
+	gpio_set_value(spi_cs, 0);	/* low */
+	gpio_set_value(spi_sclk, 1);	/* high */
+	gpio_set_value(spi_mosi, 0);
+	gpio_set_value(spi_miso, 0);
+#endif
+
+	if (toshiba_state.disp_powered_up && !toshiba_state.display_on) {
+		toshiba_spi_write(0, 0, 0);
+		mdelay(7);
+		toshiba_spi_write(0, 0, 0);
+		mdelay(7);
+		toshiba_spi_write(0, 0, 0);
+		mdelay(7);
+		toshiba_spi_write(0xba, 0x11, 1);
+		toshiba_spi_write(0x36, 0x00, 1);
+		mdelay(1);
+		toshiba_spi_write(0x3a, 0x60, 1);
+		toshiba_spi_write(0xb1, 0x5d, 1);
+		mdelay(1);
+		toshiba_spi_write(0xb2, 0x33, 1);
+		toshiba_spi_write(0xb3, 0x22, 1);
+		mdelay(1);
+		toshiba_spi_write(0xb4, 0x02, 1);
+		toshiba_spi_write(0xb5, 0x1e, 1); /* vcs -- adjust brightness */
+		mdelay(1);
+		toshiba_spi_write(0xb6, 0x27, 1);
+		toshiba_spi_write(0xb7, 0x03, 1);
+		mdelay(1);
+		toshiba_spi_write(0xb9, 0x24, 1);
+		toshiba_spi_write(0xbd, 0xa1, 1);
+		mdelay(1);
+		toshiba_spi_write(0xbb, 0x00, 1);
+		toshiba_spi_write(0xbf, 0x01, 1);
+		mdelay(1);
+		toshiba_spi_write(0xbe, 0x00, 1);
+		toshiba_spi_write(0xc0, 0x11, 1);
+		mdelay(1);
+		toshiba_spi_write(0xc1, 0x11, 1);
+		toshiba_spi_write(0xc2, 0x11, 1);
+		mdelay(1);
+		toshiba_spi_write(0xc3, 0x3232, 2);
+		mdelay(1);
+		toshiba_spi_write(0xc4, 0x3232, 2);
+		mdelay(1);
+		toshiba_spi_write(0xc5, 0x3232, 2);
+		mdelay(1);
+		toshiba_spi_write(0xc6, 0x3232, 2);
+		mdelay(1);
+		toshiba_spi_write(0xc7, 0x6445, 2);
+		mdelay(1);
+		toshiba_spi_write(0xc8, 0x44, 1);
+		toshiba_spi_write(0xc9, 0x52, 1);
+		mdelay(1);
+		toshiba_spi_write(0xca, 0x00, 1);
+		mdelay(1);
+		toshiba_spi_write(0xec, 0x02a4, 2);	/* 0x02a4 */
+		mdelay(1);
+		toshiba_spi_write(0xcf, 0x01, 1);
+		mdelay(1);
+		toshiba_spi_write(0xd0, 0xc003, 2);	/* c003 */
+		mdelay(1);
+		toshiba_spi_write(0xd1, 0x01, 1);
+		mdelay(1);
+		toshiba_spi_write(0xd2, 0x0028, 2);
+		mdelay(1);
+		toshiba_spi_write(0xd3, 0x0028, 2);
+		mdelay(1);
+		toshiba_spi_write(0xd4, 0x26a4, 2);
+		mdelay(1);
+		toshiba_spi_write(0xd5, 0x20, 1);
+		mdelay(1);
+		toshiba_spi_write(0xef, 0x3200, 2);
+		mdelay(32);
+		toshiba_spi_write(0xbc, 0x80, 1);	/* wvga pass through */
+		toshiba_spi_write(0x3b, 0x00, 1);
+		mdelay(1);
+		toshiba_spi_write(0xb0, 0x16, 1);
+		mdelay(1);
+		toshiba_spi_write(0xb8, 0xfff5, 2);
+		mdelay(1);
+		toshiba_spi_write(0x11, 0, 0);
+		mdelay(5);
+		toshiba_spi_write(0x29, 0, 0);
+		mdelay(5);
+		toshiba_state.display_on = TRUE;
+	}
+
+	data = 0;
+	toshiba_spi_read_bytes(0x04, &data, 3);
+	printk(KERN_INFO "toshiba_disp_on: id=%x\n", data);
+
+}
+
+static int lcdc_toshiba_panel_on(struct platform_device *pdev)
+{
+	if (!toshiba_state.disp_initialized) {
+		/* Configure reset GPIO that drives DAC */
+		if (lcdc_toshiba_pdata->panel_config_gpio)
+			lcdc_toshiba_pdata->panel_config_gpio(1);
+		toshiba_disp_powerup();
+		toshiba_disp_on();
+		toshiba_state.disp_initialized = TRUE;
+	}
+	return 0;
+}
+
+static int lcdc_toshiba_panel_off(struct platform_device *pdev)
+{
+	if (toshiba_state.disp_powered_up && toshiba_state.display_on) {
+		/* Main panel power off (Deep standby in) */
+
+		toshiba_spi_write(0x28, 0, 0);	/* display off */
+		mdelay(1);
+		toshiba_spi_write(0xb8, 0x8002, 2);	/* output control */
+		mdelay(1);
+		toshiba_spi_write(0x10, 0x00, 1);	/* sleep mode in */
+		mdelay(85);		/* wait 85 msec */
+		toshiba_spi_write(0xb0, 0x00, 1);	/* deep standby in */
+		mdelay(1);
+		if (lcdc_toshiba_pdata->panel_config_gpio)
+			lcdc_toshiba_pdata->panel_config_gpio(0);
+		toshiba_state.display_on = FALSE;
+		toshiba_state.disp_initialized = FALSE;
+	}
+	return 0;
+}
+
+static void lcdc_toshiba_set_backlight(struct msm_fb_data_type *mfd)
+{
+	int bl_level;
+	int ret = -EPERM;
+	int i = 0;
+
+	bl_level = mfd->bl_level;
+
+	while (i++ < 3) {
+		ret = pmic_set_led_intensity(LED_LCD, bl_level);
+		if (ret == 0)
+			return;
+		msleep(10);
+	}
+
+	printk(KERN_WARNING "%s: can't set lcd backlight!\n",
+				__func__);
+}
+
+static int __devinit toshiba_probe(struct platform_device *pdev)
+{
+	if (pdev->id == 0) {
+		lcdc_toshiba_pdata = pdev->dev.platform_data;
+#ifndef CONFIG_SPI_QSD
+		spi_pin_assign();
+#endif
+		return 0;
+	}
+	msm_fb_add_device(pdev);
+	return 0;
+}
+
+#ifdef CONFIG_SPI_QSD
+static int __devinit lcdc_toshiba_spi_probe(struct spi_device *spi)
+{
+	lcdc_toshiba_spi_client = spi;
+	lcdc_toshiba_spi_client->bits_per_word = 32;
+	return 0;
+}
+static int __devexit lcdc_toshiba_spi_remove(struct spi_device *spi)
+{
+	lcdc_toshiba_spi_client = NULL;
+	return 0;
+}
+
+static struct spi_driver lcdc_toshiba_spi_driver = {
+	.driver = {
+		.name  = LCDC_TOSHIBA_SPI_DEVICE_NAME,
+		.owner = THIS_MODULE,
+	},
+	.probe         = lcdc_toshiba_spi_probe,
+	.remove        = __devexit_p(lcdc_toshiba_spi_remove),
+};
+#endif
+static struct platform_driver this_driver = {
+	.probe  = toshiba_probe,
+	.driver = {
+		.name   = "lcdc_toshiba_wvga",
+	},
+};
+
+static struct msm_fb_panel_data toshiba_panel_data = {
+	.on = lcdc_toshiba_panel_on,
+	.off = lcdc_toshiba_panel_off,
+	.set_backlight = lcdc_toshiba_set_backlight,
+};
+
+static struct platform_device this_device = {
+	.name   = "lcdc_toshiba_wvga",
+	.id	= 1,
+	.dev	= {
+		.platform_data = &toshiba_panel_data,
+	}
+};
+
+static int __init lcdc_toshiba_panel_init(void)
+{
+	int ret;
+	struct msm_panel_info *pinfo;
+#ifdef CONFIG_FB_MSM_TRY_MDDI_CATCH_LCDC_PRISM
+	if (mddi_get_client_id() != 0)
+		return 0;
+
+	ret = msm_fb_detect_client("lcdc_toshiba_wvga_pt");
+	if (ret)
+		return 0;
+
+#endif
+
+	ret = platform_driver_register(&this_driver);
+	if (ret)
+		return ret;
+
+	pinfo = &toshiba_panel_data.panel_info;
+	pinfo->xres = 480;
+	pinfo->yres = 800;
+	MSM_FB_SINGLE_MODE_PANEL(pinfo);
+	pinfo->type = LCDC_PANEL;
+	pinfo->pdest = DISPLAY_1;
+	pinfo->wait_cycle = 0;
+	pinfo->bpp = 18;
+	pinfo->fb_num = 2;
+	/* 30Mhz mdp_lcdc_pclk and mdp_lcdc_pad_pcl */
+	pinfo->clk_rate = 30720000;
+	pinfo->bl_max = 15;
+	pinfo->bl_min = 1;
+
+	pinfo->lcdc.h_back_porch = 184;	/* hsw = 8 + hbp=184 */
+	pinfo->lcdc.h_front_porch = 4;
+	pinfo->lcdc.h_pulse_width = 8;
+	pinfo->lcdc.v_back_porch = 2;	/* vsw=1 + vbp = 2 */
+	pinfo->lcdc.v_front_porch = 3;
+	pinfo->lcdc.v_pulse_width = 1;
+	pinfo->lcdc.border_clr = 0;     /* blk */
+	pinfo->lcdc.underflow_clr = 0xff;       /* blue */
+	pinfo->lcdc.hsync_skew = 0;
+
+	ret = platform_device_register(&this_device);
+	if (ret) {
+		printk(KERN_ERR "%s not able to register the device\n",
+			 __func__);
+		goto fail_driver;
+	}
+#ifdef CONFIG_SPI_QSD
+	ret = spi_register_driver(&lcdc_toshiba_spi_driver);
+
+	if (ret) {
+		printk(KERN_ERR "%s not able to register spi\n", __func__);
+		goto fail_device;
+	}
+#endif
+	return ret;
+
+#ifdef CONFIG_SPI_QSD
+fail_device:
+	platform_device_unregister(&this_device);
+#endif
+fail_driver:
+	platform_driver_unregister(&this_driver);
+	return ret;
+}
+
+device_initcall(lcdc_toshiba_panel_init);
diff --git a/drivers/video/msm/lcdc_truly_ips3p2335.c b/drivers/video/msm/lcdc_truly_ips3p2335.c
new file mode 100644
index 0000000..a4a370e
--- /dev/null
+++ b/drivers/video/msm/lcdc_truly_ips3p2335.c
@@ -0,0 +1,305 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <mach/pmic.h>
+#include "msm_fb.h"
+
+static int prev_bl = 17;
+
+static int spi_cs;
+static int spi_sclk;
+static int spi_mosi;
+static int gpio_backlight_en;
+static int gpio_display_reset;
+
+struct truly_state_type {
+	boolean disp_initialized;
+	boolean display_on;
+	boolean disp_powered_up;
+};
+
+static struct truly_state_type truly_state = { 0 };
+static struct msm_panel_common_pdata *lcdc_truly_pdata;
+
+static char init_item_v1[] = { 0xff, 0x83, 0x57, };
+static char init_item_v2[] = { 0x03, };
+static char init_item_v3[] = { 0x00, 0x13, 0x1C, 0x1C, 0x83, 0x48, };
+static char init_item_v4[] = { 0x43, 0x06, 0x06, 0x06, };
+static char init_item_v5[] = { 0x53, };
+static char init_item_v6[] = { 0x02, 0x40, 0x00, 0x2a, 0x2a, 0x0d, 0x3f, };
+static char init_item_v7[] = { 0x70, 0x50, 0x01, 0x3c, 0xe8, 0x08, };
+static char init_item_v8[] = { 0x17, 0x0f, };
+static char init_item_v9[] = { 0x60};
+static char init_item_v10[] = { 0x00, 0x13, 0x1a, 0x29, 0x2d, 0x41, 0x49,
+				0x52, 0x48, 0x41, 0x3c, 0x33, 0x30, 0x1c,
+				0x19, 0x03, 0x00, 0x13, 0x1a, 0x29, 0x2d,
+				0x41, 0x49, 0x52, 0x48, 0x41, 0x3c, 0x33,
+				0x31, 0x1c, 0x19, 0x03, 0x00, 0x01,
+				};
+static char init_item_v11[] = { 0x40, };
+
+static inline void truly_spi_write_byte(char dc, uint8 data)
+{
+	uint32 bit;
+	int bnum;
+
+	gpio_set_value_cansleep(spi_sclk, 0); /* clk low */
+	/* dc: 0 for command, 1 for parameter */
+	gpio_set_value_cansleep(spi_mosi, dc);
+	udelay(1);	/* at least 20 ns */
+	gpio_set_value_cansleep(spi_sclk, 1); /* clk high */
+	udelay(1);	/* at least 20 ns */
+	bnum = 8;	/* 8 data bits */
+	bit = 0x80;
+	while (bnum) {
+		gpio_set_value_cansleep(spi_sclk, 0); /* clk low */
+		if (data & bit)
+			gpio_set_value_cansleep(spi_mosi, 1);
+		else
+			gpio_set_value_cansleep(spi_mosi, 0);
+		udelay(1);
+		gpio_set_value_cansleep(spi_sclk, 1); /* clk high */
+		udelay(1);
+		bit >>= 1;
+		bnum--;
+	}
+}
+
+static inline int truly_spi_write(char cmd, char *data, int num)
+{
+	int i;
+
+	gpio_set_value_cansleep(spi_cs, 0);	/* cs low */
+	/* command byte first */
+	truly_spi_write_byte(0, cmd);
+	/* followed by parameter bytes */
+	for (i = 0; i < num; i++) {
+		if (data)
+			truly_spi_write_byte(1, data[i]);
+	}
+	gpio_set_value_cansleep(spi_mosi, 1);	/* mosi high */
+	gpio_set_value_cansleep(spi_cs, 1);	/* cs high */
+	udelay(10);
+	return 0;
+}
+
+static void spi_pin_assign(void)
+{
+	/* Setting the Default GPIO's */
+	spi_mosi	= *(lcdc_truly_pdata->gpio_num);
+	spi_sclk	= *(lcdc_truly_pdata->gpio_num + 1);
+	spi_cs		= *(lcdc_truly_pdata->gpio_num + 2);
+	gpio_backlight_en = *(lcdc_truly_pdata->gpio_num + 3);
+	gpio_display_reset = *(lcdc_truly_pdata->gpio_num + 4);
+	pr_debug("spi_mosi:%d spi_sclk:%d spi_cs:%d backlight:%d reset:%d\n",
+		spi_mosi, spi_sclk, spi_cs, gpio_backlight_en,
+		gpio_display_reset);
+
+}
+
+static void truly_disp_powerup(void)
+{
+	/* Reset the hardware first */
+	/* Include DAC power up implementation here */
+	if (!truly_state.disp_powered_up && !truly_state.display_on)
+		truly_state.disp_powered_up = TRUE;
+}
+
+static void truly_disp_reginit(void)
+{
+	pr_debug("%s disp_powered_up:%d display_on:%d\n", __func__,
+			truly_state.disp_powered_up, truly_state.display_on);
+	if (truly_state.disp_powered_up && !truly_state.display_on) {
+		gpio_set_value_cansleep(spi_cs, 1);	/* cs high */
+
+		truly_spi_write(0xb9, init_item_v1, sizeof(init_item_v1));
+		msleep(20);
+		truly_spi_write(0xcc, init_item_v2, sizeof(init_item_v2));
+		truly_spi_write(0xb1, init_item_v3, sizeof(init_item_v3));
+		truly_spi_write(0xb3, init_item_v4, sizeof(init_item_v4));
+		truly_spi_write(0xb6, init_item_v5, sizeof(init_item_v5));
+		truly_spi_write(0xb4, init_item_v6, sizeof(init_item_v6));
+		truly_spi_write(0xc0, init_item_v7, sizeof(init_item_v7));
+		truly_spi_write(0xe3, init_item_v8, sizeof(init_item_v8));
+		truly_spi_write(0x3a, init_item_v9, sizeof(init_item_v9));
+		truly_spi_write(0xe0, init_item_v10, sizeof(init_item_v10));
+		truly_spi_write(0x36, init_item_v11, sizeof(init_item_v11));
+		truly_spi_write(0x11, NULL, 0);
+		msleep(150);
+		truly_spi_write(0x29, NULL, 0);
+		msleep(25);
+
+		truly_state.display_on = TRUE;
+	}
+}
+
+static int lcdc_truly_panel_on(struct platform_device *pdev)
+{
+	/* Configure reset GPIO that drives DAC */
+	if (lcdc_truly_pdata->panel_config_gpio)
+		lcdc_truly_pdata->panel_config_gpio(1);
+	gpio_set_value_cansleep(gpio_display_reset, 1);
+	truly_disp_powerup();
+	truly_disp_reginit();
+	truly_state.disp_initialized = TRUE;
+	return 0;
+}
+
+static int lcdc_truly_panel_off(struct platform_device *pdev)
+{
+	if (truly_state.disp_powered_up && truly_state.display_on) {
+		/* Main panel power off (Pull down reset) */
+		gpio_set_value_cansleep(gpio_display_reset, 0);
+		truly_state.display_on = FALSE;
+		truly_state.disp_initialized = FALSE;
+	}
+	return 0;
+}
+
+static void lcdc_truly_set_backlight(struct msm_fb_data_type *mfd)
+{
+	int step = 0, i = 0;
+	unsigned long flags;
+	int bl_level = mfd->bl_level;
+
+	/* real backlight level, 1 - max, 16 - min, 17 - off */
+	bl_level = 17 - bl_level;
+
+	if (bl_level > prev_bl) {
+		step = bl_level - prev_bl;
+		if (bl_level == 17)
+			step--;
+	} else if (bl_level < prev_bl) {
+		step = bl_level + 16 - prev_bl;
+	} else {
+		pr_info("%s: no change\n", __func__);
+		return;
+	}
+
+	if (bl_level == 17) {
+		/* turn off backlight */
+		gpio_set_value(gpio_backlight_en, 0);
+	} else {
+		local_irq_save(flags);
+
+		if (prev_bl == 17) {
+			/* turn on backlight */
+			gpio_set_value(gpio_backlight_en, 1);
+			udelay(30);
+		}
+
+		/* adjust backlight level */
+		for (i = 0; i < step; i++) {
+			gpio_set_value(gpio_backlight_en, 0);
+			udelay(1);
+			gpio_set_value(gpio_backlight_en, 1);
+			udelay(1);
+		}
+
+		local_irq_restore(flags);
+	}
+	msleep(20);
+	prev_bl = bl_level;
+
+	return;
+}
+
+static int __devinit truly_probe(struct platform_device *pdev)
+{
+
+	if (pdev->id == 0) {
+		lcdc_truly_pdata = pdev->dev.platform_data;
+
+		if (!lcdc_truly_pdata)
+			pr_err("%s pdata is null\n", __func__);
+
+		spi_pin_assign();
+		return 0;
+	}
+	msm_fb_add_device(pdev);
+
+	return 0;
+}
+
+static struct platform_driver this_driver = {
+	.probe  = truly_probe,
+	.driver = {
+		.name   = "lcdc_truly_hvga_ips3p2335_pt",
+	},
+};
+
+static struct msm_fb_panel_data truly_panel_data = {
+	.on = lcdc_truly_panel_on,
+	.off = lcdc_truly_panel_off,
+	.set_backlight = lcdc_truly_set_backlight,
+};
+
+static struct platform_device this_device = {
+	.name   = "lcdc_truly_hvga_ips3p2335_pt",
+	.id	= 1,
+	.dev	= {
+		.platform_data = &truly_panel_data,
+	}
+};
+
+static int __init lcdc_truly_panel_init(void)
+{
+	int ret;
+	struct msm_panel_info *pinfo;
+
+	ret = msm_fb_detect_client("lcdc_truly_hvga_ips3p2335_pt");
+	if (ret)
+		return 0;
+
+	ret = platform_driver_register(&this_driver);
+	if (ret) {
+		pr_err("%s() driver registration failed", __func__);
+		return ret;
+	}
+
+	pinfo = &truly_panel_data.panel_info;
+	pinfo->xres = 320;
+	pinfo->yres = 480;
+	MSM_FB_SINGLE_MODE_PANEL(pinfo);
+	pinfo->type = LCDC_PANEL;
+	pinfo->pdest = DISPLAY_1;
+	pinfo->wait_cycle = 0;
+	pinfo->bpp = 18;
+	pinfo->fb_num = 2;
+	/* 10Mhz mdp_lcdc_pclk and mdp_lcdc_pad_pcl */
+	pinfo->clk_rate = 10240000;
+	pinfo->bl_max = 16;
+	pinfo->bl_min = 1;
+
+	pinfo->lcdc.h_back_porch = 16;		/* hsw = 8 + hbp=16 */
+	pinfo->lcdc.h_front_porch = 4;
+	pinfo->lcdc.h_pulse_width = 8;
+	pinfo->lcdc.v_back_porch = 7;		/* vsw=1 + vbp = 7 */
+	pinfo->lcdc.v_front_porch = 3;
+	pinfo->lcdc.v_pulse_width = 1;
+	pinfo->lcdc.border_clr = 0;		/* blk */
+	pinfo->lcdc.underflow_clr = 0xff;	/* blue */
+	pinfo->lcdc.hsync_skew = 0;
+
+	ret = platform_device_register(&this_device);
+	if (ret) {
+		pr_err("%s not able to register the device\n", __func__);
+		platform_driver_unregister(&this_driver);
+	}
+	return ret;
+}
+
+device_initcall(lcdc_truly_panel_init);
diff --git a/drivers/video/msm/lcdc_wxga.c b/drivers/video/msm/lcdc_wxga.c
new file mode 100644
index 0000000..3204704
--- /dev/null
+++ b/drivers/video/msm/lcdc_wxga.c
@@ -0,0 +1,53 @@
+/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_fb.h"
+
+static int __init lcdc_wxga_init(void)
+{
+	int ret;
+	struct msm_panel_info pinfo;
+
+#ifdef CONFIG_FB_MSM_MDDI_AUTO_DETECT
+	if (msm_fb_detect_client("lcdc_wxga"))
+		return 0;
+#endif
+
+	pinfo.xres = 1280;
+	pinfo.yres = 720;
+	MSM_FB_SINGLE_MODE_PANEL(&pinfo);
+	pinfo.type = LCDC_PANEL;
+	pinfo.pdest = DISPLAY_1;
+	pinfo.wait_cycle = 0;
+	pinfo.bpp = 24;
+	pinfo.fb_num = 2;
+	pinfo.clk_rate = 74250000;
+
+	pinfo.lcdc.h_back_porch = 124;
+	pinfo.lcdc.h_front_porch = 110;
+	pinfo.lcdc.h_pulse_width = 136;
+	pinfo.lcdc.v_back_porch = 19;
+	pinfo.lcdc.v_front_porch = 5;
+	pinfo.lcdc.v_pulse_width = 6;
+	pinfo.lcdc.border_clr = 0;	/* blk */
+	pinfo.lcdc.underflow_clr = 0xff;	/* blue */
+	pinfo.lcdc.hsync_skew = 0;
+
+	ret = lcdc_device_register(&pinfo);
+	if (ret)
+		printk(KERN_ERR "%s: failed to register device!\n", __func__);
+
+	return ret;
+}
+
+module_init(lcdc_wxga_init);
diff --git a/drivers/video/msm/logo.c b/drivers/video/msm/logo.c
new file mode 100644
index 0000000..57d754e
--- /dev/null
+++ b/drivers/video/msm/logo.c
@@ -0,0 +1,103 @@
+/* drivers/video/msm/logo.c
+ *
+ * Show Logo in RLE 565 format
+ *
+ * Copyright (C) 2008 Google Incorporated
+ *
+ * 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/types.h>
+#include <linux/fb.h>
+#include <linux/vt_kern.h>
+#include <linux/unistd.h>
+#include <linux/syscalls.h>
+
+#include <linux/irq.h>
+#include <asm/system.h>
+
+#define fb_width(fb)	((fb)->var.xres)
+#define fb_height(fb)	((fb)->var.yres)
+#define fb_size(fb)	((fb)->var.xres * (fb)->var.yres * 2)
+
+static void memset16(void *_ptr, unsigned short val, unsigned count)
+{
+	unsigned short *ptr = _ptr;
+	count >>= 1;
+	while (count--)
+		*ptr++ = val;
+}
+
+/* 565RLE image format: [count(2 bytes), rle(2 bytes)] */
+int load_565rle_image(char *filename, bool bf_supported)
+{
+	struct fb_info *info;
+	int fd, count, err = 0;
+	unsigned max;
+	unsigned short *data, *bits, *ptr;
+
+	info = registered_fb[0];
+	if (!info) {
+		printk(KERN_WARNING "%s: Can not access framebuffer\n",
+			__func__);
+		return -ENODEV;
+	}
+
+	fd = sys_open(filename, O_RDONLY, 0);
+	if (fd < 0) {
+		printk(KERN_WARNING "%s: Can not open %s\n",
+			__func__, filename);
+		return -ENOENT;
+	}
+	count = sys_lseek(fd, (off_t)0, 2);
+	if (count <= 0) {
+		err = -EIO;
+		goto err_logo_close_file;
+	}
+	sys_lseek(fd, (off_t)0, 0);
+	data = kmalloc(count, GFP_KERNEL);
+	if (!data) {
+		printk(KERN_WARNING "%s: Can not alloc data\n", __func__);
+		err = -ENOMEM;
+		goto err_logo_close_file;
+	}
+	if (sys_read(fd, (char *)data, count) != count) {
+		err = -EIO;
+		goto err_logo_free_data;
+	}
+
+	max = fb_width(info) * fb_height(info);
+	ptr = data;
+	if (bf_supported && (info->node == 1 || info->node == 2)) {
+		err = -EPERM;
+		pr_err("%s:%d no info->creen_base on fb%d!\n",
+		       __func__, __LINE__, info->node);
+		goto err_logo_free_data;
+	}
+	bits = (unsigned short *)(info->screen_base);
+	while (count > 3) {
+		unsigned n = ptr[0];
+		if (n > max)
+			break;
+		memset16(bits, ptr[1], n << 1);
+		bits += n;
+		max -= n;
+		ptr += 2;
+		count -= 4;
+	}
+
+err_logo_free_data:
+	kfree(data);
+err_logo_close_file:
+	sys_close(fd);
+	return err;
+}
+EXPORT_SYMBOL(load_565rle_image);
diff --git a/drivers/video/msm/lvds.c b/drivers/video/msm/lvds.c
new file mode 100644
index 0000000..6323423
--- /dev/null
+++ b/drivers/video/msm/lvds.c
@@ -0,0 +1,373 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <mach/hardware.h>
+#include <mach/msm_iomap.h>
+#include <mach/clk.h>
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <linux/semaphore.h>
+#include <linux/uaccess.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+#include "msm_fb.h"
+#include "mdp4.h"
+static int lvds_probe(struct platform_device *pdev);
+static int lvds_remove(struct platform_device *pdev);
+
+static int lvds_off(struct platform_device *pdev);
+static int lvds_on(struct platform_device *pdev);
+
+static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST];
+static int pdev_list_cnt;
+
+static struct clk *lvds_clk;
+
+static struct platform_driver lvds_driver = {
+	.probe = lvds_probe,
+	.remove = lvds_remove,
+	.suspend = NULL,
+	.resume = NULL,
+	.shutdown = NULL,
+	.driver = {
+		   .name = "lvds",
+		   },
+};
+
+static struct lcdc_platform_data *lvds_pdata;
+
+static void lvds_init(struct msm_fb_data_type *mfd)
+{
+	unsigned int lvds_intf = 0, lvds_phy_cfg0 = 0;
+
+	MDP_OUTP(MDP_BASE + 0xc2034, 0x33);
+	usleep(1000);
+
+	/* LVDS PHY PLL configuration */
+	MDP_OUTP(MDP_BASE + 0xc3004, 0x62);
+	MDP_OUTP(MDP_BASE + 0xc3008, 0x30);
+	MDP_OUTP(MDP_BASE + 0xc300c, 0xc4);
+	MDP_OUTP(MDP_BASE + 0xc3014, 0x10);
+	MDP_OUTP(MDP_BASE + 0xc3018, 0x05);
+	MDP_OUTP(MDP_BASE + 0xc301c, 0x62);
+	MDP_OUTP(MDP_BASE + 0xc3020, 0x41);
+	MDP_OUTP(MDP_BASE + 0xc3024, 0x0d);
+
+	MDP_OUTP(MDP_BASE + 0xc3000, 0x01);
+	/* Wait until LVDS PLL is locked and ready */
+	while (!readl_relaxed(MDP_BASE + 0xc3080))
+		cpu_relax();
+
+	writel_relaxed(0x00, mmss_cc_base + 0x0264);
+	writel_relaxed(0x00, mmss_cc_base + 0x0094);
+
+	writel_relaxed(0x02, mmss_cc_base + 0x00E4);
+
+	writel_relaxed((0x80 | readl_relaxed(mmss_cc_base + 0x00E4)),
+	       mmss_cc_base + 0x00E4);
+	usleep(1000);
+	writel_relaxed((~0x80 & readl_relaxed(mmss_cc_base + 0x00E4)),
+	       mmss_cc_base + 0x00E4);
+
+	writel_relaxed(0x05, mmss_cc_base + 0x0094);
+	writel_relaxed(0x02, mmss_cc_base + 0x0264);
+	/* Wait until LVDS pixel clock output is enabled */
+	mb();
+
+	if (mfd->panel_info.bpp == 24) {
+		if (lvds_pdata &&
+		    lvds_pdata->lvds_pixel_remap &&
+		    lvds_pdata->lvds_pixel_remap()) {
+			/* MDP_LCDC_LVDS_MUX_CTL_FOR_D0_3_TO_0 */
+			MDP_OUTP(MDP_BASE +  0xc2014, 0x05080001);
+			/* MDP_LCDC_LVDS_MUX_CTL_FOR_D0_6_TO_4 */
+			MDP_OUTP(MDP_BASE +  0xc2018, 0x00020304);
+			/* MDP_LCDC_LVDS_MUX_CTL_FOR_D1_3_TO_0 */
+			MDP_OUTP(MDP_BASE +  0xc201c, 0x1011090a);
+			/* MDP_LCDC_LVDS_MUX_CTL_FOR_D1_6_TO_4 */
+			MDP_OUTP(MDP_BASE +  0xc2020, 0x000b0c0d);
+			/* MDP_LCDC_LVDS_MUX_CTL_FOR_D2_3_TO_0 */
+			MDP_OUTP(MDP_BASE +  0xc2024, 0x191a1213);
+			/* MDP_LCDC_LVDS_MUX_CTL_FOR_D2_6_TO_4 */
+			MDP_OUTP(MDP_BASE +  0xc2028, 0x00141518);
+			/* MDP_LCDC_LVDS_MUX_CTL_FOR_D3_3_TO_0 */
+			MDP_OUTP(MDP_BASE +  0xc202c, 0x171b0607);
+			/* MDP_LCDC_LVDS_MUX_CTL_FOR_D3_6_TO_4 */
+			MDP_OUTP(MDP_BASE +  0xc2030, 0x000e0f16);
+		} else {
+			/* MDP_LCDC_LVDS_MUX_CTL_FOR_D0_3_TO_0 */
+			MDP_OUTP(MDP_BASE +  0xc2014, 0x03040508);
+			/* MDP_LCDC_LVDS_MUX_CTL_FOR_D0_6_TO_4 */
+			MDP_OUTP(MDP_BASE +  0xc2018, 0x00000102);
+			/* MDP_LCDC_LVDS_MUX_CTL_FOR_D1_3_TO_0 */
+			MDP_OUTP(MDP_BASE +  0xc201c, 0x0c0d1011);
+			/* MDP_LCDC_LVDS_MUX_CTL_FOR_D1_6_TO_4 */
+			MDP_OUTP(MDP_BASE +  0xc2020, 0x00090a0b);
+			/* MDP_LCDC_LVDS_MUX_CTL_FOR_D2_3_TO_0 */
+			MDP_OUTP(MDP_BASE +  0xc2024, 0x151a191a);
+			/* MDP_LCDC_LVDS_MUX_CTL_FOR_D2_6_TO_4 */
+			MDP_OUTP(MDP_BASE +  0xc2028, 0x00121314);
+			/* MDP_LCDC_LVDS_MUX_CTL_FOR_D3_3_TO_0 */
+			MDP_OUTP(MDP_BASE +  0xc202c, 0x0f16171b);
+			/* MDP_LCDC_LVDS_MUX_CTL_FOR_D3_6_TO_4 */
+			MDP_OUTP(MDP_BASE +  0xc2030, 0x0006070e);
+		}
+		if (mfd->panel_info.lvds.channel_mode ==
+			LVDS_DUAL_CHANNEL_MODE) {
+			lvds_intf = 0x0001ff80;
+			lvds_phy_cfg0 = BIT(6) | BIT(7);
+			if (mfd->panel_info.lvds.channel_swap)
+				lvds_intf |= BIT(4);
+		} else {
+			lvds_intf = 0x00010f84;
+			lvds_phy_cfg0 = BIT(6);
+		}
+	} else if (mfd->panel_info.bpp == 18) {
+		/* MDP_LCDC_LVDS_MUX_CTL_FOR_D0_3_TO_0 */
+		MDP_OUTP(MDP_BASE +  0xc2014, 0x03040508);
+		/* MDP_LCDC_LVDS_MUX_CTL_FOR_D0_6_TO_4 */
+		MDP_OUTP(MDP_BASE +  0xc2018, 0x00000102);
+		/* MDP_LCDC_LVDS_MUX_CTL_FOR_D1_3_TO_0 */
+		MDP_OUTP(MDP_BASE +  0xc201c, 0x0c0d1011);
+		/* MDP_LCDC_LVDS_MUX_CTL_FOR_D1_6_TO_4 */
+		MDP_OUTP(MDP_BASE +  0xc2020, 0x00090a0b);
+		/* MDP_LCDC_LVDS_MUX_CTL_FOR_D2_3_TO_0 */
+		MDP_OUTP(MDP_BASE +  0xc2024, 0x1518191a);
+		/* MDP_LCDC_LVDS_MUX_CTL_FOR_D2_6_TO_4 */
+		MDP_OUTP(MDP_BASE +  0xc2028, 0x00121314);
+
+		if (mfd->panel_info.lvds.channel_mode ==
+			LVDS_DUAL_CHANNEL_MODE) {
+			lvds_intf = 0x00017788;
+			lvds_phy_cfg0 = BIT(6) | BIT(7);
+			if (mfd->panel_info.lvds.channel_swap)
+				lvds_intf |= BIT(4);
+		} else {
+			lvds_intf = 0x0001078c;
+			lvds_phy_cfg0 = BIT(6);
+		}
+	} else {
+		BUG();
+	}
+
+	/* MDP_LVDSPHY_CFG0 */
+	MDP_OUTP(MDP_BASE +  0xc3100, lvds_phy_cfg0);
+	/* MDP_LCDC_LVDS_INTF_CTL */
+	MDP_OUTP(MDP_BASE +  0xc2000, lvds_intf);
+	MDP_OUTP(MDP_BASE +  0xc3108, 0x30);
+	lvds_phy_cfg0 |= BIT(4);
+
+	/* Wait until LVDS PHY registers are configured */
+	mb();
+	usleep(1);
+	/* MDP_LVDSPHY_CFG0, enable serialization */
+	MDP_OUTP(MDP_BASE +  0xc3100, lvds_phy_cfg0);
+}
+
+static int lvds_off(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct msm_fb_data_type *mfd;
+
+	mfd = platform_get_drvdata(pdev);
+	ret = panel_next_off(pdev);
+
+	if (lvds_clk)
+		clk_disable_unprepare(lvds_clk);
+
+	MDP_OUTP(MDP_BASE +  0xc3100, 0x0);
+	MDP_OUTP(MDP_BASE + 0xc3000, 0x0);
+	usleep(10);
+
+	if (lvds_pdata && lvds_pdata->lcdc_power_save)
+		lvds_pdata->lcdc_power_save(0);
+
+	if (lvds_pdata && lvds_pdata->lcdc_gpio_config)
+		ret = lvds_pdata->lcdc_gpio_config(0);
+
+#ifdef CONFIG_MSM_BUS_SCALING
+	mdp_bus_scale_update_request(0);
+#endif
+
+	return ret;
+}
+
+static int lvds_on(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct msm_fb_data_type *mfd;
+	unsigned long panel_pixclock_freq = 0;
+	mfd = platform_get_drvdata(pdev);
+
+	if (lvds_pdata && lvds_pdata->lcdc_get_clk)
+		panel_pixclock_freq = lvds_pdata->lcdc_get_clk();
+
+	if (!panel_pixclock_freq)
+		panel_pixclock_freq = mfd->fbi->var.pixclock;
+#ifdef CONFIG_MSM_BUS_SCALING
+	mdp_bus_scale_update_request(2);
+#endif
+	mfd = platform_get_drvdata(pdev);
+
+	if (lvds_clk) {
+		mfd->fbi->var.pixclock = clk_round_rate(lvds_clk,
+			mfd->fbi->var.pixclock);
+		ret = clk_set_rate(lvds_clk, mfd->fbi->var.pixclock);
+		if (ret) {
+			pr_err("%s: Can't set lvds clock to rate %u\n",
+				__func__, mfd->fbi->var.pixclock);
+			goto out;
+		}
+		clk_prepare_enable(lvds_clk);
+	}
+
+	if (lvds_pdata && lvds_pdata->lcdc_power_save)
+		lvds_pdata->lcdc_power_save(1);
+	if (lvds_pdata && lvds_pdata->lcdc_gpio_config)
+		ret = lvds_pdata->lcdc_gpio_config(1);
+
+	lvds_init(mfd);
+	ret = panel_next_on(pdev);
+
+out:
+	return ret;
+}
+
+static int lvds_probe(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+	struct fb_info *fbi;
+	struct platform_device *mdp_dev = NULL;
+	struct msm_fb_panel_data *pdata = NULL;
+	int rc;
+
+	if (pdev->id == 0) {
+		lvds_pdata = pdev->dev.platform_data;
+
+		lvds_clk = clk_get(&pdev->dev, "lvds_clk");
+		if (IS_ERR_OR_NULL(lvds_clk)) {
+			pr_err("Couldnt find lvds_clk\n");
+			lvds_clk = NULL;
+		}
+		return 0;
+	}
+
+	mfd = platform_get_drvdata(pdev);
+
+	if (!mfd)
+		return -ENODEV;
+
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST)
+		return -ENOMEM;
+
+	mdp_dev = platform_device_alloc("mdp", pdev->id);
+	if (!mdp_dev)
+		return -ENOMEM;
+
+	/*
+	 * link to the latest pdev
+	 */
+	mfd->pdev = mdp_dev;
+	mfd->dest = DISPLAY_LCDC;
+
+	/*
+	 * alloc panel device data
+	 */
+	if (platform_device_add_data
+	    (mdp_dev, pdev->dev.platform_data,
+	     sizeof(struct msm_fb_panel_data))) {
+		pr_err("lvds_probe: platform_device_add_data failed!\n");
+		platform_device_put(mdp_dev);
+		return -ENOMEM;
+	}
+	/*
+	 * data chain
+	 */
+	pdata = (struct msm_fb_panel_data *)mdp_dev->dev.platform_data;
+	pdata->on = lvds_on;
+	pdata->off = lvds_off;
+	pdata->next = pdev;
+
+	/*
+	 * get/set panel specific fb info
+	 */
+	mfd->panel_info = pdata->panel_info;
+
+	if (mfd->index == 0)
+		mfd->fb_imgType = MSMFB_DEFAULT_TYPE;
+	else
+		mfd->fb_imgType = MDP_RGB_565;
+
+	fbi = mfd->fbi;
+	if (lvds_clk) {
+		fbi->var.pixclock = clk_round_rate(lvds_clk,
+			mfd->panel_info.clk_rate);
+	}
+
+	fbi->var.left_margin = mfd->panel_info.lcdc.h_back_porch;
+	fbi->var.right_margin = mfd->panel_info.lcdc.h_front_porch;
+	fbi->var.upper_margin = mfd->panel_info.lcdc.v_back_porch;
+	fbi->var.lower_margin = mfd->panel_info.lcdc.v_front_porch;
+	fbi->var.hsync_len = mfd->panel_info.lcdc.h_pulse_width;
+	fbi->var.vsync_len = mfd->panel_info.lcdc.v_pulse_width;
+
+	/*
+	 * set driver data
+	 */
+	platform_set_drvdata(mdp_dev, mfd);
+	/*
+	 * register in mdp driver
+	 */
+	rc = platform_device_add(mdp_dev);
+	if (rc)
+		goto lvds_probe_err;
+
+	pdev_list[pdev_list_cnt++] = pdev;
+
+	return 0;
+
+lvds_probe_err:
+	platform_device_put(mdp_dev);
+	return rc;
+}
+
+static int lvds_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static int lvds_register_driver(void)
+{
+	return platform_driver_register(&lvds_driver);
+}
+
+static int __init lvds_driver_init(void)
+{
+	return lvds_register_driver();
+}
+
+module_init(lvds_driver_init);
diff --git a/drivers/video/msm/lvds_chimei_wxga.c b/drivers/video/msm/lvds_chimei_wxga.c
new file mode 100644
index 0000000..9a385b9
--- /dev/null
+++ b/drivers/video/msm/lvds_chimei_wxga.c
@@ -0,0 +1,167 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "msm_fb.h"
+#include <linux/pwm.h>
+#include <linux/mfd/pm8xxx/pm8921.h>
+
+#define LVDS_CHIMEI_PWM_FREQ_HZ 300
+#define LVDS_CHIMEI_PWM_PERIOD_USEC (USEC_PER_SEC / LVDS_CHIMEI_PWM_FREQ_HZ)
+#define LVDS_CHIMEI_PWM_LEVEL 255
+#define LVDS_CHIMEI_PWM_DUTY_LEVEL \
+	(LVDS_CHIMEI_PWM_PERIOD_USEC / LVDS_CHIMEI_PWM_LEVEL)
+
+
+static struct lvds_panel_platform_data *cm_pdata;
+static struct platform_device *cm_fbpdev;
+static struct pwm_device *bl_lpm;
+
+static int lvds_chimei_panel_on(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static int lvds_chimei_panel_off(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static void lvds_chimei_set_backlight(struct msm_fb_data_type *mfd)
+{
+	int ret;
+
+	pr_debug("%s: back light level %d\n", __func__, mfd->bl_level);
+
+	if (bl_lpm) {
+		ret = pwm_config(bl_lpm, LVDS_CHIMEI_PWM_DUTY_LEVEL *
+			mfd->bl_level, LVDS_CHIMEI_PWM_PERIOD_USEC);
+		if (ret) {
+			pr_err("pwm_config on lpm failed %d\n", ret);
+			return;
+		}
+		if (mfd->bl_level) {
+			ret = pwm_enable(bl_lpm);
+			if (ret)
+				pr_err("pwm enable/disable on lpm failed"
+					"for bl %d\n",	mfd->bl_level);
+		} else {
+			pwm_disable(bl_lpm);
+		}
+	}
+}
+
+static int __devinit lvds_chimei_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+
+	if (pdev->id == 0) {
+		cm_pdata = pdev->dev.platform_data;
+		if (cm_pdata == NULL)
+			pr_err("%s: no PWM gpio specified\n", __func__);
+		return 0;
+	}
+
+	if (cm_pdata != NULL)
+		bl_lpm = pwm_request(cm_pdata->gpio[0],
+			"backlight");
+
+	if (bl_lpm == NULL || IS_ERR(bl_lpm)) {
+		pr_err("%s pwm_request() failed\n", __func__);
+		bl_lpm = NULL;
+	}
+	pr_debug("bl_lpm = %p lpm = %d\n", bl_lpm,
+		cm_pdata->gpio[0]);
+
+	cm_fbpdev = msm_fb_add_device(pdev);
+	if (!cm_fbpdev) {
+		dev_err(&pdev->dev, "failed to add msm_fb device\n");
+		rc = -ENODEV;
+		goto probe_exit;
+	}
+
+probe_exit:
+	return rc;
+}
+
+static struct platform_driver this_driver = {
+	.probe  = lvds_chimei_probe,
+	.driver = {
+		.name   = "lvds_chimei_wxga",
+	},
+};
+
+static struct msm_fb_panel_data lvds_chimei_panel_data = {
+	.on = lvds_chimei_panel_on,
+	.off = lvds_chimei_panel_off,
+	.set_backlight = lvds_chimei_set_backlight,
+};
+
+static struct platform_device this_device = {
+	.name   = "lvds_chimei_wxga",
+	.id	= 1,
+	.dev	= {
+		.platform_data = &lvds_chimei_panel_data,
+	}
+};
+
+static int __init lvds_chimei_wxga_init(void)
+{
+	int ret;
+	struct msm_panel_info *pinfo;
+
+	if (msm_fb_detect_client("lvds_chimei_wxga"))
+		return 0;
+
+	ret = platform_driver_register(&this_driver);
+	if (ret)
+		return ret;
+
+	pinfo = &lvds_chimei_panel_data.panel_info;
+	pinfo->xres = 1366;
+	pinfo->yres = 768;
+	MSM_FB_SINGLE_MODE_PANEL(pinfo);
+	pinfo->type = LVDS_PANEL;
+	pinfo->pdest = DISPLAY_1;
+	pinfo->wait_cycle = 0;
+	pinfo->bpp = 24;
+	pinfo->fb_num = 2;
+	pinfo->clk_rate = 75000000;
+	pinfo->bl_max = 255;
+	pinfo->bl_min = 1;
+
+	/*
+	 * this panel is operated by de,
+	 * vsycn and hsync are ignored
+	 */
+	pinfo->lcdc.h_back_porch = 0;
+	pinfo->lcdc.h_front_porch = 194;
+	pinfo->lcdc.h_pulse_width = 40;
+	pinfo->lcdc.v_back_porch = 0;
+	pinfo->lcdc.v_front_porch = 38;
+	pinfo->lcdc.v_pulse_width = 20;
+	pinfo->lcdc.underflow_clr = 0xff;
+	pinfo->lcdc.hsync_skew = 0;
+	pinfo->lvds.channel_mode = LVDS_SINGLE_CHANNEL_MODE;
+
+	/* Set border color, padding only for reducing active display region */
+	pinfo->lcdc.border_clr = 0x0;
+	pinfo->lcdc.xres_pad = 0;
+	pinfo->lcdc.yres_pad = 0;
+
+	ret = platform_device_register(&this_device);
+	if (ret)
+		platform_driver_unregister(&this_driver);
+
+	return ret;
+}
+
+module_init(lvds_chimei_wxga_init);
diff --git a/drivers/video/msm/mddi.c b/drivers/video/msm/mddi.c
index b061d70..1154913 100644
--- a/drivers/video/msm/mddi.c
+++ b/drivers/video/msm/mddi.c
@@ -2,7 +2,7 @@
  * MSM MDDI Transport
  *
  * Copyright (C) 2007 Google Incorporated
- * Copyright (C) 2007 QUALCOMM Incorporated
+ * Copyright (c) 2007-2012, Code Aurora Forum. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -10,816 +10,581 @@
  *
  * 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
  */
 
 #include <linux/module.h>
 #include <linux/kernel.h>
-#include <linux/dma-mapping.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/delay.h>
-#include <linux/gfp.h>
-#include <linux/spinlock.h>
-#include <linux/clk.h>
-#include <linux/io.h>
 #include <linux/sched.h>
-#include <mach/msm_iomap.h>
-#include <mach/irqs.h>
-#include <mach/board.h>
-#include <mach/msm_fb.h>
-#include "mddi_hw.h"
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <mach/hardware.h>
+#include <asm/io.h>
 
-#define FLAG_DISABLE_HIBERNATION 0x0001
-#define FLAG_HAVE_CAPS		 0x0002
-#define FLAG_HAS_VSYNC_IRQ	 0x0004
-#define FLAG_HAVE_STATUS	 0x0008
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <linux/semaphore.h>
+#include <linux/uaccess.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include "msm_fb.h"
+#include "mddihosti.h"
+#include "mddihost.h"
+#include <mach/gpio.h>
+#include <mach/clk.h>
 
-#define CMD_GET_CLIENT_CAP     0x0601
-#define CMD_GET_CLIENT_STATUS  0x0602
+static int mddi_probe(struct platform_device *pdev);
+static int mddi_remove(struct platform_device *pdev);
 
-union mddi_rev {
-	unsigned char raw[MDDI_REV_BUFFER_SIZE];
-	struct mddi_rev_packet hdr;
-	struct mddi_client_status status;
-	struct mddi_client_caps caps;
-	struct mddi_register_access reg;
-};
+static int mddi_off(struct platform_device *pdev);
+static int mddi_on(struct platform_device *pdev);
 
-struct reg_read_info {
-	struct completion done;
-	uint32_t reg;
-	uint32_t status;
-	uint32_t result;
-};
+#ifdef CONFIG_PM
+static int mddi_suspend(struct platform_device *pdev, pm_message_t state);
+static int mddi_resume(struct platform_device *pdev);
+#endif
 
-struct mddi_info {
-	uint16_t flags;
-	uint16_t version;
-	char __iomem *base;
-	int irq;
-	struct clk *clk;
-	struct msm_mddi_client_data client_data;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void mddi_early_suspend(struct early_suspend *h);
+static void mddi_early_resume(struct early_suspend *h);
+#endif
 
-	/* buffer for rev encap packets */
-	void *rev_data;
-	dma_addr_t rev_addr;
-	struct mddi_llentry *reg_write_data;
-	dma_addr_t reg_write_addr;
-	struct mddi_llentry *reg_read_data;
-	dma_addr_t reg_read_addr;
-	size_t rev_data_curr;
+static void pmdh_clk_disable(void);
+static void pmdh_clk_enable(void);
+static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST];
+static int pdev_list_cnt;
+static struct clk *mddi_clk;
+static struct clk *mddi_pclk;
+static struct mddi_platform_data *mddi_pdata;
 
-	spinlock_t int_lock;
-	uint32_t int_enable;
-	uint32_t got_int;
-	wait_queue_head_t int_wait;
+DEFINE_MUTEX(mddi_timer_lock);
 
-	struct mutex reg_write_lock;
-	struct mutex reg_read_lock;
-	struct reg_read_info *reg_read;
-
-	struct mddi_client_caps caps;
-	struct mddi_client_status status;
-
-	void (*power_client)(struct msm_mddi_client_data *, int);
-
-	/* client device published to bind us to the
-	 * appropriate mddi_client driver
-	 */
-	char client_name[20];
-
-	struct platform_device client_pdev;
-};
-
-static void mddi_init_rev_encap(struct mddi_info *mddi);
-
-#define mddi_readl(r) readl(mddi->base + (MDDI_##r))
-#define mddi_writel(v, r) writel((v), mddi->base + (MDDI_##r))
-
-void mddi_activate_link(struct msm_mddi_client_data *cdata)
+static int mddi_runtime_suspend(struct device *dev)
 {
-	struct mddi_info *mddi = container_of(cdata, struct mddi_info,
-					      client_data);
-
-	mddi_writel(MDDI_CMD_LINK_ACTIVE, CMD);
-}
-
-static void mddi_handle_link_list_done(struct mddi_info *mddi)
-{
-}
-
-static void mddi_reset_rev_encap_ptr(struct mddi_info *mddi)
-{
-	printk(KERN_INFO "mddi: resetting rev ptr\n");
-	mddi->rev_data_curr = 0;
-	mddi_writel(mddi->rev_addr, REV_PTR);
-	mddi_writel(mddi->rev_addr, REV_PTR);
-	mddi_writel(MDDI_CMD_FORCE_NEW_REV_PTR, CMD);
-}
-
-static void mddi_handle_rev_data(struct mddi_info *mddi, union mddi_rev *rev)
-{
-	int i;
-	struct reg_read_info *ri;
-
-	if ((rev->hdr.length <= MDDI_REV_BUFFER_SIZE - 2) &&
-	   (rev->hdr.length >= sizeof(struct mddi_rev_packet) - 2)) {
-
-		switch (rev->hdr.type) {
-		case TYPE_CLIENT_CAPS:
-			memcpy(&mddi->caps, &rev->caps,
-			       sizeof(struct mddi_client_caps));
-			mddi->flags |= FLAG_HAVE_CAPS;
-			wake_up(&mddi->int_wait);
-			break;
-		case TYPE_CLIENT_STATUS:
-			memcpy(&mddi->status, &rev->status,
-			       sizeof(struct mddi_client_status));
-			mddi->flags |= FLAG_HAVE_STATUS;
-			wake_up(&mddi->int_wait);
-			break;
-		case TYPE_REGISTER_ACCESS:
-			ri = mddi->reg_read;
-			if (ri == 0) {
-				printk(KERN_INFO "rev: got reg %x = %x without "
-						 " pending read\n",
-				       rev->reg.register_address,
-				       rev->reg.register_data_list);
-				break;
-			}
-			if (ri->reg != rev->reg.register_address) {
-				printk(KERN_INFO "rev: got reg %x = %x for "
-						 "wrong register, expected "
-						 "%x\n",
-				       rev->reg.register_address,
-				       rev->reg.register_data_list, ri->reg);
-				break;
-			}
-			mddi->reg_read = NULL;
-			ri->status = 0;
-			ri->result = rev->reg.register_data_list;
-			complete(&ri->done);
-			break;
-		default:
-			printk(KERN_INFO "rev: unknown reverse packet: "
-					 "len=%04x type=%04x CURR_REV_PTR=%x\n",
-			       rev->hdr.length, rev->hdr.type,
-			       mddi_readl(CURR_REV_PTR));
-			for (i = 0; i < rev->hdr.length + 2; i++) {
-				if ((i % 16) == 0)
-					printk(KERN_INFO "\n");
-				printk(KERN_INFO " %02x", rev->raw[i]);
-			}
-			printk(KERN_INFO "\n");
-			mddi_reset_rev_encap_ptr(mddi);
-		}
-	} else {
-		printk(KERN_INFO "bad rev length, %d, CURR_REV_PTR %x\n",
-		       rev->hdr.length, mddi_readl(CURR_REV_PTR));
-		mddi_reset_rev_encap_ptr(mddi);
-	}
-}
-
-static void mddi_wait_interrupt(struct mddi_info *mddi, uint32_t intmask);
-
-static void mddi_handle_rev_data_avail(struct mddi_info *mddi)
-{
-	uint32_t rev_data_count;
-	uint32_t rev_crc_err_count;
-	struct reg_read_info *ri;
-	size_t prev_offset;
-	uint16_t length;
-
-	union mddi_rev *crev = mddi->rev_data + mddi->rev_data_curr;
-
-	/* clear the interrupt */
-	mddi_writel(MDDI_INT_REV_DATA_AVAIL, INT);
-	rev_data_count = mddi_readl(REV_PKT_CNT);
-	rev_crc_err_count = mddi_readl(REV_CRC_ERR);
-	if (rev_data_count > 1)
-		printk(KERN_INFO "rev_data_count %d\n", rev_data_count);
-
-	if (rev_crc_err_count) {
-		printk(KERN_INFO "rev_crc_err_count %d, INT %x\n",
-		       rev_crc_err_count,  mddi_readl(INT));
-		ri = mddi->reg_read;
-		if (ri == 0) {
-			printk(KERN_INFO "rev: got crc error without pending "
-			       "read\n");
-		} else {
-			mddi->reg_read = NULL;
-			ri->status = -EIO;
-			ri->result = -1;
-			complete(&ri->done);
-		}
-	}
-
-	if (rev_data_count == 0)
-		return;
-
-	prev_offset = mddi->rev_data_curr;
-
-	length = *((uint8_t *)mddi->rev_data + mddi->rev_data_curr);
-	mddi->rev_data_curr++;
-	if (mddi->rev_data_curr == MDDI_REV_BUFFER_SIZE)
-		mddi->rev_data_curr = 0;
-	length += *((uint8_t *)mddi->rev_data + mddi->rev_data_curr) << 8;
-	mddi->rev_data_curr += 1 + length;
-	if (mddi->rev_data_curr >= MDDI_REV_BUFFER_SIZE)
-		mddi->rev_data_curr =
-			mddi->rev_data_curr % MDDI_REV_BUFFER_SIZE;
-
-	if (length > MDDI_REV_BUFFER_SIZE - 2) {
-		printk(KERN_INFO "mddi: rev data length greater than buffer"
-			"size\n");
-		mddi_reset_rev_encap_ptr(mddi);
-		return;
-	}
-
-	if (prev_offset + 2 + length >= MDDI_REV_BUFFER_SIZE) {
-		union mddi_rev tmprev;
-		size_t rem = MDDI_REV_BUFFER_SIZE - prev_offset;
-		memcpy(&tmprev.raw[0], mddi->rev_data + prev_offset, rem);
-		memcpy(&tmprev.raw[rem], mddi->rev_data, 2 + length - rem);
-		mddi_handle_rev_data(mddi, &tmprev);
-	} else {
-		mddi_handle_rev_data(mddi, crev);
-	}
-
-	if (prev_offset < MDDI_REV_BUFFER_SIZE / 2 &&
-	    mddi->rev_data_curr >= MDDI_REV_BUFFER_SIZE / 2) {
-		mddi_writel(mddi->rev_addr, REV_PTR);
-	}
-}
-
-static irqreturn_t mddi_isr(int irq, void *data)
-{
-	struct msm_mddi_client_data *cdata = data;
-	struct mddi_info *mddi = container_of(cdata, struct mddi_info,
-					      client_data);
-	uint32_t active, status;
-
-	spin_lock(&mddi->int_lock);
-
-	active = mddi_readl(INT);
-	status = mddi_readl(STAT);
-
-	mddi_writel(active, INT);
-
-	/* ignore any interrupts we have disabled */
-	active &= mddi->int_enable;
-
-	mddi->got_int |= active;
-	wake_up(&mddi->int_wait);
-
-	if (active & MDDI_INT_PRI_LINK_LIST_DONE) {
-		mddi->int_enable &= (~MDDI_INT_PRI_LINK_LIST_DONE);
-		mddi_handle_link_list_done(mddi);
-	}
-	if (active & MDDI_INT_REV_DATA_AVAIL)
-		mddi_handle_rev_data_avail(mddi);
-
-	if (active & ~MDDI_INT_NEED_CLEAR)
-		mddi->int_enable &= ~(active & ~MDDI_INT_NEED_CLEAR);
-
-	if (active & MDDI_INT_LINK_ACTIVE) {
-		mddi->int_enable &= (~MDDI_INT_LINK_ACTIVE);
-		mddi->int_enable |= MDDI_INT_IN_HIBERNATION;
-	}
-
-	if (active & MDDI_INT_IN_HIBERNATION) {
-		mddi->int_enable &= (~MDDI_INT_IN_HIBERNATION);
-		mddi->int_enable |= MDDI_INT_LINK_ACTIVE;
-	}
-
-	mddi_writel(mddi->int_enable, INTEN);
-	spin_unlock(&mddi->int_lock);
-
-	return IRQ_HANDLED;
-}
-
-static long mddi_wait_interrupt_timeout(struct mddi_info *mddi,
-					uint32_t intmask, int timeout)
-{
-	unsigned long irq_flags;
-
-	spin_lock_irqsave(&mddi->int_lock, irq_flags);
-	mddi->got_int &= ~intmask;
-	mddi->int_enable |= intmask;
-	mddi_writel(mddi->int_enable, INTEN);
-	spin_unlock_irqrestore(&mddi->int_lock, irq_flags);
-	return wait_event_timeout(mddi->int_wait, mddi->got_int & intmask,
-				  timeout);
-}
-
-static void mddi_wait_interrupt(struct mddi_info *mddi, uint32_t intmask)
-{
-	if (mddi_wait_interrupt_timeout(mddi, intmask, HZ/10) == 0)
-		printk(KERN_INFO "mddi_wait_interrupt %d, timeout "
-		       "waiting for %x, INT = %x, STAT = %x gotint = %x\n",
-		       current->pid, intmask, mddi_readl(INT), mddi_readl(STAT),
-		       mddi->got_int);
-}
-
-static void mddi_init_rev_encap(struct mddi_info *mddi)
-{
-	memset(mddi->rev_data, 0xee, MDDI_REV_BUFFER_SIZE);
-	mddi_writel(mddi->rev_addr, REV_PTR);
-	mddi_writel(MDDI_CMD_FORCE_NEW_REV_PTR, CMD);
-	mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-}
-
-void mddi_set_auto_hibernate(struct msm_mddi_client_data *cdata, int on)
-{
-	struct mddi_info *mddi = container_of(cdata, struct mddi_info,
-					      client_data);
-	mddi_writel(MDDI_CMD_POWERDOWN, CMD);
-	mddi_wait_interrupt(mddi, MDDI_INT_IN_HIBERNATION);
-	mddi_writel(MDDI_CMD_HIBERNATE | !!on, CMD);
-	mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-}
-
-
-static uint16_t mddi_init_registers(struct mddi_info *mddi)
-{
-	mddi_writel(0x0001, VERSION);
-	mddi_writel(MDDI_HOST_BYTES_PER_SUBFRAME, BPS);
-	mddi_writel(0x0003, SPM); /* subframes per media */
-	mddi_writel(0x0005, TA1_LEN);
-	mddi_writel(MDDI_HOST_TA2_LEN, TA2_LEN);
-	mddi_writel(0x0096, DRIVE_HI);
-	/* 0x32 normal, 0x50 for Toshiba display */
-	mddi_writel(0x0050, DRIVE_LO);
-	mddi_writel(0x003C, DISP_WAKE); /* wakeup counter */
-	mddi_writel(MDDI_HOST_REV_RATE_DIV, REV_RATE_DIV);
-
-	mddi_writel(MDDI_REV_BUFFER_SIZE, REV_SIZE);
-	mddi_writel(MDDI_MAX_REV_PKT_SIZE, REV_ENCAP_SZ);
-
-	/* disable periodic rev encap */
-	mddi_writel(MDDI_CMD_PERIODIC_REV_ENCAP, CMD);
-	mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-
-	if (mddi_readl(PAD_CTL) == 0) {
-		/* If we are turning on band gap, need to wait 5us before
-		 * turning on the rest of the PAD */
-		mddi_writel(0x08000, PAD_CTL);
-		udelay(5);
-	}
-
-	/* Recommendation from PAD hw team */
-	mddi_writel(0xa850f, PAD_CTL);
-
-
-	/* Need an even number for counts */
-	mddi_writel(0x60006, DRIVER_START_CNT);
-
-	mddi_set_auto_hibernate(&mddi->client_data, 0);
-
-	mddi_writel(MDDI_CMD_DISP_IGNORE, CMD);
-	mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-
-	mddi_init_rev_encap(mddi);
-	return mddi_readl(CORE_VER) & 0xffff;
-}
-
-static void mddi_suspend(struct msm_mddi_client_data *cdata)
-{
-	struct mddi_info *mddi = container_of(cdata, struct mddi_info,
-					      client_data);
-	/* turn off the client */
-	if (mddi->power_client)
-		mddi->power_client(&mddi->client_data, 0);
-	/* turn off the link */
-	mddi_writel(MDDI_CMD_RESET, CMD);
-	mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-	/* turn off the clock */
-	clk_disable(mddi->clk);
-}
-
-static void mddi_resume(struct msm_mddi_client_data *cdata)
-{
-	struct mddi_info *mddi = container_of(cdata, struct mddi_info,
-					      client_data);
-	mddi_set_auto_hibernate(&mddi->client_data, 0);
-	/* turn on the client */
-	if (mddi->power_client)
-		mddi->power_client(&mddi->client_data, 1);
-	/* turn on the clock */
-	clk_enable(mddi->clk);
-	/* set up the local registers */
-	mddi->rev_data_curr = 0;
-	mddi_init_registers(mddi);
-	mddi_writel(mddi->int_enable, INTEN);
-	mddi_writel(MDDI_CMD_LINK_ACTIVE, CMD);
-	mddi_writel(MDDI_CMD_SEND_RTD, CMD);
-	mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-	mddi_set_auto_hibernate(&mddi->client_data, 1);
-}
-
-static int __devinit mddi_get_client_caps(struct mddi_info *mddi)
-{
-	int i, j;
-
-	/* clear any stale interrupts */
-	mddi_writel(0xffffffff, INT);
-
-	mddi->int_enable = MDDI_INT_LINK_ACTIVE |
-			   MDDI_INT_IN_HIBERNATION |
-			   MDDI_INT_PRI_LINK_LIST_DONE |
-			   MDDI_INT_REV_DATA_AVAIL |
-			   MDDI_INT_REV_OVERFLOW |
-			   MDDI_INT_REV_OVERWRITE |
-			   MDDI_INT_RTD_FAILURE;
-	mddi_writel(mddi->int_enable, INTEN);
-
-	mddi_writel(MDDI_CMD_LINK_ACTIVE, CMD);
-	mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-
-	for (j = 0; j < 3; j++) {
-		/* the toshiba vga panel does not respond to get
-		 * caps unless you SEND_RTD, but the first SEND_RTD
-		 * will fail...
-		 */
-		for (i = 0; i < 4; i++) {
-			uint32_t stat;
-
-			mddi_writel(MDDI_CMD_SEND_RTD, CMD);
-			mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-			stat = mddi_readl(STAT);
-			printk(KERN_INFO "mddi cmd send rtd: int %x, stat %x, "
-					"rtd val %x\n", mddi_readl(INT), stat,
-					mddi_readl(RTD_VAL));
-			if ((stat & MDDI_STAT_RTD_MEAS_FAIL) == 0)
-				break;
-			msleep(1);
-		}
-
-		mddi_writel(CMD_GET_CLIENT_CAP, CMD);
-		mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-		wait_event_timeout(mddi->int_wait, mddi->flags & FLAG_HAVE_CAPS,
-				   HZ / 100);
-
-		if (mddi->flags & FLAG_HAVE_CAPS)
-			break;
-		printk(KERN_INFO "mddi_init, timeout waiting for caps\n");
-	}
-	return mddi->flags & FLAG_HAVE_CAPS;
-}
-
-/* link must be active when this is called */
-int mddi_check_status(struct mddi_info *mddi)
-{
-	int ret = -1, retry = 3;
-	mutex_lock(&mddi->reg_read_lock);
-	mddi_writel(MDDI_CMD_PERIODIC_REV_ENCAP | 1, CMD);
-	mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-
-	do {
-		mddi->flags &= ~FLAG_HAVE_STATUS;
-		mddi_writel(CMD_GET_CLIENT_STATUS, CMD);
-		mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-		wait_event_timeout(mddi->int_wait,
-				   mddi->flags & FLAG_HAVE_STATUS,
-				   HZ / 100);
-
-		if (mddi->flags & FLAG_HAVE_STATUS) {
-			if (mddi->status.crc_error_count)
-				printk(KERN_INFO "mddi status: crc_error "
-					"count: %d\n",
-					mddi->status.crc_error_count);
-			else
-				ret = 0;
-			break;
-		} else
-			printk(KERN_INFO "mddi status: failed to get client "
-				"status\n");
-		mddi_writel(MDDI_CMD_SEND_RTD, CMD);
-		mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-	} while (--retry);
-
-	mddi_writel(MDDI_CMD_PERIODIC_REV_ENCAP | 0, CMD);
-	mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-	mutex_unlock(&mddi->reg_read_lock);
-	return ret;
-}
-
-
-void mddi_remote_write(struct msm_mddi_client_data *cdata, uint32_t val,
-		       uint32_t reg)
-{
-	struct mddi_info *mddi = container_of(cdata, struct mddi_info,
-					      client_data);
-	struct mddi_llentry *ll;
-	struct mddi_register_access *ra;
-
-	mutex_lock(&mddi->reg_write_lock);
-
-	ll = mddi->reg_write_data;
-
-	ra = &(ll->u.r);
-	ra->length = 14 + 4;
-	ra->type = TYPE_REGISTER_ACCESS;
-	ra->client_id = 0;
-	ra->read_write_info = MDDI_WRITE | 1;
-	ra->crc16 = 0;
-
-	ra->register_address = reg;
-	ra->register_data_list = val;
-
-	ll->flags = 1;
-	ll->header_count = 14;
-	ll->data_count = 4;
-	ll->data = mddi->reg_write_addr + offsetof(struct mddi_llentry,
-						   u.r.register_data_list);
-	ll->next = 0;
-	ll->reserved = 0;
-
-	mddi_writel(mddi->reg_write_addr, PRI_PTR);
-
-	mddi_wait_interrupt(mddi, MDDI_INT_PRI_LINK_LIST_DONE);
-	mutex_unlock(&mddi->reg_write_lock);
-}
-
-uint32_t mddi_remote_read(struct msm_mddi_client_data *cdata, uint32_t reg)
-{
-	struct mddi_info *mddi = container_of(cdata, struct mddi_info,
-					      client_data);
-	struct mddi_llentry *ll;
-	struct mddi_register_access *ra;
-	struct reg_read_info ri;
-	unsigned s;
-	int retry_count = 2;
-	unsigned long irq_flags;
-
-	mutex_lock(&mddi->reg_read_lock);
-
-	ll = mddi->reg_read_data;
-
-	ra = &(ll->u.r);
-	ra->length = 14;
-	ra->type = TYPE_REGISTER_ACCESS;
-	ra->client_id = 0;
-	ra->read_write_info = MDDI_READ | 1;
-	ra->crc16 = 0;
-
-	ra->register_address = reg;
-
-	ll->flags = 0x11;
-	ll->header_count = 14;
-	ll->data_count = 0;
-	ll->data = 0;
-	ll->next = 0;
-	ll->reserved = 0;
-
-	s = mddi_readl(STAT);
-
-	ri.reg = reg;
-	ri.status = -1;
-
-	do {
-		init_completion(&ri.done);
-		mddi->reg_read = &ri;
-		mddi_writel(mddi->reg_read_addr, PRI_PTR);
-
-		mddi_wait_interrupt(mddi, MDDI_INT_PRI_LINK_LIST_DONE);
-
-		/* Enable Periodic Reverse Encapsulation. */
-		mddi_writel(MDDI_CMD_PERIODIC_REV_ENCAP | 1, CMD);
-		mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-		if (wait_for_completion_timeout(&ri.done, HZ/10) == 0 &&
-		    !ri.done.done) {
-			printk(KERN_INFO "mddi_remote_read(%x) timeout "
-					 "(%d %d %d)\n",
-			       reg, ri.status, ri.result, ri.done.done);
-			spin_lock_irqsave(&mddi->int_lock, irq_flags);
-			mddi->reg_read = NULL;
-			spin_unlock_irqrestore(&mddi->int_lock, irq_flags);
-			ri.status = -1;
-			ri.result = -1;
-		}
-		if (ri.status == 0)
-			break;
-
-		mddi_writel(MDDI_CMD_SEND_RTD, CMD);
-		mddi_writel(MDDI_CMD_LINK_ACTIVE, CMD);
-		mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-		printk(KERN_INFO "mddi_remote_read: failed, sent "
-		       "MDDI_CMD_SEND_RTD: int %x, stat %x, rtd val %x "
-		       "curr_rev_ptr %x\n", mddi_readl(INT), mddi_readl(STAT),
-		       mddi_readl(RTD_VAL), mddi_readl(CURR_REV_PTR));
-	} while (retry_count-- > 0);
-	/* Disable Periodic Reverse Encapsulation. */
-	mddi_writel(MDDI_CMD_PERIODIC_REV_ENCAP | 0, CMD);
-	mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-	mddi->reg_read = NULL;
-	mutex_unlock(&mddi->reg_read_lock);
-	return ri.result;
-}
-
-static struct mddi_info mddi_info[2];
-
-static int __devinit mddi_clk_setup(struct platform_device *pdev,
-				    struct mddi_info *mddi,
-				    unsigned long clk_rate)
-{
-	int ret;
-
-	/* set up the clocks */
-	mddi->clk = clk_get(&pdev->dev, "mddi_clk");
-	if (IS_ERR(mddi->clk)) {
-		printk(KERN_INFO "mddi: failed to get clock\n");
-		return PTR_ERR(mddi->clk);
-	}
-	ret =  clk_enable(mddi->clk);
-	if (ret)
-		goto fail;
-	ret = clk_set_rate(mddi->clk, clk_rate);
-	if (ret)
-		goto fail;
-	return 0;
-
-fail:
-	clk_put(mddi->clk);
-	return ret;
-}
-
-static int __init mddi_rev_data_setup(struct mddi_info *mddi)
-{
-	void *dma;
-	dma_addr_t dma_addr;
-
-	/* set up dma buffer */
-	dma = dma_alloc_coherent(NULL, 0x1000, &dma_addr, GFP_KERNEL);
-	if (dma == 0)
-		return -ENOMEM;
-	mddi->rev_data = dma;
-	mddi->rev_data_curr = 0;
-	mddi->rev_addr = dma_addr;
-	mddi->reg_write_data = dma + MDDI_REV_BUFFER_SIZE;
-	mddi->reg_write_addr = dma_addr + MDDI_REV_BUFFER_SIZE;
-	mddi->reg_read_data = mddi->reg_write_data + 1;
-	mddi->reg_read_addr = mddi->reg_write_addr +
-			      sizeof(*mddi->reg_write_data);
+	dev_dbg(dev, "pm_runtime: suspending...\n");
 	return 0;
 }
 
-static int __devinit mddi_probe(struct platform_device *pdev)
+static int mddi_runtime_resume(struct device *dev)
 {
-	struct msm_mddi_platform_data *pdata = pdev->dev.platform_data;
-	struct mddi_info *mddi = &mddi_info[pdev->id];
-	struct resource *resource;
-	int ret, i;
-
-	resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!resource) {
-		printk(KERN_ERR "mddi: no associated mem resource!\n");
-		return -ENOMEM;
-	}
-	mddi->base = ioremap(resource->start, resource_size(resource));
-	if (!mddi->base) {
-		printk(KERN_ERR "mddi: failed to remap base!\n");
-		ret = -EINVAL;
-		goto error_ioremap;
-	}
-	resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
-	if (!resource) {
-		printk(KERN_ERR "mddi: no associated irq resource!\n");
-		ret = -EINVAL;
-		goto error_get_irq_resource;
-	}
-	mddi->irq = resource->start;
-	printk(KERN_INFO "mddi: init() base=0x%p irq=%d\n", mddi->base,
-	       mddi->irq);
-	mddi->power_client = pdata->power_client;
-
-	mutex_init(&mddi->reg_write_lock);
-	mutex_init(&mddi->reg_read_lock);
-	spin_lock_init(&mddi->int_lock);
-	init_waitqueue_head(&mddi->int_wait);
-
-	ret = mddi_clk_setup(pdev, mddi, pdata->clk_rate);
-	if (ret) {
-		printk(KERN_ERR "mddi: failed to setup clock!\n");
-		goto error_clk_setup;
-	}
-
-	ret = mddi_rev_data_setup(mddi);
-	if (ret) {
-		printk(KERN_ERR "mddi: failed to setup rev data!\n");
-		goto error_rev_data;
-	}
-
-	mddi->int_enable = 0;
-	mddi_writel(mddi->int_enable, INTEN);
-	ret = request_irq(mddi->irq, mddi_isr, 0, "mddi",
-			  &mddi->client_data);
-	if (ret) {
-		printk(KERN_ERR "mddi: failed to request enable irq!\n");
-		goto error_request_irq;
-	}
-
-	/* turn on the mddi client bridge chip */
-	if (mddi->power_client)
-		mddi->power_client(&mddi->client_data, 1);
-
-	/* initialize the mddi registers */
-	mddi_set_auto_hibernate(&mddi->client_data, 0);
-	mddi_writel(MDDI_CMD_RESET, CMD);
-	mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-	mddi->version = mddi_init_registers(mddi);
-	if (mddi->version < 0x20) {
-		printk(KERN_ERR "mddi: unsupported version 0x%x\n",
-		       mddi->version);
-		ret = -ENODEV;
-		goto error_mddi_version;
-	}
-
-	/* read the capabilities off the client */
-	if (!mddi_get_client_caps(mddi)) {
-		printk(KERN_INFO "mddi: no client found\n");
-		/* power down the panel */
-		mddi_writel(MDDI_CMD_POWERDOWN, CMD);
-		printk(KERN_INFO "mddi powerdown: stat %x\n", mddi_readl(STAT));
-		msleep(100);
-		printk(KERN_INFO "mddi powerdown: stat %x\n", mddi_readl(STAT));
-		return 0;
-	}
-	mddi_set_auto_hibernate(&mddi->client_data, 1);
-
-	if (mddi->caps.Mfr_Name == 0 && mddi->caps.Product_Code == 0)
-		pdata->fixup(&mddi->caps.Mfr_Name, &mddi->caps.Product_Code);
-
-	mddi->client_pdev.id = 0;
-	for (i = 0; i < pdata->num_clients; i++) {
-		if (pdata->client_platform_data[i].product_id ==
-		    (mddi->caps.Mfr_Name << 16 | mddi->caps.Product_Code)) {
-			mddi->client_data.private_client_data =
-				pdata->client_platform_data[i].client_data;
-			mddi->client_pdev.name =
-				pdata->client_platform_data[i].name;
-			mddi->client_pdev.id =
-				pdata->client_platform_data[i].id;
-			/* XXX: possibly set clock */
-			break;
-		}
-	}
-
-	if (i >= pdata->num_clients)
-		mddi->client_pdev.name = "mddi_c_dummy";
-	printk(KERN_INFO "mddi: registering panel %s\n",
-		mddi->client_pdev.name);
-
-	mddi->client_data.suspend = mddi_suspend;
-	mddi->client_data.resume = mddi_resume;
-	mddi->client_data.activate_link = mddi_activate_link;
-	mddi->client_data.remote_write = mddi_remote_write;
-	mddi->client_data.remote_read = mddi_remote_read;
-	mddi->client_data.auto_hibernate = mddi_set_auto_hibernate;
-	mddi->client_data.fb_resource = pdata->fb_resource;
-	if (pdev->id == 0)
-		mddi->client_data.interface_type = MSM_MDDI_PMDH_INTERFACE;
-	else if (pdev->id == 1)
-		mddi->client_data.interface_type = MSM_MDDI_EMDH_INTERFACE;
-	else {
-		printk(KERN_ERR "mddi: can not determine interface %d!\n",
-		       pdev->id);
-		ret = -EINVAL;
-		goto error_mddi_interface;
-	}
-
-	mddi->client_pdev.dev.platform_data = &mddi->client_data;
-	printk(KERN_INFO "mddi: publish: %s\n", mddi->client_name);
-	platform_device_register(&mddi->client_pdev);
+	dev_dbg(dev, "pm_runtime: resuming...\n");
 	return 0;
-
-error_mddi_interface:
-error_mddi_version:
-	free_irq(mddi->irq, 0);
-error_request_irq:
-	dma_free_coherent(NULL, 0x1000, mddi->rev_data, mddi->rev_addr);
-error_rev_data:
-error_clk_setup:
-error_get_irq_resource:
-	iounmap(mddi->base);
-error_ioremap:
-
-	printk(KERN_INFO "mddi: mddi_init() failed (%d)\n", ret);
-	return ret;
 }
 
+static int mddi_runtime_idle(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: idling...\n");
+	return 0;
+}
+
+static struct dev_pm_ops mddi_dev_pm_ops = {
+	.runtime_suspend = mddi_runtime_suspend,
+	.runtime_resume = mddi_runtime_resume,
+	.runtime_idle = mddi_runtime_idle,
+};
+
+static int pmdh_clk_status;
+int irq_enabled;
+unsigned char mddi_timer_shutdown_flag;
 
 static struct platform_driver mddi_driver = {
 	.probe = mddi_probe,
-	.driver = { .name = "msm_mddi" },
+	.remove = mddi_remove,
+#ifndef CONFIG_HAS_EARLYSUSPEND
+#ifdef CONFIG_PM
+	.suspend = mddi_suspend,
+	.resume = mddi_resume,
+#endif
+#endif
+	.shutdown = NULL,
+	.driver = {
+		.name = "mddi",
+		.pm = &mddi_dev_pm_ops,
+		   },
 };
 
-static int __init _mddi_init(void)
+extern int int_mddi_pri_flag;
+DEFINE_MUTEX(pmdh_clk_lock);
+
+int pmdh_clk_func(int value)
+{
+	int ret = 0;
+
+	switch (value) {
+	case 0:
+		pmdh_clk_disable();
+		break;
+	case 1:
+		pmdh_clk_enable();
+		break;
+	case 2:
+	default:
+		mutex_lock(&pmdh_clk_lock);
+		ret = pmdh_clk_status;
+		mutex_unlock(&pmdh_clk_lock);
+		break;
+	}
+	return ret;
+}
+
+static void pmdh_clk_disable()
+{
+	mutex_lock(&pmdh_clk_lock);
+	if (pmdh_clk_status == 0) {
+		mutex_unlock(&pmdh_clk_lock);
+		return;
+	}
+
+	if (mddi_host_timer.function) {
+		mutex_lock(&mddi_timer_lock);
+		mddi_timer_shutdown_flag = 1;
+		mutex_unlock(&mddi_timer_lock);
+		del_timer_sync(&mddi_host_timer);
+		mutex_lock(&mddi_timer_lock);
+		mddi_timer_shutdown_flag = 0;
+		mutex_unlock(&mddi_timer_lock);
+	}
+	if (int_mddi_pri_flag && irq_enabled) {
+		disable_irq(INT_MDDI_PRI);
+		irq_enabled = 0;
+	}
+
+	if (mddi_clk) {
+		clk_disable_unprepare(mddi_clk);
+		pmdh_clk_status = 0;
+	}
+	if (mddi_pclk)
+		clk_disable_unprepare(mddi_pclk);
+	mutex_unlock(&pmdh_clk_lock);
+}
+
+static void pmdh_clk_enable()
+{
+	mutex_lock(&pmdh_clk_lock);
+	if (pmdh_clk_status == 1) {
+		mutex_unlock(&pmdh_clk_lock);
+		return;
+	}
+
+	if (mddi_clk) {
+		clk_prepare_enable(mddi_clk);
+		pmdh_clk_status = 1;
+	}
+	if (mddi_pclk)
+		clk_prepare_enable(mddi_pclk);
+
+	if (int_mddi_pri_flag && !irq_enabled) {
+		enable_irq(INT_MDDI_PRI);
+		irq_enabled = 1;
+	}
+
+	if (mddi_host_timer.function)
+		mddi_host_timer_service(0);
+
+	mutex_unlock(&pmdh_clk_lock);
+}
+
+static int mddi_off(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+	boolean dma_pending, dma_update_flag;
+	int ret, i;
+
+	mfd = platform_get_drvdata(pdev);
+
+	for (i = 0; i < 6; i++) {
+		dma_update_flag = mfd->dma_update_flag;
+		dma_pending = mfd->dma->busy;
+		if (dma_update_flag && !dma_pending)
+			break;
+		msleep(5);
+	}
+
+	pmdh_clk_enable();
+	ret = panel_next_off(pdev);
+	pmdh_clk_disable();
+
+	if (mddi_pdata && mddi_pdata->mddi_power_save)
+		mddi_pdata->mddi_power_save(0);
+#ifdef CONFIG_MSM_BUS_SCALING
+	mdp_bus_scale_update_request(0);
+#else
+	if (mfd->ebi1_clk)
+		clk_disable_unprepare(mfd->ebi1_clk);
+#endif
+	pm_runtime_put(&pdev->dev);
+	return ret;
+}
+
+static int mddi_on(struct platform_device *pdev)
+{
+	int ret = 0;
+	u32 clk_rate;
+	struct msm_fb_data_type *mfd;
+#ifdef ENABLE_FWD_LINK_SKEW_CALIBRATION
+	mddi_host_type host_idx = MDDI_HOST_PRIM;
+	u32 stat_reg;
+#endif
+
+	mfd = platform_get_drvdata(pdev);
+	pm_runtime_get(&pdev->dev);
+	if (mddi_pdata && mddi_pdata->mddi_power_save)
+		mddi_pdata->mddi_power_save(1);
+
+	pmdh_clk_enable();
+#ifdef ENABLE_FWD_LINK_SKEW_CALIBRATION
+	if (mddi_client_type < 2) {
+		/* For skew calibration, clock should be less than 50MHz */
+		clk_rate = clk_round_rate(mddi_clk, 49000000);
+		if (!clk_set_rate(mddi_clk, clk_rate)) {
+			stat_reg = mddi_host_reg_in(STAT);
+			printk(KERN_DEBUG "\n stat_reg = 0x%x", stat_reg);
+			mddi_host_reg_out(CMD, MDDI_CMD_HIBERNATE);
+			if (stat_reg & (0x1 << 4))
+				mddi_host_reg_out(CMD, MDDI_CMD_LINK_ACTIVE);
+
+			mddi_host_reg_out(CMD, MDDI_CMD_SEND_RTD);
+			mddi_send_fw_link_skew_cal(host_idx);
+			mddi_host_reg_out(CMD, MDDI_CMD_SEND_RTD);
+			mddi_host_reg_out(CMD, MDDI_CMD_HIBERNATE | 1);
+		} else {
+			printk(KERN_ERR "%s: clk_set_rate failed\n",
+				__func__);
+		}
+	}
+#endif
+
+	clk_rate = mfd->fbi->var.pixclock;
+	clk_rate = min(clk_rate, mfd->panel_info.clk_max);
+
+	if (mddi_pdata &&
+	    mddi_pdata->mddi_sel_clk &&
+	    mddi_pdata->mddi_sel_clk(&clk_rate))
+			printk(KERN_ERR
+			  "%s: can't select mddi io clk targate rate = %d\n",
+			  __func__, clk_rate);
+
+	clk_rate = clk_round_rate(mddi_clk, clk_rate);
+	if (clk_set_rate(mddi_clk, clk_rate) < 0)
+		printk(KERN_ERR "%s: clk_set_rate failed\n",
+			__func__);
+
+#ifdef CONFIG_MSM_BUS_SCALING
+	mdp_bus_scale_update_request(2);
+#else
+	if (mfd->ebi1_clk)
+		clk_prepare_enable(mfd->ebi1_clk);
+#endif
+	ret = panel_next_on(pdev);
+
+	return ret;
+}
+
+static int mddi_resource_initialized;
+
+static int mddi_probe(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+	struct platform_device *mdp_dev = NULL;
+	struct msm_fb_panel_data *pdata = NULL;
+	int rc;
+	resource_size_t size ;
+	u32 clk_rate;
+	unsigned long rate;
+	int ret;
+	struct clk *ebi1_clk = NULL;
+
+	if ((pdev->id == 0) && (pdev->num_resources >= 0)) {
+		mddi_pdata = pdev->dev.platform_data;
+		pmdh_clk_status = 0;
+
+		mddi_clk = clk_get(&pdev->dev, "core_clk");
+		if (IS_ERR(mddi_clk)) {
+			pr_err("can't find mddi_clk\n");
+			return PTR_ERR(mddi_clk);
+		}
+		rate = clk_round_rate(mddi_clk, 49000000);
+		ret = clk_set_rate(mddi_clk, rate);
+		if (ret)
+			pr_err("Can't set mddi_clk min rate to %lu\n",
+									rate);
+
+		pr_info("mddi_clk init rate is %lu\n",
+			clk_get_rate(mddi_clk));
+		mddi_pclk = clk_get(&pdev->dev, "iface_clk");
+		if (IS_ERR(mddi_pclk))
+			mddi_pclk = NULL;
+		pmdh_clk_enable();
+
+#ifndef CONFIG_MSM_BUS_SCALING
+		ebi1_clk = clk_get(&pdev->dev, "mem_clk");
+		if (IS_ERR(ebi1_clk))
+			return PTR_ERR(ebi1_clk);
+		clk_set_rate(ebi1_clk, 65000000);
+#endif
+
+		size =  resource_size(&pdev->resource[0]);
+		msm_pmdh_base =  ioremap(pdev->resource[0].start, size);
+
+		MSM_FB_INFO("primary mddi base phy_addr = 0x%x virt = 0x%x\n",
+				pdev->resource[0].start, (int) msm_pmdh_base);
+
+		if (unlikely(!msm_pmdh_base))
+			return -ENOMEM;
+
+		if (mddi_pdata && mddi_pdata->mddi_power_save)
+			mddi_pdata->mddi_power_save(1);
+
+		mddi_resource_initialized = 1;
+		return 0;
+	}
+
+	if (!mddi_resource_initialized)
+		return -EPERM;
+
+	mfd = platform_get_drvdata(pdev);
+	mfd->ebi1_clk = ebi1_clk;
+
+	if (!mfd)
+		return -ENODEV;
+
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST)
+		return -ENOMEM;
+
+	mdp_dev = platform_device_alloc("mdp", pdev->id);
+	if (!mdp_dev)
+		return -ENOMEM;
+
+	/*
+	 * link to the latest pdev
+	 */
+	mfd->pdev = mdp_dev;
+	mfd->dest = DISPLAY_LCD;
+
+	/*
+	 * alloc panel device data
+	 */
+	if (platform_device_add_data
+	    (mdp_dev, pdev->dev.platform_data,
+	     sizeof(struct msm_fb_panel_data))) {
+		printk(KERN_ERR "mddi_probe: platform_device_add_data failed!\n");
+		platform_device_put(mdp_dev);
+		return -ENOMEM;
+	}
+	/*
+	 * data chain
+	 */
+	pdata = mdp_dev->dev.platform_data;
+	pdata->on = mddi_on;
+	pdata->off = mddi_off;
+	pdata->next = pdev;
+	pdata->clk_func = pmdh_clk_func;
+	/*
+	 * get/set panel specific fb info
+	 */
+	mfd->panel_info = pdata->panel_info;
+
+	if (mfd->index == 0)
+		mfd->fb_imgType = MSMFB_DEFAULT_TYPE;
+	else
+		mfd->fb_imgType = MDP_RGB_565;
+
+	clk_rate = mfd->panel_info.clk_max;
+	if (mddi_pdata &&
+	    mddi_pdata->mddi_sel_clk &&
+	    mddi_pdata->mddi_sel_clk(&clk_rate))
+			printk(KERN_ERR
+			  "%s: can't select mddi io clk targate rate = %d\n",
+			  __func__, clk_rate);
+
+	if (clk_set_max_rate(mddi_clk, clk_rate) < 0)
+		printk(KERN_ERR "%s: clk_set_max_rate failed\n", __func__);
+	mfd->panel_info.clk_rate = mfd->panel_info.clk_min;
+
+	if (!mddi_client_type)
+		mddi_client_type = mfd->panel_info.lcd.rev;
+	else if (!mfd->panel_info.lcd.rev)
+		printk(KERN_ERR
+		"%s: mddi client is trying to revert back to type 1	!!!\n",
+		__func__);
+
+	/*
+	 * set driver data
+	 */
+	platform_set_drvdata(mdp_dev, mfd);
+	rc = pm_runtime_set_active(&pdev->dev);
+	if (rc < 0)
+		printk(KERN_ERR "pm_runtime: fail to set active\n");
+
+	rc = 0;
+	pm_runtime_enable(&pdev->dev);
+	/*
+	 * register in mdp driver
+	 */
+	rc = platform_device_add(mdp_dev);
+	if (rc)
+		goto mddi_probe_err;
+
+	pdev_list[pdev_list_cnt++] = pdev;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	mfd->mddi_early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+	mfd->mddi_early_suspend.suspend = mddi_early_suspend;
+	mfd->mddi_early_suspend.resume = mddi_early_resume;
+	register_early_suspend(&mfd->mddi_early_suspend);
+#endif
+
+	return 0;
+
+mddi_probe_err:
+	platform_device_put(mdp_dev);
+	return rc;
+}
+
+static int mddi_pad_ctrl;
+static int mddi_power_locked;
+
+int mddi_client_power(unsigned int client_id)
+{
+	int ret = 0;
+	if (mddi_pdata && mddi_pdata->mddi_client_power)
+		ret = mddi_pdata->mddi_client_power(client_id);
+	return ret;
+}
+
+void mddi_disable(int lock)
+{
+	mddi_host_type host_idx = MDDI_HOST_PRIM;
+
+	if (mddi_power_locked)
+		return;
+
+	if (lock)
+		mddi_power_locked = 1;
+	pmdh_clk_enable();
+
+	mddi_pad_ctrl = mddi_host_reg_in(PAD_CTL);
+	mddi_host_reg_out(PAD_CTL, 0x0);
+
+	pmdh_clk_disable();
+
+	if (mddi_pdata && mddi_pdata->mddi_power_save)
+		mddi_pdata->mddi_power_save(0);
+}
+
+#ifdef CONFIG_PM
+static int mddi_is_in_suspend;
+
+static int mddi_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	mddi_host_type host_idx = MDDI_HOST_PRIM;
+	if (mddi_is_in_suspend)
+		return 0;
+
+	mddi_is_in_suspend = 1;
+
+	if (mddi_power_locked)
+		return 0;
+
+	pmdh_clk_enable();
+
+	mddi_pad_ctrl = mddi_host_reg_in(PAD_CTL);
+	mddi_host_reg_out(PAD_CTL, 0x0);
+
+	pmdh_clk_disable();
+
+	return 0;
+}
+
+static int mddi_resume(struct platform_device *pdev)
+{
+	mddi_host_type host_idx = MDDI_HOST_PRIM;
+
+	if (!mddi_is_in_suspend)
+		return 0;
+
+	mddi_is_in_suspend = 0;
+
+	if (mddi_power_locked)
+		return 0;
+
+	pmdh_clk_enable();
+
+	mddi_host_reg_out(PAD_CTL, mddi_pad_ctrl);
+
+
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void mddi_early_suspend(struct early_suspend *h)
+{
+	pm_message_t state;
+	struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type,
+							mddi_early_suspend);
+
+	state.event = PM_EVENT_SUSPEND;
+	mddi_suspend(mfd->pdev, state);
+}
+
+static void mddi_early_resume(struct early_suspend *h)
+{
+	struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type,
+							mddi_early_suspend);
+	mddi_resume(mfd->pdev);
+}
+#endif
+
+static int mddi_remove(struct platform_device *pdev)
+{
+	pm_runtime_disable(&pdev->dev);
+	if (mddi_host_timer.function) {
+		mutex_lock(&mddi_timer_lock);
+		mddi_timer_shutdown_flag = 1;
+		mutex_unlock(&mddi_timer_lock);
+		del_timer_sync(&mddi_host_timer);
+		mutex_lock(&mddi_timer_lock);
+		mddi_timer_shutdown_flag = 0;
+		mutex_unlock(&mddi_timer_lock);
+	}
+
+	iounmap(msm_pmdh_base);
+
+	return 0;
+}
+
+static int mddi_register_driver(void)
 {
 	return platform_driver_register(&mddi_driver);
 }
 
-module_init(_mddi_init);
+static int __init mddi_driver_init(void)
+{
+	int ret;
+
+	ret = mddi_register_driver();
+	if (ret) {
+		pmdh_clk_disable();
+		clk_put(mddi_clk);
+		if (mddi_pclk)
+			clk_put(mddi_pclk);
+		printk(KERN_ERR "mddi_register_driver() failed!\n");
+		return ret;
+	}
+
+	mddi_init();
+
+	return ret;
+}
+
+module_init(mddi_driver_init);
diff --git a/drivers/video/msm/mddi_client_dummy.c b/drivers/video/msm/mddi_client_dummy.c
index d2a091c..ebbae87 100644
--- a/drivers/video/msm/mddi_client_dummy.c
+++ b/drivers/video/msm/mddi_client_dummy.c
@@ -15,7 +15,6 @@
  * GNU General Public License for more details.
  */
 
-#include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/platform_device.h>
diff --git a/drivers/video/msm/mddi_client_nt35399.c b/drivers/video/msm/mddi_client_nt35399.c
index 7fcd67e..eb8c701 100644
--- a/drivers/video/msm/mddi_client_nt35399.c
+++ b/drivers/video/msm/mddi_client_nt35399.c
@@ -21,7 +21,6 @@
 #include <linux/interrupt.h>
 #include <linux/sched.h>
 #include <linux/gpio.h>
-#include <linux/slab.h>
 #include <mach/msm_fb.h>
 
 static DECLARE_WAIT_QUEUE_HEAD(nt35399_vsync_wait);
diff --git a/drivers/video/msm/mddi_client_toshiba.c b/drivers/video/msm/mddi_client_toshiba.c
index 053eb68..8868781 100644
--- a/drivers/video/msm/mddi_client_toshiba.c
+++ b/drivers/video/msm/mddi_client_toshiba.c
@@ -21,7 +21,6 @@
 #include <linux/interrupt.h>
 #include <linux/gpio.h>
 #include <linux/sched.h>
-#include <linux/slab.h>
 #include <mach/msm_fb.h>
 
 
@@ -60,6 +59,7 @@
 	struct msm_panel_data panel_data;
 	struct msmfb_callback *toshiba_callback;
 	int toshiba_got_int;
+	int irq;
 };
 
 
@@ -175,42 +175,6 @@
 	return IRQ_HANDLED;
 }
 
-static int setup_vsync(struct panel_info *panel,
-		       int init)
-{
-	int ret;
-	int gpio = 97;
-	unsigned int irq;
-
-	if (!init) {
-		ret = 0;
-		goto uninit;
-	}
-	ret = gpio_request_one(gpio, GPIOF_IN, "vsync");
-	if (ret)
-		goto err_request_gpio_failed;
-
-	ret = irq = gpio_to_irq(gpio);
-	if (ret < 0)
-		goto err_get_irq_num_failed;
-
-	ret = request_irq(irq, toshiba_vsync_interrupt, IRQF_TRIGGER_RISING,
-			  "vsync", panel);
-	if (ret)
-		goto err_request_irq_failed;
-	printk(KERN_INFO "vsync on gpio %d now %d\n",
-	       gpio, gpio_get_value(gpio));
-	return 0;
-
-uninit:
-	free_irq(gpio_to_irq(gpio), panel);
-err_request_irq_failed:
-err_get_irq_num_failed:
-	gpio_free(gpio);
-err_request_gpio_failed:
-	return ret;
-}
-
 static int mddi_toshiba_probe(struct platform_device *pdev)
 {
 	int ret;
@@ -227,10 +191,16 @@
 	client_data->remote_write(client_data, GPIOSEL_VWAKEINT, GPIOSEL);
 	client_data->remote_write(client_data, INTMASK_VWAKEOUT, INTMASK);
 
-	ret = setup_vsync(panel, 1);
+	ret = platform_get_irq_byname(pdev, "vsync");
+	if (ret < 0)
+		goto err_plat_get_irq;
+
+	panel->irq = ret;
+	ret = request_irq(panel->irq, toshiba_vsync_interrupt,
+			  IRQF_TRIGGER_RISING, "vsync", panel);
 	if (ret) {
 		dev_err(&pdev->dev, "mddi_bridge_setup_vsync failed\n");
-		return ret;
+		goto err_req_irq;
 	}
 
 	panel->client_data = client_data;
@@ -253,13 +223,19 @@
 	platform_device_register(&panel->pdev);
 
 	return 0;
+
+err_req_irq:
+err_plat_get_irq:
+	kfree(panel);
+	return ret;
 }
 
 static int mddi_toshiba_remove(struct platform_device *pdev)
 {
 	struct panel_info *panel = platform_get_drvdata(pdev);
 
-	setup_vsync(panel, 0);
+	platform_set_drvdata(pdev, NULL);
+	free_irq(panel->irq, panel);
 	kfree(panel);
 	return 0;
 }
diff --git a/drivers/video/msm/mddi_ext.c b/drivers/video/msm/mddi_ext.c
new file mode 100644
index 0000000..dc79fed
--- /dev/null
+++ b/drivers/video/msm/mddi_ext.c
@@ -0,0 +1,354 @@
+/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <mach/hardware.h>
+#include <asm/io.h>
+
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <linux/semaphore.h>
+#include <linux/uaccess.h>
+#include <linux/clk.h>
+#include <mach/clk.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include "msm_fb.h"
+#include "mddihosti.h"
+
+static int mddi_ext_probe(struct platform_device *pdev);
+static int mddi_ext_remove(struct platform_device *pdev);
+
+static int mddi_ext_off(struct platform_device *pdev);
+static int mddi_ext_on(struct platform_device *pdev);
+
+static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST];
+static int pdev_list_cnt;
+
+static int mddi_ext_suspend(struct platform_device *pdev, pm_message_t state);
+static int mddi_ext_resume(struct platform_device *pdev);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void mddi_ext_early_suspend(struct early_suspend *h);
+static void mddi_ext_early_resume(struct early_suspend *h);
+#endif
+
+static int mddi_ext_runtime_suspend(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: suspending...\n");
+	return 0;
+}
+
+static int mddi_ext_runtime_resume(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: resuming...\n");
+	return 0;
+}
+
+static int mddi_ext_runtime_idle(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: idling...\n");
+	return 0;
+}
+static struct dev_pm_ops mddi_ext_dev_pm_ops = {
+	.runtime_suspend = mddi_ext_runtime_suspend,
+	.runtime_resume = mddi_ext_runtime_resume,
+	.runtime_idle = mddi_ext_runtime_idle,
+};
+
+static struct platform_driver mddi_ext_driver = {
+	.probe = mddi_ext_probe,
+	.remove = mddi_ext_remove,
+#ifndef CONFIG_HAS_EARLYSUSPEND
+#ifdef CONFIG_PM
+	.suspend = mddi_ext_suspend,
+	.resume = mddi_ext_resume,
+#endif
+#endif
+	.resume_early = NULL,
+	.resume = NULL,
+	.shutdown = NULL,
+	.driver = {
+		.name = "mddi_ext",
+		.pm = &mddi_ext_dev_pm_ops,
+		   },
+};
+
+static struct clk *mddi_ext_clk;
+static struct clk *mddi_ext_pclk;
+static struct mddi_platform_data *mddi_ext_pdata;
+
+extern int int_mddi_ext_flag;
+
+static int mddi_ext_off(struct platform_device *pdev)
+{
+	int ret = 0;
+
+	ret = panel_next_off(pdev);
+	mddi_host_stop_ext_display();
+	pm_runtime_put(&pdev->dev);
+	return ret;
+}
+
+static int mddi_ext_on(struct platform_device *pdev)
+{
+	int ret = 0;
+	u32 clk_rate;
+	struct msm_fb_data_type *mfd;
+
+	mfd = platform_get_drvdata(pdev);
+	pm_runtime_get(&pdev->dev);
+	clk_rate = mfd->fbi->var.pixclock;
+	clk_rate = min(clk_rate, mfd->panel_info.clk_max);
+
+	if (mddi_ext_pdata &&
+	    mddi_ext_pdata->mddi_sel_clk &&
+	    mddi_ext_pdata->mddi_sel_clk(&clk_rate))
+		printk(KERN_ERR
+			  "%s: can't select mddi io clk targate rate = %d\n",
+			  __func__, clk_rate);
+
+	clk_rate = clk_round_rate(mddi_ext_clk, clk_rate);
+	if (clk_set_rate(mddi_ext_clk, clk_rate) < 0)
+		printk(KERN_ERR "%s: clk_set_rate failed\n",
+			__func__);
+
+	mddi_host_start_ext_display();
+	ret = panel_next_on(pdev);
+
+	return ret;
+}
+
+static int mddi_ext_resource_initialized;
+
+static int mddi_ext_probe(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+	struct platform_device *mdp_dev = NULL;
+	struct msm_fb_panel_data *pdata = NULL;
+	int rc;
+	resource_size_t size ;
+	u32 clk_rate;
+
+	if ((pdev->id == 0) && (pdev->num_resources >= 0)) {
+		mddi_ext_pdata = pdev->dev.platform_data;
+		mddi_ext_clk = clk_get(&pdev->dev, "core_clk");
+		if (IS_ERR(mddi_ext_clk)) {
+			pr_err("can't find emdh_clk\n");
+			return PTR_ERR(mddi_ext_clk);
+		}
+		clk_prepare_enable(mddi_ext_clk);
+
+		mddi_ext_pclk = clk_get(&pdev->dev, "iface_clk");
+		if (IS_ERR(mddi_ext_pclk))
+			mddi_ext_pclk = NULL;
+		else
+			clk_prepare_enable(mddi_ext_pclk);
+
+		size =  resource_size(&pdev->resource[0]);
+		msm_emdh_base = ioremap(pdev->resource[0].start, size);
+
+		MSM_FB_INFO("external mddi base address = 0x%x\n",
+				pdev->resource[0].start);
+
+		if (unlikely(!msm_emdh_base))
+			return -ENOMEM;
+
+		mddi_ext_resource_initialized = 1;
+		return 0;
+	}
+
+	if (!mddi_ext_resource_initialized)
+		return -EPERM;
+
+	mfd = platform_get_drvdata(pdev);
+
+	if (!mfd)
+		return -ENODEV;
+
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST)
+		return -ENOMEM;
+
+	mdp_dev = platform_device_alloc("mdp", pdev->id);
+	if (!mdp_dev)
+		return -ENOMEM;
+
+	/*
+	 * link to the latest pdev
+	 */
+	mfd->pdev = mdp_dev;
+	mfd->dest = DISPLAY_EXT_MDDI;
+
+	/*
+	 * alloc panel device data
+	 */
+	if (platform_device_add_data
+	    (mdp_dev, pdev->dev.platform_data,
+	     sizeof(struct msm_fb_panel_data))) {
+		printk(KERN_ERR "mddi_ext_probe: platform_device_add_data failed!\n");
+		platform_device_put(mdp_dev);
+		return -ENOMEM;
+	}
+	/*
+	 * data chain
+	 */
+	pdata = mdp_dev->dev.platform_data;
+	pdata->on = mddi_ext_on;
+	pdata->off = mddi_ext_off;
+	pdata->next = pdev;
+
+	/*
+	 * get/set panel specific fb info
+	 */
+	mfd->panel_info = pdata->panel_info;
+	mfd->fb_imgType = MDP_RGB_565;
+
+	clk_rate = mfd->panel_info.clk_max;
+	if (mddi_ext_pdata &&
+	    mddi_ext_pdata->mddi_sel_clk &&
+	    mddi_ext_pdata->mddi_sel_clk(&clk_rate))
+			printk(KERN_ERR
+			  "%s: can't select mddi io clk targate rate = %d\n",
+			  __func__, clk_rate);
+
+	if (clk_set_max_rate(mddi_ext_clk, clk_rate) < 0)
+		printk(KERN_ERR "%s: clk_set_max_rate failed\n", __func__);
+	mfd->panel_info.clk_rate = mfd->panel_info.clk_min;
+
+	/*
+	 * set driver data
+	 */
+	platform_set_drvdata(mdp_dev, mfd);
+	rc = pm_runtime_set_active(&pdev->dev);
+	if (rc < 0)
+		printk(KERN_ERR "pm_runtime: fail to set active\n");
+
+	rc = 0;
+	pm_runtime_enable(&pdev->dev);
+	/*
+	 * register in mdp driver
+	 */
+	rc = platform_device_add(mdp_dev);
+	if (rc)
+		goto mddi_ext_probe_err;
+
+	pdev_list[pdev_list_cnt++] = pdev;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	mfd->mddi_ext_early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+	mfd->mddi_ext_early_suspend.suspend = mddi_ext_early_suspend;
+	mfd->mddi_ext_early_suspend.resume = mddi_ext_early_resume;
+	register_early_suspend(&mfd->mddi_ext_early_suspend);
+#endif
+
+	return 0;
+
+mddi_ext_probe_err:
+	platform_device_put(mdp_dev);
+	return rc;
+}
+
+static int mddi_ext_is_in_suspend;
+
+static int mddi_ext_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	if (mddi_ext_is_in_suspend)
+		return 0;
+
+	mddi_ext_is_in_suspend = 1;
+
+	clk_disable_unprepare(mddi_ext_clk);
+	if (mddi_ext_pclk)
+		clk_disable_unprepare(mddi_ext_pclk);
+
+	disable_irq(INT_MDDI_EXT);
+
+	return 0;
+}
+
+static int mddi_ext_resume(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+
+	mfd = platform_get_drvdata(pdev);
+
+	if (!mddi_ext_is_in_suspend)
+		return 0;
+
+	mddi_ext_is_in_suspend = 0;
+	enable_irq(INT_MDDI_EXT);
+
+	clk_prepare_enable(mddi_ext_clk);
+	if (mddi_ext_pclk)
+		clk_prepare_enable(mddi_ext_pclk);
+
+	return 0;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void mddi_ext_early_suspend(struct early_suspend *h)
+{
+	pm_message_t state;
+	struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type,
+							mddi_ext_early_suspend);
+
+	state.event = PM_EVENT_SUSPEND;
+	mddi_ext_suspend(mfd->pdev, state);
+}
+
+static void mddi_ext_early_resume(struct early_suspend *h)
+{
+	struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type,
+							mddi_ext_early_suspend);
+	mddi_ext_resume(mfd->pdev);
+}
+#endif
+
+static int mddi_ext_remove(struct platform_device *pdev)
+{
+	pm_runtim_disable(&pdev->dev);
+	iounmap(msm_emdh_base);
+	return 0;
+}
+
+static int mddi_ext_register_driver(void)
+{
+	return platform_driver_register(&mddi_ext_driver);
+}
+
+static int __init mddi_ext_driver_init(void)
+{
+	int ret;
+
+	ret = mddi_ext_register_driver();
+	if (ret) {
+		printk(KERN_ERR "mddi_ext_register_driver() failed!\n");
+		return ret;
+	}
+	mddi_init();
+
+	return ret;
+}
+
+module_init(mddi_ext_driver_init);
diff --git a/drivers/video/msm/mddi_ext_lcd.c b/drivers/video/msm/mddi_ext_lcd.c
new file mode 100644
index 0000000..da79513
--- /dev/null
+++ b/drivers/video/msm/mddi_ext_lcd.c
@@ -0,0 +1,90 @@
+/* Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_fb.h"
+#include "mddihost.h"
+#include "mddihosti.h"
+
+static int mddi_ext_lcd_on(struct platform_device *pdev);
+static int mddi_ext_lcd_off(struct platform_device *pdev);
+
+static int mddi_ext_lcd_on(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static int mddi_ext_lcd_off(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static int __init mddi_ext_lcd_probe(struct platform_device *pdev)
+{
+	msm_fb_add_device(pdev);
+
+	return 0;
+}
+
+static struct platform_driver this_driver = {
+	.probe  = mddi_ext_lcd_probe,
+	.driver = {
+		.name   = "extmddi_svga",
+	},
+};
+
+static struct msm_fb_panel_data mddi_ext_lcd_panel_data = {
+	.panel_info.xres = 800,
+	.panel_info.yres = 600,
+	.panel_info.mode2_xres = 0;
+	.panel_info.mode2_yres = 0;
+	.panel_info.mode2_bpp = 0;
+	.panel_info.type = EXT_MDDI_PANEL,
+	.panel_info.pdest = DISPLAY_1,
+	.panel_info.wait_cycle = 0,
+	.panel_info.bpp = 18,
+	.panel_info.fb_num = 2,
+	.panel_info.clk_rate = 122880000,
+	.panel_info.clk_min  = 120000000,
+	.panel_info.clk_max  = 125000000,
+	.on = mddi_ext_lcd_on,
+	.off = mddi_ext_lcd_off,
+};
+
+static struct platform_device this_device = {
+	.name   = "extmddi_svga",
+	.id	= 0,
+	.dev	= {
+		.platform_data = &mddi_ext_lcd_panel_data,
+	}
+};
+
+static int __init mddi_ext_lcd_init(void)
+{
+	int ret;
+	struct msm_panel_info *pinfo;
+
+	ret = platform_driver_register(&this_driver);
+	if (!ret) {
+		pinfo = &mddi_ext_lcd_panel_data.panel_info;
+		pinfo->lcd.vsync_enable = FALSE;
+		pinfo->mddi.vdopkt = MDDI_DEFAULT_PRIM_PIX_ATTR;
+
+		ret = platform_device_register(&this_device);
+		if (ret)
+			platform_driver_unregister(&this_driver);
+	}
+
+	return ret;
+}
+
+module_init(mddi_ext_lcd_init);
diff --git a/drivers/video/msm/mddi_hw.h b/drivers/video/msm/mddi_hw.h
index 45cc01f..47bb449 100644
--- a/drivers/video/msm/mddi_hw.h
+++ b/drivers/video/msm/mddi_hw.h
@@ -53,6 +53,9 @@
 #define MDDI_MF_CNT             0x0084
 #define MDDI_CURR_REV_PTR       0x0088
 #define MDDI_CORE_VER           0x008c
+#define MDDI_FIFO_ALLOC         0x0090
+#define MDDI_PAD_IO_CTL         0x00a0
+#define MDDI_PAD_CAL            0x00a4
 
 #define MDDI_INT_PRI_PTR_READ       0x0001
 #define MDDI_INT_SEC_PTR_READ       0x0002
@@ -125,8 +128,14 @@
 /* MDP sends 256 pixel packets, so lower value hibernates more without
  * significantly increasing latency of waiting for next subframe */
 #define MDDI_HOST_BYTES_PER_SUBFRAME  0x3C00
+
+#if defined(CONFIG_MSM_MDP31) || defined(CONFIG_MSM_MDP40)
+#define MDDI_HOST_TA2_LEN       0x001a
+#define MDDI_HOST_REV_RATE_DIV  0x0004
+#else
 #define MDDI_HOST_TA2_LEN       0x000c
 #define MDDI_HOST_REV_RATE_DIV  0x0002
+#endif
 
 
 struct __attribute__((packed)) mddi_rev_packet {
diff --git a/drivers/video/msm/mddi_orise.c b/drivers/video/msm/mddi_orise.c
new file mode 100644
index 0000000..fa48c75
--- /dev/null
+++ b/drivers/video/msm/mddi_orise.c
@@ -0,0 +1,128 @@
+/* Copyright (c) 2010, 2012 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_fb.h"
+#include "mddihost.h"
+#include "mddihosti.h"
+
+#define MDDI_ORISE_1_2 1
+#define write_client_reg(__X, __Y, __Z) {\
+	mddi_queue_register_write(__X, __Y, TRUE, 0);\
+}
+
+static int mddi_orise_lcd_on(struct platform_device *pdev);
+static int mddi_orise_lcd_off(struct platform_device *pdev);
+static int __init mddi_orise_probe(struct platform_device *pdev);
+static int __init mddi_orise_init(void);
+
+/* function used to turn on the display */
+static void mddi_orise_prim_lcd_init(void)
+{
+	write_client_reg(0x00110000, 0, TRUE);
+	mddi_wait(150);
+	write_client_reg(0x00290000, 0, TRUE);
+}
+
+static struct platform_driver this_driver = {
+	.driver = {
+		.name   = "mddi_orise",
+	},
+};
+
+static struct msm_fb_panel_data mddi_orise_panel_data = {
+	.on = mddi_orise_lcd_on,
+	.off = mddi_orise_lcd_off,
+};
+
+static struct platform_device this_device = {
+	.name	= "mddi_orise",
+	.id	= MDDI_ORISE_1_2,
+	.dev	= {
+		.platform_data = &mddi_orise_panel_data,
+	}
+};
+
+static int mddi_orise_lcd_on(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+	mfd = platform_get_drvdata(pdev);
+	if (!mfd)
+		return -ENODEV;
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	mddi_orise_prim_lcd_init();
+
+	return 0;
+}
+
+static int mddi_orise_lcd_off(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static int __init mddi_orise_probe(struct platform_device *pdev)
+{
+	msm_fb_add_device(pdev);
+	return 0;
+}
+
+static int __init mddi_orise_init(void)
+{
+	int ret;
+	struct msm_panel_info *pinfo;
+
+#ifdef CONFIG_FB_MSM_MDDI_AUTO_DETECT
+	u32 id;
+	ret = msm_fb_detect_client("mddi_orise");
+	if (ret == -ENODEV)
+		return 0;
+
+	if (ret) {
+		id = mddi_get_client_id();
+		if (((id >> 16) != 0xbe8d) || ((id & 0xffff) != 0x8031))
+			return 0;
+	}
+#endif
+	ret = platform_driver_probe(&this_driver, mddi_orise_probe);
+	if (!ret) {
+		pinfo = &mddi_orise_panel_data.panel_info;
+		pinfo->xres = 480;
+		pinfo->yres = 800;
+		MSM_FB_SINGLE_MODE_PANEL(pinfo);
+		pinfo->type = MDDI_PANEL;
+		pinfo->pdest = DISPLAY_1;
+		pinfo->mddi.is_type1 = TRUE;
+		pinfo->mddi.vdopkt = MDDI_DEFAULT_PRIM_PIX_ATTR;
+		pinfo->wait_cycle = 0;
+		pinfo->bpp = 18;
+		pinfo->fb_num = 2;
+		pinfo->clk_rate = 192000000;
+		pinfo->clk_min = 192000000;
+		pinfo->clk_max = 192000000;
+		pinfo->lcd.rev = 2;
+		pinfo->lcd.vsync_enable = FALSE;
+		pinfo->lcd.refx100 = 6050;
+		pinfo->lcd.v_back_porch = 2;
+		pinfo->lcd.v_front_porch = 2;
+		pinfo->lcd.v_pulse_width = 105;
+		pinfo->lcd.hw_vsync_mode = TRUE;
+		pinfo->lcd.vsync_notifier_period = 0;
+
+		ret = platform_device_register(&this_device);
+		if (ret)
+			platform_driver_unregister(&this_driver);
+	}
+	return ret;
+}
+module_init(mddi_orise_init);
diff --git a/drivers/video/msm/mddi_prism.c b/drivers/video/msm/mddi_prism.c
new file mode 100644
index 0000000..ec2bf57
--- /dev/null
+++ b/drivers/video/msm/mddi_prism.c
@@ -0,0 +1,112 @@
+/* Copyright (c) 2008-2010, 2012 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_fb.h"
+#include "mddihost.h"
+#include "mddihosti.h"
+
+static int prism_lcd_on(struct platform_device *pdev);
+static int prism_lcd_off(struct platform_device *pdev);
+
+static int prism_lcd_on(struct platform_device *pdev)
+{
+	/* Set the MDP pixel data attributes for Primary Display */
+	mddi_host_write_pix_attr_reg(0x00C3);
+
+	return 0;
+}
+
+static int prism_lcd_off(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static int __devinit prism_probe(struct platform_device *pdev)
+{
+	msm_fb_add_device(pdev);
+
+	return 0;
+}
+
+static struct platform_driver this_driver = {
+	.probe  = prism_probe,
+	.driver = {
+		.name   = "mddi_prism_wvga",
+	},
+};
+
+static struct msm_fb_panel_data prism_panel_data = {
+	.on = prism_lcd_on,
+	.off = prism_lcd_off,
+};
+
+static struct platform_device this_device = {
+	.name   = "mddi_prism_wvga",
+	.id	= 0,
+	.dev	= {
+		.platform_data = &prism_panel_data,
+	}
+};
+
+static int __init prism_init(void)
+{
+	int ret;
+	struct msm_panel_info *pinfo;
+
+#ifdef CONFIG_FB_MSM_MDDI_AUTO_DETECT
+	u32 id;
+
+	ret = msm_fb_detect_client("mddi_prism_wvga");
+	if (ret == -ENODEV)
+		return 0;
+
+	if (ret) {
+		id = mddi_get_client_id();
+
+		if (((id >> 16) != 0x4474) || ((id & 0xffff) == 0x8960))
+			return 0;
+	}
+#endif
+	ret = platform_driver_register(&this_driver);
+	if (!ret) {
+		pinfo = &prism_panel_data.panel_info;
+		pinfo->xres = 800;
+		pinfo->yres = 480;
+		MSM_FB_SINGLE_MODE_PANEL(pinfo);
+		pinfo->type = MDDI_PANEL;
+		pinfo->pdest = DISPLAY_1;
+		pinfo->mddi.vdopkt = MDDI_DEFAULT_PRIM_PIX_ATTR;
+		pinfo->wait_cycle = 0;
+		pinfo->mddi.is_type1 = TRUE;
+		pinfo->bpp = 18;
+		pinfo->fb_num = 2;
+		pinfo->clk_rate = 153600000;
+		pinfo->clk_min = 140000000;
+		pinfo->clk_max = 160000000;
+		pinfo->lcd.vsync_enable = TRUE;
+		pinfo->lcd.refx100 = 6050;
+		pinfo->lcd.v_back_porch = 23;
+		pinfo->lcd.v_front_porch = 20;
+		pinfo->lcd.v_pulse_width = 105;
+		pinfo->lcd.hw_vsync_mode = TRUE;
+		pinfo->lcd.vsync_notifier_period = 0;
+
+		ret = platform_device_register(&this_device);
+		if (ret)
+			platform_driver_unregister(&this_driver);
+	}
+
+	return ret;
+}
+
+module_init(prism_init);
diff --git a/drivers/video/msm/mddi_quickvx.c b/drivers/video/msm/mddi_quickvx.c
new file mode 100644
index 0000000..95e7d41
--- /dev/null
+++ b/drivers/video/msm/mddi_quickvx.c
@@ -0,0 +1,719 @@
+/* Copyright (c) 2010, 2012 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <mach/pmic.h>
+#include "msm_fb.h"
+#include "mddihost.h"
+#include "mddihosti.h"
+
+/* WVGA Primary Display */
+#define MDDI_QUICKVX_1_2		1
+/* MDDI Manufacturer Code */
+#define QUICKVX_MDDI_MFR_CODE	0xc583
+/* MDDI Product Code */
+#define QUICKVX_MDDI_PRD_CODE	0x5800
+
+/* Register Address Maps */
+/* MDDI Address Anti-fuse values for bits [31:22] */
+#define QUICKVX_ADDR_31_22_AF	(0X000 << 22)
+
+/* MDDI Address Maps */
+/* VEE Block Address Base */
+#define QUICKVX_VEE_BASE		(QUICKVX_ADDR_31_22_AF | 0x00000000)
+/* SPI Block Address Base */
+#define QUICKVX_SPI_BASE		(QUICKVX_ADDR_31_22_AF | 0x00010000)
+/* Clock and Reset (CAR) Address Base */
+#define QUICKVX_CAR_BASE		(QUICKVX_ADDR_31_22_AF | 0x00020000)
+/* Register Control Block (RCB) Address Base */
+#define QUICKVX_RCB_BASE		(QUICKVX_ADDR_31_22_AF | 0x00030000)
+/* Cellular RAM Address Base */
+#define QUICKVX_CELLRAM_BASE	(QUICKVX_ADDR_31_22_AF | 0x00100000)
+/* FB through A2F Address Base */
+#define QUICKVX_FB_A2F_BASE		(QUICKVX_ADDR_31_22_AF | 0x00200000)
+
+
+/***************************************************
+ * Common Registers in Register Control Block (RCB) Registers
+ ***************************************************/
+ /* CellRAM Configuration RCR Register */
+#define QUICKVX_RCB_RCR_REG			(QUICKVX_RCB_BASE | 0x00000000)
+/* Image Effect Register */
+#define QUICKVX_RCB_IER_REG			(QUICKVX_RCB_BASE | 0x00000004)
+/* Row Number Register */
+#define QUICKVX_RCB_ROWNUM_REG		(QUICKVX_RCB_BASE | 0x00000008)
+/* TCON Timing0 Register */
+#define QUICKVX_RCB_TCON0_REG		(QUICKVX_RCB_BASE | 0x0000000C)
+/* TCON Timing1 Register */
+#define QUICKVX_RCB_TCON1_REG		(QUICKVX_RCB_BASE | 0x00000010)
+/* TCON Timing2 Register */
+#define QUICKVX_RCB_TCON2_REG		(QUICKVX_RCB_BASE | 0x00000014)
+/* PWM Control Register */
+#define QUICKVX_RCB_PWMC_REG		(QUICKVX_RCB_BASE | 0x00000018)
+/* PWM Width Register */
+#define QUICKVX_RCB_PWMW_REG		(QUICKVX_RCB_BASE | 0x0000001C)
+/* VEE Configuration Register */
+#define QUICKVX_RCB_VEECONF_REG		(QUICKVX_RCB_BASE | 0x00000020)
+/* CellRAM Configuration BCR Register */
+#define QUICKVX_RCB_CELLBCR_REG		(QUICKVX_RCB_BASE | 0x00000024)
+/* CellRAM Configuration Control Register */
+#define QUICKVX_RCB_CELLCC_REG		(QUICKVX_RCB_BASE | 0x00000028)
+/* Use Case Register */
+#define QUICKVX_RCB_USECASE_REG		(QUICKVX_RCB_BASE | 0x00000100)
+/* Video Parameter Register */
+#define QUICKVX_RCB_VPARM_REG		(QUICKVX_RCB_BASE | 0x00000104)
+/* MDDI Client Wake-up Register */
+#define QUICKVX_RCB_MCW_REG			(QUICKVX_RCB_BASE | 0x00000108)
+/* Burst Length Register */
+#define QUICKVX_RCB_BURSTLN_REG		(QUICKVX_RCB_BASE | 0x0000010C)
+/* Display Attributes Register */
+#define QUICKVX_RCB_DISPATTR_REG	(QUICKVX_RCB_BASE | 0x00000110)
+/* Error Status Register */
+#define QUICKVX_RCB_ERRSTAT_REG		(QUICKVX_RCB_BASE | 0x00000114)
+/* Error Mask Register */
+#define QUICKVX_RCB_ERRMSK_REG		(QUICKVX_RCB_BASE | 0x00000118)
+/* MDDI ASSP FIFO Overflow Address Register */
+#define QUICKVX_RCB_ASSPFOA_REG		(QUICKVX_RCB_BASE | 0x0000011C)
+/* MDDI Fabric FIFO Overflow Address Register */
+#define QUICKVX_RCB_FABFOA_REG		(QUICKVX_RCB_BASE | 0x00000120)
+/* Incoming RGB FIFO Overflow Address Register */
+#define QUICKVX_RCB_IRFOA_REG		(QUICKVX_RCB_BASE | 0x00000124)
+/* SPI Overflow Address Register */
+#define QUICKVX_RCB_SPIOA_REG		(QUICKVX_RCB_BASE | 0x00000128)
+/* Ping Buffer Address Register */
+#define QUICKVX_RCB_PINGBA_REG		(QUICKVX_RCB_BASE | 0x0000012C)
+/* Pong Buffer Address Register */
+#define QUICKVX_RCB_PONGBA_REG		(QUICKVX_RCB_BASE | 0x00000130)
+/* Configuration Done Register */
+#define QUICKVX_RCB_CONFDONE_REG	(QUICKVX_RCB_BASE | 0x00000134)
+/* FIFO Flush Register */
+#define QUICKVX_RCB_FFLUSH_REG		(QUICKVX_RCB_BASE | 0x00000138)
+
+
+/***************************************************
+ * SPI Block Registers
+ ***************************************************/
+/* SPI Rx0 Register */
+#define QUICKVX_SPI_RX0_REG			(QUICKVX_SPI_BASE | 0x00000000)
+/* SPI Rx1 Register */
+#define QUICKVX_SPI_RX1_REG			(QUICKVX_SPI_BASE | 0x00000004)
+/* SPI Rx2 Register */
+#define QUICKVX_SPI_RX2_REG			(QUICKVX_SPI_BASE | 0x00000008)
+/* SPI Rx3 Register */
+#define QUICKVX_SPI_RX3_REG			(QUICKVX_SPI_BASE | 0x0000000C)
+/* SPI Rx4 Register */
+#define QUICKVX_SPI_RX4_REG			(QUICKVX_SPI_BASE | 0x00000010)
+/* SPI Rx5 Register */
+#define QUICKVX_SPI_RX5_REG			(QUICKVX_SPI_BASE | 0x00000014)
+/* SPI Rx6 Register */
+#define QUICKVX_SPI_RX6_REG			(QUICKVX_SPI_BASE | 0x00000018)
+/* SPI Rx7 Register */
+#define QUICKVX_SPI_RX7_REG			(QUICKVX_SPI_BASE | 0x0000001C)
+/* SPI Tx0 Register */
+#define QUICKVX_SPI_TX0_REG			(QUICKVX_SPI_BASE | 0x00000020)
+/* SPI Tx1 Register */
+#define QUICKVX_SPI_TX1_REG			(QUICKVX_SPI_BASE | 0x00000024)
+/* SPI Tx2 Register */
+#define QUICKVX_SPI_TX2_REG			(QUICKVX_SPI_BASE | 0x00000028)
+/* SPI Tx3 Register */
+#define QUICKVX_SPI_TX3_REG			(QUICKVX_SPI_BASE | 0x0000002C)
+/* SPI Tx4 Register */
+#define QUICKVX_SPI_TX4_REG			(QUICKVX_SPI_BASE | 0x00000030)
+/* SPI Tx5 Register */
+#define QUICKVX_SPI_TX5_REG			(QUICKVX_SPI_BASE | 0x00000034)
+/* SPI Tx6 Register */
+#define QUICKVX_SPI_TX6_REG			(QUICKVX_SPI_BASE | 0x00000038)
+/* SPI Tx7 Register */
+#define QUICKVX_SPI_TX7_REG			(QUICKVX_SPI_BASE | 0x0000003C)
+/* SPI Control Register */
+#define QUICKVX_SPI_CTRL_REG		(QUICKVX_SPI_BASE | 0x00000040)
+/* SPI Transfer Length Register */
+#define QUICKVX_SPI_TLEN_REG		(QUICKVX_SPI_BASE | 0x00000044)
+
+
+/***************************************************
+ * Clock and Reset (CAR) Block Registers
+ ***************************************************/
+/* ASSP Global Clock Enable Register */
+#define QUICKVX_CAR_ASSP_GCE_REG	(QUICKVX_CAR_BASE | 0x00000000)
+/* VLP Control1 Register */
+#define QUICKVX_CAR_VLPCTRL1_REG	(QUICKVX_CAR_BASE | 0x00000004)
+/* VLP Control2 Register */
+#define QUICKVX_CAR_VLPCTRL2_REG	(QUICKVX_CAR_BASE | 0x00000008)
+/* Clock Selection Register */
+#define QUICKVX_CAR_CLKSEL_REG		(QUICKVX_CAR_BASE | 0x0000000C)
+/* PLL Control Register */
+#define QUICKVX_CAR_PLLCTRL_REG		(QUICKVX_CAR_BASE | 0x00000010)
+/* PLL Clock Ratio Register */
+#define QUICKVX_CAR_PLLCLKRATIO_REG	(QUICKVX_CAR_BASE | 0x00000014)
+
+
+/***************************************************
+ * VEE Block Registers
+ ***************************************************/
+/* VEE Control Register */
+#define QUICKVX_VEE_VEECTRL_REG		(QUICKVX_VEE_BASE | 0x00000000)
+/* Strength Register */
+#define QUICKVX_VEE_STRENGTH_REG	(QUICKVX_VEE_BASE | 0x0000000C)
+/* Variance Register */
+#define QUICKVX_VEE_VARIANCE_REG	(QUICKVX_VEE_BASE | 0x00000010)
+/* Slope Register */
+#define QUICKVX_VEE_SLOPE_REG		(QUICKVX_VEE_BASE | 0x00000014)
+/* Sharpen Control0 Register */
+#define QUICKVX_VEE_SHRPCTRL0_REG	(QUICKVX_VEE_BASE | 0x0000001C)
+/* Sharpen Control1 Register */
+#define QUICKVX_VEE_SHRPCTRL1_REG	(QUICKVX_VEE_BASE | 0x00000020)
+/* Upper Horizontal Positon Register */
+#define QUICKVX_VEE_UHPOS_REG		(QUICKVX_VEE_BASE | 0x00000024)
+/* Lower Horizontal Positon Register */
+#define QUICKVX_VEE_LHPOS_REG		(QUICKVX_VEE_BASE | 0x00000028)
+/* Upper Vertical Positon Register */
+#define QUICKVX_VEE_UVPOS_REG		(QUICKVX_VEE_BASE | 0x0000002C)
+/* Lower Vertical Positon Register */
+#define QUICKVX_VEE_LVPOS_REG		(QUICKVX_VEE_BASE | 0x00000030)
+/* Upper Frame Width Register */
+#define QUICKVX_VEE_UFWDTH_REG		(QUICKVX_VEE_BASE | 0x00000034)
+/* Lower Frame Width Register */
+#define QUICKVX_VEE_LFWDTH_REG		(QUICKVX_VEE_BASE | 0x00000038)
+/* Upper Frame Height Register */
+#define QUICKVX_VEE_UFHGHT_REG		(QUICKVX_VEE_BASE | 0x0000003C)
+/* Lower Frame Height Register */
+#define QUICKVX_VEE_LFHGHT_REG		(QUICKVX_VEE_BASE | 0x00000040)
+/* Control0 Register */
+#define QUICKVX_VEE_CTRL0_REG		(QUICKVX_VEE_BASE | 0x00000044)
+/* Control1 Register */
+#define QUICKVX_VEE_CTRL1_REG		(QUICKVX_VEE_BASE | 0x00000048)
+/* Video Enhancement Enable Register */
+#define QUICKVX_VEE_VDOEEN_REG		(QUICKVX_VEE_BASE | 0x0000004C)
+/* Black Level Register */
+#define QUICKVX_VEE_BLCKLEV_REG		(QUICKVX_VEE_BASE | 0x00000050)
+/* White Level Register */
+#define QUICKVX_VEE_WHTLEV_REG		(QUICKVX_VEE_BASE | 0x00000054)
+/* Amplification Limits Register */
+#define QUICKVX_VEE_AMPLMTS_REG		(QUICKVX_VEE_BASE | 0x00000060)
+/* Dithering Mode Register */
+#define QUICKVX_VEE_DITHMOD_REG		(QUICKVX_VEE_BASE | 0x00000064)
+/* Upper Look-up Data Register */
+#define QUICKVX_VEE_ULUD_REG		(QUICKVX_VEE_BASE | 0x00000080)
+/* Lower Look-up Data Register */
+#define QUICKVX_VEE_LLUD_REG		(QUICKVX_VEE_BASE | 0x00000084)
+/* Look-up Address Register */
+#define QUICKVX_VEE_LUADDR_REG		(QUICKVX_VEE_BASE | 0x00000088)
+/* Look-up Write Enable Register */
+#define QUICKVX_VEE_LUWREN_REG		(QUICKVX_VEE_BASE | 0x0000008C)
+/* VEE ID Register */
+#define QUICKVX_VEE_VEEID_REG		(QUICKVX_VEE_BASE | 0x000003FC)
+/* M_11 Register */
+#define QUICKVX_VEE_M_11_REG		(QUICKVX_VEE_BASE | 0x000000C0)
+/* M_12 Register */
+#define QUICKVX_VEE_M_12_REG		(QUICKVX_VEE_BASE | 0x000000C4)
+/* M_13 Register */
+#define QUICKVX_VEE_M_13_REG		(QUICKVX_VEE_BASE | 0x000000C8)
+/* M_21 Register */
+#define QUICKVX_VEE_M_21_REG		(QUICKVX_VEE_BASE | 0x000000CC)
+/* M_22 Register */
+#define QUICKVX_VEE_M_22_REG		(QUICKVX_VEE_BASE | 0x000000D0)
+/* M_23 Register */
+#define QUICKVX_VEE_M_23_REG		(QUICKVX_VEE_BASE | 0x000000D4)
+/* M_31 Register */
+#define QUICKVX_VEE_M_31_REG		(QUICKVX_VEE_BASE | 0x000000D8)
+/* M_32 Register */
+#define QUICKVX_VEE_M_32_REG		(QUICKVX_VEE_BASE | 0x000000DC)
+/* M_33 Register */
+#define QUICKVX_VEE_M_33_REG		(QUICKVX_VEE_BASE | 0x000000E0)
+/* R Offset Register */
+#define QUICKVX_VEE_OFFSET_R_REG	(QUICKVX_VEE_BASE | 0x000000E8)
+/* G Offset Register */
+#define QUICKVX_VEE_OFFSET_G_REG	(QUICKVX_VEE_BASE | 0x000000EC)
+/* B Offset Register */
+#define QUICKVX_VEE_OFFSET_B_REG	(QUICKVX_VEE_BASE | 0x000000F0)
+
+/* LCD Reset Register */
+#define QUICKVX_FB_A2F_LCD_RESET_REG (QUICKVX_FB_A2F_BASE | 0x00000000)
+
+/* Register bit defines */
+/* PLL Lock bit in the PLL Control Register */
+#define QUICKVX_PLL_LOCK_BIT		(1 << 7)
+
+#define QL_SPI_CTRL_rSPISTart(x) (x)
+#define QL_SPI_CTRL_rCPHA(x) (x << 1)
+#define QL_SPI_CTRL_rCPOL(x) (x << 2)
+#define QL_SPI_CTRL_rLSB(x) (x << 3)
+#define QL_SPI_CTRL_rSLVSEL(x) (x << 4)
+#define QL_SPI_CTRL_MASK_rTxDone (1 << 9)
+
+#define QL_SPI_LCD_DEV_ID 0x1c
+#define QL_SPI_LCD_RS(x) (x << 1)
+#define QL_SPI_LCD_RW(x) (x)
+#define QL_SPI_LCD_INDEX_START_BYTE ((QL_SPI_LCD_DEV_ID << 2) | \
+	QL_SPI_LCD_RS(0) | QL_SPI_LCD_RW(0))
+#define QL_SPI_LCD_CMD_START_BYTE ((QL_SPI_LCD_DEV_ID << 2) | \
+	QL_SPI_LCD_RS(1) | QL_SPI_LCD_RW(0))
+#define QL_SPI_CTRL_LCD_START (QL_SPI_CTRL_rSPISTart(1) | \
+	QL_SPI_CTRL_rCPHA(1) | QL_SPI_CTRL_rCPOL(1) | \
+	QL_SPI_CTRL_rLSB(0) | QL_SPI_CTRL_rSLVSEL(0))
+
+int ql_mddi_write(uint32 address, uint32 value)
+{
+	uint32 regval = 0;
+	int ret = 0;
+
+	ret = mddi_queue_register_write(address, value, TRUE, 0);
+
+	if (!ret) {
+		ret = mddi_queue_register_read(address, &regval, TRUE, 0);
+		if (regval != value) {
+			MDDI_MSG_DEBUG("\nMismatch: ql_mddi_write[0x%x]->0x%x "
+				"r0x%x\n", address, value, regval);
+		} else {
+			MDDI_MSG_DEBUG("\nMatch: ql_mddi_write[0x%x]->0x%x "
+				"r0x%x\n", address, value, regval);
+		}
+	}
+
+	return ret;
+}
+
+int ql_mddi_read(uint32 address, uint32 *regval)
+{
+	int ret = 0;
+
+	ret = mddi_queue_register_read(address, regval, TRUE, 0);
+	MDDI_MSG_DEBUG("\nql_mddi_read[0x%x]=0x%x", address, *regval);
+
+	return ret;
+}
+
+int ql_send_spi_cmd_to_lcd(uint32 index, uint32 cmd)
+{
+	int retry, ret;
+	uint32 readval;
+
+	MDDI_MSG_DEBUG("\n %s(): index 0x%x, cmd 0x%x", __func__, index, cmd);
+	/* do the index phase */
+	/* send 24 bits in the index phase */
+	ql_mddi_write(QUICKVX_SPI_TLEN_REG, 23);
+
+	/* send 24 bits in the index phase, starting at bit 23 of TX0 reg */
+	ql_mddi_write(QUICKVX_SPI_TX0_REG,
+		(QL_SPI_LCD_INDEX_START_BYTE << 16) | index);
+
+	/* set start */
+	ql_mddi_write(QUICKVX_SPI_CTRL_REG,  QL_SPI_CTRL_LCD_START);
+	retry = 0;
+
+	do {
+		ret = ql_mddi_read(QUICKVX_SPI_CTRL_REG, &readval);
+
+		if (ret || ++retry > 5) {
+			MDDI_MSG_DEBUG("\n ql_send_spi_cmd_to_lcd: retry "
+				"timeout at index phase, ret = %d", ret);
+			return -EIO;
+		}
+		mddi_wait(1);
+	} while ((readval & QL_SPI_CTRL_MASK_rTxDone) == 0);
+
+	/* do the command phase */
+	/* send 24 bits in the cmd phase */
+	ql_mddi_write(QUICKVX_SPI_TLEN_REG, 23);
+
+	/* send 24 bits in the cmd phase, starting at bit 23 of TX0 reg. */
+	ql_mddi_write(QUICKVX_SPI_TX0_REG,
+		(QL_SPI_LCD_CMD_START_BYTE << 16) | cmd);
+
+	/* set start */
+	ql_mddi_write(QUICKVX_SPI_CTRL_REG,  QL_SPI_CTRL_LCD_START);
+	retry = 0;
+
+	do {
+		ret = ql_mddi_read(QUICKVX_SPI_CTRL_REG, &readval);
+
+		if (ret || ++retry > 5) {
+			MDDI_MSG_DEBUG("\n ql_send_spi_cmd_to_lcd: retry "
+				"timeout at cmd phase, ret = %d", ret);
+			return -EIO;
+		}
+		mddi_wait(1);
+	} while ((readval & QL_SPI_CTRL_MASK_rTxDone) == 0);
+
+	return 0;
+}
+
+
+int ql_send_spi_data_from_lcd(uint32 index, uint32 *value)
+{
+	int retry, ret;
+	uint32 readval;
+
+	MDDI_MSG_DEBUG("\n %s(): index 0x%x", __func__, index);
+	/* do the index phase */
+	/* send 24 bits in the index phase */
+	ql_mddi_write(QUICKVX_SPI_TLEN_REG, 23);
+
+	/* send 24 bits in the index phase, starting at bit 23 of TX0 reg */
+	ql_mddi_write(QUICKVX_SPI_TX0_REG,
+		(QL_SPI_LCD_INDEX_START_BYTE << 16) | index);
+
+	/* set start */
+	ql_mddi_write(QUICKVX_SPI_CTRL_REG,  QL_SPI_CTRL_LCD_START);
+	retry = 0;
+
+	do {
+		ret = ql_mddi_read(QUICKVX_SPI_CTRL_REG, &readval);
+
+		if (ret || ++retry > 5) {
+			MDDI_MSG_DEBUG("\n ql_send_spi_cmd_to_lcd: retry "
+				"timeout at index phase, ret = %d", ret);
+			return -EIO;
+		}
+		mddi_wait(1);
+	} while ((readval & QL_SPI_CTRL_MASK_rTxDone) == 0);
+
+	/* do the command phase */
+	/* send 8 bits  and read 24 bits in the cmd phase, so total 32 bits */
+	ql_mddi_write(QUICKVX_SPI_TLEN_REG, 31);
+
+	/* send 24 bits in the cmd phase, starting at bit 31 of TX0 reg */
+	ql_mddi_write(QUICKVX_SPI_TX0_REG,
+		((QL_SPI_LCD_CMD_START_BYTE << 16)) << 8);
+
+	/* set start */
+	ql_mddi_write(QUICKVX_SPI_CTRL_REG,  QL_SPI_CTRL_LCD_START);
+	retry = 0;
+
+	do {
+		ret = ql_mddi_read(QUICKVX_SPI_CTRL_REG, &readval);
+
+		if (ret || ++retry > 5) {
+			MDDI_MSG_DEBUG("\n ql_send_spi_cmd_to_lcd: retry "
+				"timeout at cmd phase, ret = %d", ret);
+			return -EIO;
+		}
+		mddi_wait(1);
+	} while ((readval & QL_SPI_CTRL_MASK_rTxDone) == 0);
+
+	/* value will appear at lower 16 bits */
+	ret = ql_mddi_read(QUICKVX_SPI_RX0_REG, value);
+
+	if (!ret) {
+		*value = *value & 0xffff;
+		MDDI_MSG_DEBUG("\n QUICKVX_SPI_RX0_REG value = 0x%x", *value);
+	} else
+		MDDI_MSG_DEBUG("\n Read QUICKVX_SPI_RX0_REG Failed");
+
+	return ret;
+}
+
+/* Global Variables */
+static uint32 mddi_quickvx_rows_per_second;
+static uint32 mddi_quickvx_usecs_per_refresh;
+static uint32 mddi_quickvx_rows_per_refresh;
+
+void mddi_quickvx_configure_registers(void)
+{
+	MDDI_MSG_DEBUG("\n%s(): ", __func__);
+	ql_mddi_write(QUICKVX_CAR_CLKSEL_REG, 0x00007000);
+
+	ql_mddi_write(QUICKVX_RCB_PWMW_REG, 0x0000FFFF);
+
+	ql_mddi_write(QUICKVX_RCB_PWMC_REG, 0x00000001);
+
+	ql_mddi_write(QUICKVX_RCB_CONFDONE_REG, 0x00000000);
+
+	/* display is x width = 480, y width = 864 */
+	ql_mddi_write(QUICKVX_RCB_TCON0_REG, 0x035f01df);
+
+	/* VFP=2, VBP=4, HFP=16, HBP=16 */
+	ql_mddi_write(QUICKVX_RCB_TCON1_REG, 0x01e301e1);
+
+	/* VSW =2, HSW=8 */
+	ql_mddi_write(QUICKVX_RCB_TCON2_REG, 0x000000e1);
+
+	ql_mddi_write(QUICKVX_RCB_DISPATTR_REG, 0x00000000);
+
+	ql_mddi_write(QUICKVX_RCB_USECASE_REG, 0x00000025);
+
+	ql_mddi_write(QUICKVX_RCB_VPARM_REG, 0x00000888);
+
+	ql_mddi_write(QUICKVX_RCB_VEECONF_REG, 0x00000001);
+
+	ql_mddi_write(QUICKVX_RCB_IER_REG, 0x00000000);
+
+	ql_mddi_write(QUICKVX_RCB_RCR_REG, 0x80000010);
+
+	ql_mddi_write(QUICKVX_RCB_CELLBCR_REG, 0x8008746F);
+
+	ql_mddi_write(QUICKVX_RCB_CELLCC_REG, 0x800000A3);
+
+	ql_mddi_write(QUICKVX_RCB_CONFDONE_REG, 0x00000001);
+}
+
+void mddi_quickvx_prim_lcd_init(void)
+{
+	uint32 value;
+
+	MDDI_MSG_DEBUG("\n%s(): ", __func__);
+	ql_send_spi_data_from_lcd(0, &value);
+
+	ql_send_spi_cmd_to_lcd(0x0100, 0x3000); /* power control1 */
+	ql_send_spi_cmd_to_lcd(0x0101, 0x4010); /* power control2 */
+	ql_send_spi_cmd_to_lcd(0x0106, 0x0000); /* auto seq setting */
+	mddi_wait(3);
+
+	ql_mddi_write(QUICKVX_FB_A2F_LCD_RESET_REG, 0x00000001);
+	mddi_wait(1);
+	ql_mddi_write(QUICKVX_FB_A2F_LCD_RESET_REG, 0x00000000);
+	mddi_wait(1);
+	ql_mddi_write(QUICKVX_FB_A2F_LCD_RESET_REG, 0x00000001);
+	mddi_wait(10);
+
+	ql_send_spi_cmd_to_lcd(0x0001, 0x0310); /* driver out control */
+	ql_send_spi_cmd_to_lcd(0x0002, 0x0100); /* lcd ac control */
+	ql_send_spi_cmd_to_lcd(0x0003, 0x0000); /* entry mode */
+	ql_send_spi_cmd_to_lcd(0x0007, 0x0000); /* disp cont1 */
+	ql_send_spi_cmd_to_lcd(0x0008, 0x0004); /* disp cont2 */
+	ql_send_spi_cmd_to_lcd(0x0009, 0x000C); /* disp cont3 */
+	ql_send_spi_cmd_to_lcd(0x000C, 0x4010); /* disp if cont1 */
+	ql_send_spi_cmd_to_lcd(0x000E, 0x0000); /* disp if cont2 */
+	ql_send_spi_cmd_to_lcd(0x0020, 0x013F); /* panel if cont1 */
+	ql_send_spi_cmd_to_lcd(0x0022, 0x7600); /* panel if cont3 */
+	ql_send_spi_cmd_to_lcd(0x0023, 0x1C0A); /* panel if cont4 */
+	ql_send_spi_cmd_to_lcd(0x0024, 0x1C2C); /* panel if cont5 */
+	ql_send_spi_cmd_to_lcd(0x0025, 0x1C4E); /* panel if cont6 */
+	ql_send_spi_cmd_to_lcd(0x0027, 0x0000); /* panel if cont8 */
+	ql_send_spi_cmd_to_lcd(0x0028, 0x760C); /* panel if cont9 */
+	ql_send_spi_cmd_to_lcd(0x0300, 0x0000); /* gamma adj0 */
+	ql_send_spi_cmd_to_lcd(0x0301, 0x0502); /* gamma adj1 */
+	ql_send_spi_cmd_to_lcd(0x0302, 0x0705); /* gamma adj2 */
+	ql_send_spi_cmd_to_lcd(0x0303, 0x0000); /* gamma adj3 */
+	ql_send_spi_cmd_to_lcd(0x0304, 0x0200); /* gamma adj4 */
+	ql_send_spi_cmd_to_lcd(0x0305, 0x0707); /* gamma adj5 */
+	ql_send_spi_cmd_to_lcd(0x0306, 0x1010); /* gamma adj6 */
+	ql_send_spi_cmd_to_lcd(0x0307, 0x0202); /* gamma adj7 */
+	ql_send_spi_cmd_to_lcd(0x0308, 0x0704); /* gamma adj8 */
+	ql_send_spi_cmd_to_lcd(0x0309, 0x0707); /* gamma adj9 */
+	ql_send_spi_cmd_to_lcd(0x030A, 0x0000); /* gamma adja */
+	ql_send_spi_cmd_to_lcd(0x030B, 0x0000); /* gamma adjb */
+	ql_send_spi_cmd_to_lcd(0x030C, 0x0707); /* gamma adjc */
+	ql_send_spi_cmd_to_lcd(0x030D, 0x1010); /* gamma adjd */
+	ql_send_spi_cmd_to_lcd(0x0310, 0x0104); /* gamma adj10 */
+	ql_send_spi_cmd_to_lcd(0x0311, 0x0503); /* gamma adj11 */
+	ql_send_spi_cmd_to_lcd(0x0312, 0x0304); /* gamma adj12 */
+	ql_send_spi_cmd_to_lcd(0x0315, 0x0304); /* gamma adj15 */
+	ql_send_spi_cmd_to_lcd(0x0316, 0x031C); /* gamma adj16 */
+	ql_send_spi_cmd_to_lcd(0x0317, 0x0204); /* gamma adj17 */
+	ql_send_spi_cmd_to_lcd(0x0318, 0x0402); /* gamma adj18 */
+	ql_send_spi_cmd_to_lcd(0x0319, 0x0305); /* gamma adj19 */
+	ql_send_spi_cmd_to_lcd(0x031C, 0x0707); /* gamma adj1c */
+	ql_send_spi_cmd_to_lcd(0x031D, 0x021F); /* gamma adj1d */
+	ql_send_spi_cmd_to_lcd(0x0320, 0x0507); /* gamma adj20 */
+	ql_send_spi_cmd_to_lcd(0x0321, 0x0604); /* gamma adj21 */
+	ql_send_spi_cmd_to_lcd(0x0322, 0x0405); /* gamma adj22 */
+	ql_send_spi_cmd_to_lcd(0x0327, 0x0203); /* gamma adj27 */
+	ql_send_spi_cmd_to_lcd(0x0328, 0x0300); /* gamma adj28 */
+	ql_send_spi_cmd_to_lcd(0x0329, 0x0002); /* gamma adj29 */
+	ql_send_spi_cmd_to_lcd(0x0100, 0x363C); /* power cont1 */
+	mddi_wait(1);
+	ql_send_spi_cmd_to_lcd(0x0101, 0x4003); /* power cont2 */
+	ql_send_spi_cmd_to_lcd(0x0102, 0x0001); /* power cont3 */
+	ql_send_spi_cmd_to_lcd(0x0103, 0x3C58); /* power cont4 */
+	ql_send_spi_cmd_to_lcd(0x010C, 0x0135); /* power cont6 */
+	ql_send_spi_cmd_to_lcd(0x0106, 0x0002); /* auto seq */
+	ql_send_spi_cmd_to_lcd(0x0029, 0x03BF); /* panel if cont10 */
+	ql_send_spi_cmd_to_lcd(0x0106, 0x0003); /* auto seq */
+	mddi_wait(5);
+	ql_send_spi_cmd_to_lcd(0x0101, 0x4010); /* power cont2 */
+	mddi_wait(10);
+}
+
+/* Function to Power On the Primary and Secondary LCD panels */
+static int mddi_quickvx_lcd_on(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+
+	MDDI_MSG_DEBUG("\n%s(): ", __func__);
+	mfd = platform_get_drvdata(pdev);
+
+	if (!mfd) {
+		MDDI_MSG_DEBUG("\n mddi_quickvx_lcd_on: Device not found!");
+		return -ENODEV;
+	}
+
+	if (mfd->key != MFD_KEY) {
+		MDDI_MSG_DEBUG("\n mddi_quickvx_lcd_on: Invalid MFD key!");
+		return -EINVAL;
+	}
+
+	mddi_host_client_cnt_reset();
+	mddi_quickvx_configure_registers();
+	mddi_quickvx_prim_lcd_init();
+
+	return 0;
+}
+
+
+/* Function to Power Off the Primary and Secondary LCD panels */
+static int mddi_quickvx_lcd_off(struct platform_device *pdev)
+{
+	MDDI_MSG_DEBUG("\n%s(): ", __func__);
+	mddi_wait(1);
+	ql_send_spi_cmd_to_lcd(0x0106, 0x0002); /* Auto Sequencer setting */
+	mddi_wait(10);
+	ql_send_spi_cmd_to_lcd(0x0106, 0x0000); /* Auto Sequencer setting */
+	ql_send_spi_cmd_to_lcd(0x0029, 0x0002); /* Panel IF control 10 */
+	ql_send_spi_cmd_to_lcd(0x0100, 0x300D); /* Power Control 1 */
+	mddi_wait(1);
+
+	return 0;
+}
+
+/* Function to set the Backlight brightness level */
+static void mddi_quickvx_lcd_set_backlight(struct msm_fb_data_type *mfd)
+{
+	int32 level, i = 0, ret;
+
+	MDDI_MSG_DEBUG("%s(): ", __func__);
+
+	level = mfd->bl_level;
+	MDDI_MSG_DEBUG("\n level = %d", level);
+	if (level < 0) {
+		MDDI_MSG_DEBUG("mddi_quickvx_lcd_set_backlight: "
+			"Invalid backlight level (%d)!\n", level);
+		return;
+	}
+	while (i++ < 3) {
+		ret = pmic_set_led_intensity(LED_LCD, level);
+		if (ret == 0)
+			return;
+		msleep(10);
+	}
+
+	MDDI_MSG_DEBUG("%s: can't set lcd backlight!\n",
+				__func__);
+}
+
+/* Driver Probe function */
+static int __devinit mddi_quickvx_lcd_probe(struct platform_device *pdev)
+{
+	MDDI_MSG_DEBUG("\n%s(): id is %d", __func__, pdev->id);
+	msm_fb_add_device(pdev);
+	return 0;
+}
+
+/* Driver data structure */
+static struct platform_driver this_driver = {
+	.probe  = mddi_quickvx_lcd_probe,
+	.driver	= {
+		.name	= "mddi_quickvx",
+	},
+};
+
+
+/* Primary LCD panel data structure */
+static struct msm_fb_panel_data mddi_quickvx_panel_data0 = {
+	.on					= mddi_quickvx_lcd_on,
+	.off				= mddi_quickvx_lcd_off,
+	.set_backlight		= mddi_quickvx_lcd_set_backlight,
+};
+
+
+/* Primary LCD panel device structure */
+static struct platform_device this_device0 = {
+	.name   = "mddi_quickvx",
+	.id		= MDDI_QUICKVX_1_2,
+	.dev	= {
+		.platform_data = &mddi_quickvx_panel_data0,
+	}
+};
+
+/* Module init - driver main entry point */
+static int __init mddi_quickvx_lcd_init(void)
+{
+	int ret;
+	struct msm_panel_info *pinfo;
+
+#ifdef CONFIG_FB_MSM_MDDI_AUTO_DETECT
+	u32 cid;
+	MDDI_MSG_DEBUG("\n%s(): ", __func__);
+
+	ret = msm_fb_detect_client("mddi_quickvx");
+
+	if (ret == -ENODEV)	{
+		/* Device not found */
+		MDDI_MSG_DEBUG("\n mddi_quickvx_lcd_init: No device found!");
+		return 0;
+	}
+
+	if (ret) {
+		cid = mddi_get_client_id();
+
+		MDDI_MSG_DEBUG("\n cid = 0x%x", cid);
+		if (((cid >> 16) != QUICKVX_MDDI_MFR_CODE) ||
+			((cid & 0xFFFF) != QUICKVX_MDDI_PRD_CODE)) {
+			/* MDDI Client ID not matching */
+			MDDI_MSG_DEBUG("\n mddi_quickvx_lcd_init: "
+				"Client ID missmatch!");
+
+			return 0;
+		}
+		MDDI_MSG_DEBUG("\n mddi_quickvx_lcd_init: "
+			"QuickVX LCD panel detected!");
+	}
+
+#endif /* CONFIG_FB_MSM_MDDI_AUTO_DETECT */
+
+	mddi_quickvx_rows_per_refresh = 872;
+	mddi_quickvx_rows_per_second = 52364;
+	mddi_quickvx_usecs_per_refresh = 16574;
+
+	ret = platform_driver_register(&this_driver);
+
+	if (!ret) {
+		pinfo = &mddi_quickvx_panel_data0.panel_info;
+		pinfo->xres = 480;
+		pinfo->yres = 864;
+		MSM_FB_SINGLE_MODE_PANEL(pinfo);
+		pinfo->type = MDDI_PANEL;
+		pinfo->pdest = DISPLAY_1;
+		pinfo->mddi.vdopkt = MDDI_DEFAULT_PRIM_PIX_ATTR;
+		pinfo->wait_cycle = 0;
+		pinfo->bpp = 24;
+		pinfo->fb_num = 2;
+
+		pinfo->clk_rate = 192000000;
+		pinfo->clk_min = 192000000;
+		pinfo->clk_max = 200000000;
+		pinfo->lcd.rev = 1;
+		pinfo->lcd.vsync_enable = TRUE;
+		pinfo->lcd.refx100 = (mddi_quickvx_rows_per_second \
+			* 100)/mddi_quickvx_rows_per_refresh;
+		pinfo->mddi.is_type1 = TRUE;
+		pinfo->lcd.v_back_porch = 4;
+		pinfo->lcd.v_front_porch = 2;
+		pinfo->lcd.v_pulse_width = 2;
+		pinfo->lcd.hw_vsync_mode = TRUE;
+		pinfo->lcd.vsync_notifier_period = (1 * HZ);
+		pinfo->bl_max = 10;
+		pinfo->bl_min = 0;
+
+		ret = platform_device_register(&this_device0);
+		if (ret) {
+			platform_driver_unregister(&this_driver);
+			MDDI_MSG_DEBUG("mddi_quickvx_lcd_init: "
+				"Primary device registration failed!\n");
+		}
+	}
+
+	return ret;
+}
+
+module_init(mddi_quickvx_lcd_init);
+
diff --git a/drivers/video/msm/mddi_sharp.c b/drivers/video/msm/mddi_sharp.c
new file mode 100644
index 0000000..6a9008f
--- /dev/null
+++ b/drivers/video/msm/mddi_sharp.c
@@ -0,0 +1,901 @@
+/* Copyright (c) 2008-2010, 2012 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_fb.h"
+#include "mddihost.h"
+#include "mddihosti.h"
+
+#define SHARP_QVGA_PRIM 1
+#define SHARP_128X128_SECD 2
+
+extern uint32 mddi_host_core_version;
+static boolean mddi_debug_prim_wait = FALSE;
+static boolean mddi_sharp_vsync_wake = TRUE;
+static boolean mddi_sharp_monitor_refresh_value = TRUE;
+static boolean mddi_sharp_report_refresh_measurements = FALSE;
+static uint32 mddi_sharp_rows_per_second = 13830;	/* 5200000/376 */
+static uint32 mddi_sharp_rows_per_refresh = 338;
+static uint32 mddi_sharp_usecs_per_refresh = 24440;	/* (376+338)/5200000 */
+static boolean mddi_sharp_debug_60hz_refresh = FALSE;
+
+extern mddi_gpio_info_type mddi_gpio;
+extern boolean mddi_vsync_detect_enabled;
+static msm_fb_vsync_handler_type mddi_sharp_vsync_handler;
+static void *mddi_sharp_vsync_handler_arg;
+static uint16 mddi_sharp_vsync_attempts;
+
+static void mddi_sharp_prim_lcd_init(void);
+static void mddi_sharp_sub_lcd_init(void);
+static void mddi_sharp_lcd_set_backlight(struct msm_fb_data_type *mfd);
+static void mddi_sharp_vsync_set_handler(msm_fb_vsync_handler_type handler,
+					 void *);
+static void mddi_sharp_lcd_vsync_detected(boolean detected);
+static struct msm_panel_common_pdata *mddi_sharp_pdata;
+
+#define REG_SYSCTL    0x0000
+#define REG_INTR    0x0006
+#define REG_CLKCNF    0x000C
+#define REG_CLKDIV1    0x000E
+#define REG_CLKDIV2    0x0010
+
+#define REG_GIOD    0x0040
+#define REG_GIOA    0x0042
+
+#define REG_AGM      0x010A
+#define REG_FLFT    0x0110
+#define REG_FRGT    0x0112
+#define REG_FTOP    0x0114
+#define REG_FBTM    0x0116
+#define REG_FSTRX    0x0118
+#define REG_FSTRY    0x011A
+#define REG_VRAM    0x0202
+#define REG_SSDCTL    0x0330
+#define REG_SSD0    0x0332
+#define REG_PSTCTL1    0x0400
+#define REG_PSTCTL2    0x0402
+#define REG_PTGCTL    0x042A
+#define REG_PTHP    0x042C
+#define REG_PTHB    0x042E
+#define REG_PTHW    0x0430
+#define REG_PTHF    0x0432
+#define REG_PTVP    0x0434
+#define REG_PTVB    0x0436
+#define REG_PTVW    0x0438
+#define REG_PTVF    0x043A
+#define REG_VBLKS    0x0458
+#define REG_VBLKE    0x045A
+#define REG_SUBCTL    0x0700
+#define REG_SUBTCMD    0x0702
+#define REG_SUBTCMDD  0x0704
+#define REG_REVBYTE    0x0A02
+#define REG_REVCNT    0x0A04
+#define REG_REVATTR    0x0A06
+#define REG_REVFMT    0x0A08
+
+#define SHARP_SUB_UNKNOWN 0xffffffff
+#define SHARP_SUB_HYNIX 1
+#define SHARP_SUB_ROHM  2
+
+static uint32 sharp_subpanel_type = SHARP_SUB_UNKNOWN;
+
+static void sub_through_write(int sub_rs, uint32 sub_data)
+{
+	mddi_queue_register_write(REG_SUBTCMDD, sub_data, FALSE, 0);
+
+	/* CS=1,RD=1,WE=1,RS=sub_rs */
+	mddi_queue_register_write(REG_SUBTCMD, 0x000e | sub_rs, FALSE, 0);
+
+	/* CS=0,RD=1,WE=1,RS=sub_rs */
+	mddi_queue_register_write(REG_SUBTCMD, 0x0006 | sub_rs, FALSE, 0);
+
+	/* CS=0,RD=1,WE=0,RS=sub_rs */
+	mddi_queue_register_write(REG_SUBTCMD, 0x0004 | sub_rs, FALSE, 0);
+
+	/* CS=0,RD=1,WE=1,RS=sub_rs */
+	mddi_queue_register_write(REG_SUBTCMD, 0x0006 | sub_rs, FALSE, 0);
+
+	/* CS=1,RD=1,WE=1,RS=sub_rs */
+	mddi_queue_register_write(REG_SUBTCMD, 0x000e | sub_rs, TRUE, 0);
+}
+
+static uint32 sub_through_read(int sub_rs)
+{
+	uint32 sub_data;
+
+	/* CS=1,RD=1,WE=1,RS=sub_rs */
+	mddi_queue_register_write(REG_SUBTCMD, 0x000e | sub_rs, FALSE, 0);
+
+	/* CS=0,RD=1,WE=1,RS=sub_rs */
+	mddi_queue_register_write(REG_SUBTCMD, 0x0006 | sub_rs, FALSE, 0);
+
+	/* CS=0,RD=1,WE=0,RS=sub_rs */
+	mddi_queue_register_write(REG_SUBTCMD, 0x0002 | sub_rs, TRUE, 0);
+
+	mddi_queue_register_read(REG_SUBTCMDD, &sub_data, TRUE, 0);
+
+	/* CS=0,RD=1,WE=1,RS=sub_rs */
+	mddi_queue_register_write(REG_SUBTCMD, 0x0006 | sub_rs, FALSE, 0);
+
+	/* CS=1,RD=1,WE=1,RS=sub_rs */
+	mddi_queue_register_write(REG_SUBTCMD, 0x000e | sub_rs, TRUE, 0);
+
+	return sub_data;
+}
+
+static void serigo(uint32 ssd)
+{
+	uint32 ssdctl;
+
+	mddi_queue_register_read(REG_SSDCTL, &ssdctl, TRUE, 0);
+	ssdctl = ((ssdctl & 0xE7) | 0x02);
+
+	mddi_queue_register_write(REG_SSD0, ssd, FALSE, 0);
+	mddi_queue_register_write(REG_SSDCTL, ssdctl, TRUE, 0);
+
+	do {
+		mddi_queue_register_read(REG_SSDCTL, &ssdctl, TRUE, 0);
+	} while ((ssdctl & 0x0002) != 0);
+
+	if (mddi_debug_prim_wait)
+		mddi_wait(2);
+}
+
+static void mddi_sharp_lcd_powerdown(void)
+{
+	serigo(0x0131);
+	serigo(0x0300);
+	mddi_wait(40);
+	serigo(0x0135);
+	mddi_wait(20);
+	serigo(0x2122);
+	mddi_wait(20);
+	serigo(0x0201);
+	mddi_wait(20);
+	serigo(0x2100);
+	mddi_wait(20);
+	serigo(0x2000);
+	mddi_wait(20);
+
+	mddi_queue_register_write(REG_PSTCTL1, 0x1, TRUE, 0);
+	mddi_wait(100);
+	mddi_queue_register_write(REG_PSTCTL1, 0x0, TRUE, 0);
+	mddi_wait(2);
+	mddi_queue_register_write(REG_SYSCTL, 0x1, TRUE, 0);
+	mddi_wait(2);
+	mddi_queue_register_write(REG_CLKDIV1, 0x3, TRUE, 0);
+	mddi_wait(2);
+	mddi_queue_register_write(REG_SSDCTL, 0x0000, TRUE, 0);	/* SSDRESET */
+	mddi_queue_register_write(REG_SYSCTL, 0x0, TRUE, 0);
+	mddi_wait(2);
+}
+
+static void mddi_sharp_lcd_set_backlight(struct msm_fb_data_type *mfd)
+{
+	uint32 regdata;
+	int32 level;
+	int max = mfd->panel_info.bl_max;
+	int min = mfd->panel_info.bl_min;
+
+	if (mddi_sharp_pdata && mddi_sharp_pdata->backlight_level) {
+		level = mddi_sharp_pdata->backlight_level(mfd->bl_level,
+							  max,
+							  min);
+
+		if (level < 0)
+			return;
+
+		/* use Rodem GPIO(2:0) to give 8 levels of backlight (7-0) */
+		/* Set lower 3 GPIOs as Outputs (set to 0) */
+		mddi_queue_register_read(REG_GIOA, &regdata, TRUE, 0);
+		mddi_queue_register_write(REG_GIOA, regdata & 0xfff8, TRUE, 0);
+
+		/* Set lower 3 GPIOs as level */
+		mddi_queue_register_read(REG_GIOD, &regdata, TRUE, 0);
+		mddi_queue_register_write(REG_GIOD,
+			  (regdata & 0xfff8) | (0x07 & level), TRUE, 0);
+	}
+}
+
+static void mddi_sharp_prim_lcd_init(void)
+{
+	mddi_queue_register_write(REG_SYSCTL, 0x4000, TRUE, 0);
+	mddi_wait(1);
+	mddi_queue_register_write(REG_SYSCTL, 0x0000, TRUE, 0);
+	mddi_wait(5);
+	mddi_queue_register_write(REG_SYSCTL, 0x0001, FALSE, 0);
+	mddi_queue_register_write(REG_CLKDIV1, 0x000b, FALSE, 0);
+
+	/* new reg write below */
+	if (mddi_sharp_debug_60hz_refresh)
+		mddi_queue_register_write(REG_CLKCNF, 0x070d, FALSE, 0);
+	else
+		mddi_queue_register_write(REG_CLKCNF, 0x0708, FALSE, 0);
+
+	mddi_queue_register_write(REG_SYSCTL, 0x0201, FALSE, 0);
+	mddi_queue_register_write(REG_PTGCTL, 0x0010, FALSE, 0);
+	mddi_queue_register_write(REG_PTHP, 4, FALSE, 0);
+	mddi_queue_register_write(REG_PTHB, 40, FALSE, 0);
+	mddi_queue_register_write(REG_PTHW, 240, FALSE, 0);
+	if (mddi_sharp_debug_60hz_refresh)
+		mddi_queue_register_write(REG_PTHF, 12, FALSE, 0);
+	else
+		mddi_queue_register_write(REG_PTHF, 92, FALSE, 0);
+
+	mddi_wait(1);
+
+	mddi_queue_register_write(REG_PTVP, 1, FALSE, 0);
+	mddi_queue_register_write(REG_PTVB, 2, FALSE, 0);
+	mddi_queue_register_write(REG_PTVW, 320, FALSE, 0);
+	mddi_queue_register_write(REG_PTVF, 15, FALSE, 0);
+
+	mddi_wait(1);
+
+	/* vram_color set REG_AGM???? */
+	mddi_queue_register_write(REG_AGM, 0x0000, TRUE, 0);
+
+	mddi_queue_register_write(REG_SSDCTL, 0x0000, FALSE, 0);
+	mddi_queue_register_write(REG_SSDCTL, 0x0001, TRUE, 0);
+	mddi_wait(1);
+	mddi_queue_register_write(REG_PSTCTL1, 0x0001, TRUE, 0);
+	mddi_wait(10);
+
+	serigo(0x0701);
+	/* software reset */
+	mddi_wait(1);
+	/* Wait over 50us */
+
+	serigo(0x0400);
+	/* DCLK~ACHSYNC~ACVSYNC polarity setting */
+	serigo(0x2900);
+	/* EEPROM start read address setting */
+	serigo(0x2606);
+	/* EEPROM start read register setting */
+	mddi_wait(20);
+	/* Wait over 20ms */
+
+	serigo(0x0503);
+	/* Horizontal timing setting */
+	serigo(0x062C);
+	/* Veritical timing setting */
+	serigo(0x2001);
+	/* power initialize setting(VDC2) */
+	mddi_wait(20);
+	/* Wait over 20ms */
+
+	serigo(0x2120);
+	/* Initialize power setting(CPS) */
+	mddi_wait(20);
+	/* Wait over 20ms */
+
+	serigo(0x2130);
+	/* Initialize power setting(CPS) */
+	mddi_wait(20);
+	/* Wait over 20ms */
+
+	serigo(0x2132);
+	/* Initialize power setting(CPS) */
+	mddi_wait(10);
+	/* Wait over 10ms */
+
+	serigo(0x2133);
+	/* Initialize power setting(CPS) */
+	mddi_wait(20);
+	/* Wait over 20ms */
+
+	serigo(0x0200);
+	/* Panel initialize release(INIT) */
+	mddi_wait(1);
+	/* Wait over 1ms */
+
+	serigo(0x0131);
+	/* Panel setting(CPS) */
+	mddi_wait(1);
+	/* Wait over 1ms */
+
+	mddi_queue_register_write(REG_PSTCTL1, 0x0003, TRUE, 0);
+
+	/* if (FFA LCD is upside down) -> serigo(0x0100); */
+	serigo(0x0130);
+
+	/* Black mask release(display ON) */
+	mddi_wait(1);
+	/* Wait over 1ms */
+
+	if (mddi_sharp_vsync_wake) {
+		mddi_queue_register_write(REG_VBLKS, 0x1001, TRUE, 0);
+		mddi_queue_register_write(REG_VBLKE, 0x1002, TRUE, 0);
+	}
+
+	/* Set the MDP pixel data attributes for Primary Display */
+	mddi_host_write_pix_attr_reg(0x00C3);
+	return;
+
+}
+
+void mddi_sharp_sub_lcd_init(void)
+{
+
+	mddi_queue_register_write(REG_SYSCTL, 0x4000, FALSE, 0);
+	mddi_queue_register_write(REG_SYSCTL, 0x0000, TRUE, 0);
+	mddi_wait(100);
+
+	mddi_queue_register_write(REG_SYSCTL, 0x0001, FALSE, 0);
+	mddi_queue_register_write(REG_CLKDIV1, 0x000b, FALSE, 0);
+	mddi_queue_register_write(REG_CLKCNF, 0x0708, FALSE, 0);
+	mddi_queue_register_write(REG_SYSCTL, 0x0201, FALSE, 0);
+	mddi_queue_register_write(REG_PTGCTL, 0x0010, FALSE, 0);
+	mddi_queue_register_write(REG_PTHP, 4, FALSE, 0);
+	mddi_queue_register_write(REG_PTHB, 40, FALSE, 0);
+	mddi_queue_register_write(REG_PTHW, 128, FALSE, 0);
+	mddi_queue_register_write(REG_PTHF, 92, FALSE, 0);
+	mddi_queue_register_write(REG_PTVP, 1, FALSE, 0);
+	mddi_queue_register_write(REG_PTVB, 2, FALSE, 0);
+	mddi_queue_register_write(REG_PTVW, 128, FALSE, 0);
+	mddi_queue_register_write(REG_PTVF, 15, FALSE, 0);
+
+	/* Now the sub display..... */
+	/* Reset High */
+	mddi_queue_register_write(REG_SUBCTL, 0x0200, FALSE, 0);
+	/* CS=1,RD=1,WE=1,RS=1 */
+	mddi_queue_register_write(REG_SUBTCMD, 0x000f, TRUE, 0);
+	mddi_wait(1);
+	/* Wait 5us */
+
+	if (sharp_subpanel_type == SHARP_SUB_UNKNOWN) {
+		uint32 data;
+
+		sub_through_write(1, 0x05);
+		sub_through_write(1, 0x6A);
+		sub_through_write(1, 0x1D);
+		sub_through_write(1, 0x05);
+		data = sub_through_read(1);
+		if (data == 0x6A) {
+			sharp_subpanel_type = SHARP_SUB_HYNIX;
+		} else {
+			sub_through_write(0, 0x36);
+			sub_through_write(1, 0xA8);
+			sub_through_write(0, 0x09);
+			data = sub_through_read(1);
+			data = sub_through_read(1);
+			if (data == 0x54) {
+				sub_through_write(0, 0x36);
+				sub_through_write(1, 0x00);
+				sharp_subpanel_type = SHARP_SUB_ROHM;
+			}
+		}
+	}
+
+	if (sharp_subpanel_type == SHARP_SUB_HYNIX) {
+		sub_through_write(1, 0x00);	/* Display setting 1 */
+		sub_through_write(1, 0x04);
+		sub_through_write(1, 0x01);
+		sub_through_write(1, 0x05);
+		sub_through_write(1, 0x0280);
+		sub_through_write(1, 0x0301);
+		sub_through_write(1, 0x0402);
+		sub_through_write(1, 0x0500);
+		sub_through_write(1, 0x0681);
+		sub_through_write(1, 0x077F);
+		sub_through_write(1, 0x08C0);
+		sub_through_write(1, 0x0905);
+		sub_through_write(1, 0x0A02);
+		sub_through_write(1, 0x0B00);
+		sub_through_write(1, 0x0C00);
+		sub_through_write(1, 0x0D00);
+		sub_through_write(1, 0x0E00);
+		sub_through_write(1, 0x0F00);
+
+		sub_through_write(1, 0x100B);	/* Display setting 2 */
+		sub_through_write(1, 0x1103);
+		sub_through_write(1, 0x1237);
+		sub_through_write(1, 0x1300);
+		sub_through_write(1, 0x1400);
+		sub_through_write(1, 0x1500);
+		sub_through_write(1, 0x1605);
+		sub_through_write(1, 0x1700);
+		sub_through_write(1, 0x1800);
+		sub_through_write(1, 0x192E);
+		sub_through_write(1, 0x1A00);
+		sub_through_write(1, 0x1B00);
+		sub_through_write(1, 0x1C00);
+
+		sub_through_write(1, 0x151A);	/* Power setting */
+
+		sub_through_write(1, 0x2002);	/* Gradation Palette setting */
+		sub_through_write(1, 0x2107);
+		sub_through_write(1, 0x220C);
+		sub_through_write(1, 0x2310);
+		sub_through_write(1, 0x2414);
+		sub_through_write(1, 0x2518);
+		sub_through_write(1, 0x261C);
+		sub_through_write(1, 0x2720);
+		sub_through_write(1, 0x2824);
+		sub_through_write(1, 0x2928);
+		sub_through_write(1, 0x2A2B);
+		sub_through_write(1, 0x2B2E);
+		sub_through_write(1, 0x2C31);
+		sub_through_write(1, 0x2D34);
+		sub_through_write(1, 0x2E37);
+		sub_through_write(1, 0x2F3A);
+		sub_through_write(1, 0x303C);
+		sub_through_write(1, 0x313E);
+		sub_through_write(1, 0x323F);
+		sub_through_write(1, 0x3340);
+		sub_through_write(1, 0x3441);
+		sub_through_write(1, 0x3543);
+		sub_through_write(1, 0x3646);
+		sub_through_write(1, 0x3749);
+		sub_through_write(1, 0x384C);
+		sub_through_write(1, 0x394F);
+		sub_through_write(1, 0x3A52);
+		sub_through_write(1, 0x3B59);
+		sub_through_write(1, 0x3C60);
+		sub_through_write(1, 0x3D67);
+		sub_through_write(1, 0x3E6E);
+		sub_through_write(1, 0x3F7F);
+		sub_through_write(1, 0x4001);
+		sub_through_write(1, 0x4107);
+		sub_through_write(1, 0x420C);
+		sub_through_write(1, 0x4310);
+		sub_through_write(1, 0x4414);
+		sub_through_write(1, 0x4518);
+		sub_through_write(1, 0x461C);
+		sub_through_write(1, 0x4720);
+		sub_through_write(1, 0x4824);
+		sub_through_write(1, 0x4928);
+		sub_through_write(1, 0x4A2B);
+		sub_through_write(1, 0x4B2E);
+		sub_through_write(1, 0x4C31);
+		sub_through_write(1, 0x4D34);
+		sub_through_write(1, 0x4E37);
+		sub_through_write(1, 0x4F3A);
+		sub_through_write(1, 0x503C);
+		sub_through_write(1, 0x513E);
+		sub_through_write(1, 0x523F);
+		sub_through_write(1, 0x5340);
+		sub_through_write(1, 0x5441);
+		sub_through_write(1, 0x5543);
+		sub_through_write(1, 0x5646);
+		sub_through_write(1, 0x5749);
+		sub_through_write(1, 0x584C);
+		sub_through_write(1, 0x594F);
+		sub_through_write(1, 0x5A52);
+		sub_through_write(1, 0x5B59);
+		sub_through_write(1, 0x5C60);
+		sub_through_write(1, 0x5D67);
+		sub_through_write(1, 0x5E6E);
+		sub_through_write(1, 0x5F7E);
+		sub_through_write(1, 0x6000);
+		sub_through_write(1, 0x6107);
+		sub_through_write(1, 0x620C);
+		sub_through_write(1, 0x6310);
+		sub_through_write(1, 0x6414);
+		sub_through_write(1, 0x6518);
+		sub_through_write(1, 0x661C);
+		sub_through_write(1, 0x6720);
+		sub_through_write(1, 0x6824);
+		sub_through_write(1, 0x6928);
+		sub_through_write(1, 0x6A2B);
+		sub_through_write(1, 0x6B2E);
+		sub_through_write(1, 0x6C31);
+		sub_through_write(1, 0x6D34);
+		sub_through_write(1, 0x6E37);
+		sub_through_write(1, 0x6F3A);
+		sub_through_write(1, 0x703C);
+		sub_through_write(1, 0x713E);
+		sub_through_write(1, 0x723F);
+		sub_through_write(1, 0x7340);
+		sub_through_write(1, 0x7441);
+		sub_through_write(1, 0x7543);
+		sub_through_write(1, 0x7646);
+		sub_through_write(1, 0x7749);
+		sub_through_write(1, 0x784C);
+		sub_through_write(1, 0x794F);
+		sub_through_write(1, 0x7A52);
+		sub_through_write(1, 0x7B59);
+		sub_through_write(1, 0x7C60);
+		sub_through_write(1, 0x7D67);
+		sub_through_write(1, 0x7E6E);
+		sub_through_write(1, 0x7F7D);
+
+		sub_through_write(1, 0x1851);	/* Display on */
+
+		mddi_queue_register_write(REG_AGM, 0x0000, TRUE, 0);
+
+		/* 1 pixel / 1 post clock */
+		mddi_queue_register_write(REG_CLKDIV2, 0x3b00, FALSE, 0);
+
+		/* SUB LCD select */
+		mddi_queue_register_write(REG_PSTCTL2, 0x0080, FALSE, 0);
+
+		/* RS=0,command initiate number=0,select master mode */
+		mddi_queue_register_write(REG_SUBCTL, 0x0202, FALSE, 0);
+
+		/* Sub LCD Data transform start */
+		mddi_queue_register_write(REG_PSTCTL1, 0x0003, FALSE, 0);
+
+	} else if (sharp_subpanel_type == SHARP_SUB_ROHM) {
+
+		sub_through_write(0, 0x01);	/* Display setting */
+		sub_through_write(1, 0x00);
+
+		mddi_wait(1);
+		/* Wait 100us  <----- ******* Update 2005/01/24 */
+
+		sub_through_write(0, 0xB6);
+		sub_through_write(1, 0x0C);
+		sub_through_write(1, 0x4A);
+		sub_through_write(1, 0x20);
+		sub_through_write(0, 0x3A);
+		sub_through_write(1, 0x05);
+		sub_through_write(0, 0xB7);
+		sub_through_write(1, 0x01);
+		sub_through_write(0, 0xBA);
+		sub_through_write(1, 0x20);
+		sub_through_write(1, 0x02);
+		sub_through_write(0, 0x25);
+		sub_through_write(1, 0x4F);
+		sub_through_write(0, 0xBB);
+		sub_through_write(1, 0x00);
+		sub_through_write(0, 0x36);
+		sub_through_write(1, 0x00);
+		sub_through_write(0, 0xB1);
+		sub_through_write(1, 0x05);
+		sub_through_write(0, 0xBE);
+		sub_through_write(1, 0x80);
+		sub_through_write(0, 0x26);
+		sub_through_write(1, 0x01);
+		sub_through_write(0, 0x2A);
+		sub_through_write(1, 0x02);
+		sub_through_write(1, 0x81);
+		sub_through_write(0, 0x2B);
+		sub_through_write(1, 0x00);
+		sub_through_write(1, 0x7F);
+
+		sub_through_write(0, 0x2C);
+		sub_through_write(0, 0x11);	/* Sleep mode off */
+
+		mddi_wait(1);
+		/* Wait 100 ms <----- ******* Update 2005/01/24 */
+
+		sub_through_write(0, 0x29);	/* Display on */
+		sub_through_write(0, 0xB3);
+		sub_through_write(1, 0x20);
+		sub_through_write(1, 0xAA);
+		sub_through_write(1, 0xA0);
+		sub_through_write(1, 0x20);
+		sub_through_write(1, 0x30);
+		sub_through_write(1, 0xA6);
+		sub_through_write(1, 0xFF);
+		sub_through_write(1, 0x9A);
+		sub_through_write(1, 0x9F);
+		sub_through_write(1, 0xAF);
+		sub_through_write(1, 0xBC);
+		sub_through_write(1, 0xCF);
+		sub_through_write(1, 0xDF);
+		sub_through_write(1, 0x20);
+		sub_through_write(1, 0x9C);
+		sub_through_write(1, 0x8A);
+
+		sub_through_write(0, 0x002C);	/* Display on */
+
+		/* 1 pixel / 2 post clock */
+		mddi_queue_register_write(REG_CLKDIV2, 0x7b00, FALSE, 0);
+
+		/* SUB LCD select */
+		mddi_queue_register_write(REG_PSTCTL2, 0x0080, FALSE, 0);
+
+		/* RS=1,command initiate number=0,select master mode */
+		mddi_queue_register_write(REG_SUBCTL, 0x0242, FALSE, 0);
+
+		/* Sub LCD Data transform start */
+		mddi_queue_register_write(REG_PSTCTL1, 0x0003, FALSE, 0);
+
+	}
+
+	/* Set the MDP pixel data attributes for Sub Display */
+	mddi_host_write_pix_attr_reg(0x00C0);
+}
+
+void mddi_sharp_lcd_vsync_detected(boolean detected)
+{
+	/* static timetick_type start_time = 0; */
+	static struct timeval start_time;
+	static boolean first_time = TRUE;
+	/* uint32 mdp_cnt_val = 0; */
+	/* timetick_type elapsed_us; */
+	struct timeval now;
+	uint32 elapsed_us;
+	uint32 num_vsyncs;
+
+	if ((detected) || (mddi_sharp_vsync_attempts > 5)) {
+		if ((detected) && (mddi_sharp_monitor_refresh_value)) {
+			/* if (start_time != 0) */
+			if (!first_time) {
+				jiffies_to_timeval(jiffies, &now);
+				elapsed_us =
+				    (now.tv_sec - start_time.tv_sec) * 1000000 +
+				    now.tv_usec - start_time.tv_usec;
+				/*
+				* LCD is configured for a refresh every usecs,
+				* so to determine the number of vsyncs that
+				* have occurred since the last measurement add
+				* half that to the time difference and divide
+				* by the refresh rate.
+				*/
+				num_vsyncs = (elapsed_us +
+					      (mddi_sharp_usecs_per_refresh >>
+					       1)) /
+				    mddi_sharp_usecs_per_refresh;
+				/*
+				 * LCD is configured for * hsyncs (rows) per
+				 * refresh cycle. Calculate new rows_per_second
+				 * value based upon these new measurements.
+				 * MDP can update with this new value.
+				 */
+				mddi_sharp_rows_per_second =
+				    (mddi_sharp_rows_per_refresh * 1000 *
+				     num_vsyncs) / (elapsed_us / 1000);
+			}
+			/* start_time = timetick_get(); */
+			first_time = FALSE;
+			jiffies_to_timeval(jiffies, &start_time);
+			if (mddi_sharp_report_refresh_measurements) {
+				/* mdp_cnt_val = MDP_LINE_COUNT; */
+			}
+		}
+		/* if detected = TRUE, client initiated wakeup was detected */
+		if (mddi_sharp_vsync_handler != NULL) {
+			(*mddi_sharp_vsync_handler)
+			    (mddi_sharp_vsync_handler_arg);
+			mddi_sharp_vsync_handler = NULL;
+		}
+		mddi_vsync_detect_enabled = FALSE;
+		mddi_sharp_vsync_attempts = 0;
+		/* need to clear this vsync wakeup */
+		if (!mddi_queue_register_write_int(REG_INTR, 0x0000)) {
+			MDDI_MSG_ERR("Vsync interrupt clear failed!\n");
+		}
+		if (!detected) {
+			/* give up after 5 failed attempts but show error */
+			MDDI_MSG_NOTICE("Vsync detection failed!\n");
+		} else if ((mddi_sharp_monitor_refresh_value) &&
+			(mddi_sharp_report_refresh_measurements)) {
+			MDDI_MSG_NOTICE("  Lines Per Second=%d!\n",
+				mddi_sharp_rows_per_second);
+		}
+	} else
+		/* if detected = FALSE, we woke up from hibernation, but did not
+		 * detect client initiated wakeup.
+		 */
+		mddi_sharp_vsync_attempts++;
+}
+
+/* ISR to be executed */
+void mddi_sharp_vsync_set_handler(msm_fb_vsync_handler_type handler, void *arg)
+{
+	boolean error = FALSE;
+	unsigned long flags;
+
+	/* Disable interrupts */
+	spin_lock_irqsave(&mddi_host_spin_lock, flags);
+	/* INTLOCK(); */
+
+	if (mddi_sharp_vsync_handler != NULL)
+		error = TRUE;
+
+	/* Register the handler for this particular GROUP interrupt source */
+	mddi_sharp_vsync_handler = handler;
+	mddi_sharp_vsync_handler_arg = arg;
+
+	/* Restore interrupts */
+	spin_unlock_irqrestore(&mddi_host_spin_lock, flags);
+	/* INTFREE(); */
+
+	if (error)
+		MDDI_MSG_ERR("MDDI: Previous Vsync handler never called\n");
+
+	/* Enable the vsync wakeup */
+	mddi_queue_register_write(REG_INTR, 0x8100, FALSE, 0);
+
+	mddi_sharp_vsync_attempts = 1;
+	mddi_vsync_detect_enabled = TRUE;
+}				/* mddi_sharp_vsync_set_handler */
+
+static int mddi_sharp_lcd_on(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+
+	mfd = platform_get_drvdata(pdev);
+
+	if (!mfd)
+		return -ENODEV;
+
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	mddi_host_client_cnt_reset();
+
+	if (mfd->panel.id == SHARP_QVGA_PRIM)
+		mddi_sharp_prim_lcd_init();
+	else
+		mddi_sharp_sub_lcd_init();
+
+	return 0;
+}
+
+static int mddi_sharp_lcd_off(struct platform_device *pdev)
+{
+	if (mddi_sharp_vsync_handler != NULL) {
+		(*mddi_sharp_vsync_handler)
+			    (mddi_sharp_vsync_handler_arg);
+		mddi_sharp_vsync_handler = NULL;
+		printk(KERN_INFO "%s: clean up vsyn_handler=%x\n", __func__,
+				(int)mddi_sharp_vsync_handler);
+	}
+
+	mddi_sharp_lcd_powerdown();
+	return 0;
+}
+
+static int __devinit mddi_sharp_probe(struct platform_device *pdev)
+{
+	if (pdev->id == 0) {
+		mddi_sharp_pdata = pdev->dev.platform_data;
+		return 0;
+	}
+
+	msm_fb_add_device(pdev);
+
+	return 0;
+}
+
+static struct platform_driver this_driver = {
+	.probe  = mddi_sharp_probe,
+	.driver = {
+		.name   = "mddi_sharp_qvga",
+	},
+};
+
+static struct msm_fb_panel_data mddi_sharp_panel_data0 = {
+	.on = mddi_sharp_lcd_on,
+	.off = mddi_sharp_lcd_off,
+	.set_backlight = mddi_sharp_lcd_set_backlight,
+	.set_vsync_notifier = mddi_sharp_vsync_set_handler,
+};
+
+static struct platform_device this_device_0 = {
+	.name   = "mddi_sharp_qvga",
+	.id	= SHARP_QVGA_PRIM,
+	.dev	= {
+		.platform_data = &mddi_sharp_panel_data0,
+	}
+};
+
+static struct msm_fb_panel_data mddi_sharp_panel_data1 = {
+	.on = mddi_sharp_lcd_on,
+	.off = mddi_sharp_lcd_off,
+};
+
+static struct platform_device this_device_1 = {
+	.name   = "mddi_sharp_qvga",
+	.id	= SHARP_128X128_SECD,
+	.dev	= {
+		.platform_data = &mddi_sharp_panel_data1,
+	}
+};
+
+static int __init mddi_sharp_init(void)
+{
+	int ret;
+	struct msm_panel_info *pinfo;
+
+#ifdef CONFIG_FB_MSM_MDDI_AUTO_DETECT
+	u32 id;
+
+	ret = msm_fb_detect_client("mddi_sharp_qvga");
+	if (ret == -ENODEV)
+		return 0;
+
+	if (ret) {
+		id = mddi_get_client_id();
+
+		if (((id >> 16) != 0x0) || ((id & 0xffff) != 0x8835))
+			return 0;
+	}
+#endif
+	if (mddi_host_core_version > 8) {
+		/* can use faster refresh with newer hw revisions */
+		mddi_sharp_debug_60hz_refresh = TRUE;
+
+		/* Timing variables for tracking vsync */
+		/* dot_clock = 6.00MHz
+		 * horizontal count = 296
+		 * vertical count = 338
+		 * refresh rate = 6000000/(296+338) = 60Hz
+		 */
+		mddi_sharp_rows_per_second = 20270;	/* 6000000/296 */
+		mddi_sharp_rows_per_refresh = 338;
+		mddi_sharp_usecs_per_refresh = 16674;	/* (296+338)/6000000 */
+	} else {
+		/* Timing variables for tracking vsync */
+		/* dot_clock = 5.20MHz
+		 * horizontal count = 376
+		 * vertical count = 338
+		 * refresh rate = 5200000/(376+338) = 41Hz
+		 */
+		mddi_sharp_rows_per_second = 13830;	/* 5200000/376 */
+		mddi_sharp_rows_per_refresh = 338;
+		mddi_sharp_usecs_per_refresh = 24440;	/* (376+338)/5200000 */
+	}
+
+	ret = platform_driver_register(&this_driver);
+	if (!ret) {
+		pinfo = &mddi_sharp_panel_data0.panel_info;
+		pinfo->xres = 240;
+		pinfo->yres = 320;
+		MSM_FB_SINGLE_MODE_PANEL(pinfo);
+		pinfo->type = MDDI_PANEL;
+		pinfo->pdest = DISPLAY_1;
+		pinfo->mddi.vdopkt = MDDI_DEFAULT_PRIM_PIX_ATTR;
+		pinfo->wait_cycle = 0;
+		pinfo->bpp = 18;
+		pinfo->fb_num = 2;
+		pinfo->clk_rate = 122880000;
+		pinfo->clk_min = 120000000;
+		pinfo->clk_max = 125000000;
+		pinfo->lcd.vsync_enable = TRUE;
+		pinfo->mddi.is_type1 = TRUE;
+		pinfo->lcd.refx100 =
+			(mddi_sharp_rows_per_second * 100) /
+			mddi_sharp_rows_per_refresh;
+		pinfo->lcd.v_back_porch = 12;
+		pinfo->lcd.v_front_porch = 6;
+		pinfo->lcd.v_pulse_width = 0;
+		pinfo->lcd.hw_vsync_mode = FALSE;
+		pinfo->lcd.vsync_notifier_period = (1 * HZ);
+		pinfo->bl_max = 7;
+		pinfo->bl_min = 1;
+
+		ret = platform_device_register(&this_device_0);
+		if (ret)
+			platform_driver_unregister(&this_driver);
+
+		pinfo = &mddi_sharp_panel_data1.panel_info;
+		pinfo->xres = 128;
+		pinfo->yres = 128;
+		MSM_FB_SINGLE_MODE_PANEL(pinfo);
+		pinfo->type = MDDI_PANEL;
+		pinfo->pdest = DISPLAY_2;
+		pinfo->mddi.vdopkt = 0x400;
+		pinfo->wait_cycle = 0;
+		pinfo->bpp = 18;
+		pinfo->clk_rate = 122880000;
+		pinfo->clk_min = 120000000;
+		pinfo->clk_max = 125000000;
+		pinfo->fb_num = 2;
+
+		ret = platform_device_register(&this_device_1);
+		if (ret) {
+			platform_device_unregister(&this_device_0);
+			platform_driver_unregister(&this_driver);
+		}
+	}
+
+	if (!ret)
+		mddi_lcd.vsync_detected = mddi_sharp_lcd_vsync_detected;
+
+	return ret;
+}
+
+module_init(mddi_sharp_init);
diff --git a/drivers/video/msm/mddi_toshiba.c b/drivers/video/msm/mddi_toshiba.c
new file mode 100644
index 0000000..9727453
--- /dev/null
+++ b/drivers/video/msm/mddi_toshiba.c
@@ -0,0 +1,1753 @@
+/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_fb.h"
+#include "mddihost.h"
+#include "mddihosti.h"
+#include "mddi_toshiba.h"
+
+#define TM_GET_DID(id) ((id) & 0xff)
+#define TM_GET_PID(id) (((id) & 0xff00)>>8)
+
+#define MDDI_CLIENT_CORE_BASE  0x108000
+#define LCD_CONTROL_BLOCK_BASE 0x110000
+#define SPI_BLOCK_BASE         0x120000
+#define PWM_BLOCK_BASE         0x140000
+#define SYSTEM_BLOCK1_BASE     0x160000
+
+#define TTBUSSEL    (MDDI_CLIENT_CORE_BASE|0x18)
+#define DPSET0      (MDDI_CLIENT_CORE_BASE|0x1C)
+#define DPSET1      (MDDI_CLIENT_CORE_BASE|0x20)
+#define DPSUS       (MDDI_CLIENT_CORE_BASE|0x24)
+#define DPRUN       (MDDI_CLIENT_CORE_BASE|0x28)
+#define SYSCKENA    (MDDI_CLIENT_CORE_BASE|0x2C)
+
+#define BITMAP0     (MDDI_CLIENT_CORE_BASE|0x44)
+#define BITMAP1     (MDDI_CLIENT_CORE_BASE|0x48)
+#define BITMAP2     (MDDI_CLIENT_CORE_BASE|0x4C)
+#define BITMAP3     (MDDI_CLIENT_CORE_BASE|0x50)
+#define BITMAP4     (MDDI_CLIENT_CORE_BASE|0x54)
+
+#define SRST        (LCD_CONTROL_BLOCK_BASE|0x00)
+#define PORT_ENB    (LCD_CONTROL_BLOCK_BASE|0x04)
+#define START       (LCD_CONTROL_BLOCK_BASE|0x08)
+#define PORT        (LCD_CONTROL_BLOCK_BASE|0x0C)
+
+#define INTFLG      (LCD_CONTROL_BLOCK_BASE|0x18)
+#define INTMSK      (LCD_CONTROL_BLOCK_BASE|0x1C)
+#define MPLFBUF     (LCD_CONTROL_BLOCK_BASE|0x20)
+
+#define PXL         (LCD_CONTROL_BLOCK_BASE|0x30)
+#define HCYCLE      (LCD_CONTROL_BLOCK_BASE|0x34)
+#define HSW         (LCD_CONTROL_BLOCK_BASE|0x38)
+#define HDE_START   (LCD_CONTROL_BLOCK_BASE|0x3C)
+#define HDE_SIZE    (LCD_CONTROL_BLOCK_BASE|0x40)
+#define VCYCLE      (LCD_CONTROL_BLOCK_BASE|0x44)
+#define VSW         (LCD_CONTROL_BLOCK_BASE|0x48)
+#define VDE_START   (LCD_CONTROL_BLOCK_BASE|0x4C)
+#define VDE_SIZE    (LCD_CONTROL_BLOCK_BASE|0x50)
+#define WAKEUP      (LCD_CONTROL_BLOCK_BASE|0x54)
+#define REGENB      (LCD_CONTROL_BLOCK_BASE|0x5C)
+#define VSYNIF      (LCD_CONTROL_BLOCK_BASE|0x60)
+#define WRSTB       (LCD_CONTROL_BLOCK_BASE|0x64)
+#define RDSTB       (LCD_CONTROL_BLOCK_BASE|0x68)
+#define ASY_DATA    (LCD_CONTROL_BLOCK_BASE|0x6C)
+#define ASY_DATB    (LCD_CONTROL_BLOCK_BASE|0x70)
+#define ASY_DATC    (LCD_CONTROL_BLOCK_BASE|0x74)
+#define ASY_DATD    (LCD_CONTROL_BLOCK_BASE|0x78)
+#define ASY_DATE    (LCD_CONTROL_BLOCK_BASE|0x7C)
+#define ASY_DATF    (LCD_CONTROL_BLOCK_BASE|0x80)
+#define ASY_DATG    (LCD_CONTROL_BLOCK_BASE|0x84)
+#define ASY_DATH    (LCD_CONTROL_BLOCK_BASE|0x88)
+#define ASY_CMDSET  (LCD_CONTROL_BLOCK_BASE|0x8C)
+#define MONI        (LCD_CONTROL_BLOCK_BASE|0xB0)
+#define VPOS        (LCD_CONTROL_BLOCK_BASE|0xC0)
+
+#define SSICTL      (SPI_BLOCK_BASE|0x00)
+#define SSITIME     (SPI_BLOCK_BASE|0x04)
+#define SSITX       (SPI_BLOCK_BASE|0x08)
+#define SSIINTS     (SPI_BLOCK_BASE|0x14)
+
+#define TIMER0LOAD    (PWM_BLOCK_BASE|0x00)
+#define TIMER0CTRL    (PWM_BLOCK_BASE|0x08)
+#define PWM0OFF       (PWM_BLOCK_BASE|0x1C)
+#define TIMER1LOAD    (PWM_BLOCK_BASE|0x20)
+#define TIMER1CTRL    (PWM_BLOCK_BASE|0x28)
+#define PWM1OFF       (PWM_BLOCK_BASE|0x3C)
+#define TIMER2LOAD    (PWM_BLOCK_BASE|0x40)
+#define TIMER2CTRL    (PWM_BLOCK_BASE|0x48)
+#define PWM2OFF       (PWM_BLOCK_BASE|0x5C)
+#define PWMCR         (PWM_BLOCK_BASE|0x68)
+
+#define GPIOIS      (GPIO_BLOCK_BASE|0x08)
+#define GPIOIEV     (GPIO_BLOCK_BASE|0x10)
+#define GPIOIC      (GPIO_BLOCK_BASE|0x20)
+
+#define WKREQ       (SYSTEM_BLOCK1_BASE|0x00)
+#define CLKENB      (SYSTEM_BLOCK1_BASE|0x04)
+#define DRAMPWR     (SYSTEM_BLOCK1_BASE|0x08)
+#define INTMASK     (SYSTEM_BLOCK1_BASE|0x0C)
+#define CNT_DIS     (SYSTEM_BLOCK1_BASE|0x10)
+
+typedef enum {
+	TOSHIBA_STATE_OFF,
+	TOSHIBA_STATE_PRIM_SEC_STANDBY,
+	TOSHIBA_STATE_PRIM_SEC_READY,
+	TOSHIBA_STATE_PRIM_NORMAL_MODE,
+	TOSHIBA_STATE_SEC_NORMAL_MODE
+} mddi_toshiba_state_t;
+
+static uint32 mddi_toshiba_curr_vpos;
+static boolean mddi_toshiba_monitor_refresh_value = FALSE;
+static boolean mddi_toshiba_report_refresh_measurements = FALSE;
+
+boolean mddi_toshiba_61Hz_refresh = TRUE;
+
+/* Modifications to timing to increase refresh rate to > 60Hz.
+ *   20MHz dot clock.
+ *   646 total rows.
+ *   506 total columns.
+ *   refresh rate = 61.19Hz
+ */
+static uint32 mddi_toshiba_rows_per_second = 39526;
+static uint32 mddi_toshiba_usecs_per_refresh = 16344;
+static uint32 mddi_toshiba_rows_per_refresh = 646;
+extern boolean mddi_vsync_detect_enabled;
+
+static msm_fb_vsync_handler_type mddi_toshiba_vsync_handler;
+static void *mddi_toshiba_vsync_handler_arg;
+static uint16 mddi_toshiba_vsync_attempts;
+
+static mddi_toshiba_state_t toshiba_state = TOSHIBA_STATE_OFF;
+
+static struct msm_panel_common_pdata *mddi_toshiba_pdata;
+
+static int mddi_toshiba_lcd_on(struct platform_device *pdev);
+static int mddi_toshiba_lcd_off(struct platform_device *pdev);
+
+static void mddi_toshiba_state_transition(mddi_toshiba_state_t a,
+					  mddi_toshiba_state_t b)
+{
+	if (toshiba_state != a) {
+		MDDI_MSG_ERR("toshiba state trans. (%d->%d) found %d\n", a, b,
+			     toshiba_state);
+	}
+	toshiba_state = b;
+}
+
+#define GORDON_REG_IMGCTL1      0x10	/* Image interface control 1   */
+#define GORDON_REG_IMGCTL2      0x11	/* Image interface control 2   */
+#define GORDON_REG_IMGSET1      0x12	/* Image interface settings 1  */
+#define GORDON_REG_IMGSET2      0x13	/* Image interface settings 2  */
+#define GORDON_REG_IVBP1        0x14	/* DM0: Vert back porch        */
+#define GORDON_REG_IHBP1        0x15	/* DM0: Horiz back porch       */
+#define GORDON_REG_IVNUM1       0x16	/* DM0: Num of vert lines      */
+#define GORDON_REG_IHNUM1       0x17	/* DM0: Num of pixels per line */
+#define GORDON_REG_IVBP2        0x18	/* DM1: Vert back porch        */
+#define GORDON_REG_IHBP2        0x19	/* DM1: Horiz back porch       */
+#define GORDON_REG_IVNUM2       0x1A	/* DM1: Num of vert lines      */
+#define GORDON_REG_IHNUM2       0x1B	/* DM1: Num of pixels per line */
+#define GORDON_REG_LCDIFCTL1    0x30	/* LCD interface control 1     */
+#define GORDON_REG_VALTRAN      0x31	/* LCD IF ctl: VALTRAN sync flag */
+#define GORDON_REG_AVCTL        0x33
+#define GORDON_REG_LCDIFCTL2    0x34	/* LCD interface control 2     */
+#define GORDON_REG_LCDIFCTL3    0x35	/* LCD interface control 3     */
+#define GORDON_REG_LCDIFSET1    0x36	/* LCD interface settings 1    */
+#define GORDON_REG_PCCTL        0x3C
+#define GORDON_REG_TPARAM1      0x40
+#define GORDON_REG_TLCDIF1      0x41
+#define GORDON_REG_TSSPB_ST1    0x42
+#define GORDON_REG_TSSPB_ED1    0x43
+#define GORDON_REG_TSCK_ST1     0x44
+#define GORDON_REG_TSCK_WD1     0x45
+#define GORDON_REG_TGSPB_VST1   0x46
+#define GORDON_REG_TGSPB_VED1   0x47
+#define GORDON_REG_TGSPB_CH1    0x48
+#define GORDON_REG_TGCK_ST1     0x49
+#define GORDON_REG_TGCK_ED1     0x4A
+#define GORDON_REG_TPCTL_ST1    0x4B
+#define GORDON_REG_TPCTL_ED1    0x4C
+#define GORDON_REG_TPCHG_ED1    0x4D
+#define GORDON_REG_TCOM_CH1     0x4E
+#define GORDON_REG_THBP1        0x4F
+#define GORDON_REG_TPHCTL1      0x50
+#define GORDON_REG_EVPH1        0x51
+#define GORDON_REG_EVPL1        0x52
+#define GORDON_REG_EVNH1        0x53
+#define GORDON_REG_EVNL1        0x54
+#define GORDON_REG_TBIAS1       0x55
+#define GORDON_REG_TPARAM2      0x56
+#define GORDON_REG_TLCDIF2      0x57
+#define GORDON_REG_TSSPB_ST2    0x58
+#define GORDON_REG_TSSPB_ED2    0x59
+#define GORDON_REG_TSCK_ST2     0x5A
+#define GORDON_REG_TSCK_WD2     0x5B
+#define GORDON_REG_TGSPB_VST2   0x5C
+#define GORDON_REG_TGSPB_VED2   0x5D
+#define GORDON_REG_TGSPB_CH2    0x5E
+#define GORDON_REG_TGCK_ST2     0x5F
+#define GORDON_REG_TGCK_ED2     0x60
+#define GORDON_REG_TPCTL_ST2    0x61
+#define GORDON_REG_TPCTL_ED2    0x62
+#define GORDON_REG_TPCHG_ED2    0x63
+#define GORDON_REG_TCOM_CH2     0x64
+#define GORDON_REG_THBP2        0x65
+#define GORDON_REG_TPHCTL2      0x66
+#define GORDON_REG_EVPH2        0x67
+#define GORDON_REG_EVPL2        0x68
+#define GORDON_REG_EVNH2        0x69
+#define GORDON_REG_EVNL2        0x6A
+#define GORDON_REG_TBIAS2       0x6B
+#define GORDON_REG_POWCTL       0x80
+#define GORDON_REG_POWOSC1      0x81
+#define GORDON_REG_POWOSC2      0x82
+#define GORDON_REG_POWSET       0x83
+#define GORDON_REG_POWTRM1      0x85
+#define GORDON_REG_POWTRM2      0x86
+#define GORDON_REG_POWTRM3      0x87
+#define GORDON_REG_POWTRMSEL    0x88
+#define GORDON_REG_POWHIZ       0x89
+
+void serigo(uint16 reg, uint8 data)
+{
+	uint32 mddi_val = 0;
+	mddi_queue_register_read(SSIINTS, &mddi_val, TRUE, 0);
+	if (mddi_val & (1 << 8))
+		mddi_wait(1);
+	/* No De-assert of CS and send 2 bytes */
+	mddi_val = 0x90000 | ((0x00FF & reg) << 8) | data;
+	mddi_queue_register_write(SSITX, mddi_val, TRUE, 0);
+}
+
+void gordon_init(void)
+{
+       /* Image interface settings ***/
+	serigo(GORDON_REG_IMGCTL2, 0x00);
+	serigo(GORDON_REG_IMGSET1, 0x01);
+
+	/* Exchange the RGB signal for J510(Softbank mobile) */
+	serigo(GORDON_REG_IMGSET2, 0x12);
+	serigo(GORDON_REG_LCDIFSET1, 0x00);
+	mddi_wait(2);
+
+	/* Pre-charge settings */
+	serigo(GORDON_REG_PCCTL, 0x09);
+	serigo(GORDON_REG_LCDIFCTL2, 0x1B);
+	mddi_wait(1);
+}
+
+void gordon_disp_on(void)
+{
+	/*gordon_dispmode setting */
+	/*VGA settings */
+	serigo(GORDON_REG_TPARAM1, 0x30);
+	serigo(GORDON_REG_TLCDIF1, 0x00);
+	serigo(GORDON_REG_TSSPB_ST1, 0x8B);
+	serigo(GORDON_REG_TSSPB_ED1, 0x93);
+	mddi_wait(2);
+	serigo(GORDON_REG_TSCK_ST1, 0x88);
+	serigo(GORDON_REG_TSCK_WD1, 0x00);
+	serigo(GORDON_REG_TGSPB_VST1, 0x01);
+	serigo(GORDON_REG_TGSPB_VED1, 0x02);
+	mddi_wait(2);
+	serigo(GORDON_REG_TGSPB_CH1, 0x5E);
+	serigo(GORDON_REG_TGCK_ST1, 0x80);
+	serigo(GORDON_REG_TGCK_ED1, 0x3C);
+	serigo(GORDON_REG_TPCTL_ST1, 0x50);
+	mddi_wait(2);
+	serigo(GORDON_REG_TPCTL_ED1, 0x74);
+	serigo(GORDON_REG_TPCHG_ED1, 0x78);
+	serigo(GORDON_REG_TCOM_CH1, 0x50);
+	serigo(GORDON_REG_THBP1, 0x84);
+	mddi_wait(2);
+	serigo(GORDON_REG_TPHCTL1, 0x00);
+	serigo(GORDON_REG_EVPH1, 0x70);
+	serigo(GORDON_REG_EVPL1, 0x64);
+	serigo(GORDON_REG_EVNH1, 0x56);
+	mddi_wait(2);
+	serigo(GORDON_REG_EVNL1, 0x48);
+	serigo(GORDON_REG_TBIAS1, 0x88);
+	mddi_wait(2);
+	serigo(GORDON_REG_TPARAM2, 0x28);
+	serigo(GORDON_REG_TLCDIF2, 0x14);
+	serigo(GORDON_REG_TSSPB_ST2, 0x49);
+	serigo(GORDON_REG_TSSPB_ED2, 0x4B);
+	mddi_wait(2);
+	serigo(GORDON_REG_TSCK_ST2, 0x4A);
+	serigo(GORDON_REG_TSCK_WD2, 0x02);
+	serigo(GORDON_REG_TGSPB_VST2, 0x02);
+	serigo(GORDON_REG_TGSPB_VED2, 0x03);
+	mddi_wait(2);
+	serigo(GORDON_REG_TGSPB_CH2, 0x2F);
+	serigo(GORDON_REG_TGCK_ST2, 0x40);
+	serigo(GORDON_REG_TGCK_ED2, 0x1E);
+	serigo(GORDON_REG_TPCTL_ST2, 0x2C);
+	mddi_wait(2);
+	serigo(GORDON_REG_TPCTL_ED2, 0x3A);
+	serigo(GORDON_REG_TPCHG_ED2, 0x3C);
+	serigo(GORDON_REG_TCOM_CH2, 0x28);
+	serigo(GORDON_REG_THBP2, 0x4D);
+	mddi_wait(2);
+	serigo(GORDON_REG_TPHCTL2, 0x1A);
+	mddi_wait(2);
+	serigo(GORDON_REG_IVBP1, 0x02);
+	serigo(GORDON_REG_IHBP1, 0x90);
+	serigo(GORDON_REG_IVNUM1, 0xA0);
+	serigo(GORDON_REG_IHNUM1, 0x78);
+	mddi_wait(2);
+	serigo(GORDON_REG_IVBP2, 0x02);
+	serigo(GORDON_REG_IHBP2, 0x48);
+	serigo(GORDON_REG_IVNUM2, 0x50);
+	serigo(GORDON_REG_IHNUM2, 0x3C);
+	mddi_wait(2);
+	serigo(GORDON_REG_POWCTL, 0x03);
+	mddi_wait(15);
+	serigo(GORDON_REG_POWCTL, 0x07);
+	mddi_wait(15);
+	serigo(GORDON_REG_POWCTL, 0x0F);
+	mddi_wait(15);
+	serigo(GORDON_REG_AVCTL, 0x03);
+	mddi_wait(15);
+	serigo(GORDON_REG_POWCTL, 0x1F);
+	mddi_wait(15);
+	serigo(GORDON_REG_POWCTL, 0x5F);
+	mddi_wait(15);
+	serigo(GORDON_REG_POWCTL, 0x7F);
+	mddi_wait(15);
+	serigo(GORDON_REG_LCDIFCTL1, 0x02);
+	mddi_wait(15);
+	serigo(GORDON_REG_IMGCTL1, 0x00);
+	mddi_wait(15);
+	serigo(GORDON_REG_LCDIFCTL3, 0x00);
+	mddi_wait(15);
+	serigo(GORDON_REG_VALTRAN, 0x01);
+	mddi_wait(15);
+	serigo(GORDON_REG_LCDIFCTL1, 0x03);
+	serigo(GORDON_REG_LCDIFCTL1, 0x03);
+	mddi_wait(1);
+}
+
+void gordon_disp_off(void)
+{
+	serigo(GORDON_REG_LCDIFCTL2, 0x7B);
+	serigo(GORDON_REG_VALTRAN, 0x01);
+	serigo(GORDON_REG_LCDIFCTL1, 0x02);
+	serigo(GORDON_REG_LCDIFCTL3, 0x01);
+	mddi_wait(20);
+	serigo(GORDON_REG_VALTRAN, 0x01);
+	serigo(GORDON_REG_IMGCTL1, 0x01);
+	serigo(GORDON_REG_LCDIFCTL1, 0x00);
+	mddi_wait(20);
+	serigo(GORDON_REG_POWCTL, 0x1F);
+	mddi_wait(40);
+	serigo(GORDON_REG_POWCTL, 0x07);
+	mddi_wait(40);
+	serigo(GORDON_REG_POWCTL, 0x03);
+	mddi_wait(40);
+	serigo(GORDON_REG_POWCTL, 0x00);
+	mddi_wait(40);
+}
+
+void gordon_disp_init(void)
+{
+	gordon_init();
+	mddi_wait(20);
+	gordon_disp_on();
+}
+
+static void toshiba_common_initial_setup(struct msm_fb_data_type *mfd)
+{
+	if (TM_GET_PID(mfd->panel.id) == LCD_TOSHIBA_2P4_WVGA_PT) {
+		write_client_reg(DPSET0    , 0x4bec0066, TRUE);
+		write_client_reg(DPSET1    , 0x00000113, TRUE);
+		write_client_reg(DPSUS     , 0x00000000, TRUE);
+		write_client_reg(DPRUN     , 0x00000001, TRUE);
+		mddi_wait(5);
+		write_client_reg(SYSCKENA  , 0x00000001, TRUE);
+		write_client_reg(CLKENB    , 0x0000a0e9, TRUE);
+
+		write_client_reg(GPIODATA  , 0x03FF0000, TRUE);
+		write_client_reg(GPIODIR   , 0x0000024D, TRUE);
+		write_client_reg(GPIOSEL   , 0x00000173, TRUE);
+		write_client_reg(GPIOPC    , 0x03C300C0, TRUE);
+		write_client_reg(WKREQ     , 0x00000000, TRUE);
+		write_client_reg(GPIOIS    , 0x00000000, TRUE);
+		write_client_reg(GPIOIEV   , 0x00000001, TRUE);
+		write_client_reg(GPIOIC    , 0x000003FF, TRUE);
+		write_client_reg(GPIODATA  , 0x00040004, TRUE);
+
+		write_client_reg(GPIODATA  , 0x00080008, TRUE);
+		write_client_reg(DRAMPWR   , 0x00000001, TRUE);
+		write_client_reg(CLKENB    , 0x0000a0eb, TRUE);
+		write_client_reg(PWMCR     , 0x00000000, TRUE);
+		mddi_wait(1);
+
+		write_client_reg(SSICTL    , 0x00060399, TRUE);
+		write_client_reg(SSITIME   , 0x00000100, TRUE);
+		write_client_reg(CNT_DIS   , 0x00000002, TRUE);
+		write_client_reg(SSICTL    , 0x0006039b, TRUE);
+
+		write_client_reg(SSITX     , 0x00000000, TRUE);
+		mddi_wait(7);
+		write_client_reg(SSITX     , 0x00000000, TRUE);
+		mddi_wait(7);
+		write_client_reg(SSITX     , 0x00000000, TRUE);
+		mddi_wait(7);
+
+		write_client_reg(SSITX     , 0x000800BA, TRUE);
+		write_client_reg(SSITX     , 0x00000111, TRUE);
+		write_client_reg(SSITX     , 0x00080036, TRUE);
+		write_client_reg(SSITX     , 0x00000100, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX     , 0x0008003A, TRUE);
+		write_client_reg(SSITX     , 0x00000160, TRUE);
+		write_client_reg(SSITX     , 0x000800B1, TRUE);
+		write_client_reg(SSITX     , 0x0000015D, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX     , 0x000800B2, TRUE);
+		write_client_reg(SSITX     , 0x00000133, TRUE);
+		write_client_reg(SSITX     , 0x000800B3, TRUE);
+		write_client_reg(SSITX     , 0x00000122, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX     , 0x000800B4, TRUE);
+		write_client_reg(SSITX     , 0x00000102, TRUE);
+		write_client_reg(SSITX     , 0x000800B5, TRUE);
+		write_client_reg(SSITX     , 0x0000011E, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX     , 0x000800B6, TRUE);
+		write_client_reg(SSITX     , 0x00000127, TRUE);
+		write_client_reg(SSITX     , 0x000800B7, TRUE);
+		write_client_reg(SSITX     , 0x00000103, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX     , 0x000800B9, TRUE);
+		write_client_reg(SSITX     , 0x00000124, TRUE);
+		write_client_reg(SSITX     , 0x000800BD, TRUE);
+		write_client_reg(SSITX     , 0x000001A1, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX     , 0x000800BB, TRUE);
+		write_client_reg(SSITX     , 0x00000100, TRUE);
+		write_client_reg(SSITX     , 0x000800BF, TRUE);
+		write_client_reg(SSITX     , 0x00000101, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX     , 0x000800BE, TRUE);
+		write_client_reg(SSITX     , 0x00000100, TRUE);
+		write_client_reg(SSITX     , 0x000800C0, TRUE);
+		write_client_reg(SSITX     , 0x00000111, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX     , 0x000800C1, TRUE);
+		write_client_reg(SSITX     , 0x00000111, TRUE);
+		write_client_reg(SSITX     , 0x000800C2, TRUE);
+		write_client_reg(SSITX     , 0x00000111, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX     , 0x000800C3, TRUE);
+		write_client_reg(SSITX     , 0x00080132, TRUE);
+		write_client_reg(SSITX     , 0x00000132, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX     , 0x000800C4, TRUE);
+		write_client_reg(SSITX     , 0x00080132, TRUE);
+		write_client_reg(SSITX     , 0x00000132, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX     , 0x000800C5, TRUE);
+		write_client_reg(SSITX     , 0x00080132, TRUE);
+		write_client_reg(SSITX     , 0x00000132, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX     , 0x000800C6, TRUE);
+		write_client_reg(SSITX     , 0x00080132, TRUE);
+		write_client_reg(SSITX     , 0x00000132, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX     , 0x000800C7, TRUE);
+		write_client_reg(SSITX     , 0x00080164, TRUE);
+		write_client_reg(SSITX     , 0x00000145, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX     , 0x000800C8, TRUE);
+		write_client_reg(SSITX     , 0x00000144, TRUE);
+		write_client_reg(SSITX     , 0x000800C9, TRUE);
+		write_client_reg(SSITX     , 0x00000152, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX     , 0x000800CA, TRUE);
+		write_client_reg(SSITX     , 0x00000100, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX     , 0x000800EC, TRUE);
+		write_client_reg(SSITX     , 0x00080101, TRUE);
+		write_client_reg(SSITX     , 0x000001FC, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX     , 0x000800CF, TRUE);
+		write_client_reg(SSITX     , 0x00000101, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX     , 0x000800D0, TRUE);
+		write_client_reg(SSITX     , 0x00080110, TRUE);
+		write_client_reg(SSITX     , 0x00000104, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX     , 0x000800D1, TRUE);
+		write_client_reg(SSITX     , 0x00000101, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX     , 0x000800D2, TRUE);
+		write_client_reg(SSITX     , 0x00080100, TRUE);
+		write_client_reg(SSITX     , 0x00000128, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX     , 0x000800D3, TRUE);
+		write_client_reg(SSITX     , 0x00080100, TRUE);
+		write_client_reg(SSITX     , 0x00000128, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX     , 0x000800D4, TRUE);
+		write_client_reg(SSITX     , 0x00080126, TRUE);
+		write_client_reg(SSITX     , 0x000001A4, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX     , 0x000800D5, TRUE);
+		write_client_reg(SSITX     , 0x00000120, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX     , 0x000800EF, TRUE);
+		write_client_reg(SSITX     , 0x00080132, TRUE);
+		write_client_reg(SSITX     , 0x00000100, TRUE);
+		mddi_wait(1);
+
+		write_client_reg(BITMAP0   , 0x032001E0, TRUE);
+		write_client_reg(BITMAP1   , 0x032001E0, TRUE);
+		write_client_reg(BITMAP2   , 0x014000F0, TRUE);
+		write_client_reg(BITMAP3   , 0x014000F0, TRUE);
+		write_client_reg(BITMAP4   , 0x014000F0, TRUE);
+		write_client_reg(CLKENB    , 0x0000A1EB, TRUE);
+		write_client_reg(PORT_ENB  , 0x00000001, TRUE);
+		write_client_reg(PORT      , 0x00000004, TRUE);
+		write_client_reg(PXL       , 0x00000002, TRUE);
+		write_client_reg(MPLFBUF   , 0x00000000, TRUE);
+		write_client_reg(HCYCLE    , 0x000000FD, TRUE);
+		write_client_reg(HSW       , 0x00000003, TRUE);
+		write_client_reg(HDE_START , 0x00000007, TRUE);
+		write_client_reg(HDE_SIZE  , 0x000000EF, TRUE);
+		write_client_reg(VCYCLE    , 0x00000325, TRUE);
+		write_client_reg(VSW       , 0x00000001, TRUE);
+		write_client_reg(VDE_START , 0x00000003, TRUE);
+		write_client_reg(VDE_SIZE  , 0x0000031F, TRUE);
+		write_client_reg(START     , 0x00000001, TRUE);
+		mddi_wait(32);
+		write_client_reg(SSITX     , 0x000800BC, TRUE);
+		write_client_reg(SSITX     , 0x00000180, TRUE);
+		write_client_reg(SSITX     , 0x0008003B, TRUE);
+		write_client_reg(SSITX     , 0x00000100, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX     , 0x000800B0, TRUE);
+		write_client_reg(SSITX     , 0x00000116, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX     , 0x000800B8, TRUE);
+		write_client_reg(SSITX     , 0x000801FF, TRUE);
+		write_client_reg(SSITX     , 0x000001F5, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX     , 0x00000011, TRUE);
+		mddi_wait(5);
+		write_client_reg(SSITX     , 0x00000029, TRUE);
+		return;
+	}
+
+	if (TM_GET_PID(mfd->panel.id) == LCD_SHARP_2P4_VGA) {
+		write_client_reg(DPSET0, 0x4BEC0066, TRUE);
+		write_client_reg(DPSET1, 0x00000113, TRUE);
+		write_client_reg(DPSUS, 0x00000000, TRUE);
+		write_client_reg(DPRUN, 0x00000001, TRUE);
+		mddi_wait(14);
+		write_client_reg(SYSCKENA, 0x00000001, TRUE);
+		write_client_reg(CLKENB, 0x000000EF, TRUE);
+		write_client_reg(GPIO_BLOCK_BASE, 0x03FF0000, TRUE);
+		write_client_reg(GPIODIR, 0x0000024D, TRUE);
+		write_client_reg(SYSTEM_BLOCK2_BASE, 0x00000173, TRUE);
+		write_client_reg(GPIOPC, 0x03C300C0, TRUE);
+		write_client_reg(SYSTEM_BLOCK1_BASE, 0x00000000, TRUE);
+		write_client_reg(GPIOIS, 0x00000000, TRUE);
+		write_client_reg(GPIOIEV, 0x00000001, TRUE);
+		write_client_reg(GPIOIC, 0x000003FF, TRUE);
+		write_client_reg(GPIO_BLOCK_BASE, 0x00060006, TRUE);
+		write_client_reg(GPIO_BLOCK_BASE, 0x00080008, TRUE);
+		write_client_reg(GPIO_BLOCK_BASE, 0x02000200, TRUE);
+		write_client_reg(DRAMPWR, 0x00000001, TRUE);
+		write_client_reg(TIMER0CTRL, 0x00000060, TRUE);
+		write_client_reg(PWM_BLOCK_BASE, 0x00001388, TRUE);
+		write_client_reg(PWM0OFF, 0x00001387, TRUE);
+		write_client_reg(TIMER1CTRL, 0x00000060, TRUE);
+		write_client_reg(TIMER1LOAD, 0x00001388, TRUE);
+		write_client_reg(PWM1OFF, 0x00001387, TRUE);
+		write_client_reg(TIMER0CTRL, 0x000000E0, TRUE);
+		write_client_reg(TIMER1CTRL, 0x000000E0, TRUE);
+		write_client_reg(PWMCR, 0x00000003, TRUE);
+		mddi_wait(1);
+		write_client_reg(SPI_BLOCK_BASE, 0x00063111, TRUE);
+		write_client_reg(SSITIME, 0x00000100, TRUE);
+		write_client_reg(SPI_BLOCK_BASE, 0x00063113, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX, 0x00000000, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX, 0x00000000, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX, 0x00000000, TRUE);
+		mddi_wait(1);
+		write_client_reg(CLKENB, 0x0000A1EF, TRUE);
+		write_client_reg(START, 0x00000000, TRUE);
+		write_client_reg(WRSTB, 0x0000003F, TRUE);
+		write_client_reg(RDSTB, 0x00000432, TRUE);
+		write_client_reg(PORT_ENB, 0x00000002, TRUE);
+		write_client_reg(VSYNIF, 0x00000000, TRUE);
+		write_client_reg(ASY_DATA, 0x80000000, TRUE);
+		write_client_reg(ASY_DATB, 0x00000001, TRUE);
+		write_client_reg(ASY_CMDSET, 0x00000005, TRUE);
+		write_client_reg(ASY_CMDSET, 0x00000004, TRUE);
+		mddi_wait(10);
+		write_client_reg(ASY_DATA, 0x80000000, TRUE);
+		write_client_reg(ASY_DATB, 0x80000000, TRUE);
+		write_client_reg(ASY_DATC, 0x80000000, TRUE);
+		write_client_reg(ASY_DATD, 0x80000000, TRUE);
+		write_client_reg(ASY_CMDSET, 0x00000009, TRUE);
+		write_client_reg(ASY_CMDSET, 0x00000008, TRUE);
+		write_client_reg(ASY_DATA, 0x80000007, TRUE);
+		write_client_reg(ASY_DATB, 0x00004005, TRUE);
+		write_client_reg(ASY_CMDSET, 0x00000005, TRUE);
+		write_client_reg(ASY_CMDSET, 0x00000004, TRUE);
+		mddi_wait(20);
+		write_client_reg(ASY_DATA, 0x80000059, TRUE);
+		write_client_reg(ASY_DATB, 0x00000000, TRUE);
+		write_client_reg(ASY_CMDSET, 0x00000005, TRUE);
+		write_client_reg(ASY_CMDSET, 0x00000004, TRUE);
+
+		write_client_reg(VSYNIF, 0x00000001, TRUE);
+		write_client_reg(PORT_ENB, 0x00000001, TRUE);
+	} else {
+		write_client_reg(DPSET0, 0x4BEC0066, TRUE);
+		write_client_reg(DPSET1, 0x00000113, TRUE);
+		write_client_reg(DPSUS, 0x00000000, TRUE);
+		write_client_reg(DPRUN, 0x00000001, TRUE);
+		mddi_wait(14);
+		write_client_reg(SYSCKENA, 0x00000001, TRUE);
+		write_client_reg(CLKENB, 0x000000EF, TRUE);
+		write_client_reg(GPIODATA, 0x03FF0000, TRUE);
+		write_client_reg(GPIODIR, 0x0000024D, TRUE);
+		write_client_reg(GPIOSEL, 0x00000173, TRUE);
+		write_client_reg(GPIOPC, 0x03C300C0, TRUE);
+		write_client_reg(WKREQ, 0x00000000, TRUE);
+		write_client_reg(GPIOIS, 0x00000000, TRUE);
+		write_client_reg(GPIOIEV, 0x00000001, TRUE);
+		write_client_reg(GPIOIC, 0x000003FF, TRUE);
+		write_client_reg(GPIODATA, 0x00060006, TRUE);
+		write_client_reg(GPIODATA, 0x00080008, TRUE);
+		write_client_reg(GPIODATA, 0x02000200, TRUE);
+
+		if (TM_GET_PID(mfd->panel.id) == LCD_TOSHIBA_2P4_WVGA) {
+			mddi_wait(400);
+			write_client_reg(DRAMPWR, 0x00000001, TRUE);
+
+			write_client_reg(CNT_DIS, 0x00000002, TRUE);
+			write_client_reg(BITMAP0, 0x01E00320, TRUE);
+			write_client_reg(PORT_ENB, 0x00000001, TRUE);
+			write_client_reg(PORT, 0x00000004, TRUE);
+			write_client_reg(PXL, 0x0000003A, TRUE);
+			write_client_reg(MPLFBUF, 0x00000000, TRUE);
+			write_client_reg(HCYCLE, 0x00000253, TRUE);
+			write_client_reg(HSW, 0x00000003, TRUE);
+			write_client_reg(HDE_START, 0x00000017, TRUE);
+			write_client_reg(HDE_SIZE, 0x0000018F, TRUE);
+			write_client_reg(VCYCLE, 0x000001FF, TRUE);
+			write_client_reg(VSW, 0x00000001, TRUE);
+			write_client_reg(VDE_START, 0x00000003, TRUE);
+			write_client_reg(VDE_SIZE, 0x000001DF, TRUE);
+			write_client_reg(START, 0x00000001, TRUE);
+			mddi_wait(1);
+			write_client_reg(TIMER0CTRL, 0x00000060, TRUE);
+			write_client_reg(TIMER0LOAD, 0x00001388, TRUE);
+			write_client_reg(TIMER1CTRL, 0x00000060, TRUE);
+			write_client_reg(TIMER1LOAD, 0x00001388, TRUE);
+			write_client_reg(PWM1OFF, 0x00000087, TRUE);
+		} else {
+			write_client_reg(DRAMPWR, 0x00000001, TRUE);
+			write_client_reg(TIMER0CTRL, 0x00000060, TRUE);
+			write_client_reg(TIMER0LOAD, 0x00001388, TRUE);
+			write_client_reg(TIMER1CTRL, 0x00000060, TRUE);
+			write_client_reg(TIMER1LOAD, 0x00001388, TRUE);
+			write_client_reg(PWM1OFF, 0x00001387, TRUE);
+		}
+
+		write_client_reg(TIMER0CTRL, 0x000000E0, TRUE);
+		write_client_reg(TIMER1CTRL, 0x000000E0, TRUE);
+		write_client_reg(PWMCR, 0x00000003, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSICTL, 0x00000799, TRUE);
+		write_client_reg(SSITIME, 0x00000100, TRUE);
+		write_client_reg(SSICTL, 0x0000079b, TRUE);
+		write_client_reg(SSITX, 0x00000000, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX, 0x00000000, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX, 0x00000000, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX, 0x000800BA, TRUE);
+		write_client_reg(SSITX, 0x00000111, TRUE);
+		write_client_reg(SSITX, 0x00080036, TRUE);
+		write_client_reg(SSITX, 0x00000100, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800BB, TRUE);
+		write_client_reg(SSITX, 0x00000100, TRUE);
+		write_client_reg(SSITX, 0x0008003A, TRUE);
+		write_client_reg(SSITX, 0x00000160, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800BF, TRUE);
+		write_client_reg(SSITX, 0x00000100, TRUE);
+		write_client_reg(SSITX, 0x000800B1, TRUE);
+		write_client_reg(SSITX, 0x0000015D, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800B2, TRUE);
+		write_client_reg(SSITX, 0x00000133, TRUE);
+		write_client_reg(SSITX, 0x000800B3, TRUE);
+		write_client_reg(SSITX, 0x00000122, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800B4, TRUE);
+		write_client_reg(SSITX, 0x00000102, TRUE);
+		write_client_reg(SSITX, 0x000800B5, TRUE);
+		write_client_reg(SSITX, 0x0000011F, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800B6, TRUE);
+		write_client_reg(SSITX, 0x00000128, TRUE);
+		write_client_reg(SSITX, 0x000800B7, TRUE);
+		write_client_reg(SSITX, 0x00000103, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800B9, TRUE);
+		write_client_reg(SSITX, 0x00000120, TRUE);
+		write_client_reg(SSITX, 0x000800BD, TRUE);
+		write_client_reg(SSITX, 0x00000102, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800BE, TRUE);
+		write_client_reg(SSITX, 0x00000100, TRUE);
+		write_client_reg(SSITX, 0x000800C0, TRUE);
+		write_client_reg(SSITX, 0x00000111, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800C1, TRUE);
+		write_client_reg(SSITX, 0x00000111, TRUE);
+		write_client_reg(SSITX, 0x000800C2, TRUE);
+		write_client_reg(SSITX, 0x00000111, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800C3, TRUE);
+		write_client_reg(SSITX, 0x0008010A, TRUE);
+		write_client_reg(SSITX, 0x0000010A, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800C4, TRUE);
+		write_client_reg(SSITX, 0x00080160, TRUE);
+		write_client_reg(SSITX, 0x00000160, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800C5, TRUE);
+		write_client_reg(SSITX, 0x00080160, TRUE);
+		write_client_reg(SSITX, 0x00000160, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800C6, TRUE);
+		write_client_reg(SSITX, 0x00080160, TRUE);
+		write_client_reg(SSITX, 0x00000160, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800C7, TRUE);
+		write_client_reg(SSITX, 0x00080133, TRUE);
+		write_client_reg(SSITX, 0x00000143, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800C8, TRUE);
+		write_client_reg(SSITX, 0x00000144, TRUE);
+		write_client_reg(SSITX, 0x000800C9, TRUE);
+		write_client_reg(SSITX, 0x00000133, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800CA, TRUE);
+		write_client_reg(SSITX, 0x00000100, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800EC, TRUE);
+		write_client_reg(SSITX, 0x00080102, TRUE);
+		write_client_reg(SSITX, 0x00000118, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800CF, TRUE);
+		write_client_reg(SSITX, 0x00000101, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800D0, TRUE);
+		write_client_reg(SSITX, 0x00080110, TRUE);
+		write_client_reg(SSITX, 0x00000104, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800D1, TRUE);
+		write_client_reg(SSITX, 0x00000101, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800D2, TRUE);
+		write_client_reg(SSITX, 0x00080100, TRUE);
+		write_client_reg(SSITX, 0x0000013A, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800D3, TRUE);
+		write_client_reg(SSITX, 0x00080100, TRUE);
+		write_client_reg(SSITX, 0x0000013A, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800D4, TRUE);
+		write_client_reg(SSITX, 0x00080124, TRUE);
+		write_client_reg(SSITX, 0x0000016E, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX, 0x000800D5, TRUE);
+		write_client_reg(SSITX, 0x00000124, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800ED, TRUE);
+		write_client_reg(SSITX, 0x00080101, TRUE);
+		write_client_reg(SSITX, 0x0000010A, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800D6, TRUE);
+		write_client_reg(SSITX, 0x00000101, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800D7, TRUE);
+		write_client_reg(SSITX, 0x00080110, TRUE);
+		write_client_reg(SSITX, 0x0000010A, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800D8, TRUE);
+		write_client_reg(SSITX, 0x00000101, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800D9, TRUE);
+		write_client_reg(SSITX, 0x00080100, TRUE);
+		write_client_reg(SSITX, 0x00000114, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800DE, TRUE);
+		write_client_reg(SSITX, 0x00080100, TRUE);
+		write_client_reg(SSITX, 0x00000114, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800DF, TRUE);
+		write_client_reg(SSITX, 0x00080112, TRUE);
+		write_client_reg(SSITX, 0x0000013F, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800E0, TRUE);
+		write_client_reg(SSITX, 0x0000010B, TRUE);
+		write_client_reg(SSITX, 0x000800E2, TRUE);
+		write_client_reg(SSITX, 0x00000101, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800E3, TRUE);
+		write_client_reg(SSITX, 0x00000136, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800E4, TRUE);
+		write_client_reg(SSITX, 0x00080100, TRUE);
+		write_client_reg(SSITX, 0x00000103, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800E5, TRUE);
+		write_client_reg(SSITX, 0x00080102, TRUE);
+		write_client_reg(SSITX, 0x00000104, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800E6, TRUE);
+		write_client_reg(SSITX, 0x00000103, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800E7, TRUE);
+		write_client_reg(SSITX, 0x00080104, TRUE);
+		write_client_reg(SSITX, 0x0000010A, TRUE);
+		mddi_wait(2);
+		write_client_reg(SSITX, 0x000800E8, TRUE);
+		write_client_reg(SSITX, 0x00000104, TRUE);
+		write_client_reg(CLKENB, 0x000001EF, TRUE);
+		write_client_reg(START, 0x00000000, TRUE);
+		write_client_reg(WRSTB, 0x0000003F, TRUE);
+		write_client_reg(RDSTB, 0x00000432, TRUE);
+		write_client_reg(PORT_ENB, 0x00000002, TRUE);
+		write_client_reg(VSYNIF, 0x00000000, TRUE);
+		write_client_reg(ASY_DATA, 0x80000000, TRUE);
+		write_client_reg(ASY_DATB, 0x00000001, TRUE);
+		write_client_reg(ASY_CMDSET, 0x00000005, TRUE);
+		write_client_reg(ASY_CMDSET, 0x00000004, TRUE);
+		mddi_wait(10);
+		write_client_reg(ASY_DATA, 0x80000000, TRUE);
+		write_client_reg(ASY_DATB, 0x80000000, TRUE);
+		write_client_reg(ASY_DATC, 0x80000000, TRUE);
+		write_client_reg(ASY_DATD, 0x80000000, TRUE);
+		write_client_reg(ASY_CMDSET, 0x00000009, TRUE);
+		write_client_reg(ASY_CMDSET, 0x00000008, TRUE);
+		write_client_reg(ASY_DATA, 0x80000007, TRUE);
+		write_client_reg(ASY_DATB, 0x00004005, TRUE);
+		write_client_reg(ASY_CMDSET, 0x00000005, TRUE);
+		write_client_reg(ASY_CMDSET, 0x00000004, TRUE);
+		mddi_wait(20);
+		write_client_reg(ASY_DATA, 0x80000059, TRUE);
+		write_client_reg(ASY_DATB, 0x00000000, TRUE);
+		write_client_reg(ASY_CMDSET, 0x00000005, TRUE);
+		write_client_reg(ASY_CMDSET, 0x00000004, TRUE);
+		write_client_reg(VSYNIF, 0x00000001, TRUE);
+		write_client_reg(PORT_ENB, 0x00000001, TRUE);
+	}
+
+	mddi_toshiba_state_transition(TOSHIBA_STATE_PRIM_SEC_STANDBY,
+				      TOSHIBA_STATE_PRIM_SEC_READY);
+}
+
+static void toshiba_prim_start(struct msm_fb_data_type *mfd)
+{
+	if (TM_GET_PID(mfd->panel.id) == LCD_TOSHIBA_2P4_WVGA_PT)
+		return;
+
+	if (TM_GET_PID(mfd->panel.id) == LCD_SHARP_2P4_VGA) {
+		write_client_reg(BITMAP1, 0x01E000F0, TRUE);
+		write_client_reg(BITMAP2, 0x01E000F0, TRUE);
+		write_client_reg(BITMAP3, 0x01E000F0, TRUE);
+		write_client_reg(BITMAP4, 0x00DC00B0, TRUE);
+		write_client_reg(CLKENB, 0x000001EF, TRUE);
+		write_client_reg(PORT_ENB, 0x00000001, TRUE);
+		write_client_reg(PORT, 0x00000016, TRUE);
+		write_client_reg(PXL, 0x00000002, TRUE);
+		write_client_reg(MPLFBUF, 0x00000000, TRUE);
+		write_client_reg(HCYCLE, 0x00000185, TRUE);
+		write_client_reg(HSW, 0x00000018, TRUE);
+		write_client_reg(HDE_START, 0x0000004A, TRUE);
+		write_client_reg(HDE_SIZE, 0x000000EF, TRUE);
+		write_client_reg(VCYCLE, 0x0000028E, TRUE);
+		write_client_reg(VSW, 0x00000004, TRUE);
+		write_client_reg(VDE_START, 0x00000009, TRUE);
+		write_client_reg(VDE_SIZE, 0x0000027F, TRUE);
+		write_client_reg(START, 0x00000001, TRUE);
+		write_client_reg(SYSTEM_BLOCK1_BASE, 0x00000002, TRUE);
+	} else{
+
+		write_client_reg(VSYNIF, 0x00000001, TRUE);
+		write_client_reg(PORT_ENB, 0x00000001, TRUE);
+		write_client_reg(BITMAP1, 0x01E000F0, TRUE);
+		write_client_reg(BITMAP2, 0x01E000F0, TRUE);
+		write_client_reg(BITMAP3, 0x01E000F0, TRUE);
+		write_client_reg(BITMAP4, 0x00DC00B0, TRUE);
+		write_client_reg(CLKENB, 0x000001EF, TRUE);
+		write_client_reg(PORT_ENB, 0x00000001, TRUE);
+		write_client_reg(PORT, 0x00000004, TRUE);
+		write_client_reg(PXL, 0x00000002, TRUE);
+		write_client_reg(MPLFBUF, 0x00000000, TRUE);
+
+		if (mddi_toshiba_61Hz_refresh) {
+			write_client_reg(HCYCLE, 0x000000FC, TRUE);
+			mddi_toshiba_rows_per_second = 39526;
+			mddi_toshiba_rows_per_refresh = 646;
+			mddi_toshiba_usecs_per_refresh = 16344;
+		} else {
+			write_client_reg(HCYCLE, 0x0000010b, TRUE);
+			mddi_toshiba_rows_per_second = 37313;
+			mddi_toshiba_rows_per_refresh = 646;
+			mddi_toshiba_usecs_per_refresh = 17313;
+		}
+
+		write_client_reg(HSW, 0x00000003, TRUE);
+		write_client_reg(HDE_START, 0x00000007, TRUE);
+		write_client_reg(HDE_SIZE, 0x000000EF, TRUE);
+		write_client_reg(VCYCLE, 0x00000285, TRUE);
+		write_client_reg(VSW, 0x00000001, TRUE);
+		write_client_reg(VDE_START, 0x00000003, TRUE);
+		write_client_reg(VDE_SIZE, 0x0000027F, TRUE);
+		write_client_reg(START, 0x00000001, TRUE);
+		mddi_wait(10);
+		write_client_reg(SSITX, 0x000800BC, TRUE);
+		write_client_reg(SSITX, 0x00000180, TRUE);
+		write_client_reg(SSITX, 0x0008003B, TRUE);
+		write_client_reg(SSITX, 0x00000100, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX, 0x000800B0, TRUE);
+		write_client_reg(SSITX, 0x00000116, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX, 0x000800B8, TRUE);
+		write_client_reg(SSITX, 0x000801FF, TRUE);
+		write_client_reg(SSITX, 0x000001F5, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX, 0x00000011, TRUE);
+		write_client_reg(SSITX, 0x00000029, TRUE);
+		write_client_reg(WKREQ, 0x00000000, TRUE);
+		write_client_reg(WAKEUP, 0x00000000, TRUE);
+		write_client_reg(INTMSK, 0x00000001, TRUE);
+	}
+
+	mddi_toshiba_state_transition(TOSHIBA_STATE_PRIM_SEC_READY,
+				      TOSHIBA_STATE_PRIM_NORMAL_MODE);
+}
+
+static void toshiba_sec_start(struct msm_fb_data_type *mfd)
+{
+	if (TM_GET_PID(mfd->panel.id) == LCD_TOSHIBA_2P4_WVGA_PT)
+		return;
+
+	write_client_reg(VSYNIF, 0x00000000, TRUE);
+	write_client_reg(PORT_ENB, 0x00000002, TRUE);
+	write_client_reg(CLKENB, 0x000011EF, TRUE);
+	write_client_reg(BITMAP0, 0x028001E0, TRUE);
+	write_client_reg(BITMAP1, 0x00000000, TRUE);
+	write_client_reg(BITMAP2, 0x00000000, TRUE);
+	write_client_reg(BITMAP3, 0x00000000, TRUE);
+	write_client_reg(BITMAP4, 0x00DC00B0, TRUE);
+	write_client_reg(PORT, 0x00000000, TRUE);
+	write_client_reg(PXL, 0x00000000, TRUE);
+	write_client_reg(MPLFBUF, 0x00000004, TRUE);
+	write_client_reg(HCYCLE, 0x0000006B, TRUE);
+	write_client_reg(HSW, 0x00000003, TRUE);
+	write_client_reg(HDE_START, 0x00000007, TRUE);
+	write_client_reg(HDE_SIZE, 0x00000057, TRUE);
+	write_client_reg(VCYCLE, 0x000000E6, TRUE);
+	write_client_reg(VSW, 0x00000001, TRUE);
+	write_client_reg(VDE_START, 0x00000003, TRUE);
+	write_client_reg(VDE_SIZE, 0x000000DB, TRUE);
+	write_client_reg(ASY_DATA, 0x80000001, TRUE);
+	write_client_reg(ASY_DATB, 0x0000011B, TRUE);
+	write_client_reg(ASY_DATC, 0x80000002, TRUE);
+	write_client_reg(ASY_DATD, 0x00000700, TRUE);
+	write_client_reg(ASY_DATE, 0x80000003, TRUE);
+	write_client_reg(ASY_DATF, 0x00000230, TRUE);
+	write_client_reg(ASY_DATG, 0x80000008, TRUE);
+	write_client_reg(ASY_DATH, 0x00000402, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000001, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000000, TRUE);
+	write_client_reg(ASY_DATA, 0x80000009, TRUE);
+	write_client_reg(ASY_DATB, 0x00000000, TRUE);
+	write_client_reg(ASY_DATC, 0x8000000B, TRUE);
+	write_client_reg(ASY_DATD, 0x00000000, TRUE);
+	write_client_reg(ASY_DATE, 0x8000000C, TRUE);
+	write_client_reg(ASY_DATF, 0x00000000, TRUE);
+	write_client_reg(ASY_DATG, 0x8000000D, TRUE);
+	write_client_reg(ASY_DATH, 0x00000409, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000001, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000000, TRUE);
+	write_client_reg(ASY_DATA, 0x8000000E, TRUE);
+	write_client_reg(ASY_DATB, 0x00000409, TRUE);
+	write_client_reg(ASY_DATC, 0x80000030, TRUE);
+	write_client_reg(ASY_DATD, 0x00000000, TRUE);
+	write_client_reg(ASY_DATE, 0x80000031, TRUE);
+	write_client_reg(ASY_DATF, 0x00000100, TRUE);
+	write_client_reg(ASY_DATG, 0x80000032, TRUE);
+	write_client_reg(ASY_DATH, 0x00000104, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000001, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000000, TRUE);
+	write_client_reg(ASY_DATA, 0x80000033, TRUE);
+	write_client_reg(ASY_DATB, 0x00000400, TRUE);
+	write_client_reg(ASY_DATC, 0x80000034, TRUE);
+	write_client_reg(ASY_DATD, 0x00000306, TRUE);
+	write_client_reg(ASY_DATE, 0x80000035, TRUE);
+	write_client_reg(ASY_DATF, 0x00000706, TRUE);
+	write_client_reg(ASY_DATG, 0x80000036, TRUE);
+	write_client_reg(ASY_DATH, 0x00000707, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000001, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000000, TRUE);
+	write_client_reg(ASY_DATA, 0x80000037, TRUE);
+	write_client_reg(ASY_DATB, 0x00000004, TRUE);
+	write_client_reg(ASY_DATC, 0x80000038, TRUE);
+	write_client_reg(ASY_DATD, 0x00000000, TRUE);
+	write_client_reg(ASY_DATE, 0x80000039, TRUE);
+	write_client_reg(ASY_DATF, 0x00000000, TRUE);
+	write_client_reg(ASY_DATG, 0x8000003A, TRUE);
+	write_client_reg(ASY_DATH, 0x00000001, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000001, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000000, TRUE);
+	write_client_reg(ASY_DATA, 0x80000044, TRUE);
+	write_client_reg(ASY_DATB, 0x0000AF00, TRUE);
+	write_client_reg(ASY_DATC, 0x80000045, TRUE);
+	write_client_reg(ASY_DATD, 0x0000DB00, TRUE);
+	write_client_reg(ASY_DATE, 0x08000042, TRUE);
+	write_client_reg(ASY_DATF, 0x0000DB00, TRUE);
+	write_client_reg(ASY_DATG, 0x80000021, TRUE);
+	write_client_reg(ASY_DATH, 0x00000000, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000001, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000000, TRUE);
+	write_client_reg(PXL, 0x0000000C, TRUE);
+	write_client_reg(VSYNIF, 0x00000001, TRUE);
+	write_client_reg(ASY_DATA, 0x80000022, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000003, TRUE);
+	write_client_reg(START, 0x00000001, TRUE);
+	mddi_wait(60);
+	write_client_reg(PXL, 0x00000000, TRUE);
+	write_client_reg(VSYNIF, 0x00000000, TRUE);
+	write_client_reg(START, 0x00000000, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000000, TRUE);
+	write_client_reg(ASY_DATA, 0x80000050, TRUE);
+	write_client_reg(ASY_DATB, 0x00000000, TRUE);
+	write_client_reg(ASY_DATC, 0x80000051, TRUE);
+	write_client_reg(ASY_DATD, 0x00000E00, TRUE);
+	write_client_reg(ASY_DATE, 0x80000052, TRUE);
+	write_client_reg(ASY_DATF, 0x00000D01, TRUE);
+	write_client_reg(ASY_DATG, 0x80000053, TRUE);
+	write_client_reg(ASY_DATH, 0x00000000, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000001, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000000, TRUE);
+	write_client_reg(ASY_DATA, 0x80000058, TRUE);
+	write_client_reg(ASY_DATB, 0x00000000, TRUE);
+	write_client_reg(ASY_DATC, 0x8000005A, TRUE);
+	write_client_reg(ASY_DATD, 0x00000E01, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000009, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000008, TRUE);
+	write_client_reg(ASY_DATA, 0x80000011, TRUE);
+	write_client_reg(ASY_DATB, 0x00000812, TRUE);
+	write_client_reg(ASY_DATC, 0x80000012, TRUE);
+	write_client_reg(ASY_DATD, 0x00000003, TRUE);
+	write_client_reg(ASY_DATE, 0x80000013, TRUE);
+	write_client_reg(ASY_DATF, 0x00000909, TRUE);
+	write_client_reg(ASY_DATG, 0x80000010, TRUE);
+	write_client_reg(ASY_DATH, 0x00000040, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000001, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000000, TRUE);
+	mddi_wait(40);
+	write_client_reg(ASY_DATA, 0x80000010, TRUE);
+	write_client_reg(ASY_DATB, 0x00000340, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000005, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000004, TRUE);
+	mddi_wait(60);
+	write_client_reg(ASY_DATA, 0x80000010, TRUE);
+	write_client_reg(ASY_DATB, 0x00003340, TRUE);
+	write_client_reg(ASY_DATC, 0x80000007, TRUE);
+	write_client_reg(ASY_DATD, 0x00004007, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000009, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000008, TRUE);
+	mddi_wait(1);
+	write_client_reg(ASY_DATA, 0x80000007, TRUE);
+	write_client_reg(ASY_DATB, 0x00004017, TRUE);
+	write_client_reg(ASY_DATC, 0x8000005B, TRUE);
+	write_client_reg(ASY_DATD, 0x00000000, TRUE);
+	write_client_reg(ASY_DATE, 0x80000059, TRUE);
+	write_client_reg(ASY_DATF, 0x00000011, TRUE);
+	write_client_reg(ASY_CMDSET, 0x0000000D, TRUE);
+	write_client_reg(ASY_CMDSET, 0x0000000C, TRUE);
+	mddi_wait(20);
+	write_client_reg(ASY_DATA, 0x80000059, TRUE);
+	/* LTPS I/F control */
+	write_client_reg(ASY_DATB, 0x00000019, TRUE);
+	/* Direct cmd transfer enable */
+	write_client_reg(ASY_CMDSET, 0x00000005, TRUE);
+	/* Direct cmd transfer disable */
+	write_client_reg(ASY_CMDSET, 0x00000004, TRUE);
+	mddi_wait(20);
+	/* Index setting of SUB LCDD */
+	write_client_reg(ASY_DATA, 0x80000059, TRUE);
+	/* LTPS I/F control */
+	write_client_reg(ASY_DATB, 0x00000079, TRUE);
+	/* Direct cmd transfer enable */
+	write_client_reg(ASY_CMDSET, 0x00000005, TRUE);
+	/* Direct cmd transfer disable */
+	write_client_reg(ASY_CMDSET, 0x00000004, TRUE);
+	mddi_wait(20);
+	/* Index setting of SUB LCDD */
+	write_client_reg(ASY_DATA, 0x80000059, TRUE);
+	/* LTPS I/F control */
+	write_client_reg(ASY_DATB, 0x000003FD, TRUE);
+	/* Direct cmd transfer enable */
+	write_client_reg(ASY_CMDSET, 0x00000005, TRUE);
+	/* Direct cmd transfer disable */
+	write_client_reg(ASY_CMDSET, 0x00000004, TRUE);
+	mddi_wait(20);
+	mddi_toshiba_state_transition(TOSHIBA_STATE_PRIM_SEC_READY,
+				      TOSHIBA_STATE_SEC_NORMAL_MODE);
+}
+
+static void toshiba_prim_lcd_off(struct msm_fb_data_type *mfd)
+{
+	if (TM_GET_PID(mfd->panel.id) == LCD_SHARP_2P4_VGA) {
+		gordon_disp_off();
+	} else{
+
+		/* Main panel power off (Deep standby in) */
+		write_client_reg(SSITX, 0x000800BC, TRUE);
+		write_client_reg(SSITX, 0x00000100, TRUE);
+		write_client_reg(SSITX, 0x00000028, TRUE);
+		mddi_wait(1);
+		write_client_reg(SSITX, 0x000800B8, TRUE);
+		write_client_reg(SSITX, 0x00000180, TRUE);
+		write_client_reg(SSITX, 0x00000102, TRUE);
+		write_client_reg(SSITX, 0x00000010, TRUE);
+	}
+	write_client_reg(PORT, 0x00000003, TRUE);
+	write_client_reg(REGENB, 0x00000001, TRUE);
+	mddi_wait(1);
+	write_client_reg(PXL, 0x00000000, TRUE);
+	write_client_reg(START, 0x00000000, TRUE);
+	write_client_reg(REGENB, 0x00000001, TRUE);
+	mddi_wait(3);
+	if (TM_GET_PID(mfd->panel.id) != LCD_SHARP_2P4_VGA) {
+		write_client_reg(SSITX, 0x000800B0, TRUE);
+		write_client_reg(SSITX, 0x00000100, TRUE);
+	}
+	mddi_toshiba_state_transition(TOSHIBA_STATE_PRIM_NORMAL_MODE,
+				      TOSHIBA_STATE_PRIM_SEC_STANDBY);
+}
+
+static void toshiba_sec_lcd_off(struct msm_fb_data_type *mfd)
+{
+	if (TM_GET_PID(mfd->panel.id) == LCD_TOSHIBA_2P4_WVGA_PT)
+		return;
+
+	write_client_reg(VSYNIF, 0x00000000, TRUE);
+	write_client_reg(PORT_ENB, 0x00000002, TRUE);
+	write_client_reg(ASY_DATA, 0x80000007, TRUE);
+	write_client_reg(ASY_DATB, 0x00004016, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000005, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000004, TRUE);
+	mddi_wait(2);
+	write_client_reg(ASY_DATA, 0x80000059, TRUE);
+	write_client_reg(ASY_DATB, 0x00000019, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000005, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000004, TRUE);
+	mddi_wait(2);
+	write_client_reg(ASY_DATA, 0x80000059, TRUE);
+	write_client_reg(ASY_DATB, 0x0000000B, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000005, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000004, TRUE);
+	mddi_wait(2);
+	write_client_reg(ASY_DATA, 0x80000059, TRUE);
+	write_client_reg(ASY_DATB, 0x00000002, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000005, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000004, TRUE);
+	mddi_wait(4);
+	write_client_reg(ASY_DATA, 0x80000010, TRUE);
+	write_client_reg(ASY_DATB, 0x00000300, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000005, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000004, TRUE);
+	mddi_wait(4);
+	write_client_reg(ASY_DATA, 0x80000059, TRUE);
+	write_client_reg(ASY_DATB, 0x00000000, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000005, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000004, TRUE);
+	mddi_wait(2);
+	write_client_reg(ASY_DATA, 0x80000007, TRUE);
+	write_client_reg(ASY_DATB, 0x00004004, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000005, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000004, TRUE);
+	mddi_wait(2);
+	write_client_reg(PORT, 0x00000000, TRUE);
+	write_client_reg(PXL, 0x00000000, TRUE);
+	write_client_reg(START, 0x00000000, TRUE);
+	write_client_reg(VSYNIF, 0x00000001, TRUE);
+	write_client_reg(PORT_ENB, 0x00000001, TRUE);
+	write_client_reg(REGENB, 0x00000001, TRUE);
+	mddi_toshiba_state_transition(TOSHIBA_STATE_SEC_NORMAL_MODE,
+				      TOSHIBA_STATE_PRIM_SEC_STANDBY);
+}
+
+static void toshiba_sec_cont_update_start(struct msm_fb_data_type *mfd)
+{
+
+	if (TM_GET_PID(mfd->panel.id) == LCD_TOSHIBA_2P4_WVGA_PT)
+		return;
+
+	write_client_reg(VSYNIF, 0x00000000, TRUE);
+	write_client_reg(PORT_ENB, 0x00000002, TRUE);
+	write_client_reg(INTMASK, 0x00000001, TRUE);
+	write_client_reg(TTBUSSEL, 0x0000000B, TRUE);
+	write_client_reg(MONI, 0x00000008, TRUE);
+	write_client_reg(CLKENB, 0x000000EF, TRUE);
+	write_client_reg(CLKENB, 0x000010EF, TRUE);
+	write_client_reg(CLKENB, 0x000011EF, TRUE);
+	write_client_reg(BITMAP4, 0x00DC00B0, TRUE);
+	write_client_reg(HCYCLE, 0x0000006B, TRUE);
+	write_client_reg(HSW, 0x00000003, TRUE);
+	write_client_reg(HDE_START, 0x00000002, TRUE);
+	write_client_reg(HDE_SIZE, 0x00000057, TRUE);
+	write_client_reg(VCYCLE, 0x000000E6, TRUE);
+	write_client_reg(VSW, 0x00000001, TRUE);
+	write_client_reg(VDE_START, 0x00000003, TRUE);
+	write_client_reg(VDE_SIZE, 0x000000DB, TRUE);
+	write_client_reg(WRSTB, 0x00000015, TRUE);
+	write_client_reg(MPLFBUF, 0x00000004, TRUE);
+	write_client_reg(ASY_DATA, 0x80000021, TRUE);
+	write_client_reg(ASY_DATB, 0x00000000, TRUE);
+	write_client_reg(ASY_DATC, 0x80000022, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000007, TRUE);
+	write_client_reg(PXL, 0x00000089, TRUE);
+	write_client_reg(VSYNIF, 0x00000001, TRUE);
+	mddi_wait(2);
+}
+
+static void toshiba_sec_cont_update_stop(struct msm_fb_data_type *mfd)
+{
+	if (TM_GET_PID(mfd->panel.id) == LCD_TOSHIBA_2P4_WVGA_PT)
+		return;
+
+	write_client_reg(PXL, 0x00000000, TRUE);
+	write_client_reg(VSYNIF, 0x00000000, TRUE);
+	write_client_reg(START, 0x00000000, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000000, TRUE);
+	mddi_wait(3);
+	write_client_reg(SRST, 0x00000002, TRUE);
+	mddi_wait(3);
+	write_client_reg(SRST, 0x00000003, TRUE);
+}
+
+static void toshiba_sec_backlight_on(struct msm_fb_data_type *mfd)
+{
+	if (TM_GET_PID(mfd->panel.id) == LCD_TOSHIBA_2P4_WVGA_PT)
+		return;
+
+	write_client_reg(TIMER0CTRL, 0x00000060, TRUE);
+	write_client_reg(TIMER0LOAD, 0x00001388, TRUE);
+	write_client_reg(PWM0OFF, 0x00000001, TRUE);
+	write_client_reg(TIMER1CTRL, 0x00000060, TRUE);
+	write_client_reg(TIMER1LOAD, 0x00001388, TRUE);
+	write_client_reg(PWM1OFF, 0x00001387, TRUE);
+	write_client_reg(TIMER0CTRL, 0x000000E0, TRUE);
+	write_client_reg(TIMER1CTRL, 0x000000E0, TRUE);
+	write_client_reg(PWMCR, 0x00000003, TRUE);
+}
+
+static void toshiba_sec_sleep_in(struct msm_fb_data_type *mfd)
+{
+	if (TM_GET_PID(mfd->panel.id) == LCD_TOSHIBA_2P4_WVGA_PT)
+		return;
+
+	write_client_reg(VSYNIF, 0x00000000, TRUE);
+	write_client_reg(PORT_ENB, 0x00000002, TRUE);
+	write_client_reg(ASY_DATA, 0x80000007, TRUE);
+	write_client_reg(ASY_DATB, 0x00004016, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000005, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000004, TRUE);
+	mddi_wait(2);
+	write_client_reg(ASY_DATA, 0x80000059, TRUE);
+	write_client_reg(ASY_DATB, 0x00000019, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000005, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000004, TRUE);
+	mddi_wait(2);
+	write_client_reg(ASY_DATA, 0x80000059, TRUE);
+	write_client_reg(ASY_DATB, 0x0000000B, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000005, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000004, TRUE);
+	mddi_wait(2);
+	write_client_reg(ASY_DATA, 0x80000059, TRUE);
+	write_client_reg(ASY_DATB, 0x00000002, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000005, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000004, TRUE);
+	mddi_wait(4);
+	write_client_reg(ASY_DATA, 0x80000010, TRUE);
+	write_client_reg(ASY_DATB, 0x00000300, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000005, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000004, TRUE);
+	mddi_wait(4);
+	write_client_reg(ASY_DATA, 0x80000059, TRUE);
+	write_client_reg(ASY_DATB, 0x00000000, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000005, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000004, TRUE);
+	mddi_wait(2);
+	write_client_reg(ASY_DATA, 0x80000007, TRUE);
+	write_client_reg(ASY_DATB, 0x00004004, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000005, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000004, TRUE);
+	mddi_wait(2);
+	write_client_reg(PORT, 0x00000000, TRUE);
+	write_client_reg(PXL, 0x00000000, TRUE);
+	write_client_reg(START, 0x00000000, TRUE);
+	write_client_reg(REGENB, 0x00000001, TRUE);
+	/* Sleep in sequence */
+	write_client_reg(ASY_DATA, 0x80000010, TRUE);
+	write_client_reg(ASY_DATB, 0x00000302, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000005, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000004, TRUE);
+}
+
+static void toshiba_sec_sleep_out(struct msm_fb_data_type *mfd)
+{
+	if (TM_GET_PID(mfd->panel.id) == LCD_TOSHIBA_2P4_WVGA_PT)
+		return;
+
+	write_client_reg(VSYNIF, 0x00000000, TRUE);
+	write_client_reg(PORT_ENB, 0x00000002, TRUE);
+	write_client_reg(ASY_DATA, 0x80000010, TRUE);
+	write_client_reg(ASY_DATB, 0x00000300, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000005, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000004, TRUE);
+	/*  Display ON sequence */
+	write_client_reg(ASY_DATA, 0x80000011, TRUE);
+	write_client_reg(ASY_DATB, 0x00000812, TRUE);
+	write_client_reg(ASY_DATC, 0x80000012, TRUE);
+	write_client_reg(ASY_DATD, 0x00000003, TRUE);
+	write_client_reg(ASY_DATE, 0x80000013, TRUE);
+	write_client_reg(ASY_DATF, 0x00000909, TRUE);
+	write_client_reg(ASY_DATG, 0x80000010, TRUE);
+	write_client_reg(ASY_DATH, 0x00000040, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000001, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000000, TRUE);
+	mddi_wait(4);
+	write_client_reg(ASY_DATA, 0x80000010, TRUE);
+	write_client_reg(ASY_DATB, 0x00000340, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000005, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000004, TRUE);
+	mddi_wait(6);
+	write_client_reg(ASY_DATA, 0x80000010, TRUE);
+	write_client_reg(ASY_DATB, 0x00003340, TRUE);
+	write_client_reg(ASY_DATC, 0x80000007, TRUE);
+	write_client_reg(ASY_DATD, 0x00004007, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000009, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000008, TRUE);
+	mddi_wait(1);
+	write_client_reg(ASY_DATA, 0x80000007, TRUE);
+	write_client_reg(ASY_DATB, 0x00004017, TRUE);
+	write_client_reg(ASY_DATC, 0x8000005B, TRUE);
+	write_client_reg(ASY_DATD, 0x00000000, TRUE);
+	write_client_reg(ASY_DATE, 0x80000059, TRUE);
+	write_client_reg(ASY_DATF, 0x00000011, TRUE);
+	write_client_reg(ASY_CMDSET, 0x0000000D, TRUE);
+	write_client_reg(ASY_CMDSET, 0x0000000C, TRUE);
+	mddi_wait(2);
+	write_client_reg(ASY_DATA, 0x80000059, TRUE);
+	write_client_reg(ASY_DATB, 0x00000019, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000005, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000004, TRUE);
+	mddi_wait(2);
+	write_client_reg(ASY_DATA, 0x80000059, TRUE);
+	write_client_reg(ASY_DATB, 0x00000079, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000005, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000004, TRUE);
+	mddi_wait(2);
+	write_client_reg(ASY_DATA, 0x80000059, TRUE);
+	write_client_reg(ASY_DATB, 0x000003FD, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000005, TRUE);
+	write_client_reg(ASY_CMDSET, 0x00000004, TRUE);
+	mddi_wait(2);
+}
+
+static void mddi_toshiba_lcd_set_backlight(struct msm_fb_data_type *mfd)
+{
+	int32 level;
+	int ret = -EPERM;
+	int max = mfd->panel_info.bl_max;
+	int min = mfd->panel_info.bl_min;
+	int i = 0;
+
+	if (mddi_toshiba_pdata && mddi_toshiba_pdata->pmic_backlight) {
+		while (i++ < 3) {
+			ret = mddi_toshiba_pdata->pmic_backlight(mfd->bl_level);
+			if (!ret)
+				return;
+			msleep(10);
+		}
+		printk(KERN_WARNING "%s: pmic_backlight Failed\n", __func__);
+	}
+
+
+	if (ret && mddi_toshiba_pdata && mddi_toshiba_pdata->backlight_level) {
+		level = mddi_toshiba_pdata->backlight_level(mfd->bl_level,
+								max, min);
+
+		if (level < 0)
+			return;
+
+		if (TM_GET_PID(mfd->panel.id) == LCD_SHARP_2P4_VGA)
+			write_client_reg(TIMER0LOAD, 0x00001388, TRUE);
+	} else {
+		if (!max)
+			level = 0;
+		else
+			level = (mfd->bl_level * 4999) / max;
+	}
+
+	write_client_reg(PWM0OFF, level, TRUE);
+}
+
+static void mddi_toshiba_vsync_set_handler(msm_fb_vsync_handler_type handler,	/* ISR to be executed */
+					   void *arg)
+{
+	boolean error = FALSE;
+	unsigned long flags;
+
+	/* Disable interrupts */
+	spin_lock_irqsave(&mddi_host_spin_lock, flags);
+	/* INTLOCK(); */
+
+	if (mddi_toshiba_vsync_handler != NULL) {
+		error = TRUE;
+	} else {
+		/* Register the handler for this particular GROUP interrupt source */
+		mddi_toshiba_vsync_handler = handler;
+		mddi_toshiba_vsync_handler_arg = arg;
+	}
+
+	/* Restore interrupts */
+	spin_unlock_irqrestore(&mddi_host_spin_lock, flags);
+	/* MDDI_INTFREE(); */
+	if (error) {
+		MDDI_MSG_ERR("MDDI: Previous Vsync handler never called\n");
+	} else {
+		/* Enable the vsync wakeup */
+		mddi_queue_register_write(INTMSK, 0x0000, FALSE, 0);
+
+		mddi_toshiba_vsync_attempts = 1;
+		mddi_vsync_detect_enabled = TRUE;
+	}
+}				/* mddi_toshiba_vsync_set_handler */
+
+static void mddi_toshiba_lcd_vsync_detected(boolean detected)
+{
+	/* static timetick_type start_time = 0; */
+	static struct timeval start_time;
+	static boolean first_time = TRUE;
+	/* uint32 mdp_cnt_val = 0; */
+	/* timetick_type elapsed_us; */
+	struct timeval now;
+	uint32 elapsed_us;
+	uint32 num_vsyncs;
+
+	if ((detected) || (mddi_toshiba_vsync_attempts > 5)) {
+		if ((detected) && (mddi_toshiba_monitor_refresh_value)) {
+			/* if (start_time != 0) */
+			if (!first_time) {
+				jiffies_to_timeval(jiffies, &now);
+				elapsed_us =
+				    (now.tv_sec - start_time.tv_sec) * 1000000 +
+				    now.tv_usec - start_time.tv_usec;
+				/*
+				 * LCD is configured for a refresh every usecs,
+				 *  so to determine the number of vsyncs that
+				 *  have occurred since the last measurement
+				 *  add half that to the time difference and
+				 *  divide by the refresh rate.
+				 */
+				num_vsyncs = (elapsed_us +
+					      (mddi_toshiba_usecs_per_refresh >>
+					       1)) /
+				    mddi_toshiba_usecs_per_refresh;
+				/*
+				 * LCD is configured for * hsyncs (rows) per
+				 * refresh cycle. Calculate new rows_per_second
+				 * value based upon these new measurements.
+				 * MDP can update with this new value.
+				 */
+				mddi_toshiba_rows_per_second =
+				    (mddi_toshiba_rows_per_refresh * 1000 *
+				     num_vsyncs) / (elapsed_us / 1000);
+			}
+			/* start_time = timetick_get(); */
+			first_time = FALSE;
+			jiffies_to_timeval(jiffies, &start_time);
+			if (mddi_toshiba_report_refresh_measurements) {
+				(void)mddi_queue_register_read_int(VPOS,
+								   &mddi_toshiba_curr_vpos);
+				/* mdp_cnt_val = MDP_LINE_COUNT; */
+			}
+		}
+		/* if detected = TRUE, client initiated wakeup was detected */
+		if (mddi_toshiba_vsync_handler != NULL) {
+			(*mddi_toshiba_vsync_handler)
+			    (mddi_toshiba_vsync_handler_arg);
+			mddi_toshiba_vsync_handler = NULL;
+		}
+		mddi_vsync_detect_enabled = FALSE;
+		mddi_toshiba_vsync_attempts = 0;
+		/* need to disable the interrupt wakeup */
+		if (!mddi_queue_register_write_int(INTMSK, 0x0001))
+			MDDI_MSG_ERR("Vsync interrupt disable failed!\n");
+		if (!detected) {
+			/* give up after 5 failed attempts but show error */
+			MDDI_MSG_NOTICE("Vsync detection failed!\n");
+		} else if ((mddi_toshiba_monitor_refresh_value) &&
+			   (mddi_toshiba_report_refresh_measurements)) {
+			MDDI_MSG_NOTICE("  Last Line Counter=%d!\n",
+					mddi_toshiba_curr_vpos);
+		/* MDDI_MSG_NOTICE("  MDP Line Counter=%d!\n",mdp_cnt_val); */
+			MDDI_MSG_NOTICE("  Lines Per Second=%d!\n",
+					mddi_toshiba_rows_per_second);
+		}
+		/* clear the interrupt */
+		if (!mddi_queue_register_write_int(INTFLG, 0x0001))
+			MDDI_MSG_ERR("Vsync interrupt clear failed!\n");
+	} else {
+		/* if detected = FALSE, we woke up from hibernation, but did not
+		 * detect client initiated wakeup.
+		 */
+		mddi_toshiba_vsync_attempts++;
+	}
+}
+
+static void mddi_toshiba_prim_init(struct msm_fb_data_type *mfd)
+{
+
+	switch (toshiba_state) {
+	case TOSHIBA_STATE_PRIM_SEC_READY:
+		break;
+	case TOSHIBA_STATE_OFF:
+		toshiba_state = TOSHIBA_STATE_PRIM_SEC_STANDBY;
+		toshiba_common_initial_setup(mfd);
+		break;
+	case TOSHIBA_STATE_PRIM_SEC_STANDBY:
+		toshiba_common_initial_setup(mfd);
+		break;
+	case TOSHIBA_STATE_SEC_NORMAL_MODE:
+		toshiba_sec_cont_update_stop(mfd);
+		toshiba_sec_sleep_in(mfd);
+		toshiba_sec_sleep_out(mfd);
+		toshiba_sec_lcd_off(mfd);
+		toshiba_common_initial_setup(mfd);
+		break;
+	default:
+		MDDI_MSG_ERR("mddi_toshiba_prim_init from state %d\n",
+			     toshiba_state);
+	}
+
+	toshiba_prim_start(mfd);
+	if (TM_GET_PID(mfd->panel.id) == LCD_SHARP_2P4_VGA)
+		gordon_disp_init();
+	mddi_host_write_pix_attr_reg(0x00C3);
+}
+
+static void mddi_toshiba_sec_init(struct msm_fb_data_type *mfd)
+{
+
+	switch (toshiba_state) {
+	case TOSHIBA_STATE_PRIM_SEC_READY:
+		break;
+	case TOSHIBA_STATE_PRIM_SEC_STANDBY:
+		toshiba_common_initial_setup(mfd);
+		break;
+	case TOSHIBA_STATE_PRIM_NORMAL_MODE:
+		toshiba_prim_lcd_off(mfd);
+		toshiba_common_initial_setup(mfd);
+		break;
+	default:
+		MDDI_MSG_ERR("mddi_toshiba_sec_init from state %d\n",
+			     toshiba_state);
+	}
+
+	toshiba_sec_start(mfd);
+	toshiba_sec_backlight_on(mfd);
+	toshiba_sec_cont_update_start(mfd);
+	mddi_host_write_pix_attr_reg(0x0400);
+}
+
+static void mddi_toshiba_lcd_powerdown(struct msm_fb_data_type *mfd)
+{
+	switch (toshiba_state) {
+	case TOSHIBA_STATE_PRIM_SEC_READY:
+		mddi_toshiba_prim_init(mfd);
+		mddi_toshiba_lcd_powerdown(mfd);
+		return;
+	case TOSHIBA_STATE_PRIM_SEC_STANDBY:
+		break;
+	case TOSHIBA_STATE_PRIM_NORMAL_MODE:
+		toshiba_prim_lcd_off(mfd);
+		break;
+	case TOSHIBA_STATE_SEC_NORMAL_MODE:
+		toshiba_sec_cont_update_stop(mfd);
+		toshiba_sec_sleep_in(mfd);
+		toshiba_sec_sleep_out(mfd);
+		toshiba_sec_lcd_off(mfd);
+		break;
+	default:
+		MDDI_MSG_ERR("mddi_toshiba_lcd_powerdown from state %d\n",
+			     toshiba_state);
+	}
+}
+
+static int mddi_sharpgordon_firsttime = 1;
+
+static int mddi_toshiba_lcd_on(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+	mfd = platform_get_drvdata(pdev);
+	if (!mfd)
+		return -ENODEV;
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	mddi_host_client_cnt_reset();
+
+	if (TM_GET_DID(mfd->panel.id) == TOSHIBA_VGA_PRIM)
+		mddi_toshiba_prim_init(mfd);
+	else
+		mddi_toshiba_sec_init(mfd);
+	if (TM_GET_PID(mfd->panel.id) == LCD_SHARP_2P4_VGA) {
+		if (mddi_sharpgordon_firsttime) {
+			mddi_sharpgordon_firsttime = 0;
+			write_client_reg(REGENB, 0x00000001, TRUE);
+		}
+	}
+	return 0;
+}
+
+static int mddi_toshiba_lcd_off(struct platform_device *pdev)
+{
+	if (mddi_toshiba_vsync_handler != NULL) {
+		(*mddi_toshiba_vsync_handler)
+			    (mddi_toshiba_vsync_handler_arg);
+		mddi_toshiba_vsync_handler = NULL;
+		printk(KERN_INFO "%s: clean up vsyn_handler=%x\n", __func__,
+				(int)mddi_toshiba_vsync_handler);
+	}
+
+	mddi_toshiba_lcd_powerdown(platform_get_drvdata(pdev));
+	return 0;
+}
+
+static int __devinit mddi_toshiba_lcd_probe(struct platform_device *pdev)
+{
+	if (pdev->id == 0) {
+		mddi_toshiba_pdata = pdev->dev.platform_data;
+		return 0;
+	}
+
+	msm_fb_add_device(pdev);
+
+	return 0;
+}
+
+static struct platform_driver this_driver = {
+	.probe  = mddi_toshiba_lcd_probe,
+	.driver = {
+		.name   = "mddi_toshiba",
+	},
+};
+
+static struct msm_fb_panel_data toshiba_panel_data = {
+	.on 		= mddi_toshiba_lcd_on,
+	.off 		= mddi_toshiba_lcd_off,
+};
+
+static int ch_used[3];
+
+int mddi_toshiba_device_register(struct msm_panel_info *pinfo,
+					u32 channel, u32 panel)
+{
+	struct platform_device *pdev = NULL;
+	int ret;
+
+	if ((channel >= 3) || ch_used[channel])
+		return -ENODEV;
+
+	if ((channel != TOSHIBA_VGA_PRIM) &&
+	    mddi_toshiba_pdata && mddi_toshiba_pdata->panel_num)
+		if (mddi_toshiba_pdata->panel_num() < 2)
+			return -ENODEV;
+
+	ch_used[channel] = TRUE;
+
+	pdev = platform_device_alloc("mddi_toshiba", (panel << 8)|channel);
+	if (!pdev)
+		return -ENOMEM;
+
+	if (channel == TOSHIBA_VGA_PRIM) {
+		toshiba_panel_data.set_backlight =
+				mddi_toshiba_lcd_set_backlight;
+
+		if (pinfo->lcd.vsync_enable) {
+			toshiba_panel_data.set_vsync_notifier =
+				mddi_toshiba_vsync_set_handler;
+			mddi_lcd.vsync_detected =
+				mddi_toshiba_lcd_vsync_detected;
+		}
+	} else {
+		toshiba_panel_data.set_backlight = NULL;
+		toshiba_panel_data.set_vsync_notifier = NULL;
+	}
+
+	toshiba_panel_data.panel_info = *pinfo;
+
+	ret = platform_device_add_data(pdev, &toshiba_panel_data,
+		sizeof(toshiba_panel_data));
+	if (ret) {
+		printk(KERN_ERR
+		  "%s: platform_device_add_data failed!\n", __func__);
+		goto err_device_put;
+	}
+
+	ret = platform_device_add(pdev);
+	if (ret) {
+		printk(KERN_ERR
+		  "%s: platform_device_register failed!\n", __func__);
+		goto err_device_put;
+	}
+
+	return 0;
+
+err_device_put:
+	platform_device_put(pdev);
+	return ret;
+}
+
+static int __init mddi_toshiba_lcd_init(void)
+{
+	return platform_driver_register(&this_driver);
+}
+
+module_init(mddi_toshiba_lcd_init);
diff --git a/drivers/video/msm/mddi_toshiba.h b/drivers/video/msm/mddi_toshiba.h
new file mode 100644
index 0000000..646f5e9
--- /dev/null
+++ b/drivers/video/msm/mddi_toshiba.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef MDDI_TOSHIBA_H
+#define MDDI_TOSHIBA_H
+
+#define TOSHIBA_VGA_PRIM 1
+#define TOSHIBA_VGA_SECD 2
+
+#define LCD_TOSHIBA_2P4_VGA 	0
+#define LCD_TOSHIBA_2P4_WVGA 	1
+#define LCD_TOSHIBA_2P4_WVGA_PT	2
+#define LCD_SHARP_2P4_VGA 	3
+
+#define GPIO_BLOCK_BASE        0x150000
+#define SYSTEM_BLOCK2_BASE     0x170000
+
+#define GPIODIR     (GPIO_BLOCK_BASE|0x04)
+#define GPIOSEL     (SYSTEM_BLOCK2_BASE|0x00)
+#define GPIOPC      (GPIO_BLOCK_BASE|0x28)
+#define GPIODATA    (GPIO_BLOCK_BASE|0x00)
+
+#define write_client_reg(__X, __Y, __Z) {\
+  mddi_queue_register_write(__X, __Y, TRUE, 0);\
+}
+
+#endif /* MDDI_TOSHIBA_H */
diff --git a/drivers/video/msm/mddi_toshiba_vga.c b/drivers/video/msm/mddi_toshiba_vga.c
new file mode 100644
index 0000000..73749f9
--- /dev/null
+++ b/drivers/video/msm/mddi_toshiba_vga.c
@@ -0,0 +1,134 @@
+/* Copyright (c) 2009-2010, 2012 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_fb.h"
+#include "mddihost.h"
+#include "mddihosti.h"
+#include "mddi_toshiba.h"
+
+static uint32 read_client_reg(uint32 addr)
+{
+	uint32 val;
+	mddi_queue_register_read(addr, &val, TRUE, 0);
+	return val;
+}
+
+static uint32 toshiba_lcd_gpio_read(void)
+{
+	uint32 val;
+
+	write_client_reg(GPIODIR, 0x0000000C, TRUE);
+	write_client_reg(GPIOSEL, 0x00000000, TRUE);
+	write_client_reg(GPIOSEL, 0x00000000, TRUE);
+	write_client_reg(GPIOPC, 0x03CF00C0, TRUE);
+	val = read_client_reg(GPIODATA) & 0x2C0;
+
+	return val;
+}
+
+static u32 mddi_toshiba_panel_detect(void)
+{
+	mddi_host_type host_idx = MDDI_HOST_PRIM;
+	uint32 lcd_gpio;
+	u32 mddi_toshiba_lcd = LCD_TOSHIBA_2P4_VGA;
+
+	/* Toshiba display requires larger drive_lo value */
+	mddi_host_reg_out(DRIVE_LO, 0x0050);
+
+	lcd_gpio = toshiba_lcd_gpio_read();
+	switch (lcd_gpio) {
+	case 0x0080:
+		mddi_toshiba_lcd = LCD_SHARP_2P4_VGA;
+		break;
+
+	case 0x00C0:
+	default:
+		mddi_toshiba_lcd = LCD_TOSHIBA_2P4_VGA;
+		break;
+	}
+
+	return mddi_toshiba_lcd;
+}
+
+static int __init mddi_toshiba_vga_init(void)
+{
+	int ret;
+	struct msm_panel_info pinfo;
+	u32 panel;
+
+#ifdef CONFIG_FB_MSM_MDDI_AUTO_DETECT
+	u32 id;
+
+	ret = msm_fb_detect_client("mddi_toshiba_vga");
+	if (ret == -ENODEV)
+		return 0;
+
+	if (ret) {
+		id = mddi_get_client_id();
+		if ((id >> 16) != 0xD263)
+			return 0;
+	}
+#endif
+
+	panel = mddi_toshiba_panel_detect();
+
+	pinfo.xres = 480;
+	pinfo.yres = 640;
+	pinfo.type = MDDI_PANEL;
+	pinfo.pdest = DISPLAY_1;
+	pinfo.mddi.vdopkt = MDDI_DEFAULT_PRIM_PIX_ATTR;
+	pinfo.wait_cycle = 0;
+	pinfo.bpp = 18;
+	pinfo.lcd.vsync_enable = TRUE;
+	pinfo.mddi.is_type1 = TRUE;
+	pinfo.lcd.refx100 = 6118;
+	pinfo.lcd.v_back_porch = 6;
+	pinfo.lcd.v_front_porch = 0;
+	pinfo.lcd.v_pulse_width = 0;
+	pinfo.lcd.hw_vsync_mode = FALSE;
+	pinfo.lcd.vsync_notifier_period = (1 * HZ);
+	pinfo.bl_max = 99;
+	pinfo.bl_min = 1;
+	pinfo.clk_rate = 122880000;
+	pinfo.clk_min =  120000000;
+	pinfo.clk_max =  200000000;
+	pinfo.fb_num = 2;
+
+	ret = mddi_toshiba_device_register(&pinfo, TOSHIBA_VGA_PRIM, panel);
+	if (ret) {
+		printk(KERN_ERR "%s: failed to register device!\n", __func__);
+		return ret;
+	}
+
+	pinfo.xres = 176;
+	pinfo.yres = 220;
+	MSM_FB_SINGLE_MODE_PANEL(&pinfo);
+	pinfo.type = MDDI_PANEL;
+	pinfo.pdest = DISPLAY_2;
+	pinfo.mddi.vdopkt = 0x400;
+	pinfo.wait_cycle = 0;
+	pinfo.bpp = 18;
+	pinfo.clk_rate = 122880000;
+	pinfo.clk_min =  120000000;
+	pinfo.clk_max =  200000000;
+	pinfo.fb_num = 2;
+
+	ret = mddi_toshiba_device_register(&pinfo, TOSHIBA_VGA_SECD, panel);
+	if (ret)
+		printk(KERN_WARNING
+			"%s: failed to register device!\n", __func__);
+
+	return ret;
+}
+
+module_init(mddi_toshiba_vga_init);
diff --git a/drivers/video/msm/mddi_toshiba_wvga.c b/drivers/video/msm/mddi_toshiba_wvga.c
new file mode 100644
index 0000000..c1925e1
--- /dev/null
+++ b/drivers/video/msm/mddi_toshiba_wvga.c
@@ -0,0 +1,61 @@
+/* Copyright (c) 2009-2010, 2012 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_fb.h"
+#include "mddihost.h"
+#include "mddi_toshiba.h"
+
+static int __init mddi_toshiba_wvga_init(void)
+{
+	int ret;
+	struct msm_panel_info pinfo;
+
+#ifdef CONFIG_FB_MSM_MDDI_AUTO_DETECT
+	if (msm_fb_detect_client("mddi_toshiba_wvga"))
+		return 0;
+#endif
+
+	pinfo.xres = 800;
+	pinfo.yres = 480;
+	MSM_FB_SINGLE_MODE_PANEL(&pinfo);
+	pinfo.pdest = DISPLAY_2;
+	pinfo.type = MDDI_PANEL;
+	pinfo.mddi.vdopkt = MDDI_DEFAULT_PRIM_PIX_ATTR;
+	pinfo.wait_cycle = 0;
+	pinfo.bpp = 18;
+	pinfo.lcd.vsync_enable = TRUE;
+	pinfo.mddi.is_type1 = TRUE;
+	pinfo.lcd.refx100 = 6118;
+	pinfo.lcd.v_back_porch = 6;
+	pinfo.lcd.v_front_porch = 0;
+	pinfo.lcd.v_pulse_width = 0;
+	pinfo.lcd.hw_vsync_mode = FALSE;
+	pinfo.lcd.vsync_notifier_period = (1 * HZ);
+	pinfo.bl_max = 4;
+	pinfo.bl_min = 1;
+	pinfo.clk_rate = 192000000;
+	pinfo.clk_min =  190000000;
+	pinfo.clk_max =  200000000;
+	pinfo.fb_num = 2;
+
+	ret = mddi_toshiba_device_register(&pinfo, TOSHIBA_VGA_PRIM,
+					   LCD_TOSHIBA_2P4_WVGA);
+	if (ret) {
+		printk(KERN_ERR "%s: failed to register device!\n", __func__);
+		return ret;
+	}
+
+	return ret;
+}
+
+module_init(mddi_toshiba_wvga_init);
diff --git a/drivers/video/msm/mddi_toshiba_wvga_pt.c b/drivers/video/msm/mddi_toshiba_wvga_pt.c
new file mode 100644
index 0000000..8da1485
--- /dev/null
+++ b/drivers/video/msm/mddi_toshiba_wvga_pt.c
@@ -0,0 +1,69 @@
+/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_fb.h"
+#include "mddihost.h"
+#include "mddihosti.h"
+#include "mddi_toshiba.h"
+
+static struct msm_panel_info pinfo;
+
+static int __init mddi_toshiba_wvga_pt_init(void)
+{
+	int ret;
+#ifdef CONFIG_FB_MSM_MDDI_AUTO_DETECT
+	uint id;
+
+	ret = msm_fb_detect_client("mddi_toshiba_wvga_pt");
+	if (ret == -ENODEV)
+		return 0;
+
+	if (ret) {
+		id = mddi_get_client_id();
+		if (id != 0xd2638722)
+			return 0;
+	}
+#endif
+
+	pinfo.xres = 480;
+	pinfo.yres = 800;
+	MSM_FB_SINGLE_MODE_PANEL(&pinfo);
+	pinfo.type = MDDI_PANEL;
+	pinfo.pdest = DISPLAY_1;
+	pinfo.mddi.vdopkt = MDDI_DEFAULT_PRIM_PIX_ATTR;
+	pinfo.wait_cycle = 0;
+	pinfo.bpp = 18;
+	pinfo.lcd.vsync_enable = TRUE;
+	pinfo.lcd.refx100 = 6102; /* adjust refx100 to prevent tearing */
+	pinfo.mddi.is_type1 = TRUE;
+	pinfo.lcd.v_back_porch = 8;     /* vsw=10 + vbp = 8 */
+	pinfo.lcd.v_front_porch = 2;
+	pinfo.lcd.v_pulse_width = 10;
+	pinfo.lcd.hw_vsync_mode = FALSE;
+	pinfo.lcd.vsync_notifier_period = (1 * HZ);
+	pinfo.bl_max = 15;
+	pinfo.bl_min = 1;
+	pinfo.clk_rate = 222750000;
+	pinfo.clk_min =  200000000;
+	pinfo.clk_max =  240000000;
+	pinfo.fb_num = 2;
+
+	ret = mddi_toshiba_device_register(&pinfo, TOSHIBA_VGA_PRIM,
+						LCD_TOSHIBA_2P4_WVGA_PT);
+	if (ret)
+		printk(KERN_ERR "%s: failed to register device!\n", __func__);
+
+	return ret;
+}
+
+module_init(mddi_toshiba_wvga_pt_init);
diff --git a/drivers/video/msm/mddihost.c b/drivers/video/msm/mddihost.c
new file mode 100644
index 0000000..c6acf9f
--- /dev/null
+++ b/drivers/video/msm/mddihost.c
@@ -0,0 +1,626 @@
+/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+
+#include "msm_fb.h"
+#include "mddihost.h"
+#include "mddihosti.h"
+
+#include <linux/clk.h>
+#include <mach/clk.h>
+
+struct semaphore mddi_host_mutex;
+
+struct clk *mddi_io_clk;
+static boolean mddi_host_powered = FALSE;
+static boolean mddi_host_initialized = FALSE;
+extern uint32 *mddi_reg_read_value_ptr;
+
+mddi_lcd_func_type mddi_lcd;
+
+extern mddi_client_capability_type mddi_client_capability_pkt;
+
+#ifdef MDDI_HOST_WINDOW_WORKAROUND
+/* Tables showing number of rows that would cause a packet length
+ * ending in 0x02, for each number of columns. These tables have
+ * been generated for MDDI packets that have 16 and 16 bits-per-pixel.
+ * This is a work-around for MDDI clients that declare a CRC error
+ * on MDDI packets where ((length & 0x00ff) == 0x02).
+ */
+static uint16 error_vals_16bpp[] = {
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 12, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 10, 0, 0, 0, 14, 0, 0, 0, 2, 0, 0, 4, 6, 12, 0,
+0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0,
+0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 11, 4, 0, 12, 0,
+0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0,
+0, 10, 0, 1, 0, 14, 0, 0, 0, 2, 0, 3, 4, 6, 12, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 12, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 10, 0, 0, 0, 14, 0, 0, 0, 2, 0, 0, 4, 6, 12, 0,
+0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0,
+0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 11, 4, 0, 12, 0,
+0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0,
+};
+
+static uint16 error_vals_18bpp[] = {
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 14,
+0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 9, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 7,
+0, 0, 0, 0, 0, 0, 1, 0, 0, 16, 0, 0, 0, 0, 0, 6,
+14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+7, 0, 0, 0, 0, 0, 0, 4, 0, 16, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
+0, 7, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 9, 0
+};
+#endif
+
+#ifdef FEATURE_MDDI_HITACHI
+extern void mddi_hitachi_window_adjust(uint16 x1,
+				       uint16 x2, uint16 y1, uint16 y2);
+#endif
+
+extern void mddi_toshiba_lcd_init(void);
+
+#ifdef FEATURE_MDDI_S6D0142
+extern void mddi_s6d0142_lcd_init(void);
+extern void mddi_s6d0142_window_adjust(uint16 x1,
+				       uint16 x2,
+				       uint16 y1,
+				       uint16 y2,
+				       mddi_llist_done_cb_type done_cb);
+#endif
+
+void mddi_init(void)
+{
+	if (mddi_host_initialized)
+		return;
+
+	mddi_host_initialized = TRUE;
+
+	sema_init(&mddi_host_mutex, 1);
+
+	if (!mddi_host_powered) {
+		down(&mddi_host_mutex);
+		mddi_host_init(MDDI_HOST_PRIM);
+		mddi_host_powered = TRUE;
+		up(&mddi_host_mutex);
+		mdelay(10);
+	}
+}
+
+int mddi_host_register_read(uint32 reg_addr,
+     uint32 *reg_value_ptr, boolean wait, mddi_host_type host) {
+	mddi_linked_list_type *curr_llist_ptr;
+	mddi_register_access_packet_type *regacc_pkt_ptr;
+	uint16 curr_llist_idx;
+	int ret = 0;
+
+	if (in_interrupt())
+		MDDI_MSG_CRIT("Called from ISR context\n");
+
+	if (!mddi_host_powered) {
+		MDDI_MSG_ERR("MDDI powered down!\n");
+		mddi_init();
+	}
+
+	down(&mddi_host_mutex);
+
+	mddi_reg_read_value_ptr = reg_value_ptr;
+	curr_llist_idx = mddi_get_reg_read_llist_item(host, TRUE);
+	if (curr_llist_idx == UNASSIGNED_INDEX) {
+		up(&mddi_host_mutex);
+
+		/* need to change this to some sort of wait */
+		MDDI_MSG_ERR("Attempting to queue up more than 1 reg read\n");
+		return -EINVAL;
+	}
+
+	curr_llist_ptr = &llist_extern[host][curr_llist_idx];
+	curr_llist_ptr->link_controller_flags = 0x11;
+	curr_llist_ptr->packet_header_count = 14;
+	curr_llist_ptr->packet_data_count = 0;
+
+	curr_llist_ptr->next_packet_pointer = NULL;
+	curr_llist_ptr->packet_data_pointer = NULL;
+	curr_llist_ptr->reserved = 0;
+
+	regacc_pkt_ptr = &curr_llist_ptr->packet_header.register_pkt;
+
+	regacc_pkt_ptr->packet_length = curr_llist_ptr->packet_header_count;
+	regacc_pkt_ptr->packet_type = 146;	/* register access packet */
+	regacc_pkt_ptr->bClient_ID = 0;
+	regacc_pkt_ptr->read_write_info = 0x8001;
+	regacc_pkt_ptr->register_address = reg_addr;
+
+	/* now adjust pointers */
+	mddi_queue_forward_packets(curr_llist_idx, curr_llist_idx, wait,
+				   NULL, host);
+	/* need to check if we can write the pointer or not */
+
+	up(&mddi_host_mutex);
+
+	if (wait) {
+		int wait_ret;
+
+		mddi_linked_list_notify_type *llist_notify_ptr;
+		llist_notify_ptr = &llist_extern_notify[host][curr_llist_idx];
+		wait_ret = wait_for_completion_timeout(
+					&(llist_notify_ptr->done_comp), 5 * HZ);
+
+		if (wait_ret <= 0)
+			ret = -EBUSY;
+
+		if (wait_ret < 0)
+			printk(KERN_ERR "%s: failed to wait for completion!\n",
+				__func__);
+		else if (!wait_ret)
+			printk(KERN_ERR "%s: Timed out waiting!\n", __func__);
+
+		if (!ret && (mddi_reg_read_value_ptr == reg_value_ptr) &&
+			(*reg_value_ptr == -EBUSY)) {
+			printk(KERN_ERR "%s - failed to get data from client",
+				   __func__);
+			mddi_reg_read_value_ptr = NULL;
+			ret = -EBUSY;
+		}
+	}
+
+	MDDI_MSG_DEBUG("Reg Read value=0x%x\n", *reg_value_ptr);
+
+	return ret;
+}				/* mddi_host_register_read */
+
+int mddi_host_register_write(uint32 reg_addr,
+     uint32 reg_val, enum mddi_data_packet_size_type packet_size,
+     boolean wait, mddi_llist_done_cb_type done_cb, mddi_host_type host) {
+	mddi_linked_list_type *curr_llist_ptr;
+	mddi_linked_list_type *curr_llist_dma_ptr;
+	mddi_register_access_packet_type *regacc_pkt_ptr;
+	uint16 curr_llist_idx;
+	int ret = 0;
+
+	if (in_interrupt())
+		MDDI_MSG_CRIT("Called from ISR context\n");
+
+	if (!mddi_host_powered) {
+		MDDI_MSG_ERR("MDDI powered down!\n");
+		mddi_init();
+	}
+
+	down(&mddi_host_mutex);
+
+	curr_llist_idx = mddi_get_next_free_llist_item(host, TRUE);
+	curr_llist_ptr = &llist_extern[host][curr_llist_idx];
+	curr_llist_dma_ptr = &llist_dma_extern[host][curr_llist_idx];
+
+	curr_llist_ptr->link_controller_flags = 1;
+	curr_llist_ptr->packet_header_count = 14;
+	curr_llist_ptr->packet_data_count = 4;
+
+	curr_llist_ptr->next_packet_pointer = NULL;
+	curr_llist_ptr->reserved = 0;
+
+	regacc_pkt_ptr = &curr_llist_ptr->packet_header.register_pkt;
+
+	regacc_pkt_ptr->packet_length = curr_llist_ptr->packet_header_count +
+					(uint16)packet_size;
+	regacc_pkt_ptr->packet_type = 146;	/* register access packet */
+	regacc_pkt_ptr->bClient_ID = 0;
+	regacc_pkt_ptr->read_write_info = 0x0001;
+	regacc_pkt_ptr->register_address = reg_addr;
+	regacc_pkt_ptr->register_data_list[0] = reg_val;
+
+	MDDI_MSG_DEBUG("Reg Access write reg=0x%x, value=0x%x\n",
+		       regacc_pkt_ptr->register_address,
+		       regacc_pkt_ptr->register_data_list[0]);
+
+	regacc_pkt_ptr = &curr_llist_dma_ptr->packet_header.register_pkt;
+	curr_llist_ptr->packet_data_pointer =
+	    (void *)(&regacc_pkt_ptr->register_data_list[0]);
+
+	/* now adjust pointers */
+	mddi_queue_forward_packets(curr_llist_idx, curr_llist_idx, wait,
+				   done_cb, host);
+
+	up(&mddi_host_mutex);
+
+	if (wait) {
+		int wait_ret;
+
+		mddi_linked_list_notify_type *llist_notify_ptr;
+		llist_notify_ptr = &llist_extern_notify[host][curr_llist_idx];
+		wait_ret = wait_for_completion_timeout(
+					&(llist_notify_ptr->done_comp), 5 * HZ);
+
+		if (wait_ret <= 0)
+			ret = -EBUSY;
+
+		if (wait_ret < 0)
+			printk(KERN_ERR "%s: failed to wait for completion!\n",
+				__func__);
+		else if (!wait_ret)
+			printk(KERN_ERR "%s: Timed out waiting!\n", __func__);
+	}
+
+	return ret;
+}				/* mddi_host_register_write */
+
+boolean mddi_host_register_read_int
+    (uint32 reg_addr, uint32 *reg_value_ptr, mddi_host_type host) {
+	mddi_linked_list_type *curr_llist_ptr;
+	mddi_register_access_packet_type *regacc_pkt_ptr;
+	uint16 curr_llist_idx;
+
+	if (!in_interrupt())
+		MDDI_MSG_CRIT("Called from TASK context\n");
+
+	if (!mddi_host_powered) {
+		MDDI_MSG_ERR("MDDI powered down!\n");
+		return FALSE;
+	}
+
+	if (down_trylock(&mddi_host_mutex) != 0)
+		return FALSE;
+
+	mddi_reg_read_value_ptr = reg_value_ptr;
+	curr_llist_idx = mddi_get_reg_read_llist_item(host, FALSE);
+	if (curr_llist_idx == UNASSIGNED_INDEX) {
+		up(&mddi_host_mutex);
+		return FALSE;
+	}
+
+	curr_llist_ptr = &llist_extern[host][curr_llist_idx];
+	curr_llist_ptr->link_controller_flags = 0x11;
+	curr_llist_ptr->packet_header_count = 14;
+	curr_llist_ptr->packet_data_count = 0;
+
+	curr_llist_ptr->next_packet_pointer = NULL;
+	curr_llist_ptr->packet_data_pointer = NULL;
+	curr_llist_ptr->reserved = 0;
+
+	regacc_pkt_ptr = &curr_llist_ptr->packet_header.register_pkt;
+
+	regacc_pkt_ptr->packet_length = curr_llist_ptr->packet_header_count;
+	regacc_pkt_ptr->packet_type = 146;	/* register access packet */
+	regacc_pkt_ptr->bClient_ID = 0;
+	regacc_pkt_ptr->read_write_info = 0x8001;
+	regacc_pkt_ptr->register_address = reg_addr;
+
+	/* now adjust pointers */
+	mddi_queue_forward_packets(curr_llist_idx, curr_llist_idx, FALSE,
+				   NULL, host);
+	/* need to check if we can write the pointer or not */
+
+	up(&mddi_host_mutex);
+
+	return TRUE;
+
+}				/* mddi_host_register_read */
+
+boolean mddi_host_register_write_int
+    (uint32 reg_addr,
+     uint32 reg_val, mddi_llist_done_cb_type done_cb, mddi_host_type host) {
+	mddi_linked_list_type *curr_llist_ptr;
+	mddi_linked_list_type *curr_llist_dma_ptr;
+	mddi_register_access_packet_type *regacc_pkt_ptr;
+	uint16 curr_llist_idx;
+
+	if (!in_interrupt())
+		MDDI_MSG_CRIT("Called from TASK context\n");
+
+	if (!mddi_host_powered) {
+		MDDI_MSG_ERR("MDDI powered down!\n");
+		return FALSE;
+	}
+
+	if (down_trylock(&mddi_host_mutex) != 0)
+		return FALSE;
+
+	curr_llist_idx = mddi_get_next_free_llist_item(host, FALSE);
+	if (curr_llist_idx == UNASSIGNED_INDEX) {
+		up(&mddi_host_mutex);
+		return FALSE;
+	}
+
+	curr_llist_ptr = &llist_extern[host][curr_llist_idx];
+	curr_llist_dma_ptr = &llist_dma_extern[host][curr_llist_idx];
+
+	curr_llist_ptr->link_controller_flags = 1;
+	curr_llist_ptr->packet_header_count = 14;
+	curr_llist_ptr->packet_data_count = 4;
+
+	curr_llist_ptr->next_packet_pointer = NULL;
+	curr_llist_ptr->reserved = 0;
+
+	regacc_pkt_ptr = &curr_llist_ptr->packet_header.register_pkt;
+
+	regacc_pkt_ptr->packet_length = curr_llist_ptr->packet_header_count + 4;
+	regacc_pkt_ptr->packet_type = 146;	/* register access packet */
+	regacc_pkt_ptr->bClient_ID = 0;
+	regacc_pkt_ptr->read_write_info = 0x0001;
+	regacc_pkt_ptr->register_address = reg_addr;
+	regacc_pkt_ptr->register_data_list[0] = reg_val;
+
+	regacc_pkt_ptr = &curr_llist_dma_ptr->packet_header.register_pkt;
+	curr_llist_ptr->packet_data_pointer =
+	    (void *)(&(regacc_pkt_ptr->register_data_list[0]));
+
+	/* now adjust pointers */
+	mddi_queue_forward_packets(curr_llist_idx, curr_llist_idx, FALSE,
+				   done_cb, host);
+	up(&mddi_host_mutex);
+
+	return TRUE;
+
+}				/* mddi_host_register_write */
+
+void mddi_wait(uint16 time_ms)
+{
+	mdelay(time_ms);
+}
+
+void mddi_client_lcd_vsync_detected(boolean detected)
+{
+	if (mddi_lcd.vsync_detected)
+		(*mddi_lcd.vsync_detected) (detected);
+}
+
+/* extended version of function includes done callback */
+void mddi_window_adjust_ext(struct msm_fb_data_type *mfd,
+			    uint16 x1,
+			    uint16 x2,
+			    uint16 y1,
+			    uint16 y2, mddi_llist_done_cb_type done_cb)
+{
+#ifdef FEATURE_MDDI_HITACHI
+	if (mfd->panel.id == HITACHI)
+		mddi_hitachi_window_adjust(x1, x2, y1, y2);
+#elif defined(FEATURE_MDDI_S6D0142)
+	if (mfd->panel.id == MDDI_LCD_S6D0142)
+		mddi_s6d0142_window_adjust(x1, x2, y1, y2, done_cb);
+#else
+	/* Do nothing then... except avoid lint/compiler warnings */
+	(void)x1;
+	(void)x2;
+	(void)y1;
+	(void)y2;
+	(void)done_cb;
+#endif
+}
+
+void mddi_window_adjust(struct msm_fb_data_type *mfd,
+			uint16 x1, uint16 x2, uint16 y1, uint16 y2)
+{
+	mddi_window_adjust_ext(mfd, x1, x2, y1, y2, NULL);
+}
+
+#ifdef MDDI_HOST_WINDOW_WORKAROUND
+uint16 mddi_assign_pkt_height(uint16 pkt_width,
+	uint16 pkt_height, uint16 bpp)
+{
+	uint16 new_pkt_height;
+	uint16 problem_height = 0;
+
+	if (pkt_width <= 240) {
+		if (bpp == 16)
+			problem_height = error_vals_16bpp[pkt_width-1];
+		else if (bpp == 18)
+			problem_height = error_vals_18bpp[pkt_width-1];
+		else {
+			printk(KERN_ERR"Invalid bpp value");
+			return -EINVAL;
+		}
+	}
+	if (problem_height == pkt_height)
+		new_pkt_height = problem_height - 1;
+	else
+		new_pkt_height = pkt_height;
+
+	return new_pkt_height;
+}
+#endif
+
+#ifdef ENABLE_MDDI_MULTI_READ_WRITE
+int mddi_host_register_multiwrite(uint32 reg_addr,
+	uint32 *value_list_ptr,
+	uint32 value_count, boolean wait, mddi_llist_done_cb_type done_cb,
+	mddi_host_type host)
+{
+	mddi_linked_list_type *curr_llist_ptr;
+	mddi_linked_list_type *curr_llist_dma_ptr;
+	mddi_register_access_packet_type *regacc_pkt_ptr;
+	uint16 curr_llist_idx;
+	int ret = 0;
+
+	if (!value_list_ptr || !value_count ||
+		value_count > MDDI_HOST_MAX_CLIENT_REG_IN_SAME_ADDR) {
+		MDDI_MSG_ERR("\n Invalid value_list or value_count");
+		return -EINVAL;
+	}
+
+	if (in_interrupt())
+		MDDI_MSG_CRIT("Called from ISR context\n");
+
+	if (!mddi_host_powered) {
+		MDDI_MSG_ERR("MDDI powered down!\n");
+		mddi_init();
+	}
+
+	down(&mddi_host_mutex);
+
+	curr_llist_idx = mddi_get_next_free_llist_item(host, TRUE);
+	curr_llist_ptr = &llist_extern[host][curr_llist_idx];
+	curr_llist_dma_ptr = &llist_dma_extern[host][curr_llist_idx];
+
+	curr_llist_ptr->link_controller_flags = 1;
+	curr_llist_ptr->packet_header_count = 14;
+	curr_llist_ptr->packet_data_count =
+		(uint16)(value_count * 4);
+
+	curr_llist_ptr->next_packet_pointer = NULL;
+	curr_llist_ptr->reserved = 0;
+
+	regacc_pkt_ptr = &curr_llist_ptr->packet_header.register_pkt;
+
+	regacc_pkt_ptr->packet_length = curr_llist_ptr->packet_header_count
+		+ curr_llist_ptr->packet_data_count;
+	regacc_pkt_ptr->packet_type = 146;	/* register access packet */
+	regacc_pkt_ptr->bClient_ID = 0;
+	regacc_pkt_ptr->read_write_info = value_count;
+	regacc_pkt_ptr->register_address = reg_addr;
+	memcpy((void *)&regacc_pkt_ptr->register_data_list[0], value_list_ptr,
+		   curr_llist_ptr->packet_data_count);
+
+	regacc_pkt_ptr = &curr_llist_dma_ptr->packet_header.register_pkt;
+	curr_llist_ptr->packet_data_pointer =
+		(void *)(&regacc_pkt_ptr->register_data_list[0]);
+	MDDI_MSG_DEBUG("MultiReg Access write reg=0x%x, value[0]=0x%x\n",
+		       regacc_pkt_ptr->register_address,
+		       regacc_pkt_ptr->register_data_list[0]);
+
+	/* now adjust pointers */
+	mddi_queue_forward_packets(curr_llist_idx, curr_llist_idx, wait,
+				   done_cb, host);
+
+	up(&mddi_host_mutex);
+
+	if (wait) {
+		int wait_ret;
+
+		mddi_linked_list_notify_type *llist_notify_ptr;
+		llist_notify_ptr = &llist_extern_notify[host][curr_llist_idx];
+		wait_ret = wait_for_completion_timeout(
+					&(llist_notify_ptr->done_comp), 5 * HZ);
+
+		if (wait_ret <= 0)
+			ret = -EBUSY;
+
+		if (wait_ret < 0)
+			printk(KERN_ERR "%s: failed to wait for completion!\n",
+				__func__);
+		else if (!wait_ret)
+			printk(KERN_ERR "%s: Timed out waiting!\n", __func__);
+	}
+
+	return ret;
+}
+
+int mddi_host_register_multiread(uint32 reg_addr,
+	uint32 *value_list_ptr, uint32 value_count,
+	boolean wait, mddi_host_type host) {
+	mddi_linked_list_type *curr_llist_ptr;
+	mddi_register_access_packet_type *regacc_pkt_ptr;
+	uint16 curr_llist_idx;
+	int ret = 0;
+
+	if (!value_list_ptr || !value_count ||
+		value_count >= MDDI_HOST_MAX_CLIENT_REG_IN_SAME_ADDR) {
+		MDDI_MSG_ERR("\n Invalid value_list or value_count");
+		return -EINVAL;
+	}
+
+	if (in_interrupt())
+		MDDI_MSG_CRIT("Called from ISR context\n");
+
+	if (!mddi_host_powered) {
+		MDDI_MSG_ERR("MDDI powered down!\n");
+		mddi_init();
+	}
+
+	down(&mddi_host_mutex);
+
+	mddi_reg_read_value_ptr = value_list_ptr;
+	curr_llist_idx = mddi_get_reg_read_llist_item(host, TRUE);
+	if (curr_llist_idx == UNASSIGNED_INDEX) {
+		up(&mddi_host_mutex);
+
+		/* need to change this to some sort of wait */
+		MDDI_MSG_ERR("Attempting to queue up more than 1 reg read\n");
+		return -EINVAL;
+	}
+
+	curr_llist_ptr = &llist_extern[host][curr_llist_idx];
+	curr_llist_ptr->link_controller_flags = 0x11;
+	curr_llist_ptr->packet_header_count = 14;
+	curr_llist_ptr->packet_data_count = 0;
+
+	curr_llist_ptr->next_packet_pointer = NULL;
+	curr_llist_ptr->packet_data_pointer = NULL;
+	curr_llist_ptr->reserved = 0;
+
+	regacc_pkt_ptr = &curr_llist_ptr->packet_header.register_pkt;
+
+	regacc_pkt_ptr->packet_length = curr_llist_ptr->packet_header_count;
+	regacc_pkt_ptr->packet_type = 146;	/* register access packet */
+	regacc_pkt_ptr->bClient_ID = 0;
+	regacc_pkt_ptr->read_write_info = 0x8000 | value_count;
+	regacc_pkt_ptr->register_address = reg_addr;
+
+	/* now adjust pointers */
+	mddi_queue_forward_packets(curr_llist_idx, curr_llist_idx, wait,
+				   NULL, host);
+	/* need to check if we can write the pointer or not */
+
+	up(&mddi_host_mutex);
+
+	if (wait) {
+		int wait_ret;
+
+		mddi_linked_list_notify_type *llist_notify_ptr;
+		llist_notify_ptr = &llist_extern_notify[host][curr_llist_idx];
+		wait_ret = wait_for_completion_timeout(
+					&(llist_notify_ptr->done_comp), 5 * HZ);
+
+		if (wait_ret <= 0)
+			ret = -EBUSY;
+
+		if (wait_ret < 0)
+			printk(KERN_ERR "%s: failed to wait for completion!\n",
+				__func__);
+		else if (!wait_ret)
+			printk(KERN_ERR "%s: Timed out waiting!\n", __func__);
+
+		if (!ret && (mddi_reg_read_value_ptr == value_list_ptr) &&
+			(*value_list_ptr == -EBUSY)) {
+			printk(KERN_ERR "%s - failed to get data from client",
+				   __func__);
+			mddi_reg_read_value_ptr = NULL;
+			ret = -EBUSY;
+		}
+	}
+
+	MDDI_MSG_DEBUG("MultiReg Read value[0]=0x%x\n", *value_list_ptr);
+
+	return ret;
+}
+#endif
diff --git a/drivers/video/msm/mddihost.h b/drivers/video/msm/mddihost.h
new file mode 100644
index 0000000..52bc67c
--- /dev/null
+++ b/drivers/video/msm/mddihost.h
@@ -0,0 +1,231 @@
+/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef MDDIHOST_H
+#define MDDIHOST_H
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include "linux/proc_fs.h"
+#include <linux/types.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+
+#include <mach/hardware.h>
+#include <linux/io.h>
+
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <linux/types.h>
+#include <linux/dma-mapping.h>
+
+#include "msm_fb_panel.h"
+
+#undef FEATURE_MDDI_MC4
+#undef FEATURE_MDDI_S6D0142
+#undef FEATURE_MDDI_HITACHI
+#define FEATURE_MDDI_SHARP
+#define FEATURE_MDDI_TOSHIBA
+#undef FEATURE_MDDI_E751
+#define FEATURE_MDDI_CORONA
+#define FEATURE_MDDI_PRISM
+
+#define T_MSM7500
+
+typedef enum {
+	format_16bpp,
+	format_18bpp,
+	format_24bpp
+} mddi_video_format;
+
+typedef enum {
+	MDDI_LCD_NONE = 0,
+	MDDI_LCD_MC4,
+	MDDI_LCD_S6D0142,
+	MDDI_LCD_SHARP,
+	MDDI_LCD_E751,
+	MDDI_LCD_CORONA,
+	MDDI_LCD_HITACHI,
+	MDDI_LCD_TOSHIBA,
+	MDDI_LCD_PRISM,
+	MDDI_LCD_TP2,
+	MDDI_NUM_LCD_TYPES,
+	MDDI_LCD_DEFAULT = MDDI_LCD_TOSHIBA
+} mddi_lcd_type;
+
+typedef enum {
+	MDDI_HOST_PRIM = 0,
+	MDDI_HOST_EXT,
+	MDDI_NUM_HOST_CORES
+} mddi_host_type;
+
+typedef enum {
+	MDDI_DRIVER_RESET,	/* host core registers have not been written. */
+	MDDI_DRIVER_DISABLED,	/* registers written, interrupts disabled. */
+	MDDI_DRIVER_ENABLED	/* registers written, interrupts enabled. */
+} mddi_host_driver_state_type;
+
+typedef enum {
+	MDDI_GPIO_INT_0 = 0,
+	MDDI_GPIO_INT_1,
+	MDDI_GPIO_INT_2,
+	MDDI_GPIO_INT_3,
+	MDDI_GPIO_INT_4,
+	MDDI_GPIO_INT_5,
+	MDDI_GPIO_INT_6,
+	MDDI_GPIO_INT_7,
+	MDDI_GPIO_INT_8,
+	MDDI_GPIO_INT_9,
+	MDDI_GPIO_INT_10,
+	MDDI_GPIO_INT_11,
+	MDDI_GPIO_INT_12,
+	MDDI_GPIO_INT_13,
+	MDDI_GPIO_INT_14,
+	MDDI_GPIO_INT_15,
+	MDDI_GPIO_NUM_INTS
+} mddi_gpio_int_type;
+
+enum mddi_data_packet_size_type {
+	MDDI_DATA_PACKET_4_BYTES  = 4,
+	MDDI_DATA_PACKET_8_BYTES  = 8,
+	MDDI_DATA_PACKET_12_BYTES = 12,
+	MDDI_DATA_PACKET_16_BYTES = 16,
+	MDDI_DATA_PACKET_24_BYTES = 24
+};
+
+typedef struct {
+	uint32 addr;
+	uint32 value;
+} mddi_reg_write_type;
+
+boolean mddi_vsync_set_handler(msm_fb_vsync_handler_type handler, void *arg);
+
+typedef void (*mddi_llist_done_cb_type) (void);
+
+typedef void (*mddi_rev_handler_type) (void *);
+
+boolean mddi_set_rev_handler(mddi_rev_handler_type handler, uint16 pkt_type);
+
+#define MDDI_DEFAULT_PRIM_PIX_ATTR 0xC3
+#define MDDI_DEFAULT_SECD_PIX_ATTR 0xC0
+
+typedef int gpio_int_polarity_type;
+typedef int gpio_int_handler_type;
+
+typedef struct {
+	void (*vsync_detected) (boolean);
+} mddi_lcd_func_type;
+
+extern mddi_lcd_func_type mddi_lcd;
+extern int irq_enabled;
+extern unsigned char mddi_timer_shutdown_flag;
+extern struct mutex mddi_timer_lock;
+
+void mddi_init(void);
+void mddi_powerdown(void);
+
+void mddi_host_start_ext_display(void);
+void mddi_host_stop_ext_display(void);
+
+extern spinlock_t mddi_host_spin_lock;
+#ifdef T_MSM7500
+void mddi_reset(void);
+#ifdef FEATURE_DUAL_PROC_MODEM_DISPLAY
+void mddi_host_switch_proc_control(boolean on);
+#endif
+#endif
+void mddi_host_exit_power_collapse(void);
+
+void mddi_queue_splash_screen
+    (void *buf_ptr,
+     boolean clear_area,
+     int16 src_width,
+     int16 src_starting_row,
+     int16 src_starting_column,
+     int16 num_of_rows,
+     int16 num_of_columns, int16 dst_starting_row, int16 dst_starting_column);
+
+void mddi_queue_image
+    (void *buf_ptr,
+     uint8 stereo_video,
+     boolean clear_area,
+     int16 src_width,
+     int16 src_starting_row,
+     int16 src_starting_column,
+     int16 num_of_rows,
+     int16 num_of_columns, int16 dst_starting_row, int16 dst_starting_column);
+
+int mddi_host_register_read
+    (uint32 reg_addr,
+     uint32 *reg_value_ptr, boolean wait, mddi_host_type host_idx);
+int mddi_host_register_write
+    (uint32 reg_addr, uint32 reg_val,
+     enum mddi_data_packet_size_type packet_size,
+     boolean wait, mddi_llist_done_cb_type done_cb, mddi_host_type host);
+boolean mddi_host_register_write_int
+    (uint32 reg_addr,
+     uint32 reg_val, mddi_llist_done_cb_type done_cb, mddi_host_type host);
+boolean mddi_host_register_read_int
+    (uint32 reg_addr, uint32 *reg_value_ptr, mddi_host_type host_idx);
+void mddi_queue_register_write_static
+    (uint32 reg_addr,
+     uint32 reg_val, boolean wait, mddi_llist_done_cb_type done_cb);
+void mddi_queue_static_window_adjust
+    (const mddi_reg_write_type *reg_write,
+     uint16 num_writes, mddi_llist_done_cb_type done_cb);
+
+#ifdef ENABLE_MDDI_MULTI_READ_WRITE
+int mddi_host_register_multiwrite(uint32 reg_addr,
+	uint32 *value_list_ptr, uint32 value_count,
+    boolean wait, mddi_llist_done_cb_type done_cb,
+	mddi_host_type host);
+int mddi_host_register_multiread(uint32 reg_addr,
+	uint32 *value_list_ptr, uint32 value_count,
+	boolean wait, mddi_host_type host);
+#endif
+
+#define mddi_queue_register_read(reg, val_ptr, wait, sig) \
+	mddi_host_register_read(reg, val_ptr, wait, MDDI_HOST_PRIM)
+#define mddi_queue_register_write(reg, val, wait, sig) \
+	mddi_host_register_write(reg, val, MDDI_DATA_PACKET_4_BYTES,\
+	wait, NULL, MDDI_HOST_PRIM)
+#define mddi_queue_register_write_extn(reg, val, pkt_size, wait, sig) \
+	mddi_host_register_write(reg, val, pkt_size, \
+	wait, NULL, MDDI_HOST_PRIM)
+#define mddi_queue_register_write_int(reg, val) \
+	mddi_host_register_write_int(reg, val, NULL, MDDI_HOST_PRIM)
+#define mddi_queue_register_read_int(reg, val_ptr) \
+	mddi_host_register_read_int(reg, val_ptr, MDDI_HOST_PRIM)
+#define mddi_queue_register_writes(reg_ptr, val, wait, sig) \
+	mddi_host_register_writes(reg_ptr, val, wait, sig, MDDI_HOST_PRIM)
+
+void mddi_wait(uint16 time_ms);
+void mddi_assign_max_pkt_dimensions(uint16 image_cols,
+				    uint16 image_rows,
+				    uint16 bpp,
+				    uint16 *max_cols, uint16 * max_rows);
+#ifdef MDDI_HOST_WINDOW_WORKAROUND
+uint16 mddi_assign_pkt_height(uint16 pkt_width, uint16 pkt_height, uint16 bpp);
+#endif
+void mddi_queue_reverse_encapsulation(boolean wait);
+int mddi_client_power(unsigned int client_id);
+void mddi_disable(int lock);
+void mddi_window_adjust(struct msm_fb_data_type *mfd,
+	uint16 x1, uint16 x2, uint16 y1, uint16 y2);
+void mddi_send_fw_link_skew_cal(mddi_host_type host_idx);
+int pmdh_clk_func(int enable);
+
+#endif /* MDDIHOST_H */
diff --git a/drivers/video/msm/mddihost_e.c b/drivers/video/msm/mddihost_e.c
new file mode 100644
index 0000000..d53aa6f
--- /dev/null
+++ b/drivers/video/msm/mddihost_e.c
@@ -0,0 +1,59 @@
+/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+
+#include "msm_fb.h"
+#include "mddihost.h"
+#include "mddihosti.h"
+
+#include <linux/clk.h>
+#include <mach/clk.h>
+
+extern struct semaphore mddi_host_mutex;
+static boolean mddi_host_ext_powered = FALSE;
+
+void mddi_host_start_ext_display(void)
+{
+	down(&mddi_host_mutex);
+
+	if (!mddi_host_ext_powered) {
+		mddi_host_init(MDDI_HOST_EXT);
+
+		mddi_host_ext_powered = TRUE;
+	}
+
+	up(&mddi_host_mutex);
+}
+
+void mddi_host_stop_ext_display(void)
+{
+	down(&mddi_host_mutex);
+
+	if (mddi_host_ext_powered) {
+		mddi_host_powerdown(MDDI_HOST_EXT);
+
+		mddi_host_ext_powered = FALSE;
+	}
+
+	up(&mddi_host_mutex);
+}
diff --git a/drivers/video/msm/mddihosti.c b/drivers/video/msm/mddihosti.c
new file mode 100644
index 0000000..1a5a3fd
--- /dev/null
+++ b/drivers/video/msm/mddihosti.c
@@ -0,0 +1,2268 @@
+/* Copyright (c) 2008-2010, 2012 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+
+#include "msm_fb_panel.h"
+#include "mddihost.h"
+#include "mddihosti.h"
+
+#define FEATURE_MDDI_UNDERRUN_RECOVERY
+#ifndef FEATURE_MDDI_DISABLE_REVERSE
+static void mddi_read_rev_packet(byte *data_ptr);
+#endif
+
+struct timer_list mddi_host_timer;
+
+#define MDDI_DEFAULT_TIMER_LENGTH 5000	/* 5 seconds */
+uint32 mddi_rtd_frequency = 60000;	/* send RTD every 60 seconds */
+uint32 mddi_client_status_frequency = 60000;	/* get status pkt every 60 secs */
+
+boolean mddi_vsync_detect_enabled = FALSE;
+mddi_gpio_info_type mddi_gpio;
+
+uint32 mddi_host_core_version;
+boolean mddi_debug_log_statistics = FALSE;
+/* #define FEATURE_MDDI_HOST_ENABLE_EARLY_HIBERNATION */
+/* default to TRUE in case MDP does not vote */
+static boolean mddi_host_mdp_active_flag = TRUE;
+static uint32 mddi_log_stats_counter;
+uint32 mddi_log_stats_frequency = 4000;
+int32 mddi_client_type;
+
+#define MDDI_DEFAULT_REV_PKT_SIZE            0x20
+
+#ifndef FEATURE_MDDI_DISABLE_REVERSE
+static boolean mddi_rev_ptr_workaround = TRUE;
+static uint32 mddi_reg_read_retry;
+static uint32 mddi_reg_read_retry_max = 20;
+static boolean mddi_enable_reg_read_retry = TRUE;
+static boolean mddi_enable_reg_read_retry_once = FALSE;
+
+#define MDDI_MAX_REV_PKT_SIZE                0x60
+
+#define MDDI_CLIENT_CAPABILITY_REV_PKT_SIZE  0x60
+
+#define MDDI_VIDEO_REV_PKT_SIZE              0x40
+#define MDDI_REV_BUFFER_SIZE  MDDI_MAX_REV_PKT_SIZE
+static byte rev_packet_data[MDDI_MAX_REV_PKT_SIZE];
+#endif /* FEATURE_MDDI_DISABLE_REVERSE */
+/* leave these variables so graphics will compile */
+
+#define MDDI_MAX_REV_DATA_SIZE  128
+/*lint -d__align(x) */
+boolean mddi_debug_clear_rev_data = TRUE;
+
+uint32 *mddi_reg_read_value_ptr;
+
+mddi_client_capability_type mddi_client_capability_pkt;
+static boolean mddi_client_capability_request = FALSE;
+
+#ifndef FEATURE_MDDI_DISABLE_REVERSE
+
+#define MAX_MDDI_REV_HANDLERS 2
+#define INVALID_PKT_TYPE 0xFFFF
+
+typedef struct {
+	mddi_rev_handler_type handler;	/* ISR to be executed */
+	uint16 pkt_type;
+} mddi_rev_pkt_handler_type;
+static mddi_rev_pkt_handler_type mddi_rev_pkt_handler[MAX_MDDI_REV_HANDLERS] =
+    { {NULL, INVALID_PKT_TYPE}, {NULL, INVALID_PKT_TYPE} };
+
+static boolean mddi_rev_encap_user_request = FALSE;
+static mddi_linked_list_notify_type mddi_rev_user;
+
+spinlock_t mddi_host_spin_lock;
+extern uint32 mdp_in_processing;
+#endif
+
+typedef enum {
+	MDDI_REV_IDLE
+#ifndef FEATURE_MDDI_DISABLE_REVERSE
+	    , MDDI_REV_REG_READ_ISSUED,
+	MDDI_REV_REG_READ_SENT,
+	MDDI_REV_ENCAP_ISSUED,
+	MDDI_REV_STATUS_REQ_ISSUED,
+	MDDI_REV_CLIENT_CAP_ISSUED
+#endif
+} mddi_rev_link_state_type;
+
+typedef enum {
+	MDDI_LINK_DISABLED,
+	MDDI_LINK_HIBERNATING,
+	MDDI_LINK_ACTIVATING,
+	MDDI_LINK_ACTIVE
+} mddi_host_link_state_type;
+
+typedef struct {
+	uint32 count;
+	uint32 in_count;
+	uint32 disp_req_count;
+	uint32 state_change_count;
+	uint32 ll_done_count;
+	uint32 rev_avail_count;
+	uint32 error_count;
+	uint32 rev_encap_count;
+	uint32 llist_ptr_write_1;
+	uint32 llist_ptr_write_2;
+} mddi_host_int_type;
+
+typedef struct {
+	uint32 fwd_crc_count;
+	uint32 rev_crc_count;
+	uint32 pri_underflow;
+	uint32 sec_underflow;
+	uint32 rev_overflow;
+	uint32 pri_overwrite;
+	uint32 sec_overwrite;
+	uint32 rev_overwrite;
+	uint32 dma_failure;
+	uint32 rtd_failure;
+	uint32 reg_read_failure;
+#ifdef FEATURE_MDDI_UNDERRUN_RECOVERY
+	uint32 pri_underrun_detected;
+#endif
+} mddi_host_stat_type;
+
+typedef struct {
+	uint32 rtd_cnt;
+	uint32 rev_enc_cnt;
+	uint32 vid_cnt;
+	uint32 reg_acc_cnt;
+	uint32 cli_stat_cnt;
+	uint32 cli_cap_cnt;
+	uint32 reg_read_cnt;
+	uint32 link_active_cnt;
+	uint32 link_hibernate_cnt;
+	uint32 vsync_response_cnt;
+	uint32 fwd_crc_cnt;
+	uint32 rev_crc_cnt;
+} mddi_log_params_struct_type;
+
+typedef struct {
+	uint32 rtd_value;
+	uint32 rtd_counter;
+	uint32 client_status_cnt;
+	boolean rev_ptr_written;
+	uint8 *rev_ptr_start;
+	uint8 *rev_ptr_curr;
+	uint32 mddi_rev_ptr_write_val;
+	dma_addr_t rev_data_dma_addr;
+	uint16 rev_pkt_size;
+	mddi_rev_link_state_type rev_state;
+	mddi_host_link_state_type link_state;
+	mddi_host_driver_state_type driver_state;
+	boolean disable_hibernation;
+	uint32 saved_int_reg;
+	uint32 saved_int_en;
+	mddi_linked_list_type *llist_ptr;
+	dma_addr_t llist_dma_addr;
+	mddi_linked_list_type *llist_dma_ptr;
+	uint32 *rev_data_buf;
+	struct completion mddi_llist_avail_comp;
+	boolean mddi_waiting_for_llist_avail;
+	mddi_host_int_type int_type;
+	mddi_host_stat_type stats;
+	mddi_log_params_struct_type log_parms;
+	mddi_llist_info_type llist_info;
+	mddi_linked_list_notify_type llist_notify[MDDI_MAX_NUM_LLIST_ITEMS];
+} mddi_host_cntl_type;
+
+static mddi_host_type mddi_curr_host = MDDI_HOST_PRIM;
+static mddi_host_cntl_type mhctl[MDDI_NUM_HOST_CORES];
+mddi_linked_list_type *llist_extern[MDDI_NUM_HOST_CORES];
+mddi_linked_list_type *llist_dma_extern[MDDI_NUM_HOST_CORES];
+mddi_linked_list_notify_type *llist_extern_notify[MDDI_NUM_HOST_CORES];
+static mddi_log_params_struct_type prev_parms[MDDI_NUM_HOST_CORES];
+
+extern uint32 mdp_total_vdopkts;
+
+static boolean mddi_host_io_clock_on = FALSE;
+static boolean mddi_host_hclk_on = FALSE;
+
+int int_mddi_pri_flag = FALSE;
+int int_mddi_ext_flag = FALSE;
+
+static void mddi_report_errors(uint32 int_reg)
+{
+	mddi_host_type host_idx = mddi_curr_host;
+	mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]);
+
+	if (int_reg & MDDI_INT_PRI_UNDERFLOW) {
+		pmhctl->stats.pri_underflow++;
+		MDDI_MSG_ERR("!!! MDDI Primary Underflow !!!\n");
+	}
+	if (int_reg & MDDI_INT_SEC_UNDERFLOW) {
+		pmhctl->stats.sec_underflow++;
+		MDDI_MSG_ERR("!!! MDDI Secondary Underflow !!!\n");
+	}
+#ifndef FEATURE_MDDI_DISABLE_REVERSE
+	if (int_reg & MDDI_INT_REV_OVERFLOW) {
+		pmhctl->stats.rev_overflow++;
+		MDDI_MSG_ERR("!!! MDDI Reverse Overflow !!!\n");
+		pmhctl->rev_ptr_curr = pmhctl->rev_ptr_start;
+		mddi_host_reg_out(REV_PTR, pmhctl->mddi_rev_ptr_write_val);
+
+	}
+	if (int_reg & MDDI_INT_CRC_ERROR)
+		MDDI_MSG_ERR("!!! MDDI Reverse CRC Error !!!\n");
+#endif
+	if (int_reg & MDDI_INT_PRI_OVERWRITE) {
+		pmhctl->stats.pri_overwrite++;
+		MDDI_MSG_ERR("!!! MDDI Primary Overwrite !!!\n");
+	}
+	if (int_reg & MDDI_INT_SEC_OVERWRITE) {
+		pmhctl->stats.sec_overwrite++;
+		MDDI_MSG_ERR("!!! MDDI Secondary Overwrite !!!\n");
+	}
+#ifndef FEATURE_MDDI_DISABLE_REVERSE
+	if (int_reg & MDDI_INT_REV_OVERWRITE) {
+		pmhctl->stats.rev_overwrite++;
+		/* This will show up normally and is not a problem */
+		MDDI_MSG_DEBUG("MDDI Reverse Overwrite!\n");
+	}
+	if (int_reg & MDDI_INT_RTD_FAILURE) {
+		mddi_host_reg_outm(INTEN, MDDI_INT_RTD_FAILURE, 0);
+		pmhctl->stats.rtd_failure++;
+		MDDI_MSG_ERR("!!! MDDI RTD Failure !!!\n");
+	}
+#endif
+	if (int_reg & MDDI_INT_DMA_FAILURE) {
+		pmhctl->stats.dma_failure++;
+		MDDI_MSG_ERR("!!! MDDI DMA Abort !!!\n");
+	}
+}
+
+static void mddi_host_enable_io_clock(void)
+{
+	if (!MDDI_HOST_IS_IO_CLOCK_ON)
+		MDDI_HOST_ENABLE_IO_CLOCK;
+}
+
+static void mddi_host_enable_hclk(void)
+{
+
+	if (!MDDI_HOST_IS_HCLK_ON)
+		MDDI_HOST_ENABLE_HCLK;
+}
+
+static void mddi_host_disable_io_clock(void)
+{
+#ifndef FEATURE_MDDI_HOST_IO_CLOCK_CONTROL_DISABLE
+	if (MDDI_HOST_IS_IO_CLOCK_ON)
+		MDDI_HOST_DISABLE_IO_CLOCK;
+#endif
+}
+
+static void mddi_host_disable_hclk(void)
+{
+#ifndef FEATURE_MDDI_HOST_HCLK_CONTROL_DISABLE
+	if (MDDI_HOST_IS_HCLK_ON)
+		MDDI_HOST_DISABLE_HCLK;
+#endif
+}
+
+static void mddi_vote_to_sleep(mddi_host_type host_idx, boolean sleep)
+{
+	uint16 vote_mask;
+
+	if (host_idx == MDDI_HOST_PRIM)
+		vote_mask = 0x01;
+	else
+		vote_mask = 0x02;
+}
+
+static void mddi_report_state_change(uint32 int_reg)
+{
+	mddi_host_type host_idx = mddi_curr_host;
+	mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]);
+
+	if ((pmhctl->saved_int_reg & MDDI_INT_IN_HIBERNATION) &&
+	    (pmhctl->saved_int_reg & MDDI_INT_LINK_ACTIVE)) {
+		/* recover from condition where the io_clock was turned off by the
+		   clock driver during a transition to hibernation. The io_clock
+		   disable is to prevent MDP/MDDI underruns when changing ARM
+		   clock speeds. In the process of halting the ARM, the hclk
+		   divider needs to be set to 1. When it is set to 1, there is
+		   a small time (usecs) when hclk is off or slow, and this can
+		   cause an underrun. To prevent the underrun, clock driver turns
+		   off the MDDI io_clock before making the change. */
+		mddi_host_reg_out(CMD, MDDI_CMD_POWERUP);
+	}
+
+	if (int_reg & MDDI_INT_LINK_ACTIVE) {
+		pmhctl->link_state = MDDI_LINK_ACTIVE;
+		pmhctl->log_parms.link_active_cnt++;
+		pmhctl->rtd_value = mddi_host_reg_in(RTD_VAL);
+		MDDI_MSG_DEBUG("!!! MDDI Active RTD:0x%x!!!\n",
+			       pmhctl->rtd_value);
+		/* now interrupt on hibernation */
+		mddi_host_reg_outm(INTEN,
+				   (MDDI_INT_IN_HIBERNATION |
+				    MDDI_INT_LINK_ACTIVE),
+				   MDDI_INT_IN_HIBERNATION);
+
+#ifdef DEBUG_MDDIHOSTI
+		/* if gpio interrupt is enabled, start polling at fastest
+		 * registered rate
+		 */
+		if (mddi_gpio.polling_enabled) {
+			timer_reg(&mddi_gpio_poll_timer,
+		mddi_gpio_poll_timer_cb, 0, mddi_gpio.polling_interval, 0);
+		}
+#endif
+#ifndef FEATURE_MDDI_DISABLE_REVERSE
+		if (mddi_rev_ptr_workaround) {
+			/* HW CR: need to reset reverse register stuff */
+			pmhctl->rev_ptr_written = FALSE;
+			pmhctl->rev_ptr_curr = pmhctl->rev_ptr_start;
+		}
+#endif
+		/* vote on sleep */
+		mddi_vote_to_sleep(host_idx, FALSE);
+
+		if (host_idx == MDDI_HOST_PRIM) {
+			if (mddi_vsync_detect_enabled) {
+				/*
+				 * Indicate to client specific code that vsync
+				 * was enabled, but we did not detect a client
+				 * intiated wakeup. The client specific
+				 * handler can either reassert vsync detection,
+				 * or treat this as a valid vsync.
+				 */
+				mddi_client_lcd_vsync_detected(FALSE);
+				pmhctl->log_parms.vsync_response_cnt++;
+			}
+		}
+	}
+	if (int_reg & MDDI_INT_IN_HIBERNATION) {
+		pmhctl->link_state = MDDI_LINK_HIBERNATING;
+		pmhctl->log_parms.link_hibernate_cnt++;
+		MDDI_MSG_DEBUG("!!! MDDI Hibernating !!!\n");
+
+		if (mddi_client_type == 2) {
+			mddi_host_reg_out(PAD_CTL, 0x402a850f);
+			mddi_host_reg_out(PAD_CAL, 0x10220020);
+			mddi_host_reg_out(TA1_LEN, 0x0010);
+			mddi_host_reg_out(TA2_LEN, 0x0040);
+		}
+		/* now interrupt on link_active */
+#ifdef FEATURE_MDDI_DISABLE_REVERSE
+		mddi_host_reg_outm(INTEN,
+				   (MDDI_INT_MDDI_IN |
+				    MDDI_INT_IN_HIBERNATION |
+				    MDDI_INT_LINK_ACTIVE),
+				   MDDI_INT_LINK_ACTIVE);
+#else
+		mddi_host_reg_outm(INTEN,
+				   (MDDI_INT_MDDI_IN |
+				    MDDI_INT_IN_HIBERNATION |
+				    MDDI_INT_LINK_ACTIVE),
+				   (MDDI_INT_MDDI_IN | MDDI_INT_LINK_ACTIVE));
+
+		pmhctl->rtd_counter = mddi_rtd_frequency;
+
+		if (pmhctl->rev_state != MDDI_REV_IDLE) {
+			/* a rev_encap will not wake up the link, so we do that here */
+			pmhctl->link_state = MDDI_LINK_ACTIVATING;
+			mddi_host_reg_out(CMD, MDDI_CMD_LINK_ACTIVE);
+		}
+#endif
+
+		if (pmhctl->disable_hibernation) {
+			mddi_host_reg_out(CMD, MDDI_CMD_HIBERNATE);
+			mddi_host_reg_out(CMD, MDDI_CMD_LINK_ACTIVE);
+			pmhctl->link_state = MDDI_LINK_ACTIVATING;
+		}
+#ifdef FEATURE_MDDI_UNDERRUN_RECOVERY
+		if ((pmhctl->llist_info.transmitting_start_idx !=
+		     UNASSIGNED_INDEX)
+		    &&
+		    ((pmhctl->
+		      saved_int_reg & (MDDI_INT_PRI_LINK_LIST_DONE |
+				       MDDI_INT_PRI_PTR_READ)) ==
+		     MDDI_INT_PRI_PTR_READ)) {
+			mddi_linked_list_type *llist_dma;
+			llist_dma = pmhctl->llist_dma_ptr;
+			/*
+			 * All indications are that we have not received a
+			 * linked list done interrupt, due to an underrun
+			 * condition. Recovery attempt is to send again.
+			 */
+			dma_coherent_pre_ops();
+			/* Write to primary pointer register again */
+			mddi_host_reg_out(PRI_PTR,
+					  &llist_dma[pmhctl->llist_info.
+						     transmitting_start_idx]);
+			pmhctl->stats.pri_underrun_detected++;
+		}
+#endif
+
+		/* vote on sleep */
+		if (pmhctl->link_state == MDDI_LINK_HIBERNATING) {
+			mddi_vote_to_sleep(host_idx, TRUE);
+		}
+
+#ifdef DEBUG_MDDIHOSTI
+		/* need to stop polling timer */
+		if (mddi_gpio.polling_enabled) {
+			(void) timer_clr(&mddi_gpio_poll_timer, T_NONE);
+		}
+#endif
+	}
+}
+
+void mddi_host_timer_service(unsigned long data)
+{
+#ifndef FEATURE_MDDI_DISABLE_REVERSE
+	unsigned long flags;
+#endif
+	mddi_host_type host_idx;
+	mddi_host_cntl_type *pmhctl;
+
+	unsigned long time_ms = MDDI_DEFAULT_TIMER_LENGTH;
+	init_timer(&mddi_host_timer);
+	for (host_idx = MDDI_HOST_PRIM; host_idx < MDDI_NUM_HOST_CORES;
+	     host_idx++) {
+		pmhctl = &(mhctl[host_idx]);
+		mddi_log_stats_counter += (uint32) time_ms;
+#ifndef FEATURE_MDDI_DISABLE_REVERSE
+		pmhctl->rtd_counter += (uint32) time_ms;
+		pmhctl->client_status_cnt += (uint32) time_ms;
+
+		if (host_idx == MDDI_HOST_PRIM) {
+			if (pmhctl->client_status_cnt >=
+			    mddi_client_status_frequency) {
+				if ((pmhctl->link_state ==
+				     MDDI_LINK_HIBERNATING)
+				    && (pmhctl->client_status_cnt >
+					mddi_client_status_frequency)) {
+					/*
+					 * special case where we are hibernating
+					 * and mddi_host_isr is not firing, so
+					 * kick the link so that the status can
+					 * be retrieved
+					 */
+
+					/* need to wake up link before issuing
+					 * rev encap command
+					 */
+					MDDI_MSG_INFO("wake up link!\n");
+					spin_lock_irqsave(&mddi_host_spin_lock,
+							  flags);
+					mddi_host_enable_hclk();
+					mddi_host_enable_io_clock();
+					pmhctl->link_state =
+					    MDDI_LINK_ACTIVATING;
+					mddi_host_reg_out(CMD,
+							  MDDI_CMD_LINK_ACTIVE);
+					spin_unlock_irqrestore
+					    (&mddi_host_spin_lock, flags);
+				} else
+				    if ((pmhctl->link_state == MDDI_LINK_ACTIVE)
+					&& pmhctl->disable_hibernation) {
+					/*
+					 * special case where we have disabled
+					 * hibernation and mddi_host_isr
+					 * is not firing, so enable interrupt
+					 * for no pkts pending, which will
+					 * generate an interrupt
+					 */
+					MDDI_MSG_INFO("kick isr!\n");
+					spin_lock_irqsave(&mddi_host_spin_lock,
+							  flags);
+					mddi_host_enable_hclk();
+					mddi_host_reg_outm(INTEN,
+							   MDDI_INT_NO_CMD_PKTS_PEND,
+							   MDDI_INT_NO_CMD_PKTS_PEND);
+					spin_unlock_irqrestore
+					    (&mddi_host_spin_lock, flags);
+				}
+			}
+		}
+#endif /* #ifndef FEATURE_MDDI_DISABLE_REVERSE */
+	}
+
+	/* Check if logging is turned on */
+	for (host_idx = MDDI_HOST_PRIM; host_idx < MDDI_NUM_HOST_CORES;
+	     host_idx++) {
+		mddi_log_params_struct_type *prev_ptr = &(prev_parms[host_idx]);
+		pmhctl = &(mhctl[host_idx]);
+
+		if (mddi_debug_log_statistics) {
+
+			/* get video pkt count from MDP, since MDDI sw cannot know this */
+			pmhctl->log_parms.vid_cnt = mdp_total_vdopkts;
+
+			if (mddi_log_stats_counter >= mddi_log_stats_frequency) {
+				/* mddi_log_stats_counter = 0; */
+				if (mddi_debug_log_statistics) {
+					MDDI_MSG_NOTICE
+					    ("MDDI Statistics since last report:\n");
+					MDDI_MSG_NOTICE("  Packets sent:\n");
+					MDDI_MSG_NOTICE
+					    ("    %d RTD packet(s)\n",
+					     pmhctl->log_parms.rtd_cnt -
+					     prev_ptr->rtd_cnt);
+					if (prev_ptr->rtd_cnt !=
+					    pmhctl->log_parms.rtd_cnt) {
+						unsigned long flags;
+						spin_lock_irqsave
+						    (&mddi_host_spin_lock,
+						     flags);
+						mddi_host_enable_hclk();
+						pmhctl->rtd_value =
+						    mddi_host_reg_in(RTD_VAL);
+						spin_unlock_irqrestore
+						    (&mddi_host_spin_lock,
+						     flags);
+						MDDI_MSG_NOTICE
+						    ("      RTD value=%d\n",
+						     pmhctl->rtd_value);
+					}
+					MDDI_MSG_NOTICE
+					    ("    %d VIDEO packets\n",
+					     pmhctl->log_parms.vid_cnt -
+					     prev_ptr->vid_cnt);
+					MDDI_MSG_NOTICE
+					    ("    %d Register Access packets\n",
+					     pmhctl->log_parms.reg_acc_cnt -
+					     prev_ptr->reg_acc_cnt);
+					MDDI_MSG_NOTICE
+					    ("    %d Reverse Encapsulation packet(s)\n",
+					     pmhctl->log_parms.rev_enc_cnt -
+					     prev_ptr->rev_enc_cnt);
+					if (prev_ptr->rev_enc_cnt !=
+					    pmhctl->log_parms.rev_enc_cnt) {
+						/* report # of reverse CRC errors */
+						MDDI_MSG_NOTICE
+						    ("      %d reverse CRC errors detected\n",
+						     pmhctl->log_parms.
+						     rev_crc_cnt -
+						     prev_ptr->rev_crc_cnt);
+					}
+					MDDI_MSG_NOTICE
+					    ("  Packets received:\n");
+					MDDI_MSG_NOTICE
+					    ("    %d Client Status packets",
+					     pmhctl->log_parms.cli_stat_cnt -
+					     prev_ptr->cli_stat_cnt);
+					if (prev_ptr->cli_stat_cnt !=
+					    pmhctl->log_parms.cli_stat_cnt) {
+						MDDI_MSG_NOTICE
+						    ("      %d forward CRC errors reported\n",
+						     pmhctl->log_parms.
+						     fwd_crc_cnt -
+						     prev_ptr->fwd_crc_cnt);
+					}
+					MDDI_MSG_NOTICE
+					    ("    %d Register Access Read packets\n",
+					     pmhctl->log_parms.reg_read_cnt -
+					     prev_ptr->reg_read_cnt);
+
+					if (pmhctl->link_state ==
+					    MDDI_LINK_ACTIVE) {
+						MDDI_MSG_NOTICE
+						    ("  Current Link Status: Active\n");
+					} else
+					    if ((pmhctl->link_state ==
+						 MDDI_LINK_HIBERNATING)
+						|| (pmhctl->link_state ==
+						    MDDI_LINK_ACTIVATING)) {
+						MDDI_MSG_NOTICE
+						    ("  Current Link Status: Hibernation\n");
+					} else {
+						MDDI_MSG_NOTICE
+						    ("  Current Link Status: Inactive\n");
+					}
+					MDDI_MSG_NOTICE
+					    ("    Active state entered %d times\n",
+					     pmhctl->log_parms.link_active_cnt -
+					     prev_ptr->link_active_cnt);
+					MDDI_MSG_NOTICE
+					    ("    Hibernation state entered %d times\n",
+					     pmhctl->log_parms.
+					     link_hibernate_cnt -
+					     prev_ptr->link_hibernate_cnt);
+				}
+			}
+			prev_parms[host_idx] = pmhctl->log_parms;
+		}
+	}
+	if (mddi_log_stats_counter >= mddi_log_stats_frequency)
+		mddi_log_stats_counter = 0;
+
+	mutex_lock(&mddi_timer_lock);
+	if (!mddi_timer_shutdown_flag) {
+		mddi_host_timer.function = mddi_host_timer_service;
+		mddi_host_timer.data = 0;
+		mddi_host_timer.expires = jiffies + ((time_ms * HZ) / 1000);
+		add_timer(&mddi_host_timer);
+	}
+	mutex_unlock(&mddi_timer_lock);
+
+	return;
+}				/* mddi_host_timer_cb */
+
+static void mddi_process_link_list_done(void)
+{
+	mddi_host_type host_idx = mddi_curr_host;
+	mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]);
+
+	/* normal forward linked list packet(s) were sent */
+	if (pmhctl->llist_info.transmitting_start_idx == UNASSIGNED_INDEX) {
+		MDDI_MSG_ERR("**** getting LL done, but no list ****\n");
+	} else {
+		uint16 idx;
+
+#ifndef FEATURE_MDDI_DISABLE_REVERSE
+		if (pmhctl->rev_state == MDDI_REV_REG_READ_ISSUED) {
+			/* special case where a register read packet was sent */
+			pmhctl->rev_state = MDDI_REV_REG_READ_SENT;
+			if (pmhctl->llist_info.reg_read_idx == UNASSIGNED_INDEX) {
+				MDDI_MSG_ERR
+				    ("**** getting LL done, but no list ****\n");
+			}
+		}
+#endif
+		for (idx = pmhctl->llist_info.transmitting_start_idx;;) {
+			uint16 next_idx = pmhctl->llist_notify[idx].next_idx;
+			/* with reg read we don't release the waiting tcb until after
+			 * the reverse encapsulation has completed.
+			 */
+			if (idx != pmhctl->llist_info.reg_read_idx) {
+				/* notify task that may be waiting on this completion */
+				if (pmhctl->llist_notify[idx].waiting) {
+					complete(&
+						 (pmhctl->llist_notify[idx].
+						  done_comp));
+				}
+				if (pmhctl->llist_notify[idx].done_cb != NULL) {
+					(*(pmhctl->llist_notify[idx].done_cb))
+					    ();
+				}
+
+				pmhctl->llist_notify[idx].in_use = FALSE;
+				pmhctl->llist_notify[idx].waiting = FALSE;
+				pmhctl->llist_notify[idx].done_cb = NULL;
+				if (idx < MDDI_NUM_DYNAMIC_LLIST_ITEMS) {
+					/* static LLIST items are configured only once */
+					pmhctl->llist_notify[idx].next_idx =
+					    UNASSIGNED_INDEX;
+				}
+				/*
+				 * currently, all linked list packets are
+				 * register access, so we can increment the
+				 * counter for that packet type here.
+				 */
+				pmhctl->log_parms.reg_acc_cnt++;
+			}
+			if (idx == pmhctl->llist_info.transmitting_end_idx)
+				break;
+			idx = next_idx;
+			if (idx == UNASSIGNED_INDEX)
+				MDDI_MSG_CRIT("MDDI linked list corruption!\n");
+		}
+
+		pmhctl->llist_info.transmitting_start_idx = UNASSIGNED_INDEX;
+		pmhctl->llist_info.transmitting_end_idx = UNASSIGNED_INDEX;
+
+		if (pmhctl->mddi_waiting_for_llist_avail) {
+			if (!
+			    (pmhctl->
+			     llist_notify[pmhctl->llist_info.next_free_idx].
+			     in_use)) {
+				pmhctl->mddi_waiting_for_llist_avail = FALSE;
+				complete(&(pmhctl->mddi_llist_avail_comp));
+			}
+		}
+	}
+
+	/* Turn off MDDI_INT_PRI_LINK_LIST_DONE interrupt */
+	mddi_host_reg_outm(INTEN, MDDI_INT_PRI_LINK_LIST_DONE, 0);
+
+}
+
+static void mddi_queue_forward_linked_list(void)
+{
+	uint16 first_pkt_index;
+	mddi_linked_list_type *llist_dma;
+	mddi_host_type host_idx = mddi_curr_host;
+	mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]);
+	llist_dma = pmhctl->llist_dma_ptr;
+
+	first_pkt_index = UNASSIGNED_INDEX;
+
+	if (pmhctl->llist_info.transmitting_start_idx == UNASSIGNED_INDEX) {
+#ifndef FEATURE_MDDI_DISABLE_REVERSE
+		if (pmhctl->llist_info.reg_read_waiting) {
+			if (pmhctl->rev_state == MDDI_REV_IDLE) {
+				/*
+				 * we have a register read to send and
+				 * can send it now
+				 */
+				pmhctl->rev_state = MDDI_REV_REG_READ_ISSUED;
+				mddi_reg_read_retry = 0;
+				first_pkt_index =
+				    pmhctl->llist_info.waiting_start_idx;
+				pmhctl->llist_info.reg_read_waiting = FALSE;
+			}
+		} else
+#endif
+		{
+			/*
+			 * not register read to worry about, go ahead and write
+			 * anything that may be on the waiting list.
+			 */
+			first_pkt_index = pmhctl->llist_info.waiting_start_idx;
+		}
+	}
+
+	if (first_pkt_index != UNASSIGNED_INDEX) {
+		pmhctl->llist_info.transmitting_start_idx =
+		    pmhctl->llist_info.waiting_start_idx;
+		pmhctl->llist_info.transmitting_end_idx =
+		    pmhctl->llist_info.waiting_end_idx;
+		pmhctl->llist_info.waiting_start_idx = UNASSIGNED_INDEX;
+		pmhctl->llist_info.waiting_end_idx = UNASSIGNED_INDEX;
+
+		/* write to the primary pointer register */
+		MDDI_MSG_DEBUG("MDDI writing primary ptr with idx=%d\n",
+			       first_pkt_index);
+
+		pmhctl->int_type.llist_ptr_write_2++;
+
+		dma_coherent_pre_ops();
+		mddi_host_reg_out(PRI_PTR, &llist_dma[first_pkt_index]);
+
+		/* enable interrupt when complete */
+		mddi_host_reg_outm(INTEN, MDDI_INT_PRI_LINK_LIST_DONE,
+				   MDDI_INT_PRI_LINK_LIST_DONE);
+
+	}
+
+}
+
+#ifndef FEATURE_MDDI_DISABLE_REVERSE
+static void mddi_read_rev_packet(byte *data_ptr)
+{
+	uint16 i, length;
+	mddi_host_type host_idx = mddi_curr_host;
+	mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]);
+
+	uint8 *rev_ptr_overflow =
+	    (pmhctl->rev_ptr_start + MDDI_REV_BUFFER_SIZE);
+
+	/* first determine the length and handle invalid lengths */
+	length = *pmhctl->rev_ptr_curr++;
+	if (pmhctl->rev_ptr_curr >= rev_ptr_overflow)
+		pmhctl->rev_ptr_curr = pmhctl->rev_ptr_start;
+	length |= ((*pmhctl->rev_ptr_curr++) << 8);
+	if (pmhctl->rev_ptr_curr >= rev_ptr_overflow)
+		pmhctl->rev_ptr_curr = pmhctl->rev_ptr_start;
+	if (length > (pmhctl->rev_pkt_size - 2)) {
+		MDDI_MSG_ERR("Invalid rev pkt length %d\n", length);
+		/* rev_pkt_size should always be <= rev_ptr_size so limit to packet size */
+		length = pmhctl->rev_pkt_size - 2;
+	}
+
+	/* If the data pointer is NULL, just increment the pmhctl->rev_ptr_curr.
+	 * Loop around if necessary. Don't bother reading the data.
+	 */
+	if (data_ptr == NULL) {
+		pmhctl->rev_ptr_curr += length;
+		if (pmhctl->rev_ptr_curr >= rev_ptr_overflow)
+			pmhctl->rev_ptr_curr -= MDDI_REV_BUFFER_SIZE;
+		return;
+	}
+
+	data_ptr[0] = length & 0x0ff;
+	data_ptr[1] = length >> 8;
+	data_ptr += 2;
+	/* copy the data to data_ptr byte-at-a-time */
+	for (i = 0; (i < length) && (pmhctl->rev_ptr_curr < rev_ptr_overflow);
+	     i++)
+		*data_ptr++ = *pmhctl->rev_ptr_curr++;
+	if (pmhctl->rev_ptr_curr >= rev_ptr_overflow)
+		pmhctl->rev_ptr_curr = pmhctl->rev_ptr_start;
+	for (; (i < length) && (pmhctl->rev_ptr_curr < rev_ptr_overflow); i++)
+		*data_ptr++ = *pmhctl->rev_ptr_curr++;
+}
+
+static void mddi_process_rev_packets(void)
+{
+	uint32 rev_packet_count;
+	word i;
+	uint32 crc_errors;
+	boolean mddi_reg_read_successful = FALSE;
+	mddi_host_type host_idx = mddi_curr_host;
+	mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]);
+
+	pmhctl->log_parms.rev_enc_cnt++;
+	if ((pmhctl->rev_state != MDDI_REV_ENCAP_ISSUED) &&
+	    (pmhctl->rev_state != MDDI_REV_STATUS_REQ_ISSUED) &&
+	    (pmhctl->rev_state != MDDI_REV_CLIENT_CAP_ISSUED)) {
+		MDDI_MSG_ERR("Wrong state %d for reverse int\n",
+			     pmhctl->rev_state);
+	}
+	/* Turn off MDDI_INT_REV_AVAIL interrupt */
+	mddi_host_reg_outm(INTEN, MDDI_INT_REV_DATA_AVAIL, 0);
+
+	/* Clear rev data avail int */
+	mddi_host_reg_out(INT, MDDI_INT_REV_DATA_AVAIL);
+
+	/* Get Number of packets */
+	rev_packet_count = mddi_host_reg_in(REV_PKT_CNT);
+
+#ifndef T_MSM7500
+	/* Clear out rev packet counter */
+	mddi_host_reg_out(REV_PKT_CNT, 0x0000);
+#endif
+
+#if defined(CONFIG_FB_MSM_MDP31) || defined(CONFIG_FB_MSM_MDP40)
+	if ((pmhctl->rev_state == MDDI_REV_CLIENT_CAP_ISSUED) &&
+	    (rev_packet_count > 0) &&
+	    (mddi_host_core_version == 0x28 ||
+	     mddi_host_core_version == 0x30)) {
+
+		uint32 int_reg;
+		uint32 max_count = 0;
+
+		mddi_host_reg_out(REV_PTR, pmhctl->mddi_rev_ptr_write_val);
+		int_reg = mddi_host_reg_in(INT);
+		while ((int_reg & 0x100000) == 0) {
+			udelay(3);
+			int_reg = mddi_host_reg_in(INT);
+			if (++max_count > 100)
+				break;
+		}
+	}
+#endif
+
+	/* Get CRC error count */
+	crc_errors = mddi_host_reg_in(REV_CRC_ERR);
+	if (crc_errors != 0) {
+		pmhctl->log_parms.rev_crc_cnt += crc_errors;
+		pmhctl->stats.rev_crc_count += crc_errors;
+		MDDI_MSG_ERR("!!! MDDI %d Reverse CRC Error(s) !!!\n",
+			     crc_errors);
+#ifndef T_MSM7500
+		/* Clear CRC error count */
+		mddi_host_reg_out(REV_CRC_ERR, 0x0000);
+#endif
+		/* also issue an RTD to attempt recovery */
+		pmhctl->rtd_counter = mddi_rtd_frequency;
+	}
+
+	pmhctl->rtd_value = mddi_host_reg_in(RTD_VAL);
+
+	MDDI_MSG_DEBUG("MDDI rev pkt cnt=%d, ptr=0x%x, RTD:0x%x\n",
+		       rev_packet_count,
+		       pmhctl->rev_ptr_curr - pmhctl->rev_ptr_start,
+		       pmhctl->rtd_value);
+
+	if (rev_packet_count >= 1) {
+		mddi_invalidate_cache_lines((uint32 *) pmhctl->rev_ptr_start,
+					    MDDI_REV_BUFFER_SIZE);
+	} else {
+		MDDI_MSG_ERR("Reverse pkt sent, no data rxd\n");
+		if (mddi_reg_read_value_ptr)
+			*mddi_reg_read_value_ptr = -EBUSY;
+	}
+	/* order the reads */
+	dma_coherent_post_ops();
+	for (i = 0; i < rev_packet_count; i++) {
+		mddi_rev_packet_type *rev_pkt_ptr;
+
+		mddi_read_rev_packet(rev_packet_data);
+
+		rev_pkt_ptr = (mddi_rev_packet_type *) rev_packet_data;
+
+		if (rev_pkt_ptr->packet_length > pmhctl->rev_pkt_size) {
+			MDDI_MSG_ERR("!!!invalid packet size: %d\n",
+				     rev_pkt_ptr->packet_length);
+		}
+
+		MDDI_MSG_DEBUG("MDDI rev pkt 0x%x size 0x%x\n",
+			       rev_pkt_ptr->packet_type,
+			       rev_pkt_ptr->packet_length);
+
+		/* Do whatever you want to do with the data based on the packet type */
+		switch (rev_pkt_ptr->packet_type) {
+		case 66:	/* Client Capability */
+			{
+				mddi_client_capability_type
+				    *client_capability_pkt_ptr;
+
+				client_capability_pkt_ptr =
+				    (mddi_client_capability_type *)
+				    rev_packet_data;
+				MDDI_MSG_NOTICE
+				    ("Client Capability: Week=%d, Year=%d\n",
+				     client_capability_pkt_ptr->
+				     Week_of_Manufacture,
+				     client_capability_pkt_ptr->
+				     Year_of_Manufacture);
+				memcpy((void *)&mddi_client_capability_pkt,
+				       (void *)rev_packet_data,
+				       sizeof(mddi_client_capability_type));
+				pmhctl->log_parms.cli_cap_cnt++;
+			}
+			break;
+
+		case 70:	/* Display Status */
+			{
+				mddi_client_status_type *client_status_pkt_ptr;
+
+				client_status_pkt_ptr =
+				    (mddi_client_status_type *) rev_packet_data;
+				if ((client_status_pkt_ptr->crc_error_count !=
+				     0)
+				    || (client_status_pkt_ptr->
+					reverse_link_request != 0)) {
+					MDDI_MSG_ERR
+					    ("Client Status: RevReq=%d, CrcErr=%d\n",
+					     client_status_pkt_ptr->
+					     reverse_link_request,
+					     client_status_pkt_ptr->
+					     crc_error_count);
+				} else {
+					MDDI_MSG_DEBUG
+					    ("Client Status: RevReq=%d, CrcErr=%d\n",
+					     client_status_pkt_ptr->
+					     reverse_link_request,
+					     client_status_pkt_ptr->
+					     crc_error_count);
+				}
+				pmhctl->log_parms.fwd_crc_cnt +=
+				    client_status_pkt_ptr->crc_error_count;
+				pmhctl->stats.fwd_crc_count +=
+				    client_status_pkt_ptr->crc_error_count;
+				pmhctl->log_parms.cli_stat_cnt++;
+			}
+			break;
+
+		case 146:	/* register access packet */
+			{
+				mddi_register_access_packet_type
+				    * regacc_pkt_ptr;
+				uint32 data_count;
+
+				regacc_pkt_ptr =
+				    (mddi_register_access_packet_type *)
+				    rev_packet_data;
+
+				/* Bits[0:13] - read data count */
+				data_count = regacc_pkt_ptr->read_write_info
+					& 0x3FFF;
+				MDDI_MSG_DEBUG("\n MDDI rev read: 0x%x",
+					regacc_pkt_ptr->read_write_info);
+				MDDI_MSG_DEBUG("Reg Acc parse reg=0x%x,"
+					"value=0x%x\n", regacc_pkt_ptr->
+					register_address, regacc_pkt_ptr->
+					register_data_list[0]);
+
+				/* Copy register value to location passed in */
+				if (mddi_reg_read_value_ptr) {
+#if defined(T_MSM6280) && !defined(T_MSM7200)
+					/* only least significant 16 bits are valid with 6280 */
+					*mddi_reg_read_value_ptr =
+					    regacc_pkt_ptr->
+					    register_data_list[0] & 0x0000ffff;
+					mddi_reg_read_successful = TRUE;
+					mddi_reg_read_value_ptr = NULL;
+#else
+				if (data_count && data_count <=
+					MDDI_HOST_MAX_CLIENT_REG_IN_SAME_ADDR) {
+					memcpy(mddi_reg_read_value_ptr,
+						(void *)&regacc_pkt_ptr->
+						register_data_list[0],
+						data_count * 4);
+					mddi_reg_read_successful = TRUE;
+					mddi_reg_read_value_ptr = NULL;
+				}
+#endif
+				}
+
+#ifdef DEBUG_MDDIHOSTI
+				if ((mddi_gpio.polling_enabled) &&
+				    (regacc_pkt_ptr->register_address ==
+				     mddi_gpio.polling_reg)) {
+					/*
+					 * ToDo: need to call Linux GPIO call
+					 * here...
+					 */
+					 mddi_client_lcd_gpio_poll(
+					 regacc_pkt_ptr->register_data_list[0]);
+				}
+#endif
+				pmhctl->log_parms.reg_read_cnt++;
+			}
+			break;
+
+		case INVALID_PKT_TYPE:	/* 0xFFFF */
+			MDDI_MSG_ERR("!!!INVALID_PKT_TYPE rcvd\n");
+			break;
+
+		default:	/* any other packet */
+			{
+				uint16 hdlr;
+
+				for (hdlr = 0; hdlr < MAX_MDDI_REV_HANDLERS;
+				     hdlr++) {
+					if (mddi_rev_pkt_handler[hdlr].
+							handler == NULL)
+						continue;
+					if (mddi_rev_pkt_handler[hdlr].
+					    pkt_type ==
+					    rev_pkt_ptr->packet_type) {
+						(*(mddi_rev_pkt_handler[hdlr].
+						  handler)) (rev_pkt_ptr);
+					/* pmhctl->rev_state = MDDI_REV_IDLE; */
+						break;
+					}
+				}
+				if (hdlr >= MAX_MDDI_REV_HANDLERS)
+					MDDI_MSG_ERR("MDDI unknown rev pkt\n");
+			}
+			break;
+		}
+	}
+	if ((pmhctl->rev_ptr_curr + pmhctl->rev_pkt_size) >=
+	    (pmhctl->rev_ptr_start + MDDI_REV_BUFFER_SIZE)) {
+		pmhctl->rev_ptr_written = FALSE;
+	}
+
+	if (pmhctl->rev_state == MDDI_REV_ENCAP_ISSUED) {
+		pmhctl->rev_state = MDDI_REV_IDLE;
+		if (mddi_rev_user.waiting) {
+			mddi_rev_user.waiting = FALSE;
+			complete(&(mddi_rev_user.done_comp));
+		} else if (pmhctl->llist_info.reg_read_idx == UNASSIGNED_INDEX) {
+			MDDI_MSG_ERR
+			    ("Reverse Encap state, but no reg read in progress\n");
+		} else {
+			if ((!mddi_reg_read_successful) &&
+			    (mddi_reg_read_retry < mddi_reg_read_retry_max) &&
+			    (mddi_enable_reg_read_retry)) {
+				/*
+				 * There is a race condition that can happen
+				 * where the reverse encapsulation message is
+				 * sent out by the MDDI host before the register
+				 * read packet is sent. As a work-around for
+				 * that problem we issue the reverse
+				 * encapsulation one more time before giving up.
+				 */
+				if (mddi_enable_reg_read_retry_once)
+					mddi_reg_read_retry =
+					    mddi_reg_read_retry_max;
+				else
+					mddi_reg_read_retry++;
+				pmhctl->rev_state = MDDI_REV_REG_READ_SENT;
+				pmhctl->stats.reg_read_failure++;
+			} else {
+				uint16 reg_read_idx =
+				    pmhctl->llist_info.reg_read_idx;
+
+				mddi_reg_read_retry = 0;
+				if (pmhctl->llist_notify[reg_read_idx].waiting) {
+					complete(&
+						 (pmhctl->
+						  llist_notify[reg_read_idx].
+						  done_comp));
+				}
+				pmhctl->llist_info.reg_read_idx =
+				    UNASSIGNED_INDEX;
+				if (pmhctl->llist_notify[reg_read_idx].
+				    done_cb != NULL) {
+					(*
+					 (pmhctl->llist_notify[reg_read_idx].
+					  done_cb)) ();
+				}
+				pmhctl->llist_notify[reg_read_idx].next_idx =
+				    UNASSIGNED_INDEX;
+				pmhctl->llist_notify[reg_read_idx].in_use =
+				    FALSE;
+				pmhctl->llist_notify[reg_read_idx].waiting =
+				    FALSE;
+				pmhctl->llist_notify[reg_read_idx].done_cb =
+				    NULL;
+				if (!mddi_reg_read_successful)
+					pmhctl->stats.reg_read_failure++;
+			}
+		}
+	} else if (pmhctl->rev_state == MDDI_REV_CLIENT_CAP_ISSUED) {
+#if defined(CONFIG_FB_MSM_MDP31) || defined(CONFIG_FB_MSM_MDP40)
+		if (mddi_host_core_version == 0x28 ||
+		    mddi_host_core_version == 0x30) {
+			mddi_host_reg_out(FIFO_ALLOC, 0x00);
+			pmhctl->rev_ptr_written = TRUE;
+			mddi_host_reg_out(REV_PTR,
+				pmhctl->mddi_rev_ptr_write_val);
+			pmhctl->rev_ptr_curr = pmhctl->rev_ptr_start;
+			mddi_host_reg_out(CMD, 0xC00);
+		}
+#endif
+
+		if (mddi_rev_user.waiting) {
+			mddi_rev_user.waiting = FALSE;
+			complete(&(mddi_rev_user.done_comp));
+		}
+		pmhctl->rev_state = MDDI_REV_IDLE;
+	} else {
+		pmhctl->rev_state = MDDI_REV_IDLE;
+	}
+
+	/* pmhctl->rev_state = MDDI_REV_IDLE; */
+
+	/* Re-enable interrupt */
+	mddi_host_reg_outm(INTEN, MDDI_INT_REV_DATA_AVAIL,
+			   MDDI_INT_REV_DATA_AVAIL);
+
+}
+
+static void mddi_issue_reverse_encapsulation(void)
+{
+	mddi_host_type host_idx = mddi_curr_host;
+	mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]);
+	/* Only issue a reverse encapsulation packet if:
+	 * 1) another reverse is not in progress (MDDI_REV_IDLE).
+	 * 2) a register read has been sent (MDDI_REV_REG_READ_SENT).
+	 * 3) forward is not in progress, because of a hw bug in client that
+	 *    causes forward crc errors on packet immediately after rev encap.
+	 */
+	if (((pmhctl->rev_state == MDDI_REV_IDLE) ||
+	     (pmhctl->rev_state == MDDI_REV_REG_READ_SENT)) &&
+	    (pmhctl->llist_info.transmitting_start_idx == UNASSIGNED_INDEX) &&
+	    (!mdp_in_processing)) {
+		uint32 mddi_command = MDDI_CMD_SEND_REV_ENCAP;
+
+		if ((pmhctl->rev_state == MDDI_REV_REG_READ_SENT) ||
+		    (mddi_rev_encap_user_request == TRUE)) {
+			mddi_host_enable_io_clock();
+			if (pmhctl->link_state == MDDI_LINK_HIBERNATING) {
+				/* need to wake up link before issuing rev encap command */
+				MDDI_MSG_DEBUG("wake up link!\n");
+				pmhctl->link_state = MDDI_LINK_ACTIVATING;
+				mddi_host_reg_out(CMD, MDDI_CMD_LINK_ACTIVE);
+			} else {
+				if (pmhctl->rtd_counter >= mddi_rtd_frequency) {
+					MDDI_MSG_DEBUG
+					    ("mddi sending RTD command!\n");
+					mddi_host_reg_out(CMD,
+							  MDDI_CMD_SEND_RTD);
+					pmhctl->rtd_counter = 0;
+					pmhctl->log_parms.rtd_cnt++;
+				}
+				if (pmhctl->rev_state != MDDI_REV_REG_READ_SENT) {
+					/* this is generic reverse request by user, so
+					 * reset the waiting flag. */
+					mddi_rev_encap_user_request = FALSE;
+				}
+				/* link is active so send reverse encap to get register read results */
+				pmhctl->rev_state = MDDI_REV_ENCAP_ISSUED;
+				mddi_command = MDDI_CMD_SEND_REV_ENCAP;
+				MDDI_MSG_DEBUG("sending rev encap!\n");
+			}
+		} else
+		    if ((pmhctl->client_status_cnt >=
+			 mddi_client_status_frequency)
+			|| mddi_client_capability_request) {
+			mddi_host_enable_io_clock();
+			if (pmhctl->link_state == MDDI_LINK_HIBERNATING) {
+				/* only wake up the link if it client status is overdue */
+				if ((pmhctl->client_status_cnt >=
+				     (mddi_client_status_frequency * 2))
+				    || mddi_client_capability_request) {
+					/* need to wake up link before issuing rev encap command */
+					MDDI_MSG_DEBUG("wake up link!\n");
+					pmhctl->link_state =
+					    MDDI_LINK_ACTIVATING;
+					mddi_host_reg_out(CMD,
+							  MDDI_CMD_LINK_ACTIVE);
+				}
+			} else {
+				if (pmhctl->rtd_counter >= mddi_rtd_frequency) {
+					MDDI_MSG_DEBUG
+					    ("mddi sending RTD command!\n");
+					mddi_host_reg_out(CMD,
+							  MDDI_CMD_SEND_RTD);
+					pmhctl->rtd_counter = 0;
+					pmhctl->log_parms.rtd_cnt++;
+				}
+				/* periodically get client status */
+				MDDI_MSG_DEBUG
+				    ("mddi sending rev enc! (get status)\n");
+				if (mddi_client_capability_request) {
+					pmhctl->rev_state =
+					    MDDI_REV_CLIENT_CAP_ISSUED;
+					mddi_command = MDDI_CMD_GET_CLIENT_CAP;
+					mddi_client_capability_request = FALSE;
+				} else {
+					pmhctl->rev_state =
+					    MDDI_REV_STATUS_REQ_ISSUED;
+					pmhctl->client_status_cnt = 0;
+					mddi_command =
+					    MDDI_CMD_GET_CLIENT_STATUS;
+				}
+			}
+		}
+		if ((pmhctl->rev_state == MDDI_REV_ENCAP_ISSUED) ||
+		    (pmhctl->rev_state == MDDI_REV_STATUS_REQ_ISSUED) ||
+		    (pmhctl->rev_state == MDDI_REV_CLIENT_CAP_ISSUED)) {
+			pmhctl->int_type.rev_encap_count++;
+#if defined(T_MSM6280) && !defined(T_MSM7200)
+			mddi_rev_pointer_written = TRUE;
+			mddi_host_reg_out(REV_PTR, mddi_rev_ptr_write_val);
+			mddi_rev_ptr_curr = mddi_rev_ptr_start;
+			/* force new rev ptr command */
+			mddi_host_reg_out(CMD, 0xC00);
+#else
+			if (!pmhctl->rev_ptr_written) {
+				MDDI_MSG_DEBUG("writing reverse pointer!\n");
+				pmhctl->rev_ptr_written = TRUE;
+#if defined(CONFIG_FB_MSM_MDP31) || defined(CONFIG_FB_MSM_MDP40)
+				if ((pmhctl->rev_state ==
+				     MDDI_REV_CLIENT_CAP_ISSUED) &&
+				    (mddi_host_core_version == 0x28 ||
+				     mddi_host_core_version == 0x30)) {
+					pmhctl->rev_ptr_written = FALSE;
+					mddi_host_reg_out(FIFO_ALLOC, 0x02);
+				} else
+					mddi_host_reg_out(REV_PTR,
+						  pmhctl->
+						  mddi_rev_ptr_write_val);
+#else
+				mddi_host_reg_out(REV_PTR,
+						  pmhctl->
+						  mddi_rev_ptr_write_val);
+#endif
+			}
+#endif
+			if (mddi_debug_clear_rev_data) {
+				uint16 i;
+				for (i = 0; i < MDDI_MAX_REV_DATA_SIZE / 4; i++)
+					pmhctl->rev_data_buf[i] = 0xdddddddd;
+				/* clean cache */
+				mddi_flush_cache_lines(pmhctl->rev_data_buf,
+						       MDDI_MAX_REV_DATA_SIZE);
+			}
+
+			/* send reverse encapsulation to get needed data */
+			mddi_host_reg_out(CMD, mddi_command);
+		}
+	}
+
+}
+
+static void mddi_process_client_initiated_wakeup(void)
+{
+	mddi_host_type host_idx = mddi_curr_host;
+	mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]);
+
+	/* Disable MDDI_INT Interrupt, we detect client initiated wakeup one
+	 * time for each entry into hibernation */
+	mddi_host_reg_outm(INTEN, MDDI_INT_MDDI_IN, 0);
+
+	if (host_idx == MDDI_HOST_PRIM) {
+		if (mddi_vsync_detect_enabled) {
+			mddi_host_enable_io_clock();
+#ifndef MDDI_HOST_DISP_LISTEN
+			/* issue command to bring up link */
+			/* need to do this to clear the vsync condition */
+			if (pmhctl->link_state == MDDI_LINK_HIBERNATING) {
+				pmhctl->link_state = MDDI_LINK_ACTIVATING;
+				mddi_host_reg_out(CMD, MDDI_CMD_LINK_ACTIVE);
+			}
+#endif
+			/*
+			 * Indicate to client specific code that vsync was
+			 * enabled, and we did not detect a client initiated
+			 * wakeup. The client specific handler can clear the
+			 * condition if necessary to prevent subsequent
+			 * client initiated wakeups.
+			 */
+			mddi_client_lcd_vsync_detected(TRUE);
+			pmhctl->log_parms.vsync_response_cnt++;
+			MDDI_MSG_NOTICE("MDDI_INT_IN condition\n");
+
+		}
+	}
+
+	if (mddi_gpio.polling_enabled) {
+		mddi_host_enable_io_clock();
+		/* check interrupt status now */
+		(void)mddi_queue_register_read_int(mddi_gpio.polling_reg,
+						   &mddi_gpio.polling_val);
+	}
+}
+#endif /* FEATURE_MDDI_DISABLE_REVERSE */
+
+static void mddi_host_isr(void)
+{
+	uint32 int_reg, int_en;
+#ifndef FEATURE_MDDI_DISABLE_REVERSE
+	uint32 status_reg;
+#endif
+	mddi_host_type host_idx = mddi_curr_host;
+	mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]);
+
+	if (!MDDI_HOST_IS_HCLK_ON) {
+		MDDI_HOST_ENABLE_HCLK;
+	}
+	int_reg = mddi_host_reg_in(INT);
+	int_en = mddi_host_reg_in(INTEN);
+	pmhctl->saved_int_reg = int_reg;
+	pmhctl->saved_int_en = int_en;
+	int_reg = int_reg & int_en;
+	pmhctl->int_type.count++;
+
+
+#ifndef FEATURE_MDDI_DISABLE_REVERSE
+	status_reg = mddi_host_reg_in(STAT);
+
+	if ((int_reg & MDDI_INT_MDDI_IN) ||
+	    ((int_en & MDDI_INT_MDDI_IN) &&
+	     ((int_reg == 0) || (status_reg & MDDI_STAT_CLIENT_WAKEUP_REQ)))) {
+		/*
+		 * The MDDI_IN condition will clear itself, and so it is
+		 * possible that MDDI_IN was the reason for the isr firing,
+		 * even though the interrupt register does not have the
+		 * MDDI_IN bit set. To check if this was the case we need to
+		 * look at the status register bit that signifies a client
+		 * initiated wakeup. If the status register bit is set, as well
+		 * as the MDDI_IN interrupt enabled, then we treat this as a
+		 * client initiated wakeup.
+		 */
+		if (int_reg & MDDI_INT_MDDI_IN)
+			pmhctl->int_type.in_count++;
+		mddi_process_client_initiated_wakeup();
+	}
+#endif
+
+	if (int_reg & MDDI_INT_LINK_STATE_CHANGES) {
+		pmhctl->int_type.state_change_count++;
+		mddi_report_state_change(int_reg);
+	}
+
+	if (int_reg & MDDI_INT_PRI_LINK_LIST_DONE) {
+		pmhctl->int_type.ll_done_count++;
+		mddi_process_link_list_done();
+	}
+#ifndef FEATURE_MDDI_DISABLE_REVERSE
+	if (int_reg & MDDI_INT_REV_DATA_AVAIL) {
+		pmhctl->int_type.rev_avail_count++;
+		mddi_process_rev_packets();
+	}
+#endif
+
+	if (int_reg & MDDI_INT_ERROR_CONDITIONS) {
+		pmhctl->int_type.error_count++;
+		mddi_report_errors(int_reg);
+
+		mddi_host_reg_out(INT, int_reg & MDDI_INT_ERROR_CONDITIONS);
+	}
+#ifndef FEATURE_MDDI_DISABLE_REVERSE
+	mddi_issue_reverse_encapsulation();
+
+	if ((pmhctl->rev_state != MDDI_REV_ENCAP_ISSUED) &&
+	    (pmhctl->rev_state != MDDI_REV_STATUS_REQ_ISSUED))
+#endif
+		/* don't want simultaneous reverse and forward with Eagle */
+		mddi_queue_forward_linked_list();
+
+	if (int_reg & MDDI_INT_NO_CMD_PKTS_PEND) {
+		/* this interrupt is used to kick the isr when hibernation is disabled */
+		mddi_host_reg_outm(INTEN, MDDI_INT_NO_CMD_PKTS_PEND, 0);
+	}
+
+	if ((!mddi_host_mdp_active_flag) &&
+	    (!mddi_vsync_detect_enabled) &&
+	    (pmhctl->llist_info.transmitting_start_idx == UNASSIGNED_INDEX) &&
+	    (pmhctl->llist_info.waiting_start_idx == UNASSIGNED_INDEX) &&
+	    (pmhctl->rev_state == MDDI_REV_IDLE)) {
+		if (pmhctl->link_state == MDDI_LINK_HIBERNATING) {
+			mddi_host_disable_io_clock();
+			mddi_host_disable_hclk();
+		}
+#ifdef FEATURE_MDDI_HOST_ENABLE_EARLY_HIBERNATION
+		else if ((pmhctl->link_state == MDDI_LINK_ACTIVE) &&
+			 (!pmhctl->disable_hibernation)) {
+			mddi_host_reg_out(CMD, MDDI_CMD_POWERDOWN);
+		}
+#endif
+	}
+}
+
+static void mddi_host_isr_primary(void)
+{
+	mddi_curr_host = MDDI_HOST_PRIM;
+	mddi_host_isr();
+}
+
+irqreturn_t mddi_pmdh_isr_proxy(int irq, void *ptr)
+{
+	mddi_host_isr_primary();
+	return IRQ_HANDLED;
+}
+
+static void mddi_host_isr_external(void)
+{
+	mddi_curr_host = MDDI_HOST_EXT;
+	mddi_host_isr();
+	mddi_curr_host = MDDI_HOST_PRIM;
+}
+
+irqreturn_t mddi_emdh_isr_proxy(int irq, void *ptr)
+{
+	mddi_host_isr_external();
+	return IRQ_HANDLED;
+}
+
+static void mddi_host_initialize_registers(mddi_host_type host_idx)
+{
+	uint32 pad_reg_val;
+	mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]);
+
+	if (pmhctl->driver_state == MDDI_DRIVER_ENABLED)
+		return;
+
+	/* turn on HCLK to MDDI host core */
+	mddi_host_enable_hclk();
+
+	/* MDDI Reset command */
+	mddi_host_reg_out(CMD, MDDI_CMD_RESET);
+
+	/* Version register (= 0x01) */
+	mddi_host_reg_out(VERSION, 0x0001);
+
+	/* Bytes per subframe register */
+	mddi_host_reg_out(BPS, MDDI_HOST_BYTES_PER_SUBFRAME);
+
+	/* Subframes per media frames register (= 0x03) */
+	mddi_host_reg_out(SPM, 0x0003);
+
+	/* Turn Around 1 register (= 0x05) */
+	mddi_host_reg_out(TA1_LEN, 0x0005);
+
+	/* Turn Around 2 register (= 0x0C) */
+	mddi_host_reg_out(TA2_LEN, MDDI_HOST_TA2_LEN);
+
+	/* Drive hi register (= 0x96) */
+	mddi_host_reg_out(DRIVE_HI, 0x0096);
+
+	/* Drive lo register (= 0x32) */
+	mddi_host_reg_out(DRIVE_LO, 0x0032);
+
+	/* Display wakeup count register (= 0x3c) */
+	mddi_host_reg_out(DISP_WAKE, 0x003c);
+
+	/* Reverse Rate Divisor register (= 0x2) */
+	mddi_host_reg_out(REV_RATE_DIV, MDDI_HOST_REV_RATE_DIV);
+
+#ifndef FEATURE_MDDI_DISABLE_REVERSE
+	/* Reverse Pointer Size */
+	mddi_host_reg_out(REV_SIZE, MDDI_REV_BUFFER_SIZE);
+
+	/* Rev Encap Size */
+	mddi_host_reg_out(REV_ENCAP_SZ, pmhctl->rev_pkt_size);
+#endif
+
+	/* Periodic Rev Encap */
+	/* don't send periodically */
+	mddi_host_reg_out(CMD, MDDI_CMD_PERIODIC_REV_ENCAP);
+
+	pad_reg_val = mddi_host_reg_in(PAD_CTL);
+	if (pad_reg_val == 0) {
+		/* If we are turning on band gap, need to wait 5us before turning
+		 * on the rest of the PAD */
+		mddi_host_reg_out(PAD_CTL, 0x08000);
+		udelay(5);
+	}
+#ifdef T_MSM7200
+	/* Recommendation from PAD hw team */
+	mddi_host_reg_out(PAD_CTL, 0xa850a);
+#else
+	/* Recommendation from PAD hw team */
+	mddi_host_reg_out(PAD_CTL, 0xa850f);
+#endif
+
+	pad_reg_val = 0x00220020;
+
+#if defined(CONFIG_FB_MSM_MDP31) || defined(CONFIG_FB_MSM_MDP40)
+	mddi_host_reg_out(PAD_IO_CTL, 0x00320000);
+	mddi_host_reg_out(PAD_CAL, pad_reg_val);
+#endif
+
+	mddi_host_core_version = mddi_host_reg_inm(CORE_VER, 0xffff);
+
+#ifndef FEATURE_MDDI_DISABLE_REVERSE
+	if (mddi_host_core_version >= 8)
+		mddi_rev_ptr_workaround = FALSE;
+	pmhctl->rev_ptr_curr = pmhctl->rev_ptr_start;
+#endif
+
+	if ((mddi_host_core_version > 8) && (mddi_host_core_version < 0x19))
+		mddi_host_reg_out(TEST, 0x2);
+
+	/* Need an even number for counts */
+	mddi_host_reg_out(DRIVER_START_CNT, 0x60006);
+
+#ifndef T_MSM7500
+	/* Setup defaults for MDP related register */
+	mddi_host_reg_out(MDP_VID_FMT_DES, 0x5666);
+	mddi_host_reg_out(MDP_VID_PIX_ATTR, 0x00C3);
+	mddi_host_reg_out(MDP_VID_CLIENTID, 0);
+#endif
+
+	/* automatically hibernate after 1 empty subframe */
+	if (pmhctl->disable_hibernation)
+		mddi_host_reg_out(CMD, MDDI_CMD_HIBERNATE);
+	else
+		mddi_host_reg_out(CMD, MDDI_CMD_HIBERNATE | 1);
+
+	/* Bring up link if display (client) requests it */
+#ifdef MDDI_HOST_DISP_LISTEN
+	mddi_host_reg_out(CMD, MDDI_CMD_DISP_LISTEN);
+#else
+	mddi_host_reg_out(CMD, MDDI_CMD_DISP_IGNORE);
+#endif
+
+}
+
+void mddi_host_configure_interrupts(mddi_host_type host_idx, boolean enable)
+{
+	unsigned long flags;
+	mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]);
+
+	spin_lock_irqsave(&mddi_host_spin_lock, flags);
+
+	/* turn on HCLK to MDDI host core if it has been disabled */
+	mddi_host_enable_hclk();
+	/* Clear MDDI Interrupt enable reg */
+	mddi_host_reg_out(INTEN, 0);
+
+	spin_unlock_irqrestore(&mddi_host_spin_lock, flags);
+
+	if (enable) {
+		pmhctl->driver_state = MDDI_DRIVER_ENABLED;
+
+		if (host_idx == MDDI_HOST_PRIM) {
+			if (request_irq
+			    (INT_MDDI_PRI, mddi_pmdh_isr_proxy, IRQF_DISABLED,
+			     "PMDH", 0) != 0)
+				printk(KERN_ERR
+				       "a mddi: unable to request_irq\n");
+			else {
+				int_mddi_pri_flag = TRUE;
+				irq_enabled = 1;
+			}
+		} else {
+			if (request_irq
+			    (INT_MDDI_EXT, mddi_emdh_isr_proxy, IRQF_DISABLED,
+			     "EMDH", 0) != 0)
+				printk(KERN_ERR
+				       "b mddi: unable to request_irq\n");
+			else
+				int_mddi_ext_flag = TRUE;
+		}
+
+		/* Set MDDI Interrupt enable reg -- Enable Reverse data avail */
+#ifdef FEATURE_MDDI_DISABLE_REVERSE
+		mddi_host_reg_out(INTEN,
+				  MDDI_INT_ERROR_CONDITIONS |
+				  MDDI_INT_LINK_STATE_CHANGES);
+#else
+		/* Reverse Pointer register */
+		pmhctl->rev_ptr_written = FALSE;
+
+		mddi_host_reg_out(INTEN,
+				  MDDI_INT_REV_DATA_AVAIL |
+				  MDDI_INT_ERROR_CONDITIONS |
+				  MDDI_INT_LINK_STATE_CHANGES);
+		pmhctl->rtd_counter = mddi_rtd_frequency;
+		pmhctl->client_status_cnt = 0;
+#endif
+	} else {
+		if (pmhctl->driver_state == MDDI_DRIVER_ENABLED)
+			pmhctl->driver_state = MDDI_DRIVER_DISABLED;
+	}
+
+}
+
+/*
+ * mddi_host_client_cnt_reset:
+ * reset client_status_cnt to 0 to make sure host does not
+ * send RTD cmd to client right after resume before mddi
+ * client be powered up. this fix "MDDI RTD Failure" problem
+ */
+void mddi_host_client_cnt_reset(void)
+{
+	unsigned long flags;
+	mddi_host_cntl_type *pmhctl;
+
+	pmhctl = &(mhctl[MDDI_HOST_PRIM]);
+	spin_lock_irqsave(&mddi_host_spin_lock, flags);
+	pmhctl->client_status_cnt = 0;
+	spin_unlock_irqrestore(&mddi_host_spin_lock, flags);
+}
+
+static void mddi_host_powerup(mddi_host_type host_idx)
+{
+	mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]);
+
+	if (pmhctl->link_state != MDDI_LINK_DISABLED)
+		return;
+
+	/* enable IO_CLK and hclk to MDDI host core */
+	mddi_host_enable_io_clock();
+
+	mddi_host_initialize_registers(host_idx);
+	mddi_host_configure_interrupts(host_idx, TRUE);
+
+	pmhctl->link_state = MDDI_LINK_ACTIVATING;
+
+	/* Link activate command */
+	mddi_host_reg_out(CMD, MDDI_CMD_LINK_ACTIVE);
+
+#ifdef CLKRGM_MDDI_IO_CLOCK_IN_MHZ
+	MDDI_MSG_NOTICE("MDDI Host: Activating Link %d Mbps\n",
+			CLKRGM_MDDI_IO_CLOCK_IN_MHZ * 2);
+#else
+	MDDI_MSG_NOTICE("MDDI Host: Activating Link\n");
+#endif
+
+	/* Initialize the timer */
+	if (host_idx == MDDI_HOST_PRIM)
+		mddi_host_timer_service(0);
+}
+
+void mddi_send_fw_link_skew_cal(mddi_host_type host_idx)
+{
+	mddi_host_reg_out(CMD, MDDI_CMD_FW_LINK_SKEW_CAL);
+	MDDI_MSG_DEBUG("%s: Skew Calibration done!!\n", __func__);
+}
+
+
+void mddi_host_init(mddi_host_type host_idx)
+/* Write out the MDDI configuration registers */
+{
+	static boolean initialized = FALSE;
+	mddi_host_cntl_type *pmhctl;
+
+	if (host_idx >= MDDI_NUM_HOST_CORES) {
+		MDDI_MSG_ERR("Invalid host core index\n");
+		return;
+	}
+
+	if (!initialized) {
+		uint16 idx;
+		mddi_host_type host;
+
+		for (host = MDDI_HOST_PRIM; host < MDDI_NUM_HOST_CORES; host++) {
+			pmhctl = &(mhctl[host]);
+			initialized = TRUE;
+
+			pmhctl->llist_ptr =
+			    dma_alloc_coherent(NULL, MDDI_LLIST_POOL_SIZE,
+					       &(pmhctl->llist_dma_addr),
+					       GFP_KERNEL);
+			pmhctl->llist_dma_ptr =
+			    (mddi_linked_list_type *) (void *)pmhctl->
+			    llist_dma_addr;
+#ifdef FEATURE_MDDI_DISABLE_REVERSE
+			pmhctl->rev_data_buf = NULL;
+			if (pmhctl->llist_ptr == NULL)
+#else
+			mddi_rev_user.waiting = FALSE;
+			init_completion(&(mddi_rev_user.done_comp));
+			pmhctl->rev_data_buf =
+			    dma_alloc_coherent(NULL, MDDI_MAX_REV_DATA_SIZE,
+					       &(pmhctl->rev_data_dma_addr),
+					       GFP_KERNEL);
+			if ((pmhctl->llist_ptr == NULL)
+			    || (pmhctl->rev_data_buf == NULL))
+#endif
+			{
+				MDDI_MSG_CRIT
+				    ("unable to alloc non-cached memory\n");
+			}
+			llist_extern[host] = pmhctl->llist_ptr;
+			llist_dma_extern[host] = pmhctl->llist_dma_ptr;
+			llist_extern_notify[host] = pmhctl->llist_notify;
+
+			for (idx = 0; idx < UNASSIGNED_INDEX; idx++) {
+				init_completion(&
+						(pmhctl->llist_notify[idx].
+						 done_comp));
+			}
+			init_completion(&(pmhctl->mddi_llist_avail_comp));
+			spin_lock_init(&mddi_host_spin_lock);
+			pmhctl->mddi_waiting_for_llist_avail = FALSE;
+			pmhctl->mddi_rev_ptr_write_val =
+			    (uint32) (void *)(pmhctl->rev_data_dma_addr);
+			pmhctl->rev_ptr_start = (void *)pmhctl->rev_data_buf;
+
+			pmhctl->rev_pkt_size = MDDI_DEFAULT_REV_PKT_SIZE;
+			pmhctl->rev_state = MDDI_REV_IDLE;
+#ifdef IMAGE_MODEM_PROC
+			/* assume hibernation state is last state from APPS proc, so that
+			 * we don't reinitialize the host core */
+			pmhctl->link_state = MDDI_LINK_HIBERNATING;
+#else
+			pmhctl->link_state = MDDI_LINK_DISABLED;
+#endif
+			pmhctl->driver_state = MDDI_DRIVER_DISABLED;
+			pmhctl->disable_hibernation = FALSE;
+
+			/* initialize llist variables */
+			pmhctl->llist_info.transmitting_start_idx =
+			    UNASSIGNED_INDEX;
+			pmhctl->llist_info.transmitting_end_idx =
+			    UNASSIGNED_INDEX;
+			pmhctl->llist_info.waiting_start_idx = UNASSIGNED_INDEX;
+			pmhctl->llist_info.waiting_end_idx = UNASSIGNED_INDEX;
+			pmhctl->llist_info.reg_read_idx = UNASSIGNED_INDEX;
+			pmhctl->llist_info.next_free_idx =
+			    MDDI_FIRST_DYNAMIC_LLIST_IDX;
+			pmhctl->llist_info.reg_read_waiting = FALSE;
+
+			mddi_vsync_detect_enabled = FALSE;
+			mddi_gpio.polling_enabled = FALSE;
+
+			pmhctl->int_type.count = 0;
+			pmhctl->int_type.in_count = 0;
+			pmhctl->int_type.disp_req_count = 0;
+			pmhctl->int_type.state_change_count = 0;
+			pmhctl->int_type.ll_done_count = 0;
+			pmhctl->int_type.rev_avail_count = 0;
+			pmhctl->int_type.error_count = 0;
+			pmhctl->int_type.rev_encap_count = 0;
+			pmhctl->int_type.llist_ptr_write_1 = 0;
+			pmhctl->int_type.llist_ptr_write_2 = 0;
+
+			pmhctl->stats.fwd_crc_count = 0;
+			pmhctl->stats.rev_crc_count = 0;
+			pmhctl->stats.pri_underflow = 0;
+			pmhctl->stats.sec_underflow = 0;
+			pmhctl->stats.rev_overflow = 0;
+			pmhctl->stats.pri_overwrite = 0;
+			pmhctl->stats.sec_overwrite = 0;
+			pmhctl->stats.rev_overwrite = 0;
+			pmhctl->stats.dma_failure = 0;
+			pmhctl->stats.rtd_failure = 0;
+			pmhctl->stats.reg_read_failure = 0;
+#ifdef FEATURE_MDDI_UNDERRUN_RECOVERY
+			pmhctl->stats.pri_underrun_detected = 0;
+#endif
+
+			pmhctl->log_parms.rtd_cnt = 0;
+			pmhctl->log_parms.rev_enc_cnt = 0;
+			pmhctl->log_parms.vid_cnt = 0;
+			pmhctl->log_parms.reg_acc_cnt = 0;
+			pmhctl->log_parms.cli_stat_cnt = 0;
+			pmhctl->log_parms.cli_cap_cnt = 0;
+			pmhctl->log_parms.reg_read_cnt = 0;
+			pmhctl->log_parms.link_active_cnt = 0;
+			pmhctl->log_parms.link_hibernate_cnt = 0;
+			pmhctl->log_parms.fwd_crc_cnt = 0;
+			pmhctl->log_parms.rev_crc_cnt = 0;
+			pmhctl->log_parms.vsync_response_cnt = 0;
+
+			prev_parms[host_idx] = pmhctl->log_parms;
+			mddi_client_capability_pkt.packet_length = 0;
+		}
+
+#ifndef T_MSM7500
+		/* tell clock driver we are user of this PLL */
+		MDDI_HOST_ENABLE_IO_CLOCK;
+#endif
+	}
+
+	mddi_host_powerup(host_idx);
+	pmhctl = &(mhctl[host_idx]);
+}
+
+#ifdef CONFIG_FB_MSM_MDDI_AUTO_DETECT
+static uint32 mddi_client_id;
+
+uint32 mddi_get_client_id(void)
+{
+
+#ifndef FEATURE_MDDI_DISABLE_REVERSE
+	mddi_host_type host_idx = MDDI_HOST_PRIM;
+	static boolean client_detection_try = FALSE;
+	mddi_host_cntl_type *pmhctl;
+	unsigned long flags;
+	uint16 saved_rev_pkt_size;
+	int ret;
+
+	if (!client_detection_try) {
+		/* Toshiba display requires larger drive_lo value */
+		mddi_host_reg_out(DRIVE_LO, 0x0050);
+
+		pmhctl = &(mhctl[MDDI_HOST_PRIM]);
+
+		saved_rev_pkt_size = pmhctl->rev_pkt_size;
+
+		/* Increase Rev Encap Size */
+		pmhctl->rev_pkt_size = MDDI_CLIENT_CAPABILITY_REV_PKT_SIZE;
+		mddi_host_reg_out(REV_ENCAP_SZ, pmhctl->rev_pkt_size);
+
+		/* disable hibernation temporarily */
+		if (!pmhctl->disable_hibernation)
+			mddi_host_reg_out(CMD, MDDI_CMD_HIBERNATE);
+
+		mddi_rev_user.waiting = TRUE;
+		INIT_COMPLETION(mddi_rev_user.done_comp);
+
+		spin_lock_irqsave(&mddi_host_spin_lock, flags);
+
+		/* turn on clock(s), if they have been disabled */
+		mddi_host_enable_hclk();
+		mddi_host_enable_io_clock();
+
+		mddi_client_capability_request = TRUE;
+
+		if (pmhctl->rev_state == MDDI_REV_IDLE) {
+			/* attempt to send the reverse encapsulation now */
+			mddi_issue_reverse_encapsulation();
+		}
+		spin_unlock_irqrestore(&mddi_host_spin_lock, flags);
+
+		wait_for_completion_killable(&(mddi_rev_user.done_comp));
+
+		/* Set Rev Encap Size back to its original value */
+		pmhctl->rev_pkt_size = saved_rev_pkt_size;
+		mddi_host_reg_out(REV_ENCAP_SZ, pmhctl->rev_pkt_size);
+
+		/* reenable auto-hibernate */
+		if (!pmhctl->disable_hibernation)
+			mddi_host_reg_out(CMD, MDDI_CMD_HIBERNATE | 1);
+
+		mddi_host_reg_out(DRIVE_LO, 0x0032);
+		client_detection_try = TRUE;
+
+		mddi_client_id = (mddi_client_capability_pkt.Mfr_Name<<16) |
+				mddi_client_capability_pkt.Product_Code;
+
+		if (!mddi_client_id)
+			mddi_disable(1);
+
+		ret = mddi_client_power(mddi_client_id);
+		if (ret < 0)
+			MDDI_MSG_ERR("mddi_client_power return %d", ret);
+	}
+
+#endif
+
+	return mddi_client_id;
+}
+#endif
+
+void mddi_host_powerdown(mddi_host_type host_idx)
+{
+	mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]);
+
+	if (host_idx >= MDDI_NUM_HOST_CORES) {
+		MDDI_MSG_ERR("Invalid host core index\n");
+		return;
+	}
+
+	if (pmhctl->driver_state == MDDI_DRIVER_RESET) {
+		return;
+	}
+
+	if (host_idx == MDDI_HOST_PRIM) {
+		/* disable timer */
+		del_timer(&mddi_host_timer);
+	}
+
+	mddi_host_configure_interrupts(host_idx, FALSE);
+
+	/* turn on HCLK to MDDI host core if it has been disabled */
+	mddi_host_enable_hclk();
+
+	/* MDDI Reset command */
+	mddi_host_reg_out(CMD, MDDI_CMD_RESET);
+
+	/* Pad Control Register */
+	mddi_host_reg_out(PAD_CTL, 0x0);
+
+	/* disable IO_CLK and hclk to MDDI host core */
+	mddi_host_disable_io_clock();
+	mddi_host_disable_hclk();
+
+	pmhctl->link_state = MDDI_LINK_DISABLED;
+	pmhctl->driver_state = MDDI_DRIVER_RESET;
+
+	MDDI_MSG_NOTICE("MDDI Host: Disabling Link\n");
+
+}
+
+uint16 mddi_get_next_free_llist_item(mddi_host_type host_idx, boolean wait)
+{
+	unsigned long flags;
+	uint16 ret_idx;
+	boolean forced_wait = FALSE;
+	mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]);
+
+	ret_idx = pmhctl->llist_info.next_free_idx;
+
+	pmhctl->llist_info.next_free_idx++;
+	if (pmhctl->llist_info.next_free_idx >= MDDI_NUM_DYNAMIC_LLIST_ITEMS)
+		pmhctl->llist_info.next_free_idx = MDDI_FIRST_DYNAMIC_LLIST_IDX;
+	spin_lock_irqsave(&mddi_host_spin_lock, flags);
+	if (pmhctl->llist_notify[ret_idx].in_use) {
+		if (!wait) {
+			pmhctl->llist_info.next_free_idx = ret_idx;
+			ret_idx = UNASSIGNED_INDEX;
+		} else {
+			forced_wait = TRUE;
+			INIT_COMPLETION(pmhctl->mddi_llist_avail_comp);
+		}
+	}
+	spin_unlock_irqrestore(&mddi_host_spin_lock, flags);
+
+	if (forced_wait) {
+		wait_for_completion_killable(&
+						  (pmhctl->
+						   mddi_llist_avail_comp));
+		MDDI_MSG_ERR("task waiting on mddi llist item\n");
+	}
+
+	if (ret_idx != UNASSIGNED_INDEX) {
+		pmhctl->llist_notify[ret_idx].waiting = FALSE;
+		pmhctl->llist_notify[ret_idx].done_cb = NULL;
+		pmhctl->llist_notify[ret_idx].in_use = TRUE;
+		pmhctl->llist_notify[ret_idx].next_idx = UNASSIGNED_INDEX;
+	}
+
+	return ret_idx;
+}
+
+uint16 mddi_get_reg_read_llist_item(mddi_host_type host_idx, boolean wait)
+{
+#ifdef FEATURE_MDDI_DISABLE_REVERSE
+	MDDI_MSG_CRIT("No reverse link available\n");
+	(void)wait;
+	return FALSE;
+#else
+	unsigned long flags;
+	uint16 ret_idx;
+	boolean error = FALSE;
+	mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]);
+
+	spin_lock_irqsave(&mddi_host_spin_lock, flags);
+	if (pmhctl->llist_info.reg_read_idx != UNASSIGNED_INDEX) {
+		/* need to block here or is this an error condition? */
+		error = TRUE;
+		ret_idx = UNASSIGNED_INDEX;
+	}
+	spin_unlock_irqrestore(&mddi_host_spin_lock, flags);
+
+	if (!error) {
+		ret_idx = pmhctl->llist_info.reg_read_idx =
+		    mddi_get_next_free_llist_item(host_idx, wait);
+		/* clear the reg_read_waiting flag */
+		pmhctl->llist_info.reg_read_waiting = FALSE;
+	}
+
+	if (error)
+		MDDI_MSG_ERR("***** Reg read still in progress! ****\n");
+	return ret_idx;
+#endif
+
+}
+
+void mddi_queue_forward_packets(uint16 first_llist_idx,
+				uint16 last_llist_idx,
+				boolean wait,
+				mddi_llist_done_cb_type llist_done_cb,
+				mddi_host_type host_idx)
+{
+	unsigned long flags;
+	mddi_linked_list_type *llist;
+	mddi_linked_list_type *llist_dma;
+	mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]);
+
+	if ((first_llist_idx >= UNASSIGNED_INDEX) ||
+	    (last_llist_idx >= UNASSIGNED_INDEX)) {
+		MDDI_MSG_ERR("MDDI queueing invalid linked list\n");
+		return;
+	}
+
+	if (pmhctl->link_state == MDDI_LINK_DISABLED)
+		MDDI_MSG_CRIT("MDDI host powered down!\n");
+
+	llist = pmhctl->llist_ptr;
+	llist_dma = pmhctl->llist_dma_ptr;
+
+	/* clean cache so MDDI host can read data */
+	memory_barrier();
+
+	pmhctl->llist_notify[last_llist_idx].waiting = wait;
+	if (wait)
+		INIT_COMPLETION(pmhctl->llist_notify[last_llist_idx].done_comp);
+	pmhctl->llist_notify[last_llist_idx].done_cb = llist_done_cb;
+
+	spin_lock_irqsave(&mddi_host_spin_lock, flags);
+
+	if ((pmhctl->llist_info.transmitting_start_idx == UNASSIGNED_INDEX) &&
+	    (pmhctl->llist_info.waiting_start_idx == UNASSIGNED_INDEX) &&
+	    (pmhctl->rev_state == MDDI_REV_IDLE)) {
+		/* no packets are currently transmitting */
+#ifndef FEATURE_MDDI_DISABLE_REVERSE
+		if (first_llist_idx == pmhctl->llist_info.reg_read_idx) {
+			/* This is the special case where the packet is a register read. */
+			pmhctl->rev_state = MDDI_REV_REG_READ_ISSUED;
+			mddi_reg_read_retry = 0;
+			/* mddi_rev_reg_read_attempt = 1; */
+		}
+#endif
+		/* assign transmitting index values */
+		pmhctl->llist_info.transmitting_start_idx = first_llist_idx;
+		pmhctl->llist_info.transmitting_end_idx = last_llist_idx;
+
+		/* turn on clock(s), if they have been disabled */
+		mddi_host_enable_hclk();
+		mddi_host_enable_io_clock();
+		pmhctl->int_type.llist_ptr_write_1++;
+		/* Write to primary pointer register */
+		dma_coherent_pre_ops();
+		mddi_host_reg_out(PRI_PTR, &llist_dma[first_llist_idx]);
+
+		/* enable interrupt when complete */
+		mddi_host_reg_outm(INTEN, MDDI_INT_PRI_LINK_LIST_DONE,
+				   MDDI_INT_PRI_LINK_LIST_DONE);
+
+	} else if (pmhctl->llist_info.waiting_start_idx == UNASSIGNED_INDEX) {
+#ifndef FEATURE_MDDI_DISABLE_REVERSE
+		if (first_llist_idx == pmhctl->llist_info.reg_read_idx) {
+			/*
+			 * we have a register read to send but need to wait
+			 * for current reverse activity to end or there are
+			 * packets currently transmitting
+			 */
+			/* mddi_rev_reg_read_attempt = 0; */
+			pmhctl->llist_info.reg_read_waiting = TRUE;
+		}
+#endif
+
+		/* assign waiting index values */
+		pmhctl->llist_info.waiting_start_idx = first_llist_idx;
+		pmhctl->llist_info.waiting_end_idx = last_llist_idx;
+	} else {
+		uint16 prev_end_idx = pmhctl->llist_info.waiting_end_idx;
+#ifndef FEATURE_MDDI_DISABLE_REVERSE
+		if (first_llist_idx == pmhctl->llist_info.reg_read_idx) {
+			/*
+			 * we have a register read to send but need to wait
+			 * for current reverse activity to end or there are
+			 * packets currently transmitting
+			 */
+			/* mddi_rev_reg_read_attempt = 0; */
+			pmhctl->llist_info.reg_read_waiting = TRUE;
+		}
+#endif
+
+		llist = pmhctl->llist_ptr;
+
+		/* clear end flag in previous last packet */
+		llist[prev_end_idx].link_controller_flags = 0;
+		pmhctl->llist_notify[prev_end_idx].next_idx = first_llist_idx;
+
+		/* set the next_packet_pointer of the previous last packet */
+		llist[prev_end_idx].next_packet_pointer =
+		    (void *)(&llist_dma[first_llist_idx]);
+
+		/* clean cache so MDDI host can read data */
+		memory_barrier();
+
+		/* assign new waiting last index value */
+		pmhctl->llist_info.waiting_end_idx = last_llist_idx;
+	}
+
+	spin_unlock_irqrestore(&mddi_host_spin_lock, flags);
+
+}
+
+void mddi_host_write_pix_attr_reg(uint32 value)
+{
+	(void)value;
+}
+
+void mddi_queue_reverse_encapsulation(boolean wait)
+{
+#ifdef FEATURE_MDDI_DISABLE_REVERSE
+	MDDI_MSG_CRIT("No reverse link available\n");
+	(void)wait;
+#else
+	unsigned long flags;
+	boolean error = FALSE;
+	mddi_host_type host_idx = MDDI_HOST_PRIM;
+	mddi_host_cntl_type *pmhctl = &(mhctl[MDDI_HOST_PRIM]);
+
+	spin_lock_irqsave(&mddi_host_spin_lock, flags);
+
+	/* turn on clock(s), if they have been disabled */
+	mddi_host_enable_hclk();
+	mddi_host_enable_io_clock();
+
+	if (wait) {
+		if (!mddi_rev_user.waiting) {
+			mddi_rev_user.waiting = TRUE;
+			INIT_COMPLETION(mddi_rev_user.done_comp);
+		} else
+			error = TRUE;
+	}
+	mddi_rev_encap_user_request = TRUE;
+
+	if (pmhctl->rev_state == MDDI_REV_IDLE) {
+		/* attempt to send the reverse encapsulation now */
+		mddi_host_type orig_host_idx = mddi_curr_host;
+		mddi_curr_host = host_idx;
+		mddi_issue_reverse_encapsulation();
+		mddi_curr_host = orig_host_idx;
+	}
+	spin_unlock_irqrestore(&mddi_host_spin_lock, flags);
+
+	if (error) {
+		MDDI_MSG_ERR("Reverse Encap request already in progress\n");
+	} else if (wait)
+		wait_for_completion_killable(&(mddi_rev_user.done_comp));
+#endif
+}
+
+/* ISR to be executed */
+boolean mddi_set_rev_handler(mddi_rev_handler_type handler, uint16 pkt_type)
+{
+#ifdef FEATURE_MDDI_DISABLE_REVERSE
+	MDDI_MSG_CRIT("No reverse link available\n");
+	(void)handler;
+	(void)pkt_type;
+	return (FALSE);
+#else
+	unsigned long flags;
+	uint16 hdlr;
+	boolean handler_set = FALSE;
+	boolean overwrite = FALSE;
+	mddi_host_type host_idx = MDDI_HOST_PRIM;
+	mddi_host_cntl_type *pmhctl = &(mhctl[MDDI_HOST_PRIM]);
+
+	/* Disable interrupts */
+	spin_lock_irqsave(&mddi_host_spin_lock, flags);
+
+	for (hdlr = 0; hdlr < MAX_MDDI_REV_HANDLERS; hdlr++) {
+		if (mddi_rev_pkt_handler[hdlr].pkt_type == pkt_type) {
+			mddi_rev_pkt_handler[hdlr].handler = handler;
+			if (handler == NULL) {
+				/* clearing handler from table */
+				mddi_rev_pkt_handler[hdlr].pkt_type =
+				    INVALID_PKT_TYPE;
+				handler_set = TRUE;
+				if (pkt_type == 0x10) {	/* video stream packet */
+					/* ensure HCLK on to MDDI host core before register write */
+					mddi_host_enable_hclk();
+					/* No longer getting video, so reset rev encap size to default */
+					pmhctl->rev_pkt_size =
+					    MDDI_DEFAULT_REV_PKT_SIZE;
+					mddi_host_reg_out(REV_ENCAP_SZ,
+							  pmhctl->rev_pkt_size);
+				}
+			} else {
+				/* already a handler for this packet */
+				overwrite = TRUE;
+			}
+			break;
+		}
+	}
+	if ((hdlr >= MAX_MDDI_REV_HANDLERS) && (handler != NULL)) {
+		/* assigning new handler */
+		for (hdlr = 0; hdlr < MAX_MDDI_REV_HANDLERS; hdlr++) {
+			if (mddi_rev_pkt_handler[hdlr].pkt_type ==
+			    INVALID_PKT_TYPE) {
+				if ((pkt_type == 0x10) &&	/* video stream packet */
+				    (pmhctl->rev_pkt_size <
+				     MDDI_VIDEO_REV_PKT_SIZE)) {
+					/* ensure HCLK on to MDDI host core before register write */
+					mddi_host_enable_hclk();
+					/* Increase Rev Encap Size */
+					pmhctl->rev_pkt_size =
+					    MDDI_VIDEO_REV_PKT_SIZE;
+					mddi_host_reg_out(REV_ENCAP_SZ,
+							  pmhctl->rev_pkt_size);
+				}
+				mddi_rev_pkt_handler[hdlr].handler = handler;
+				mddi_rev_pkt_handler[hdlr].pkt_type = pkt_type;
+				handler_set = TRUE;
+				break;
+			}
+		}
+	}
+
+	/* Restore interrupts */
+	spin_unlock_irqrestore(&mddi_host_spin_lock, flags);
+
+	if (overwrite)
+		MDDI_MSG_ERR("Overwriting previous rev packet handler\n");
+
+	return handler_set;
+
+#endif
+}				/* mddi_set_rev_handler */
+
+void mddi_host_disable_hibernation(boolean disable)
+{
+	mddi_host_type host_idx = MDDI_HOST_PRIM;
+	mddi_host_cntl_type *pmhctl = &(mhctl[MDDI_HOST_PRIM]);
+
+	if (disable) {
+		pmhctl->disable_hibernation = TRUE;
+		/* hibernation will be turned off by isr next time it is entered */
+	} else {
+		if (pmhctl->disable_hibernation) {
+			unsigned long flags;
+			spin_lock_irqsave(&mddi_host_spin_lock, flags);
+			if (!MDDI_HOST_IS_HCLK_ON)
+				MDDI_HOST_ENABLE_HCLK;
+			mddi_host_reg_out(CMD, MDDI_CMD_HIBERNATE | 1);
+			spin_unlock_irqrestore(&mddi_host_spin_lock, flags);
+			pmhctl->disable_hibernation = FALSE;
+		}
+	}
+}
+
+void mddi_mhctl_remove(mddi_host_type host_idx)
+{
+	mddi_host_cntl_type *pmhctl;
+
+	pmhctl = &(mhctl[host_idx]);
+
+	dma_free_coherent(NULL, MDDI_LLIST_POOL_SIZE, (void *)pmhctl->llist_ptr,
+			  pmhctl->llist_dma_addr);
+
+	dma_free_coherent(NULL, MDDI_MAX_REV_DATA_SIZE,
+			  (void *)pmhctl->rev_data_buf,
+			  pmhctl->rev_data_dma_addr);
+}
diff --git a/drivers/video/msm/mddihosti.h b/drivers/video/msm/mddihosti.h
new file mode 100644
index 0000000..166d15c
--- /dev/null
+++ b/drivers/video/msm/mddihosti.h
@@ -0,0 +1,552 @@
+/* Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef MDDIHOSTI_H
+#define MDDIHOSTI_H
+
+#include "msm_fb.h"
+#include "mddihost.h"
+#include <linux/clk.h>
+
+/* Register offsets in MDDI, applies to both msm_pmdh_base and
+ * (u32)msm_emdh_base. */
+#define MDDI_CMD   		0x0000
+#define MDDI_VERSION   		0x0004
+#define MDDI_PRI_PTR		0x0008
+#define MDDI_BPS		0x0010
+#define MDDI_SPM		0x0014
+#define MDDI_INT		0x0018
+#define MDDI_INTEN		0x001c
+#define MDDI_REV_PTR		0x0020
+#define MDDI_REV_SIZE		0x0024
+#define MDDI_STAT		0x0028
+#define MDDI_REV_RATE_DIV	0x002c
+#define MDDI_REV_CRC_ERR	0x0030
+#define MDDI_TA1_LEN		0x0034
+#define MDDI_TA2_LEN		0x0038
+#define MDDI_TEST		0x0040
+#define MDDI_REV_PKT_CNT	0x0044
+#define MDDI_DRIVE_HI		0x0048
+#define MDDI_DRIVE_LO		0x004c
+#define MDDI_DISP_WAKE		0x0050
+#define MDDI_REV_ENCAP_SZ	0x0054
+#define MDDI_RTD_VAL		0x0058
+#define MDDI_PAD_CTL		0x0068
+#define MDDI_DRIVER_START_CNT	0x006c
+#define MDDI_CORE_VER		0x008c
+#define MDDI_FIFO_ALLOC         0x0090
+#define MDDI_PAD_IO_CTL         0x00a0
+#define MDDI_PAD_CAL            0x00a4
+
+#ifdef ENABLE_MDDI_MULTI_READ_WRITE
+#define MDDI_HOST_MAX_CLIENT_REG_IN_SAME_ADDR 128
+#else
+#define MDDI_HOST_MAX_CLIENT_REG_IN_SAME_ADDR 1
+#endif
+
+extern int32 mddi_client_type;
+extern u32 mddi_msg_level;
+
+/* No longer need to write to clear these registers */
+#define xxxx_mddi_host_reg_outm(reg, mask, val)  \
+do { \
+	if (host_idx == MDDI_HOST_PRIM) \
+		mddi_host_reg_outm_pmdh(reg, mask, val); \
+	else \
+		mddi_host_reg_outm_emdh(reg, mask, val); \
+} while (0)
+
+#define mddi_host_reg_outm(reg, mask, val) \
+do { \
+	unsigned long __addr; \
+	if (host_idx == MDDI_HOST_PRIM) \
+		__addr = (u32)msm_pmdh_base + MDDI_##reg; \
+	else \
+		__addr = (u32)msm_emdh_base + MDDI_##reg; \
+	writel((readl(__addr) & ~(mask)) | ((val) & (mask)), __addr); \
+} while (0)
+
+#define xxxx_mddi_host_reg_out(reg, val) \
+do { \
+	if (host_idx == MDDI_HOST_PRIM)  \
+		mddi_host_reg_out_pmdh(reg, val); \
+	else \
+		mddi_host_reg_out_emdh(reg, val); \
+	} while (0)
+
+#define mddi_host_reg_out(reg, val) \
+do { \
+	if (host_idx == MDDI_HOST_PRIM) \
+		writel(val, (u32)msm_pmdh_base + MDDI_##reg); \
+	else \
+		writel(val, (u32)msm_emdh_base + MDDI_##reg); \
+} while (0)
+
+#define xxxx_mddi_host_reg_in(reg)  \
+  ((host_idx) ? \
+     mddi_host_reg_in_emdh(reg) : mddi_host_reg_in_pmdh(reg));
+
+#define mddi_host_reg_in(reg) \
+((host_idx) ? \
+	readl((u32)msm_emdh_base + MDDI_##reg) : \
+	readl((u32)msm_pmdh_base + MDDI_##reg)) \
+
+#define xxxx_mddi_host_reg_inm(reg, mask)  \
+  ((host_idx) ? \
+    mddi_host_reg_inm_emdh(reg, mask) : \
+    mddi_host_reg_inm_pmdh(reg, mask);)
+
+#define mddi_host_reg_inm(reg, mask) \
+((host_idx) ? \
+	readl((u32)msm_emdh_base + MDDI_##reg) & (mask) : \
+	readl((u32)msm_pmdh_base + MDDI_##reg) & (mask)) \
+
+/* Using non-cacheable pmem, so do nothing */
+#define mddi_invalidate_cache_lines(addr_start, num_bytes)
+/*
+ * Using non-cacheable pmem, so do nothing with cache
+ * but, ensure write goes out to memory
+ */
+#define mddi_flush_cache_lines(addr_start, num_bytes)  \
+    (void) addr_start; \
+    (void) num_bytes;  \
+    memory_barrier()
+
+/* Since this translates to Remote Procedure Calls to check on clock status
+* just use a local variable to keep track of io_clock */
+#define MDDI_HOST_IS_IO_CLOCK_ON mddi_host_io_clock_on
+#define MDDI_HOST_ENABLE_IO_CLOCK
+#define MDDI_HOST_DISABLE_IO_CLOCK
+#define MDDI_HOST_IS_HCLK_ON mddi_host_hclk_on
+#define MDDI_HOST_ENABLE_HCLK
+#define MDDI_HOST_DISABLE_HCLK
+#define FEATURE_MDDI_HOST_IO_CLOCK_CONTROL_DISABLE
+#define FEATURE_MDDI_HOST_HCLK_CONTROL_DISABLE
+
+#define TRAMP_MDDI_HOST_ISR TRAMP_MDDI_PRI_ISR
+#define TRAMP_MDDI_HOST_EXT_ISR TRAMP_MDDI_EXT_ISR
+#define MDP_LINE_COUNT_BMSK  0x3ff
+#define MDP_SYNC_STATUS  0x000c
+#define MDP_LINE_COUNT      \
+(readl(msm_mdp_base + MDP_SYNC_STATUS) & MDP_LINE_COUNT_BMSK)
+
+/* MDP sends 256 pixel packets, so lower value hibernates more without
+* significantly increasing latency of waiting for next subframe */
+#define MDDI_HOST_BYTES_PER_SUBFRAME  0x3C00
+
+#if defined(CONFIG_FB_MSM_MDP31) || defined(CONFIG_FB_MSM_MDP40)
+#define MDDI_HOST_TA2_LEN       0x001a
+#define MDDI_HOST_REV_RATE_DIV  0x0004
+#else
+#define MDDI_HOST_TA2_LEN       0x000c
+#define MDDI_HOST_REV_RATE_DIV  0x0002
+#endif
+
+#define MDDI_MSG_EMERG(msg, ...)    \
+	if (mddi_msg_level > 0)  \
+		printk(KERN_EMERG msg, ## __VA_ARGS__);
+#define MDDI_MSG_ALERT(msg, ...)    \
+	if (mddi_msg_level > 1)  \
+		printk(KERN_ALERT msg, ## __VA_ARGS__);
+#define MDDI_MSG_CRIT(msg, ...)    \
+	if (mddi_msg_level > 2)  \
+		printk(KERN_CRIT msg, ## __VA_ARGS__);
+#define MDDI_MSG_ERR(msg, ...)    \
+	if (mddi_msg_level > 3)  \
+		printk(KERN_ERR msg, ## __VA_ARGS__);
+#define MDDI_MSG_WARNING(msg, ...)    \
+	if (mddi_msg_level > 4)  \
+		printk(KERN_WARNING msg, ## __VA_ARGS__);
+#define MDDI_MSG_NOTICE(msg, ...)    \
+	if (mddi_msg_level > 5)  \
+		printk(KERN_NOTICE msg, ## __VA_ARGS__);
+#define MDDI_MSG_INFO(msg, ...)    \
+	if (mddi_msg_level > 6)  \
+		printk(KERN_INFO msg, ## __VA_ARGS__);
+#define MDDI_MSG_DEBUG(msg, ...)    \
+	if (mddi_msg_level > 7)  \
+		printk(KERN_DEBUG msg, ## __VA_ARGS__);
+
+#define GCC_PACKED __attribute__((packed))
+typedef struct GCC_PACKED {
+	uint16 packet_length;
+	/* total # of bytes in the packet not including
+		the packet_length field. */
+
+	uint16 packet_type;
+	/* A Packet Type of 70 identifies the packet as
+		a Client status Packet. */
+
+	uint16 bClient_ID;
+	/* This field is reserved for future use and shall
+		be set to zero. */
+
+} mddi_rev_packet_type;
+
+typedef struct GCC_PACKED {
+	uint16 packet_length;
+	/* total # of bytes in the packet not including
+		the packet_length field. */
+
+	uint16 packet_type;
+	/* A Packet Type of 70 identifies the packet as
+		a Client status Packet. */
+
+	uint16 bClient_ID;
+	/* This field is reserved for future use and shall
+		be set to zero. */
+
+	uint16 reverse_link_request;
+	/* 16 bit unsigned integer with number of bytes client
+		needs in the * reverse encapsulation message
+		to transmit data. */
+
+	uint8 crc_error_count;
+	uint8 capability_change;
+	uint16 graphics_busy_flags;
+
+	uint16 parameter_CRC;
+	/* 16-bit CRC of all the bytes in the packet
+		including Packet Length. */
+
+} mddi_client_status_type;
+
+typedef struct GCC_PACKED {
+	uint16 packet_length;
+	/* total # of bytes in the packet not including
+		the packet_length field. */
+
+	uint16 packet_type;
+	/* A Packet Type of 66 identifies the packet as
+		a Client Capability Packet. */
+
+	uint16 bClient_ID;
+	/* This field is reserved for future use and
+		shall be set to zero. */
+
+	uint16 Protocol_Version;
+	uint16 Minimum_Protocol_Version;
+	uint16 Data_Rate_Capability;
+	uint8 Interface_Type_Capability;
+	uint8 Number_of_Alt_Displays;
+	uint16 PostCal_Data_Rate;
+	uint16 Bitmap_Width;
+	uint16 Bitmap_Height;
+	uint16 Display_Window_Width;
+	uint16 Display_Window_Height;
+	uint32 Color_Map_Size;
+	uint16 Color_Map_RGB_Width;
+	uint16 RGB_Capability;
+	uint8 Monochrome_Capability;
+	uint8 Reserved_1;
+	uint16 Y_Cb_Cr_Capability;
+	uint16 Bayer_Capability;
+	uint16 Alpha_Cursor_Image_Planes;
+	uint32 Client_Feature_Capability_Indicators;
+	uint8 Maximum_Video_Frame_Rate_Capability;
+	uint8 Minimum_Video_Frame_Rate_Capability;
+	uint16 Minimum_Sub_frame_Rate;
+	uint16 Audio_Buffer_Depth;
+	uint16 Audio_Channel_Capability;
+	uint16 Audio_Sample_Rate_Capability;
+	uint8 Audio_Sample_Resolution;
+	uint8 Mic_Audio_Sample_Resolution;
+	uint16 Mic_Sample_Rate_Capability;
+	uint8 Keyboard_Data_Format;
+	uint8 pointing_device_data_format;
+	uint16 content_protection_type;
+	uint16 Mfr_Name;
+	uint16 Product_Code;
+	uint16 Reserved_3;
+	uint32 Serial_Number;
+	uint8 Week_of_Manufacture;
+	uint8 Year_of_Manufacture;
+
+	uint16 parameter_CRC;
+	/* 16-bit CRC of all the bytes in the packet including Packet Length. */
+
+} mddi_client_capability_type;
+
+typedef struct GCC_PACKED {
+	uint16 packet_length;
+	/* total # of bytes in the packet not including the packet_length field. */
+
+	uint16 packet_type;
+	/* A Packet Type of 16 identifies the packet as a Video Stream Packet. */
+
+	uint16 bClient_ID;
+	/* This field is reserved for future use and shall be set to zero. */
+
+	uint16 video_data_format_descriptor;
+	/* format of each pixel in the Pixel Data in the present stream in the
+	 * present packet.
+	 * If bits [15:13] = 000 monochrome
+	 * If bits [15:13] = 001 color pixels (palette).
+	 * If bits [15:13] = 010 color pixels in raw RGB
+	 * If bits [15:13] = 011 data in 4:2:2 Y Cb Cr format
+	 * If bits [15:13] = 100 Bayer pixels
+	 */
+
+	uint16 pixel_data_attributes;
+	/* interpreted as follows:
+	 * Bits [1:0] = 11  pixel data is displayed to both eyes
+	 * Bits [1:0] = 10  pixel data is routed to the left eye only.
+	 * Bits [1:0] = 01  pixel data is routed to the right eye only.
+	 * Bits [1:0] = 00  pixel data is routed to the alternate display.
+	 * Bit 2 is 0  Pixel Data is in the standard progressive format.
+	 * Bit 2 is 1  Pixel Data is in interlace format.
+	 * Bit 3 is 0  Pixel Data is in the standard progressive format.
+	 * Bit 3 is 1  Pixel Data is in alternate pixel format.
+	 * Bit 4 is 0  Pixel Data is to or from the display frame buffer.
+	 * Bit 4 is 1  Pixel Data is to or from the camera.
+	 * Bit 5 is 0  pixel data contains the next consecutive row of pixels.
+	 * Bit 5 is 1  X Left Edge, Y Top Edge, X Right Edge, Y Bottom Edge,
+	 *             X Start, and Y Start parameters are not defined and
+	 *             shall be ignored by the client.
+	 * Bits [7:6] = 01  Pixel data is written to the offline image buffer.
+	 * Bits [7:6] = 00  Pixel data is written to the buffer to refresh display.
+	 * Bits [7:6] = 11  Pixel data is written to all image buffers.
+	 * Bits [7:6] = 10  Invalid. Reserved for future use.
+	 * Bits 8 through 11 alternate display number.
+	 * Bits 12 through 14 are reserved for future use and shall be set to zero.
+	 * Bit 15 is 1 the row of pixels is the last row of pixels in a frame.
+	 */
+
+	uint16 x_left_edge;
+	uint16 y_top_edge;
+	/* X,Y coordinate of the top left edge of the screen window */
+
+	uint16 x_right_edge;
+	uint16 y_bottom_edge;
+	/*  X,Y coordinate of the bottom right edge of the window being updated. */
+
+	uint16 x_start;
+	uint16 y_start;
+	/*  (X Start, Y Start) is the first pixel in the Pixel Data field below. */
+
+	uint16 pixel_count;
+	/*  number of pixels in the Pixel Data field below. */
+
+	uint16 parameter_CRC;
+	/*  16-bit CRC of all bytes from the Packet Length to the Pixel Count. */
+
+	uint16 reserved;
+	/* 16-bit variable to make structure align on 4 byte boundary */
+
+} mddi_video_stream_packet_type;
+
+typedef struct GCC_PACKED {
+	uint16 packet_length;
+	/* total # of bytes in the packet not including the packet_length field. */
+
+	uint16 packet_type;
+	/* A Packet Type of 146 identifies the packet as a Register Access Packet. */
+
+	uint16 bClient_ID;
+	/* This field is reserved for future use and shall be set to zero. */
+
+	uint16 read_write_info;
+	/* Bits 13:0  a 14-bit unsigned integer that specifies the number of
+	 *            32-bit Register Data List items to be transferred in the
+	 *            Register Data List field.
+	 * Bits[15:14] = 00  Write to register(s);
+	 * Bits[15:14] = 10  Read from register(s);
+	 * Bits[15:14] = 11  Response to a Read.
+	 * Bits[15:14] = 01  this value is reserved for future use. */
+
+	uint32 register_address;
+	/* the register address that is to be written to or read from. */
+
+	uint16 parameter_CRC;
+	/* 16-bit CRC of all bytes from the Packet Length to the Register Address. */
+
+	uint32 register_data_list[MDDI_HOST_MAX_CLIENT_REG_IN_SAME_ADDR];
+	/* list of 4-byte register data values for/from client registers */
+	/* For multi-read/write, 512(128 * 4) bytes of data available */
+
+} mddi_register_access_packet_type;
+
+typedef union GCC_PACKED {
+	mddi_video_stream_packet_type video_pkt;
+	mddi_register_access_packet_type register_pkt;
+#ifdef ENABLE_MDDI_MULTI_READ_WRITE
+	/* add 1008 byte pad to ensure 1024 byte llist struct, that can be
+	 * manipulated easily with cache */
+	uint32 alignment_pad[252];	/* 1008 bytes */
+#else
+	/* add 48 byte pad to ensure 64 byte llist struct, that can be
+	 * manipulated easily with cache */
+	uint32 alignment_pad[12];	/* 48 bytes */
+#endif
+} mddi_packet_header_type;
+
+typedef struct GCC_PACKED mddi_host_llist_struct {
+	uint16 link_controller_flags;
+	uint16 packet_header_count;
+	uint16 packet_data_count;
+	void *packet_data_pointer;
+	struct mddi_host_llist_struct *next_packet_pointer;
+	uint16 reserved;
+	mddi_packet_header_type packet_header;
+} mddi_linked_list_type;
+
+typedef struct {
+	struct completion done_comp;
+	mddi_llist_done_cb_type done_cb;
+	uint16 next_idx;
+	boolean waiting;
+	boolean in_use;
+} mddi_linked_list_notify_type;
+
+#ifdef ENABLE_MDDI_MULTI_READ_WRITE
+#define MDDI_LLIST_POOL_SIZE 0x10000
+#else
+#define MDDI_LLIST_POOL_SIZE 0x1000
+#endif
+#define MDDI_MAX_NUM_LLIST_ITEMS (MDDI_LLIST_POOL_SIZE / \
+		 sizeof(mddi_linked_list_type))
+#define UNASSIGNED_INDEX MDDI_MAX_NUM_LLIST_ITEMS
+#define MDDI_FIRST_DYNAMIC_LLIST_IDX 0
+
+/* Static llist items can be used for applications that frequently send
+ * the same set of packets using the linked list interface. */
+/* Here we configure for 6 static linked list items:
+ *  The 1st is used for a the adaptive backlight setting.
+ *  and the remaining 5 are used for sending window adjustments for
+ *  MDDI clients that need windowing info sent separate from video
+ *  packets. */
+#define MDDI_NUM_STATIC_ABL_ITEMS 1
+#define MDDI_NUM_STATIC_WINDOW_ITEMS 5
+#define MDDI_NUM_STATIC_LLIST_ITEMS (MDDI_NUM_STATIC_ABL_ITEMS + \
+				MDDI_NUM_STATIC_WINDOW_ITEMS)
+#define MDDI_NUM_DYNAMIC_LLIST_ITEMS (MDDI_MAX_NUM_LLIST_ITEMS - \
+				MDDI_NUM_STATIC_LLIST_ITEMS)
+
+#define MDDI_FIRST_STATIC_LLIST_IDX  MDDI_NUM_DYNAMIC_LLIST_ITEMS
+#define MDDI_FIRST_STATIC_ABL_IDX  MDDI_FIRST_STATIC_LLIST_IDX
+#define MDDI_FIRST_STATIC_WINDOW_IDX  (MDDI_FIRST_STATIC_LLIST_IDX + \
+				MDDI_NUM_STATIC_ABL_ITEMS)
+
+/* GPIO registers */
+#define VSYNC_WAKEUP_REG          0x80
+#define GPIO_REG                  0x81
+#define GPIO_OUTPUT_REG           0x82
+#define GPIO_INTERRUPT_REG        0x83
+#define GPIO_INTERRUPT_ENABLE_REG 0x84
+#define GPIO_POLARITY_REG         0x85
+
+/* Interrupt Bits */
+#define MDDI_INT_PRI_PTR_READ       0x0001
+#define MDDI_INT_SEC_PTR_READ       0x0002
+#define MDDI_INT_REV_DATA_AVAIL     0x0004
+#define MDDI_INT_DISP_REQ           0x0008
+#define MDDI_INT_PRI_UNDERFLOW      0x0010
+#define MDDI_INT_SEC_UNDERFLOW      0x0020
+#define MDDI_INT_REV_OVERFLOW       0x0040
+#define MDDI_INT_CRC_ERROR          0x0080
+#define MDDI_INT_MDDI_IN            0x0100
+#define MDDI_INT_PRI_OVERWRITE      0x0200
+#define MDDI_INT_SEC_OVERWRITE      0x0400
+#define MDDI_INT_REV_OVERWRITE      0x0800
+#define MDDI_INT_DMA_FAILURE        0x1000
+#define MDDI_INT_LINK_ACTIVE        0x2000
+#define MDDI_INT_IN_HIBERNATION     0x4000
+#define MDDI_INT_PRI_LINK_LIST_DONE 0x8000
+#define MDDI_INT_SEC_LINK_LIST_DONE 0x10000
+#define MDDI_INT_NO_CMD_PKTS_PEND   0x20000
+#define MDDI_INT_RTD_FAILURE        0x40000
+
+#define MDDI_INT_ERROR_CONDITIONS ( \
+	MDDI_INT_PRI_UNDERFLOW | MDDI_INT_SEC_UNDERFLOW | \
+	MDDI_INT_REV_OVERFLOW | MDDI_INT_CRC_ERROR | \
+	MDDI_INT_PRI_OVERWRITE | MDDI_INT_SEC_OVERWRITE | \
+	MDDI_INT_RTD_FAILURE | \
+	MDDI_INT_REV_OVERWRITE | MDDI_INT_DMA_FAILURE)
+
+#define MDDI_INT_LINK_STATE_CHANGES ( \
+	MDDI_INT_LINK_ACTIVE | MDDI_INT_IN_HIBERNATION)
+
+/* Status Bits */
+#define MDDI_STAT_LINK_ACTIVE        0x0001
+#define MDDI_STAT_NEW_REV_PTR        0x0002
+#define MDDI_STAT_NEW_PRI_PTR        0x0004
+#define MDDI_STAT_NEW_SEC_PTR        0x0008
+#define MDDI_STAT_IN_HIBERNATION     0x0010
+#define MDDI_STAT_PRI_LINK_LIST_DONE 0x0020
+#define MDDI_STAT_SEC_LINK_LIST_DONE 0x0040
+#define MDDI_STAT_PENDING_TIMING_PKT 0x0080
+#define MDDI_STAT_PENDING_REV_ENCAP  0x0100
+#define MDDI_STAT_PENDING_POWERDOWN  0x0200
+#define MDDI_STAT_RTD_MEAS_FAIL      0x0800
+#define MDDI_STAT_CLIENT_WAKEUP_REQ  0x1000
+
+/* Command Bits */
+#define MDDI_CMD_POWERDOWN           0x0100
+#define MDDI_CMD_POWERUP             0x0200
+#define MDDI_CMD_HIBERNATE           0x0300
+#define MDDI_CMD_RESET               0x0400
+#define MDDI_CMD_DISP_IGNORE         0x0501
+#define MDDI_CMD_DISP_LISTEN         0x0500
+#define MDDI_CMD_SEND_REV_ENCAP      0x0600
+#define MDDI_CMD_GET_CLIENT_CAP      0x0601
+#define MDDI_CMD_GET_CLIENT_STATUS   0x0602
+#define MDDI_CMD_SEND_RTD            0x0700
+#define MDDI_CMD_LINK_ACTIVE         0x0900
+#define MDDI_CMD_PERIODIC_REV_ENCAP  0x0A00
+#define MDDI_CMD_FW_LINK_SKEW_CAL    0x0D00
+
+extern void mddi_host_init(mddi_host_type host);
+extern void mddi_host_powerdown(mddi_host_type host);
+extern uint16 mddi_get_next_free_llist_item(mddi_host_type host, boolean wait);
+extern uint16 mddi_get_reg_read_llist_item(mddi_host_type host, boolean wait);
+extern void mddi_queue_forward_packets(uint16 first_llist_idx,
+				       uint16 last_llist_idx,
+				       boolean wait,
+				       mddi_llist_done_cb_type llist_done_cb,
+				       mddi_host_type host);
+
+extern void mddi_host_write_pix_attr_reg(uint32 value);
+extern void mddi_client_lcd_gpio_poll(uint32 poll_reg_val);
+extern void mddi_client_lcd_vsync_detected(boolean detected);
+extern void mddi_host_disable_hibernation(boolean disable);
+
+extern mddi_linked_list_type *llist_extern[];
+extern mddi_linked_list_type *llist_dma_extern[];
+extern mddi_linked_list_notify_type *llist_extern_notify[];
+extern struct timer_list mddi_host_timer;
+
+typedef struct {
+	uint16 transmitting_start_idx;
+	uint16 transmitting_end_idx;
+	uint16 waiting_start_idx;
+	uint16 waiting_end_idx;
+	uint16 reg_read_idx;
+	uint16 next_free_idx;
+	boolean reg_read_waiting;
+} mddi_llist_info_type;
+
+extern mddi_llist_info_type mddi_llist;
+
+#define MDDI_GPIO_DEFAULT_POLLING_INTERVAL 200
+typedef struct {
+	uint32 polling_reg;
+	uint32 polling_val;
+	uint32 polling_interval;
+	boolean polling_enabled;
+} mddi_gpio_info_type;
+
+uint32 mddi_get_client_id(void);
+void mddi_mhctl_remove(mddi_host_type host_idx);
+void mddi_host_timer_service(unsigned long data);
+void mddi_host_client_cnt_reset(void);
+#endif /* MDDIHOSTI_H */
diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c
index cb2ddf1..7fd2d91 100644
--- a/drivers/video/msm/mdp.c
+++ b/drivers/video/msm/mdp.c
@@ -2,7 +2,7 @@
  *
  * MSM MDP Interface (used by framebuffer core)
  *
- * Copyright (C) 2007 QUALCOMM Incorporated
+ * Copyright (c) 2007-2012, Code Aurora Forum. All rights reserved.
  * Copyright (C) 2007 Google Incorporated
  *
  * This software is licensed under the terms of the GNU General Public
@@ -15,509 +15,2700 @@
  * GNU General Public License for more details.
  */
 
+#include <linux/module.h>
 #include <linux/kernel.h>
-#include <linux/fb.h>
-#include <linux/msm_mdp.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
 #include <linux/interrupt.h>
-#include <linux/wait.h>
+#include <linux/spinlock.h>
+#include <linux/hrtimer.h>
 #include <linux/clk.h>
-#include <linux/file.h>
-#include <linux/major.h>
-#include <linux/slab.h>
+#include <mach/hardware.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <linux/semaphore.h>
+#include <linux/uaccess.h>
+#include <mach/clk.h>
+#include "mdp.h"
+#include "msm_fb.h"
+#ifdef CONFIG_FB_MSM_MDP40
+#include "mdp4.h"
+#endif
+#include "mipi_dsi.h"
 
-#include <mach/msm_iomap.h>
-#include <mach/msm_fb.h>
-#include <linux/platform_device.h>
-#include <linux/export.h>
+uint32 mdp4_extn_disp;
 
-#include "mdp_hw.h"
+static struct clk *mdp_clk;
+static struct clk *mdp_pclk;
+static struct clk *mdp_lut_clk;
+int mdp_rev;
 
-struct class *mdp_class;
+static struct platform_device *mdp_init_pdev;
+static struct regulator *footswitch;
+static unsigned int mdp_footswitch_on;
 
-#define MDP_CMD_DEBUG_ACCESS_BASE (0x10000)
+struct completion mdp_ppp_comp;
+struct semaphore mdp_ppp_mutex;
+struct semaphore mdp_pipe_ctrl_mutex;
 
-static uint16_t mdp_default_ccs[] = {
-	0x254, 0x000, 0x331, 0x254, 0xF38, 0xE61, 0x254, 0x409, 0x000,
-	0x010, 0x080, 0x080
-};
+unsigned long mdp_timer_duration = (HZ/20);   /* 50 msecond */
 
-static DECLARE_WAIT_QUEUE_HEAD(mdp_dma2_waitqueue);
-static DECLARE_WAIT_QUEUE_HEAD(mdp_ppp_waitqueue);
-static struct msmfb_callback *dma_callback;
-static struct clk *clk;
-static unsigned int mdp_irq_mask;
-static DEFINE_SPINLOCK(mdp_lock);
-DEFINE_MUTEX(mdp_mutex);
+boolean mdp_ppp_waiting = FALSE;
+uint32 mdp_tv_underflow_cnt;
+uint32 mdp_lcdc_underflow_cnt;
 
-static int enable_mdp_irq(struct mdp_info *mdp, uint32_t mask)
+boolean mdp_current_clk_on = FALSE;
+boolean mdp_is_in_isr = FALSE;
+
+/*
+ * legacy mdp_in_processing is only for DMA2-MDDI
+ * this applies to DMA2 block only
+ */
+uint32 mdp_in_processing = FALSE;
+
+#ifdef CONFIG_FB_MSM_MDP40
+uint32 mdp_intr_mask = MDP4_ANY_INTR_MASK;
+#else
+uint32 mdp_intr_mask = MDP_ANY_INTR_MASK;
+#endif
+
+MDP_BLOCK_TYPE mdp_debug[MDP_MAX_BLOCK];
+
+atomic_t mdp_block_power_cnt[MDP_MAX_BLOCK];
+
+spinlock_t mdp_spin_lock;
+struct workqueue_struct *mdp_dma_wq;	/*mdp dma wq */
+struct workqueue_struct *mdp_vsync_wq;	/*mdp vsync wq */
+
+struct workqueue_struct *mdp_hist_wq;	/*mdp histogram wq */
+
+static struct workqueue_struct *mdp_pipe_ctrl_wq; /* mdp mdp pipe ctrl wq */
+static struct delayed_work mdp_pipe_ctrl_worker;
+
+static boolean mdp_suspended = FALSE;
+static DEFINE_MUTEX(mdp_suspend_mutex);
+
+#ifdef CONFIG_FB_MSM_MDP40
+struct mdp_dma_data dma2_data;
+struct mdp_dma_data dma_s_data;
+struct mdp_dma_data dma_e_data;
+ulong mdp4_display_intf;
+#else
+static struct mdp_dma_data dma2_data;
+static struct mdp_dma_data dma_s_data;
+#ifndef CONFIG_FB_MSM_MDP303
+static struct mdp_dma_data dma_e_data;
+#endif
+#endif
+
+#ifdef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL
+struct mdp_dma_data dma_wb_data;
+#endif
+
+static struct mdp_dma_data dma3_data;
+
+extern ktime_t mdp_dma2_last_update_time;
+
+extern uint32 mdp_dma2_update_time_in_usec;
+extern int mdp_lcd_rd_cnt_offset_slow;
+extern int mdp_lcd_rd_cnt_offset_fast;
+extern int mdp_usec_diff_threshold;
+
+extern int first_pixel_start_x;
+extern int first_pixel_start_y;
+
+#ifdef MSM_FB_ENABLE_DBGFS
+struct dentry *mdp_dir;
+#endif
+
+#if defined(CONFIG_PM) && !defined(CONFIG_HAS_EARLYSUSPEND)
+static int mdp_suspend(struct platform_device *pdev, pm_message_t state);
+#else
+#define mdp_suspend NULL
+#endif
+
+struct timeval mdp_dma2_timeval;
+struct timeval mdp_ppp_timeval;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static struct early_suspend early_suspend;
+#endif
+
+static u32 mdp_irq;
+
+static uint32 mdp_prim_panel_type = NO_PANEL;
+#ifndef CONFIG_FB_MSM_MDP22
+
+struct list_head mdp_hist_lut_list;
+DEFINE_MUTEX(mdp_hist_lut_list_mutex);
+
+uint32_t mdp_block2base(uint32_t block)
 {
-	unsigned long irq_flags;
+	uint32_t base = 0x0;
+	switch (block) {
+	case MDP_BLOCK_DMA_P:
+		base = 0x90000;
+		break;
+	case MDP_BLOCK_DMA_S:
+		base = 0xA0000;
+		break;
+	case MDP_BLOCK_VG_1:
+		base = 0x20000;
+		break;
+	case MDP_BLOCK_VG_2:
+		base = 0x30000;
+		break;
+	case MDP_BLOCK_RGB_1:
+		base = 0x40000;
+		break;
+	case MDP_BLOCK_RGB_2:
+		base = 0x50000;
+		break;
+	case MDP_BLOCK_OVERLAY_0:
+		base = 0x10000;
+		break;
+	case MDP_BLOCK_OVERLAY_1:
+		base = 0x18000;
+		break;
+	case MDP_BLOCK_OVERLAY_2:
+		base = (mdp_rev >= MDP_REV_44) ? 0x88000 : 0;
+		break;
+	default:
+		break;
+	}
+	return base;
+}
+
+static uint32_t mdp_pp_block2hist_lut(uint32_t block)
+{
+	uint32_t valid = 0;
+	switch (block) {
+	case MDP_BLOCK_DMA_P:
+		valid = (mdp_rev >= MDP_REV_40) ? 1 : 0;
+		break;
+	case MDP_BLOCK_DMA_S:
+		valid = (mdp_rev >= MDP_REV_40) ? 1 : 0;
+		break;
+	case MDP_BLOCK_VG_1:
+		valid = (mdp_rev >= MDP_REV_40) ? 1 : 0;
+		break;
+	case MDP_BLOCK_VG_2:
+		valid = (mdp_rev >= MDP_REV_40) ? 1 : 0;
+		break;
+	default:
+		break;
+	}
+	return valid;
+}
+
+static void mdp_hist_lut_init_mgmt(struct mdp_hist_lut_mgmt *mgmt,
+		uint32_t block)
+{
+	mutex_init(&mgmt->lock);
+	mgmt->block = block;
+
+	mutex_lock(&mdp_hist_lut_list_mutex);
+	list_add(&mgmt->list, &mdp_hist_lut_list);
+	mutex_unlock(&mdp_hist_lut_list_mutex);
+}
+
+static int mdp_hist_lut_init(void)
+{
+	struct mdp_hist_lut_mgmt *temp;
+	struct list_head *pos, *q;
+	INIT_LIST_HEAD(&mdp_hist_lut_list);
+
+	if (mdp_rev >= MDP_REV_30) {
+		temp = kmalloc(sizeof(struct mdp_hist_lut_mgmt), GFP_KERNEL);
+		if (!temp)
+			goto exit;
+		mdp_hist_lut_init_mgmt(temp, MDP_BLOCK_DMA_P);
+	}
+
+	if (mdp_rev >= MDP_REV_40) {
+		temp = kmalloc(sizeof(struct mdp_hist_lut_mgmt), GFP_KERNEL);
+		if (!temp)
+			goto exit_list;
+		mdp_hist_lut_init_mgmt(temp, MDP_BLOCK_VG_1);
+
+		temp = kmalloc(sizeof(struct mdp_hist_lut_mgmt), GFP_KERNEL);
+		if (!temp)
+			goto exit_list;
+		mdp_hist_lut_init_mgmt(temp, MDP_BLOCK_VG_2);
+	}
+
+	if (mdp_rev > MDP_REV_42) {
+		temp = kmalloc(sizeof(struct mdp_hist_lut_mgmt), GFP_KERNEL);
+		if (!temp)
+			goto exit_list;
+		mdp_hist_lut_init_mgmt(temp, MDP_BLOCK_DMA_S);
+	}
+	return 0;
+
+exit_list:
+	mutex_lock(&mdp_hist_lut_list_mutex);
+	list_for_each_safe(pos, q, &mdp_hist_lut_list) {
+		temp = list_entry(pos, struct mdp_hist_lut_mgmt, list);
+		list_del(pos);
+		kfree(temp);
+	}
+	mutex_unlock(&mdp_hist_lut_list_mutex);
+exit:
+	pr_err("Failed initializing histogram LUT memory\n");
+	return -ENOMEM;
+}
+
+static int mdp_hist_lut_block2mgmt(uint32_t block,
+		struct mdp_hist_lut_mgmt **mgmt)
+{
+	struct mdp_hist_lut_mgmt *temp, *output;
 	int ret = 0;
 
-	BUG_ON(!mask);
+	output = NULL;
 
-	spin_lock_irqsave(&mdp_lock, irq_flags);
-	/* if the mask bits are already set return an error, this interrupt
-	 * is already enabled */
-	if (mdp_irq_mask & mask) {
-		printk(KERN_ERR "mdp irq already on already on %x %x\n",
-		       mdp_irq_mask, mask);
-		ret = -1;
+	mutex_lock(&mdp_hist_lut_list_mutex);
+	list_for_each_entry(temp, &mdp_hist_lut_list, list) {
+		if (temp->block == block)
+			output = temp;
 	}
-	/* if the mdp irq is not already enabled enable it */
-	if (!mdp_irq_mask) {
-		if (clk)
-			clk_enable(clk);
-		enable_irq(mdp->irq);
-	}
+	mutex_unlock(&mdp_hist_lut_list_mutex);
 
-	/* update the irq mask to reflect the fact that the interrupt is
-	 * enabled */
-	mdp_irq_mask |= mask;
-	spin_unlock_irqrestore(&mdp_lock, irq_flags);
+	if (output == NULL)
+		ret = -EINVAL;
+	else
+		*mgmt = output;
+
 	return ret;
 }
 
-static int locked_disable_mdp_irq(struct mdp_info *mdp, uint32_t mask)
+#define MDP_HIST_LUT_SIZE (256)
+static int mdp_hist_lut_write_off(struct mdp_hist_lut_data *data,
+		struct mdp_hist_lut_info *info, uint32_t offset)
 {
-	/* this interrupt is already disabled! */
-	if (!(mdp_irq_mask & mask)) {
-		printk(KERN_ERR "mdp irq already off %x %x\n",
-		       mdp_irq_mask, mask);
-		return -1;
+	int i;
+	uint32_t element[MDP_HIST_LUT_SIZE];
+	uint32_t base = mdp_block2base(info->block);
+	uint32_t sel = info->bank_sel;
+
+
+	if (data->len != MDP_HIST_LUT_SIZE) {
+		pr_err("%s: data->len != %d", __func__, MDP_HIST_LUT_SIZE);
+		return -EINVAL;
 	}
-	/* update the irq mask to reflect the fact that the interrupt is
-	 * disabled */
-	mdp_irq_mask &= ~(mask);
-	/* if no one is waiting on the interrupt, disable it */
-	if (!mdp_irq_mask) {
-		disable_irq_nosync(mdp->irq);
-		if (clk)
-			clk_disable(clk);
+
+	if (copy_from_user(&element, data->data,
+				MDP_HIST_LUT_SIZE * sizeof(uint32_t))) {
+		pr_err("%s: Error copying histogram data", __func__);
+		return -ENOMEM;
 	}
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	for (i = 0; i < MDP_HIST_LUT_SIZE; i++)
+		MDP_OUTP(MDP_BASE + base + offset + (0x400*(sel)) + (4*i),
+				element[i]);
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
 	return 0;
 }
 
-static int disable_mdp_irq(struct mdp_info *mdp, uint32_t mask)
-{
-	unsigned long irq_flags;
-	int ret;
-
-	spin_lock_irqsave(&mdp_lock, irq_flags);
-	ret = locked_disable_mdp_irq(mdp, mask);
-	spin_unlock_irqrestore(&mdp_lock, irq_flags);
-	return ret;
-}
-
-static irqreturn_t mdp_isr(int irq, void *data)
-{
-	uint32_t status;
-	unsigned long irq_flags;
-	struct mdp_info *mdp = data;
-
-	spin_lock_irqsave(&mdp_lock, irq_flags);
-
-	status = mdp_readl(mdp, MDP_INTR_STATUS);
-	mdp_writel(mdp, status, MDP_INTR_CLEAR);
-
-	status &= mdp_irq_mask;
-	if (status & DL0_DMA2_TERM_DONE) {
-		if (dma_callback) {
-			dma_callback->func(dma_callback);
-			dma_callback = NULL;
-		}
-		wake_up(&mdp_dma2_waitqueue);
-	}
-
-	if (status & DL0_ROI_DONE)
-		wake_up(&mdp_ppp_waitqueue);
-
-	if (status)
-		locked_disable_mdp_irq(mdp, status);
-
-	spin_unlock_irqrestore(&mdp_lock, irq_flags);
-	return IRQ_HANDLED;
-}
-
-static uint32_t mdp_check_mask(uint32_t mask)
-{
-	uint32_t ret;
-	unsigned long irq_flags;
-
-	spin_lock_irqsave(&mdp_lock, irq_flags);
-	ret = mdp_irq_mask & mask;
-	spin_unlock_irqrestore(&mdp_lock, irq_flags);
-	return ret;
-}
-
-static int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq)
+static int mdp_hist_lut_write(struct mdp_hist_lut_data *data,
+						struct mdp_hist_lut_info *info)
 {
 	int ret = 0;
-	unsigned long irq_flags;
 
-	wait_event_timeout(*wq, !mdp_check_mask(mask), HZ);
-
-	spin_lock_irqsave(&mdp_lock, irq_flags);
-	if (mdp_irq_mask & mask) {
-		locked_disable_mdp_irq(mdp, mask);
-		printk(KERN_WARNING "timeout waiting for mdp to complete %x\n",
-		       mask);
-		ret = -ETIMEDOUT;
+	if (data->block != info->block) {
+		ret = -1;
+		pr_err("%s, data/info mdp_block mismatch! %d != %d\n",
+				__func__, data->block, info->block);
+		goto error;
 	}
-	spin_unlock_irqrestore(&mdp_lock, irq_flags);
+
+	switch (data->block) {
+	case MDP_BLOCK_VG_1:
+	case MDP_BLOCK_VG_2:
+		ret = mdp_hist_lut_write_off(data, info, 0x3400);
+		break;
+	case MDP_BLOCK_DMA_P:
+	case MDP_BLOCK_DMA_S:
+		ret = mdp_hist_lut_write_off(data, info, 0x4800);
+		break;
+	default:
+		ret = -EINVAL;
+		goto error;
+	}
+
+error:
+	return ret;
+}
+
+#define MDP_HIST_LUT_VG_EN_MASK (0x20000)
+#define MDP_HIST_LUT_VG_EN_SHIFT (17)
+#define MDP_HIST_LUT_VG_EN_OFFSET (0x0058)
+#define MDP_HIST_LUT_VG_SEL_OFFSET (0x0064)
+static void mdp_hist_lut_commit_vg(struct mdp_hist_lut_info *info)
+{
+	uint32_t out_en, temp_en;
+	uint32_t base = mdp_block2base(info->block);
+	temp_en = (info->is_enabled) ? (1 << MDP_HIST_LUT_VG_EN_SHIFT) : 0x0;
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	out_en = inpdw(MDP_BASE + base + MDP_HIST_LUT_VG_EN_OFFSET) &
+						~MDP_HIST_LUT_VG_EN_MASK;
+	MDP_OUTP(MDP_BASE + base + MDP_HIST_LUT_VG_EN_OFFSET, out_en | temp_en);
+
+	if (info->has_sel_update)
+		MDP_OUTP(MDP_BASE + base + MDP_HIST_LUT_VG_SEL_OFFSET,
+								info->bank_sel);
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+#define MDP_HIST_LUT_DMA_EN_MASK (0x7)
+#define MDP_HIST_LUT_DMA_SEL_MASK (0x400)
+#define MDP_HIST_LUT_DMA_SEL_SHIFT (10)
+#define MDP_HIST_LUT_DMA_P_OFFSET (0x0070)
+#define MDP_HIST_LUT_DMA_S_OFFSET (0x0028)
+static void mdp_hist_lut_commit_dma(struct mdp_hist_lut_info *info)
+{
+	uint32_t out, temp, mask;
+	uint32_t base = mdp_block2base(info->block);
+	uint32_t offset = (info->block == MDP_BLOCK_DMA_P) ?
+		MDP_HIST_LUT_DMA_P_OFFSET : MDP_HIST_LUT_DMA_S_OFFSET;
+
+	mask = MDP_HIST_LUT_DMA_EN_MASK;
+	temp = (info->is_enabled) ? 0x7 : 0x0;
+
+	if (info->has_sel_update) {
+		mask |= MDP_HIST_LUT_DMA_SEL_MASK;
+		temp |=  ((info->bank_sel & 0x1) << MDP_HIST_LUT_DMA_SEL_SHIFT);
+	}
+
+	out = inpdw(MDP_BASE + base + offset) & ~mask;
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	MDP_OUTP(MDP_BASE + base + offset, out | temp);
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+static void mdp_hist_lut_commit_info(struct mdp_hist_lut_info *info)
+{
+	switch (info->block) {
+	case MDP_BLOCK_VG_1:
+	case MDP_BLOCK_VG_2:
+		mdp_hist_lut_commit_vg(info);
+		break;
+	case MDP_BLOCK_DMA_P:
+	case MDP_BLOCK_DMA_S:
+		mdp_hist_lut_commit_dma(info);
+		break;
+	default:
+		goto error;
+	}
+
+error:
+	return;
+}
+
+static void mdp_hist_lut_update_info(struct mdp_hist_lut_info *info, int ops)
+{
+	info->bank_sel = (ops & 0x8) >> 3;
+	info->is_enabled = (ops & 0x1) ? TRUE : FALSE;
+	info->has_sel_update = (ops & 0x10) ? TRUE : FALSE;
+}
+
+int mdp_hist_lut_config(struct mdp_hist_lut_data *data)
+{
+	struct mdp_hist_lut_mgmt *mgmt = NULL;
+	struct mdp_hist_lut_info info;
+	int ret = 0;
+
+	if (!mdp_pp_block2hist_lut(data->block)) {
+		ret = -ENOTTY;
+		goto error;
+	}
+
+	ret = mdp_hist_lut_block2mgmt(data->block, &mgmt);
+	if (ret)
+		goto error;
+
+	mutex_lock(&mgmt->lock);
+
+	info.block = mgmt->block;
+
+	mdp_hist_lut_update_info(&info, data->ops);
+
+	switch ((data->ops & 0x6) >> 1) {
+	case 0x1:
+		pr_info("%s: histogram LUT read not supported\n", __func__);
+		break;
+	case 0x2:
+		ret = mdp_hist_lut_write(data, &info);
+		if (ret)
+			goto error_lock;
+		break;
+	default:
+		break;
+	}
+
+	mdp_hist_lut_commit_info(&info);
+
+error_lock:
+	mutex_unlock(&mgmt->lock);
+error:
+	return ret;
+}
+
+DEFINE_MUTEX(mdp_lut_push_sem);
+static int mdp_lut_i;
+static int mdp_lut_hw_update(struct fb_cmap *cmap)
+{
+	int i;
+	u16 *c[3];
+	u16 r, g, b;
+
+	c[0] = cmap->green;
+	c[1] = cmap->blue;
+	c[2] = cmap->red;
+
+	for (i = 0; i < cmap->len; i++) {
+		if (copy_from_user(&r, cmap->red++, sizeof(r)) ||
+		    copy_from_user(&g, cmap->green++, sizeof(g)) ||
+		    copy_from_user(&b, cmap->blue++, sizeof(b)))
+			return -EFAULT;
+
+#ifdef CONFIG_FB_MSM_MDP40
+		MDP_OUTP(MDP_BASE + 0x94800 +
+#else
+		MDP_OUTP(MDP_BASE + 0x93800 +
+#endif
+			(0x400*mdp_lut_i) + cmap->start*4 + i*4,
+				((g & 0xff) |
+				 ((b & 0xff) << 8) |
+				 ((r & 0xff) << 16)));
+	}
+
+	return 0;
+}
+
+static int mdp_lut_push;
+static int mdp_lut_push_i;
+static int mdp_lut_update_nonlcdc(struct fb_info *info, struct fb_cmap *cmap)
+{
+	int ret;
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	ret = mdp_lut_hw_update(cmap);
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	if (ret)
+		return ret;
+
+	mutex_lock(&mdp_lut_push_sem);
+	mdp_lut_push = 1;
+	mdp_lut_push_i = mdp_lut_i;
+	mutex_unlock(&mdp_lut_push_sem);
+
+	mdp_lut_i = (mdp_lut_i + 1)%2;
+
+	return 0;
+}
+
+static int mdp_lut_update_lcdc(struct fb_info *info, struct fb_cmap *cmap)
+{
+	int ret;
+	uint32_t out;
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	ret = mdp_lut_hw_update(cmap);
+
+	if (ret) {
+		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+		return ret;
+	}
+
+	/*mask off non LUT select bits*/
+	out = inpdw(MDP_BASE + 0x90070) & ~((0x1 << 10) | 0x7);
+	MDP_OUTP(MDP_BASE + 0x90070, (mdp_lut_i << 10) | 0x7 | out);
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+	mdp_lut_i = (mdp_lut_i + 1)%2;
+
+	return 0;
+}
+
+static void mdp_lut_enable(void)
+{
+	uint32_t out;
+	if (mdp_lut_push) {
+		mutex_lock(&mdp_lut_push_sem);
+		mdp_lut_push = 0;
+		out = inpdw(MDP_BASE + 0x90070) & ~((0x1 << 10) | 0x7);
+		MDP_OUTP(MDP_BASE + 0x90070,
+				(mdp_lut_push_i << 10) | 0x7 | out);
+		mutex_unlock(&mdp_lut_push_sem);
+	}
+}
+
+#define MDP_REV42_HIST_MAX_BIN 128
+#define MDP_REV41_HIST_MAX_BIN 32
+
+#define MDP_HIST_DATA32_R_OFF 0x0100
+#define MDP_HIST_DATA32_G_OFF 0x0200
+#define MDP_HIST_DATA32_B_OFF 0x0300
+
+#define MDP_HIST_DATA128_R_OFF 0x0400
+#define MDP_HIST_DATA128_G_OFF 0x0800
+#define MDP_HIST_DATA128_B_OFF 0x0C00
+
+#define MDP_HIST_DATA_LUMA_OFF 0x0200
+
+#define MDP_HIST_EXTRA_DATA0_OFF 0x0028
+#define MDP_HIST_EXTRA_DATA1_OFF 0x002C
+
+struct mdp_hist_mgmt *mdp_hist_mgmt_array[MDP_HIST_MGMT_MAX];
+
+void __mdp_histogram_kickoff(struct mdp_hist_mgmt *mgmt)
+{
+	char *mdp_hist_base = MDP_BASE + mgmt->base;
+	if (mgmt->mdp_is_hist_data == TRUE) {
+		MDP_OUTP(mdp_hist_base + 0x0004, mgmt->frame_cnt);
+		MDP_OUTP(mdp_hist_base, 1);
+	}
+}
+
+void __mdp_histogram_reset(struct mdp_hist_mgmt *mgmt)
+{
+	char *mdp_hist_base = MDP_BASE + mgmt->base;
+	MDP_OUTP(mdp_hist_base + 0x000C, 1);
+}
+
+static void mdp_hist_read_work(struct work_struct *data);
+
+static int mdp_hist_init_mgmt(struct mdp_hist_mgmt *mgmt, uint32_t block)
+{
+	uint32_t bins, extra, index, term = 0;
+	init_completion(&mgmt->mdp_hist_comp);
+	mutex_init(&mgmt->mdp_hist_mutex);
+	mutex_init(&mgmt->mdp_do_hist_mutex);
+	mgmt->block = block;
+	mgmt->base = mdp_block2base(block);
+	mgmt->mdp_is_hist_start = FALSE;
+	mgmt->mdp_is_hist_data = FALSE;
+	mgmt->mdp_is_hist_valid = FALSE;
+	mgmt->mdp_is_hist_init = FALSE;
+	mgmt->frame_cnt = 0;
+	mgmt->bit_mask = 0;
+	mgmt->num_bins = 0;
+	switch (block) {
+	case MDP_BLOCK_DMA_P:
+		term = MDP_HISTOGRAM_TERM_DMA_P;
+		bins = (mdp_rev >= MDP_REV_42) ? MDP_REV42_HIST_MAX_BIN :
+			MDP_REV41_HIST_MAX_BIN;
+		extra = 2;
+		mgmt->base += (mdp_rev >= MDP_REV_40) ? 0x5000 : 0x4000;
+		index = MDP_HIST_MGMT_DMA_P;
+		break;
+	case MDP_BLOCK_DMA_S:
+		term = MDP_HISTOGRAM_TERM_DMA_S;
+		bins = MDP_REV42_HIST_MAX_BIN;
+		extra = 2;
+		mgmt->base += 0x5000;
+		index = MDP_HIST_MGMT_DMA_S;
+		break;
+	case MDP_BLOCK_VG_1:
+		term = MDP_HISTOGRAM_TERM_VG_1;
+		bins = MDP_REV42_HIST_MAX_BIN;
+		extra = 1;
+		mgmt->base += 0x6000;
+		index = MDP_HIST_MGMT_VG_1;
+		break;
+	case MDP_BLOCK_VG_2:
+		term = MDP_HISTOGRAM_TERM_VG_2;
+		bins = MDP_REV42_HIST_MAX_BIN;
+		extra = 1;
+		mgmt->base += 0x6000;
+		index = MDP_HIST_MGMT_VG_2;
+		break;
+	default:
+		term = MDP_HISTOGRAM_TERM_DMA_P;
+		bins = (mdp_rev >= MDP_REV_42) ? MDP_REV42_HIST_MAX_BIN :
+			MDP_REV41_HIST_MAX_BIN;
+		extra = 2;
+		mgmt->base += (mdp_rev >= MDP_REV_40) ? 0x5000 : 0x4000;
+		index = MDP_HIST_MGMT_DMA_P;
+	}
+	mgmt->irq_term = term;
+
+	mgmt->c0 = kmalloc(bins * sizeof(uint32_t), GFP_KERNEL);
+	if (mgmt->c0 == NULL)
+		goto error;
+
+	mgmt->c1 = kmalloc(bins * sizeof(uint32_t), GFP_KERNEL);
+	if (mgmt->c1 == NULL)
+		goto error_1;
+
+	mgmt->c2 = kmalloc(bins * sizeof(uint32_t), GFP_KERNEL);
+	if (mgmt->c2 == NULL)
+		goto error_2;
+
+	mgmt->extra_info = kmalloc(extra * sizeof(uint32_t), GFP_KERNEL);
+	if (mgmt->extra_info == NULL)
+		goto error_extra;
+
+	INIT_WORK(&mgmt->mdp_histogram_worker, mdp_hist_read_work);
+	mgmt->hist = NULL;
+
+	mdp_hist_mgmt_array[index] = mgmt;
+	return 0;
+
+error_extra:
+	kfree(mgmt->c2);
+error_2:
+	kfree(mgmt->c1);
+error_1:
+	kfree(mgmt->c0);
+error:
+	return -ENOMEM;
+}
+
+static void mdp_hist_del_mgmt(struct mdp_hist_mgmt *mgmt)
+{
+	kfree(mgmt->extra_info);
+	kfree(mgmt->c2);
+	kfree(mgmt->c1);
+	kfree(mgmt->c0);
+}
+
+static int mdp_histogram_init(void)
+{
+	struct mdp_hist_mgmt *temp;
+	int i, ret;
+	mdp_hist_wq = alloc_workqueue("mdp_hist_wq",
+					WQ_NON_REENTRANT | WQ_UNBOUND, 0);
+
+	for (i = 0; i < MDP_HIST_MGMT_MAX; i++)
+		mdp_hist_mgmt_array[i] = NULL;
+
+	if (mdp_rev >= MDP_REV_30) {
+		temp = kmalloc(sizeof(struct mdp_hist_mgmt), GFP_KERNEL);
+		if (!temp)
+			goto exit;
+		ret = mdp_hist_init_mgmt(temp, MDP_BLOCK_DMA_P);
+		if (ret) {
+			kfree(temp);
+			goto exit;
+		}
+	}
+
+	if (mdp_rev >= MDP_REV_40) {
+		temp = kmalloc(sizeof(struct mdp_hist_mgmt), GFP_KERNEL);
+		if (!temp)
+			goto exit_list;
+		ret = mdp_hist_init_mgmt(temp, MDP_BLOCK_VG_1);
+		if (ret)
+			goto exit_list;
+
+		temp = kmalloc(sizeof(struct mdp_hist_mgmt), GFP_KERNEL);
+		if (!temp)
+			goto exit_list;
+		ret = mdp_hist_init_mgmt(temp, MDP_BLOCK_VG_2);
+		if (ret)
+			goto exit_list;
+	}
+
+	if (mdp_rev >= MDP_REV_42) {
+		temp = kmalloc(sizeof(struct mdp_hist_mgmt), GFP_KERNEL);
+		if (!temp)
+			goto exit_list;
+		ret = mdp_hist_init_mgmt(temp, MDP_BLOCK_DMA_S);
+		if (ret)
+			goto exit_list;
+	}
+
+	return 0;
+
+exit_list:
+	for (i = 0; i < MDP_HIST_MGMT_MAX; i++) {
+		temp = mdp_hist_mgmt_array[i];
+		if (!temp)
+			continue;
+		mdp_hist_del_mgmt(temp);
+		kfree(temp);
+		mdp_hist_mgmt_array[i] = NULL;
+	}
+exit:
+	return -ENOMEM;
+}
+
+int mdp_histogram_block2mgmt(uint32_t block, struct mdp_hist_mgmt **mgmt)
+{
+	struct mdp_hist_mgmt *temp, *output;
+	int i, ret = 0;
+
+	output = NULL;
+
+	for (i = 0; i < MDP_HIST_MGMT_MAX; i++) {
+		temp = mdp_hist_mgmt_array[i];
+		if (!temp)
+			continue;
+
+		if (temp->block == block) {
+			output = temp;
+			break;
+		}
+	}
+
+	if (output == NULL)
+		ret = -EINVAL;
+	else
+		*mgmt = output;
 
 	return ret;
 }
 
-void mdp_dma_wait(struct mdp_device *mdp_dev)
+static int mdp_histogram_enable(struct mdp_hist_mgmt *mgmt)
 {
-#define MDP_MAX_TIMEOUTS 20
-	static int timeout_count;
-	struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
+	uint32_t base;
+	if (mgmt->mdp_is_hist_data == TRUE) {
+		pr_err("%s histogram already started\n", __func__);
+		return -EINVAL;
+	}
 
-	if (mdp_wait(mdp, DL0_DMA2_TERM_DONE, &mdp_dma2_waitqueue) == -ETIMEDOUT)
-		timeout_count++;
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	mdp_enable_irq(mgmt->irq_term);
+	INIT_COMPLETION(mgmt->mdp_hist_comp);
+
+	base = (uint32_t) (MDP_BASE + mgmt->base);
+	MDP_OUTP(base + 0x0018, INTR_HIST_DONE | INTR_HIST_RESET_SEQ_DONE);
+	MDP_OUTP(base + 0x0010, 1);
+	MDP_OUTP(base + 0x001C, INTR_HIST_DONE | INTR_HIST_RESET_SEQ_DONE);
+
+	MDP_OUTP(base + 0x0004, mgmt->frame_cnt);
+	if (mgmt->block != MDP_BLOCK_VG_1 && mgmt->block != MDP_BLOCK_VG_2)
+		MDP_OUTP(base + 0x0008, mgmt->bit_mask);
+	mgmt->mdp_is_hist_data = TRUE;
+	mgmt->mdp_is_hist_valid = TRUE;
+	mgmt->mdp_is_hist_init = FALSE;
+	__mdp_histogram_reset(mgmt);
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+	return 0;
+}
+
+static int mdp_histogram_disable(struct mdp_hist_mgmt *mgmt)
+{
+	uint32_t base, status;
+	if (mgmt->mdp_is_hist_data == FALSE) {
+		pr_err("%s histogram already stopped\n", __func__);
+		return -EINVAL;
+	}
+
+	mgmt->mdp_is_hist_data = FALSE;
+	mgmt->mdp_is_hist_valid = FALSE;
+	mgmt->mdp_is_hist_init = FALSE;
+
+	base = (uint32_t) (MDP_BASE + mgmt->base);
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	if (mdp_rev >= MDP_REV_42)
+		MDP_OUTP(base + 0x0020, 1);
+	status = inpdw(base + 0x001C);
+	status &= ~(INTR_HIST_DONE | INTR_HIST_RESET_SEQ_DONE);
+	MDP_OUTP(base + 0x001C, status);
+
+	MDP_OUTP(base + 0x0018, INTR_HIST_DONE | INTR_HIST_RESET_SEQ_DONE);
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	complete(&mgmt->mdp_hist_comp);
+	mdp_disable_irq(mgmt->irq_term);
+	return 0;
+}
+
+/*call when spanning mgmt_array only*/
+int _mdp_histogram_ctrl(boolean en, struct mdp_hist_mgmt *mgmt)
+{
+	int ret = 0;
+
+	mutex_lock(&mgmt->mdp_hist_mutex);
+	if (mgmt->mdp_is_hist_start == TRUE) {
+		if (en)
+			ret = mdp_histogram_enable(mgmt);
+		else
+			ret = mdp_histogram_disable(mgmt);
+	}
+	mutex_unlock(&mgmt->mdp_hist_mutex);
+
+	if (en == false)
+		cancel_work_sync(&mgmt->mdp_histogram_worker);
+
+	return ret;
+}
+
+int mdp_histogram_ctrl(boolean en, uint32_t block)
+{
+	struct mdp_hist_mgmt *mgmt = NULL;
+	int ret = 0;
+
+	ret = mdp_histogram_block2mgmt(block, &mgmt);
+	if (ret)
+		goto error;
+
+	ret = _mdp_histogram_ctrl(en, mgmt);
+error:
+	return ret;
+}
+
+int mdp_histogram_ctrl_all(boolean en)
+{
+	struct mdp_hist_mgmt *temp;
+	int i, ret = 0, ret_temp = 0;
+
+	for (i = 0; i < MDP_HIST_MGMT_MAX; i++) {
+		temp = mdp_hist_mgmt_array[i];
+		if (!temp)
+			continue;
+
+		ret_temp = _mdp_histogram_ctrl(en, temp);
+		if (ret_temp)
+			ret = ret_temp;
+	}
+	return ret;
+}
+
+int mdp_histogram_start(struct mdp_histogram_start_req *req)
+{
+	struct mdp_hist_mgmt *mgmt = NULL;
+	int ret;
+
+	ret = mdp_histogram_block2mgmt(req->block, &mgmt);
+	if (ret) {
+		ret = -ENOTTY;
+		goto error;
+	}
+
+	mutex_lock(&mgmt->mdp_hist_mutex);
+	if (mgmt->mdp_is_hist_start == TRUE) {
+		pr_err("%s histogram already started\n", __func__);
+		ret = -EPERM;
+		goto error_lock;
+	}
+
+	mgmt->block = req->block;
+	mgmt->frame_cnt = req->frame_cnt;
+	mgmt->bit_mask = req->bit_mask;
+	mgmt->num_bins = req->num_bins;
+	mgmt->hist = NULL;
+
+	ret = mdp_histogram_enable(mgmt);
+
+	mgmt->mdp_is_hist_start = TRUE;
+
+error_lock:
+	mutex_unlock(&mgmt->mdp_hist_mutex);
+error:
+	return ret;
+}
+
+int mdp_histogram_stop(struct fb_info *info, uint32_t block)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *) info->par;
+	struct mdp_hist_mgmt *mgmt = NULL;
+	int ret;
+
+	ret = mdp_histogram_block2mgmt(block, &mgmt);
+	if (ret) {
+		ret = -ENOTTY;
+		goto error;
+	}
+
+	mutex_lock(&mgmt->mdp_hist_mutex);
+	if (mgmt->mdp_is_hist_start == FALSE) {
+		pr_err("%s histogram already stopped\n", __func__);
+		ret = -EPERM;
+		goto error_lock;
+	}
+
+	mgmt->mdp_is_hist_start = FALSE;
+
+	if (!mfd->panel_power_on) {
+		mgmt->mdp_is_hist_data = FALSE;
+		complete(&mgmt->mdp_hist_comp);
+		ret = -EINVAL;
+		goto error_lock;
+	}
+
+	ret = mdp_histogram_disable(mgmt);
+
+	mutex_unlock(&mgmt->mdp_hist_mutex);
+	cancel_work_sync(&mgmt->mdp_histogram_worker);
+	return ret;
+
+error_lock:
+	mutex_unlock(&mgmt->mdp_hist_mutex);
+error:
+	return ret;
+}
+
+/*call from within mdp_hist_mutex context*/
+static int _mdp_histogram_read_dma_data(struct mdp_hist_mgmt *mgmt)
+{
+	char *mdp_hist_base;
+	uint32_t r_data_offset, g_data_offset, b_data_offset;
+	int i, ret = 0;
+
+	mdp_hist_base = MDP_BASE + mgmt->base;
+
+	r_data_offset = (32 == mgmt->num_bins) ? MDP_HIST_DATA32_R_OFF :
+		MDP_HIST_DATA128_R_OFF;
+	g_data_offset = (32 == mgmt->num_bins) ? MDP_HIST_DATA32_G_OFF :
+		MDP_HIST_DATA128_G_OFF;
+	b_data_offset = (32 == mgmt->num_bins) ? MDP_HIST_DATA32_B_OFF :
+		MDP_HIST_DATA128_B_OFF;
+
+	if (mgmt->c0 == NULL || mgmt->c1 == NULL || mgmt->c2 == NULL) {
+		ret = -ENOMEM;
+		goto hist_err;
+	}
+
+	if (!mgmt->hist) {
+		pr_err("%s: mgmt->hist not set, mgmt->hist = 0x%08x",
+		__func__, (uint32_t) mgmt->hist);
+		return -EINVAL;
+	}
+
+	if (mgmt->hist->bin_cnt != mgmt->num_bins) {
+		pr_err("%s, bins config = %d, bin requested = %d", __func__,
+					mgmt->num_bins, mgmt->hist->bin_cnt);
+		return -EINVAL;
+	}
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	for (i = 0; i < mgmt->num_bins; i++) {
+		mgmt->c0[i] = inpdw(mdp_hist_base + r_data_offset + (4*i));
+		mgmt->c1[i] = inpdw(mdp_hist_base + g_data_offset + (4*i));
+		mgmt->c2[i] = inpdw(mdp_hist_base + b_data_offset + (4*i));
+	}
+
+	if (mdp_rev >= MDP_REV_42) {
+		if (mgmt->extra_info) {
+			mgmt->extra_info[0] = inpdw(mdp_hist_base +
+					MDP_HIST_EXTRA_DATA0_OFF);
+			mgmt->extra_info[1] = inpdw(mdp_hist_base +
+					MDP_HIST_EXTRA_DATA0_OFF + 4);
+		} else
+			ret = -ENOMEM;
+	}
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	if (!ret)
+		return ret;
+
+hist_err:
+	pr_err("%s: invalid hist buffer\n", __func__);
+	return ret;
+}
+
+/*call from within mdp_hist_mutex context*/
+static int _mdp_histogram_read_vg_data(struct mdp_hist_mgmt *mgmt)
+{
+	char *mdp_hist_base;
+	int i, ret = 0;
+
+	mdp_hist_base = MDP_BASE + mgmt->base;
+
+	if (mgmt->c0 == NULL) {
+		ret = -ENOMEM;
+		goto hist_err;
+	}
+
+	if (!mgmt->hist) {
+		pr_err("%s: mgmt->hist not set", __func__);
+		return -EINVAL;
+	}
+
+	if (mgmt->hist->bin_cnt != mgmt->num_bins) {
+		pr_err("%s, bins config = %d, bin requested = %d", __func__,
+					mgmt->num_bins, mgmt->hist->bin_cnt);
+		return -EINVAL;
+	}
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	for (i = 0; i < mgmt->num_bins; i++)
+		mgmt->c0[i] = inpdw(mdp_hist_base + MDP_HIST_DATA_LUMA_OFF +
+									(4*i));
+
+	if (mdp_rev >= MDP_REV_42) {
+		if (mgmt->extra_info) {
+			mgmt->extra_info[0] = inpdw(mdp_hist_base +
+						MDP_HIST_EXTRA_DATA0_OFF);
+		} else
+			ret = -ENOMEM;
+	}
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	if (!ret)
+		return ret;
+
+hist_err:
+	pr_err("%s: invalid hist buffer\n", __func__);
+	return ret;
+}
+
+static void mdp_hist_read_work(struct work_struct *data)
+{
+	struct mdp_hist_mgmt *mgmt = container_of(data, struct mdp_hist_mgmt,
+							mdp_histogram_worker);
+	int ret = 0;
+	mutex_lock(&mgmt->mdp_hist_mutex);
+	if (mgmt->mdp_is_hist_data == FALSE) {
+		pr_debug("%s, Histogram disabled before read.\n", __func__);
+		ret = -EINVAL;
+		goto error;
+	}
+
+	switch (mgmt->block) {
+	case MDP_BLOCK_DMA_P:
+	case MDP_BLOCK_DMA_S:
+		ret = _mdp_histogram_read_dma_data(mgmt);
+		break;
+	case MDP_BLOCK_VG_1:
+	case MDP_BLOCK_VG_2:
+		ret = _mdp_histogram_read_vg_data(mgmt);
+		break;
+	default:
+		pr_err("%s, invalid MDP block = %d\n", __func__, mgmt->block);
+		ret = -EINVAL;
+		goto error;
+	}
+
+	/*
+	 * if read was triggered by an underrun or failed copying,
+	 * don't wake up readers
+	 */
+	if (!ret && mgmt->mdp_is_hist_valid && mgmt->mdp_is_hist_init) {
+		mgmt->hist = NULL;
+		complete(&mgmt->mdp_hist_comp);
+	}
+
+	if (mgmt->mdp_is_hist_valid == FALSE)
+			mgmt->mdp_is_hist_valid = TRUE;
+	if (mgmt->mdp_is_hist_init == FALSE)
+			mgmt->mdp_is_hist_init = TRUE;
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	if (!ret)
+		__mdp_histogram_kickoff(mgmt);
 	else
-		timeout_count = 0;
+		__mdp_histogram_reset(mgmt);
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
 
-	if (timeout_count > MDP_MAX_TIMEOUTS) {
-		printk(KERN_ERR "mdp: dma failed %d times, somethings wrong!\n",
-		       MDP_MAX_TIMEOUTS);
-		BUG();
+error:
+	mutex_unlock(&mgmt->mdp_hist_mutex);
+}
+
+/*call from within mdp_hist_mutex*/
+static int _mdp_copy_hist_data(struct mdp_histogram_data *hist,
+						struct mdp_hist_mgmt *mgmt)
+{
+	int ret;
+
+	if (hist->c0) {
+		ret = copy_to_user(hist->c0, mgmt->c0,
+		sizeof(uint32_t) * (hist->bin_cnt));
+		if (ret)
+			goto err;
+	}
+	if (hist->c1) {
+		ret = copy_to_user(hist->c1, mgmt->c1,
+		sizeof(uint32_t) * (hist->bin_cnt));
+		if (ret)
+			goto err;
+	}
+	if (hist->c2) {
+		ret = copy_to_user(hist->c2, mgmt->c2,
+		sizeof(uint32_t) * (hist->bin_cnt));
+		if (ret)
+			goto err;
+	}
+	if (hist->extra_info) {
+		ret = copy_to_user(hist->extra_info, mgmt->extra_info,
+		sizeof(uint32_t) * ((hist->block > MDP_BLOCK_VG_2) ? 2 : 1));
+		if (ret)
+			goto err;
+	}
+err:
+	return ret;
+}
+
+static int mdp_do_histogram(struct fb_info *info,
+					struct mdp_histogram_data *hist)
+{
+	struct mdp_hist_mgmt *mgmt = NULL;
+	int ret = 0;
+
+	ret = mdp_histogram_block2mgmt(hist->block, &mgmt);
+	if (ret) {
+		pr_info("%s - %d", __func__, __LINE__);
+		ret = -EINVAL;
+		return ret;
+	}
+
+	mutex_lock(&mgmt->mdp_do_hist_mutex);
+	if (!mgmt->frame_cnt || (mgmt->num_bins == 0)) {
+		pr_info("%s - frame_cnt = %d, num_bins = %d", __func__,
+		mgmt->frame_cnt, mgmt->num_bins);
+		ret = -EINVAL;
+		goto error;
+}
+	if ((mdp_rev <= MDP_REV_41 && hist->bin_cnt > MDP_REV41_HIST_MAX_BIN)
+		|| (mdp_rev == MDP_REV_42 &&
+				hist->bin_cnt > MDP_REV42_HIST_MAX_BIN)) {
+		pr_info("%s - mdp_rev = %d, num_bins = %d", __func__, mdp_rev,
+								hist->bin_cnt);
+		ret = -EINVAL;
+		goto error;
+}
+	mutex_lock(&mgmt->mdp_hist_mutex);
+	if (!mgmt->mdp_is_hist_data) {
+		pr_info("%s - hist_data = false!", __func__);
+		ret = -EINVAL;
+		goto error_lock;
+	}
+
+	if (!mgmt->mdp_is_hist_start) {
+		pr_err("%s histogram not started\n", __func__);
+		ret = -EPERM;
+		goto error_lock;
+	}
+
+	if (mgmt->hist != NULL) {
+		pr_err("%s; histogram attempted to be read twice\n", __func__);
+		ret = -EPERM;
+		goto error_lock;
+	}
+	mgmt->hist = hist;
+	mutex_unlock(&mgmt->mdp_hist_mutex);
+
+	if (wait_for_completion_killable(&mgmt->mdp_hist_comp)) {
+		pr_err("%s(): histogram bin collection killed", __func__);
+		ret = -EINVAL;
+		goto error;
+	}
+
+	mutex_lock(&mgmt->mdp_hist_mutex);
+	if (mgmt->mdp_is_hist_data && mgmt->mdp_is_hist_init)
+		ret =  _mdp_copy_hist_data(hist, mgmt);
+	else
+		ret = -ENODATA;
+error_lock:
+	mutex_unlock(&mgmt->mdp_hist_mutex);
+error:
+	mutex_unlock(&mgmt->mdp_do_hist_mutex);
+	return ret;
+}
+#endif
+
+/* Returns < 0 on error, 0 on timeout, or > 0 on successful wait */
+
+int mdp_ppp_pipe_wait(void)
+{
+	int ret = 1;
+	boolean wait;
+	unsigned long flag;
+
+	/* wait 5 seconds for the operation to complete before declaring
+	the MDP hung */
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	wait = mdp_ppp_waiting;
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+
+	if (wait == TRUE) {
+		ret = wait_for_completion_interruptible_timeout(&mdp_ppp_comp,
+								5 * HZ);
+
+		if (!ret)
+			printk(KERN_ERR "%s: Timed out waiting for the MDP.\n",
+					__func__);
+	}
+
+	return ret;
+}
+
+static DEFINE_SPINLOCK(mdp_lock);
+static int mdp_irq_mask;
+static int mdp_irq_enabled;
+
+/*
+ * mdp_enable_irq: can not be called from isr
+ */
+void mdp_enable_irq(uint32 term)
+{
+	unsigned long irq_flags;
+
+	spin_lock_irqsave(&mdp_lock, irq_flags);
+	if (mdp_irq_mask & term) {
+		printk(KERN_ERR "%s: MDP IRQ term-0x%x is already set, mask=%x irq=%d\n",
+				__func__, term, mdp_irq_mask, mdp_irq_enabled);
+	} else {
+		mdp_irq_mask |= term;
+		if (mdp_irq_mask && !mdp_irq_enabled) {
+			mdp_irq_enabled = 1;
+			enable_irq(mdp_irq);
+		}
+	}
+	spin_unlock_irqrestore(&mdp_lock, irq_flags);
+}
+
+/*
+ * mdp_disable_irq: can not be called from isr
+ */
+void mdp_disable_irq(uint32 term)
+{
+	unsigned long irq_flags;
+
+	spin_lock_irqsave(&mdp_lock, irq_flags);
+	if (!(mdp_irq_mask & term)) {
+		printk(KERN_ERR "%s: MDP IRQ term-0x%x is NOT set, mask=%x irq=%d\n",
+				__func__, term, mdp_irq_mask, mdp_irq_enabled);
+	} else {
+		mdp_irq_mask &= ~term;
+		if (!mdp_irq_mask && mdp_irq_enabled) {
+			mdp_irq_enabled = 0;
+			disable_irq(mdp_irq);
+		}
+	}
+	spin_unlock_irqrestore(&mdp_lock, irq_flags);
+}
+
+void mdp_disable_irq_nosync(uint32 term)
+{
+	spin_lock(&mdp_lock);
+	if (!(mdp_irq_mask & term)) {
+		printk(KERN_ERR "%s: MDP IRQ term-0x%x is NOT set, mask=%x irq=%d\n",
+				__func__, term, mdp_irq_mask, mdp_irq_enabled);
+	} else {
+		mdp_irq_mask &= ~term;
+		if (!mdp_irq_mask && mdp_irq_enabled) {
+			mdp_irq_enabled = 0;
+			disable_irq_nosync(mdp_irq);
+		}
+	}
+	spin_unlock(&mdp_lock);
+}
+
+void mdp_pipe_kickoff(uint32 term, struct msm_fb_data_type *mfd)
+{
+	unsigned long flag;
+	/* complete all the writes before starting */
+	wmb();
+
+	/* kick off PPP engine */
+	if (term == MDP_PPP_TERM) {
+		if (mdp_debug[MDP_PPP_BLOCK])
+			jiffies_to_timeval(jiffies, &mdp_ppp_timeval);
+
+		/* let's turn on PPP block */
+		mdp_pipe_ctrl(MDP_PPP_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+		mdp_enable_irq(term);
+		INIT_COMPLETION(mdp_ppp_comp);
+		spin_lock_irqsave(&mdp_spin_lock, flag);
+		mdp_ppp_waiting = TRUE;
+		spin_unlock_irqrestore(&mdp_spin_lock, flag);
+		outpdw(MDP_BASE + 0x30, 0x1000);
+		wait_for_completion_killable(&mdp_ppp_comp);
+		mdp_disable_irq(term);
+
+		if (mdp_debug[MDP_PPP_BLOCK]) {
+			struct timeval now;
+
+			jiffies_to_timeval(jiffies, &now);
+			mdp_ppp_timeval.tv_usec =
+			    now.tv_usec - mdp_ppp_timeval.tv_usec;
+			MSM_FB_DEBUG("MDP-PPP: %d\n",
+				    (int)mdp_ppp_timeval.tv_usec);
+		}
+	} else if (term == MDP_DMA2_TERM) {
+		if (mdp_debug[MDP_DMA2_BLOCK]) {
+			MSM_FB_DEBUG("MDP-DMA2: %d\n",
+				    (int)mdp_dma2_timeval.tv_usec);
+			jiffies_to_timeval(jiffies, &mdp_dma2_timeval);
+		}
+		/* DMA update timestamp */
+		mdp_dma2_last_update_time = ktime_get_real();
+		/* let's turn on DMA2 block */
+#ifdef CONFIG_FB_MSM_MDP22
+		outpdw(MDP_CMD_DEBUG_ACCESS_BASE + 0x0044, 0x0);/* start DMA */
+#else
+		mdp_lut_enable();
+
+#ifdef CONFIG_FB_MSM_MDP40
+		outpdw(MDP_BASE + 0x000c, 0x0);	/* start DMA */
+#else
+		outpdw(MDP_BASE + 0x0044, 0x0);	/* start DMA */
+
+#ifdef CONFIG_FB_MSM_MDP303
+
+#ifdef CONFIG_FB_MSM_MIPI_DSI
+		mipi_dsi_cmd_mdp_start();
+#endif
+
+#endif
+
+#endif
+#endif
+#ifdef CONFIG_FB_MSM_MDP40
+	} else if (term == MDP_DMA_S_TERM) {
+		mdp_pipe_ctrl(MDP_DMA_S_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+		outpdw(MDP_BASE + 0x0010, 0x0);	/* start DMA */
+	} else if (term == MDP_DMA_E_TERM) {
+		mdp_pipe_ctrl(MDP_DMA_E_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+		outpdw(MDP_BASE + 0x0014, 0x0);	/* start DMA */
+	} else if (term == MDP_OVERLAY0_TERM) {
+		mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+		mdp_lut_enable();
+		outpdw(MDP_BASE + 0x0004, 0);
+	} else if (term == MDP_OVERLAY1_TERM) {
+		mdp_pipe_ctrl(MDP_OVERLAY1_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+		mdp_lut_enable();
+		outpdw(MDP_BASE + 0x0008, 0);
+	} else if (term == MDP_OVERLAY2_TERM) {
+		mdp_pipe_ctrl(MDP_OVERLAY2_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+		mdp_lut_enable();
+		outpdw(MDP_BASE + 0x00D0, 0);
+	}
+#else
+	} else if (term == MDP_DMA_S_TERM) {
+		mdp_pipe_ctrl(MDP_DMA_S_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+		outpdw(MDP_BASE + 0x0048, 0x0);	/* start DMA */
+	} else if (term == MDP_DMA_E_TERM) {
+		mdp_pipe_ctrl(MDP_DMA_E_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+		outpdw(MDP_BASE + 0x004C, 0x0);
+	}
+#endif
+}
+
+static int mdp_clk_rate;
+static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST];
+static int pdev_list_cnt;
+
+static void mdp_pipe_ctrl_workqueue_handler(struct work_struct *work)
+{
+	mdp_pipe_ctrl(MDP_MASTER_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+void mdp_pipe_ctrl(MDP_BLOCK_TYPE block, MDP_BLOCK_POWER_STATE state,
+		   boolean isr)
+{
+	boolean mdp_all_blocks_off = TRUE;
+	int i;
+	unsigned long flag;
+	struct msm_fb_panel_data *pdata;
+
+	/*
+	 * It is assumed that if isr = TRUE then start = OFF
+	 * if start = ON when isr = TRUE it could happen that the usercontext
+	 * could turn off the clocks while the interrupt is updating the
+	 * power to ON
+	 */
+	WARN_ON(isr == TRUE && state == MDP_BLOCK_POWER_ON);
+
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	if (MDP_BLOCK_POWER_ON == state) {
+		atomic_inc(&mdp_block_power_cnt[block]);
+
+		if (MDP_DMA2_BLOCK == block)
+			mdp_in_processing = TRUE;
+	} else {
+		atomic_dec(&mdp_block_power_cnt[block]);
+
+		if (atomic_read(&mdp_block_power_cnt[block]) < 0) {
+			/*
+			* Master has to serve a request to power off MDP always
+			* It also has a timer to power off.  So, in case of
+			* timer expires first and DMA2 finishes later,
+			* master has to power off two times
+			* There shouldn't be multiple power-off request for
+			* other blocks
+			*/
+			if (block != MDP_MASTER_BLOCK) {
+				MSM_FB_INFO("mdp_block_power_cnt[block=%d] \
+				multiple power-off request\n", block);
+			}
+			atomic_set(&mdp_block_power_cnt[block], 0);
+		}
+
+		if (MDP_DMA2_BLOCK == block)
+			mdp_in_processing = FALSE;
+	}
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+
+	/*
+	 * If it's in isr, we send our request to workqueue.
+	 * Otherwise, processing happens in the current context
+	 */
+	if (isr) {
+		if (mdp_current_clk_on) {
+			/* checking all blocks power state */
+			for (i = 0; i < MDP_MAX_BLOCK; i++) {
+				if (atomic_read(&mdp_block_power_cnt[i]) > 0) {
+					mdp_all_blocks_off = FALSE;
+					break;
+				}
+			}
+
+			if (mdp_all_blocks_off) {
+				/* send workqueue to turn off mdp power */
+				queue_delayed_work(mdp_pipe_ctrl_wq,
+						   &mdp_pipe_ctrl_worker,
+						   mdp_timer_duration);
+			}
+		}
+	} else {
+		down(&mdp_pipe_ctrl_mutex);
+		/* checking all blocks power state */
+		for (i = 0; i < MDP_MAX_BLOCK; i++) {
+			if (atomic_read(&mdp_block_power_cnt[i]) > 0) {
+				mdp_all_blocks_off = FALSE;
+				break;
+			}
+		}
+
+		/*
+		 * find out whether a delayable work item is currently
+		 * pending
+		 */
+
+		if (delayed_work_pending(&mdp_pipe_ctrl_worker)) {
+			/*
+			 * try to cancel the current work if it fails to
+			 * stop (which means del_timer can't delete it
+			 * from the list, it's about to expire and run),
+			 * we have to let it run. queue_delayed_work won't
+			 * accept the next job which is same as
+			 * queue_delayed_work(mdp_timer_duration = 0)
+			 */
+			cancel_delayed_work(&mdp_pipe_ctrl_worker);
+		}
+
+		if ((mdp_all_blocks_off) && (mdp_current_clk_on)) {
+			mutex_lock(&mdp_suspend_mutex);
+			if (block == MDP_MASTER_BLOCK || mdp_suspended) {
+				mdp_current_clk_on = FALSE;
+				mb();
+				/* turn off MDP clks */
+				mdp_vsync_clk_disable();
+				for (i = 0; i < pdev_list_cnt; i++) {
+					pdata = (struct msm_fb_panel_data *)
+						pdev_list[i]->dev.platform_data;
+					if (pdata && pdata->clk_func)
+						pdata->clk_func(0);
+				}
+				if (mdp_clk != NULL) {
+					mdp_clk_rate = clk_get_rate(mdp_clk);
+					clk_disable_unprepare(mdp_clk);
+					if (mdp_hw_revision <=
+						MDP4_REVISION_V2_1 &&
+						mdp_clk_rate > 122880000) {
+						clk_set_rate(mdp_clk,
+							 122880000);
+					}
+					MSM_FB_DEBUG("MDP CLK OFF\n");
+				}
+				if (mdp_pclk != NULL) {
+					clk_disable_unprepare(mdp_pclk);
+					MSM_FB_DEBUG("MDP PCLK OFF\n");
+				}
+				if (mdp_lut_clk != NULL)
+					clk_disable_unprepare(mdp_lut_clk);
+			} else {
+				/* send workqueue to turn off mdp power */
+				queue_delayed_work(mdp_pipe_ctrl_wq,
+						   &mdp_pipe_ctrl_worker,
+						   mdp_timer_duration);
+			}
+			mutex_unlock(&mdp_suspend_mutex);
+		} else if ((!mdp_all_blocks_off) && (!mdp_current_clk_on)) {
+			mdp_current_clk_on = TRUE;
+			/* turn on MDP clks */
+			for (i = 0; i < pdev_list_cnt; i++) {
+				pdata = (struct msm_fb_panel_data *)
+					pdev_list[i]->dev.platform_data;
+				if (pdata && pdata->clk_func)
+					pdata->clk_func(1);
+			}
+			if (mdp_clk != NULL) {
+				if (mdp_hw_revision <=
+					MDP4_REVISION_V2_1 &&
+					mdp_clk_rate > 122880000) {
+					clk_set_rate(mdp_clk,
+						 mdp_clk_rate);
+				}
+				clk_prepare_enable(mdp_clk);
+				MSM_FB_DEBUG("MDP CLK ON\n");
+			}
+			if (mdp_pclk != NULL) {
+				clk_prepare_enable(mdp_pclk);
+				MSM_FB_DEBUG("MDP PCLK ON\n");
+			}
+			if (mdp_lut_clk != NULL)
+				clk_prepare_enable(mdp_lut_clk);
+			mdp_vsync_clk_enable();
+		}
+		up(&mdp_pipe_ctrl_mutex);
 	}
 }
 
-static int mdp_ppp_wait(struct mdp_info *mdp)
+void mdp_histogram_handle_isr(struct mdp_hist_mgmt *mgmt)
 {
-	return mdp_wait(mdp, DL0_ROI_DONE, &mdp_ppp_waitqueue);
+	uint32 isr, mask;
+	char *base_addr = MDP_BASE + mgmt->base;
+	isr = inpdw(base_addr + MDP_HIST_INTR_STATUS_OFF);
+	mask = inpdw(base_addr + MDP_HIST_INTR_ENABLE_OFF);
+	outpdw(base_addr + MDP_HIST_INTR_CLEAR_OFF, isr);
+	mb();
+	isr &= mask;
+	if (isr & INTR_HIST_RESET_SEQ_DONE)
+		__mdp_histogram_kickoff(mgmt);
+
+	if (isr & INTR_HIST_DONE) {
+		if ((waitqueue_active(&mgmt->mdp_hist_comp.wait))
+			 && (mgmt->hist != NULL)) {
+			if (!queue_work(mdp_hist_wq,
+						&mgmt->mdp_histogram_worker)) {
+				pr_err("%s %d- can't queue hist_read\n",
+							 __func__, mgmt->block);
+			}
+		} else {
+			__mdp_histogram_reset(mgmt);
+		}
+	}
 }
 
-void mdp_dma_to_mddi(struct mdp_info *mdp, uint32_t addr, uint32_t stride,
-		     uint32_t width, uint32_t height, uint32_t x, uint32_t y,
-		     struct msmfb_callback *callback)
+#ifndef CONFIG_FB_MSM_MDP40
+irqreturn_t mdp_isr(int irq, void *ptr)
 {
-	uint32_t dma2_cfg;
-	uint16_t ld_param = 0; /* 0=PRIM, 1=SECD, 2=EXT */
+	uint32 mdp_interrupt = 0;
+	struct mdp_dma_data *dma;
+	unsigned long flag;
+	struct mdp_hist_mgmt *mgmt = NULL;
+	char *base_addr;
+	int i, ret;
+	/* Ensure all the register write are complete */
+	mb();
 
-	if (enable_mdp_irq(mdp, DL0_DMA2_TERM_DONE)) {
-		printk(KERN_ERR "mdp_dma_to_mddi: busy\n");
+	mdp_is_in_isr = TRUE;
+
+	mdp_interrupt = inp32(MDP_INTR_STATUS);
+	outp32(MDP_INTR_CLEAR, mdp_interrupt);
+
+	mdp_interrupt &= mdp_intr_mask;
+
+	if (mdp_interrupt & TV_ENC_UNDERRUN) {
+		mdp_interrupt &= ~(TV_ENC_UNDERRUN);
+		mdp_tv_underflow_cnt++;
+	}
+
+	if (!mdp_interrupt)
+		goto out;
+
+	/* DMA3 TV-Out Start */
+	if (mdp_interrupt & TV_OUT_DMA3_START) {
+		/* let's disable TV out interrupt */
+		mdp_intr_mask &= ~TV_OUT_DMA3_START;
+		outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+
+		dma = &dma3_data;
+		if (dma->waiting) {
+			dma->waiting = FALSE;
+			complete(&dma->comp);
+		}
+	}
+
+	if (mdp_rev >= MDP_REV_30) {
+		/* Only DMA_P histogram exists for this MDP rev*/
+		if (mdp_interrupt & MDP_HIST_DONE) {
+			ret = mdp_histogram_block2mgmt(MDP_BLOCK_DMA_P, &mgmt);
+			if (!ret)
+				mdp_histogram_handle_isr(mgmt);
+			outp32(MDP_INTR_CLEAR, MDP_HIST_DONE);
+		}
+
+		/* LCDC UnderFlow */
+		if (mdp_interrupt & LCDC_UNDERFLOW) {
+			mdp_lcdc_underflow_cnt++;
+			/*when underflow happens HW resets all the histogram
+			  registers that were set before so restore them back
+			  to normal.*/
+			for (i = 0; i < MDP_HIST_MGMT_MAX; i++) {
+				mgmt = mdp_hist_mgmt_array[i];
+				if (!mgmt)
+					continue;
+
+				base_addr = MDP_BASE + mgmt->base;
+				outpdw(base_addr + 0x010, 1);
+				outpdw(base_addr + 0x01C, INTR_HIST_DONE |
+						INTR_HIST_RESET_SEQ_DONE);
+				mgmt->mdp_is_hist_valid = FALSE;
+				__mdp_histogram_reset(mgmt);
+			}
+		}
+
+		/* LCDC Frame Start */
+		if (mdp_interrupt & LCDC_FRAME_START) {
+			dma = &dma2_data;
+			spin_lock_irqsave(&mdp_spin_lock, flag);
+			/* let's disable LCDC interrupt */
+			mdp_intr_mask &= ~LCDC_FRAME_START;
+			outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+			if (dma->waiting) {
+				dma->waiting = FALSE;
+				complete(&dma->comp);
+			}
+			spin_unlock_irqrestore(&mdp_spin_lock, flag);
+		}
+
+		/* DMA2 LCD-Out Complete */
+		if (mdp_interrupt & MDP_DMA_S_DONE) {
+			dma = &dma_s_data;
+			dma->busy = FALSE;
+			mdp_pipe_ctrl(MDP_DMA_S_BLOCK, MDP_BLOCK_POWER_OFF,
+									TRUE);
+			complete(&dma->comp);
+		}
+
+		/* DMA_E LCD-Out Complete */
+		if (mdp_interrupt & MDP_DMA_E_DONE) {
+			dma = &dma_s_data;
+			dma->busy = FALSE;
+			mdp_pipe_ctrl(MDP_DMA_E_BLOCK, MDP_BLOCK_POWER_OFF,
+									TRUE);
+			complete(&dma->comp);
+		}
+	}
+
+	/* DMA2 LCD-Out Complete */
+	if (mdp_interrupt & MDP_DMA_P_DONE) {
+		struct timeval now;
+
+		mdp_dma2_last_update_time = ktime_sub(ktime_get_real(),
+			mdp_dma2_last_update_time);
+		if (mdp_debug[MDP_DMA2_BLOCK]) {
+			jiffies_to_timeval(jiffies, &now);
+			mdp_dma2_timeval.tv_usec =
+			    now.tv_usec - mdp_dma2_timeval.tv_usec;
+		}
+#ifndef CONFIG_FB_MSM_MDP303
+		dma = &dma2_data;
+		spin_lock_irqsave(&mdp_spin_lock, flag);
+		dma->busy = FALSE;
+		spin_unlock_irqrestore(&mdp_spin_lock, flag);
+		mdp_pipe_ctrl(MDP_DMA2_BLOCK, MDP_BLOCK_POWER_OFF, TRUE);
+		complete(&dma->comp);
+#else
+		if (mdp_prim_panel_type == MIPI_CMD_PANEL) {
+			dma = &dma2_data;
+			spin_lock_irqsave(&mdp_spin_lock, flag);
+			dma->busy = FALSE;
+			spin_unlock_irqrestore(&mdp_spin_lock, flag);
+			mdp_pipe_ctrl(MDP_DMA2_BLOCK, MDP_BLOCK_POWER_OFF,
+				TRUE);
+			complete(&dma->comp);
+		}
+#endif
+	}
+
+	/* PPP Complete */
+	if (mdp_interrupt & MDP_PPP_DONE) {
+#ifdef	CONFIG_FB_MSM_MDP31
+		MDP_OUTP(MDP_BASE + 0x00100, 0xFFFF);
+#endif
+		mdp_pipe_ctrl(MDP_PPP_BLOCK, MDP_BLOCK_POWER_OFF, TRUE);
+		spin_lock_irqsave(&mdp_spin_lock, flag);
+		if (mdp_ppp_waiting) {
+			mdp_ppp_waiting = FALSE;
+			complete(&mdp_ppp_comp);
+		}
+		spin_unlock_irqrestore(&mdp_spin_lock, flag);
+	}
+
+out:
+mdp_is_in_isr = FALSE;
+
+	return IRQ_HANDLED;
+}
+#endif
+
+static void mdp_drv_init(void)
+{
+	int i;
+
+	for (i = 0; i < MDP_MAX_BLOCK; i++) {
+		mdp_debug[i] = 0;
+	}
+
+	/* initialize spin lock and workqueue */
+	spin_lock_init(&mdp_spin_lock);
+	mdp_dma_wq = create_singlethread_workqueue("mdp_dma_wq");
+	mdp_vsync_wq = create_singlethread_workqueue("mdp_vsync_wq");
+	mdp_pipe_ctrl_wq = create_singlethread_workqueue("mdp_pipe_ctrl_wq");
+	INIT_DELAYED_WORK(&mdp_pipe_ctrl_worker,
+			  mdp_pipe_ctrl_workqueue_handler);
+
+	/* initialize semaphore */
+	init_completion(&mdp_ppp_comp);
+	sema_init(&mdp_ppp_mutex, 1);
+	sema_init(&mdp_pipe_ctrl_mutex, 1);
+
+	dma2_data.busy = FALSE;
+	dma2_data.dmap_busy = FALSE;
+	dma2_data.waiting = FALSE;
+	init_completion(&dma2_data.comp);
+	init_completion(&dma2_data.dmap_comp);
+	sema_init(&dma2_data.mutex, 1);
+	mutex_init(&dma2_data.ov_mutex);
+
+	dma3_data.busy = FALSE;
+	dma3_data.waiting = FALSE;
+	init_completion(&dma3_data.comp);
+	sema_init(&dma3_data.mutex, 1);
+
+	dma_s_data.busy = FALSE;
+	dma_s_data.waiting = FALSE;
+	init_completion(&dma_s_data.comp);
+	sema_init(&dma_s_data.mutex, 1);
+
+#ifndef CONFIG_FB_MSM_MDP303
+	dma_e_data.busy = FALSE;
+	dma_e_data.waiting = FALSE;
+	init_completion(&dma_e_data.comp);
+	mutex_init(&dma_e_data.ov_mutex);
+#endif
+#ifdef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL
+	dma_wb_data.busy = FALSE;
+	dma_wb_data.waiting = FALSE;
+	init_completion(&dma_wb_data.comp);
+	mutex_init(&dma_wb_data.ov_mutex);
+#endif
+
+	/* initializing mdp power block counter to 0 */
+	for (i = 0; i < MDP_MAX_BLOCK; i++) {
+		atomic_set(&mdp_block_power_cnt[i], 0);
+	}
+
+#ifdef MSM_FB_ENABLE_DBGFS
+	{
+		struct dentry *root;
+		char sub_name[] = "mdp";
+
+		root = msm_fb_get_debugfs_root();
+		if (root != NULL) {
+			mdp_dir = debugfs_create_dir(sub_name, root);
+
+			if (mdp_dir) {
+				msm_fb_debugfs_file_create(mdp_dir,
+					"dma2_update_time_in_usec",
+					(u32 *) &mdp_dma2_update_time_in_usec);
+				msm_fb_debugfs_file_create(mdp_dir,
+					"vs_rdcnt_slow",
+					(u32 *) &mdp_lcd_rd_cnt_offset_slow);
+				msm_fb_debugfs_file_create(mdp_dir,
+					"vs_rdcnt_fast",
+					(u32 *) &mdp_lcd_rd_cnt_offset_fast);
+				msm_fb_debugfs_file_create(mdp_dir,
+					"mdp_usec_diff_threshold",
+					(u32 *) &mdp_usec_diff_threshold);
+				msm_fb_debugfs_file_create(mdp_dir,
+					"mdp_current_clk_on",
+					(u32 *) &mdp_current_clk_on);
+#ifdef CONFIG_FB_MSM_LCDC
+				msm_fb_debugfs_file_create(mdp_dir,
+					"lcdc_start_x",
+					(u32 *) &first_pixel_start_x);
+				msm_fb_debugfs_file_create(mdp_dir,
+					"lcdc_start_y",
+					(u32 *) &first_pixel_start_y);
+#endif
+			}
+		}
+	}
+#endif
+}
+
+static int mdp_probe(struct platform_device *pdev);
+static int mdp_remove(struct platform_device *pdev);
+
+static int mdp_runtime_suspend(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: suspending...\n");
+	return 0;
+}
+
+static int mdp_runtime_resume(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: resuming...\n");
+	return 0;
+}
+
+static struct dev_pm_ops mdp_dev_pm_ops = {
+	.runtime_suspend = mdp_runtime_suspend,
+	.runtime_resume = mdp_runtime_resume,
+};
+
+
+static struct platform_driver mdp_driver = {
+	.probe = mdp_probe,
+	.remove = mdp_remove,
+#ifndef CONFIG_HAS_EARLYSUSPEND
+	.suspend = mdp_suspend,
+	.resume = NULL,
+#endif
+	.shutdown = NULL,
+	.driver = {
+		/*
+		 * Driver name must match the device name added in
+		 * platform.c.
+		 */
+		.name = "mdp",
+		.pm = &mdp_dev_pm_ops,
+	},
+};
+
+static int mdp_off(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct msm_fb_data_type *mfd = platform_get_drvdata(pdev);
+
+	mdp_histogram_ctrl_all(FALSE);
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	ret = panel_next_off(pdev);
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	if (mdp_rev >= MDP_REV_41 && mfd->panel.type == MIPI_CMD_PANEL)
+		mdp_dsi_cmd_overlay_suspend();
+	return ret;
+}
+
+static int mdp_on(struct platform_device *pdev)
+{
+	int ret = 0;
+
+#ifdef CONFIG_FB_MSM_MDP40
+	struct msm_fb_data_type *mfd;
+	mdp4_overlay_ctrl_db_reset();
+
+	mfd = platform_get_drvdata(pdev);
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	if (is_mdp4_hw_reset()) {
+		mdp_vsync_cfg_regs(mfd, FALSE);
+		mdp4_hw_init();
+		outpdw(MDP_BASE + 0x0038, mdp4_display_intf);
+	}
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+#endif
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	ret = panel_next_on(pdev);
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+#ifdef CONFIG_FB_MSM_MDP40
+	if (mfd->panel.type == MIPI_CMD_PANEL)
+		mdp4_dsi_cmd_overlay_restore();
+	else if (mfd->panel.type == MDDI_PANEL)
+		mdp4_mddi_overlay_restore();
+#endif
+
+	mdp_histogram_ctrl_all(TRUE);
+
+	return ret;
+}
+
+static int mdp_resource_initialized;
+static struct msm_panel_common_pdata *mdp_pdata;
+
+uint32 mdp_hw_revision;
+
+/*
+ * mdp_hw_revision:
+ * 0 == V1
+ * 1 == V2
+ * 2 == V2.1
+ *
+ */
+void mdp_hw_version(void)
+{
+	char *cp;
+	uint32 *hp;
+
+	if (mdp_pdata == NULL)
+		return;
+
+	mdp_hw_revision = MDP4_REVISION_NONE;
+	if (mdp_pdata->hw_revision_addr == 0)
+		return;
+
+	/* tlmmgpio2 shadow */
+	cp = (char *)ioremap(mdp_pdata->hw_revision_addr, 0x16);
+
+	if (cp == NULL)
+		return;
+
+	hp = (uint32 *)cp;	/* HW_REVISION_NUMBER */
+	mdp_hw_revision = *hp;
+	iounmap(cp);
+
+	mdp_hw_revision >>= 28;	/* bit 31:28 */
+	mdp_hw_revision &= 0x0f;
+
+	MSM_FB_DEBUG("%s: mdp_hw_revision=%x\n",
+				__func__, mdp_hw_revision);
+}
+
+#ifdef CONFIG_FB_MSM_MDP40
+static void configure_mdp_core_clk_table(uint32 min_clk_rate)
+{
+	uint8 count;
+	uint32 current_rate;
+	if (mdp_clk && mdp_pdata && mdp_pdata->mdp_core_clk_table) {
+		min_clk_rate = clk_round_rate(mdp_clk, min_clk_rate);
+		if (clk_set_rate(mdp_clk, min_clk_rate) < 0)
+			printk(KERN_ERR "%s: clk_set_rate failed\n",
+							 __func__);
+		else {
+			count = 0;
+			current_rate = clk_get_rate(mdp_clk);
+			while (count < mdp_pdata->num_mdp_clk) {
+				if (mdp_pdata->mdp_core_clk_table[count]
+						< current_rate) {
+					mdp_pdata->
+					mdp_core_clk_table[count] =
+							current_rate;
+				}
+				count++;
+			}
+		}
+	}
+}
+#endif
+
+#ifdef CONFIG_MSM_BUS_SCALING
+static uint32_t mdp_bus_scale_handle;
+int mdp_bus_scale_update_request(uint32_t index)
+{
+	if (!mdp_pdata && (!mdp_pdata->mdp_bus_scale_table
+	     || index > (mdp_pdata->mdp_bus_scale_table->num_usecases - 1))) {
+		printk(KERN_ERR "%s invalid table or index\n", __func__);
+		return -EINVAL;
+	}
+	if (mdp_bus_scale_handle < 1) {
+		pr_debug("%s invalid bus handle\n", __func__);
+		return -EINVAL;
+	}
+	return msm_bus_scale_client_update_request(mdp_bus_scale_handle,
+							index);
+}
+#endif
+DEFINE_MUTEX(mdp_clk_lock);
+int mdp_set_core_clk(uint16 perf_level)
+{
+	int ret = -EINVAL;
+	if (mdp_clk && mdp_pdata
+		 && mdp_pdata->mdp_core_clk_table) {
+		if (perf_level > mdp_pdata->num_mdp_clk)
+			printk(KERN_ERR "%s invalid perf level\n", __func__);
+		else {
+			mutex_lock(&mdp_clk_lock);
+			ret = clk_set_rate(mdp_clk,
+				mdp_pdata->
+				mdp_core_clk_table[mdp_pdata->num_mdp_clk
+						 - perf_level]);
+			mutex_unlock(&mdp_clk_lock);
+			if (ret) {
+				printk(KERN_ERR "%s unable to set mdp_core_clk rate\n",
+					__func__);
+			}
+		}
+	}
+	return ret;
+}
+
+unsigned long mdp_get_core_clk(void)
+{
+	unsigned long clk_rate = 0;
+	if (mdp_clk) {
+		mutex_lock(&mdp_clk_lock);
+		clk_rate = clk_get_rate(mdp_clk);
+		mutex_unlock(&mdp_clk_lock);
+	}
+
+	return clk_rate;
+}
+
+unsigned long mdp_perf_level2clk_rate(uint32 perf_level)
+{
+	unsigned long clk_rate = 0;
+
+	if (mdp_pdata && mdp_pdata->mdp_core_clk_table) {
+		if (perf_level > mdp_pdata->num_mdp_clk) {
+			printk(KERN_ERR "%s invalid perf level\n", __func__);
+			clk_rate = mdp_get_core_clk();
+		} else {
+			clk_rate = mdp_pdata->
+				mdp_core_clk_table[mdp_pdata->num_mdp_clk
+					- perf_level];
+		}
+	} else
+		clk_rate = mdp_get_core_clk();
+
+	return clk_rate;
+}
+
+static int mdp_irq_clk_setup(struct platform_device *pdev,
+	char cont_splashScreen)
+{
+	int ret;
+
+#ifdef CONFIG_FB_MSM_MDP40
+	ret = request_irq(mdp_irq, mdp4_isr, IRQF_DISABLED, "MDP", 0);
+#else
+	ret = request_irq(mdp_irq, mdp_isr, IRQF_DISABLED, "MDP", 0);
+#endif
+	if (ret) {
+		printk(KERN_ERR "mdp request_irq() failed!\n");
+		return ret;
+	}
+	disable_irq(mdp_irq);
+
+	footswitch = regulator_get(&pdev->dev, "vdd");
+	if (IS_ERR(footswitch))
+		footswitch = NULL;
+	else {
+		regulator_enable(footswitch);
+		mdp_footswitch_on = 1;
+
+		if (mdp_rev == MDP_REV_42 && !cont_splashScreen) {
+			regulator_disable(footswitch);
+			msleep(20);
+			regulator_enable(footswitch);
+		}
+	}
+
+	mdp_clk = clk_get(&pdev->dev, "core_clk");
+	if (IS_ERR(mdp_clk)) {
+		ret = PTR_ERR(mdp_clk);
+		printk(KERN_ERR "can't get mdp_clk error:%d!\n", ret);
+		free_irq(mdp_irq, 0);
+		return ret;
+	}
+
+	mdp_pclk = clk_get(&pdev->dev, "iface_clk");
+	if (IS_ERR(mdp_pclk))
+		mdp_pclk = NULL;
+
+	if (mdp_rev >= MDP_REV_42) {
+		mdp_lut_clk = clk_get(&pdev->dev, "lut_clk");
+		if (IS_ERR(mdp_lut_clk)) {
+			ret = PTR_ERR(mdp_lut_clk);
+			pr_err("can't get mdp_clk error:%d!\n", ret);
+			clk_put(mdp_clk);
+			free_irq(mdp_irq, 0);
+			return ret;
+		}
+	} else {
+		mdp_lut_clk = NULL;
+	}
+
+#ifdef CONFIG_FB_MSM_MDP40
+	/*
+	 * mdp_clk should greater than mdp_pclk always
+	 */
+	if (mdp_pdata && mdp_pdata->mdp_core_clk_rate) {
+		if (cont_splashScreen)
+			mdp_clk_rate = clk_get_rate(mdp_clk);
+		else
+			mdp_clk_rate = mdp_pdata->mdp_core_clk_rate;
+
+		mutex_lock(&mdp_clk_lock);
+		clk_set_rate(mdp_clk, mdp_clk_rate);
+		if (mdp_lut_clk != NULL)
+			clk_set_rate(mdp_lut_clk, mdp_clk_rate);
+		mutex_unlock(&mdp_clk_lock);
+	}
+
+	MSM_FB_DEBUG("mdp_clk: mdp_clk=%d\n", (int)clk_get_rate(mdp_clk));
+#endif
+	return 0;
+}
+
+static int mdp_probe(struct platform_device *pdev)
+{
+	struct platform_device *msm_fb_dev = NULL;
+	struct msm_fb_data_type *mfd;
+	struct msm_fb_panel_data *pdata = NULL;
+	int rc;
+	resource_size_t  size ;
+	unsigned long flag;
+#ifdef CONFIG_FB_MSM_MDP40
+	int intf, if_no;
+#endif
+#if defined(CONFIG_FB_MSM_MIPI_DSI) && defined(CONFIG_FB_MSM_MDP40)
+	struct mipi_panel_info *mipi;
+#endif
+	static int contSplash_update_done;
+
+	if ((pdev->id == 0) && (pdev->num_resources > 0)) {
+		mdp_init_pdev = pdev;
+		mdp_pdata = pdev->dev.platform_data;
+
+		size =  resource_size(&pdev->resource[0]);
+		msm_mdp_base = ioremap(pdev->resource[0].start, size);
+
+		MSM_FB_DEBUG("MDP HW Base phy_Address = 0x%x virt = 0x%x\n",
+			(int)pdev->resource[0].start, (int)msm_mdp_base);
+
+		if (unlikely(!msm_mdp_base))
+			return -ENOMEM;
+
+		mdp_irq = platform_get_irq(pdev, 0);
+		if (mdp_irq < 0) {
+			pr_err("mdp: can not get mdp irq\n");
+			return -ENOMEM;
+		}
+
+		mdp_rev = mdp_pdata->mdp_rev;
+
+		rc = mdp_irq_clk_setup(pdev, mdp_pdata->cont_splash_enabled);
+
+		if (rc)
+			return rc;
+
+		mdp_hw_version();
+
+		/* initializing mdp hw */
+#ifdef CONFIG_FB_MSM_MDP40
+		if (!(mdp_pdata->cont_splash_enabled))
+			mdp4_hw_init();
+#else
+		mdp_hw_init();
+#endif
+
+#ifdef CONFIG_FB_MSM_OVERLAY
+		mdp_hw_cursor_init();
+#endif
+		mdp_resource_initialized = 1;
+		return 0;
+	}
+
+	if (!mdp_resource_initialized)
+		return -EPERM;
+
+	mfd = platform_get_drvdata(pdev);
+
+	if (!mfd)
+		return -ENODEV;
+
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST)
+		return -ENOMEM;
+
+	msm_fb_dev = platform_device_alloc("msm_fb", pdev->id);
+	if (!msm_fb_dev)
+		return -ENOMEM;
+
+	/* link to the latest pdev */
+	mfd->pdev = msm_fb_dev;
+	mfd->mdp_rev = mdp_rev;
+
+	if (mdp_pdata) {
+		if (mdp_pdata->cont_splash_enabled) {
+			mfd->cont_splash_done = 0;
+			if (!contSplash_update_done) {
+				mdp_pipe_ctrl(MDP_CMD_BLOCK,
+					MDP_BLOCK_POWER_ON, FALSE);
+				contSplash_update_done = 1;
+			}
+		} else
+			mfd->cont_splash_done = 1;
+	}
+
+	mfd->ov0_wb_buf = MDP_ALLOC(sizeof(struct mdp_buf_type));
+	mfd->ov1_wb_buf = MDP_ALLOC(sizeof(struct mdp_buf_type));
+	memset((void *)mfd->ov0_wb_buf, 0, sizeof(struct mdp_buf_type));
+	memset((void *)mfd->ov1_wb_buf, 0, sizeof(struct mdp_buf_type));
+
+	if (mdp_pdata) {
+		mfd->ov0_wb_buf->size = mdp_pdata->ov0_wb_size;
+		mfd->ov1_wb_buf->size = mdp_pdata->ov1_wb_size;
+		mfd->mem_hid = mdp_pdata->mem_hid;
+	} else {
+		mfd->ov0_wb_buf->size = 0;
+		mfd->ov1_wb_buf->size = 0;
+		mfd->mem_hid = 0;
+	}
+	mfd->ov0_blt_state  = 0;
+	mfd->use_ov0_blt = 0 ;
+
+	/* initialize Post Processing data*/
+	mdp_hist_lut_init();
+	mdp_histogram_init();
+
+	/* add panel data */
+	if (platform_device_add_data
+	    (msm_fb_dev, pdev->dev.platform_data,
+	     sizeof(struct msm_fb_panel_data))) {
+		printk(KERN_ERR "mdp_probe: platform_device_add_data failed!\n");
+		rc = -ENOMEM;
+		goto mdp_probe_err;
+	}
+	/* data chain */
+	pdata = msm_fb_dev->dev.platform_data;
+	pdata->on = mdp_on;
+	pdata->off = mdp_off;
+	pdata->next = pdev;
+
+	mdp_prim_panel_type = mfd->panel.type;
+	switch (mfd->panel.type) {
+	case EXT_MDDI_PANEL:
+	case MDDI_PANEL:
+	case EBI2_PANEL:
+		INIT_WORK(&mfd->dma_update_worker,
+			  mdp_lcd_update_workqueue_handler);
+		INIT_WORK(&mfd->vsync_resync_worker,
+			  mdp_vsync_resync_workqueue_handler);
+		mfd->hw_refresh = FALSE;
+
+		if (mfd->panel.type == EXT_MDDI_PANEL) {
+			/* 15 fps -> 66 msec */
+			mfd->refresh_timer_duration = (66 * HZ / 1000);
+		} else {
+			/* 24 fps -> 42 msec */
+			mfd->refresh_timer_duration = (42 * HZ / 1000);
+		}
+
+#ifdef CONFIG_FB_MSM_MDP22
+		mfd->dma_fnc = mdp_dma2_update;
+		mfd->dma = &dma2_data;
+#else
+		if (mfd->panel_info.pdest == DISPLAY_1) {
+#if defined(CONFIG_FB_MSM_OVERLAY) && defined(CONFIG_FB_MSM_MDDI)
+			mfd->dma_fnc = mdp4_mddi_overlay;
+			mfd->cursor_update = mdp4_mddi_overlay_cursor;
+#else
+			mfd->dma_fnc = mdp_dma2_update;
+#endif
+			mfd->dma = &dma2_data;
+			mfd->lut_update = mdp_lut_update_nonlcdc;
+			mfd->do_histogram = mdp_do_histogram;
+			mfd->start_histogram = mdp_histogram_start;
+			mfd->stop_histogram = mdp_histogram_stop;
+		} else {
+			mfd->dma_fnc = mdp_dma_s_update;
+			mfd->dma = &dma_s_data;
+		}
+#endif
+		if (mdp_pdata)
+			mfd->vsync_gpio = mdp_pdata->gpio;
+		else
+			mfd->vsync_gpio = -1;
+
+#ifdef CONFIG_FB_MSM_MDP40
+		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+		spin_lock_irqsave(&mdp_spin_lock, flag);
+		mdp_intr_mask |= INTR_OVERLAY0_DONE;
+		if (mdp_hw_revision < MDP4_REVISION_V2_1) {
+			/* dmas dmap switch */
+			mdp_intr_mask |= INTR_DMA_S_DONE;
+		}
+		outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+		spin_unlock_irqrestore(&mdp_spin_lock, flag);
+		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+		if (mfd->panel.type == EBI2_PANEL)
+			intf = EBI2_INTF;
+		else
+			intf = MDDI_INTF;
+
+		if (mfd->panel_info.pdest == DISPLAY_1)
+			if_no = PRIMARY_INTF_SEL;
+		else
+			if_no = SECONDARY_INTF_SEL;
+
+		mdp4_display_intf_sel(if_no, intf);
+#endif
+		mdp_config_vsync(mdp_init_pdev, mfd);
+		break;
+
+#ifdef CONFIG_FB_MSM_MIPI_DSI
+	case MIPI_VIDEO_PANEL:
+#ifndef CONFIG_FB_MSM_MDP303
+		pdata->on = mdp4_dsi_video_on;
+		pdata->off = mdp4_dsi_video_off;
+		mfd->hw_refresh = TRUE;
+		mfd->dma_fnc = mdp4_dsi_video_overlay;
+		mfd->lut_update = mdp_lut_update_lcdc;
+		mfd->do_histogram = mdp_do_histogram;
+		mfd->start_histogram = mdp_histogram_start;
+		mfd->stop_histogram = mdp_histogram_stop;
+		if (mfd->panel_info.pdest == DISPLAY_1) {
+			if_no = PRIMARY_INTF_SEL;
+			mfd->dma = &dma2_data;
+		} else {
+			if_no = EXTERNAL_INTF_SEL;
+			mfd->dma = &dma_e_data;
+		}
+		mdp4_display_intf_sel(if_no, DSI_VIDEO_INTF);
+#else
+		pdata->on = mdp_dsi_video_on;
+		pdata->off = mdp_dsi_video_off;
+		mfd->hw_refresh = TRUE;
+		mfd->dma_fnc = mdp_dsi_video_update;
+		mfd->do_histogram = mdp_do_histogram;
+		mfd->start_histogram = mdp_histogram_start;
+		mfd->stop_histogram = mdp_histogram_stop;
+		if (mfd->panel_info.pdest == DISPLAY_1)
+			mfd->dma = &dma2_data;
+		else {
+			printk(KERN_ERR "Invalid Selection of destination panel\n");
+			rc = -ENODEV;
+			goto mdp_probe_err;
+		}
+
+#endif
+		if (mdp_rev >= MDP_REV_40)
+			mfd->cursor_update = mdp_hw_cursor_sync_update;
+		else
+			mfd->cursor_update = mdp_hw_cursor_update;
+		break;
+
+	case MIPI_CMD_PANEL:
+#ifndef CONFIG_FB_MSM_MDP303
+		mfd->dma_fnc = mdp4_dsi_cmd_overlay;
+		mipi = &mfd->panel_info.mipi;
+		configure_mdp_core_clk_table((mipi->dsi_pclk_rate) * 3 / 2);
+		if (mfd->panel_info.pdest == DISPLAY_1) {
+			if_no = PRIMARY_INTF_SEL;
+			mfd->dma = &dma2_data;
+		} else {
+			if_no = SECONDARY_INTF_SEL;
+			mfd->dma = &dma_s_data;
+		}
+		mfd->lut_update = mdp_lut_update_nonlcdc;
+		mfd->do_histogram = mdp_do_histogram;
+		mfd->start_histogram = mdp_histogram_start;
+		mfd->stop_histogram = mdp_histogram_stop;
+		mdp4_display_intf_sel(if_no, DSI_CMD_INTF);
+
+		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+		spin_lock_irqsave(&mdp_spin_lock, flag);
+		mdp_intr_mask |= INTR_OVERLAY0_DONE;
+		outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+		spin_unlock_irqrestore(&mdp_spin_lock, flag);
+		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+#else
+		mfd->dma_fnc = mdp_dma2_update;
+		mfd->do_histogram = mdp_do_histogram;
+		mfd->start_histogram = mdp_histogram_start;
+		mfd->stop_histogram = mdp_histogram_stop;
+		if (mfd->panel_info.pdest == DISPLAY_1)
+			mfd->dma = &dma2_data;
+		else {
+			printk(KERN_ERR "Invalid Selection of destination panel\n");
+			rc = -ENODEV;
+			goto mdp_probe_err;
+		}
+		INIT_WORK(&mfd->dma_update_worker,
+			mdp_lcd_update_workqueue_handler);
+#endif
+		mdp_config_vsync(mdp_init_pdev, mfd);
+		break;
+#endif
+
+#ifdef CONFIG_FB_MSM_DTV
+	case DTV_PANEL:
+		pdata->on = mdp4_dtv_on;
+		pdata->off = mdp4_dtv_off;
+		mfd->hw_refresh = TRUE;
+		mfd->cursor_update = mdp_hw_cursor_update;
+		mfd->dma_fnc = mdp4_dtv_overlay;
+		mfd->dma = &dma_e_data;
+		mdp4_display_intf_sel(EXTERNAL_INTF_SEL, DTV_INTF);
+		break;
+#endif
+	case HDMI_PANEL:
+	case LCDC_PANEL:
+	case LVDS_PANEL:
+		pdata->on = mdp_lcdc_on;
+		pdata->off = mdp_lcdc_off;
+		mfd->hw_refresh = TRUE;
+#if	defined(CONFIG_FB_MSM_OVERLAY) && defined(CONFIG_FB_MSM_MDP40)
+		mfd->cursor_update = mdp_hw_cursor_sync_update;
+#else
+		mfd->cursor_update = mdp_hw_cursor_update;
+#endif
+#ifndef CONFIG_FB_MSM_MDP22
+		mfd->lut_update = mdp_lut_update_lcdc;
+		mfd->do_histogram = mdp_do_histogram;
+		mfd->start_histogram = mdp_histogram_start;
+		mfd->stop_histogram = mdp_histogram_stop;
+#endif
+#ifdef CONFIG_FB_MSM_OVERLAY
+		mfd->dma_fnc = mdp4_lcdc_overlay;
+#else
+		mfd->dma_fnc = mdp_lcdc_update;
+#endif
+
+#ifdef CONFIG_FB_MSM_MDP40
+		configure_mdp_core_clk_table((mfd->panel_info.clk_rate)
+								* 23 / 20);
+		if (mfd->panel.type == HDMI_PANEL) {
+			mfd->dma = &dma_e_data;
+			mdp4_display_intf_sel(EXTERNAL_INTF_SEL, LCDC_RGB_INTF);
+		} else {
+			mfd->dma = &dma2_data;
+			mdp4_display_intf_sel(PRIMARY_INTF_SEL, LCDC_RGB_INTF);
+		}
+#else
+		mfd->dma = &dma2_data;
+		spin_lock_irqsave(&mdp_spin_lock, flag);
+		mdp_intr_mask &= ~MDP_DMA_P_DONE;
+		outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+		spin_unlock_irqrestore(&mdp_spin_lock, flag);
+#endif
+		break;
+
+	case TV_PANEL:
+#if defined(CONFIG_FB_MSM_OVERLAY) && defined(CONFIG_FB_MSM_TVOUT)
+		pdata->on = mdp4_atv_on;
+		pdata->off = mdp4_atv_off;
+		mfd->dma_fnc = mdp4_atv_overlay;
+		mfd->dma = &dma_e_data;
+		mdp4_display_intf_sel(EXTERNAL_INTF_SEL, TV_INTF);
+#else
+		pdata->on = mdp_dma3_on;
+		pdata->off = mdp_dma3_off;
+		mfd->hw_refresh = TRUE;
+		mfd->dma_fnc = mdp_dma3_update;
+		mfd->dma = &dma3_data;
+#endif
+		break;
+
+#ifdef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL
+	case WRITEBACK_PANEL:
+		{
+			unsigned int mdp_version;
+			mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON,
+						 FALSE);
+			mdp_version = inpdw(MDP_BASE + 0x0);
+			mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF,
+						FALSE);
+			if (mdp_version < 0x04030303) {
+				pr_err("%s: writeback panel not supprted\n",
+					 __func__);
+				platform_device_put(msm_fb_dev);
+				return -ENODEV;
+			}
+			pdata->on = mdp4_overlay_writeback_on;
+			pdata->off = mdp4_overlay_writeback_off;
+			mfd->dma_fnc = mdp4_writeback_overlay;
+			mfd->dma = &dma_wb_data;
+			mdp4_display_intf_sel(EXTERNAL_INTF_SEL, DTV_INTF);
+		}
+		break;
+#endif
+	default:
+		printk(KERN_ERR "mdp_probe: unknown device type!\n");
+		rc = -ENODEV;
+		goto mdp_probe_err;
+	}
+#ifdef CONFIG_FB_MSM_MDP40
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	mdp4_display_intf = inpdw(MDP_BASE + 0x0038);
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+#endif
+
+#ifdef CONFIG_MSM_BUS_SCALING
+	if (!mdp_bus_scale_handle && mdp_pdata &&
+		mdp_pdata->mdp_bus_scale_table) {
+		mdp_bus_scale_handle =
+			msm_bus_scale_register_client(
+					mdp_pdata->mdp_bus_scale_table);
+		if (!mdp_bus_scale_handle) {
+			printk(KERN_ERR "%s not able to get bus scale\n",
+				__func__);
+			return -ENOMEM;
+		}
+	}
+
+	/* req bus bandwidth immediately */
+	if (!(mfd->cont_splash_done))
+		mdp_bus_scale_update_request(5);
+
+#endif
+
+	/* set driver data */
+	platform_set_drvdata(msm_fb_dev, mfd);
+
+	rc = platform_device_add(msm_fb_dev);
+	if (rc) {
+		goto mdp_probe_err;
+	}
+
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+
+	pdev_list[pdev_list_cnt++] = pdev;
+	mdp4_extn_disp = 0;
+	return 0;
+
+      mdp_probe_err:
+	platform_device_put(msm_fb_dev);
+#ifdef CONFIG_MSM_BUS_SCALING
+	if (mdp_pdata && mdp_pdata->mdp_bus_scale_table &&
+		mdp_bus_scale_handle > 0)
+		msm_bus_scale_unregister_client(mdp_bus_scale_handle);
+#endif
+	return rc;
+}
+
+unsigned int mdp_check_suspended(void)
+{
+	unsigned int ret;
+
+	mutex_lock(&mdp_suspend_mutex);
+	ret = mdp_suspended;
+	mutex_unlock(&mdp_suspend_mutex);
+
+	return ret;
+}
+
+void mdp_footswitch_ctrl(boolean on)
+{
+	mutex_lock(&mdp_suspend_mutex);
+	if (!mdp_suspended || mdp4_extn_disp || !footswitch ||
+		mdp_rev <= MDP_REV_41) {
+		mutex_unlock(&mdp_suspend_mutex);
 		return;
 	}
 
-	dma_callback = callback;
-
-	dma2_cfg = DMA_PACK_TIGHT |
-		DMA_PACK_ALIGN_LSB |
-		DMA_PACK_PATTERN_RGB |
-		DMA_OUT_SEL_AHB |
-		DMA_IBUF_NONCONTIGUOUS;
-
-	dma2_cfg |= DMA_IBUF_FORMAT_RGB565;
-
-	dma2_cfg |= DMA_OUT_SEL_MDDI;
-
-	dma2_cfg |= DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY;
-
-	dma2_cfg |= DMA_DITHER_EN;
-
-	/* setup size, address, and stride */
-	mdp_writel(mdp, (height << 16) | (width),
-		   MDP_CMD_DEBUG_ACCESS_BASE + 0x0184);
-	mdp_writel(mdp, addr, MDP_CMD_DEBUG_ACCESS_BASE + 0x0188);
-	mdp_writel(mdp, stride, MDP_CMD_DEBUG_ACCESS_BASE + 0x018C);
-
-	/* 666 18BPP */
-	dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
-
-	/* set y & x offset and MDDI transaction parameters */
-	mdp_writel(mdp, (y << 16) | (x), MDP_CMD_DEBUG_ACCESS_BASE + 0x0194);
-	mdp_writel(mdp, ld_param, MDP_CMD_DEBUG_ACCESS_BASE + 0x01a0);
-	mdp_writel(mdp, (MDDI_VDO_PACKET_DESC << 16) | MDDI_VDO_PACKET_PRIM,
-		   MDP_CMD_DEBUG_ACCESS_BASE + 0x01a4);
-
-	mdp_writel(mdp, dma2_cfg, MDP_CMD_DEBUG_ACCESS_BASE + 0x0180);
-
-	/* start DMA2 */
-	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0044);
-}
-
-void mdp_dma(struct mdp_device *mdp_dev, uint32_t addr, uint32_t stride,
-	     uint32_t width, uint32_t height, uint32_t x, uint32_t y,
-	     struct msmfb_callback *callback, int interface)
-{
-	struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
-
-	if (interface == MSM_MDDI_PMDH_INTERFACE) {
-		mdp_dma_to_mddi(mdp, addr, stride, width, height, x, y,
-				callback);
-	}
-}
-
-int get_img(struct mdp_img *img, struct fb_info *info,
-	    unsigned long *start, unsigned long *len,
-	    struct file **filep)
-{
-	int put_needed, ret = 0;
-	struct file *file;
-
-	file = fget_light(img->memory_id, &put_needed);
-	if (file == NULL)
-		return -1;
-
-	if (MAJOR(file->f_dentry->d_inode->i_rdev) == FB_MAJOR) {
-		*start = info->fix.smem_start;
-		*len = info->fix.smem_len;
-	} else
-		ret = -1;
-	fput_light(file, put_needed);
-
-	return ret;
-}
-
-void put_img(struct file *src_file, struct file *dst_file)
-{
-}
-
-int mdp_blit(struct mdp_device *mdp_dev, struct fb_info *fb,
-	     struct mdp_blit_req *req)
-{
-	int ret;
-	unsigned long src_start = 0, src_len = 0, dst_start = 0, dst_len = 0;
-	struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
-	struct file *src_file = 0, *dst_file = 0;
-
-	/* WORKAROUND FOR HARDWARE BUG IN BG TILE FETCH */
-	if (unlikely(req->src_rect.h == 0 ||
-		     req->src_rect.w == 0)) {
-		printk(KERN_ERR "mpd_ppp: src img of zero size!\n");
-		return -EINVAL;
-	}
-	if (unlikely(req->dst_rect.h == 0 ||
-		     req->dst_rect.w == 0))
-		return -EINVAL;
-
-	/* do this first so that if this fails, the caller can always
-	 * safely call put_img */
-	if (unlikely(get_img(&req->src, fb, &src_start, &src_len, &src_file))) {
-		printk(KERN_ERR "mpd_ppp: could not retrieve src image from "
-				"memory\n");
-		return -EINVAL;
+	if (on && !mdp_footswitch_on) {
+		pr_debug("Enable MDP FS\n");
+		regulator_enable(footswitch);
+		mdp_footswitch_on = 1;
+	} else if (!on && mdp_footswitch_on) {
+		pr_debug("Disable MDP FS\n");
+		regulator_disable(footswitch);
+		mdp_footswitch_on = 0;
 	}
 
-	if (unlikely(get_img(&req->dst, fb, &dst_start, &dst_len, &dst_file))) {
-		printk(KERN_ERR "mpd_ppp: could not retrieve dst image from "
-				"memory\n");
-		return -EINVAL;
-	}
-	mutex_lock(&mdp_mutex);
+	mutex_unlock(&mdp_suspend_mutex);
+}
 
-	/* transp_masking unimplemented */
-	req->transp_mask = MDP_TRANSP_NOP;
-	if (unlikely((req->transp_mask != MDP_TRANSP_NOP ||
-		      req->alpha != MDP_ALPHA_NOP ||
-		      HAS_ALPHA(req->src.format)) &&
-		     (req->flags & MDP_ROT_90 &&
-		      req->dst_rect.w <= 16 && req->dst_rect.h >= 16))) {
-		int i;
-		unsigned int tiles = req->dst_rect.h / 16;
-		unsigned int remainder = req->dst_rect.h % 16;
-		req->src_rect.w = 16*req->src_rect.w / req->dst_rect.h;
-		req->dst_rect.h = 16;
-		for (i = 0; i < tiles; i++) {
-			enable_mdp_irq(mdp, DL0_ROI_DONE);
-			ret = mdp_ppp_blit(mdp, req, src_file, src_start,
-					   src_len, dst_file, dst_start,
-					   dst_len);
-			if (ret)
-				goto err_bad_blit;
-			ret = mdp_ppp_wait(mdp);
-			if (ret)
-				goto err_wait_failed;
-			req->dst_rect.y += 16;
-			req->src_rect.x += req->src_rect.w;
+#ifdef CONFIG_PM
+static void mdp_suspend_sub(void)
+{
+	/* cancel pipe ctrl worker */
+	cancel_delayed_work(&mdp_pipe_ctrl_worker);
+
+	/* for workder can't be cancelled... */
+	flush_workqueue(mdp_pipe_ctrl_wq);
+
+	/* let's wait for PPP completion */
+	while (atomic_read(&mdp_block_power_cnt[MDP_PPP_BLOCK]) > 0)
+		cpu_relax();
+
+	/* try to power down */
+	mdp_pipe_ctrl(MDP_MASTER_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	mutex_lock(&mdp_suspend_mutex);
+	mdp_suspended = TRUE;
+	mutex_unlock(&mdp_suspend_mutex);
+}
+#endif
+
+#if defined(CONFIG_PM) && !defined(CONFIG_HAS_EARLYSUSPEND)
+static int mdp_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	if (pdev->id == 0) {
+		mdp_suspend_sub();
+		if (mdp_current_clk_on) {
+			printk(KERN_WARNING"MDP suspend failed\n");
+			return -EBUSY;
 		}
-		if (!remainder)
-			goto end;
-		req->src_rect.w = remainder*req->src_rect.w / req->dst_rect.h;
-		req->dst_rect.h = remainder;
 	}
-	enable_mdp_irq(mdp, DL0_ROI_DONE);
-	ret = mdp_ppp_blit(mdp, req, src_file, src_start, src_len, dst_file,
-			   dst_start,
-			   dst_len);
-	if (ret)
-		goto err_bad_blit;
-	ret = mdp_ppp_wait(mdp);
-	if (ret)
-		goto err_wait_failed;
-end:
-	put_img(src_file, dst_file);
-	mutex_unlock(&mdp_mutex);
+
 	return 0;
-err_bad_blit:
-	disable_mdp_irq(mdp, DL0_ROI_DONE);
-err_wait_failed:
-	put_img(src_file, dst_file);
-	mutex_unlock(&mdp_mutex);
-	return ret;
+}
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void mdp_early_suspend(struct early_suspend *h)
+{
+	mdp_suspend_sub();
+#ifdef CONFIG_FB_MSM_DTV
+	mdp4_dtv_set_black_screen();
+#endif
+	mdp4_iommu_detach();
+	mdp_footswitch_ctrl(FALSE);
 }
 
-void mdp_set_grp_disp(struct mdp_device *mdp_dev, unsigned disp_id)
+static void mdp_early_resume(struct early_suspend *h)
 {
-	struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
+	mdp_footswitch_ctrl(TRUE);
+	mutex_lock(&mdp_suspend_mutex);
+	mdp_suspended = FALSE;
+	mutex_unlock(&mdp_suspend_mutex);
+}
+#endif
 
-	disp_id &= 0xf;
-	mdp_writel(mdp, disp_id, MDP_FULL_BYPASS_WORD43);
+static int mdp_remove(struct platform_device *pdev)
+{
+	if (footswitch != NULL)
+		regulator_put(footswitch);
+	iounmap(msm_mdp_base);
+	pm_runtime_disable(&pdev->dev);
+#ifdef CONFIG_MSM_BUS_SCALING
+	if (mdp_pdata && mdp_pdata->mdp_bus_scale_table &&
+		mdp_bus_scale_handle > 0)
+		msm_bus_scale_unregister_client(mdp_bus_scale_handle);
+#endif
+	return 0;
 }
 
-int register_mdp_client(struct class_interface *cint)
+static int mdp_register_driver(void)
 {
-	if (!mdp_class) {
-		pr_err("mdp: no mdp_class when registering mdp client\n");
-		return -ENODEV;
-	}
-	cint->class = mdp_class;
-	return class_interface_register(cint);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB - 1;
+	early_suspend.suspend = mdp_early_suspend;
+	early_suspend.resume = mdp_early_resume;
+	register_early_suspend(&early_suspend);
+#endif
+
+	return platform_driver_register(&mdp_driver);
 }
 
-#include "mdp_csc_table.h"
-#include "mdp_scale_tables.h"
-
-int mdp_probe(struct platform_device *pdev)
+static int __init mdp_driver_init(void)
 {
-	struct resource *resource;
 	int ret;
-	int n;
-	struct mdp_info *mdp;
 
-	resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!resource) {
-		pr_err("mdp: can not get mdp mem resource!\n");
-		return -ENOMEM;
+	mdp_drv_init();
+
+	ret = mdp_register_driver();
+	if (ret) {
+		printk(KERN_ERR "mdp_register_driver() failed!\n");
+		return ret;
 	}
 
-	mdp = kzalloc(sizeof(struct mdp_info), GFP_KERNEL);
-	if (!mdp)
-		return -ENOMEM;
+#if defined(CONFIG_DEBUG_FS)
+	mdp_debugfs_init();
+#endif
 
-	mdp->irq = platform_get_irq(pdev, 0);
-	if (mdp->irq < 0) {
-		pr_err("mdp: can not get mdp irq\n");
-		ret = mdp->irq;
-		goto error_get_irq;
-	}
-
-	mdp->base = ioremap(resource->start, resource_size(resource));
-	if (mdp->base == 0) {
-		printk(KERN_ERR "msmfb: cannot allocate mdp regs!\n");
-		ret = -ENOMEM;
-		goto error_ioremap;
-	}
-
-	mdp->mdp_dev.dma = mdp_dma;
-	mdp->mdp_dev.dma_wait = mdp_dma_wait;
-	mdp->mdp_dev.blit = mdp_blit;
-	mdp->mdp_dev.set_grp_disp = mdp_set_grp_disp;
-
-	clk = clk_get(&pdev->dev, "mdp_clk");
-	if (IS_ERR(clk)) {
-		printk(KERN_INFO "mdp: failed to get mdp clk");
-		ret = PTR_ERR(clk);
-		goto error_get_clk;
-	}
-
-	ret = request_irq(mdp->irq, mdp_isr, 0, "msm_mdp", mdp);
-	if (ret)
-		goto error_request_irq;
-	disable_irq(mdp->irq);
-	mdp_irq_mask = 0;
-
-	/* debug interface write access */
-	mdp_writel(mdp, 1, 0x60);
-
-	mdp_writel(mdp, MDP_ANY_INTR_MASK, MDP_INTR_ENABLE);
-	mdp_writel(mdp, 1, MDP_EBI2_PORTMAP_MODE);
-
-	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01f8);
-	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01fc);
-
-	for (n = 0; n < ARRAY_SIZE(csc_table); n++)
-		mdp_writel(mdp, csc_table[n].val, csc_table[n].reg);
-
-	/* clear up unused fg/main registers */
-	/* comp.plane 2&3 ystride */
-	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0120);
-
-	/* unpacked pattern */
-	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x012c);
-	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0130);
-	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0134);
-	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0158);
-	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x015c);
-	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0160);
-	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0170);
-	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0174);
-	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x017c);
-
-	/* comp.plane 2 & 3 */
-	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0114);
-	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0118);
-
-	/* clear unused bg registers */
-	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01c8);
-	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01d0);
-	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01dc);
-	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01e0);
-	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01e4);
-
-	for (n = 0; n < ARRAY_SIZE(mdp_upscale_table); n++)
-		mdp_writel(mdp, mdp_upscale_table[n].val,
-		       mdp_upscale_table[n].reg);
-
-	for (n = 0; n < 9; n++)
-		mdp_writel(mdp, mdp_default_ccs[n], 0x40440 + 4 * n);
-	mdp_writel(mdp, mdp_default_ccs[9], 0x40500 + 4 * 0);
-	mdp_writel(mdp, mdp_default_ccs[10], 0x40500 + 4 * 0);
-	mdp_writel(mdp, mdp_default_ccs[11], 0x40500 + 4 * 0);
-
-	/* register mdp device */
-	mdp->mdp_dev.dev.parent = &pdev->dev;
-	mdp->mdp_dev.dev.class = mdp_class;
-	dev_set_name(&mdp->mdp_dev.dev, "mdp%d", pdev->id);
-
-	/* if you can remove the platform device you'd have to implement
-	 * this:
-	mdp_dev.release = mdp_class; */
-
-	ret = device_register(&mdp->mdp_dev.dev);
-	if (ret)
-		goto error_device_register;
 	return 0;
 
-error_device_register:
-	free_irq(mdp->irq, mdp);
-error_request_irq:
-error_get_clk:
-	iounmap(mdp->base);
-error_get_irq:
-error_ioremap:
-	kfree(mdp);
-	return ret;
 }
 
-static struct platform_driver msm_mdp_driver = {
-	.probe = mdp_probe,
-	.driver = {.name = "msm_mdp"},
-};
-
-static int __init mdp_init(void)
-{
-	mdp_class = class_create(THIS_MODULE, "msm_mdp");
-	if (IS_ERR(mdp_class)) {
-		printk(KERN_ERR "Error creating mdp class\n");
-		return PTR_ERR(mdp_class);
-	}
-	return platform_driver_register(&msm_mdp_driver);
-}
-
-subsys_initcall(mdp_init);
+module_init(mdp_driver_init);
diff --git a/drivers/video/msm/mdp.h b/drivers/video/msm/mdp.h
new file mode 100644
index 0000000..b104b33
--- /dev/null
+++ b/drivers/video/msm/mdp.h
@@ -0,0 +1,845 @@
+/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef MDP_H
+#define MDP_H
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/fb.h>
+#include <linux/hrtimer.h>
+#include <linux/msm_mdp.h>
+#include <linux/memory_alloc.h>
+#include <mach/hardware.h>
+#include <linux/ion.h>
+
+#ifdef CONFIG_MSM_BUS_SCALING
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
+#endif
+
+#include <linux/io.h>
+
+#include <asm/system.h>
+#include <asm/mach-types.h>
+
+#include "msm_fb_panel.h"
+
+extern uint32 mdp_hw_revision;
+extern ulong mdp4_display_intf;
+extern spinlock_t mdp_spin_lock;
+extern int mdp_rev;
+extern struct mdp_csc_cfg mdp_csc_convert[4];
+
+extern struct workqueue_struct *mdp_hist_wq;
+
+#define MDP4_REVISION_V1		0
+#define MDP4_REVISION_V2		1
+#define MDP4_REVISION_V2_1	2
+#define MDP4_REVISION_NONE	0xffffffff
+
+#ifdef BIT
+#undef BIT
+#endif
+
+#define BIT(x)  (1<<(x))
+
+#define MDPOP_NOP               0
+#define MDPOP_LR                BIT(0)	/* left to right flip */
+#define MDPOP_UD                BIT(1)	/* up and down flip */
+#define MDPOP_ROT90             BIT(2)	/* rotate image to 90 degree */
+#define MDPOP_ROT180            (MDPOP_UD|MDPOP_LR)
+#define MDPOP_ROT270            (MDPOP_ROT90|MDPOP_UD|MDPOP_LR)
+#define MDPOP_ASCALE            BIT(7)
+#define MDPOP_ALPHAB            BIT(8)	/* enable alpha blending */
+#define MDPOP_TRANSP            BIT(9)	/* enable transparency */
+#define MDPOP_DITHER            BIT(10)	/* enable dither */
+#define MDPOP_SHARPENING	BIT(11) /* enable sharpening */
+#define MDPOP_BLUR		BIT(12) /* enable blur */
+#define MDPOP_FG_PM_ALPHA       BIT(13)
+#define MDP_ALLOC(x)  kmalloc(x, GFP_KERNEL)
+
+struct mdp_buf_type {
+	struct ion_handle *ihdl;
+	u32 phys_addr;
+	u32 size;
+};
+
+struct mdp_table_entry {
+	uint32_t reg;
+	uint32_t val;
+};
+
+extern struct mdp_ccs mdp_ccs_yuv2rgb ;
+extern struct mdp_ccs mdp_ccs_rgb2yuv ;
+extern unsigned char hdmi_prim_display;
+
+/*
+ * MDP Image Structure
+ */
+typedef struct mdpImg_ {
+	uint32 imgType;		/* Image type */
+	uint32 *bmy_addr;	/* bitmap or y addr */
+	uint32 *cbcr_addr;	/* cbcr addr */
+	uint32 width;		/* image width */
+	uint32 mdpOp;		/* image opertion (rotation,flip up/down, alpha/tp) */
+	uint32 tpVal;		/* transparency color */
+	uint32 alpha;		/* alpha percentage 0%(0x0) ~ 100%(0x100) */
+	int    sp_value;        /* sharpening strength */
+} MDPIMG;
+
+#define MDP_OUTP(addr, data) outpdw((addr), (data))
+
+#define MDP_BASE msm_mdp_base
+
+typedef enum {
+	MDP_BC_SCALE_POINT2_POINT4,
+	MDP_BC_SCALE_POINT4_POINT6,
+	MDP_BC_SCALE_POINT6_POINT8,
+	MDP_BC_SCALE_POINT8_1,
+	MDP_BC_SCALE_UP,
+	MDP_PR_SCALE_POINT2_POINT4,
+	MDP_PR_SCALE_POINT4_POINT6,
+	MDP_PR_SCALE_POINT6_POINT8,
+	MDP_PR_SCALE_POINT8_1,
+	MDP_PR_SCALE_UP,
+	MDP_SCALE_BLUR,
+	MDP_INIT_SCALE
+} MDP_SCALE_MODE;
+
+typedef enum {
+	MDP_BLOCK_POWER_OFF,
+	MDP_BLOCK_POWER_ON
+} MDP_BLOCK_POWER_STATE;
+
+typedef enum {
+	MDP_CMD_BLOCK,
+	MDP_OVERLAY0_BLOCK,
+	MDP_MASTER_BLOCK,
+	MDP_PPP_BLOCK,
+	MDP_DMA2_BLOCK,
+	MDP_DMA3_BLOCK,
+	MDP_DMA_S_BLOCK,
+	MDP_DMA_E_BLOCK,
+	MDP_OVERLAY1_BLOCK,
+	MDP_OVERLAY2_BLOCK,
+	MDP_MAX_BLOCK
+} MDP_BLOCK_TYPE;
+
+/* Let's keep Q Factor power of 2 for optimization */
+#define MDP_SCALE_Q_FACTOR 512
+
+#ifdef CONFIG_FB_MSM_MDP31
+#define MDP_MAX_X_SCALE_FACTOR (MDP_SCALE_Q_FACTOR*8)
+#define MDP_MIN_X_SCALE_FACTOR (MDP_SCALE_Q_FACTOR/8)
+#define MDP_MAX_Y_SCALE_FACTOR (MDP_SCALE_Q_FACTOR*8)
+#define MDP_MIN_Y_SCALE_FACTOR (MDP_SCALE_Q_FACTOR/8)
+#else
+#define MDP_MAX_X_SCALE_FACTOR (MDP_SCALE_Q_FACTOR*4)
+#define MDP_MIN_X_SCALE_FACTOR (MDP_SCALE_Q_FACTOR/4)
+#define MDP_MAX_Y_SCALE_FACTOR (MDP_SCALE_Q_FACTOR*4)
+#define MDP_MIN_Y_SCALE_FACTOR (MDP_SCALE_Q_FACTOR/4)
+#endif
+
+/* SHIM Q Factor */
+#define PHI_Q_FACTOR          29
+#define PQF_PLUS_5            (PHI_Q_FACTOR + 5)	/* due to 32 phases */
+#define PQF_PLUS_4            (PHI_Q_FACTOR + 4)
+#define PQF_PLUS_2            (PHI_Q_FACTOR + 2)	/* to get 4.0 */
+#define PQF_MINUS_2           (PHI_Q_FACTOR - 2)	/* to get 0.25 */
+#define PQF_PLUS_5_PLUS_2     (PQF_PLUS_5 + 2)
+#define PQF_PLUS_5_MINUS_2    (PQF_PLUS_5 - 2)
+
+#define MDP_CONVTP(tpVal) (((tpVal&0xF800)<<8)|((tpVal&0x7E0)<<5)|((tpVal&0x1F)<<3))
+
+#define MDPOP_ROTATION (MDPOP_ROT90|MDPOP_LR|MDPOP_UD)
+#define MDP_CHKBIT(val, bit) ((bit) == ((val) & (bit)))
+
+/* overlay interface API defines */
+typedef enum {
+	MORE_IBUF,
+	FINAL_IBUF,
+	COMPLETE_IBUF
+} MDP_IBUF_STATE;
+
+struct mdp_dirty_region {
+	__u32 xoffset;		/* source origin in the x-axis */
+	__u32 yoffset;		/* source origin in the y-axis */
+	__u32 width;		/* number of pixels in the x-axis */
+	__u32 height;		/* number of pixels in the y-axis */
+};
+
+/*
+ * MDP extended data types
+ */
+typedef struct mdp_roi_s {
+	uint32 x;
+	uint32 y;
+	uint32 width;
+	uint32 height;
+	int32 lcd_x;
+	int32 lcd_y;
+	uint32 dst_width;
+	uint32 dst_height;
+} MDP_ROI;
+
+typedef struct mdp_ibuf_s {
+	uint8 *buf;
+	uint32 bpp;
+	uint32 ibuf_type;
+	uint32 ibuf_width;
+	uint32 ibuf_height;
+
+	MDP_ROI roi;
+	MDPIMG mdpImg;
+
+	int32 dma_x;
+	int32 dma_y;
+	uint32 dma_w;
+	uint32 dma_h;
+
+	uint32 vsync_enable;
+} MDPIBUF;
+
+struct mdp_dma_data {
+	boolean busy;
+	boolean dmap_busy;
+	boolean waiting;
+	struct mutex ov_mutex;
+	struct semaphore mutex;
+	struct completion comp;
+	struct completion dmap_comp;
+};
+
+extern struct list_head mdp_hist_lut_list;
+extern struct mutex mdp_hist_lut_list_mutex;
+struct mdp_hist_lut_mgmt {
+	uint32_t block;
+	struct mutex lock;
+	struct list_head list;
+};
+
+struct mdp_hist_lut_info {
+	uint32_t block;
+	boolean is_enabled, has_sel_update;
+	int bank_sel;
+};
+
+struct mdp_hist_mgmt {
+	uint32_t block;
+	uint32_t irq_term;
+	uint32_t base;
+	struct completion mdp_hist_comp;
+	struct mutex mdp_hist_mutex;
+	struct mutex mdp_do_hist_mutex;
+	boolean mdp_is_hist_start, mdp_is_hist_data;
+	boolean mdp_is_hist_valid, mdp_is_hist_init;
+	uint8_t frame_cnt, bit_mask, num_bins;
+	struct work_struct mdp_histogram_worker;
+	struct mdp_histogram_data *hist;
+	uint32_t *c0, *c1, *c2;
+	uint32_t *extra_info;
+};
+
+enum {
+	MDP_HIST_MGMT_DMA_P = 0,
+	MDP_HIST_MGMT_DMA_S,
+	MDP_HIST_MGMT_VG_1,
+	MDP_HIST_MGMT_VG_2,
+	MDP_HIST_MGMT_MAX,
+};
+
+extern struct mdp_hist_mgmt *mdp_hist_mgmt_array[];
+
+#define MDP_CMD_DEBUG_ACCESS_BASE   (MDP_BASE+0x10000)
+
+#define MDP_DMA2_TERM 0x1
+#define MDP_DMA3_TERM 0x2
+#define MDP_PPP_TERM 0x4
+#define MDP_DMA_S_TERM 0x8
+#define MDP_DMA_E_TERM 0x10
+#ifdef CONFIG_FB_MSM_MDP40
+#define MDP_OVERLAY0_TERM 0x20
+#define MDP_OVERLAY1_TERM 0x40
+#endif
+#define MDP_OVERLAY2_TERM 0x80
+#define MDP_HISTOGRAM_TERM_DMA_P 0x100
+#define MDP_HISTOGRAM_TERM_DMA_S 0x200
+#define MDP_HISTOGRAM_TERM_VG_1 0x400
+#define MDP_HISTOGRAM_TERM_VG_2 0x800
+
+#define ACTIVE_START_X_EN BIT(31)
+#define ACTIVE_START_Y_EN BIT(31)
+#define ACTIVE_HIGH 0
+#define ACTIVE_LOW 1
+#define MDP_DMA_S_DONE  BIT(2)
+#define MDP_DMA_E_DONE  BIT(3)
+#define LCDC_FRAME_START    BIT(15)
+#define LCDC_UNDERFLOW      BIT(16)
+
+#ifdef CONFIG_FB_MSM_MDP22
+#define MDP_DMA_P_DONE 	BIT(2)
+#else
+#define MDP_DMA_P_DONE 	BIT(14)
+#endif
+
+#define MDP_PPP_DONE 				BIT(0)
+#define TV_OUT_DMA3_DONE    BIT(6)
+#define TV_ENC_UNDERRUN     BIT(7)
+#define TV_OUT_DMA3_START   BIT(13)
+#define MDP_HIST_DONE       BIT(20)
+
+/* histogram interrupts */
+#define INTR_HIST_DONE			BIT(1)
+#define INTR_HIST_RESET_SEQ_DONE	BIT(0)
+
+#ifdef CONFIG_FB_MSM_MDP22
+#define MDP_ANY_INTR_MASK (MDP_PPP_DONE| \
+			MDP_DMA_P_DONE| \
+			TV_ENC_UNDERRUN)
+#else
+#define MDP_ANY_INTR_MASK (MDP_PPP_DONE| \
+			MDP_DMA_P_DONE| \
+			MDP_DMA_S_DONE| \
+			MDP_DMA_E_DONE| \
+			LCDC_UNDERFLOW| \
+			MDP_HIST_DONE| \
+			TV_ENC_UNDERRUN)
+#endif
+
+#define MDP_TOP_LUMA       16
+#define MDP_TOP_CHROMA     0
+#define MDP_BOTTOM_LUMA    19
+#define MDP_BOTTOM_CHROMA  3
+#define MDP_LEFT_LUMA      22
+#define MDP_LEFT_CHROMA    6
+#define MDP_RIGHT_LUMA     25
+#define MDP_RIGHT_CHROMA   9
+
+#define CLR_G 0x0
+#define CLR_B 0x1
+#define CLR_R 0x2
+#define CLR_ALPHA 0x3
+
+#define CLR_Y  CLR_G
+#define CLR_CB CLR_B
+#define CLR_CR CLR_R
+
+/* from lsb to msb */
+#define MDP_GET_PACK_PATTERN(a,x,y,z,bit) (((a)<<(bit*3))|((x)<<(bit*2))|((y)<<bit)|(z))
+
+/*
+ * 0x0000 0x0004 0x0008 MDP sync config
+ */
+#ifdef CONFIG_FB_MSM_MDP22
+#define MDP_SYNCFG_HGT_LOC 22
+#define MDP_SYNCFG_VSYNC_EXT_EN BIT(21)
+#define MDP_SYNCFG_VSYNC_INT_EN BIT(20)
+#else
+#define MDP_SYNCFG_HGT_LOC 21
+#define MDP_SYNCFG_VSYNC_EXT_EN BIT(20)
+#define MDP_SYNCFG_VSYNC_INT_EN BIT(19)
+#define MDP_HW_VSYNC
+#endif
+
+/*
+ * 0x0018 MDP VSYNC THREASH
+ */
+#define MDP_PRIM_BELOW_LOC 0
+#define MDP_PRIM_ABOVE_LOC 8
+
+/*
+ * MDP_PRIMARY_VSYNC_OUT_CTRL
+ * 0x0080,84,88 internal vsync pulse config
+ */
+#define VSYNC_PULSE_EN BIT(31)
+#define VSYNC_PULSE_INV BIT(30)
+
+/*
+ * 0x008c MDP VSYNC CONTROL
+ */
+#define DISP0_VSYNC_MAP_VSYNC0 0
+#define DISP0_VSYNC_MAP_VSYNC1 BIT(0)
+#define DISP0_VSYNC_MAP_VSYNC2 BIT(0)|BIT(1)
+
+#define DISP1_VSYNC_MAP_VSYNC0 0
+#define DISP1_VSYNC_MAP_VSYNC1 BIT(2)
+#define DISP1_VSYNC_MAP_VSYNC2 BIT(2)|BIT(3)
+
+#define PRIMARY_LCD_SYNC_EN BIT(4)
+#define PRIMARY_LCD_SYNC_DISABLE 0
+
+#define SECONDARY_LCD_SYNC_EN BIT(5)
+#define SECONDARY_LCD_SYNC_DISABLE 0
+
+#define EXTERNAL_LCD_SYNC_EN BIT(6)
+#define EXTERNAL_LCD_SYNC_DISABLE 0
+
+/*
+ * 0x101f0 MDP VSYNC Threshold
+ */
+#define VSYNC_THRESHOLD_ABOVE_LOC 0
+#define VSYNC_THRESHOLD_BELOW_LOC 16
+#define VSYNC_ANTI_TEAR_EN BIT(31)
+
+/*
+ * 0x10004 command config
+ */
+#define MDP_CMD_DBGBUS_EN BIT(0)
+
+/*
+ * 0x10124 or 0x101d4PPP source config
+ */
+#define PPP_SRC_C0G_8BITS (BIT(1)|BIT(0))
+#define PPP_SRC_C1B_8BITS (BIT(3)|BIT(2))
+#define PPP_SRC_C2R_8BITS (BIT(5)|BIT(4))
+#define PPP_SRC_C3A_8BITS (BIT(7)|BIT(6))
+
+#define PPP_SRC_C0G_6BITS BIT(1)
+#define PPP_SRC_C1B_6BITS BIT(3)
+#define PPP_SRC_C2R_6BITS BIT(5)
+
+#define PPP_SRC_C0G_5BITS BIT(0)
+#define PPP_SRC_C1B_5BITS BIT(2)
+#define PPP_SRC_C2R_5BITS BIT(4)
+
+#define PPP_SRC_C3_ALPHA_EN BIT(8)
+
+#define PPP_SRC_BPP_INTERLVD_1BYTES 0
+#define PPP_SRC_BPP_INTERLVD_2BYTES BIT(9)
+#define PPP_SRC_BPP_INTERLVD_3BYTES BIT(10)
+#define PPP_SRC_BPP_INTERLVD_4BYTES (BIT(10)|BIT(9))
+
+#define PPP_SRC_BPP_ROI_ODD_X BIT(11)
+#define PPP_SRC_BPP_ROI_ODD_Y BIT(12)
+#define PPP_SRC_INTERLVD_2COMPONENTS BIT(13)
+#define PPP_SRC_INTERLVD_3COMPONENTS BIT(14)
+#define PPP_SRC_INTERLVD_4COMPONENTS (BIT(14)|BIT(13))
+
+/*
+ * RGB666 unpack format
+ * TIGHT means R6+G6+B6 together
+ * LOOSE means R6+2 +G6+2+ B6+2 (with MSB)
+ * or 2+R6 +2+G6 +2+B6 (with LSB)
+ */
+#define PPP_SRC_UNPACK_TIGHT BIT(17)
+#define PPP_SRC_UNPACK_LOOSE 0
+#define PPP_SRC_UNPACK_ALIGN_LSB 0
+#define PPP_SRC_UNPACK_ALIGN_MSB BIT(18)
+
+#define PPP_SRC_FETCH_PLANES_INTERLVD 0
+#define PPP_SRC_FETCH_PLANES_PSEUDOPLNR BIT(20)
+
+#define PPP_SRC_WMV9_MODE BIT(21)	/* window media version 9 */
+
+/*
+ * 0x10138 PPP operation config
+ */
+#define PPP_OP_SCALE_X_ON BIT(0)
+#define PPP_OP_SCALE_Y_ON BIT(1)
+
+#define PPP_OP_CONVERT_RGB2YCBCR 0
+#define PPP_OP_CONVERT_YCBCR2RGB BIT(2)
+#define PPP_OP_CONVERT_ON BIT(3)
+
+#define PPP_OP_CONVERT_MATRIX_PRIMARY 0
+#define PPP_OP_CONVERT_MATRIX_SECONDARY BIT(4)
+
+#define PPP_OP_LUT_C0_ON BIT(5)
+#define PPP_OP_LUT_C1_ON BIT(6)
+#define PPP_OP_LUT_C2_ON BIT(7)
+
+/* rotate or blend enable */
+#define PPP_OP_ROT_ON BIT(8)
+
+#define PPP_OP_ROT_90 BIT(9)
+#define PPP_OP_FLIP_LR BIT(10)
+#define PPP_OP_FLIP_UD BIT(11)
+
+#define PPP_OP_BLEND_ON BIT(12)
+
+#define PPP_OP_BLEND_SRCPIXEL_ALPHA 0
+#define PPP_OP_BLEND_DSTPIXEL_ALPHA BIT(13)
+#define PPP_OP_BLEND_CONSTANT_ALPHA BIT(14)
+#define PPP_OP_BLEND_SRCPIXEL_TRANSP (BIT(13)|BIT(14))
+
+#define PPP_OP_BLEND_ALPHA_BLEND_NORMAL 0
+#define PPP_OP_BLEND_ALPHA_BLEND_REVERSE BIT(15)
+
+#define PPP_OP_DITHER_EN BIT(16)
+
+#define PPP_OP_COLOR_SPACE_RGB 0
+#define PPP_OP_COLOR_SPACE_YCBCR BIT(17)
+
+#define PPP_OP_SRC_CHROMA_RGB 0
+#define PPP_OP_SRC_CHROMA_H2V1 BIT(18)
+#define PPP_OP_SRC_CHROMA_H1V2 BIT(19)
+#define PPP_OP_SRC_CHROMA_420 (BIT(18)|BIT(19))
+#define PPP_OP_SRC_CHROMA_COSITE 0
+#define PPP_OP_SRC_CHROMA_OFFSITE BIT(20)
+
+#define PPP_OP_DST_CHROMA_RGB 0
+#define PPP_OP_DST_CHROMA_H2V1 BIT(21)
+#define PPP_OP_DST_CHROMA_H1V2 BIT(22)
+#define PPP_OP_DST_CHROMA_420 (BIT(21)|BIT(22))
+#define PPP_OP_DST_CHROMA_COSITE 0
+#define PPP_OP_DST_CHROMA_OFFSITE BIT(23)
+
+#define PPP_BLEND_CALPHA_TRNASP BIT(24)
+
+#define PPP_OP_BG_CHROMA_RGB 0
+#define PPP_OP_BG_CHROMA_H2V1 BIT(25)
+#define PPP_OP_BG_CHROMA_H1V2 BIT(26)
+#define PPP_OP_BG_CHROMA_420 BIT(25)|BIT(26)
+#define PPP_OP_BG_CHROMA_SITE_COSITE 0
+#define PPP_OP_BG_CHROMA_SITE_OFFSITE BIT(27)
+#define PPP_OP_DEINT_EN BIT(28)
+
+#define PPP_BLEND_BG_USE_ALPHA_SEL      (1 << 0)
+#define PPP_BLEND_BG_ALPHA_REVERSE      (1 << 3)
+#define PPP_BLEND_BG_SRCPIXEL_ALPHA     (0 << 1)
+#define PPP_BLEND_BG_DSTPIXEL_ALPHA     (1 << 1)
+#define PPP_BLEND_BG_CONSTANT_ALPHA     (2 << 1)
+#define PPP_BLEND_BG_CONST_ALPHA_VAL(x) ((x) << 24)
+
+#define PPP_OP_DST_RGB 0
+#define PPP_OP_DST_YCBCR BIT(30)
+/*
+ * 0x10150 PPP destination config
+ */
+#define PPP_DST_C0G_8BIT (BIT(0)|BIT(1))
+#define PPP_DST_C1B_8BIT (BIT(3)|BIT(2))
+#define PPP_DST_C2R_8BIT (BIT(5)|BIT(4))
+#define PPP_DST_C3A_8BIT (BIT(7)|BIT(6))
+
+#define PPP_DST_C0G_6BIT BIT(1)
+#define PPP_DST_C1B_6BIT BIT(3)
+#define PPP_DST_C2R_6BIT BIT(5)
+
+#define PPP_DST_C0G_5BIT BIT(0)
+#define PPP_DST_C1B_5BIT BIT(2)
+#define PPP_DST_C2R_5BIT BIT(4)
+
+#define PPP_DST_C3A_8BIT (BIT(7)|BIT(6))
+#define PPP_DST_C3ALPHA_EN BIT(8)
+
+#define PPP_DST_PACKET_CNT_INTERLVD_2ELEM BIT(9)
+#define PPP_DST_PACKET_CNT_INTERLVD_3ELEM BIT(10)
+#define PPP_DST_PACKET_CNT_INTERLVD_4ELEM (BIT(10)|BIT(9))
+#define PPP_DST_PACKET_CNT_INTERLVD_6ELEM (BIT(11)|BIT(9))
+
+#define PPP_DST_PACK_LOOSE 0
+#define PPP_DST_PACK_TIGHT BIT(13)
+#define PPP_DST_PACK_ALIGN_LSB 0
+#define PPP_DST_PACK_ALIGN_MSB BIT(14)
+
+#define PPP_DST_OUT_SEL_AXI 0
+#define PPP_DST_OUT_SEL_MDDI BIT(15)
+
+#define PPP_DST_BPP_2BYTES BIT(16)
+#define PPP_DST_BPP_3BYTES BIT(17)
+#define PPP_DST_BPP_4BYTES (BIT(17)|BIT(16))
+
+#define PPP_DST_PLANE_INTERLVD 0
+#define PPP_DST_PLANE_PLANAR BIT(18)
+#define PPP_DST_PLANE_PSEUDOPLN BIT(19)
+
+#define PPP_DST_TO_TV BIT(20)
+
+#define PPP_DST_MDDI_PRIMARY 0
+#define PPP_DST_MDDI_SECONDARY BIT(21)
+#define PPP_DST_MDDI_EXTERNAL BIT(22)
+
+/*
+ * 0x10180 DMA config
+ */
+#define DMA_DSTC0G_8BITS (BIT(1)|BIT(0))
+#define DMA_DSTC1B_8BITS (BIT(3)|BIT(2))
+#define DMA_DSTC2R_8BITS (BIT(5)|BIT(4))
+
+#define DMA_DSTC0G_6BITS BIT(1)
+#define DMA_DSTC1B_6BITS BIT(3)
+#define DMA_DSTC2R_6BITS BIT(5)
+
+#define DMA_DSTC0G_5BITS BIT(0)
+#define DMA_DSTC1B_5BITS BIT(2)
+#define DMA_DSTC2R_5BITS BIT(4)
+
+#define DMA_PACK_TIGHT                      BIT(6)
+#define DMA_PACK_LOOSE                      0
+#define DMA_PACK_ALIGN_LSB                  0
+/*
+ * use DMA_PACK_ALIGN_MSB if the upper 6 bits from 8 bits output
+ * from LCDC block maps into 6 pins out to the panel
+ */
+#define DMA_PACK_ALIGN_MSB                  BIT(7)
+#define DMA_PACK_PATTERN_RGB \
+       (MDP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 2)<<8)
+#define DMA_PACK_PATTERN_BGR \
+       (MDP_GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 2)<<8)
+#define DMA_OUT_SEL_AHB                     0
+#define DMA_OUT_SEL_LCDC                    BIT(20)
+#define DMA_IBUF_FORMAT_RGB888              0
+#define DMA_IBUF_FORMAT_xRGB8888_OR_ARGB8888  BIT(26)
+
+#ifdef CONFIG_FB_MSM_MDP303
+#define DMA_OUT_SEL_DSI_CMD                  BIT(19)
+#define DMA_OUT_SEL_DSI_VIDEO               (3 << 19)
+#endif
+
+#ifdef CONFIG_FB_MSM_MDP22
+#define DMA_OUT_SEL_MDDI BIT(14)
+#define DMA_AHBM_LCD_SEL_PRIMARY 0
+#define DMA_AHBM_LCD_SEL_SECONDARY BIT(15)
+#define DMA_IBUF_C3ALPHA_EN BIT(16)
+#define DMA_DITHER_EN BIT(17)
+#define DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY 0
+#define DMA_MDDI_DMAOUT_LCD_SEL_SECONDARY BIT(18)
+#define DMA_MDDI_DMAOUT_LCD_SEL_EXTERNAL BIT(19)
+#define DMA_IBUF_FORMAT_RGB565 BIT(20)
+#define DMA_IBUF_FORMAT_RGB888_OR_ARGB8888 0
+#define DMA_IBUF_NONCONTIGUOUS BIT(21)
+#else
+#define DMA_OUT_SEL_MDDI                    BIT(19)
+#define DMA_AHBM_LCD_SEL_PRIMARY            0
+#define DMA_AHBM_LCD_SEL_SECONDARY          0
+#define DMA_IBUF_C3ALPHA_EN                 0
+#define DMA_BUF_FORMAT_RGB565		BIT(25)
+#define DMA_DITHER_EN                       BIT(24)	/* dma_p */
+#define DMA_DEFLKR_EN                       BIT(24)	/* dma_e */
+#define DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY     0
+#define DMA_MDDI_DMAOUT_LCD_SEL_SECONDARY   0
+#define DMA_MDDI_DMAOUT_LCD_SEL_EXTERNAL    0
+#define DMA_IBUF_FORMAT_RGB565              BIT(25)
+#define DMA_IBUF_NONCONTIGUOUS 0
+#endif
+
+/*
+ * MDDI Register
+ */
+#define MDDI_VDO_PACKET_DESC_16  0x5565
+#define MDDI_VDO_PACKET_DESC	 0x5666	/* 18 bits */
+#define MDDI_VDO_PACKET_DESC_24  0x5888
+
+#define MDP_HIST_INTR_STATUS_OFF	(0x0014)
+#define MDP_HIST_INTR_CLEAR_OFF		(0x0018)
+#define MDP_HIST_INTR_ENABLE_OFF	(0x001C)
+
+#ifdef CONFIG_FB_MSM_MDP40
+#define MDP_INTR_ENABLE		(msm_mdp_base + 0x0050)
+#define MDP_INTR_STATUS		(msm_mdp_base + 0x0054)
+#define MDP_INTR_CLEAR		(msm_mdp_base + 0x0058)
+#define MDP_EBI2_LCD0		(msm_mdp_base + 0x0060)
+#define MDP_EBI2_LCD1		(msm_mdp_base + 0x0064)
+#define MDP_EBI2_PORTMAP_MODE	(msm_mdp_base + 0x0070)
+
+#define MDP_DMA_P_HIST_INTR_STATUS 	(msm_mdp_base + 0x95014)
+#define MDP_DMA_P_HIST_INTR_CLEAR 	(msm_mdp_base + 0x95018)
+#define MDP_DMA_P_HIST_INTR_ENABLE 	(msm_mdp_base + 0x9501C)
+
+#else
+#define MDP_INTR_ENABLE		(msm_mdp_base + 0x0020)
+#define MDP_INTR_STATUS		(msm_mdp_base + 0x0024)
+#define MDP_INTR_CLEAR		(msm_mdp_base + 0x0028)
+#define MDP_EBI2_LCD0		(msm_mdp_base + 0x003c)
+#define MDP_EBI2_LCD1		(msm_mdp_base + 0x0040)
+#define MDP_EBI2_PORTMAP_MODE	(msm_mdp_base + 0x005c)
+
+#define MDP_DMA_P_HIST_INTR_STATUS	(msm_mdp_base + 0x94014)
+#define MDP_DMA_P_HIST_INTR_CLEAR	(msm_mdp_base + 0x94018)
+#define MDP_DMA_P_HIST_INTR_ENABLE	(msm_mdp_base + 0x9401C)
+#endif
+
+#define MDP_FULL_BYPASS_WORD43  (msm_mdp_base + 0x101ac)
+
+#define MDP_CSC_PFMVn(n)	(msm_mdp_base + 0x40400 + 4 * (n))
+#define MDP_CSC_PRMVn(n)	(msm_mdp_base + 0x40440 + 4 * (n))
+#define MDP_CSC_PRE_BV1n(n)	(msm_mdp_base + 0x40500 + 4 * (n))
+#define MDP_CSC_PRE_BV2n(n)	(msm_mdp_base + 0x40540 + 4 * (n))
+#define MDP_CSC_POST_BV1n(n)	(msm_mdp_base + 0x40580 + 4 * (n))
+#define MDP_CSC_POST_BV2n(n)	(msm_mdp_base + 0x405c0 + 4 * (n))
+
+#ifdef CONFIG_FB_MSM_MDP31
+#define MDP_CSC_PRE_LV1n(n)	(msm_mdp_base + 0x40600 + 4 * (n))
+#define MDP_CSC_PRE_LV2n(n)	(msm_mdp_base + 0x40640 + 4 * (n))
+#define MDP_CSC_POST_LV1n(n)	(msm_mdp_base + 0x40680 + 4 * (n))
+#define MDP_CSC_POST_LV2n(n)	(msm_mdp_base + 0x406c0 + 4 * (n))
+#define MDP_PPP_SCALE_COEFF_LSBn(n)	(msm_mdp_base + 0x50400 + 8 * (n))
+#define MDP_PPP_SCALE_COEFF_MSBn(n)	(msm_mdp_base + 0x50404 + 8 * (n))
+
+#define SCALE_D0_SET  0
+#define SCALE_D1_SET  BIT(0)
+#define SCALE_D2_SET  BIT(1)
+#define SCALE_U1_SET  (BIT(0)|BIT(1))
+
+#else
+#define MDP_CSC_PRE_LV1n(n)	(msm_mdp_base + 0x40580 + 4 * (n))
+#endif
+
+#define MDP_CURSOR_WIDTH 64
+#define MDP_CURSOR_HEIGHT 64
+#define MDP_CURSOR_SIZE (MDP_CURSOR_WIDTH*MDP_CURSOR_WIDTH*4)
+
+#define MDP_DMA_P_LUT_C0_EN   BIT(0)
+#define MDP_DMA_P_LUT_C1_EN   BIT(1)
+#define MDP_DMA_P_LUT_C2_EN   BIT(2)
+#define MDP_DMA_P_LUT_POST    BIT(4)
+
+void mdp_hw_init(void);
+int mdp_ppp_pipe_wait(void);
+void mdp_pipe_kickoff(uint32 term, struct msm_fb_data_type *mfd);
+void mdp_pipe_ctrl(MDP_BLOCK_TYPE block, MDP_BLOCK_POWER_STATE state,
+		   boolean isr);
+void mdp_set_dma_pan_info(struct fb_info *info, struct mdp_dirty_region *dirty,
+			  boolean sync);
+void mdp_dma_pan_update(struct fb_info *info);
+void mdp_refresh_screen(unsigned long data);
+int mdp_ppp_blit(struct fb_info *info, struct mdp_blit_req *req);
+void mdp_lcd_update_workqueue_handler(struct work_struct *work);
+void mdp_vsync_resync_workqueue_handler(struct work_struct *work);
+void mdp_dma2_update(struct msm_fb_data_type *mfd);
+void mdp_vsync_cfg_regs(struct msm_fb_data_type *mfd,
+	boolean first_time);
+void mdp_config_vsync(struct platform_device *pdev,
+	struct msm_fb_data_type *mfd);
+uint32 mdp_get_lcd_line_counter(struct msm_fb_data_type *mfd);
+enum hrtimer_restart mdp_dma2_vsync_hrtimer_handler(struct hrtimer *ht);
+void mdp_set_scale(MDPIBUF *iBuf,
+		   uint32 dst_roi_width,
+		   uint32 dst_roi_height,
+		   boolean inputRGB, boolean outputRGB, uint32 *pppop_reg_ptr);
+void mdp_init_scale_table(void);
+void mdp_adjust_start_addr(uint8 **src0,
+			   uint8 **src1,
+			   int v_slice,
+			   int h_slice,
+			   int x,
+			   int y,
+			   uint32 width,
+			   uint32 height, int bpp, MDPIBUF *iBuf, int layer);
+void mdp_set_blend_attr(MDPIBUF *iBuf,
+			uint32 *alpha,
+			uint32 *tpVal,
+			uint32 perPixelAlpha, uint32 *pppop_reg_ptr);
+
+int mdp_dma3_on(struct platform_device *pdev);
+int mdp_dma3_off(struct platform_device *pdev);
+void mdp_dma3_update(struct msm_fb_data_type *mfd);
+
+int mdp_lcdc_on(struct platform_device *pdev);
+int mdp_lcdc_off(struct platform_device *pdev);
+void mdp_lcdc_update(struct msm_fb_data_type *mfd);
+
+#ifdef CONFIG_FB_MSM_MDP303
+int mdp_dsi_video_on(struct platform_device *pdev);
+int mdp_dsi_video_off(struct platform_device *pdev);
+void mdp_dsi_video_update(struct msm_fb_data_type *mfd);
+void mdp3_dsi_cmd_dma_busy_wait(struct msm_fb_data_type *mfd);
+#endif
+
+void set_cont_splashScreen_status(int);
+
+int mdp_hw_cursor_update(struct fb_info *info, struct fb_cursor *cursor);
+#if defined(CONFIG_FB_MSM_OVERLAY) && defined(CONFIG_FB_MSM_MDP40)
+int mdp_hw_cursor_sync_update(struct fb_info *info, struct fb_cursor *cursor);
+#else
+static inline int mdp_hw_cursor_sync_update(struct fb_info *info,
+		struct fb_cursor *cursor)
+{
+	return 0;
+}
+#endif
+
+void mdp_enable_irq(uint32 term);
+void mdp_disable_irq(uint32 term);
+void mdp_disable_irq_nosync(uint32 term);
+int mdp_get_bytes_per_pixel(uint32_t format,
+				 struct msm_fb_data_type *mfd);
+int mdp_set_core_clk(uint16 perf_level);
+unsigned long mdp_get_core_clk(void);
+unsigned long mdp_perf_level2clk_rate(uint32 perf_level);
+
+#ifdef CONFIG_MSM_BUS_SCALING
+int mdp_bus_scale_update_request(uint32_t index);
+#else
+static inline int mdp_bus_scale_update_request(uint32_t index)
+{
+	return 0;
+}
+#endif
+
+#ifdef MDP_HW_VSYNC
+void mdp_hw_vsync_clk_enable(struct msm_fb_data_type *mfd);
+void mdp_hw_vsync_clk_disable(struct msm_fb_data_type *mfd);
+void mdp_vsync_clk_disable(void);
+void mdp_vsync_clk_enable(void);
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+int mdp_debugfs_init(void);
+#endif
+
+void mdp_dma_s_update(struct msm_fb_data_type *mfd);
+int mdp_histogram_start(struct mdp_histogram_start_req *req);
+int mdp_histogram_stop(struct fb_info *info, uint32_t block);
+int mdp_histogram_ctrl(boolean en, uint32_t block);
+int mdp_histogram_ctrl_all(boolean en);
+int mdp_histogram_block2mgmt(uint32_t block, struct mdp_hist_mgmt **mgmt);
+void mdp_histogram_handle_isr(struct mdp_hist_mgmt *mgmt);
+void __mdp_histogram_kickoff(struct mdp_hist_mgmt *mgmt);
+void __mdp_histogram_reset(struct mdp_hist_mgmt *mgmt);
+unsigned int mdp_check_suspended(void);
+void mdp_footswitch_ctrl(boolean on);
+
+#ifdef CONFIG_FB_MSM_MDP303
+static inline void mdp4_dsi_cmd_dma_busy_wait(struct msm_fb_data_type *mfd)
+{
+	/* empty */
+}
+
+static inline void mdp4_dsi_blt_dmap_busy_wait(struct msm_fb_data_type *mfd)
+{
+	/* empty */
+}
+static inline void mdp4_overlay_dsi_state_set(int state)
+{
+	/* empty */
+}
+static inline int mdp4_overlay_dsi_state_get(void)
+{
+	return 0;
+}
+#endif
+
+#ifndef CONFIG_FB_MSM_MDP40
+static inline void mdp_dsi_cmd_overlay_suspend(void)
+{
+	/* empty */
+}
+static inline void mdp4_iommu_detach(void)
+{
+    /* empty */
+}
+#endif
+
+int mdp_ppp_v4l2_overlay_set(struct fb_info *info, struct mdp_overlay *req);
+int mdp_ppp_v4l2_overlay_clear(void);
+int mdp_ppp_v4l2_overlay_play(struct fb_info *info,
+	unsigned long srcp0_addr, unsigned long srcp0_size,
+	unsigned long srcp1_addr, unsigned long srcp1_size);
+
+void mdp_vid_quant_set(void);
+#endif /* MDP_H */
diff --git a/drivers/video/msm/mdp4.h b/drivers/video/msm/mdp4.h
new file mode 100644
index 0000000..a7161fe
--- /dev/null
+++ b/drivers/video/msm/mdp4.h
@@ -0,0 +1,770 @@
+/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef MDP4_H
+#define MDP4_H
+
+extern struct mdp_dma_data dma2_data;
+extern struct mdp_dma_data dma_s_data;
+extern struct mdp_dma_data dma_e_data;
+extern struct mdp_dma_data dma_wb_data;
+extern unsigned int mdp_hist_frame_cnt;
+extern struct completion mdp_hist_comp;
+extern boolean mdp_is_in_isr;
+extern uint32 mdp_intr_mask;
+extern spinlock_t mdp_spin_lock;
+extern struct mdp4_statistic mdp4_stat;
+extern uint32 mdp4_extn_disp;
+extern char *mmss_cc_base;	/* mutimedia sub system clock control */
+
+#define MDP4_OVERLAYPROC0_BASE	0x10000
+#define MDP4_OVERLAYPROC1_BASE	0x18000
+#define MDP4_OVERLAYPROC2_BASE	0x88000
+
+#define MDP4_VIDEO_BASE 0x20000
+#define MDP4_VIDEO_OFF 0x10000
+
+#define MDP4_RGB_BASE 0x40000
+#define MDP4_RGB_OFF 0x10000
+
+/* chip select controller */
+#define CS_CONTROLLER_0 0x0707ffff
+#define CS_CONTROLLER_1 0x03073f3f
+
+enum {
+	OVERLAY_PERF_LEVEL1 = 1,
+	OVERLAY_PERF_LEVEL2,
+	OVERLAY_PERF_LEVEL3,
+	OVERLAY_PERF_LEVEL4
+};
+
+typedef int (*cmd_fxn_t)(struct platform_device *pdev);
+
+enum {		/* display */
+	PRIMARY_INTF_SEL,
+	SECONDARY_INTF_SEL,
+	EXTERNAL_INTF_SEL
+};
+
+enum {
+	LCDC_RGB_INTF,			/* 0 */
+	DTV_INTF = LCDC_RGB_INTF,	/* 0 */
+	MDDI_LCDC_INTF,			/* 1 */
+	MDDI_INTF,			/* 2 */
+	EBI2_INTF,			/* 3 */
+	TV_INTF = EBI2_INTF,		/* 3 */
+	DSI_VIDEO_INTF,
+	DSI_CMD_INTF
+};
+
+enum {
+	MDDI_PRIMARY_SET,
+	MDDI_SECONDARY_SET,
+	MDDI_EXTERNAL_SET
+};
+
+enum {
+	EBI2_LCD0,
+	EBI2_LCD1
+};
+
+#define MDP4_3D_NONE		0
+#define MDP4_3D_SIDE_BY_SIDE	1
+#define MDP4_3D_TOP_DOWN	2
+
+#define MDP4_PANEL_MDDI		BIT(0)
+#define MDP4_PANEL_LCDC		BIT(1)
+#define MDP4_PANEL_DTV		BIT(2)
+#define MDP4_PANEL_ATV		BIT(3)
+#define MDP4_PANEL_DSI_VIDEO	BIT(4)
+#define MDP4_PANEL_DSI_CMD	BIT(5)
+#define MDP4_PANEL_WRITEBACK		BIT(6)
+
+enum {
+	OVERLAY_MODE_NONE,
+	OVERLAY_MODE_BLT
+};
+
+enum {
+	OVERLAY_REFRESH_ON_DEMAND,
+	OVERLAY_REFRESH_VSYNC,
+	OVERLAY_REFRESH_VSYNC_HALF,
+	OVERLAY_REFRESH_VSYNC_QUARTER
+};
+
+enum {
+	OVERLAY_FRAMEBUF,
+	OVERLAY_DIRECTOUT
+};
+
+/* system interrupts */
+#define INTR_OVERLAY0_DONE		BIT(0)
+#define INTR_OVERLAY1_DONE		BIT(1)
+#define INTR_DMA_S_DONE			BIT(2)
+#define INTR_DMA_E_DONE			BIT(3)
+#define INTR_DMA_P_DONE			BIT(4)
+#define INTR_VG1_HISTOGRAM		BIT(5)
+#define INTR_VG2_HISTOGRAM		BIT(6)
+#define INTR_PRIMARY_VSYNC		BIT(7)
+#define INTR_PRIMARY_INTF_UDERRUN	BIT(8)
+#define INTR_EXTERNAL_VSYNC		BIT(9)
+#define INTR_EXTERNAL_INTF_UDERRUN	BIT(10)
+#define INTR_PRIMARY_READ_PTR		BIT(11)
+#define INTR_DMA_P_HISTOGRAM		BIT(17)
+#define INTR_DMA_S_HISTOGRAM		BIT(26)
+#define INTR_OVERLAY2_DONE		BIT(30)
+
+#ifdef CONFIG_FB_MSM_OVERLAY
+#define MDP4_ANY_INTR_MASK	(INTR_DMA_P_HISTOGRAM | \
+				INTR_DMA_S_HISTOGRAM | \
+				INTR_VG1_HISTOGRAM | \
+				INTR_VG2_HISTOGRAM)
+#else
+#define MDP4_ANY_INTR_MASK	(INTR_DMA_P_DONE| \
+				INTR_DMA_P_HISTOGRAM | \
+				INTR_DMA_S_HISTOGRAM | \
+				INTR_VG1_HISTOGRAM | \
+				INTR_VG2_HISTOGRAM)
+#endif
+
+enum {
+	OVERLAY_PIPE_VG1,	/* video/graphic */
+	OVERLAY_PIPE_VG2,
+	OVERLAY_PIPE_RGB1,
+	OVERLAY_PIPE_RGB2,
+	OVERLAY_PIPE_RGB3,
+	OVERLAY_PIPE_VG3,
+	OVERLAY_PIPE_VG4,
+	OVERLAY_PIPE_MAX
+};
+
+enum {
+	OVERLAY_TYPE_RGB,
+	OVERLAY_TYPE_VIDEO,
+	OVERLAY_TYPE_BF
+};
+
+enum {
+	MDP4_MIXER0,
+	MDP4_MIXER1,
+	MDP4_MIXER2,
+	MDP4_MIXER_MAX
+};
+
+enum {
+	OVERLAY_PLANE_INTERLEAVED,
+	OVERLAY_PLANE_PLANAR,
+	OVERLAY_PLANE_PSEUDO_PLANAR
+};
+
+enum {
+	MDP4_MIXER_STAGE_UNUNSED,	/* pipe not used */
+	MDP4_MIXER_STAGE_BASE,
+	MDP4_MIXER_STAGE0,	/* zorder 0 */
+	MDP4_MIXER_STAGE1,	/* zorder 1 */
+	MDP4_MIXER_STAGE2,	/* zorder 2 */
+	MDP4_MIXER_STAGE_MAX
+};
+
+enum {
+	MDP4_FRAME_FORMAT_LINEAR,
+	MDP4_FRAME_FORMAT_ARGB_TILE,
+	MDP4_FRAME_FORMAT_VIDEO_SUPERTILE
+};
+
+enum {
+	MDP4_CHROMA_RGB,
+	MDP4_CHROMA_H2V1,
+	MDP4_CHROMA_H1V2,
+	MDP4_CHROMA_420
+};
+
+#define MDP4_BLEND_BG_TRANSP_EN		BIT(9)
+#define MDP4_BLEND_FG_TRANSP_EN		BIT(8)
+#define MDP4_BLEND_BG_MOD_ALPHA		BIT(7)
+#define MDP4_BLEND_BG_INV_ALPHA		BIT(6)
+#define MDP4_BLEND_BG_ALPHA_FG_CONST	(0 << 4)
+#define MDP4_BLEND_BG_ALPHA_BG_CONST	(1 << 4)
+#define MDP4_BLEND_BG_ALPHA_FG_PIXEL	(2 << 4)
+#define MDP4_BLEND_BG_ALPHA_BG_PIXEL	(3 << 4)
+#define MDP4_BLEND_FG_MOD_ALPHA		BIT(3)
+#define MDP4_BLEND_FG_INV_ALPHA		BIT(2)
+#define MDP4_BLEND_FG_ALPHA_FG_CONST	(0 << 0)
+#define MDP4_BLEND_FG_ALPHA_BG_CONST	(1 << 0)
+#define MDP4_BLEND_FG_ALPHA_FG_PIXEL	(2 << 0)
+#define MDP4_BLEND_FG_ALPHA_BG_PIXEL	(3 << 0)
+
+#define MDP4_FORMAT_SOLID_FILL		BIT(22)
+#define MDP4_FORMAT_UNPACK_ALIGN_MSB	BIT(18)
+#define MDP4_FORMAT_UNPACK_TIGHT	BIT(17)
+#define MDP4_FORMAT_90_ROTATED		BIT(12)
+#define MDP4_FORMAT_ALPHA_ENABLE	BIT(8)
+
+#define MDP4_OP_DEINT_ODD_REF  	BIT(19)
+#define MDP4_OP_DEINT_EN	BIT(18)
+#define MDP4_OP_IGC_LUT_EN	BIT(16)
+#define MDP4_OP_DITHER_EN     	BIT(15)
+#define MDP4_OP_FLIP_UD		BIT(14)
+#define MDP4_OP_FLIP_LR		BIT(13)
+#define MDP4_OP_CSC_EN		BIT(11)
+#define MDP4_OP_SRC_DATA_YCBCR	BIT(9)
+#define MDP4_OP_SCALEY_FIR 		(0 << 4)
+#define MDP4_OP_SCALEY_MN_PHASE 	(1 << 4)
+#define MDP4_OP_SCALEY_PIXEL_RPT	(2 << 4)
+#define MDP4_OP_SCALEX_FIR 		(0 << 2)
+#define MDP4_OP_SCALEX_MN_PHASE 	(1 << 2)
+#define MDP4_OP_SCALEX_PIXEL_RPT 	(2 << 2)
+#define MDP4_OP_SCALE_RGB_ENHANCED	(1 << 4)
+#define MDP4_OP_SCALE_RGB_PIXEL_RPT	(0 << 3)
+#define MDP4_OP_SCALE_RGB_BILINEAR	(1 << 3)
+#define MDP4_OP_SCALE_ALPHA_PIXEL_RPT	(0 << 2)
+#define MDP4_OP_SCALE_ALPHA_BILINEAR	(1 << 2)
+#define MDP4_OP_SCALEY_EN	BIT(1)
+#define MDP4_OP_SCALEX_EN	BIT(0)
+
+#define MDP4_REV40_UP_SCALING_MAX (8)
+#define MDP4_REV41_OR_LATER_UP_SCALING_MAX (20)
+
+#define MDP4_PIPE_PER_MIXER	2
+
+#define MDP4_MAX_PLANE		4
+#define VSYNC_PERIOD		16
+
+struct mdp4_hsic_regs {
+	int32_t params[NUM_HSIC_PARAM];
+	int32_t conv_matrix[3][3];
+	int32_t	pre_limit[6];
+	int32_t post_limit[6];
+	int32_t pre_bias[3];
+	int32_t post_bias[3];
+	int32_t dirty;
+};
+
+struct mdp4_iommu_pipe_info {
+	struct ion_handle *ihdl[MDP4_MAX_PLANE];
+	struct ion_handle *prev_ihdl[MDP4_MAX_PLANE];
+	u8 mark_unmap;
+};
+
+struct mdp4_overlay_pipe {
+	uint32 pipe_used;
+	uint32 pipe_type;		/* rgb, video/graphic */
+	uint32 pipe_num;
+	uint32 pipe_ndx;
+	uint32 pipe_share;
+	uint32 mixer_num;		/* which mixer used */
+	uint32 mixer_stage;		/* which stage of mixer used */
+	uint32 src_format;
+	uint32 src_width;	/* source img width */
+	uint32 src_height;	/* source img height */
+	uint32 is_3d;
+	uint32 src_width_3d;	/* source img width */
+	uint32 src_height_3d;	/* source img height */
+	uint32 src_w;		/* roi */
+	uint32 src_h;		/* roi */
+	uint32 src_x;		/* roi */
+	uint32 src_y;		/* roi */
+	uint32 dst_w;		/* roi */
+	uint32 dst_h;		/* roi */
+	uint32 dst_x;		/* roi */
+	uint32 dst_y;		/* roi */
+	uint32 flags;
+	uint32 op_mode;
+	uint32 transp;
+	uint32 blend_op;
+	uint32 phasex_step;
+	uint32 phasey_step;
+	uint32 alpha;
+	uint32 is_fg;		/* control alpha & color key */
+	uint32 srcp0_addr;	/* interleave, luma */
+	uint32 srcp0_ystride;
+	uint32 srcp1_addr;	/* pseudoplanar, chroma plane */
+	uint32 srcp1_ystride;
+	uint32 srcp2_addr;	/* planar color 2*/
+	uint32 srcp2_ystride;
+	uint32 srcp3_addr;	/* alpha/color 3 */
+	uint32 srcp3_ystride;
+	uint32 fetch_plane;
+	uint32 frame_format;		/* video */
+	uint32 chroma_site;		/* video */
+	uint32 chroma_sample;		/* video */
+	uint32 solid_fill;
+	uint32 vc1_reduce;		/* video */
+	uint32 unpack_align_msb;/* 0 to LSB, 1 to MSB */
+	uint32 unpack_tight;/* 0 for loose, 1 for tight */
+	uint32 unpack_count;/* 0 = 1 component, 1 = 2 component ... */
+	uint32 rotated_90; /* has been rotated 90 degree */
+	uint32 bpp;	/* byte per pixel */
+	uint32 alpha_enable;/*  source has alpha */
+	/*
+	 * number of bits for source component,
+	 * 0 = 1 bit, 1 = 2 bits, 2 = 6 bits, 3 = 8 bits
+	 */
+	uint32 a_bit;	/* component 3, alpha */
+	uint32 r_bit;	/* component 2, R_Cr */
+	uint32 b_bit;	/* component 1, B_Cb */
+	uint32 g_bit;	/* component 0, G_lumz */
+	/*
+	 * unpack pattern
+	 * A = C3, R = C2, B = C1, G = C0
+	 */
+	uint32 element3; /* 0 = C0, 1 = C1, 2 = C2, 3 = C3 */
+	uint32 element2; /* 0 = C0, 1 = C1, 2 = C2, 3 = C3 */
+	uint32 element1; /* 0 = C0, 1 = C1, 2 = C2, 3 = C3 */
+	uint32 element0; /* 0 = C0, 1 = C1, 2 = C2, 3 = C3 */
+	struct completion comp;
+	ulong blt_addr; /* blt mode addr */
+	ulong blt_base;
+	ulong blt_offset;
+	uint32 blt_cnt;
+	uint32 ov_cnt;
+	uint32 dmap_cnt;
+	uint32 dmae_cnt;
+	uint32 blt_end;
+	uint32 luma_align_size;
+	struct mdp4_hsic_regs hsic_regs;
+	struct completion dmas_comp;
+	struct mdp_overlay req_data;
+};
+
+struct mdp4_statistic {
+	ulong intr_tot;
+	ulong intr_dma_p;
+	ulong intr_dma_s;
+	ulong intr_dma_e;
+	ulong intr_overlay0;
+	ulong intr_overlay1;
+	ulong intr_overlay2;
+	ulong intr_vsync_p;	/* Primary interface */
+	ulong intr_underrun_p;	/* Primary interface */
+	ulong intr_vsync_e;	/* external interface */
+	ulong intr_underrun_e;	/* external interface */
+	ulong intr_histogram;
+	ulong intr_rd_ptr;
+	ulong dsi_mdp_start;
+	ulong dsi_clk_on;
+	ulong dsi_clk_off;
+	ulong intr_dsi;
+	ulong intr_dsi_mdp;
+	ulong intr_dsi_cmd;
+	ulong intr_dsi_err;
+	ulong kickoff_ov0;
+	ulong kickoff_ov1;
+	ulong kickoff_dmap;
+	ulong kickoff_dmae;
+	ulong kickoff_dmas;
+	ulong blt_dsi_cmd;	/* blt */
+	ulong blt_dsi_video;	/* blt */
+	ulong blt_lcdc;	/* blt */
+	ulong blt_dtv;	/* blt */
+	ulong blt_mddi;	/* blt */
+	ulong overlay_set[MDP4_MIXER_MAX];
+	ulong overlay_unset[MDP4_MIXER_MAX];
+	ulong overlay_play[MDP4_MIXER_MAX];
+	ulong pipe[OVERLAY_PIPE_MAX];
+	ulong dsi_clkoff;
+	ulong err_mixer;
+	ulong err_zorder;
+	ulong err_size;
+	ulong err_scale;
+	ulong err_format;
+	ulong err_stage;
+	ulong err_play;
+	ulong err_underflow;
+};
+
+struct mdp4_overlay_pipe *mdp4_overlay_ndx2pipe(int ndx);
+void mdp4_sw_reset(unsigned long bits);
+void mdp4_display_intf_sel(int output, unsigned long intf);
+void mdp4_overlay_cfg(int layer, int blt_mode, int refresh, int direct_out);
+void mdp4_ebi2_lcd_setup(int lcd, unsigned long base, int ystride);
+void mdp4_mddi_setup(int which, unsigned long id);
+unsigned long mdp4_display_status(void);
+void mdp4_enable_clk_irq(void);
+void mdp4_disable_clk_irq(void);
+void mdp4_dma_p_update(struct msm_fb_data_type *mfd);
+void mdp4_dma_s_update(struct msm_fb_data_type *mfd);
+void mdp_pipe_ctrl(MDP_BLOCK_TYPE block, MDP_BLOCK_POWER_STATE state,
+		   boolean isr);
+void mdp4_pipe_kickoff(uint32 pipe, struct msm_fb_data_type *mfd);
+int mdp4_lcdc_on(struct platform_device *pdev);
+int mdp4_lcdc_off(struct platform_device *pdev);
+void mdp4_lcdc_update(struct msm_fb_data_type *mfd);
+void mdp4_intr_clear_set(ulong clear, ulong set);
+void mdp4_dma_p_cfg(void);
+unsigned is_mdp4_hw_reset(void);
+void mdp4_hw_init(void);
+void mdp4_isr_read(int);
+void mdp4_clear_lcdc(void);
+void mdp4_mixer_blend_init(int mixer_num);
+void mdp4_vg_qseed_init(int vg_num);
+void mdp4_vg_csc_setup(int vp_num);
+void mdp4_mixer_csc_setup(uint32 mixer);
+void mdp4_dmap_csc_setup(void);
+void mdp4_vg_csc_update(struct mdp_csc *p);
+irqreturn_t mdp4_isr(int irq, void *ptr);
+void mdp4_overlay_format_to_pipe(uint32 format, struct mdp4_overlay_pipe *pipe);
+uint32 mdp4_overlay_format(struct mdp4_overlay_pipe *pipe);
+uint32 mdp4_overlay_unpack_pattern(struct mdp4_overlay_pipe *pipe);
+uint32 mdp4_overlay_op_mode(struct mdp4_overlay_pipe *pipe);
+void mdp4_lcdc_overlay(struct msm_fb_data_type *mfd);
+#ifdef CONFIG_FB_MSM_DTV
+void mdp4_overlay_dtv_start(void);
+void mdp4_overlay_dtv_ov_done_push(struct msm_fb_data_type *mfd,
+			struct mdp4_overlay_pipe *pipe);
+void mdp4_overlay_dtv_wait_for_ov(struct msm_fb_data_type *mfd,
+	struct mdp4_overlay_pipe *pipe);
+int mdp4_overlay_dtv_set(struct msm_fb_data_type *mfd,
+			struct mdp4_overlay_pipe *pipe);
+int mdp4_overlay_dtv_unset(struct msm_fb_data_type *mfd,
+			struct mdp4_overlay_pipe *pipe);
+void mdp4_dma_e_done_dtv(void);
+void mdp4_overlay_dtv_wait4vsync(void);
+#else
+static inline void mdp4_overlay_dtv_start(void)
+{
+	/* empty */
+}
+static inline void  mdp4_overlay_dtv_ov_done_push(struct msm_fb_data_type *mfd,
+			struct mdp4_overlay_pipe *pipe)
+{
+	/* empty */
+}
+static inline void  mdp4_overlay_dtv_wait_for_ov(struct msm_fb_data_type *mfd,
+	struct mdp4_overlay_pipe *pipe)
+{
+	/* empty */
+}
+static inline int mdp4_overlay_dtv_set(struct msm_fb_data_type *mfd,
+			struct mdp4_overlay_pipe *pipe)
+{
+	return 0;
+}
+static inline int mdp4_overlay_dtv_unset(struct msm_fb_data_type *mfd,
+			struct mdp4_overlay_pipe *pipe)
+{
+	return 0;
+}
+
+static inline void mdp4_dma_e_done_dtv(void)
+{
+    /* empty */
+}
+static inline void mdp4_overlay_dtv_wait4vsync(void)
+{
+    /* empty */
+}
+static inline void mdp4_dtv_overlay_blt_start(struct msm_fb_data_type *mfd)
+{
+	return;
+}
+static inline void mdp4_dtv_overlay_blt_stop(struct msm_fb_data_type *mfd)
+{
+	return;
+}
+#endif
+
+void mdp4_dtv_set_black_screen(void);
+
+static inline int mdp4_overlay_borderfill_supported(void)
+{
+	unsigned int mdp_hw_version;
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	mdp_hw_version = inpdw(MDP_BASE + 0x0); /* MDP_HW_VERSION */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+	return (mdp_hw_version >= 0x0402030b);
+}
+
+int mdp4_overlay_dtv_set(struct msm_fb_data_type *mfd,
+			struct mdp4_overlay_pipe *pipe);
+int mdp4_overlay_dtv_unset(struct msm_fb_data_type *mfd,
+			struct mdp4_overlay_pipe *pipe);
+void mdp4_dtv_overlay(struct msm_fb_data_type *mfd);
+int mdp4_dtv_on(struct platform_device *pdev);
+int mdp4_dtv_off(struct platform_device *pdev);
+void mdp4_atv_overlay(struct msm_fb_data_type *mfd);
+int mdp4_atv_on(struct platform_device *pdev);
+int mdp4_atv_off(struct platform_device *pdev);
+void mdp4_dsi_video_fxn_register(cmd_fxn_t fxn);
+void mdp4_dsi_video_overlay(struct msm_fb_data_type *mfd);
+int mdp4_dsi_video_on(struct platform_device *pdev);
+int mdp4_dsi_video_off(struct platform_device *pdev);
+void mdp4_overlay0_done_dsi_video(struct mdp_dma_data *dma);
+void mdp4_overlay0_done_dsi_cmd(struct mdp_dma_data *dma);
+void mdp4_dsi_cmd_overlay(struct msm_fb_data_type *mfd);
+void mdp4_overlay_dsi_state_set(int state);
+int mdp4_overlay_dsi_state_get(void);
+void mdp4_overlay_rgb_setup(struct mdp4_overlay_pipe *pipe);
+void mdp4_overlay_reg_flush(struct mdp4_overlay_pipe *pipe, int all);
+void mdp4_mixer_blend_setup(struct mdp4_overlay_pipe *pipe);
+struct mdp4_overlay_pipe *mdp4_overlay_stage_pipe(int mixer, int stage);
+void mdp4_mixer_stage_up(struct mdp4_overlay_pipe *pipe);
+void mdp4_mixer_stage_down(struct mdp4_overlay_pipe *pipe);
+void mdp4_mixer_pipe_cleanup(int mixer);
+int mdp4_mixer_stage_can_run(struct mdp4_overlay_pipe *pipe);
+void mdp4_overlayproc_cfg(struct mdp4_overlay_pipe *pipe);
+void mdp4_mddi_overlay(struct msm_fb_data_type *mfd);
+int mdp4_overlay_format2type(uint32 format);
+int mdp4_overlay_format2pipe(struct mdp4_overlay_pipe *pipe);
+int mdp4_overlay_get(struct fb_info *info, struct mdp_overlay *req);
+int mdp4_overlay_set(struct fb_info *info, struct mdp_overlay *req);
+int mdp4_overlay_unset(struct fb_info *info, int ndx);
+int mdp4_overlay_play_wait(struct fb_info *info,
+	struct msmfb_overlay_data *req);
+int mdp4_overlay_play(struct fb_info *info, struct msmfb_overlay_data *req);
+struct mdp4_overlay_pipe *mdp4_overlay_pipe_alloc(int ptype, int mixer);
+void mdp4_overlay_pipe_free(struct mdp4_overlay_pipe *pipe);
+void mdp4_overlay_dmap_cfg(struct msm_fb_data_type *mfd, int lcdc);
+void mdp4_overlay_dmap_xy(struct mdp4_overlay_pipe *pipe);
+void mdp4_overlay_dmae_cfg(struct msm_fb_data_type *mfd, int atv);
+void mdp4_overlay_dmae_xy(struct mdp4_overlay_pipe *pipe);
+int mdp4_overlay_pipe_staged(int mixer);
+void mdp4_lcdc_primary_vsyn(void);
+void mdp4_overlay0_done_lcdc(struct mdp_dma_data *dma);
+void mdp4_overlay0_done_mddi(struct mdp_dma_data *dma);
+void mdp4_dma_s_done_mddi(void);
+void mdp4_dma_p_done_mddi(void);
+void mdp4_dma_p_done_dsi(struct mdp_dma_data *dma);
+void mdp4_dma_p_done_dsi_video(struct mdp_dma_data *dma);
+void mdp4_dma_p_done_lcdc(void);
+void mdp4_overlay1_done_dtv(void);
+void mdp4_overlay1_done_atv(void);
+void mdp4_primary_vsync_lcdc(void);
+void mdp4_external_vsync_dtv(void);
+void mdp4_overlay_lcdc_wait4vsync(struct msm_fb_data_type *mfd);
+void mdp4_overlay_lcdc_start(void);
+void mdp4_overlay_lcdc_vsync_push(struct msm_fb_data_type *mfd,
+				struct mdp4_overlay_pipe *pipe);
+void mdp4_update_perf_level(u32 perf_level);
+void mdp4_set_perf_level(void);
+void mdp4_mddi_overlay_dmas_restore(void);
+
+#ifndef CONFIG_FB_MSM_MIPI_DSI
+void mdp4_mddi_dma_busy_wait(struct msm_fb_data_type *mfd);
+void mdp4_mddi_overlay_restore(void);
+#else
+static inline void mdp4_mddi_dma_busy_wait(struct msm_fb_data_type *mfd)
+{
+	/* empty */
+}
+static inline void mdp4_mddi_overlay_restore(void)
+{
+	/* empty */
+}
+#endif
+
+void mdp4_mddi_overlay_kickoff(struct msm_fb_data_type *mfd,
+				struct mdp4_overlay_pipe *pipe);
+void mdp4_rgb_igc_lut_setup(int num);
+void mdp4_vg_igc_lut_setup(int num);
+void mdp4_mixer_gc_lut_setup(int mixer_num);
+void mdp4_fetch_cfg(uint32 clk);
+uint32 mdp4_rgb_igc_lut_cvt(uint32 ndx);
+void mdp4_vg_qseed_init(int);
+int mdp4_overlay_blt(struct fb_info *info, struct msmfb_overlay_blt *req);
+int mdp4_overlay_blt_offset(struct fb_info *info,
+					struct msmfb_overlay_blt *req);
+
+
+#ifdef CONFIG_FB_MSM_MIPI_DSI
+int mdp4_dsi_overlay_blt_start(struct msm_fb_data_type *mfd);
+int mdp4_dsi_overlay_blt_stop(struct msm_fb_data_type *mfd);
+void mdp4_dsi_video_blt_start(struct msm_fb_data_type *mfd);
+void mdp4_dsi_video_blt_stop(struct msm_fb_data_type *mfd);
+void mdp4_dsi_overlay_blt(struct msm_fb_data_type *mfd,
+					struct msmfb_overlay_blt *req);
+int mdp4_dsi_overlay_blt_offset(struct msm_fb_data_type *mfd,
+					struct msmfb_overlay_blt *req);
+
+void mdp4_dsi_video_overlay_blt(struct msm_fb_data_type *mfd,
+					struct msmfb_overlay_blt *req);
+int mdp4_dsi_video_overlay_blt_offset(struct msm_fb_data_type *mfd,
+					struct msmfb_overlay_blt *req);
+
+#ifdef CONFIG_FB_MSM_MDP40
+static inline void mdp3_dsi_cmd_dma_busy_wait(struct msm_fb_data_type *mfd)
+{
+	/* empty */
+}
+#endif
+#else
+static inline int mdp4_dsi_overlay_blt_start(struct msm_fb_data_type *mfd)
+{
+	return -ENODEV;
+}
+static inline int mdp4_dsi_overlay_blt_stop(struct msm_fb_data_type *mfd)
+{
+	return -ENODEV;
+}
+static inline void mdp4_dsi_video_blt_start(struct msm_fb_data_type *mfd)
+{
+}
+static inline void mdp4_dsi_video_blt_stop(struct msm_fb_data_type *mfd)
+{
+}
+static inline void mdp4_dsi_overlay_blt(
+	struct msm_fb_data_type *mfd, struct msmfb_overlay_blt *req)
+{
+}
+static inline int mdp4_dsi_overlay_blt_offset(
+	struct msm_fb_data_type *mfd, struct msmfb_overlay_blt *req)
+{
+	return -ENODEV;
+}
+static inline void mdp4_dsi_video_overlay_blt(
+	struct msm_fb_data_type *mfd, struct msmfb_overlay_blt *req)
+{
+}
+static inline int mdp4_dsi_video_overlay_blt_offset(
+	struct msm_fb_data_type *mfd, struct msmfb_overlay_blt *req)
+{
+	return -ENODEV;
+}
+#endif
+
+void mdp4_lcdc_overlay_blt(struct msm_fb_data_type *mfd,
+					struct msmfb_overlay_blt *req);
+int mdp4_lcdc_overlay_blt_offset(struct msm_fb_data_type *mfd,
+					struct msmfb_overlay_blt *req);
+void mdp4_lcdc_overlay_blt_start(struct msm_fb_data_type *mfd);
+void mdp4_lcdc_overlay_blt_stop(struct msm_fb_data_type *mfd);
+void mdp4_dtv_overlay_blt_start(struct msm_fb_data_type *mfd);
+void mdp4_dtv_overlay_blt_stop(struct msm_fb_data_type *mfd);
+
+int mdp4_mddi_overlay_blt_offset(int *off);
+void mdp4_mddi_overlay_blt(ulong addr);
+void mdp4_overlay_panel_mode(int mixer_num, uint32 mode);
+void mdp4_overlay_panel_mode_unset(int mixer_num, uint32 mode);
+int mdp4_overlay_mixer_play(int mixer_num);
+uint32 mdp4_overlay_panel_list(void);
+void mdp4_lcdc_overlay_kickoff(struct msm_fb_data_type *mfd,
+			struct mdp4_overlay_pipe *pipe);
+
+void mdp4_mddi_kickoff_video(struct msm_fb_data_type *mfd,
+				struct mdp4_overlay_pipe *pipe);
+
+void mdp4_mddi_read_ptr_intr(void);
+
+void mdp4_dsi_cmd_dma_busy_check(void);
+
+#ifdef CONFIG_FB_MSM_MIPI_DSI
+void mdp4_dsi_cmd_dma_busy_wait(struct msm_fb_data_type *mfd);
+void mdp4_dsi_blt_dmap_busy_wait(struct msm_fb_data_type *mfd);
+void mdp4_overlay_dsi_video_start(void);
+void mdp4_overlay_dsi_video_vsync_push(struct msm_fb_data_type *mfd,
+				struct mdp4_overlay_pipe *pipe);
+void mdp4_dsi_cmd_overlay_restore(void);
+void mdp_dsi_cmd_overlay_suspend(void);
+#else
+static inline void mdp4_dsi_cmd_dma_busy_wait(struct msm_fb_data_type *mfd)
+{
+	/* empty */
+}
+static inline void mdp4_dsi_blt_dmap_busy_wait(struct msm_fb_data_type *mfd)
+{
+	/* empty */
+}
+static inline void mdp4_overlay_dsi_video_start(void)
+{
+	/* empty */
+}
+static inline void mdp4_overlay_dsi_video_vsync_push(
+	struct msm_fb_data_type *mfd, struct mdp4_overlay_pipe *pipe)
+{
+	/* empty */
+}
+static inline void mdp4_dsi_cmd_overlay_restore(void)
+{
+	/* empty */
+}
+#ifdef CONFIG_FB_MSM_MDP40
+static inline void mdp_dsi_cmd_overlay_suspend(void)
+{
+	/* empty */
+}
+#endif
+#endif /* MIPI_DSI */
+
+void mdp4_dsi_cmd_kickoff_ui(struct msm_fb_data_type *mfd,
+				struct mdp4_overlay_pipe *pipe);
+void mdp4_dsi_cmd_kickoff_video(struct msm_fb_data_type *mfd,
+				struct mdp4_overlay_pipe *pipe);
+void mdp4_dsi_cmd_overlay_kickoff(struct msm_fb_data_type *mfd,
+				struct mdp4_overlay_pipe *pipe);
+
+void mdp4_overlay_panel_3d(int mixer_num, uint32 panel_3d);
+int mdp4_overlay_3d_sbys(struct fb_info *info, struct msmfb_overlay_3d *req);
+void mdp4_dsi_cmd_3d_sbys(struct msm_fb_data_type *mfd,
+			 struct msmfb_overlay_3d *r3d);
+void mdp4_dsi_video_3d_sbys(struct msm_fb_data_type *mfd,
+			 struct msmfb_overlay_3d *r3d);
+
+int mdp4_mixer_info(int mixer_num, struct mdp_mixer_info *info);
+
+void mdp_dmap_vsync_set(int enable);
+int mdp_dmap_vsync_get(void);
+void mdp_hw_cursor_done(void);
+void mdp_hw_cursor_init(void);
+int mdp4_mddi_overlay_cursor(struct fb_info *info, struct fb_cursor *cursor);
+int mdp_ppp_blit(struct fb_info *info, struct mdp_blit_req *req);
+void mdp4_overlay_resource_release(void);
+void mdp4_overlay_dsi_video_wait4vsync(struct msm_fb_data_type *mfd);
+void mdp4_primary_vsync_dsi_video(void);
+uint32_t mdp4_ss_table_value(int8_t param, int8_t index);
+void mdp4_overlay_ctrl_db_reset(void);
+
+int mdp4_overlay_writeback_on(struct platform_device *pdev);
+int mdp4_overlay_writeback_off(struct platform_device *pdev);
+void mdp4_writeback_overlay(struct msm_fb_data_type *mfd);
+void mdp4_writeback_kickoff_video(struct msm_fb_data_type *mfd,
+		struct mdp4_overlay_pipe *pipe);
+void mdp4_writeback_dma_busy_wait(struct msm_fb_data_type *mfd);
+void mdp4_overlay1_done_writeback(struct mdp_dma_data *dma);
+
+int mdp4_writeback_start(struct fb_info *info);
+int mdp4_writeback_stop(struct fb_info *info);
+int mdp4_writeback_dequeue_buffer(struct fb_info *info,
+		struct msmfb_data *data);
+int mdp4_writeback_queue_buffer(struct fb_info *info,
+		struct msmfb_data *data);
+void mdp4_writeback_dma_stop(struct msm_fb_data_type *mfd);
+int mdp4_writeback_init(struct fb_info *info);
+int mdp4_writeback_terminate(struct fb_info *info);
+
+uint32_t mdp_block2base(uint32_t block);
+int mdp_hist_lut_config(struct mdp_hist_lut_data *data);
+
+void mdp4_hsic_set(struct mdp4_overlay_pipe *pipe, struct dpp_ctrl *ctrl);
+void mdp4_hsic_update(struct mdp4_overlay_pipe *pipe);
+int mdp4_csc_config(struct mdp_csc_cfg_data *config);
+void mdp4_csc_write(struct mdp_csc_cfg *data, uint32_t base);
+int mdp4_csc_enable(struct mdp_csc_cfg_data *config);
+int mdp4_pcc_cfg(struct mdp_pcc_cfg_data *cfg_ptr);
+int mdp4_argc_cfg(struct mdp_pgc_lut_data *pgc_ptr);
+int mdp4_qseed_cfg(struct mdp_qseed_cfg_data *cfg);
+u32  mdp4_allocate_writeback_buf(struct msm_fb_data_type *mfd, u32 mix_num);
+void mdp4_init_writeback_buf(struct msm_fb_data_type *mfd, u32 mix_num);
+void mdp4_free_writeback_buf(struct msm_fb_data_type *mfd, u32 mix_num);
+
+int mdp4_igc_lut_config(struct mdp_igc_lut_data *cfg);
+void mdp4_iommu_unmap(struct mdp4_overlay_pipe *pipe);
+void mdp4_iommu_attach(void);
+void mdp4_iommu_detach(void);
+int mdp4_v4l2_overlay_set(struct fb_info *info, struct mdp_overlay *req,
+		struct mdp4_overlay_pipe **ppipe);
+void mdp4_v4l2_overlay_clear(struct mdp4_overlay_pipe *pipe);
+int mdp4_v4l2_overlay_play(struct fb_info *info, struct mdp4_overlay_pipe *pipe,
+	unsigned long srcp0_addr, unsigned long srcp1_addr,
+	unsigned long srcp2_addr);
+
+#endif /* MDP_H */
diff --git a/drivers/video/msm/mdp4_dtv.c b/drivers/video/msm/mdp4_dtv.c
new file mode 100644
index 0000000..f0353bd
--- /dev/null
+++ b/drivers/video/msm/mdp4_dtv.c
@@ -0,0 +1,326 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/semaphore.h>
+#include <linux/uaccess.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <linux/pm_runtime.h>
+#include <mach/clk.h>
+
+#include "msm_fb.h"
+#include "mdp4.h"
+
+static int dtv_probe(struct platform_device *pdev);
+static int dtv_remove(struct platform_device *pdev);
+
+static int dtv_off(struct platform_device *pdev);
+static int dtv_on(struct platform_device *pdev);
+
+static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST];
+static int pdev_list_cnt;
+
+static struct clk *tv_src_clk;
+static struct clk *hdmi_clk;
+static struct clk *mdp_tv_clk;
+
+
+static int mdp4_dtv_runtime_suspend(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: suspending...\n");
+	return 0;
+}
+
+static int mdp4_dtv_runtime_resume(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: resuming...\n");
+	return 0;
+}
+
+static const struct dev_pm_ops mdp4_dtv_dev_pm_ops = {
+	.runtime_suspend = mdp4_dtv_runtime_suspend,
+	.runtime_resume = mdp4_dtv_runtime_resume,
+};
+
+static struct platform_driver dtv_driver = {
+	.probe = dtv_probe,
+	.remove = dtv_remove,
+	.suspend = NULL,
+	.resume = NULL,
+	.shutdown = NULL,
+	.driver = {
+		   .name = "dtv",
+		   .pm = &mdp4_dtv_dev_pm_ops,
+		   },
+};
+
+static struct lcdc_platform_data *dtv_pdata;
+#ifdef CONFIG_MSM_BUS_SCALING
+static uint32_t dtv_bus_scale_handle;
+#else
+static struct clk *ebi1_clk;
+#endif
+
+static int dtv_off(struct platform_device *pdev)
+{
+	int ret = 0;
+
+	ret = panel_next_off(pdev);
+
+	pr_info("%s\n", __func__);
+
+	clk_disable_unprepare(hdmi_clk);
+	if (mdp_tv_clk)
+		clk_disable_unprepare(mdp_tv_clk);
+
+	if (dtv_pdata && dtv_pdata->lcdc_power_save)
+		dtv_pdata->lcdc_power_save(0);
+
+	if (dtv_pdata && dtv_pdata->lcdc_gpio_config)
+		ret = dtv_pdata->lcdc_gpio_config(0);
+#ifdef CONFIG_MSM_BUS_SCALING
+	if (dtv_bus_scale_handle > 0)
+		msm_bus_scale_client_update_request(dtv_bus_scale_handle,
+							0);
+#else
+	if (ebi1_clk)
+		clk_disable_unprepare(ebi1_clk);
+#endif
+	mdp4_extn_disp = 0;
+	return ret;
+}
+
+static int dtv_on(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct msm_fb_data_type *mfd;
+	unsigned long panel_pixclock_freq , pm_qos_rate;
+
+	mfd = platform_get_drvdata(pdev);
+	panel_pixclock_freq = mfd->fbi->var.pixclock;
+
+	if (panel_pixclock_freq > 58000000)
+		/* pm_qos_rate should be in Khz */
+		pm_qos_rate = panel_pixclock_freq / 1000 ;
+	else
+		pm_qos_rate = 58000;
+	mdp4_extn_disp = 1;
+#ifdef CONFIG_MSM_BUS_SCALING
+	if (dtv_bus_scale_handle > 0)
+		msm_bus_scale_client_update_request(dtv_bus_scale_handle,
+							1);
+#else
+	if (ebi1_clk) {
+		clk_set_rate(ebi1_clk, pm_qos_rate * 1000);
+		clk_prepare_enable(ebi1_clk);
+	}
+#endif
+	mfd = platform_get_drvdata(pdev);
+
+	ret = clk_set_rate(tv_src_clk, mfd->fbi->var.pixclock);
+	if (ret) {
+		pr_info("%s: clk_set_rate(%d) failed\n", __func__,
+			mfd->fbi->var.pixclock);
+		if (mfd->fbi->var.pixclock == 27030000)
+			mfd->fbi->var.pixclock = 27000000;
+		ret = clk_set_rate(tv_src_clk, mfd->fbi->var.pixclock);
+	}
+	pr_info("%s: tv_src_clk=%dkHz, pm_qos_rate=%ldkHz, [%d]\n", __func__,
+		mfd->fbi->var.pixclock/1000, pm_qos_rate, ret);
+	mfd->panel_info.clk_rate = mfd->fbi->var.pixclock;
+	clk_prepare_enable(hdmi_clk);
+	clk_reset(hdmi_clk, CLK_RESET_ASSERT);
+	udelay(20);
+	clk_reset(hdmi_clk, CLK_RESET_DEASSERT);
+
+	if (mdp_tv_clk)
+		clk_prepare_enable(mdp_tv_clk);
+
+	if (dtv_pdata && dtv_pdata->lcdc_power_save)
+		dtv_pdata->lcdc_power_save(1);
+	if (dtv_pdata && dtv_pdata->lcdc_gpio_config)
+		ret = dtv_pdata->lcdc_gpio_config(1);
+
+	ret = panel_next_on(pdev);
+	return ret;
+}
+
+static int dtv_probe(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+	struct fb_info *fbi;
+	struct platform_device *mdp_dev = NULL;
+	struct msm_fb_panel_data *pdata = NULL;
+	int rc;
+
+	if (pdev->id == 0) {
+		dtv_pdata = pdev->dev.platform_data;
+#ifdef CONFIG_MSM_BUS_SCALING
+		if (!dtv_bus_scale_handle && dtv_pdata &&
+			dtv_pdata->bus_scale_table) {
+			dtv_bus_scale_handle =
+				msm_bus_scale_register_client(
+						dtv_pdata->bus_scale_table);
+			if (!dtv_bus_scale_handle) {
+				pr_err("%s not able to get bus scale\n",
+					__func__);
+			}
+		}
+#else
+		ebi1_clk = clk_get(&pdev->dev, "mem_clk");
+		if (IS_ERR(ebi1_clk)) {
+			ebi1_clk = NULL;
+			pr_warning("%s: Couldn't get ebi1 clock\n", __func__);
+		}
+#endif
+		tv_src_clk = clk_get(&pdev->dev, "src_clk");
+		if (IS_ERR(tv_src_clk)) {
+			pr_err("error: can't get tv_src_clk!\n");
+			return IS_ERR(tv_src_clk);
+		}
+
+		hdmi_clk = clk_get(&pdev->dev, "hdmi_clk");
+		if (IS_ERR(hdmi_clk)) {
+			pr_err("error: can't get hdmi_clk!\n");
+			return IS_ERR(hdmi_clk);
+		}
+
+		mdp_tv_clk = clk_get(&pdev->dev, "mdp_clk");
+		if (IS_ERR(mdp_tv_clk))
+			mdp_tv_clk = NULL;
+
+		return 0;
+	}
+
+	mfd = platform_get_drvdata(pdev);
+
+	if (!mfd)
+		return -ENODEV;
+
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST)
+		return -ENOMEM;
+
+	mdp_dev = platform_device_alloc("mdp", pdev->id);
+	if (!mdp_dev)
+		return -ENOMEM;
+
+	/*
+	 * link to the latest pdev
+	 */
+	mfd->pdev = mdp_dev;
+	mfd->dest = DISPLAY_LCDC;
+
+	/*
+	 * alloc panel device data
+	 */
+	if (platform_device_add_data
+	    (mdp_dev, pdev->dev.platform_data,
+	     sizeof(struct msm_fb_panel_data))) {
+		pr_err("dtv_probe: platform_device_add_data failed!\n");
+		platform_device_put(mdp_dev);
+		return -ENOMEM;
+	}
+	/*
+	 * data chain
+	 */
+	pdata = (struct msm_fb_panel_data *)mdp_dev->dev.platform_data;
+	pdata->on = dtv_on;
+	pdata->off = dtv_off;
+	pdata->next = pdev;
+
+	/*
+	 * get/set panel specific fb info
+	 */
+	mfd->panel_info = pdata->panel_info;
+	if (hdmi_prim_display)
+		mfd->fb_imgType = MSMFB_DEFAULT_TYPE;
+	else
+		mfd->fb_imgType = MDP_RGB_565;
+
+	fbi = mfd->fbi;
+	fbi->var.pixclock = mfd->panel_info.clk_rate;
+	fbi->var.left_margin = mfd->panel_info.lcdc.h_back_porch;
+	fbi->var.right_margin = mfd->panel_info.lcdc.h_front_porch;
+	fbi->var.upper_margin = mfd->panel_info.lcdc.v_back_porch;
+	fbi->var.lower_margin = mfd->panel_info.lcdc.v_front_porch;
+	fbi->var.hsync_len = mfd->panel_info.lcdc.h_pulse_width;
+	fbi->var.vsync_len = mfd->panel_info.lcdc.v_pulse_width;
+
+	/*
+	 * set driver data
+	 */
+	platform_set_drvdata(mdp_dev, mfd);
+
+	/*
+	 * register in mdp driver
+	 */
+	rc = platform_device_add(mdp_dev);
+	if (rc)
+		goto dtv_probe_err;
+
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+
+	pdev_list[pdev_list_cnt++] = pdev;
+	return 0;
+
+dtv_probe_err:
+#ifdef CONFIG_MSM_BUS_SCALING
+	if (dtv_pdata && dtv_pdata->bus_scale_table &&
+		dtv_bus_scale_handle > 0)
+		msm_bus_scale_unregister_client(dtv_bus_scale_handle);
+#endif
+	platform_device_put(mdp_dev);
+	return rc;
+}
+
+static int dtv_remove(struct platform_device *pdev)
+{
+#ifdef CONFIG_MSM_BUS_SCALING
+	if (dtv_pdata && dtv_pdata->bus_scale_table &&
+		dtv_bus_scale_handle > 0)
+		msm_bus_scale_unregister_client(dtv_bus_scale_handle);
+#else
+	if (ebi1_clk)
+		clk_put(ebi1_clk);
+#endif
+	pm_runtime_disable(&pdev->dev);
+	return 0;
+}
+
+static int dtv_register_driver(void)
+{
+	return platform_driver_register(&dtv_driver);
+}
+
+static int __init dtv_driver_init(void)
+{
+	return dtv_register_driver();
+}
+
+module_init(dtv_driver_init);
diff --git a/drivers/video/msm/mdp4_hsic.c b/drivers/video/msm/mdp4_hsic.c
new file mode 100644
index 0000000..5735f45
--- /dev/null
+++ b/drivers/video/msm/mdp4_hsic.c
@@ -0,0 +1,534 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/msm_mdp.h>
+#include "mdp.h"
+#include "mdp4.h"
+
+/* Definitions */
+#define MDP4_CSC_MV_OFF		0x4400
+#define MDP4_CSC_PRE_BV_OFF	0x4500
+#define MDP4_CSC_POST_BV_OFF	0x4580
+#define MDP4_CSC_PRE_LV_OFF	0x4600
+#define MDP4_CSC_POST_LV_OFF	0x4680
+#define MDP_VG1_BASE	(MDP_BASE + MDP4_VIDEO_BASE)
+
+#define MDP_VG1_CSC_MVn(n)	(MDP_VG1_BASE + MDP4_CSC_MV_OFF + 4 * (n))
+#define MDP_VG1_CSC_PRE_LVn(n)	(MDP_VG1_BASE + MDP4_CSC_PRE_LV_OFF + 4 * (n))
+#define MDP_VG1_CSC_POST_LVn(n)	(MDP_VG1_BASE + MDP4_CSC_POST_LV_OFF + 4 * (n))
+#define MDP_VG1_CSC_PRE_BVn(n)	(MDP_VG1_BASE + MDP4_CSC_PRE_BV_OFF + 4 * (n))
+#define MDP_VG1_CSC_POST_BVn(n)	(MDP_VG1_BASE + MDP4_CSC_POST_BV_OFF + 4 * (n))
+
+#define Q16	(16)
+#define Q16_ONE	(1 << Q16)
+
+#define Q16_VALUE(x)	((int32_t)((uint32_t)x << Q16))
+#define Q16_PERCENT_VALUE(x, n)	((int32_t)( \
+				div_s64(((int64_t)x * (int64_t)Q16_ONE), n)))
+
+#define Q16_WHOLE(x)	((int32_t)(x >> 16))
+#define Q16_FRAC(x)	((int32_t)(x & 0xFFFF))
+#define Q16_S1Q16_MUL(x, y)	(((x >> 1) * (y >> 1)) >> 14)
+
+#define Q16_MUL(x, y)	((int32_t)((((int64_t)x) * ((int64_t)y)) >> Q16))
+#define Q16_NEGATE(x)	(0 - (x))
+
+/*
+ * HSIC Control min/max values
+ *    These settings are based on the maximum/minimum allowed modifications to
+ *    HSIC controls for layer and display color.  Allowing too much variation in
+ *    the CSC block will result in color clipping resulting in unwanted color
+ *    shifts.
+ */
+#define TRIG_MAX	Q16_VALUE(128)
+#define CON_SAT_MAX	Q16_VALUE(128)
+#define INTENSITY_MAX	(Q16_VALUE(2047) >> 12)
+
+#define HUE_MAX	Q16_VALUE(100)
+#define HUE_MIN	Q16_VALUE(-100)
+#define HUE_DEF	Q16_VALUE(0)
+
+#define SAT_MAX	Q16_VALUE(100)
+#define SAT_MIN	Q16_VALUE(-100)
+#define SAT_DEF	CON_SAT_MAX
+
+#define CON_MAX	Q16_VALUE(100)
+#define CON_MIN	Q16_VALUE(-100)
+#define CON_DEF	CON_SAT_MAX
+
+#define INTEN_MAX	Q16_VALUE(100)
+#define INTEN_MIN	Q16_VALUE(-100)
+#define INTEN_DEF	Q16_VALUE(0)
+
+enum {
+	DIRTY,
+	GENERATED,
+	CLEAN
+};
+
+/* local vars*/
+static int32_t csc_matrix_tab[3][3] = {
+	{0x00012a00, 0x00000000, 0x00019880},
+	{0x00012a00, 0xffff9b80, 0xffff3000},
+	{0x00012a00, 0x00020480, 0x00000000}
+};
+
+static int32_t csc_yuv2rgb_conv_tab[3][3] = {
+	{0x00010000, 0x00000000, 0x000123cb},
+	{0x00010000, 0xffff9af9, 0xffff6b5e},
+	{0x00010000, 0x00020838, 0x00000000}
+};
+
+static int32_t csc_rgb2yuv_conv_tab[3][3] = {
+	{0x00004c8b, 0x00009645, 0x00001d2f},
+	{0xffffda56, 0xffffb60e, 0x00006f9d},
+	{0x00009d70, 0xffff7c2a, 0xffffe666}
+};
+
+static uint32_t csc_pre_bv_tab[3]  = {0xfffff800, 0xffffc000, 0xffffc000};
+static uint32_t csc_post_bv_tab[3] = {0x00000000, 0x00000000, 0x00000000};
+
+static uint32_t csc_pre_lv_tab[6] =  {0x00000000, 0x00007f80, 0x00000000,
+					0x00007f80, 0x00000000, 0x00007f80};
+static uint32_t csc_post_lv_tab[6] = {0x00000000, 0x00007f80, 0x00000000,
+					0x00007f80, 0x00000000, 0x00007f80};
+
+/* Lookup table for Sin/Cos lookup - Q16*/
+static const int32_t  trig_lut[65] = {
+	0x00000000, /* sin((2*M_PI/256) * 0x00);*/
+	0x00000648, /* sin((2*M_PI/256) * 0x01);*/
+	0x00000C90, /* sin((2*M_PI/256) * 0x02);*/
+	0x000012D5,
+	0x00001918,
+	0x00001F56,
+	0x00002590,
+	0x00002BC4,
+	0x000031F1,
+	0x00003817,
+	0x00003E34,
+	0x00004447,
+	0x00004A50,
+	0x0000504D,
+	0x0000563E,
+	0x00005C22,
+	0x000061F8,
+	0x000067BE,
+	0x00006D74,
+	0x0000731A,
+	0x000078AD,
+	0x00007E2F,
+	0x0000839C,
+	0x000088F6,
+	0x00008E3A,
+	0x00009368,
+	0x00009880,
+	0x00009D80,
+	0x0000A268,
+	0x0000A736,
+	0x0000ABEB,
+	0x0000B086,
+	0x0000B505,
+	0x0000B968,
+	0x0000BDAF,
+	0x0000C1D8,
+	0x0000C5E4,
+	0x0000C9D1,
+	0x0000CD9F,
+	0x0000D14D,
+	0x0000D4DB,
+	0x0000D848,
+	0x0000DB94,
+	0x0000DEBE,
+	0x0000E1C6,
+	0x0000E4AA,
+	0x0000E768,
+	0x0000EA0A,
+	0x0000EC83,
+	0x0000EED9,
+	0x0000F109,
+	0x0000F314,
+	0x0000F4FA,
+	0x0000F6BA,
+	0x0000F854,
+	0x0000F9C8,
+	0x0000FB15,
+	0x0000FC3B,
+	0x0000FD3B,
+	0x0000FE13,
+	0x0000FEC4,
+	0x0000FF4E,
+	0x0000FFB1,
+	0x0000FFEC,
+	0x00010000, /* sin((2*M_PI/256) * 0x40);*/
+};
+
+void trig_values_q16(int32_t deg, int32_t *cos, int32_t *sin)
+{
+	int32_t   angle;
+	int32_t   quad, anglei, anglef;
+	int32_t   v0 = 0, v1 = 0;
+	int32_t   t1, t2;
+
+	/*
+	 * Scale the angle so that 256 is one complete revolution and mask it
+	 * to this domain
+	 * NOTE: 0xB60B == 256/360
+	 */
+	angle = Q16_MUL(deg, 0xB60B) & 0x00FFFFFF;
+
+	/* Obtain a quadrant number, integer, and fractional part */
+	quad   =  angle >> 22;
+	anglei = (angle >> 16) & 0x3F;
+	anglef =  angle & 0xFFFF;
+
+	/*
+	 * Using the integer part, obtain the lookup table entry and its
+	 * complement. Using the quadrant, swap and negate these as
+	 * necessary.
+	 * (The values and all derivatives of sine and cosine functions
+	 * can be derived from these values)
+	 */
+	switch (quad) {
+	case 0x0:
+		v0 += trig_lut[anglei];
+		v1 += trig_lut[0x40-anglei];
+		break;
+
+	case 0x1:
+		v0 += trig_lut[0x40-anglei];
+		v1 -= trig_lut[anglei];
+		break;
+
+	case 0x2:
+		v0 -= trig_lut[anglei];
+		v1 -= trig_lut[0x40-anglei];
+		break;
+
+	case 0x3:
+		v0 -= trig_lut[0x40-anglei];
+		v1 += trig_lut[anglei];
+		break;
+	}
+
+	/*
+	 * Multiply the fractional part by 2*PI/256 to move it from lookup
+	 *  table units to radians, giving us the coefficient for first
+	 *  derivatives.
+	 */
+	t1 = Q16_S1Q16_MUL(anglef, 0x0648);
+
+	/*
+	 * Square this and divide by 2 to get the coefficient for second
+	 *   derivatives
+	 */
+	t2 = Q16_S1Q16_MUL(t1, t1) >> 1;
+
+	*sin = v0 + Q16_S1Q16_MUL(v1, t1) - Q16_S1Q16_MUL(v0, t2);
+
+	*cos = v1 - Q16_S1Q16_MUL(v0, t1) - Q16_S1Q16_MUL(v1, t2);
+}
+
+/* Convert input Q16 value to s4.9 */
+int16_t convert_q16_s49(int32_t q16Value)
+{	/* Top half is the whole number, Bottom half is fractional portion*/
+	int16_t whole = Q16_WHOLE(q16Value);
+	int32_t fraction  = Q16_FRAC(q16Value);
+
+	/* Clamp whole to 3 bits */
+	if (whole > 7)
+		whole = 7;
+	else if (whole < -7)
+		whole = -7;
+
+	/* Reduce fraction to 9 bits. */
+	fraction = (fraction<<9)>>Q16;
+
+	return (int16_t) ((int16_t)whole<<9) | ((int16_t)fraction);
+}
+
+/* Convert input Q16 value to uint16 */
+int16_t convert_q16_int16(int32_t val)
+{
+	int32_t rounded;
+
+	if (val >= 0) {
+		/* Add 0.5 */
+		rounded = val + (Q16_ONE>>1);
+	} else {
+		/* Subtract 0.5 */
+		rounded = val - (Q16_ONE>>1);
+	}
+
+	/* Truncate rounded value */
+	return (int16_t)(rounded>>Q16);
+}
+
+/*
+ * norm_q16
+ *              Return a Q16 value represeting a normalized value
+ *
+ * value       -100%                 0%               +100%
+ *                 |-----------------|----------------|
+ *                 ^                 ^                ^
+ *             q16MinValue     q16DefaultValue       q16MaxValue
+ *
+ */
+int32_t norm_q16(int32_t value, int32_t min, int32_t default_val, int32_t max,
+								int32_t range)
+{
+	int32_t diff, perc, mul, result;
+
+	if (0 == value) {
+		result = default_val;
+	} else if (value > 0) {
+		/* value is between 0% and +100% represent 1.0 -> QRange Max */
+		diff = range;
+		perc = Q16_PERCENT_VALUE(value, max);
+		mul = Q16_MUL(perc, diff);
+		result = default_val + mul;
+	} else {
+		/* if (value <= 0) */
+		diff = -range;
+		perc = Q16_PERCENT_VALUE(-value, -min);
+		mul = Q16_MUL(perc, diff);
+		result = default_val + mul;
+	}
+	return result;
+}
+
+void matrix_mul_3x3(int32_t dest[][3], int32_t a[][3], int32_t b[][3])
+{
+	int32_t i, j, k;
+	int32_t tmp[3][3];
+
+	for (i = 0; i < 3; i++) {
+		for (j = 0; j < 3; j++) {
+			tmp[i][j] = 0;
+			for (k = 0; k < 3; k++)
+				tmp[i][j] += Q16_MUL(a[i][k], b[k][j]);
+		}
+	}
+
+	/* in case dest = a or b*/
+	for (i = 0; i < 3; i++) {
+		for (j = 0; j < 3; j++)
+			dest[i][j] = tmp[i][j];
+	}
+}
+
+#define CONVERT(x)	(x)/*convert_q16_s49((x))*/
+void pr_params(struct mdp4_hsic_regs *regs)
+{
+	int i;
+	if (regs) {
+		for (i = 0; i < NUM_HSIC_PARAM; i++) {
+			pr_info("\t: hsic->params[%d] =	0x%08x [raw = 0x%08x]\n",
+			i, CONVERT(regs->params[i]), regs->params[i]);
+		}
+	}
+}
+
+void pr_3x3_matrix(int32_t in[][3])
+{
+	pr_info("\t[0x%08x\t0x%08x\t0x%08x]\n", CONVERT(in[0][0]),
+	CONVERT(in[0][1]), CONVERT(in[0][2]));
+	pr_info("\t[0x%08x\t0x%08x\t0x%08x]\n", CONVERT(in[1][0]),
+	CONVERT(in[1][1]), CONVERT(in[1][2]));
+	pr_info("\t[0x%08x\t0x%08x\t0x%08x]\n", CONVERT(in[2][0]),
+	CONVERT(in[2][1]), CONVERT(in[2][2]));
+}
+
+void _hsic_get(struct mdp4_hsic_regs *regs, int32_t type, int8_t *val)
+{
+	if (type < 0 || type >= NUM_HSIC_PARAM)
+		BUG_ON(-EINVAL);
+	*val = regs->params[type];
+	pr_info("%s: getting params[%d] = %d\n", __func__, type, *val);
+}
+
+void _hsic_set(struct mdp4_hsic_regs *regs, int32_t type, int8_t val)
+{
+	if (type < 0 || type >= NUM_HSIC_PARAM)
+		BUG_ON(-EINVAL);
+
+	if (regs->params[type] != Q16_VALUE(val)) {
+		regs->params[type] = Q16_VALUE(val);
+		regs->dirty = DIRTY;
+	}
+}
+
+void _hsic_generate_csc_matrix(struct mdp4_overlay_pipe *pipe)
+{
+	int i, j;
+	int32_t sin, cos;
+
+	int32_t hue_matrix[3][3];
+	int32_t con_sat_matrix[3][3];
+	struct mdp4_hsic_regs *regs = &(pipe->hsic_regs);
+
+	memset(con_sat_matrix, 0x0, sizeof(con_sat_matrix));
+	memset(hue_matrix, 0x0, sizeof(hue_matrix));
+
+	/*
+	 * HSIC control require matrix multiplication of these two tables
+	 *  [T 0 0][1 0  0]   T = Contrast       C=Cos(Hue)
+	 *  [0 S 0][0 C -N]   S = Saturation     N=Sin(Hue)
+	 *  [0 0 S][0 N  C]
+	 */
+
+	con_sat_matrix[0][0] = norm_q16(regs->params[HSIC_CON], CON_MIN,
+						CON_DEF, CON_MAX, CON_SAT_MAX);
+	con_sat_matrix[1][1] = norm_q16(regs->params[HSIC_SAT], SAT_MIN,
+						SAT_DEF, SAT_MAX, CON_SAT_MAX);
+	con_sat_matrix[2][2] = con_sat_matrix[1][1];
+
+	hue_matrix[0][0] = TRIG_MAX;
+
+	trig_values_q16(norm_q16(regs->params[HSIC_HUE], HUE_MIN, HUE_DEF,
+					 HUE_MAX, TRIG_MAX), &cos, &sin);
+
+	cos = Q16_MUL(cos, TRIG_MAX);
+	sin = Q16_MUL(sin, TRIG_MAX);
+
+	hue_matrix[1][1] = cos;
+	hue_matrix[2][2] = cos;
+	hue_matrix[2][1] = sin;
+	hue_matrix[1][2] = Q16_NEGATE(sin);
+
+	/* Generate YUV CSC matrix */
+	matrix_mul_3x3(regs->conv_matrix, con_sat_matrix, hue_matrix);
+
+	if (!(pipe->op_mode & MDP4_OP_SRC_DATA_YCBCR)) {
+		/* Convert input RGB to YUV then apply CSC matrix */
+		pr_info("Pipe %d, has RGB input\n", pipe->pipe_num);
+		matrix_mul_3x3(regs->conv_matrix, regs->conv_matrix,
+							csc_rgb2yuv_conv_tab);
+	}
+
+	/* Normalize the matrix */
+	for (i = 0; i < 3; i++) {
+		for (j = 0; j < 3; j++)
+			regs->conv_matrix[i][j] = (regs->conv_matrix[i][j]>>14);
+	}
+
+	/* Multiply above result by current csc table */
+	matrix_mul_3x3(regs->conv_matrix, regs->conv_matrix, csc_matrix_tab);
+
+	if (!(pipe->op_mode & MDP4_OP_SRC_DATA_YCBCR)) {
+		/*HACK:only "works"for src side*/
+		/* Convert back to RGB */
+		pr_info("Pipe %d, has RGB output\n", pipe->pipe_num);
+		matrix_mul_3x3(regs->conv_matrix, csc_yuv2rgb_conv_tab,
+							regs->conv_matrix);
+	}
+
+	/* Update clamps pre and post. */
+	/* TODO: different tables for different color formats? */
+	for (i = 0; i < 6; i++) {
+		regs->pre_limit[i] = csc_pre_lv_tab[i];
+		regs->post_limit[i] = csc_post_lv_tab[i];
+	}
+
+	/* update bias values, pre and post */
+	for (i = 0; i < 3; i++) {
+		regs->pre_bias[i] = csc_pre_bv_tab[i];
+		regs->post_bias[i] = csc_post_bv_tab[i] +
+				norm_q16(regs->params[HSIC_INT],
+				INTEN_MIN, INTEN_DEF, INTEN_MAX, INTENSITY_MAX);
+	}
+
+	regs->dirty = GENERATED;
+}
+
+void _hsic_update_mdp(struct mdp4_overlay_pipe *pipe)
+{
+	struct mdp4_hsic_regs *regs = &(pipe->hsic_regs);
+	int i, j, k;
+
+	uint32_t *csc_mv;
+	uint32_t *pre_lv;
+	uint32_t *post_lv;
+	uint32_t *pre_bv;
+	uint32_t *post_bv;
+
+	switch (pipe->pipe_num) {
+	case OVERLAY_PIPE_VG2:
+		csc_mv = (uint32_t *) (MDP_VG1_CSC_MVn(0) +
+					MDP4_VIDEO_OFF);
+		pre_lv = (uint32_t *) (MDP_VG1_CSC_PRE_LVn(0) +
+					MDP4_VIDEO_OFF);
+		post_lv = (uint32_t *) (MDP_VG1_CSC_POST_LVn(0) +
+					MDP4_VIDEO_OFF);
+		pre_bv = (uint32_t *) (MDP_VG1_CSC_PRE_BVn(0) +
+					MDP4_VIDEO_OFF);
+		post_bv = (uint32_t *) (MDP_VG1_CSC_POST_BVn(0) +
+					MDP4_VIDEO_OFF);
+		break;
+	case OVERLAY_PIPE_VG1:
+	default:
+			csc_mv = (uint32_t *) MDP_VG1_CSC_MVn(0);
+			pre_lv = (uint32_t *) MDP_VG1_CSC_PRE_LVn(0);
+			post_lv = (uint32_t *) MDP_VG1_CSC_POST_LVn(0);
+			pre_bv = (uint32_t *) MDP_VG1_CSC_PRE_BVn(0);
+			post_bv = (uint32_t *) MDP_VG1_CSC_POST_BVn(0);
+		break;
+	}
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	for (i = 0; i < 3; i++) {
+		for (j = 0; j < 3; j++) {
+			k = (3*i) + j;
+			MDP_OUTP(csc_mv + k, convert_q16_s49(
+						regs->conv_matrix[i][j]));
+		}
+	}
+
+	for (i = 0; i < 6; i++) {
+		MDP_OUTP(pre_lv + i, convert_q16_s49(regs->pre_limit[i]));
+		MDP_OUTP(post_lv + i, convert_q16_s49(regs->post_limit[i]));
+	}
+
+	for (i = 0; i < 3; i++) {
+		MDP_OUTP(pre_bv + i, convert_q16_s49(regs->pre_bias[i]));
+		MDP_OUTP(post_bv + i, convert_q16_s49(regs->post_bias[i]));
+	}
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	regs->dirty = CLEAN;
+}
+
+void mdp4_hsic_get(struct mdp4_overlay_pipe *pipe, struct dpp_ctrl *ctrl)
+{
+	int i;
+	for (i = 0; i < NUM_HSIC_PARAM; i++)
+		_hsic_get(&(pipe->hsic_regs), i, &(ctrl->hsic_params[i]));
+}
+
+void mdp4_hsic_set(struct mdp4_overlay_pipe *pipe, struct dpp_ctrl *ctrl)
+{
+	int i;
+	for (i = 0; i < NUM_HSIC_PARAM; i++)
+		_hsic_set(&(pipe->hsic_regs), i, ctrl->hsic_params[i]);
+
+	if (pipe->hsic_regs.dirty == DIRTY)
+		_hsic_generate_csc_matrix(pipe);
+}
+
+void mdp4_hsic_update(struct mdp4_overlay_pipe *pipe)
+{
+	if (pipe->hsic_regs.dirty == GENERATED)
+		_hsic_update_mdp(pipe);
+}
diff --git a/drivers/video/msm/mdp4_overlay.c b/drivers/video/msm/mdp4_overlay.c
new file mode 100644
index 0000000..6d4e44b
--- /dev/null
+++ b/drivers/video/msm/mdp4_overlay.c
@@ -0,0 +1,3187 @@
+/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/hrtimer.h>
+#include <linux/clk.h>
+#include <mach/hardware.h>
+#include <mach/iommu_domains.h>
+#include <mach/iommu.h>
+#include <linux/iommu.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+#include <linux/fb.h>
+#include <linux/msm_mdp.h>
+#include <linux/file.h>
+#include <linux/android_pmem.h>
+#include <linux/major.h>
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <linux/semaphore.h>
+#include <linux/uaccess.h>
+#include <linux/mutex.h>
+#include <linux/msm_kgsl.h>
+#include "mdp.h"
+#include "msm_fb.h"
+#include "mdp4.h"
+
+#define VERSION_KEY_MASK	0xFFFFFF00
+
+struct mdp4_overlay_ctrl {
+	struct mdp4_overlay_pipe plist[OVERLAY_PIPE_MAX];
+	struct mdp4_overlay_pipe *stage[MDP4_MIXER_MAX][MDP4_MIXER_STAGE_MAX];
+	uint32 mixer_cfg[MDP4_MIXER_MAX];
+	uint32 cs_controller;
+	uint32 panel_3d;
+	uint32 panel_mode;
+	uint32 mixer0_played;
+	uint32 mixer1_played;
+	uint32 mixer2_played;
+} mdp4_overlay_db = {
+	.cs_controller = CS_CONTROLLER_0,
+	.plist = {
+		{
+			.pipe_type = OVERLAY_TYPE_RGB,
+			.pipe_num = OVERLAY_PIPE_RGB1,
+			.pipe_ndx = 1,
+		},
+		{
+			.pipe_type = OVERLAY_TYPE_RGB,
+			.pipe_num = OVERLAY_PIPE_RGB2,
+			.pipe_ndx = 2,
+		},
+		{
+			.pipe_type = OVERLAY_TYPE_VIDEO,
+			.pipe_num = OVERLAY_PIPE_VG1,
+			.pipe_ndx = 3,
+		},
+		{
+			.pipe_type = OVERLAY_TYPE_VIDEO,
+			.pipe_num = OVERLAY_PIPE_VG2,
+			.pipe_ndx = 4,
+		},
+		{
+			.pipe_type = OVERLAY_TYPE_BF,
+			.pipe_num = OVERLAY_PIPE_RGB3,
+			.pipe_ndx = 5,
+			.mixer_num = MDP4_MIXER0,
+		},
+		{
+			.pipe_type = OVERLAY_TYPE_BF,
+			.pipe_num = OVERLAY_PIPE_VG3,
+			.pipe_ndx = 6,
+			.mixer_num = MDP4_MIXER1,
+		},
+		{
+			.pipe_type = OVERLAY_TYPE_BF,
+			.pipe_num = OVERLAY_PIPE_VG4,
+			.pipe_ndx = 7,
+			.mixer_num = MDP4_MIXER2,
+		},
+	},
+};
+
+static struct mdp4_overlay_ctrl *ctrl = &mdp4_overlay_db;
+static int new_perf_level;
+static struct ion_client *display_iclient;
+static struct mdp4_iommu_pipe_info mdp_iommu[MDP4_MIXER_MAX][OVERLAY_PIPE_MAX];
+
+int mdp4_overlay_iommu_map_buf(int mem_id,
+	struct mdp4_overlay_pipe *pipe, unsigned int plane,
+	unsigned long *start, unsigned long *len,
+	struct ion_handle **srcp_ihdl)
+{
+	struct mdp4_iommu_pipe_info *iom_pipe_info;
+
+	if (!display_iclient)
+		return -EINVAL;
+
+	*srcp_ihdl = ion_import_fd(display_iclient, mem_id);
+	if (IS_ERR_OR_NULL(*srcp_ihdl)) {
+		pr_err("ion_import_fd() failed\n");
+		return PTR_ERR(*srcp_ihdl);
+	}
+	pr_debug("%s(): ion_hdl %p, ion_buf %p\n", __func__, *srcp_ihdl,
+		ion_share(display_iclient, *srcp_ihdl));
+	pr_debug("mixer %u, pipe %u, plane %u\n", pipe->mixer_num,
+		pipe->pipe_ndx, plane);
+	if (ion_map_iommu(display_iclient, *srcp_ihdl,
+		DISPLAY_DOMAIN, GEN_POOL, SZ_4K, 0, start,
+		len, 0, ION_IOMMU_UNMAP_DELAYED)) {
+		ion_free(display_iclient, *srcp_ihdl);
+		pr_err("ion_map_iommu() failed\n");
+		return -EINVAL;
+	}
+
+	iom_pipe_info = &mdp_iommu[pipe->mixer_num][pipe->pipe_ndx - 1];
+	if (!iom_pipe_info->ihdl[plane]) {
+		iom_pipe_info->ihdl[plane] = *srcp_ihdl;
+	} else {
+		if (iom_pipe_info->prev_ihdl[plane]) {
+			ion_unmap_iommu(display_iclient,
+				iom_pipe_info->prev_ihdl[plane],
+				DISPLAY_DOMAIN, GEN_POOL);
+			ion_free(display_iclient,
+				iom_pipe_info->prev_ihdl[plane]);
+			pr_debug("Previous: mixer %u, pipe %u, plane %u, "
+				"prev_ihdl %p\n", pipe->mixer_num,
+				pipe->pipe_ndx, plane,
+				iom_pipe_info->prev_ihdl[plane]);
+		}
+
+		iom_pipe_info->prev_ihdl[plane] = iom_pipe_info->ihdl[plane];
+		iom_pipe_info->ihdl[plane] = *srcp_ihdl;
+	}
+	pr_debug("mem_id %d, start 0x%lx, len 0x%lx\n",
+		mem_id, *start, *len);
+	return 0;
+}
+
+void mdp4_iommu_unmap(struct mdp4_overlay_pipe *pipe)
+{
+	struct mdp4_iommu_pipe_info *iom_pipe_info;
+	unsigned char i, j;
+
+	if (!display_iclient)
+		return;
+
+	for (j = 0; j < OVERLAY_PIPE_MAX; j++) {
+		iom_pipe_info = &mdp_iommu[pipe->mixer_num][j];
+		for (i = 0; i < MDP4_MAX_PLANE; i++) {
+			if (iom_pipe_info->prev_ihdl[i]) {
+				pr_debug("%s(): mixer %u, pipe %u, plane %u, "
+					"prev_ihdl %p\n", __func__,
+					pipe->mixer_num, j + 1, i,
+					iom_pipe_info->prev_ihdl[i]);
+				ion_unmap_iommu(display_iclient,
+					iom_pipe_info->prev_ihdl[i],
+					DISPLAY_DOMAIN, GEN_POOL);
+				ion_free(display_iclient,
+					iom_pipe_info->prev_ihdl[i]);
+				iom_pipe_info->prev_ihdl[i] = NULL;
+			}
+
+			if (iom_pipe_info->mark_unmap) {
+				if (iom_pipe_info->ihdl[i]) {
+					if (pipe->mixer_num == MDP4_MIXER1)
+						mdp4_overlay_dtv_wait4vsync();
+					pr_debug("%s(): mixer %u, pipe %u, plane %u, "
+						"ihdl %p\n", __func__,
+						pipe->mixer_num, j + 1, i,
+						iom_pipe_info->ihdl[i]);
+					ion_unmap_iommu(display_iclient,
+						iom_pipe_info->ihdl[i],
+						DISPLAY_DOMAIN, GEN_POOL);
+					ion_free(display_iclient,
+						iom_pipe_info->ihdl[i]);
+					iom_pipe_info->ihdl[i] = NULL;
+				}
+			}
+		}
+		iom_pipe_info->mark_unmap = 0;
+	}
+}
+
+void mdp4_overlay_ctrl_db_reset(void)
+{
+	int i;
+
+	for (i = MDP4_MIXER0; i < MDP4_MIXER_MAX; i++)
+		ctrl->mixer_cfg[i] = 0;
+}
+
+int mdp4_overlay_mixer_play(int mixer_num)
+{
+	if (mixer_num == MDP4_MIXER2)
+		return ctrl->mixer2_played;
+	else if (mixer_num == MDP4_MIXER1)
+		return ctrl->mixer1_played;
+	else
+		return ctrl->mixer0_played;
+}
+
+void mdp4_overlay_panel_3d(int mixer_num, uint32 panel_3d)
+{
+	ctrl->panel_3d = panel_3d;
+}
+
+void mdp4_overlay_panel_mode(int mixer_num, uint32 mode)
+{
+	ctrl->panel_mode |= mode;
+}
+
+void mdp4_overlay_panel_mode_unset(int mixer_num, uint32 mode)
+{
+	ctrl->panel_mode &= ~mode;
+}
+
+uint32 mdp4_overlay_panel_list(void)
+{
+	return ctrl->panel_mode;
+}
+
+void mdp4_overlay_dmae_cfg(struct msm_fb_data_type *mfd, int atv)
+{
+	uint32	dmae_cfg_reg;
+
+	if (atv)
+		dmae_cfg_reg = DMA_DEFLKR_EN;
+	else
+		dmae_cfg_reg = 0;
+
+	if (mfd->fb_imgType == MDP_BGR_565)
+		dmae_cfg_reg |= DMA_PACK_PATTERN_BGR;
+	else
+		dmae_cfg_reg |= DMA_PACK_PATTERN_RGB;
+
+
+	if (mfd->panel_info.bpp == 18) {
+		dmae_cfg_reg |= DMA_DSTC0G_6BITS |	/* 666 18BPP */
+		    DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
+	} else if (mfd->panel_info.bpp == 16) {
+		dmae_cfg_reg |= DMA_DSTC0G_6BITS |	/* 565 16BPP */
+		    DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS;
+	} else {
+		dmae_cfg_reg |= DMA_DSTC0G_8BITS |	/* 888 16BPP */
+		    DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS;
+	}
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	/* dma2 config register */
+	MDP_OUTP(MDP_BASE + 0xb0000, dmae_cfg_reg);
+	if (atv) {
+		MDP_OUTP(MDP_BASE + 0xb0070, 0xeb0010);
+		MDP_OUTP(MDP_BASE + 0xb0074, 0xf00010);
+		MDP_OUTP(MDP_BASE + 0xb0078, 0xf00010);
+		MDP_OUTP(MDP_BASE + 0xb3000, 0x80);
+		MDP_OUTP(MDP_BASE + 0xb3010, 0x1800040);
+		MDP_OUTP(MDP_BASE + 0xb3014, 0x1000080);
+		MDP_OUTP(MDP_BASE + 0xb4004, 0x67686970);
+	} else {
+		mdp_vid_quant_set();
+	}
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+#ifdef CONFIG_FB_MSM_HDMI_3D
+void unfill_black_screen(void) { return; }
+#else
+void unfill_black_screen(void)
+{
+	uint32 temp_src_format;
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	/*
+	* VG2 Constant Color
+	*/
+	temp_src_format = inpdw(MDP_BASE + 0x30050);
+	MDP_OUTP(MDP_BASE + 0x30050, temp_src_format&(~BIT(22)));
+	/*
+	* MDP_OVERLAY_REG_FLUSH
+	*/
+	MDP_OUTP(MDP_BASE + 0x18000, BIT(3));
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+	return;
+}
+#endif
+
+#ifdef CONFIG_FB_MSM_HDMI_3D
+void fill_black_screen(void) { return; }
+#else
+void fill_black_screen(void)
+{
+	/*Black color*/
+	uint32 color = 0x00000000;
+	uint32 temp_src_format;
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	/*
+	* VG2 Constant Color
+	*/
+	MDP_OUTP(MDP_BASE + 0x31008, color);
+	/*
+	* MDP_VG2_SRC_FORMAT
+	*/
+	temp_src_format = inpdw(MDP_BASE + 0x30050);
+	MDP_OUTP(MDP_BASE + 0x30050, temp_src_format | BIT(22));
+	/*
+	* MDP_OVERLAY_REG_FLUSH
+	*/
+	MDP_OUTP(MDP_BASE + 0x18000, BIT(3));
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+	return;
+}
+#endif
+
+void mdp4_overlay_dmae_xy(struct mdp4_overlay_pipe *pipe)
+{
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	MDP_OUTP(MDP_BASE + 0xb0004,
+			(pipe->src_height << 16 | pipe->src_width));
+	if (pipe->blt_addr) {
+		uint32 off, bpp;
+#ifdef BLT_RGB565
+		bpp = 2; /* overlay ouput is RGB565 */
+#else
+		bpp = 3; /* overlay ouput is RGB888 */
+#endif
+		off = 0;
+		if (pipe->ov_cnt & 0x01)
+			off = pipe->src_height * pipe->src_width * bpp;
+		MDP_OUTP(MDP_BASE + 0xb0008, pipe->blt_addr + off);
+		/* RGB888, output of overlay blending */
+		MDP_OUTP(MDP_BASE + 0xb000c, pipe->src_width * bpp);
+	} else {
+		/* dma_e source */
+		MDP_OUTP(MDP_BASE + 0xb0008, pipe->srcp0_addr);
+		MDP_OUTP(MDP_BASE + 0xb000c, pipe->srcp0_ystride);
+	}
+	/* dma_e dest */
+	MDP_OUTP(MDP_BASE + 0xb0010, (pipe->dst_y << 16 | pipe->dst_x));
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+void mdp4_overlay_dmap_cfg(struct msm_fb_data_type *mfd, int lcdc)
+{
+	uint32	dma2_cfg_reg;
+	uint32 mask, curr;
+
+	dma2_cfg_reg = DMA_DITHER_EN;
+#ifdef BLT_RGB565
+	/* RGB888 is 0 */
+	dma2_cfg_reg |= DMA_BUF_FORMAT_RGB565; /* blt only */
+#endif
+
+	if (mfd->fb_imgType == MDP_BGR_565)
+		dma2_cfg_reg |= DMA_PACK_PATTERN_BGR;
+	else
+		dma2_cfg_reg |= DMA_PACK_PATTERN_RGB;
+
+
+	if (mfd->panel_info.bpp == 18) {
+		dma2_cfg_reg |= DMA_DSTC0G_6BITS |	/* 666 18BPP */
+		    DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
+	} else if (mfd->panel_info.bpp == 16) {
+		dma2_cfg_reg |= DMA_DSTC0G_6BITS |	/* 565 16BPP */
+		    DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS;
+	} else {
+		dma2_cfg_reg |= DMA_DSTC0G_8BITS |	/* 888 16BPP */
+		    DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS;
+	}
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+#ifndef CONFIG_FB_MSM_LCDC_CHIMEI_WXGA_PANEL
+	if (lcdc)
+		dma2_cfg_reg |= DMA_PACK_ALIGN_MSB;
+#endif
+
+	/* dma2 config register */
+	curr = inpdw(MDP_BASE + 0x90000);
+	mask = 0x0FFFFFFF;
+	dma2_cfg_reg = (dma2_cfg_reg & mask) | (curr & ~mask);
+	MDP_OUTP(MDP_BASE + 0x90000, dma2_cfg_reg);
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+/*
+ * mdp4_overlay_dmap_xy: called form baselayer only
+ */
+void mdp4_overlay_dmap_xy(struct mdp4_overlay_pipe *pipe)
+{
+	uint32 off, bpp;
+
+	if (mdp_is_in_isr == FALSE)
+		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	/* dma_p source */
+	MDP_OUTP(MDP_BASE + 0x90004,
+			(pipe->src_height << 16 | pipe->src_width));
+	if (pipe->blt_addr) {
+#ifdef BLT_RGB565
+		bpp = 2; /* overlay ouput is RGB565 */
+#else
+		bpp = 3; /* overlay ouput is RGB888 */
+#endif
+		off = 0;
+		if (pipe->dmap_cnt & 0x01)
+			off = pipe->src_height * pipe->src_width * bpp;
+		MDP_OUTP(MDP_BASE + 0x90008, pipe->blt_addr + off);
+		/* RGB888, output of overlay blending */
+		MDP_OUTP(MDP_BASE + 0x9000c, pipe->src_width * bpp);
+	} else {
+		MDP_OUTP(MDP_BASE + 0x90008, pipe->srcp0_addr);
+		MDP_OUTP(MDP_BASE + 0x9000c, pipe->srcp0_ystride);
+	}
+
+	/* dma_p dest */
+	MDP_OUTP(MDP_BASE + 0x90010, (pipe->dst_y << 16 | pipe->dst_x));
+
+	if (mdp_is_in_isr == FALSE)
+		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+#define MDP4_VG_PHASE_STEP_DEFAULT	0x20000000
+#define MDP4_VG_PHASE_STEP_SHIFT	29
+
+static int mdp4_leading_0(uint32 num)
+{
+	uint32 bit = 0x80000000;
+	int i;
+
+	for (i = 0; i < 32; i++) {
+		if (bit & num)
+			return i;
+		bit >>= 1;
+	}
+
+	return i;
+}
+
+static uint32 mdp4_scale_phase_step(int f_num, uint32 src, uint32 dst)
+{
+	uint32 val, s;
+	int	n;
+
+	n = mdp4_leading_0(src);
+	if (n > f_num)
+		n = f_num;
+	s = src << n;	/* maximum to reduce lose of resolution */
+	val = s / dst;
+	if (n < f_num) {
+		n = f_num - n;
+		val <<= n;
+		val |= ((s % dst) << n) / dst;
+	}
+
+	return val;
+}
+
+static void mdp4_scale_setup(struct mdp4_overlay_pipe *pipe)
+{
+	pipe->phasex_step = MDP4_VG_PHASE_STEP_DEFAULT;
+	pipe->phasey_step = MDP4_VG_PHASE_STEP_DEFAULT;
+
+	if (pipe->dst_h && pipe->src_h != pipe->dst_h) {
+		u32 upscale_max;
+		upscale_max = (mdp_rev >= MDP_REV_41) ?
+			MDP4_REV41_OR_LATER_UP_SCALING_MAX :
+			MDP4_REV40_UP_SCALING_MAX;
+		if (pipe->dst_h > pipe->src_h * upscale_max)
+			return;
+
+		pipe->op_mode |= MDP4_OP_SCALEY_EN;
+
+		if (pipe->pipe_type == OVERLAY_TYPE_VIDEO) {
+			if (pipe->flags & MDP_BACKEND_COMPOSITION &&
+				pipe->alpha_enable && pipe->dst_h > pipe->src_h)
+				pipe->op_mode |= MDP4_OP_SCALEY_PIXEL_RPT;
+			else if (pipe->dst_h <= (pipe->src_h / 4))
+				pipe->op_mode |= MDP4_OP_SCALEY_MN_PHASE;
+			else
+				pipe->op_mode |= MDP4_OP_SCALEY_FIR;
+		} else { /* RGB pipe */
+			pipe->op_mode |= MDP4_OP_SCALE_RGB_ENHANCED |
+				MDP4_OP_SCALE_RGB_BILINEAR |
+				MDP4_OP_SCALE_ALPHA_BILINEAR;
+		}
+
+		pipe->phasey_step = mdp4_scale_phase_step(29,
+					pipe->src_h, pipe->dst_h);
+	}
+
+	if (pipe->dst_w && pipe->src_w != pipe->dst_w) {
+		u32 upscale_max;
+		upscale_max = (mdp_rev >= MDP_REV_41) ?
+			MDP4_REV41_OR_LATER_UP_SCALING_MAX :
+			MDP4_REV40_UP_SCALING_MAX;
+
+		if (pipe->dst_w > pipe->src_w * upscale_max)
+			return;
+		pipe->op_mode |= MDP4_OP_SCALEX_EN;
+		if (pipe->pipe_type == OVERLAY_TYPE_VIDEO) {
+			if (pipe->flags & MDP_BACKEND_COMPOSITION &&
+				pipe->alpha_enable && pipe->dst_w > pipe->src_w)
+				pipe->op_mode |= MDP4_OP_SCALEX_PIXEL_RPT;
+			else if (pipe->dst_w <= (pipe->src_w / 4))
+				pipe->op_mode |= MDP4_OP_SCALEX_MN_PHASE;
+			else
+				pipe->op_mode |= MDP4_OP_SCALEX_FIR;
+		} else { /* RGB pipe */
+			pipe->op_mode |= MDP4_OP_SCALE_RGB_ENHANCED |
+				MDP4_OP_SCALE_RGB_BILINEAR |
+				MDP4_OP_SCALE_ALPHA_BILINEAR;
+		}
+
+		pipe->phasex_step = mdp4_scale_phase_step(29,
+					pipe->src_w, pipe->dst_w);
+	}
+}
+
+void mdp4_overlay_rgb_setup(struct mdp4_overlay_pipe *pipe)
+{
+	char *rgb_base;
+	uint32 src_size, src_xy, dst_size, dst_xy;
+	uint32 format, pattern;
+	uint32 curr, mask;
+	uint32 offset = 0;
+	int pnum;
+
+	pnum = pipe->pipe_num - OVERLAY_PIPE_RGB1; /* start from 0 */
+	rgb_base = MDP_BASE + MDP4_RGB_BASE;
+	rgb_base += (MDP4_RGB_OFF * pnum);
+
+	src_size = ((pipe->src_h << 16) | pipe->src_w);
+	src_xy = ((pipe->src_y << 16) | pipe->src_x);
+	dst_size = ((pipe->dst_h << 16) | pipe->dst_w);
+	dst_xy = ((pipe->dst_y << 16) | pipe->dst_x);
+
+	if ((pipe->src_x + pipe->src_w) > 0x7FF) {
+		offset += pipe->src_x * pipe->bpp;
+		src_xy &= 0xFFFF0000;
+	}
+
+	if ((pipe->src_y + pipe->src_h) > 0x7FF) {
+		offset += pipe->src_y * pipe->src_width * pipe->bpp;
+		src_xy &= 0x0000FFFF;
+	}
+
+	format = mdp4_overlay_format(pipe);
+	pattern = mdp4_overlay_unpack_pattern(pipe);
+
+#ifdef MDP4_IGC_LUT_ENABLE
+	pipe->op_mode |= MDP4_OP_IGC_LUT_EN;
+#endif
+
+	mdp4_scale_setup(pipe);
+
+	/* Ensure proper covert matrix loaded when color space swaps */
+	curr = inpdw(rgb_base + 0x0058);
+	/* Don't touch bits you don't want to configure*/
+	mask = 0xFFFEFFFF;
+	pipe->op_mode = (pipe->op_mode & mask) | (curr & ~mask);
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	outpdw(rgb_base + 0x0000, src_size);	/* MDP_RGB_SRC_SIZE */
+	outpdw(rgb_base + 0x0004, src_xy);	/* MDP_RGB_SRC_XY */
+	outpdw(rgb_base + 0x0008, dst_size);	/* MDP_RGB_DST_SIZE */
+	outpdw(rgb_base + 0x000c, dst_xy);	/* MDP_RGB_DST_XY */
+
+	outpdw(rgb_base + 0x0010, pipe->srcp0_addr + offset);
+	outpdw(rgb_base + 0x0040, pipe->srcp0_ystride);
+
+	outpdw(rgb_base + 0x0050, format);/* MDP_RGB_SRC_FORMAT */
+	outpdw(rgb_base + 0x0054, pattern);/* MDP_RGB_SRC_UNPACK_PATTERN */
+	if (format & MDP4_FORMAT_SOLID_FILL) {
+		u32 op_mode = pipe->op_mode;
+		op_mode &= ~(MDP4_OP_FLIP_LR + MDP4_OP_SCALEX_EN);
+		op_mode &= ~(MDP4_OP_FLIP_UD + MDP4_OP_SCALEY_EN);
+		outpdw(rgb_base + 0x0058, op_mode);/* MDP_RGB_OP_MODE */
+	} else
+		outpdw(rgb_base + 0x0058, pipe->op_mode);/* MDP_RGB_OP_MODE */
+	outpdw(rgb_base + 0x005c, pipe->phasex_step);
+	outpdw(rgb_base + 0x0060, pipe->phasey_step);
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	mdp4_stat.pipe[pipe->pipe_num]++;
+}
+
+
+static void mdp4_overlay_vg_get_src_offset(struct mdp4_overlay_pipe *pipe,
+	char *vg_base, uint32 *luma_off, uint32 *chroma_off)
+{
+	uint32 src_xy;
+	*luma_off = 0;
+	*chroma_off = 0;
+
+	if (pipe->src_x && (pipe->frame_format ==
+		MDP4_FRAME_FORMAT_LINEAR)) {
+		src_xy = (pipe->src_y << 16) | pipe->src_x;
+		src_xy &= 0xffff0000;
+		outpdw(vg_base + 0x0004, src_xy);	/* MDP_RGB_SRC_XY */
+
+		switch (pipe->src_format) {
+		case MDP_Y_CR_CB_H2V2:
+		case MDP_Y_CR_CB_GH2V2:
+		case MDP_Y_CB_CR_H2V2:
+				*luma_off = pipe->src_x;
+				*chroma_off = pipe->src_x/2;
+			break;
+
+		case MDP_Y_CBCR_H2V2_TILE:
+		case MDP_Y_CRCB_H2V2_TILE:
+		case MDP_Y_CBCR_H2V2:
+		case MDP_Y_CRCB_H2V2:
+		case MDP_Y_CRCB_H1V1:
+		case MDP_Y_CBCR_H1V1:
+		case MDP_Y_CRCB_H2V1:
+		case MDP_Y_CBCR_H2V1:
+			*luma_off = pipe->src_x;
+			*chroma_off = pipe->src_x;
+			break;
+
+		case MDP_YCRYCB_H2V1:
+			if (pipe->src_x & 0x1)
+				pipe->src_x += 1;
+			*luma_off += pipe->src_x * 2;
+			break;
+
+		case MDP_ARGB_8888:
+		case MDP_RGBA_8888:
+		case MDP_BGRA_8888:
+		case MDP_RGBX_8888:
+		case MDP_RGB_565:
+		case MDP_BGR_565:
+		case MDP_XRGB_8888:
+		case MDP_RGB_888:
+		case MDP_YCBCR_H1V1:
+		case MDP_YCRCB_H1V1:
+			*luma_off = pipe->src_x * pipe->bpp;
+			break;
+
+		default:
+			pr_err("%s: fmt %u not supported for adjustment\n",
+				__func__, pipe->src_format);
+			break;
+		}
+	}
+}
+
+void mdp4_overlay_vg_setup(struct mdp4_overlay_pipe *pipe)
+{
+	char *vg_base;
+	uint32 frame_size, src_size, src_xy, dst_size, dst_xy;
+	uint32 format, pattern, luma_offset, chroma_offset;
+	uint32 mask, curr, addr;
+	int pnum, ptype;
+
+	pnum = pipe->pipe_num - OVERLAY_PIPE_VG1; /* start from 0 */
+	vg_base = MDP_BASE + MDP4_VIDEO_BASE;
+	vg_base += (MDP4_VIDEO_OFF * pnum);
+
+	frame_size = ((pipe->src_height << 16) | pipe->src_width);
+	src_size = ((pipe->src_h << 16) | pipe->src_w);
+	src_xy = ((pipe->src_y << 16) | pipe->src_x);
+	dst_size = ((pipe->dst_h << 16) | pipe->dst_w);
+	dst_xy = ((pipe->dst_y << 16) | pipe->dst_x);
+
+	ptype = mdp4_overlay_format2type(pipe->src_format);
+	format = mdp4_overlay_format(pipe);
+	pattern = mdp4_overlay_unpack_pattern(pipe);
+
+	/* not RGB use VG pipe, pure VG pipe */
+	pipe->op_mode |= MDP4_OP_CSC_EN;
+	if (ptype != OVERLAY_TYPE_RGB)
+		pipe->op_mode |= MDP4_OP_SRC_DATA_YCBCR;
+
+#ifdef MDP4_IGC_LUT_ENABLE
+	pipe->op_mode |= MDP4_OP_IGC_LUT_EN;
+#endif
+
+	mdp4_scale_setup(pipe);
+
+	luma_offset = 0;
+	chroma_offset = 0;
+
+	if (ptype == OVERLAY_TYPE_RGB) {
+		if ((pipe->src_y + pipe->src_h) > 0x7FF) {
+			luma_offset = pipe->src_y * pipe->src_width * pipe->bpp;
+			src_xy &= 0x0000FFFF;
+		}
+
+		if ((pipe->src_x + pipe->src_w) > 0x7FF) {
+			luma_offset += pipe->src_x * pipe->bpp;
+			src_xy &= 0xFFFF0000;
+		}
+	}
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	outpdw(vg_base + 0x0000, src_size);	/* MDP_RGB_SRC_SIZE */
+	outpdw(vg_base + 0x0004, src_xy);	/* MDP_RGB_SRC_XY */
+	outpdw(vg_base + 0x0008, dst_size);	/* MDP_RGB_DST_SIZE */
+	outpdw(vg_base + 0x000c, dst_xy);	/* MDP_RGB_DST_XY */
+
+	if (pipe->frame_format != MDP4_FRAME_FORMAT_LINEAR)
+		outpdw(vg_base + 0x0048, frame_size);	/* TILE frame size */
+
+	/*
+	 * Adjust src X offset to avoid MDP from overfetching pixels
+	 * present before the offset. This is required for video
+	 * frames coming with unused green pixels along the left margin
+	 */
+	/* not RGB use VG pipe, pure VG pipe */
+	if (ptype != OVERLAY_TYPE_RGB) {
+		mdp4_overlay_vg_get_src_offset(pipe, vg_base, &luma_offset,
+			&chroma_offset);
+	}
+
+	/* Ensure proper covert matrix loaded when color space swaps */
+	curr = inpdw(vg_base + 0x0058);
+	mask = 0x600;
+
+	if ((curr & mask) != (pipe->op_mode & mask)) {
+		addr = ((uint32_t)vg_base) + 0x4000;
+		if (ptype != OVERLAY_TYPE_RGB)
+			mdp4_csc_write(&(mdp_csc_convert[1]), addr);
+		else
+			mdp4_csc_write(&(mdp_csc_convert[0]), addr);
+
+		mask = 0xFFFCFFFF;
+	} else {
+		/* Don't touch bits you don't want to configure*/
+		mask = 0xFFFCF1FF;
+	}
+	pipe->op_mode = (pipe->op_mode & mask) | (curr & ~mask);
+
+	/* luma component plane */
+	outpdw(vg_base + 0x0010, pipe->srcp0_addr + luma_offset);
+
+	/* chroma component plane or  planar color 1 */
+	outpdw(vg_base + 0x0014, pipe->srcp1_addr + chroma_offset);
+
+	/* planar color 2 */
+	outpdw(vg_base + 0x0018, pipe->srcp2_addr + chroma_offset);
+
+	outpdw(vg_base + 0x0040,
+			pipe->srcp1_ystride << 16 | pipe->srcp0_ystride);
+
+	outpdw(vg_base + 0x0044,
+			pipe->srcp3_ystride << 16 | pipe->srcp2_ystride);
+
+	outpdw(vg_base + 0x0050, format);	/* MDP_RGB_SRC_FORMAT */
+	outpdw(vg_base + 0x0054, pattern);	/* MDP_RGB_SRC_UNPACK_PATTERN */
+	if (format & MDP4_FORMAT_SOLID_FILL) {
+		u32 op_mode = pipe->op_mode;
+		op_mode &= ~(MDP4_OP_FLIP_LR + MDP4_OP_SCALEX_EN);
+		op_mode &= ~(MDP4_OP_FLIP_UD + MDP4_OP_SCALEY_EN);
+		outpdw(vg_base + 0x0058, op_mode);/* MDP_RGB_OP_MODE */
+	} else
+		outpdw(vg_base + 0x0058, pipe->op_mode);/* MDP_RGB_OP_MODE */
+	outpdw(vg_base + 0x005c, pipe->phasex_step);
+	outpdw(vg_base + 0x0060, pipe->phasey_step);
+
+	if (pipe->op_mode & MDP4_OP_DITHER_EN) {
+		outpdw(vg_base + 0x0068,
+			pipe->r_bit << 4 | pipe->b_bit << 2 | pipe->g_bit);
+	}
+
+	if (pipe->flags & MDP_SHARPENING) {
+		outpdw(vg_base + 0x8200,
+			mdp4_ss_table_value(pipe->req_data.dpp.sharp_strength,
+									0));
+		outpdw(vg_base + 0x8204,
+			mdp4_ss_table_value(pipe->req_data.dpp.sharp_strength,
+									1));
+	}
+
+	if (mdp_rev > MDP_REV_41) {
+		/* mdp chip select controller */
+		mask = 0;
+		if (pipe->pipe_num == OVERLAY_PIPE_VG1)
+			mask = 0x020; /* bit 5 */
+		else if (pipe->pipe_num == OVERLAY_PIPE_VG2)
+			mask = 0x02000; /* bit 13 */
+		if (mask) {
+			if (pipe->op_mode & MDP4_OP_SCALEY_MN_PHASE)
+				ctrl->cs_controller &= ~mask;
+			else
+				ctrl->cs_controller |= mask;
+			/* NOT double buffered */
+			outpdw(MDP_BASE + 0x00c0, ctrl->cs_controller);
+		}
+	}
+
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	mdp4_stat.pipe[pipe->pipe_num]++;
+}
+
+int mdp4_overlay_format2type(uint32 format)
+{
+	switch (format) {
+	case MDP_RGB_565:
+	case MDP_RGB_888:
+	case MDP_BGR_565:
+	case MDP_XRGB_8888:
+	case MDP_ARGB_8888:
+	case MDP_RGBA_8888:
+	case MDP_BGRA_8888:
+	case MDP_RGBX_8888:
+		return OVERLAY_TYPE_RGB;
+	case MDP_YCRYCB_H2V1:
+	case MDP_Y_CRCB_H2V1:
+	case MDP_Y_CBCR_H2V1:
+	case MDP_Y_CRCB_H2V2:
+	case MDP_Y_CBCR_H2V2:
+	case MDP_Y_CBCR_H2V2_TILE:
+	case MDP_Y_CRCB_H2V2_TILE:
+	case MDP_Y_CR_CB_H2V2:
+	case MDP_Y_CR_CB_GH2V2:
+	case MDP_Y_CB_CR_H2V2:
+	case MDP_Y_CRCB_H1V1:
+	case MDP_Y_CBCR_H1V1:
+	case MDP_YCRCB_H1V1:
+	case MDP_YCBCR_H1V1:
+		return OVERLAY_TYPE_VIDEO;
+	default:
+		mdp4_stat.err_format++;
+		return -ERANGE;
+	}
+
+}
+
+#define C3_ALPHA	3	/* alpha */
+#define C2_R_Cr		2	/* R/Cr */
+#define C1_B_Cb		1	/* B/Cb */
+#define C0_G_Y		0	/* G/luma */
+#define YUV_444_MAX_WIDTH		1280	/* Max width for YUV 444*/
+
+int mdp4_overlay_format2pipe(struct mdp4_overlay_pipe *pipe)
+{
+	switch (pipe->src_format) {
+	case MDP_RGB_565:
+		pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR;
+		pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED;
+		pipe->a_bit = 0;
+		pipe->r_bit = 1;	/* R, 5 bits */
+		pipe->b_bit = 1;	/* B, 5 bits */
+		pipe->g_bit = 2;	/* G, 6 bits */
+		pipe->alpha_enable = 0;
+		pipe->unpack_tight = 1;
+		pipe->unpack_align_msb = 0;
+		pipe->unpack_count = 2;
+		pipe->element2 = C2_R_Cr;	/* R */
+		pipe->element1 = C0_G_Y;	/* G */
+		pipe->element0 = C1_B_Cb;	/* B */
+		pipe->bpp = 2;	/* 2 bpp */
+		break;
+	case MDP_RGB_888:
+		pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR;
+		pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED;
+		pipe->a_bit = 0;
+		pipe->r_bit = 3;	/* R, 8 bits */
+		pipe->b_bit = 3;	/* B, 8 bits */
+		pipe->g_bit = 3;	/* G, 8 bits */
+		pipe->alpha_enable = 0;
+		pipe->unpack_tight = 1;
+		pipe->unpack_align_msb = 0;
+		pipe->unpack_count = 2;
+		pipe->element2 = C1_B_Cb;	/* B */
+		pipe->element1 = C0_G_Y;	/* G */
+		pipe->element0 = C2_R_Cr;	/* R */
+		pipe->bpp = 3;	/* 3 bpp */
+		break;
+	case MDP_BGR_565:
+		pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR;
+		pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED;
+		pipe->a_bit = 0;
+		pipe->r_bit = 1;	/* R, 5 bits */
+		pipe->b_bit = 1;	/* B, 5 bits */
+		pipe->g_bit = 2;	/* G, 6 bits */
+		pipe->alpha_enable = 0;
+		pipe->unpack_tight = 1;
+		pipe->unpack_align_msb = 0;
+		pipe->unpack_count = 2;
+		pipe->element2 = C1_B_Cb;	/* B */
+		pipe->element1 = C0_G_Y;	/* G */
+		pipe->element0 = C2_R_Cr;	/* R */
+		pipe->bpp = 2;	/* 2 bpp */
+		break;
+	case MDP_XRGB_8888:
+		pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR;
+		pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED;
+		pipe->a_bit = 3;	/* alpha, 4 bits */
+		pipe->r_bit = 3;	/* R, 8 bits */
+		pipe->b_bit = 3;	/* B, 8 bits */
+		pipe->g_bit = 3;	/* G, 8 bits */
+		pipe->alpha_enable = 0;
+		pipe->unpack_tight = 1;
+		pipe->unpack_align_msb = 0;
+		pipe->unpack_count = 3;
+		pipe->element3 = C1_B_Cb;	/* B */
+		pipe->element2 = C0_G_Y;	/* G */
+		pipe->element1 = C2_R_Cr;	/* R */
+		pipe->element0 = C3_ALPHA;	/* alpha */
+		pipe->bpp = 4;		/* 4 bpp */
+		break;
+	case MDP_ARGB_8888:
+		pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR;
+		pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED;
+		pipe->a_bit = 3;	/* alpha, 4 bits */
+		pipe->r_bit = 3;	/* R, 8 bits */
+		pipe->b_bit = 3;	/* B, 8 bits */
+		pipe->g_bit = 3;	/* G, 8 bits */
+		pipe->alpha_enable = 1;
+		pipe->unpack_tight = 1;
+		pipe->unpack_align_msb = 0;
+		pipe->unpack_count = 3;
+		pipe->element3 = C1_B_Cb;	/* B */
+		pipe->element2 = C0_G_Y;	/* G */
+		pipe->element1 = C2_R_Cr;	/* R */
+		pipe->element0 = C3_ALPHA;	/* alpha */
+		pipe->bpp = 4;		/* 4 bpp */
+		break;
+	case MDP_RGBA_8888:
+		pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR;
+		pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED;
+		pipe->a_bit = 3;	/* alpha, 4 bits */
+		pipe->r_bit = 3;	/* R, 8 bits */
+		pipe->b_bit = 3;	/* B, 8 bits */
+		pipe->g_bit = 3;	/* G, 8 bits */
+		pipe->alpha_enable = 1;
+		pipe->unpack_tight = 1;
+		pipe->unpack_align_msb = 0;
+		pipe->unpack_count = 3;
+		pipe->element3 = C3_ALPHA;	/* alpha */
+		pipe->element2 = C1_B_Cb;	/* B */
+		pipe->element1 = C0_G_Y;	/* G */
+		pipe->element0 = C2_R_Cr;	/* R */
+		pipe->bpp = 4;		/* 4 bpp */
+		break;
+	case MDP_RGBX_8888:
+		pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR;
+		pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED;
+		pipe->a_bit = 3;
+		pipe->r_bit = 3;	/* R, 8 bits */
+		pipe->b_bit = 3;	/* B, 8 bits */
+		pipe->g_bit = 3;	/* G, 8 bits */
+		pipe->alpha_enable = 0;
+		pipe->unpack_tight = 1;
+		pipe->unpack_align_msb = 0;
+		pipe->unpack_count = 3;
+		pipe->element3 = C3_ALPHA;	/* alpha */
+		pipe->element2 = C1_B_Cb;	/* B */
+		pipe->element1 = C0_G_Y;	/* G */
+		pipe->element0 = C2_R_Cr;	/* R */
+		pipe->bpp = 4;		/* 4 bpp */
+		break;
+	case MDP_BGRA_8888:
+		pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR;
+		pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED;
+		pipe->a_bit = 3;	/* alpha, 4 bits */
+		pipe->r_bit = 3;	/* R, 8 bits */
+		pipe->b_bit = 3;	/* B, 8 bits */
+		pipe->g_bit = 3;	/* G, 8 bits */
+		pipe->alpha_enable = 1;
+		pipe->unpack_tight = 1;
+		pipe->unpack_align_msb = 0;
+		pipe->unpack_count = 3;
+		pipe->element3 = C3_ALPHA;	/* alpha */
+		pipe->element2 = C2_R_Cr;	/* R */
+		pipe->element1 = C0_G_Y;	/* G */
+		pipe->element0 = C1_B_Cb;	/* B */
+		pipe->bpp = 4;		/* 4 bpp */
+		break;
+	case MDP_YCRYCB_H2V1:
+		pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR;
+		pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED;
+		pipe->a_bit = 0;	/* alpha, 4 bits */
+		pipe->r_bit = 3;	/* R, 8 bits */
+		pipe->b_bit = 3;	/* B, 8 bits */
+		pipe->g_bit = 3;	/* G, 8 bits */
+		pipe->alpha_enable = 0;
+		pipe->unpack_tight = 1;
+		pipe->unpack_align_msb = 0;
+		pipe->unpack_count = 3;
+		pipe->element3 = C0_G_Y;	/* G */
+		pipe->element2 = C2_R_Cr;	/* R */
+		pipe->element1 = C0_G_Y;	/* G */
+		pipe->element0 = C1_B_Cb;	/* B */
+		pipe->bpp = 2;		/* 2 bpp */
+		pipe->chroma_sample = MDP4_CHROMA_H2V1;
+		break;
+	case MDP_Y_CRCB_H2V1:
+	case MDP_Y_CBCR_H2V1:
+	case MDP_Y_CRCB_H2V2:
+	case MDP_Y_CBCR_H2V2:
+	case MDP_Y_CRCB_H1V1:
+	case MDP_Y_CBCR_H1V1:
+		pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR;
+		pipe->fetch_plane = OVERLAY_PLANE_PSEUDO_PLANAR;
+		pipe->a_bit = 0;
+		pipe->r_bit = 3;	/* R, 8 bits */
+		pipe->b_bit = 3;	/* B, 8 bits */
+		pipe->g_bit = 3;	/* G, 8 bits */
+		pipe->alpha_enable = 0;
+		pipe->unpack_tight = 1;
+		pipe->unpack_align_msb = 0;
+		pipe->unpack_count = 1;		/* 2 */
+		if (pipe->src_format == MDP_Y_CRCB_H2V1) {
+			pipe->element1 = C1_B_Cb;
+			pipe->element0 = C2_R_Cr;
+			pipe->chroma_sample = MDP4_CHROMA_H2V1;
+		} else if (pipe->src_format == MDP_Y_CRCB_H1V1) {
+			pipe->element1 = C1_B_Cb;
+			pipe->element0 = C2_R_Cr;
+			if (pipe->src_width > YUV_444_MAX_WIDTH)
+				pipe->chroma_sample = MDP4_CHROMA_H1V2;
+			else
+				pipe->chroma_sample = MDP4_CHROMA_RGB;
+		} else if (pipe->src_format == MDP_Y_CBCR_H2V1) {
+			pipe->element1 = C2_R_Cr;
+			pipe->element0 = C1_B_Cb;
+			pipe->chroma_sample = MDP4_CHROMA_H2V1;
+		} else if (pipe->src_format == MDP_Y_CBCR_H1V1) {
+			pipe->element1 = C2_R_Cr;
+			pipe->element0 = C1_B_Cb;
+			if (pipe->src_width > YUV_444_MAX_WIDTH)
+				pipe->chroma_sample = MDP4_CHROMA_H1V2;
+			else
+				pipe->chroma_sample = MDP4_CHROMA_RGB;
+		} else if (pipe->src_format == MDP_Y_CRCB_H2V2) {
+			pipe->element1 = C1_B_Cb;
+			pipe->element0 = C2_R_Cr;
+			pipe->chroma_sample = MDP4_CHROMA_420;
+		} else if (pipe->src_format == MDP_Y_CBCR_H2V2) {
+			pipe->element1 = C2_R_Cr;
+			pipe->element0 = C1_B_Cb;
+			pipe->chroma_sample = MDP4_CHROMA_420;
+		}
+		pipe->bpp = 2;	/* 2 bpp */
+		break;
+	case MDP_Y_CBCR_H2V2_TILE:
+	case MDP_Y_CRCB_H2V2_TILE:
+		pipe->frame_format = MDP4_FRAME_FORMAT_VIDEO_SUPERTILE;
+		pipe->fetch_plane = OVERLAY_PLANE_PSEUDO_PLANAR;
+		pipe->a_bit = 0;
+		pipe->r_bit = 3;	/* R, 8 bits */
+		pipe->b_bit = 3;	/* B, 8 bits */
+		pipe->g_bit = 3;	/* G, 8 bits */
+		pipe->alpha_enable = 0;
+		pipe->unpack_tight = 1;
+		pipe->unpack_align_msb = 0;
+		pipe->unpack_count = 1;		/* 2 */
+		if (pipe->src_format == MDP_Y_CRCB_H2V2_TILE) {
+			pipe->element1 = C1_B_Cb;	/* B */
+			pipe->element0 = C2_R_Cr;	/* R */
+			pipe->chroma_sample = MDP4_CHROMA_420;
+		} else if (pipe->src_format == MDP_Y_CBCR_H2V2_TILE) {
+			pipe->element1 = C2_R_Cr;	/* R */
+			pipe->element0 = C1_B_Cb;	/* B */
+			pipe->chroma_sample = MDP4_CHROMA_420;
+		}
+		pipe->bpp = 2;	/* 2 bpp */
+		break;
+	case MDP_Y_CR_CB_H2V2:
+	case MDP_Y_CR_CB_GH2V2:
+	case MDP_Y_CB_CR_H2V2:
+		pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR;
+		pipe->fetch_plane = OVERLAY_PLANE_PLANAR;
+		pipe->a_bit = 0;
+		pipe->r_bit = 3;	/* R, 8 bits */
+		pipe->b_bit = 3;	/* B, 8 bits */
+		pipe->g_bit = 3;	/* G, 8 bits */
+		pipe->alpha_enable = 0;
+		pipe->chroma_sample = MDP4_CHROMA_420;
+		pipe->bpp = 2;	/* 2 bpp */
+		break;
+	case MDP_YCBCR_H1V1:
+	case MDP_YCRCB_H1V1:
+		pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR;
+		pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED;
+		pipe->a_bit = 0;
+		pipe->r_bit = 3;    /* R, 8 bits */
+		pipe->b_bit = 3;    /* B, 8 bits */
+		pipe->g_bit = 3;    /* G, 8 bits */
+		pipe->alpha_enable = 0;
+		pipe->unpack_tight = 1;
+		pipe->unpack_align_msb = 0;
+		pipe->unpack_count = 2;
+		pipe->element0 = C0_G_Y;    /* G */
+		if (pipe->src_format == MDP_YCRCB_H1V1) {
+			pipe->element1 = C2_R_Cr; /* R */
+			pipe->element2 = C1_B_Cb; /* B */
+		} else {
+			pipe->element1 = C1_B_Cb;   /* B */
+			pipe->element2 = C2_R_Cr;   /* R */
+		}
+		pipe->bpp = 3;  /* 3 bpp */
+		break;
+	default:
+		/* not likely */
+		mdp4_stat.err_format++;
+		return -ERANGE;
+	}
+
+	return 0;
+}
+
+/*
+ * color_key_convert: output with 12 bits color key
+ */
+static uint32 color_key_convert(int start, int num, uint32 color)
+{
+	uint32 data;
+
+	data = (color >> start) & ((1 << num) - 1);
+
+	/* convert to 8 bits */
+	if (num == 5)
+		data = ((data << 3) | (data >> 2));
+	else if (num == 6)
+		data = ((data << 2) | (data >> 4));
+
+	/* convert 8 bits to 12 bits */
+	data = (data << 4) | (data >> 4);
+
+	return data;
+}
+
+void transp_color_key(int format, uint32 transp,
+			uint32 *c0, uint32 *c1, uint32 *c2)
+{
+	int b_start, g_start, r_start;
+	int b_num, g_num, r_num;
+
+	switch (format) {
+	case MDP_RGB_565:
+		b_start = 0;
+		g_start = 5;
+		r_start = 11;
+		r_num = 5;
+		g_num = 6;
+		b_num = 5;
+		break;
+	case MDP_RGB_888:
+	case MDP_XRGB_8888:
+	case MDP_ARGB_8888:
+	case MDP_BGRA_8888:
+		b_start = 0;
+		g_start = 8;
+		r_start = 16;
+		r_num = 8;
+		g_num = 8;
+		b_num = 8;
+		break;
+	case MDP_RGBA_8888:
+	case MDP_RGBX_8888:
+		b_start = 16;
+		g_start = 8;
+		r_start = 0;
+		r_num = 8;
+		g_num = 8;
+		b_num = 8;
+		break;
+	case MDP_BGR_565:
+		b_start = 11;
+		g_start = 5;
+		r_start = 0;
+		r_num = 5;
+		g_num = 6;
+		b_num = 5;
+		break;
+	case MDP_Y_CB_CR_H2V2:
+	case MDP_Y_CBCR_H2V2:
+	case MDP_Y_CBCR_H2V1:
+	case MDP_YCBCR_H1V1:
+		b_start = 8;
+		g_start = 16;
+		r_start = 0;
+		r_num = 8;
+		g_num = 8;
+		b_num = 8;
+		break;
+	case MDP_Y_CR_CB_H2V2:
+	case MDP_Y_CR_CB_GH2V2:
+	case MDP_Y_CRCB_H2V2:
+	case MDP_Y_CRCB_H2V1:
+	case MDP_Y_CRCB_H1V1:
+	case MDP_Y_CBCR_H1V1:
+	case MDP_YCRCB_H1V1:
+		b_start = 0;
+		g_start = 16;
+		r_start = 8;
+		r_num = 8;
+		g_num = 8;
+		b_num = 8;
+		break;
+	default:
+		b_start = 0;
+		g_start = 8;
+		r_start = 16;
+		r_num = 8;
+		g_num = 8;
+		b_num = 8;
+		break;
+	}
+
+	*c0 = color_key_convert(g_start, g_num, transp);
+	*c1 = color_key_convert(b_start, b_num, transp);
+	*c2 = color_key_convert(r_start, r_num, transp);
+}
+
+uint32 mdp4_overlay_format(struct mdp4_overlay_pipe *pipe)
+{
+	uint32	format;
+
+	format = 0;
+
+	if (pipe->solid_fill)
+		format |= MDP4_FORMAT_SOLID_FILL;
+
+	if (pipe->unpack_align_msb)
+		format |= MDP4_FORMAT_UNPACK_ALIGN_MSB;
+
+	if (pipe->unpack_tight)
+		format |= MDP4_FORMAT_UNPACK_TIGHT;
+
+	if (pipe->alpha_enable)
+		format |= MDP4_FORMAT_ALPHA_ENABLE;
+
+	if (pipe->flags & MDP_SOURCE_ROTATED_90)
+		format |= MDP4_FORMAT_90_ROTATED;
+	format |= (pipe->unpack_count << 13);
+	format |= ((pipe->bpp - 1) << 9);
+	format |= (pipe->a_bit << 6);
+	format |= (pipe->r_bit << 4);
+	format |= (pipe->b_bit << 2);
+	format |= pipe->g_bit;
+
+	format |= (pipe->frame_format << 29);
+
+	if (pipe->fetch_plane == OVERLAY_PLANE_PSEUDO_PLANAR ||
+			pipe->fetch_plane == OVERLAY_PLANE_PLANAR) {
+		/* video/graphic */
+		format |= (pipe->fetch_plane << 19);
+		format |= (pipe->chroma_site << 28);
+		format |= (pipe->chroma_sample << 26);
+	}
+
+	return format;
+}
+
+uint32 mdp4_overlay_unpack_pattern(struct mdp4_overlay_pipe *pipe)
+{
+	return (pipe->element3 << 24) | (pipe->element2 << 16) |
+			(pipe->element1 << 8) | pipe->element0;
+}
+
+/*
+ * mdp4_overlayproc_cfg: only be called from base layer
+ */
+void mdp4_overlayproc_cfg(struct mdp4_overlay_pipe *pipe)
+{
+	uint32 data, intf;
+	char *overlay_base;
+	uint32 curr;
+
+	intf = 0;
+	if (pipe->mixer_num == MDP4_MIXER2)
+		overlay_base = MDP_BASE + MDP4_OVERLAYPROC2_BASE;
+	else if (pipe->mixer_num == MDP4_MIXER1) {
+		overlay_base = MDP_BASE + MDP4_OVERLAYPROC1_BASE;/* 0x18000 */
+		intf = inpdw(MDP_BASE + 0x0038); /* MDP_DISP_INTF_SEL */
+		intf >>= 4;
+		intf &= 0x03;
+	} else
+		overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */
+
+	if (mdp_is_in_isr == FALSE)
+		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	/*
+	 * BLT support both primary and external external
+	 */
+	if (pipe->blt_addr) {
+		int off, bpp;
+#ifdef BLT_RGB565
+		bpp = 2;  /* overlay ouput is RGB565 */
+#else
+		bpp = 3;  /* overlay ouput is RGB888 */
+#endif
+		data = pipe->src_height;
+		data <<= 16;
+		data |= pipe->src_width;
+		outpdw(overlay_base + 0x0008, data); /* ROI, height + width */
+		if (pipe->mixer_num == MDP4_MIXER0 ||
+		    pipe->mixer_num == MDP4_MIXER1) {
+			off = 0;
+			if (pipe->ov_cnt & 0x01)
+				off = pipe->src_height * pipe->src_width * bpp;
+
+			outpdw(overlay_base + 0x000c, pipe->blt_addr + off);
+			/* overlay ouput is RGB888 */
+			outpdw(overlay_base + 0x0010, pipe->src_width * bpp);
+			outpdw(overlay_base + 0x001c, pipe->blt_addr + off);
+			/* MDDI - BLT + on demand */
+			outpdw(overlay_base + 0x0004, 0x08);
+
+			curr = inpdw(overlay_base + 0x0014);
+			curr &= 0x4;
+#ifdef BLT_RGB565
+			outpdw(overlay_base + 0x0014, curr | 0x1); /* RGB565 */
+#else
+			outpdw(overlay_base + 0x0014, curr | 0x0); /* RGB888 */
+#endif
+		} else if (pipe->mixer_num == MDP4_MIXER2) {
+			if (ctrl->panel_mode & MDP4_PANEL_WRITEBACK) {
+				off = 0;
+				bpp = 1;
+				if (pipe->ov_cnt & 0x01)
+					off = pipe->src_height *
+							pipe->src_width * bpp;
+
+				outpdw(overlay_base + 0x000c,
+						pipe->blt_addr + off);
+				/* overlay ouput is RGB888 */
+				outpdw(overlay_base + 0x0010,
+					((pipe->src_width << 16) |
+					 pipe->src_width));
+				outpdw(overlay_base + 0x001c,
+						pipe->blt_addr + off);
+				off = pipe->src_height * pipe->src_width;
+				/* align chroma to 2k address */
+				off = (off + 2047) & ~2047;
+				/* UV plane adress */
+				outpdw(overlay_base + 0x0020,
+						pipe->blt_addr + off);
+				/* MDDI - BLT + on demand */
+				outpdw(overlay_base + 0x0004, 0x08);
+				/* pseudo planar + writeback */
+				curr = inpdw(overlay_base + 0x0014);
+				curr &= 0x4;
+				outpdw(overlay_base + 0x0014, curr | 0x012);
+				/* rgb->yuv */
+				outpdw(overlay_base + 0x0200, 0x05);
+			}
+		}
+	} else {
+		data = pipe->src_height;
+		data <<= 16;
+		data |= pipe->src_width;
+		outpdw(overlay_base + 0x0008, data); /* ROI, height + width */
+		outpdw(overlay_base + 0x000c, pipe->srcp0_addr);
+		outpdw(overlay_base + 0x0010, pipe->srcp0_ystride);
+		outpdw(overlay_base + 0x0004, 0x01); /* directout */
+	}
+
+	if (pipe->mixer_num == MDP4_MIXER1) {
+		if (intf == TV_INTF) {
+			curr = inpdw(overlay_base + 0x0014);
+			curr &= 0x4;
+			outpdw(overlay_base + 0x0014, 0x02); /* yuv422 */
+			/* overlay1 CSC config */
+			outpdw(overlay_base + 0x0200, 0x05); /* rgb->yuv */
+		}
+	}
+
+#ifdef MDP4_IGC_LUT_ENABLE
+	curr = inpdw(overlay_base + 0x0014);
+	curr &= ~0x4;
+	outpdw(overlay_base + 0x0014, curr | 0x4);	/* GC_LUT_EN, 888 */
+#endif
+
+	if (mdp_is_in_isr == FALSE)
+		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+int mdp4_overlay_pipe_staged(int mixer)
+{
+	uint32 data, mask, i, off;
+	int p1, p2;
+
+	if (mixer == MDP4_MIXER2)
+		off = 0x100F0;
+	else
+		off = 0x10100;
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	data = inpdw(MDP_BASE + off);
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+	p1 = 0;
+	p2 = 0;
+	for (i = 0; i < 8; i++) {
+		mask = data & 0x0f;
+		if (mask) {
+			if (mask <= 4)
+				p1++;
+			else
+				p2++;
+		}
+		data >>= 4;
+	}
+
+	if (mixer)
+		return p2;
+	else
+		return p1;
+}
+
+int mdp4_mixer_info(int mixer_num, struct mdp_mixer_info *info)
+{
+
+	int ndx, cnt;
+	struct mdp4_overlay_pipe *pipe;
+
+	if (mixer_num > MDP4_MIXER_MAX)
+		return -ENODEV;
+
+	cnt = 0;
+	ndx = 1; /* ndx 0 if not used */
+
+	for ( ; ndx < MDP4_MIXER_STAGE_MAX; ndx++) {
+		pipe = ctrl->stage[mixer_num][ndx];
+		if (pipe == NULL)
+			continue;
+		info->z_order = pipe->mixer_stage - MDP4_MIXER_STAGE0;
+		info->ptype = pipe->pipe_type;
+		info->pnum = pipe->pipe_num;
+		info->pndx = pipe->pipe_ndx;
+		info->mixer_num = pipe->mixer_num;
+		info++;
+		cnt++;
+	}
+	return cnt;
+}
+
+static void mdp4_overlay_bg_solidfill_clear(uint32 mixer_num)
+{
+	struct mdp4_overlay_pipe *bg_pipe;
+	unsigned char *rgb_base;
+	uint32 rgb_src_format;
+	int pnum;
+
+	bg_pipe = mdp4_overlay_stage_pipe(mixer_num,
+		MDP4_MIXER_STAGE_BASE);
+	if (bg_pipe && bg_pipe->pipe_type == OVERLAY_TYPE_BF) {
+		bg_pipe = mdp4_overlay_stage_pipe(mixer_num,
+				MDP4_MIXER_STAGE0);
+	}
+
+	if (bg_pipe && bg_pipe->pipe_type == OVERLAY_TYPE_RGB) {
+		rgb_src_format = mdp4_overlay_format(bg_pipe);
+		if (!(rgb_src_format & MDP4_FORMAT_SOLID_FILL)) {
+			pnum = bg_pipe->pipe_num - OVERLAY_PIPE_RGB1;
+			rgb_base = MDP_BASE + MDP4_RGB_BASE;
+			rgb_base += MDP4_RGB_OFF * pnum;
+			outpdw(rgb_base + 0x50, rgb_src_format);
+			outpdw(rgb_base + 0x0058, bg_pipe->op_mode);
+			mdp4_overlay_reg_flush(bg_pipe, 0);
+		}
+	}
+}
+
+void mdp4_mixer_pipe_cleanup(int mixer)
+{
+	struct mdp4_overlay_pipe *pipe;
+	int j;
+
+	for (j = MDP4_MIXER_STAGE_MAX - 1; j > MDP4_MIXER_STAGE_BASE; j--) {
+		pipe = ctrl->stage[mixer][j];
+		if (pipe == NULL)
+			continue;
+		pr_debug("%s(): pipe %u\n", __func__, pipe->pipe_ndx);
+		mdp4_mixer_stage_down(pipe);
+		mdp4_overlay_pipe_free(pipe);
+	}
+}
+
+static void mdp4_mixer_stage_commit(int mixer)
+{
+	struct mdp4_overlay_pipe *pipe;
+	int i, j, off;
+	u32 data = 0, stage, flush_bits = 0, pipe_cnt = 0, pull_mode = 0;
+	u32 cfg[MDP4_MIXER_MAX];
+
+	if (mixer == MDP4_MIXER0)
+		flush_bits |= 0x1;
+	else if (mixer == MDP4_MIXER1)
+		flush_bits |= 0x2;
+
+	for (i = MDP4_MIXER0; i < MDP4_MIXER_MAX; i++) {
+		cfg[i] = 0;
+		for (j = MDP4_MIXER_STAGE_BASE; j < MDP4_MIXER_STAGE_MAX; j++) {
+			pipe = ctrl->stage[i][j];
+			if (pipe == NULL)
+				break;
+			stage = pipe->mixer_stage;
+			if (i >= MDP4_MIXER1)
+				stage += 8;
+			stage <<= (4 * pipe->pipe_num);
+			cfg[i] |= stage;
+			pipe_cnt++;
+
+			mdp4_mixer_blend_setup(pipe);
+		}
+	}
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	if (ctrl->mixer_cfg[mixer] != cfg[mixer]) {
+		if ((ctrl->mixer_cfg[MDP4_MIXER0] != cfg[MDP4_MIXER0]) ||
+		    (ctrl->mixer_cfg[MDP4_MIXER1] != cfg[MDP4_MIXER1])) {
+			off = 0x10100;
+			if (ctrl->mixer_cfg[MDP4_MIXER0] != cfg[MDP4_MIXER0]) {
+				flush_bits |= 0x1;
+				ctrl->mixer_cfg[MDP4_MIXER0] = cfg[MDP4_MIXER0];
+
+				if ((ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO) ||
+				    (ctrl->panel_mode & MDP4_PANEL_LCDC))
+					pull_mode = 1;
+			}
+			if (ctrl->mixer_cfg[MDP4_MIXER1] != cfg[MDP4_MIXER1]) {
+				flush_bits |= 0x2;
+				ctrl->mixer_cfg[MDP4_MIXER1] = cfg[MDP4_MIXER1];
+
+				pull_mode = 1;
+			}
+
+			data = cfg[MDP4_MIXER0] | cfg[MDP4_MIXER1];
+
+			pr_debug("%s: mixer=%d data=%x flush=%x\n", __func__,
+			       mixer, data, flush_bits);
+
+			outpdw(MDP_BASE + off, data); /* LAYERMIXER_IN_CFG */
+			if (pull_mode) {
+				outpdw(MDP_BASE + 0x18000, flush_bits);
+			/* wait for vsync on both pull mode interfaces */
+				msleep(20);
+			}
+		}
+
+		if (ctrl->mixer_cfg[MDP4_MIXER2] != cfg[MDP4_MIXER2]) {
+			off = 0x100F0;
+			ctrl->mixer_cfg[MDP4_MIXER2] = cfg[MDP4_MIXER2];
+			data = cfg[MDP4_MIXER2];
+
+			pr_debug("%s: mixer=%d data=%x\n", __func__,
+			       mixer, data);
+
+			outpdw(MDP_BASE + off, data); /* LAYERMIXER_IN_CFG */
+		}
+	} else {
+		if (mixer == MDP4_MIXER0) {
+			if ((ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO) ||
+			(ctrl->panel_mode & MDP4_PANEL_LCDC))
+				pull_mode = 1;
+		} else if (mixer == MDP4_MIXER1) {
+			pull_mode = 1;
+		}
+
+		if (pull_mode)
+			outpdw(MDP_BASE + 0x18000, flush_bits);
+	}
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	if (data && pipe_cnt == 1)
+		mdp4_update_perf_level(OVERLAY_PERF_LEVEL4);
+}
+
+void mdp4_mixer_stage_up(struct mdp4_overlay_pipe *pipe)
+{
+	struct mdp4_overlay_pipe *spipe;
+	int mixer;
+
+	mixer = pipe->mixer_num;
+
+	spipe = ctrl->stage[mixer][pipe->mixer_stage];
+	if ((spipe != NULL) && (spipe->pipe_num != pipe->pipe_num)) {
+		mdp4_stat.err_stage++;
+		return;
+	}
+
+	ctrl->stage[mixer][pipe->mixer_stage] = pipe;	/* keep it */
+
+	if (!(pipe->flags & MDP_OV_PLAY_NOWAIT))
+		mdp4_mixer_stage_commit(mixer);
+}
+
+void mdp4_mixer_stage_down(struct mdp4_overlay_pipe *pipe)
+{
+	struct mdp4_overlay_pipe *spipe;
+
+	spipe = ctrl->stage[pipe->mixer_num][pipe->mixer_stage];
+	if (spipe == NULL)	/* not running */
+		return;
+
+	ctrl->stage[pipe->mixer_num][pipe->mixer_stage] = NULL;	/* clear it */
+
+	mdp4_mixer_stage_commit(pipe->mixer_num);
+}
+
+void mdp4_mixer_blend_setup(struct mdp4_overlay_pipe *pipe)
+{
+	struct mdp4_overlay_pipe *bg_pipe;
+	unsigned char *overlay_base, *rgb_base;
+	uint32 c0, c1, c2, blend_op, constant_color = 0, rgb_src_format;
+	uint32 fg_color3_out, fg_alpha = 0, bg_alpha = 0;
+	int off, pnum;
+
+	if (pipe->mixer_stage == MDP4_MIXER_STAGE_BASE)
+		return;
+
+	/* mixer numer, /dev/fb0, /dev/fb1, /dev/fb2 */
+	if (pipe->mixer_num == MDP4_MIXER2)
+		overlay_base = MDP_BASE + MDP4_OVERLAYPROC2_BASE;/* 0x88000 */
+	else if (pipe->mixer_num == MDP4_MIXER1)
+		overlay_base = MDP_BASE + MDP4_OVERLAYPROC1_BASE;/* 0x18000 */
+	else
+		overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */
+
+	/* stage 0 to stage 2 */
+	off = 0x20 * (pipe->mixer_stage - MDP4_MIXER_STAGE0);
+
+	bg_pipe = mdp4_overlay_stage_pipe(pipe->mixer_num,
+					  MDP4_MIXER_STAGE_BASE);
+	if (bg_pipe == NULL) {
+		pr_err("%s: Error: no bg_pipe\n", __func__);
+		return;
+	}
+
+	if (bg_pipe->pipe_type == OVERLAY_TYPE_BF &&
+	    pipe->mixer_stage > MDP4_MIXER_STAGE0) {
+		bg_pipe = mdp4_overlay_stage_pipe(pipe->mixer_num,
+						  MDP4_MIXER_STAGE0);
+	}
+
+	if (pipe->alpha_enable) {
+		/* alpha channel is lost on VG pipe when downscaling */
+		if (pipe->pipe_type == OVERLAY_TYPE_VIDEO &&
+		    (pipe->dst_w < pipe->src_w || pipe->dst_h < pipe->src_h))
+			fg_alpha = 0;
+		else
+			fg_alpha = 1;
+	}
+
+	if (!fg_alpha && bg_pipe && bg_pipe->alpha_enable) {
+		struct mdp4_overlay_pipe *tmp;
+		int stage;
+
+		bg_alpha = 1;
+		/* check all bg layers are opaque to propagate bg alpha */
+		stage = bg_pipe->mixer_stage + 1;
+		for (; stage < pipe->mixer_stage; stage++) {
+			tmp = mdp4_overlay_stage_pipe(pipe->mixer_num, stage);
+			if (!tmp || tmp->alpha_enable || tmp->is_fg) {
+				bg_alpha = 0;
+				break;
+			}
+		}
+	}
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	blend_op = (MDP4_BLEND_FG_ALPHA_FG_CONST |
+		    MDP4_BLEND_BG_ALPHA_BG_CONST);
+	outpdw(overlay_base + off + 0x108, pipe->alpha);
+	outpdw(overlay_base + off + 0x10c, 0xff - pipe->alpha);
+	fg_color3_out = 1; /* keep fg alpha by default */
+
+	if (pipe->is_fg) {
+		if (pipe->alpha == 0xff &&
+			bg_pipe->pipe_type == OVERLAY_TYPE_RGB) {
+			u32 op_mode;
+			pnum = bg_pipe->pipe_num - OVERLAY_PIPE_RGB1;
+			rgb_base = MDP_BASE + MDP4_RGB_BASE;
+			rgb_base += MDP4_RGB_OFF * pnum;
+			rgb_src_format = inpdw(rgb_base + 0x50);
+			rgb_src_format |= MDP4_FORMAT_SOLID_FILL;
+			/*
+			 * If solid fill is enabled, flip and scale
+			 * have to be disabled. otherwise, h/w
+			 * underruns. Also flush the pipe inorder
+			 * to take solid fill into effect.
+			 */
+			op_mode = inpdw(rgb_base + 0x0058);
+			op_mode &= ~(MDP4_OP_FLIP_LR + MDP4_OP_SCALEX_EN);
+			op_mode &= ~(MDP4_OP_FLIP_UD + MDP4_OP_SCALEY_EN);
+			outpdw(rgb_base + 0x0058, op_mode);
+			outpdw(rgb_base + 0x50, rgb_src_format);
+			outpdw(rgb_base + 0x1008, constant_color);
+			mdp4_overlay_reg_flush(bg_pipe, 0);
+		}
+	} else if (fg_alpha) {
+		blend_op = (MDP4_BLEND_BG_ALPHA_FG_PIXEL |
+			    MDP4_BLEND_BG_INV_ALPHA);
+		fg_color3_out = 1; /* keep fg alpha */
+	} else if (bg_alpha) {
+		blend_op = (MDP4_BLEND_BG_ALPHA_BG_PIXEL |
+			    MDP4_BLEND_FG_ALPHA_BG_PIXEL |
+			    MDP4_BLEND_FG_INV_ALPHA);
+		fg_color3_out = 0; /* keep bg alpha */
+	}
+
+	if (pipe->transp != MDP_TRANSP_NOP) {
+		if (pipe->is_fg) {
+			transp_color_key(pipe->src_format, pipe->transp,
+					&c0, &c1, &c2);
+			/* Fg blocked */
+			blend_op |= MDP4_BLEND_FG_TRANSP_EN;
+			/* lower limit */
+			outpdw(overlay_base + off + 0x110,
+					(c1 << 16 | c0));/* low */
+			outpdw(overlay_base + off + 0x114, c2);/* low */
+			/* upper limit */
+			outpdw(overlay_base + off + 0x118,
+					(c1 << 16 | c0));
+			outpdw(overlay_base + off + 0x11c, c2);
+		} else if (bg_pipe) {
+			transp_color_key(bg_pipe->src_format,
+				pipe->transp, &c0, &c1, &c2);
+			/* bg blocked */
+			blend_op |= MDP4_BLEND_BG_TRANSP_EN;
+			/* lower limit */
+			outpdw(overlay_base + 0x180,
+					(c1 << 16 | c0));/* low */
+			outpdw(overlay_base + 0x184, c2);/* low */
+			/* upper limit */
+			outpdw(overlay_base + 0x188,
+					(c1 << 16 | c0));/* high */
+			outpdw(overlay_base + 0x18c, c2);/* high */
+		}
+	}
+
+	outpdw(overlay_base + off + 0x104, blend_op);
+	outpdw(overlay_base + (off << 5) + 0x1004, fg_color3_out);
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+void mdp4_overlay_reg_flush(struct mdp4_overlay_pipe *pipe, int all)
+{
+	struct mdp4_overlay_pipe *bg_pipe;
+	uint32 bits = 0;
+
+	if (all) {
+		if (pipe->mixer_num == MDP4_MIXER1)
+			bits |= 0x02;
+		else
+			bits |= 0x01;
+	}
+
+	if (pipe->pipe_num <= OVERLAY_PIPE_RGB2)
+		bits |= 1 << (2 + pipe->pipe_num);
+	if (pipe->is_fg && pipe->alpha == 0xFF) {
+		bg_pipe = mdp4_overlay_stage_pipe(pipe->mixer_num,
+						  MDP4_MIXER_STAGE_BASE);
+		bits |= 1 << (2 + bg_pipe->pipe_num);
+	}
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	outpdw(MDP_BASE + 0x18000, bits);	/* MDP_OVERLAY_REG_FLUSH */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+struct mdp4_overlay_pipe *mdp4_overlay_stage_pipe(int mixer, int stage)
+{
+	return ctrl->stage[mixer][stage];
+}
+
+struct mdp4_overlay_pipe *mdp4_overlay_ndx2pipe(int ndx)
+{
+	struct mdp4_overlay_pipe *pipe;
+
+	if (ndx <= 0 || ndx > OVERLAY_PIPE_MAX)
+		return NULL;
+
+	pipe = &ctrl->plist[ndx - 1];	/* ndx start from 1 */
+
+	if (pipe->pipe_used == 0)
+		return NULL;
+
+	return pipe;
+}
+
+struct mdp4_overlay_pipe *mdp4_overlay_pipe_alloc(int ptype, int mixer)
+{
+	int i;
+	struct mdp4_overlay_pipe *pipe;
+
+	for (i = 0; i < OVERLAY_PIPE_MAX; i++) {
+		pipe = &ctrl->plist[i];
+		if ((pipe->pipe_used == 0) && ((pipe->pipe_type == ptype) ||
+		    (ptype == OVERLAY_TYPE_RGB &&
+		     pipe->pipe_type == OVERLAY_TYPE_VIDEO))) {
+			if (ptype == OVERLAY_TYPE_BF &&
+			    mixer != pipe->mixer_num)
+				continue;
+			init_completion(&pipe->comp);
+			init_completion(&pipe->dmas_comp);
+			pr_debug("%s: pipe=%x ndx=%d num=%d\n", __func__,
+				(int)pipe, pipe->pipe_ndx, pipe->pipe_num);
+			return pipe;
+		}
+	}
+
+	pr_err("%s: ptype=%d FAILED\n", __func__, ptype);
+
+	return NULL;
+}
+
+
+void mdp4_overlay_pipe_free(struct mdp4_overlay_pipe *pipe)
+{
+	uint32 ptype, num, ndx, mixer;
+	struct mdp4_iommu_pipe_info *iom_pipe_info;
+
+	pr_debug("%s: pipe=%x ndx=%d\n", __func__, (int)pipe, pipe->pipe_ndx);
+
+	ptype = pipe->pipe_type;
+	num = pipe->pipe_num;
+	ndx = pipe->pipe_ndx;
+	mixer = pipe->mixer_num;
+	iom_pipe_info = &mdp_iommu[pipe->mixer_num][pipe->pipe_ndx - 1];
+	iom_pipe_info->mark_unmap = 1;
+
+	memset(pipe, 0, sizeof(*pipe));
+
+	pipe->pipe_type = ptype;
+	pipe->pipe_num = num;
+	pipe->pipe_ndx = ndx;
+	pipe->mixer_num = mixer;
+}
+
+static int mdp4_overlay_validate_downscale(struct mdp_overlay *req,
+	struct msm_fb_data_type *mfd, uint32 perf_level, uint32 pclk_rate)
+{
+	__u32 panel_clk_khz, mdp_clk_khz;
+	__u32 num_hsync_pix_clks, mdp_clks_per_hsync, src_wh;
+	__u32 hsync_period_ps, mdp_period_ps, total_hsync_period_ps;
+	unsigned long fill_rate_y_dir, fill_rate_x_dir;
+	unsigned long fillratex100, mdp_pixels_produced;
+	unsigned long mdp_clk_hz;
+
+	pr_debug("%s: LCDC Mode Downscale validation with MDP Core"
+		" Clk rate\n", __func__);
+	pr_debug("src_w %u, src_h %u, dst_w %u, dst_h %u\n",
+		req->src_rect.w, req->src_rect.h, req->dst_rect.w,
+		req->dst_rect.h);
+
+
+	panel_clk_khz = pclk_rate/1000;
+	mdp_clk_hz = mdp_perf_level2clk_rate(perf_level);
+
+	if (!mdp_clk_hz || !req->dst_rect.w || !req->dst_rect.h) {
+		pr_debug("mdp_perf_level2clk_rate returned 0,"
+			 "or dst_rect height/width is 0,"
+			 "Downscale Validation incomplete\n");
+		return 0;
+	}
+
+	mdp_clk_khz = mdp_clk_hz/1000;
+
+	num_hsync_pix_clks = mfd->panel_info.lcdc.h_back_porch +
+		mfd->panel_info.lcdc.h_front_porch +
+		mfd->panel_info.lcdc.h_pulse_width +
+		mfd->panel_info.xres;
+
+	hsync_period_ps = 1000000000/panel_clk_khz;
+	mdp_period_ps = 1000000000/mdp_clk_khz;
+
+	total_hsync_period_ps = num_hsync_pix_clks * hsync_period_ps;
+	mdp_clks_per_hsync = total_hsync_period_ps/mdp_period_ps;
+
+	pr_debug("hsync_period_ps %u, mdp_period_ps %u,"
+		"total_hsync_period_ps %u\n", hsync_period_ps,
+		mdp_period_ps, total_hsync_period_ps);
+
+	src_wh = req->src_rect.w * req->src_rect.h;
+	if (src_wh % req->dst_rect.h)
+		fill_rate_y_dir = (src_wh / req->dst_rect.h) + 1;
+	else
+		fill_rate_y_dir = (src_wh / req->dst_rect.h);
+
+	fill_rate_x_dir = (mfd->panel_info.xres - req->dst_rect.w)
+		+ req->src_rect.w;
+
+	if (fill_rate_y_dir >= fill_rate_x_dir)
+		fillratex100 = 100 * fill_rate_y_dir / mfd->panel_info.xres;
+	else
+		fillratex100 = 100 * fill_rate_x_dir / mfd->panel_info.xres;
+
+	pr_debug("mdp_clks_per_hsync %u, fill_rate_y_dir %lu,"
+		"fill_rate_x_dir %lu\n", mdp_clks_per_hsync,
+		fill_rate_y_dir, fill_rate_x_dir);
+
+	mdp_pixels_produced = 100 * mdp_clks_per_hsync/fillratex100;
+	pr_debug("fillratex100 %lu, mdp_pixels_produced %lu\n",
+		fillratex100, mdp_pixels_produced);
+	if (mdp_pixels_produced <= mfd->panel_info.xres) {
+		mdp4_stat.err_underflow++;
+		return -ERANGE;
+	}
+
+	return 0;
+}
+
+static int mdp4_overlay_req2pipe(struct mdp_overlay *req, int mixer,
+			struct mdp4_overlay_pipe **ppipe,
+			struct msm_fb_data_type *mfd)
+{
+	struct mdp4_overlay_pipe *pipe;
+	struct mdp4_iommu_pipe_info *iom_pipe_info;
+	int ret, ptype;
+
+	u32 upscale_max;
+	upscale_max = (mdp_rev >= MDP_REV_41) ?
+		MDP4_REV41_OR_LATER_UP_SCALING_MAX :
+		MDP4_REV40_UP_SCALING_MAX;
+
+	if (mfd == NULL) {
+		pr_err("%s: mfd == NULL, -ENODEV\n", __func__);
+		return -ENODEV;
+	}
+
+	if (mixer >= MDP4_MIXER_MAX) {
+		pr_err("%s: mixer out of range!\n", __func__);
+		mdp4_stat.err_mixer++;
+		return -ERANGE;
+	}
+
+	if (req->z_order < 0 || req->z_order > 2) {
+		pr_err("%s: z_order=%d out of range!\n", __func__,
+				req->z_order);
+		mdp4_stat.err_zorder++;
+		return -ERANGE;
+	}
+
+	if (req->src_rect.h == 0 || req->src_rect.w == 0) {
+		pr_err("%s: src img of zero size!\n", __func__);
+		mdp4_stat.err_size++;
+		return -EINVAL;
+	}
+
+	if (req->dst_rect.h > (req->src_rect.h * upscale_max)) {
+		mdp4_stat.err_scale++;
+		pr_err("%s: scale up, too much (h)!\n", __func__);
+		return -ERANGE;
+	}
+
+	if (req->src_rect.h > (req->dst_rect.h * 8)) {	/* too little */
+		mdp4_stat.err_scale++;
+		pr_err("%s: scale down, too little (h)!\n", __func__);
+		return -ERANGE;
+	}
+
+	if (req->dst_rect.w > (req->src_rect.w * upscale_max)) {
+		mdp4_stat.err_scale++;
+		pr_err("%s: scale up, too much (w)!\n", __func__);
+		return -ERANGE;
+	}
+
+	if (req->src_rect.w > (req->dst_rect.w * 8)) {	/* too little */
+		mdp4_stat.err_scale++;
+		pr_err("%s: scale down, too little (w)!\n", __func__);
+		return -ERANGE;
+	}
+
+	if (mdp_hw_revision == MDP4_REVISION_V1) {
+		/*  non integer down saceling ratio  smaller than 1/4
+		 *  is not supportted
+		 */
+		if (req->src_rect.h > (req->dst_rect.h * 4)) {
+			if (req->src_rect.h % req->dst_rect.h) {
+				mdp4_stat.err_scale++;
+				pr_err("%s: need integer (h)!\n", __func__);
+				return -ERANGE;
+			}
+		}
+
+		if (req->src_rect.w > (req->dst_rect.w * 4)) {
+			if (req->src_rect.w % req->dst_rect.w) {
+				mdp4_stat.err_scale++;
+				pr_err("%s: need integer (w)!\n", __func__);
+				return -ERANGE;
+			}
+		}
+	}
+
+	if (((req->src_rect.x + req->src_rect.w) > req->src.width) ||
+		((req->src_rect.y + req->src_rect.h) > req->src.height)) {
+		mdp4_stat.err_size++;
+		pr_err("%s invalid src rectangle\n", __func__);
+		return -ERANGE;
+	}
+
+	if (ctrl->panel_3d != MDP4_3D_SIDE_BY_SIDE) {
+		int xres;
+		int yres;
+
+		xres = mfd->panel_info.xres;
+		yres = mfd->panel_info.yres;
+
+		if (((req->dst_rect.x + req->dst_rect.w) > xres) ||
+			((req->dst_rect.y + req->dst_rect.h) > yres)) {
+			mdp4_stat.err_size++;
+			pr_err("%s invalid dst rectangle\n", __func__);
+			return -ERANGE;
+		}
+	}
+
+	ptype = mdp4_overlay_format2type(req->src.format);
+	if (ptype < 0) {
+		pr_err("%s: mdp4_overlay_format2type!\n", __func__);
+		return ptype;
+	}
+
+	if (req->flags & MDP_OV_PIPE_SHARE)
+		ptype = OVERLAY_TYPE_VIDEO; /* VG pipe supports both RGB+YUV */
+
+	if (req->id == MSMFB_NEW_REQUEST)  /* new request */
+		pipe = mdp4_overlay_pipe_alloc(ptype, mixer);
+	else
+		pipe = mdp4_overlay_ndx2pipe(req->id);
+
+	if (pipe == NULL) {
+		pr_err("%s: pipe == NULL!\n", __func__);
+		return -ENOMEM;
+	}
+
+	if (!display_iclient && !IS_ERR_OR_NULL(mfd->iclient)) {
+		display_iclient = mfd->iclient;
+		pr_debug("%s(): display_iclient %p\n", __func__,
+			display_iclient);
+	}
+
+	iom_pipe_info = &mdp_iommu[pipe->mixer_num][pipe->pipe_ndx - 1];
+	iom_pipe_info->mark_unmap = 0;
+
+	pipe->src_format = req->src.format;
+	ret = mdp4_overlay_format2pipe(pipe);
+
+	if (ret < 0) {
+		pr_err("%s: mdp4_overlay_format2pipe!\n", __func__);
+		return ret;
+	}
+
+	/*
+	 * base layer == 1, reserved for frame buffer
+	 * zorder 0 == stage 0 == 2
+	 * zorder 1 == stage 1 == 3
+	 * zorder 2 == stage 2 == 4
+	 */
+	if (req->id == MSMFB_NEW_REQUEST) {  /* new request */
+		pipe->pipe_used++;
+		pipe->mixer_num = mixer;
+		pipe->mixer_stage = req->z_order + MDP4_MIXER_STAGE0;
+		pr_debug("%s: zorder=%d pipe ndx=%d num=%d\n", __func__,
+			req->z_order, pipe->pipe_ndx, pipe->pipe_num);
+
+	}
+
+	pipe->src_width = req->src.width & 0x1fff;	/* source img width */
+	pipe->src_height = req->src.height & 0x1fff;	/* source img height */
+	pipe->src_h = req->src_rect.h & 0x07ff;
+	pipe->src_w = req->src_rect.w & 0x07ff;
+	pipe->src_y = req->src_rect.y & 0x07ff;
+	pipe->src_x = req->src_rect.x & 0x07ff;
+	pipe->dst_h = req->dst_rect.h & 0x07ff;
+	pipe->dst_w = req->dst_rect.w & 0x07ff;
+	pipe->dst_y = req->dst_rect.y & 0x07ff;
+	pipe->dst_x = req->dst_rect.x & 0x07ff;
+
+	pipe->op_mode = 0;
+
+	if (req->flags & MDP_FLIP_LR)
+		pipe->op_mode |= MDP4_OP_FLIP_LR;
+
+	if (req->flags & MDP_FLIP_UD)
+		pipe->op_mode |= MDP4_OP_FLIP_UD;
+
+	if (req->flags & MDP_DITHER)
+		pipe->op_mode |= MDP4_OP_DITHER_EN;
+
+	if (req->flags & MDP_DEINTERLACE)
+		pipe->op_mode |= MDP4_OP_DEINT_EN;
+
+	if (req->flags & MDP_DEINTERLACE_ODD)
+		pipe->op_mode |= MDP4_OP_DEINT_ODD_REF;
+
+	pipe->is_fg = req->is_fg;/* control alpha and color key */
+
+	pipe->alpha = req->alpha & 0x0ff;
+
+	pipe->transp = req->transp_mask;
+
+	*ppipe = pipe;
+
+	return 0;
+}
+
+static int get_img(struct msmfb_data *img, struct fb_info *info,
+	struct mdp4_overlay_pipe *pipe, unsigned int plane,
+	unsigned long *start, unsigned long *len, struct file **srcp_file,
+	int *p_need, struct ion_handle **srcp_ihdl)
+{
+	struct file *file;
+	int put_needed, ret = 0, fb_num;
+#ifdef CONFIG_ANDROID_PMEM
+	unsigned long vstart;
+#endif
+	*p_need = 0;
+
+	if (img->flags & MDP_BLIT_SRC_GEM) {
+		*srcp_file = NULL;
+		return kgsl_gem_obj_addr(img->memory_id, (int) img->priv,
+					 start, len);
+	}
+
+	if (img->flags & MDP_MEMORY_ID_TYPE_FB) {
+		file = fget_light(img->memory_id, &put_needed);
+		if (file == NULL)
+			return -EINVAL;
+
+		if (MAJOR(file->f_dentry->d_inode->i_rdev) == FB_MAJOR) {
+			fb_num = MINOR(file->f_dentry->d_inode->i_rdev);
+			if (get_fb_phys_info(start, len, fb_num,
+				DISPLAY_SUBSYSTEM_ID)) {
+				ret = -1;
+			} else {
+				*srcp_file = file;
+				*p_need = put_needed;
+			}
+		} else
+			ret = -1;
+		if (ret)
+			fput_light(file, put_needed);
+		return ret;
+	}
+
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+	return mdp4_overlay_iommu_map_buf(img->memory_id, pipe, plane,
+		start, len, srcp_ihdl);
+#endif
+#ifdef CONFIG_ANDROID_PMEM
+	if (!get_pmem_file(img->memory_id, start, &vstart,
+					    len, srcp_file))
+		return 0;
+	else
+		return -EINVAL;
+#endif
+}
+
+#ifdef CONFIG_FB_MSM_MIPI_DSI
+int mdp4_overlay_3d_sbys(struct fb_info *info, struct msmfb_overlay_3d *req)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	int ret = -EPERM;
+
+	if (mutex_lock_interruptible(&mfd->dma->ov_mutex))
+		return -EINTR;
+
+	if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) {
+		mdp4_dsi_cmd_3d_sbys(mfd, req);
+		ret = 0;
+	} else if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO) {
+		mdp4_dsi_video_3d_sbys(mfd, req);
+		ret = 0;
+	}
+	mutex_unlock(&mfd->dma->ov_mutex);
+
+	return ret;
+}
+#else
+int mdp4_overlay_3d_sbys(struct fb_info *info, struct msmfb_overlay_3d *req)
+{
+	/* do nothing */
+	return -EPERM;
+}
+#endif
+
+int mdp4_overlay_blt(struct fb_info *info, struct msmfb_overlay_blt *req)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+
+	if (mfd == NULL)
+		return -ENODEV;
+
+	if (mutex_lock_interruptible(&mfd->dma->ov_mutex))
+		return -EINTR;
+
+	if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD)
+		mdp4_dsi_overlay_blt(mfd, req);
+	else if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO)
+		mdp4_dsi_video_overlay_blt(mfd, req);
+	else if (ctrl->panel_mode & MDP4_PANEL_LCDC)
+		mdp4_lcdc_overlay_blt(mfd, req);
+
+	mutex_unlock(&mfd->dma->ov_mutex);
+
+	return 0;
+}
+
+int mdp4_overlay_blt_offset(struct fb_info *info, struct msmfb_overlay_blt *req)
+{
+	int ret = 0;
+
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+
+	if (mutex_lock_interruptible(&mfd->dma->ov_mutex))
+		return -EINTR;
+
+	if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD)
+		ret = mdp4_dsi_overlay_blt_offset(mfd, req);
+	else if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO)
+		ret = mdp4_dsi_video_overlay_blt_offset(mfd, req);
+	else if (ctrl->panel_mode & MDP4_PANEL_LCDC)
+		ret = mdp4_lcdc_overlay_blt_offset(mfd, req);
+
+	mutex_unlock(&mfd->dma->ov_mutex);
+
+	return ret;
+}
+
+int mdp4_overlay_get(struct fb_info *info, struct mdp_overlay *req)
+{
+	struct mdp4_overlay_pipe *pipe;
+
+	pipe = mdp4_overlay_ndx2pipe(req->id);
+	if (pipe == NULL)
+		return -ENODEV;
+
+	*req = pipe->req_data;
+
+	if (mdp4_overlay_borderfill_supported())
+		req->flags |= MDP_BORDERFILL_SUPPORTED;
+
+	return 0;
+}
+
+#define OVERLAY_VGA_SIZE	0x04B000
+#define OVERLAY_720P_TILE_SIZE  0x0E6000
+#define OVERLAY_WSVGA_SIZE 0x98000 /* 1024x608, align 600 to 32bit */
+
+#define OVERLAY_BUS_SCALE_TABLE_BASE	6
+
+static int mdp4_overlay_is_rgb_type(int format)
+{
+	switch (format) {
+	case MDP_RGB_565:
+	case MDP_RGB_888:
+	case MDP_BGR_565:
+	case MDP_XRGB_8888:
+	case MDP_ARGB_8888:
+	case MDP_RGBA_8888:
+	case MDP_BGRA_8888:
+	case MDP_RGBX_8888:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+static uint32 mdp4_overlay_get_perf_level(struct mdp_overlay *req,
+					  struct msm_fb_data_type *mfd)
+{
+	int is_fg = 0, i, cnt;
+
+	if (req->is_fg && ((req->alpha & 0x0ff) == 0xff))
+		is_fg = 1;
+
+	if (mdp4_extn_disp)
+		return OVERLAY_PERF_LEVEL1;
+
+	if (req->flags & (MDP_DEINTERLACE | MDP_BACKEND_COMPOSITION))
+		return OVERLAY_PERF_LEVEL1;
+
+	for (i = 0, cnt = 0; i < OVERLAY_PIPE_MAX; i++) {
+		if (ctrl->plist[i].pipe_used && ++cnt > 2)
+			return OVERLAY_PERF_LEVEL1;
+	}
+
+	if (mdp4_overlay_is_rgb_type(req->src.format) && is_fg &&
+		((req->src.width * req->src.height) <= OVERLAY_WSVGA_SIZE))
+		return OVERLAY_PERF_LEVEL4;
+	else if (mdp4_overlay_is_rgb_type(req->src.format))
+		return OVERLAY_PERF_LEVEL1;
+
+	if (req->src.width*req->src.height <= OVERLAY_VGA_SIZE) {
+		if (mfd->mdp_rev >= MDP_REV_42)
+			return OVERLAY_PERF_LEVEL4;
+		else
+			return OVERLAY_PERF_LEVEL3;
+
+	} else if (req->src.width*req->src.height <= OVERLAY_720P_TILE_SIZE) {
+		u32 max, min;
+		max = (req->dst_rect.h > req->dst_rect.w) ?
+			req->dst_rect.h : req->dst_rect.w;
+		min = (mfd->panel_info.yres > mfd->panel_info.xres) ?
+			mfd->panel_info.xres : mfd->panel_info.yres;
+		if (max > min)	/* landscape mode */
+			return OVERLAY_PERF_LEVEL3;
+		else		/* potrait mode */
+			return OVERLAY_PERF_LEVEL2;
+	}
+	else
+		return OVERLAY_PERF_LEVEL1;
+}
+
+void mdp4_update_perf_level(u32 perf_level)
+{
+	new_perf_level = perf_level;
+}
+
+void mdp4_set_perf_level(void)
+{
+	static int old_perf_level;
+	int cur_perf_level;
+
+	if (mdp4_extn_disp)
+		cur_perf_level = OVERLAY_PERF_LEVEL1;
+	else
+		cur_perf_level = new_perf_level;
+
+	if (old_perf_level != cur_perf_level) {
+		mdp_set_core_clk(cur_perf_level);
+		old_perf_level = cur_perf_level;
+		mdp_bus_scale_update_request(OVERLAY_BUS_SCALE_TABLE_BASE
+					     - cur_perf_level);
+	}
+}
+
+static void mdp4_overlay_update_blt_mode(struct msm_fb_data_type *mfd)
+{
+	if (mfd->use_ov0_blt == mfd->ov0_blt_state)
+		return;
+
+	if (mfd->use_ov0_blt) {
+		if (mfd->panel_info.type == LCDC_PANEL ||
+		    mfd->panel_info.type == LVDS_PANEL)
+			mdp4_lcdc_overlay_blt_start(mfd);
+		else if (mfd->panel_info.type == MIPI_VIDEO_PANEL)
+			mdp4_dsi_video_blt_start(mfd);
+		else if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD)
+			mdp4_dsi_overlay_blt_start(mfd);
+	} else {
+		if (mfd->panel_info.type == LCDC_PANEL ||
+		    mfd->panel_info.type == LVDS_PANEL)
+			mdp4_lcdc_overlay_blt_stop(mfd);
+		else if (mfd->panel_info.type == MIPI_VIDEO_PANEL)
+			mdp4_dsi_video_blt_stop(mfd);
+		else if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD)
+			mdp4_dsi_overlay_blt_stop(mfd);
+	}
+	mfd->ov0_blt_state = mfd->use_ov0_blt;
+}
+
+static void mdp4_overlay1_update_blt_mode(struct msm_fb_data_type *mfd)
+{
+	if (mfd->ov1_blt_state == mfd->use_ov1_blt)
+		return;
+	if (mfd->use_ov1_blt) {
+		mdp4_allocate_writeback_buf(mfd, MDP4_MIXER1);
+		mdp4_dtv_overlay_blt_start(mfd);
+		pr_debug("%s overlay1 writeback is enabled\n", __func__);
+	} else {
+		mdp4_dtv_overlay_blt_stop(mfd);
+		pr_debug("%s overlay1 writeback is disabled\n", __func__);
+	}
+	mfd->ov1_blt_state = mfd->use_ov1_blt;
+}
+
+static u32 mdp4_overlay_blt_enable(struct mdp_overlay *req,
+	struct msm_fb_data_type *mfd, uint32 perf_level)
+{
+	u32 clk_rate = mfd->panel_info.clk_rate;
+	u32 pull_mode = 0, use_blt = 0;
+
+	if (mfd->panel_info.type == MIPI_VIDEO_PANEL)
+		clk_rate = (&mfd->panel_info.mipi)->dsi_pclk_rate;
+
+	if ((mfd->panel_info.type == LCDC_PANEL) ||
+	    (mfd->panel_info.type == MIPI_VIDEO_PANEL) ||
+	    (mfd->panel_info.type == DTV_PANEL))
+		pull_mode = 1;
+
+	if (pull_mode && (req->src_rect.h > req->dst_rect.h ||
+		req->src_rect.w > req->dst_rect.w)) {
+		if (mdp4_overlay_validate_downscale(req, mfd, perf_level,
+			clk_rate))
+			use_blt = 1;
+	}
+
+	if (mfd->mdp_rev == MDP_REV_41) {
+		/*
+		* writeback (blt) mode to provide work around for
+		* dsi cmd mode interface hardware bug.
+		*/
+		if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) {
+			if (req->dst_rect.x != 0)
+				use_blt = 1;
+		}
+		if ((mfd->panel_info.xres > 1280) &&
+		    (mfd->panel_info.type != DTV_PANEL))
+			use_blt = 1;
+	}
+	return use_blt;
+}
+
+int mdp4_overlay_set(struct fb_info *info, struct mdp_overlay *req)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	int ret, mixer, perf_level;
+	struct mdp4_overlay_pipe *pipe;
+
+	if (mfd == NULL) {
+		pr_err("%s: mfd == NULL, -ENODEV\n", __func__);
+		return -ENODEV;
+	}
+
+	if (info->node != 0 || mfd->cont_splash_done)	/* primary */
+		if (!mfd->panel_power_on)		/* suspended */
+			return -EPERM;
+
+	if (req->src.format == MDP_FB_FORMAT)
+		req->src.format = mfd->fb_imgType;
+
+	if (mutex_lock_interruptible(&mfd->dma->ov_mutex)) {
+		pr_err("%s: mutex_lock_interruptible, -EINTR\n", __func__);
+		return -EINTR;
+	}
+
+	mixer = mfd->panel_info.pdest;	/* DISPLAY_1 or DISPLAY_2 */
+
+	ret = mdp4_overlay_req2pipe(req, mixer, &pipe, mfd);
+
+	if (ret < 0) {
+		mutex_unlock(&mfd->dma->ov_mutex);
+		pr_err("%s: mdp4_overlay_req2pipe, ret=%d\n", __func__, ret);
+		return ret;
+	}
+
+	perf_level = mdp4_overlay_get_perf_level(req, mfd);
+
+	if (mixer == MDP4_MIXER0) {
+		u32 use_blt = mdp4_overlay_blt_enable(req, mfd,	perf_level);
+		mfd->use_ov0_blt &= ~(1 << (pipe->pipe_ndx-1));
+		mfd->use_ov0_blt |= (use_blt << (pipe->pipe_ndx-1));
+	}
+
+	/* return id back to user */
+	req->id = pipe->pipe_ndx;	/* pipe_ndx start from 1 */
+	pipe->req_data = *req;		/* keep original req */
+
+	pipe->flags = req->flags;
+
+	if (!IS_ERR_OR_NULL(mfd->iclient)) {
+		pr_debug("pipe->flags 0x%x\n", pipe->flags);
+		if (pipe->flags & MDP_SECURE_OVERLAY_SESSION) {
+			mfd->mem_hid &= ~BIT(ION_IOMMU_HEAP_ID);
+			mfd->mem_hid |= ION_SECURE;
+		} else {
+			mfd->mem_hid |= BIT(ION_IOMMU_HEAP_ID);
+			mfd->mem_hid &= ~ION_SECURE;
+		}
+	}
+
+	if (pipe->flags & MDP_SHARPENING) {
+		bool test = ((pipe->req_data.dpp.sharp_strength > 0) &&
+			((req->src_rect.w > req->dst_rect.w) &&
+			 (req->src_rect.h > req->dst_rect.h)));
+		if (test) {
+			pr_debug("%s: No sharpening while downscaling.\n",
+								__func__);
+			pipe->flags &= ~MDP_SHARPENING;
+		}
+	}
+
+	/* precompute HSIC matrices */
+	if (req->flags & MDP_DPP_HSIC)
+		mdp4_hsic_set(pipe, &(req->dpp));
+
+	mdp4_stat.overlay_set[pipe->mixer_num]++;
+
+	if (ctrl->panel_mode & MDP4_PANEL_DTV &&
+	    pipe->mixer_num == MDP4_MIXER1) {
+		u32 use_blt = mdp4_overlay_blt_enable(req, mfd, perf_level);
+
+		if (hdmi_prim_display) {
+			if (!mdp4_overlay_is_rgb_type(req->src.format) &&
+				pipe->pipe_type == OVERLAY_TYPE_VIDEO &&
+				(req->src_rect.h > req->dst_rect.h ||
+				req->src_rect.w > req->dst_rect.w))
+				use_blt = 1;
+		}
+
+		mdp4_overlay_dtv_set(mfd, pipe);
+		mfd->use_ov1_blt &= ~(1 << (pipe->pipe_ndx-1));
+		mfd->use_ov1_blt |= (use_blt << (pipe->pipe_ndx-1));
+	}
+
+	if (new_perf_level != perf_level) {
+		u32 old_level = new_perf_level;
+		mdp4_update_perf_level(perf_level);
+
+		/* change clck base on perf level */
+		if (pipe->mixer_num == MDP4_MIXER0) {
+			if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO) {
+				if (old_level > perf_level)
+					mdp4_set_perf_level();
+			} else if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) {
+				mdp4_dsi_cmd_dma_busy_wait(mfd);
+				mdp4_dsi_blt_dmap_busy_wait(mfd);
+				mdp4_set_perf_level();
+			} else if (ctrl->panel_mode & MDP4_PANEL_LCDC) {
+				if (old_level > perf_level)
+					mdp4_set_perf_level();
+			} else if (ctrl->panel_mode & MDP4_PANEL_MDDI) {
+				mdp4_mddi_dma_busy_wait(mfd);
+				mdp4_set_perf_level();
+			}
+		} else {
+			if (ctrl->panel_mode & MDP4_PANEL_DTV) {
+				mdp4_overlay_reg_flush(pipe, 0);
+				mdp4_overlay_dtv_ov_done_push(mfd, pipe);
+			}
+		}
+	}
+	mutex_unlock(&mfd->dma->ov_mutex);
+
+	return 0;
+}
+
+int mdp4_overlay_unset(struct fb_info *info, int ndx)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	struct mdp4_overlay_pipe *pipe;
+	struct dpp_ctrl dpp;
+	int i;
+
+	if (mfd == NULL)
+		return -ENODEV;
+
+	if (mutex_lock_interruptible(&mfd->dma->ov_mutex))
+		return -EINTR;
+
+	pipe = mdp4_overlay_ndx2pipe(ndx);
+
+	if (pipe == NULL) {
+		mutex_unlock(&mfd->dma->ov_mutex);
+		return -ENODEV;
+	}
+
+	if (pipe->mixer_num == MDP4_MIXER2)
+		ctrl->mixer2_played = 0;
+	else if (pipe->mixer_num == MDP4_MIXER1)
+		ctrl->mixer1_played = 0;
+	else {
+		/* mixer 0 */
+		ctrl->mixer0_played = 0;
+#ifdef CONFIG_FB_MSM_MIPI_DSI
+		if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) {
+			if (mfd->panel_power_on) {
+				mdp4_dsi_blt_dmap_busy_wait(mfd);
+			}
+		}
+#else
+		if (ctrl->panel_mode & MDP4_PANEL_MDDI) {
+			if (mfd->panel_power_on)
+				mdp4_mddi_dma_busy_wait(mfd);
+		}
+#endif
+	}
+
+	if (mfd->mdp_rev >= MDP_REV_41 &&
+		mdp4_overlay_is_rgb_type(pipe->src_format) &&
+		!mfd->use_ov0_blt && (pipe->mixer_num == MDP4_MIXER0 ||
+		hdmi_prim_display)) {
+			ctrl->stage[pipe->mixer_num][pipe->mixer_stage] = NULL;
+	} else {
+		if (pipe->is_fg &&
+			!mdp4_overlay_is_rgb_type(pipe->src_format)) {
+			mdp4_overlay_bg_solidfill_clear(pipe->mixer_num);
+			pipe->is_fg = 0;
+		}
+
+		mdp4_mixer_stage_down(pipe);
+
+		if (pipe->mixer_num == MDP4_MIXER0) {
+#ifdef CONFIG_FB_MSM_MIPI_DSI
+			if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) {
+				if (mfd->panel_power_on)
+					mdp4_dsi_cmd_overlay_restore();
+			} else if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO) {
+				pipe->flags &= ~MDP_OV_PLAY_NOWAIT;
+				if (mfd->panel_power_on)
+					mdp4_overlay_dsi_video_vsync_push(mfd,
+									  pipe);
+			}
+#else
+			if (ctrl->panel_mode & MDP4_PANEL_MDDI) {
+				if (mfd->panel_power_on)
+					mdp4_mddi_overlay_restore();
+			}
+#endif
+			else if (ctrl->panel_mode & MDP4_PANEL_LCDC) {
+				pipe->flags &= ~MDP_OV_PLAY_NOWAIT;
+				if (mfd->panel_power_on)
+					mdp4_overlay_lcdc_vsync_push(mfd, pipe);
+			}
+			mfd->use_ov0_blt &= ~(1 << (pipe->pipe_ndx-1));
+			mdp4_overlay_update_blt_mode(mfd);
+			if (!mfd->use_ov0_blt)
+				mdp4_free_writeback_buf(mfd, MDP4_MIXER0);
+		} else {	/* mixer1, DTV, ATV */
+			if (ctrl->panel_mode & MDP4_PANEL_DTV) {
+				mdp4_overlay_dtv_unset(mfd, pipe);
+				mfd->use_ov1_blt &= ~(1 << (pipe->pipe_ndx-1));
+				mdp4_overlay1_update_blt_mode(mfd);
+				if (!mfd->use_ov1_blt)
+					mdp4_free_writeback_buf(mfd,
+								MDP4_MIXER1);
+			}
+		}
+	}
+
+	/* Reset any HSIC settings to default */
+	if (pipe->flags & MDP_DPP_HSIC) {
+		for (i = 0; i < NUM_HSIC_PARAM; i++)
+			dpp.hsic_params[i] = 0;
+
+		mdp4_hsic_set(pipe, &dpp);
+		mdp4_hsic_update(pipe);
+	}
+
+	mdp4_stat.overlay_unset[pipe->mixer_num]++;
+
+	mdp4_overlay_pipe_free(pipe);
+
+	mutex_unlock(&mfd->dma->ov_mutex);
+
+	return 0;
+}
+
+
+struct tile_desc {
+	uint32 width;  /* tile's width */
+	uint32 height; /* tile's height */
+	uint32 row_tile_w; /* tiles per row's width */
+	uint32 row_tile_h; /* tiles per row's height */
+};
+
+void tile_samsung(struct tile_desc *tp)
+{
+	/*
+	 * each row of samsung tile consists of two tiles in height
+	 * and two tiles in width which means width should align to
+	 * 64 x 2 bytes and height should align to 32 x 2 bytes.
+	 * video decoder generate two tiles in width and one tile
+	 * in height which ends up height align to 32 X 1 bytes.
+	 */
+	tp->width = 64;		/* 64 bytes */
+	tp->row_tile_w = 2;	/* 2 tiles per row's width */
+	tp->height = 32;	/* 32 bytes */
+	tp->row_tile_h = 1;	/* 1 tiles per row's height */
+}
+
+uint32 tile_mem_size(struct mdp4_overlay_pipe *pipe, struct tile_desc *tp)
+{
+	uint32 tile_w, tile_h;
+	uint32 row_num_w, row_num_h;
+
+
+	tile_w = tp->width * tp->row_tile_w;
+	tile_h = tp->height * tp->row_tile_h;
+
+	row_num_w = (pipe->src_width + tile_w - 1) / tile_w;
+	row_num_h = (pipe->src_height + tile_h - 1) / tile_h;
+	return ((row_num_w * row_num_h * tile_w * tile_h) + 8191) & ~8191;
+}
+
+int mdp4_overlay_play_wait(struct fb_info *info, struct msmfb_overlay_data *req)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	struct mdp4_overlay_pipe *pipe;
+
+	if (mfd == NULL)
+		return -ENODEV;
+
+	if (!mfd->panel_power_on) /* suspended */
+		return -EPERM;
+
+	pipe = mdp4_overlay_ndx2pipe(req->id);
+
+	if (!pipe) {
+		mdp4_stat.err_play++;
+		return -ENODEV;
+	}
+
+	if (mutex_lock_interruptible(&mfd->dma->ov_mutex))
+		return -EINTR;
+
+	mdp4_mixer_stage_commit(pipe->mixer_num);
+
+	if (mfd->use_ov1_blt)
+		mdp4_overlay1_update_blt_mode(mfd);
+
+	mdp4_overlay_dtv_wait4vsync();
+	mdp4_iommu_unmap(pipe);
+
+	mutex_unlock(&mfd->dma->ov_mutex);
+	return 0;
+}
+
+int mdp4_overlay_play(struct fb_info *info, struct msmfb_overlay_data *req)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	struct msmfb_data *img;
+	struct mdp4_overlay_pipe *pipe;
+	ulong start, addr;
+	ulong len = 0;
+	struct file *srcp0_file = NULL;
+	struct file *srcp1_file = NULL, *srcp2_file = NULL;
+	struct ion_handle *srcp0_ihdl = NULL;
+	struct ion_handle *srcp1_ihdl = NULL, *srcp2_ihdl = NULL;
+	int ps0_need, p_need;
+	uint32_t overlay_version = 0;
+	int ret = 0;
+
+	if (mfd == NULL)
+		return -ENODEV;
+
+	if (!mfd->panel_power_on) /* suspended */
+		return -EPERM;
+
+	pipe = mdp4_overlay_ndx2pipe(req->id);
+	if (pipe == NULL) {
+		mdp4_stat.err_play++;
+		return -ENODEV;
+	}
+
+	if (mutex_lock_interruptible(&mfd->dma->ov_mutex))
+		return -EINTR;
+
+	img = &req->data;
+	get_img(img, info, pipe, 0, &start, &len, &srcp0_file,
+		&ps0_need, &srcp0_ihdl);
+	if (len == 0) {
+		mutex_unlock(&mfd->dma->ov_mutex);
+		pr_err("%s: pmem Error\n", __func__);
+		ret = -1;
+		goto end;
+	}
+
+	addr = start + img->offset;
+	pipe->srcp0_addr = addr;
+	pipe->srcp0_ystride = pipe->src_width * pipe->bpp;
+
+	if ((req->version_key & VERSION_KEY_MASK) == 0xF9E8D700)
+		overlay_version = (req->version_key & ~VERSION_KEY_MASK);
+
+	if (pipe->fetch_plane == OVERLAY_PLANE_PSEUDO_PLANAR) {
+		if (overlay_version > 0) {
+			img = &req->plane1_data;
+			get_img(img, info, pipe, 1, &start, &len, &srcp1_file,
+				&p_need, &srcp1_ihdl);
+			if (len == 0) {
+				mutex_unlock(&mfd->dma->ov_mutex);
+				pr_err("%s: Error to get plane1\n", __func__);
+				ret = -EINVAL;
+				goto end;
+			}
+			pipe->srcp1_addr = start + img->offset;
+		} else if (pipe->frame_format ==
+				MDP4_FRAME_FORMAT_VIDEO_SUPERTILE) {
+			struct tile_desc tile;
+
+			tile_samsung(&tile);
+			pipe->srcp1_addr = addr + tile_mem_size(pipe, &tile);
+		} else {
+			pipe->srcp1_addr = addr + (pipe->src_width *
+						pipe->src_height);
+		}
+		pipe->srcp0_ystride = pipe->src_width;
+		if ((pipe->src_format == MDP_Y_CRCB_H1V1) ||
+			(pipe->src_format == MDP_Y_CBCR_H1V1)) {
+			if (pipe->src_width > YUV_444_MAX_WIDTH)
+				pipe->srcp1_ystride = pipe->src_width << 2;
+			else
+				pipe->srcp1_ystride = pipe->src_width << 1;
+		} else
+			pipe->srcp1_ystride = pipe->src_width;
+
+	} else if (pipe->fetch_plane == OVERLAY_PLANE_PLANAR) {
+		if (overlay_version > 0) {
+			img = &req->plane1_data;
+			get_img(img, info, pipe, 1, &start, &len, &srcp1_file,
+				&p_need, &srcp1_ihdl);
+			if (len == 0) {
+				mutex_unlock(&mfd->dma->ov_mutex);
+				pr_err("%s: Error to get plane1\n", __func__);
+				ret = -EINVAL;
+				goto end;
+			}
+			pipe->srcp1_addr = start + img->offset;
+
+			img = &req->plane2_data;
+			get_img(img, info, pipe, 2, &start, &len, &srcp2_file,
+				&p_need, &srcp2_ihdl);
+			if (len == 0) {
+				mutex_unlock(&mfd->dma->ov_mutex);
+				pr_err("%s: Error to get plane2\n", __func__);
+				ret = -EINVAL;
+				goto end;
+			}
+			pipe->srcp2_addr = start + img->offset;
+		} else {
+			if (pipe->src_format == MDP_Y_CR_CB_GH2V2) {
+				addr += (ALIGN(pipe->src_width, 16) *
+					pipe->src_height);
+				pipe->srcp1_addr = addr;
+				addr += ((ALIGN((pipe->src_width / 2), 16)) *
+					(pipe->src_height / 2));
+				pipe->srcp2_addr = addr;
+			} else {
+				addr += (pipe->src_width * pipe->src_height);
+				pipe->srcp1_addr = addr;
+				addr += ((pipe->src_width / 2) *
+					(pipe->src_height / 2));
+				pipe->srcp2_addr = addr;
+			}
+		}
+		/* mdp planar format expects Cb in srcp1 and Cr in p2 */
+		if ((pipe->src_format == MDP_Y_CR_CB_H2V2) ||
+			(pipe->src_format == MDP_Y_CR_CB_GH2V2))
+			swap(pipe->srcp1_addr, pipe->srcp2_addr);
+
+		if (pipe->src_format == MDP_Y_CR_CB_GH2V2) {
+			pipe->srcp0_ystride = ALIGN(pipe->src_width, 16);
+			pipe->srcp1_ystride = ALIGN(pipe->src_width / 2, 16);
+			pipe->srcp2_ystride = ALIGN(pipe->src_width / 2, 16);
+		} else {
+			pipe->srcp0_ystride = pipe->src_width;
+			pipe->srcp1_ystride = pipe->src_width / 2;
+			pipe->srcp2_ystride = pipe->src_width / 2;
+		}
+	}
+
+	if (mfd->use_ov0_blt)
+		mdp4_overlay_update_blt_mode(mfd);
+
+	if (mfd->use_ov1_blt)
+		mdp4_overlay1_update_blt_mode(mfd);
+
+	if (pipe->pipe_type == OVERLAY_TYPE_VIDEO) {
+		mdp4_overlay_vg_setup(pipe);	/* video/graphic pipe */
+	} else {
+		if (pipe->flags & MDP_SHARPENING) {
+			pr_debug(
+			"%s: Sharpening/Smoothing not supported on RGB pipe\n",
+								     __func__);
+			pipe->flags &= ~MDP_SHARPENING;
+		}
+		mdp4_overlay_rgb_setup(pipe);	/* rgb pipe */
+	}
+
+	if ((ctrl->panel_mode & MDP4_PANEL_DTV) ||
+		(ctrl->panel_mode & MDP4_PANEL_LCDC) ||
+		(ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO))
+		mdp4_overlay_reg_flush(pipe, 0);
+
+	mdp4_mixer_stage_up(pipe);
+
+	if (pipe->mixer_num == MDP4_MIXER2) {
+		ctrl->mixer2_played++;
+#ifdef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL
+		if (ctrl->panel_mode & MDP4_PANEL_WRITEBACK) {
+			mdp4_writeback_dma_busy_wait(mfd);
+			mdp4_writeback_kickoff_video(mfd, pipe);
+		}
+#endif
+	} else if (pipe->mixer_num == MDP4_MIXER1) {
+		ctrl->mixer1_played++;
+		/* enternal interface */
+		if (ctrl->panel_mode & MDP4_PANEL_DTV) {
+			mdp4_overlay_dtv_start();
+			mdp4_overlay_dtv_ov_done_push(mfd, pipe);
+			if (!mfd->use_ov1_blt)
+				mdp4_overlay1_update_blt_mode(mfd);
+		}
+	} else {
+
+		/* primary interface */
+		ctrl->mixer0_played++;
+		if (ctrl->panel_mode & MDP4_PANEL_LCDC) {
+			mdp4_overlay_reg_flush(pipe, 0);
+			mdp4_overlay_lcdc_start();
+			mdp4_overlay_lcdc_vsync_push(mfd, pipe);
+			if (!mfd->use_ov0_blt &&
+					!(pipe->flags & MDP_OV_PLAY_NOWAIT))
+				mdp4_overlay_update_blt_mode(mfd);
+		}
+#ifdef CONFIG_FB_MSM_MIPI_DSI
+		else if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO) {
+			mdp4_overlay_reg_flush(pipe, 0);
+			mdp4_overlay_dsi_video_start();
+			mdp4_overlay_dsi_video_vsync_push(mfd, pipe);
+			if (!mfd->use_ov0_blt &&
+					!(pipe->flags & MDP_OV_PLAY_NOWAIT))
+				mdp4_overlay_update_blt_mode(mfd);
+		}
+#endif
+		else {
+			/* mddi & mipi dsi cmd mode */
+			if (pipe->flags & MDP_OV_PLAY_NOWAIT) {
+				mdp4_stat.overlay_play[pipe->mixer_num]++;
+				mutex_unlock(&mfd->dma->ov_mutex);
+				goto end;
+			}
+#ifdef CONFIG_FB_MSM_MIPI_DSI
+			if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) {
+				mdp4_iommu_attach();
+				mdp4_dsi_cmd_dma_busy_wait(mfd);
+				mdp4_dsi_cmd_kickoff_video(mfd, pipe);
+			}
+#else
+			if (ctrl->panel_mode & MDP4_PANEL_MDDI) {
+				mdp4_mddi_dma_busy_wait(mfd);
+				mdp4_mddi_kickoff_video(mfd, pipe);
+			}
+#endif
+		}
+	}
+
+	/* write out DPP HSIC registers */
+	if (pipe->flags & MDP_DPP_HSIC)
+		mdp4_hsic_update(pipe);
+	if (!(pipe->flags & MDP_OV_PLAY_NOWAIT))
+		mdp4_iommu_unmap(pipe);
+	mdp4_stat.overlay_play[pipe->mixer_num]++;
+	mutex_unlock(&mfd->dma->ov_mutex);
+end:
+#ifdef CONFIG_ANDROID_PMEM
+	if (srcp0_file)
+		put_pmem_file(srcp0_file);
+	if (srcp1_file)
+		put_pmem_file(srcp1_file);
+	if (srcp2_file)
+		put_pmem_file(srcp2_file);
+#endif
+	/* only source may use frame buffer */
+	if (img->flags & MDP_MEMORY_ID_TYPE_FB)
+		fput_light(srcp0_file, ps0_need);
+	return ret;
+}
+
+static struct {
+	char *name;
+	int  domain;
+} msm_iommu_ctx_names[] = {
+	/* Display */
+	{
+		.name = "mdp_port0_cb0",
+		.domain = DISPLAY_DOMAIN,
+	},
+	/* Display */
+	{
+		.name = "mdp_port0_cb1",
+		.domain = DISPLAY_DOMAIN,
+	},
+	/* Display */
+	{
+		.name = "mdp_port1_cb0",
+		.domain = DISPLAY_DOMAIN,
+	},
+	/* Display */
+	{
+		.name = "mdp_port1_cb1",
+		.domain = DISPLAY_DOMAIN,
+	},
+};
+
+static int iommu_enabled;
+static int mdp_iommu_fault_handler(struct iommu_domain *domain,
+	struct device *dev, unsigned long iova, int flags)
+{
+	pr_err("MDP IOMMU page fault: iova 0x%lx", iova);
+	return 0;
+}
+
+void mdp4_iommu_attach(void)
+{
+	struct iommu_domain *domain;
+	int i;
+
+	if (!iommu_enabled) {
+		for (i = 0; i < ARRAY_SIZE(msm_iommu_ctx_names); i++) {
+			int domain_idx;
+			struct device *ctx = msm_iommu_get_ctx(
+				msm_iommu_ctx_names[i].name);
+
+			if (!ctx)
+				continue;
+
+			domain_idx = msm_iommu_ctx_names[i].domain;
+
+			domain = msm_get_iommu_domain(domain_idx);
+			if (!domain)
+				continue;
+
+			iommu_set_fault_handler(domain,
+				mdp_iommu_fault_handler);
+			if (iommu_attach_device(domain,	ctx)) {
+				WARN(1, "%s: could not attach domain %d to context %s."
+					" iommu programming will not occur.\n",
+					__func__, domain_idx,
+					msm_iommu_ctx_names[i].name);
+				continue;
+			}
+		}
+		pr_debug("Attached MDP IOMMU device\n");
+		iommu_enabled = 1;
+	}
+}
+
+void mdp4_iommu_detach(void)
+{
+	struct iommu_domain *domain;
+	int i;
+
+	if (!mdp_check_suspended() || mdp4_extn_disp)
+		return;
+
+	if (iommu_enabled) {
+		for (i = 0; i < ARRAY_SIZE(msm_iommu_ctx_names); i++) {
+			int domain_idx;
+			struct device *ctx = msm_iommu_get_ctx(
+				msm_iommu_ctx_names[i].name);
+
+			if (!ctx)
+				continue;
+
+			domain_idx = msm_iommu_ctx_names[i].domain;
+
+			domain = msm_get_iommu_domain(domain_idx);
+			if (!domain)
+				continue;
+
+			iommu_detach_device(domain,	ctx);
+		}
+		pr_debug("Detached MDP IOMMU device\n");
+		iommu_enabled = 0;
+	}
+}
+
+int mdp4_v4l2_overlay_set(struct fb_info *info, struct mdp_overlay *req,
+	struct mdp4_overlay_pipe **ppipe)
+{
+	struct mdp4_overlay_pipe *pipe;
+	int err;
+	struct msm_fb_data_type *mfb = info->par;
+
+	req->z_order = 0;
+	req->id = MSMFB_NEW_REQUEST;
+	req->is_fg = false;
+	req->alpha = 0xff;
+	err = mdp4_overlay_req2pipe(req, MDP4_MIXER0, &pipe, mfb);
+	if (err < 0) {
+		pr_err("%s:Could not allocate MDP overlay pipe\n", __func__);
+		return err;
+	}
+
+	mdp4_mixer_blend_setup(pipe);
+	*ppipe = pipe;
+
+	return 0;
+}
+
+void mdp4_v4l2_overlay_clear(struct mdp4_overlay_pipe *pipe)
+{
+	mdp4_mixer_stage_down(pipe);
+	mdp4_overlay_pipe_free(pipe);
+}
+
+int mdp4_v4l2_overlay_play(struct fb_info *info, struct mdp4_overlay_pipe *pipe,
+	unsigned long srcp0_addr, unsigned long srcp1_addr,
+	unsigned long srcp2_addr)
+{
+	struct msm_fb_data_type *mfd = info->par;
+	int err;
+
+	if (mutex_lock_interruptible(&mfd->dma->ov_mutex))
+		return -EINTR;
+
+	switch (pipe->src_format) {
+	case MDP_Y_CR_CB_H2V2:
+		/* YUV420 */
+		pipe->srcp0_addr = srcp0_addr;
+		pipe->srcp0_ystride = pipe->src_width;
+		/*
+		 * For YUV420, the luma plane is 1 byte per pixel times
+		 * num of pixels in the image Also, the planes are
+		 * switched in MDP, srcp2 is actually first chroma plane
+		 */
+		pipe->srcp2_addr = srcp1_addr ? srcp1_addr :
+		pipe->srcp0_addr + (pipe->src_width * pipe->src_height);
+		pipe->srcp2_ystride = pipe->src_width/2;
+		/*
+		 * The chroma planes are half the size of the luma
+		 * planes
+		 */
+		pipe->srcp1_addr = srcp2_addr ? srcp2_addr :
+		pipe->srcp2_addr +
+			(pipe->src_width * pipe->src_height / 4);
+		pipe->srcp1_ystride = pipe->src_width/2;
+		break;
+	case MDP_Y_CRCB_H2V2:
+		/* NV12 */
+		pipe->srcp0_addr = srcp0_addr;
+		pipe->srcp0_ystride = pipe->src_width;
+		pipe->srcp1_addr = srcp1_addr ? srcp1_addr :
+		pipe->srcp0_addr +
+			(pipe->src_width * pipe->src_height);
+		pipe->srcp1_ystride = pipe->src_width;
+		break;
+	default:
+		pr_err("%s: format (%u) is not supported\n", __func__,
+				pipe->src_format);
+		err = -EINVAL;
+		goto done;
+	}
+
+	pr_debug("%s: pipe ndx=%d stage=%d format=%x\n", __func__,
+		pipe->pipe_ndx, pipe->mixer_stage, pipe->src_format);
+
+	if (pipe->pipe_type == OVERLAY_TYPE_VIDEO)
+		mdp4_overlay_vg_setup(pipe);
+	else
+		mdp4_overlay_rgb_setup(pipe);
+
+	if (ctrl->panel_mode & MDP4_PANEL_LCDC)
+		mdp4_overlay_reg_flush(pipe, 1);
+
+	mdp4_mixer_stage_up(pipe);
+
+	if (ctrl->panel_mode & MDP4_PANEL_LCDC) {
+		mdp4_overlay_lcdc_vsync_push(mfd, pipe);
+	} else {
+#ifdef CONFIG_FB_MSM_MIPI_DSI
+		if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) {
+			mdp4_dsi_cmd_dma_busy_wait(mfd);
+			mdp4_dsi_cmd_kickoff_video(mfd, pipe);
+		}
+#else
+		if (ctrl->panel_mode & MDP4_PANEL_MDDI) {
+			mdp4_mddi_dma_busy_wait(mfd);
+			mdp4_mddi_kickoff_video(mfd, pipe);
+		}
+#endif
+	}
+done:
+	mutex_unlock(&mfd->dma->ov_mutex);
+	return err;
+}
+
diff --git a/drivers/video/msm/mdp4_overlay_atv.c b/drivers/video/msm/mdp4_overlay_atv.c
new file mode 100644
index 0000000..753ff23
--- /dev/null
+++ b/drivers/video/msm/mdp4_overlay_atv.c
@@ -0,0 +1,210 @@
+/* Copyright (c) 2010, 2012 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/hrtimer.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/semaphore.h>
+#include <linux/spinlock.h>
+#include <linux/fb.h>
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+
+#include "mdp.h"
+#include "msm_fb.h"
+#include "mdp4.h"
+
+
+static struct mdp4_overlay_pipe *atv_pipe;
+
+int mdp4_atv_on(struct platform_device *pdev)
+{
+	uint8 *buf;
+	unsigned int buf_offset;
+	int bpp, ptype;
+	struct fb_info *fbi;
+	struct fb_var_screeninfo *var;
+	struct msm_fb_data_type *mfd;
+	struct mdp4_overlay_pipe *pipe;
+	int ret;
+
+	mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
+
+	if (!mfd)
+		return -ENODEV;
+
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	fbi = mfd->fbi;
+	var = &fbi->var;
+
+	bpp = fbi->var.bits_per_pixel / 8;
+	buf = (uint8 *) fbi->fix.smem_start;
+	buf_offset = calc_fb_offset(mfd, fbi, bpp);
+
+	if (atv_pipe == NULL) {
+		ptype = mdp4_overlay_format2type(mfd->fb_imgType);
+		pipe = mdp4_overlay_pipe_alloc(ptype, MDP4_MIXER1);
+		if (pipe == NULL)
+			return -EBUSY;
+		pipe->pipe_used++;
+		pipe->mixer_stage  = MDP4_MIXER_STAGE_BASE;
+		pipe->mixer_num  = MDP4_MIXER1;
+		pipe->src_format = mfd->fb_imgType;
+		mdp4_overlay_panel_mode(pipe->mixer_num, MDP4_PANEL_ATV);
+		mdp4_overlay_format2pipe(pipe);
+
+		atv_pipe = pipe; /* keep it */
+	} else {
+		pipe = atv_pipe;
+	}
+
+	printk(KERN_INFO "mdp4_atv_overlay: pipe=%x ndx=%d\n",
+					(int)pipe, pipe->pipe_ndx);
+
+	/* MDP cmd block enable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	/* Turn the next panel on, get correct resolution
+		before configuring overlay pipe */
+	ret = panel_next_on(pdev);
+
+	pr_info("%s: fbi->var.yres: %d | fbi->var.xres: %d",
+			__func__, fbi->var.yres, fbi->var.xres);
+
+	/* MDP4 Config */
+	pipe->src_height = fbi->var.yres;
+	pipe->src_width = fbi->var.xres;
+	pipe->src_h = fbi->var.yres;
+	pipe->src_w = fbi->var.xres;
+	pipe->src_y = 0;
+	pipe->src_x = 0;
+	if (mfd->map_buffer) {
+		pipe->srcp0_addr = (unsigned int)mfd->map_buffer->iova[0] + \
+			buf_offset;
+		pr_debug("start 0x%lx srcp0_addr 0x%x\n", mfd->
+			map_buffer->iova[0], pipe->srcp0_addr);
+	} else {
+		pipe->srcp0_addr = (uint32)(buf + buf_offset);
+	}
+
+	pipe->srcp0_ystride = fbi->fix.line_length;
+
+	mdp4_overlay_dmae_xy(pipe);	/* dma_e */
+	mdp4_overlay_dmae_cfg(mfd, 1);
+
+	mdp4_overlay_rgb_setup(pipe);
+
+	mdp4_overlayproc_cfg(pipe);
+
+	mdp4_overlay_reg_flush(pipe, 1);
+	mdp4_mixer_stage_up(pipe);
+
+	if (ret == 0)
+		mdp_pipe_ctrl(MDP_OVERLAY1_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	/* MDP cmd block disable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	return ret;
+}
+
+int mdp4_atv_off(struct platform_device *pdev)
+{
+	int ret = 0;
+
+	mdp_pipe_ctrl(MDP_OVERLAY1_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	ret = panel_next_off(pdev);
+
+	/* delay to make sure the last frame finishes */
+	msleep(100);
+
+	/* dis-engage rgb2 from mixer1 */
+	if (atv_pipe) {
+		mdp4_mixer_stage_down(atv_pipe);
+		mdp4_iommu_unmap(atv_pipe);
+	}
+
+	return ret;
+}
+
+/*
+ * mdp4_overlay1_done_atv: called from isr
+ */
+void mdp4_overlay1_done_atv()
+{
+	complete(&atv_pipe->comp);
+}
+
+void mdp4_atv_overlay(struct msm_fb_data_type *mfd)
+{
+	struct fb_info *fbi = mfd->fbi;
+	uint8 *buf;
+	unsigned int buf_offset;
+	int bpp;
+	unsigned long flag;
+	struct mdp4_overlay_pipe *pipe;
+
+	if (!mfd->panel_power_on)
+		return;
+
+	/* no need to power on cmd block since it's lcdc mode */
+	bpp = fbi->var.bits_per_pixel / 8;
+	buf = (uint8 *) fbi->fix.smem_start;
+	buf_offset = calc_fb_offset(mfd, fbi, bpp);
+
+	mutex_lock(&mfd->dma->ov_mutex);
+
+	pipe = atv_pipe;
+	if (mfd->map_buffer) {
+		pipe->srcp0_addr = (unsigned int)mfd->map_buffer->iova[0] + \
+			buf_offset;
+		pr_debug("start 0x%lx srcp0_addr 0x%x\n", mfd->
+			map_buffer->iova[0], pipe->srcp0_addr);
+	} else {
+		pipe->srcp0_addr = (uint32)(buf + buf_offset);
+	}
+	mdp4_overlay_rgb_setup(pipe);
+	mdp4_overlay_reg_flush(pipe, 0);
+	mdp4_mixer_stage_up(pipe);
+
+	printk(KERN_INFO "mdp4_atv_overlay: pipe=%x ndx=%d\n",
+					(int)pipe, pipe->pipe_ndx);
+
+	/* enable irq */
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	mdp_enable_irq(MDP_OVERLAY1_TERM);
+	INIT_COMPLETION(atv_pipe->comp);
+	mfd->dma->waiting = TRUE;
+	outp32(MDP_INTR_CLEAR, INTR_OVERLAY1_DONE);
+	mdp_intr_mask |= INTR_OVERLAY1_DONE;
+	outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+	wait_for_completion_killable(&atv_pipe->comp);
+	mdp_disable_irq(MDP_OVERLAY1_TERM);
+
+	/* change mdp clk while mdp is idle` */
+	mdp4_set_perf_level();
+
+	mdp4_stat.kickoff_atv++;
+	mutex_unlock(&mfd->dma->ov_mutex);
+}
diff --git a/drivers/video/msm/mdp4_overlay_dsi_cmd.c b/drivers/video/msm/mdp4_overlay_dsi_cmd.c
new file mode 100644
index 0000000..a5b4b3e
--- /dev/null
+++ b/drivers/video/msm/mdp4_overlay_dsi_cmd.c
@@ -0,0 +1,685 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/hrtimer.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/semaphore.h>
+#include <linux/spinlock.h>
+#include <linux/fb.h>
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+
+#include "mdp.h"
+#include "msm_fb.h"
+#include "mdp4.h"
+#include "mipi_dsi.h"
+
+static struct mdp4_overlay_pipe *dsi_pipe;
+static struct msm_fb_data_type *dsi_mfd;
+static int busy_wait_cnt;
+static int dsi_state;
+static unsigned long  tout_expired;
+
+#define TOUT_PERIOD	HZ	/* 1 second */
+#define MS_100		(HZ/10)	/* 100 ms */
+
+static int vsync_start_y_adjust = 4;
+
+struct timer_list dsi_clock_timer;
+
+void mdp4_overlay_dsi_state_set(int state)
+{
+	unsigned long flag;
+
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	dsi_state = state;
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+}
+
+int mdp4_overlay_dsi_state_get(void)
+{
+	return dsi_state;
+}
+
+static void dsi_clock_tout(unsigned long data)
+{
+	if (mipi_dsi_clk_on) {
+		if (dsi_state == ST_DSI_PLAYING) {
+			mipi_dsi_turn_off_clks();
+			mdp4_overlay_dsi_state_set(ST_DSI_CLK_OFF);
+		}
+	}
+}
+
+static __u32 msm_fb_line_length(__u32 fb_index, __u32 xres, int bpp)
+{
+	/*
+	 * The adreno GPU hardware requires that the pitch be aligned to
+	 * 32 pixels for color buffers, so for the cases where the GPU
+	 * is writing directly to fb0, the framebuffer pitch
+	 * also needs to be 32 pixel aligned
+	 */
+
+	if (fb_index == 0)
+		return ALIGN(xres, 32) * bpp;
+	else
+		return xres * bpp;
+}
+
+void mdp4_mipi_vsync_enable(struct msm_fb_data_type *mfd,
+		struct mdp4_overlay_pipe *pipe, int which)
+{
+	uint32 start_y, data, tear_en;
+
+	tear_en = (1 << which);
+
+	if ((mfd->use_mdp_vsync) && (mfd->ibuf.vsync_enable) &&
+		(mfd->panel_info.lcd.vsync_enable)) {
+
+		if (vsync_start_y_adjust <= pipe->dst_y)
+			start_y = pipe->dst_y - vsync_start_y_adjust;
+		else
+			start_y = (mfd->total_lcd_lines - 1) -
+				(vsync_start_y_adjust - pipe->dst_y);
+		if (which == 0)
+			MDP_OUTP(MDP_BASE + 0x210, start_y);	/* primary */
+		else
+			MDP_OUTP(MDP_BASE + 0x214, start_y);	/* secondary */
+
+		data = inpdw(MDP_BASE + 0x20c);
+		data |= tear_en;
+		MDP_OUTP(MDP_BASE + 0x20c, data);
+	} else {
+		data = inpdw(MDP_BASE + 0x20c);
+		data &= ~tear_en;
+		MDP_OUTP(MDP_BASE + 0x20c, data);
+	}
+}
+
+void mdp4_overlay_update_dsi_cmd(struct msm_fb_data_type *mfd)
+{
+	MDPIBUF *iBuf = &mfd->ibuf;
+	uint8 *src;
+	int ptype;
+	struct mdp4_overlay_pipe *pipe;
+	int bpp;
+	int ret;
+
+	if (mfd->key != MFD_KEY)
+		return;
+
+	dsi_mfd = mfd;		/* keep it */
+
+	/* MDP cmd block enable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	if (dsi_pipe == NULL) {
+		ptype = mdp4_overlay_format2type(mfd->fb_imgType);
+		if (ptype < 0)
+			printk(KERN_INFO "%s: format2type failed\n", __func__);
+		pipe = mdp4_overlay_pipe_alloc(ptype, MDP4_MIXER0);
+		if (pipe == NULL)
+			printk(KERN_INFO "%s: pipe_alloc failed\n", __func__);
+		pipe->pipe_used++;
+		pipe->mixer_stage  = MDP4_MIXER_STAGE_BASE;
+		pipe->mixer_num  = MDP4_MIXER0;
+		pipe->src_format = mfd->fb_imgType;
+		mdp4_overlay_panel_mode(pipe->mixer_num, MDP4_PANEL_DSI_CMD);
+		ret = mdp4_overlay_format2pipe(pipe);
+		if (ret < 0)
+			printk(KERN_INFO "%s: format2type failed\n", __func__);
+
+		init_timer(&dsi_clock_timer);
+		dsi_clock_timer.function = dsi_clock_tout;
+		dsi_clock_timer.data = (unsigned long) mfd;;
+		dsi_clock_timer.expires = 0xffffffff;
+		add_timer(&dsi_clock_timer);
+		tout_expired = jiffies;
+
+		dsi_pipe = pipe; /* keep it */
+
+		mdp4_init_writeback_buf(mfd, MDP4_MIXER0);
+		pipe->blt_addr = 0;
+
+	} else {
+		pipe = dsi_pipe;
+	}
+	/*
+	 * configure dsi stream id
+	 * dma_p = 0, dma_s = 1
+	 */
+	MDP_OUTP(MDP_BASE + 0x000a0, 0x10);
+	/* disable dsi trigger */
+	MDP_OUTP(MDP_BASE + 0x000a4, 0x00);
+	/* whole screen for base layer */
+	src = (uint8 *) iBuf->buf;
+
+
+	{
+		struct fb_info *fbi;
+
+		fbi = mfd->fbi;
+		if (pipe->is_3d) {
+			bpp = fbi->var.bits_per_pixel / 8;
+			pipe->src_height = pipe->src_height_3d;
+			pipe->src_width = pipe->src_width_3d;
+			pipe->src_h = pipe->src_height_3d;
+			pipe->src_w = pipe->src_width_3d;
+			pipe->dst_h = pipe->src_height_3d;
+			pipe->dst_w = pipe->src_width_3d;
+			pipe->srcp0_ystride = msm_fb_line_length(0,
+						pipe->src_width, bpp);
+		} else {
+			 /* 2D */
+			pipe->src_height = fbi->var.yres;
+			pipe->src_width = fbi->var.xres;
+			pipe->src_h = fbi->var.yres;
+			pipe->src_w = fbi->var.xres;
+			pipe->dst_h = fbi->var.yres;
+			pipe->dst_w = fbi->var.xres;
+			pipe->srcp0_ystride = fbi->fix.line_length;
+		}
+		pipe->src_y = 0;
+		pipe->src_x = 0;
+		pipe->dst_y = 0;
+		pipe->dst_x = 0;
+		pipe->srcp0_addr = (uint32)src;
+	}
+
+
+	mdp4_overlay_rgb_setup(pipe);
+
+	mdp4_mixer_stage_up(pipe);
+
+	mdp4_overlayproc_cfg(pipe);
+
+	mdp4_overlay_dmap_xy(pipe);
+
+	mdp4_overlay_dmap_cfg(mfd, 0);
+
+	mdp4_mipi_vsync_enable(mfd, pipe, 0);
+
+	/* MDP cmd block disable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	wmb();
+}
+
+/* 3D side by side */
+void mdp4_dsi_cmd_3d_sbys(struct msm_fb_data_type *mfd,
+				struct msmfb_overlay_3d *r3d)
+{
+	struct fb_info *fbi;
+	struct mdp4_overlay_pipe *pipe;
+	int bpp;
+	uint8 *src = NULL;
+
+	if (dsi_pipe == NULL)
+		return;
+
+	dsi_pipe->is_3d = r3d->is_3d;
+	dsi_pipe->src_height_3d = r3d->height;
+	dsi_pipe->src_width_3d = r3d->width;
+
+	pipe = dsi_pipe;
+
+	if (pipe->is_3d)
+		mdp4_overlay_panel_3d(pipe->mixer_num, MDP4_3D_SIDE_BY_SIDE);
+	else
+		mdp4_overlay_panel_3d(pipe->mixer_num, MDP4_3D_NONE);
+
+	if (mfd->panel_power_on) {
+		mdp4_dsi_cmd_dma_busy_wait(mfd);
+		mdp4_dsi_blt_dmap_busy_wait(mfd);
+	}
+
+	fbi = mfd->fbi;
+	if (pipe->is_3d) {
+		bpp = fbi->var.bits_per_pixel / 8;
+		pipe->src_height = pipe->src_height_3d;
+		pipe->src_width = pipe->src_width_3d;
+		pipe->src_h = pipe->src_height_3d;
+		pipe->src_w = pipe->src_width_3d;
+		pipe->dst_h = pipe->src_height_3d;
+		pipe->dst_w = pipe->src_width_3d;
+		pipe->srcp0_ystride = msm_fb_line_length(0,
+					pipe->src_width, bpp);
+	} else {
+		 /* 2D */
+		pipe->src_height = fbi->var.yres;
+		pipe->src_width = fbi->var.xres;
+		pipe->src_h = fbi->var.yres;
+		pipe->src_w = fbi->var.xres;
+		pipe->dst_h = fbi->var.yres;
+		pipe->dst_w = fbi->var.xres;
+		pipe->srcp0_ystride = fbi->fix.line_length;
+	}
+	pipe->src_y = 0;
+	pipe->src_x = 0;
+	pipe->dst_y = 0;
+	pipe->dst_x = 0;
+	pipe->srcp0_addr = (uint32)src;
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	mdp4_overlay_rgb_setup(pipe);
+
+	mdp4_mixer_stage_up(pipe);
+
+	mdp4_overlayproc_cfg(pipe);
+
+	mdp4_overlay_dmap_xy(pipe);
+
+	mdp4_overlay_dmap_cfg(mfd, 0);
+
+	/* MDP cmd block disable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+int mdp4_dsi_overlay_blt_start(struct msm_fb_data_type *mfd)
+{
+	unsigned long flag;
+
+	pr_debug("%s: blt_end=%d blt_addr=%x pid=%d\n",
+	__func__, dsi_pipe->blt_end, (int)dsi_pipe->blt_addr, current->pid);
+
+	mdp4_allocate_writeback_buf(mfd, MDP4_MIXER0);
+
+	if (mfd->ov0_wb_buf->phys_addr == 0) {
+		pr_info("%s: no blt_base assigned\n", __func__);
+		return -EBUSY;
+	}
+
+	if (dsi_pipe->blt_addr == 0) {
+		mdp4_dsi_cmd_dma_busy_wait(mfd);
+		spin_lock_irqsave(&mdp_spin_lock, flag);
+		dsi_pipe->blt_end = 0;
+		dsi_pipe->blt_cnt = 0;
+		dsi_pipe->ov_cnt = 0;
+		dsi_pipe->dmap_cnt = 0;
+		dsi_pipe->blt_addr = mfd->ov0_wb_buf->phys_addr;
+		mdp4_stat.blt_dsi_cmd++;
+		spin_unlock_irqrestore(&mdp_spin_lock, flag);
+		return 0;
+	}
+
+	return -EBUSY;
+}
+
+int mdp4_dsi_overlay_blt_stop(struct msm_fb_data_type *mfd)
+{
+	unsigned long flag;
+
+
+	pr_debug("%s: blt_end=%d blt_addr=%x\n",
+		 __func__, dsi_pipe->blt_end, (int)dsi_pipe->blt_addr);
+
+	if ((dsi_pipe->blt_end == 0) && dsi_pipe->blt_addr) {
+		spin_lock_irqsave(&mdp_spin_lock, flag);
+		dsi_pipe->blt_end = 1;	/* mark as end */
+		spin_unlock_irqrestore(&mdp_spin_lock, flag);
+		return 0;
+	}
+
+	return -EBUSY;
+}
+
+int mdp4_dsi_overlay_blt_offset(struct msm_fb_data_type *mfd,
+					struct msmfb_overlay_blt *req)
+{
+	req->offset = 0;
+	req->width = dsi_pipe->src_width;
+	req->height = dsi_pipe->src_height;
+	req->bpp = dsi_pipe->bpp;
+
+	return sizeof(*req);
+}
+
+void mdp4_dsi_overlay_blt(struct msm_fb_data_type *mfd,
+					struct msmfb_overlay_blt *req)
+{
+	if (req->enable)
+		mdp4_dsi_overlay_blt_start(mfd);
+	else if (req->enable == 0)
+		mdp4_dsi_overlay_blt_stop(mfd);
+
+}
+
+void mdp4_blt_xy_update(struct mdp4_overlay_pipe *pipe)
+{
+	uint32 off, addr, addr2;
+	int bpp;
+	char *overlay_base;
+
+
+	if (pipe->blt_addr == 0)
+		return;
+
+
+#ifdef BLT_RGB565
+	bpp = 2; /* overlay ouput is RGB565 */
+#else
+	bpp = 3; /* overlay ouput is RGB888 */
+#endif
+	off = 0;
+	if (pipe->dmap_cnt & 0x01)
+		off = pipe->src_height * pipe->src_width * bpp;
+	addr = pipe->blt_addr + off;
+
+	/* dmap */
+	MDP_OUTP(MDP_BASE + 0x90008, addr);
+
+	off = 0;
+	if (pipe->ov_cnt & 0x01)
+		off = pipe->src_height * pipe->src_width * bpp;
+	addr2 = pipe->blt_addr + off;
+	/* overlay 0 */
+	overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */
+	outpdw(overlay_base + 0x000c, addr2);
+	outpdw(overlay_base + 0x001c, addr2);
+}
+
+
+/*
+ * mdp4_dmap_done_dsi: called from isr
+ * DAM_P_DONE only used when blt enabled
+ */
+void mdp4_dma_p_done_dsi(struct mdp_dma_data *dma)
+{
+	int diff;
+
+	dsi_pipe->dmap_cnt++;
+	diff = dsi_pipe->ov_cnt - dsi_pipe->dmap_cnt;
+	pr_debug("%s: ov_cnt=%d dmap_cnt=%d\n",
+			__func__, dsi_pipe->ov_cnt, dsi_pipe->dmap_cnt);
+
+	if (diff <= 0) {
+		spin_lock(&mdp_spin_lock);
+		dma->dmap_busy = FALSE;
+		complete(&dma->dmap_comp);
+		spin_unlock(&mdp_spin_lock);
+		if (dsi_pipe->blt_end) {
+			dsi_pipe->blt_end = 0;
+			dsi_pipe->blt_addr = 0;
+			pr_debug("%s: END, ov_cnt=%d dmap_cnt=%d\n",
+				__func__, dsi_pipe->ov_cnt, dsi_pipe->dmap_cnt);
+			mdp_intr_mask &= ~INTR_DMA_P_DONE;
+			outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+		}
+		mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_OFF, TRUE);
+		mdp_disable_irq_nosync(MDP_DMA2_TERM);  /* disable intr */
+		return;
+	}
+
+	spin_lock(&mdp_spin_lock);
+	dma->busy = FALSE;
+	spin_unlock(&mdp_spin_lock);
+	complete(&dma->comp);
+	if (busy_wait_cnt)
+		busy_wait_cnt--;
+
+	pr_debug("%s: kickoff dmap\n", __func__);
+
+	mdp4_blt_xy_update(dsi_pipe);
+	/* kick off dmap */
+	outpdw(MDP_BASE + 0x000c, 0x0);
+	mdp4_stat.kickoff_dmap++;
+	/* trigger dsi cmd engine */
+	mipi_dsi_cmd_mdp_start();
+
+	mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_OFF, TRUE);
+}
+
+
+/*
+ * mdp4_overlay0_done_dsi_cmd: called from isr
+ */
+void mdp4_overlay0_done_dsi_cmd(struct mdp_dma_data *dma)
+{
+	int diff;
+
+	if (dsi_pipe->blt_addr == 0) {
+		mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_OFF, TRUE);
+		spin_lock(&mdp_spin_lock);
+		dma->busy = FALSE;
+		spin_unlock(&mdp_spin_lock);
+		complete(&dma->comp);
+		if (busy_wait_cnt)
+			busy_wait_cnt--;
+		mdp_disable_irq_nosync(MDP_OVERLAY0_TERM);
+		return;
+	}
+
+	/* blt enabled */
+	if (dsi_pipe->blt_end == 0)
+		dsi_pipe->ov_cnt++;
+
+	pr_debug("%s: ov_cnt=%d dmap_cnt=%d\n",
+			__func__, dsi_pipe->ov_cnt, dsi_pipe->dmap_cnt);
+
+	if (dsi_pipe->blt_cnt == 0) {
+		/* first kickoff since blt enabled */
+		mdp_intr_mask |= INTR_DMA_P_DONE;
+		outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+	}
+	dsi_pipe->blt_cnt++;
+
+	diff = dsi_pipe->ov_cnt - dsi_pipe->dmap_cnt;
+	if (diff >= 2) {
+		mdp_disable_irq_nosync(MDP_OVERLAY0_TERM);
+		return;
+	}
+
+	spin_lock(&mdp_spin_lock);
+	dma->busy = FALSE;
+	dma->dmap_busy = TRUE;
+	spin_unlock(&mdp_spin_lock);
+	complete(&dma->comp);
+	if (busy_wait_cnt)
+		busy_wait_cnt--;
+
+	pr_debug("%s: kickoff dmap\n", __func__);
+
+	mdp4_blt_xy_update(dsi_pipe);
+	mdp_enable_irq(MDP_DMA2_TERM);	/* enable intr */
+	/* kick off dmap */
+	outpdw(MDP_BASE + 0x000c, 0x0);
+	mdp4_stat.kickoff_dmap++;
+	/* trigger dsi cmd engine */
+	mipi_dsi_cmd_mdp_start();
+	mdp_disable_irq_nosync(MDP_OVERLAY0_TERM);
+}
+
+void mdp4_dsi_cmd_overlay_restore(void)
+{
+	/* mutex holded by caller */
+	if (dsi_mfd && dsi_pipe) {
+		mdp4_dsi_cmd_dma_busy_wait(dsi_mfd);
+		mipi_dsi_mdp_busy_wait(dsi_mfd);
+		mdp4_overlay_update_dsi_cmd(dsi_mfd);
+
+		if (dsi_pipe->blt_addr)
+			mdp4_dsi_blt_dmap_busy_wait(dsi_mfd);
+		mdp4_dsi_cmd_overlay_kickoff(dsi_mfd, dsi_pipe);
+	}
+}
+
+void mdp4_dsi_blt_dmap_busy_wait(struct msm_fb_data_type *mfd)
+{
+	unsigned long flag;
+	int need_wait = 0;
+
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	if (mfd->dma->dmap_busy == TRUE) {
+		INIT_COMPLETION(mfd->dma->dmap_comp);
+		need_wait++;
+	}
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+
+	if (need_wait) {
+		/* wait until DMA finishes the current job */
+		wait_for_completion(&mfd->dma->dmap_comp);
+	}
+}
+
+/*
+ * mdp4_dsi_cmd_dma_busy_wait: check dsi link activity
+ * dsi link is a shared resource and it can only be used
+ * while it is in idle state.
+ * ov_mutex need to be acquired before call this function.
+ */
+void mdp4_dsi_cmd_dma_busy_wait(struct msm_fb_data_type *mfd)
+{
+	unsigned long flag;
+	int need_wait = 0;
+
+
+
+	if (dsi_clock_timer.function) {
+		if (time_after(jiffies, tout_expired)) {
+			tout_expired = jiffies + TOUT_PERIOD;
+			mod_timer(&dsi_clock_timer, tout_expired);
+			tout_expired -= MS_100;
+		}
+	}
+
+	pr_debug("%s: start pid=%d dsi_clk_on=%d\n",
+			__func__, current->pid, mipi_dsi_clk_on);
+
+	/* satrt dsi clock if necessary */
+	if (mipi_dsi_clk_on == 0) {
+		local_bh_disable();
+		mipi_dsi_turn_on_clks();
+		local_bh_enable();
+	}
+
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	if (mfd->dma->busy == TRUE) {
+		if (busy_wait_cnt == 0)
+			INIT_COMPLETION(mfd->dma->comp);
+		busy_wait_cnt++;
+		need_wait++;
+	}
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+
+	if (need_wait) {
+		/* wait until DMA finishes the current job */
+		pr_debug("%s: pending pid=%d dsi_clk_on=%d\n",
+				__func__, current->pid, mipi_dsi_clk_on);
+		wait_for_completion(&mfd->dma->comp);
+	}
+	pr_debug("%s: done pid=%d dsi_clk_on=%d\n",
+			 __func__, current->pid, mipi_dsi_clk_on);
+}
+
+void mdp4_dsi_cmd_kickoff_video(struct msm_fb_data_type *mfd,
+				struct mdp4_overlay_pipe *pipe)
+{
+	/*
+	 * a video kickoff may happen before UI kickoff after
+	 * blt enabled. mdp4_overlay_update_dsi_cmd() need
+	 * to be called before kickoff.
+	 * vice versa for blt disabled.
+	 */
+	if (dsi_pipe->blt_addr && dsi_pipe->blt_cnt == 0)
+		mdp4_overlay_update_dsi_cmd(mfd); /* first time */
+	else if (dsi_pipe->blt_addr == 0  && dsi_pipe->blt_cnt) {
+		mdp4_overlay_update_dsi_cmd(mfd); /* last time */
+		dsi_pipe->blt_cnt = 0;
+	}
+
+	pr_debug("%s: blt_addr=%d blt_cnt=%d\n",
+		__func__, (int)dsi_pipe->blt_addr, dsi_pipe->blt_cnt);
+
+	if (dsi_pipe->blt_addr)
+		mdp4_dsi_blt_dmap_busy_wait(dsi_mfd);
+
+	mdp4_dsi_cmd_overlay_kickoff(mfd, pipe);
+}
+
+void mdp4_dsi_cmd_kickoff_ui(struct msm_fb_data_type *mfd,
+				struct mdp4_overlay_pipe *pipe)
+{
+
+	pr_debug("%s: pid=%d\n", __func__, current->pid);
+	mdp4_dsi_cmd_overlay_kickoff(mfd, pipe);
+}
+
+
+void mdp4_dsi_cmd_overlay_kickoff(struct msm_fb_data_type *mfd,
+				struct mdp4_overlay_pipe *pipe)
+{
+	unsigned long flag;
+
+
+	/* change mdp clk */
+	mdp4_set_perf_level();
+
+	mipi_dsi_mdp_busy_wait(mfd);
+
+	if (dsi_pipe->blt_addr == 0)
+		mipi_dsi_cmd_mdp_start();
+
+	mdp4_overlay_dsi_state_set(ST_DSI_PLAYING);
+
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	mdp_enable_irq(MDP_OVERLAY0_TERM);
+	mfd->dma->busy = TRUE;
+	if (dsi_pipe->blt_addr)
+		mfd->dma->dmap_busy = TRUE;
+	/* start OVERLAY pipe */
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+	mdp_pipe_kickoff(MDP_OVERLAY0_TERM, mfd);
+	mdp4_stat.kickoff_ov0++;
+}
+
+void mdp_dsi_cmd_overlay_suspend(void)
+{
+	/* dis-engage rgb0 from mixer0 */
+	if (dsi_pipe) {
+		mdp4_mixer_stage_down(dsi_pipe);
+		mdp4_iommu_unmap(dsi_pipe);
+	}
+}
+
+void mdp4_dsi_cmd_overlay(struct msm_fb_data_type *mfd)
+{
+	mutex_lock(&mfd->dma->ov_mutex);
+
+	if (mfd && mfd->panel_power_on) {
+		mdp4_dsi_cmd_dma_busy_wait(mfd);
+
+		if (dsi_pipe && dsi_pipe->blt_addr)
+			mdp4_dsi_blt_dmap_busy_wait(mfd);
+
+		mdp4_overlay_update_dsi_cmd(mfd);
+
+		mdp4_iommu_attach();
+		mdp4_dsi_cmd_kickoff_ui(mfd, dsi_pipe);
+		mdp4_iommu_unmap(dsi_pipe);
+	/* signal if pan function is waiting for the update completion */
+		if (mfd->pan_waiting) {
+			mfd->pan_waiting = FALSE;
+			complete(&mfd->pan_comp);
+		}
+	}
+	mutex_unlock(&mfd->dma->ov_mutex);
+}
diff --git a/drivers/video/msm/mdp4_overlay_dsi_video.c b/drivers/video/msm/mdp4_overlay_dsi_video.c
new file mode 100644
index 0000000..fb71cc1
--- /dev/null
+++ b/drivers/video/msm/mdp4_overlay_dsi_video.c
@@ -0,0 +1,709 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/hrtimer.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/semaphore.h>
+#include <linux/spinlock.h>
+#include <linux/fb.h>
+#include <linux/msm_mdp.h>
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include "mdp.h"
+#include "msm_fb.h"
+#include "mdp4.h"
+#include "mipi_dsi.h"
+
+#include <mach/iommu_domains.h>
+
+#define DSI_VIDEO_BASE	0xE0000
+
+static int first_pixel_start_x;
+static int first_pixel_start_y;
+static int dsi_video_enabled;
+
+static struct mdp4_overlay_pipe *dsi_pipe;
+static struct completion dsi_video_comp;
+static int blt_cfg_changed;
+
+static cmd_fxn_t display_on;
+
+static __u32 msm_fb_line_length(__u32 fb_index, __u32 xres, int bpp)
+{
+	/*
+	 * The adreno GPU hardware requires that the pitch be aligned to
+	 * 32 pixels for color buffers, so for the cases where the GPU
+	 * is writing directly to fb0, the framebuffer pitch
+	 * also needs to be 32 pixel aligned
+	 */
+
+	if (fb_index == 0)
+		return ALIGN(xres, 32) * bpp;
+	else
+		return xres * bpp;
+}
+
+void mdp4_dsi_video_fxn_register(cmd_fxn_t fxn)
+{
+	display_on = fxn;
+}
+
+static void mdp4_overlay_dsi_video_wait4event(struct msm_fb_data_type *mfd,
+						int intr_done);
+
+int mdp4_dsi_video_on(struct platform_device *pdev)
+{
+	int dsi_width;
+	int dsi_height;
+	int dsi_bpp;
+	int dsi_border_clr;
+	int dsi_underflow_clr;
+	int dsi_hsync_skew;
+
+	int hsync_period;
+	int hsync_ctrl;
+	int vsync_period;
+	int display_hctl;
+	int display_v_start;
+	int display_v_end;
+	int active_hctl;
+	int active_h_start;
+	int active_h_end;
+	int active_v_start;
+	int active_v_end;
+	int ctrl_polarity;
+	int h_back_porch;
+	int h_front_porch;
+	int v_back_porch;
+	int v_front_porch;
+	int hsync_pulse_width;
+	int vsync_pulse_width;
+	int hsync_polarity;
+	int vsync_polarity;
+	int data_en_polarity;
+	int hsync_start_x;
+	int hsync_end_x;
+	uint8 *buf;
+	unsigned int buf_offset;
+	int bpp, ptype;
+	struct fb_info *fbi;
+	struct fb_var_screeninfo *var;
+	struct msm_fb_data_type *mfd;
+	struct mdp4_overlay_pipe *pipe;
+	int ret;
+
+	mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
+
+	if (!mfd)
+		return -ENODEV;
+
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	mdp4_overlay_ctrl_db_reset();
+
+	fbi = mfd->fbi;
+	var = &fbi->var;
+
+	bpp = fbi->var.bits_per_pixel / 8;
+	buf = (uint8 *) fbi->fix.smem_start;
+	buf_offset = calc_fb_offset(mfd, fbi, bpp);
+
+	if (dsi_pipe == NULL) {
+		ptype = mdp4_overlay_format2type(mfd->fb_imgType);
+		if (ptype < 0)
+			printk(KERN_INFO "%s: format2type failed\n", __func__);
+		pipe = mdp4_overlay_pipe_alloc(ptype, MDP4_MIXER0);
+		if (pipe == NULL) {
+			printk(KERN_INFO "%s: pipe_alloc failed\n", __func__);
+			return -EBUSY;
+		}
+		pipe->pipe_used++;
+		pipe->mixer_stage  = MDP4_MIXER_STAGE_BASE;
+		pipe->mixer_num  = MDP4_MIXER0;
+		pipe->src_format = mfd->fb_imgType;
+		mdp4_overlay_panel_mode(pipe->mixer_num, MDP4_PANEL_DSI_VIDEO);
+		ret = mdp4_overlay_format2pipe(pipe);
+		if (ret < 0)
+			printk(KERN_INFO "%s: format2type failed\n", __func__);
+
+		dsi_pipe = pipe; /* keep it */
+		init_completion(&dsi_video_comp);
+
+		mdp4_init_writeback_buf(mfd, MDP4_MIXER0);
+		pipe->blt_addr = 0;
+
+	} else {
+		pipe = dsi_pipe;
+	}
+
+	/* MDP cmd block enable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	if (!(mfd->cont_splash_done)) {
+		mfd->cont_splash_done = 1;
+		mdp_pipe_ctrl(MDP_CMD_BLOCK,
+			      MDP_BLOCK_POWER_OFF, FALSE);
+		mdp4_overlay_dsi_video_wait4event(mfd, INTR_DMA_P_DONE);
+		/* disable timing generator */
+		MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE, 0);
+		mipi_dsi_controller_cfg(0);
+	}
+
+	if (is_mdp4_hw_reset()) {
+		mdp4_hw_init();
+		outpdw(MDP_BASE + 0x0038, mdp4_display_intf);
+	}
+
+	pipe->src_height = fbi->var.yres;
+	pipe->src_width = fbi->var.xres;
+	pipe->src_h = fbi->var.yres;
+	pipe->src_w = fbi->var.xres;
+	pipe->src_y = 0;
+	pipe->src_x = 0;
+	pipe->srcp0_ystride = fbi->fix.line_length;
+	pipe->bpp = bpp;
+
+	if (mfd->display_iova)
+		pipe->srcp0_addr = mfd->display_iova + buf_offset;
+	else
+		pipe->srcp0_addr = (uint32)(buf + buf_offset);
+
+	pipe->dst_h = fbi->var.yres;
+	pipe->dst_w = fbi->var.xres;
+
+	mdp4_overlay_dmap_xy(pipe);	/* dma_p */
+	mdp4_overlay_dmap_cfg(mfd, 1);
+
+	mdp4_overlay_rgb_setup(pipe);
+
+	mdp4_overlayproc_cfg(pipe);
+
+	/*
+	 * DSI timing setting
+	 */
+	h_back_porch = var->left_margin;
+	h_front_porch = var->right_margin;
+	v_back_porch = var->upper_margin;
+	v_front_porch = var->lower_margin;
+	hsync_pulse_width = var->hsync_len;
+	vsync_pulse_width = var->vsync_len;
+	dsi_border_clr = mfd->panel_info.lcdc.border_clr;
+	dsi_underflow_clr = mfd->panel_info.lcdc.underflow_clr;
+	dsi_hsync_skew = mfd->panel_info.lcdc.hsync_skew;
+	dsi_width = mfd->panel_info.xres +
+		mfd->panel_info.lcdc.xres_pad;
+	dsi_height = mfd->panel_info.yres +
+		mfd->panel_info.lcdc.yres_pad;
+	dsi_bpp = mfd->panel_info.bpp;
+
+	hsync_period = hsync_pulse_width + h_back_porch + dsi_width
+				+ h_front_porch;
+	hsync_ctrl = (hsync_period << 16) | hsync_pulse_width;
+	hsync_start_x = h_back_porch + hsync_pulse_width;
+	hsync_end_x = hsync_period - h_front_porch - 1;
+	display_hctl = (hsync_end_x << 16) | hsync_start_x;
+
+	vsync_period =
+	    (vsync_pulse_width + v_back_porch + dsi_height + v_front_porch);
+	display_v_start = ((vsync_pulse_width + v_back_porch) * hsync_period)
+				+ dsi_hsync_skew;
+	display_v_end =
+	  ((vsync_period - v_front_porch) * hsync_period) + dsi_hsync_skew - 1;
+
+	if (dsi_width != var->xres) {
+		active_h_start = hsync_start_x + first_pixel_start_x;
+		active_h_end = active_h_start + var->xres - 1;
+		active_hctl =
+		    ACTIVE_START_X_EN | (active_h_end << 16) | active_h_start;
+	} else {
+		active_hctl = 0;
+	}
+
+	if (dsi_height != var->yres) {
+		active_v_start =
+		    display_v_start + first_pixel_start_y * hsync_period;
+		active_v_end = active_v_start + (var->yres) * hsync_period - 1;
+		active_v_start |= ACTIVE_START_Y_EN;
+	} else {
+		active_v_start = 0;
+		active_v_end = 0;
+	}
+
+	dsi_underflow_clr |= 0x80000000;	/* enable recovery */
+	hsync_polarity = 0;
+	vsync_polarity = 0;
+	data_en_polarity = 0;
+
+	ctrl_polarity =
+	    (data_en_polarity << 2) | (vsync_polarity << 1) | (hsync_polarity);
+
+	MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x4, hsync_ctrl);
+	MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x8, vsync_period * hsync_period);
+	MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0xc,
+				vsync_pulse_width * hsync_period);
+	MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x10, display_hctl);
+	MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x14, display_v_start);
+	MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x18, display_v_end);
+	MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x1c, active_hctl);
+	MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x20, active_v_start);
+	MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x24, active_v_end);
+	MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x28, dsi_border_clr);
+	MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x2c, dsi_underflow_clr);
+	MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x30, dsi_hsync_skew);
+	MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x38, ctrl_polarity);
+	mdp4_overlay_reg_flush(pipe, 1);
+	mdp4_mixer_stage_up(pipe);
+
+	mdp_histogram_ctrl_all(TRUE);
+
+	ret = panel_next_on(pdev);
+	if (ret == 0) {
+		if (display_on != NULL) {
+			msleep(50);
+			display_on(pdev);
+		}
+	}
+	/* MDP cmd block disable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	return ret;
+}
+
+int mdp4_dsi_video_off(struct platform_device *pdev)
+{
+	int ret = 0;
+
+	/* MDP cmd block enable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	mdp4_mixer_pipe_cleanup(dsi_pipe->mixer_num);
+	MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE, 0);
+	dsi_video_enabled = 0;
+	/* MDP cmd block disable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+	mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+	mdp_histogram_ctrl_all(FALSE);
+	ret = panel_next_off(pdev);
+
+	/* delay to make sure the last frame finishes */
+	msleep(20);
+
+	/* dis-engage rgb0 from mixer0 */
+	if (dsi_pipe) {
+		mdp4_mixer_stage_down(dsi_pipe);
+		mdp4_iommu_unmap(dsi_pipe);
+	}
+
+	return ret;
+}
+
+/* 3D side by side */
+void mdp4_dsi_video_3d_sbys(struct msm_fb_data_type *mfd,
+				struct msmfb_overlay_3d *r3d)
+{
+	struct fb_info *fbi;
+	struct mdp4_overlay_pipe *pipe;
+	unsigned int buf_offset;
+	int bpp;
+	uint8 *buf = NULL;
+
+	if (dsi_pipe == NULL)
+		return;
+
+	dsi_pipe->is_3d = r3d->is_3d;
+	dsi_pipe->src_height_3d = r3d->height;
+	dsi_pipe->src_width_3d = r3d->width;
+
+	pipe = dsi_pipe;
+
+	if (pipe->is_3d)
+		mdp4_overlay_panel_3d(pipe->mixer_num, MDP4_3D_SIDE_BY_SIDE);
+	else
+		mdp4_overlay_panel_3d(pipe->mixer_num, MDP4_3D_NONE);
+
+	fbi = mfd->fbi;
+
+	bpp = fbi->var.bits_per_pixel / 8;
+	buf = (uint8 *) fbi->fix.smem_start;
+	buf_offset = calc_fb_offset(mfd, fbi, bpp);
+
+	if (pipe->is_3d) {
+		pipe->src_height = pipe->src_height_3d;
+		pipe->src_width = pipe->src_width_3d;
+		pipe->src_h = pipe->src_height_3d;
+		pipe->src_w = pipe->src_width_3d;
+		pipe->dst_h = pipe->src_height_3d;
+		pipe->dst_w = pipe->src_width_3d;
+		pipe->srcp0_ystride = msm_fb_line_length(0,
+					pipe->src_width, bpp);
+	} else {
+		 /* 2D */
+		pipe->src_height = fbi->var.yres;
+		pipe->src_width = fbi->var.xres;
+		pipe->src_h = fbi->var.yres;
+		pipe->src_w = fbi->var.xres;
+		pipe->dst_h = fbi->var.yres;
+		pipe->dst_w = fbi->var.xres;
+		pipe->srcp0_ystride = fbi->fix.line_length;
+	}
+
+	pipe->src_y = 0;
+	pipe->src_x = 0;
+	pipe->dst_y = 0;
+	pipe->dst_x = 0;
+
+	if (mfd->display_iova)
+		pipe->srcp0_addr = mfd->display_iova + buf_offset;
+	else
+		pipe->srcp0_addr = (uint32)(buf + buf_offset);
+
+	mdp4_overlay_rgb_setup(pipe);
+
+	mdp4_overlayproc_cfg(pipe);
+
+	mdp4_overlay_dmap_xy(pipe);
+
+	mdp4_overlay_dmap_cfg(mfd, 1);
+
+	mdp4_overlay_reg_flush(pipe, 1);
+
+	mdp4_mixer_stage_up(pipe);
+
+	mb();
+
+	/* wait for vsycn */
+	mdp4_overlay_dsi_video_vsync_push(mfd, pipe);
+}
+
+static void mdp4_dsi_video_blt_ov_update(struct mdp4_overlay_pipe *pipe)
+{
+	uint32 off, addr;
+	int bpp;
+	char *overlay_base;
+
+
+	if (pipe->blt_addr == 0)
+		return;
+
+
+#ifdef BLT_RGB565
+	bpp = 2; /* overlay ouput is RGB565 */
+#else
+	bpp = 3; /* overlay ouput is RGB888 */
+#endif
+	off = 0;
+	if (pipe->ov_cnt & 0x01)
+		off = pipe->src_height * pipe->src_width * bpp;
+	addr = pipe->blt_addr + off;
+
+	/* overlay 0 */
+	overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */
+	outpdw(overlay_base + 0x000c, addr);
+	outpdw(overlay_base + 0x001c, addr);
+}
+
+static void mdp4_dsi_video_blt_dmap_update(struct mdp4_overlay_pipe *pipe)
+{
+	uint32 off, addr;
+	int bpp;
+
+	if (pipe->blt_addr == 0)
+		return;
+
+
+#ifdef BLT_RGB565
+	bpp = 2; /* overlay ouput is RGB565 */
+#else
+	bpp = 3; /* overlay ouput is RGB888 */
+#endif
+	off = 0;
+	if (pipe->dmap_cnt & 0x01)
+		off = pipe->src_height * pipe->src_width * bpp;
+	addr = pipe->blt_addr + off;
+
+	/* dmap */
+	MDP_OUTP(MDP_BASE + 0x90008, addr);
+}
+
+/*
+ * mdp4_overlay_dsi_video_wait4event:
+ * INTR_DMA_P_DONE and INTR_PRIMARY_VSYNC event only
+ * no INTR_OVERLAY0_DONE event allowed.
+ */
+static void mdp4_overlay_dsi_video_wait4event(struct msm_fb_data_type *mfd,
+						int intr_done)
+{
+	unsigned long flag;
+	unsigned int data;
+
+	data = inpdw(MDP_BASE + DSI_VIDEO_BASE);
+	data &= 0x01;
+	if (data == 0)	/* timing generator disabled */
+		return;
+
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	INIT_COMPLETION(dsi_video_comp);
+	mfd->dma->waiting = TRUE;
+	outp32(MDP_INTR_CLEAR, intr_done);
+	mdp_intr_mask |= intr_done;
+	outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+	mdp_enable_irq(MDP_DMA2_TERM);  /* enable intr */
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+	wait_for_completion(&dsi_video_comp);
+	mdp_disable_irq(MDP_DMA2_TERM);
+}
+
+static void mdp4_overlay_dsi_video_dma_busy_wait(struct msm_fb_data_type *mfd)
+{
+	unsigned long flag;
+	int need_wait = 0;
+
+	pr_debug("%s: start pid=%d\n", __func__, current->pid);
+
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	if (mfd->dma->busy == TRUE) {
+		INIT_COMPLETION(mfd->dma->comp);
+		need_wait++;
+	}
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+
+	if (need_wait) {
+		/* wait until DMA finishes the current job */
+		pr_debug("%s: pending pid=%d\n", __func__, current->pid);
+		wait_for_completion(&mfd->dma->comp);
+	}
+	pr_debug("%s: done pid=%d\n", __func__, current->pid);
+}
+
+void mdp4_overlay_dsi_video_start(void)
+{
+	if (!dsi_video_enabled) {
+		/* enable DSI block */
+		mdp4_iommu_attach();
+		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+		MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE, 1);
+		mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+		dsi_video_enabled = 1;
+	}
+}
+
+void mdp4_overlay_dsi_video_vsync_push(struct msm_fb_data_type *mfd,
+			struct mdp4_overlay_pipe *pipe)
+{
+	unsigned long flag;
+
+	if (pipe->flags & MDP_OV_PLAY_NOWAIT)
+		return;
+
+	if (dsi_pipe->blt_addr) {
+		mdp4_overlay_dsi_video_dma_busy_wait(mfd);
+
+		mdp4_dsi_video_blt_ov_update(dsi_pipe);
+		dsi_pipe->ov_cnt++;
+
+		spin_lock_irqsave(&mdp_spin_lock, flag);
+		outp32(MDP_INTR_CLEAR, INTR_OVERLAY0_DONE);
+		mdp_intr_mask |= INTR_OVERLAY0_DONE;
+		outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+		mdp_enable_irq(MDP_OVERLAY0_TERM);
+		mfd->dma->busy = TRUE;
+		mb();	/* make sure all registers updated */
+		spin_unlock_irqrestore(&mdp_spin_lock, flag);
+		outpdw(MDP_BASE + 0x0004, 0); /* kickoff overlay engine */
+		mdp4_stat.kickoff_ov0++;
+		mb();
+		mdp4_overlay_dsi_video_wait4event(mfd, INTR_DMA_P_DONE);
+	} else {
+		mdp4_overlay_dsi_video_wait4event(mfd, INTR_PRIMARY_VSYNC);
+	}
+
+	mdp4_set_perf_level();
+}
+
+/*
+ * mdp4_primary_vsync_dsi_video: called from isr
+ */
+void mdp4_primary_vsync_dsi_video(void)
+{
+	complete_all(&dsi_video_comp);
+}
+
+ /*
+ * mdp4_dma_p_done_dsi_video: called from isr
+ */
+void mdp4_dma_p_done_dsi_video(struct mdp_dma_data *dma)
+{
+	if (blt_cfg_changed) {
+		mdp_is_in_isr = TRUE;
+		mdp4_overlayproc_cfg(dsi_pipe);
+		mdp4_overlay_dmap_xy(dsi_pipe);
+		mdp_is_in_isr = FALSE;
+		if (dsi_pipe->blt_addr) {
+			mdp4_dsi_video_blt_ov_update(dsi_pipe);
+			dsi_pipe->ov_cnt++;
+			outp32(MDP_INTR_CLEAR, INTR_OVERLAY0_DONE);
+			mdp_intr_mask |= INTR_OVERLAY0_DONE;
+			outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+			dma->busy = TRUE;
+			mdp_enable_irq(MDP_OVERLAY0_TERM);
+			/* kickoff overlay engine */
+			outpdw(MDP_BASE + 0x0004, 0);
+		}
+		blt_cfg_changed = 0;
+	}
+	complete_all(&dsi_video_comp);
+}
+
+/*
+ * mdp4_overlay1_done_dsi: called from isr
+ */
+void mdp4_overlay0_done_dsi_video(struct mdp_dma_data *dma)
+{
+	spin_lock(&mdp_spin_lock);
+	dma->busy = FALSE;
+	if (dsi_pipe->blt_addr == 0) {
+		spin_unlock(&mdp_spin_lock);
+		return;
+	}
+	mdp4_dsi_video_blt_dmap_update(dsi_pipe);
+	dsi_pipe->dmap_cnt++;
+	mdp_disable_irq_nosync(MDP_OVERLAY0_TERM);
+	spin_unlock(&mdp_spin_lock);
+	complete(&dma->comp);
+}
+
+/*
+ * make sure the MIPI_DSI_WRITEBACK_SIZE defined at boardfile
+ * has enough space h * w * 3 * 2
+ */
+static void mdp4_dsi_video_do_blt(struct msm_fb_data_type *mfd, int enable)
+{
+	unsigned long flag;
+	int data;
+	int change = 0;
+
+	mdp4_allocate_writeback_buf(mfd, MDP4_MIXER0);
+
+	if (mfd->ov0_wb_buf->phys_addr == 0) {
+		pr_info("%s: no blt_base assigned\n", __func__);
+		return;
+	}
+
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	if (enable && dsi_pipe->blt_addr == 0) {
+		dsi_pipe->blt_addr = mfd->ov0_wb_buf->phys_addr;
+		dsi_pipe->blt_cnt = 0;
+		dsi_pipe->ov_cnt = 0;
+		dsi_pipe->dmap_cnt = 0;
+		mdp4_stat.blt_dsi_video++;
+		change++;
+	} else if (enable == 0 && dsi_pipe->blt_addr) {
+		dsi_pipe->blt_addr = 0;
+		change++;
+	}
+
+	if (!change) {
+		spin_unlock_irqrestore(&mdp_spin_lock, flag);
+		return;
+	}
+
+	pr_debug("%s: enable=%d blt_addr=%x\n", __func__,
+			enable, (int)dsi_pipe->blt_addr);
+	blt_cfg_changed = 1;
+
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+
+
+	/*
+	 * may need mutex here to sync with whom dsiable
+	 * timing generator
+	 */
+	data = inpdw(MDP_BASE + DSI_VIDEO_BASE);
+	data &= 0x01;
+	if (data) {	/* timing generator enabled */
+		mdp4_overlay_dsi_video_wait4event(mfd, INTR_DMA_P_DONE);
+		mdp4_overlay_dsi_video_wait4event(mfd, INTR_PRIMARY_VSYNC);
+	}
+
+
+}
+
+int mdp4_dsi_video_overlay_blt_offset(struct msm_fb_data_type *mfd,
+					struct msmfb_overlay_blt *req)
+{
+	req->offset = 0;
+	req->width = dsi_pipe->src_width;
+	req->height = dsi_pipe->src_height;
+	req->bpp = dsi_pipe->bpp;
+
+	return sizeof(*req);
+}
+
+void mdp4_dsi_video_overlay_blt(struct msm_fb_data_type *mfd,
+					struct msmfb_overlay_blt *req)
+{
+	mdp4_dsi_video_do_blt(mfd, req->enable);
+}
+
+void mdp4_dsi_video_blt_start(struct msm_fb_data_type *mfd)
+{
+	mdp4_dsi_video_do_blt(mfd, 1);
+}
+
+void mdp4_dsi_video_blt_stop(struct msm_fb_data_type *mfd)
+{
+	mdp4_dsi_video_do_blt(mfd, 0);
+}
+
+void mdp4_dsi_video_overlay(struct msm_fb_data_type *mfd)
+{
+	struct fb_info *fbi = mfd->fbi;
+	uint8 *buf;
+	unsigned int buf_offset;
+	int bpp;
+	struct mdp4_overlay_pipe *pipe;
+
+	if (!mfd->panel_power_on)
+		return;
+
+	/* no need to power on cmd block since it's dsi video mode */
+	bpp = fbi->var.bits_per_pixel / 8;
+	buf = (uint8 *) fbi->fix.smem_start;
+	buf_offset = calc_fb_offset(mfd, fbi, bpp);
+
+	mutex_lock(&mfd->dma->ov_mutex);
+
+	pipe = dsi_pipe;
+
+	if (mfd->display_iova)
+		pipe->srcp0_addr = mfd->display_iova + buf_offset;
+	else
+		pipe->srcp0_addr = (uint32)(buf + buf_offset);
+
+	mdp4_overlay_rgb_setup(pipe);
+	mdp4_overlay_reg_flush(pipe, 0);
+	mdp4_mixer_stage_up(pipe);
+	mdp4_overlay_dsi_video_start();
+	mdp4_overlay_dsi_video_vsync_push(mfd, pipe);
+	mdp4_iommu_unmap(pipe);
+	mutex_unlock(&mfd->dma->ov_mutex);
+}
diff --git a/drivers/video/msm/mdp4_overlay_dtv.c b/drivers/video/msm/mdp4_overlay_dtv.c
new file mode 100644
index 0000000..dd96439
--- /dev/null
+++ b/drivers/video/msm/mdp4_overlay_dtv.c
@@ -0,0 +1,709 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/hrtimer.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/semaphore.h>
+#include <linux/spinlock.h>
+#include <linux/fb.h>
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+
+#include "mdp.h"
+#include "msm_fb.h"
+#include "mdp4.h"
+
+#define DTV_BASE	0xD0000
+
+/*#define DEBUG*/
+#ifdef DEBUG
+static void __mdp_outp(uint32 port, uint32 value)
+{
+	uint32 in_val;
+
+	outpdw(port, value);
+	in_val = inpdw(port);
+	printk(KERN_INFO "MDP-DTV[%04x] => %08x [%08x]\n",
+		port-(uint32)(MDP_BASE + DTV_BASE), value, in_val);
+}
+
+#undef MDP_OUTP
+#define MDP_OUTP(port, value)	__mdp_outp((uint32)(port), (value))
+#endif
+
+static int first_pixel_start_x;
+static int first_pixel_start_y;
+static int dtv_enabled;
+
+static struct mdp4_overlay_pipe *dtv_pipe;
+static DECLARE_COMPLETION(dtv_comp);
+
+static int mdp4_dtv_start(struct msm_fb_data_type *mfd)
+{
+	int dtv_width;
+	int dtv_height;
+	int dtv_bpp;
+	int dtv_border_clr;
+	int dtv_underflow_clr;
+	int dtv_hsync_skew;
+
+	int hsync_period;
+	int hsync_ctrl;
+	int vsync_period;
+	int display_hctl;
+	int display_v_start;
+	int display_v_end;
+	int active_hctl;
+	int active_h_start;
+	int active_h_end;
+	int active_v_start;
+	int active_v_end;
+	int ctrl_polarity;
+	int h_back_porch;
+	int h_front_porch;
+	int v_back_porch;
+	int v_front_porch;
+	int hsync_pulse_width;
+	int vsync_pulse_width;
+	int hsync_polarity;
+	int vsync_polarity;
+	int data_en_polarity;
+	int hsync_start_x;
+	int hsync_end_x;
+	struct fb_info *fbi;
+	struct fb_var_screeninfo *var;
+
+	if (!mfd)
+		return -ENODEV;
+
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	if (dtv_pipe == NULL)
+		return -EINVAL;
+
+	fbi = mfd->fbi;
+	var = &fbi->var;
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	if (hdmi_prim_display) {
+		if (is_mdp4_hw_reset()) {
+			mdp4_hw_init();
+			outpdw(MDP_BASE + 0x0038, mdp4_display_intf);
+		}
+	}
+	mdp4_overlay_dmae_cfg(mfd, 0);
+
+	/*
+	 * DTV timing setting
+	 */
+	h_back_porch = var->left_margin;
+	h_front_porch = var->right_margin;
+	v_back_porch = var->upper_margin;
+	v_front_porch = var->lower_margin;
+	hsync_pulse_width = var->hsync_len;
+	vsync_pulse_width = var->vsync_len;
+	dtv_border_clr = mfd->panel_info.lcdc.border_clr;
+	dtv_underflow_clr = mfd->panel_info.lcdc.underflow_clr;
+	dtv_hsync_skew = mfd->panel_info.lcdc.hsync_skew;
+
+	pr_info("%s: <ID=%d %dx%d (%d,%d,%d), (%d,%d,%d) %dMHz>\n", __func__,
+		var->reserved[3], var->xres, var->yres,
+		var->right_margin, var->hsync_len, var->left_margin,
+		var->lower_margin, var->vsync_len, var->upper_margin,
+		var->pixclock/1000/1000);
+
+	dtv_width = var->xres;
+	dtv_height = var->yres;
+	dtv_bpp = mfd->panel_info.bpp;
+
+	hsync_period =
+	    hsync_pulse_width + h_back_porch + dtv_width + h_front_porch;
+	hsync_ctrl = (hsync_period << 16) | hsync_pulse_width;
+	hsync_start_x = hsync_pulse_width + h_back_porch;
+	hsync_end_x = hsync_period - h_front_porch - 1;
+	display_hctl = (hsync_end_x << 16) | hsync_start_x;
+
+	vsync_period =
+	    (vsync_pulse_width + v_back_porch + dtv_height +
+	     v_front_porch) * hsync_period;
+	display_v_start =
+	    (vsync_pulse_width + v_back_porch) * hsync_period + dtv_hsync_skew;
+	display_v_end =
+	    vsync_period - (v_front_porch * hsync_period) + dtv_hsync_skew - 1;
+
+	if (dtv_width != var->xres) {
+		active_h_start = hsync_start_x + first_pixel_start_x;
+		active_h_end = active_h_start + var->xres - 1;
+		active_hctl =
+		    ACTIVE_START_X_EN | (active_h_end << 16) | active_h_start;
+	} else {
+		active_hctl = 0;
+	}
+
+	if (dtv_height != var->yres) {
+		active_v_start =
+		    display_v_start + first_pixel_start_y * hsync_period;
+		active_v_end = active_v_start + (var->yres) * hsync_period - 1;
+		active_v_start |= ACTIVE_START_Y_EN;
+	} else {
+		active_v_start = 0;
+		active_v_end = 0;
+	}
+
+	dtv_underflow_clr |= 0x80000000;	/* enable recovery */
+	hsync_polarity = fbi->var.yres >= 720 ? 0 : 1;
+	vsync_polarity = fbi->var.yres >= 720 ? 0 : 1;
+	data_en_polarity = 0;
+
+	ctrl_polarity =
+	    (data_en_polarity << 2) | (vsync_polarity << 1) | (hsync_polarity);
+
+
+	MDP_OUTP(MDP_BASE + DTV_BASE + 0x4, hsync_ctrl);
+	MDP_OUTP(MDP_BASE + DTV_BASE + 0x8, vsync_period);
+	MDP_OUTP(MDP_BASE + DTV_BASE + 0xc, vsync_pulse_width * hsync_period);
+	MDP_OUTP(MDP_BASE + DTV_BASE + 0x18, display_hctl);
+	MDP_OUTP(MDP_BASE + DTV_BASE + 0x1c, display_v_start);
+	MDP_OUTP(MDP_BASE + DTV_BASE + 0x20, display_v_end);
+	MDP_OUTP(MDP_BASE + DTV_BASE + 0x40, dtv_border_clr);
+	MDP_OUTP(MDP_BASE + DTV_BASE + 0x44, dtv_underflow_clr);
+	MDP_OUTP(MDP_BASE + DTV_BASE + 0x48, dtv_hsync_skew);
+	MDP_OUTP(MDP_BASE + DTV_BASE + 0x50, ctrl_polarity);
+	MDP_OUTP(MDP_BASE + DTV_BASE + 0x2c, active_hctl);
+	MDP_OUTP(MDP_BASE + DTV_BASE + 0x30, active_v_start);
+	MDP_OUTP(MDP_BASE + DTV_BASE + 0x38, active_v_end);
+
+	/* Test pattern 8 x 8 pixel */
+	/* MDP_OUTP(MDP_BASE + DTV_BASE + 0x4C, 0x80000808); */
+
+	/* MDP cmd block disable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	return 0;
+}
+
+static int mdp4_dtv_stop(struct msm_fb_data_type *mfd)
+{
+	if (dtv_pipe == NULL)
+		return -EINVAL;
+
+	/* MDP cmd block enable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	mdp4_mixer_pipe_cleanup(dtv_pipe->mixer_num);
+	msleep(20);
+	MDP_OUTP(MDP_BASE + DTV_BASE, 0);
+	dtv_enabled = 0;
+	/* MDP cmd block disable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+	mdp_pipe_ctrl(MDP_OVERLAY1_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	return 0;
+}
+
+int mdp4_dtv_on(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+	int ret = 0;
+
+	mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
+
+	if (!mfd)
+		return -ENODEV;
+
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	mdp_footswitch_ctrl(TRUE);
+	mdp4_overlay_panel_mode(MDP4_MIXER1, MDP4_PANEL_DTV);
+
+	/* Allocate dtv_pipe at dtv_on*/
+	if (dtv_pipe == NULL) {
+		if (mdp4_overlay_dtv_set(mfd, NULL)) {
+			pr_warn("%s: dtv_pipe is NULL, dtv_set failed\n",
+				__func__);
+			return -EINVAL;
+		}
+	}
+
+	ret = panel_next_on(pdev);
+	if (ret != 0)
+		dev_warn(&pdev->dev, "mdp4_overlay_dtv: panel_next_on failed");
+
+	dev_info(&pdev->dev, "mdp4_overlay_dtv: on");
+
+	return ret;
+}
+
+int mdp4_dtv_off(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+	int ret = 0;
+
+	mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
+
+	if (dtv_pipe != NULL) {
+		mdp4_dtv_stop(mfd);
+
+		/* delay to make sure the last frame finishes */
+		msleep(20);
+
+		mdp4_mixer_stage_down(dtv_pipe);
+		mdp4_overlay_pipe_free(dtv_pipe);
+		mdp4_iommu_unmap(dtv_pipe);
+		dtv_pipe = NULL;
+	}
+	mdp4_overlay_panel_mode_unset(MDP4_MIXER1, MDP4_PANEL_DTV);
+
+	ret = panel_next_off(pdev);
+	mdp4_iommu_detach();
+	mdp_footswitch_ctrl(FALSE);
+
+	dev_info(&pdev->dev, "mdp4_overlay_dtv: off");
+	return ret;
+}
+
+static void mdp4_overlay_dtv_alloc_pipe(struct msm_fb_data_type *mfd,
+		int32 ptype)
+{
+	int ret = 0;
+	struct fb_info *fbi = mfd->fbi;
+	struct mdp4_overlay_pipe *pipe;
+
+	if (dtv_pipe != NULL)
+		return;
+
+	pr_debug("%s: ptype=%d\n", __func__, ptype);
+
+	pipe = mdp4_overlay_pipe_alloc(ptype, MDP4_MIXER1);
+	if (pipe == NULL) {
+		pr_err("%s: pipe_alloc failed\n", __func__);
+		return;
+	}
+	pipe->pipe_used++;
+	pipe->mixer_stage = MDP4_MIXER_STAGE_BASE;
+	pipe->mixer_num = MDP4_MIXER1;
+
+	if (ptype == OVERLAY_TYPE_BF) {
+		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+		/* LSP_BORDER_COLOR */
+		MDP_OUTP(MDP_BASE + MDP4_OVERLAYPROC1_BASE + 0x5004,
+			((0x0 & 0xFFF) << 16) |	/* 12-bit B */
+			(0x0 & 0xFFF));		/* 12-bit G */
+		/* MSP_BORDER_COLOR */
+		MDP_OUTP(MDP_BASE + MDP4_OVERLAYPROC1_BASE + 0x5008,
+			(0x0 & 0xFFF));		/* 12-bit R */
+		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+	} else {
+		switch (mfd->ibuf.bpp) {
+		case 2:
+			pipe->src_format = MDP_RGB_565;
+			break;
+		case 3:
+			pipe->src_format = MDP_RGB_888;
+			break;
+		case 4:
+		default:
+			if (hdmi_prim_display)
+				pipe->src_format = MSMFB_DEFAULT_TYPE;
+			else
+				pipe->src_format = MDP_ARGB_8888;
+			break;
+		}
+	}
+
+	pipe->src_height = fbi->var.yres;
+	pipe->src_width = fbi->var.xres;
+	pipe->src_h = fbi->var.yres;
+	pipe->src_w = fbi->var.xres;
+	pipe->src_y = 0;
+	pipe->src_x = 0;
+	pipe->srcp0_ystride = fbi->fix.line_length;
+
+	ret = mdp4_overlay_format2pipe(pipe);
+	if (ret < 0)
+		pr_warn("%s: format2type failed\n", __func__);
+
+	mdp4_overlay_dmae_xy(pipe);	/* dma_e */
+	mdp4_overlayproc_cfg(pipe);
+
+	if (pipe->pipe_type == OVERLAY_TYPE_RGB) {
+		pipe->srcp0_addr = (uint32) mfd->ibuf.buf;
+		mdp4_overlay_rgb_setup(pipe);
+	}
+
+	mdp4_overlay_reg_flush(pipe, 1);
+	mdp4_mixer_stage_up(pipe);
+
+	dtv_pipe = pipe; /* keep it */
+}
+
+int mdp4_overlay_dtv_set(struct msm_fb_data_type *mfd,
+			struct mdp4_overlay_pipe *pipe)
+{
+	if (dtv_pipe != NULL)
+		return 0;
+
+	if (pipe != NULL && pipe->mixer_stage == MDP4_MIXER_STAGE_BASE &&
+			pipe->pipe_type == OVERLAY_TYPE_RGB)
+		dtv_pipe = pipe; /* keep it */
+	else if (!hdmi_prim_display && mdp4_overlay_borderfill_supported())
+		mdp4_overlay_dtv_alloc_pipe(mfd, OVERLAY_TYPE_BF);
+	else
+		mdp4_overlay_dtv_alloc_pipe(mfd, OVERLAY_TYPE_RGB);
+	if (dtv_pipe == NULL)
+		return -ENODEV;
+
+	mdp4_init_writeback_buf(mfd, MDP4_MIXER1);
+	dtv_pipe->blt_addr = 0;
+
+	return mdp4_dtv_start(mfd);
+}
+
+int mdp4_overlay_dtv_unset(struct msm_fb_data_type *mfd,
+			struct mdp4_overlay_pipe *pipe)
+{
+	int result = 0;
+
+	if (dtv_pipe == NULL)
+		return result;
+
+	pipe->flags &= ~MDP_OV_PLAY_NOWAIT;
+	mdp4_overlay_reg_flush(pipe, 0);
+	mdp4_overlay_dtv_ov_done_push(mfd, pipe);
+
+	if (pipe->mixer_stage == MDP4_MIXER_STAGE_BASE &&
+			pipe->pipe_type == OVERLAY_TYPE_RGB) {
+		result = mdp4_dtv_stop(mfd);
+		dtv_pipe = NULL;
+	}
+	return result;
+}
+
+static void mdp4_dtv_blt_ov_update(struct mdp4_overlay_pipe *pipe)
+{
+	uint32 off, addr;
+	int bpp;
+	char *overlay_base;
+
+	if (pipe->blt_addr == 0)
+		return;
+#ifdef BLT_RGB565
+	bpp = 2; /* overlay ouput is RGB565 */
+#else
+	bpp = 3; /* overlay ouput is RGB888 */
+#endif
+	off = (pipe->ov_cnt & 0x01) ?
+		pipe->src_height * pipe->src_width * bpp : 0;
+
+	addr = pipe->blt_addr + off;
+	pr_debug("%s overlay addr 0x%x\n", __func__, addr);
+	/* overlay 1 */
+	overlay_base = MDP_BASE + MDP4_OVERLAYPROC1_BASE;/* 0x18000 */
+	outpdw(overlay_base + 0x000c, addr);
+	outpdw(overlay_base + 0x001c, addr);
+}
+
+static inline void mdp4_dtv_blt_dmae_update(struct mdp4_overlay_pipe *pipe)
+{
+	uint32 off, addr;
+	int bpp;
+
+	if (pipe->blt_addr == 0)
+		return;
+
+#ifdef BLT_RGB565
+	bpp = 2; /* overlay ouput is RGB565 */
+#else
+	bpp = 3; /* overlay ouput is RGB888 */
+#endif
+	off =  (pipe->dmae_cnt & 0x01) ?
+		pipe->src_height * pipe->src_width * bpp : 0;
+	addr = pipe->blt_addr + off;
+	MDP_OUTP(MDP_BASE + 0xb0008, addr);
+}
+
+static inline void mdp4_overlay_dtv_ov_kick_start(void)
+{
+	outpdw(MDP_BASE + 0x0008, 0);
+}
+
+static void mdp4_overlay_dtv_ov_start(struct msm_fb_data_type *mfd)
+{
+	unsigned long flag;
+
+	/* enable irq */
+	if (mfd->ov_start)
+		return;
+
+	if (!dtv_pipe) {
+		pr_debug("%s: no mixer1 base layer pipe allocated!\n",
+			 __func__);
+		return;
+	}
+
+	if (dtv_pipe->blt_addr) {
+		mdp4_dtv_blt_ov_update(dtv_pipe);
+		dtv_pipe->ov_cnt++;
+		mdp4_overlay_dtv_ov_kick_start();
+	}
+
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	mdp_enable_irq(MDP_OVERLAY1_TERM);
+	INIT_COMPLETION(dtv_pipe->comp);
+	mfd->dma->waiting = TRUE;
+	outp32(MDP_INTR_CLEAR, INTR_OVERLAY1_DONE);
+	mdp_intr_mask |= INTR_OVERLAY1_DONE;
+	outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+	mfd->ov_start = true;
+}
+
+static void mdp4_overlay_dtv_wait4dmae(struct msm_fb_data_type *mfd)
+{
+	unsigned long flag;
+
+	if (!dtv_pipe) {
+		pr_debug("%s: no mixer1 base layer pipe allocated!\n",
+			 __func__);
+		return;
+	}
+	/* enable irq */
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	mdp_enable_irq(MDP_DMA_E_TERM);
+	INIT_COMPLETION(dtv_pipe->comp);
+	mfd->dma->waiting = TRUE;
+	outp32(MDP_INTR_CLEAR, INTR_DMA_E_DONE);
+	mdp_intr_mask |= INTR_DMA_E_DONE;
+	outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+	wait_for_completion_killable(&dtv_pipe->comp);
+	mdp_disable_irq(MDP_DMA_E_TERM);
+}
+
+static void mdp4_overlay_dtv_wait4_ov_done(struct msm_fb_data_type *mfd,
+	struct mdp4_overlay_pipe *pipe)
+{
+	u32 data = inpdw(MDP_BASE + DTV_BASE);
+
+	if (mfd->ov_start)
+		mfd->ov_start = false;
+	else
+		return;
+	if (!(data & 0x1) || (pipe == NULL))
+		return;
+	if (!dtv_pipe) {
+		pr_debug("%s: no mixer1 base layer pipe allocated!\n",
+			 __func__);
+		return;
+	}
+
+	wait_for_completion_timeout(&dtv_pipe->comp,
+			msecs_to_jiffies(VSYNC_PERIOD*2));
+	mdp_disable_irq(MDP_OVERLAY1_TERM);
+
+	if (dtv_pipe->blt_addr)
+		mdp4_overlay_dtv_wait4dmae(mfd);
+}
+
+void mdp4_overlay_dtv_start(void)
+{
+	if (!dtv_enabled) {
+		mdp4_iommu_attach();
+		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+		/* enable DTV block */
+		MDP_OUTP(MDP_BASE + DTV_BASE, 1);
+		mdp_pipe_ctrl(MDP_OVERLAY1_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+		dtv_enabled = 1;
+	}
+}
+
+void mdp4_overlay_dtv_ov_done_push(struct msm_fb_data_type *mfd,
+			struct mdp4_overlay_pipe *pipe)
+{
+	mdp4_overlay_dtv_ov_start(mfd);
+	if (pipe->flags & MDP_OV_PLAY_NOWAIT)
+		return;
+
+	mdp4_overlay_dtv_wait4_ov_done(mfd, pipe);
+
+	/* change mdp clk while mdp is idle` */
+	mdp4_set_perf_level();
+}
+
+void mdp4_overlay_dtv_wait_for_ov(struct msm_fb_data_type *mfd,
+	struct mdp4_overlay_pipe *pipe)
+{
+	mdp4_overlay_dtv_wait4_ov_done(mfd, pipe);
+	mdp4_set_perf_level();
+}
+
+void mdp4_dma_e_done_dtv()
+{
+	if (!dtv_pipe)
+		return;
+
+	complete(&dtv_pipe->comp);
+}
+
+void mdp4_external_vsync_dtv()
+{
+
+	complete_all(&dtv_comp);
+}
+
+/*
+ * mdp4_overlay1_done_dtv: called from isr
+ */
+void mdp4_overlay1_done_dtv()
+{
+	if (!dtv_pipe)
+		return;
+	if (dtv_pipe->blt_addr) {
+		mdp4_dtv_blt_dmae_update(dtv_pipe);
+		dtv_pipe->dmae_cnt++;
+	}
+	complete_all(&dtv_pipe->comp);
+}
+
+void mdp4_dtv_set_black_screen(void)
+{
+	char *rgb_base;
+	/*Black color*/
+	uint32 color = 0x00000000;
+	uint32 temp_src_format;
+
+	if (!dtv_pipe || !hdmi_prim_display) {
+		pr_err("dtv_pipe/hdmi as primary are not"
+			   " configured yet\n");
+		return;
+	}
+	rgb_base = MDP_BASE + MDP4_RGB_BASE;
+	rgb_base += (MDP4_RGB_OFF * dtv_pipe->pipe_num);
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	/*
+	* RGB Constant Color
+	*/
+	MDP_OUTP(rgb_base + 0x1008, color);
+	/*
+	* MDP_RGB_SRC_FORMAT
+	*/
+	temp_src_format = inpdw(rgb_base + 0x0050);
+	MDP_OUTP(rgb_base + 0x0050, temp_src_format | BIT(22));
+	mdp4_overlay_reg_flush(dtv_pipe, 0);
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+void mdp4_overlay_dtv_wait4vsync(void)
+{
+	unsigned long flag;
+
+	if (!dtv_enabled)
+		return;
+
+	/* enable irq */
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	mdp_enable_irq(MDP_DMA_E_TERM);
+	INIT_COMPLETION(dtv_comp);
+	outp32(MDP_INTR_CLEAR, INTR_EXTERNAL_VSYNC);
+	mdp_intr_mask |= INTR_EXTERNAL_VSYNC;
+	outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+	wait_for_completion_killable(&dtv_comp);
+	mdp_disable_irq(MDP_DMA_E_TERM);
+}
+
+static void mdp4_dtv_do_blt(struct msm_fb_data_type *mfd, int enable)
+{
+	unsigned long flag;
+	int change = 0;
+
+	if (!mfd->ov1_wb_buf->phys_addr) {
+		pr_debug("%s: no writeback buf assigned\n", __func__);
+		return;
+	}
+
+	if (!dtv_pipe) {
+		pr_debug("%s: no mixer1 base layer pipe allocated!\n",
+			 __func__);
+		return;
+	}
+
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	if (enable && dtv_pipe->blt_addr == 0) {
+		dtv_pipe->blt_addr = mfd->ov1_wb_buf->phys_addr;
+		change++;
+		dtv_pipe->ov_cnt = 0;
+		dtv_pipe->dmae_cnt = 0;
+	} else if (enable == 0 && dtv_pipe->blt_addr) {
+		dtv_pipe->blt_addr = 0;
+		change++;
+	}
+	pr_debug("%s: blt_addr=%x\n", __func__, (int)dtv_pipe->blt_addr);
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+
+	if (!change)
+		return;
+
+	mdp4_overlay_dtv_wait4dmae(mfd);
+
+	MDP_OUTP(MDP_BASE + DTV_BASE, 0);	/* stop dtv */
+	msleep(20);
+	mdp4_overlayproc_cfg(dtv_pipe);
+	mdp4_overlay_dmae_xy(dtv_pipe);
+	MDP_OUTP(MDP_BASE + DTV_BASE, 1);	/* start dtv */
+}
+
+void mdp4_dtv_overlay_blt_start(struct msm_fb_data_type *mfd)
+{
+	mdp4_dtv_do_blt(mfd, 1);
+}
+
+void mdp4_dtv_overlay_blt_stop(struct msm_fb_data_type *mfd)
+{
+	mdp4_dtv_do_blt(mfd, 0);
+}
+
+void mdp4_dtv_overlay(struct msm_fb_data_type *mfd)
+{
+	struct mdp4_overlay_pipe *pipe;
+	if (!mfd->panel_power_on)
+		return;
+	if (!dtv_pipe) {
+		pr_debug("%s: no mixer1 base layer pipe allocated!\n",
+			 __func__);
+		return;
+	}
+	mutex_lock(&mfd->dma->ov_mutex);
+	pipe = dtv_pipe;
+	if (pipe->pipe_type == OVERLAY_TYPE_RGB) {
+		pipe->srcp0_addr = (uint32) mfd->ibuf.buf;
+		mdp4_overlay_rgb_setup(pipe);
+	}
+	mdp4_overlay_reg_flush(pipe, 0);
+	mdp4_mixer_stage_up(pipe);
+	mdp4_overlay_dtv_start();
+	mdp4_overlay_dtv_ov_done_push(mfd, pipe);
+	mdp4_iommu_unmap(pipe);
+	mutex_unlock(&mfd->dma->ov_mutex);
+}
diff --git a/drivers/video/msm/mdp4_overlay_lcdc.c b/drivers/video/msm/mdp4_overlay_lcdc.c
new file mode 100644
index 0000000..a1fecb6
--- /dev/null
+++ b/drivers/video/msm/mdp4_overlay_lcdc.c
@@ -0,0 +1,601 @@
+/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/hrtimer.h>
+#include <linux/delay.h>
+#include <mach/hardware.h>
+#include <linux/io.h>
+
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <linux/semaphore.h>
+#include <linux/spinlock.h>
+
+#include <linux/fb.h>
+
+#include "mdp.h"
+#include "msm_fb.h"
+#include "mdp4.h"
+
+#ifdef CONFIG_FB_MSM_MDP40
+#define LCDC_BASE	0xC0000
+#else
+#define LCDC_BASE	0xE0000
+#endif
+
+int first_pixel_start_x;
+int first_pixel_start_y;
+static int lcdc_enabled;
+
+static struct mdp4_overlay_pipe *lcdc_pipe;
+static struct completion lcdc_comp;
+
+int mdp_lcdc_on(struct platform_device *pdev)
+{
+	int lcdc_width;
+	int lcdc_height;
+	int lcdc_bpp;
+	int lcdc_border_clr;
+	int lcdc_underflow_clr;
+	int lcdc_hsync_skew;
+
+	int hsync_period;
+	int hsync_ctrl;
+	int vsync_period;
+	int display_hctl;
+	int display_v_start;
+	int display_v_end;
+	int active_hctl;
+	int active_h_start;
+	int active_h_end;
+	int active_v_start;
+	int active_v_end;
+	int ctrl_polarity;
+	int h_back_porch;
+	int h_front_porch;
+	int v_back_porch;
+	int v_front_porch;
+	int hsync_pulse_width;
+	int vsync_pulse_width;
+	int hsync_polarity;
+	int vsync_polarity;
+	int data_en_polarity;
+	int hsync_start_x;
+	int hsync_end_x;
+	uint8 *buf;
+	unsigned int buf_offset;
+	int bpp, ptype;
+	struct fb_info *fbi;
+	struct fb_var_screeninfo *var;
+	struct msm_fb_data_type *mfd;
+	struct mdp4_overlay_pipe *pipe;
+	int ret;
+
+	mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
+
+	if (!mfd)
+		return -ENODEV;
+
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	mdp4_overlay_ctrl_db_reset();
+
+	fbi = mfd->fbi;
+	var = &fbi->var;
+
+	/* MDP cmd block enable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	if (is_mdp4_hw_reset()) {
+		mdp4_hw_init();
+		outpdw(MDP_BASE + 0x0038, mdp4_display_intf);
+	}
+
+	bpp = fbi->var.bits_per_pixel / 8;
+	buf = (uint8 *) fbi->fix.smem_start;
+	buf_offset = calc_fb_offset(mfd, fbi, bpp);
+
+	if (lcdc_pipe == NULL) {
+		ptype = mdp4_overlay_format2type(mfd->fb_imgType);
+		if (ptype < 0)
+			printk(KERN_INFO "%s: format2type failed\n", __func__);
+		pipe = mdp4_overlay_pipe_alloc(ptype, MDP4_MIXER0);
+		if (pipe == NULL)
+			printk(KERN_INFO "%s: pipe_alloc failed\n", __func__);
+		pipe->pipe_used++;
+		pipe->mixer_stage  = MDP4_MIXER_STAGE_BASE;
+		pipe->mixer_num  = MDP4_MIXER0;
+		pipe->src_format = mfd->fb_imgType;
+		mdp4_overlay_panel_mode(pipe->mixer_num, MDP4_PANEL_LCDC);
+		ret = mdp4_overlay_format2pipe(pipe);
+		if (ret < 0)
+			printk(KERN_INFO "%s: format2pipe failed\n", __func__);
+		lcdc_pipe = pipe; /* keep it */
+		init_completion(&lcdc_comp);
+
+		mdp4_init_writeback_buf(mfd, MDP4_MIXER0);
+		pipe->blt_addr = 0;
+
+	} else {
+		pipe = lcdc_pipe;
+	}
+
+	pipe->src_height = fbi->var.yres;
+	pipe->src_width = fbi->var.xres;
+	pipe->src_h = fbi->var.yres;
+	pipe->src_w = fbi->var.xres;
+	pipe->src_y = 0;
+	pipe->src_x = 0;
+
+	if (mfd->display_iova)
+		pipe->srcp0_addr = mfd->display_iova + buf_offset;
+	else
+		pipe->srcp0_addr = (uint32)(buf + buf_offset);
+
+	pipe->srcp0_ystride = fbi->fix.line_length;
+	pipe->bpp = bpp;
+
+	mdp4_overlay_dmap_xy(pipe);
+	mdp4_overlay_dmap_cfg(mfd, 1);
+
+	mdp4_overlay_rgb_setup(pipe);
+
+	mdp4_overlayproc_cfg(pipe);
+
+	/*
+	 * LCDC timing setting
+	 */
+	h_back_porch = var->left_margin;
+	h_front_porch = var->right_margin;
+	v_back_porch = var->upper_margin;
+	v_front_porch = var->lower_margin;
+	hsync_pulse_width = var->hsync_len;
+	vsync_pulse_width = var->vsync_len;
+	lcdc_border_clr = mfd->panel_info.lcdc.border_clr;
+	lcdc_underflow_clr = mfd->panel_info.lcdc.underflow_clr;
+	lcdc_hsync_skew = mfd->panel_info.lcdc.hsync_skew;
+
+	lcdc_width = var->xres + mfd->panel_info.lcdc.xres_pad;
+	lcdc_height = var->yres + mfd->panel_info.lcdc.yres_pad;
+	lcdc_bpp = mfd->panel_info.bpp;
+
+	hsync_period =
+	    hsync_pulse_width + h_back_porch + lcdc_width + h_front_porch;
+	hsync_ctrl = (hsync_period << 16) | hsync_pulse_width;
+	hsync_start_x = hsync_pulse_width + h_back_porch;
+	hsync_end_x = hsync_period - h_front_porch - 1;
+	display_hctl = (hsync_end_x << 16) | hsync_start_x;
+
+	vsync_period =
+	    (vsync_pulse_width + v_back_porch + lcdc_height +
+	     v_front_porch) * hsync_period;
+	display_v_start =
+	    (vsync_pulse_width + v_back_porch) * hsync_period + lcdc_hsync_skew;
+	display_v_end =
+	    vsync_period - (v_front_porch * hsync_period) + lcdc_hsync_skew - 1;
+
+	if (lcdc_width != var->xres) {
+		active_h_start = hsync_start_x + first_pixel_start_x;
+		active_h_end = active_h_start + var->xres - 1;
+		active_hctl =
+		    ACTIVE_START_X_EN | (active_h_end << 16) | active_h_start;
+	} else {
+		active_hctl = 0;
+	}
+
+	if (lcdc_height != var->yres) {
+		active_v_start =
+		    display_v_start + first_pixel_start_y * hsync_period;
+		active_v_end = active_v_start + (var->yres) * hsync_period - 1;
+		active_v_start |= ACTIVE_START_Y_EN;
+	} else {
+		active_v_start = 0;
+		active_v_end = 0;
+	}
+
+
+#ifdef CONFIG_FB_MSM_MDP40
+	hsync_polarity = 1;
+	vsync_polarity = 1;
+	lcdc_underflow_clr |= 0x80000000;	/* enable recovery */
+#else
+	hsync_polarity = 0;
+	vsync_polarity = 0;
+#endif
+	data_en_polarity = 0;
+
+	ctrl_polarity =
+	    (data_en_polarity << 2) | (vsync_polarity << 1) | (hsync_polarity);
+
+	MDP_OUTP(MDP_BASE + LCDC_BASE + 0x4, hsync_ctrl);
+	MDP_OUTP(MDP_BASE + LCDC_BASE + 0x8, vsync_period);
+	MDP_OUTP(MDP_BASE + LCDC_BASE + 0xc, vsync_pulse_width * hsync_period);
+	MDP_OUTP(MDP_BASE + LCDC_BASE + 0x10, display_hctl);
+	MDP_OUTP(MDP_BASE + LCDC_BASE + 0x14, display_v_start);
+	MDP_OUTP(MDP_BASE + LCDC_BASE + 0x18, display_v_end);
+	MDP_OUTP(MDP_BASE + LCDC_BASE + 0x28, lcdc_border_clr);
+	MDP_OUTP(MDP_BASE + LCDC_BASE + 0x2c, lcdc_underflow_clr);
+	MDP_OUTP(MDP_BASE + LCDC_BASE + 0x30, lcdc_hsync_skew);
+	MDP_OUTP(MDP_BASE + LCDC_BASE + 0x38, ctrl_polarity);
+	MDP_OUTP(MDP_BASE + LCDC_BASE + 0x1c, active_hctl);
+	MDP_OUTP(MDP_BASE + LCDC_BASE + 0x20, active_v_start);
+	MDP_OUTP(MDP_BASE + LCDC_BASE + 0x24, active_v_end);
+
+	mdp4_overlay_reg_flush(pipe, 1);
+	mdp4_mixer_stage_up(pipe);
+
+#ifdef CONFIG_MSM_BUS_SCALING
+	mdp_bus_scale_update_request(2);
+#endif
+	mdp_histogram_ctrl_all(TRUE);
+
+	ret = panel_next_on(pdev);
+	/* MDP cmd block disable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	return ret;
+}
+
+int mdp_lcdc_off(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct msm_fb_data_type *mfd;
+
+	mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
+
+	mutex_lock(&mfd->dma->ov_mutex);
+
+	/* MDP cmd block enable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	mdp4_mixer_pipe_cleanup(lcdc_pipe->mixer_num);
+	MDP_OUTP(MDP_BASE + LCDC_BASE, 0);
+	lcdc_enabled = 0;
+	/* MDP cmd block disable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+	mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	mdp_histogram_ctrl_all(FALSE);
+	ret = panel_next_off(pdev);
+
+	mutex_unlock(&mfd->dma->ov_mutex);
+
+	/* delay to make sure the last frame finishes */
+	msleep(20);
+
+	/* dis-engage rgb0 from mixer0 */
+	if (lcdc_pipe) {
+		mdp4_mixer_stage_down(lcdc_pipe);
+		mdp4_iommu_unmap(lcdc_pipe);
+	}
+
+#ifdef CONFIG_MSM_BUS_SCALING
+	mdp_bus_scale_update_request(0);
+#endif
+
+	return ret;
+}
+
+static void mdp4_lcdc_blt_ov_update(struct mdp4_overlay_pipe *pipe)
+{
+	uint32 off, addr;
+	int bpp;
+	char *overlay_base;
+
+
+	if (pipe->blt_addr == 0)
+		return;
+
+
+#ifdef BLT_RGB565
+	bpp = 2; /* overlay ouput is RGB565 */
+#else
+	bpp = 3; /* overlay ouput is RGB888 */
+#endif
+	off = 0;
+	if (pipe->ov_cnt & 0x01)
+		off = pipe->src_height * pipe->src_width * bpp;
+	addr = pipe->blt_addr + off;
+
+	/* overlay 0 */
+	overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */
+	outpdw(overlay_base + 0x000c, addr);
+	outpdw(overlay_base + 0x001c, addr);
+}
+
+static void mdp4_lcdc_blt_dmap_update(struct mdp4_overlay_pipe *pipe)
+{
+	uint32 off, addr;
+	int bpp;
+
+	if (pipe->blt_addr == 0)
+		return;
+
+
+#ifdef BLT_RGB565
+	bpp = 2; /* overlay ouput is RGB565 */
+#else
+	bpp = 3; /* overlay ouput is RGB888 */
+#endif
+	off = 0;
+	if (pipe->dmap_cnt & 0x01)
+		off = pipe->src_height * pipe->src_width * bpp;
+	addr = pipe->blt_addr + off;
+
+	/* dmap */
+	MDP_OUTP(MDP_BASE + 0x90008, addr);
+}
+
+/*
+ * mdp4_overlay_lcdc_wait4event:
+ * INTR_DMA_P_DONE and INTR_PRIMARY_VSYNC event only
+ * no INTR_OVERLAY0_DONE event allowed.
+ */
+static void mdp4_overlay_lcdc_wait4event(struct msm_fb_data_type *mfd,
+					int intr_done)
+{
+	unsigned long flag;
+	unsigned int data;
+
+	data = inpdw(MDP_BASE + LCDC_BASE);
+	data &= 0x01;
+	if (data == 0)	/* timing generator disabled */
+		return;
+
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	INIT_COMPLETION(lcdc_comp);
+	mfd->dma->waiting = TRUE;
+	outp32(MDP_INTR_CLEAR, intr_done);
+	mdp_intr_mask |= intr_done;
+	outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+	mdp_enable_irq(MDP_DMA2_TERM);  /* enable intr */
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+	wait_for_completion(&lcdc_comp);
+	mdp_disable_irq(MDP_DMA2_TERM);
+}
+
+static void mdp4_overlay_lcdc_dma_busy_wait(struct msm_fb_data_type *mfd)
+{
+	unsigned long flag;
+	int need_wait = 0;
+
+	pr_debug("%s: start pid=%d\n", __func__, current->pid);
+
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	if (mfd->dma->busy == TRUE) {
+		INIT_COMPLETION(mfd->dma->comp);
+		need_wait++;
+	}
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+
+	if (need_wait) {
+		/* wait until DMA finishes the current job */
+		pr_debug("%s: pending pid=%d\n", __func__, current->pid);
+		wait_for_completion(&mfd->dma->comp);
+	}
+	pr_debug("%s: done pid=%d\n", __func__, current->pid);
+}
+
+void mdp4_overlay_lcdc_start(void)
+{
+	if (!lcdc_enabled) {
+		/* enable LCDC block */
+		mdp4_iommu_attach();
+		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+		MDP_OUTP(MDP_BASE + LCDC_BASE, 1);
+		mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+		lcdc_enabled = 1;
+	}
+}
+
+void mdp4_overlay_lcdc_vsync_push(struct msm_fb_data_type *mfd,
+			struct mdp4_overlay_pipe *pipe)
+{
+	unsigned long flag;
+
+	if (pipe->flags & MDP_OV_PLAY_NOWAIT)
+		return;
+
+	if (lcdc_pipe->blt_addr) {
+		mdp4_overlay_lcdc_dma_busy_wait(mfd);
+
+		mdp4_lcdc_blt_ov_update(lcdc_pipe);
+		lcdc_pipe->ov_cnt++;
+
+		spin_lock_irqsave(&mdp_spin_lock, flag);
+		outp32(MDP_INTR_CLEAR, INTR_OVERLAY0_DONE);
+		mdp_intr_mask |= INTR_OVERLAY0_DONE;
+		outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+		mdp_enable_irq(MDP_OVERLAY0_TERM);
+		mfd->dma->busy = TRUE;
+		mb();	/* make sure all registers updated */
+		spin_unlock_irqrestore(&mdp_spin_lock, flag);
+		outpdw(MDP_BASE + 0x0004, 0); /* kickoff overlay engine */
+		mdp4_stat.kickoff_ov0++;
+		mb();
+		mdp4_overlay_lcdc_wait4event(mfd, INTR_DMA_P_DONE);
+	} else {
+		mdp4_overlay_lcdc_wait4event(mfd, INTR_PRIMARY_VSYNC);
+	}
+	mdp4_set_perf_level();
+}
+
+/*
+ * mdp4_primary_vsync_lcdc: called from isr
+ */
+void mdp4_primary_vsync_lcdc(void)
+{
+	complete_all(&lcdc_comp);
+}
+
+/*
+ * mdp4_dma_p_done_lcdc: called from isr
+ */
+void mdp4_dma_p_done_lcdc(void)
+{
+	complete_all(&lcdc_comp);
+}
+
+/*
+ * mdp4_overlay0_done_lcdc: called from isr
+ */
+void mdp4_overlay0_done_lcdc(struct mdp_dma_data *dma)
+{
+	spin_lock(&mdp_spin_lock);
+	dma->busy = FALSE;
+	if (lcdc_pipe->blt_addr == 0) {
+		spin_unlock(&mdp_spin_lock);
+		return;
+	}
+	mdp4_lcdc_blt_dmap_update(lcdc_pipe);
+	lcdc_pipe->dmap_cnt++;
+	mdp_disable_irq_nosync(MDP_OVERLAY0_TERM);
+	spin_unlock(&mdp_spin_lock);
+	complete(&dma->comp);
+}
+
+static void mdp4_overlay_lcdc_prefill(struct msm_fb_data_type *mfd)
+{
+	unsigned long flag;
+
+	if (lcdc_pipe->blt_addr) {
+		mdp4_overlay_lcdc_dma_busy_wait(mfd);
+
+		mdp4_lcdc_blt_ov_update(lcdc_pipe);
+		lcdc_pipe->ov_cnt++;
+
+		spin_lock_irqsave(&mdp_spin_lock, flag);
+		outp32(MDP_INTR_CLEAR, INTR_OVERLAY0_DONE);
+		mdp_intr_mask |= INTR_OVERLAY0_DONE;
+		outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+		mdp_enable_irq(MDP_OVERLAY0_TERM);
+		mfd->dma->busy = TRUE;
+		mb();	/* make sure all registers updated */
+		spin_unlock_irqrestore(&mdp_spin_lock, flag);
+		outpdw(MDP_BASE + 0x0004, 0); /* kickoff overlay engine */
+		mdp4_stat.kickoff_ov0++;
+		mb();
+	}
+}
+/*
+ * make sure the MIPI_DSI_WRITEBACK_SIZE defined at boardfile
+ * has enough space h * w * 3 * 2
+ */
+static void mdp4_lcdc_do_blt(struct msm_fb_data_type *mfd, int enable)
+{
+	unsigned long flag;
+	int change = 0;
+
+	mdp4_allocate_writeback_buf(mfd, MDP4_MIXER0);
+
+	if (!mfd->ov0_wb_buf->phys_addr) {
+		pr_debug("%s: no blt_base assigned\n", __func__);
+		return;
+	}
+
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	if (enable && lcdc_pipe->blt_addr == 0) {
+		lcdc_pipe->blt_addr = mfd->ov0_wb_buf->phys_addr;
+		change++;
+		lcdc_pipe->blt_cnt = 0;
+		lcdc_pipe->ov_cnt = 0;
+		lcdc_pipe->dmap_cnt = 0;
+		mdp4_stat.blt_lcdc++;
+	} else if (enable == 0 && lcdc_pipe->blt_addr) {
+		lcdc_pipe->blt_addr = 0;
+		change++;
+	}
+	pr_info("%s: blt_addr=%x\n", __func__, (int)lcdc_pipe->blt_addr);
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+
+	if (!change)
+		return;
+
+	mdp4_overlay_lcdc_wait4event(mfd, INTR_DMA_P_DONE);
+	MDP_OUTP(MDP_BASE + LCDC_BASE, 0);	/* stop lcdc */
+	msleep(20);
+	mdp4_overlayproc_cfg(lcdc_pipe);
+	mdp4_overlay_dmap_xy(lcdc_pipe);
+	if (lcdc_pipe->blt_addr) {
+		mdp4_overlay_lcdc_prefill(mfd);
+		mdp4_overlay_lcdc_prefill(mfd);
+	}
+	MDP_OUTP(MDP_BASE + LCDC_BASE, 1);	/* start lcdc */
+}
+
+int mdp4_lcdc_overlay_blt_offset(struct msm_fb_data_type *mfd,
+					struct msmfb_overlay_blt *req)
+{
+	req->offset = 0;
+	req->width = lcdc_pipe->src_width;
+	req->height = lcdc_pipe->src_height;
+	req->bpp = lcdc_pipe->bpp;
+
+	return sizeof(*req);
+}
+
+void mdp4_lcdc_overlay_blt(struct msm_fb_data_type *mfd,
+					struct msmfb_overlay_blt *req)
+{
+	mdp4_lcdc_do_blt(mfd, req->enable);
+}
+
+void mdp4_lcdc_overlay_blt_start(struct msm_fb_data_type *mfd)
+{
+	mdp4_lcdc_do_blt(mfd, 1);
+}
+
+void mdp4_lcdc_overlay_blt_stop(struct msm_fb_data_type *mfd)
+{
+	mdp4_lcdc_do_blt(mfd, 0);
+}
+
+void mdp4_lcdc_overlay(struct msm_fb_data_type *mfd)
+{
+	struct fb_info *fbi = mfd->fbi;
+	uint8 *buf;
+	unsigned int buf_offset;
+	int bpp;
+	struct mdp4_overlay_pipe *pipe;
+
+	if (!mfd->panel_power_on)
+		return;
+
+	/* no need to power on cmd block since it's lcdc mode */
+	bpp = fbi->var.bits_per_pixel / 8;
+	buf = (uint8 *) fbi->fix.smem_start;
+	buf_offset = calc_fb_offset(mfd, fbi, bpp);
+
+	mutex_lock(&mfd->dma->ov_mutex);
+
+	pipe = lcdc_pipe;
+
+	if (mfd->display_iova)
+		pipe->srcp0_addr = mfd->display_iova + buf_offset;
+	else
+		pipe->srcp0_addr = (uint32)(buf + buf_offset);
+
+	mdp4_overlay_rgb_setup(pipe);
+	mdp4_overlay_reg_flush(pipe, 0);
+	mdp4_mixer_stage_up(pipe);
+	mdp4_overlay_lcdc_start();
+	mdp4_overlay_lcdc_vsync_push(mfd, pipe);
+	mdp4_iommu_unmap(pipe);
+	mutex_unlock(&mfd->dma->ov_mutex);
+}
diff --git a/drivers/video/msm/mdp4_overlay_mddi.c b/drivers/video/msm/mdp4_overlay_mddi.c
new file mode 100644
index 0000000..5aa5965
--- /dev/null
+++ b/drivers/video/msm/mdp4_overlay_mddi.c
@@ -0,0 +1,596 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/hrtimer.h>
+#include <linux/delay.h>
+#include <mach/hardware.h>
+#include <linux/io.h>
+
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <linux/semaphore.h>
+#include <linux/spinlock.h>
+
+#include <linux/fb.h>
+
+#include "mdp.h"
+#include "msm_fb.h"
+#include "mdp4.h"
+
+static struct mdp4_overlay_pipe *mddi_pipe;
+static struct msm_fb_data_type *mddi_mfd;
+static int busy_wait_cnt;
+
+static int vsync_start_y_adjust = 4;
+
+static int dmap_vsync_enable;
+
+void mdp_dmap_vsync_set(int enable)
+{
+	dmap_vsync_enable = enable;
+}
+
+int mdp_dmap_vsync_get(void)
+{
+	return dmap_vsync_enable;
+}
+
+void mdp4_mddi_vsync_enable(struct msm_fb_data_type *mfd,
+		struct mdp4_overlay_pipe *pipe, int which)
+{
+	uint32 start_y, data, tear_en;
+
+	tear_en = (1 << which);
+
+	if ((mfd->use_mdp_vsync) && (mfd->ibuf.vsync_enable) &&
+		(mfd->panel_info.lcd.vsync_enable)) {
+
+		if (mdp_hw_revision < MDP4_REVISION_V2_1) {
+			/* need dmas dmap switch */
+			if (which == 0 && dmap_vsync_enable == 0 &&
+				mfd->panel_info.lcd.rev < 2) /* dma_p */
+				return;
+		}
+
+		if (vsync_start_y_adjust <= pipe->dst_y)
+			start_y = pipe->dst_y - vsync_start_y_adjust;
+		else
+			start_y = (mfd->total_lcd_lines - 1) -
+				(vsync_start_y_adjust - pipe->dst_y);
+		if (which == 0)
+			MDP_OUTP(MDP_BASE + 0x210, start_y);	/* primary */
+		else
+			MDP_OUTP(MDP_BASE + 0x214, start_y);	/* secondary */
+
+		data = inpdw(MDP_BASE + 0x20c);
+		data |= tear_en;
+		MDP_OUTP(MDP_BASE + 0x20c, data);
+	} else {
+		data = inpdw(MDP_BASE + 0x20c);
+		data &= ~tear_en;
+		MDP_OUTP(MDP_BASE + 0x20c, data);
+	}
+}
+
+#define WHOLESCREEN
+
+void mdp4_overlay_update_lcd(struct msm_fb_data_type *mfd)
+{
+	MDPIBUF *iBuf = &mfd->ibuf;
+	uint8 *src;
+	int ptype;
+	uint32 mddi_ld_param;
+	uint16 mddi_vdo_packet_reg;
+	struct mdp4_overlay_pipe *pipe;
+	int ret;
+
+	if (mfd->key != MFD_KEY)
+		return;
+
+	mddi_mfd = mfd;		/* keep it */
+
+	/* MDP cmd block enable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	if (mddi_pipe == NULL) {
+		ptype = mdp4_overlay_format2type(mfd->fb_imgType);
+		if (ptype < 0)
+			printk(KERN_INFO "%s: format2type failed\n", __func__);
+		pipe = mdp4_overlay_pipe_alloc(ptype, MDP4_MIXER0);
+		if (pipe == NULL)
+			printk(KERN_INFO "%s: pipe_alloc failed\n", __func__);
+		pipe->pipe_used++;
+		pipe->mixer_num  = MDP4_MIXER0;
+		pipe->src_format = mfd->fb_imgType;
+		mdp4_overlay_panel_mode(pipe->mixer_num, MDP4_PANEL_MDDI);
+		ret = mdp4_overlay_format2pipe(pipe);
+		if (ret < 0)
+			printk(KERN_INFO "%s: format2type failed\n", __func__);
+
+		mddi_pipe = pipe; /* keep it */
+		mddi_pipe->blt_end = 1;	/* mark as end */
+		mddi_ld_param = 0;
+		mddi_vdo_packet_reg = mfd->panel_info.mddi.vdopkt;
+
+		if (mdp_hw_revision == MDP4_REVISION_V2_1) {
+			uint32	data;
+
+			data = inpdw(MDP_BASE + 0x0028);
+			data &= ~0x0300;	/* bit 8, 9, MASTER4 */
+			if (mfd->fbi->var.xres == 540) /* qHD, 540x960 */
+				data |= 0x0200;
+			else
+				data |= 0x0100;
+
+			MDP_OUTP(MDP_BASE + 0x00028, data);
+		}
+
+		if (mfd->panel_info.type == MDDI_PANEL) {
+			if (mfd->panel_info.pdest == DISPLAY_1)
+				mddi_ld_param = 0;
+			else
+				mddi_ld_param = 1;
+		} else {
+			mddi_ld_param = 2;
+		}
+
+		MDP_OUTP(MDP_BASE + 0x00090, mddi_ld_param);
+
+		if (mfd->panel_info.bpp == 24)
+			MDP_OUTP(MDP_BASE + 0x00094,
+			 (MDDI_VDO_PACKET_DESC_24 << 16) | mddi_vdo_packet_reg);
+		else if (mfd->panel_info.bpp == 16)
+			MDP_OUTP(MDP_BASE + 0x00094,
+			 (MDDI_VDO_PACKET_DESC_16 << 16) | mddi_vdo_packet_reg);
+		else
+			MDP_OUTP(MDP_BASE + 0x00094,
+			 (MDDI_VDO_PACKET_DESC << 16) | mddi_vdo_packet_reg);
+
+		MDP_OUTP(MDP_BASE + 0x00098, 0x01);
+	} else {
+		pipe = mddi_pipe;
+	}
+
+	/* 0 for dma_p, client_id = 0 */
+	MDP_OUTP(MDP_BASE + 0x00090, 0);
+
+
+	src = (uint8 *) iBuf->buf;
+
+#ifdef WHOLESCREEN
+
+	{
+		struct fb_info *fbi;
+
+		fbi = mfd->fbi;
+		pipe->src_height = fbi->var.yres;
+		pipe->src_width = fbi->var.xres;
+		pipe->src_h = fbi->var.yres;
+		pipe->src_w = fbi->var.xres;
+		pipe->src_y = 0;
+		pipe->src_x = 0;
+		pipe->dst_h = fbi->var.yres;
+		pipe->dst_w = fbi->var.xres;
+		pipe->dst_y = 0;
+		pipe->dst_x = 0;
+		pipe->srcp0_addr = (uint32)src;
+		pipe->srcp0_ystride = fbi->fix.line_length;
+	}
+
+#else
+	if (mdp4_overlay_active(MDP4_MIXER0)) {
+		struct fb_info *fbi;
+
+		fbi = mfd->fbi;
+		pipe->src_height = fbi->var.yres;
+		pipe->src_width = fbi->var.xres;
+		pipe->src_h = fbi->var.yres;
+		pipe->src_w = fbi->var.xres;
+		pipe->src_y = 0;
+		pipe->src_x = 0;
+		pipe->dst_h = fbi->var.yres;
+		pipe->dst_w = fbi->var.xres;
+		pipe->dst_y = 0;
+		pipe->dst_x = 0;
+		pipe->srcp0_addr = (uint32) src;
+		pipe->srcp0_ystride = fbi->fix.line_length;
+	} else {
+		/* starting input address */
+		src += (iBuf->dma_x + iBuf->dma_y * iBuf->ibuf_width)
+					* iBuf->bpp;
+
+		pipe->src_height = iBuf->dma_h;
+		pipe->src_width = iBuf->dma_w;
+		pipe->src_h = iBuf->dma_h;
+		pipe->src_w = iBuf->dma_w;
+		pipe->src_y = 0;
+		pipe->src_x = 0;
+		pipe->dst_h = iBuf->dma_h;
+		pipe->dst_w = iBuf->dma_w;
+		pipe->dst_y = iBuf->dma_y;
+		pipe->dst_x = iBuf->dma_x;
+		pipe->srcp0_addr = (uint32) src;
+		pipe->srcp0_ystride = iBuf->ibuf_width * iBuf->bpp;
+	}
+#endif
+
+	pipe->mixer_stage  = MDP4_MIXER_STAGE_BASE;
+
+	mdp4_overlay_rgb_setup(pipe);
+
+	mdp4_mixer_stage_up(pipe);
+
+	mdp4_overlayproc_cfg(pipe);
+
+	mdp4_overlay_dmap_xy(pipe);
+
+	mdp4_overlay_dmap_cfg(mfd, 0);
+
+	mdp4_mddi_vsync_enable(mfd, pipe, 0);
+
+	/* MDP cmd block disable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+}
+
+int mdp4_mddi_overlay_blt_offset(int *off)
+{
+	if (mdp_hw_revision < MDP4_REVISION_V2_1) { /* need dmas dmap switch */
+		if (mddi_pipe->blt_end ||
+			(mdp4_overlay_mixer_play(mddi_pipe->mixer_num) == 0)) {
+			*off = -1;
+			return -EINVAL;
+		}
+	} else {
+		/* no dmas dmap switch */
+		if (mddi_pipe->blt_end) {
+			*off = -1;
+			return -EINVAL;
+		}
+	}
+
+	if (mddi_pipe->blt_cnt & 0x01)
+		*off = mddi_pipe->src_height * mddi_pipe->src_width * 3;
+	else
+		*off = 0;
+
+	return 0;
+}
+
+void mdp4_mddi_overlay_blt(ulong addr)
+{
+	unsigned long flag;
+
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	if (addr) {
+		mdp_pipe_ctrl(MDP_DMA2_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+		mdp_intr_mask |= INTR_DMA_P_DONE;
+		outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+		mdp_pipe_ctrl(MDP_DMA2_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+		mddi_pipe->blt_cnt = 0;
+		mddi_pipe->blt_end = 0;
+		mddi_pipe->blt_addr = addr;
+	} else {
+		mddi_pipe->blt_end = 1;	/* mark as end */
+	}
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+}
+
+void mdp4_blt_xy_update(struct mdp4_overlay_pipe *pipe)
+{
+	uint32 off, addr;
+	int bpp;
+	char *overlay_base;
+
+
+	if (pipe->blt_addr == 0)
+		return;
+
+
+#ifdef BLT_RGB565
+	bpp = 2; /* overlay ouput is RGB565 */
+#else
+	bpp = 3; /* overlay ouput is RGB888 */
+#endif
+	off = 0;
+	if (pipe->dmap_cnt & 0x01)
+		off = pipe->src_height * pipe->src_width * bpp;
+
+	addr = pipe->blt_addr + off;
+
+	/* dmap */
+	MDP_OUTP(MDP_BASE + 0x90008, addr);
+
+	/* overlay 0 */
+	overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */
+	outpdw(overlay_base + 0x000c, addr);
+	outpdw(overlay_base + 0x001c, addr);
+}
+
+/*
+ * mdp4_dmap_done_mddi: called from isr
+ */
+void mdp4_dma_p_done_mddi(void)
+{
+	if (mddi_pipe->blt_end) {
+		mddi_pipe->blt_addr = 0;
+		mdp_intr_mask &= ~INTR_DMA_P_DONE;
+		outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+		mdp4_overlayproc_cfg(mddi_pipe);
+		mdp4_overlay_dmap_xy(mddi_pipe);
+	}
+
+	/*
+	 * single buffer, no need to increase
+	 * mdd_pipe->dmap_cnt here
+	 */
+}
+
+/*
+ * mdp4_overlay0_done_mddi: called from isr
+ */
+void mdp4_overlay0_done_mddi(struct mdp_dma_data *dma)
+{
+	mdp_disable_irq_nosync(MDP_OVERLAY0_TERM);
+
+	dma->busy = FALSE;
+	complete(&dma->comp);
+	mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK,
+			MDP_BLOCK_POWER_OFF, TRUE);
+
+	if (busy_wait_cnt)
+		busy_wait_cnt--;
+
+	pr_debug("%s: ISR-done\n", __func__);
+
+	if (mddi_pipe->blt_addr) {
+		if (mddi_pipe->blt_cnt == 0) {
+			mdp4_overlayproc_cfg(mddi_pipe);
+			mdp4_overlay_dmap_xy(mddi_pipe);
+			mddi_pipe->ov_cnt = 0;
+			mddi_pipe->dmap_cnt = 0;
+			/* BLT start from next frame */
+		} else {
+			mdp_pipe_ctrl(MDP_DMA2_BLOCK, MDP_BLOCK_POWER_ON,
+						FALSE);
+			mdp4_blt_xy_update(mddi_pipe);
+			outpdw(MDP_BASE + 0x000c, 0x0); /* start DMAP */
+		}
+		mddi_pipe->blt_cnt++;
+		mddi_pipe->ov_cnt++;
+	}
+
+
+
+}
+
+void mdp4_mddi_overlay_restore(void)
+{
+	if (mddi_mfd == NULL)
+		return;
+
+	pr_debug("%s: resotre, pid=%d\n", __func__, current->pid);
+
+	if (mddi_mfd->panel_power_on == 0)
+		return;
+	if (mddi_mfd && mddi_pipe) {
+		mdp4_mddi_dma_busy_wait(mddi_mfd);
+		mdp4_overlay_update_lcd(mddi_mfd);
+		mdp4_mddi_overlay_kickoff(mddi_mfd, mddi_pipe);
+		mddi_mfd->dma_update_flag = 1;
+	}
+	if (mdp_hw_revision < MDP4_REVISION_V2_1) /* need dmas dmap switch */
+		mdp4_mddi_overlay_dmas_restore();
+}
+
+/*
+ * mdp4_mddi_cmd_dma_busy_wait: check mddi link activity
+ * dsi link is a shared resource and it can only be used
+ * while it is in idle state.
+ * ov_mutex need to be acquired before call this function.
+ */
+void mdp4_mddi_dma_busy_wait(struct msm_fb_data_type *mfd)
+{
+	unsigned long flag;
+	int need_wait = 0;
+
+	pr_debug("%s: START, pid=%d\n", __func__, current->pid);
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	if (mfd->dma->busy == TRUE) {
+		if (busy_wait_cnt == 0)
+			INIT_COMPLETION(mfd->dma->comp);
+		busy_wait_cnt++;
+		need_wait++;
+	}
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+
+
+	if (need_wait) {
+		/* wait until DMA finishes the current job */
+		pr_debug("%s: PENDING, pid=%d\n", __func__, current->pid);
+		wait_for_completion(&mfd->dma->comp);
+	}
+	pr_debug("%s: DONE, pid=%d\n", __func__, current->pid);
+}
+
+void mdp4_mddi_kickoff_video(struct msm_fb_data_type *mfd,
+				struct mdp4_overlay_pipe *pipe)
+{
+	pr_debug("%s: pid=%d\n", __func__, current->pid);
+	mdp4_mddi_overlay_kickoff(mfd, pipe);
+}
+
+void mdp4_mddi_kickoff_ui(struct msm_fb_data_type *mfd,
+				struct mdp4_overlay_pipe *pipe)
+{
+	pr_debug("%s: pid=%d\n", __func__, current->pid);
+	mdp4_mddi_overlay_kickoff(mfd, pipe);
+}
+
+
+void mdp4_mddi_overlay_kickoff(struct msm_fb_data_type *mfd,
+				struct mdp4_overlay_pipe *pipe)
+{
+	/* change mdp clk while mdp is idle` */
+	mdp4_set_perf_level();
+
+	mdp_enable_irq(MDP_OVERLAY0_TERM);
+	mfd->dma->busy = TRUE;
+	/* start OVERLAY pipe */
+	mdp_pipe_kickoff(MDP_OVERLAY0_TERM, mfd);
+	mdp4_stat.kickoff_ov0++;
+}
+
+void mdp4_dma_s_update_lcd(struct msm_fb_data_type *mfd,
+				struct mdp4_overlay_pipe *pipe)
+{
+	MDPIBUF *iBuf = &mfd->ibuf;
+	uint32 outBpp = iBuf->bpp;
+	uint16 mddi_vdo_packet_reg;
+	uint32 dma_s_cfg_reg;
+
+	dma_s_cfg_reg = 0;
+
+	if (mfd->fb_imgType == MDP_RGBA_8888)
+		dma_s_cfg_reg |= DMA_PACK_PATTERN_BGR; /* on purpose */
+	else if (mfd->fb_imgType == MDP_BGR_565)
+		dma_s_cfg_reg |= DMA_PACK_PATTERN_BGR;
+	else
+		dma_s_cfg_reg |= DMA_PACK_PATTERN_RGB;
+
+	if (outBpp == 4)
+		dma_s_cfg_reg |= (1 << 26); /* xRGB8888 */
+	else if (outBpp == 2)
+		dma_s_cfg_reg |= DMA_IBUF_FORMAT_RGB565;
+
+	dma_s_cfg_reg |= DMA_DITHER_EN;
+
+	/* MDP cmd block enable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	/* PIXELSIZE */
+	MDP_OUTP(MDP_BASE + 0xa0004, (pipe->dst_h << 16 | pipe->dst_w));
+	MDP_OUTP(MDP_BASE + 0xa0008, pipe->srcp0_addr);	/* ibuf address */
+	MDP_OUTP(MDP_BASE + 0xa000c, pipe->srcp0_ystride);/* ystride */
+
+	if (mfd->panel_info.bpp == 24) {
+		dma_s_cfg_reg |= DMA_DSTC0G_8BITS |	/* 666 18BPP */
+		    DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS;
+	} else if (mfd->panel_info.bpp == 18) {
+		dma_s_cfg_reg |= DMA_DSTC0G_6BITS |	/* 666 18BPP */
+		    DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
+	} else {
+		dma_s_cfg_reg |= DMA_DSTC0G_6BITS |	/* 565 16BPP */
+		    DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS;
+	}
+
+	MDP_OUTP(MDP_BASE + 0xa0010, (pipe->dst_y << 16) | pipe->dst_x);
+
+	/* 1 for dma_s, client_id = 0 */
+	MDP_OUTP(MDP_BASE + 0x00090, 1);
+
+	mddi_vdo_packet_reg = mfd->panel_info.mddi.vdopkt;
+
+	if (mfd->panel_info.bpp == 24)
+		MDP_OUTP(MDP_BASE + 0x00094,
+			(MDDI_VDO_PACKET_DESC_24 << 16) | mddi_vdo_packet_reg);
+	else if (mfd->panel_info.bpp == 16)
+		MDP_OUTP(MDP_BASE + 0x00094,
+			 (MDDI_VDO_PACKET_DESC_16 << 16) | mddi_vdo_packet_reg);
+	else
+		MDP_OUTP(MDP_BASE + 0x00094,
+			 (MDDI_VDO_PACKET_DESC << 16) | mddi_vdo_packet_reg);
+
+	MDP_OUTP(MDP_BASE + 0x00098, 0x01);
+
+	MDP_OUTP(MDP_BASE + 0xa0000, dma_s_cfg_reg);
+
+	mdp4_mddi_vsync_enable(mfd, pipe, 1);
+
+	/* MDP cmd block disable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+void mdp4_mddi_dma_s_kickoff(struct msm_fb_data_type *mfd,
+				struct mdp4_overlay_pipe *pipe)
+{
+	/* change mdp clk while mdp is idle` */
+	mdp4_set_perf_level();
+
+	mdp_enable_irq(MDP_DMA_S_TERM);
+	mfd->dma->busy = TRUE;
+	mfd->ibuf_flushed = TRUE;
+	/* start dma_s pipe */
+	mdp_pipe_kickoff(MDP_DMA_S_TERM, mfd);
+	mdp4_stat.kickoff_dmas++;
+
+	/* wait until DMA finishes the current job */
+	wait_for_completion(&mfd->dma->comp);
+	mdp_disable_irq(MDP_DMA_S_TERM);
+}
+
+void mdp4_mddi_overlay_dmas_restore(void)
+{
+	/* mutex held by caller */
+	if (mddi_mfd && mddi_pipe) {
+		mdp4_mddi_dma_busy_wait(mddi_mfd);
+		mdp4_dma_s_update_lcd(mddi_mfd, mddi_pipe);
+		mdp4_mddi_dma_s_kickoff(mddi_mfd, mddi_pipe);
+		mddi_mfd->dma_update_flag = 1;
+	}
+}
+
+void mdp4_mddi_overlay(struct msm_fb_data_type *mfd)
+{
+	mutex_lock(&mfd->dma->ov_mutex);
+
+	if (mfd && mfd->panel_power_on) {
+		mdp4_mddi_dma_busy_wait(mfd);
+		mdp4_overlay_update_lcd(mfd);
+
+		if (mdp_hw_revision < MDP4_REVISION_V2_1) {
+			/* dmas dmap switch */
+			if (mdp4_overlay_mixer_play(mddi_pipe->mixer_num)
+						== 0) {
+				mdp4_dma_s_update_lcd(mfd, mddi_pipe);
+				mdp4_mddi_dma_s_kickoff(mfd, mddi_pipe);
+			} else
+				mdp4_mddi_kickoff_ui(mfd, mddi_pipe);
+		} else	/* no dams dmap switch  */
+			mdp4_mddi_kickoff_ui(mfd, mddi_pipe);
+
+	/* signal if pan function is waiting for the update completion */
+		if (mfd->pan_waiting) {
+			mfd->pan_waiting = FALSE;
+			complete(&mfd->pan_comp);
+		}
+	}
+	mutex_unlock(&mfd->dma->ov_mutex);
+}
+
+int mdp4_mddi_overlay_cursor(struct fb_info *info, struct fb_cursor *cursor)
+{
+	struct msm_fb_data_type *mfd = info->par;
+	mutex_lock(&mfd->dma->ov_mutex);
+	if (mfd && mfd->panel_power_on) {
+		mdp4_mddi_dma_busy_wait(mfd);
+		mdp_hw_cursor_update(info, cursor);
+	}
+	mutex_unlock(&mfd->dma->ov_mutex);
+	return 0;
+}
diff --git a/drivers/video/msm/mdp4_overlay_writeback.c b/drivers/video/msm/mdp4_overlay_writeback.c
new file mode 100644
index 0000000..f1a2ada
--- /dev/null
+++ b/drivers/video/msm/mdp4_overlay_writeback.c
@@ -0,0 +1,596 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/hrtimer.h>
+#include <linux/delay.h>
+#include <mach/hardware.h>
+#include <linux/io.h>
+
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <linux/semaphore.h>
+#include <linux/spinlock.h>
+
+#include <linux/fb.h>
+
+#include "mdp.h"
+#include "msm_fb.h"
+#include "mdp4.h"
+enum {
+	WB_OPEN,
+	WB_START,
+	WB_STOPING,
+	WB_STOP
+};
+enum {
+	REGISTERED,
+	IN_FREE_QUEUE,
+	IN_BUSY_QUEUE,
+	WITH_CLIENT
+};
+
+static struct mdp4_overlay_pipe *writeback_pipe;
+static struct msm_fb_data_type *writeback_mfd;
+static int busy_wait_cnt;
+
+int mdp4_overlay_writeback_on(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+	struct fb_info *fbi;
+	uint8 *buf;
+	struct mdp4_overlay_pipe *pipe;
+	int bpp;
+	int ret;
+	uint32 data;
+
+	mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
+
+	if (!mfd)
+		return -ENODEV;
+
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	writeback_mfd = mfd;		  /* keep it */
+
+	fbi = mfd->fbi;
+
+	bpp = fbi->var.bits_per_pixel / 8;
+	buf = (uint8 *) fbi->fix.smem_start;
+	buf += fbi->var.xoffset * bpp +
+		fbi->var.yoffset * fbi->fix.line_length;
+
+	/* MDP cmd block enable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	if (writeback_pipe == NULL) {
+		pipe = mdp4_overlay_pipe_alloc(OVERLAY_TYPE_BF, MDP4_MIXER2);
+		if (pipe == NULL)
+			pr_info("%s: pipe_alloc failed\n", __func__);
+		pipe->pipe_used++;
+		pipe->mixer_stage  = MDP4_MIXER_STAGE_BASE;
+		pipe->mixer_num  = MDP4_MIXER2;
+		pipe->src_format = MDP_ARGB_8888;
+		mdp4_overlay_panel_mode(pipe->mixer_num, MDP4_PANEL_WRITEBACK);
+		ret = mdp4_overlay_format2pipe(pipe);
+		if (ret < 0)
+			pr_info("%s: format2type failed\n", __func__);
+
+		writeback_pipe = pipe; /* keep it */
+
+	} else {
+		pipe = writeback_pipe;
+	}
+	ret = panel_next_on(pdev);
+
+	/* MDP_LAYERMIXER_WB_MUX_SEL to use mixer1 axi for mixer2 writeback */
+	if (hdmi_prim_display)
+		data = 0x01;
+	else
+		data = 0x02;
+	outpdw(MDP_BASE + 0x100F4, data);
+
+	MDP_OUTP(MDP_BASE + MDP4_OVERLAYPROC1_BASE + 0x5004,
+		((0x0 & 0xFFF) << 16) | /* 12-bit B */
+			(0x0 & 0xFFF));         /* 12-bit G */
+	/* MSP_BORDER_COLOR */
+	MDP_OUTP(MDP_BASE + MDP4_OVERLAYPROC1_BASE + 0x5008,
+		(0x0 & 0xFFF));         /* 12-bit R */
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+	return ret;
+}
+
+int mdp4_overlay_writeback_off(struct platform_device *pdev)
+{
+	int ret;
+	struct msm_fb_data_type *mfd =
+			(struct msm_fb_data_type *)platform_get_drvdata(pdev);
+	if (mfd && writeback_pipe) {
+		mdp4_writeback_dma_busy_wait(mfd);
+		mdp4_overlay_pipe_free(writeback_pipe);
+		mdp4_overlay_panel_mode_unset(writeback_pipe->mixer_num,
+						MDP4_PANEL_WRITEBACK);
+		writeback_pipe = NULL;
+	}
+	ret = panel_next_off(pdev);
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	/* MDP_LAYERMIXER_WB_MUX_SEL to restore to default cfg*/
+	outpdw(MDP_BASE + 0x100F4, 0x0);
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+	return ret;
+}
+int mdp4_overlay_writeback_update(struct msm_fb_data_type *mfd)
+{
+	struct fb_info *fbi;
+	uint8 *buf;
+	unsigned int buf_offset;
+	struct mdp4_overlay_pipe *pipe;
+	int bpp;
+
+	if (mfd->key != MFD_KEY)
+		return -ENODEV;
+
+	if (!writeback_pipe)
+		return -EINVAL;
+
+	fbi = mfd->fbi;
+
+	pipe = writeback_pipe;
+
+	bpp = fbi->var.bits_per_pixel / 8;
+	buf = (uint8 *) fbi->fix.smem_start;
+	buf_offset = fbi->var.xoffset * bpp +
+		fbi->var.yoffset * fbi->fix.line_length;
+
+	/* MDP cmd block enable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	pipe->src_height = fbi->var.yres;
+	pipe->src_width = fbi->var.xres;
+	pipe->src_h = fbi->var.yres;
+	pipe->src_w = fbi->var.xres;
+	pipe->dst_h = fbi->var.yres;
+	pipe->dst_w = fbi->var.xres;
+	pipe->srcp0_ystride = fbi->fix.line_length;
+	pipe->src_y = 0;
+	pipe->src_x = 0;
+	pipe->dst_y = 0;
+	pipe->dst_x = 0;
+
+	if (mfd->display_iova)
+		pipe->srcp0_addr = mfd->display_iova + buf_offset;
+	else
+		pipe->srcp0_addr = (uint32)(buf + buf_offset);
+
+	mdp4_mixer_stage_up(pipe);
+
+	mdp4_overlayproc_cfg(pipe);
+
+	/* MDP cmd block disable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	wmb();
+	return 0;
+}
+void mdp4_writeback_dma_busy_wait(struct msm_fb_data_type *mfd)
+{
+	unsigned long flag;
+	int need_wait = 0;
+
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	if (mfd->dma->busy == TRUE) {
+		if (busy_wait_cnt == 0)
+			INIT_COMPLETION(mfd->dma->comp);
+		busy_wait_cnt = 1;
+		need_wait++;
+	}
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+
+	if (need_wait) {
+		/* wait until DMA finishes the current job */
+		pr_debug("%s: pending pid=%d\n",
+				__func__, current->pid);
+		wait_for_completion(&mfd->dma->comp);
+	}
+}
+
+void mdp4_overlay1_done_writeback(struct mdp_dma_data *dma)
+{
+	spin_lock(&mdp_spin_lock);
+	dma->busy = FALSE;
+	if (busy_wait_cnt)
+		busy_wait_cnt = 0;
+	mdp_disable_irq_nosync(MDP_OVERLAY2_TERM);
+	spin_unlock(&mdp_spin_lock);
+	complete_all(&dma->comp);
+	pr_debug("%s ovdone interrupt\n", __func__);
+
+}
+void mdp4_writeback_overlay_kickoff(struct msm_fb_data_type *mfd,
+				    struct mdp4_overlay_pipe *pipe)
+{
+	unsigned long flag;
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	mdp_enable_irq(MDP_OVERLAY2_TERM);
+
+	mfd->dma->busy = TRUE;
+	outp32(MDP_INTR_CLEAR, INTR_OVERLAY2_DONE);
+	mdp_intr_mask |= INTR_OVERLAY2_DONE;
+	outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+
+	wmb();	/* make sure all registers updated */
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+	/* start OVERLAY pipe */
+	mdp_pipe_kickoff(MDP_OVERLAY2_TERM, mfd);
+	wmb();
+	pr_debug("%s: before ov done interrupt\n", __func__);
+}
+void mdp4_writeback_dma_stop(struct msm_fb_data_type *mfd)
+{
+	/* mutex holded by caller */
+	if (mfd && writeback_pipe) {
+		mdp4_writeback_dma_busy_wait(mfd);
+		mdp4_overlay_writeback_update(mfd);
+
+		mdp4_writeback_overlay_kickoff(mfd, writeback_pipe);
+	}
+}
+
+void mdp4_writeback_kickoff_video(struct msm_fb_data_type *mfd,
+		struct mdp4_overlay_pipe *pipe)
+{
+	struct msmfb_writeback_data_list *node = NULL;
+	mutex_lock(&mfd->unregister_mutex);
+	mutex_lock(&mfd->writeback_mutex);
+	if (!list_empty(&mfd->writeback_free_queue)
+		&& mfd->writeback_state != WB_STOPING
+		&& mfd->writeback_state != WB_STOP) {
+		node = list_first_entry(&mfd->writeback_free_queue,
+				struct msmfb_writeback_data_list, active_entry);
+	}
+	if (node) {
+		list_del(&(node->active_entry));
+		node->state = IN_BUSY_QUEUE;
+		mfd->writeback_active_cnt++;
+	}
+	mutex_unlock(&mfd->writeback_mutex);
+
+	writeback_pipe->blt_addr = (ulong) (node ? node->addr : NULL);
+
+	if (!writeback_pipe->blt_addr) {
+		pr_err("%s: no writeback buffer 0x%x, %p\n", __func__,
+				(unsigned int)writeback_pipe->blt_addr, node);
+		mutex_unlock(&mfd->unregister_mutex);
+		return;
+	}
+
+	if (writeback_pipe->blt_cnt == 0)
+		mdp4_overlay_writeback_update(mfd);
+
+	pr_debug("%s: pid=%d\n", __func__, current->pid);
+
+	mdp4_writeback_overlay_kickoff(mfd, pipe);
+
+	mutex_lock(&mfd->writeback_mutex);
+	list_add_tail(&node->active_entry, &mfd->writeback_busy_queue);
+	mutex_unlock(&mfd->writeback_mutex);
+	mfd->writeback_active_cnt--;
+	mutex_unlock(&mfd->unregister_mutex);
+	wake_up(&mfd->wait_q);
+}
+
+void mdp4_writeback_kickoff_ui(struct msm_fb_data_type *mfd,
+		struct mdp4_overlay_pipe *pipe)
+{
+
+	pr_debug("%s: pid=%d\n", __func__, current->pid);
+	mdp4_writeback_overlay_kickoff(mfd, pipe);
+}
+
+void mdp4_writeback_overlay(struct msm_fb_data_type *mfd)
+{
+	int ret = 0;
+	struct msmfb_writeback_data_list *node = NULL;
+
+	mutex_lock(&mfd->unregister_mutex);
+	mutex_lock(&mfd->writeback_mutex);
+	if (!list_empty(&mfd->writeback_free_queue)
+		&& mfd->writeback_state != WB_STOPING
+		&& mfd->writeback_state != WB_STOP) {
+		node = list_first_entry(&mfd->writeback_free_queue,
+				struct msmfb_writeback_data_list, active_entry);
+	}
+	if (node) {
+		list_del(&(node->active_entry));
+		node->state = IN_BUSY_QUEUE;
+		mfd->writeback_active_cnt++;
+	}
+	mutex_unlock(&mfd->writeback_mutex);
+
+	writeback_pipe->blt_addr = (ulong) (node ? node->addr : NULL);
+
+	mutex_lock(&mfd->dma->ov_mutex);
+	pr_debug("%s in writeback\n", __func__);
+	if (writeback_pipe && !writeback_pipe->blt_addr) {
+		pr_err("%s: no writeback buffer 0x%x\n", __func__,
+				(unsigned int)writeback_pipe->blt_addr);
+		ret = mdp4_overlay_writeback_update(mfd);
+		if (ret)
+			pr_err("%s: update failed writeback pipe NULL\n",
+					__func__);
+		goto fail_no_blt_addr;
+	}
+
+	if (mfd && mfd->panel_power_on) {
+		pr_debug("%s in before busy wait\n", __func__);
+		mdp4_writeback_dma_busy_wait(mfd);
+
+		pr_debug("%s in before update\n", __func__);
+		ret = mdp4_overlay_writeback_update(mfd);
+		if (ret) {
+			pr_err("%s: update failed writeback pipe NULL\n",
+					__func__);
+			goto fail_no_blt_addr;
+		}
+
+		pr_debug("%s: in writeback pan display 0x%x\n", __func__,
+				(unsigned int)writeback_pipe->blt_addr);
+		mdp4_writeback_kickoff_ui(mfd, writeback_pipe);
+		mdp4_iommu_unmap(writeback_pipe);
+
+		/* signal if pan function is waiting for the
+		 * update completion */
+		if (mfd->pan_waiting) {
+			mfd->pan_waiting = FALSE;
+			complete(&mfd->pan_comp);
+		}
+	}
+
+	mutex_lock(&mfd->writeback_mutex);
+	list_add_tail(&node->active_entry, &mfd->writeback_busy_queue);
+	mfd->writeback_active_cnt--;
+	mutex_unlock(&mfd->writeback_mutex);
+	wake_up(&mfd->wait_q);
+fail_no_blt_addr:
+	/*NOTE: This api was removed
+	  mdp4_overlay_resource_release();*/
+	mutex_unlock(&mfd->dma->ov_mutex);
+	mutex_unlock(&mfd->unregister_mutex);
+}
+static int mdp4_overlay_writeback_register_buffer(
+	struct msm_fb_data_type *mfd, struct msmfb_writeback_data_list *node)
+{
+	if (!node) {
+		pr_err("Cannot register a NULL node\n");
+		return -EINVAL;
+	}
+	node->state = REGISTERED;
+	list_add_tail(&node->registered_entry, &mfd->writeback_register_queue);
+	return 0;
+}
+static struct msmfb_writeback_data_list *get_if_registered(
+			struct msm_fb_data_type *mfd, struct msmfb_data *data)
+{
+	struct msmfb_writeback_data_list *temp;
+	bool found = false;
+	if (!list_empty(&mfd->writeback_register_queue)) {
+		list_for_each_entry(temp,
+				&mfd->writeback_register_queue,
+				registered_entry) {
+			if (temp && temp->buf_info.iova == data->iova) {
+				found = true;
+				break;
+			}
+		}
+	}
+	if (!found) {
+		temp = kzalloc(sizeof(struct msmfb_writeback_data_list),
+				GFP_KERNEL);
+		if (temp == NULL) {
+			pr_err("%s: out of memory\n", __func__);
+			goto register_alloc_fail;
+		}
+
+		if (data->iova)
+			temp->addr = (void *)(data->iova + data->offset);
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+		else {
+			struct ion_handle *srcp_ihdl;
+			ulong len;
+			srcp_ihdl = ion_import_fd(mfd->iclient,
+						  data->memory_id);
+			if (IS_ERR_OR_NULL(srcp_ihdl)) {
+				pr_err("%s: ion import fd failed\n", __func__);
+				goto register_ion_fail;
+			}
+			if (ion_phys(mfd->iclient,
+				     srcp_ihdl,
+				     (ulong *)&temp->addr,
+				     (size_t *)&len)) {
+				pr_err("%s: unable to get ion phys\n",
+				       __func__);
+				goto register_ion_fail;
+			}
+			temp->addr += data->offset;
+		}
+#else
+		else {
+			pr_err("%s: only support ion memory\n", __func__);
+			goto register_ion_fail;
+		}
+#endif
+		memcpy(&temp->buf_info, data, sizeof(struct msmfb_data));
+		if (mdp4_overlay_writeback_register_buffer(mfd, temp)) {
+			pr_err("%s: error registering node\n", __func__);
+			goto register_ion_fail;
+		}
+	}
+	return temp;
+ register_ion_fail:
+	kfree(temp);
+ register_alloc_fail:
+	return NULL;
+}
+int mdp4_writeback_start(
+		struct fb_info *info)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	mutex_lock(&mfd->writeback_mutex);
+	mfd->writeback_state = WB_START;
+	mutex_unlock(&mfd->writeback_mutex);
+	wake_up(&mfd->wait_q);
+	return 0;
+}
+
+int mdp4_writeback_queue_buffer(struct fb_info *info, struct msmfb_data *data)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	struct msmfb_writeback_data_list *node = NULL;
+	int rv = 0;
+
+	mutex_lock(&mfd->writeback_mutex);
+	node = get_if_registered(mfd, data);
+	if (!node || node->state == IN_BUSY_QUEUE ||
+		node->state == IN_FREE_QUEUE) {
+		pr_err("memory not registered or Buffer already with us\n");
+		rv = -EINVAL;
+		goto exit;
+	}
+
+	list_add_tail(&node->active_entry, &mfd->writeback_free_queue);
+	node->state = IN_FREE_QUEUE;
+
+exit:
+	mutex_unlock(&mfd->writeback_mutex);
+	return rv;
+}
+static int is_buffer_ready(struct msm_fb_data_type *mfd)
+{
+	int rc;
+	mutex_lock(&mfd->writeback_mutex);
+	rc = !list_empty(&mfd->writeback_busy_queue) ||
+			(mfd->writeback_state == WB_STOPING);
+	mutex_unlock(&mfd->writeback_mutex);
+	return rc;
+}
+int mdp4_writeback_dequeue_buffer(struct fb_info *info, struct msmfb_data *data)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	struct msmfb_writeback_data_list *node = NULL;
+	int rc = 0;
+
+	rc = wait_event_interruptible(mfd->wait_q, is_buffer_ready(mfd));
+	if (rc) {
+		pr_err("failed to get dequeued buffer\n");
+		return -ENOBUFS;
+	}
+	mutex_lock(&mfd->writeback_mutex);
+	if (mfd->writeback_state == WB_STOPING) {
+		mfd->writeback_state = WB_STOP;
+		mutex_unlock(&mfd->writeback_mutex);
+		return -ENOBUFS;
+	} else	if (!list_empty(&mfd->writeback_busy_queue)) {
+		node = list_first_entry(&mfd->writeback_busy_queue,
+				struct msmfb_writeback_data_list, active_entry);
+	}
+	if (node) {
+		list_del(&node->active_entry);
+		node->state = WITH_CLIENT;
+		memcpy(data, &node->buf_info, sizeof(struct msmfb_data));
+	} else {
+		pr_err("node is NULL. Somebody else dequeued?\n");
+		rc = -ENOBUFS;
+	}
+	mutex_unlock(&mfd->writeback_mutex);
+	return rc;
+}
+
+static bool is_writeback_inactive(struct msm_fb_data_type *mfd)
+{
+	bool active;
+	mutex_lock(&mfd->writeback_mutex);
+	active = !mfd->writeback_active_cnt;
+	mutex_unlock(&mfd->writeback_mutex);
+	return active;
+}
+int mdp4_writeback_stop(struct fb_info *info)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	mutex_lock(&mfd->writeback_mutex);
+	mfd->writeback_state = WB_STOPING;
+	mutex_unlock(&mfd->writeback_mutex);
+	/* Wait for all pending writebacks to finish */
+	wait_event_interruptible(mfd->wait_q, is_writeback_inactive(mfd));
+
+	/* Wake up dequeue thread in case of no UI update*/
+	wake_up(&mfd->wait_q);
+
+	return 0;
+}
+int mdp4_writeback_init(struct fb_info *info)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	mutex_init(&mfd->writeback_mutex);
+	mutex_init(&mfd->unregister_mutex);
+	INIT_LIST_HEAD(&mfd->writeback_free_queue);
+	INIT_LIST_HEAD(&mfd->writeback_busy_queue);
+	INIT_LIST_HEAD(&mfd->writeback_register_queue);
+	mfd->writeback_state = WB_OPEN;
+	init_waitqueue_head(&mfd->wait_q);
+	return 0;
+}
+int mdp4_writeback_terminate(struct fb_info *info)
+{
+	struct list_head *ptr, *next;
+	struct msmfb_writeback_data_list *temp;
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	int rc = 0;
+
+	mutex_lock(&mfd->unregister_mutex);
+	mutex_lock(&mfd->writeback_mutex);
+
+	if (mfd->writeback_state != WB_STOPING &&
+		mfd->writeback_state != WB_STOP) {
+		pr_err("%s called without stopping\n", __func__);
+		rc = -EPERM;
+		goto terminate_err;
+
+	}
+
+	if (!list_empty(&mfd->writeback_register_queue)) {
+		list_for_each_safe(ptr, next,
+				&mfd->writeback_register_queue) {
+			temp = list_entry(ptr,
+					struct msmfb_writeback_data_list,
+					registered_entry);
+			list_del(&temp->registered_entry);
+			kfree(temp);
+		}
+	}
+	INIT_LIST_HEAD(&mfd->writeback_register_queue);
+	INIT_LIST_HEAD(&mfd->writeback_busy_queue);
+	INIT_LIST_HEAD(&mfd->writeback_free_queue);
+
+
+terminate_err:
+	mutex_unlock(&mfd->writeback_mutex);
+	mutex_unlock(&mfd->unregister_mutex);
+	return rc;
+}
diff --git a/drivers/video/msm/mdp4_util.c b/drivers/video/msm/mdp4_util.c
new file mode 100644
index 0000000..b2657cf
--- /dev/null
+++ b/drivers/video/msm/mdp4_util.c
@@ -0,0 +1,3301 @@
+/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/hrtimer.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+#include <linux/semaphore.h>
+#include <linux/uaccess.h>
+#include <linux/msm_mdp.h>
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <mach/iommu_domains.h>
+#include "mdp.h"
+#include "msm_fb.h"
+#include "mdp4.h"
+
+struct mdp4_statistic mdp4_stat;
+
+unsigned is_mdp4_hw_reset(void)
+{
+	unsigned hw_reset = 0;
+
+	/* Only revisions > v2.1 may be reset or powered off/on at runtime */
+	if (mdp_hw_revision > MDP4_REVISION_V2_1) {
+		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+		hw_reset = !inpdw(MDP_BASE + 0x003c);
+		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+	}
+
+	return hw_reset;
+}
+
+void mdp4_sw_reset(ulong bits)
+{
+	/* MDP cmd block enable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	bits &= 0x1f;	/* 5 bits */
+	outpdw(MDP_BASE + 0x001c, bits);	/* MDP_SW_RESET */
+
+	while (inpdw(MDP_BASE + 0x001c) & bits) /* self clear when complete */
+		;
+	/* MDP cmd block disable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	MSM_FB_DEBUG("mdp4_sw_reset: 0x%x\n", (int)bits);
+}
+
+void mdp4_overlay_cfg(int overlayer, int blt_mode, int refresh, int direct_out)
+{
+	ulong bits = 0;
+
+	if (blt_mode)
+		bits |= (1 << 3);
+	refresh &= 0x03;	/* 2 bites */
+	bits |= (refresh << 1);
+	direct_out &= 0x01;
+	bits |= direct_out;
+	/* MDP cmd block enable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+
+	if (overlayer == MDP4_MIXER0)
+		outpdw(MDP_BASE + 0x10004, bits); /* MDP_OVERLAY0_CFG */
+	else if (overlayer == MDP4_MIXER1)
+		outpdw(MDP_BASE + 0x18004, bits); /* MDP_OVERLAY1_CFG */
+
+	MSM_FB_DEBUG("mdp4_overlay_cfg: 0x%x\n",
+		(int)inpdw(MDP_BASE + 0x10004));
+	/* MDP cmd block disable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+void mdp4_display_intf_sel(int output, ulong intf)
+{
+	ulong bits, mask, data;
+	/* MDP cmd block enable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	bits = inpdw(MDP_BASE + 0x0038);	/* MDP_DISP_INTF_SEL */
+
+	if (intf == DSI_VIDEO_INTF) {
+		data = 0x40;	/* bit 6 */
+		intf = MDDI_LCDC_INTF;
+		if (output == SECONDARY_INTF_SEL) {
+			MSM_FB_INFO("%s: Illegal INTF selected, output=%d \
+				intf=%d\n", __func__, output, (int)intf);
+		}
+	} else if (intf == DSI_CMD_INTF) {
+		data = 0x80;	/* bit 7 */
+		intf = MDDI_INTF;
+		if (output == EXTERNAL_INTF_SEL) {
+			MSM_FB_INFO("%s: Illegal INTF selected, output=%d \
+				intf=%d\n", __func__, output, (int)intf);
+		}
+	} else
+		data = 0;
+
+	mask = 0x03;	/* 2 bits */
+	intf &= 0x03;	/* 2 bits */
+
+	switch (output) {
+	case EXTERNAL_INTF_SEL:
+		intf <<= 4;
+		mask <<= 4;
+		break;
+	case SECONDARY_INTF_SEL:
+		intf &= 0x02;	/* only MDDI and EBI2 support */
+		intf <<= 2;
+		mask <<= 2;
+		break;
+	default:
+		break;
+	}
+
+	intf |= data;
+	mask |= data;
+
+	bits &= ~mask;
+	bits |= intf;
+
+	outpdw(MDP_BASE + 0x0038, bits);	/* MDP_DISP_INTF_SEL */
+	/* MDP cmd block disable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+  MSM_FB_DEBUG("mdp4_display_intf_sel: 0x%x\n", (int)inpdw(MDP_BASE + 0x0038));
+}
+
+unsigned long mdp4_display_status(void)
+{
+	ulong status;
+	/* MDP cmd block enable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	status = inpdw(MDP_BASE + 0x0018) & 0x3ff;	/* MDP_DISPLAY_STATUS */
+
+	/* MDP cmd block disable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+	return status;
+}
+
+void mdp4_ebi2_lcd_setup(int lcd, ulong base, int ystride)
+{
+	/* always use memory map */
+	ystride &= 0x01fff;	/* 13 bits */
+	/* MDP cmd block enable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	if (lcd == EBI2_LCD0) {
+		outpdw(MDP_BASE + 0x0060, base);/* MDP_EBI2_LCD0 */
+		outpdw(MDP_BASE + 0x0068, ystride);/* MDP_EBI2_LCD0_YSTRIDE */
+	} else {
+		outpdw(MDP_BASE + 0x0064, base);/* MDP_EBI2_LCD1 */
+		outpdw(MDP_BASE + 0x006c, ystride);/* MDP_EBI2_LCD1_YSTRIDE */
+	}
+	/* MDP cmd block disable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+void mdp4_mddi_setup(int mddi, unsigned long id)
+{
+	ulong 	bits;
+
+	if (mddi == MDDI_EXTERNAL_SET)
+		bits = 0x02;
+	else if (mddi == MDDI_SECONDARY_SET)
+		bits = 0x01;
+	else
+		bits = 0;	/* PRIMARY_SET */
+
+	id <<= 16;
+
+	bits |= id;
+	/* MDP cmd block enable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	outpdw(MDP_BASE + 0x0090, bits); /* MDP_MDDI_PARAM_WR_SEL */
+	/* MDP cmd block disable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+int mdp_ppp_blit(struct fb_info *info, struct mdp_blit_req *req)
+{
+
+	/* not implemented yet */
+	return -1;
+}
+
+void mdp4_fetch_cfg(uint32 core_clk)
+{
+	uint32 dmap_data, vg_data;
+	char *base;
+	int i;
+	/* MDP cmd block enable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	if (mdp_rev >= MDP_REV_41 || core_clk >= 90000000) { /* 90 Mhz */
+		dmap_data = 0x47; /* 16 bytes-burst x 8 req */
+		vg_data = 0x47; /* 16 bytes-burs x 8 req */
+	} else {
+		dmap_data = 0x27; /* 8 bytes-burst x 8 req */
+		vg_data = 0x43; /* 16 bytes-burst x 4 req */
+	}
+
+	MSM_FB_DEBUG("mdp4_fetch_cfg: dmap=%x vg=%x\n",
+			dmap_data, vg_data);
+
+	/* dma_p fetch config */
+	outpdw(MDP_BASE + 0x91004, dmap_data);
+	/* dma_e fetch config */
+	outpdw(MDP_BASE + 0xB1004, dmap_data);
+
+	/*
+	 * set up two vg pipes and two rgb pipes
+	 */
+	base = MDP_BASE + MDP4_VIDEO_BASE;
+	for (i = 0; i < 4; i++) {
+		outpdw(base + 0x1004, vg_data);
+		base += MDP4_VIDEO_OFF;
+	}
+	/* MDP cmd block disable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+void mdp4_hw_init(void)
+{
+	ulong bits;
+	uint32 clk_rate;
+
+	/* MDP cmd block enable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	mdp4_update_perf_level(OVERLAY_PERF_LEVEL4);
+
+#ifdef MDP4_ERROR
+	/*
+	 * Issue software reset on DMA_P will casue DMA_P dma engine stall
+	 * on LCDC mode. However DMA_P does not stall at MDDI mode.
+	 * This need further investigation.
+	 */
+	mdp4_sw_reset(0x17);
+#endif
+
+	if (mdp_rev > MDP_REV_41) {
+		/* mdp chip select controller */
+		outpdw(MDP_BASE + 0x00c0, CS_CONTROLLER_0);
+		outpdw(MDP_BASE + 0x00c4, CS_CONTROLLER_1);
+	}
+
+	mdp4_clear_lcdc();
+
+	mdp4_mixer_blend_init(0);
+	mdp4_mixer_blend_init(1);
+	mdp4_vg_qseed_init(0);
+	mdp4_vg_qseed_init(1);
+
+	mdp4_vg_csc_setup(0);
+	mdp4_vg_csc_setup(1);
+	mdp4_mixer_csc_setup(1);
+	mdp4_mixer_csc_setup(2);
+	mdp4_dmap_csc_setup();
+
+	if (mdp_rev <= MDP_REV_41) {
+		mdp4_mixer_gc_lut_setup(0);
+		mdp4_mixer_gc_lut_setup(1);
+	}
+
+	mdp4_vg_igc_lut_setup(0);
+	mdp4_vg_igc_lut_setup(1);
+
+	mdp4_rgb_igc_lut_setup(0);
+	mdp4_rgb_igc_lut_setup(1);
+
+	outp32(MDP_EBI2_PORTMAP_MODE, 0x3);
+
+	/* system interrupts */
+
+	bits =  mdp_intr_mask;
+	outpdw(MDP_BASE + 0x0050, bits);/* enable specififed interrupts */
+
+	/* For the max read pending cmd config below, if the MDP clock     */
+	/* is less than the AXI clock, then we must use 3 pending          */
+	/* pending requests.  Otherwise, we should use 8 pending requests. */
+	/* In the future we should do this detection automatically.	   */
+
+	/* max read pending cmd config */
+	outpdw(MDP_BASE + 0x004c, 0x02222);	/* 3 pending requests */
+
+#ifndef CONFIG_FB_MSM_OVERLAY
+	/* both REFRESH_MODE and DIRECT_OUT are ignored at BLT mode */
+	mdp4_overlay_cfg(MDP4_MIXER0, OVERLAY_MODE_BLT, 0, 0);
+	mdp4_overlay_cfg(MDP4_MIXER1, OVERLAY_MODE_BLT, 0, 0);
+#endif
+
+	clk_rate = mdp_get_core_clk();
+	mdp4_fetch_cfg(clk_rate);
+
+	/* Mark hardware as initialized. Only revisions > v2.1 have a register
+	 * for tracking core reset status. */
+	if (mdp_hw_revision > MDP4_REVISION_V2_1)
+		outpdw(MDP_BASE + 0x003c, 1);
+
+	/* MDP cmd block disable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+
+void mdp4_clear_lcdc(void)
+{
+	uint32 bits;
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	bits = inpdw(MDP_BASE + 0xc0000);
+	if (bits & 0x01) { /* enabled already */
+		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+		return;
+	}
+
+	outpdw(MDP_BASE + 0xc0004, 0);	/* vsync ctrl out */
+	outpdw(MDP_BASE + 0xc0008, 0);	/* vsync period */
+	outpdw(MDP_BASE + 0xc000c, 0);	/* vsync pusle width */
+	outpdw(MDP_BASE + 0xc0010, 0);	/* lcdc display HCTL */
+	outpdw(MDP_BASE + 0xc0014, 0);	/* lcdc display v start */
+	outpdw(MDP_BASE + 0xc0018, 0);	/* lcdc display v end */
+	outpdw(MDP_BASE + 0xc001c, 0);	/* lcdc active hctl */
+	outpdw(MDP_BASE + 0xc0020, 0);	/* lcdc active v start */
+	outpdw(MDP_BASE + 0xc0024, 0);	/* lcdc active v end */
+	outpdw(MDP_BASE + 0xc0028, 0);	/* lcdc board color */
+	outpdw(MDP_BASE + 0xc002c, 0);	/* lcdc underflow ctrl */
+	outpdw(MDP_BASE + 0xc0030, 0);	/* lcdc hsync skew */
+	outpdw(MDP_BASE + 0xc0034, 0);	/* lcdc test ctl */
+	outpdw(MDP_BASE + 0xc0038, 0);	/* lcdc ctl polarity */
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+irqreturn_t mdp4_isr(int irq, void *ptr)
+{
+	uint32 isr, mask, panel;
+	struct mdp_dma_data *dma;
+	struct mdp_hist_mgmt *mgmt = NULL;
+	char *base_addr;
+	int i, ret;
+
+	mdp_is_in_isr = TRUE;
+
+	/* complete all the reads before reading the interrupt
+	* status register - eliminate effects of speculative
+	* reads by the cpu
+	*/
+	rmb();
+	isr = inpdw(MDP_INTR_STATUS);
+	if (isr == 0)
+		goto out;
+
+	mdp4_stat.intr_tot++;
+	mask = inpdw(MDP_INTR_ENABLE);
+	outpdw(MDP_INTR_CLEAR, isr);
+
+	if (isr & INTR_PRIMARY_INTF_UDERRUN) {
+		mdp4_stat.intr_underrun_p++;
+		/* When underun occurs mdp clear the histogram registers
+		that are set before in hw_init so restore them back so
+		that histogram works.*/
+		for (i = 0; i < MDP_HIST_MGMT_MAX; i++) {
+			mgmt = mdp_hist_mgmt_array[i];
+			if (!mgmt)
+				continue;
+			base_addr = MDP_BASE + mgmt->base;
+			MDP_OUTP(base_addr + 0x010, 1);
+			outpdw(base_addr + 0x01c, INTR_HIST_DONE |
+						INTR_HIST_RESET_SEQ_DONE);
+			mgmt->mdp_is_hist_valid = FALSE;
+			__mdp_histogram_reset(mgmt);
+		}
+	}
+
+	if (isr & INTR_EXTERNAL_INTF_UDERRUN)
+		mdp4_stat.intr_underrun_e++;
+
+	isr &= mask;
+
+	if (isr == 0)
+		goto out;
+
+	panel = mdp4_overlay_panel_list();
+	if (isr & INTR_PRIMARY_VSYNC) {
+		mdp4_stat.intr_vsync_p++;
+		dma = &dma2_data;
+		spin_lock(&mdp_spin_lock);
+		mdp_intr_mask &= ~INTR_PRIMARY_VSYNC;
+		outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+		dma->waiting = FALSE;
+		if (panel & MDP4_PANEL_LCDC)
+			mdp4_primary_vsync_lcdc();
+#ifdef CONFIG_FB_MSM_MIPI_DSI
+		else if (panel & MDP4_PANEL_DSI_VIDEO)
+			mdp4_primary_vsync_dsi_video();
+#endif
+		spin_unlock(&mdp_spin_lock);
+	}
+#ifdef CONFIG_FB_MSM_DTV
+	if (isr & INTR_EXTERNAL_VSYNC) {
+		mdp4_stat.intr_vsync_e++;
+		dma = &dma_e_data;
+		spin_lock(&mdp_spin_lock);
+		mdp_intr_mask &= ~INTR_EXTERNAL_VSYNC;
+		outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+		dma->waiting = FALSE;
+		if (panel & MDP4_PANEL_DTV)
+			mdp4_external_vsync_dtv();
+		spin_unlock(&mdp_spin_lock);
+	}
+#endif
+
+#ifdef CONFIG_FB_MSM_OVERLAY
+	if (isr & INTR_OVERLAY0_DONE) {
+		mdp4_stat.intr_overlay0++;
+		dma = &dma2_data;
+		if (panel & (MDP4_PANEL_LCDC | MDP4_PANEL_DSI_VIDEO)) {
+			/* disable LCDC interrupt */
+			spin_lock(&mdp_spin_lock);
+			mdp_intr_mask &= ~INTR_OVERLAY0_DONE;
+			outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+			dma->waiting = FALSE;
+			spin_unlock(&mdp_spin_lock);
+			if (panel & MDP4_PANEL_LCDC)
+				mdp4_overlay0_done_lcdc(dma);
+#ifdef CONFIG_FB_MSM_MIPI_DSI
+			else if (panel & MDP4_PANEL_DSI_VIDEO)
+				mdp4_overlay0_done_dsi_video(dma);
+#endif
+		} else {        /* MDDI, DSI_CMD  */
+#ifdef CONFIG_FB_MSM_MIPI_DSI
+			if (panel & MDP4_PANEL_DSI_CMD)
+				mdp4_overlay0_done_dsi_cmd(dma);
+#else
+			if (panel & MDP4_PANEL_MDDI)
+				mdp4_overlay0_done_mddi(dma);
+#endif
+		}
+		mdp_hw_cursor_done();
+	}
+	if (isr & INTR_OVERLAY1_DONE) {
+		mdp4_stat.intr_overlay1++;
+		/* disable DTV interrupt */
+		dma = &dma_e_data;
+		spin_lock(&mdp_spin_lock);
+		mdp_intr_mask &= ~INTR_OVERLAY1_DONE;
+		outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+		dma->waiting = FALSE;
+		spin_unlock(&mdp_spin_lock);
+#if defined(CONFIG_FB_MSM_DTV)
+		if (panel & MDP4_PANEL_DTV)
+			mdp4_overlay1_done_dtv();
+#endif
+#if defined(CONFIG_FB_MSM_TVOUT)
+		if (panel & MDP4_PANEL_ATV)
+			mdp4_overlay1_done_atv();
+#endif
+	}
+#if defined(CONFIG_FB_MSM_WRITEBACK_MSM_PANEL)
+	if (isr & INTR_OVERLAY2_DONE) {
+		mdp4_stat.intr_overlay2++;
+		/* disable DTV interrupt */
+		dma = &dma_wb_data;
+		spin_lock(&mdp_spin_lock);
+		mdp_intr_mask &= ~INTR_OVERLAY2_DONE;
+		outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+		dma->waiting = FALSE;
+		spin_unlock(&mdp_spin_lock);
+		if (panel & MDP4_PANEL_WRITEBACK)
+			mdp4_overlay1_done_writeback(dma);
+	}
+#endif
+#endif	/* OVERLAY */
+
+	if (isr & INTR_DMA_P_DONE) {
+		mdp4_stat.intr_dma_p++;
+		dma = &dma2_data;
+		if (panel & MDP4_PANEL_LCDC) {
+			/* disable LCDC interrupt */
+			spin_lock(&mdp_spin_lock);
+			mdp_intr_mask &= ~INTR_DMA_P_DONE;
+			outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+			dma->waiting = FALSE;
+			mdp4_dma_p_done_lcdc();
+			spin_unlock(&mdp_spin_lock);
+		}
+#ifdef CONFIG_FB_MSM_OVERLAY
+#ifdef CONFIG_FB_MSM_MIPI_DSI
+		else if (panel & MDP4_PANEL_DSI_VIDEO) {
+			/* disable LCDC interrupt */
+			spin_lock(&mdp_spin_lock);
+			mdp_intr_mask &= ~INTR_DMA_P_DONE;
+			outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+			dma->waiting = FALSE;
+			mdp4_dma_p_done_dsi_video(dma);
+			spin_unlock(&mdp_spin_lock);
+		} else if (panel & MDP4_PANEL_DSI_CMD) {
+			mdp4_dma_p_done_dsi(dma);
+		}
+#else
+		else { /* MDDI */
+			mdp4_dma_p_done_mddi();
+			mdp_pipe_ctrl(MDP_DMA2_BLOCK,
+				MDP_BLOCK_POWER_OFF, TRUE);
+			complete(&dma->comp);
+		}
+#endif
+#else
+		else {
+			spin_lock(&mdp_spin_lock);
+			dma->busy = FALSE;
+			spin_unlock(&mdp_spin_lock);
+			complete(&dma->comp);
+		}
+#endif
+	}
+	if (isr & INTR_DMA_S_DONE) {
+		mdp4_stat.intr_dma_s++;
+#if defined(CONFIG_FB_MSM_OVERLAY) && defined(CONFIG_FB_MSM_MDDI)
+		dma = &dma2_data;
+#else
+		dma = &dma_s_data;
+#endif
+
+		dma->busy = FALSE;
+		mdp_pipe_ctrl(MDP_DMA_S_BLOCK,
+				MDP_BLOCK_POWER_OFF, TRUE);
+		complete(&dma->comp);
+	}
+	if (isr & INTR_DMA_E_DONE) {
+		mdp4_stat.intr_dma_e++;
+		dma = &dma_e_data;
+		spin_lock(&mdp_spin_lock);
+		mdp_intr_mask &= ~INTR_DMA_E_DONE;
+		outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+		dma->busy = FALSE;
+		mdp4_dma_e_done_dtv();
+		if (dma->waiting) {
+			dma->waiting = FALSE;
+			complete(&dma->comp);
+		}
+		spin_unlock(&mdp_spin_lock);
+	}
+	if (isr & INTR_DMA_P_HISTOGRAM) {
+		mdp4_stat.intr_histogram++;
+		ret = mdp_histogram_block2mgmt(MDP_BLOCK_DMA_P, &mgmt);
+		if (!ret)
+			mdp_histogram_handle_isr(mgmt);
+	}
+	if (isr & INTR_DMA_S_HISTOGRAM) {
+		mdp4_stat.intr_histogram++;
+		ret = mdp_histogram_block2mgmt(MDP_BLOCK_DMA_S, &mgmt);
+		if (!ret)
+			mdp_histogram_handle_isr(mgmt);
+	}
+	if (isr & INTR_VG1_HISTOGRAM) {
+		mdp4_stat.intr_histogram++;
+		ret = mdp_histogram_block2mgmt(MDP_BLOCK_VG_1, &mgmt);
+		if (!ret)
+			mdp_histogram_handle_isr(mgmt);
+	}
+	if (isr & INTR_VG2_HISTOGRAM) {
+		mdp4_stat.intr_histogram++;
+		ret = mdp_histogram_block2mgmt(MDP_BLOCK_VG_2, &mgmt);
+		if (!ret)
+			mdp_histogram_handle_isr(mgmt);
+	}
+
+out:
+	mdp_is_in_isr = FALSE;
+
+	return IRQ_HANDLED;
+}
+
+
+/*
+ * QSEED tables
+ */
+
+static uint32 vg_qseed_table0[] = {
+	0x5556aaff, 0x00000000, 0x00000000, 0x00000000
+};
+
+static uint32 vg_qseed_table1[] = {
+	0x00000000, 0x20000000,
+};
+
+static uint32 vg_qseed_table2[] = {
+	0x02000000, 0x00000000, 0x01ff0ff9, 0x00000008,
+	0x01fb0ff2, 0x00000013, 0x01f50fed, 0x0ffe0020,
+	0x01ed0fe8, 0x0ffd002e, 0x01e30fe4, 0x0ffb003e,
+	0x01d80fe1, 0x0ff9004e, 0x01cb0fde, 0x0ff70060,
+	0x01bc0fdc, 0x0ff40074, 0x01ac0fdb, 0x0ff20087,
+	0x019a0fdb, 0x0fef009c, 0x01870fdb, 0x0fed00b1,
+	0x01740fdb, 0x0fea00c7, 0x01600fdc, 0x0fe700dd,
+	0x014b0fdd, 0x0fe500f3, 0x01350fdf, 0x0fe30109,
+	0x01200fe0, 0x0fe00120, 0x01090fe3, 0x0fdf0135,
+	0x00f30fe5, 0x0fdd014b, 0x00dd0fe7, 0x0fdc0160,
+	0x00c70fea, 0x0fdb0174, 0x00b10fed, 0x0fdb0187,
+	0x009c0fef, 0x0fdb019a, 0x00870ff2, 0x0fdb01ac,
+	0x00740ff4, 0x0fdc01bc, 0x00600ff7, 0x0fde01cb,
+	0x004e0ff9, 0x0fe101d8, 0x003e0ffb, 0x0fe401e3,
+	0x002e0ffd, 0x0fe801ed, 0x00200ffe, 0x0fed01f5,
+	0x00130000, 0x0ff201fb, 0x00080000, 0x0ff901ff,
+
+	0x02000000, 0x00000000, 0x02000000, 0x00000000,
+	0x02000000, 0x00000000, 0x02000000, 0x00000000,
+	0x02000000, 0x00000000, 0x02000000, 0x00000000,
+	0x02000000, 0x00000000, 0x02000000, 0x00000000,
+	0x02000000, 0x00000000, 0x02000000, 0x00000000,
+	0x02000000, 0x00000000, 0x02000000, 0x00000000,
+	0x02000000, 0x00000000, 0x02000000, 0x00000000,
+	0x02000000, 0x00000000, 0x02000000, 0x00000000,
+	0x02000000, 0x00000000, 0x02000000, 0x00000000,
+	0x02000000, 0x00000000, 0x02000000, 0x00000000,
+	0x02000000, 0x00000000, 0x02000000, 0x00000000,
+	0x02000000, 0x00000000, 0x02000000, 0x00000000,
+	0x02000000, 0x00000000, 0x02000000, 0x00000000,
+	0x02000000, 0x00000000, 0x02000000, 0x00000000,
+	0x02000000, 0x00000000, 0x02000000, 0x00000000,
+	0x02000000, 0x00000000, 0x02000000, 0x00000000,
+
+	0x02000000, 0x00000000, 0x01fc0ff9, 0x0ffe000d,
+	0x01f60ff3, 0x0ffb001c, 0x01ef0fed, 0x0ff9002b,
+	0x01e60fe8, 0x0ff6003c, 0x01dc0fe4, 0x0ff3004d,
+	0x01d00fe0, 0x0ff1005f, 0x01c30fde, 0x0fee0071,
+	0x01b50fdb, 0x0feb0085, 0x01a70fd9, 0x0fe80098,
+	0x01960fd8, 0x0fe600ac, 0x01850fd7, 0x0fe300c1,
+	0x01730fd7, 0x0fe100d5, 0x01610fd7, 0x0fdf00e9,
+	0x014e0fd8, 0x0fdd00fd, 0x013b0fd8, 0x0fdb0112,
+	0x01250fda, 0x0fda0127, 0x01120fdb, 0x0fd8013b,
+	0x00fd0fdd, 0x0fd8014e, 0x00e90fdf, 0x0fd70161,
+	0x00d50fe1, 0x0fd70173, 0x00c10fe3, 0x0fd70185,
+	0x00ac0fe6, 0x0fd80196, 0x00980fe8, 0x0fd901a7,
+	0x00850feb, 0x0fdb01b5, 0x00710fee, 0x0fde01c3,
+	0x005f0ff1, 0x0fe001d0, 0x004d0ff3, 0x0fe401dc,
+	0x003c0ff6, 0x0fe801e6, 0x002b0ff9, 0x0fed01ef,
+	0x001c0ffb, 0x0ff301f6, 0x000d0ffe, 0x0ff901fc,
+
+	0x020f0034, 0x0f7a0043, 0x01e80023, 0x0fa8004d,
+	0x01d30016, 0x0fbe0059, 0x01c6000a, 0x0fc90067,
+	0x01bd0000, 0x0fce0075, 0x01b50ff7, 0x0fcf0085,
+	0x01ae0fee, 0x0fcf0095, 0x01a70fe6, 0x0fcd00a6,
+	0x019d0fe0, 0x0fcb00b8, 0x01940fd9, 0x0fc900ca,
+	0x01890fd4, 0x0fc700dc, 0x017d0fcf, 0x0fc600ee,
+	0x01700fcc, 0x0fc40100, 0x01620fc9, 0x0fc40111,
+	0x01540fc6, 0x0fc30123, 0x01430fc5, 0x0fc40134,
+	0x01340fc4, 0x0fc50143, 0x01230fc3, 0x0fc60154,
+	0x01110fc4, 0x0fc90162, 0x01000fc4, 0x0fcc0170,
+	0x00ee0fc6, 0x0fcf017d, 0x00dc0fc7, 0x0fd40189,
+	0x00ca0fc9, 0x0fd90194, 0x00b80fcb, 0x0fe0019d,
+	0x00a60fcd, 0x0fe601a7, 0x00950fcf, 0x0fee01ae,
+	0x00850fcf, 0x0ff701b5, 0x00750fce, 0x000001bd,
+	0x00670fc9, 0x000a01c6, 0x00590fbe, 0x001601d3,
+	0x004d0fa8, 0x002301e8, 0x00430f7a, 0x0034020f,
+
+	0x015c005e, 0x0fde0068, 0x015c0054, 0x0fdd0073,
+	0x015b004b, 0x0fdc007e, 0x015a0042, 0x0fdb0089,
+	0x01590039, 0x0fda0094, 0x01560030, 0x0fda00a0,
+	0x01530028, 0x0fda00ab, 0x014f0020, 0x0fda00b7,
+	0x014a0019, 0x0fdb00c2, 0x01450011, 0x0fdc00ce,
+	0x013e000b, 0x0fde00d9, 0x01390004, 0x0fdf00e4,
+	0x01310ffe, 0x0fe200ef, 0x01290ff9, 0x0fe400fa,
+	0x01200ff4, 0x0fe80104, 0x01180fef, 0x0feb010e,
+	0x010e0feb, 0x0fef0118, 0x01040fe8, 0x0ff40120,
+	0x00fa0fe4, 0x0ff90129, 0x00ef0fe2, 0x0ffe0131,
+	0x00e40fdf, 0x00040139, 0x00d90fde, 0x000b013e,
+	0x00ce0fdc, 0x00110145, 0x00c20fdb, 0x0019014a,
+	0x00b70fda, 0x0020014f, 0x00ab0fda, 0x00280153,
+	0x00a00fda, 0x00300156, 0x00940fda, 0x00390159,
+	0x00890fdb, 0x0042015a, 0x007e0fdc, 0x004b015b,
+	0x00730fdd, 0x0054015c, 0x00680fde, 0x005e015c,
+
+	0x01300068, 0x0ff80070, 0x01300060, 0x0ff80078,
+	0x012f0059, 0x0ff80080, 0x012d0052, 0x0ff80089,
+	0x012b004b, 0x0ff90091, 0x01290044, 0x0ff9009a,
+	0x0126003d, 0x0ffa00a3, 0x01220037, 0x0ffb00ac,
+	0x011f0031, 0x0ffc00b4, 0x011a002b, 0x0ffe00bd,
+	0x01150026, 0x000000c5, 0x010f0021, 0x000200ce,
+	0x010a001c, 0x000400d6, 0x01030018, 0x000600df,
+	0x00fd0014, 0x000900e6, 0x00f60010, 0x000c00ee,
+	0x00ee000c, 0x001000f6, 0x00e60009, 0x001400fd,
+	0x00df0006, 0x00180103, 0x00d60004, 0x001c010a,
+	0x00ce0002, 0x0021010f, 0x00c50000, 0x00260115,
+	0x00bd0ffe, 0x002b011a, 0x00b40ffc, 0x0031011f,
+	0x00ac0ffb, 0x00370122, 0x00a30ffa, 0x003d0126,
+	0x009a0ff9, 0x00440129, 0x00910ff9, 0x004b012b,
+	0x00890ff8, 0x0052012d, 0x00800ff8, 0x0059012f,
+	0x00780ff8, 0x00600130, 0x00700ff8, 0x00680130,
+
+	0x01050079, 0x0003007f, 0x01040073, 0x00030086,
+	0x0103006d, 0x0004008c, 0x01030066, 0x00050092,
+	0x01010060, 0x00060099, 0x0100005a, 0x0007009f,
+	0x00fe0054, 0x000900a5, 0x00fa004f, 0x000b00ac,
+	0x00f80049, 0x000d00b2, 0x00f50044, 0x000f00b8,
+	0x00f2003f, 0x001200bd, 0x00ef0039, 0x001500c3,
+	0x00ea0035, 0x001800c9, 0x00e60030, 0x001c00ce,
+	0x00e3002b, 0x001f00d3, 0x00dd0027, 0x002300d9,
+	0x00d90023, 0x002700dd, 0x00d3001f, 0x002b00e3,
+	0x00ce001c, 0x003000e6, 0x00c90018, 0x003500ea,
+	0x00c30015, 0x003900ef, 0x00bd0012, 0x003f00f2,
+	0x00b8000f, 0x004400f5, 0x00b2000d, 0x004900f8,
+	0x00ac000b, 0x004f00fa, 0x00a50009, 0x005400fe,
+	0x009f0007, 0x005a0100, 0x00990006, 0x00600101,
+	0x00920005, 0x00660103, 0x008c0004, 0x006d0103,
+	0x00860003, 0x00730104, 0x007f0003, 0x00790105,
+
+	0x00cf0088, 0x001d008c, 0x00ce0084, 0x0020008e,
+	0x00cd0080, 0x00210092, 0x00cd007b, 0x00240094,
+	0x00ca0077, 0x00270098, 0x00c90073, 0x0029009b,
+	0x00c8006f, 0x002c009d, 0x00c6006b, 0x002f00a0,
+	0x00c50067, 0x003200a2, 0x00c30062, 0x003600a5,
+	0x00c0005f, 0x003900a8, 0x00c0005b, 0x003b00aa,
+	0x00be0057, 0x003e00ad, 0x00ba0054, 0x004200b0,
+	0x00b90050, 0x004500b2, 0x00b7004c, 0x004900b4,
+	0x00b40049, 0x004c00b7, 0x00b20045, 0x005000b9,
+	0x00b00042, 0x005400ba, 0x00ad003e, 0x005700be,
+	0x00aa003b, 0x005b00c0, 0x00a80039, 0x005f00c0,
+	0x00a50036, 0x006200c3, 0x00a20032, 0x006700c5,
+	0x00a0002f, 0x006b00c6, 0x009d002c, 0x006f00c8,
+	0x009b0029, 0x007300c9, 0x00980027, 0x007700ca,
+	0x00940024, 0x007b00cd, 0x00920021, 0x008000cd,
+	0x008e0020, 0x008400ce, 0x008c001d, 0x008800cf,
+
+	0x008e0083, 0x006b0084, 0x008d0083, 0x006c0084,
+	0x008d0082, 0x006d0084, 0x008d0081, 0x006d0085,
+	0x008d0080, 0x006e0085, 0x008c007f, 0x006f0086,
+	0x008b007f, 0x00700086, 0x008b007e, 0x00710086,
+	0x008b007d, 0x00720086, 0x008a007d, 0x00730086,
+	0x008a007c, 0x00730087, 0x008a007b, 0x00740087,
+	0x0089007b, 0x00750087, 0x008a0079, 0x00750088,
+	0x008a0078, 0x00760088, 0x008a0077, 0x00770088,
+	0x00880077, 0x0077008a, 0x00880076, 0x0078008a,
+	0x00880075, 0x0079008a, 0x00870075, 0x007b0089,
+	0x00870074, 0x007b008a, 0x00870073, 0x007c008a,
+	0x00860073, 0x007d008a, 0x00860072, 0x007d008b,
+	0x00860071, 0x007e008b, 0x00860070, 0x007f008b,
+	0x0086006f, 0x007f008c, 0x0085006e, 0x0080008d,
+	0x0085006d, 0x0081008d, 0x0084006d, 0x0082008d,
+	0x0084006c, 0x0083008d, 0x0084006b, 0x0083008e,
+
+	0x023c0fe2, 0x00000fe2, 0x023a0fdb, 0x00000feb,
+	0x02360fd3, 0x0fff0ff8, 0x022e0fcf, 0x0ffc0007,
+	0x02250fca, 0x0ffa0017, 0x021a0fc6, 0x0ff70029,
+	0x020c0fc4, 0x0ff4003c, 0x01fd0fc1, 0x0ff10051,
+	0x01eb0fc0, 0x0fed0068, 0x01d80fc0, 0x0fe9007f,
+	0x01c30fc1, 0x0fe50097, 0x01ac0fc2, 0x0fe200b0,
+	0x01960fc3, 0x0fdd00ca, 0x017e0fc5, 0x0fd900e4,
+	0x01650fc8, 0x0fd500fe, 0x014b0fcb, 0x0fd20118,
+	0x01330fcd, 0x0fcd0133, 0x01180fd2, 0x0fcb014b,
+	0x00fe0fd5, 0x0fc80165, 0x00e40fd9, 0x0fc5017e,
+	0x00ca0fdd, 0x0fc30196, 0x00b00fe2, 0x0fc201ac,
+	0x00970fe5, 0x0fc101c3, 0x007f0fe9, 0x0fc001d8,
+	0x00680fed, 0x0fc001eb, 0x00510ff1, 0x0fc101fd,
+	0x003c0ff4, 0x0fc4020c, 0x00290ff7, 0x0fc6021a,
+	0x00170ffa, 0x0fca0225, 0x00070ffc, 0x0fcf022e,
+	0x0ff80fff, 0x0fd30236, 0x0feb0000, 0x0fdb023a,
+
+	0x02780fc4, 0x00000fc4, 0x02770fbc, 0x0fff0fce,
+	0x02710fb5, 0x0ffe0fdc, 0x02690fb0, 0x0ffa0fed,
+	0x025f0fab, 0x0ff70fff, 0x02500fa8, 0x0ff30015,
+	0x02410fa6, 0x0fef002a, 0x022f0fa4, 0x0feb0042,
+	0x021a0fa4, 0x0fe5005d, 0x02040fa5, 0x0fe10076,
+	0x01eb0fa7, 0x0fdb0093, 0x01d20fa9, 0x0fd600af,
+	0x01b80fab, 0x0fd000cd, 0x019d0faf, 0x0fca00ea,
+	0x01810fb2, 0x0fc50108, 0x01620fb7, 0x0fc10126,
+	0x01440fbb, 0x0fbb0146, 0x01260fc1, 0x0fb70162,
+	0x01080fc5, 0x0fb20181, 0x00ea0fca, 0x0faf019d,
+	0x00cd0fd0, 0x0fab01b8, 0x00af0fd6, 0x0fa901d2,
+	0x00930fdb, 0x0fa701eb, 0x00760fe1, 0x0fa50204,
+	0x005d0fe5, 0x0fa4021a, 0x00420feb, 0x0fa4022f,
+	0x002a0fef, 0x0fa60241, 0x00150ff3, 0x0fa80250,
+	0x0fff0ff7, 0x0fab025f, 0x0fed0ffa, 0x0fb00269,
+	0x0fdc0ffe, 0x0fb50271, 0x0fce0fff, 0x0fbc0277,
+
+	0x02a00fb0, 0x00000fb0, 0x029e0fa8, 0x0fff0fbb,
+	0x02980fa1, 0x0ffd0fca, 0x028f0f9c, 0x0ff90fdc,
+	0x02840f97, 0x0ff50ff0, 0x02740f94, 0x0ff10007,
+	0x02640f92, 0x0fec001e, 0x02500f91, 0x0fe70038,
+	0x023a0f91, 0x0fe00055, 0x02220f92, 0x0fdb0071,
+	0x02080f95, 0x0fd4008f, 0x01ec0f98, 0x0fce00ae,
+	0x01cf0f9b, 0x0fc700cf, 0x01b10f9f, 0x0fc100ef,
+	0x01920fa4, 0x0fbb010f, 0x01710faa, 0x0fb50130,
+	0x01520fae, 0x0fae0152, 0x01300fb5, 0x0faa0171,
+	0x010f0fbb, 0x0fa40192, 0x00ef0fc1, 0x0f9f01b1,
+	0x00cf0fc7, 0x0f9b01cf, 0x00ae0fce, 0x0f9801ec,
+	0x008f0fd4, 0x0f950208, 0x00710fdb, 0x0f920222,
+	0x00550fe0, 0x0f91023a, 0x00380fe7, 0x0f910250,
+	0x001e0fec, 0x0f920264, 0x00070ff1, 0x0f940274,
+	0x0ff00ff5, 0x0f970284, 0x0fdc0ff9, 0x0f9c028f,
+	0x0fca0ffd, 0x0fa10298, 0x0fbb0fff, 0x0fa8029e,
+
+	0x02c80f9c, 0x00000f9c, 0x02c70f94, 0x0ffe0fa7,
+	0x02c10f8c, 0x0ffc0fb7, 0x02b70f87, 0x0ff70fcb,
+	0x02aa0f83, 0x0ff30fe0, 0x02990f80, 0x0fee0ff9,
+	0x02870f7f, 0x0fe80012, 0x02720f7e, 0x0fe2002e,
+	0x025a0f7e, 0x0fdb004d, 0x02400f80, 0x0fd5006b,
+	0x02230f84, 0x0fcd008c, 0x02050f87, 0x0fc700ad,
+	0x01e60f8b, 0x0fbf00d0, 0x01c60f90, 0x0fb700f3,
+	0x01a30f96, 0x0fb00117, 0x01800f9c, 0x0faa013a,
+	0x015d0fa2, 0x0fa2015f, 0x013a0faa, 0x0f9c0180,
+	0x01170fb0, 0x0f9601a3, 0x00f30fb7, 0x0f9001c6,
+	0x00d00fbf, 0x0f8b01e6, 0x00ad0fc7, 0x0f870205,
+	0x008c0fcd, 0x0f840223, 0x006b0fd5, 0x0f800240,
+	0x004d0fdb, 0x0f7e025a, 0x002e0fe2, 0x0f7e0272,
+	0x00120fe8, 0x0f7f0287, 0x0ff90fee, 0x0f800299,
+	0x0fe00ff3, 0x0f8302aa, 0x0fcb0ff7, 0x0f8702b7,
+	0x0fb70ffc, 0x0f8c02c1, 0x0fa70ffe, 0x0f9402c7,
+
+	0x02f00f88, 0x00000f88, 0x02ee0f80, 0x0ffe0f94,
+	0x02e70f78, 0x0ffc0fa5, 0x02dd0f73, 0x0ff60fba,
+	0x02ce0f6f, 0x0ff20fd1, 0x02be0f6c, 0x0feb0feb,
+	0x02aa0f6b, 0x0fe50006, 0x02940f6a, 0x0fde0024,
+	0x02790f6c, 0x0fd60045, 0x025e0f6e, 0x0fcf0065,
+	0x023f0f72, 0x0fc60089, 0x021d0f77, 0x0fbf00ad,
+	0x01fd0f7b, 0x0fb600d2, 0x01da0f81, 0x0fad00f8,
+	0x01b50f87, 0x0fa6011e, 0x018f0f8f, 0x0f9e0144,
+	0x016b0f95, 0x0f95016b, 0x01440f9e, 0x0f8f018f,
+	0x011e0fa6, 0x0f8701b5, 0x00f80fad, 0x0f8101da,
+	0x00d20fb6, 0x0f7b01fd, 0x00ad0fbf, 0x0f77021d,
+	0x00890fc6, 0x0f72023f, 0x00650fcf, 0x0f6e025e,
+	0x00450fd6, 0x0f6c0279, 0x00240fde, 0x0f6a0294,
+	0x00060fe5, 0x0f6b02aa, 0x0feb0feb, 0x0f6c02be,
+	0x0fd10ff2, 0x0f6f02ce, 0x0fba0ff6, 0x0f7302dd,
+	0x0fa50ffc, 0x0f7802e7, 0x0f940ffe, 0x0f8002ee,
+
+	0x03180f74, 0x00000f74, 0x03160f6b, 0x0ffe0f81,
+	0x030e0f64, 0x0ffb0f93, 0x03030f5f, 0x0ff50fa9,
+	0x02f40f5b, 0x0ff00fc1, 0x02e20f58, 0x0fe90fdd,
+	0x02cd0f57, 0x0fe20ffa, 0x02b60f57, 0x0fda0019,
+	0x02990f59, 0x0fd1003d, 0x027b0f5c, 0x0fc90060,
+	0x02590f61, 0x0fc00086, 0x02370f66, 0x0fb700ac,
+	0x02130f6b, 0x0fae00d4, 0x01ee0f72, 0x0fa400fc,
+	0x01c70f79, 0x0f9b0125, 0x019f0f81, 0x0f93014d,
+	0x01760f89, 0x0f890178, 0x014d0f93, 0x0f81019f,
+	0x01250f9b, 0x0f7901c7, 0x00fc0fa4, 0x0f7201ee,
+	0x00d40fae, 0x0f6b0213, 0x00ac0fb7, 0x0f660237,
+	0x00860fc0, 0x0f610259, 0x00600fc9, 0x0f5c027b,
+	0x003d0fd1, 0x0f590299, 0x00190fda, 0x0f5702b6,
+	0x0ffa0fe2, 0x0f5702cd, 0x0fdd0fe9, 0x0f5802e2,
+	0x0fc10ff0, 0x0f5b02f4, 0x0fa90ff5, 0x0f5f0303,
+	0x0f930ffb, 0x0f64030e, 0x0f810ffe, 0x0f6b0316,
+
+	0x03400f60, 0x00000f60, 0x033e0f57, 0x0ffe0f6d,
+	0x03370f4f, 0x0ffa0f80, 0x032a0f4b, 0x0ff30f98,
+	0x031a0f46, 0x0fee0fb2, 0x03070f44, 0x0fe60fcf,
+	0x02f10f44, 0x0fde0fed, 0x02d70f44, 0x0fd6000f,
+	0x02b80f46, 0x0fcc0036, 0x02990f4a, 0x0fc3005a,
+	0x02750f4f, 0x0fb90083, 0x02500f55, 0x0fb000ab,
+	0x022a0f5b, 0x0fa500d6, 0x02020f63, 0x0f9a0101,
+	0x01d80f6b, 0x0f91012c, 0x01ae0f74, 0x0f870157,
+	0x01840f7c, 0x0f7c0184, 0x01570f87, 0x0f7401ae,
+	0x012c0f91, 0x0f6b01d8, 0x01010f9a, 0x0f630202,
+	0x00d60fa5, 0x0f5b022a, 0x00ab0fb0, 0x0f550250,
+	0x00830fb9, 0x0f4f0275, 0x005a0fc3, 0x0f4a0299,
+	0x00360fcc, 0x0f4602b8, 0x000f0fd6, 0x0f4402d7,
+	0x0fed0fde, 0x0f4402f1, 0x0fcf0fe6, 0x0f440307,
+	0x0fb20fee, 0x0f46031a, 0x0f980ff3, 0x0f4b032a,
+	0x0f800ffa, 0x0f4f0337, 0x0f6d0ffe, 0x0f57033e,
+
+	0x02000000, 0x00000000, 0x01ff0ff9, 0x00000008,
+	0x01fb0ff2, 0x00000013, 0x01f50fed, 0x0ffe0020,
+	0x01ed0fe8, 0x0ffd002e, 0x01e30fe4, 0x0ffb003e,
+	0x01d80fe1, 0x0ff9004e, 0x01cb0fde, 0x0ff70060,
+	0x01bc0fdc, 0x0ff40074, 0x01ac0fdb, 0x0ff20087,
+	0x019a0fdb, 0x0fef009c, 0x01870fdb, 0x0fed00b1,
+	0x01740fdb, 0x0fea00c7, 0x01600fdc, 0x0fe700dd,
+	0x014b0fdd, 0x0fe500f3, 0x01350fdf, 0x0fe30109,
+	0x01200fe0, 0x0fe00120, 0x01090fe3, 0x0fdf0135,
+	0x00f30fe5, 0x0fdd014b, 0x00dd0fe7, 0x0fdc0160,
+	0x00c70fea, 0x0fdb0174, 0x00b10fed, 0x0fdb0187,
+	0x009c0fef, 0x0fdb019a, 0x00870ff2, 0x0fdb01ac,
+	0x00740ff4, 0x0fdc01bc, 0x00600ff7, 0x0fde01cb,
+	0x004e0ff9, 0x0fe101d8, 0x003e0ffb, 0x0fe401e3,
+	0x002e0ffd, 0x0fe801ed, 0x00200ffe, 0x0fed01f5,
+	0x00130000, 0x0ff201fb, 0x00080000, 0x0ff901ff,
+
+	0x02000000, 0x00000000, 0x02000000, 0x00000000,
+	0x02000000, 0x00000000, 0x02000000, 0x00000000,
+	0x02000000, 0x00000000, 0x02000000, 0x00000000,
+	0x02000000, 0x00000000, 0x02000000, 0x00000000,
+	0x02000000, 0x00000000, 0x02000000, 0x00000000,
+	0x02000000, 0x00000000, 0x02000000, 0x00000000,
+	0x02000000, 0x00000000, 0x02000000, 0x00000000,
+	0x02000000, 0x00000000, 0x02000000, 0x00000000,
+	0x02000000, 0x00000000, 0x02000000, 0x00000000,
+	0x02000000, 0x00000000, 0x02000000, 0x00000000,
+	0x02000000, 0x00000000, 0x02000000, 0x00000000,
+	0x02000000, 0x00000000, 0x02000000, 0x00000000,
+	0x02000000, 0x00000000, 0x02000000, 0x00000000,
+	0x02000000, 0x00000000, 0x02000000, 0x00000000,
+	0x02000000, 0x00000000, 0x02000000, 0x00000000,
+	0x02000000, 0x00000000, 0x02000000, 0x00000000,
+
+	0x02000000, 0x00000000, 0x01fc0ff9, 0x0ffe000d,
+	0x01f60ff3, 0x0ffb001c, 0x01ef0fed, 0x0ff9002b,
+	0x01e60fe8, 0x0ff6003c, 0x01dc0fe4, 0x0ff3004d,
+	0x01d00fe0, 0x0ff1005f, 0x01c30fde, 0x0fee0071,
+	0x01b50fdb, 0x0feb0085, 0x01a70fd9, 0x0fe80098,
+	0x01960fd8, 0x0fe600ac, 0x01850fd7, 0x0fe300c1,
+	0x01730fd7, 0x0fe100d5, 0x01610fd7, 0x0fdf00e9,
+	0x014e0fd8, 0x0fdd00fd, 0x013b0fd8, 0x0fdb0112,
+	0x01250fda, 0x0fda0127, 0x01120fdb, 0x0fd8013b,
+	0x00fd0fdd, 0x0fd8014e, 0x00e90fdf, 0x0fd70161,
+	0x00d50fe1, 0x0fd70173, 0x00c10fe3, 0x0fd70185,
+	0x00ac0fe6, 0x0fd80196, 0x00980fe8, 0x0fd901a7,
+	0x00850feb, 0x0fdb01b5, 0x00710fee, 0x0fde01c3,
+	0x005f0ff1, 0x0fe001d0, 0x004d0ff3, 0x0fe401dc,
+	0x003c0ff6, 0x0fe801e6, 0x002b0ff9, 0x0fed01ef,
+	0x001c0ffb, 0x0ff301f6, 0x000d0ffe, 0x0ff901fc,
+
+	0x020f0034, 0x0f7a0043, 0x01e80023, 0x0fa8004d,
+	0x01d30016, 0x0fbe0059, 0x01c6000a, 0x0fc90067,
+	0x01bd0000, 0x0fce0075, 0x01b50ff7, 0x0fcf0085,
+	0x01ae0fee, 0x0fcf0095, 0x01a70fe6, 0x0fcd00a6,
+	0x019d0fe0, 0x0fcb00b8, 0x01940fd9, 0x0fc900ca,
+	0x01890fd4, 0x0fc700dc, 0x017d0fcf, 0x0fc600ee,
+	0x01700fcc, 0x0fc40100, 0x01620fc9, 0x0fc40111,
+	0x01540fc6, 0x0fc30123, 0x01430fc5, 0x0fc40134,
+	0x01340fc4, 0x0fc50143, 0x01230fc3, 0x0fc60154,
+	0x01110fc4, 0x0fc90162, 0x01000fc4, 0x0fcc0170,
+	0x00ee0fc6, 0x0fcf017d, 0x00dc0fc7, 0x0fd40189,
+	0x00ca0fc9, 0x0fd90194, 0x00b80fcb, 0x0fe0019d,
+	0x00a60fcd, 0x0fe601a7, 0x00950fcf, 0x0fee01ae,
+	0x00850fcf, 0x0ff701b5, 0x00750fce, 0x000001bd,
+	0x00670fc9, 0x000a01c6, 0x00590fbe, 0x001601d3,
+	0x004d0fa8, 0x002301e8, 0x00430f7a, 0x0034020f,
+
+	0x015c005e, 0x0fde0068, 0x015c0054, 0x0fdd0073,
+	0x015b004b, 0x0fdc007e, 0x015a0042, 0x0fdb0089,
+	0x01590039, 0x0fda0094, 0x01560030, 0x0fda00a0,
+	0x01530028, 0x0fda00ab, 0x014f0020, 0x0fda00b7,
+	0x014a0019, 0x0fdb00c2, 0x01450011, 0x0fdc00ce,
+	0x013e000b, 0x0fde00d9, 0x01390004, 0x0fdf00e4,
+	0x01310ffe, 0x0fe200ef, 0x01290ff9, 0x0fe400fa,
+	0x01200ff4, 0x0fe80104, 0x01180fef, 0x0feb010e,
+	0x010e0feb, 0x0fef0118, 0x01040fe8, 0x0ff40120,
+	0x00fa0fe4, 0x0ff90129, 0x00ef0fe2, 0x0ffe0131,
+	0x00e40fdf, 0x00040139, 0x00d90fde, 0x000b013e,
+	0x00ce0fdc, 0x00110145, 0x00c20fdb, 0x0019014a,
+	0x00b70fda, 0x0020014f, 0x00ab0fda, 0x00280153,
+	0x00a00fda, 0x00300156, 0x00940fda, 0x00390159,
+	0x00890fdb, 0x0042015a, 0x007e0fdc, 0x004b015b,
+	0x00730fdd, 0x0054015c, 0x00680fde, 0x005e015c,
+
+	0x01300068, 0x0ff80070, 0x01300060, 0x0ff80078,
+	0x012f0059, 0x0ff80080, 0x012d0052, 0x0ff80089,
+	0x012b004b, 0x0ff90091, 0x01290044, 0x0ff9009a,
+	0x0126003d, 0x0ffa00a3, 0x01220037, 0x0ffb00ac,
+	0x011f0031, 0x0ffc00b4, 0x011a002b, 0x0ffe00bd,
+	0x01150026, 0x000000c5, 0x010f0021, 0x000200ce,
+	0x010a001c, 0x000400d6, 0x01030018, 0x000600df,
+	0x00fd0014, 0x000900e6, 0x00f60010, 0x000c00ee,
+	0x00ee000c, 0x001000f6, 0x00e60009, 0x001400fd,
+	0x00df0006, 0x00180103, 0x00d60004, 0x001c010a,
+	0x00ce0002, 0x0021010f, 0x00c50000, 0x00260115,
+	0x00bd0ffe, 0x002b011a, 0x00b40ffc, 0x0031011f,
+	0x00ac0ffb, 0x00370122, 0x00a30ffa, 0x003d0126,
+	0x009a0ff9, 0x00440129, 0x00910ff9, 0x004b012b,
+	0x00890ff8, 0x0052012d, 0x00800ff8, 0x0059012f,
+	0x00780ff8, 0x00600130, 0x00700ff8, 0x00680130,
+
+	0x01050079, 0x0003007f, 0x01040073, 0x00030086,
+	0x0103006d, 0x0004008c, 0x01030066, 0x00050092,
+	0x01010060, 0x00060099, 0x0100005a, 0x0007009f,
+	0x00fe0054, 0x000900a5, 0x00fa004f, 0x000b00ac,
+	0x00f80049, 0x000d00b2, 0x00f50044, 0x000f00b8,
+	0x00f2003f, 0x001200bd, 0x00ef0039, 0x001500c3,
+	0x00ea0035, 0x001800c9, 0x00e60030, 0x001c00ce,
+	0x00e3002b, 0x001f00d3, 0x00dd0027, 0x002300d9,
+	0x00d90023, 0x002700dd, 0x00d3001f, 0x002b00e3,
+	0x00ce001c, 0x003000e6, 0x00c90018, 0x003500ea,
+	0x00c30015, 0x003900ef, 0x00bd0012, 0x003f00f2,
+	0x00b8000f, 0x004400f5, 0x00b2000d, 0x004900f8,
+	0x00ac000b, 0x004f00fa, 0x00a50009, 0x005400fe,
+	0x009f0007, 0x005a0100, 0x00990006, 0x00600101,
+	0x00920005, 0x00660103, 0x008c0004, 0x006d0103,
+	0x00860003, 0x00730104, 0x007f0003, 0x00790105,
+
+	0x00cf0088, 0x001d008c, 0x00ce0084, 0x0020008e,
+	0x00cd0080, 0x00210092, 0x00cd007b, 0x00240094,
+	0x00ca0077, 0x00270098, 0x00c90073, 0x0029009b,
+	0x00c8006f, 0x002c009d, 0x00c6006b, 0x002f00a0,
+	0x00c50067, 0x003200a2, 0x00c30062, 0x003600a5,
+	0x00c0005f, 0x003900a8, 0x00c0005b, 0x003b00aa,
+	0x00be0057, 0x003e00ad, 0x00ba0054, 0x004200b0,
+	0x00b90050, 0x004500b2, 0x00b7004c, 0x004900b4,
+	0x00b40049, 0x004c00b7, 0x00b20045, 0x005000b9,
+	0x00b00042, 0x005400ba, 0x00ad003e, 0x005700be,
+	0x00aa003b, 0x005b00c0, 0x00a80039, 0x005f00c0,
+	0x00a50036, 0x006200c3, 0x00a20032, 0x006700c5,
+	0x00a0002f, 0x006b00c6, 0x009d002c, 0x006f00c8,
+	0x009b0029, 0x007300c9, 0x00980027, 0x007700ca,
+	0x00940024, 0x007b00cd, 0x00920021, 0x008000cd,
+	0x008e0020, 0x008400ce, 0x008c001d, 0x008800cf,
+
+	0x008e0083, 0x006b0084, 0x008d0083, 0x006c0084,
+	0x008d0082, 0x006d0084, 0x008d0081, 0x006d0085,
+	0x008d0080, 0x006e0085, 0x008c007f, 0x006f0086,
+	0x008b007f, 0x00700086, 0x008b007e, 0x00710086,
+	0x008b007d, 0x00720086, 0x008a007d, 0x00730086,
+	0x008a007c, 0x00730087, 0x008a007b, 0x00740087,
+	0x0089007b, 0x00750087, 0x008a0079, 0x00750088,
+	0x008a0078, 0x00760088, 0x008a0077, 0x00770088,
+	0x00880077, 0x0077008a, 0x00880076, 0x0078008a,
+	0x00880075, 0x0079008a, 0x00870075, 0x007b0089,
+	0x00870074, 0x007b008a, 0x00870073, 0x007c008a,
+	0x00860073, 0x007d008a, 0x00860072, 0x007d008b,
+	0x00860071, 0x007e008b, 0x00860070, 0x007f008b,
+	0x0086006f, 0x007f008c, 0x0085006e, 0x0080008d,
+	0x0085006d, 0x0081008d, 0x0084006d, 0x0082008d,
+	0x0084006c, 0x0083008d, 0x0084006b, 0x0083008e,
+
+	0x023c0fe2, 0x00000fe2, 0x023a0fdb, 0x00000feb,
+	0x02360fd3, 0x0fff0ff8, 0x022e0fcf, 0x0ffc0007,
+	0x02250fca, 0x0ffa0017, 0x021a0fc6, 0x0ff70029,
+	0x020c0fc4, 0x0ff4003c, 0x01fd0fc1, 0x0ff10051,
+	0x01eb0fc0, 0x0fed0068, 0x01d80fc0, 0x0fe9007f,
+	0x01c30fc1, 0x0fe50097, 0x01ac0fc2, 0x0fe200b0,
+	0x01960fc3, 0x0fdd00ca, 0x017e0fc5, 0x0fd900e4,
+	0x01650fc8, 0x0fd500fe, 0x014b0fcb, 0x0fd20118,
+	0x01330fcd, 0x0fcd0133, 0x01180fd2, 0x0fcb014b,
+	0x00fe0fd5, 0x0fc80165, 0x00e40fd9, 0x0fc5017e,
+	0x00ca0fdd, 0x0fc30196, 0x00b00fe2, 0x0fc201ac,
+	0x00970fe5, 0x0fc101c3, 0x007f0fe9, 0x0fc001d8,
+	0x00680fed, 0x0fc001eb, 0x00510ff1, 0x0fc101fd,
+	0x003c0ff4, 0x0fc4020c, 0x00290ff7, 0x0fc6021a,
+	0x00170ffa, 0x0fca0225, 0x00070ffc, 0x0fcf022e,
+	0x0ff80fff, 0x0fd30236, 0x0feb0000, 0x0fdb023a,
+
+	0x02780fc4, 0x00000fc4, 0x02770fbc, 0x0fff0fce,
+	0x02710fb5, 0x0ffe0fdc, 0x02690fb0, 0x0ffa0fed,
+	0x025f0fab, 0x0ff70fff, 0x02500fa8, 0x0ff30015,
+	0x02410fa6, 0x0fef002a, 0x022f0fa4, 0x0feb0042,
+	0x021a0fa4, 0x0fe5005d, 0x02040fa5, 0x0fe10076,
+	0x01eb0fa7, 0x0fdb0093, 0x01d20fa9, 0x0fd600af,
+	0x01b80fab, 0x0fd000cd, 0x019d0faf, 0x0fca00ea,
+	0x01810fb2, 0x0fc50108, 0x01620fb7, 0x0fc10126,
+	0x01440fbb, 0x0fbb0146, 0x01260fc1, 0x0fb70162,
+	0x01080fc5, 0x0fb20181, 0x00ea0fca, 0x0faf019d,
+	0x00cd0fd0, 0x0fab01b8, 0x00af0fd6, 0x0fa901d2,
+	0x00930fdb, 0x0fa701eb, 0x00760fe1, 0x0fa50204,
+	0x005d0fe5, 0x0fa4021a, 0x00420feb, 0x0fa4022f,
+	0x002a0fef, 0x0fa60241, 0x00150ff3, 0x0fa80250,
+	0x0fff0ff7, 0x0fab025f, 0x0fed0ffa, 0x0fb00269,
+	0x0fdc0ffe, 0x0fb50271, 0x0fce0fff, 0x0fbc0277,
+
+	0x02a00fb0, 0x00000fb0, 0x029e0fa8, 0x0fff0fbb,
+	0x02980fa1, 0x0ffd0fca, 0x028f0f9c, 0x0ff90fdc,
+	0x02840f97, 0x0ff50ff0, 0x02740f94, 0x0ff10007,
+	0x02640f92, 0x0fec001e, 0x02500f91, 0x0fe70038,
+	0x023a0f91, 0x0fe00055, 0x02220f92, 0x0fdb0071,
+	0x02080f95, 0x0fd4008f, 0x01ec0f98, 0x0fce00ae,
+	0x01cf0f9b, 0x0fc700cf, 0x01b10f9f, 0x0fc100ef,
+	0x01920fa4, 0x0fbb010f, 0x01710faa, 0x0fb50130,
+	0x01520fae, 0x0fae0152, 0x01300fb5, 0x0faa0171,
+	0x010f0fbb, 0x0fa40192, 0x00ef0fc1, 0x0f9f01b1,
+	0x00cf0fc7, 0x0f9b01cf, 0x00ae0fce, 0x0f9801ec,
+	0x008f0fd4, 0x0f950208, 0x00710fdb, 0x0f920222,
+	0x00550fe0, 0x0f91023a, 0x00380fe7, 0x0f910250,
+	0x001e0fec, 0x0f920264, 0x00070ff1, 0x0f940274,
+	0x0ff00ff5, 0x0f970284, 0x0fdc0ff9, 0x0f9c028f,
+	0x0fca0ffd, 0x0fa10298, 0x0fbb0fff, 0x0fa8029e,
+
+	0x02c80f9c, 0x00000f9c, 0x02c70f94, 0x0ffe0fa7,
+	0x02c10f8c, 0x0ffc0fb7, 0x02b70f87, 0x0ff70fcb,
+	0x02aa0f83, 0x0ff30fe0, 0x02990f80, 0x0fee0ff9,
+	0x02870f7f, 0x0fe80012, 0x02720f7e, 0x0fe2002e,
+	0x025a0f7e, 0x0fdb004d, 0x02400f80, 0x0fd5006b,
+	0x02230f84, 0x0fcd008c, 0x02050f87, 0x0fc700ad,
+	0x01e60f8b, 0x0fbf00d0, 0x01c60f90, 0x0fb700f3,
+	0x01a30f96, 0x0fb00117, 0x01800f9c, 0x0faa013a,
+	0x015d0fa2, 0x0fa2015f, 0x013a0faa, 0x0f9c0180,
+	0x01170fb0, 0x0f9601a3, 0x00f30fb7, 0x0f9001c6,
+	0x00d00fbf, 0x0f8b01e6, 0x00ad0fc7, 0x0f870205,
+	0x008c0fcd, 0x0f840223, 0x006b0fd5, 0x0f800240,
+	0x004d0fdb, 0x0f7e025a, 0x002e0fe2, 0x0f7e0272,
+	0x00120fe8, 0x0f7f0287, 0x0ff90fee, 0x0f800299,
+	0x0fe00ff3, 0x0f8302aa, 0x0fcb0ff7, 0x0f8702b7,
+	0x0fb70ffc, 0x0f8c02c1, 0x0fa70ffe, 0x0f9402c7,
+
+	0x02f00f88, 0x00000f88, 0x02ee0f80, 0x0ffe0f94,
+	0x02e70f78, 0x0ffc0fa5, 0x02dd0f73, 0x0ff60fba,
+	0x02ce0f6f, 0x0ff20fd1, 0x02be0f6c, 0x0feb0feb,
+	0x02aa0f6b, 0x0fe50006, 0x02940f6a, 0x0fde0024,
+	0x02790f6c, 0x0fd60045, 0x025e0f6e, 0x0fcf0065,
+	0x023f0f72, 0x0fc60089, 0x021d0f77, 0x0fbf00ad,
+	0x01fd0f7b, 0x0fb600d2, 0x01da0f81, 0x0fad00f8,
+	0x01b50f87, 0x0fa6011e, 0x018f0f8f, 0x0f9e0144,
+	0x016b0f95, 0x0f95016b, 0x01440f9e, 0x0f8f018f,
+	0x011e0fa6, 0x0f8701b5, 0x00f80fad, 0x0f8101da,
+	0x00d20fb6, 0x0f7b01fd, 0x00ad0fbf, 0x0f77021d,
+	0x00890fc6, 0x0f72023f, 0x00650fcf, 0x0f6e025e,
+	0x00450fd6, 0x0f6c0279, 0x00240fde, 0x0f6a0294,
+	0x00060fe5, 0x0f6b02aa, 0x0feb0feb, 0x0f6c02be,
+	0x0fd10ff2, 0x0f6f02ce, 0x0fba0ff6, 0x0f7302dd,
+	0x0fa50ffc, 0x0f7802e7, 0x0f940ffe, 0x0f8002ee,
+
+	0x03180f74, 0x00000f74, 0x03160f6b, 0x0ffe0f81,
+	0x030e0f64, 0x0ffb0f93, 0x03030f5f, 0x0ff50fa9,
+	0x02f40f5b, 0x0ff00fc1, 0x02e20f58, 0x0fe90fdd,
+	0x02cd0f57, 0x0fe20ffa, 0x02b60f57, 0x0fda0019,
+	0x02990f59, 0x0fd1003d, 0x027b0f5c, 0x0fc90060,
+	0x02590f61, 0x0fc00086, 0x02370f66, 0x0fb700ac,
+	0x02130f6b, 0x0fae00d4, 0x01ee0f72, 0x0fa400fc,
+	0x01c70f79, 0x0f9b0125, 0x019f0f81, 0x0f93014d,
+	0x01760f89, 0x0f890178, 0x014d0f93, 0x0f81019f,
+	0x01250f9b, 0x0f7901c7, 0x00fc0fa4, 0x0f7201ee,
+	0x00d40fae, 0x0f6b0213, 0x00ac0fb7, 0x0f660237,
+	0x00860fc0, 0x0f610259, 0x00600fc9, 0x0f5c027b,
+	0x003d0fd1, 0x0f590299, 0x00190fda, 0x0f5702b6,
+	0x0ffa0fe2, 0x0f5702cd, 0x0fdd0fe9, 0x0f5802e2,
+	0x0fc10ff0, 0x0f5b02f4, 0x0fa90ff5, 0x0f5f0303,
+	0x0f930ffb, 0x0f64030e, 0x0f810ffe, 0x0f6b0316,
+
+	0x03400f60, 0x00000f60, 0x033e0f57, 0x0ffe0f6d,
+	0x03370f4f, 0x0ffa0f80, 0x032a0f4b, 0x0ff30f98,
+	0x031a0f46, 0x0fee0fb2, 0x03070f44, 0x0fe60fcf,
+	0x02f10f44, 0x0fde0fed, 0x02d70f44, 0x0fd6000f,
+	0x02b80f46, 0x0fcc0036, 0x02990f4a, 0x0fc3005a,
+	0x02750f4f, 0x0fb90083, 0x02500f55, 0x0fb000ab,
+	0x022a0f5b, 0x0fa500d6, 0x02020f63, 0x0f9a0101,
+	0x01d80f6b, 0x0f91012c, 0x01ae0f74, 0x0f870157,
+	0x01840f7c, 0x0f7c0184, 0x01570f87, 0x0f7401ae,
+	0x012c0f91, 0x0f6b01d8, 0x01010f9a, 0x0f630202,
+	0x00d60fa5, 0x0f5b022a, 0x00ab0fb0, 0x0f550250,
+	0x00830fb9, 0x0f4f0275, 0x005a0fc3, 0x0f4a0299,
+	0x00360fcc, 0x0f4602b8, 0x000f0fd6, 0x0f4402d7,
+	0x0fed0fde, 0x0f4402f1, 0x0fcf0fe6, 0x0f440307,
+	0x0fb20fee, 0x0f46031a, 0x0f980ff3, 0x0f4b032a,
+	0x0f800ffa, 0x0f4f0337, 0x0f6d0ffe, 0x0f57033e
+};
+
+
+#define MDP4_QSEED_TABLE0_OFF 0x8100
+#define MDP4_QSEED_TABLE1_OFF 0x8200
+#define MDP4_QSEED_TABLE2_OFF 0x9000
+
+void mdp4_vg_qseed_init(int vp_num)
+{
+	uint32 *off;
+	int i, voff;
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	voff = MDP4_VIDEO_OFF * vp_num;
+	off = (uint32 *)(MDP_BASE + MDP4_VIDEO_BASE + voff +
+						MDP4_QSEED_TABLE0_OFF);
+	for (i = 0; i < (sizeof(vg_qseed_table0) / sizeof(uint32)); i++) {
+		outpdw(off, vg_qseed_table0[i]);
+		off++;
+		/* This code is added to workaround the 1K Boundary AXI
+		Interleave operations from Scorpion that can potentially
+		corrupt the QSEED table. The idea is to complete the prevous
+		to the buffer before making the next write when address is
+		1KB aligned to ensure the write has been committed prior to
+		next instruction write that can go out from  the secondary AXI
+		port.This happens also because of the expected write sequence
+		from QSEED table, where LSP has to be written first then the
+		MSP to trigger both to write out to SRAM, if this has not been
+		the expectation, then corruption wouldn't have happened.*/
+
+		if (!((uint32)off & 0x3FF))
+			wmb();
+	}
+
+	off = (uint32 *)(MDP_BASE + MDP4_VIDEO_BASE + voff +
+						MDP4_QSEED_TABLE1_OFF);
+	for (i = 0; i < (sizeof(vg_qseed_table1) / sizeof(uint32)); i++) {
+		outpdw(off, vg_qseed_table1[i]);
+		off++;
+		if (!((uint32)off & 0x3FF))
+			wmb();
+	}
+
+	off = (uint32 *)(MDP_BASE + MDP4_VIDEO_BASE + voff +
+						MDP4_QSEED_TABLE2_OFF);
+	for (i = 0; i < (sizeof(vg_qseed_table2) / sizeof(uint32)); i++) {
+		outpdw(off, vg_qseed_table2[i]);
+		off++;
+		if (!((uint32)off & 0x3FF))
+			wmb();
+	}
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+}
+
+void mdp4_mixer_blend_init(mixer_num)
+{
+	unsigned char *overlay_base;
+	int off;
+
+	if (mixer_num) 	/* mixer number, /dev/fb0, /dev/fb1 */
+		overlay_base = MDP_BASE + MDP4_OVERLAYPROC1_BASE;/* 0x18000 */
+	else
+		overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	/* stage 0 to stage 2 */
+	off = 0;
+	outpdw(overlay_base + off + 0x104, 0x010);
+	outpdw(overlay_base + off + 0x108, 0xff);/* FG */
+	outpdw(overlay_base + off + 0x10c, 0x00);/* BG */
+
+	off += 0x20;
+	outpdw(overlay_base + off + 0x104, 0x010);
+	outpdw(overlay_base + off + 0x108, 0xff);/* FG */
+	outpdw(overlay_base + off + 0x10c, 0x00);/* BG */
+
+	off += 0x20;
+	outpdw(overlay_base + off + 0x104, 0x010);
+	outpdw(overlay_base + off + 0x108, 0xff);/* FG */
+	outpdw(overlay_base + off + 0x10c, 0x00);/* BG */
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+struct mdp_csc_cfg mdp_csc_convert[4] = {
+	{ /*RGB2RGB*/
+		0,
+		{
+			0x0200, 0x0000, 0x0000,
+			0x0000, 0x0200, 0x0000,
+			0x0000, 0x0000, 0x0200,
+		},
+		{ 0x0, 0x0, 0x0, },
+		{ 0x0, 0x0, 0x0, },
+		{ 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, },
+		{ 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, },
+	},
+	{ /*YUV2RGB*/
+		0,
+		{
+			0x0254, 0x0000, 0x0331,
+			0x0254, 0xff37, 0xfe60,
+			0x0254, 0x0409, 0x0000,
+		},
+		{ 0xfff0, 0xff80, 0xff80, },
+		{ 0x0, 0x0, 0x0, },
+		{ 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, },
+		{ 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, },
+	},
+	{ /*RGB2YUV*/
+		0,
+		{
+			0x0083, 0x0102, 0x0032,
+			0x1fb5, 0x1f6c, 0x00e1,
+			0x00e1, 0x1f45, 0x1fdc
+		},
+		{ 0x0, 0x0, 0x0, },
+		{ 0x0010, 0x0080, 0x0080, },
+		{ 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, },
+		{ 0x0010, 0x00eb, 0x0010, 0x00f0, 0x0010, 0x00f0, },
+	},
+	{ /*YUV2YUV ???*/
+		0,
+		{
+			0x0200, 0x0000, 0x0000,
+			0x0000, 0x0200, 0x0000,
+			0x0000, 0x0000, 0x0200,
+		},
+		{ 0x0, 0x0, 0x0, },
+		{ 0x0, 0x0, 0x0, },
+		{ 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, },
+		{ 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, },
+	},
+};
+
+struct mdp_csc_cfg csc_matrix[3] = {
+	{
+		(MDP_CSC_FLAG_YUV_OUT),
+		{
+			0x0254, 0x0000, 0x0331,
+			0x0254, 0xff37, 0xfe60,
+			0x0254, 0x0409, 0x0000,
+		},
+		{
+			0xfff0, 0xff80, 0xff80,
+		},
+		{
+			0, 0, 0,
+		},
+		{
+			0, 0xff, 0, 0xff, 0, 0xff,
+		},
+		{
+			0, 0xff, 0, 0xff, 0, 0xff,
+		},
+	},
+	{
+		(MDP_CSC_FLAG_YUV_OUT),
+		{
+			0x0254, 0x0000, 0x0331,
+			0x0254, 0xff37, 0xfe60,
+			0x0254, 0x0409, 0x0000,
+		},
+		{
+			0xfff0, 0xff80, 0xff80,
+		},
+		{
+			0, 0, 0,
+		},
+		{
+			0, 0xff, 0, 0xff, 0, 0xff,
+		},
+		{
+			0, 0xff, 0, 0xff, 0, 0xff,
+		},
+	},
+	{
+		(0),
+		{
+			0x0200, 0x0000, 0x0000,
+			0x0000, 0x0200, 0x0000,
+			0x0000, 0x0000, 0x0200,
+		},
+		{
+			0x0, 0x0, 0x0,
+		},
+		{
+			0, 0, 0,
+		},
+		{
+			0, 0xff, 0, 0xff, 0, 0xff,
+		},
+		{
+			0, 0xff, 0, 0xff, 0, 0xff,
+		},
+	},
+};
+
+
+
+#define MDP4_CSC_MV_OFF 	0x4400
+#define MDP4_CSC_PRE_BV_OFF 	0x4500
+#define MDP4_CSC_POST_BV_OFF 	0x4580
+#define MDP4_CSC_PRE_LV_OFF 	0x4600
+#define MDP4_CSC_POST_LV_OFF 	0x4680
+
+void mdp4_vg_csc_mv_setup(int vp_num)
+{
+	uint32 *off;
+	int i, voff;
+
+	voff = MDP4_VIDEO_OFF * vp_num;
+	off = (uint32 *)(MDP_BASE + MDP4_VIDEO_BASE + voff +
+					MDP4_CSC_MV_OFF);
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	for (i = 0; i < 9; i++) {
+		outpdw(off, csc_matrix[vp_num].csc_mv[i]);
+		off++;
+	}
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+void mdp4_vg_csc_pre_bv_setup(int vp_num)
+{
+	uint32 *off;
+	int i, voff;
+
+	voff = MDP4_VIDEO_OFF * vp_num;
+	off = (uint32 *)(MDP_BASE + MDP4_VIDEO_BASE + voff +
+					MDP4_CSC_PRE_BV_OFF);
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	for (i = 0; i < 3; i++) {
+		outpdw(off, csc_matrix[vp_num].csc_pre_bv[i]);
+		off++;
+	}
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+void mdp4_vg_csc_post_bv_setup(int vp_num)
+{
+	uint32 *off;
+	int i, voff;
+
+	voff = MDP4_VIDEO_OFF * vp_num;
+	off = (uint32 *)(MDP_BASE + MDP4_VIDEO_BASE + voff +
+					MDP4_CSC_POST_BV_OFF);
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	for (i = 0; i < 3; i++) {
+		outpdw(off, csc_matrix[vp_num].csc_post_bv[i]);
+		off++;
+	}
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+void mdp4_vg_csc_pre_lv_setup(int vp_num)
+{
+	uint32 *off;
+	int i, voff;
+
+	voff = MDP4_VIDEO_OFF * vp_num;
+	off = (uint32 *)(MDP_BASE + MDP4_VIDEO_BASE + voff +
+					MDP4_CSC_PRE_LV_OFF);
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	for (i = 0; i < 6; i++) {
+		outpdw(off, csc_matrix[vp_num].csc_pre_lv[i]);
+		off++;
+	}
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+void mdp4_vg_csc_post_lv_setup(int vp_num)
+{
+	uint32 *off;
+	int i, voff;
+
+	voff = MDP4_VIDEO_OFF * vp_num;
+	off = (uint32 *)(MDP_BASE + MDP4_VIDEO_BASE + voff +
+					MDP4_CSC_POST_LV_OFF);
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	for (i = 0; i < 6; i++) {
+		outpdw(off, csc_matrix[vp_num].csc_post_lv[i]);
+		off++;
+	}
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+void mdp4_vg_csc_convert_setup(int vp_num)
+{
+	struct mdp_csc_cfg_data cfg;
+
+	switch (vp_num) {
+	case 0:
+		cfg.block = MDP_BLOCK_VG_1;
+		break;
+	case 1:
+		cfg.block = MDP_BLOCK_VG_2;
+		break;
+	default:
+		pr_err("%s - invalid vp_num = %d", __func__, vp_num);
+		return;
+	}
+	cfg.csc_data = csc_matrix[vp_num];
+	mdp4_csc_enable(&cfg);
+}
+
+void mdp4_vg_csc_setup(int vp_num)
+{
+		/* yuv2rgb */
+		mdp4_vg_csc_mv_setup(vp_num);
+		mdp4_vg_csc_pre_bv_setup(vp_num);
+		mdp4_vg_csc_post_bv_setup(vp_num);
+		mdp4_vg_csc_pre_lv_setup(vp_num);
+		mdp4_vg_csc_post_lv_setup(vp_num);
+		mdp4_vg_csc_convert_setup(vp_num);
+}
+void mdp4_vg_csc_update(struct mdp_csc *p)
+{
+	struct mdp4_overlay_pipe *pipe;
+	int vp_num;
+
+	pipe = mdp4_overlay_ndx2pipe(p->id);
+	if (pipe == NULL) {
+		pr_err("%s: p->id = %d Error\n", __func__, p->id);
+		return;
+	}
+
+	vp_num = pipe->pipe_num - OVERLAY_PIPE_VG1;
+
+	if (vp_num == 0 || vp_num == 1) {
+		memcpy(csc_matrix[vp_num].csc_mv, p->csc_mv, sizeof(p->csc_mv));
+		memcpy(csc_matrix[vp_num].csc_pre_bv, p->csc_pre_bv,
+			sizeof(p->csc_pre_bv));
+		memcpy(csc_matrix[vp_num].csc_post_bv, p->csc_post_bv,
+			sizeof(p->csc_post_bv));
+		memcpy(csc_matrix[vp_num].csc_pre_lv, p->csc_pre_lv,
+			sizeof(p->csc_pre_lv));
+		memcpy(csc_matrix[vp_num].csc_post_lv, p->csc_post_lv,
+			sizeof(p->csc_post_lv));
+		mdp4_vg_csc_setup(vp_num);
+	}
+}
+static uint32 csc_rgb2yuv_matrix_tab[9] = {
+	0x0083, 0x0102, 0x0032,
+	0x1fb5, 0x1f6c, 0x00e1,
+	0x00e1, 0x1f45, 0x1fdc
+};
+
+static uint32 csc_rgb2yuv_pre_bv_tab[3] = {0, 0, 0};
+
+static uint32 csc_rgb2yuv_post_bv_tab[3] = {0x0010, 0x0080, 0x0080};
+
+static  uint32 csc_rgb2yuv_pre_lv_tab[6] = {
+	0x00, 0xff, 0x00,
+	0xff, 0x00, 0xff
+};
+
+static  uint32 csc_rgb2yuv_post_lv_tab[6] = {
+	0x0010, 0x00eb, 0x0010,
+	0x00f0, 0x0010, 0x00f0
+};
+
+void mdp4_mixer_csc_mv_setup(uint32 mixer)
+{
+	uint32 *off;
+	int i;
+
+	if (mixer == MDP4_MIXER1)
+		off = (uint32 *)(MDP_BASE + MDP4_OVERLAYPROC1_BASE + 0x2400);
+	else
+		off = (uint32 *)(MDP_BASE + MDP4_OVERLAYPROC2_BASE + 0x2400);
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	for (i = 0; i < 9; i++) {
+		outpdw(off, csc_rgb2yuv_matrix_tab[i]);
+		off++;
+	}
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+void mdp4_mixer_csc_pre_bv_setup(uint32 mixer)
+{
+	uint32 *off;
+	int i;
+
+	if (mixer == MDP4_MIXER1)
+		off = (uint32 *)(MDP_BASE + MDP4_OVERLAYPROC1_BASE + 0x2500);
+	else
+		off = (uint32 *)(MDP_BASE + MDP4_OVERLAYPROC2_BASE + 0x2500);
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	for (i = 0; i < 3; i++) {
+		outpdw(off, csc_rgb2yuv_pre_bv_tab[i]);
+		off++;
+	}
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+void mdp4_mixer_csc_post_bv_setup(uint32 mixer)
+{
+	uint32 *off;
+	int i;
+
+	if (mixer == MDP4_MIXER1)
+		off = (uint32 *)(MDP_BASE + MDP4_OVERLAYPROC1_BASE + 0x2580);
+	else
+		off = (uint32 *)(MDP_BASE + MDP4_OVERLAYPROC2_BASE + 0x2580);
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	for (i = 0; i < 3; i++) {
+		outpdw(off, csc_rgb2yuv_post_bv_tab[i]);
+		off++;
+	}
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+void mdp4_mixer_csc_pre_lv_setup(uint32 mixer)
+{
+	uint32 *off;
+	int i;
+
+	if (mixer == MDP4_MIXER1)
+		off = (uint32 *)(MDP_BASE + MDP4_OVERLAYPROC1_BASE + 0x2600);
+	else
+		off = (uint32 *)(MDP_BASE + MDP4_OVERLAYPROC2_BASE + 0x2600);
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	for (i = 0; i < 6; i++) {
+		outpdw(off, csc_rgb2yuv_pre_lv_tab[i]);
+		off++;
+	}
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+void mdp4_mixer_csc_post_lv_setup(uint32 mixer)
+{
+	uint32 *off;
+	int i;
+
+	if (mixer == MDP4_MIXER1)
+		off = (uint32 *)(MDP_BASE + MDP4_OVERLAYPROC1_BASE + 0x2680);
+	else
+		off = (uint32 *)(MDP_BASE + MDP4_OVERLAYPROC2_BASE + 0x2680);
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	for (i = 0; i < 6; i++) {
+		outpdw(off, csc_rgb2yuv_post_lv_tab[i]);
+		off++;
+	}
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+void mdp4_mixer_csc_setup(uint32 mixer)
+{
+	if (mixer >= MDP4_MIXER1) {
+		/* rgb2yuv */
+		mdp4_mixer_csc_mv_setup(mixer);
+		mdp4_mixer_csc_pre_bv_setup(mixer);
+		mdp4_mixer_csc_post_bv_setup(mixer);
+		mdp4_mixer_csc_pre_lv_setup(mixer);
+		mdp4_mixer_csc_post_lv_setup(mixer);
+	}
+}
+
+#define DMA_P_BASE 0x90000
+void mdp4_dmap_csc_mv_setup(void)
+{
+	uint32 *off;
+	int i;
+
+	off = (uint32 *)(MDP_BASE + DMA_P_BASE + 0x3400);
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	for (i = 0; i < 9; i++) {
+		outpdw(off, csc_matrix[2].csc_mv[i]);
+		off++;
+	}
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+void mdp4_dmap_csc_pre_bv_setup(void)
+{
+	uint32 *off;
+	int i;
+
+	off = (uint32 *)(MDP_BASE + DMA_P_BASE + 0x3500);
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	for (i = 0; i < 3; i++) {
+		outpdw(off, csc_matrix[2].csc_pre_bv[i]);
+		off++;
+	}
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+void mdp4_dmap_csc_post_bv_setup(void)
+{
+	uint32 *off;
+	int i;
+
+	off = (uint32 *)(MDP_BASE + DMA_P_BASE + 0x3580);
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	for (i = 0; i < 3; i++) {
+		outpdw(off, csc_matrix[2].csc_post_bv[i]);
+		off++;
+	}
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+void mdp4_dmap_csc_pre_lv_setup(void)
+{
+	uint32 *off;
+	int i;
+
+	off = (uint32 *)(MDP_BASE + DMA_P_BASE + 0x3600);
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	for (i = 0; i < 6; i++) {
+		outpdw(off, csc_matrix[2].csc_pre_lv[i]);
+		off++;
+	}
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+void mdp4_dmap_csc_post_lv_setup(void)
+{
+	uint32 *off;
+	int i;
+
+	off = (uint32 *)(MDP_BASE + DMA_P_BASE + 0x3680);
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	for (i = 0; i < 6; i++) {
+		outpdw(off, csc_matrix[2].csc_post_lv[i]);
+		off++;
+	}
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+void mdp4_dmap_csc_setup(void)
+{
+	mdp4_dmap_csc_mv_setup();
+	mdp4_dmap_csc_pre_bv_setup();
+	mdp4_dmap_csc_post_bv_setup();
+	mdp4_dmap_csc_pre_lv_setup();
+	mdp4_dmap_csc_post_lv_setup();
+}
+
+char gc_lut[] = {
+	0x0, 0x1, 0x2, 0x2, 0x3, 0x4, 0x5, 0x6,
+	0x6, 0x7, 0x8, 0x9, 0xA, 0xA, 0xB, 0xC,
+	0xD, 0xD, 0xE, 0xF, 0xF, 0x10, 0x10, 0x11,
+	0x12, 0x12, 0x13, 0x13, 0x14, 0x14, 0x15, 0x15,
+	0x16, 0x16, 0x17, 0x17, 0x17, 0x18, 0x18, 0x19,
+	0x19, 0x19, 0x1A, 0x1A, 0x1B, 0x1B, 0x1B, 0x1C,
+	0x1C, 0x1D, 0x1D, 0x1D, 0x1E, 0x1E, 0x1E, 0x1F,
+	0x1F, 0x1F, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21,
+	0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x24,
+	0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x26, 0x26,
+	0x26, 0x26, 0x27, 0x27, 0x27, 0x28, 0x28, 0x28,
+	0x28, 0x29, 0x29, 0x29, 0x29, 0x2A, 0x2A, 0x2A,
+	0x2A, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2C, 0x2C,
+	0x2C, 0x2C, 0x2D, 0x2D, 0x2D, 0x2D, 0x2E, 0x2E,
+	0x2E, 0x2E, 0x2E, 0x2F, 0x2F, 0x2F, 0x2F, 0x30,
+	0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x31,
+	0x31, 0x32, 0x32, 0x32, 0x32, 0x32, 0x33, 0x33,
+	0x33, 0x33, 0x33, 0x34, 0x34, 0x34, 0x34, 0x34,
+	0x35, 0x35, 0x35, 0x35, 0x35, 0x36, 0x36, 0x36,
+	0x36, 0x36, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+	0x38, 0x38, 0x38, 0x38, 0x38, 0x39, 0x39, 0x39,
+	0x39, 0x39, 0x39, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A,
+	0x3A, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3C,
+	0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3D, 0x3D, 0x3D,
+	0x3D, 0x3D, 0x3D, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E,
+	0x3E, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x40,
+	0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x41, 0x41,
+	0x41, 0x41, 0x41, 0x41, 0x42, 0x42, 0x42, 0x42,
+	0x42, 0x42, 0x42, 0x43, 0x43, 0x43, 0x43, 0x43,
+	0x43, 0x43, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
+	0x44, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45,
+	0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x47,
+	0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x48, 0x48,
+	0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x49, 0x49,
+	0x49, 0x49, 0x49, 0x49, 0x49, 0x4A, 0x4A, 0x4A,
+	0x4A, 0x4A, 0x4A, 0x4A, 0x4A, 0x4B, 0x4B, 0x4B,
+	0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4C, 0x4C, 0x4C,
+	0x4C, 0x4C, 0x4C, 0x4C, 0x4D, 0x4D, 0x4D, 0x4D,
+	0x4D, 0x4D, 0x4D, 0x4D, 0x4E, 0x4E, 0x4E, 0x4E,
+	0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4F, 0x4F, 0x4F,
+	0x4F, 0x4F, 0x4F, 0x4F, 0x4F, 0x50, 0x50, 0x50,
+	0x50, 0x50, 0x50, 0x50, 0x50, 0x51, 0x51, 0x51,
+	0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x52, 0x52,
+	0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x53, 0x53,
+	0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x54,
+	0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
+	0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
+	0x55, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56,
+	0x56, 0x56, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+	0x57, 0x57, 0x57, 0x58, 0x58, 0x58, 0x58, 0x58,
+	0x58, 0x58, 0x58, 0x58, 0x58, 0x59, 0x59, 0x59,
+	0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x5A, 0x5A,
+	0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A,
+	0x5B, 0x5B, 0x5B, 0x5B, 0x5B, 0x5B, 0x5B, 0x5B,
+	0x5B, 0x5B, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
+	0x5C, 0x5C, 0x5C, 0x5C, 0x5D, 0x5D, 0x5D, 0x5D,
+	0x5D, 0x5D, 0x5D, 0x5D, 0x5D, 0x5D, 0x5E, 0x5E,
+	0x5E, 0x5E, 0x5E, 0x5E, 0x5E, 0x5E, 0x5E, 0x5E,
+	0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F,
+	0x5F, 0x5F, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60,
+	0x60, 0x60, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61,
+	0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x62,
+	0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62,
+	0x62, 0x62, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+	0x63, 0x63, 0x63, 0x63, 0x63, 0x64, 0x64, 0x64,
+	0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64,
+	0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65,
+	0x65, 0x65, 0x65, 0x66, 0x66, 0x66, 0x66, 0x66,
+	0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x67, 0x67,
+	0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67,
+	0x67, 0x67, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68,
+	0x68, 0x68, 0x68, 0x68, 0x68, 0x69, 0x69, 0x69,
+	0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+	0x69, 0x6A, 0x6A, 0x6A, 0x6A, 0x6A, 0x6A, 0x6A,
+	0x6A, 0x6A, 0x6A, 0x6A, 0x6A, 0x6B, 0x6B, 0x6B,
+	0x6B, 0x6B, 0x6B, 0x6B, 0x6B, 0x6B, 0x6B, 0x6B,
+	0x6B, 0x6C, 0x6C, 0x6C, 0x6C, 0x6C, 0x6C, 0x6C,
+	0x6C, 0x6C, 0x6C, 0x6C, 0x6C, 0x6D, 0x6D, 0x6D,
+	0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D,
+	0x6D, 0x6E, 0x6E, 0x6E, 0x6E, 0x6E, 0x6E, 0x6E,
+	0x6E, 0x6E, 0x6E, 0x6E, 0x6E, 0x6F, 0x6F, 0x6F,
+	0x6F, 0x6F, 0x6F, 0x6F, 0x6F, 0x6F, 0x6F, 0x6F,
+	0x6F, 0x6F, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70,
+	0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x71, 0x71,
+	0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71,
+	0x71, 0x71, 0x71, 0x72, 0x72, 0x72, 0x72, 0x72,
+	0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72,
+	0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73,
+	0x73, 0x73, 0x73, 0x73, 0x73, 0x74, 0x74, 0x74,
+	0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74,
+	0x74, 0x74, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75,
+	0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75,
+	0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76,
+	0x76, 0x76, 0x76, 0x76, 0x76, 0x77, 0x77, 0x77,
+	0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
+	0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x78, 0x78,
+	0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+	0x78, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79,
+	0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x7A, 0x7A,
+	0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A,
+	0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7B, 0x7B, 0x7B,
+	0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B,
+	0x7B, 0x7B, 0x7B, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C,
+	0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C,
+	0x7C, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D,
+	0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D,
+	0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E,
+	0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F,
+	0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F,
+	0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x80, 0x80, 0x80,
+	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+	0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x81,
+	0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+	0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82, 0x82,
+	0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+	0x82, 0x82, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+	0x83, 0x83, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+	0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+	0x84, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+	0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+	0x85, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+	0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+	0x86, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+	0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+	0x87, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+	0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+	0x88, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+	0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+	0x89, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A,
+	0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A,
+	0x8A, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B,
+	0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B,
+	0x8B, 0x8B, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C,
+	0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C,
+	0x8C, 0x8C, 0x8C, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D,
+	0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D,
+	0x8D, 0x8D, 0x8D, 0x8D, 0x8E, 0x8E, 0x8E, 0x8E,
+	0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E,
+	0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8F, 0x8F, 0x8F,
+	0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
+	0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x90, 0x90,
+	0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+	0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x91,
+	0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+	0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+	0x91, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+	0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+	0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+	0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+	0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94,
+	0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+	0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x95, 0x95,
+	0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+	0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+	0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+	0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+	0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x97, 0x97,
+	0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+	0x97, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98,
+	0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+	0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+	0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+	0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+	0x99, 0x99, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
+	0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
+	0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9B, 0x9B, 0x9B,
+	0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B,
+	0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B,
+	0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C,
+	0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C,
+	0x9C, 0x9C, 0x9C, 0x9C, 0x9D, 0x9D, 0x9D, 0x9D,
+	0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D,
+	0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9E,
+	0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E,
+	0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E,
+	0x9E, 0x9E, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F,
+	0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F,
+	0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0xA0, 0xA0,
+	0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0,
+	0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0,
+	0xA0, 0xA0, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1,
+	0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1,
+	0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA2, 0xA2,
+	0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2,
+	0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2,
+	0xA2, 0xA2, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3,
+	0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3,
+	0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA4, 0xA4,
+	0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4,
+	0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4,
+	0xA4, 0xA4, 0xA4, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5,
+	0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5,
+	0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5,
+	0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6,
+	0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6,
+	0xA6, 0xA6, 0xA6, 0xA6, 0xA7, 0xA7, 0xA7, 0xA7,
+	0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7,
+	0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7,
+	0xA7, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8,
+	0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8,
+	0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA9,
+	0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9,
+	0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9,
+	0xA9, 0xA9, 0xA9, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA,
+	0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+	0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+	0xAA, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB,
+	0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB,
+	0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAC,
+	0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC,
+	0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC,
+	0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAD, 0xAD, 0xAD,
+	0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD,
+	0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD,
+	0xAD, 0xAD, 0xAD, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE,
+	0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE,
+	0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE,
+	0xAE, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF,
+	0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF,
+	0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xB0,
+	0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0,
+	0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0,
+	0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB1, 0xB1,
+	0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1,
+	0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1,
+	0xB1, 0xB1, 0xB1, 0xB1, 0xB2, 0xB2, 0xB2, 0xB2,
+	0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2,
+	0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2,
+	0xB2, 0xB2, 0xB2, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3,
+	0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3,
+	0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3,
+	0xB3, 0xB3, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4,
+	0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4,
+	0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4,
+	0xB4, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5,
+	0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5,
+	0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5,
+	0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6,
+	0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6,
+	0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6,
+	0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7,
+	0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7,
+	0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB8,
+	0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8,
+	0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8,
+	0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB9,
+	0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9,
+	0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9,
+	0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xBA,
+	0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA,
+	0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA,
+	0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBB,
+	0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB,
+	0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB,
+	0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB,
+	0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC,
+	0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC,
+	0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC,
+	0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD,
+	0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD,
+	0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD,
+	0xBD, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE,
+	0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE,
+	0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE,
+	0xBE, 0xBE, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF,
+	0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF,
+	0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF,
+	0xBF, 0xBF, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0,
+	0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0,
+	0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0,
+	0xC0, 0xC0, 0xC0, 0xC0, 0xC1, 0xC1, 0xC1, 0xC1,
+	0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1,
+	0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1,
+	0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC2, 0xC2, 0xC2,
+	0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2,
+	0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2,
+	0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC3, 0xC3,
+	0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3,
+	0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3,
+	0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3,
+	0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4,
+	0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4,
+	0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4,
+	0xC4, 0xC4, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5,
+	0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5,
+	0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5,
+	0xC5, 0xC5, 0xC5, 0xC5, 0xC6, 0xC6, 0xC6, 0xC6,
+	0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6,
+	0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6,
+	0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC7, 0xC7,
+	0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7,
+	0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7,
+	0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7,
+	0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8,
+	0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8,
+	0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8,
+	0xC8, 0xC8, 0xC8, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9,
+	0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9,
+	0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9,
+	0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xCA, 0xCA,
+	0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA,
+	0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA,
+	0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA,
+	0xCA, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB,
+	0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB,
+	0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB,
+	0xCB, 0xCB, 0xCB, 0xCB, 0xCC, 0xCC, 0xCC, 0xCC,
+	0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
+	0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
+	0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCD,
+	0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD,
+	0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD,
+	0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD,
+	0xCD, 0xCD, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE,
+	0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE,
+	0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE,
+	0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCF, 0xCF,
+	0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF,
+	0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF,
+	0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF,
+	0xCF, 0xCF, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0,
+	0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0,
+	0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0,
+	0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD1, 0xD1, 0xD1,
+	0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1,
+	0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1,
+	0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1,
+	0xD1, 0xD1, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+	0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+	0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+	0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD3, 0xD3,
+	0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3,
+	0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3,
+	0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3,
+	0xD3, 0xD3, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4,
+	0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4,
+	0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4,
+	0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD5,
+	0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5,
+	0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5,
+	0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5,
+	0xD5, 0xD5, 0xD5, 0xD5, 0xD6, 0xD6, 0xD6, 0xD6,
+	0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6,
+	0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6,
+	0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6,
+	0xD6, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7,
+	0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7,
+	0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7,
+	0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD8, 0xD8,
+	0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8,
+	0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8,
+	0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8,
+	0xD8, 0xD8, 0xD8, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9,
+	0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9,
+	0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9,
+	0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9,
+	0xD9, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA,
+	0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA,
+	0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA,
+	0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDB, 0xDB,
+	0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB,
+	0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB,
+	0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB,
+	0xDB, 0xDB, 0xDB, 0xDB, 0xDC, 0xDC, 0xDC, 0xDC,
+	0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
+	0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
+	0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
+	0xDC, 0xDC, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD,
+	0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD,
+	0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD,
+	0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD,
+	0xDD, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE,
+	0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE,
+	0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE,
+	0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDF,
+	0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF,
+	0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF,
+	0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF,
+	0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xE0, 0xE0,
+	0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0,
+	0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0,
+	0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0,
+	0xE0, 0xE0, 0xE0, 0xE0, 0xE1, 0xE1, 0xE1, 0xE1,
+	0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1,
+	0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1,
+	0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1,
+	0xE1, 0xE1, 0xE1, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2,
+	0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2,
+	0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2,
+	0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2,
+	0xE2, 0xE2, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3,
+	0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3,
+	0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3,
+	0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3,
+	0xE3, 0xE3, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4,
+	0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4,
+	0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4,
+	0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4,
+	0xE4, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+	0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+	0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+	0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+	0xE5, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6,
+	0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6,
+	0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6,
+	0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6,
+	0xE6, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7,
+	0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7,
+	0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7,
+	0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7,
+	0xE7, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8,
+	0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8,
+	0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8,
+	0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8,
+	0xE8, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9,
+	0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9,
+	0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9,
+	0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9,
+	0xE9, 0xE9, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA,
+	0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA,
+	0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA,
+	0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA,
+	0xEA, 0xEA, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB,
+	0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB,
+	0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB,
+	0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB,
+	0xEB, 0xEB, 0xEB, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC,
+	0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC,
+	0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC,
+	0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC,
+	0xEC, 0xEC, 0xEC, 0xEC, 0xED, 0xED, 0xED, 0xED,
+	0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED,
+	0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED,
+	0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED,
+	0xED, 0xED, 0xED, 0xED, 0xED, 0xEE, 0xEE, 0xEE,
+	0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE,
+	0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE,
+	0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE,
+	0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEF, 0xEF,
+	0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF,
+	0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF,
+	0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF,
+	0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF,
+	0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
+	0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
+	0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
+	0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
+	0xF0, 0xF0, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1,
+	0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1,
+	0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1,
+	0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1,
+	0xF1, 0xF1, 0xF1, 0xF1, 0xF2, 0xF2, 0xF2, 0xF2,
+	0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
+	0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
+	0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
+	0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF3, 0xF3,
+	0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3,
+	0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3,
+	0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3,
+	0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3,
+	0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4,
+	0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4,
+	0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4,
+	0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4,
+	0xF4, 0xF4, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5,
+	0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5,
+	0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5,
+	0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5,
+	0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF6, 0xF6, 0xF6,
+	0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6,
+	0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6,
+	0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6,
+	0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6,
+	0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7,
+	0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7,
+	0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7,
+	0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7,
+	0xF7, 0xF7, 0xF7, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8,
+	0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8,
+	0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8,
+	0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8,
+	0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF9, 0xF9,
+	0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9,
+	0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9,
+	0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9,
+	0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9,
+	0xF9, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA,
+	0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA,
+	0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA,
+	0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA,
+	0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFB, 0xFB, 0xFB,
+	0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB,
+	0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB,
+	0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB,
+	0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB,
+	0xFB, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+	0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+	0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+	0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+	0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFD, 0xFD, 0xFD,
+	0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD,
+	0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD,
+	0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD,
+	0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD,
+	0xFD, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
+	0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
+	0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
+	0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
+	0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+};
+
+void mdp4_mixer_gc_lut_setup(int mixer_num)
+{
+	unsigned char *base;
+	uint32 data;
+	char val;
+	int i, off;
+
+	if (mixer_num) 	/* mixer number, /dev/fb0, /dev/fb1 */
+		base = MDP_BASE + MDP4_OVERLAYPROC1_BASE;/* 0x18000 */
+	else
+		base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */
+
+	base += 0x4000;	/* GC_LUT offset */
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	off = 0;
+	for (i = 0; i < 4096; i++) {
+		val = gc_lut[i];
+		data = (val << 16 | val << 8 | val); /* R, B, and G are same */
+		outpdw(base + off, data);
+		off += 4;
+	}
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+uint32 igc_video_lut[] = {	 /* non linear */
+	0x0, 0x1, 0x2, 0x4, 0x5, 0x6, 0x7, 0x9,
+	0xA, 0xB, 0xC, 0xE, 0xF, 0x10, 0x12, 0x14,
+	0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F, 0x21, 0x23,
+	0x25, 0x28, 0x2A, 0x2D, 0x30, 0x32, 0x35, 0x38,
+	0x3B, 0x3E, 0x42, 0x45, 0x48, 0x4C, 0x4F, 0x53,
+	0x57, 0x5B, 0x5F, 0x63, 0x67, 0x6B, 0x70, 0x74,
+	0x79, 0x7E, 0x83, 0x88, 0x8D, 0x92, 0x97, 0x9C,
+	0xA2, 0xA8, 0xAD, 0xB3, 0xB9, 0xBF, 0xC5, 0xCC,
+	0xD2, 0xD8, 0xDF, 0xE6, 0xED, 0xF4, 0xFB, 0x102,
+	0x109, 0x111, 0x118, 0x120, 0x128, 0x130, 0x138, 0x140,
+	0x149, 0x151, 0x15A, 0x162, 0x16B, 0x174, 0x17D, 0x186,
+	0x190, 0x199, 0x1A3, 0x1AC, 0x1B6, 0x1C0, 0x1CA, 0x1D5,
+	0x1DF, 0x1EA, 0x1F4, 0x1FF, 0x20A, 0x215, 0x220, 0x22B,
+	0x237, 0x242, 0x24E, 0x25A, 0x266, 0x272, 0x27F, 0x28B,
+	0x298, 0x2A4, 0x2B1, 0x2BE, 0x2CB, 0x2D8, 0x2E6, 0x2F3,
+	0x301, 0x30F, 0x31D, 0x32B, 0x339, 0x348, 0x356, 0x365,
+	0x374, 0x383, 0x392, 0x3A1, 0x3B1, 0x3C0, 0x3D0, 0x3E0,
+	0x3F0, 0x400, 0x411, 0x421, 0x432, 0x443, 0x454, 0x465,
+	0x476, 0x487, 0x499, 0x4AB, 0x4BD, 0x4CF, 0x4E1, 0x4F3,
+	0x506, 0x518, 0x52B, 0x53E, 0x551, 0x565, 0x578, 0x58C,
+	0x5A0, 0x5B3, 0x5C8, 0x5DC, 0x5F0, 0x605, 0x61A, 0x62E,
+	0x643, 0x659, 0x66E, 0x684, 0x699, 0x6AF, 0x6C5, 0x6DB,
+	0x6F2, 0x708, 0x71F, 0x736, 0x74D, 0x764, 0x77C, 0x793,
+	0x7AB, 0x7C3, 0x7DB, 0x7F3, 0x80B, 0x824, 0x83D, 0x855,
+	0x86F, 0x888, 0x8A1, 0x8BB, 0x8D4, 0x8EE, 0x908, 0x923,
+	0x93D, 0x958, 0x973, 0x98E, 0x9A9, 0x9C4, 0x9DF, 0x9FB,
+	0xA17, 0xA33, 0xA4F, 0xA6C, 0xA88, 0xAA5, 0xAC2, 0xADF,
+	0xAFC, 0xB19, 0xB37, 0xB55, 0xB73, 0xB91, 0xBAF, 0xBCE,
+	0xBEC, 0xC0B, 0xC2A, 0xC4A, 0xC69, 0xC89, 0xCA8, 0xCC8,
+	0xCE8, 0xD09, 0xD29, 0xD4A, 0xD6B, 0xD8C, 0xDAD, 0xDCF,
+	0xDF0, 0xE12, 0xE34, 0xE56, 0xE79, 0xE9B, 0xEBE, 0xEE1,
+	0xF04, 0xF27, 0xF4B, 0xF6E, 0xF92, 0xFB6, 0xFDB, 0xFFF,
+};
+
+void mdp4_vg_igc_lut_setup(int vp_num)
+{
+	unsigned char *base;
+	int i, voff, off;
+	uint32 data, val;
+
+	voff = MDP4_VIDEO_OFF * vp_num;
+	base = MDP_BASE + MDP4_VIDEO_BASE + voff + 0x5000;
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	off = 0;
+	for (i = 0; i < 256; i++) {
+		val = igc_video_lut[i];
+		data = (val << 16 | val);	/* color 0 and 1 */
+		outpdw(base + off, data);
+		outpdw(base + off + 0x800, val);	/* color 2 */
+		off += 4;
+	}
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+uint32 igc_rgb_lut[] = {   /* linear */
+	0x0, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70,
+	0x80, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1,
+	0x101, 0x111, 0x121, 0x131, 0x141, 0x151, 0x161, 0x171,
+	0x181, 0x191, 0x1A2, 0x1B2, 0x1C2, 0x1D2, 0x1E2, 0x1F2,
+	0x202, 0x212, 0x222, 0x232, 0x242, 0x252, 0x262, 0x272,
+	0x282, 0x292, 0x2A2, 0x2B3, 0x2C3, 0x2D3, 0x2E3, 0x2F3,
+	0x303, 0x313, 0x323, 0x333, 0x343, 0x353, 0x363, 0x373,
+	0x383, 0x393, 0x3A3, 0x3B3, 0x3C4, 0x3D4, 0x3E4, 0x3F4,
+	0x404, 0x414, 0x424, 0x434, 0x444, 0x454, 0x464, 0x474,
+	0x484, 0x494, 0x4A4, 0x4B4, 0x4C4, 0x4D5, 0x4E5, 0x4F5,
+	0x505, 0x515, 0x525, 0x535, 0x545, 0x555, 0x565, 0x575,
+	0x585, 0x595, 0x5A5, 0x5B5, 0x5C5, 0x5D5, 0x5E6, 0x5F6,
+	0x606, 0x616, 0x626, 0x636, 0x646, 0x656, 0x666, 0x676,
+	0x686, 0x696, 0x6A6, 0x6B6, 0x6C6, 0x6D6, 0x6E6, 0x6F7,
+	0x707, 0x717, 0x727, 0x737, 0x747, 0x757, 0x767, 0x777,
+	0x787, 0x797, 0x7A7, 0x7B7, 0x7C7, 0x7D7, 0x7E7, 0x7F7,
+	0x808, 0x818, 0x828, 0x838, 0x848, 0x858, 0x868, 0x878,
+	0x888, 0x898, 0x8A8, 0x8B8, 0x8C8, 0x8D8, 0x8E8, 0x8F8,
+	0x908, 0x919, 0x929, 0x939, 0x949, 0x959, 0x969, 0x979,
+	0x989, 0x999, 0x9A9, 0x9B9, 0x9C9, 0x9D9, 0x9E9, 0x9F9,
+	0xA09, 0xA19, 0xA2A, 0xA3A, 0xA4A, 0xA5A, 0xA6A, 0xA7A,
+	0xA8A, 0xA9A, 0xAAA, 0xABA, 0xACA, 0xADA, 0xAEA, 0xAFA,
+	0xB0A, 0xB1A, 0xB2A, 0xB3B, 0xB4B, 0xB5B, 0xB6B, 0xB7B,
+	0xB8B, 0xB9B, 0xBAB, 0xBBB, 0xBCB, 0xBDB, 0xBEB, 0xBFB,
+	0xC0B, 0xC1B, 0xC2B, 0xC3B, 0xC4C, 0xC5C, 0xC6C, 0xC7C,
+	0xC8C, 0xC9C, 0xCAC, 0xCBC, 0xCCC, 0xCDC, 0xCEC, 0xCFC,
+	0xD0C, 0xD1C, 0xD2C, 0xD3C, 0xD4C, 0xD5D, 0xD6D, 0xD7D,
+	0xD8D, 0xD9D, 0xDAD, 0xDBD, 0xDCD, 0xDDD, 0xDED, 0xDFD,
+	0xE0D, 0xE1D, 0xE2D, 0xE3D, 0xE4D, 0xE5D, 0xE6E, 0xE7E,
+	0xE8E, 0xE9E, 0xEAE, 0xEBE, 0xECE, 0xEDE, 0xEEE, 0xEFE,
+	0xF0E, 0xF1E, 0xF2E, 0xF3E, 0xF4E, 0xF5E, 0xF6E, 0xF7F,
+	0xF8F, 0xF9F, 0xFAF, 0xFBF, 0xFCF, 0xFDF, 0xFEF, 0xFFF,
+};
+
+void mdp4_rgb_igc_lut_setup(int num)
+{
+	unsigned char *base;
+	int i, voff, off;
+	uint32 data, val;
+
+	voff = MDP4_RGB_OFF * num;
+	base = MDP_BASE + MDP4_RGB_BASE + voff + 0x5000;
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	off = 0;
+	for (i = 0; i < 256; i++) {
+		val = igc_rgb_lut[i];
+		data = (val << 16 | val);	/* color 0 and 1 */
+		outpdw(base + off, data);
+		outpdw(base + off + 0x800, val);	/* color 2 */
+		off += 4;
+	}
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+uint32 mdp4_rgb_igc_lut_cvt(uint32 ndx)
+{
+	return igc_rgb_lut[ndx & 0x0ff];
+}
+
+uint32_t mdp4_ss_table_value(int8_t value, int8_t index)
+{
+	uint32_t out = 0x0;
+	int8_t level = -1;
+	uint32_t mask = 0xffffffff;
+
+	if (value < 0) {
+		if (value == -128)
+			value = 127;
+		else
+			value = -value;
+		out = 0x11111111;
+	} else {
+		out = 0x88888888;
+		mask = 0x0fffffff;
+	}
+
+	if (value == 0)
+		level = 0;
+	else {
+		while (value > 0 && level < 7) {
+			level++;
+			value -= 16;
+		}
+	}
+
+	if (level == 0) {
+		if (index == 0)
+			out = 0x0;
+		else
+			out = 0x20000000;
+	} else {
+		out += (0x11111111 * level);
+		if (index == 1)
+			out &= mask;
+	}
+
+	return out;
+}
+
+static uint32_t mdp4_csc_block2base(uint32_t block)
+{
+	uint32_t base = 0x0;
+	switch (block) {
+	case MDP_BLOCK_OVERLAY_1:
+		base = 0x1A000;
+		break;
+	case MDP_BLOCK_OVERLAY_2:
+		base = (mdp_rev >= MDP_REV_44) ? 0x8A000 : 0x0;
+		break;
+	case MDP_BLOCK_VG_1:
+		base = 0x24000;
+		break;
+	case MDP_BLOCK_VG_2:
+		base = 0x34000;
+		break;
+	case MDP_BLOCK_DMA_P:
+		base = 0x93000;
+		break;
+	case MDP_BLOCK_DMA_S:
+		base = (mdp_rev >= MDP_REV_42) ? 0xA3000 : 0x0;
+	default:
+		break;
+	}
+	return base;
+}
+
+int mdp4_csc_enable(struct mdp_csc_cfg_data *config)
+{
+	uint32_t output, base, temp, mask;
+
+	switch (config->block) {
+	case MDP_BLOCK_DMA_P:
+		base = 0x90070;
+		output = (config->csc_data.flags << 3) & (0x08);
+		temp = (config->csc_data.flags << 10) & (0x1800);
+		output |= temp;
+		mask = 0x08 | 0x1800;
+		break;
+	case MDP_BLOCK_DMA_S:
+		base = 0xA0028;
+		output = (config->csc_data.flags << 3) & (0x08);
+		temp = (config->csc_data.flags << 10) & (0x1800);
+		output |= temp;
+		mask = 0x08 | 0x1800;
+		break;
+	case MDP_BLOCK_VG_1:
+		base = 0x20058;
+		output = (config->csc_data.flags << 11) & (0x800);
+		temp = (config->csc_data.flags << 8) & (0x600);
+		output |= temp;
+		mask = 0x800 | 0x600;
+		break;
+	case MDP_BLOCK_VG_2:
+		base = 0x30058;
+		output = (config->csc_data.flags << 11) & (0x800);
+		temp = (config->csc_data.flags << 8) & (0x600);
+		output |= temp;
+		mask = 0x800 | 0x600;
+		break;
+	case MDP_BLOCK_OVERLAY_1:
+		base = 0x18200;
+		output = config->csc_data.flags;
+		mask = 0x07;
+		break;
+	case MDP_BLOCK_OVERLAY_2:
+		base = 0x88200;
+		output = config->csc_data.flags;
+		mask = 0x07;
+		break;
+	default:
+		pr_err("%s - CSC block does not exist on MDP_BLOCK = %d\n",
+						__func__, config->block);
+		return -EINVAL;
+	}
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	temp = inpdw(MDP_BASE + base) & ~mask;
+	output |= temp;
+	outpdw(MDP_BASE + base, output);
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+	return 0;
+}
+
+#define CSC_MV_OFF	0x400
+#define CSC_BV_OFF	0x500
+#define CSC_LV_OFF	0x600
+#define CSC_POST_OFF	0x80
+
+void mdp4_csc_write(struct mdp_csc_cfg *data, uint32_t base)
+{
+	int i;
+	uint32_t *off;
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	off = (uint32_t *) ((uint32_t) base + CSC_MV_OFF);
+	for (i = 0; i < 9; i++) {
+		outpdw(off, data->csc_mv[i]);
+		off++;
+	}
+
+	off = (uint32_t *) ((uint32_t) base + CSC_BV_OFF);
+	for (i = 0; i < 3; i++) {
+		outpdw(off, data->csc_pre_bv[i]);
+		outpdw((uint32_t *)((uint32_t)off + CSC_POST_OFF),
+					data->csc_post_bv[i]);
+		off++;
+	}
+
+	off = (uint32_t *) ((uint32_t) base + CSC_LV_OFF);
+	for (i = 0; i < 6; i++) {
+		outpdw(off, data->csc_pre_lv[i]);
+		outpdw((uint32_t *)((uint32_t)off + CSC_POST_OFF),
+					data->csc_post_lv[i]);
+		off++;
+	}
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+int mdp4_csc_config(struct mdp_csc_cfg_data *config)
+{
+	int ret = 0;
+	uint32_t base;
+
+	base = mdp4_csc_block2base(config->block);
+	if (!base) {
+		pr_warn("%s: Block type %d isn't supported by CSC.\n",
+				__func__, config->block);
+		return -EINVAL;
+	}
+
+	mdp4_csc_write(&config->csc_data, (uint32_t) (MDP_BASE + base));
+
+	ret = mdp4_csc_enable(config);
+
+	return ret;
+}
+
+void mdp4_init_writeback_buf(struct msm_fb_data_type *mfd, u32 mix_num)
+{
+	struct mdp_buf_type *buf;
+
+	if (mix_num == MDP4_MIXER0)
+		buf = mfd->ov0_wb_buf;
+	else
+		buf = mfd->ov1_wb_buf;
+
+	buf->ihdl = NULL;
+	buf->phys_addr = 0;
+}
+
+u32 mdp4_allocate_writeback_buf(struct msm_fb_data_type *mfd, u32 mix_num)
+{
+	struct mdp_buf_type *buf;
+	ion_phys_addr_t	addr;
+	size_t buffer_size;
+	unsigned long len;
+
+	if (mix_num == MDP4_MIXER0)
+		buf = mfd->ov0_wb_buf;
+	else
+		buf = mfd->ov1_wb_buf;
+
+	if (buf->phys_addr || !IS_ERR_OR_NULL(buf->ihdl))
+		return 0;
+
+	if (!buf->size) {
+		pr_err("%s:%d In valid size\n", __func__, __LINE__);
+		return -EINVAL;
+	}
+
+	buffer_size = roundup(mfd->panel_info.xres * \
+		mfd->panel_info.yres * 3 * 2, SZ_4K);
+
+	if (!IS_ERR_OR_NULL(mfd->iclient)) {
+		pr_info("%s:%d ion based allocation mfd->mem_hid 0x%x\n",
+			__func__, __LINE__, mfd->mem_hid);
+		buf->ihdl = ion_alloc(mfd->iclient, buffer_size, SZ_4K,
+			mfd->mem_hid);
+		if (!IS_ERR_OR_NULL(buf->ihdl)) {
+			if (ion_map_iommu(mfd->iclient, buf->ihdl,
+				DISPLAY_DOMAIN, GEN_POOL, SZ_4K, 0, &addr,
+				&len, 0, 0)) {
+				pr_err("ion_map_iommu() failed\n");
+				return -ENOMEM;
+			}
+		} else {
+			pr_err("%s:%d: ion_alloc failed\n", __func__,
+				__LINE__);
+			return -ENOMEM;
+		}
+	} else {
+		addr = allocate_contiguous_memory_nomap(buffer_size,
+			mfd->mem_hid, 4);
+	}
+	if (addr) {
+		pr_info("allocating %d bytes at %x for mdp writeback\n",
+			buffer_size, (u32) addr);
+		buf->phys_addr = addr;
+		return 0;
+	} else {
+		pr_err("%s cannot allocate memory for mdp writeback!\n",
+			 __func__);
+		return -ENOMEM;
+	}
+}
+
+void mdp4_free_writeback_buf(struct msm_fb_data_type *mfd, u32 mix_num)
+{
+	struct mdp_buf_type *buf;
+
+	if (mix_num == MDP4_MIXER0)
+		buf = mfd->ov0_wb_buf;
+	else
+		buf = mfd->ov1_wb_buf;
+
+	if (!IS_ERR_OR_NULL(mfd->iclient)) {
+		if (!IS_ERR_OR_NULL(buf->ihdl)) {
+			ion_unmap_iommu(mfd->iclient, buf->ihdl,
+				DISPLAY_DOMAIN, GEN_POOL);
+			ion_free(mfd->iclient, buf->ihdl);
+			pr_debug("%s:%d free writeback imem\n", __func__,
+				__LINE__);
+			buf->ihdl = NULL;
+		}
+	} else {
+		if (buf->phys_addr) {
+			free_contiguous_memory_by_paddr(buf->phys_addr);
+			pr_debug("%s:%d free writeback pmem\n", __func__,
+				__LINE__);
+		}
+	}
+	buf->phys_addr = 0;
+}
+
+static int mdp4_update_pcc_regs(uint32_t offset,
+				struct mdp_pcc_cfg_data *cfg_ptr)
+{
+	int ret = -1;
+
+	if (offset && cfg_ptr) {
+
+		outpdw(offset, cfg_ptr->r.c);
+		outpdw(offset + 0x30, cfg_ptr->g.c);
+		outpdw(offset + 0x60, cfg_ptr->b.c);
+		offset += 4;
+
+		outpdw(offset, cfg_ptr->r.r);
+		outpdw(offset + 0x30, cfg_ptr->g.r);
+		outpdw(offset + 0x60, cfg_ptr->b.r);
+		offset += 4;
+
+		outpdw(offset, cfg_ptr->r.g);
+		outpdw(offset + 0x30, cfg_ptr->g.g);
+		outpdw(offset + 0x60, cfg_ptr->b.g);
+		offset += 4;
+
+		outpdw(offset, cfg_ptr->r.b);
+		outpdw(offset + 0x30, cfg_ptr->g.b);
+		outpdw(offset + 0x60, cfg_ptr->b.b);
+		offset += 4;
+
+		outpdw(offset, cfg_ptr->r.rr);
+		outpdw(offset + 0x30, cfg_ptr->g.rr);
+		outpdw(offset + 0x60, cfg_ptr->b.rr);
+		offset += 4;
+
+		outpdw(offset, cfg_ptr->r.gg);
+		outpdw(offset + 0x30, cfg_ptr->g.gg);
+		outpdw(offset + 0x60, cfg_ptr->b.gg);
+		offset += 4;
+
+		outpdw(offset, cfg_ptr->r.bb);
+		outpdw(offset + 0x30, cfg_ptr->g.bb);
+		outpdw(offset + 0x60, cfg_ptr->b.bb);
+		offset += 4;
+
+		outpdw(offset, cfg_ptr->r.rg);
+		outpdw(offset + 0x30, cfg_ptr->g.rg);
+		outpdw(offset + 0x60, cfg_ptr->b.rg);
+		offset += 4;
+
+		outpdw(offset, cfg_ptr->r.gb);
+		outpdw(offset + 0x30, cfg_ptr->g.gb);
+		outpdw(offset + 0x60, cfg_ptr->b.gb);
+		offset += 4;
+
+		outpdw(offset, cfg_ptr->r.rb);
+		outpdw(offset + 0x30, cfg_ptr->g.rb);
+		outpdw(offset + 0x60, cfg_ptr->b.rb);
+		offset += 4;
+
+		outpdw(offset, cfg_ptr->r.rgb_0);
+		outpdw(offset + 0x30, cfg_ptr->g.rgb_0);
+		outpdw(offset + 0x60, cfg_ptr->b.rgb_0);
+		offset += 4;
+
+		outpdw(offset, cfg_ptr->r.rgb_1);
+		outpdw(offset + 0x30, cfg_ptr->g.rgb_1);
+		outpdw(offset + 0x60, cfg_ptr->b.rgb_1);
+
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static int mdp4_read_pcc_regs(uint32_t offset,
+				struct mdp_pcc_cfg_data *cfg_ptr)
+{
+	int ret = -1;
+
+	if (offset && cfg_ptr) {
+		cfg_ptr->r.c = inpdw(offset);
+		cfg_ptr->g.c = inpdw(offset + 0x30);
+		cfg_ptr->b.c = inpdw(offset + 0x60);
+		offset += 4;
+
+		cfg_ptr->r.r = inpdw(offset);
+		cfg_ptr->g.r = inpdw(offset + 0x30);
+		cfg_ptr->b.r = inpdw(offset + 0x60);
+		offset += 4;
+
+		cfg_ptr->r.g = inpdw(offset);
+		cfg_ptr->g.g = inpdw(offset + 0x30);
+		cfg_ptr->b.g = inpdw(offset + 0x60);
+		offset += 4;
+
+		cfg_ptr->r.b = inpdw(offset);
+		cfg_ptr->g.b = inpdw(offset + 0x30);
+		cfg_ptr->b.b = inpdw(offset + 0x60);
+		offset += 4;
+
+		cfg_ptr->r.rr = inpdw(offset);
+		cfg_ptr->g.rr = inpdw(offset + 0x30);
+		cfg_ptr->b.rr = inpdw(offset + 0x60);
+		offset += 4;
+
+		cfg_ptr->r.gg = inpdw(offset);
+		cfg_ptr->g.gg = inpdw(offset + 0x30);
+		cfg_ptr->b.gg = inpdw(offset + 0x60);
+		offset += 4;
+
+		cfg_ptr->r.bb = inpdw(offset);
+		cfg_ptr->g.bb = inpdw(offset + 0x30);
+		cfg_ptr->b.bb = inpdw(offset + 0x60);
+		offset += 4;
+
+		cfg_ptr->r.rg = inpdw(offset);
+		cfg_ptr->g.rg = inpdw(offset + 0x30);
+		cfg_ptr->b.rg = inpdw(offset + 0x60);
+		offset += 4;
+
+		cfg_ptr->r.gb = inpdw(offset);
+		cfg_ptr->g.gb = inpdw(offset + 0x30);
+		cfg_ptr->b.gb = inpdw(offset + 0x60);
+		offset += 4;
+
+		cfg_ptr->r.rb = inpdw(offset);
+		cfg_ptr->g.rb = inpdw(offset + 0x30);
+		cfg_ptr->b.rb = inpdw(offset + 0x60);
+		offset += 4;
+
+		cfg_ptr->r.rgb_0 = inpdw(offset);
+		cfg_ptr->g.rgb_0 = inpdw(offset + 0x30);
+		cfg_ptr->b.rgb_0 = inpdw(offset + 0x60);
+		offset += 4;
+
+		cfg_ptr->r.rgb_1 = inpdw(offset);
+		cfg_ptr->g.rgb_1 = inpdw(offset + 0x30);
+		cfg_ptr->b.rgb_1 = inpdw(offset + 0x60);
+
+		ret = 0;
+	}
+
+	return ret;
+}
+
+
+#define MDP_PCC_OFFSET 0xA000
+#define MDP_DMA_GC_OFFSET 0x8800
+#define MDP_LM_GC_OFFSET 0x4800
+
+#define MDP_DMA_P_OP_MODE_OFFSET 0x70
+#define MDP_DMA_S_OP_MODE_OFFSET 0x28
+#define MDP_LM_OP_MODE_OFFSET 0x14
+
+#define DMA_PCC_R2_OFFSET 0x100
+
+#define MDP_GC_COLOR_OFFSET	0x100
+#define MDP_GC_PARMS_OFFSET	0x80
+
+#define MDP_AR_GC_MAX_STAGES	16
+
+static uint32_t mdp_pp_block2pcc(uint32_t block)
+{
+	uint32_t valid = 0;
+
+	switch (block) {
+	case MDP_BLOCK_DMA_P:
+	case MDP_BLOCK_DMA_S:
+		valid = (mdp_rev >= MDP_REV_42) ? 1 : 0;
+		break;
+
+	default:
+		break;
+	}
+
+	return valid;
+}
+
+int mdp4_pcc_cfg(struct mdp_pcc_cfg_data *cfg_ptr)
+{
+	int ret = -1;
+	uint32_t pcc_offset = 0, mdp_cfg_offset = 0;
+	uint32_t mdp_dma_op_mode = 0;
+	uint32_t blockbase;
+
+	if (!mdp_pp_block2pcc(cfg_ptr->block))
+		return ret;
+
+	blockbase = mdp_block2base(cfg_ptr->block);
+	if (!blockbase)
+		return ret;
+
+	blockbase += (uint32_t) MDP_BASE;
+
+	switch (cfg_ptr->block) {
+	case MDP_BLOCK_DMA_P:
+	case MDP_BLOCK_DMA_S:
+		pcc_offset = blockbase + MDP_PCC_OFFSET;
+		mdp_cfg_offset = blockbase;
+		mdp_dma_op_mode = blockbase +
+			(MDP_BLOCK_DMA_P == cfg_ptr->block ?
+			 MDP_DMA_P_OP_MODE_OFFSET
+			 : MDP_DMA_S_OP_MODE_OFFSET);
+		break;
+
+	default:
+		break;
+	}
+
+	if (0x8 & cfg_ptr->ops)
+		pcc_offset += DMA_PCC_R2_OFFSET;
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	switch ((0x6 & cfg_ptr->ops)>>1) {
+	case 0x1:
+		ret = mdp4_read_pcc_regs(pcc_offset, cfg_ptr);
+		break;
+
+	case 0x2:
+		ret = mdp4_update_pcc_regs(pcc_offset, cfg_ptr);
+		break;
+
+	default:
+		break;
+	}
+
+	if (0x8 & cfg_ptr->ops)
+		outpdw(mdp_dma_op_mode,
+			((inpdw(mdp_dma_op_mode) & ~(0x1<<10)) |
+						((0x8 & cfg_ptr->ops)<<10)));
+
+	outpdw(mdp_cfg_offset,
+			((inpdw(mdp_cfg_offset) & ~(0x1<<29)) |
+						((cfg_ptr->ops & 0x1)<<29)));
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	return ret;
+}
+
+static uint32_t mdp_pp_block2argc(uint32_t block)
+{
+	uint32_t valid = 0;
+
+	switch (block) {
+	case MDP_BLOCK_DMA_P:
+	case MDP_BLOCK_DMA_S:
+	case MDP_BLOCK_OVERLAY_0:
+	case MDP_BLOCK_OVERLAY_1:
+		valid = (mdp_rev >= MDP_REV_42) ? 1 : 0;
+		break;
+
+	case MDP_BLOCK_OVERLAY_2:
+		valid = (mdp_rev >= MDP_REV_44) ? 1 : 0;
+		break;
+
+	default:
+		break;
+	}
+
+	return valid;
+}
+
+static int update_ar_gc_lut(uint32_t *offset, struct mdp_pgc_lut_data *lut_data)
+{
+	int count = 0;
+
+	uint32_t *c0_offset = offset;
+	uint32_t *c0_params_offset = (uint32_t *)((uint32_t)c0_offset
+							+ MDP_GC_PARMS_OFFSET);
+
+	uint32_t *c1_offset = (uint32_t *)((uint32_t)offset
+							+ MDP_GC_COLOR_OFFSET);
+
+	uint32_t *c1_params_offset = (uint32_t *)((uint32_t)c1_offset
+							+ MDP_GC_PARMS_OFFSET);
+
+	uint32_t *c2_offset = (uint32_t *)((uint32_t)offset
+						+ 2*MDP_GC_COLOR_OFFSET);
+
+	uint32_t *c2_params_offset = (uint32_t *)((uint32_t)c2_offset
+						+MDP_GC_PARMS_OFFSET);
+
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	for (count = 0; count < MDP_AR_GC_MAX_STAGES; count++) {
+		if (count < lut_data->num_r_stages) {
+			outpdw(c0_offset+count,
+				((0xfff & lut_data->r_data[count].x_start)
+					| 0x10000));
+
+			outpdw(c0_params_offset+count,
+				((0x7fff & lut_data->r_data[count].slope)
+					| ((0xffff
+					& lut_data->r_data[count].offset)
+						<< 16)));
+		} else
+			outpdw(c0_offset+count, 0);
+
+		if (count < lut_data->num_b_stages) {
+			outpdw(c1_offset+count,
+				((0xfff & lut_data->b_data[count].x_start)
+					| 0x10000));
+
+			outpdw(c1_params_offset+count,
+				((0x7fff & lut_data->b_data[count].slope)
+					| ((0xffff
+					& lut_data->b_data[count].offset)
+						<< 16)));
+		} else
+			outpdw(c1_offset+count, 0);
+
+		if (count < lut_data->num_g_stages) {
+			outpdw(c2_offset+count,
+				((0xfff & lut_data->g_data[count].x_start)
+					| 0x10000));
+
+			outpdw(c2_params_offset+count,
+				((0x7fff & lut_data->g_data[count].slope)
+				| ((0xffff
+				& lut_data->g_data[count].offset)
+					<< 16)));
+		} else
+			outpdw(c2_offset+count, 0);
+	}
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	return 0;
+}
+
+static int mdp4_argc_process_write_req(uint32_t *offset,
+		struct mdp_pgc_lut_data *pgc_ptr)
+{
+	int ret = -1;
+	struct mdp_ar_gc_lut_data r[MDP_AR_GC_MAX_STAGES];
+	struct mdp_ar_gc_lut_data g[MDP_AR_GC_MAX_STAGES];
+	struct mdp_ar_gc_lut_data b[MDP_AR_GC_MAX_STAGES];
+
+	ret = copy_from_user(&r[0], pgc_ptr->r_data,
+		pgc_ptr->num_r_stages * sizeof(struct mdp_ar_gc_lut_data));
+
+	if (!ret) {
+		ret = copy_from_user(&g[0],
+				pgc_ptr->g_data,
+				pgc_ptr->num_g_stages
+				* sizeof(struct mdp_ar_gc_lut_data));
+		if (!ret)
+			ret = copy_from_user(&b[0],
+					pgc_ptr->b_data,
+					pgc_ptr->num_b_stages
+					* sizeof(struct mdp_ar_gc_lut_data));
+	}
+
+	if (ret)
+		return ret;
+
+	pgc_ptr->r_data = &r[0];
+	pgc_ptr->g_data = &g[0];
+	pgc_ptr->b_data = &b[0];
+
+	ret = update_ar_gc_lut(offset, pgc_ptr);
+	return ret;
+}
+
+int mdp4_argc_cfg(struct mdp_pgc_lut_data *pgc_ptr)
+{
+	int ret = -1;
+	uint32_t *offset = 0, *pgc_enable_offset = 0, lshift_bits = 0;
+	uint32_t blockbase;
+
+	if (!mdp_pp_block2argc(pgc_ptr->block))
+		return ret;
+
+	blockbase = mdp_block2base(pgc_ptr->block);
+	if (!blockbase)
+		return ret;
+
+	blockbase += (uint32_t) MDP_BASE;
+	ret = 0;
+
+	switch (pgc_ptr->block) {
+	case MDP_BLOCK_DMA_P:
+	case MDP_BLOCK_DMA_S:
+		offset = (uint32_t *)(blockbase + MDP_DMA_GC_OFFSET);
+		pgc_enable_offset = (uint32_t *) blockbase;
+		lshift_bits = 28;
+		break;
+
+	case MDP_BLOCK_OVERLAY_0:
+	case MDP_BLOCK_OVERLAY_1:
+	case MDP_BLOCK_OVERLAY_2:
+		offset = (uint32_t *)(blockbase + MDP_LM_GC_OFFSET);
+		pgc_enable_offset = (uint32_t *)(blockbase
+				+ MDP_LM_OP_MODE_OFFSET);
+		lshift_bits = 2;
+		break;
+
+	default:
+		ret = -1;
+		break;
+	}
+
+	if (!ret) {
+
+		switch ((0x6 & pgc_ptr->flags)>>1) {
+		case 0x1:
+			ret = -ENOTTY;
+			break;
+
+		case 0x2:
+			ret = mdp4_argc_process_write_req(offset, pgc_ptr);
+			break;
+
+		default:
+			break;
+		}
+
+		if (!ret) {
+			mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+			outpdw(pgc_enable_offset, (inpdw(pgc_enable_offset) &
+							~(0x1<<lshift_bits)) |
+				((0x1 & pgc_ptr->flags) << lshift_bits));
+			mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF,
+									FALSE);
+		}
+	}
+
+	return ret;
+}
+
+static uint32_t mdp4_pp_block2igc(uint32_t block)
+{
+	uint32_t valid = 0;
+	switch (block) {
+	case MDP_BLOCK_VG_1:
+		valid = 0x1;
+		break;
+	case MDP_BLOCK_VG_2:
+		valid = 0x1;
+		break;
+	case MDP_BLOCK_RGB_1:
+		valid = 0x1;
+		break;
+	case MDP_BLOCK_RGB_2:
+		valid = 0x1;
+		break;
+	case MDP_BLOCK_DMA_P:
+		valid = (mdp_rev >= MDP_REV_40) ? 1 : 0;
+		break;
+	case MDP_BLOCK_DMA_S:
+		valid = (mdp_rev >= MDP_REV_40) ? 1 : 0;
+		break;
+	default:
+		break;
+	}
+	return valid;
+}
+
+static int mdp4_igc_lut_write(struct mdp_igc_lut_data *cfg, uint32_t en_off,
+		uint32_t lut_off)
+{
+	int i;
+	uint32_t base, *off_low, *off_high;
+	uint32_t low[cfg->len];
+	uint32_t high[cfg->len];
+
+	base = mdp_block2base(cfg->block);
+
+	if (cfg->len != 256)
+		return -EINVAL;
+
+	off_low = (uint32_t *)(MDP_BASE + base + lut_off);
+	off_high = (uint32_t *)(MDP_BASE + base + lut_off + 0x800);
+	if (copy_from_user(&low, cfg->c0_c1_data, cfg->len * sizeof(uint32_t)))
+		return -EFAULT;
+	if (copy_from_user(&high, cfg->c2_data, cfg->len * sizeof(uint32_t)))
+		return -EFAULT;
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	for (i = 0; i < cfg->len; i++) {
+		MDP_OUTP(off_low++, low[i]);
+		/*low address write should occur before high address write*/
+		wmb();
+		MDP_OUTP(off_high++, high[i]);
+	}
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+	return 0;
+}
+
+static int mdp4_igc_lut_ctrl(struct mdp_igc_lut_data *cfg)
+{
+	uint32_t mask, out;
+	uint32_t base = mdp_block2base(cfg->block);
+	int8_t shift = 0;
+
+	switch (cfg->block) {
+	case MDP_BLOCK_DMA_P:
+	case MDP_BLOCK_DMA_S:
+		base = base;
+		shift = 30;
+		break;
+	case MDP_BLOCK_VG_1:
+	case MDP_BLOCK_VG_2:
+	case MDP_BLOCK_RGB_1:
+	case MDP_BLOCK_RGB_2:
+		base += 0x58;
+		shift = 16;
+		break;
+	default:
+		return -EINVAL;
+
+	}
+	out = 1<<shift;
+	mask = ~out;
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	out = inpdw(MDP_BASE + base) & mask;
+	MDP_OUTP(MDP_BASE + base, out | ((cfg->ops & 0x1)<<shift));
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	return 0;
+}
+
+static int mdp4_igc_lut_write_cfg(struct mdp_igc_lut_data *cfg)
+{
+	int ret = 0;
+
+	switch (cfg->block) {
+	case MDP_BLOCK_DMA_P:
+	case MDP_BLOCK_DMA_S:
+		ret = mdp4_igc_lut_write(cfg, 0x00, 0x9000);
+		break;
+	case MDP_BLOCK_VG_1:
+	case MDP_BLOCK_VG_2:
+	case MDP_BLOCK_RGB_1:
+	case MDP_BLOCK_RGB_2:
+		ret = mdp4_igc_lut_write(cfg, 0x58, 0x5000);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+int mdp4_igc_lut_config(struct mdp_igc_lut_data *cfg)
+{
+	int ret = 0;
+
+	if (!mdp4_pp_block2igc(cfg->block)) {
+		ret = -ENOTTY;
+		goto error;
+	}
+
+	switch ((cfg->ops & 0x6) >> 1) {
+	case 0x1:
+		pr_info("%s: IGC LUT read not supported\n", __func__);
+		break;
+	case 0x2:
+		ret = mdp4_igc_lut_write_cfg(cfg);
+		if (ret)
+			goto error;
+		break;
+	default:
+		break;
+	}
+
+	ret = mdp4_igc_lut_ctrl(cfg);
+
+error:
+	return ret;
+}
+
+#define QSEED_TABLE_1_COUNT	2
+#define QSEED_TABLE_2_COUNT	1024
+
+static uint32_t mdp4_pp_block2qseed(uint32_t block)
+{
+	uint32_t valid = 0;
+	switch (block) {
+	case MDP_BLOCK_VG_1:
+	case MDP_BLOCK_VG_2:
+		valid = 0x1;
+		break;
+	default:
+		break;
+	}
+	return valid;
+}
+
+static int mdp4_qseed_write_cfg(struct mdp_qseed_cfg_data *cfg)
+{
+	int i, ret = 0;
+	uint32_t base = (uint32_t) (MDP_BASE + mdp_block2base(cfg->block));
+	uint32_t *values;
+
+	if ((cfg->table_num != 1) && (cfg->table_num != 2)) {
+		ret = -ENOTTY;
+		goto error;
+	}
+
+	if (((cfg->table_num == 1) && (cfg->len != QSEED_TABLE_1_COUNT)) ||
+		((cfg->table_num == 2) && (cfg->len != QSEED_TABLE_2_COUNT))) {
+		ret = -EINVAL;
+		goto error;
+	}
+
+	values = kmalloc(cfg->len * sizeof(uint32_t), GFP_KERNEL);
+	if (!values) {
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	ret = copy_from_user(values, cfg->data, sizeof(uint32_t) * cfg->len);
+
+	base += (cfg->table_num == 1) ? MDP4_QSEED_TABLE1_OFF :
+						MDP4_QSEED_TABLE2_OFF;
+	for (i = 0; i < cfg->len; i++) {
+		MDP_OUTP(base , values[i]);
+		base += sizeof(uint32_t);
+	}
+
+	kfree(values);
+error:
+	return ret;
+}
+
+int mdp4_qseed_cfg(struct mdp_qseed_cfg_data *cfg)
+{
+	int ret = 0;
+
+	if (!mdp4_pp_block2qseed(cfg->block)) {
+		ret = -ENOTTY;
+		goto error;
+	}
+
+	if (cfg->table_num != 1) {
+		ret = -ENOTTY;
+		pr_info("%s: Only QSEED table1 supported.\n", __func__);
+		goto error;
+	}
+
+	switch ((cfg->ops & 0x6) >> 1) {
+	case 0x1:
+		pr_info("%s: QSEED read not supported\n", __func__);
+		ret = -ENOTTY;
+		break;
+	case 0x2:
+		ret = mdp4_qseed_write_cfg(cfg);
+		if (ret)
+			goto error;
+		break;
+	default:
+		break;
+	}
+
+error:
+	return ret;
+}
diff --git a/drivers/video/msm/mdp4_wfd_writeback.c b/drivers/video/msm/mdp4_wfd_writeback.c
new file mode 100644
index 0000000..a8fdcc0
--- /dev/null
+++ b/drivers/video/msm/mdp4_wfd_writeback.c
@@ -0,0 +1,102 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/ioctl.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/version.h>
+
+#include "mdp4_wfd_writeback_util.h"
+#include "msm_fb.h"
+
+static int writeback_on(struct platform_device *pdev)
+{
+	return 0;
+}
+static int writeback_off(struct platform_device *pdev)
+{
+	return 0;
+}
+static int writeback_probe(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+	struct platform_device *mdp_dev = NULL;
+	struct msm_fb_panel_data *pdata = NULL;
+	int rc = 0;
+
+	WRITEBACK_MSG_ERR("Inside writeback_probe\n");
+	mfd = platform_get_drvdata(pdev);
+	if (!mfd)
+		return -ENODEV;
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	mdp_dev = platform_device_alloc("mdp", pdev->id);
+	if (!mdp_dev)
+		return -ENOMEM;
+	/*
+	 * link to the latest pdev
+	 */
+	mfd->pdev = mdp_dev;
+	mfd->dest = DISPLAY_LCD;
+
+	if (platform_device_add_data
+			(mdp_dev, pdev->dev.platform_data,
+			 sizeof(struct msm_fb_panel_data))) {
+		pr_err("writeback_probe: "
+			"platform_device_add_data failed!\n");
+		platform_device_put(mdp_dev);
+		return -ENOMEM;
+	}
+	pdata = (struct msm_fb_panel_data *)mdp_dev->dev.platform_data;
+	pdata->on = writeback_on;
+	pdata->off = writeback_off;
+	pdata->next = pdev;
+
+	/*
+	 * get/set panel specific fb info
+	 */
+	mfd->panel_info = pdata->panel_info;
+
+	mfd->fb_imgType = MDP_RGB_565;
+
+	platform_set_drvdata(mdp_dev, mfd);
+
+	rc = platform_device_add(mdp_dev);
+	if (rc) {
+		WRITEBACK_MSG_ERR("failed to add device");
+		platform_device_put(mdp_dev);
+		return rc;
+	}
+	return rc;
+}
+
+static struct platform_driver writeback_driver = {
+	.probe = writeback_probe,
+	.driver = {
+		.name = "writeback",
+	},
+};
+
+static int __init writeback_driver_init(void)
+{
+	int rc = 0;
+	WRITEBACK_MSG_ERR("Inside writeback_driver_init\n");
+	rc = platform_driver_register(&writeback_driver);
+	return rc;
+}
+
+module_init(writeback_driver_init);
diff --git a/drivers/video/msm/mdp4_wfd_writeback_panel.c b/drivers/video/msm/mdp4_wfd_writeback_panel.c
new file mode 100644
index 0000000..40ffb65
--- /dev/null
+++ b/drivers/video/msm/mdp4_wfd_writeback_panel.c
@@ -0,0 +1,83 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/ioctl.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/version.h>
+
+#include "mdp4_wfd_writeback_util.h"
+#include "msm_fb.h"
+
+static int __devinit writeback_panel_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	if (pdev->id == 0)
+		return 0;
+
+	if (!msm_fb_add_device(pdev)) {
+		WRITEBACK_MSG_ERR("Failed to add fd device\n");
+		rc = -ENOMEM;
+	}
+	return rc;
+}
+static struct msm_fb_panel_data writeback_msm_panel_data = {
+	.panel_info = {
+		.type = WRITEBACK_PANEL,
+		.xres = 1280,
+		.yres = 720,
+		.pdest = DISPLAY_3,
+		.wait_cycle = 0,
+		.bpp = 24,
+		.fb_num = 1,
+		.clk_rate = 74250000,
+	},
+};
+
+static struct platform_device writeback_panel_device = {
+	.name = "writeback_panel",
+	.id = 1,
+	.dev.platform_data = &writeback_msm_panel_data,
+};
+static struct platform_driver writeback_panel_driver = {
+	.probe = writeback_panel_probe,
+	.driver = {
+		.name = "writeback_panel"
+	}
+};
+
+static int __init writeback_panel_init(void)
+{
+	int rc = 0;
+	rc = platform_driver_register(&writeback_panel_driver);
+	if (rc) {
+		WRITEBACK_MSG_ERR("Failed to register platform driver\n");
+		goto fail_driver_registration;
+	}
+	rc = platform_device_register(&writeback_panel_device);
+	if (rc) {
+		WRITEBACK_MSG_ERR("Failed to register "
+				"writeback_panel_device\n");
+		goto fail_device_registration;
+	}
+	return rc;
+fail_device_registration:
+	platform_driver_unregister(&writeback_panel_driver);
+fail_driver_registration:
+	return rc;
+}
+
+module_init(writeback_panel_init);
diff --git a/drivers/video/msm/mdp4_wfd_writeback_util.h b/drivers/video/msm/mdp4_wfd_writeback_util.h
new file mode 100644
index 0000000..2d62713
--- /dev/null
+++ b/drivers/video/msm/mdp4_wfd_writeback_util.h
@@ -0,0 +1,28 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _WRITEBACK_UTIL_H_
+#define _WRITEBACK_UTIL_H_
+
+#define DEBUG
+
+#ifdef DEBUG
+	#define WRITEBACK_MSG_INFO(fmt...) pr_info(fmt)
+	#define WRITEBACK_MSG_WARN(fmt...) pr_warning(fmt)
+#else
+	#define WRITEBACK_MSG_INFO(fmt...)
+	#define WRITEBACK_MSG_WARN(fmt...)
+#endif
+	#define WRITEBACK_MSG_ERR(fmt...) pr_err(fmt)
+	#define WRITEBACK_MSG_CRIT(fmt...) pr_crit(fmt)
+#endif
diff --git a/drivers/video/msm/mdp_csc_table.h b/drivers/video/msm/mdp_csc_table.h
index d1cde30..a0f72c0 100644
--- a/drivers/video/msm/mdp_csc_table.h
+++ b/drivers/video/msm/mdp_csc_table.h
@@ -1,4 +1,4 @@
-/* drivers/video/msm_fb/mdp_csc_table.h
+/* drivers/video/msm/mdp_csc_table.h
  *
  * Copyright (C) 2007 QUALCOMM Incorporated
  * Copyright (C) 2007 Google Incorporated
@@ -16,57 +16,116 @@
 static struct {
 	uint32_t reg;
 	uint32_t val;
-} csc_table[] = {
-	{ 0x40400, 0x83 },
-	{ 0x40404, 0x102 },
-	{ 0x40408, 0x32 },
-	{ 0x4040c, 0xffffffb5 },
-	{ 0x40410, 0xffffff6c },
-	{ 0x40414, 0xe1 },
-	{ 0x40418, 0xe1 },
-	{ 0x4041c, 0xffffff45 },
-	{ 0x40420, 0xffffffdc },
-	{ 0x40440, 0x254 },
-	{ 0x40444, 0x0 },
-	{ 0x40448, 0x331 },
-	{ 0x4044c, 0x254 },
-	{ 0x40450, 0xffffff38 },
-	{ 0x40454, 0xfffffe61 },
-	{ 0x40458, 0x254 },
-	{ 0x4045c, 0x409 },
-	{ 0x40460, 0x0 },
-	{ 0x40480, 0x5d },
-	{ 0x40484, 0x13a },
-	{ 0x40488, 0x20 },
-	{ 0x4048c, 0xffffffcd },
-	{ 0x40490, 0xffffff54 },
-	{ 0x40494, 0xe1 },
-	{ 0x40498, 0xe1 },
-	{ 0x4049c, 0xffffff35 },
-	{ 0x404a0, 0xffffffec },
-	{ 0x404c0, 0x254 },
-	{ 0x404c4, 0x0 },
-	{ 0x404c8, 0x396 },
-	{ 0x404cc, 0x254 },
-	{ 0x404d0, 0xffffff94 },
-	{ 0x404d4, 0xfffffef0 },
-	{ 0x404d8, 0x254 },
-	{ 0x404dc, 0x43a },
-	{ 0x404e0, 0x0 },
-	{ 0x40500, 0x10 },
-	{ 0x40504, 0x80 },
-	{ 0x40508, 0x80 },
-	{ 0x40540, 0x10 },
-	{ 0x40544, 0x80 },
-	{ 0x40548, 0x80 },
-	{ 0x40580, 0x10 },
-	{ 0x40584, 0xeb },
-	{ 0x40588, 0x10 },
-	{ 0x4058c, 0xf0 },
-	{ 0x405c0, 0x10 },
-	{ 0x405c4, 0xeb },
-	{ 0x405c8, 0x10 },
-	{ 0x405cc, 0xf0 },
+} csc_matrix_config_table[] = {
+	/* RGB -> YUV primary forward matrix (set1). */
+	{ MDP_CSC_PFMVn(0), 0x83 },
+	{ MDP_CSC_PFMVn(1), 0x102 },
+	{ MDP_CSC_PFMVn(2), 0x32 },
+	{ MDP_CSC_PFMVn(3), 0xffffffb5 },
+	{ MDP_CSC_PFMVn(4), 0xffffff6c },
+	{ MDP_CSC_PFMVn(5), 0xe1 },
+	{ MDP_CSC_PFMVn(6), 0xe1 },
+	{ MDP_CSC_PFMVn(7), 0xffffff45 },
+	{ MDP_CSC_PFMVn(8), 0xffffffdc },
+
+	/* YUV -> RGB primary reverse matrix (set2) */
+	{ MDP_CSC_PRMVn(0), 0x254 },
+	{ MDP_CSC_PRMVn(1), 0x0 },
+	{ MDP_CSC_PRMVn(2), 0x331 },
+	{ MDP_CSC_PRMVn(3), 0x254 },
+	{ MDP_CSC_PRMVn(4), 0xffffff38 },
+	{ MDP_CSC_PRMVn(5), 0xfffffe61 },
+	{ MDP_CSC_PRMVn(6), 0x254 },
+	{ MDP_CSC_PRMVn(7), 0x409 },
+	{ MDP_CSC_PRMVn(8), 0x0 },
+
+#ifndef CONFIG_MSM_MDP31
+	/* For MDP 2.2/3.0 */
+
+	/* primary limit vector */
+	{ MDP_CSC_PLVn(0), 0x10 },
+	{ MDP_CSC_PLVn(1), 0xeb },
+	{ MDP_CSC_PLVn(2), 0x10 },
+	{ MDP_CSC_PLVn(3), 0xf0 },
+
+	/* primary bias vector */
+	{ MDP_CSC_PBVn(0), 0x10 },
+	{ MDP_CSC_PBVn(1), 0x80 },
+	{ MDP_CSC_PBVn(2), 0x80 },
+
+#else /* CONFIG_MSM_MDP31 */
+
+	/* limit vectors configuration */
+	/* rgb -> yuv (set1) pre-limit vector */
+	{ MDP_PPP_CSC_PRE_LV1n(0), 0x10 },
+	{ MDP_PPP_CSC_PRE_LV1n(1), 0xeb },
+	{ MDP_PPP_CSC_PRE_LV1n(2), 0x10 },
+	{ MDP_PPP_CSC_PRE_LV1n(3), 0xf0 },
+	{ MDP_PPP_CSC_PRE_LV1n(4), 0x10 },
+	{ MDP_PPP_CSC_PRE_LV1n(5), 0xf0 },
+
+	/* rgb -> yuv (set1) post-limit vector */
+	{ MDP_PPP_CSC_POST_LV1n(0), 0x0 },
+	{ MDP_PPP_CSC_POST_LV1n(1), 0xff },
+	{ MDP_PPP_CSC_POST_LV1n(2), 0x0 },
+	{ MDP_PPP_CSC_POST_LV1n(3), 0xff },
+	{ MDP_PPP_CSC_POST_LV1n(4), 0x0 },
+	{ MDP_PPP_CSC_POST_LV1n(5), 0xff },
+
+	/* yuv -> rgb (set2) pre-limit vector */
+	{ MDP_PPP_CSC_PRE_LV2n(0), 0x0 },
+	{ MDP_PPP_CSC_PRE_LV2n(1), 0xff },
+	{ MDP_PPP_CSC_PRE_LV2n(2), 0x0 },
+	{ MDP_PPP_CSC_PRE_LV2n(3), 0xff },
+	{ MDP_PPP_CSC_PRE_LV2n(4), 0x0 },
+	{ MDP_PPP_CSC_PRE_LV2n(5), 0xff },
+
+	/* yuv -> rgb (set2) post-limit vector */
+	{ MDP_PPP_CSC_POST_LV2n(0), 0x10 },
+	{ MDP_PPP_CSC_POST_LV2n(1), 0xeb },
+	{ MDP_PPP_CSC_POST_LV2n(2), 0x10 },
+	{ MDP_PPP_CSC_POST_LV2n(3), 0xf0 },
+	{ MDP_PPP_CSC_POST_LV2n(4), 0x10 },
+	{ MDP_PPP_CSC_POST_LV2n(5), 0xf0 },
+
+	/* bias vectors configuration */
+
+	/* XXX: why is set2 used for rgb->yuv, but set1 */
+	/* used for yuv -> rgb??!? Seems to be the reverse of the
+	 * other vectors. */
+
+	/* RGB -> YUV pre-bias vector... */
+	{ MDP_PPP_CSC_PRE_BV2n(0), 0 },
+	{ MDP_PPP_CSC_PRE_BV2n(1), 0 },
+	{ MDP_PPP_CSC_PRE_BV2n(2), 0 },
+
+	/* RGB -> YUV post-bias vector */
+	{ MDP_PPP_CSC_POST_BV2n(0), 0x10 },
+	{ MDP_PPP_CSC_POST_BV2n(1), 0x80 },
+	{ MDP_PPP_CSC_POST_BV2n(2), 0x80 },
+
+	/* YUV -> RGB pre-bias vector... */
+	{ MDP_PPP_CSC_PRE_BV1n(0), 0x1f0 },
+	{ MDP_PPP_CSC_PRE_BV1n(1), 0x180 },
+	{ MDP_PPP_CSC_PRE_BV1n(2), 0x180 },
+
+	/* YUV -> RGB post-bias vector */
+	{ MDP_PPP_CSC_POST_BV1n(0), 0 },
+	{ MDP_PPP_CSC_POST_BV1n(1), 0 },
+	{ MDP_PPP_CSC_POST_BV1n(2), 0 },
+
+	/* luma filter coefficients */
+	{ MDP_PPP_DEINT_COEFFn(0), 0x3e0 },
+	{ MDP_PPP_DEINT_COEFFn(1), 0x360 },
+	{ MDP_PPP_DEINT_COEFFn(2), 0x120 },
+	{ MDP_PPP_DEINT_COEFFn(3), 0x140 },
+#endif
+};
+
+static struct {
+	uint32_t reg;
+	uint32_t val;
+} csc_color_lut[] = {
 	{ 0x40800, 0x0 },
 	{ 0x40804, 0x151515 },
 	{ 0x40808, 0x1d1d1d },
diff --git a/drivers/video/msm/mdp_cursor.c b/drivers/video/msm/mdp_cursor.c
new file mode 100644
index 0000000..f8c08e3
--- /dev/null
+++ b/drivers/video/msm/mdp_cursor.c
@@ -0,0 +1,264 @@
+/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/hrtimer.h>
+
+#include <mach/hardware.h>
+#include <asm/io.h>
+
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <linux/semaphore.h>
+#include <linux/spinlock.h>
+
+#include <linux/fb.h>
+
+#include "mdp.h"
+#include "msm_fb.h"
+
+static int cursor_enabled;
+
+#include "mdp4.h"
+
+#if	defined(CONFIG_FB_MSM_OVERLAY) && defined(CONFIG_FB_MSM_MDP40)
+static struct workqueue_struct *mdp_cursor_ctrl_wq;
+static struct work_struct mdp_cursor_ctrl_worker;
+
+/* cursor configuration */
+static void *cursor_buf_phys;
+static __u32 width, height, bg_color;
+static int calpha_en, transp_en, alpha;
+static int sync_disabled = -1;
+
+void mdp_cursor_ctrl_workqueue_handler(struct work_struct *work)
+{
+	unsigned long flag;
+
+	/* disable vsync */
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	mdp_disable_irq(MDP_OVERLAY0_TERM);
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+}
+
+void mdp_hw_cursor_init(void)
+{
+	mdp_cursor_ctrl_wq =
+			create_singlethread_workqueue("mdp_cursor_ctrl_wq");
+	INIT_WORK(&mdp_cursor_ctrl_worker, mdp_cursor_ctrl_workqueue_handler);
+}
+
+void mdp_hw_cursor_done(void)
+{
+	/* Cursor configuration:
+	 *
+	 * This is done in DMA_P_DONE ISR because the following registers are
+	 * not double buffered in hardware:
+	 *
+	 * MDP_DMA_P_CURSOR_SIZE, address = 0x90044
+	 * MDP_DMA_P_CURSOR_BLEND_CONFIG, address = 0x90060
+	 * MDP_DMA_P_CURSOR_BLEND_PARAM, address = 0x90064
+	 * MDP_DMA_P_CURSOR_BLEND_TRANS_LOW, address = 0x90068
+	 * MDP_DMA_P_CURSOR_BLEND_TRANS_HIG, address = 0x9006C
+	 *
+	 * Moving this code out of the ISR will cause the MDP to underrun!
+	 */
+	spin_lock(&mdp_spin_lock);
+	if (sync_disabled) {
+		spin_unlock(&mdp_spin_lock);
+		return;
+	}
+
+	MDP_OUTP(MDP_BASE + 0x90044, (height << 16) | width);
+	MDP_OUTP(MDP_BASE + 0x90048, cursor_buf_phys);
+
+	MDP_OUTP(MDP_BASE + 0x90060,
+		 (transp_en << 3) | (calpha_en << 1) |
+		 (inp32(MDP_BASE + 0x90060) & 0x1));
+
+	MDP_OUTP(MDP_BASE + 0x90064, (alpha << 24));
+	MDP_OUTP(MDP_BASE + 0x90068, (0xffffff & bg_color));
+	MDP_OUTP(MDP_BASE + 0x9006C, (0xffffff & bg_color));
+
+	/* enable/disable the cursor as per the last request */
+	if (cursor_enabled && !(inp32(MDP_BASE + 0x90060) & (0x1)))
+		MDP_OUTP(MDP_BASE + 0x90060, inp32(MDP_BASE + 0x90060) | 0x1);
+	else if (!cursor_enabled && (inp32(MDP_BASE + 0x90060) & (0x1)))
+		MDP_OUTP(MDP_BASE + 0x90060,
+					inp32(MDP_BASE + 0x90060) & (~0x1));
+
+	/* enqueue the task to disable MDP interrupts */
+	queue_work(mdp_cursor_ctrl_wq, &mdp_cursor_ctrl_worker);
+
+	/* update done */
+	sync_disabled = 1;
+	spin_unlock(&mdp_spin_lock);
+}
+
+static void mdp_hw_cursor_enable_vsync(void)
+{
+	/* if the cursor registers were updated (once or more) since the
+	 * last vsync, enable the vsync interrupt (if not already enabled)
+	 * for the next update
+	 */
+	if (sync_disabled) {
+
+		/* cancel pending task to disable MDP interrupts */
+		if (work_pending(&mdp_cursor_ctrl_worker))
+			cancel_work_sync(&mdp_cursor_ctrl_worker);
+		else
+			/* enable irq */
+			mdp_enable_irq(MDP_OVERLAY0_TERM);
+
+		sync_disabled = 0;
+
+		/* enable vsync intr */
+		outp32(MDP_INTR_CLEAR, INTR_OVERLAY0_DONE);
+		mdp_intr_mask |= INTR_OVERLAY0_DONE;
+		outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+	}
+}
+
+int mdp_hw_cursor_sync_update(struct fb_info *info, struct fb_cursor *cursor)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	struct fb_image *img = &cursor->image;
+	unsigned long flag;
+	int sync_needed = 0, ret = 0;
+
+	if ((img->width > MDP_CURSOR_WIDTH) ||
+	    (img->height > MDP_CURSOR_HEIGHT) ||
+	    (img->depth != 32))
+		return -EINVAL;
+
+	if (cursor->set & FB_CUR_SETPOS)
+		MDP_OUTP(MDP_BASE + 0x9004c, (img->dy << 16) | img->dx);
+
+	if (cursor->set & FB_CUR_SETIMAGE) {
+		ret = copy_from_user(mfd->cursor_buf, img->data,
+					img->width*img->height*4);
+		if (ret)
+			return ret;
+
+		spin_lock_irqsave(&mdp_spin_lock, flag);
+		if (img->bg_color == 0xffffffff)
+			transp_en = 0;
+		else
+			transp_en = 1;
+
+		alpha = (img->fg_color & 0xff000000) >> 24;
+
+		if (alpha)
+			calpha_en = 0x2; /* xrgb */
+		else
+			calpha_en = 0x1; /* argb */
+
+		/* cursor parameters */
+		height = img->height;
+		width = img->width;
+		bg_color = img->bg_color;
+		cursor_buf_phys = mfd->cursor_buf_phys;
+
+		sync_needed = 1;
+	} else
+		spin_lock_irqsave(&mdp_spin_lock, flag);
+
+	if ((cursor->enable) && (!cursor_enabled)) {
+		cursor_enabled = 1;
+		sync_needed = 1;
+	} else if ((!cursor->enable) && (cursor_enabled)) {
+		cursor_enabled = 0;
+		sync_needed = 1;
+	}
+
+	/* if sync cursor update is needed, enable vsync */
+	if (sync_needed)
+		mdp_hw_cursor_enable_vsync();
+
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+
+	return 0;
+}
+#endif /* CONFIG_FB_MSM_OVERLAY && CONFIG_FB_MSM_MDP40 */
+
+int mdp_hw_cursor_update(struct fb_info *info, struct fb_cursor *cursor)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	struct fb_image *img = &cursor->image;
+	int calpha_en, transp_en;
+	int alpha;
+	int ret = 0;
+
+	if ((img->width > MDP_CURSOR_WIDTH) ||
+	    (img->height > MDP_CURSOR_HEIGHT) ||
+	    (img->depth != 32))
+		return -EINVAL;
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	if (cursor->set & FB_CUR_SETPOS)
+		MDP_OUTP(MDP_BASE + 0x9004c, (img->dy << 16) | img->dx);
+
+	if (cursor->set & FB_CUR_SETIMAGE) {
+		ret = copy_from_user(mfd->cursor_buf, img->data,
+					img->width*img->height*4);
+		if (ret)
+			return ret;
+
+		if (img->bg_color == 0xffffffff)
+			transp_en = 0;
+		else
+			transp_en = 1;
+
+		alpha = (img->fg_color & 0xff000000) >> 24;
+
+		if (alpha)
+			calpha_en = 0x2; /* xrgb */
+		else
+			calpha_en = 0x1; /* argb */
+
+		MDP_OUTP(MDP_BASE + 0x90044, (img->height << 16) | img->width);
+		MDP_OUTP(MDP_BASE + 0x90048, mfd->cursor_buf_phys);
+		/* order the writes the cursor_buf before updating the
+		 * hardware */
+		dma_coherent_pre_ops();
+		MDP_OUTP(MDP_BASE + 0x90060,
+			 (transp_en << 3) | (calpha_en << 1) |
+			 (inp32(MDP_BASE + 0x90060) & 0x1));
+#ifdef CONFIG_FB_MSM_MDP40
+		MDP_OUTP(MDP_BASE + 0x90064, (alpha << 24));
+		MDP_OUTP(MDP_BASE + 0x90068, (0xffffff & img->bg_color));
+		MDP_OUTP(MDP_BASE + 0x9006C, (0xffffff & img->bg_color));
+#else
+		MDP_OUTP(MDP_BASE + 0x90064,
+			 (alpha << 24) | (0xffffff & img->bg_color));
+		MDP_OUTP(MDP_BASE + 0x90068, 0);
+#endif
+	}
+
+	if ((cursor->enable) && (!cursor_enabled)) {
+		cursor_enabled = 1;
+		MDP_OUTP(MDP_BASE + 0x90060, inp32(MDP_BASE + 0x90060) | 0x1);
+	} else if ((!cursor->enable) && (cursor_enabled)) {
+		cursor_enabled = 0;
+		MDP_OUTP(MDP_BASE + 0x90060,
+			 inp32(MDP_BASE + 0x90060) & (~0x1));
+	}
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	return 0;
+}
diff --git a/drivers/video/msm/mdp_debugfs.c b/drivers/video/msm/mdp_debugfs.c
new file mode 100644
index 0000000..7defd82
--- /dev/null
+++ b/drivers/video/msm/mdp_debugfs.c
@@ -0,0 +1,1392 @@
+/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/hrtimer.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+#include <linux/semaphore.h>
+#include <linux/uaccess.h>
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+
+#include "mdp.h"
+#include "msm_fb.h"
+#ifdef CONFIG_FB_MSM_MDP40
+#include "mdp4.h"
+#endif
+#include "mddihosti.h"
+#include "tvenc.h"
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL
+#include "hdmi_msm.h"
+#endif
+
+#define MDP_DEBUG_BUF	2048
+
+static uint32	mdp_offset;
+static uint32	mdp_count;
+
+static char	debug_buf[MDP_DEBUG_BUF];
+
+/*
+ * MDP4
+ *
+ */
+
+static int mdp_offset_open(struct inode *inode, struct file *file)
+{
+	/* non-seekable */
+	file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
+	return 0;
+}
+
+static int mdp_offset_release(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static ssize_t mdp_offset_write(
+	struct file *file,
+	const char __user *buff,
+	size_t count,
+	loff_t *ppos)
+{
+	uint32 off, cnt;
+
+	if (count >= sizeof(debug_buf))
+		return -EFAULT;
+
+	if (copy_from_user(debug_buf, buff, count))
+		return -EFAULT;
+
+	debug_buf[count] = 0;	/* end of string */
+
+	sscanf(debug_buf, "%x %d", &off, &cnt);
+
+	if (cnt <= 0)
+		cnt = 1;
+
+	mdp_offset = off;
+	mdp_count = cnt;
+
+	printk(KERN_INFO "%s: offset=%x cnt=%d\n", __func__,
+				mdp_offset, mdp_count);
+
+	return count;
+}
+
+static ssize_t mdp_offset_read(
+	struct file *file,
+	char __user *buff,
+	size_t count,
+	loff_t *ppos)
+{
+	int len = 0;
+
+
+	if (*ppos)
+		return 0;	/* the end */
+
+	len = snprintf(debug_buf, sizeof(debug_buf), "0x%08x %d\n",
+					mdp_offset, mdp_count);
+	if (len < 0)
+		return 0;
+
+	if (copy_to_user(buff, debug_buf, len))
+		return -EFAULT;
+
+	*ppos += len;	/* increase offset */
+
+	return len;
+}
+
+static const struct file_operations mdp_off_fops = {
+	.open = mdp_offset_open,
+	.release = mdp_offset_release,
+	.read = mdp_offset_read,
+	.write = mdp_offset_write,
+};
+
+static int mdp_reg_open(struct inode *inode, struct file *file)
+{
+	/* non-seekable */
+	file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
+	return 0;
+}
+
+static int mdp_reg_release(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static ssize_t mdp_reg_write(
+	struct file *file,
+	const char __user *buff,
+	size_t count,
+	loff_t *ppos)
+{
+	uint32 off, data;
+	int cnt;
+
+	if (count >= sizeof(debug_buf))
+		return -EFAULT;
+
+	if (copy_from_user(debug_buf, buff, count))
+		return -EFAULT;
+
+	debug_buf[count] = 0;	/* end of string */
+
+	cnt = sscanf(debug_buf, "%x %x", &off, &data);
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	outpdw(MDP_BASE + off, data);
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	printk(KERN_INFO "%s: addr=%x data=%x\n", __func__, off, data);
+
+	return count;
+}
+
+static ssize_t mdp_reg_read(
+	struct file *file,
+	char __user *buff,
+	size_t count,
+	loff_t *ppos)
+{
+	int len = 0;
+	uint32 data;
+	int i, j, off, dlen, num;
+	char *bp, *cp;
+	int tot = 0;
+
+
+	if (*ppos)
+		return 0;	/* the end */
+
+	j = 0;
+	num = 0;
+	bp = debug_buf;
+	cp = MDP_BASE + mdp_offset;
+	dlen = sizeof(debug_buf);
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	while (j++ < 8) {
+		len = snprintf(bp, dlen, "0x%08x: ", (int)cp);
+		tot += len;
+		bp += len;
+		dlen -= len;
+		off = 0;
+		i = 0;
+		while (i++ < 4) {
+			data = inpdw(cp + off);
+			len = snprintf(bp, dlen, "%08x ", data);
+			tot += len;
+			bp += len;
+			dlen -= len;
+			off += 4;
+			num++;
+			if (num >= mdp_count)
+				break;
+		}
+		*bp++ = '\n';
+		--dlen;
+		tot++;
+		cp += off;
+		if (num >= mdp_count)
+			break;
+	}
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+	*bp = 0;
+	tot++;
+
+	if (copy_to_user(buff, debug_buf, tot))
+		return -EFAULT;
+
+	*ppos += tot;	/* increase offset */
+
+	return tot;
+}
+
+
+static const struct file_operations mdp_reg_fops = {
+	.open = mdp_reg_open,
+	.release = mdp_reg_release,
+	.read = mdp_reg_read,
+	.write = mdp_reg_write,
+};
+
+#ifdef CONFIG_FB_MSM_MDP40
+static int mdp_stat_open(struct inode *inode, struct file *file)
+{
+	/* non-seekable */
+	file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
+	return 0;
+}
+
+static int mdp_stat_release(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static ssize_t mdp_stat_write(
+	struct file *file,
+	const char __user *buff,
+	size_t count,
+	loff_t *ppos)
+{
+	unsigned long flag;
+
+	if (count > sizeof(debug_buf))
+		return -EFAULT;
+
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	memset((char *)&mdp4_stat, 0 , sizeof(mdp4_stat));	/* reset */
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+
+	return count;
+}
+
+static ssize_t mdp_stat_read(
+	struct file *file,
+	char __user *buff,
+	size_t count,
+	loff_t *ppos)
+{
+	int len = 0;
+	int tot = 0;
+	int dlen;
+	char *bp;
+
+
+	if (*ppos)
+		return 0;	/* the end */
+
+	bp = debug_buf;
+	dlen = sizeof(debug_buf);
+
+	len = snprintf(bp, dlen, "\nmdp:\n");
+	bp += len;
+	dlen -= len;
+
+	len = snprintf(bp, dlen, "int_total: %08lu\t",
+					mdp4_stat.intr_tot);
+	bp += len;
+	dlen -= len;
+
+	len = snprintf(bp, dlen, "int_overlay0: %08lu\t",
+					mdp4_stat.intr_overlay0);
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "int_overlay1: %08lu\n",
+					mdp4_stat.intr_overlay1);
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "int_overlay1: %08lu\n",
+					mdp4_stat.intr_overlay2);
+	bp += len;
+	dlen -= len;
+
+	len = snprintf(bp, dlen, "int_dmap: %08lu\t",
+					mdp4_stat.intr_dma_p);
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "int_dmas: %08lu\t",
+					mdp4_stat.intr_dma_s);
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "int_dmae:  %08lu\n",
+					mdp4_stat.intr_dma_e);
+	bp += len;
+	dlen -= len;
+
+	len = snprintf(bp, dlen, "primary:   vsync: %08lu\t",
+					mdp4_stat.intr_vsync_p);
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "underrun: %08lu\n",
+					mdp4_stat.intr_underrun_p);
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "external:  vsync: %08lu\t",
+					mdp4_stat.intr_vsync_e);
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "underrun: %08lu\n",
+					mdp4_stat.intr_underrun_e);
+
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "histogram: %08lu\t",
+					mdp4_stat.intr_histogram);
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "read_ptr: %08lu\n\n",
+					mdp4_stat.intr_rd_ptr);
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "dsi:\n");
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "int_total: %08lu\tmdp_start: %08lu\n",
+			mdp4_stat.intr_dsi, mdp4_stat.dsi_mdp_start);
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "int_cmd: %08lu\t",
+					mdp4_stat.intr_dsi_cmd);
+
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "int_mdp: %08lu\t",
+					mdp4_stat.intr_dsi_mdp);
+
+	bp += len;
+	dlen -= len;
+
+	len = snprintf(bp, dlen, "int_err: %08lu\n",
+					mdp4_stat.intr_dsi_err);
+
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "clk_on : %08lu\t",
+					mdp4_stat.dsi_clk_on);
+
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "clk_off: %08lu\n\n",
+					mdp4_stat.dsi_clk_off);
+
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "kickoff:\n");
+	bp += len;
+	dlen -= len;
+
+	len = snprintf(bp, dlen, "overlay0: %08lu\t",
+					mdp4_stat.kickoff_ov0);
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "dmap: %08lu\t",
+					mdp4_stat.kickoff_dmap);
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "dmas: %08lu\n",
+					mdp4_stat.kickoff_dmas);
+
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "overlay1: %08lu\t",
+					mdp4_stat.kickoff_ov1);
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "dmae: %08lu\n\n",
+					mdp4_stat.kickoff_dmae);
+
+	bp += len;
+	dlen -= len;
+
+	len = snprintf(bp, dlen, "overlay0_play:\n");
+	bp += len;
+	dlen -= len;
+
+	len = snprintf(bp, dlen, "set:   %08lu\t",
+					mdp4_stat.overlay_set[0]);
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "unset: %08lu\t",
+					mdp4_stat.overlay_unset[0]);
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "play:  %08lu\n",
+					mdp4_stat.overlay_play[0]);
+	bp += len;
+	dlen -= len;
+
+	len = snprintf(bp, dlen, "overlay1_play:\n");
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "set:   %08lu\t",
+					mdp4_stat.overlay_set[1]);
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "unset: %08lu\t",
+					mdp4_stat.overlay_unset[1]);
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "play:  %08lu\n\n",
+					mdp4_stat.overlay_play[1]);
+
+	bp += len;
+	dlen -= len;
+
+	len = snprintf(bp, dlen, "frame_push:\n");
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "rgb1:  %08lu\t\t",
+		       mdp4_stat.pipe[OVERLAY_PIPE_RGB1]);
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "rgb2:  %08lu\n",
+		       mdp4_stat.pipe[OVERLAY_PIPE_RGB2]);
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "vg1:   %08lu\t\t",
+		       mdp4_stat.pipe[OVERLAY_PIPE_VG1]);
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "vg2:   %08lu\n",
+		       mdp4_stat.pipe[OVERLAY_PIPE_VG2]);
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "err_mixer: %08lu\t", mdp4_stat.err_mixer);
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "err_size : %08lu\n", mdp4_stat.err_size);
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "err_scale: %08lu\t", mdp4_stat.err_scale);
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "err_format: %08lu\n", mdp4_stat.err_format);
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "err_play:  %08lu\t", mdp4_stat.err_play);
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "err_stage: %08lu\n", mdp4_stat.err_stage);
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "err_underflow: %08lu\n\n",
+		       mdp4_stat.err_underflow);
+	bp += len;
+	dlen -= len;
+
+	len = snprintf(bp, dlen, "writeback:\n");
+	bp += len;
+	dlen -= len;
+
+	len = snprintf(bp, dlen, "dsi_cmd: %08lu\t",
+					mdp4_stat.blt_dsi_cmd);
+	bp += len;
+	dlen -= len;
+
+	len = snprintf(bp, dlen, "dsi_video: %08lu\n",
+					mdp4_stat.blt_dsi_video);
+	bp += len;
+	dlen -= len;
+
+	len = snprintf(bp, dlen, "lcdc: %08lu\t",
+					mdp4_stat.blt_lcdc);
+	bp += len;
+	dlen -= len;
+
+	len = snprintf(bp, dlen, "dtv: %08lu\t",
+					mdp4_stat.blt_dtv);
+	bp += len;
+	dlen -= len;
+
+	len = snprintf(bp, dlen, "mddi: %08lu\n\n",
+					mdp4_stat.blt_mddi);
+	bp += len;
+	dlen -= len;
+
+	tot = (uint32)bp - (uint32)debug_buf;
+	*bp = 0;
+	tot++;
+
+	if (tot < 0)
+		return 0;
+	if (copy_to_user(buff, debug_buf, tot))
+		return -EFAULT;
+
+	*ppos += tot;	/* increase offset */
+
+	return tot;
+}
+
+static const struct file_operations mdp_stat_fops = {
+	.open = mdp_stat_open,
+	.release = mdp_stat_release,
+	.read = mdp_stat_read,
+	.write = mdp_stat_write,
+};
+#endif
+
+/*
+ * MDDI
+ *
+ */
+
+struct mddi_reg {
+	char *name;
+	int off;
+};
+
+static struct mddi_reg mddi_regs_list[] = {
+	{"MDDI_CMD", MDDI_CMD},	 	/* 0x0000 */
+	{"MDDI_VERSION", MDDI_VERSION},  /* 0x0004 */
+	{"MDDI_PRI_PTR", MDDI_PRI_PTR},  /* 0x0008 */
+	{"MDDI_BPS",  MDDI_BPS}, 	/* 0x0010 */
+	{"MDDI_SPM", MDDI_SPM}, 	/* 0x0014 */
+	{"MDDI_INT", MDDI_INT}, 	/* 0x0018 */
+	{"MDDI_INTEN", MDDI_INTEN},	/* 0x001c */
+	{"MDDI_REV_PTR", MDDI_REV_PTR},	/* 0x0020 */
+	{"MDDI_	REV_SIZE", MDDI_REV_SIZE},/* 0x0024 */
+	{"MDDI_STAT", MDDI_STAT},	/* 0x0028 */
+	{"MDDI_REV_RATE_DIV", MDDI_REV_RATE_DIV}, /* 0x002c */
+	{"MDDI_REV_CRC_ERR", MDDI_REV_CRC_ERR}, /* 0x0030 */
+	{"MDDI_TA1_LEN", MDDI_TA1_LEN}, /* 0x0034 */
+	{"MDDI_TA2_LEN", MDDI_TA2_LEN}, /* 0x0038 */
+	{"MDDI_TEST", MDDI_TEST}, 	/* 0x0040 */
+	{"MDDI_REV_PKT_CNT", MDDI_REV_PKT_CNT}, /* 0x0044 */
+	{"MDDI_DRIVE_HI", MDDI_DRIVE_HI},/* 0x0048 */
+	{"MDDI_DRIVE_LO", MDDI_DRIVE_LO},	/* 0x004c */
+	{"MDDI_DISP_WAKE", MDDI_DISP_WAKE},/* 0x0050 */
+	{"MDDI_REV_ENCAP_SZ", MDDI_REV_ENCAP_SZ}, /* 0x0054 */
+	{"MDDI_RTD_VAL", MDDI_RTD_VAL}, /* 0x0058 */
+	{"MDDI_PAD_CTL", MDDI_PAD_CTL},	 /* 0x0068 */
+	{"MDDI_DRIVER_START_CNT", MDDI_DRIVER_START_CNT}, /* 0x006c */
+	{"MDDI_CORE_VER", MDDI_CORE_VER}, /* 0x008c */
+	{"MDDI_FIFO_ALLOC", MDDI_FIFO_ALLOC}, /* 0x0090 */
+	{"MDDI_PAD_IO_CTL", MDDI_PAD_IO_CTL}, /* 0x00a0 */
+	{"MDDI_PAD_CAL", MDDI_PAD_CAL},  /* 0x00a4 */
+	{0, 0}
+};
+
+static int mddi_reg_open(struct inode *inode, struct file *file)
+{
+	/* non-seekable */
+	file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
+	return 0;
+}
+
+static int mddi_reg_release(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static void mddi_reg_write(int ndx, uint32 off, uint32 data)
+{
+	char *base;
+
+	if (ndx)
+		base = (char *)msm_emdh_base;
+	else
+		base = (char *)msm_pmdh_base;
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	writel(data, base + off);
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	printk(KERN_INFO "%s: addr=%x data=%x\n",
+			__func__, (int)(base+off), (int)data);
+}
+
+static int mddi_reg_read(int ndx)
+{
+	struct mddi_reg *reg;
+	unsigned char *base;
+	int data;
+	char *bp;
+	int len = 0;
+	int tot = 0;
+	int dlen;
+
+	if (ndx)
+		base = msm_emdh_base;
+	else
+		base = msm_pmdh_base;
+
+	reg = mddi_regs_list;
+	bp = debug_buf;
+	dlen = sizeof(debug_buf);
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	while (reg->name) {
+		data = readl((u32)base + reg->off);
+		len = snprintf(bp, dlen, "%s:0x%08x\t\t= 0x%08x\n",
+					reg->name, reg->off, data);
+		tot += len;
+		bp += len;
+		dlen -= len;
+		reg++;
+	}
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+	*bp = 0;
+	tot++;
+
+	return tot;
+}
+
+static ssize_t pmdh_reg_write(
+	struct file *file,
+	const char __user *buff,
+	size_t count,
+	loff_t *ppos)
+{
+	uint32 off, data;
+	int cnt;
+
+	if (count >= sizeof(debug_buf))
+		return -EFAULT;
+
+	if (copy_from_user(debug_buf, buff, count))
+		return -EFAULT;
+
+	debug_buf[count] = 0;	/* end of string */
+
+	cnt = sscanf(debug_buf, "%x %x", &off, &data);
+
+	mddi_reg_write(0, off, data);
+
+	return count;
+}
+
+static ssize_t pmdh_reg_read(
+	struct file *file,
+	char __user *buff,
+	size_t count,
+	loff_t *ppos)
+{
+	int tot = 0;
+
+	if (*ppos)
+		return 0;	/* the end */
+
+	tot = mddi_reg_read(0);	/* pmdh */
+
+	if (tot < 0)
+		return 0;
+	if (copy_to_user(buff, debug_buf, tot))
+		return -EFAULT;
+
+	*ppos += tot;	/* increase offset */
+
+	return tot;
+}
+
+
+static const struct file_operations pmdh_fops = {
+	.open = mddi_reg_open,
+	.release = mddi_reg_release,
+	.read = pmdh_reg_read,
+	.write = pmdh_reg_write,
+};
+
+
+
+#if defined(CONFIG_FB_MSM_OVERLAY) && defined(CONFIG_FB_MSM_MDDI)
+static int vsync_reg_open(struct inode *inode, struct file *file)
+{
+	/* non-seekable */
+	file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
+	return 0;
+}
+
+static int vsync_reg_release(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static ssize_t vsync_reg_write(
+	struct file *file,
+	const char __user *buff,
+	size_t count,
+	loff_t *ppos)
+{
+	uint32 enable;
+	int cnt;
+
+	if (count >= sizeof(debug_buf))
+		return -EFAULT;
+
+	if (copy_from_user(debug_buf, buff, count))
+		return -EFAULT;
+
+	debug_buf[count] = 0;	/* end of string */
+
+	cnt = sscanf(debug_buf, "%x", &enable);
+
+	mdp_dmap_vsync_set(enable);
+
+	return count;
+}
+
+static ssize_t vsync_reg_read(
+	struct file *file,
+	char __user *buff,
+	size_t count,
+	loff_t *ppos)
+{
+	char *bp;
+	int len = 0;
+	int tot = 0;
+	int dlen;
+
+	if (*ppos)
+		return 0;	/* the end */
+
+	bp = debug_buf;
+	dlen = sizeof(debug_buf);
+	len = snprintf(bp, dlen, "%x\n", mdp_dmap_vsync_get());
+	tot += len;
+	bp += len;
+	*bp = 0;
+	tot++;
+
+	if (copy_to_user(buff, debug_buf, tot))
+		return -EFAULT;
+
+	*ppos += tot;	/* increase offset */
+
+	return tot;
+}
+
+
+static const struct file_operations vsync_fops = {
+	.open = vsync_reg_open,
+	.release = vsync_reg_release,
+	.read = vsync_reg_read,
+	.write = vsync_reg_write,
+};
+#endif
+
+static ssize_t emdh_reg_write(
+	struct file *file,
+	const char __user *buff,
+	size_t count,
+	loff_t *ppos)
+{
+	uint32 off, data;
+	int cnt;
+
+	if (count >= sizeof(debug_buf))
+		return -EFAULT;
+
+	if (copy_from_user(debug_buf, buff, count))
+		return -EFAULT;
+
+	debug_buf[count] = 0;	/* end of string */
+
+	cnt = sscanf(debug_buf, "%x %x", &off, &data);
+
+	mddi_reg_write(1, off, data);
+
+	return count;
+}
+
+static ssize_t emdh_reg_read(
+	struct file *file,
+	char __user *buff,
+	size_t count,
+	loff_t *ppos)
+{
+	int tot = 0;
+
+	if (*ppos)
+		return 0;	/* the end */
+
+	tot = mddi_reg_read(1);	/* emdh */
+
+	if (tot < 0)
+		return 0;
+	if (copy_to_user(buff, debug_buf, tot))
+		return -EFAULT;
+
+	*ppos += tot;	/* increase offset */
+
+	return tot;
+}
+
+static const struct file_operations emdh_fops = {
+	.open = mddi_reg_open,
+	.release = mddi_reg_release,
+	.read = emdh_reg_read,
+	.write = emdh_reg_write,
+};
+
+
+uint32 dbg_offset;
+uint32 dbg_count;
+char *dbg_base;
+
+
+static int dbg_open(struct inode *inode, struct file *file)
+{
+	/* non-seekable */
+	file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
+	return 0;
+}
+
+static int dbg_release(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static ssize_t dbg_base_write(
+	struct file *file,
+	const char __user *buff,
+	size_t count,
+	loff_t *ppos)
+{
+	return count;
+}
+
+static ssize_t dbg_base_read(
+	struct file *file,
+	char __user *buff,
+	size_t count,
+	loff_t *ppos)
+{
+	int len = 0;
+	int tot = 0;
+	int dlen;
+	char *bp;
+
+
+	if (*ppos)
+		return 0;	/* the end */
+
+
+	bp = debug_buf;
+	dlen = sizeof(debug_buf);
+
+	len = snprintf(bp, dlen, "mdp_base  :    %08x\n",
+				(int)msm_mdp_base);
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "mddi_base :    %08x\n",
+				(int)msm_pmdh_base);
+	bp += len;
+	dlen -= len;
+	len = snprintf(bp, dlen, "emdh_base :    %08x\n",
+				(int)msm_emdh_base);
+	bp += len;
+	dlen -= len;
+#ifdef CONFIG_FB_MSM_TVOUT
+	len = snprintf(bp, dlen, "tvenv_base:    %08x\n",
+				(int)tvenc_base);
+	bp += len;
+	dlen -= len;
+#endif
+
+#ifdef CONFIG_FB_MSM_MIPI_DSI
+	len = snprintf(bp, dlen, "mipi_dsi_base: %08x\n",
+				(int)mipi_dsi_base);
+	bp += len;
+	dlen -= len;
+#endif
+
+	tot = (uint32)bp - (uint32)debug_buf;
+	*bp = 0;
+	tot++;
+
+	if (tot < 0)
+		return 0;
+	if (copy_to_user(buff, debug_buf, tot))
+		return -EFAULT;
+
+	*ppos += tot;	/* increase offset */
+
+	return tot;
+}
+
+static const struct file_operations dbg_base_fops = {
+	.open = dbg_open,
+	.release = dbg_release,
+	.read = dbg_base_read,
+	.write = dbg_base_write,
+};
+
+static ssize_t dbg_offset_write(
+	struct file *file,
+	const char __user *buff,
+	size_t count,
+	loff_t *ppos)
+{
+	uint32 off, cnt, num, base;
+
+	if (count >= sizeof(debug_buf))
+		return -EFAULT;
+
+	if (copy_from_user(debug_buf, buff, count))
+		return -EFAULT;
+
+	debug_buf[count] = 0;	/* end of string */
+
+	cnt = sscanf(debug_buf, "%x %d %x", &off, &num, &base);
+
+	if (cnt < 0)
+		cnt = 0;
+
+	if (cnt >= 1)
+		dbg_offset = off;
+	if (cnt >= 2)
+		dbg_count = num;
+	if (cnt >= 3)
+		dbg_base = (char *)base;
+
+	printk(KERN_INFO "%s: offset=%x cnt=%d base=%x\n", __func__,
+				dbg_offset, dbg_count, (int)dbg_base);
+
+	return count;
+}
+
+static ssize_t dbg_offset_read(
+	struct file *file,
+	char __user *buff,
+	size_t count,
+	loff_t *ppos)
+{
+	int len = 0;
+
+
+	if (*ppos)
+		return 0;	/* the end */
+
+	len = snprintf(debug_buf, sizeof(debug_buf), "0x%08x %d 0x%08x\n",
+				dbg_offset, dbg_count, (int)dbg_base);
+	if (len < 0)
+		return 0;
+
+	if (copy_to_user(buff, debug_buf, len))
+		return -EFAULT;
+
+	*ppos += len;	/* increase offset */
+
+	return len;
+}
+
+static const struct file_operations dbg_off_fops = {
+	.open = dbg_open,
+	.release = dbg_release,
+	.read = dbg_offset_read,
+	.write = dbg_offset_write,
+};
+
+
+static ssize_t dbg_reg_write(
+	struct file *file,
+	const char __user *buff,
+	size_t count,
+	loff_t *ppos)
+{
+	uint32 off, data;
+	int cnt;
+
+	if (count >= sizeof(debug_buf))
+		return -EFAULT;
+
+	if (copy_from_user(debug_buf, buff, count))
+		return -EFAULT;
+
+	debug_buf[count] = 0;	/* end of string */
+
+	cnt = sscanf(debug_buf, "%x %x", &off, &data);
+
+	writel(data, dbg_base + off);
+
+	printk(KERN_INFO "%s: addr=%x data=%x\n",
+			__func__, (int)(dbg_base+off), (int)data);
+
+	return count;
+}
+
+static ssize_t dbg_reg_read(
+	struct file *file,
+	char __user *buff,
+	size_t count,
+	loff_t *ppos)
+{
+	int len = 0;
+	uint32 data;
+	int i, j, off, dlen, num;
+	char *bp, *cp;
+	int tot = 0;
+
+
+	if (*ppos)
+		return 0;	/* the end */
+
+	if (dbg_base == 0)
+		return 0;	/* nothing to read */
+
+	j = 0;
+	num = 0;
+	bp = debug_buf;
+	cp = (char *)(dbg_base + dbg_offset);
+	dlen = sizeof(debug_buf);
+	while (j++ < 16) {
+		len = snprintf(bp, dlen, "0x%08x: ", (int)cp);
+		tot += len;
+		bp += len;
+		dlen -= len;
+		off = 0;
+		i = 0;
+		while (i++ < 4) {
+			data = readl(cp + off);
+			len = snprintf(bp, dlen, "%08x ", data);
+			tot += len;
+			bp += len;
+			dlen -= len;
+			off += 4;
+			num++;
+			if (num >= dbg_count)
+				break;
+		}
+		data = readl((u32)cp + off);
+		*bp++ = '\n';
+		--dlen;
+		tot++;
+		cp += off;
+		if (num >= dbg_count)
+			break;
+	}
+	*bp = 0;
+	tot++;
+
+	if (copy_to_user(buff, debug_buf, tot))
+		return -EFAULT;
+
+	*ppos += tot;	/* increase offset */
+
+	return tot;
+}
+
+
+static const struct file_operations dbg_reg_fops = {
+	.open = dbg_open,
+	.release = dbg_release,
+	.read = dbg_reg_read,
+	.write = dbg_reg_write,
+};
+
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL
+static uint32 hdmi_offset;
+static uint32 hdmi_count;
+
+static int hdmi_open(struct inode *inode, struct file *file)
+{
+	/* non-seekable */
+	file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
+	return 0;
+}
+
+static int hdmi_release(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static ssize_t hdmi_offset_write(
+	struct file *file,
+	const char __user *buff,
+	size_t count,
+	loff_t *ppos)
+{
+	uint32 off, cnt, num;
+
+	if (count >= sizeof(debug_buf))
+		return -EFAULT;
+
+	if (copy_from_user(debug_buf, buff, count))
+		return -EFAULT;
+
+	debug_buf[count] = 0;	/* end of string */
+
+	cnt = sscanf(debug_buf, "%x %d", &off, &num);
+
+	if (cnt < 0)
+		cnt = 0;
+
+	if (cnt >= 1)
+		hdmi_offset = off;
+	if (cnt >= 2)
+		hdmi_count = num;
+
+	printk(KERN_INFO "%s: offset=%x cnt=%d\n", __func__,
+				hdmi_offset, hdmi_count);
+
+	return count;
+}
+
+static ssize_t hdmi_offset_read(
+	struct file *file,
+	char __user *buff,
+	size_t count,
+	loff_t *ppos)
+{
+	int len = 0;
+
+
+	if (*ppos)
+		return 0;	/* the end */
+
+	len = snprintf(debug_buf, sizeof(debug_buf), "0x%08x %d\n",
+				hdmi_offset, hdmi_count);
+	if (len < 0)
+		return 0;
+
+	if (copy_to_user(buff, debug_buf, len))
+		return -EFAULT;
+
+	*ppos += len;	/* increase offset */
+
+	return len;
+}
+
+static const struct file_operations hdmi_off_fops = {
+	.open = hdmi_open,
+	.release = hdmi_release,
+	.read = hdmi_offset_read,
+	.write = hdmi_offset_write,
+};
+
+
+static ssize_t hdmi_reg_write(
+	struct file *file,
+	const char __user *buff,
+	size_t count,
+	loff_t *ppos)
+{
+	uint32 off, data, base;
+	int cnt;
+
+	if (count >= sizeof(debug_buf))
+		return -EFAULT;
+
+	if (copy_from_user(debug_buf, buff, count))
+		return -EFAULT;
+
+	base = hdmi_msm_get_io_base();
+	if (base == 0)
+		return -EFAULT;
+
+	debug_buf[count] = 0;	/* end of string */
+
+	cnt = sscanf(debug_buf, "%x %x", &off, &data);
+
+	writel(data, base + off);
+
+	printk(KERN_INFO "%s: addr=%x data=%x\n",
+			__func__, (int)(base+off), (int)data);
+
+	return count;
+}
+
+static ssize_t hdmi_reg_read(
+	struct file *file,
+	char __user *buff,
+	size_t count,
+	loff_t *ppos)
+{
+	int len = 0;
+	uint32 data;
+	int i, j, off, dlen, num;
+	char *bp, *cp;
+	int tot = 0;
+
+
+	if (*ppos)
+		return 0;	/* the end */
+
+	if (hdmi_msm_get_io_base() == 0)
+		return 0;	/* nothing to read */
+
+	j = 0;
+	num = 0;
+	bp = debug_buf;
+	cp = (char *)(hdmi_msm_get_io_base() + hdmi_offset);
+	dlen = sizeof(debug_buf);
+	while (j++ < 16) {
+		len = snprintf(bp, dlen, "0x%08x: ", (int)cp);
+		tot += len;
+		bp += len;
+		dlen -= len;
+		off = 0;
+		i = 0;
+		while (i++ < 4) {
+			data = readl(cp + off);
+			len = snprintf(bp, dlen, "%08x ", data);
+			tot += len;
+			bp += len;
+			dlen -= len;
+			off += 4;
+			num++;
+			if (num >= hdmi_count)
+				break;
+		}
+		data = readl((u32)cp + off);
+		*bp++ = '\n';
+		--dlen;
+		tot++;
+		cp += off;
+		if (num >= hdmi_count)
+			break;
+	}
+	*bp = 0;
+	tot++;
+
+	if (copy_to_user(buff, debug_buf, tot))
+		return -EFAULT;
+
+	*ppos += tot;	/* increase offset */
+
+	return tot;
+}
+
+
+static const struct file_operations hdmi_reg_fops = {
+	.open = hdmi_open,
+	.release = hdmi_release,
+	.read = hdmi_reg_read,
+	.write = hdmi_reg_write,
+};
+#endif
+
+/*
+ * debugfs
+ *
+ */
+
+int mdp_debugfs_init(void)
+{
+	struct dentry *dent = debugfs_create_dir("mdp", NULL);
+
+	if (IS_ERR(dent)) {
+		printk(KERN_ERR "%s(%d): debugfs_create_dir fail, error %ld\n",
+			__FILE__, __LINE__, PTR_ERR(dent));
+		return -1;
+	}
+
+	if (debugfs_create_file("off", 0644, dent, 0, &mdp_off_fops)
+			== NULL) {
+		printk(KERN_ERR "%s(%d): debugfs_create_file: index fail\n",
+			__FILE__, __LINE__);
+		return -1;
+	}
+
+	if (debugfs_create_file("reg", 0644, dent, 0, &mdp_reg_fops)
+			== NULL) {
+		printk(KERN_ERR "%s(%d): debugfs_create_file: debug fail\n",
+			__FILE__, __LINE__);
+		return -1;
+	}
+
+#ifdef CONFIG_FB_MSM_MDP40
+	if (debugfs_create_file("stat", 0644, dent, 0, &mdp_stat_fops)
+			== NULL) {
+		printk(KERN_ERR "%s(%d): debugfs_create_file: debug fail\n",
+			__FILE__, __LINE__);
+		return -1;
+	}
+#endif
+
+	dent = debugfs_create_dir("mddi", NULL);
+
+	if (IS_ERR(dent)) {
+		printk(KERN_ERR "%s(%d): debugfs_create_dir fail, error %ld\n",
+			__FILE__, __LINE__, PTR_ERR(dent));
+		return -1;
+	}
+
+	if (debugfs_create_file("reg", 0644, dent, 0, &pmdh_fops)
+			== NULL) {
+		printk(KERN_ERR "%s(%d): debugfs_create_file: debug fail\n",
+			__FILE__, __LINE__);
+		return -1;
+	}
+
+#if defined(CONFIG_FB_MSM_OVERLAY) && defined(CONFIG_FB_MSM_MDDI)
+	if (debugfs_create_file("vsync", 0644, dent, 0, &vsync_fops)
+			== NULL) {
+		printk(KERN_ERR "%s(%d): debugfs_create_file: debug fail\n",
+			__FILE__, __LINE__);
+		return -1;
+	}
+#endif
+
+	dent = debugfs_create_dir("emdh", NULL);
+
+	if (IS_ERR(dent)) {
+		printk(KERN_ERR "%s(%d): debugfs_create_dir fail, error %ld\n",
+			__FILE__, __LINE__, PTR_ERR(dent));
+		return -1;
+	}
+
+	if (debugfs_create_file("reg", 0644, dent, 0, &emdh_fops)
+			== NULL) {
+		printk(KERN_ERR "%s(%d): debugfs_create_file: debug fail\n",
+			__FILE__, __LINE__);
+		return -1;
+	}
+
+	dent = debugfs_create_dir("mdp-dbg", NULL);
+
+	if (IS_ERR(dent)) {
+		printk(KERN_ERR "%s(%d): debugfs_create_dir fail, error %ld\n",
+			__FILE__, __LINE__, PTR_ERR(dent));
+		return -1;
+	}
+
+	if (debugfs_create_file("base", 0644, dent, 0, &dbg_base_fops)
+			== NULL) {
+		printk(KERN_ERR "%s(%d): debugfs_create_file: index fail\n",
+			__FILE__, __LINE__);
+		return -1;
+	}
+
+	if (debugfs_create_file("off", 0644, dent, 0, &dbg_off_fops)
+			== NULL) {
+		printk(KERN_ERR "%s(%d): debugfs_create_file: index fail\n",
+			__FILE__, __LINE__);
+		return -1;
+	}
+
+	if (debugfs_create_file("reg", 0644, dent, 0, &dbg_reg_fops)
+			== NULL) {
+		printk(KERN_ERR "%s(%d): debugfs_create_file: debug fail\n",
+			__FILE__, __LINE__);
+		return -1;
+	}
+
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL
+	dent = debugfs_create_dir("hdmi", NULL);
+
+	if (IS_ERR(dent)) {
+		printk(KERN_ERR "%s(%d): debugfs_create_dir fail, error %ld\n",
+			__FILE__, __LINE__, PTR_ERR(dent));
+		return PTR_ERR(dent);
+	}
+
+	if (debugfs_create_file("off", 0644, dent, 0, &hdmi_off_fops)
+			== NULL) {
+		printk(KERN_ERR "%s(%d): debugfs_create_file: 'off' fail\n",
+			__FILE__, __LINE__);
+		return -ENOENT;
+	}
+
+	if (debugfs_create_file("reg", 0644, dent, 0, &hdmi_reg_fops)
+			== NULL) {
+		printk(KERN_ERR "%s(%d): debugfs_create_file: 'reg' fail\n",
+			__FILE__, __LINE__);
+		return -ENOENT;
+	}
+#endif
+
+	return 0;
+}
diff --git a/drivers/video/msm/mdp_dma.c b/drivers/video/msm/mdp_dma.c
new file mode 100644
index 0000000..2ba2c85
--- /dev/null
+++ b/drivers/video/msm/mdp_dma.c
@@ -0,0 +1,612 @@
+/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/hrtimer.h>
+
+#include <mach/hardware.h>
+#include <linux/io.h>
+
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <linux/semaphore.h>
+#include <linux/spinlock.h>
+
+#include <linux/fb.h>
+
+#include "mdp.h"
+#include "msm_fb.h"
+#include "mddihost.h"
+
+static uint32 mdp_last_dma2_update_width;
+static uint32 mdp_last_dma2_update_height;
+static uint32 mdp_curr_dma2_update_width;
+static uint32 mdp_curr_dma2_update_height;
+
+ktime_t mdp_dma2_last_update_time = { 0 };
+
+int mdp_lcd_rd_cnt_offset_slow = 20;
+int mdp_lcd_rd_cnt_offset_fast = 20;
+int mdp_vsync_usec_wait_line_too_short = 5;
+uint32 mdp_dma2_update_time_in_usec;
+uint32 mdp_total_vdopkts;
+
+extern u32 msm_fb_debug_enabled;
+extern struct workqueue_struct *mdp_dma_wq;
+
+int vsync_start_y_adjust = 4;
+
+static void mdp_dma2_update_lcd(struct msm_fb_data_type *mfd)
+{
+	MDPIBUF *iBuf = &mfd->ibuf;
+	int mddi_dest = FALSE;
+	int cmd_mode = FALSE;
+	uint32 outBpp = iBuf->bpp;
+	uint32 dma2_cfg_reg;
+	uint8 *src;
+	uint32 mddi_ld_param;
+	uint16 mddi_vdo_packet_reg;
+#ifndef CONFIG_FB_MSM_MDP303
+	struct msm_fb_panel_data *pdata =
+	    (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data;
+#endif
+	uint32 ystride = mfd->fbi->fix.line_length;
+	uint32 mddi_pkt_desc;
+
+	dma2_cfg_reg = DMA_PACK_ALIGN_LSB |
+		    DMA_OUT_SEL_AHB | DMA_IBUF_NONCONTIGUOUS;
+
+#ifdef CONFIG_FB_MSM_MDP22
+	dma2_cfg_reg |= DMA_PACK_TIGHT;
+#endif
+
+#ifdef CONFIG_FB_MSM_MDP30
+	/*
+	 * Software workaround:  On 7x25/7x27, the MDP will not
+	 * respond if dma_w is 1 pixel.  Set the update width to
+	 * 2 pixels and adjust the x offset if needed.
+	 */
+	if (iBuf->dma_w == 1) {
+		iBuf->dma_w = 2;
+		if (iBuf->dma_x == (iBuf->ibuf_width - 2))
+			iBuf->dma_x--;
+	}
+#endif
+
+	if (mfd->fb_imgType == MDP_BGR_565)
+		dma2_cfg_reg |= DMA_PACK_PATTERN_BGR;
+	else if (mfd->fb_imgType == MDP_RGBA_8888)
+		dma2_cfg_reg |= DMA_PACK_PATTERN_BGR;
+	else
+		dma2_cfg_reg |= DMA_PACK_PATTERN_RGB;
+
+	if (outBpp == 4) {
+		dma2_cfg_reg |= DMA_IBUF_C3ALPHA_EN;
+		dma2_cfg_reg |= DMA_IBUF_FORMAT_xRGB8888_OR_ARGB8888;
+	}
+
+	if (outBpp == 2)
+		dma2_cfg_reg |= DMA_IBUF_FORMAT_RGB565;
+
+	mddi_ld_param = 0;
+	mddi_vdo_packet_reg = mfd->panel_info.mddi.vdopkt;
+
+	if ((mfd->panel_info.type == MDDI_PANEL) ||
+	    (mfd->panel_info.type == EXT_MDDI_PANEL)) {
+		dma2_cfg_reg |= DMA_OUT_SEL_MDDI;
+		mddi_dest = TRUE;
+
+		if (mfd->panel_info.type == MDDI_PANEL) {
+			mdp_total_vdopkts++;
+			if (mfd->panel_info.pdest == DISPLAY_1) {
+				dma2_cfg_reg |= DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY;
+				mddi_ld_param = 0;
+#ifdef MDDI_HOST_WINDOW_WORKAROUND
+				mddi_window_adjust(mfd, iBuf->dma_x,
+						   iBuf->dma_w - 1, iBuf->dma_y,
+						   iBuf->dma_h - 1);
+#endif
+			} else {
+				dma2_cfg_reg |=
+				    DMA_MDDI_DMAOUT_LCD_SEL_SECONDARY;
+				mddi_ld_param = 1;
+#ifdef MDDI_HOST_WINDOW_WORKAROUND
+				mddi_window_adjust(mfd, iBuf->dma_x,
+						   iBuf->dma_w - 1, iBuf->dma_y,
+						   iBuf->dma_h - 1);
+#endif
+			}
+		} else {
+			dma2_cfg_reg |= DMA_MDDI_DMAOUT_LCD_SEL_EXTERNAL;
+			mddi_ld_param = 2;
+		}
+#ifdef CONFIG_FB_MSM_MDP303
+	} else if (mfd->panel_info.type == MIPI_CMD_PANEL) {
+		cmd_mode = TRUE;
+		dma2_cfg_reg |= DMA_OUT_SEL_DSI_CMD;
+#endif
+	} else {
+		if (mfd->panel_info.pdest == DISPLAY_1) {
+			dma2_cfg_reg |= DMA_AHBM_LCD_SEL_PRIMARY;
+			outp32(MDP_EBI2_LCD0, mfd->data_port_phys);
+		} else {
+			dma2_cfg_reg |= DMA_AHBM_LCD_SEL_SECONDARY;
+			outp32(MDP_EBI2_LCD1, mfd->data_port_phys);
+		}
+	}
+
+	src = (uint8 *) iBuf->buf;
+	/* starting input address */
+	src += iBuf->dma_x * outBpp + iBuf->dma_y * ystride;
+
+	mdp_curr_dma2_update_width = iBuf->dma_w;
+	mdp_curr_dma2_update_height = iBuf->dma_h;
+
+	/* MDP cmd block enable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+#ifdef CONFIG_FB_MSM_MDP22
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0184,
+			(iBuf->dma_h << 16 | iBuf->dma_w));
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0188, src);
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x018C, ystride);
+#else
+	if (cmd_mode)
+		MDP_OUTP(MDP_BASE + 0x90004,
+			(mfd->panel_info.yres << 16 | mfd->panel_info.xres));
+	else
+		MDP_OUTP(MDP_BASE + 0x90004, (iBuf->dma_h << 16 | iBuf->dma_w));
+
+	MDP_OUTP(MDP_BASE + 0x90008, src);
+	MDP_OUTP(MDP_BASE + 0x9000c, ystride);
+#endif
+
+	if (mfd->panel_info.bpp == 18) {
+		mddi_pkt_desc = MDDI_VDO_PACKET_DESC;
+		dma2_cfg_reg |= DMA_DSTC0G_6BITS |	/* 666 18BPP */
+		    DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
+	} else if (mfd->panel_info.bpp == 24) {
+		mddi_pkt_desc = MDDI_VDO_PACKET_DESC_24;
+		dma2_cfg_reg |= DMA_DSTC0G_8BITS |      /* 888 24BPP */
+			DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS;
+	} else {
+		mddi_pkt_desc = MDDI_VDO_PACKET_DESC_16;
+		dma2_cfg_reg |= DMA_DSTC0G_6BITS |	/* 565 16BPP */
+		    DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS;
+	}
+
+#ifndef CONFIG_FB_MSM_MDP303
+
+	if (mddi_dest) {
+#ifdef CONFIG_FB_MSM_MDP22
+		MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0194,
+			 (iBuf->dma_y << 16) | iBuf->dma_x);
+		MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01a0, mddi_ld_param);
+		MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01a4,
+			 (mddi_pkt_desc << 16) | mddi_vdo_packet_reg);
+#else
+		MDP_OUTP(MDP_BASE + 0x90010, (iBuf->dma_y << 16) | iBuf->dma_x);
+		MDP_OUTP(MDP_BASE + 0x00090, mddi_ld_param);
+		MDP_OUTP(MDP_BASE + 0x00094,
+			 (mddi_pkt_desc << 16) | mddi_vdo_packet_reg);
+#endif
+	} else {
+		/* setting EBI2 LCDC write window */
+		pdata->set_rect(iBuf->dma_x, iBuf->dma_y, iBuf->dma_w,
+				iBuf->dma_h);
+	}
+#else
+	if (mfd->panel_info.type == MIPI_CMD_PANEL) {
+		/* dma_p = 0, dma_s = 1 */
+		 MDP_OUTP(MDP_BASE + 0xF1000, 0x10);
+		 /* enable dsi trigger on dma_p */
+		 MDP_OUTP(MDP_BASE + 0xF1004, 0x01);
+	}
+#endif
+
+	/* dma2 config register */
+#ifdef MDP_HW_VSYNC
+	MDP_OUTP(MDP_BASE + 0x90000, dma2_cfg_reg);
+
+	if ((mfd->use_mdp_vsync) &&
+	    (mfd->ibuf.vsync_enable) && (mfd->panel_info.lcd.vsync_enable)) {
+		uint32 start_y;
+
+		if (vsync_start_y_adjust <= iBuf->dma_y)
+			start_y = iBuf->dma_y - vsync_start_y_adjust;
+		else
+			start_y =
+			    (mfd->total_lcd_lines - 1) - (vsync_start_y_adjust -
+							  iBuf->dma_y);
+
+		/*
+		 * MDP VSYNC clock must be On by now so, we don't have to
+		 * re-enable it
+		 */
+		MDP_OUTP(MDP_BASE + 0x210, start_y);
+		MDP_OUTP(MDP_BASE + 0x20c, 1);	/* enable prim vsync */
+	} else {
+		MDP_OUTP(MDP_BASE + 0x20c, 0);	/* disable prim vsync */
+	}
+#else
+#ifdef CONFIG_FB_MSM_MDP22
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0180, dma2_cfg_reg);
+#else
+	MDP_OUTP(MDP_BASE + 0x90000, dma2_cfg_reg);
+#endif
+#endif /* MDP_HW_VSYNC */
+
+	/* MDP cmd block disable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+static ktime_t vt = { 0 };
+int mdp_usec_diff_threshold = 100;
+int mdp_expected_usec_wait;
+
+enum hrtimer_restart mdp_dma2_vsync_hrtimer_handler(struct hrtimer *ht)
+{
+	struct msm_fb_data_type *mfd = NULL;
+
+	mfd = container_of(ht, struct msm_fb_data_type, dma_hrtimer);
+
+	mdp_pipe_kickoff(MDP_DMA2_TERM, mfd);
+
+	if (msm_fb_debug_enabled) {
+		ktime_t t;
+		int usec_diff;
+		int actual_wait;
+
+		t = ktime_get_real();
+
+		actual_wait = ktime_to_us(ktime_sub(t, vt));
+		usec_diff = actual_wait - mdp_expected_usec_wait;
+
+		if ((mdp_usec_diff_threshold < usec_diff) || (usec_diff < 0))
+			MSM_FB_DEBUG
+			    ("HRT Diff = %d usec Exp=%d usec  Act=%d usec\n",
+			     usec_diff, mdp_expected_usec_wait, actual_wait);
+	}
+
+	return HRTIMER_NORESTART;
+}
+
+
+#ifdef CONFIG_FB_MSM_MDP303
+static int busy_wait_cnt;
+
+void	mdp3_dsi_cmd_dma_busy_wait(struct msm_fb_data_type *mfd)
+{
+	unsigned long flag;
+	int need_wait = 0;
+
+#ifdef DSI_CLK_CTRL
+	mod_timer(&dsi_clock_timer, jiffies + HZ); /* one second */
+#endif
+
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+#ifdef DSI_CLK_CTRL
+	if (mipi_dsi_clk_on == 0)
+		mipi_dsi_turn_on_clks();
+#endif
+
+	if (mfd->dma->busy == TRUE) {
+		if (busy_wait_cnt == 0)
+			INIT_COMPLETION(mfd->dma->comp);
+		busy_wait_cnt++;
+		need_wait++;
+	}
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+
+	if (need_wait) {
+		/* wait until DMA finishes the current job */
+		wait_for_completion(&mfd->dma->comp);
+	}
+}
+#endif
+
+static void mdp_dma_schedule(struct msm_fb_data_type *mfd, uint32 term)
+{
+	/*
+	 * dma2 configure VSYNC block
+	 * vsync supported on Primary LCD only for now
+	 */
+	int32 mdp_lcd_rd_cnt;
+	uint32 usec_wait_time;
+	uint32 start_y;
+
+	/*
+	 * ToDo: if we can move HRT timer callback to workqueue, we can
+	 * move DMA2 power on under mdp_pipe_kickoff().
+	 * This will save a power for hrt time wait.
+	 * However if the latency for context switch (hrt irq -> workqueue)
+	 * is too big, we will miss the vsync timing.
+	 */
+	if (term == MDP_DMA2_TERM)
+		mdp_pipe_ctrl(MDP_DMA2_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	mdp_dma2_update_time_in_usec = ktime_to_us(mdp_dma2_last_update_time);
+
+	if ((!mfd->ibuf.vsync_enable) || (!mfd->panel_info.lcd.vsync_enable)
+	    || (mfd->use_mdp_vsync)) {
+		mdp_pipe_kickoff(term, mfd);
+		return;
+	}
+	/* SW vsync logic starts here */
+
+	/* get current rd counter */
+	mdp_lcd_rd_cnt = mdp_get_lcd_line_counter(mfd);
+	if (mdp_dma2_update_time_in_usec != 0) {
+		uint32 num, den;
+
+		/*
+		 * roi width boundary calculation to know the size of pixel
+		 * width that MDP can send faster or slower than LCD read
+		 * pointer
+		 */
+
+		num = mdp_last_dma2_update_width * mdp_last_dma2_update_height;
+		den =
+		    (((mfd->panel_info.lcd.refx100 * mfd->total_lcd_lines) /
+		      1000) * (mdp_dma2_update_time_in_usec / 100)) / 1000;
+
+		if (den == 0)
+			mfd->vsync_width_boundary[mdp_last_dma2_update_width] =
+			    mfd->panel_info.xres + 1;
+		else
+			mfd->vsync_width_boundary[mdp_last_dma2_update_width] =
+			    (int)(num / den);
+	}
+
+	if (mfd->vsync_width_boundary[mdp_last_dma2_update_width] >
+	    mdp_curr_dma2_update_width) {
+		/* MDP wrp is faster than LCD rdp */
+		mdp_lcd_rd_cnt += mdp_lcd_rd_cnt_offset_fast;
+	} else {
+		/* MDP wrp is slower than LCD rdp */
+		mdp_lcd_rd_cnt -= mdp_lcd_rd_cnt_offset_slow;
+	}
+
+	if (mdp_lcd_rd_cnt < 0)
+		mdp_lcd_rd_cnt = mfd->total_lcd_lines + mdp_lcd_rd_cnt;
+	else if (mdp_lcd_rd_cnt > mfd->total_lcd_lines)
+		mdp_lcd_rd_cnt = mdp_lcd_rd_cnt - mfd->total_lcd_lines - 1;
+
+	/* get wrt pointer position */
+	start_y = mfd->ibuf.dma_y;
+
+	/* measure line difference between start_y and rd counter */
+	if (start_y > mdp_lcd_rd_cnt) {
+		/*
+		 * *100 for lcd_ref_hzx100 was already multiplied by 100
+		 * *1000000 is for usec conversion
+		 */
+
+		if ((start_y - mdp_lcd_rd_cnt) <=
+		    mdp_vsync_usec_wait_line_too_short)
+			usec_wait_time = 0;
+		else
+			usec_wait_time =
+			    ((start_y -
+			      mdp_lcd_rd_cnt) * 1000000) /
+			    ((mfd->total_lcd_lines *
+			      mfd->panel_info.lcd.refx100) / 100);
+	} else {
+		if ((start_y + (mfd->total_lcd_lines - mdp_lcd_rd_cnt)) <=
+		    mdp_vsync_usec_wait_line_too_short)
+			usec_wait_time = 0;
+		else
+			usec_wait_time =
+			    ((start_y +
+			      (mfd->total_lcd_lines -
+			       mdp_lcd_rd_cnt)) * 1000000) /
+			    ((mfd->total_lcd_lines *
+			      mfd->panel_info.lcd.refx100) / 100);
+	}
+
+	mdp_last_dma2_update_width = mdp_curr_dma2_update_width;
+	mdp_last_dma2_update_height = mdp_curr_dma2_update_height;
+
+	if (usec_wait_time == 0) {
+		mdp_pipe_kickoff(term, mfd);
+	} else {
+		ktime_t wait_time;
+
+		wait_time = ns_to_ktime(usec_wait_time * 1000);
+
+		if (msm_fb_debug_enabled) {
+			vt = ktime_get_real();
+			mdp_expected_usec_wait = usec_wait_time;
+		}
+		hrtimer_start(&mfd->dma_hrtimer, wait_time, HRTIMER_MODE_REL);
+	}
+}
+
+#ifdef MDDI_HOST_WINDOW_WORKAROUND
+static void mdp_dma2_update_sub(struct msm_fb_data_type *mfd);
+void mdp_dma2_update(struct msm_fb_data_type *mfd)
+{
+	MDPIBUF *iBuf;
+	uint32 upper_height;
+
+	if (mfd->panel.type == EXT_MDDI_PANEL) {
+		mdp_dma2_update_sub(mfd);
+		return;
+	}
+
+	iBuf = &mfd->ibuf;
+
+	upper_height =
+	    (uint32) mddi_assign_pkt_height((uint16) iBuf->dma_w,
+					    (uint16) iBuf->dma_h, 18);
+
+	if (upper_height >= iBuf->dma_h) {
+		mdp_dma2_update_sub(mfd);
+	} else {
+		uint32 lower_height;
+
+		/* sending the upper region first */
+		lower_height = iBuf->dma_h - upper_height;
+		iBuf->dma_h = upper_height;
+		mdp_dma2_update_sub(mfd);
+
+		/* sending the lower region second */
+		iBuf->dma_h = lower_height;
+		iBuf->dma_y += lower_height;
+		iBuf->vsync_enable = FALSE;
+		mdp_dma2_update_sub(mfd);
+	}
+}
+
+static void mdp_dma2_update_sub(struct msm_fb_data_type *mfd)
+#else
+void mdp_dma2_update(struct msm_fb_data_type *mfd)
+#endif
+{
+	unsigned long flag;
+
+	down(&mfd->dma->mutex);
+	if ((mfd) && (!mfd->dma->busy) && (mfd->panel_power_on)) {
+		down(&mfd->sem);
+		mfd->ibuf_flushed = TRUE;
+		mdp_dma2_update_lcd(mfd);
+
+		spin_lock_irqsave(&mdp_spin_lock, flag);
+		mdp_enable_irq(MDP_DMA2_TERM);
+		mfd->dma->busy = TRUE;
+		INIT_COMPLETION(mfd->dma->comp);
+
+		spin_unlock_irqrestore(&mdp_spin_lock, flag);
+		/* schedule DMA to start */
+		mdp_dma_schedule(mfd, MDP_DMA2_TERM);
+		up(&mfd->sem);
+
+		/* wait until DMA finishes the current job */
+		wait_for_completion_killable(&mfd->dma->comp);
+		mdp_disable_irq(MDP_DMA2_TERM);
+
+	/* signal if pan function is waiting for the update completion */
+		if (mfd->pan_waiting) {
+			mfd->pan_waiting = FALSE;
+			complete(&mfd->pan_comp);
+		}
+	}
+	up(&mfd->dma->mutex);
+}
+
+void mdp_lcd_update_workqueue_handler(struct work_struct *work)
+{
+	struct msm_fb_data_type *mfd = NULL;
+
+	mfd = container_of(work, struct msm_fb_data_type, dma_update_worker);
+	if (mfd)
+		mfd->dma_fnc(mfd);
+}
+
+void mdp_set_dma_pan_info(struct fb_info *info, struct mdp_dirty_region *dirty,
+			  boolean sync)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	struct fb_info *fbi = mfd->fbi;
+	MDPIBUF *iBuf;
+	int bpp = info->var.bits_per_pixel / 8;
+
+	down(&mfd->sem);
+
+	iBuf = &mfd->ibuf;
+
+	if (mfd->display_iova)
+		iBuf->buf = (uint8 *)mfd->display_iova;
+	else
+		iBuf->buf = (uint8 *) info->fix.smem_start;
+
+	iBuf->buf += calc_fb_offset(mfd, fbi, bpp);
+
+	iBuf->ibuf_width = info->var.xres_virtual;
+	iBuf->bpp = bpp;
+
+	iBuf->vsync_enable = sync;
+
+	if (dirty) {
+		/*
+		 * ToDo: dirty region check inside var.xoffset+xres
+		 * <-> var.yoffset+yres
+		 */
+		iBuf->dma_x = dirty->xoffset % info->var.xres;
+		iBuf->dma_y = dirty->yoffset % info->var.yres;
+		iBuf->dma_w = dirty->width;
+		iBuf->dma_h = dirty->height;
+	} else {
+		iBuf->dma_x = 0;
+		iBuf->dma_y = 0;
+		iBuf->dma_w = info->var.xres;
+		iBuf->dma_h = info->var.yres;
+	}
+	mfd->ibuf_flushed = FALSE;
+	up(&mfd->sem);
+}
+
+void mdp_dma_pan_update(struct fb_info *info)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	MDPIBUF *iBuf;
+
+	iBuf = &mfd->ibuf;
+
+	if (mfd->sw_currently_refreshing) {
+		/* we need to wait for the pending update */
+		mfd->pan_waiting = TRUE;
+		if (!mfd->ibuf_flushed) {
+			wait_for_completion_killable(&mfd->pan_comp);
+		}
+		/* waiting for this update to complete */
+		mfd->pan_waiting = TRUE;
+		wait_for_completion_killable(&mfd->pan_comp);
+	} else
+		mfd->dma_fnc(mfd);
+}
+
+void mdp_refresh_screen(unsigned long data)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)data;
+
+	if ((mfd->sw_currently_refreshing) && (mfd->sw_refreshing_enable)) {
+		init_timer(&mfd->refresh_timer);
+		mfd->refresh_timer.function = mdp_refresh_screen;
+		mfd->refresh_timer.data = data;
+
+		if (mfd->dma->busy)
+			/* come back in 1 msec */
+			mfd->refresh_timer.expires = jiffies + (HZ / 1000);
+		else
+			mfd->refresh_timer.expires =
+			    jiffies + mfd->refresh_timer_duration;
+
+		add_timer(&mfd->refresh_timer);
+
+		if (!mfd->dma->busy) {
+			if (!queue_work(mdp_dma_wq, &mfd->dma_update_worker)) {
+				MSM_FB_DEBUG("mdp_dma: can't queue_work! -> \
+			MDP/MDDI/LCD clock speed needs to be increased\n");
+			}
+		}
+	} else {
+		if (!mfd->hw_refresh)
+			complete(&mfd->refresher_comp);
+	}
+}
diff --git a/drivers/video/msm/mdp_dma_dsi_video.c b/drivers/video/msm/mdp_dma_dsi_video.c
new file mode 100644
index 0000000..1ba5b8d
--- /dev/null
+++ b/drivers/video/msm/mdp_dma_dsi_video.c
@@ -0,0 +1,275 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/semaphore.h>
+#include <linux/spinlock.h>
+#include <linux/fb.h>
+#include <asm/system.h>
+#include <mach/hardware.h>
+#include "mdp.h"
+#include "msm_fb.h"
+#include "mdp4.h"
+
+#define DSI_VIDEO_BASE	0xF0000
+#define DMA_P_BASE      0x90000
+
+static int first_pixel_start_x;
+static int first_pixel_start_y;
+
+int mdp_dsi_video_on(struct platform_device *pdev)
+{
+	int dsi_width;
+	int dsi_height;
+	int dsi_bpp;
+	int dsi_border_clr;
+	int dsi_underflow_clr;
+	int dsi_hsync_skew;
+
+	int hsync_period;
+	int hsync_ctrl;
+	int vsync_period;
+	int display_hctl;
+	int display_v_start;
+	int display_v_end;
+	int active_hctl;
+	int active_h_start;
+	int active_h_end;
+	int active_v_start;
+	int active_v_end;
+	int ctrl_polarity;
+	int h_back_porch;
+	int h_front_porch;
+	int v_back_porch;
+	int v_front_porch;
+	int hsync_pulse_width;
+	int vsync_pulse_width;
+	int hsync_polarity;
+	int vsync_polarity;
+	int data_en_polarity;
+	int hsync_start_x;
+	int hsync_end_x;
+	uint8 *buf;
+	uint32 dma2_cfg_reg;
+
+	int bpp;
+	struct fb_info *fbi;
+	struct fb_var_screeninfo *var;
+	struct msm_fb_data_type *mfd;
+	int ret;
+	uint32_t mask, curr;
+
+	mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
+
+	if (!mfd)
+		return -ENODEV;
+
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	fbi = mfd->fbi;
+	var = &fbi->var;
+
+	bpp = fbi->var.bits_per_pixel / 8;
+	buf = (uint8 *) fbi->fix.smem_start;
+
+	buf += calc_fb_offset(mfd, fbi, bpp);
+
+	dma2_cfg_reg = DMA_PACK_ALIGN_LSB | DMA_OUT_SEL_DSI_VIDEO;
+
+	if (mfd->fb_imgType == MDP_BGR_565)
+		dma2_cfg_reg |= DMA_PACK_PATTERN_BGR;
+	else if (mfd->fb_imgType == MDP_RGBA_8888)
+		dma2_cfg_reg |= DMA_PACK_PATTERN_BGR;
+	else
+		dma2_cfg_reg |= DMA_PACK_PATTERN_RGB;
+
+	if (bpp == 2)
+		dma2_cfg_reg |= DMA_IBUF_FORMAT_RGB565;
+	else if (bpp == 3)
+		dma2_cfg_reg |= DMA_IBUF_FORMAT_RGB888;
+	else
+		dma2_cfg_reg |= DMA_IBUF_FORMAT_xRGB8888_OR_ARGB8888;
+
+	switch (mfd->panel_info.bpp) {
+	case 24:
+		dma2_cfg_reg |= DMA_DSTC0G_8BITS |
+			DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS;
+		break;
+	case 18:
+		dma2_cfg_reg |= DMA_DSTC0G_6BITS |
+			DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
+		break;
+	case 16:
+		dma2_cfg_reg |= DMA_DSTC0G_6BITS |
+			DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS;
+		break;
+	default:
+		printk(KERN_ERR "mdp lcdc can't support format %d bpp!\n",
+			mfd->panel_info.bpp);
+		return -ENODEV;
+	}
+	/* MDP cmd block enable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	/* starting address */
+	MDP_OUTP(MDP_BASE + DMA_P_BASE + 0x8, (uint32) buf);
+
+	/* active window width and height */
+	MDP_OUTP(MDP_BASE + DMA_P_BASE + 0x4, ((fbi->var.yres) << 16) |
+		(fbi->var.xres));
+
+	/* buffer ystride */
+	MDP_OUTP(MDP_BASE + DMA_P_BASE + 0xc, fbi->fix.line_length);
+
+	/* x/y coordinate = always 0 for lcdc */
+	MDP_OUTP(MDP_BASE + DMA_P_BASE + 0x10, 0);
+
+	/* dma config */
+	curr = inpdw(MDP_BASE + DMA_P_BASE);
+	mask = 0x0FFFFFFF;
+	dma2_cfg_reg = (dma2_cfg_reg & mask) | (curr & ~mask);
+	MDP_OUTP(MDP_BASE + DMA_P_BASE, dma2_cfg_reg);
+
+	/*
+	 * DSI timing setting
+	 */
+	h_back_porch = var->left_margin;
+	h_front_porch = var->right_margin;
+	v_back_porch = var->upper_margin;
+	v_front_porch = var->lower_margin;
+	hsync_pulse_width = var->hsync_len;
+	vsync_pulse_width = var->vsync_len;
+	dsi_border_clr = mfd->panel_info.lcdc.border_clr;
+	dsi_underflow_clr = mfd->panel_info.lcdc.underflow_clr;
+	dsi_hsync_skew = mfd->panel_info.lcdc.hsync_skew;
+	dsi_width = mfd->panel_info.xres;
+	dsi_height = mfd->panel_info.yres;
+	dsi_bpp = mfd->panel_info.bpp;
+	hsync_period = h_back_porch + dsi_width + h_front_porch + 1;
+	hsync_ctrl = (hsync_period << 16) | hsync_pulse_width;
+	hsync_start_x = h_back_porch;
+	hsync_end_x = dsi_width + h_back_porch - 1;
+	display_hctl = (hsync_end_x << 16) | hsync_start_x;
+
+	vsync_period =
+		(v_back_porch + dsi_height + v_front_porch + 1) * hsync_period;
+	display_v_start = v_back_porch * hsync_period + dsi_hsync_skew;
+	display_v_end = (dsi_height + v_back_porch) * hsync_period;
+
+	active_h_start = hsync_start_x + first_pixel_start_x;
+	active_h_end = active_h_start + var->xres - 1;
+	active_hctl = ACTIVE_START_X_EN |
+			(active_h_end << 16) | active_h_start;
+
+	active_v_start = display_v_start +
+			first_pixel_start_y * hsync_period;
+	active_v_end = active_v_start +	(var->yres) * hsync_period - 1;
+	active_v_start |= ACTIVE_START_Y_EN;
+
+	dsi_underflow_clr |= 0x80000000;	/* enable recovery */
+	hsync_polarity = 0;
+	vsync_polarity = 0;
+	data_en_polarity = 0;
+
+	ctrl_polarity =	(data_en_polarity << 2) |
+		(vsync_polarity << 1) | (hsync_polarity);
+
+	MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x4, hsync_ctrl);
+	MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x8, vsync_period);
+	MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0xc, vsync_pulse_width);
+	MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x10, display_hctl);
+	MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x14, display_v_start);
+	MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x18, display_v_end);
+	MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x1c, active_hctl);
+	MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x20, active_v_start);
+	MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x24, active_v_end);
+	MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x28, dsi_border_clr);
+	MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x2c, dsi_underflow_clr);
+	MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x30, dsi_hsync_skew);
+	MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x38, ctrl_polarity);
+
+	ret = panel_next_on(pdev);
+	if (ret == 0) {
+		/* enable DSI block */
+		MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE, 1);
+		/*Turning on DMA_P block*/
+		mdp_pipe_ctrl(MDP_DMA2_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	}
+
+	/* MDP cmd block disable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	return ret;
+}
+
+int mdp_dsi_video_off(struct platform_device *pdev)
+{
+	int ret = 0;
+	/* MDP cmd block enable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE, 0);
+	/* MDP cmd block disable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+	/*Turning off DMA_P block*/
+	mdp_pipe_ctrl(MDP_DMA2_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	ret = panel_next_off(pdev);
+	/* delay to make sure the last frame finishes */
+	msleep(20);
+
+	return ret;
+}
+
+void mdp_dsi_video_update(struct msm_fb_data_type *mfd)
+{
+	struct fb_info *fbi = mfd->fbi;
+	uint8 *buf;
+	int bpp;
+	unsigned long flag;
+	int irq_block = MDP_DMA2_TERM;
+
+	if (!mfd->panel_power_on)
+		return;
+
+	down(&mfd->dma->mutex);
+
+	bpp = fbi->var.bits_per_pixel / 8;
+	buf = (uint8 *) fbi->fix.smem_start;
+
+	buf += calc_fb_offset(mfd, fbi, bpp);
+
+	/* no need to power on cmd block since it's dsi mode */
+	/* starting address */
+	MDP_OUTP(MDP_BASE + DMA_P_BASE + 0x8, (uint32) buf);
+	/* enable  irq */
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	mdp_enable_irq(irq_block);
+	INIT_COMPLETION(mfd->dma->comp);
+	mfd->dma->waiting = TRUE;
+
+	outp32(MDP_INTR_CLEAR, LCDC_FRAME_START);
+	mdp_intr_mask |= LCDC_FRAME_START;
+	outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+	wait_for_completion_killable(&mfd->dma->comp);
+	mdp_disable_irq(irq_block);
+	up(&mfd->dma->mutex);
+}
diff --git a/drivers/video/msm/mdp_dma_lcdc.c b/drivers/video/msm/mdp_dma_lcdc.c
new file mode 100644
index 0000000..c418e9c
--- /dev/null
+++ b/drivers/video/msm/mdp_dma_lcdc.c
@@ -0,0 +1,377 @@
+/* Copyright (c) 2008-2009, 2012 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/hrtimer.h>
+#include <linux/delay.h>
+#include <mach/hardware.h>
+#include <linux/io.h>
+
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <linux/semaphore.h>
+#include <linux/spinlock.h>
+
+#include <linux/fb.h>
+
+#include "mdp.h"
+#include "msm_fb.h"
+#include "mdp4.h"
+
+#ifdef CONFIG_FB_MSM_MDP40
+#define LCDC_BASE	0xC0000
+#define DTV_BASE	0xD0000
+#define DMA_E_BASE      0xB0000
+#else
+#define LCDC_BASE	0xE0000
+#endif
+
+#define DMA_P_BASE      0x90000
+
+extern spinlock_t mdp_spin_lock;
+#ifndef CONFIG_FB_MSM_MDP40
+extern uint32 mdp_intr_mask;
+#endif
+
+int first_pixel_start_x;
+int first_pixel_start_y;
+
+int mdp_lcdc_on(struct platform_device *pdev)
+{
+	int lcdc_width;
+	int lcdc_height;
+	int lcdc_bpp;
+	int lcdc_border_clr;
+	int lcdc_underflow_clr;
+	int lcdc_hsync_skew;
+
+	int hsync_period;
+	int hsync_ctrl;
+	int vsync_period;
+	int display_hctl;
+	int display_v_start;
+	int display_v_end;
+	int active_hctl;
+	int active_h_start;
+	int active_h_end;
+	int active_v_start;
+	int active_v_end;
+	int ctrl_polarity;
+	int h_back_porch;
+	int h_front_porch;
+	int v_back_porch;
+	int v_front_porch;
+	int hsync_pulse_width;
+	int vsync_pulse_width;
+	int hsync_polarity;
+	int vsync_polarity;
+	int data_en_polarity;
+	int hsync_start_x;
+	int hsync_end_x;
+	uint8 *buf;
+	int bpp;
+	uint32 dma2_cfg_reg;
+	struct fb_info *fbi;
+	struct fb_var_screeninfo *var;
+	struct msm_fb_data_type *mfd;
+	uint32 dma_base;
+	uint32 timer_base = LCDC_BASE;
+	uint32 block = MDP_DMA2_BLOCK;
+	int ret;
+	uint32_t mask, curr;
+
+	mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
+
+	if (!mfd)
+		return -ENODEV;
+
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	fbi = mfd->fbi;
+	var = &fbi->var;
+
+	/* MDP cmd block enable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	bpp = fbi->var.bits_per_pixel / 8;
+	buf = (uint8 *) fbi->fix.smem_start;
+
+	buf += calc_fb_offset(mfd, fbi, bpp);
+
+	dma2_cfg_reg = DMA_PACK_ALIGN_LSB | DMA_OUT_SEL_LCDC;
+
+	if (mfd->fb_imgType == MDP_BGR_565)
+		dma2_cfg_reg |= DMA_PACK_PATTERN_BGR;
+	else if (mfd->fb_imgType == MDP_RGBA_8888)
+		dma2_cfg_reg |= DMA_PACK_PATTERN_BGR;
+	else
+		dma2_cfg_reg |= DMA_PACK_PATTERN_RGB;
+
+	if (bpp == 2)
+		dma2_cfg_reg |= DMA_IBUF_FORMAT_RGB565;
+	else if (bpp == 3)
+		dma2_cfg_reg |= DMA_IBUF_FORMAT_RGB888;
+	else
+		dma2_cfg_reg |= DMA_IBUF_FORMAT_xRGB8888_OR_ARGB8888;
+
+	switch (mfd->panel_info.bpp) {
+	case 24:
+		dma2_cfg_reg |= DMA_DSTC0G_8BITS |
+		    DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS;
+		break;
+
+	case 18:
+		dma2_cfg_reg |= DMA_DSTC0G_6BITS |
+		    DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
+		break;
+
+	case 16:
+		dma2_cfg_reg |= DMA_DSTC0G_6BITS |
+		    DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS;
+		break;
+
+	default:
+		printk(KERN_ERR "mdp lcdc can't support format %d bpp!\n",
+		       mfd->panel_info.bpp);
+		return -ENODEV;
+	}
+
+	/* DMA register config */
+
+	dma_base = DMA_P_BASE;
+
+#ifdef CONFIG_FB_MSM_MDP40
+	if (mfd->panel.type == HDMI_PANEL)
+		dma_base = DMA_E_BASE;
+#endif
+
+	/* starting address */
+	MDP_OUTP(MDP_BASE + dma_base + 0x8, (uint32) buf);
+	/* active window width and height */
+	MDP_OUTP(MDP_BASE + dma_base + 0x4, ((fbi->var.yres) << 16) |
+						(fbi->var.xres));
+	/* buffer ystride */
+	MDP_OUTP(MDP_BASE + dma_base + 0xc, fbi->fix.line_length);
+	/* x/y coordinate = always 0 for lcdc */
+	MDP_OUTP(MDP_BASE + dma_base + 0x10, 0);
+	/* dma config */
+	curr = inpdw(MDP_BASE + DMA_P_BASE);
+	mask = 0x0FFFFFFF;
+	dma2_cfg_reg = (dma2_cfg_reg & mask) | (curr & ~mask);
+	MDP_OUTP(MDP_BASE + dma_base, dma2_cfg_reg);
+
+	/*
+	 * LCDC timing setting
+	 */
+	h_back_porch = var->left_margin;
+	h_front_porch = var->right_margin;
+	v_back_porch = var->upper_margin;
+	v_front_porch = var->lower_margin;
+	hsync_pulse_width = var->hsync_len;
+	vsync_pulse_width = var->vsync_len;
+	lcdc_border_clr = mfd->panel_info.lcdc.border_clr;
+	lcdc_underflow_clr = mfd->panel_info.lcdc.underflow_clr;
+	lcdc_hsync_skew = mfd->panel_info.lcdc.hsync_skew;
+
+	lcdc_width = mfd->panel_info.xres;
+	lcdc_height = mfd->panel_info.yres;
+	lcdc_bpp = mfd->panel_info.bpp;
+
+	hsync_period =
+	    hsync_pulse_width + h_back_porch + lcdc_width + h_front_porch;
+	hsync_ctrl = (hsync_period << 16) | hsync_pulse_width;
+	hsync_start_x = hsync_pulse_width + h_back_porch;
+	hsync_end_x = hsync_period - h_front_porch - 1;
+	display_hctl = (hsync_end_x << 16) | hsync_start_x;
+
+	vsync_period =
+	    (vsync_pulse_width + v_back_porch + lcdc_height +
+	     v_front_porch) * hsync_period;
+	display_v_start =
+	    (vsync_pulse_width + v_back_porch) * hsync_period + lcdc_hsync_skew;
+	display_v_end =
+	    vsync_period - (v_front_porch * hsync_period) + lcdc_hsync_skew - 1;
+
+	if (lcdc_width != var->xres) {
+		active_h_start = hsync_start_x + first_pixel_start_x;
+		active_h_end = active_h_start + var->xres - 1;
+		active_hctl =
+		    ACTIVE_START_X_EN | (active_h_end << 16) | active_h_start;
+	} else {
+		active_hctl = 0;
+	}
+
+	if (lcdc_height != var->yres) {
+		active_v_start =
+		    display_v_start + first_pixel_start_y * hsync_period;
+		active_v_end = active_v_start + (var->yres) * hsync_period - 1;
+		active_v_start |= ACTIVE_START_Y_EN;
+	} else {
+		active_v_start = 0;
+		active_v_end = 0;
+	}
+
+
+#ifdef CONFIG_FB_MSM_MDP40
+	if (mfd->panel.type == HDMI_PANEL) {
+		block = MDP_DMA_E_BLOCK;
+		timer_base = DTV_BASE;
+		hsync_polarity = 0;
+		vsync_polarity = 0;
+	} else {
+		hsync_polarity = 1;
+		vsync_polarity = 1;
+	}
+
+	lcdc_underflow_clr |= 0x80000000;	/* enable recovery */
+#else
+	hsync_polarity = 0;
+	vsync_polarity = 0;
+#endif
+	data_en_polarity = 0;
+
+	ctrl_polarity =
+	    (data_en_polarity << 2) | (vsync_polarity << 1) | (hsync_polarity);
+
+	MDP_OUTP(MDP_BASE + timer_base + 0x4, hsync_ctrl);
+	MDP_OUTP(MDP_BASE + timer_base + 0x8, vsync_period);
+	MDP_OUTP(MDP_BASE + timer_base + 0xc, vsync_pulse_width * hsync_period);
+	if (timer_base == LCDC_BASE) {
+		MDP_OUTP(MDP_BASE + timer_base + 0x10, display_hctl);
+		MDP_OUTP(MDP_BASE + timer_base + 0x14, display_v_start);
+		MDP_OUTP(MDP_BASE + timer_base + 0x18, display_v_end);
+		MDP_OUTP(MDP_BASE + timer_base + 0x28, lcdc_border_clr);
+		MDP_OUTP(MDP_BASE + timer_base + 0x2c, lcdc_underflow_clr);
+		MDP_OUTP(MDP_BASE + timer_base + 0x30, lcdc_hsync_skew);
+		MDP_OUTP(MDP_BASE + timer_base + 0x38, ctrl_polarity);
+		MDP_OUTP(MDP_BASE + timer_base + 0x1c, active_hctl);
+		MDP_OUTP(MDP_BASE + timer_base + 0x20, active_v_start);
+		MDP_OUTP(MDP_BASE + timer_base + 0x24, active_v_end);
+	} else {
+		MDP_OUTP(MDP_BASE + timer_base + 0x18, display_hctl);
+		MDP_OUTP(MDP_BASE + timer_base + 0x1c, display_v_start);
+		MDP_OUTP(MDP_BASE + timer_base + 0x20, display_v_end);
+		MDP_OUTP(MDP_BASE + timer_base + 0x40, lcdc_border_clr);
+		MDP_OUTP(MDP_BASE + timer_base + 0x44, lcdc_underflow_clr);
+		MDP_OUTP(MDP_BASE + timer_base + 0x48, lcdc_hsync_skew);
+		MDP_OUTP(MDP_BASE + timer_base + 0x50, ctrl_polarity);
+		MDP_OUTP(MDP_BASE + timer_base + 0x2c, active_hctl);
+		MDP_OUTP(MDP_BASE + timer_base + 0x30, active_v_start);
+		MDP_OUTP(MDP_BASE + timer_base + 0x38, active_v_end);
+	}
+
+	ret = panel_next_on(pdev);
+	if (ret == 0) {
+		/* enable LCDC block */
+		MDP_OUTP(MDP_BASE + timer_base, 1);
+		mdp_pipe_ctrl(block, MDP_BLOCK_POWER_ON, FALSE);
+	}
+	/* MDP cmd block disable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	return ret;
+}
+
+int mdp_lcdc_off(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct msm_fb_data_type *mfd;
+	uint32 timer_base = LCDC_BASE;
+	uint32 block = MDP_DMA2_BLOCK;
+
+	mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
+
+#ifdef CONFIG_FB_MSM_MDP40
+	if (mfd->panel.type == HDMI_PANEL) {
+		block = MDP_DMA_E_BLOCK;
+		timer_base = DTV_BASE;
+	}
+#endif
+
+	down(&mfd->dma->mutex);
+	/* MDP cmd block enable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	MDP_OUTP(MDP_BASE + timer_base, 0);
+	/* MDP cmd block disable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+	mdp_pipe_ctrl(block, MDP_BLOCK_POWER_OFF, FALSE);
+
+	ret = panel_next_off(pdev);
+	up(&mfd->dma->mutex);
+
+	/* delay to make sure the last frame finishes */
+	msleep(16);
+
+	return ret;
+}
+
+void mdp_lcdc_update(struct msm_fb_data_type *mfd)
+{
+	struct fb_info *fbi = mfd->fbi;
+	uint8 *buf;
+	int bpp;
+	unsigned long flag;
+	uint32 dma_base;
+	int irq_block = MDP_DMA2_TERM;
+#ifdef CONFIG_FB_MSM_MDP40
+	int intr = INTR_DMA_P_DONE;
+#endif
+
+	if (!mfd->panel_power_on)
+		return;
+
+	down(&mfd->dma->mutex);
+	/* no need to power on cmd block since it's lcdc mode */
+	bpp = fbi->var.bits_per_pixel / 8;
+	buf = (uint8 *) fbi->fix.smem_start;
+
+	buf += calc_fb_offset(mfd, fbi, bpp);
+
+	dma_base = DMA_P_BASE;
+
+#ifdef CONFIG_FB_MSM_MDP40
+	if (mfd->panel.type == HDMI_PANEL) {
+		intr = INTR_DMA_E_DONE;
+		irq_block = MDP_DMA_E_TERM;
+		dma_base = DMA_E_BASE;
+	}
+#endif
+
+	/* starting address */
+	MDP_OUTP(MDP_BASE + dma_base + 0x8, (uint32) buf);
+
+	/* enable LCDC irq */
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	mdp_enable_irq(irq_block);
+	INIT_COMPLETION(mfd->dma->comp);
+	mfd->dma->waiting = TRUE;
+#ifdef CONFIG_FB_MSM_MDP40
+	outp32(MDP_INTR_CLEAR, intr);
+	mdp_intr_mask |= intr;
+	outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+#else
+	outp32(MDP_INTR_CLEAR, LCDC_FRAME_START);
+	mdp_intr_mask |= LCDC_FRAME_START;
+	outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+#endif
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+	wait_for_completion_killable(&mfd->dma->comp);
+	mdp_disable_irq(irq_block);
+	up(&mfd->dma->mutex);
+}
diff --git a/drivers/video/msm/mdp_dma_s.c b/drivers/video/msm/mdp_dma_s.c
new file mode 100644
index 0000000..22d79be
--- /dev/null
+++ b/drivers/video/msm/mdp_dma_s.c
@@ -0,0 +1,166 @@
+/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/hrtimer.h>
+
+#include <mach/hardware.h>
+#include <linux/io.h>
+
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <linux/semaphore.h>
+#include <linux/spinlock.h>
+
+#include <linux/fb.h>
+
+#include "mdp.h"
+#include "msm_fb.h"
+
+static void mdp_dma_s_update_lcd(struct msm_fb_data_type *mfd)
+{
+	MDPIBUF *iBuf = &mfd->ibuf;
+	int mddi_dest = FALSE;
+	uint32 outBpp = iBuf->bpp;
+	uint32 dma_s_cfg_reg;
+	uint8 *src;
+	struct msm_fb_panel_data *pdata =
+	    (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data;
+
+	dma_s_cfg_reg = DMA_PACK_TIGHT | DMA_PACK_ALIGN_LSB |
+	    DMA_OUT_SEL_AHB | DMA_IBUF_NONCONTIGUOUS;
+
+	if (mfd->fb_imgType == MDP_BGR_565)
+		dma_s_cfg_reg |= DMA_PACK_PATTERN_BGR;
+	else
+		dma_s_cfg_reg |= DMA_PACK_PATTERN_RGB;
+
+	if (outBpp == 4)
+		dma_s_cfg_reg |= DMA_IBUF_C3ALPHA_EN;
+
+	if (outBpp == 2)
+		dma_s_cfg_reg |= DMA_IBUF_FORMAT_RGB565;
+
+	if (mfd->panel_info.pdest != DISPLAY_2) {
+		printk(KERN_ERR "error: non-secondary type through dma_s!\n");
+		return;
+	}
+
+	if (mfd->panel_info.type == MDDI_PANEL ||
+		mfd->panel_info.type == EXT_MDDI_PANEL) {
+		dma_s_cfg_reg |= DMA_OUT_SEL_MDDI;
+		mddi_dest = TRUE;
+	} else {
+		dma_s_cfg_reg |= DMA_AHBM_LCD_SEL_SECONDARY;
+		outp32(MDP_EBI2_LCD1, mfd->data_port_phys);
+	}
+
+	src = (uint8 *) iBuf->buf;
+	/* starting input address */
+	src += (iBuf->dma_x + iBuf->dma_y * iBuf->ibuf_width) * outBpp;
+
+	/* MDP cmd block enable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	/* PIXELSIZE */
+	if (mfd->panel_info.type == MDDI_PANEL) {
+		MDP_OUTP(MDP_BASE + 0xa0004,
+			(iBuf->dma_h << 16 | iBuf->dma_w));
+		MDP_OUTP(MDP_BASE + 0xa0008, src);	/* ibuf address */
+		MDP_OUTP(MDP_BASE + 0xa000c,
+			iBuf->ibuf_width * outBpp);/* ystride */
+	} else {
+		MDP_OUTP(MDP_BASE + 0xb0004,
+			(iBuf->dma_h << 16 | iBuf->dma_w));
+		MDP_OUTP(MDP_BASE + 0xb0008, src);	/* ibuf address */
+		MDP_OUTP(MDP_BASE + 0xb000c,
+			iBuf->ibuf_width * outBpp);/* ystride */
+	}
+
+	if (mfd->panel_info.bpp == 18) {
+		dma_s_cfg_reg |= DMA_DSTC0G_6BITS |	/* 666 18BPP */
+		    DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
+	} else {
+		dma_s_cfg_reg |= DMA_DSTC0G_6BITS |	/* 565 16BPP */
+		    DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS;
+	}
+
+	if (mddi_dest) {
+		if (mfd->panel_info.type == MDDI_PANEL) {
+			MDP_OUTP(MDP_BASE + 0xa0010,
+				(iBuf->dma_y << 16) | iBuf->dma_x);
+			MDP_OUTP(MDP_BASE + 0x00090, 1);
+		} else {
+			MDP_OUTP(MDP_BASE + 0xb0010,
+				(iBuf->dma_y << 16) | iBuf->dma_x);
+			MDP_OUTP(MDP_BASE + 0x00090, 2);
+		}
+		MDP_OUTP(MDP_BASE + 0x00094,
+				(MDDI_VDO_PACKET_DESC << 16) |
+				mfd->panel_info.mddi.vdopkt);
+	} else {
+		/* setting LCDC write window */
+		pdata->set_rect(iBuf->dma_x, iBuf->dma_y, iBuf->dma_w,
+				iBuf->dma_h);
+	}
+
+	if (mfd->panel_info.type == MDDI_PANEL)
+		MDP_OUTP(MDP_BASE + 0xa0000, dma_s_cfg_reg);
+	else
+		MDP_OUTP(MDP_BASE + 0xb0000, dma_s_cfg_reg);
+
+
+	/* MDP cmd block disable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+	if (mfd->panel_info.type == MDDI_PANEL)
+		mdp_pipe_kickoff(MDP_DMA_S_TERM, mfd);
+	else
+		mdp_pipe_kickoff(MDP_DMA_E_TERM, mfd);
+
+}
+
+void mdp_dma_s_update(struct msm_fb_data_type *mfd)
+{
+	down(&mfd->dma->mutex);
+	if ((mfd) && (!mfd->dma->busy) && (mfd->panel_power_on)) {
+		down(&mfd->sem);
+		mdp_enable_irq(MDP_DMA_S_TERM);
+		if (mfd->panel_info.type == MDDI_PANEL)
+			mdp_enable_irq(MDP_DMA_S_TERM);
+		else
+			mdp_enable_irq(MDP_DMA_E_TERM);
+		mfd->dma->busy = TRUE;
+		INIT_COMPLETION(mfd->dma->comp);
+		mfd->ibuf_flushed = TRUE;
+		mdp_dma_s_update_lcd(mfd);
+		up(&mfd->sem);
+
+		/* wait until DMA finishes the current job */
+		wait_for_completion_killable(&mfd->dma->comp);
+		if (mfd->panel_info.type == MDDI_PANEL)
+			mdp_disable_irq(MDP_DMA_S_TERM);
+		else
+			mdp_disable_irq(MDP_DMA_E_TERM);
+
+	/* signal if pan function is waiting for the update completion */
+		if (mfd->pan_waiting) {
+			mfd->pan_waiting = FALSE;
+			complete(&mfd->pan_comp);
+		}
+	}
+	up(&mfd->dma->mutex);
+}
diff --git a/drivers/video/msm/mdp_dma_tv.c b/drivers/video/msm/mdp_dma_tv.c
new file mode 100644
index 0000000..b578ba2
--- /dev/null
+++ b/drivers/video/msm/mdp_dma_tv.c
@@ -0,0 +1,140 @@
+/* Copyright (c) 2008-2009, 2012 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/hrtimer.h>
+#include <linux/delay.h>
+
+#include <mach/hardware.h>
+#include <linux/io.h>
+
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <linux/semaphore.h>
+#include <linux/spinlock.h>
+
+#include <linux/fb.h>
+
+#include "mdp.h"
+#include "msm_fb.h"
+
+extern spinlock_t mdp_spin_lock;
+extern uint32 mdp_intr_mask;
+
+int mdp_dma3_on(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+	struct fb_info *fbi;
+	uint8 *buf;
+	int bpp;
+	int ret = 0;
+
+	mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
+
+	if (!mfd)
+		return -ENODEV;
+
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	fbi = mfd->fbi;
+
+	/* MDP cmd block enable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	bpp = fbi->var.bits_per_pixel / 8;
+	buf = (uint8 *) fbi->fix.smem_start;
+
+	buf += calc_fb_offset(mfd, fbi, bpp);
+
+	/* starting address[31..8] of Video frame buffer is CS0 */
+	MDP_OUTP(MDP_BASE + 0xC0008, (uint32) buf >> 3);
+
+	mdp_pipe_ctrl(MDP_DMA3_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	MDP_OUTP(MDP_BASE + 0xC0004, 0x4c60674); /* flicker filter enabled */
+	MDP_OUTP(MDP_BASE + 0xC0010, 0x20);	/* sobel treshold */
+
+	MDP_OUTP(MDP_BASE + 0xC0018, 0xeb0010);	/* Y  Max, Y  min */
+	MDP_OUTP(MDP_BASE + 0xC001C, 0xf00010);	/* Cb Max, Cb min */
+	MDP_OUTP(MDP_BASE + 0xC0020, 0xf00010);	/* Cb Max, Cb min */
+
+	MDP_OUTP(MDP_BASE + 0xC000C, 0x67686970); /* add a few chars for CC */
+	MDP_OUTP(MDP_BASE + 0xC0000, 0x1);	/* MDP tv out enable */
+
+	/* MDP cmd block disable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	ret = panel_next_on(pdev);
+
+	return ret;
+}
+
+int mdp_dma3_off(struct platform_device *pdev)
+{
+	int ret = 0;
+
+	ret = panel_next_off(pdev);
+	if (ret)
+		return ret;
+
+	/* MDP cmd block enable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	MDP_OUTP(MDP_BASE + 0xC0000, 0x0);
+	/* MDP cmd block disable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	mdp_pipe_ctrl(MDP_DMA3_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	/* delay to make sure the last frame finishes */
+	msleep(16);
+
+	return ret;
+}
+
+void mdp_dma3_update(struct msm_fb_data_type *mfd)
+{
+	struct fb_info *fbi = mfd->fbi;
+	uint8 *buf;
+	int bpp;
+	unsigned long flag;
+
+	if (!mfd->panel_power_on)
+		return;
+
+	/* no need to power on cmd block since dma3 is running */
+	bpp = fbi->var.bits_per_pixel / 8;
+	buf = (uint8 *) fbi->fix.smem_start;
+
+	buf += calc_fb_offset(mfd, fbi, bpp);
+
+	MDP_OUTP(MDP_BASE + 0xC0008, (uint32) buf >> 3);
+
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	mdp_enable_irq(MDP_DMA3_TERM);
+	INIT_COMPLETION(mfd->dma->comp);
+	mfd->dma->waiting = TRUE;
+
+	outp32(MDP_INTR_CLEAR, TV_OUT_DMA3_START);
+	mdp_intr_mask |= TV_OUT_DMA3_START;
+	outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+
+	wait_for_completion_killable(&mfd->dma->comp);
+	mdp_disable_irq(MDP_DMA3_TERM);
+}
diff --git a/drivers/video/msm/mdp_hw.h b/drivers/video/msm/mdp_hw.h
index d804774..f35a757 100644
--- a/drivers/video/msm/mdp_hw.h
+++ b/drivers/video/msm/mdp_hw.h
@@ -15,20 +15,61 @@
 #ifndef _MDP_HW_H_
 #define _MDP_HW_H_
 
+#include <linux/platform_device.h>
+#include <linux/wait.h>
 #include <mach/msm_iomap.h>
 #include <mach/msm_fb.h>
 
+typedef void (*mdp_dma_start_func_t)(void *private_data, uint32_t addr,
+				     uint32_t stride, uint32_t width,
+				     uint32_t height, uint32_t x, uint32_t y);
+
+struct mdp_out_interface {
+	uint32_t		registered:1;
+	void			*priv;
+
+	/* If the interface client wants to get DMA_DONE events */
+	uint32_t		dma_mask;
+	mdp_dma_start_func_t	dma_start;
+
+	struct msmfb_callback	*dma_cb;
+	wait_queue_head_t	dma_waitqueue;
+
+	/* If the interface client wants to be notified of non-DMA irqs,
+	 * e.g. LCDC/TV-out frame start */
+	uint32_t		irq_mask;
+	struct msmfb_callback	*irq_cb;
+};
+
 struct mdp_info {
+	spinlock_t lock;
 	struct mdp_device mdp_dev;
 	char * __iomem base;
 	int irq;
+	struct clk *clk;
+	struct clk *ebi1_clk;
+	struct mdp_out_interface out_if[MSM_MDP_NUM_INTERFACES];
+	int format;
+	int pack_pattern;
+	bool dma_config_dirty;
 };
+
+extern int mdp_out_if_register(struct mdp_device *mdp_dev, int interface,
+			       void *private_data, uint32_t dma_mask,
+			       mdp_dma_start_func_t dma_start);
+
+extern int mdp_out_if_req_irq(struct mdp_device *mdp_dev, int interface,
+			      uint32_t mask, struct msmfb_callback *cb);
+
 struct mdp_blit_req;
 struct mdp_device;
 int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req,
 		 struct file *src_file, unsigned long src_start,
 		 unsigned long src_len, struct file *dst_file,
 		 unsigned long dst_start, unsigned long dst_len);
+
+void mdp_ppp_dump_debug(const struct mdp_info *mdp);
+
 #define mdp_writel(mdp, value, offset) writel(value, mdp->base + offset)
 #define mdp_readl(mdp, offset) readl(mdp->base + offset)
 
@@ -48,10 +89,18 @@
 #define MDP_DISPLAY_STATUS               (0x00038)
 #define MDP_EBI2_LCD0                    (0x0003c)
 #define MDP_EBI2_LCD1                    (0x00040)
+#define MDP_EBI2_PORTMAP_MODE            (0x0005c)
+
+#ifndef CONFIG_MSM_MDP31
 #define MDP_DISPLAY0_ADDR                (0x00054)
 #define MDP_DISPLAY1_ADDR                (0x00058)
-#define MDP_EBI2_PORTMAP_MODE            (0x0005c)
-#define MDP_MODE                         (0x00060)
+#define MDP_PPP_CMD_MODE                 (0x00060)
+#else
+#define MDP_DISPLAY0_ADDR                (0x10000)
+#define MDP_DISPLAY1_ADDR                (0x10004)
+#define MDP_PPP_CMD_MODE                 (0x10060)
+#endif
+
 #define MDP_TV_OUT_STATUS                (0x00064)
 #define MDP_HW_VERSION                   (0x00070)
 #define MDP_SW_RESET                     (0x00074)
@@ -61,6 +110,8 @@
 #define MDP_SECONDARY_VSYNC_OUT_CTRL     (0x00084)
 #define MDP_EXTERNAL_VSYNC_OUT_CTRL      (0x00088)
 #define MDP_VSYNC_CTRL                   (0x0008c)
+#define MDP_MDDI_PARAM_WR_SEL            (0x00090)
+#define MDP_MDDI_PARAM                   (0x00094)
 #define MDP_CGC_EN                       (0x00100)
 #define MDP_CMD_STATUS                   (0x10008)
 #define MDP_PROFILE_EN                   (0x10010)
@@ -107,6 +158,7 @@
 #define MDP_FULL_BYPASS_WORD35           (0x1018c)
 #define MDP_FULL_BYPASS_WORD37           (0x10194)
 #define MDP_FULL_BYPASS_WORD39           (0x1019c)
+#define MDP_PPP_OUT_XY                   (0x1019c)
 #define MDP_FULL_BYPASS_WORD40           (0x101a0)
 #define MDP_FULL_BYPASS_WORD41           (0x101a4)
 #define MDP_FULL_BYPASS_WORD43           (0x101ac)
@@ -129,11 +181,27 @@
 #define MDP_FULL_BYPASS_WORD61           (0x101f4)
 #define MDP_FULL_BYPASS_WORD62           (0x101f8)
 #define MDP_FULL_BYPASS_WORD63           (0x101fc)
+
+#ifdef CONFIG_MSM_MDP31
+#define MDP_PPP_SRC_XY                   (0x10200)
+#define MDP_PPP_BG_XY                    (0x10204)
+#define MDP_PPP_SRC_IMAGE_SIZE           (0x10208)
+#define MDP_PPP_BG_IMAGE_SIZE            (0x1020c)
+#define MDP_PPP_SCALE_CONFIG             (0x10230)
+#define MDP_PPP_CSC_CONFIG               (0x10240)
+#define MDP_PPP_BLEND_BG_ALPHA_SEL       (0x70010)
+#endif
+
 #define MDP_TFETCH_TEST_MODE             (0x20004)
 #define MDP_TFETCH_STATUS                (0x20008)
 #define MDP_TFETCH_TILE_COUNT            (0x20010)
 #define MDP_TFETCH_FETCH_COUNT           (0x20014)
 #define MDP_TFETCH_CONSTANT_COLOR        (0x20040)
+#define MDP_BGTFETCH_TEST_MODE           (0x28004)
+#define MDP_BGTFETCH_STATUS              (0x28008)
+#define MDP_BGTFETCH_TILE_COUNT          (0x28010)
+#define MDP_BGTFETCH_FETCH_COUNT         (0x28014)
+#define MDP_BGTFETCH_CONSTANT_COLOR      (0x28040)
 #define MDP_CSC_BYPASS                   (0x40004)
 #define MDP_SCALE_COEFF_LSB              (0x5fffc)
 #define MDP_TV_OUT_CTL                   (0xc0000)
@@ -158,55 +226,49 @@
 #define MDP_TEST_MISR_CURR_VAL_DCLK      (0xd020c)
 #define MDP_TEST_CAPTURED_DCLK           (0xd0210)
 #define MDP_TEST_MISR_CAPT_VAL_DCLK      (0xd0214)
-#define MDP_LCDC_CTL                     (0xe0000)
+
+#define MDP_DMA_P_START                  (0x00044)
+#define MDP_DMA_P_CONFIG                 (0x90000)
+#define MDP_DMA_P_SIZE                   (0x90004)
+#define MDP_DMA_P_IBUF_ADDR              (0x90008)
+#define MDP_DMA_P_IBUF_Y_STRIDE          (0x9000c)
+#define MDP_DMA_P_OUT_XY                 (0x90010)
+#define MDP_DMA_P_COLOR_CORRECT_CONFIG   (0x90070)
+
+#define MDP_LCDC_EN                      (0xe0000)
 #define MDP_LCDC_HSYNC_CTL               (0xe0004)
-#define MDP_LCDC_VSYNC_CTL               (0xe0008)
-#define MDP_LCDC_ACTIVE_HCTL             (0xe000c)
-#define MDP_LCDC_ACTIVE_VCTL             (0xe0010)
-#define MDP_LCDC_BORDER_CLR              (0xe0014)
-#define MDP_LCDC_H_BLANK                 (0xe0018)
-#define MDP_LCDC_V_BLANK                 (0xe001c)
-#define MDP_LCDC_UNDERFLOW_CLR           (0xe0020)
-#define MDP_LCDC_HSYNC_SKEW              (0xe0024)
-#define MDP_LCDC_TEST_CTL                (0xe0028)
-#define MDP_LCDC_LINE_IRQ                (0xe002c)
-#define MDP_LCDC_CTL_POLARITY            (0xe0030)
-#define MDP_LCDC_DMA_CONFIG              (0xe1000)
-#define MDP_LCDC_DMA_SIZE                (0xe1004)
-#define MDP_LCDC_DMA_IBUF_ADDR           (0xe1008)
-#define MDP_LCDC_DMA_IBUF_Y_STRIDE       (0xe100c)
+#define MDP_LCDC_VSYNC_PERIOD            (0xe0008)
+#define MDP_LCDC_VSYNC_PULSE_WIDTH       (0xe000c)
+#define MDP_LCDC_DISPLAY_HCTL            (0xe0010)
+#define MDP_LCDC_DISPLAY_V_START         (0xe0014)
+#define MDP_LCDC_DISPLAY_V_END           (0xe0018)
+#define MDP_LCDC_ACTIVE_HCTL             (0xe001c)
+#define MDP_LCDC_ACTIVE_V_START          (0xe0020)
+#define MDP_LCDC_ACTIVE_V_END            (0xe0024)
+#define MDP_LCDC_BORDER_CLR              (0xe0028)
+#define MDP_LCDC_UNDERFLOW_CTL           (0xe002c)
+#define MDP_LCDC_HSYNC_SKEW              (0xe0030)
+#define MDP_LCDC_TEST_CTL                (0xe0034)
+#define MDP_LCDC_CTL_POLARITY            (0xe0038)
 
+#define MDP_PPP_SCALE_STATUS             (0x50000)
+#define MDP_PPP_BLEND_STATUS             (0x70000)
 
-#define MDP_DMA2_TERM 0x1
-#define MDP_DMA3_TERM 0x2
-#define MDP_PPP_TERM 0x3
+/* MDP_SW_RESET */
+#define MDP_PPP_SW_RESET                (1<<4)
 
 /* MDP_INTR_ENABLE */
-#define DL0_ROI_DONE           (1<<0)
-#define DL1_ROI_DONE           (1<<1)
-#define DL0_DMA2_TERM_DONE     (1<<2)
-#define DL1_DMA2_TERM_DONE     (1<<3)
-#define DL0_PPP_TERM_DONE      (1<<4)
-#define DL1_PPP_TERM_DONE      (1<<5)
-#define TV_OUT_DMA3_DONE       (1<<6)
-#define TV_ENC_UNDERRUN        (1<<7)
-#define DL0_FETCH_DONE         (1<<11)
-#define DL1_FETCH_DONE         (1<<12)
+#define DL0_ROI_DONE			(1<<0)
+#define TV_OUT_DMA3_DONE		(1<<6)
+#define TV_ENC_UNDERRUN			(1<<7)
 
-#define MDP_PPP_BUSY_STATUS (DL0_ROI_DONE| \
-			   DL1_ROI_DONE| \
-			   DL0_PPP_TERM_DONE| \
-			   DL1_PPP_TERM_DONE)
-
-#define MDP_ANY_INTR_MASK (DL0_ROI_DONE| \
-			   DL1_ROI_DONE| \
-			   DL0_DMA2_TERM_DONE| \
-			   DL1_DMA2_TERM_DONE| \
-			   DL0_PPP_TERM_DONE| \
-			   DL1_PPP_TERM_DONE| \
-			   DL0_FETCH_DONE| \
-			   DL1_FETCH_DONE| \
-			   TV_ENC_UNDERRUN)
+#ifdef CONFIG_MSM_MDP22
+#define MDP_DMA_P_DONE			(1 << 2)
+#else /* CONFIG_MSM_MDP31 */
+#define MDP_DMA_P_DONE			(1 << 14)
+#define MDP_LCDC_UNDERFLOW		(1 << 16)
+#define MDP_LCDC_FRAME_START		(1 << 15)
+#endif
 
 #define MDP_TOP_LUMA       16
 #define MDP_TOP_CHROMA     0
@@ -316,7 +378,12 @@
 #define PPP_OP_SCALE_X_ON (1<<0)
 #define PPP_OP_SCALE_Y_ON (1<<1)
 
+#ifndef CONFIG_MSM_MDP31
 #define PPP_OP_CONVERT_RGB2YCBCR 0
+#else
+#define PPP_OP_CONVERT_RGB2YCBCR (1<<30)
+#endif
+
 #define PPP_OP_CONVERT_YCBCR2RGB (1<<2)
 #define PPP_OP_CONVERT_ON (1<<3)
 
@@ -372,6 +439,13 @@
 #define PPP_OP_BG_CHROMA_SITE_COSITE 0
 #define PPP_OP_BG_CHROMA_SITE_OFFSITE (1<<27)
 
+#define PPP_BLEND_BG_USE_ALPHA_SEL      (1 << 0)
+#define PPP_BLEND_BG_ALPHA_REVERSE      (1 << 3)
+#define PPP_BLEND_BG_SRCPIXEL_ALPHA     (0 << 1)
+#define PPP_BLEND_BG_DSTPIXEL_ALPHA     (1 << 1)
+#define PPP_BLEND_BG_CONSTANT_ALPHA     (2 << 1)
+#define PPP_BLEND_BG_CONST_ALPHA_VAL(x) ((x) << 24)
+
 /* MDP_PPP_DESTINATION_CONFIG / MDP_FULL_BYPASS_WORD20 */
 #define PPP_DST_C0G_8BIT ((1<<0)|(1<<1))
 #define PPP_DST_C1B_8BIT ((1<<3)|(1<<2))
@@ -589,20 +663,71 @@
 #define PPP_ADDR_BG_CFG			MDP_FULL_BYPASS_WORD53
 #define PPP_ADDR_BG_PACK_PATTERN	MDP_FULL_BYPASS_WORD54
 
+/* color conversion matrix configuration registers */
+/* pfmv is mv1, prmv is mv2 */
+#define MDP_CSC_PFMVn(n)		(0x40400 + (4 * (n)))
+#define MDP_CSC_PRMVn(n)		(0x40440 + (4 * (n)))
+
+#ifdef CONFIG_MSM_MDP31
+#define MDP_PPP_CSC_PRE_BV1n(n)		(0x40500 + (4 * (n)))
+#define MDP_PPP_CSC_PRE_BV2n(n)		(0x40540 + (4 * (n)))
+#define MDP_PPP_CSC_POST_BV1n(n)	(0x40580 + (4 * (n)))
+#define MDP_PPP_CSC_POST_BV2n(n)	(0x405c0 + (4 * (n)))
+
+#define MDP_PPP_CSC_PRE_LV1n(n)		(0x40600 + (4 * (n)))
+#define MDP_PPP_CSC_PRE_LV2n(n)		(0x40640 + (4 * (n)))
+#define MDP_PPP_CSC_POST_LV1n(n)	(0x40680 + (4 * (n)))
+#define MDP_PPP_CSC_POST_LV2n(n)	(0x406c0 + (4 * (n)))
+
+#define MDP_PPP_SCALE_COEFF_D0_SET	(0)
+#define MDP_PPP_SCALE_COEFF_D1_SET	(1)
+#define MDP_PPP_SCALE_COEFF_D2_SET	(2)
+#define MDP_PPP_SCALE_COEFF_U1_SET	(3)
+#define MDP_PPP_SCALE_COEFF_LSBn(n)	(0x50400 + (8 * (n)))
+#define MDP_PPP_SCALE_COEFF_MSBn(n)	(0x50404 + (8 * (n)))
+
+#define MDP_PPP_DEINT_COEFFn(n)		(0x30010 + (4 * (n)))
+
+#define MDP_PPP_SCALER_FIR		(0)
+#define MDP_PPP_SCALER_MN		(1)
+
+#else /* !defined(CONFIG_MSM_MDP31) */
+
+#define MDP_CSC_PBVn(n)			(0x40500 + (4 * (n)))
+#define MDP_CSC_SBVn(n)			(0x40540 + (4 * (n)))
+#define MDP_CSC_PLVn(n)			(0x40580 + (4 * (n)))
+#define MDP_CSC_SLVn(n)			(0x405c0 + (4 * (n)))
+
+#endif
+
+
 /* MDP_DMA_CONFIG / MDP_FULL_BYPASS_WORD32 */
-#define DMA_DSTC0G_6BITS (1<<1)
-#define DMA_DSTC1B_6BITS (1<<3)
-#define DMA_DSTC2R_6BITS (1<<5)
 #define DMA_DSTC0G_5BITS (1<<0)
 #define DMA_DSTC1B_5BITS (1<<2)
 #define DMA_DSTC2R_5BITS (1<<4)
 
+#define DMA_DSTC0G_6BITS (2<<0)
+#define DMA_DSTC1B_6BITS (2<<2)
+#define DMA_DSTC2R_6BITS (2<<4)
+
+#define DMA_DSTC0G_8BITS (3<<0)
+#define DMA_DSTC1B_8BITS (3<<2)
+#define DMA_DSTC2R_8BITS (3<<4)
+
+#define DMA_DST_BITS_MASK 0x3F
+
 #define DMA_PACK_TIGHT (1<<6)
 #define DMA_PACK_LOOSE 0
 #define DMA_PACK_ALIGN_LSB 0
 #define DMA_PACK_ALIGN_MSB (1<<7)
+#define DMA_PACK_PATTERN_MASK (0x3f<<8)
 #define DMA_PACK_PATTERN_RGB \
 	(MDP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 2)<<8)
+#define DMA_PACK_PATTERN_BGR \
+	(MDP_GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 2)<<8)
+
+
+#ifdef CONFIG_MSM_MDP22
 
 #define DMA_OUT_SEL_AHB  0
 #define DMA_OUT_SEL_MDDI (1<<14)
@@ -610,16 +735,32 @@
 #define DMA_AHBM_LCD_SEL_SECONDARY (1<<15)
 #define DMA_IBUF_C3ALPHA_EN (1<<16)
 #define DMA_DITHER_EN (1<<17)
-
 #define DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY 0
 #define DMA_MDDI_DMAOUT_LCD_SEL_SECONDARY (1<<18)
 #define DMA_MDDI_DMAOUT_LCD_SEL_EXTERNAL (1<<19)
-
 #define DMA_IBUF_FORMAT_RGB565 (1<<20)
 #define DMA_IBUF_FORMAT_RGB888_OR_ARGB8888 0
-
+#define DMA_IBUF_FORMAT_MASK (1 << 20)
 #define DMA_IBUF_NONCONTIGUOUS (1<<21)
 
+#else /* CONFIG_MSM_MDP31 */
+
+#define DMA_OUT_SEL_AHB				(0 << 19)
+#define DMA_OUT_SEL_MDDI			(1 << 19)
+#define DMA_OUT_SEL_LCDC			(2 << 19)
+#define DMA_OUT_SEL_LCDC_MDDI			(3 << 19)
+#define DMA_DITHER_EN				(1 << 24)
+#define DMA_IBUF_FORMAT_RGB888			(0 << 25)
+#define DMA_IBUF_FORMAT_RGB565			(1 << 25)
+#define DMA_IBUF_FORMAT_XRGB8888		(2 << 25)
+#define DMA_IBUF_FORMAT_MASK			(3 << 25)
+#define DMA_IBUF_NONCONTIGUOUS			(0)
+
+#define DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY		(0)
+#define DMA_MDDI_DMAOUT_LCD_SEL_SECONDARY	(0)
+#define DMA_MDDI_DMAOUT_LCD_SEL_EXTERNAL	(0)
+#endif
+
 /* MDDI REGISTER ? */
 #define MDDI_VDO_PACKET_DESC  0x5666
 #define MDDI_VDO_PACKET_PRIM  0xC3
diff --git a/drivers/video/msm/mdp_hw40.c b/drivers/video/msm/mdp_hw40.c
new file mode 100644
index 0000000..a642c9b
--- /dev/null
+++ b/drivers/video/msm/mdp_hw40.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ * Author: Dima Zavin <dima@android.com>
+ *
+ * Based on code from Code Aurora Forum.
+ *
+ * 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/clk.h>
+#include <linux/io.h>
+
+#include "mdp_hw.h"
+
+static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride,
+			    uint32_t width, uint32_t height, uint32_t x,
+			    uint32_t y)
+{
+	struct mdp_info *mdp = priv;
+	uint32_t dma2_cfg;
+	uint16_t ld_param = 0; /* 0=PRIM, 1=SECD, 2=EXT */
+
+	dma2_cfg = DMA_PACK_TIGHT |
+		DMA_PACK_ALIGN_LSB;
+
+	dma2_cfg |= mdp->dma_format;
+	dma2_cfg |= mdp->dma_pack_pattern;
+	dma2_cfg |= DMA_DITHER_EN;
+
+	/* 666 18BPP */
+	dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
+
+	/* setup size, address, and stride */
+	mdp_writel(mdp, (height << 16) | (width), MDP_DMA_P_SIZE);
+	mdp_writel(mdp, addr, MDP_DMA_P_IBUF_ADDR);
+	mdp_writel(mdp, stride, MDP_DMA_P_IBUF_Y_STRIDE);
+
+	/* set y & x offset and MDDI transaction parameters */
+	mdp_writel(mdp, (y << 16) | (x), MDP_DMA_P_OUT_XY);
+	mdp_writel(mdp, ld_param, MDP_MDDI_PARAM_WR_SEL);
+	mdp_writel(mdp, (MDDI_VDO_PACKET_DESC << 16) | MDDI_VDO_PACKET_PRIM,
+		   MDP_MDDI_PARAM);
+
+	mdp_writel(mdp, 0x1, MDP_MDDI_DATA_XFR);
+	mdp_writel(mdp, dma2_cfg, MDP_DMA_P_CONFIG);
+	mdp_writel(mdp, 0, MDP_DMA_P_START);
+}
+
+int mdp_hw_init(struct mdp_info *mdp)
+{
+	int ret;
+
+	ret = mdp_out_if_register(&mdp->mdp_dev, MSM_MDDI_PMDH_INTERFACE, mdp,
+				  MDP_DMA_P_DONE, mdp_dma_to_mddi);
+	if (ret)
+		return ret;
+
+	mdp_writel(mdp, 0, MDP_INTR_ENABLE);
+	mdp_writel(mdp, 0, MDP_DMA_P_HIST_INTR_ENABLE);
+
+	/* XXX: why set this? QCT says it should be > mdp_pclk,
+	 * but they never set the clkrate of pclk */
+	clk_set_rate(mdp->clk, 122880000); /* 122.88 Mhz */
+	pr_info("%s: mdp_clk=%lu\n", __func__, clk_get_rate(mdp->clk));
+
+	/* TODO: Configure the VG/RGB pipes fetch data */
+
+	/* this should work for any mdp_clk freq. 
+	 * TODO: use different value for mdp_clk freqs >= 90Mhz */
+	mdp_writel(mdp, 0x27, MDP_DMA_P_FETCH_CFG); /* 8 bytes-burst x 8 req */
+
+	mdp_writel(mdp, 0x3, MDP_EBI2_PORTMAP_MODE);
+
+	/* 3 pending requests */
+	mdp_writel(mdp, 0x02222, MDP_MAX_RD_PENDING_CMD_CONFIG);
+
+	/* no overlay processing, sw controls everything */
+	mdp_writel(mdp, 0, MDP_LAYERMIXER_IN_CFG);
+	mdp_writel(mdp, 1 << 3, MDP_OVERLAYPROC0_CFG);
+	mdp_writel(mdp, 1 << 3, MDP_OVERLAYPROC1_CFG);
+
+	/* XXX: HACK! hardcode to do mddi on primary */
+	mdp_writel(mdp, 0x2, MDP_DISP_INTF_SEL);
+	return 0;
+}
+
diff --git a/drivers/video/msm/mdp_hw_init.c b/drivers/video/msm/mdp_hw_init.c
new file mode 100644
index 0000000..ff3ad41
--- /dev/null
+++ b/drivers/video/msm/mdp_hw_init.c
@@ -0,0 +1,714 @@
+/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "mdp.h"
+
+/* mdp primary csc limit vector */
+uint32 mdp_plv[] = { 0x10, 0xeb, 0x10, 0xf0 };
+
+/* Color Coefficient matrix for YUV -> RGB */
+struct mdp_ccs mdp_ccs_yuv2rgb = {
+	MDP_CCS_YUV2RGB,
+	{
+		0x254,
+		0x000,
+		0x331,
+		0x254,
+		0xff38,
+		0xfe61,
+		0x254,
+		0x409,
+		0x000,
+	},
+	{
+#ifdef CONFIG_FB_MSM_MDP31
+		0x1f0,
+		0x180,
+		0x180
+#else
+		0x10,
+		0x80,
+		0x80
+#endif
+	}
+};
+
+/* Color Coefficient matrix for RGB -> YUV */
+struct mdp_ccs mdp_ccs_rgb2yuv = {
+	MDP_CCS_RGB2YUV,
+	{
+		0x83,
+		0x102,
+		0x32,
+		0xffb5,
+		0xff6c,
+		0xe1,
+		0xe1,
+		0xff45,
+		0xffdc,
+	},
+#ifdef CONFIG_FB_MSM_MDP31
+	{
+		0x10,
+		0x80,
+		0x80
+	}
+#endif
+};
+
+static void mdp_load_lut_param(void)
+{
+	outpdw(MDP_BASE + 0x40800, 0x0);
+	outpdw(MDP_BASE + 0x40804, 0x151515);
+	outpdw(MDP_BASE + 0x40808, 0x1d1d1d);
+	outpdw(MDP_BASE + 0x4080c, 0x232323);
+	outpdw(MDP_BASE + 0x40810, 0x272727);
+	outpdw(MDP_BASE + 0x40814, 0x2b2b2b);
+	outpdw(MDP_BASE + 0x40818, 0x2f2f2f);
+	outpdw(MDP_BASE + 0x4081c, 0x333333);
+	outpdw(MDP_BASE + 0x40820, 0x363636);
+	outpdw(MDP_BASE + 0x40824, 0x393939);
+	outpdw(MDP_BASE + 0x40828, 0x3b3b3b);
+	outpdw(MDP_BASE + 0x4082c, 0x3e3e3e);
+	outpdw(MDP_BASE + 0x40830, 0x404040);
+	outpdw(MDP_BASE + 0x40834, 0x434343);
+	outpdw(MDP_BASE + 0x40838, 0x454545);
+	outpdw(MDP_BASE + 0x4083c, 0x474747);
+	outpdw(MDP_BASE + 0x40840, 0x494949);
+	outpdw(MDP_BASE + 0x40844, 0x4b4b4b);
+	outpdw(MDP_BASE + 0x40848, 0x4d4d4d);
+	outpdw(MDP_BASE + 0x4084c, 0x4f4f4f);
+	outpdw(MDP_BASE + 0x40850, 0x515151);
+	outpdw(MDP_BASE + 0x40854, 0x535353);
+	outpdw(MDP_BASE + 0x40858, 0x555555);
+	outpdw(MDP_BASE + 0x4085c, 0x565656);
+	outpdw(MDP_BASE + 0x40860, 0x585858);
+	outpdw(MDP_BASE + 0x40864, 0x5a5a5a);
+	outpdw(MDP_BASE + 0x40868, 0x5b5b5b);
+	outpdw(MDP_BASE + 0x4086c, 0x5d5d5d);
+	outpdw(MDP_BASE + 0x40870, 0x5e5e5e);
+	outpdw(MDP_BASE + 0x40874, 0x606060);
+	outpdw(MDP_BASE + 0x40878, 0x616161);
+	outpdw(MDP_BASE + 0x4087c, 0x636363);
+	outpdw(MDP_BASE + 0x40880, 0x646464);
+	outpdw(MDP_BASE + 0x40884, 0x666666);
+	outpdw(MDP_BASE + 0x40888, 0x676767);
+	outpdw(MDP_BASE + 0x4088c, 0x686868);
+	outpdw(MDP_BASE + 0x40890, 0x6a6a6a);
+	outpdw(MDP_BASE + 0x40894, 0x6b6b6b);
+	outpdw(MDP_BASE + 0x40898, 0x6c6c6c);
+	outpdw(MDP_BASE + 0x4089c, 0x6e6e6e);
+	outpdw(MDP_BASE + 0x408a0, 0x6f6f6f);
+	outpdw(MDP_BASE + 0x408a4, 0x707070);
+	outpdw(MDP_BASE + 0x408a8, 0x717171);
+	outpdw(MDP_BASE + 0x408ac, 0x727272);
+	outpdw(MDP_BASE + 0x408b0, 0x747474);
+	outpdw(MDP_BASE + 0x408b4, 0x757575);
+	outpdw(MDP_BASE + 0x408b8, 0x767676);
+	outpdw(MDP_BASE + 0x408bc, 0x777777);
+	outpdw(MDP_BASE + 0x408c0, 0x787878);
+	outpdw(MDP_BASE + 0x408c4, 0x797979);
+	outpdw(MDP_BASE + 0x408c8, 0x7a7a7a);
+	outpdw(MDP_BASE + 0x408cc, 0x7c7c7c);
+	outpdw(MDP_BASE + 0x408d0, 0x7d7d7d);
+	outpdw(MDP_BASE + 0x408d4, 0x7e7e7e);
+	outpdw(MDP_BASE + 0x408d8, 0x7f7f7f);
+	outpdw(MDP_BASE + 0x408dc, 0x808080);
+	outpdw(MDP_BASE + 0x408e0, 0x818181);
+	outpdw(MDP_BASE + 0x408e4, 0x828282);
+	outpdw(MDP_BASE + 0x408e8, 0x838383);
+	outpdw(MDP_BASE + 0x408ec, 0x848484);
+	outpdw(MDP_BASE + 0x408f0, 0x858585);
+	outpdw(MDP_BASE + 0x408f4, 0x868686);
+	outpdw(MDP_BASE + 0x408f8, 0x878787);
+	outpdw(MDP_BASE + 0x408fc, 0x888888);
+	outpdw(MDP_BASE + 0x40900, 0x898989);
+	outpdw(MDP_BASE + 0x40904, 0x8a8a8a);
+	outpdw(MDP_BASE + 0x40908, 0x8b8b8b);
+	outpdw(MDP_BASE + 0x4090c, 0x8c8c8c);
+	outpdw(MDP_BASE + 0x40910, 0x8d8d8d);
+	outpdw(MDP_BASE + 0x40914, 0x8e8e8e);
+	outpdw(MDP_BASE + 0x40918, 0x8f8f8f);
+	outpdw(MDP_BASE + 0x4091c, 0x8f8f8f);
+	outpdw(MDP_BASE + 0x40920, 0x909090);
+	outpdw(MDP_BASE + 0x40924, 0x919191);
+	outpdw(MDP_BASE + 0x40928, 0x929292);
+	outpdw(MDP_BASE + 0x4092c, 0x939393);
+	outpdw(MDP_BASE + 0x40930, 0x949494);
+	outpdw(MDP_BASE + 0x40934, 0x959595);
+	outpdw(MDP_BASE + 0x40938, 0x969696);
+	outpdw(MDP_BASE + 0x4093c, 0x969696);
+	outpdw(MDP_BASE + 0x40940, 0x979797);
+	outpdw(MDP_BASE + 0x40944, 0x989898);
+	outpdw(MDP_BASE + 0x40948, 0x999999);
+	outpdw(MDP_BASE + 0x4094c, 0x9a9a9a);
+	outpdw(MDP_BASE + 0x40950, 0x9b9b9b);
+	outpdw(MDP_BASE + 0x40954, 0x9c9c9c);
+	outpdw(MDP_BASE + 0x40958, 0x9c9c9c);
+	outpdw(MDP_BASE + 0x4095c, 0x9d9d9d);
+	outpdw(MDP_BASE + 0x40960, 0x9e9e9e);
+	outpdw(MDP_BASE + 0x40964, 0x9f9f9f);
+	outpdw(MDP_BASE + 0x40968, 0xa0a0a0);
+	outpdw(MDP_BASE + 0x4096c, 0xa0a0a0);
+	outpdw(MDP_BASE + 0x40970, 0xa1a1a1);
+	outpdw(MDP_BASE + 0x40974, 0xa2a2a2);
+	outpdw(MDP_BASE + 0x40978, 0xa3a3a3);
+	outpdw(MDP_BASE + 0x4097c, 0xa4a4a4);
+	outpdw(MDP_BASE + 0x40980, 0xa4a4a4);
+	outpdw(MDP_BASE + 0x40984, 0xa5a5a5);
+	outpdw(MDP_BASE + 0x40988, 0xa6a6a6);
+	outpdw(MDP_BASE + 0x4098c, 0xa7a7a7);
+	outpdw(MDP_BASE + 0x40990, 0xa7a7a7);
+	outpdw(MDP_BASE + 0x40994, 0xa8a8a8);
+	outpdw(MDP_BASE + 0x40998, 0xa9a9a9);
+	outpdw(MDP_BASE + 0x4099c, 0xaaaaaa);
+	outpdw(MDP_BASE + 0x409a0, 0xaaaaaa);
+	outpdw(MDP_BASE + 0x409a4, 0xababab);
+	outpdw(MDP_BASE + 0x409a8, 0xacacac);
+	outpdw(MDP_BASE + 0x409ac, 0xadadad);
+	outpdw(MDP_BASE + 0x409b0, 0xadadad);
+	outpdw(MDP_BASE + 0x409b4, 0xaeaeae);
+	outpdw(MDP_BASE + 0x409b8, 0xafafaf);
+	outpdw(MDP_BASE + 0x409bc, 0xafafaf);
+	outpdw(MDP_BASE + 0x409c0, 0xb0b0b0);
+	outpdw(MDP_BASE + 0x409c4, 0xb1b1b1);
+	outpdw(MDP_BASE + 0x409c8, 0xb2b2b2);
+	outpdw(MDP_BASE + 0x409cc, 0xb2b2b2);
+	outpdw(MDP_BASE + 0x409d0, 0xb3b3b3);
+	outpdw(MDP_BASE + 0x409d4, 0xb4b4b4);
+	outpdw(MDP_BASE + 0x409d8, 0xb4b4b4);
+	outpdw(MDP_BASE + 0x409dc, 0xb5b5b5);
+	outpdw(MDP_BASE + 0x409e0, 0xb6b6b6);
+	outpdw(MDP_BASE + 0x409e4, 0xb6b6b6);
+	outpdw(MDP_BASE + 0x409e8, 0xb7b7b7);
+	outpdw(MDP_BASE + 0x409ec, 0xb8b8b8);
+	outpdw(MDP_BASE + 0x409f0, 0xb8b8b8);
+	outpdw(MDP_BASE + 0x409f4, 0xb9b9b9);
+	outpdw(MDP_BASE + 0x409f8, 0xbababa);
+	outpdw(MDP_BASE + 0x409fc, 0xbababa);
+	outpdw(MDP_BASE + 0x40a00, 0xbbbbbb);
+	outpdw(MDP_BASE + 0x40a04, 0xbcbcbc);
+	outpdw(MDP_BASE + 0x40a08, 0xbcbcbc);
+	outpdw(MDP_BASE + 0x40a0c, 0xbdbdbd);
+	outpdw(MDP_BASE + 0x40a10, 0xbebebe);
+	outpdw(MDP_BASE + 0x40a14, 0xbebebe);
+	outpdw(MDP_BASE + 0x40a18, 0xbfbfbf);
+	outpdw(MDP_BASE + 0x40a1c, 0xc0c0c0);
+	outpdw(MDP_BASE + 0x40a20, 0xc0c0c0);
+	outpdw(MDP_BASE + 0x40a24, 0xc1c1c1);
+	outpdw(MDP_BASE + 0x40a28, 0xc1c1c1);
+	outpdw(MDP_BASE + 0x40a2c, 0xc2c2c2);
+	outpdw(MDP_BASE + 0x40a30, 0xc3c3c3);
+	outpdw(MDP_BASE + 0x40a34, 0xc3c3c3);
+	outpdw(MDP_BASE + 0x40a38, 0xc4c4c4);
+	outpdw(MDP_BASE + 0x40a3c, 0xc5c5c5);
+	outpdw(MDP_BASE + 0x40a40, 0xc5c5c5);
+	outpdw(MDP_BASE + 0x40a44, 0xc6c6c6);
+	outpdw(MDP_BASE + 0x40a48, 0xc6c6c6);
+	outpdw(MDP_BASE + 0x40a4c, 0xc7c7c7);
+	outpdw(MDP_BASE + 0x40a50, 0xc8c8c8);
+	outpdw(MDP_BASE + 0x40a54, 0xc8c8c8);
+	outpdw(MDP_BASE + 0x40a58, 0xc9c9c9);
+	outpdw(MDP_BASE + 0x40a5c, 0xc9c9c9);
+	outpdw(MDP_BASE + 0x40a60, 0xcacaca);
+	outpdw(MDP_BASE + 0x40a64, 0xcbcbcb);
+	outpdw(MDP_BASE + 0x40a68, 0xcbcbcb);
+	outpdw(MDP_BASE + 0x40a6c, 0xcccccc);
+	outpdw(MDP_BASE + 0x40a70, 0xcccccc);
+	outpdw(MDP_BASE + 0x40a74, 0xcdcdcd);
+	outpdw(MDP_BASE + 0x40a78, 0xcecece);
+	outpdw(MDP_BASE + 0x40a7c, 0xcecece);
+	outpdw(MDP_BASE + 0x40a80, 0xcfcfcf);
+	outpdw(MDP_BASE + 0x40a84, 0xcfcfcf);
+	outpdw(MDP_BASE + 0x40a88, 0xd0d0d0);
+	outpdw(MDP_BASE + 0x40a8c, 0xd0d0d0);
+	outpdw(MDP_BASE + 0x40a90, 0xd1d1d1);
+	outpdw(MDP_BASE + 0x40a94, 0xd2d2d2);
+	outpdw(MDP_BASE + 0x40a98, 0xd2d2d2);
+	outpdw(MDP_BASE + 0x40a9c, 0xd3d3d3);
+	outpdw(MDP_BASE + 0x40aa0, 0xd3d3d3);
+	outpdw(MDP_BASE + 0x40aa4, 0xd4d4d4);
+	outpdw(MDP_BASE + 0x40aa8, 0xd4d4d4);
+	outpdw(MDP_BASE + 0x40aac, 0xd5d5d5);
+	outpdw(MDP_BASE + 0x40ab0, 0xd6d6d6);
+	outpdw(MDP_BASE + 0x40ab4, 0xd6d6d6);
+	outpdw(MDP_BASE + 0x40ab8, 0xd7d7d7);
+	outpdw(MDP_BASE + 0x40abc, 0xd7d7d7);
+	outpdw(MDP_BASE + 0x40ac0, 0xd8d8d8);
+	outpdw(MDP_BASE + 0x40ac4, 0xd8d8d8);
+	outpdw(MDP_BASE + 0x40ac8, 0xd9d9d9);
+	outpdw(MDP_BASE + 0x40acc, 0xd9d9d9);
+	outpdw(MDP_BASE + 0x40ad0, 0xdadada);
+	outpdw(MDP_BASE + 0x40ad4, 0xdbdbdb);
+	outpdw(MDP_BASE + 0x40ad8, 0xdbdbdb);
+	outpdw(MDP_BASE + 0x40adc, 0xdcdcdc);
+	outpdw(MDP_BASE + 0x40ae0, 0xdcdcdc);
+	outpdw(MDP_BASE + 0x40ae4, 0xdddddd);
+	outpdw(MDP_BASE + 0x40ae8, 0xdddddd);
+	outpdw(MDP_BASE + 0x40aec, 0xdedede);
+	outpdw(MDP_BASE + 0x40af0, 0xdedede);
+	outpdw(MDP_BASE + 0x40af4, 0xdfdfdf);
+	outpdw(MDP_BASE + 0x40af8, 0xdfdfdf);
+	outpdw(MDP_BASE + 0x40afc, 0xe0e0e0);
+	outpdw(MDP_BASE + 0x40b00, 0xe0e0e0);
+	outpdw(MDP_BASE + 0x40b04, 0xe1e1e1);
+	outpdw(MDP_BASE + 0x40b08, 0xe1e1e1);
+	outpdw(MDP_BASE + 0x40b0c, 0xe2e2e2);
+	outpdw(MDP_BASE + 0x40b10, 0xe3e3e3);
+	outpdw(MDP_BASE + 0x40b14, 0xe3e3e3);
+	outpdw(MDP_BASE + 0x40b18, 0xe4e4e4);
+	outpdw(MDP_BASE + 0x40b1c, 0xe4e4e4);
+	outpdw(MDP_BASE + 0x40b20, 0xe5e5e5);
+	outpdw(MDP_BASE + 0x40b24, 0xe5e5e5);
+	outpdw(MDP_BASE + 0x40b28, 0xe6e6e6);
+	outpdw(MDP_BASE + 0x40b2c, 0xe6e6e6);
+	outpdw(MDP_BASE + 0x40b30, 0xe7e7e7);
+	outpdw(MDP_BASE + 0x40b34, 0xe7e7e7);
+	outpdw(MDP_BASE + 0x40b38, 0xe8e8e8);
+	outpdw(MDP_BASE + 0x40b3c, 0xe8e8e8);
+	outpdw(MDP_BASE + 0x40b40, 0xe9e9e9);
+	outpdw(MDP_BASE + 0x40b44, 0xe9e9e9);
+	outpdw(MDP_BASE + 0x40b48, 0xeaeaea);
+	outpdw(MDP_BASE + 0x40b4c, 0xeaeaea);
+	outpdw(MDP_BASE + 0x40b50, 0xebebeb);
+	outpdw(MDP_BASE + 0x40b54, 0xebebeb);
+	outpdw(MDP_BASE + 0x40b58, 0xececec);
+	outpdw(MDP_BASE + 0x40b5c, 0xececec);
+	outpdw(MDP_BASE + 0x40b60, 0xededed);
+	outpdw(MDP_BASE + 0x40b64, 0xededed);
+	outpdw(MDP_BASE + 0x40b68, 0xeeeeee);
+	outpdw(MDP_BASE + 0x40b6c, 0xeeeeee);
+	outpdw(MDP_BASE + 0x40b70, 0xefefef);
+	outpdw(MDP_BASE + 0x40b74, 0xefefef);
+	outpdw(MDP_BASE + 0x40b78, 0xf0f0f0);
+	outpdw(MDP_BASE + 0x40b7c, 0xf0f0f0);
+	outpdw(MDP_BASE + 0x40b80, 0xf1f1f1);
+	outpdw(MDP_BASE + 0x40b84, 0xf1f1f1);
+	outpdw(MDP_BASE + 0x40b88, 0xf2f2f2);
+	outpdw(MDP_BASE + 0x40b8c, 0xf2f2f2);
+	outpdw(MDP_BASE + 0x40b90, 0xf2f2f2);
+	outpdw(MDP_BASE + 0x40b94, 0xf3f3f3);
+	outpdw(MDP_BASE + 0x40b98, 0xf3f3f3);
+	outpdw(MDP_BASE + 0x40b9c, 0xf4f4f4);
+	outpdw(MDP_BASE + 0x40ba0, 0xf4f4f4);
+	outpdw(MDP_BASE + 0x40ba4, 0xf5f5f5);
+	outpdw(MDP_BASE + 0x40ba8, 0xf5f5f5);
+	outpdw(MDP_BASE + 0x40bac, 0xf6f6f6);
+	outpdw(MDP_BASE + 0x40bb0, 0xf6f6f6);
+	outpdw(MDP_BASE + 0x40bb4, 0xf7f7f7);
+	outpdw(MDP_BASE + 0x40bb8, 0xf7f7f7);
+	outpdw(MDP_BASE + 0x40bbc, 0xf8f8f8);
+	outpdw(MDP_BASE + 0x40bc0, 0xf8f8f8);
+	outpdw(MDP_BASE + 0x40bc4, 0xf9f9f9);
+	outpdw(MDP_BASE + 0x40bc8, 0xf9f9f9);
+	outpdw(MDP_BASE + 0x40bcc, 0xfafafa);
+	outpdw(MDP_BASE + 0x40bd0, 0xfafafa);
+	outpdw(MDP_BASE + 0x40bd4, 0xfafafa);
+	outpdw(MDP_BASE + 0x40bd8, 0xfbfbfb);
+	outpdw(MDP_BASE + 0x40bdc, 0xfbfbfb);
+	outpdw(MDP_BASE + 0x40be0, 0xfcfcfc);
+	outpdw(MDP_BASE + 0x40be4, 0xfcfcfc);
+	outpdw(MDP_BASE + 0x40be8, 0xfdfdfd);
+	outpdw(MDP_BASE + 0x40bec, 0xfdfdfd);
+	outpdw(MDP_BASE + 0x40bf0, 0xfefefe);
+	outpdw(MDP_BASE + 0x40bf4, 0xfefefe);
+	outpdw(MDP_BASE + 0x40bf8, 0xffffff);
+	outpdw(MDP_BASE + 0x40bfc, 0xffffff);
+	outpdw(MDP_BASE + 0x40c00, 0x0);
+	outpdw(MDP_BASE + 0x40c04, 0x0);
+	outpdw(MDP_BASE + 0x40c08, 0x0);
+	outpdw(MDP_BASE + 0x40c0c, 0x0);
+	outpdw(MDP_BASE + 0x40c10, 0x0);
+	outpdw(MDP_BASE + 0x40c14, 0x0);
+	outpdw(MDP_BASE + 0x40c18, 0x0);
+	outpdw(MDP_BASE + 0x40c1c, 0x0);
+	outpdw(MDP_BASE + 0x40c20, 0x0);
+	outpdw(MDP_BASE + 0x40c24, 0x0);
+	outpdw(MDP_BASE + 0x40c28, 0x0);
+	outpdw(MDP_BASE + 0x40c2c, 0x0);
+	outpdw(MDP_BASE + 0x40c30, 0x0);
+	outpdw(MDP_BASE + 0x40c34, 0x0);
+	outpdw(MDP_BASE + 0x40c38, 0x0);
+	outpdw(MDP_BASE + 0x40c3c, 0x0);
+	outpdw(MDP_BASE + 0x40c40, 0x10101);
+	outpdw(MDP_BASE + 0x40c44, 0x10101);
+	outpdw(MDP_BASE + 0x40c48, 0x10101);
+	outpdw(MDP_BASE + 0x40c4c, 0x10101);
+	outpdw(MDP_BASE + 0x40c50, 0x10101);
+	outpdw(MDP_BASE + 0x40c54, 0x10101);
+	outpdw(MDP_BASE + 0x40c58, 0x10101);
+	outpdw(MDP_BASE + 0x40c5c, 0x10101);
+	outpdw(MDP_BASE + 0x40c60, 0x10101);
+	outpdw(MDP_BASE + 0x40c64, 0x10101);
+	outpdw(MDP_BASE + 0x40c68, 0x20202);
+	outpdw(MDP_BASE + 0x40c6c, 0x20202);
+	outpdw(MDP_BASE + 0x40c70, 0x20202);
+	outpdw(MDP_BASE + 0x40c74, 0x20202);
+	outpdw(MDP_BASE + 0x40c78, 0x20202);
+	outpdw(MDP_BASE + 0x40c7c, 0x20202);
+	outpdw(MDP_BASE + 0x40c80, 0x30303);
+	outpdw(MDP_BASE + 0x40c84, 0x30303);
+	outpdw(MDP_BASE + 0x40c88, 0x30303);
+	outpdw(MDP_BASE + 0x40c8c, 0x30303);
+	outpdw(MDP_BASE + 0x40c90, 0x30303);
+	outpdw(MDP_BASE + 0x40c94, 0x40404);
+	outpdw(MDP_BASE + 0x40c98, 0x40404);
+	outpdw(MDP_BASE + 0x40c9c, 0x40404);
+	outpdw(MDP_BASE + 0x40ca0, 0x40404);
+	outpdw(MDP_BASE + 0x40ca4, 0x40404);
+	outpdw(MDP_BASE + 0x40ca8, 0x50505);
+	outpdw(MDP_BASE + 0x40cac, 0x50505);
+	outpdw(MDP_BASE + 0x40cb0, 0x50505);
+	outpdw(MDP_BASE + 0x40cb4, 0x50505);
+	outpdw(MDP_BASE + 0x40cb8, 0x60606);
+	outpdw(MDP_BASE + 0x40cbc, 0x60606);
+	outpdw(MDP_BASE + 0x40cc0, 0x60606);
+	outpdw(MDP_BASE + 0x40cc4, 0x70707);
+	outpdw(MDP_BASE + 0x40cc8, 0x70707);
+	outpdw(MDP_BASE + 0x40ccc, 0x70707);
+	outpdw(MDP_BASE + 0x40cd0, 0x70707);
+	outpdw(MDP_BASE + 0x40cd4, 0x80808);
+	outpdw(MDP_BASE + 0x40cd8, 0x80808);
+	outpdw(MDP_BASE + 0x40cdc, 0x80808);
+	outpdw(MDP_BASE + 0x40ce0, 0x90909);
+	outpdw(MDP_BASE + 0x40ce4, 0x90909);
+	outpdw(MDP_BASE + 0x40ce8, 0xa0a0a);
+	outpdw(MDP_BASE + 0x40cec, 0xa0a0a);
+	outpdw(MDP_BASE + 0x40cf0, 0xa0a0a);
+	outpdw(MDP_BASE + 0x40cf4, 0xb0b0b);
+	outpdw(MDP_BASE + 0x40cf8, 0xb0b0b);
+	outpdw(MDP_BASE + 0x40cfc, 0xb0b0b);
+	outpdw(MDP_BASE + 0x40d00, 0xc0c0c);
+	outpdw(MDP_BASE + 0x40d04, 0xc0c0c);
+	outpdw(MDP_BASE + 0x40d08, 0xd0d0d);
+	outpdw(MDP_BASE + 0x40d0c, 0xd0d0d);
+	outpdw(MDP_BASE + 0x40d10, 0xe0e0e);
+	outpdw(MDP_BASE + 0x40d14, 0xe0e0e);
+	outpdw(MDP_BASE + 0x40d18, 0xe0e0e);
+	outpdw(MDP_BASE + 0x40d1c, 0xf0f0f);
+	outpdw(MDP_BASE + 0x40d20, 0xf0f0f);
+	outpdw(MDP_BASE + 0x40d24, 0x101010);
+	outpdw(MDP_BASE + 0x40d28, 0x101010);
+	outpdw(MDP_BASE + 0x40d2c, 0x111111);
+	outpdw(MDP_BASE + 0x40d30, 0x111111);
+	outpdw(MDP_BASE + 0x40d34, 0x121212);
+	outpdw(MDP_BASE + 0x40d38, 0x121212);
+	outpdw(MDP_BASE + 0x40d3c, 0x131313);
+	outpdw(MDP_BASE + 0x40d40, 0x131313);
+	outpdw(MDP_BASE + 0x40d44, 0x141414);
+	outpdw(MDP_BASE + 0x40d48, 0x151515);
+	outpdw(MDP_BASE + 0x40d4c, 0x151515);
+	outpdw(MDP_BASE + 0x40d50, 0x161616);
+	outpdw(MDP_BASE + 0x40d54, 0x161616);
+	outpdw(MDP_BASE + 0x40d58, 0x171717);
+	outpdw(MDP_BASE + 0x40d5c, 0x171717);
+	outpdw(MDP_BASE + 0x40d60, 0x181818);
+	outpdw(MDP_BASE + 0x40d64, 0x191919);
+	outpdw(MDP_BASE + 0x40d68, 0x191919);
+	outpdw(MDP_BASE + 0x40d6c, 0x1a1a1a);
+	outpdw(MDP_BASE + 0x40d70, 0x1b1b1b);
+	outpdw(MDP_BASE + 0x40d74, 0x1b1b1b);
+	outpdw(MDP_BASE + 0x40d78, 0x1c1c1c);
+	outpdw(MDP_BASE + 0x40d7c, 0x1c1c1c);
+	outpdw(MDP_BASE + 0x40d80, 0x1d1d1d);
+	outpdw(MDP_BASE + 0x40d84, 0x1e1e1e);
+	outpdw(MDP_BASE + 0x40d88, 0x1f1f1f);
+	outpdw(MDP_BASE + 0x40d8c, 0x1f1f1f);
+	outpdw(MDP_BASE + 0x40d90, 0x202020);
+	outpdw(MDP_BASE + 0x40d94, 0x212121);
+	outpdw(MDP_BASE + 0x40d98, 0x212121);
+	outpdw(MDP_BASE + 0x40d9c, 0x222222);
+	outpdw(MDP_BASE + 0x40da0, 0x232323);
+	outpdw(MDP_BASE + 0x40da4, 0x242424);
+	outpdw(MDP_BASE + 0x40da8, 0x242424);
+	outpdw(MDP_BASE + 0x40dac, 0x252525);
+	outpdw(MDP_BASE + 0x40db0, 0x262626);
+	outpdw(MDP_BASE + 0x40db4, 0x272727);
+	outpdw(MDP_BASE + 0x40db8, 0x272727);
+	outpdw(MDP_BASE + 0x40dbc, 0x282828);
+	outpdw(MDP_BASE + 0x40dc0, 0x292929);
+	outpdw(MDP_BASE + 0x40dc4, 0x2a2a2a);
+	outpdw(MDP_BASE + 0x40dc8, 0x2b2b2b);
+	outpdw(MDP_BASE + 0x40dcc, 0x2c2c2c);
+	outpdw(MDP_BASE + 0x40dd0, 0x2c2c2c);
+	outpdw(MDP_BASE + 0x40dd4, 0x2d2d2d);
+	outpdw(MDP_BASE + 0x40dd8, 0x2e2e2e);
+	outpdw(MDP_BASE + 0x40ddc, 0x2f2f2f);
+	outpdw(MDP_BASE + 0x40de0, 0x303030);
+	outpdw(MDP_BASE + 0x40de4, 0x313131);
+	outpdw(MDP_BASE + 0x40de8, 0x323232);
+	outpdw(MDP_BASE + 0x40dec, 0x333333);
+	outpdw(MDP_BASE + 0x40df0, 0x333333);
+	outpdw(MDP_BASE + 0x40df4, 0x343434);
+	outpdw(MDP_BASE + 0x40df8, 0x353535);
+	outpdw(MDP_BASE + 0x40dfc, 0x363636);
+	outpdw(MDP_BASE + 0x40e00, 0x373737);
+	outpdw(MDP_BASE + 0x40e04, 0x383838);
+	outpdw(MDP_BASE + 0x40e08, 0x393939);
+	outpdw(MDP_BASE + 0x40e0c, 0x3a3a3a);
+	outpdw(MDP_BASE + 0x40e10, 0x3b3b3b);
+	outpdw(MDP_BASE + 0x40e14, 0x3c3c3c);
+	outpdw(MDP_BASE + 0x40e18, 0x3d3d3d);
+	outpdw(MDP_BASE + 0x40e1c, 0x3e3e3e);
+	outpdw(MDP_BASE + 0x40e20, 0x3f3f3f);
+	outpdw(MDP_BASE + 0x40e24, 0x404040);
+	outpdw(MDP_BASE + 0x40e28, 0x414141);
+	outpdw(MDP_BASE + 0x40e2c, 0x424242);
+	outpdw(MDP_BASE + 0x40e30, 0x434343);
+	outpdw(MDP_BASE + 0x40e34, 0x444444);
+	outpdw(MDP_BASE + 0x40e38, 0x464646);
+	outpdw(MDP_BASE + 0x40e3c, 0x474747);
+	outpdw(MDP_BASE + 0x40e40, 0x484848);
+	outpdw(MDP_BASE + 0x40e44, 0x494949);
+	outpdw(MDP_BASE + 0x40e48, 0x4a4a4a);
+	outpdw(MDP_BASE + 0x40e4c, 0x4b4b4b);
+	outpdw(MDP_BASE + 0x40e50, 0x4c4c4c);
+	outpdw(MDP_BASE + 0x40e54, 0x4d4d4d);
+	outpdw(MDP_BASE + 0x40e58, 0x4f4f4f);
+	outpdw(MDP_BASE + 0x40e5c, 0x505050);
+	outpdw(MDP_BASE + 0x40e60, 0x515151);
+	outpdw(MDP_BASE + 0x40e64, 0x525252);
+	outpdw(MDP_BASE + 0x40e68, 0x535353);
+	outpdw(MDP_BASE + 0x40e6c, 0x545454);
+	outpdw(MDP_BASE + 0x40e70, 0x565656);
+	outpdw(MDP_BASE + 0x40e74, 0x575757);
+	outpdw(MDP_BASE + 0x40e78, 0x585858);
+	outpdw(MDP_BASE + 0x40e7c, 0x595959);
+	outpdw(MDP_BASE + 0x40e80, 0x5b5b5b);
+	outpdw(MDP_BASE + 0x40e84, 0x5c5c5c);
+	outpdw(MDP_BASE + 0x40e88, 0x5d5d5d);
+	outpdw(MDP_BASE + 0x40e8c, 0x5e5e5e);
+	outpdw(MDP_BASE + 0x40e90, 0x606060);
+	outpdw(MDP_BASE + 0x40e94, 0x616161);
+	outpdw(MDP_BASE + 0x40e98, 0x626262);
+	outpdw(MDP_BASE + 0x40e9c, 0x646464);
+	outpdw(MDP_BASE + 0x40ea0, 0x656565);
+	outpdw(MDP_BASE + 0x40ea4, 0x666666);
+	outpdw(MDP_BASE + 0x40ea8, 0x686868);
+	outpdw(MDP_BASE + 0x40eac, 0x696969);
+	outpdw(MDP_BASE + 0x40eb0, 0x6a6a6a);
+	outpdw(MDP_BASE + 0x40eb4, 0x6c6c6c);
+	outpdw(MDP_BASE + 0x40eb8, 0x6d6d6d);
+	outpdw(MDP_BASE + 0x40ebc, 0x6f6f6f);
+	outpdw(MDP_BASE + 0x40ec0, 0x707070);
+	outpdw(MDP_BASE + 0x40ec4, 0x717171);
+	outpdw(MDP_BASE + 0x40ec8, 0x737373);
+	outpdw(MDP_BASE + 0x40ecc, 0x747474);
+	outpdw(MDP_BASE + 0x40ed0, 0x767676);
+	outpdw(MDP_BASE + 0x40ed4, 0x777777);
+	outpdw(MDP_BASE + 0x40ed8, 0x797979);
+	outpdw(MDP_BASE + 0x40edc, 0x7a7a7a);
+	outpdw(MDP_BASE + 0x40ee0, 0x7c7c7c);
+	outpdw(MDP_BASE + 0x40ee4, 0x7d7d7d);
+	outpdw(MDP_BASE + 0x40ee8, 0x7f7f7f);
+	outpdw(MDP_BASE + 0x40eec, 0x808080);
+	outpdw(MDP_BASE + 0x40ef0, 0x828282);
+	outpdw(MDP_BASE + 0x40ef4, 0x838383);
+	outpdw(MDP_BASE + 0x40ef8, 0x858585);
+	outpdw(MDP_BASE + 0x40efc, 0x868686);
+	outpdw(MDP_BASE + 0x40f00, 0x888888);
+	outpdw(MDP_BASE + 0x40f04, 0x898989);
+	outpdw(MDP_BASE + 0x40f08, 0x8b8b8b);
+	outpdw(MDP_BASE + 0x40f0c, 0x8d8d8d);
+	outpdw(MDP_BASE + 0x40f10, 0x8e8e8e);
+	outpdw(MDP_BASE + 0x40f14, 0x909090);
+	outpdw(MDP_BASE + 0x40f18, 0x919191);
+	outpdw(MDP_BASE + 0x40f1c, 0x939393);
+	outpdw(MDP_BASE + 0x40f20, 0x959595);
+	outpdw(MDP_BASE + 0x40f24, 0x969696);
+	outpdw(MDP_BASE + 0x40f28, 0x989898);
+	outpdw(MDP_BASE + 0x40f2c, 0x9a9a9a);
+	outpdw(MDP_BASE + 0x40f30, 0x9b9b9b);
+	outpdw(MDP_BASE + 0x40f34, 0x9d9d9d);
+	outpdw(MDP_BASE + 0x40f38, 0x9f9f9f);
+	outpdw(MDP_BASE + 0x40f3c, 0xa1a1a1);
+	outpdw(MDP_BASE + 0x40f40, 0xa2a2a2);
+	outpdw(MDP_BASE + 0x40f44, 0xa4a4a4);
+	outpdw(MDP_BASE + 0x40f48, 0xa6a6a6);
+	outpdw(MDP_BASE + 0x40f4c, 0xa7a7a7);
+	outpdw(MDP_BASE + 0x40f50, 0xa9a9a9);
+	outpdw(MDP_BASE + 0x40f54, 0xababab);
+	outpdw(MDP_BASE + 0x40f58, 0xadadad);
+	outpdw(MDP_BASE + 0x40f5c, 0xafafaf);
+	outpdw(MDP_BASE + 0x40f60, 0xb0b0b0);
+	outpdw(MDP_BASE + 0x40f64, 0xb2b2b2);
+	outpdw(MDP_BASE + 0x40f68, 0xb4b4b4);
+	outpdw(MDP_BASE + 0x40f6c, 0xb6b6b6);
+	outpdw(MDP_BASE + 0x40f70, 0xb8b8b8);
+	outpdw(MDP_BASE + 0x40f74, 0xbababa);
+	outpdw(MDP_BASE + 0x40f78, 0xbbbbbb);
+	outpdw(MDP_BASE + 0x40f7c, 0xbdbdbd);
+	outpdw(MDP_BASE + 0x40f80, 0xbfbfbf);
+	outpdw(MDP_BASE + 0x40f84, 0xc1c1c1);
+	outpdw(MDP_BASE + 0x40f88, 0xc3c3c3);
+	outpdw(MDP_BASE + 0x40f8c, 0xc5c5c5);
+	outpdw(MDP_BASE + 0x40f90, 0xc7c7c7);
+	outpdw(MDP_BASE + 0x40f94, 0xc9c9c9);
+	outpdw(MDP_BASE + 0x40f98, 0xcbcbcb);
+	outpdw(MDP_BASE + 0x40f9c, 0xcdcdcd);
+	outpdw(MDP_BASE + 0x40fa0, 0xcfcfcf);
+	outpdw(MDP_BASE + 0x40fa4, 0xd1d1d1);
+	outpdw(MDP_BASE + 0x40fa8, 0xd3d3d3);
+	outpdw(MDP_BASE + 0x40fac, 0xd5d5d5);
+	outpdw(MDP_BASE + 0x40fb0, 0xd7d7d7);
+	outpdw(MDP_BASE + 0x40fb4, 0xd9d9d9);
+	outpdw(MDP_BASE + 0x40fb8, 0xdbdbdb);
+	outpdw(MDP_BASE + 0x40fbc, 0xdddddd);
+	outpdw(MDP_BASE + 0x40fc0, 0xdfdfdf);
+	outpdw(MDP_BASE + 0x40fc4, 0xe1e1e1);
+	outpdw(MDP_BASE + 0x40fc8, 0xe3e3e3);
+	outpdw(MDP_BASE + 0x40fcc, 0xe5e5e5);
+	outpdw(MDP_BASE + 0x40fd0, 0xe7e7e7);
+	outpdw(MDP_BASE + 0x40fd4, 0xe9e9e9);
+	outpdw(MDP_BASE + 0x40fd8, 0xebebeb);
+	outpdw(MDP_BASE + 0x40fdc, 0xeeeeee);
+	outpdw(MDP_BASE + 0x40fe0, 0xf0f0f0);
+	outpdw(MDP_BASE + 0x40fe4, 0xf2f2f2);
+	outpdw(MDP_BASE + 0x40fe8, 0xf4f4f4);
+	outpdw(MDP_BASE + 0x40fec, 0xf6f6f6);
+	outpdw(MDP_BASE + 0x40ff0, 0xf8f8f8);
+	outpdw(MDP_BASE + 0x40ff4, 0xfbfbfb);
+	outpdw(MDP_BASE + 0x40ff8, 0xfdfdfd);
+	outpdw(MDP_BASE + 0x40ffc, 0xffffff);
+}
+
+#define   IRQ_EN_1__MDP_IRQ___M    0x00000800
+
+void mdp_hw_init(void)
+{
+	int i;
+
+	/* MDP cmd block enable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	/* debug interface write access */
+	outpdw(MDP_BASE + 0x60, 1);
+
+	outp32(MDP_INTR_ENABLE, MDP_ANY_INTR_MASK);
+	outp32(MDP_EBI2_PORTMAP_MODE, 0x3);
+	outpdw(MDP_CMD_DEBUG_ACCESS_BASE + 0x01f8, 0x0);
+	outpdw(MDP_CMD_DEBUG_ACCESS_BASE + 0x01fc, 0x0);
+	outpdw(MDP_BASE + 0x60, 0x1);
+	mdp_load_lut_param();
+
+	/*
+	 * clear up unused fg/main registers
+	 */
+	/* comp.plane 2&3 ystride */
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0120, 0x0);
+	/* unpacked pattern */
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x012c, 0x0);
+	/* unpacked pattern */
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0130, 0x0);
+	/* unpacked pattern */
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0134, 0x0);
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0158, 0x0);
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x15c, 0x0);
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0160, 0x0);
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0170, 0x0);
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0174, 0x0);
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x017c, 0x0);
+
+	/* comp.plane 2 */
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0114, 0x0);
+	/* comp.plane 3 */
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0118, 0x0);
+
+	/* clear up unused bg registers */
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01c8, 0);
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01d0, 0);
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01dc, 0);
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01e0, 0);
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01e4, 0);
+
+#ifndef CONFIG_FB_MSM_MDP22
+	MDP_OUTP(MDP_BASE + 0xE0000, 0);
+	MDP_OUTP(MDP_BASE + 0x100, 0xffffffff);
+	MDP_OUTP(MDP_BASE + 0x90070, 0);
+#endif
+
+	/*
+	 * limit vector
+	 * pre gets applied before color matrix conversion
+	 * post is after ccs
+	 */
+	writel(mdp_plv[0], MDP_CSC_PRE_LV1n(0));
+	writel(mdp_plv[1], MDP_CSC_PRE_LV1n(1));
+	writel(mdp_plv[2], MDP_CSC_PRE_LV1n(2));
+	writel(mdp_plv[3], MDP_CSC_PRE_LV1n(3));
+
+#ifdef CONFIG_FB_MSM_MDP31
+	writel(mdp_plv[2], MDP_CSC_PRE_LV1n(4));
+	writel(mdp_plv[3], MDP_CSC_PRE_LV1n(5));
+
+	writel(0, MDP_CSC_POST_LV1n(0));
+	writel(0xff, MDP_CSC_POST_LV1n(1));
+	writel(0, MDP_CSC_POST_LV1n(2));
+	writel(0xff, MDP_CSC_POST_LV1n(3));
+	writel(0, MDP_CSC_POST_LV1n(4));
+	writel(0xff, MDP_CSC_POST_LV1n(5));
+
+	writel(0, MDP_CSC_PRE_LV2n(0));
+	writel(0xff, MDP_CSC_PRE_LV2n(1));
+	writel(0, MDP_CSC_PRE_LV2n(2));
+	writel(0xff, MDP_CSC_PRE_LV2n(3));
+	writel(0, MDP_CSC_PRE_LV2n(4));
+	writel(0xff, MDP_CSC_PRE_LV2n(5));
+
+	writel(mdp_plv[0], MDP_CSC_POST_LV2n(0));
+	writel(mdp_plv[1], MDP_CSC_POST_LV2n(1));
+	writel(mdp_plv[2], MDP_CSC_POST_LV2n(2));
+	writel(mdp_plv[3], MDP_CSC_POST_LV2n(3));
+	writel(mdp_plv[2], MDP_CSC_POST_LV2n(4));
+	writel(mdp_plv[3], MDP_CSC_POST_LV2n(5));
+#endif
+
+	/* primary forward matrix */
+	for (i = 0; i < MDP_CCS_SIZE; i++)
+		writel(mdp_ccs_rgb2yuv.ccs[i], MDP_CSC_PFMVn(i));
+
+#ifdef CONFIG_FB_MSM_MDP31
+	for (i = 0; i < MDP_BV_SIZE; i++)
+		writel(mdp_ccs_rgb2yuv.bv[i], MDP_CSC_POST_BV2n(i));
+
+	writel(0, MDP_CSC_PRE_BV2n(0));
+	writel(0, MDP_CSC_PRE_BV2n(1));
+	writel(0, MDP_CSC_PRE_BV2n(2));
+#endif
+	/* primary reverse matrix */
+	for (i = 0; i < MDP_CCS_SIZE; i++)
+		writel(mdp_ccs_yuv2rgb.ccs[i], MDP_CSC_PRMVn(i));
+
+	for (i = 0; i < MDP_BV_SIZE; i++)
+		writel(mdp_ccs_yuv2rgb.bv[i], MDP_CSC_PRE_BV1n(i));
+
+#ifdef CONFIG_FB_MSM_MDP31
+	writel(0, MDP_CSC_POST_BV1n(0));
+	writel(0, MDP_CSC_POST_BV1n(1));
+	writel(0, MDP_CSC_POST_BV1n(2));
+
+	outpdw(MDP_BASE + 0x30010, 0x03e0);
+	outpdw(MDP_BASE + 0x30014, 0x0360);
+	outpdw(MDP_BASE + 0x30018, 0x0120);
+	outpdw(MDP_BASE + 0x3001c, 0x0140);
+#endif
+	mdp_init_scale_table();
+
+#ifndef CONFIG_FB_MSM_MDP31
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0104,
+		 ((16 << 6) << 16) | (16) << 6);
+#endif
+
+	/* MDP cmd block disable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
diff --git a/drivers/video/msm/mdp_lcdc.c b/drivers/video/msm/mdp_lcdc.c
new file mode 100644
index 0000000..62b0975
--- /dev/null
+++ b/drivers/video/msm/mdp_lcdc.c
@@ -0,0 +1,432 @@
+/* drivers/video/msm/mdp_lcdc.c
+ *
+ * Copyright (c) 2009 Google Inc.
+ * Copyright (c) 2009 Code Aurora Forum. All rights reserved.
+ *
+ * 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.
+ *
+ * Author: Dima Zavin <dima@android.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+
+#include <asm/io.h>
+#include <asm/mach-types.h>
+
+#include <mach/msm_fb.h>
+
+#include "mdp_hw.h"
+
+struct mdp_lcdc_info {
+	struct mdp_info			*mdp;
+	struct clk			*mdp_clk;
+	struct clk			*pclk;
+	struct clk			*pad_pclk;
+	struct msm_panel_data		fb_panel_data;
+	struct platform_device		fb_pdev;
+	struct msm_lcdc_platform_data	*pdata;
+	uint32_t fb_start;
+
+	struct msmfb_callback		frame_start_cb;
+	wait_queue_head_t		vsync_waitq;
+	int				got_vsync;
+
+	struct {
+		uint32_t	clk_rate;
+		uint32_t	hsync_ctl;
+		uint32_t	vsync_period;
+		uint32_t	vsync_pulse_width;
+		uint32_t	display_hctl;
+		uint32_t	display_vstart;
+		uint32_t	display_vend;
+		uint32_t	hsync_skew;
+		uint32_t	polarity;
+	} parms;
+};
+
+static struct mdp_device *mdp_dev;
+
+#define panel_to_lcdc(p) container_of((p), struct mdp_lcdc_info, fb_panel_data)
+
+static int lcdc_unblank(struct msm_panel_data *fb_panel)
+{
+	struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
+	struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops;
+
+	pr_info("%s: ()\n", __func__);
+	panel_ops->unblank(panel_ops);
+
+	return 0;
+}
+
+static int lcdc_blank(struct msm_panel_data *fb_panel)
+{
+	struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
+	struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops;
+
+	pr_info("%s: ()\n", __func__);
+	panel_ops->blank(panel_ops);
+
+	return 0;
+}
+
+static int lcdc_suspend(struct msm_panel_data *fb_panel)
+{
+	struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
+
+	pr_info("%s: suspending\n", __func__);
+
+	mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN);
+	clk_disable_unprepare(lcdc->pad_pclk);
+	clk_disable_unprepare(lcdc->pclk);
+	clk_disable_unprepare(lcdc->mdp_clk);
+
+	return 0;
+}
+
+static int lcdc_resume(struct msm_panel_data *fb_panel)
+{
+	struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
+
+	pr_info("%s: resuming\n", __func__);
+
+	clk_prepare_enable(lcdc->mdp_clk);
+	clk_prepare_enable(lcdc->pclk);
+	clk_prepare_enable(lcdc->pad_pclk);
+	mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN);
+
+	return 0;
+}
+
+static int lcdc_hw_init(struct mdp_lcdc_info *lcdc)
+{
+	struct msm_panel_data *fb_panel = &lcdc->fb_panel_data;
+	uint32_t dma_cfg;
+
+	clk_prepare_enable(lcdc->mdp_clk);
+	clk_prepare_enable(lcdc->pclk);
+	clk_prepare_enable(lcdc->pad_pclk);
+
+	clk_set_rate(lcdc->pclk, lcdc->parms.clk_rate);
+	clk_set_rate(lcdc->pad_pclk, lcdc->parms.clk_rate);
+
+	/* write the lcdc params */
+	mdp_writel(lcdc->mdp, lcdc->parms.hsync_ctl, MDP_LCDC_HSYNC_CTL);
+	mdp_writel(lcdc->mdp, lcdc->parms.vsync_period, MDP_LCDC_VSYNC_PERIOD);
+	mdp_writel(lcdc->mdp, lcdc->parms.vsync_pulse_width,
+		   MDP_LCDC_VSYNC_PULSE_WIDTH);
+	mdp_writel(lcdc->mdp, lcdc->parms.display_hctl, MDP_LCDC_DISPLAY_HCTL);
+	mdp_writel(lcdc->mdp, lcdc->parms.display_vstart,
+		   MDP_LCDC_DISPLAY_V_START);
+	mdp_writel(lcdc->mdp, lcdc->parms.display_vend, MDP_LCDC_DISPLAY_V_END);
+	mdp_writel(lcdc->mdp, lcdc->parms.hsync_skew, MDP_LCDC_HSYNC_SKEW);
+
+	mdp_writel(lcdc->mdp, 0, MDP_LCDC_BORDER_CLR);
+	mdp_writel(lcdc->mdp, 0xff, MDP_LCDC_UNDERFLOW_CTL);
+	mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_HCTL);
+	mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_START);
+	mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_END);
+	mdp_writel(lcdc->mdp, lcdc->parms.polarity, MDP_LCDC_CTL_POLARITY);
+
+	/* config the dma_p block that drives the lcdc data */
+	mdp_writel(lcdc->mdp, lcdc->fb_start, MDP_DMA_P_IBUF_ADDR);
+	mdp_writel(lcdc->mdp, (((fb_panel->fb_data->yres & 0x7ff) << 16) |
+			       (fb_panel->fb_data->xres & 0x7ff)),
+		   MDP_DMA_P_SIZE);
+
+	mdp_writel(lcdc->mdp, 0, MDP_DMA_P_OUT_XY);
+
+	dma_cfg = mdp_readl(lcdc->mdp, MDP_DMA_P_CONFIG);
+	dma_cfg |= (DMA_PACK_ALIGN_LSB |
+		   DMA_PACK_PATTERN_RGB |
+		   DMA_DITHER_EN);
+	dma_cfg |= DMA_OUT_SEL_LCDC;
+	dma_cfg &= ~DMA_DST_BITS_MASK;
+
+	if (fb_panel->fb_data->output_format == MSM_MDP_OUT_IF_FMT_RGB666)
+		dma_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
+	else
+		dma_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS;
+
+	mdp_writel(lcdc->mdp, dma_cfg, MDP_DMA_P_CONFIG);
+
+	/* enable the lcdc timing generation */
+	mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN);
+
+	return 0;
+}
+
+static void lcdc_wait_vsync(struct msm_panel_data *panel)
+{
+	struct mdp_lcdc_info *lcdc = panel_to_lcdc(panel);
+	int ret;
+
+	ret = wait_event_timeout(lcdc->vsync_waitq, lcdc->got_vsync, HZ / 2);
+	if (!ret && !lcdc->got_vsync)
+		pr_err("%s: timeout waiting for VSYNC\n", __func__);
+	lcdc->got_vsync = 0;
+}
+
+static void lcdc_request_vsync(struct msm_panel_data *fb_panel,
+			       struct msmfb_callback *vsync_cb)
+{
+	struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
+
+	/* the vsync callback will start the dma */
+	vsync_cb->func(vsync_cb);
+	lcdc->got_vsync = 0;
+	mdp_out_if_req_irq(mdp_dev, MSM_LCDC_INTERFACE, MDP_LCDC_FRAME_START,
+			   &lcdc->frame_start_cb);
+	lcdc_wait_vsync(fb_panel);
+}
+
+static void lcdc_clear_vsync(struct msm_panel_data *fb_panel)
+{
+	struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
+	lcdc->got_vsync = 0;
+	mdp_out_if_req_irq(mdp_dev, MSM_LCDC_INTERFACE, 0, NULL);
+}
+
+/* called in irq context with mdp lock held, when mdp gets the
+ * MDP_LCDC_FRAME_START interrupt */
+static void lcdc_frame_start(struct msmfb_callback *cb)
+{
+	struct mdp_lcdc_info *lcdc;
+
+	lcdc = container_of(cb, struct mdp_lcdc_info, frame_start_cb);
+
+	lcdc->got_vsync = 1;
+	wake_up(&lcdc->vsync_waitq);
+}
+
+static void lcdc_dma_start(void *priv, uint32_t addr, uint32_t stride,
+			   uint32_t width, uint32_t height, uint32_t x,
+			   uint32_t y)
+{
+	struct mdp_lcdc_info *lcdc = priv;
+
+	struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
+	if (mdp->dma_config_dirty)
+	{
+		mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN);
+		mdelay(20);
+		mdp_dev->configure_dma(mdp_dev);
+		mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN);
+	}
+	mdp_writel(lcdc->mdp, stride, MDP_DMA_P_IBUF_Y_STRIDE);
+	mdp_writel(lcdc->mdp, addr, MDP_DMA_P_IBUF_ADDR);
+}
+
+static void precompute_timing_parms(struct mdp_lcdc_info *lcdc)
+{
+	struct msm_lcdc_timing *timing = lcdc->pdata->timing;
+	struct msm_fb_data *fb_data = lcdc->pdata->fb_data;
+	unsigned int hsync_period;
+	unsigned int hsync_start_x;
+	unsigned int hsync_end_x;
+	unsigned int vsync_period;
+	unsigned int display_vstart;
+	unsigned int display_vend;
+
+	hsync_period = (timing->hsync_back_porch +
+			fb_data->xres + timing->hsync_front_porch);
+	hsync_start_x = timing->hsync_back_porch;
+	hsync_end_x = hsync_start_x + fb_data->xres - 1;
+
+	vsync_period = (timing->vsync_back_porch +
+			fb_data->yres + timing->vsync_front_porch);
+	vsync_period *= hsync_period;
+
+	display_vstart = timing->vsync_back_porch;
+	display_vstart *= hsync_period;
+	display_vstart += timing->hsync_skew;
+
+	display_vend = (timing->vsync_back_porch + fb_data->yres) *
+		hsync_period;
+	display_vend += timing->hsync_skew - 1;
+
+	/* register values we pre-compute at init time from the timing
+	 * information in the panel info */
+	lcdc->parms.hsync_ctl = (((hsync_period & 0xfff) << 16) |
+				 (timing->hsync_pulse_width & 0xfff));
+	lcdc->parms.vsync_period = vsync_period & 0xffffff;
+	lcdc->parms.vsync_pulse_width = (timing->vsync_pulse_width *
+					 hsync_period) & 0xffffff;
+
+	lcdc->parms.display_hctl = (((hsync_end_x & 0xfff) << 16) |
+				    (hsync_start_x & 0xfff));
+	lcdc->parms.display_vstart = display_vstart & 0xffffff;
+	lcdc->parms.display_vend = display_vend & 0xffffff;
+	lcdc->parms.hsync_skew = timing->hsync_skew & 0xfff;
+	lcdc->parms.polarity = ((timing->hsync_act_low << 0) |
+				(timing->vsync_act_low << 1) |
+				(timing->den_act_low << 2));
+	lcdc->parms.clk_rate = timing->clk_rate;
+}
+
+static int mdp_lcdc_probe(struct platform_device *pdev)
+{
+	struct msm_lcdc_platform_data *pdata = pdev->dev.platform_data;
+	struct mdp_lcdc_info *lcdc;
+	int ret = 0;
+
+	if (!pdata) {
+		pr_err("%s: no LCDC platform data found\n", __func__);
+		return -EINVAL;
+	}
+
+	lcdc = kzalloc(sizeof(struct mdp_lcdc_info), GFP_KERNEL);
+	if (!lcdc)
+		return -ENOMEM;
+
+	/* We don't actually own the clocks, the mdp does. */
+	lcdc->mdp_clk = clk_get(mdp_dev->dev.parent, "mdp_clk");
+	if (IS_ERR(lcdc->mdp_clk)) {
+		pr_err("%s: failed to get mdp_clk\n", __func__);
+		ret = PTR_ERR(lcdc->mdp_clk);
+		goto err_get_mdp_clk;
+	}
+
+	lcdc->pclk = clk_get(mdp_dev->dev.parent, "lcdc_pclk_clk");
+	if (IS_ERR(lcdc->pclk)) {
+		pr_err("%s: failed to get lcdc_pclk\n", __func__);
+		ret = PTR_ERR(lcdc->pclk);
+		goto err_get_pclk;
+	}
+
+	lcdc->pad_pclk = clk_get(mdp_dev->dev.parent, "lcdc_pad_pclk_clk");
+	if (IS_ERR(lcdc->pad_pclk)) {
+		pr_err("%s: failed to get lcdc_pad_pclk\n", __func__);
+		ret = PTR_ERR(lcdc->pad_pclk);
+		goto err_get_pad_pclk;
+	}
+
+	init_waitqueue_head(&lcdc->vsync_waitq);
+	lcdc->pdata = pdata;
+	lcdc->frame_start_cb.func = lcdc_frame_start;
+
+	platform_set_drvdata(pdev, lcdc);
+
+	mdp_out_if_register(mdp_dev, MSM_LCDC_INTERFACE, lcdc, MDP_DMA_P_DONE,
+			    lcdc_dma_start);
+
+	precompute_timing_parms(lcdc);
+
+	lcdc->fb_start = pdata->fb_resource->start;
+	lcdc->mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
+
+	lcdc->fb_panel_data.suspend = lcdc_suspend;
+	lcdc->fb_panel_data.resume = lcdc_resume;
+	lcdc->fb_panel_data.wait_vsync = lcdc_wait_vsync;
+	lcdc->fb_panel_data.request_vsync = lcdc_request_vsync;
+	lcdc->fb_panel_data.clear_vsync = lcdc_clear_vsync;
+	lcdc->fb_panel_data.blank = lcdc_blank;
+	lcdc->fb_panel_data.unblank = lcdc_unblank;
+	lcdc->fb_panel_data.fb_data = pdata->fb_data;
+	lcdc->fb_panel_data.interface_type = MSM_LCDC_INTERFACE;
+
+	ret = lcdc_hw_init(lcdc);
+	if (ret) {
+		pr_err("%s: Cannot initialize the mdp_lcdc\n", __func__);
+		goto err_hw_init;
+	}
+
+	lcdc->fb_pdev.name = "msm_panel";
+	lcdc->fb_pdev.id = pdata->fb_id;
+	lcdc->fb_pdev.resource = pdata->fb_resource;
+	lcdc->fb_pdev.num_resources = 1;
+	lcdc->fb_pdev.dev.platform_data = &lcdc->fb_panel_data;
+
+	if (pdata->panel_ops->init)
+		pdata->panel_ops->init(pdata->panel_ops);
+
+	ret = platform_device_register(&lcdc->fb_pdev);
+	if (ret) {
+		pr_err("%s: Cannot register msm_panel pdev\n", __func__);
+		goto err_plat_dev_reg;
+	}
+
+	pr_info("%s: initialized\n", __func__);
+
+	return 0;
+
+err_plat_dev_reg:
+err_hw_init:
+	platform_set_drvdata(pdev, NULL);
+	clk_put(lcdc->pad_pclk);
+err_get_pad_pclk:
+	clk_put(lcdc->pclk);
+err_get_pclk:
+	clk_put(lcdc->mdp_clk);
+err_get_mdp_clk:
+	kfree(lcdc);
+	return ret;
+}
+
+static int mdp_lcdc_remove(struct platform_device *pdev)
+{
+	struct mdp_lcdc_info *lcdc = platform_get_drvdata(pdev);
+
+	platform_set_drvdata(pdev, NULL);
+
+	clk_put(lcdc->pclk);
+	clk_put(lcdc->pad_pclk);
+	kfree(lcdc);
+
+	return 0;
+}
+
+static struct platform_driver mdp_lcdc_driver = {
+	.probe = mdp_lcdc_probe,
+	.remove = mdp_lcdc_remove,
+	.driver = {
+		.name	= "msm_mdp_lcdc",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int mdp_lcdc_add_mdp_device(struct device *dev,
+				   struct class_interface *class_intf)
+{
+	/* might need locking if mulitple mdp devices */
+	if (mdp_dev)
+		return 0;
+	mdp_dev = container_of(dev, struct mdp_device, dev);
+	return platform_driver_register(&mdp_lcdc_driver);
+}
+
+static void mdp_lcdc_remove_mdp_device(struct device *dev,
+				       struct class_interface *class_intf)
+{
+	/* might need locking if mulitple mdp devices */
+	if (dev != &mdp_dev->dev)
+		return;
+	platform_driver_unregister(&mdp_lcdc_driver);
+	mdp_dev = NULL;
+}
+
+static struct class_interface mdp_lcdc_interface = {
+	.add_dev = &mdp_lcdc_add_mdp_device,
+	.remove_dev = &mdp_lcdc_remove_mdp_device,
+};
+
+static int __init mdp_lcdc_init(void)
+{
+	return register_mdp_client(&mdp_lcdc_interface);
+}
+
+module_init(mdp_lcdc_init);
diff --git a/drivers/video/msm/mdp_ppp.c b/drivers/video/msm/mdp_ppp.c
index 2b6564e..4009ffc 100644
--- a/drivers/video/msm/mdp_ppp.c
+++ b/drivers/video/msm/mdp_ppp.c
@@ -1,7 +1,7 @@
-/* drivers/video/msm/mdp_ppp.c
+/* drivers/video/msm/src/drv/mdp/mdp_ppp.c
  *
- * Copyright (C) 2007 QUALCOMM Incorporated
  * Copyright (C) 2007 Google Incorporated
+ * Copyright (c) 2008-2009, 2012 Code Aurora Forum. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -12,55 +12,35 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
 #include <linux/fb.h>
-#include <linux/file.h>
-#include <linux/delay.h>
 #include <linux/msm_mdp.h>
-#include <mach/msm_fb.h>
+#include <linux/file.h>
+#include <linux/android_pmem.h>
+#include <linux/major.h>
 
-#include "mdp_hw.h"
-#include "mdp_scale_tables.h"
+#include "linux/proc_fs.h"
 
-#define DLOG(x...) do {} while (0)
+#include <mach/hardware.h>
+#include <linux/io.h>
 
-#define MDP_DOWNSCALE_BLUR (MDP_DOWNSCALE_MAX + 1)
-static int downscale_y_table = MDP_DOWNSCALE_MAX;
-static int downscale_x_table = MDP_DOWNSCALE_MAX;
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <linux/semaphore.h>
+#include <linux/msm_kgsl.h>
 
-struct mdp_regs {
-	uint32_t src0;
-	uint32_t src1;
-	uint32_t dst0;
-	uint32_t dst1;
-	uint32_t src_cfg;
-	uint32_t dst_cfg;
-	uint32_t src_pack;
-	uint32_t dst_pack;
-	uint32_t src_rect;
-	uint32_t dst_rect;
-	uint32_t src_ystride;
-	uint32_t dst_ystride;
-	uint32_t op;
-	uint32_t src_bpp;
-	uint32_t dst_bpp;
-	uint32_t edge;
-	uint32_t phasex_init;
-	uint32_t phasey_init;
-	uint32_t phasex_step;
-	uint32_t phasey_step;
-};
+#include "mdp.h"
+#include "msm_fb.h"
 
-static uint32_t pack_pattern[] = {
-	PPP_ARRAY0(PACK_PATTERN)
-};
-
-static uint32_t src_img_cfg[] = {
-	PPP_ARRAY1(CFG, SRC)
-};
-
-static uint32_t dst_img_cfg[] = {
-	PPP_ARRAY1(CFG, DST)
-};
+#define MDP_IS_IMGTYPE_BAD(x) (((x) >= MDP_IMGTYPE_LIMIT) && \
+				(((x) < MDP_IMGTYPE2_START) || \
+				 ((x) >= MDP_IMGTYPE_LIMIT2)))
 
 static uint32_t bytes_per_pixel[] = {
 	[MDP_RGB_565] = 2,
@@ -72,459 +52,504 @@
 	[MDP_RGBX_8888] = 4,
 	[MDP_Y_CBCR_H2V1] = 1,
 	[MDP_Y_CBCR_H2V2] = 1,
+	[MDP_Y_CBCR_H2V2_ADRENO] = 1,
 	[MDP_Y_CRCB_H2V1] = 1,
 	[MDP_Y_CRCB_H2V2] = 1,
-	[MDP_YCRYCB_H2V1] = 2
+	[MDP_YCRYCB_H2V1] = 2,
+	[MDP_BGR_565] = 2
 };
 
-static uint32_t dst_op_chroma[] = {
-	PPP_ARRAY1(CHROMA_SAMP, DST)
-};
+extern uint32 mdp_plv[];
+extern struct semaphore mdp_ppp_mutex;
 
-static uint32_t src_op_chroma[] = {
-	PPP_ARRAY1(CHROMA_SAMP, SRC)
-};
-
-static uint32_t bg_op_chroma[] = {
-	PPP_ARRAY1(CHROMA_SAMP, BG)
-};
-
-static void rotate_dst_addr_x(struct mdp_blit_req *req, struct mdp_regs *regs)
+int mdp_get_bytes_per_pixel(uint32_t format,
+				 struct msm_fb_data_type *mfd)
 {
-	regs->dst0 += (req->dst_rect.w -
-		       min((uint32_t)16, req->dst_rect.w)) * regs->dst_bpp;
-	regs->dst1 += (req->dst_rect.w -
-		       min((uint32_t)16, req->dst_rect.w)) * regs->dst_bpp;
+	int bpp = -EINVAL;
+	if (format == MDP_FB_FORMAT)
+		format = mfd->fb_imgType;
+	if (format < ARRAY_SIZE(bytes_per_pixel))
+		bpp = bytes_per_pixel[format];
+
+	if (bpp <= 0)
+		printk(KERN_ERR "%s incorrect format %d\n", __func__, format);
+	return bpp;
 }
 
-static void rotate_dst_addr_y(struct mdp_blit_req *req, struct mdp_regs *regs)
+static uint32 mdp_conv_matx_rgb2yuv(uint32 input_pixel,
+				    uint16 *matrix_and_bias_vector,
+				    uint32 *clamp_vector,
+				    uint32 *look_up_table)
 {
-	regs->dst0 += (req->dst_rect.h -
-		       min((uint32_t)16, req->dst_rect.h)) *
-		       regs->dst_ystride;
-	regs->dst1 += (req->dst_rect.h -
-		       min((uint32_t)16, req->dst_rect.h)) *
-		       regs->dst_ystride;
-}
+	uint8 input_C2, input_C0, input_C1;
+	uint32 output;
+	int32 comp_C2, comp_C1, comp_C0, temp;
+	int32 temp1, temp2, temp3;
+	int32 matrix[9];
+	int32 bias_vector[3];
+	int32 Y_low_limit, Y_high_limit, C_low_limit, C_high_limit;
+	int32 i;
+	uint32 _is_lookup_table_enabled;
 
-static void blit_rotate(struct mdp_blit_req *req,
-			struct mdp_regs *regs)
-{
-	if (req->flags == MDP_ROT_NOP)
-		return;
+	input_C2 = (input_pixel >> 16) & 0xFF;
+	input_C1 = (input_pixel >> 8) & 0xFF;
+	input_C0 = (input_pixel >> 0) & 0xFF;
 
-	regs->op |= PPP_OP_ROT_ON;
-	if ((req->flags & MDP_ROT_90 || req->flags & MDP_FLIP_LR) &&
-	    !(req->flags & MDP_ROT_90 && req->flags & MDP_FLIP_LR))
-		rotate_dst_addr_x(req, regs);
-	if (req->flags & MDP_ROT_90)
-		regs->op |= PPP_OP_ROT_90;
-	if (req->flags & MDP_FLIP_UD) {
-		regs->op |= PPP_OP_FLIP_UD;
-		rotate_dst_addr_y(req, regs);
-	}
-	if (req->flags & MDP_FLIP_LR)
-		regs->op |= PPP_OP_FLIP_LR;
-}
+	comp_C0 = input_C0;
+	comp_C1 = input_C1;
+	comp_C2 = input_C2;
 
-static void blit_convert(struct mdp_blit_req *req, struct mdp_regs *regs)
-{
-	if (req->src.format == req->dst.format)
-		return;
-	if (IS_RGB(req->src.format) && IS_YCRCB(req->dst.format)) {
-		regs->op |= PPP_OP_CONVERT_RGB2YCBCR | PPP_OP_CONVERT_ON;
-	} else if (IS_YCRCB(req->src.format) && IS_RGB(req->dst.format)) {
-		regs->op |= PPP_OP_CONVERT_YCBCR2RGB | PPP_OP_CONVERT_ON;
-		if (req->dst.format == MDP_RGB_565)
-			regs->op |= PPP_OP_CONVERT_MATRIX_SECONDARY;
-	}
-}
+	for (i = 0; i < 9; i++)
+		matrix[i] =
+		    ((int32) (((int32) matrix_and_bias_vector[i]) << 20)) >> 20;
 
-#define GET_BIT_RANGE(value, high, low) \
-	(((1 << (high - low + 1)) - 1) & (value >> low))
-static uint32_t transp_convert(struct mdp_blit_req *req)
-{
-	uint32_t transp = 0;
-	if (req->src.format == MDP_RGB_565) {
-		/* pad each value to 8 bits by copying the high bits into the
-		 * low end, convert RGB to RBG by switching low 2 components */
-		transp |= ((GET_BIT_RANGE(req->transp_mask, 15, 11) << 3) |
-			   (GET_BIT_RANGE(req->transp_mask, 15, 13))) << 16;
+	bias_vector[0] = (int32) (matrix_and_bias_vector[9] & 0xFF);
+	bias_vector[1] = (int32) (matrix_and_bias_vector[10] & 0xFF);
+	bias_vector[2] = (int32) (matrix_and_bias_vector[11] & 0xFF);
 
-		transp |= ((GET_BIT_RANGE(req->transp_mask, 4, 0) << 3) |
-			   (GET_BIT_RANGE(req->transp_mask, 4, 2))) << 8;
+	Y_low_limit = (int32) clamp_vector[0];
+	Y_high_limit = (int32) clamp_vector[1];
+	C_low_limit = (int32) clamp_vector[2];
+	C_high_limit = (int32) clamp_vector[3];
 
-		transp |= (GET_BIT_RANGE(req->transp_mask, 10, 5) << 2) |
-			  (GET_BIT_RANGE(req->transp_mask, 10, 9));
-	} else {
-		/* convert RGB to RBG */
-		transp |= (GET_BIT_RANGE(req->transp_mask, 15, 8)) |
-			  (GET_BIT_RANGE(req->transp_mask, 23, 16) << 16) |
-			  (GET_BIT_RANGE(req->transp_mask, 7, 0) << 8);
-	}
-	return transp;
-}
-#undef GET_BIT_RANGE
-
-static void blit_blend(struct mdp_blit_req *req, struct mdp_regs *regs)
-{
-	/* TRANSP BLEND */
-	if (req->transp_mask != MDP_TRANSP_NOP) {
-		req->transp_mask = transp_convert(req);
-		if (req->alpha != MDP_ALPHA_NOP) {
-			/* use blended transparancy mode
-			 * pixel = (src == transp) ? dst : blend
-			 * blend is combo of blend_eq_sel and
-			 * blend_alpha_sel */
-			regs->op |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON |
-				PPP_OP_BLEND_ALPHA_BLEND_NORMAL |
-				PPP_OP_BLEND_CONSTANT_ALPHA |
-				PPP_BLEND_ALPHA_TRANSP;
-		} else {
-			/* simple transparancy mode
-			 * pixel = (src == transp) ? dst : src */
-			regs->op |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON |
-				PPP_OP_BLEND_SRCPIXEL_TRANSP;
-		}
-	}
-
-	req->alpha &= 0xff;
-	/* ALPHA BLEND */
-	if (HAS_ALPHA(req->src.format)) {
-		regs->op |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON |
-			PPP_OP_BLEND_SRCPIXEL_ALPHA;
-	} else if (req->alpha < MDP_ALPHA_NOP) {
-		/* just blend by alpha */
-		regs->op |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON |
-			PPP_OP_BLEND_ALPHA_BLEND_NORMAL |
-			PPP_OP_BLEND_CONSTANT_ALPHA;
-	}
-
-	regs->op |= bg_op_chroma[req->dst.format];
-}
-
-#define ONE_HALF	(1LL << 32)
-#define ONE		(1LL << 33)
-#define TWO		(2LL << 33)
-#define THREE		(3LL << 33)
-#define FRAC_MASK (ONE - 1)
-#define INT_MASK (~FRAC_MASK)
-
-static int scale_params(uint32_t dim_in, uint32_t dim_out, uint32_t origin,
-			uint32_t *phase_init, uint32_t *phase_step)
-{
-	/* to improve precicsion calculations are done in U31.33 and converted
-	 * to U3.29 at the end */
-	int64_t k1, k2, k3, k4, tmp;
-	uint64_t n, d, os, os_p, od, od_p, oreq;
-	unsigned rpa = 0;
-	int64_t ip64, delta;
-
-	if (dim_out % 3 == 0)
-		rpa = !(dim_in % (dim_out / 3));
-
-	n = ((uint64_t)dim_out) << 34;
-	d = dim_in;
-	if (!d)
-		return -1;
-	do_div(n, d);
-	k3 = (n + 1) >> 1;
-	if ((k3 >> 4) < (1LL << 27) || (k3 >> 4) > (1LL << 31)) {
-		DLOG("crap bad scale\n");
-		return -1;
-	}
-	n = ((uint64_t)dim_in) << 34;
-	d = (uint64_t)dim_out;
-	if (!d)
-		return -1;
-	do_div(n, d);
-	k1 = (n + 1) >> 1;
-	k2 = (k1 - ONE) >> 1;
-
-	*phase_init = (int)(k2 >> 4);
-	k4 = (k3 - ONE) >> 1;
-
-	if (rpa) {
-		os = ((uint64_t)origin << 33) - ONE_HALF;
-		tmp = (dim_out * os) + ONE_HALF;
-		if (!dim_in)
-			return -1;
-		do_div(tmp, dim_in);
-		od = tmp - ONE_HALF;
-	} else {
-		os = ((uint64_t)origin << 1) - 1;
-		od = (((k3 * os) >> 1) + k4);
-	}
-
-	od_p = od & INT_MASK;
-	if (od_p != od)
-		od_p += ONE;
-
-	if (rpa) {
-		tmp = (dim_in * od_p) + ONE_HALF;
-		if (!dim_in)
-			return -1;
-		do_div(tmp, dim_in);
-		os_p = tmp - ONE_HALF;
-	} else {
-		os_p = ((k1 * (od_p >> 33)) + k2);
-	}
-
-	oreq = (os_p & INT_MASK) - ONE;
-
-	ip64 = os_p - oreq;
-	delta = ((int64_t)(origin) << 33) - oreq;
-	ip64 -= delta;
-	/* limit to valid range before the left shift */
-	delta = (ip64 & (1LL << 63)) ? 4 : -4;
-	delta <<= 33;
-	while (abs((int)(ip64 >> 33)) > 4)
-		ip64 += delta;
-	*phase_init = (int)(ip64 >> 4);
-	*phase_step = (uint32_t)(k1 >> 4);
-	return 0;
-}
-
-static void load_scale_table(const struct mdp_info *mdp,
-			     struct mdp_table_entry *table, int len)
-{
-	int i;
-	for (i = 0; i < len; i++)
-		mdp_writel(mdp, table[i].val, table[i].reg);
-}
-
-enum {
-IMG_LEFT,
-IMG_RIGHT,
-IMG_TOP,
-IMG_BOTTOM,
-};
-
-static void get_edge_info(uint32_t src, uint32_t src_coord, uint32_t dst,
-			  uint32_t *interp1, uint32_t *interp2,
-			  uint32_t *repeat1, uint32_t *repeat2) {
-	if (src > 3 * dst) {
-		*interp1 = 0;
-		*interp2 = src - 1;
-		*repeat1 = 0;
-		*repeat2 = 0;
-	} else if (src == 3 * dst) {
-		*interp1 = 0;
-		*interp2 = src;
-		*repeat1 = 0;
-		*repeat2 = 1;
-	} else if (src > dst && src < 3 * dst) {
-		*interp1 = -1;
-		*interp2 = src;
-		*repeat1 = 1;
-		*repeat2 = 1;
-	} else if (src == dst) {
-		*interp1 = -1;
-		*interp2 = src + 1;
-		*repeat1 = 1;
-		*repeat2 = 2;
-	} else {
-		*interp1 = -2;
-		*interp2 = src + 1;
-		*repeat1 = 2;
-		*repeat2 = 2;
-	}
-	*interp1 += src_coord;
-	*interp2 += src_coord;
-}
-
-static int get_edge_cond(struct mdp_blit_req *req, struct mdp_regs *regs)
-{
-	int32_t luma_interp[4];
-	int32_t luma_repeat[4];
-	int32_t chroma_interp[4];
-	int32_t chroma_bound[4];
-	int32_t chroma_repeat[4];
-	uint32_t dst_w, dst_h;
-
-	memset(&luma_interp, 0, sizeof(int32_t) * 4);
-	memset(&luma_repeat, 0, sizeof(int32_t) * 4);
-	memset(&chroma_interp, 0, sizeof(int32_t) * 4);
-	memset(&chroma_bound, 0, sizeof(int32_t) * 4);
-	memset(&chroma_repeat, 0, sizeof(int32_t) * 4);
-	regs->edge = 0;
-
-	if (req->flags & MDP_ROT_90) {
-		dst_w = req->dst_rect.h;
-		dst_h = req->dst_rect.w;
-	} else {
-		dst_w = req->dst_rect.w;
-		dst_h = req->dst_rect.h;
-	}
-
-	if (regs->op & (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON)) {
-		get_edge_info(req->src_rect.h, req->src_rect.y, dst_h,
-			      &luma_interp[IMG_TOP], &luma_interp[IMG_BOTTOM],
-			      &luma_repeat[IMG_TOP], &luma_repeat[IMG_BOTTOM]);
-		get_edge_info(req->src_rect.w, req->src_rect.x, dst_w,
-			      &luma_interp[IMG_LEFT], &luma_interp[IMG_RIGHT],
-			      &luma_repeat[IMG_LEFT], &luma_repeat[IMG_RIGHT]);
-	} else {
-		luma_interp[IMG_LEFT] = req->src_rect.x;
-		luma_interp[IMG_RIGHT] = req->src_rect.x + req->src_rect.w - 1;
-		luma_interp[IMG_TOP] = req->src_rect.y;
-		luma_interp[IMG_BOTTOM] = req->src_rect.y + req->src_rect.h - 1;
-		luma_repeat[IMG_LEFT] = 0;
-		luma_repeat[IMG_TOP] = 0;
-		luma_repeat[IMG_RIGHT] = 0;
-		luma_repeat[IMG_BOTTOM] = 0;
-	}
-
-	chroma_interp[IMG_LEFT] = luma_interp[IMG_LEFT];
-	chroma_interp[IMG_RIGHT] = luma_interp[IMG_RIGHT];
-	chroma_interp[IMG_TOP] = luma_interp[IMG_TOP];
-	chroma_interp[IMG_BOTTOM] = luma_interp[IMG_BOTTOM];
-
-	chroma_bound[IMG_LEFT] = req->src_rect.x;
-	chroma_bound[IMG_RIGHT] = req->src_rect.x + req->src_rect.w - 1;
-	chroma_bound[IMG_TOP] = req->src_rect.y;
-	chroma_bound[IMG_BOTTOM] = req->src_rect.y + req->src_rect.h - 1;
-
-	if (IS_YCRCB(req->src.format)) {
-		chroma_interp[IMG_LEFT] = chroma_interp[IMG_LEFT] >> 1;
-		chroma_interp[IMG_RIGHT] = (chroma_interp[IMG_RIGHT] + 1) >> 1;
-
-		chroma_bound[IMG_LEFT] = chroma_bound[IMG_LEFT] >> 1;
-		chroma_bound[IMG_RIGHT] = chroma_bound[IMG_RIGHT] >> 1;
-	}
-
-	if (req->src.format == MDP_Y_CBCR_H2V2 ||
-	    req->src.format == MDP_Y_CRCB_H2V2) {
-		chroma_interp[IMG_TOP] = (chroma_interp[IMG_TOP] - 1) >> 1;
-		chroma_interp[IMG_BOTTOM] = (chroma_interp[IMG_BOTTOM] + 1)
-					    >> 1;
-		chroma_bound[IMG_TOP] = (chroma_bound[IMG_TOP] + 1) >> 1;
-		chroma_bound[IMG_BOTTOM] = chroma_bound[IMG_BOTTOM] >> 1;
-	}
-
-	chroma_repeat[IMG_LEFT] = chroma_bound[IMG_LEFT] -
-				  chroma_interp[IMG_LEFT];
-	chroma_repeat[IMG_RIGHT] = chroma_interp[IMG_RIGHT] -
-				  chroma_bound[IMG_RIGHT];
-	chroma_repeat[IMG_TOP] = chroma_bound[IMG_TOP] -
-				  chroma_interp[IMG_TOP];
-	chroma_repeat[IMG_BOTTOM] = chroma_interp[IMG_BOTTOM] -
-				  chroma_bound[IMG_BOTTOM];
-
-	if (chroma_repeat[IMG_LEFT] < 0 || chroma_repeat[IMG_LEFT] > 3 ||
-	    chroma_repeat[IMG_RIGHT] < 0 || chroma_repeat[IMG_RIGHT] > 3 ||
-	    chroma_repeat[IMG_TOP] < 0 || chroma_repeat[IMG_TOP] > 3 ||
-	    chroma_repeat[IMG_BOTTOM] < 0 || chroma_repeat[IMG_BOTTOM] > 3 ||
-	    luma_repeat[IMG_LEFT] < 0 || luma_repeat[IMG_LEFT] > 3 ||
-	    luma_repeat[IMG_RIGHT] < 0 || luma_repeat[IMG_RIGHT] > 3 ||
-	    luma_repeat[IMG_TOP] < 0 || luma_repeat[IMG_TOP] > 3 ||
-	    luma_repeat[IMG_BOTTOM] < 0 || luma_repeat[IMG_BOTTOM] > 3)
-		return -1;
-
-	regs->edge |= (chroma_repeat[IMG_LEFT] & 3) << MDP_LEFT_CHROMA;
-	regs->edge |= (chroma_repeat[IMG_RIGHT] & 3) << MDP_RIGHT_CHROMA;
-	regs->edge |= (chroma_repeat[IMG_TOP] & 3) << MDP_TOP_CHROMA;
-	regs->edge |= (chroma_repeat[IMG_BOTTOM] & 3) << MDP_BOTTOM_CHROMA;
-	regs->edge |= (luma_repeat[IMG_LEFT] & 3) << MDP_LEFT_LUMA;
-	regs->edge |= (luma_repeat[IMG_RIGHT] & 3) << MDP_RIGHT_LUMA;
-	regs->edge |= (luma_repeat[IMG_TOP] & 3) << MDP_TOP_LUMA;
-	regs->edge |= (luma_repeat[IMG_BOTTOM] & 3) << MDP_BOTTOM_LUMA;
-	return 0;
-}
-
-static int blit_scale(const struct mdp_info *mdp, struct mdp_blit_req *req,
-		      struct mdp_regs *regs)
-{
-	uint32_t phase_init_x, phase_init_y, phase_step_x, phase_step_y;
-	uint32_t scale_factor_x, scale_factor_y;
-	uint32_t downscale;
-	uint32_t dst_w, dst_h;
-
-	if (req->flags & MDP_ROT_90) {
-		dst_w = req->dst_rect.h;
-		dst_h = req->dst_rect.w;
-	} else {
-		dst_w = req->dst_rect.w;
-		dst_h = req->dst_rect.h;
-	}
-	if ((req->src_rect.w == dst_w)  && (req->src_rect.h == dst_h) &&
-	    !(req->flags & MDP_BLUR)) {
-		regs->phasex_init = 0;
-		regs->phasey_init = 0;
-		regs->phasex_step = 0;
-		regs->phasey_step = 0;
-		return 0;
-	}
-
-	if (scale_params(req->src_rect.w, dst_w, 1, &phase_init_x,
-			 &phase_step_x) ||
-	    scale_params(req->src_rect.h, dst_h, 1, &phase_init_y,
-			 &phase_step_y))
-		return -1;
-
-	scale_factor_x = (dst_w * 10) / req->src_rect.w;
-	scale_factor_y = (dst_h * 10) / req->src_rect.h;
-
-	if (scale_factor_x > 8)
-		downscale = MDP_DOWNSCALE_PT8TO1;
-	else if (scale_factor_x > 6)
-		downscale = MDP_DOWNSCALE_PT6TOPT8;
-	else if (scale_factor_x > 4)
-		downscale = MDP_DOWNSCALE_PT4TOPT6;
+	if (look_up_table == 0)	/* check for NULL point */
+		_is_lookup_table_enabled = 0;
 	else
-		downscale = MDP_DOWNSCALE_PT2TOPT4;
-	if (downscale != downscale_x_table) {
-		load_scale_table(mdp, mdp_downscale_x_table[downscale], 64);
-		downscale_x_table = downscale;
+		_is_lookup_table_enabled = 1;
+
+	if (_is_lookup_table_enabled == 1) {
+		comp_C2 = (look_up_table[comp_C2] >> 16) & 0xFF;
+		comp_C1 = (look_up_table[comp_C1] >> 8) & 0xFF;
+		comp_C0 = (look_up_table[comp_C0] >> 0) & 0xFF;
 	}
+	/*
+	 * Color Conversion
+	 * reorder input colors
+	 */
+	temp = comp_C2;
+	comp_C2 = comp_C1;
+	comp_C1 = comp_C0;
+	comp_C0 = temp;
 
-	if (scale_factor_y > 8)
-		downscale = MDP_DOWNSCALE_PT8TO1;
-	else if (scale_factor_y > 6)
-		downscale = MDP_DOWNSCALE_PT6TOPT8;
-	else if (scale_factor_y > 4)
-		downscale = MDP_DOWNSCALE_PT4TOPT6;
-	else
-		downscale = MDP_DOWNSCALE_PT2TOPT4;
-	if (downscale != downscale_y_table) {
-		load_scale_table(mdp, mdp_downscale_y_table[downscale], 64);
-		downscale_y_table = downscale;
-	}
+	/* matrix multiplication */
+	temp1 = comp_C0 * matrix[0] + comp_C1 * matrix[1] + comp_C2 * matrix[2];
+	temp2 = comp_C0 * matrix[3] + comp_C1 * matrix[4] + comp_C2 * matrix[5];
+	temp3 = comp_C0 * matrix[6] + comp_C1 * matrix[7] + comp_C2 * matrix[8];
 
-	regs->phasex_init = phase_init_x;
-	regs->phasey_init = phase_init_y;
-	regs->phasex_step = phase_step_x;
-	regs->phasey_step = phase_step_y;
-	regs->op |= (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON);
-	return 0;
+	comp_C0 = temp1 + 0x100;
+	comp_C1 = temp2 + 0x100;
+	comp_C2 = temp3 + 0x100;
 
+	/* take interger part */
+	comp_C0 >>= 9;
+	comp_C1 >>= 9;
+	comp_C2 >>= 9;
+
+	/* post bias (+) */
+	comp_C0 += bias_vector[0];
+	comp_C1 += bias_vector[1];
+	comp_C2 += bias_vector[2];
+
+	/* limit pixel to 8-bit */
+	if (comp_C0 < 0)
+		comp_C0 = 0;
+
+	if (comp_C0 > 255)
+		comp_C0 = 255;
+
+	if (comp_C1 < 0)
+		comp_C1 = 0;
+
+	if (comp_C1 > 255)
+		comp_C1 = 255;
+
+	if (comp_C2 < 0)
+		comp_C2 = 0;
+
+	if (comp_C2 > 255)
+		comp_C2 = 255;
+
+	/* clamp */
+	if (comp_C0 < Y_low_limit)
+		comp_C0 = Y_low_limit;
+
+	if (comp_C0 > Y_high_limit)
+		comp_C0 = Y_high_limit;
+
+	if (comp_C1 < C_low_limit)
+		comp_C1 = C_low_limit;
+
+	if (comp_C1 > C_high_limit)
+		comp_C1 = C_high_limit;
+
+	if (comp_C2 < C_low_limit)
+		comp_C2 = C_low_limit;
+
+	if (comp_C2 > C_high_limit)
+		comp_C2 = C_high_limit;
+
+	output = (comp_C2 << 16) | (comp_C1 << 8) | comp_C0;
+	return output;
 }
 
-static void blit_blur(const struct mdp_info *mdp, struct mdp_blit_req *req,
-		      struct mdp_regs *regs)
+uint32 mdp_conv_matx_yuv2rgb(uint32 input_pixel,
+			     uint16 *matrix_and_bias_vector,
+			     uint32 *clamp_vector, uint32 *look_up_table)
 {
-	if (!(req->flags & MDP_BLUR))
-		return;
+	uint8 input_C2, input_C0, input_C1;
+	uint32 output;
+	int32 comp_C2, comp_C1, comp_C0, temp;
+	int32 temp1, temp2, temp3;
+	int32 matrix[9];
+	int32 bias_vector[3];
+	int32 Y_low_limit, Y_high_limit, C_low_limit, C_high_limit;
+	int32 i;
+	uint32 _is_lookup_table_enabled;
 
-	if (!(downscale_x_table == MDP_DOWNSCALE_BLUR &&
-	      downscale_y_table == MDP_DOWNSCALE_BLUR)) {
-		load_scale_table(mdp, mdp_gaussian_blur_table, 128);
-		downscale_x_table = MDP_DOWNSCALE_BLUR;
-		downscale_y_table = MDP_DOWNSCALE_BLUR;
+	input_C2 = (input_pixel >> 16) & 0xFF;
+	input_C1 = (input_pixel >> 8) & 0xFF;
+	input_C0 = (input_pixel >> 0) & 0xFF;
+
+	comp_C0 = input_C0;
+	comp_C1 = input_C1;
+	comp_C2 = input_C2;
+
+	for (i = 0; i < 9; i++)
+		matrix[i] =
+		    ((int32) (((int32) matrix_and_bias_vector[i]) << 20)) >> 20;
+
+	bias_vector[0] = (int32) (matrix_and_bias_vector[9] & 0xFF);
+	bias_vector[1] = (int32) (matrix_and_bias_vector[10] & 0xFF);
+	bias_vector[2] = (int32) (matrix_and_bias_vector[11] & 0xFF);
+
+	Y_low_limit = (int32) clamp_vector[0];
+	Y_high_limit = (int32) clamp_vector[1];
+	C_low_limit = (int32) clamp_vector[2];
+	C_high_limit = (int32) clamp_vector[3];
+
+	if (look_up_table == 0)	/* check for NULL point */
+		_is_lookup_table_enabled = 0;
+	else
+		_is_lookup_table_enabled = 1;
+
+	/* clamp */
+	if (comp_C0 < Y_low_limit)
+		comp_C0 = Y_low_limit;
+
+	if (comp_C0 > Y_high_limit)
+		comp_C0 = Y_high_limit;
+
+	if (comp_C1 < C_low_limit)
+		comp_C1 = C_low_limit;
+
+	if (comp_C1 > C_high_limit)
+		comp_C1 = C_high_limit;
+
+	if (comp_C2 < C_low_limit)
+		comp_C2 = C_low_limit;
+
+	if (comp_C2 > C_high_limit)
+		comp_C2 = C_high_limit;
+
+	/*
+	 * Color Conversion
+	 * pre bias (-)
+	 */
+	comp_C0 -= bias_vector[0];
+	comp_C1 -= bias_vector[1];
+	comp_C2 -= bias_vector[2];
+
+	/* matrix multiplication */
+	temp1 = comp_C0 * matrix[0] + comp_C1 * matrix[1] + comp_C2 * matrix[2];
+	temp2 = comp_C0 * matrix[3] + comp_C1 * matrix[4] + comp_C2 * matrix[5];
+	temp3 = comp_C0 * matrix[6] + comp_C1 * matrix[7] + comp_C2 * matrix[8];
+
+	comp_C0 = temp1 + 0x100;
+	comp_C1 = temp2 + 0x100;
+	comp_C2 = temp3 + 0x100;
+
+	/* take interger part */
+	comp_C0 >>= 9;
+	comp_C1 >>= 9;
+	comp_C2 >>= 9;
+
+	/* reorder output colors */
+	temp = comp_C0;
+	comp_C0 = comp_C1;
+	comp_C1 = comp_C2;
+	comp_C2 = temp;
+
+	/* limit pixel to 8-bit */
+	if (comp_C0 < 0)
+		comp_C0 = 0;
+
+	if (comp_C0 > 255)
+		comp_C0 = 255;
+
+	if (comp_C1 < 0)
+		comp_C1 = 0;
+
+	if (comp_C1 > 255)
+		comp_C1 = 255;
+
+	if (comp_C2 < 0)
+		comp_C2 = 0;
+
+	if (comp_C2 > 255)
+		comp_C2 = 255;
+
+	/* Look-up table */
+	if (_is_lookup_table_enabled == 1) {
+		comp_C2 = (look_up_table[comp_C2] >> 16) & 0xFF;
+		comp_C1 = (look_up_table[comp_C1] >> 8) & 0xFF;
+		comp_C0 = (look_up_table[comp_C0] >> 0) & 0xFF;
 	}
 
-	regs->op |= (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON);
+	output = (comp_C2 << 16) | (comp_C1 << 8) | comp_C0;
+	return output;
 }
 
+static uint32 mdp_calc_tpval(MDPIMG *mdpImg)
+{
+	uint32 tpVal;
+	uint8 plane_tp;
+
+	tpVal = 0;
+	if ((mdpImg->imgType == MDP_RGB_565)
+	    || (mdpImg->imgType == MDP_BGR_565)) {
+		/*
+		 * transparent color conversion into 24 bpp
+		 *
+		 * C2R_8BIT
+		 * left shift the entire bit and or it with the upper most bits
+		 */
+		plane_tp = (uint8) ((mdpImg->tpVal & 0xF800) >> 11);
+		tpVal |= ((plane_tp << 3) | ((plane_tp & 0x1C) >> 2)) << 16;
+
+		/* C1B_8BIT */
+		plane_tp = (uint8) (mdpImg->tpVal & 0x1F);
+		tpVal |= ((plane_tp << 3) | ((plane_tp & 0x1C) >> 2)) << 8;
+
+		/* C0G_8BIT */
+		plane_tp = (uint8) ((mdpImg->tpVal & 0x7E0) >> 5);
+		tpVal |= ((plane_tp << 2) | ((plane_tp & 0x30) >> 4));
+	} else {
+		/* 24bit RGB to RBG conversion */
+
+		tpVal = (mdpImg->tpVal & 0xFF00) >> 8;
+		tpVal |= (mdpImg->tpVal & 0xFF) << 8;
+		tpVal |= (mdpImg->tpVal & 0xFF0000);
+	}
+
+	return tpVal;
+}
+
+static uint8 *mdp_get_chroma_addr(MDPIBUF *iBuf)
+{
+	uint8 *dest1;
+
+	dest1 = NULL;
+	switch (iBuf->ibuf_type) {
+	case MDP_Y_CBCR_H2V2:
+	case MDP_Y_CRCB_H2V2:
+	case MDP_Y_CBCR_H2V1:
+	case MDP_Y_CRCB_H2V1:
+		dest1 = (uint8 *) iBuf->buf;
+		dest1 += iBuf->ibuf_width * iBuf->ibuf_height * iBuf->bpp;
+		break;
+
+	default:
+		break;
+	}
+
+	return dest1;
+}
+
+static void mdp_ppp_setbg(MDPIBUF *iBuf)
+{
+	uint8 *bg0_addr;
+	uint8 *bg1_addr;
+	uint32 bg0_ystride, bg1_ystride;
+	uint32 ppp_src_cfg_reg, unpack_pattern;
+	int v_slice, h_slice;
+
+	v_slice = h_slice = 1;
+	bg0_addr = (uint8 *) iBuf->buf;
+	bg1_addr = mdp_get_chroma_addr(iBuf);
+
+	bg0_ystride = iBuf->ibuf_width * iBuf->bpp;
+	bg1_ystride = iBuf->ibuf_width * iBuf->bpp;
+
+	switch (iBuf->ibuf_type) {
+	case MDP_BGR_565:
+	case MDP_RGB_565:
+		/* 888 = 3bytes
+		 * RGB = 3Components
+		 * RGB interleaved
+		 */
+		ppp_src_cfg_reg = PPP_SRC_C2R_5BITS | PPP_SRC_C0G_6BITS |
+			PPP_SRC_C1B_5BITS | PPP_SRC_BPP_INTERLVD_2BYTES |
+			PPP_SRC_INTERLVD_3COMPONENTS | PPP_SRC_UNPACK_TIGHT |
+			PPP_SRC_UNPACK_ALIGN_LSB |
+			PPP_SRC_FETCH_PLANES_INTERLVD;
+
+		if (iBuf->ibuf_type == MDP_RGB_565)
+			unpack_pattern =
+			    MDP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8);
+		else
+			unpack_pattern =
+			    MDP_GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8);
+		break;
+
+	case MDP_RGB_888:
+		/*
+		 * 888 = 3bytes
+		 * RGB = 3Components
+		 * RGB interleaved
+		 */
+		ppp_src_cfg_reg = PPP_SRC_C2R_8BITS | PPP_SRC_C0G_8BITS |
+		PPP_SRC_C1B_8BITS | PPP_SRC_BPP_INTERLVD_3BYTES |
+		PPP_SRC_INTERLVD_3COMPONENTS | PPP_SRC_UNPACK_TIGHT |
+		PPP_SRC_UNPACK_ALIGN_LSB | PPP_SRC_FETCH_PLANES_INTERLVD;
+
+		unpack_pattern =
+		    MDP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8);
+		break;
+
+	case MDP_BGRA_8888:
+	case MDP_RGBA_8888:
+	case MDP_ARGB_8888:
+	case MDP_XRGB_8888:
+	case MDP_RGBX_8888:
+		/*
+		 * 8888 = 4bytes
+		 * ARGB = 4Components
+		 * ARGB interleaved
+		 */
+		ppp_src_cfg_reg = PPP_SRC_C2R_8BITS | PPP_SRC_C0G_8BITS |
+		PPP_SRC_C1B_8BITS | PPP_SRC_C3A_8BITS | PPP_SRC_C3_ALPHA_EN |
+		PPP_SRC_BPP_INTERLVD_4BYTES | PPP_SRC_INTERLVD_4COMPONENTS |
+		PPP_SRC_UNPACK_TIGHT | PPP_SRC_UNPACK_ALIGN_LSB |
+		PPP_SRC_FETCH_PLANES_INTERLVD;
+
+		if (iBuf->ibuf_type == MDP_BGRA_8888)
+			unpack_pattern =
+			    MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G, CLR_B,
+						 8);
+		else if (iBuf->ibuf_type == MDP_RGBA_8888 ||
+				 iBuf->ibuf_type == MDP_RGBX_8888)
+			unpack_pattern =
+			    MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_B, CLR_G, CLR_R,
+						 8);
+		else if (iBuf->ibuf_type == MDP_XRGB_8888)
+			unpack_pattern =
+			    MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G, CLR_B,
+						 8);
+		else
+			unpack_pattern =
+			    MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G, CLR_B,
+						 8);
+		break;
+
+	case MDP_Y_CBCR_H2V2:
+	case MDP_Y_CRCB_H2V2:
+		ppp_src_cfg_reg = PPP_SRC_C2R_8BITS |
+		    PPP_SRC_C0G_8BITS |
+		    PPP_SRC_C1B_8BITS |
+		    PPP_SRC_C3A_8BITS |
+		    PPP_SRC_BPP_INTERLVD_2BYTES |
+		    PPP_SRC_INTERLVD_2COMPONENTS |
+		    PPP_SRC_UNPACK_TIGHT |
+		    PPP_SRC_UNPACK_ALIGN_LSB | PPP_SRC_FETCH_PLANES_PSEUDOPLNR;
+
+		if (iBuf->ibuf_type == MDP_Y_CBCR_H2V1)
+			unpack_pattern =
+			    MDP_GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8);
+		else
+			unpack_pattern =
+			    MDP_GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8);
+		v_slice = h_slice = 2;
+		break;
+
+	case MDP_YCRYCB_H2V1:
+		ppp_src_cfg_reg = PPP_SRC_C2R_8BITS |
+		    PPP_SRC_C0G_8BITS |
+		    PPP_SRC_C1B_8BITS |
+		    PPP_SRC_C3A_8BITS |
+		    PPP_SRC_BPP_INTERLVD_2BYTES |
+		    PPP_SRC_INTERLVD_4COMPONENTS |
+		    PPP_SRC_UNPACK_TIGHT | PPP_SRC_UNPACK_ALIGN_LSB;
+
+		unpack_pattern =
+		    MDP_GET_PACK_PATTERN(CLR_Y, CLR_CR, CLR_Y, CLR_CB, 8);
+		h_slice = 2;
+		break;
+
+	case MDP_Y_CBCR_H2V1:
+	case MDP_Y_CRCB_H2V1:
+		ppp_src_cfg_reg = PPP_SRC_C2R_8BITS |
+		    PPP_SRC_C0G_8BITS |
+		    PPP_SRC_C1B_8BITS |
+		    PPP_SRC_C3A_8BITS |
+		    PPP_SRC_BPP_INTERLVD_2BYTES |
+		    PPP_SRC_INTERLVD_2COMPONENTS |
+		    PPP_SRC_UNPACK_TIGHT |
+		    PPP_SRC_UNPACK_ALIGN_LSB | PPP_SRC_FETCH_PLANES_PSEUDOPLNR;
+
+		if (iBuf->ibuf_type == MDP_Y_CBCR_H2V1)
+			unpack_pattern =
+			    MDP_GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8);
+		else
+			unpack_pattern =
+			    MDP_GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8);
+		h_slice = 2;
+		break;
+
+	default:
+		return;
+	}
+
+	/* starting input address adjustment */
+	mdp_adjust_start_addr(&bg0_addr, &bg1_addr, v_slice, h_slice,
+			      iBuf->roi.lcd_x, iBuf->roi.lcd_y,
+			      iBuf->ibuf_width, iBuf->ibuf_height, iBuf->bpp,
+			      iBuf, 1);
+
+	/*
+	 * 0x01c0: background plane 0 addr
+	 * 0x01c4: background plane 1 addr
+	 * 0x01c8: background plane 2 addr
+	 * 0x01cc: bg y stride for plane 0 and 1
+	 * 0x01d0: bg y stride for plane 2
+	 * 0x01d4: bg src PPP config
+	 * 0x01d8: unpack pattern
+	 */
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01c0, bg0_addr);
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01c4, bg1_addr);
+
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01cc,
+		 (bg1_ystride << 16) | bg0_ystride);
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01d4, ppp_src_cfg_reg);
+
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01d8, unpack_pattern);
+}
+
+#define IS_PSEUDOPLNR(img) ((img == MDP_Y_CRCB_H2V2) | \
+				(img == MDP_Y_CBCR_H2V2) | \
+				(img == MDP_Y_CBCR_H2V2_ADRENO) | \
+				(img == MDP_Y_CRCB_H2V1) | \
+				(img == MDP_Y_CBCR_H2V1))
 
 #define IMG_LEN(rect_h, w, rect_w, bpp) (((rect_h) * w) * bpp)
 
 #define Y_TO_CRCB_RATIO(format) \
-	((format == MDP_Y_CBCR_H2V2 || format == MDP_Y_CRCB_H2V2) ?  2 :\
-	 (format == MDP_Y_CBCR_H2V1 || format == MDP_Y_CRCB_H2V1) ?  1 : 1)
+	((format == MDP_Y_CBCR_H2V2 || format == MDP_Y_CBCR_H2V2_ADRENO || \
+	  format == MDP_Y_CRCB_H2V2) ?  2 : (format == MDP_Y_CBCR_H2V1 || \
+	  format == MDP_Y_CRCB_H2V1) ?  1 : 1)
 
+#ifdef CONFIG_ANDROID_PMEM
 static void get_len(struct mdp_img *img, struct mdp_rect *rect, uint32_t bpp,
-		    uint32_t *len0, uint32_t *len1)
+			uint32_t *len0, uint32_t *len1)
 {
 	*len0 = IMG_LEN(rect->h, img->width, rect->w, bpp);
 	if (IS_PSEUDOPLNR(img->format))
@@ -533,199 +558,1129 @@
 		*len1 = 0;
 }
 
-static int valid_src_dst(unsigned long src_start, unsigned long src_len,
-			 unsigned long dst_start, unsigned long dst_len,
-			 struct mdp_blit_req *req, struct mdp_regs *regs)
+static void flush_imgs(struct mdp_blit_req *req, int src_bpp, int dst_bpp,
+			struct file *p_src_file, struct file *p_dst_file)
 {
-	unsigned long src_min_ok = src_start;
-	unsigned long src_max_ok = src_start + src_len;
-	unsigned long dst_min_ok = dst_start;
-	unsigned long dst_max_ok = dst_start + dst_len;
-	uint32_t src0_len, src1_len, dst0_len, dst1_len;
-	get_len(&req->src, &req->src_rect, regs->src_bpp, &src0_len,
-		 &src1_len);
-	get_len(&req->dst, &req->dst_rect, regs->dst_bpp, &dst0_len,
-		 &dst1_len);
+	uint32_t src0_len, src1_len;
 
-	if (regs->src0 < src_min_ok || regs->src0 > src_max_ok ||
-	    regs->src0 + src0_len > src_max_ok) {
-		DLOG("invalid_src %x %x %lx %lx\n", regs->src0,
-		      src0_len, src_min_ok, src_max_ok);
-		return 0;
+	if (!(req->flags & MDP_BLIT_NON_CACHED)) {
+		/* flush src images to memory before dma to mdp */
+		get_len(&req->src, &req->src_rect, src_bpp,
+		&src0_len, &src1_len);
+
+		flush_pmem_file(p_src_file,
+		req->src.offset, src0_len);
+
+		if (IS_PSEUDOPLNR(req->src.format))
+			flush_pmem_file(p_src_file,
+				req->src.offset + src0_len, src1_len);
 	}
-	if (regs->src_cfg & PPP_SRC_PLANE_PSEUDOPLNR) {
-		if (regs->src1 < src_min_ok || regs->src1 > src_max_ok ||
-		    regs->src1 + src1_len > src_max_ok) {
-			DLOG("invalid_src1");
-			return 0;
+
+}
+#else
+static void flush_imgs(struct mdp_blit_req *req, int src_bpp, int dst_bpp,
+			struct file *p_src_file, struct file *p_dst_file) { }
+#endif
+
+static void mdp_start_ppp(struct msm_fb_data_type *mfd, MDPIBUF *iBuf,
+struct mdp_blit_req *req, struct file *p_src_file, struct file *p_dst_file)
+{
+	uint8 *src0, *src1;
+	uint8 *dest0, *dest1;
+	uint16 inpBpp;
+	uint32 dest0_ystride;
+	uint32 src_width;
+	uint32 src_height;
+	uint32 src0_ystride;
+	uint32 src0_y1stride;
+	uint32 dst_roi_width;
+	uint32 dst_roi_height;
+	uint32 ppp_src_cfg_reg, ppp_operation_reg, ppp_dst_cfg_reg;
+	uint32 alpha, tpVal;
+	uint32 packPattern;
+	uint32 dst_packPattern;
+	boolean inputRGB, outputRGB, pseudoplanr_output;
+	int sv_slice, sh_slice;
+	int dv_slice, dh_slice;
+	boolean perPixelAlpha = FALSE;
+	boolean ppp_lookUp_enable = FALSE;
+
+	sv_slice = sh_slice = dv_slice = dh_slice = 1;
+	alpha = tpVal = 0;
+	src_width = iBuf->mdpImg.width;
+	src_height = iBuf->roi.y + iBuf->roi.height;
+	src1 = NULL;
+	dest1 = NULL;
+
+	inputRGB = outputRGB = TRUE;
+	pseudoplanr_output = FALSE;
+	ppp_operation_reg = 0;
+	ppp_dst_cfg_reg = 0;
+	ppp_src_cfg_reg = 0;
+
+	/* Wait for the pipe to clear */
+	do { } while (mdp_ppp_pipe_wait() <= 0);
+
+	/*
+	 * destination config
+	 */
+	switch (iBuf->ibuf_type) {
+	case MDP_RGB_888:
+		dst_packPattern =
+		    MDP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8);
+		ppp_dst_cfg_reg =
+		    PPP_DST_C0G_8BIT | PPP_DST_C1B_8BIT | PPP_DST_C2R_8BIT |
+		    PPP_DST_PACKET_CNT_INTERLVD_3ELEM | PPP_DST_PACK_TIGHT |
+		    PPP_DST_PACK_ALIGN_LSB | PPP_DST_OUT_SEL_AXI |
+		    PPP_DST_BPP_3BYTES | PPP_DST_PLANE_INTERLVD;
+		break;
+
+	case MDP_BGRA_8888:
+	case MDP_XRGB_8888:
+	case MDP_ARGB_8888:
+	case MDP_RGBA_8888:
+	case MDP_RGBX_8888:
+		if (iBuf->ibuf_type == MDP_BGRA_8888)
+			dst_packPattern =
+			    MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G, CLR_B,
+						 8);
+		else if (iBuf->ibuf_type == MDP_RGBA_8888 ||
+				 iBuf->ibuf_type == MDP_RGBX_8888)
+			dst_packPattern =
+			    MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_B, CLR_G, CLR_R,
+						 8);
+		else if (iBuf->ibuf_type == MDP_XRGB_8888)
+			dst_packPattern =
+			    MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G, CLR_B,
+						 8);
+		else
+			dst_packPattern =
+			    MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G, CLR_B,
+						 8);
+
+		ppp_dst_cfg_reg = PPP_DST_C0G_8BIT |
+		    PPP_DST_C1B_8BIT |
+		    PPP_DST_C2R_8BIT |
+		    PPP_DST_C3A_8BIT |
+		    PPP_DST_C3ALPHA_EN |
+		    PPP_DST_PACKET_CNT_INTERLVD_4ELEM |
+		    PPP_DST_PACK_TIGHT |
+		    PPP_DST_PACK_ALIGN_LSB |
+		    PPP_DST_OUT_SEL_AXI |
+		    PPP_DST_BPP_4BYTES | PPP_DST_PLANE_INTERLVD;
+		break;
+
+	case MDP_Y_CBCR_H2V2:
+	case MDP_Y_CRCB_H2V2:
+		if (iBuf->ibuf_type == MDP_Y_CBCR_H2V2)
+			dst_packPattern =
+			    MDP_GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8);
+		else
+			dst_packPattern =
+			    MDP_GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8);
+
+		ppp_dst_cfg_reg = PPP_DST_C2R_8BIT |
+		    PPP_DST_C0G_8BIT |
+		    PPP_DST_C1B_8BIT |
+		    PPP_DST_C3A_8BIT |
+		    PPP_DST_PACKET_CNT_INTERLVD_2ELEM |
+		    PPP_DST_PACK_TIGHT |
+		    PPP_DST_PACK_ALIGN_LSB |
+		    PPP_DST_OUT_SEL_AXI | PPP_DST_BPP_2BYTES;
+
+		ppp_operation_reg |= PPP_OP_DST_CHROMA_420;
+		outputRGB = FALSE;
+		pseudoplanr_output = TRUE;
+		/*
+		 * vertically (y direction) and horizontally (x direction)
+		 * sample reduction by 2
+		 */
+
+		/*
+		 * H2V2(YUV420) Cosite
+		 *
+		 * Y    Y    Y    Y
+		 * CbCr      CbCr
+		 * Y    Y    Y    Y
+		 * Y    Y    Y    Y
+		 * CbCr      CbCr
+		 * Y    Y    Y    Y
+		 */
+		dv_slice = dh_slice = 2;
+
+		/* (x,y) and (width,height) must be even numbern */
+		iBuf->roi.lcd_x = (iBuf->roi.lcd_x / 2) * 2;
+		iBuf->roi.dst_width = (iBuf->roi.dst_width / 2) * 2;
+		iBuf->roi.x = (iBuf->roi.x / 2) * 2;
+		iBuf->roi.width = (iBuf->roi.width / 2) * 2;
+
+		iBuf->roi.lcd_y = (iBuf->roi.lcd_y / 2) * 2;
+		iBuf->roi.dst_height = (iBuf->roi.dst_height / 2) * 2;
+		iBuf->roi.y = (iBuf->roi.y / 2) * 2;
+		iBuf->roi.height = (iBuf->roi.height / 2) * 2;
+		break;
+
+	case MDP_YCRYCB_H2V1:
+		dst_packPattern =
+		    MDP_GET_PACK_PATTERN(CLR_Y, CLR_CR, CLR_Y, CLR_CB, 8);
+		ppp_dst_cfg_reg =
+		    PPP_DST_C2R_8BIT | PPP_DST_C0G_8BIT | PPP_DST_C1B_8BIT |
+		    PPP_DST_C3A_8BIT | PPP_DST_PACKET_CNT_INTERLVD_4ELEM |
+		    PPP_DST_PACK_TIGHT | PPP_DST_PACK_ALIGN_LSB |
+		    PPP_DST_OUT_SEL_AXI | PPP_DST_BPP_2BYTES |
+		    PPP_DST_PLANE_INTERLVD;
+
+		ppp_operation_reg |= PPP_OP_DST_CHROMA_H2V1;
+		outputRGB = FALSE;
+		/*
+		 * horizontally (x direction) sample reduction by 2
+		 *
+		 * H2V1(YUV422) Cosite
+		 *
+		 * YCbCr    Y    YCbCr    Y
+		 * YCbCr    Y    YCbCr    Y
+		 * YCbCr    Y    YCbCr    Y
+		 * YCbCr    Y    YCbCr    Y
+		 */
+		dh_slice = 2;
+
+		/*
+		 * if it's TV-Out/MDP_YCRYCB_H2V1, let's go through the
+		 * preloaded gamma setting of 2.2 when the content is
+		 * non-linear ppp_lookUp_enable = TRUE;
+		 */
+
+		/* x and width must be even number */
+		iBuf->roi.lcd_x = (iBuf->roi.lcd_x / 2) * 2;
+		iBuf->roi.dst_width = (iBuf->roi.dst_width / 2) * 2;
+		iBuf->roi.x = (iBuf->roi.x / 2) * 2;
+		iBuf->roi.width = (iBuf->roi.width / 2) * 2;
+		break;
+
+	case MDP_Y_CBCR_H2V1:
+	case MDP_Y_CRCB_H2V1:
+		if (iBuf->ibuf_type == MDP_Y_CBCR_H2V1)
+			dst_packPattern =
+			    MDP_GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8);
+		else
+			dst_packPattern =
+			    MDP_GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8);
+
+		ppp_dst_cfg_reg = PPP_DST_C2R_8BIT |
+		    PPP_DST_C0G_8BIT |
+		    PPP_DST_C1B_8BIT |
+		    PPP_DST_C3A_8BIT |
+		    PPP_DST_PACKET_CNT_INTERLVD_2ELEM |
+		    PPP_DST_PACK_TIGHT |
+		    PPP_DST_PACK_ALIGN_LSB |
+		    PPP_DST_OUT_SEL_AXI | PPP_DST_BPP_2BYTES;
+
+		ppp_operation_reg |= PPP_OP_DST_CHROMA_H2V1;
+		outputRGB = FALSE;
+		pseudoplanr_output = TRUE;
+		/* horizontally (x direction) sample reduction by 2 */
+		dh_slice = 2;
+
+		/* x and width must be even number */
+		iBuf->roi.lcd_x = (iBuf->roi.lcd_x / 2) * 2;
+		iBuf->roi.dst_width = (iBuf->roi.dst_width / 2) * 2;
+		iBuf->roi.x = (iBuf->roi.x / 2) * 2;
+		iBuf->roi.width = (iBuf->roi.width / 2) * 2;
+		break;
+
+	case MDP_BGR_565:
+	case MDP_RGB_565:
+	default:
+		if (iBuf->ibuf_type == MDP_RGB_565)
+			dst_packPattern =
+			    MDP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8);
+		else
+			dst_packPattern =
+			    MDP_GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8);
+
+		ppp_dst_cfg_reg = PPP_DST_C0G_6BIT |
+		    PPP_DST_C1B_5BIT |
+		    PPP_DST_C2R_5BIT |
+		    PPP_DST_PACKET_CNT_INTERLVD_3ELEM |
+		    PPP_DST_PACK_TIGHT |
+		    PPP_DST_PACK_ALIGN_LSB |
+		    PPP_DST_OUT_SEL_AXI |
+		    PPP_DST_BPP_2BYTES | PPP_DST_PLANE_INTERLVD;
+		break;
+	}
+
+	/* source config */
+	switch (iBuf->mdpImg.imgType) {
+	case MDP_RGB_888:
+		inpBpp = 3;
+		/*
+		 * 565 = 2bytes
+		 * RGB = 3Components
+		 * RGB interleaved
+		 */
+		ppp_src_cfg_reg = PPP_SRC_C2R_8BITS | PPP_SRC_C0G_8BITS |
+			PPP_SRC_C1B_8BITS | PPP_SRC_BPP_INTERLVD_3BYTES |
+			PPP_SRC_INTERLVD_3COMPONENTS | PPP_SRC_UNPACK_TIGHT |
+			PPP_SRC_UNPACK_ALIGN_LSB |
+			PPP_SRC_FETCH_PLANES_INTERLVD;
+
+		packPattern = MDP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8);
+
+		ppp_operation_reg |= PPP_OP_COLOR_SPACE_RGB |
+		    PPP_OP_SRC_CHROMA_RGB | PPP_OP_DST_CHROMA_RGB;
+		break;
+
+	case MDP_BGRA_8888:
+	case MDP_RGBA_8888:
+	case MDP_ARGB_8888:
+		perPixelAlpha = TRUE;
+	case MDP_XRGB_8888:
+	case MDP_RGBX_8888:
+		inpBpp = 4;
+		/*
+		 * 8888 = 4bytes
+		 * ARGB = 4Components
+		 * ARGB interleaved
+		 */
+		ppp_src_cfg_reg = PPP_SRC_C2R_8BITS | PPP_SRC_C0G_8BITS |
+			PPP_SRC_C1B_8BITS | PPP_SRC_C3A_8BITS |
+			PPP_SRC_C3_ALPHA_EN | PPP_SRC_BPP_INTERLVD_4BYTES |
+			PPP_SRC_INTERLVD_4COMPONENTS | PPP_SRC_UNPACK_TIGHT |
+			PPP_SRC_UNPACK_ALIGN_LSB |
+			PPP_SRC_FETCH_PLANES_INTERLVD;
+
+		if (iBuf->mdpImg.imgType == MDP_BGRA_8888)
+			packPattern =
+			    MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G, CLR_B,
+						 8);
+		else if (iBuf->mdpImg.imgType == MDP_RGBA_8888 ||
+				 iBuf->mdpImg.imgType == MDP_RGBX_8888)
+			packPattern =
+			    MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_B, CLR_G, CLR_R,
+						 8);
+		else if (iBuf->ibuf_type == MDP_XRGB_8888)
+			packPattern =
+			    MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G, CLR_B,
+						 8);
+		else
+			packPattern =
+			    MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G, CLR_B,
+						 8);
+
+		ppp_operation_reg |= PPP_OP_COLOR_SPACE_RGB |
+		    PPP_OP_SRC_CHROMA_RGB | PPP_OP_DST_CHROMA_RGB;
+		break;
+
+	case MDP_Y_CBCR_H2V2:
+	case MDP_Y_CBCR_H2V2_ADRENO:
+	case MDP_Y_CRCB_H2V2:
+		inpBpp = 1;
+		src1 = (uint8 *) iBuf->mdpImg.cbcr_addr;
+
+		/*
+		 * CbCr = 2bytes
+		 * CbCr = 2Components
+		 * Y+CbCr
+		 */
+		ppp_src_cfg_reg = PPP_SRC_C2R_8BITS | PPP_SRC_C0G_8BITS |
+			PPP_SRC_C1B_8BITS | PPP_SRC_BPP_INTERLVD_2BYTES |
+			PPP_SRC_INTERLVD_2COMPONENTS | PPP_SRC_UNPACK_TIGHT |
+			PPP_SRC_UNPACK_ALIGN_LSB |
+			PPP_SRC_FETCH_PLANES_PSEUDOPLNR;
+
+		if (iBuf->mdpImg.imgType == MDP_Y_CRCB_H2V2)
+			packPattern =
+			    MDP_GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8);
+		else
+			packPattern =
+			    MDP_GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8);
+
+		ppp_operation_reg |= PPP_OP_COLOR_SPACE_YCBCR |
+		    PPP_OP_SRC_CHROMA_420 |
+		    PPP_OP_SRC_CHROMA_COSITE |
+		    PPP_OP_DST_CHROMA_RGB | PPP_OP_DST_CHROMA_COSITE;
+
+		inputRGB = FALSE;
+		sh_slice = sv_slice = 2;
+		break;
+
+	case MDP_YCRYCB_H2V1:
+		inpBpp = 2;
+		ppp_src_cfg_reg = PPP_SRC_C2R_8BITS |
+		    PPP_SRC_C0G_8BITS |
+		    PPP_SRC_C1B_8BITS |
+		    PPP_SRC_C3A_8BITS |
+		    PPP_SRC_BPP_INTERLVD_2BYTES |
+		    PPP_SRC_INTERLVD_4COMPONENTS |
+		    PPP_SRC_UNPACK_TIGHT | PPP_SRC_UNPACK_ALIGN_LSB;
+
+		packPattern =
+		    MDP_GET_PACK_PATTERN(CLR_Y, CLR_CR, CLR_Y, CLR_CB, 8);
+
+		ppp_operation_reg |= PPP_OP_SRC_CHROMA_H2V1 |
+		    PPP_OP_SRC_CHROMA_COSITE | PPP_OP_DST_CHROMA_COSITE;
+
+		/*
+		 * if it's TV-Out/MDP_YCRYCB_H2V1, let's go through the
+		 * preloaded inverse gamma setting of 2.2 since they're
+		 * symetric when the content is non-linear
+		 * ppp_lookUp_enable = TRUE;
+		 */
+
+		/* x and width must be even number */
+		iBuf->roi.lcd_x = (iBuf->roi.lcd_x / 2) * 2;
+		iBuf->roi.dst_width = (iBuf->roi.dst_width / 2) * 2;
+		iBuf->roi.x = (iBuf->roi.x / 2) * 2;
+		iBuf->roi.width = (iBuf->roi.width / 2) * 2;
+
+		inputRGB = FALSE;
+		sh_slice = 2;
+		break;
+
+	case MDP_Y_CBCR_H2V1:
+	case MDP_Y_CRCB_H2V1:
+		inpBpp = 1;
+		src1 = (uint8 *) iBuf->mdpImg.cbcr_addr;
+
+		ppp_src_cfg_reg = PPP_SRC_C2R_8BITS |
+		    PPP_SRC_C0G_8BITS |
+		    PPP_SRC_C1B_8BITS |
+		    PPP_SRC_C3A_8BITS |
+		    PPP_SRC_BPP_INTERLVD_2BYTES |
+		    PPP_SRC_INTERLVD_2COMPONENTS |
+		    PPP_SRC_UNPACK_TIGHT |
+		    PPP_SRC_UNPACK_ALIGN_LSB | PPP_SRC_FETCH_PLANES_PSEUDOPLNR;
+
+		if (iBuf->mdpImg.imgType == MDP_Y_CBCR_H2V1)
+			packPattern =
+			    MDP_GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8);
+		else
+			packPattern =
+			    MDP_GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8);
+
+		ppp_operation_reg |= PPP_OP_SRC_CHROMA_H2V1 |
+		    PPP_OP_SRC_CHROMA_COSITE | PPP_OP_DST_CHROMA_COSITE;
+		inputRGB = FALSE;
+		sh_slice = 2;
+		break;
+
+	case MDP_BGR_565:
+	case MDP_RGB_565:
+	default:
+		inpBpp = 2;
+		/*
+		 * 565 = 2bytes
+		 * RGB = 3Components
+		 * RGB interleaved
+		 */
+		ppp_src_cfg_reg = PPP_SRC_C2R_5BITS | PPP_SRC_C0G_6BITS |
+			PPP_SRC_C1B_5BITS | PPP_SRC_BPP_INTERLVD_2BYTES |
+			PPP_SRC_INTERLVD_3COMPONENTS | PPP_SRC_UNPACK_TIGHT |
+			PPP_SRC_UNPACK_ALIGN_LSB |
+			PPP_SRC_FETCH_PLANES_INTERLVD;
+
+		if (iBuf->mdpImg.imgType == MDP_RGB_565)
+			packPattern =
+			    MDP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8);
+		else
+			packPattern =
+			    MDP_GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8);
+
+		ppp_operation_reg |= PPP_OP_COLOR_SPACE_RGB |
+		    PPP_OP_SRC_CHROMA_RGB | PPP_OP_DST_CHROMA_RGB;
+		break;
+
+	}
+
+	if (pseudoplanr_output)
+		ppp_dst_cfg_reg |= PPP_DST_PLANE_PSEUDOPLN;
+
+	/* YCbCr to RGB color conversion flag */
+	if ((!inputRGB) && (outputRGB)) {
+		ppp_operation_reg |= PPP_OP_CONVERT_YCBCR2RGB |
+		    PPP_OP_CONVERT_ON;
+
+		/*
+		 * primary/secondary is sort of misleading term...but
+		 * in mdp2.2/3.0 we only use primary matrix (forward/rev)
+		 * in mdp3.1 we use set1(prim) and set2(secd)
+		 */
+#ifdef CONFIG_FB_MSM_MDP31
+		ppp_operation_reg |= PPP_OP_CONVERT_MATRIX_SECONDARY |
+					PPP_OP_DST_RGB;
+		MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0240, 0);
+#endif
+
+		if (ppp_lookUp_enable) {
+			ppp_operation_reg |= PPP_OP_LUT_C0_ON |
+			    PPP_OP_LUT_C1_ON | PPP_OP_LUT_C2_ON;
 		}
 	}
-	if (regs->dst0 < dst_min_ok || regs->dst0 > dst_max_ok ||
-	    regs->dst0 + dst0_len > dst_max_ok) {
-		DLOG("invalid_dst");
-		return 0;
-	}
-	if (regs->dst_cfg & PPP_SRC_PLANE_PSEUDOPLNR) {
-		if (regs->dst1 < dst_min_ok || regs->dst1 > dst_max_ok ||
-		    regs->dst1 + dst1_len > dst_max_ok) {
-			DLOG("invalid_dst1");
-			return 0;
+	/* RGB to YCbCr color conversion flag */
+	if ((inputRGB) && (!outputRGB)) {
+		ppp_operation_reg |= PPP_OP_CONVERT_RGB2YCBCR |
+		    PPP_OP_CONVERT_ON;
+
+#ifdef CONFIG_FB_MSM_MDP31
+		ppp_operation_reg |= PPP_OP_CONVERT_MATRIX_PRIMARY |
+					PPP_OP_DST_YCBCR;
+		MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0240, 0x1e);
+#endif
+
+		if (ppp_lookUp_enable) {
+			ppp_operation_reg |= PPP_OP_LUT_C0_ON |
+			    PPP_OP_LUT_C1_ON | PPP_OP_LUT_C2_ON;
 		}
 	}
-	return 1;
+	/* YCbCr to YCbCr color conversion flag */
+	if ((!inputRGB) && (!outputRGB)) {
+		if ((ppp_lookUp_enable) &&
+		    (iBuf->mdpImg.imgType != iBuf->ibuf_type)) {
+			ppp_operation_reg |= PPP_OP_LUT_C0_ON;
+		}
+	}
+
+	ppp_src_cfg_reg |= (iBuf->roi.x % 2) ? PPP_SRC_BPP_ROI_ODD_X : 0;
+	ppp_src_cfg_reg |= (iBuf->roi.y % 2) ? PPP_SRC_BPP_ROI_ODD_Y : 0;
+
+	if (req->flags & MDP_DEINTERLACE)
+		ppp_operation_reg |= PPP_OP_DEINT_EN;
+
+	/* Dither at DMA side only since iBuf format is RGB888 */
+	if (iBuf->mdpImg.mdpOp & MDPOP_DITHER)
+		ppp_operation_reg |= PPP_OP_DITHER_EN;
+
+	if (iBuf->mdpImg.mdpOp & MDPOP_ROTATION) {
+		ppp_operation_reg |= PPP_OP_ROT_ON;
+
+		if (iBuf->mdpImg.mdpOp & MDPOP_ROT90) {
+			ppp_operation_reg |= PPP_OP_ROT_90;
+		}
+		if (iBuf->mdpImg.mdpOp & MDPOP_LR) {
+			ppp_operation_reg |= PPP_OP_FLIP_LR;
+		}
+		if (iBuf->mdpImg.mdpOp & MDPOP_UD) {
+			ppp_operation_reg |= PPP_OP_FLIP_UD;
+		}
+	}
+
+	if (iBuf->mdpImg.imgType == MDP_Y_CBCR_H2V2_ADRENO)
+		src0_ystride = ALIGN(src_width, 32) * inpBpp;
+	else
+		src0_ystride = src_width * inpBpp;
+
+	if (iBuf->mdpImg.imgType == MDP_Y_CBCR_H2V2_ADRENO)
+		src0_y1stride = 2 * ALIGN(src_width/2, 32);
+	else
+		src0_y1stride = src0_ystride;
+
+	dest0_ystride = iBuf->ibuf_width * iBuf->bpp;
+
+	/* no need to care about rotation since it's the real-XY. */
+	dst_roi_width = iBuf->roi.dst_width;
+	dst_roi_height = iBuf->roi.dst_height;
+
+	src0 = (uint8 *) iBuf->mdpImg.bmy_addr;
+	dest0 = (uint8 *) iBuf->buf;
+
+	/* Jumping from Y-Plane to Chroma Plane */
+	dest1 = mdp_get_chroma_addr(iBuf);
+
+	/* first pixel addr calculation */
+	mdp_adjust_start_addr(&src0, &src1, sv_slice, sh_slice, iBuf->roi.x,
+			      iBuf->roi.y, src_width, src_height, inpBpp, iBuf,
+			      0);
+	mdp_adjust_start_addr(&dest0, &dest1, dv_slice, dh_slice,
+			      iBuf->roi.lcd_x, iBuf->roi.lcd_y,
+			      iBuf->ibuf_width, iBuf->ibuf_height, iBuf->bpp,
+			      iBuf, 2);
+
+	/* set scale operation */
+	mdp_set_scale(iBuf, dst_roi_width, dst_roi_height,
+		      inputRGB, outputRGB, &ppp_operation_reg);
+
+	/*
+	 * setting background source for blending
+	 */
+	mdp_set_blend_attr(iBuf, &alpha, &tpVal, perPixelAlpha,
+			   &ppp_operation_reg);
+
+	if (ppp_operation_reg & PPP_OP_BLEND_ON) {
+		mdp_ppp_setbg(iBuf);
+
+		if (iBuf->ibuf_type == MDP_YCRYCB_H2V1) {
+			ppp_operation_reg |= PPP_OP_BG_CHROMA_H2V1;
+
+			if (iBuf->mdpImg.mdpOp & MDPOP_TRANSP) {
+				tpVal = mdp_conv_matx_rgb2yuv(tpVal,
+						      (uint16 *) &
+						      mdp_ccs_rgb2yuv,
+						      &mdp_plv[0], NULL);
+			}
+		}
+	}
+
+	/*
+	 * 0x0004: enable dbg bus
+	 * 0x0100: "don't care" Edge Condit until scaling is on
+	 * 0x0104: xrc tile x&y size u7.6 format = 7bit.6bit
+	 * 0x0108: src pixel size
+	 * 0x010c: component plane 0 starting address
+	 * 0x011c: component plane 0 ystride
+	 * 0x0124: PPP source config register
+	 * 0x0128: unpacked pattern from lsb to msb (eg. RGB->BGR)
+	 */
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0108, (iBuf->roi.height << 16 |
+						      iBuf->roi.width));
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x010c, src0); /* comp.plane 0 */
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0110, src1); /* comp.plane 1 */
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x011c,
+		 (src0_y1stride << 16 | src0_ystride));
+
+	/* setup for rgb 565 */
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0124, ppp_src_cfg_reg);
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0128, packPattern);
+	/*
+	 * 0x0138: PPP destination operation register
+	 * 0x014c: constant_alpha|transparent_color
+	 * 0x0150: PPP destination config register
+	 * 0x0154: PPP packing pattern
+	 */
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0138, ppp_operation_reg);
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x014c, alpha << 24 | (tpVal &
+								0xffffff));
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0150, ppp_dst_cfg_reg);
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0154, dst_packPattern);
+
+	/*
+	 * 0x0164: ROI height and width
+	 * 0x0168: Component Plane 0 starting addr
+	 * 0x016c: Component Plane 1 starting addr
+	 * 0x0178: Component Plane 1/0 y stride
+	 */
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0164,
+		 (dst_roi_height << 16 | dst_roi_width));
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0168, dest0);
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x016c, dest1);
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0178,
+		 (dest0_ystride << 16 | dest0_ystride));
+
+	flush_imgs(req, inpBpp, iBuf->bpp, p_src_file, p_dst_file);
+#ifdef	CONFIG_FB_MSM_MDP31
+	MDP_OUTP(MDP_BASE + 0x00100, 0xFF00);
+#endif
+	mdp_pipe_kickoff(MDP_PPP_TERM, mfd);
 }
 
-
-static void flush_imgs(struct mdp_blit_req *req, struct mdp_regs *regs,
-		       struct file *src_file, struct file *dst_file)
+static int mdp_ppp_verify_req(struct mdp_blit_req *req)
 {
-}
+	u32 src_width, src_height, dst_width, dst_height;
 
-static void get_chroma_addr(struct mdp_img *img, struct mdp_rect *rect,
-			    uint32_t base, uint32_t bpp, uint32_t cfg,
-			    uint32_t *addr, uint32_t *ystride)
-{
-	uint32_t compress_v = Y_TO_CRCB_RATIO(img->format);
-	uint32_t compress_h = 2;
-	uint32_t  offset;
+	if (req == NULL) {
+		printk(KERN_ERR "\n%s(): Error in Line %u", __func__,
+			__LINE__);
+		return -1;
+	}
 
-	if (IS_PSEUDOPLNR(img->format)) {
-		offset = (rect->x / compress_h) * compress_h;
-		offset += rect->y == 0 ? 0 :
-			  ((rect->y + 1) / compress_v) * img->width;
-		*addr = base + (img->width * img->height * bpp);
-		*addr += offset * bpp;
-		*ystride |= *ystride << 16;
+	if (MDP_IS_IMGTYPE_BAD(req->src.format) ||
+	    MDP_IS_IMGTYPE_BAD(req->dst.format)) {
+		printk(KERN_ERR "\n%s(): Error in Line %u", __func__,
+			__LINE__);
+		return -1;
+	}
+
+	if ((req->src.width == 0) || (req->src.height == 0) ||
+	    (req->src_rect.w == 0) || (req->src_rect.h == 0) ||
+	    (req->dst.width == 0) || (req->dst.height == 0) ||
+	    (req->dst_rect.w == 0) || (req->dst_rect.h == 0)) {
+		printk(KERN_ERR "\n%s(): Error in Line %u", __func__,
+			__LINE__);
+
+		return -1;
+	}
+
+	if (((req->src_rect.x + req->src_rect.w) > req->src.width) ||
+	    ((req->src_rect.y + req->src_rect.h) > req->src.height)) {
+		printk(KERN_ERR "\n%s(): Error in Line %u", __func__,
+			__LINE__);
+		return -1;
+	}
+
+	if (((req->dst_rect.x + req->dst_rect.w) > req->dst.width) ||
+	    ((req->dst_rect.y + req->dst_rect.h) > req->dst.height)) {
+		printk(KERN_ERR "\n%s(): Error in Line %u", __func__,
+			__LINE__);
+		return -1;
+	}
+
+	/*
+	 * scaling range check
+	 */
+	src_width = req->src_rect.w;
+	src_height = req->src_rect.h;
+
+	if (req->flags & MDP_ROT_90) {
+		dst_width = req->dst_rect.h;
+		dst_height = req->dst_rect.w;
 	} else {
-		*addr = 0;
+		dst_width = req->dst_rect.w;
+		dst_height = req->dst_rect.h;
 	}
-}
 
-static int send_blit(const struct mdp_info *mdp, struct mdp_blit_req *req,
-		     struct mdp_regs *regs, struct file *src_file,
-		     struct file *dst_file)
-{
-	mdp_writel(mdp, 1, 0x060);
-	mdp_writel(mdp, regs->src_rect, PPP_ADDR_SRC_ROI);
-	mdp_writel(mdp, regs->src0, PPP_ADDR_SRC0);
-	mdp_writel(mdp, regs->src1, PPP_ADDR_SRC1);
-	mdp_writel(mdp, regs->src_ystride, PPP_ADDR_SRC_YSTRIDE);
-	mdp_writel(mdp, regs->src_cfg, PPP_ADDR_SRC_CFG);
-	mdp_writel(mdp, regs->src_pack, PPP_ADDR_SRC_PACK_PATTERN);
+	switch (req->dst.format) {
+	case MDP_Y_CRCB_H2V2:
+	case MDP_Y_CBCR_H2V2:
+		src_width = (src_width / 2) * 2;
+		src_height = (src_height / 2) * 2;
+		dst_width = (src_width / 2) * 2;
+		dst_height = (src_height / 2) * 2;
+		break;
 
-	mdp_writel(mdp, regs->op, PPP_ADDR_OPERATION);
-	mdp_writel(mdp, regs->phasex_init, PPP_ADDR_PHASEX_INIT);
-	mdp_writel(mdp, regs->phasey_init, PPP_ADDR_PHASEY_INIT);
-	mdp_writel(mdp, regs->phasex_step, PPP_ADDR_PHASEX_STEP);
-	mdp_writel(mdp, regs->phasey_step, PPP_ADDR_PHASEY_STEP);
+	case MDP_Y_CRCB_H2V1:
+	case MDP_Y_CBCR_H2V1:
+	case MDP_YCRYCB_H2V1:
+		src_width = (src_width / 2) * 2;
+		dst_width = (src_width / 2) * 2;
+		break;
 
-	mdp_writel(mdp, (req->alpha << 24) | (req->transp_mask & 0xffffff),
-	       PPP_ADDR_ALPHA_TRANSP);
-
-	mdp_writel(mdp, regs->dst_cfg, PPP_ADDR_DST_CFG);
-	mdp_writel(mdp, regs->dst_pack, PPP_ADDR_DST_PACK_PATTERN);
-	mdp_writel(mdp, regs->dst_rect, PPP_ADDR_DST_ROI);
-	mdp_writel(mdp, regs->dst0, PPP_ADDR_DST0);
-	mdp_writel(mdp, regs->dst1, PPP_ADDR_DST1);
-	mdp_writel(mdp, regs->dst_ystride, PPP_ADDR_DST_YSTRIDE);
-
-	mdp_writel(mdp, regs->edge, PPP_ADDR_EDGE);
-	if (regs->op & PPP_OP_BLEND_ON) {
-		mdp_writel(mdp, regs->dst0, PPP_ADDR_BG0);
-		mdp_writel(mdp, regs->dst1, PPP_ADDR_BG1);
-		mdp_writel(mdp, regs->dst_ystride, PPP_ADDR_BG_YSTRIDE);
-		mdp_writel(mdp, src_img_cfg[req->dst.format], PPP_ADDR_BG_CFG);
-		mdp_writel(mdp, pack_pattern[req->dst.format],
-			   PPP_ADDR_BG_PACK_PATTERN);
+	default:
+		break;
 	}
-	flush_imgs(req, regs, src_file, dst_file);
-	mdp_writel(mdp, 0x1000, MDP_DISPLAY0_START);
+
+	if (((MDP_SCALE_Q_FACTOR * dst_width) / src_width >
+	     MDP_MAX_X_SCALE_FACTOR)
+	    || ((MDP_SCALE_Q_FACTOR * dst_width) / src_width <
+		MDP_MIN_X_SCALE_FACTOR)) {
+		printk(KERN_ERR "\n%s(): Error in Line %u", __func__,
+			__LINE__);
+		return -1;
+	}
+
+	if (((MDP_SCALE_Q_FACTOR * dst_height) / src_height >
+	     MDP_MAX_Y_SCALE_FACTOR)
+	    || ((MDP_SCALE_Q_FACTOR * dst_height) / src_height <
+		MDP_MIN_Y_SCALE_FACTOR)) {
+		printk(KERN_ERR "\n%s(): Error in Line %u", __func__,
+			__LINE__);
+		return -1;
+	}
 	return 0;
 }
 
-int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req,
-		 struct file *src_file, unsigned long src_start, unsigned long src_len,
-		 struct file *dst_file, unsigned long dst_start, unsigned long dst_len)
+int get_gem_img(struct mdp_img *img, unsigned long *start, unsigned long *len)
 {
-	struct mdp_regs regs = {0};
+	/* Set len to zero to appropriately error out if
+	   kgsl_gem_obj_addr fails */
 
-	if (unlikely(req->src.format >= MDP_IMGTYPE_LIMIT ||
-		     req->dst.format >= MDP_IMGTYPE_LIMIT)) {
-		printk(KERN_ERR "mpd_ppp: img is of wrong format\n");
-		return -EINVAL;
+	*len = 0;
+	return kgsl_gem_obj_addr(img->memory_id, (int) img->priv, start, len);
+}
+
+int get_img(struct mdp_img *img, struct fb_info *info, unsigned long *start,
+	    unsigned long *len, struct file **pp_file)
+{
+	int put_needed, ret = 0;
+	struct file *file;
+#ifdef CONFIG_ANDROID_PMEM
+	unsigned long vstart;
+#endif
+
+#ifdef CONFIG_ANDROID_PMEM
+	if (!get_pmem_file(img->memory_id, start, &vstart, len, pp_file))
+		return 0;
+#endif
+	file = fget_light(img->memory_id, &put_needed);
+	if (file == NULL)
+		return -1;
+
+	if (MAJOR(file->f_dentry->d_inode->i_rdev) == FB_MAJOR) {
+		*start = info->fix.smem_start;
+		*len = info->fix.smem_len;
+		*pp_file = file;
+	} else {
+		ret = -1;
+		fput_light(file, put_needed);
+	}
+	return ret;
+}
+
+
+void put_img(struct file *p_src_file)
+{
+#ifdef CONFIG_ANDROID_PMEM
+	if (p_src_file)
+		put_pmem_file(p_src_file);
+#endif
+}
+
+
+static int mdp_ppp_blit_addr(struct fb_info *info, struct mdp_blit_req *req,
+	unsigned long srcp0_start, unsigned long srcp0_len,
+	unsigned long srcp1_start, unsigned long srcp1_len,
+	unsigned long dst_start, unsigned long dst_len,
+	struct file *p_src_file, struct file *p_dst_file)
+{
+	MDPIBUF iBuf;
+	u32 dst_width, dst_height;
+	struct msm_fb_data_type *mfd = info->par;
+
+	if (req->dst.format == MDP_FB_FORMAT)
+		req->dst.format =  mfd->fb_imgType;
+	if (req->src.format == MDP_FB_FORMAT)
+		req->src.format = mfd->fb_imgType;
+
+	if (mdp_ppp_verify_req(req)) {
+		printk(KERN_ERR "mdp_ppp: invalid image!\n");
+		put_img(p_src_file);
+		put_img(p_dst_file);
+		return -1;
 	}
 
-	if (unlikely(req->src_rect.x > req->src.width ||
-		     req->src_rect.y > req->src.height ||
-		     req->dst_rect.x > req->dst.width ||
-		     req->dst_rect.y > req->dst.height)) {
-		printk(KERN_ERR "mpd_ppp: img rect is outside of img!\n");
-		return -EINVAL;
+	iBuf.ibuf_width = req->dst.width;
+	iBuf.ibuf_height = req->dst.height;
+	iBuf.bpp = bytes_per_pixel[req->dst.format];
+
+	iBuf.ibuf_type = req->dst.format;
+	iBuf.buf = (uint8 *) dst_start;
+	iBuf.buf += req->dst.offset;
+
+	iBuf.roi.lcd_x = req->dst_rect.x;
+	iBuf.roi.lcd_y = req->dst_rect.y;
+	iBuf.roi.dst_width = req->dst_rect.w;
+	iBuf.roi.dst_height = req->dst_rect.h;
+
+	iBuf.roi.x = req->src_rect.x;
+	iBuf.roi.width = req->src_rect.w;
+	iBuf.roi.y = req->src_rect.y;
+	iBuf.roi.height = req->src_rect.h;
+
+	iBuf.mdpImg.width = req->src.width;
+	iBuf.mdpImg.imgType = req->src.format;
+
+
+	iBuf.mdpImg.bmy_addr = (uint32 *) (srcp0_start + req->src.offset);
+	if (iBuf.mdpImg.imgType == MDP_Y_CBCR_H2V2_ADRENO)
+		iBuf.mdpImg.cbcr_addr =
+			(uint32 *) ((uint32) iBuf.mdpImg.bmy_addr +
+				ALIGN((ALIGN(req->src.width, 32) *
+				ALIGN(req->src.height, 32)), 4096));
+	else
+		iBuf.mdpImg.cbcr_addr = srcp1_start ? (uint32 *)srcp1_start :
+			(uint32 *) ((uint32) iBuf.mdpImg.bmy_addr +
+			req->src.width * req->src.height);
+
+	iBuf.mdpImg.mdpOp = MDPOP_NOP;
+
+	/* blending check */
+	if (req->transp_mask != MDP_TRANSP_NOP) {
+		iBuf.mdpImg.mdpOp |= MDPOP_TRANSP;
+		iBuf.mdpImg.tpVal = req->transp_mask;
+		iBuf.mdpImg.tpVal = mdp_calc_tpval(&iBuf.mdpImg);
+	} else {
+		iBuf.mdpImg.tpVal = 0;
 	}
 
-	/* set the src image configuration */
-	regs.src_cfg = src_img_cfg[req->src.format];
-	regs.src_cfg |= (req->src_rect.x & 0x1) ? PPP_SRC_BPP_ROI_ODD_X : 0;
-	regs.src_cfg |= (req->src_rect.y & 0x1) ? PPP_SRC_BPP_ROI_ODD_Y : 0;
-	regs.src_rect = (req->src_rect.h << 16) | req->src_rect.w;
-	regs.src_pack = pack_pattern[req->src.format];
-
-	/* set the dest image configuration */
-	regs.dst_cfg = dst_img_cfg[req->dst.format] | PPP_DST_OUT_SEL_AXI;
-	regs.dst_rect = (req->dst_rect.h << 16) | req->dst_rect.w;
-	regs.dst_pack = pack_pattern[req->dst.format];
-
-	/* set src, bpp, start pixel and ystride */
-	regs.src_bpp = bytes_per_pixel[req->src.format];
-	regs.src0 = src_start + req->src.offset;
-	regs.src_ystride = req->src.width * regs.src_bpp;
-	get_chroma_addr(&req->src, &req->src_rect, regs.src0, regs.src_bpp,
-			regs.src_cfg, &regs.src1, &regs.src_ystride);
-	regs.src0 += (req->src_rect.x + (req->src_rect.y * req->src.width)) *
-		      regs.src_bpp;
-
-	/* set dst, bpp, start pixel and ystride */
-	regs.dst_bpp = bytes_per_pixel[req->dst.format];
-	regs.dst0 = dst_start + req->dst.offset;
-	regs.dst_ystride = req->dst.width * regs.dst_bpp;
-	get_chroma_addr(&req->dst, &req->dst_rect, regs.dst0, regs.dst_bpp,
-			regs.dst_cfg, &regs.dst1, &regs.dst_ystride);
-	regs.dst0 += (req->dst_rect.x + (req->dst_rect.y * req->dst.width)) *
-		      regs.dst_bpp;
-
-	if (!valid_src_dst(src_start, src_len, dst_start, dst_len, req,
-			   &regs)) {
-		printk(KERN_ERR "mpd_ppp: final src or dst location is "
-			"invalid, are you trying to make an image too large "
-			"or to place it outside the screen?\n");
-		return -EINVAL;
+	req->alpha &= 0xff;
+	if (req->alpha < MDP_ALPHA_NOP) {
+		iBuf.mdpImg.mdpOp |= MDPOP_ALPHAB;
+		iBuf.mdpImg.alpha = req->alpha;
+	} else {
+		iBuf.mdpImg.alpha = 0xff;
 	}
 
-	/* set up operation register */
-	regs.op = 0;
-	blit_rotate(req, &regs);
-	blit_convert(req, &regs);
+	/* rotation check */
+	if (req->flags & MDP_FLIP_LR)
+		iBuf.mdpImg.mdpOp |= MDPOP_LR;
+	if (req->flags & MDP_FLIP_UD)
+		iBuf.mdpImg.mdpOp |= MDPOP_UD;
+	if (req->flags & MDP_ROT_90)
+		iBuf.mdpImg.mdpOp |= MDPOP_ROT90;
 	if (req->flags & MDP_DITHER)
-		regs.op |= PPP_OP_DITHER_EN;
-	blit_blend(req, &regs);
-	if (blit_scale(mdp, req, &regs)) {
-		printk(KERN_ERR "mpd_ppp: error computing scale for img.\n");
-		return -EINVAL;
-	}
-	blit_blur(mdp, req, &regs);
-	regs.op |= dst_op_chroma[req->dst.format] |
-		   src_op_chroma[req->src.format];
+		iBuf.mdpImg.mdpOp |= MDPOP_DITHER;
 
-	/* if the image is YCRYCB, the x and w must be even */
-	if (unlikely(req->src.format == MDP_YCRYCB_H2V1)) {
-		req->src_rect.x = req->src_rect.x & (~0x1);
-		req->src_rect.w = req->src_rect.w & (~0x1);
-		req->dst_rect.x = req->dst_rect.x & (~0x1);
-		req->dst_rect.w = req->dst_rect.w & (~0x1);
-	}
-	if (get_edge_cond(req, &regs))
+	if (req->flags & MDP_BLEND_FG_PREMULT) {
+#if defined(CONFIG_FB_MSM_MDP31) || defined(CONFIG_FB_MSM_MDP303)
+		iBuf.mdpImg.mdpOp |= MDPOP_FG_PM_ALPHA;
+#else
+		put_img(p_src_file);
+		put_img(p_dst_file);
 		return -EINVAL;
+#endif
+	}
 
-	send_blit(mdp, req, &regs, src_file, dst_file);
+	if (req->flags & MDP_DEINTERLACE) {
+#ifdef CONFIG_FB_MSM_MDP31
+		if ((req->src.format != MDP_Y_CBCR_H2V2) &&
+			(req->src.format != MDP_Y_CRCB_H2V2)) {
+#endif
+			put_img(p_src_file);
+			put_img(p_dst_file);
+			return -EINVAL;
+#ifdef CONFIG_FB_MSM_MDP31
+		}
+#endif
+	}
+
+	/* scale check */
+	if (req->flags & MDP_ROT_90) {
+		dst_width = req->dst_rect.h;
+		dst_height = req->dst_rect.w;
+	} else {
+		dst_width = req->dst_rect.w;
+		dst_height = req->dst_rect.h;
+	}
+
+	if ((iBuf.roi.width != dst_width) || (iBuf.roi.height != dst_height))
+		iBuf.mdpImg.mdpOp |= MDPOP_ASCALE;
+
+	if (req->flags & MDP_BLUR) {
+#ifdef CONFIG_FB_MSM_MDP31
+		if (req->flags & MDP_SHARPENING)
+			printk(KERN_WARNING
+				"mdp: MDP_SHARPENING is set with MDP_BLUR!\n");
+		req->flags |= MDP_SHARPENING;
+		req->sharpening_strength = -127;
+#else
+		iBuf.mdpImg.mdpOp |= MDPOP_ASCALE | MDPOP_BLUR;
+
+#endif
+	}
+
+	if (req->flags & MDP_SHARPENING) {
+#ifdef CONFIG_FB_MSM_MDP31
+		if ((req->sharpening_strength > 127) ||
+			(req->sharpening_strength < -127)) {
+			printk(KERN_ERR
+				"%s: sharpening strength out of range\n",
+				__func__);
+			put_img(p_src_file);
+			put_img(p_dst_file);
+			return -EINVAL;
+		}
+
+		iBuf.mdpImg.mdpOp |= MDPOP_ASCALE | MDPOP_SHARPENING;
+		iBuf.mdpImg.sp_value = req->sharpening_strength & 0xff;
+#else
+		put_img(p_src_file);
+		put_img(p_dst_file);
+		return -EINVAL;
+#endif
+	}
+
+	down(&mdp_ppp_mutex);
+	/* MDP cmd block enable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+#ifndef CONFIG_FB_MSM_MDP22
+	mdp_start_ppp(mfd, &iBuf, req, p_src_file, p_dst_file);
+#else
+	/* bg tile fetching HW workaround */
+	if (((iBuf.mdpImg.mdpOp & (MDPOP_TRANSP | MDPOP_ALPHAB)) ||
+	     (req->src.format == MDP_ARGB_8888) ||
+	     (req->src.format == MDP_BGRA_8888) ||
+	     (req->src.format == MDP_RGBA_8888)) &&
+	    (iBuf.mdpImg.mdpOp & MDPOP_ROT90) && (req->dst_rect.w <= 16)) {
+		int dst_h, src_w, i;
+		uint32 mdpOp = iBuf.mdpImg.mdpOp;
+
+		src_w = req->src_rect.w;
+		dst_h = iBuf.roi.dst_height;
+
+		for (i = 0; i < (req->dst_rect.h / 16); i++) {
+			/* this tile size */
+			iBuf.roi.dst_height = 16;
+			iBuf.roi.width =
+			    (16 * req->src_rect.w) / req->dst_rect.h;
+
+			/* if it's out of scale range... */
+			if (((MDP_SCALE_Q_FACTOR * iBuf.roi.dst_height) /
+			     iBuf.roi.width) > MDP_MAX_X_SCALE_FACTOR)
+				iBuf.roi.width =
+				    (MDP_SCALE_Q_FACTOR * iBuf.roi.dst_height) /
+				    MDP_MAX_X_SCALE_FACTOR;
+			else if (((MDP_SCALE_Q_FACTOR * iBuf.roi.dst_height) /
+				  iBuf.roi.width) < MDP_MIN_X_SCALE_FACTOR)
+				iBuf.roi.width =
+				    (MDP_SCALE_Q_FACTOR * iBuf.roi.dst_height) /
+				    MDP_MIN_X_SCALE_FACTOR;
+
+			mdp_start_ppp(mfd, &iBuf, req, p_src_file, p_dst_file);
+
+			/* next tile location */
+			iBuf.roi.lcd_y += 16;
+			iBuf.roi.x += iBuf.roi.width;
+
+			/* this is for a remainder update */
+			dst_h -= 16;
+			src_w -= iBuf.roi.width;
+			/* restore mdpOp since MDPOP_ASCALE have been cleared */
+			iBuf.mdpImg.mdpOp = mdpOp;
+		}
+
+		if ((dst_h < 0) || (src_w < 0))
+			printk
+			    ("msm_fb: mdp_blt_ex() unexpected result! line:%d\n",
+			     __LINE__);
+
+		/* remainder update */
+		if ((dst_h > 0) && (src_w > 0)) {
+			u32 tmp_v;
+
+			iBuf.roi.dst_height = dst_h;
+			iBuf.roi.width = src_w;
+
+			if (((MDP_SCALE_Q_FACTOR * iBuf.roi.dst_height) /
+			     iBuf.roi.width) > MDP_MAX_X_SCALE_FACTOR) {
+				tmp_v =
+				    (MDP_SCALE_Q_FACTOR * iBuf.roi.dst_height) /
+				    MDP_MAX_X_SCALE_FACTOR +
+				    (MDP_SCALE_Q_FACTOR * iBuf.roi.dst_height) %
+				    MDP_MAX_X_SCALE_FACTOR ? 1 : 0;
+
+				/* move x location as roi width gets bigger */
+				iBuf.roi.x -= tmp_v - iBuf.roi.width;
+				iBuf.roi.width = tmp_v;
+			} else
+			    if (((MDP_SCALE_Q_FACTOR * iBuf.roi.dst_height) /
+				 iBuf.roi.width) < MDP_MIN_X_SCALE_FACTOR) {
+				tmp_v =
+				    (MDP_SCALE_Q_FACTOR * iBuf.roi.dst_height) /
+				    MDP_MIN_X_SCALE_FACTOR +
+				    (MDP_SCALE_Q_FACTOR * iBuf.roi.dst_height) %
+				    MDP_MIN_X_SCALE_FACTOR ? 1 : 0;
+
+				/*
+				 * we don't move x location for continuity of
+				 * source image
+				 */
+				iBuf.roi.width = tmp_v;
+			}
+
+			mdp_start_ppp(mfd, &iBuf, req, p_src_file, p_dst_file);
+		}
+	} else {
+		mdp_start_ppp(mfd, &iBuf, req, p_src_file, p_dst_file);
+	}
+#endif
+
+	/* MDP cmd block disable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+	up(&mdp_ppp_mutex);
+
+	put_img(p_src_file);
+	put_img(p_dst_file);
 	return 0;
 }
+
+int mdp_ppp_blit(struct fb_info *info, struct mdp_blit_req *req)
+{
+	unsigned long src_start, dst_start;
+	unsigned long src_len = 0;
+	unsigned long dst_len = 0;
+	struct file *p_src_file = 0 , *p_dst_file = 0;
+
+	if (req->flags & MDP_BLIT_SRC_GEM)
+		get_gem_img(&req->src, &src_start, &src_len);
+	else
+		get_img(&req->src, info, &src_start, &src_len, &p_src_file);
+	if (src_len == 0) {
+		printk(KERN_ERR "mdp_ppp: could not retrieve image from "
+		       "memory\n");
+		return -EINVAL;
+	}
+	if (req->flags & MDP_BLIT_DST_GEM)
+		get_gem_img(&req->dst, &dst_start, &dst_len);
+	else
+		get_img(&req->dst, info, &dst_start, &dst_len, &p_dst_file);
+	if (dst_len == 0) {
+		put_img(p_src_file);
+		printk(KERN_ERR "mdp_ppp: could not retrieve image from "
+		       "memory\n");
+		return -EINVAL;
+	}
+
+	return mdp_ppp_blit_addr(info, req, src_start, src_len, 0, 0, dst_start,
+		dst_len, p_src_file, p_dst_file);
+}
+
+static struct mdp_blit_req overlay_req;
+static bool mdp_overlay_req_set;
+
+int mdp_ppp_v4l2_overlay_set(struct fb_info *info, struct mdp_overlay *req)
+{
+	memset(&overlay_req, 0, sizeof(struct mdp_blit_req));
+
+	overlay_req.src.width  = req->src.width;
+	overlay_req.src.height = req->src.height;
+	overlay_req.src.format = req->src.format;
+
+
+	overlay_req.dst.width  = req->dst_rect.w;
+	overlay_req.dst.height = req->dst_rect.h;
+	overlay_req.dst.format = MDP_FB_FORMAT;
+	overlay_req.transp_mask = req->transp_mask;
+	overlay_req.flags = req->flags;
+	overlay_req.alpha = req->alpha;
+
+	overlay_req.src_rect.x = req->src_rect.x;
+	overlay_req.src_rect.y = req->src_rect.y;
+	overlay_req.src_rect.w = req->src_rect.w;
+	overlay_req.src_rect.h = req->src_rect.h;
+	overlay_req.dst_rect.x = req->dst_rect.x;
+	overlay_req.dst_rect.y = req->dst_rect.y;
+	overlay_req.dst_rect.w = req->dst_rect.w;
+	overlay_req.dst_rect.h = req->dst_rect.h;
+	mdp_overlay_req_set = true;
+
+	pr_debug("%s: Overlay parameters:", __func__);
+	pr_debug("Src_Image (%u %u)\n", overlay_req.src.width,
+	overlay_req.src.height);
+
+	if (overlay_req.src.format == MDP_Y_CRCB_H2V2)
+		pr_debug("Overlay format MDP_Y_CRCB_H2V2\n");
+	else if (overlay_req.src.format == MDP_RGB_565)
+		pr_debug("Overlay format MDP_RGB_565\n");
+	else
+		pr_debug("Overlay format(%u) unknown\n",
+		overlay_req.src.format);
+
+	pr_debug("Dst_Image (%u %u)\n", overlay_req.dst.width,
+		overlay_req.dst.height);
+	pr_debug("Src rect: (%u,%u,%u,%u), Dst rect: (%u,%u,%u,%u)\n",
+		overlay_req.src_rect.x, overlay_req.src_rect.y,
+		overlay_req.src_rect.w, overlay_req.src_rect.h,
+		overlay_req.dst_rect.x, overlay_req.dst_rect.y,
+		overlay_req.dst_rect.w, overlay_req.dst_rect.h);
+	return 0;
+}
+
+int mdp_ppp_v4l2_overlay_clear(void)
+{
+	memset(&overlay_req, 0, sizeof(struct mdp_overlay));
+	mdp_overlay_req_set = false;
+	return 0;
+}
+
+int mdp_ppp_v4l2_overlay_play(struct fb_info *info,
+	unsigned long srcp0_addr, unsigned long srcp0_size,
+	unsigned long srcp1_addr, unsigned long srcp1_size)
+{
+	int ret;
+
+	if (!mdp_overlay_req_set) {
+		pr_err("mdp_ppp:v4l2:No overlay set, ignore play req\n");
+		return -EINVAL;
+	}
+
+	overlay_req.dst.width = info->var.xres;
+	overlay_req.dst.height = info->var.yres;
+
+	ret = mdp_ppp_blit_addr(info, &overlay_req,
+		srcp0_addr, srcp0_size, srcp1_addr, srcp1_size,
+		info->fix.smem_start, info->fix.smem_len, NULL, NULL);
+
+	if (ret)
+		pr_err("%s:Blitting overlay failed(%d)\n", __func__, ret);
+
+	return ret;
+}
diff --git a/drivers/video/msm/mdp_ppp.h b/drivers/video/msm/mdp_ppp.h
new file mode 100644
index 0000000..e045643
--- /dev/null
+++ b/drivers/video/msm/mdp_ppp.h
@@ -0,0 +1,82 @@
+/* drivers/video/msm/mdp_ppp.h
+ *
+ * Copyright (C) 2009 Google Incorporated
+ *
+ * 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.
+ */
+
+#ifndef _VIDEO_MSM_MDP_PPP_H_
+#define _VIDEO_MSM_MDP_PPP_H_
+
+#include <linux/types.h>
+
+struct ppp_regs {
+	uint32_t src0;
+	uint32_t src1;
+	uint32_t dst0;
+	uint32_t dst1;
+	uint32_t src_cfg;
+	uint32_t dst_cfg;
+	uint32_t src_pack;
+	uint32_t dst_pack;
+	uint32_t src_rect;
+	uint32_t dst_rect;
+	uint32_t src_ystride;
+	uint32_t dst_ystride;
+	uint32_t op;
+	uint32_t src_bpp;
+	uint32_t dst_bpp;
+	uint32_t edge;
+	uint32_t phasex_init;
+	uint32_t phasey_init;
+	uint32_t phasex_step;
+	uint32_t phasey_step;
+
+	uint32_t bg0;
+	uint32_t bg1;
+	uint32_t bg_cfg;
+	uint32_t bg_bpp;
+	uint32_t bg_pack;
+	uint32_t bg_ystride;
+
+#ifdef CONFIG_MSM_MDP31
+	uint32_t src_xy;
+	uint32_t src_img_sz;
+	uint32_t dst_xy;
+	uint32_t bg_xy;
+	uint32_t bg_img_sz;
+	uint32_t bg_alpha_sel;
+
+	uint32_t scale_cfg;
+	uint32_t csc_cfg;
+#endif
+};
+
+struct mdp_info;
+struct mdp_rect;
+struct mdp_blit_req;
+
+void mdp_ppp_init_scale(const struct mdp_info *mdp);
+int mdp_ppp_cfg_scale(const struct mdp_info *mdp, struct ppp_regs *regs,
+		      struct mdp_rect *src_rect, struct mdp_rect *dst_rect,
+		      uint32_t src_format, uint32_t dst_format);
+int mdp_ppp_load_blur(const struct mdp_info *mdp);
+
+#ifndef CONFIG_MSM_MDP31
+int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, struct ppp_regs *regs);
+#else
+static inline int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req,
+				 struct ppp_regs *regs)
+{
+	return 0;
+}
+#endif
+
+#endif /* _VIDEO_MSM_MDP_PPP_H_ */
diff --git a/drivers/video/msm/mdp_ppp22.c b/drivers/video/msm/mdp_ppp22.c
new file mode 100644
index 0000000..9016f0a
--- /dev/null
+++ b/drivers/video/msm/mdp_ppp22.c
@@ -0,0 +1,1091 @@
+/* drivers/video/msm/mdp_ppp22.c
+ *
+ * Copyright (C) 2007 Code Aurora Forum. All rights reserved.
+ * Copyright (C) 2007 Google Incorporated
+ *
+ * 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/kernel.h>
+#include <asm/io.h>
+#include <linux/msm_mdp.h>
+
+#include "mdp_hw.h"
+#include "mdp_ppp.h"
+
+struct mdp_table_entry {
+	uint32_t reg;
+	uint32_t val;
+};
+
+enum {
+	MDP_DOWNSCALE_PT2TOPT4,
+	MDP_DOWNSCALE_PT4TOPT6,
+	MDP_DOWNSCALE_PT6TOPT8,
+	MDP_DOWNSCALE_PT8TO1,
+	MDP_DOWNSCALE_MAX,
+
+	/* not technically in the downscale table list */
+	MDP_DOWNSCALE_BLUR,
+};
+
+static int downscale_x_table;
+static int downscale_y_table;
+
+static struct mdp_table_entry mdp_upscale_table[] = {
+	{ 0x5fffc, 0x0 },
+	{ 0x50200, 0x7fc00000 },
+	{ 0x5fffc, 0xff80000d },
+	{ 0x50204, 0x7ec003f9 },
+	{ 0x5fffc, 0xfec0001c },
+	{ 0x50208, 0x7d4003f3 },
+	{ 0x5fffc, 0xfe40002b },
+	{ 0x5020c, 0x7b8003ed },
+	{ 0x5fffc, 0xfd80003c },
+	{ 0x50210, 0x794003e8 },
+	{ 0x5fffc, 0xfcc0004d },
+	{ 0x50214, 0x76c003e4 },
+	{ 0x5fffc, 0xfc40005f },
+	{ 0x50218, 0x73c003e0 },
+	{ 0x5fffc, 0xfb800071 },
+	{ 0x5021c, 0x708003de },
+	{ 0x5fffc, 0xfac00085 },
+	{ 0x50220, 0x6d0003db },
+	{ 0x5fffc, 0xfa000098 },
+	{ 0x50224, 0x698003d9 },
+	{ 0x5fffc, 0xf98000ac },
+	{ 0x50228, 0x654003d8 },
+	{ 0x5fffc, 0xf8c000c1 },
+	{ 0x5022c, 0x610003d7 },
+	{ 0x5fffc, 0xf84000d5 },
+	{ 0x50230, 0x5c8003d7 },
+	{ 0x5fffc, 0xf7c000e9 },
+	{ 0x50234, 0x580003d7 },
+	{ 0x5fffc, 0xf74000fd },
+	{ 0x50238, 0x534003d8 },
+	{ 0x5fffc, 0xf6c00112 },
+	{ 0x5023c, 0x4e8003d8 },
+	{ 0x5fffc, 0xf6800126 },
+	{ 0x50240, 0x494003da },
+	{ 0x5fffc, 0xf600013a },
+	{ 0x50244, 0x448003db },
+	{ 0x5fffc, 0xf600014d },
+	{ 0x50248, 0x3f4003dd },
+	{ 0x5fffc, 0xf5c00160 },
+	{ 0x5024c, 0x3a4003df },
+	{ 0x5fffc, 0xf5c00172 },
+	{ 0x50250, 0x354003e1 },
+	{ 0x5fffc, 0xf5c00184 },
+	{ 0x50254, 0x304003e3 },
+	{ 0x5fffc, 0xf6000195 },
+	{ 0x50258, 0x2b0003e6 },
+	{ 0x5fffc, 0xf64001a6 },
+	{ 0x5025c, 0x260003e8 },
+	{ 0x5fffc, 0xf6c001b4 },
+	{ 0x50260, 0x214003eb },
+	{ 0x5fffc, 0xf78001c2 },
+	{ 0x50264, 0x1c4003ee },
+	{ 0x5fffc, 0xf80001cf },
+	{ 0x50268, 0x17c003f1 },
+	{ 0x5fffc, 0xf90001db },
+	{ 0x5026c, 0x134003f3 },
+	{ 0x5fffc, 0xfa0001e5 },
+	{ 0x50270, 0xf0003f6 },
+	{ 0x5fffc, 0xfb4001ee },
+	{ 0x50274, 0xac003f9 },
+	{ 0x5fffc, 0xfcc001f5 },
+	{ 0x50278, 0x70003fb },
+	{ 0x5fffc, 0xfe4001fb },
+	{ 0x5027c, 0x34003fe },
+};
+
+static struct mdp_table_entry mdp_downscale_x_table_PT2TOPT4[] = {
+	{ 0x5fffc, 0x740008c },
+	{ 0x50280, 0x33800088 },
+	{ 0x5fffc, 0x800008e },
+	{ 0x50284, 0x33400084 },
+	{ 0x5fffc, 0x8400092 },
+	{ 0x50288, 0x33000080 },
+	{ 0x5fffc, 0x9000094 },
+	{ 0x5028c, 0x3300007b },
+	{ 0x5fffc, 0x9c00098 },
+	{ 0x50290, 0x32400077 },
+	{ 0x5fffc, 0xa40009b },
+	{ 0x50294, 0x32000073 },
+	{ 0x5fffc, 0xb00009d },
+	{ 0x50298,  0x31c0006f },
+	{ 0x5fffc,  0xbc000a0 },
+	{ 0x5029c,  0x3140006b },
+	{ 0x5fffc,  0xc8000a2 },
+	{ 0x502a0,  0x31000067 },
+	{ 0x5fffc,  0xd8000a5 },
+	{ 0x502a4,  0x30800062 },
+	{ 0x5fffc,  0xe4000a8 },
+	{ 0x502a8,  0x2fc0005f },
+	{ 0x5fffc,  0xec000aa },
+	{ 0x502ac,  0x2fc0005b },
+	{ 0x5fffc,  0xf8000ad },
+	{ 0x502b0,  0x2f400057 },
+	{ 0x5fffc,  0x108000b0 },
+	{ 0x502b4,  0x2e400054 },
+	{ 0x5fffc,  0x114000b2 },
+	{ 0x502b8,  0x2e000050 },
+	{ 0x5fffc,  0x124000b4 },
+	{ 0x502bc,  0x2d80004c },
+	{ 0x5fffc,  0x130000b6 },
+	{ 0x502c0,  0x2d000049 },
+	{ 0x5fffc,  0x140000b8 },
+	{ 0x502c4,  0x2c800045 },
+	{ 0x5fffc,  0x150000b9 },
+	{ 0x502c8,  0x2c000042 },
+	{ 0x5fffc,  0x15c000bd },
+	{ 0x502cc,  0x2b40003e },
+	{ 0x5fffc,  0x16c000bf },
+	{ 0x502d0,  0x2a80003b },
+	{ 0x5fffc,  0x17c000bf },
+	{ 0x502d4,  0x2a000039 },
+	{ 0x5fffc,  0x188000c2 },
+	{ 0x502d8,  0x29400036 },
+	{ 0x5fffc,  0x19c000c4 },
+	{ 0x502dc,  0x28800032 },
+	{ 0x5fffc,  0x1ac000c5 },
+	{ 0x502e0,  0x2800002f },
+	{ 0x5fffc,  0x1bc000c7 },
+	{ 0x502e4,  0x2740002c },
+	{ 0x5fffc,  0x1cc000c8 },
+	{ 0x502e8,  0x26c00029 },
+	{ 0x5fffc,  0x1dc000c9 },
+	{ 0x502ec,  0x26000027 },
+	{ 0x5fffc,  0x1ec000cc },
+	{ 0x502f0,  0x25000024 },
+	{ 0x5fffc,  0x200000cc },
+	{ 0x502f4,  0x24800021 },
+	{ 0x5fffc,  0x210000cd },
+	{ 0x502f8,  0x23800020 },
+	{ 0x5fffc,  0x220000ce },
+	{ 0x502fc,  0x2300001d },
+};
+
+static struct mdp_table_entry mdp_downscale_x_table_PT4TOPT6[] = {
+	{ 0x5fffc,  0x740008c },
+	{ 0x50280,  0x33800088 },
+	{ 0x5fffc,  0x800008e },
+	{ 0x50284,  0x33400084 },
+	{ 0x5fffc,  0x8400092 },
+	{ 0x50288,  0x33000080 },
+	{ 0x5fffc,  0x9000094 },
+	{ 0x5028c,  0x3300007b },
+	{ 0x5fffc,  0x9c00098 },
+	{ 0x50290,  0x32400077 },
+	{ 0x5fffc,  0xa40009b },
+	{ 0x50294,  0x32000073 },
+	{ 0x5fffc,  0xb00009d },
+	{ 0x50298,  0x31c0006f },
+	{ 0x5fffc,  0xbc000a0 },
+	{ 0x5029c,  0x3140006b },
+	{ 0x5fffc,  0xc8000a2 },
+	{ 0x502a0,  0x31000067 },
+	{ 0x5fffc,  0xd8000a5 },
+	{ 0x502a4,  0x30800062 },
+	{ 0x5fffc,  0xe4000a8 },
+	{ 0x502a8,  0x2fc0005f },
+	{ 0x5fffc,  0xec000aa },
+	{ 0x502ac,  0x2fc0005b },
+	{ 0x5fffc,  0xf8000ad },
+	{ 0x502b0,  0x2f400057 },
+	{ 0x5fffc,  0x108000b0 },
+	{ 0x502b4,  0x2e400054 },
+	{ 0x5fffc,  0x114000b2 },
+	{ 0x502b8,  0x2e000050 },
+	{ 0x5fffc,  0x124000b4 },
+	{ 0x502bc,  0x2d80004c },
+	{ 0x5fffc,  0x130000b6 },
+	{ 0x502c0,  0x2d000049 },
+	{ 0x5fffc,  0x140000b8 },
+	{ 0x502c4,  0x2c800045 },
+	{ 0x5fffc,  0x150000b9 },
+	{ 0x502c8,  0x2c000042 },
+	{ 0x5fffc,  0x15c000bd },
+	{ 0x502cc,  0x2b40003e },
+	{ 0x5fffc,  0x16c000bf },
+	{ 0x502d0,  0x2a80003b },
+	{ 0x5fffc,  0x17c000bf },
+	{ 0x502d4,  0x2a000039 },
+	{ 0x5fffc,  0x188000c2 },
+	{ 0x502d8,  0x29400036 },
+	{ 0x5fffc,  0x19c000c4 },
+	{ 0x502dc,  0x28800032 },
+	{ 0x5fffc,  0x1ac000c5 },
+	{ 0x502e0,  0x2800002f },
+	{ 0x5fffc,  0x1bc000c7 },
+	{ 0x502e4,  0x2740002c },
+	{ 0x5fffc,  0x1cc000c8 },
+	{ 0x502e8,  0x26c00029 },
+	{ 0x5fffc,  0x1dc000c9 },
+	{ 0x502ec,  0x26000027 },
+	{ 0x5fffc,  0x1ec000cc },
+	{ 0x502f0,  0x25000024 },
+	{ 0x5fffc,  0x200000cc },
+	{ 0x502f4,  0x24800021 },
+	{ 0x5fffc,  0x210000cd },
+	{ 0x502f8,  0x23800020 },
+	{ 0x5fffc,  0x220000ce },
+	{ 0x502fc,  0x2300001d },
+};
+
+static struct mdp_table_entry mdp_downscale_x_table_PT6TOPT8[] = {
+	{ 0x5fffc,  0xfe000070 },
+	{ 0x50280,  0x4bc00068 },
+	{ 0x5fffc,  0xfe000078 },
+	{ 0x50284,  0x4bc00060 },
+	{ 0x5fffc,  0xfe000080 },
+	{ 0x50288,  0x4b800059 },
+	{ 0x5fffc,  0xfe000089 },
+	{ 0x5028c,  0x4b000052 },
+	{ 0x5fffc,  0xfe400091 },
+	{ 0x50290,  0x4a80004b },
+	{ 0x5fffc,  0xfe40009a },
+	{ 0x50294,  0x4a000044 },
+	{ 0x5fffc,  0xfe8000a3 },
+	{ 0x50298,  0x4940003d },
+	{ 0x5fffc,  0xfec000ac },
+	{ 0x5029c,  0x48400037 },
+	{ 0x5fffc,  0xff0000b4 },
+	{ 0x502a0,  0x47800031 },
+	{ 0x5fffc,  0xff8000bd },
+	{ 0x502a4,  0x4640002b },
+	{ 0x5fffc,  0xc5 },
+	{ 0x502a8,  0x45000026 },
+	{ 0x5fffc,  0x8000ce },
+	{ 0x502ac,  0x43800021 },
+	{ 0x5fffc,  0x10000d6 },
+	{ 0x502b0,  0x4240001c },
+	{ 0x5fffc,  0x18000df },
+	{ 0x502b4,  0x40800018 },
+	{ 0x5fffc,  0x24000e6 },
+	{ 0x502b8,  0x3f000014 },
+	{ 0x5fffc,  0x30000ee },
+	{ 0x502bc,  0x3d400010 },
+	{ 0x5fffc,  0x40000f5 },
+	{ 0x502c0,  0x3b80000c },
+	{ 0x5fffc,  0x50000fc },
+	{ 0x502c4,  0x39800009 },
+	{ 0x5fffc,  0x6000102 },
+	{ 0x502c8,  0x37c00006 },
+	{ 0x5fffc,  0x7000109 },
+	{ 0x502cc,  0x35800004 },
+	{ 0x5fffc,  0x840010e },
+	{ 0x502d0,  0x33800002 },
+	{ 0x5fffc,  0x9800114 },
+	{ 0x502d4,  0x31400000 },
+	{ 0x5fffc,  0xac00119 },
+	{ 0x502d8,  0x2f4003fe },
+	{ 0x5fffc,  0xc40011e },
+	{ 0x502dc,  0x2d0003fc },
+	{ 0x5fffc,  0xdc00121 },
+	{ 0x502e0,  0x2b0003fb },
+	{ 0x5fffc,  0xf400125 },
+	{ 0x502e4,  0x28c003fa },
+	{ 0x5fffc,  0x11000128 },
+	{ 0x502e8,  0x268003f9 },
+	{ 0x5fffc,  0x12c0012a },
+	{ 0x502ec,  0x244003f9 },
+	{ 0x5fffc,  0x1480012c },
+	{ 0x502f0,  0x224003f8 },
+	{ 0x5fffc,  0x1640012e },
+	{ 0x502f4,  0x200003f8 },
+	{ 0x5fffc,  0x1800012f },
+	{ 0x502f8,  0x1e0003f8 },
+	{ 0x5fffc,  0x1a00012f },
+	{ 0x502fc,  0x1c0003f8 },
+};
+
+static struct mdp_table_entry mdp_downscale_x_table_PT8TO1[] = {
+	{ 0x5fffc,  0x0 },
+	{ 0x50280,  0x7fc00000 },
+	{ 0x5fffc,  0xff80000d },
+	{ 0x50284,  0x7ec003f9 },
+	{ 0x5fffc,  0xfec0001c },
+	{ 0x50288,  0x7d4003f3 },
+	{ 0x5fffc,  0xfe40002b },
+	{ 0x5028c,  0x7b8003ed },
+	{ 0x5fffc,  0xfd80003c },
+	{ 0x50290,  0x794003e8 },
+	{ 0x5fffc,  0xfcc0004d },
+	{ 0x50294,  0x76c003e4 },
+	{ 0x5fffc,  0xfc40005f },
+	{ 0x50298,  0x73c003e0 },
+	{ 0x5fffc,  0xfb800071 },
+	{ 0x5029c,  0x708003de },
+	{ 0x5fffc,  0xfac00085 },
+	{ 0x502a0,  0x6d0003db },
+	{ 0x5fffc,  0xfa000098 },
+	{ 0x502a4,  0x698003d9 },
+	{ 0x5fffc,  0xf98000ac },
+	{ 0x502a8,  0x654003d8 },
+	{ 0x5fffc,  0xf8c000c1 },
+	{ 0x502ac,  0x610003d7 },
+	{ 0x5fffc,  0xf84000d5 },
+	{ 0x502b0,  0x5c8003d7 },
+	{ 0x5fffc,  0xf7c000e9 },
+	{ 0x502b4,  0x580003d7 },
+	{ 0x5fffc,  0xf74000fd },
+	{ 0x502b8,  0x534003d8 },
+	{ 0x5fffc,  0xf6c00112 },
+	{ 0x502bc,  0x4e8003d8 },
+	{ 0x5fffc,  0xf6800126 },
+	{ 0x502c0,  0x494003da },
+	{ 0x5fffc,  0xf600013a },
+	{ 0x502c4,  0x448003db },
+	{ 0x5fffc,  0xf600014d },
+	{ 0x502c8,  0x3f4003dd },
+	{ 0x5fffc,  0xf5c00160 },
+	{ 0x502cc,  0x3a4003df },
+	{ 0x5fffc,  0xf5c00172 },
+	{ 0x502d0,  0x354003e1 },
+	{ 0x5fffc,  0xf5c00184 },
+	{ 0x502d4,  0x304003e3 },
+	{ 0x5fffc,  0xf6000195 },
+	{ 0x502d8,  0x2b0003e6 },
+	{ 0x5fffc,  0xf64001a6 },
+	{ 0x502dc,  0x260003e8 },
+	{ 0x5fffc,  0xf6c001b4 },
+	{ 0x502e0,  0x214003eb },
+	{ 0x5fffc,  0xf78001c2 },
+	{ 0x502e4,  0x1c4003ee },
+	{ 0x5fffc,  0xf80001cf },
+	{ 0x502e8,  0x17c003f1 },
+	{ 0x5fffc,  0xf90001db },
+	{ 0x502ec,  0x134003f3 },
+	{ 0x5fffc,  0xfa0001e5 },
+	{ 0x502f0,  0xf0003f6 },
+	{ 0x5fffc,  0xfb4001ee },
+	{ 0x502f4,  0xac003f9 },
+	{ 0x5fffc,  0xfcc001f5 },
+	{ 0x502f8,  0x70003fb },
+	{ 0x5fffc,  0xfe4001fb },
+	{ 0x502fc,  0x34003fe },
+};
+
+struct mdp_table_entry *mdp_downscale_x_table[MDP_DOWNSCALE_MAX] = {
+	[MDP_DOWNSCALE_PT2TOPT4] = mdp_downscale_x_table_PT2TOPT4,
+	[MDP_DOWNSCALE_PT4TOPT6] = mdp_downscale_x_table_PT4TOPT6,
+	[MDP_DOWNSCALE_PT6TOPT8] = mdp_downscale_x_table_PT6TOPT8,
+	[MDP_DOWNSCALE_PT8TO1]  = mdp_downscale_x_table_PT8TO1,
+};
+
+static struct mdp_table_entry mdp_downscale_y_table_PT2TOPT4[] = {
+	{ 0x5fffc,  0x740008c },
+	{ 0x50300,  0x33800088 },
+	{ 0x5fffc,  0x800008e },
+	{ 0x50304,  0x33400084 },
+	{ 0x5fffc,  0x8400092 },
+	{ 0x50308,  0x33000080 },
+	{ 0x5fffc,  0x9000094 },
+	{ 0x5030c,  0x3300007b },
+	{ 0x5fffc,  0x9c00098 },
+	{ 0x50310,  0x32400077 },
+	{ 0x5fffc,  0xa40009b },
+	{ 0x50314,  0x32000073 },
+	{ 0x5fffc,  0xb00009d },
+	{ 0x50318,  0x31c0006f },
+	{ 0x5fffc,  0xbc000a0 },
+	{ 0x5031c,  0x3140006b },
+	{ 0x5fffc,  0xc8000a2 },
+	{ 0x50320,  0x31000067 },
+	{ 0x5fffc,  0xd8000a5 },
+	{ 0x50324,  0x30800062 },
+	{ 0x5fffc,  0xe4000a8 },
+	{ 0x50328,  0x2fc0005f },
+	{ 0x5fffc,  0xec000aa },
+	{ 0x5032c,  0x2fc0005b },
+	{ 0x5fffc,  0xf8000ad },
+	{ 0x50330,  0x2f400057 },
+	{ 0x5fffc,  0x108000b0 },
+	{ 0x50334,  0x2e400054 },
+	{ 0x5fffc,  0x114000b2 },
+	{ 0x50338,  0x2e000050 },
+	{ 0x5fffc,  0x124000b4 },
+	{ 0x5033c,  0x2d80004c },
+	{ 0x5fffc,  0x130000b6 },
+	{ 0x50340,  0x2d000049 },
+	{ 0x5fffc,  0x140000b8 },
+	{ 0x50344,  0x2c800045 },
+	{ 0x5fffc,  0x150000b9 },
+	{ 0x50348,  0x2c000042 },
+	{ 0x5fffc,  0x15c000bd },
+	{ 0x5034c,  0x2b40003e },
+	{ 0x5fffc,  0x16c000bf },
+	{ 0x50350,  0x2a80003b },
+	{ 0x5fffc,  0x17c000bf },
+	{ 0x50354,  0x2a000039 },
+	{ 0x5fffc,  0x188000c2 },
+	{ 0x50358,  0x29400036 },
+	{ 0x5fffc,  0x19c000c4 },
+	{ 0x5035c,  0x28800032 },
+	{ 0x5fffc,  0x1ac000c5 },
+	{ 0x50360,  0x2800002f },
+	{ 0x5fffc,  0x1bc000c7 },
+	{ 0x50364,  0x2740002c },
+	{ 0x5fffc,  0x1cc000c8 },
+	{ 0x50368,  0x26c00029 },
+	{ 0x5fffc,  0x1dc000c9 },
+	{ 0x5036c,  0x26000027 },
+	{ 0x5fffc,  0x1ec000cc },
+	{ 0x50370,  0x25000024 },
+	{ 0x5fffc,  0x200000cc },
+	{ 0x50374,  0x24800021 },
+	{ 0x5fffc,  0x210000cd },
+	{ 0x50378,  0x23800020 },
+	{ 0x5fffc,  0x220000ce },
+	{ 0x5037c,  0x2300001d },
+};
+
+static struct mdp_table_entry mdp_downscale_y_table_PT4TOPT6[] = {
+	{ 0x5fffc,  0x740008c },
+	{ 0x50300,  0x33800088 },
+	{ 0x5fffc,  0x800008e },
+	{ 0x50304,  0x33400084 },
+	{ 0x5fffc,  0x8400092 },
+	{ 0x50308,  0x33000080 },
+	{ 0x5fffc,  0x9000094 },
+	{ 0x5030c,  0x3300007b },
+	{ 0x5fffc,  0x9c00098 },
+	{ 0x50310,  0x32400077 },
+	{ 0x5fffc,  0xa40009b },
+	{ 0x50314,  0x32000073 },
+	{ 0x5fffc,  0xb00009d },
+	{ 0x50318,  0x31c0006f },
+	{ 0x5fffc,  0xbc000a0 },
+	{ 0x5031c,  0x3140006b },
+	{ 0x5fffc,  0xc8000a2 },
+	{ 0x50320,  0x31000067 },
+	{ 0x5fffc,  0xd8000a5 },
+	{ 0x50324,  0x30800062 },
+	{ 0x5fffc,  0xe4000a8 },
+	{ 0x50328,  0x2fc0005f },
+	{ 0x5fffc,  0xec000aa },
+	{ 0x5032c,  0x2fc0005b },
+	{ 0x5fffc,  0xf8000ad },
+	{ 0x50330,  0x2f400057 },
+	{ 0x5fffc,  0x108000b0 },
+	{ 0x50334,  0x2e400054 },
+	{ 0x5fffc,  0x114000b2 },
+	{ 0x50338,  0x2e000050 },
+	{ 0x5fffc,  0x124000b4 },
+	{ 0x5033c,  0x2d80004c },
+	{ 0x5fffc,  0x130000b6 },
+	{ 0x50340,  0x2d000049 },
+	{ 0x5fffc,  0x140000b8 },
+	{ 0x50344,  0x2c800045 },
+	{ 0x5fffc,  0x150000b9 },
+	{ 0x50348,  0x2c000042 },
+	{ 0x5fffc,  0x15c000bd },
+	{ 0x5034c,  0x2b40003e },
+	{ 0x5fffc,  0x16c000bf },
+	{ 0x50350,  0x2a80003b },
+	{ 0x5fffc,  0x17c000bf },
+	{ 0x50354,  0x2a000039 },
+	{ 0x5fffc,  0x188000c2 },
+	{ 0x50358,  0x29400036 },
+	{ 0x5fffc,  0x19c000c4 },
+	{ 0x5035c,  0x28800032 },
+	{ 0x5fffc,  0x1ac000c5 },
+	{ 0x50360,  0x2800002f },
+	{ 0x5fffc,  0x1bc000c7 },
+	{ 0x50364,  0x2740002c },
+	{ 0x5fffc,  0x1cc000c8 },
+	{ 0x50368,  0x26c00029 },
+	{ 0x5fffc,  0x1dc000c9 },
+	{ 0x5036c,  0x26000027 },
+	{ 0x5fffc,  0x1ec000cc },
+	{ 0x50370,  0x25000024 },
+	{ 0x5fffc,  0x200000cc },
+	{ 0x50374,  0x24800021 },
+	{ 0x5fffc,  0x210000cd },
+	{ 0x50378,  0x23800020 },
+	{ 0x5fffc,  0x220000ce },
+	{ 0x5037c,  0x2300001d },
+};
+
+static struct mdp_table_entry mdp_downscale_y_table_PT6TOPT8[] = {
+	{ 0x5fffc,  0xfe000070 },
+	{ 0x50300,  0x4bc00068 },
+	{ 0x5fffc,  0xfe000078 },
+	{ 0x50304,  0x4bc00060 },
+	{ 0x5fffc,  0xfe000080 },
+	{ 0x50308,  0x4b800059 },
+	{ 0x5fffc,  0xfe000089 },
+	{ 0x5030c,  0x4b000052 },
+	{ 0x5fffc,  0xfe400091 },
+	{ 0x50310,  0x4a80004b },
+	{ 0x5fffc,  0xfe40009a },
+	{ 0x50314,  0x4a000044 },
+	{ 0x5fffc,  0xfe8000a3 },
+	{ 0x50318,  0x4940003d },
+	{ 0x5fffc,  0xfec000ac },
+	{ 0x5031c,  0x48400037 },
+	{ 0x5fffc,  0xff0000b4 },
+	{ 0x50320,  0x47800031 },
+	{ 0x5fffc,  0xff8000bd },
+	{ 0x50324,  0x4640002b },
+	{ 0x5fffc,  0xc5 },
+	{ 0x50328,  0x45000026 },
+	{ 0x5fffc,  0x8000ce },
+	{ 0x5032c,  0x43800021 },
+	{ 0x5fffc,  0x10000d6 },
+	{ 0x50330,  0x4240001c },
+	{ 0x5fffc,  0x18000df },
+	{ 0x50334,  0x40800018 },
+	{ 0x5fffc,  0x24000e6 },
+	{ 0x50338,  0x3f000014 },
+	{ 0x5fffc,  0x30000ee },
+	{ 0x5033c,  0x3d400010 },
+	{ 0x5fffc,  0x40000f5 },
+	{ 0x50340,  0x3b80000c },
+	{ 0x5fffc,  0x50000fc },
+	{ 0x50344,  0x39800009 },
+	{ 0x5fffc,  0x6000102 },
+	{ 0x50348,  0x37c00006 },
+	{ 0x5fffc,  0x7000109 },
+	{ 0x5034c,  0x35800004 },
+	{ 0x5fffc,  0x840010e },
+	{ 0x50350,  0x33800002 },
+	{ 0x5fffc,  0x9800114 },
+	{ 0x50354,  0x31400000 },
+	{ 0x5fffc,  0xac00119 },
+	{ 0x50358,  0x2f4003fe },
+	{ 0x5fffc,  0xc40011e },
+	{ 0x5035c,  0x2d0003fc },
+	{ 0x5fffc,  0xdc00121 },
+	{ 0x50360,  0x2b0003fb },
+	{ 0x5fffc,  0xf400125 },
+	{ 0x50364,  0x28c003fa },
+	{ 0x5fffc,  0x11000128 },
+	{ 0x50368,  0x268003f9 },
+	{ 0x5fffc,  0x12c0012a },
+	{ 0x5036c,  0x244003f9 },
+	{ 0x5fffc,  0x1480012c },
+	{ 0x50370,  0x224003f8 },
+	{ 0x5fffc,  0x1640012e },
+	{ 0x50374,  0x200003f8 },
+	{ 0x5fffc,  0x1800012f },
+	{ 0x50378,  0x1e0003f8 },
+	{ 0x5fffc,  0x1a00012f },
+	{ 0x5037c,  0x1c0003f8 },
+};
+
+static struct mdp_table_entry mdp_downscale_y_table_PT8TO1[] = {
+	{ 0x5fffc,  0x0 },
+	{ 0x50300,  0x7fc00000 },
+	{ 0x5fffc,  0xff80000d },
+	{ 0x50304,  0x7ec003f9 },
+	{ 0x5fffc,  0xfec0001c },
+	{ 0x50308,  0x7d4003f3 },
+	{ 0x5fffc,  0xfe40002b },
+	{ 0x5030c,  0x7b8003ed },
+	{ 0x5fffc,  0xfd80003c },
+	{ 0x50310,  0x794003e8 },
+	{ 0x5fffc,  0xfcc0004d },
+	{ 0x50314,  0x76c003e4 },
+	{ 0x5fffc,  0xfc40005f },
+	{ 0x50318,  0x73c003e0 },
+	{ 0x5fffc,  0xfb800071 },
+	{ 0x5031c,  0x708003de },
+	{ 0x5fffc,  0xfac00085 },
+	{ 0x50320,  0x6d0003db },
+	{ 0x5fffc,  0xfa000098 },
+	{ 0x50324,  0x698003d9 },
+	{ 0x5fffc,  0xf98000ac },
+	{ 0x50328,  0x654003d8 },
+	{ 0x5fffc,  0xf8c000c1 },
+	{ 0x5032c,  0x610003d7 },
+	{ 0x5fffc,  0xf84000d5 },
+	{ 0x50330,  0x5c8003d7 },
+	{ 0x5fffc,  0xf7c000e9 },
+	{ 0x50334,  0x580003d7 },
+	{ 0x5fffc,  0xf74000fd },
+	{ 0x50338,  0x534003d8 },
+	{ 0x5fffc,  0xf6c00112 },
+	{ 0x5033c,  0x4e8003d8 },
+	{ 0x5fffc,  0xf6800126 },
+	{ 0x50340,  0x494003da },
+	{ 0x5fffc,  0xf600013a },
+	{ 0x50344,  0x448003db },
+	{ 0x5fffc,  0xf600014d },
+	{ 0x50348,  0x3f4003dd },
+	{ 0x5fffc,  0xf5c00160 },
+	{ 0x5034c,  0x3a4003df },
+	{ 0x5fffc,  0xf5c00172 },
+	{ 0x50350,  0x354003e1 },
+	{ 0x5fffc,  0xf5c00184 },
+	{ 0x50354,  0x304003e3 },
+	{ 0x5fffc,  0xf6000195 },
+	{ 0x50358,  0x2b0003e6 },
+	{ 0x5fffc,  0xf64001a6 },
+	{ 0x5035c,  0x260003e8 },
+	{ 0x5fffc,  0xf6c001b4 },
+	{ 0x50360,  0x214003eb },
+	{ 0x5fffc,  0xf78001c2 },
+	{ 0x50364,  0x1c4003ee },
+	{ 0x5fffc,  0xf80001cf },
+	{ 0x50368,  0x17c003f1 },
+	{ 0x5fffc,  0xf90001db },
+	{ 0x5036c,  0x134003f3 },
+	{ 0x5fffc,  0xfa0001e5 },
+	{ 0x50370,  0xf0003f6 },
+	{ 0x5fffc,  0xfb4001ee },
+	{ 0x50374,  0xac003f9 },
+	{ 0x5fffc,  0xfcc001f5 },
+	{ 0x50378,  0x70003fb },
+	{ 0x5fffc,  0xfe4001fb },
+	{ 0x5037c,  0x34003fe },
+};
+
+struct mdp_table_entry *mdp_downscale_y_table[MDP_DOWNSCALE_MAX] = {
+	[MDP_DOWNSCALE_PT2TOPT4] = mdp_downscale_y_table_PT2TOPT4,
+	[MDP_DOWNSCALE_PT4TOPT6] = mdp_downscale_y_table_PT4TOPT6,
+	[MDP_DOWNSCALE_PT6TOPT8] = mdp_downscale_y_table_PT6TOPT8,
+	[MDP_DOWNSCALE_PT8TO1]  = mdp_downscale_y_table_PT8TO1,
+};
+
+struct mdp_table_entry mdp_gaussian_blur_table[] = {
+	/* max variance */
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50280, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50284, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50288, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x5028c, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50290, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50294, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50298, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x5029c, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502a0, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502a4, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502a8, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502ac, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502b0, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502b4, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502b8, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502bc, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502c0, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502c4, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502c8, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502cc, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502d0, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502d4, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502d8, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502dc, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502e0, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502e4, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502e8, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502ec, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502f0, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502f4, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502f8, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502fc, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50300, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50304, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50308, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x5030c, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50310, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50314, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50318, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x5031c, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50320, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50324, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50328, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x5032c, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50330, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50334, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50338, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x5033c, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50340, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50344, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50348, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x5034c, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50350, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50354, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50358, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x5035c, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50360, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50364, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50368, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x5036c, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50370, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50374, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50378, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x5037c, 0x20000080 },
+};
+
+static void load_table(const struct mdp_info *mdp,
+		       struct mdp_table_entry *table, int len)
+{
+	int i;
+	for (i = 0; i < len; i++)
+		mdp_writel(mdp, table[i].val, table[i].reg);
+}
+
+enum {
+	IMG_LEFT,
+	IMG_RIGHT,
+	IMG_TOP,
+	IMG_BOTTOM,
+};
+
+static void get_edge_info(uint32_t src, uint32_t src_coord, uint32_t dst,
+			  uint32_t *interp1, uint32_t *interp2,
+			  uint32_t *repeat1, uint32_t *repeat2) {
+	if (src > 3 * dst) {
+		*interp1 = 0;
+		*interp2 = src - 1;
+		*repeat1 = 0;
+		*repeat2 = 0;
+	} else if (src == 3 * dst) {
+		*interp1 = 0;
+		*interp2 = src;
+		*repeat1 = 0;
+		*repeat2 = 1;
+	} else if (src > dst && src < 3 * dst) {
+		*interp1 = -1;
+		*interp2 = src;
+		*repeat1 = 1;
+		*repeat2 = 1;
+	} else if (src == dst) {
+		*interp1 = -1;
+		*interp2 = src + 1;
+		*repeat1 = 1;
+		*repeat2 = 2;
+	} else {
+		*interp1 = -2;
+		*interp2 = src + 1;
+		*repeat1 = 2;
+		*repeat2 = 2;
+	}
+	*interp1 += src_coord;
+	*interp2 += src_coord;
+}
+
+int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, struct ppp_regs *regs)
+{
+	int32_t luma_interp[4];
+	int32_t luma_repeat[4];
+	int32_t chroma_interp[4];
+	int32_t chroma_bound[4];
+	int32_t chroma_repeat[4];
+	uint32_t dst_w, dst_h;
+
+	memset(&luma_interp, 0, sizeof(int32_t) * 4);
+	memset(&luma_repeat, 0, sizeof(int32_t) * 4);
+	memset(&chroma_interp, 0, sizeof(int32_t) * 4);
+	memset(&chroma_bound, 0, sizeof(int32_t) * 4);
+	memset(&chroma_repeat, 0, sizeof(int32_t) * 4);
+	regs->edge = 0;
+
+	if (req->flags & MDP_ROT_90) {
+		dst_w = req->dst_rect.h;
+		dst_h = req->dst_rect.w;
+	} else {
+		dst_w = req->dst_rect.w;
+		dst_h = req->dst_rect.h;
+	}
+
+	if (regs->op & (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON)) {
+		get_edge_info(req->src_rect.h, req->src_rect.y, dst_h,
+			      &luma_interp[IMG_TOP], &luma_interp[IMG_BOTTOM],
+			      &luma_repeat[IMG_TOP], &luma_repeat[IMG_BOTTOM]);
+		get_edge_info(req->src_rect.w, req->src_rect.x, dst_w,
+			      &luma_interp[IMG_LEFT], &luma_interp[IMG_RIGHT],
+			      &luma_repeat[IMG_LEFT], &luma_repeat[IMG_RIGHT]);
+	} else {
+		luma_interp[IMG_LEFT] = req->src_rect.x;
+		luma_interp[IMG_RIGHT] = req->src_rect.x + req->src_rect.w - 1;
+		luma_interp[IMG_TOP] = req->src_rect.y;
+		luma_interp[IMG_BOTTOM] = req->src_rect.y + req->src_rect.h - 1;
+		luma_repeat[IMG_LEFT] = 0;
+		luma_repeat[IMG_TOP] = 0;
+		luma_repeat[IMG_RIGHT] = 0;
+		luma_repeat[IMG_BOTTOM] = 0;
+	}
+
+	chroma_interp[IMG_LEFT] = luma_interp[IMG_LEFT];
+	chroma_interp[IMG_RIGHT] = luma_interp[IMG_RIGHT];
+	chroma_interp[IMG_TOP] = luma_interp[IMG_TOP];
+	chroma_interp[IMG_BOTTOM] = luma_interp[IMG_BOTTOM];
+
+	chroma_bound[IMG_LEFT] = req->src_rect.x;
+	chroma_bound[IMG_RIGHT] = req->src_rect.x + req->src_rect.w - 1;
+	chroma_bound[IMG_TOP] = req->src_rect.y;
+	chroma_bound[IMG_BOTTOM] = req->src_rect.y + req->src_rect.h - 1;
+
+	if (IS_YCRCB(req->src.format)) {
+		chroma_interp[IMG_LEFT] = chroma_interp[IMG_LEFT] >> 1;
+		chroma_interp[IMG_RIGHT] = (chroma_interp[IMG_RIGHT] + 1) >> 1;
+
+		chroma_bound[IMG_LEFT] = chroma_bound[IMG_LEFT] >> 1;
+		chroma_bound[IMG_RIGHT] = chroma_bound[IMG_RIGHT] >> 1;
+	}
+
+	if (req->src.format == MDP_Y_CBCR_H2V2 ||
+	    req->src.format == MDP_Y_CRCB_H2V2) {
+		chroma_interp[IMG_TOP] = (chroma_interp[IMG_TOP] - 1) >> 1;
+		chroma_interp[IMG_BOTTOM] = (chroma_interp[IMG_BOTTOM] + 1)
+					    >> 1;
+		chroma_bound[IMG_TOP] = (chroma_bound[IMG_TOP] + 1) >> 1;
+		chroma_bound[IMG_BOTTOM] = chroma_bound[IMG_BOTTOM] >> 1;
+	}
+
+	chroma_repeat[IMG_LEFT] = chroma_bound[IMG_LEFT] -
+				  chroma_interp[IMG_LEFT];
+	chroma_repeat[IMG_RIGHT] = chroma_interp[IMG_RIGHT] -
+				  chroma_bound[IMG_RIGHT];
+	chroma_repeat[IMG_TOP] = chroma_bound[IMG_TOP] -
+				  chroma_interp[IMG_TOP];
+	chroma_repeat[IMG_BOTTOM] = chroma_interp[IMG_BOTTOM] -
+				  chroma_bound[IMG_BOTTOM];
+
+	if (chroma_repeat[IMG_LEFT] < 0 || chroma_repeat[IMG_LEFT] > 3 ||
+	    chroma_repeat[IMG_RIGHT] < 0 || chroma_repeat[IMG_RIGHT] > 3 ||
+	    chroma_repeat[IMG_TOP] < 0 || chroma_repeat[IMG_TOP] > 3 ||
+	    chroma_repeat[IMG_BOTTOM] < 0 || chroma_repeat[IMG_BOTTOM] > 3 ||
+	    luma_repeat[IMG_LEFT] < 0 || luma_repeat[IMG_LEFT] > 3 ||
+	    luma_repeat[IMG_RIGHT] < 0 || luma_repeat[IMG_RIGHT] > 3 ||
+	    luma_repeat[IMG_TOP] < 0 || luma_repeat[IMG_TOP] > 3 ||
+	    luma_repeat[IMG_BOTTOM] < 0 || luma_repeat[IMG_BOTTOM] > 3)
+		return -1;
+
+	regs->edge |= (chroma_repeat[IMG_LEFT] & 3) << MDP_LEFT_CHROMA;
+	regs->edge |= (chroma_repeat[IMG_RIGHT] & 3) << MDP_RIGHT_CHROMA;
+	regs->edge |= (chroma_repeat[IMG_TOP] & 3) << MDP_TOP_CHROMA;
+	regs->edge |= (chroma_repeat[IMG_BOTTOM] & 3) << MDP_BOTTOM_CHROMA;
+	regs->edge |= (luma_repeat[IMG_LEFT] & 3) << MDP_LEFT_LUMA;
+	regs->edge |= (luma_repeat[IMG_RIGHT] & 3) << MDP_RIGHT_LUMA;
+	regs->edge |= (luma_repeat[IMG_TOP] & 3) << MDP_TOP_LUMA;
+	regs->edge |= (luma_repeat[IMG_BOTTOM] & 3) << MDP_BOTTOM_LUMA;
+	return 0;
+}
+
+#define ONE_HALF	(1LL << 32)
+#define ONE		(1LL << 33)
+#define TWO		(2LL << 33)
+#define THREE		(3LL << 33)
+#define FRAC_MASK (ONE - 1)
+#define INT_MASK (~FRAC_MASK)
+
+static int scale_params(uint32_t dim_in, uint32_t dim_out, uint32_t origin,
+			uint32_t *phase_init, uint32_t *phase_step)
+{
+	/* to improve precicsion calculations are done in U31.33 and converted
+	 * to U3.29 at the end */
+	int64_t k1, k2, k3, k4, tmp;
+	uint64_t n, d, os, os_p, od, od_p, oreq;
+	unsigned rpa = 0;
+	int64_t ip64, delta;
+
+	if (dim_out % 3 == 0)
+		rpa = !(dim_in % (dim_out / 3));
+
+	n = ((uint64_t)dim_out) << 34;
+	d = dim_in;
+	if (!d)
+		return -1;
+	do_div(n, d);
+	k3 = (n + 1) >> 1;
+	if ((k3 >> 4) < (1LL << 27) || (k3 >> 4) > (1LL << 31))
+		return -1;
+
+	n = ((uint64_t)dim_in) << 34;
+	d = (uint64_t)dim_out;
+	if (!d)
+		return -1;
+	do_div(n, d);
+	k1 = (n + 1) >> 1;
+	k2 = (k1 - ONE) >> 1;
+
+	*phase_init = (int)(k2 >> 4);
+	k4 = (k3 - ONE) >> 1;
+
+	if (rpa) {
+		os = ((uint64_t)origin << 33) - ONE_HALF;
+		tmp = (dim_out * os) + ONE_HALF;
+		if (!dim_in)
+			return -1;
+		do_div(tmp, dim_in);
+		od = tmp - ONE_HALF;
+	} else {
+		os = ((uint64_t)origin << 1) - 1;
+		od = (((k3 * os) >> 1) + k4);
+	}
+
+	od_p = od & INT_MASK;
+	if (od_p != od)
+		od_p += ONE;
+
+	if (rpa) {
+		tmp = (dim_in * od_p) + ONE_HALF;
+		if (!dim_in)
+			return -1;
+		do_div(tmp, dim_in);
+		os_p = tmp - ONE_HALF;
+	} else {
+		os_p = ((k1 * (od_p >> 33)) + k2);
+	}
+
+	oreq = (os_p & INT_MASK) - ONE;
+
+	ip64 = os_p - oreq;
+	delta = ((int64_t)(origin) << 33) - oreq;
+	ip64 -= delta;
+	/* limit to valid range before the left shift */
+	delta = (ip64 & (1LL << 63)) ? 4 : -4;
+	delta <<= 33;
+	while (abs((int)(ip64 >> 33)) > 4)
+		ip64 += delta;
+	*phase_init = (int)(ip64 >> 4);
+	*phase_step = (uint32_t)(k1 >> 4);
+	return 0;
+}
+
+int mdp_ppp_cfg_scale(const struct mdp_info *mdp, struct ppp_regs *regs,
+		      struct mdp_rect *src_rect, struct mdp_rect *dst_rect,
+		      uint32_t src_format, uint32_t dst_format)
+{
+	int downscale;
+	uint32_t phase_init_x, phase_init_y, phase_step_x, phase_step_y;
+	uint32_t scale_factor_x, scale_factor_y;
+
+	if (scale_params(src_rect->w, dst_rect->w, 1, &phase_init_x,
+			 &phase_step_x) ||
+	    scale_params(src_rect->h, dst_rect->h, 1, &phase_init_y,
+			 &phase_step_y))
+		return -1;
+
+	regs->phasex_init = phase_init_x;
+	regs->phasey_init = phase_init_y;
+	regs->phasex_step = phase_step_x;
+	regs->phasey_step = phase_step_y;
+
+	scale_factor_x = (dst_rect->w * 10) / src_rect->w;
+	scale_factor_y = (dst_rect->h * 10) / src_rect->h;
+
+	if (scale_factor_x > 8)
+		downscale = MDP_DOWNSCALE_PT8TO1;
+	else if (scale_factor_x > 6)
+		downscale = MDP_DOWNSCALE_PT6TOPT8;
+	else if (scale_factor_x > 4)
+		downscale = MDP_DOWNSCALE_PT4TOPT6;
+	else
+		downscale = MDP_DOWNSCALE_PT2TOPT4;
+
+	if (downscale != downscale_x_table) {
+		load_table(mdp, mdp_downscale_x_table[downscale], 64);
+		downscale_x_table = downscale;
+	}
+
+	if (scale_factor_y > 8)
+		downscale = MDP_DOWNSCALE_PT8TO1;
+	else if (scale_factor_y > 6)
+		downscale = MDP_DOWNSCALE_PT6TOPT8;
+	else if (scale_factor_y > 4)
+		downscale = MDP_DOWNSCALE_PT4TOPT6;
+	else
+		downscale = MDP_DOWNSCALE_PT2TOPT4;
+
+	if (downscale != downscale_y_table) {
+		load_table(mdp, mdp_downscale_y_table[downscale], 64);
+		downscale_y_table = downscale;
+	}
+
+	return 0;
+}
+
+
+int mdp_ppp_load_blur(const struct mdp_info *mdp)
+{
+	if (!(downscale_x_table == MDP_DOWNSCALE_BLUR &&
+              downscale_y_table == MDP_DOWNSCALE_BLUR)) {
+		load_table(mdp, mdp_gaussian_blur_table, 128);
+		downscale_x_table = MDP_DOWNSCALE_BLUR;
+		downscale_y_table = MDP_DOWNSCALE_BLUR;
+	}
+
+	return 0;
+}
+
+void mdp_ppp_init_scale(const struct mdp_info *mdp)
+{
+	downscale_x_table = MDP_DOWNSCALE_MAX;
+	downscale_y_table = MDP_DOWNSCALE_MAX;
+
+	load_table(mdp, mdp_upscale_table, ARRAY_SIZE(mdp_upscale_table));
+}
diff --git a/drivers/video/msm/mdp_ppp31.c b/drivers/video/msm/mdp_ppp31.c
new file mode 100644
index 0000000..91764fe
--- /dev/null
+++ b/drivers/video/msm/mdp_ppp31.c
@@ -0,0 +1,332 @@
+/* drivers/video/msm/mdp_ppp31.c
+ *
+ * Copyright (C) 2009 Code Aurora Forum. All rights reserved.
+ * Copyright (C) 2009 Google Incorporated
+ *
+ * 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/errno.h>
+#include <linux/kernel.h>
+#include <asm/io.h>
+#include <linux/msm_mdp.h>
+
+#include "mdp_hw.h"
+#include "mdp_ppp.h"
+
+#define NUM_COEFFS			32
+
+struct mdp_scale_coeffs {
+	uint16_t	c[4][NUM_COEFFS];
+};
+
+struct mdp_scale_tbl_info {
+	uint16_t			offset;
+	uint32_t			set:2;
+	int				use_pr;
+	struct mdp_scale_coeffs		coeffs;
+};
+
+enum {
+	MDP_SCALE_PT2TOPT4,
+	MDP_SCALE_PT4TOPT6,
+	MDP_SCALE_PT6TOPT8,
+	MDP_SCALE_PT8TO8,
+	MDP_SCALE_MAX,
+};
+
+static struct mdp_scale_coeffs mdp_scale_pr_coeffs = {
+	.c = {
+		[0] = {
+			0, 0, 0, 0, 0, 0, 0, 0,
+			0, 0, 0, 0, 0, 0, 0, 0,
+			0, 0, 0, 0, 0, 0, 0, 0,
+			0, 0, 0, 0, 0, 0, 0, 0,
+		},
+		[1] = {
+			511, 511, 511, 511, 511, 511, 511, 511,
+			511, 511, 511, 511, 511, 511, 511, 511,
+			0, 0, 0, 0, 0, 0, 0, 0,
+			0, 0, 0, 0, 0, 0, 0, 0,
+		},
+		[2] = {
+			0, 0, 0, 0, 0, 0, 0, 0,
+			0, 0, 0, 0, 0, 0, 0, 0,
+			511, 511, 511, 511, 511, 511, 511, 511,
+			511, 511, 511, 511, 511, 511, 511, 511,
+		},
+		[3] = {
+			0, 0, 0, 0, 0, 0, 0, 0,
+			0, 0, 0, 0, 0, 0, 0, 0,
+			0, 0, 0, 0, 0, 0, 0, 0,
+			0, 0, 0, 0, 0, 0, 0, 0,
+		},
+	},
+};
+
+static struct mdp_scale_tbl_info mdp_scale_tbl[MDP_SCALE_MAX] = {
+	[ MDP_SCALE_PT2TOPT4 ]	= {
+		.offset		= 0,
+		.set		= MDP_PPP_SCALE_COEFF_D0_SET,
+		.use_pr		= -1,
+		.coeffs.c	= {
+			[0] = {
+				131, 131, 130, 129, 128, 127, 127, 126,
+				125, 125, 124, 123, 123, 121, 120, 119,
+				119, 118, 117, 117, 116, 115, 115, 114,
+				113, 112, 111, 110, 109, 109, 108, 107,
+			},
+			[1] = {
+				141, 140, 140, 140, 140, 139, 138, 138,
+				138, 137, 137, 137, 136, 137, 137, 137,
+				136, 136, 136, 135, 135, 135, 134, 134,
+				134, 134, 134, 133, 133, 132, 132, 132,
+			},
+			[2] = {
+				132, 132, 132, 133, 133, 134, 134, 134,
+				134, 134, 135, 135, 135, 136, 136, 136,
+				137, 137, 137, 136, 137, 137, 137, 138,
+				138, 138, 139, 140, 140, 140, 140, 141,
+			},
+			[3] = {
+				107, 108, 109, 109, 110, 111, 112, 113,
+				114, 115, 115, 116, 117, 117, 118, 119,
+				119, 120, 121, 123, 123, 124, 125, 125,
+				126, 127, 127, 128, 129, 130, 131, 131,
+			}
+		},
+	},
+	[ MDP_SCALE_PT4TOPT6 ] = {
+		.offset		= 32,
+		.set		= MDP_PPP_SCALE_COEFF_D1_SET,
+		.use_pr		= -1,
+		.coeffs.c	= {
+			[0] = {
+				136, 132, 128, 123, 119, 115, 111, 107,
+				103, 98, 95, 91, 87, 84, 80, 76,
+				73, 69, 66, 62, 59, 57, 54, 50,
+				47, 44, 41, 39, 36, 33, 32, 29,
+			},
+			[1] = {
+				206, 205, 204, 204, 201, 200, 199, 197,
+				196, 194, 191, 191, 189, 185, 184, 182,
+				180, 178, 176, 173, 170, 168, 165, 162,
+				160, 157, 155, 152, 148, 146, 142, 140,
+			},
+			[2] = {
+				140, 142, 146, 148, 152, 155, 157, 160,
+				162, 165, 168, 170, 173, 176, 178, 180,
+				182, 184, 185, 189, 191, 191, 194, 196,
+				197, 199, 200, 201, 204, 204, 205, 206,
+			},
+			[3] = {
+				29, 32, 33, 36, 39, 41, 44, 47,
+				50, 54, 57, 59, 62, 66, 69, 73,
+				76, 80, 84, 87, 91, 95, 98, 103,
+				107, 111, 115, 119, 123, 128, 132, 136,
+			},
+		},
+	},
+	[ MDP_SCALE_PT6TOPT8 ] = {
+		.offset		= 64,
+		.set		= MDP_PPP_SCALE_COEFF_D2_SET,
+		.use_pr		= -1,
+		.coeffs.c	= {
+			[0] = {
+				104, 96, 89, 82, 75, 68, 61, 55,
+				49, 43, 38, 33, 28, 24, 20, 16,
+				12, 9, 6, 4, 2, 0, -2, -4,
+				-5, -6, -7, -7, -8, -8, -8, -8,
+			},
+			[1] = {
+				303, 303, 302, 300, 298, 296, 293, 289,
+				286, 281, 276, 270, 265, 258, 252, 245,
+				238, 230, 223, 214, 206, 197, 189, 180,
+				172, 163, 154, 145, 137, 128, 120, 112,
+			},
+			[2] = {
+				112, 120, 128, 137, 145, 154, 163, 172,
+				180, 189, 197, 206, 214, 223, 230, 238,
+				245, 252, 258, 265, 270, 276, 281, 286,
+				289, 293, 296, 298, 300, 302, 303, 303,
+			},
+			[3] = {
+				-8, -8, -8, -8, -7, -7, -6, -5,
+				-4, -2, 0, 2, 4, 6, 9, 12,
+				16, 20, 24, 28, 33, 38, 43, 49,
+				55, 61, 68, 75, 82, 89, 96, 104,
+			},
+		},
+	},
+	[ MDP_SCALE_PT8TO8 ] = {
+		.offset		= 96,
+		.set		= MDP_PPP_SCALE_COEFF_U1_SET,
+		.use_pr		= -1,
+		.coeffs.c	= {
+			[0] = {
+				0, -7, -13, -19, -24, -28, -32, -34,
+				-37, -39, -40, -41, -41, -41, -40, -40,
+				-38, -37, -35, -33, -31, -29, -26, -24,
+				-21, -18, -15, -13, -10, -7, -5, -2,
+			},
+			[1] = {
+				511, 507, 501, 494, 485, 475, 463, 450,
+				436, 422, 405, 388, 370, 352, 333, 314,
+				293, 274, 253, 233, 213, 193, 172, 152,
+				133, 113, 95, 77, 60, 43, 28, 13,
+			},
+			[2] = {
+				0, 13, 28, 43, 60, 77, 95, 113,
+				133, 152, 172, 193, 213, 233, 253, 274,
+				294, 314, 333, 352, 370, 388, 405, 422,
+				436, 450, 463, 475, 485, 494, 501, 507,
+			},
+			[3] = {
+				0, -2, -5, -7, -10, -13, -15, -18,
+				-21, -24, -26, -29, -31, -33, -35, -37,
+				-38, -40, -40, -41, -41, -41, -40, -39,
+				-37, -34, -32, -28, -24, -19, -13, -7,
+			},
+		},
+	},
+};
+
+static void load_table(const struct mdp_info *mdp, int scale, int use_pr)
+{
+	int i;
+	uint32_t val;
+	struct mdp_scale_coeffs *coeffs;
+	struct mdp_scale_tbl_info *tbl = &mdp_scale_tbl[scale];
+
+	if (use_pr == tbl->use_pr)
+		return;
+
+	tbl->use_pr = use_pr;
+	if (!use_pr)
+		coeffs = &tbl->coeffs;
+	else
+		coeffs = &mdp_scale_pr_coeffs;
+
+	for (i = 0; i < NUM_COEFFS; ++i) {
+		val = ((coeffs->c[1][i] & 0x3ff) << 16) |
+			(coeffs->c[0][i] & 0x3ff);
+		mdp_writel(mdp, val, MDP_PPP_SCALE_COEFF_LSBn(tbl->offset + i));
+
+		val = ((coeffs->c[3][i] & 0x3ff) << 16) |
+			(coeffs->c[2][i] & 0x3ff);
+		mdp_writel(mdp, val, MDP_PPP_SCALE_COEFF_MSBn(tbl->offset + i));
+	}
+}
+
+#define SCALER_PHASE_BITS		29
+static void scale_params(uint32_t dim_in, uint32_t dim_out, uint32_t scaler,
+			 uint32_t *phase_init, uint32_t *phase_step)
+{
+	uint64_t src = dim_in;
+	uint64_t dst = dim_out;
+	uint64_t numer;
+	uint64_t denom;
+
+	*phase_init = 0;
+
+	if (dst == 1) {
+		/* if destination is 1 pixel wide, the value of phase_step
+		 * is unimportant. */
+		*phase_step = (uint32_t) (src << SCALER_PHASE_BITS);
+		if (scaler == MDP_PPP_SCALER_FIR)
+			*phase_init =
+				(uint32_t) ((src - 1) << SCALER_PHASE_BITS);
+		return;
+	}
+
+	if (scaler == MDP_PPP_SCALER_FIR) {
+		numer = (src - 1) << SCALER_PHASE_BITS;
+		denom = dst - 1;
+		/* we want to round up the result*/
+		numer += denom - 1;
+	} else {
+		numer = src << SCALER_PHASE_BITS;
+		denom = dst;
+	}
+
+	do_div(numer, denom);
+	*phase_step = (uint32_t) numer;
+}
+
+static int scale_idx(int factor)
+{
+	int idx;
+
+	if (factor > 80)
+		idx = MDP_SCALE_PT8TO8;
+	else if (factor > 60)
+		idx = MDP_SCALE_PT6TOPT8;
+	else if (factor > 40)
+		idx = MDP_SCALE_PT4TOPT6;
+	else
+		idx = MDP_SCALE_PT2TOPT4;
+
+	return idx;
+}
+
+int mdp_ppp_cfg_scale(const struct mdp_info *mdp, struct ppp_regs *regs,
+		      struct mdp_rect *src_rect, struct mdp_rect *dst_rect,
+		      uint32_t src_format, uint32_t dst_format)
+{
+	uint32_t x_fac;
+	uint32_t y_fac;
+	uint32_t scaler_x = MDP_PPP_SCALER_FIR;
+	uint32_t scaler_y = MDP_PPP_SCALER_FIR;
+	// Don't use pixel repeat mode, it looks bad
+	int use_pr = 0;
+	int x_idx;
+	int y_idx;
+
+	if (unlikely(src_rect->w > 2048 || src_rect->h > 2048))
+		return -ENOTSUPP;
+
+	x_fac = (dst_rect->w * 100) / src_rect->w;
+	y_fac = (dst_rect->h * 100) / src_rect->h;
+
+	/* if down-scaling by a factor smaller than 1/4, use M/N */
+	scaler_x = x_fac <= 25 ? MDP_PPP_SCALER_MN : MDP_PPP_SCALER_FIR;
+	scaler_y = y_fac <= 25 ? MDP_PPP_SCALER_MN : MDP_PPP_SCALER_FIR;
+	scale_params(src_rect->w, dst_rect->w, scaler_x, &regs->phasex_init,
+		     &regs->phasex_step);
+	scale_params(src_rect->h, dst_rect->h, scaler_y, &regs->phasey_init,
+		     &regs->phasey_step);
+
+	x_idx = scale_idx(x_fac);
+	y_idx = scale_idx(y_fac);
+	load_table(mdp, x_idx, use_pr);
+	load_table(mdp, y_idx, use_pr);
+
+	regs->scale_cfg = 0;
+	// Enable SVI when source or destination is YUV
+	if (!IS_RGB(src_format) && !IS_RGB(dst_format))
+		regs->scale_cfg |= (1 << 6);
+	regs->scale_cfg |= (mdp_scale_tbl[x_idx].set << 2) |
+		(mdp_scale_tbl[x_idx].set << 4);
+	regs->scale_cfg |= (scaler_x << 0) | (scaler_y << 1);
+
+	return 0;
+}
+
+int mdp_ppp_load_blur(const struct mdp_info *mdp)
+{
+	return -ENOTSUPP;
+}
+
+void mdp_ppp_init_scale(const struct mdp_info *mdp)
+{
+	int scale;
+	for (scale = 0; scale < MDP_SCALE_MAX; ++scale)
+		load_table(mdp, scale, 0);
+}
diff --git a/drivers/video/msm/mdp_ppp_v20.c b/drivers/video/msm/mdp_ppp_v20.c
new file mode 100644
index 0000000..d271b85
--- /dev/null
+++ b/drivers/video/msm/mdp_ppp_v20.c
@@ -0,0 +1,2533 @@
+/* Copyright (c) 2008-2009, 2012 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/fb.h>
+#include "linux/proc_fs.h"
+
+#include <mach/hardware.h>
+#include <linux/io.h>
+
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <linux/semaphore.h>
+#include <asm/div64.h>
+
+#include "mdp.h"
+#include "msm_fb.h"
+
+static MDP_SCALE_MODE mdp_curr_up_scale_xy;
+static MDP_SCALE_MODE mdp_curr_down_scale_x;
+static MDP_SCALE_MODE mdp_curr_down_scale_y;
+
+static long long mdp_do_div(long long num, long long den)
+{
+	do_div(num, den);
+	return num;
+}
+
+struct mdp_table_entry mdp_gaussian_blur_table[] = {
+	/* max variance */
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50280, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50284, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50288, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x5028c, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50290, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50294, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50298, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x5029c, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502a0, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502a4, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502a8, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502ac, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502b0, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502b4, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502b8, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502bc, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502c0, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502c4, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502c8, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502cc, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502d0, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502d4, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502d8, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502dc, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502e0, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502e4, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502e8, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502ec, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502f0, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502f4, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502f8, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x502fc, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50300, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50304, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50308, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x5030c, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50310, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50314, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50318, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x5031c, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50320, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50324, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50328, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x5032c, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50330, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50334, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50338, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x5033c, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50340, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50344, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50348, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x5034c, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50350, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50354, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50358, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x5035c, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50360, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50364, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50368, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x5036c, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50370, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50374, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x50378, 0x20000080 },
+	{ 0x5fffc, 0x20000080 },
+	{ 0x5037c, 0x20000080 },
+};
+
+static void load_scale_table(
+	struct mdp_table_entry *table, int len)
+{
+	int i;
+	for (i = 0; i < len; i++)
+		MDP_OUTP(MDP_BASE + table[i].reg, table[i].val);
+}
+
+static void mdp_load_pr_upscale_table(void)
+{
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50200, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50204, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50208, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5020c, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50210, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50214, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50218, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5021c, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50220, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50224, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50228, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5022c, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50230, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50234, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50238, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5023c, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50240, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50244, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50248, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x5024c, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50250, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50254, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50258, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x5025c, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50260, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50264, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50268, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x5026c, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50270, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50274, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50278, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x5027c, 0x0);
+}
+
+static void mdp_load_pr_downscale_table_x_point2TOpoint4(void)
+{
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50280, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50284, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50288, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5028c, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50290, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50294, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50298, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5029c, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x502a0, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x502a4, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x502a8, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x502ac, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x502b0, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x502b4, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x502b8, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x502bc, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502c0, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502c4, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502c8, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502cc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502d0, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502d4, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502d8, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502dc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502e0, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502e4, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502e8, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502ec, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502f0, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502f4, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502f8, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502fc, 0x0);
+}
+
+static void mdp_load_pr_downscale_table_y_point2TOpoint4(void)
+{
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50300, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50304, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50308, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5030c, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50310, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50314, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50318, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5031c, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50320, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50324, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50328, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5032c, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50330, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50334, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50338, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5033c, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50340, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50344, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50348, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x5034c, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50350, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50354, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50358, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x5035c, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50360, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50364, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50368, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x5036c, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50370, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50374, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50378, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x5037c, 0x0);
+}
+
+static void mdp_load_pr_downscale_table_x_point4TOpoint6(void)
+{
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50280, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50284, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50288, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5028c, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50290, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50294, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50298, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5029c, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x502a0, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x502a4, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x502a8, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x502ac, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x502b0, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x502b4, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x502b8, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x502bc, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502c0, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502c4, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502c8, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502cc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502d0, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502d4, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502d8, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502dc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502e0, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502e4, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502e8, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502ec, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502f0, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502f4, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502f8, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502fc, 0x0);
+}
+
+static void mdp_load_pr_downscale_table_y_point4TOpoint6(void)
+{
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50300, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50304, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50308, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5030c, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50310, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50314, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50318, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5031c, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50320, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50324, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50328, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5032c, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50330, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50334, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50338, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5033c, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50340, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50344, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50348, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x5034c, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50350, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50354, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50358, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x5035c, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50360, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50364, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50368, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x5036c, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50370, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50374, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50378, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x5037c, 0x0);
+}
+
+static void mdp_load_pr_downscale_table_x_point6TOpoint8(void)
+{
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50280, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50284, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50288, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5028c, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50290, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50294, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50298, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5029c, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x502a0, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x502a4, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x502a8, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x502ac, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x502b0, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x502b4, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x502b8, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x502bc, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502c0, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502c4, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502c8, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502cc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502d0, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502d4, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502d8, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502dc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502e0, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502e4, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502e8, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502ec, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502f0, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502f4, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502f8, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502fc, 0x0);
+}
+
+static void mdp_load_pr_downscale_table_y_point6TOpoint8(void)
+{
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50300, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50304, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50308, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5030c, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50310, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50314, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50318, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5031c, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50320, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50324, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50328, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5032c, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50330, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50334, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50338, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5033c, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50340, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50344, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50348, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x5034c, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50350, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50354, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50358, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x5035c, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50360, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50364, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50368, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x5036c, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50370, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50374, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50378, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x5037c, 0x0);
+}
+
+static void mdp_load_pr_downscale_table_x_point8TO1(void)
+{
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50280, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50284, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50288, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5028c, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50290, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50294, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50298, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5029c, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x502a0, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x502a4, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x502a8, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x502ac, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x502b0, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x502b4, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x502b8, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x502bc, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502c0, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502c4, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502c8, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502cc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502d0, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502d4, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502d8, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502dc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502e0, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502e4, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502e8, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502ec, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502f0, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502f4, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502f8, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x502fc, 0x0);
+}
+
+static void mdp_load_pr_downscale_table_y_point8TO1(void)
+{
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50300, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50304, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50308, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5030c, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50310, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50314, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50318, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5031c, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50320, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50324, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50328, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5032c, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50330, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50334, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50338, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5033c, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50340, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50344, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50348, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x5034c, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50350, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50354, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50358, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x5035c, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50360, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50364, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50368, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x5036c, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50370, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50374, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x50378, 0x0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff);
+	MDP_OUTP(MDP_BASE + 0x5037c, 0x0);
+}
+
+static void mdp_load_bc_upscale_table(void)
+{
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50200, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xff80000d);
+	MDP_OUTP(MDP_BASE + 0x50204, 0x7ec003f9);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfec0001c);
+	MDP_OUTP(MDP_BASE + 0x50208, 0x7d4003f3);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe40002b);
+	MDP_OUTP(MDP_BASE + 0x5020c, 0x7b8003ed);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfd80003c);
+	MDP_OUTP(MDP_BASE + 0x50210, 0x794003e8);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfcc0004d);
+	MDP_OUTP(MDP_BASE + 0x50214, 0x76c003e4);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfc40005f);
+	MDP_OUTP(MDP_BASE + 0x50218, 0x73c003e0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfb800071);
+	MDP_OUTP(MDP_BASE + 0x5021c, 0x708003de);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfac00085);
+	MDP_OUTP(MDP_BASE + 0x50220, 0x6d0003db);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfa000098);
+	MDP_OUTP(MDP_BASE + 0x50224, 0x698003d9);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf98000ac);
+	MDP_OUTP(MDP_BASE + 0x50228, 0x654003d8);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf8c000c1);
+	MDP_OUTP(MDP_BASE + 0x5022c, 0x610003d7);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf84000d5);
+	MDP_OUTP(MDP_BASE + 0x50230, 0x5c8003d7);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf7c000e9);
+	MDP_OUTP(MDP_BASE + 0x50234, 0x580003d7);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf74000fd);
+	MDP_OUTP(MDP_BASE + 0x50238, 0x534003d8);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf6c00112);
+	MDP_OUTP(MDP_BASE + 0x5023c, 0x4e8003d8);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf6800126);
+	MDP_OUTP(MDP_BASE + 0x50240, 0x494003da);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf600013a);
+	MDP_OUTP(MDP_BASE + 0x50244, 0x448003db);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf600014d);
+	MDP_OUTP(MDP_BASE + 0x50248, 0x3f4003dd);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf5c00160);
+	MDP_OUTP(MDP_BASE + 0x5024c, 0x3a4003df);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf5c00172);
+	MDP_OUTP(MDP_BASE + 0x50250, 0x354003e1);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf5c00184);
+	MDP_OUTP(MDP_BASE + 0x50254, 0x304003e3);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf6000195);
+	MDP_OUTP(MDP_BASE + 0x50258, 0x2b0003e6);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf64001a6);
+	MDP_OUTP(MDP_BASE + 0x5025c, 0x260003e8);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf6c001b4);
+	MDP_OUTP(MDP_BASE + 0x50260, 0x214003eb);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf78001c2);
+	MDP_OUTP(MDP_BASE + 0x50264, 0x1c4003ee);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf80001cf);
+	MDP_OUTP(MDP_BASE + 0x50268, 0x17c003f1);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf90001db);
+	MDP_OUTP(MDP_BASE + 0x5026c, 0x134003f3);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfa0001e5);
+	MDP_OUTP(MDP_BASE + 0x50270, 0xf0003f6);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfb4001ee);
+	MDP_OUTP(MDP_BASE + 0x50274, 0xac003f9);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfcc001f5);
+	MDP_OUTP(MDP_BASE + 0x50278, 0x70003fb);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe4001fb);
+	MDP_OUTP(MDP_BASE + 0x5027c, 0x34003fe);
+}
+
+static void mdp_load_bc_downscale_table_x_point2TOpoint4(void)
+{
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ac00084);
+	MDP_OUTP(MDP_BASE + 0x50280, 0x23400083);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1b000084);
+	MDP_OUTP(MDP_BASE + 0x50284, 0x23000083);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1b400084);
+	MDP_OUTP(MDP_BASE + 0x50288, 0x23000082);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1b400085);
+	MDP_OUTP(MDP_BASE + 0x5028c, 0x23000081);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1b800085);
+	MDP_OUTP(MDP_BASE + 0x50290, 0x23000080);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1bc00086);
+	MDP_OUTP(MDP_BASE + 0x50294, 0x22c0007f);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1c000086);
+	MDP_OUTP(MDP_BASE + 0x50298, 0x2280007f);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1c400086);
+	MDP_OUTP(MDP_BASE + 0x5029c, 0x2280007e);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1c800086);
+	MDP_OUTP(MDP_BASE + 0x502a0, 0x2280007d);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1cc00086);
+	MDP_OUTP(MDP_BASE + 0x502a4, 0x2240007d);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1cc00087);
+	MDP_OUTP(MDP_BASE + 0x502a8, 0x2240007c);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1d000087);
+	MDP_OUTP(MDP_BASE + 0x502ac, 0x2240007b);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1d400087);
+	MDP_OUTP(MDP_BASE + 0x502b0, 0x2200007b);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1d400088);
+	MDP_OUTP(MDP_BASE + 0x502b4, 0x22400079);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1d800088);
+	MDP_OUTP(MDP_BASE + 0x502b8, 0x22400078);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1dc00088);
+	MDP_OUTP(MDP_BASE + 0x502bc, 0x22400077);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1dc00089);
+	MDP_OUTP(MDP_BASE + 0x502c0, 0x22000077);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1e000089);
+	MDP_OUTP(MDP_BASE + 0x502c4, 0x22000076);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1e400089);
+	MDP_OUTP(MDP_BASE + 0x502c8, 0x22000075);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ec00088);
+	MDP_OUTP(MDP_BASE + 0x502cc, 0x21c00075);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ec00089);
+	MDP_OUTP(MDP_BASE + 0x502d0, 0x21c00074);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1f000089);
+	MDP_OUTP(MDP_BASE + 0x502d4, 0x21c00073);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1f400089);
+	MDP_OUTP(MDP_BASE + 0x502d8, 0x21800073);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1f40008a);
+	MDP_OUTP(MDP_BASE + 0x502dc, 0x21800072);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1f80008a);
+	MDP_OUTP(MDP_BASE + 0x502e0, 0x21800071);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1fc0008a);
+	MDP_OUTP(MDP_BASE + 0x502e4, 0x21800070);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1fc0008b);
+	MDP_OUTP(MDP_BASE + 0x502e8, 0x2180006f);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x2000008c);
+	MDP_OUTP(MDP_BASE + 0x502ec, 0x2140006e);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x2040008c);
+	MDP_OUTP(MDP_BASE + 0x502f0, 0x2140006d);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x2080008c);
+	MDP_OUTP(MDP_BASE + 0x502f4, 0x2100006d);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x20c0008c);
+	MDP_OUTP(MDP_BASE + 0x502f8, 0x2100006c);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x20c0008d);
+	MDP_OUTP(MDP_BASE + 0x502fc, 0x2100006b);
+}
+
+static void mdp_load_bc_downscale_table_y_point2TOpoint4(void)
+{
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ac00084);
+	MDP_OUTP(MDP_BASE + 0x50300, 0x23400083);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1b000084);
+	MDP_OUTP(MDP_BASE + 0x50304, 0x23000083);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1b400084);
+	MDP_OUTP(MDP_BASE + 0x50308, 0x23000082);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1b400085);
+	MDP_OUTP(MDP_BASE + 0x5030c, 0x23000081);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1b800085);
+	MDP_OUTP(MDP_BASE + 0x50310, 0x23000080);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1bc00086);
+	MDP_OUTP(MDP_BASE + 0x50314, 0x22c0007f);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1c000086);
+	MDP_OUTP(MDP_BASE + 0x50318, 0x2280007f);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1c400086);
+	MDP_OUTP(MDP_BASE + 0x5031c, 0x2280007e);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1c800086);
+	MDP_OUTP(MDP_BASE + 0x50320, 0x2280007d);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1cc00086);
+	MDP_OUTP(MDP_BASE + 0x50324, 0x2240007d);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1cc00087);
+	MDP_OUTP(MDP_BASE + 0x50328, 0x2240007c);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1d000087);
+	MDP_OUTP(MDP_BASE + 0x5032c, 0x2240007b);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1d400087);
+	MDP_OUTP(MDP_BASE + 0x50330, 0x2200007b);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1d400088);
+	MDP_OUTP(MDP_BASE + 0x50334, 0x22400079);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1d800088);
+	MDP_OUTP(MDP_BASE + 0x50338, 0x22400078);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1dc00088);
+	MDP_OUTP(MDP_BASE + 0x5033c, 0x22400077);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1dc00089);
+	MDP_OUTP(MDP_BASE + 0x50340, 0x22000077);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1e000089);
+	MDP_OUTP(MDP_BASE + 0x50344, 0x22000076);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1e400089);
+	MDP_OUTP(MDP_BASE + 0x50348, 0x22000075);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ec00088);
+	MDP_OUTP(MDP_BASE + 0x5034c, 0x21c00075);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ec00089);
+	MDP_OUTP(MDP_BASE + 0x50350, 0x21c00074);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1f000089);
+	MDP_OUTP(MDP_BASE + 0x50354, 0x21c00073);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1f400089);
+	MDP_OUTP(MDP_BASE + 0x50358, 0x21800073);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1f40008a);
+	MDP_OUTP(MDP_BASE + 0x5035c, 0x21800072);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1f80008a);
+	MDP_OUTP(MDP_BASE + 0x50360, 0x21800071);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1fc0008a);
+	MDP_OUTP(MDP_BASE + 0x50364, 0x21800070);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1fc0008b);
+	MDP_OUTP(MDP_BASE + 0x50368, 0x2180006f);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x2000008c);
+	MDP_OUTP(MDP_BASE + 0x5036c, 0x2140006e);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x2040008c);
+	MDP_OUTP(MDP_BASE + 0x50370, 0x2140006d);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x2080008c);
+	MDP_OUTP(MDP_BASE + 0x50374, 0x2100006d);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x20c0008c);
+	MDP_OUTP(MDP_BASE + 0x50378, 0x2100006c);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x20c0008d);
+	MDP_OUTP(MDP_BASE + 0x5037c, 0x2100006b);
+}
+
+static void mdp_load_bc_downscale_table_x_point4TOpoint6(void)
+{
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x740008c);
+	MDP_OUTP(MDP_BASE + 0x50280, 0x33800088);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x800008e);
+	MDP_OUTP(MDP_BASE + 0x50284, 0x33400084);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x8400092);
+	MDP_OUTP(MDP_BASE + 0x50288, 0x33000080);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x9000094);
+	MDP_OUTP(MDP_BASE + 0x5028c, 0x3300007b);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x9c00098);
+	MDP_OUTP(MDP_BASE + 0x50290, 0x32400077);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xa40009b);
+	MDP_OUTP(MDP_BASE + 0x50294, 0x32000073);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xb00009d);
+	MDP_OUTP(MDP_BASE + 0x50298, 0x31c0006f);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xbc000a0);
+	MDP_OUTP(MDP_BASE + 0x5029c, 0x3140006b);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xc8000a2);
+	MDP_OUTP(MDP_BASE + 0x502a0, 0x31000067);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xd8000a5);
+	MDP_OUTP(MDP_BASE + 0x502a4, 0x30800062);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xe4000a8);
+	MDP_OUTP(MDP_BASE + 0x502a8, 0x2fc0005f);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xec000aa);
+	MDP_OUTP(MDP_BASE + 0x502ac, 0x2fc0005b);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf8000ad);
+	MDP_OUTP(MDP_BASE + 0x502b0, 0x2f400057);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x108000b0);
+	MDP_OUTP(MDP_BASE + 0x502b4, 0x2e400054);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x114000b2);
+	MDP_OUTP(MDP_BASE + 0x502b8, 0x2e000050);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x124000b4);
+	MDP_OUTP(MDP_BASE + 0x502bc, 0x2d80004c);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x130000b6);
+	MDP_OUTP(MDP_BASE + 0x502c0, 0x2d000049);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x140000b8);
+	MDP_OUTP(MDP_BASE + 0x502c4, 0x2c800045);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x150000b9);
+	MDP_OUTP(MDP_BASE + 0x502c8, 0x2c000042);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x15c000bd);
+	MDP_OUTP(MDP_BASE + 0x502cc, 0x2b40003e);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x16c000bf);
+	MDP_OUTP(MDP_BASE + 0x502d0, 0x2a80003b);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x17c000bf);
+	MDP_OUTP(MDP_BASE + 0x502d4, 0x2a000039);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x188000c2);
+	MDP_OUTP(MDP_BASE + 0x502d8, 0x29400036);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x19c000c4);
+	MDP_OUTP(MDP_BASE + 0x502dc, 0x28800032);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ac000c5);
+	MDP_OUTP(MDP_BASE + 0x502e0, 0x2800002f);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1bc000c7);
+	MDP_OUTP(MDP_BASE + 0x502e4, 0x2740002c);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1cc000c8);
+	MDP_OUTP(MDP_BASE + 0x502e8, 0x26c00029);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1dc000c9);
+	MDP_OUTP(MDP_BASE + 0x502ec, 0x26000027);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ec000cc);
+	MDP_OUTP(MDP_BASE + 0x502f0, 0x25000024);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x200000cc);
+	MDP_OUTP(MDP_BASE + 0x502f4, 0x24800021);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x210000cd);
+	MDP_OUTP(MDP_BASE + 0x502f8, 0x23800020);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x220000ce);
+	MDP_OUTP(MDP_BASE + 0x502fc, 0x2300001d);
+}
+
+static void mdp_load_bc_downscale_table_y_point4TOpoint6(void)
+{
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x740008c);
+	MDP_OUTP(MDP_BASE + 0x50300, 0x33800088);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x800008e);
+	MDP_OUTP(MDP_BASE + 0x50304, 0x33400084);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x8400092);
+	MDP_OUTP(MDP_BASE + 0x50308, 0x33000080);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x9000094);
+	MDP_OUTP(MDP_BASE + 0x5030c, 0x3300007b);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x9c00098);
+	MDP_OUTP(MDP_BASE + 0x50310, 0x32400077);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xa40009b);
+	MDP_OUTP(MDP_BASE + 0x50314, 0x32000073);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xb00009d);
+	MDP_OUTP(MDP_BASE + 0x50318, 0x31c0006f);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xbc000a0);
+	MDP_OUTP(MDP_BASE + 0x5031c, 0x3140006b);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xc8000a2);
+	MDP_OUTP(MDP_BASE + 0x50320, 0x31000067);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xd8000a5);
+	MDP_OUTP(MDP_BASE + 0x50324, 0x30800062);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xe4000a8);
+	MDP_OUTP(MDP_BASE + 0x50328, 0x2fc0005f);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xec000aa);
+	MDP_OUTP(MDP_BASE + 0x5032c, 0x2fc0005b);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf8000ad);
+	MDP_OUTP(MDP_BASE + 0x50330, 0x2f400057);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x108000b0);
+	MDP_OUTP(MDP_BASE + 0x50334, 0x2e400054);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x114000b2);
+	MDP_OUTP(MDP_BASE + 0x50338, 0x2e000050);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x124000b4);
+	MDP_OUTP(MDP_BASE + 0x5033c, 0x2d80004c);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x130000b6);
+	MDP_OUTP(MDP_BASE + 0x50340, 0x2d000049);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x140000b8);
+	MDP_OUTP(MDP_BASE + 0x50344, 0x2c800045);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x150000b9);
+	MDP_OUTP(MDP_BASE + 0x50348, 0x2c000042);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x15c000bd);
+	MDP_OUTP(MDP_BASE + 0x5034c, 0x2b40003e);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x16c000bf);
+	MDP_OUTP(MDP_BASE + 0x50350, 0x2a80003b);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x17c000bf);
+	MDP_OUTP(MDP_BASE + 0x50354, 0x2a000039);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x188000c2);
+	MDP_OUTP(MDP_BASE + 0x50358, 0x29400036);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x19c000c4);
+	MDP_OUTP(MDP_BASE + 0x5035c, 0x28800032);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ac000c5);
+	MDP_OUTP(MDP_BASE + 0x50360, 0x2800002f);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1bc000c7);
+	MDP_OUTP(MDP_BASE + 0x50364, 0x2740002c);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1cc000c8);
+	MDP_OUTP(MDP_BASE + 0x50368, 0x26c00029);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1dc000c9);
+	MDP_OUTP(MDP_BASE + 0x5036c, 0x26000027);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ec000cc);
+	MDP_OUTP(MDP_BASE + 0x50370, 0x25000024);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x200000cc);
+	MDP_OUTP(MDP_BASE + 0x50374, 0x24800021);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x210000cd);
+	MDP_OUTP(MDP_BASE + 0x50378, 0x23800020);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x220000ce);
+	MDP_OUTP(MDP_BASE + 0x5037c, 0x2300001d);
+}
+
+static void mdp_load_bc_downscale_table_x_point6TOpoint8(void)
+{
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe000070);
+	MDP_OUTP(MDP_BASE + 0x50280, 0x4bc00068);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe000078);
+	MDP_OUTP(MDP_BASE + 0x50284, 0x4bc00060);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe000080);
+	MDP_OUTP(MDP_BASE + 0x50288, 0x4b800059);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe000089);
+	MDP_OUTP(MDP_BASE + 0x5028c, 0x4b000052);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe400091);
+	MDP_OUTP(MDP_BASE + 0x50290, 0x4a80004b);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe40009a);
+	MDP_OUTP(MDP_BASE + 0x50294, 0x4a000044);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe8000a3);
+	MDP_OUTP(MDP_BASE + 0x50298, 0x4940003d);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfec000ac);
+	MDP_OUTP(MDP_BASE + 0x5029c, 0x48400037);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xff0000b4);
+	MDP_OUTP(MDP_BASE + 0x502a0, 0x47800031);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xff8000bd);
+	MDP_OUTP(MDP_BASE + 0x502a4, 0x4640002b);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xc5);
+	MDP_OUTP(MDP_BASE + 0x502a8, 0x45000026);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x8000ce);
+	MDP_OUTP(MDP_BASE + 0x502ac, 0x43800021);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x10000d6);
+	MDP_OUTP(MDP_BASE + 0x502b0, 0x4240001c);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x18000df);
+	MDP_OUTP(MDP_BASE + 0x502b4, 0x40800018);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x24000e6);
+	MDP_OUTP(MDP_BASE + 0x502b8, 0x3f000014);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x30000ee);
+	MDP_OUTP(MDP_BASE + 0x502bc, 0x3d400010);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x40000f5);
+	MDP_OUTP(MDP_BASE + 0x502c0, 0x3b80000c);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x50000fc);
+	MDP_OUTP(MDP_BASE + 0x502c4, 0x39800009);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x6000102);
+	MDP_OUTP(MDP_BASE + 0x502c8, 0x37c00006);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x7000109);
+	MDP_OUTP(MDP_BASE + 0x502cc, 0x35800004);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x840010e);
+	MDP_OUTP(MDP_BASE + 0x502d0, 0x33800002);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x9800114);
+	MDP_OUTP(MDP_BASE + 0x502d4, 0x31400000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xac00119);
+	MDP_OUTP(MDP_BASE + 0x502d8, 0x2f4003fe);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xc40011e);
+	MDP_OUTP(MDP_BASE + 0x502dc, 0x2d0003fc);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xdc00121);
+	MDP_OUTP(MDP_BASE + 0x502e0, 0x2b0003fb);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf400125);
+	MDP_OUTP(MDP_BASE + 0x502e4, 0x28c003fa);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x11000128);
+	MDP_OUTP(MDP_BASE + 0x502e8, 0x268003f9);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x12c0012a);
+	MDP_OUTP(MDP_BASE + 0x502ec, 0x244003f9);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1480012c);
+	MDP_OUTP(MDP_BASE + 0x502f0, 0x224003f8);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1640012e);
+	MDP_OUTP(MDP_BASE + 0x502f4, 0x200003f8);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1800012f);
+	MDP_OUTP(MDP_BASE + 0x502f8, 0x1e0003f8);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1a00012f);
+	MDP_OUTP(MDP_BASE + 0x502fc, 0x1c0003f8);
+}
+
+static void mdp_load_bc_downscale_table_y_point6TOpoint8(void)
+{
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe000070);
+	MDP_OUTP(MDP_BASE + 0x50300, 0x4bc00068);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe000078);
+	MDP_OUTP(MDP_BASE + 0x50304, 0x4bc00060);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe000080);
+	MDP_OUTP(MDP_BASE + 0x50308, 0x4b800059);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe000089);
+	MDP_OUTP(MDP_BASE + 0x5030c, 0x4b000052);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe400091);
+	MDP_OUTP(MDP_BASE + 0x50310, 0x4a80004b);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe40009a);
+	MDP_OUTP(MDP_BASE + 0x50314, 0x4a000044);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe8000a3);
+	MDP_OUTP(MDP_BASE + 0x50318, 0x4940003d);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfec000ac);
+	MDP_OUTP(MDP_BASE + 0x5031c, 0x48400037);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xff0000b4);
+	MDP_OUTP(MDP_BASE + 0x50320, 0x47800031);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xff8000bd);
+	MDP_OUTP(MDP_BASE + 0x50324, 0x4640002b);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xc5);
+	MDP_OUTP(MDP_BASE + 0x50328, 0x45000026);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x8000ce);
+	MDP_OUTP(MDP_BASE + 0x5032c, 0x43800021);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x10000d6);
+	MDP_OUTP(MDP_BASE + 0x50330, 0x4240001c);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x18000df);
+	MDP_OUTP(MDP_BASE + 0x50334, 0x40800018);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x24000e6);
+	MDP_OUTP(MDP_BASE + 0x50338, 0x3f000014);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x30000ee);
+	MDP_OUTP(MDP_BASE + 0x5033c, 0x3d400010);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x40000f5);
+	MDP_OUTP(MDP_BASE + 0x50340, 0x3b80000c);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x50000fc);
+	MDP_OUTP(MDP_BASE + 0x50344, 0x39800009);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x6000102);
+	MDP_OUTP(MDP_BASE + 0x50348, 0x37c00006);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x7000109);
+	MDP_OUTP(MDP_BASE + 0x5034c, 0x35800004);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x840010e);
+	MDP_OUTP(MDP_BASE + 0x50350, 0x33800002);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x9800114);
+	MDP_OUTP(MDP_BASE + 0x50354, 0x31400000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xac00119);
+	MDP_OUTP(MDP_BASE + 0x50358, 0x2f4003fe);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xc40011e);
+	MDP_OUTP(MDP_BASE + 0x5035c, 0x2d0003fc);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xdc00121);
+	MDP_OUTP(MDP_BASE + 0x50360, 0x2b0003fb);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf400125);
+	MDP_OUTP(MDP_BASE + 0x50364, 0x28c003fa);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x11000128);
+	MDP_OUTP(MDP_BASE + 0x50368, 0x268003f9);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x12c0012a);
+	MDP_OUTP(MDP_BASE + 0x5036c, 0x244003f9);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1480012c);
+	MDP_OUTP(MDP_BASE + 0x50370, 0x224003f8);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1640012e);
+	MDP_OUTP(MDP_BASE + 0x50374, 0x200003f8);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1800012f);
+	MDP_OUTP(MDP_BASE + 0x50378, 0x1e0003f8);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x1a00012f);
+	MDP_OUTP(MDP_BASE + 0x5037c, 0x1c0003f8);
+}
+
+static void mdp_load_bc_downscale_table_x_point8TO1(void)
+{
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50280, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xff80000d);
+	MDP_OUTP(MDP_BASE + 0x50284, 0x7ec003f9);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfec0001c);
+	MDP_OUTP(MDP_BASE + 0x50288, 0x7d4003f3);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe40002b);
+	MDP_OUTP(MDP_BASE + 0x5028c, 0x7b8003ed);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfd80003c);
+	MDP_OUTP(MDP_BASE + 0x50290, 0x794003e8);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfcc0004d);
+	MDP_OUTP(MDP_BASE + 0x50294, 0x76c003e4);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfc40005f);
+	MDP_OUTP(MDP_BASE + 0x50298, 0x73c003e0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfb800071);
+	MDP_OUTP(MDP_BASE + 0x5029c, 0x708003de);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfac00085);
+	MDP_OUTP(MDP_BASE + 0x502a0, 0x6d0003db);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfa000098);
+	MDP_OUTP(MDP_BASE + 0x502a4, 0x698003d9);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf98000ac);
+	MDP_OUTP(MDP_BASE + 0x502a8, 0x654003d8);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf8c000c1);
+	MDP_OUTP(MDP_BASE + 0x502ac, 0x610003d7);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf84000d5);
+	MDP_OUTP(MDP_BASE + 0x502b0, 0x5c8003d7);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf7c000e9);
+	MDP_OUTP(MDP_BASE + 0x502b4, 0x580003d7);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf74000fd);
+	MDP_OUTP(MDP_BASE + 0x502b8, 0x534003d8);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf6c00112);
+	MDP_OUTP(MDP_BASE + 0x502bc, 0x4e8003d8);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf6800126);
+	MDP_OUTP(MDP_BASE + 0x502c0, 0x494003da);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf600013a);
+	MDP_OUTP(MDP_BASE + 0x502c4, 0x448003db);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf600014d);
+	MDP_OUTP(MDP_BASE + 0x502c8, 0x3f4003dd);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf5c00160);
+	MDP_OUTP(MDP_BASE + 0x502cc, 0x3a4003df);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf5c00172);
+	MDP_OUTP(MDP_BASE + 0x502d0, 0x354003e1);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf5c00184);
+	MDP_OUTP(MDP_BASE + 0x502d4, 0x304003e3);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf6000195);
+	MDP_OUTP(MDP_BASE + 0x502d8, 0x2b0003e6);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf64001a6);
+	MDP_OUTP(MDP_BASE + 0x502dc, 0x260003e8);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf6c001b4);
+	MDP_OUTP(MDP_BASE + 0x502e0, 0x214003eb);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf78001c2);
+	MDP_OUTP(MDP_BASE + 0x502e4, 0x1c4003ee);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf80001cf);
+	MDP_OUTP(MDP_BASE + 0x502e8, 0x17c003f1);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf90001db);
+	MDP_OUTP(MDP_BASE + 0x502ec, 0x134003f3);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfa0001e5);
+	MDP_OUTP(MDP_BASE + 0x502f0, 0xf0003f6);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfb4001ee);
+	MDP_OUTP(MDP_BASE + 0x502f4, 0xac003f9);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfcc001f5);
+	MDP_OUTP(MDP_BASE + 0x502f8, 0x70003fb);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe4001fb);
+	MDP_OUTP(MDP_BASE + 0x502fc, 0x34003fe);
+}
+
+static void mdp_load_bc_downscale_table_y_point8TO1(void)
+{
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0x0);
+	MDP_OUTP(MDP_BASE + 0x50300, 0x7fc00000);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xff80000d);
+	MDP_OUTP(MDP_BASE + 0x50304, 0x7ec003f9);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfec0001c);
+	MDP_OUTP(MDP_BASE + 0x50308, 0x7d4003f3);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe40002b);
+	MDP_OUTP(MDP_BASE + 0x5030c, 0x7b8003ed);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfd80003c);
+	MDP_OUTP(MDP_BASE + 0x50310, 0x794003e8);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfcc0004d);
+	MDP_OUTP(MDP_BASE + 0x50314, 0x76c003e4);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfc40005f);
+	MDP_OUTP(MDP_BASE + 0x50318, 0x73c003e0);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfb800071);
+	MDP_OUTP(MDP_BASE + 0x5031c, 0x708003de);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfac00085);
+	MDP_OUTP(MDP_BASE + 0x50320, 0x6d0003db);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfa000098);
+	MDP_OUTP(MDP_BASE + 0x50324, 0x698003d9);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf98000ac);
+	MDP_OUTP(MDP_BASE + 0x50328, 0x654003d8);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf8c000c1);
+	MDP_OUTP(MDP_BASE + 0x5032c, 0x610003d7);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf84000d5);
+	MDP_OUTP(MDP_BASE + 0x50330, 0x5c8003d7);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf7c000e9);
+	MDP_OUTP(MDP_BASE + 0x50334, 0x580003d7);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf74000fd);
+	MDP_OUTP(MDP_BASE + 0x50338, 0x534003d8);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf6c00112);
+	MDP_OUTP(MDP_BASE + 0x5033c, 0x4e8003d8);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf6800126);
+	MDP_OUTP(MDP_BASE + 0x50340, 0x494003da);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf600013a);
+	MDP_OUTP(MDP_BASE + 0x50344, 0x448003db);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf600014d);
+	MDP_OUTP(MDP_BASE + 0x50348, 0x3f4003dd);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf5c00160);
+	MDP_OUTP(MDP_BASE + 0x5034c, 0x3a4003df);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf5c00172);
+	MDP_OUTP(MDP_BASE + 0x50350, 0x354003e1);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf5c00184);
+	MDP_OUTP(MDP_BASE + 0x50354, 0x304003e3);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf6000195);
+	MDP_OUTP(MDP_BASE + 0x50358, 0x2b0003e6);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf64001a6);
+	MDP_OUTP(MDP_BASE + 0x5035c, 0x260003e8);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf6c001b4);
+	MDP_OUTP(MDP_BASE + 0x50360, 0x214003eb);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf78001c2);
+	MDP_OUTP(MDP_BASE + 0x50364, 0x1c4003ee);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf80001cf);
+	MDP_OUTP(MDP_BASE + 0x50368, 0x17c003f1);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xf90001db);
+	MDP_OUTP(MDP_BASE + 0x5036c, 0x134003f3);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfa0001e5);
+	MDP_OUTP(MDP_BASE + 0x50370, 0xf0003f6);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfb4001ee);
+	MDP_OUTP(MDP_BASE + 0x50374, 0xac003f9);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfcc001f5);
+	MDP_OUTP(MDP_BASE + 0x50378, 0x70003fb);
+	MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe4001fb);
+	MDP_OUTP(MDP_BASE + 0x5037c, 0x34003fe);
+}
+
+static int mdp_get_edge_cond(MDPIBUF *iBuf, uint32 *dup, uint32 *dup2)
+{
+	uint32 reg;
+	uint32 dst_roi_width;	/* Dimensions of DST ROI. */
+	uint32 dst_roi_height;	/* Used to calculate scaling ratios. */
+
+	/*
+	 * positions of the luma pixel(relative to the image ) required for
+	 * scaling the ROI
+	 */
+	int32 luma_interp_point_left = 0; /* left-most luma pixel needed */
+	int32 luma_interp_point_right = 0; /* right-most luma pixel needed */
+	int32 luma_interp_point_top = 0; /* top-most luma pixel needed */
+	int32 luma_interp_point_bottom = 0; /* bottom-most luma pixel needed */
+
+	/*
+	 * positions of the chroma pixel(relative to the image ) required for
+	 * interpolating a chroma value at all required luma positions
+	 */
+	/* left-most chroma pixel needed */
+	int32 chroma_interp_point_left = 0;
+	/* right-most chroma pixel needed */
+	int32 chroma_interp_point_right = 0;
+	/* top-most chroma pixel needed */
+	int32 chroma_interp_point_top = 0;
+	/* bottom-most chroma pixel needed */
+	int32 chroma_interp_point_bottom = 0;
+
+	/*
+	 * a rectangular region within the chroma plane of the "image".
+	 * Chroma pixels falling inside of this rectangle belongs to the ROI
+	 */
+	int32 chroma_bound_left = 0;
+	int32 chroma_bound_right = 0;
+	int32 chroma_bound_top = 0;
+	int32 chroma_bound_bottom = 0;
+
+	/*
+	 * number of chroma pixels to replicate on the left, right,
+	 * top and bottom edge of the ROI.
+	 */
+	int32 chroma_repeat_left = 0;
+	int32 chroma_repeat_right = 0;
+	int32 chroma_repeat_top = 0;
+	int32 chroma_repeat_bottom = 0;
+
+	/*
+	 * number of luma pixels to replicate on the left, right,
+	 * top and bottom edge of the ROI.
+	 */
+	int32 luma_repeat_left = 0;
+	int32 luma_repeat_right = 0;
+	int32 luma_repeat_top = 0;
+	int32 luma_repeat_bottom = 0;
+
+	boolean chroma_edge_enable;
+
+	uint32 _is_scale_enabled = 0;
+	uint32 _is_yuv_offsite_vertical = 0;
+
+	/* fg edge duplicate */
+	reg = 0x0;
+
+	if (iBuf->mdpImg.mdpOp & MDPOP_ASCALE) {	/* if scaling enabled */
+
+		_is_scale_enabled = 1;
+
+		/*
+		 * if rotation mode involves a 90 deg rotation, flip
+		 * dst_roi_width with dst_roi_height.
+		 * Scaling ratios is based on source ROI dimensions, and
+		 * dst ROI dimensions before rotation.
+		 */
+		if (iBuf->mdpImg.mdpOp & MDPOP_ROT90) {
+			dst_roi_width = iBuf->roi.dst_height;
+			dst_roi_height = iBuf->roi.dst_width;
+		} else {
+			dst_roi_width = iBuf->roi.dst_width;
+			dst_roi_height = iBuf->roi.dst_height;
+		}
+
+		/*
+		 * Find out the luma pixels needed for scaling in the
+		 * x direction (LEFT and RIGHT).  Locations of pixels are
+		 * relative to the ROI. Upper-left corner of ROI corresponds
+		 * to coordinates (0,0). Also set the number of luma pixel
+		 * to repeat.
+		 */
+		if (iBuf->roi.width > 3 * dst_roi_width) {
+			/* scale factor < 1/3 */
+			luma_interp_point_left = 0;
+			luma_interp_point_right = (iBuf->roi.width - 1);
+			luma_repeat_left = 0;
+			luma_repeat_right = 0;
+		} else if (iBuf->roi.width == 3 * dst_roi_width) {
+			/* scale factor == 1/3 */
+			luma_interp_point_left = 0;
+			luma_interp_point_right = (iBuf->roi.width - 1) + 1;
+			luma_repeat_left = 0;
+			luma_repeat_right = 1;
+		} else if ((iBuf->roi.width > dst_roi_width) &&
+			   (iBuf->roi.width < 3 * dst_roi_width)) {
+			/* 1/3 < scale factor < 1 */
+			luma_interp_point_left = -1;
+			luma_interp_point_right = (iBuf->roi.width - 1) + 1;
+			luma_repeat_left = 1;
+			luma_repeat_right = 1;
+		}
+
+		else if (iBuf->roi.width == dst_roi_width) {
+			/* scale factor == 1 */
+			luma_interp_point_left = -1;
+			luma_interp_point_right = (iBuf->roi.width - 1) + 2;
+			luma_repeat_left = 1;
+			luma_repeat_right = 2;
+		} else {	/* (iBuf->roi.width < dst_roi_width) */
+			  /* scale factor > 1 */
+			luma_interp_point_left = -2;
+			luma_interp_point_right = (iBuf->roi.width - 1) + 2;
+			luma_repeat_left = 2;
+			luma_repeat_right = 2;
+		}
+
+		/*
+		 * Find out the number of pixels needed for scaling in the
+		 * y direction (TOP and BOTTOM).  Locations of pixels are
+		 * relative to the ROI. Upper-left corner of ROI corresponds
+		 * to coordinates (0,0). Also set the number of luma pixel
+		 * to repeat.
+		 */
+		if (iBuf->roi.height > 3 * dst_roi_height) {
+			/* scale factor < 1/3 */
+			luma_interp_point_top = 0;
+			luma_interp_point_bottom = (iBuf->roi.height - 1);
+			luma_repeat_top = 0;
+			luma_repeat_bottom = 0;
+		} else if (iBuf->roi.height == 3 * dst_roi_height) {
+			/* scale factor == 1/3 */
+			luma_interp_point_top = 0;
+			luma_interp_point_bottom = (iBuf->roi.height - 1) + 1;
+			luma_repeat_top = 0;
+			luma_repeat_bottom = 1;
+		} else if ((iBuf->roi.height > dst_roi_height) &&
+			   (iBuf->roi.height < 3 * dst_roi_height)) {
+			/* 1/3 < scale factor < 1 */
+			luma_interp_point_top = -1;
+			luma_interp_point_bottom = (iBuf->roi.height - 1) + 1;
+			luma_repeat_top = 1;
+			luma_repeat_bottom = 1;
+		} else if (iBuf->roi.height == dst_roi_height) {
+			/* scale factor == 1 */
+			luma_interp_point_top = -1;
+			luma_interp_point_bottom = (iBuf->roi.height - 1) + 2;
+			luma_repeat_top = 1;
+			luma_repeat_bottom = 2;
+		} else {	/* (iBuf->roi.height < dst_roi_height) */
+			 /* scale factor > 1 */
+			luma_interp_point_top = -2;
+			luma_interp_point_bottom = (iBuf->roi.height - 1) + 2;
+			luma_repeat_top = 2;
+			luma_repeat_bottom = 2;
+		}
+	}			/* if (iBuf->scale.scale_flag) */
+	else {			/* scaling disabled */
+		/*
+		 * Since no scaling needed, Tile Fetch does not require any
+		 * more luma pixel than what the ROI contains.
+		 */
+		luma_interp_point_left = (int32) 0;
+		luma_interp_point_right = (int32) (iBuf->roi.width - 1);
+		luma_interp_point_top = (int32) 0;
+		luma_interp_point_bottom = (int32) (iBuf->roi.height - 1);
+
+		luma_repeat_left = 0;
+		luma_repeat_right = 0;
+		luma_repeat_top = 0;
+		luma_repeat_bottom = 0;
+	}
+
+	/* After adding the ROI offsets, we have locations of
+	 * luma_interp_points relative to the image.
+	 */
+	luma_interp_point_left += (int32) (iBuf->roi.x);
+	luma_interp_point_right += (int32) (iBuf->roi.x);
+	luma_interp_point_top += (int32) (iBuf->roi.y);
+	luma_interp_point_bottom += (int32) (iBuf->roi.y);
+
+	/*
+	 * After adding the ROI offsets, we have locations of
+	 * chroma_interp_points relative to the image.
+	 */
+	chroma_interp_point_left = luma_interp_point_left;
+	chroma_interp_point_right = luma_interp_point_right;
+	chroma_interp_point_top = luma_interp_point_top;
+	chroma_interp_point_bottom = luma_interp_point_bottom;
+
+	chroma_edge_enable = TRUE;
+	/* find out which chroma pixels are needed for chroma upsampling. */
+	switch (iBuf->mdpImg.imgType) {
+		/*
+		 * cosite in horizontal axis
+		 * fully sampled in vertical axis
+		 */
+	case MDP_Y_CBCR_H2V1:
+	case MDP_Y_CRCB_H2V1:
+	case MDP_YCRYCB_H2V1:
+		/* floor( luma_interp_point_left / 2 ); */
+		chroma_interp_point_left = luma_interp_point_left >> 1;
+		/* floor( ( luma_interp_point_right + 1 ) / 2 ); */
+		chroma_interp_point_right = (luma_interp_point_right + 1) >> 1;
+
+		chroma_interp_point_top = luma_interp_point_top;
+		chroma_interp_point_bottom = luma_interp_point_bottom;
+		break;
+
+		/*
+		 * cosite in horizontal axis
+		 * offsite in vertical axis
+		 */
+	case MDP_Y_CBCR_H2V2:
+	case MDP_Y_CBCR_H2V2_ADRENO:
+	case MDP_Y_CRCB_H2V2:
+		/* floor( luma_interp_point_left / 2) */
+		chroma_interp_point_left = luma_interp_point_left >> 1;
+
+		/* floor( ( luma_interp_point_right + 1 )/ 2 ) */
+		chroma_interp_point_right = (luma_interp_point_right + 1) >> 1;
+
+		/* floor( (luma_interp_point_top - 1 ) / 2 ) */
+		chroma_interp_point_top = (luma_interp_point_top - 1) >> 1;
+
+		/* floor( ( luma_interp_point_bottom + 1 ) / 2 ) */
+		chroma_interp_point_bottom =
+		    (luma_interp_point_bottom + 1) >> 1;
+
+		_is_yuv_offsite_vertical = 1;
+		break;
+
+	default:
+		chroma_edge_enable = FALSE;
+		chroma_interp_point_left = luma_interp_point_left;
+		chroma_interp_point_right = luma_interp_point_right;
+		chroma_interp_point_top = luma_interp_point_top;
+		chroma_interp_point_bottom = luma_interp_point_bottom;
+
+		break;
+	}
+
+	/* only if the image type is in YUV domain, we calculate chroma edge */
+	if (chroma_edge_enable) {
+		/* Defines which chroma pixels belongs to the roi */
+		switch (iBuf->mdpImg.imgType) {
+			/*
+			 * Cosite in horizontal direction, and fully sampled
+			 * in vertical direction.
+			 */
+		case MDP_Y_CBCR_H2V1:
+		case MDP_Y_CRCB_H2V1:
+		case MDP_YCRYCB_H2V1:
+			/*
+			 * width of chroma ROI is 1/2 of size of luma ROI
+			 * height of chroma ROI same as size of luma ROI
+			 */
+			chroma_bound_left = iBuf->roi.x / 2;
+
+			/* there are half as many chroma pixel as luma pixels */
+			chroma_bound_right =
+			    (iBuf->roi.width + iBuf->roi.x - 1) / 2;
+			chroma_bound_top = iBuf->roi.y;
+			chroma_bound_bottom =
+			    (iBuf->roi.height + iBuf->roi.y - 1);
+			break;
+
+		case MDP_Y_CBCR_H2V2:
+		case MDP_Y_CBCR_H2V2_ADRENO:
+		case MDP_Y_CRCB_H2V2:
+			/*
+			 * cosite in horizontal dir, and offsite in vertical dir
+			 * width of chroma ROI is 1/2 of size of luma ROI
+			 * height of chroma ROI is 1/2 of size of luma ROI
+			 */
+
+			chroma_bound_left = iBuf->roi.x / 2;
+			chroma_bound_right =
+			    (iBuf->roi.width + iBuf->roi.x - 1) / 2;
+			chroma_bound_top = iBuf->roi.y / 2;
+			chroma_bound_bottom =
+			    (iBuf->roi.height + iBuf->roi.y - 1) / 2;
+			break;
+
+		default:
+			/*
+			 * If no valid chroma sub-sampling format specified,
+			 * assume 4:4:4 ( i.e. fully sampled).  Set ROI
+			 * boundaries for chroma same as ROI boundaries for
+			 * luma.
+			 */
+			chroma_bound_left = iBuf->roi.x;
+			chroma_bound_right = iBuf->roi.width + iBuf->roi.x - 1;
+			chroma_bound_top = iBuf->roi.y;
+			chroma_bound_bottom =
+			    (iBuf->roi.height + iBuf->roi.y - 1);
+			break;
+		}
+
+		/*
+		 * Knowing which chroma pixels are needed, and which chroma
+		 * pixels belong to the ROI (i.e. available for fetching ),
+		 * calculate how many chroma pixels Tile Fetch needs to
+		 * duplicate.  If any required chroma pixels falls outside
+		 * of the ROI, Tile Fetch must obtain them by replicating
+		 * pixels.
+		 */
+		if (chroma_bound_left > chroma_interp_point_left)
+			chroma_repeat_left =
+			    chroma_bound_left - chroma_interp_point_left;
+		else
+			chroma_repeat_left = 0;
+
+		if (chroma_interp_point_right > chroma_bound_right)
+			chroma_repeat_right =
+			    chroma_interp_point_right - chroma_bound_right;
+		else
+			chroma_repeat_right = 0;
+
+		if (chroma_bound_top > chroma_interp_point_top)
+			chroma_repeat_top =
+			    chroma_bound_top - chroma_interp_point_top;
+		else
+			chroma_repeat_top = 0;
+
+		if (chroma_interp_point_bottom > chroma_bound_bottom)
+			chroma_repeat_bottom =
+			    chroma_interp_point_bottom - chroma_bound_bottom;
+		else
+			chroma_repeat_bottom = 0;
+
+		if (_is_scale_enabled && (iBuf->roi.height == 1)
+		    && _is_yuv_offsite_vertical) {
+			chroma_repeat_bottom = 3;
+			chroma_repeat_top = 0;
+		}
+	}
+	/* make sure chroma repeats are non-negative */
+	if ((chroma_repeat_left < 0) || (chroma_repeat_right < 0) ||
+	    (chroma_repeat_top < 0) || (chroma_repeat_bottom < 0))
+		return -1;
+
+	/* make sure chroma repeats are no larger than 3 pixels */
+	if ((chroma_repeat_left > 3) || (chroma_repeat_right > 3) ||
+	    (chroma_repeat_top > 3) || (chroma_repeat_bottom > 3))
+		return -1;
+
+	/* make sure luma repeats are non-negative */
+	if ((luma_repeat_left < 0) || (luma_repeat_right < 0) ||
+	    (luma_repeat_top < 0) || (luma_repeat_bottom < 0))
+		return -1;
+
+	/* make sure luma repeats are no larger than 3 pixels */
+	if ((luma_repeat_left > 3) || (luma_repeat_right > 3) ||
+	    (luma_repeat_top > 3) || (luma_repeat_bottom > 3))
+		return -1;
+
+	/* write chroma_repeat_left to register */
+	reg |= (chroma_repeat_left & 3) << MDP_LEFT_CHROMA;
+
+	/* write chroma_repeat_right to register */
+	reg |= (chroma_repeat_right & 3) << MDP_RIGHT_CHROMA;
+
+	/* write chroma_repeat_top to register */
+	reg |= (chroma_repeat_top & 3) << MDP_TOP_CHROMA;
+
+	/* write chroma_repeat_bottom to register */
+	reg |= (chroma_repeat_bottom & 3) << MDP_BOTTOM_CHROMA;
+
+	/* write luma_repeat_left to register */
+	reg |= (luma_repeat_left & 3) << MDP_LEFT_LUMA;
+
+	/* write luma_repeat_right to register */
+	reg |= (luma_repeat_right & 3) << MDP_RIGHT_LUMA;
+
+	/* write luma_repeat_top to register */
+	reg |= (luma_repeat_top & 3) << MDP_TOP_LUMA;
+
+	/* write luma_repeat_bottom to register */
+	reg |= (luma_repeat_bottom & 3) << MDP_BOTTOM_LUMA;
+
+	/* done with reg */
+	*dup = reg;
+
+	/* bg edge duplicate */
+	reg = 0x0;
+
+	switch (iBuf->ibuf_type) {
+	case MDP_Y_CBCR_H2V2:
+	case MDP_Y_CRCB_H2V2:
+		/*
+		 * Edge condition for MDP_Y_CRCB/CBCR_H2V2 cosite only.
+		 * For 420 cosite, 1 chroma replicated on all sides except
+		 * left, so reg 101b8 should be 0x0209. For 420 offsite,
+		 * 1 chroma replicated all sides.
+		 */
+		if (iBuf->roi.lcd_y == 0) {
+			reg |= BIT(MDP_TOP_CHROMA);
+		}
+
+		if ((iBuf->roi.lcd_y + iBuf->roi.dst_height) ==
+		    iBuf->ibuf_height) {
+			reg |= BIT(MDP_BOTTOM_CHROMA);
+		}
+
+		if (((iBuf->roi.lcd_x + iBuf->roi.dst_width) ==
+		     iBuf->ibuf_width) && ((iBuf->roi.dst_width % 2) == 0)) {
+			reg |= BIT(MDP_RIGHT_CHROMA);
+		}
+
+		break;
+
+	case MDP_Y_CBCR_H2V1:
+	case MDP_Y_CRCB_H2V1:
+	case MDP_YCRYCB_H2V1:
+		if (((iBuf->roi.lcd_x + iBuf->roi.dst_width) ==
+		     iBuf->ibuf_width) && ((iBuf->roi.dst_width % 2) == 0)) {
+			reg |= BIT(MDP_RIGHT_CHROMA);
+		}
+		break;
+	default:
+		break;
+	}
+
+	*dup2 = reg;
+
+	return 0;
+}
+
+#define ADJUST_IP		/* for 1/3 scale factor fix */
+
+static int mdp_calc_scale_params(
+/* ROI origin coordinate for the dimension */
+					uint32 org,
+/* src ROI dimension */
+					uint32 dim_in,
+/* scaled ROI dimension*/
+					uint32 dim_out,
+/* is this ROI width dimension? */
+					boolean is_W,
+/* initial phase location address */
+					int32 *phase_init_ptr,
+/* phase increment location address */
+					uint32 *phase_step_ptr,
+/* ROI start over-fetch location address */
+					uint32 *num_repl_beg_ptr,
+/* ROI end over-fetch location address */
+					uint32 *num_repl_end_ptr)
+{
+	boolean rpa_on = FALSE;
+	int init_phase = 0;
+	uint32 beg_of = 0;
+	uint32 end_of = 0;
+	uint64 numer = 0;
+	uint64 denom = 0;
+	/*uint64 inverter = 1; */
+	int64 point5 = 1;
+	int64 one = 1;
+	int64 k1, k2, k3, k4;	/* linear equation coefficients */
+	uint64 int_mask;
+	uint64 fract_mask;
+	uint64 Os;
+	int64 Osprime;
+	int64 Od;
+	int64 Odprime;
+	int64 Oreq;
+	uint64 Es;
+	uint64 Ed;
+	uint64 Ereq;
+#ifdef ADJUST_IP
+	int64 IP64;
+	int64 delta;
+#endif
+	uint32 mult;
+
+	/*
+	 * The phase accumulator should really be rational for all cases in a
+	 * general purpose polyphase scaler for a tiled architecture with
+	 * non-zero * origin capability because there is no way to represent
+	 * certain scale factors in fixed point regardless of precision.
+	 * The error incurred in attempting to use fixed point is most
+	 * eggregious for SF where 1/SF is an integral multiple of 1/3.
+	 *
+	 * However, since the MDP2 has already been committed to HW, we
+	 * only use the rational phase accumulator (RPA) when 1/SF is an
+	 * integral multiple of 1/3.  This will help minimize regressions in
+	 * matching the HW to the C-Sim.
+	 */
+	/*
+	 * Set the RPA flag for this dimension.
+	 *
+	 * In order for 1/SF (dim_in/dim_out) to be an integral multiple of
+	 * 1/3, dim_out must be an integral multiple of 3.
+	 */
+	if (!(dim_out % 3)) {
+		mult = dim_out / 3;
+		rpa_on = (!(dim_in % mult));
+	}
+
+	numer = dim_out;
+	denom = dim_in;
+
+	/*
+	 * convert to U30.34 before division
+	 *
+	 * The K vectors carry 4 extra bits of precision
+	 * and are rounded.
+	 *
+	 * We initially go 5 bits over then round by adding
+	 * 1 and right shifting by 1
+	 * so final result is U31.33
+	 */
+	numer <<= PQF_PLUS_5;
+
+	/* now calculate the scale factor (aka k3) */
+	k3 = ((mdp_do_div(numer, denom) + 1) >> 1);
+
+	/* check scale factor for legal range [0.25 - 4.0] */
+	if (((k3 >> 4) < (1LL << PQF_MINUS_2)) ||
+	    ((k3 >> 4) > (1LL << PQF_PLUS_2))) {
+		return -1;
+	}
+
+	/* calculate inverse scale factor (aka k1) for phase init */
+	numer = dim_in;
+	denom = dim_out;
+	numer <<= PQF_PLUS_5;
+	k1 = ((mdp_do_div(numer, denom) + 1) >> 1);
+
+	/*
+	 * calculate initial phase and ROI overfetch
+	 */
+	/* convert point5 & one to S39.24 (will always be positive) */
+	point5 <<= (PQF_PLUS_4 - 1);
+	one <<= PQF_PLUS_4;
+	k2 = ((k1 - one) >> 1);
+	init_phase = (int)(k2 >> 4);
+	k4 = ((k3 - one) >> 1);
+	if (k3 == one) {
+		/* the simple case; SF = 1.0 */
+		beg_of = 1;
+		end_of = 2;
+	} else {
+		/* calculate the masks */
+		fract_mask = one - 1;
+		int_mask = ~fract_mask;
+
+		if (!rpa_on) {
+			/*
+			 * FIXED POINT IMPLEMENTATION
+			 */
+			if (!org) {
+				/* A fairly simple case; ROI origin = 0 */
+				if (k1 < one) {
+					/* upscaling */
+					beg_of = end_of = 2;
+				}
+				/* 0.33 <= SF < 1.0 */
+				else if (k1 < (3LL << PQF_PLUS_4))
+					beg_of = end_of = 1;
+				/* 0.33 == SF */
+				else if (k1 == (3LL << PQF_PLUS_4)) {
+					beg_of = 0;
+					end_of = 1;
+				}
+				/* 0.25 <= SF < 0.33 */
+				else
+					beg_of = end_of = 0;
+			} else {
+				/*
+				 * The complicated case; ROI origin != 0
+				 * init_phase needs to be adjusted
+				 * OF is also position dependent
+				 */
+
+				/* map (org - .5) into destination space */
+				Os = ((uint64) org << 1) - 1;
+				Od = ((k3 * Os) >> 1) + k4;
+
+				/* take the ceiling */
+				Odprime = (Od & int_mask);
+				if (Odprime != Od)
+					Odprime += one;
+
+				/* now map that back to source space */
+				Osprime = (k1 * (Odprime >> PQF_PLUS_4)) + k2;
+
+				/* then floor & decrement to calculate the required
+				   starting coordinate */
+				Oreq = (Osprime & int_mask) - one;
+
+				/* calculate end coord in destination space then map to
+				   source space */
+				Ed = Odprime +
+				    ((uint64) dim_out << PQF_PLUS_4) - one;
+				Es = (k1 * (Ed >> PQF_PLUS_4)) + k2;
+
+				/* now floor & increment by 2 to calculate the required
+				   ending coordinate */
+				Ereq = (Es & int_mask) + (one << 1);
+
+				/* calculate initial phase */
+#ifdef ADJUST_IP
+
+				IP64 = Osprime - Oreq;
+				delta = ((int64) (org) << PQF_PLUS_4) - Oreq;
+				IP64 -= delta;
+
+				/* limit to valid range before the left shift */
+				delta = (IP64 & (1LL << 63)) ? 4 : -4;
+				delta <<= PQF_PLUS_4;
+				while (abs((int)(IP64 >> PQF_PLUS_4)) > 4)
+					IP64 += delta;
+
+				/* right shift to account for extra bits of precision */
+				init_phase = (int)(IP64 >> 4);
+
+#else /* ADJUST_IP */
+
+				/* just calculate the real initial phase */
+				init_phase = (int)((Osprime - Oreq) >> 4);
+
+#endif /* ADJUST_IP */
+
+				/* calculate the overfetch */
+				beg_of = org - (uint32) (Oreq >> PQF_PLUS_4);
+				end_of =
+				    (uint32) (Ereq >> PQF_PLUS_4) - (org +
+								     dim_in -
+								     1);
+			}
+		} else {
+			/*
+			 * RPA IMPLEMENTATION
+			 *
+			 * init_phase needs to be calculated in all RPA_on cases
+			 * because it's a numerator, not a fixed point value.
+			 */
+
+			/* map (org - .5) into destination space */
+			Os = ((uint64) org << PQF_PLUS_4) - point5;
+			Od = mdp_do_div((dim_out * (Os + point5)),
+					dim_in) - point5;
+
+			/* take the ceiling */
+			Odprime = (Od & int_mask);
+			if (Odprime != Od)
+				Odprime += one;
+
+			/* now map that back to source space */
+			Osprime =
+			    mdp_do_div((dim_in * (Odprime + point5)),
+				       dim_out) - point5;
+
+			/* then floor & decrement to calculate the required
+			   starting coordinate */
+			Oreq = (Osprime & int_mask) - one;
+
+			/* calculate end coord in destination space then map to
+			   source space */
+			Ed = Odprime + ((uint64) dim_out << PQF_PLUS_4) - one;
+			Es = mdp_do_div((dim_in * (Ed + point5)),
+					dim_out) - point5;
+
+			/* now floor & increment by 2 to calculate the required
+			   ending coordinate */
+			Ereq = (Es & int_mask) + (one << 1);
+
+			/* calculate initial phase */
+
+#ifdef ADJUST_IP
+
+			IP64 = Osprime - Oreq;
+			delta = ((int64) (org) << PQF_PLUS_4) - Oreq;
+			IP64 -= delta;
+
+			/* limit to valid range before the left shift */
+			delta = (IP64 & (1LL << 63)) ? 4 : -4;
+			delta <<= PQF_PLUS_4;
+			while (abs((int)(IP64 >> PQF_PLUS_4)) > 4)
+				IP64 += delta;
+
+			/* right shift to account for extra bits of precision */
+			init_phase = (int)(IP64 >> 4);
+
+#else /* ADJUST_IP */
+
+			/* just calculate the real initial phase */
+			init_phase = (int)((Osprime - Oreq) >> 4);
+
+#endif /* ADJUST_IP */
+
+			/* calculate the overfetch */
+			beg_of = org - (uint32) (Oreq >> PQF_PLUS_4);
+			end_of =
+			    (uint32) (Ereq >> PQF_PLUS_4) - (org + dim_in - 1);
+		}
+	}
+
+	/* return the scale parameters */
+	*phase_init_ptr = init_phase;
+	*phase_step_ptr = (uint32) (k1 >> 4);
+	*num_repl_beg_ptr = beg_of;
+	*num_repl_end_ptr = end_of;
+
+	return 0;
+}
+
+static uint8 *mdp_adjust_rot_addr(MDPIBUF *iBuf, uint8 *addr, uint32 uv)
+{
+	uint32 dest_ystride = iBuf->ibuf_width * iBuf->bpp;
+	uint32 h_slice = 1;
+
+	if (uv && ((iBuf->ibuf_type == MDP_Y_CBCR_H2V2) ||
+		(iBuf->ibuf_type == MDP_Y_CRCB_H2V2)))
+		h_slice = 2;
+
+	if (MDP_CHKBIT(iBuf->mdpImg.mdpOp, MDPOP_ROT90) ^
+	    MDP_CHKBIT(iBuf->mdpImg.mdpOp, MDPOP_LR)) {
+		addr =
+		    addr + (iBuf->roi.dst_width -
+			    MIN(16, iBuf->roi.dst_width)) * iBuf->bpp;
+	}
+	if (MDP_CHKBIT(iBuf->mdpImg.mdpOp, MDPOP_UD)) {
+		addr =
+		    addr + ((iBuf->roi.dst_height -
+			MIN(16, iBuf->roi.dst_height))/h_slice) * dest_ystride;
+	}
+
+	return addr;
+}
+
+void mdp_set_scale(MDPIBUF *iBuf,
+		   uint32 dst_roi_width,
+		   uint32 dst_roi_height,
+		   boolean inputRGB, boolean outputRGB, uint32 *pppop_reg_ptr)
+{
+	uint32 dst_roi_width_scale;
+	uint32 dst_roi_height_scale;
+	boolean use_pr;
+	uint32 phasex_step = 0;
+	uint32 phasey_step = 0;
+	int32 phasex_init = 0;
+	int32 phasey_init = 0;
+	uint32 lines_dup = 0;
+	uint32 lines_dup_bg = 0;
+	uint32 dummy;
+	uint32 mdp_blur = 0;
+
+	if (iBuf->mdpImg.mdpOp & MDPOP_ASCALE) {
+		if (iBuf->mdpImg.mdpOp & MDPOP_ROT90) {
+			dst_roi_width_scale = dst_roi_height;
+			dst_roi_height_scale = dst_roi_width;
+		} else {
+			dst_roi_width_scale = dst_roi_width;
+			dst_roi_height_scale = dst_roi_height;
+		}
+
+		mdp_blur = iBuf->mdpImg.mdpOp & MDPOP_BLUR;
+
+		if ((dst_roi_width_scale != iBuf->roi.width) ||
+		    (dst_roi_height_scale != iBuf->roi.height) ||
+			mdp_blur) {
+			*pppop_reg_ptr |=
+			    (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON);
+
+			/* let's use SHIM logic to calculate the
+			   partial ROI scaling */
+			mdp_calc_scale_params(iBuf->roi.x, iBuf->roi.width,
+					      dst_roi_width_scale, 1,
+					      &phasex_init, &phasex_step,
+					      &dummy, &dummy);
+			mdp_calc_scale_params(iBuf->roi.y, iBuf->roi.height,
+					      dst_roi_height_scale, 0,
+					      &phasey_init, &phasey_step,
+					      &dummy, &dummy);
+			MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x013c,
+				 phasex_init);
+			MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0140,
+				 phasey_init);
+			MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0144,
+				 phasex_step);
+			MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0148,
+				 phasey_step);
+
+			/* disable the pixel repeat option for scaling */
+			use_pr = false;
+
+			if ((dst_roi_width_scale > iBuf->roi.width) ||
+			    (dst_roi_height_scale > iBuf->roi.height)) {
+				if ((use_pr)
+				    && (mdp_curr_up_scale_xy !=
+					MDP_PR_SCALE_UP)) {
+					mdp_load_pr_upscale_table();
+					mdp_curr_up_scale_xy = MDP_PR_SCALE_UP;
+				} else if ((!use_pr)
+					   && (mdp_curr_up_scale_xy !=
+					       MDP_BC_SCALE_UP)) {
+					mdp_load_bc_upscale_table();
+					mdp_curr_up_scale_xy = MDP_BC_SCALE_UP;
+				}
+			}
+
+			if (mdp_blur) {
+				load_scale_table(mdp_gaussian_blur_table,
+					ARRAY_SIZE(mdp_gaussian_blur_table));
+				mdp_curr_down_scale_x = MDP_SCALE_BLUR;
+				mdp_curr_down_scale_y = MDP_SCALE_BLUR;
+			}
+
+			/* 0.2 < x <= 1 scaling factor */
+			if ((dst_roi_width_scale <= iBuf->roi.width) &&
+				!mdp_blur) {
+				if (((dst_roi_width_scale * 10) /
+				     iBuf->roi.width) > 8) {
+					if ((use_pr)
+					    && (mdp_curr_down_scale_x !=
+						MDP_PR_SCALE_POINT8_1)) {
+						mdp_load_pr_downscale_table_x_point8TO1
+						    ();
+						mdp_curr_down_scale_x =
+						    MDP_PR_SCALE_POINT8_1;
+					} else if ((!use_pr)
+						   && (mdp_curr_down_scale_x !=
+						       MDP_BC_SCALE_POINT8_1)) {
+						mdp_load_bc_downscale_table_x_point8TO1
+						    ();
+						mdp_curr_down_scale_x =
+						    MDP_BC_SCALE_POINT8_1;
+					}
+				} else
+				    if (((dst_roi_width_scale * 10) /
+					 iBuf->roi.width) > 6) {
+					if ((use_pr)
+					    && (mdp_curr_down_scale_x !=
+						MDP_PR_SCALE_POINT6_POINT8)) {
+						mdp_load_pr_downscale_table_x_point6TOpoint8
+						    ();
+						mdp_curr_down_scale_x =
+						    MDP_PR_SCALE_POINT6_POINT8;
+					} else if ((!use_pr)
+						   && (mdp_curr_down_scale_x !=
+						       MDP_BC_SCALE_POINT6_POINT8))
+					{
+						mdp_load_bc_downscale_table_x_point6TOpoint8
+						    ();
+						mdp_curr_down_scale_x =
+						    MDP_BC_SCALE_POINT6_POINT8;
+					}
+				} else
+				    if (((dst_roi_width_scale * 10) /
+					 iBuf->roi.width) > 4) {
+					if ((use_pr)
+					    && (mdp_curr_down_scale_x !=
+						MDP_PR_SCALE_POINT4_POINT6)) {
+						mdp_load_pr_downscale_table_x_point4TOpoint6
+						    ();
+						mdp_curr_down_scale_x =
+						    MDP_PR_SCALE_POINT4_POINT6;
+					} else if ((!use_pr)
+						   && (mdp_curr_down_scale_x !=
+						       MDP_BC_SCALE_POINT4_POINT6))
+					{
+						mdp_load_bc_downscale_table_x_point4TOpoint6
+						    ();
+						mdp_curr_down_scale_x =
+						    MDP_BC_SCALE_POINT4_POINT6;
+					}
+				} else {
+					if ((use_pr)
+					    && (mdp_curr_down_scale_x !=
+						MDP_PR_SCALE_POINT2_POINT4)) {
+						mdp_load_pr_downscale_table_x_point2TOpoint4
+						    ();
+						mdp_curr_down_scale_x =
+						    MDP_PR_SCALE_POINT2_POINT4;
+					} else if ((!use_pr)
+						   && (mdp_curr_down_scale_x !=
+						       MDP_BC_SCALE_POINT2_POINT4))
+					{
+						mdp_load_bc_downscale_table_x_point2TOpoint4
+						    ();
+						mdp_curr_down_scale_x =
+						    MDP_BC_SCALE_POINT2_POINT4;
+					}
+				}
+			}
+			/* 0.2 < y <= 1 scaling factor */
+			if ((dst_roi_height_scale <= iBuf->roi.height) &&
+				!mdp_blur) {
+				if (((dst_roi_height_scale * 10) /
+				     iBuf->roi.height) > 8) {
+					if ((use_pr)
+					    && (mdp_curr_down_scale_y !=
+						MDP_PR_SCALE_POINT8_1)) {
+						mdp_load_pr_downscale_table_y_point8TO1
+						    ();
+						mdp_curr_down_scale_y =
+						    MDP_PR_SCALE_POINT8_1;
+					} else if ((!use_pr)
+						   && (mdp_curr_down_scale_y !=
+						       MDP_BC_SCALE_POINT8_1)) {
+						mdp_load_bc_downscale_table_y_point8TO1
+						    ();
+						mdp_curr_down_scale_y =
+						    MDP_BC_SCALE_POINT8_1;
+					}
+				} else
+				    if (((dst_roi_height_scale * 10) /
+					 iBuf->roi.height) > 6) {
+					if ((use_pr)
+					    && (mdp_curr_down_scale_y !=
+						MDP_PR_SCALE_POINT6_POINT8)) {
+						mdp_load_pr_downscale_table_y_point6TOpoint8
+						    ();
+						mdp_curr_down_scale_y =
+						    MDP_PR_SCALE_POINT6_POINT8;
+					} else if ((!use_pr)
+						   && (mdp_curr_down_scale_y !=
+						       MDP_BC_SCALE_POINT6_POINT8))
+					{
+						mdp_load_bc_downscale_table_y_point6TOpoint8
+						    ();
+						mdp_curr_down_scale_y =
+						    MDP_BC_SCALE_POINT6_POINT8;
+					}
+				} else
+				    if (((dst_roi_height_scale * 10) /
+					 iBuf->roi.height) > 4) {
+					if ((use_pr)
+					    && (mdp_curr_down_scale_y !=
+						MDP_PR_SCALE_POINT4_POINT6)) {
+						mdp_load_pr_downscale_table_y_point4TOpoint6
+						    ();
+						mdp_curr_down_scale_y =
+						    MDP_PR_SCALE_POINT4_POINT6;
+					} else if ((!use_pr)
+						   && (mdp_curr_down_scale_y !=
+						       MDP_BC_SCALE_POINT4_POINT6))
+					{
+						mdp_load_bc_downscale_table_y_point4TOpoint6
+						    ();
+						mdp_curr_down_scale_y =
+						    MDP_BC_SCALE_POINT4_POINT6;
+					}
+				} else {
+					if ((use_pr)
+					    && (mdp_curr_down_scale_y !=
+						MDP_PR_SCALE_POINT2_POINT4)) {
+						mdp_load_pr_downscale_table_y_point2TOpoint4
+						    ();
+						mdp_curr_down_scale_y =
+						    MDP_PR_SCALE_POINT2_POINT4;
+					} else if ((!use_pr)
+						   && (mdp_curr_down_scale_y !=
+						       MDP_BC_SCALE_POINT2_POINT4))
+					{
+						mdp_load_bc_downscale_table_y_point2TOpoint4
+						    ();
+						mdp_curr_down_scale_y =
+						    MDP_BC_SCALE_POINT2_POINT4;
+					}
+				}
+			}
+		} else {
+			iBuf->mdpImg.mdpOp &= ~(MDPOP_ASCALE);
+		}
+	}
+	/* setting edge condition here after scaling check */
+	if (mdp_get_edge_cond(iBuf, &lines_dup, &lines_dup_bg))
+		printk(KERN_ERR "msm_fb: mdp_get_edge_cond() error!\n");
+
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01b8, lines_dup);
+	MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01bc, lines_dup_bg);
+}
+
+void mdp_init_scale_table(void)
+{
+	mdp_curr_up_scale_xy = MDP_INIT_SCALE;
+	mdp_curr_down_scale_x = MDP_INIT_SCALE;
+	mdp_curr_down_scale_y = MDP_INIT_SCALE;
+}
+
+void mdp_adjust_start_addr(uint8 **src0,
+			   uint8 **src1,
+			   int v_slice,
+			   int h_slice,
+			   int x,
+			   int y,
+			   uint32 width,
+			   uint32 height, int bpp, MDPIBUF *iBuf, int layer)
+{
+	*src0 += (x + y * width) * bpp;
+
+	/* if it's dest/bg buffer, we need to adjust it for rotation */
+	if (layer != 0)
+		*src0 = mdp_adjust_rot_addr(iBuf, *src0, 0);
+
+	if (*src1) {
+		/*
+		 * MDP_Y_CBCR_H2V2/MDP_Y_CRCB_H2V2 cosite for now
+		 * we need to shift x direction same as y dir for offsite
+		 */
+		*src1 +=
+		    ((x / h_slice) * h_slice +
+		     ((y == 0) ? 0 : ((y + 1) / v_slice - 1) * width)) * bpp;
+
+		/* if it's dest/bg buffer, we need to adjust it for rotation */
+		if (layer != 0)
+			*src1 = mdp_adjust_rot_addr(iBuf, *src1, 1);
+	}
+}
+
+void mdp_set_blend_attr(MDPIBUF *iBuf,
+			uint32 *alpha,
+			uint32 *tpVal,
+			uint32 perPixelAlpha, uint32 *pppop_reg_ptr)
+{
+	if (mdp_rev == MDP_REV_303) {
+		int bg_alpha;
+
+		*alpha = iBuf->mdpImg.alpha;
+		*tpVal = iBuf->mdpImg.tpVal;
+
+		if (iBuf->mdpImg.mdpOp & MDPOP_FG_PM_ALPHA) {
+			if (perPixelAlpha) {
+				*pppop_reg_ptr |= PPP_OP_ROT_ON |
+						  PPP_OP_BLEND_ON |
+						  PPP_OP_BLEND_CONSTANT_ALPHA;
+			} else {
+				if ((iBuf->mdpImg.mdpOp & MDPOP_ALPHAB)
+					&& (iBuf->mdpImg.alpha == 0xff)) {
+					iBuf->mdpImg.mdpOp &= ~(MDPOP_ALPHAB);
+				}
+
+				if ((iBuf->mdpImg.mdpOp & MDPOP_ALPHAB)
+				   || (iBuf->mdpImg.mdpOp & MDPOP_TRANSP)) {
+
+					*pppop_reg_ptr |= PPP_OP_ROT_ON |
+						PPP_OP_BLEND_ON |
+						PPP_OP_BLEND_CONSTANT_ALPHA |
+						PPP_OP_BLEND_ALPHA_BLEND_NORMAL;
+				}
+			}
+
+			bg_alpha = PPP_BLEND_BG_USE_ALPHA_SEL |
+				PPP_BLEND_BG_ALPHA_REVERSE;
+
+			if (perPixelAlpha) {
+				bg_alpha |= PPP_BLEND_BG_SRCPIXEL_ALPHA;
+			} else {
+				bg_alpha |= PPP_BLEND_BG_CONSTANT_ALPHA;
+				bg_alpha |= iBuf->mdpImg.alpha << 24;
+			}
+			outpdw(MDP_BASE + 0x70010, bg_alpha);
+
+			if (iBuf->mdpImg.mdpOp & MDPOP_TRANSP)
+				*pppop_reg_ptr |= PPP_BLEND_CALPHA_TRNASP;
+		} else if (perPixelAlpha) {
+				*pppop_reg_ptr |= PPP_OP_ROT_ON |
+						  PPP_OP_BLEND_ON |
+						  PPP_OP_BLEND_SRCPIXEL_ALPHA;
+				outpdw(MDP_BASE + 0x70010, 0);
+			} else {
+				if ((iBuf->mdpImg.mdpOp & MDPOP_ALPHAB)
+					&& (iBuf->mdpImg.alpha == 0xff)) {
+						iBuf->mdpImg.mdpOp &=
+							~(MDPOP_ALPHAB);
+				}
+
+				if ((iBuf->mdpImg.mdpOp & MDPOP_ALPHAB)
+				   || (iBuf->mdpImg.mdpOp & MDPOP_TRANSP)) {
+					*pppop_reg_ptr |= PPP_OP_ROT_ON |
+						PPP_OP_BLEND_ON |
+						PPP_OP_BLEND_CONSTANT_ALPHA |
+						PPP_OP_BLEND_ALPHA_BLEND_NORMAL;
+				}
+
+				if (iBuf->mdpImg.mdpOp & MDPOP_TRANSP)
+					*pppop_reg_ptr |=
+						PPP_BLEND_CALPHA_TRNASP;
+				outpdw(MDP_BASE + 0x70010, 0);
+			}
+	} else {
+		if (perPixelAlpha) {
+			*pppop_reg_ptr |= PPP_OP_ROT_ON |
+			    PPP_OP_BLEND_ON | PPP_OP_BLEND_SRCPIXEL_ALPHA;
+		} else {
+			if ((iBuf->mdpImg.mdpOp & MDPOP_ALPHAB)
+			    && (iBuf->mdpImg.alpha == 0xff)) {
+				iBuf->mdpImg.mdpOp &= ~(MDPOP_ALPHAB);
+			}
+
+			if ((iBuf->mdpImg.mdpOp & MDPOP_ALPHAB)
+			    && (iBuf->mdpImg.mdpOp & MDPOP_TRANSP)) {
+				*pppop_reg_ptr |=
+				    PPP_OP_ROT_ON | PPP_OP_BLEND_ON |
+				    PPP_OP_BLEND_CONSTANT_ALPHA |
+				    PPP_OP_BLEND_ALPHA_BLEND_NORMAL |
+				    PPP_BLEND_CALPHA_TRNASP;
+
+				*alpha = iBuf->mdpImg.alpha;
+				*tpVal = iBuf->mdpImg.tpVal;
+			} else {
+				if (iBuf->mdpImg.mdpOp & MDPOP_TRANSP) {
+					*pppop_reg_ptr |= PPP_OP_ROT_ON |
+					    PPP_OP_BLEND_ON |
+					    PPP_OP_BLEND_SRCPIXEL_TRANSP;
+					*tpVal = iBuf->mdpImg.tpVal;
+				} else if (iBuf->mdpImg.mdpOp & MDPOP_ALPHAB) {
+					*pppop_reg_ptr |= PPP_OP_ROT_ON |
+					    PPP_OP_BLEND_ON |
+					    PPP_OP_BLEND_ALPHA_BLEND_NORMAL |
+					    PPP_OP_BLEND_CONSTANT_ALPHA;
+					*alpha = iBuf->mdpImg.alpha;
+				}
+			}
+		}
+	}
+}
diff --git a/drivers/video/msm/mdp_ppp_v31.c b/drivers/video/msm/mdp_ppp_v31.c
new file mode 100644
index 0000000..ee6af53
--- /dev/null
+++ b/drivers/video/msm/mdp_ppp_v31.c
@@ -0,0 +1,844 @@
+/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/fb.h>
+#include "linux/proc_fs.h"
+
+#include <mach/hardware.h>
+#include <linux/io.h>
+
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <linux/semaphore.h>
+#include <asm/div64.h>
+
+#include "mdp.h"
+#include "msm_fb.h"
+
+#define MDP_SCALE_COEFF_NUM      32
+#define MDP_SCALE_0P2_TO_0P4_INDEX 0
+#define MDP_SCALE_0P4_TO_0P6_INDEX 32
+#define MDP_SCALE_0P6_TO_0P8_INDEX 64
+#define MDP_SCALE_0P8_TO_8P0_INDEX 96
+#define MDP_SCALE_COEFF_MASK 0x3ff
+
+#define MDP_SCALE_PR  0
+#define MDP_SCALE_FIR 1
+
+static uint32 mdp_scale_0p8_to_8p0_mode;
+static uint32 mdp_scale_0p6_to_0p8_mode;
+static uint32 mdp_scale_0p4_to_0p6_mode;
+static uint32 mdp_scale_0p2_to_0p4_mode;
+
+/* -------- All scaling range, "pixel repeat" -------- */
+static int16 mdp_scale_pixel_repeat_C0[MDP_SCALE_COEFF_NUM] = {
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static int16 mdp_scale_pixel_repeat_C1[MDP_SCALE_COEFF_NUM] = {
+	511, 511, 511, 511, 511, 511, 511, 511,
+	511, 511, 511, 511, 511, 511, 511, 511,
+	511, 511, 511, 511, 511, 511, 511, 511,
+	511, 511, 511, 511, 511, 511, 511, 511
+};
+
+static int16 mdp_scale_pixel_repeat_C2[MDP_SCALE_COEFF_NUM] = {
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static int16 mdp_scale_pixel_repeat_C3[MDP_SCALE_COEFF_NUM] = {
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* --------------------------- FIR ------------------------------------- */
+/* -------- Downscale, ranging from 0.8x to 8.0x of original size -------- */
+
+static int16 mdp_scale_0p8_to_8p0_C0[MDP_SCALE_COEFF_NUM] = {
+	0, -7, -13, -19, -24, -28, -32, -34, -37, -39,
+	-40, -41, -41, -41, -40, -40, -38, -37, -35, -33,
+	-31, -29, -26, -24, -21, -18, -15, -13, -10, -7,
+	-5, -2
+};
+
+static int16 mdp_scale_0p8_to_8p0_C1[MDP_SCALE_COEFF_NUM] = {
+	511, 507, 501, 494, 485, 475, 463, 450, 436, 422,
+	405, 388, 370, 352, 333, 314, 293, 274, 253, 233,
+	213, 193, 172, 152, 133, 113, 95, 77, 60, 43,
+	28, 13
+};
+
+static int16 mdp_scale_0p8_to_8p0_C2[MDP_SCALE_COEFF_NUM] = {
+	0, 13, 28, 43, 60, 77, 95, 113, 133, 152,
+	172, 193, 213, 233, 253, 274, 294, 314, 333, 352,
+	370, 388, 405, 422, 436, 450, 463, 475, 485, 494,
+	501, 507,
+};
+
+static int16 mdp_scale_0p8_to_8p0_C3[MDP_SCALE_COEFF_NUM] = {
+	0, -2, -5, -7, -10, -13, -15, -18, -21, -24,
+	-26, -29, -31, -33, -35, -37, -38, -40, -40, -41,
+	-41, -41, -40, -39, -37, -34, -32, -28, -24, -19,
+	-13, -7
+};
+
+/* -------- Downscale, ranging from 0.6x to 0.8x of original size -------- */
+
+static int16 mdp_scale_0p6_to_0p8_C0[MDP_SCALE_COEFF_NUM] = {
+	104, 96, 89, 82, 75, 68, 61, 55, 49, 43,
+	38, 33, 28, 24, 20, 16, 12, 9, 6, 4,
+	2, 0, -2, -4, -5, -6, -7, -7, -8, -8,
+	-8, -8
+};
+
+static int16 mdp_scale_0p6_to_0p8_C1[MDP_SCALE_COEFF_NUM] = {
+	303, 303, 302, 300, 298, 296, 293, 289, 286, 281,
+	276, 270, 265, 258, 252, 245, 238, 230, 223, 214,
+	206, 197, 189, 180, 172, 163, 154, 145, 137, 128,
+	120, 112
+};
+
+static int16 mdp_scale_0p6_to_0p8_C2[MDP_SCALE_COEFF_NUM] = {
+	112, 120, 128, 137, 145, 154, 163, 172, 180, 189,
+	197, 206, 214, 223, 230, 238, 245, 252, 258, 265,
+	270, 276, 281, 286, 289, 293, 296, 298, 300, 302,
+	303, 303
+};
+
+static int16 mdp_scale_0p6_to_0p8_C3[MDP_SCALE_COEFF_NUM] = {
+	-8, -8, -8, -8, -7, -7, -6, -5, -4, -2,
+	0, 2, 4, 6, 9, 12, 16, 20, 24, 28,
+	33, 38, 43, 49, 55, 61, 68, 75, 82, 89,
+	96, 104
+};
+
+/* -------- Downscale, ranging from 0.4x to 0.6x of original size -------- */
+
+static int16 mdp_scale_0p4_to_0p6_C0[MDP_SCALE_COEFF_NUM] = {
+	136, 132, 128, 123, 119, 115, 111, 107, 103, 98,
+	95, 91, 87, 84, 80, 76, 73, 69, 66, 62,
+	59, 57, 54, 50, 47, 44, 41, 39, 36, 33,
+	32, 29
+};
+
+static int16 mdp_scale_0p4_to_0p6_C1[MDP_SCALE_COEFF_NUM] = {
+	206, 205, 204, 204, 201, 200, 199, 197, 196, 194,
+	191, 191, 189, 185, 184, 182, 180, 178, 176, 173,
+	170, 168, 165, 162, 160, 157, 155, 152, 148, 146,
+	142, 140
+};
+
+static int16 mdp_scale_0p4_to_0p6_C2[MDP_SCALE_COEFF_NUM] = {
+	140, 142, 146, 148, 152, 155, 157, 160, 162, 165,
+	168, 170, 173, 176, 178, 180, 182, 184, 185, 189,
+	191, 191, 194, 196, 197, 199, 200, 201, 204, 204,
+	205, 206
+};
+
+static int16 mdp_scale_0p4_to_0p6_C3[MDP_SCALE_COEFF_NUM] = {
+	29, 32, 33, 36, 39, 41, 44, 47, 50, 54,
+	57, 59, 62, 66, 69, 73, 76, 80, 84, 87,
+	91, 95, 98, 103, 107, 111, 115, 119, 123, 128,
+	132, 136
+};
+
+/* -------- Downscale, ranging from 0.2x to 0.4x of original size -------- */
+
+static int16 mdp_scale_0p2_to_0p4_C0[MDP_SCALE_COEFF_NUM] = {
+	131, 131, 130, 129, 128, 127, 127, 126, 125, 125,
+	124, 123, 123, 121, 120, 119, 119, 118, 117, 117,
+	116, 115, 115, 114, 113, 112, 111, 110, 109, 109,
+	108, 107
+};
+
+static int16 mdp_scale_0p2_to_0p4_C1[MDP_SCALE_COEFF_NUM] = {
+	141, 140, 140, 140, 140, 139, 138, 138, 138, 137,
+	137, 137, 136, 137, 137, 137, 136, 136, 136, 135,
+	135, 135, 134, 134, 134, 134, 134, 133, 133, 132,
+	132, 132
+};
+
+static int16 mdp_scale_0p2_to_0p4_C2[MDP_SCALE_COEFF_NUM] = {
+	132, 132, 132, 133, 133, 134, 134, 134, 134, 134,
+	135, 135, 135, 136, 136, 136, 137, 137, 137, 136,
+	137, 137, 137, 138, 138, 138, 139, 140, 140, 140,
+	140, 141
+};
+
+static int16 mdp_scale_0p2_to_0p4_C3[MDP_SCALE_COEFF_NUM] = {
+	107, 108, 109, 109, 110, 111, 112, 113, 114, 115,
+	115, 116, 117, 117, 118, 119, 119, 120, 121, 123,
+	123, 124, 125, 125, 126, 127, 127, 128, 129, 130,
+	131, 131
+};
+
+static void mdp_update_scale_table(int index, int16 *c0, int16 *c1,
+				   int16 *c2, int16 *c3)
+{
+	int i, val;
+
+	for (i = 0; i < MDP_SCALE_COEFF_NUM; i++) {
+		val =
+		    ((MDP_SCALE_COEFF_MASK & c1[i]) << 16) |
+		    (MDP_SCALE_COEFF_MASK & c0[i]);
+		writel(val, MDP_PPP_SCALE_COEFF_LSBn(index));
+		val =
+		    ((MDP_SCALE_COEFF_MASK & c3[i]) << 16) |
+		    (MDP_SCALE_COEFF_MASK & c2[i]);
+		writel(val, MDP_PPP_SCALE_COEFF_MSBn(index));
+		index++;
+	}
+}
+
+void mdp_init_scale_table(void)
+{
+	mdp_scale_0p2_to_0p4_mode = MDP_SCALE_FIR;
+	mdp_update_scale_table(MDP_SCALE_0P2_TO_0P4_INDEX,
+			       mdp_scale_0p2_to_0p4_C0,
+			       mdp_scale_0p2_to_0p4_C1,
+			       mdp_scale_0p2_to_0p4_C2,
+			       mdp_scale_0p2_to_0p4_C3);
+
+	mdp_scale_0p4_to_0p6_mode = MDP_SCALE_FIR;
+	mdp_update_scale_table(MDP_SCALE_0P4_TO_0P6_INDEX,
+			       mdp_scale_0p4_to_0p6_C0,
+			       mdp_scale_0p4_to_0p6_C1,
+			       mdp_scale_0p4_to_0p6_C2,
+			       mdp_scale_0p4_to_0p6_C3);
+
+	mdp_scale_0p6_to_0p8_mode = MDP_SCALE_FIR;
+	mdp_update_scale_table(MDP_SCALE_0P6_TO_0P8_INDEX,
+			       mdp_scale_0p6_to_0p8_C0,
+			       mdp_scale_0p6_to_0p8_C1,
+			       mdp_scale_0p6_to_0p8_C2,
+			       mdp_scale_0p6_to_0p8_C3);
+
+	mdp_scale_0p8_to_8p0_mode = MDP_SCALE_FIR;
+	mdp_update_scale_table(MDP_SCALE_0P8_TO_8P0_INDEX,
+			       mdp_scale_0p8_to_8p0_C0,
+			       mdp_scale_0p8_to_8p0_C1,
+			       mdp_scale_0p8_to_8p0_C2,
+			       mdp_scale_0p8_to_8p0_C3);
+}
+
+static long long mdp_do_div(long long num, long long den)
+{
+	do_div(num, den);
+	return num;
+}
+
+#define SCALER_PHASE_BITS 29
+#define HAL_MDP_PHASE_STEP_2P50    0x50000000
+#define HAL_MDP_PHASE_STEP_1P66    0x35555555
+#define HAL_MDP_PHASE_STEP_1P25    0x28000000
+
+struct phase_val {
+	int phase_init_x;
+	int phase_init_y;
+	int phase_step_x;
+	int phase_step_y;
+};
+
+static void mdp_calc_scaleInitPhase_3p1(uint32 in_w,
+					uint32 in_h,
+					uint32 out_w,
+					uint32 out_h,
+					boolean is_rotate,
+					boolean is_pp_x,
+					boolean is_pp_y, struct phase_val *pval)
+{
+	uint64 dst_ROI_width;
+	uint64 dst_ROI_height;
+	uint64 src_ROI_width;
+	uint64 src_ROI_height;
+
+	/*
+	 * phase_step_x, phase_step_y, phase_init_x and phase_init_y
+	 * are represented in fixed-point, unsigned 3.29 format
+	 */
+	uint32 phase_step_x = 0;
+	uint32 phase_step_y = 0;
+	uint32 phase_init_x = 0;
+	uint32 phase_init_y = 0;
+	uint32 yscale_filter_sel, xscale_filter_sel;
+	uint32 scale_unit_sel_x, scale_unit_sel_y;
+
+	uint64 numerator, denominator;
+	uint64 temp_dim;
+
+	src_ROI_width = in_w;
+	src_ROI_height = in_h;
+	dst_ROI_width = out_w;
+	dst_ROI_height = out_h;
+
+	/* if there is a 90 degree rotation */
+	if (is_rotate) {
+		/* decide whether to use FIR or M/N for scaling */
+
+		/* if down-scaling by a factor smaller than 1/4 */
+		if ((dst_ROI_height == 1 && src_ROI_width < 4) ||
+			(src_ROI_width < 4 * dst_ROI_height - 3))
+			scale_unit_sel_x = 0;/* use FIR scalar */
+		else
+			scale_unit_sel_x = 1;/* use M/N scalar */
+
+		/* if down-scaling by a factor smaller than 1/4 */
+		if ((dst_ROI_width == 1 && src_ROI_height < 4) ||
+			(src_ROI_height < 4 * dst_ROI_width - 3))
+			scale_unit_sel_y = 0;/* use FIR scalar */
+		else
+			scale_unit_sel_y = 1;/* use M/N scalar */
+	} else {
+		/* decide whether to use FIR or M/N for scaling */
+		if ((dst_ROI_width == 1 && src_ROI_width < 4) ||
+			(src_ROI_width < 4 * dst_ROI_width - 3))
+			scale_unit_sel_x = 0;/* use FIR scalar */
+		else
+			scale_unit_sel_x = 1;/* use M/N scalar */
+
+		if ((dst_ROI_height == 1 && src_ROI_height < 4) ||
+			(src_ROI_height < 4 * dst_ROI_height - 3))
+			scale_unit_sel_y = 0;/* use FIR scalar */
+		else
+			scale_unit_sel_y = 1;/* use M/N scalar */
+	}
+
+	/* if there is a 90 degree rotation */
+	if (is_rotate) {
+		/* swap the width and height of dst ROI */
+		temp_dim = dst_ROI_width;
+		dst_ROI_width = dst_ROI_height;
+		dst_ROI_height = temp_dim;
+	}
+
+	/* calculate phase step for the x direction */
+
+	/* if destination is only 1 pixel wide, the value of phase_step_x
+	   is unimportant. Assigning phase_step_x to src ROI width
+	   as an arbitrary value. */
+	if (dst_ROI_width == 1)
+		phase_step_x = (uint32) ((src_ROI_width) << SCALER_PHASE_BITS);
+
+	/* if using FIR scalar */
+	else if (scale_unit_sel_x == 0) {
+
+		/* Calculate the quotient ( src_ROI_width - 1 ) / ( dst_ROI_width - 1)
+		   with u3.29 precision. Quotient is rounded up to the larger
+		   29th decimal point. */
+		numerator = (src_ROI_width - 1) << SCALER_PHASE_BITS;
+		denominator = (dst_ROI_width - 1);	/* never equals to 0 because of the "( dst_ROI_width == 1 ) case" */
+		phase_step_x = (uint32) mdp_do_div((numerator + denominator - 1), denominator);	/* divide and round up to the larger 29th decimal point. */
+
+	}
+
+	/* if M/N scalar */
+	else if (scale_unit_sel_x == 1) {
+		/* Calculate the quotient ( src_ROI_width ) / ( dst_ROI_width)
+		   with u3.29 precision. Quotient is rounded down to the
+		   smaller 29th decimal point. */
+		numerator = (src_ROI_width) << SCALER_PHASE_BITS;
+		denominator = (dst_ROI_width);
+		phase_step_x = (uint32) mdp_do_div(numerator, denominator);
+	}
+	/* calculate phase step for the y direction */
+
+	/* if destination is only 1 pixel wide, the value of
+	   phase_step_x is unimportant. Assigning phase_step_x
+	   to src ROI width as an arbitrary value. */
+	if (dst_ROI_height == 1)
+		phase_step_y = (uint32) ((src_ROI_height) << SCALER_PHASE_BITS);
+
+	/* if FIR scalar */
+	else if (scale_unit_sel_y == 0) {
+		/* Calculate the quotient ( src_ROI_height - 1 ) / ( dst_ROI_height - 1)
+		   with u3.29 precision. Quotient is rounded up to the larger
+		   29th decimal point. */
+		numerator = (src_ROI_height - 1) << SCALER_PHASE_BITS;
+		denominator = (dst_ROI_height - 1);	/* never equals to 0 because of the "( dst_ROI_height == 1 )" case */
+		phase_step_y = (uint32) mdp_do_div((numerator + denominator - 1), denominator);	/* Quotient is rounded up to the larger 29th decimal point. */
+
+	}
+
+	/* if M/N scalar */
+	else if (scale_unit_sel_y == 1) {
+		/* Calculate the quotient ( src_ROI_height ) / ( dst_ROI_height)
+		   with u3.29 precision. Quotient is rounded down to the smaller
+		   29th decimal point. */
+		numerator = (src_ROI_height) << SCALER_PHASE_BITS;
+		denominator = (dst_ROI_height);
+		phase_step_y = (uint32) mdp_do_div(numerator, denominator);
+	}
+
+	/* decide which set of FIR coefficients to use */
+	if (phase_step_x > HAL_MDP_PHASE_STEP_2P50)
+		xscale_filter_sel = 0;
+	else if (phase_step_x > HAL_MDP_PHASE_STEP_1P66)
+		xscale_filter_sel = 1;
+	else if (phase_step_x > HAL_MDP_PHASE_STEP_1P25)
+		xscale_filter_sel = 2;
+	else
+		xscale_filter_sel = 3;
+
+	if (phase_step_y > HAL_MDP_PHASE_STEP_2P50)
+		yscale_filter_sel = 0;
+	else if (phase_step_y > HAL_MDP_PHASE_STEP_1P66)
+		yscale_filter_sel = 1;
+	else if (phase_step_y > HAL_MDP_PHASE_STEP_1P25)
+		yscale_filter_sel = 2;
+	else
+		yscale_filter_sel = 3;
+
+	/* calculate phase init for the x direction */
+
+	/* if using FIR scalar */
+	if (scale_unit_sel_x == 0) {
+		if (dst_ROI_width == 1)
+			phase_init_x =
+			    (uint32) ((src_ROI_width - 1) << SCALER_PHASE_BITS);
+		else
+			phase_init_x = 0;
+
+	}
+	/* M over N scalar  */
+	else if (scale_unit_sel_x == 1)
+		phase_init_x = 0;
+
+	/* calculate phase init for the y direction
+	   if using FIR scalar */
+	if (scale_unit_sel_y == 0) {
+		if (dst_ROI_height == 1)
+			phase_init_y =
+			    (uint32) ((src_ROI_height -
+				       1) << SCALER_PHASE_BITS);
+		else
+			phase_init_y = 0;
+
+	}
+	/* M over N scalar   */
+	else if (scale_unit_sel_y == 1)
+		phase_init_y = 0;
+
+	/* write registers */
+	pval->phase_step_x = (uint32) phase_step_x;
+	pval->phase_step_y = (uint32) phase_step_y;
+	pval->phase_init_x = (uint32) phase_init_x;
+	pval->phase_init_y = (uint32) phase_init_y;
+
+	return;
+}
+
+void mdp_set_scale(MDPIBUF *iBuf,
+		   uint32 dst_roi_width,
+		   uint32 dst_roi_height,
+		   boolean inputRGB, boolean outputRGB, uint32 *pppop_reg_ptr)
+{
+	uint32 dst_roi_width_scale;
+	uint32 dst_roi_height_scale;
+	struct phase_val pval;
+	boolean use_pr;
+	uint32 ppp_scale_config = 0;
+
+	if (!inputRGB)
+		ppp_scale_config |= BIT(6);
+
+	if (iBuf->mdpImg.mdpOp & MDPOP_ASCALE) {
+		if (iBuf->mdpImg.mdpOp & MDPOP_ROT90) {
+			dst_roi_width_scale = dst_roi_height;
+			dst_roi_height_scale = dst_roi_width;
+		} else {
+			dst_roi_width_scale = dst_roi_width;
+			dst_roi_height_scale = dst_roi_height;
+		}
+
+		if ((dst_roi_width_scale != iBuf->roi.width) ||
+		    (dst_roi_height_scale != iBuf->roi.height) ||
+			(iBuf->mdpImg.mdpOp & MDPOP_SHARPENING)) {
+			*pppop_reg_ptr |=
+			    (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON);
+
+			mdp_calc_scaleInitPhase_3p1(iBuf->roi.width,
+						    iBuf->roi.height,
+						    dst_roi_width,
+						    dst_roi_height,
+						    iBuf->mdpImg.
+						    mdpOp & MDPOP_ROT90, 1, 1,
+						    &pval);
+
+			MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x013c,
+				 pval.phase_init_x);
+			MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0140,
+				 pval.phase_init_y);
+			MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0144,
+				 pval.phase_step_x);
+			MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0148,
+				 pval.phase_step_y);
+
+			/* disable the pixel repeat option for scaling */
+			use_pr = false;
+
+			/* x-direction */
+			if ((dst_roi_width_scale == iBuf->roi.width) &&
+				!(iBuf->mdpImg.mdpOp & MDPOP_SHARPENING)) {
+				*pppop_reg_ptr &= ~PPP_OP_SCALE_X_ON;
+			} else
+			    if (((dst_roi_width_scale * 10) / iBuf->roi.width) >
+				8) {
+				if ((use_pr)
+				    && (mdp_scale_0p8_to_8p0_mode !=
+					MDP_SCALE_PR)) {
+					mdp_scale_0p8_to_8p0_mode =
+					    MDP_SCALE_PR;
+					mdp_update_scale_table
+					    (MDP_SCALE_0P8_TO_8P0_INDEX,
+					     mdp_scale_pixel_repeat_C0,
+					     mdp_scale_pixel_repeat_C1,
+					     mdp_scale_pixel_repeat_C2,
+					     mdp_scale_pixel_repeat_C3);
+				} else if ((!use_pr)
+					   && (mdp_scale_0p8_to_8p0_mode !=
+					       MDP_SCALE_FIR)) {
+					mdp_scale_0p8_to_8p0_mode =
+					    MDP_SCALE_FIR;
+					mdp_update_scale_table
+					    (MDP_SCALE_0P8_TO_8P0_INDEX,
+					     mdp_scale_0p8_to_8p0_C0,
+					     mdp_scale_0p8_to_8p0_C1,
+					     mdp_scale_0p8_to_8p0_C2,
+					     mdp_scale_0p8_to_8p0_C3);
+				}
+				ppp_scale_config |= (SCALE_U1_SET << 2);
+			} else
+			    if (((dst_roi_width_scale * 10) / iBuf->roi.width) >
+				6) {
+				if ((use_pr)
+				    && (mdp_scale_0p6_to_0p8_mode !=
+					MDP_SCALE_PR)) {
+					mdp_scale_0p6_to_0p8_mode =
+					    MDP_SCALE_PR;
+					mdp_update_scale_table
+					    (MDP_SCALE_0P6_TO_0P8_INDEX,
+					     mdp_scale_pixel_repeat_C0,
+					     mdp_scale_pixel_repeat_C1,
+					     mdp_scale_pixel_repeat_C2,
+					     mdp_scale_pixel_repeat_C3);
+				} else if ((!use_pr)
+					   && (mdp_scale_0p6_to_0p8_mode !=
+					       MDP_SCALE_FIR)) {
+					mdp_scale_0p6_to_0p8_mode =
+					    MDP_SCALE_FIR;
+					mdp_update_scale_table
+					    (MDP_SCALE_0P6_TO_0P8_INDEX,
+					     mdp_scale_0p6_to_0p8_C0,
+					     mdp_scale_0p6_to_0p8_C1,
+					     mdp_scale_0p6_to_0p8_C2,
+					     mdp_scale_0p6_to_0p8_C3);
+				}
+				ppp_scale_config |= (SCALE_D2_SET << 2);
+			} else
+			    if (((dst_roi_width_scale * 10) / iBuf->roi.width) >
+				4) {
+				if ((use_pr)
+				    && (mdp_scale_0p4_to_0p6_mode !=
+					MDP_SCALE_PR)) {
+					mdp_scale_0p4_to_0p6_mode =
+					    MDP_SCALE_PR;
+					mdp_update_scale_table
+					    (MDP_SCALE_0P4_TO_0P6_INDEX,
+					     mdp_scale_pixel_repeat_C0,
+					     mdp_scale_pixel_repeat_C1,
+					     mdp_scale_pixel_repeat_C2,
+					     mdp_scale_pixel_repeat_C3);
+				} else if ((!use_pr)
+					   && (mdp_scale_0p4_to_0p6_mode !=
+					       MDP_SCALE_FIR)) {
+					mdp_scale_0p4_to_0p6_mode =
+					    MDP_SCALE_FIR;
+					mdp_update_scale_table
+					    (MDP_SCALE_0P4_TO_0P6_INDEX,
+					     mdp_scale_0p4_to_0p6_C0,
+					     mdp_scale_0p4_to_0p6_C1,
+					     mdp_scale_0p4_to_0p6_C2,
+					     mdp_scale_0p4_to_0p6_C3);
+				}
+				ppp_scale_config |= (SCALE_D1_SET << 2);
+			} else
+			if ((dst_roi_width_scale == 1 && iBuf->roi.width < 4) ||
+			(iBuf->roi.width < 4 * dst_roi_width_scale - 3)) {
+				if ((use_pr)
+				    && (mdp_scale_0p2_to_0p4_mode !=
+					MDP_SCALE_PR)) {
+					mdp_scale_0p2_to_0p4_mode =
+					    MDP_SCALE_PR;
+					mdp_update_scale_table
+					    (MDP_SCALE_0P2_TO_0P4_INDEX,
+					     mdp_scale_pixel_repeat_C0,
+					     mdp_scale_pixel_repeat_C1,
+					     mdp_scale_pixel_repeat_C2,
+					     mdp_scale_pixel_repeat_C3);
+				} else if ((!use_pr)
+					   && (mdp_scale_0p2_to_0p4_mode !=
+					       MDP_SCALE_FIR)) {
+					mdp_scale_0p2_to_0p4_mode =
+					    MDP_SCALE_FIR;
+					mdp_update_scale_table
+					    (MDP_SCALE_0P2_TO_0P4_INDEX,
+					     mdp_scale_0p2_to_0p4_C0,
+					     mdp_scale_0p2_to_0p4_C1,
+					     mdp_scale_0p2_to_0p4_C2,
+					     mdp_scale_0p2_to_0p4_C3);
+				}
+				ppp_scale_config |= (SCALE_D0_SET << 2);
+			} else
+				ppp_scale_config |= BIT(0);
+
+			/* y-direction */
+			if ((dst_roi_height_scale == iBuf->roi.height) &&
+				!(iBuf->mdpImg.mdpOp & MDPOP_SHARPENING)) {
+				*pppop_reg_ptr &= ~PPP_OP_SCALE_Y_ON;
+			} else if (((dst_roi_height_scale * 10) /
+					iBuf->roi.height) > 8) {
+				if ((use_pr)
+				    && (mdp_scale_0p8_to_8p0_mode !=
+					MDP_SCALE_PR)) {
+					mdp_scale_0p8_to_8p0_mode =
+					    MDP_SCALE_PR;
+					mdp_update_scale_table
+					    (MDP_SCALE_0P8_TO_8P0_INDEX,
+					     mdp_scale_pixel_repeat_C0,
+					     mdp_scale_pixel_repeat_C1,
+					     mdp_scale_pixel_repeat_C2,
+					     mdp_scale_pixel_repeat_C3);
+				} else if ((!use_pr)
+					   && (mdp_scale_0p8_to_8p0_mode !=
+					       MDP_SCALE_FIR)) {
+					mdp_scale_0p8_to_8p0_mode =
+					    MDP_SCALE_FIR;
+					mdp_update_scale_table
+					    (MDP_SCALE_0P8_TO_8P0_INDEX,
+					     mdp_scale_0p8_to_8p0_C0,
+					     mdp_scale_0p8_to_8p0_C1,
+					     mdp_scale_0p8_to_8p0_C2,
+					     mdp_scale_0p8_to_8p0_C3);
+				}
+				ppp_scale_config |= (SCALE_U1_SET << 4);
+			} else
+			    if (((dst_roi_height_scale * 10) /
+				 iBuf->roi.height) > 6) {
+				if ((use_pr)
+				    && (mdp_scale_0p6_to_0p8_mode !=
+					MDP_SCALE_PR)) {
+					mdp_scale_0p6_to_0p8_mode =
+					    MDP_SCALE_PR;
+					mdp_update_scale_table
+					    (MDP_SCALE_0P6_TO_0P8_INDEX,
+					     mdp_scale_pixel_repeat_C0,
+					     mdp_scale_pixel_repeat_C1,
+					     mdp_scale_pixel_repeat_C2,
+					     mdp_scale_pixel_repeat_C3);
+				} else if ((!use_pr)
+					   && (mdp_scale_0p6_to_0p8_mode !=
+					       MDP_SCALE_FIR)) {
+					mdp_scale_0p6_to_0p8_mode =
+					    MDP_SCALE_FIR;
+					mdp_update_scale_table
+					    (MDP_SCALE_0P6_TO_0P8_INDEX,
+					     mdp_scale_0p6_to_0p8_C0,
+					     mdp_scale_0p6_to_0p8_C1,
+					     mdp_scale_0p6_to_0p8_C2,
+					     mdp_scale_0p6_to_0p8_C3);
+				}
+				ppp_scale_config |= (SCALE_D2_SET << 4);
+			} else
+			    if (((dst_roi_height_scale * 10) /
+				 iBuf->roi.height) > 4) {
+				if ((use_pr)
+				    && (mdp_scale_0p4_to_0p6_mode !=
+					MDP_SCALE_PR)) {
+					mdp_scale_0p4_to_0p6_mode =
+					    MDP_SCALE_PR;
+					mdp_update_scale_table
+					    (MDP_SCALE_0P4_TO_0P6_INDEX,
+					     mdp_scale_pixel_repeat_C0,
+					     mdp_scale_pixel_repeat_C1,
+					     mdp_scale_pixel_repeat_C2,
+					     mdp_scale_pixel_repeat_C3);
+				} else if ((!use_pr)
+					   && (mdp_scale_0p4_to_0p6_mode !=
+					       MDP_SCALE_FIR)) {
+					mdp_scale_0p4_to_0p6_mode =
+					    MDP_SCALE_FIR;
+					mdp_update_scale_table
+					    (MDP_SCALE_0P4_TO_0P6_INDEX,
+					     mdp_scale_0p4_to_0p6_C0,
+					     mdp_scale_0p4_to_0p6_C1,
+					     mdp_scale_0p4_to_0p6_C2,
+					     mdp_scale_0p4_to_0p6_C3);
+				}
+				ppp_scale_config |= (SCALE_D1_SET << 4);
+			} else if ((dst_roi_height_scale == 1 &&
+			iBuf->roi.height < 4) ||
+			(iBuf->roi.height < 4 * dst_roi_height_scale - 3)) {
+				if ((use_pr)
+				    && (mdp_scale_0p2_to_0p4_mode !=
+					MDP_SCALE_PR)) {
+					mdp_scale_0p2_to_0p4_mode =
+					    MDP_SCALE_PR;
+					mdp_update_scale_table
+					    (MDP_SCALE_0P2_TO_0P4_INDEX,
+					     mdp_scale_pixel_repeat_C0,
+					     mdp_scale_pixel_repeat_C1,
+					     mdp_scale_pixel_repeat_C2,
+					     mdp_scale_pixel_repeat_C3);
+				} else if ((!use_pr)
+					   && (mdp_scale_0p2_to_0p4_mode !=
+					       MDP_SCALE_FIR)) {
+					mdp_scale_0p2_to_0p4_mode =
+					    MDP_SCALE_FIR;
+					mdp_update_scale_table
+					    (MDP_SCALE_0P2_TO_0P4_INDEX,
+					     mdp_scale_0p2_to_0p4_C0,
+					     mdp_scale_0p2_to_0p4_C1,
+					     mdp_scale_0p2_to_0p4_C2,
+					     mdp_scale_0p2_to_0p4_C3);
+				}
+				ppp_scale_config |= (SCALE_D0_SET << 4);
+			} else
+				ppp_scale_config |= BIT(1);
+
+			if (iBuf->mdpImg.mdpOp & MDPOP_SHARPENING) {
+				ppp_scale_config |= BIT(7);
+				MDP_OUTP(MDP_BASE + 0x50020,
+						iBuf->mdpImg.sp_value);
+			}
+
+			MDP_OUTP(MDP_BASE + 0x10230, ppp_scale_config);
+		} else {
+			iBuf->mdpImg.mdpOp &= ~(MDPOP_ASCALE);
+		}
+	}
+}
+
+void mdp_adjust_start_addr(uint8 **src0,
+			   uint8 **src1,
+			   int v_slice,
+			   int h_slice,
+			   int x,
+			   int y,
+			   uint32 width,
+			   uint32 height, int bpp, MDPIBUF *iBuf, int layer)
+{
+	switch (layer) {
+	case 0:
+		MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0200, (y << 16) | (x));
+		MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0208,
+			 (height << 16) | (width));
+		break;
+
+	case 1:
+		/* MDP 3.1 HW bug workaround */
+		if (iBuf->ibuf_type == MDP_YCRYCB_H2V1) {
+			*src0 += (x + y * width) * bpp;
+			x = y = 0;
+			width = iBuf->roi.dst_width;
+			height = iBuf->roi.dst_height;
+		}
+
+		MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0204, (y << 16) | (x));
+		MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x020c,
+			 (height << 16) | (width));
+		break;
+
+	case 2:
+		MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x019c, (y << 16) | (x));
+		break;
+	}
+}
+
+void mdp_set_blend_attr(MDPIBUF *iBuf,
+			uint32 *alpha,
+			uint32 *tpVal,
+			uint32 perPixelAlpha, uint32 *pppop_reg_ptr)
+{
+	int bg_alpha;
+
+	*alpha = iBuf->mdpImg.alpha;
+	*tpVal = iBuf->mdpImg.tpVal;
+
+	if (iBuf->mdpImg.mdpOp & MDPOP_FG_PM_ALPHA) {
+		if (perPixelAlpha) {
+			*pppop_reg_ptr |= PPP_OP_ROT_ON |
+			PPP_OP_BLEND_ON | PPP_OP_BLEND_CONSTANT_ALPHA;
+			}
+		else {
+			if ((iBuf->mdpImg.mdpOp & MDPOP_ALPHAB)
+				&& (iBuf->mdpImg.alpha == 0xff)) {
+					iBuf->mdpImg.mdpOp &= ~(MDPOP_ALPHAB);
+				}
+
+			if ((iBuf->mdpImg.mdpOp & MDPOP_ALPHAB)
+				|| (iBuf->mdpImg.mdpOp & MDPOP_TRANSP)) {
+				*pppop_reg_ptr |=
+				PPP_OP_ROT_ON | PPP_OP_BLEND_ON |
+				PPP_OP_BLEND_CONSTANT_ALPHA |
+				PPP_OP_BLEND_ALPHA_BLEND_NORMAL;
+			}
+		}
+
+		bg_alpha = PPP_BLEND_BG_USE_ALPHA_SEL |
+				PPP_BLEND_BG_ALPHA_REVERSE;
+
+		if (perPixelAlpha)
+			bg_alpha |= PPP_BLEND_BG_SRCPIXEL_ALPHA;
+		else {
+			bg_alpha |= PPP_BLEND_BG_CONSTANT_ALPHA;
+			bg_alpha |= iBuf->mdpImg.alpha << 24;
+			}
+		outpdw(MDP_BASE + 0x70010, bg_alpha);
+
+		if (iBuf->mdpImg.mdpOp & MDPOP_TRANSP)
+			*pppop_reg_ptr |= PPP_BLEND_CALPHA_TRNASP;
+	} else if (perPixelAlpha) {
+		*pppop_reg_ptr |= PPP_OP_ROT_ON |
+		    PPP_OP_BLEND_ON | PPP_OP_BLEND_SRCPIXEL_ALPHA;
+	} else {
+		if ((iBuf->mdpImg.mdpOp & MDPOP_ALPHAB)
+		    && (iBuf->mdpImg.alpha == 0xff)) {
+			iBuf->mdpImg.mdpOp &= ~(MDPOP_ALPHAB);
+		}
+
+		if ((iBuf->mdpImg.mdpOp & MDPOP_ALPHAB)
+		    || (iBuf->mdpImg.mdpOp & MDPOP_TRANSP)) {
+			*pppop_reg_ptr |=
+			    PPP_OP_ROT_ON | PPP_OP_BLEND_ON |
+			    PPP_OP_BLEND_CONSTANT_ALPHA |
+			    PPP_OP_BLEND_ALPHA_BLEND_NORMAL;
+		}
+
+		if (iBuf->mdpImg.mdpOp & MDPOP_TRANSP)
+			*pppop_reg_ptr |= PPP_BLEND_CALPHA_TRNASP;
+	}
+}
diff --git a/drivers/video/msm/mdp_vsync.c b/drivers/video/msm/mdp_vsync.c
new file mode 100644
index 0000000..87e74d9
--- /dev/null
+++ b/drivers/video/msm/mdp_vsync.c
@@ -0,0 +1,491 @@
+/* Copyright (c) 2008-2009, 2012 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/hrtimer.h>
+#include <linux/vmalloc.h>
+#include <linux/clk.h>
+
+#include <mach/hardware.h>
+#include <linux/io.h>
+
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <linux/semaphore.h>
+#include <linux/uaccess.h>
+#include <mach/gpio.h>
+
+#include "mdp.h"
+#include "msm_fb.h"
+#include "mddihost.h"
+
+#ifdef CONFIG_FB_MSM_MDP40
+#include "mdp4.h"
+
+#define MDP_SYNC_CFG_0		0x100
+#define MDP_SYNC_STATUS_0	0x10c
+#define MDP_SYNC_CFG_1		0x104
+#define MDP_SYNC_STATUS_1	0x110
+#define MDP_PRIM_VSYNC_OUT_CTRL	0x118
+#define MDP_SEC_VSYNC_OUT_CTRL	0x11C
+#define MDP_VSYNC_SEL		0x124
+#define MDP_PRIM_VSYNC_INIT_VAL	0x128
+#define MDP_SEC_VSYNC_INIT_VAL	0x12C
+#else
+#define MDP_SYNC_CFG_0		0x300
+#define MDP_SYNC_STATUS_0	0x30c
+#define MDP_PRIM_VSYNC_OUT_CTRL	0x318
+#define MDP_PRIM_VSYNC_INIT_VAL	0x328
+#endif
+
+extern mddi_lcd_type mddi_lcd_idx;
+extern spinlock_t mdp_spin_lock;
+extern struct workqueue_struct *mdp_vsync_wq;
+extern int lcdc_mode;
+extern int vsync_mode;
+
+#ifdef MDP_HW_VSYNC
+int vsync_above_th = 4;
+int vsync_start_th = 1;
+int vsync_load_cnt;
+int vsync_clk_status;
+DEFINE_MUTEX(vsync_clk_lock);
+static DEFINE_SPINLOCK(vsync_timer_lock);
+
+static struct clk *mdp_vsync_clk;
+static struct msm_fb_data_type *vsync_mfd;
+static unsigned char timer_shutdown_flag;
+static uint32 vsync_cnt_cfg;
+
+void mdp_hw_vsync_clk_enable(struct msm_fb_data_type *mfd)
+{
+	if (vsync_clk_status == 1)
+		return;
+	mutex_lock(&vsync_clk_lock);
+	if (mfd->use_mdp_vsync) {
+		clk_prepare_enable(mdp_vsync_clk);
+		vsync_clk_status = 1;
+	}
+	mutex_unlock(&vsync_clk_lock);
+}
+
+void mdp_hw_vsync_clk_disable(struct msm_fb_data_type *mfd)
+{
+	if (vsync_clk_status == 0)
+		return;
+	mutex_lock(&vsync_clk_lock);
+	if (mfd->use_mdp_vsync) {
+		clk_disable_unprepare(mdp_vsync_clk);
+		vsync_clk_status = 0;
+	}
+	mutex_unlock(&vsync_clk_lock);
+}
+
+static void mdp_set_vsync(unsigned long data);
+void mdp_vsync_clk_enable(void)
+{
+	if (vsync_mfd) {
+		mdp_hw_vsync_clk_enable(vsync_mfd);
+		if (!vsync_mfd->vsync_resync_timer.function)
+			mdp_set_vsync((unsigned long) vsync_mfd);
+	}
+}
+
+void mdp_vsync_clk_disable(void)
+{
+	if (vsync_mfd) {
+		if (vsync_mfd->vsync_resync_timer.function) {
+			spin_lock(&vsync_timer_lock);
+			timer_shutdown_flag = 1;
+			spin_unlock(&vsync_timer_lock);
+			del_timer_sync(&vsync_mfd->vsync_resync_timer);
+			spin_lock(&vsync_timer_lock);
+			timer_shutdown_flag = 0;
+			spin_unlock(&vsync_timer_lock);
+			vsync_mfd->vsync_resync_timer.function = NULL;
+		}
+
+		mdp_hw_vsync_clk_disable(vsync_mfd);
+	}
+}
+#endif
+
+static void mdp_set_vsync(unsigned long data)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)data;
+	struct msm_fb_panel_data *pdata = NULL;
+
+	pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data;
+
+	vsync_mfd = mfd;
+	init_timer(&mfd->vsync_resync_timer);
+
+	if ((pdata) && (pdata->set_vsync_notifier == NULL))
+		return;
+
+	if ((mfd->panel_info.lcd.vsync_enable) && (mfd->panel_power_on)
+	    && (!mfd->vsync_handler_pending)) {
+		mfd->vsync_handler_pending = TRUE;
+		if (!queue_work(mdp_vsync_wq, &mfd->vsync_resync_worker)) {
+			MSM_FB_INFO
+			    ("mdp_set_vsync: can't queue_work! -> needs to increase vsync_resync_timer_duration\n");
+		}
+	} else {
+		MSM_FB_DEBUG
+		    ("mdp_set_vsync failed!  EN:%d  PWR:%d  PENDING:%d\n",
+		     mfd->panel_info.lcd.vsync_enable, mfd->panel_power_on,
+		     mfd->vsync_handler_pending);
+	}
+
+	spin_lock(&vsync_timer_lock);
+	if (!timer_shutdown_flag) {
+		mfd->vsync_resync_timer.function = mdp_set_vsync;
+		mfd->vsync_resync_timer.data = data;
+		mfd->vsync_resync_timer.expires =
+			jiffies + mfd->panel_info.lcd.vsync_notifier_period;
+		add_timer(&mfd->vsync_resync_timer);
+	}
+	spin_unlock(&vsync_timer_lock);
+}
+
+static void mdp_vsync_handler(void *data)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)data;
+
+	if (vsync_clk_status == 0) {
+		pr_debug("Warning: vsync clk is disabled\n");
+		mfd->vsync_handler_pending = FALSE;
+		return;
+	}
+
+	if (mfd->use_mdp_vsync) {
+#ifdef MDP_HW_VSYNC
+		if (mfd->panel_power_on) {
+			MDP_OUTP(MDP_BASE + MDP_SYNC_STATUS_0, vsync_load_cnt);
+
+#ifdef CONFIG_FB_MSM_MDP40
+			if (mdp_hw_revision < MDP4_REVISION_V2_1)
+				MDP_OUTP(MDP_BASE + MDP_SYNC_STATUS_1,
+						vsync_load_cnt);
+#endif
+		}
+
+#endif
+	} else {
+		mfd->last_vsync_timetick = ktime_get_real();
+	}
+
+	mfd->vsync_handler_pending = FALSE;
+}
+
+irqreturn_t mdp_hw_vsync_handler_proxy(int irq, void *data)
+{
+	/*
+	 * ToDo: tried enabling/disabling GPIO MDP HW VSYNC interrupt
+	 * but getting inaccurate timing in mdp_vsync_handler()
+	 * disable_irq(MDP_HW_VSYNC_IRQ);
+	 */
+	mdp_vsync_handler(data);
+
+	return IRQ_HANDLED;
+}
+
+#ifdef MDP_HW_VSYNC
+static void mdp_set_sync_cfg_0(struct msm_fb_data_type *mfd, int vsync_cnt)
+{
+	unsigned long cfg;
+
+	cfg = mfd->total_lcd_lines - 1;
+	cfg <<= MDP_SYNCFG_HGT_LOC;
+	if (mfd->panel_info.lcd.hw_vsync_mode)
+		cfg |= MDP_SYNCFG_VSYNC_EXT_EN;
+	cfg |= (MDP_SYNCFG_VSYNC_INT_EN | vsync_cnt);
+
+	MDP_OUTP(MDP_BASE + MDP_SYNC_CFG_0, cfg);
+}
+
+#ifdef CONFIG_FB_MSM_MDP40
+static void mdp_set_sync_cfg_1(struct msm_fb_data_type *mfd, int vsync_cnt)
+{
+	unsigned long cfg;
+
+	cfg = mfd->total_lcd_lines - 1;
+	cfg <<= MDP_SYNCFG_HGT_LOC;
+	if (mfd->panel_info.lcd.hw_vsync_mode)
+		cfg |= MDP_SYNCFG_VSYNC_EXT_EN;
+	cfg |= (MDP_SYNCFG_VSYNC_INT_EN | vsync_cnt);
+
+	MDP_OUTP(MDP_BASE + MDP_SYNC_CFG_1, cfg);
+}
+#endif
+
+void mdp_vsync_cfg_regs(struct msm_fb_data_type *mfd,
+	boolean first_time)
+{
+	/* MDP cmd block enable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON,
+			  FALSE);
+	if (first_time)
+		mdp_hw_vsync_clk_enable(mfd);
+
+	mdp_set_sync_cfg_0(mfd, vsync_cnt_cfg);
+
+#ifdef CONFIG_FB_MSM_MDP40
+	if (mdp_hw_revision < MDP4_REVISION_V2_1)
+		mdp_set_sync_cfg_1(mfd, vsync_cnt_cfg);
+#endif
+
+	/*
+	 * load the last line + 1 to be in the
+	 * safety zone
+	 */
+	vsync_load_cnt = mfd->panel_info.yres;
+
+	/* line counter init value at the next pulse */
+	MDP_OUTP(MDP_BASE + MDP_PRIM_VSYNC_INIT_VAL,
+		vsync_load_cnt);
+#ifdef CONFIG_FB_MSM_MDP40
+	if (mdp_hw_revision < MDP4_REVISION_V2_1) {
+		MDP_OUTP(MDP_BASE +	MDP_SEC_VSYNC_INIT_VAL,
+			vsync_load_cnt);
+	}
+#endif
+
+	/*
+	 * external vsync source pulse width and
+	 * polarity flip
+	 */
+	MDP_OUTP(MDP_BASE + MDP_PRIM_VSYNC_OUT_CTRL, BIT(0));
+#ifdef CONFIG_FB_MSM_MDP40
+	if (mdp_hw_revision < MDP4_REVISION_V2_1) {
+		MDP_OUTP(MDP_BASE +	MDP_SEC_VSYNC_OUT_CTRL, BIT(0));
+		MDP_OUTP(MDP_BASE +	MDP_VSYNC_SEL, 0x20);
+	}
+#endif
+
+	/* threshold */
+	MDP_OUTP(MDP_BASE + 0x200, (vsync_above_th << 16) |
+		 (vsync_start_th));
+
+	if (first_time)
+		mdp_hw_vsync_clk_disable(mfd);
+
+	/* MDP cmd block disable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+#endif
+
+void mdp_config_vsync(struct platform_device *pdev,
+	struct msm_fb_data_type *mfd)
+{
+	/* vsync on primary lcd only for now */
+	if ((mfd->dest != DISPLAY_LCD) || (mfd->panel_info.pdest != DISPLAY_1)
+	    || (!vsync_mode)) {
+		goto err_handle;
+	}
+
+	vsync_clk_status = 0;
+	if (mfd->panel_info.lcd.vsync_enable) {
+		mfd->total_porch_lines = mfd->panel_info.lcd.v_back_porch +
+		    mfd->panel_info.lcd.v_front_porch +
+		    mfd->panel_info.lcd.v_pulse_width;
+		mfd->total_lcd_lines =
+		    mfd->panel_info.yres + mfd->total_porch_lines;
+		mfd->lcd_ref_usec_time =
+		    100000000 / mfd->panel_info.lcd.refx100;
+		mfd->vsync_handler_pending = FALSE;
+
+		mfd->last_vsync_timetick.tv64 = 0;
+
+#ifdef MDP_HW_VSYNC
+		if (mdp_vsync_clk == NULL)
+			mdp_vsync_clk = clk_get(&pdev->dev, "vsync_clk");
+
+		if (IS_ERR(mdp_vsync_clk)) {
+			printk(KERN_ERR "error: can't get mdp_vsync_clk!\n");
+			mfd->use_mdp_vsync = 0;
+		} else
+			mfd->use_mdp_vsync = 1;
+
+		if (mfd->use_mdp_vsync) {
+			uint32 vsync_cnt_cfg_dem;
+			uint32 mdp_vsync_clk_speed_hz;
+
+			mdp_vsync_clk_speed_hz = clk_get_rate(mdp_vsync_clk);
+
+			if (mdp_vsync_clk_speed_hz == 0) {
+				mfd->use_mdp_vsync = 0;
+			} else {
+				/*
+				 * Do this calculation in 2 steps for
+				 * rounding uint32 properly.
+				 */
+				vsync_cnt_cfg_dem =
+				    (mfd->panel_info.lcd.refx100 *
+				     mfd->total_lcd_lines) / 100;
+				vsync_cnt_cfg =
+				    (mdp_vsync_clk_speed_hz) /
+				    vsync_cnt_cfg_dem;
+				mdp_vsync_cfg_regs(mfd, TRUE);
+			}
+		}
+#else
+		mfd->use_mdp_vsync = 0;
+		hrtimer_init(&mfd->dma_hrtimer, CLOCK_MONOTONIC,
+			     HRTIMER_MODE_REL);
+		mfd->dma_hrtimer.function = mdp_dma2_vsync_hrtimer_handler;
+		mfd->vsync_width_boundary = vmalloc(mfd->panel_info.xres * 4);
+#endif
+
+#ifdef CONFIG_FB_MSM_MDDI
+		mfd->channel_irq = 0;
+		if (mfd->panel_info.lcd.hw_vsync_mode) {
+			u32 vsync_gpio = mfd->vsync_gpio;
+			u32 ret;
+
+			if (vsync_gpio == -1) {
+				MSM_FB_INFO("vsync_gpio not defined!\n");
+				goto err_handle;
+			}
+
+			ret = gpio_tlmm_config(GPIO_CFG
+					(vsync_gpio,
+					(mfd->use_mdp_vsync) ? 1 : 0,
+					GPIO_CFG_INPUT,
+					GPIO_CFG_PULL_DOWN,
+					GPIO_CFG_2MA),
+					GPIO_CFG_ENABLE);
+			if (ret)
+				goto err_handle;
+
+			/*
+			 * if use_mdp_vsync, then no interrupt need since
+			 * mdp_vsync is feed directly to mdp to reset the
+			 * write pointer counter. therefore no irq_handler
+			 * need to reset write pointer counter.
+			 */
+			if (!mfd->use_mdp_vsync) {
+				mfd->channel_irq = MSM_GPIO_TO_INT(vsync_gpio);
+				if (request_irq
+				    (mfd->channel_irq,
+				     &mdp_hw_vsync_handler_proxy,
+				     IRQF_TRIGGER_FALLING, "VSYNC_GPIO",
+				     (void *)mfd)) {
+					MSM_FB_INFO
+					("irq=%d failed! vsync_gpio=%d\n",
+						mfd->channel_irq,
+						vsync_gpio);
+					goto err_handle;
+				}
+			}
+		}
+#endif
+		mdp_hw_vsync_clk_enable(mfd);
+		mdp_set_vsync((unsigned long)mfd);
+	}
+
+	return;
+
+err_handle:
+	if (mfd->vsync_width_boundary)
+		vfree(mfd->vsync_width_boundary);
+	mfd->panel_info.lcd.vsync_enable = FALSE;
+	printk(KERN_ERR "%s: failed!\n", __func__);
+}
+
+void mdp_vsync_resync_workqueue_handler(struct work_struct *work)
+{
+	struct msm_fb_data_type *mfd = NULL;
+	int vsync_fnc_enabled = FALSE;
+	struct msm_fb_panel_data *pdata = NULL;
+
+	mfd = container_of(work, struct msm_fb_data_type, vsync_resync_worker);
+
+	if (mfd) {
+		if (mfd->panel_power_on) {
+			pdata =
+			    (struct msm_fb_panel_data *)mfd->pdev->dev.
+			    platform_data;
+
+			if (pdata->set_vsync_notifier != NULL) {
+				if (pdata->clk_func && !pdata->clk_func(2)) {
+					mfd->vsync_handler_pending = FALSE;
+					return;
+				}
+
+				pdata->set_vsync_notifier(
+						mdp_vsync_handler,
+						(void *)mfd);
+				vsync_fnc_enabled = TRUE;
+			}
+		}
+	}
+
+	if ((mfd) && (!vsync_fnc_enabled))
+		mfd->vsync_handler_pending = FALSE;
+}
+
+boolean mdp_hw_vsync_set_handler(msm_fb_vsync_handler_type handler, void *data)
+{
+	/*
+	 * ToDo: tried enabling/disabling GPIO MDP HW VSYNC interrupt
+	 * but getting inaccurate timing in mdp_vsync_handler()
+	 * enable_irq(MDP_HW_VSYNC_IRQ);
+	 */
+
+	return TRUE;
+}
+
+uint32 mdp_get_lcd_line_counter(struct msm_fb_data_type *mfd)
+{
+	uint32 elapsed_usec_time;
+	uint32 lcd_line;
+	ktime_t last_vsync_timetick_local;
+	ktime_t curr_time;
+	unsigned long flag;
+
+	if ((!mfd->panel_info.lcd.vsync_enable) || (!vsync_mode))
+		return 0;
+
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	last_vsync_timetick_local = mfd->last_vsync_timetick;
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+
+	curr_time = ktime_get_real();
+	elapsed_usec_time = ktime_to_us(ktime_sub(curr_time,
+						last_vsync_timetick_local));
+
+	elapsed_usec_time = elapsed_usec_time % mfd->lcd_ref_usec_time;
+
+	/* lcd line calculation referencing to line counter = 0 */
+	lcd_line =
+	    (elapsed_usec_time * mfd->total_lcd_lines) / mfd->lcd_ref_usec_time;
+
+	/* lcd line adjusment referencing to the actual line counter at vsync */
+	lcd_line =
+	    (mfd->total_lcd_lines - mfd->panel_info.lcd.v_back_porch +
+	     lcd_line) % (mfd->total_lcd_lines + 1);
+
+	if (lcd_line > mfd->total_lcd_lines) {
+		MSM_FB_INFO
+		    ("mdp_get_lcd_line_counter: mdp_lcd_rd_cnt >= mfd->total_lcd_lines error!\n");
+	}
+
+	return lcd_line;
+}
diff --git a/drivers/video/msm/mhl/mhl_8334.c b/drivers/video/msm/mhl/mhl_8334.c
new file mode 100644
index 0000000..43280a5
--- /dev/null
+++ b/drivers/video/msm/mhl/mhl_8334.c
@@ -0,0 +1,849 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/mutex.h>
+#include <mach/msm_hdmi_audio.h>
+#include <mach/clk.h>
+#include <mach/msm_iomap.h>
+#include <mach/socinfo.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+
+#include "msm_fb.h"
+#include "external_common.h"
+#include "mhl_8334.h"
+#include "mhl_i2c_utils.h"
+
+#define DEBUG
+
+
+static struct i2c_device_id mhl_sii_i2c_id[] = {
+	{ MHL_DRIVER_NAME, 0 },
+	{ }
+};
+
+struct mhl_msm_state_t *mhl_msm_state;
+spinlock_t mhl_state_lock;
+
+static int mhl_i2c_probe(struct i2c_client *client,\
+	const struct i2c_device_id *id);
+static int mhl_i2c_remove(struct i2c_client *client);
+static void force_usb_switch_open(void);
+static void release_usb_switch_open(void);
+static void switch_mode(enum mhl_st_type to_mode);
+static irqreturn_t mhl_tx_isr(int irq, void *dev_id);
+
+static struct i2c_driver mhl_sii_i2c_driver = {
+	.driver = {
+		.name = MHL_DRIVER_NAME,
+		.owner = THIS_MODULE,
+	},
+	.probe = mhl_i2c_probe,
+	/*.remove =  __exit_p(mhl_i2c_remove),*/
+	.remove =  mhl_i2c_remove,
+	.id_table = mhl_sii_i2c_id,
+};
+
+bool mhl_is_connected(void)
+{
+	return true;
+}
+
+static void cbus_reset(void)
+{
+	uint8_t i;
+
+	/*
+	 * REG_SRST
+	 */
+	mhl_i2c_reg_modify(TX_PAGE_3, 0x0000, BIT3, BIT3);
+	msleep(20);
+	mhl_i2c_reg_modify(TX_PAGE_3, 0x0000, BIT3, 0x00);
+	/*
+	 * REG_INTR1 and REG_INTR4
+	 */
+	mhl_i2c_reg_write(TX_PAGE_L0, 0x0075, BIT6 | BIT5);
+	mhl_i2c_reg_write(TX_PAGE_3, 0x0022,
+		BIT0 | BIT2 | BIT3 | BIT4 | BIT5 | BIT6);
+	/* REG5 */
+	if (mhl_msm_state->chip_rev_id < 1)
+		mhl_i2c_reg_write(TX_PAGE_3, 0x0024, BIT3 | BIT4);
+	else
+		/*REG5 Mask disabled due to auto FIFO reset ??*/
+		mhl_i2c_reg_write(TX_PAGE_3, 0x0024, 0x00);
+
+	/* Unmask CBUS1 Intrs */
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0009,
+		BIT2 | BIT3 | BIT4 | BIT5 | BIT6);
+
+	/* Unmask CBUS2 Intrs */
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x001F, BIT2 | BIT3);
+
+	for (i = 0; i < 4; i++) {
+		/*
+		 * Enable WRITE_STAT interrupt for writes to
+		 * all 4 MSC Status registers.
+		 */
+		mhl_i2c_reg_write(TX_PAGE_CBUS, (0xE0 + i), 0xFF);
+
+		/*
+		 * Enable SET_INT interrupt for writes to
+		 * all 4 MSC Interrupt registers.
+		 */
+		mhl_i2c_reg_write(TX_PAGE_CBUS, (0xF0 + i), 0xFF);
+	}
+}
+
+static void init_cbus_regs(void)
+{
+	uint8_t		regval;
+
+	/* Increase DDC translation layer timer*/
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0007, 0xF2);
+	/* Drive High Time */
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0036, 0x03);
+	/* Use programmed timing */
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0039, 0x30);
+	/* CBUS Drive Strength */
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0040, 0x03);
+	/*
+	 * Write initial default settings
+	 * to devcap regs: default settings
+	 */
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_DEV_STATE,
+		DEVCAP_VAL_DEV_STATE);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_MHL_VERSION,
+		DEVCAP_VAL_MHL_VERSION);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_DEV_CAT,
+		DEVCAP_VAL_DEV_CAT);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_ADOPTER_ID_H,
+		DEVCAP_VAL_ADOPTER_ID_H);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_ADOPTER_ID_L,
+		DEVCAP_VAL_ADOPTER_ID_L);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_VID_LINK_MODE,
+		DEVCAP_VAL_VID_LINK_MODE);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_AUD_LINK_MODE,
+		DEVCAP_VAL_AUD_LINK_MODE);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_VIDEO_TYPE,
+		DEVCAP_VAL_VIDEO_TYPE);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_LOG_DEV_MAP,
+		DEVCAP_VAL_LOG_DEV_MAP);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_BANDWIDTH,
+		DEVCAP_VAL_BANDWIDTH);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_FEATURE_FLAG,
+		DEVCAP_VAL_FEATURE_FLAG);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_DEVICE_ID_H,
+		DEVCAP_VAL_DEVICE_ID_H);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_DEVICE_ID_L,
+		DEVCAP_VAL_DEVICE_ID_L);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_SCRATCHPAD_SIZE,
+		DEVCAP_VAL_SCRATCHPAD_SIZE);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_INT_STAT_SIZE,
+		DEVCAP_VAL_INT_STAT_SIZE);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_RESERVED,
+		DEVCAP_VAL_RESERVED);
+
+	/* Make bits 2,3 (initiator timeout) to 1,1
+	 * for register CBUS_LINK_CONTROL_2
+	 * REG_CBUS_LINK_CONTROL_2
+	 */
+	regval = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x0031);
+	regval = (regval | 0x0C);
+	/* REG_CBUS_LINK_CONTROL_2 */
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0031, regval);
+	 /* REG_MSC_TIMEOUT_LIMIT */
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0022, 0x0F);
+	/* REG_CBUS_LINK_CONTROL_1 */
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0030, 0x01);
+	/* disallow vendor specific commands */
+	mhl_i2c_reg_modify(TX_PAGE_CBUS, 0x002E, BIT4, BIT4);
+}
+
+/*
+ * Configure the initial reg settings
+ */
+static void mhl_init_reg_settings(void)
+{
+
+	/*
+	 * ============================================
+	 * POWER UP
+	 * ============================================
+	 */
+
+	/* Power up 1.2V core */
+	mhl_i2c_reg_write(TX_PAGE_L1, 0x003D, 0x3F);
+	/* Enable Tx PLL Clock */
+	mhl_i2c_reg_write(TX_PAGE_2, 0x0011, 0x01);
+	/* Enable Tx Clock Path and Equalizer */
+	mhl_i2c_reg_write(TX_PAGE_2, 0x0012, 0x11);
+	/* Tx Source Termination ON */
+	mhl_i2c_reg_write(TX_PAGE_3, 0x0030, 0x10);
+	/* Enable 1X MHL Clock output */
+	mhl_i2c_reg_write(TX_PAGE_3, 0x0035, 0xAC);
+	/* Tx Differential Driver Config */
+	mhl_i2c_reg_write(TX_PAGE_3, 0x0031, 0x3C);
+	mhl_i2c_reg_write(TX_PAGE_3, 0x0033, 0xD9);
+	/* PLL Bandwidth Control */
+	mhl_i2c_reg_write(TX_PAGE_3, 0x0037, 0x02);
+	/*
+	 * ============================================
+	 * Analog PLL Control
+	 * ============================================
+	 */
+	/* Enable Rx PLL clock */
+	mhl_i2c_reg_write(TX_PAGE_L0, 0x0080, 0x00);
+	mhl_i2c_reg_write(TX_PAGE_L0, 0x00F8, 0x0C);
+	mhl_i2c_reg_write(TX_PAGE_L0, 0x0085, 0x02);
+	mhl_i2c_reg_write(TX_PAGE_2, 0x0000, 0x00);
+	mhl_i2c_reg_write(TX_PAGE_2, 0x0013, 0x60);
+	/* PLL Cal ref sel */
+	mhl_i2c_reg_write(TX_PAGE_2, 0x0017, 0x03);
+	/* VCO Cal */
+	mhl_i2c_reg_write(TX_PAGE_2, 0x001A, 0x20);
+	/* Auto EQ */
+	mhl_i2c_reg_write(TX_PAGE_2, 0x0022, 0xE0);
+	mhl_i2c_reg_write(TX_PAGE_2, 0x0023, 0xC0);
+	mhl_i2c_reg_write(TX_PAGE_2, 0x0024, 0xA0);
+	mhl_i2c_reg_write(TX_PAGE_2, 0x0025, 0x80);
+	mhl_i2c_reg_write(TX_PAGE_2, 0x0026, 0x60);
+	mhl_i2c_reg_write(TX_PAGE_2, 0x0027, 0x40);
+	mhl_i2c_reg_write(TX_PAGE_2, 0x0028, 0x20);
+	mhl_i2c_reg_write(TX_PAGE_2, 0x0029, 0x00);
+	/* Rx PLL Bandwidth 4MHz */
+	mhl_i2c_reg_write(TX_PAGE_2, 0x0031, 0x0A);
+	/* Rx PLL Bandwidth value from I2C */
+	mhl_i2c_reg_write(TX_PAGE_2, 0x0045, 0x06);
+	mhl_i2c_reg_write(TX_PAGE_2, 0x004B, 0x06);
+	/* Manual zone control */
+	mhl_i2c_reg_write(TX_PAGE_2, 0x004C, 0xE0);
+	/* PLL Mode value */
+	mhl_i2c_reg_write(TX_PAGE_2, 0x004D, 0x00);
+	mhl_i2c_reg_write(TX_PAGE_L0, 0x0008, 0x35);
+	/*
+	 * Discovery Control and Status regs
+	 * Setting De-glitch time to 50 ms (default)
+	 * Switch Control Disabled
+	 */
+	mhl_i2c_reg_write(TX_PAGE_3, 0x0011, 0xAD);
+	/* 1.8V CBUS VTH */
+	mhl_i2c_reg_write(TX_PAGE_3, 0x0014, 0x55);
+	/* RGND and single Discovery attempt */
+	mhl_i2c_reg_write(TX_PAGE_3, 0x0015, 0x11);
+	/* Ignore VBUS */
+	mhl_i2c_reg_write(TX_PAGE_3, 0x0017, 0x82);
+	mhl_i2c_reg_write(TX_PAGE_3, 0x0018, 0x24);
+	/* Pull-up resistance off for IDLE state */
+	mhl_i2c_reg_write(TX_PAGE_3, 0x0013, 0x84);
+	/* Enable CBUS Discovery */
+	mhl_i2c_reg_write(TX_PAGE_3, 0x0010, 0x27);
+	mhl_i2c_reg_write(TX_PAGE_3, 0x0016, 0x20);
+	/* MHL CBUS Discovery - immediate comm.  */
+	mhl_i2c_reg_write(TX_PAGE_3, 0x0012, 0x86);
+	/* Do not force HPD to 0 during wake-up from D3 */
+	if (mhl_msm_state->cur_state != POWER_STATE_D3) {
+		mhl_i2c_reg_modify(TX_PAGE_3, 0x0020,
+			       BIT5 | BIT4, BIT4);
+	}
+	/* Enable Auto Soft RESET */
+	mhl_i2c_reg_write(TX_PAGE_3, 0x0000, 0x084);
+	/* HDMI Transcode mode enable */
+	mhl_i2c_reg_write(TX_PAGE_L0, 0x000D, 0x1C);
+
+	cbus_reset();
+	init_cbus_regs();
+}
+
+static int mhl_chip_init(void)
+{
+	/* Read the chip rev ID */
+	mhl_msm_state->chip_rev_id = mhl_i2c_reg_read(TX_PAGE_L0, 0x04);
+	pr_debug("MHL: chip rev ID read=[%x]\n", mhl_msm_state->chip_rev_id);
+
+	/* Reset the TX chip */
+	mhl_msm_state->mhl_data->reset_pin(0);
+	msleep(20);
+	mhl_msm_state->mhl_data->reset_pin(1);
+	/* MHL spec requires a 100 ms wait here.  */
+	msleep(100);
+
+	mhl_init_reg_settings();
+
+	/*
+	 * Power down the chip to the
+	 * D3 - a low power standby mode
+	 * cable impedance measurement logic is operational
+	 */
+	switch_mode(POWER_STATE_D3);
+	return 0;
+}
+
+/*
+ * I2C probe
+ */
+static int mhl_i2c_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	int ret;
+	mhl_msm_state->mhl_data = kzalloc(sizeof(struct msm_mhl_platform_data),
+		GFP_KERNEL);
+	if (!(mhl_msm_state->mhl_data)) {
+		ret = -ENOMEM;
+		goto probe_exit;
+	}
+	pr_debug("Inside probe\n");
+	mhl_msm_state->i2c_client = client;
+
+	spin_lock_init(&mhl_state_lock);
+
+	i2c_set_clientdata(client, mhl_msm_state);
+	mhl_msm_state->mhl_data = client->dev.platform_data;
+
+	/* Init GPIO stuff here */
+	ret = mhl_msm_state->mhl_data->gpio_setup(1);
+	if (ret == -1) {
+		pr_err("MHL: mhl_gpio_init has failed\n");
+		ret = -ENODEV;
+		goto probe_exit;
+	}
+	return 0;
+
+probe_exit:
+	if (mhl_msm_state->mhl_data) {
+		/* free the gpios */
+		mhl_msm_state->mhl_data->gpio_setup(0);
+		kfree(mhl_msm_state->mhl_data);
+		mhl_msm_state->mhl_data = NULL;
+	}
+	return ret;
+}
+
+static int mhl_i2c_remove(struct i2c_client *client)
+{
+	pr_debug("inside i2c remove\n");
+	mhl_msm_state->mhl_data->gpio_setup(0);
+	kfree(mhl_msm_state->mhl_data);
+	return 0;
+}
+
+static int __init mhl_msm_init(void)
+{
+	int32_t     ret;
+
+	mhl_msm_state = kzalloc(sizeof(struct mhl_msm_state_t), GFP_KERNEL);
+	if (!mhl_msm_state) {
+		pr_err("mhl_msm_init FAILED: out of memory\n");
+		ret = -ENOMEM;
+		goto init_exit;
+	}
+
+	mhl_msm_state->i2c_client = NULL;
+	ret = i2c_add_driver(&mhl_sii_i2c_driver);
+	if (ret) {
+		pr_err("MHL: I2C driver add failed: %d\n", ret);
+		ret = -ENODEV;
+		goto init_exit;
+	} else {
+		if (mhl_msm_state->i2c_client == NULL) {
+			pr_err("JSR: I2C driver add failed\n");
+			ret = -ENODEV;
+			goto init_exit;
+		}
+		pr_debug("MHL: I2C driver added\n");
+	}
+
+	/* Request IRQ stuff here */
+	pr_debug("MHL: mhl_msm_state->mhl_data->irq=[%d]\n",
+		mhl_msm_state->mhl_data->irq);
+	ret = request_threaded_irq(mhl_msm_state->mhl_data->irq, NULL,
+				   &mhl_tx_isr,
+				 IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+				 "mhl_tx_isr", mhl_msm_state);
+	if (ret != 0) {
+		pr_err("request_threaded_irq failed, status: %d\n",
+			ret);
+		ret = -EACCES; /* Error code???? */
+		goto init_exit;
+	}
+
+	mhl_msm_state->cur_state = POWER_STATE_D0_MHL;
+
+	/* MHL SII 8334 chip specific init */
+	mhl_chip_init();
+	return 0;
+
+init_exit:
+	pr_err("Exiting from the init with err\n");
+	i2c_del_driver(&mhl_sii_i2c_driver);
+	if (!mhl_msm_state) {
+		kfree(mhl_msm_state);
+		mhl_msm_state = NULL;
+	 }
+	 return ret;
+}
+
+static void switch_mode(enum mhl_st_type to_mode)
+{
+	unsigned long flags;
+
+	switch (to_mode) {
+	case POWER_STATE_D0_NO_MHL:
+		break;
+	case POWER_STATE_D0_MHL:
+		mhl_init_reg_settings();
+
+		/* REG_DISC_CTRL1 */
+		mhl_i2c_reg_modify(TX_PAGE_3, 0x0010, BIT1, 0);
+
+		/*
+		 * TPI_DEVICE_POWER_STATE_CTRL_REG
+		 * TX_POWER_STATE_MASK = BIT1 | BIT0
+		 */
+		mhl_i2c_reg_modify(TX_PAGE_TPI, 0x001E, BIT1 | BIT0, 0x00);
+		break;
+	case POWER_STATE_D3:
+		if (mhl_msm_state->cur_state != POWER_STATE_D3) {
+			/* Force HPD to 0 when not in MHL mode.  */
+			mhl_i2c_reg_modify(TX_PAGE_3, 0x0020,
+				BIT5 | BIT4, BIT4);
+
+			/*
+			 * Change TMDS termination to high impedance
+			 * on disconnection.
+			 */
+			mhl_i2c_reg_write(TX_PAGE_3, 0x0030, 0xD0);
+			mhl_i2c_reg_modify(TX_PAGE_L1, 0x003D,
+				BIT1 | BIT0, BIT0);
+			spin_lock_irqsave(&mhl_state_lock, flags);
+			mhl_msm_state->cur_state = POWER_STATE_D3;
+			spin_unlock_irqrestore(&mhl_state_lock, flags);
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+static void mhl_drive_hpd(uint8_t to_state)
+{
+	pr_debug("%s: To state=[0x%x]\n", __func__, to_state);
+	if (to_state == HPD_UP) {
+		/*
+		 * Drive HPD to UP state
+		 *
+		 * The below two reg configs combined
+		 * enable TMDS output.
+		 */
+
+		/* Enable TMDS on TMDS_CCTRL */
+		mhl_i2c_reg_modify(TX_PAGE_L0, 0x0080, BIT4, BIT4);
+
+		/*
+		 * Set HPD_OUT_OVR_EN = HPD State
+		 * EDID read and Un-force HPD (from low)
+		 * propogate to src let HPD float by clearing
+		 * HPD OUT OVRRD EN
+		 */
+		mhl_i2c_reg_modify(TX_PAGE_3, 0x0020, BIT4, 0x00);
+	} else {
+		/*
+		 * Drive HPD to DOWN state
+		 * Disable TMDS Output on REG_TMDS_CCTRL
+		 * Enable/Disable TMDS output (MHL TMDS output only)
+		 */
+		mhl_i2c_reg_modify(TX_PAGE_L0, 0x0080, BIT4, 0x00);
+	}
+	return;
+}
+
+static void mhl_msm_connection(void)
+{
+	uint8_t val;
+	unsigned long flags;
+
+	pr_err("%s: cur state = [0x%x]\n", __func__, mhl_msm_state->cur_state);
+
+	if (mhl_msm_state->cur_state == POWER_STATE_D0_MHL) {
+		/* Already in D0 - MHL power state */
+		return;
+	}
+	spin_lock_irqsave(&mhl_state_lock, flags);
+	mhl_msm_state->cur_state = POWER_STATE_D0_MHL;
+	spin_unlock_irqrestore(&mhl_state_lock, flags);
+
+	mhl_i2c_reg_write(TX_PAGE_3, 0x30, 0x10);
+
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x07, 0xF2);
+
+	/*
+	 * Keep the discovery enabled. Need RGND interrupt
+	 * Possibly chip disables discovery after MHL_EST??
+	 * Need to re-enable here
+	 */
+	val = mhl_i2c_reg_read(TX_PAGE_3, 0x10);
+	mhl_i2c_reg_write(TX_PAGE_3, 0x10, val | BIT(0));
+
+	return;
+}
+
+static void mhl_msm_disconnection(void)
+{
+	uint8_t reg;
+
+	/* Clear interrupts - REG INTR4 */
+	reg = mhl_i2c_reg_read(TX_PAGE_3, 0x0021);
+	mhl_i2c_reg_write(TX_PAGE_3, 0x0021, reg);
+	/*
+	 * MHL TX CTL1
+	 * Disabling Tx termination
+	 */
+	mhl_i2c_reg_write(TX_PAGE_3, 0x30, 0xD0);
+	/*
+	 * MSC REQUESTOR ABORT REASON
+	 * Clear CBUS_HPD status
+	 */
+	mhl_i2c_reg_modify(TX_PAGE_CBUS, 0x000D, BIT6, 0x00);
+	/* Change HPD line to drive it low */
+	mhl_drive_hpd(HPD_DOWN);
+	/* switch power state to D3 */
+	switch_mode(POWER_STATE_D3);
+	return;
+}
+
+/*
+ * If hardware detected a change in impedence and raised an INTR
+ * We check the range of this impedence to infer if the connected
+ * device is MHL or USB and take appropriate actions.
+ */
+static void mhl_msm_read_rgnd_int(void)
+{
+	uint8_t rgnd_imp;
+
+	/*
+	 * DISC STATUS REG 2
+	 * 1:0 RGND
+	 * 00  - open (USB)
+	 * 01  - 2 kOHM (USB)
+	 * 10  - 1 kOHM ***(MHL)**** It's range 800 - 1200 OHM from MHL spec
+	 * 11  - short (USB)
+	 */
+	rgnd_imp = mhl_i2c_reg_read(TX_PAGE_3, 0x001C);
+	pr_debug("Imp Range read = %02X\n", (int)rgnd_imp);
+
+
+	if (0x02 == rgnd_imp) {
+		pr_debug("MHL: MHL DEVICE!!!\n");
+		/*
+		 * Handling the MHL event in driver
+		 */
+		mhl_i2c_reg_modify(TX_PAGE_3, 0x0018, BIT0, BIT0);
+	} else {
+		pr_debug("MHL: NON-MHL DEVICE!!!\n");
+		mhl_i2c_reg_modify(TX_PAGE_3, 0x0018, BIT3, BIT3);
+	}
+}
+
+static void force_usb_switch_open(void)
+{
+	/*DISABLE_DISCOVERY*/
+	mhl_i2c_reg_modify(TX_PAGE_3, 0x0010, BIT0, 0);
+	/* Force USB ID switch to open*/
+	mhl_i2c_reg_modify(TX_PAGE_3, 0x0015, BIT6, BIT6);
+	mhl_i2c_reg_write(TX_PAGE_3, 0x0012, 0x86);
+	/* Force HPD to 0 when not in Mobile HD mode. */
+	mhl_i2c_reg_modify(TX_PAGE_3, 0x0020, BIT5 | BIT4, BIT4);
+}
+
+static void release_usb_switch_open(void)
+{
+	msleep(50);
+	mhl_i2c_reg_modify(TX_PAGE_3, 0x0015, BIT6, 0x00);
+	mhl_i2c_reg_modify(TX_PAGE_3, 0x0010, BIT0, BIT0);
+}
+
+static void int_4_isr(void)
+{
+	uint8_t status;
+
+	/* INTR_STATUS4 */
+	status = mhl_i2c_reg_read(TX_PAGE_3, 0x0021);
+
+	/*
+	 * When I2C is inoperational (D3) and
+	 * a previous interrupt brought us here,
+	 * do nothing.
+	 */
+	pr_debug("MHL: MRR Interrupt status is = %02X\n", (int) status);
+	if (0xFF != status) {
+		if ((status & BIT0) && (mhl_msm_state->chip_rev_id < 1)) {
+			uint8_t tmds_cstat;
+			uint8_t mhl_fifo_status;
+
+			/* TMDS CSTAT */
+			tmds_cstat = mhl_i2c_reg_read(TX_PAGE_3, 0x0040);
+
+			pr_debug("TMDS CSTAT: 0x%02x\n", tmds_cstat);
+
+			if (tmds_cstat & 0x02) {
+				mhl_fifo_status = mhl_i2c_reg_read(TX_PAGE_3,
+					0x0023);
+				pr_debug("MHL FIFO status: 0x%02x\n",
+					mhl_fifo_status);
+				if (mhl_fifo_status & 0x0C) {
+					mhl_i2c_reg_write(TX_PAGE_3, 0x0023,
+						0x0C);
+
+					pr_debug("Apply MHL FIFO Reset\n");
+					mhl_i2c_reg_write(TX_PAGE_3, 0x0000,
+						0x94);
+					mhl_i2c_reg_write(TX_PAGE_3, 0x0000,
+						0x84);
+				}
+			}
+		}
+
+		if (status & BIT1)
+			pr_err("MHL: INT4 BIT1 is set\n");
+
+		/* MHL_EST interrupt */
+		if (status & BIT2) {
+			pr_err("MHL: Calling mhl_msm_connection() from ISR\n");
+			mhl_msm_connection();
+			pr_err("MHL Connect  Drv: INT4 Status = %02X\n",
+				(int) status);
+		} else if (status & BIT3) {
+			pr_err("MHL: uUSB-A type device detected.\n");
+			mhl_i2c_reg_write(TX_PAGE_3, 0x001C, 0x80);
+			switch_mode(POWER_STATE_D3);
+		}
+
+		if (status & BIT5) {
+			mhl_msm_disconnection();
+			pr_err("MHL Disconnect Drv: INT4 Status = %02X\n",
+				(int)status);
+		}
+
+		if ((mhl_msm_state->cur_state != POWER_STATE_D0_MHL) &&\
+			(status & BIT6)) {
+			/* RGND READY Intr */
+			switch_mode(POWER_STATE_D0_MHL);
+			mhl_msm_read_rgnd_int();
+		}
+
+		/* Can't succeed at these in D3 */
+		if (mhl_msm_state->cur_state != POWER_STATE_D3) {
+			/* CBUS Lockout interrupt? */
+			/*
+			 * Hardware detection mechanism figures that
+			 * CBUS line is latched and raises this intr
+			 * where we force usb switch open and release
+			 */
+			if (status & BIT4) {
+				force_usb_switch_open();
+				release_usb_switch_open();
+			}
+		}
+	}
+	pr_debug("MHL END  Drv: INT4 Status = %02X\n", (int) status);
+	mhl_i2c_reg_write(TX_PAGE_3, 0x0021, status);
+
+	return;
+}
+
+static void int_5_isr(void)
+{
+	uint8_t intr_5_stat;
+
+	/*
+	 * Clear INT 5 ??
+	 * Probably need to revisit this later
+	 * INTR5 is related to FIFO underflow/overflow reset
+	 * which is handled in 8334 by auto FIFO reset
+	 */
+	intr_5_stat = mhl_i2c_reg_read(TX_PAGE_3, 0x0023);
+	mhl_i2c_reg_write(TX_PAGE_3, 0x0023, intr_5_stat);
+}
+
+
+static void int_1_isr(void)
+{
+	/* This ISR mainly handles the HPD status changes */
+	uint8_t intr_1_stat;
+	uint8_t cbus_stat;
+
+	/* INTR STATUS 1 */
+	intr_1_stat = mhl_i2c_reg_read(TX_PAGE_L0, 0x0071);
+
+	if (intr_1_stat) {
+		/* Clear interrupts */
+		mhl_i2c_reg_write(TX_PAGE_L0, 0x0071, intr_1_stat);
+		if (BIT6 & intr_1_stat) {
+			/*
+			 * HPD status change event is pending
+			 * Read CBUS HPD status for this info
+			 */
+
+			/* MSC REQ ABRT REASON */
+			cbus_stat = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x0D);
+			if (BIT6 & cbus_stat)
+				mhl_drive_hpd(HPD_UP);
+		}
+	}
+	return;
+}
+
+/*
+ * RCP, RAP messages - mandatory for compliance
+ *
+ */
+static void mhl_cbus_isr(void)
+{
+	uint8_t regval;
+	int req_done = FALSE;
+	uint8_t sub_cmd;
+	uint8_t cmd_data;
+	int msc_msg_recved = FALSE;
+	int rc = -1;
+
+	regval  = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x08);
+	if (regval == 0xff)
+		return;
+
+	/* clear all interrupts that were raised even if we did not process */
+	if (regval)
+		mhl_i2c_reg_write(TX_PAGE_CBUS, 0x08, regval);
+
+	pr_err("%s: CBUS_INT = %02x\n", __func__, regval);
+
+	/* MSC_MSG (RCP/RAP) */
+	if (regval & BIT(3)) {
+		sub_cmd = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x18);
+		cmd_data = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x19);
+		msc_msg_recved = TRUE;
+	}
+
+	/* MSC_REQ_DONE */
+	if (regval & BIT(4))
+		req_done = TRUE;
+
+	/* Now look for interrupts on CBUS_MSC_INT2 */
+	regval  = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x1E);
+
+	/* clear all interrupts that were raised */
+	/* even if we did not process */
+	if (regval)
+		mhl_i2c_reg_write(TX_PAGE_CBUS, 0x1E, regval);
+
+	pr_err("%s: CBUS_MSC_INT2 = %02x\n", __func__, regval);
+
+	/* received SET_INT */
+	if (regval & BIT(2)) {
+		uint8_t intr;
+		intr = mhl_i2c_reg_read(TX_PAGE_CBUS, 0xA0);
+		pr_debug("%s: MHL_INT_0 = %02x\n", __func__, intr);
+		intr = mhl_i2c_reg_read(TX_PAGE_CBUS, 0xA1);
+		pr_debug("%s: MHL_INT_1 = %02x\n", __func__, intr);
+		mhl_i2c_reg_write(TX_PAGE_CBUS, 0xA0, 0xFF);
+		mhl_i2c_reg_write(TX_PAGE_CBUS, 0xA1, 0xFF);
+		mhl_i2c_reg_write(TX_PAGE_CBUS, 0xA2, 0xFF);
+		mhl_i2c_reg_write(TX_PAGE_CBUS, 0xA3, 0xFF);
+	}
+
+	/* received WRITE_STAT */
+	if (regval & BIT(3)) {
+		uint8_t stat;
+		stat = mhl_i2c_reg_read(TX_PAGE_CBUS, 0xB0);
+		pr_err("%s: MHL_STATUS_0 = %02x\n", __func__, stat);
+		stat = mhl_i2c_reg_read(TX_PAGE_CBUS, 0xB1);
+		pr_err("%s: MHL_STATUS_1 = %02x\n", __func__, stat);
+
+		mhl_i2c_reg_write(TX_PAGE_CBUS, 0xB0, 0xFF);
+		mhl_i2c_reg_write(TX_PAGE_CBUS, 0xB1, 0xFF);
+		mhl_i2c_reg_write(TX_PAGE_CBUS, 0xB2, 0xFF);
+		mhl_i2c_reg_write(TX_PAGE_CBUS, 0xB3, 0xFF);
+	}
+
+	/* received MSC_MSG */
+	if (msc_msg_recved) {
+		/*mhl msc recv msc msg*/
+		if (rc)
+			pr_err("MHL: mhl msc recv msc msg failed(%d)!\n", rc);
+	}
+
+	return;
+}
+
+static irqreturn_t mhl_tx_isr(int irq, void *dev_id)
+{
+	/*
+	 * Check discovery interrupt
+	 * if not yet connected
+	 */
+	pr_debug("MHL: Current POWER state is [0x%x]\n",
+		mhl_msm_state->cur_state);
+	/*
+	 * Check RGND, MHL_EST, CBUS_LOCKOUT, SCDT
+	 * interrupts. In D3, we get only RGND
+	 */
+	int_4_isr();
+
+	pr_debug("MHL: Current POWER state is [0x%x]\n",
+		mhl_msm_state->cur_state);
+	if (mhl_msm_state->cur_state == POWER_STATE_D0_MHL) {
+		/*
+		 * If int_4_isr() didn't move the tx to D3
+		 * on disconnect, continue to check other
+		 * interrupt sources.
+		 */
+		int_5_isr();
+
+		/*
+		 * Check for any peer messages for DCAP_CHG etc
+		 * Dispatch to have the CBUS module working only
+		 * once connected.
+		 */
+		mhl_cbus_isr();
+		int_1_isr();
+	}
+	return IRQ_HANDLED;
+}
+
+static void __exit mhl_msm_exit(void)
+{
+	pr_warn("MHL: Exiting, Bye\n");
+	/*
+	 * Delete driver if i2c client structure is NULL
+	 */
+	i2c_del_driver(&mhl_sii_i2c_driver);
+	if (!mhl_msm_state) {
+		kfree(mhl_msm_state);
+		mhl_msm_state = NULL;
+	 }
+}
+
+module_init(mhl_msm_init);
+module_exit(mhl_msm_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MHL SII 8334 TX driver");
diff --git a/drivers/video/msm/mhl/mhl_8334.h b/drivers/video/msm/mhl/mhl_8334.h
new file mode 100644
index 0000000..c1d9030
--- /dev/null
+++ b/drivers/video/msm/mhl/mhl_8334.h
@@ -0,0 +1,74 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __MHL_MSM_H__
+#define __MHL_MSM_H__
+
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <mach/board.h>
+
+#include "mhl_devcap.h"
+#include "mhl_defs.h"
+
+#define GPIO_MHL_RESET       15
+#define GPIO_MHL_INT         4
+
+#define MHL_DEVICE_NAME "sii8334"
+#define MHL_DRIVER_NAME "sii8334"
+
+#define HPD_UP               1
+#define HPD_DOWN             0
+
+struct mhl_msm_state_t {
+	struct i2c_client *i2c_client;
+	struct i2c_driver *i2c_driver;
+	uint8_t      cur_state;
+	uint8_t chip_rev_id;
+	struct msm_mhl_platform_data *mhl_data;
+};
+
+enum {
+	TX_PAGE_TPI          = 0x00,
+	TX_PAGE_L0           = 0x01,
+	TX_PAGE_L1           = 0x02,
+	TX_PAGE_2            = 0x03,
+	TX_PAGE_3            = 0x04,
+	TX_PAGE_CBUS         = 0x05,
+	TX_PAGE_DDC_EDID     = 0x06,
+	TX_PAGE_DDC_SEGM     = 0x07,
+};
+
+enum mhl_st_type {
+	POWER_STATE_D0_NO_MHL = 0,
+	POWER_STATE_D0_MHL    = 2,
+	POWER_STATE_D3        = 3,
+};
+
+enum {
+	DEV_PAGE_TPI_0      = (0x72),
+	DEV_PAGE_TX_L0_0    = (0x72),
+	DEV_PAGE_TPI_1      = (0x76),
+	DEV_PAGE_TX_L0_1    = (0x76),
+	DEV_PAGE_TX_L1_0    = (0x7A),
+	DEV_PAGE_TX_L1_1    = (0x7E),
+	DEV_PAGE_TX_2_0     = (0x92),
+	DEV_PAGE_TX_2_1     = (0x96),
+	DEV_PAGE_TX_3_0	    = (0x9A),
+	DEV_PAGE_TX_3_1	    = (0x9E),
+	DEV_PAGE_CBUS       = (0xC8),
+	DEV_PAGE_DDC_EDID   = (0xA0),
+	DEV_PAGE_DDC_SEGM   = (0x60),
+};
+
+#endif /* __MHL_MSM_H__ */
diff --git a/drivers/video/msm/mhl/mhl_defs.h b/drivers/video/msm/mhl/mhl_defs.h
new file mode 100644
index 0000000..094874e
--- /dev/null
+++ b/drivers/video/msm/mhl/mhl_defs.h
@@ -0,0 +1,222 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __MHL_SPEC_DEFS_H__
+#define __MHL_SPEC_DEFS_H__
+
+enum DevCapOffset_e {
+	DEVCAP_OFFSET_DEV_STATE         = 0x00,
+	DEVCAP_OFFSET_MHL_VERSION	= 0x01,
+	DEVCAP_OFFSET_DEV_CAT           = 0x02,
+	DEVCAP_OFFSET_ADOPTER_ID_H      = 0x03,
+	DEVCAP_OFFSET_ADOPTER_ID_L      = 0x04,
+	DEVCAP_OFFSET_VID_LINK_MODE     = 0x05,
+	DEVCAP_OFFSET_AUD_LINK_MODE     = 0x06,
+	DEVCAP_OFFSET_VIDEO_TYPE        = 0x07,
+	DEVCAP_OFFSET_LOG_DEV_MAP       = 0x08,
+	DEVCAP_OFFSET_BANDWIDTH         = 0x09,
+	DEVCAP_OFFSET_FEATURE_FLAG      = 0x0A,
+	DEVCAP_OFFSET_DEVICE_ID_H       = 0x0B,
+	DEVCAP_OFFSET_DEVICE_ID_L       = 0x0C,
+	DEVCAP_OFFSET_SCRATCHPAD_SIZE   = 0x0D,
+	DEVCAP_OFFSET_INT_STAT_SIZE     = 0x0E,
+	DEVCAP_OFFSET_RESERVED          = 0x0F,
+	/* this one must be last */
+	DEVCAP_SIZE
+};
+
+#ifndef __MHL_MSM_8334_REGS_H__
+#define __MHL_MSM_8334_REGS_H__
+
+#define BIT0                    0x01
+#define BIT1                    0x02
+#define BIT2                    0x04
+#define BIT3                    0x08
+#define BIT4                    0x10
+#define BIT5                    0x20
+#define BIT6                    0x40
+#define BIT7                    0x80
+
+#define LOW                     0
+#define HIGH                    1
+
+#define MAX_PAGES               8
+#endif
+
+
+/* Version that this chip supports*/
+/* bits 4..7 */
+#define	MHL_VER_MAJOR           (0x01 << 4)
+/* bits 0..3 */
+#define	MHL_VER_MINOR		0x01
+#define MHL_VERSION		(MHL_VER_MAJOR | MHL_VER_MINOR)
+
+/*Device Category*/
+#define	MHL_DEV_CATEGORY_OFFSET		DEVCAP_OFFSET_DEV_CAT
+#define	MHL_DEV_CATEGORY_POW_BIT	(BIT4)
+
+#define	MHL_DEV_CAT_SOURCE		0x02
+
+/*Video Link Mode*/
+#define	MHL_DEV_VID_LINK_SUPPRGB444		0x01
+#define	MHL_DEV_VID_LINK_SUPPYCBCR444		0x02
+#define	MHL_DEV_VID_LINK_SUPPYCBCR422		0x04
+#define	MHL_DEV_VID_LINK_SUPP_PPIXEL		0x08
+#define	MHL_DEV_VID_LINK_SUPP_ISLANDS		0x10
+
+/*Audio Link Mode Support*/
+#define	MHL_DEV_AUD_LINK_2CH				0x01
+#define	MHL_DEV_AUD_LINK_8CH				0x02
+
+
+/*Feature Flag in the devcap*/
+#define	MHL_DEV_FEATURE_FLAG_OFFSET		DEVCAP_OFFSET_FEATURE_FLAG
+/* Dongles have freedom to not support RCP */
+#define	MHL_FEATURE_RCP_SUPPORT				BIT0
+/* Dongles have freedom to not support RAP */
+#define	MHL_FEATURE_RAP_SUPPORT				BIT1
+/* Dongles have freedom to not support SCRATCHPAD */
+#define	MHL_FEATURE_SP_SUPPORT				BIT2
+
+/*Logical Dev Map*/
+#define	MHL_DEV_LD_DISPLAY					(0x01 << 0)
+#define	MHL_DEV_LD_VIDEO					(0x01 << 1)
+#define	MHL_DEV_LD_AUDIO					(0x01 << 2)
+#define	MHL_DEV_LD_MEDIA					(0x01 << 3)
+#define	MHL_DEV_LD_TUNER					(0x01 << 4)
+#define	MHL_DEV_LD_RECORD					(0x01 << 5)
+#define	MHL_DEV_LD_SPEAKER					(0x01 << 6)
+#define	MHL_DEV_LD_GUI						(0x01 << 7)
+
+/*Bandwidth*/
+/* 225 MHz */
+#define	MHL_BANDWIDTH_LIMIT					22
+
+
+#define MHL_STATUS_REG_CONNECTED_RDY        0x30
+#define MHL_STATUS_REG_LINK_MODE            0x31
+
+#define	MHL_STATUS_DCAP_RDY					BIT0
+
+#define MHL_STATUS_CLK_MODE_MASK            0x07
+#define MHL_STATUS_CLK_MODE_PACKED_PIXEL    0x02
+#define MHL_STATUS_CLK_MODE_NORMAL          0x03
+#define MHL_STATUS_PATH_EN_MASK             0x08
+#define MHL_STATUS_PATH_ENABLED             0x08
+#define MHL_STATUS_PATH_DISABLED            0x00
+#define MHL_STATUS_MUTED_MASK               0x10
+
+#define MHL_RCHANGE_INT                     0x20
+#define MHL_DCHANGE_INT                     0x21
+
+#define	MHL_INT_DCAP_CHG					BIT0
+#define MHL_INT_DSCR_CHG                    BIT1
+#define MHL_INT_REQ_WRT                     BIT2
+#define MHL_INT_GRT_WRT                     BIT3
+
+/* On INTR_1 the EDID_CHG is located at BIT 0*/
+#define	MHL_INT_EDID_CHG					BIT1
+
+/* This contains one nibble each - max offset */
+#define		MHL_INT_AND_STATUS_SIZE			0x33
+#define		MHL_SCRATCHPAD_SIZE			16
+/* manually define highest number */
+#define		MHL_MAX_BUFFER_SIZE			MHL_SCRATCHPAD_SIZE
+
+
+
+enum {
+	/* RCP sub-command  */
+	MHL_MSC_MSG_RCP             = 0x10,
+	/* RCP Acknowledge sub-command  */
+	MHL_MSC_MSG_RCPK            = 0x11,
+	/* RCP Error sub-command  */
+	MHL_MSC_MSG_RCPE            = 0x12,
+	/* Mode Change Warning sub-command  */
+	MHL_MSC_MSG_RAP             = 0x20,
+	/* MCW Acknowledge sub-command  */
+	MHL_MSC_MSG_RAPK            = 0x21,
+};
+
+#define	RCPE_NO_ERROR				0x00
+#define	RCPE_INEEFECTIVE_KEY_CODE	0x01
+#define	RCPE_BUSY					0x02
+/* MHL spec related defines*/
+enum {
+	/* Command or Data byte acknowledge */
+	MHL_ACK						= 0x33,
+	/* Command or Data byte not acknowledge */
+	MHL_NACK					= 0x34,
+	/* Transaction abort */
+	MHL_ABORT					= 0x35,
+	/* 0xE0 - Write one status register strip top bit */
+	MHL_WRITE_STAT				= 0x60 | 0x80,
+	/* Write one interrupt register */
+	MHL_SET_INT					= 0x60,
+	/* Read one register */
+	MHL_READ_DEVCAP				= 0x61,
+	/* Read CBUS revision level from follower */
+	MHL_GET_STATE				= 0x62,
+	/* Read vendor ID value from follower. */
+	MHL_GET_VENDOR_ID			= 0x63,
+	/* Set Hot Plug Detect in follower */
+	MHL_SET_HPD					= 0x64,
+	/* Clear Hot Plug Detect in follower */
+	MHL_CLR_HPD					= 0x65,
+	/* Set Capture ID for downstream device. */
+	MHL_SET_CAP_ID				= 0x66,
+	/* Get Capture ID from downstream device. */
+	MHL_GET_CAP_ID				= 0x67,
+	/* VS command to send RCP sub-commands */
+	MHL_MSC_MSG					= 0x68,
+	/* Get Vendor-Specific command error code. */
+	MHL_GET_SC1_ERRORCODE		= 0x69,
+	/* Get DDC channel command error code. */
+	MHL_GET_DDC_ERRORCODE		= 0x6A,
+	/* Get MSC command error code. */
+	MHL_GET_MSC_ERRORCODE		= 0x6B,
+	/* Write 1-16 bytes to responder's scratchpad. */
+	MHL_WRITE_BURST				= 0x6C,
+	/* Get channel 3 command error code. */
+	MHL_GET_SC3_ERRORCODE		= 0x6D,
+};
+
+/* Turn content streaming ON. */
+#define	MHL_RAP_CONTENT_ON		0x10
+/* Turn content streaming OFF. */
+#define	MHL_RAP_CONTENT_OFF		0x11
+
+/*
+ *
+ * MHL Timings applicable to this driver.
+ *
+ */
+/* 100 - 1000 milliseconds. Per MHL 1.0 Specs */
+#define	T_SRC_VBUS_CBUS_TO_STABLE	(200)
+/* 20 milliseconds. Per MHL 1.0 Specs */
+#define	T_SRC_WAKE_PULSE_WIDTH_1	(20)
+/* 60 milliseconds. Per MHL 1.0 Specs */
+#define	T_SRC_WAKE_PULSE_WIDTH_2	(60)
+
+/* 100 - 1000 milliseconds. Per MHL 1.0 Specs */
+#define	T_SRC_WAKE_TO_DISCOVER		(500)
+
+#define T_SRC_VBUS_CBUS_T0_STABLE	(500)
+
+/* Allow RSEN to stay low this much before reacting.*/
+#define	T_SRC_RSEN_DEGLITCH			(100)
+
+/* Wait this much after connection before reacting to RSEN (300-500ms)*/
+/* Per specs between 300 to 500 ms*/
+#define	T_SRC_RXSENSE_CHK			(400)
+
+#endif /* __MHL_SPEC_DEFS_H__ */
diff --git a/drivers/video/msm/mhl/mhl_devcap.h b/drivers/video/msm/mhl/mhl_devcap.h
new file mode 100644
index 0000000..6d01daf
--- /dev/null
+++ b/drivers/video/msm/mhl/mhl_devcap.h
@@ -0,0 +1,45 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __MHL_DEVCAP_H__
+#define __MHL_DEVCAP_H__
+
+#define SILICON_IMAGE_ADOPTER_ID 322
+#define TRANSCODER_DEVICE_ID 0x8334
+
+#define MHL_DEV_LD_AUDIO (0x01 << 2)
+#define MHL_DEV_LD_VIDEO (0x01 << 1)
+#define MHL_DEV_LD_MEDIA (0x01 << 3)
+#define MHL_DEV_LD_GUI (0x01 << 7)
+#define	MHL_LOGICAL_DEVICE_MAP		(MHL_DEV_LD_AUDIO |\
+	MHL_DEV_LD_VIDEO | MHL_DEV_LD_MEDIA | MHL_DEV_LD_GUI)
+
+#define DEVCAP_VAL_DEV_STATE       0
+#define DEVCAP_VAL_MHL_VERSION     MHL_VERSION
+#define DEVCAP_VAL_DEV_CAT         (MHL_DEV_CAT_SOURCE |\
+	MHL_DEV_CATEGORY_POW_BIT)
+#define DEVCAP_VAL_ADOPTER_ID_H    (uint8_t)(SILICON_IMAGE_ADOPTER_ID >>   8)
+#define DEVCAP_VAL_ADOPTER_ID_L    (uint8_t)(SILICON_IMAGE_ADOPTER_ID & 0xFF)
+#define DEVCAP_VAL_VID_LINK_MODE   MHL_DEV_VID_LINK_SUPPRGB444
+#define DEVCAP_VAL_AUD_LINK_MODE   MHL_DEV_AUD_LINK_2CH
+#define DEVCAP_VAL_VIDEO_TYPE      0
+#define DEVCAP_VAL_LOG_DEV_MAP     MHL_LOGICAL_DEVICE_MAP
+#define DEVCAP_VAL_BANDWIDTH       0
+#define DEVCAP_VAL_FEATURE_FLAG    (MHL_FEATURE_RCP_SUPPORT |\
+	MHL_FEATURE_RAP_SUPPORT | MHL_FEATURE_SP_SUPPORT)
+#define DEVCAP_VAL_DEVICE_ID_H     (uint8_t)(TRANSCODER_DEVICE_ID >>   8)
+#define DEVCAP_VAL_DEVICE_ID_L     (uint8_t)(TRANSCODER_DEVICE_ID & 0xFF)
+#define DEVCAP_VAL_SCRATCHPAD_SIZE MHL_SCRATCHPAD_SIZE
+#define DEVCAP_VAL_INT_STAT_SIZE   MHL_INT_AND_STATUS_SIZE
+#define DEVCAP_VAL_RESERVED        0
+
+#endif /* __MHL_DEVCAP_H__ */
diff --git a/drivers/video/msm/mhl/mhl_i2c_utils.c b/drivers/video/msm/mhl/mhl_i2c_utils.c
new file mode 100644
index 0000000..596af2e
--- /dev/null
+++ b/drivers/video/msm/mhl/mhl_i2c_utils.c
@@ -0,0 +1,107 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/i2c.h>
+
+#include "mhl_i2c_utils.h"
+#include "mhl_8334.h"
+
+#define DEBUG
+
+uint8_t slave_addrs[MAX_PAGES] = {
+	DEV_PAGE_TPI_0    ,
+	DEV_PAGE_TX_L0_0  ,
+	DEV_PAGE_TX_L1_0  ,
+	DEV_PAGE_TX_2_0   ,
+	DEV_PAGE_TX_3_0   ,
+	DEV_PAGE_CBUS     ,
+	DEV_PAGE_DDC_EDID ,
+	DEV_PAGE_DDC_SEGM ,
+};
+
+int mhl_i2c_reg_read(uint8_t slave_addr_index, uint8_t reg_offset)
+{
+	struct i2c_msg msgs[2];
+	uint8_t buffer = 0;
+	int ret = -1;
+
+	pr_debug("MRR: Reading from slave_addr_index=[%x] and offset=[%x]\n",
+		slave_addr_index, reg_offset);
+	pr_debug("MRR: Addr slave_addr_index=[%x]\n",
+		slave_addrs[slave_addr_index]);
+
+	/* Slave addr */
+	msgs[0].addr = slave_addrs[slave_addr_index] >> 1;
+	msgs[1].addr = slave_addrs[slave_addr_index] >> 1;
+
+	/* Write Command */
+	msgs[0].flags = 0;
+	msgs[1].flags = I2C_M_RD;
+
+	/* Register offset for the next transaction */
+	msgs[0].buf = &reg_offset;
+	msgs[1].buf = &buffer;
+
+	/* Offset is 1 Byte long */
+	msgs[0].len = 1;
+	msgs[1].len = 1;
+
+	ret = i2c_transfer(mhl_msm_state->i2c_client->adapter, msgs, 2);
+	if (ret < 1) {
+		pr_err("I2C READ FAILED=[%d]\n", ret);
+		return -EACCES;
+	}
+	pr_err("Buffer is [%x]\n", buffer);
+	return buffer;
+}
+
+
+int mhl_i2c_reg_write(uint8_t slave_addr_index, uint8_t reg_offset,
+	uint8_t value)
+{
+	return mhl_i2c_reg_write_cmds(slave_addr_index, reg_offset, &value, 1);
+}
+
+int mhl_i2c_reg_write_cmds(uint8_t slave_addr_index, uint8_t reg_offset,
+	uint8_t *value, uint16_t count)
+{
+	struct i2c_msg msgs[1];
+	uint8_t data[2];
+	int status = -EACCES;
+
+	msgs[0].addr = slave_addrs[slave_addr_index] >> 1;
+	msgs[0].flags = 0;
+	msgs[0].len = 2;
+	msgs[0].buf = data;
+	data[0] = reg_offset;
+	data[1] = *value;
+
+	status = i2c_transfer(mhl_msm_state->i2c_client->adapter, msgs, 1);
+	if (status < 1) {
+		pr_err("I2C WRITE FAILED=[%d]\n", status);
+		return -EACCES;
+	}
+
+	return status;
+}
+
+void mhl_i2c_reg_modify(uint8_t slave_addr_index, uint8_t reg_offset,
+	uint8_t mask, uint8_t val)
+{
+	uint8_t temp;
+
+	temp = mhl_i2c_reg_read(slave_addr_index, reg_offset);
+	temp &= (~mask);
+	temp |= (mask & val);
+	mhl_i2c_reg_write(slave_addr_index, reg_offset, temp);
+}
+
diff --git a/drivers/video/msm/mhl/mhl_i2c_utils.h b/drivers/video/msm/mhl/mhl_i2c_utils.h
new file mode 100644
index 0000000..76498d4
--- /dev/null
+++ b/drivers/video/msm/mhl/mhl_i2c_utils.h
@@ -0,0 +1,39 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __MHL_I2C_UTILS_H__
+#define __MHL_I2C_UTILS_H__
+
+#include <linux/i2c.h>
+#include <linux/types.h>
+
+#include "mhl_defs.h"
+
+/*
+ * I2C command to the adapter to append
+ * the buffer from next msg to this one.
+ */
+#define I2C_M_APPND_NXT_WR          0x0002
+
+extern uint8_t slave_addrs[MAX_PAGES];
+extern struct mhl_msm_state_t *mhl_msm_state;
+
+int mhl_i2c_reg_read(uint8_t slave_addr_index, uint8_t reg_offset);
+int mhl_i2c_reg_write(uint8_t slave_addr_index, uint8_t reg_offset,
+	uint8_t value);
+int mhl_i2c_reg_write_cmds(uint8_t slave_addr_index, uint8_t reg_offset,
+	uint8_t *value, uint16_t count);
+void mhl_i2c_reg_modify(uint8_t slave_addr_index, uint8_t reg_offset,
+	uint8_t mask, uint8_t val);
+
+#endif /* __MHL_I2C_UTILS_H__ */
diff --git a/drivers/video/msm/mhl_api.h b/drivers/video/msm/mhl_api.h
new file mode 100644
index 0000000..a4364ea
--- /dev/null
+++ b/drivers/video/msm/mhl_api.h
@@ -0,0 +1,26 @@
+
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __MHL_API_H__
+#define __MHL_API_H__
+
+#ifdef CONFIG_FB_MSM_HDMI_MHL_8334
+bool mhl_is_connected(void);
+#else
+static bool mhl_is_connected(void)
+{
+	return false;
+}
+#endif
+
+#endif /* __MHL_API_H__ */
diff --git a/drivers/video/msm/mipi_NT35510.c b/drivers/video/msm/mipi_NT35510.c
new file mode 100644
index 0000000..964df4e
--- /dev/null
+++ b/drivers/video/msm/mipi_NT35510.c
@@ -0,0 +1,609 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_fb.h"
+#include "mipi_dsi.h"
+#include "mipi_NT35510.h"
+
+static struct msm_panel_common_pdata *mipi_nt35510_pdata;
+static struct dsi_buf nt35510_tx_buf;
+static struct dsi_buf nt35510_rx_buf;
+
+#define NT35510_SLEEP_OFF_DELAY 150
+#define NT35510_DISPLAY_ON_DELAY 150
+
+/* common setting */
+static char exit_sleep[2] = {0x11, 0x00};
+static char display_on[2] = {0x29, 0x00};
+static char display_off[2] = {0x28, 0x00};
+static char enter_sleep[2] = {0x10, 0x00};
+static char write_ram[2] = {0x2c, 0x00}; /* write ram */
+
+static struct dsi_cmd_desc nt35510_display_off_cmds[] = {
+	{DTYPE_DCS_WRITE, 1, 0, 0, 150, sizeof(display_off), display_off},
+	{DTYPE_DCS_WRITE, 1, 0, 0, 150, sizeof(enter_sleep), enter_sleep}
+};
+
+static char cmd0[6] = {
+	0xF0, 0x55, 0xAA, 0x52,
+	0x08, 0x01,
+};
+static char cmd1[4] = {
+	0xBC, 0x00, 0xA0, 0x00,
+};
+static char cmd2[4] = {
+	0xBD, 0x00, 0xA0, 0x00,
+};
+static char cmd3[3] = {
+	0xBE, 0x00, 0x79,
+};
+static char cmd4[53] = {
+	0xD1, 0x00, 0x00, 0x00,
+	0x14, 0x00, 0x32, 0x00,
+	0x4F, 0x00, 0x65, 0x00,
+	0x8B, 0x00, 0xA8, 0x00,
+	0xD5, 0x00, 0xF7, 0x01,
+	0x2B, 0x01, 0x54, 0x01,
+	0x8E, 0x01, 0xBB, 0x01,
+	0xBC, 0x01, 0xE3, 0x02,
+	0x08, 0x02, 0x1C, 0x02,
+	0x39, 0x02, 0x4F, 0x02,
+	0x76, 0x02, 0xA3, 0x02,
+	0xE3, 0x03, 0x12, 0x03,
+	0x4C, 0x03, 0x66, 0x03,
+	0x9A,
+};
+static char cmd5[53] = {
+	0xD2, 0x00, 0x00, 0x00,
+	0x14, 0x00, 0x32, 0x00,
+	0x4F, 0x00, 0x65, 0x00,
+	0x8B, 0x00, 0xA8, 0x00,
+	0xD5, 0x00, 0xF7, 0x01,
+	0x2B, 0x01, 0x54, 0x01,
+	0x8E, 0x01, 0xBB, 0x01,
+	0xBC, 0x01, 0xE3, 0x02,
+	0x08, 0x02, 0x1C, 0x02,
+	0x39, 0x02, 0x4F, 0x02,
+	0x76, 0x02, 0xA3, 0x02,
+	0xE3, 0x03, 0x12, 0x03,
+	0x4C, 0x03, 0x66, 0x03,
+	0x9A,
+};
+static char cmd6[53] = {
+	0xD3, 0x00, 0x00, 0x00,
+	0x14, 0x00, 0x32, 0x00,
+	0x4F, 0x00, 0x65, 0x00,
+	0x8B, 0x00, 0xA8, 0x00,
+	0xD5, 0x00, 0xF7, 0x01,
+	0x2B, 0x01, 0x54, 0x01,
+	0x8E, 0x01, 0xBB, 0x01,
+	0xBC, 0x01, 0xE3, 0x02,
+	0x08, 0x02, 0x1C, 0x02,
+	0x39, 0x02, 0x4F, 0x02,
+	0x76, 0x02, 0xA3, 0x02,
+	0xE3, 0x03, 0x12, 0x03,
+	0x4C, 0x03, 0x66, 0x03,
+	0x9A,
+};
+static char cmd7[53] = {
+	0xD4, 0x00, 0x00, 0x00,
+	0x14, 0x00, 0x32, 0x00,
+	0x4F, 0x00, 0x65, 0x00,
+	0x8B, 0x00, 0xA8, 0x00,
+	0xD5, 0x00, 0xF7, 0x01,
+	0x2B, 0x01, 0x54, 0x01,
+	0x8E, 0x01, 0xBB, 0x01,
+	0xBC, 0x01, 0xE3, 0x02,
+	0x08, 0x02, 0x1C, 0x02,
+	0x39, 0x02, 0x4F, 0x02,
+	0x76, 0x02, 0xA3, 0x02,
+	0xE3, 0x03, 0x12, 0x03,
+	0x4C, 0x03, 0x66, 0x03,
+	0x9A,
+};
+static char cmd8[53] = {
+	0xD5, 0x00, 0x00, 0x00,
+	0x14, 0x00, 0x32, 0x00,
+	0x4F, 0x00, 0x65, 0x00,
+	0x8B, 0x00, 0xA8, 0x00,
+	0xD5, 0x00, 0xF7, 0x01,
+	0x2B, 0x01, 0x54, 0x01,
+	0x8E, 0x01, 0xBB, 0x01,
+	0xBC, 0x01, 0xE3, 0x02,
+	0x08, 0x02, 0x1C, 0x02,
+	0x39, 0x02, 0x4F, 0x02,
+	0x76, 0x02, 0xA3, 0x02,
+	0xE3, 0x03, 0x12, 0x03,
+	0x4C, 0x03, 0x66, 0x03,
+	0x9A,
+};
+static char cmd9[53] = {
+	0xD6, 0x00, 0x00, 0x00,
+	0x14, 0x00, 0x32, 0x00,
+	0x4F, 0x00, 0x65, 0x00,
+	0x8B, 0x00, 0xA8, 0x00,
+	0xD5, 0x00, 0xF7, 0x01,
+	0x2B, 0x01, 0x54, 0x01,
+	0x8E, 0x01, 0xBB, 0x01,
+	0xBC, 0x01, 0xE3, 0x02,
+	0x08, 0x02, 0x1C, 0x02,
+	0x39, 0x02, 0x4F, 0x02,
+	0x76, 0x02, 0xA3, 0x02,
+	0xE3, 0x03, 0x12, 0x03,
+	0x4C, 0x03, 0x66, 0x03,
+	0x9A,
+};
+static char cmd10[4] = {
+	0xB0, 0x0A, 0x0A, 0x0A,
+};
+static char cmd11[4] = {
+	0xB1, 0x0A, 0x0A, 0x0A,
+};
+static char cmd12[4] = {
+	0xBA, 0x24, 0x24, 0x24,
+};
+static char cmd13[4] = {
+	0xB9, 0x24, 0x24, 0x24,
+};
+static char cmd14[4] = {
+	0xB8, 0x24, 0x24, 0x24,
+};
+static char cmd15[6] = {
+	0xF0, 0x55, 0xAA, 0x52,
+	0x08, 0x00,
+};
+static char cmd16[2] = {
+	0xB3, 0x00,
+};
+static char cmd17[2] = {
+	0xB4, 0x10,
+};
+static char cmd18[2] = {
+	0xB6, 0x02,
+};
+static char cmd19[3] = {
+	0xB1, 0xEC, 0x00,
+};
+static char cmd20[4] = {
+	0xBC, 0x05, 0x05, 0x05,
+};
+static char cmd21[3] = {
+	0xB7, 0x20, 0x20,
+};
+static char cmd22[5] = {
+	0xB8, 0x01, 0x03, 0x03,
+	0x03,
+};
+static char cmd23[19] = {
+	0xC8, 0x01, 0x00, 0x78,
+	0x50, 0x78, 0x50, 0x78,
+	0x50, 0x78, 0x50, 0xC8,
+	0x3C, 0x3C, 0xC8, 0xC8,
+	0x3C, 0x3C, 0xC8,
+};
+static char cmd24[6] = {
+	0xBD, 0x01, 0x84, 0x07,
+	0x31, 0x00,
+};
+static char cmd25[6] = {
+	0xBE, 0x01, 0x84, 0x07,
+	0x31, 0x00,
+};
+static char cmd26[6] = {
+	0xBF, 0x01, 0x84, 0x07,
+	0x31, 0x00,
+};
+static char cmd27[2] = {
+	0x35, 0x00,
+};
+static char config_MADCTL[2] = {0x36, 0x00};
+static struct dsi_cmd_desc nt35510_cmd_display_on_cmds[] = {
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd0), cmd0},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd1), cmd1},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd2), cmd2},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd3), cmd3},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd4), cmd4},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd5), cmd5},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd6), cmd6},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd7), cmd7},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd8), cmd8},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd9), cmd9},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd10), cmd10},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd11), cmd11},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd12), cmd12},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd13), cmd13},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd14), cmd14},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd15), cmd15},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd16), cmd16},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd17), cmd17},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd18), cmd18},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd19), cmd19},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd20), cmd20},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd21), cmd21},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd22), cmd22},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd23), cmd23},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd24), cmd24},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd25), cmd25},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd26), cmd26},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd27), cmd27},
+
+	{DTYPE_DCS_WRITE, 1, 0, 0, 150,	sizeof(exit_sleep), exit_sleep},
+	{DTYPE_DCS_WRITE, 1, 0, 0, 10,	sizeof(display_on), display_on},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, 150,
+		sizeof(config_MADCTL), config_MADCTL},
+
+	{DTYPE_DCS_WRITE, 1, 0, 0, 10,	sizeof(write_ram), write_ram},
+};
+
+static char video0[6] = {
+	0xF0, 0x55, 0xAA, 0x52,
+	0x08, 0x01,
+};
+static char video1[4] = {
+	0xBC, 0x00, 0xA0, 0x00,
+};
+static char video2[4] = {
+	0xBD, 0x00, 0xA0, 0x00,
+};
+static char video3[3] = {
+	0xBE, 0x00, 0x79,
+};
+static char video4[53] = {
+	0xD1, 0x00, 0x00, 0x00,
+	0x14, 0x00, 0x32, 0x00,
+	0x4F, 0x00, 0x65, 0x00,
+	0x8B, 0x00, 0xA8, 0x00,
+	0xD5, 0x00, 0xF7, 0x01,
+	0x2B, 0x01, 0x54, 0x01,
+	0x8E, 0x01, 0xBB, 0x01,
+	0xBC, 0x01, 0xE3, 0x02,
+	0x08, 0x02, 0x1C, 0x02,
+	0x39, 0x02, 0x4F, 0x02,
+	0x76, 0x02, 0xA3, 0x02,
+	0xE3, 0x03, 0x12, 0x03,
+	0x4C, 0x03, 0x66, 0x03,
+	0x9A,
+};
+static char video5[53] = {
+	0xD2, 0x00, 0x00, 0x00,
+	0x14, 0x00, 0x32, 0x00,
+	0x4F, 0x00, 0x65, 0x00,
+	0x8B, 0x00, 0xA8, 0x00,
+	0xD5, 0x00, 0xF7, 0x01,
+	0x2B, 0x01, 0x54, 0x01,
+	0x8E, 0x01, 0xBB, 0x01,
+	0xBC, 0x01, 0xE3, 0x02,
+	0x08, 0x02, 0x1C, 0x02,
+	0x39, 0x02, 0x4F, 0x02,
+	0x76, 0x02, 0xA3, 0x02,
+	0xE3, 0x03, 0x12, 0x03,
+	0x4C, 0x03, 0x66, 0x03,
+	0x9A,
+};
+static char video6[53] = {
+	0xD3, 0x00, 0x00, 0x00,
+	0x14, 0x00, 0x32, 0x00,
+	0x4F, 0x00, 0x65, 0x00,
+	0x8B, 0x00, 0xA8, 0x00,
+	0xD5, 0x00, 0xF7, 0x01,
+	0x2B, 0x01, 0x54, 0x01,
+	0x8E, 0x01, 0xBB, 0x01,
+	0xBC, 0x01, 0xE3, 0x02,
+	0x08, 0x02, 0x1C, 0x02,
+	0x39, 0x02, 0x4F, 0x02,
+	0x76, 0x02, 0xA3, 0x02,
+	0xE3, 0x03, 0x12, 0x03,
+	0x4C, 0x03, 0x66, 0x03,
+	0x9A,
+};
+static char video7[53] = {
+	0xD4, 0x00, 0x00, 0x00,
+	0x14, 0x00, 0x32, 0x00,
+	0x4F, 0x00, 0x65, 0x00,
+	0x8B, 0x00, 0xA8, 0x00,
+	0xD5, 0x00, 0xF7, 0x01,
+	0x2B, 0x01, 0x54, 0x01,
+	0x8E, 0x01, 0xBB, 0x01,
+	0xBC, 0x01, 0xE3, 0x02,
+	0x08, 0x02, 0x1C, 0x02,
+	0x39, 0x02, 0x4F, 0x02,
+	0x76, 0x02, 0xA3, 0x02,
+	0xE3, 0x03, 0x12, 0x03,
+	0x4C, 0x03, 0x66, 0x03,
+	0x9A,
+};
+static char video8[53] = {
+	0xD5, 0x00, 0x00, 0x00,
+	0x14, 0x00, 0x32, 0x00,
+	0x4F, 0x00, 0x65, 0x00,
+	0x8B, 0x00, 0xA8, 0x00,
+	0xD5, 0x00, 0xF7, 0x01,
+	0x2B, 0x01, 0x54, 0x01,
+	0x8E, 0x01, 0xBB, 0x01,
+	0xBC, 0x01, 0xE3, 0x02,
+	0x08, 0x02, 0x1C, 0x02,
+	0x39, 0x02, 0x4F, 0x02,
+	0x76, 0x02, 0xA3, 0x02,
+	0xE3, 0x03, 0x12, 0x03,
+	0x4C, 0x03, 0x66, 0x03,
+	0x9A,
+};
+static char video9[53] = {
+	0xD6, 0x00, 0x00, 0x00,
+	0x14, 0x00, 0x32, 0x00,
+	0x4F, 0x00, 0x65, 0x00,
+	0x8B, 0x00, 0xA8, 0x00,
+	0xD5, 0x00, 0xF7, 0x01,
+	0x2B, 0x01, 0x54, 0x01,
+	0x8E, 0x01, 0xBB, 0x01,
+	0xBC, 0x01, 0xE3, 0x02,
+	0x08, 0x02, 0x1C, 0x02,
+	0x39, 0x02, 0x4F, 0x02,
+	0x76, 0x02, 0xA3, 0x02,
+	0xE3, 0x03, 0x12, 0x03,
+	0x4C, 0x03, 0x66, 0x03,
+	0x9A,
+};
+static char video10[4] = {
+	0xB0, 0x0A, 0x0A, 0x0A,
+};
+static char video11[4] = {
+	0xB1, 0x0A, 0x0A, 0x0A,
+};
+static char video12[4] = {
+	0xBA, 0x24, 0x24, 0x24,
+};
+static char video13[4] = {
+	0xB9, 0x24, 0x24, 0x24,
+};
+static char video14[4] = {
+	0xB8, 0x24, 0x24, 0x24,
+};
+static char video15[6] = {
+	0xF0, 0x55, 0xAA, 0x52,
+	0x08, 0x00,
+};
+static char video16[2] = {
+	0xB3, 0x00,
+};
+static char video17[2] = {
+	0xB4, 0x10,
+};
+static char video18[2] = {
+	0xB6, 0x02,
+};
+static char video19[3] = {
+	0xB1, 0xFC, 0x00,
+};
+static char video20[4] = {
+	0xBC, 0x05, 0x05, 0x05,
+};
+static char video21[3] = {
+	0xB7, 0x20, 0x20,
+};
+static char video22[5] = {
+	0xB8, 0x01, 0x03, 0x03,
+	0x03,
+};
+static char video23[19] = {
+	0xC8, 0x01, 0x00, 0x78,
+	0x50, 0x78, 0x50, 0x78,
+	0x50, 0x78, 0x50, 0xC8,
+	0x3C, 0x3C, 0xC8, 0xC8,
+	0x3C, 0x3C, 0xC8,
+};
+static char video24[6] = {
+	0xBD, 0x01, 0x84, 0x07,
+	0x31, 0x00,
+};
+static char video25[6] = {
+	0xBE, 0x01, 0x84, 0x07,
+	0x31, 0x00,
+};
+static char video26[6] = {
+	0xBF, 0x01, 0x84, 0x07,
+	0x31, 0x00,
+};
+static char video27[2] = {
+	0x35, 0x00,
+};
+static char config_video_MADCTL[2] = {0x36, 0xC0};
+static struct dsi_cmd_desc nt35510_video_display_on_cmds[] = {
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video0), video0},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video1), video1},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video2), video2},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video3), video3},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video4), video4},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video5), video5},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video6), video6},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video7), video7},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video8), video8},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video9), video9},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video10), video10},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video11), video11},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video12), video12},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video13), video13},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video14), video14},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video15), video15},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video16), video16},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video17), video17},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video18), video18},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video19), video19},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video20), video20},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video21), video21},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video22), video22},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video23), video23},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video24), video24},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video25), video25},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video26), video26},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video27), video27},
+	{DTYPE_DCS_WRITE, 1, 0, 0, NT35510_SLEEP_OFF_DELAY, sizeof(exit_sleep),
+			exit_sleep},
+	{DTYPE_DCS_WRITE, 1, 0, 0, NT35510_DISPLAY_ON_DELAY, sizeof(display_on),
+			display_on},
+};
+
+static struct dsi_cmd_desc nt35510_video_display_on_cmds_rotate[] = {
+	{DTYPE_DCS_WRITE1, 1, 0, 0, 150,
+		sizeof(config_video_MADCTL), config_video_MADCTL},
+};
+static int mipi_nt35510_lcd_on(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+	struct mipi_panel_info *mipi;
+	static int rotate;
+	mfd = platform_get_drvdata(pdev);
+	if (!mfd)
+		return -ENODEV;
+
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	mipi  = &mfd->panel_info.mipi;
+
+	if (mipi_nt35510_pdata && mipi_nt35510_pdata->rotate_panel)
+		rotate = mipi_nt35510_pdata->rotate_panel();
+
+	if (mipi->mode == DSI_VIDEO_MODE) {
+		mipi_dsi_cmds_tx(mfd, &nt35510_tx_buf,
+			nt35510_video_display_on_cmds,
+			ARRAY_SIZE(nt35510_video_display_on_cmds));
+
+		if (rotate) {
+			mipi_dsi_cmds_tx(mfd, &nt35510_tx_buf,
+				nt35510_video_display_on_cmds_rotate,
+			ARRAY_SIZE(nt35510_video_display_on_cmds_rotate));
+		}
+	} else if (mipi->mode == DSI_CMD_MODE) {
+		mipi_dsi_cmds_tx(mfd, &nt35510_tx_buf,
+			nt35510_cmd_display_on_cmds,
+			ARRAY_SIZE(nt35510_cmd_display_on_cmds));
+	}
+
+	return 0;
+}
+
+static int mipi_nt35510_lcd_off(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+
+	pr_debug("mipi_nt35510_lcd_off E\n");
+
+	mfd = platform_get_drvdata(pdev);
+
+	if (!mfd)
+		return -ENODEV;
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	mipi_dsi_cmds_tx(mfd, &nt35510_tx_buf, nt35510_display_off_cmds,
+			ARRAY_SIZE(nt35510_display_off_cmds));
+
+	pr_debug("mipi_nt35510_lcd_off X\n");
+	return 0;
+}
+
+static int __devinit mipi_nt35510_lcd_probe(struct platform_device *pdev)
+{
+	pr_debug("%s\n", __func__);
+
+	if (pdev->id == 0) {
+		mipi_nt35510_pdata = pdev->dev.platform_data;
+		if (mipi_nt35510_pdata->bl_lock)
+			spin_lock_init(&mipi_nt35510_pdata->bl_spinlock);
+		return 0;
+	}
+
+	msm_fb_add_device(pdev);
+
+	return 0;
+}
+
+static struct platform_driver this_driver = {
+	.probe  = mipi_nt35510_lcd_probe,
+	.driver = {
+		.name   = "mipi_NT35510",
+	},
+};
+
+static void mipi_nt35510_set_backlight(struct msm_fb_data_type *mfd)
+{
+	int bl_level;
+	unsigned long flags;
+	bl_level = mfd->bl_level;
+
+	if (mipi_nt35510_pdata->bl_lock) {
+		spin_lock_irqsave(&mipi_nt35510_pdata->bl_spinlock, flags);
+		mipi_nt35510_pdata->pmic_backlight(bl_level);
+		spin_unlock_irqrestore(&mipi_nt35510_pdata->bl_spinlock, flags);
+	} else
+		mipi_nt35510_pdata->pmic_backlight(bl_level);
+}
+
+static struct msm_fb_panel_data nt35510_panel_data = {
+	.on	= mipi_nt35510_lcd_on,
+	.off = mipi_nt35510_lcd_off,
+	.set_backlight = mipi_nt35510_set_backlight,
+};
+
+static int ch_used[3];
+
+static int mipi_nt35510_lcd_init(void)
+{
+	mipi_dsi_buf_alloc(&nt35510_tx_buf, DSI_BUF_SIZE);
+	mipi_dsi_buf_alloc(&nt35510_rx_buf, DSI_BUF_SIZE);
+
+	return platform_driver_register(&this_driver);
+}
+int mipi_nt35510_device_register(struct msm_panel_info *pinfo,
+					u32 channel, u32 panel)
+{
+	struct platform_device *pdev = NULL;
+	int ret;
+
+	if ((channel >= 3) || ch_used[channel])
+		return -ENODEV;
+
+	ch_used[channel] = TRUE;
+
+	ret = mipi_nt35510_lcd_init();
+	if (ret) {
+		pr_err("mipi_nt35510_lcd_init() failed with ret %u\n", ret);
+		return ret;
+	}
+
+	pdev = platform_device_alloc("mipi_NT35510", (panel << 8)|channel);
+	if (!pdev)
+		return -ENOMEM;
+
+	nt35510_panel_data.panel_info = *pinfo;
+	ret = platform_device_add_data(pdev, &nt35510_panel_data,
+				sizeof(nt35510_panel_data));
+	if (ret) {
+		pr_debug("%s: platform_device_add_data failed!\n", __func__);
+		goto err_device_put;
+	}
+
+	ret = platform_device_add(pdev);
+	if (ret) {
+		pr_debug("%s: platform_device_register failed!\n", __func__);
+		goto err_device_put;
+	}
+
+	return 0;
+
+err_device_put:
+	platform_device_put(pdev);
+	return ret;
+}
diff --git a/drivers/video/msm/mipi_NT35510.h b/drivers/video/msm/mipi_NT35510.h
new file mode 100644
index 0000000..5c81875
--- /dev/null
+++ b/drivers/video/msm/mipi_NT35510.h
@@ -0,0 +1,20 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef MIPI_NT35510_H
+#define MIPI_NT35510_H
+
+int mipi_nt35510_device_register(struct msm_panel_info *pinfo,
+					u32 channel, u32 panel);
+
+#endif  /* MIPI_NT35510_H */
diff --git a/drivers/video/msm/mipi_NT35510_cmd_wvga_pt.c b/drivers/video/msm/mipi_NT35510_cmd_wvga_pt.c
new file mode 100644
index 0000000..f052e93
--- /dev/null
+++ b/drivers/video/msm/mipi_NT35510_cmd_wvga_pt.c
@@ -0,0 +1,98 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "msm_fb.h"
+#include "mipi_dsi.h"
+#include "mipi_NT35510.h"
+
+static struct msm_panel_info pinfo;
+
+static struct mipi_dsi_phy_ctrl dsi_cmd_mode_phy_db = {
+	/* DSI Bit Clock at 500 MHz, 2 lane, RGB888 */
+	/* regulator */
+	{0x03, 0x01, 0x01, 0x00},
+	/* timing   */
+	{0xb9, 0x8e, 0x1f, 0x00, 0x98, 0x9c, 0x22, 0x90,
+	0x18, 0x03, 0x04},
+	/* phy ctrl */
+	{0x7f, 0x00, 0x00, 0x00},
+	/* strength */
+	{0xbb, 0x02, 0x06, 0x00},
+	/* pll control */
+	{0x01, 0xec, 0x31, 0xd2, 0x00, 0x40, 0x37, 0x62,
+	0x01, 0x0f, 0x07,
+	0x05, 0x14, 0x03, 0x0, 0x0, 0x0, 0x20, 0x0, 0x02, 0x0},
+};
+
+static int mipi_cmd_nt35510_wvga_pt_init(void)
+{
+	int ret;
+
+	if (msm_fb_detect_client("mipi_cmd_nt35510_wvga"))
+		return 0;
+
+	pinfo.xres = 480;
+	pinfo.yres = 800;
+	pinfo.type = MIPI_CMD_PANEL;
+	pinfo.pdest = DISPLAY_1;
+	pinfo.wait_cycle = 0;
+	pinfo.bpp = 24;
+	pinfo.lcdc.h_back_porch = 100;
+	pinfo.lcdc.h_front_porch = 100;
+	pinfo.lcdc.h_pulse_width = 8;
+	pinfo.lcdc.v_back_porch = 20;
+	pinfo.lcdc.v_front_porch = 20;
+	pinfo.lcdc.v_pulse_width = 1;
+
+	pinfo.lcdc.border_clr = 0;	/* blk */
+	pinfo.lcdc.underflow_clr = 0xff;	/* blue */
+	pinfo.lcdc.hsync_skew = 0;
+	pinfo.bl_max = 31;
+	pinfo.bl_min = 1;
+	pinfo.fb_num = 2;
+
+	pinfo.clk_rate = 499000000;
+
+	pinfo.lcd.vsync_enable = TRUE;
+	pinfo.lcd.hw_vsync_mode = TRUE;
+	pinfo.lcd.refx100 = 6000; /* adjust refx100 to prevent tearing */
+
+	pinfo.mipi.mode = DSI_CMD_MODE;
+	pinfo.mipi.dst_format = DSI_CMD_DST_FORMAT_RGB888;
+	pinfo.mipi.vc = 0;
+	pinfo.mipi.rgb_swap = DSI_RGB_SWAP_RGB;
+	pinfo.mipi.data_lane0 = TRUE;
+	pinfo.mipi.data_lane1 = TRUE;
+	pinfo.mipi.t_clk_post = 0x20;
+	pinfo.mipi.t_clk_pre = 0x2F;
+	pinfo.mipi.stream = 0; /* dma_p */
+	pinfo.mipi.mdp_trigger = DSI_CMD_TRIGGER_SW;
+	pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW;
+	pinfo.mipi.te_sel = 1; /* TE from vsync gpio */
+	pinfo.mipi.interleave_max = 1;
+	pinfo.mipi.insert_dcs_cmd = TRUE;
+	pinfo.mipi.wr_mem_continue = 0x3c;
+	pinfo.mipi.wr_mem_start = 0x2c;
+	pinfo.mipi.dsi_phy_db = &dsi_cmd_mode_phy_db;
+	pinfo.mipi.tx_eot_append = 0x01;
+	pinfo.mipi.rx_eot_ignore = 0x0;
+	pinfo.mipi.dlane_swap = 0x01;
+
+	ret = mipi_nt35510_device_register(&pinfo, MIPI_DSI_PRIM,
+						MIPI_DSI_PANEL_WVGA_PT);
+	if (ret)
+		pr_err("%s: failed to register device!\n", __func__);
+
+	return ret;
+}
+
+module_init(mipi_cmd_nt35510_wvga_pt_init);
diff --git a/drivers/video/msm/mipi_NT35510_video_wvga_pt.c b/drivers/video/msm/mipi_NT35510_video_wvga_pt.c
new file mode 100644
index 0000000..4e97d99
--- /dev/null
+++ b/drivers/video/msm/mipi_NT35510_video_wvga_pt.c
@@ -0,0 +1,108 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_fb.h"
+#include "mipi_dsi.h"
+#include "mipi_NT35510.h"
+
+static struct msm_panel_info pinfo;
+
+static struct mipi_dsi_phy_ctrl dsi_video_mode_phy_db = {
+	/* DSI Bit Clock at 500 MHz, 2 lane, RGB888 */
+	/* regulator */
+	{0x03, 0x01, 0x01, 0x00},
+	/* timing   */
+	{0xb9, 0x8e, 0x1f, 0x00, 0x98, 0x9c, 0x22, 0x90,
+	0x18, 0x03, 0x04},
+	/* phy ctrl */
+	{0x7f, 0x00, 0x00, 0x00},
+	/* strength */
+	{0xbb, 0x02, 0x06, 0x00},
+	/* pll control */
+	{0x00, 0xec, 0x31, 0xd2, 0x00, 0x40, 0x37, 0x62,
+	0x01, 0x0f, 0x07,
+	0x05, 0x14, 0x03, 0x0, 0x0, 0x0, 0x20, 0x0, 0x02, 0x0},
+};
+
+static int mipi_video_nt35510_wvga_pt_init(void)
+{
+	int ret;
+
+	if (msm_fb_detect_client("mipi_video_nt35510_wvga"))
+		return 0;
+
+	pinfo.xres = 480;
+	pinfo.yres = 800;
+	pinfo.type = MIPI_VIDEO_PANEL;
+	pinfo.pdest = DISPLAY_1;
+	pinfo.wait_cycle = 0;
+	pinfo.bpp = 24;
+	pinfo.lcdc.h_back_porch = 100;
+	pinfo.lcdc.h_front_porch = 100;
+	pinfo.lcdc.h_pulse_width = 8;
+	pinfo.lcdc.v_back_porch = 20;
+	pinfo.lcdc.v_front_porch = 20;
+	pinfo.lcdc.v_pulse_width = 1;
+	pinfo.lcdc.border_clr = 0;	/* blk */
+	pinfo.lcdc.underflow_clr = 0xff;	/* blue */
+	/* number of dot_clk cycles HSYNC active edge is
+	delayed from VSYNC active edge */
+	pinfo.lcdc.hsync_skew = 0;
+	pinfo.clk_rate = 499000000;
+	pinfo.bl_max = 31;
+	pinfo.bl_min = 1;
+	pinfo.fb_num = 2;
+
+	pinfo.mipi.mode = DSI_VIDEO_MODE;
+	/* send HSA and HE following VS/VE packet */
+	pinfo.mipi.pulse_mode_hsa_he = TRUE;
+	pinfo.mipi.hfp_power_stop = TRUE; /* LP-11 during the HFP period */
+	pinfo.mipi.hbp_power_stop = TRUE; /* LP-11 during the HBP period */
+	pinfo.mipi.hsa_power_stop = TRUE; /* LP-11 during the HSA period */
+	/* LP-11 or let Command Mode Engine send packets in
+	HS or LP mode for the BLLP of the last line of a frame */
+	pinfo.mipi.eof_bllp_power_stop = TRUE;
+	/* LP-11 or let Command Mode Engine send packets in
+	HS or LP mode for packets sent during BLLP period */
+	pinfo.mipi.bllp_power_stop = TRUE;
+
+	pinfo.mipi.traffic_mode = DSI_BURST_MODE;
+	pinfo.mipi.dst_format =  DSI_VIDEO_DST_FORMAT_RGB888;
+	pinfo.mipi.vc = 0;
+	pinfo.mipi.rgb_swap = DSI_RGB_SWAP_RGB; /* RGB */
+	pinfo.mipi.data_lane0 = TRUE;
+	pinfo.mipi.data_lane1 = TRUE;
+
+	pinfo.mipi.t_clk_post = 0x20;
+	pinfo.mipi.t_clk_pre = 0x2f;
+
+	pinfo.mipi.stream = 0; /* dma_p */
+	pinfo.mipi.mdp_trigger = DSI_CMD_TRIGGER_NONE;
+	pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW;
+	pinfo.mipi.frame_rate = 60; /* FIXME */
+
+	pinfo.mipi.dsi_phy_db = &dsi_video_mode_phy_db;
+	pinfo.mipi.dlane_swap = 0x01;
+	/* append EOT at the end of data burst */
+	pinfo.mipi.tx_eot_append = 0x01;
+
+	ret = mipi_nt35510_device_register(&pinfo, MIPI_DSI_PRIM,
+						MIPI_DSI_PANEL_WVGA_PT);
+
+	if (ret)
+		pr_err("%s: failed to register device!\n", __func__);
+
+	return ret;
+}
+
+module_init(mipi_video_nt35510_wvga_pt_init);
diff --git a/drivers/video/msm/mipi_chimei_wuxga.c b/drivers/video/msm/mipi_chimei_wuxga.c
new file mode 100644
index 0000000..6ddf74d
--- /dev/null
+++ b/drivers/video/msm/mipi_chimei_wuxga.c
@@ -0,0 +1,196 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*
+ * Chimei WUXGA LVDS Panel driver.
+ * The panel model is N101JSF-L21.
+ *
+ * The panel interface includes:
+ * 1. LVDS input for video (clock & data).
+ * 2. few configuration	pins to control 3D module: Enable, Mode (2D/3D).
+ * 3. Backlight LED control (PWM 200 HZ).
+ *
+ * This panel is controled via the Toshiba DSI-to-LVDS bridge.
+ *
+ */
+
+/* #define DEBUG 1 */
+
+#include "msm_fb.h"
+#include "msm_fb_panel.h"
+#include "mipi_dsi.h"
+#include "mipi_tc358764_dsi2lvds.h"
+
+#define MHZ (1000*1000)
+
+/**
+ * Panel info parameters.
+ * The panel info is passed to the mipi framebuffer driver.
+ */
+static struct msm_panel_info chimei_wuxga_pinfo;
+
+/**
+ * The mipi_dsi_phy_ctrl is calculated according to the
+ * "dsi_timing_program.xlsm" excel sheet.
+ * Output is based on: 1200x1920, RGB565, 4 lanes , 58 frames
+ * per second.
+ */
+static struct mipi_dsi_phy_ctrl dsi_video_mode_phy_db = {
+	/* DSIPHY_REGULATOR_CTRL */
+	.regulator = {0x03, 0x0a, 0x04, 0x00, 0x20}, /* common 8960 */
+	/* DSIPHY_CTRL */
+	.ctrl = {0x5f, 0x00, 0x00, 0x10}, /* common 8960 */
+	/* DSIPHY_STRENGTH_CTRL */
+	.strength = {0xff, 0x00, 0x06, 0x00}, /* common 8960 */
+	/* DSIPHY_TIMING_CTRL */
+	.timing = { 0xC9, 0x92, 0x29, /* panel specific */
+	0, /* DSIPHY_TIMING_CTRL_3 = 0 */
+	0x2D, 0x9B, 0x2B, 0x94, 0x2D, 0x03, 0x04},  /* panel specific */
+
+	/* DSIPHY_PLL_CTRL */
+	.pll = { 0x00, /* common 8960 */
+	/* VCO */
+	0x30, (0x01 | 0x30) , (0x19 | 0xC0), /* panel specific */
+	0x00, 0x50, 0x48, 0x63,
+	0x77, 0x88, 0x99, /* Auto update by dsi-mipi driver */
+	0x00, 0x14, 0x03, 0x00, 0x02, /* common 8960 */
+	0x00, 0x20, 0x00, 0x01 }, /* common 8960 */
+};
+
+/**
+ * Module init.
+ *
+ * Register the panel-info.
+ *
+ * Some parameters are from the panel datasheet
+ * and other are *calculated* by the "dsi_timing_program.xlsm"
+ * excel file
+ *
+ * @return int
+ */
+static int __init mipi_chimei_wuxga_init(void)
+{
+	int ret;
+	struct msm_panel_info *pinfo = &chimei_wuxga_pinfo;
+
+	if (msm_fb_detect_client("mipi_video_chimei_wuxga"))
+		return 0;
+
+	pr_info("mipi-dsi chimei wuxga (1200x1920) driver ver 1.0.\n");
+
+	/* Portrait */
+	pinfo->xres = 1200;
+	pinfo->yres = 1920;
+	pinfo->type =  MIPI_VIDEO_PANEL;
+	pinfo->pdest = DISPLAY_1; /* Primary Display */
+	pinfo->wait_cycle = 0;
+	pinfo->bpp = 24; /* RGB565 requires 24 bits-per-pixel :-O */
+	pinfo->fb_num = 2; /* using two frame buffers */
+
+	/*
+	 * The CMI panel requires 80 MHZ LVDS-CLK.
+	 * The D2L bridge drives the LVDS-CLK from the DSI-CLK.
+	 * The DSI-CLK = bitclk/2, 640 MHZ/2= 320 MHZ.
+	 * LVDS-CLK = DSI-CLK/4 , 320 MHZ/4= 80 MHZ.
+	 */
+
+	pinfo->clk_rate = 635 * MHZ ; /* bitclk Calculated */
+
+	/*
+	 * this panel is operated by DE,
+	 * vsycn and hsync are ignored
+	 */
+
+	pinfo->lcdc.h_front_porch = 160-48-32;	/* thfp */
+	pinfo->lcdc.h_back_porch = 48;	/* thb */
+	pinfo->lcdc.h_pulse_width = 32;	/* thpw */
+
+	pinfo->lcdc.v_front_porch = 26-3-6;	/* tvfp */
+	pinfo->lcdc.v_back_porch = 3;	/* tvb */
+	pinfo->lcdc.v_pulse_width = 6;	/* tvpw */
+
+	pinfo->lcdc.border_clr = 0;		/* black */
+	pinfo->lcdc.underflow_clr = 0xff;	/* blue */
+
+	pinfo->lcdc.hsync_skew = 0;
+
+	/* Backlight levels - controled via PMIC pwm gpio */
+	pinfo->bl_max = PWM_LEVEL;
+	pinfo->bl_min = 1;
+
+	/* mipi - general */
+	pinfo->mipi.vc = 0; /* virtual channel */
+	pinfo->mipi.rgb_swap = DSI_RGB_SWAP_RGB;
+	pinfo->mipi.tx_eot_append = true;
+	pinfo->mipi.t_clk_post = 34;		/* Calculated */
+	pinfo->mipi.t_clk_pre = 69;		/* Calculated */
+
+	pinfo->mipi.dsi_phy_db = &dsi_video_mode_phy_db;
+
+	/* Four lanes are recomended for 1920x1200 at 60 frames per second */
+	pinfo->mipi.frame_rate = 60;
+	pinfo->mipi.data_lane0 = true;
+	pinfo->mipi.data_lane1 = true;
+	pinfo->mipi.data_lane2 = true;
+	pinfo->mipi.data_lane3 = true;
+
+	pinfo->mipi.mode = DSI_VIDEO_MODE;
+	/*
+	 * Note: The CMI panel input is RGB888,
+	 * thus the DSI-to-LVDS bridge output is RGB888.
+	 * This parameter selects the DSI-Core output to the bridge.
+	 */
+	pinfo->mipi.dst_format = DSI_VIDEO_DST_FORMAT_RGB565;
+
+	/* mipi - video mode */
+	pinfo->mipi.traffic_mode = DSI_NON_BURST_SYNCH_EVENT;
+	pinfo->mipi.pulse_mode_hsa_he = false; /* sync mode */
+
+	pinfo->mipi.hfp_power_stop = false;
+	pinfo->mipi.hbp_power_stop = false;
+	pinfo->mipi.hsa_power_stop = false;
+	pinfo->mipi.eof_bllp_power_stop = false;
+	pinfo->mipi.bllp_power_stop = false;
+
+	/* mipi - command mode */
+	pinfo->mipi.te_sel = 1; /* TE from vsycn gpio */
+	pinfo->mipi.interleave_max = 1;
+	/* The bridge supports only Generic Read/Write commands */
+	pinfo->mipi.insert_dcs_cmd = false;
+	pinfo->mipi.wr_mem_continue = 0;
+	pinfo->mipi.wr_mem_start = 0;
+	pinfo->mipi.stream = false; /* dma_p */
+	pinfo->mipi.mdp_trigger = DSI_CMD_TRIGGER_NONE;
+	pinfo->mipi.dma_trigger = DSI_CMD_TRIGGER_SW;
+	/*
+	 * toshiba d2l chip does not need max_pkt_size dcs cmd
+	 * client reply len is directly configure through
+	 * RDPKTLN register (0x0404)
+	 */
+	pinfo->mipi.no_max_pkt_size = 1;
+	pinfo->mipi.force_clk_lane_hs = 1;
+
+	pinfo->is_3d_panel = FB_TYPE_3D_PANEL;
+
+	ret = mipi_tc358764_dsi2lvds_register(pinfo, MIPI_DSI_PRIM, 1);
+	if (ret)
+		pr_err("%s: failed to register device!\n", __func__);
+
+	return ret;
+}
+
+module_init(mipi_chimei_wuxga_init);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Chimei WUXGA LVDS Panel driver");
+MODULE_AUTHOR("Amir Samuelov <amirs@codeaurora.org>");
diff --git a/drivers/video/msm/mipi_chimei_wxga_pt.c b/drivers/video/msm/mipi_chimei_wxga_pt.c
new file mode 100644
index 0000000..1ab50b7
--- /dev/null
+++ b/drivers/video/msm/mipi_chimei_wxga_pt.c
@@ -0,0 +1,191 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*
+ * Chimei WXGA LVDS Panel driver.
+ * The panel model is N101BCG-L21.
+ *
+ * The panel interface includes:
+ * 1. LVDS input for video (clock & data).
+ * 2. few configuration	pins: Up/Down scan, Left/Right scan etc.
+ * 3. Backlight LED control.
+ * 4. I2C interface for EEPROM access.
+ *
+ * The Panel is *internally* controlled by Novatek NT51009 controller.
+ * However, the "3-wire" SPI interface is not exposed on the panel interface.
+ *
+ * This panel is controled via the Toshiba DSI-to-LVDS bridge.
+ *
+ */
+
+/* #define DEBUG 1 */
+
+#include "msm_fb.h"
+#include "msm_fb_panel.h"
+#include "mipi_dsi.h"
+#include "mipi_tc358764_dsi2lvds.h"
+
+#define MHZ (1000*1000)
+
+/**
+ * Panel info parameters.
+ * The panel info is passed to the mipi framebuffer driver.
+ */
+static struct msm_panel_info chimei_wxga_pinfo;
+
+/**
+ * The mipi_dsi_phy_ctrl is calculated according to the
+ * "DSI_panel_bring_up_guide_ver3.docm" using the excel sheet.
+ * Output is based on: 1366x768, RGB888, 4 lanes , 60 frames per second.
+ */
+static struct mipi_dsi_phy_ctrl dsi_video_mode_phy_db = {
+	/* DSIPHY_REGULATOR_CTRL */
+	.regulator = {0x03, 0x0a, 0x04, 0x00, 0x20}, /* common 8960 */
+	/* DSIPHY_CTRL */
+	.ctrl = {0x5f, 0x00, 0x00, 0x10}, /* common 8960 */
+	/* DSIPHY_STRENGTH_CTRL */
+	.strength = {0xff, 0x00, 0x06, 0x00}, /* common 8960 */
+	/* DSIPHY_TIMING_CTRL */
+	.timing = { 0xB6, 0x8D, 0x1E, /* panel specific */
+	0, /* DSIPHY_TIMING_CTRL_3 = 0 */
+	0x21, 0x95, 0x21, 0x8F, 0x21, 0x03, 0x04},  /* panel specific */
+
+	/* DSIPHY_PLL_CTRL */
+	.pll = { 0x00, /* common 8960 */
+	/* VCO */
+	0xC6, 0x01, 0x19, /* panel specific */
+	0x00, 0x50, 0x48, 0x63,
+	0x77, 0x88, 0x99, /* Auto update by dsi-mipi driver */
+	0x00, 0x14, 0x03, 0x00, 0x02, /* common 8960 */
+	0x00, 0x20, 0x00, 0x01 }, /* common 8960 */
+};
+
+/**
+ * Module init.
+ *
+ * Register the panel-info.
+ *
+ * Some parameters are from the panel datasheet
+ * and other are *calculated* according to the
+ * "DSI_panel_bring_up_guide_ver3.docm".
+ *
+ * @return int
+ */
+static int __init mipi_chimei_wxga_init(void)
+{
+	int ret;
+	struct msm_panel_info *pinfo = &chimei_wxga_pinfo;
+
+	if (msm_fb_detect_client("mipi_video_chimei_wxga"))
+		return 0;
+
+	pr_debug("mipi-dsi chimei wxga (1366x768) driver ver 1.0.\n");
+	/* Landscape */
+	pinfo->xres = 1366;
+	pinfo->yres = 768;
+	pinfo->type =  MIPI_VIDEO_PANEL;
+	pinfo->pdest = DISPLAY_1; /* Primary Display */
+	pinfo->wait_cycle = 0;
+	pinfo->bpp = 24; /* RGB888 = 24 bits-per-pixel */
+	pinfo->fb_num = 2; /* using two frame buffers */
+
+	/* bitclk */
+	pinfo->clk_rate = 473400000; /* 473.4 MHZ Calculated */
+
+	/*
+	 * this panel is operated by DE,
+	 * vsycn and hsync are ignored
+	 */
+
+	pinfo->lcdc.h_front_porch = 96+2;/* thfp */
+	pinfo->lcdc.h_back_porch = 88;	/* thb */
+	pinfo->lcdc.h_pulse_width = 40;	/* thpw */
+
+	pinfo->lcdc.v_front_porch = 15;	/* tvfp */
+	pinfo->lcdc.v_back_porch = 23;	/* tvb */
+	pinfo->lcdc.v_pulse_width = 20;	/* tvpw */
+
+	pinfo->lcdc.border_clr = 0;		/* black */
+	pinfo->lcdc.underflow_clr = 0xff;	/* blue */
+
+	pinfo->lcdc.hsync_skew = 0;
+
+	/* Backlight levels - controled via PMIC pwm gpio */
+	pinfo->bl_max = PWM_LEVEL;
+	pinfo->bl_min = 1;
+
+	/* mipi - general */
+	pinfo->mipi.vc = 0; /* virtual channel */
+	pinfo->mipi.rgb_swap = DSI_RGB_SWAP_RGB;
+	pinfo->mipi.tx_eot_append = true;
+	pinfo->mipi.t_clk_post = 34;		/* Calculated */
+	pinfo->mipi.t_clk_pre = 64;		/* Calculated */
+
+	pinfo->mipi.dsi_phy_db = &dsi_video_mode_phy_db;
+
+	/* Four lanes are recomended for 1366x768 at 60 frames per second */
+	pinfo->mipi.frame_rate = 60; /* 60 frames per second */
+	pinfo->mipi.data_lane0 = true;
+	pinfo->mipi.data_lane1 = true;
+	pinfo->mipi.data_lane2 = true;
+	pinfo->mipi.data_lane3 = true;
+
+	pinfo->mipi.mode = DSI_VIDEO_MODE;
+	/*
+	 * Note: The CMI panel input is RGB888,
+	 * thus the DSI-to-LVDS bridge output is RGB888.
+	 * This parameter selects the DSI-Core output to the bridge.
+	 */
+	pinfo->mipi.dst_format = DSI_VIDEO_DST_FORMAT_RGB888;
+
+	/* mipi - video mode */
+	pinfo->mipi.traffic_mode = DSI_NON_BURST_SYNCH_EVENT;
+	pinfo->mipi.pulse_mode_hsa_he = false; /* sync mode */
+
+	pinfo->mipi.hfp_power_stop = false;
+	pinfo->mipi.hbp_power_stop = false;
+	pinfo->mipi.hsa_power_stop = false;
+	pinfo->mipi.eof_bllp_power_stop = false;
+	pinfo->mipi.bllp_power_stop = false;
+
+	/* mipi - command mode */
+	pinfo->mipi.te_sel = 1; /* TE from vsycn gpio */
+	pinfo->mipi.interleave_max = 1;
+	/* The bridge supports only Generic Read/Write commands */
+	pinfo->mipi.insert_dcs_cmd = false;
+	pinfo->mipi.wr_mem_continue = 0;
+	pinfo->mipi.wr_mem_start = 0;
+	pinfo->mipi.stream = false; /* dma_p */
+	pinfo->mipi.mdp_trigger = DSI_CMD_TRIGGER_NONE;
+	pinfo->mipi.dma_trigger = DSI_CMD_TRIGGER_SW;
+	/*
+	 * toshiba d2l chip does not need max_pkt_szie dcs cmd
+	 * client reply len is directly configure through
+	 * RDPKTLN register (0x0404)
+	 */
+	pinfo->mipi.no_max_pkt_size = 1;
+	pinfo->mipi.force_clk_lane_hs = 1;
+
+	ret = mipi_tc358764_dsi2lvds_register(pinfo, MIPI_DSI_PRIM,
+					      MIPI_DSI_PANEL_WXGA);
+	if (ret)
+		pr_err("%s: failed to register device!\n", __func__);
+
+	return ret;
+}
+
+module_init(mipi_chimei_wxga_init);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Chimei WXGA LVDS Panel driver");
+MODULE_AUTHOR("Amir Samuelov <amirs@codeaurora.org>");
diff --git a/drivers/video/msm/mipi_dsi.c b/drivers/video/msm/mipi_dsi.c
new file mode 100644
index 0000000..7564016
--- /dev/null
+++ b/drivers/video/msm/mipi_dsi.c
@@ -0,0 +1,637 @@
+/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/semaphore.h>
+#include <linux/uaccess.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include <mach/clk.h>
+
+#include "msm_fb.h"
+#include "mipi_dsi.h"
+#include "mdp.h"
+#include "mdp4.h"
+
+u32 dsi_irq;
+
+static boolean tlmm_settings = FALSE;
+
+static int mipi_dsi_probe(struct platform_device *pdev);
+static int mipi_dsi_remove(struct platform_device *pdev);
+
+static int mipi_dsi_off(struct platform_device *pdev);
+static int mipi_dsi_on(struct platform_device *pdev);
+
+static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST];
+static int pdev_list_cnt;
+static struct mipi_dsi_platform_data *mipi_dsi_pdata;
+
+static int vsync_gpio = -1;
+
+static struct platform_driver mipi_dsi_driver = {
+	.probe = mipi_dsi_probe,
+	.remove = mipi_dsi_remove,
+	.shutdown = NULL,
+	.driver = {
+		   .name = "mipi_dsi",
+		   },
+};
+
+struct device dsi_dev;
+
+static int mipi_dsi_off(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct msm_fb_data_type *mfd;
+	struct msm_panel_info *pinfo;
+
+	mfd = platform_get_drvdata(pdev);
+	pinfo = &mfd->panel_info;
+
+	if (mdp_rev >= MDP_REV_41)
+		mutex_lock(&mfd->dma->ov_mutex);
+	else
+		down(&mfd->dma->mutex);
+
+	mdp4_overlay_dsi_state_set(ST_DSI_SUSPEND);
+
+	/*
+	 * Description: dsi clock is need to perform shutdown.
+	 * mdp4_dsi_cmd_dma_busy_wait() will enable dsi clock if disabled.
+	 * also, wait until dma (overlay and dmap) finish.
+	 */
+	if (mfd->panel_info.type == MIPI_CMD_PANEL) {
+		if (mdp_rev >= MDP_REV_41) {
+			mdp4_dsi_cmd_dma_busy_wait(mfd);
+			mdp4_dsi_blt_dmap_busy_wait(mfd);
+			mipi_dsi_mdp_busy_wait(mfd);
+		} else {
+			mdp3_dsi_cmd_dma_busy_wait(mfd);
+		}
+	} else {
+		/* video mode, wait until fifo cleaned */
+		mipi_dsi_controller_cfg(0);
+	}
+
+	/*
+	 * Desctiption: change to DSI_CMD_MODE since it needed to
+	 * tx DCS dsiplay off comamnd to panel
+	 */
+	mipi_dsi_op_mode_config(DSI_CMD_MODE);
+
+	if (mfd->panel_info.type == MIPI_CMD_PANEL) {
+		if (pinfo->lcd.vsync_enable) {
+			if (pinfo->lcd.hw_vsync_mode && vsync_gpio >= 0) {
+				if (MDP_REV_303 != mdp_rev)
+					gpio_free(vsync_gpio);
+			}
+			mipi_dsi_set_tear_off(mfd);
+		}
+	}
+
+	ret = panel_next_off(pdev);
+
+#ifdef CONFIG_MSM_BUS_SCALING
+	mdp_bus_scale_update_request(0);
+#endif
+
+	local_bh_disable();
+	mipi_dsi_clk_disable();
+	local_bh_enable();
+
+	/* disbale dsi engine */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x0000, 0);
+
+	mipi_dsi_phy_ctrl(0);
+
+
+	local_bh_disable();
+	mipi_dsi_ahb_ctrl(0);
+	local_bh_enable();
+
+	mipi_dsi_unprepare_clocks();
+	if (mipi_dsi_pdata && mipi_dsi_pdata->dsi_power_save)
+		mipi_dsi_pdata->dsi_power_save(0);
+
+	if (mdp_rev >= MDP_REV_41)
+		mutex_unlock(&mfd->dma->ov_mutex);
+	else
+		up(&mfd->dma->mutex);
+
+	pr_debug("%s-:\n", __func__);
+
+	return ret;
+}
+
+static int mipi_dsi_on(struct platform_device *pdev)
+{
+	int ret = 0;
+	u32 clk_rate;
+	struct msm_fb_data_type *mfd;
+	struct fb_info *fbi;
+	struct fb_var_screeninfo *var;
+	struct msm_panel_info *pinfo;
+	struct mipi_panel_info *mipi;
+	u32 hbp, hfp, vbp, vfp, hspw, vspw, width, height;
+	u32 ystride, bpp, data;
+	u32 dummy_xres, dummy_yres;
+	int target_type = 0;
+
+	mfd = platform_get_drvdata(pdev);
+	fbi = mfd->fbi;
+	var = &fbi->var;
+	pinfo = &mfd->panel_info;
+
+	if (mipi_dsi_pdata && mipi_dsi_pdata->dsi_power_save)
+		mipi_dsi_pdata->dsi_power_save(1);
+
+	cont_splash_clk_ctrl(0);
+	mipi_dsi_prepare_clocks();
+
+	local_bh_disable();
+	mipi_dsi_ahb_ctrl(1);
+	local_bh_enable();
+
+	clk_rate = mfd->fbi->var.pixclock;
+	clk_rate = min(clk_rate, mfd->panel_info.clk_max);
+
+	mipi_dsi_phy_ctrl(1);
+
+	if (mdp_rev == MDP_REV_42 && mipi_dsi_pdata)
+		target_type = mipi_dsi_pdata->target_type;
+
+	mipi_dsi_phy_init(0, &(mfd->panel_info), target_type);
+
+	local_bh_disable();
+	mipi_dsi_clk_enable();
+	local_bh_enable();
+
+	MIPI_OUTP(MIPI_DSI_BASE + 0x114, 1);
+	MIPI_OUTP(MIPI_DSI_BASE + 0x114, 0);
+
+	hbp = var->left_margin;
+	hfp = var->right_margin;
+	vbp = var->upper_margin;
+	vfp = var->lower_margin;
+	hspw = var->hsync_len;
+	vspw = var->vsync_len;
+	width = mfd->panel_info.xres;
+	height = mfd->panel_info.yres;
+
+	mipi  = &mfd->panel_info.mipi;
+	if (mfd->panel_info.type == MIPI_VIDEO_PANEL) {
+		dummy_xres = mfd->panel_info.lcdc.xres_pad;
+		dummy_yres = mfd->panel_info.lcdc.yres_pad;
+
+		if (mdp_rev >= MDP_REV_41) {
+			MIPI_OUTP(MIPI_DSI_BASE + 0x20,
+				((hspw + hbp + width + dummy_xres) << 16 |
+				(hspw + hbp)));
+			MIPI_OUTP(MIPI_DSI_BASE + 0x24,
+				((vspw + vbp + height + dummy_yres) << 16 |
+				(vspw + vbp)));
+			MIPI_OUTP(MIPI_DSI_BASE + 0x28,
+				(vspw + vbp + height + dummy_yres +
+					vfp - 1) << 16 | (hspw + hbp +
+					width + dummy_xres + hfp - 1));
+		} else {
+			/* DSI_LAN_SWAP_CTRL */
+			MIPI_OUTP(MIPI_DSI_BASE + 0x00ac, mipi->dlane_swap);
+
+			MIPI_OUTP(MIPI_DSI_BASE + 0x20,
+				((hbp + width + dummy_xres) << 16 | (hbp)));
+			MIPI_OUTP(MIPI_DSI_BASE + 0x24,
+				((vbp + height + dummy_yres) << 16 | (vbp)));
+			MIPI_OUTP(MIPI_DSI_BASE + 0x28,
+				(vbp + height + dummy_yres + vfp) << 16 |
+					(hbp + width + dummy_xres + hfp));
+		}
+
+		MIPI_OUTP(MIPI_DSI_BASE + 0x2c, (hspw << 16));
+		MIPI_OUTP(MIPI_DSI_BASE + 0x30, 0);
+		MIPI_OUTP(MIPI_DSI_BASE + 0x34, (vspw << 16));
+
+	} else {		/* command mode */
+		if (mipi->dst_format == DSI_CMD_DST_FORMAT_RGB888)
+			bpp = 3;
+		else if (mipi->dst_format == DSI_CMD_DST_FORMAT_RGB666)
+			bpp = 3;
+		else if (mipi->dst_format == DSI_CMD_DST_FORMAT_RGB565)
+			bpp = 2;
+		else
+			bpp = 3;	/* Default format set to RGB888 */
+
+		ystride = width * bpp + 1;
+
+		/* DSI_COMMAND_MODE_MDP_STREAM_CTRL */
+		data = (ystride << 16) | (mipi->vc << 8) | DTYPE_DCS_LWRITE;
+		MIPI_OUTP(MIPI_DSI_BASE + 0x5c, data);
+		MIPI_OUTP(MIPI_DSI_BASE + 0x54, data);
+
+		/* DSI_COMMAND_MODE_MDP_STREAM_TOTAL */
+		data = height << 16 | width;
+		MIPI_OUTP(MIPI_DSI_BASE + 0x60, data);
+		MIPI_OUTP(MIPI_DSI_BASE + 0x58, data);
+	}
+
+	mipi_dsi_host_init(mipi);
+
+	if (mipi->force_clk_lane_hs) {
+		u32 tmp;
+
+		tmp = MIPI_INP(MIPI_DSI_BASE + 0xA8);
+		tmp |= (1<<28);
+		MIPI_OUTP(MIPI_DSI_BASE + 0xA8, tmp);
+		wmb();
+	}
+
+	if (mdp_rev >= MDP_REV_41)
+		mutex_lock(&mfd->dma->ov_mutex);
+	else
+		down(&mfd->dma->mutex);
+
+	ret = panel_next_on(pdev);
+
+	mipi_dsi_op_mode_config(mipi->mode);
+
+	if (mfd->panel_info.type == MIPI_CMD_PANEL) {
+		if (pinfo->lcd.vsync_enable) {
+			if (pinfo->lcd.hw_vsync_mode && vsync_gpio >= 0) {
+				if (mdp_rev >= MDP_REV_41) {
+					if (gpio_request(vsync_gpio,
+						"MDP_VSYNC") == 0)
+						gpio_direction_input(
+							vsync_gpio);
+					else
+						pr_err("%s: unable to \
+							request gpio=%d\n",
+							__func__, vsync_gpio);
+				} else if (mdp_rev == MDP_REV_303) {
+					if (!tlmm_settings && gpio_request(
+						vsync_gpio, "MDP_VSYNC") == 0) {
+						ret = gpio_tlmm_config(
+							GPIO_CFG(
+							vsync_gpio, 1,
+							GPIO_CFG_INPUT,
+							GPIO_CFG_PULL_DOWN,
+							GPIO_CFG_2MA),
+							GPIO_CFG_ENABLE);
+
+						if (ret) {
+							pr_err(
+							"%s: unable to config \
+							tlmm = %d\n",
+							__func__, vsync_gpio);
+						}
+						tlmm_settings = TRUE;
+
+						gpio_direction_input(
+							vsync_gpio);
+					} else {
+						if (!tlmm_settings) {
+							pr_err(
+							"%s: unable to request \
+							gpio=%d\n",
+							__func__, vsync_gpio);
+						}
+					}
+				}
+			}
+			mipi_dsi_set_tear_on(mfd);
+		}
+	}
+
+#ifdef CONFIG_MSM_BUS_SCALING
+	mdp_bus_scale_update_request(2);
+#endif
+
+	mdp4_overlay_dsi_state_set(ST_DSI_RESUME);
+
+	if (mdp_rev >= MDP_REV_41)
+		mutex_unlock(&mfd->dma->ov_mutex);
+	else
+		up(&mfd->dma->mutex);
+
+	pr_debug("%s-:\n", __func__);
+
+	return ret;
+}
+
+
+static int mipi_dsi_resource_initialized;
+
+static int mipi_dsi_probe(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+	struct fb_info *fbi;
+	struct msm_panel_info *pinfo;
+	struct mipi_panel_info *mipi;
+	struct platform_device *mdp_dev = NULL;
+	struct msm_fb_panel_data *pdata = NULL;
+	int rc;
+	uint8 lanes = 0, bpp;
+	uint32 h_period, v_period, dsi_pclk_rate;
+
+	resource_size_t size ;
+
+	if ((pdev->id == 1) && (pdev->num_resources >= 0)) {
+		mipi_dsi_pdata = pdev->dev.platform_data;
+
+		size =  resource_size(&pdev->resource[0]);
+		mipi_dsi_base =  ioremap(pdev->resource[0].start, size);
+
+		MSM_FB_INFO("mipi_dsi base phy_addr = 0x%x virt = 0x%x\n",
+				pdev->resource[0].start, (int) mipi_dsi_base);
+
+		if (!mipi_dsi_base)
+			return -ENOMEM;
+
+		if (mdp_rev >= MDP_REV_41) {
+			mmss_sfpb_base =  ioremap(MMSS_SFPB_BASE_PHY, 0x100);
+			MSM_FB_INFO("mmss_sfpb  base phy_addr = 0x%x,"
+				"virt = 0x%x\n", MMSS_SFPB_BASE_PHY,
+				(int) mmss_sfpb_base);
+
+			if (!mmss_sfpb_base)
+				return -ENOMEM;
+		}
+
+		dsi_irq = platform_get_irq(pdev, 0);
+		if (dsi_irq < 0) {
+			pr_err("mipi_dsi: can not get mdp irq\n");
+			return -ENOMEM;
+		}
+
+		rc = request_irq(dsi_irq, mipi_dsi_isr, IRQF_DISABLED,
+						"MIPI_DSI", 0);
+		if (rc) {
+			pr_err("mipi_dsi_host request_irq() failed!\n");
+			return rc;
+		}
+
+		disable_irq(dsi_irq);
+
+		if (mdp_rev == MDP_REV_42 && mipi_dsi_pdata &&
+			mipi_dsi_pdata->target_type == 1) {
+			/* Target type is 1 for device with (De)serializer
+			 * 0x4f00000 is the base for TV Encoder.
+			 * Unused Offset 0x1000 is used for
+			 * (de)serializer on emulation platform
+			 */
+			periph_base = ioremap(MMSS_SERDES_BASE_PHY, 0x100);
+
+			if (periph_base) {
+				pr_debug("periph_base %p\n", periph_base);
+				writel(0x4, periph_base + 0x28);
+				writel(0xc, periph_base + 0x28);
+			} else {
+				pr_err("periph_base is NULL\n");
+				free_irq(dsi_irq, 0);
+				return -ENOMEM;
+			}
+		}
+
+		if (mipi_dsi_pdata) {
+			vsync_gpio = mipi_dsi_pdata->vsync_gpio;
+			pr_debug("%s: vsync_gpio=%d\n", __func__, vsync_gpio);
+
+			if (mdp_rev == MDP_REV_303 &&
+				mipi_dsi_pdata->dsi_client_reset) {
+				if (mipi_dsi_pdata->dsi_client_reset())
+					pr_err("%s: DSI Client Reset failed!\n",
+						__func__);
+				else
+					pr_debug("%s: DSI Client Reset success\n",
+						__func__);
+			}
+		}
+
+		if (mipi_dsi_clk_init(pdev))
+			return -EPERM;
+
+		if (mipi_dsi_pdata->splash_is_enabled &&
+			!mipi_dsi_pdata->splash_is_enabled()) {
+			mipi_dsi_ahb_ctrl(1);
+			MIPI_OUTP(MIPI_DSI_BASE + 0x118, 0);
+			MIPI_OUTP(MIPI_DSI_BASE + 0x0, 0);
+			MIPI_OUTP(MIPI_DSI_BASE + 0x200, 0);
+			mipi_dsi_ahb_ctrl(0);
+		}
+		mipi_dsi_resource_initialized = 1;
+
+		return 0;
+	}
+
+	if (!mipi_dsi_resource_initialized)
+		return -EPERM;
+
+	mfd = platform_get_drvdata(pdev);
+
+	if (!mfd)
+		return -ENODEV;
+
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST)
+		return -ENOMEM;
+
+	if (!mfd->cont_splash_done)
+		cont_splash_clk_ctrl(1);
+
+	mdp_dev = platform_device_alloc("mdp", pdev->id);
+	if (!mdp_dev)
+		return -ENOMEM;
+
+	/*
+	 * link to the latest pdev
+	 */
+	mfd->pdev = mdp_dev;
+
+	/*
+	 * alloc panel device data
+	 */
+	if (platform_device_add_data
+	    (mdp_dev, pdev->dev.platform_data,
+	     sizeof(struct msm_fb_panel_data))) {
+		pr_err("mipi_dsi_probe: platform_device_add_data failed!\n");
+		platform_device_put(mdp_dev);
+		return -ENOMEM;
+	}
+	/*
+	 * data chain
+	 */
+	pdata = mdp_dev->dev.platform_data;
+	pdata->on = mipi_dsi_on;
+	pdata->off = mipi_dsi_off;
+	pdata->next = pdev;
+
+	/*
+	 * get/set panel specific fb info
+	 */
+	mfd->panel_info = pdata->panel_info;
+	pinfo = &mfd->panel_info;
+
+	if (mfd->panel_info.type == MIPI_VIDEO_PANEL)
+		mfd->dest = DISPLAY_LCDC;
+	else
+		mfd->dest = DISPLAY_LCD;
+
+	if (mdp_rev == MDP_REV_303 &&
+		mipi_dsi_pdata->get_lane_config) {
+		if (mipi_dsi_pdata->get_lane_config() != 2) {
+			pr_info("Changing to DSI Single Mode Configuration\n");
+#ifdef CONFIG_FB_MSM_MDP303
+			update_lane_config(pinfo);
+#endif
+		}
+	}
+
+	if (mfd->index == 0)
+		mfd->fb_imgType = MSMFB_DEFAULT_TYPE;
+	else
+		mfd->fb_imgType = MDP_RGB_565;
+
+	fbi = mfd->fbi;
+	fbi->var.pixclock = mfd->panel_info.clk_rate;
+	fbi->var.left_margin = mfd->panel_info.lcdc.h_back_porch;
+	fbi->var.right_margin = mfd->panel_info.lcdc.h_front_porch;
+	fbi->var.upper_margin = mfd->panel_info.lcdc.v_back_porch;
+	fbi->var.lower_margin = mfd->panel_info.lcdc.v_front_porch;
+	fbi->var.hsync_len = mfd->panel_info.lcdc.h_pulse_width;
+	fbi->var.vsync_len = mfd->panel_info.lcdc.v_pulse_width;
+
+	h_period = ((mfd->panel_info.lcdc.h_pulse_width)
+			+ (mfd->panel_info.lcdc.h_back_porch)
+			+ (mfd->panel_info.xres)
+			+ (mfd->panel_info.lcdc.h_front_porch));
+
+	v_period = ((mfd->panel_info.lcdc.v_pulse_width)
+			+ (mfd->panel_info.lcdc.v_back_porch)
+			+ (mfd->panel_info.yres)
+			+ (mfd->panel_info.lcdc.v_front_porch));
+
+	mipi  = &mfd->panel_info.mipi;
+
+	if (mipi->data_lane3)
+		lanes += 1;
+	if (mipi->data_lane2)
+		lanes += 1;
+	if (mipi->data_lane1)
+		lanes += 1;
+	if (mipi->data_lane0)
+		lanes += 1;
+
+	if ((mipi->dst_format == DSI_CMD_DST_FORMAT_RGB888)
+	    || (mipi->dst_format == DSI_VIDEO_DST_FORMAT_RGB888)
+	    || (mipi->dst_format == DSI_VIDEO_DST_FORMAT_RGB666_LOOSE))
+		bpp = 3;
+	else if ((mipi->dst_format == DSI_CMD_DST_FORMAT_RGB565)
+		 || (mipi->dst_format == DSI_VIDEO_DST_FORMAT_RGB565))
+		bpp = 2;
+	else
+		bpp = 3;		/* Default format set to RGB888 */
+
+	if (mfd->panel_info.type == MIPI_VIDEO_PANEL &&
+		!mfd->panel_info.clk_rate) {
+		h_period += mfd->panel_info.lcdc.xres_pad;
+		v_period += mfd->panel_info.lcdc.yres_pad;
+
+		if (lanes > 0) {
+			mfd->panel_info.clk_rate =
+			((h_period * v_period * (mipi->frame_rate) * bpp * 8)
+			   / lanes);
+		} else {
+			pr_err("%s: forcing mipi_dsi lanes to 1\n", __func__);
+			mfd->panel_info.clk_rate =
+				(h_period * v_period
+					 * (mipi->frame_rate) * bpp * 8);
+		}
+	}
+	pll_divider_config.clk_rate = mfd->panel_info.clk_rate;
+
+	rc = mipi_dsi_clk_div_config(bpp, lanes, &dsi_pclk_rate);
+	if (rc)
+		goto mipi_dsi_probe_err;
+
+	if ((dsi_pclk_rate < 3300000) || (dsi_pclk_rate > 103300000))
+		dsi_pclk_rate = 35000000;
+	mipi->dsi_pclk_rate = dsi_pclk_rate;
+
+	/*
+	 * set driver data
+	 */
+	platform_set_drvdata(mdp_dev, mfd);
+
+	/*
+	 * register in mdp driver
+	 */
+	rc = platform_device_add(mdp_dev);
+	if (rc)
+		goto mipi_dsi_probe_err;
+
+	pdev_list[pdev_list_cnt++] = pdev;
+
+return 0;
+
+mipi_dsi_probe_err:
+	platform_device_put(mdp_dev);
+	return rc;
+}
+
+static int mipi_dsi_remove(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+
+	mfd = platform_get_drvdata(pdev);
+	iounmap(mipi_dsi_base);
+	return 0;
+}
+
+static int mipi_dsi_register_driver(void)
+{
+	return platform_driver_register(&mipi_dsi_driver);
+}
+
+static int __init mipi_dsi_driver_init(void)
+{
+	int ret;
+
+	mipi_dsi_init();
+
+	ret = mipi_dsi_register_driver();
+
+	device_initialize(&dsi_dev);
+
+	if (ret) {
+		pr_err("mipi_dsi_register_driver() failed!\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+module_init(mipi_dsi_driver_init);
diff --git a/drivers/video/msm/mipi_dsi.h b/drivers/video/msm/mipi_dsi.h
new file mode 100644
index 0000000..d54c5b5
--- /dev/null
+++ b/drivers/video/msm/mipi_dsi.h
@@ -0,0 +1,317 @@
+/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef MIPI_DSI_H
+#define MIPI_DSI_H
+
+#include <mach/scm-io.h>
+#include <linux/list.h>
+
+#ifdef BIT
+#undef BIT
+#endif
+
+#define BIT(x)  (1<<(x))
+
+#define MMSS_CC_BASE_PHY 0x04000000	/* mmss clcok control */
+#define MMSS_SFPB_BASE_PHY 0x05700000	/* mmss SFPB CFG */
+#define MMSS_SERDES_BASE_PHY 0x04f01000 /* mmss (De)Serializer CFG */
+
+#define MIPI_DSI_BASE mipi_dsi_base
+
+#define MIPI_OUTP(addr, data) writel((data), (addr))
+#define MIPI_INP(addr) readl(addr)
+
+#ifdef CONFIG_MSM_SECURE_IO
+#define MIPI_OUTP_SECURE(addr, data) secure_writel((data), (addr))
+#define MIPI_INP_SECURE(addr) secure_readl(addr)
+#else
+#define MIPI_OUTP_SECURE(addr, data) writel((data), (addr))
+#define MIPI_INP_SECURE(addr) readl(addr)
+#endif
+
+#define MIPI_DSI_PRIM 1
+#define MIPI_DSI_SECD 2
+
+#define MIPI_DSI_PANEL_VGA	0
+#define MIPI_DSI_PANEL_WVGA	1
+#define MIPI_DSI_PANEL_WVGA_PT	2
+#define MIPI_DSI_PANEL_FWVGA_PT	3
+#define MIPI_DSI_PANEL_WSVGA_PT	4
+#define MIPI_DSI_PANEL_QHD_PT 5
+#define MIPI_DSI_PANEL_WXGA	6
+#define MIPI_DSI_PANEL_WUXGA	7
+#define MIPI_DSI_PANEL_720P_PT	8
+#define DSI_PANEL_MAX	8
+
+enum {		/* mipi dsi panel */
+	DSI_VIDEO_MODE,
+	DSI_CMD_MODE,
+};
+
+enum {
+	ST_DSI_CLK_OFF,
+	ST_DSI_SUSPEND,
+	ST_DSI_RESUME,
+	ST_DSI_PLAYING,
+	ST_DSI_NUM
+};
+
+enum {
+	EV_DSI_UPDATE,
+	EV_DSI_DONE,
+	EV_DSI_TOUT,
+	EV_DSI_NUM
+};
+
+enum {
+	LANDSCAPE = 1,
+	PORTRAIT = 2,
+};
+
+enum dsi_trigger_type {
+	DSI_CMD_MODE_DMA,
+	DSI_CMD_MODE_MDP,
+};
+
+#define DSI_NON_BURST_SYNCH_PULSE	0
+#define DSI_NON_BURST_SYNCH_EVENT	1
+#define DSI_BURST_MODE			2
+
+
+#define DSI_RGB_SWAP_RGB	0
+#define DSI_RGB_SWAP_RBG	1
+#define DSI_RGB_SWAP_BGR	2
+#define DSI_RGB_SWAP_BRG	3
+#define DSI_RGB_SWAP_GRB	4
+#define DSI_RGB_SWAP_GBR	5
+
+#define DSI_VIDEO_DST_FORMAT_RGB565		0
+#define DSI_VIDEO_DST_FORMAT_RGB666		1
+#define DSI_VIDEO_DST_FORMAT_RGB666_LOOSE	2
+#define DSI_VIDEO_DST_FORMAT_RGB888		3
+
+#define DSI_CMD_DST_FORMAT_RGB111	0
+#define DSI_CMD_DST_FORMAT_RGB332	3
+#define DSI_CMD_DST_FORMAT_RGB444	4
+#define DSI_CMD_DST_FORMAT_RGB565	6
+#define DSI_CMD_DST_FORMAT_RGB666	7
+#define DSI_CMD_DST_FORMAT_RGB888	8
+
+#define DSI_INTR_ERROR_MASK		BIT(25)
+#define DSI_INTR_ERROR			BIT(24)
+#define DSI_INTR_VIDEO_DONE_MASK	BIT(17)
+#define DSI_INTR_VIDEO_DONE		BIT(16)
+#define DSI_INTR_CMD_MDP_DONE_MASK	BIT(9)
+#define DSI_INTR_CMD_MDP_DONE		BIT(8)
+#define DSI_INTR_CMD_DMA_DONE_MASK	BIT(1)
+#define DSI_INTR_CMD_DMA_DONE		BIT(0)
+
+#define DSI_CMD_TRIGGER_NONE		0x0	/* mdp trigger */
+#define DSI_CMD_TRIGGER_TE		0x02
+#define DSI_CMD_TRIGGER_SW		0x04
+#define DSI_CMD_TRIGGER_SW_SEOF		0x05	/* cmd dma only */
+#define DSI_CMD_TRIGGER_SW_TE		0x06
+
+extern struct device dsi_dev;
+extern int mipi_dsi_clk_on;
+extern u32 dsi_irq;
+
+extern void  __iomem *periph_base;
+extern char *mmss_cc_base;	/* mutimedia sub system clock control */
+extern char *mmss_sfpb_base;	/* mutimedia sub system sfpb */
+
+struct dsiphy_pll_divider_config {
+	u32 clk_rate;
+	u32 fb_divider;
+	u32 ref_divider_ratio;
+	u32 bit_clk_divider;	/* oCLK1 */
+	u32 byte_clk_divider;	/* oCLK2 */
+	u32 dsi_clk_divider;	/* oCLK3 */
+};
+
+extern struct dsiphy_pll_divider_config pll_divider_config;
+
+struct dsi_clk_mnd_table {
+	uint8 lanes;
+	uint8 bpp;
+	uint8 dsiclk_div;
+	uint8 dsiclk_m;
+	uint8 dsiclk_n;
+	uint8 dsiclk_d;
+	uint8 pclk_m;
+	uint8 pclk_n;
+	uint8 pclk_d;
+};
+
+static const struct dsi_clk_mnd_table mnd_table[] = {
+	{ 1, 2, 8, 1, 1, 0, 1,  2, 1},
+	{ 1, 3, 8, 1, 1, 0, 1,  3, 2},
+	{ 2, 2, 4, 1, 1, 0, 1,  2, 1},
+	{ 2, 3, 4, 1, 1, 0, 1,  3, 2},
+	{ 3, 2, 1, 3, 8, 4, 3, 16, 8},
+	{ 3, 3, 1, 3, 8, 4, 1,  8, 4},
+	{ 4, 2, 2, 1, 1, 0, 1,  2, 1},
+	{ 4, 3, 2, 1, 1, 0, 1,  3, 2},
+};
+
+struct dsi_clk_desc {
+	uint32 src;
+	uint32 m;
+	uint32 n;
+	uint32 d;
+	uint32 mnd_mode;
+	uint32 pre_div_func;
+};
+
+#define DSI_HOST_HDR_SIZE	4
+#define DSI_HDR_LAST		BIT(31)
+#define DSI_HDR_LONG_PKT	BIT(30)
+#define DSI_HDR_BTA		BIT(29)
+#define DSI_HDR_VC(vc)		(((vc) & 0x03) << 22)
+#define DSI_HDR_DTYPE(dtype)	(((dtype) & 0x03f) << 16)
+#define DSI_HDR_DATA2(data)	(((data) & 0x0ff) << 8)
+#define DSI_HDR_DATA1(data)	((data) & 0x0ff)
+#define DSI_HDR_WC(wc)		((wc) & 0x0ffff)
+
+#define DSI_BUF_SIZE	1024
+#define MIPI_DSI_MRPS	0x04  /* Maximum Return Packet Size */
+
+#define MIPI_DSI_LEN 8 /* 4 x 4 - 6 - 2, bytes dcs header+crc-align  */
+
+struct dsi_buf {
+	uint32 *hdr;	/* dsi host header */
+	char *start;	/* buffer start addr */
+	char *end;	/* buffer end addr */
+	int size;	/* size of buffer */
+	char *data;	/* buffer */
+	int len;	/* data length */
+	dma_addr_t dmap; /* mapped dma addr */
+};
+
+/* dcs read/write */
+#define DTYPE_DCS_WRITE		0x05	/* short write, 0 parameter */
+#define DTYPE_DCS_WRITE1	0x15	/* short write, 1 parameter */
+#define DTYPE_DCS_READ		0x06	/* read */
+#define DTYPE_DCS_LWRITE	0x39	/* long write */
+
+/* generic read/write */
+#define DTYPE_GEN_WRITE		0x03	/* short write, 0 parameter */
+#define DTYPE_GEN_WRITE1	0x13	/* short write, 1 parameter */
+#define DTYPE_GEN_WRITE2	0x23	/* short write, 2 parameter */
+#define DTYPE_GEN_LWRITE	0x29	/* long write */
+#define DTYPE_GEN_READ		0x04	/* long read, 0 parameter */
+#define DTYPE_GEN_READ1		0x14	/* long read, 1 parameter */
+#define DTYPE_GEN_READ2		0x24	/* long read, 2 parameter */
+
+#define DTYPE_TEAR_ON		0x35	/* set tear on */
+#define DTYPE_MAX_PKTSIZE	0x37	/* set max packet size */
+#define DTYPE_NULL_PKT		0x09	/* null packet, no data */
+#define DTYPE_BLANK_PKT		0x19	/* blankiing packet, no data */
+
+#define DTYPE_CM_ON		0x02	/* color mode off */
+#define DTYPE_CM_OFF		0x12	/* color mode on */
+#define DTYPE_PERIPHERAL_OFF	0x22
+#define DTYPE_PERIPHERAL_ON	0x32
+
+/*
+ * dcs response
+ */
+#define DTYPE_ACK_ERR_RESP      0x02
+#define DTYPE_EOT_RESP          0x08    /* end of tx */
+#define DTYPE_GEN_READ1_RESP    0x11    /* 1 parameter, short */
+#define DTYPE_GEN_READ2_RESP    0x12    /* 2 parameter, short */
+#define DTYPE_GEN_LREAD_RESP    0x1a
+#define DTYPE_DCS_LREAD_RESP    0x1c
+#define DTYPE_DCS_READ1_RESP    0x21    /* 1 parameter, short */
+#define DTYPE_DCS_READ2_RESP    0x22    /* 2 parameter, short */
+
+struct dsi_cmd_desc {
+	int dtype;
+	int last;
+	int vc;
+	int ack;	/* ask ACK from peripheral */
+	int wait;
+	int dlen;
+	char *payload;
+};
+
+
+typedef void (*kickoff_act)(void *);
+
+struct dsi_kickoff_action {
+	struct list_head act_entry;
+	kickoff_act	action;
+	void *data;
+};
+
+
+char *mipi_dsi_buf_reserve_hdr(struct dsi_buf *dp, int hlen);
+char *mipi_dsi_buf_init(struct dsi_buf *dp);
+void mipi_dsi_init(void);
+void mipi_dsi_lane_cfg(void);
+void mipi_dsi_bist_ctrl(void);
+int mipi_dsi_buf_alloc(struct dsi_buf *, int size);
+int mipi_dsi_cmd_dma_add(struct dsi_buf *dp, struct dsi_cmd_desc *cm);
+int mipi_dsi_cmds_tx(struct msm_fb_data_type *mfd,
+		struct dsi_buf *dp, struct dsi_cmd_desc *cmds, int cnt);
+
+int mipi_dsi_cmd_dma_tx(struct dsi_buf *dp);
+int mipi_dsi_cmd_reg_tx(uint32 data);
+int mipi_dsi_cmds_rx(struct msm_fb_data_type *mfd,
+			struct dsi_buf *tp, struct dsi_buf *rp,
+			struct dsi_cmd_desc *cmds, int len);
+int mipi_dsi_cmd_dma_rx(struct dsi_buf *tp, int rlen);
+void mipi_dsi_host_init(struct mipi_panel_info *pinfo);
+void mipi_dsi_op_mode_config(int mode);
+void mipi_dsi_cmd_mode_ctrl(int enable);
+void mdp4_dsi_cmd_trigger(void);
+void mipi_dsi_cmd_mdp_start(void);
+void mipi_dsi_cmd_bta_sw_trigger(void);
+void mipi_dsi_ack_err_status(void);
+void mipi_dsi_set_tear_on(struct msm_fb_data_type *mfd);
+void mipi_dsi_set_tear_off(struct msm_fb_data_type *mfd);
+void mipi_dsi_clk_enable(void);
+void mipi_dsi_clk_disable(void);
+void mipi_dsi_pre_kickoff_action(void);
+void mipi_dsi_post_kickoff_action(void);
+void mipi_dsi_pre_kickoff_add(struct dsi_kickoff_action *act);
+void mipi_dsi_post_kickoff_add(struct dsi_kickoff_action *act);
+void mipi_dsi_pre_kickoff_del(struct dsi_kickoff_action *act);
+void mipi_dsi_post_kickoff_del(struct dsi_kickoff_action *act);
+void mipi_dsi_controller_cfg(int enable);
+void mipi_dsi_sw_reset(void);
+void mipi_dsi_mdp_busy_wait(struct msm_fb_data_type *mfd);
+
+irqreturn_t mipi_dsi_isr(int irq, void *ptr);
+
+void mipi_set_tx_power_mode(int mode);
+void mipi_dsi_phy_ctrl(int on);
+void mipi_dsi_phy_init(int panel_ndx, struct msm_panel_info const *panel_info,
+	int target_type);
+int mipi_dsi_clk_div_config(uint8 bpp, uint8 lanes,
+			    uint32 *expected_dsi_pclk);
+int mipi_dsi_clk_init(struct platform_device *pdev);
+void mipi_dsi_clk_deinit(struct device *dev);
+void mipi_dsi_prepare_clocks(void);
+void mipi_dsi_unprepare_clocks(void);
+void mipi_dsi_ahb_ctrl(u32 enable);
+void cont_splash_clk_ctrl(int enable);
+void mipi_dsi_turn_on_clks(void);
+void mipi_dsi_turn_off_clks(void);
+
+#ifdef CONFIG_FB_MSM_MDP303
+void update_lane_config(struct msm_panel_info *pinfo);
+#endif
+
+#endif /* MIPI_DSI_H */
diff --git a/drivers/video/msm/mipi_dsi_host.c b/drivers/video/msm/mipi_dsi_host.c
new file mode 100644
index 0000000..dbf45b0
--- /dev/null
+++ b/drivers/video/msm/mipi_dsi_host.c
@@ -0,0 +1,1493 @@
+
+/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/semaphore.h>
+#include <linux/uaccess.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+#include <asm/system.h>
+#include <asm/mach-types.h>
+
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include <mach/clk.h>
+#include <mach/dma.h>
+
+#include "msm_fb.h"
+#include "mipi_dsi.h"
+#include "mdp.h"
+#include "mdp4.h"
+
+static struct completion dsi_dma_comp;
+static struct completion dsi_mdp_comp;
+static struct dsi_buf dsi_tx_buf;
+static int dsi_irq_enabled;
+static spinlock_t dsi_irq_lock;
+static spinlock_t dsi_mdp_lock;
+static int dsi_mdp_busy;
+
+static struct list_head pre_kickoff_list;
+static struct list_head post_kickoff_list;
+
+enum {
+	STAT_DSI_START,
+	STAT_DSI_ERROR,
+	STAT_DSI_CMD,
+	STAT_DSI_MDP
+};
+
+#ifdef CONFIG_FB_MSM_MDP40
+void mipi_dsi_mdp_stat_inc(int which)
+{
+	switch (which) {
+	case STAT_DSI_START:
+		mdp4_stat.dsi_mdp_start++;
+		break;
+	case STAT_DSI_ERROR:
+		mdp4_stat.intr_dsi_err++;
+		break;
+	case STAT_DSI_CMD:
+		mdp4_stat.intr_dsi_cmd++;
+		break;
+	case STAT_DSI_MDP:
+		mdp4_stat.intr_dsi_mdp++;
+		break;
+	default:
+		break;
+	}
+}
+#else
+void mipi_dsi_mdp_stat_inc(int which)
+{
+}
+#endif
+
+void mipi_dsi_init(void)
+{
+	init_completion(&dsi_dma_comp);
+	init_completion(&dsi_mdp_comp);
+	mipi_dsi_buf_alloc(&dsi_tx_buf, DSI_BUF_SIZE);
+	spin_lock_init(&dsi_irq_lock);
+	spin_lock_init(&dsi_mdp_lock);
+
+	INIT_LIST_HEAD(&pre_kickoff_list);
+	INIT_LIST_HEAD(&post_kickoff_list);
+}
+
+void mipi_dsi_enable_irq(void)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dsi_irq_lock, flags);
+	if (dsi_irq_enabled) {
+		pr_debug("%s: IRQ aleady enabled\n", __func__);
+		spin_unlock_irqrestore(&dsi_irq_lock, flags);
+		return;
+	}
+	dsi_irq_enabled = 1;
+	enable_irq(dsi_irq);
+	spin_unlock_irqrestore(&dsi_irq_lock, flags);
+}
+
+void mipi_dsi_disable_irq(void)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dsi_irq_lock, flags);
+	if (dsi_irq_enabled == 0) {
+		pr_debug("%s: IRQ already disabled\n", __func__);
+		spin_unlock_irqrestore(&dsi_irq_lock, flags);
+		return;
+	}
+
+	dsi_irq_enabled = 0;
+	disable_irq(dsi_irq);
+	spin_unlock_irqrestore(&dsi_irq_lock, flags);
+}
+
+/*
+ * mipi_dsi_disale_irq_nosync() should be called
+ * from interrupt context
+ */
+ void mipi_dsi_disable_irq_nosync(void)
+{
+	spin_lock(&dsi_irq_lock);
+	if (dsi_irq_enabled == 0) {
+		pr_debug("%s: IRQ cannot be disabled\n", __func__);
+		spin_unlock(&dsi_irq_lock);
+		return;
+	}
+
+	dsi_irq_enabled = 0;
+	disable_irq_nosync(dsi_irq);
+	spin_unlock(&dsi_irq_lock);
+}
+
+void mipi_dsi_turn_on_clks(void)
+{
+	local_bh_disable();
+	mipi_dsi_ahb_ctrl(1);
+	mipi_dsi_clk_enable();
+	local_bh_enable();
+}
+
+void mipi_dsi_turn_off_clks(void)
+{
+	local_bh_disable();
+	mipi_dsi_clk_disable();
+	mipi_dsi_ahb_ctrl(0);
+	local_bh_enable();
+}
+
+static void mipi_dsi_action(struct list_head *act_list)
+{
+	struct list_head *lp;
+	struct dsi_kickoff_action *act;
+
+	list_for_each(lp, act_list) {
+		act = list_entry(lp, struct dsi_kickoff_action, act_entry);
+		if (act && act->action)
+			act->action(act->data);
+	}
+}
+
+void mipi_dsi_pre_kickoff_action(void)
+{
+	mipi_dsi_action(&pre_kickoff_list);
+}
+
+void mipi_dsi_post_kickoff_action(void)
+{
+	mipi_dsi_action(&post_kickoff_list);
+}
+
+/*
+ * mipi_dsi_pre_kickoff_add:
+ * ov_mutex need to be acquired before call this function.
+ */
+void mipi_dsi_pre_kickoff_add(struct dsi_kickoff_action *act)
+{
+	if (act)
+		list_add_tail(&act->act_entry, &pre_kickoff_list);
+}
+
+/*
+ * mipi_dsi_pre_kickoff_add:
+ * ov_mutex need to be acquired before call this function.
+ */
+void mipi_dsi_post_kickoff_add(struct dsi_kickoff_action *act)
+{
+	if (act)
+		list_add_tail(&act->act_entry, &post_kickoff_list);
+}
+
+/*
+ * mipi_dsi_pre_kickoff_add:
+ * ov_mutex need to be acquired before call this function.
+ */
+void mipi_dsi_pre_kickoff_del(struct dsi_kickoff_action *act)
+{
+	if (!list_empty(&pre_kickoff_list) && act)
+		list_del(&act->act_entry);
+}
+
+/*
+ * mipi_dsi_pre_kickoff_add:
+ * ov_mutex need to be acquired before call this function.
+ */
+void mipi_dsi_post_kickoff_del(struct dsi_kickoff_action *act)
+{
+	if (!list_empty(&post_kickoff_list) && act)
+		list_del(&act->act_entry);
+}
+
+/*
+ * mipi dsi buf mechanism
+ */
+char *mipi_dsi_buf_reserve(struct dsi_buf *dp, int len)
+{
+	dp->data += len;
+	return dp->data;
+}
+
+char *mipi_dsi_buf_unreserve(struct dsi_buf *dp, int len)
+{
+	dp->data -= len;
+	return dp->data;
+}
+
+char *mipi_dsi_buf_push(struct dsi_buf *dp, int len)
+{
+	dp->data -= len;
+	dp->len += len;
+	return dp->data;
+}
+
+char *mipi_dsi_buf_reserve_hdr(struct dsi_buf *dp, int hlen)
+{
+	dp->hdr = (uint32 *)dp->data;
+	return mipi_dsi_buf_reserve(dp, hlen);
+}
+
+char *mipi_dsi_buf_init(struct dsi_buf *dp)
+{
+	int off;
+
+	dp->data = dp->start;
+	off = (int)dp->data;
+	/* 8 byte align */
+	off &= 0x07;
+	if (off)
+		off = 8 - off;
+	dp->data += off;
+	dp->len = 0;
+	return dp->data;
+}
+
+int mipi_dsi_buf_alloc(struct dsi_buf *dp, int size)
+{
+
+	dp->start = kmalloc(size, GFP_KERNEL);
+	if (dp->start == NULL) {
+		pr_err("%s:%u\n", __func__, __LINE__);
+		return -ENOMEM;
+	}
+
+	dp->end = dp->start + size;
+	dp->size = size;
+
+	if ((int)dp->start & 0x07)
+		pr_err("%s: buf NOT 8 bytes aligned\n", __func__);
+
+	dp->data = dp->start;
+	dp->len = 0;
+	return size;
+}
+
+/*
+ * mipi dsi gerneric long write
+ */
+static int mipi_dsi_generic_lwrite(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
+{
+	char *bp;
+	uint32 *hp;
+	int i, len;
+
+	bp = mipi_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
+
+	/* fill up payload */
+	if (cm->payload) {
+		len = cm->dlen;
+		len += 3;
+		len &= ~0x03;	/* multipled by 4 */
+		for (i = 0; i < cm->dlen; i++)
+			*bp++ = cm->payload[i];
+
+		/* append 0xff to the end */
+		for (; i < len; i++)
+			*bp++ = 0xff;
+
+		dp->len += len;
+	}
+
+	/* fill up header */
+	hp = dp->hdr;
+	*hp = 0;
+	*hp = DSI_HDR_WC(cm->dlen);
+	*hp |= DSI_HDR_VC(cm->vc);
+	*hp |= DSI_HDR_LONG_PKT;
+	*hp |= DSI_HDR_DTYPE(DTYPE_GEN_LWRITE);
+	if (cm->last)
+		*hp |= DSI_HDR_LAST;
+
+	mipi_dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
+
+	return dp->len;
+}
+
+/*
+ * mipi dsi gerneric short write with 0, 1 2 parameters
+ */
+static int mipi_dsi_generic_swrite(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
+{
+	uint32 *hp;
+	int len;
+
+	if (cm->dlen && cm->payload == 0) {
+		pr_err("%s: NO payload error\n", __func__);
+		return 0;
+	}
+
+	mipi_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
+	hp = dp->hdr;
+	*hp = 0;
+	*hp |= DSI_HDR_VC(cm->vc);
+	if (cm->last)
+		*hp |= DSI_HDR_LAST;
+
+
+	len = (cm->dlen > 2) ? 2 : cm->dlen;
+
+	if (len == 1) {
+		*hp |= DSI_HDR_DTYPE(DTYPE_GEN_WRITE1);
+		*hp |= DSI_HDR_DATA1(cm->payload[0]);
+		*hp |= DSI_HDR_DATA2(0);
+	} else if (len == 2) {
+		*hp |= DSI_HDR_DTYPE(DTYPE_GEN_WRITE2);
+		*hp |= DSI_HDR_DATA1(cm->payload[0]);
+		*hp |= DSI_HDR_DATA2(cm->payload[1]);
+	} else {
+		*hp |= DSI_HDR_DTYPE(DTYPE_GEN_WRITE);
+		*hp |= DSI_HDR_DATA1(0);
+		*hp |= DSI_HDR_DATA2(0);
+	}
+
+	mipi_dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
+
+	return dp->len;	/* 4 bytes */
+}
+
+/*
+ * mipi dsi gerneric read with 0, 1 2 parameters
+ */
+static int mipi_dsi_generic_read(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
+{
+	uint32 *hp;
+	int len;
+
+	if (cm->dlen && cm->payload == 0) {
+		pr_err("%s: NO payload error\n", __func__);
+		return 0;
+	}
+
+	mipi_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
+	hp = dp->hdr;
+	*hp = 0;
+	*hp |= DSI_HDR_VC(cm->vc);
+	*hp |= DSI_HDR_BTA;
+	if (cm->last)
+		*hp |= DSI_HDR_LAST;
+
+	len = (cm->dlen > 2) ? 2 : cm->dlen;
+
+	if (len == 1) {
+		*hp |= DSI_HDR_DTYPE(DTYPE_GEN_READ1);
+		*hp |= DSI_HDR_DATA1(cm->payload[0]);
+		*hp |= DSI_HDR_DATA2(0);
+	} else if (len == 2) {
+		*hp |= DSI_HDR_DTYPE(DTYPE_GEN_READ2);
+		*hp |= DSI_HDR_DATA1(cm->payload[0]);
+		*hp |= DSI_HDR_DATA2(cm->payload[1]);
+	} else {
+		*hp |= DSI_HDR_DTYPE(DTYPE_GEN_READ);
+		*hp |= DSI_HDR_DATA1(0);
+		*hp |= DSI_HDR_DATA2(0);
+	}
+
+	mipi_dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
+	return dp->len;	/* 4 bytes */
+}
+
+/*
+ * mipi dsi dcs long write
+ */
+static int mipi_dsi_dcs_lwrite(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
+{
+	char *bp;
+	uint32 *hp;
+	int i, len;
+
+	bp = mipi_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
+
+	/*
+	 * fill up payload
+	 * dcs command byte (first byte) followed by payload
+	 */
+	if (cm->payload) {
+		len = cm->dlen;
+		len += 3;
+		len &= ~0x03;	/* multipled by 4 */
+		for (i = 0; i < cm->dlen; i++)
+			*bp++ = cm->payload[i];
+
+		/* append 0xff to the end */
+		for (; i < len; i++)
+			*bp++ = 0xff;
+
+		dp->len += len;
+	}
+
+	/* fill up header */
+	hp = dp->hdr;
+	*hp = 0;
+	*hp = DSI_HDR_WC(cm->dlen);
+	*hp |= DSI_HDR_VC(cm->vc);
+	*hp |= DSI_HDR_LONG_PKT;
+	*hp |= DSI_HDR_DTYPE(DTYPE_DCS_LWRITE);
+	if (cm->last)
+		*hp |= DSI_HDR_LAST;
+
+	mipi_dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
+
+	return dp->len;
+}
+
+/*
+ * mipi dsi dcs short write with 0 parameters
+ */
+static int mipi_dsi_dcs_swrite(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
+{
+	uint32 *hp;
+	int len;
+
+	if (cm->payload == 0) {
+		pr_err("%s: NO payload error\n", __func__);
+		return -EINVAL;
+	}
+
+	mipi_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
+	hp = dp->hdr;
+	*hp = 0;
+	*hp |= DSI_HDR_VC(cm->vc);
+	if (cm->ack)		/* ask ACK trigger msg from peripeheral */
+		*hp |= DSI_HDR_BTA;
+	if (cm->last)
+		*hp |= DSI_HDR_LAST;
+
+	len = (cm->dlen > 1) ? 1 : cm->dlen;
+
+	*hp |= DSI_HDR_DTYPE(DTYPE_DCS_WRITE);
+	*hp |= DSI_HDR_DATA1(cm->payload[0]);	/* dcs command byte */
+	*hp |= DSI_HDR_DATA2(0);
+
+	mipi_dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
+	return dp->len;
+}
+
+/*
+ * mipi dsi dcs short write with 1 parameters
+ */
+static int mipi_dsi_dcs_swrite1(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
+{
+	uint32 *hp;
+
+	if (cm->dlen < 2 || cm->payload == 0) {
+		pr_err("%s: NO payload error\n", __func__);
+		return -EINVAL;
+	}
+
+	mipi_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
+	hp = dp->hdr;
+	*hp = 0;
+	*hp |= DSI_HDR_VC(cm->vc);
+	if (cm->ack)		/* ask ACK trigger msg from peripeheral */
+		*hp |= DSI_HDR_BTA;
+	if (cm->last)
+		*hp |= DSI_HDR_LAST;
+
+	*hp |= DSI_HDR_DTYPE(DTYPE_DCS_WRITE1);
+	*hp |= DSI_HDR_DATA1(cm->payload[0]);	/* dcs comamnd byte */
+	*hp |= DSI_HDR_DATA2(cm->payload[1]);	/* parameter */
+
+	mipi_dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
+
+	return dp->len;
+}
+/*
+ * mipi dsi dcs read with 0 parameters
+ */
+
+static int mipi_dsi_dcs_read(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
+{
+	uint32 *hp;
+
+	if (cm->payload == 0) {
+		pr_err("%s: NO payload error\n", __func__);
+		return -EINVAL;
+	}
+
+	mipi_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
+	hp = dp->hdr;
+	*hp = 0;
+	*hp |= DSI_HDR_VC(cm->vc);
+	*hp |= DSI_HDR_BTA;
+	*hp |= DSI_HDR_DTYPE(DTYPE_DCS_READ);
+	if (cm->last)
+		*hp |= DSI_HDR_LAST;
+
+	*hp |= DSI_HDR_DATA1(cm->payload[0]);	/* dcs command byte */
+	*hp |= DSI_HDR_DATA2(0);
+
+	mipi_dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
+
+	return dp->len;	/* 4 bytes */
+}
+
+static int mipi_dsi_cm_on(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
+{
+	uint32 *hp;
+
+	mipi_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
+	hp = dp->hdr;
+	*hp = 0;
+	*hp |= DSI_HDR_VC(cm->vc);
+	*hp |= DSI_HDR_DTYPE(DTYPE_CM_ON);
+	if (cm->last)
+		*hp |= DSI_HDR_LAST;
+
+	mipi_dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
+
+	return dp->len;	/* 4 bytes */
+}
+
+static int mipi_dsi_cm_off(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
+{
+	uint32 *hp;
+
+	mipi_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
+	hp = dp->hdr;
+	*hp = 0;
+	*hp |= DSI_HDR_VC(cm->vc);
+	*hp |= DSI_HDR_DTYPE(DTYPE_CM_OFF);
+	if (cm->last)
+		*hp |= DSI_HDR_LAST;
+
+	mipi_dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
+
+	return dp->len;	/* 4 bytes */
+}
+
+static int mipi_dsi_peripheral_on(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
+{
+	uint32 *hp;
+
+	mipi_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
+	hp = dp->hdr;
+	*hp = 0;
+	*hp |= DSI_HDR_VC(cm->vc);
+	*hp |= DSI_HDR_DTYPE(DTYPE_PERIPHERAL_ON);
+	if (cm->last)
+		*hp |= DSI_HDR_LAST;
+
+	mipi_dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
+
+	return dp->len;	/* 4 bytes */
+}
+
+static int mipi_dsi_peripheral_off(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
+{
+	uint32 *hp;
+
+	mipi_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
+	hp = dp->hdr;
+	*hp = 0;
+	*hp |= DSI_HDR_VC(cm->vc);
+	*hp |= DSI_HDR_DTYPE(DTYPE_PERIPHERAL_OFF);
+	if (cm->last)
+		*hp |= DSI_HDR_LAST;
+
+	mipi_dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
+
+	return dp->len;	/* 4 bytes */
+}
+
+static int mipi_dsi_set_max_pktsize(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
+{
+	uint32 *hp;
+
+	if (cm->payload == 0) {
+		pr_err("%s: NO payload error\n", __func__);
+		return 0;
+	}
+
+	mipi_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
+	hp = dp->hdr;
+	*hp = 0;
+	*hp |= DSI_HDR_VC(cm->vc);
+	*hp |= DSI_HDR_DTYPE(DTYPE_MAX_PKTSIZE);
+	if (cm->last)
+		*hp |= DSI_HDR_LAST;
+
+	*hp |= DSI_HDR_DATA1(cm->payload[0]);
+	*hp |= DSI_HDR_DATA2(cm->payload[1]);
+
+	mipi_dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
+
+	return dp->len;	/* 4 bytes */
+}
+
+static int mipi_dsi_null_pkt(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
+{
+	uint32 *hp;
+
+	mipi_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
+	hp = dp->hdr;
+	*hp = 0;
+	*hp = DSI_HDR_WC(cm->dlen);
+	*hp |= DSI_HDR_LONG_PKT;
+	*hp |= DSI_HDR_VC(cm->vc);
+	*hp |= DSI_HDR_DTYPE(DTYPE_NULL_PKT);
+	if (cm->last)
+		*hp |= DSI_HDR_LAST;
+
+	mipi_dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
+
+	return dp->len;	/* 4 bytes */
+}
+
+static int mipi_dsi_blank_pkt(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
+{
+	uint32 *hp;
+
+	mipi_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
+	hp = dp->hdr;
+	*hp = 0;
+	*hp = DSI_HDR_WC(cm->dlen);
+	*hp |= DSI_HDR_LONG_PKT;
+	*hp |= DSI_HDR_VC(cm->vc);
+	*hp |= DSI_HDR_DTYPE(DTYPE_BLANK_PKT);
+	if (cm->last)
+		*hp |= DSI_HDR_LAST;
+
+	mipi_dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
+
+	return dp->len;	/* 4 bytes */
+}
+
+/*
+ * prepare cmd buffer to be txed
+ */
+int mipi_dsi_cmd_dma_add(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
+{
+	int len = 0;
+
+	switch (cm->dtype) {
+	case DTYPE_GEN_WRITE:
+	case DTYPE_GEN_WRITE1:
+	case DTYPE_GEN_WRITE2:
+		len = mipi_dsi_generic_swrite(dp, cm);
+		break;
+	case DTYPE_GEN_LWRITE:
+		len = mipi_dsi_generic_lwrite(dp, cm);
+		break;
+	case DTYPE_GEN_READ:
+	case DTYPE_GEN_READ1:
+	case DTYPE_GEN_READ2:
+		len = mipi_dsi_generic_read(dp, cm);
+		break;
+	case DTYPE_DCS_LWRITE:
+		len = mipi_dsi_dcs_lwrite(dp, cm);
+		break;
+	case DTYPE_DCS_WRITE:
+		len = mipi_dsi_dcs_swrite(dp, cm);
+		break;
+	case DTYPE_DCS_WRITE1:
+		len = mipi_dsi_dcs_swrite1(dp, cm);
+		break;
+	case DTYPE_DCS_READ:
+		len = mipi_dsi_dcs_read(dp, cm);
+		break;
+	case DTYPE_MAX_PKTSIZE:
+		len = mipi_dsi_set_max_pktsize(dp, cm);
+		break;
+	case DTYPE_NULL_PKT:
+		len = mipi_dsi_null_pkt(dp, cm);
+		break;
+	case DTYPE_BLANK_PKT:
+		len = mipi_dsi_blank_pkt(dp, cm);
+		break;
+	case DTYPE_CM_ON:
+		len = mipi_dsi_cm_on(dp, cm);
+		break;
+	case DTYPE_CM_OFF:
+		len = mipi_dsi_cm_off(dp, cm);
+		break;
+	case DTYPE_PERIPHERAL_ON:
+		len = mipi_dsi_peripheral_on(dp, cm);
+		break;
+	case DTYPE_PERIPHERAL_OFF:
+		len = mipi_dsi_peripheral_off(dp, cm);
+		break;
+	default:
+		pr_debug("%s: dtype=%x NOT supported\n",
+					__func__, cm->dtype);
+		break;
+
+	}
+
+	return len;
+}
+
+/*
+ * mipi_dsi_short_read1_resp: 1 parameter
+ */
+static int mipi_dsi_short_read1_resp(struct dsi_buf *rp)
+{
+	/* strip out dcs type */
+	rp->data++;
+	rp->len = 1;
+	return rp->len;
+}
+
+/*
+ * mipi_dsi_short_read2_resp: 2 parameter
+ */
+static int mipi_dsi_short_read2_resp(struct dsi_buf *rp)
+{
+	/* strip out dcs type */
+	rp->data++;
+	rp->len = 2;
+	return rp->len;
+}
+
+static int mipi_dsi_long_read_resp(struct dsi_buf *rp)
+{
+	short len;
+
+	len = rp->data[2];
+	len <<= 8;
+	len |= rp->data[1];
+	/* strip out dcs header */
+	rp->data += 4;
+	rp->len -= 4;
+	/* strip out 2 bytes of checksum */
+	rp->len -= 2;
+	return len;
+}
+
+void mipi_dsi_host_init(struct mipi_panel_info *pinfo)
+{
+	uint32 dsi_ctrl, intr_ctrl;
+	uint32 data;
+
+	if (mdp_rev > MDP_REV_41 || mdp_rev == MDP_REV_303)
+		pinfo->rgb_swap = DSI_RGB_SWAP_RGB;
+	else
+		pinfo->rgb_swap = DSI_RGB_SWAP_BGR;
+
+	if (pinfo->mode == DSI_VIDEO_MODE) {
+		data = 0;
+		if (pinfo->pulse_mode_hsa_he)
+			data |= BIT(28);
+		if (pinfo->hfp_power_stop)
+			data |= BIT(24);
+		if (pinfo->hbp_power_stop)
+			data |= BIT(20);
+		if (pinfo->hsa_power_stop)
+			data |= BIT(16);
+		if (pinfo->eof_bllp_power_stop)
+			data |= BIT(15);
+		if (pinfo->bllp_power_stop)
+			data |= BIT(12);
+		data |= ((pinfo->traffic_mode & 0x03) << 8);
+		data |= ((pinfo->dst_format & 0x03) << 4); /* 2 bits */
+		data |= (pinfo->vc & 0x03);
+		MIPI_OUTP(MIPI_DSI_BASE + 0x000c, data);
+
+		data = 0;
+		data |= ((pinfo->rgb_swap & 0x07) << 12);
+		if (pinfo->b_sel)
+			data |= BIT(8);
+		if (pinfo->g_sel)
+			data |= BIT(4);
+		if (pinfo->r_sel)
+			data |= BIT(0);
+		MIPI_OUTP(MIPI_DSI_BASE + 0x001c, data);
+	} else if (pinfo->mode == DSI_CMD_MODE) {
+		data = 0;
+		data |= ((pinfo->interleave_max & 0x0f) << 20);
+		data |= ((pinfo->rgb_swap & 0x07) << 16);
+		if (pinfo->b_sel)
+			data |= BIT(12);
+		if (pinfo->g_sel)
+			data |= BIT(8);
+		if (pinfo->r_sel)
+			data |= BIT(4);
+		data |= (pinfo->dst_format & 0x0f);	/* 4 bits */
+		MIPI_OUTP(MIPI_DSI_BASE + 0x003c, data);
+
+		/* DSI_COMMAND_MODE_MDP_DCS_CMD_CTRL */
+		data = pinfo->wr_mem_continue & 0x0ff;
+		data <<= 8;
+		data |= (pinfo->wr_mem_start & 0x0ff);
+		if (pinfo->insert_dcs_cmd)
+			data |= BIT(16);
+		MIPI_OUTP(MIPI_DSI_BASE + 0x0040, data);
+	} else
+		pr_err("%s: Unknown DSI mode=%d\n", __func__, pinfo->mode);
+
+	dsi_ctrl = BIT(8) | BIT(2);	/* clock enable & cmd mode */
+	intr_ctrl = 0;
+	intr_ctrl = (DSI_INTR_CMD_DMA_DONE_MASK | DSI_INTR_CMD_MDP_DONE_MASK);
+
+	if (pinfo->crc_check)
+		dsi_ctrl |= BIT(24);
+	if (pinfo->ecc_check)
+		dsi_ctrl |= BIT(20);
+	if (pinfo->data_lane3)
+		dsi_ctrl |= BIT(7);
+	if (pinfo->data_lane2)
+		dsi_ctrl |= BIT(6);
+	if (pinfo->data_lane1)
+		dsi_ctrl |= BIT(5);
+	if (pinfo->data_lane0)
+		dsi_ctrl |= BIT(4);
+
+	/* from frame buffer, low power mode */
+	/* DSI_COMMAND_MODE_DMA_CTRL */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x38, 0x14000000);
+
+	data = 0;
+	if (pinfo->te_sel)
+		data |= BIT(31);
+	data |= pinfo->mdp_trigger << 4;/* cmd mdp trigger */
+	data |= pinfo->dma_trigger;	/* cmd dma trigger */
+	data |= (pinfo->stream & 0x01) << 8;
+	MIPI_OUTP(MIPI_DSI_BASE + 0x0080, data); /* DSI_TRIG_CTRL */
+
+	/* DSI_LAN_SWAP_CTRL */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x00ac, pinfo->dlane_swap);
+
+	/* clock out ctrl */
+	data = pinfo->t_clk_post & 0x3f;	/* 6 bits */
+	data <<= 8;
+	data |= pinfo->t_clk_pre & 0x3f;	/*  6 bits */
+	MIPI_OUTP(MIPI_DSI_BASE + 0xc0, data);	/* DSI_CLKOUT_TIMING_CTRL */
+
+	data = 0;
+	if (pinfo->rx_eot_ignore)
+		data |= BIT(4);
+	if (pinfo->tx_eot_append)
+		data |= BIT(0);
+	MIPI_OUTP(MIPI_DSI_BASE + 0x00c8, data); /* DSI_EOT_PACKET_CTRL */
+
+
+	/* allow only ack-err-status  to generate interrupt */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x0108, 0x13ff3fe0); /* DSI_ERR_INT_MASK0 */
+
+	intr_ctrl |= DSI_INTR_ERROR_MASK;
+	MIPI_OUTP(MIPI_DSI_BASE + 0x010c, intr_ctrl); /* DSI_INTL_CTRL */
+
+	/* turn esc, byte, dsi, pclk, sclk, hclk on */
+	if (mdp_rev >= MDP_REV_41)
+		MIPI_OUTP(MIPI_DSI_BASE + 0x118, 0x23f); /* DSI_CLK_CTRL */
+	else
+		MIPI_OUTP(MIPI_DSI_BASE + 0x118, 0x33f); /* DSI_CLK_CTRL */
+
+	dsi_ctrl |= BIT(0);	/* enable dsi */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x0000, dsi_ctrl);
+
+	wmb();
+}
+
+void mipi_set_tx_power_mode(int mode)
+{
+	uint32 data = MIPI_INP(MIPI_DSI_BASE + 0x38);
+
+	if (mode == 0)
+		data &= ~BIT(26);
+	else
+		data |= BIT(26);
+
+	MIPI_OUTP(MIPI_DSI_BASE + 0x38, data);
+}
+
+void mipi_dsi_sw_reset(void)
+{
+	MIPI_OUTP(MIPI_DSI_BASE + 0x114, 0x01);
+	wmb();
+	MIPI_OUTP(MIPI_DSI_BASE + 0x114, 0x00);
+	wmb();
+}
+
+void mipi_dsi_controller_cfg(int enable)
+{
+
+	uint32 dsi_ctrl;
+	uint32 status;
+	int cnt;
+
+	cnt = 16;
+	while (cnt--) {
+		status = MIPI_INP(MIPI_DSI_BASE + 0x0004);
+		status &= 0x02;		/* CMD_MODE_DMA_BUSY */
+		if (status == 0)
+			break;
+		usleep(1000);
+	}
+	if (cnt == 0)
+		pr_info("%s: DSI status=%x failed\n", __func__, status);
+
+	cnt = 16;
+	while (cnt--) {
+		status = MIPI_INP(MIPI_DSI_BASE + 0x0008);
+		status &= 0x11111000;	/* x_HS_FIFO_EMPTY */
+		if (status == 0x11111000)	/* all empty */
+			break;
+		usleep(1000);
+	}
+
+	if (cnt == 0)
+		pr_info("%s: FIFO status=%x failed\n", __func__, status);
+
+	dsi_ctrl = MIPI_INP(MIPI_DSI_BASE + 0x0000);
+	if (enable)
+		dsi_ctrl |= 0x01;
+	else
+		dsi_ctrl &= ~0x01;
+
+	MIPI_OUTP(MIPI_DSI_BASE + 0x0000, dsi_ctrl);
+	wmb();
+}
+
+void mipi_dsi_op_mode_config(int mode)
+{
+
+	uint32 dsi_ctrl, intr_ctrl;
+
+	dsi_ctrl = MIPI_INP(MIPI_DSI_BASE + 0x0000);
+	dsi_ctrl &= ~0x07;
+	if (mode == DSI_VIDEO_MODE) {
+		dsi_ctrl |= 0x03;
+		intr_ctrl = DSI_INTR_CMD_DMA_DONE_MASK;
+	} else {		/* command mode */
+		dsi_ctrl |= 0x05;
+		intr_ctrl = DSI_INTR_CMD_DMA_DONE_MASK | DSI_INTR_ERROR_MASK |
+				DSI_INTR_CMD_MDP_DONE_MASK;
+	}
+
+	pr_debug("%s: dsi_ctrl=%x intr=%x\n", __func__, dsi_ctrl, intr_ctrl);
+
+	MIPI_OUTP(MIPI_DSI_BASE + 0x010c, intr_ctrl); /* DSI_INTL_CTRL */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x0000, dsi_ctrl);
+	wmb();
+}
+
+void mipi_dsi_mdp_busy_wait(struct msm_fb_data_type *mfd)
+{
+	unsigned long flag;
+	int need_wait = 0;
+
+	pr_debug("%s: start pid=%d\n",
+				__func__, current->pid);
+	spin_lock_irqsave(&dsi_mdp_lock, flag);
+	if (dsi_mdp_busy == TRUE) {
+		INIT_COMPLETION(dsi_mdp_comp);
+		need_wait++;
+	}
+	spin_unlock_irqrestore(&dsi_mdp_lock, flag);
+
+	if (need_wait) {
+		/* wait until DMA finishes the current job */
+		pr_debug("%s: pending pid=%d\n",
+				__func__, current->pid);
+		wait_for_completion(&dsi_mdp_comp);
+	}
+	pr_debug("%s: done pid=%d\n",
+				__func__, current->pid);
+}
+
+
+void mipi_dsi_cmd_mdp_start(void)
+{
+	unsigned long flag;
+
+
+	if (!in_interrupt())
+		mipi_dsi_pre_kickoff_action();
+
+	mipi_dsi_mdp_stat_inc(STAT_DSI_START);
+
+	spin_lock_irqsave(&dsi_mdp_lock, flag);
+	mipi_dsi_enable_irq();
+	dsi_mdp_busy = TRUE;
+	spin_unlock_irqrestore(&dsi_mdp_lock, flag);
+}
+
+
+void mipi_dsi_cmd_bta_sw_trigger(void)
+{
+	uint32 data;
+	int cnt = 0;
+
+	MIPI_OUTP(MIPI_DSI_BASE + 0x094, 0x01);	/* trigger */
+	wmb();
+
+	while (cnt < 10000) {
+		data = MIPI_INP(MIPI_DSI_BASE + 0x0004);/* DSI_STATUS */
+		if ((data & 0x0010) == 0)
+			break;
+		cnt++;
+	}
+
+	mipi_dsi_ack_err_status();
+
+	pr_debug("%s: BTA done, cnt=%d\n", __func__, cnt);
+}
+
+static char set_tear_on[2] = {0x35, 0x00};
+static struct dsi_cmd_desc dsi_tear_on_cmd = {
+	DTYPE_DCS_WRITE1, 1, 0, 0, 0, sizeof(set_tear_on), set_tear_on};
+
+static char set_tear_off[2] = {0x34, 0x00};
+static struct dsi_cmd_desc dsi_tear_off_cmd = {
+	DTYPE_DCS_WRITE, 1, 0, 0, 0, sizeof(set_tear_off), set_tear_off};
+
+void mipi_dsi_set_tear_on(struct msm_fb_data_type *mfd)
+{
+	mipi_dsi_buf_init(&dsi_tx_buf);
+	mipi_dsi_cmds_tx(mfd, &dsi_tx_buf, &dsi_tear_on_cmd, 1);
+}
+
+void mipi_dsi_set_tear_off(struct msm_fb_data_type *mfd)
+{
+	mipi_dsi_buf_init(&dsi_tx_buf);
+	mipi_dsi_cmds_tx(mfd, &dsi_tx_buf, &dsi_tear_off_cmd, 1);
+}
+
+int mipi_dsi_cmd_reg_tx(uint32 data)
+{
+#ifdef DSI_HOST_DEBUG
+	int i;
+	char *bp;
+
+	bp = (char *)&data;
+	pr_debug("%s: ", __func__);
+	for (i = 0; i < 4; i++)
+		pr_debug("%x ", *bp++);
+
+	pr_debug("\n");
+#endif
+
+	MIPI_OUTP(MIPI_DSI_BASE + 0x0080, 0x04);/* sw trigger */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x0, 0x135);
+
+	wmb();
+
+	MIPI_OUTP(MIPI_DSI_BASE + 0x038, data);
+	wmb();
+	MIPI_OUTP(MIPI_DSI_BASE + 0x08c, 0x01);	/* trigger */
+	wmb();
+
+	udelay(300);
+
+	return 4;
+}
+
+/*
+ * mipi_dsi_cmds_tx:
+ * ov_mutex need to be acquired before call this function.
+ */
+int mipi_dsi_cmds_tx(struct msm_fb_data_type *mfd,
+		struct dsi_buf *tp, struct dsi_cmd_desc *cmds, int cnt)
+{
+	struct dsi_cmd_desc *cm;
+	uint32 dsi_ctrl, ctrl;
+	int i, video_mode;
+	unsigned long flag;
+
+	/* turn on cmd mode
+	* for video mode, do not send cmds more than
+	* one pixel line, since it only transmit it
+	* during BLLP.
+	*/
+	dsi_ctrl = MIPI_INP(MIPI_DSI_BASE + 0x0000);
+	video_mode = dsi_ctrl & 0x02; /* VIDEO_MODE_EN */
+	if (video_mode) {
+		ctrl = dsi_ctrl | 0x04; /* CMD_MODE_EN */
+		MIPI_OUTP(MIPI_DSI_BASE + 0x0000, ctrl);
+	} else { /* cmd mode */
+		/*
+		 * during boot up, cmd mode is configured
+		 * even it is video mode panel.
+		 */
+		/* make sure mdp dma is not txing pixel data */
+		if (mfd->panel_info.type == MIPI_CMD_PANEL) {
+#ifndef CONFIG_FB_MSM_MDP303
+			mdp4_dsi_cmd_dma_busy_wait(mfd);
+#else
+			mdp3_dsi_cmd_dma_busy_wait(mfd);
+#endif
+		}
+	}
+
+	spin_lock_irqsave(&dsi_mdp_lock, flag);
+	mipi_dsi_enable_irq();
+	dsi_mdp_busy = TRUE;
+	spin_unlock_irqrestore(&dsi_mdp_lock, flag);
+
+	cm = cmds;
+	mipi_dsi_buf_init(tp);
+	for (i = 0; i < cnt; i++) {
+		mipi_dsi_buf_init(tp);
+		mipi_dsi_cmd_dma_add(tp, cm);
+		mipi_dsi_cmd_dma_tx(tp);
+		if (cm->wait)
+			msleep(cm->wait);
+		cm++;
+	}
+
+	spin_lock_irqsave(&dsi_mdp_lock, flag);
+	dsi_mdp_busy = FALSE;
+	mipi_dsi_disable_irq();
+	complete(&dsi_mdp_comp);
+	spin_unlock_irqrestore(&dsi_mdp_lock, flag);
+
+	if (video_mode)
+		MIPI_OUTP(MIPI_DSI_BASE + 0x0000, dsi_ctrl); /* restore */
+
+	return cnt;
+}
+
+/* MIPI_DSI_MRPS, Maximum Return Packet Size */
+static char max_pktsize[2] = {0x00, 0x00}; /* LSB tx first, 10 bytes */
+
+static struct dsi_cmd_desc pkt_size_cmd[] = {
+	{DTYPE_MAX_PKTSIZE, 1, 0, 0, 0,
+		sizeof(max_pktsize), max_pktsize}
+};
+
+/*
+ * DSI panel reply with  MAX_RETURN_PACKET_SIZE bytes of data
+ * plus DCS header, ECC and CRC for DCS long read response
+ * mipi_dsi_controller only have 4x32 bits register ( 16 bytes) to
+ * hold data per transaction.
+ * MIPI_DSI_LEN equal to 8
+ * len should be either 4 or 8
+ * any return data more than MIPI_DSI_LEN need to be break down
+ * to multiple transactions.
+ *
+ * ov_mutex need to be acquired before call this function.
+ */
+int mipi_dsi_cmds_rx(struct msm_fb_data_type *mfd,
+			struct dsi_buf *tp, struct dsi_buf *rp,
+			struct dsi_cmd_desc *cmds, int rlen)
+{
+	int cnt, len, diff, pkt_size;
+	unsigned long flag;
+	char cmd;
+
+	if (mfd->panel_info.mipi.no_max_pkt_size) {
+		/* Only support rlen = 4*n */
+		rlen += 3;
+		rlen &= ~0x03;
+	}
+
+	len = rlen;
+	diff = 0;
+
+	if (len <= 2)
+		cnt = 4;	/* short read */
+	else {
+		if (len > MIPI_DSI_LEN)
+			len = MIPI_DSI_LEN;	/* 8 bytes at most */
+
+		len = (len + 3) & ~0x03; /* len 4 bytes align */
+		diff = len - rlen;
+		/*
+		 * add extra 2 bytes to len to have overall
+		 * packet size is multipe by 4. This also make
+		 * sure 4 bytes dcs headerlocates within a
+		 * 32 bits register after shift in.
+		 * after all, len should be either 6 or 10.
+		 */
+		len += 2;
+		cnt = len + 6; /* 4 bytes header + 2 bytes crc */
+	}
+
+	if (mfd->panel_info.type == MIPI_CMD_PANEL) {
+		/* make sure mdp dma is not txing pixel data */
+#ifndef CONFIG_FB_MSM_MDP303
+			mdp4_dsi_cmd_dma_busy_wait(mfd);
+#else
+			mdp3_dsi_cmd_dma_busy_wait(mfd);
+#endif
+	}
+
+	spin_lock_irqsave(&dsi_mdp_lock, flag);
+	mipi_dsi_enable_irq();
+	dsi_mdp_busy = TRUE;
+	spin_unlock_irqrestore(&dsi_mdp_lock, flag);
+
+	if (!mfd->panel_info.mipi.no_max_pkt_size) {
+		/* packet size need to be set at every read */
+		pkt_size = len;
+		max_pktsize[0] = pkt_size;
+		mipi_dsi_buf_init(tp);
+		mipi_dsi_cmd_dma_add(tp, pkt_size_cmd);
+		mipi_dsi_cmd_dma_tx(tp);
+	}
+
+	mipi_dsi_buf_init(tp);
+	mipi_dsi_cmd_dma_add(tp, cmds);
+
+	/* transmit read comamnd to client */
+	mipi_dsi_cmd_dma_tx(tp);
+	/*
+	 * once cmd_dma_done interrupt received,
+	 * return data from client is ready and stored
+	 * at RDBK_DATA register already
+	 */
+	mipi_dsi_buf_init(rp);
+	if (mfd->panel_info.mipi.no_max_pkt_size) {
+		/*
+		 * expect rlen = n * 4
+		 * short alignement for start addr
+		 */
+		rp->data += 2;
+	}
+
+	mipi_dsi_cmd_dma_rx(rp, cnt);
+
+	spin_lock_irqsave(&dsi_mdp_lock, flag);
+	dsi_mdp_busy = FALSE;
+	mipi_dsi_disable_irq();
+	complete(&dsi_mdp_comp);
+	spin_unlock_irqrestore(&dsi_mdp_lock, flag);
+
+	if (mfd->panel_info.mipi.no_max_pkt_size) {
+		/*
+		 * remove extra 2 bytes from previous
+		 * rx transaction at shift register
+		 * which was inserted during copy
+		 * shift registers to rx buffer
+		 * rx payload start from long alignment addr
+		 */
+		rp->data += 2;
+	}
+
+	cmd = rp->data[0];
+	switch (cmd) {
+	case DTYPE_ACK_ERR_RESP:
+		pr_debug("%s: rx ACK_ERR_PACLAGE\n", __func__);
+		break;
+	case DTYPE_GEN_READ1_RESP:
+	case DTYPE_DCS_READ1_RESP:
+		mipi_dsi_short_read1_resp(rp);
+		break;
+	case DTYPE_GEN_READ2_RESP:
+	case DTYPE_DCS_READ2_RESP:
+		mipi_dsi_short_read2_resp(rp);
+		break;
+	case DTYPE_GEN_LREAD_RESP:
+	case DTYPE_DCS_LREAD_RESP:
+		mipi_dsi_long_read_resp(rp);
+		rp->len -= 2; /* extra 2 bytes added */
+		rp->len -= diff; /* align bytes */
+		break;
+	default:
+		break;
+	}
+
+	return rp->len;
+}
+
+int mipi_dsi_cmd_dma_tx(struct dsi_buf *tp)
+{
+	int len;
+
+#ifdef DSI_HOST_DEBUG
+	int i;
+	char *bp;
+
+	bp = tp->data;
+
+	pr_debug("%s: ", __func__);
+	for (i = 0; i < tp->len; i++)
+		pr_debug("%x ", *bp++);
+
+	pr_debug("\n");
+#endif
+
+	len = tp->len;
+	len += 3;
+	len &= ~0x03;	/* multipled by 4 */
+
+	tp->dmap = dma_map_single(&dsi_dev, tp->data, len, DMA_TO_DEVICE);
+	if (dma_mapping_error(&dsi_dev, tp->dmap))
+		pr_err("%s: dmap mapp failed\n", __func__);
+
+	INIT_COMPLETION(dsi_dma_comp);
+
+	MIPI_OUTP(MIPI_DSI_BASE + 0x044, tp->dmap);
+	MIPI_OUTP(MIPI_DSI_BASE + 0x048, len);
+	wmb();
+	MIPI_OUTP(MIPI_DSI_BASE + 0x08c, 0x01);	/* trigger */
+	wmb();
+
+	wait_for_completion(&dsi_dma_comp);
+
+	dma_unmap_single(&dsi_dev, tp->dmap, len, DMA_TO_DEVICE);
+	tp->dmap = 0;
+	return tp->len;
+}
+
+int mipi_dsi_cmd_dma_rx(struct dsi_buf *rp, int rlen)
+{
+	uint32 *lp, data;
+	int i, off, cnt;
+
+	lp = (uint32 *)rp->data;
+	cnt = rlen;
+	cnt += 3;
+	cnt >>= 2;
+
+	if (cnt > 4)
+		cnt = 4; /* 4 x 32 bits registers only */
+
+	off = 0x068;	/* DSI_RDBK_DATA0 */
+	off += ((cnt - 1) * 4);
+
+
+	for (i = 0; i < cnt; i++) {
+		data = (uint32)MIPI_INP(MIPI_DSI_BASE + off);
+		*lp++ = ntohl(data);	/* to network byte order */
+		off -= 4;
+		rp->len += sizeof(*lp);
+	}
+
+	return rlen;
+}
+
+void mipi_dsi_irq_set(uint32 mask, uint32 irq)
+{
+	uint32 data;
+
+	data = MIPI_INP(MIPI_DSI_BASE + 0x010c);/* DSI_INTR_CTRL */
+	data &= ~mask;
+	data |= irq;
+	MIPI_OUTP(MIPI_DSI_BASE + 0x010c, data);
+}
+
+
+void mipi_dsi_ack_err_status(void)
+{
+	uint32 status;
+
+	status = MIPI_INP(MIPI_DSI_BASE + 0x0064);/* DSI_ACK_ERR_STATUS */
+
+	if (status) {
+		MIPI_OUTP(MIPI_DSI_BASE + 0x0064, status);
+		pr_debug("%s: status=%x\n", __func__, status);
+	}
+}
+
+void mipi_dsi_timeout_status(void)
+{
+	uint32 status;
+
+	status = MIPI_INP(MIPI_DSI_BASE + 0x00bc);/* DSI_TIMEOUT_STATUS */
+	if (status & 0x0111) {
+		MIPI_OUTP(MIPI_DSI_BASE + 0x00bc, status);
+		pr_debug("%s: status=%x\n", __func__, status);
+	}
+}
+
+void mipi_dsi_dln0_phy_err(void)
+{
+	uint32 status;
+
+	status = MIPI_INP(MIPI_DSI_BASE + 0x00b0);/* DSI_DLN0_PHY_ERR */
+
+	if (status & 0x011111) {
+		MIPI_OUTP(MIPI_DSI_BASE + 0x00b0, status);
+		pr_debug("%s: status=%x\n", __func__, status);
+	}
+}
+
+void mipi_dsi_fifo_status(void)
+{
+	uint32 status;
+
+	status = MIPI_INP(MIPI_DSI_BASE + 0x0008);/* DSI_FIFO_STATUS */
+
+	if (status & 0x44444489) {
+		MIPI_OUTP(MIPI_DSI_BASE + 0x0008, status);
+		pr_debug("%s: status=%x\n", __func__, status);
+	}
+}
+
+void mipi_dsi_status(void)
+{
+	uint32 status;
+
+	status = MIPI_INP(MIPI_DSI_BASE + 0x0004);/* DSI_STATUS */
+
+	if (status & 0x80000000) {
+		MIPI_OUTP(MIPI_DSI_BASE + 0x0004, status);
+		pr_debug("%s: status=%x\n", __func__, status);
+	}
+}
+
+void mipi_dsi_error(void)
+{
+	/* DSI_ERR_INT_MASK0 */
+	mipi_dsi_ack_err_status();	/* mask0, 0x01f */
+	mipi_dsi_timeout_status();	/* mask0, 0x0e0 */
+	mipi_dsi_fifo_status();		/* mask0, 0x133d00 */
+	mipi_dsi_status();		/* mask0, 0xc0100 */
+	mipi_dsi_dln0_phy_err();	/* mask0, 0x3e00000 */
+}
+
+
+irqreturn_t mipi_dsi_isr(int irq, void *ptr)
+{
+	uint32 isr;
+
+	isr = MIPI_INP(MIPI_DSI_BASE + 0x010c);/* DSI_INTR_CTRL */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x010c, isr);
+
+#ifdef CONFIG_FB_MSM_MDP40
+	mdp4_stat.intr_dsi++;
+#endif
+
+	if (isr & DSI_INTR_ERROR) {
+		mipi_dsi_mdp_stat_inc(STAT_DSI_ERROR);
+		mipi_dsi_error();
+	}
+
+	if (isr & DSI_INTR_VIDEO_DONE) {
+		/*
+		* do something  here
+		*/
+	}
+
+	if (isr & DSI_INTR_CMD_DMA_DONE) {
+		mipi_dsi_mdp_stat_inc(STAT_DSI_CMD);
+		complete(&dsi_dma_comp);
+	}
+
+	if (isr & DSI_INTR_CMD_MDP_DONE) {
+		mipi_dsi_mdp_stat_inc(STAT_DSI_MDP);
+		spin_lock(&dsi_mdp_lock);
+		dsi_mdp_busy = FALSE;
+		mipi_dsi_disable_irq_nosync();
+		spin_unlock(&dsi_mdp_lock);
+		complete(&dsi_mdp_comp);
+		mipi_dsi_post_kickoff_action();
+	}
+
+
+	return IRQ_HANDLED;
+}
diff --git a/drivers/video/msm/mipi_novatek.c b/drivers/video/msm/mipi_novatek.c
new file mode 100644
index 0000000..0070757
--- /dev/null
+++ b/drivers/video/msm/mipi_novatek.c
@@ -0,0 +1,658 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifdef CONFIG_SPI_QUP
+#include <linux/spi/spi.h>
+#endif
+#include <linux/leds.h>
+#include "msm_fb.h"
+#include "mipi_dsi.h"
+#include "mipi_novatek.h"
+#include "mdp4.h"
+
+
+static struct mipi_dsi_panel_platform_data *mipi_novatek_pdata;
+
+static struct dsi_buf novatek_tx_buf;
+static struct dsi_buf novatek_rx_buf;
+static int mipi_novatek_lcd_init(void);
+
+static int wled_trigger_initialized;
+
+#define MIPI_DSI_NOVATEK_SPI_DEVICE_NAME	"dsi_novatek_3d_panel_spi"
+#define HPCI_FPGA_READ_CMD	0x84
+#define HPCI_FPGA_WRITE_CMD	0x04
+
+#ifdef CONFIG_SPI_QUP
+static struct spi_device *panel_3d_spi_client;
+
+static void novatek_fpga_write(uint8 addr, uint16 value)
+{
+	char tx_buf[32];
+	int  rc;
+	struct spi_message  m;
+	struct spi_transfer t;
+	u8 data[4] = {0x0, 0x0, 0x0, 0x0};
+
+	if (!panel_3d_spi_client) {
+		pr_err("%s panel_3d_spi_client is NULL\n", __func__);
+		return;
+	}
+	data[0] = HPCI_FPGA_WRITE_CMD;
+	data[1] = addr;
+	data[2] = ((value >> 8) & 0xFF);
+	data[3] = (value & 0xFF);
+
+	memset(&t, 0, sizeof t);
+	memset(tx_buf, 0, sizeof tx_buf);
+	t.tx_buf = data;
+	t.len = 4;
+	spi_setup(panel_3d_spi_client);
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+
+	rc = spi_sync(panel_3d_spi_client, &m);
+	if (rc)
+		pr_err("%s: SPI transfer failed\n", __func__);
+
+	return;
+}
+
+static void novatek_fpga_read(uint8 addr)
+{
+	char tx_buf[32];
+	int  rc;
+	struct spi_message  m;
+	struct spi_transfer t;
+	struct spi_transfer rx;
+	char rx_value[2];
+	u8 data[4] = {0x0, 0x0};
+
+	if (!panel_3d_spi_client) {
+		pr_err("%s panel_3d_spi_client is NULL\n", __func__);
+		return;
+	}
+
+	data[0] = HPCI_FPGA_READ_CMD;
+	data[1] = addr;
+
+	memset(&t, 0, sizeof t);
+	memset(tx_buf, 0, sizeof tx_buf);
+	memset(&rx, 0, sizeof rx);
+	memset(rx_value, 0, sizeof rx_value);
+	t.tx_buf = data;
+	t.len = 2;
+	rx.rx_buf = rx_value;
+	rx.len = 2;
+	spi_setup(panel_3d_spi_client);
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+	spi_message_add_tail(&rx, &m);
+
+	rc = spi_sync(panel_3d_spi_client, &m);
+	if (rc)
+		pr_err("%s: SPI transfer failed\n", __func__);
+	else
+		pr_info("%s: rx_value = 0x%x, 0x%x\n", __func__,
+						rx_value[0], rx_value[1]);
+
+	return;
+}
+
+static int __devinit panel_3d_spi_probe(struct spi_device *spi)
+{
+	panel_3d_spi_client = spi;
+	return 0;
+}
+static int __devexit panel_3d_spi_remove(struct spi_device *spi)
+{
+	panel_3d_spi_client = NULL;
+	return 0;
+}
+static struct spi_driver panel_3d_spi_driver = {
+	.probe         = panel_3d_spi_probe,
+	.remove        = __devexit_p(panel_3d_spi_remove),
+	.driver		   = {
+		.name = "dsi_novatek_3d_panel_spi",
+		.owner  = THIS_MODULE,
+	}
+};
+
+#else
+
+static void novatek_fpga_write(uint8 addr, uint16 value)
+{
+	return;
+}
+
+static void novatek_fpga_read(uint8 addr)
+{
+	return;
+}
+
+#endif
+
+
+/* novatek blue panel */
+
+#ifdef NOVETAK_COMMANDS_UNUSED
+static char display_config_cmd_mode1[] = {
+	/* TYPE_DCS_LWRITE */
+	0x2A, 0x00, 0x00, 0x01,
+	0x3F, 0xFF, 0xFF, 0xFF
+};
+
+static char display_config_cmd_mode2[] = {
+	/* DTYPE_DCS_LWRITE */
+	0x2B, 0x00, 0x00, 0x01,
+	0xDF, 0xFF, 0xFF, 0xFF
+};
+
+static char display_config_cmd_mode3_666[] = {
+	/* DTYPE_DCS_WRITE1 */
+	0x3A, 0x66, 0x15, 0x80 /* 666 Packed (18-bits) */
+};
+
+static char display_config_cmd_mode3_565[] = {
+	/* DTYPE_DCS_WRITE1 */
+	0x3A, 0x55, 0x15, 0x80 /* 565 mode */
+};
+
+static char display_config_321[] = {
+	/* DTYPE_DCS_WRITE1 */
+	0x66, 0x2e, 0x15, 0x00 /* Reg 0x66 : 2E */
+};
+
+static char display_config_323[] = {
+	/* DTYPE_DCS_WRITE */
+	0x13, 0x00, 0x05, 0x00 /* Reg 0x13 < Set for Normal Mode> */
+};
+
+static char display_config_2lan[] = {
+	/* DTYPE_DCS_WRITE */
+	0x61, 0x01, 0x02, 0xff /* Reg 0x61 : 01,02 < Set for 2 Data Lane > */
+};
+
+static char display_config_exit_sleep[] = {
+	/* DTYPE_DCS_WRITE */
+	0x11, 0x00, 0x05, 0x80 /* Reg 0x11 < exit sleep mode> */
+};
+
+static char display_config_TE_ON[] = {
+	/* DTYPE_DCS_WRITE1 */
+	0x35, 0x00, 0x15, 0x80
+};
+
+static char display_config_39H[] = {
+	/* DTYPE_DCS_WRITE */
+	0x39, 0x00, 0x05, 0x80
+};
+
+static char display_config_set_tear_scanline[] = {
+	/* DTYPE_DCS_LWRITE */
+	0x44, 0x00, 0x00, 0xff
+};
+
+static char display_config_set_twolane[] = {
+	/* DTYPE_DCS_WRITE1 */
+	0xae, 0x03, 0x15, 0x80
+};
+
+static char display_config_set_threelane[] = {
+	/* DTYPE_DCS_WRITE1 */
+	0xae, 0x05, 0x15, 0x80
+};
+
+#else
+
+static char sw_reset[2] = {0x01, 0x00}; /* DTYPE_DCS_WRITE */
+static char enter_sleep[2] = {0x10, 0x00}; /* DTYPE_DCS_WRITE */
+static char exit_sleep[2] = {0x11, 0x00}; /* DTYPE_DCS_WRITE */
+static char display_off[2] = {0x28, 0x00}; /* DTYPE_DCS_WRITE */
+static char display_on[2] = {0x29, 0x00}; /* DTYPE_DCS_WRITE */
+
+
+
+static char rgb_888[2] = {0x3A, 0x77}; /* DTYPE_DCS_WRITE1 */
+
+#if defined(NOVATEK_TWO_LANE)
+static char set_num_of_lanes[2] = {0xae, 0x03}; /* DTYPE_DCS_WRITE1 */
+#else  /* 1 lane */
+static char set_num_of_lanes[2] = {0xae, 0x01}; /* DTYPE_DCS_WRITE1 */
+#endif
+/* commands by Novatke */
+static char novatek_f4[2] = {0xf4, 0x55}; /* DTYPE_DCS_WRITE1 */
+static char novatek_8c[16] = { /* DTYPE_DCS_LWRITE */
+	0x8C, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x08, 0x08, 0x00, 0x30, 0xC0, 0xB7, 0x37};
+static char novatek_ff[2] = {0xff, 0x55 }; /* DTYPE_DCS_WRITE1 */
+
+static char set_width[5] = { /* DTYPE_DCS_LWRITE */
+	0x2A, 0x00, 0x00, 0x02, 0x1B}; /* 540 - 1 */
+static char set_height[5] = { /* DTYPE_DCS_LWRITE */
+	0x2B, 0x00, 0x00, 0x03, 0xBF}; /* 960 - 1 */
+#endif
+
+static char led_pwm1[2] = {0x51, 0x0};	/* DTYPE_DCS_WRITE1 */
+static char led_pwm2[2] = {0x53, 0x24}; /* DTYPE_DCS_WRITE1 */
+static char led_pwm3[2] = {0x55, 0x00}; /* DTYPE_DCS_WRITE1 */
+
+static struct dsi_cmd_desc novatek_cmd_backlight_cmds[] = {
+	{DTYPE_DCS_LWRITE, 1, 0, 0, 1, sizeof(led_pwm1), led_pwm1},
+};
+
+static struct dsi_cmd_desc novatek_video_on_cmds[] = {
+	{DTYPE_DCS_WRITE, 1, 0, 0, 50,
+		sizeof(sw_reset), sw_reset},
+	{DTYPE_DCS_WRITE, 1, 0, 0, 10,
+		sizeof(exit_sleep), exit_sleep},
+	{DTYPE_DCS_WRITE, 1, 0, 0, 10,
+		sizeof(display_on), display_on},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, 10,
+		sizeof(set_num_of_lanes), set_num_of_lanes},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, 10,
+		sizeof(rgb_888), rgb_888},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, 10,
+		sizeof(led_pwm2), led_pwm2},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, 10,
+		sizeof(led_pwm3), led_pwm3},
+};
+
+static struct dsi_cmd_desc novatek_cmd_on_cmds[] = {
+	{DTYPE_DCS_WRITE, 1, 0, 0, 50,
+		sizeof(sw_reset), sw_reset},
+	{DTYPE_DCS_WRITE, 1, 0, 0, 10,
+		sizeof(exit_sleep), exit_sleep},
+	{DTYPE_DCS_WRITE, 1, 0, 0, 10,
+		sizeof(display_on), display_on},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, 50,
+		sizeof(novatek_f4), novatek_f4},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, 50,
+		sizeof(novatek_8c), novatek_8c},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, 50,
+		sizeof(novatek_ff), novatek_ff},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, 10,
+		sizeof(set_num_of_lanes), set_num_of_lanes},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, 50,
+		sizeof(set_width), set_width},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, 50,
+		sizeof(set_height), set_height},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, 10,
+		sizeof(rgb_888), rgb_888},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, 1,
+		sizeof(led_pwm2), led_pwm2},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, 1,
+		sizeof(led_pwm3), led_pwm3},
+};
+
+static struct dsi_cmd_desc novatek_display_off_cmds[] = {
+	{DTYPE_DCS_WRITE, 1, 0, 0, 10,
+		sizeof(display_off), display_off},
+	{DTYPE_DCS_WRITE, 1, 0, 0, 120,
+		sizeof(enter_sleep), enter_sleep}
+};
+
+static char manufacture_id[2] = {0x04, 0x00}; /* DTYPE_DCS_READ */
+
+static struct dsi_cmd_desc novatek_manufacture_id_cmd = {
+	DTYPE_DCS_READ, 1, 0, 1, 5, sizeof(manufacture_id), manufacture_id};
+
+static uint32 mipi_novatek_manufacture_id(struct msm_fb_data_type *mfd)
+{
+	struct dsi_buf *rp, *tp;
+	struct dsi_cmd_desc *cmd;
+	uint32 *lp;
+
+	tp = &novatek_tx_buf;
+	rp = &novatek_rx_buf;
+	cmd = &novatek_manufacture_id_cmd;
+	mipi_dsi_cmds_rx(mfd, tp, rp, cmd, 3);
+	lp = (uint32 *)rp->data;
+	pr_info("%s: manufacture_id=%x", __func__, *lp);
+	return *lp;
+}
+
+static int fpga_addr;
+static int fpga_access_mode;
+static bool support_3d;
+
+static void mipi_novatek_3d_init(int addr, int mode)
+{
+	fpga_addr = addr;
+	fpga_access_mode = mode;
+}
+
+static void mipi_dsi_enable_3d_barrier(int mode)
+{
+	void __iomem *fpga_ptr;
+	uint32_t ptr_value = 0;
+
+	if (!fpga_addr && support_3d) {
+		pr_err("%s: fpga_addr not set. Failed to enable 3D barrier\n",
+					__func__);
+		return;
+	}
+
+	if (fpga_access_mode == FPGA_SPI_INTF) {
+		if (mode == LANDSCAPE)
+			novatek_fpga_write(fpga_addr, 1);
+		else if (mode == PORTRAIT)
+			novatek_fpga_write(fpga_addr, 3);
+		else
+			novatek_fpga_write(fpga_addr, 0);
+
+		mb();
+		novatek_fpga_read(fpga_addr);
+	} else if (fpga_access_mode == FPGA_EBI2_INTF) {
+		fpga_ptr = ioremap_nocache(fpga_addr, sizeof(uint32_t));
+		if (!fpga_ptr) {
+			pr_err("%s: FPGA ioremap failed."
+				"Failed to enable 3D barrier\n",
+						__func__);
+			return;
+		}
+
+		ptr_value = readl_relaxed(fpga_ptr);
+		if (mode == LANDSCAPE)
+			writel_relaxed(((0xFFFF0000 & ptr_value) | 1),
+								fpga_ptr);
+		else if (mode == PORTRAIT)
+			writel_relaxed(((0xFFFF0000 & ptr_value) | 3),
+								fpga_ptr);
+		else
+			writel_relaxed((0xFFFF0000 & ptr_value),
+								fpga_ptr);
+
+		mb();
+		iounmap(fpga_ptr);
+	} else
+		pr_err("%s: 3D barrier not configured correctly\n",
+					__func__);
+}
+
+static int mipi_novatek_lcd_on(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+	struct mipi_panel_info *mipi;
+	struct msm_panel_info *pinfo;
+
+	mfd = platform_get_drvdata(pdev);
+	if (!mfd)
+		return -ENODEV;
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	pinfo = &mfd->panel_info;
+	if (pinfo->is_3d_panel)
+		support_3d = TRUE;
+
+	mipi  = &mfd->panel_info.mipi;
+
+	if (mipi->mode == DSI_VIDEO_MODE) {
+		mipi_dsi_cmds_tx(mfd, &novatek_tx_buf, novatek_video_on_cmds,
+			ARRAY_SIZE(novatek_video_on_cmds));
+	} else {
+		mipi_dsi_cmds_tx(mfd, &novatek_tx_buf, novatek_cmd_on_cmds,
+			ARRAY_SIZE(novatek_cmd_on_cmds));
+
+		mipi_dsi_cmd_bta_sw_trigger(); /* clean up ack_err_status */
+
+		mipi_novatek_manufacture_id(mfd);
+	}
+
+	return 0;
+}
+
+static int mipi_novatek_lcd_off(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+
+	mfd = platform_get_drvdata(pdev);
+
+	if (!mfd)
+		return -ENODEV;
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	mipi_dsi_cmds_tx(mfd, &novatek_tx_buf, novatek_display_off_cmds,
+			ARRAY_SIZE(novatek_display_off_cmds));
+
+	return 0;
+}
+
+DEFINE_LED_TRIGGER(bkl_led_trigger);
+
+static void mipi_novatek_set_backlight(struct msm_fb_data_type *mfd)
+{
+	struct mipi_panel_info *mipi;
+
+	if ((mipi_novatek_pdata->enable_wled_bl_ctrl)
+	    && (wled_trigger_initialized)) {
+		led_trigger_event(bkl_led_trigger, mfd->bl_level);
+		return;
+	}
+	mipi  = &mfd->panel_info.mipi;
+
+	mutex_lock(&mfd->dma->ov_mutex);
+	if (mdp4_overlay_dsi_state_get() <= ST_DSI_SUSPEND) {
+		mutex_unlock(&mfd->dma->ov_mutex);
+		return;
+	}
+	/* mdp4_dsi_cmd_busy_wait: will turn on dsi clock also */
+	mdp4_dsi_cmd_dma_busy_wait(mfd);
+	mdp4_dsi_blt_dmap_busy_wait(mfd);
+	mipi_dsi_mdp_busy_wait(mfd);
+
+	led_pwm1[1] = (unsigned char)(mfd->bl_level);
+	mipi_dsi_cmds_tx(mfd, &novatek_tx_buf, novatek_cmd_backlight_cmds,
+			ARRAY_SIZE(novatek_cmd_backlight_cmds));
+	mutex_unlock(&mfd->dma->ov_mutex);
+	return;
+}
+
+static int mipi_dsi_3d_barrier_sysfs_register(struct device *dev);
+static int barrier_mode;
+
+static int __devinit mipi_novatek_lcd_probe(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+	struct mipi_panel_info *mipi;
+	struct platform_device *current_pdev;
+	static struct mipi_dsi_phy_ctrl *phy_settings;
+	static char dlane_swap;
+
+	if (pdev->id == 0) {
+		mipi_novatek_pdata = pdev->dev.platform_data;
+
+		if (mipi_novatek_pdata
+			&& mipi_novatek_pdata->phy_ctrl_settings) {
+			phy_settings = (mipi_novatek_pdata->phy_ctrl_settings);
+		}
+
+		if (mipi_novatek_pdata
+			&& mipi_novatek_pdata->dlane_swap) {
+			dlane_swap = (mipi_novatek_pdata->dlane_swap);
+		}
+
+		if (mipi_novatek_pdata
+			 && mipi_novatek_pdata->fpga_3d_config_addr)
+			mipi_novatek_3d_init(mipi_novatek_pdata
+	->fpga_3d_config_addr, mipi_novatek_pdata->fpga_ctrl_mode);
+
+		/* create sysfs to control 3D barrier for the Sharp panel */
+		if (mipi_dsi_3d_barrier_sysfs_register(&pdev->dev)) {
+			pr_err("%s: Failed to register 3d Barrier sysfs\n",
+						__func__);
+			return -ENODEV;
+		}
+		barrier_mode = 0;
+
+		return 0;
+	}
+
+	current_pdev = msm_fb_add_device(pdev);
+
+	if (current_pdev) {
+		mfd = platform_get_drvdata(current_pdev);
+		if (!mfd)
+			return -ENODEV;
+		if (mfd->key != MFD_KEY)
+			return -EINVAL;
+
+		mipi  = &mfd->panel_info.mipi;
+
+		if (phy_settings != NULL)
+			mipi->dsi_phy_db = phy_settings;
+
+		if (dlane_swap)
+			mipi->dlane_swap = dlane_swap;
+	}
+	return 0;
+}
+
+static struct platform_driver this_driver = {
+	.probe  = mipi_novatek_lcd_probe,
+	.driver = {
+		.name   = "mipi_novatek",
+	},
+};
+
+static struct msm_fb_panel_data novatek_panel_data = {
+	.on		= mipi_novatek_lcd_on,
+	.off		= mipi_novatek_lcd_off,
+	.set_backlight = mipi_novatek_set_backlight,
+};
+
+static ssize_t mipi_dsi_3d_barrier_read(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	return snprintf((char *)buf, sizeof(buf), "%u\n", barrier_mode);
+}
+
+static ssize_t mipi_dsi_3d_barrier_write(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf,
+				size_t count)
+{
+	int ret = -1;
+	u32 data = 0;
+
+	if (sscanf((char *)buf, "%u", &data) != 1) {
+		dev_err(dev, "%s\n", __func__);
+		ret = -EINVAL;
+	} else {
+		barrier_mode = data;
+		if (data == 1)
+			mipi_dsi_enable_3d_barrier(LANDSCAPE);
+		else if (data == 2)
+			mipi_dsi_enable_3d_barrier(PORTRAIT);
+		else
+			mipi_dsi_enable_3d_barrier(0);
+	}
+
+	return count;
+}
+
+static struct device_attribute mipi_dsi_3d_barrier_attributes[] = {
+	__ATTR(enable_3d_barrier, 0664, mipi_dsi_3d_barrier_read,
+					 mipi_dsi_3d_barrier_write),
+};
+
+static int mipi_dsi_3d_barrier_sysfs_register(struct device *dev)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(mipi_dsi_3d_barrier_attributes); i++)
+		if (device_create_file(dev, mipi_dsi_3d_barrier_attributes + i))
+			goto error;
+
+	return 0;
+
+error:
+	for (; i >= 0 ; i--)
+		device_remove_file(dev, mipi_dsi_3d_barrier_attributes + i);
+	pr_err("%s: Unable to create interface\n", __func__);
+
+	return -ENODEV;
+}
+
+static int ch_used[3];
+
+int mipi_novatek_device_register(struct msm_panel_info *pinfo,
+					u32 channel, u32 panel)
+{
+	struct platform_device *pdev = NULL;
+	int ret;
+
+	if ((channel >= 3) || ch_used[channel])
+		return -ENODEV;
+
+	ch_used[channel] = TRUE;
+
+	ret = mipi_novatek_lcd_init();
+	if (ret) {
+		pr_err("mipi_novatek_lcd_init() failed with ret %u\n", ret);
+		return ret;
+	}
+
+	pdev = platform_device_alloc("mipi_novatek", (panel << 8)|channel);
+	if (!pdev)
+		return -ENOMEM;
+
+	novatek_panel_data.panel_info = *pinfo;
+
+	ret = platform_device_add_data(pdev, &novatek_panel_data,
+		sizeof(novatek_panel_data));
+	if (ret) {
+		printk(KERN_ERR
+		  "%s: platform_device_add_data failed!\n", __func__);
+		goto err_device_put;
+	}
+
+	ret = platform_device_add(pdev);
+	if (ret) {
+		printk(KERN_ERR
+		  "%s: platform_device_register failed!\n", __func__);
+		goto err_device_put;
+	}
+
+	return 0;
+
+err_device_put:
+	platform_device_put(pdev);
+	return ret;
+}
+
+static int mipi_novatek_lcd_init(void)
+{
+#ifdef CONFIG_SPI_QUP
+	int ret;
+	ret = spi_register_driver(&panel_3d_spi_driver);
+
+	if (ret) {
+		pr_err("%s: spi register failed: rc=%d\n", __func__, ret);
+		platform_driver_unregister(&this_driver);
+	} else
+		pr_info("%s: SUCCESS (SPI)\n", __func__);
+#endif
+
+	led_trigger_register_simple("bkl_trigger", &bkl_led_trigger);
+	pr_info("%s: SUCCESS (WLED TRIGGER)\n", __func__);
+	wled_trigger_initialized = 1;
+
+	mipi_dsi_buf_alloc(&novatek_tx_buf, DSI_BUF_SIZE);
+	mipi_dsi_buf_alloc(&novatek_rx_buf, DSI_BUF_SIZE);
+
+	return platform_driver_register(&this_driver);
+}
diff --git a/drivers/video/msm/mipi_novatek.h b/drivers/video/msm/mipi_novatek.h
new file mode 100644
index 0000000..f84de9a
--- /dev/null
+++ b/drivers/video/msm/mipi_novatek.h
@@ -0,0 +1,22 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef MIPI_NOVATEK_BLUE_H
+#define MIPI_NOVATEK_BLUE_H
+
+#define NOVATEK_TWO_LANE
+
+int mipi_novatek_device_register(struct msm_panel_info *pinfo,
+					u32 channel, u32 panel);
+
+#endif  /* MIPI_NOVATEK_BLUE_H */
diff --git a/drivers/video/msm/mipi_novatek_cmd_qhd_pt.c b/drivers/video/msm/mipi_novatek_cmd_qhd_pt.c
new file mode 100644
index 0000000..fbd2495
--- /dev/null
+++ b/drivers/video/msm/mipi_novatek_cmd_qhd_pt.c
@@ -0,0 +1,99 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_fb.h"
+#include "mipi_dsi.h"
+#include "mipi_novatek.h"
+
+static struct msm_panel_info pinfo;
+
+static struct mipi_dsi_phy_ctrl dsi_cmd_mode_phy_db = {
+/* DSI_BIT_CLK at 500MHz, 2 lane, RGB888 */
+		{0x03, 0x01, 0x01, 0x00},	/* regulator */
+		/* timing   */
+		{0xB4, 0x8D, 0x1D, 0x00, 0x20, 0x94, 0x20,
+		0x8F, 0x20, 0x03, 0x04},
+		{0x7f, 0x00, 0x00, 0x00},	/* phy ctrl */
+		{0xee, 0x02, 0x86, 0x00},	/* strength */
+		/* pll control */
+		{0x40, 0xf9, 0xb0, 0xda, 0x00, 0x50, 0x48, 0x63,
+#if defined(NOVATEK_TWO_LANE)
+		0x30, 0x07, 0x03,
+#else           /* default set to 1 lane */
+		0x30, 0x07, 0x07,
+#endif
+		0x05, 0x14, 0x03, 0x0, 0x0, 0x54, 0x06, 0x10, 0x04, 0x0},
+};
+
+static int __init mipi_cmd_novatek_blue_qhd_pt_init(void)
+{
+	int ret;
+
+	if (msm_fb_detect_client("mipi_cmd_novatek_qhd"))
+		return 0;
+
+	pinfo.xres = 540;
+	pinfo.yres = 960;
+	pinfo.type = MIPI_CMD_PANEL;
+	pinfo.pdest = DISPLAY_1;
+	pinfo.wait_cycle = 0;
+	pinfo.bpp = 24;
+	pinfo.lcdc.h_back_porch = 50;
+	pinfo.lcdc.h_front_porch = 50;
+	pinfo.lcdc.h_pulse_width = 20;
+	pinfo.lcdc.v_back_porch = 11;
+	pinfo.lcdc.v_front_porch = 10;
+	pinfo.lcdc.v_pulse_width = 5;
+	pinfo.lcdc.border_clr = 0;	/* blk */
+	pinfo.lcdc.underflow_clr = 0xff;	/* blue */
+	pinfo.lcdc.hsync_skew = 0;
+	pinfo.bl_max = 255;
+	pinfo.bl_min = 1;
+	pinfo.fb_num = 2;
+	pinfo.clk_rate = 454000000;
+	pinfo.is_3d_panel = FB_TYPE_3D_PANEL;
+	pinfo.lcd.vsync_enable = TRUE;
+	pinfo.lcd.hw_vsync_mode = TRUE;
+	pinfo.lcd.refx100 = 6000; /* adjust refx100 to prevent tearing */
+	pinfo.lcd.v_back_porch = 11;
+	pinfo.lcd.v_front_porch = 10;
+	pinfo.lcd.v_pulse_width = 5;
+
+	pinfo.mipi.mode = DSI_CMD_MODE;
+	pinfo.mipi.dst_format = DSI_CMD_DST_FORMAT_RGB888;
+	pinfo.mipi.vc = 0;
+	pinfo.mipi.data_lane0 = TRUE;
+#if defined(NOVATEK_TWO_LANE)
+	pinfo.mipi.data_lane1 = TRUE;
+#endif
+	pinfo.mipi.t_clk_post = 0x22;
+	pinfo.mipi.t_clk_pre = 0x3f;
+	pinfo.mipi.stream = 0;	/* dma_p */
+	pinfo.mipi.mdp_trigger = DSI_CMD_TRIGGER_NONE;
+	pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW;
+	pinfo.mipi.te_sel = 1; /* TE from vsycn gpio */
+	pinfo.mipi.interleave_max = 1;
+	pinfo.mipi.insert_dcs_cmd = TRUE;
+	pinfo.mipi.wr_mem_continue = 0x3c;
+	pinfo.mipi.wr_mem_start = 0x2c;
+	pinfo.mipi.dsi_phy_db = &dsi_cmd_mode_phy_db;
+
+	ret = mipi_novatek_device_register(&pinfo, MIPI_DSI_PRIM,
+						MIPI_DSI_PANEL_QHD_PT);
+	if (ret)
+		pr_err("%s: failed to register device!\n", __func__);
+
+	return ret;
+}
+
+module_init(mipi_cmd_novatek_blue_qhd_pt_init);
diff --git a/drivers/video/msm/mipi_novatek_video_qhd_pt.c b/drivers/video/msm/mipi_novatek_video_qhd_pt.c
new file mode 100644
index 0000000..42ddfbe
--- /dev/null
+++ b/drivers/video/msm/mipi_novatek_video_qhd_pt.c
@@ -0,0 +1,96 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_fb.h"
+#include "mipi_dsi.h"
+#include "mipi_novatek.h"
+
+static struct msm_panel_info pinfo;
+
+static struct mipi_dsi_phy_ctrl dsi_video_mode_phy_db = {
+/* DSI_BIT_CLK at 500MHz, 2 lane, RGB888 */
+		{0x03, 0x01, 0x01, 0x00},	/* regulator */
+		/* timing   */
+		{0x82, 0x31, 0x13, 0x0, 0x42, 0x4D, 0x18,
+		0x35, 0x21, 0x03, 0x04},
+		{0x7f, 0x00, 0x00, 0x00},	/* phy ctrl */
+		{0xee, 0x02, 0x86, 0x00},	/* strength */
+		/* pll control */
+		{0x40, 0xf9, 0xb0, 0xda, 0x00, 0x50, 0x48, 0x63,
+#if defined(NOVATEK_TWO_LANE)
+		0x30, 0x07, 0x03,
+#else           /* default set to 1 lane */
+		0x30, 0x07, 0x07,
+#endif
+		0x05, 0x14, 0x03, 0x0, 0x0, 0x54, 0x06, 0x10, 0x04, 0x0},
+};
+
+static int __init mipi_video_novatek_qhd_pt_init(void)
+{
+	int ret;
+
+	if (msm_fb_detect_client("mipi_video_novatek_qhd"))
+		return 0;
+
+	pinfo.xres = 540;
+	pinfo.yres = 960;
+	pinfo.type = MIPI_VIDEO_PANEL;
+	pinfo.pdest = DISPLAY_1;
+	pinfo.wait_cycle = 0;
+	pinfo.bpp = 24;
+	pinfo.lcdc.h_back_porch = 80;
+	pinfo.lcdc.h_front_porch = 24;
+	pinfo.lcdc.h_pulse_width = 8;
+	pinfo.lcdc.v_back_porch = 16;
+	pinfo.lcdc.v_front_porch = 8;
+	pinfo.lcdc.v_pulse_width = 1;
+	pinfo.lcdc.border_clr = 0;	/* blk */
+	pinfo.lcdc.underflow_clr = 0xff;	/* blue */
+	pinfo.lcdc.hsync_skew = 0;
+	pinfo.bl_max = 15;
+	pinfo.bl_min = 1;
+	pinfo.fb_num = 2;
+
+	pinfo.mipi.mode = DSI_VIDEO_MODE;
+	pinfo.mipi.pulse_mode_hsa_he = TRUE;
+	pinfo.mipi.hfp_power_stop = FALSE;
+	pinfo.mipi.hbp_power_stop = FALSE;
+	pinfo.mipi.hsa_power_stop = FALSE;
+	pinfo.mipi.eof_bllp_power_stop = TRUE;
+	pinfo.mipi.bllp_power_stop = TRUE;
+	pinfo.mipi.traffic_mode = DSI_NON_BURST_SYNCH_PULSE;
+	pinfo.mipi.dst_format = DSI_VIDEO_DST_FORMAT_RGB888;
+	pinfo.mipi.vc = 0;
+	pinfo.mipi.rgb_swap = DSI_RGB_SWAP_BGR;
+	pinfo.mipi.data_lane0 = TRUE;
+#if defined(NOVATEK_TWO_LANE)
+	pinfo.mipi.data_lane1 = TRUE;
+#endif
+	pinfo.mipi.tx_eot_append = TRUE;
+	pinfo.mipi.t_clk_post = 0x04;
+	pinfo.mipi.t_clk_pre = 0x1c;
+	pinfo.mipi.stream = 0; /* dma_p */
+	pinfo.mipi.mdp_trigger = DSI_CMD_TRIGGER_SW;
+	pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW;
+	pinfo.mipi.frame_rate = 60;
+	pinfo.mipi.dsi_phy_db = &dsi_video_mode_phy_db;
+
+	ret = mipi_novatek_device_register(&pinfo, MIPI_DSI_PRIM,
+						MIPI_DSI_PANEL_QHD_PT);
+	if (ret)
+		pr_err("%s: failed to register device!\n", __func__);
+
+	return ret;
+}
+
+module_init(mipi_video_novatek_qhd_pt_init);
diff --git a/drivers/video/msm/mipi_orise.c b/drivers/video/msm/mipi_orise.c
new file mode 100644
index 0000000..2afbb9b
--- /dev/null
+++ b/drivers/video/msm/mipi_orise.c
@@ -0,0 +1,194 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_fb.h"
+#include "mipi_dsi.h"
+#include "mipi_orise.h"
+#include "mdp4.h"
+
+
+static struct mipi_dsi_panel_platform_data *mipi_orise_pdata;
+
+static struct dsi_buf orise_tx_buf;
+static struct dsi_buf orise_rx_buf;
+
+static char enter_sleep[2] = {0x10, 0x00}; /* DTYPE_DCS_WRITE */
+static char exit_sleep[2] = {0x11, 0x00}; /* DTYPE_DCS_WRITE */
+static char display_off[2] = {0x28, 0x00}; /* DTYPE_DCS_WRITE */
+static char display_on[2] = {0x29, 0x00}; /* DTYPE_DCS_WRITE */
+
+static struct dsi_cmd_desc orise_video_on_cmds[] = {
+	{DTYPE_DCS_WRITE, 1, 0, 0, 10,
+		sizeof(exit_sleep), exit_sleep},
+	{DTYPE_DCS_WRITE, 1, 0, 0, 10,
+		sizeof(display_on), display_on},
+};
+
+static struct dsi_cmd_desc orise_cmd_on_cmds[] = {
+	{DTYPE_DCS_WRITE, 1, 0, 0, 10,
+		sizeof(exit_sleep), exit_sleep},
+	{DTYPE_DCS_WRITE, 1, 0, 0, 10,
+		sizeof(display_on), display_on},
+};
+
+static struct dsi_cmd_desc orise_display_off_cmds[] = {
+	{DTYPE_DCS_WRITE, 1, 0, 0, 10,
+		sizeof(display_off), display_off},
+	{DTYPE_DCS_WRITE, 1, 0, 0, 120,
+		sizeof(enter_sleep), enter_sleep}
+};
+
+static int mipi_orise_lcd_on(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+	struct mipi_panel_info *mipi;
+	struct msm_panel_info *pinfo;
+
+	mfd = platform_get_drvdata(pdev);
+	if (!mfd)
+		return -ENODEV;
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	pinfo = &mfd->panel_info;
+	mipi  = &mfd->panel_info.mipi;
+
+	if (mipi->mode == DSI_VIDEO_MODE) {
+		mipi_dsi_cmds_tx(mfd, &orise_tx_buf, orise_video_on_cmds,
+			ARRAY_SIZE(orise_video_on_cmds));
+	} else {
+		mipi_dsi_cmds_tx(mfd, &orise_tx_buf, orise_cmd_on_cmds,
+			ARRAY_SIZE(orise_cmd_on_cmds));
+
+		mipi_dsi_cmd_bta_sw_trigger(); /* clean up ack_err_status */
+	}
+
+	return 0;
+}
+
+static int mipi_orise_lcd_off(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+
+	mfd = platform_get_drvdata(pdev);
+
+	if (!mfd)
+		return -ENODEV;
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	mipi_dsi_cmds_tx(mfd, &orise_tx_buf, orise_display_off_cmds,
+			ARRAY_SIZE(orise_display_off_cmds));
+
+	return 0;
+}
+
+
+
+static int __devinit mipi_orise_lcd_probe(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+	struct mipi_panel_info *mipi;
+	struct platform_device *current_pdev;
+	static struct mipi_dsi_phy_ctrl *phy_settings;
+
+	if (pdev->id == 0) {
+		mipi_orise_pdata = pdev->dev.platform_data;
+
+		if (mipi_orise_pdata
+			&& mipi_orise_pdata->phy_ctrl_settings) {
+			phy_settings = (mipi_orise_pdata->phy_ctrl_settings);
+		}
+
+		return 0;
+	}
+
+	current_pdev = msm_fb_add_device(pdev);
+
+	if (current_pdev) {
+		mfd = platform_get_drvdata(current_pdev);
+		if (!mfd)
+			return -ENODEV;
+		if (mfd->key != MFD_KEY)
+			return -EINVAL;
+
+		mipi  = &mfd->panel_info.mipi;
+
+		if (phy_settings != NULL)
+			mipi->dsi_phy_db = phy_settings;
+	}
+	return 0;
+}
+
+static struct platform_driver this_driver = {
+	.probe  = mipi_orise_lcd_probe,
+	.driver = {
+		.name   = "mipi_orise",
+	},
+};
+
+static struct msm_fb_panel_data orise_panel_data = {
+	.on		= mipi_orise_lcd_on,
+	.off		= mipi_orise_lcd_off,
+};
+
+static int ch_used[3];
+
+int mipi_orise_device_register(struct msm_panel_info *pinfo,
+					u32 channel, u32 panel)
+{
+	struct platform_device *pdev = NULL;
+	int ret;
+
+	if ((channel >= 3) || ch_used[channel])
+		return -ENODEV;
+
+	ch_used[channel] = TRUE;
+
+	pdev = platform_device_alloc("mipi_orise", (panel << 8)|channel);
+	if (!pdev)
+		return -ENOMEM;
+
+	orise_panel_data.panel_info = *pinfo;
+
+	ret = platform_device_add_data(pdev, &orise_panel_data,
+		sizeof(orise_panel_data));
+	if (ret) {
+		printk(KERN_ERR
+		  "%s: platform_device_add_data failed!\n", __func__);
+		goto err_device_put;
+	}
+
+	ret = platform_device_add(pdev);
+	if (ret) {
+		printk(KERN_ERR
+		  "%s: platform_device_register failed!\n", __func__);
+		goto err_device_put;
+	}
+
+	return 0;
+
+err_device_put:
+	platform_device_put(pdev);
+	return ret;
+}
+
+static int __init mipi_orise_lcd_init(void)
+{
+	mipi_dsi_buf_alloc(&orise_tx_buf, DSI_BUF_SIZE);
+	mipi_dsi_buf_alloc(&orise_rx_buf, DSI_BUF_SIZE);
+
+	return platform_driver_register(&this_driver);
+}
+
+module_init(mipi_orise_lcd_init);
diff --git a/drivers/video/msm/mipi_orise.h b/drivers/video/msm/mipi_orise.h
new file mode 100644
index 0000000..1659479
--- /dev/null
+++ b/drivers/video/msm/mipi_orise.h
@@ -0,0 +1,21 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+
+#ifndef MIPI_ORISE_H
+#define MIPI_ORISE_H
+
+int mipi_orise_device_register(struct msm_panel_info *pinfo,
+					u32 channel, u32 panel);
+
+#endif  /* MIPI_ORISE_H */
diff --git a/drivers/video/msm/mipi_orise_cmd_720p_pt.c b/drivers/video/msm/mipi_orise_cmd_720p_pt.c
new file mode 100644
index 0000000..c2a158d
--- /dev/null
+++ b/drivers/video/msm/mipi_orise_cmd_720p_pt.c
@@ -0,0 +1,96 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_fb.h"
+#include "mipi_dsi.h"
+#include "mipi_orise.h"
+
+static struct msm_panel_info pinfo;
+
+static struct mipi_dsi_phy_ctrl dsi_cmd_mode_phy_db = {
+/* DSI_BIT_CLK at 507MHz, 4 lane, RGB888 */
+	{0x03, 0x0a, 0x04, 0x00, 0x20},
+	/* timing */
+	{0x8c, 0x34, 0x15, 0x00, 0x46, 0x50, 0x1a, 0x38,
+	0x24, 0x03, 0x04, 0xa0},
+    /* phy ctrl */
+	{0x5f, 0x00, 0x00, 0x10},
+    /* strength */
+	{0xff, 0x00, 0x06, 0x00},
+	/* pll control */
+		{0x0, 0xf9, 0x30, 0xda, 0x00, 0x40, 0x03, 0x62,
+	0x40, 0x07, 0x03,
+	0x00, 0x1a, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0x01 },
+};
+
+static int __init mipi_cmd_orise_720p_pt_init(void)
+{
+	int ret;
+
+	if (msm_fb_detect_client("mipi_cmd_orise_720p"))
+		return 0;
+
+	pinfo.xres = 720;
+	pinfo.yres = 1280;
+	pinfo.type = MIPI_CMD_PANEL;
+	pinfo.pdest = DISPLAY_1;
+	pinfo.wait_cycle = 0;
+	pinfo.bpp = 24;
+	pinfo.lcdc.h_back_porch = 160;
+	pinfo.lcdc.h_front_porch = 160;
+	pinfo.lcdc.h_pulse_width = 8;
+	pinfo.lcdc.v_back_porch = 32;
+	pinfo.lcdc.v_front_porch = 32;
+	pinfo.lcdc.v_pulse_width = 1;
+	pinfo.lcdc.border_clr = 0;	/* blk */
+	pinfo.lcdc.underflow_clr = 0xff;	/* blue */
+	pinfo.lcdc.hsync_skew = 0;
+	pinfo.bl_max = 200;
+	pinfo.bl_min = 1;
+	pinfo.fb_num = 2;
+	pinfo.clk_rate = 507000000;
+	pinfo.lcd.vsync_enable = TRUE;
+	pinfo.lcd.hw_vsync_mode = TRUE;
+	pinfo.lcd.refx100 = 6000; /* adjust refx100 to prevent tearing */
+	pinfo.lcd.v_back_porch = 32;
+	pinfo.lcd.v_front_porch = 32;
+	pinfo.lcd.v_pulse_width = 1;
+
+	pinfo.mipi.mode = DSI_CMD_MODE;
+	pinfo.mipi.dst_format = DSI_CMD_DST_FORMAT_RGB888;
+	pinfo.mipi.vc = 0;
+	pinfo.mipi.data_lane0 = TRUE;
+	pinfo.mipi.data_lane1 = TRUE;
+	pinfo.mipi.data_lane2 = TRUE;
+	pinfo.mipi.data_lane3 = TRUE;
+	pinfo.mipi.t_clk_post = 0x04;
+	pinfo.mipi.t_clk_pre = 0x1e;
+	pinfo.mipi.stream = 0;	/* dma_p */
+	pinfo.mipi.mdp_trigger = DSI_CMD_TRIGGER_NONE;
+	pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW;
+	pinfo.mipi.te_sel = 1; /* TE from vsycn gpio */
+	pinfo.mipi.interleave_max = 1;
+	pinfo.mipi.insert_dcs_cmd = TRUE;
+	pinfo.mipi.wr_mem_continue = 0x3c;
+	pinfo.mipi.wr_mem_start = 0x2c;
+	pinfo.mipi.dsi_phy_db = &dsi_cmd_mode_phy_db;
+
+	ret = mipi_orise_device_register(&pinfo, MIPI_DSI_PRIM,
+						MIPI_DSI_PANEL_720P_PT);
+	if (ret)
+		pr_err("%s: failed to register device!\n", __func__);
+
+	return ret;
+}
+
+module_init(mipi_cmd_orise_720p_pt_init);
diff --git a/drivers/video/msm/mipi_orise_video_720p_pt.c b/drivers/video/msm/mipi_orise_video_720p_pt.c
new file mode 100644
index 0000000..629ff10
--- /dev/null
+++ b/drivers/video/msm/mipi_orise_video_720p_pt.c
@@ -0,0 +1,97 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_fb.h"
+#include "mipi_dsi.h"
+#include "mipi_orise.h"
+
+static struct msm_panel_info pinfo;
+
+static struct mipi_dsi_phy_ctrl dsi_video_mode_phy_db = {
+    /* regulator */
+	{0x03, 0x0a, 0x04, 0x00, 0x20},
+	/* timing */
+	{0x83, 0x31, 0x13, 0x00, 0x42, 0x4d, 0x18, 0x35,
+	0x21, 0x03, 0x04, 0xa0},
+    /* phy ctrl */
+	{0x5f, 0x00, 0x00, 0x10},
+    /* strength */
+	{0xff, 0x00, 0x06, 0x00},
+	/* pll control */
+	{0x0, 0x0e, 0x30, 0xc0, 0x00, 0x40, 0x03, 0x62,
+	0x40, 0x07, 0x07,
+	0x00, 0x1a, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0x01 },
+};
+
+static int __init mipi_video_orise_720p_pt_init(void)
+{
+	int ret;
+
+	if (msm_fb_detect_client("mipi_video_orise_720p"))
+		return 0;
+
+	pinfo.xres = 720;
+	pinfo.yres = 1280;
+	pinfo.lcdc.xres_pad = 0;
+	pinfo.lcdc.yres_pad = 0;
+
+	pinfo.type = MIPI_VIDEO_PANEL;
+	pinfo.pdest = DISPLAY_1;
+	pinfo.wait_cycle = 0;
+	pinfo.bpp = 24;
+	pinfo.lcdc.h_back_porch = 160;
+	pinfo.lcdc.h_front_porch = 160;
+	pinfo.lcdc.h_pulse_width = 8;
+	pinfo.lcdc.v_back_porch = 32;
+	pinfo.lcdc.v_front_porch = 32;
+	pinfo.lcdc.v_pulse_width = 1;
+	pinfo.lcdc.border_clr = 0;	/* blk */
+	pinfo.lcdc.underflow_clr = 0xff;	/* blue */
+	pinfo.lcdc.hsync_skew = 0;
+	pinfo.bl_max = 200;
+	pinfo.bl_min = 1;
+	pinfo.fb_num = 2;
+
+	pinfo.mipi.mode = DSI_VIDEO_MODE;
+	pinfo.mipi.pulse_mode_hsa_he = TRUE;
+	pinfo.mipi.hfp_power_stop = TRUE;
+	pinfo.mipi.hbp_power_stop = TRUE;
+	pinfo.mipi.hsa_power_stop = FALSE;
+	pinfo.mipi.eof_bllp_power_stop = TRUE;
+	pinfo.mipi.bllp_power_stop = TRUE;
+	pinfo.mipi.traffic_mode = DSI_NON_BURST_SYNCH_EVENT;
+	pinfo.mipi.dst_format = DSI_VIDEO_DST_FORMAT_RGB888;
+	pinfo.mipi.vc = 0;
+	pinfo.mipi.rgb_swap = DSI_RGB_SWAP_RGB;
+	pinfo.mipi.data_lane0 = TRUE;
+	pinfo.mipi.data_lane1 = TRUE;
+	pinfo.mipi.data_lane2 = TRUE;
+	pinfo.mipi.data_lane3 = TRUE;
+	pinfo.mipi.t_clk_post = 0x04;
+	pinfo.mipi.t_clk_pre = 0x1c;
+	pinfo.mipi.stream = 0; /* dma_p */
+	pinfo.mipi.mdp_trigger = 0;
+	pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW;
+	pinfo.mipi.frame_rate = 55;
+	pinfo.mipi.dsi_phy_db = &dsi_video_mode_phy_db;
+	pinfo.mipi.tx_eot_append = TRUE;
+
+	ret = mipi_orise_device_register(&pinfo, MIPI_DSI_PRIM,
+						MIPI_DSI_PANEL_720P_PT);
+	if (ret)
+		printk(KERN_ERR "%s: failed to register device!\n", __func__);
+
+	return ret;
+}
+
+module_init(mipi_video_orise_720p_pt_init);
diff --git a/drivers/video/msm/mipi_renesas.c b/drivers/video/msm/mipi_renesas.c
new file mode 100644
index 0000000..c9dc8255
--- /dev/null
+++ b/drivers/video/msm/mipi_renesas.c
@@ -0,0 +1,1262 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "msm_fb.h"
+#include "mipi_dsi.h"
+#include "mipi_renesas.h"
+#include <mach/socinfo.h>
+
+#define RENESAS_CMD_DELAY 0 /* 50 */
+#define RENESAS_SLEEP_OFF_DELAY 50
+static struct msm_panel_common_pdata *mipi_renesas_pdata;
+
+static struct dsi_buf renesas_tx_buf;
+static struct dsi_buf renesas_rx_buf;
+
+static int mipi_renesas_lcd_init(void);
+
+static char config_sleep_out[2] = {0x11, 0x00};
+static char config_CMD_MODE[2] = {0x40, 0x01};
+static char config_WRTXHT[7] = {0x92, 0x16, 0x08, 0x08, 0x00, 0x01, 0xe0};
+static char config_WRTXVT[7] = {0x8b, 0x02, 0x02, 0x02, 0x00, 0x03, 0x60};
+static char config_PLL2NR[2] = {0xa0, 0x24};
+static char config_PLL2NF1[2] = {0xa2, 0xd0};
+static char config_PLL2NF2[2] = {0xa4, 0x00};
+static char config_PLL2BWADJ1[2] = {0xa6, 0xd0};
+static char config_PLL2BWADJ2[2] = {0xa8, 0x00};
+static char config_PLL2CTL[2] = {0xaa, 0x00};
+static char config_DBICBR[2] = {0x48, 0x03};
+static char config_DBICTYPE[2] = {0x49, 0x00};
+static char config_DBICSET1[2] = {0x4a, 0x1c};
+static char config_DBICADD[2] = {0x4b, 0x00};
+static char config_DBICCTL[2] = {0x4e, 0x01};
+/* static char config_COLMOD_565[2] = {0x3a, 0x05}; */
+/* static char config_COLMOD_666PACK[2] = {0x3a, 0x06}; */
+static char config_COLMOD_888[2] = {0x3a, 0x07};
+static char config_MADCTL[2] = {0x36, 0x00};
+static char config_DBIOC[2] = {0x82, 0x40};
+static char config_CASET[7] = {0x2a, 0x00, 0x00, 0x00, 0x00, 0x01, 0xdf };
+static char config_PASET[7] = {0x2b, 0x00, 0x00, 0x00, 0x00, 0x03, 0x5f };
+static char config_TXON[2] = {0x81, 0x00};
+static char config_BLSET_TM[2] = {0xff, 0x6c};
+static char config_DSIRXCTL[2] = {0x41, 0x01};
+static char config_TEON[2] = {0x35, 0x00};
+static char config_TEOFF[1] = {0x34};
+
+static char config_AGCPSCTL_TM[2] = {0x56, 0x08};
+
+static char config_DBICADD70[2] = {0x4b, 0x70};
+static char config_DBICSET_15[2] = {0x4a, 0x15};
+static char config_DBICADD72[2] = {0x4b, 0x72};
+
+static char config_Power_Ctrl_2a_cmd[3] = {0x4c, 0x40, 0x10};
+static char config_Auto_Sequencer_Setting_a_cmd[3] = {0x4c, 0x00, 0x00};
+static char Driver_Output_Ctrl_indx[3] = {0x4c, 0x00, 0x01};
+static char Driver_Output_Ctrl_cmd[3] = {0x4c, 0x03, 0x10};
+static char config_LCD_drive_AC_Ctrl_indx[3] = {0x4c, 0x00, 0x02};
+static char config_LCD_drive_AC_Ctrl_cmd[3] = {0x4c, 0x01, 0x00};
+static char config_Entry_Mode_indx[3] = {0x4c, 0x00, 0x03};
+static char config_Entry_Mode_cmd[3] = {0x4c, 0x00, 0x00};
+static char config_Display_Ctrl_1_indx[3] = {0x4c, 0x00, 0x07};
+static char config_Display_Ctrl_1_cmd[3] = {0x4c, 0x00, 0x00};
+static char config_Display_Ctrl_2_indx[3] = {0x4c, 0x00, 0x08};
+static char config_Display_Ctrl_2_cmd[3] = {0x4c, 0x00, 0x04};
+static char config_Display_Ctrl_3_indx[3] = {0x4c, 0x00, 0x09};
+static char config_Display_Ctrl_3_cmd[3] = {0x4c, 0x00, 0x0c};
+static char config_Display_IF_Ctrl_1_indx[3] = {0x4c, 0x00, 0x0c};
+static char config_Display_IF_Ctrl_1_cmd[3] = {0x4c, 0x40, 0x10};
+static char config_Display_IF_Ctrl_2_indx[3] = {0x4c, 0x00, 0x0e};
+static char config_Display_IF_Ctrl_2_cmd[3] = {0x4c, 0x00, 0x00};
+
+static char config_Panel_IF_Ctrl_1_indx[3] = {0x4c, 0x00, 0x20};
+static char config_Panel_IF_Ctrl_1_cmd[3] = {0x4c, 0x01, 0x3f};
+static char config_Panel_IF_Ctrl_3_indx[3] = {0x4c, 0x00, 0x22};
+static char config_Panel_IF_Ctrl_3_cmd[3] = {0x4c, 0x76, 0x00};
+static char config_Panel_IF_Ctrl_4_indx[3] = {0x4c, 0x00, 0x23};
+static char config_Panel_IF_Ctrl_4_cmd[3] = {0x4c, 0x1c, 0x0a};
+static char config_Panel_IF_Ctrl_5_indx[3] = {0x4c, 0x00, 0x24};
+static char config_Panel_IF_Ctrl_5_cmd[3] = {0x4c, 0x1c, 0x2c};
+static char config_Panel_IF_Ctrl_6_indx[3] = {0x4c, 0x00, 0x25};
+static char config_Panel_IF_Ctrl_6_cmd[3] = {0x4c, 0x1c, 0x4e};
+static char config_Panel_IF_Ctrl_8_indx[3] = {0x4c, 0x00, 0x27};
+static char config_Panel_IF_Ctrl_8_cmd[3] = {0x4c, 0x00, 0x00};
+static char config_Panel_IF_Ctrl_9_indx[3] = {0x4c, 0x00, 0x28};
+static char config_Panel_IF_Ctrl_9_cmd[3] = {0x4c, 0x76, 0x0c};
+
+
+static char config_gam_adjust_00_indx[3] = {0x4c, 0x03, 0x00};
+static char config_gam_adjust_00_cmd[3] = {0x4c, 0x00, 0x00};
+static char config_gam_adjust_01_indx[3] = {0x4c, 0x03, 0x01};
+static char config_gam_adjust_01_cmd[3] = {0x4c, 0x05, 0x02};
+static char config_gam_adjust_02_indx[3] = {0x4c, 0x03, 0x02};
+static char config_gam_adjust_02_cmd[3] = {0x4c, 0x07, 0x05};
+static char config_gam_adjust_03_indx[3] = {0x4c, 0x03, 0x03};
+static char config_gam_adjust_03_cmd[3] = {0x4c, 0x00, 0x00};
+static char config_gam_adjust_04_indx[3] = {0x4c, 0x03, 0x04};
+static char config_gam_adjust_04_cmd[3] = {0x4c, 0x02, 0x00};
+static char config_gam_adjust_05_indx[3] = {0x4c, 0x03, 0x05};
+static char config_gam_adjust_05_cmd[3] = {0x4c, 0x07, 0x07};
+static char config_gam_adjust_06_indx[3] = {0x4c, 0x03, 0x06};
+static char config_gam_adjust_06_cmd[3] = {0x4c, 0x10, 0x10};
+static char config_gam_adjust_07_indx[3] = {0x4c, 0x03, 0x07};
+static char config_gam_adjust_07_cmd[3] = {0x4c, 0x02, 0x02};
+static char config_gam_adjust_08_indx[3] = {0x4c, 0x03, 0x08};
+static char config_gam_adjust_08_cmd[3] = {0x4c, 0x07, 0x04};
+static char config_gam_adjust_09_indx[3] = {0x4c, 0x03, 0x09};
+static char config_gam_adjust_09_cmd[3] = {0x4c, 0x07, 0x07};
+static char config_gam_adjust_0A_indx[3] = {0x4c, 0x03, 0x0a};
+static char config_gam_adjust_0A_cmd[3] = {0x4c, 0x00, 0x00};
+static char config_gam_adjust_0B_indx[3] = {0x4c, 0x03, 0x0b};
+static char config_gam_adjust_0B_cmd[3] = {0x4c, 0x00, 0x00};
+static char config_gam_adjust_0C_indx[3] = {0x4c, 0x03, 0x0c};
+static char config_gam_adjust_0C_cmd[3] = {0x4c, 0x07, 0x07};
+static char config_gam_adjust_0D_indx[3] = {0x4c, 0x03, 0x0d};
+static char config_gam_adjust_0D_cmd[3] = {0x4c, 0x10, 0x10};
+static char config_gam_adjust_10_indx[3] = {0x4c, 0x03, 0x10};
+static char config_gam_adjust_10_cmd[3] = {0x4c, 0x01, 0x04};
+static char config_gam_adjust_11_indx[3] = {0x4c, 0x03, 0x11};
+static char config_gam_adjust_11_cmd[3] = {0x4c, 0x05, 0x03};
+static char config_gam_adjust_12_indx[3] = {0x4c, 0x03, 0x12};
+static char config_gam_adjust_12_cmd[3] = {0x4c, 0x03, 0x04};
+static char config_gam_adjust_15_indx[3] = {0x4c, 0x03, 0x15};
+static char config_gam_adjust_15_cmd[3] = {0x4c, 0x03, 0x04};
+static char config_gam_adjust_16_indx[3] = {0x4c, 0x03, 0x16};
+static char config_gam_adjust_16_cmd[3] = {0x4c, 0x03, 0x1c};
+static char config_gam_adjust_17_indx[3] = {0x4c, 0x03, 0x17};
+static char config_gam_adjust_17_cmd[3] = {0x4c, 0x02, 0x04};
+static char config_gam_adjust_18_indx[3] = {0x4c, 0x03, 0x18};
+static char config_gam_adjust_18_cmd[3] = {0x4c, 0x04, 0x02};
+static char config_gam_adjust_19_indx[3] = {0x4c, 0x03, 0x19};
+static char config_gam_adjust_19_cmd[3] = {0x4c, 0x03, 0x05};
+static char config_gam_adjust_1C_indx[3] = {0x4c, 0x03, 0x1c};
+static char config_gam_adjust_1C_cmd[3] = {0x4c, 0x07, 0x07};
+static char config_gam_adjust_1D_indx[3] = {0x4c, 0x03, 0x1D};
+static char config_gam_adjust_1D_cmd[3] = {0x4c, 0x02, 0x1f};
+static char config_gam_adjust_20_indx[3] = {0x4c, 0x03, 0x20};
+static char config_gam_adjust_20_cmd[3] = {0x4c, 0x05, 0x07};
+static char config_gam_adjust_21_indx[3] = {0x4c, 0x03, 0x21};
+static char config_gam_adjust_21_cmd[3] = {0x4c, 0x06, 0x04};
+static char config_gam_adjust_22_indx[3] = {0x4c, 0x03, 0x22};
+static char config_gam_adjust_22_cmd[3] = {0x4c, 0x04, 0x05};
+static char config_gam_adjust_27_indx[3] = {0x4c, 0x03, 0x27};
+static char config_gam_adjust_27_cmd[3] = {0x4c, 0x02, 0x03};
+static char config_gam_adjust_28_indx[3] = {0x4c, 0x03, 0x28};
+static char config_gam_adjust_28_cmd[3] = {0x4c, 0x03, 0x00};
+static char config_gam_adjust_29_indx[3] = {0x4c, 0x03, 0x29};
+static char config_gam_adjust_29_cmd[3] = {0x4c, 0x00, 0x02};
+
+static char config_Power_Ctrl_1_indx[3] = {0x4c, 0x01, 0x00};
+static char config_Power_Ctrl_1b_cmd[3] = {0x4c, 0x36, 0x3c};
+static char config_Power_Ctrl_2_indx[3] = {0x4c, 0x01, 0x01};
+static char config_Power_Ctrl_2b_cmd[3] = {0x4c, 0x40, 0x03};
+static char config_Power_Ctrl_3_indx[3] = {0x4c, 0x01, 0x02};
+static char config_Power_Ctrl_3a_cmd[3] = {0x4c, 0x00, 0x01};
+static char config_Power_Ctrl_4_indx[3] = {0x4c, 0x01, 0x03};
+static char config_Power_Ctrl_4a_cmd[3] = {0x4c, 0x3c, 0x58};
+static char config_Power_Ctrl_6_indx[3] = {0x4c, 0x01, 0x0c};
+static char config_Power_Ctrl_6a_cmd[3] = {0x4c, 0x01, 0x35};
+
+static char config_Auto_Sequencer_Setting_b_cmd[3] = {0x4c, 0x00, 0x02};
+
+static char config_Panel_IF_Ctrl_10_indx[3] = {0x4c, 0x00, 0x29};
+static char config_Panel_IF_Ctrl_10a_cmd[3] = {0x4c, 0x03, 0xbf};
+static char config_Auto_Sequencer_Setting_indx[3] = {0x4c, 0x01, 0x06};
+static char config_Auto_Sequencer_Setting_c_cmd[3] = {0x4c, 0x00, 0x03};
+static char config_Power_Ctrl_2c_cmd[3] = {0x4c, 0x40, 0x10};
+
+static char config_VIDEO[2] = {0x40, 0x00};
+
+static char config_Panel_IF_Ctrl_10_indx_off[3] = {0x4C, 0x00, 0x29};
+
+static char config_Panel_IF_Ctrl_10b_cmd_off[3] = {0x4C, 0x00, 0x02};
+
+static char config_Power_Ctrl_1a_cmd[3] = {0x4C, 0x30, 0x00};
+
+static struct dsi_cmd_desc renesas_sleep_off_cmds[] = {
+	{DTYPE_DCS_WRITE, 1, 0, 0, RENESAS_SLEEP_OFF_DELAY,
+		sizeof(config_sleep_out), config_sleep_out }
+};
+
+static struct dsi_cmd_desc renesas_display_off_cmds[] = {
+	/* Choosing Command Mode */
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_CMD_MODE), config_CMD_MODE },
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Auto_Sequencer_Setting_indx),
+			config_Auto_Sequencer_Setting_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Auto_Sequencer_Setting_b_cmd),
+			config_Auto_Sequencer_Setting_b_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY * 2,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	/* After waiting >= 5 frames, turn OFF RGB signals
+	This is done by on DSI/MDP (depends on Vid/Cmd Mode.  */
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Auto_Sequencer_Setting_indx),
+			config_Auto_Sequencer_Setting_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Auto_Sequencer_Setting_a_cmd),
+			config_Auto_Sequencer_Setting_a_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Panel_IF_Ctrl_10_indx_off),
+			config_Panel_IF_Ctrl_10_indx_off},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Panel_IF_Ctrl_10b_cmd_off),
+				config_Panel_IF_Ctrl_10b_cmd_off},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Power_Ctrl_1_indx),
+				config_Power_Ctrl_1_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Power_Ctrl_1a_cmd),
+				config_Power_Ctrl_1a_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_TEOFF), config_TEOFF},
+};
+
+static struct dsi_cmd_desc renesas_display_on_cmds[] = {
+	/* Choosing Command Mode */
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_CMD_MODE), config_CMD_MODE },
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_WRTXHT), config_WRTXHT },
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_WRTXVT), config_WRTXVT },
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_PLL2NR), config_PLL2NR },
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_PLL2NF1), config_PLL2NF1 },
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_PLL2NF2), config_PLL2NF2 },
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_PLL2BWADJ1), config_PLL2BWADJ1},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_PLL2BWADJ2), config_PLL2BWADJ2},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_PLL2CTL), config_PLL2CTL},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICBR), config_DBICBR},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICTYPE), config_DBICTYPE},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET1), config_DBICSET1},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD), config_DBICADD},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICCTL), config_DBICCTL},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_COLMOD_888), config_COLMOD_888},
+	/* Choose config_COLMOD_565 or config_COLMOD_666PACK for other modes */
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_MADCTL), config_MADCTL},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBIOC), config_DBIOC},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_CASET), config_CASET},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_PASET), config_PASET},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DSIRXCTL), config_DSIRXCTL},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_TEON), config_TEON},
+	{DTYPE_DCS_WRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_TXON), config_TXON},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_BLSET_TM), config_BLSET_TM},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_AGCPSCTL_TM), config_AGCPSCTL_TM},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Power_Ctrl_1_indx), config_Power_Ctrl_1_indx },
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Power_Ctrl_1a_cmd), config_Power_Ctrl_1a_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Power_Ctrl_2_indx), config_Power_Ctrl_2_indx },
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Power_Ctrl_2a_cmd), config_Power_Ctrl_2a_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Auto_Sequencer_Setting_indx),
+			config_Auto_Sequencer_Setting_indx },
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Auto_Sequencer_Setting_a_cmd),
+			config_Auto_Sequencer_Setting_a_cmd },
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(Driver_Output_Ctrl_indx), Driver_Output_Ctrl_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(Driver_Output_Ctrl_cmd),
+			Driver_Output_Ctrl_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_LCD_drive_AC_Ctrl_indx),
+			config_LCD_drive_AC_Ctrl_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_LCD_drive_AC_Ctrl_cmd),
+			config_LCD_drive_AC_Ctrl_cmd },
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Entry_Mode_indx),
+			config_Entry_Mode_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Entry_Mode_cmd),
+			config_Entry_Mode_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Display_Ctrl_1_indx),
+			config_Display_Ctrl_1_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Display_Ctrl_1_cmd),
+			config_Display_Ctrl_1_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Display_Ctrl_2_indx),
+			config_Display_Ctrl_2_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Display_Ctrl_2_cmd),
+			config_Display_Ctrl_2_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Display_Ctrl_3_indx),
+			config_Display_Ctrl_3_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Display_Ctrl_3_cmd),
+			config_Display_Ctrl_3_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Display_IF_Ctrl_1_indx),
+			config_Display_IF_Ctrl_1_indx },
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Display_IF_Ctrl_1_cmd),
+			config_Display_IF_Ctrl_1_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Display_IF_Ctrl_2_indx),
+			config_Display_IF_Ctrl_2_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Display_IF_Ctrl_2_cmd),
+			config_Display_IF_Ctrl_2_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Panel_IF_Ctrl_1_indx),
+			config_Panel_IF_Ctrl_1_indx },
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Panel_IF_Ctrl_1_cmd),
+			config_Panel_IF_Ctrl_1_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Panel_IF_Ctrl_3_indx),
+			config_Panel_IF_Ctrl_3_indx },
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Panel_IF_Ctrl_3_cmd),
+			config_Panel_IF_Ctrl_3_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Panel_IF_Ctrl_4_indx),
+			config_Panel_IF_Ctrl_4_indx },
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Panel_IF_Ctrl_4_cmd),
+			config_Panel_IF_Ctrl_4_cmd },
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Panel_IF_Ctrl_5_indx),
+			config_Panel_IF_Ctrl_5_indx },
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Panel_IF_Ctrl_5_cmd),
+			config_Panel_IF_Ctrl_5_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Panel_IF_Ctrl_6_indx),
+			config_Panel_IF_Ctrl_6_indx },
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Panel_IF_Ctrl_6_cmd),
+			config_Panel_IF_Ctrl_6_cmd },
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Panel_IF_Ctrl_8_indx),
+			config_Panel_IF_Ctrl_8_indx },
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Panel_IF_Ctrl_8_cmd),
+			config_Panel_IF_Ctrl_8_cmd },
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Panel_IF_Ctrl_9_indx),
+			config_Panel_IF_Ctrl_9_indx },
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Panel_IF_Ctrl_9_cmd),
+			config_Panel_IF_Ctrl_9_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_00_indx),
+			config_gam_adjust_00_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_00_cmd),
+			config_gam_adjust_00_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_01_indx),
+			config_gam_adjust_01_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_01_cmd),
+			config_gam_adjust_01_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_02_indx),
+			config_gam_adjust_02_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_02_cmd),
+			config_gam_adjust_02_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_03_indx),
+			config_gam_adjust_03_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_03_cmd),
+			config_gam_adjust_03_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_04_indx), config_gam_adjust_04_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_04_cmd), config_gam_adjust_04_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_05_indx), config_gam_adjust_05_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_05_cmd), config_gam_adjust_05_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_06_indx), config_gam_adjust_06_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_06_cmd), config_gam_adjust_06_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_07_indx), config_gam_adjust_07_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_07_cmd), config_gam_adjust_07_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_08_indx), config_gam_adjust_08_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_08_cmd), config_gam_adjust_08_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_09_indx), config_gam_adjust_09_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_09_cmd), config_gam_adjust_09_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_0A_indx), config_gam_adjust_0A_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_0A_cmd), config_gam_adjust_0A_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_0B_indx), config_gam_adjust_0B_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_0B_cmd), config_gam_adjust_0B_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_0C_indx), config_gam_adjust_0C_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_0C_cmd), config_gam_adjust_0C_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_0D_indx), config_gam_adjust_0D_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_0D_cmd), config_gam_adjust_0D_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_10_indx), config_gam_adjust_10_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_10_cmd), config_gam_adjust_10_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_11_indx), config_gam_adjust_11_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_11_cmd), config_gam_adjust_11_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_12_indx), config_gam_adjust_12_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_12_cmd), config_gam_adjust_12_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_15_indx), config_gam_adjust_15_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_15_cmd), config_gam_adjust_15_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_16_indx), config_gam_adjust_16_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_16_cmd), config_gam_adjust_16_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_17_indx), config_gam_adjust_17_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_17_cmd), config_gam_adjust_17_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_18_indx), config_gam_adjust_18_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_18_cmd), config_gam_adjust_18_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_19_indx), config_gam_adjust_19_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_19_cmd), config_gam_adjust_19_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_1C_indx), config_gam_adjust_1C_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_1C_cmd), config_gam_adjust_1C_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_1D_indx), config_gam_adjust_1D_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_1D_cmd), config_gam_adjust_1D_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_20_indx), config_gam_adjust_20_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_20_cmd), config_gam_adjust_20_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_21_indx), config_gam_adjust_21_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_21_cmd), config_gam_adjust_21_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_22_indx), config_gam_adjust_22_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_22_cmd), config_gam_adjust_22_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_27_indx), config_gam_adjust_27_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_27_cmd), config_gam_adjust_27_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_28_indx), config_gam_adjust_28_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_28_cmd), config_gam_adjust_28_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_29_indx), config_gam_adjust_29_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_gam_adjust_29_cmd), config_gam_adjust_29_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Power_Ctrl_1_indx), config_Power_Ctrl_1_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Power_Ctrl_1b_cmd), config_Power_Ctrl_1b_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Power_Ctrl_2_indx), config_Power_Ctrl_2_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Power_Ctrl_2b_cmd), config_Power_Ctrl_2b_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Power_Ctrl_3_indx), config_Power_Ctrl_3_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Power_Ctrl_3a_cmd), config_Power_Ctrl_3a_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Power_Ctrl_4_indx), config_Power_Ctrl_4_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Power_Ctrl_4a_cmd), config_Power_Ctrl_4a_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Power_Ctrl_6_indx), config_Power_Ctrl_6_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Power_Ctrl_6a_cmd), config_Power_Ctrl_6a_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Auto_Sequencer_Setting_indx),
+			config_Auto_Sequencer_Setting_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Auto_Sequencer_Setting_b_cmd),
+			config_Auto_Sequencer_Setting_b_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Panel_IF_Ctrl_10_indx),
+			config_Panel_IF_Ctrl_10_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Panel_IF_Ctrl_10a_cmd),
+			config_Panel_IF_Ctrl_10a_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Auto_Sequencer_Setting_indx),
+			config_Auto_Sequencer_Setting_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Auto_Sequencer_Setting_c_cmd),
+			config_Auto_Sequencer_Setting_c_cmd},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD70), config_DBICADD70},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Power_Ctrl_2_indx),
+			config_Power_Ctrl_2_indx},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_DBICADD72), config_DBICADD72},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_Power_Ctrl_2c_cmd),
+			config_Power_Ctrl_2c_cmd},
+
+	{DTYPE_DCS_WRITE1, 1, 0, 0, 0/* RENESAS_CMD_DELAY */,
+		sizeof(config_DBICSET_15), config_DBICSET_15},
+
+};
+
+static char config_WRTXHT2[7] = {0x92, 0x15, 0x05, 0x0F, 0x00, 0x01, 0xe0};
+static char config_WRTXVT2[7] = {0x8b, 0x14, 0x01, 0x14, 0x00, 0x03, 0x60};
+
+static struct dsi_cmd_desc renesas_hvga_on_cmds[] = {
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_WRTXHT2), config_WRTXHT2},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_WRTXVT2), config_WRTXVT2},
+};
+
+static struct dsi_cmd_desc renesas_video_on_cmds[] = {
+{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_VIDEO), config_VIDEO}
+};
+
+static struct dsi_cmd_desc renesas_cmd_on_cmds[] = {
+{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY,
+		sizeof(config_CMD_MODE), config_CMD_MODE},
+};
+
+static int mipi_renesas_lcd_on(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+	struct mipi_panel_info *mipi;
+
+	mfd = platform_get_drvdata(pdev);
+	mipi  = &mfd->panel_info.mipi;
+
+	if (!mfd)
+		return -ENODEV;
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	mipi_dsi_cmds_tx(mfd, &renesas_tx_buf, renesas_sleep_off_cmds,
+			ARRAY_SIZE(renesas_sleep_off_cmds));
+
+	mipi_set_tx_power_mode(1);
+	mipi_dsi_cmds_tx(mfd, &renesas_tx_buf, renesas_display_on_cmds,
+			ARRAY_SIZE(renesas_display_on_cmds));
+
+	if (cpu_is_msm7x25a() || cpu_is_msm7x25aa() || cpu_is_msm7x25ab()) {
+		mipi_dsi_cmds_tx(mfd, &renesas_tx_buf, renesas_hvga_on_cmds,
+			ARRAY_SIZE(renesas_hvga_on_cmds));
+	}
+
+	if (mipi->mode == DSI_VIDEO_MODE)
+		mipi_dsi_cmds_tx(mfd, &renesas_tx_buf, renesas_video_on_cmds,
+			ARRAY_SIZE(renesas_video_on_cmds));
+	else
+		mipi_dsi_cmds_tx(mfd, &renesas_tx_buf, renesas_cmd_on_cmds,
+			ARRAY_SIZE(renesas_cmd_on_cmds));
+	mipi_set_tx_power_mode(0);
+
+	return 0;
+}
+
+static int mipi_renesas_lcd_off(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+
+	mfd = platform_get_drvdata(pdev);
+
+	if (!mfd)
+		return -ENODEV;
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	mipi_dsi_cmds_tx(mfd, &renesas_tx_buf, renesas_display_off_cmds,
+			ARRAY_SIZE(renesas_display_off_cmds));
+
+	return 0;
+}
+
+static int __devinit mipi_renesas_lcd_probe(struct platform_device *pdev)
+{
+	if (pdev->id == 0) {
+		mipi_renesas_pdata = pdev->dev.platform_data;
+		return 0;
+	}
+
+	msm_fb_add_device(pdev);
+
+	return 0;
+}
+
+static void mipi_renesas_set_backlight(struct msm_fb_data_type *mfd)
+{
+	int ret = -EPERM;
+	int bl_level;
+
+	bl_level = mfd->bl_level;
+
+	if (mipi_renesas_pdata && mipi_renesas_pdata->pmic_backlight)
+		ret = mipi_renesas_pdata->pmic_backlight(bl_level);
+	else
+		pr_err("%s(): Backlight level set failed", __func__);
+}
+
+static struct platform_driver this_driver = {
+	.probe  = mipi_renesas_lcd_probe,
+	.driver = {
+		.name   = "mipi_renesas",
+	},
+};
+
+static struct msm_fb_panel_data renesas_panel_data = {
+	.on		= mipi_renesas_lcd_on,
+	.off	= mipi_renesas_lcd_off,
+	.set_backlight = mipi_renesas_set_backlight,
+};
+
+static int ch_used[3];
+
+int mipi_renesas_device_register(struct msm_panel_info *pinfo,
+					u32 channel, u32 panel)
+{
+	struct platform_device *pdev = NULL;
+	int ret;
+	if ((channel >= 3) || ch_used[channel])
+		return -ENODEV;
+
+	ch_used[channel] = TRUE;
+
+	ret = mipi_renesas_lcd_init();
+	if (ret) {
+		pr_err("mipi_renesas_lcd_init() failed with ret %u\n", ret);
+		return ret;
+	}
+
+	pdev = platform_device_alloc("mipi_renesas", (panel << 8)|channel);
+	if (!pdev)
+		return -ENOMEM;
+
+	renesas_panel_data.panel_info = *pinfo;
+
+	ret = platform_device_add_data(pdev, &renesas_panel_data,
+		sizeof(renesas_panel_data));
+	if (ret) {
+		pr_err("%s: platform_device_add_data failed!\n", __func__);
+		goto err_device_put;
+	}
+
+	ret = platform_device_add(pdev);
+	if (ret) {
+		pr_err("%s: platform_device_register failed!\n", __func__);
+		goto err_device_put;
+	}
+
+	return 0;
+
+err_device_put:
+	platform_device_put(pdev);
+	return ret;
+}
+
+static int mipi_renesas_lcd_init(void)
+{
+	mipi_dsi_buf_alloc(&renesas_tx_buf, DSI_BUF_SIZE);
+	mipi_dsi_buf_alloc(&renesas_rx_buf, DSI_BUF_SIZE);
+
+	return platform_driver_register(&this_driver);
+}
diff --git a/drivers/video/msm/mipi_renesas.h b/drivers/video/msm/mipi_renesas.h
new file mode 100644
index 0000000..59ccfd0
--- /dev/null
+++ b/drivers/video/msm/mipi_renesas.h
@@ -0,0 +1,21 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MIPI_RENESAS_H
+#define MIPI_RENESAS_H
+
+#define RENESAS_FWVGA_TWO_LANE
+
+int mipi_renesas_device_register(struct msm_panel_info *pinfo,
+					u32 channel, u32 panel);
+
+#endif  /* MIPI_RENESAS_H */
diff --git a/drivers/video/msm/mipi_renesas_cmd_fwvga_pt.c b/drivers/video/msm/mipi_renesas_cmd_fwvga_pt.c
new file mode 100644
index 0000000..7f5ac70
--- /dev/null
+++ b/drivers/video/msm/mipi_renesas_cmd_fwvga_pt.c
@@ -0,0 +1,157 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "msm_fb.h"
+#include "mipi_dsi.h"
+#include "mipi_renesas.h"
+
+static struct msm_panel_info pinfo;
+
+static struct mipi_dsi_phy_ctrl dsi_cmd_mode_phy_db = {
+#ifdef CONFIG_FB_MSM_MDP303
+	/* DSI Bit Clock at 500 MHz, 2 lane, RGB888 */
+	/* regulator */
+	{0x03, 0x01, 0x01, 0x00},
+	/* timing   */
+	{0xb9, 0x8e, 0x1f, 0x00, 0x98, 0x9c, 0x22, 0x90,
+	0x18, 0x03, 0x04},
+	/* phy ctrl */
+	{0x7f, 0x00, 0x00, 0x00},
+	/* strength */
+	{0xbb, 0x02, 0x06, 0x00},
+	/* pll control */
+	{0x01, 0xec, 0x31, 0xd2, 0x00, 0x40, 0x37, 0x62,
+	0x01, 0x0f, 0x07,
+	0x05, 0x14, 0x03, 0x0, 0x0, 0x0, 0x20, 0x0, 0x02, 0x0},
+#else
+	/* DSI_BIT_CLK at 400MHz, 1 lane, RGB888 */
+	{0x03, 0x01, 0x01, 0x00},	/* regulator */
+	/* timing   */
+	{0x22, 0x0c, 0x7, 0x00, 0x10, 0x20, 0x10,
+	0xd, 0x8, 0x2, 0x3},
+	/* phy ctrl */
+	{0x7f, 0x00, 0x00, 0x00},
+	/* strength */
+	{0xee, 0x00, 0x6, 0x00},
+	/* pll control */
+	{0x40, 0x2f, 0xb1, 0xda, 0x00, 0x50, 0x48, 0x63,
+#if defined(RENESAS_FWVGA_TWO_LANE)
+	0x33, 0x1f, 0x07,
+#else	/* default set to 1 lane */
+	0x30, 0x07, 0x07,
+#endif
+	0x05, 0x14, 0x03, 0x0, 0x0, 0x54, 0x06, 0x10, 0x04, 0x0},
+#endif
+};
+
+static int __init mipi_cmd_renesas_fwvga_pt_init(void)
+{
+	int ret;
+
+	if (msm_fb_detect_client("mipi_cmd_renesas_fwvga"))
+		return 0;
+
+	pinfo.xres = 480;
+	pinfo.yres = 864;
+	pinfo.type = MIPI_CMD_PANEL;
+	pinfo.pdest = DISPLAY_1;
+	pinfo.wait_cycle = 0;
+	pinfo.bpp = 24;
+#ifdef CONFIG_FB_MSM_MDP303
+	pinfo.lcdc.h_back_porch = 100;
+	pinfo.lcdc.h_front_porch = 100;
+	pinfo.lcdc.h_pulse_width = 8;
+	pinfo.lcdc.v_back_porch = 20;
+	pinfo.lcdc.v_front_porch = 20;
+	pinfo.lcdc.v_pulse_width = 1;
+#else
+	pinfo.lcdc.h_front_porch = 50;
+#if defined(RENESAS_FWVGA_TWO_LANE)
+	pinfo.lcdc.h_back_porch = 400;
+	pinfo.lcdc.h_pulse_width = 5;
+	pinfo.lcdc.v_back_porch = 75;
+	pinfo.lcdc.v_front_porch = 5;
+	pinfo.lcdc.v_pulse_width = 1;
+#else
+	pinfo.lcdc.h_back_porch = 50;
+	pinfo.lcdc.h_pulse_width = 20;
+	pinfo.lcdc.v_back_porch = 10;
+	pinfo.lcdc.v_front_porch = 10;
+	pinfo.lcdc.v_pulse_width = 5;
+#endif
+
+#endif /* CONFIG_FB_MSM_MDP303 */
+	pinfo.lcdc.border_clr = 0;	/* blk */
+	pinfo.lcdc.underflow_clr = 0xff;	/* blue */
+	pinfo.lcdc.hsync_skew = 0;
+	pinfo.bl_max = 100;
+	pinfo.bl_min = 1;
+	pinfo.fb_num = 2;
+
+#ifdef CONFIG_FB_MSM_MDP303
+	pinfo.clk_rate = 499000000;
+#else
+	pinfo.clk_rate = 152000000;
+#endif
+	pinfo.lcd.refx100 = 6000; /* adjust refx100 to prevent tearing */
+
+	pinfo.mipi.mode = DSI_CMD_MODE;
+	pinfo.mipi.dst_format = DSI_CMD_DST_FORMAT_RGB888;
+	pinfo.mipi.vc = 0;
+	pinfo.mipi.rgb_swap = DSI_RGB_SWAP_RGB;
+	pinfo.mipi.data_lane0 = TRUE;
+#ifdef CONFIG_FB_MSM_MDP303
+	pinfo.lcd.vsync_enable = TRUE;
+	pinfo.lcd.hw_vsync_mode = TRUE;
+	pinfo.mipi.data_lane1 = TRUE;
+	pinfo.mipi.t_clk_post = 0x20;
+	pinfo.mipi.t_clk_pre = 0x2F;
+	pinfo.mipi.stream = 0; /* dma_p */
+	pinfo.mipi.mdp_trigger = DSI_CMD_TRIGGER_SW;
+	pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW;
+	pinfo.mipi.te_sel = 1; /* TE from vsync gpio */
+	pinfo.mipi.interleave_max = 1;
+	pinfo.mipi.insert_dcs_cmd = TRUE;
+	pinfo.mipi.wr_mem_continue = 0x3c;
+	pinfo.mipi.wr_mem_start = 0x2c;
+	pinfo.mipi.dsi_phy_db = &dsi_cmd_mode_phy_db;
+	pinfo.mipi.tx_eot_append = 0x01;
+	pinfo.mipi.rx_eot_ignore = 0;
+	pinfo.mipi.dlane_swap = 0x01;
+#else
+#if defined(RENESAS_FWVGA_TWO_LANE)
+	pinfo.mipi.data_lane1 = TRUE;
+#else
+	pinfo.mipi.data_lane1 = FALSE;
+#endif
+	pinfo.mipi.t_clk_post = 0x18;
+	pinfo.mipi.t_clk_pre = 0x14;
+	pinfo.mipi.stream = 0;	/* dma_p */
+	pinfo.mipi.mdp_trigger = DSI_CMD_TRIGGER_SW;
+	pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW;
+	pinfo.mipi.te_sel = 1; /* TE from vsycn gpio */
+	pinfo.mipi.interleave_max = 1;
+	pinfo.mipi.insert_dcs_cmd = TRUE;
+	pinfo.mipi.wr_mem_continue = 0x3c;
+	pinfo.mipi.wr_mem_start = 0x2c;
+	pinfo.mipi.dsi_phy_db = &dsi_cmd_mode_phy_db;
+#endif /* CONFIG_FB_MSM_MDP303 */
+
+	ret = mipi_renesas_device_register(&pinfo, MIPI_DSI_PRIM,
+						MIPI_DSI_PANEL_FWVGA_PT);
+	if (ret)
+		pr_err("%s: failed to register device!\n", __func__);
+
+	return ret;
+}
+
+module_init(mipi_cmd_renesas_fwvga_pt_init);
diff --git a/drivers/video/msm/mipi_renesas_video_fwvga_pt.c b/drivers/video/msm/mipi_renesas_video_fwvga_pt.c
new file mode 100644
index 0000000..e826773
--- /dev/null
+++ b/drivers/video/msm/mipi_renesas_video_fwvga_pt.c
@@ -0,0 +1,163 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "msm_fb.h"
+#include "mipi_dsi.h"
+#include "mipi_renesas.h"
+
+static struct msm_panel_info pinfo;
+
+static struct mipi_dsi_phy_ctrl dsi_video_mode_phy_db = {
+#ifdef CONFIG_FB_MSM_MDP303
+	/* DSI Bit Clock at 500 MHz, 2 lane, RGB888 */
+	/* regulator */
+	{0x03, 0x01, 0x01, 0x00},
+	/* timing   */
+	{0xb9, 0x8e, 0x1f, 0x00, 0x98, 0x9c, 0x22, 0x90,
+	0x18, 0x03, 0x04},
+	/* phy ctrl */
+	{0x7f, 0x00, 0x00, 0x00},
+	/* strength */
+	{0xbb, 0x02, 0x06, 0x00},
+	/* pll control */
+	{0x00, 0xec, 0x31, 0xd2, 0x00, 0x40, 0x37, 0x62,
+	0x01, 0x0f, 0x07,
+	0x05, 0x14, 0x03, 0x0, 0x0, 0x0, 0x20, 0x0, 0x02, 0x0},
+#else
+	/* DSI_BIT_CLK at 400MHz, 1 lane, RGB888 */
+	/* regulator */
+	{0x03, 0x01, 0x01, 0x00},
+	/* timing   */
+	{0xaa, 0x3b, 0x1b, 0x00, 0x52, 0x58, 0x20, 0x3f,
+	0x2e, 0x03, 0x04},
+	/* phy ctrl */
+	{0x7f, 0x00, 0x00, 0x00},
+	/* strength */
+	{0xee, 0x00, 0x86, 0x00},
+	/* pll control */
+	{0x40, 0xc7, 0xb0, 0xda, 0x00, 0x50, 0x48, 0x63,
+#if defined(RENESAS_FWVGA_TWO_LANE)
+	0x30, 0x07, 0x03,
+#else
+	/* default set to 1 lane */
+	0x30, 0x07, 0x07,
+#endif
+	0x05, 0x14, 0x03, 0x0, 0x0, 0x54, 0x06, 0x10, 0x04, 0x0},
+#endif
+};
+
+static int __init mipi_video_renesas_fwvga_pt_init(void)
+{
+	int ret;
+
+	if (msm_fb_detect_client("mipi_video_renesas_fwvga"))
+		return 0;
+
+	pinfo.xres = 480;
+	pinfo.yres = 864;
+	pinfo.type = MIPI_VIDEO_PANEL;
+	pinfo.pdest = DISPLAY_1;
+	pinfo.wait_cycle = 0;
+	pinfo.bpp = 24;
+#ifdef CONFIG_FB_MSM_MDP303
+	pinfo.lcdc.h_back_porch = 100;
+	pinfo.lcdc.h_front_porch = 100;
+	pinfo.lcdc.h_pulse_width = 8;
+	pinfo.lcdc.v_back_porch = 20;
+	pinfo.lcdc.v_front_porch = 20;
+	pinfo.lcdc.v_pulse_width = 1;
+	pinfo.clk_rate = 499000000;
+#else
+
+#if defined(RENESAS_FWVGA_TWO_LANE)
+	pinfo.lcdc.h_back_porch = 400;
+#else
+	pinfo.lcdc.h_back_porch = 50;
+#endif
+	pinfo.lcdc.h_front_porch = 50;
+
+#if defined(RENESAS_FWVGA_TWO_LANE)
+	pinfo.lcdc.h_pulse_width = 5;
+#else
+	pinfo.lcdc.h_pulse_width = 20;
+#endif
+
+#if defined(RENESAS_FWVGA_TWO_LANE)
+	pinfo.lcdc.v_back_porch = 75;
+	pinfo.lcdc.v_front_porch = 5;
+	pinfo.lcdc.v_pulse_width = 1;
+#else
+	pinfo.lcdc.v_back_porch = 10;
+	pinfo.lcdc.v_front_porch = 10;
+	pinfo.lcdc.v_pulse_width = 5;
+#endif
+
+#endif
+	pinfo.lcdc.border_clr = 0;	/* blk */
+	pinfo.lcdc.underflow_clr = 0xff;	/* blue */
+	pinfo.lcdc.hsync_skew = 0;
+	pinfo.bl_max = 100;
+	pinfo.bl_min = 1;
+	pinfo.fb_num = 2;
+
+	pinfo.mipi.mode = DSI_VIDEO_MODE;
+	pinfo.mipi.pulse_mode_hsa_he = TRUE;
+	pinfo.mipi.hfp_power_stop = TRUE;
+	pinfo.mipi.hbp_power_stop = TRUE;
+	pinfo.mipi.hsa_power_stop = TRUE;
+	pinfo.mipi.eof_bllp_power_stop = TRUE;
+	pinfo.mipi.bllp_power_stop = TRUE;
+#ifdef CONFIG_FB_MSM_MDP303
+	pinfo.mipi.traffic_mode = DSI_BURST_MODE;
+	pinfo.mipi.dst_format =  DSI_VIDEO_DST_FORMAT_RGB888;
+	pinfo.mipi.vc = 0;
+	pinfo.mipi.rgb_swap = DSI_RGB_SWAP_RGB;
+	pinfo.mipi.data_lane0 = TRUE;
+	pinfo.mipi.data_lane1 = TRUE;
+	pinfo.mipi.t_clk_post = 0x20;
+	pinfo.mipi.t_clk_pre = 0x2F;
+	pinfo.mipi.stream = 0; /* dma_p */
+	pinfo.mipi.mdp_trigger = DSI_CMD_TRIGGER_NONE;
+	pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW;
+	pinfo.mipi.frame_rate = 60;
+	pinfo.mipi.dsi_phy_db = &dsi_video_mode_phy_db;
+	pinfo.mipi.dlane_swap = 0x01;
+	pinfo.mipi.tx_eot_append = 0x01;
+#else
+	pinfo.mipi.traffic_mode = DSI_NON_BURST_SYNCH_PULSE;
+	pinfo.mipi.dst_format = DSI_VIDEO_DST_FORMAT_RGB888;
+	pinfo.mipi.vc = 0;
+	pinfo.mipi.rgb_swap = DSI_RGB_SWAP_BGR;
+	pinfo.mipi.data_lane0 = TRUE;
+#if defined(RENESAS_FWVGA_TWO_LANE)
+	pinfo.mipi.data_lane1 = TRUE;
+#else
+	pinfo.mipi.data_lane1 = FALSE;
+#endif
+	pinfo.mipi.t_clk_post = 0x03;
+	pinfo.mipi.t_clk_pre = 0x24;
+	pinfo.mipi.stream = 0; /* dma_p */
+	pinfo.mipi.mdp_trigger = DSI_CMD_TRIGGER_SW;
+	pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW;
+	pinfo.mipi.frame_rate = 60;
+	pinfo.mipi.dsi_phy_db = &dsi_video_mode_phy_db;
+#endif
+
+	ret = mipi_renesas_device_register(&pinfo, MIPI_DSI_PRIM,
+						MIPI_DSI_PANEL_FWVGA_PT);
+	if (ret)
+		pr_err("%s: failed to register device!\n", __func__);
+
+	return ret;
+}
+
+module_init(mipi_video_renesas_fwvga_pt_init);
diff --git a/drivers/video/msm/mipi_simulator.c b/drivers/video/msm/mipi_simulator.c
new file mode 100644
index 0000000..c6bf534
--- /dev/null
+++ b/drivers/video/msm/mipi_simulator.c
@@ -0,0 +1,167 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "msm_fb.h"
+#include "mipi_dsi.h"
+#include "mipi_simulator.h"
+
+static struct dsi_buf simulator_tx_buf;
+static struct dsi_buf simulator_rx_buf;
+static struct msm_panel_common_pdata *mipi_simulator_pdata;
+
+static int mipi_simulator_lcd_init(void);
+
+static char display_on[2]  = {0x00, 0x00};
+static char display_off[2] = {0x00, 0x00};
+
+static struct dsi_cmd_desc display_on_cmds[] = {
+		{DTYPE_PERIPHERAL_ON, 1, 0, 0, 0, sizeof(display_on),
+				display_on}
+};
+static struct dsi_cmd_desc display_off_cmds[] = {
+		{DTYPE_PERIPHERAL_OFF, 1, 0, 0, 0, sizeof(display_off),
+				display_off}
+};
+
+static int mipi_simulator_lcd_on(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+	struct mipi_panel_info *mipi;
+
+	mfd = platform_get_drvdata(pdev);
+	mipi  = &mfd->panel_info.mipi;
+
+	if (!mfd)
+		return -ENODEV;
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	pr_debug("%s:%d, debug info (mode) : %d", __func__, __LINE__,
+		 mipi->mode);
+
+	if (mipi->mode == DSI_VIDEO_MODE) {
+		mipi_dsi_cmds_tx(mfd, &simulator_tx_buf, display_on_cmds,
+			ARRAY_SIZE(display_on_cmds));
+	} else {
+		pr_err("%s:%d, CMD MODE NOT SUPPORTED", __func__, __LINE__);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int mipi_simulator_lcd_off(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+	struct mipi_panel_info *mipi;
+
+	mfd = platform_get_drvdata(pdev);
+	mipi  = &mfd->panel_info.mipi;
+
+	if (!mfd)
+		return -ENODEV;
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	pr_debug("%s:%d, debug info", __func__, __LINE__);
+
+	if (mipi->mode == DSI_VIDEO_MODE) {
+		mipi_dsi_cmds_tx(mfd, &simulator_tx_buf, display_off_cmds,
+			ARRAY_SIZE(display_off_cmds));
+	} else {
+		pr_debug("%s:%d, DONT REACH HERE", __func__, __LINE__);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int __devinit mipi_simulator_lcd_probe(struct platform_device *pdev)
+{
+	if (pdev->id == 0) {
+		mipi_simulator_pdata = pdev->dev.platform_data;
+		return 0;
+	}
+	pr_debug("%s:%d, debug info", __func__, __LINE__);
+
+	msm_fb_add_device(pdev);
+
+	return 0;
+}
+
+static struct platform_driver this_driver = {
+	.probe  = mipi_simulator_lcd_probe,
+	.driver = {
+		.name   = "mipi_simulator",
+	},
+};
+
+static struct msm_fb_panel_data simulator_panel_data = {
+	.on		= mipi_simulator_lcd_on,
+	.off		= mipi_simulator_lcd_off,
+};
+
+static int ch_used[3];
+
+int mipi_simulator_device_register(struct msm_panel_info *pinfo,
+					u32 channel, u32 panel)
+{
+	struct platform_device *pdev = NULL;
+	int ret;
+
+	if ((channel >= 3) || ch_used[channel])
+		return -ENODEV;
+
+	ch_used[channel] = TRUE;
+
+	pr_debug("%s:%d, debug info", __func__, __LINE__);
+	ret = mipi_simulator_lcd_init();
+	if (ret) {
+		pr_err("mipi_simulator_lcd_init() failed with ret %u\n", ret);
+		return ret;
+	}
+
+	pdev = platform_device_alloc("mipi_simulator", (panel << 8)|channel);
+	if (!pdev)
+		return -ENOMEM;
+
+	simulator_panel_data.panel_info = *pinfo;
+
+	ret = platform_device_add_data(pdev, &simulator_panel_data,
+		sizeof(simulator_panel_data));
+	if (ret) {
+		pr_err(KERN_ERR
+		  "%s: platform_device_add_data failed!\n", __func__);
+		goto err_device_put;
+	}
+
+	ret = platform_device_add(pdev);
+	if (ret) {
+		pr_err(KERN_ERR
+		  "%s: platform_device_register failed!\n", __func__);
+		goto err_device_put;
+	}
+
+	return 0;
+
+err_device_put:
+	platform_device_put(pdev);
+	return ret;
+}
+
+static int mipi_simulator_lcd_init(void)
+{
+	mipi_dsi_buf_alloc(&simulator_tx_buf, DSI_BUF_SIZE);
+	mipi_dsi_buf_alloc(&simulator_rx_buf, DSI_BUF_SIZE);
+
+	return platform_driver_register(&this_driver);
+}
diff --git a/drivers/video/msm/mipi_simulator.h b/drivers/video/msm/mipi_simulator.h
new file mode 100644
index 0000000..274ce8f
--- /dev/null
+++ b/drivers/video/msm/mipi_simulator.h
@@ -0,0 +1,19 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MIPI_SIMULATOR_H
+#define MIPI_SIMULATOR_H
+
+int mipi_simulator_device_register(struct msm_panel_info *pinfo,
+					u32 channel, u32 panel);
+
+#endif  /* MIPI_SIMULATOR_H */
diff --git a/drivers/video/msm/mipi_simulator_video.c b/drivers/video/msm/mipi_simulator_video.c
new file mode 100644
index 0000000..845df75
--- /dev/null
+++ b/drivers/video/msm/mipi_simulator_video.c
@@ -0,0 +1,86 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "msm_fb.h"
+#include "mipi_dsi.h"
+#include "mipi_simulator.h"
+
+static struct msm_panel_info pinfo;
+
+static struct mipi_dsi_phy_ctrl dsi_video_mode_phy_db = {
+	{0x03, 0x01, 0x01, 0x00},
+	{0xaa, 0x3b, 0x1b, 0x00, 0x52, 0x58, 0x20, 0x3f,
+		0x2e, 0x03, 0x04},
+	{0x7f, 0x00, 0x00, 0x00},
+	{0xee, 0x00, 0x86, 0x00},
+	{0x40, 0xc7, 0xb0, 0xda, 0x00, 0x50, 0x48, 0x63,
+		0x30, 0x07, 0x03,
+		0x05, 0x14, 0x03, 0x0, 0x0, 0x54, 0x06, 0x10, 0x04, 0x0},
+};
+
+static int __init mipi_video_simulator_init(void)
+{
+	int ret;
+
+	if (msm_fb_detect_client("mipi_video_simulator_vga"))
+		return 0;
+	pinfo.xres = 640;
+	pinfo.yres = 480;
+	pinfo.type = MIPI_VIDEO_PANEL;
+	pinfo.pdest = DISPLAY_1;
+	pinfo.wait_cycle = 0;
+	pinfo.bpp = 24;
+
+	pinfo.lcdc.h_back_porch  = 6;
+	pinfo.lcdc.h_front_porch = 6;
+	pinfo.lcdc.h_pulse_width = 2;
+	pinfo.lcdc.v_back_porch  = 6;
+	pinfo.lcdc.v_front_porch = 6;
+	pinfo.lcdc.v_pulse_width = 2;
+
+	pinfo.lcdc.border_clr = 0;	/* blk */
+	pinfo.lcdc.underflow_clr = 0xff;	/* blue */
+	pinfo.lcdc.hsync_skew = 0;
+	pinfo.bl_max = 15;
+	pinfo.bl_min = 1;
+	pinfo.fb_num = 2;
+
+	pinfo.mipi.mode = DSI_VIDEO_MODE;
+	pinfo.mipi.pulse_mode_hsa_he = TRUE;
+	pinfo.mipi.hfp_power_stop = TRUE;
+	pinfo.mipi.hbp_power_stop = TRUE;
+	pinfo.mipi.hsa_power_stop = TRUE;
+	pinfo.mipi.eof_bllp_power_stop = TRUE;
+	pinfo.mipi.bllp_power_stop = TRUE;
+	pinfo.mipi.traffic_mode = DSI_NON_BURST_SYNCH_PULSE;
+	pinfo.mipi.dst_format = DSI_VIDEO_DST_FORMAT_RGB888;
+	pinfo.mipi.vc = 0;
+	pinfo.mipi.rgb_swap = DSI_RGB_SWAP_RGB;
+	pinfo.mipi.data_lane0 = TRUE;
+	pinfo.mipi.data_lane1 = TRUE;
+	pinfo.mipi.t_clk_post = 0x03;
+	pinfo.mipi.t_clk_pre = 0x24;
+	pinfo.mipi.stream = 0; /* dma_p */
+	pinfo.mipi.mdp_trigger = DSI_CMD_TRIGGER_SW;
+	pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW;
+	pinfo.mipi.frame_rate = 60;
+	pinfo.mipi.dsi_phy_db = &dsi_video_mode_phy_db;
+
+	ret = mipi_simulator_device_register(&pinfo, MIPI_DSI_PRIM,
+		MIPI_DSI_PANEL_VGA);
+	if (ret)
+		pr_err("%s: failed to register device!\n", __func__);
+
+		return ret;
+}
+
+module_init(mipi_video_simulator_init);
diff --git a/drivers/video/msm/mipi_tc358764_dsi2lvds.c b/drivers/video/msm/mipi_tc358764_dsi2lvds.c
new file mode 100644
index 0000000..f7f353f
--- /dev/null
+++ b/drivers/video/msm/mipi_tc358764_dsi2lvds.c
@@ -0,0 +1,997 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*
+ * Toshiba MIPI-DSI-to-LVDS Bridge driver.
+ * Device Model TC358764XBG/65XBG.
+ * Reference document: TC358764XBG_65XBG_V119.pdf
+ *
+ * The Host sends a DSI Generic Long Write packet (Data ID = 0x29) over the
+ * DSI link for each write access transaction to the chip configuration
+ * registers.
+ * Payload of this packet is 16-bit register address and 32-bit data.
+ * Multiple data values are allowed for sequential addresses.
+ *
+ * The Host sends a DSI Generic Read packet (Data ID = 0x24) over the DSI
+ * link for each read request transaction to the chip configuration
+ * registers. Payload of this packet is further defined as follows:
+ * 16-bit address followed by a 32-bit value (Generic Long Read Response
+ * packet).
+ *
+ * The bridge supports 5 GPIO lines controlled via the GPC register.
+ *
+ * The bridge support I2C Master/Slave.
+ * The I2C slave can be used for read/write to the bridge register instead of
+ * using the DSI interface.
+ * I2C slave address is 0x0F (read/write 0x1F/0x1E).
+ * The I2C Master can be used for communication with the panel if
+ * it has an I2C slave.
+ *
+ * NOTE: The I2C interface is not used in this driver.
+ * Only the DSI interface is used for read/write the bridge registers.
+ *
+ * Pixel data can be transmitted in non-burst or burst fashion.
+ * Non-burst refers to pixel data packet transmission time on DSI link
+ * being roughly the same (to account for packet overhead time)
+ * as active video line time on LVDS output (i.e. DE = 1).
+ * And burst refers to pixel data packet transmission time on DSI link
+ * being less than the active video line time on LVDS output.
+ * Video mode transmission is further differentiated by the types of
+ * timing events being transmitted.
+ * Video pulse mode refers to the case where both sync start and sync end
+ * events (for frame and line) are transmitted.
+ * Video event mode refers to the case where only sync start events
+ * are transmitted.
+ * This is configured via register bit VPCTRL.EVTMODE.
+ *
+ */
+
+/* #define DEBUG 1 */
+
+/**
+ * Use the I2C master to control the panel.
+ */
+/* #define TC358764_USE_I2C_MASTER */
+
+#define DRV_NAME "mipi_tc358764"
+
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/pwm.h>
+#include <linux/gpio.h>
+#include "msm_fb.h"
+#include "mdp4.h"
+#include "mipi_dsi.h"
+#include "mipi_tc358764_dsi2lvds.h"
+
+/* Registers definition */
+
+/* DSI D-PHY Layer Registers */
+#define D0W_DPHYCONTTX	0x0004	/* Data Lane 0 DPHY Tx Control */
+#define CLW_DPHYCONTRX	0x0020	/* Clock Lane DPHY Rx Control */
+#define D0W_DPHYCONTRX	0x0024	/* Data Lane 0 DPHY Rx Control */
+#define D1W_DPHYCONTRX	0x0028	/* Data Lane 1 DPHY Rx Control */
+#define D2W_DPHYCONTRX	0x002C	/* Data Lane 2 DPHY Rx Control */
+#define D3W_DPHYCONTRX	0x0030	/* Data Lane 3 DPHY Rx Control */
+#define COM_DPHYCONTRX	0x0038	/* DPHY Rx Common Control */
+#define CLW_CNTRL	0x0040	/* Clock Lane Control */
+#define D0W_CNTRL	0x0044	/* Data Lane 0 Control */
+#define D1W_CNTRL	0x0048	/* Data Lane 1 Control */
+#define D2W_CNTRL	0x004C	/* Data Lane 2 Control */
+#define D3W_CNTRL	0x0050	/* Data Lane 3 Control */
+#define DFTMODE_CNTRL	0x0054	/* DFT Mode Control */
+
+/* DSI PPI Layer Registers */
+#define PPI_STARTPPI	0x0104	/* START control bit of PPI-TX function. */
+#define PPI_BUSYPPI	0x0108
+#define PPI_LINEINITCNT	0x0110	/* Line Initialization Wait Counter  */
+#define PPI_LPTXTIMECNT	0x0114
+#define PPI_LANEENABLE	0x0134	/* Enables each lane at the PPI layer. */
+#define PPI_TX_RX_TA	0x013C	/* DSI Bus Turn Around timing parameters */
+
+/* Analog timer function enable */
+#define PPI_CLS_ATMR	0x0140	/* Delay for Clock Lane in LPRX  */
+#define PPI_D0S_ATMR	0x0144	/* Delay for Data Lane 0 in LPRX */
+#define PPI_D1S_ATMR	0x0148	/* Delay for Data Lane 1 in LPRX */
+#define PPI_D2S_ATMR	0x014C	/* Delay for Data Lane 2 in LPRX */
+#define PPI_D3S_ATMR	0x0150	/* Delay for Data Lane 3 in LPRX */
+#define PPI_D0S_CLRSIPOCOUNT	0x0164
+
+#define PPI_D1S_CLRSIPOCOUNT	0x0168	/* For lane 1 */
+#define PPI_D2S_CLRSIPOCOUNT	0x016C	/* For lane 2 */
+#define PPI_D3S_CLRSIPOCOUNT	0x0170	/* For lane 3 */
+
+#define CLS_PRE		0x0180	/* Digital Counter inside of PHY IO */
+#define D0S_PRE		0x0184	/* Digital Counter inside of PHY IO */
+#define D1S_PRE		0x0188	/* Digital Counter inside of PHY IO */
+#define D2S_PRE		0x018C	/* Digital Counter inside of PHY IO */
+#define D3S_PRE		0x0190	/* Digital Counter inside of PHY IO */
+#define CLS_PREP	0x01A0	/* Digital Counter inside of PHY IO */
+#define D0S_PREP	0x01A4	/* Digital Counter inside of PHY IO */
+#define D1S_PREP	0x01A8	/* Digital Counter inside of PHY IO */
+#define D2S_PREP	0x01AC	/* Digital Counter inside of PHY IO */
+#define D3S_PREP	0x01B0	/* Digital Counter inside of PHY IO */
+#define CLS_ZERO	0x01C0	/* Digital Counter inside of PHY IO */
+#define D0S_ZERO	0x01C4	/* Digital Counter inside of PHY IO */
+#define D1S_ZERO	0x01C8	/* Digital Counter inside of PHY IO */
+#define D2S_ZERO	0x01CC	/* Digital Counter inside of PHY IO */
+#define D3S_ZERO	0x01D0	/* Digital Counter inside of PHY IO */
+
+#define PPI_CLRFLG	0x01E0	/* PRE Counters has reached set values */
+#define PPI_CLRSIPO	0x01E4	/* Clear SIPO values, Slave mode use only. */
+#define HSTIMEOUT	0x01F0	/* HS Rx Time Out Counter */
+#define HSTIMEOUTENABLE	0x01F4	/* Enable HS Rx Time Out Counter */
+#define DSI_STARTDSI	0x0204	/* START control bit of DSI-TX function */
+#define DSI_BUSYDSI	0x0208
+#define DSI_LANEENABLE	0x0210	/* Enables each lane at the Protocol layer. */
+#define DSI_LANESTATUS0	0x0214	/* Displays lane is in HS RX mode. */
+#define DSI_LANESTATUS1	0x0218	/* Displays lane is in ULPS or STOP state */
+
+#define DSI_INTSTATUS	0x0220	/* Interrupt Status */
+#define DSI_INTMASK	0x0224	/* Interrupt Mask */
+#define DSI_INTCLR	0x0228	/* Interrupt Clear */
+#define DSI_LPTXTO	0x0230	/* Low Power Tx Time Out Counter */
+
+#define DSIERRCNT	0x0300	/* DSI Error Count */
+#define APLCTRL		0x0400	/* Application Layer Control */
+#define RDPKTLN		0x0404	/* Command Read Packet Length */
+#define VPCTRL		0x0450	/* Video Path Control */
+#define HTIM1		0x0454	/* Horizontal Timing Control 1 */
+#define HTIM2		0x0458	/* Horizontal Timing Control 2 */
+#define VTIM1		0x045C	/* Vertical Timing Control 1 */
+#define VTIM2		0x0460	/* Vertical Timing Control 2 */
+#define VFUEN		0x0464	/* Video Frame Timing Update Enable */
+
+/* Mux Input Select for LVDS LINK Input */
+#define LVMX0003	0x0480	/* Bit 0 to 3 */
+#define LVMX0407	0x0484	/* Bit 4 to 7 */
+#define LVMX0811	0x0488	/* Bit 8 to 11 */
+#define LVMX1215	0x048C	/* Bit 12 to 15 */
+#define LVMX1619	0x0490	/* Bit 16 to 19 */
+#define LVMX2023	0x0494	/* Bit 20 to 23 */
+#define LVMX2427	0x0498	/* Bit 24 to 27 */
+
+#define LVCFG		0x049C	/* LVDS Configuration  */
+#define LVPHY0		0x04A0	/* LVDS PHY 0 */
+#define LVPHY1		0x04A4	/* LVDS PHY 1 */
+#define SYSSTAT		0x0500	/* System Status  */
+#define SYSRST		0x0504	/* System Reset  */
+
+/* GPIO Registers */
+#define GPIOC		0x0520	/* GPIO Control  */
+#define GPIOO		0x0524	/* GPIO Output  */
+#define GPIOI		0x0528	/* GPIO Input  */
+
+/* I2C Registers */
+#define I2CTIMCTRL	0x0540	/* I2C IF Timing and Enable Control */
+#define I2CMADDR	0x0544	/* I2C Master Addressing */
+#define WDATAQ		0x0548	/* Write Data Queue */
+#define RDATAQ		0x054C	/* Read Data Queue */
+
+/* Chip ID and Revision ID Register */
+#define IDREG		0x0580
+
+#define TC358764XBG_ID	0x00006500
+
+/* Debug Registers */
+#define DEBUG00		0x05A0	/* Debug */
+#define DEBUG01		0x05A4	/* LVDS Data */
+
+/* PWM */
+static u32 d2l_pwm_freq_hz = (3.921*1000);
+
+#define PWM_FREQ_HZ	(d2l_pwm_freq_hz)
+#define PWM_PERIOD_USEC (USEC_PER_SEC / PWM_FREQ_HZ)
+#define PWM_DUTY_LEVEL (PWM_PERIOD_USEC / PWM_LEVEL)
+
+#define CMD_DELAY 100
+#define DSI_MAX_LANES 4
+#define KHZ 1000
+#define MHZ (1000*1000)
+
+/**
+ * Command payload for DTYPE_GEN_LWRITE (0x29) / DTYPE_GEN_READ2 (0x24).
+ */
+struct wr_cmd_payload {
+	u16 addr;
+	u32 data;
+} __packed;
+
+/*
+ * Driver state.
+ */
+static struct msm_panel_common_pdata *d2l_common_pdata;
+struct msm_fb_data_type *d2l_mfd;
+static struct dsi_buf d2l_tx_buf;
+static struct dsi_buf d2l_rx_buf;
+static int led_pwm;
+static struct pwm_device *bl_pwm;
+static struct pwm_device *tn_pwm;
+static int bl_level;
+static u32 d2l_gpio_out_mask;
+static u32 d2l_gpio_out_val;
+static u32 d2l_3d_gpio_enable;
+static u32 d2l_3d_gpio_mode;
+static int d2l_enable_3d;
+static struct i2c_client *d2l_i2c_client;
+static struct i2c_driver d2l_i2c_slave_driver;
+
+static int mipi_d2l_init(void);
+static int mipi_d2l_enable_3d(struct msm_fb_data_type *mfd,
+			      bool enable, bool mode);
+static u32 d2l_i2c_read_reg(struct i2c_client *client, u16 reg);
+static u32 d2l_i2c_write_reg(struct i2c_client *client, u16 reg, u32 val);
+
+/**
+ * Read a bridge register
+ *
+ * @param mfd
+ *
+ * @return register data value
+ */
+static u32 mipi_d2l_read_reg(struct msm_fb_data_type *mfd, u16 reg)
+{
+	u32 data;
+	int len = 4;
+	struct dsi_cmd_desc cmd_read_reg = {
+		DTYPE_GEN_READ2, 1, 0, 1, 0, /* cmd 0x24 */
+			sizeof(reg), (char *) &reg};
+
+	mipi_dsi_buf_init(&d2l_tx_buf);
+	mipi_dsi_buf_init(&d2l_rx_buf);
+
+	/* mutex had been acquired at mipi_dsi_on */
+	len = mipi_dsi_cmds_rx(mfd, &d2l_tx_buf, &d2l_rx_buf,
+			       &cmd_read_reg, len);
+
+	data = *(u32 *)d2l_rx_buf.data;
+
+	if (len != 4)
+		pr_err("%s: invalid rlen=%d, expecting 4.\n", __func__, len);
+
+	pr_debug("%s: reg=0x%x.data=0x%08x.\n", __func__, reg, data);
+
+	return data;
+}
+
+/**
+ * Write a bridge register
+ *
+ * @param mfd
+ *
+ * @return int
+ */
+static int mipi_d2l_write_reg(struct msm_fb_data_type *mfd, u16 reg, u32 data)
+{
+	struct wr_cmd_payload payload;
+	struct dsi_cmd_desc cmd_write_reg = {
+		DTYPE_GEN_LWRITE, 1, 0, 0, 0,
+			sizeof(payload), (char *)&payload};
+
+	payload.addr = reg;
+	payload.data = data;
+
+	/* mutex had been acquired at mipi_dsi_on */
+	mipi_dsi_cmds_tx(mfd, &d2l_tx_buf, &cmd_write_reg, 1);
+
+	pr_debug("%s: reg=0x%x. data=0x%x.\n", __func__, reg, data);
+
+	return 0;
+}
+
+static void mipi_d2l_read_status(struct msm_fb_data_type *mfd)
+{
+	mipi_d2l_read_reg(mfd, DSI_LANESTATUS0);	/* 0x214 */
+	mipi_d2l_read_reg(mfd, DSI_LANESTATUS1);	/* 0x218 */
+	mipi_d2l_read_reg(mfd, DSI_INTSTATUS);		/* 0x220 */
+	mipi_d2l_read_reg(mfd, SYSSTAT);		/* 0x500 */
+}
+
+static void mipi_d2l_read_status_via_i2c(struct i2c_client *client)
+{
+	u32 tmp = 0;
+
+	tmp = d2l_i2c_read_reg(client, DSIERRCNT);
+	d2l_i2c_write_reg(client, DSIERRCNT, 0xFFFF0000);
+
+	d2l_i2c_read_reg(client, DSI_LANESTATUS0);	/* 0x214 */
+	d2l_i2c_read_reg(client, DSI_LANESTATUS1);	/* 0x218 */
+	d2l_i2c_read_reg(client, DSI_INTSTATUS);	/* 0x220 */
+	d2l_i2c_read_reg(client, SYSSTAT);		/* 0x500 */
+
+	d2l_i2c_write_reg(client, DSIERRCNT, tmp);
+}
+/**
+ * Init the D2L bridge via the DSI interface for Video.
+ *
+ * VPCTRL.EVTMODE (0x20) configuration bit is needed to determine whether
+ * video timing information is delivered in pulse mode or event mode.
+ * In pulse mode, both Sync Start and End packets are required.
+ * In event mode, only Sync Start packets are required.
+ *
+ * @param mfd
+ *
+ * @return int
+ */
+static int mipi_d2l_dsi_init_sequence(struct msm_fb_data_type *mfd)
+{
+	struct mipi_panel_info *mipi = &mfd->panel_info.mipi;
+	u32 lanes_enable;
+	u32 vpctrl;
+	u32 htime1;
+	u32 vtime1;
+	u32 htime2;
+	u32 vtime2;
+	u32 ppi_tx_rx_ta; /* BTA Bus-Turn-Around */
+	u32 lvcfg;
+	u32 hbpr;	/* Horizontal Back Porch */
+	u32 hpw;	/* Horizontal Pulse Width */
+	u32 vbpr;	/* Vertical Back Porch */
+	u32 vpw;	/* Vertical Pulse Width */
+
+	u32 hfpr;	/* Horizontal Front Porch */
+	u32 hsize;	/* Horizontal Active size */
+	u32 vfpr;	/* Vertical Front Porch */
+	u32 vsize;	/* Vertical Active size */
+	bool vesa_rgb888 = false;
+
+	lanes_enable = 0x01; /* clock-lane enable */
+	lanes_enable |= (mipi->data_lane0 << 1);
+	lanes_enable |= (mipi->data_lane1 << 2);
+	lanes_enable |= (mipi->data_lane2 << 3);
+	lanes_enable |= (mipi->data_lane3 << 4);
+
+	if (mipi->traffic_mode == DSI_NON_BURST_SYNCH_EVENT)
+		vpctrl = 0x01000120;
+	else if (mipi->traffic_mode == DSI_NON_BURST_SYNCH_PULSE)
+		vpctrl = 0x01000100;
+	else {
+		pr_err("%s.unsupported traffic_mode %d.\n",
+		       __func__, mipi->traffic_mode);
+		return -EINVAL;
+	}
+
+	if (mfd->panel_info.clk_rate > 800*1000*1000) {
+		pr_err("%s.unsupported clk_rate %d.\n",
+		       __func__, mfd->panel_info.clk_rate);
+		return -EINVAL;
+	}
+
+	pr_debug("%s.xres=%d.yres=%d.fps=%d.dst_format=%d.\n",
+		__func__,
+		 mfd->panel_info.xres,
+		 mfd->panel_info.yres,
+		 mfd->panel_info.mipi.frame_rate,
+		 mfd->panel_info.mipi.dst_format);
+
+	hbpr = mfd->panel_info.lcdc.h_back_porch;
+	hpw	= mfd->panel_info.lcdc.h_pulse_width;
+	vbpr = mfd->panel_info.lcdc.v_back_porch;
+	vpw	= mfd->panel_info.lcdc.v_pulse_width;
+
+	htime1 = (hbpr << 16) + hpw;
+	vtime1 = (vbpr << 16) + vpw;
+
+	hfpr = mfd->panel_info.lcdc.h_front_porch;
+	hsize = mfd->panel_info.xres;
+	vfpr = mfd->panel_info.lcdc.v_front_porch;
+	vsize = mfd->panel_info.yres;
+
+	htime2 = (hfpr << 16) + hsize;
+	vtime2 = (vfpr << 16) + vsize;
+
+	lvcfg = 0x0003; /* PCLK=DCLK/3, Dual Link, LVEN */
+	vpctrl = 0x01000120; /* Output RGB888 , Event-Mode , */
+	ppi_tx_rx_ta = 0x00040004;
+
+	if (mfd->panel_info.xres == 1366) {
+		ppi_tx_rx_ta = 0x00040004;
+		lvcfg = 0x01; /* LVEN */
+		vesa_rgb888 = true;
+	}
+
+	if (mfd->panel_info.xres == 1200) {
+		lvcfg = 0x0103; /* PCLK=DCLK/4, Dual Link, LVEN */
+		vesa_rgb888 = true;
+	}
+
+	pr_debug("%s.htime1=0x%x.\n", __func__, htime1);
+	pr_debug("%s.vtime1=0x%x.\n", __func__, vtime1);
+	pr_debug("%s.vpctrl=0x%x.\n", __func__, vpctrl);
+	pr_debug("%s.lvcfg=0x%x.\n", __func__, lvcfg);
+
+	mipi_d2l_write_reg(mfd, SYSRST, 0xFF);
+	msleep(30);
+
+	if (vesa_rgb888) {
+		/* VESA format instead of JEIDA format for RGB888 */
+		mipi_d2l_write_reg(mfd, LVMX0003, 0x03020100);
+		mipi_d2l_write_reg(mfd, LVMX0407, 0x08050704);
+		mipi_d2l_write_reg(mfd, LVMX0811, 0x0F0E0A09);
+		mipi_d2l_write_reg(mfd, LVMX1215, 0x100D0C0B);
+		mipi_d2l_write_reg(mfd, LVMX1619, 0x12111716);
+		mipi_d2l_write_reg(mfd, LVMX2023, 0x1B151413);
+		mipi_d2l_write_reg(mfd, LVMX2427, 0x061A1918);
+	}
+
+	mipi_d2l_write_reg(mfd, PPI_TX_RX_TA, ppi_tx_rx_ta); /* BTA */
+	mipi_d2l_write_reg(mfd, PPI_LPTXTIMECNT, 0x00000004);
+	mipi_d2l_write_reg(mfd, PPI_D0S_CLRSIPOCOUNT, 0x00000003);
+	mipi_d2l_write_reg(mfd, PPI_D1S_CLRSIPOCOUNT, 0x00000003);
+	mipi_d2l_write_reg(mfd, PPI_D2S_CLRSIPOCOUNT, 0x00000003);
+	mipi_d2l_write_reg(mfd, PPI_D3S_CLRSIPOCOUNT, 0x00000003);
+	mipi_d2l_write_reg(mfd, PPI_LANEENABLE, lanes_enable);
+	mipi_d2l_write_reg(mfd, DSI_LANEENABLE, lanes_enable);
+	mipi_d2l_write_reg(mfd, PPI_STARTPPI, 0x00000001);
+	mipi_d2l_write_reg(mfd, DSI_STARTDSI, 0x00000001);
+
+	mipi_d2l_write_reg(mfd, VPCTRL, vpctrl); /* RGB888 + Event mode */
+	mipi_d2l_write_reg(mfd, HTIM1, htime1);
+	mipi_d2l_write_reg(mfd, VTIM1, vtime1);
+	mipi_d2l_write_reg(mfd, HTIM2, htime2);
+	mipi_d2l_write_reg(mfd, VTIM2, vtime2);
+	mipi_d2l_write_reg(mfd, VFUEN, 0x00000001);
+	mipi_d2l_write_reg(mfd, LVCFG, lvcfg); /* Enables LVDS tx */
+
+	return 0;
+}
+
+/**
+ * Set Backlight level.
+ *
+ * @param pwm
+ * @param level
+ *
+ * @return int
+ */
+static int mipi_d2l_set_backlight_level(struct pwm_device *pwm, int level)
+{
+	int ret = 0;
+
+	pr_debug("%s: level=%d.\n", __func__, level);
+
+	if ((pwm == NULL) || (level > PWM_LEVEL) || (level < 0)) {
+		pr_err("%s.pwm=NULL.\n", __func__);
+		return -EINVAL;
+	}
+
+	ret = pwm_config(pwm, PWM_DUTY_LEVEL * level, PWM_PERIOD_USEC);
+	if (ret) {
+		pr_err("%s: pwm_config() failed err=%d.\n", __func__, ret);
+		return ret;
+	}
+
+	ret = pwm_enable(pwm);
+	if (ret) {
+		pr_err("%s: pwm_enable() failed err=%d\n",
+		       __func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * Set TN CLK.
+ *
+ * @param pwm
+ * @param level
+ *
+ * @return int
+ */
+static int mipi_d2l_set_tn_clk(struct pwm_device *pwm, u32 usec)
+{
+	int ret = 0;
+
+	pr_debug("%s: usec=%d.\n", __func__, usec);
+
+	ret = pwm_config(pwm, usec/2 , usec);
+	if (ret) {
+		pr_err("%s: pwm_config() failed err=%d.\n", __func__, ret);
+		return ret;
+	}
+
+	ret = pwm_enable(pwm);
+	if (ret) {
+		pr_err("%s: pwm_enable() failed err=%d\n",
+		       __func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * LCD ON.
+ *
+ * Set LCD On via MIPI interface or I2C-Slave interface.
+ * Set Backlight on.
+ *
+ * @param pdev
+ *
+ * @return int
+ */
+static int mipi_d2l_lcd_on(struct platform_device *pdev)
+{
+	int ret = 0;
+	u32 chip_id;
+	struct msm_fb_data_type *mfd;
+
+	pr_info("%s.\n", __func__);
+
+	/* wait for valid clock before sending data over DSI or I2C. */
+	msleep(30);
+
+	mfd = platform_get_drvdata(pdev);
+	d2l_mfd = mfd;
+
+	if (!mfd)
+		return -ENODEV;
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	chip_id = mipi_d2l_read_reg(mfd, IDREG);
+
+
+	if (chip_id != TC358764XBG_ID) {
+		pr_err("%s: invalid chip_id=0x%x", __func__, chip_id);
+		return -ENODEV;
+	}
+
+	ret = mipi_d2l_dsi_init_sequence(mfd);
+	if (ret)
+		return ret;
+
+	mipi_d2l_write_reg(mfd, GPIOC, d2l_gpio_out_mask);
+	/* Set gpio#4=U/D=0, gpio#3=L/R=1 , gpio#2,1=CABC=0, gpio#0=NA. */
+	mipi_d2l_write_reg(mfd, GPIOO, d2l_gpio_out_val);
+
+	d2l_pwm_freq_hz = (3.921*1000);
+
+	if (bl_level == 0)
+		bl_level = PWM_LEVEL * 2 / 3 ; /* Default ON value */
+
+	/* Set backlight via PWM */
+	if (bl_pwm) {
+		ret = mipi_d2l_set_backlight_level(bl_pwm, bl_level);
+		if (ret)
+			pr_err("%s.mipi_d2l_set_backlight_level.ret=%d",
+			       __func__, ret);
+	}
+
+	mipi_d2l_read_status(mfd);
+
+	mipi_d2l_enable_3d(mfd, false, false);
+
+	/* Add I2C driver only after DSI-CLK is running */
+	i2c_add_driver(&d2l_i2c_slave_driver);
+
+	pr_info("%s.ret=%d.\n", __func__, ret);
+
+	return ret;
+}
+
+/**
+ * LCD OFF.
+ *
+ * @param pdev
+ *
+ * @return int
+ */
+static int mipi_d2l_lcd_off(struct platform_device *pdev)
+{
+	int ret;
+	struct msm_fb_data_type *mfd;
+
+	pr_info("%s.\n", __func__);
+
+	mfd = platform_get_drvdata(pdev);
+
+	if (!mfd)
+		return -ENODEV;
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	ret = mipi_d2l_set_backlight_level(bl_pwm, 1);
+
+	pr_info("%s.ret=%d.\n", __func__, ret);
+
+	return ret;
+}
+
+static void mipi_d2l_set_backlight(struct msm_fb_data_type *mfd)
+{
+	int level = mfd->bl_level;
+
+	pr_debug("%s.lvl=%d.\n", __func__, level);
+
+	mipi_d2l_set_backlight_level(bl_pwm, level);
+
+	bl_level = level;
+}
+
+static struct msm_fb_panel_data d2l_panel_data = {
+	.on = mipi_d2l_lcd_on,
+	.off = mipi_d2l_lcd_off,
+	.set_backlight = mipi_d2l_set_backlight,
+};
+
+static u32 d2l_i2c_read_reg(struct i2c_client *client, u16 reg)
+{
+	int rc;
+	u32 val = 0;
+	u8 buf[6];
+
+	if (client == NULL) {
+		pr_err("%s.invalid i2c client.\n", __func__);
+		return -EINVAL;
+	}
+
+	buf[0] = reg >> 8;
+	buf[1] = reg & 0xFF;
+
+	rc = i2c_master_send(client, buf, sizeof(reg));
+	rc = i2c_master_recv(client, buf, 4);
+
+	if (rc >= 0) {
+		val = buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24);
+		pr_debug("%s.reg=0x%x.val=0x%x.\n", __func__, reg, val);
+	} else
+		pr_err("%s.fail.reg=0x%x.\n", __func__, reg);
+
+	return val;
+}
+
+static u32 d2l_i2c_write_reg(struct i2c_client *client, u16 reg, u32 val)
+{
+	int rc;
+	u8 buf[6];
+
+	if (client == NULL) {
+		pr_err("%s.invalid i2c client.\n", __func__);
+		return -EINVAL;
+	}
+
+	buf[0] = reg >> 8;
+	buf[1] = reg & 0xFF;
+
+	buf[2] = (val >> 0) & 0xFF;
+	buf[3] = (val >> 8) & 0xFF;
+	buf[4] = (val >> 16) & 0xFF;
+	buf[5] = (val >> 24) & 0xFF;
+
+	rc = i2c_master_send(client, buf, sizeof(buf));
+
+	if (rc >= 0)
+		pr_debug("%s.reg=0x%x.val=0x%x.\n", __func__, reg, val);
+	else
+		pr_err("%s.fail.reg=0x%x.\n", __func__, reg);
+
+	return val;
+}
+
+static int __devinit d2l_i2c_slave_probe(struct i2c_client *client,
+					 const struct i2c_device_id *id)
+{
+	static const u32 i2c_funcs = I2C_FUNC_I2C;
+
+	d2l_i2c_client = client;
+
+	if (!i2c_check_functionality(client->adapter, i2c_funcs)) {
+		pr_err("%s.i2c_check_functionality failed.\n", __func__);
+		return -ENOSYS;
+	} else {
+		pr_debug("%s.i2c_check_functionality OK.\n", __func__);
+	}
+
+	d2l_i2c_read_reg(client, IDREG);
+
+	mipi_d2l_read_status_via_i2c(d2l_i2c_client);
+
+	return 0;
+}
+
+static __devexit int d2l_i2c_slave_remove(struct i2c_client *client)
+{
+	d2l_i2c_client = NULL;
+
+	return 0;
+}
+
+static const struct i2c_device_id d2l_i2c_id[] = {
+	{"tc358764-i2c", 0},
+	{}
+};
+
+static struct i2c_driver d2l_i2c_slave_driver = {
+	.driver = {
+		.name = "tc358764-i2c",
+		.owner = THIS_MODULE
+	},
+	.probe    = d2l_i2c_slave_probe,
+	.remove   = __devexit_p(d2l_i2c_slave_remove),
+	.id_table = d2l_i2c_id,
+};
+
+static int mipi_d2l_enable_3d(struct msm_fb_data_type *mfd,
+			      bool enable, bool mode)
+{
+	u32 tn_usec = 1000000 / 66; /* 66 HZ */
+
+	pr_debug("%s.enable=%d.mode=%d.\n", __func__, enable, mode);
+
+	gpio_direction_output(d2l_3d_gpio_enable, enable);
+	gpio_direction_output(d2l_3d_gpio_mode, mode);
+
+	mipi_d2l_set_tn_clk(tn_pwm, tn_usec);
+
+	return 0;
+}
+
+static ssize_t mipi_d2l_enable_3d_read(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	return snprintf((char *)buf, sizeof(buf), "%u\n", d2l_enable_3d);
+}
+
+static ssize_t mipi_d2l_enable_3d_write(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf,
+				size_t count)
+{
+	int ret = -1;
+	u32 data = 0;
+
+	if (sscanf((char *)buf, "%u", &data) != 1) {
+		dev_err(dev, "%s. Invalid input.\n", __func__);
+		ret = -EINVAL;
+	} else {
+		d2l_enable_3d = data;
+		if (data == 1) /* LANDSCAPE */
+			mipi_d2l_enable_3d(d2l_mfd, true, true);
+		else if (data == 2) /* PORTRAIT */
+			mipi_d2l_enable_3d(d2l_mfd, true, false);
+		else if (data == 0)
+			mipi_d2l_enable_3d(d2l_mfd, false, false);
+		else if (data == 9)
+			mipi_d2l_read_status_via_i2c(d2l_i2c_client);
+		else
+			pr_err("%s.Invalid value=%d.\n", __func__, data);
+	}
+
+	return count;
+}
+
+static struct device_attribute mipi_d2l_3d_barrier_attributes[] = {
+	__ATTR(enable_3d_barrier, 0666,
+	       mipi_d2l_enable_3d_read,
+	       mipi_d2l_enable_3d_write),
+};
+
+static int mipi_dsi_3d_barrier_sysfs_register(struct device *dev)
+{
+	int ret;
+
+	pr_debug("%s.d2l_3d_gpio_enable=%d.\n", __func__, d2l_3d_gpio_enable);
+	pr_debug("%s.d2l_3d_gpio_mode=%d.\n", __func__, d2l_3d_gpio_mode);
+
+	ret  = device_create_file(dev, mipi_d2l_3d_barrier_attributes);
+	if (ret) {
+		pr_err("%s.failed to create 3D sysfs.\n", __func__);
+		goto err_device_create_file;
+	}
+
+	ret = gpio_request(d2l_3d_gpio_enable, "d2l_3d_gpio_enable");
+	if (ret) {
+		pr_err("%s.failed to get d2l_3d_gpio_enable=%d.\n",
+		       __func__, d2l_3d_gpio_enable);
+		goto err_d2l_3d_gpio_enable;
+	}
+
+	ret = gpio_request(d2l_3d_gpio_mode, "d2l_3d_gpio_mode");
+	if (ret) {
+		pr_err("%s.failed to get d2l_3d_gpio_mode=%d.\n",
+		       __func__, d2l_3d_gpio_mode);
+		goto err_d2l_3d_gpio_mode;
+	}
+
+	return 0;
+
+err_d2l_3d_gpio_mode:
+	gpio_free(d2l_3d_gpio_enable);
+err_d2l_3d_gpio_enable:
+	device_remove_file(dev, mipi_d2l_3d_barrier_attributes);
+err_device_create_file:
+
+	return ret;
+}
+
+/**
+ * Probe for device.
+ *
+ * Both the "target" and "panel" device use the same probe function.
+ * "Target" device has id=0, "Panel" devic has non-zero id.
+ * Target device should register first, passing msm_panel_common_pdata.
+ * Panel device passing msm_panel_info.
+ *
+ * @param pdev
+ *
+ * @return int
+ */
+static int __devinit mipi_d2l_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct msm_panel_info *pinfo = NULL;
+
+	pr_debug("%s.id=%d.\n", __func__, pdev->id);
+
+	if (pdev->id == 0) {
+		d2l_common_pdata = pdev->dev.platform_data;
+
+		if (d2l_common_pdata == NULL) {
+			pr_err("%s: no PWM gpio specified.\n", __func__);
+			return 0;
+		}
+
+		led_pwm = d2l_common_pdata->gpio_num[0];
+		d2l_gpio_out_mask = d2l_common_pdata->gpio_num[1] >> 8;
+		d2l_gpio_out_val = d2l_common_pdata->gpio_num[1] & 0xFF;
+		d2l_3d_gpio_enable = d2l_common_pdata->gpio_num[2];
+		d2l_3d_gpio_mode = d2l_common_pdata->gpio_num[3];
+
+		mipi_dsi_buf_alloc(&d2l_tx_buf, DSI_BUF_SIZE);
+		mipi_dsi_buf_alloc(&d2l_rx_buf, DSI_BUF_SIZE);
+
+		return 0;
+	}
+
+	if (d2l_common_pdata == NULL) {
+		pr_err("%s: d2l_common_pdata is NULL.\n", __func__);
+		return -ENODEV;
+	}
+
+	bl_pwm = NULL;
+	if (led_pwm >= 0) {
+		bl_pwm = pwm_request(led_pwm, "lcd-backlight");
+		if (bl_pwm == NULL || IS_ERR(bl_pwm)) {
+			pr_err("%s pwm_request() failed.id=%d.bl_pwm=%d.\n",
+			       __func__, led_pwm, (int) bl_pwm);
+			bl_pwm = NULL;
+			return -EIO;
+		} else {
+			pr_debug("%s.pwm_request() ok.pwm-id=%d.\n",
+			       __func__, led_pwm);
+
+		}
+	} else {
+		pr_err("%s. led_pwm is invalid.\n", __func__);
+	}
+
+	tn_pwm = pwm_request(1, "3D_TN_clk");
+	if (tn_pwm == NULL || IS_ERR(tn_pwm)) {
+		pr_err("%s pwm_request() failed.id=%d.tn_pwm=%d.\n",
+		       __func__, 1, (int) tn_pwm);
+		tn_pwm = NULL;
+		return -EIO;
+	} else {
+		pr_debug("%s.pwm_request() ok.pwm-id=%d.\n", __func__, 1);
+
+	}
+
+	pinfo = pdev->dev.platform_data;
+
+	if (pinfo == NULL) {
+		pr_err("%s: pinfo is NULL.\n", __func__);
+		return -ENODEV;
+	}
+
+	d2l_panel_data.panel_info = *pinfo;
+
+	pdev->dev.platform_data = &d2l_panel_data;
+
+	msm_fb_add_device(pdev);
+
+	if (pinfo->is_3d_panel)
+		mipi_dsi_3d_barrier_sysfs_register(&(pdev->dev));
+
+	return ret;
+}
+
+/**
+ * Device removal notification handler.
+ *
+ * @param pdev
+ *
+ * @return int
+ */
+static int __devexit mipi_d2l_remove(struct platform_device *pdev)
+{
+	/* Note: There are no APIs to remove fb device and free DSI buf. */
+	pr_debug("%s.\n", __func__);
+
+	if (bl_pwm) {
+		pwm_free(bl_pwm);
+		bl_pwm = NULL;
+	}
+
+	return 0;
+}
+
+/**
+ * Register the panel device.
+ *
+ * @param pinfo
+ * @param channel_id
+ * @param panel_id
+ *
+ * @return int
+ */
+int mipi_tc358764_dsi2lvds_register(struct msm_panel_info *pinfo,
+					   u32 channel_id, u32 panel_id)
+{
+	struct platform_device *pdev = NULL;
+	int ret;
+	/* Use DSI-to-LVDS bridge */
+	const char driver_name[] = "mipi_tc358764";
+
+	pr_debug("%s.\n", __func__);
+	ret = mipi_d2l_init();
+	if (ret) {
+		pr_err("mipi_d2l_init() failed with ret %u\n", ret);
+		return ret;
+	}
+
+	/* Note: the device id should be non-zero */
+	pdev = platform_device_alloc(driver_name, (panel_id << 8)|channel_id);
+	if (pdev == NULL)
+		return -ENOMEM;
+
+	pdev->dev.platform_data = pinfo;
+
+	ret = platform_device_add(pdev);
+	if (ret) {
+		pr_err("%s: platform_device_register failed!\n", __func__);
+		goto err_device_put;
+	}
+
+	return 0;
+
+err_device_put:
+	platform_device_put(pdev);
+	return ret;
+}
+
+static struct platform_driver d2l_driver = {
+	.probe  = mipi_d2l_probe,
+	.remove = __devexit_p(mipi_d2l_remove),
+	.driver = {
+		.name   = DRV_NAME,
+	},
+};
+
+/**
+ * Module Init
+ *
+ * @return int
+ */
+static int mipi_d2l_init(void)
+{
+	pr_debug("%s.\n", __func__);
+
+	return platform_driver_register(&d2l_driver);
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Toshiba MIPI-DSI-to-LVDS bridge driver");
+MODULE_AUTHOR("Amir Samuelov <amirs@codeaurora.org>");
diff --git a/drivers/video/msm/mipi_tc358764_dsi2lvds.h b/drivers/video/msm/mipi_tc358764_dsi2lvds.h
new file mode 100644
index 0000000..1b949f0
--- /dev/null
+++ b/drivers/video/msm/mipi_tc358764_dsi2lvds.h
@@ -0,0 +1,21 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef MIPI_TC358764_DSI2LVDS_H
+#define MIPI_TC358764_DSI2LVDS_H
+
+#define PWM_LEVEL 255
+
+int mipi_tc358764_dsi2lvds_register(struct msm_panel_info *pinfo,
+	u32 channel_id, u32 panel_id);
+#endif  /* MIPI_TC358764_DSI2LVDS_H */
diff --git a/drivers/video/msm/mipi_toshiba.c b/drivers/video/msm/mipi_toshiba.c
new file mode 100644
index 0000000..aeaa5aa
--- /dev/null
+++ b/drivers/video/msm/mipi_toshiba.c
@@ -0,0 +1,355 @@
+/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_fb.h"
+#include "mipi_dsi.h"
+#include "mipi_toshiba.h"
+
+static struct pwm_device *bl_lpm;
+static struct mipi_dsi_panel_platform_data *mipi_toshiba_pdata;
+
+#define TM_GET_PID(id) (((id) & 0xff00)>>8)
+
+static struct dsi_buf toshiba_tx_buf;
+static struct dsi_buf toshiba_rx_buf;
+static int mipi_toshiba_lcd_init(void);
+
+#ifdef TOSHIBA_CMDS_UNUSED
+static char one_lane[3] = {0xEF, 0x60, 0x62};
+static char dmode_wqvga[2] = {0xB3, 0x01};
+static char intern_wr_clk1_wqvga[3] = {0xef, 0x2f, 0x22};
+static char intern_wr_clk2_wqvga[3] = {0xef, 0x6e, 0x33};
+static char hor_addr_2A_wqvga[5] = {0x2A, 0x00, 0x00, 0x00, 0xef};
+static char hor_addr_2B_wqvga[5] = {0x2B, 0x00, 0x00, 0x01, 0xaa};
+static char if_sel_cmd[2] = {0x53, 0x00};
+#endif
+
+static char exit_sleep[2] = {0x11, 0x00};
+static char display_on[2] = {0x29, 0x00};
+static char display_off[2] = {0x28, 0x00};
+static char enter_sleep[2] = {0x10, 0x00};
+
+static char mcap_off[2] = {0xb2, 0x00};
+static char ena_test_reg[3] = {0xEF, 0x01, 0x01};
+static char two_lane[3] = {0xEF, 0x60, 0x63};
+static char non_burst_sync_pulse[3] = {0xef, 0x61, 0x09};
+static char dmode_wvga[2] = {0xB3, 0x00};
+static char intern_wr_clk1_wvga[3] = {0xef, 0x2f, 0xcc};
+static char intern_wr_clk2_wvga[3] = {0xef, 0x6e, 0xdd};
+static char hor_addr_2A_wvga[5] = {0x2A, 0x00, 0x00, 0x01, 0xdf};
+static char hor_addr_2B_wvga[5] = {0x2B, 0x00, 0x00, 0x03, 0x55};
+static char if_sel_video[2] = {0x53, 0x01};
+
+static struct dsi_cmd_desc toshiba_wvga_display_on_cmds[] = {
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(mcap_off), mcap_off},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(ena_test_reg), ena_test_reg},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(two_lane), two_lane},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(non_burst_sync_pulse),
+					non_burst_sync_pulse},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(dmode_wvga), dmode_wvga},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(intern_wr_clk1_wvga),
+					intern_wr_clk1_wvga},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(intern_wr_clk2_wvga),
+					intern_wr_clk2_wvga},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, 0, sizeof(hor_addr_2A_wvga),
+					hor_addr_2A_wvga},
+	{DTYPE_DCS_LWRITE, 1, 0, 0, 0, sizeof(hor_addr_2B_wvga),
+					hor_addr_2B_wvga},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(if_sel_video), if_sel_video},
+	{DTYPE_DCS_WRITE, 1, 0, 0, 0, sizeof(exit_sleep), exit_sleep},
+	{DTYPE_DCS_WRITE, 1, 0, 0, 0, sizeof(display_on), display_on}
+};
+
+static char mcap_start[2] = {0xb0, 0x04};
+static char num_out_pixelform[3] = {0xb3, 0x00, 0x87};
+static char dsi_ctrl[3] = {0xb6, 0x30, 0x83};
+static char panel_driving[7] = {0xc0, 0x01, 0x00, 0x85, 0x00, 0x00, 0x00};
+static char dispV_timing[5] = {0xc1, 0x00, 0x10, 0x00, 0x01};
+static char dispCtrl[3] = {0xc3, 0x00, 0x19};
+static char test_mode_c4[2] = {0xc4, 0x03};
+static char dispH_timing[15] = {
+	/* TYPE_DCS_LWRITE */
+	0xc5, 0x00, 0x01, 0x05,
+	0x04, 0x5e, 0x00, 0x00,
+	0x00, 0x00, 0x0b, 0x17,
+	0x05, 0x00, 0x00
+};
+static char test_mode_c6[2] = {0xc6, 0x00};
+static char gamma_setA[13] = {
+	0xc8, 0x0a, 0x15, 0x18,
+	0x1b, 0x1c, 0x0d, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00
+};
+static char gamma_setB[13] = {
+	0xc9, 0x0d, 0x1d, 0x1f,
+	0x1f, 0x1f, 0x10, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00
+};
+static char gamma_setC[13] = {
+	0xca, 0x1e, 0x1f, 0x1e,
+	0x1d, 0x1d, 0x10, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00
+};
+static char powerSet_ChrgPmp[5] = {0xd0, 0x02, 0x00, 0xa3, 0xb8};
+static char testMode_d1[6] = {0xd1, 0x10, 0x14, 0x53, 0x64, 0x00};
+static char powerSet_SrcAmp[3] = {0xd2, 0xb3, 0x00};
+static char powerInt_PS[3] = {0xd3, 0x33, 0x03};
+static char vreg[2] = {0xd5, 0x00};
+static char test_mode_d6[2] = {0xd6, 0x01};
+static char timingCtrl_d7[9] = {
+	0xd7, 0x09, 0x00, 0x84,
+	0x81, 0x61, 0xbc, 0xb5,
+	0x05
+};
+static char timingCtrl_d8[7] = {
+	0xd8, 0x04, 0x25, 0x90,
+	0x4c, 0x92, 0x00
+};
+static char timingCtrl_d9[4] = {0xd9, 0x5b, 0x7f, 0x05};
+static char white_balance[6] = {0xcb, 0x00, 0x00, 0x00, 0x1c, 0x00};
+static char vcs_settings[2] = {0xdd, 0x53};
+static char vcom_dc_settings[2] = {0xde, 0x43};
+static char testMode_e3[5] = {0xe3, 0x00, 0x00, 0x00, 0x00};
+static char testMode_e4[6] = {0xe4, 0x00, 0x00, 0x22, 0xaa, 0x00};
+static char testMode_e5[2] = {0xe5, 0x00};
+static char testMode_fa[4] = {0xfa, 0x00, 0x00, 0x00};
+static char testMode_fd[5] = {0xfd, 0x00, 0x00, 0x00, 0x00};
+static char testMode_fe[5] = {0xfe, 0x00, 0x00, 0x00, 0x00};
+static char mcap_end[2] = {0xb0, 0x03};
+static char set_add_mode[2] = {0x36, 0x0};
+static char set_pixel_format[2] = {0x3a, 0x70};
+
+
+static struct dsi_cmd_desc toshiba_wsvga_display_on_cmds[] = {
+	{DTYPE_GEN_WRITE2, 1, 0, 0, 10, sizeof(mcap_start), mcap_start},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 10, sizeof(num_out_pixelform),
+		num_out_pixelform},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 10, sizeof(dsi_ctrl), dsi_ctrl},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(panel_driving), panel_driving},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(dispV_timing), dispV_timing},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(dispCtrl), dispCtrl},
+	{DTYPE_GEN_WRITE2, 1, 0, 0, 0, sizeof(test_mode_c4), test_mode_c4},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(dispH_timing), dispH_timing},
+	{DTYPE_GEN_WRITE2, 1, 0, 0, 0, sizeof(test_mode_c6), test_mode_c6},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(gamma_setA), gamma_setA},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(gamma_setB), gamma_setB},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(gamma_setC), gamma_setC},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(powerSet_ChrgPmp),
+		powerSet_ChrgPmp},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(testMode_d1), testMode_d1},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(powerSet_SrcAmp),
+		powerSet_SrcAmp},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(powerInt_PS), powerInt_PS},
+	{DTYPE_GEN_WRITE2, 1, 0, 0, 0, sizeof(vreg), vreg},
+	{DTYPE_GEN_WRITE2, 1, 0, 0, 0, sizeof(test_mode_d6), test_mode_d6},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(timingCtrl_d7), timingCtrl_d7},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(timingCtrl_d8), timingCtrl_d8},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(timingCtrl_d9), timingCtrl_d9},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(white_balance), white_balance},
+	{DTYPE_GEN_WRITE2, 1, 0, 0, 0, sizeof(vcs_settings), vcs_settings},
+	{DTYPE_GEN_WRITE2, 1, 0, 0, 0, sizeof(vcom_dc_settings),
+		vcom_dc_settings},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(testMode_e3), testMode_e3},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(testMode_e4), testMode_e4},
+	{DTYPE_GEN_WRITE2, 1, 0, 0, 0, sizeof(testMode_e5), testMode_e5},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(testMode_fa), testMode_fa},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(testMode_fd), testMode_fd},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(testMode_fe), testMode_fe},
+	{DTYPE_GEN_WRITE2, 1, 0, 0, 0, sizeof(mcap_end), mcap_end},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, 0, sizeof(set_add_mode), set_add_mode},
+	{DTYPE_DCS_WRITE1, 1, 0, 0, 0, sizeof(set_pixel_format),
+		set_pixel_format},
+	{DTYPE_DCS_WRITE, 1, 0, 0, 120, sizeof(exit_sleep), exit_sleep},
+	{DTYPE_DCS_WRITE, 1, 0, 0, 50, sizeof(display_on), display_on}
+};
+
+static struct dsi_cmd_desc toshiba_display_off_cmds[] = {
+	{DTYPE_DCS_WRITE, 1, 0, 0, 50, sizeof(display_off), display_off},
+	{DTYPE_DCS_WRITE, 1, 0, 0, 120, sizeof(enter_sleep), enter_sleep}
+};
+
+static int mipi_toshiba_lcd_on(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+
+	mfd = platform_get_drvdata(pdev);
+
+	if (!mfd)
+		return -ENODEV;
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	if (TM_GET_PID(mfd->panel.id) == MIPI_DSI_PANEL_WVGA_PT)
+		mipi_dsi_cmds_tx(mfd, &toshiba_tx_buf,
+			toshiba_wvga_display_on_cmds,
+			ARRAY_SIZE(toshiba_wvga_display_on_cmds));
+	else if (TM_GET_PID(mfd->panel.id) == MIPI_DSI_PANEL_WSVGA_PT ||
+		TM_GET_PID(mfd->panel.id) == MIPI_DSI_PANEL_WUXGA)
+		mipi_dsi_cmds_tx(mfd, &toshiba_tx_buf,
+			toshiba_wsvga_display_on_cmds,
+			ARRAY_SIZE(toshiba_wsvga_display_on_cmds));
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+static int mipi_toshiba_lcd_off(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+
+	mfd = platform_get_drvdata(pdev);
+
+	if (!mfd)
+		return -ENODEV;
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	mipi_dsi_cmds_tx(mfd, &toshiba_tx_buf, toshiba_display_off_cmds,
+			ARRAY_SIZE(toshiba_display_off_cmds));
+
+	return 0;
+}
+
+void mipi_bklight_pwm_cfg(void)
+{
+	if (mipi_toshiba_pdata && mipi_toshiba_pdata->dsi_pwm_cfg)
+		mipi_toshiba_pdata->dsi_pwm_cfg();
+}
+
+static void mipi_toshiba_set_backlight(struct msm_fb_data_type *mfd)
+{
+	int ret;
+	static int bklight_pwm_cfg;
+
+	if (bklight_pwm_cfg == 0) {
+		mipi_bklight_pwm_cfg();
+		bklight_pwm_cfg++;
+	}
+
+	if (bl_lpm) {
+		ret = pwm_config(bl_lpm, MIPI_TOSHIBA_PWM_DUTY_LEVEL *
+			mfd->bl_level, MIPI_TOSHIBA_PWM_PERIOD_USEC);
+		if (ret) {
+			pr_err("pwm_config on lpm failed %d\n", ret);
+			return;
+		}
+		if (mfd->bl_level) {
+			ret = pwm_enable(bl_lpm);
+			if (ret)
+				pr_err("pwm enable/disable on lpm failed"
+					"for bl %d\n",	mfd->bl_level);
+		} else {
+			pwm_disable(bl_lpm);
+		}
+	}
+}
+
+static int __devinit mipi_toshiba_lcd_probe(struct platform_device *pdev)
+{
+	if (pdev->id == 0) {
+		mipi_toshiba_pdata = pdev->dev.platform_data;
+		return 0;
+	}
+
+	if (mipi_toshiba_pdata == NULL) {
+		pr_err("%s.invalid platform data.\n", __func__);
+		return -ENODEV;
+	}
+
+	if (mipi_toshiba_pdata != NULL)
+		bl_lpm = pwm_request(mipi_toshiba_pdata->gpio[0],
+			"backlight");
+
+	if (bl_lpm == NULL || IS_ERR(bl_lpm)) {
+		pr_err("%s pwm_request() failed\n", __func__);
+		bl_lpm = NULL;
+	}
+	pr_debug("bl_lpm = %p lpm = %d\n", bl_lpm,
+		mipi_toshiba_pdata->gpio[0]);
+
+	msm_fb_add_device(pdev);
+
+	return 0;
+}
+
+static struct platform_driver this_driver = {
+	.probe  = mipi_toshiba_lcd_probe,
+	.driver = {
+		.name   = "mipi_toshiba",
+	},
+};
+
+static struct msm_fb_panel_data toshiba_panel_data = {
+	.on		= mipi_toshiba_lcd_on,
+	.off		= mipi_toshiba_lcd_off,
+	.set_backlight  = mipi_toshiba_set_backlight,
+};
+
+static int ch_used[3];
+
+int mipi_toshiba_device_register(struct msm_panel_info *pinfo,
+					u32 channel, u32 panel)
+{
+	struct platform_device *pdev = NULL;
+	int ret;
+
+	if ((channel >= 3) || ch_used[channel])
+		return -ENODEV;
+
+	ch_used[channel] = TRUE;
+
+	ret = mipi_toshiba_lcd_init();
+	if (ret) {
+		pr_err("mipi_toshiba_lcd_init() failed with ret %u\n", ret);
+		return ret;
+	}
+
+	pdev = platform_device_alloc("mipi_toshiba", (panel << 8)|channel);
+	if (!pdev)
+		return -ENOMEM;
+
+	toshiba_panel_data.panel_info = *pinfo;
+
+	ret = platform_device_add_data(pdev, &toshiba_panel_data,
+		sizeof(toshiba_panel_data));
+	if (ret) {
+		printk(KERN_ERR
+		  "%s: platform_device_add_data failed!\n", __func__);
+		goto err_device_put;
+	}
+
+	ret = platform_device_add(pdev);
+	if (ret) {
+		printk(KERN_ERR
+		  "%s: platform_device_register failed!\n", __func__);
+		goto err_device_put;
+	}
+
+	return 0;
+
+err_device_put:
+	platform_device_put(pdev);
+	return ret;
+}
+
+static int mipi_toshiba_lcd_init(void)
+{
+	mipi_dsi_buf_alloc(&toshiba_tx_buf, DSI_BUF_SIZE);
+	mipi_dsi_buf_alloc(&toshiba_rx_buf, DSI_BUF_SIZE);
+
+	return platform_driver_register(&this_driver);
+}
diff --git a/drivers/video/msm/mipi_toshiba.h b/drivers/video/msm/mipi_toshiba.h
new file mode 100644
index 0000000..4107161
--- /dev/null
+++ b/drivers/video/msm/mipi_toshiba.h
@@ -0,0 +1,30 @@
+
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef MIPI_TOSHIBA_H
+#define MIPI_TOSHIBA_H
+
+#include <linux/pwm.h>
+#include <linux/mfd/pm8xxx/pm8921.h>
+
+int mipi_toshiba_device_register(struct msm_panel_info *pinfo,
+					u32 channel, u32 panel);
+
+#define MIPI_TOSHIBA_PWM_FREQ_HZ 3921
+#define MIPI_TOSHIBA_PWM_PERIOD_USEC (USEC_PER_SEC / MIPI_TOSHIBA_PWM_FREQ_HZ)
+#define MIPI_TOSHIBA_PWM_LEVEL 255
+#define MIPI_TOSHIBA_PWM_DUTY_LEVEL \
+	(MIPI_TOSHIBA_PWM_PERIOD_USEC / MIPI_TOSHIBA_PWM_LEVEL)
+
+#endif  /* MIPI_TOSHIBA_H */
diff --git a/drivers/video/msm/mipi_toshiba_video_wsvga_pt.c b/drivers/video/msm/mipi_toshiba_video_wsvga_pt.c
new file mode 100644
index 0000000..2a8610b
--- /dev/null
+++ b/drivers/video/msm/mipi_toshiba_video_wsvga_pt.c
@@ -0,0 +1,104 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_fb.h"
+#include "mipi_dsi.h"
+#include "mipi_toshiba.h"
+
+static struct msm_panel_info pinfo;
+
+static struct mipi_dsi_phy_ctrl dsi_video_mode_phy_db = {
+	/* 600*1024, RGB888, 3 Lane 55 fps video mode */
+    /* regulator */
+	{0x09, 0x08, 0x05, 0x00, 0x20},
+	/* timing */
+	{0xab, 0x8a, 0x18, 0x00, 0x92, 0x97, 0x1b, 0x8c,
+	0x0c, 0x03, 0x04, 0xa0},
+    /* phy ctrl */
+	{0x5f, 0x00, 0x00, 0x10},
+    /* strength */
+	{0xff, 0x00, 0x06, 0x00},
+	/* pll control */
+	{0x0, 0x7f, 0x31, 0xda, 0x00, 0x50, 0x48, 0x63,
+	0x41, 0x0f, 0x01,
+	0x00, 0x14, 0x03, 0x00, 0x02, 0x00, 0x20, 0x00, 0x01 },
+};
+
+static int __init mipi_video_toshiba_wsvga_pt_init(void)
+{
+	int ret;
+
+	if (msm_fb_detect_client("mipi_video_toshiba_wsvga"))
+		return 0;
+
+	pinfo.xres = 600;
+	pinfo.yres = 1024;
+	/*
+	 *
+	 * Panel's Horizontal input timing requirement is to
+	 * include dummy(pad) data of 200 clk in addition to
+	 * width and porch/sync width values
+	 */
+	pinfo.lcdc.xres_pad = 200;
+	pinfo.lcdc.yres_pad = 0;
+
+	pinfo.type = MIPI_VIDEO_PANEL;
+	pinfo.pdest = DISPLAY_1;
+	pinfo.wait_cycle = 0;
+	pinfo.bpp = 24;
+	pinfo.lcdc.h_back_porch = 16;
+	pinfo.lcdc.h_front_porch = 23;
+	pinfo.lcdc.h_pulse_width = 8;
+	pinfo.lcdc.v_back_porch = 2;
+	pinfo.lcdc.v_front_porch = 7;
+	pinfo.lcdc.v_pulse_width = 2;
+	pinfo.lcdc.border_clr = 0;	/* blk */
+	pinfo.lcdc.underflow_clr = 0xff;	/* blue */
+	pinfo.lcdc.hsync_skew = 0;
+	pinfo.bl_max = MIPI_TOSHIBA_PWM_LEVEL;
+	pinfo.bl_min = 1;
+	pinfo.fb_num = 2;
+	pinfo.clk_rate = 384000000;
+
+	pinfo.mipi.mode = DSI_VIDEO_MODE;
+	pinfo.mipi.pulse_mode_hsa_he = FALSE;
+	pinfo.mipi.hfp_power_stop = FALSE;
+	pinfo.mipi.hbp_power_stop = FALSE;
+	pinfo.mipi.hsa_power_stop = FALSE;
+	pinfo.mipi.eof_bllp_power_stop = FALSE;
+	pinfo.mipi.bllp_power_stop = FALSE;
+	pinfo.mipi.traffic_mode = DSI_NON_BURST_SYNCH_EVENT;
+	pinfo.mipi.dst_format = DSI_VIDEO_DST_FORMAT_RGB888;
+	pinfo.mipi.vc = 0;
+	pinfo.mipi.rgb_swap = DSI_RGB_SWAP_RGB;
+	pinfo.mipi.data_lane0 = TRUE;
+	pinfo.mipi.data_lane1 = TRUE;
+	pinfo.mipi.data_lane2 = TRUE;
+	pinfo.mipi.t_clk_post = 0x20;
+	pinfo.mipi.t_clk_pre = 0x2d;
+	pinfo.mipi.stream = 0; /* dma_p */
+	pinfo.mipi.mdp_trigger = 0;
+	pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW;
+	pinfo.mipi.frame_rate = 55;
+	pinfo.mipi.dsi_phy_db = &dsi_video_mode_phy_db;
+	pinfo.mipi.tx_eot_append = TRUE;
+
+	ret = mipi_toshiba_device_register(&pinfo, MIPI_DSI_PRIM,
+						MIPI_DSI_PANEL_WSVGA_PT);
+	if (ret)
+		printk(KERN_ERR "%s: failed to register device!\n", __func__);
+
+	return ret;
+}
+
+module_init(mipi_video_toshiba_wsvga_pt_init);
diff --git a/drivers/video/msm/mipi_toshiba_video_wuxga.c b/drivers/video/msm/mipi_toshiba_video_wuxga.c
new file mode 100644
index 0000000..297248f
--- /dev/null
+++ b/drivers/video/msm/mipi_toshiba_video_wuxga.c
@@ -0,0 +1,97 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_fb.h"
+#include "mipi_dsi.h"
+#include "mipi_toshiba.h"
+
+static struct msm_panel_info pinfo;
+
+static struct mipi_dsi_phy_ctrl dsi_video_mode_phy_db = {
+	/* 1920*1200, RGB888, 4 Lane 60 fps video mode */
+	/* regulator */
+	{0x03, 0x0a, 0x04, 0x00, 0x20},
+	/* timing */
+	{0x66, 0x26, 0x1F, 0x00, 0x55, 0x9C, 0x16, 0x90,
+	0x23, 0x03, 0x04, 0xa0},
+	/* phy ctrl */
+	{0x5f, 0x00, 0x00, 0x10},
+	/* strength */
+	{0xff, 0x00, 0x06, 0x00},
+	/* pll control */
+	{0x0, 0xD7, 0x1, 0x19, 0x00, 0x50, 0x48, 0x63,
+	0x41, 0x0f, 0x01,
+	0x00, 0x14, 0x03, 0x00, 0x02, 0x00, 0x20, 0x00, 0x01 },
+};
+
+static int __init mipi_video_toshiba_wuxga_init(void)
+{
+	int ret;
+
+	if (msm_fb_detect_client("mipi_video_toshiba_wuxga"))
+		return 0;
+
+	pinfo.xres = 1920;
+	pinfo.yres = 1200;
+
+	pinfo.type = MIPI_VIDEO_PANEL;
+	pinfo.pdest = DISPLAY_1;
+	pinfo.wait_cycle = 0;
+	pinfo.bpp = 24;
+	pinfo.lcdc.h_back_porch = 50;
+	pinfo.lcdc.h_front_porch = 50;
+	pinfo.lcdc.h_pulse_width = 170;
+	pinfo.lcdc.v_back_porch = 7;
+	pinfo.lcdc.v_front_porch = 8;
+	pinfo.lcdc.v_pulse_width = 30;
+	pinfo.lcdc.border_clr = 0;	/* blk */
+	pinfo.lcdc.underflow_clr = 0xff;	/* blue */
+	pinfo.lcdc.hsync_skew = 0;
+	pinfo.bl_max = MIPI_TOSHIBA_PWM_LEVEL;
+	pinfo.bl_min = 1;
+	pinfo.fb_num = 2;
+	pinfo.clk_rate = 981560000;
+
+	pinfo.mipi.mode = DSI_VIDEO_MODE;
+	pinfo.mipi.pulse_mode_hsa_he = TRUE;
+	pinfo.mipi.hfp_power_stop = FALSE;
+	pinfo.mipi.hbp_power_stop = FALSE;
+	pinfo.mipi.hsa_power_stop = FALSE;
+	pinfo.mipi.eof_bllp_power_stop = TRUE;
+	pinfo.mipi.bllp_power_stop = TRUE;
+	pinfo.mipi.traffic_mode = DSI_NON_BURST_SYNCH_PULSE;
+	pinfo.mipi.dst_format = DSI_VIDEO_DST_FORMAT_RGB888;
+	pinfo.mipi.vc = 0;
+	pinfo.mipi.rgb_swap = DSI_RGB_SWAP_BGR;
+	pinfo.mipi.data_lane0 = TRUE;
+	pinfo.mipi.data_lane1 = TRUE;
+	pinfo.mipi.data_lane2 = TRUE;
+	pinfo.mipi.data_lane3 = TRUE;
+	pinfo.mipi.tx_eot_append = TRUE;
+	pinfo.mipi.t_clk_post = 0x04;
+	pinfo.mipi.t_clk_pre = 0x1c;
+	pinfo.mipi.stream = 0; /* dma_p */
+	pinfo.mipi.mdp_trigger = DSI_CMD_TRIGGER_SW;
+	pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW;
+	pinfo.mipi.frame_rate = 60;
+	pinfo.mipi.dsi_phy_db = &dsi_video_mode_phy_db;
+
+	ret = mipi_toshiba_device_register(&pinfo, MIPI_DSI_PRIM,
+						MIPI_DSI_PANEL_WUXGA);
+	if (ret)
+		printk(KERN_ERR "%s: failed to register device!\n", __func__);
+
+	return ret;
+}
+
+module_init(mipi_video_toshiba_wuxga_init);
diff --git a/drivers/video/msm/mipi_toshiba_video_wvga_pt.c b/drivers/video/msm/mipi_toshiba_video_wvga_pt.c
new file mode 100644
index 0000000..d6cabfc
--- /dev/null
+++ b/drivers/video/msm/mipi_toshiba_video_wvga_pt.c
@@ -0,0 +1,106 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_fb.h"
+#include "mipi_dsi.h"
+#include "mipi_toshiba.h"
+
+static struct msm_panel_info pinfo;
+
+static struct mipi_dsi_phy_ctrl dsi_video_mode_phy_db = {
+	/* 480*854, RGB888, 2 Lane 60 fps video mode */
+		{0x03, 0x01, 0x01, 0x00},	/* regulator */
+		/* timing   */
+		{0x6a, 0x22, 0x0f, 0x00, 0x30, 0x38, 0x13, 0x26,
+		0x1b, 0x03, 0x04},
+		{0x7f, 0x00, 0x00, 0x00},	/* phy ctrl */
+		{0xee, 0x03, 0x86, 0x03},	/* strength */
+		/* pll control */
+
+#define DSI_BIT_CLK_380MHZ
+
+#if defined(DSI_BIT_CLK_366MHZ)
+		{0x41, 0xdb, 0xb2, 0xf5, 0x00, 0x50, 0x48, 0x63,
+		0x31, 0x0f, 0x07,
+		0x05, 0x14, 0x03, 0x03, 0x03, 0x54, 0x06, 0x10, 0x04, 0x03 },
+#elif defined(DSI_BIT_CLK_380MHZ)
+		{0x41, 0xf7, 0xb2, 0xf5, 0x00, 0x50, 0x48, 0x63,
+		0x31, 0x0f, 0x07,
+		0x05, 0x14, 0x03, 0x03, 0x03, 0x54, 0x06, 0x10, 0x04, 0x03 },
+#elif defined(DSI_BIT_CLK_400MHZ)
+		{0x41, 0x8f, 0xb1, 0xda, 0x00, 0x50, 0x48, 0x63,
+		0x31, 0x0f, 0x07,
+		0x05, 0x14, 0x03, 0x03, 0x03, 0x54, 0x06, 0x10, 0x04, 0x03 },
+#else		/* 200 mhz */
+		{0x41, 0x8f, 0xb1, 0xda, 0x00, 0x50, 0x48, 0x63,
+		0x33, 0x1f, 0x0f,
+		0x05, 0x14, 0x03, 0x03, 0x03, 0x54, 0x06, 0x10, 0x04, 0x03 },
+#endif
+};
+
+static int __init mipi_video_toshiba_wvga_pt_init(void)
+{
+	int ret;
+
+	if (msm_fb_detect_client("mipi_video_toshiba_wvga"))
+		return 0;
+
+	pinfo.xres = 480;
+	pinfo.yres = 864; /* 856 for V1 surf */
+	pinfo.type = MIPI_VIDEO_PANEL;
+	pinfo.pdest = DISPLAY_1;
+	pinfo.wait_cycle = 0;
+	pinfo.bpp = 24;
+	pinfo.lcdc.h_back_porch = 64;
+	pinfo.lcdc.h_front_porch = 64;
+	pinfo.lcdc.h_pulse_width = 16;
+	pinfo.lcdc.v_back_porch = 8;
+	pinfo.lcdc.v_front_porch = 4;
+	pinfo.lcdc.v_pulse_width = 1;
+	pinfo.lcdc.border_clr = 0;	/* blk */
+	pinfo.lcdc.underflow_clr = 0xff;	/* blue */
+	pinfo.lcdc.hsync_skew = 0;
+	pinfo.bl_max = 15;
+	pinfo.bl_min = 1;
+	pinfo.fb_num = 2;
+
+	pinfo.mipi.mode = DSI_VIDEO_MODE;
+	pinfo.mipi.pulse_mode_hsa_he = TRUE;
+	pinfo.mipi.hfp_power_stop = FALSE;
+	pinfo.mipi.hbp_power_stop = FALSE;
+	pinfo.mipi.hsa_power_stop = FALSE;
+	pinfo.mipi.eof_bllp_power_stop = TRUE;
+	pinfo.mipi.bllp_power_stop = TRUE;
+	pinfo.mipi.traffic_mode = DSI_NON_BURST_SYNCH_PULSE;
+	pinfo.mipi.dst_format = DSI_VIDEO_DST_FORMAT_RGB888;
+	pinfo.mipi.vc = 0;
+	pinfo.mipi.rgb_swap = DSI_RGB_SWAP_BGR;
+	pinfo.mipi.data_lane0 = TRUE;
+	pinfo.mipi.data_lane1 = TRUE;
+	pinfo.mipi.t_clk_post = 0x04;
+	pinfo.mipi.t_clk_pre = 0x17;
+	pinfo.mipi.stream = 0; /* dma_p */
+	pinfo.mipi.mdp_trigger = DSI_CMD_TRIGGER_SW;
+	pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW;
+	pinfo.mipi.frame_rate = 60;
+	pinfo.mipi.dsi_phy_db = &dsi_video_mode_phy_db;
+
+	ret = mipi_toshiba_device_register(&pinfo, MIPI_DSI_PRIM,
+						MIPI_DSI_PANEL_WVGA_PT);
+	if (ret)
+		printk(KERN_ERR "%s: failed to register device!\n", __func__);
+
+	return ret;
+}
+
+module_init(mipi_video_toshiba_wvga_pt_init);
diff --git a/drivers/video/msm/mipi_truly.c b/drivers/video/msm/mipi_truly.c
new file mode 100644
index 0000000..a2060f0
--- /dev/null
+++ b/drivers/video/msm/mipi_truly.c
@@ -0,0 +1,259 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_fb.h"
+#include "mipi_dsi.h"
+#include "mipi_truly.h"
+
+static struct msm_panel_common_pdata *mipi_truly_pdata;
+static struct dsi_buf truly_tx_buf;
+static struct dsi_buf truly_rx_buf;
+
+#define TRULY_CMD_DELAY		0
+#define TRULY_SLEEP_OFF_DELAY	150
+#define TRULY_DISPLAY_ON_DELAY	150
+#define GPIO_TRULY_LCD_RESET	129
+
+static int prev_bl = 17;
+
+static char extend_cmd_enable[4] = {0xB9, 0xFF, 0x83, 0x69};
+static char display_setting[16] = {
+	0xB2, 0x00, 0x23, 0x62,
+	0x62, 0x70, 0x00, 0xFF,
+	0x00, 0x00, 0x00, 0x00,
+	0x03, 0x03, 0x00, 0x01,
+};
+static char wave_cycle_setting[6] = {0xB4, 0x00, 0x1D, 0x5F, 0x0E, 0x06};
+static char gip_setting[27] = {
+	0xD5, 0x00, 0x04, 0x03,
+	0x00, 0x01, 0x05, 0x1C,
+	0x70, 0x01, 0x03, 0x00,
+	0x00, 0x40, 0x06, 0x51,
+	0x07, 0x00, 0x00, 0x41,
+	0x06, 0x50, 0x07, 0x07,
+	0x0F, 0x04, 0x00,
+};
+static char power_setting[20] = {
+	0xB1, 0x01, 0x00, 0x34,
+	0x06, 0x00, 0x0F, 0x0F,
+	0x2A, 0x32, 0x3F, 0x3F,
+	0x07, 0x3A, 0x01, 0xE6,
+	0xE6, 0xE6, 0xE6, 0xE6,
+};
+static char vcom_setting[3] = {0xB6, 0x56, 0x56};
+static char pannel_setting[2] = {0xCC, 0x02};
+static char gamma_setting[35] = {
+	0xE0, 0x00, 0x1D, 0x22,
+	0x38, 0x3D, 0x3F, 0x2E,
+	0x4A, 0x06, 0x0D, 0x0F,
+	0x13, 0x15, 0x13, 0x16,
+	0x10, 0x19, 0x00, 0x1D,
+	0x22, 0x38, 0x3D, 0x3F,
+	0x2E, 0x4A, 0x06, 0x0D,
+	0x0F, 0x13, 0x15, 0x13,
+	0x16, 0x10, 0x19,
+};
+static char mipi_setting[14] = {
+	0xBA, 0x00, 0xA0, 0xC6,
+	0x00, 0x0A, 0x00, 0x10,
+	0x30, 0x6F, 0x02, 0x11,
+	0x18, 0x40,
+};
+static char exit_sleep[2] = {0x11, 0x00};
+static char display_on[2] = {0x29, 0x00};
+static char display_off[2] = {0x28, 0x00};
+static char enter_sleep[2] = {0x10, 0x00};
+
+static struct dsi_cmd_desc truly_display_off_cmds[] = {
+	{DTYPE_DCS_WRITE, 1, 0, 0, 10, sizeof(display_off), display_off},
+	{DTYPE_DCS_WRITE, 1, 0, 0, 120, sizeof(enter_sleep), enter_sleep}
+};
+
+static struct dsi_cmd_desc truly_display_on_cmds[] = {
+	{DTYPE_GEN_LWRITE, 1, 0, 0, TRULY_CMD_DELAY,
+			sizeof(extend_cmd_enable), extend_cmd_enable},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, TRULY_CMD_DELAY,
+			sizeof(display_setting), display_setting},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, TRULY_CMD_DELAY,
+			sizeof(wave_cycle_setting), wave_cycle_setting},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, TRULY_CMD_DELAY,
+			sizeof(gip_setting), gip_setting},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, TRULY_CMD_DELAY,
+			sizeof(power_setting), power_setting},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, TRULY_CMD_DELAY,
+			sizeof(vcom_setting), vcom_setting},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, TRULY_CMD_DELAY,
+			sizeof(pannel_setting), pannel_setting},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, TRULY_CMD_DELAY,
+			sizeof(gamma_setting), gamma_setting},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, TRULY_CMD_DELAY,
+			sizeof(mipi_setting), mipi_setting},
+	{DTYPE_DCS_WRITE, 1, 0, 0, TRULY_SLEEP_OFF_DELAY,
+			sizeof(exit_sleep), exit_sleep},
+	{DTYPE_DCS_WRITE, 1, 0, 0, TRULY_DISPLAY_ON_DELAY,
+			sizeof(display_on), display_on},
+};
+
+static int mipi_truly_lcd_on(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+
+	mfd = platform_get_drvdata(pdev);
+
+	if (!mfd)
+		return -ENODEV;
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	msleep(20);
+	mipi_dsi_cmds_tx(mfd, &truly_tx_buf, truly_display_on_cmds,
+			ARRAY_SIZE(truly_display_on_cmds));
+
+	return 0;
+}
+
+static int mipi_truly_lcd_off(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+
+	mfd = platform_get_drvdata(pdev);
+
+	if (!mfd)
+		return -ENODEV;
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	mipi_dsi_cmds_tx(mfd, &truly_tx_buf, truly_display_off_cmds,
+			ARRAY_SIZE(truly_display_off_cmds));
+
+	return 0;
+}
+
+#define BL_LEVEL	17
+static void mipi_truly_set_backlight(struct msm_fb_data_type *mfd)
+{
+	int step = 0, i = 0;
+	int bl_level = mfd->bl_level;
+
+	/* real backlight level, 1 - max, 16 - min, 17 - off */
+	bl_level = BL_LEVEL - bl_level;
+
+	if (bl_level > prev_bl) {
+		step = bl_level - prev_bl;
+		if (bl_level == BL_LEVEL)
+			step--;
+	} else if (bl_level < prev_bl) {
+		step = bl_level + 16 - prev_bl;
+	} else {
+		pr_debug("%s: no change\n", __func__);
+		return;
+	}
+
+	if (bl_level == BL_LEVEL) {
+		/* turn off backlight */
+		mipi_truly_pdata->pmic_backlight(0);
+	} else {
+		if (prev_bl == BL_LEVEL) {
+			/* turn on backlight */
+			mipi_truly_pdata->pmic_backlight(1);
+			udelay(30);
+		}
+		/* adjust backlight level */
+		for (i = 0; i < step; i++) {
+			mipi_truly_pdata->pmic_backlight(0);
+			udelay(1);
+			mipi_truly_pdata->pmic_backlight(1);
+			udelay(1);
+		}
+	}
+	msleep(20);
+	prev_bl = bl_level;
+
+	return;
+}
+
+static int __devinit mipi_truly_lcd_probe(struct platform_device *pdev)
+{
+	if (pdev->id == 0) {
+		mipi_truly_pdata = pdev->dev.platform_data;
+		return 0;
+	}
+
+	msm_fb_add_device(pdev);
+
+	return 0;
+}
+
+static struct platform_driver this_driver = {
+	.probe  = mipi_truly_lcd_probe,
+	.driver = {
+		.name   = "mipi_truly",
+	},
+};
+
+static struct msm_fb_panel_data truly_panel_data = {
+	.on		= mipi_truly_lcd_on,
+	.off		= mipi_truly_lcd_off,
+	.set_backlight	= mipi_truly_set_backlight,
+};
+
+static int ch_used[3];
+
+int mipi_truly_device_register(struct msm_panel_info *pinfo,
+					u32 channel, u32 panel)
+{
+	struct platform_device *pdev = NULL;
+	int ret;
+
+	if ((channel >= 3) || ch_used[channel])
+		return -ENODEV;
+
+	ch_used[channel] = TRUE;
+
+	pdev = platform_device_alloc("mipi_truly", (panel << 8)|channel);
+
+	if (!pdev)
+		return -ENOMEM;
+
+	truly_panel_data.panel_info = *pinfo;
+
+	ret = platform_device_add_data(pdev, &truly_panel_data,
+				sizeof(truly_panel_data));
+	if (ret) {
+		pr_err("%s: platform_device_add_data failed!\n", __func__);
+		goto err_device_put;
+	}
+
+	ret = platform_device_add(pdev);
+
+	if (ret) {
+		pr_err("%s: platform_device_register failed!\n", __func__);
+		goto err_device_put;
+	}
+
+	return 0;
+
+err_device_put:
+	platform_device_put(pdev);
+	return ret;
+}
+
+static int __init mipi_truly_lcd_init(void)
+{
+	mipi_dsi_buf_alloc(&truly_tx_buf, DSI_BUF_SIZE);
+	mipi_dsi_buf_alloc(&truly_rx_buf, DSI_BUF_SIZE);
+
+	return platform_driver_register(&this_driver);
+}
+
+module_init(mipi_truly_lcd_init);
diff --git a/drivers/video/msm/mipi_truly.h b/drivers/video/msm/mipi_truly.h
new file mode 100644
index 0000000..900e6f6
--- /dev/null
+++ b/drivers/video/msm/mipi_truly.h
@@ -0,0 +1,21 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MIPI_TRULY_H
+#define MIPI_TRULY_H
+
+/* #define MIPI_TRULY_FAKE_PANEL */	/* FAKE PANEL for test */
+
+int mipi_truly_device_register(struct msm_panel_info *pinfo,
+		u32 channel, u32 panel);
+
+#endif  /* MIPI_TRULY_H */
diff --git a/drivers/video/msm/mipi_truly_tft540960_1_e.c b/drivers/video/msm/mipi_truly_tft540960_1_e.c
new file mode 100644
index 0000000..e465d46
--- /dev/null
+++ b/drivers/video/msm/mipi_truly_tft540960_1_e.c
@@ -0,0 +1,817 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_fb.h"
+#include "mipi_dsi.h"
+#include "mipi_truly_tft540960_1_e.h"
+
+static struct msm_panel_common_pdata *mipi_truly_pdata;
+static struct dsi_buf truly_tx_buf;
+static struct dsi_buf truly_rx_buf;
+
+#define TRULY_CMD_DELAY 0
+#define MIPI_SETTING_DELAY 10
+#define TRULY_SLEEP_OFF_DELAY 150
+#define TRULY_DISPLAY_ON_DELAY 150
+
+/* common setting */
+static char exit_sleep[2] = {0x11, 0x00};
+static char display_on[2] = {0x29, 0x00};
+static char display_off[2] = {0x28, 0x00};
+static char enter_sleep[2] = {0x10, 0x00};
+static char write_ram[2] = {0x2c, 0x00}; /* write ram */
+
+static struct dsi_cmd_desc truly_display_off_cmds[] = {
+	{DTYPE_DCS_WRITE, 1, 0, 0, 150, sizeof(display_off), display_off},
+	{DTYPE_DCS_WRITE, 1, 0, 0, 150, sizeof(enter_sleep), enter_sleep}
+};
+
+
+/* TFT540960_1_E CMD mode */
+static char cmd0[5] = {
+	0xFF, 0xAA, 0x55, 0x25,
+	0x01,
+};
+
+static char cmd2[5] = {
+	0xF3, 0x02, 0x03, 0x07,
+	0x45,
+};
+
+static char cmd3[6] = {
+	0xF0, 0x55, 0xAA, 0x52,
+	0x08, 0x00,
+};
+
+static char cmd4[2] = {
+	0xB1, 0xeC,
+};
+
+/* add 0X BD command */
+static char cmd26_2[6] = {
+	0xBD, 0x01, 0x48, 0x10, 0x38, 0x01 /* 59 HZ */
+};
+
+static char cmd5[5] = {
+	0xB8, 0x01, 0x02, 0x02,
+	0x02,
+};
+
+static char cmd6[4] = {
+	0xBC, 0x05, 0x05, 0x05,
+};
+
+static char cmd7[2] = {
+	0x4C, 0x11,
+};
+
+static char cmd8[6] = {
+	0xF0, 0x55, 0xAA, 0x52,
+	0x08, 0x01,
+};
+
+static char cmd9[4] = {
+	0xB0, 0x05, 0x05, 0x05,
+};
+
+static char cmd10[4] = {
+	0xB6, 0x44, 0x44, 0x44,
+};
+static char cmd11[4] = {
+	0xB1, 0x05, 0x05, 0x05,
+};
+
+static char cmd12[4] = {
+	0xB7, 0x34, 0x34, 0x34,
+};
+
+static char cmd13[4] = {
+	0xB3, 0x10, 0x10, 0x10,
+};
+
+static char cmd14[4] = {
+	0xB9, 0x34, 0x34, 0x34,
+};
+
+static char cmd15[4] = {
+	0xB4, 0x0A, 0x0A, 0x0A,
+};
+
+static char cmd16[4] = {
+	0xBA, 0x14, 0x14, 0x14,
+};
+static char cmd17[4] = {
+	0xBC, 0x00, 0xA0, 0x00,
+};
+
+static char cmd18[4] = {
+	0xBD, 0x00, 0xA0, 0x00,
+};
+
+static char cmd19[2] = {
+	0xBE, 0x45,
+};
+
+static char cmd20[17] = {
+	0xD1, 0x00, 0x32, 0x00,
+	0x41, 0x00, 0x54, 0x00,
+	0x67, 0x00, 0x7A, 0x00,
+	0x98, 0x00, 0xB0, 0x00,
+	0xDB,
+};
+
+static char cmd21[17] = {
+	0xD2, 0x01, 0x01, 0x01,
+	0x3F, 0x01, 0x70, 0x01,
+	0xB4, 0x01, 0xEC, 0x01,
+	0xED, 0x02, 0x1E, 0x02,
+	0x51,
+};
+
+static char cmd22[17] = {
+	0xD3, 0x02, 0x6C, 0x02,
+	0x8D, 0x02, 0xA5, 0x02,
+	0xC9, 0x02, 0xEA, 0x03,
+	0x19, 0x03, 0x45, 0x03,
+	0x7A,
+};
+
+static char cmd23[5] = {
+	0xD4, 0x03, 0xB0, 0x03,
+	0xF4,
+};
+
+static char cmd24[17] = {
+	0xD5, 0x00, 0x32, 0x00,
+	0x41, 0x00, 0x54, 0x00,
+	0x67, 0x00, 0x7A, 0x00,
+	0x98, 0x00, 0xB0, 0x00,
+	0xDB,
+};
+static char cmd25[17] = {
+	0xD6, 0x01, 0x01, 0x01,
+	0x3F, 0x01, 0x70, 0x01,
+	0xB4, 0x01, 0xEC, 0x01,
+	0xED, 0x02, 0x1E, 0x02,
+	0x51,
+};
+
+static char cmd26[17] = {
+	0xD7, 0x02, 0x6C, 0x02,
+	0x8D, 0x02, 0xA5, 0x02,
+	0xC9, 0x02, 0xEA, 0x03,
+	0x19, 0x03, 0x45, 0x03,
+	0x7A,
+};
+static char cmd27[5] = {
+	0xD8, 0x03, 0xB0, 0x03,
+	0xF4,
+};
+
+
+static char cmd28[17] = {
+	0xD9, 0x00, 0x32, 0x00,
+	0x41, 0x00, 0x54, 0x00,
+	0x67, 0x00, 0x7A, 0x00,
+	0x98, 0x00, 0xB0, 0x00,
+	0xDB,
+};
+
+static char cmd29[17] = {
+	0xDD, 0x01, 0x01, 0x01,
+	0x3F, 0x01, 0x70, 0x01,
+	0xB4, 0x01, 0xEC, 0x01,
+	0xED, 0x02, 0x1E, 0x02,
+	0x51,
+};
+static char cmd30[17] = {
+	0xDE, 0x02, 0x6C, 0x02,
+	0x8D, 0x02, 0xA5, 0x02,
+	0xC9, 0x02, 0xEA, 0x03,
+	0x19, 0x03, 0x45, 0x03,
+	0x7A,
+};
+
+static char cmd31[5] = {
+	0xDF, 0x03, 0xB0, 0x03,
+	0xF4,
+};
+
+static char cmd32[17] = {
+	0xE0, 0x00, 0x32, 0x00,
+	0x41, 0x00, 0x54, 0x00,
+	0x67, 0x00, 0x7A, 0x00,
+	0x98, 0x00, 0xB0, 0x00,
+	0xDB,
+};
+
+static char cmd33[17] = {
+	0xE1, 0x01, 0x01, 0x01,
+	0x3F, 0x01, 0x70, 0x01,
+	0xB4, 0x01, 0xEC, 0x01,
+	0xED, 0x02, 0x1E, 0x02,
+	0x51,
+};
+
+static char cmd34[17] = {
+	0xE2, 0x02, 0x6C, 0x02,
+	0x8D, 0x02, 0xA5, 0x02,
+	0xC9, 0x02, 0xEA, 0x03,
+	0x19, 0x03, 0x45, 0x03,
+	0x7A,
+};
+
+static char cmd35[5] = {
+	0xE3, 0x03, 0xB0, 0x03,
+	0xF4,
+};
+
+static char cmd36[17] = {
+	0xE4, 0x00, 0x32, 0x00,
+	0x41, 0x00, 0x54, 0x00,
+	0x67, 0x00, 0x7A, 0x00,
+	0x98, 0x00, 0xB0, 0x00,
+	0xDB,
+};
+static char cmd37[17] = {
+	0xE5, 0x01, 0x01, 0x01,
+	0x3F, 0x01, 0x70, 0x01,
+	0xB4, 0x01, 0xEC, 0x01,
+	0xED, 0x02, 0x1E, 0x02,
+	0x51,
+};
+
+static char cmd38[17] = {
+	0xE6, 0x02, 0x6C, 0x02,
+	0x8D, 0x02, 0xA5, 0x02,
+	0xC9, 0x02, 0xEA, 0x03,
+	0x19, 0x03, 0x45, 0x03,
+	0x7A,
+};
+
+static char cmd39[5] = {
+	0xE7, 0x03, 0xB0, 0x03,
+	0xF4,
+};
+
+static char cmd40[17] = {
+	0xE8, 0x00, 0x32, 0x00,
+	0x41, 0x00, 0x54, 0x00,
+	0x67, 0x00, 0x7A, 0x00,
+	0x98, 0x00, 0xB0, 0x00,
+	0xDB,
+};
+
+static char cmd41[17] = {
+	0xE9, 0x01, 0x01, 0x01,
+	0x3F, 0x01, 0x70, 0x01,
+	0xB4, 0x01, 0xEC, 0x01,
+	0xED, 0x02, 0x1E, 0x02,
+	0x51,
+};
+
+static char cmd42[17] = {
+	0xEA, 0x02, 0x6C, 0x02,
+	0x8D, 0x02, 0xA5, 0x02,
+	0xC9, 0x02, 0xEA, 0x03,
+	0x19, 0x03, 0x45, 0x03,
+	0x7A,
+};
+
+static char cmd43[5] = {
+	0xEB, 0x03, 0xB0, 0x03,
+	0xF4,
+};
+
+static char cmd44[2] = {
+	0x3A, 0x07,
+};
+
+static char cmd45[2] = {
+	0x35, 0x00,
+};
+
+
+static struct dsi_cmd_desc truly_cmd_display_on_cmds[] = {
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd0), cmd0},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd2), cmd2},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd3), cmd3},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd4), cmd4},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd26_2), cmd26_2},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd5), cmd5},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd6), cmd6},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd7), cmd7},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd8), cmd8},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd9), cmd9},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd10), cmd10},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd11), cmd11},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd12), cmd12},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd13), cmd13},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd14), cmd14},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd15), cmd15},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd16), cmd16},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd17), cmd17},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd18), cmd18},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd19), cmd19},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd20), cmd20},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd21), cmd21},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd22), cmd22},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd23), cmd23},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd24), cmd24},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd25), cmd25},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd26), cmd26},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd27), cmd27},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd28), cmd28},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd29), cmd29},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd30), cmd30},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd31), cmd31},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd32), cmd32},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd33), cmd33},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd34), cmd34},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd35), cmd35},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd36), cmd36},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd37), cmd37},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd38), cmd38},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd39), cmd39},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd40), cmd40},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd41), cmd41},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd42), cmd42},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd43), cmd43},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd44), cmd44},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd45), cmd45},
+	{DTYPE_DCS_WRITE, 1, 0, 0, TRULY_SLEEP_OFF_DELAY, sizeof(exit_sleep),
+								exit_sleep},
+	{DTYPE_DCS_WRITE, 1, 0, 0, TRULY_CMD_DELAY, sizeof(display_on),
+							display_on},
+	{DTYPE_DCS_WRITE, 1, 0, 0, TRULY_CMD_DELAY, sizeof(write_ram),
+							write_ram},
+
+};
+
+/* TFT540960_1_E VIDEO mode */
+static char video0[5] = {
+	0xFF, 0xAA, 0x55, 0x25,
+	0x01,
+};
+
+static char video2[5] = {
+	0xF3, 0x02, 0x03, 0x07,
+	0x15,
+};
+
+static char video3[6] = {
+	0xF0, 0x55, 0xAA, 0x52,
+	0x08, 0x00,
+};
+
+static char video4[2] = {
+	0xB1, 0xFC,
+};
+
+static char video5[5] = {
+	0xB8, 0x01, 0x02, 0x02,
+	0x02,
+};
+
+static char video6[4] = {
+	0xBC, 0x05, 0x05, 0x05,
+};
+
+static char video7[2] = {
+	0x4C, 0x11,
+};
+
+static char video8[6] = {
+	0xF0, 0x55, 0xAA, 0x52,
+	0x08, 0x01,
+};
+
+static char video9[4] = {
+	0xB0, 0x05, 0x05, 0x05,
+};
+
+static char video10[4] = {
+	0xB6, 0x44, 0x44, 0x44,
+};
+
+static char video11[4] = {
+	0xB1, 0x05, 0x05, 0x05,
+};
+
+static char video12[4] = {
+	0xB7, 0x34, 0x34, 0x34,
+};
+
+static char video13[4] = {
+	0xB3, 0x10, 0x10, 0x10,
+};
+
+static char video14[4] = {
+	0xB9, 0x34, 0x34, 0x34,
+};
+
+static char video15[4] = {
+	0xB4, 0x0A, 0x0A, 0x0A,
+};
+
+static char video16[4] = {
+	0xBA, 0x14, 0x14, 0x14,
+};
+
+static char video17[4] = {
+	0xBC, 0x00, 0xA0, 0x00,
+};
+
+static char video18[4] = {
+	0xBD, 0x00, 0xA0, 0x00,
+};
+
+static char video19[2] = {
+	0xBE, 0x45,
+};
+
+static char video20[17] = {
+	0xD1, 0x00, 0x32, 0x00,
+	0x41, 0x00, 0x54, 0x00,
+	0x67, 0x00, 0x7A, 0x00,
+	0x98, 0x00, 0xB0, 0x00,
+	0xDB,
+};
+
+static char video21[17] = {
+	0xD2, 0x01, 0x01, 0x01,
+	0x3F, 0x01, 0x70, 0x01,
+	0xB4, 0x01, 0xEC, 0x01,
+	0xED, 0x02, 0x1E, 0x02,
+	0x51,
+};
+
+static char video22[17] = {
+	0xD3, 0x02, 0x6C, 0x02,
+	0x8D, 0x02, 0xA5, 0x02,
+	0xC9, 0x02, 0xEA, 0x03,
+	0x19, 0x03, 0x45, 0x03,
+	0x7A,
+};
+
+static char video23[5] = {
+	0xD4, 0x03, 0xB0, 0x03,
+	0xF4,
+};
+
+static char video24[17] = {
+	0xD5, 0x00, 0x32, 0x00,
+	0x41, 0x00, 0x54, 0x00,
+	0x67, 0x00, 0x7A, 0x00,
+	0x98, 0x00, 0xB0, 0x00,
+	0xDB,
+};
+
+static char video25[17] = {
+	0xD6, 0x01, 0x01, 0x01,
+	0x3F, 0x01, 0x70, 0x01,
+	0xB4, 0x01, 0xEC, 0x01,
+	0xED, 0x02, 0x1E, 0x02,
+	0x51,
+};
+
+static char video26[17] = {
+	0xD7, 0x02, 0x6C, 0x02,
+	0x8D, 0x02, 0xA5, 0x02,
+	0xC9, 0x02, 0xEA, 0x03,
+	0x19, 0x03, 0x45, 0x03,
+	0x7A,
+};
+
+static char video27[5] = {
+	0xD8, 0x03, 0xB0, 0x03,
+	0xF4,
+};
+
+static char video28[17] = {
+	0xD9, 0x00, 0x32, 0x00,
+	0x41, 0x00, 0x54, 0x00,
+	0x67, 0x00, 0x7A, 0x00,
+	0x98, 0x00, 0xB0, 0x00,
+	0xDB,
+};
+
+static char video29[17] = {
+	0xDD, 0x01, 0x01, 0x01,
+	0x3F, 0x01, 0x70, 0x01,
+	0xB4, 0x01, 0xEC, 0x01,
+	0xED, 0x02, 0x1E, 0x02,
+	0x51,
+};
+
+static char video30[17] = {
+	0xDE, 0x02, 0x6C, 0x02,
+	0x8D, 0x02, 0xA5, 0x02,
+	0xC9, 0x02, 0xEA, 0x03,
+	0x19, 0x03, 0x45, 0x03,
+	0x7A,
+};
+
+static char video31[5] = {
+	0xDF, 0x03, 0xB0, 0x03,
+	0xF4,
+};
+
+static char video32[17] = {
+	0xE0, 0x00, 0x32, 0x00,
+	0x41, 0x00, 0x54, 0x00,
+	0x67, 0x00, 0x7A, 0x00,
+	0x98, 0x00, 0xB0, 0x00,
+	0xDB,
+};
+
+static char video33[17] = {
+	0xE1, 0x01, 0x01, 0x01,
+	0x3F, 0x01, 0x70, 0x01,
+	0xB4, 0x01, 0xEC, 0x01,
+	0xED, 0x02, 0x1E, 0x02,
+	0x51,
+};
+
+static char video34[17] = {
+	0xE2, 0x02, 0x6C, 0x02,
+	0x8D, 0x02, 0xA5, 0x02,
+	0xC9, 0x02, 0xEA, 0x03,
+	0x19, 0x03, 0x45, 0x03,
+	0x7A,
+};
+
+static char video35[5] = {
+	0xE3, 0x03, 0xB0, 0x03,
+	0xF4,
+};
+
+static char video36[17] = {
+	0xE4, 0x00, 0x32, 0x00,
+	0x41, 0x00, 0x54, 0x00,
+	0x67, 0x00, 0x7A, 0x00,
+	0x98, 0x00, 0xB0, 0x00,
+	0xDB,
+};
+
+static char video37[17] = {
+	0xE5, 0x01, 0x01, 0x01,
+	0x3F, 0x01, 0x70, 0x01,
+	0xB4, 0x01, 0xEC, 0x01,
+	0xED, 0x02, 0x1E, 0x02,
+	0x51,
+};
+
+static char video38[17] = {
+	0xE6, 0x02, 0x6C, 0x02,
+	0x8D, 0x02, 0xA5, 0x02,
+	0xC9, 0x02, 0xEA, 0x03,
+	0x19, 0x03, 0x45, 0x03,
+	0x7A,
+};
+
+static char video39[5] = {
+	0xE7, 0x03, 0xB0, 0x03,
+	0xF4,
+};
+
+static char video40[17] = {
+	0xE8, 0x00, 0x32, 0x00,
+	0x41, 0x00, 0x54, 0x00,
+	0x67, 0x00, 0x7A, 0x00,
+	0x98, 0x00, 0xB0, 0x00,
+	0xDB,
+};
+
+static char video41[17] = {
+	0xE9, 0x01, 0x01, 0x01,
+	0x3F, 0x01, 0x70, 0x01,
+	0xB4, 0x01, 0xEC, 0x01,
+	0xED, 0x02, 0x1E, 0x02,
+	0x51,
+};
+
+static char video42[17] = {
+	0xEA, 0x02, 0x6C, 0x02,
+	0x8D, 0x02, 0xA5, 0x02,
+	0xC9, 0x02, 0xEA, 0x03,
+	0x19, 0x03, 0x45, 0x03,
+	0x7A,
+};
+
+static char video43[5] = {
+	0xEB, 0x03, 0xB0, 0x03,
+	0xF4,
+};
+
+static char video44[2] = {
+	0x3A, 0x07,
+};
+
+static char video45[2] = {
+	0x35, 0x00,
+};
+
+static struct dsi_cmd_desc truly_video_display_on_cmds[] = {
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video0), video0},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video2), video2},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video3), video3},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video4), video4},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video5), video5},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video6), video6},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video7), video7},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video8), video8},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video9), video9},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video10), video10},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video11), video11},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video12), video12},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video13), video13},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video14), video14},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video15), video15},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video16), video16},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video17), video17},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video18), video18},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video19), video19},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video20), video20},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video21), video21},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video22), video22},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video23), video23},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video24), video24},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video25), video25},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video26), video26},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video27), video27},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video28), video28},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video29), video29},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video30), video30},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video31), video31},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video32), video32},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video33), video33},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video34), video34},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video35), video35},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video36), video36},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video37), video37},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video38), video38},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video39), video39},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video40), video40},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video41), video41},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video42), video42},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video43), video43},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video44), video44},
+	{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video45), video45},
+
+	{DTYPE_DCS_WRITE, 1, 0, 0, 150, sizeof(exit_sleep), exit_sleep},
+	{DTYPE_DCS_WRITE, 1, 0, 0, 50, sizeof(display_on), display_on},
+};
+
+static int mipi_truly_lcd_on(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+	struct mipi_panel_info *mipi;
+
+	mfd = platform_get_drvdata(pdev);
+
+	if (!mfd)
+		return -ENODEV;
+
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	mipi  = &mfd->panel_info.mipi;
+	pr_info("%s: mode = %d\n", __func__, mipi->mode);
+	msleep(120);
+
+	if (mipi->mode == DSI_VIDEO_MODE) {
+		mipi_dsi_cmds_tx(mfd, &truly_tx_buf,
+			truly_video_display_on_cmds,
+			ARRAY_SIZE(truly_video_display_on_cmds));
+	} else if (mipi->mode == DSI_CMD_MODE) {
+		mipi_dsi_cmds_tx(mfd, &truly_tx_buf,
+			truly_cmd_display_on_cmds,
+			ARRAY_SIZE(truly_cmd_display_on_cmds));
+	}
+
+	return 0;
+}
+
+static int mipi_truly_lcd_off(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+
+	mfd = platform_get_drvdata(pdev);
+
+	if (!mfd)
+		return -ENODEV;
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	mipi_dsi_cmds_tx(mfd, &truly_tx_buf, truly_display_off_cmds,
+			ARRAY_SIZE(truly_display_off_cmds));
+
+	return 0;
+}
+
+static int __devinit mipi_truly_lcd_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+
+	if (pdev->id == 0) {
+		mipi_truly_pdata = pdev->dev.platform_data;
+		if (mipi_truly_pdata->bl_lock)
+			spin_lock_init(&mipi_truly_pdata->bl_spinlock);
+		return rc;
+	}
+
+	msm_fb_add_device(pdev);
+
+	return rc;
+}
+
+static struct platform_driver this_driver = {
+	.probe  = mipi_truly_lcd_probe,
+	.driver = {
+		.name   = "mipi_truly_tft540960_1_e",
+	},
+};
+
+static void mipi_truly_set_backlight(struct msm_fb_data_type *mfd)
+{
+	int bl_level;
+	unsigned long flags;
+	bl_level = mfd->bl_level;
+
+	if (mipi_truly_pdata->bl_lock) {
+		spin_lock_irqsave(&mipi_truly_pdata->bl_spinlock, flags);
+		mipi_truly_pdata->pmic_backlight(bl_level);
+		spin_unlock_irqrestore(&mipi_truly_pdata->bl_spinlock, flags);
+	} else
+		mipi_truly_pdata->pmic_backlight(bl_level);
+}
+
+static struct msm_fb_panel_data truly_panel_data = {
+	.on	= mipi_truly_lcd_on,
+	.off = mipi_truly_lcd_off,
+	.set_backlight = mipi_truly_set_backlight,
+};
+
+static int ch_used[3];
+
+static int mipi_truly_tft540960_1_e_lcd_init(void)
+{
+	mipi_dsi_buf_alloc(&truly_tx_buf, DSI_BUF_SIZE);
+	mipi_dsi_buf_alloc(&truly_rx_buf, DSI_BUF_SIZE);
+
+	return platform_driver_register(&this_driver);
+}
+int mipi_truly_tft540960_1_e_device_register(struct msm_panel_info *pinfo,
+					u32 channel, u32 panel)
+{
+	struct platform_device *pdev = NULL;
+	int ret;
+
+	if ((channel >= 3) || ch_used[channel])
+		return -ENODEV;
+
+	ch_used[channel] = TRUE;
+
+	ret = mipi_truly_tft540960_1_e_lcd_init();
+	if (ret) {
+		pr_err("%s: platform_device_register failed!\n", __func__);
+		return ret;
+	}
+
+	pdev = platform_device_alloc("mipi_truly_tft540960_1_e",
+						(panel << 8)|channel);
+	if (!pdev)
+		return -ENOMEM;
+
+	truly_panel_data.panel_info = *pinfo;
+
+	ret = platform_device_add_data(pdev, &truly_panel_data,
+		sizeof(truly_panel_data));
+	if (ret) {
+		pr_err("%s: platform_device_add_data failed!\n", __func__);
+		goto err_device_put;
+	}
+
+	ret = platform_device_add(pdev);
+	if (ret) {
+		pr_err("%s: platform_device_register failed!\n", __func__);
+		goto err_device_put;
+	}
+
+	return 0;
+
+err_device_put:
+	platform_device_put(pdev);
+	return ret;
+}
diff --git a/drivers/video/msm/mipi_truly_tft540960_1_e.h b/drivers/video/msm/mipi_truly_tft540960_1_e.h
new file mode 100644
index 0000000..8cbfb80
--- /dev/null
+++ b/drivers/video/msm/mipi_truly_tft540960_1_e.h
@@ -0,0 +1,20 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef MIPI_TRULY_H
+#define MIPI_TRULY_H
+
+int mipi_truly_tft540960_1_e_device_register(struct msm_panel_info *pinfo,
+					u32 channel, u32 panel);
+
+#endif  /* MIPI_TRULY_H */
diff --git a/drivers/video/msm/mipi_truly_tft540960_1_e_cmd_qhd_pt.c b/drivers/video/msm/mipi_truly_tft540960_1_e_cmd_qhd_pt.c
new file mode 100644
index 0000000..de98177
--- /dev/null
+++ b/drivers/video/msm/mipi_truly_tft540960_1_e_cmd_qhd_pt.c
@@ -0,0 +1,98 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_fb.h"
+#include "mipi_dsi.h"
+#include "mipi_truly_tft540960_1_e.h"
+
+static struct msm_panel_info pinfo;
+
+static struct mipi_dsi_phy_ctrl dsi_cmd_mode_phy_db = {
+	/* DSI Bit Clock at 500 MHz, 2 lane, RGB888 */
+	/* regulator */
+	{0x03, 0x01, 0x01, 0x00},
+	/* timing   */
+	{0xb9, 0x8e, 0x1f, 0x00, 0x98, 0x9c, 0x22, 0x90,
+	0x18, 0x03, 0x04},
+	/* phy ctrl */
+	{0x7f, 0x00, 0x00, 0x00},
+	/* strength */
+	{0xbb, 0x02, 0x06, 0x00},
+	/* pll control */
+	{0x01, 0xec, 0x31, 0xd2, 0x00, 0x40, 0x37, 0x62,
+	0x01, 0x0f, 0x07,
+	0x05, 0x14, 0x03, 0x0, 0x0, 0x0, 0x20, 0x0, 0x02, 0x0},
+};
+
+static int mipi_cmd_truly_qhd_pt_init(void)
+{
+	int ret;
+	if (msm_fb_detect_client("mipi_cmd_truly_qhd"))
+		return 0;
+
+	pinfo.xres = 540;
+	pinfo.yres = 960;
+	pinfo.type = MIPI_CMD_PANEL;
+	pinfo.pdest = DISPLAY_1;
+	pinfo.wait_cycle = 0;
+	pinfo.bpp = 24;
+	pinfo.lcdc.h_back_porch = 100;
+	pinfo.lcdc.h_front_porch = 100;
+	pinfo.lcdc.h_pulse_width = 8;
+	pinfo.lcdc.v_back_porch = 20;
+	pinfo.lcdc.v_front_porch = 20;
+	pinfo.lcdc.v_pulse_width = 1;
+
+	pinfo.lcdc.border_clr = 0;	/* blk */
+	pinfo.lcdc.underflow_clr = 0xff;	/* blue */
+	pinfo.lcdc.hsync_skew = 0;
+	pinfo.bl_max = 31;
+	pinfo.bl_min = 1;
+	pinfo.fb_num = 2;
+
+	pinfo.clk_rate = 499000000;
+
+	pinfo.lcd.vsync_enable = TRUE;
+	pinfo.lcd.hw_vsync_mode = TRUE;
+	pinfo.lcd.refx100 = 6100; /* adjust refx100 to prevent tearing */
+
+	pinfo.mipi.mode = DSI_CMD_MODE;
+	pinfo.mipi.dst_format = DSI_CMD_DST_FORMAT_RGB888;
+	pinfo.mipi.vc = 0;
+	pinfo.mipi.rgb_swap = DSI_RGB_SWAP_RGB;
+	pinfo.mipi.data_lane0 = TRUE;
+	pinfo.mipi.data_lane1 = TRUE;
+	pinfo.mipi.t_clk_post = 0x20;
+	pinfo.mipi.t_clk_pre = 0x2F;
+	pinfo.mipi.stream = 0; /* dma_p */
+	pinfo.mipi.mdp_trigger = DSI_CMD_TRIGGER_SW_TE;
+	pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW;
+	pinfo.mipi.te_sel = 1; /* TE from vsync gpio */
+	pinfo.mipi.interleave_max = 1;
+	pinfo.mipi.insert_dcs_cmd = TRUE;
+	pinfo.mipi.wr_mem_continue = 0x3c;
+	pinfo.mipi.wr_mem_start = 0x2c;
+	pinfo.mipi.dsi_phy_db = &dsi_cmd_mode_phy_db;
+	pinfo.mipi.tx_eot_append = 0x01;
+	pinfo.mipi.rx_eot_ignore = 0x0;
+	pinfo.mipi.dlane_swap = 0x01;
+
+	ret = mipi_truly_tft540960_1_e_device_register(&pinfo, MIPI_DSI_PRIM,
+						MIPI_DSI_PANEL_WVGA_PT);
+	if (ret)
+		pr_err("%s: failed to register device!\n", __func__);
+
+	return ret;
+}
+
+module_init(mipi_cmd_truly_qhd_pt_init);
diff --git a/drivers/video/msm/mipi_truly_tft540960_1_e_video_qhd_pt.c b/drivers/video/msm/mipi_truly_tft540960_1_e_video_qhd_pt.c
new file mode 100644
index 0000000..ea2ff47
--- /dev/null
+++ b/drivers/video/msm/mipi_truly_tft540960_1_e_video_qhd_pt.c
@@ -0,0 +1,107 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_fb.h"
+#include "mipi_dsi.h"
+#include "mipi_truly_tft540960_1_e.h"
+
+static struct msm_panel_info pinfo;
+
+static struct mipi_dsi_phy_ctrl dsi_video_mode_phy_db = {
+	/* DSI Bit Clock at 500 MHz, 2 lane, RGB888 */
+	/* regulator */
+	{0x03, 0x01, 0x01, 0x00},
+	/* timing   */
+	{0xb9, 0x8e, 0x1f, 0x00, 0x98, 0x9c, 0x22, 0x90,
+	0x18, 0x03, 0x04},
+	/* phy ctrl */
+	{0x7f, 0x00, 0x00, 0x00},
+	/* strength */
+	{0xbb, 0x02, 0x06, 0x00},
+	/* pll control */
+	{0x00, 0xec, 0x31, 0xd2, 0x00, 0x40, 0x37, 0x62,
+	0x01, 0x0f, 0x07,
+	0x05, 0x14, 0x03, 0x0, 0x0, 0x0, 0x20, 0x0, 0x02, 0x0},
+};
+
+static int mipi_video_truly_qhd_pt_init(void)
+{
+	int ret;
+	if (msm_fb_detect_client("mipi_video_truly_qhd"))
+		return 0;
+
+	pinfo.xres = 540;
+	pinfo.yres = 960;
+	pinfo.type = MIPI_VIDEO_PANEL;
+	pinfo.pdest = DISPLAY_1;
+	pinfo.wait_cycle = 0;
+	pinfo.bpp = 24;
+	pinfo.lcdc.h_back_porch = 100;
+	pinfo.lcdc.h_front_porch = 100;
+	pinfo.lcdc.h_pulse_width = 8;
+	pinfo.lcdc.v_back_porch = 20;
+	pinfo.lcdc.v_front_porch = 20;
+	pinfo.lcdc.v_pulse_width = 1;
+	pinfo.lcdc.border_clr = 0;	/* blk */
+	pinfo.lcdc.underflow_clr = 0xff;	/* blue */
+	/* number of dot_clk cycles HSYNC active edge
+	   is delayed from VSYNC active edge */
+	pinfo.lcdc.hsync_skew = 0;
+	pinfo.clk_rate = 699000000;
+	pinfo.lcd.refx100 = 6000; /* FB driver calc FPS based on this value */
+	pinfo.bl_max = 31;
+	pinfo.bl_min = 1;
+	pinfo.fb_num = 2;
+
+	pinfo.mipi.mode = DSI_VIDEO_MODE;
+	/* send HSA and HE following VS/VE packet */
+	pinfo.mipi.pulse_mode_hsa_he = TRUE;
+	pinfo.mipi.hfp_power_stop = TRUE; /* LP-11 during the HFP period */
+	pinfo.mipi.hbp_power_stop = TRUE; /* LP-11 during the HBP period */
+	pinfo.mipi.hsa_power_stop = TRUE; /* LP-11 during the HSA period */
+	/* LP-11 or let Command Mode Engine send packets in
+	HS or LP mode for the BLLP of the last line of a frame */
+	pinfo.mipi.eof_bllp_power_stop = TRUE;
+	/* LP-11 or let Command Mode Engine send packets in
+	HS or LP mode for packets sent during BLLP period */
+	pinfo.mipi.bllp_power_stop = TRUE;
+
+	pinfo.mipi.traffic_mode = DSI_BURST_MODE;
+	pinfo.mipi.dst_format =  DSI_VIDEO_DST_FORMAT_RGB888;
+	pinfo.mipi.vc = 0;
+	pinfo.mipi.rgb_swap = DSI_RGB_SWAP_RGB; /* RGB */
+	pinfo.mipi.data_lane0 = TRUE;
+	pinfo.mipi.data_lane1 = TRUE;
+
+	pinfo.mipi.t_clk_post = 0x20;
+	pinfo.mipi.t_clk_pre = 0x2f;
+
+	pinfo.mipi.stream = 0; /* dma_p */
+	pinfo.mipi.mdp_trigger = DSI_CMD_TRIGGER_NONE;
+	pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW;
+	pinfo.mipi.frame_rate = 60;
+
+	pinfo.mipi.dsi_phy_db = &dsi_video_mode_phy_db;
+	pinfo.mipi.dlane_swap = 0x01;
+	/* append EOT at the end of data burst */
+	pinfo.mipi.tx_eot_append = 0x01;
+
+	ret = mipi_truly_tft540960_1_e_device_register(&pinfo, MIPI_DSI_PRIM,
+						MIPI_DSI_PANEL_WVGA_PT);
+	if (ret)
+		pr_err("%s: failed to register device!\n", __func__);
+
+	return ret;
+}
+
+module_init(mipi_video_truly_qhd_pt_init);
diff --git a/drivers/video/msm/mipi_truly_video_wvga_pt.c b/drivers/video/msm/mipi_truly_video_wvga_pt.c
new file mode 100644
index 0000000..03ef32b
--- /dev/null
+++ b/drivers/video/msm/mipi_truly_video_wvga_pt.c
@@ -0,0 +1,108 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_fb.h"
+#include "mipi_dsi.h"
+#include "mipi_truly.h"
+
+static struct msm_panel_info pinfo;
+
+static struct mipi_dsi_phy_ctrl dsi_video_mode_phy_db = {
+	/* DSI Bit Clock at 500 MHz, 2 lane, RGB888 */
+	/* regulator */
+	{0x03, 0x01, 0x01, 0x00},
+	/* timing   */
+	{0xb9, 0x8e, 0x1f, 0x00, 0x98, 0x9c, 0x22, 0x90,
+	0x18, 0x03, 0x04},
+	/* phy ctrl */
+	{0x7f, 0x00, 0x00, 0x00},
+	/* strength */
+	{0xbb, 0x02, 0x06, 0x00},
+	/* pll control */
+	{0x00, 0xec, 0x31, 0xd2, 0x00, 0x40, 0x37, 0x62,
+	0x01, 0x0f, 0x07,
+	0x05, 0x14, 0x03, 0x0, 0x0, 0x0, 0x20, 0x0, 0x02, 0x0},
+};
+
+static int __init mipi_video_truly_wvga_pt_init(void)
+{
+	int ret;
+
+	if (msm_fb_detect_client("mipi_video_truly_wvga"))
+		return 0;
+
+	pinfo.xres = 480;
+	pinfo.yres = 800;
+	pinfo.type = MIPI_VIDEO_PANEL;
+	pinfo.pdest = DISPLAY_1;
+	pinfo.wait_cycle = 0;
+	pinfo.bpp = 24;
+	pinfo.lcdc.h_back_porch = 100;
+	pinfo.lcdc.h_front_porch = 100;
+	pinfo.lcdc.h_pulse_width = 8;
+	pinfo.lcdc.v_back_porch = 20;
+	pinfo.lcdc.v_front_porch = 20;
+	pinfo.lcdc.v_pulse_width = 1;
+	pinfo.lcdc.border_clr = 0;	/* blk */
+	pinfo.lcdc.underflow_clr = 0xff;	/* blue */
+	/* number of dot_clk cycles HSYNC active edge
+	   is delayed from VSYNC active edge */
+	pinfo.lcdc.hsync_skew = 0;
+	pinfo.clk_rate = 499000000;
+	pinfo.bl_max = 15;
+	pinfo.bl_min = 1;
+	pinfo.fb_num = 2;
+
+	pinfo.mipi.mode = DSI_VIDEO_MODE;
+	pinfo.mipi.pulse_mode_hsa_he = TRUE; /* send HSA and HE following
+						VS/VE packet */
+	pinfo.mipi.hfp_power_stop = TRUE; /* LP-11 during the HFP period */
+	pinfo.mipi.hbp_power_stop = TRUE; /* LP-11 during the HBP period */
+	pinfo.mipi.hsa_power_stop = TRUE; /* LP-11 during the HSA period */
+	/* LP-11 or let Command Mode Engine send packets in
+	HS or LP mode for the BLLP of the last line of a frame */
+	pinfo.mipi.eof_bllp_power_stop = TRUE;
+	/* LP-11 or let Command Mode Engine send packets in
+	HS or LP mode for packets sent during BLLP period */
+	pinfo.mipi.bllp_power_stop = TRUE;
+
+	pinfo.mipi.traffic_mode = DSI_BURST_MODE;
+	pinfo.mipi.dst_format =  DSI_VIDEO_DST_FORMAT_RGB888;
+	pinfo.mipi.vc = 0;
+	pinfo.mipi.rgb_swap = DSI_RGB_SWAP_RGB; /* RGB */
+	pinfo.mipi.data_lane0 = TRUE;
+	pinfo.mipi.data_lane1 = TRUE;
+
+	pinfo.mipi.t_clk_post = 0x20;
+	pinfo.mipi.t_clk_pre = 0x2f;
+
+	pinfo.mipi.stream = 0; /* dma_p */
+	pinfo.mipi.mdp_trigger = DSI_CMD_TRIGGER_NONE;
+	pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW;
+	pinfo.mipi.frame_rate = 60; /* FIXME */
+
+	pinfo.mipi.dsi_phy_db = &dsi_video_mode_phy_db;
+	pinfo.mipi.dlane_swap = 0x01;
+	pinfo.mipi.tx_eot_append = 0x01; /* append EOT at the end
+					    of data burst */
+
+	ret = mipi_truly_device_register(&pinfo, MIPI_DSI_PRIM,
+						MIPI_DSI_PANEL_WVGA_PT);
+
+	if (ret)
+		pr_err("%s: failed to register device!\n", __func__);
+
+	return ret;
+}
+
+module_init(mipi_video_truly_wvga_pt_init);
diff --git a/drivers/video/msm/msm_dss_io_7x27a.c b/drivers/video/msm/msm_dss_io_7x27a.c
new file mode 100644
index 0000000..f4f8375
--- /dev/null
+++ b/drivers/video/msm/msm_dss_io_7x27a.c
@@ -0,0 +1,470 @@
+/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/clk.h>
+#include "msm_fb.h"
+#include "mipi_dsi.h"
+
+/* multimedia sub system sfpb */
+char *mmss_sfpb_base;
+void  __iomem *periph_base;
+
+static struct dsi_clk_desc dsicore_clk;
+static struct dsi_clk_desc dsi_pclk;
+
+static struct clk *dsi_byte_div_clk;
+static struct clk *dsi_esc_clk;
+static struct clk *dsi_pixel_clk;
+static struct clk *dsi_clk;
+static struct clk *dsi_ref_clk;
+static struct clk *mdp_dsi_pclk;
+static struct clk *ahb_m_clk;
+static struct clk *ahb_s_clk;
+static struct clk *ebi1_dsi_clk;
+int mipi_dsi_clk_on;
+
+int mipi_dsi_clk_init(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	dsi_esc_clk = clk_get(dev, "esc_clk");
+	if (IS_ERR_OR_NULL(dsi_esc_clk)) {
+		printk(KERN_ERR "can't find dsi_esc_clk\n");
+		dsi_esc_clk = NULL;
+		goto mipi_dsi_clk_err;
+	}
+
+	dsi_byte_div_clk = clk_get(dev, "byte_clk");
+	if (IS_ERR_OR_NULL(dsi_byte_div_clk)) {
+		pr_err("can't find dsi_byte_div_clk\n");
+		dsi_byte_div_clk = NULL;
+		goto mipi_dsi_clk_err;
+	}
+
+	dsi_pixel_clk = clk_get(dev, "pixel_clk");
+	if (IS_ERR_OR_NULL(dsi_pixel_clk)) {
+		pr_err("can't find dsi_pixel_clk\n");
+		dsi_pixel_clk = NULL;
+		goto mipi_dsi_clk_err;
+	}
+
+	dsi_clk = clk_get(dev, "core_clk");
+	if (IS_ERR_OR_NULL(dsi_clk)) {
+		pr_err("can't find dsi_clk\n");
+		dsi_clk = NULL;
+		goto mipi_dsi_clk_err;
+	}
+
+	dsi_ref_clk = clk_get(dev, "ref_clk");
+	if (IS_ERR_OR_NULL(dsi_ref_clk)) {
+		pr_err("can't find dsi_ref_clk\n");
+		dsi_ref_clk = NULL;
+		goto mipi_dsi_clk_err;
+	}
+
+	mdp_dsi_pclk = clk_get(dev, "mdp_clk");
+	if (IS_ERR_OR_NULL(mdp_dsi_pclk)) {
+		pr_err("can't find mdp_dsi_pclk\n");
+		mdp_dsi_pclk = NULL;
+		goto mipi_dsi_clk_err;
+	}
+
+	ahb_m_clk = clk_get(dev, "master_iface_clk");
+	if (IS_ERR_OR_NULL(ahb_m_clk)) {
+		pr_err("can't find ahb_m_clk\n");
+		ahb_m_clk = NULL;
+		goto mipi_dsi_clk_err;
+	}
+
+	ahb_s_clk = clk_get(dev, "slave_iface_clk");
+	if (IS_ERR_OR_NULL(ahb_s_clk)) {
+		pr_err("can't find ahb_s_clk\n");
+		ahb_s_clk = NULL;
+		goto mipi_dsi_clk_err;
+	}
+
+	ebi1_dsi_clk = clk_get(dev, "mem_clk");
+	if (IS_ERR_OR_NULL(ebi1_dsi_clk)) {
+		pr_err("can't find ebi1_dsi_clk\n");
+		ebi1_dsi_clk = NULL;
+		goto mipi_dsi_clk_err;
+	}
+
+	return 0;
+
+mipi_dsi_clk_err:
+	mipi_dsi_clk_deinit(NULL);
+	return -EPERM;
+}
+
+void mipi_dsi_clk_deinit(struct device *dev)
+{
+	if (mdp_dsi_pclk)
+		clk_put(mdp_dsi_pclk);
+	if (ahb_m_clk)
+		clk_put(ahb_m_clk);
+	if (ahb_s_clk)
+		clk_put(ahb_s_clk);
+	if (dsi_ref_clk)
+		clk_put(dsi_ref_clk);
+	if (dsi_byte_div_clk)
+		clk_put(dsi_byte_div_clk);
+	if (dsi_esc_clk)
+		clk_put(dsi_esc_clk);
+	if (ebi1_dsi_clk)
+		clk_put(ebi1_dsi_clk);
+}
+
+static void mipi_dsi_clk_ctrl(struct dsi_clk_desc *clk, int clk_en)
+{
+	uint32 data;
+	if (clk_en) {
+		data = (clk->pre_div_func) << 24 |
+			(clk->m) << 16 | (clk->n) << 8 |
+			((clk->d) * 2);
+		clk_set_rate(dsi_clk, data);
+		clk_enable(dsi_clk);
+	} else
+		clk_disable(dsi_clk);
+}
+
+static void mipi_dsi_pclk_ctrl(struct dsi_clk_desc *clk, int clk_en)
+{
+	uint32 data;
+
+	if (clk_en) {
+		data = (clk->pre_div_func) << 24 | (clk->m) << 16
+			| (clk->n) << 8 | ((clk->d) * 2);
+		if ((clk_set_rate(dsi_pixel_clk, data)) < 0)
+			pr_err("%s: pixel clk set rate failed\n", __func__);
+		if (clk_enable(dsi_pixel_clk))
+			pr_err("%s clk enable failed\n", __func__);
+	} else {
+		clk_disable(dsi_pixel_clk);
+	}
+}
+
+static void mipi_dsi_calibration(void)
+{
+	MIPI_OUTP(MIPI_DSI_BASE + 0xf8, 0x00a105a1); /* cal_hw_ctrl */
+}
+
+#define PREF_DIV_RATIO 19
+struct dsiphy_pll_divider_config pll_divider_config;
+
+int mipi_dsi_clk_div_config(uint8 bpp, uint8 lanes,
+			    uint32 *expected_dsi_pclk)
+{
+	u32 fb_divider, rate, vco;
+	u32 div_ratio = 0;
+	struct dsi_clk_mnd_table const *mnd_entry = mnd_table;
+	if (pll_divider_config.clk_rate == 0)
+		pll_divider_config.clk_rate = 454000000;
+
+	rate = pll_divider_config.clk_rate / 1000000; /* In Mhz */
+
+	if (rate < 125) {
+		vco = rate * 8;
+		div_ratio = 8;
+	} else if (rate < 250) {
+		vco = rate * 4;
+		div_ratio = 4;
+	} else if (rate < 500) {
+		vco = rate * 2;
+		div_ratio = 2;
+	} else {
+		vco = rate * 1;
+		div_ratio = 1;
+	}
+
+	/* find the mnd settings from mnd_table entry */
+	for (; mnd_entry != mnd_table + ARRAY_SIZE(mnd_table); ++mnd_entry) {
+		if (((mnd_entry->lanes) == lanes) &&
+			((mnd_entry->bpp) == bpp))
+			break;
+	}
+
+	if (mnd_entry == mnd_table + ARRAY_SIZE(mnd_table)) {
+		pr_err("%s: requested Lanes, %u & BPP, %u, not supported\n",
+			__func__, lanes, bpp);
+		return -EINVAL;
+	}
+	fb_divider = ((vco * PREF_DIV_RATIO) / 27);
+	pll_divider_config.fb_divider = fb_divider;
+	pll_divider_config.ref_divider_ratio = PREF_DIV_RATIO;
+	pll_divider_config.bit_clk_divider = div_ratio;
+	pll_divider_config.byte_clk_divider =
+			pll_divider_config.bit_clk_divider * 8;
+	pll_divider_config.dsi_clk_divider =
+			(mnd_entry->dsiclk_div) * div_ratio;
+
+	if ((mnd_entry->dsiclk_d == 0)
+		|| (mnd_entry->dsiclk_m == 1)) {
+		dsicore_clk.mnd_mode = 0;
+		dsicore_clk.src = 0x3;
+		dsicore_clk.pre_div_func = (mnd_entry->dsiclk_n - 1);
+	} else {
+		dsicore_clk.mnd_mode = 2;
+		dsicore_clk.src = 0x3;
+		dsicore_clk.m = mnd_entry->dsiclk_m;
+		dsicore_clk.n = mnd_entry->dsiclk_n;
+		dsicore_clk.d = mnd_entry->dsiclk_d;
+	}
+
+	if ((mnd_entry->pclk_d == 0)
+		|| (mnd_entry->pclk_m == 1)) {
+		dsi_pclk.mnd_mode = 0;
+		dsi_pclk.src = 0x3;
+		dsi_pclk.pre_div_func = (mnd_entry->pclk_n - 1);
+		*expected_dsi_pclk = ((vco * 1000000) /
+					((pll_divider_config.dsi_clk_divider)
+					* (mnd_entry->pclk_n)));
+	} else {
+		dsi_pclk.mnd_mode = 2;
+		dsi_pclk.src = 0x3;
+		dsi_pclk.m = mnd_entry->pclk_m;
+		dsi_pclk.n = mnd_entry->pclk_n;
+		dsi_pclk.d = mnd_entry->pclk_d;
+		*expected_dsi_pclk = ((vco * 1000000 * dsi_pclk.m) /
+					((pll_divider_config.dsi_clk_divider)
+					* (mnd_entry->pclk_n)));
+	}
+	dsicore_clk.m = 1;
+	dsicore_clk.n = 1;
+	dsicore_clk.d = 2;
+	dsicore_clk.pre_div_func = 0;
+
+	dsi_pclk.m = 1;
+	dsi_pclk.n = 3;
+	dsi_pclk.d = 2;
+	dsi_pclk.pre_div_func = 0;
+	return 0;
+}
+
+void mipi_dsi_phy_init(int panel_ndx, struct msm_panel_info const *panel_info,
+	int target_type)
+{
+	struct mipi_dsi_phy_ctrl *pd;
+	int i, off;
+
+	MIPI_OUTP(MIPI_DSI_BASE + 0x128, 0x0001);/* start phy sw reset */
+	wmb();
+	usleep(10);
+	MIPI_OUTP(MIPI_DSI_BASE + 0x128, 0x0000);/* end phy w reset */
+	wmb();
+	usleep(10);
+	MIPI_OUTP(MIPI_DSI_BASE + 0x2cc, 0x0003);/* regulator_ctrl_0 */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x2d0, 0x0001);/* regulator_ctrl_1 */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x2d4, 0x0001);/* regulator_ctrl_2 */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x2d8, 0x0000);/* regulator_ctrl_3 */
+#ifdef DSI_POWER
+	MIPI_OUTP(MIPI_DSI_BASE + 0x2dc, 0x0100);/* regulator_ctrl_4 */
+#endif
+
+	pd = (panel_info->mipi).dsi_phy_db;
+
+	off = 0x02cc;	/* regulator ctrl 0 */
+	for (i = 0; i < 4; i++) {
+		MIPI_OUTP(MIPI_DSI_BASE + off, pd->regulator[i]);
+		wmb();
+		off += 4;
+	}
+
+	off = 0x0260;	/* phy timig ctrl 0 */
+	for (i = 0; i < 11; i++) {
+		MIPI_OUTP(MIPI_DSI_BASE + off, pd->timing[i]);
+		wmb();
+		off += 4;
+	}
+
+	off = 0x0290;	/* ctrl 0 */
+	for (i = 0; i < 4; i++) {
+		MIPI_OUTP(MIPI_DSI_BASE + off, pd->ctrl[i]);
+		wmb();
+		off += 4;
+	}
+
+	off = 0x02a0;	/* strength 0 */
+	for (i = 0; i < 4; i++) {
+		MIPI_OUTP(MIPI_DSI_BASE + off, pd->strength[i]);
+		wmb();
+		off += 4;
+	}
+
+	mipi_dsi_calibration();
+
+	off = 0x0204;	/* pll ctrl 1, skip 0 */
+	for (i = 1; i < 21; i++) {
+		MIPI_OUTP(MIPI_DSI_BASE + off, pd->pll[i]);
+		wmb();
+		off += 4;
+	}
+
+	MIPI_OUTP(MIPI_DSI_BASE + 0x100, 0x67);
+
+	/* pll ctrl 0 */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x0200, pd->pll[0]);
+	wmb();
+}
+
+void cont_splash_clk_ctrl(int enable)
+{
+}
+
+void mipi_dsi_prepare_clocks(void)
+{
+	clk_prepare(dsi_ref_clk);
+	clk_prepare(ahb_m_clk);
+	clk_prepare(ahb_s_clk);
+	clk_prepare(ebi1_dsi_clk);
+	clk_prepare(mdp_dsi_pclk);
+	clk_prepare(dsi_byte_div_clk);
+	clk_prepare(dsi_esc_clk);
+	clk_prepare(dsi_clk);
+	clk_prepare(dsi_pixel_clk);
+}
+
+void mipi_dsi_unprepare_clocks(void)
+{
+	clk_unprepare(dsi_esc_clk);
+	clk_unprepare(dsi_byte_div_clk);
+	clk_unprepare(mdp_dsi_pclk);
+	clk_unprepare(ebi1_dsi_clk);
+	clk_unprepare(ahb_m_clk);
+	clk_unprepare(ahb_s_clk);
+	clk_unprepare(dsi_ref_clk);
+	clk_unprepare(dsi_clk);
+	clk_unprepare(dsi_pixel_clk);
+}
+
+void mipi_dsi_ahb_ctrl(u32 enable)
+{
+	static int ahb_ctrl_done;
+	if (enable) {
+		if (ahb_ctrl_done) {
+			pr_info("%s: ahb clks already ON\n", __func__);
+			return;
+		}
+		clk_enable(dsi_ref_clk);
+		clk_enable(ahb_m_clk);
+		clk_enable(ahb_s_clk);
+		ahb_ctrl_done = 1;
+	} else {
+		if (ahb_ctrl_done == 0) {
+			pr_info("%s: ahb clks already OFF\n", __func__);
+			return;
+		}
+		clk_disable(ahb_m_clk);
+		clk_disable(ahb_s_clk);
+		clk_disable(dsi_ref_clk);
+		ahb_ctrl_done = 0;
+	}
+}
+
+void mipi_dsi_clk_enable(void)
+{
+	unsigned data = 0;
+	uint32 pll_ctrl;
+
+	if (mipi_dsi_clk_on) {
+		pr_info("%s: mipi_dsi_clks already ON\n", __func__);
+		return;
+	}
+	if (clk_set_rate(ebi1_dsi_clk, 65000000)) /* 65 MHz */
+		pr_err("%s: ebi1_dsi_clk set rate failed\n", __func__);
+	clk_enable(ebi1_dsi_clk);
+
+	pll_ctrl = MIPI_INP(MIPI_DSI_BASE + 0x0200);
+	MIPI_OUTP(MIPI_DSI_BASE + 0x0200, pll_ctrl | 0x01);
+	mb();
+
+	clk_set_rate(dsi_byte_div_clk, data);
+	clk_set_rate(dsi_esc_clk, data);
+	clk_enable(mdp_dsi_pclk);
+	clk_enable(dsi_byte_div_clk);
+	clk_enable(dsi_esc_clk);
+	mipi_dsi_pclk_ctrl(&dsi_pclk, 1);
+	mipi_dsi_clk_ctrl(&dsicore_clk, 1);
+	mipi_dsi_clk_on = 1;
+}
+
+void mipi_dsi_clk_disable(void)
+{
+	if (mipi_dsi_clk_on == 0) {
+		pr_info("%s: mipi_dsi_clks already OFF\n", __func__);
+		return;
+	}
+	mipi_dsi_pclk_ctrl(&dsi_pclk, 0);
+	mipi_dsi_clk_ctrl(&dsicore_clk, 0);
+	clk_disable(dsi_esc_clk);
+	clk_disable(dsi_byte_div_clk);
+	clk_disable(mdp_dsi_pclk);
+	/* DSIPHY_PLL_CTRL_0, disable dsi pll */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x0200, 0x40);
+	if (clk_set_rate(ebi1_dsi_clk, 0))
+		pr_err("%s: ebi1_dsi_clk set rate failed\n", __func__);
+	clk_disable(ebi1_dsi_clk);
+	mipi_dsi_clk_on = 0;
+}
+
+void mipi_dsi_phy_ctrl(int on)
+{
+	if (on) {
+		/* DSIPHY_PLL_CTRL_5 */
+		MIPI_OUTP(MIPI_DSI_BASE + 0x0214, 0x050);
+
+		/* DSIPHY_TPA_CTRL_1 */
+		MIPI_OUTP(MIPI_DSI_BASE + 0x0258, 0x00f);
+
+		/* DSIPHY_TPA_CTRL_2 */
+		MIPI_OUTP(MIPI_DSI_BASE + 0x025c, 0x000);
+	} else {
+		/* DSIPHY_PLL_CTRL_5 */
+		MIPI_OUTP(MIPI_DSI_BASE + 0x0214, 0x05f);
+
+		/* DSIPHY_TPA_CTRL_1 */
+		MIPI_OUTP(MIPI_DSI_BASE + 0x0258, 0x08f);
+
+		/* DSIPHY_TPA_CTRL_2 */
+		MIPI_OUTP(MIPI_DSI_BASE + 0x025c, 0x001);
+
+		/* DSIPHY_REGULATOR_CTRL_0 */
+		MIPI_OUTP(MIPI_DSI_BASE + 0x02cc, 0x02);
+
+		/* DSIPHY_CTRL_0 */
+		MIPI_OUTP(MIPI_DSI_BASE + 0x0290, 0x00);
+
+		/* DSIPHY_CTRL_1 */
+		MIPI_OUTP(MIPI_DSI_BASE + 0x0294, 0x7f);
+
+		/* disable dsi clk */
+		MIPI_OUTP(MIPI_DSI_BASE + 0x0118, 0);
+	}
+}
+
+#ifdef CONFIG_FB_MSM_MDP303
+void update_lane_config(struct msm_panel_info *pinfo)
+{
+	struct mipi_dsi_phy_ctrl *pd;
+
+	pd = (pinfo->mipi).dsi_phy_db;
+	pinfo->mipi.data_lane1 = FALSE;
+	pd->pll[10] |= 0x08;
+
+	pinfo->yres = 320;
+	pinfo->lcdc.h_back_porch = 15;
+	pinfo->lcdc.h_front_porch = 21;
+	pinfo->lcdc.h_pulse_width = 5;
+	pinfo->lcdc.v_back_porch = 50;
+	pinfo->lcdc.v_front_porch = 101;
+	pinfo->lcdc.v_pulse_width = 50;
+}
+#endif
diff --git a/drivers/video/msm/msm_dss_io_8960.c b/drivers/video/msm/msm_dss_io_8960.c
new file mode 100644
index 0000000..b17c195
--- /dev/null
+++ b/drivers/video/msm/msm_dss_io_8960.c
@@ -0,0 +1,830 @@
+/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/clk.h>
+#include <mach/clk.h>
+#include "msm_fb.h"
+#include "mdp.h"
+#include "mdp4.h"
+#include "mipi_dsi.h"
+#include "hdmi_msm.h"
+#include <mach/msm_iomap.h>
+
+/* HDMI PHY macros */
+#define HDMI_PHY_REG_0                   (0x00000400)
+#define HDMI_PHY_REG_1                   (0x00000404)
+#define HDMI_PHY_REG_2                   (0x00000408)
+#define HDMI_PHY_REG_3                   (0x0000040c)
+#define HDMI_PHY_REG_4                   (0x00000410)
+#define HDMI_PHY_REG_5                   (0x00000414)
+#define HDMI_PHY_REG_6                   (0x00000418)
+#define HDMI_PHY_REG_7                   (0x0000041c)
+#define HDMI_PHY_REG_8                   (0x00000420)
+#define HDMI_PHY_REG_9                   (0x00000424)
+#define HDMI_PHY_REG_10                  (0x00000428)
+#define HDMI_PHY_REG_11                  (0x0000042c)
+#define HDMI_PHY_REG_12                  (0x00000430)
+#define HDMI_PHY_REG_BIST_CFG            (0x00000434)
+#define HDMI_PHY_DEBUG_BUS_SEL           (0x00000438)
+#define HDMI_PHY_REG_MISC0               (0x0000043c)
+#define HDMI_PHY_REG_13                  (0x00000440)
+#define HDMI_PHY_REG_14                  (0x00000444)
+#define HDMI_PHY_REG_15                  (0x00000448)
+#define HDMI_PHY_CTRL			         (0x000002D4)
+
+/* HDMI PHY/PLL bit field macros */
+#define HDMI_PHY_PLL_STATUS0             (0x00000598)
+#define SW_RESET BIT(2)
+#define SW_RESET_PLL BIT(0)
+#define PWRDN_B BIT(7)
+
+/* multimedia sub system clock control */
+char *mmss_cc_base = MSM_MMSS_CLK_CTL_BASE;
+/* multimedia sub system sfpb */
+char *mmss_sfpb_base;
+void  __iomem *periph_base;
+
+static struct dsi_clk_desc dsicore_clk;
+static struct dsi_clk_desc dsi_pclk;
+
+static struct clk *dsi_byte_div_clk;
+static struct clk *dsi_esc_clk;
+static struct clk *dsi_m_pclk;
+static struct clk *dsi_s_pclk;
+
+static struct clk *amp_pclk;
+int mipi_dsi_clk_on;
+
+int mipi_dsi_clk_init(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+	struct device *dev = &pdev->dev;
+
+	mfd = platform_get_drvdata(pdev);
+
+	amp_pclk = clk_get(dev, "arb_clk");
+	if (IS_ERR_OR_NULL(amp_pclk)) {
+		pr_err("can't find amp_pclk\n");
+		amp_pclk = NULL;
+		goto mipi_dsi_clk_err;
+	}
+
+	dsi_m_pclk = clk_get(dev, "master_iface_clk");
+	if (IS_ERR_OR_NULL(dsi_m_pclk)) {
+		pr_err("can't find dsi_m_pclk\n");
+		dsi_m_pclk = NULL;
+		goto mipi_dsi_clk_err;
+	}
+
+	dsi_s_pclk = clk_get(dev, "slave_iface_clk");
+	if (IS_ERR_OR_NULL(dsi_s_pclk)) {
+		pr_err("can't find dsi_s_pclk\n");
+		dsi_s_pclk = NULL;
+		goto mipi_dsi_clk_err;
+	}
+
+	dsi_byte_div_clk = clk_get(dev, "byte_clk");
+	if (IS_ERR(dsi_byte_div_clk)) {
+		pr_err("can't find dsi_byte_div_clk\n");
+		dsi_byte_div_clk = NULL;
+		goto mipi_dsi_clk_err;
+	}
+
+	dsi_esc_clk = clk_get(dev, "esc_clk");
+	if (IS_ERR(dsi_esc_clk)) {
+		printk(KERN_ERR "can't find dsi_esc_clk\n");
+		dsi_esc_clk = NULL;
+		goto mipi_dsi_clk_err;
+	}
+
+	return 0;
+
+mipi_dsi_clk_err:
+	mipi_dsi_clk_deinit(dev);
+	return -EPERM;
+}
+
+void mipi_dsi_clk_deinit(struct device *dev)
+{
+	if (amp_pclk)
+		clk_put(amp_pclk);
+	if (amp_pclk)
+		clk_put(dsi_m_pclk);
+	if (dsi_s_pclk)
+		clk_put(dsi_s_pclk);
+	if (dsi_byte_div_clk)
+		clk_put(dsi_byte_div_clk);
+	if (dsi_esc_clk)
+		clk_put(dsi_esc_clk);
+}
+
+static void mipi_dsi_clk_ctrl(struct dsi_clk_desc *clk, int clk_en)
+{
+	char	*cc, *ns, *md;
+	int	pmxo_sel = 0;
+	char	mnd_en = 1, root_en = 1;
+	uint32	data, val;
+
+	cc = mmss_cc_base + 0x004c;
+	md = mmss_cc_base + 0x0050;
+	ns = mmss_cc_base + 0x0054;
+
+	if (clk_en) {
+		if (clk->mnd_mode == 0) {
+			data  = clk->pre_div_func << 14;
+			data |= clk->src;
+			MIPI_OUTP_SECURE(ns, data);
+			MIPI_OUTP_SECURE(cc, ((pmxo_sel << 8)
+						| (clk->mnd_mode << 6)
+						| (root_en << 2) | clk_en));
+		} else {
+			val = clk->d * 2;
+			data = (~val) & 0x0ff;
+			data |= clk->m << 8;
+			MIPI_OUTP_SECURE(md, data);
+
+			val = clk->n - clk->m;
+			data = (~val) & 0x0ff;
+			data <<= 24;
+			data |= clk->src;
+			MIPI_OUTP_SECURE(ns, data);
+
+			MIPI_OUTP_SECURE(cc, ((pmxo_sel << 8)
+					      | (clk->mnd_mode << 6)
+					      | (mnd_en << 5)
+					      | (root_en << 2) | clk_en));
+		}
+	} else
+		MIPI_OUTP_SECURE(cc, 0);
+
+	wmb();
+}
+
+static void mipi_dsi_sfpb_cfg(void)
+{
+	char *sfpb;
+	int data;
+
+	sfpb = mmss_sfpb_base + 0x058;
+
+	data = MIPI_INP(sfpb);
+	data |= 0x01800;
+	MIPI_OUTP(sfpb, data);
+	wmb();
+}
+
+static void mipi_dsi_pclk_ctrl(struct dsi_clk_desc *clk, int clk_en)
+{
+	char	*cc, *ns, *md;
+	char	mnd_en = 1, root_en = 1;
+	uint32	data, val;
+
+	cc = mmss_cc_base + 0x0130;
+	md = mmss_cc_base + 0x0134;
+	ns = mmss_cc_base + 0x0138;
+
+	if (clk_en) {
+		if (clk->mnd_mode == 0) {
+			data  = clk->pre_div_func << 12;
+			data |= clk->src;
+			MIPI_OUTP_SECURE(ns, data);
+			MIPI_OUTP_SECURE(cc, ((clk->mnd_mode << 6)
+					      | (root_en << 2) | clk_en));
+		} else {
+			val = clk->d * 2;
+			data = (~val) & 0x0ff;
+			data |= clk->m << 8;
+			MIPI_OUTP_SECURE(md, data);
+
+			val = clk->n - clk->m;
+			data = (~val) & 0x0ff;
+			data <<= 24;
+			data |= clk->src;
+			MIPI_OUTP_SECURE(ns, data);
+
+			MIPI_OUTP_SECURE(cc, ((clk->mnd_mode << 6)
+					      | (mnd_en << 5)
+					      | (root_en << 2) | clk_en));
+		}
+	} else
+		MIPI_OUTP_SECURE(cc, 0);
+
+	wmb();
+}
+
+static void mipi_dsi_ahb_en(void)
+{
+	char	*ahb;
+
+	ahb = mmss_cc_base + 0x08;
+
+	pr_debug("%s: ahb=%x %x\n",
+		__func__, (int) ahb, MIPI_INP_SECURE(ahb));
+}
+
+void mipi_dsi_lane_cfg(void)
+{
+	int i, ln_offset;
+
+	ln_offset = 0x300;
+	for (i = 0; i < 4; i++) {
+		/* DSI1_DSIPHY_LN_CFG0 */
+		MIPI_OUTP(MIPI_DSI_BASE + ln_offset, 0x80);
+		/* DSI1_DSIPHY_LN_CFG1 */
+		MIPI_OUTP(MIPI_DSI_BASE + ln_offset + 0x04, 0x45);
+		/* DSI1_DSIPHY_LN_CFG2 */
+		MIPI_OUTP(MIPI_DSI_BASE + ln_offset + 0x08, 0x0);
+		/* DSI1_DSIPHY_LN_TEST_DATAPATH */
+		MIPI_OUTP(MIPI_DSI_BASE + ln_offset + 0x0c, 0x0);
+		/* DSI1_DSIPHY_LN_TEST_STR0 */
+		MIPI_OUTP(MIPI_DSI_BASE + ln_offset + 0x14, 0x1);
+		/* DSI1_DSIPHY_LN_TEST_STR1 */
+		MIPI_OUTP(MIPI_DSI_BASE + ln_offset + 0x18, 0x66);
+		ln_offset += 0x40;
+	}
+
+	MIPI_OUTP(MIPI_DSI_BASE + 0x0400, 0x40); /* DSI1_DSIPHY_LNCK_CFG0 */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x0404, 0x67); /* DSI1_DSIPHY_LNCK_CFG1 */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x0408, 0x0); /* DSI1_DSIPHY_LNCK_CFG2 */
+	/* DSI1_DSIPHY_LNCK_TEST_DATAPATH */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x040c, 0x0);
+	MIPI_OUTP(MIPI_DSI_BASE + 0x0414, 0x1); /* DSI1_DSIPHY_LNCK_TEST_STR0 */
+	/* DSI1_DSIPHY_LNCK_TEST_STR1 */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x0418, 0x88);
+}
+
+void mipi_dsi_bist_ctrl(void)
+{
+	MIPI_OUTP(MIPI_DSI_BASE + 0x049c, 0x0f); /* DSI1_DSIPHY_BIST_CTRL4 */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x0490, 0x03); /* DSI1_DSIPHY_BIST_CTRL1 */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x048c, 0x03); /* DSI1_DSIPHY_BIST_CTRL0 */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x049c, 0x0); /* DSI1_DSIPHY_BIST_CTRL4 */
+}
+
+static void mipi_dsi_calibration(void)
+{
+	int i = 0;
+	uint32 term_cnt = 5000;
+	int cal_busy = MIPI_INP(MIPI_DSI_BASE + 0x550);
+
+	/* DSI1_DSIPHY_REGULATOR_CAL_PWR_CFG */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x0518, 0x03);
+
+	/* DSI1_DSIPHY_CAL_SW_CFG2 */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x0534, 0x0);
+	/* DSI1_DSIPHY_CAL_HW_CFG1 */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x053c, 0x5a);
+	/* DSI1_DSIPHY_CAL_HW_CFG3 */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x0544, 0x10);
+	/* DSI1_DSIPHY_CAL_HW_CFG4 */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x0548, 0x01);
+	/* DSI1_DSIPHY_CAL_HW_CFG0 */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x0538, 0x01);
+
+	/* DSI1_DSIPHY_CAL_HW_TRIGGER */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x0528, 0x01);
+	usleep_range(5000, 5000);
+	/* DSI1_DSIPHY_CAL_HW_TRIGGER */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x0528, 0x00);
+
+	cal_busy = MIPI_INP(MIPI_DSI_BASE + 0x550);
+	while (cal_busy & 0x10) {
+		i++;
+		if (i > term_cnt) {
+			pr_err("DSI1 PHY REGULATOR NOT READY,"
+				"exceeded polling TIMEOUT!\n");
+			break;
+		}
+		cal_busy = MIPI_INP(MIPI_DSI_BASE + 0x550);
+	}
+}
+
+void mipi_dsi_phy_rdy_poll(void)
+{
+	uint32 phy_pll_busy;
+	uint32 i = 0;
+	uint32 term_cnt = 0xFFFFFF;
+
+	phy_pll_busy = MIPI_INP(MIPI_DSI_BASE + 0x280);
+	while (!(phy_pll_busy & 0x1)) {
+		i++;
+		if (i > term_cnt) {
+			pr_err("DSI1 PHY NOT READY, exceeded polling TIMEOUT!\n");
+			break;
+		}
+		phy_pll_busy = MIPI_INP(MIPI_DSI_BASE + 0x280);
+	}
+}
+
+#define PREF_DIV_RATIO 27
+struct dsiphy_pll_divider_config pll_divider_config;
+
+int mipi_dsi_phy_pll_config(u32 clk_rate)
+{
+	struct dsiphy_pll_divider_config *dividers;
+	u32 fb_divider, tmp;
+	dividers = &pll_divider_config;
+
+	/* DSIPHY_PLL_CTRL_x:    1     2     3     8     9     10 */
+	/* masks               0xff  0x07  0x3f  0x0f  0xff  0xff */
+
+	/* DSIPHY_PLL_CTRL_1 */
+	fb_divider = ((dividers->fb_divider) / 2) - 1;
+	MIPI_OUTP(MIPI_DSI_BASE + 0x204, fb_divider & 0xff);
+
+	/* DSIPHY_PLL_CTRL_2 */
+	tmp = MIPI_INP(MIPI_DSI_BASE + 0x208);
+	tmp &= ~0x07;
+	tmp |= (fb_divider >> 8) & 0x07;
+	MIPI_OUTP(MIPI_DSI_BASE + 0x208, tmp);
+
+	/* DSIPHY_PLL_CTRL_3 */
+	tmp = MIPI_INP(MIPI_DSI_BASE + 0x20c);
+	tmp &= ~0x3f;
+	tmp |= (dividers->ref_divider_ratio - 1) & 0x3f;
+	MIPI_OUTP(MIPI_DSI_BASE + 0x20c, tmp);
+
+	/* DSIPHY_PLL_CTRL_8 */
+	tmp = MIPI_INP(MIPI_DSI_BASE + 0x220);
+	tmp &= ~0x0f;
+	tmp |= (dividers->bit_clk_divider - 1) & 0x0f;
+	MIPI_OUTP(MIPI_DSI_BASE + 0x220, tmp);
+
+	/* DSIPHY_PLL_CTRL_9 */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x224, (dividers->byte_clk_divider - 1));
+
+	/* DSIPHY_PLL_CTRL_10 */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x228, (dividers->dsi_clk_divider - 1));
+
+	return 0;
+}
+
+int mipi_dsi_clk_div_config(uint8 bpp, uint8 lanes,
+			    uint32 *expected_dsi_pclk)
+{
+	u32 fb_divider, rate, vco;
+	u32 div_ratio = 0;
+	struct dsi_clk_mnd_table const *mnd_entry = mnd_table;
+	if (pll_divider_config.clk_rate == 0)
+		pll_divider_config.clk_rate = 454000000;
+
+	rate = pll_divider_config.clk_rate / 1000000; /* In Mhz */
+
+	if (rate < 125) {
+		vco = rate * 8;
+		div_ratio = 8;
+	} else if (rate < 250) {
+		vco = rate * 4;
+		div_ratio = 4;
+	} else if (rate < 600) {
+		vco = rate * 2;
+		div_ratio = 2;
+	} else {
+		vco = rate * 1;
+		div_ratio = 1;
+	}
+
+	/* find the mnd settings from mnd_table entry */
+	for (; mnd_entry != mnd_table + ARRAY_SIZE(mnd_table); ++mnd_entry) {
+		if (((mnd_entry->lanes) == lanes) &&
+			((mnd_entry->bpp) == bpp))
+			break;
+	}
+
+	if (mnd_entry == mnd_table + ARRAY_SIZE(mnd_table)) {
+		pr_err("%s: requested Lanes, %u & BPP, %u, not supported\n",
+			__func__, lanes, bpp);
+		return -EINVAL;
+	}
+	fb_divider = ((vco * PREF_DIV_RATIO) / 27);
+	pll_divider_config.fb_divider = fb_divider;
+	pll_divider_config.ref_divider_ratio = PREF_DIV_RATIO;
+	pll_divider_config.bit_clk_divider = div_ratio;
+	pll_divider_config.byte_clk_divider =
+			pll_divider_config.bit_clk_divider * 8;
+	pll_divider_config.dsi_clk_divider =
+			(mnd_entry->dsiclk_div) * div_ratio;
+
+	if (mnd_entry->dsiclk_d == 0) {
+		dsicore_clk.mnd_mode = 0;
+		dsicore_clk.src = 0x3;
+		dsicore_clk.pre_div_func = (mnd_entry->dsiclk_n - 1);
+	} else {
+		dsicore_clk.mnd_mode = 2;
+		dsicore_clk.src = 0x3;
+		dsicore_clk.m = mnd_entry->dsiclk_m;
+		dsicore_clk.n = mnd_entry->dsiclk_n;
+		dsicore_clk.d = mnd_entry->dsiclk_d;
+	}
+
+	if ((mnd_entry->pclk_d == 0)
+		|| (mnd_entry->pclk_m == 1)) {
+		dsi_pclk.mnd_mode = 0;
+		dsi_pclk.src = 0x3;
+		dsi_pclk.pre_div_func = (mnd_entry->pclk_n - 1);
+		*expected_dsi_pclk = ((vco * 1000000) /
+					((pll_divider_config.dsi_clk_divider)
+					* (mnd_entry->pclk_n)));
+	} else {
+		dsi_pclk.mnd_mode = 2;
+		dsi_pclk.src = 0x3;
+		dsi_pclk.m = mnd_entry->pclk_m;
+		dsi_pclk.n = mnd_entry->pclk_n;
+		dsi_pclk.d = mnd_entry->pclk_d;
+		*expected_dsi_pclk = ((vco * 1000000 * dsi_pclk.m) /
+					((pll_divider_config.dsi_clk_divider)
+					* (mnd_entry->pclk_n)));
+	}
+	return 0;
+}
+
+static void mipi_dsi_configure_serdes(void)
+{
+	void __iomem *cc;
+
+	/* PHY registers programemd thru S2P interface */
+	if (periph_base) {
+		MIPI_OUTP(periph_base + 0x2c, 0x000000b6);
+		MIPI_OUTP(periph_base + 0x2c, 0x000001b5);
+		MIPI_OUTP(periph_base + 0x2c, 0x000001b4);
+		MIPI_OUTP(periph_base + 0x2c, 0x000003b3);
+		MIPI_OUTP(periph_base + 0x2c, 0x000003a2);
+		MIPI_OUTP(periph_base + 0x2c, 0x000002a1);
+		MIPI_OUTP(periph_base + 0x2c, 0x000008a0);
+		MIPI_OUTP(periph_base + 0x2c, 0x00000d9f);
+		MIPI_OUTP(periph_base + 0x2c, 0x0000109e);
+		MIPI_OUTP(periph_base + 0x2c, 0x0000209d);
+		MIPI_OUTP(periph_base + 0x2c, 0x0000109c);
+		MIPI_OUTP(periph_base + 0x2c, 0x0000079a);
+		MIPI_OUTP(periph_base + 0x2c, 0x00000c99);
+		MIPI_OUTP(periph_base + 0x2c, 0x00002298);
+		MIPI_OUTP(periph_base + 0x2c, 0x000000a7);
+		MIPI_OUTP(periph_base + 0x2c, 0x000000a6);
+		MIPI_OUTP(periph_base + 0x2c, 0x000000a5);
+		MIPI_OUTP(periph_base + 0x2c, 0x00007fa4);
+		MIPI_OUTP(periph_base + 0x2c, 0x0000eea8);
+		MIPI_OUTP(periph_base + 0x2c, 0x000006aa);
+		MIPI_OUTP(periph_base + 0x2c, 0x00002095);
+		MIPI_OUTP(periph_base + 0x2c, 0x00000493);
+		MIPI_OUTP(periph_base + 0x2c, 0x00001092);
+		MIPI_OUTP(periph_base + 0x2c, 0x00000691);
+		MIPI_OUTP(periph_base + 0x2c, 0x00005490);
+		MIPI_OUTP(periph_base + 0x2c, 0x0000038d);
+		MIPI_OUTP(periph_base + 0x2c, 0x0000148c);
+		MIPI_OUTP(periph_base + 0x2c, 0x0000058b);
+		MIPI_OUTP(periph_base + 0x2c, 0x0000078a);
+		MIPI_OUTP(periph_base + 0x2c, 0x00001f89);
+		MIPI_OUTP(periph_base + 0x2c, 0x00003388);
+		MIPI_OUTP(periph_base + 0x2c, 0x00006387);
+		MIPI_OUTP(periph_base + 0x2c, 0x00004886);
+		MIPI_OUTP(periph_base + 0x2c, 0x00005085);
+		MIPI_OUTP(periph_base + 0x2c, 0x00000084);
+		MIPI_OUTP(periph_base + 0x2c, 0x0000da83);
+		MIPI_OUTP(periph_base + 0x2c, 0x0000b182);
+		MIPI_OUTP(periph_base + 0x2c, 0x00002f81);
+		MIPI_OUTP(periph_base + 0x2c, 0x00004080);
+		MIPI_OUTP(periph_base + 0x2c, 0x00004180);
+		MIPI_OUTP(periph_base + 0x2c, 0x000006aa);
+	}
+
+	cc = MIPI_DSI_BASE + 0x0130;
+	MIPI_OUTP(cc, 0x806c11c8);
+	MIPI_OUTP(cc, 0x804c11c8);
+	MIPI_OUTP(cc, 0x806d0080);
+	MIPI_OUTP(cc, 0x804d0080);
+	MIPI_OUTP(cc, 0x00000000);
+	MIPI_OUTP(cc, 0x807b1597);
+	MIPI_OUTP(cc, 0x805b1597);
+	MIPI_OUTP(cc, 0x807c0080);
+	MIPI_OUTP(cc, 0x805c0080);
+	MIPI_OUTP(cc, 0x00000000);
+	MIPI_OUTP(cc, 0x807911c8);
+	MIPI_OUTP(cc, 0x805911c8);
+	MIPI_OUTP(cc, 0x807a0080);
+	MIPI_OUTP(cc, 0x805a0080);
+	MIPI_OUTP(cc, 0x00000000);
+	MIPI_OUTP(cc, 0x80721555);
+	MIPI_OUTP(cc, 0x80521555);
+	MIPI_OUTP(cc, 0x80730000);
+	MIPI_OUTP(cc, 0x80530000);
+	MIPI_OUTP(cc, 0x00000000);
+}
+
+void mipi_dsi_phy_init(int panel_ndx, struct msm_panel_info const *panel_info,
+	int target_type)
+{
+	struct mipi_dsi_phy_ctrl *pd;
+	int i, off;
+
+	MIPI_OUTP(MIPI_DSI_BASE + 0x128, 0x0001);/* start phy sw reset */
+	wmb();
+	usleep(1);
+	MIPI_OUTP(MIPI_DSI_BASE + 0x128, 0x0000);/* end phy w reset */
+	wmb();
+	usleep(1);
+	MIPI_OUTP(MIPI_DSI_BASE + 0x500, 0x0003);/* regulator_ctrl_0 */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x504, 0x0001);/* regulator_ctrl_1 */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x508, 0x0001);/* regulator_ctrl_2 */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x50c, 0x0000);/* regulator_ctrl_3 */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x510, 0x0100);/* regulator_ctrl_4 */
+
+	MIPI_OUTP(MIPI_DSI_BASE + 0x4b0, 0x04);/* DSIPHY_LDO_CNTRL */
+
+	pd = (panel_info->mipi).dsi_phy_db;
+
+	off = 0x0480;	/* strength 0 - 2 */
+	for (i = 0; i < 3; i++) {
+		MIPI_OUTP(MIPI_DSI_BASE + off, pd->strength[i]);
+		wmb();
+		off += 4;
+	}
+
+	off = 0x0470;	/* ctrl 0 - 3 */
+	for (i = 0; i < 4; i++) {
+		MIPI_OUTP(MIPI_DSI_BASE + off, pd->ctrl[i]);
+		wmb();
+		off += 4;
+	}
+
+	off = 0x0500;	/* regulator ctrl 0 - 4 */
+	for (i = 0; i < 5; i++) {
+		MIPI_OUTP(MIPI_DSI_BASE + off, pd->regulator[i]);
+		wmb();
+		off += 4;
+	}
+	mipi_dsi_calibration();
+	mipi_dsi_lane_cfg(); /* lane cfgs */
+	mipi_dsi_bist_ctrl(); /* bist ctrl */
+
+	off = 0x0204;	/* pll ctrl 1 - 19, skip 0 */
+	for (i = 1; i < 20; i++) {
+		MIPI_OUTP(MIPI_DSI_BASE + off, pd->pll[i]);
+		wmb();
+		off += 4;
+	}
+
+	if (panel_info)
+		mipi_dsi_phy_pll_config(panel_info->clk_rate);
+
+	/* pll ctrl 0 */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x200, pd->pll[0]);
+	wmb();
+
+	off = 0x0440;	/* phy timing ctrl 0 - 11 */
+	for (i = 0; i < 12; i++) {
+		MIPI_OUTP(MIPI_DSI_BASE + off, pd->timing[i]);
+		wmb();
+		off += 4;
+	}
+
+	if (target_type == 1)
+		mipi_dsi_configure_serdes();
+}
+
+void cont_splash_clk_ctrl(int enable)
+{
+	static int cont_splash_clks_enabled;
+	if (enable && !cont_splash_clks_enabled) {
+			clk_prepare_enable(dsi_byte_div_clk);
+			clk_prepare_enable(dsi_esc_clk);
+			cont_splash_clks_enabled = 1;
+	} else if (!enable && cont_splash_clks_enabled) {
+			clk_disable_unprepare(dsi_byte_div_clk);
+			clk_disable_unprepare(dsi_esc_clk);
+			cont_splash_clks_enabled = 0;
+	}
+}
+
+void mipi_dsi_prepare_clocks(void)
+{
+	clk_prepare(amp_pclk);
+	clk_prepare(dsi_m_pclk);
+	clk_prepare(dsi_s_pclk);
+	clk_prepare(dsi_byte_div_clk);
+	clk_prepare(dsi_esc_clk);
+}
+
+void mipi_dsi_unprepare_clocks(void)
+{
+	clk_unprepare(dsi_esc_clk);
+	clk_unprepare(dsi_byte_div_clk);
+	clk_unprepare(dsi_m_pclk);
+	clk_unprepare(dsi_s_pclk);
+	clk_unprepare(amp_pclk);
+}
+
+void mipi_dsi_ahb_ctrl(u32 enable)
+{
+	static int ahb_ctrl_done;
+	if (enable) {
+		if (ahb_ctrl_done) {
+			pr_info("%s: ahb clks already ON\n", __func__);
+			return;
+		}
+		clk_enable(amp_pclk); /* clock for AHB-master to AXI */
+		clk_enable(dsi_m_pclk);
+		clk_enable(dsi_s_pclk);
+		mipi_dsi_ahb_en();
+		mipi_dsi_sfpb_cfg();
+		ahb_ctrl_done = 1;
+	} else {
+		if (ahb_ctrl_done == 0) {
+			pr_info("%s: ahb clks already OFF\n", __func__);
+			return;
+		}
+		clk_disable(dsi_m_pclk);
+		clk_disable(dsi_s_pclk);
+		clk_disable(amp_pclk); /* clock for AHB-master to AXI */
+		ahb_ctrl_done = 0;
+	}
+}
+
+void mipi_dsi_clk_enable(void)
+{
+	u32 pll_ctrl = MIPI_INP(MIPI_DSI_BASE + 0x0200);
+	if (mipi_dsi_clk_on) {
+		pr_info("%s: mipi_dsi_clks already ON\n", __func__);
+		return;
+	}
+	MIPI_OUTP(MIPI_DSI_BASE + 0x0200, pll_ctrl | 0x01);
+	mipi_dsi_phy_rdy_poll();
+
+	if (clk_set_rate(dsi_byte_div_clk, 1) < 0)	/* divided by 1 */
+		pr_err("%s: dsi_byte_div_clk - "
+			"clk_set_rate failed\n", __func__);
+	if (clk_set_rate(dsi_esc_clk, 2) < 0) /* divided by 2 */
+		pr_err("%s: dsi_esc_clk - "
+			"clk_set_rate failed\n", __func__);
+	mipi_dsi_pclk_ctrl(&dsi_pclk, 1);
+	mipi_dsi_clk_ctrl(&dsicore_clk, 1);
+	clk_enable(dsi_byte_div_clk);
+	clk_enable(dsi_esc_clk);
+	mipi_dsi_clk_on = 1;
+	mdp4_stat.dsi_clk_on++;
+}
+
+void mipi_dsi_clk_disable(void)
+{
+	if (mipi_dsi_clk_on == 0) {
+		pr_info("%s: mipi_dsi_clks already OFF\n", __func__);
+		return;
+	}
+	clk_disable(dsi_esc_clk);
+	clk_disable(dsi_byte_div_clk);
+	mipi_dsi_pclk_ctrl(&dsi_pclk, 0);
+	mipi_dsi_clk_ctrl(&dsicore_clk, 0);
+	/* DSIPHY_PLL_CTRL_0, disable dsi pll */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x0200, 0x0);
+	mipi_dsi_clk_on = 0;
+	mdp4_stat.dsi_clk_off++;
+}
+
+void mipi_dsi_phy_ctrl(int on)
+{
+	if (on) {
+		/* DSIPHY_PLL_CTRL_5 */
+		MIPI_OUTP(MIPI_DSI_BASE + 0x0214, 0x050);
+	} else {
+		/* DSIPHY_PLL_CTRL_5 */
+		MIPI_OUTP(MIPI_DSI_BASE + 0x0214, 0x05f);
+
+		/* DSIPHY_REGULATOR_CTRL_0 */
+		MIPI_OUTP(MIPI_DSI_BASE + 0x0500, 0x02);
+
+		/* DSIPHY_CTRL_0 */
+		MIPI_OUTP(MIPI_DSI_BASE + 0x0470, 0x00);
+
+		/* DSIPHY_CTRL_1 */
+		MIPI_OUTP(MIPI_DSI_BASE + 0x0474, 0x7f);
+
+		/* disable dsi clk */
+		MIPI_OUTP(MIPI_DSI_BASE + 0x0118, 0);
+	}
+}
+
+#ifdef CONFIG_FB_MSM_HDMI_COMMON
+void hdmi_phy_reset(void)
+{
+	unsigned int phy_reset_polarity = 0x0;
+	unsigned int pll_reset_polarity = 0x0;
+
+	unsigned int val = HDMI_INP_ND(HDMI_PHY_CTRL);
+
+	phy_reset_polarity = val >> 3 & 0x1;
+	pll_reset_polarity = val >> 1 & 0x1;
+
+	if (phy_reset_polarity == 0)
+		HDMI_OUTP(HDMI_PHY_CTRL, val | SW_RESET);
+	else
+		HDMI_OUTP(HDMI_PHY_CTRL, val & (~SW_RESET));
+
+	if (pll_reset_polarity == 0)
+		HDMI_OUTP(HDMI_PHY_CTRL, val | SW_RESET_PLL);
+	else
+		HDMI_OUTP(HDMI_PHY_CTRL, val & (~SW_RESET_PLL));
+
+	msleep(100);
+
+	if (phy_reset_polarity == 0)
+		HDMI_OUTP(HDMI_PHY_CTRL, val & (~SW_RESET));
+	else
+		HDMI_OUTP(HDMI_PHY_CTRL, val | SW_RESET);
+
+	if (pll_reset_polarity == 0)
+		HDMI_OUTP(HDMI_PHY_CTRL, val & (~SW_RESET_PLL));
+	else
+		HDMI_OUTP(HDMI_PHY_CTRL, val | SW_RESET_PLL);
+}
+
+void hdmi_msm_reset_core(void)
+{
+	hdmi_msm_set_mode(FALSE);
+	hdmi_msm_clk(0);
+	udelay(5);
+	hdmi_msm_clk(1);
+
+	clk_reset(hdmi_msm_state->hdmi_app_clk, CLK_RESET_ASSERT);
+	udelay(20);
+	clk_reset(hdmi_msm_state->hdmi_app_clk, CLK_RESET_DEASSERT);
+}
+
+void hdmi_msm_init_phy(int video_format)
+{
+	uint32 offset;
+	pr_err("Video format is : %u\n", video_format);
+
+	HDMI_OUTP(HDMI_PHY_REG_0, 0x1B);
+	HDMI_OUTP(HDMI_PHY_REG_1, 0xf2);
+
+	offset = HDMI_PHY_REG_4;
+	while (offset <= HDMI_PHY_REG_11) {
+		HDMI_OUTP(offset, 0x0);
+		offset += 0x4;
+	}
+
+	HDMI_OUTP(HDMI_PHY_REG_3, 0x20);
+}
+
+void hdmi_msm_powerdown_phy(void)
+{
+	/* Power down PHY */
+	HDMI_OUTP_ND(HDMI_PHY_REG_2, 0x7F); /*0b01111111*/
+}
+
+void hdmi_frame_ctrl_cfg(const struct hdmi_disp_mode_timing_type *timing)
+{
+	/*  0x02C8 HDMI_FRAME_CTRL
+	 *  31 INTERLACED_EN   Interlaced or progressive enable bit
+	 *    0: Frame in progressive
+	 *    1: Frame is interlaced
+	 *  29 HSYNC_HDMI_POL  HSYNC polarity fed to HDMI core
+	 *     0: Active Hi Hsync, detect the rising edge of hsync
+	 *     1: Active lo Hsync, Detect the falling edge of Hsync
+	 *  28 VSYNC_HDMI_POL  VSYNC polarity fed to HDMI core
+	 *     0: Active Hi Vsync, detect the rising edge of vsync
+	 *     1: Active Lo Vsync, Detect the falling edge of Vsync
+	 *  12 RGB_MUX_SEL     ALPHA mdp4 input is RGB, mdp4 input is BGR
+	 */
+	HDMI_OUTP(0x02C8,
+		  ((timing->interlaced << 31) & 0x80000000)
+		| ((timing->active_low_h << 29) & 0x20000000)
+		| ((timing->active_low_v << 28) & 0x10000000));
+}
+
+void hdmi_msm_phy_status_poll(void)
+{
+	unsigned int lock_det, phy_ready;
+	lock_det = 0x1 & HDMI_INP_ND(HDMI_PHY_PLL_STATUS0);
+	if (lock_det) {
+		pr_debug("HDMI Phy PLL Lock Detect Bit is set\n");
+	} else {
+		pr_debug("HDMI Phy Lock Detect Bit is not set,"
+			 "waiting for lock detection\n");
+		do {
+			lock_det = 0x1 & \
+				HDMI_INP_ND(HDMI_PHY_PLL_STATUS0);
+		} while (!lock_det);
+	}
+
+	phy_ready = 0x1 & HDMI_INP_ND(HDMI_PHY_REG_15);
+	if (phy_ready) {
+		pr_debug("HDMI Phy Status bit is set and ready\n");
+	} else {
+		pr_debug("HDMI Phy Status bit is not set,"
+			"waiting for ready status\n");
+		do {
+			phy_ready = 0x1 & HDMI_INP_ND(HDMI_PHY_REG_15);
+		} while (!phy_ready);
+	}
+}
+
+#endif
diff --git a/drivers/video/msm/msm_dss_io_8x60.c b/drivers/video/msm/msm_dss_io_8x60.c
new file mode 100644
index 0000000..a1897e3
--- /dev/null
+++ b/drivers/video/msm/msm_dss_io_8x60.c
@@ -0,0 +1,690 @@
+/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/clk.h>
+#include <mach/clk.h>
+#include "msm_fb.h"
+#include "mipi_dsi.h"
+#include "hdmi_msm.h"
+#include <mach/msm_iomap.h>
+
+/* multimedia sub system clock control */
+char *mmss_cc_base = MSM_MMSS_CLK_CTL_BASE;
+/* multimedia sub system sfpb */
+char *mmss_sfpb_base;
+void  __iomem *periph_base;
+
+static struct dsi_clk_desc dsicore_clk;
+static struct dsi_clk_desc dsi_pclk;
+
+static struct clk *dsi_byte_div_clk;
+static struct clk *dsi_esc_clk;
+static struct clk *dsi_m_pclk;
+static struct clk *dsi_s_pclk;
+
+static struct clk *amp_pclk;
+int mipi_dsi_clk_on;
+
+int mipi_dsi_clk_init(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	amp_pclk = clk_get(dev, "arb_clk");
+	if (IS_ERR_OR_NULL(amp_pclk)) {
+		pr_err("can't find amp_pclk\n");
+		amp_pclk = NULL;
+		goto mipi_dsi_clk_err;
+	}
+
+	dsi_m_pclk = clk_get(dev, "master_iface_clk");
+	if (IS_ERR_OR_NULL(dsi_m_pclk)) {
+		pr_err("can't find dsi_m_pclk\n");
+		dsi_m_pclk = NULL;
+		goto mipi_dsi_clk_err;
+	}
+
+	dsi_s_pclk = clk_get(dev, "slave_iface_clk");
+	if (IS_ERR_OR_NULL(dsi_s_pclk)) {
+		pr_err("can't find dsi_s_pclk\n");
+		dsi_s_pclk = NULL;
+		goto mipi_dsi_clk_err;
+	}
+
+	dsi_byte_div_clk = clk_get(dev, "byte_clk");
+	if (IS_ERR_OR_NULL(dsi_byte_div_clk)) {
+		pr_err("can't find dsi_byte_div_clk\n");
+		dsi_byte_div_clk = NULL;
+		goto mipi_dsi_clk_err;
+	}
+
+	dsi_esc_clk = clk_get(dev, "esc_clk");
+	if (IS_ERR_OR_NULL(dsi_esc_clk)) {
+		printk(KERN_ERR "can't find dsi_esc_clk\n");
+		dsi_esc_clk = NULL;
+		goto mipi_dsi_clk_err;
+	}
+
+	return 0;
+
+mipi_dsi_clk_err:
+	mipi_dsi_clk_deinit(NULL);
+	return -EPERM;
+}
+
+void mipi_dsi_clk_deinit(struct device *dev)
+{
+	if (amp_pclk)
+		clk_put(amp_pclk);
+	if (dsi_m_pclk)
+		clk_put(dsi_m_pclk);
+	if (dsi_s_pclk)
+		clk_put(dsi_s_pclk);
+	if (dsi_byte_div_clk)
+		clk_put(dsi_byte_div_clk);
+	if (dsi_esc_clk)
+		clk_put(dsi_esc_clk);
+}
+
+static void mipi_dsi_clk_ctrl(struct dsi_clk_desc *clk, int clk_en)
+{
+	char	*cc, *ns, *md;
+	int	pmxo_sel = 0;
+	char	mnd_en = 1, root_en = 1;
+	uint32	data, val;
+
+	cc = mmss_cc_base + 0x004c;
+	md = mmss_cc_base + 0x0050;
+	ns = mmss_cc_base + 0x0054;
+
+	if (clk_en) {
+		if (clk->mnd_mode == 0) {
+			data  = clk->pre_div_func << 14;
+			data |= clk->src;
+			MIPI_OUTP_SECURE(ns, data);
+			MIPI_OUTP_SECURE(cc, ((pmxo_sel << 8)
+						| (clk->mnd_mode << 6)
+						| (root_en << 2) | clk_en));
+		} else {
+			val = clk->d * 2;
+			data = (~val) & 0x0ff;
+			data |= clk->m << 8;
+			MIPI_OUTP_SECURE(md, data);
+
+			val = clk->n - clk->m;
+			data = (~val) & 0x0ff;
+			data <<= 24;
+			data |= clk->src;
+			MIPI_OUTP_SECURE(ns, data);
+
+			MIPI_OUTP_SECURE(cc, ((pmxo_sel << 8)
+					      | (clk->mnd_mode << 6)
+					      | (mnd_en << 5)
+					      | (root_en << 2) | clk_en));
+		}
+
+	} else
+		MIPI_OUTP_SECURE(cc, 0);
+
+	wmb();
+}
+
+static void mipi_dsi_sfpb_cfg(void)
+{
+	char *sfpb;
+	int data;
+
+	sfpb = mmss_sfpb_base + 0x058;
+
+	data = MIPI_INP(sfpb);
+	data |= 0x01800;
+	MIPI_OUTP(sfpb, data);
+	wmb();
+}
+
+static void mipi_dsi_pclk_ctrl(struct dsi_clk_desc *clk, int clk_en)
+{
+	char	*cc, *ns, *md;
+	char	mnd_en = 1, root_en = 1;
+	uint32	data, val;
+
+	cc = mmss_cc_base + 0x0130;
+	md = mmss_cc_base + 0x0134;
+	ns = mmss_cc_base + 0x0138;
+
+	if (clk_en) {
+		if (clk->mnd_mode == 0) {
+			data  = clk->pre_div_func << 12;
+			data |= clk->src;
+			MIPI_OUTP_SECURE(ns, data);
+			MIPI_OUTP_SECURE(cc, ((clk->mnd_mode << 6)
+					      | (root_en << 2) | clk_en));
+		} else {
+			val = clk->d * 2;
+			data = (~val) & 0x0ff;
+			data |= clk->m << 8;
+			MIPI_OUTP_SECURE(md, data);
+
+			val = clk->n - clk->m;
+			data = (~val) & 0x0ff;
+			data <<= 24;
+			data |= clk->src;
+			MIPI_OUTP_SECURE(ns, data);
+
+			MIPI_OUTP_SECURE(cc, ((clk->mnd_mode << 6)
+					      | (mnd_en << 5)
+					      | (root_en << 2) | clk_en));
+		}
+
+	} else
+		MIPI_OUTP_SECURE(cc, 0);
+
+	wmb();
+}
+
+static void mipi_dsi_ahb_en(void)
+{
+	char	*ahb;
+
+	ahb = mmss_cc_base + 0x08;
+
+	pr_debug("%s: ahb=%x %x\n",
+		__func__, (int) ahb, MIPI_INP_SECURE(ahb));
+}
+
+static void mipi_dsi_calibration(void)
+{
+	uint32 data;
+
+	MIPI_OUTP(MIPI_DSI_BASE + 0xf4, 0x0000ff11); /* cal_ctrl */
+	MIPI_OUTP(MIPI_DSI_BASE + 0xf0, 0x01); /* cal_hw_trigger */
+
+	while (1) {
+		data = MIPI_INP(MIPI_DSI_BASE + 0xfc); /* cal_status */
+		if ((data & 0x10000000) == 0)
+			break;
+
+		udelay(10);
+	}
+}
+
+#define PREF_DIV_RATIO 27
+struct dsiphy_pll_divider_config pll_divider_config;
+
+
+int mipi_dsi_phy_pll_config(u32 clk_rate)
+{
+	struct dsiphy_pll_divider_config *dividers;
+	u32 fb_divider, tmp;
+	dividers = &pll_divider_config;
+
+	/* DSIPHY_PLL_CTRL_x:    1     2     3     8     9     10 */
+	/* masks               0xff  0x07  0x3f  0x0f  0xff  0xff */
+
+	/* DSIPHY_PLL_CTRL_1 */
+	fb_divider = ((dividers->fb_divider) / 2) - 1;
+	MIPI_OUTP(MIPI_DSI_BASE + 0x204, fb_divider & 0xff);
+
+	/* DSIPHY_PLL_CTRL_2 */
+	tmp = MIPI_INP(MIPI_DSI_BASE + 0x208);
+	tmp &= ~0x07;
+	tmp |= (fb_divider >> 8) & 0x07;
+	MIPI_OUTP(MIPI_DSI_BASE + 0x208, tmp);
+
+	/* DSIPHY_PLL_CTRL_3 */
+	tmp = MIPI_INP(MIPI_DSI_BASE + 0x20c);
+	tmp &= ~0x3f;
+	tmp |= (dividers->ref_divider_ratio - 1) & 0x3f;
+	MIPI_OUTP(MIPI_DSI_BASE + 0x20c, tmp);
+
+	/* DSIPHY_PLL_CTRL_8 */
+	tmp = MIPI_INP(MIPI_DSI_BASE + 0x220);
+	tmp &= ~0x0f;
+	tmp |= (dividers->bit_clk_divider - 1) & 0x0f;
+	MIPI_OUTP(MIPI_DSI_BASE + 0x220, tmp);
+
+	/* DSIPHY_PLL_CTRL_9 */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x224, (dividers->byte_clk_divider - 1));
+
+	/* DSIPHY_PLL_CTRL_10 */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x228, (dividers->dsi_clk_divider - 1));
+
+	return 0;
+}
+
+int mipi_dsi_clk_div_config(uint8 bpp, uint8 lanes,
+			    uint32 *expected_dsi_pclk)
+{
+	u32 fb_divider, rate, vco;
+	u32 div_ratio = 0;
+	struct dsi_clk_mnd_table const *mnd_entry = mnd_table;
+	if (pll_divider_config.clk_rate == 0)
+		pll_divider_config.clk_rate = 454000000;
+
+	rate = pll_divider_config.clk_rate / 1000000; /* In Mhz */
+
+	if (rate < 125) {
+		vco = rate * 8;
+		div_ratio = 8;
+	} else if (rate < 250) {
+		vco = rate * 4;
+		div_ratio = 4;
+	} else if (rate < 500) {
+		vco = rate * 2;
+		div_ratio = 2;
+	} else {
+		vco = rate * 1;
+		div_ratio = 1;
+	}
+
+	/* find the mnd settings from mnd_table entry */
+	for (; mnd_entry != mnd_table + ARRAY_SIZE(mnd_table); ++mnd_entry) {
+		if (((mnd_entry->lanes) == lanes) &&
+			((mnd_entry->bpp) == bpp))
+			break;
+	}
+
+	if (mnd_entry == mnd_table + ARRAY_SIZE(mnd_table)) {
+		pr_err("%s: requested Lanes, %u & BPP, %u, not supported\n",
+			__func__, lanes, bpp);
+		return -EINVAL;
+	}
+	fb_divider = ((vco * PREF_DIV_RATIO) / 27);
+	pll_divider_config.fb_divider = fb_divider;
+	pll_divider_config.ref_divider_ratio = PREF_DIV_RATIO;
+	pll_divider_config.bit_clk_divider = div_ratio;
+	pll_divider_config.byte_clk_divider =
+			pll_divider_config.bit_clk_divider * 8;
+	pll_divider_config.dsi_clk_divider =
+			(mnd_entry->dsiclk_div) * div_ratio;
+
+	if ((mnd_entry->dsiclk_d == 0)
+		|| (mnd_entry->dsiclk_m == 1)) {
+		dsicore_clk.mnd_mode = 0;
+		dsicore_clk.src = 0x3;
+		dsicore_clk.pre_div_func = (mnd_entry->dsiclk_n - 1);
+	} else {
+		dsicore_clk.mnd_mode = 2;
+		dsicore_clk.src = 0x3;
+		dsicore_clk.m = mnd_entry->dsiclk_m;
+		dsicore_clk.n = mnd_entry->dsiclk_n;
+		dsicore_clk.d = mnd_entry->dsiclk_d;
+	}
+
+	if ((mnd_entry->pclk_d == 0)
+		|| (mnd_entry->pclk_m == 1)) {
+		dsi_pclk.mnd_mode = 0;
+		dsi_pclk.src = 0x3;
+		dsi_pclk.pre_div_func = (mnd_entry->pclk_n - 1);
+		*expected_dsi_pclk = ((vco * 1000000) /
+					((pll_divider_config.dsi_clk_divider)
+					* (mnd_entry->pclk_n)));
+	} else {
+		dsi_pclk.mnd_mode = 2;
+		dsi_pclk.src = 0x3;
+		dsi_pclk.m = mnd_entry->pclk_m;
+		dsi_pclk.n = mnd_entry->pclk_n;
+		dsi_pclk.d = mnd_entry->pclk_d;
+		*expected_dsi_pclk = ((vco * 1000000 * dsi_pclk.m) /
+					((pll_divider_config.dsi_clk_divider)
+					* (mnd_entry->pclk_n)));
+	}
+	return 0;
+}
+
+void mipi_dsi_phy_init(int panel_ndx, struct msm_panel_info const *panel_info,
+	int target_type)
+{
+	struct mipi_dsi_phy_ctrl *pd;
+	int i, off;
+
+	MIPI_OUTP(MIPI_DSI_BASE + 0x128, 0x0001);/* start phy sw reset */
+	wmb();
+	usleep(1);
+	MIPI_OUTP(MIPI_DSI_BASE + 0x128, 0x0000);/* end phy w reset */
+	wmb();
+	usleep(1);
+	MIPI_OUTP(MIPI_DSI_BASE + 0x2cc, 0x0003);/* regulator_ctrl_0 */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x2d0, 0x0001);/* regulator_ctrl_1 */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x2d4, 0x0001);/* regulator_ctrl_2 */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x2d8, 0x0000);/* regulator_ctrl_3 */
+#ifdef DSI_POWER
+	MIPI_OUTP(MIPI_DSI_BASE + 0x2dc, 0x0100);/* regulator_ctrl_4 */
+#endif
+
+	pd = (panel_info->mipi).dsi_phy_db;
+
+	off = 0x02cc;	/* regulator ctrl 0 */
+	for (i = 0; i < 4; i++) {
+		MIPI_OUTP(MIPI_DSI_BASE + off, pd->regulator[i]);
+		wmb();
+		off += 4;
+	}
+
+	off = 0x0260;	/* phy timig ctrl 0 */
+	for (i = 0; i < 11; i++) {
+		MIPI_OUTP(MIPI_DSI_BASE + off, pd->timing[i]);
+		wmb();
+		off += 4;
+	}
+
+	off = 0x0290;	/* ctrl 0 */
+	for (i = 0; i < 4; i++) {
+		MIPI_OUTP(MIPI_DSI_BASE + off, pd->ctrl[i]);
+		wmb();
+		off += 4;
+	}
+
+	off = 0x02a0;	/* strength 0 */
+	for (i = 0; i < 4; i++) {
+		MIPI_OUTP(MIPI_DSI_BASE + off, pd->strength[i]);
+		wmb();
+		off += 4;
+	}
+
+	mipi_dsi_calibration();
+
+	off = 0x0204;	/* pll ctrl 1, skip 0 */
+	for (i = 1; i < 21; i++) {
+		MIPI_OUTP(MIPI_DSI_BASE + off, pd->pll[i]);
+		wmb();
+		off += 4;
+	}
+
+	if (panel_info)
+		mipi_dsi_phy_pll_config(panel_info->clk_rate);
+
+	/* pll ctrl 0 */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x200, pd->pll[0]);
+	wmb();
+}
+
+void cont_splash_clk_ctrl(int enable)
+{
+}
+
+void mipi_dsi_prepare_clocks(void)
+{
+	clk_prepare(amp_pclk);
+	clk_prepare(dsi_m_pclk);
+	clk_prepare(dsi_s_pclk);
+	clk_prepare(dsi_byte_div_clk);
+	clk_prepare(dsi_esc_clk);
+}
+
+void mipi_dsi_unprepare_clocks(void)
+{
+	clk_unprepare(dsi_esc_clk);
+	clk_unprepare(dsi_byte_div_clk);
+	clk_unprepare(dsi_m_pclk);
+	clk_unprepare(dsi_s_pclk);
+	clk_unprepare(amp_pclk);
+}
+
+void mipi_dsi_ahb_ctrl(u32 enable)
+{
+	static int ahb_ctrl_done;
+	if (enable) {
+		if (ahb_ctrl_done) {
+			pr_info("%s: ahb clks already ON\n", __func__);
+			return;
+		}
+		clk_enable(amp_pclk); /* clock for AHB-master to AXI */
+		clk_enable(dsi_m_pclk);
+		clk_enable(dsi_s_pclk);
+		mipi_dsi_ahb_en();
+		mipi_dsi_sfpb_cfg();
+		ahb_ctrl_done = 1;
+	} else {
+		if (ahb_ctrl_done == 0) {
+			pr_info("%s: ahb clks already OFF\n", __func__);
+			return;
+		}
+		clk_disable(dsi_m_pclk);
+		clk_disable(dsi_s_pclk);
+		clk_disable(amp_pclk); /* clock for AHB-master to AXI */
+		ahb_ctrl_done = 0;
+	}
+}
+
+void mipi_dsi_clk_enable(void)
+{
+	u32 pll_ctrl = MIPI_INP(MIPI_DSI_BASE + 0x0200);
+	if (mipi_dsi_clk_on) {
+		pr_info("%s: mipi_dsi_clks already ON\n", __func__);
+		return;
+	}
+	MIPI_OUTP(MIPI_DSI_BASE + 0x0200, pll_ctrl | 0x01);
+	mb();
+
+	if (clk_set_rate(dsi_byte_div_clk, 1) < 0)	/* divided by 1 */
+		pr_err("%s: clk_set_rate failed\n",	__func__);
+	mipi_dsi_pclk_ctrl(&dsi_pclk, 1);
+	mipi_dsi_clk_ctrl(&dsicore_clk, 1);
+	clk_enable(dsi_byte_div_clk);
+	clk_enable(dsi_esc_clk);
+	mipi_dsi_clk_on = 1;
+}
+
+void mipi_dsi_clk_disable(void)
+{
+	if (mipi_dsi_clk_on == 0) {
+		pr_info("%s: mipi_dsi_clks already OFF\n", __func__);
+		return;
+	}
+	clk_disable(dsi_esc_clk);
+	clk_disable(dsi_byte_div_clk);
+
+	mipi_dsi_pclk_ctrl(&dsi_pclk, 0);
+	mipi_dsi_clk_ctrl(&dsicore_clk, 0);
+	/* DSIPHY_PLL_CTRL_0, disable dsi pll */
+	MIPI_OUTP(MIPI_DSI_BASE + 0x0200, 0x40);
+	mipi_dsi_clk_on = 0;
+}
+
+void mipi_dsi_phy_ctrl(int on)
+{
+	if (on) {
+		/* DSIPHY_PLL_CTRL_5 */
+		MIPI_OUTP(MIPI_DSI_BASE + 0x0214, 0x050);
+
+		/* DSIPHY_TPA_CTRL_1 */
+		MIPI_OUTP(MIPI_DSI_BASE + 0x0258, 0x00f);
+
+		/* DSIPHY_TPA_CTRL_2 */
+		MIPI_OUTP(MIPI_DSI_BASE + 0x025c, 0x000);
+	} else {
+		/* DSIPHY_PLL_CTRL_5 */
+		MIPI_OUTP(MIPI_DSI_BASE + 0x0214, 0x05f);
+
+		/* DSIPHY_TPA_CTRL_1 */
+		MIPI_OUTP(MIPI_DSI_BASE + 0x0258, 0x08f);
+
+		/* DSIPHY_TPA_CTRL_2 */
+		MIPI_OUTP(MIPI_DSI_BASE + 0x025c, 0x001);
+
+		/* DSIPHY_REGULATOR_CTRL_0 */
+		MIPI_OUTP(MIPI_DSI_BASE + 0x02cc, 0x02);
+
+		/* DSIPHY_CTRL_0 */
+		MIPI_OUTP(MIPI_DSI_BASE + 0x0290, 0x00);
+
+		/* DSIPHY_CTRL_1 */
+		MIPI_OUTP(MIPI_DSI_BASE + 0x0294, 0x7f);
+
+		/* disable dsi clk */
+		MIPI_OUTP(MIPI_DSI_BASE + 0x0118, 0);
+	}
+}
+
+#ifdef CONFIG_FB_MSM_HDMI_COMMON
+#define SW_RESET BIT(2)
+void hdmi_phy_reset(void)
+{
+	unsigned int phy_reset_polarity = 0x0;
+	unsigned int val = HDMI_INP_ND(0x2D4);
+
+	phy_reset_polarity = val >> 3 & 0x1;
+
+	if (phy_reset_polarity == 0)
+		HDMI_OUTP(0x2D4, val | SW_RESET);
+	else
+		HDMI_OUTP(0x2D4, val & (~SW_RESET));
+
+	msleep(100);
+
+	if (phy_reset_polarity == 0)
+		HDMI_OUTP(0x2D4, val & (~SW_RESET));
+	else
+		HDMI_OUTP(0x2D4, val | SW_RESET);
+}
+
+void hdmi_msm_reset_core(void)
+{
+	hdmi_msm_set_mode(FALSE);
+	hdmi_msm_clk(0);
+	udelay(5);
+	hdmi_msm_clk(1);
+
+	clk_reset(hdmi_msm_state->hdmi_app_clk, CLK_RESET_ASSERT);
+	clk_reset(hdmi_msm_state->hdmi_m_pclk, CLK_RESET_ASSERT);
+	clk_reset(hdmi_msm_state->hdmi_s_pclk, CLK_RESET_ASSERT);
+	udelay(20);
+	clk_reset(hdmi_msm_state->hdmi_app_clk, CLK_RESET_DEASSERT);
+	clk_reset(hdmi_msm_state->hdmi_m_pclk, CLK_RESET_DEASSERT);
+	clk_reset(hdmi_msm_state->hdmi_s_pclk, CLK_RESET_DEASSERT);
+}
+
+void hdmi_msm_init_phy(int video_format)
+{
+	uint32 offset;
+	/* De-serializer delay D/C for non-lbk mode
+	 * PHY REG0 = (DESER_SEL(0) | DESER_DEL_CTRL(3)
+	 * | AMUX_OUT_SEL(0))
+	 */
+	HDMI_OUTP_ND(0x0300, 0x0C); /*0b00001100*/
+
+	if (video_format == HDMI_VFRMT_720x480p60_16_9) {
+		/* PHY REG1 = DTEST_MUX_SEL(5) | PLL_GAIN_SEL(0)
+		 * | OUTVOL_SWING_CTRL(3)
+		 */
+		HDMI_OUTP_ND(0x0304, 0x53); /*0b01010011*/
+	} else {
+		/* If the freq. is less than 120MHz, use low gain 0
+		 * for board with termination
+		 * PHY REG1 = DTEST_MUX_SEL(5) | PLL_GAIN_SEL(0)
+		 * | OUTVOL_SWING_CTRL(4)
+		 */
+		HDMI_OUTP_ND(0x0304, 0x54); /*0b01010100*/
+	}
+
+	/* No matter what, start from the power down mode
+	 * PHY REG2 = PD_PWRGEN | PD_PLL | PD_DRIVE_4 | PD_DRIVE_3
+	 * | PD_DRIVE_2 | PD_DRIVE_1 | PD_DESER
+	 */
+	HDMI_OUTP_ND(0x0308, 0x7F); /*0b01111111*/
+
+	/* Turn PowerGen on
+	 * PHY REG2 = PD_PLL | PD_DRIVE_4 | PD_DRIVE_3
+	 * | PD_DRIVE_2 | PD_DRIVE_1 | PD_DESER
+	 */
+	HDMI_OUTP_ND(0x0308, 0x3F); /*0b00111111*/
+
+	/* Turn PLL power on
+	 * PHY REG2 = PD_DRIVE_4 | PD_DRIVE_3
+	 * | PD_DRIVE_2 | PD_DRIVE_1 | PD_DESER
+	 */
+	HDMI_OUTP_ND(0x0308, 0x1F); /*0b00011111*/
+
+	/* Write to HIGH after PLL power down de-assert
+	 * PHY REG3 = PLL_ENABLE
+	 */
+	HDMI_OUTP_ND(0x030C, 0x01);
+	/* ASIC power on; PHY REG9 = 0 */
+	HDMI_OUTP_ND(0x0324, 0x00);
+	/* Enable PLL lock detect, PLL lock det will go high after lock
+	 * Enable the re-time logic
+	 * PHY REG12 = PLL_LOCK_DETECT_EN | RETIMING_ENABLE
+	 */
+	HDMI_OUTP_ND(0x0330, 0x03); /*0b00000011*/
+
+	/* Drivers are on
+	 * PHY REG2 = PD_DESER
+	 */
+	HDMI_OUTP_ND(0x0308, 0x01); /*0b00000001*/
+	/* If the RX detector is needed
+	 * PHY REG2 = RCV_SENSE_EN | PD_DESER
+	 */
+	HDMI_OUTP_ND(0x0308, 0x81); /*0b10000001*/
+
+	offset = 0x0310;
+	while (offset <= 0x032C) {
+		HDMI_OUTP(offset, 0x0);
+		offset += 0x4;
+	}
+
+	/* If we want to use lock enable based on counting
+	 * PHY REG12 = FORCE_LOCK | PLL_LOCK_DETECT_EN | RETIMING_ENABLE
+	 */
+	HDMI_OUTP_ND(0x0330, 0x13); /*0b00010011*/
+}
+
+void hdmi_msm_powerdown_phy(void)
+{
+	/* Assert RESET PHY from controller */
+	HDMI_OUTP_ND(0x02D4, 0x4);
+	udelay(10);
+	/* De-assert RESET PHY from controller */
+	HDMI_OUTP_ND(0x02D4, 0x0);
+	/* Turn off Driver */
+	HDMI_OUTP_ND(0x0308, 0x1F);
+	udelay(10);
+	/* Disable PLL */
+	HDMI_OUTP_ND(0x030C, 0x00);
+	/* Power down PHY */
+	HDMI_OUTP_ND(0x0308, 0x7F); /*0b01111111*/
+}
+
+void hdmi_frame_ctrl_cfg(const struct hdmi_disp_mode_timing_type *timing)
+{
+	/*  0x02C8 HDMI_FRAME_CTRL
+	 *  31 INTERLACED_EN   Interlaced or progressive enable bit
+	 *    0: Frame in progressive
+	 *    1: Frame is interlaced
+	 *  29 HSYNC_HDMI_POL  HSYNC polarity fed to HDMI core
+	 *     0: Active Hi Hsync, detect the rising edge of hsync
+	 *     1: Active lo Hsync, Detect the falling edge of Hsync
+	 *  28 VSYNC_HDMI_POL  VSYNC polarity fed to HDMI core
+	 *     0: Active Hi Vsync, detect the rising edge of vsync
+	 *     1: Active Lo Vsync, Detect the falling edge of Vsync
+	 *  12 RGB_MUX_SEL     ALPHA mdp4 input is RGB, mdp4 input is BGR
+	 */
+	HDMI_OUTP(0x02C8,
+		  ((timing->interlaced << 31) & 0x80000000)
+		| ((timing->active_low_h << 29) & 0x20000000)
+		| ((timing->active_low_v << 28) & 0x10000000)
+		| (1 << 12));
+}
+
+void hdmi_msm_phy_status_poll(void)
+{
+	unsigned int phy_ready;
+	phy_ready = 0x1 & HDMI_INP_ND(0x33c);
+	if (phy_ready) {
+		pr_debug("HDMI Phy Status bit is set and ready\n");
+	} else {
+		pr_debug("HDMI Phy Status bit is not set,"
+			"waiting for ready status\n");
+		do {
+			phy_ready = 0x1 & HDMI_INP_ND(0x33c);
+		} while (!phy_ready);
+	}
+}
+#endif
diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c
index c6e3b4f..3ed305b 100644
--- a/drivers/video/msm/msm_fb.c
+++ b/drivers/video/msm/msm_fb.c
@@ -3,6 +3,7 @@
  * Core MSM framebuffer driver.
  *
  * Copyright (C) 2007 Google Incorporated
+ * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -14,626 +15,3757 @@
  * GNU General Public License for more details.
  */
 
-#include <linux/platform_device.h>
 #include <linux/module.h>
-#include <linux/fb.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
-
-#include <linux/freezer.h>
-#include <linux/wait.h>
+#include <linux/mm.h>
+#include <linux/fb.h>
 #include <linux/msm_mdp.h>
-#include <linux/io.h>
-#include <linux/uaccess.h>
-#include <mach/msm_fb.h>
-#include <mach/board.h>
-#include <linux/workqueue.h>
-#include <linux/clk.h>
-#include <linux/debugfs.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
 #include <linux/dma-mapping.h>
+#include <mach/board.h>
+#include <linux/uaccess.h>
+#include <mach/iommu_domains.h>
 
-#define PRINT_FPS 0
-#define PRINT_BLIT_TIME 0
+#include <linux/workqueue.h>
+#include <linux/string.h>
+#include <linux/version.h>
+#include <linux/proc_fs.h>
+#include <linux/vmalloc.h>
+#include <linux/debugfs.h>
+#include <linux/console.h>
+#include <linux/android_pmem.h>
+#include <linux/leds.h>
+#include <linux/pm_runtime.h>
 
-#define SLEEPING 0x4
-#define UPDATING 0x3
-#define FULL_UPDATE_DONE 0x2
-#define WAKING 0x1
-#define AWAKE 0x0
+#define MSM_FB_C
+#include "msm_fb.h"
+#include "mddihosti.h"
+#include "tvenc.h"
+#include "mdp.h"
+#include "mdp4.h"
 
-#define NONE 0
-#define SUSPEND_RESUME 0x1
-#define FPS 0x2
-#define BLIT_TIME 0x4
-#define SHOW_UPDATES 0x8
+#ifdef CONFIG_FB_MSM_TRIPLE_BUFFER
+#define MSM_FB_NUM	3
+#endif
 
-#define DLOG(mask, fmt, args...) \
-do { \
-	if (msmfb_debug_mask & mask) \
-		printk(KERN_INFO "msmfb: "fmt, ##args); \
-} while (0)
+static unsigned char *fbram;
+static unsigned char *fbram_phys;
+static int fbram_size;
+static boolean bf_supported;
 
-static int msmfb_debug_mask;
-module_param_named(msmfb_debug_mask, msmfb_debug_mask, int,
-		   S_IRUGO | S_IWUSR | S_IWGRP);
+static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST];
+static int pdev_list_cnt;
 
-struct mdp_device *mdp;
+int vsync_mode = 1;
 
-struct msmfb_info {
-	struct fb_info *fb;
-	struct msm_panel_data *panel;
-	int xres;
-	int yres;
-	unsigned output_format;
-	unsigned yoffset;
-	unsigned frame_requested;
-	unsigned frame_done;
-	int sleeping;
-	unsigned update_frame;
-	struct {
-		int left;
-		int top;
-		int eright; /* exclusive */
-		int ebottom; /* exclusive */
-	} update_info;
-	char *black;
+#define MAX_BLIT_REQ 256
 
-	spinlock_t update_lock;
-	struct mutex panel_init_lock;
-	wait_queue_head_t frame_wq;
-	struct work_struct resume_work;
-	struct msmfb_callback dma_callback;
-	struct msmfb_callback vsync_callback;
-	struct hrtimer fake_vsync;
-	ktime_t vsync_request_time;
+#define MAX_FBI_LIST 32
+static struct fb_info *fbi_list[MAX_FBI_LIST];
+static int fbi_list_index;
+
+static struct msm_fb_data_type *mfd_list[MAX_FBI_LIST];
+static int mfd_list_index;
+
+static u32 msm_fb_pseudo_palette[16] = {
+	0x00000000, 0xffffffff, 0xffffffff, 0xffffffff,
+	0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+	0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+	0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff
 };
 
-static int msmfb_open(struct fb_info *info, int user)
+static struct ion_client *iclient;
+
+u32 msm_fb_debug_enabled;
+/* Setting msm_fb_msg_level to 8 prints out ALL messages */
+u32 msm_fb_msg_level = 7;
+
+/* Setting mddi_msg_level to 8 prints out ALL messages */
+u32 mddi_msg_level = 5;
+
+extern int32 mdp_block_power_cnt[MDP_MAX_BLOCK];
+extern unsigned long mdp_timer_duration;
+
+static int msm_fb_register(struct msm_fb_data_type *mfd);
+static int msm_fb_open(struct fb_info *info, int user);
+static int msm_fb_release(struct fb_info *info, int user);
+static int msm_fb_pan_display(struct fb_var_screeninfo *var,
+			      struct fb_info *info);
+static int msm_fb_stop_sw_refresher(struct msm_fb_data_type *mfd);
+int msm_fb_resume_sw_refresher(struct msm_fb_data_type *mfd);
+static int msm_fb_check_var(struct fb_var_screeninfo *var,
+			    struct fb_info *info);
+static int msm_fb_set_par(struct fb_info *info);
+static int msm_fb_blank_sub(int blank_mode, struct fb_info *info,
+			    boolean op_enable);
+static int msm_fb_suspend_sub(struct msm_fb_data_type *mfd);
+static int msm_fb_ioctl(struct fb_info *info, unsigned int cmd,
+			unsigned long arg);
+static int msm_fb_mmap(struct fb_info *info, struct vm_area_struct * vma);
+
+#ifdef MSM_FB_ENABLE_DBGFS
+
+#define MSM_FB_MAX_DBGFS 1024
+#define MAX_BACKLIGHT_BRIGHTNESS 255
+
+int msm_fb_debugfs_file_index;
+struct dentry *msm_fb_debugfs_root;
+struct dentry *msm_fb_debugfs_file[MSM_FB_MAX_DBGFS];
+
+DEFINE_MUTEX(msm_fb_notify_update_sem);
+void msmfb_no_update_notify_timer_cb(unsigned long data)
 {
-	return 0;
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)data;
+	if (!mfd)
+		pr_err("%s mfd NULL\n", __func__);
+	complete(&mfd->msmfb_no_update_notify);
 }
 
-static int msmfb_release(struct fb_info *info, int user)
+struct dentry *msm_fb_get_debugfs_root(void)
 {
-	return 0;
+	if (msm_fb_debugfs_root == NULL)
+		msm_fb_debugfs_root = debugfs_create_dir("msm_fb", NULL);
+
+	return msm_fb_debugfs_root;
 }
 
-/* Called from dma interrupt handler, must not sleep */
-static void msmfb_handle_dma_interrupt(struct msmfb_callback *callback)
+void msm_fb_debugfs_file_create(struct dentry *root, const char *name,
+				u32 *var)
 {
-	unsigned long irq_flags;
-	struct msmfb_info *msmfb  = container_of(callback, struct msmfb_info,
-					       dma_callback);
-
-	spin_lock_irqsave(&msmfb->update_lock, irq_flags);
-	msmfb->frame_done = msmfb->frame_requested;
-	if (msmfb->sleeping == UPDATING &&
-	    msmfb->frame_done == msmfb->update_frame) {
-		DLOG(SUSPEND_RESUME, "full update completed\n");
-		schedule_work(&msmfb->resume_work);
-	}
-	spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
-	wake_up(&msmfb->frame_wq);
-}
-
-static int msmfb_start_dma(struct msmfb_info *msmfb)
-{
-	uint32_t x, y, w, h;
-	unsigned addr;
-	unsigned long irq_flags;
-	uint32_t yoffset;
-	s64 time_since_request;
-	struct msm_panel_data *panel = msmfb->panel;
-
-	spin_lock_irqsave(&msmfb->update_lock, irq_flags);
-	time_since_request = ktime_to_ns(ktime_sub(ktime_get(),
-			     msmfb->vsync_request_time));
-	if (time_since_request > 20 * NSEC_PER_MSEC) {
-		uint32_t us;
-		us = do_div(time_since_request, NSEC_PER_MSEC) / NSEC_PER_USEC;
-		printk(KERN_WARNING "msmfb_start_dma %lld.%03u ms after vsync "
-			"request\n", time_since_request, us);
-	}
-	if (msmfb->frame_done == msmfb->frame_requested) {
-		spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
-		return -1;
-	}
-	if (msmfb->sleeping == SLEEPING) {
-		DLOG(SUSPEND_RESUME, "tried to start dma while asleep\n");
-		spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
-		return -1;
-	}
-	x = msmfb->update_info.left;
-	y = msmfb->update_info.top;
-	w = msmfb->update_info.eright - x;
-	h = msmfb->update_info.ebottom - y;
-	yoffset = msmfb->yoffset;
-	msmfb->update_info.left = msmfb->xres + 1;
-	msmfb->update_info.top = msmfb->yres + 1;
-	msmfb->update_info.eright = 0;
-	msmfb->update_info.ebottom = 0;
-	if (unlikely(w > msmfb->xres || h > msmfb->yres ||
-		     w == 0 || h == 0)) {
-		printk(KERN_INFO "invalid update: %d %d %d "
-				"%d\n", x, y, w, h);
-		msmfb->frame_done = msmfb->frame_requested;
-		goto error;
-	}
-	spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
-
-	addr = ((msmfb->xres * (yoffset + y) + x) * 2);
-	mdp->dma(mdp, addr + msmfb->fb->fix.smem_start,
-		 msmfb->xres * 2, w, h, x, y, &msmfb->dma_callback,
-		 panel->interface_type);
-	return 0;
-error:
-	spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
-	/* some clients need to clear their vsync interrupt */
-	if (panel->clear_vsync)
-		panel->clear_vsync(panel);
-	wake_up(&msmfb->frame_wq);
-	return 0;
-}
-
-/* Called from esync interrupt handler, must not sleep */
-static void msmfb_handle_vsync_interrupt(struct msmfb_callback *callback)
-{
-	struct msmfb_info *msmfb = container_of(callback, struct msmfb_info,
-					       vsync_callback);
-	msmfb_start_dma(msmfb);
-}
-
-static enum hrtimer_restart msmfb_fake_vsync(struct hrtimer *timer)
-{
-	struct msmfb_info *msmfb  = container_of(timer, struct msmfb_info,
-					       fake_vsync);
-	msmfb_start_dma(msmfb);
-	return HRTIMER_NORESTART;
-}
-
-static void msmfb_pan_update(struct fb_info *info, uint32_t left, uint32_t top,
-			     uint32_t eright, uint32_t ebottom,
-			     uint32_t yoffset, int pan_display)
-{
-	struct msmfb_info *msmfb = info->par;
-	struct msm_panel_data *panel = msmfb->panel;
-	unsigned long irq_flags;
-	int sleeping;
-	int retry = 1;
-
-	DLOG(SHOW_UPDATES, "update %d %d %d %d %d %d\n",
-		left, top, eright, ebottom, yoffset, pan_display);
-restart:
-	spin_lock_irqsave(&msmfb->update_lock, irq_flags);
-
-	/* if we are sleeping, on a pan_display wait 10ms (to throttle back
-	 * drawing otherwise return */
-	if (msmfb->sleeping == SLEEPING) {
-		DLOG(SUSPEND_RESUME, "drawing while asleep\n");
-		spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
-		if (pan_display)
-			wait_event_interruptible_timeout(msmfb->frame_wq,
-				msmfb->sleeping != SLEEPING, HZ/10);
+	if (msm_fb_debugfs_file_index >= MSM_FB_MAX_DBGFS)
 		return;
-	}
 
-	sleeping = msmfb->sleeping;
-	/* on a full update, if the last frame has not completed, wait for it */
-	if ((pan_display && msmfb->frame_requested != msmfb->frame_done) ||
-			    sleeping == UPDATING) {
-		int ret;
-		spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
-		ret = wait_event_interruptible_timeout(msmfb->frame_wq,
-			msmfb->frame_done == msmfb->frame_requested &&
-			msmfb->sleeping != UPDATING, 5 * HZ);
-		if (ret <= 0 && (msmfb->frame_requested != msmfb->frame_done ||
-				 msmfb->sleeping == UPDATING)) {
-			if (retry && panel->request_vsync &&
-			    (sleeping == AWAKE)) {
-				panel->request_vsync(panel,
-					&msmfb->vsync_callback);
-				retry = 0;
-				printk(KERN_WARNING "msmfb_pan_display timeout "
-					"rerequest vsync\n");
-			} else {
-				printk(KERN_WARNING "msmfb_pan_display timeout "
-					"waiting for frame start, %d %d\n",
-					msmfb->frame_requested,
-					msmfb->frame_done);
-				return;
-			}
-		}
-		goto restart;
-	}
-
-
-	msmfb->frame_requested++;
-	/* if necessary, update the y offset, if this is the
-	 * first full update on resume, set the sleeping state */
-	if (pan_display) {
-		msmfb->yoffset = yoffset;
-		if (left == 0 && top == 0 && eright == info->var.xres &&
-		    ebottom == info->var.yres) {
-			if (sleeping == WAKING) {
-				msmfb->update_frame = msmfb->frame_requested;
-				DLOG(SUSPEND_RESUME, "full update starting\n");
-				msmfb->sleeping = UPDATING;
-			}
-		}
-	}
-
-	/* set the update request */
-	if (left < msmfb->update_info.left)
-		msmfb->update_info.left = left;
-	if (top < msmfb->update_info.top)
-		msmfb->update_info.top = top;
-	if (eright > msmfb->update_info.eright)
-		msmfb->update_info.eright = eright;
-	if (ebottom > msmfb->update_info.ebottom)
-		msmfb->update_info.ebottom = ebottom;
-	DLOG(SHOW_UPDATES, "update queued %d %d %d %d %d\n",
-		msmfb->update_info.left, msmfb->update_info.top,
-		msmfb->update_info.eright, msmfb->update_info.ebottom,
-		msmfb->yoffset);
-	spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
-
-	/* if the panel is all the way on wait for vsync, otherwise sleep
-	 * for 16 ms (long enough for the dma to panel) and then begin dma */
-	msmfb->vsync_request_time = ktime_get();
-	if (panel->request_vsync && (sleeping == AWAKE)) {
-		panel->request_vsync(panel, &msmfb->vsync_callback);
-	} else {
-		if (!hrtimer_active(&msmfb->fake_vsync)) {
-			hrtimer_start(&msmfb->fake_vsync,
-				      ktime_set(0, NSEC_PER_SEC/60),
-				      HRTIMER_MODE_REL);
-		}
-	}
+	msm_fb_debugfs_file[msm_fb_debugfs_file_index++] =
+	    debugfs_create_u32(name, S_IRUGO | S_IWUSR, root, var);
 }
+#endif
 
-static void msmfb_update(struct fb_info *info, uint32_t left, uint32_t top,
-			 uint32_t eright, uint32_t ebottom)
+int msm_fb_cursor(struct fb_info *info, struct fb_cursor *cursor)
 {
-	msmfb_pan_update(info, left, top, eright, ebottom, 0, 0);
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+
+	if (!mfd->cursor_update)
+		return -ENODEV;
+
+	return mfd->cursor_update(info, cursor);
 }
 
-static void power_on_panel(struct work_struct *work)
+static int msm_fb_resource_initialized;
+
+#ifndef CONFIG_FB_BACKLIGHT
+static int lcd_backlight_registered;
+
+static void msm_fb_set_bl_brightness(struct led_classdev *led_cdev,
+					enum led_brightness value)
 {
-	struct msmfb_info *msmfb =
-		container_of(work, struct msmfb_info, resume_work);
-	struct msm_panel_data *panel = msmfb->panel;
-	unsigned long irq_flags;
+	struct msm_fb_data_type *mfd = dev_get_drvdata(led_cdev->dev->parent);
+	int bl_lvl;
 
-	mutex_lock(&msmfb->panel_init_lock);
-	DLOG(SUSPEND_RESUME, "turning on panel\n");
-	if (msmfb->sleeping == UPDATING) {
-		if (panel->unblank(panel)) {
-			printk(KERN_INFO "msmfb: panel unblank failed,"
-			       "not starting drawing\n");
-			goto error;
-		}
-		spin_lock_irqsave(&msmfb->update_lock, irq_flags);
-		msmfb->sleeping = AWAKE;
-		wake_up(&msmfb->frame_wq);
-		spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
-	}
-error:
-	mutex_unlock(&msmfb->panel_init_lock);
+	if (value > MAX_BACKLIGHT_BRIGHTNESS)
+		value = MAX_BACKLIGHT_BRIGHTNESS;
+
+	/* This maps android backlight level 0 to 255 into
+	   driver backlight level 0 to bl_max with rounding */
+	bl_lvl = (2 * value * mfd->panel_info.bl_max + MAX_BACKLIGHT_BRIGHTNESS)
+		/(2 * MAX_BACKLIGHT_BRIGHTNESS);
+
+	if (!bl_lvl && value)
+		bl_lvl = 1;
+
+	msm_fb_set_backlight(mfd, bl_lvl);
 }
 
-
-static int msmfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
-{
-	if ((var->xres != info->var.xres) ||
-	    (var->yres != info->var.yres) ||
-	    (var->xres_virtual != info->var.xres_virtual) ||
-	    (var->yres_virtual != info->var.yres_virtual) ||
-	    (var->xoffset != info->var.xoffset) ||
-	    (var->bits_per_pixel != info->var.bits_per_pixel) ||
-	    (var->grayscale != info->var.grayscale))
-		 return -EINVAL;
-	return 0;
-}
-
-int msmfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
-{
-	struct msmfb_info *msmfb = info->par;
-	struct msm_panel_data *panel = msmfb->panel;
-
-	/* "UPDT" */
-	if ((panel->caps & MSMFB_CAP_PARTIAL_UPDATES) &&
-	    (var->reserved[0] == 0x54445055)) {
-		msmfb_pan_update(info, var->reserved[1] & 0xffff,
-				 var->reserved[1] >> 16,
-				 var->reserved[2] & 0xffff,
-				 var->reserved[2] >> 16, var->yoffset, 1);
-	} else {
-		msmfb_pan_update(info, 0, 0, info->var.xres, info->var.yres,
-				 var->yoffset, 1);
-	}
-	return 0;
-}
-
-static void msmfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
-{
-	cfb_fillrect(p, rect);
-	msmfb_update(p, rect->dx, rect->dy, rect->dx + rect->width,
-		     rect->dy + rect->height);
-}
-
-static void msmfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
-{
-	cfb_copyarea(p, area);
-	msmfb_update(p, area->dx, area->dy, area->dx + area->width,
-		     area->dy + area->height);
-}
-
-static void msmfb_imageblit(struct fb_info *p, const struct fb_image *image)
-{
-	cfb_imageblit(p, image);
-	msmfb_update(p, image->dx, image->dy, image->dx + image->width,
-		     image->dy + image->height);
-}
-
-
-static int msmfb_blit(struct fb_info *info,
-		      void __user *p)
-{
-	struct mdp_blit_req req;
-	struct mdp_blit_req_list req_list;
-	int i;
-	int ret;
-
-	if (copy_from_user(&req_list, p, sizeof(req_list)))
-		return -EFAULT;
-
-	for (i = 0; i < req_list.count; i++) {
-		struct mdp_blit_req_list *list =
-			(struct mdp_blit_req_list *)p;
-		if (copy_from_user(&req, &list->req[i], sizeof(req)))
-			return -EFAULT;
-		ret = mdp->blit(mdp, info, &req);
-		if (ret)
-			return ret;
-	}
-	return 0;
-}
-
-
-DEFINE_MUTEX(mdp_ppp_lock);
-
-static int msmfb_ioctl(struct fb_info *p, unsigned int cmd, unsigned long arg)
-{
-	void __user *argp = (void __user *)arg;
-	int ret;
-
-	switch (cmd) {
-	case MSMFB_GRP_DISP:
-		mdp->set_grp_disp(mdp, arg);
-		break;
-	case MSMFB_BLIT:
-		ret = msmfb_blit(p, argp);
-		if (ret)
-			return ret;
-		break;
-	default:
-			printk(KERN_INFO "msmfb unknown ioctl: %d\n", cmd);
-			return -EINVAL;
-	}
-	return 0;
-}
-
-static struct fb_ops msmfb_ops = {
-	.owner = THIS_MODULE,
-	.fb_open = msmfb_open,
-	.fb_release = msmfb_release,
-	.fb_check_var = msmfb_check_var,
-	.fb_pan_display = msmfb_pan_display,
-	.fb_fillrect = msmfb_fillrect,
-	.fb_copyarea = msmfb_copyarea,
-	.fb_imageblit = msmfb_imageblit,
-	.fb_ioctl = msmfb_ioctl,
+static struct led_classdev backlight_led = {
+	.name		= "lcd-backlight",
+	.brightness	= MAX_BACKLIGHT_BRIGHTNESS,
+	.brightness_set	= msm_fb_set_bl_brightness,
 };
+#endif
 
-static unsigned PP[16];
+static struct msm_fb_platform_data *msm_fb_pdata;
+unsigned char hdmi_prim_display;
 
-
-
-#define BITS_PER_PIXEL 16
-
-static void setup_fb_info(struct msmfb_info *msmfb)
+int msm_fb_detect_client(const char *name)
 {
-	struct fb_info *fb_info = msmfb->fb;
-	int r;
+	int ret = 0;
+	u32 len;
+#ifdef CONFIG_FB_MSM_MDDI_AUTO_DETECT
+	u32 id;
+#endif
+	if (!msm_fb_pdata)
+		return -EPERM;
 
-	/* finish setting up the fb_info struct */
-	strncpy(fb_info->fix.id, "msmfb", 16);
-	fb_info->fix.ypanstep = 1;
-
-	fb_info->fbops = &msmfb_ops;
-	fb_info->flags = FBINFO_DEFAULT;
-
-	fb_info->fix.type = FB_TYPE_PACKED_PIXELS;
-	fb_info->fix.visual = FB_VISUAL_TRUECOLOR;
-	fb_info->fix.line_length = msmfb->xres * 2;
-
-	fb_info->var.xres = msmfb->xres;
-	fb_info->var.yres = msmfb->yres;
-	fb_info->var.width = msmfb->panel->fb_data->width;
-	fb_info->var.height = msmfb->panel->fb_data->height;
-	fb_info->var.xres_virtual = msmfb->xres;
-	fb_info->var.yres_virtual = msmfb->yres * 2;
-	fb_info->var.bits_per_pixel = BITS_PER_PIXEL;
-	fb_info->var.accel_flags = 0;
-
-	fb_info->var.yoffset = 0;
-
-	if (msmfb->panel->caps & MSMFB_CAP_PARTIAL_UPDATES) {
-		/*
-		 * Set the param in the fixed screen, so userspace can't
-		 * change it. This will be used to check for the
-		 * capability.
-		 */
-		fb_info->fix.reserved[0] = 0x5444;
-		fb_info->fix.reserved[1] = 0x5055;
-
-		/*
-		 * This preloads the value so that if userspace doesn't
-		 * change it, it will be a full update
-		 */
-		fb_info->var.reserved[0] = 0x54445055;
-		fb_info->var.reserved[1] = 0;
-		fb_info->var.reserved[2] = (uint16_t)msmfb->xres |
-					   ((uint32_t)msmfb->yres << 16);
+	len = strnlen(name, PANEL_NAME_MAX_LEN);
+	if (strnlen(msm_fb_pdata->prim_panel_name, PANEL_NAME_MAX_LEN)) {
+		pr_err("\n name = %s, prim_display = %s",
+			name, msm_fb_pdata->prim_panel_name);
+		if (!strncmp((char *)msm_fb_pdata->prim_panel_name,
+			name, len)) {
+			if (!strncmp((char *)msm_fb_pdata->prim_panel_name,
+				"hdmi_msm", len))
+				hdmi_prim_display = 1;
+			return 0;
+		} else {
+			ret = -EPERM;
+		}
 	}
 
-	fb_info->var.red.offset = 11;
-	fb_info->var.red.length = 5;
-	fb_info->var.red.msb_right = 0;
-	fb_info->var.green.offset = 5;
-	fb_info->var.green.length = 6;
-	fb_info->var.green.msb_right = 0;
-	fb_info->var.blue.offset = 0;
-	fb_info->var.blue.length = 5;
-	fb_info->var.blue.msb_right = 0;
-
-	r = fb_alloc_cmap(&fb_info->cmap, 16, 0);
-	fb_info->pseudo_palette = PP;
-
-	PP[0] = 0;
-	for (r = 1; r < 16; r++)
-		PP[r] = 0xffffffff;
-}
-
-static int setup_fbmem(struct msmfb_info *msmfb, struct platform_device *pdev)
-{
-	struct fb_info *fb = msmfb->fb;
-	struct resource *resource;
-	unsigned long size = msmfb->xres * msmfb->yres *
-			     (BITS_PER_PIXEL >> 3) * 2;
-	unsigned char *fbram;
-
-	/* board file might have attached a resource describing an fb */
-	resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!resource)
-		return -EINVAL;
-
-	/* check the resource is large enough to fit the fb */
-	if (resource->end - resource->start < size) {
-		printk(KERN_ERR "allocated resource is too small for "
-				"fb\n");
-		return -ENOMEM;
-	}
-	fb->fix.smem_start = resource->start;
-	fb->fix.smem_len = resource_size(resource);
-	fbram = ioremap(resource->start, resource_size(resource));
-	if (fbram == NULL) {
-		printk(KERN_ERR "msmfb: cannot allocate fbram!\n");
-		return -ENOMEM;
-	}
-	fb->screen_base = fbram;
-	return 0;
-}
-
-static int msmfb_probe(struct platform_device *pdev)
-{
-	struct fb_info *fb;
-	struct msmfb_info *msmfb;
-	struct msm_panel_data *panel = pdev->dev.platform_data;
-	int ret;
-
-	if (!panel) {
-		pr_err("msmfb_probe: no platform data\n");
-		return -EINVAL;
-	}
-	if (!panel->fb_data) {
-		pr_err("msmfb_probe: no fb_data\n");
-		return -EINVAL;
+	if (strnlen(msm_fb_pdata->ext_panel_name, PANEL_NAME_MAX_LEN)) {
+		pr_err("\n name = %s, ext_display = %s",
+			name, msm_fb_pdata->ext_panel_name);
+		if (!strncmp((char *)msm_fb_pdata->ext_panel_name, name, len))
+			return 0;
+		else
+			ret = -EPERM;
 	}
 
-	fb = framebuffer_alloc(sizeof(struct msmfb_info), &pdev->dev);
-	if (!fb)
-		return -ENOMEM;
-	msmfb = fb->par;
-	msmfb->fb = fb;
-	msmfb->panel = panel;
-	msmfb->xres = panel->fb_data->xres;
-	msmfb->yres = panel->fb_data->yres;
-
-	ret = setup_fbmem(msmfb, pdev);
 	if (ret)
-		goto error_setup_fbmem;
+		return ret;
 
-	setup_fb_info(msmfb);
+	ret = -EPERM;
+	if (msm_fb_pdata && msm_fb_pdata->detect_client) {
+		ret = msm_fb_pdata->detect_client(name);
 
-	spin_lock_init(&msmfb->update_lock);
-	mutex_init(&msmfb->panel_init_lock);
-	init_waitqueue_head(&msmfb->frame_wq);
-	INIT_WORK(&msmfb->resume_work, power_on_panel);
-	msmfb->black = kzalloc(msmfb->fb->var.bits_per_pixel*msmfb->xres,
-			       GFP_KERNEL);
+		/* if it's non mddi panel, we need to pre-scan
+		   mddi client to see if we can disable mddi host */
 
-	printk(KERN_INFO "msmfb_probe() installing %d x %d panel\n",
-	       msmfb->xres, msmfb->yres);
+#ifdef CONFIG_FB_MSM_MDDI_AUTO_DETECT
+		if (!ret && msm_fb_pdata->mddi_prescan)
+			id = mddi_get_client_id();
+#endif
+	}
 
-	msmfb->dma_callback.func = msmfb_handle_dma_interrupt;
-	msmfb->vsync_callback.func = msmfb_handle_vsync_interrupt;
-	hrtimer_init(&msmfb->fake_vsync, CLOCK_MONOTONIC,
-		     HRTIMER_MODE_REL);
-
-
-	msmfb->fake_vsync.function = msmfb_fake_vsync;
-
-	ret = register_framebuffer(fb);
-	if (ret)
-		goto error_register_framebuffer;
-
-	msmfb->sleeping = WAKING;
-
-	return 0;
-
-error_register_framebuffer:
-	iounmap(fb->screen_base);
-error_setup_fbmem:
-	framebuffer_release(msmfb->fb);
 	return ret;
 }
 
-static struct platform_driver msm_panel_driver = {
-	/* need to write remove */
-	.probe = msmfb_probe,
-	.driver = {.name = "msm_panel"},
+static ssize_t msm_fb_msm_fb_type(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	ssize_t ret = 0;
+	struct fb_info *fbi = dev_get_drvdata(dev);
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par;
+	struct msm_fb_panel_data *pdata =
+		(struct msm_fb_panel_data *)mfd->pdev->dev.platform_data;
+
+	switch (pdata->panel_info.type) {
+	case NO_PANEL:
+		ret = snprintf(buf, PAGE_SIZE, "no panel\n");
+		break;
+	case MDDI_PANEL:
+		ret = snprintf(buf, PAGE_SIZE, "mddi panel\n");
+		break;
+	case EBI2_PANEL:
+		ret = snprintf(buf, PAGE_SIZE, "ebi2 panel\n");
+		break;
+	case LCDC_PANEL:
+		ret = snprintf(buf, PAGE_SIZE, "lcdc panel\n");
+		break;
+	case EXT_MDDI_PANEL:
+		ret = snprintf(buf, PAGE_SIZE, "ext mddi panel\n");
+		break;
+	case TV_PANEL:
+		ret = snprintf(buf, PAGE_SIZE, "tv panel\n");
+		break;
+	case HDMI_PANEL:
+		ret = snprintf(buf, PAGE_SIZE, "hdmi panel\n");
+		break;
+	case LVDS_PANEL:
+		ret = snprintf(buf, PAGE_SIZE, "lvds panel\n");
+		break;
+	case DTV_PANEL:
+		ret = snprintf(buf, PAGE_SIZE, "dtv panel\n");
+		break;
+	case MIPI_VIDEO_PANEL:
+		ret = snprintf(buf, PAGE_SIZE, "mipi dsi video panel\n");
+		break;
+	case MIPI_CMD_PANEL:
+		ret = snprintf(buf, PAGE_SIZE, "mipi dsi cmd panel\n");
+		break;
+	case WRITEBACK_PANEL:
+		ret = snprintf(buf, PAGE_SIZE, "writeback panel\n");
+		break;
+	default:
+		ret = snprintf(buf, PAGE_SIZE, "unknown panel\n");
+		break;
+	}
+
+	return ret;
+}
+
+static DEVICE_ATTR(msm_fb_type, S_IRUGO, msm_fb_msm_fb_type, NULL);
+static struct attribute *msm_fb_attrs[] = {
+	&dev_attr_msm_fb_type.attr,
+	NULL,
+};
+static struct attribute_group msm_fb_attr_group = {
+	.attrs = msm_fb_attrs,
 };
 
-
-static int msmfb_add_mdp_device(struct device *dev,
-				struct class_interface *class_intf)
+static int msm_fb_create_sysfs(struct platform_device *pdev)
 {
-	/* might need locking if mulitple mdp devices */
-	if (mdp)
+	int rc;
+	struct msm_fb_data_type *mfd = platform_get_drvdata(pdev);
+
+	rc = sysfs_create_group(&mfd->fbi->dev->kobj, &msm_fb_attr_group);
+	if (rc)
+		MSM_FB_ERR("%s: sysfs group creation failed, rc=%d\n", __func__,
+			rc);
+	return rc;
+}
+static void msm_fb_remove_sysfs(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd = platform_get_drvdata(pdev);
+	sysfs_remove_group(&mfd->fbi->dev->kobj, &msm_fb_attr_group);
+}
+
+static int msm_fb_probe(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+	int rc;
+	int err = 0;
+
+	MSM_FB_DEBUG("msm_fb_probe\n");
+
+	if ((pdev->id == 0) && (pdev->num_resources > 0)) {
+		msm_fb_pdata = pdev->dev.platform_data;
+		fbram_size =
+			pdev->resource[0].end - pdev->resource[0].start + 1;
+		fbram_phys = (char *)pdev->resource[0].start;
+		fbram = __va(fbram_phys);
+
+		if (!fbram) {
+			printk(KERN_ERR "fbram ioremap failed!\n");
+			return -ENOMEM;
+		}
+		MSM_FB_DEBUG("msm_fb_probe:  phy_Addr = 0x%x virt = 0x%x\n",
+			     (int)fbram_phys, (int)fbram);
+
+		iclient = msm_ion_client_create(-1, pdev->name);
+		if (IS_ERR_OR_NULL(iclient)) {
+			pr_err("msm_ion_client_create() return"
+				" error, val %p\n", iclient);
+			iclient = NULL;
+		}
+
+		msm_fb_resource_initialized = 1;
 		return 0;
-	mdp = container_of(dev, struct mdp_device, dev);
-	return platform_driver_register(&msm_panel_driver);
+	}
+
+	if (!msm_fb_resource_initialized)
+		return -EPERM;
+
+	mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
+
+	if (!mfd)
+		return -ENODEV;
+
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST)
+		return -ENOMEM;
+
+	mfd->panel_info.frame_count = 0;
+	mfd->bl_level = 0;
+#ifdef CONFIG_FB_MSM_OVERLAY
+	mfd->overlay_play_enable = 1;
+#endif
+
+	bf_supported = mdp4_overlay_borderfill_supported();
+
+	rc = msm_fb_register(mfd);
+	if (rc)
+		return rc;
+	err = pm_runtime_set_active(mfd->fbi->dev);
+	if (err < 0)
+		printk(KERN_ERR "pm_runtime: fail to set active.\n");
+	pm_runtime_enable(mfd->fbi->dev);
+#ifdef CONFIG_FB_BACKLIGHT
+	msm_fb_config_backlight(mfd);
+#else
+	/* android supports only one lcd-backlight/lcd for now */
+	if (!lcd_backlight_registered) {
+		if (led_classdev_register(&pdev->dev, &backlight_led))
+			printk(KERN_ERR "led_classdev_register failed\n");
+		else
+			lcd_backlight_registered = 1;
+	}
+#endif
+
+	pdev_list[pdev_list_cnt++] = pdev;
+	msm_fb_create_sysfs(pdev);
+	return 0;
 }
 
-static void msmfb_remove_mdp_device(struct device *dev,
-				struct class_interface *class_intf)
+static int msm_fb_remove(struct platform_device *pdev)
 {
-	/* might need locking if mulitple mdp devices */
-	if (dev != &mdp->dev)
-		return;
-	platform_driver_unregister(&msm_panel_driver);
-	mdp = NULL;
+	struct msm_fb_data_type *mfd;
+
+	MSM_FB_DEBUG("msm_fb_remove\n");
+
+	mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
+
+	msm_fb_remove_sysfs(pdev);
+
+	pm_runtime_disable(mfd->fbi->dev);
+
+	if (!mfd)
+		return -ENODEV;
+
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	if (msm_fb_suspend_sub(mfd))
+		printk(KERN_ERR "msm_fb_remove: can't stop the device %d\n", mfd->index);
+
+	if (mfd->channel_irq != 0)
+		free_irq(mfd->channel_irq, (void *)mfd);
+
+	if (mfd->vsync_width_boundary)
+		vfree(mfd->vsync_width_boundary);
+
+	if (mfd->vsync_resync_timer.function)
+		del_timer(&mfd->vsync_resync_timer);
+
+	if (mfd->refresh_timer.function)
+		del_timer(&mfd->refresh_timer);
+
+	if (mfd->dma_hrtimer.function)
+		hrtimer_cancel(&mfd->dma_hrtimer);
+
+	if (mfd->msmfb_no_update_notify_timer.function)
+		del_timer(&mfd->msmfb_no_update_notify_timer);
+	complete(&mfd->msmfb_no_update_notify);
+	complete(&mfd->msmfb_update_notify);
+
+	/* remove /dev/fb* */
+	unregister_framebuffer(mfd->fbi);
+
+#ifdef CONFIG_FB_BACKLIGHT
+	/* remove /sys/class/backlight */
+	backlight_device_unregister(mfd->fbi->bl_dev);
+#else
+	if (lcd_backlight_registered) {
+		lcd_backlight_registered = 0;
+		led_classdev_unregister(&backlight_led);
+	}
+#endif
+
+#ifdef MSM_FB_ENABLE_DBGFS
+	if (mfd->sub_dir)
+		debugfs_remove(mfd->sub_dir);
+#endif
+
+	return 0;
 }
 
-static struct class_interface msm_fb_interface = {
-	.add_dev = &msmfb_add_mdp_device,
-	.remove_dev = &msmfb_remove_mdp_device,
+#if defined(CONFIG_PM) && !defined(CONFIG_HAS_EARLYSUSPEND)
+static int msm_fb_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct msm_fb_data_type *mfd;
+	int ret = 0;
+
+	MSM_FB_DEBUG("msm_fb_suspend\n");
+
+	mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
+
+	if ((!mfd) || (mfd->key != MFD_KEY))
+		return 0;
+
+	console_lock();
+	fb_set_suspend(mfd->fbi, FBINFO_STATE_SUSPENDED);
+
+	ret = msm_fb_suspend_sub(mfd);
+	if (ret != 0) {
+		printk(KERN_ERR "msm_fb: failed to suspend! %d\n", ret);
+		fb_set_suspend(mfd->fbi, FBINFO_STATE_RUNNING);
+	} else {
+		pdev->dev.power.power_state = state;
+	}
+
+	console_unlock();
+	return ret;
+}
+#else
+#define msm_fb_suspend NULL
+#endif
+
+static int msm_fb_suspend_sub(struct msm_fb_data_type *mfd)
+{
+	int ret = 0;
+
+	if ((!mfd) || (mfd->key != MFD_KEY))
+		return 0;
+
+	if (mfd->msmfb_no_update_notify_timer.function)
+		del_timer(&mfd->msmfb_no_update_notify_timer);
+	complete(&mfd->msmfb_no_update_notify);
+
+	/*
+	 * suspend this channel
+	 */
+	mfd->suspend.sw_refreshing_enable = mfd->sw_refreshing_enable;
+	mfd->suspend.op_enable = mfd->op_enable;
+	mfd->suspend.panel_power_on = mfd->panel_power_on;
+
+	if (mfd->op_enable) {
+		ret =
+		     msm_fb_blank_sub(FB_BLANK_POWERDOWN, mfd->fbi,
+				      mfd->suspend.op_enable);
+		if (ret) {
+			MSM_FB_INFO
+			    ("msm_fb_suspend: can't turn off display!\n");
+			return ret;
+		}
+		mfd->op_enable = FALSE;
+	}
+	/*
+	 * try to power down
+	 */
+	mdp_pipe_ctrl(MDP_MASTER_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	/*
+	 * detach display channel irq if there's any
+	 * or wait until vsync-resync completes
+	 */
+	if ((mfd->dest == DISPLAY_LCD)) {
+		if (mfd->panel_info.lcd.vsync_enable) {
+			if (mfd->panel_info.lcd.hw_vsync_mode) {
+				if (mfd->channel_irq != 0)
+					disable_irq(mfd->channel_irq);
+			} else {
+				volatile boolean vh_pending;
+				do {
+					vh_pending = mfd->vsync_handler_pending;
+				} while (vh_pending);
+			}
+		}
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int msm_fb_resume_sub(struct msm_fb_data_type *mfd)
+{
+	int ret = 0;
+	struct msm_fb_panel_data *pdata = NULL;
+
+	if ((!mfd) || (mfd->key != MFD_KEY))
+		return 0;
+
+	pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data;
+
+	/* attach display channel irq if there's any */
+	if (mfd->channel_irq != 0)
+		enable_irq(mfd->channel_irq);
+
+	/* resume state var recover */
+	mfd->sw_refreshing_enable = mfd->suspend.sw_refreshing_enable;
+	mfd->op_enable = mfd->suspend.op_enable;
+
+	if (mfd->suspend.panel_power_on) {
+		ret =
+		     msm_fb_blank_sub(FB_BLANK_UNBLANK, mfd->fbi,
+				      mfd->op_enable);
+		if (ret)
+			MSM_FB_INFO("msm_fb_resume: can't turn on display!\n");
+	} else {
+		if (pdata->power_ctrl)
+			pdata->power_ctrl(TRUE);
+	}
+
+	return ret;
+}
+#endif
+
+#if defined(CONFIG_PM) && !defined(CONFIG_HAS_EARLYSUSPEND)
+static int msm_fb_resume(struct platform_device *pdev)
+{
+	/* This resume function is called when interrupt is enabled.
+	 */
+	int ret = 0;
+	struct msm_fb_data_type *mfd;
+
+	MSM_FB_DEBUG("msm_fb_resume\n");
+
+	mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
+
+	if ((!mfd) || (mfd->key != MFD_KEY))
+		return 0;
+
+	console_lock();
+	ret = msm_fb_resume_sub(mfd);
+	pdev->dev.power.power_state = PMSG_ON;
+	fb_set_suspend(mfd->fbi, FBINFO_STATE_RUNNING);
+	console_unlock();
+
+	return ret;
+}
+#else
+#define msm_fb_resume NULL
+#endif
+
+static int msm_fb_runtime_suspend(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: suspending...\n");
+	return 0;
+}
+
+static int msm_fb_runtime_resume(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: resuming...\n");
+	return 0;
+}
+
+static int msm_fb_runtime_idle(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: idling...\n");
+	return 0;
+}
+
+#if (defined(CONFIG_SUSPEND) && defined(CONFIG_FB_MSM_HDMI_MSM_PANEL))
+static int msm_fb_ext_suspend(struct device *dev)
+{
+	struct msm_fb_data_type *mfd = dev_get_drvdata(dev);
+	int ret = 0;
+
+	if ((!mfd) || (mfd->key != MFD_KEY))
+		return 0;
+
+	if (mfd->panel_info.type == HDMI_PANEL ||
+		mfd->panel_info.type == DTV_PANEL)
+		ret = msm_fb_suspend_sub(mfd);
+
+	return ret;
+}
+
+static int msm_fb_ext_resume(struct device *dev)
+{
+	struct msm_fb_data_type *mfd = dev_get_drvdata(dev);
+	int ret = 0;
+
+	if ((!mfd) || (mfd->key != MFD_KEY))
+		return 0;
+
+	if (mfd->panel_info.type == HDMI_PANEL ||
+		mfd->panel_info.type == DTV_PANEL)
+		ret = msm_fb_resume_sub(mfd);
+
+	return ret;
+}
+#endif
+
+static struct dev_pm_ops msm_fb_dev_pm_ops = {
+	.runtime_suspend = msm_fb_runtime_suspend,
+	.runtime_resume = msm_fb_runtime_resume,
+	.runtime_idle = msm_fb_runtime_idle,
+#if (defined(CONFIG_SUSPEND) && defined(CONFIG_FB_MSM_HDMI_MSM_PANEL))
+	.suspend = msm_fb_ext_suspend,
+	.resume = msm_fb_ext_resume,
+#endif
 };
 
-static int __init msmfb_init(void)
+static struct platform_driver msm_fb_driver = {
+	.probe = msm_fb_probe,
+	.remove = msm_fb_remove,
+#ifndef CONFIG_HAS_EARLYSUSPEND
+	.suspend = msm_fb_suspend,
+	.resume = msm_fb_resume,
+#endif
+	.shutdown = NULL,
+	.driver = {
+		   /* Driver name must match the device name added in platform.c. */
+		   .name = "msm_fb",
+		   .pm = &msm_fb_dev_pm_ops,
+		   },
+};
+
+#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_FB_MSM_MDP303)
+static void memset32_io(u32 __iomem *_ptr, u32 val, size_t count)
 {
-	return register_mdp_client(&msm_fb_interface);
+	count >>= 2;
+	while (count--)
+		writel(val, _ptr++);
+}
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void msmfb_early_suspend(struct early_suspend *h)
+{
+	struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type,
+						    early_suspend);
+#if defined(CONFIG_FB_MSM_MDP303)
+	/*
+	* For MDP with overlay, set framebuffer with black pixels
+	* to show black screen on HDMI.
+	*/
+	struct fb_info *fbi = mfd->fbi;
+	switch (mfd->fbi->var.bits_per_pixel) {
+	case 32:
+		memset32_io((void *)fbi->screen_base, 0xFF000000,
+							fbi->fix.smem_len);
+		break;
+	default:
+		memset32_io((void *)fbi->screen_base, 0x00, fbi->fix.smem_len);
+		break;
+	}
+#endif
+	msm_fb_suspend_sub(mfd);
 }
 
-module_init(msmfb_init);
+static void msmfb_early_resume(struct early_suspend *h)
+{
+	struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type,
+						    early_suspend);
+	msm_fb_resume_sub(mfd);
+}
+#endif
+
+static int unset_bl_level, bl_updated;
+static int bl_level_old;
+
+void msm_fb_set_backlight(struct msm_fb_data_type *mfd, __u32 bkl_lvl)
+{
+	struct msm_fb_panel_data *pdata;
+
+	if (!mfd->panel_power_on || !bl_updated) {
+		unset_bl_level = bkl_lvl;
+		return;
+	} else {
+		unset_bl_level = 0;
+	}
+
+	pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data;
+
+	if ((pdata) && (pdata->set_backlight)) {
+		down(&mfd->sem);
+		if (bl_level_old == bkl_lvl) {
+			up(&mfd->sem);
+			return;
+		}
+		mfd->bl_level = bkl_lvl;
+		pdata->set_backlight(mfd);
+		bl_level_old = mfd->bl_level;
+		up(&mfd->sem);
+	}
+}
+
+static int msm_fb_blank_sub(int blank_mode, struct fb_info *info,
+			    boolean op_enable)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	struct msm_fb_panel_data *pdata = NULL;
+	int ret = 0;
+
+	if (!op_enable)
+		return -EPERM;
+
+	pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data;
+	if ((!pdata) || (!pdata->on) || (!pdata->off)) {
+		printk(KERN_ERR "msm_fb_blank_sub: no panel operation detected!\n");
+		return -ENODEV;
+	}
+
+	switch (blank_mode) {
+	case FB_BLANK_UNBLANK:
+		if (!mfd->panel_power_on) {
+			msleep(16);
+			ret = pdata->on(mfd->pdev);
+			if (ret == 0) {
+				mfd->panel_power_on = TRUE;
+
+/* ToDo: possible conflict with android which doesn't expect sw refresher */
+/*
+	  if (!mfd->hw_refresh)
+	  {
+	    if ((ret = msm_fb_resume_sw_refresher(mfd)) != 0)
+	    {
+	      MSM_FB_INFO("msm_fb_blank_sub: msm_fb_resume_sw_refresher failed = %d!\n",ret);
+	    }
+	  }
+*/
+			}
+		}
+		break;
+
+	case FB_BLANK_VSYNC_SUSPEND:
+	case FB_BLANK_HSYNC_SUSPEND:
+	case FB_BLANK_NORMAL:
+	case FB_BLANK_POWERDOWN:
+	default:
+		if (mfd->panel_power_on) {
+			int curr_pwr_state;
+
+			mfd->op_enable = FALSE;
+			curr_pwr_state = mfd->panel_power_on;
+			mfd->panel_power_on = FALSE;
+			bl_updated = 0;
+
+			msleep(16);
+			ret = pdata->off(mfd->pdev);
+			if (ret)
+				mfd->panel_power_on = curr_pwr_state;
+
+			mfd->op_enable = TRUE;
+		} else {
+			if (pdata->power_ctrl)
+				pdata->power_ctrl(FALSE);
+		}
+		break;
+	}
+
+	return ret;
+}
+
+int calc_fb_offset(struct msm_fb_data_type *mfd, struct fb_info *fbi, int bpp)
+{
+	struct msm_panel_info *panel_info = &mfd->panel_info;
+	int remainder, yres, offset;
+
+	if (panel_info->mode2_yres != 0) {
+		yres = panel_info->mode2_yres;
+		remainder = (fbi->fix.line_length*yres) & (PAGE_SIZE - 1);
+	} else {
+		yres = panel_info->yres;
+		remainder = (fbi->fix.line_length*yres) & (PAGE_SIZE - 1);
+	}
+
+	if (!remainder)
+		remainder = PAGE_SIZE;
+
+	if (fbi->var.yoffset < yres) {
+		offset = (fbi->var.xoffset * bpp);
+				/* iBuf->buf +=	fbi->var.xoffset * bpp + 0 *
+				yres * fbi->fix.line_length; */
+	} else if (fbi->var.yoffset >= yres && fbi->var.yoffset < 2 * yres) {
+		offset = (fbi->var.xoffset * bpp + yres *
+		fbi->fix.line_length + PAGE_SIZE - remainder);
+	} else {
+		offset = (fbi->var.xoffset * bpp + 2 * yres *
+		fbi->fix.line_length + 2 * (PAGE_SIZE - remainder));
+	}
+	return offset;
+}
+
+static void msm_fb_fillrect(struct fb_info *info,
+			    const struct fb_fillrect *rect)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+
+	cfb_fillrect(info, rect);
+	if (!mfd->hw_refresh && (info->var.yoffset == 0) &&
+		!mfd->sw_currently_refreshing) {
+		struct fb_var_screeninfo var;
+
+		var = info->var;
+		var.reserved[0] = 0x54445055;
+		var.reserved[1] = (rect->dy << 16) | (rect->dx);
+		var.reserved[2] = ((rect->dy + rect->height) << 16) |
+		    (rect->dx + rect->width);
+
+		msm_fb_pan_display(&var, info);
+	}
+}
+
+static void msm_fb_copyarea(struct fb_info *info,
+			    const struct fb_copyarea *area)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+
+	cfb_copyarea(info, area);
+	if (!mfd->hw_refresh && (info->var.yoffset == 0) &&
+		!mfd->sw_currently_refreshing) {
+		struct fb_var_screeninfo var;
+
+		var = info->var;
+		var.reserved[0] = 0x54445055;
+		var.reserved[1] = (area->dy << 16) | (area->dx);
+		var.reserved[2] = ((area->dy + area->height) << 16) |
+		    (area->dx + area->width);
+
+		msm_fb_pan_display(&var, info);
+	}
+}
+
+static void msm_fb_imageblit(struct fb_info *info, const struct fb_image *image)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+
+	cfb_imageblit(info, image);
+	if (!mfd->hw_refresh && (info->var.yoffset == 0) &&
+		!mfd->sw_currently_refreshing) {
+		struct fb_var_screeninfo var;
+
+		var = info->var;
+		var.reserved[0] = 0x54445055;
+		var.reserved[1] = (image->dy << 16) | (image->dx);
+		var.reserved[2] = ((image->dy + image->height) << 16) |
+		    (image->dx + image->width);
+
+		msm_fb_pan_display(&var, info);
+	}
+}
+
+static int msm_fb_blank(int blank_mode, struct fb_info *info)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	return msm_fb_blank_sub(blank_mode, info, mfd->op_enable);
+}
+
+static int msm_fb_set_lut(struct fb_cmap *cmap, struct fb_info *info)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+
+	if (!mfd->lut_update)
+		return -ENODEV;
+
+	mfd->lut_update(info, cmap);
+	return 0;
+}
+
+/*
+ * Custom Framebuffer mmap() function for MSM driver.
+ * Differs from standard mmap() function by allowing for customized
+ * page-protection.
+ */
+static int msm_fb_mmap(struct fb_info *info, struct vm_area_struct * vma)
+{
+	/* Get frame buffer memory range. */
+	unsigned long start = info->fix.smem_start;
+	u32 len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);
+	unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	if (off >= len) {
+		/* memory mapped io */
+		off -= len;
+		if (info->var.accel_flags) {
+			mutex_unlock(&info->lock);
+			return -EINVAL;
+		}
+		start = info->fix.mmio_start;
+		len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);
+	}
+
+	/* Set VM flags. */
+	start &= PAGE_MASK;
+	if ((vma->vm_end - vma->vm_start + off) > len)
+		return -EINVAL;
+	off += start;
+	vma->vm_pgoff = off >> PAGE_SHIFT;
+	/* This is an IO map - tell maydump to skip this VMA */
+	vma->vm_flags |= VM_IO | VM_RESERVED;
+
+	/* Set VM page protection */
+	if (mfd->mdp_fb_page_protection == MDP_FB_PAGE_PROTECTION_WRITECOMBINE)
+		vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+	else if (mfd->mdp_fb_page_protection ==
+			MDP_FB_PAGE_PROTECTION_WRITETHROUGHCACHE)
+		vma->vm_page_prot = pgprot_writethroughcache(vma->vm_page_prot);
+	else if (mfd->mdp_fb_page_protection ==
+			MDP_FB_PAGE_PROTECTION_WRITEBACKCACHE)
+		vma->vm_page_prot = pgprot_writebackcache(vma->vm_page_prot);
+	else if (mfd->mdp_fb_page_protection ==
+			MDP_FB_PAGE_PROTECTION_WRITEBACKWACACHE)
+		vma->vm_page_prot = pgprot_writebackwacache(vma->vm_page_prot);
+	else
+		vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+	/* Remap the frame buffer I/O range */
+	if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
+				vma->vm_end - vma->vm_start,
+				vma->vm_page_prot))
+		return -EAGAIN;
+
+	return 0;
+}
+
+static struct fb_ops msm_fb_ops = {
+	.owner = THIS_MODULE,
+	.fb_open = msm_fb_open,
+	.fb_release = msm_fb_release,
+	.fb_read = NULL,
+	.fb_write = NULL,
+	.fb_cursor = NULL,
+	.fb_check_var = msm_fb_check_var,	/* vinfo check */
+	.fb_set_par = msm_fb_set_par,	/* set the video mode according to info->var */
+	.fb_setcolreg = NULL,	/* set color register */
+	.fb_blank = msm_fb_blank,	/* blank display */
+	.fb_pan_display = msm_fb_pan_display,	/* pan display */
+	.fb_fillrect = msm_fb_fillrect,	/* Draws a rectangle */
+	.fb_copyarea = msm_fb_copyarea,	/* Copy data from area to another */
+	.fb_imageblit = msm_fb_imageblit,	/* Draws a image to the display */
+	.fb_rotate = NULL,
+	.fb_sync = NULL,	/* wait for blit idle, optional */
+	.fb_ioctl = msm_fb_ioctl,	/* perform fb specific ioctl (optional) */
+	.fb_mmap = msm_fb_mmap,
+};
+
+static __u32 msm_fb_line_length(__u32 fb_index, __u32 xres, int bpp)
+{
+	/* The adreno GPU hardware requires that the pitch be aligned to
+	   32 pixels for color buffers, so for the cases where the GPU
+	   is writing directly to fb0, the framebuffer pitch
+	   also needs to be 32 pixel aligned */
+
+	if (fb_index == 0)
+		return ALIGN(xres, 32) * bpp;
+	else
+		return xres * bpp;
+}
+
+static int msm_fb_register(struct msm_fb_data_type *mfd)
+{
+	int ret = -ENODEV;
+	int bpp;
+	struct msm_panel_info *panel_info = &mfd->panel_info;
+	struct fb_info *fbi = mfd->fbi;
+	struct fb_fix_screeninfo *fix;
+	struct fb_var_screeninfo *var;
+	int *id;
+	int fbram_offset;
+	int remainder, remainder_mode2;
+
+	/*
+	 * fb info initialization
+	 */
+	fix = &fbi->fix;
+	var = &fbi->var;
+
+	fix->type_aux = 0;	/* if type == FB_TYPE_INTERLEAVED_PLANES */
+	fix->visual = FB_VISUAL_TRUECOLOR;	/* True Color */
+	fix->ywrapstep = 0;	/* No support */
+	fix->mmio_start = 0;	/* No MMIO Address */
+	fix->mmio_len = 0;	/* No MMIO Address */
+	fix->accel = FB_ACCEL_NONE;/* FB_ACCEL_MSM needes to be added in fb.h */
+
+	var->xoffset = 0,	/* Offset from virtual to visible */
+	var->yoffset = 0,	/* resolution */
+	var->grayscale = 0,	/* No graylevels */
+	var->nonstd = 0,	/* standard pixel format */
+	var->activate = FB_ACTIVATE_VBL,	/* activate it at vsync */
+	var->height = -1,	/* height of picture in mm */
+	var->width = -1,	/* width of picture in mm */
+	var->accel_flags = 0,	/* acceleration flags */
+	var->sync = 0,	/* see FB_SYNC_* */
+	var->rotate = 0,	/* angle we rotate counter clockwise */
+	mfd->op_enable = FALSE;
+
+	switch (mfd->fb_imgType) {
+	case MDP_RGB_565:
+		fix->type = FB_TYPE_PACKED_PIXELS;
+		fix->xpanstep = 1;
+		fix->ypanstep = 1;
+		var->vmode = FB_VMODE_NONINTERLACED;
+		var->blue.offset = 0;
+		var->green.offset = 5;
+		var->red.offset = 11;
+		var->blue.length = 5;
+		var->green.length = 6;
+		var->red.length = 5;
+		var->blue.msb_right = 0;
+		var->green.msb_right = 0;
+		var->red.msb_right = 0;
+		var->transp.offset = 0;
+		var->transp.length = 0;
+		bpp = 2;
+		break;
+
+	case MDP_RGB_888:
+		fix->type = FB_TYPE_PACKED_PIXELS;
+		fix->xpanstep = 1;
+		fix->ypanstep = 1;
+		var->vmode = FB_VMODE_NONINTERLACED;
+		var->blue.offset = 0;
+		var->green.offset = 8;
+		var->red.offset = 16;
+		var->blue.length = 8;
+		var->green.length = 8;
+		var->red.length = 8;
+		var->blue.msb_right = 0;
+		var->green.msb_right = 0;
+		var->red.msb_right = 0;
+		var->transp.offset = 0;
+		var->transp.length = 0;
+		bpp = 3;
+		break;
+
+	case MDP_ARGB_8888:
+		fix->type = FB_TYPE_PACKED_PIXELS;
+		fix->xpanstep = 1;
+		fix->ypanstep = 1;
+		var->vmode = FB_VMODE_NONINTERLACED;
+		var->blue.offset = 0;
+		var->green.offset = 8;
+		var->red.offset = 16;
+		var->blue.length = 8;
+		var->green.length = 8;
+		var->red.length = 8;
+		var->blue.msb_right = 0;
+		var->green.msb_right = 0;
+		var->red.msb_right = 0;
+		var->transp.offset = 24;
+		var->transp.length = 8;
+		bpp = 4;
+		break;
+
+	case MDP_RGBA_8888:
+		fix->type = FB_TYPE_PACKED_PIXELS;
+		fix->xpanstep = 1;
+		fix->ypanstep = 1;
+		var->vmode = FB_VMODE_NONINTERLACED;
+		var->blue.offset = 8;
+		var->green.offset = 16;
+		var->red.offset = 24;
+		var->blue.length = 8;
+		var->green.length = 8;
+		var->red.length = 8;
+		var->blue.msb_right = 0;
+		var->green.msb_right = 0;
+		var->red.msb_right = 0;
+		var->transp.offset = 0;
+		var->transp.length = 8;
+		bpp = 4;
+		break;
+
+	case MDP_YCRYCB_H2V1:
+		/* ToDo: need to check TV-Out YUV422i framebuffer format */
+		/*       we might need to create new type define */
+		fix->type = FB_TYPE_INTERLEAVED_PLANES;
+		fix->xpanstep = 2;
+		fix->ypanstep = 1;
+		var->vmode = FB_VMODE_NONINTERLACED;
+
+		/* how about R/G/B offset? */
+		var->blue.offset = 0;
+		var->green.offset = 5;
+		var->red.offset = 11;
+		var->blue.length = 5;
+		var->green.length = 6;
+		var->red.length = 5;
+		var->blue.msb_right = 0;
+		var->green.msb_right = 0;
+		var->red.msb_right = 0;
+		var->transp.offset = 0;
+		var->transp.length = 0;
+		bpp = 2;
+		break;
+
+	default:
+		MSM_FB_ERR("msm_fb_init: fb %d unkown image type!\n",
+			   mfd->index);
+		return ret;
+	}
+
+	fix->type = panel_info->is_3d_panel;
+
+	fix->line_length = msm_fb_line_length(mfd->index, panel_info->xres,
+					      bpp);
+
+	/* Make sure all buffers can be addressed on a page boundary by an x
+	 * and y offset */
+
+	remainder = (fix->line_length * panel_info->yres) & (PAGE_SIZE - 1);
+					/* PAGE_SIZE is a power of 2 */
+	if (!remainder)
+		remainder = PAGE_SIZE;
+	remainder_mode2 = (fix->line_length *
+				panel_info->mode2_yres) & (PAGE_SIZE - 1);
+	if (!remainder_mode2)
+		remainder_mode2 = PAGE_SIZE;
+
+	/*
+	 * calculate smem_len based on max size of two supplied modes.
+	 * Only fb0 has mem. fb1 and fb2 don't have mem.
+	 */
+	if (!bf_supported || mfd->index == 0)
+		fix->smem_len = MAX((msm_fb_line_length(mfd->index,
+							panel_info->xres,
+							bpp) *
+				     panel_info->yres + PAGE_SIZE -
+				     remainder) * mfd->fb_page,
+				    (msm_fb_line_length(mfd->index,
+							panel_info->mode2_xres,
+							bpp) *
+				     panel_info->mode2_yres + PAGE_SIZE -
+				     remainder_mode2) * mfd->fb_page);
+	else if (mfd->index == 1 || mfd->index == 2) {
+		pr_debug("%s:%d no memory is allocated for fb%d!\n",
+			__func__, __LINE__, mfd->index);
+		fix->smem_len = 0;
+	}
+
+	mfd->var_xres = panel_info->xres;
+	mfd->var_yres = panel_info->yres;
+	mfd->var_frame_rate = panel_info->frame_rate;
+
+	var->pixclock = mfd->panel_info.clk_rate;
+	mfd->var_pixclock = var->pixclock;
+
+	var->xres = panel_info->xres;
+	var->yres = panel_info->yres;
+	var->xres_virtual = panel_info->xres;
+	var->yres_virtual = panel_info->yres * mfd->fb_page +
+		((PAGE_SIZE - remainder)/fix->line_length) * mfd->fb_page;
+	var->bits_per_pixel = bpp * 8;	/* FrameBuffer color depth */
+	if (mfd->dest == DISPLAY_LCD) {
+		if (panel_info->type == MDDI_PANEL && panel_info->mddi.is_type1)
+			var->reserved[3] = panel_info->lcd.refx100 / (100 * 2);
+		else
+			var->reserved[3] = panel_info->lcd.refx100 / 100;
+	} else {
+		if (panel_info->type == MIPI_VIDEO_PANEL) {
+			var->reserved[3] = panel_info->mipi.frame_rate;
+		} else {
+			var->reserved[3] = panel_info->clk_rate /
+				((panel_info->lcdc.h_back_porch +
+				  panel_info->lcdc.h_front_porch +
+				  panel_info->lcdc.h_pulse_width +
+				  panel_info->xres) *
+				 (panel_info->lcdc.v_back_porch +
+				  panel_info->lcdc.v_front_porch +
+				  panel_info->lcdc.v_pulse_width +
+				  panel_info->yres));
+		}
+	}
+	pr_debug("reserved[3] %u\n", var->reserved[3]);
+
+		/*
+		 * id field for fb app
+		 */
+	id = (int *)&mfd->panel;
+
+	switch (mdp_rev) {
+	case MDP_REV_20:
+		snprintf(fix->id, sizeof(fix->id), "msmfb20_%x", (__u32) *id);
+		break;
+	case MDP_REV_22:
+		snprintf(fix->id, sizeof(fix->id), "msmfb22_%x", (__u32) *id);
+		break;
+	case MDP_REV_30:
+		snprintf(fix->id, sizeof(fix->id), "msmfb30_%x", (__u32) *id);
+		break;
+	case MDP_REV_303:
+		snprintf(fix->id, sizeof(fix->id), "msmfb303_%x", (__u32) *id);
+		break;
+	case MDP_REV_31:
+		snprintf(fix->id, sizeof(fix->id), "msmfb31_%x", (__u32) *id);
+		break;
+	case MDP_REV_40:
+		snprintf(fix->id, sizeof(fix->id), "msmfb40_%x", (__u32) *id);
+		break;
+	case MDP_REV_41:
+		snprintf(fix->id, sizeof(fix->id), "msmfb41_%x", (__u32) *id);
+		break;
+	case MDP_REV_42:
+		snprintf(fix->id, sizeof(fix->id), "msmfb42_%x", (__u32) *id);
+		break;
+	case MDP_REV_43:
+		snprintf(fix->id, sizeof(fix->id), "msmfb43_%x", (__u32) *id);
+		break;
+	case MDP_REV_44:
+		snprintf(fix->id, sizeof(fix->id), "msmfb44_%x", (__u32) *id);
+		break;
+	default:
+		snprintf(fix->id, sizeof(fix->id), "msmfb0_%x", (__u32) *id);
+		break;
+	}
+
+	fbi->fbops = &msm_fb_ops;
+	fbi->flags = FBINFO_FLAG_DEFAULT;
+	fbi->pseudo_palette = msm_fb_pseudo_palette;
+
+	mfd->ref_cnt = 0;
+	mfd->sw_currently_refreshing = FALSE;
+	mfd->sw_refreshing_enable = TRUE;
+	mfd->panel_power_on = FALSE;
+
+	mfd->pan_waiting = FALSE;
+	init_completion(&mfd->pan_comp);
+	init_completion(&mfd->refresher_comp);
+	sema_init(&mfd->sem, 1);
+
+	init_timer(&mfd->msmfb_no_update_notify_timer);
+	mfd->msmfb_no_update_notify_timer.function =
+			msmfb_no_update_notify_timer_cb;
+	mfd->msmfb_no_update_notify_timer.data = (unsigned long)mfd;
+	init_completion(&mfd->msmfb_update_notify);
+	init_completion(&mfd->msmfb_no_update_notify);
+
+	fbram_offset = PAGE_ALIGN((int)fbram)-(int)fbram;
+	fbram += fbram_offset;
+	fbram_phys += fbram_offset;
+	fbram_size -= fbram_offset;
+
+	if (!bf_supported || mfd->index == 0)
+		if (fbram_size < fix->smem_len) {
+			pr_err("error: no more framebuffer memory!\n");
+			return -ENOMEM;
+		}
+
+	fbi->screen_base = fbram;
+	fbi->fix.smem_start = (unsigned long)fbram_phys;
+
+	msm_iommu_map_contig_buffer(fbi->fix.smem_start,
+					DISPLAY_DOMAIN,
+					GEN_POOL,
+					fbi->fix.smem_len,
+					SZ_4K,
+					1,
+					&(mfd->display_iova));
+
+	msm_iommu_map_contig_buffer(fbi->fix.smem_start,
+					ROTATOR_DOMAIN,
+					GEN_POOL,
+					fbi->fix.smem_len,
+					SZ_4K,
+					1,
+					&(mfd->rotator_iova));
+
+	if (!bf_supported || mfd->index == 0)
+		memset(fbi->screen_base, 0x0, fix->smem_len);
+
+	mfd->op_enable = TRUE;
+	mfd->panel_power_on = FALSE;
+
+	/* cursor memory allocation */
+	if (mfd->cursor_update) {
+		mfd->cursor_buf = dma_alloc_coherent(NULL,
+					MDP_CURSOR_SIZE,
+					(dma_addr_t *) &mfd->cursor_buf_phys,
+					GFP_KERNEL);
+		if (!mfd->cursor_buf)
+			mfd->cursor_update = 0;
+	}
+
+	if (mfd->lut_update) {
+		ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
+		if (ret)
+			printk(KERN_ERR "%s: fb_alloc_cmap() failed!\n",
+					__func__);
+	}
+
+	if (register_framebuffer(fbi) < 0) {
+		if (mfd->lut_update)
+			fb_dealloc_cmap(&fbi->cmap);
+
+		if (mfd->cursor_buf)
+			dma_free_coherent(NULL,
+				MDP_CURSOR_SIZE,
+				mfd->cursor_buf,
+				(dma_addr_t) mfd->cursor_buf_phys);
+
+		mfd->op_enable = FALSE;
+		return -EPERM;
+	}
+
+	fbram += fix->smem_len;
+	fbram_phys += fix->smem_len;
+	fbram_size -= fix->smem_len;
+
+	MSM_FB_INFO
+	    ("FrameBuffer[%d] %dx%d size=%d bytes is registered successfully!\n",
+	     mfd->index, fbi->var.xres, fbi->var.yres, fbi->fix.smem_len);
+
+#ifdef CONFIG_FB_MSM_LOGO
+	/* Flip buffer */
+	if (!load_565rle_image(INIT_IMAGE_FILE, bf_supported))
+		;
+#endif
+	ret = 0;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	if (mfd->panel_info.type != DTV_PANEL) {
+		mfd->early_suspend.suspend = msmfb_early_suspend;
+		mfd->early_suspend.resume = msmfb_early_resume;
+		mfd->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB - 2;
+		register_early_suspend(&mfd->early_suspend);
+	}
+#endif
+
+#ifdef MSM_FB_ENABLE_DBGFS
+	{
+		struct dentry *root;
+		struct dentry *sub_dir;
+		char sub_name[2];
+
+		root = msm_fb_get_debugfs_root();
+		if (root != NULL) {
+			sub_name[0] = (char)(mfd->index + 0x30);
+			sub_name[1] = '\0';
+			sub_dir = debugfs_create_dir(sub_name, root);
+		} else {
+			sub_dir = NULL;
+		}
+
+		mfd->sub_dir = sub_dir;
+
+		if (sub_dir) {
+			msm_fb_debugfs_file_create(sub_dir, "op_enable",
+						   (u32 *) &mfd->op_enable);
+			msm_fb_debugfs_file_create(sub_dir, "panel_power_on",
+						   (u32 *) &mfd->
+						   panel_power_on);
+			msm_fb_debugfs_file_create(sub_dir, "ref_cnt",
+						   (u32 *) &mfd->ref_cnt);
+			msm_fb_debugfs_file_create(sub_dir, "fb_imgType",
+						   (u32 *) &mfd->fb_imgType);
+			msm_fb_debugfs_file_create(sub_dir,
+						   "sw_currently_refreshing",
+						   (u32 *) &mfd->
+						   sw_currently_refreshing);
+			msm_fb_debugfs_file_create(sub_dir,
+						   "sw_refreshing_enable",
+						   (u32 *) &mfd->
+						   sw_refreshing_enable);
+
+			msm_fb_debugfs_file_create(sub_dir, "xres",
+						   (u32 *) &mfd->panel_info.
+						   xres);
+			msm_fb_debugfs_file_create(sub_dir, "yres",
+						   (u32 *) &mfd->panel_info.
+						   yres);
+			msm_fb_debugfs_file_create(sub_dir, "bpp",
+						   (u32 *) &mfd->panel_info.
+						   bpp);
+			msm_fb_debugfs_file_create(sub_dir, "type",
+						   (u32 *) &mfd->panel_info.
+						   type);
+			msm_fb_debugfs_file_create(sub_dir, "wait_cycle",
+						   (u32 *) &mfd->panel_info.
+						   wait_cycle);
+			msm_fb_debugfs_file_create(sub_dir, "pdest",
+						   (u32 *) &mfd->panel_info.
+						   pdest);
+			msm_fb_debugfs_file_create(sub_dir, "backbuff",
+						   (u32 *) &mfd->panel_info.
+						   fb_num);
+			msm_fb_debugfs_file_create(sub_dir, "clk_rate",
+						   (u32 *) &mfd->panel_info.
+						   clk_rate);
+			msm_fb_debugfs_file_create(sub_dir, "frame_count",
+						   (u32 *) &mfd->panel_info.
+						   frame_count);
+
+
+			switch (mfd->dest) {
+			case DISPLAY_LCD:
+				msm_fb_debugfs_file_create(sub_dir,
+				"vsync_enable",
+				(u32 *)&mfd->panel_info.lcd.vsync_enable);
+				msm_fb_debugfs_file_create(sub_dir,
+				"refx100",
+				(u32 *) &mfd->panel_info.lcd. refx100);
+				msm_fb_debugfs_file_create(sub_dir,
+				"v_back_porch",
+				(u32 *) &mfd->panel_info.lcd.v_back_porch);
+				msm_fb_debugfs_file_create(sub_dir,
+				"v_front_porch",
+				(u32 *) &mfd->panel_info.lcd.v_front_porch);
+				msm_fb_debugfs_file_create(sub_dir,
+				"v_pulse_width",
+				(u32 *) &mfd->panel_info.lcd.v_pulse_width);
+				msm_fb_debugfs_file_create(sub_dir,
+				"hw_vsync_mode",
+				(u32 *) &mfd->panel_info.lcd.hw_vsync_mode);
+				msm_fb_debugfs_file_create(sub_dir,
+				"vsync_notifier_period", (u32 *)
+				&mfd->panel_info.lcd.vsync_notifier_period);
+				break;
+
+			case DISPLAY_LCDC:
+				msm_fb_debugfs_file_create(sub_dir,
+				"h_back_porch",
+				(u32 *) &mfd->panel_info.lcdc.h_back_porch);
+				msm_fb_debugfs_file_create(sub_dir,
+				"h_front_porch",
+				(u32 *) &mfd->panel_info.lcdc.h_front_porch);
+				msm_fb_debugfs_file_create(sub_dir,
+				"h_pulse_width",
+				(u32 *) &mfd->panel_info.lcdc.h_pulse_width);
+				msm_fb_debugfs_file_create(sub_dir,
+				"v_back_porch",
+				(u32 *) &mfd->panel_info.lcdc.v_back_porch);
+				msm_fb_debugfs_file_create(sub_dir,
+				"v_front_porch",
+				(u32 *) &mfd->panel_info.lcdc.v_front_porch);
+				msm_fb_debugfs_file_create(sub_dir,
+				"v_pulse_width",
+				(u32 *) &mfd->panel_info.lcdc.v_pulse_width);
+				msm_fb_debugfs_file_create(sub_dir,
+				"border_clr",
+				(u32 *) &mfd->panel_info.lcdc.border_clr);
+				msm_fb_debugfs_file_create(sub_dir,
+				"underflow_clr",
+				(u32 *) &mfd->panel_info.lcdc.underflow_clr);
+				msm_fb_debugfs_file_create(sub_dir,
+				"hsync_skew",
+				(u32 *) &mfd->panel_info.lcdc.hsync_skew);
+				break;
+
+			default:
+				break;
+			}
+		}
+	}
+#endif /* MSM_FB_ENABLE_DBGFS */
+
+	return ret;
+}
+
+static int msm_fb_open(struct fb_info *info, int user)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	int result;
+
+	result = pm_runtime_get_sync(info->dev);
+
+	if (result < 0) {
+		printk(KERN_ERR "pm_runtime: fail to wake up\n");
+	}
+
+	if (info->node == 0 && !(mfd->cont_splash_done)) {	/* primary */
+			mfd->ref_cnt++;
+			return 0;
+	}
+
+	if (!mfd->ref_cnt) {
+		if (!bf_supported ||
+			(info->node != 1 && info->node != 2))
+			mdp_set_dma_pan_info(info, NULL, TRUE);
+		else
+			pr_debug("%s:%d no mdp_set_dma_pan_info %d\n",
+				__func__, __LINE__, info->node);
+
+		if (msm_fb_blank_sub(FB_BLANK_UNBLANK, info, mfd->op_enable)) {
+			printk(KERN_ERR "msm_fb_open: can't turn on display!\n");
+			return -1;
+		}
+	}
+
+	mfd->ref_cnt++;
+	return 0;
+}
+
+static int msm_fb_release(struct fb_info *info, int user)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	int ret = 0;
+
+	if (!mfd->ref_cnt) {
+		MSM_FB_INFO("msm_fb_release: try to close unopened fb %d!\n",
+			    mfd->index);
+		return -EINVAL;
+	}
+
+	mfd->ref_cnt--;
+
+	if (!mfd->ref_cnt) {
+		if ((ret =
+		     msm_fb_blank_sub(FB_BLANK_POWERDOWN, info,
+				      mfd->op_enable)) != 0) {
+			printk(KERN_ERR "msm_fb_release: can't turn off display!\n");
+			return ret;
+		}
+	}
+
+	pm_runtime_put(info->dev);
+	return ret;
+}
+
+DEFINE_SEMAPHORE(msm_fb_pan_sem);
+
+static int msm_fb_pan_display(struct fb_var_screeninfo *var,
+			      struct fb_info *info)
+{
+	struct mdp_dirty_region dirty;
+	struct mdp_dirty_region *dirtyPtr = NULL;
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	struct msm_fb_panel_data *pdata;
+
+	/*
+	 * If framebuffer is 1 or 2, io pen display is not allowed.
+	 */
+	if (bf_supported &&
+		(info->node == 1 || info->node == 2)) {
+		pr_err("%s: no pan display for fb%d!",
+		       __func__, info->node);
+		return -EPERM;
+	}
+
+	if (info->node != 0 || mfd->cont_splash_done)	/* primary */
+		if ((!mfd->op_enable) || (!mfd->panel_power_on))
+			return -EPERM;
+
+	if (var->xoffset > (info->var.xres_virtual - info->var.xres))
+		return -EINVAL;
+
+	if (var->yoffset > (info->var.yres_virtual - info->var.yres))
+		return -EINVAL;
+
+	if (info->fix.xpanstep)
+		info->var.xoffset =
+		    (var->xoffset / info->fix.xpanstep) * info->fix.xpanstep;
+
+	if (info->fix.ypanstep)
+		info->var.yoffset =
+		    (var->yoffset / info->fix.ypanstep) * info->fix.ypanstep;
+
+	/* "UPDT" */
+	if (var->reserved[0] == 0x54445055) {
+
+		dirty.xoffset = var->reserved[1] & 0xffff;
+		dirty.yoffset = (var->reserved[1] >> 16) & 0xffff;
+
+		if ((var->reserved[2] & 0xffff) <= dirty.xoffset)
+			return -EINVAL;
+		if (((var->reserved[2] >> 16) & 0xffff) <= dirty.yoffset)
+			return -EINVAL;
+
+		dirty.width = (var->reserved[2] & 0xffff) - dirty.xoffset;
+		dirty.height =
+		    ((var->reserved[2] >> 16) & 0xffff) - dirty.yoffset;
+		info->var.yoffset = var->yoffset;
+
+		if (dirty.xoffset < 0)
+			return -EINVAL;
+
+		if (dirty.yoffset < 0)
+			return -EINVAL;
+
+		if ((dirty.xoffset + dirty.width) > info->var.xres)
+			return -EINVAL;
+
+		if ((dirty.yoffset + dirty.height) > info->var.yres)
+			return -EINVAL;
+
+		if ((dirty.width <= 0) || (dirty.height <= 0))
+			return -EINVAL;
+
+		dirtyPtr = &dirty;
+	}
+	complete(&mfd->msmfb_update_notify);
+	mutex_lock(&msm_fb_notify_update_sem);
+	if (mfd->msmfb_no_update_notify_timer.function)
+		del_timer(&mfd->msmfb_no_update_notify_timer);
+
+	mfd->msmfb_no_update_notify_timer.expires =
+				jiffies + ((1000 * HZ) / 1000);
+	add_timer(&mfd->msmfb_no_update_notify_timer);
+	mutex_unlock(&msm_fb_notify_update_sem);
+
+	down(&msm_fb_pan_sem);
+
+	if (info->node == 0 && !(mfd->cont_splash_done)) { /* primary */
+		mdp_set_dma_pan_info(info, NULL, TRUE);
+		if (msm_fb_blank_sub(FB_BLANK_UNBLANK, info, mfd->op_enable)) {
+			pr_err("%s: can't turn on display!\n", __func__);
+			return -EINVAL;
+		}
+	}
+
+	mdp_set_dma_pan_info(info, dirtyPtr,
+			     (var->activate == FB_ACTIVATE_VBL));
+	mdp_dma_pan_update(info);
+	up(&msm_fb_pan_sem);
+
+	if (unset_bl_level && !bl_updated) {
+		pdata = (struct msm_fb_panel_data *)mfd->pdev->
+			dev.platform_data;
+		if ((pdata) && (pdata->set_backlight)) {
+			down(&mfd->sem);
+			mfd->bl_level = unset_bl_level;
+			pdata->set_backlight(mfd);
+			bl_level_old = unset_bl_level;
+			up(&mfd->sem);
+			bl_updated = 1;
+		}
+	}
+
+	++mfd->panel_info.frame_count;
+	return 0;
+}
+
+static int msm_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+
+	if (var->rotate != FB_ROTATE_UR)
+		return -EINVAL;
+	if (var->grayscale != info->var.grayscale)
+		return -EINVAL;
+
+	switch (var->bits_per_pixel) {
+	case 16:
+		if ((var->green.offset != 5) ||
+			!((var->blue.offset == 11)
+				|| (var->blue.offset == 0)) ||
+			!((var->red.offset == 11)
+				|| (var->red.offset == 0)) ||
+			(var->blue.length != 5) ||
+			(var->green.length != 6) ||
+			(var->red.length != 5) ||
+			(var->blue.msb_right != 0) ||
+			(var->green.msb_right != 0) ||
+			(var->red.msb_right != 0) ||
+			(var->transp.offset != 0) ||
+			(var->transp.length != 0))
+				return -EINVAL;
+		break;
+
+	case 24:
+		if ((var->blue.offset != 0) ||
+			(var->green.offset != 8) ||
+			(var->red.offset != 16) ||
+			(var->blue.length != 8) ||
+			(var->green.length != 8) ||
+			(var->red.length != 8) ||
+			(var->blue.msb_right != 0) ||
+			(var->green.msb_right != 0) ||
+			(var->red.msb_right != 0) ||
+			!(((var->transp.offset == 0) &&
+				(var->transp.length == 0)) ||
+			  ((var->transp.offset == 24) &&
+				(var->transp.length == 8))))
+				return -EINVAL;
+		break;
+
+	case 32:
+		/* Figure out if the user meant RGBA or ARGB
+		   and verify the position of the RGB components */
+
+		if (var->transp.offset == 24) {
+			if ((var->blue.offset != 0) ||
+			    (var->green.offset != 8) ||
+			    (var->red.offset != 16))
+				return -EINVAL;
+		} else if (var->transp.offset == 0) {
+			if ((var->blue.offset != 8) ||
+			    (var->green.offset != 16) ||
+			    (var->red.offset != 24))
+				return -EINVAL;
+		} else
+			return -EINVAL;
+
+		/* Check the common values for both RGBA and ARGB */
+
+		if ((var->blue.length != 8) ||
+		    (var->green.length != 8) ||
+		    (var->red.length != 8) ||
+		    (var->transp.length != 8) ||
+		    (var->blue.msb_right != 0) ||
+		    (var->green.msb_right != 0) ||
+		    (var->red.msb_right != 0))
+			return -EINVAL;
+
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	if ((var->xres_virtual <= 0) || (var->yres_virtual <= 0))
+		return -EINVAL;
+
+	if (!bf_supported ||
+		(info->node != 1 && info->node != 2))
+		if (info->fix.smem_len <
+		    (var->xres_virtual*
+		     var->yres_virtual*
+		     (var->bits_per_pixel/8)))
+			return -EINVAL;
+
+	if ((var->xres == 0) || (var->yres == 0))
+		return -EINVAL;
+
+	if ((var->xres > MAX(mfd->panel_info.xres,
+			     mfd->panel_info.mode2_xres)) ||
+		(var->yres > MAX(mfd->panel_info.yres,
+				 mfd->panel_info.mode2_yres)))
+		return -EINVAL;
+
+	if (var->xoffset > (var->xres_virtual - var->xres))
+		return -EINVAL;
+
+	if (var->yoffset > (var->yres_virtual - var->yres))
+		return -EINVAL;
+
+	return 0;
+}
+
+int msm_fb_check_frame_rate(struct msm_fb_data_type *mfd
+						, struct fb_info *info)
+{
+	int panel_height, panel_width, var_frame_rate, fps_mod;
+	struct fb_var_screeninfo *var = &info->var;
+	fps_mod = 0;
+	if ((mfd->panel_info.type == DTV_PANEL) ||
+		(mfd->panel_info.type == HDMI_PANEL)) {
+		panel_height = var->yres + var->upper_margin +
+			var->vsync_len + var->lower_margin;
+		panel_width = var->xres + var->right_margin +
+			var->hsync_len + var->left_margin;
+		var_frame_rate = ((var->pixclock)/(panel_height * panel_width));
+		if (mfd->var_frame_rate != var_frame_rate) {
+			fps_mod = 1;
+			mfd->var_frame_rate = var_frame_rate;
+		}
+	}
+	return fps_mod;
+}
+
+static int msm_fb_set_par(struct fb_info *info)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	struct fb_var_screeninfo *var = &info->var;
+	int old_imgType;
+	int blank = 0;
+
+	old_imgType = mfd->fb_imgType;
+	switch (var->bits_per_pixel) {
+	case 16:
+		if (var->red.offset == 0)
+			mfd->fb_imgType = MDP_BGR_565;
+		else
+			mfd->fb_imgType = MDP_RGB_565;
+		break;
+
+	case 24:
+		if ((var->transp.offset == 0) && (var->transp.length == 0))
+			mfd->fb_imgType = MDP_RGB_888;
+		else if ((var->transp.offset == 24) &&
+				(var->transp.length == 8)) {
+			mfd->fb_imgType = MDP_ARGB_8888;
+			info->var.bits_per_pixel = 32;
+		}
+		break;
+
+	case 32:
+		if (var->transp.offset == 24)
+			mfd->fb_imgType = MDP_ARGB_8888;
+		else
+			mfd->fb_imgType = MDP_RGBA_8888;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	if ((mfd->var_pixclock != var->pixclock) ||
+		(mfd->hw_refresh && ((mfd->fb_imgType != old_imgType) ||
+				(mfd->var_pixclock != var->pixclock) ||
+				(mfd->var_xres != var->xres) ||
+				(mfd->var_yres != var->yres) ||
+				(msm_fb_check_frame_rate(mfd, info))))) {
+		mfd->var_xres = var->xres;
+		mfd->var_yres = var->yres;
+		mfd->var_pixclock = var->pixclock;
+		blank = 1;
+	}
+	mfd->fbi->fix.line_length = msm_fb_line_length(mfd->index, var->xres,
+						       var->bits_per_pixel/8);
+
+	if (blank) {
+		msm_fb_blank_sub(FB_BLANK_POWERDOWN, info, mfd->op_enable);
+		msm_fb_blank_sub(FB_BLANK_UNBLANK, info, mfd->op_enable);
+	}
+
+	return 0;
+}
+
+static int msm_fb_stop_sw_refresher(struct msm_fb_data_type *mfd)
+{
+	if (mfd->hw_refresh)
+		return -EPERM;
+
+	if (mfd->sw_currently_refreshing) {
+		down(&mfd->sem);
+		mfd->sw_currently_refreshing = FALSE;
+		up(&mfd->sem);
+
+		/* wait until the refresher finishes the last job */
+		wait_for_completion_killable(&mfd->refresher_comp);
+	}
+
+	return 0;
+}
+
+int msm_fb_resume_sw_refresher(struct msm_fb_data_type *mfd)
+{
+	boolean do_refresh;
+
+	if (mfd->hw_refresh)
+		return -EPERM;
+
+	down(&mfd->sem);
+	if ((!mfd->sw_currently_refreshing) && (mfd->sw_refreshing_enable)) {
+		do_refresh = TRUE;
+		mfd->sw_currently_refreshing = TRUE;
+	} else {
+		do_refresh = FALSE;
+	}
+	up(&mfd->sem);
+
+	if (do_refresh)
+		mdp_refresh_screen((unsigned long)mfd);
+
+	return 0;
+}
+
+#if defined CONFIG_FB_MSM_MDP31
+static int mdp_blit_split_height(struct fb_info *info,
+				struct mdp_blit_req *req)
+{
+	int ret;
+	struct mdp_blit_req splitreq;
+	int s_x_0, s_x_1, s_w_0, s_w_1, s_y_0, s_y_1, s_h_0, s_h_1;
+	int d_x_0, d_x_1, d_w_0, d_w_1, d_y_0, d_y_1, d_h_0, d_h_1;
+
+	splitreq = *req;
+	/* break dest roi at height*/
+	d_x_0 = d_x_1 = req->dst_rect.x;
+	d_w_0 = d_w_1 = req->dst_rect.w;
+	d_y_0 = req->dst_rect.y;
+	if (req->dst_rect.h % 32 == 3)
+		d_h_1 = (req->dst_rect.h - 3) / 2 - 1;
+	else if (req->dst_rect.h % 32 == 2)
+		d_h_1 = (req->dst_rect.h - 2) / 2 - 6;
+	else
+		d_h_1 = (req->dst_rect.h - 1) / 2 - 1;
+	d_h_0 = req->dst_rect.h - d_h_1;
+	d_y_1 = d_y_0 + d_h_0;
+	if (req->dst_rect.h == 3) {
+		d_h_1 = 2;
+		d_h_0 = 2;
+		d_y_1 = d_y_0 + 1;
+	}
+
+	/* blit first region */
+	if (((splitreq.flags & 0x07) == 0x04) ||
+		((splitreq.flags & 0x07) == 0x0)) {
+
+		if (splitreq.flags & MDP_ROT_90) {
+			s_y_0 = s_y_1 = req->src_rect.y;
+			s_h_0 = s_h_1 = req->src_rect.h;
+			s_x_0 = req->src_rect.x;
+			s_w_1 = (req->src_rect.w * d_h_1) / req->dst_rect.h;
+			s_w_0 = req->src_rect.w - s_w_1;
+			s_x_1 = s_x_0 + s_w_0;
+			if (d_h_1 >= 8 * s_w_1) {
+				s_w_1++;
+				s_x_1--;
+			}
+		} else {
+			s_x_0 = s_x_1 = req->src_rect.x;
+			s_w_0 = s_w_1 = req->src_rect.w;
+			s_y_0 = req->src_rect.y;
+			s_h_1 = (req->src_rect.h * d_h_1) / req->dst_rect.h;
+			s_h_0 = req->src_rect.h - s_h_1;
+			s_y_1 = s_y_0 + s_h_0;
+			if (d_h_1 >= 8 * s_h_1) {
+				s_h_1++;
+				s_y_1--;
+			}
+		}
+
+		splitreq.src_rect.h = s_h_0;
+		splitreq.src_rect.y = s_y_0;
+		splitreq.dst_rect.h = d_h_0;
+		splitreq.dst_rect.y = d_y_0;
+		splitreq.src_rect.x = s_x_0;
+		splitreq.src_rect.w = s_w_0;
+		splitreq.dst_rect.x = d_x_0;
+		splitreq.dst_rect.w = d_w_0;
+	} else {
+
+		if (splitreq.flags & MDP_ROT_90) {
+			s_y_0 = s_y_1 = req->src_rect.y;
+			s_h_0 = s_h_1 = req->src_rect.h;
+			s_x_0 = req->src_rect.x;
+			s_w_1 = (req->src_rect.w * d_h_0) / req->dst_rect.h;
+			s_w_0 = req->src_rect.w - s_w_1;
+			s_x_1 = s_x_0 + s_w_0;
+			if (d_h_0 >= 8 * s_w_1) {
+				s_w_1++;
+				s_x_1--;
+			}
+		} else {
+			s_x_0 = s_x_1 = req->src_rect.x;
+			s_w_0 = s_w_1 = req->src_rect.w;
+			s_y_0 = req->src_rect.y;
+			s_h_1 = (req->src_rect.h * d_h_0) / req->dst_rect.h;
+			s_h_0 = req->src_rect.h - s_h_1;
+			s_y_1 = s_y_0 + s_h_0;
+			if (d_h_0 >= 8 * s_h_1) {
+				s_h_1++;
+				s_y_1--;
+			}
+		}
+		splitreq.src_rect.h = s_h_0;
+		splitreq.src_rect.y = s_y_0;
+		splitreq.dst_rect.h = d_h_1;
+		splitreq.dst_rect.y = d_y_1;
+		splitreq.src_rect.x = s_x_0;
+		splitreq.src_rect.w = s_w_0;
+		splitreq.dst_rect.x = d_x_1;
+		splitreq.dst_rect.w = d_w_1;
+	}
+	ret = mdp_ppp_blit(info, &splitreq);
+	if (ret)
+		return ret;
+
+	/* blit second region */
+	if (((splitreq.flags & 0x07) == 0x04) ||
+		((splitreq.flags & 0x07) == 0x0)) {
+		splitreq.src_rect.h = s_h_1;
+		splitreq.src_rect.y = s_y_1;
+		splitreq.dst_rect.h = d_h_1;
+		splitreq.dst_rect.y = d_y_1;
+		splitreq.src_rect.x = s_x_1;
+		splitreq.src_rect.w = s_w_1;
+		splitreq.dst_rect.x = d_x_1;
+		splitreq.dst_rect.w = d_w_1;
+	} else {
+		splitreq.src_rect.h = s_h_1;
+		splitreq.src_rect.y = s_y_1;
+		splitreq.dst_rect.h = d_h_0;
+		splitreq.dst_rect.y = d_y_0;
+		splitreq.src_rect.x = s_x_1;
+		splitreq.src_rect.w = s_w_1;
+		splitreq.dst_rect.x = d_x_0;
+		splitreq.dst_rect.w = d_w_0;
+	}
+	ret = mdp_ppp_blit(info, &splitreq);
+	return ret;
+}
+#endif
+
+int mdp_blit(struct fb_info *info, struct mdp_blit_req *req)
+{
+	int ret;
+#if defined CONFIG_FB_MSM_MDP31 || defined CONFIG_FB_MSM_MDP30
+	unsigned int remainder = 0, is_bpp_4 = 0;
+	struct mdp_blit_req splitreq;
+	int s_x_0, s_x_1, s_w_0, s_w_1, s_y_0, s_y_1, s_h_0, s_h_1;
+	int d_x_0, d_x_1, d_w_0, d_w_1, d_y_0, d_y_1, d_h_0, d_h_1;
+
+	if (req->flags & MDP_ROT_90) {
+		if (((req->dst_rect.h == 1) && ((req->src_rect.w != 1) ||
+			(req->dst_rect.w != req->src_rect.h))) ||
+			((req->dst_rect.w == 1) && ((req->src_rect.h != 1) ||
+			(req->dst_rect.h != req->src_rect.w)))) {
+			printk(KERN_ERR "mpd_ppp: error scaling when size is 1!\n");
+			return -EINVAL;
+		}
+	} else {
+		if (((req->dst_rect.w == 1) && ((req->src_rect.w != 1) ||
+			(req->dst_rect.h != req->src_rect.h))) ||
+			((req->dst_rect.h == 1) && ((req->src_rect.h != 1) ||
+			(req->dst_rect.w != req->src_rect.w)))) {
+			printk(KERN_ERR "mpd_ppp: error scaling when size is 1!\n");
+			return -EINVAL;
+		}
+	}
+#endif
+	if (unlikely(req->src_rect.h == 0 || req->src_rect.w == 0)) {
+		printk(KERN_ERR "mpd_ppp: src img of zero size!\n");
+		return -EINVAL;
+	}
+	if (unlikely(req->dst_rect.h == 0 || req->dst_rect.w == 0))
+		return 0;
+
+#if defined CONFIG_FB_MSM_MDP31
+	/* MDP width split workaround */
+	remainder = (req->dst_rect.w)%32;
+	ret = mdp_get_bytes_per_pixel(req->dst.format,
+					(struct msm_fb_data_type *)info->par);
+	if (ret <= 0) {
+		printk(KERN_ERR "mdp_ppp: incorrect bpp!\n");
+		return -EINVAL;
+	}
+	is_bpp_4 = (ret == 4) ? 1 : 0;
+
+	if ((is_bpp_4 && (remainder == 6 || remainder == 14 ||
+	remainder == 22 || remainder == 30)) || remainder == 3 ||
+	(remainder == 1 && req->dst_rect.w != 1) ||
+	(remainder == 2 && req->dst_rect.w != 2)) {
+		/* make new request as provide by user */
+		splitreq = *req;
+
+		/* break dest roi at width*/
+		d_y_0 = d_y_1 = req->dst_rect.y;
+		d_h_0 = d_h_1 = req->dst_rect.h;
+		d_x_0 = req->dst_rect.x;
+
+		if (remainder == 14)
+			d_w_1 = (req->dst_rect.w - 14) / 2 + 4;
+		else if (remainder == 22)
+			d_w_1 = (req->dst_rect.w - 22) / 2 + 10;
+		else if (remainder == 30)
+			d_w_1 = (req->dst_rect.w - 30) / 2 + 10;
+		else if (remainder == 6)
+			d_w_1 = req->dst_rect.w / 2 - 1;
+		else if (remainder == 3)
+			d_w_1 = (req->dst_rect.w - 3) / 2 - 1;
+		else if (remainder == 2)
+			d_w_1 = (req->dst_rect.w - 2) / 2 - 6;
+		else
+			d_w_1 = (req->dst_rect.w - 1) / 2 - 1;
+		d_w_0 = req->dst_rect.w - d_w_1;
+		d_x_1 = d_x_0 + d_w_0;
+		if (req->dst_rect.w == 3) {
+			d_w_1 = 2;
+			d_w_0 = 2;
+			d_x_1 = d_x_0 + 1;
+		}
+
+		/* blit first region */
+		if (((splitreq.flags & 0x07) == 0x07) ||
+			((splitreq.flags & 0x07) == 0x0)) {
+
+			if (splitreq.flags & MDP_ROT_90) {
+				s_x_0 = s_x_1 = req->src_rect.x;
+				s_w_0 = s_w_1 = req->src_rect.w;
+				s_y_0 = req->src_rect.y;
+				s_h_1 = (req->src_rect.h * d_w_1) /
+					req->dst_rect.w;
+				s_h_0 = req->src_rect.h - s_h_1;
+				s_y_1 = s_y_0 + s_h_0;
+				if (d_w_1 >= 8 * s_h_1) {
+					s_h_1++;
+					s_y_1--;
+				}
+			} else {
+				s_y_0 = s_y_1 = req->src_rect.y;
+				s_h_0 = s_h_1 = req->src_rect.h;
+				s_x_0 = req->src_rect.x;
+				s_w_1 = (req->src_rect.w * d_w_1) /
+					req->dst_rect.w;
+				s_w_0 = req->src_rect.w - s_w_1;
+				s_x_1 = s_x_0 + s_w_0;
+				if (d_w_1 >= 8 * s_w_1) {
+					s_w_1++;
+					s_x_1--;
+				}
+			}
+
+			splitreq.src_rect.h = s_h_0;
+			splitreq.src_rect.y = s_y_0;
+			splitreq.dst_rect.h = d_h_0;
+			splitreq.dst_rect.y = d_y_0;
+			splitreq.src_rect.x = s_x_0;
+			splitreq.src_rect.w = s_w_0;
+			splitreq.dst_rect.x = d_x_0;
+			splitreq.dst_rect.w = d_w_0;
+		} else {
+			if (splitreq.flags & MDP_ROT_90) {
+				s_x_0 = s_x_1 = req->src_rect.x;
+				s_w_0 = s_w_1 = req->src_rect.w;
+				s_y_0 = req->src_rect.y;
+				s_h_1 = (req->src_rect.h * d_w_0) /
+					req->dst_rect.w;
+				s_h_0 = req->src_rect.h - s_h_1;
+				s_y_1 = s_y_0 + s_h_0;
+				if (d_w_0 >= 8 * s_h_1) {
+					s_h_1++;
+					s_y_1--;
+				}
+			} else {
+				s_y_0 = s_y_1 = req->src_rect.y;
+				s_h_0 = s_h_1 = req->src_rect.h;
+				s_x_0 = req->src_rect.x;
+				s_w_1 = (req->src_rect.w * d_w_0) /
+					req->dst_rect.w;
+				s_w_0 = req->src_rect.w - s_w_1;
+				s_x_1 = s_x_0 + s_w_0;
+				if (d_w_0 >= 8 * s_w_1) {
+					s_w_1++;
+					s_x_1--;
+				}
+			}
+			splitreq.src_rect.h = s_h_0;
+			splitreq.src_rect.y = s_y_0;
+			splitreq.dst_rect.h = d_h_1;
+			splitreq.dst_rect.y = d_y_1;
+			splitreq.src_rect.x = s_x_0;
+			splitreq.src_rect.w = s_w_0;
+			splitreq.dst_rect.x = d_x_1;
+			splitreq.dst_rect.w = d_w_1;
+		}
+
+		if ((splitreq.dst_rect.h % 32 == 3) ||
+			((req->dst_rect.h % 32) == 1 && req->dst_rect.h != 1) ||
+			((req->dst_rect.h % 32) == 2 && req->dst_rect.h != 2))
+			ret = mdp_blit_split_height(info, &splitreq);
+		else
+			ret = mdp_ppp_blit(info, &splitreq);
+		if (ret)
+			return ret;
+		/* blit second region */
+		if (((splitreq.flags & 0x07) == 0x07) ||
+			((splitreq.flags & 0x07) == 0x0)) {
+			splitreq.src_rect.h = s_h_1;
+			splitreq.src_rect.y = s_y_1;
+			splitreq.dst_rect.h = d_h_1;
+			splitreq.dst_rect.y = d_y_1;
+			splitreq.src_rect.x = s_x_1;
+			splitreq.src_rect.w = s_w_1;
+			splitreq.dst_rect.x = d_x_1;
+			splitreq.dst_rect.w = d_w_1;
+		} else {
+			splitreq.src_rect.h = s_h_1;
+			splitreq.src_rect.y = s_y_1;
+			splitreq.dst_rect.h = d_h_0;
+			splitreq.dst_rect.y = d_y_0;
+			splitreq.src_rect.x = s_x_1;
+			splitreq.src_rect.w = s_w_1;
+			splitreq.dst_rect.x = d_x_0;
+			splitreq.dst_rect.w = d_w_0;
+		}
+		if (((splitreq.dst_rect.h % 32) == 3) ||
+			((req->dst_rect.h % 32) == 1 && req->dst_rect.h != 1) ||
+			((req->dst_rect.h % 32) == 2 && req->dst_rect.h != 2))
+			ret = mdp_blit_split_height(info, &splitreq);
+		else
+			ret = mdp_ppp_blit(info, &splitreq);
+		if (ret)
+			return ret;
+	} else if ((req->dst_rect.h % 32) == 3 ||
+		((req->dst_rect.h % 32) == 1 && req->dst_rect.h != 1) ||
+		((req->dst_rect.h % 32) == 2 && req->dst_rect.h != 2))
+		ret = mdp_blit_split_height(info, req);
+	else
+		ret = mdp_ppp_blit(info, req);
+	return ret;
+#elif defined CONFIG_FB_MSM_MDP30
+	/* MDP width split workaround */
+	remainder = (req->dst_rect.w)%16;
+	ret = mdp_get_bytes_per_pixel(req->dst.format,
+					(struct msm_fb_data_type *)info->par);
+	if (ret <= 0) {
+		printk(KERN_ERR "mdp_ppp: incorrect bpp!\n");
+		return -EINVAL;
+	}
+	is_bpp_4 = (ret == 4) ? 1 : 0;
+
+	if ((is_bpp_4 && (remainder == 6 || remainder == 14))) {
+
+		/* make new request as provide by user */
+		splitreq = *req;
+
+		/* break dest roi at width*/
+		d_y_0 = d_y_1 = req->dst_rect.y;
+		d_h_0 = d_h_1 = req->dst_rect.h;
+		d_x_0 = req->dst_rect.x;
+
+		if (remainder == 14 || remainder == 6)
+			d_w_1 = req->dst_rect.w / 2;
+		else
+			d_w_1 = (req->dst_rect.w - 1) / 2 - 1;
+
+		d_w_0 = req->dst_rect.w - d_w_1;
+		d_x_1 = d_x_0 + d_w_0;
+
+		/* blit first region */
+		if (((splitreq.flags & 0x07) == 0x07) ||
+			((splitreq.flags & 0x07) == 0x05) ||
+			((splitreq.flags & 0x07) == 0x02) ||
+			((splitreq.flags & 0x07) == 0x0)) {
+
+			if (splitreq.flags & MDP_ROT_90) {
+				s_x_0 = s_x_1 = req->src_rect.x;
+				s_w_0 = s_w_1 = req->src_rect.w;
+				s_y_0 = req->src_rect.y;
+				s_h_1 = (req->src_rect.h * d_w_1) /
+					req->dst_rect.w;
+				s_h_0 = req->src_rect.h - s_h_1;
+				s_y_1 = s_y_0 + s_h_0;
+				if (d_w_1 >= 8 * s_h_1) {
+					s_h_1++;
+					s_y_1--;
+				}
+			} else {
+				s_y_0 = s_y_1 = req->src_rect.y;
+				s_h_0 = s_h_1 = req->src_rect.h;
+				s_x_0 = req->src_rect.x;
+				s_w_1 = (req->src_rect.w * d_w_1) /
+					req->dst_rect.w;
+				s_w_0 = req->src_rect.w - s_w_1;
+				s_x_1 = s_x_0 + s_w_0;
+				if (d_w_1 >= 8 * s_w_1) {
+					s_w_1++;
+					s_x_1--;
+				}
+			}
+
+			splitreq.src_rect.h = s_h_0;
+			splitreq.src_rect.y = s_y_0;
+			splitreq.dst_rect.h = d_h_0;
+			splitreq.dst_rect.y = d_y_0;
+			splitreq.src_rect.x = s_x_0;
+			splitreq.src_rect.w = s_w_0;
+			splitreq.dst_rect.x = d_x_0;
+			splitreq.dst_rect.w = d_w_0;
+		} else {
+			if (splitreq.flags & MDP_ROT_90) {
+				s_x_0 = s_x_1 = req->src_rect.x;
+				s_w_0 = s_w_1 = req->src_rect.w;
+				s_y_0 = req->src_rect.y;
+				s_h_1 = (req->src_rect.h * d_w_0) /
+					req->dst_rect.w;
+				s_h_0 = req->src_rect.h - s_h_1;
+				s_y_1 = s_y_0 + s_h_0;
+				if (d_w_0 >= 8 * s_h_1) {
+					s_h_1++;
+					s_y_1--;
+				}
+			} else {
+				s_y_0 = s_y_1 = req->src_rect.y;
+				s_h_0 = s_h_1 = req->src_rect.h;
+				s_x_0 = req->src_rect.x;
+				s_w_1 = (req->src_rect.w * d_w_0) /
+					req->dst_rect.w;
+				s_w_0 = req->src_rect.w - s_w_1;
+				s_x_1 = s_x_0 + s_w_0;
+				if (d_w_0 >= 8 * s_w_1) {
+					s_w_1++;
+					s_x_1--;
+				}
+			}
+			splitreq.src_rect.h = s_h_0;
+			splitreq.src_rect.y = s_y_0;
+			splitreq.dst_rect.h = d_h_1;
+			splitreq.dst_rect.y = d_y_1;
+			splitreq.src_rect.x = s_x_0;
+			splitreq.src_rect.w = s_w_0;
+			splitreq.dst_rect.x = d_x_1;
+			splitreq.dst_rect.w = d_w_1;
+		}
+
+		/* No need to split in height */
+		ret = mdp_ppp_blit(info, &splitreq);
+
+		if (ret)
+			return ret;
+
+		/* blit second region */
+		if (((splitreq.flags & 0x07) == 0x07) ||
+			((splitreq.flags & 0x07) == 0x05) ||
+			((splitreq.flags & 0x07) == 0x02) ||
+			((splitreq.flags & 0x07) == 0x0)) {
+			splitreq.src_rect.h = s_h_1;
+			splitreq.src_rect.y = s_y_1;
+			splitreq.dst_rect.h = d_h_1;
+			splitreq.dst_rect.y = d_y_1;
+			splitreq.src_rect.x = s_x_1;
+			splitreq.src_rect.w = s_w_1;
+			splitreq.dst_rect.x = d_x_1;
+			splitreq.dst_rect.w = d_w_1;
+		} else {
+			splitreq.src_rect.h = s_h_1;
+			splitreq.src_rect.y = s_y_1;
+			splitreq.dst_rect.h = d_h_0;
+			splitreq.dst_rect.y = d_y_0;
+			splitreq.src_rect.x = s_x_1;
+			splitreq.src_rect.w = s_w_1;
+			splitreq.dst_rect.x = d_x_0;
+			splitreq.dst_rect.w = d_w_0;
+		}
+
+		/* No need to split in height ... just width */
+		ret = mdp_ppp_blit(info, &splitreq);
+
+		if (ret)
+			return ret;
+
+	} else
+		ret = mdp_ppp_blit(info, req);
+	return ret;
+#else
+	ret = mdp_ppp_blit(info, req);
+	return ret;
+#endif
+}
+
+typedef void (*msm_dma_barrier_function_pointer) (void *, size_t);
+
+static inline void msm_fb_dma_barrier_for_rect(struct fb_info *info,
+			struct mdp_img *img, struct mdp_rect *rect,
+			msm_dma_barrier_function_pointer dma_barrier_fp
+			)
+{
+	/*
+	 * Compute the start and end addresses of the rectangles.
+	 * NOTE: As currently implemented, the data between
+	 *       the end of one row and the start of the next is
+	 *       included in the address range rather than
+	 *       doing multiple calls for each row.
+	 */
+	unsigned long start;
+	size_t size;
+	char * const pmem_start = info->screen_base;
+	int bytes_per_pixel = mdp_get_bytes_per_pixel(img->format,
+					(struct msm_fb_data_type *)info->par);
+	if (bytes_per_pixel <= 0) {
+		printk(KERN_ERR "%s incorrect bpp!\n", __func__);
+		return;
+	}
+	start = (unsigned long)pmem_start + img->offset +
+		(img->width * rect->y + rect->x) * bytes_per_pixel;
+	size  = (rect->h * img->width + rect->w) * bytes_per_pixel;
+	(*dma_barrier_fp) ((void *) start, size);
+
+}
+
+static inline void msm_dma_nc_pre(void)
+{
+	dmb();
+}
+static inline void msm_dma_wt_pre(void)
+{
+	dmb();
+}
+static inline void msm_dma_todevice_wb_pre(void *start, size_t size)
+{
+	dma_cache_pre_ops(start, size, DMA_TO_DEVICE);
+}
+
+static inline void msm_dma_fromdevice_wb_pre(void *start, size_t size)
+{
+	dma_cache_pre_ops(start, size, DMA_FROM_DEVICE);
+}
+
+static inline void msm_dma_nc_post(void)
+{
+	dmb();
+}
+
+static inline void msm_dma_fromdevice_wt_post(void *start, size_t size)
+{
+	dma_cache_post_ops(start, size, DMA_FROM_DEVICE);
+}
+
+static inline void msm_dma_todevice_wb_post(void *start, size_t size)
+{
+	dma_cache_post_ops(start, size, DMA_TO_DEVICE);
+}
+
+static inline void msm_dma_fromdevice_wb_post(void *start, size_t size)
+{
+	dma_cache_post_ops(start, size, DMA_FROM_DEVICE);
+}
+
+/*
+ * Do the write barriers required to guarantee data is committed to RAM
+ * (from CPU cache or internal buffers) before a DMA operation starts.
+ * NOTE: As currently implemented, the data between
+ *       the end of one row and the start of the next is
+ *       included in the address range rather than
+ *       doing multiple calls for each row.
+*/
+static void msm_fb_ensure_memory_coherency_before_dma(struct fb_info *info,
+		struct mdp_blit_req *req_list,
+		int req_list_count)
+{
+#ifdef CONFIG_ARCH_QSD8X50
+	int i;
+
+	/*
+	 * Normally, do the requested barriers for each address
+	 * range that corresponds to a rectangle.
+	 *
+	 * But if at least one write barrier is requested for data
+	 * going to or from the device but no address range is
+	 * needed for that barrier, then do the barrier, but do it
+	 * only once, no matter how many requests there are.
+	 */
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	switch (mfd->mdp_fb_page_protection)	{
+	default:
+	case MDP_FB_PAGE_PROTECTION_NONCACHED:
+	case MDP_FB_PAGE_PROTECTION_WRITECOMBINE:
+		/*
+		 * The following barrier is only done at most once,
+		 * since further calls would be redundant.
+		 */
+		for (i = 0; i < req_list_count; i++) {
+			if (!(req_list[i].flags
+				& MDP_NO_DMA_BARRIER_START)) {
+				msm_dma_nc_pre();
+				break;
+			}
+		}
+		break;
+
+	case MDP_FB_PAGE_PROTECTION_WRITETHROUGHCACHE:
+		/*
+		 * The following barrier is only done at most once,
+		 * since further calls would be redundant.
+		 */
+		for (i = 0; i < req_list_count; i++) {
+			if (!(req_list[i].flags
+				& MDP_NO_DMA_BARRIER_START)) {
+				msm_dma_wt_pre();
+				break;
+			}
+		}
+		break;
+
+	case MDP_FB_PAGE_PROTECTION_WRITEBACKCACHE:
+	case MDP_FB_PAGE_PROTECTION_WRITEBACKWACACHE:
+		for (i = 0; i < req_list_count; i++) {
+			if (!(req_list[i].flags &
+					MDP_NO_DMA_BARRIER_START)) {
+
+				msm_fb_dma_barrier_for_rect(info,
+						&(req_list[i].src),
+						&(req_list[i].src_rect),
+						msm_dma_todevice_wb_pre
+						);
+
+				msm_fb_dma_barrier_for_rect(info,
+						&(req_list[i].dst),
+						&(req_list[i].dst_rect),
+						msm_dma_todevice_wb_pre
+						);
+			}
+		}
+		break;
+	}
+#else
+	dmb();
+#endif
+}
+
+
+/*
+ * Do the write barriers required to guarantee data will be re-read from RAM by
+ * the CPU after a DMA operation ends.
+ * NOTE: As currently implemented, the data between
+ *       the end of one row and the start of the next is
+ *       included in the address range rather than
+ *       doing multiple calls for each row.
+*/
+static void msm_fb_ensure_memory_coherency_after_dma(struct fb_info *info,
+		struct mdp_blit_req *req_list,
+		int req_list_count)
+{
+#ifdef CONFIG_ARCH_QSD8X50
+	int i;
+
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	switch (mfd->mdp_fb_page_protection)	{
+	default:
+	case MDP_FB_PAGE_PROTECTION_NONCACHED:
+	case MDP_FB_PAGE_PROTECTION_WRITECOMBINE:
+		/*
+		 * The following barrier is only done at most once,
+		 * since further calls would be redundant.
+		 */
+		for (i = 0; i < req_list_count; i++) {
+			if (!(req_list[i].flags
+				& MDP_NO_DMA_BARRIER_END)) {
+				msm_dma_nc_post();
+				break;
+			}
+		}
+		break;
+
+	case MDP_FB_PAGE_PROTECTION_WRITETHROUGHCACHE:
+		for (i = 0; i < req_list_count; i++) {
+			if (!(req_list[i].flags &
+					MDP_NO_DMA_BARRIER_END)) {
+
+				msm_fb_dma_barrier_for_rect(info,
+						&(req_list[i].dst),
+						&(req_list[i].dst_rect),
+						msm_dma_fromdevice_wt_post
+						);
+			}
+		}
+		break;
+	case MDP_FB_PAGE_PROTECTION_WRITEBACKCACHE:
+	case MDP_FB_PAGE_PROTECTION_WRITEBACKWACACHE:
+		for (i = 0; i < req_list_count; i++) {
+			if (!(req_list[i].flags &
+					MDP_NO_DMA_BARRIER_END)) {
+
+				msm_fb_dma_barrier_for_rect(info,
+						&(req_list[i].dst),
+						&(req_list[i].dst_rect),
+						msm_dma_fromdevice_wb_post
+						);
+			}
+		}
+		break;
+	}
+#else
+	dmb();
+#endif
+}
+
+/*
+ * NOTE: The userspace issues blit operations in a sequence, the sequence
+ * start with a operation marked START and ends in an operation marked
+ * END. It is guranteed by the userspace that all the blit operations
+ * between START and END are only within the regions of areas designated
+ * by the START and END operations and that the userspace doesnt modify
+ * those areas. Hence it would be enough to perform barrier/cache operations
+ * only on the START and END operations.
+ */
+static int msmfb_blit(struct fb_info *info, void __user *p)
+{
+	/*
+	 * CAUTION: The names of the struct types intentionally *DON'T* match
+	 * the names of the variables declared -- they appear to be swapped.
+	 * Read the code carefully and you should see that the variable names
+	 * make sense.
+	 */
+	const int MAX_LIST_WINDOW = 16;
+	struct mdp_blit_req req_list[MAX_LIST_WINDOW];
+	struct mdp_blit_req_list req_list_header;
+
+	int count, i, req_list_count;
+	if (bf_supported &&
+		(info->node == 1 || info->node == 2)) {
+		pr_err("%s: no pan display for fb%d.",
+		       __func__, info->node);
+		return -EPERM;
+	}
+	/* Get the count size for the total BLIT request. */
+	if (copy_from_user(&req_list_header, p, sizeof(req_list_header)))
+		return -EFAULT;
+	p += sizeof(req_list_header);
+	count = req_list_header.count;
+	if (count < 0 || count >= MAX_BLIT_REQ)
+		return -EINVAL;
+	while (count > 0) {
+		/*
+		 * Access the requests through a narrow window to decrease copy
+		 * overhead and make larger requests accessible to the
+		 * coherency management code.
+		 * NOTE: The window size is intended to be larger than the
+		 *       typical request size, but not require more than 2
+		 *       kbytes of stack storage.
+		 */
+		req_list_count = count;
+		if (req_list_count > MAX_LIST_WINDOW)
+			req_list_count = MAX_LIST_WINDOW;
+		if (copy_from_user(&req_list, p,
+				sizeof(struct mdp_blit_req)*req_list_count))
+			return -EFAULT;
+
+		/*
+		 * Ensure that any data CPU may have previously written to
+		 * internal state (but not yet committed to memory) is
+		 * guaranteed to be committed to memory now.
+		 */
+		msm_fb_ensure_memory_coherency_before_dma(info,
+				req_list, req_list_count);
+
+		/*
+		 * Do the blit DMA, if required -- returning early only if
+		 * there is a failure.
+		 */
+		for (i = 0; i < req_list_count; i++) {
+			if (!(req_list[i].flags & MDP_NO_BLIT)) {
+				/* Do the actual blit. */
+				int ret = mdp_blit(info, &(req_list[i]));
+
+				/*
+				 * Note that early returns don't guarantee
+				 * memory coherency.
+				 */
+				if (ret)
+					return ret;
+			}
+		}
+
+		/*
+		 * Ensure that CPU cache and other internal CPU state is
+		 * updated to reflect any change in memory modified by MDP blit
+		 * DMA.
+		 */
+		msm_fb_ensure_memory_coherency_after_dma(info,
+				req_list,
+				req_list_count);
+
+		/* Go to next window of requests. */
+		count -= req_list_count;
+		p += sizeof(struct mdp_blit_req)*req_list_count;
+	}
+	return 0;
+}
+
+#ifdef CONFIG_FB_MSM_OVERLAY
+static int msmfb_overlay_get(struct fb_info *info, void __user *p)
+{
+	struct mdp_overlay req;
+	int ret;
+
+	if (copy_from_user(&req, p, sizeof(req)))
+		return -EFAULT;
+
+	ret = mdp4_overlay_get(info, &req);
+	if (ret) {
+		printk(KERN_ERR "%s: ioctl failed \n",
+			__func__);
+		return ret;
+	}
+	if (copy_to_user(p, &req, sizeof(req))) {
+		printk(KERN_ERR "%s: copy2user failed \n",
+			__func__);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int msmfb_overlay_set(struct fb_info *info, void __user *p)
+{
+	struct mdp_overlay req;
+	int ret;
+
+	if (copy_from_user(&req, p, sizeof(req)))
+		return -EFAULT;
+
+	ret = mdp4_overlay_set(info, &req);
+	if (ret) {
+		printk(KERN_ERR "%s: ioctl failed, rc=%d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	if (copy_to_user(p, &req, sizeof(req))) {
+		printk(KERN_ERR "%s: copy2user failed \n",
+			__func__);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int msmfb_overlay_unset(struct fb_info *info, unsigned long *argp)
+{
+	int	ret, ndx;
+
+	ret = copy_from_user(&ndx, argp, sizeof(ndx));
+	if (ret) {
+		printk(KERN_ERR "%s:msmfb_overlay_unset ioctl failed \n",
+			__func__);
+		return ret;
+	}
+
+	return mdp4_overlay_unset(info, ndx);
+}
+
+static int msmfb_overlay_play_wait(struct fb_info *info, unsigned long *argp)
+{
+	int ret;
+	struct msmfb_overlay_data req;
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+
+	if (mfd->overlay_play_enable == 0)      /* nothing to do */
+		return 0;
+
+	ret = copy_from_user(&req, argp, sizeof(req));
+	if (ret) {
+		pr_err("%s:msmfb_overlay_wait ioctl failed", __func__);
+		return ret;
+	}
+
+	ret = mdp4_overlay_play_wait(info, &req);
+
+	return ret;
+}
+
+static int msmfb_overlay_play(struct fb_info *info, unsigned long *argp)
+{
+	int	ret;
+	struct msmfb_overlay_data req;
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	struct msm_fb_panel_data *pdata;
+
+	if (mfd->overlay_play_enable == 0)	/* nothing to do */
+		return 0;
+
+	ret = copy_from_user(&req, argp, sizeof(req));
+	if (ret) {
+		printk(KERN_ERR "%s:msmfb_overlay_play ioctl failed \n",
+			__func__);
+		return ret;
+	}
+
+	complete(&mfd->msmfb_update_notify);
+	mutex_lock(&msm_fb_notify_update_sem);
+	if (mfd->msmfb_no_update_notify_timer.function)
+		del_timer(&mfd->msmfb_no_update_notify_timer);
+
+	mfd->msmfb_no_update_notify_timer.expires =
+				jiffies + ((1000 * HZ) / 1000);
+	add_timer(&mfd->msmfb_no_update_notify_timer);
+	mutex_unlock(&msm_fb_notify_update_sem);
+
+	if (info->node == 0 && !(mfd->cont_splash_done)) { /* primary */
+		mdp_set_dma_pan_info(info, NULL, TRUE);
+		if (msm_fb_blank_sub(FB_BLANK_UNBLANK, info, mfd->op_enable)) {
+			pr_err("%s: can't turn on display!\n", __func__);
+			return -EINVAL;
+		}
+	}
+
+	ret = mdp4_overlay_play(info, &req);
+
+	if (unset_bl_level && !bl_updated) {
+		pdata = (struct msm_fb_panel_data *)mfd->pdev->
+			dev.platform_data;
+		if ((pdata) && (pdata->set_backlight)) {
+			down(&mfd->sem);
+			mfd->bl_level = unset_bl_level;
+			pdata->set_backlight(mfd);
+			bl_level_old = unset_bl_level;
+			up(&mfd->sem);
+			bl_updated = 1;
+		}
+	}
+
+	return ret;
+}
+
+static int msmfb_overlay_play_enable(struct fb_info *info, unsigned long *argp)
+{
+	int	ret, enable;
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+
+	ret = copy_from_user(&enable, argp, sizeof(enable));
+	if (ret) {
+		printk(KERN_ERR "%s:msmfb_overlay_play_enable ioctl failed \n",
+			__func__);
+		return ret;
+	}
+
+	mfd->overlay_play_enable = enable;
+
+	return 0;
+}
+
+static int msmfb_overlay_blt(struct fb_info *info, unsigned long *argp)
+{
+	int     ret;
+	struct msmfb_overlay_blt req;
+
+	ret = copy_from_user(&req, argp, sizeof(req));
+	if (ret) {
+		pr_err("%s: failed\n", __func__);
+		return ret;
+	}
+
+	ret = mdp4_overlay_blt(info, &req);
+
+	return ret;
+}
+
+static int msmfb_overlay_blt_off(struct fb_info *info, unsigned long *argp)
+{
+	int	ret;
+	struct msmfb_overlay_blt req;
+
+	ret = copy_from_user(&req, argp, sizeof(req));
+	if (ret) {
+		pr_err("%s: failed\n", __func__);
+		return ret;
+	}
+
+	ret = mdp4_overlay_blt_offset(info, &req);
+
+	ret = copy_to_user(argp, &req, sizeof(req));
+	if (ret)
+		printk(KERN_ERR "%s:msmfb_overlay_blt_off ioctl failed\n",
+		__func__);
+
+	return ret;
+}
+
+#ifdef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL
+static int msmfb_overlay_ioctl_writeback_init(struct fb_info *info)
+{
+	return mdp4_writeback_init(info);
+}
+static int msmfb_overlay_ioctl_writeback_start(
+		struct fb_info *info)
+{
+	int ret = 0;
+	ret = mdp4_writeback_start(info);
+	if (ret)
+		goto error;
+error:
+	if (ret)
+		pr_err("%s:msmfb_writeback_start "
+				" ioctl failed\n", __func__);
+	return ret;
+}
+
+static int msmfb_overlay_ioctl_writeback_stop(
+		struct fb_info *info)
+{
+	int ret = 0;
+	ret = mdp4_writeback_stop(info);
+	if (ret)
+		goto error;
+
+error:
+	if (ret)
+		pr_err("%s:msmfb_writeback_stop ioctl failed\n",
+				__func__);
+	return ret;
+}
+
+static int msmfb_overlay_ioctl_writeback_queue_buffer(
+		struct fb_info *info, unsigned long *argp)
+{
+	int ret = 0;
+	struct msmfb_data data;
+
+	ret = copy_from_user(&data, argp, sizeof(data));
+	if (ret)
+		goto error;
+
+	ret = mdp4_writeback_queue_buffer(info, &data);
+	if (ret)
+		goto error;
+
+error:
+	if (ret)
+		pr_err("%s:msmfb_writeback_queue_buffer ioctl failed\n",
+				__func__);
+	return ret;
+}
+
+static int msmfb_overlay_ioctl_writeback_dequeue_buffer(
+		struct fb_info *info, unsigned long *argp)
+{
+	int ret = 0;
+	struct msmfb_data data;
+
+	ret = copy_from_user(&data, argp, sizeof(data));
+	if (ret)
+		goto error;
+
+	ret = mdp4_writeback_dequeue_buffer(info, &data);
+	if (ret)
+		goto error;
+
+	ret = copy_to_user(argp, &data, sizeof(data));
+	if (ret)
+		goto error;
+
+error:
+	if (ret)
+		pr_err("%s:msmfb_writeback_dequeue_buffer ioctl failed\n",
+				__func__);
+	return ret;
+}
+static int msmfb_overlay_ioctl_writeback_terminate(struct fb_info *info)
+{
+	return mdp4_writeback_terminate(info);
+}
+
+#else
+static int msmfb_overlay_ioctl_writeback_init(struct fb_info *info)
+{
+	return -ENOTSUPP;
+}
+static int msmfb_overlay_ioctl_writeback_start(
+		struct fb_info *info)
+{
+	return -ENOTSUPP;
+}
+
+static int msmfb_overlay_ioctl_writeback_stop(
+		struct fb_info *info)
+{
+	return -ENOTSUPP;
+}
+
+static int msmfb_overlay_ioctl_writeback_queue_buffer(
+		struct fb_info *info, unsigned long *argp)
+{
+	return -ENOTSUPP;
+}
+
+static int msmfb_overlay_ioctl_writeback_dequeue_buffer(
+		struct fb_info *info, unsigned long *argp)
+{
+	return -ENOTSUPP;
+}
+static int msmfb_overlay_ioctl_writeback_terminate(struct fb_info *info)
+{
+	return -ENOTSUPP;
+}
+#endif
+
+static int msmfb_overlay_3d_sbys(struct fb_info *info, unsigned long *argp)
+{
+	int	ret;
+	struct msmfb_overlay_3d req;
+
+	ret = copy_from_user(&req, argp, sizeof(req));
+	if (ret) {
+		pr_err("%s:msmfb_overlay_3d_ctrl ioctl failed\n",
+			__func__);
+		return ret;
+	}
+
+	ret = mdp4_overlay_3d_sbys(info, &req);
+
+	return ret;
+}
+
+static int msmfb_mixer_info(struct fb_info *info, unsigned long *argp)
+{
+	int     ret, cnt;
+	struct msmfb_mixer_info_req req;
+
+	ret = copy_from_user(&req, argp, sizeof(req));
+	if (ret) {
+		pr_err("%s: failed\n", __func__);
+		return ret;
+	}
+
+	cnt = mdp4_mixer_info(req.mixer_num, req.info);
+	req.cnt = cnt;
+	ret = copy_to_user(argp, &req, sizeof(req));
+	if (ret)
+		pr_err("%s:msmfb_overlay_blt_off ioctl failed\n",
+		__func__);
+
+	return cnt;
+}
+
+#endif
+
+DEFINE_SEMAPHORE(msm_fb_ioctl_ppp_sem);
+DEFINE_MUTEX(msm_fb_ioctl_lut_sem);
+
+/* Set color conversion matrix from user space */
+
+#ifndef CONFIG_FB_MSM_MDP40
+static void msmfb_set_color_conv(struct mdp_ccs *p)
+{
+	int i;
+
+	if (p->direction == MDP_CCS_RGB2YUV) {
+		/* MDP cmd block enable */
+		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+		/* RGB->YUV primary forward matrix */
+		for (i = 0; i < MDP_CCS_SIZE; i++)
+			writel(p->ccs[i], MDP_CSC_PFMVn(i));
+
+		#ifdef CONFIG_FB_MSM_MDP31
+		for (i = 0; i < MDP_BV_SIZE; i++)
+			writel(p->bv[i], MDP_CSC_POST_BV2n(i));
+		#endif
+
+		/* MDP cmd block disable */
+		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+	} else {
+		/* MDP cmd block enable */
+		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+		/* YUV->RGB primary reverse matrix */
+		for (i = 0; i < MDP_CCS_SIZE; i++)
+			writel(p->ccs[i], MDP_CSC_PRMVn(i));
+		for (i = 0; i < MDP_BV_SIZE; i++)
+			writel(p->bv[i], MDP_CSC_PRE_BV1n(i));
+
+		/* MDP cmd block disable */
+		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+	}
+}
+#else
+static void msmfb_set_color_conv(struct mdp_csc *p)
+{
+	mdp4_vg_csc_update(p);
+}
+#endif
+
+static int msmfb_notify_update(struct fb_info *info, unsigned long *argp)
+{
+	int ret, notify;
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+
+	ret = copy_from_user(&notify, argp, sizeof(int));
+	if (ret) {
+		pr_err("%s:ioctl failed\n", __func__);
+		return ret;
+	}
+
+	if (notify > NOTIFY_UPDATE_STOP)
+		return -EINVAL;
+
+	if (notify == NOTIFY_UPDATE_START) {
+		INIT_COMPLETION(mfd->msmfb_update_notify);
+		wait_for_completion_interruptible(&mfd->msmfb_update_notify);
+	} else {
+		INIT_COMPLETION(mfd->msmfb_no_update_notify);
+		wait_for_completion_interruptible(&mfd->msmfb_no_update_notify);
+	}
+	return 0;
+}
+
+static int msmfb_handle_pp_ioctl(struct msmfb_mdp_pp *pp_ptr)
+{
+	int ret = -1;
+
+	if (!pp_ptr)
+		return ret;
+
+	switch (pp_ptr->op) {
+#ifdef CONFIG_FB_MSM_MDP40
+	case mdp_op_csc_cfg:
+		ret = mdp4_csc_config(&(pp_ptr->data.csc_cfg_data));
+		break;
+
+	case mdp_op_pcc_cfg:
+		ret = mdp4_pcc_cfg(&(pp_ptr->data.pcc_cfg_data));
+		break;
+
+	case mdp_op_lut_cfg:
+		switch (pp_ptr->data.lut_cfg_data.lut_type) {
+		case mdp_lut_igc:
+			ret = mdp4_igc_lut_config(
+					(struct mdp_igc_lut_data *)
+					&pp_ptr->data.lut_cfg_data.data);
+			break;
+
+		case mdp_lut_pgc:
+			ret = mdp4_argc_cfg(
+				&pp_ptr->data.lut_cfg_data.data.pgc_lut_data);
+			break;
+
+		case mdp_lut_hist:
+			ret = mdp_hist_lut_config(
+					(struct mdp_hist_lut_data *)
+					&pp_ptr->data.lut_cfg_data.data);
+			break;
+
+		default:
+			break;
+		}
+		break;
+	case mdp_op_qseed_cfg:
+		ret = mdp4_qseed_cfg((struct mdp_qseed_cfg_data *)
+						&pp_ptr->data.qseed_cfg_data);
+		break;
+#endif
+	default:
+		pr_warn("Unsupported request to MDP_PP IOCTL.\n");
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int msm_fb_ioctl(struct fb_info *info, unsigned int cmd,
+			unsigned long arg)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	void __user *argp = (void __user *)arg;
+	struct fb_cursor cursor;
+	struct fb_cmap cmap;
+	struct mdp_histogram_data hist;
+	struct mdp_histogram_start_req hist_req;
+	uint32_t block;
+#ifndef CONFIG_FB_MSM_MDP40
+	struct mdp_ccs ccs_matrix;
+#else
+	struct mdp_csc csc_matrix;
+#endif
+	struct mdp_page_protection fb_page_protection;
+	struct msmfb_mdp_pp mdp_pp;
+	int ret = 0;
+
+	switch (cmd) {
+#ifdef CONFIG_FB_MSM_OVERLAY
+	case MSMFB_OVERLAY_GET:
+		down(&msm_fb_ioctl_ppp_sem);
+		ret = msmfb_overlay_get(info, argp);
+		up(&msm_fb_ioctl_ppp_sem);
+		break;
+	case MSMFB_OVERLAY_SET:
+		down(&msm_fb_ioctl_ppp_sem);
+		ret = msmfb_overlay_set(info, argp);
+		up(&msm_fb_ioctl_ppp_sem);
+		break;
+	case MSMFB_OVERLAY_UNSET:
+		down(&msm_fb_ioctl_ppp_sem);
+		ret = msmfb_overlay_unset(info, argp);
+		up(&msm_fb_ioctl_ppp_sem);
+		break;
+	case MSMFB_OVERLAY_PLAY:
+		down(&msm_fb_ioctl_ppp_sem);
+		ret = msmfb_overlay_play(info, argp);
+		up(&msm_fb_ioctl_ppp_sem);
+		break;
+	case MSMFB_OVERLAY_PLAY_ENABLE:
+		down(&msm_fb_ioctl_ppp_sem);
+		ret = msmfb_overlay_play_enable(info, argp);
+		up(&msm_fb_ioctl_ppp_sem);
+		break;
+	case MSMFB_OVERLAY_PLAY_WAIT:
+		down(&msm_fb_ioctl_ppp_sem);
+		ret = msmfb_overlay_play_wait(info, argp);
+		up(&msm_fb_ioctl_ppp_sem);
+		break;
+	case MSMFB_OVERLAY_BLT:
+		down(&msm_fb_ioctl_ppp_sem);
+		ret = msmfb_overlay_blt(info, argp);
+		up(&msm_fb_ioctl_ppp_sem);
+		break;
+	case MSMFB_OVERLAY_BLT_OFFSET:
+		down(&msm_fb_ioctl_ppp_sem);
+		ret = msmfb_overlay_blt_off(info, argp);
+		up(&msm_fb_ioctl_ppp_sem);
+		break;
+	case MSMFB_OVERLAY_3D:
+		down(&msm_fb_ioctl_ppp_sem);
+		ret = msmfb_overlay_3d_sbys(info, argp);
+		up(&msm_fb_ioctl_ppp_sem);
+		break;
+	case MSMFB_MIXER_INFO:
+		down(&msm_fb_ioctl_ppp_sem);
+		ret = msmfb_mixer_info(info, argp);
+		up(&msm_fb_ioctl_ppp_sem);
+		break;
+	case MSMFB_WRITEBACK_INIT:
+		ret = msmfb_overlay_ioctl_writeback_init(info);
+		break;
+	case MSMFB_WRITEBACK_START:
+		ret = msmfb_overlay_ioctl_writeback_start(
+				info);
+		break;
+	case MSMFB_WRITEBACK_STOP:
+		ret = msmfb_overlay_ioctl_writeback_stop(
+				info);
+		break;
+	case MSMFB_WRITEBACK_QUEUE_BUFFER:
+		ret = msmfb_overlay_ioctl_writeback_queue_buffer(
+				info, argp);
+		break;
+	case MSMFB_WRITEBACK_DEQUEUE_BUFFER:
+		ret = msmfb_overlay_ioctl_writeback_dequeue_buffer(
+				info, argp);
+		break;
+	case MSMFB_WRITEBACK_TERMINATE:
+		ret = msmfb_overlay_ioctl_writeback_terminate(info);
+		break;
+#endif
+	case MSMFB_BLIT:
+		down(&msm_fb_ioctl_ppp_sem);
+		ret = msmfb_blit(info, argp);
+		up(&msm_fb_ioctl_ppp_sem);
+
+		break;
+
+	/* Ioctl for setting ccs matrix from user space */
+	case MSMFB_SET_CCS_MATRIX:
+#ifndef CONFIG_FB_MSM_MDP40
+		ret = copy_from_user(&ccs_matrix, argp, sizeof(ccs_matrix));
+		if (ret) {
+			printk(KERN_ERR
+				"%s:MSMFB_SET_CCS_MATRIX ioctl failed \n",
+				__func__);
+			return ret;
+		}
+
+		down(&msm_fb_ioctl_ppp_sem);
+		if (ccs_matrix.direction == MDP_CCS_RGB2YUV)
+			mdp_ccs_rgb2yuv = ccs_matrix;
+		else
+			mdp_ccs_yuv2rgb = ccs_matrix;
+
+		msmfb_set_color_conv(&ccs_matrix) ;
+		up(&msm_fb_ioctl_ppp_sem);
+#else
+		ret = copy_from_user(&csc_matrix, argp, sizeof(csc_matrix));
+		if (ret) {
+			pr_err("%s:MSMFB_SET_CSC_MATRIX ioctl failed\n",
+				__func__);
+			return ret;
+		}
+		down(&msm_fb_ioctl_ppp_sem);
+		msmfb_set_color_conv(&csc_matrix);
+		up(&msm_fb_ioctl_ppp_sem);
+
+#endif
+
+		break;
+
+	/* Ioctl for getting ccs matrix to user space */
+	case MSMFB_GET_CCS_MATRIX:
+#ifndef CONFIG_FB_MSM_MDP40
+		ret = copy_from_user(&ccs_matrix, argp, sizeof(ccs_matrix)) ;
+		if (ret) {
+			printk(KERN_ERR
+				"%s:MSMFB_GET_CCS_MATRIX ioctl failed \n",
+				 __func__);
+			return ret;
+		}
+
+		down(&msm_fb_ioctl_ppp_sem);
+		if (ccs_matrix.direction == MDP_CCS_RGB2YUV)
+			ccs_matrix = mdp_ccs_rgb2yuv;
+		 else
+			ccs_matrix =  mdp_ccs_yuv2rgb;
+
+		ret = copy_to_user(argp, &ccs_matrix, sizeof(ccs_matrix));
+
+		if (ret)	{
+			printk(KERN_ERR
+				"%s:MSMFB_GET_CCS_MATRIX ioctl failed \n",
+				 __func__);
+			return ret ;
+		}
+		up(&msm_fb_ioctl_ppp_sem);
+#else
+		ret = -EINVAL;
+#endif
+
+		break;
+
+	case MSMFB_GRP_DISP:
+#ifdef CONFIG_FB_MSM_MDP22
+		{
+			unsigned long grp_id;
+
+			ret = copy_from_user(&grp_id, argp, sizeof(grp_id));
+			if (ret)
+				return ret;
+
+			mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+			writel(grp_id, MDP_FULL_BYPASS_WORD43);
+			mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF,
+				      FALSE);
+			break;
+		}
+#else
+		return -EFAULT;
+#endif
+	case MSMFB_SUSPEND_SW_REFRESHER:
+		if (!mfd->panel_power_on)
+			return -EPERM;
+
+		mfd->sw_refreshing_enable = FALSE;
+		ret = msm_fb_stop_sw_refresher(mfd);
+		break;
+
+	case MSMFB_RESUME_SW_REFRESHER:
+		if (!mfd->panel_power_on)
+			return -EPERM;
+
+		mfd->sw_refreshing_enable = TRUE;
+		ret = msm_fb_resume_sw_refresher(mfd);
+		break;
+
+	case MSMFB_CURSOR:
+		ret = copy_from_user(&cursor, argp, sizeof(cursor));
+		if (ret)
+			return ret;
+
+		ret = msm_fb_cursor(info, &cursor);
+		break;
+
+	case MSMFB_SET_LUT:
+		ret = copy_from_user(&cmap, argp, sizeof(cmap));
+		if (ret)
+			return ret;
+
+		mutex_lock(&msm_fb_ioctl_lut_sem);
+		ret = msm_fb_set_lut(&cmap, info);
+		mutex_unlock(&msm_fb_ioctl_lut_sem);
+		break;
+
+	case MSMFB_HISTOGRAM:
+		if (!mfd->panel_power_on)
+			return -EPERM;
+
+		if (!mfd->do_histogram)
+			return -ENODEV;
+
+		ret = copy_from_user(&hist, argp, sizeof(hist));
+		if (ret)
+			return ret;
+
+		ret = mfd->do_histogram(info, &hist);
+		break;
+
+	case MSMFB_HISTOGRAM_START:
+		if (!mfd->panel_power_on)
+			return -EPERM;
+
+		if (!mfd->start_histogram)
+			return -ENODEV;
+
+		ret = copy_from_user(&hist_req, argp, sizeof(hist_req));
+		if (ret)
+			return ret;
+
+		ret = mfd->start_histogram(&hist_req);
+		break;
+
+	case MSMFB_HISTOGRAM_STOP:
+		if (!mfd->stop_histogram)
+			return -ENODEV;
+
+		ret = copy_from_user(&block, argp, sizeof(int));
+		if (ret)
+			return ret;
+
+		ret = mfd->stop_histogram(info, block);
+		break;
+
+
+	case MSMFB_GET_PAGE_PROTECTION:
+		fb_page_protection.page_protection
+			= mfd->mdp_fb_page_protection;
+		ret = copy_to_user(argp, &fb_page_protection,
+				sizeof(fb_page_protection));
+		if (ret)
+				return ret;
+		break;
+
+	case MSMFB_NOTIFY_UPDATE:
+		ret = msmfb_notify_update(info, argp);
+		break;
+
+	case MSMFB_SET_PAGE_PROTECTION:
+#if defined CONFIG_ARCH_QSD8X50 || defined CONFIG_ARCH_MSM8X60
+		ret = copy_from_user(&fb_page_protection, argp,
+				sizeof(fb_page_protection));
+		if (ret)
+				return ret;
+
+		/* Validate the proposed page protection settings. */
+		switch (fb_page_protection.page_protection)	{
+		case MDP_FB_PAGE_PROTECTION_NONCACHED:
+		case MDP_FB_PAGE_PROTECTION_WRITECOMBINE:
+		case MDP_FB_PAGE_PROTECTION_WRITETHROUGHCACHE:
+		/* Write-back cache (read allocate)  */
+		case MDP_FB_PAGE_PROTECTION_WRITEBACKCACHE:
+		/* Write-back cache (write allocate) */
+		case MDP_FB_PAGE_PROTECTION_WRITEBACKWACACHE:
+			mfd->mdp_fb_page_protection =
+				fb_page_protection.page_protection;
+			break;
+		default:
+			ret = -EINVAL;
+			break;
+		}
+#else
+		/*
+		 * Don't allow caching until 7k DMA cache operations are
+		 * available.
+		 */
+		ret = -EINVAL;
+#endif
+		break;
+
+	case MSMFB_MDP_PP:
+		ret = copy_from_user(&mdp_pp, argp, sizeof(mdp_pp));
+		if (ret)
+			return ret;
+
+		ret = msmfb_handle_pp_ioctl(&mdp_pp);
+		break;
+
+	default:
+		MSM_FB_INFO("MDP: unknown ioctl (cmd=%x) received!\n", cmd);
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int msm_fb_register_driver(void)
+{
+	return platform_driver_register(&msm_fb_driver);
+}
+
+#ifdef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL
+struct fb_info *msm_fb_get_writeback_fb(void)
+{
+	int c = 0;
+	for (c = 0; c < fbi_list_index; ++c) {
+		struct msm_fb_data_type *mfd;
+		mfd = (struct msm_fb_data_type *)fbi_list[c]->par;
+		if (mfd->panel.type == WRITEBACK_PANEL)
+			return fbi_list[c];
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL(msm_fb_get_writeback_fb);
+
+int msm_fb_writeback_start(struct fb_info *info)
+{
+	return mdp4_writeback_start(info);
+}
+EXPORT_SYMBOL(msm_fb_writeback_start);
+
+int msm_fb_writeback_queue_buffer(struct fb_info *info,
+		struct msmfb_data *data)
+{
+	return mdp4_writeback_queue_buffer(info, data);
+}
+EXPORT_SYMBOL(msm_fb_writeback_queue_buffer);
+
+int msm_fb_writeback_dequeue_buffer(struct fb_info *info,
+		struct msmfb_data *data)
+{
+	return mdp4_writeback_dequeue_buffer(info, data);
+}
+EXPORT_SYMBOL(msm_fb_writeback_dequeue_buffer);
+
+int msm_fb_writeback_stop(struct fb_info *info)
+{
+	return mdp4_writeback_stop(info);
+}
+EXPORT_SYMBOL(msm_fb_writeback_stop);
+int msm_fb_writeback_init(struct fb_info *info)
+{
+	return mdp4_writeback_init(info);
+}
+EXPORT_SYMBOL(msm_fb_writeback_init);
+int msm_fb_writeback_terminate(struct fb_info *info)
+{
+	return mdp4_writeback_terminate(info);
+}
+EXPORT_SYMBOL(msm_fb_writeback_terminate);
+#endif
+
+struct platform_device *msm_fb_add_device(struct platform_device *pdev)
+{
+	struct msm_fb_panel_data *pdata;
+	struct platform_device *this_dev = NULL;
+	struct fb_info *fbi;
+	struct msm_fb_data_type *mfd = NULL;
+	u32 type, id, fb_num;
+
+	if (!pdev)
+		return NULL;
+	id = pdev->id;
+
+	pdata = pdev->dev.platform_data;
+	if (!pdata)
+		return NULL;
+	type = pdata->panel_info.type;
+
+#if defined MSM_FB_NUM
+	/*
+	 * over written fb_num which defined
+	 * at panel_info
+	 *
+	 */
+	if (type == HDMI_PANEL || type == DTV_PANEL ||
+		type == TV_PANEL || type == WRITEBACK_PANEL) {
+		if (hdmi_prim_display)
+			pdata->panel_info.fb_num = 2;
+		else
+			pdata->panel_info.fb_num = 1;
+	}
+	else
+		pdata->panel_info.fb_num = MSM_FB_NUM;
+
+	MSM_FB_INFO("setting pdata->panel_info.fb_num to %d. type: %d\n",
+			pdata->panel_info.fb_num, type);
+#endif
+	fb_num = pdata->panel_info.fb_num;
+
+	if (fb_num <= 0)
+		return NULL;
+
+	if (fbi_list_index >= MAX_FBI_LIST) {
+		printk(KERN_ERR "msm_fb: no more framebuffer info list!\n");
+		return NULL;
+	}
+	/*
+	 * alloc panel device data
+	 */
+	this_dev = msm_fb_device_alloc(pdata, type, id);
+
+	if (!this_dev) {
+		printk(KERN_ERR
+		"%s: msm_fb_device_alloc failed!\n", __func__);
+		return NULL;
+	}
+
+	/*
+	 * alloc framebuffer info + par data
+	 */
+	fbi = framebuffer_alloc(sizeof(struct msm_fb_data_type), NULL);
+	if (fbi == NULL) {
+		platform_device_put(this_dev);
+		printk(KERN_ERR "msm_fb: can't alloca framebuffer info data!\n");
+		return NULL;
+	}
+
+	mfd = (struct msm_fb_data_type *)fbi->par;
+	mfd->key = MFD_KEY;
+	mfd->fbi = fbi;
+	mfd->panel.type = type;
+	mfd->panel.id = id;
+	mfd->fb_page = fb_num;
+	mfd->index = fbi_list_index;
+	mfd->mdp_fb_page_protection = MDP_FB_PAGE_PROTECTION_WRITECOMBINE;
+	mfd->iclient = iclient;
+	/* link to the latest pdev */
+	mfd->pdev = this_dev;
+
+	mfd_list[mfd_list_index++] = mfd;
+	fbi_list[fbi_list_index++] = fbi;
+
+	/*
+	 * set driver data
+	 */
+	platform_set_drvdata(this_dev, mfd);
+
+	if (platform_device_add(this_dev)) {
+		printk(KERN_ERR "msm_fb: platform_device_add failed!\n");
+		platform_device_put(this_dev);
+		framebuffer_release(fbi);
+		fbi_list_index--;
+		return NULL;
+	}
+	return this_dev;
+}
+EXPORT_SYMBOL(msm_fb_add_device);
+
+int get_fb_phys_info(unsigned long *start, unsigned long *len, int fb_num,
+	int subsys_id)
+{
+	struct fb_info *info;
+	struct msm_fb_data_type *mfd;
+
+	if (fb_num > MAX_FBI_LIST ||
+		(subsys_id != DISPLAY_SUBSYSTEM_ID &&
+		 subsys_id != ROTATOR_SUBSYSTEM_ID)) {
+		pr_err("%s(): Invalid parameters\n", __func__);
+		return -1;
+	}
+
+	info = fbi_list[fb_num];
+	if (!info) {
+		pr_err("%s(): info is NULL\n", __func__);
+		return -1;
+	}
+
+	mfd = (struct msm_fb_data_type *)info->par;
+
+	if (subsys_id == DISPLAY_SUBSYSTEM_ID) {
+		if (mfd->display_iova)
+			*start = mfd->display_iova;
+		else
+			*start = info->fix.smem_start;
+	} else {
+		if (mfd->rotator_iova)
+			*start = mfd->rotator_iova;
+		else
+			*start = info->fix.smem_start;
+	}
+
+	*len = info->fix.smem_len;
+
+	return 0;
+}
+EXPORT_SYMBOL(get_fb_phys_info);
+
+int __init msm_fb_init(void)
+{
+	int rc = -ENODEV;
+
+	if (msm_fb_register_driver())
+		return rc;
+
+#ifdef MSM_FB_ENABLE_DBGFS
+	{
+		struct dentry *root;
+
+		if ((root = msm_fb_get_debugfs_root()) != NULL) {
+			msm_fb_debugfs_file_create(root,
+						   "msm_fb_msg_printing_level",
+						   (u32 *) &msm_fb_msg_level);
+			msm_fb_debugfs_file_create(root,
+						   "mddi_msg_printing_level",
+						   (u32 *) &mddi_msg_level);
+			msm_fb_debugfs_file_create(root, "msm_fb_debug_enabled",
+						   (u32 *) &msm_fb_debug_enabled);
+		}
+	}
+#endif
+
+	return 0;
+}
+
+/* Called by v4l2 driver to enable/disable overlay pipe */
+int msm_fb_v4l2_enable(struct mdp_overlay *req, bool enable, void **par)
+{
+	int err = 0;
+#ifdef CONFIG_FB_MSM_MDP40
+	struct mdp4_overlay_pipe *pipe;
+	if (enable) {
+
+		err = mdp4_v4l2_overlay_set(fbi_list[0], req, &pipe);
+
+		*(struct mdp4_overlay_pipe **)par = pipe;
+
+	} else {
+		pipe = *(struct mdp4_overlay_pipe **)par;
+		mdp4_v4l2_overlay_clear(pipe);
+	}
+#else
+#ifdef CONFIG_FB_MSM_MDP30
+	if (enable)
+		err = mdp_ppp_v4l2_overlay_set(fbi_list[0], req);
+	else
+		err = mdp_ppp_v4l2_overlay_clear();
+#else
+	err = -EINVAL;
+#endif
+#endif
+
+	return err;
+}
+EXPORT_SYMBOL(msm_fb_v4l2_enable);
+
+/* Called by v4l2 driver to provide a frame for display */
+int msm_fb_v4l2_update(void *par,
+	unsigned long srcp0_addr, unsigned long srcp0_size,
+	unsigned long srcp1_addr, unsigned long srcp1_size,
+	unsigned long srcp2_addr, unsigned long srcp2_size)
+{
+#ifdef CONFIG_FB_MSM_MDP40
+	struct mdp4_overlay_pipe *pipe = (struct mdp4_overlay_pipe *)par;
+	return mdp4_v4l2_overlay_play(fbi_list[0], pipe,
+		srcp0_addr, srcp1_addr,
+		srcp2_addr);
+#else
+#ifdef CONFIG_FB_MSM_MDP30
+	return mdp_ppp_v4l2_overlay_play(fbi_list[0],
+		srcp0_addr, srcp0_size,
+		srcp1_addr, srcp1_size);
+#else
+	return -EINVAL;
+#endif
+#endif
+}
+EXPORT_SYMBOL(msm_fb_v4l2_update);
+
+module_init(msm_fb_init);
diff --git a/drivers/video/msm/msm_fb.h b/drivers/video/msm/msm_fb.h
new file mode 100644
index 0000000..c9eb7dd
--- /dev/null
+++ b/drivers/video/msm/msm_fb.h
@@ -0,0 +1,223 @@
+/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef MSM_FB_H
+#define MSM_FB_H
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include "linux/proc_fs.h"
+
+#include <mach/hardware.h>
+#include <linux/io.h>
+#include <mach/board.h>
+
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <mach/memory.h>
+#include <linux/semaphore.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/hrtimer.h>
+
+#include <linux/fb.h>
+#include <linux/list.h>
+#include <linux/types.h>
+
+#include <linux/msm_mdp.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+#include "msm_fb_panel.h"
+#include "mdp.h"
+
+#define MSM_FB_DEFAULT_PAGE_SIZE 2
+#define MFD_KEY  0x11161126
+#define MSM_FB_MAX_DEV_LIST 32
+
+struct disp_info_type_suspend {
+	boolean op_enable;
+	boolean sw_refreshing_enable;
+	boolean panel_power_on;
+};
+
+struct msmfb_writeback_data_list {
+	struct list_head registered_entry;
+	struct list_head active_entry;
+	void *addr;
+	struct file *pmem_file;
+	struct msmfb_data buf_info;
+	struct msmfb_img img;
+	int state;
+};
+
+
+struct msm_fb_data_type {
+	__u32 key;
+	__u32 index;
+	__u32 ref_cnt;
+	__u32 fb_page;
+
+	panel_id_type panel;
+	struct msm_panel_info panel_info;
+
+	DISP_TARGET dest;
+	struct fb_info *fbi;
+
+	boolean op_enable;
+	uint32 fb_imgType;
+	boolean sw_currently_refreshing;
+	boolean sw_refreshing_enable;
+	boolean hw_refresh;
+#ifdef CONFIG_FB_MSM_OVERLAY
+	int overlay_play_enable;
+#endif
+
+	MDPIBUF ibuf;
+	boolean ibuf_flushed;
+	struct timer_list refresh_timer;
+	struct completion refresher_comp;
+
+	boolean pan_waiting;
+	struct completion pan_comp;
+
+	/* vsync */
+	boolean use_mdp_vsync;
+	__u32 vsync_gpio;
+	__u32 total_lcd_lines;
+	__u32 total_porch_lines;
+	__u32 lcd_ref_usec_time;
+	__u32 refresh_timer_duration;
+
+	struct hrtimer dma_hrtimer;
+
+	boolean panel_power_on;
+	struct work_struct dma_update_worker;
+	struct semaphore sem;
+
+	struct timer_list vsync_resync_timer;
+	boolean vsync_handler_pending;
+	struct work_struct vsync_resync_worker;
+
+	ktime_t last_vsync_timetick;
+
+	__u32 *vsync_width_boundary;
+
+	unsigned int pmem_id;
+	struct disp_info_type_suspend suspend;
+
+	__u32 channel_irq;
+
+	struct mdp_dma_data *dma;
+	void (*dma_fnc) (struct msm_fb_data_type *mfd);
+	int (*cursor_update) (struct fb_info *info,
+			      struct fb_cursor *cursor);
+	int (*lut_update) (struct fb_info *info,
+			      struct fb_cmap *cmap);
+	int (*do_histogram) (struct fb_info *info,
+			      struct mdp_histogram_data *hist);
+	int (*start_histogram) (struct mdp_histogram_start_req *req);
+	int (*stop_histogram) (struct fb_info *info, uint32_t block);
+	void *cursor_buf;
+	void *cursor_buf_phys;
+
+	void *cmd_port;
+	void *data_port;
+	void *data_port_phys;
+
+	__u32 bl_level;
+
+	struct platform_device *pdev;
+
+	__u32 var_xres;
+	__u32 var_yres;
+	__u32 var_pixclock;
+	__u32 var_frame_rate;
+
+#ifdef MSM_FB_ENABLE_DBGFS
+	struct dentry *sub_dir;
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	struct early_suspend early_suspend;
+#ifdef CONFIG_FB_MSM_MDDI
+	struct early_suspend mddi_early_suspend;
+	struct early_suspend mddi_ext_early_suspend;
+#endif
+#endif
+	u32 mdp_fb_page_protection;
+
+	struct clk *ebi1_clk;
+	boolean dma_update_flag;
+	struct timer_list msmfb_no_update_notify_timer;
+	struct completion msmfb_update_notify;
+	struct completion msmfb_no_update_notify;
+	struct mutex writeback_mutex;
+	struct mutex unregister_mutex;
+	struct list_head writeback_busy_queue;
+	struct list_head writeback_free_queue;
+	struct list_head writeback_register_queue;
+	wait_queue_head_t wait_q;
+	struct ion_client *iclient;
+	unsigned long display_iova;
+	unsigned long rotator_iova;
+	struct mdp_buf_type *ov0_wb_buf;
+	struct mdp_buf_type *ov1_wb_buf;
+	u32 ov_start;
+	u32 mem_hid;
+	u32 mdp_rev;
+	u32 use_ov0_blt, ov0_blt_state;
+	u32 use_ov1_blt, ov1_blt_state;
+	u32 writeback_state;
+	bool writeback_active_cnt;
+	int cont_splash_done;
+};
+
+struct dentry *msm_fb_get_debugfs_root(void);
+void msm_fb_debugfs_file_create(struct dentry *root, const char *name,
+				u32 *var);
+void msm_fb_set_backlight(struct msm_fb_data_type *mfd, __u32 bkl_lvl);
+
+struct platform_device *msm_fb_add_device(struct platform_device *pdev);
+struct fb_info *msm_fb_get_writeback_fb(void);
+int msm_fb_writeback_init(struct fb_info *info);
+int msm_fb_writeback_start(struct fb_info *info);
+int msm_fb_writeback_queue_buffer(struct fb_info *info,
+		struct msmfb_data *data);
+int msm_fb_writeback_dequeue_buffer(struct fb_info *info,
+		struct msmfb_data *data);
+int msm_fb_writeback_stop(struct fb_info *info);
+int msm_fb_writeback_terminate(struct fb_info *info);
+int msm_fb_detect_client(const char *name);
+int calc_fb_offset(struct msm_fb_data_type *mfd, struct fb_info *fbi, int bpp);
+
+#ifdef CONFIG_FB_BACKLIGHT
+void msm_fb_config_backlight(struct msm_fb_data_type *mfd);
+#endif
+
+void fill_black_screen(void);
+void unfill_black_screen(void);
+int msm_fb_check_frame_rate(struct msm_fb_data_type *mfd,
+				struct fb_info *info);
+
+#ifdef CONFIG_FB_MSM_LOGO
+#define INIT_IMAGE_FILE "/initlogo.rle"
+int load_565rle_image(char *filename, bool bf_supported);
+#endif
+
+#endif /* MSM_FB_H */
diff --git a/drivers/video/msm/msm_fb_bl.c b/drivers/video/msm/msm_fb_bl.c
new file mode 100644
index 0000000..9afbbf1
--- /dev/null
+++ b/drivers/video/msm/msm_fb_bl.c
@@ -0,0 +1,75 @@
+/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/fb.h>
+#include <linux/string.h>
+#include <linux/version.h>
+#include <linux/backlight.h>
+
+#include "msm_fb.h"
+
+static int msm_fb_bl_get_brightness(struct backlight_device *pbd)
+{
+	return pbd->props.brightness;
+}
+
+static int msm_fb_bl_update_status(struct backlight_device *pbd)
+{
+	struct msm_fb_data_type *mfd = bl_get_data(pbd);
+	__u32 bl_lvl;
+
+	bl_lvl = pbd->props.brightness;
+	bl_lvl = mfd->fbi->bl_curve[bl_lvl];
+	msm_fb_set_backlight(mfd, bl_lvl);
+	return 0;
+}
+
+static struct backlight_ops msm_fb_bl_ops = {
+	.get_brightness = msm_fb_bl_get_brightness,
+	.update_status = msm_fb_bl_update_status,
+};
+
+void msm_fb_config_backlight(struct msm_fb_data_type *mfd)
+{
+	struct msm_fb_panel_data *pdata;
+	struct backlight_device *pbd;
+	struct fb_info *fbi;
+	char name[16];
+	struct backlight_properties props;
+
+	fbi = mfd->fbi;
+	pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data;
+
+	if ((pdata) && (pdata->set_backlight)) {
+		snprintf(name, sizeof(name), "msmfb_bl%d", mfd->index);
+		props.max_brightness = FB_BACKLIGHT_LEVELS - 1;
+		props.brightness = FB_BACKLIGHT_LEVELS - 1;
+		pbd =
+		    backlight_device_register(name, fbi->dev, mfd,
+					      &msm_fb_bl_ops, &props);
+		if (!IS_ERR(pbd)) {
+			fbi->bl_dev = pbd;
+			fb_bl_default_curve(fbi,
+					    0,
+					    mfd->panel_info.bl_min,
+					    mfd->panel_info.bl_max);
+		} else {
+			fbi->bl_dev = NULL;
+			printk(KERN_ERR "msm_fb: backlight_device_register failed!\n");
+		}
+	}
+}
diff --git a/drivers/video/msm/msm_fb_def.h b/drivers/video/msm/msm_fb_def.h
new file mode 100644
index 0000000..1c1f392
--- /dev/null
+++ b/drivers/video/msm/msm_fb_def.h
@@ -0,0 +1,204 @@
+/* Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef MSM_FB_DEF_H
+#define MSM_FB_DEF_H
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/fb.h>
+#include <linux/msm_mdp.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/uaccess.h>
+#include <linux/workqueue.h>
+#include <linux/string.h>
+#include <linux/version.h>
+#include <linux/proc_fs.h>
+#include <linux/vmalloc.h>
+#include <linux/debugfs.h>
+#include <linux/console.h>
+#include <linux/android_pmem.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include "linux/proc_fs.h"
+#include <linux/io.h>
+#include <linux/fb.h>
+#include <linux/platform_device.h>
+
+#include <asm/system.h>
+#include <asm/mach-types.h>
+
+#include <mach/hardware.h>
+
+
+typedef s64 int64;
+typedef s32 int32;
+typedef s16 int16;
+typedef s8 int8;
+
+typedef u64 uint64;
+typedef u32 uint32;
+typedef u16 uint16;
+typedef u8 uint8;
+
+typedef s32 int4;
+typedef s16 int2;
+typedef s8 int1;
+
+typedef u32 uint4;
+typedef u16 uint2;
+typedef u8 uint1;
+
+typedef u32 dword;
+typedef u16 word;
+typedef u8 byte;
+
+typedef unsigned int boolean;
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define MSM_FB_ENABLE_DBGFS
+#define FEATURE_MDDI
+
+#if defined(CONFIG_FB_MSM_DEFAULT_DEPTH_RGB565)
+#define MSMFB_DEFAULT_TYPE MDP_RGB_565
+#elif defined(CONFIG_FB_MSM_DEFAULT_DEPTH_ARGB8888)
+#define MSMFB_DEFAULT_TYPE MDP_ARGB_8888
+#elif defined(CONFIG_FB_MSM_DEFAULT_DEPTH_RGBA8888)
+#define MSMFB_DEFAULT_TYPE MDP_RGBA_8888
+#else
+#define MSMFB_DEFAULT_TYPE MDP_RGB_565
+#endif
+
+#define outp32(addr, val) writel(val, addr)
+#define outp16(addr, val) writew(val, addr)
+#define outp8(addr, val) writeb(val, addr)
+#define outp(addr, val) outp32(addr, val)
+
+#ifndef MAX
+#define  MAX( x, y ) (((x) > (y)) ? (x) : (y))
+#endif
+
+#ifndef MIN
+#define  MIN( x, y ) (((x) < (y)) ? (x) : (y))
+#endif
+
+/*--------------------------------------------------------------------------*/
+
+#define inp32(addr) readl(addr)
+#define inp16(addr) readw(addr)
+#define inp8(addr) readb(addr)
+#define inp(addr) inp32(addr)
+
+#define inpw(port)             readw(port)
+#define outpw(port, val)       writew(val, port)
+#define inpdw(port)            readl(port)
+#define outpdw(port, val)      writel(val, port)
+
+
+#define clk_busy_wait(x) msleep_interruptible((x)/1000)
+
+#define memory_barrier()
+
+#define assert(expr) \
+	if(!(expr)) { \
+		printk(KERN_ERR "msm_fb: assertion failed! %s,%s,%s,line=%d\n",\
+			#expr, __FILE__, __func__, __LINE__); \
+	}
+
+#define ASSERT(x)   assert(x)
+
+#define DISP_EBI2_LOCAL_DEFINE
+#ifdef DISP_EBI2_LOCAL_DEFINE
+#define LCD_PRIM_BASE_PHYS 0x98000000
+#define LCD_SECD_BASE_PHYS 0x9c000000
+#define EBI2_PRIM_LCD_RS_PIN 0x20000
+#define EBI2_SECD_LCD_RS_PIN 0x20000
+
+#define EBI2_PRIM_LCD_CLR 0xC0
+#define EBI2_PRIM_LCD_SEL 0x40
+
+#define EBI2_SECD_LCD_CLR 0x300
+#define EBI2_SECD_LCD_SEL 0x100
+#endif
+
+extern u32 msm_fb_msg_level;
+
+/*
+ * Message printing priorities:
+ * LEVEL 0 KERN_EMERG (highest priority)
+ * LEVEL 1 KERN_ALERT
+ * LEVEL 2 KERN_CRIT
+ * LEVEL 3 KERN_ERR
+ * LEVEL 4 KERN_WARNING
+ * LEVEL 5 KERN_NOTICE
+ * LEVEL 6 KERN_INFO
+ * LEVEL 7 KERN_DEBUG (Lowest priority)
+ */
+#define MSM_FB_EMERG(msg, ...)    \
+	if (msm_fb_msg_level > 0)  \
+		printk(KERN_EMERG msg, ## __VA_ARGS__);
+#define MSM_FB_ALERT(msg, ...)    \
+	if (msm_fb_msg_level > 1)  \
+		printk(KERN_ALERT msg, ## __VA_ARGS__);
+#define MSM_FB_CRIT(msg, ...)    \
+	if (msm_fb_msg_level > 2)  \
+		printk(KERN_CRIT msg, ## __VA_ARGS__);
+#define MSM_FB_ERR(msg, ...)    \
+	if (msm_fb_msg_level > 3)  \
+		printk(KERN_ERR msg, ## __VA_ARGS__);
+#define MSM_FB_WARNING(msg, ...)    \
+	if (msm_fb_msg_level > 4)  \
+		printk(KERN_WARNING msg, ## __VA_ARGS__);
+#define MSM_FB_NOTICE(msg, ...)    \
+	if (msm_fb_msg_level > 5)  \
+		printk(KERN_NOTICE msg, ## __VA_ARGS__);
+#define MSM_FB_INFO(msg, ...)    \
+	if (msm_fb_msg_level > 6)  \
+		printk(KERN_INFO msg, ## __VA_ARGS__);
+#define MSM_FB_DEBUG(msg, ...)    \
+	if (msm_fb_msg_level > 7)  \
+		printk(KERN_DEBUG msg, ## __VA_ARGS__);
+
+#ifdef MSM_FB_C
+unsigned char *msm_mdp_base;
+unsigned char *msm_pmdh_base;
+unsigned char *msm_emdh_base;
+unsigned char *mipi_dsi_base;
+#else
+extern unsigned char *msm_mdp_base;
+extern unsigned char *msm_pmdh_base;
+extern unsigned char *msm_emdh_base;
+extern unsigned char *mipi_dsi_base;
+#endif
+
+#undef ENABLE_MDDI_MULTI_READ_WRITE
+#undef ENABLE_FWD_LINK_SKEW_CALIBRATION
+
+#endif /* MSM_FB_DEF_H */
diff --git a/drivers/video/msm/msm_fb_panel.c b/drivers/video/msm/msm_fb_panel.c
new file mode 100644
index 0000000..8640116
--- /dev/null
+++ b/drivers/video/msm/msm_fb_panel.c
@@ -0,0 +1,148 @@
+/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/uaccess.h>
+#include <linux/workqueue.h>
+#include <linux/string.h>
+#include <linux/version.h>
+#include <linux/proc_fs.h>
+#include <linux/vmalloc.h>
+#include <linux/debugfs.h>
+
+#include "msm_fb_panel.h"
+
+int panel_next_on(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct msm_fb_panel_data *pdata;
+	struct msm_fb_panel_data *next_pdata;
+	struct platform_device *next_pdev;
+
+	pdata = (struct msm_fb_panel_data *)pdev->dev.platform_data;
+
+	if (pdata) {
+		next_pdev = pdata->next;
+		if (next_pdev) {
+			next_pdata =
+			    (struct msm_fb_panel_data *)next_pdev->dev.
+			    platform_data;
+			if ((next_pdata) && (next_pdata->on))
+				ret = next_pdata->on(next_pdev);
+		}
+	}
+
+	return ret;
+}
+
+int panel_next_off(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct msm_fb_panel_data *pdata;
+	struct msm_fb_panel_data *next_pdata;
+	struct platform_device *next_pdev;
+
+	pdata = (struct msm_fb_panel_data *)pdev->dev.platform_data;
+
+	if (pdata) {
+		next_pdev = pdata->next;
+		if (next_pdev) {
+			next_pdata =
+			    (struct msm_fb_panel_data *)next_pdev->dev.
+			    platform_data;
+			if ((next_pdata) && (next_pdata->on))
+				ret = next_pdata->off(next_pdev);
+		}
+	}
+
+	return ret;
+}
+
+struct platform_device *msm_fb_device_alloc(struct msm_fb_panel_data *pdata,
+						u32 type, u32 id)
+{
+	struct platform_device *this_dev = NULL;
+	char dev_name[16];
+
+	switch (type) {
+	case EBI2_PANEL:
+		snprintf(dev_name, sizeof(dev_name), "ebi2_lcd");
+		break;
+
+	case MDDI_PANEL:
+		snprintf(dev_name, sizeof(dev_name), "mddi");
+		break;
+
+	case EXT_MDDI_PANEL:
+		snprintf(dev_name, sizeof(dev_name), "mddi_ext");
+		break;
+
+	case TV_PANEL:
+		snprintf(dev_name, sizeof(dev_name), "tvenc");
+		break;
+
+	case HDMI_PANEL:
+	case LCDC_PANEL:
+		snprintf(dev_name, sizeof(dev_name), "lcdc");
+		break;
+
+	case LVDS_PANEL:
+		snprintf(dev_name, sizeof(dev_name), "lvds");
+		break;
+
+	case DTV_PANEL:
+		snprintf(dev_name, sizeof(dev_name), "dtv");
+		break;
+
+	case MIPI_VIDEO_PANEL:
+	case MIPI_CMD_PANEL:
+		snprintf(dev_name, sizeof(dev_name), "mipi_dsi");
+		break;
+	case WRITEBACK_PANEL:
+		snprintf(dev_name, sizeof(dev_name), "writeback");
+		break;
+
+	default:
+		return NULL;
+	}
+
+	if (pdata != NULL)
+		pdata->next = NULL;
+	else
+		return NULL;
+
+	this_dev =
+	    platform_device_alloc(dev_name, ((u32) type << 16) | (u32) id);
+
+	if (this_dev) {
+		if (platform_device_add_data
+		    (this_dev, pdata, sizeof(struct msm_fb_panel_data))) {
+			printk
+			    ("msm_fb_device_alloc: platform_device_add_data failed!\n");
+			platform_device_put(this_dev);
+			return NULL;
+		}
+	}
+
+	return this_dev;
+}
diff --git a/drivers/video/msm/msm_fb_panel.h b/drivers/video/msm/msm_fb_panel.h
new file mode 100644
index 0000000..9d16b7b
--- /dev/null
+++ b/drivers/video/msm/msm_fb_panel.h
@@ -0,0 +1,211 @@
+/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef MSM_FB_PANEL_H
+#define MSM_FB_PANEL_H
+
+#include "msm_fb_def.h"
+
+struct msm_fb_data_type;
+
+typedef void (*msm_fb_vsync_handler_type) (void *arg);
+
+/* panel id type */
+typedef struct panel_id_s {
+	uint16 id;
+	uint16 type;
+} panel_id_type;
+
+/* panel type list */
+#define NO_PANEL		0xffff	/* No Panel */
+#define MDDI_PANEL		1	/* MDDI */
+#define EBI2_PANEL		2	/* EBI2 */
+#define LCDC_PANEL		3	/* internal LCDC type */
+#define EXT_MDDI_PANEL		4	/* Ext.MDDI */
+#define TV_PANEL		5	/* TV */
+#define HDMI_PANEL		6	/* HDMI TV */
+#define DTV_PANEL		7	/* DTV */
+#define MIPI_VIDEO_PANEL	8	/* MIPI */
+#define MIPI_CMD_PANEL		9	/* MIPI */
+#define WRITEBACK_PANEL		10	/* Wifi display */
+#define LVDS_PANEL		11	/* LVDS */
+
+/* panel class */
+typedef enum {
+	DISPLAY_LCD = 0,	/* lcd = ebi2/mddi */
+	DISPLAY_LCDC,		/* lcdc */
+	DISPLAY_TV,		/* TV Out */
+	DISPLAY_EXT_MDDI,	/* External MDDI */
+} DISP_TARGET;
+
+/* panel device locaiton */
+typedef enum {
+	DISPLAY_1 = 0,		/* attached as first device */
+	DISPLAY_2,		/* attached on second device */
+	DISPLAY_3,              /* attached on third writeback device */
+	MAX_PHYS_TARGET_NUM,
+} DISP_TARGET_PHYS;
+
+/* panel info type */
+struct lcd_panel_info {
+	__u32 vsync_enable;
+	__u32 refx100;
+	__u32 v_back_porch;
+	__u32 v_front_porch;
+	__u32 v_pulse_width;
+	__u32 hw_vsync_mode;
+	__u32 vsync_notifier_period;
+	__u32 rev;
+};
+
+struct lcdc_panel_info {
+	__u32 h_back_porch;
+	__u32 h_front_porch;
+	__u32 h_pulse_width;
+	__u32 v_back_porch;
+	__u32 v_front_porch;
+	__u32 v_pulse_width;
+	__u32 border_clr;
+	__u32 underflow_clr;
+	__u32 hsync_skew;
+	/* Pad width */
+	uint32 xres_pad;
+	/* Pad height */
+	uint32 yres_pad;
+};
+
+struct mddi_panel_info {
+	__u32 vdopkt;
+	boolean is_type1;
+};
+
+struct mipi_panel_info {
+	char mode;		/* video/cmd */
+	char interleave_mode;
+	char crc_check;
+	char ecc_check;
+	char dst_format;	/* shared by video and command */
+	char data_lane0;
+	char data_lane1;
+	char data_lane2;
+	char data_lane3;
+	char dlane_swap;	/* data lane swap */
+	char rgb_swap;
+	char b_sel;
+	char g_sel;
+	char r_sel;
+	char rx_eot_ignore;
+	char tx_eot_append;
+	char t_clk_post; /* 0xc0, DSI_CLKOUT_TIMING_CTRL */
+	char t_clk_pre;  /* 0xc0, DSI_CLKOUT_TIMING_CTRL */
+	char vc;	/* virtual channel */
+	struct mipi_dsi_phy_ctrl *dsi_phy_db;
+	/* video mode */
+	char pulse_mode_hsa_he;
+	char hfp_power_stop;
+	char hbp_power_stop;
+	char hsa_power_stop;
+	char eof_bllp_power_stop;
+	char bllp_power_stop;
+	char traffic_mode;
+	char frame_rate;
+	/* command mode */
+	char interleave_max;
+	char insert_dcs_cmd;
+	char wr_mem_continue;
+	char wr_mem_start;
+	char te_sel;
+	char stream;	/* 0 or 1 */
+	char mdp_trigger;
+	char dma_trigger;
+	uint32 dsi_pclk_rate;
+	/* The packet-size should not bet changed */
+	char no_max_pkt_size;
+	/* Clock required during LP commands */
+	char force_clk_lane_hs;
+};
+
+enum lvds_mode {
+	LVDS_SINGLE_CHANNEL_MODE,
+	LVDS_DUAL_CHANNEL_MODE,
+};
+
+struct lvds_panel_info {
+	enum lvds_mode channel_mode;
+	/* Channel swap in dual mode */
+	char channel_swap;
+};
+
+struct msm_panel_info {
+	__u32 xres;
+	__u32 yres;
+	__u32 bpp;
+	__u32 mode2_xres;
+	__u32 mode2_yres;
+	__u32 mode2_bpp;
+	__u32 type;
+	__u32 wait_cycle;
+	DISP_TARGET_PHYS pdest;
+	__u32 bl_max;
+	__u32 bl_min;
+	__u32 fb_num;
+	__u32 clk_rate;
+	__u32 clk_min;
+	__u32 clk_max;
+	__u32 frame_count;
+	__u32 is_3d_panel;
+	__u32 frame_rate;
+
+
+	struct mddi_panel_info mddi;
+	struct lcd_panel_info lcd;
+	struct lcdc_panel_info lcdc;
+	struct mipi_panel_info mipi;
+	struct lvds_panel_info lvds;
+};
+
+#define MSM_FB_SINGLE_MODE_PANEL(pinfo)		\
+	do {					\
+		(pinfo)->mode2_xres = 0;	\
+		(pinfo)->mode2_yres = 0;	\
+		(pinfo)->mode2_bpp = 0;		\
+	} while (0)
+
+struct msm_fb_panel_data {
+	struct msm_panel_info panel_info;
+	void (*set_rect) (int x, int y, int xres, int yres);
+	void (*set_vsync_notifier) (msm_fb_vsync_handler_type, void *arg);
+	void (*set_backlight) (struct msm_fb_data_type *);
+
+	/* function entry chain */
+	int (*on) (struct platform_device *pdev);
+	int (*off) (struct platform_device *pdev);
+	int (*power_ctrl) (boolean enable);
+	struct platform_device *next;
+	int (*clk_func) (int enable);
+};
+
+/*===========================================================================
+  FUNCTIONS PROTOTYPES
+============================================================================*/
+struct platform_device *msm_fb_device_alloc(struct msm_fb_panel_data *pdata,
+						u32 type, u32 id);
+int panel_next_on(struct platform_device *pdev);
+int panel_next_off(struct platform_device *pdev);
+
+int lcdc_device_register(struct msm_panel_info *pinfo);
+
+int mddi_toshiba_device_register(struct msm_panel_info *pinfo,
+					u32 channel, u32 panel);
+
+#endif /* MSM_FB_PANEL_H */
diff --git a/drivers/video/msm/tvenc.c b/drivers/video/msm/tvenc.c
new file mode 100644
index 0000000..30dc854
--- /dev/null
+++ b/drivers/video/msm/tvenc.c
@@ -0,0 +1,522 @@
+/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <mach/hardware.h>
+#include <linux/io.h>
+#include <linux/pm_runtime.h>
+
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <linux/semaphore.h>
+#include <linux/uaccess.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+#define TVENC_C
+#include "tvenc.h"
+#include "msm_fb.h"
+#include "mdp4.h"
+/* AXI rate in KHz */
+#define MSM_SYSTEM_BUS_RATE	128000000
+
+static int tvenc_probe(struct platform_device *pdev);
+static int tvenc_remove(struct platform_device *pdev);
+
+static int tvenc_off(struct platform_device *pdev);
+static int tvenc_on(struct platform_device *pdev);
+
+static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST];
+static int pdev_list_cnt;
+
+static struct clk *tvenc_clk;
+static struct clk *tvdac_clk;
+static struct clk *tvenc_pclk;
+static struct clk *mdp_tv_clk;
+#ifdef CONFIG_FB_MSM_MDP40
+static struct clk *tv_src_clk;
+#endif
+
+#ifdef CONFIG_MSM_BUS_SCALING
+static uint32_t tvenc_bus_scale_handle;
+#endif
+
+static int tvenc_runtime_suspend(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: suspending...\n");
+	return 0;
+}
+
+static int tvenc_runtime_resume(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: resuming...\n");
+	return 0;
+}
+
+static struct dev_pm_ops tvenc_dev_pm_ops = {
+	.runtime_suspend = tvenc_runtime_suspend,
+	.runtime_resume = tvenc_runtime_resume,
+};
+
+static struct platform_driver tvenc_driver = {
+	.probe = tvenc_probe,
+	.remove = tvenc_remove,
+	.suspend = NULL,
+	.resume = NULL,
+	.shutdown = NULL,
+	.driver = {
+		   .name = "tvenc",
+		   .pm = &tvenc_dev_pm_ops
+		   },
+};
+
+int tvenc_set_encoder_clock(boolean clock_on)
+{
+	int ret = 0;
+	if (clock_on) {
+#ifdef CONFIG_FB_MSM_MDP40
+		/* Consolidated clock used by both HDMI & TV encoder.
+		Clock exists only in MDP4 and not in older versions */
+		ret = clk_set_rate(tv_src_clk, 27000000);
+		if (ret) {
+			pr_err("%s: tvsrc_clk set rate failed! %d\n",
+				__func__, ret);
+			goto tvsrc_err;
+		}
+#endif
+		ret = clk_prepare_enable(tvenc_clk);
+		if (ret) {
+			pr_err("%s: tvenc_clk enable failed! %d\n",
+				__func__, ret);
+			goto tvsrc_err;
+		}
+
+		if (!IS_ERR(tvenc_pclk)) {
+			ret = clk_prepare_enable(tvenc_pclk);
+			if (ret) {
+				pr_err("%s: tvenc_pclk enable failed! %d\n",
+					__func__, ret);
+				goto tvencp_err;
+			}
+		}
+		return ret;
+	} else {
+		if (!IS_ERR(tvenc_pclk))
+			clk_disable_unprepare(tvenc_pclk);
+		clk_disable_unprepare(tvenc_clk);
+		return ret;
+	}
+tvencp_err:
+	clk_disable_unprepare(tvenc_clk);
+tvsrc_err:
+	return ret;
+}
+
+int tvenc_set_clock(boolean clock_on)
+{
+	int ret = 0;
+	if (clock_on) {
+		if (tvenc_pdata->poll) {
+			ret = tvenc_set_encoder_clock(CLOCK_ON);
+			if (ret) {
+				pr_err("%s: TVenc clock(s) enable failed! %d\n",
+					__func__, ret);
+				goto tvenc_err;
+			}
+		}
+		ret = clk_prepare_enable(tvdac_clk);
+		if (ret) {
+			pr_err("%s: tvdac_clk enable failed! %d\n",
+				__func__, ret);
+			goto tvdac_err;
+		}
+		if (!IS_ERR(mdp_tv_clk)) {
+			ret = clk_prepare_enable(mdp_tv_clk);
+			if (ret) {
+				pr_err("%s: mdp_tv_clk enable failed! %d\n",
+					__func__, ret);
+				goto mdptv_err;
+			}
+		}
+		return ret;
+	} else {
+		if (!IS_ERR(mdp_tv_clk))
+			clk_disable_unprepare(mdp_tv_clk);
+		clk_disable_unprepare(tvdac_clk);
+		if (tvenc_pdata->poll)
+			tvenc_set_encoder_clock(CLOCK_OFF);
+		return ret;
+	}
+
+mdptv_err:
+	clk_disable_unprepare(tvdac_clk);
+tvdac_err:
+	tvenc_set_encoder_clock(CLOCK_OFF);
+tvenc_err:
+	return ret;
+}
+
+static int tvenc_off(struct platform_device *pdev)
+{
+	int ret = 0;
+
+	struct msm_fb_data_type *mfd;
+
+	mfd = platform_get_drvdata(pdev);
+
+	ret = panel_next_off(pdev);
+	if (ret)
+		pr_err("%s: tvout_off failed! %d\n",
+		__func__, ret);
+
+	tvenc_set_clock(CLOCK_OFF);
+
+	if (tvenc_pdata && tvenc_pdata->pm_vid_en)
+		ret = tvenc_pdata->pm_vid_en(0);
+#ifdef CONFIG_MSM_BUS_SCALING
+	if (tvenc_bus_scale_handle > 0)
+		msm_bus_scale_client_update_request(tvenc_bus_scale_handle,
+							0);
+#else
+	if (mfd->ebi1_clk)
+		clk_disable_unprepare(mfd->ebi1_clk);
+#endif
+
+	if (ret)
+		pr_err("%s: pm_vid_en(off) failed! %d\n",
+		__func__, ret);
+	mdp4_extn_disp = 0;
+	return ret;
+}
+
+static int tvenc_on(struct platform_device *pdev)
+{
+	int ret = 0;
+
+#ifndef CONFIG_MSM_BUS_SCALING
+	struct msm_fb_data_type *mfd = platform_get_drvdata(pdev);
+#endif
+
+#ifdef CONFIG_MSM_BUS_SCALING
+	if (tvenc_bus_scale_handle > 0)
+		msm_bus_scale_client_update_request(tvenc_bus_scale_handle,
+							1);
+#else
+	if (mfd->ebi1_clk)
+		clk_prepare_enable(mfd->ebi1_clk);
+#endif
+	mdp4_extn_disp = 1;
+	if (tvenc_pdata && tvenc_pdata->pm_vid_en)
+		ret = tvenc_pdata->pm_vid_en(1);
+	if (ret) {
+		pr_err("%s: pm_vid_en(on) failed! %d\n",
+		__func__, ret);
+		return ret;
+	}
+
+	ret = tvenc_set_clock(CLOCK_ON);
+	if (ret) {
+		pr_err("%s: tvenc_set_clock(CLOCK_ON) failed! %d\n",
+		__func__, ret);
+		tvenc_pdata->pm_vid_en(0);
+		goto error;
+	}
+
+	ret = panel_next_on(pdev);
+	if (ret) {
+		pr_err("%s: tvout_on failed! %d\n",
+		__func__, ret);
+		tvenc_set_clock(CLOCK_OFF);
+		tvenc_pdata->pm_vid_en(0);
+	}
+
+error:
+	return ret;
+
+}
+
+void tvenc_gen_test_pattern(struct msm_fb_data_type *mfd)
+{
+	uint32 reg = 0, i;
+
+	reg = readl(MSM_TV_ENC_CTL);
+	reg |= TVENC_CTL_TEST_PATT_EN;
+
+	for (i = 0; i < 3; i++) {
+		TV_OUT(TV_ENC_CTL, 0);	/* disable TV encoder */
+
+		switch (i) {
+			/*
+			 * TV Encoder - Color Bar Test Pattern
+			 */
+		case 0:
+			reg |= TVENC_CTL_TPG_CLRBAR;
+			break;
+			/*
+			 * TV Encoder - Red Frame Test Pattern
+			 */
+		case 1:
+			reg |= TVENC_CTL_TPG_REDCLR;
+			break;
+			/*
+			 * TV Encoder - Modulated Ramp Test Pattern
+			 */
+		default:
+			reg |= TVENC_CTL_TPG_MODRAMP;
+			break;
+		}
+
+		TV_OUT(TV_ENC_CTL, reg);
+		mdelay(5000);
+
+		switch (i) {
+			/*
+			 * TV Encoder - Color Bar Test Pattern
+			 */
+		case 0:
+			reg &= ~TVENC_CTL_TPG_CLRBAR;
+			break;
+			/*
+			 * TV Encoder - Red Frame Test Pattern
+			 */
+		case 1:
+			reg &= ~TVENC_CTL_TPG_REDCLR;
+			break;
+			/*
+			 * TV Encoder - Modulated Ramp Test Pattern
+			 */
+		default:
+			reg &= ~TVENC_CTL_TPG_MODRAMP;
+			break;
+		}
+	}
+}
+
+static int tvenc_resource_initialized;
+
+static int tvenc_probe(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+	struct platform_device *mdp_dev = NULL;
+	struct msm_fb_panel_data *pdata = NULL;
+	int rc, ret;
+	struct clk *ebi1_clk = NULL;
+
+	if (pdev->id == 0) {
+		tvenc_base = ioremap(pdev->resource[0].start,
+					pdev->resource[0].end -
+					pdev->resource[0].start + 1);
+		if (!tvenc_base) {
+			pr_err("tvenc_base ioremap failed!\n");
+			return -ENOMEM;
+		}
+
+		tvenc_clk = clk_get(&pdev->dev, "enc_clk");
+		tvdac_clk = clk_get(&pdev->dev, "dac_clk");
+		tvenc_pclk = clk_get(&pdev->dev, "iface_clk");
+		mdp_tv_clk = clk_get(&pdev->dev, "mdp_clk");
+
+#ifndef CONFIG_MSM_BUS_SCALING
+		ebi1_clk = clk_get(&pdev->dev, "mem_clk");
+		if (IS_ERR(ebi1_clk)) {
+			rc = PTR_ERR(ebi1_clk);
+			goto tvenc_probe_err;
+		}
+		clk_set_rate(ebi1_clk, MSM_SYSTEM_BUS_RATE);
+#endif
+
+#ifdef CONFIG_FB_MSM_MDP40
+		tv_src_clk = clk_get(&pdev->dev, "src_clk");
+		if (IS_ERR(tv_src_clk))
+			tv_src_clk = tvenc_clk; /* Fallback to slave */
+#endif
+
+		if (IS_ERR(tvenc_clk)) {
+			pr_err("%s: error: can't get tvenc_clk!\n", __func__);
+			return PTR_ERR(tvenc_clk);
+		}
+
+		if (IS_ERR(tvdac_clk)) {
+			pr_err("%s: error: can't get tvdac_clk!\n", __func__);
+			return PTR_ERR(tvdac_clk);
+		}
+
+		if (IS_ERR(tvenc_pclk)) {
+			ret = PTR_ERR(tvenc_pclk);
+			if (-ENOENT == ret)
+				pr_info("%s: tvenc_pclk does not exist!\n",
+								__func__);
+			else {
+				pr_err("%s: error: can't get tvenc_pclk!\n",
+								__func__);
+				return ret;
+			}
+		}
+
+		if (IS_ERR(mdp_tv_clk)) {
+			ret = PTR_ERR(mdp_tv_clk);
+			if (-ENOENT == ret)
+				pr_info("%s: mdp_tv_clk does not exist!\n",
+								__func__);
+			else {
+				pr_err("%s: error: can't get mdp_tv_clk!\n",
+								__func__);
+				return ret;
+			}
+		}
+
+		tvenc_pdata = pdev->dev.platform_data;
+		tvenc_resource_initialized = 1;
+		return 0;
+	}
+
+	if (!tvenc_resource_initialized)
+		return -EPERM;
+
+	mfd = platform_get_drvdata(pdev);
+	mfd->ebi1_clk = ebi1_clk;
+
+	if (!mfd)
+		return -ENODEV;
+
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST)
+		return -ENOMEM;
+
+	if (tvenc_base == NULL)
+		return -ENOMEM;
+
+	mdp_dev = platform_device_alloc("mdp", pdev->id);
+	if (!mdp_dev)
+		return -ENOMEM;
+
+	/*
+	 * link to the latest pdev
+	 */
+	mfd->pdev = mdp_dev;
+	mfd->dest = DISPLAY_TV;
+
+	/*
+	 * alloc panel device data
+	 */
+	if (platform_device_add_data
+	    (mdp_dev, pdev->dev.platform_data,
+	     sizeof(struct msm_fb_panel_data))) {
+		pr_err("tvenc_probe: platform_device_add_data failed!\n");
+		platform_device_put(mdp_dev);
+		return -ENOMEM;
+	}
+	/*
+	 * data chain
+	 */
+	pdata = mdp_dev->dev.platform_data;
+	pdata->on = tvenc_on;
+	pdata->off = tvenc_off;
+	pdata->next = pdev;
+
+	/*
+	 * get/set panel specific fb info
+	 */
+	mfd->panel_info = pdata->panel_info;
+#ifdef CONFIG_FB_MSM_MDP40
+	mfd->fb_imgType = MDP_RGB_565;  /* base layer */
+#else
+	mfd->fb_imgType = MDP_YCRYCB_H2V1;
+#endif
+
+#ifdef CONFIG_MSM_BUS_SCALING
+	if (!tvenc_bus_scale_handle && tvenc_pdata &&
+		tvenc_pdata->bus_scale_table) {
+		tvenc_bus_scale_handle =
+			msm_bus_scale_register_client(
+				tvenc_pdata->bus_scale_table);
+		if (!tvenc_bus_scale_handle) {
+			printk(KERN_ERR "%s not able to get bus scale\n",
+				__func__);
+		}
+	}
+#endif
+
+	/*
+	 * set driver data
+	 */
+	platform_set_drvdata(mdp_dev, mfd);
+
+	/*
+	 * register in mdp driver
+	 */
+	rc = platform_device_add(mdp_dev);
+	if (rc)
+		goto tvenc_probe_err;
+
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+
+
+
+	pdev_list[pdev_list_cnt++] = pdev;
+
+	return 0;
+
+tvenc_probe_err:
+#ifdef CONFIG_MSM_BUS_SCALING
+	if (tvenc_pdata && tvenc_pdata->bus_scale_table &&
+		tvenc_bus_scale_handle > 0) {
+		msm_bus_scale_unregister_client(tvenc_bus_scale_handle);
+		tvenc_bus_scale_handle = 0;
+	}
+#endif
+	platform_device_put(mdp_dev);
+	return rc;
+}
+
+static int tvenc_remove(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+
+	mfd = platform_get_drvdata(pdev);
+
+#ifdef CONFIG_MSM_BUS_SCALING
+	if (tvenc_pdata && tvenc_pdata->bus_scale_table &&
+		tvenc_bus_scale_handle > 0) {
+		msm_bus_scale_unregister_client(tvenc_bus_scale_handle);
+		tvenc_bus_scale_handle = 0;
+	}
+#else
+	clk_put(mfd->ebi1_clk);
+#endif
+
+	pm_runtime_disable(&pdev->dev);
+	return 0;
+}
+
+static int tvenc_register_driver(void)
+{
+	return platform_driver_register(&tvenc_driver);
+}
+
+static int __init tvenc_driver_init(void)
+{
+	return tvenc_register_driver();
+}
+
+module_init(tvenc_driver_init);
diff --git a/drivers/video/msm/tvenc.h b/drivers/video/msm/tvenc.h
new file mode 100644
index 0000000..c64c160
--- /dev/null
+++ b/drivers/video/msm/tvenc.h
@@ -0,0 +1,129 @@
+/* Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef TVENC_H
+#define TVENC_H
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/fb.h>
+
+#include <mach/hardware.h>
+#include <linux/io.h>
+
+#include <asm/system.h>
+#include <asm/mach-types.h>
+
+#include "msm_fb_panel.h"
+
+#define NTSC_M		0 /* North America, Korea */
+#define NTSC_J		1 /* Japan */
+#define PAL_BDGHIN	2 /* Non-argentina PAL-N */
+#define PAL_M		3 /* PAL-M */
+#define PAL_N		4 /* Argentina PAL-N */
+
+#define CLOCK_OFF	0
+#define CLOCK_ON	1
+
+/* 3.57954545 Mhz */
+#define TVENC_CTL_TV_MODE_NTSC_M_PAL60  0
+/* 3.57961149 Mhz */
+#define TVENC_CTL_TV_MODE_PAL_M			BIT(0)
+/*non-Argintina = 4.3361875 Mhz */
+#define TVENC_CTL_TV_MODE_PAL_BDGHIN		BIT(1)
+/*Argentina = 3.582055625 Mhz */
+#define TVENC_CTL_TV_MODE_PAL_N			(BIT(1)|BIT(0))
+
+#define TVENC_CTL_ENC_EN			BIT(2)
+#define TVENC_CTL_CC_EN				BIT(3)
+#define TVENC_CTL_CGMS_EN			BIT(4)
+#define TVENC_CTL_MACRO_EN			BIT(5)
+#define TVENC_CTL_Y_FILTER_W_NOTCH		BIT(6)
+#define TVENC_CTL_Y_FILTER_WO_NOTCH		0
+#define TVENC_CTL_Y_FILTER_EN			BIT(7)
+#define TVENC_CTL_CR_FILTER_EN			BIT(8)
+#define TVENC_CTL_CB_FILTER_EN			BIT(9)
+#define TVENC_CTL_SINX_FILTER_EN		BIT(10)
+#define TVENC_CTL_TEST_PATT_EN			BIT(11)
+#define TVENC_CTL_OUTPUT_INV			BIT(12)
+#define TVENC_CTL_PAL60_MODE			BIT(13)
+#define TVENC_CTL_NTSCJ_MODE			BIT(14)
+#define TVENC_CTL_S_VIDEO_EN			BIT(19)
+
+
+#define TVENC_CTL_TPG_CLRBAR			0
+#define TVENC_CTL_TPG_MODRAMP			BIT(15)
+#define TVENC_CTL_TPG_REDCLR			BIT(16)
+#define TVENC_CTL_TPG_NTSC_CBAR			(BIT(16)|BIT(15))
+#define TVENC_CTL_TPG_BLACK			BIT(17)
+#define TVENC_CTL_TPG_WHITE100			(BIT(17)|BIT(15))
+#define TVENC_CTL_TPG_YELLOW75			(BIT(17)|BIT(16))
+#define TVENC_CTL_TPG_CYAN75			(BIT(17)|BIT(16)|BIT(15))
+#define TVENC_CTL_TPG_GREEN75			BIT(18)
+#define TVENC_CTL_TPG_MAGENTA75			(BIT(18)|BIT(15))
+#define TVENC_CTL_TPG_RED75			(BIT(18)|BIT(16))
+#define TVENC_CTL_TPG_BLUE75			(BIT(18)|BIT(16)|BIT(15))
+#define TVENC_CTL_TPG_WHITE75			(BIT(18)|BIT(17))
+#define TVENC_CTL_TPG_WHITE_TRSTN		(BIT(18)|BIT(17)|BIT(15))
+
+#define TVENC_LOAD_DETECT_EN			BIT(8)
+
+#ifdef TVENC_C
+void *tvenc_base;
+struct tvenc_platform_data *tvenc_pdata;
+#else
+extern void *tvenc_base;
+extern struct tvenc_platform_data *tvenc_pdata;
+#endif
+
+#define TV_OUT(reg, v)		writel(v, tvenc_base + MSM_##reg)
+#define TV_IN(reg)		readl(tvenc_base + MSM_##reg)
+
+#define MSM_TV_ENC_CTL				0x00
+#define MSM_TV_LEVEL				0x04
+#define MSM_TV_GAIN				0x08
+#define MSM_TV_OFFSET				0x0c
+#define MSM_TV_CGMS				0x10
+#define MSM_TV_SYNC_1				0x14
+#define MSM_TV_SYNC_2				0x18
+#define MSM_TV_SYNC_3				0x1c
+#define MSM_TV_SYNC_4				0x20
+#define MSM_TV_SYNC_5				0x24
+#define MSM_TV_SYNC_6				0x28
+#define MSM_TV_SYNC_7				0x2c
+#define MSM_TV_BURST_V1				0x30
+#define MSM_TV_BURST_V2				0x34
+#define MSM_TV_BURST_V3				0x38
+#define MSM_TV_BURST_V4				0x3c
+#define MSM_TV_BURST_H				0x40
+#define MSM_TV_SOL_REQ_ODD			0x44
+#define MSM_TV_SOL_REQ_EVEN			0x48
+#define MSM_TV_DAC_CTL				0x4c
+#define MSM_TV_TEST_MUX				0x50
+#define MSM_TV_TEST_MODE			0x54
+#define MSM_TV_TEST_MISR_RESET			0x58
+#define MSM_TV_TEST_EXPORT_MISR			0x5c
+#define MSM_TV_TEST_MISR_CURR_VAL		0x60
+#define MSM_TV_TEST_SOF_CFG			0x64
+#define MSM_TV_DAC_INTF				0x100
+
+#define MSM_TV_INTR_ENABLE			0x200
+#define MSM_TV_INTR_STATUS			0x204
+#define MSM_TV_INTR_CLEAR			0x208
+
+int tvenc_set_encoder_clock(boolean clock_on);
+int tvenc_set_clock(boolean clock_on);
+#endif /* TVENC_H */
diff --git a/drivers/video/msm/tvout_msm.c b/drivers/video/msm/tvout_msm.c
new file mode 100644
index 0000000..9ee55cc
--- /dev/null
+++ b/drivers/video/msm/tvout_msm.c
@@ -0,0 +1,652 @@
+/* Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+
+#include "msm_fb.h"
+#include "tvenc.h"
+#include "external_common.h"
+
+#define TVOUT_HPD_DUTY_CYCLE 3000
+
+#define TV_DIMENSION_MAX_WIDTH		720
+#define TV_DIMENSION_MAX_HEIGHT		576
+
+struct tvout_msm_state_type {
+	struct external_common_state_type common;
+	struct platform_device *pdev;
+	struct timer_list hpd_state_timer;
+	struct timer_list hpd_work_timer;
+	struct work_struct hpd_work;
+	uint32 hpd_int_status;
+	uint32 prev_hpd_int_status;
+	uint32 five_retry;
+	int irq;
+	uint16 y_res;
+	boolean hpd_initialized;
+	boolean disp_powered_up;
+#ifdef CONFIG_SUSPEND
+	boolean pm_suspended;
+#endif
+
+};
+
+static struct tvout_msm_state_type *tvout_msm_state;
+static DEFINE_MUTEX(tvout_msm_state_mutex);
+
+static int tvout_off(struct platform_device *pdev);
+static int tvout_on(struct platform_device *pdev);
+static void tvout_check_status(void);
+
+static void tvout_msm_turn_on(boolean power_on)
+{
+	uint32 reg_val = 0;
+	reg_val = TV_IN(TV_ENC_CTL);
+	if (power_on) {
+		DEV_DBG("%s: TV Encoder turned on\n", __func__);
+		reg_val |= TVENC_CTL_ENC_EN;
+	} else {
+		DEV_DBG("%s: TV Encoder turned off\n", __func__);
+		reg_val = 0;
+	}
+	/* Enable TV Encoder*/
+	TV_OUT(TV_ENC_CTL, reg_val);
+}
+
+static void tvout_check_status()
+{
+	tvout_msm_state->hpd_int_status &= 0x05;
+	/* hpd_int_status could either be 0x05 or 0x04 for a cable
+		plug-out event when cable detect is driven by polling. */
+	if ((((tvout_msm_state->hpd_int_status == 0x05) ||
+		(tvout_msm_state->hpd_int_status == 0x04)) &&
+		(tvout_msm_state->prev_hpd_int_status == BIT(2))) ||
+		((tvout_msm_state->hpd_int_status == 0x01) &&
+		(tvout_msm_state->prev_hpd_int_status == BIT(0)))) {
+		DEV_DBG("%s: cable event sent already!", __func__);
+		return;
+	}
+
+	if (tvout_msm_state->hpd_int_status & BIT(2)) {
+		DEV_DBG("%s: cable plug-out\n", __func__);
+		mutex_lock(&external_common_state_hpd_mutex);
+		external_common_state->hpd_state = FALSE;
+		mutex_unlock(&external_common_state_hpd_mutex);
+		kobject_uevent(external_common_state->uevent_kobj,
+				KOBJ_OFFLINE);
+		tvout_msm_state->prev_hpd_int_status = BIT(2);
+	} else if (tvout_msm_state->hpd_int_status & BIT(0)) {
+		DEV_DBG("%s: cable plug-in\n", __func__);
+		mutex_lock(&external_common_state_hpd_mutex);
+		external_common_state->hpd_state = TRUE;
+		mutex_unlock(&external_common_state_hpd_mutex);
+		kobject_uevent(external_common_state->uevent_kobj,
+				KOBJ_ONLINE);
+		tvout_msm_state->prev_hpd_int_status = BIT(0);
+	}
+}
+
+/* ISR for TV out cable detect */
+static irqreturn_t tvout_msm_isr(int irq, void *dev_id)
+{
+	tvout_msm_state->hpd_int_status = TV_IN(TV_INTR_STATUS);
+	TV_OUT(TV_INTR_CLEAR, tvout_msm_state->hpd_int_status);
+	DEV_DBG("%s: ISR: 0x%02x\n", __func__,
+		tvout_msm_state->hpd_int_status & 0x05);
+
+	if (tvenc_pdata->poll)
+		if (!tvout_msm_state || !tvout_msm_state->disp_powered_up) {
+			DEV_DBG("%s: ISR ignored, display not yet powered on\n",
+				__func__);
+			return IRQ_HANDLED;
+		}
+	if (tvout_msm_state->hpd_int_status & BIT(0) ||
+		tvout_msm_state->hpd_int_status & BIT(2)) {
+		/* Use .75sec to debounce the interrupt */
+		mod_timer(&tvout_msm_state->hpd_state_timer, jiffies
+			+ msecs_to_jiffies(750));
+	}
+
+	return IRQ_HANDLED;
+}
+
+/* Interrupt debounce timer */
+static void tvout_msm_hpd_state_timer(unsigned long data)
+{
+#ifdef CONFIG_SUSPEND
+	mutex_lock(&tvout_msm_state_mutex);
+	if (tvout_msm_state->pm_suspended) {
+		mutex_unlock(&tvout_msm_state_mutex);
+		DEV_WARN("%s: ignored, pm_suspended\n", __func__);
+		return;
+	}
+	mutex_unlock(&tvout_msm_state_mutex);
+#endif
+
+	if (tvenc_pdata->poll)
+		if (!tvout_msm_state || !tvout_msm_state->disp_powered_up) {
+			DEV_DBG("%s: ignored, display powered off\n", __func__);
+			return;
+		}
+
+	/* TV_INTR_STATUS[0x204]
+		When a TV_ENC interrupt occurs, then reading this register will
+		indicate what caused the interrupt since that each bit indicates
+		the source of the interrupt that had happened. If multiple
+		interrupt sources had happened, then multiple bits of this
+		register will be set
+		Bit 0 : Load present on Video1
+		Bit 1 : Load present on Video2
+		Bit 2 : Load removed on Video1
+		Bit 3 : Load removed on Video2
+	*/
+
+	/* Locking interrupt status is not required because
+	last status read after debouncing is used */
+	if ((tvout_msm_state->hpd_int_status & 0x05) == 0x05) {
+		/* SW-workaround :If the status read after debouncing is
+		0x05(indicating both load present & load removed- which can't
+		happen in reality), force an update. If status remains 0x05
+		after retry, it's a cable unplug event */
+		if (++tvout_msm_state->five_retry < 2) {
+			uint32 reg;
+			DEV_DBG("tvout: Timer: 0x05\n");
+			TV_OUT(TV_INTR_CLEAR, 0xf);
+			reg = TV_IN(TV_DAC_INTF);
+			TV_OUT(TV_DAC_INTF, reg & ~TVENC_LOAD_DETECT_EN);
+			TV_OUT(TV_INTR_CLEAR, 0xf);
+			reg = TV_IN(TV_DAC_INTF);
+			TV_OUT(TV_DAC_INTF, reg | TVENC_LOAD_DETECT_EN);
+			return;
+		}
+	}
+	tvout_msm_state->five_retry = 0;
+	tvout_check_status();
+}
+
+static void tvout_msm_hpd_work(struct work_struct *work)
+{
+	uint32 reg;
+
+#ifdef CONFIG_SUSPEND
+	mutex_lock(&tvout_msm_state_mutex);
+	if (tvout_msm_state->pm_suspended) {
+		mutex_unlock(&tvout_msm_state_mutex);
+		DEV_WARN("%s: ignored, pm_suspended\n", __func__);
+		return;
+	}
+	mutex_unlock(&tvout_msm_state_mutex);
+#endif
+
+	/* Enable power lines & clocks */
+	tvenc_pdata->pm_vid_en(1);
+	tvenc_set_clock(CLOCK_ON);
+
+	/* Enable encoder to get a stable interrupt */
+	reg = TV_IN(TV_ENC_CTL);
+	TV_OUT(TV_ENC_CTL, reg | TVENC_CTL_ENC_EN);
+
+	/* SW- workaround to update status register */
+	reg = TV_IN(TV_DAC_INTF);
+	TV_OUT(TV_DAC_INTF, reg & ~TVENC_LOAD_DETECT_EN);
+	TV_OUT(TV_INTR_CLEAR, 0xf);
+	reg = TV_IN(TV_DAC_INTF);
+	TV_OUT(TV_DAC_INTF, reg | TVENC_LOAD_DETECT_EN);
+
+	tvout_msm_state->hpd_int_status = TV_IN(TV_INTR_STATUS);
+
+	/* Disable TV encoder */
+	reg = TV_IN(TV_ENC_CTL);
+	TV_OUT(TV_ENC_CTL, reg & ~TVENC_CTL_ENC_EN);
+
+	/*Disable power lines & clocks */
+	tvenc_set_clock(CLOCK_OFF);
+	tvenc_pdata->pm_vid_en(0);
+
+	DEV_DBG("%s: ISR: 0x%02x\n", __func__,
+		tvout_msm_state->hpd_int_status & 0x05);
+
+	mod_timer(&tvout_msm_state->hpd_work_timer, jiffies
+		+ msecs_to_jiffies(TVOUT_HPD_DUTY_CYCLE));
+
+	tvout_check_status();
+}
+
+static void tvout_msm_hpd_work_timer(unsigned long data)
+{
+	schedule_work(&tvout_msm_state->hpd_work);
+}
+
+static int tvout_on(struct platform_device *pdev)
+{
+	uint32 reg = 0;
+	struct fb_var_screeninfo *var;
+	struct msm_fb_data_type *mfd = platform_get_drvdata(pdev);
+
+	if (!mfd)
+		return -ENODEV;
+
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+#ifdef CONFIG_SUSPEND
+	mutex_lock(&tvout_msm_state_mutex);
+	if (tvout_msm_state->pm_suspended) {
+		mutex_unlock(&tvout_msm_state_mutex);
+		DEV_WARN("%s: ignored, pm_suspended\n", __func__);
+		return -ENODEV;
+	}
+	mutex_unlock(&tvout_msm_state_mutex);
+#endif
+
+	var = &mfd->fbi->var;
+	if (var->reserved[3] >= NTSC_M && var->reserved[3] <= PAL_N)
+		external_common_state->video_resolution = var->reserved[3];
+
+	tvout_msm_state->pdev = pdev;
+	if (del_timer(&tvout_msm_state->hpd_work_timer))
+		DEV_DBG("%s: work timer stopped\n", __func__);
+
+	TV_OUT(TV_ENC_CTL, 0);	/* disable TV encoder */
+
+	switch (external_common_state->video_resolution) {
+	case NTSC_M:
+	case NTSC_J:
+		TV_OUT(TV_CGMS, 0x0);
+		/*  NTSC Timing */
+		TV_OUT(TV_SYNC_1, 0x0020009e);
+		TV_OUT(TV_SYNC_2, 0x011306B4);
+		TV_OUT(TV_SYNC_3, 0x0006000C);
+		TV_OUT(TV_SYNC_4, 0x0028020D);
+		TV_OUT(TV_SYNC_5, 0x005E02FB);
+		TV_OUT(TV_SYNC_6, 0x0006000C);
+		TV_OUT(TV_SYNC_7, 0x00000012);
+		TV_OUT(TV_BURST_V1, 0x0013020D);
+		TV_OUT(TV_BURST_V2, 0x0014020C);
+		TV_OUT(TV_BURST_V3, 0x0013020D);
+		TV_OUT(TV_BURST_V4, 0x0014020C);
+		TV_OUT(TV_BURST_H, 0x00AE00F2);
+		TV_OUT(TV_SOL_REQ_ODD, 0x00280208);
+		TV_OUT(TV_SOL_REQ_EVEN, 0x00290209);
+
+		reg |= TVENC_CTL_TV_MODE_NTSC_M_PAL60;
+
+		if (external_common_state->video_resolution == NTSC_M) {
+			/* Cr gain 11, Cb gain C6, y_gain 97 */
+			TV_OUT(TV_GAIN, 0x0081B697);
+		} else {
+			/* Cr gain 11, Cb gain C6, y_gain 97 */
+			TV_OUT(TV_GAIN, 0x008bc4a3);
+			reg |= TVENC_CTL_NTSCJ_MODE;
+		}
+
+		var->yres = 480;
+		break;
+	case PAL_BDGHIN:
+	case PAL_N:
+		/*  PAL Timing */
+		TV_OUT(TV_SYNC_1, 0x00180097);
+		TV_OUT(TV_SYNC_3, 0x0005000a);
+		TV_OUT(TV_SYNC_4, 0x00320271);
+		TV_OUT(TV_SYNC_5, 0x005602f9);
+		TV_OUT(TV_SYNC_6, 0x0005000a);
+		TV_OUT(TV_SYNC_7, 0x0000000f);
+		TV_OUT(TV_BURST_V1, 0x0012026e);
+		TV_OUT(TV_BURST_V2, 0x0011026d);
+		TV_OUT(TV_BURST_V3, 0x00100270);
+		TV_OUT(TV_BURST_V4, 0x0013026f);
+		TV_OUT(TV_SOL_REQ_ODD, 0x0030026e);
+		TV_OUT(TV_SOL_REQ_EVEN, 0x0031026f);
+
+		if (external_common_state->video_resolution == PAL_BDGHIN) {
+			/* Cr gain 11, Cb gain C6, y_gain 97 */
+			TV_OUT(TV_GAIN, 0x0088c1a0);
+			TV_OUT(TV_CGMS, 0x00012345);
+			TV_OUT(TV_SYNC_2, 0x011f06c0);
+			TV_OUT(TV_BURST_H, 0x00af00ea);
+			reg |= TVENC_CTL_TV_MODE_PAL_BDGHIN;
+		} else {
+			/* Cr gain 11, Cb gain C6, y_gain 97 */
+			TV_OUT(TV_GAIN, 0x0081b697);
+			TV_OUT(TV_CGMS, 0x000af317);
+			TV_OUT(TV_SYNC_2, 0x12006c0);
+			TV_OUT(TV_BURST_H, 0x00af00fa);
+			reg |= TVENC_CTL_TV_MODE_PAL_N;
+		}
+		var->yres = 576;
+		break;
+	case PAL_M:
+		/* Cr gain 11, Cb gain C6, y_gain 97 */
+		TV_OUT(TV_GAIN, 0x0081b697);
+		TV_OUT(TV_CGMS, 0x000af317);
+		TV_OUT(TV_TEST_MUX, 0x000001c3);
+		TV_OUT(TV_TEST_MODE, 0x00000002);
+		/*  PAL Timing */
+		TV_OUT(TV_SYNC_1, 0x0020009e);
+		TV_OUT(TV_SYNC_2, 0x011306b4);
+		TV_OUT(TV_SYNC_3, 0x0006000c);
+		TV_OUT(TV_SYNC_4, 0x0028020D);
+		TV_OUT(TV_SYNC_5, 0x005e02fb);
+		TV_OUT(TV_SYNC_6, 0x0006000c);
+		TV_OUT(TV_SYNC_7, 0x00000012);
+		TV_OUT(TV_BURST_V1, 0x0012020b);
+		TV_OUT(TV_BURST_V2, 0x0016020c);
+		TV_OUT(TV_BURST_V3, 0x00150209);
+		TV_OUT(TV_BURST_V4, 0x0013020c);
+		TV_OUT(TV_BURST_H, 0x00bf010b);
+		TV_OUT(TV_SOL_REQ_ODD, 0x00280208);
+		TV_OUT(TV_SOL_REQ_EVEN, 0x00290209);
+
+		reg |= TVENC_CTL_TV_MODE_PAL_M;
+		var->yres = 480;
+		break;
+	default:
+		return -ENODEV;
+	}
+
+	reg |= TVENC_CTL_Y_FILTER_EN | TVENC_CTL_CR_FILTER_EN |
+		TVENC_CTL_CB_FILTER_EN | TVENC_CTL_SINX_FILTER_EN;
+
+	/* DC offset to 0. */
+	TV_OUT(TV_LEVEL, 0x00000000);
+	TV_OUT(TV_OFFSET, 0x008080f0);
+
+#ifdef CONFIG_FB_MSM_TVOUT_SVIDEO
+	reg |= TVENC_CTL_S_VIDEO_EN;
+#endif
+#if defined(CONFIG_FB_MSM_MDP31)
+	TV_OUT(TV_DAC_INTF, 0x29);
+#endif
+	TV_OUT(TV_ENC_CTL, reg);
+
+	if (!tvout_msm_state->hpd_initialized) {
+		tvout_msm_state->hpd_initialized = TRUE;
+		/* Load detect enable */
+		reg = TV_IN(TV_DAC_INTF);
+		reg |= TVENC_LOAD_DETECT_EN;
+		TV_OUT(TV_DAC_INTF, reg);
+	}
+
+	tvout_msm_state->disp_powered_up = TRUE;
+	tvout_msm_turn_on(TRUE);
+
+	if (tvenc_pdata->poll) {
+		/* Enable Load present & removal interrupts for Video1 */
+		TV_OUT(TV_INTR_ENABLE, 0x5);
+
+		/* Enable interrupts when display is on */
+		enable_irq(tvout_msm_state->irq);
+	}
+	return 0;
+}
+
+static int tvout_off(struct platform_device *pdev)
+{
+	/* Disable TV encoder irqs when display is off */
+	if (tvenc_pdata->poll)
+		disable_irq(tvout_msm_state->irq);
+	tvout_msm_turn_on(FALSE);
+	tvout_msm_state->hpd_initialized = FALSE;
+	tvout_msm_state->disp_powered_up = FALSE;
+	if (tvenc_pdata->poll) {
+		mod_timer(&tvout_msm_state->hpd_work_timer, jiffies
+			+ msecs_to_jiffies(TVOUT_HPD_DUTY_CYCLE));
+	}
+	return 0;
+}
+
+static int __devinit tvout_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	uint32 reg;
+	struct platform_device *fb_dev;
+
+#ifdef CONFIG_FB_MSM_TVOUT_NTSC_M
+	external_common_state->video_resolution = NTSC_M;
+#elif defined CONFIG_FB_MSM_TVOUT_NTSC_J
+	external_common_state->video_resolution = NTSC_J;
+#elif defined CONFIG_FB_MSM_TVOUT_PAL_M
+	external_common_state->video_resolution = PAL_M;
+#elif defined CONFIG_FB_MSM_TVOUT_PAL_N
+	external_common_state->video_resolution = PAL_N;
+#elif defined CONFIG_FB_MSM_TVOUT_PAL_BDGHIN
+	external_common_state->video_resolution = PAL_BDGHIN;
+#endif
+	external_common_state->dev = &pdev->dev;
+	if (pdev->id == 0) {
+		struct resource *res;
+
+		#define GET_RES(name, mode) do {			\
+			res = platform_get_resource_byname(pdev, mode, name); \
+			if (!res) {					\
+				DEV_DBG("'" name "' resource not found\n"); \
+				rc = -ENODEV;				\
+				goto error;				\
+			}						\
+		} while (0)
+
+		#define GET_IRQ(var, name) do {				\
+			GET_RES(name, IORESOURCE_IRQ);			\
+			var = res->start;				\
+		} while (0)
+
+		GET_IRQ(tvout_msm_state->irq, "tvout_device_irq");
+		#undef GET_IRQ
+		#undef GET_RES
+		return 0;
+	}
+
+	DEV_DBG("%s: tvout_msm_state->irq : %d",
+			__func__, tvout_msm_state->irq);
+
+	rc = request_irq(tvout_msm_state->irq, &tvout_msm_isr,
+		IRQF_TRIGGER_HIGH, "tvout_msm_isr", NULL);
+
+	if (rc) {
+		DEV_DBG("Init FAILED: IRQ request, rc=%d\n", rc);
+		goto error;
+	}
+	disable_irq(tvout_msm_state->irq);
+
+	init_timer(&tvout_msm_state->hpd_state_timer);
+	tvout_msm_state->hpd_state_timer.function =
+		tvout_msm_hpd_state_timer;
+	tvout_msm_state->hpd_state_timer.data = (uint32)NULL;
+	tvout_msm_state->hpd_state_timer.expires = jiffies
+						+ msecs_to_jiffies(1000);
+
+	if (tvenc_pdata->poll) {
+		init_timer(&tvout_msm_state->hpd_work_timer);
+		tvout_msm_state->hpd_work_timer.function =
+			tvout_msm_hpd_work_timer;
+		tvout_msm_state->hpd_work_timer.data = (uint32)NULL;
+		tvout_msm_state->hpd_work_timer.expires = jiffies
+						+ msecs_to_jiffies(1000);
+	}
+	fb_dev = msm_fb_add_device(pdev);
+	if (fb_dev) {
+		rc = external_common_state_create(fb_dev);
+		if (rc) {
+			DEV_ERR("Init FAILED: tvout_msm_state_create, rc=%d\n",
+				rc);
+			goto error;
+		}
+		if (tvenc_pdata->poll) {
+			/* Start polling timer to detect load */
+			mod_timer(&tvout_msm_state->hpd_work_timer, jiffies
+				+ msecs_to_jiffies(TVOUT_HPD_DUTY_CYCLE));
+		} else {
+			/* Enable interrupt to detect load */
+			tvenc_set_encoder_clock(CLOCK_ON);
+			reg = TV_IN(TV_DAC_INTF);
+			reg |= TVENC_LOAD_DETECT_EN;
+			TV_OUT(TV_DAC_INTF, reg);
+			TV_OUT(TV_INTR_ENABLE, 0x5);
+			enable_irq(tvout_msm_state->irq);
+		}
+	} else
+		DEV_ERR("Init FAILED: failed to add fb device\n");
+error:
+	return 0;
+}
+
+static int __devexit tvout_remove(struct platform_device *pdev)
+{
+	external_common_state_remove();
+	kfree(tvout_msm_state);
+	tvout_msm_state = NULL;
+	return 0;
+}
+
+#ifdef CONFIG_SUSPEND
+static int tvout_device_pm_suspend(struct device *dev)
+{
+	mutex_lock(&tvout_msm_state_mutex);
+	if (tvout_msm_state->pm_suspended) {
+		mutex_unlock(&tvout_msm_state_mutex);
+		return 0;
+	}
+	if (tvenc_pdata->poll) {
+		if (del_timer(&tvout_msm_state->hpd_work_timer))
+			DEV_DBG("%s: suspending cable detect timer\n",
+				__func__);
+	} else {
+		disable_irq(tvout_msm_state->irq);
+		tvenc_set_encoder_clock(CLOCK_OFF);
+	}
+	tvout_msm_state->pm_suspended = TRUE;
+	mutex_unlock(&tvout_msm_state_mutex);
+	return 0;
+}
+
+static int tvout_device_pm_resume(struct device *dev)
+{
+	mutex_lock(&tvout_msm_state_mutex);
+	if (!tvout_msm_state->pm_suspended) {
+		mutex_unlock(&tvout_msm_state_mutex);
+		return 0;
+	}
+
+	if (tvenc_pdata->poll) {
+		tvout_msm_state->pm_suspended = FALSE;
+		mod_timer(&tvout_msm_state->hpd_work_timer, jiffies
+				+ msecs_to_jiffies(TVOUT_HPD_DUTY_CYCLE));
+		mutex_unlock(&tvout_msm_state_mutex);
+		DEV_DBG("%s: resuming cable detect timer\n", __func__);
+	} else {
+		tvenc_set_encoder_clock(CLOCK_ON);
+		tvout_msm_state->pm_suspended = FALSE;
+		mutex_unlock(&tvout_msm_state_mutex);
+		enable_irq(tvout_msm_state->irq);
+		DEV_DBG("%s: enable cable detect interrupt\n", __func__);
+	}
+	return 0;
+}
+#else
+#define tvout_device_pm_suspend	NULL
+#define tvout_device_pm_resume		NULL
+#endif
+
+
+static const struct dev_pm_ops tvout_device_pm_ops = {
+	.suspend = tvout_device_pm_suspend,
+	.resume = tvout_device_pm_resume,
+};
+
+static struct platform_driver this_driver = {
+	.probe  = tvout_probe,
+	.remove = tvout_remove,
+	.driver = {
+		.name	= "tvout_device",
+		.pm	= &tvout_device_pm_ops,
+	},
+};
+
+static struct msm_fb_panel_data tvout_panel_data = {
+	.panel_info.xres = TV_DIMENSION_MAX_WIDTH,
+	.panel_info.yres = TV_DIMENSION_MAX_HEIGHT,
+	.panel_info.type = TV_PANEL,
+	.panel_info.pdest = DISPLAY_2,
+	.panel_info.wait_cycle = 0,
+#ifdef CONFIG_FB_MSM_MDP40
+	.panel_info.bpp = 24,
+#else
+	.panel_info.bpp = 16,
+#endif
+	.panel_info.fb_num = 2,
+	.on = tvout_on,
+	.off = tvout_off,
+};
+
+static struct platform_device this_device = {
+	.name   = "tvout_device",
+	.id = 1,
+	.dev	= {
+		.platform_data = &tvout_panel_data,
+	}
+};
+
+static int __init tvout_init(void)
+{
+	int ret;
+
+	if (msm_fb_detect_client("tvout_msm"))
+		return 0;
+
+	tvout_msm_state = kzalloc(sizeof(*tvout_msm_state), GFP_KERNEL);
+	if (!tvout_msm_state) {
+		DEV_ERR("tvout_msm_init FAILED: out of memory\n");
+		ret = -ENOMEM;
+		goto init_exit;
+	}
+
+	external_common_state = &tvout_msm_state->common;
+	ret = platform_driver_register(&this_driver);
+	if (ret) {
+		DEV_ERR("tvout_device_init FAILED: platform_driver_register\
+			rc=%d\n", ret);
+		goto init_exit;
+	}
+
+	ret = platform_device_register(&this_device);
+	if (ret) {
+		DEV_ERR("tvout_device_init FAILED: platform_driver_register\
+			rc=%d\n", ret);
+		platform_driver_unregister(&this_driver);
+		goto init_exit;
+	}
+
+	INIT_WORK(&tvout_msm_state->hpd_work, tvout_msm_hpd_work);
+	return 0;
+
+init_exit:
+	kfree(tvout_msm_state);
+	tvout_msm_state = NULL;
+	return ret;
+}
+
+static void __exit tvout_exit(void)
+{
+	platform_device_unregister(&this_device);
+	platform_driver_unregister(&this_driver);
+}
+
+module_init(tvout_init);
+module_exit(tvout_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("1.0");
+MODULE_AUTHOR("Qualcomm Innovation Center, Inc.");
+MODULE_DESCRIPTION("TV out driver");
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl.c
new file mode 100644
index 0000000..81b1436
--- /dev/null
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl.c
@@ -0,0 +1,719 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <mach/msm_memtypes.h>
+#include "vcd_ddl.h"
+#include "vcd_ddl_metadata.h"
+#include "vcd_res_tracker_api.h"
+
+static unsigned int first_time;
+
+u32 ddl_device_init(struct ddl_init_config *ddl_init_config,
+	void *client_data)
+{
+	struct ddl_context *ddl_context;
+	u32 status = VCD_S_SUCCESS;
+	void *ptr = NULL;
+	DDL_MSG_HIGH("ddl_device_init");
+
+	if ((!ddl_init_config) || (!ddl_init_config->ddl_callback) ||
+		(!ddl_init_config->core_virtual_base_addr)) {
+		DDL_MSG_ERROR("ddl_dev_init:Bad_argument");
+		return VCD_ERR_ILLEGAL_PARM;
+	}
+	ddl_context = ddl_get_context();
+	if (DDL_IS_INITIALIZED(ddl_context)) {
+		DDL_MSG_ERROR("ddl_dev_init:Multiple_init");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	if (!DDL_IS_IDLE(ddl_context)) {
+		DDL_MSG_ERROR("ddl_dev_init:Ddl_busy");
+		return VCD_ERR_BUSY;
+	}
+	memset(ddl_context, 0, sizeof(struct ddl_context));
+	DDL_BUSY(ddl_context);
+	if (res_trk_get_enable_ion()) {
+		DDL_MSG_LOW("ddl_dev_init:ION framework enabled");
+		ddl_context->video_ion_client  =
+			res_trk_get_ion_client();
+		if (!ddl_context->video_ion_client) {
+			DDL_MSG_ERROR("ION client create failed");
+			return VCD_ERR_ILLEGAL_OP;
+		}
+	}
+	ddl_context->ddl_callback = ddl_init_config->ddl_callback;
+	if (ddl_init_config->interrupt_clr)
+		ddl_context->interrupt_clr =
+			ddl_init_config->interrupt_clr;
+	ddl_context->core_virtual_base_addr =
+		ddl_init_config->core_virtual_base_addr;
+	ddl_context->client_data = client_data;
+	ddl_context->ddl_hw_response.arg1 = DDL_INVALID_INTR_STATUS;
+
+	ddl_context->frame_channel_depth = VCD_FRAME_COMMAND_DEPTH;
+
+	DDL_MSG_LOW("%s() : virtual address of core(%x)\n", __func__,
+		(u32) ddl_init_config->core_virtual_base_addr);
+	vidc_1080p_set_device_base_addr(
+		ddl_context->core_virtual_base_addr);
+	ddl_context->cmd_state =	DDL_CMD_INVALID;
+	ddl_client_transact(DDL_INIT_CLIENTS, NULL);
+	ddl_context->fw_memory_size =
+		DDL_FW_INST_GLOBAL_CONTEXT_SPACE_SIZE;
+	if (res_trk_get_firmware_addr(&ddl_context->dram_base_a)) {
+		DDL_MSG_ERROR("firmware allocation failed");
+		ptr = NULL;
+	} else {
+		ptr = (void *)ddl_context->dram_base_a.virtual_base_addr;
+	}
+	if (!ptr) {
+		DDL_MSG_ERROR("Memory Aocation Failed for FW Base");
+		status = VCD_ERR_ALLOC_FAIL;
+	} else {
+		DDL_MSG_LOW("%s() : physical address of base(%x)\n",
+			 __func__, (u32) ddl_context->dram_base_a.\
+			align_physical_addr);
+		ddl_context->dram_base_b.align_physical_addr =
+			ddl_context->dram_base_a.align_physical_addr;
+		ddl_context->dram_base_b.align_virtual_addr  =
+			ddl_context->dram_base_a.align_virtual_addr;
+	}
+	if (!status) {
+		ddl_context->metadata_shared_input.mem_type = DDL_FW_MEM;
+		ptr = ddl_pmem_alloc(&ddl_context->metadata_shared_input,
+			DDL_METADATA_TOTAL_INPUTBUFSIZE,
+			DDL_LINEAR_BUFFER_ALIGN_BYTES);
+		if (!ptr) {
+			DDL_MSG_ERROR("ddl_device_init: metadata alloc fail");
+			status = VCD_ERR_ALLOC_FAIL;
+		}
+	}
+	if (!status && !ddl_fw_init(&ddl_context->dram_base_a)) {
+		DDL_MSG_ERROR("ddl_dev_init:fw_init_failed");
+		status = VCD_ERR_ALLOC_FAIL;
+	}
+	if (!status) {
+		ddl_context->cmd_state = DDL_CMD_DMA_INIT;
+		ddl_vidc_core_init(ddl_context);
+	} else {
+		ddl_release_context_buffers(ddl_context);
+		DDL_IDLE(ddl_context);
+	}
+	return status;
+}
+
+u32 ddl_device_release(void *client_data)
+{
+	struct ddl_context *ddl_context;
+
+	DDL_MSG_HIGH("ddl_device_release");
+	ddl_context = ddl_get_context();
+	if (!DDL_IS_IDLE(ddl_context)) {
+		DDL_MSG_ERROR("ddl_dev_rel:Ddl_busy");
+		return VCD_ERR_BUSY;
+	}
+	if (!DDL_IS_INITIALIZED(ddl_context)) {
+		DDL_MSG_ERROR("ddl_dev_rel:Not_inited");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	if (!ddl_client_transact(DDL_ACTIVE_CLIENT, NULL)) {
+		DDL_MSG_ERROR("ddl_dev_rel:Client_present_err");
+		return VCD_ERR_CLIENT_PRESENT;
+	}
+	DDL_BUSY(ddl_context);
+	ddl_context->device_state = DDL_DEVICE_NOTINIT;
+	ddl_context->client_data = client_data;
+	ddl_context->cmd_state = DDL_CMD_INVALID;
+	ddl_vidc_core_term(ddl_context);
+	DDL_MSG_LOW("FW_ENDDONE");
+	ddl_context->core_virtual_base_addr = NULL;
+	ddl_release_context_buffers(ddl_context);
+	ddl_context->video_ion_client = NULL;
+	DDL_IDLE(ddl_context);
+	return VCD_S_SUCCESS;
+}
+
+u32 ddl_open(u32 **ddl_handle, u32 decoding)
+{
+	struct ddl_context *ddl_context;
+	struct ddl_client_context *ddl;
+	void *ptr;
+	u32 status;
+
+	DDL_MSG_HIGH("ddl_open");
+	if (!ddl_handle) {
+		DDL_MSG_ERROR("ddl_open:Bad_handle");
+		return VCD_ERR_BAD_HANDLE;
+	}
+	ddl_context = ddl_get_context();
+	if (!DDL_IS_INITIALIZED(ddl_context)) {
+		DDL_MSG_ERROR("ddl_open:Not_inited");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	status = ddl_client_transact(DDL_GET_CLIENT, &ddl);
+	if (status) {
+		DDL_MSG_ERROR("ddl_open:Client_trasac_failed");
+		return status;
+	}
+	if (res_trk_check_for_sec_session())
+		ddl->shared_mem[0].mem_type = DDL_CMD_MEM;
+	else
+		ddl->shared_mem[0].mem_type = DDL_FW_MEM;
+	ptr = ddl_pmem_alloc(&ddl->shared_mem[0],
+			DDL_FW_AUX_HOST_CMD_SPACE_SIZE, 0);
+	if (!ptr)
+		status = VCD_ERR_ALLOC_FAIL;
+	if (!status && ddl_context->frame_channel_depth
+		== VCD_DUAL_FRAME_COMMAND_CHANNEL) {
+		if (res_trk_check_for_sec_session())
+			ddl->shared_mem[1].mem_type = DDL_CMD_MEM;
+		else
+			ddl->shared_mem[1].mem_type = DDL_FW_MEM;
+		ptr = ddl_pmem_alloc(&ddl->shared_mem[1],
+				DDL_FW_AUX_HOST_CMD_SPACE_SIZE, 0);
+		if (!ptr) {
+			ddl_pmem_free(&ddl->shared_mem[0]);
+			status = VCD_ERR_ALLOC_FAIL;
+		}
+	}
+	if (!status) {
+		memset(ddl->shared_mem[0].align_virtual_addr, 0,
+			DDL_FW_AUX_HOST_CMD_SPACE_SIZE);
+		if (ddl_context->frame_channel_depth ==
+			VCD_DUAL_FRAME_COMMAND_CHANNEL) {
+			memset(ddl->shared_mem[1].align_virtual_addr, 0,
+				DDL_FW_AUX_HOST_CMD_SPACE_SIZE);
+		}
+		DDL_MSG_LOW("ddl_state_transition: %s ~~> DDL_CLIENT_OPEN",
+		ddl_get_state_string(ddl->client_state));
+		ddl->client_state = DDL_CLIENT_OPEN;
+		ddl->codec_data.hdr.decoding = decoding;
+		ddl->decoding = decoding;
+		ddl_set_default_meta_data_hdr(ddl);
+		ddl_set_initial_default_values(ddl);
+		*ddl_handle	= (u32 *) ddl;
+	} else {
+		ddl_pmem_free(&ddl->shared_mem[0]);
+		if (ddl_context->frame_channel_depth
+			== VCD_DUAL_FRAME_COMMAND_CHANNEL)
+			ddl_pmem_free(&ddl->shared_mem[1]);
+		ddl_client_transact(DDL_FREE_CLIENT, &ddl);
+	}
+	return status;
+}
+
+u32 ddl_close(u32 **ddl_handle)
+{
+	struct ddl_context *ddl_context;
+	struct ddl_client_context **pp_ddl =
+		(struct ddl_client_context **)ddl_handle;
+
+	DDL_MSG_HIGH("ddl_close");
+	if (!pp_ddl || !*pp_ddl) {
+		DDL_MSG_ERROR("ddl_close:Bad_handle");
+		return VCD_ERR_BAD_HANDLE;
+	}
+	ddl_context = ddl_get_context();
+	if (!DDL_IS_INITIALIZED(ddl_context)) {
+		DDL_MSG_ERROR("ddl_close:Not_inited");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	if (!DDLCLIENT_STATE_IS(*pp_ddl, DDL_CLIENT_OPEN)) {
+		DDL_MSG_ERROR("ddl_close:Not_in_open_state");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	ddl_pmem_free(&(*pp_ddl)->shared_mem[0]);
+	if (ddl_context->frame_channel_depth ==
+		VCD_DUAL_FRAME_COMMAND_CHANNEL)
+		ddl_pmem_free(&(*pp_ddl)->shared_mem[1]);
+	DDL_MSG_LOW("ddl_state_transition: %s ~~> DDL_CLIENT_INVALID",
+	ddl_get_state_string((*pp_ddl)->client_state));
+	(*pp_ddl)->client_state = DDL_CLIENT_INVALID;
+	ddl_codec_type_transact(*pp_ddl, true, (enum vcd_codec)0);
+	ddl_client_transact(DDL_FREE_CLIENT, pp_ddl);
+	return VCD_S_SUCCESS;
+}
+
+u32 ddl_encode_start(u32 *ddl_handle, void *client_data)
+{
+	struct ddl_client_context *ddl =
+		(struct ddl_client_context *) ddl_handle;
+	struct ddl_context *ddl_context;
+	struct ddl_encoder_data *encoder;
+	void *ptr;
+	u32 status = VCD_S_SUCCESS;
+	DDL_MSG_HIGH("ddl_encode_start");
+	if (first_time < 2) {
+		ddl_reset_core_time_variables(ENC_OP_TIME);
+		first_time++;
+	 }
+	ddl_set_core_start_time(__func__, ENC_OP_TIME);
+	ddl_context = ddl_get_context();
+	if (!DDL_IS_INITIALIZED(ddl_context)) {
+		DDL_MSG_ERROR("ddl_enc_start:Not_inited");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	if (DDL_IS_BUSY(ddl_context)) {
+		DDL_MSG_ERROR("ddl_enc_start:Ddl_busy");
+		return VCD_ERR_BUSY;
+	}
+	if (!ddl || ddl->decoding) {
+		DDL_MSG_ERROR("ddl_enc_start:Bad_handle");
+		return VCD_ERR_BAD_HANDLE;
+	}
+	if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN)) {
+		DDL_MSG_ERROR("ddl_enc_start:Not_opened");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	if (!ddl_encoder_ready_to_start(ddl)) {
+		DDL_MSG_ERROR("ddl_enc_start:Err_param_settings");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	encoder = &ddl->codec_data.encoder;
+	status = ddl_allocate_enc_hw_buffers(ddl);
+	if (status)
+		return status;
+#ifdef DDL_BUF_LOG
+	ddl_list_buffers(ddl);
+#endif
+	encoder->seq_header.mem_type = DDL_MM_MEM;
+	ptr = ddl_pmem_alloc(&encoder->seq_header,
+		DDL_ENC_SEQHEADER_SIZE, DDL_LINEAR_BUFFER_ALIGN_BYTES);
+	if (!ptr) {
+		ddl_free_enc_hw_buffers(ddl);
+		DDL_MSG_ERROR("ddl_enc_start:Seq_hdr_alloc_failed");
+		return VCD_ERR_ALLOC_FAIL;
+	}
+	msm_ion_do_cache_op(ddl_context->video_ion_client,
+				encoder->seq_header.alloc_handle,
+				encoder->seq_header.virtual_base_addr,
+				encoder->seq_header.buffer_size,
+				ION_IOC_CLEAN_INV_CACHES);
+	if (encoder->slice_delivery_info.enable) {
+		DDL_MSG_LOW("%s: slice mode allocate memory for struct\n",
+					__func__);
+		ptr = ddl_pmem_alloc(&encoder->batch_frame.slice_batch_in,
+				DDL_ENC_SLICE_BATCH_INPSTRUCT_SIZE,
+				DDL_LINEAR_BUFFER_ALIGN_BYTES);
+		if (ptr) {
+			ptr = ddl_pmem_alloc(
+				&encoder->batch_frame.slice_batch_out,
+				DDL_ENC_SLICE_BATCH_OUTSTRUCT_SIZE,
+				DDL_LINEAR_BUFFER_ALIGN_BYTES);
+		}
+		if (!ptr) {
+			ddl_pmem_free(&encoder->batch_frame.slice_batch_in);
+			ddl_pmem_free(&encoder->batch_frame.slice_batch_out);
+			ddl_free_enc_hw_buffers(ddl);
+			ddl_pmem_free(&encoder->seq_header);
+			DDL_MSG_ERROR("ddlEncStart:SeqHdrAllocFailed");
+			return VCD_ERR_ALLOC_FAIL;
+		}
+	}
+	if (!ddl_take_command_channel(ddl_context, ddl, client_data))
+		return VCD_ERR_BUSY;
+	ddl_vidc_channel_set(ddl);
+	return status;
+}
+
+u32 ddl_decode_start(u32 *ddl_handle, struct vcd_sequence_hdr *header,
+	void *client_data)
+{
+	struct ddl_client_context  *ddl =
+		(struct ddl_client_context *) ddl_handle;
+	struct ddl_context *ddl_context;
+	struct ddl_decoder_data *decoder;
+	u32 status = VCD_S_SUCCESS;
+
+	DDL_MSG_HIGH("ddl_decode_start");
+	ddl_reset_core_time_variables(DEC_OP_TIME);
+	ddl_reset_core_time_variables(DEC_IP_TIME);
+	ddl_context = ddl_get_context();
+	if (!DDL_IS_INITIALIZED(ddl_context)) {
+		DDL_MSG_ERROR("ddl_dec_start:Not_inited");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	if (DDL_IS_BUSY(ddl_context)) {
+		DDL_MSG_ERROR("ddl_dec_start:Ddl_busy");
+		return VCD_ERR_BUSY;
+	}
+	if (!ddl || !ddl->decoding) {
+		DDL_MSG_ERROR("ddl_dec_start:Bad_handle");
+		return VCD_ERR_BAD_HANDLE;
+	}
+	if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN)) {
+		DDL_MSG_ERROR("ddl_dec_start:Not_in_opened_state");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+
+	if ((header) && ((!header->sequence_header_len) ||
+		(!header->sequence_header))) {
+		DDL_MSG_ERROR("ddl_dec_start:Bad_param_seq_header");
+		return VCD_ERR_ILLEGAL_PARM;
+	}
+	if (!ddl_decoder_ready_to_start(ddl, header)) {
+		DDL_MSG_ERROR("ddl_dec_start:Err_param_settings");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	decoder = &ddl->codec_data.decoder;
+	status = ddl_allocate_dec_hw_buffers(ddl);
+	if (status)
+		return status;
+#ifdef DDL_BUF_LOG
+	ddl_list_buffers(ddl);
+#endif
+	if (!ddl_take_command_channel(ddl_context, ddl, client_data))
+		return VCD_ERR_BUSY;
+	if (header) {
+		decoder->header_in_start = true;
+		decoder->decode_config = *header;
+	} else {
+		decoder->header_in_start = false;
+		decoder->decode_config.sequence_header_len = 0;
+	}
+	ddl_vidc_channel_set(ddl);
+	return status;
+}
+
+u32 ddl_decode_frame(u32 *ddl_handle,
+	struct ddl_frame_data_tag *input_bits, void *client_data)
+{
+	u32 vcd_status = VCD_S_SUCCESS;
+	struct ddl_client_context *ddl =
+		(struct ddl_client_context *) ddl_handle;
+	struct ddl_context *ddl_context;
+	struct ddl_decoder_data *decoder;
+	DDL_MSG_HIGH("ddl_decode_frame");
+	ddl_context = ddl_get_context();
+	if (!DDL_IS_INITIALIZED(ddl_context)) {
+		DDL_MSG_ERROR("ddl_dec_frame:Not_inited");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	if (DDL_IS_BUSY(ddl_context)) {
+		DDL_MSG_ERROR("ddl_dec_frame:Ddl_busy");
+		return VCD_ERR_BUSY;
+	}
+	if (!ddl || !ddl->decoding) {
+		DDL_MSG_ERROR("ddl_dec_frame:Bad_handle");
+		return VCD_ERR_BAD_HANDLE;
+	}
+	if (!input_bits || ((!input_bits->vcd_frm.physical ||
+		!input_bits->vcd_frm.data_len) &&
+		(!(VCD_FRAME_FLAG_EOS &	input_bits->vcd_frm.flags)))) {
+		DDL_MSG_ERROR("ddl_dec_frame:Bad_input_param");
+		return VCD_ERR_ILLEGAL_PARM;
+	}
+	if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME) &&
+		!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_INITCODEC) &&
+		!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_DPB)) {
+		DDL_MSG_ERROR("Dec_frame:Wrong_state");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	decoder = &(ddl->codec_data.decoder);
+	if (DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_INITCODEC)	&&
+		!ddl->codec_data.decoder.dp_buf.no_of_dec_pic_buf) {
+		DDL_MSG_ERROR("ddl_dec_frame:Dpbs_requied");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	if (!ddl_take_command_channel(ddl_context, ddl, client_data))
+		return VCD_ERR_BUSY;
+
+	ddl->input_frame = *input_bits;
+	if (DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME))
+		ddl_vidc_decode_frame_run(ddl);
+	else {
+		if (!ddl->codec_data.decoder.dp_buf.no_of_dec_pic_buf) {
+			DDL_MSG_ERROR("ddl_dec_frame:Dpbs_requied");
+			vcd_status = VCD_ERR_ILLEGAL_OP;
+		} else if (DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_DPB)) {
+			vcd_status = ddl_vidc_decode_set_buffers(ddl);
+		if (vcd_status)
+			ddl_release_command_channel(ddl_context,
+				ddl->command_channel);
+		} else if (DDLCLIENT_STATE_IS(ddl,
+			DDL_CLIENT_WAIT_FOR_INITCODEC)) {
+			if (decoder->codec.codec == VCD_CODEC_DIVX_3) {
+				if ((!decoder->client_frame_size.width) ||
+				(!decoder->client_frame_size.height))
+					return VCD_ERR_ILLEGAL_OP;
+		}
+		ddl->codec_data.decoder.decode_config.sequence_header =
+			ddl->input_frame.vcd_frm.physical;
+		ddl->codec_data.decoder.decode_config.sequence_header_len =
+			ddl->input_frame.vcd_frm.data_len;
+		ddl_vidc_decode_init_codec(ddl);
+		} else {
+			DDL_MSG_ERROR("Dec_frame:Wrong_state");
+			vcd_status = VCD_ERR_ILLEGAL_OP;
+		}
+		if (vcd_status)
+			DDL_IDLE(ddl_context);
+		}
+	return vcd_status;
+}
+
+u32 ddl_encode_frame(u32 *ddl_handle,
+	struct ddl_frame_data_tag *input_frame,
+	struct ddl_frame_data_tag *output_bit, void *client_data)
+{
+	struct ddl_client_context *ddl =
+		(struct ddl_client_context *) ddl_handle;
+	struct ddl_context *ddl_context;
+	struct ddl_encoder_data *encoder =
+		&ddl->codec_data.encoder;
+	u32 vcd_status = VCD_S_SUCCESS;
+	struct vcd_transc *transc;
+	transc = (struct vcd_transc *)(ddl->client_data);
+	DDL_MSG_LOW("%s: transc = 0x%x, in_use = %u",
+		__func__, (u32)ddl->client_data, transc->in_use);
+	if (encoder->slice_delivery_info.enable) {
+		return ddl_encode_frame_batch(ddl_handle,
+					input_frame,
+					output_bit,
+					1,
+					encoder->slice_delivery_info.num_slices,
+					client_data);
+	}
+
+	ddl_set_core_start_time(__func__, ENC_OP_TIME);
+	ddl_context = ddl_get_context();
+	if (!DDL_IS_INITIALIZED(ddl_context)) {
+		DDL_MSG_ERROR("ddl_enc_frame:Not_inited");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	if (DDL_IS_BUSY(ddl_context)) {
+		DDL_MSG_ERROR("ddl_enc_frame:Ddl_busy");
+		return VCD_ERR_BUSY;
+	}
+	if (!ddl || ddl->decoding) {
+		DDL_MSG_ERROR("ddl_enc_frame:Bad_handle");
+	return VCD_ERR_BAD_HANDLE;
+	}
+	if (!input_frame || !input_frame->vcd_frm.physical	||
+		!input_frame->vcd_frm.data_len) {
+		DDL_MSG_ERROR("ddl_enc_frame:Bad_input_params");
+		return VCD_ERR_ILLEGAL_PARM;
+	}
+	if ((((u32) input_frame->vcd_frm.physical +
+		input_frame->vcd_frm.offset) &
+		(DDL_STREAMBUF_ALIGN_GUARD_BYTES))) {
+		DDL_MSG_ERROR("ddl_enc_frame:Un_aligned_yuv_start_address");
+		return VCD_ERR_ILLEGAL_PARM;
+	}
+	if (!output_bit || !output_bit->vcd_frm.physical ||
+		!output_bit->vcd_frm.alloc_len) {
+		DDL_MSG_ERROR("ddl_enc_frame:Bad_output_params");
+		return VCD_ERR_ILLEGAL_PARM;
+	}
+	if ((ddl->codec_data.encoder.output_buf_req.sz +
+		output_bit->vcd_frm.offset) >
+		output_bit->vcd_frm.alloc_len)
+		DDL_MSG_ERROR("ddl_enc_frame:offset_large,"
+			"Exceeds_min_buf_size");
+	if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME)) {
+		DDL_MSG_ERROR("ddl_enc_frame:Wrong_state");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	if (!ddl_take_command_channel(ddl_context, ddl, client_data))
+		return VCD_ERR_BUSY;
+
+	ddl->input_frame = *input_frame;
+	ddl->output_frame = *output_bit;
+	if (ddl->codec_data.encoder.i_period.b_frames > 0) {
+		if (!ddl->b_count) {
+			ddl->first_output_frame = *output_bit;
+			ddl->b_count++;
+		} else if (ddl->codec_data.encoder.i_period.b_frames >=
+			ddl->b_count) {
+			ddl->extra_output_frame[ddl->b_count-1] =
+				*output_bit;
+			ddl->output_frame = ddl->first_output_frame;
+			ddl->b_count++;
+		}
+	}
+	ddl_insert_input_frame_to_pool(ddl, input_frame);
+	if (!vcd_status)
+		ddl_vidc_encode_frame_run(ddl);
+	else
+		DDL_MSG_ERROR("insert to frame pool failed %u", vcd_status);
+	return vcd_status;
+}
+
+u32 ddl_encode_frame_batch(u32 *ddl_handle,
+	struct ddl_frame_data_tag *input_frame,
+	struct ddl_frame_data_tag *output_bit,
+	u32 num_in_frames, u32 num_out_frames,
+	void *client_data)
+{
+	struct ddl_client_context *ddl =
+		(struct ddl_client_context *) ddl_handle;
+	struct ddl_context *ddl_context;
+	u32 vcd_status = VCD_S_SUCCESS;
+	struct ddl_encoder_data *encoder;
+
+	DDL_MSG_LOW("ddl_encode_frame_batch");
+	ddl_context = ddl_get_context();
+	if (!DDL_IS_INITIALIZED(ddl_context)) {
+		DDL_MSG_ERROR("ddl_enc_frame:Not_inited");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	if (DDL_IS_BUSY(ddl_context)) {
+		DDL_MSG_ERROR("ddl_enc_frame:Ddl_busy");
+		return VCD_ERR_BUSY;
+	}
+	if (!ddl || ddl->decoding) {
+		DDL_MSG_ERROR("ddl_enc_frame:Bad_handle");
+	return VCD_ERR_BAD_HANDLE;
+	}
+	if (!input_frame || !input_frame->vcd_frm.physical	||
+		!input_frame->vcd_frm.data_len) {
+		DDL_MSG_ERROR("ddl_enc_frame:Bad_input_params");
+		return VCD_ERR_ILLEGAL_PARM;
+	}
+	if ((((u32) input_frame->vcd_frm.physical +
+		input_frame->vcd_frm.offset) &
+		(DDL_STREAMBUF_ALIGN_GUARD_BYTES))) {
+		DDL_MSG_ERROR("ddl_enc_frame:Un_aligned_yuv_start_address");
+		return VCD_ERR_ILLEGAL_PARM;
+	}
+	if (!output_bit || !output_bit->vcd_frm.physical ||
+		!output_bit->vcd_frm.alloc_len) {
+		DDL_MSG_ERROR("ddl_enc_frame:Bad_output_params");
+		return VCD_ERR_ILLEGAL_PARM;
+	}
+	if ((ddl->codec_data.encoder.output_buf_req.sz +
+		output_bit->vcd_frm.offset) >
+		output_bit->vcd_frm.alloc_len)
+		DDL_MSG_ERROR("ddl_enc_frame:offset_large,"
+			"Exceeds_min_buf_size");
+	if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME)) {
+		DDL_MSG_ERROR("ddl_enc_frame:Wrong_state");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	if (!ddl_take_command_channel(ddl_context, ddl, client_data))
+		return VCD_ERR_BUSY;
+	encoder = &ddl->codec_data.encoder;
+	if (encoder->slice_delivery_info.enable) {
+		DDL_MEMCPY((void *)&(encoder->batch_frame.output_frame[0]),
+			(void *)output_bit,
+			sizeof(struct ddl_frame_data_tag) * num_out_frames);
+		encoder->batch_frame.num_output_frames = num_out_frames;
+		ddl->input_frame = *input_frame;
+		vcd_status = ddl_insert_input_frame_to_pool(ddl, input_frame);
+		if (!vcd_status)
+			ddl_vidc_encode_slice_batch_run(ddl);
+		else
+			DDL_MSG_ERROR("insert to frame pool failed %u",
+					vcd_status);
+	}
+	return vcd_status;
+}
+
+u32 ddl_decode_end(u32 *ddl_handle, void *client_data)
+{
+	struct ddl_client_context *ddl =
+		(struct ddl_client_context *) ddl_handle;
+	struct ddl_context *ddl_context;
+
+	DDL_MSG_HIGH("ddl_decode_end");
+	ddl_reset_core_time_variables(DEC_OP_TIME);
+	ddl_reset_core_time_variables(DEC_IP_TIME);
+	ddl_context = ddl_get_context();
+	if (!DDL_IS_INITIALIZED(ddl_context)) {
+		DDL_MSG_ERROR("ddl_dec_end:Not_inited");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	if (DDL_IS_BUSY(ddl_context)) {
+		DDL_MSG_ERROR("ddl_dec_end:Ddl_busy");
+		return VCD_ERR_BUSY;
+	}
+	if (!ddl || !ddl->decoding) {
+		DDL_MSG_ERROR("ddl_dec_end:Bad_handle");
+		return VCD_ERR_BAD_HANDLE;
+	}
+	if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME) &&
+		!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_INITCODEC) &&
+		!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_DPB) &&
+		!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_FAVIDC_ERROR)) {
+		DDL_MSG_ERROR("ddl_dec_end:Wrong_state");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	if (!ddl_take_command_channel(ddl_context, ddl, client_data))
+		return VCD_ERR_BUSY;
+	ddl_vidc_channel_end(ddl);
+	return VCD_S_SUCCESS;
+}
+
+u32 ddl_encode_end(u32 *ddl_handle, void *client_data)
+{
+	struct ddl_client_context  *ddl =
+		(struct ddl_client_context *) ddl_handle;
+	struct ddl_context *ddl_context;
+
+	DDL_MSG_HIGH("ddl_encode_end");
+	ddl_reset_core_time_variables(ENC_OP_TIME);
+	ddl_context = ddl_get_context();
+	if (!DDL_IS_INITIALIZED(ddl_context)) {
+		DDL_MSG_ERROR("ddl_enc_end:Not_inited");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	if (DDL_IS_BUSY(ddl_context)) {
+		DDL_MSG_ERROR("ddl_enc_end:Ddl_busy");
+		return VCD_ERR_BUSY;
+	}
+	if (!ddl || ddl->decoding) {
+		DDL_MSG_ERROR("ddl_enc_end:Bad_handle");
+		return VCD_ERR_BAD_HANDLE;
+	}
+	if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME) &&
+		!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_INITCODEC) &&
+		!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_FAVIDC_ERROR)) {
+		DDL_MSG_ERROR("ddl_enc_end:Wrong_state");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	if (!ddl_take_command_channel(ddl_context, ddl, client_data))
+		return VCD_ERR_BUSY;
+	ddl_vidc_channel_end(ddl);
+	return VCD_S_SUCCESS;
+}
+
+u32 ddl_reset_hw(u32 mode)
+{
+	struct ddl_context *ddl_context;
+	struct ddl_client_context *ddl;
+	u32 i;
+
+	DDL_MSG_HIGH("ddl_reset_hw");
+	DDL_MSG_LOW("ddl_reset_hw:called");
+	ddl_context = ddl_get_context();
+	ddl_context->cmd_state = DDL_CMD_INVALID;
+	DDL_BUSY(ddl_context);
+	if (ddl_context->core_virtual_base_addr) {
+		vidc_1080p_do_sw_reset(VIDC_1080P_RESET_IN_SEQ_FIRST_STAGE);
+		msleep(DDL_SW_RESET_SLEEP);
+		vidc_1080p_do_sw_reset(VIDC_1080P_RESET_IN_SEQ_SECOND_STAGE);
+		msleep(DDL_SW_RESET_SLEEP);
+		ddl_context->core_virtual_base_addr = NULL;
+	}
+	ddl_context->device_state = DDL_DEVICE_NOTINIT;
+	for (i = 0; i < VCD_MAX_NO_CLIENT; i++) {
+		ddl = ddl_context->ddl_clients[i];
+		ddl_context->ddl_clients[i] = NULL;
+		if (ddl) {
+			ddl_release_client_internal_buffers(ddl);
+			ddl_client_transact(DDL_FREE_CLIENT, &ddl);
+		}
+	}
+	ddl_release_context_buffers(ddl_context);
+	memset(ddl_context, 0, sizeof(struct ddl_context));
+	return true;
+}
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl.h b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl.h
new file mode 100644
index 0000000..ac1ff24
--- /dev/null
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl.h
@@ -0,0 +1,492 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VCD_DDL_H_
+#define _VCD_DDL_H_
+
+#include <mach/msm_subsystem_map.h>
+#include "vcd_ddl_api.h"
+#include "vcd_ddl_core.h"
+#include "vcd_ddl_utils.h"
+#include "vidc.h"
+#include "vidc_hwio.h"
+#include "vidc_pix_cache.h"
+#include "vidc.h"
+
+#define DDL_IDLE_STATE  0
+#define DDL_BUSY_STATE  1
+#define DDL_ERROR_STATE 2
+#define DDL_RUN_STATE   3
+
+#define DDL_IS_BUSY(ddl_context) \
+		((ddl_context)->ddl_busy == DDL_BUSY_STATE)
+#define DDL_IS_IDLE(ddl_context) \
+		((ddl_context)->ddl_busy == DDL_IDLE_STATE)
+#define DDL_BUSY(ddl_context) \
+		((ddl_context)->ddl_busy = DDL_BUSY_STATE)
+#define DDL_IDLE(ddl_context) \
+		((ddl_context)->ddl_busy = DDL_IDLE_STATE)
+#define DDL_ERROR(ddl_context) \
+		((ddl_context)->ddl_busy = DDL_ERROR_STATE)
+#define DDL_RUN(ddl_context) \
+	((ddl_context)->ddl_busy = DDL_RUN_STATE)
+
+#define DDL_DEVICE_NOTINIT  0
+#define DDL_DEVICE_INITED   1
+#define DDL_DEVICE_HWFATAL  2
+
+#define DDL_IS_INITIALIZED(ddl_context) \
+	(ddl_context->device_state == DDL_DEVICE_INITED)
+#define DDLCOMMAND_STATE_IS(ddl_context, command_state) \
+	(command_state == (ddl_context)->cmd_state)
+#define DDLCLIENT_STATE_IS(ddl, state) \
+	(state == (ddl)->client_state)
+
+#define DDL_DPB_OP_INIT       1
+#define DDL_DPB_OP_MARK_FREE  2
+#define DDL_DPB_OP_MARK_BUSY  3
+#define DDL_DPB_OP_SET_MASK   4
+#define DDL_DPB_OP_RETRIEVE   5
+
+#define DDL_INIT_CLIENTS     0
+#define DDL_GET_CLIENT       1
+#define DDL_FREE_CLIENT      2
+#define DDL_ACTIVE_CLIENT    3
+
+#define DDL_INVALID_CHANNEL_ID  ((u32)~0)
+#define DDL_INVALID_CODEC_TYPE  ((u32)~0)
+#define DDL_INVALID_INTR_STATUS ((u32)~0)
+
+#define DDL_ENC_REQ_IFRAME        0x01
+#define DDL_ENC_CHANGE_IPERIOD    0x02
+#define DDL_ENC_CHANGE_BITRATE    0x04
+#define DDL_ENC_CHANGE_FRAMERATE  0x08
+#define DDL_ENC_CHANGE_CIR        0x10
+
+#define DDL_DEC_REQ_OUTPUT_FLUSH  0x1
+
+#define DDL_MIN_NUM_OF_B_FRAME  0
+#define DDL_MAX_NUM_OF_B_FRAME  1
+#define DDL_DEFAULT_NUM_OF_B_FRAME  DDL_MIN_NUM_OF_B_FRAME
+
+#define DDL_MIN_NUM_REF_FOR_P_FRAME             1
+#define DDL_MAX_NUM_REF_FOR_P_FRAME             2
+
+#define DDL_MAX_NUM_IN_INPUTFRAME_POOL          (DDL_MAX_NUM_OF_B_FRAME + 1)
+
+#define MDP_MIN_TILE_HEIGHT			96
+
+enum ddl_mem_area {
+	DDL_FW_MEM	= 0x0,
+	DDL_MM_MEM	= 0x1,
+	DDL_CMD_MEM	= 0x2
+};
+
+struct ddl_buf_addr{
+	u8  *virtual_base_addr;
+	u8  *physical_base_addr;
+	u8  *align_physical_addr;
+	u8  *align_virtual_addr;
+	phys_addr_t alloced_phys_addr;
+	struct msm_mapped_buffer *mapped_buffer;
+	struct ion_handle *alloc_handle;
+	u32 buffer_size;
+	enum ddl_mem_area mem_type;
+	void *pil_cookie;
+};
+enum ddl_cmd_state{
+	DDL_CMD_INVALID         = 0x0,
+	DDL_CMD_DMA_INIT        = 0x1,
+	DDL_CMD_CPU_RESET       = 0x2,
+	DDL_CMD_CHANNEL_SET     = 0x3,
+	DDL_CMD_INIT_CODEC      = 0x4,
+	DDL_CMD_HEADER_PARSE    = 0x5,
+	DDL_CMD_DECODE_SET_DPB  = 0x6,
+	DDL_CMD_DECODE_FRAME    = 0x7,
+	DDL_CMD_ENCODE_FRAME    = 0x8,
+	DDL_CMD_EOS             = 0x9,
+	DDL_CMD_CHANNEL_END     = 0xA,
+	DDL_CMD_ENCODE_CONTINUE = 0xB,
+	DDL_CMD_32BIT           = 0x7FFFFFFF
+};
+enum ddl_client_state{
+	DDL_CLIENT_INVALID                 = 0x0,
+	DDL_CLIENT_OPEN                    = 0x1,
+	DDL_CLIENT_WAIT_FOR_CHDONE         = 0x2,
+	DDL_CLIENT_WAIT_FOR_INITCODEC      = 0x3,
+	DDL_CLIENT_WAIT_FOR_INITCODECDONE  = 0x4,
+	DDL_CLIENT_WAIT_FOR_DPB            = 0x5,
+	DDL_CLIENT_WAIT_FOR_DPBDONE        = 0x6,
+	DDL_CLIENT_WAIT_FOR_FRAME          = 0x7,
+	DDL_CLIENT_WAIT_FOR_FRAME_DONE     = 0x8,
+	DDL_CLIENT_WAIT_FOR_EOS_DONE       = 0x9,
+	DDL_CLIENT_WAIT_FOR_CHEND          = 0xA,
+	DDL_CLIENT_FATAL_ERROR             = 0xB,
+	DDL_CLIENT_FAVIDC_ERROR            = 0xC,
+	DDL_CLIENT_WAIT_FOR_CONTINUE       = 0xD,
+	DDL_CLIENT_32BIT                   = 0x7FFFFFFF
+};
+struct ddl_hw_interface{
+	u32 cmd;
+	u32 arg1;
+	u32 arg2;
+	u32 arg3;
+	u32 arg4;
+};
+struct ddl_mask{
+	u32  client_mask;
+	u32  hw_mask;
+};
+struct ddl_yuv_buffer_size{
+	u32  size_yuv;
+	u32  size_y;
+	u32  size_c;
+};
+struct ddl_dec_buffer_size{
+	u32  sz_dpb0;
+	u32  sz_dpb1;
+	u32  sz_mv;
+	u32  sz_vert_nb_mv;
+	u32  sz_nb_ip;
+	u32  sz_luma;
+	u32  sz_chroma;
+	u32  sz_nb_dcac;
+	u32  sz_upnb_mv;
+	u32  sz_sub_anchor_mv;
+	u32  sz_overlap_xform;
+	u32  sz_bit_plane3;
+	u32  sz_bit_plane2;
+	u32  sz_bit_plane1;
+	u32  sz_stx_parser;
+	u32  sz_desc;
+	u32  sz_cpb;
+	u32  sz_context;
+};
+struct ddl_dec_buffers{
+	struct ddl_buf_addr desc;
+	struct ddl_buf_addr nb_dcac;
+	struct ddl_buf_addr upnb_mv;
+	struct ddl_buf_addr sub_anchor_mv;
+	struct ddl_buf_addr overlay_xform;
+	struct ddl_buf_addr bit_plane3;
+	struct ddl_buf_addr bit_plane2;
+	struct ddl_buf_addr bit_plane1;
+	struct ddl_buf_addr stx_parser;
+	struct ddl_buf_addr h264_mv[DDL_MAX_BUFFER_COUNT];
+	struct ddl_buf_addr h264_vert_nb_mv;
+	struct ddl_buf_addr h264_nb_ip;
+	struct ddl_buf_addr context;
+};
+struct ddl_enc_buffer_size{
+	u32  sz_cur_y;
+	u32  sz_cur_c;
+	u32  sz_dpb_y;
+	u32  sz_dpb_c;
+	u32  sz_strm;
+	u32  sz_mv;
+	u32  sz_col_zero;
+	u32  sz_md;
+	u32  sz_pred;
+	u32  sz_nbor_info;
+	u32  sz_acdc_coef;
+	u32  sz_mb_info;
+	u32  sz_context;
+};
+struct ddl_enc_buffers{
+	struct ddl_buf_addr dpb_y[4];
+	struct ddl_buf_addr dpb_c[4];
+	struct ddl_buf_addr mv;
+	struct ddl_buf_addr col_zero;
+	struct ddl_buf_addr md;
+	struct ddl_buf_addr pred;
+	struct ddl_buf_addr nbor_info;
+	struct ddl_buf_addr acdc_coef;
+	struct ddl_buf_addr mb_info;
+	struct ddl_buf_addr context;
+	u32  dpb_count;
+	u32  sz_dpb_y;
+	u32  sz_dpb_c;
+};
+struct ddl_codec_data_hdr{
+	u32  decoding;
+};
+struct ddl_batch_frame_data {
+	struct ddl_buf_addr slice_batch_in;
+	struct ddl_buf_addr slice_batch_out;
+	struct ddl_frame_data_tag input_frame;
+	struct ddl_frame_data_tag output_frame
+			[DDL_MAX_NUM_BFRS_FOR_SLICE_BATCH];
+	u32 num_output_frames;
+	u32 out_frm_next_frmindex;
+};
+struct ddl_encoder_data{
+	struct ddl_codec_data_hdr   hdr;
+	struct vcd_property_codec   codec;
+	struct vcd_property_frame_size  frame_size;
+	struct vcd_property_frame_rate  frame_rate;
+	struct vcd_property_target_bitrate  target_bit_rate;
+	struct vcd_property_profile  profile;
+	struct vcd_property_level  level;
+	struct vcd_property_rate_control  rc;
+	struct vcd_property_multi_slice  multi_slice;
+	struct ddl_buf_addr  meta_data_input;
+	struct vcd_property_short_header  short_header;
+	struct vcd_property_vop_timing  vop_timing;
+	struct vcd_property_db_config  db_control;
+	struct vcd_property_entropy_control  entropy_control;
+	struct vcd_property_i_period  i_period;
+	struct vcd_property_session_qp  session_qp;
+	struct vcd_property_qp_range  qp_range;
+	struct vcd_property_rc_level  rc_level;
+	struct vcd_property_frame_level_rc_params  frame_level_rc;
+	struct vcd_property_adaptive_rc_params  adaptive_rc;
+	struct vcd_property_intra_refresh_mb_number  intra_refresh;
+	struct vcd_property_buffer_format  buf_format;
+	struct vcd_property_buffer_format  recon_buf_format;
+	struct vcd_property_sps_pps_for_idr_enable sps_pps;
+	struct ddl_buf_addr  seq_header;
+	struct vcd_buffer_requirement  input_buf_req;
+	struct vcd_buffer_requirement  output_buf_req;
+	struct vcd_buffer_requirement  client_input_buf_req;
+	struct vcd_buffer_requirement  client_output_buf_req;
+	struct ddl_enc_buffers  hw_bufs;
+	struct ddl_yuv_buffer_size  input_buf_size;
+	struct vidc_1080p_enc_frame_info enc_frame_info;
+	u32  meta_data_enable_flag;
+	u32  suffix;
+	u32  meta_data_offset;
+	u32  hdr_ext_control;
+	u32  r_cframe_skip;
+	u32  vb_vbuffer_size;
+	u32  dynamic_prop_change;
+	u32  dynmic_prop_change_req;
+	u32  seq_header_length;
+	u32  intra_frame_insertion;
+	u32  mb_info_enable;
+	u32  ext_enc_control_val;
+	u32  num_references_for_p_frame;
+	u32  closed_gop;
+	struct vcd_property_slice_delivery_info slice_delivery_info;
+	struct ddl_batch_frame_data batch_frame;
+};
+struct ddl_decoder_data {
+	struct ddl_codec_data_hdr  hdr;
+	struct vcd_property_codec  codec;
+	struct vcd_property_buffer_format  buf_format;
+	struct vcd_property_frame_size  frame_size;
+	struct vcd_property_frame_size  client_frame_size;
+	struct vcd_property_profile  profile;
+	struct vcd_property_level  level;
+	struct ddl_buf_addr  meta_data_input;
+	struct vcd_property_post_filter  post_filter;
+	struct vcd_sequence_hdr  decode_config;
+	struct ddl_property_dec_pic_buffers  dp_buf;
+	struct ddl_mask  dpb_mask;
+	struct vcd_buffer_requirement  actual_input_buf_req;
+	struct vcd_buffer_requirement  min_input_buf_req;
+	struct vcd_buffer_requirement  client_input_buf_req;
+	struct vcd_buffer_requirement  actual_output_buf_req;
+	struct vcd_buffer_requirement  min_output_buf_req;
+	struct vcd_buffer_requirement  client_output_buf_req;
+	struct ddl_dec_buffers  hw_bufs;
+	struct ddl_yuv_buffer_size  dpb_buf_size;
+	struct vidc_1080p_dec_disp_info dec_disp_info;
+	u32  progressive_only;
+	u32  output_order;
+	u32  meta_data_enable_flag;
+	u32  suffix;
+	u32  meta_data_offset;
+	u32  header_in_start;
+	u32  min_dpb_num;
+	u32  y_cb_cr_size;
+	u32  dynamic_prop_change;
+	u32  dynmic_prop_change_req;
+	u32  flush_pending;
+	u32  meta_data_exists;
+	u32  idr_only_decoding;
+	u32  field_needed_for_prev_ip;
+	u32  prev_ip_frm_tag;
+	u32  cont_mode;
+	u32  reconfig_detected;
+	u32  dmx_disable;
+	int avg_dec_time;
+	int dec_time_sum;
+};
+union ddl_codec_data{
+	struct ddl_codec_data_hdr  hdr;
+	struct ddl_decoder_data   decoder;
+	struct ddl_encoder_data   encoder;
+};
+struct ddl_context{
+	u8 *core_virtual_base_addr;
+	void *client_data;
+	u32 device_state;
+	u32 ddl_busy;
+	u32 cmd_err_status;
+	u32 disp_pic_err_status;
+	u32 pix_cache_enable;
+	u32 fw_version;
+	u32 fw_memory_size;
+	u32 cmd_seq_num;
+	u32 response_cmd_ch_id;
+	enum ddl_cmd_state cmd_state;
+	struct ddl_client_context *current_ddl[2];
+	struct ddl_buf_addr metadata_shared_input;
+	struct ddl_client_context *ddl_clients[VCD_MAX_NO_CLIENT];
+	struct ddl_buf_addr dram_base_a;
+	struct ddl_buf_addr dram_base_b;
+	struct ddl_hw_interface ddl_hw_response;
+	struct ion_client *video_ion_client;
+	void (*ddl_callback) (u32 event, u32 status, void *payload,
+		size_t sz, u32 *ddl_handle, void *const client_data);
+	void (*interrupt_clr) (void);
+	void (*vidc_decode_seq_start[2])
+		(struct vidc_1080p_dec_seq_start_param *param);
+	void (*vidc_set_dec_resolution[2])
+		(u32 width, u32 height);
+	void(*vidc_decode_init_buffers[2])
+		(struct vidc_1080p_dec_init_buffers_param *param);
+	void(*vidc_decode_frame_start[2])
+		(struct vidc_1080p_dec_frame_start_param *param);
+	void(*vidc_encode_seq_start[2])
+		(struct vidc_1080p_enc_seq_start_param *param);
+	void(*vidc_encode_frame_start[2])
+		(struct vidc_1080p_enc_frame_start_param *param);
+	void(*vidc_encode_slice_batch_start[2])
+		(struct vidc_1080p_enc_frame_start_param *param);
+	u32 frame_channel_depth;
+};
+struct ddl_client_context{
+	struct ddl_context  *ddl_context;
+	enum ddl_client_state  client_state;
+	struct ddl_frame_data_tag  first_output_frame;
+	struct ddl_frame_data_tag
+		extra_output_frame[DDL_MAX_NUM_OF_B_FRAME];
+	struct ddl_frame_data_tag  input_frame;
+	struct ddl_frame_data_tag  output_frame;
+	struct ddl_frame_data_tag
+		input_frame_pool[DDL_MAX_NUM_IN_INPUTFRAME_POOL];
+	union ddl_codec_data  codec_data;
+	enum ddl_cmd_state  cmd_state;
+	struct ddl_buf_addr  shared_mem[2];
+	void *client_data;
+	u32  decoding;
+	u32  channel_id;
+	u32  command_channel;
+	u32  b_count;
+	s32  extra_output_buf_count;
+	u32  instance_id;
+};
+
+struct ddl_context *ddl_get_context(void);
+void ddl_vidc_core_init(struct ddl_context *);
+void ddl_vidc_core_term(struct ddl_context *);
+void ddl_vidc_channel_set(struct ddl_client_context *);
+void ddl_vidc_channel_end(struct ddl_client_context *);
+void ddl_vidc_encode_init_codec(struct ddl_client_context *);
+void ddl_vidc_decode_init_codec(struct ddl_client_context *);
+void ddl_vidc_encode_frame_continue(struct ddl_client_context *);
+void ddl_vidc_encode_frame_run(struct ddl_client_context *);
+void ddl_vidc_encode_slice_batch_run(struct ddl_client_context *);
+void ddl_vidc_decode_frame_run(struct ddl_client_context *);
+void ddl_vidc_decode_eos_run(struct ddl_client_context *ddl);
+void ddl_vidc_encode_eos_run(struct ddl_client_context *ddl);
+void ddl_release_context_buffers(struct ddl_context *);
+void ddl_release_client_internal_buffers(struct ddl_client_context *ddl);
+u32  ddl_vidc_decode_set_buffers(struct ddl_client_context *);
+u32  ddl_decoder_dpb_transact(struct ddl_decoder_data *decoder,
+	struct ddl_frame_data_tag *in_out_frame, u32 operation);
+u32  ddl_decoder_dpb_init(struct ddl_client_context *ddl);
+u32  ddl_client_transact(u32 , struct ddl_client_context **);
+u32  ddl_set_default_decoder_buffer_req(struct ddl_decoder_data *decoder,
+	u32 estimate);
+void ddl_set_default_encoder_buffer_req(struct ddl_encoder_data
+	*encoder);
+void ddl_set_default_dec_property(struct ddl_client_context *);
+u32  ddl_encoder_ready_to_start(struct ddl_client_context *);
+u32  ddl_decoder_ready_to_start(struct ddl_client_context *,
+	struct vcd_sequence_hdr *);
+u32  ddl_get_yuv_buffer_size(struct vcd_property_frame_size *frame_size,
+	struct vcd_property_buffer_format *buf_format, u32 interlace,
+	u32 decoding, u32 *pn_c_offset);
+void ddl_calculate_stride(struct vcd_property_frame_size *frame_size,
+	u32 interlace);
+u32  ddl_codec_type_transact(struct ddl_client_context *ddl,
+	u32 remove, enum vcd_codec requested_codec);
+void ddl_vidc_encode_dynamic_property(struct ddl_client_context *ddl,
+	u32 enable);
+void ddl_vidc_decode_dynamic_property(struct ddl_client_context *ddl,
+	u32 enable);
+void ddl_set_initial_default_values(struct ddl_client_context *ddl);
+
+u32  ddl_take_command_channel(struct ddl_context *ddl_context,
+	struct ddl_client_context *ddl, void *client_data);
+void ddl_release_command_channel(struct ddl_context  *ddl_context,
+	u32 command_channel);
+struct ddl_client_context *ddl_get_current_ddl_client_for_channel_id(
+	struct ddl_context *ddl_context, u32 channel_id);
+struct ddl_client_context *ddl_get_current_ddl_client_for_command(
+	struct ddl_context *ddl_context,
+	enum ddl_cmd_state cmd_state);
+
+u32  ddl_get_yuv_buf_size(u32 width, u32 height, u32 format);
+void ddl_free_dec_hw_buffers(struct ddl_client_context *ddl);
+void ddl_free_enc_hw_buffers(struct ddl_client_context *ddl);
+void ddl_calc_dec_hw_buffers_size(enum vcd_codec codec, u32 width,
+	u32 height, u32 h264_dpb,
+	struct ddl_dec_buffer_size *buf_size);
+u32  ddl_allocate_dec_hw_buffers(struct ddl_client_context *ddl);
+u32  ddl_calc_enc_hw_buffers_size(enum vcd_codec codec, u32 width,
+	u32 height, enum vcd_yuv_buffer_format  input_format,
+	struct ddl_client_context *ddl,
+	struct ddl_enc_buffer_size *buf_size);
+u32  ddl_allocate_enc_hw_buffers(struct ddl_client_context *ddl);
+
+u32  ddl_handle_core_errors(struct ddl_context *ddl_context);
+void ddl_client_fatal_cb(struct ddl_client_context *ddl);
+void ddl_hw_fatal_cb(struct ddl_client_context *ddl);
+
+void *ddl_pmem_alloc(struct ddl_buf_addr *addr, size_t sz, u32 alignment);
+void ddl_pmem_free(struct ddl_buf_addr *addr);
+
+u32 ddl_get_input_frame_from_pool(struct ddl_client_context *ddl,
+	u8 *input_buffer_address);
+u32 ddl_get_stream_buf_from_batch_pool(struct ddl_client_context *ddl,
+	struct ddl_frame_data_tag *stream_buffer);
+u32 ddl_insert_input_frame_to_pool(struct ddl_client_context *ddl,
+	struct ddl_frame_data_tag *ddl_input_frame);
+void ddl_decoder_chroma_dpb_change(struct ddl_client_context *ddl);
+u32  ddl_check_reconfig(struct ddl_client_context *ddl);
+void ddl_handle_reconfig(u32 res_change, struct ddl_client_context *ddl);
+void ddl_fill_dec_desc_buffer(struct ddl_client_context *ddl);
+void ddl_set_vidc_timeout(struct ddl_client_context *ddl);
+
+
+#ifdef DDL_BUF_LOG
+void ddl_list_buffers(struct ddl_client_context *ddl);
+#endif
+#ifdef DDL_MSG_LOG
+s8 *ddl_get_state_string(enum ddl_client_state client_state);
+#endif
+extern unsigned char *vidc_video_codec_fw;
+extern u32 vidc_video_codec_fw_size;
+
+u32 ddl_fw_init(struct ddl_buf_addr *dram_base);
+void ddl_get_fw_info(const unsigned char **fw_array_addr,
+	unsigned int *fw_size);
+void ddl_fw_release(struct ddl_buf_addr *);
+int ddl_vidc_decode_get_avg_time(struct ddl_client_context *ddl);
+void ddl_vidc_decode_reset_avg_time(struct ddl_client_context *ddl);
+void ddl_calc_core_proc_time(const char *func_name, u32 index,
+		struct ddl_client_context *ddl);
+#endif
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_api.h b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_api.h
new file mode 100644
index 0000000..5c1ee21
--- /dev/null
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_api.h
@@ -0,0 +1,120 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VCD_DDL_API_H_
+#define _VCD_DDL_API_H_
+
+#include <media/msm/vcd_api.h>
+#include "vidc.h"
+
+#define VCD_EVT_RESP_DDL_BASE             0x3000
+#define VCD_EVT_RESP_DEVICE_INIT          (VCD_EVT_RESP_DDL_BASE + 0x1)
+#define VCD_EVT_RESP_OUTPUT_REQ           (VCD_EVT_RESP_DDL_BASE + 0x2)
+#define VCD_EVT_RESP_EOS_DONE             (VCD_EVT_RESP_DDL_BASE + 0x3)
+#define VCD_EVT_RESP_TRANSACTION_PENDING  (VCD_EVT_RESP_DDL_BASE + 0x4)
+
+#define VCD_S_DDL_ERR_BASE       0x90000000
+#define VCD_ERR_MAX_NO_CODEC     (VCD_S_DDL_ERR_BASE + 0x1)
+#define VCD_ERR_CLIENT_PRESENT   (VCD_S_DDL_ERR_BASE + 0x2)
+#define VCD_ERR_CLIENT_FATAL     (VCD_S_DDL_ERR_BASE + 0x3)
+#define VCD_ERR_NO_SEQ_HDR       (VCD_S_DDL_ERR_BASE + 0x4)
+
+#define VCD_I_CUSTOM_BASE        (VCD_I_RESERVED_BASE)
+#define VCD_I_RC_LEVEL_CONFIG    (VCD_I_CUSTOM_BASE + 0x1)
+#define VCD_I_FRAME_LEVEL_RC     (VCD_I_CUSTOM_BASE + 0x2)
+#define VCD_I_ADAPTIVE_RC        (VCD_I_CUSTOM_BASE + 0x3)
+#define VCD_I_CUSTOM_DDL_BASE    (VCD_I_RESERVED_BASE + 0x100)
+#define DDL_I_INPUT_BUF_REQ      (VCD_I_CUSTOM_DDL_BASE + 0x1)
+#define DDL_I_OUTPUT_BUF_REQ     (VCD_I_CUSTOM_DDL_BASE + 0x2)
+#define DDL_I_DPB                (VCD_I_CUSTOM_DDL_BASE + 0x3)
+#define DDL_I_DPB_RELEASE        (VCD_I_CUSTOM_DDL_BASE + 0x4)
+#define DDL_I_DPB_RETRIEVE       (VCD_I_CUSTOM_DDL_BASE + 0x5)
+#define DDL_I_REQ_OUTPUT_FLUSH   (VCD_I_CUSTOM_DDL_BASE + 0x6)
+#define DDL_I_SEQHDR_ALIGN_BYTES (VCD_I_CUSTOM_DDL_BASE + 0x7)
+#define DDL_I_CAPABILITY         (VCD_I_CUSTOM_DDL_BASE + 0x8)
+#define DDL_I_FRAME_PROC_UNITS   (VCD_I_CUSTOM_DDL_BASE + 0x9)
+#define DDL_I_SEQHDR_PRESENT     (VCD_I_CUSTOM_DDL_BASE + 0xA)
+
+#define DDL_FRAME_VGA_SIZE     (640*480)
+#define DDL_FRAME_720P_WIDTH   1280
+#define DDL_FRAME_720P_HEIGHT  720
+
+struct vcd_property_rc_level{
+	u32 frame_level_rc;
+	u32 mb_level_rc;
+};
+struct vcd_property_frame_level_rc_params{
+	u32 reaction_coeff;
+};
+struct vcd_property_adaptive_rc_params{
+	u32 disable_dark_region_as_flag;
+	u32 disable_smooth_region_as_flag;
+	u32 disable_static_region_as_flag;
+	u32 disable_activity_region_flag;
+};
+struct vcd_property_slice_delivery_info {
+	u32  enable;
+	u32  num_slices;
+	u32  num_slices_enc;
+};
+struct ddl_property_dec_pic_buffers{
+	struct ddl_frame_data_tag *dec_pic_buffers;
+	u32 no_of_dec_pic_buf;
+};
+struct ddl_property_capability{
+	u32 max_num_client;
+	u32 general_command_depth;
+	u32 exclusive;
+	u32 frame_command_depth;
+	u32 ddl_time_out_in_ms;
+};
+struct ddl_init_config{
+	int memtype;
+	u8 *core_virtual_base_addr;
+	void (*interrupt_clr) (void);
+	void (*ddl_callback) (u32 event, u32 status, void *payload, size_t sz,
+		u32 *ddl_handle, void *const client_data);
+};
+struct ddl_frame_data_tag{
+	struct vcd_frame_data vcd_frm;
+	u32 frm_trans_end;
+	u32 frm_delta;
+};
+u32 ddl_device_init(struct ddl_init_config *ddl_init_config,
+	void *client_data);
+u32 ddl_device_release(void *client_data);
+u32 ddl_open(u32 **ddl_handle, u32 decoding);
+u32 ddl_close(u32 **ddl_handle);
+u32 ddl_encode_start(u32 *ddl_handle, void *client_data);
+u32 ddl_encode_frame(u32 *ddl_handle,
+	struct ddl_frame_data_tag *input_frame,
+	struct ddl_frame_data_tag *output_bit, void *client_data);
+u32 ddl_encode_frame_batch(u32 *ddl_handle,
+	struct ddl_frame_data_tag *input_frame,
+	struct ddl_frame_data_tag *output_bit,
+	u32 num_in_frames, u32 num_out_frames,
+	void *client_data);
+u32 ddl_encode_end(u32 *ddl_handle, void *client_data);
+u32 ddl_decode_start(u32 *ddl_handle, struct vcd_sequence_hdr *header,
+	void *client_data);
+u32 ddl_decode_frame(u32 *ddl_handle,
+	struct ddl_frame_data_tag *input_bits, void *client_data);
+u32 ddl_decode_end(u32 *ddl_handle, void *client_data);
+u32 ddl_set_property(u32 *ddl_handle,
+	struct vcd_property_hdr *property_hdr, void *property_value);
+u32 ddl_get_property(u32 *ddl_handle,
+	struct vcd_property_hdr *property_hdr, void *property_value);
+u32 ddl_process_core_response(void);
+u32 ddl_reset_hw(u32 mode);
+void ddl_read_and_clear_interrupt(void);
+#endif
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_core.h b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_core.h
new file mode 100644
index 0000000..50c3696
--- /dev/null
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_core.h
@@ -0,0 +1,153 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VCD_DDL_CORE_H_
+#define _VCD_DDL_CORE_H_
+
+#define DDL_LINEAR_BUF_ALIGN_MASK         0xFFFFF800U
+#define DDL_LINEAR_BUF_ALIGN_GUARD_BYTES  0x7FF
+#define DDL_LINEAR_BUFFER_ALIGN_BYTES     2048
+#define DDL_TILE_BUF_ALIGN_MASK           0xFFFFE000U
+#define DDL_TILE_BUF_ALIGN_GUARD_BYTES    0x1FFF
+#define DDL_TILE_BUFFER_ALIGN_BYTES       8192
+
+#define DDL_YUV_BUF_TYPE_LINEAR 0
+#define DDL_YUV_BUF_TYPE_TILE   1
+
+#define DDL_NO_OF_MB(nWidth, nHeight) \
+	((((nWidth) + 15) >> 4) * (((nHeight) + 15) >> 4))
+
+#define DDL_MAX_FRAME_WIDTH   1920
+#define DDL_MAX_FRAME_HEIGHT  1088
+
+#define MAX_DPB_SIZE_L4PT0_MBS    DDL_KILO_BYTE(32)
+#define MAX_FRAME_SIZE_L4PT0_MBS  DDL_KILO_BYTE(8)
+
+#define DDL_MAX_MB_PER_FRAME (DDL_NO_OF_MB(DDL_MAX_FRAME_WIDTH,\
+	DDL_MAX_FRAME_HEIGHT))
+
+#define DDL_DB_LINE_BUF_SIZE\
+	(((((DDL_MAX_FRAME_WIDTH * 4) - 1) / 256) + 1) * 8 * 1024)
+
+#define DDL_MAX_FRAME_RATE               120
+#define DDL_INITIAL_FRAME_RATE            30
+
+#define DDL_MAX_BIT_RATE    (20*1024*1024)
+#define DDL_MAX_MB_PER_SEC  (DDL_MAX_MB_PER_FRAME * DDL_INITIAL_FRAME_RATE)
+
+#define DDL_SW_RESET_SLEEP               1
+#define VCD_MAX_NO_CLIENT                4
+#define VCD_SINGLE_FRAME_COMMAND_CHANNEL 1
+#define VCD_DUAL_FRAME_COMMAND_CHANNEL   2
+#define VCD_FRAME_COMMAND_DEPTH          VCD_SINGLE_FRAME_COMMAND_CHANNEL
+#define VCD_GENEVIDC_COMMAND_DEPTH        1
+#define VCD_COMMAND_EXCLUSIVE            true
+#define DDL_HW_TIMEOUT_IN_MS             1000
+#define DDL_STREAMBUF_ALIGN_GUARD_BYTES  0x7FF
+
+#define DDL_VIDC_1080P_48MHZ			(48000000)
+#define DDL_VIDC_1080P_133MHZ			(133330000)
+#define DDL_VIDC_1080P_200MHZ			(200000000)
+#define DDL_VIDC_1080P_48MHZ_TIMEOUT_VALUE	(0xCB8)
+#define DDL_VIDC_1080P_133MHZ_TIMEOUT_VALUE	(0x2355)
+#define DDL_VIDC_1080P_200MHZ_TIMEOUT_VALUE	(0x3500)
+
+#define DDL_CONTEXT_MEMORY (1024 * 15 * (VCD_MAX_NO_CLIENT + 1))
+
+#define DDL_ENC_MIN_DPB_BUFFERS           2
+#define DDL_ENC_MAX_DPB_BUFFERS           4
+
+#define DDL_FW_AUX_HOST_CMD_SPACE_SIZE         (DDL_KILO_BYTE(4))
+#define DDL_FW_INST_GLOBAL_CONTEXT_SPACE_SIZE  (DDL_KILO_BYTE(800))
+#define DDL_FW_H264DEC_CONTEXT_SPACE_SIZE      (DDL_KILO_BYTE(800))
+#define DDL_FW_H264ENC_CONTEXT_SPACE_SIZE      (DDL_KILO_BYTE(20))
+#define DDL_FW_OTHER_CONTEXT_SPACE_SIZE        (DDL_KILO_BYTE(20))
+
+#define VCD_DEC_CPB_SIZE         (DDL_KILO_BYTE(512))
+#define DDL_DBG_CORE_DUMP_SIZE   (DDL_KILO_BYTE(10))
+#define DDL_VIDC_1080P_BASE_OFFSET_SHIFT        11
+
+#define DDL_BUFEND_PAD                    256
+#define DDL_ENC_SEQHEADER_SIZE            (512+DDL_BUFEND_PAD)
+#define DDL_ENC_SLICE_BATCH_FACTOR         5
+#define DDL_MAX_NUM_BFRS_FOR_SLICE_BATCH   8
+#define DDL_ENC_SLICE_BATCH_INPSTRUCT_SIZE (128 + \
+				32 * DDL_MAX_NUM_BFRS_FOR_SLICE_BATCH)
+#define DDL_ENC_SLICE_BATCH_OUTSTRUCT_SIZE (64 + \
+				64 * DDL_MAX_NUM_BFRS_FOR_SLICE_BATCH)
+#define DDL_MAX_BUFFER_COUNT              32
+#define DDL_MIN_BUFFER_COUNT              1
+
+#define DDL_MPEG_REFBUF_COUNT             2
+#define DDL_MPEG_COMV_BUF_NO              2
+#define DDL_H263_COMV_BUF_NO              0
+#define DDL_COMV_BUFLINE_NO               128
+#define DDL_VC1_COMV_BUFLINE_NO           32
+
+#define DDL_MAX_H264_QP            51
+#define DDL_MAX_MPEG4_QP           31
+
+#define DDL_CONCEALMENT_Y_COLOR                 16
+#define DDL_CONCEALMENT_C_COLOR                 128
+
+#define DDL_ALLOW_DEC_FRAMESIZE(width, height) \
+	((DDL_NO_OF_MB(width, height) <= \
+	MAX_FRAME_SIZE_L4PT0_MBS) && \
+	(width <= DDL_MAX_FRAME_WIDTH) && \
+	(height <= DDL_MAX_FRAME_WIDTH) && \
+	((width >= 32 && height >= 16) || \
+	(width >= 16 && height >= 32)))
+
+#define DDL_ALLOW_ENC_FRAMESIZE(width, height) \
+	((DDL_NO_OF_MB(width, height) <= \
+	MAX_FRAME_SIZE_L4PT0_MBS) && \
+	(width <= DDL_MAX_FRAME_WIDTH) && \
+	(height <= DDL_MAX_FRAME_WIDTH) && \
+	((width >= 32 && height >= 32)))
+
+#define DDL_LINEAR_ALIGN_WIDTH      16
+#define DDL_LINEAR_ALIGN_HEIGHT     16
+#define DDL_LINEAR_MULTIPLY_FACTOR  2048
+#define DDL_TILE_ALIGN_WIDTH        128
+#define DDL_TILE_ALIGN_HEIGHT       32
+#define DDL_TILE_MULTIPLY_FACTOR    8192
+#define DDL_TILE_ALIGN(val, grid) \
+	(((val) + (grid) - 1) / (grid) * (grid))
+
+#define VCD_DDL_720P_YUV_BUF_SIZE     ((1280*720*3) >> 1)
+#define VCD_DDL_WVGA_BUF_SIZE         (800*480)
+
+#define VCD_DDL_TEST_MAX_WIDTH        (DDL_MAX_FRAME_WIDTH)
+#define VCD_DDL_TEST_MAX_HEIGHT       (DDL_MAX_FRAME_HEIGHT)
+
+#define VCD_DDL_TEST_MAX_NUM_H264_DPB  8
+
+#define VCD_DDL_TEST_NUM_ENC_INPUT_BUFS   6
+#define VCD_DDL_TEST_NUM_ENC_OUTPUT_BUFS  4
+
+#define VCD_DDL_TEST_DEFAULT_WIDTH       176
+#define VCD_DDL_TEST_DEFAULT_HEIGHT      144
+
+#define DDL_PIXEL_CACHE_NOT_IDLE          0x4000
+#define DDL_PIXEL_CACHE_STATUS_READ_RETRY 10
+#define DDL_PIXEL_CACHE_STATUS_READ_SLEEP 200
+
+#define DDL_RESL_CHANGE_NO_CHANGE               0
+#define DDL_RESL_CHANGE_INCREASED               1
+#define DDL_RESL_CHANGE_DECREASED               2
+
+#define VIDC_SM_ERR_CONCEALMENT_ENABLE				1
+#define VIDC_SM_ERR_CONCEALMENT_INTER_SLICE_MB_COPY		2
+#define VIDC_SM_ERR_CONCEALMENT_INTRA_SLICE_COLOR_CONCEALMENT	1
+
+#endif
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_errors.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_errors.c
new file mode 100644
index 0000000..a2327d5
--- /dev/null
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_errors.c
@@ -0,0 +1,771 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "vcd_ddl.h"
+#include "vcd_ddl_shared_mem.h"
+#include "vidc.h"
+
+static u32 ddl_handle_hw_fatal_errors(struct ddl_client_context *ddl);
+static u32 ddl_handle_client_fatal_errors(
+	struct ddl_client_context *ddl);
+static void ddl_input_failed_cb(struct ddl_client_context *ddl,
+	u32 vcd_event, u32 vcd_status);
+static u32 ddl_handle_core_recoverable_errors(
+	struct ddl_client_context *ddl);
+static u32 ddl_handle_core_warnings(u32 error_code);
+static void ddl_release_prev_field(
+	struct ddl_client_context *ddl);
+static u32 ddl_handle_dec_seq_hdr_fail_error(struct ddl_client_context *ddl);
+static void print_core_errors(u32 error_code);
+static void print_core_recoverable_errors(u32 error_code);
+
+void ddl_hw_fatal_cb(struct ddl_client_context *ddl)
+{
+	struct ddl_context *ddl_context = ddl->ddl_context;
+	u32 error_code = ddl_context->cmd_err_status;
+
+	DDL_MSG_FATAL("VIDC_HW_FATAL");
+	ddl->cmd_state = DDL_CMD_INVALID;
+	ddl_context->device_state = DDL_DEVICE_HWFATAL;
+
+	ddl_context->ddl_callback(VCD_EVT_IND_HWERRFATAL, VCD_ERR_HW_FATAL,
+		&error_code, sizeof(error_code),
+		(u32 *)ddl, ddl->client_data);
+
+	ddl_release_command_channel(ddl_context, ddl->command_channel);
+}
+
+static u32 ddl_handle_hw_fatal_errors(struct ddl_client_context *ddl)
+{
+	struct ddl_context *ddl_context = ddl->ddl_context;
+	u32 status = false, error_code = ddl_context->cmd_err_status;
+
+	switch (error_code) {
+	case VIDC_1080P_ERROR_INVALID_CHANNEL_NUMBER:
+	case VIDC_1080P_ERROR_INVALID_COMMAND_ID:
+	case VIDC_1080P_ERROR_CHANNEL_ALREADY_IN_USE:
+	case VIDC_1080P_ERROR_CHANNEL_NOT_OPEN_BEFORE_CHANNEL_CLOSE:
+	case VIDC_1080P_ERROR_OPEN_CH_ERROR_SEQ_START:
+	case VIDC_1080P_ERROR_SEQ_START_ALREADY_CALLED:
+	case VIDC_1080P_ERROR_OPEN_CH_ERROR_INIT_BUFFERS:
+	case VIDC_1080P_ERROR_SEQ_START_ERROR_INIT_BUFFERS:
+	case VIDC_1080P_ERROR_INIT_BUFFER_ALREADY_CALLED:
+	case VIDC_1080P_ERROR_OPEN_CH_ERROR_FRAME_START:
+	case VIDC_1080P_ERROR_SEQ_START_ERROR_FRAME_START:
+	case VIDC_1080P_ERROR_INIT_BUFFERS_ERROR_FRAME_START:
+	case VIDC_1080P_ERROR_RESOLUTION_CHANGED:
+	case VIDC_1080P_ERROR_INVALID_COMMAND_LAST_FRAME:
+	case VIDC_1080P_ERROR_INVALID_COMMAND:
+	case VIDC_1080P_ERROR_INVALID_CODEC_TYPE:
+	case VIDC_1080P_ERROR_MEM_ALLOCATION_FAILED:
+	case VIDC_1080P_ERROR_INSUFFICIENT_CONTEXT_SIZE:
+	case VIDC_1080P_ERROR_DIVIDE_BY_ZERO:
+	case VIDC_1080P_ERROR_DESCRIPTOR_BUFFER_EMPTY:
+	case VIDC_1080P_ERROR_DMA_TX_NOT_COMPLETE:
+	case VIDC_1080P_ERROR_VSP_NOT_READY:
+	case VIDC_1080P_ERROR_BUFFER_FULL_STATE:
+		ddl_hw_fatal_cb(ddl);
+		status = true;
+	break;
+	default:
+	break;
+	}
+	return status;
+}
+
+void ddl_client_fatal_cb(struct ddl_client_context *ddl)
+{
+	struct ddl_context *ddl_context = ddl->ddl_context;
+
+	if (ddl->cmd_state == DDL_CMD_DECODE_FRAME)
+		ddl_vidc_decode_dynamic_property(ddl, false);
+	else if (ddl->cmd_state == DDL_CMD_ENCODE_FRAME)
+		ddl_vidc_encode_dynamic_property(ddl, false);
+	ddl->cmd_state = DDL_CMD_INVALID;
+	DDL_MSG_LOW("ddl_state_transition: %s ~~> DDL_CLIENT_FAVIDC_ERROR",
+		ddl_get_state_string(ddl->client_state));
+	ddl->client_state = DDL_CLIENT_FAVIDC_ERROR;
+	ddl_context->ddl_callback(VCD_EVT_IND_HWERRFATAL,
+		VCD_ERR_CLIENT_FATAL, NULL, 0, (u32 *)ddl,
+		ddl->client_data);
+	ddl_release_command_channel(ddl_context, ddl->command_channel);
+}
+
+static u32 ddl_handle_client_fatal_errors(
+	struct ddl_client_context *ddl)
+{
+	struct ddl_context *ddl_context = ddl->ddl_context;
+	u32 status = false;
+
+	switch (ddl_context->cmd_err_status) {
+	case VIDC_1080P_ERROR_UNSUPPORTED_FEATURE_IN_PROFILE:
+	case VIDC_1080P_ERROR_RESOLUTION_NOT_SUPPORTED:
+	case VIDC_1080P_ERROR_VOS_END_CODE_RECEIVED:
+	case VIDC_1080P_ERROR_FRAME_RATE_NOT_SUPPORTED:
+	case VIDC_1080P_ERROR_INVALID_QP_VALUE:
+	case VIDC_1080P_ERROR_INVALID_RC_REACTION_COEFFICIENT:
+	case VIDC_1080P_ERROR_INVALID_CPB_SIZE_AT_GIVEN_LEVEL:
+	case VIDC_1080P_ERROR_B_FRAME_NOT_SUPPORTED:
+	case VIDC_1080P_ERROR_ALLOC_DPB_SIZE_NOT_SUFFICIENT:
+	case VIDC_1080P_ERROR_NUM_DPB_OUT_OF_RANGE:
+	case VIDC_1080P_ERROR_NULL_METADATA_INPUT_POINTER:
+	case VIDC_1080P_ERROR_NULL_DPB_POINTER:
+	case VIDC_1080P_ERROR_NULL_OTH_EXT_BUFADDR:
+	case VIDC_1080P_ERROR_NULL_MV_POINTER:
+		status = true;
+		DDL_MSG_ERROR("VIDC_CLIENT_FATAL!!");
+	break;
+	default:
+	break;
+	}
+	if (!status)
+		DDL_MSG_ERROR("VIDC_UNKNOWN_OP_FAILED %d",
+				ddl_context->cmd_err_status);
+	ddl_client_fatal_cb(ddl);
+	return true;
+}
+
+static void ddl_input_failed_cb(struct ddl_client_context *ddl,
+	u32 vcd_event, u32 vcd_status)
+{
+	struct ddl_context *ddl_context = ddl->ddl_context;
+	u32 payload_size = sizeof(struct ddl_frame_data_tag);
+
+	ddl->cmd_state = DDL_CMD_INVALID;
+	if (ddl->decoding)
+		ddl_vidc_decode_dynamic_property(ddl, false);
+	else
+		ddl_vidc_encode_dynamic_property(ddl, false);
+	if (ddl->client_state == DDL_CLIENT_WAIT_FOR_INITCODECDONE) {
+		payload_size = 0;
+		DDL_MSG_LOW("ddl_state_transition: %s ~~> "
+			"DDL_CLIENT_WAIT_FOR_INITCODEC",
+			ddl_get_state_string(ddl->client_state));
+		ddl->client_state = DDL_CLIENT_WAIT_FOR_INITCODEC;
+	} else {
+		DDL_MSG_LOW("ddl_state_transition: %s ~~> "
+			"DDL_CLIENT_WAIT_FOR_FRAME",
+			ddl_get_state_string(ddl->client_state));
+		ddl->client_state = DDL_CLIENT_WAIT_FOR_FRAME;
+	}
+	if (vcd_status == VCD_ERR_IFRAME_EXPECTED)
+		vcd_status = VCD_S_SUCCESS;
+	ddl_context->ddl_callback(vcd_event, vcd_status, &ddl->input_frame,
+		payload_size, (u32 *)ddl, ddl->client_data);
+}
+
+static u32 ddl_handle_core_recoverable_errors(
+	struct ddl_client_context *ddl)
+{
+	struct ddl_context *ddl_context = ddl->ddl_context;
+	u32 vcd_status = VCD_S_SUCCESS;
+	u32 vcd_event = VCD_EVT_RESP_INPUT_DONE;
+	u32 eos = false, status = false;
+
+	if (ddl->decoding) {
+		if (ddl_handle_dec_seq_hdr_fail_error(ddl))
+			return true;
+	}
+
+	if ((ddl->cmd_state != DDL_CMD_DECODE_FRAME) &&
+		(ddl->cmd_state != DDL_CMD_ENCODE_FRAME))
+		return false;
+
+	if (ddl->decoding &&
+		(ddl->codec_data.decoder.field_needed_for_prev_ip == 1)) {
+		ddl->codec_data.decoder.field_needed_for_prev_ip = 0;
+		ddl_release_prev_field(ddl);
+		if (ddl_context->cmd_err_status ==
+		 VIDC_1080P_ERROR_NON_PAIRED_FIELD_NOT_SUPPORTED) {
+			ddl_vidc_decode_frame_run(ddl);
+			return true;
+		}
+	}
+
+	switch (ddl_context->cmd_err_status) {
+	case VIDC_1080P_ERROR_SYNC_POINT_NOT_RECEIVED:
+		vcd_status = VCD_ERR_IFRAME_EXPECTED;
+		break;
+	case VIDC_1080P_ERROR_NO_BUFFER_RELEASED_FROM_HOST:
+		{
+			u32 pending_display = 0, release_mask;
+
+			release_mask =
+				ddl->codec_data.decoder.\
+				dpb_mask.hw_mask;
+			while (release_mask > 0) {
+				if (release_mask & 0x1)
+					pending_display++;
+				release_mask >>= 1;
+			}
+			if (pending_display >= ddl->codec_data.\
+				decoder.min_dpb_num) {
+				DDL_MSG_ERROR("VIDC_FW_ISSUE_REQ_BUF");
+				ddl_client_fatal_cb(ddl);
+				status = true;
+			} else {
+				vcd_event = VCD_EVT_RESP_OUTPUT_REQ;
+				DDL_MSG_LOW("VIDC_OUTPUT_BUF_REQ!!");
+			}
+			break;
+		}
+	case VIDC_1080P_ERROR_BIT_STREAM_BUF_EXHAUST:
+	case VIDC_1080P_ERROR_DESCRIPTOR_TABLE_ENTRY_INVALID:
+	case VIDC_1080P_ERROR_MB_COEFF_NOT_DONE:
+	case VIDC_1080P_ERROR_CODEC_SLICE_NOT_DONE:
+	case VIDC_1080P_ERROR_VIDC_CORE_TIME_OUT:
+	case VIDC_1080P_ERROR_VC1_BITPLANE_DECODE_ERR:
+	case VIDC_1080P_ERROR_RESOLUTION_MISMATCH:
+	case VIDC_1080P_ERROR_NV_QUANT_ERR:
+	case VIDC_1080P_ERROR_SYNC_MARKER_ERR:
+	case VIDC_1080P_ERROR_FEATURE_NOT_SUPPORTED:
+	case VIDC_1080P_ERROR_MEM_CORRUPTION:
+	case VIDC_1080P_ERROR_INVALID_REFERENCE_FRAME:
+	case VIDC_1080P_ERROR_PICTURE_CODING_TYPE_ERR:
+	case VIDC_1080P_ERROR_MV_RANGE_ERR:
+	case VIDC_1080P_ERROR_PICTURE_STRUCTURE_ERR:
+	case VIDC_1080P_ERROR_SLICE_ADDR_INVALID:
+	case VIDC_1080P_ERROR_NON_FRAME_DATA_RECEIVED:
+	case VIDC_1080P_ERROR_NALU_HEADER_ERROR:
+	case VIDC_1080P_ERROR_SPS_PARSE_ERROR:
+	case VIDC_1080P_ERROR_PPS_PARSE_ERROR:
+	case VIDC_1080P_ERROR_HEADER_NOT_FOUND:
+	case VIDC_1080P_ERROR_SLICE_PARSE_ERROR:
+	case VIDC_1080P_ERROR_NON_PAIRED_FIELD_NOT_SUPPORTED:
+		vcd_status = VCD_ERR_BITSTREAM_ERR;
+		DDL_MSG_ERROR("VIDC_BIT_STREAM_ERR");
+		break;
+	case VIDC_1080P_ERROR_B_FRAME_NOT_SUPPORTED:
+	case VIDC_1080P_ERROR_UNSUPPORTED_FEATURE_IN_PROFILE:
+	case VIDC_1080P_ERROR_RESOLUTION_NOT_SUPPORTED:
+		if (ddl->decoding) {
+			vcd_status = VCD_ERR_BITSTREAM_ERR;
+			DDL_MSG_ERROR("VIDC_BIT_STREAM_ERR");
+		}
+		break;
+	default:
+		break;
+	}
+
+	if (((vcd_status) || (vcd_event != VCD_EVT_RESP_INPUT_DONE)) &&
+		!status) {
+				ddl->input_frame.frm_trans_end = true;
+		eos = ((vcd_event == VCD_EVT_RESP_INPUT_DONE) &&
+			(ddl->input_frame.vcd_frm.flags & VCD_FRAME_FLAG_EOS));
+		if (((ddl->decoding) && (eos)) || !ddl->decoding)
+			ddl->input_frame.frm_trans_end = false;
+		ddl_input_failed_cb(ddl, vcd_event, vcd_status);
+		if (!ddl->decoding) {
+			ddl->output_frame.frm_trans_end = !eos;
+			ddl->output_frame.vcd_frm.data_len = 0;
+			ddl_context->ddl_callback(VCD_EVT_RESP_OUTPUT_DONE,
+				VCD_ERR_FAIL, &ddl->output_frame,
+				sizeof(struct ddl_frame_data_tag), (u32 *)ddl,
+				ddl->client_data);
+			if (eos) {
+				DDL_MSG_LOW("VIDC_ENC_EOS_DONE");
+				ddl_context->ddl_callback(VCD_EVT_RESP_EOS_DONE,
+					VCD_S_SUCCESS, NULL, 0, (u32 *)ddl,
+					ddl->client_data);
+			}
+		}
+		if ((ddl->decoding) && (eos))
+			ddl_vidc_decode_eos_run(ddl);
+		else
+			ddl_release_command_channel(ddl_context,
+				ddl->command_channel);
+			status = true;
+	}
+	return status;
+}
+
+static u32 ddl_handle_core_warnings(u32 err_status)
+{
+	u32 status = false;
+
+	switch (err_status) {
+	case VIDC_1080P_WARN_COMMAND_FLUSHED:
+	case VIDC_1080P_WARN_FRAME_RATE_UNKNOWN:
+	case VIDC_1080P_WARN_ASPECT_RATIO_UNKNOWN:
+	case VIDC_1080P_WARN_COLOR_PRIMARIES_UNKNOWN:
+	case VIDC_1080P_WARN_TRANSFER_CHAR_UNKNOWN:
+	case VIDC_1080P_WARN_MATRIX_COEFF_UNKNOWN:
+	case VIDC_1080P_WARN_NON_SEQ_SLICE_ADDR:
+	case VIDC_1080P_WARN_BROKEN_LINK:
+	case VIDC_1080P_WARN_FRAME_CONCEALED:
+	case VIDC_1080P_WARN_PROFILE_UNKNOWN:
+	case VIDC_1080P_WARN_LEVEL_UNKNOWN:
+	case VIDC_1080P_WARN_BIT_RATE_NOT_SUPPORTED:
+	case VIDC_1080P_WARN_COLOR_DIFF_FORMAT_NOT_SUPPORTED:
+	case VIDC_1080P_WARN_NULL_EXTRA_METADATA_POINTER:
+	case VIDC_1080P_WARN_DEBLOCKING_NOT_DONE:
+	case VIDC_1080P_WARN_INCOMPLETE_FRAME:
+	case VIDC_1080P_ERROR_NULL_FW_DEBUG_INFO_POINTER:
+	case VIDC_1080P_ERROR_ALLOC_DEBUG_INFO_SIZE_INSUFFICIENT:
+	case VIDC_1080P_WARN_METADATA_NO_SPACE_NUM_CONCEAL_MB:
+	case VIDC_1080P_WARN_METADATA_NO_SPACE_QP:
+	case VIDC_1080P_WARN_METADATA_NO_SPACE_CONCEAL_MB:
+	case VIDC_1080P_WARN_METADATA_NO_SPACE_VC1_PARAM:
+	case VIDC_1080P_WARN_METADATA_NO_SPACE_SEI:
+	case VIDC_1080P_WARN_METADATA_NO_SPACE_VUI:
+	case VIDC_1080P_WARN_METADATA_NO_SPACE_EXTRA:
+	case VIDC_1080P_WARN_METADATA_NO_SPACE_DATA_NONE:
+	case VIDC_1080P_WARN_METADATA_NO_SPACE_MB_INFO:
+	case VIDC_1080P_WARN_METADATA_NO_SPACE_SLICE_SIZE:
+	case VIDC_1080P_WARN_RESOLUTION_WARNING:
+	case VIDC_1080P_WARN_NO_LONG_TERM_REFERENCE:
+	case VIDC_1080P_WARN_NO_SPACE_MPEG2_DATA_DUMP:
+	case VIDC_1080P_WARN_METADATA_NO_SPACE_MISSING_MB:
+		status = true;
+		DDL_MSG_ERROR("VIDC_WARNING_IGNORED");
+	break;
+	default:
+	break;
+	}
+	return status;
+}
+
+u32 ddl_handle_core_errors(struct ddl_context *ddl_context)
+{
+	struct ddl_client_context *ddl;
+	u32 channel_inst_id, status = false;
+	u32 disp_status;
+
+	if (!ddl_context->cmd_err_status &&
+		!ddl_context->disp_pic_err_status) {
+		DDL_MSG_ERROR("VIDC_NO_ERROR");
+		return false;
+	}
+		vidc_1080p_get_returned_channel_inst_id(&channel_inst_id);
+		vidc_1080p_clear_returned_channel_inst_id();
+		ddl = ddl_get_current_ddl_client_for_channel_id(ddl_context,
+			ddl_context->response_cmd_ch_id);
+	if (!ddl) {
+		DDL_MSG_ERROR("VIDC_SPURIOUS_INTERRUPT_ERROR");
+		return true;
+	}
+	if (ddl_context->cmd_err_status) {
+		print_core_errors(ddl_context->cmd_err_status);
+		print_core_recoverable_errors(ddl_context->cmd_err_status);
+	}
+	if (ddl_context->disp_pic_err_status)
+		print_core_errors(ddl_context->disp_pic_err_status);
+	status = ddl_handle_core_warnings(ddl_context->cmd_err_status);
+	disp_status = ddl_handle_core_warnings(
+		ddl_context->disp_pic_err_status);
+	if (!status && !disp_status) {
+		DDL_MSG_ERROR("ddl_warning:Unknown");
+		status = ddl_handle_hw_fatal_errors(ddl);
+		if (!status)
+			status = ddl_handle_core_recoverable_errors(ddl);
+		if (!status)
+			status = ddl_handle_client_fatal_errors(ddl);
+	}
+	return status;
+}
+
+static void ddl_release_prev_field(struct ddl_client_context *ddl)
+{
+	ddl->output_frame.vcd_frm.ip_frm_tag =
+		ddl->codec_data.decoder.prev_ip_frm_tag;
+		ddl->output_frame.vcd_frm.physical = NULL;
+		ddl->output_frame.vcd_frm.virtual = NULL;
+		ddl->output_frame.frm_trans_end = false;
+		ddl->ddl_context->ddl_callback(VCD_EVT_RESP_OUTPUT_DONE,
+			VCD_ERR_INTRLCD_FIELD_DROP, &(ddl->output_frame),
+			sizeof(struct ddl_frame_data_tag),
+			(u32 *) ddl, ddl->client_data);
+}
+
+static u32 ddl_handle_dec_seq_hdr_fail_error(struct ddl_client_context *ddl)
+{
+	struct ddl_context *ddl_context = ddl->ddl_context;
+	u32 status = false;
+
+	if ((ddl->cmd_state != DDL_CMD_HEADER_PARSE) ||
+		(ddl->client_state != DDL_CLIENT_WAIT_FOR_INITCODECDONE)) {
+		DDL_MSG_ERROR("STATE-CRITICAL-HDDONE");
+		return false;
+	}
+
+	switch (ddl_context->cmd_err_status) {
+	case VIDC_1080P_ERROR_UNSUPPORTED_FEATURE_IN_PROFILE:
+	case VIDC_1080P_ERROR_RESOLUTION_NOT_SUPPORTED:
+	case VIDC_1080P_ERROR_HEADER_NOT_FOUND:
+	case VIDC_1080P_ERROR_SPS_PARSE_ERROR:
+	case VIDC_1080P_ERROR_PPS_PARSE_ERROR:
+	{
+		struct ddl_decoder_data *decoder = &ddl->codec_data.decoder;
+		if (ddl_context->cmd_err_status ==
+			VIDC_1080P_ERROR_UNSUPPORTED_FEATURE_IN_PROFILE
+			&& decoder->codec.codec == VCD_CODEC_H264) {
+			DDL_MSG_ERROR("Unsupported Feature for H264");
+			ddl_client_fatal_cb(ddl);
+			return true;
+		}
+		if ((ddl_context->cmd_err_status ==
+			VIDC_1080P_ERROR_RESOLUTION_NOT_SUPPORTED)
+			&& (decoder->codec.codec == VCD_CODEC_H263
+			|| decoder->codec.codec == VCD_CODEC_H264
+			|| decoder->codec.codec == VCD_CODEC_MPEG4
+			|| decoder->codec.codec == VCD_CODEC_VC1
+			|| decoder->codec.codec == VCD_CODEC_VC1_RCV)) {
+			DDL_MSG_ERROR("Unsupported resolution");
+			ddl_client_fatal_cb(ddl);
+			return true;
+		}
+
+		DDL_MSG_ERROR("SEQHDR-FAILED");
+		if (decoder->header_in_start) {
+			decoder->header_in_start = false;
+			ddl_context->ddl_callback(VCD_EVT_RESP_START,
+				VCD_ERR_SEQHDR_PARSE_FAIL, NULL, 0,
+				(u32 *) ddl, ddl->client_data);
+		} else {
+			ddl->input_frame.frm_trans_end = true;
+			if ((ddl->input_frame.vcd_frm.flags &
+				VCD_FRAME_FLAG_EOS)) {
+				ddl->input_frame.frm_trans_end = false;
+			}
+			ddl_vidc_decode_dynamic_property(ddl, false);
+			ddl_context->ddl_callback(
+				VCD_EVT_RESP_INPUT_DONE,
+				VCD_ERR_SEQHDR_PARSE_FAIL, &ddl->input_frame,
+				sizeof(struct ddl_frame_data_tag), (u32 *)ddl,
+				ddl->client_data);
+			if ((ddl->input_frame.vcd_frm.flags &
+				VCD_FRAME_FLAG_EOS)) {
+				DDL_MSG_HIGH("EOS_DONE-fromDDL");
+				ddl_context->ddl_callback(VCD_EVT_RESP_EOS_DONE,
+				VCD_S_SUCCESS, NULL, 0, (u32 *) ddl,
+				ddl->client_data);
+			}
+		}
+		DDL_MSG_LOW("ddl_state_transition: %s ~~> "
+			"DDL_CLIENT_WAIT_FOR_INITCODEC",
+			ddl_get_state_string(ddl->client_state));
+		ddl->client_state = DDL_CLIENT_WAIT_FOR_INITCODEC;
+		ddl_release_command_channel(ddl_context, ddl->command_channel);
+		status = true;
+		break;
+	}
+	default:
+		break;
+	}
+	return status;
+}
+
+void print_core_errors(u32 error_code)
+{
+	s8 *string = NULL;
+
+	switch (error_code) {
+	case VIDC_1080P_ERROR_INVALID_CHANNEL_NUMBER:
+		string = "VIDC_1080P_ERROR_INVALID_CHANNEL_NUMBER";
+	break;
+	case VIDC_1080P_ERROR_INVALID_COMMAND_ID:
+		string = "VIDC_1080P_ERROR_INVALID_COMMAND_ID";
+	break;
+	case VIDC_1080P_ERROR_CHANNEL_ALREADY_IN_USE:
+		string = "VIDC_1080P_ERROR_CHANNEL_ALREADY_IN_USE";
+	break;
+	case VIDC_1080P_ERROR_CHANNEL_NOT_OPEN_BEFORE_CHANNEL_CLOSE:
+		string =
+		"VIDC_1080P_ERROR_CHANNEL_NOT_OPEN_BEFORE_CHANNEL_CLOSE";
+	break;
+	case VIDC_1080P_ERROR_OPEN_CH_ERROR_SEQ_START:
+		string = "VIDC_1080P_ERROR_OPEN_CH_ERROR_SEQ_START";
+	break;
+	case VIDC_1080P_ERROR_SEQ_START_ALREADY_CALLED:
+		string = "VIDC_1080P_ERROR_SEQ_START_ALREADY_CALLED";
+	break;
+	case VIDC_1080P_ERROR_OPEN_CH_ERROR_INIT_BUFFERS:
+		string = "VIDC_1080P_ERROR_OPEN_CH_ERROR_INIT_BUFFERS";
+	break;
+	case VIDC_1080P_ERROR_SEQ_START_ERROR_INIT_BUFFERS:
+		string = "VIDC_1080P_ERROR_SEQ_START_ERROR_INIT_BUFFERS";
+	break;
+	case VIDC_1080P_ERROR_INIT_BUFFER_ALREADY_CALLED:
+		string = "VIDC_1080P_ERROR_INIT_BUFFER_ALREADY_CALLED";
+	break;
+	case VIDC_1080P_ERROR_OPEN_CH_ERROR_FRAME_START:
+		string = "VIDC_1080P_ERROR_OPEN_CH_ERROR_FRAME_START";
+	break;
+	case VIDC_1080P_ERROR_SEQ_START_ERROR_FRAME_START:
+		string = "VIDC_1080P_ERROR_SEQ_START_ERROR_FRAME_START";
+	break;
+	case VIDC_1080P_ERROR_INIT_BUFFERS_ERROR_FRAME_START:
+		string = "VIDC_1080P_ERROR_INIT_BUFFERS_ERROR_FRAME_START";
+	break;
+	case VIDC_1080P_ERROR_RESOLUTION_CHANGED:
+		string = "VIDC_1080P_ERROR_RESOLUTION_CHANGED";
+	break;
+	case VIDC_1080P_ERROR_INVALID_COMMAND_LAST_FRAME:
+		string = "VIDC_1080P_ERROR_INVALID_COMMAND_LAST_FRAME";
+	break;
+	case VIDC_1080P_ERROR_INVALID_COMMAND:
+		string = "VIDC_1080P_ERROR_INVALID_COMMAND";
+	break;
+	case VIDC_1080P_ERROR_INVALID_CODEC_TYPE:
+		string = "VIDC_1080P_ERROR_INVALID_CODEC_TYPE";
+	break;
+	case VIDC_1080P_ERROR_MEM_ALLOCATION_FAILED:
+		string = "VIDC_1080P_ERROR_MEM_ALLOCATION_FAILED";
+	break;
+	case VIDC_1080P_ERROR_INSUFFICIENT_CONTEXT_SIZE:
+		string = "VIDC_1080P_ERROR_INSUFFICIENT_CONTEXT_SIZE";
+	break;
+	case VIDC_1080P_ERROR_DIVIDE_BY_ZERO:
+		string = "VIDC_1080P_ERROR_DIVIDE_BY_ZERO";
+	break;
+	case VIDC_1080P_ERROR_DESCRIPTOR_BUFFER_EMPTY:
+		string = "VIDC_1080P_ERROR_DESCRIPTOR_BUFFER_EMPTY";
+	break;
+	case VIDC_1080P_ERROR_DMA_TX_NOT_COMPLETE:
+		string = "VIDC_1080P_ERROR_DMA_TX_NOT_COMPLETE";
+	break;
+	case VIDC_1080P_ERROR_VSP_NOT_READY:
+		string = "VIDC_1080P_ERROR_VSP_NOT_READY";
+	break;
+	case VIDC_1080P_ERROR_BUFFER_FULL_STATE:
+		string = "VIDC_1080P_ERROR_BUFFER_FULL_STATE";
+	break;
+	case VIDC_1080P_ERROR_UNSUPPORTED_FEATURE_IN_PROFILE:
+		string = "VIDC_1080P_ERROR_UNSUPPORTED_FEATURE_IN_PROFILE";
+	break;
+	case VIDC_1080P_ERROR_HEADER_NOT_FOUND:
+		string = "VIDC_1080P_ERROR_HEADER_NOT_FOUND";
+	break;
+	case VIDC_1080P_ERROR_VOS_END_CODE_RECEIVED:
+		string = "VIDC_1080P_ERROR_VOS_END_CODE_RECEIVED";
+	break;
+	case VIDC_1080P_ERROR_RESOLUTION_NOT_SUPPORTED:
+		string = "VIDC_1080P_ERROR_RESOLUTION_NOT_SUPPORTED";
+	break;
+	case VIDC_1080P_ERROR_FRAME_RATE_NOT_SUPPORTED:
+		string = "VIDC_1080P_ERROR_FRAME_RATE_NOT_SUPPORTED";
+	break;
+	case VIDC_1080P_ERROR_INVALID_QP_VALUE:
+		string = "VIDC_1080P_ERROR_INVALID_QP_VALUE";
+	break;
+	case VIDC_1080P_ERROR_INVALID_RC_REACTION_COEFFICIENT:
+		string = "VIDC_1080P_ERROR_INVALID_RC_REACTION_COEFFICIENT";
+	break;
+	case VIDC_1080P_ERROR_INVALID_CPB_SIZE_AT_GIVEN_LEVEL:
+		string = "VIDC_1080P_ERROR_INVALID_CPB_SIZE_AT_GIVEN_LEVEL";
+	break;
+	case VIDC_1080P_ERROR_B_FRAME_NOT_SUPPORTED:
+		string = "VIDC_1080P_ERROR_B_FRAME_NOT_SUPPORTED";
+	break;
+	case VIDC_1080P_ERROR_ALLOC_DPB_SIZE_NOT_SUFFICIENT:
+		string = "VIDC_1080P_ERROR_ALLOC_DPB_SIZE_NOT_SUFFICIENT";
+	break;
+	case VIDC_1080P_ERROR_NUM_DPB_OUT_OF_RANGE:
+		string = "VIDC_1080P_ERROR_NUM_DPB_OUT_OF_RANGE";
+	break;
+	case VIDC_1080P_ERROR_NULL_METADATA_INPUT_POINTER:
+		string = "VIDC_1080P_ERROR_NULL_METADATA_INPUT_POINTER";
+	break;
+	case VIDC_1080P_ERROR_NULL_DPB_POINTER:
+		string = "VIDC_1080P_ERROR_NULL_DPB_POINTER";
+	break;
+	case VIDC_1080P_ERROR_NULL_OTH_EXT_BUFADDR:
+		string = "VIDC_1080P_ERROR_NULL_OTH_EXT_BUFADDR";
+	break;
+	case VIDC_1080P_ERROR_NULL_MV_POINTER:
+		string = "VIDC_1080P_ERROR_NULL_MV_POINTER";
+	break;
+	case VIDC_1080P_ERROR_NON_PAIRED_FIELD_NOT_SUPPORTED:
+		string = "VIDC_1080P_ERROR_NON_PAIRED_FIELD_NOT_SUPPORTED";
+	break;
+	case VIDC_1080P_WARN_COMMAND_FLUSHED:
+		string = "VIDC_1080P_WARN_COMMAND_FLUSHED";
+	break;
+	case VIDC_1080P_WARN_FRAME_RATE_UNKNOWN:
+		string = "VIDC_1080P_WARN_FRAME_RATE_UNKNOWN";
+	break;
+	case VIDC_1080P_WARN_ASPECT_RATIO_UNKNOWN:
+		string = "VIDC_1080P_WARN_ASPECT_RATIO_UNKNOWN";
+	break;
+	case VIDC_1080P_WARN_COLOR_PRIMARIES_UNKNOWN:
+		string = "VIDC_1080P_WARN_COLOR_PRIMARIES_UNKNOWN";
+	break;
+	case VIDC_1080P_WARN_TRANSFER_CHAR_UNKNOWN:
+		string = "VIDC_1080P_WARN_TRANSFER_CHAR_UNKNOWN";
+	break;
+	case VIDC_1080P_WARN_MATRIX_COEFF_UNKNOWN:
+		string = "VIDC_1080P_WARN_MATRIX_COEFF_UNKNOWN";
+	break;
+	case VIDC_1080P_WARN_NON_SEQ_SLICE_ADDR:
+		string = "VIDC_1080P_WARN_NON_SEQ_SLICE_ADDR";
+	break;
+	case VIDC_1080P_WARN_BROKEN_LINK:
+		string = "VIDC_1080P_WARN_BROKEN_LINK";
+	break;
+	case VIDC_1080P_WARN_FRAME_CONCEALED:
+		string = "VIDC_1080P_WARN_FRAME_CONCEALED";
+	break;
+	case VIDC_1080P_WARN_PROFILE_UNKNOWN:
+		string = "VIDC_1080P_WARN_PROFILE_UNKNOWN";
+	break;
+	case VIDC_1080P_WARN_LEVEL_UNKNOWN:
+		string = "VIDC_1080P_WARN_LEVEL_UNKNOWN";
+	break;
+	case VIDC_1080P_WARN_BIT_RATE_NOT_SUPPORTED:
+		string = "VIDC_1080P_WARN_BIT_RATE_NOT_SUPPORTED";
+	break;
+	case VIDC_1080P_WARN_COLOR_DIFF_FORMAT_NOT_SUPPORTED:
+		string = "VIDC_1080P_WARN_COLOR_DIFF_FORMAT_NOT_SUPPORTED";
+	break;
+	case VIDC_1080P_WARN_NULL_EXTRA_METADATA_POINTER:
+		string = "VIDC_1080P_WARN_NULL_EXTRA_METADATA_POINTER";
+	break;
+	case VIDC_1080P_WARN_DEBLOCKING_NOT_DONE:
+		string = "VIDC_1080P_WARN_DEBLOCKING_NOT_DONE";
+	break;
+	case VIDC_1080P_WARN_INCOMPLETE_FRAME:
+		string = "VIDC_1080P_WARN_INCOMPLETE_FRAME";
+	break;
+	case VIDC_1080P_ERROR_NULL_FW_DEBUG_INFO_POINTER:
+		string = "VIDC_1080P_ERROR_NULL_FW_DEBUG_INFO_POINTER";
+	break;
+	case VIDC_1080P_ERROR_ALLOC_DEBUG_INFO_SIZE_INSUFFICIENT:
+		string =
+		"VIDC_1080P_ERROR_ALLOC_DEBUG_INFO_SIZE_INSUFFICIENT";
+	break;
+	case VIDC_1080P_WARN_METADATA_NO_SPACE_NUM_CONCEAL_MB:
+		string = "VIDC_1080P_WARN_METADATA_NO_SPACE_NUM_CONCEAL_MB";
+	break;
+	case VIDC_1080P_WARN_METADATA_NO_SPACE_QP:
+		string = "VIDC_1080P_WARN_METADATA_NO_SPACE_QP";
+	break;
+	case VIDC_1080P_WARN_METADATA_NO_SPACE_CONCEAL_MB:
+		string = "VIDC_1080P_WARN_METADATA_NO_SPACE_CONCEAL_MB";
+	break;
+	case VIDC_1080P_WARN_METADATA_NO_SPACE_VC1_PARAM:
+		string = "VIDC_1080P_WARN_METADATA_NO_SPACE_VC1_PARAM";
+	break;
+	case VIDC_1080P_WARN_METADATA_NO_SPACE_SEI:
+		string = "VIDC_1080P_WARN_METADATA_NO_SPACE_SEI";
+	break;
+	case VIDC_1080P_WARN_METADATA_NO_SPACE_VUI:
+		string = "VIDC_1080P_WARN_METADATA_NO_SPACE_VUI";
+	break;
+	case VIDC_1080P_WARN_METADATA_NO_SPACE_EXTRA:
+		string = "VIDC_1080P_WARN_METADATA_NO_SPACE_EXTRA";
+	break;
+	case VIDC_1080P_WARN_METADATA_NO_SPACE_DATA_NONE:
+		string = "VIDC_1080P_WARN_METADATA_NO_SPACE_DATA_NONE";
+	break;
+	case VIDC_1080P_WARN_METADATA_NO_SPACE_MB_INFO:
+		string = "VIDC_1080P_WARN_METADATA_NO_SPACE_MB_INFO";
+	break;
+	case VIDC_1080P_WARN_METADATA_NO_SPACE_SLICE_SIZE:
+		string = "VIDC_1080P_WARN_METADATA_NO_SPACE_SLICE_SIZE";
+	break;
+	case VIDC_1080P_WARN_RESOLUTION_WARNING:
+		string = "VIDC_1080P_WARN_RESOLUTION_WARNING";
+	break;
+	case VIDC_1080P_WARN_NO_LONG_TERM_REFERENCE:
+		string = "VIDC_1080P_WARN_NO_LONG_TERM_REFERENCE";
+	break;
+	case VIDC_1080P_WARN_NO_SPACE_MPEG2_DATA_DUMP:
+		string = "VIDC_1080P_WARN_NO_SPACE_MPEG2_DATA_DUMP";
+	break;
+	case VIDC_1080P_WARN_METADATA_NO_SPACE_MISSING_MB:
+		string = "VIDC_1080P_WARN_METADATA_NO_SPACE_MISSING_MB";
+	break;
+	}
+	if (string)
+		DDL_MSG_ERROR("Error code = 0x%x : %s", error_code, string);
+}
+
+void print_core_recoverable_errors(u32 error_code)
+{
+	s8 *string = NULL;
+
+	switch (error_code) {
+	case VIDC_1080P_ERROR_SYNC_POINT_NOT_RECEIVED:
+		string = "VIDC_1080P_ERROR_SYNC_POINT_NOT_RECEIVED";
+	break;
+	case VIDC_1080P_ERROR_NO_BUFFER_RELEASED_FROM_HOST:
+		string = "VIDC_1080P_ERROR_NO_BUFFER_RELEASED_FROM_HOST";
+	break;
+	case VIDC_1080P_ERROR_BIT_STREAM_BUF_EXHAUST:
+		string = "VIDC_1080P_ERROR_BIT_STREAM_BUF_EXHAUST";
+	break;
+	case VIDC_1080P_ERROR_DESCRIPTOR_TABLE_ENTRY_INVALID:
+		string = "VIDC_1080P_ERROR_DESCRIPTOR_TABLE_ENTRY_INVALID";
+	break;
+	case VIDC_1080P_ERROR_MB_COEFF_NOT_DONE:
+		string = "VIDC_1080P_ERROR_MB_COEFF_NOT_DONE";
+	break;
+	case VIDC_1080P_ERROR_CODEC_SLICE_NOT_DONE:
+		string = "VIDC_1080P_ERROR_CODEC_SLICE_NOT_DONE";
+	break;
+	case VIDC_1080P_ERROR_VIDC_CORE_TIME_OUT:
+		string = "VIDC_1080P_ERROR_VIDC_CORE_TIME_OUT";
+	break;
+	case VIDC_1080P_ERROR_VC1_BITPLANE_DECODE_ERR:
+		string = "VIDC_1080P_ERROR_VC1_BITPLANE_DECODE_ERR";
+	break;
+	case VIDC_1080P_ERROR_RESOLUTION_MISMATCH:
+		string = "VIDC_1080P_ERROR_RESOLUTION_MISMATCH";
+	break;
+	case VIDC_1080P_ERROR_NV_QUANT_ERR:
+		string = "VIDC_1080P_ERROR_NV_QUANT_ERR";
+	break;
+	case VIDC_1080P_ERROR_SYNC_MARKER_ERR:
+		string = "VIDC_1080P_ERROR_SYNC_MARKER_ERR";
+	break;
+	case VIDC_1080P_ERROR_FEATURE_NOT_SUPPORTED:
+		string = "VIDC_1080P_ERROR_FEATURE_NOT_SUPPORTED";
+	break;
+	case VIDC_1080P_ERROR_MEM_CORRUPTION:
+		string = "VIDC_1080P_ERROR_MEM_CORRUPTION";
+	break;
+	case VIDC_1080P_ERROR_INVALID_REFERENCE_FRAME:
+		string = "VIDC_1080P_ERROR_INVALID_REFERENCE_FRAME";
+	break;
+	case VIDC_1080P_ERROR_PICTURE_CODING_TYPE_ERR:
+		string = "VIDC_1080P_ERROR_PICTURE_CODING_TYPE_ERR";
+	break;
+	case VIDC_1080P_ERROR_MV_RANGE_ERR:
+		string = "VIDC_1080P_ERROR_MV_RANGE_ERR";
+	break;
+	case VIDC_1080P_ERROR_PICTURE_STRUCTURE_ERR:
+		string = "VIDC_1080P_ERROR_PICTURE_STRUCTURE_ERR";
+	break;
+	case VIDC_1080P_ERROR_SLICE_ADDR_INVALID:
+		string = "VIDC_1080P_ERROR_SLICE_ADDR_INVALID";
+	break;
+	case VIDC_1080P_ERROR_NON_FRAME_DATA_RECEIVED:
+		string = "VIDC_1080P_ERROR_NON_FRAME_DATA_RECEIVED";
+	break;
+	case VIDC_1080P_ERROR_NALU_HEADER_ERROR:
+		string = "VIDC_1080P_ERROR_NALU_HEADER_ERROR";
+	break;
+	case VIDC_1080P_ERROR_SPS_PARSE_ERROR:
+		string = "VIDC_1080P_ERROR_SPS_PARSE_ERROR";
+	break;
+	case VIDC_1080P_ERROR_PPS_PARSE_ERROR:
+		string = "VIDC_1080P_ERROR_PPS_PARSE_ERROR";
+	break;
+	case VIDC_1080P_ERROR_SLICE_PARSE_ERROR:
+		string = "VIDC_1080P_ERROR_SLICE_PARSE_ERROR";
+	break;
+	}
+	if (string)
+		DDL_MSG_ERROR("Recoverable Error code = 0x%x : %s",
+					  error_code, string);
+}
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_helper.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_helper.c
new file mode 100644
index 0000000..64d2976
--- /dev/null
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_helper.c
@@ -0,0 +1,1074 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/ion.h>
+#include <mach/msm_memtypes.h>
+#include "vcd_ddl.h"
+#include "vcd_ddl_shared_mem.h"
+#include "vcd_res_tracker_api.h"
+
+struct ddl_context *ddl_get_context(void)
+{
+	static struct ddl_context ddl_context;
+	return &ddl_context;
+}
+
+#ifdef DDL_MSG_LOG
+s8 *ddl_get_state_string(enum ddl_client_state client_state)
+{
+	s8 *ptr;
+
+	switch (client_state) {
+	case DDL_CLIENT_INVALID:
+		ptr = "INVALID        ";
+	break;
+	case DDL_CLIENT_OPEN:
+		ptr = "OPEN   ";
+	break;
+	case DDL_CLIENT_WAIT_FOR_CHDONE:
+		ptr = "WAIT_FOR_CHDONE       ";
+	break;
+	case DDL_CLIENT_WAIT_FOR_INITCODEC:
+		ptr = "WAIT_FOR_INITCODEC    ";
+	break;
+	case DDL_CLIENT_WAIT_FOR_INITCODECDONE:
+		ptr = "WAIT_FOR_INITCODECDONE";
+	break;
+	case DDL_CLIENT_WAIT_FOR_DPB:
+		ptr = "WAIT_FOR_DPB   ";
+	break;
+	case DDL_CLIENT_WAIT_FOR_DPBDONE:
+		ptr = "WAIT_FOR_DPBDONE";
+	break;
+	case DDL_CLIENT_WAIT_FOR_FRAME:
+		ptr = "WAIT_FOR_FRAME ";
+	break;
+	case DDL_CLIENT_WAIT_FOR_FRAME_DONE:
+		ptr = "WAIT_FOR_FRAME_DONE   ";
+	break;
+	case DDL_CLIENT_WAIT_FOR_EOS_DONE:
+		ptr = "WAIT_FOR_EOS_DONE     ";
+	break;
+	case DDL_CLIENT_WAIT_FOR_CHEND:
+		ptr = "WAIT_FOR_CHEND ";
+	break;
+	case DDL_CLIENT_FATAL_ERROR:
+		ptr = "FATAL_ERROR";
+	break;
+	default:
+		ptr = "UNKNOWN        ";
+	break;
+	}
+	return ptr;
+}
+#endif
+
+u32 ddl_client_transact(u32 operation,
+	struct ddl_client_context **pddl_client)
+{
+	struct ddl_context *ddl_context;
+	u32 ret_status = VCD_ERR_FAIL;
+	s32 counter;
+
+	ddl_context = ddl_get_context();
+	switch (operation) {
+	case DDL_FREE_CLIENT:
+		ret_status = VCD_ERR_MAX_CLIENT;
+		for (counter = 0; (counter < VCD_MAX_NO_CLIENT) &&
+			(ret_status == VCD_ERR_MAX_CLIENT); ++counter) {
+			if (*pddl_client == ddl_context->ddl_clients
+				[counter]) {
+					kfree(*pddl_client);
+					*pddl_client = NULL;
+					ddl_context->ddl_clients[counter]
+						= NULL;
+				ret_status = VCD_S_SUCCESS;
+			}
+		}
+	break;
+	case DDL_GET_CLIENT:
+		ret_status = VCD_ERR_MAX_CLIENT;
+		for (counter = (VCD_MAX_NO_CLIENT - 1); (counter >= 0) &&
+			(ret_status == VCD_ERR_MAX_CLIENT); --counter) {
+			if (!ddl_context->ddl_clients[counter]) {
+				*pddl_client =
+					(struct ddl_client_context *)
+					kmalloc(sizeof(struct
+					ddl_client_context), GFP_KERNEL);
+				if (!*pddl_client)
+					ret_status = VCD_ERR_ALLOC_FAIL;
+				else {
+					memset(*pddl_client, 0,
+						sizeof(struct
+						ddl_client_context));
+					ddl_context->ddl_clients
+						[counter] = *pddl_client;
+					(*pddl_client)->ddl_context =
+						ddl_context;
+					ret_status = VCD_S_SUCCESS;
+				}
+			}
+		}
+	break;
+	case DDL_INIT_CLIENTS:
+		for (counter = 0; counter < VCD_MAX_NO_CLIENT; ++counter)
+			ddl_context->ddl_clients[counter] = NULL;
+		ret_status = VCD_S_SUCCESS;
+	break;
+	case DDL_ACTIVE_CLIENT:
+		for (counter = 0; counter < VCD_MAX_NO_CLIENT;
+			++counter) {
+			if (ddl_context->ddl_clients[counter]) {
+				ret_status = VCD_S_SUCCESS;
+				break;
+			}
+		}
+	break;
+	default:
+		ret_status = VCD_ERR_ILLEGAL_PARM;
+	break;
+	}
+	return ret_status;
+}
+
+u32 ddl_decoder_dpb_transact(struct ddl_decoder_data *decoder,
+	struct ddl_frame_data_tag  *in_out_frame, u32 operation)
+{
+	struct ddl_frame_data_tag *found_frame = NULL;
+	struct ddl_mask *dpb_mask = &decoder->dpb_mask;
+	u32 vcd_status = VCD_S_SUCCESS, loopc;
+
+	switch (operation) {
+	case DDL_DPB_OP_MARK_BUSY:
+	case DDL_DPB_OP_MARK_FREE:
+		for (loopc = 0; !found_frame && loopc <
+			decoder->dp_buf.no_of_dec_pic_buf; ++loopc) {
+			if (in_out_frame->vcd_frm.physical ==
+				decoder->dp_buf.dec_pic_buffers[loopc].
+				vcd_frm.physical) {
+				found_frame = &(decoder->dp_buf.
+					dec_pic_buffers[loopc]);
+			break;
+			}
+		}
+		if (found_frame) {
+			if (operation == DDL_DPB_OP_MARK_BUSY) {
+				dpb_mask->hw_mask &=
+					(~(u32)(0x1 << loopc));
+				*in_out_frame = *found_frame;
+			} else if (operation == DDL_DPB_OP_MARK_FREE) {
+				dpb_mask->client_mask |= (0x1 << loopc);
+				*found_frame = *in_out_frame;
+			}
+		} else {
+			in_out_frame->vcd_frm.physical = NULL;
+			in_out_frame->vcd_frm.virtual = NULL;
+			vcd_status = VCD_ERR_BAD_POINTER;
+			DDL_MSG_ERROR("BUF_NOT_FOUND");
+		}
+	break;
+	case DDL_DPB_OP_SET_MASK:
+		dpb_mask->hw_mask |= dpb_mask->client_mask;
+		dpb_mask->client_mask = 0;
+	break;
+	case DDL_DPB_OP_INIT:
+	{
+		u32 dpb_size;
+		dpb_size = (!decoder->meta_data_offset) ?
+		decoder->dp_buf.dec_pic_buffers[0].vcd_frm.alloc_len :
+			decoder->meta_data_offset;
+	}
+	break;
+	case DDL_DPB_OP_RETRIEVE:
+	{
+		u32 position;
+		if (dpb_mask->client_mask) {
+			position = 0x1;
+			for (loopc = 0; loopc <
+				decoder->dp_buf.no_of_dec_pic_buf &&
+				!found_frame; ++loopc) {
+				if (dpb_mask->client_mask & position) {
+					found_frame = &decoder->dp_buf.
+						dec_pic_buffers[loopc];
+					dpb_mask->client_mask &=
+						~(position);
+				}
+				position <<= 1;
+			}
+		} else if (dpb_mask->hw_mask) {
+			position = 0x1;
+			for (loopc = 0; loopc <
+				decoder->dp_buf.no_of_dec_pic_buf &&
+				!found_frame; ++loopc) {
+				if (dpb_mask->hw_mask & position) {
+					found_frame = &decoder->dp_buf.
+						dec_pic_buffers[loopc];
+					dpb_mask->hw_mask &= ~(position);
+				}
+				position <<= 1;
+			}
+		}
+		if (found_frame)
+			*in_out_frame = *found_frame;
+		else {
+			in_out_frame->vcd_frm.physical = NULL;
+			in_out_frame->vcd_frm.virtual = NULL;
+		}
+	}
+	break;
+	default:
+	break;
+	}
+	return vcd_status;
+}
+
+u32 ddl_decoder_dpb_init(struct ddl_client_context *ddl)
+{
+	struct ddl_context *ddl_context = ddl->ddl_context;
+	struct ddl_decoder_data *decoder = &ddl->codec_data.decoder;
+	struct ddl_dec_buffers *dec_buffers = &decoder->hw_bufs;
+	struct ddl_frame_data_tag *frame;
+	u32 luma[DDL_MAX_BUFFER_COUNT], chroma[DDL_MAX_BUFFER_COUNT];
+	u32 mv[DDL_MAX_BUFFER_COUNT], luma_size, i, dpb;
+	frame = &decoder->dp_buf.dec_pic_buffers[0];
+	luma_size = ddl_get_yuv_buf_size(decoder->frame_size.width,
+			decoder->frame_size.height, DDL_YUV_BUF_TYPE_TILE);
+	dpb = decoder->dp_buf.no_of_dec_pic_buf;
+	DDL_MSG_LOW("%s Decoder num DPB buffers = %u Luma Size = %u"
+				 __func__, dpb, luma_size);
+	if (dpb > DDL_MAX_BUFFER_COUNT)
+		dpb = DDL_MAX_BUFFER_COUNT;
+	for (i = 0; i < dpb; i++) {
+		if (!(res_trk_check_for_sec_session()) &&
+			frame[i].vcd_frm.virtual) {
+			if (luma_size <= frame[i].vcd_frm.alloc_len) {
+				memset(frame[i].vcd_frm.virtual,
+					 0x10101010, luma_size);
+				memset(frame[i].vcd_frm.virtual + luma_size,
+					 0x80808080,
+					frame[i].vcd_frm.alloc_len - luma_size);
+				if (frame[i].vcd_frm.ion_flag == CACHED) {
+					msm_ion_do_cache_op(
+					ddl_context->video_ion_client,
+					frame[i].vcd_frm.buff_ion_handle,
+					(unsigned long *)frame[i].
+					vcd_frm.virtual,
+					(unsigned long)frame[i].
+					vcd_frm.alloc_len,
+					ION_IOC_CLEAN_INV_CACHES);
+				}
+			} else {
+				DDL_MSG_ERROR("luma size error");
+				return VCD_ERR_FAIL;
+			}
+		}
+
+		luma[i] = DDL_OFFSET(ddl_context->dram_base_a.
+			align_physical_addr, frame[i].vcd_frm.physical);
+		chroma[i] = luma[i] + luma_size;
+		DDL_MSG_LOW("%s Decoder Luma address = %x Chroma address = %x"
+					__func__, luma[i], chroma[i]);
+	}
+	switch (decoder->codec.codec) {
+	case VCD_CODEC_MPEG1:
+	case VCD_CODEC_MPEG2:
+		vidc_1080p_set_decode_recon_buffers(dpb, luma, chroma);
+	break;
+	case VCD_CODEC_DIVX_3:
+	case VCD_CODEC_DIVX_4:
+	case VCD_CODEC_DIVX_5:
+	case VCD_CODEC_DIVX_6:
+	case VCD_CODEC_XVID:
+	case VCD_CODEC_MPEG4:
+		vidc_1080p_set_decode_recon_buffers(dpb, luma, chroma);
+		vidc_1080p_set_mpeg4_divx_decode_work_buffers(
+		DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+			dec_buffers->nb_dcac),
+		DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+			dec_buffers->upnb_mv),
+		DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+			dec_buffers->sub_anchor_mv),
+		DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+			dec_buffers->overlay_xform),
+		DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+			dec_buffers->stx_parser));
+	break;
+	case VCD_CODEC_H263:
+		vidc_1080p_set_decode_recon_buffers(dpb, luma, chroma);
+		vidc_1080p_set_h263_decode_work_buffers(
+			DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+				dec_buffers->nb_dcac),
+			DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+				dec_buffers->upnb_mv),
+			DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+				dec_buffers->sub_anchor_mv),
+			DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+				dec_buffers->overlay_xform));
+	break;
+	case VCD_CODEC_VC1:
+	case VCD_CODEC_VC1_RCV:
+		vidc_1080p_set_decode_recon_buffers(dpb, luma, chroma);
+		vidc_1080p_set_vc1_decode_work_buffers(
+			DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+				dec_buffers->nb_dcac),
+			DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+				dec_buffers->upnb_mv),
+			DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+				dec_buffers->sub_anchor_mv),
+			DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+				dec_buffers->overlay_xform),
+			DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+				dec_buffers->bit_plane1),
+			DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+				dec_buffers->bit_plane2),
+			DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+				dec_buffers->bit_plane3));
+	break;
+	case VCD_CODEC_H264:
+		for (i = 0; i < dpb; i++)
+			mv[i] = DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+					dec_buffers->h264_mv[i]);
+		vidc_1080p_set_h264_decode_buffers(dpb,
+			DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+				dec_buffers->h264_vert_nb_mv),
+			DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+				dec_buffers->h264_nb_ip),
+			luma, chroma, mv);
+	break;
+	default:
+	break;
+	}
+	return VCD_S_SUCCESS;
+}
+
+void ddl_release_context_buffers(struct ddl_context *ddl_context)
+{
+	ddl_pmem_free(&ddl_context->metadata_shared_input);
+	ddl_fw_release(&ddl_context->dram_base_a);
+}
+
+void ddl_release_client_internal_buffers(struct ddl_client_context *ddl)
+{
+	if (ddl->decoding) {
+		struct ddl_decoder_data *decoder =
+			&(ddl->codec_data.decoder);
+		kfree(decoder->dp_buf.dec_pic_buffers);
+		decoder->dp_buf.dec_pic_buffers = NULL;
+		ddl_vidc_decode_dynamic_property(ddl, false);
+		decoder->decode_config.sequence_header_len = 0;
+		decoder->decode_config.sequence_header = NULL;
+		decoder->dpb_mask.client_mask = 0;
+		decoder->dpb_mask.hw_mask = 0;
+		decoder->dp_buf.no_of_dec_pic_buf = 0;
+		decoder->dynamic_prop_change = 0;
+		ddl_free_dec_hw_buffers(ddl);
+	} else {
+		struct ddl_encoder_data *encoder =
+			&(ddl->codec_data.encoder);
+		ddl_pmem_free(&encoder->seq_header);
+		ddl_pmem_free(&encoder->batch_frame.slice_batch_in);
+		ddl_pmem_free(&encoder->batch_frame.slice_batch_out);
+		ddl_vidc_encode_dynamic_property(ddl, false);
+		encoder->dynamic_prop_change = 0;
+		ddl_free_enc_hw_buffers(ddl);
+	}
+	ddl_pmem_free(&ddl->shared_mem[0]);
+	ddl_pmem_free(&ddl->shared_mem[1]);
+}
+
+u32 ddl_codec_type_transact(struct ddl_client_context *ddl,
+	u32 remove, enum vcd_codec requested_codec)
+{
+	if (requested_codec > VCD_CODEC_VC1_RCV ||
+		requested_codec < VCD_CODEC_H264)
+		return false;
+	if (!ddl->decoding && requested_codec != VCD_CODEC_MPEG4 &&
+		requested_codec != VCD_CODEC_H264 &&
+		requested_codec != VCD_CODEC_H263)
+		return false;
+
+	return true;
+}
+
+u32 ddl_take_command_channel(struct ddl_context *ddl_context,
+	struct ddl_client_context *ddl, void *client_data)
+{
+	u32  status = true;
+
+	if (!ddl_context->current_ddl[0]) {
+		ddl_context->current_ddl[0] = ddl;
+		ddl->client_data = client_data;
+		ddl->command_channel = 0;
+	} else if (!ddl_context->current_ddl[1]) {
+		ddl_context->current_ddl[1] = ddl;
+		ddl->client_data = client_data;
+		ddl->command_channel = 1;
+	} else
+		status = false;
+	if (status) {
+		if (ddl_context->current_ddl[0] &&
+			ddl_context->current_ddl[1])
+			DDL_BUSY(ddl_context);
+		else
+			DDL_RUN(ddl_context);
+	}
+	return status;
+}
+
+void ddl_release_command_channel(struct ddl_context *ddl_context,
+	u32 command_channel)
+{
+	ddl_context->current_ddl[command_channel]->client_data = NULL;
+	ddl_context->current_ddl[command_channel] = NULL;
+	if (!ddl_context->current_ddl[0] &&
+		!ddl_context->current_ddl[1])
+		DDL_IDLE(ddl_context);
+	else
+		DDL_RUN(ddl_context);
+}
+
+struct ddl_client_context *ddl_get_current_ddl_client_for_channel_id(
+	struct ddl_context *ddl_context, u32 channel_id)
+{
+	struct ddl_client_context *ddl;
+
+	if (ddl_context->current_ddl[0] && channel_id ==
+		ddl_context->current_ddl[0]->command_channel)
+		ddl = ddl_context->current_ddl[0];
+	else if (ddl_context->current_ddl[1] && channel_id ==
+		ddl_context->current_ddl[1]->command_channel)
+		ddl = ddl_context->current_ddl[1];
+	else {
+		DDL_MSG_LOW("STATE-CRITICAL-FRMRUN");
+		DDL_MSG_ERROR("Unexpected channel ID = %d", channel_id);
+		ddl = NULL;
+	}
+	return ddl;
+}
+
+struct ddl_client_context *ddl_get_current_ddl_client_for_command(
+	struct ddl_context *ddl_context,
+	enum ddl_cmd_state cmd_state)
+{
+	struct ddl_client_context *ddl;
+
+	if (ddl_context->current_ddl[0] &&
+		cmd_state == ddl_context->current_ddl[0]->cmd_state)
+		ddl = ddl_context->current_ddl[0];
+	else if (ddl_context->current_ddl[1] &&
+		cmd_state == ddl_context->current_ddl[1]->cmd_state)
+		ddl = ddl_context->current_ddl[1];
+	else {
+		DDL_MSG_LOW("STATE-CRITICAL-FRMRUN");
+		DDL_MSG_ERROR("Error: Unexpected cmd_state = %d",
+			cmd_state);
+		ddl = NULL;
+	}
+	return ddl;
+}
+
+u32 ddl_get_yuv_buf_size(u32 width, u32 height, u32 format)
+{
+	u32 mem_size, width_round_up, height_round_up, align;
+
+	width_round_up  = width;
+	height_round_up = height;
+	if (format == DDL_YUV_BUF_TYPE_TILE) {
+		width_round_up  = DDL_ALIGN(width, DDL_TILE_ALIGN_WIDTH);
+		height_round_up = DDL_ALIGN(height, DDL_TILE_ALIGN_HEIGHT);
+		align = DDL_TILE_MULTIPLY_FACTOR;
+	}
+	if (format == DDL_YUV_BUF_TYPE_LINEAR) {
+		width_round_up = DDL_ALIGN(width, DDL_LINEAR_ALIGN_WIDTH);
+		align = DDL_LINEAR_MULTIPLY_FACTOR;
+	}
+	mem_size = (width_round_up * height_round_up);
+	mem_size = DDL_ALIGN(mem_size, align);
+	return mem_size;
+}
+void ddl_free_dec_hw_buffers(struct ddl_client_context *ddl)
+{
+	struct ddl_dec_buffers *dec_bufs =
+		&ddl->codec_data.decoder.hw_bufs;
+	ddl_pmem_free(&dec_bufs->h264_nb_ip);
+	ddl_pmem_free(&dec_bufs->h264_vert_nb_mv);
+	ddl_pmem_free(&dec_bufs->nb_dcac);
+	ddl_pmem_free(&dec_bufs->upnb_mv);
+	ddl_pmem_free(&dec_bufs->sub_anchor_mv);
+	ddl_pmem_free(&dec_bufs->overlay_xform);
+	ddl_pmem_free(&dec_bufs->bit_plane3);
+	ddl_pmem_free(&dec_bufs->bit_plane2);
+	ddl_pmem_free(&dec_bufs->bit_plane1);
+	ddl_pmem_free(&dec_bufs->stx_parser);
+	ddl_pmem_free(&dec_bufs->desc);
+	ddl_pmem_free(&dec_bufs->context);
+	memset(dec_bufs, 0, sizeof(struct ddl_dec_buffers));
+}
+
+void ddl_free_enc_hw_buffers(struct ddl_client_context *ddl)
+{
+	struct ddl_enc_buffers *enc_bufs =
+		&ddl->codec_data.encoder.hw_bufs;
+	u32 i;
+
+	for (i = 0; i < enc_bufs->dpb_count; i++) {
+		ddl_pmem_free(&enc_bufs->dpb_y[i]);
+		ddl_pmem_free(&enc_bufs->dpb_c[i]);
+	}
+	ddl_pmem_free(&enc_bufs->mv);
+	ddl_pmem_free(&enc_bufs->col_zero);
+	ddl_pmem_free(&enc_bufs->md);
+	ddl_pmem_free(&enc_bufs->pred);
+	ddl_pmem_free(&enc_bufs->nbor_info);
+	ddl_pmem_free(&enc_bufs->acdc_coef);
+	ddl_pmem_free(&enc_bufs->context);
+	memset(enc_bufs, 0, sizeof(struct ddl_enc_buffers));
+}
+
+u32 ddl_get_input_frame_from_pool(struct ddl_client_context *ddl,
+	u8 *input_buffer_address)
+{
+	u32 vcd_status = VCD_S_SUCCESS, i, found = false;
+
+	for (i = 0; i < DDL_MAX_NUM_IN_INPUTFRAME_POOL && !found; i++) {
+		if (input_buffer_address ==
+			ddl->input_frame_pool[i].vcd_frm.physical) {
+			found = true;
+			ddl->input_frame = ddl->input_frame_pool[i];
+			memset(&ddl->input_frame_pool[i], 0,
+				sizeof(struct ddl_frame_data_tag));
+		}
+	}
+	if (!found)
+		vcd_status = VCD_ERR_FAIL;
+
+	return vcd_status;
+}
+
+u32 ddl_insert_input_frame_to_pool(struct ddl_client_context *ddl,
+	struct ddl_frame_data_tag *ddl_input_frame)
+{
+	u32 vcd_status = VCD_S_SUCCESS, i, found = false;
+
+	for (i = 0; i < DDL_MAX_NUM_IN_INPUTFRAME_POOL && !found; i++) {
+		if (!ddl->input_frame_pool[i].vcd_frm.physical) {
+			found = true;
+			ddl->input_frame_pool[i] = *ddl_input_frame;
+		}
+	}
+	if (!found)
+		vcd_status = VCD_ERR_FAIL;
+
+	return vcd_status;
+}
+
+void ddl_calc_dec_hw_buffers_size(enum vcd_codec codec, u32 width,
+	u32 height, u32 dpb, struct ddl_dec_buffer_size *buf_size)
+{
+	u32 sz_dpb0 = 0, sz_dpb1 = 0, sz_mv = 0;
+	u32 sz_luma = 0, sz_chroma = 0, sz_nb_dcac = 0, sz_upnb_mv = 0;
+	u32 sz_sub_anchor_mv = 0, sz_overlap_xform = 0, sz_bit_plane3 = 0;
+	u32 sz_bit_plane2 = 0, sz_bit_plane1 = 0, sz_stx_parser = 0;
+	u32 sz_desc, sz_cpb, sz_context, sz_vert_nb_mv = 0, sz_nb_ip = 0;
+
+	if (codec == VCD_CODEC_H264) {
+		sz_mv = ddl_get_yuv_buf_size(width,
+			height>>2, DDL_YUV_BUF_TYPE_TILE);
+		sz_nb_ip = DDL_KILO_BYTE(32);
+		sz_vert_nb_mv = DDL_KILO_BYTE(16);
+	} else {
+		if ((codec == VCD_CODEC_MPEG4) ||
+			(codec == VCD_CODEC_DIVX_3) ||
+			(codec == VCD_CODEC_DIVX_4) ||
+			(codec == VCD_CODEC_DIVX_5) ||
+			(codec == VCD_CODEC_DIVX_6) ||
+			(codec == VCD_CODEC_XVID) ||
+			(codec == VCD_CODEC_H263)) {
+			sz_nb_dcac = DDL_KILO_BYTE(16);
+			sz_upnb_mv = DDL_KILO_BYTE(68);
+			sz_sub_anchor_mv = DDL_KILO_BYTE(136);
+			sz_overlap_xform = DDL_KILO_BYTE(32);
+			if (codec != VCD_CODEC_H263)
+				sz_stx_parser = DDL_KILO_BYTE(68);
+		} else if ((codec == VCD_CODEC_VC1) ||
+			(codec == VCD_CODEC_VC1_RCV)) {
+			sz_nb_dcac = DDL_KILO_BYTE(16);
+			sz_upnb_mv = DDL_KILO_BYTE(68);
+			sz_sub_anchor_mv = DDL_KILO_BYTE(136);
+			sz_overlap_xform = DDL_KILO_BYTE(32);
+			sz_bit_plane3 = DDL_KILO_BYTE(2);
+			sz_bit_plane2 = DDL_KILO_BYTE(2);
+			sz_bit_plane1 = DDL_KILO_BYTE(2);
+		}
+	}
+	sz_desc = DDL_KILO_BYTE(128);
+	sz_cpb = VCD_DEC_CPB_SIZE;
+	if (codec == VCD_CODEC_H264)
+		sz_context = DDL_FW_H264DEC_CONTEXT_SPACE_SIZE;
+	else
+		sz_context = DDL_FW_OTHER_CONTEXT_SPACE_SIZE;
+	if (buf_size) {
+		buf_size->sz_dpb0           = sz_dpb0;
+		buf_size->sz_dpb1           = sz_dpb1;
+		buf_size->sz_mv             = sz_mv;
+		buf_size->sz_vert_nb_mv     = sz_vert_nb_mv;
+		buf_size->sz_nb_ip          = sz_nb_ip;
+		buf_size->sz_luma           = sz_luma;
+		buf_size->sz_chroma         = sz_chroma;
+		buf_size->sz_nb_dcac        = sz_nb_dcac;
+		buf_size->sz_upnb_mv        = sz_upnb_mv;
+		buf_size->sz_sub_anchor_mv  = sz_sub_anchor_mv;
+		buf_size->sz_overlap_xform  = sz_overlap_xform;
+		buf_size->sz_bit_plane3     = sz_bit_plane3;
+		buf_size->sz_bit_plane2     = sz_bit_plane2;
+		buf_size->sz_bit_plane1     = sz_bit_plane1;
+		buf_size->sz_stx_parser     = sz_stx_parser;
+		buf_size->sz_desc           = sz_desc;
+		buf_size->sz_cpb            = sz_cpb;
+		buf_size->sz_context        = sz_context;
+	}
+}
+
+u32 ddl_allocate_dec_hw_buffers(struct ddl_client_context *ddl)
+{
+	struct ddl_dec_buffers *dec_bufs;
+	struct ddl_dec_buffer_size buf_size;
+	u32 status = VCD_S_SUCCESS, dpb = 0;
+	u32 width = 0, height = 0;
+	u8 *ptr;
+	struct ddl_context *ddl_context = ddl->ddl_context;
+
+	dec_bufs = &ddl->codec_data.decoder.hw_bufs;
+	ddl_calc_dec_hw_buffers_size(ddl->codec_data.decoder.
+		codec.codec, width, height, dpb, &buf_size);
+	if (buf_size.sz_context > 0) {
+		dec_bufs->context.mem_type = DDL_MM_MEM;
+		ptr = ddl_pmem_alloc(&dec_bufs->context, buf_size.sz_context,
+			DDL_KILO_BYTE(2));
+		if (!ptr)
+			goto fail_free_exit;
+		else
+			msm_ion_do_cache_op(ddl_context->video_ion_client,
+					dec_bufs->context.alloc_handle,
+					dec_bufs->context.virtual_base_addr,
+					dec_bufs->context.buffer_size,
+					ION_IOC_CLEAN_INV_CACHES);
+	}
+	if (buf_size.sz_nb_ip > 0) {
+		dec_bufs->h264_nb_ip.mem_type = DDL_MM_MEM;
+		ptr = ddl_pmem_alloc(&dec_bufs->h264_nb_ip, buf_size.sz_nb_ip,
+			DDL_KILO_BYTE(2));
+		if (!ptr)
+			goto fail_free_exit;
+	}
+	if (buf_size.sz_vert_nb_mv > 0) {
+		dec_bufs->h264_vert_nb_mv.mem_type = DDL_MM_MEM;
+		ptr = ddl_pmem_alloc(&dec_bufs->h264_vert_nb_mv,
+			buf_size.sz_vert_nb_mv, DDL_KILO_BYTE(2));
+		if (!ptr)
+			goto fail_free_exit;
+	}
+	if (buf_size.sz_nb_dcac > 0) {
+		dec_bufs->nb_dcac.mem_type = DDL_MM_MEM;
+		ptr = ddl_pmem_alloc(&dec_bufs->nb_dcac, buf_size.sz_nb_dcac,
+			DDL_KILO_BYTE(2));
+		if (!ptr)
+			goto fail_free_exit;
+	}
+	if (buf_size.sz_upnb_mv > 0) {
+		dec_bufs->upnb_mv.mem_type = DDL_MM_MEM;
+		ptr = ddl_pmem_alloc(&dec_bufs->upnb_mv, buf_size.sz_upnb_mv,
+			DDL_KILO_BYTE(2));
+		if (!ptr)
+			goto fail_free_exit;
+	}
+	if (buf_size.sz_sub_anchor_mv > 0) {
+		dec_bufs->sub_anchor_mv.mem_type = DDL_MM_MEM;
+		ptr = ddl_pmem_alloc(&dec_bufs->sub_anchor_mv,
+			buf_size.sz_sub_anchor_mv, DDL_KILO_BYTE(2));
+		if (!ptr)
+			goto fail_free_exit;
+	}
+	if (buf_size.sz_overlap_xform > 0) {
+		dec_bufs->overlay_xform.mem_type = DDL_MM_MEM;
+		ptr = ddl_pmem_alloc(&dec_bufs->overlay_xform,
+			buf_size.sz_overlap_xform, DDL_KILO_BYTE(2));
+		if (!ptr)
+			goto fail_free_exit;
+	}
+	if (buf_size.sz_bit_plane3 > 0) {
+		dec_bufs->bit_plane3.mem_type = DDL_MM_MEM;
+		ptr = ddl_pmem_alloc(&dec_bufs->bit_plane3,
+			buf_size.sz_bit_plane3, DDL_KILO_BYTE(2));
+		if (!ptr)
+			goto fail_free_exit;
+	}
+	if (buf_size.sz_bit_plane2 > 0) {
+		dec_bufs->bit_plane2.mem_type = DDL_MM_MEM;
+		ptr = ddl_pmem_alloc(&dec_bufs->bit_plane2,
+			buf_size.sz_bit_plane2, DDL_KILO_BYTE(2));
+		if (!ptr)
+			goto fail_free_exit;
+	}
+	if (buf_size.sz_bit_plane1 > 0) {
+		dec_bufs->bit_plane1.mem_type = DDL_MM_MEM;
+		ptr = ddl_pmem_alloc(&dec_bufs->bit_plane1,
+			buf_size.sz_bit_plane1, DDL_KILO_BYTE(2));
+		if (!ptr)
+			goto fail_free_exit;
+	}
+	if (buf_size.sz_stx_parser > 0) {
+		dec_bufs->stx_parser.mem_type = DDL_MM_MEM;
+		ptr = ddl_pmem_alloc(&dec_bufs->stx_parser,
+			buf_size.sz_stx_parser, DDL_KILO_BYTE(2));
+		if (!ptr)
+			goto fail_free_exit;
+	}
+	if (buf_size.sz_desc > 0) {
+		dec_bufs->desc.mem_type = DDL_MM_MEM;
+		ptr = ddl_pmem_alloc(&dec_bufs->desc, buf_size.sz_desc,
+			DDL_KILO_BYTE(2));
+		if (!ptr)
+			goto fail_free_exit;
+		else {
+			if (!res_trk_check_for_sec_session()) {
+				memset(dec_bufs->desc.align_virtual_addr,
+					   0, buf_size.sz_desc);
+				msm_ion_do_cache_op(
+					ddl_context->video_ion_client,
+					dec_bufs->desc.alloc_handle,
+					dec_bufs->desc.virtual_base_addr,
+					dec_bufs->desc.buffer_size,
+					ION_IOC_CLEAN_INV_CACHES);
+			}
+		}
+	}
+	return status;
+fail_free_exit:
+	status = VCD_ERR_ALLOC_FAIL;
+	ddl_free_dec_hw_buffers(ddl);
+	return status;
+}
+
+u32 ddl_calc_enc_hw_buffers_size(enum vcd_codec codec, u32 width,
+	u32 height, enum vcd_yuv_buffer_format input_format,
+	struct ddl_client_context *ddl,
+	struct ddl_enc_buffer_size *buf_size)
+{
+	u32 status = VCD_S_SUCCESS, mb_x, mb_y;
+	u32 sz_cur_y, sz_cur_c, sz_dpb_y, sz_dpb_c, sz_strm = 0, sz_mv;
+	u32 sz_md = 0, sz_pred = 0, sz_nbor_info = 0 , sz_acdc_coef = 0;
+	u32 sz_mb_info = 0, sz_context, sz_col_zero = 0;
+
+	mb_x = (width + 15) / 16;
+	mb_y = (height + 15) / 16;
+	sz_dpb_y = ddl_get_yuv_buf_size(width,
+		height, DDL_YUV_BUF_TYPE_TILE);
+	sz_dpb_c = ddl_get_yuv_buf_size(width, height>>1,
+		DDL_YUV_BUF_TYPE_TILE);
+	if (input_format ==
+		VCD_BUFFER_FORMAT_NV12_16M2KA) {
+		sz_cur_y = ddl_get_yuv_buf_size(width, height,
+			DDL_YUV_BUF_TYPE_LINEAR);
+		sz_cur_c = ddl_get_yuv_buf_size(width, height>>1,
+			DDL_YUV_BUF_TYPE_LINEAR);
+	} else if (VCD_BUFFER_FORMAT_TILE_4x2 == input_format) {
+		sz_cur_y = sz_dpb_y;
+		sz_cur_c = sz_dpb_c;
+	} else
+		status = VCD_ERR_NOT_SUPPORTED;
+	sz_context = DDL_FW_OTHER_CONTEXT_SPACE_SIZE;
+	if (!status) {
+		sz_strm = DDL_ALIGN(ddl_get_yuv_buf_size(width, height,
+			DDL_YUV_BUF_TYPE_LINEAR) + ddl_get_yuv_buf_size(width,
+			height/2, DDL_YUV_BUF_TYPE_LINEAR), DDL_KILO_BYTE(4));
+		sz_mv = DDL_ALIGN(2 * mb_x * mb_y * 8, DDL_KILO_BYTE(2));
+		if ((codec == VCD_CODEC_MPEG4) ||
+			(codec == VCD_CODEC_H264)) {
+			sz_col_zero = DDL_ALIGN(((mb_x * mb_y + 7) / 8) *
+					8, DDL_KILO_BYTE(2));
+		}
+		if ((codec == VCD_CODEC_MPEG4) ||
+			(codec == VCD_CODEC_H263)) {
+			sz_acdc_coef = DDL_ALIGN((width / 2) * 8,
+						DDL_KILO_BYTE(2));
+		} else if (codec == VCD_CODEC_H264) {
+			sz_md = DDL_ALIGN(mb_x * 48, DDL_KILO_BYTE(2));
+			sz_pred = DDL_ALIGN(2 * 8 * 1024, DDL_KILO_BYTE(2));
+			sz_context = DDL_FW_H264ENC_CONTEXT_SPACE_SIZE;
+			if (ddl) {
+				if (ddl->codec_data.encoder.
+					entropy_control.entropy_sel ==
+					VCD_ENTROPY_SEL_CAVLC)
+					sz_nbor_info = DDL_ALIGN(8 * 8 * mb_x,
+						DDL_KILO_BYTE(2));
+				else if (ddl->codec_data.encoder.
+					entropy_control.entropy_sel ==
+					VCD_ENTROPY_SEL_CABAC)
+					sz_nbor_info = DDL_ALIGN(8 * 24 *
+						mb_x, DDL_KILO_BYTE(2));
+				if ((ddl->codec_data.encoder.
+					mb_info_enable) &&
+					(codec == VCD_CODEC_H264)) {
+					sz_mb_info = DDL_ALIGN(mb_x * mb_y *
+						6 * 8, DDL_KILO_BYTE(2));
+				}
+			}
+		} else {
+			sz_nbor_info = DDL_ALIGN(8 * 24 * mb_x,
+						DDL_KILO_BYTE(2));
+			sz_mb_info = DDL_ALIGN(mb_x * mb_y * 6 * 8,
+					DDL_KILO_BYTE(2));
+		}
+		if (buf_size) {
+			buf_size->sz_cur_y      = sz_cur_y;
+			buf_size->sz_cur_c      = sz_cur_c;
+			buf_size->sz_dpb_y      = sz_dpb_y;
+			buf_size->sz_dpb_c      = sz_dpb_c;
+			buf_size->sz_strm       = sz_strm;
+			buf_size->sz_mv         = sz_mv;
+			buf_size->sz_col_zero   = sz_col_zero;
+			buf_size->sz_md         = sz_md;
+			buf_size->sz_pred       = sz_pred;
+			buf_size->sz_nbor_info  = sz_nbor_info;
+			buf_size->sz_acdc_coef  = sz_acdc_coef;
+			buf_size->sz_mb_info    = sz_mb_info;
+			buf_size->sz_context    = sz_context;
+		}
+	}
+	return status;
+}
+
+u32 ddl_allocate_enc_hw_buffers(struct ddl_client_context *ddl)
+{
+	struct ddl_enc_buffers *enc_bufs;
+	struct ddl_enc_buffer_size buf_size;
+	void *ptr;
+	u32 status = VCD_S_SUCCESS;
+	struct ddl_context *ddl_context = ddl->ddl_context;
+
+	enc_bufs = &ddl->codec_data.encoder.hw_bufs;
+	enc_bufs->dpb_count = DDL_ENC_MIN_DPB_BUFFERS;
+
+	if ((ddl->codec_data.encoder.i_period.b_frames >
+		DDL_MIN_NUM_OF_B_FRAME) ||
+		(ddl->codec_data.encoder.num_references_for_p_frame
+		> DDL_MIN_NUM_REF_FOR_P_FRAME))
+		enc_bufs->dpb_count = DDL_ENC_MAX_DPB_BUFFERS;
+		DDL_MSG_HIGH("Encoder num DPB buffers allocated = %d",
+			enc_bufs->dpb_count);
+
+	status = ddl_calc_enc_hw_buffers_size(
+		ddl->codec_data.encoder.codec.codec,
+		ddl->codec_data.encoder.frame_size.width,
+		ddl->codec_data.encoder.frame_size.height,
+		ddl->codec_data.encoder.buf_format.buffer_format,
+		ddl, &buf_size);
+	buf_size.sz_strm = ddl->codec_data.encoder.
+		client_output_buf_req.sz;
+	if (!status) {
+		enc_bufs->sz_dpb_y = buf_size.sz_dpb_y;
+		enc_bufs->sz_dpb_c = buf_size.sz_dpb_c;
+		if (buf_size.sz_mv > 0) {
+			enc_bufs->mv.mem_type = DDL_MM_MEM;
+			ptr = ddl_pmem_alloc(&enc_bufs->mv, buf_size.sz_mv,
+				DDL_KILO_BYTE(2));
+			if (!ptr)
+				goto fail_enc_free_exit;
+		}
+		if (buf_size.sz_col_zero > 0) {
+			enc_bufs->col_zero.mem_type = DDL_MM_MEM;
+			ptr = ddl_pmem_alloc(&enc_bufs->col_zero,
+				buf_size.sz_col_zero, DDL_KILO_BYTE(2));
+			if (!ptr)
+				goto fail_enc_free_exit;
+		}
+		if (buf_size.sz_md > 0) {
+			enc_bufs->md.mem_type = DDL_MM_MEM;
+			ptr = ddl_pmem_alloc(&enc_bufs->md, buf_size.sz_md,
+				DDL_KILO_BYTE(2));
+			if (!ptr)
+				goto fail_enc_free_exit;
+		}
+		if (buf_size.sz_pred > 0) {
+			enc_bufs->pred.mem_type = DDL_MM_MEM;
+			ptr = ddl_pmem_alloc(&enc_bufs->pred,
+				buf_size.sz_pred, DDL_KILO_BYTE(2));
+			if (!ptr)
+				goto fail_enc_free_exit;
+		}
+		if (buf_size.sz_nbor_info > 0) {
+			enc_bufs->nbor_info.mem_type = DDL_MM_MEM;
+			ptr = ddl_pmem_alloc(&enc_bufs->nbor_info,
+				buf_size.sz_nbor_info, DDL_KILO_BYTE(2));
+			if (!ptr)
+				goto fail_enc_free_exit;
+		}
+		if (buf_size.sz_acdc_coef > 0) {
+			enc_bufs->acdc_coef.mem_type = DDL_MM_MEM;
+			ptr = ddl_pmem_alloc(&enc_bufs->acdc_coef,
+				buf_size.sz_acdc_coef, DDL_KILO_BYTE(2));
+			if (!ptr)
+				goto fail_enc_free_exit;
+		}
+		if (buf_size.sz_mb_info > 0) {
+			enc_bufs->mb_info.mem_type = DDL_MM_MEM;
+			ptr = ddl_pmem_alloc(&enc_bufs->mb_info,
+				buf_size.sz_mb_info, DDL_KILO_BYTE(2));
+			if (!ptr)
+				goto fail_enc_free_exit;
+		}
+		if (buf_size.sz_context > 0) {
+			enc_bufs->context.mem_type = DDL_MM_MEM;
+			ptr = ddl_pmem_alloc(&enc_bufs->context,
+				buf_size.sz_context, DDL_KILO_BYTE(2));
+			if (!ptr)
+				goto fail_enc_free_exit;
+			else
+				msm_ion_do_cache_op(
+					ddl_context->video_ion_client,
+					enc_bufs->context.alloc_handle,
+					enc_bufs->context.virtual_base_addr,
+					enc_bufs->context.buffer_size,
+					ION_IOC_CLEAN_INV_CACHES);
+		}
+	}
+	return status;
+fail_enc_free_exit:
+	status = VCD_ERR_ALLOC_FAIL;
+	ddl_free_enc_hw_buffers(ddl);
+	return status;
+}
+
+void ddl_decoder_chroma_dpb_change(struct ddl_client_context *ddl)
+{
+	struct ddl_context *ddl_context = ddl->ddl_context;
+	struct ddl_decoder_data *decoder = &ddl->codec_data.decoder;
+	struct ddl_frame_data_tag *frame =
+			&(decoder->dp_buf.dec_pic_buffers[0]);
+	u32 luma[DDL_MAX_BUFFER_COUNT];
+	u32 chroma[DDL_MAX_BUFFER_COUNT];
+	u32 luma_size, i, dpb;
+	luma_size = decoder->dpb_buf_size.size_y;
+	dpb = decoder->dp_buf.no_of_dec_pic_buf;
+	DDL_MSG_HIGH("%s Decoder num DPB buffers = %u Luma Size = %u"
+			 __func__, dpb, luma_size);
+	if (dpb > DDL_MAX_BUFFER_COUNT)
+		dpb = DDL_MAX_BUFFER_COUNT;
+	for (i = 0; i < dpb; i++) {
+		luma[i] = DDL_OFFSET(
+			ddl_context->dram_base_a.align_physical_addr,
+			frame[i].vcd_frm.physical);
+		chroma[i] = luma[i] + luma_size;
+		DDL_MSG_LOW("%s Decoder Luma address = %x"
+			"Chroma address = %x", __func__, luma[i], chroma[i]);
+	}
+	vidc_1080p_set_decode_recon_buffers(dpb, luma, chroma);
+}
+
+u32 ddl_check_reconfig(struct ddl_client_context *ddl)
+{
+	u32 need_reconfig = true;
+	struct ddl_decoder_data *decoder = &ddl->codec_data.decoder;
+	if (decoder->cont_mode) {
+		if ((decoder->actual_output_buf_req.sz <=
+			 decoder->client_output_buf_req.sz) &&
+			(decoder->actual_output_buf_req.actual_count <=
+			 decoder->client_output_buf_req.actual_count)) {
+			need_reconfig = false;
+			if (decoder->min_dpb_num >
+				decoder->min_output_buf_req.min_count) {
+				decoder->min_output_buf_req =
+					decoder->actual_output_buf_req;
+			}
+			DDL_MSG_LOW("%s Decoder width = %u height = %u "
+				"Client width = %u height = %u\n",
+				__func__, decoder->frame_size.width,
+				 decoder->frame_size.height,
+				 decoder->client_frame_size.width,
+				 decoder->client_frame_size.height);
+		}
+	} else {
+		if ((decoder->frame_size.width ==
+			decoder->client_frame_size.width) &&
+			(decoder->frame_size.height ==
+			decoder->client_frame_size.height) &&
+			(decoder->actual_output_buf_req.sz <=
+			decoder->client_output_buf_req.sz) &&
+			(decoder->actual_output_buf_req.min_count ==
+			decoder->client_output_buf_req.min_count) &&
+			(decoder->actual_output_buf_req.actual_count ==
+			decoder->client_output_buf_req.actual_count) &&
+			(decoder->frame_size.scan_lines ==
+			decoder->client_frame_size.scan_lines) &&
+			(decoder->frame_size.stride ==
+			decoder->client_frame_size.stride) &&
+			decoder->progressive_only)
+				need_reconfig = false;
+	}
+	return need_reconfig;
+}
+
+void ddl_handle_reconfig(u32 res_change, struct ddl_client_context *ddl)
+{
+	struct ddl_decoder_data *decoder = &ddl->codec_data.decoder;
+	if ((decoder->cont_mode) &&
+		(res_change == DDL_RESL_CHANGE_DECREASED)) {
+		DDL_MSG_LOW("%s Resolution decreased, continue decoding\n",
+				 __func__);
+		vidc_sm_get_min_yc_dpb_sizes(
+					&ddl->shared_mem[ddl->command_channel],
+					&decoder->dpb_buf_size.size_y,
+					&decoder->dpb_buf_size.size_c);
+		DDL_MSG_LOW(" %s Resolution decreased, size_y = %u"
+				" size_c = %u\n",
+				__func__,
+				decoder->dpb_buf_size.size_y,
+				decoder->dpb_buf_size.size_c);
+		ddl_decoder_chroma_dpb_change(ddl);
+		vidc_sm_set_chroma_addr_change(
+				&ddl->shared_mem[ddl->command_channel],
+				true);
+		ddl_vidc_decode_frame_run(ddl);
+	} else {
+		DDL_MSG_LOW("%s Resolution change, start realloc\n",
+				 __func__);
+		decoder->reconfig_detected = true;
+		ddl->client_state = DDL_CLIENT_WAIT_FOR_EOS_DONE;
+		ddl->cmd_state = DDL_CMD_EOS;
+		vidc_1080p_frame_start_realloc(ddl->instance_id);
+	}
+}
+
+void ddl_fill_dec_desc_buffer(struct ddl_client_context *ddl)
+{
+	struct ddl_decoder_data *decoder = &ddl->codec_data.decoder;
+	struct vcd_frame_data *ip_bitstream = &(ddl->input_frame.vcd_frm);
+	struct ddl_buf_addr *dec_desc_buf = &(decoder->hw_bufs.desc);
+
+	if (ip_bitstream->desc_buf &&
+		ip_bitstream->desc_size < DDL_KILO_BYTE(128))
+		memcpy(dec_desc_buf->align_virtual_addr,
+			   ip_bitstream->desc_buf,
+			   ip_bitstream->desc_size);
+}
+
+void ddl_set_vidc_timeout(struct ddl_client_context *ddl)
+{
+	u32 vidc_time_out = 0;
+	if (ddl->codec_data.decoder.idr_only_decoding)
+		vidc_time_out = 2 * DDL_VIDC_1080P_200MHZ_TIMEOUT_VALUE;
+	DDL_MSG_HIGH("%s Video core time out value = 0x%x",
+		 __func__, vidc_time_out);
+	vidc_sm_set_video_core_timeout_value(
+		&ddl->shared_mem[ddl->command_channel], vidc_time_out);
+}
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_interrupt_handler.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_interrupt_handler.c
new file mode 100644
index 0000000..e2c0a2a
--- /dev/null
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_interrupt_handler.c
@@ -0,0 +1,1975 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+
+#include "vcd_ddl.h"
+#include "vcd_ddl_shared_mem.h"
+#include "vcd_ddl_metadata.h"
+#include "vcd_res_tracker_api.h"
+#include <linux/delay.h>
+
+static void ddl_decoder_input_done_callback(
+	struct ddl_client_context *ddl, u32 frame_transact_end);
+static u32 ddl_decoder_output_done_callback(
+	struct ddl_client_context *ddl, u32 frame_transact_end);
+static u32 ddl_get_decoded_frame(struct vcd_frame_data  *frame,
+	enum vidc_1080p_decode_frame frame_type);
+static u32 ddl_get_encoded_frame(struct vcd_frame_data *frame,
+	enum vcd_codec codec,
+	enum vidc_1080p_encode_frame frame_type);
+static void ddl_get_dec_profile_level(struct ddl_decoder_data *decoder,
+	u32 profile, u32 level);
+static void ddl_handle_enc_frame_done(struct ddl_client_context *ddl,
+	u32 eos_present);
+static void ddl_handle_slice_done_slice_batch(struct ddl_client_context *ddl);
+static void ddl_handle_enc_frame_done_slice_mode(
+		struct ddl_client_context *ddl, u32 eos_present);
+static void ddl_handle_enc_skipframe_slice_mode(
+		struct ddl_client_context *ddl, u32 eos_present);
+
+static void ddl_fw_status_done_callback(struct ddl_context *ddl_context)
+{
+	DDL_MSG_MED("ddl_fw_status_done_callback");
+	if (!DDLCOMMAND_STATE_IS(ddl_context, DDL_CMD_DMA_INIT)) {
+		DDL_MSG_ERROR("UNKWN_DMADONE");
+	} else {
+		DDL_MSG_LOW("FW_STATUS_DONE");
+		vidc_1080p_set_host2risc_cmd(VIDC_1080P_HOST2RISC_CMD_SYS_INIT,
+			ddl_context->fw_memory_size, 0, 0, 0);
+	}
+}
+
+static void ddl_sys_init_done_callback(struct ddl_context *ddl_context,
+	u32 fw_size)
+{
+	u32 vcd_status = VCD_S_SUCCESS;
+	u8 *fw_ver;
+
+	DDL_MSG_MED("ddl_sys_init_done_callback");
+	if (!DDLCOMMAND_STATE_IS(ddl_context, DDL_CMD_DMA_INIT)) {
+		DDL_MSG_ERROR("UNKNOWN_SYS_INIT_DONE");
+	} else {
+		ddl_context->cmd_state = DDL_CMD_INVALID;
+		DDL_MSG_LOW("SYS_INIT_DONE");
+		vidc_1080p_get_fw_version(&ddl_context->fw_version);
+		fw_ver = (u8 *)&ddl_context->fw_version;
+		DDL_MSG_ERROR("fw_version %x:%x:20%x",
+			fw_ver[1]&0xFF, fw_ver[0]&0xFF, fw_ver[2]&0xFF);
+		if (ddl_context->fw_memory_size >= fw_size) {
+			ddl_context->device_state = DDL_DEVICE_INITED;
+			vcd_status = VCD_S_SUCCESS;
+		} else
+			vcd_status = VCD_ERR_FAIL;
+		ddl_context->ddl_callback(VCD_EVT_RESP_DEVICE_INIT,
+			vcd_status, NULL, 0, NULL,
+			ddl_context->client_data);
+		DDL_IDLE(ddl_context);
+	}
+}
+
+static void ddl_decoder_eos_done_callback(
+	struct ddl_client_context *ddl)
+{
+	struct ddl_context *ddl_context = ddl->ddl_context;
+
+	if (!ddl->decoding) {
+		DDL_MSG_ERROR("STATE-CRITICAL-EOSDONE");
+		ddl_client_fatal_cb(ddl);
+	} else {
+		ddl->client_state = DDL_CLIENT_WAIT_FOR_FRAME;
+		DDL_MSG_LOW("EOS_DONE");
+		ddl_context->ddl_callback(VCD_EVT_RESP_EOS_DONE,
+			VCD_S_SUCCESS, NULL, 0, (u32 *)ddl,
+			ddl->client_data);
+		ddl_release_command_channel(ddl_context,
+			ddl->command_channel);
+	}
+}
+
+static u32 ddl_channel_set_callback(struct ddl_context *ddl_context,
+	u32 instance_id)
+{
+	struct ddl_client_context *ddl;
+	u32 ret = false;
+
+	DDL_MSG_MED("ddl_channel_open_callback");
+	ddl = ddl_get_current_ddl_client_for_command(ddl_context,
+			DDL_CMD_CHANNEL_SET);
+	if (ddl) {
+		ddl->cmd_state = DDL_CMD_INVALID;
+		if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_CHDONE)) {
+			DDL_MSG_ERROR("STATE-CRITICAL-CHSET");
+			ddl_release_command_channel(ddl_context,
+			ddl->command_channel);
+		} else {
+			DDL_MSG_LOW("CH_SET_DONE");
+			DDL_MSG_LOW("ddl_state_transition: %s ~~>"
+				"DDL_CLIENT_WAIT_FOR_INITCODEC",
+				ddl_get_state_string(ddl->client_state));
+			ddl->client_state = DDL_CLIENT_WAIT_FOR_INITCODEC;
+			ddl->instance_id = instance_id;
+			if (ddl->decoding) {
+				ddl_calc_core_proc_time(__func__,
+						DEC_OP_TIME, ddl);
+				if (ddl->codec_data.decoder.header_in_start)
+					ddl_vidc_decode_init_codec(ddl);
+				else {
+					ddl_context->ddl_callback(
+						VCD_EVT_RESP_START,
+						VCD_S_SUCCESS, NULL, 0,
+						(u32 *)ddl,
+						ddl->client_data);
+					ddl_release_command_channel(
+						ddl_context,
+						ddl->command_channel);
+					ret = true;
+				}
+			} else
+				ddl_vidc_encode_init_codec(ddl);
+		}
+	}
+	return ret;
+}
+
+static u32 ddl_encoder_seq_done_callback(struct ddl_context *ddl_context,
+	struct ddl_client_context *ddl)
+{
+	struct ddl_encoder_data *encoder;
+
+	DDL_MSG_HIGH("ddl_encoder_seq_done_callback");
+	if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_INITCODECDONE)) {
+		DDL_MSG_ERROR("STATE-CRITICAL-INITCODEC");
+		ddl_client_fatal_cb(ddl);
+		return true;
+	}
+	ddl_calc_core_proc_time(__func__, ENC_OP_TIME, ddl);
+	ddl->cmd_state = DDL_CMD_INVALID;
+	DDL_MSG_LOW("ddl_state_transition: %s ~~> DDL_CLIENT_WAIT_FOR_FRAME",
+	ddl_get_state_string(ddl->client_state));
+	ddl->client_state = DDL_CLIENT_WAIT_FOR_FRAME;
+	DDL_MSG_LOW("INIT_CODEC_DONE");
+	encoder = &ddl->codec_data.encoder;
+	vidc_1080p_get_encoder_sequence_header_size(
+		&encoder->seq_header_length);
+	if ((encoder->codec.codec == VCD_CODEC_H264) &&
+		(encoder->profile.profile == VCD_PROFILE_H264_BASELINE))
+		if ((encoder->seq_header.align_virtual_addr) &&
+			(encoder->seq_header_length > 6))
+			encoder->seq_header.align_virtual_addr[6] = 0xC0;
+	ddl_context->ddl_callback(VCD_EVT_RESP_START, VCD_S_SUCCESS,
+		NULL, 0, (u32 *) ddl, ddl->client_data);
+	ddl_release_command_channel(ddl_context,
+		ddl->command_channel);
+	return true;
+}
+
+static void parse_hdr_size_data(struct ddl_client_context *ddl,
+	struct vidc_1080p_seq_hdr_info *seq_hdr_info)
+{
+	u32 progressive;
+	struct ddl_decoder_data *decoder = &ddl->codec_data.decoder;
+	if (decoder->output_order == VCD_DEC_ORDER_DISPLAY) {
+		decoder->frame_size.width = seq_hdr_info->img_size_x;
+		decoder->frame_size.height = seq_hdr_info->img_size_y;
+		progressive = seq_hdr_info->disp_progressive;
+	} else {
+		vidc_sm_get_dec_order_resl(
+			&ddl->shared_mem[ddl->command_channel],
+			&decoder->frame_size.width,
+			&decoder->frame_size.height);
+		progressive = seq_hdr_info->dec_progressive;
+	}
+	decoder->min_dpb_num = seq_hdr_info->min_num_dpb;
+	vidc_sm_get_min_yc_dpb_sizes(
+		&ddl->shared_mem[ddl->command_channel],
+		&seq_hdr_info->min_luma_dpb_size,
+		&seq_hdr_info->min_chroma_dpb_size);
+	decoder->y_cb_cr_size = seq_hdr_info->min_luma_dpb_size +
+		seq_hdr_info->min_chroma_dpb_size;
+	decoder->dpb_buf_size.size_yuv = decoder->y_cb_cr_size;
+	decoder->dpb_buf_size.size_y =
+		seq_hdr_info->min_luma_dpb_size;
+	decoder->dpb_buf_size.size_c =
+		seq_hdr_info->min_chroma_dpb_size;
+	decoder->progressive_only = progressive ? false : true;
+}
+
+static void parse_hdr_crop_data(struct ddl_client_context *ddl,
+	struct vidc_1080p_seq_hdr_info *seq_hdr_info)
+{
+	struct ddl_decoder_data *decoder = &ddl->codec_data.decoder;
+	u32 crop_exists = (decoder->output_order == VCD_DEC_ORDER_DISPLAY) ?
+		seq_hdr_info->disp_crop_exists : seq_hdr_info->dec_crop_exists;
+	if (crop_exists) {
+		if (decoder->output_order ==
+			VCD_DEC_ORDER_DISPLAY)
+			vidc_sm_get_crop_info(
+				&ddl->shared_mem[ddl->command_channel],
+				&seq_hdr_info->crop_left_offset,
+				&seq_hdr_info->crop_right_offset,
+				&seq_hdr_info->crop_top_offset,
+				&seq_hdr_info->crop_bottom_offset);
+		else
+			vidc_sm_get_dec_order_crop_info(
+				&ddl->shared_mem[ddl->command_channel],
+				&seq_hdr_info->crop_left_offset,
+				&seq_hdr_info->crop_right_offset,
+				&seq_hdr_info->crop_top_offset,
+				&seq_hdr_info->crop_bottom_offset);
+		decoder->frame_size.width -=
+			seq_hdr_info->crop_right_offset +
+			seq_hdr_info->crop_left_offset;
+		decoder->frame_size.height -=
+			seq_hdr_info->crop_top_offset +
+			seq_hdr_info->crop_bottom_offset;
+	}
+}
+
+static u32 ddl_decoder_seq_done_callback(struct ddl_context *ddl_context,
+	struct ddl_client_context *ddl)
+{
+	struct ddl_decoder_data *decoder = &ddl->codec_data.decoder;
+	struct vidc_1080p_seq_hdr_info seq_hdr_info;
+	u32 process_further = true;
+	struct ddl_profile_info_type disp_profile_info;
+
+	DDL_MSG_MED("ddl_decoder_seq_done_callback");
+	if (!ddl->decoding ||
+		!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_INITCODECDONE)) {
+		DDL_MSG_ERROR("STATE-CRITICAL-HDDONE");
+		ddl_client_fatal_cb(ddl);
+	} else {
+		ddl_calc_core_proc_time(__func__, DEC_OP_TIME, ddl);
+		ddl->cmd_state = DDL_CMD_INVALID;
+		DDL_MSG_LOW("ddl_state_transition: %s ~~>"
+			"DDL_CLIENT_WAIT_FOR_DPB",
+			ddl_get_state_string(ddl->client_state));
+		ddl->client_state = DDL_CLIENT_WAIT_FOR_DPB;
+		DDL_MSG_LOW("HEADER_DONE");
+		vidc_1080p_get_decode_seq_start_result(&seq_hdr_info);
+		parse_hdr_size_data(ddl, &seq_hdr_info);
+		if (res_trk_get_disable_fullhd() &&
+			(seq_hdr_info.img_size_x * seq_hdr_info.img_size_y >
+				1280 * 720)) {
+			DDL_MSG_ERROR("FATAL:Resolution greater than 720P HD");
+			ddl_client_fatal_cb(ddl);
+			return process_further;
+		}
+		if (!seq_hdr_info.img_size_x || !seq_hdr_info.img_size_y) {
+			DDL_MSG_ERROR("FATAL:ZeroImageSize");
+			ddl_client_fatal_cb(ddl);
+			return process_further;
+		}
+		vidc_sm_get_profile_info(&ddl->shared_mem
+			[ddl->command_channel], &disp_profile_info);
+		disp_profile_info.pic_profile = seq_hdr_info.profile;
+		disp_profile_info.pic_level = seq_hdr_info.level;
+		ddl_get_dec_profile_level(decoder, seq_hdr_info.profile,
+			seq_hdr_info.level);
+		switch (decoder->codec.codec) {
+		case VCD_CODEC_H264:
+			if (decoder->profile.profile == VCD_PROFILE_H264_HIGH ||
+				decoder->profile.profile ==
+				VCD_PROFILE_UNKNOWN) {
+				if ((disp_profile_info.chroma_format_idc >
+					VIDC_1080P_IDCFORMAT_420) ||
+					(disp_profile_info.bit_depth_luma_minus8
+					 || disp_profile_info.
+					bit_depth_chroma_minus8)) {
+					DDL_MSG_ERROR("Unsupported H.264 "
+						"feature: IDC format : %d, Bitdepth: %d",
+						disp_profile_info.
+						chroma_format_idc,
+						(disp_profile_info.
+						 bit_depth_luma_minus8
+						 ||	disp_profile_info.
+					bit_depth_chroma_minus8));
+					ddl_client_fatal_cb(ddl);
+					return process_further;
+				}
+			}
+			break;
+		case VCD_CODEC_MPEG4:
+		case VCD_CODEC_DIVX_4:
+		case VCD_CODEC_DIVX_5:
+		case VCD_CODEC_DIVX_6:
+		case VCD_CODEC_XVID:
+			if (seq_hdr_info.data_partition)
+				if ((seq_hdr_info.img_size_x *
+				seq_hdr_info.img_size_y) > (720 * 576)) {
+					DDL_MSG_ERROR("Unsupported DP clip");
+					ddl_client_fatal_cb(ddl);
+					return process_further;
+				}
+			break;
+		default:
+			break;
+		}
+		ddl_calculate_stride(&decoder->frame_size,
+			!decoder->progressive_only);
+		decoder->frame_size.scan_lines =
+		DDL_ALIGN(decoder->frame_size.height, DDL_TILE_ALIGN_HEIGHT);
+		decoder->frame_size.stride =
+		DDL_ALIGN(decoder->frame_size.width, DDL_TILE_ALIGN_WIDTH);
+		parse_hdr_crop_data(ddl, &seq_hdr_info);
+		if (decoder->codec.codec == VCD_CODEC_H264 &&
+			seq_hdr_info.level > VIDC_1080P_H264_LEVEL4) {
+			DDL_MSG_ERROR("WARNING: H264MaxLevelExceeded : %d",
+				seq_hdr_info.level);
+		}
+		ddl_set_default_decoder_buffer_req(decoder, false);
+		if (decoder->header_in_start) {
+			if (!(decoder->cont_mode) ||
+				(decoder->min_dpb_num >
+				 decoder->client_output_buf_req.min_count) ||
+				(decoder->actual_output_buf_req.sz >
+				 decoder->client_output_buf_req.sz)) {
+				decoder->client_frame_size =
+					 decoder->frame_size;
+				decoder->client_output_buf_req =
+					decoder->actual_output_buf_req;
+				decoder->client_input_buf_req =
+					decoder->actual_input_buf_req;
+			}
+			ddl_context->ddl_callback(VCD_EVT_RESP_START,
+				VCD_S_SUCCESS, NULL, 0, (u32 *) ddl,
+				ddl->client_data);
+			ddl_release_command_channel(ddl_context,
+				ddl->command_channel);
+		} else {
+			u32 seq_hdr_only_frame = false;
+			u32 need_reconfig = false;
+			struct vcd_frame_data *input_vcd_frm =
+				&ddl->input_frame.vcd_frm;
+			need_reconfig = ddl_check_reconfig(ddl);
+			DDL_MSG_HIGH("%s : need_reconfig = %u\n", __func__,
+				 need_reconfig);
+			if (input_vcd_frm->flags &
+				  VCD_FRAME_FLAG_EOS) {
+				need_reconfig = false;
+			}
+			if (((input_vcd_frm->flags &
+				VCD_FRAME_FLAG_CODECCONFIG) &&
+				(!(input_vcd_frm->flags &
+				VCD_FRAME_FLAG_SYNCFRAME))) ||
+				input_vcd_frm->data_len <=
+				seq_hdr_info.dec_frm_size) {
+				seq_hdr_only_frame = true;
+				input_vcd_frm->offset +=
+					seq_hdr_info.dec_frm_size;
+				input_vcd_frm->data_len = 0;
+				input_vcd_frm->flags |=
+					VCD_FRAME_FLAG_CODECCONFIG;
+				ddl->input_frame.frm_trans_end =
+					!need_reconfig;
+				ddl_context->ddl_callback(
+					VCD_EVT_RESP_INPUT_DONE,
+					VCD_S_SUCCESS, &ddl->input_frame,
+					sizeof(struct ddl_frame_data_tag),
+					(u32 *) ddl, ddl->client_data);
+			} else {
+				if (decoder->codec.codec ==
+					VCD_CODEC_VC1_RCV) {
+					vidc_sm_set_start_byte_number(
+						&ddl->shared_mem
+						[ddl->command_channel],
+						seq_hdr_info.dec_frm_size);
+				}
+			}
+			if (need_reconfig) {
+				struct ddl_frame_data_tag *payload =
+					&ddl->input_frame;
+				u32 payload_size =
+					sizeof(struct ddl_frame_data_tag);
+				decoder->client_frame_size =
+					decoder->frame_size;
+				decoder->client_output_buf_req =
+					decoder->actual_output_buf_req;
+				decoder->client_input_buf_req =
+					decoder->actual_input_buf_req;
+				if (seq_hdr_only_frame) {
+					payload = NULL;
+					payload_size = 0;
+				}
+				DDL_MSG_HIGH("%s : sending port reconfig\n",
+					 __func__);
+				ddl_context->ddl_callback(
+					VCD_EVT_IND_OUTPUT_RECONFIG,
+					VCD_S_SUCCESS, payload,
+					payload_size, (u32 *) ddl,
+					ddl->client_data);
+			}
+			if (!need_reconfig && !seq_hdr_only_frame) {
+				if (!ddl_vidc_decode_set_buffers(ddl))
+					process_further = false;
+				else {
+					DDL_MSG_ERROR("ddl_vidc_decode_set_"
+						"buffers failed");
+					ddl_client_fatal_cb(ddl);
+				}
+			} else
+				ddl_release_command_channel(ddl_context,
+					ddl->command_channel);
+		}
+	}
+	return process_further;
+}
+
+static u32 ddl_sequence_done_callback(struct ddl_context *ddl_context)
+{
+	struct ddl_client_context *ddl;
+	u32 channel_inst_id, ret;
+
+	vidc_1080p_get_returned_channel_inst_id(&channel_inst_id);
+	vidc_1080p_clear_returned_channel_inst_id();
+	ddl = ddl_get_current_ddl_client_for_channel_id(ddl_context,
+			ddl_context->response_cmd_ch_id);
+	if (!ddl) {
+		DDL_MSG_ERROR("UNKWN_SEQ_DONE");
+		ret = true;
+	} else {
+		if (ddl->decoding)
+			ret = ddl_decoder_seq_done_callback(ddl_context,
+					ddl);
+		else
+			ret = ddl_encoder_seq_done_callback(ddl_context,
+					ddl);
+	}
+	return ret;
+}
+
+static u32 ddl_dpb_buffers_set_done_callback(
+	struct ddl_context *ddl_context)
+{
+	struct ddl_client_context *ddl;
+	u32 channel_inst_id, ret_status = true;
+
+	DDL_MSG_MED("ddl_dpb_buffers_set_done_callback");
+	vidc_1080p_get_returned_channel_inst_id(&channel_inst_id);
+	vidc_1080p_clear_returned_channel_inst_id();
+	ddl = ddl_get_current_ddl_client_for_command(ddl_context,
+			DDL_CMD_DECODE_SET_DPB);
+	if (ddl) {
+		ddl->cmd_state = DDL_CMD_INVALID;
+		if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_DPBDONE)) {
+			DDL_MSG_ERROR("STATE-CRITICAL-DPBDONE");
+			ddl_client_fatal_cb(ddl);
+		} else {
+			DDL_MSG_LOW("INTR_DPBDONE");
+			DDL_MSG_LOW("ddl_state_transition: %s ~~>"
+				"DDL_CLIENT_WAIT_FOR_FRAME",
+				ddl_get_state_string(ddl->client_state));
+			ddl_calc_core_proc_time(__func__, DEC_OP_TIME, ddl);
+			ddl_reset_core_time_variables(DEC_OP_TIME);
+			ddl->client_state = DDL_CLIENT_WAIT_FOR_FRAME;
+			ddl_vidc_decode_frame_run(ddl);
+			ret_status = false;
+		}
+	}
+	return ret_status;
+}
+
+static void ddl_encoder_frame_run_callback(
+	struct ddl_client_context *ddl)
+{
+	struct ddl_context *ddl_context = ddl->ddl_context;
+	struct ddl_encoder_data *encoder =
+		&(ddl->codec_data.encoder);
+	struct vcd_frame_data *output_frame =
+		&(ddl->output_frame.vcd_frm);
+	u32 eos_present = false;
+
+	DDL_MSG_MED("ddl_encoder_frame_run_callback\n");
+	if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME_DONE) &&
+		!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_EOS_DONE)) {
+		DDL_MSG_ERROR("STATE-CRITICAL-ENCFRMRUN");
+		ddl_client_fatal_cb(ddl);
+	} else {
+		ddl_calc_core_proc_time(__func__, ENC_OP_TIME, ddl);
+		DDL_MSG_LOW("ENC_FRM_RUN_DONE");
+		ddl->cmd_state = DDL_CMD_INVALID;
+		vidc_1080p_get_encode_frame_info(&encoder->enc_frame_info);
+
+		if (encoder->meta_data_enable_flag)
+			vidc_sm_get_metadata_status(&ddl->shared_mem
+				[ddl->command_channel],
+				&encoder->enc_frame_info.meta_data_exists);
+
+		if (VCD_FRAME_FLAG_EOS & ddl->input_frame.vcd_frm.flags) {
+				DDL_MSG_LOW("%s EOS detected\n", __func__);
+				eos_present = true;
+		}
+
+		if (encoder->enc_frame_info.enc_frame_size ||
+			(encoder->enc_frame_info.enc_frame ==
+			VIDC_1080P_ENCODE_FRAMETYPE_SKIPPED) ||
+			DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_EOS_DONE)) {
+			if (encoder->slice_delivery_info.enable) {
+				if (encoder->enc_frame_info.enc_frame ==
+					VIDC_1080P_ENCODE_FRAMETYPE_SKIPPED)
+					ddl_handle_enc_skipframe_slice_mode(
+							ddl, eos_present);
+				else
+					ddl_handle_enc_frame_done_slice_mode(
+							ddl, eos_present);
+			} else {
+				ddl_handle_enc_frame_done(ddl, eos_present);
+			}
+			if (DDLCLIENT_STATE_IS(ddl,
+				DDL_CLIENT_WAIT_FOR_EOS_DONE) &&
+				encoder->i_period.b_frames) {
+				if ((ddl->extra_output_buf_count < 0) ||
+					(ddl->extra_output_buf_count >
+					encoder->i_period.b_frames)) {
+					DDL_MSG_ERROR("Invalid B frame output"
+								  "buffer index");
+				} else {
+					struct vidc_1080p_enc_frame_start_param
+						enc_param;
+					ddl->output_frame = ddl->\
+					extra_output_frame[ddl->\
+					extra_output_buf_count];
+					ddl->\
+					extra_output_buf_count--;
+					output_frame =
+					&ddl->output_frame.\
+					vcd_frm;
+					memset(&enc_param, 0,
+						sizeof(enc_param));
+					enc_param.cmd_seq_num =
+						++ddl_context->cmd_seq_num;
+					enc_param.inst_id = ddl->instance_id;
+					enc_param.shared_mem_addr_offset =
+					   DDL_ADDR_OFFSET(ddl_context->\
+						dram_base_a, ddl->shared_mem
+						[ddl->command_channel]);
+					enc_param.stream_buffer_addr_offset =
+						DDL_OFFSET(ddl_context->\
+						dram_base_a.\
+						align_physical_addr,
+						output_frame->physical);
+					enc_param.stream_buffer_size =
+					encoder->client_output_buf_req.sz;
+					enc_param.encode =
+					VIDC_1080P_ENC_TYPE_LAST_FRAME_DATA;
+					ddl->cmd_state = DDL_CMD_ENCODE_FRAME;
+					ddl_context->vidc_encode_frame_start
+						[ddl->command_channel]
+						(&enc_param);
+					}
+				} else if (eos_present &&
+					encoder->slice_delivery_info.enable) {
+					ddl_vidc_encode_eos_run(ddl);
+				} else {
+				DDL_MSG_LOW("ddl_state_transition: %s ~~>"
+					"DDL_CLIENT_WAIT_FOR_FRAME",
+					ddl_get_state_string(
+					ddl->client_state));
+				ddl->client_state =
+					DDL_CLIENT_WAIT_FOR_FRAME;
+				ddl_release_command_channel(ddl_context,
+				ddl->command_channel);
+			}
+		} else {
+			ddl_context->ddl_callback(
+				VCD_EVT_RESP_TRANSACTION_PENDING,
+				VCD_S_SUCCESS, NULL, 0, (u32 *)ddl,
+				ddl->client_data);
+			DDL_MSG_LOW("ddl_state_transition: %s ~~>"
+				"DDL_CLIENT_WAIT_FOR_FRAME",
+			ddl_get_state_string(ddl->client_state));
+			ddl->client_state = DDL_CLIENT_WAIT_FOR_FRAME;
+			ddl_release_command_channel(ddl_context,
+			ddl->command_channel);
+		}
+	}
+}
+
+static void get_dec_status(struct ddl_client_context *ddl,
+	 struct vidc_1080p_dec_disp_info *dec_disp_info,
+	 u32 output_order, u32 *status, u32 *rsl_chg)
+{
+	if (output_order == VCD_DEC_ORDER_DISPLAY) {
+		vidc_1080p_get_display_frame_result(dec_disp_info);
+		*status = dec_disp_info->display_status;
+		*rsl_chg = dec_disp_info->disp_resl_change;
+	} else {
+		vidc_1080p_get_decode_frame_result(dec_disp_info);
+		vidc_sm_get_dec_order_resl(
+			&ddl->shared_mem[ddl->command_channel],
+			&dec_disp_info->img_size_x,
+			&dec_disp_info->img_size_y);
+		*status = dec_disp_info->decode_status;
+		*rsl_chg = dec_disp_info->dec_resl_change;
+	}
+}
+
+static u32 ddl_decoder_frame_run_callback(struct ddl_client_context *ddl)
+{
+	struct ddl_context *ddl_context = ddl->ddl_context;
+	struct ddl_decoder_data *decoder = &ddl->codec_data.decoder;
+	u32 callback_end = false, ret_status = false;
+	u32 eos_present = false, rsl_chg;
+	u32 more_field_needed, extended_rsl_chg;
+	enum vidc_1080p_display_status disp_status;
+	DDL_MSG_MED("ddl_decoder_frame_run_callback");
+	if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME_DONE)) {
+		DDL_MSG_ERROR("STATE-CRITICAL-DECFRMRUN");
+		ddl_client_fatal_cb(ddl);
+		ret_status = true;
+	} else {
+		DDL_MSG_LOW("DEC_FRM_RUN_DONE");
+		ddl->cmd_state = DDL_CMD_INVALID;
+		get_dec_status(ddl, &ddl->codec_data.decoder.dec_disp_info,
+			ddl->codec_data.decoder.output_order,
+			&disp_status, &rsl_chg);
+
+		vidc_sm_get_extended_decode_status(
+			&ddl->shared_mem[ddl->command_channel],
+			&more_field_needed,
+			&extended_rsl_chg);
+		decoder->field_needed_for_prev_ip =
+			more_field_needed;
+		decoder->prev_ip_frm_tag =
+			ddl->input_frame.vcd_frm.ip_frm_tag;
+
+		ddl_vidc_decode_dynamic_property(ddl, false);
+		if (rsl_chg != DDL_RESL_CHANGE_NO_CHANGE) {
+			ddl_handle_reconfig(rsl_chg, ddl);
+			ret_status = false;
+		} else {
+			if ((VCD_FRAME_FLAG_EOS &
+				ddl->input_frame.vcd_frm.flags)) {
+				callback_end = false;
+				eos_present = true;
+			}
+			if (disp_status ==
+				VIDC_1080P_DISPLAY_STATUS_DECODE_ONLY ||
+				disp_status ==
+				VIDC_1080P_DISPLAY_STATUS_DECODE_AND_DISPLAY) {
+				if (!eos_present)
+					callback_end =
+					(disp_status ==
+					VIDC_1080P_DISPLAY_STATUS_DECODE_ONLY);
+				ddl_decoder_input_done_callback(ddl,
+					callback_end);
+			}
+			if (disp_status ==
+				VIDC_1080P_DISPLAY_STATUS_DECODE_AND_DISPLAY ||
+				disp_status ==
+				VIDC_1080P_DISPLAY_STATUS_DISPLAY_ONLY) {
+				if (!eos_present)
+					callback_end = (disp_status ==
+				VIDC_1080P_DISPLAY_STATUS_DECODE_AND_DISPLAY);
+				if (ddl_decoder_output_done_callback(
+					ddl, callback_end))
+					ret_status = true;
+			}
+			if (!ret_status) {
+				if (disp_status ==
+					VIDC_1080P_DISPLAY_STATUS_DISPLAY_ONLY
+					|| disp_status ==
+					VIDC_1080P_DISPLAY_STATUS_DPB_EMPTY ||
+					disp_status ==
+					VIDC_1080P_DISPLAY_STATUS_NOOP) {
+					ddl_vidc_decode_frame_run(ddl);
+				} else if (eos_present)
+					ddl_vidc_decode_eos_run(ddl);
+				else {
+					ddl->client_state =
+						DDL_CLIENT_WAIT_FOR_FRAME;
+					ddl_release_command_channel(ddl_context,
+						ddl->command_channel);
+					ret_status = true;
+				}
+			}
+		}
+	}
+	return ret_status;
+}
+
+static u32 ddl_eos_frame_done_callback(
+	struct ddl_client_context *ddl)
+{
+	struct ddl_context *ddl_context = ddl->ddl_context;
+	struct ddl_decoder_data *decoder = &ddl->codec_data.decoder;
+	struct ddl_mask *dpb_mask = &decoder->dpb_mask;
+	u32 ret_status = true, rsl_chg, more_field_needed;
+	enum vidc_1080p_display_status disp_status;
+
+	if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_EOS_DONE)) {
+		DDL_MSG_ERROR("STATE-CRITICAL-EOSFRMRUN");
+		ddl_client_fatal_cb(ddl);
+	} else {
+		DDL_MSG_LOW("EOS_FRM_RUN_DONE");
+		ddl->cmd_state = DDL_CMD_INVALID;
+		get_dec_status(ddl, &ddl->codec_data.decoder.dec_disp_info,
+			ddl->codec_data.decoder.output_order,
+			&disp_status, &rsl_chg);
+		vidc_sm_get_extended_decode_status(
+			&ddl->shared_mem[ddl->command_channel],
+			&more_field_needed, &rsl_chg);
+
+		decoder->field_needed_for_prev_ip =
+			more_field_needed;
+		decoder->prev_ip_frm_tag =
+			ddl->input_frame.vcd_frm.ip_frm_tag;
+		ddl_vidc_decode_dynamic_property(ddl, false);
+		if (disp_status ==
+			VIDC_1080P_DISPLAY_STATUS_DPB_EMPTY) {
+			if (rsl_chg) {
+				decoder->header_in_start = false;
+				decoder->decode_config.sequence_header =
+					ddl->input_frame.vcd_frm.physical;
+				decoder->decode_config.sequence_header_len =
+					ddl->input_frame.vcd_frm.data_len;
+				decoder->reconfig_detected = false;
+				ddl_vidc_decode_init_codec(ddl);
+				ret_status = false;
+			} else
+				ddl_decoder_eos_done_callback(ddl);
+		} else {
+			struct vidc_1080p_dec_frame_start_param dec_param;
+			ret_status = false;
+			if (disp_status ==
+				VIDC_1080P_DISPLAY_STATUS_DISPLAY_ONLY) {
+				if (ddl_decoder_output_done_callback(
+					ddl, false))
+					ret_status = true;
+			} else if (disp_status !=
+				VIDC_1080P_DISPLAY_STATUS_NOOP)
+				DDL_MSG_ERROR("EOS-STATE-CRITICAL-"
+					"WRONG-DISP-STATUS");
+			if (!ret_status) {
+				ddl_decoder_dpb_transact(decoder, NULL,
+					DDL_DPB_OP_SET_MASK);
+				ddl->cmd_state = DDL_CMD_EOS;
+
+				memset(&dec_param, 0, sizeof(dec_param));
+
+				dec_param.cmd_seq_num =
+					++ddl_context->cmd_seq_num;
+				dec_param.inst_id = ddl->instance_id;
+				dec_param.shared_mem_addr_offset =
+					DDL_ADDR_OFFSET(
+					ddl_context->dram_base_a,
+					ddl->shared_mem[ddl->command_channel]);
+				dec_param.release_dpb_bit_mask =
+					dpb_mask->hw_mask;
+				dec_param.decode =
+					(decoder->reconfig_detected) ?\
+					VIDC_1080P_DEC_TYPE_FRAME_DATA :\
+					VIDC_1080P_DEC_TYPE_LAST_FRAME_DATA;
+
+				ddl_context->vidc_decode_frame_start[ddl->\
+					command_channel](&dec_param);
+			}
+		}
+	}
+	return ret_status;
+}
+
+static u32 ddl_frame_run_callback(struct ddl_context *ddl_context)
+{
+	struct ddl_client_context *ddl;
+	u32 channel_inst_id;
+	u32 return_status = true;
+
+	vidc_1080p_get_returned_channel_inst_id(&channel_inst_id);
+	vidc_1080p_clear_returned_channel_inst_id();
+	ddl = ddl_get_current_ddl_client_for_channel_id(ddl_context,
+			ddl_context->response_cmd_ch_id);
+	if (ddl) {
+		if (ddl_context->pix_cache_enable) {
+			struct vidc_1080P_pix_cache_statistics
+			pixel_cache_stats;
+			vidc_pix_cache_get_statistics(&pixel_cache_stats);
+
+			DDL_MSG_HIGH(" pixel cache hits = %d,"
+				"miss = %d", pixel_cache_stats.access_hit,
+				pixel_cache_stats.access_miss);
+			DDL_MSG_HIGH(" pixel cache core reqs = %d,"
+				"axi reqs = %d", pixel_cache_stats.core_req,
+				pixel_cache_stats.axi_req);
+			DDL_MSG_HIGH(" pixel cache core bus stats = %d,"
+			"axi bus stats = %d", pixel_cache_stats.core_bus,
+				pixel_cache_stats.axi_bus);
+		}
+
+		if (ddl->cmd_state == DDL_CMD_DECODE_FRAME)
+			return_status = ddl_decoder_frame_run_callback(ddl);
+		else if (ddl->cmd_state == DDL_CMD_ENCODE_FRAME)
+			ddl_encoder_frame_run_callback(ddl);
+		else if (ddl->cmd_state == DDL_CMD_EOS)
+			return_status = ddl_eos_frame_done_callback(ddl);
+		else {
+			DDL_MSG_ERROR("UNKWN_FRAME_DONE");
+			return_status = false;
+		}
+	} else
+		return_status = false;
+
+	return return_status;
+}
+
+static void ddl_channel_end_callback(struct ddl_context *ddl_context)
+{
+	struct ddl_client_context *ddl;
+
+	DDL_MSG_MED("ddl_channel_end_callback");
+	ddl = ddl_get_current_ddl_client_for_command(ddl_context,
+			DDL_CMD_CHANNEL_END);
+	if (ddl) {
+		ddl->cmd_state = DDL_CMD_INVALID;
+		if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_CHEND)) {
+			DDL_MSG_LOW("STATE-CRITICAL-CHEND");
+		} else {
+			DDL_MSG_LOW("CH_END_DONE");
+			ddl_release_client_internal_buffers(ddl);
+			ddl_context->ddl_callback(VCD_EVT_RESP_STOP,
+				VCD_S_SUCCESS, NULL, 0, (u32 *)ddl,
+				ddl->client_data);
+			DDL_MSG_LOW("ddl_state_transition: %s ~~>"
+				"DDL_CLIENT_OPEN",
+				ddl_get_state_string(ddl->client_state));
+			ddl->client_state = DDL_CLIENT_OPEN;
+		}
+		ddl_release_command_channel(ddl_context,
+			ddl->command_channel);
+	}
+}
+
+static void ddl_edfu_callback(struct ddl_context *ddl_context)
+{
+	struct ddl_client_context *ddl;
+	u32 channel_inst_id;
+
+	DDL_MSG_MED("ddl_edfu_callback");
+	vidc_1080p_get_returned_channel_inst_id(&channel_inst_id);
+	vidc_1080p_clear_returned_channel_inst_id();
+	ddl = ddl_get_current_ddl_client_for_channel_id(ddl_context,
+			ddl_context->response_cmd_ch_id);
+	if (ddl) {
+		if (ddl->cmd_state != DDL_CMD_ENCODE_FRAME)
+			DDL_MSG_LOW("UNKWN_EDFU");
+	}
+}
+
+static void ddl_encoder_eos_done(struct ddl_context *ddl_context)
+{
+	struct ddl_client_context *ddl;
+	u32 channel_inst_id;
+
+	vidc_1080p_get_returned_channel_inst_id(&channel_inst_id);
+	vidc_1080p_clear_returned_channel_inst_id();
+	ddl = ddl_get_current_ddl_client_for_channel_id(ddl_context,
+			ddl_context->response_cmd_ch_id);
+	DDL_MSG_LOW("ddl_encoder_eos_done\n");
+	if (ddl == NULL) {
+		DDL_MSG_ERROR("NO_DDL_CONTEXT");
+	} else {
+		if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_EOS_DONE)) {
+			DDL_MSG_ERROR("STATE-CRITICAL-EOSFRMDONE");
+			ddl_client_fatal_cb(ddl);
+		} else {
+			struct ddl_encoder_data *encoder =
+				&(ddl->codec_data.encoder);
+			vidc_1080p_get_encode_frame_info(
+				&encoder->enc_frame_info);
+			if (!encoder->slice_delivery_info.enable) {
+				ddl_handle_enc_frame_done(ddl, true);
+				ddl->cmd_state = DDL_CMD_INVALID;
+			}
+			DDL_MSG_LOW("encoder_eos_done");
+			DDL_MSG_LOW("ddl_state_transition: %s ~~>"
+				"DDL_CLIENT_WAIT_FOR_FRAME",
+				ddl_get_state_string(ddl->client_state));
+			ddl->client_state = DDL_CLIENT_WAIT_FOR_FRAME;
+			DDL_MSG_LOW("eos_done");
+			ddl_context->ddl_callback(VCD_EVT_RESP_EOS_DONE,
+					VCD_S_SUCCESS, NULL, 0,
+					(u32 *)ddl, ddl->client_data);
+			ddl_release_command_channel(ddl_context,
+				ddl->command_channel);
+		}
+	}
+}
+
+static u32 ddl_slice_done_callback(struct ddl_context *ddl_context)
+{
+	struct ddl_client_context *ddl;
+	u32 channel_inst_id;
+	u32 return_status = true;
+	DDL_MSG_LOW("ddl_sliceDoneCallback");
+	vidc_1080p_get_returned_channel_inst_id(&channel_inst_id);
+	vidc_1080p_clear_returned_channel_inst_id();
+	ddl = ddl_get_current_ddl_client_for_channel_id(ddl_context,
+			ddl_context->response_cmd_ch_id);
+	if (ddl == NULL) {
+		DDL_MSG_ERROR("NO_DDL_CONTEXT");
+		return_status = false;
+	} else if (ddl->cmd_state == DDL_CMD_ENCODE_FRAME) {
+			ddl->cmd_state = DDL_CMD_INVALID;
+			if (DDLCLIENT_STATE_IS(ddl,
+				DDL_CLIENT_WAIT_FOR_FRAME_DONE)) {
+				ddl_handle_slice_done_slice_batch(ddl);
+			} else {
+				DDL_MSG_ERROR("STATE-CRITICAL-ENCFRMRUN : %s\n",
+					 __func__);
+				ddl_client_fatal_cb(ddl);
+			}
+	} else {
+		DDL_MSG_ERROR("UNKNOWN_SLICEDONE : %s\n", __func__);
+	}
+	return return_status;
+}
+
+static u32 ddl_process_intr_status(struct ddl_context *ddl_context,
+	u32 intr_status)
+{
+	u32 return_status = true;
+	switch (intr_status) {
+	case VIDC_1080P_RISC2HOST_CMD_OPEN_CH_RET:
+		return_status = ddl_channel_set_callback(ddl_context,
+			ddl_context->response_cmd_ch_id);
+	break;
+	case VIDC_1080P_RISC2HOST_CMD_CLOSE_CH_RET:
+		ddl_channel_end_callback(ddl_context);
+	break;
+	case VIDC_1080P_RISC2HOST_CMD_SEQ_DONE_RET:
+		return_status = ddl_sequence_done_callback(ddl_context);
+	break;
+	case VIDC_1080P_RISC2HOST_CMD_FRAME_DONE_RET:
+		return_status = ddl_frame_run_callback(ddl_context);
+	break;
+	case VIDC_1080P_RISC2HOST_CMD_SLICE_DONE_RET:
+		 ddl_slice_done_callback(ddl_context);
+	break;
+	case VIDC_1080P_RISC2HOST_CMD_SYS_INIT_RET:
+		ddl_sys_init_done_callback(ddl_context,
+			ddl_context->response_cmd_ch_id);
+	break;
+	case VIDC_1080P_RISC2HOST_CMD_FW_STATUS_RET:
+		ddl_fw_status_done_callback(ddl_context);
+	break;
+	case VIDC_1080P_RISC2HOST_CMD_EDFU_INT_RET:
+		ddl_edfu_callback(ddl_context);
+	break;
+	case VIDC_1080P_RISC2HOST_CMD_ENC_COMPLETE_RET:
+		ddl_encoder_eos_done(ddl_context);
+	break;
+	case VIDC_1080P_RISC2HOST_CMD_ERROR_RET:
+		DDL_MSG_ERROR("CMD_ERROR_INTR");
+		return_status = ddl_handle_core_errors(ddl_context);
+	break;
+	case VIDC_1080P_RISC2HOST_CMD_INIT_BUFFERS_RET:
+		return_status =
+			ddl_dpb_buffers_set_done_callback(ddl_context);
+	break;
+	default:
+		DDL_MSG_LOW("UNKWN_INTR");
+	break;
+	}
+	return return_status;
+}
+
+void ddl_read_and_clear_interrupt(void)
+{
+	struct ddl_context *ddl_context;
+	struct ddl_hw_interface  *ddl_hw_response;
+	struct ddl_client_context *ddl;
+	struct ddl_encoder_data *encoder;
+
+	ddl_context = ddl_get_context();
+	if (!ddl_context->core_virtual_base_addr) {
+		DDL_MSG_LOW("SPURIOUS_INTERRUPT");
+	} else {
+		ddl_hw_response = &ddl_context->ddl_hw_response;
+		vidc_1080p_get_risc2host_cmd(&ddl_hw_response->cmd,
+			&ddl_hw_response->arg1, &ddl_hw_response->arg2,
+			&ddl_hw_response->arg3,
+			&ddl_hw_response->arg4);
+		DDL_MSG_LOW("%s vidc_1080p_get_risc2host_cmd cmd = %u"
+			"arg1 = %u arg2 = %u arg3 = %u"
+			"arg4 = %u\n",
+			__func__, ddl_hw_response->cmd,
+			ddl_hw_response->arg1, ddl_hw_response->arg2,
+			ddl_hw_response->arg3,
+			ddl_hw_response->arg4);
+		ddl = ddl_get_current_ddl_client_for_channel_id(ddl_context,
+			ddl_context->response_cmd_ch_id);
+		if (ddl) {
+			encoder = &(ddl->codec_data.encoder);
+			if (encoder && encoder->slice_delivery_info.enable
+			&&
+			((ddl_hw_response->cmd ==
+				VIDC_1080P_RISC2HOST_CMD_SLICE_DONE_RET)
+			|| (ddl_hw_response->cmd ==
+				VIDC_1080P_RISC2HOST_CMD_FRAME_DONE_RET))) {
+				vidc_sm_set_encoder_slice_batch_int_ctrl(
+				&ddl->shared_mem[ddl->command_channel],
+				1);
+			}
+		}
+		vidc_1080p_clear_risc2host_cmd();
+		vidc_1080p_clear_interrupt();
+		vidc_1080p_get_risc2host_cmd_status(ddl_hw_response->arg2,
+			&ddl_context->cmd_err_status,
+			&ddl_context->disp_pic_err_status);
+		DDL_MSG_LOW("%s cmd_err_status = %d\n", __func__,
+				ddl_context->cmd_err_status);
+		ddl_context->response_cmd_ch_id = ddl_hw_response->arg1;
+	}
+}
+
+u32 ddl_process_core_response(void)
+{
+	struct ddl_context *ddl_context;
+	struct ddl_hw_interface *ddl_hw_response;
+	u32 status = false;
+
+	ddl_context = ddl_get_context();
+	if (!ddl_context->core_virtual_base_addr) {
+		DDL_MSG_LOW("SPURIOUS_INTERRUPT");
+		return status;
+	}
+	ddl_hw_response = &ddl_context->ddl_hw_response;
+	status = ddl_process_intr_status(ddl_context, ddl_hw_response->cmd);
+	if (ddl_context->interrupt_clr)
+		(*ddl_context->interrupt_clr)();
+	return status;
+}
+
+static void ddl_decoder_input_done_callback(
+	struct ddl_client_context *ddl, u32 frame_transact_end)
+{
+	struct ddl_context *ddl_context = ddl->ddl_context;
+	struct ddl_decoder_data *decoder = &(ddl->codec_data.decoder);
+	struct vidc_1080p_dec_disp_info *dec_disp_info =
+		&decoder->dec_disp_info;
+	struct vcd_frame_data *input_vcd_frm = &(ddl->input_frame.vcd_frm);
+	u32 is_interlaced;
+	vidc_1080p_get_decoded_frame_size(
+		&dec_disp_info->input_bytes_consumed);
+	vidc_sm_set_start_byte_number(&ddl->shared_mem
+		[ddl->command_channel], 0);
+	vidc_1080p_get_decode_frame(&dec_disp_info->input_frame);
+	ddl_get_decoded_frame(input_vcd_frm,
+		dec_disp_info->input_frame);
+	vidc_1080p_get_decode_frame_result(dec_disp_info);
+	is_interlaced = (dec_disp_info->decode_coding ==
+		VIDC_1080P_DISPLAY_CODING_INTERLACED);
+	if (decoder->output_order == VCD_DEC_ORDER_DECODE) {
+		dec_disp_info->tag_bottom = is_interlaced ?
+			dec_disp_info->tag_top :
+			VCD_FRAMETAG_INVALID;
+		dec_disp_info->tag_top = input_vcd_frm->ip_frm_tag;
+	}
+	input_vcd_frm->interlaced = is_interlaced;
+	input_vcd_frm->offset += dec_disp_info->input_bytes_consumed;
+	input_vcd_frm->data_len -= dec_disp_info->input_bytes_consumed;
+	ddl->input_frame.frm_trans_end = frame_transact_end;
+	ddl_calc_core_proc_time(__func__, DEC_IP_TIME, ddl);
+	ddl_context->ddl_callback(VCD_EVT_RESP_INPUT_DONE, VCD_S_SUCCESS,
+		&ddl->input_frame, sizeof(struct ddl_frame_data_tag),
+		(u32 *)ddl, ddl->client_data);
+}
+
+static void get_dec_op_done_data(struct vidc_1080p_dec_disp_info *dec_disp_info,
+	u32 output_order, u8 **physical, u32 *is_interlaced)
+{
+	enum vidc_1080p_display_coding disp_coding;
+	if (output_order == VCD_DEC_ORDER_DECODE) {
+		*physical = (u8 *)(dec_disp_info->decode_y_addr << 11);
+		disp_coding = dec_disp_info->decode_coding;
+	} else {
+		*physical = (u8 *)(dec_disp_info->display_y_addr << 11);
+		disp_coding = dec_disp_info->display_coding;
+	}
+	*is_interlaced = (disp_coding ==
+			VIDC_1080P_DISPLAY_CODING_INTERLACED);
+}
+
+static void get_dec_op_done_crop(u32 output_order,
+	struct vidc_1080p_dec_disp_info *dec_disp_info,
+	struct vcd_frame_rect *crop_data,
+	struct vcd_property_frame_size *op_frame_sz,
+	struct vcd_property_frame_size *frame_sz,
+	struct ddl_buf_addr *shared_mem)
+{
+	u32 crop_exists =
+		(output_order == VCD_DEC_ORDER_DECODE) ?
+		dec_disp_info->dec_crop_exists :
+		dec_disp_info->disp_crop_exists;
+	crop_data->left = 0;
+	crop_data->top = 0;
+	crop_data->right = dec_disp_info->img_size_x;
+	crop_data->bottom = dec_disp_info->img_size_y;
+	op_frame_sz->width = dec_disp_info->img_size_x;
+	op_frame_sz->height = dec_disp_info->img_size_y;
+	ddl_calculate_stride(op_frame_sz, false);
+	op_frame_sz->stride = DDL_ALIGN(op_frame_sz->width,
+				DDL_TILE_ALIGN_WIDTH);
+	op_frame_sz->scan_lines = DDL_ALIGN(op_frame_sz->height,
+					DDL_TILE_ALIGN_HEIGHT);
+	DDL_MSG_LOW("%s img_size_x = %u img_size_y = %u\n",
+				__func__, dec_disp_info->img_size_x,
+				dec_disp_info->img_size_y);
+	if (crop_exists) {
+		if (output_order == VCD_DEC_ORDER_DECODE)
+			vidc_sm_get_dec_order_crop_info(shared_mem,
+				&dec_disp_info->crop_left_offset,
+				&dec_disp_info->crop_right_offset,
+				&dec_disp_info->crop_top_offset,
+				&dec_disp_info->crop_bottom_offset);
+		else
+			vidc_sm_get_crop_info(shared_mem,
+				&dec_disp_info->crop_left_offset,
+				&dec_disp_info->crop_right_offset,
+				&dec_disp_info->crop_top_offset,
+				&dec_disp_info->crop_bottom_offset);
+		crop_data->left = dec_disp_info->crop_left_offset;
+		crop_data->top = dec_disp_info->crop_top_offset;
+		crop_data->right -= dec_disp_info->crop_right_offset;
+		crop_data->bottom -= dec_disp_info->crop_bottom_offset;
+		op_frame_sz->width = crop_data->right - crop_data->left;
+		op_frame_sz->height = crop_data->bottom - crop_data->top;
+	}
+}
+
+static u32 ddl_decoder_output_done_callback(
+	struct ddl_client_context *ddl, u32 frame_transact_end)
+{
+	struct ddl_context *ddl_context = ddl->ddl_context;
+	struct ddl_decoder_data *decoder = &(ddl->codec_data.decoder);
+	struct vidc_1080p_dec_disp_info *dec_disp_info =
+		&(decoder->dec_disp_info);
+	struct ddl_frame_data_tag *output_frame = &(ddl->output_frame);
+	struct vcd_frame_data *output_vcd_frm = &(output_frame->vcd_frm);
+	u32 vcd_status, free_luma_dpb = 0, disp_pict = 0, is_interlaced;
+	get_dec_op_done_data(dec_disp_info, decoder->output_order,
+		&output_vcd_frm->physical, &is_interlaced);
+	decoder->progressive_only = !(is_interlaced);
+	output_vcd_frm->frame = VCD_FRAME_YUV;
+	if (decoder->codec.codec == VCD_CODEC_MPEG4 ||
+		decoder->codec.codec == VCD_CODEC_VC1 ||
+		decoder->codec.codec == VCD_CODEC_VC1_RCV ||
+		(decoder->codec.codec >= VCD_CODEC_DIVX_3 &&
+		decoder->codec.codec <= VCD_CODEC_XVID)) {
+		vidc_sm_get_displayed_picture_frame(&ddl->shared_mem
+		[ddl->command_channel], &disp_pict);
+		if (decoder->output_order == VCD_DEC_ORDER_DISPLAY) {
+			if (!disp_pict) {
+				output_vcd_frm->frame = VCD_FRAME_NOTCODED;
+				vidc_sm_get_available_luma_dpb_address(
+					&ddl->shared_mem[ddl->command_channel],
+					&free_luma_dpb);
+			}
+		} else {
+			if (dec_disp_info->input_frame ==
+				VIDC_1080P_DECODE_FRAMETYPE_NOT_CODED) {
+				output_vcd_frm->frame = VCD_FRAME_NOTCODED;
+			vidc_sm_get_available_luma_dpb_dec_order_address(
+					&ddl->shared_mem[ddl->command_channel],
+					&free_luma_dpb);
+			}
+		}
+		if (free_luma_dpb)
+			output_vcd_frm->physical =
+				(u8 *)(free_luma_dpb << 11);
+	}
+	vcd_status = ddl_decoder_dpb_transact(decoder, output_frame,
+			DDL_DPB_OP_MARK_BUSY);
+	if (vcd_status) {
+		DDL_MSG_ERROR("CORRUPTED_OUTPUT_BUFFER_ADDRESS");
+		ddl_hw_fatal_cb(ddl);
+	} else {
+		vidc_sm_get_metadata_status(&ddl->shared_mem
+			[ddl->command_channel],
+			&decoder->meta_data_exists);
+		if (decoder->output_order == VCD_DEC_ORDER_DISPLAY) {
+			vidc_sm_get_frame_tags(&ddl->shared_mem
+				[ddl->command_channel],
+				&dec_disp_info->tag_top,
+				&dec_disp_info->tag_bottom);
+			if (dec_disp_info->display_correct ==
+				VIDC_1080P_DECODE_NOT_CORRECT ||
+				dec_disp_info->display_correct ==
+				VIDC_1080P_DECODE_APPROX_CORRECT)
+				output_vcd_frm->flags |=
+					VCD_FRAME_FLAG_DATACORRUPT;
+		} else {
+			if (dec_disp_info->decode_correct ==
+				VIDC_1080P_DECODE_NOT_CORRECT ||
+				dec_disp_info->decode_correct ==
+				VIDC_1080P_DECODE_APPROX_CORRECT)
+				output_vcd_frm->flags |=
+					VCD_FRAME_FLAG_DATACORRUPT;
+		}
+		output_vcd_frm->ip_frm_tag = dec_disp_info->tag_top;
+		vidc_sm_get_picture_times(&ddl->shared_mem
+			[ddl->command_channel],
+			&dec_disp_info->pic_time_top,
+			&dec_disp_info->pic_time_bottom);
+		get_dec_op_done_crop(decoder->output_order, dec_disp_info,
+			&output_vcd_frm->dec_op_prop.disp_frm,
+			&output_vcd_frm->dec_op_prop.frm_size,
+			&decoder->frame_size,
+			&ddl->shared_mem[ddl_context->response_cmd_ch_id]);
+		if ((decoder->cont_mode) &&
+			((output_vcd_frm->dec_op_prop.frm_size.width !=
+			decoder->frame_size.width) ||
+			(output_vcd_frm->dec_op_prop.frm_size.height !=
+			decoder->frame_size.height) ||
+			(decoder->frame_size.width !=
+			decoder->client_frame_size.width) ||
+			(decoder->frame_size.height !=
+			decoder->client_frame_size.height))) {
+			DDL_MSG_LOW("%s o/p width = %u o/p height = %u"
+				"decoder width = %u decoder height = %u ",
+				__func__,
+				output_vcd_frm->dec_op_prop.frm_size.width,
+				output_vcd_frm->dec_op_prop.frm_size.height,
+				decoder->frame_size.width,
+				decoder->frame_size.height);
+			DDL_MSG_HIGH("%s Sending INFO_OP_RECONFIG event\n",
+				 __func__);
+			ddl_context->ddl_callback(
+				VCD_EVT_IND_INFO_OUTPUT_RECONFIG,
+				VCD_S_SUCCESS, NULL, 0,
+				(u32 *)ddl,
+				ddl->client_data);
+			decoder->frame_size =
+				 output_vcd_frm->dec_op_prop.frm_size;
+			decoder->client_frame_size = decoder->frame_size;
+			decoder->y_cb_cr_size =
+				ddl_get_yuv_buffer_size(&decoder->frame_size,
+					&decoder->buf_format,
+					(!decoder->progressive_only),
+					decoder->codec.codec, NULL);
+			decoder->actual_output_buf_req.sz =
+				decoder->y_cb_cr_size + decoder->suffix;
+			decoder->min_output_buf_req =
+				decoder->actual_output_buf_req;
+			DDL_MSG_LOW("%s y_cb_cr_size = %u "
+				"actual_output_buf_req.sz = %u"
+				"min_output_buf_req.sz = %u\n",
+				decoder->y_cb_cr_size,
+				decoder->actual_output_buf_req.sz,
+				decoder->min_output_buf_req.sz);
+			vidc_sm_set_chroma_addr_change(
+			&ddl->shared_mem[ddl->command_channel],
+			false);
+		}
+		output_vcd_frm->interlaced = is_interlaced;
+		output_vcd_frm->intrlcd_ip_frm_tag =
+			(!is_interlaced || !dec_disp_info->tag_bottom) ?
+			VCD_FRAMETAG_INVALID : dec_disp_info->tag_bottom;
+		output_vcd_frm->offset = 0;
+		output_vcd_frm->data_len = decoder->y_cb_cr_size;
+		if (free_luma_dpb) {
+			output_vcd_frm->data_len = 0;
+			output_vcd_frm->flags |= VCD_FRAME_FLAG_DECODEONLY;
+		}
+		output_vcd_frm->flags |= VCD_FRAME_FLAG_ENDOFFRAME;
+		output_frame->frm_trans_end = frame_transact_end;
+		ddl_calc_core_proc_time(__func__, DEC_OP_TIME, ddl);
+		ddl_process_decoder_metadata(ddl);
+		vidc_sm_get_aspect_ratio_info(
+			&ddl->shared_mem[ddl->command_channel],
+			&output_vcd_frm->aspect_ratio_info);
+		ddl_context->ddl_callback(VCD_EVT_RESP_OUTPUT_DONE,
+			vcd_status, output_frame,
+			sizeof(struct ddl_frame_data_tag),
+			(u32 *)ddl, ddl->client_data);
+	}
+	return vcd_status;
+}
+
+static u32 ddl_get_decoded_frame(struct vcd_frame_data  *frame,
+	enum vidc_1080p_decode_frame frame_type)
+{
+	u32 status = true;
+
+	switch (frame_type) {
+	case VIDC_1080P_DECODE_FRAMETYPE_I:
+		frame->flags |= VCD_FRAME_FLAG_SYNCFRAME;
+		frame->frame = VCD_FRAME_I;
+	break;
+	case VIDC_1080P_DECODE_FRAMETYPE_P:
+		frame->frame = VCD_FRAME_P;
+	break;
+	case VIDC_1080P_DECODE_FRAMETYPE_B:
+		frame->frame = VCD_FRAME_B;
+	break;
+	case VIDC_1080P_DECODE_FRAMETYPE_NOT_CODED:
+		frame->frame = VCD_FRAME_NOTCODED;
+		frame->data_len = 0;
+		DDL_MSG_HIGH("DDL_INFO:Decoder:NotCodedFrame>");
+	break;
+	case VIDC_1080P_DECODE_FRAMETYPE_OTHERS:
+		frame->frame = VCD_FRAME_YUV;
+	break;
+	case VIDC_1080P_DECODE_FRAMETYPE_32BIT:
+	default:
+		DDL_MSG_ERROR("UNKNOWN-FRAMETYPE");
+		status = false;
+	break;
+	}
+	return status;
+}
+
+static u32 ddl_get_encoded_frame(struct vcd_frame_data *frame,
+	enum vcd_codec codec,
+	enum vidc_1080p_encode_frame frame_type)
+{
+	u32 status = true;
+
+	if (codec == VCD_CODEC_H264) {
+		switch (frame_type) {
+		case VIDC_1080P_ENCODE_FRAMETYPE_NOT_CODED:
+			frame->frame = VCD_FRAME_P;
+		break;
+		case VIDC_1080P_ENCODE_FRAMETYPE_I:
+			frame->flags |= VCD_FRAME_FLAG_SYNCFRAME;
+			frame->frame = VCD_FRAME_I;
+		break;
+		case VIDC_1080P_ENCODE_FRAMETYPE_P:
+			frame->frame = VCD_FRAME_P;
+		break;
+		case VIDC_1080P_ENCODE_FRAMETYPE_B:
+			frame->frame = VCD_FRAME_B;
+			frame->flags |= VCD_FRAME_FLAG_BFRAME;
+		break;
+		case VIDC_1080P_ENCODE_FRAMETYPE_SKIPPED:
+			frame->frame = VCD_FRAME_NOTCODED;
+			frame->data_len = 0;
+		break;
+		case VIDC_1080P_ENCODE_FRAMETYPE_OTHERS:
+			DDL_MSG_LOW("FRAMETYPE-OTHERS");
+		break;
+		case VIDC_1080P_ENCODE_FRAMETYPE_32BIT:
+		default:
+			DDL_MSG_LOW("UNKNOWN-FRAMETYPE");
+			status = false;
+		break;
+		}
+	} else if (codec == VCD_CODEC_MPEG4) {
+		switch (frame_type) {
+		case VIDC_1080P_ENCODE_FRAMETYPE_NOT_CODED:
+			frame->frame = VCD_FRAME_P;
+		break;
+		case VIDC_1080P_ENCODE_FRAMETYPE_I:
+			frame->flags |= VCD_FRAME_FLAG_SYNCFRAME;
+			frame->frame = VCD_FRAME_I;
+		break;
+		case VIDC_1080P_ENCODE_FRAMETYPE_P:
+			frame->frame = VCD_FRAME_P;
+		break;
+		case VIDC_1080P_ENCODE_FRAMETYPE_B:
+			frame->frame = VCD_FRAME_B;
+			frame->flags |= VCD_FRAME_FLAG_BFRAME;
+		break;
+		case VIDC_1080P_ENCODE_FRAMETYPE_SKIPPED:
+			frame->frame = VCD_FRAME_NOTCODED;
+			frame->data_len = 0;
+		break;
+		case VIDC_1080P_ENCODE_FRAMETYPE_OTHERS:
+			DDL_MSG_LOW("FRAMETYPE-OTHERS");
+		break;
+		case VIDC_1080P_ENCODE_FRAMETYPE_32BIT:
+		default:
+			DDL_MSG_LOW("UNKNOWN-FRAMETYPE");
+			status = false;
+		break;
+		}
+	} else if (codec == VCD_CODEC_H263) {
+		switch (frame_type) {
+		case VIDC_1080P_ENCODE_FRAMETYPE_NOT_CODED:
+			frame->frame = VCD_FRAME_P;
+		break;
+		case VIDC_1080P_ENCODE_FRAMETYPE_I:
+			frame->flags |= VCD_FRAME_FLAG_SYNCFRAME;
+			frame->frame = VCD_FRAME_I;
+		break;
+		case VIDC_1080P_ENCODE_FRAMETYPE_P:
+			frame->frame = VCD_FRAME_P;
+		break;
+		case VIDC_1080P_ENCODE_FRAMETYPE_SKIPPED:
+			frame->frame = VCD_FRAME_NOTCODED;
+			frame->data_len = 0;
+		break;
+		case VIDC_1080P_ENCODE_FRAMETYPE_OTHERS:
+			DDL_MSG_LOW("FRAMETYPE-OTHERS");
+		break;
+		case VIDC_1080P_ENCODE_FRAMETYPE_32BIT:
+		default:
+			DDL_MSG_LOW("UNKNOWN-FRAMETYPE");
+			status = false;
+		break;
+		}
+	} else
+		status = false;
+	DDL_MSG_HIGH("Enc Frame Type %u", (u32)frame->frame);
+	return status;
+}
+
+static void ddl_get_mpeg4_dec_level(enum vcd_codec_level *level,
+	u32 level_codec, enum vcd_codec_profile mpeg4_profile)
+{
+	switch (level_codec) {
+	case VIDC_1080P_MPEG4_LEVEL0:
+		*level = VCD_LEVEL_MPEG4_0;
+	break;
+	case VIDC_1080P_MPEG4_LEVEL0b:
+		*level = VCD_LEVEL_MPEG4_0b;
+	break;
+	case VIDC_1080P_MPEG4_LEVEL1:
+		*level = VCD_LEVEL_MPEG4_1;
+	break;
+	case VIDC_1080P_MPEG4_LEVEL2:
+		*level = VCD_LEVEL_MPEG4_2;
+	break;
+	case VIDC_1080P_MPEG4_LEVEL3:
+		*level = VCD_LEVEL_MPEG4_3;
+	break;
+	case VIDC_1080P_MPEG4_LEVEL3b:
+		if (mpeg4_profile == VCD_PROFILE_MPEG4_SP)
+			*level = VCD_LEVEL_MPEG4_7;
+		else
+			*level = VCD_LEVEL_MPEG4_3b;
+	break;
+	case VIDC_1080P_MPEG4_LEVEL4a:
+		*level = VCD_LEVEL_MPEG4_4a;
+	break;
+	case VIDC_1080P_MPEG4_LEVEL5:
+		*level = VCD_LEVEL_MPEG4_5;
+	break;
+	case VIDC_1080P_MPEG4_LEVEL6:
+		*level = VCD_LEVEL_MPEG4_6;
+	break;
+	default:
+		*level = VCD_LEVEL_UNKNOWN;
+	break;
+	}
+}
+
+static void ddl_get_h264_dec_level(enum vcd_codec_level *level,
+	u32 level_codec)
+{
+	switch (level_codec) {
+	case VIDC_1080P_H264_LEVEL1:
+		*level = VCD_LEVEL_H264_1;
+	break;
+	case VIDC_1080P_H264_LEVEL1b:
+		*level = VCD_LEVEL_H264_1b;
+	break;
+	case VIDC_1080P_H264_LEVEL1p1:
+		*level = VCD_LEVEL_H264_1p1;
+	break;
+	case VIDC_1080P_H264_LEVEL1p2:
+		*level = VCD_LEVEL_H264_1p2;
+	break;
+	case VIDC_1080P_H264_LEVEL1p3:
+		*level = VCD_LEVEL_H264_1p3;
+	break;
+	case VIDC_1080P_H264_LEVEL2:
+		*level = VCD_LEVEL_H264_2;
+	break;
+	case VIDC_1080P_H264_LEVEL2p1:
+		*level = VCD_LEVEL_H264_2p1;
+	break;
+	case VIDC_1080P_H264_LEVEL2p2:
+		*level = VCD_LEVEL_H264_2p2;
+	break;
+	case VIDC_1080P_H264_LEVEL3:
+		*level = VCD_LEVEL_H264_3;
+	break;
+	case VIDC_1080P_H264_LEVEL3p1:
+		*level = VCD_LEVEL_H264_3p1;
+	break;
+	case VIDC_1080P_H264_LEVEL3p2:
+		*level = VCD_LEVEL_H264_3p2;
+	break;
+	case VIDC_1080P_H264_LEVEL4:
+		*level = VCD_LEVEL_H264_4;
+	break;
+	default:
+		*level = VCD_LEVEL_UNKNOWN;
+	break;
+	}
+}
+
+static void ddl_get_h263_dec_level(enum vcd_codec_level *level,
+	u32 level_codec)
+{
+	switch (level_codec) {
+	case VIDC_1080P_H263_LEVEL10:
+		*level = VCD_LEVEL_H263_10;
+	break;
+	case VIDC_1080P_H263_LEVEL20:
+		*level = VCD_LEVEL_H263_20;
+	break;
+	case VIDC_1080P_H263_LEVEL30:
+		*level = VCD_LEVEL_H263_30;
+	break;
+	case VIDC_1080P_H263_LEVEL40:
+		*level = VCD_LEVEL_H263_40;
+	break;
+	case VIDC_1080P_H263_LEVEL45:
+		*level = VCD_LEVEL_H263_45;
+	break;
+	case VIDC_1080P_H263_LEVEL50:
+		*level = VCD_LEVEL_H263_50;
+	break;
+	case VIDC_1080P_H263_LEVEL60:
+		*level = VCD_LEVEL_H263_60;
+	break;
+	case VIDC_1080P_H263_LEVEL70:
+		*level = VCD_LEVEL_H263_70;
+	break;
+	default:
+		*level = VCD_LEVEL_UNKNOWN;
+	break;
+	}
+}
+
+static void ddl_get_vc1_dec_level(enum vcd_codec_level *level,
+	u32 level_codec, enum vcd_codec_profile vc1_profile)
+{
+	if (vc1_profile == VCD_PROFILE_VC1_ADVANCE) {
+		switch (level_codec) {
+		case VIDC_SM_LEVEL_VC1_ADV_0:
+			*level = VCD_LEVEL_VC1_A_0;
+		break;
+		case VIDC_SM_LEVEL_VC1_ADV_1:
+			*level = VCD_LEVEL_VC1_A_1;
+		break;
+		case VIDC_SM_LEVEL_VC1_ADV_2:
+			*level = VCD_LEVEL_VC1_A_2;
+		break;
+		case VIDC_SM_LEVEL_VC1_ADV_3:
+			*level = VCD_LEVEL_VC1_A_3;
+		break;
+		case VIDC_SM_LEVEL_VC1_ADV_4:
+			*level = VCD_LEVEL_VC1_A_4;
+		break;
+		default:
+			*level = VCD_LEVEL_UNKNOWN;
+		break;
+		}
+	} else if (vc1_profile == VCD_PROFILE_VC1_MAIN) {
+		switch (level_codec) {
+		case VIDC_SM_LEVEL_VC1_LOW:
+			*level = VCD_LEVEL_VC1_M_LOW;
+		break;
+		case VIDC_SM_LEVEL_VC1_MEDIUM:
+			*level = VCD_LEVEL_VC1_M_MEDIUM;
+		break;
+		case VIDC_SM_LEVEL_VC1_HIGH:
+			*level = VCD_LEVEL_VC1_M_HIGH;
+		break;
+		default:
+			*level = VCD_LEVEL_UNKNOWN;
+		break;
+		}
+	} else if (vc1_profile == VCD_PROFILE_VC1_SIMPLE) {
+		switch (level_codec) {
+		case VIDC_SM_LEVEL_VC1_LOW:
+			*level = VCD_LEVEL_VC1_S_LOW;
+		break;
+		case VIDC_SM_LEVEL_VC1_MEDIUM:
+			*level = VCD_LEVEL_VC1_S_MEDIUM;
+		break;
+		default:
+			*level = VCD_LEVEL_UNKNOWN;
+		break;
+		}
+	}
+}
+
+static void ddl_get_mpeg2_dec_level(enum vcd_codec_level *level,
+	u32 level_codec)
+{
+	switch (level_codec) {
+	case VIDC_SM_LEVEL_MPEG2_LOW:
+		*level = VCD_LEVEL_MPEG2_LOW;
+	break;
+	case VIDC_SM_LEVEL_MPEG2_MAIN:
+		*level = VCD_LEVEL_MPEG2_MAIN;
+	break;
+	case VIDC_SM_LEVEL_MPEG2_HIGH_1440:
+		*level = VCD_LEVEL_MPEG2_HIGH_14;
+	break;
+	case VIDC_SM_LEVEL_MPEG2_HIGH:
+		*level = VCD_LEVEL_MPEG2_HIGH;
+	break;
+	default:
+		*level = VCD_LEVEL_UNKNOWN;
+	break;
+	}
+}
+
+static void ddl_get_dec_profile_level(struct ddl_decoder_data *decoder,
+	u32 profile_codec, u32 level_codec)
+{
+	enum vcd_codec_profile profile = VCD_PROFILE_UNKNOWN;
+	enum vcd_codec_level level = VCD_LEVEL_UNKNOWN;
+
+	switch (decoder->codec.codec) {
+	case VCD_CODEC_MPEG4:
+	case VCD_CODEC_XVID:
+		if (profile_codec == VIDC_SM_PROFILE_MPEG4_SIMPLE)
+			profile = VCD_PROFILE_MPEG4_SP;
+		else if (profile_codec == VIDC_SM_PROFILE_MPEG4_ADV_SIMPLE)
+			profile = VCD_PROFILE_MPEG4_ASP;
+		else
+			profile = VCD_PROFILE_UNKNOWN;
+		ddl_get_mpeg4_dec_level(&level, level_codec, profile);
+	break;
+	case VCD_CODEC_H264:
+		if (profile_codec == VIDC_SM_PROFILE_H264_BASELINE)
+			profile = VCD_PROFILE_H264_BASELINE;
+		else if (profile_codec == VIDC_SM_PROFILE_H264_MAIN)
+			profile = VCD_PROFILE_H264_MAIN;
+		else if (profile_codec == VIDC_SM_PROFILE_H264_HIGH)
+			profile = VCD_PROFILE_H264_HIGH;
+		else
+			profile = VCD_PROFILE_UNKNOWN;
+		ddl_get_h264_dec_level(&level, level_codec);
+	break;
+	case VCD_CODEC_H263:
+		if (profile_codec == VIDC_SM_PROFILE_H263_BASELINE)
+			profile = VCD_PROFILE_H263_BASELINE;
+		else
+			profile = VCD_PROFILE_UNKNOWN;
+		ddl_get_h263_dec_level(&level, level_codec);
+	break;
+	case VCD_CODEC_MPEG2:
+		if (profile_codec == VIDC_SM_PROFILE_MPEG2_MAIN)
+			profile = VCD_PROFILE_MPEG2_MAIN;
+		else if (profile_codec == VIDC_SM_PROFILE_MPEG2_SIMPLE)
+			profile = VCD_PROFILE_MPEG2_SIMPLE;
+		else
+			profile = VCD_PROFILE_UNKNOWN;
+		ddl_get_mpeg2_dec_level(&level, level_codec);
+	break;
+	case VCD_CODEC_VC1:
+	case VCD_CODEC_VC1_RCV:
+		if (profile_codec == VIDC_SM_PROFILE_VC1_SIMPLE)
+			profile = VCD_PROFILE_VC1_SIMPLE;
+		else if (profile_codec == VIDC_SM_PROFILE_VC1_MAIN)
+			profile = VCD_PROFILE_VC1_MAIN;
+		else if (profile_codec == VIDC_SM_PROFILE_VC1_ADVANCED)
+			profile = VCD_PROFILE_VC1_ADVANCE;
+		else
+			profile = VCD_PROFILE_UNKNOWN;
+		ddl_get_vc1_dec_level(&level, level_codec, profile);
+	break;
+	default:
+		if (!profile_codec)
+			profile = VCD_PROFILE_UNKNOWN;
+		if (!level)
+			level = VCD_LEVEL_UNKNOWN;
+	break;
+	}
+	decoder->profile.profile = profile;
+	decoder->level.level = level;
+}
+
+static void ddl_handle_enc_frame_done(struct ddl_client_context *ddl,
+					u32 eos_present)
+{
+	struct ddl_context       *ddl_context = ddl->ddl_context;
+	struct ddl_encoder_data  *encoder = &(ddl->codec_data.encoder);
+	struct vcd_frame_data    *output_frame = &(ddl->output_frame.vcd_frm);
+	u32 bottom_frame_tag;
+	u8 *input_buffer_address = NULL;
+
+	vidc_sm_get_frame_tags(&ddl->shared_mem[ddl->command_channel],
+		&output_frame->ip_frm_tag, &bottom_frame_tag);
+	output_frame->data_len = encoder->enc_frame_info.enc_frame_size;
+	output_frame->flags |= VCD_FRAME_FLAG_ENDOFFRAME;
+	(void)ddl_get_encoded_frame(output_frame,
+		encoder->codec.codec, encoder->enc_frame_info.enc_frame
+								);
+	ddl_process_encoder_metadata(ddl);
+	ddl_vidc_encode_dynamic_property(ddl, false);
+	ddl->input_frame.frm_trans_end = false;
+	input_buffer_address = ddl_context->dram_base_a.align_physical_addr +
+			encoder->enc_frame_info.enc_luma_address;
+	ddl_get_input_frame_from_pool(ddl, input_buffer_address);
+	ddl_context->ddl_callback(VCD_EVT_RESP_INPUT_DONE,
+					VCD_S_SUCCESS, &(ddl->input_frame),
+					sizeof(struct ddl_frame_data_tag),
+					(u32 *) ddl, ddl->client_data);
+	ddl->output_frame.frm_trans_end = eos_present ?
+		false : true;
+	ddl_context->ddl_callback(VCD_EVT_RESP_OUTPUT_DONE,
+				VCD_S_SUCCESS, &(ddl->output_frame),
+				sizeof(struct ddl_frame_data_tag),
+				(u32 *) ddl, ddl->client_data);
+}
+
+static void ddl_handle_slice_done_slice_batch(struct ddl_client_context *ddl)
+{
+	struct ddl_context       *ddl_context = ddl->ddl_context;
+	struct ddl_encoder_data  *encoder = &(ddl->codec_data.encoder);
+	struct vcd_frame_data    *output_frame = NULL;
+	u32 bottom_frame_tag;
+	struct vidc_1080p_enc_slice_batch_out_param *slice_output = NULL;
+	u32 num_slices_comp = 0;
+	u32 index = 0;
+	u32 start_bfr_idx = 0;
+	u32 actual_idx = 0;
+
+	DDL_MSG_LOW("%s\n", __func__);
+	vidc_sm_get_num_slices_comp(
+			&ddl->shared_mem[ddl->command_channel],
+			&num_slices_comp);
+	slice_output = (struct vidc_1080p_enc_slice_batch_out_param *)
+		(encoder->batch_frame.slice_batch_out.align_virtual_addr);
+	DDL_MSG_LOW(" after get no of slices = %d\n", num_slices_comp);
+	if (slice_output == NULL)
+		DDL_MSG_ERROR(" slice_output is NULL\n");
+	encoder->slice_delivery_info.num_slices_enc += num_slices_comp;
+	if (vidc_msg_timing) {
+		ddl_calc_core_proc_time_cnt(__func__, ENC_SLICE_OP_TIME,
+					num_slices_comp);
+		ddl_set_core_start_time(__func__, ENC_SLICE_OP_TIME);
+	}
+	DDL_MSG_LOW("%s : Slices Completed %d Total slices %d OutBfrInfo:"
+			"Cmd %d Size %d\n",
+			__func__,
+			num_slices_comp,
+			encoder->slice_delivery_info.num_slices_enc,
+			slice_output->cmd_type,
+			slice_output->output_size);
+	start_bfr_idx = encoder->batch_frame.out_frm_next_frmindex;
+	for (index = 0; index < num_slices_comp; index++) {
+		actual_idx =
+		slice_output->slice_info[start_bfr_idx+index].stream_buffer_idx;
+		DDL_MSG_LOW("Slice Info: OutBfrIndex %d SliceSize %d\n",
+			actual_idx,
+			slice_output->slice_info[start_bfr_idx+index].
+			stream_buffer_size);
+		output_frame = &(
+			encoder->batch_frame.output_frame[actual_idx].vcd_frm);
+		DDL_MSG_LOW("OutBfr: vcd_frm 0x%x frmbfr(virtual) 0x%x"
+			"frmbfr(physical) 0x%x\n",
+			&output_frame,
+			output_frame.virtual_base_addr,
+			output_frame.physical_base_addr);
+		vidc_1080p_get_encode_frame_info(&encoder->enc_frame_info);
+		vidc_sm_get_frame_tags(&ddl->shared_mem
+			[ddl->command_channel],
+			&output_frame->ip_frm_tag, &bottom_frame_tag);
+		ddl_get_encoded_frame(output_frame,
+				encoder->codec.codec,
+				encoder->enc_frame_info.enc_frame);
+		output_frame->data_len =
+			slice_output->slice_info[actual_idx].stream_buffer_size;
+		ddl->output_frame =
+			encoder->batch_frame.output_frame[actual_idx];
+		ddl->output_frame.frm_trans_end = false;
+		ddl_context->ddl_callback(VCD_EVT_RESP_OUTPUT_DONE,
+				VCD_S_SUCCESS, &(ddl->output_frame),
+				sizeof(struct ddl_frame_data_tag),
+				(u32 *) ddl, ddl->client_data);
+		ddl->input_frame.frm_trans_end = false;
+		DDL_MSG_LOW("%s Set i/p o/p transactions to false\n", __func__);
+	}
+	encoder->batch_frame.out_frm_next_frmindex = start_bfr_idx + index;
+	ddl->cmd_state = DDL_CMD_ENCODE_FRAME;
+	vidc_sm_set_encoder_slice_batch_int_ctrl(
+			&ddl->shared_mem[ddl->command_channel],
+			0);
+}
+
+static void ddl_handle_enc_frame_done_slice_mode(
+		struct ddl_client_context *ddl, u32 eos_present)
+{
+	struct ddl_context       *ddl_context = ddl->ddl_context;
+	struct ddl_encoder_data  *encoder = &(ddl->codec_data.encoder);
+	struct vcd_frame_data    *output_frame = NULL;
+	u32 bottom_frame_tag;
+	u8 *input_buffer_address = NULL;
+	struct vidc_1080p_enc_slice_batch_out_param *slice_output = NULL;
+	u32 num_slices_comp = 0;
+	u32 index = 0;
+	u32 start_bfr_idx = 0;
+	u32 actual_idx = 0;
+	struct vcd_transc *transc;
+
+	DDL_MSG_LOW("%s\n", __func__);
+	vidc_sm_get_num_slices_comp(
+				&ddl->shared_mem[ddl->command_channel],
+				&num_slices_comp);
+	slice_output = (struct vidc_1080p_enc_slice_batch_out_param *)
+		encoder->batch_frame.slice_batch_out.align_virtual_addr;
+	encoder->slice_delivery_info.num_slices_enc += num_slices_comp;
+	if (vidc_msg_timing) {
+		ddl_calc_core_proc_time_cnt(__func__, ENC_OP_TIME,
+					num_slices_comp);
+	}
+	DDL_MSG_LOW("%s Slices Completed %d Total slices done = %d"
+		" OutBfrInfo: Cmd %d Size %d",
+		__func__,
+		num_slices_comp,
+		encoder->slice_delivery_info.num_slices_enc,
+		slice_output->cmd_type,
+		slice_output->output_size);
+	start_bfr_idx = encoder->batch_frame.out_frm_next_frmindex;
+	if ((encoder->slice_delivery_info.num_slices_enc %
+		encoder->batch_frame.num_output_frames) != 0) {
+		DDL_MSG_ERROR("ERROR : %d %d\n",
+		encoder->slice_delivery_info.num_slices_enc,
+		encoder->batch_frame.num_output_frames);
+	}
+	for (index = 0; index < num_slices_comp; index++) {
+		actual_idx =
+			slice_output->slice_info[start_bfr_idx+index]. \
+			stream_buffer_idx;
+		DDL_MSG_LOW("Slice Info: OutBfrIndex %d SliceSize %d",
+			actual_idx,
+			slice_output->slice_info[start_bfr_idx+index]. \
+			stream_buffer_size, 0);
+		output_frame =
+		&(encoder->batch_frame.output_frame[actual_idx].vcd_frm);
+		DDL_MSG_LOW("OutBfr: vcd_frm 0x%x frmbfr(virtual) 0x%x"
+				"frmbfr(physical) 0x%x",
+				&output_frame,
+				output_frame.virtual_base_addr,
+				output_frame.physical_base_addr);
+		vidc_1080p_get_encode_frame_info(
+			&encoder->enc_frame_info);
+		vidc_sm_get_frame_tags(&ddl->shared_mem
+			[ddl->command_channel],
+			&output_frame->ip_frm_tag, &bottom_frame_tag);
+		ddl_get_encoded_frame(output_frame,
+				encoder->codec.codec,
+				encoder->enc_frame_info.enc_frame);
+		output_frame->data_len =
+			slice_output->slice_info[actual_idx].stream_buffer_size;
+		ddl->output_frame =
+			encoder->batch_frame.output_frame[actual_idx];
+		DDL_MSG_LOW(" %s actual_idx = %d"
+		"encoder->batch_frame.num_output_frames = %d\n", __func__,
+		actual_idx, encoder->batch_frame.num_output_frames);
+		if (encoder->batch_frame.num_output_frames == (actual_idx+1)) {
+			output_frame->flags |= VCD_FRAME_FLAG_ENDOFFRAME;
+			ddl_vidc_encode_dynamic_property(ddl, false);
+			ddl->input_frame.frm_trans_end = true;
+			DDL_MSG_LOW("%s End of frame detected\n", __func__);
+			input_buffer_address =
+				ddl_context->dram_base_a.align_physical_addr +
+				encoder->enc_frame_info.enc_luma_address;
+			ddl_get_input_frame_from_pool(ddl,
+				input_buffer_address);
+			transc = (struct vcd_transc *)(ddl->client_data);
+			if (eos_present)
+				ddl->output_frame.frm_trans_end = false;
+			else
+				ddl->output_frame.frm_trans_end = true;
+		}
+		DDL_MSG_LOW("%s Before output done cb\n", __func__);
+		transc = (struct vcd_transc *)(ddl->client_data);
+		ddl->output_frame.vcd_frm = *output_frame;
+		ddl_context->ddl_callback(VCD_EVT_RESP_OUTPUT_DONE,
+				VCD_S_SUCCESS, &(ddl->output_frame),
+				sizeof(struct ddl_frame_data_tag),
+				(u32 *) ddl, ddl->client_data);
+	}
+	if (encoder->batch_frame.num_output_frames == (actual_idx+1)) {
+		DDL_MSG_LOW("%s sending input done\n", __func__);
+		ddl_context->ddl_callback(VCD_EVT_RESP_INPUT_DONE,
+				VCD_S_SUCCESS, &(ddl->input_frame),
+				sizeof(struct ddl_frame_data_tag),
+				(u32 *) ddl, ddl->client_data);
+	}
+}
+
+static void ddl_handle_enc_skipframe_slice_mode(
+		struct ddl_client_context *ddl, u32 eos_present)
+{
+	struct ddl_context       *ddl_context = ddl->ddl_context;
+	struct ddl_encoder_data  *encoder = &(ddl->codec_data.encoder);
+	struct vcd_frame_data    *output_frame = NULL;
+	u32 bottom_frame_tag;
+	u8 *input_buffer_address = NULL;
+	u32 index = 0;
+	DDL_MSG_HIGH("ddl_handle_enc_skipframe_slice_mode: frame skipped");
+	vidc_sm_set_encoder_slice_batch_int_ctrl(
+			&ddl->shared_mem[ddl->command_channel],
+			1);
+	for (index = 0;
+		index < encoder->batch_frame.num_output_frames;
+		index++) {
+		output_frame =
+			&(encoder->batch_frame.output_frame[index].vcd_frm);
+		DDL_MSG_MED("output buffer: vcd_frm = 0x%x "
+				"frmbfr(virtual) = 0x%x frmbfr(physical) = 0x%x",
+				(u32)output_frame, (u32)output_frame->virtual,
+				(u32)output_frame->physical);
+		vidc_sm_get_frame_tags(
+				&ddl->shared_mem[ddl->command_channel],
+				&output_frame->ip_frm_tag,
+				&bottom_frame_tag);
+		ddl_get_encoded_frame(
+				output_frame,
+				encoder->codec.codec,
+				encoder->enc_frame_info.enc_frame);
+		output_frame->data_len = 0;
+		ddl->output_frame.frm_trans_end = false;
+		if (encoder->batch_frame.num_output_frames ==
+			(index + 1)) {
+			DDL_MSG_MED("last output bfr for skip frame, set EOF");
+			output_frame->flags |= VCD_FRAME_FLAG_ENDOFFRAME;
+			ddl_vidc_encode_dynamic_property(ddl, false);
+			if (eos_present)
+				ddl->output_frame.frm_trans_end = false;
+			else
+				ddl->output_frame.frm_trans_end = true;
+		}
+		ddl->output_frame.vcd_frm = *output_frame;
+		ddl_context->ddl_callback(
+				VCD_EVT_RESP_OUTPUT_DONE,
+				VCD_S_SUCCESS,
+				&(ddl->output_frame),
+				sizeof(struct ddl_frame_data_tag),
+				(u32 *)ddl,
+				ddl->client_data);
+
+		if (encoder->batch_frame.num_output_frames ==
+			(index + 1)) {
+			ddl->input_frame.frm_trans_end = false;
+			input_buffer_address =
+				ddl_context->dram_base_a.physical_base_addr +
+				(encoder->enc_frame_info.enc_luma_address);
+			ddl_get_input_frame_from_pool(ddl,
+					input_buffer_address);
+			DDL_MSG_MED("InpBfr: vcd_frm 0x%x frmbfr(virtual) 0x%x"
+					" frmbfr(physical) 0x%x",
+					(u32)&(ddl->input_frame.vcd_frm),
+					(u32)ddl->input_frame.vcd_frm.virtual,
+					(u32)ddl->input_frame.vcd_frm.physical);
+			ddl_context->ddl_callback(
+					VCD_EVT_RESP_INPUT_DONE,
+					VCD_S_SUCCESS,
+					&(ddl->input_frame),
+					sizeof(struct ddl_frame_data_tag),
+					(u32 *)ddl,
+					ddl->client_data);
+		}
+	}
+}
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_metadata.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_metadata.c
new file mode 100644
index 0000000..267e924
--- /dev/null
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_metadata.c
@@ -0,0 +1,508 @@
+/* Copyright (c) 2010, 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "vcd_ddl.h"
+#include "vcd_ddl_shared_mem.h"
+#include "vcd_ddl_metadata.h"
+
+static u32 *ddl_metadata_hdr_entry(struct ddl_client_context *ddl,
+	u32 meta_data)
+{
+	u32 skip_words = 0;
+	u32 *buffer;
+
+	if (ddl->decoding) {
+		buffer = (u32 *) ddl->codec_data.decoder.meta_data_input.
+			align_virtual_addr;
+		skip_words = 32 + 1;
+		buffer += skip_words;
+		switch (meta_data) {
+		default:
+		case VCD_METADATA_DATANONE:
+			skip_words = 0;
+		break;
+		case VCD_METADATA_QPARRAY:
+			skip_words = 3;
+		break;
+		case VCD_METADATA_CONCEALMB:
+			skip_words = 6;
+		break;
+		case VCD_METADATA_VC1:
+			skip_words = 9;
+		break;
+		case VCD_METADATA_SEI:
+			skip_words = 12;
+		break;
+		case VCD_METADATA_VUI:
+			skip_words = 15;
+		break;
+		case VCD_METADATA_PASSTHROUGH:
+			skip_words = 18;
+		break;
+		case VCD_METADATA_QCOMFILLER:
+			skip_words = 21;
+		break;
+		}
+	} else {
+		buffer = (u32 *) ddl->codec_data.encoder.meta_data_input.
+				align_virtual_addr;
+		skip_words = 2;
+		buffer += skip_words;
+		switch (meta_data) {
+		default:
+		case VCD_METADATA_DATANONE:
+			skip_words = 0;
+		break;
+		case VCD_METADATA_ENC_SLICE:
+			skip_words = 3;
+		break;
+		case VCD_METADATA_QCOMFILLER:
+			skip_words = 6;
+		break;
+		}
+	}
+	buffer += skip_words;
+	return buffer;
+}
+
+void ddl_set_default_meta_data_hdr(struct ddl_client_context *ddl)
+{
+	struct ddl_buf_addr *main_buffer =
+		&ddl->ddl_context->metadata_shared_input;
+	struct ddl_buf_addr *client_buffer;
+	u32 *hdr_entry;
+
+	if (ddl->decoding)
+		client_buffer = &(ddl->codec_data.decoder.meta_data_input);
+	else
+		client_buffer = &(ddl->codec_data.encoder.meta_data_input);
+	DDL_METADATA_CLIENT_INPUTBUF(main_buffer, client_buffer,
+		ddl->instance_id);
+	hdr_entry = ddl_metadata_hdr_entry(ddl, VCD_METADATA_QCOMFILLER);
+	hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 0x00000101;
+	hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 1;
+	hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_QCOMFILLER;
+	hdr_entry = ddl_metadata_hdr_entry(ddl, VCD_METADATA_DATANONE);
+	hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 0x00000101;
+	hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 1;
+	hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_DATANONE;
+	if (ddl->decoding) {
+		hdr_entry = ddl_metadata_hdr_entry(ddl, VCD_METADATA_QPARRAY);
+		hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 0x00000101;
+		hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 1;
+		hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_QPARRAY;
+		hdr_entry = ddl_metadata_hdr_entry(ddl,	VCD_METADATA_CONCEALMB);
+		hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 0x00000101;
+		hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 1;
+		hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_CONCEALMB;
+		hdr_entry = ddl_metadata_hdr_entry(ddl, VCD_METADATA_SEI);
+		hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 0x00000101;
+		hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 1;
+		hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_SEI;
+		hdr_entry = ddl_metadata_hdr_entry(ddl, VCD_METADATA_VUI);
+		hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 0x00000101;
+		hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 1;
+		hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_VUI;
+		hdr_entry = ddl_metadata_hdr_entry(ddl, VCD_METADATA_VC1);
+		hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 0x00000101;
+		hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 1;
+		hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_VC1;
+		hdr_entry = ddl_metadata_hdr_entry(ddl,
+			VCD_METADATA_PASSTHROUGH);
+		hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 0x00000101;
+		hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 1;
+		hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] =
+			VCD_METADATA_PASSTHROUGH;
+	} else {
+		hdr_entry = ddl_metadata_hdr_entry(ddl, VCD_METADATA_ENC_SLICE);
+		hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 0x00000101;
+		hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 1;
+		hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_ENC_SLICE;
+	}
+}
+
+static u32 ddl_supported_metadata_flag(struct ddl_client_context *ddl)
+{
+	u32 flag = 0;
+
+	if (ddl->decoding) {
+		enum vcd_codec codec =
+			ddl->codec_data.decoder.codec.codec;
+
+		flag |= (VCD_METADATA_CONCEALMB | VCD_METADATA_PASSTHROUGH |
+				VCD_METADATA_QPARRAY);
+		if (codec == VCD_CODEC_H264)
+			flag |= (VCD_METADATA_SEI | VCD_METADATA_VUI);
+		else if (codec == VCD_CODEC_VC1 ||
+			codec == VCD_CODEC_VC1_RCV)
+			flag |= VCD_METADATA_VC1;
+	} else
+		flag |= VCD_METADATA_ENC_SLICE;
+	return flag;
+}
+
+void ddl_set_default_metadata_flag(struct ddl_client_context *ddl)
+{
+	if (ddl->decoding)
+		ddl->codec_data.decoder.meta_data_enable_flag = 0;
+	else
+		ddl->codec_data.encoder.meta_data_enable_flag = 0;
+}
+
+void ddl_set_default_decoder_metadata_buffer_size(struct ddl_decoder_data
+	*decoder, struct vcd_property_frame_size *frame_size,
+	struct vcd_buffer_requirement *output_buf_req)
+{
+	u32 flag = decoder->meta_data_enable_flag;
+	u32 suffix = 0, size = 0;
+
+	if (!flag) {
+		decoder->suffix = 0;
+		return;
+	}
+	if (flag & VCD_METADATA_QPARRAY) {
+		u32 num_of_mb = DDL_NO_OF_MB(frame_size->width,
+			frame_size->height);
+
+		size = DDL_METADATA_HDR_SIZE;
+		size += num_of_mb;
+		DDL_METADATA_ALIGNSIZE(size);
+		suffix += size;
+	}
+	if (flag & VCD_METADATA_CONCEALMB) {
+		u32 num_of_mb = DDL_NO_OF_MB(frame_size->width,
+			frame_size->height);
+		size = DDL_METADATA_HDR_SIZE + (num_of_mb >> 3);
+		DDL_METADATA_ALIGNSIZE(size);
+		suffix += size;
+	}
+	if (flag & VCD_METADATA_VC1) {
+		size = DDL_METADATA_HDR_SIZE;
+		size += DDL_METADATA_VC1_PAYLOAD_SIZE;
+		DDL_METADATA_ALIGNSIZE(size);
+		suffix += size;
+	}
+	if (flag & VCD_METADATA_SEI) {
+		size = DDL_METADATA_HDR_SIZE;
+		size += DDL_METADATA_SEI_PAYLOAD_SIZE;
+		DDL_METADATA_ALIGNSIZE(size);
+		suffix += (size * DDL_METADATA_SEI_MAX);
+	}
+	if (flag & VCD_METADATA_VUI) {
+		size = DDL_METADATA_HDR_SIZE;
+		size += DDL_METADATA_VUI_PAYLOAD_SIZE;
+		DDL_METADATA_ALIGNSIZE(size);
+		suffix += (size);
+	}
+	if (flag & VCD_METADATA_PASSTHROUGH) {
+		size = DDL_METADATA_HDR_SIZE;
+		size += DDL_METADATA_PASSTHROUGH_PAYLOAD_SIZE;
+		DDL_METADATA_ALIGNSIZE(size);
+		suffix += (size);
+	}
+	size = DDL_METADATA_EXTRADATANONE_SIZE;
+	DDL_METADATA_ALIGNSIZE(size);
+	suffix += (size);
+	suffix += DDL_METADATA_EXTRAPAD_SIZE;
+	DDL_METADATA_ALIGNSIZE(suffix);
+	decoder->suffix = suffix;
+	output_buf_req->sz += suffix;
+	DDL_MSG_LOW("metadata output buf size : %d", suffix);
+}
+
+void ddl_set_default_encoder_metadata_buffer_size(struct ddl_encoder_data
+	*encoder)
+{
+	u32 flag = encoder->meta_data_enable_flag;
+	u32 suffix = 0, size = 0;
+
+	if (!flag) {
+		encoder->suffix = 0;
+		return;
+	}
+	if (flag & VCD_METADATA_ENC_SLICE) {
+		u32 num_of_mb = DDL_NO_OF_MB(encoder->frame_size.width,
+			encoder->frame_size.height);
+		size = DDL_METADATA_HDR_SIZE;
+		size += 4;
+		size += (num_of_mb << 3);
+		DDL_METADATA_ALIGNSIZE(size);
+		suffix += size;
+	}
+	size = DDL_METADATA_EXTRADATANONE_SIZE;
+	DDL_METADATA_ALIGNSIZE(size);
+	suffix += (size);
+	suffix += DDL_METADATA_EXTRAPAD_SIZE;
+	DDL_METADATA_ALIGNSIZE(suffix);
+	encoder->suffix = suffix;
+	encoder->output_buf_req.sz += suffix;
+	encoder->output_buf_req.sz =
+		DDL_ALIGN(encoder->output_buf_req.sz, DDL_KILO_BYTE(4));
+}
+
+u32 ddl_set_metadata_params(struct ddl_client_context *ddl,
+	struct vcd_property_hdr *property_hdr, void *property_value)
+{
+	u32  vcd_status = VCD_ERR_ILLEGAL_PARM;
+	if (property_hdr->prop_id == VCD_I_METADATA_ENABLE) {
+		struct vcd_property_meta_data_enable *meta_data_enable =
+			(struct vcd_property_meta_data_enable *) property_value;
+		u32 *meta_data_enable_flag;
+		enum vcd_codec codec;
+
+		if (ddl->decoding) {
+			meta_data_enable_flag =
+			&(ddl->codec_data.decoder.meta_data_enable_flag);
+			codec = ddl->codec_data.decoder.codec.codec;
+		} else {
+			meta_data_enable_flag =
+				&ddl->codec_data.encoder.meta_data_enable_flag;
+			codec = ddl->codec_data.encoder.codec.codec;
+		}
+		if (sizeof(struct vcd_property_meta_data_enable) ==
+			property_hdr->sz &&
+			DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN) && codec) {
+			u32 flag = ddl_supported_metadata_flag(ddl);
+			flag &= (meta_data_enable->meta_data_enable_flag);
+			if (flag)
+				flag |= DDL_METADATA_MANDATORY;
+			if (*meta_data_enable_flag != flag) {
+				*meta_data_enable_flag = flag;
+				if (ddl->decoding)
+					ddl_set_default_decoder_buffer_req(
+						&ddl->codec_data.decoder, true);
+				else
+					ddl_set_default_encoder_buffer_req(
+						&ddl->codec_data.encoder);
+			}
+			vcd_status = VCD_S_SUCCESS;
+		}
+	} else if (property_hdr->prop_id == VCD_I_METADATA_HEADER) {
+		struct vcd_property_metadata_hdr *hdr =
+			(struct vcd_property_metadata_hdr *) property_value;
+
+		if (sizeof(struct vcd_property_metadata_hdr) ==
+			property_hdr->sz) {
+			u32 flag = ddl_supported_metadata_flag(ddl);
+
+			flag |= DDL_METADATA_MANDATORY;
+			flag &= hdr->meta_data_id;
+			if (!(flag & (flag - 1))) {
+				u32 *hdr_entry = ddl_metadata_hdr_entry(ddl,
+					flag);
+				hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] =
+					hdr->version;
+				hdr_entry[DDL_METADATA_HDR_PORT_INDEX] =
+					hdr->port_index;
+				hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] =
+					hdr->type;
+				vcd_status = VCD_S_SUCCESS;
+			}
+		}
+	}
+	return vcd_status;
+}
+
+u32 ddl_get_metadata_params(struct ddl_client_context *ddl,
+	struct vcd_property_hdr *property_hdr, void *property_value)
+{
+	u32 vcd_status = VCD_ERR_ILLEGAL_PARM;
+	if (property_hdr->prop_id == VCD_I_METADATA_ENABLE &&
+		sizeof(struct vcd_property_meta_data_enable) ==
+		property_hdr->sz) {
+		struct vcd_property_meta_data_enable *meta_data_enable =
+			(struct vcd_property_meta_data_enable *) property_value;
+
+		meta_data_enable->meta_data_enable_flag =
+			((ddl->decoding) ?
+			(ddl->codec_data.decoder.meta_data_enable_flag) :
+			(ddl->codec_data.encoder.meta_data_enable_flag));
+		vcd_status = VCD_S_SUCCESS;
+	} else if (property_hdr->prop_id == VCD_I_METADATA_HEADER &&
+		sizeof(struct vcd_property_metadata_hdr) ==
+		property_hdr->sz) {
+		struct vcd_property_metadata_hdr *hdr =
+			(struct vcd_property_metadata_hdr *) property_value;
+		u32 flag = ddl_supported_metadata_flag(ddl);
+
+		flag |= DDL_METADATA_MANDATORY;
+		flag &= hdr->meta_data_id;
+		if (!(flag & (flag - 1))) {
+			u32 *hdr_entry = ddl_metadata_hdr_entry(ddl, flag);
+			hdr->version =
+				hdr_entry[DDL_METADATA_HDR_VERSION_INDEX];
+			hdr->port_index =
+				hdr_entry[DDL_METADATA_HDR_PORT_INDEX];
+			hdr->type = hdr_entry[DDL_METADATA_HDR_TYPE_INDEX];
+			vcd_status = VCD_S_SUCCESS;
+		}
+	}
+	return vcd_status;
+}
+
+void ddl_vidc_metadata_enable(struct ddl_client_context *ddl)
+{
+	u32 flag, extradata_enable = false;
+	u32 qp_enable = false, concealed_mb_enable = false;
+	u32 vc1_param_enable = false, sei_nal_enable = false;
+	u32 vui_enable = false, enc_slice_size_enable = false;
+
+	if (ddl->decoding)
+		flag = ddl->codec_data.decoder.meta_data_enable_flag;
+	else
+		flag = ddl->codec_data.encoder.meta_data_enable_flag;
+	if (flag) {
+		if (flag & VCD_METADATA_QPARRAY)
+			qp_enable = true;
+		if (flag & VCD_METADATA_CONCEALMB)
+			concealed_mb_enable = true;
+		if (flag & VCD_METADATA_VC1)
+			vc1_param_enable = true;
+		if (flag & VCD_METADATA_SEI)
+			sei_nal_enable = true;
+		if (flag & VCD_METADATA_VUI)
+			vui_enable = true;
+		if (flag & VCD_METADATA_ENC_SLICE)
+			enc_slice_size_enable = true;
+		if (flag & VCD_METADATA_PASSTHROUGH)
+			extradata_enable = true;
+	}
+
+	DDL_MSG_LOW("metadata enable flag : %d", sei_nal_enable);
+	vidc_sm_set_metadata_enable(&ddl->shared_mem
+		[ddl->command_channel], extradata_enable, qp_enable,
+		concealed_mb_enable, vc1_param_enable, sei_nal_enable,
+		vui_enable, enc_slice_size_enable);
+}
+
+u32 ddl_vidc_encode_set_metadata_output_buf(struct ddl_client_context *ddl)
+{
+	struct ddl_encoder_data *encoder = &ddl->codec_data.encoder;
+	struct vcd_frame_data *stream = &ddl->output_frame.vcd_frm;
+	struct ddl_context *ddl_context;
+	u32 ext_buffer_end, hw_metadata_start;
+	u32 *buffer;
+
+	ddl_context = ddl_get_context();
+	ext_buffer_end = (u32) stream->physical + stream->alloc_len;
+	if (!encoder->meta_data_enable_flag) {
+		ext_buffer_end &= ~(DDL_STREAMBUF_ALIGN_GUARD_BYTES);
+		return ext_buffer_end;
+	}
+	hw_metadata_start = (ext_buffer_end - encoder->suffix) &
+		~(DDL_STREAMBUF_ALIGN_GUARD_BYTES);
+	ext_buffer_end = (hw_metadata_start - 1) &
+		~(DDL_STREAMBUF_ALIGN_GUARD_BYTES);
+	buffer = (u32 *) encoder->meta_data_input.align_virtual_addr;
+	*buffer++ = encoder->suffix;
+	*buffer  = DDL_OFFSET(ddl_context->dram_base_a.align_physical_addr,
+		hw_metadata_start);
+	encoder->meta_data_offset = hw_metadata_start - (u32) stream->physical;
+	return ext_buffer_end;
+}
+
+void ddl_vidc_decode_set_metadata_output(struct ddl_decoder_data *decoder)
+{
+	struct ddl_context *ddl_context;
+	u32 loopc, yuv_size;
+	u32 *buffer;
+
+	if (!decoder->meta_data_enable_flag) {
+		decoder->meta_data_offset = 0;
+		return;
+	}
+	ddl_context = ddl_get_context();
+	yuv_size = ddl_get_yuv_buffer_size(&decoder->client_frame_size,
+		&decoder->buf_format, !decoder->progressive_only,
+		decoder->hdr.decoding, NULL);
+	decoder->meta_data_offset = DDL_ALIGN_SIZE(yuv_size,
+		DDL_LINEAR_BUF_ALIGN_GUARD_BYTES, DDL_LINEAR_BUF_ALIGN_MASK);
+	buffer = (u32 *) decoder->meta_data_input.align_virtual_addr;
+	*buffer++ = decoder->suffix;
+	DDL_MSG_LOW("Metadata offset & size : %d/%d",
+		decoder->meta_data_offset, decoder->suffix);
+	for (loopc = 0; loopc < decoder->dp_buf.no_of_dec_pic_buf;
+		++loopc) {
+		*buffer++ = (u32)(decoder->meta_data_offset + (u8 *)
+			DDL_OFFSET(ddl_context->dram_base_a.
+			align_physical_addr, decoder->dp_buf.
+			dec_pic_buffers[loopc].vcd_frm.physical));
+	}
+}
+
+void ddl_process_encoder_metadata(struct ddl_client_context *ddl)
+{
+	struct ddl_encoder_data *encoder = &(ddl->codec_data.encoder);
+	struct vcd_frame_data *out_frame =
+		&(ddl->output_frame.vcd_frm);
+	u32 *qfiller_hdr, *qfiller, start_addr;
+	u32 qfiller_size;
+	if (!encoder->meta_data_enable_flag) {
+		out_frame->flags &= ~(VCD_FRAME_FLAG_EXTRADATA);
+		return;
+	}
+	if (!encoder->enc_frame_info.meta_data_exists) {
+		out_frame->flags &= ~(VCD_FRAME_FLAG_EXTRADATA);
+		return;
+	}
+	out_frame->flags |= VCD_FRAME_FLAG_EXTRADATA;
+	DDL_MSG_LOW("processing metadata for encoder");
+	start_addr = (u32) ((u8 *)out_frame->virtual + out_frame->offset);
+	qfiller = (u32 *)((out_frame->data_len +
+				start_addr + 3) & ~3);
+	qfiller_size = (u32)((encoder->meta_data_offset +
+		(u8 *) out_frame->virtual) - (u8 *) qfiller);
+	qfiller_hdr = ddl_metadata_hdr_entry(ddl, VCD_METADATA_QCOMFILLER);
+	*qfiller++ = qfiller_size;
+	*qfiller++ = qfiller_hdr[DDL_METADATA_HDR_VERSION_INDEX];
+	*qfiller++ = qfiller_hdr[DDL_METADATA_HDR_PORT_INDEX];
+	*qfiller++ = qfiller_hdr[DDL_METADATA_HDR_TYPE_INDEX];
+	*qfiller = (u32)(qfiller_size - DDL_METADATA_HDR_SIZE);
+}
+
+void ddl_process_decoder_metadata(struct ddl_client_context *ddl)
+{
+	struct ddl_decoder_data *decoder = &(ddl->codec_data.decoder);
+	struct vcd_frame_data *output_frame =
+		&(ddl->output_frame.vcd_frm);
+	u32 *qfiller_hdr, *qfiller;
+	u32 qfiller_size;
+
+	if (!decoder->meta_data_enable_flag) {
+		output_frame->flags &= ~(VCD_FRAME_FLAG_EXTRADATA);
+		return;
+	}
+	if (!decoder->meta_data_exists) {
+		output_frame->flags &= ~(VCD_FRAME_FLAG_EXTRADATA);
+		return;
+	}
+	DDL_MSG_LOW("processing metadata for decoder");
+	DDL_MSG_LOW("data_len/metadata_offset : %d/%d",
+		output_frame->data_len, decoder->meta_data_offset);
+	output_frame->flags |= VCD_FRAME_FLAG_EXTRADATA;
+	if (output_frame->data_len != decoder->meta_data_offset) {
+		qfiller = (u32 *)((u32)((output_frame->data_len +
+			output_frame->offset  +
+				(u8 *) output_frame->virtual) + 3) & ~3);
+		qfiller_size = (u32)((decoder->meta_data_offset +
+				(u8 *) output_frame->virtual) -
+				(u8 *) qfiller);
+		qfiller_hdr = ddl_metadata_hdr_entry(ddl,
+				VCD_METADATA_QCOMFILLER);
+		*qfiller++ = qfiller_size;
+		*qfiller++ = qfiller_hdr[DDL_METADATA_HDR_VERSION_INDEX];
+		*qfiller++ = qfiller_hdr[DDL_METADATA_HDR_PORT_INDEX];
+		*qfiller++ = qfiller_hdr[DDL_METADATA_HDR_TYPE_INDEX];
+		*qfiller = (u32)(qfiller_size - DDL_METADATA_HDR_SIZE);
+	}
+}
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_metadata.h b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_metadata.h
new file mode 100644
index 0000000..c63b6a9
--- /dev/null
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_metadata.h
@@ -0,0 +1,66 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VCD_DDL_METADATA_H_
+#define _VCD_DDL_METADATA_H_
+
+#define DDL_MAX_DEC_METADATATYPE          8
+#define DDL_MAX_ENC_METADATATYPE          3
+#define DDL_METADATA_EXTRAPAD_SIZE      256
+#define DDL_METADATA_HDR_SIZE            20
+#define DDL_METADATA_EXTRADATANONE_SIZE  24
+#define DDL_METADATA_ALIGNSIZE(x) ((x) = (((x) + 0x7) & ~0x7))
+#define DDL_METADATA_MANDATORY \
+	(VCD_METADATA_DATANONE | VCD_METADATA_QCOMFILLER)
+#define DDL_METADATA_VC1_PAYLOAD_SIZE         (38*4)
+#define DDL_METADATA_SEI_PAYLOAD_SIZE          100
+#define DDL_METADATA_SEI_MAX                     5
+#define DDL_METADATA_VUI_PAYLOAD_SIZE          256
+#define DDL_METADATA_PASSTHROUGH_PAYLOAD_SIZE   68
+#define DDL_METADATA_CLIENT_INPUTBUFSIZE       256
+#define DDL_METADATA_TOTAL_INPUTBUFSIZE \
+	(DDL_METADATA_CLIENT_INPUTBUFSIZE * VCD_MAX_NO_CLIENT)
+
+#define DDL_METADATA_CLIENT_INPUTBUF(main_buffer, client_buffer,\
+	channel_id) { \
+	(client_buffer)->align_physical_addr = (u8 *) \
+	((u8 *)(main_buffer)->align_physical_addr + \
+	(DDL_METADATA_CLIENT_INPUTBUFSIZE * channel_id)); \
+	(client_buffer)->align_virtual_addr = (u8 *) \
+	((u8 *)(main_buffer)->align_virtual_addr + \
+	(DDL_METADATA_CLIENT_INPUTBUFSIZE * channel_id)); \
+	(client_buffer)->virtual_base_addr = 0;	\
+	}
+
+#define DDL_METADATA_HDR_VERSION_INDEX 0
+#define DDL_METADATA_HDR_PORT_INDEX    1
+#define DDL_METADATA_HDR_TYPE_INDEX    2
+
+void ddl_set_default_meta_data_hdr(struct ddl_client_context *ddl);
+u32 ddl_get_metadata_params(struct ddl_client_context *ddl,
+	struct vcd_property_hdr *property_hdr, void *property_value);
+u32 ddl_set_metadata_params(struct ddl_client_context *ddl,
+	struct vcd_property_hdr *property_hdr, void *property_value);
+void ddl_set_default_metadata_flag(struct ddl_client_context *ddl);
+void ddl_set_default_decoder_metadata_buffer_size(struct ddl_decoder_data
+	*decoder, struct vcd_property_frame_size *frame_size,
+	struct vcd_buffer_requirement *output_buf_req);
+void ddl_set_default_encoder_metadata_buffer_size(
+	struct ddl_encoder_data *encoder);
+void ddl_vidc_metadata_enable(struct ddl_client_context *ddl);
+u32 ddl_vidc_encode_set_metadata_output_buf(struct ddl_client_context *ddl);
+void ddl_vidc_decode_set_metadata_output(struct ddl_decoder_data *decoder);
+void ddl_process_encoder_metadata(struct ddl_client_context *ddl);
+void ddl_process_decoder_metadata(struct ddl_client_context *ddl);
+
+#endif
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_properties.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_properties.c
new file mode 100644
index 0000000..3827bc1
--- /dev/null
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_properties.c
@@ -0,0 +1,2113 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "vcd_ddl.h"
+#include "vcd_ddl_metadata.h"
+#include "vcd_res_tracker_api.h"
+
+static u32 ddl_set_dec_property(struct ddl_client_context *pddl,
+	struct vcd_property_hdr *property_hdr, void *property_value);
+static u32 ddl_set_enc_property(struct ddl_client_context *pddl,
+	struct vcd_property_hdr *property_hdr, void *property_value);
+static u32 ddl_get_dec_property(struct ddl_client_context *pddl,
+	struct vcd_property_hdr *property_hdr, void *property_value);
+static u32 ddl_get_enc_property(struct ddl_client_context *pddl,
+	struct vcd_property_hdr *property_hdr, void *property_value);
+static u32 ddl_set_enc_dynamic_property(struct ddl_client_context *ddl,
+	struct vcd_property_hdr *property_hdr, void *property_value);
+static void ddl_set_default_enc_property(struct ddl_client_context *ddl);
+static void ddl_set_default_enc_profile(
+	struct ddl_encoder_data *encoder);
+static void ddl_set_default_enc_level(struct ddl_encoder_data *encoder);
+static void ddl_set_default_enc_vop_timing(
+	struct ddl_encoder_data *encoder);
+static void ddl_set_default_enc_intra_period(
+	struct ddl_encoder_data *encoder);
+static void ddl_set_default_enc_rc_params(
+	struct ddl_encoder_data *encoder);
+static u32 ddl_valid_buffer_requirement(
+	struct vcd_buffer_requirement *original_buf_req,
+	struct vcd_buffer_requirement *req_buf_req);
+static u32 ddl_decoder_min_num_dpb(struct ddl_decoder_data *decoder);
+static u32 ddl_set_dec_buffers(struct ddl_decoder_data *decoder,
+	struct ddl_property_dec_pic_buffers *dpb);
+
+u32 ddl_set_property(u32 *ddl_handle,
+	struct vcd_property_hdr *property_hdr, void *property_value)
+{
+	struct ddl_context *ddl_context;
+	struct ddl_client_context *ddl =
+		(struct ddl_client_context *) ddl_handle;
+	u32 vcd_status;
+
+	DDL_MSG_HIGH("ddl_set_property");
+	if (!property_hdr || !property_value) {
+		DDL_MSG_ERROR("ddl_set_prop:Bad_argument");
+		return VCD_ERR_ILLEGAL_PARM;
+	}
+	ddl_context = ddl_get_context();
+	if (!DDL_IS_INITIALIZED(ddl_context)) {
+		DDL_MSG_ERROR("ddl_set_prop:Not_inited");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	if (!ddl) {
+		DDL_MSG_ERROR("ddl_set_prop:Bad_handle");
+		return VCD_ERR_BAD_HANDLE;
+	}
+	if (ddl->decoding)
+		vcd_status = ddl_set_dec_property(ddl, property_hdr,
+				property_value);
+	else
+		vcd_status = ddl_set_enc_property(ddl, property_hdr,
+				property_value);
+	if (vcd_status)
+		DDL_MSG_ERROR("ddl_set_prop:FAILED");
+	return vcd_status;
+}
+
+u32 ddl_get_property(u32 *ddl_handle,
+	struct vcd_property_hdr *property_hdr, void *property_value)
+{
+	struct ddl_context *ddl_context;
+	struct ddl_client_context *ddl =
+		(struct ddl_client_context *) ddl_handle;
+	u32 vcd_status = VCD_ERR_ILLEGAL_PARM;
+
+	DDL_MSG_HIGH("ddl_get_property");
+	if (!property_hdr || !property_value)
+		return VCD_ERR_ILLEGAL_PARM;
+	if (property_hdr->prop_id == DDL_I_CAPABILITY) {
+		if (sizeof(struct ddl_property_capability) ==
+			property_hdr->sz) {
+			struct ddl_property_capability *ddl_capability =
+				(struct ddl_property_capability *)
+				property_value;
+
+			ddl_capability->max_num_client = VCD_MAX_NO_CLIENT;
+			ddl_capability->exclusive = VCD_COMMAND_EXCLUSIVE;
+			ddl_capability->frame_command_depth =
+				VCD_FRAME_COMMAND_DEPTH;
+			ddl_capability->general_command_depth =
+				VCD_GENEVIDC_COMMAND_DEPTH;
+			ddl_capability->ddl_time_out_in_ms =
+				DDL_HW_TIMEOUT_IN_MS;
+			vcd_status = VCD_S_SUCCESS;
+		}
+		return vcd_status;
+	}
+	ddl_context = ddl_get_context();
+	if (!DDL_IS_INITIALIZED(ddl_context))
+		return VCD_ERR_ILLEGAL_OP;
+	if (!ddl)
+		return VCD_ERR_BAD_HANDLE;
+	if (ddl->decoding)
+		vcd_status = ddl_get_dec_property(ddl, property_hdr,
+				property_value);
+	else
+		vcd_status = ddl_get_enc_property(ddl, property_hdr,
+				property_value);
+	if (vcd_status)
+		DDL_MSG_ERROR("ddl_get_prop:FAILED");
+	else
+		DDL_MSG_MED("ddl_get_prop:SUCCESS");
+	return vcd_status;
+}
+
+u32 ddl_decoder_ready_to_start(struct ddl_client_context *ddl,
+	struct vcd_sequence_hdr  *header)
+{
+	struct ddl_decoder_data *decoder =
+		&(ddl->codec_data.decoder);
+
+	if (!decoder->codec.codec) {
+		DDL_MSG_ERROR("ddl_dec_start_check:Codec_not_set");
+		return false;
+	}
+	if ((!header) && (!decoder->client_frame_size.height ||
+		!decoder->client_frame_size.width)) {
+		DDL_MSG_ERROR("ddl_dec_start_check:"
+			"Client_height_width_default");
+		return false;
+	}
+	return true;
+}
+
+u32 ddl_encoder_ready_to_start(struct ddl_client_context *ddl)
+{
+	struct ddl_encoder_data *encoder = &(ddl->codec_data.encoder);
+
+	if (!encoder->codec.codec || !encoder->frame_size.height ||
+		!encoder->frame_size.width ||
+		!encoder->frame_rate.fps_denominator ||
+		!encoder->frame_rate.fps_numerator ||
+		!encoder->target_bit_rate.target_bitrate)
+		return false;
+	if (encoder->frame_rate.fps_numerator >
+		(encoder->frame_rate.fps_denominator *
+		encoder->vop_timing.vop_time_resolution)) {
+		DDL_MSG_ERROR("ResVsFrameRateFailed!");
+		return false;
+	}
+	if (encoder->profile.profile == VCD_PROFILE_H264_BASELINE &&
+		encoder->entropy_control.entropy_sel == VCD_ENTROPY_SEL_CABAC) {
+		DDL_MSG_ERROR("H264BaseLineCABAC!!");
+		return false;
+	}
+	return true;
+}
+
+static u32 ddl_set_dec_property(struct ddl_client_context *ddl,
+	struct vcd_property_hdr *property_hdr, void *property_value)
+{
+	struct ddl_decoder_data *decoder = &(ddl->codec_data.decoder);
+	u32  vcd_status = VCD_ERR_ILLEGAL_PARM ;
+
+	switch (property_hdr->prop_id) {
+	case DDL_I_DPB_RELEASE:
+		if ((sizeof(struct ddl_frame_data_tag) ==
+			property_hdr->sz) &&
+			(decoder->dp_buf.no_of_dec_pic_buf))
+			vcd_status = ddl_decoder_dpb_transact(decoder,
+				(struct ddl_frame_data_tag *)
+				property_value, DDL_DPB_OP_MARK_FREE);
+	break;
+	case DDL_I_DPB:
+	{
+		struct ddl_property_dec_pic_buffers *dpb =
+		(struct ddl_property_dec_pic_buffers *) property_value;
+
+		if ((sizeof(struct ddl_property_dec_pic_buffers) ==
+			property_hdr->sz) &&
+			(DDLCLIENT_STATE_IS(ddl,
+			DDL_CLIENT_WAIT_FOR_INITCODEC) ||
+			DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_DPB)) &&
+			(dpb->no_of_dec_pic_buf ==
+			decoder->client_output_buf_req.actual_count))
+			vcd_status = ddl_set_dec_buffers(decoder, dpb);
+	}
+	break;
+	case DDL_I_REQ_OUTPUT_FLUSH:
+		if (sizeof(u32) == property_hdr->sz) {
+			decoder->dynamic_prop_change |=
+				DDL_DEC_REQ_OUTPUT_FLUSH;
+			decoder->dpb_mask.client_mask = 0;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case DDL_I_INPUT_BUF_REQ:
+	{
+		struct vcd_buffer_requirement *buffer_req =
+			(struct vcd_buffer_requirement *)property_value;
+
+		if (sizeof(struct vcd_buffer_requirement) ==
+			property_hdr->sz &&
+			(DDLCLIENT_STATE_IS(ddl,
+			DDL_CLIENT_WAIT_FOR_INITCODEC) ||
+			DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_DPB) ||
+			DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN)) &&
+			(ddl_valid_buffer_requirement(
+			&decoder->min_input_buf_req, buffer_req))) {
+			decoder->client_input_buf_req = *buffer_req;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	}
+	break;
+	case DDL_I_OUTPUT_BUF_REQ:
+	{
+		struct vcd_buffer_requirement *buffer_req =
+			(struct vcd_buffer_requirement *)property_value;
+
+		if (sizeof(struct vcd_buffer_requirement) ==
+			property_hdr->sz &&
+			(DDLCLIENT_STATE_IS(ddl,
+			DDL_CLIENT_WAIT_FOR_INITCODEC) ||
+			DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_DPB) ||
+			DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN)) &&
+			(ddl_valid_buffer_requirement(
+			&decoder->min_output_buf_req, buffer_req))) {
+				decoder->client_output_buf_req =
+					*buffer_req;
+				vcd_status = VCD_S_SUCCESS;
+		}
+	}
+	break;
+	case VCD_I_CODEC:
+	{
+		struct vcd_property_codec *codec =
+			(struct vcd_property_codec *)property_value;
+		if (sizeof(struct vcd_property_codec) ==
+			property_hdr->sz &&
+			DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN) &&
+			ddl_codec_type_transact(ddl, false,
+			codec->codec)) {
+			if (decoder->codec.codec != codec->codec) {
+				decoder->codec = *codec;
+				ddl_set_default_dec_property(ddl);
+			}
+			vcd_status = VCD_S_SUCCESS;
+		}
+	}
+	break;
+	case VCD_I_POST_FILTER:
+		if (sizeof(struct vcd_property_post_filter) ==
+			property_hdr->sz &&
+			DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN) && (
+			decoder->codec.codec == VCD_CODEC_MPEG4 ||
+			decoder->codec.codec == VCD_CODEC_MPEG2)) {
+			decoder->post_filter =
+				*(struct vcd_property_post_filter *)
+				property_value;
+			vcd_status = VCD_S_SUCCESS;
+	}
+	break;
+	case VCD_I_FRAME_SIZE:
+	{
+		struct vcd_property_frame_size *frame_size =
+		(struct vcd_property_frame_size *) property_value;
+		if ((sizeof(struct vcd_property_frame_size) ==
+			property_hdr->sz) &&
+			(DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN)) &&
+			(DDL_ALLOW_DEC_FRAMESIZE(frame_size->width,
+			frame_size->height))) {
+			if (decoder->client_frame_size.height !=
+				frame_size->height ||
+				decoder->client_frame_size.width !=
+				frame_size->width) {
+				decoder->client_frame_size = *frame_size;
+				ddl_set_default_decoder_buffer_req(decoder,
+					true);
+			}
+			DDL_MSG_LOW("set  VCD_I_FRAME_SIZE width = %d"
+				" height = %d\n",
+				frame_size->width, frame_size->height);
+			vcd_status = VCD_S_SUCCESS;
+		}
+	}
+	break;
+	case VCD_I_BUFFER_FORMAT:
+	{
+		struct vcd_property_buffer_format *tile =
+			(struct vcd_property_buffer_format *)
+			property_value;
+		if (sizeof(struct vcd_property_buffer_format) ==
+			property_hdr->sz &&
+			DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN) &&
+			tile->buffer_format == VCD_BUFFER_FORMAT_TILE_4x2) {
+			if (tile->buffer_format !=
+				decoder->buf_format.buffer_format) {
+				decoder->buf_format = *tile;
+				ddl_set_default_decoder_buffer_req(
+					decoder, true);
+			}
+			vcd_status = VCD_S_SUCCESS;
+		}
+	}
+	break;
+	case VCD_I_H264_MV_BUFFER:
+	{
+		int index, buffer_size;
+		u8 *phys_addr;
+		u8 *virt_addr;
+		struct vcd_property_h264_mv_buffer *mv_buff =
+			(struct vcd_property_h264_mv_buffer *)
+		property_value;
+		DDL_MSG_LOW("Entered VCD_I_H264_MV_BUFFER Virt: %p, Phys %p,"
+					"fd: %d size: %d count: %d\n",
+					mv_buff->kernel_virtual_addr,
+					mv_buff->physical_addr,
+					mv_buff->pmem_fd,
+					mv_buff->size, mv_buff->count);
+		if ((property_hdr->sz == sizeof(struct
+			vcd_property_h264_mv_buffer)) &&
+			(DDLCLIENT_STATE_IS(ddl,
+			DDL_CLIENT_WAIT_FOR_INITCODEC) ||
+			DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_DPB) ||
+			DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN))) {
+			phys_addr = mv_buff->dev_addr;
+			virt_addr = mv_buff->kernel_virtual_addr;
+			buffer_size = mv_buff->size/mv_buff->count;
+
+			for (index = 0; index < mv_buff->count; index++) {
+				ddl->codec_data.decoder.hw_bufs.
+					h264_mv[index].align_physical_addr
+					= phys_addr;
+				ddl->codec_data.decoder.hw_bufs.
+					h264_mv[index].align_virtual_addr
+					= virt_addr;
+				ddl->codec_data.decoder.hw_bufs.
+					h264_mv[index].buffer_size
+					= buffer_size;
+				ddl->codec_data.decoder.hw_bufs.
+					h264_mv[index].physical_base_addr
+					= phys_addr;
+				ddl->codec_data.decoder.hw_bufs.
+					h264_mv[index].virtual_base_addr
+					= virt_addr;
+				DDL_MSG_LOW("Assigned %d buffer for "
+							"virt: %p, phys %p for "
+							"h264_mv_buffers "
+							"of size: %d\n",
+							index, virt_addr,
+							phys_addr, buffer_size);
+				phys_addr += buffer_size;
+				virt_addr += buffer_size;
+			}
+			vcd_status = VCD_S_SUCCESS;
+		}
+	}
+	break;
+	case VCD_I_FREE_H264_MV_BUFFER:
+		{
+			memset(&decoder->hw_bufs.h264_mv, 0, sizeof(struct
+					ddl_buf_addr) * DDL_MAX_BUFFER_COUNT);
+			vcd_status = VCD_S_SUCCESS;
+		}
+		break;
+	case VCD_I_OUTPUT_ORDER:
+		{
+			if (sizeof(u32) == property_hdr->sz &&
+				DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN)) {
+					decoder->output_order =
+						*(u32 *)property_value;
+					vcd_status = VCD_S_SUCCESS;
+			}
+		}
+		break;
+	case VCD_I_DEC_PICTYPE:
+		{
+			if ((sizeof(u32) == property_hdr->sz) &&
+				DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN)) {
+				decoder->idr_only_decoding =
+					*(u32 *)property_value;
+				ddl_set_default_decoder_buffer_req(
+						decoder, true);
+				vcd_status = VCD_S_SUCCESS;
+			}
+		}
+		break;
+	case VCD_I_METADATA_ENABLE:
+	case VCD_I_METADATA_HEADER:
+		DDL_MSG_MED("Meta Data Interface is Requested");
+		vcd_status = ddl_set_metadata_params(ddl, property_hdr,
+			property_value);
+		vcd_status = VCD_S_SUCCESS;
+		break;
+	case VCD_I_FRAME_RATE:
+		vcd_status = VCD_S_SUCCESS;
+		break;
+	case VCD_I_CONT_ON_RECONFIG:
+	{
+		DDL_MSG_LOW("Set property VCD_I_CONT_ON_RECONFIG\n");
+		if (sizeof(u32) == property_hdr->sz &&
+			DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN)) {
+				decoder->cont_mode = *(u32 *)property_value;
+				vcd_status = VCD_S_SUCCESS;
+		}
+	}
+	break;
+	case VCD_I_DISABLE_DMX:
+	{
+		int disable_dmx_allowed = 0;
+		DDL_MSG_LOW("Set property VCD_I_DISABLE_DMX\n");
+		if (res_trk_get_disable_dmx() &&
+			((decoder->codec.codec == VCD_CODEC_H264) ||
+			 (decoder->codec.codec == VCD_CODEC_VC1) ||
+			 (decoder->codec.codec == VCD_CODEC_VC1_RCV)))
+			disable_dmx_allowed = 1;
+
+		if (sizeof(u32) == property_hdr->sz &&
+			DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN) &&
+			disable_dmx_allowed) {
+				decoder->dmx_disable = *(u32 *)property_value;
+				vcd_status = VCD_S_SUCCESS;
+		}
+	}
+	break;
+	case VCD_REQ_PERF_LEVEL:
+		vcd_status = VCD_S_SUCCESS;
+		break;
+	default:
+		vcd_status = VCD_ERR_ILLEGAL_OP;
+		break;
+	}
+	return vcd_status;
+}
+
+static u32 ddl_check_valid_enc_level(struct vcd_property_codec *codec,
+	struct vcd_property_profile *profile,
+	struct vcd_property_level *level)
+{
+	u32 status = false;
+
+	if (codec && profile && level) {
+		switch (codec->codec) {
+		case VCD_CODEC_MPEG4:
+			status = (profile->profile ==
+				VCD_PROFILE_MPEG4_SP) &&
+				(level->level >= VCD_LEVEL_MPEG4_0) &&
+				(level->level <= VCD_LEVEL_MPEG4_6) &&
+				(VCD_LEVEL_MPEG4_3b != level->level);
+			status = status ||
+				((profile->profile ==
+				VCD_PROFILE_MPEG4_ASP) &&
+				(level->level >= VCD_LEVEL_MPEG4_0) &&
+				(level->level <= VCD_LEVEL_MPEG4_5));
+		break;
+		case VCD_CODEC_H264:
+		status = (level->level >= VCD_LEVEL_H264_1) &&
+				(level->level <= VCD_LEVEL_H264_4);
+		break;
+		case VCD_CODEC_H263:
+		status = (level->level >= VCD_LEVEL_H263_10) &&
+			(level->level <= VCD_LEVEL_H263_70);
+		break;
+		default:
+		break;
+		}
+	}
+	return status;
+}
+
+static u32 ddl_set_enc_property(struct ddl_client_context *ddl,
+	struct vcd_property_hdr *property_hdr,
+	void *property_value)
+{
+	struct ddl_encoder_data *encoder =
+		&(ddl->codec_data.encoder);
+	u32 vcd_status = VCD_ERR_ILLEGAL_PARM;
+
+	if (DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME) ||
+		DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME_DONE) ||
+		DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN)) {
+		vcd_status = ddl_set_enc_dynamic_property(ddl,
+				property_hdr, property_value);
+	}
+	if (vcd_status) {
+		if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN) ||
+			vcd_status != VCD_ERR_ILLEGAL_OP) {
+			DDL_MSG_ERROR("ddl_set_enc_property:"
+				"Fails_as_not_in_open_state");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	} else
+		return vcd_status;
+
+	switch (property_hdr->prop_id) {
+	case VCD_I_FRAME_SIZE:
+	{
+		struct vcd_property_frame_size *frame_size =
+		(struct vcd_property_frame_size *) property_value;
+		if ((sizeof(struct vcd_property_frame_size) ==
+			property_hdr->sz) &&
+			(DDL_ALLOW_ENC_FRAMESIZE(frame_size->width,
+			frame_size->height))) {
+			if (encoder->frame_size.height != frame_size->height ||
+				encoder->frame_size.width !=
+				frame_size->width) {
+				ddl_calculate_stride(frame_size, false);
+				encoder->frame_size = *frame_size;
+				ddl_set_default_encoder_buffer_req(encoder);
+			}
+			vcd_status = VCD_S_SUCCESS;
+		}
+	}
+	break;
+	case VCD_I_CODEC:
+	{
+		struct vcd_property_codec *codec =
+		(struct vcd_property_codec *) property_value;
+		if ((sizeof(struct vcd_property_codec) ==
+		property_hdr->sz) &&
+		(ddl_codec_type_transact(ddl, false, codec->codec))) {
+			if (codec->codec != encoder->codec.codec) {
+				encoder->codec = *codec;
+				ddl_set_default_enc_property(ddl);
+			}
+			vcd_status = VCD_S_SUCCESS;
+		}
+	}
+	break;
+	case VCD_I_REQ_IFRAME:
+		vcd_status = VCD_S_SUCCESS;
+		break;
+	case VCD_I_INTRA_PERIOD:
+	{
+		struct vcd_property_i_period *i_period =
+			(struct vcd_property_i_period *)property_value;
+		if (sizeof(struct vcd_property_i_period) ==
+			property_hdr->sz &&
+			i_period->b_frames <= DDL_MAX_NUM_OF_B_FRAME) {
+			encoder->i_period = *i_period;
+			encoder->client_input_buf_req.min_count =
+				i_period->b_frames + 1;
+			encoder->client_input_buf_req.actual_count =
+				DDL_MAX(encoder->client_input_buf_req.\
+				actual_count, encoder->\
+				client_input_buf_req.min_count);
+			encoder->client_output_buf_req.min_count =
+				i_period->b_frames + 2;
+			encoder->client_output_buf_req.actual_count =
+				DDL_MAX(encoder->client_output_buf_req.\
+				actual_count, encoder->\
+				client_output_buf_req.min_count);
+			ddl->extra_output_buf_count =
+				i_period->b_frames - 1;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	}
+	break;
+	case VCD_I_PROFILE:
+	{
+		struct vcd_property_profile *profile =
+			(struct vcd_property_profile *)property_value;
+
+		if ((sizeof(struct vcd_property_profile) ==
+			property_hdr->sz) && ((
+			(encoder->codec.codec == VCD_CODEC_MPEG4) && (
+			profile->profile == VCD_PROFILE_MPEG4_SP ||
+			profile->profile == VCD_PROFILE_MPEG4_ASP)) ||
+			((encoder->codec.codec == VCD_CODEC_H264) &&
+			(profile->profile >= VCD_PROFILE_H264_BASELINE) &&
+			(profile->profile <= VCD_PROFILE_H264_HIGH)) ||
+			((encoder->codec.codec == VCD_CODEC_H263) &&
+			(profile->profile == VCD_PROFILE_H263_BASELINE)))) {
+			encoder->profile = *profile;
+			vcd_status = VCD_S_SUCCESS;
+			if (profile->profile == VCD_PROFILE_H264_BASELINE)
+				encoder->entropy_control.entropy_sel =
+					VCD_ENTROPY_SEL_CAVLC;
+			else
+				encoder->entropy_control.entropy_sel =
+					VCD_ENTROPY_SEL_CABAC;
+		}
+	}
+	break;
+	case VCD_I_LEVEL:
+	{
+		struct vcd_property_level *level =
+			(struct vcd_property_level *) property_value;
+
+		if ((sizeof(struct vcd_property_level) ==
+			property_hdr->sz) && (ddl_check_valid_enc_level
+			(&encoder->codec,
+			&encoder->profile, level))) {
+			encoder->level = *level;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	}
+	break;
+	case VCD_I_MULTI_SLICE:
+	{
+		struct vcd_property_multi_slice *multi_slice =
+			(struct vcd_property_multi_slice *)
+				property_value;
+		DDL_MSG_HIGH("VCD_I_MULTI_SLICE eMSliceSel %d  nMSliceSize %d"
+				"Tot#of MB %d encoder->frame_size.width = %d"
+				"encoder->frame_size.height = %d",
+				(int)multi_slice->m_slice_sel,
+				multi_slice->m_slice_size,
+				DDL_NO_OF_MB(encoder->frame_size.width,
+					encoder->frame_size.height),
+				encoder->frame_size.width,
+				encoder->frame_size.height);
+		switch (multi_slice->m_slice_sel) {
+		case VCD_MSLICE_OFF:
+			vcd_status = VCD_S_SUCCESS;
+		break;
+		case VCD_MSLICE_BY_GOB:
+			if (encoder->codec.codec == VCD_CODEC_H263)
+				vcd_status = VCD_S_SUCCESS;
+		break;
+		case VCD_MSLICE_BY_MB_COUNT:
+		{
+			if ((multi_slice->m_slice_size >= 1) &&
+				(multi_slice->m_slice_size <=
+				DDL_NO_OF_MB(encoder->frame_size.width,
+					encoder->frame_size.height))) {
+				vcd_status = VCD_S_SUCCESS;
+			}
+		}
+		break;
+		case VCD_MSLICE_BY_BYTE_COUNT:
+			if (multi_slice->m_slice_size > 0)
+				vcd_status = VCD_S_SUCCESS;
+		break;
+		default:
+		break;
+		}
+		if (sizeof(struct vcd_property_multi_slice) ==
+			property_hdr->sz && !vcd_status) {
+			encoder->multi_slice = *multi_slice;
+			if (multi_slice->m_slice_sel == VCD_MSLICE_OFF)
+				encoder->multi_slice.m_slice_size = 0;
+		}
+	}
+	break;
+	case VCD_I_RATE_CONTROL:
+	{
+		struct vcd_property_rate_control *rate_control =
+			(struct vcd_property_rate_control *)
+			property_value;
+		if (sizeof(struct vcd_property_rate_control) ==
+			property_hdr->sz &&
+			rate_control->rate_control >=
+			VCD_RATE_CONTROL_OFF &&
+			rate_control->rate_control <=
+			VCD_RATE_CONTROL_CBR_CFR) {
+			encoder->rc = *rate_control;
+			ddl_set_default_enc_rc_params(encoder);
+			vcd_status = VCD_S_SUCCESS;
+		}
+
+	}
+	break;
+	case VCD_I_SHORT_HEADER:
+		if (sizeof(struct vcd_property_short_header) ==
+			property_hdr->sz &&
+			encoder->codec.codec ==
+			VCD_CODEC_MPEG4) {
+			encoder->short_header =
+			*(struct vcd_property_short_header *)
+				property_value;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case VCD_I_VOP_TIMING:
+	{
+		struct vcd_property_vop_timing *vop_time =
+			(struct vcd_property_vop_timing *)
+				property_value;
+
+		if ((sizeof(struct vcd_property_vop_timing) ==
+			property_hdr->sz) &&
+			(encoder->frame_rate.fps_numerator <=
+			vop_time->vop_time_resolution) &&
+			(encoder->codec.codec == VCD_CODEC_MPEG4)) {
+			encoder->vop_timing = *vop_time;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	}
+	break;
+	case VCD_I_HEADER_EXTENSION:
+		if (sizeof(u32) == property_hdr->sz &&
+			encoder->codec.codec == VCD_CODEC_MPEG4) {
+			encoder->hdr_ext_control = *(u32 *)property_value;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case VCD_I_ENTROPY_CTRL:
+	{
+		struct vcd_property_entropy_control *entropy_control =
+			(struct vcd_property_entropy_control *)
+			property_value;
+		if (sizeof(struct vcd_property_entropy_control) ==
+			property_hdr->sz &&
+			encoder->codec.codec == VCD_CODEC_H264 &&
+			entropy_control->entropy_sel >=
+			VCD_ENTROPY_SEL_CAVLC &&
+			entropy_control->entropy_sel <=
+			VCD_ENTROPY_SEL_CABAC) {
+			if ((entropy_control->entropy_sel ==
+			     VCD_ENTROPY_SEL_CABAC) &&
+			     (encoder->entropy_control.cabac_model ==
+			     VCD_CABAC_MODEL_NUMBER_1 ||
+			     encoder->entropy_control.cabac_model ==
+			     VCD_CABAC_MODEL_NUMBER_2)) {
+				vcd_status = VCD_ERR_ILLEGAL_PARM;
+			} else {
+				encoder->entropy_control = *entropy_control;
+				vcd_status = VCD_S_SUCCESS;
+			}
+		}
+	}
+	break;
+	case VCD_I_DEBLOCKING:
+	{
+		struct vcd_property_db_config *db_config =
+			(struct vcd_property_db_config *) property_value;
+		if (sizeof(struct vcd_property_db_config) ==
+			property_hdr->sz  &&
+			encoder->codec.codec == VCD_CODEC_H264 &&
+			db_config->db_config >=
+			VCD_DB_ALL_BLOCKING_BOUNDARY &&
+			db_config->db_config <=
+			VCD_DB_SKIP_SLICE_BOUNDARY) {
+			encoder->db_control = *db_config;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	}
+	break;
+	case VCD_I_QP_RANGE:
+	{
+		struct vcd_property_qp_range *qp =
+			(struct vcd_property_qp_range *)property_value;
+
+		if ((sizeof(struct vcd_property_qp_range) ==
+			property_hdr->sz) && (qp->min_qp <=
+			qp->max_qp) && ((encoder->codec.codec ==
+			VCD_CODEC_H264 && qp->max_qp <= DDL_MAX_H264_QP) ||
+			(qp->max_qp <= DDL_MAX_MPEG4_QP))) {
+			encoder->qp_range = *qp;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	}
+	break;
+	case VCD_I_SESSION_QP:
+	{
+		struct vcd_property_session_qp *qp =
+			(struct vcd_property_session_qp *)property_value;
+		if ((sizeof(struct vcd_property_session_qp) ==
+			property_hdr->sz) &&
+			(qp->i_frame_qp >= encoder->qp_range.min_qp) &&
+			(qp->i_frame_qp <= encoder->qp_range.max_qp) &&
+			(qp->p_frame_qp >= encoder->qp_range.min_qp) &&
+			(qp->p_frame_qp <= encoder->qp_range.max_qp)) {
+			encoder->session_qp = *qp;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	}
+	break;
+	case VCD_I_RC_LEVEL_CONFIG:
+	{
+		struct vcd_property_rc_level *rc_level =
+			(struct vcd_property_rc_level *) property_value;
+		if (sizeof(struct vcd_property_rc_level) ==
+			property_hdr->sz &&
+			(encoder->rc.rate_control >=
+			VCD_RATE_CONTROL_VBR_VFR ||
+			encoder->rc.rate_control <=
+			VCD_RATE_CONTROL_CBR_VFR) &&
+			(!rc_level->mb_level_rc ||
+			encoder->codec.codec == VCD_CODEC_H264)) {
+			encoder->rc_level = *rc_level;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	}
+	break;
+	case VCD_I_FRAME_LEVEL_RC:
+	{
+		struct vcd_property_frame_level_rc_params
+			*frame_level_rc =
+			(struct vcd_property_frame_level_rc_params *)
+			property_value;
+		if ((sizeof(struct vcd_property_frame_level_rc_params) ==
+			property_hdr->sz) &&
+			(frame_level_rc->reaction_coeff) &&
+			(encoder->rc_level.frame_level_rc)) {
+			encoder->frame_level_rc = *frame_level_rc;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	}
+	break;
+	case VCD_I_ADAPTIVE_RC:
+		if ((sizeof(struct vcd_property_adaptive_rc_params) ==
+			property_hdr->sz) &&
+			(encoder->codec.codec == VCD_CODEC_H264) &&
+			(encoder->rc_level.mb_level_rc)) {
+			encoder->adaptive_rc =
+				*(struct vcd_property_adaptive_rc_params *)
+				property_value;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case VCD_I_BUFFER_FORMAT:
+	{
+		struct vcd_property_buffer_format *buffer_format =
+			(struct vcd_property_buffer_format *)
+			property_value;
+
+		if (sizeof(struct vcd_property_buffer_format) ==
+			property_hdr->sz &&
+			((buffer_format->buffer_format ==
+			VCD_BUFFER_FORMAT_NV12_16M2KA) ||
+			(VCD_BUFFER_FORMAT_TILE_4x2 ==
+			buffer_format->buffer_format))) {
+			if (buffer_format->buffer_format !=
+				encoder->buf_format.buffer_format) {
+				encoder->buf_format = *buffer_format;
+				ddl_set_default_encoder_buffer_req(encoder);
+			}
+			vcd_status = VCD_S_SUCCESS;
+		}
+	}
+	break;
+	case DDL_I_INPUT_BUF_REQ:
+	{
+		struct vcd_buffer_requirement *buffer_req =
+			(struct vcd_buffer_requirement *)property_value;
+		if (sizeof(struct vcd_buffer_requirement) ==
+			property_hdr->sz && (ddl_valid_buffer_requirement(
+			&encoder->input_buf_req, buffer_req))) {
+			encoder->client_input_buf_req = *buffer_req;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	}
+	break;
+	case DDL_I_OUTPUT_BUF_REQ:
+	{
+		struct vcd_buffer_requirement *buffer_req =
+			(struct vcd_buffer_requirement *)property_value;
+		if (sizeof(struct vcd_buffer_requirement) ==
+			property_hdr->sz && (ddl_valid_buffer_requirement(
+			&encoder->output_buf_req, buffer_req))) {
+			encoder->client_output_buf_req = *buffer_req;
+			encoder->client_output_buf_req.sz =
+				DDL_ALIGN(buffer_req->sz,
+				DDL_KILO_BYTE(4));
+			DDL_MSG_LOW("%s encoder->client_output_buf_req.sz"
+				"  = %d\n", __func__,
+				encoder->client_output_buf_req.sz);
+			vcd_status = VCD_S_SUCCESS;
+		}
+	}
+	break;
+	case VCD_I_RECON_BUFFERS:
+	{
+		int index, index_hw_bufs = -1;
+		struct vcd_property_enc_recon_buffer *recon_buffers =
+			(struct vcd_property_enc_recon_buffer *)property_value;
+		for (index = 0; index < 4; index++) {
+			if (!encoder->hw_bufs.dpb_y[index].
+				align_physical_addr) {
+					index_hw_bufs = index;
+				break;
+			} else
+				continue;
+		}
+		if (index_hw_bufs == -1) {
+			DDL_MSG_HIGH("ERROR: value of index_hw_bufs");
+			vcd_status = VCD_ERR_ILLEGAL_PARM;
+		} else {
+			if (property_hdr->sz == sizeof(struct
+				vcd_property_enc_recon_buffer)) {
+				encoder->hw_bufs.dpb_y[index_hw_bufs].
+				align_physical_addr =
+					recon_buffers->dev_addr;
+				encoder->hw_bufs.dpb_y[index_hw_bufs].
+				align_virtual_addr =
+					recon_buffers->kernel_virtual_addr;
+				encoder->hw_bufs.dpb_y[index_hw_bufs].
+				buffer_size = recon_buffers->buffer_size;
+				encoder->hw_bufs.dpb_c[index_hw_bufs].
+				align_physical_addr =
+				recon_buffers->dev_addr +
+					ddl_get_yuv_buf_size(
+						encoder->frame_size.width,
+						encoder->frame_size.height,
+						DDL_YUV_BUF_TYPE_TILE);
+				encoder->hw_bufs.dpb_c[index_hw_bufs].
+					align_virtual_addr =
+					recon_buffers->kernel_virtual_addr +
+					recon_buffers->ysize;
+				DDL_MSG_LOW("Y::KVirt: %p,KPhys: %p"
+							"UV::KVirt: %p,KPhys: %p\n",
+				encoder->hw_bufs.dpb_y[index_hw_bufs].
+				align_virtual_addr,
+				encoder->hw_bufs.dpb_y[index_hw_bufs].
+				align_physical_addr,
+				encoder->hw_bufs.dpb_c[index_hw_bufs].
+				align_virtual_addr,
+				encoder->hw_bufs.dpb_c[index_hw_bufs].
+				align_physical_addr);
+				vcd_status = VCD_S_SUCCESS;
+				}
+		}
+	}
+	break;
+	case VCD_I_FREE_RECON_BUFFERS:
+	{
+		memset(&encoder->hw_bufs.dpb_y, 0,
+			sizeof(struct ddl_buf_addr) * 4);
+		memset(&encoder->hw_bufs.dpb_c, 0,
+			sizeof(struct ddl_buf_addr) * 4);
+		vcd_status = VCD_S_SUCCESS;
+		break;
+	}
+	case VCD_I_METADATA_ENABLE:
+	case VCD_I_METADATA_HEADER:
+		DDL_MSG_LOW("Meta Data Interface is Requested");
+		if (!res_trk_check_for_sec_session()) {
+			if (!encoder->slice_delivery_info.enable) {
+				vcd_status = ddl_set_metadata_params(ddl,
+						property_hdr, property_value);
+			} else {
+				DDL_MSG_ERROR("Ignoring meta data settting in "
+					"slice mode: %s\n", __func__);
+				vcd_status = VCD_S_SUCCESS;
+			}
+		} else {
+			DDL_MSG_ERROR("Meta Data Interface is not "
+				"supported in secure session");
+			vcd_status = VCD_ERR_ILLEGAL_OP;
+		}
+	break;
+	case VCD_I_META_BUFFER_MODE:
+		vcd_status = VCD_S_SUCCESS;
+		break;
+	case VCD_I_ENABLE_SPS_PPS_FOR_IDR:
+	{
+		struct vcd_property_sps_pps_for_idr_enable *sps_pps =
+		(struct vcd_property_sps_pps_for_idr_enable *) property_value;
+
+		if ((sizeof(struct vcd_property_sps_pps_for_idr_enable)) ==
+		property_hdr->sz) {
+			DDL_MSG_LOW("SPS PPS generation for IDR Encode "
+				"is Requested");
+			encoder->sps_pps.sps_pps_for_idr_enable_flag =
+			sps_pps->sps_pps_for_idr_enable_flag;
+			vcd_status = VCD_S_SUCCESS;
+		}
+		break;
+	}
+	case VCD_I_SLICE_DELIVERY_MODE:
+	{
+		size_t output_buf_size;
+		u32 num_mb, num_slices;
+		struct vcd_property_hdr slice_property_hdr;
+		struct vcd_property_meta_data_enable slice_meta_data;
+		DDL_MSG_HIGH("Set property VCD_I_SLICE_DELIVERY_MODE\n");
+		if (sizeof(u32) == property_hdr->sz &&
+			encoder->codec.codec == VCD_CODEC_H264 &&
+			encoder->multi_slice.m_slice_sel
+				 == VCD_MSLICE_BY_MB_COUNT &&
+			DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN)) {
+			encoder->slice_delivery_info.enable
+				= *(u32 *)property_value;
+			DDL_MSG_HIGH("set encoder->slice_delivery_mode = %u\n",
+				encoder->slice_delivery_info.enable);
+			output_buf_size =
+				encoder->client_output_buf_req.sz;
+			num_mb = DDL_NO_OF_MB(encoder->frame_size.width,
+					encoder->frame_size.height);
+			num_slices = num_mb/
+				encoder->multi_slice.m_slice_size;
+			num_slices = ((num_mb - num_slices *
+				encoder->multi_slice.m_slice_size) > 0)
+				? (num_slices + 1) : num_slices;
+			encoder->slice_delivery_info.num_slices =
+				num_slices;
+			if (num_slices <= DDL_MAX_NUM_BFRS_FOR_SLICE_BATCH) {
+				DDL_MSG_HIGH("%s: currently slice info "
+					"metadata is not supported when slice "
+					"delivery mode is enabled. hence "
+					"disabling slice info metadata.\n",
+					__func__);
+				slice_property_hdr.prop_id =
+					VCD_I_METADATA_ENABLE;
+				slice_property_hdr.sz =
+					sizeof(struct \
+					vcd_property_meta_data_enable);
+				ddl_get_metadata_params(ddl,
+						&slice_property_hdr,
+						&slice_meta_data);
+				slice_meta_data.meta_data_enable_flag
+					&= ~VCD_METADATA_ENC_SLICE;
+				ddl_set_metadata_params(ddl,
+						&slice_property_hdr,
+						&slice_meta_data);
+				encoder->client_output_buf_req.min_count =
+				((DDL_ENC_SLICE_BATCH_FACTOR * num_slices + 2)
+				> DDL_MAX_BUFFER_COUNT)
+				? DDL_MAX_BUFFER_COUNT :
+				(DDL_ENC_SLICE_BATCH_FACTOR * num_slices + 2);
+				output_buf_size =
+				encoder->client_output_buf_req.sz/num_slices;
+				encoder->client_output_buf_req.sz =
+				DDL_ALIGN(output_buf_size, DDL_KILO_BYTE(4));
+				encoder->output_buf_req =
+				encoder->client_output_buf_req;
+				DDL_MSG_HIGH("%s num_mb = %u num_slices = %u "
+				"output_buf_count = %u "
+				"output_buf_size = %u aligned size = %u\n",
+				__func__, num_mb, num_slices,
+				encoder->client_output_buf_req.min_count,
+				output_buf_size,
+				encoder->client_output_buf_req.sz);
+				vcd_status = VCD_S_SUCCESS;
+			}
+		}
+		break;
+	}
+	case VCD_REQ_PERF_LEVEL:
+		vcd_status = VCD_S_SUCCESS;
+		break;
+	default:
+		DDL_MSG_ERROR("INVALID ID %d\n", (int)property_hdr->prop_id);
+		vcd_status = VCD_ERR_ILLEGAL_OP;
+	break;
+	}
+	return vcd_status;
+}
+
+static u32 ddl_get_dec_property(struct ddl_client_context *ddl,
+	struct vcd_property_hdr *property_hdr, void *property_value)
+{
+	struct ddl_decoder_data *decoder = &ddl->codec_data.decoder;
+	struct vcd_property_frame_size *fz_size;
+	u32 vcd_status = VCD_ERR_ILLEGAL_PARM;
+	DDL_MSG_HIGH("property_hdr->prop_id:%x\n", property_hdr->prop_id);
+	switch (property_hdr->prop_id) {
+	case VCD_I_FRAME_SIZE:
+		if (sizeof(struct vcd_property_frame_size) ==
+			property_hdr->sz) {
+			ddl_calculate_stride(&decoder->client_frame_size,
+				!decoder->progressive_only);
+			fz_size =
+			&decoder->client_frame_size;
+			fz_size->stride =
+			DDL_TILE_ALIGN(fz_size->width,
+				DDL_TILE_ALIGN_WIDTH);
+			fz_size->scan_lines =
+			DDL_TILE_ALIGN(fz_size->height,
+				DDL_TILE_ALIGN_HEIGHT);
+			*(struct vcd_property_frame_size *)
+				property_value =
+					decoder->client_frame_size;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case VCD_I_PROFILE:
+		if (sizeof(struct vcd_property_profile) ==
+			property_hdr->sz) {
+			*(struct vcd_property_profile *)property_value =
+				decoder->profile;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case VCD_I_LEVEL:
+		if (sizeof(struct vcd_property_level) ==
+			property_hdr->sz) {
+			*(struct vcd_property_level *)property_value =
+				decoder->level;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case VCD_I_PROGRESSIVE_ONLY:
+		if (sizeof(u32) == property_hdr->sz) {
+			*(u32 *)property_value =
+				decoder->progressive_only;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case DDL_I_INPUT_BUF_REQ:
+		if (sizeof(struct vcd_buffer_requirement) ==
+			property_hdr->sz) {
+			*(struct vcd_buffer_requirement *)
+				property_value =
+					decoder->client_input_buf_req;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case DDL_I_OUTPUT_BUF_REQ:
+		if (sizeof(struct vcd_buffer_requirement) ==
+			property_hdr->sz) {
+				*(struct vcd_buffer_requirement *)
+			property_value = decoder->client_output_buf_req;
+				vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case VCD_I_CODEC:
+	if (sizeof(struct vcd_property_codec) ==
+		property_hdr->sz) {
+			*(struct vcd_property_codec *) property_value =
+				decoder->codec;
+			vcd_status = VCD_S_SUCCESS;
+	}
+	break;
+	case VCD_I_BUFFER_FORMAT:
+		if (sizeof(struct vcd_property_buffer_format) ==
+			property_hdr->sz) {
+			*(struct vcd_property_buffer_format *)
+				property_value = decoder->buf_format;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case VCD_I_POST_FILTER:
+		if (sizeof(struct vcd_property_post_filter) ==
+			property_hdr->sz) {
+			*(struct vcd_property_post_filter *)
+				property_value =
+					decoder->post_filter;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case DDL_I_SEQHDR_ALIGN_BYTES:
+		if (sizeof(u32) == property_hdr->sz) {
+			*(u32 *)property_value =
+				DDL_LINEAR_BUFFER_ALIGN_BYTES;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case DDL_I_FRAME_PROC_UNITS:
+		if (sizeof(u32) == property_hdr->sz) {
+			if (!decoder->progressive_only &&
+				(decoder->client_frame_size.width *
+				 decoder->client_frame_size.height) <=
+				DDL_FRAME_VGA_SIZE) {
+				*(u32 *) property_value = DDL_NO_OF_MB(
+					DDL_FRAME_720P_WIDTH,
+					DDL_FRAME_720P_HEIGHT);
+			} else {
+				*(u32 *) property_value = DDL_NO_OF_MB(
+					decoder->client_frame_size.width,
+					decoder->client_frame_size.height);
+			}
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case DDL_I_DPB_RETRIEVE:
+		if (sizeof(struct ddl_frame_data_tag) ==
+			property_hdr->sz) {
+			vcd_status = ddl_decoder_dpb_transact(decoder,
+				(struct ddl_frame_data_tag *)
+				property_value, DDL_DPB_OP_RETRIEVE);
+		}
+	break;
+	case VCD_I_GET_H264_MV_SIZE:
+		if (property_hdr->sz == sizeof(struct
+			vcd_property_buffer_size)) {
+			struct vcd_property_buffer_size *mv_size =
+			(struct vcd_property_buffer_size *) property_value;
+			mv_size->size = ddl_get_yuv_buf_size(mv_size->width,
+				mv_size->height, DDL_YUV_BUF_TYPE_TILE);
+			mv_size->alignment = DDL_TILE_BUFFER_ALIGN_BYTES;
+			DDL_MSG_LOW("w: %d, h: %d, S: %d, "
+						"A: %d", mv_size->width,
+						mv_size->height, mv_size->size,
+						mv_size->alignment);
+			vcd_status = VCD_S_SUCCESS;
+		}
+		break;
+	case VCD_I_OUTPUT_ORDER:
+		{
+			if (sizeof(u32) == property_hdr->sz) {
+				*(u32 *)property_value = decoder->output_order;
+				vcd_status = VCD_S_SUCCESS;
+			}
+		}
+		break;
+	case VCD_I_METADATA_ENABLE:
+	case VCD_I_METADATA_HEADER:
+		DDL_MSG_ERROR("Meta Data Interface is Requested");
+		vcd_status = ddl_get_metadata_params(ddl, property_hdr,
+			property_value);
+		vcd_status = VCD_S_SUCCESS;
+	break;
+	case VCD_I_CONT_ON_RECONFIG:
+		if (sizeof(u32) == property_hdr->sz) {
+			*(u32 *)property_value = decoder->cont_mode;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case VCD_I_DISABLE_DMX_SUPPORT:
+		if (sizeof(u32) == property_hdr->sz) {
+			*(u32 *)property_value = res_trk_get_disable_dmx();
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case VCD_I_DISABLE_DMX:
+		if (sizeof(u32) == property_hdr->sz) {
+			*(u32 *)property_value = decoder->dmx_disable;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	default:
+		vcd_status = VCD_ERR_ILLEGAL_OP;
+	break;
+	}
+	return vcd_status;
+}
+
+static u32 ddl_get_enc_property(struct ddl_client_context *ddl,
+	struct vcd_property_hdr *property_hdr, void *property_value)
+{
+	struct ddl_encoder_data *encoder = &ddl->codec_data.encoder;
+	u32 vcd_status = VCD_ERR_ILLEGAL_PARM;
+
+	switch (property_hdr->prop_id) {
+	case VCD_I_CODEC:
+		if (sizeof(struct vcd_property_codec) ==
+			property_hdr->sz) {
+			*(struct vcd_property_codec *) property_value =
+				encoder->codec;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case VCD_I_FRAME_SIZE:
+		if (sizeof(struct vcd_property_frame_size) ==
+			property_hdr->sz) {
+			*(struct vcd_property_frame_size *)
+				property_value = encoder->frame_size;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case VCD_I_FRAME_RATE:
+		if (sizeof(struct vcd_property_frame_rate) ==
+			property_hdr->sz) {
+			*(struct vcd_property_frame_rate *)
+				property_value = encoder->frame_rate;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case VCD_I_TARGET_BITRATE:
+		if (sizeof(struct vcd_property_target_bitrate) ==
+			property_hdr->sz) {
+			*(struct vcd_property_target_bitrate *)
+				property_value = encoder->target_bit_rate;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case VCD_I_RATE_CONTROL:
+		if (sizeof(struct vcd_property_rate_control) ==
+			property_hdr->sz) {
+			*(struct vcd_property_rate_control *)
+				property_value = encoder->rc;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case VCD_I_PROFILE:
+		if (sizeof(struct vcd_property_profile) ==
+			property_hdr->sz) {
+			*(struct vcd_property_profile *) property_value =
+				encoder->profile;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case VCD_I_LEVEL:
+		if (sizeof(struct vcd_property_level) ==
+			property_hdr->sz) {
+			*(struct vcd_property_level *) property_value =
+				encoder->level;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case VCD_I_MULTI_SLICE:
+		if (sizeof(struct vcd_property_multi_slice) ==
+			property_hdr->sz) {
+			*(struct vcd_property_multi_slice *)
+				property_value = encoder->multi_slice;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case VCD_I_SEQ_HEADER:
+	{
+		struct vcd_sequence_hdr *seq_hdr =
+			(struct vcd_sequence_hdr *) property_value;
+
+		if (!encoder->seq_header_length) {
+			seq_hdr->sequence_header_len =
+				encoder->seq_header_length;
+			vcd_status = VCD_ERR_NO_SEQ_HDR;
+		} else if (sizeof(struct vcd_sequence_hdr) ==
+			property_hdr->sz &&
+			encoder->seq_header_length <=
+			seq_hdr->sequence_header_len) {
+			memcpy(seq_hdr->sequence_header,
+				encoder->seq_header.align_virtual_addr,
+				encoder->seq_header_length);
+			seq_hdr->sequence_header_len =
+				encoder->seq_header_length;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	}
+	break;
+	case DDL_I_SEQHDR_PRESENT:
+		if (sizeof(u32) == property_hdr->sz) {
+			if ((encoder->codec.codec ==
+				VCD_CODEC_MPEG4 &&
+				!encoder->short_header.short_header) ||
+				encoder->codec.codec == VCD_CODEC_H264)
+				*(u32 *) property_value = 0x1;
+			else
+				*(u32 *) property_value = 0x0;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case VCD_I_VOP_TIMING:
+		if (sizeof(struct vcd_property_vop_timing) ==
+			property_hdr->sz) {
+			*(struct vcd_property_vop_timing *)
+				property_value = encoder->vop_timing;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case VCD_I_SHORT_HEADER:
+		if (sizeof(struct vcd_property_short_header) ==
+			property_hdr->sz) {
+			if (encoder->codec.codec == VCD_CODEC_MPEG4) {
+				*(struct vcd_property_short_header *)
+					property_value =
+						encoder->short_header;
+				vcd_status = VCD_S_SUCCESS;
+			} else
+				vcd_status = VCD_ERR_ILLEGAL_OP;
+		}
+	break;
+	case VCD_I_ENTROPY_CTRL:
+		if (sizeof(struct vcd_property_entropy_control) ==
+			property_hdr->sz) {
+			if (encoder->codec.codec == VCD_CODEC_H264) {
+				*(struct vcd_property_entropy_control *)
+					property_value =
+						encoder->entropy_control;
+				vcd_status = VCD_S_SUCCESS;
+			} else
+				vcd_status = VCD_ERR_ILLEGAL_OP;
+		}
+	break;
+	case VCD_I_DEBLOCKING:
+		if (sizeof(struct vcd_property_db_config) ==
+			property_hdr->sz) {
+			if (encoder->codec.codec == VCD_CODEC_H264) {
+				*(struct vcd_property_db_config *)
+					property_value =
+						encoder->db_control;
+				vcd_status = VCD_S_SUCCESS;
+			} else
+				vcd_status = VCD_ERR_ILLEGAL_OP;
+		}
+	break;
+	case VCD_I_INTRA_PERIOD:
+		if (sizeof(struct vcd_property_i_period) ==
+			property_hdr->sz) {
+			*(struct vcd_property_i_period *)
+				property_value = encoder->i_period;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case VCD_I_QP_RANGE:
+		if (sizeof(struct vcd_property_qp_range) ==
+			property_hdr->sz) {
+			*(struct vcd_property_qp_range *)
+				property_value = encoder->qp_range;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case VCD_I_SESSION_QP:
+		if (sizeof(struct vcd_property_session_qp) ==
+			property_hdr->sz) {
+			*(struct vcd_property_session_qp *)
+				property_value = encoder->session_qp;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case VCD_I_RC_LEVEL_CONFIG:
+		if (sizeof(struct vcd_property_rc_level) ==
+			property_hdr->sz) {
+			*(struct vcd_property_rc_level *)
+				property_value = encoder->rc_level;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case VCD_I_FRAME_LEVEL_RC:
+		if (sizeof(struct vcd_property_frame_level_rc_params) ==
+			property_hdr->sz) {
+			*(struct vcd_property_frame_level_rc_params *)
+			property_value = encoder->frame_level_rc;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case VCD_I_ADAPTIVE_RC:
+		if (sizeof(struct vcd_property_adaptive_rc_params) ==
+			property_hdr->sz) {
+			*(struct vcd_property_adaptive_rc_params *)
+				property_value = encoder->adaptive_rc;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case VCD_I_INTRA_REFRESH:
+		if (sizeof(struct vcd_property_intra_refresh_mb_number) ==
+			property_hdr->sz) {
+			*(struct vcd_property_intra_refresh_mb_number *)
+				property_value = encoder->intra_refresh;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case DDL_I_INPUT_BUF_REQ:
+		if (sizeof(struct vcd_buffer_requirement) ==
+			property_hdr->sz) {
+				*(struct vcd_buffer_requirement *)
+			property_value = encoder->client_input_buf_req;
+				vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case DDL_I_OUTPUT_BUF_REQ:
+		if (sizeof(struct vcd_buffer_requirement) ==
+			property_hdr->sz) {
+				*(struct vcd_buffer_requirement *)
+			property_value = encoder->client_output_buf_req;
+			DDL_MSG_LOW("%s encoder->client_output_buf_req = %d\n",
+				 __func__,
+				encoder->client_output_buf_req.sz);
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case VCD_I_BUFFER_FORMAT:
+		if (sizeof(struct vcd_property_buffer_format) ==
+			property_hdr->sz) {
+			*(struct vcd_property_buffer_format *)
+			property_value = encoder->buf_format;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case DDL_I_FRAME_PROC_UNITS:
+		if (sizeof(u32) == property_hdr->sz &&
+			encoder->frame_size.width &&
+			encoder->frame_size.height) {
+			*(u32 *)property_value = DDL_NO_OF_MB(
+				encoder->frame_size.width,
+				encoder->frame_size.height);
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case VCD_I_HEADER_EXTENSION:
+		if (sizeof(u32) == property_hdr->sz &&
+			encoder->codec.codec == VCD_CODEC_MPEG4) {
+			*(u32 *) property_value =
+				encoder->hdr_ext_control;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case VCD_I_GET_RECON_BUFFER_SIZE:
+	{
+		u32 ysize, uvsize;
+		if (property_hdr->sz == sizeof(struct
+			vcd_property_buffer_size)) {
+			struct vcd_property_buffer_size *recon_buff_size =
+			(struct vcd_property_buffer_size *) property_value;
+
+			ysize = ddl_get_yuv_buf_size(recon_buff_size->width,
+				recon_buff_size->height, DDL_YUV_BUF_TYPE_TILE);
+			uvsize = ddl_get_yuv_buf_size(recon_buff_size->width,
+				recon_buff_size->height/2,
+				DDL_YUV_BUF_TYPE_TILE);
+			recon_buff_size->size = ysize + uvsize;
+			recon_buff_size->alignment =
+				DDL_TILE_BUFFER_ALIGN_BYTES;
+			DDL_MSG_LOW("w: %d, h: %d, S: %d, A: %d",
+			recon_buff_size->width, recon_buff_size->height,
+			recon_buff_size->size, recon_buff_size->alignment);
+			vcd_status = VCD_S_SUCCESS;
+		}
+	}
+	break;
+	case VCD_I_METADATA_ENABLE:
+	case VCD_I_METADATA_HEADER:
+		DDL_MSG_ERROR("Meta Data Interface is Requested");
+		vcd_status = ddl_get_metadata_params(ddl, property_hdr,
+			property_value);
+		vcd_status = VCD_S_SUCCESS;
+	break;
+	case VCD_I_ENABLE_SPS_PPS_FOR_IDR:
+		if (sizeof(struct vcd_property_sps_pps_for_idr_enable) ==
+			property_hdr->sz) {
+			*(struct vcd_property_sps_pps_for_idr_enable *)
+				property_value = encoder->sps_pps;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case VCD_I_SLICE_DELIVERY_MODE:
+		if (sizeof(struct vcd_property_slice_delivery_info) ==
+			property_hdr->sz) {
+			*(struct vcd_property_slice_delivery_info *)
+				property_value = encoder->slice_delivery_info;
+			vcd_status = VCD_S_SUCCESS;
+		}
+		break;
+	default:
+		vcd_status = VCD_ERR_ILLEGAL_OP;
+		break;
+	}
+	return vcd_status;
+}
+
+static u32 ddl_set_enc_dynamic_property(struct ddl_client_context *ddl,
+	struct vcd_property_hdr *property_hdr, void *property_value)
+{
+	struct ddl_encoder_data *encoder = &ddl->codec_data.encoder;
+	u32  vcd_status = VCD_ERR_ILLEGAL_PARM;
+	u32  dynamic_prop_change = 0x0;
+
+	switch (property_hdr->prop_id) {
+	case VCD_I_REQ_IFRAME:
+		if (sizeof(struct vcd_property_req_i_frame) ==
+			property_hdr->sz) {
+			dynamic_prop_change |= DDL_ENC_REQ_IFRAME;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	break;
+	case VCD_I_TARGET_BITRATE:
+	{
+		struct vcd_property_target_bitrate *bitrate =
+			(struct vcd_property_target_bitrate *)property_value;
+
+		if (sizeof(struct vcd_property_target_bitrate) ==
+			property_hdr->sz && bitrate->target_bitrate &&
+			bitrate->target_bitrate <= DDL_MAX_BIT_RATE) {
+			encoder->target_bit_rate = *bitrate;
+			dynamic_prop_change = DDL_ENC_CHANGE_BITRATE;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	}
+	break;
+	case VCD_I_INTRA_PERIOD:
+	{
+		struct vcd_property_i_period *i_period =
+			(struct vcd_property_i_period *)property_value;
+
+		if (sizeof(struct vcd_property_i_period) ==
+			property_hdr->sz) {
+			encoder->i_period = *i_period;
+			dynamic_prop_change = DDL_ENC_CHANGE_IPERIOD;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	}
+	break;
+	case VCD_I_FRAME_RATE:
+	{
+		struct vcd_property_frame_rate *frame_rate =
+			(struct vcd_property_frame_rate *)
+			property_value;
+		if (sizeof(struct vcd_property_frame_rate) ==
+			property_hdr->sz &&
+			frame_rate->fps_denominator &&
+			frame_rate->fps_numerator &&
+			frame_rate->fps_denominator <=
+			frame_rate->fps_numerator) {
+			encoder->frame_rate = *frame_rate;
+			dynamic_prop_change = DDL_ENC_CHANGE_FRAMERATE;
+			if (DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN) &&
+				(encoder->codec.codec != VCD_CODEC_MPEG4 ||
+				encoder->short_header.short_header)) {
+				ddl_set_default_enc_vop_timing(encoder);
+			}
+			vcd_status = VCD_S_SUCCESS;
+		}
+	}
+	break;
+	case VCD_I_INTRA_REFRESH:
+	{
+		struct vcd_property_intra_refresh_mb_number
+			*intra_refresh_mb_num =
+			(struct vcd_property_intra_refresh_mb_number *)
+			property_value;
+		u32 frame_mb_num = DDL_NO_OF_MB(encoder->frame_size.width,
+			encoder->frame_size.height);
+
+		if ((sizeof(struct vcd_property_intra_refresh_mb_number) ==
+			property_hdr->sz) &&
+			(intra_refresh_mb_num->cir_mb_number <= frame_mb_num)) {
+			encoder->intra_refresh = *intra_refresh_mb_num;
+			dynamic_prop_change = DDL_ENC_CHANGE_CIR;
+			vcd_status = VCD_S_SUCCESS;
+		}
+	}
+	break;
+	default:
+		vcd_status = VCD_ERR_ILLEGAL_OP;
+		break;
+	}
+
+	if (!vcd_status && (DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME)
+		|| DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME_DONE)))
+		encoder->dynamic_prop_change |= dynamic_prop_change;
+
+	return vcd_status;
+}
+
+void ddl_set_default_dec_property(struct ddl_client_context *ddl)
+{
+	struct ddl_decoder_data *decoder =
+		&(ddl->codec_data.decoder);
+
+	if (decoder->codec.codec >= VCD_CODEC_MPEG2 &&
+		decoder->codec.codec <=  VCD_CODEC_XVID)
+		decoder->post_filter.post_filter = false;
+	else
+		decoder->post_filter.post_filter = false;
+	decoder->buf_format.buffer_format = VCD_BUFFER_FORMAT_TILE_4x2;
+	decoder->client_frame_size.height = VCD_DDL_TEST_DEFAULT_HEIGHT;
+	decoder->client_frame_size.width  = VCD_DDL_TEST_DEFAULT_WIDTH;
+	decoder->client_frame_size.stride = VCD_DDL_TEST_DEFAULT_WIDTH;
+	decoder->client_frame_size.scan_lines = VCD_DDL_TEST_DEFAULT_HEIGHT;
+	decoder->progressive_only = 1;
+	decoder->idr_only_decoding = false;
+	decoder->output_order = VCD_DEC_ORDER_DISPLAY;
+	decoder->field_needed_for_prev_ip = 0;
+	decoder->cont_mode = 0;
+	decoder->reconfig_detected = false;
+	decoder->dmx_disable = false;
+	ddl_set_default_metadata_flag(ddl);
+	ddl_set_default_decoder_buffer_req(decoder, true);
+}
+
+static void ddl_set_default_enc_property(struct ddl_client_context *ddl)
+{
+	struct ddl_encoder_data *encoder = &(ddl->codec_data.encoder);
+
+	ddl_set_default_enc_profile(encoder);
+	ddl_set_default_enc_level(encoder);
+	encoder->rc.rate_control = VCD_RATE_CONTROL_VBR_VFR;
+	ddl_set_default_enc_rc_params(encoder);
+	ddl_set_default_enc_intra_period(encoder);
+	encoder->intra_refresh.cir_mb_number = 0;
+	ddl_set_default_enc_vop_timing(encoder);
+	encoder->multi_slice.m_slice_sel = VCD_MSLICE_OFF;
+	encoder->multi_slice.m_slice_size = 0;
+	ddl->b_count = 0;
+	encoder->short_header.short_header    = false;
+	encoder->entropy_control.entropy_sel  = VCD_ENTROPY_SEL_CAVLC;
+	encoder->entropy_control.cabac_model  = VCD_CABAC_MODEL_NUMBER_0;
+	encoder->db_control.db_config         =
+		VCD_DB_ALL_BLOCKING_BOUNDARY;
+	encoder->db_control.slice_alpha_offset = 0;
+	encoder->db_control.slice_beta_offset = 0;
+	encoder->recon_buf_format.buffer_format =
+		VCD_BUFFER_FORMAT_TILE_1x1;
+	encoder->buf_format.buffer_format = VCD_BUFFER_FORMAT_NV12_16M2KA;
+	encoder->hdr_ext_control = 0;
+	encoder->mb_info_enable  = false;
+	encoder->num_references_for_p_frame = DDL_MIN_NUM_REF_FOR_P_FRAME;
+	if (encoder->codec.codec == VCD_CODEC_MPEG4)
+		encoder->closed_gop = true;
+	ddl_set_default_metadata_flag(ddl);
+	ddl_set_default_encoder_buffer_req(encoder);
+	encoder->slice_delivery_info.enable = 0;
+	encoder->slice_delivery_info.num_slices = 0;
+	encoder->slice_delivery_info.num_slices_enc = 0;
+}
+
+static void ddl_set_default_enc_profile(struct ddl_encoder_data *encoder)
+{
+	enum vcd_codec codec = encoder->codec.codec;
+
+	if (codec == VCD_CODEC_MPEG4)
+		encoder->profile.profile = VCD_PROFILE_MPEG4_SP;
+	else if (codec == VCD_CODEC_H264)
+		encoder->profile.profile = VCD_PROFILE_H264_BASELINE;
+	else
+		encoder->profile.profile = VCD_PROFILE_H263_BASELINE;
+}
+
+static void ddl_set_default_enc_level(struct ddl_encoder_data *encoder)
+{
+	enum vcd_codec codec = encoder->codec.codec;
+
+	if (codec == VCD_CODEC_MPEG4)
+		encoder->level.level = VCD_LEVEL_MPEG4_1;
+	else if (codec == VCD_CODEC_H264)
+		encoder->level.level = VCD_LEVEL_H264_1;
+	else
+		encoder->level.level = VCD_LEVEL_H263_10;
+}
+
+static void ddl_set_default_enc_vop_timing(
+	struct ddl_encoder_data *encoder)
+{
+	if (encoder->codec.codec == VCD_CODEC_MPEG4) {
+		encoder->vop_timing.vop_time_resolution =
+			(encoder->frame_rate.fps_numerator << 1) /
+			encoder->frame_rate.fps_denominator;
+	} else
+		encoder->vop_timing.vop_time_resolution =
+			DDL_FRAMERATE_SCALE(DDL_INITIAL_FRAME_RATE);
+}
+
+static void ddl_set_default_enc_intra_period(
+	struct ddl_encoder_data *encoder)
+{
+	switch (encoder->rc.rate_control) {
+	default:
+	case VCD_RATE_CONTROL_VBR_VFR:
+	case VCD_RATE_CONTROL_VBR_CFR:
+	case VCD_RATE_CONTROL_CBR_VFR:
+	case VCD_RATE_CONTROL_OFF:
+		encoder->i_period.p_frames =
+			((encoder->frame_rate.fps_numerator << 1) /
+			encoder->frame_rate.fps_denominator) - 1;
+	break;
+	case VCD_RATE_CONTROL_CBR_CFR:
+		encoder->i_period.p_frames =
+			((encoder->frame_rate.fps_numerator >> 1) /
+			encoder->frame_rate.fps_denominator) - 1;
+	break;
+	}
+	encoder->i_period.b_frames = DDL_DEFAULT_NUM_OF_B_FRAME;
+}
+
+static void ddl_set_default_enc_rc_params(
+	struct ddl_encoder_data *encoder)
+{
+	enum vcd_codec codec = encoder->codec.codec;
+	encoder->rc_level.frame_level_rc = true;
+	encoder->qp_range.min_qp = 0x1;
+	if (codec == VCD_CODEC_H264) {
+		encoder->qp_range.min_qp = 0x1;
+		encoder->qp_range.max_qp = 0x33;
+		encoder->session_qp.i_frame_qp = 0x14;
+		encoder->session_qp.p_frame_qp = 0x14;
+		encoder->session_qp.b_frame_qp = 0x14;
+		encoder->rc_level.mb_level_rc  = true;
+		encoder->adaptive_rc.disable_activity_region_flag = true;
+		encoder->adaptive_rc.disable_dark_region_as_flag = true;
+		encoder->adaptive_rc.disable_smooth_region_as_flag = true;
+		encoder->adaptive_rc.disable_static_region_as_flag = true;
+	} else {
+		encoder->qp_range.max_qp       = 0x1f;
+		encoder->qp_range.min_qp       = 0x1;
+		encoder->session_qp.i_frame_qp = 0xd;
+		encoder->session_qp.p_frame_qp = 0xd;
+		encoder->session_qp.b_frame_qp = 0xd;
+		encoder->rc_level.frame_level_rc = true;
+		encoder->rc_level.mb_level_rc  = false;
+	}
+	switch (encoder->rc.rate_control) {
+	case VCD_RATE_CONTROL_VBR_CFR:
+		encoder->r_cframe_skip = 0;
+		encoder->frame_level_rc.reaction_coeff = 0x1f4;
+	break;
+	case VCD_RATE_CONTROL_CBR_VFR:
+		encoder->r_cframe_skip = 1;
+		if (codec != VCD_CODEC_H264) {
+			encoder->session_qp.i_frame_qp = 0xf;
+			encoder->session_qp.p_frame_qp = 0xf;
+			encoder->session_qp.b_frame_qp = 0xf;
+		}
+		encoder->frame_level_rc.reaction_coeff = 0x14;
+	break;
+	case VCD_RATE_CONTROL_CBR_CFR:
+		encoder->r_cframe_skip = 0;
+		encoder->frame_level_rc.reaction_coeff = 0x6;
+	break;
+	case VCD_RATE_CONTROL_OFF:
+		encoder->r_cframe_skip = 0;
+		encoder->rc_level.frame_level_rc = false;
+		encoder->rc_level.mb_level_rc = false;
+	break;
+	case VCD_RATE_CONTROL_VBR_VFR:
+	default:
+		encoder->r_cframe_skip = 1;
+		encoder->frame_level_rc.reaction_coeff = 0x1f4;
+	break;
+	}
+}
+
+void ddl_set_default_encoder_buffer_req(struct ddl_encoder_data *encoder)
+{
+	u32 y_cb_cr_size, y_size;
+	memset(&encoder->hw_bufs.dpb_y, 0, sizeof(struct ddl_buf_addr) * 4);
+	memset(&encoder->hw_bufs.dpb_c, 0, sizeof(struct ddl_buf_addr) * 4);
+
+	y_cb_cr_size = ddl_get_yuv_buffer_size(&encoder->frame_size,
+				&encoder->buf_format, false,
+				encoder->hdr.decoding, &y_size);
+	encoder->input_buf_size.size_yuv = y_cb_cr_size;
+	encoder->input_buf_size.size_y   = y_size;
+	encoder->input_buf_size.size_c   = y_cb_cr_size - y_size;
+	memset(&encoder->input_buf_req , 0 ,
+		sizeof(struct vcd_buffer_requirement));
+	encoder->input_buf_req.min_count    = 3;
+	encoder->input_buf_req.actual_count =
+		encoder->input_buf_req.min_count;
+	encoder->input_buf_req.max_count    = DDL_MAX_BUFFER_COUNT;
+	encoder->input_buf_req.sz = y_cb_cr_size;
+	if (encoder->buf_format.buffer_format ==
+		VCD_BUFFER_FORMAT_NV12_16M2KA)
+		encoder->input_buf_req.align =
+			DDL_LINEAR_BUFFER_ALIGN_BYTES;
+	else if (VCD_BUFFER_FORMAT_TILE_4x2 ==
+		encoder->buf_format.buffer_format)
+		encoder->input_buf_req.align = DDL_TILE_BUFFER_ALIGN_BYTES;
+	encoder->client_input_buf_req = encoder->input_buf_req;
+	memset(&encoder->output_buf_req , 0 ,
+		sizeof(struct vcd_buffer_requirement));
+	encoder->output_buf_req.min_count = encoder->i_period.b_frames + 2;
+	encoder->output_buf_req.actual_count =
+		encoder->output_buf_req.min_count + 3;
+	encoder->output_buf_req.max_count    = DDL_MAX_BUFFER_COUNT;
+	encoder->output_buf_req.align	= DDL_LINEAR_BUFFER_ALIGN_BYTES;
+	if (y_cb_cr_size >= VCD_DDL_720P_YUV_BUF_SIZE)
+		y_cb_cr_size = y_cb_cr_size>>1;
+	encoder->output_buf_req.sz =
+		DDL_ALIGN(y_cb_cr_size, DDL_KILO_BYTE(4));
+	ddl_set_default_encoder_metadata_buffer_size(encoder);
+	encoder->client_output_buf_req = encoder->output_buf_req;
+	DDL_MSG_LOW("%s encoder->client_output_buf_req.sz = %d\n",
+		__func__, encoder->client_output_buf_req.sz);
+}
+
+u32 ddl_set_default_decoder_buffer_req(struct ddl_decoder_data *decoder,
+	u32 estimate)
+{
+	struct vcd_property_frame_size *frame_size;
+	struct vcd_buffer_requirement *input_buf_req;
+	struct vcd_buffer_requirement *output_buf_req;
+	u32  min_dpb, y_cb_cr_size;
+
+	if (!decoder->codec.codec)
+		return false;
+	if (estimate) {
+		if (!decoder->cont_mode)
+			min_dpb = ddl_decoder_min_num_dpb(decoder);
+		else
+			min_dpb = res_trk_get_min_dpb_count();
+		frame_size = &decoder->client_frame_size;
+		output_buf_req = &decoder->client_output_buf_req;
+		input_buf_req = &decoder->client_input_buf_req;
+		y_cb_cr_size = ddl_get_yuv_buffer_size(frame_size,
+					&decoder->buf_format,
+					(!decoder->progressive_only),
+					decoder->hdr.decoding, NULL);
+	} else {
+		frame_size = &decoder->frame_size;
+		output_buf_req = &decoder->actual_output_buf_req;
+		input_buf_req = &decoder->actual_input_buf_req;
+		min_dpb = decoder->min_dpb_num;
+		y_cb_cr_size = decoder->y_cb_cr_size;
+		if ((decoder->buf_format.buffer_format ==
+			VCD_BUFFER_FORMAT_TILE_4x2) &&
+			(frame_size->height < MDP_MIN_TILE_HEIGHT)) {
+			frame_size->height = MDP_MIN_TILE_HEIGHT;
+			ddl_calculate_stride(frame_size,
+				!decoder->progressive_only);
+			y_cb_cr_size = ddl_get_yuv_buffer_size(
+				frame_size,
+				&decoder->buf_format,
+				(!decoder->progressive_only),
+				decoder->hdr.decoding, NULL);
+		} else
+			y_cb_cr_size = decoder->y_cb_cr_size;
+	}
+	memset(output_buf_req, 0,
+		sizeof(struct vcd_buffer_requirement));
+	if (!decoder->idr_only_decoding && !decoder->cont_mode)
+		output_buf_req->actual_count = min_dpb + 4;
+	else
+		output_buf_req->actual_count = min_dpb;
+	output_buf_req->min_count = min_dpb;
+	output_buf_req->max_count = DDL_MAX_BUFFER_COUNT;
+	output_buf_req->sz = y_cb_cr_size;
+	DDL_MSG_LOW("output_buf_req->sz : %d", output_buf_req->sz);
+	if (decoder->buf_format.buffer_format != VCD_BUFFER_FORMAT_NV12)
+		output_buf_req->align = DDL_TILE_BUFFER_ALIGN_BYTES;
+	else
+		output_buf_req->align = DDL_LINEAR_BUFFER_ALIGN_BYTES;
+	ddl_set_default_decoder_metadata_buffer_size(decoder, frame_size,
+		output_buf_req);
+
+	decoder->min_output_buf_req = *output_buf_req;
+	memset(input_buf_req, 0,
+		sizeof(struct vcd_buffer_requirement));
+	input_buf_req->min_count = 1;
+	input_buf_req->actual_count = input_buf_req->min_count + 1;
+	input_buf_req->max_count = DDL_MAX_BUFFER_COUNT;
+	input_buf_req->sz = (1024 * 1024 * 2);
+	input_buf_req->align = DDL_LINEAR_BUFFER_ALIGN_BYTES;
+	decoder->min_input_buf_req = *input_buf_req;
+	return true;
+}
+
+u32 ddl_get_yuv_buffer_size(struct vcd_property_frame_size *frame_size,
+	struct vcd_property_buffer_format *buf_format,
+	u32 interlace, u32 decoding, u32 *pn_c_offset)
+{
+	struct vcd_property_frame_size frame_sz = *frame_size;
+	u32 total_memory_size = 0, c_offset = 0;
+	ddl_calculate_stride(&frame_sz, interlace);
+	if (buf_format->buffer_format == VCD_BUFFER_FORMAT_TILE_4x2) {
+		u32 component_mem_size, width_round_up;
+		u32 height_round_up, height_chroma = (frame_sz.scan_lines >> 1);
+
+		width_round_up =
+			DDL_ALIGN(frame_sz.stride, DDL_TILE_ALIGN_WIDTH);
+		height_round_up =
+			DDL_ALIGN(frame_sz.scan_lines,
+						   DDL_TILE_ALIGN_HEIGHT);
+		component_mem_size = width_round_up * height_round_up;
+		component_mem_size = DDL_ALIGN(component_mem_size,
+			DDL_TILE_MULTIPLY_FACTOR);
+		c_offset = component_mem_size;
+		total_memory_size = ((component_mem_size +
+					DDL_TILE_BUF_ALIGN_GUARD_BYTES) &
+					DDL_TILE_BUF_ALIGN_MASK);
+		height_round_up = DDL_ALIGN(height_chroma,
+					DDL_TILE_ALIGN_HEIGHT);
+		component_mem_size = width_round_up * height_round_up;
+		component_mem_size = DDL_ALIGN(component_mem_size,
+					DDL_TILE_MULTIPLY_FACTOR);
+		total_memory_size += component_mem_size;
+	} else {
+		if (decoding)
+			total_memory_size = frame_sz.scan_lines *
+						frame_sz.stride;
+		else
+			total_memory_size = frame_sz.height * frame_sz.stride;
+		c_offset = DDL_ALIGN(total_memory_size,
+			DDL_LINEAR_MULTIPLY_FACTOR);
+		total_memory_size = c_offset + DDL_ALIGN(
+			total_memory_size >> 1, DDL_LINEAR_MULTIPLY_FACTOR);
+	}
+	if (pn_c_offset)
+		*pn_c_offset = c_offset;
+	return total_memory_size;
+}
+
+
+void ddl_calculate_stride(struct vcd_property_frame_size *frame_size,
+	u32 interlace)
+{
+	frame_size->stride = DDL_ALIGN(frame_size->width,
+					DDL_LINEAR_ALIGN_WIDTH);
+	if (interlace)
+		frame_size->scan_lines = DDL_ALIGN(frame_size->height,
+						DDL_TILE_ALIGN_HEIGHT);
+	else
+		frame_size->scan_lines = DDL_ALIGN(frame_size->height,
+						DDL_LINEAR_ALIGN_HEIGHT);
+}
+
+
+static u32 ddl_valid_buffer_requirement(struct vcd_buffer_requirement
+	*original_buf_req, struct vcd_buffer_requirement *req_buf_req)
+{
+	u32 status = false;
+
+	if (original_buf_req->max_count >= req_buf_req->actual_count &&
+		original_buf_req->min_count <=
+		req_buf_req->actual_count &&
+		!((original_buf_req->align - (u32)0x1) &
+		req_buf_req->align) &&
+		/*original_buf_req->align <= req_buf_req->align,*/
+		original_buf_req->sz <= req_buf_req->sz)
+		status = true;
+	else {
+		DDL_MSG_ERROR("ddl_valid_buf_req:Failed");
+		DDL_MSG_ERROR("codec_buf_req: min_cnt=%d, mx_cnt=%d, "
+			"align=%d, sz=%d\n", original_buf_req->min_count,
+			original_buf_req->max_count, original_buf_req->align,
+			original_buf_req->sz);
+		DDL_MSG_ERROR("client_buffs: actual_count=%d, align=%d, "
+			"sz=%d\n", req_buf_req->actual_count,
+			req_buf_req->align,	req_buf_req->sz);
+	}
+	return status;
+}
+
+static u32 ddl_decoder_min_num_dpb(struct ddl_decoder_data *decoder)
+{
+	u32 min_dpb = 0;
+
+	if (decoder->idr_only_decoding) {
+		min_dpb = DDL_MIN_BUFFER_COUNT;
+		if (decoder->post_filter.post_filter)
+			min_dpb *= 2;
+		return min_dpb;
+	}
+
+	switch (decoder->codec.codec) {
+	case VCD_CODEC_H264:
+	{
+		u32 yuv_size_in_mb = DDL_MIN(DDL_NO_OF_MB(
+			decoder->client_frame_size.stride,
+			decoder->client_frame_size.scan_lines),
+			MAX_FRAME_SIZE_L4PT0_MBS);
+		min_dpb = DDL_MIN((MAX_DPB_SIZE_L4PT0_MBS /
+				yuv_size_in_mb), 16);
+		min_dpb += 2;
+	}
+	break;
+	case VCD_CODEC_H263:
+		min_dpb = 3;
+	break;
+	default:
+	case VCD_CODEC_MPEG1:
+	case VCD_CODEC_MPEG2:
+	case VCD_CODEC_MPEG4:
+	case VCD_CODEC_DIVX_3:
+	case VCD_CODEC_DIVX_4:
+	case VCD_CODEC_DIVX_5:
+	case VCD_CODEC_DIVX_6:
+	case VCD_CODEC_XVID:
+	case VCD_CODEC_VC1:
+	case VCD_CODEC_VC1_RCV:
+		min_dpb = 4;
+		if (decoder->post_filter.post_filter)
+			min_dpb *= 2;
+	break;
+	}
+	return min_dpb;
+}
+
+static u32 ddl_set_dec_buffers(struct ddl_decoder_data *decoder,
+	struct ddl_property_dec_pic_buffers *dpb)
+{
+	u32 vcd_status  = VCD_S_SUCCESS, loopc;
+
+
+	for (loopc = 0; !vcd_status &&
+		loopc < dpb->no_of_dec_pic_buf; ++loopc) {
+		if ((!DDL_ADDR_IS_ALIGNED(dpb->dec_pic_buffers[loopc].
+			vcd_frm.physical,
+			decoder->client_output_buf_req.align)) ||
+			(dpb->dec_pic_buffers[loopc].vcd_frm.alloc_len <
+			decoder->client_output_buf_req.sz))
+			vcd_status = VCD_ERR_ILLEGAL_PARM;
+	}
+	if (vcd_status) {
+		DDL_MSG_ERROR("ddl_set_prop:"
+			"Dpb_align_fail_or_alloc_size_small");
+		return vcd_status;
+	}
+	if (decoder->dp_buf.no_of_dec_pic_buf) {
+		kfree(decoder->dp_buf.dec_pic_buffers);
+		decoder->dp_buf.no_of_dec_pic_buf = 0;
+	}
+	decoder->dp_buf.dec_pic_buffers =
+		kmalloc(dpb->no_of_dec_pic_buf *
+			sizeof(struct ddl_frame_data_tag), GFP_KERNEL);
+	if (!decoder->dp_buf.dec_pic_buffers) {
+		DDL_MSG_ERROR("ddl_dec_set_prop:Dpb_container_alloc_failed");
+		return VCD_ERR_ALLOC_FAIL;
+	}
+	decoder->dp_buf.no_of_dec_pic_buf = dpb->no_of_dec_pic_buf;
+	for (loopc = 0; loopc < dpb->no_of_dec_pic_buf; ++loopc)
+		decoder->dp_buf.dec_pic_buffers[loopc] =
+			dpb->dec_pic_buffers[loopc];
+	decoder->dpb_mask.client_mask = 0;
+	decoder->dpb_mask.hw_mask = 0;
+	decoder->dynamic_prop_change = 0;
+	return VCD_S_SUCCESS;
+}
+
+void ddl_set_initial_default_values(struct ddl_client_context *ddl)
+{
+
+	if (ddl->decoding) {
+		ddl->codec_data.decoder.codec.codec = VCD_CODEC_MPEG4;
+		ddl_set_default_dec_property(ddl);
+	} else {
+		struct ddl_encoder_data *encoder =
+			&(ddl->codec_data.encoder);
+		encoder->codec.codec = VCD_CODEC_MPEG4;
+		encoder->target_bit_rate.target_bitrate = 64000;
+		encoder->frame_size.width = VCD_DDL_TEST_DEFAULT_WIDTH;
+		encoder->frame_size.height = VCD_DDL_TEST_DEFAULT_HEIGHT;
+		encoder->frame_size.scan_lines =
+			VCD_DDL_TEST_DEFAULT_HEIGHT;
+		encoder->frame_size.stride = VCD_DDL_TEST_DEFAULT_WIDTH;
+		encoder->frame_rate.fps_numerator = DDL_INITIAL_FRAME_RATE;
+		encoder->frame_rate.fps_denominator = 1;
+		ddl_set_default_enc_property(ddl);
+		encoder->sps_pps.sps_pps_for_idr_enable_flag = false;
+	}
+}
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_shared_mem.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_shared_mem.c
new file mode 100644
index 0000000..878db62
--- /dev/null
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_shared_mem.c
@@ -0,0 +1,876 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "vcd_ddl_shared_mem.h"
+
+#define VIDC_SM_EXTENDED_DECODE_STATUS_ADDR    0x0000
+#define VIDC_SM_EXT_DEC_STATUS_RESOLUTION_CHANGE_BMSK 0x1
+#define VIDC_SM_EXT_DEC_STATUS_RESOLUTION_CHANGE_SHFT 0x0
+#define VIDC_SM_EXT_DEC_STATUS_MORE_FIELD_NEEDED_BMSK 0x4
+#define VIDC_SM_EXT_DEC_STATUS_MORE_FIELD_NEEDED_SHFT 0x2
+
+#define VIDC_SM_SET_FRAME_TAG_ADDR             0x0004
+#define VIDC_SM_GET_FRAME_TAG_TOP_ADDR         0x0008
+#define VIDC_SM_GET_FRAME_TAG_BOTTOM_ADDR      0x000c
+#define VIDC_SM_PIC_TIME_TOP_ADDR              0x0010
+#define VIDC_SM_PIC_TIME_BOTTOM_ADDR           0x0014
+#define VIDC_SM_START_BYTE_NUM_ADDR            0x0018
+
+#define VIDC_SM_CROP_INFO1_ADDR                0x0020
+#define VIDC_SM_CROP_INFO1_RIGHT_OFFSET_BMSK   0xffff0000
+#define VIDC_SM_CROP_INFO1_RIGHT_OFFSET_SHFT   16
+#define VIDC_SM_CROP_INFO1_LEFT_OFFSET_BMSK    0x0000ffff
+#define VIDC_SM_CROP_INFO1_LEFT_OFFSET_SHFT    0
+
+#define VIDC_SM_CROP_INFO2_ADDR                0x0024
+#define VIDC_SM_CROP_INFO2_BOTTOM_OFFSET_BMSK  0xffff0000
+#define VIDC_SM_CROP_INFO2_BOTTOM_OFFSET_SHFT  16
+#define VIDC_SM_CROP_INFO2_TOP_OFFSET_BMSK     0x0000ffff
+#define VIDC_SM_CROP_INFO2_TOP_OFFSET_SHFT     0
+
+#define VIDC_SM_DISP_PIC_PROFILE_ADDR                       0x007c
+#define VIDC_SM_DISP_PIC_PROFILE_DISP_PIC_LEVEL_BMASK       0x0000ff00
+#define VIDC_SM_DISP_PIC_PROFILE_DISP_PIC_LEVEL_SHFT        8
+#define VIDC_SM_DISP_PIC_PROFILE_DISP_PIC_PROFILE_BMASK     0x0000001f
+#define VIDC_SM_DISP_PIC_PROFILE_DISP_PIC_PROFILE_SHFT      0
+
+#define VIDC_SM_DISP_PIC_FRAME_TYPE_ADDR                    0x00c0
+#define VIDC_SM_DISP_PIC_FRAME_TYPE_BMSK                    0x00000003
+#define VIDC_SM_DISP_PIC_FRAME_TYPE_SHFT                    0
+
+#define VIDC_SM_FREE_LUMA_DPB_ADDR                          0x00c4
+#define VIDC_SM_FREE_LUMA_DPB_BMSK                          0xffffffff
+#define VIDC_SM_FREE_LUMA_DPB_SHFT                          0
+
+#define VIDC_SM_FREE_LUMA_DPB_DEC_ORDER_ADDR                0x00fc
+#define VIDC_SM_FREE_LUMA_DPB_DEC_ORDER_BMSK                0xffffffff
+#define VIDC_SM_FREE_LUMA_DPB_DEC_ORDER_SHFT                0
+
+#define VIDC_SM_DEC_ORDER_WIDTH_ADDR                        0x00e8
+#define VIDC_SM_DEC_ORDER_WIDTH_BMSK                        0xffffffff
+#define VIDC_SM_DEC_ORDER_WIDTH_SHFT                        0
+
+#define VIDC_SM_DEC_ORDER_HEIGHT_ADDR                       0x00ec
+#define VIDC_SM_DEC_ORDER_HEIGHT_BMSK                       0xffffffff
+#define VIDC_SM_DEC_ORDER_HEIGHT_SHFT                       0
+
+#define VIDC_SM_DEC_CROP_INFO1_ADDR                         0x00f4
+#define VIDC_SM_DEC_CROP_INFO1_RIGHT_OFFSET_BMSK            0xffff0000
+#define VIDC_SM_DEC_CROP_INFO1_RIGHT_OFFSET_SHFT            16
+#define VIDC_SM_DEC_CROP_INFO1_LEFT_OFFSET_BMSK             0x0000ffff
+#define VIDC_SM_DEC_CROP_INFO1_LEFT_OFFSET_SHFT             0
+
+#define VIDC_SM_DEC_CROP_INFO2_ADDR                         0x00f8
+#define VIDC_SM_DEC_CROP_INFO2_BOTTOM_OFFSET_BMSK           0xffff0000
+#define VIDC_SM_DEC_CROP_INFO2_BOTTOM_OFFSET_SHFT           16
+#define VIDC_SM_DEC_CROP_INFO2_TOP_OFFSET_BMSK              0x0000ffff
+#define VIDC_SM_DEC_CROP_INFO2_TOP_OFFSET_SHFT              0
+
+#define VIDC_SM_IDR_DECODING_ONLY_ADDR                      0x0108
+#define VIDC_SM_IDR_DECODING_ONLY_BMSK                      0x00000001
+#define VIDC_SM_IDR_DECODING_ONLY_SHIFT                     0
+
+#define VIDC_SM_ENC_EXT_CTRL_ADDR                    0x0028
+#define VIDC_SM_ENC_EXT_CTRL_VBV_BUFFER_SIZE_BMSK    0xffff0000
+#define VIDC_SM_ENC_EXT_CTRL_VBV_BUFFER_SIZE_SHFT    16
+#define VIDC_SM_ENC_EXT_CTRL_H263_CPCFC_ENABLE_BMSK  0x80
+#define VIDC_SM_ENC_EXT_CTRL_H263_CPCFC_ENABLE_SHFT  7
+#define VIDC_SM_ENC_EXT_CTRL_SPS_PPS_CONTROL_BMSK    0X100
+#define VIDC_SM_ENC_EXT_CTRL_SPS_PPS_CONTROL_SHFT    8
+#define VIDC_SM_ENC_EXT_CTRL_SEQ_HDR_CTRL_BMSK       0x8
+#define VIDC_SM_ENC_EXT_CTRL_SEQ_HDR_CTRL_SHFT       3
+#define VIDC_SM_ENC_EXT_CTRL_FRAME_SKIP_ENABLE_BMSK  0x6
+#define VIDC_SM_ENC_EXT_CTRL_FRAME_SKIP_ENABLE_SHFT  1
+#define VIDC_SM_ENC_EXT_CTRL_HEC_ENABLE_BMSK         0x1
+#define VIDC_SM_ENC_EXT_CTRL_HEC_ENABLE_SHFT         0
+
+#define VIDC_SM_ENC_PARAM_CHANGE_ADDR                0x002c
+#define VIDC_SM_ENC_PARAM_CHANGE_RC_BIT_RATE_BMSK    0x4
+#define VIDC_SM_ENC_PARAM_CHANGE_RC_BIT_RATE_SHFT    2
+#define VIDC_SM_ENC_PARAM_CHANGE_RC_FRAME_RATE_BMSK  0x2
+#define VIDC_SM_ENC_PARAM_CHANGE_RC_FRAME_RATE_SHFT  1
+#define VIDC_SM_ENC_PARAM_CHANGE_I_PERIOD_BMSK       0x1
+#define VIDC_SM_ENC_PARAM_CHANGE_I_PERIOD_SHFT       0
+
+#define VIDC_SM_ENC_VOP_TIMING_ADDR                  0x0030
+#define VIDC_SM_ENC_VOP_TIMING_ENABLE_BMSK           0x80000000
+#define VIDC_SM_ENC_VOP_TIMING_ENABLE_SHFT           31
+#define VIDC_SM_ENC_VOP_TIMING_TIME_RESOLUTION_BMSK  0x7fff0000
+#define VIDC_SM_ENC_VOP_TIMING_TIME_RESOLUTION_SHFT  16
+#define VIDC_SM_ENC_VOP_TIMING_FRAME_DELTA_BMSK      0x0000ffff
+#define VIDC_SM_ENC_VOP_TIMING_FRAME_DELTA_SHFT      0
+
+#define VIDC_SM_ENC_HEC_PERIOD_ADDR                  0x0034
+
+#define VIDC_SM_H264_REF_L0_ADDR                    0x005c
+#define VIDC_SM_H264_REF_L0_CHRO_BTM_FLG_1_BMSK     0x80000000
+#define VIDC_SM_H264_REF_L0_CHRO_BTM_FLG_1_SHFT     31
+#define VIDC_SM_H264_REF_L0_CHRO_REF_1_BMSK         0x7f000000
+#define VIDC_SM_H264_REF_L0_CHRO_REF_1_SHFT         24
+#define VIDC_SM_H264_REF_L0_CHRO_BTM_FLG_0_BMSK     0x00800000
+#define VIDC_SM_H264_REF_L0_CHRO_BTM_FLG_0_SHFT     23
+#define VIDC_SM_H264_REF_L0_CHRO_REF_0_BMSK         0x007f0000
+#define VIDC_SM_H264_REF_L0_CHRO_REF_0_SHFT         16
+#define VIDC_SM_H264_REF_L0_LUMA_BTM_FLG_1_BMSK     0x00008000
+#define VIDC_SM_H264_REF_L0_LUMA_BTM_FLG_1_SHFT     15
+#define VIDC_SM_H264_REF_L0_LUMA_REF_1_BMSK         0x00007f00
+#define VIDC_SM_H264_REF_L0_LUMA_REF_1_SHFT         8
+#define VIDC_SM_H264_REF_L0_LUMA_BTM_FLG_0_BMSK     0x00000080
+#define VIDC_SM_H264_REF_L0_LUMA_BTM_FLG_0_SHFT     7
+#define VIDC_SM_H264_REF_L0_LUMA_REF_0_BMSK         0x0000007f
+#define VIDC_SM_H264_REF_L0_LUMA_REF_0_SHFT         0
+
+#define VIDC_SM_H264_REF_L1_ADDR                  0x0060
+#define VIDC_SM_H264_REF_L1_CHRO_BTM_FLG_0_BMSK   0x00800000
+#define VIDC_SM_H264_REF_L1_CHRO_BTM_FLG_0_SHFT   23
+#define VIDC_SM_H264_REF_L1_CHRO_REF_0_BMSK       0x007f0000
+#define VIDC_SM_H264_REF_L1_CHRO_REF_0_SHFT       16
+#define VIDC_SM_H264_REF_L1_LUMA_BTM_FLG_0_BMSK   0x00000080
+#define VIDC_SM_H264_REF_L1_LUMA_BTM_FLG_0_SHFT   7
+#define VIDC_SM_H264_REF_L1_LUMA_REF_0_BMSK       0x0000007f
+#define VIDC_SM_H264_REF_L1_LUMA_REF_0_SHFT       0
+
+#define VIDC_SM_P_B_FRAME_QP_ADDR               0x0070
+#define VIDC_SM_P_B_FRAME_QP_B_FRAME_QP_BMASK   0x00000fc0
+#define VIDC_SM_P_B_FRAME_QP_B_FRAME_QP_SHFT    6
+#define VIDC_SM_P_B_FRAME_QP_P_FRAME_QP_BMASK   0x0000003f
+#define VIDC_SM_P_B_FRAME_QP_P_FRAME_QP_SHFT    0
+
+#define VIDC_SM_NEW_RC_BIT_RATE_ADDR           0x0090
+#define VIDC_SM_NEW_RC_BIT_RATE_VALUE_BMASK    0xffffffff
+#define VIDC_SM_NEW_RC_BIT_RATE_VALUE_SHFT     0
+#define VIDC_SM_NEW_RC_FRAME_RATE_ADDR         0x0094
+#define VIDC_SM_NEW_RC_FRAME_RATE_VALUE_BMASK  0xffffffff
+#define VIDC_SM_NEW_RC_FRAME_RATE_VALUE_SHFT   0
+#define VIDC_SM_NEW_I_PERIOD_ADDR              0x0098
+#define VIDC_SM_NEW_I_PERIOD_VALUE_BMASK       0xffffffff
+#define VIDC_SM_NEW_I_PERIOD_VALUE_SHFT        0
+
+#define VIDC_SM_BATCH_INPUT_ADDR                                  0x00a4
+#define VIDC_SM_BATCH_INPUT_ADDR_VALUE_BMSK                       0xffffffff
+#define VIDC_SM_BATCH_INPUT_ADDRL_VALUE_SHFT                      0
+#define VIDC_SM_BATCH_OUTPUT_ADDR                                 0x00a8
+#define VIDC_SM_BATCH_OUTPUT_ADDR_VALUE_BMSK                      0xffffffff
+#define VIDC_SM_BATCH_OUTPUT_ADDR_VALUE_SHFT                      0
+#define VIDC_SM_BATCH_OUTPUT_SIZE_ADDR                            0x00ac
+#define VIDC_SM_BATCH_OUTPUT_SIZE_VALUE_BMSK                      0xffffffff
+#define VIDC_SM_BATCH_OUTPUT_SIZE_VALUE_SHFT                      0
+#define VIDC_SM_ENC_SLICE_BATCH_INT_CTRL_ADDR                     0x01c8
+#define VIDC_SM_ENC_SLICE_BATCH_INT_CTRL_VALUE_BMSK               0x1
+#define VIDC_SM_ENC_SLICE_BATCH_INT_CTRL_VALUE_SHFT               0
+#define VIDC_SM_ENC_NUM_OF_SLICE_ADDR                             0x01cc
+#define VIDC_SM_ENC_NUM_OF_SLICE_VALUE_BMSK                       0xffffffff
+#define VIDC_SM_ENC_NUM_OF_SLICE_VALUE_SHFT                       0
+#define VIDC_SM_ENC_NUM_OF_SLICE_COMP_ADDR                        0x01d0
+#define VIDC_SM_ENC_NUM_OF_SLICE_COMP_VALUE_BMSK                  0xffffffff
+#define VIDC_SM_ENC_NUM_OF_SLICE_COMP_VALUE_SHFT                  0
+
+#define VIDC_SM_ALLOCATED_LUMA_DPB_SIZE_ADDR               0x0064
+#define VIDC_SM_ALLOCATED_CHROMA_DPB_SIZE_ADDR             0x0068
+#define VIDC_SM_ALLOCATED_MV_SIZE_ADDR                     0x006c
+#define VIDC_SM_FLUSH_CMD_TYPE_ADDR                        0x0080
+#define VIDC_SM_FLUSH_CMD_INBUF1_ADDR                      0x0084
+#define VIDC_SM_FLUSH_CMD_INBUF2_ADDR                      0x0088
+#define VIDC_SM_FLUSH_CMD_OUTBUF_ADDR                      0x008c
+#define VIDC_SM_MIN_LUMA_DPB_SIZE_ADDR                     0x00b0
+#define VIDC_SM_MIN_CHROMA_DPB_SIZE_ADDR                   0x00bc
+
+
+#define VIDC_SM_METADATA_ENABLE_ADDR                 0x0038
+#define VIDC_SM_METADATA_ENABLE_EXTRADATA_BMSK       0x40
+#define VIDC_SM_METADATA_ENABLE_EXTRADATA_SHFT       6
+#define VIDC_SM_METADATA_ENABLE_ENC_SLICE_SIZE_BMSK  0x20
+#define VIDC_SM_METADATA_ENABLE_ENC_SLICE_SIZE_SHFT  5
+#define VIDC_SM_METADATA_ENABLE_VUI_BMSK             0x10
+#define VIDC_SM_METADATA_ENABLE_VUI_SHFT             4
+#define VIDC_SM_METADATA_ENABLE_SEI_VIDC_BMSK         0x8
+#define VIDC_SM_METADATA_ENABLE_SEI_VIDC_SHFT         3
+#define VIDC_SM_METADATA_ENABLE_VC1_PARAM_BMSK       0x4
+#define VIDC_SM_METADATA_ENABLE_VC1_PARAM_SHFT       2
+#define VIDC_SM_METADATA_ENABLE_CONCEALED_MB_BMSK    0x2
+#define VIDC_SM_METADATA_ENABLE_CONCEALED_MB_SHFT    1
+#define VIDC_SM_METADATA_ENABLE_QP_BMSK              0x1
+#define VIDC_SM_METADATA_ENABLE_QP_SHFT              0
+
+#define VIDC_SM_ASPECT_RATIO_INFO_ADDR               0x00c8
+#define VIDC_SM_MPEG4_ASPECT_RATIO_INFO_BMSK         0xf
+#define VIDC_SM_MPEG4_ASPECT_RATIO_INFO_SHFT         0x0
+#define VIDC_SM_EXTENDED_PAR_ADDR                    0x00cc
+#define VIDC_SM_EXTENDED_PAR_WIDTH_BMSK              0xffff0000
+#define VIDC_SM_EXTENDED_PAR_WIDTH_SHFT              0xf
+#define VIDC_SM_EXTENDED_PAR_HEIGHT_BMSK             0x0000ffff
+#define VIDC_SM_EXTENDED_PAR_HEIGHT_SHFT             0x0
+
+#define VIDC_SM_METADATA_STATUS_ADDR         0x003c
+#define VIDC_SM_METADATA_STATUS_STATUS_BMSK  0x1
+#define VIDC_SM_METADATA_STATUS_STATUS_SHFT  0
+
+#define VIDC_SM_METADATA_DISPLAY_INDEX_ADDR   0x0040
+#define VIDC_SM_EXT_METADATA_START_ADDR_ADDR  0x0044
+
+#define VIDC_SM_PUT_EXTRADATA_ADDR      0x0048
+#define VIDC_SM_PUT_EXTRADATA_PUT_BMSK  0x1
+#define VIDC_SM_PUT_EXTRADATA_PUT_SHFT  0
+
+#define VIDC_SM_EXTRADATA_ADDR_ADDR     0x004c
+
+#define VIDC_SM_CHROMA_ADDR_CHANGE_ADDR   0x0148
+#define VIDC_SM_CHROMA_ADDR_CHANGE_BMASK  0x00000001
+#define VIDC_SM_CHROMA_ADDR_CHANGE_SHFT   0
+
+#define VIDC_SM_ERROR_CONCEALMENT_CONFIG_ADDR   0x0154
+
+#define VIDC_SM_ERROR_CONCEALMENT_CONFIG_INTER_SLICE_BMSK  0x0c
+#define VIDC_SM_ERROR_CONCEALMENT_CONFIG_INTER_SLICE_SHFT 2
+#define VIDC_SM_ERROR_CONCEALMENT_CONFIG_INTRA_SLICE_BMSK 0X02
+#define VIDC_SM_ERROR_CONCEALMENT_CONFIG_INTRA_SLICE_SHFT 1
+#define VIDC_SM_ERROR_CONCEALMENT_CONFIG_CONCEAL_ENABLE_BMSK  0x01
+#define VIDC_SM_ERROR_CONCEALMENT_CONFIG_CONCEAL_ENABLE_SHFT   0
+
+#define VIDC_SM_SEI_ENABLE_ADDR                     0x0180
+#define VIDC_SM_SEI_ENABLE_RECOVERY_POINT_SEI_BMSK  0x00000001
+#define VIDC_SM_SEI_ENABLE_RECOVERY_POINT_SEI_SHFT  0
+
+#define VIDC_SM_NUM_STUFF_BYTES_CONSUME_ADDR    0X01ac
+
+#define VIDC_SM_TIMEOUT_VALUE_ADDR        0x0158
+#define VIDC_SM_TIMEOUT_VALUE_BMSK        0xffffffff
+#define VIDC_SM_TIMEOUT_VALUE_SHFT        0
+
+#define VIDC_SM_ENC_EXT_CTRL_CLOSED_GOP_ENABLE_BMSK	0x40
+#define VIDC_SM_ENC_EXT_CTRL_CLOSED_GOP_ENABLE_SHFT	6
+
+#define DDL_MEM_WRITE_32(base, offset, val) ddl_mem_write_32(\
+	(u32 *) ((u8 *) (base)->align_virtual_addr + (offset)), (val))
+#define DDL_MEM_READ_32(base, offset) ddl_mem_read_32(\
+	(u32 *) ((u8 *) (base)->align_virtual_addr + (offset)))
+
+#define DDL_SHARED_MEM_11BIT_RIGHT_SHIFT  11
+
+static void ddl_mem_write_32(u32 *addr, u32 data)
+{
+	*addr = data;
+}
+
+static u32 ddl_mem_read_32(u32 *addr)
+{
+	return *addr;
+}
+
+void vidc_sm_get_extended_decode_status(struct ddl_buf_addr *shared_mem,
+	u32 *more_field_needed,
+	u32 *resl_change)
+{
+	u32 decode_status = DDL_MEM_READ_32(shared_mem,
+					VIDC_SM_EXTENDED_DECODE_STATUS_ADDR);
+	if (more_field_needed)
+		*more_field_needed =
+				VIDC_GETFIELD(decode_status,
+				VIDC_SM_EXT_DEC_STATUS_MORE_FIELD_NEEDED_BMSK,
+				VIDC_SM_EXT_DEC_STATUS_MORE_FIELD_NEEDED_SHFT);
+	if (resl_change)
+		*resl_change =
+				VIDC_GETFIELD(decode_status,
+				VIDC_SM_EXT_DEC_STATUS_RESOLUTION_CHANGE_BMSK,
+				VIDC_SM_EXT_DEC_STATUS_RESOLUTION_CHANGE_SHFT);
+}
+
+void vidc_sm_set_frame_tag(struct ddl_buf_addr *shared_mem,
+	u32 frame_tag)
+{
+	DDL_MEM_WRITE_32(shared_mem, VIDC_SM_SET_FRAME_TAG_ADDR, frame_tag);
+}
+
+void vidc_sm_get_frame_tags(struct ddl_buf_addr *shared_mem,
+	u32  *pn_frame_tag_top, u32 *pn_frame_tag_bottom)
+{
+	*pn_frame_tag_top = DDL_MEM_READ_32(shared_mem,
+				VIDC_SM_GET_FRAME_TAG_TOP_ADDR);
+	*pn_frame_tag_bottom = DDL_MEM_READ_32(shared_mem,
+					VIDC_SM_GET_FRAME_TAG_BOTTOM_ADDR);
+}
+
+void vidc_sm_get_picture_times(struct ddl_buf_addr *shared_mem,
+	u32 *pn_time_top, u32 *pn_time_bottom)
+{
+	*pn_time_top = DDL_MEM_READ_32(shared_mem, VIDC_SM_PIC_TIME_TOP_ADDR);
+	*pn_time_bottom = DDL_MEM_READ_32(shared_mem,
+						VIDC_SM_PIC_TIME_BOTTOM_ADDR);
+}
+
+void vidc_sm_set_start_byte_number(struct ddl_buf_addr *shared_mem,
+	u32 byte_num)
+{
+	DDL_MEM_WRITE_32(shared_mem, VIDC_SM_START_BYTE_NUM_ADDR, byte_num);
+}
+
+void vidc_sm_get_crop_info(struct ddl_buf_addr *shared_mem,
+	u32 *pn_left, u32 *pn_right, u32 *pn_top, u32 *pn_bottom)
+{
+	u32 info1, info2;
+
+	info1 = DDL_MEM_READ_32(shared_mem, VIDC_SM_CROP_INFO1_ADDR);
+
+	*pn_left = VIDC_GETFIELD(info1, VIDC_SM_CROP_INFO1_LEFT_OFFSET_BMSK,
+					VIDC_SM_CROP_INFO1_LEFT_OFFSET_SHFT);
+	*pn_right = VIDC_GETFIELD(info1, VIDC_SM_CROP_INFO1_RIGHT_OFFSET_BMSK,
+					VIDC_SM_CROP_INFO1_RIGHT_OFFSET_SHFT);
+	info2 = DDL_MEM_READ_32(shared_mem, VIDC_SM_CROP_INFO2_ADDR);
+	*pn_top = VIDC_GETFIELD(info2, VIDC_SM_CROP_INFO2_TOP_OFFSET_BMSK,
+					VIDC_SM_CROP_INFO2_TOP_OFFSET_SHFT);
+	*pn_bottom = VIDC_GETFIELD(info2,
+					VIDC_SM_CROP_INFO2_BOTTOM_OFFSET_BMSK,
+					VIDC_SM_CROP_INFO2_BOTTOM_OFFSET_SHFT);
+}
+
+void vidc_sm_get_displayed_picture_frame(struct ddl_buf_addr
+	*shared_mem, u32  *n_disp_picture_frame)
+{
+	u32 disp_pict_frame;
+
+	disp_pict_frame = DDL_MEM_READ_32(shared_mem,
+					VIDC_SM_DISP_PIC_FRAME_TYPE_ADDR);
+	*n_disp_picture_frame = VIDC_GETFIELD(disp_pict_frame,
+			VIDC_SM_DISP_PIC_FRAME_TYPE_BMSK,
+			VIDC_SM_DISP_PIC_FRAME_TYPE_SHFT);
+}
+void vidc_sm_get_available_luma_dpb_address(struct ddl_buf_addr
+	*shared_mem, u32 *pn_free_luma_dpb_address)
+{
+	*pn_free_luma_dpb_address = DDL_MEM_READ_32(shared_mem,
+		VIDC_SM_FREE_LUMA_DPB_ADDR);
+}
+
+void vidc_sm_get_available_luma_dpb_dec_order_address(
+	struct ddl_buf_addr	*shared_mem,
+	u32 *pn_free_luma_dpb_address)
+{
+	*pn_free_luma_dpb_address = DDL_MEM_READ_32(shared_mem,
+		VIDC_SM_FREE_LUMA_DPB_DEC_ORDER_ADDR);
+}
+
+void vidc_sm_get_dec_order_resl(
+	struct ddl_buf_addr *shared_mem, u32 *width, u32 *height)
+{
+	*width = DDL_MEM_READ_32(shared_mem,
+			VIDC_SM_DEC_ORDER_WIDTH_ADDR);
+	*height = DDL_MEM_READ_32(shared_mem,
+			VIDC_SM_DEC_ORDER_HEIGHT_ADDR);
+}
+
+void vidc_sm_get_dec_order_crop_info(
+	struct ddl_buf_addr *shared_mem, u32 *left,
+	u32 *right, u32 *top, u32 *bottom)
+{
+	u32 crop_data;
+	crop_data = DDL_MEM_READ_32(shared_mem,
+		VIDC_SM_DEC_CROP_INFO1_ADDR);
+	*left = VIDC_GETFIELD(crop_data,
+		VIDC_SM_DEC_CROP_INFO1_LEFT_OFFSET_BMSK,
+		VIDC_SM_DEC_CROP_INFO1_LEFT_OFFSET_SHFT);
+	*right = VIDC_GETFIELD(crop_data,
+		VIDC_SM_DEC_CROP_INFO1_RIGHT_OFFSET_BMSK,
+		VIDC_SM_DEC_CROP_INFO1_RIGHT_OFFSET_SHFT);
+	crop_data = DDL_MEM_READ_32(shared_mem,
+		VIDC_SM_DEC_CROP_INFO2_ADDR);
+	*top = VIDC_GETFIELD(crop_data,
+		VIDC_SM_DEC_CROP_INFO2_TOP_OFFSET_BMSK,
+		VIDC_SM_DEC_CROP_INFO2_TOP_OFFSET_SHFT);
+	*bottom = VIDC_GETFIELD(crop_data,
+		VIDC_SM_DEC_CROP_INFO2_BOTTOM_OFFSET_BMSK,
+		VIDC_SM_DEC_CROP_INFO2_BOTTOM_OFFSET_SHFT);
+}
+
+void vidc_sm_set_extended_encoder_control(struct ddl_buf_addr
+	*shared_mem, u32 hec_enable,
+	enum VIDC_SM_frame_skip frame_skip_mode,
+	u32 seq_hdr_in_band, u32 vbv_buffer_size, u32 cpcfc_enable,
+	u32 sps_pps_control, u32 closed_gop_enable)
+{
+	u32 enc_ctrl;
+
+	enc_ctrl = VIDC_SETFIELD((hec_enable) ? 1 : 0,
+			VIDC_SM_ENC_EXT_CTRL_HEC_ENABLE_SHFT,
+			VIDC_SM_ENC_EXT_CTRL_HEC_ENABLE_BMSK) |
+			VIDC_SETFIELD((u32) frame_skip_mode,
+			VIDC_SM_ENC_EXT_CTRL_FRAME_SKIP_ENABLE_SHFT,
+			VIDC_SM_ENC_EXT_CTRL_FRAME_SKIP_ENABLE_BMSK) |
+			VIDC_SETFIELD((seq_hdr_in_band) ? 1 : 0 ,
+			VIDC_SM_ENC_EXT_CTRL_SEQ_HDR_CTRL_SHFT ,
+			VIDC_SM_ENC_EXT_CTRL_SEQ_HDR_CTRL_BMSK) |
+			VIDC_SETFIELD(vbv_buffer_size,
+			VIDC_SM_ENC_EXT_CTRL_VBV_BUFFER_SIZE_SHFT,
+			VIDC_SM_ENC_EXT_CTRL_VBV_BUFFER_SIZE_BMSK) |
+			VIDC_SETFIELD((cpcfc_enable) ? 1 : 0,
+			VIDC_SM_ENC_EXT_CTRL_H263_CPCFC_ENABLE_SHFT,
+			VIDC_SM_ENC_EXT_CTRL_H263_CPCFC_ENABLE_BMSK) |
+			VIDC_SETFIELD((sps_pps_control) ? 1 : 0,
+			VIDC_SM_ENC_EXT_CTRL_SPS_PPS_CONTROL_SHFT,
+			VIDC_SM_ENC_EXT_CTRL_SPS_PPS_CONTROL_BMSK) |
+			VIDC_SETFIELD(closed_gop_enable,
+			VIDC_SM_ENC_EXT_CTRL_CLOSED_GOP_ENABLE_SHFT,
+			VIDC_SM_ENC_EXT_CTRL_CLOSED_GOP_ENABLE_BMSK);
+	DDL_MEM_WRITE_32(shared_mem, VIDC_SM_ENC_EXT_CTRL_ADDR, enc_ctrl);
+}
+
+void vidc_sm_set_encoder_param_change(struct ddl_buf_addr *shared_mem,
+	u32 bit_rate_chg, u32 frame_rate_chg, u32 i_period_chg)
+{
+	u32 enc_param_chg;
+
+	enc_param_chg = VIDC_SETFIELD((bit_rate_chg) ? 1 : 0,
+				VIDC_SM_ENC_PARAM_CHANGE_RC_BIT_RATE_SHFT,
+				VIDC_SM_ENC_PARAM_CHANGE_RC_BIT_RATE_BMSK) |
+				VIDC_SETFIELD((frame_rate_chg) ? 1 : 0,
+				VIDC_SM_ENC_PARAM_CHANGE_RC_FRAME_RATE_SHFT,
+				VIDC_SM_ENC_PARAM_CHANGE_RC_FRAME_RATE_BMSK) |
+				VIDC_SETFIELD((i_period_chg) ? 1 : 0,
+				VIDC_SM_ENC_PARAM_CHANGE_I_PERIOD_SHFT,
+				VIDC_SM_ENC_PARAM_CHANGE_I_PERIOD_BMSK);
+	DDL_MEM_WRITE_32(shared_mem, VIDC_SM_ENC_PARAM_CHANGE_ADDR,
+		enc_param_chg);
+}
+
+void vidc_sm_set_encoder_vop_time(struct ddl_buf_addr *shared_mem,
+	u32 vop_time_enable, u32 time_resolution, u32 frame_delta)
+{
+	u32 vop_time;
+
+	vop_time = VIDC_SETFIELD((vop_time_enable) ? 1 : 0,
+			VIDC_SM_ENC_VOP_TIMING_ENABLE_SHFT ,
+			VIDC_SM_ENC_VOP_TIMING_ENABLE_BMSK) |
+			VIDC_SETFIELD(time_resolution ,
+			VIDC_SM_ENC_VOP_TIMING_TIME_RESOLUTION_SHFT,
+			VIDC_SM_ENC_VOP_TIMING_TIME_RESOLUTION_BMSK) |
+			VIDC_SETFIELD(frame_delta,
+			VIDC_SM_ENC_VOP_TIMING_FRAME_DELTA_SHFT,
+			VIDC_SM_ENC_VOP_TIMING_FRAME_DELTA_BMSK);
+	DDL_MEM_WRITE_32(shared_mem, VIDC_SM_ENC_VOP_TIMING_ADDR, vop_time);
+}
+
+void vidc_sm_set_encoder_hec_period(struct ddl_buf_addr *shared_mem,
+	u32 hec_period)
+{
+	DDL_MEM_WRITE_32(shared_mem, VIDC_SM_ENC_HEC_PERIOD_ADDR,
+		hec_period);
+}
+
+void vidc_sm_get_h264_encoder_reference_list0(struct ddl_buf_addr
+	*shared_mem, enum VIDC_SM_ref_picture *pe_luma_picture0,
+	u32 *pn_luma_picture_index0, enum VIDC_SM_ref_picture
+		*pe_luma_picture1, u32 *pn_luma_picture_index1,
+	enum VIDC_SM_ref_picture *pe_chroma_picture0,
+	u32 *pn_chroma_picture_index0,
+	enum VIDC_SM_ref_picture *pe_chroma_picture1,
+	u32 *pn_chroma_picture_index1)
+{
+	u32 ref_list;
+
+	ref_list = DDL_MEM_READ_32(shared_mem, VIDC_SM_H264_REF_L0_ADDR);
+
+	*pe_luma_picture0 = (enum VIDC_SM_ref_picture)
+				VIDC_GETFIELD(ref_list,
+				VIDC_SM_H264_REF_L0_LUMA_BTM_FLG_0_BMSK,
+				VIDC_SM_H264_REF_L0_LUMA_BTM_FLG_0_SHFT);
+	*pn_luma_picture_index0 =
+				VIDC_GETFIELD(ref_list,
+				VIDC_SM_H264_REF_L0_LUMA_REF_0_BMSK,
+				VIDC_SM_H264_REF_L0_LUMA_REF_0_SHFT);
+	*pe_luma_picture1 = (enum VIDC_SM_ref_picture)
+				VIDC_GETFIELD(ref_list,
+				VIDC_SM_H264_REF_L0_LUMA_BTM_FLG_1_BMSK,
+				VIDC_SM_H264_REF_L0_LUMA_BTM_FLG_1_SHFT);
+	*pn_luma_picture_index1 = VIDC_GETFIELD(ref_list,
+				VIDC_SM_H264_REF_L0_LUMA_REF_1_BMSK,
+				VIDC_SM_H264_REF_L0_LUMA_REF_1_SHFT);
+	*pe_chroma_picture0 = (enum VIDC_SM_ref_picture)
+				VIDC_GETFIELD(ref_list,
+				VIDC_SM_H264_REF_L0_CHRO_BTM_FLG_0_BMSK,
+				VIDC_SM_H264_REF_L0_CHRO_BTM_FLG_0_SHFT);
+	*pn_chroma_picture_index0 = VIDC_GETFIELD(ref_list,
+				VIDC_SM_H264_REF_L0_CHRO_REF_0_BMSK,
+				VIDC_SM_H264_REF_L0_CHRO_REF_0_SHFT);
+	*pe_chroma_picture1 = (enum VIDC_SM_ref_picture)
+				VIDC_GETFIELD(ref_list,
+				VIDC_SM_H264_REF_L0_CHRO_BTM_FLG_1_BMSK,
+				VIDC_SM_H264_REF_L0_CHRO_BTM_FLG_1_SHFT);
+	*pn_chroma_picture_index1 =
+				VIDC_GETFIELD(ref_list,
+				VIDC_SM_H264_REF_L0_CHRO_REF_1_BMSK,
+				VIDC_SM_H264_REF_L0_CHRO_REF_1_SHFT);
+}
+
+void vidc_sm_get_h264_encoder_reference_list1(struct ddl_buf_addr
+	*shared_mem, enum VIDC_SM_ref_picture *pe_luma_picture,
+	u32 *pn_luma_picture_index,
+	enum VIDC_SM_ref_picture *pe_chroma_picture,
+	u32 *pn_chroma_picture_index)
+{
+	u32 ref_list;
+
+	ref_list = DDL_MEM_READ_32(shared_mem, VIDC_SM_H264_REF_L1_ADDR);
+
+	*pe_luma_picture = (enum VIDC_SM_ref_picture)
+				VIDC_GETFIELD(ref_list,
+				VIDC_SM_H264_REF_L1_LUMA_BTM_FLG_0_BMSK,
+				VIDC_SM_H264_REF_L1_LUMA_BTM_FLG_0_SHFT);
+	*pn_luma_picture_index =
+				VIDC_GETFIELD(ref_list,
+				VIDC_SM_H264_REF_L1_LUMA_REF_0_BMSK,
+				VIDC_SM_H264_REF_L1_LUMA_REF_0_SHFT);
+	*pe_chroma_picture = (enum VIDC_SM_ref_picture)
+				VIDC_GETFIELD(ref_list,
+				VIDC_SM_H264_REF_L1_CHRO_BTM_FLG_0_BMSK,
+				VIDC_SM_H264_REF_L1_CHRO_BTM_FLG_0_SHFT);
+	*pn_chroma_picture_index = VIDC_GETFIELD(ref_list,
+				VIDC_SM_H264_REF_L1_CHRO_REF_0_BMSK,
+				VIDC_SM_H264_REF_L1_CHRO_REF_0_SHFT);
+}
+
+void vidc_sm_set_allocated_dpb_size(struct ddl_buf_addr *shared_mem,
+		u32 y_size, u32 c_size)
+{
+	DDL_MEM_WRITE_32(shared_mem, VIDC_SM_ALLOCATED_LUMA_DPB_SIZE_ADDR,
+		y_size);
+	DDL_MEM_WRITE_32(shared_mem, VIDC_SM_ALLOCATED_CHROMA_DPB_SIZE_ADDR,
+		c_size);
+}
+
+void vidc_sm_set_allocated_h264_mv_size(struct ddl_buf_addr *shared_mem,
+	u32 mv_size)
+{
+	DDL_MEM_WRITE_32(shared_mem, VIDC_SM_ALLOCATED_MV_SIZE_ADDR,
+		mv_size);
+}
+
+void vidc_sm_get_min_yc_dpb_sizes(struct ddl_buf_addr *shared_mem,
+	u32 *pn_min_luma_dpb_size, u32 *pn_min_chroma_dpb_size)
+{
+	*pn_min_luma_dpb_size = DDL_MEM_READ_32(shared_mem,
+		VIDC_SM_MIN_LUMA_DPB_SIZE_ADDR);
+	*pn_min_chroma_dpb_size = DDL_MEM_READ_32(shared_mem,
+		VIDC_SM_MIN_CHROMA_DPB_SIZE_ADDR);
+}
+
+void vidc_sm_set_concealment_color(struct ddl_buf_addr *shared_mem,
+	u32 conceal_ycolor, u32 conceal_ccolor)
+{
+	u32 conceal_color;
+
+	conceal_color = (((conceal_ycolor << 8) & 0xff00) |
+		(conceal_ccolor & 0xff));
+	DDL_MEM_WRITE_32(shared_mem, 0x00f0, conceal_color);
+}
+
+void vidc_sm_set_metadata_enable(struct ddl_buf_addr *shared_mem,
+	u32 extradata_enable, u32 qp_enable, u32 concealed_mb_enable,
+	u32 vc1Param_enable, u32 sei_nal_enable, u32 vui_enable,
+	u32 enc_slice_size_enable)
+{
+	u32 metadata_enable;
+
+	metadata_enable = VIDC_SETFIELD((extradata_enable) ? 1 : 0,
+				VIDC_SM_METADATA_ENABLE_EXTRADATA_SHFT,
+				VIDC_SM_METADATA_ENABLE_EXTRADATA_BMSK) |
+				VIDC_SETFIELD((enc_slice_size_enable) ? 1 : 0,
+				VIDC_SM_METADATA_ENABLE_ENC_SLICE_SIZE_SHFT,
+				VIDC_SM_METADATA_ENABLE_ENC_SLICE_SIZE_BMSK) |
+				VIDC_SETFIELD((vui_enable) ? 1 : 0,
+				VIDC_SM_METADATA_ENABLE_VUI_SHFT,
+				VIDC_SM_METADATA_ENABLE_VUI_BMSK) |
+				VIDC_SETFIELD((sei_nal_enable) ? 1 : 0,
+				VIDC_SM_METADATA_ENABLE_SEI_VIDC_SHFT,
+				VIDC_SM_METADATA_ENABLE_SEI_VIDC_BMSK) |
+				VIDC_SETFIELD((vc1Param_enable) ? 1 : 0,
+				VIDC_SM_METADATA_ENABLE_VC1_PARAM_SHFT,
+				VIDC_SM_METADATA_ENABLE_VC1_PARAM_BMSK) |
+				VIDC_SETFIELD((concealed_mb_enable) ? 1 : 0,
+				VIDC_SM_METADATA_ENABLE_CONCEALED_MB_SHFT,
+				VIDC_SM_METADATA_ENABLE_CONCEALED_MB_BMSK) |
+				VIDC_SETFIELD((qp_enable) ? 1 : 0,
+				VIDC_SM_METADATA_ENABLE_QP_SHFT,
+				VIDC_SM_METADATA_ENABLE_QP_BMSK);
+	DDL_MEM_WRITE_32(shared_mem, VIDC_SM_METADATA_ENABLE_ADDR,
+		metadata_enable);
+}
+
+void vidc_sm_get_metadata_status(struct ddl_buf_addr
+		*shared_mem, u32 *pb_metadata_present)
+{
+	u32 status;
+
+	status = DDL_MEM_READ_32(shared_mem, VIDC_SM_METADATA_STATUS_ADDR);
+	*pb_metadata_present = (u32) VIDC_GETFIELD(status,
+				VIDC_SM_METADATA_STATUS_STATUS_BMSK,
+				VIDC_SM_METADATA_STATUS_STATUS_SHFT);
+}
+
+void vidc_sm_get_metadata_display_index(struct ddl_buf_addr *shared_mem,
+	u32 *pn_dixplay_index)
+{
+	*pn_dixplay_index = DDL_MEM_READ_32(shared_mem,
+					VIDC_SM_METADATA_DISPLAY_INDEX_ADDR);
+}
+
+void vidc_sm_set_metadata_start_address(struct ddl_buf_addr *shared_mem,
+	u32 address)
+{
+	u32 address_shift = address;
+
+	DDL_MEM_WRITE_32(shared_mem, VIDC_SM_EXT_METADATA_START_ADDR_ADDR,
+		address_shift);
+}
+
+void vidc_sm_set_extradata_presence(struct ddl_buf_addr *shared_mem,
+	u32 extradata_present)
+{
+	u32 put_extradata;
+
+	put_extradata = VIDC_SETFIELD((extradata_present) ? 1 : 0,
+				VIDC_SM_PUT_EXTRADATA_PUT_SHFT,
+				VIDC_SM_PUT_EXTRADATA_PUT_BMSK);
+	DDL_MEM_WRITE_32(shared_mem, VIDC_SM_PUT_EXTRADATA_ADDR,
+			put_extradata);
+}
+
+void vidc_sm_set_extradata_addr(struct ddl_buf_addr *shared_mem,
+	u32 extradata_addr)
+{
+	u32 address_shift = extradata_addr;
+
+	DDL_MEM_WRITE_32(shared_mem, VIDC_SM_EXTRADATA_ADDR_ADDR,
+		address_shift);
+}
+
+void vidc_sm_set_pand_b_frame_qp(struct ddl_buf_addr *shared_mem,
+	u32 b_frame_qp, u32 p_frame_qp)
+{
+	u32 nP_B_frame_qp;
+
+	nP_B_frame_qp = VIDC_SETFIELD(b_frame_qp,
+				VIDC_SM_P_B_FRAME_QP_B_FRAME_QP_SHFT,
+				VIDC_SM_P_B_FRAME_QP_B_FRAME_QP_BMASK);
+	nP_B_frame_qp |= VIDC_SETFIELD(p_frame_qp,
+				VIDC_SM_P_B_FRAME_QP_P_FRAME_QP_SHFT,
+				VIDC_SM_P_B_FRAME_QP_P_FRAME_QP_BMASK);
+	DDL_MEM_WRITE_32(shared_mem , VIDC_SM_P_B_FRAME_QP_ADDR,
+		nP_B_frame_qp);
+}
+
+
+void vidc_sm_get_profile_info(struct ddl_buf_addr *shared_mem,
+	struct ddl_profile_info_type *ddl_profile_info)
+{
+	u32 disp_pic_profile;
+
+	disp_pic_profile = DDL_MEM_READ_32(shared_mem,
+		VIDC_SM_DISP_PIC_PROFILE_ADDR);
+	ddl_profile_info->bit_depth_chroma_minus8 =
+		(disp_pic_profile  & 0x00380000) >> 19;
+	ddl_profile_info->bit_depth_luma_minus8 =
+		(disp_pic_profile & 0x00070000) >> 16;
+	ddl_profile_info->pic_profile = VIDC_GETFIELD(
+		disp_pic_profile,
+		VIDC_SM_DISP_PIC_PROFILE_DISP_PIC_PROFILE_BMASK,
+		VIDC_SM_DISP_PIC_PROFILE_DISP_PIC_PROFILE_SHFT);
+	ddl_profile_info->pic_level = VIDC_GETFIELD(
+		disp_pic_profile,
+		VIDC_SM_DISP_PIC_PROFILE_DISP_PIC_LEVEL_BMASK,
+		VIDC_SM_DISP_PIC_PROFILE_DISP_PIC_LEVEL_SHFT);
+	ddl_profile_info->chroma_format_idc =
+		(disp_pic_profile & 0x60) >> 5;
+}
+
+void vidc_sm_set_encoder_new_bit_rate(struct ddl_buf_addr *shared_mem,
+	u32 new_bit_rate)
+{
+	DDL_MEM_WRITE_32(shared_mem, VIDC_SM_NEW_RC_BIT_RATE_ADDR,
+		new_bit_rate);
+}
+
+void vidc_sm_set_encoder_new_frame_rate(struct ddl_buf_addr *shared_mem,
+	u32 new_frame_rate)
+{
+	DDL_MEM_WRITE_32(shared_mem, VIDC_SM_NEW_RC_FRAME_RATE_ADDR,
+		new_frame_rate);
+}
+
+void vidc_sm_set_encoder_new_i_period(struct ddl_buf_addr *shared_mem,
+	u32 new_i_period)
+{
+	DDL_MEM_WRITE_32(shared_mem, VIDC_SM_NEW_I_PERIOD_ADDR,
+		new_i_period);
+}
+void vidc_sm_set_encoder_init_rc_value(struct ddl_buf_addr *shared_mem,
+	u32 new_rc_value)
+{
+	DDL_MEM_WRITE_32(shared_mem, 0x011C, new_rc_value);
+
+}
+void vidc_sm_set_idr_decode_only(struct ddl_buf_addr *shared_mem,
+	u32 enable)
+{
+	u32 idr_decode_only = VIDC_SETFIELD((enable) ? 1 : 0,
+			VIDC_SM_IDR_DECODING_ONLY_SHIFT,
+			VIDC_SM_IDR_DECODING_ONLY_BMSK
+			);
+	DDL_MEM_WRITE_32(shared_mem, VIDC_SM_IDR_DECODING_ONLY_ADDR,
+			idr_decode_only);
+}
+
+void vidc_sm_set_chroma_addr_change(struct ddl_buf_addr *shared_mem,
+	u32 addr_change)
+{
+	u32 chroma_addr_change = VIDC_SETFIELD((addr_change) ? 1 : 0,
+					VIDC_SM_CHROMA_ADDR_CHANGE_SHFT,
+					VIDC_SM_CHROMA_ADDR_CHANGE_BMASK);
+	DDL_MEM_WRITE_32(shared_mem, VIDC_SM_CHROMA_ADDR_CHANGE_ADDR,
+					 chroma_addr_change);
+
+}
+
+void vidc_sm_set_mpeg4_profile_override(struct ddl_buf_addr *shared_mem,
+	enum vidc_sm_mpeg4_profileinfo profile_info)
+{
+	u32 profile_enforce = 0;
+	if (shared_mem != NULL) {
+		profile_enforce = 1;
+		switch (profile_info) {
+		case VIDC_SM_PROFILE_INFO_ASP:
+			profile_enforce |= 4;
+			break;
+		case VIDC_SM_PROFILE_INFO_SP:
+			profile_enforce |= 2;
+			break;
+		case VIDC_SM_PROFILE_INFO_DISABLE:
+		default:
+			profile_enforce = 0;
+			break;
+		}
+		DDL_MEM_WRITE_32(shared_mem, 0x15c, profile_enforce);
+	}
+}
+void vidc_sm_set_decoder_sei_enable(struct ddl_buf_addr *shared_mem,
+	u32 sei_enable)
+{
+	DDL_MEM_WRITE_32(shared_mem, VIDC_SM_SEI_ENABLE_ADDR, sei_enable);
+}
+
+void vidc_sm_get_decoder_sei_enable(struct ddl_buf_addr *shared_mem,
+	u32 *sei_enable)
+{
+	*sei_enable = DDL_MEM_READ_32(shared_mem, VIDC_SM_SEI_ENABLE_ADDR);
+}
+
+void vidc_sm_set_error_concealment_config(struct ddl_buf_addr *shared_mem,
+	u32 inter_slice, u32 intra_slice, u32 conceal_config_enable)
+{
+	u32 error_conceal_config = 0;
+
+	error_conceal_config = VIDC_SETFIELD(inter_slice,
+			VIDC_SM_ERROR_CONCEALMENT_CONFIG_INTER_SLICE_SHFT,
+			VIDC_SM_ERROR_CONCEALMENT_CONFIG_INTER_SLICE_BMSK);
+
+	error_conceal_config |= VIDC_SETFIELD(intra_slice,
+			VIDC_SM_ERROR_CONCEALMENT_CONFIG_INTRA_SLICE_SHFT,
+			VIDC_SM_ERROR_CONCEALMENT_CONFIG_INTRA_SLICE_BMSK);
+
+	error_conceal_config |= VIDC_SETFIELD(conceal_config_enable,
+			VIDC_SM_ERROR_CONCEALMENT_CONFIG_CONCEAL_ENABLE_SHFT,
+			VIDC_SM_ERROR_CONCEALMENT_CONFIG_CONCEAL_ENABLE_BMSK);
+
+	DDL_MEM_WRITE_32(shared_mem, VIDC_SM_ERROR_CONCEALMENT_CONFIG_ADDR,
+			error_conceal_config);
+}
+
+void vidc_sm_set_decoder_stuff_bytes_consumption(
+	struct ddl_buf_addr *shared_mem,
+	enum vidc_sm_num_stuff_bytes_consume_info consume_info)
+{
+	DDL_MEM_WRITE_32(shared_mem, VIDC_SM_NUM_STUFF_BYTES_CONSUME_ADDR,
+	consume_info);
+}
+
+void vidc_sm_get_aspect_ratio_info(struct ddl_buf_addr *shared_mem,
+	struct vcd_aspect_ratio *aspect_ratio_info)
+{
+	u32 extended_par_info = 0;
+	aspect_ratio_info->aspect_ratio = DDL_MEM_READ_32(shared_mem,
+				VIDC_SM_ASPECT_RATIO_INFO_ADDR);
+
+	if (aspect_ratio_info->aspect_ratio == 0x0f) {
+		extended_par_info = DDL_MEM_READ_32(shared_mem,
+			VIDC_SM_EXTENDED_PAR_ADDR);
+		aspect_ratio_info->extended_par_width =
+			VIDC_GETFIELD(extended_par_info,
+			VIDC_SM_EXTENDED_PAR_WIDTH_BMSK,
+			VIDC_SM_EXTENDED_PAR_WIDTH_SHFT);
+		aspect_ratio_info->extended_par_height =
+			VIDC_GETFIELD(extended_par_info,
+			VIDC_SM_EXTENDED_PAR_HEIGHT_BMSK,
+			VIDC_SM_EXTENDED_PAR_HEIGHT_SHFT);
+	}
+}
+
+void vidc_sm_set_encoder_slice_batch_int_ctrl(struct ddl_buf_addr *shared_mem,
+	u32 slice_batch_int_enable)
+{
+	u32 slice_batch_int_ctrl = VIDC_SETFIELD((slice_batch_int_enable) ?
+				1 : 0,
+				VIDC_SM_ENC_EXT_CTRL_HEC_ENABLE_SHFT,
+				VIDC_SM_ENC_EXT_CTRL_HEC_ENABLE_BMSK);
+	DDL_MEM_WRITE_32(shared_mem,
+			VIDC_SM_ENC_SLICE_BATCH_INT_CTRL_ADDR,
+			slice_batch_int_ctrl);
+}
+
+void vidc_sm_get_num_slices_comp(struct ddl_buf_addr *shared_mem,
+	u32 *num_slices_comp)
+{
+	*num_slices_comp = DDL_MEM_READ_32(shared_mem,
+				VIDC_SM_ENC_NUM_OF_SLICE_COMP_ADDR);
+}
+
+void vidc_sm_set_encoder_batch_config(struct ddl_buf_addr *shared_mem,
+				u32 num_slices,
+				u32 input_addr, u32 output_addr,
+				u32 output_buffer_size)
+{
+	DDL_MEM_WRITE_32(shared_mem,
+			VIDC_SM_ENC_NUM_OF_SLICE_ADDR,
+			num_slices);
+	DDL_MEM_WRITE_32(shared_mem,
+			VIDC_SM_BATCH_INPUT_ADDR,
+			input_addr);
+	DDL_MEM_WRITE_32(shared_mem,
+			VIDC_SM_BATCH_OUTPUT_ADDR,
+			output_addr);
+	DDL_MEM_WRITE_32(shared_mem,
+			VIDC_SM_BATCH_OUTPUT_SIZE_ADDR,
+			output_buffer_size);
+}
+
+void vidc_sm_get_encoder_batch_output_size(struct ddl_buf_addr *shared_mem,
+	u32 *output_buffer_size)
+{
+	*output_buffer_size = DDL_MEM_READ_32(shared_mem,
+			VIDC_SM_BATCH_OUTPUT_SIZE_ADDR);
+}
+
+void vidc_sm_set_video_core_timeout_value(struct ddl_buf_addr *shared_mem,
+	u32 timeout)
+{
+	DDL_MEM_WRITE_32(shared_mem, VIDC_SM_TIMEOUT_VALUE_ADDR,
+			timeout);
+}
+
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_shared_mem.h b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_shared_mem.h
new file mode 100644
index 0000000..6cd75595
--- /dev/null
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_shared_mem.h
@@ -0,0 +1,196 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VCD_DDL_SHARED_MEM_H_
+#define _VCD_DDL_SHARED_MEM_H_
+
+#include "vcd_ddl.h"
+
+#define VIDC_SM_PROFILE_MPEG4_SIMPLE      (0)
+#define VIDC_SM_PROFILE_MPEG4_ADV_SIMPLE  (1)
+
+#define VIDC_SM_PROFILE_H264_BASELINE     (0)
+#define VIDC_SM_PROFILE_H264_MAIN         (1)
+#define VIDC_SM_PROFILE_H264_HIGH         (2)
+
+#define VIDC_SM_PROFILE_H263_BASELINE     (0)
+
+#define VIDC_SM_PROFILE_VC1_SIMPLE        (0)
+#define VIDC_SM_PROFILE_VC1_MAIN          (1)
+#define VIDC_SM_PROFILE_VC1_ADVANCED      (2)
+
+#define VIDC_SM_PROFILE_MPEG2_MAIN        (4)
+#define VIDC_SM_PROFILE_MPEG2_SIMPLE      (5)
+
+#define VIDC_SM_LEVEL_MPEG2_LOW        (10)
+#define VIDC_SM_LEVEL_MPEG2_MAIN        (8)
+#define VIDC_SM_LEVEL_MPEG2_HIGH_1440   (6)
+#define VIDC_SM_LEVEL_MPEG2_HIGH        (4)
+
+#define VIDC_SM_LEVEL_VC1_LOW     (0)
+#define VIDC_SM_LEVEL_VC1_MEDIUM  (2)
+#define VIDC_SM_LEVEL_VC1_HIGH    (4)
+
+#define VIDC_SM_LEVEL_VC1_ADV_0  (0)
+#define VIDC_SM_LEVEL_VC1_ADV_1  (1)
+#define VIDC_SM_LEVEL_VC1_ADV_2  (2)
+#define VIDC_SM_LEVEL_VC1_ADV_3  (3)
+#define VIDC_SM_LEVEL_VC1_ADV_4  (4)
+
+#define VIDC_SM_RECOVERY_POINT_SEI  (1)
+enum VIDC_SM_frame_skip {
+	VIDC_SM_FRAME_SKIP_DISABLE      = 0,
+	VIDC_SM_FRAME_SKIP_ENABLE_LEVEL = 1,
+	VIDC_SM_FRAME_SKIP_ENABLE_VBV   = 2
+};
+enum VIDC_SM_ref_picture {
+	VIDC_SM_REF_PICT_FRAME_OR_TOP_FIELD   = 0,
+	VIDC_SM_REF_PICT_BOTTOM_FIELD         = 1
+};
+
+struct ddl_profile_info_type {
+	u32 bit_depth_chroma_minus8;
+	u32 bit_depth_luma_minus8;
+	u32 pic_level;
+	u32 chroma_format_idc;
+	u32 pic_profile;
+};
+
+enum vidc_sm_mpeg4_profileinfo {
+	VIDC_SM_PROFILE_INFO_DISABLE  = 0,
+	VIDC_SM_PROFILE_INFO_SP       = 1,
+	VIDC_SM_PROFILE_INFO_ASP      = 2,
+	VIDC_SM_PROFILE_INFO_MAX      = 0x7fffffff
+};
+
+enum vidc_sm_num_stuff_bytes_consume_info {
+	VIDC_SM_NUM_STUFF_BYTES_CONSUME_ALL  = 0x0,
+	VIDC_SM_NUM_STUFF_BYTES_CONSUME_NONE = 0xffffffff
+};
+
+void vidc_sm_get_extended_decode_status(struct ddl_buf_addr *shared_mem,
+	u32 *more_field_needed,
+	u32 *resl_change);
+void vidc_sm_set_frame_tag(struct ddl_buf_addr *shared_mem,
+	u32 frame_tag);
+void vidc_sm_get_frame_tags(struct ddl_buf_addr *shared_mem,
+	u32 *pn_frame_tag_top, u32 *pn_frame_tag_bottom);
+void vidc_sm_get_picture_times(struct ddl_buf_addr *shared_mem,
+	u32 *pn_time_top, u32 *pn_time_bottom);
+void vidc_sm_set_start_byte_number(struct ddl_buf_addr *shared_mem,
+	u32 byte_num);
+void vidc_sm_get_crop_info(struct ddl_buf_addr *shared_mem, u32 *pn_left,
+	u32 *pn_right, u32 *pn_top, u32 *pn_bottom);
+void vidc_sm_get_displayed_picture_frame(struct ddl_buf_addr
+	*shared_mem, u32 *n_disp_picture_frame);
+void vidc_sm_get_available_luma_dpb_address(
+	struct ddl_buf_addr *shared_mem, u32 *pn_free_luma_dpb_address);
+void vidc_sm_get_available_luma_dpb_dec_order_address(
+	struct ddl_buf_addr *shared_mem, u32 *pn_free_luma_dpb_address);
+void vidc_sm_get_dec_order_resl(
+	struct ddl_buf_addr *shared_mem, u32 *width, u32 *height);
+void vidc_sm_get_dec_order_crop_info(
+	struct ddl_buf_addr *shared_mem, u32 *left,
+	u32 *right, u32 *top, u32 *bottom);
+void vidc_sm_set_extended_encoder_control(
+	struct ddl_buf_addr *shared_mem, u32 hec_enable,
+	enum VIDC_SM_frame_skip  frame_skip_mode, u32 seq_hdr_in_band,
+	u32 vbv_buffer_size, u32 cpcfc_enable, u32 sps_pps_control,
+	u32 closed_gop_enable);
+void vidc_sm_set_encoder_param_change(struct ddl_buf_addr *shared_mem,
+	u32 bit_rate_chg, u32 frame_rate_chg, u32 i_period_chg);
+void vidc_sm_set_encoder_vop_time(struct ddl_buf_addr *shared_mem,
+	u32 vop_time_enable, u32 time_resolution, u32 frame_delta);
+void vidc_sm_set_encoder_hec_period(struct ddl_buf_addr *shared_mem,
+	u32 hec_period);
+void vidc_sm_get_h264_encoder_reference_list0(
+	struct ddl_buf_addr *shared_mem,
+	enum VIDC_SM_ref_picture *pe_luma_picture0,
+	u32 *pn_luma_picture_index0,
+	enum VIDC_SM_ref_picture *pe_luma_picture1,
+	u32 *pn_luma_picture_index1,
+	enum VIDC_SM_ref_picture *pe_chroma_picture0,
+	u32 *pn_chroma_picture_index0,
+	enum VIDC_SM_ref_picture *pe_chroma_picture1,
+	u32 *pn_chroma_picture_index1);
+
+void vidc_sm_get_h264_encoder_reference_list1(
+	struct ddl_buf_addr *shared_mem,
+	enum VIDC_SM_ref_picture *pe_luma_picture,
+	u32 *pn_luma_picture_index,
+	enum VIDC_SM_ref_picture *pe_chroma_picture,
+	u32 *pn_chroma_picture_index);
+void vidc_sm_set_allocated_dpb_size(struct ddl_buf_addr *shared_mem,
+	u32 y_size, u32 c_size);
+void vidc_sm_set_allocated_h264_mv_size(struct ddl_buf_addr *shared_mem,
+	u32 mv_size);
+void vidc_sm_get_min_yc_dpb_sizes(struct ddl_buf_addr *shared_mem,
+	u32 *pn_min_luma_dpb_size, u32 *pn_min_chroma_dpb_size);
+void vidc_sm_set_metadata_enable(struct ddl_buf_addr *shared_mem,
+	u32 extradata_enable, u32 qp_enable, u32 concealed_mb_enable,
+	u32 vc1Param_enable, u32 sei_nal_enable, u32 vui_enable,
+	u32 enc_slice_size_enable);
+void vidc_sm_get_metadata_status(struct ddl_buf_addr *shared_mem,
+	u32 *pb_metadata_present);
+void vidc_sm_get_metadata_display_index(struct ddl_buf_addr *shared_mem,
+	u32 *pn_dixplay_index);
+void vidc_sm_set_metadata_start_address(struct ddl_buf_addr *shared_mem,
+	u32 address);
+void vidc_sm_set_extradata_presence(struct ddl_buf_addr *shared_mem,
+	u32 extradata_present);
+void vidc_sm_set_extradata_addr(struct ddl_buf_addr *shared_mem,
+	u32 extradata_addr);
+void vidc_sm_set_pand_b_frame_qp(struct ddl_buf_addr *shared_mem,
+	u32 b_frame_qp, u32 p_frame_qp);
+void vidc_sm_get_profile_info(struct ddl_buf_addr *shared_mem,
+	struct ddl_profile_info_type *ddl_profile_info);
+void vidc_sm_set_encoder_new_bit_rate(struct ddl_buf_addr *shared_mem,
+	u32 new_bit_rate);
+void vidc_sm_set_encoder_new_frame_rate(struct ddl_buf_addr *shared_mem,
+	u32 new_frame_rate);
+void vidc_sm_set_encoder_new_i_period(struct ddl_buf_addr *shared_mem,
+	u32 new_i_period);
+void vidc_sm_set_encoder_init_rc_value(struct ddl_buf_addr *shared_mem,
+	u32 new_rc_value);
+void vidc_sm_set_idr_decode_only(struct ddl_buf_addr *shared_mem,
+	u32 enable);
+void vidc_sm_set_concealment_color(struct ddl_buf_addr *shared_mem,
+	u32 conceal_ycolor, u32 conceal_ccolor);
+void vidc_sm_set_chroma_addr_change(struct ddl_buf_addr *shared_mem,
+	u32 addr_change);
+void vidc_sm_set_mpeg4_profile_override(struct ddl_buf_addr *shared_mem,
+	enum vidc_sm_mpeg4_profileinfo profile_info);
+void vidc_sm_set_decoder_sei_enable(struct ddl_buf_addr *shared_mem,
+	u32 sei_enable);
+void vidc_sm_get_decoder_sei_enable(struct ddl_buf_addr *shared_mem,
+	u32 *sei_enable);
+void vidc_sm_set_error_concealment_config(struct ddl_buf_addr *shared_mem,
+	u32 inter_slice, u32 intra_slice, u32 conceal_config_enable);
+void vidc_sm_set_decoder_stuff_bytes_consumption(
+	struct ddl_buf_addr *shared_mem,
+	enum vidc_sm_num_stuff_bytes_consume_info consume_info);
+void vidc_sm_get_aspect_ratio_info(struct ddl_buf_addr *shared_mem,
+	struct vcd_aspect_ratio *aspect_ratio_info);
+void vidc_sm_set_encoder_slice_batch_int_ctrl(struct ddl_buf_addr *shared_mem,
+	u32 slice_batch_int_enable);
+void vidc_sm_get_num_slices_comp(struct ddl_buf_addr *shared_mem,
+	u32 *num_slices_comp);
+void vidc_sm_set_encoder_batch_config(struct ddl_buf_addr *shared_mem,
+				u32 num_slices,
+				u32 input_addr, u32 output_addr,
+				u32 output_buffer_size);
+void vidc_sm_get_encoder_batch_output_size(struct ddl_buf_addr *shared_mem,
+	u32 *output_buffer_size);
+void vidc_sm_set_video_core_timeout_value(struct ddl_buf_addr *shared_mem,
+	u32 timeout);
+#endif
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_utils.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_utils.c
new file mode 100644
index 0000000..260cd72
--- /dev/null
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_utils.c
@@ -0,0 +1,518 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/memory_alloc.h>
+#include <linux/delay.h>
+#include <mach/msm_subsystem_map.h>
+#include <mach/peripheral-loader.h>
+#include "vcd_ddl_utils.h"
+#include "vcd_ddl.h"
+#include "vcd_res_tracker_api.h"
+
+struct time_data {
+	unsigned int ddl_t1;
+	unsigned int ddl_ttotal;
+	unsigned int ddl_count;
+};
+static struct time_data proc_time[MAX_TIME_DATA];
+#define DDL_MSG_TIME(x...) printk(KERN_DEBUG x)
+static unsigned int vidc_mmu_subsystem[] =	{
+		MSM_SUBSYSTEM_VIDEO, MSM_SUBSYSTEM_VIDEO_FWARE};
+
+#ifdef DDL_BUF_LOG
+static void ddl_print_buffer(struct ddl_context *ddl_context,
+	struct ddl_buf_addr *buf, u32 idx, u8 *str);
+static void ddl_print_port(struct ddl_context *ddl_context,
+	struct ddl_buf_addr *buf);
+static void ddl_print_buffer_port(struct ddl_context *ddl_context,
+	struct ddl_buf_addr *buf, u32 idx, u8 *str);
+#endif
+void *ddl_pmem_alloc(struct ddl_buf_addr *addr, size_t sz, u32 alignment)
+{
+	u32 alloc_size, offset = 0 ;
+	u32 index = 0;
+	struct ddl_context *ddl_context;
+	struct msm_mapped_buffer *mapped_buffer = NULL;
+	unsigned long iova = 0;
+	unsigned long buffer_size = 0;
+	unsigned long *kernel_vaddr = NULL;
+	unsigned long ionflag = 0;
+	unsigned long flags = 0;
+	int ret = 0;
+	ion_phys_addr_t phyaddr = 0;
+	size_t len = 0;
+	int rc = 0;
+	DBG_PMEM("\n%s() IN: Requested alloc size(%u)", __func__, (u32)sz);
+	if (!addr) {
+		DDL_MSG_ERROR("\n%s() Invalid Parameters", __func__);
+		goto bail_out;
+	}
+	ddl_context = ddl_get_context();
+	res_trk_set_mem_type(addr->mem_type);
+	alloc_size = (sz + alignment);
+	if (res_trk_get_enable_ion()) {
+		if (!ddl_context->video_ion_client)
+			ddl_context->video_ion_client =
+				res_trk_get_ion_client();
+		if (!ddl_context->video_ion_client) {
+			DDL_MSG_ERROR("%s() :DDL ION Client Invalid handle\n",
+						 __func__);
+			goto bail_out;
+		}
+		alloc_size = (alloc_size+4095) & ~4095;
+		addr->alloc_handle = ion_alloc(
+		ddl_context->video_ion_client, alloc_size, SZ_4K,
+			res_trk_get_mem_type());
+		if (IS_ERR_OR_NULL(addr->alloc_handle)) {
+			DDL_MSG_ERROR("%s() :DDL ION alloc failed\n",
+						 __func__);
+			goto bail_out;
+		}
+		if (res_trk_check_for_sec_session() ||
+			addr->mem_type == DDL_FW_MEM)
+			ionflag = UNCACHED;
+		else
+			ionflag = CACHED;
+		kernel_vaddr = (unsigned long *) ion_map_kernel(
+					ddl_context->video_ion_client,
+					addr->alloc_handle, ionflag);
+		if (IS_ERR_OR_NULL(kernel_vaddr)) {
+				DDL_MSG_ERROR("%s() :DDL ION map failed\n",
+							 __func__);
+				goto free_ion_alloc;
+		}
+		addr->virtual_base_addr = (u8 *) kernel_vaddr;
+		if (res_trk_check_for_sec_session()) {
+			rc = ion_phys(ddl_context->video_ion_client,
+				addr->alloc_handle, &phyaddr,
+					&len);
+			if (rc || !phyaddr) {
+				DDL_MSG_ERROR(
+				"%s():DDL ION client physical failed\n",
+				__func__);
+				goto unmap_ion_alloc;
+			}
+			addr->alloced_phys_addr = phyaddr;
+		} else {
+			ret = ion_map_iommu(ddl_context->video_ion_client,
+					addr->alloc_handle,
+					VIDEO_DOMAIN,
+					VIDEO_MAIN_POOL,
+					SZ_4K,
+					0,
+					&iova,
+					&buffer_size,
+					UNCACHED, 0);
+			if (ret) {
+				DDL_MSG_ERROR(
+				"%s():DDL ION ion map iommu failed\n",
+				 __func__);
+				goto unmap_ion_alloc;
+			}
+			addr->alloced_phys_addr = (phys_addr_t) iova;
+		}
+		if (!addr->alloced_phys_addr) {
+			DDL_MSG_ERROR("%s():DDL ION client physical failed\n",
+						__func__);
+			goto unmap_ion_alloc;
+		}
+		addr->mapped_buffer = NULL;
+		addr->physical_base_addr = (u8 *) addr->alloced_phys_addr;
+		addr->align_physical_addr = (u8 *) DDL_ALIGN((u32)
+			addr->physical_base_addr, alignment);
+		offset = (u32)(addr->align_physical_addr -
+				addr->physical_base_addr);
+		addr->align_virtual_addr = addr->virtual_base_addr + offset;
+		addr->buffer_size = alloc_size;
+	} else {
+		addr->alloced_phys_addr = (phys_addr_t)
+		allocate_contiguous_memory_nomap(alloc_size,
+			res_trk_get_mem_type(), SZ_4K);
+		if (!addr->alloced_phys_addr) {
+			DDL_MSG_ERROR("%s() : acm alloc failed (%d)\n",
+					 __func__, alloc_size);
+			goto bail_out;
+		}
+		flags = MSM_SUBSYSTEM_MAP_IOVA | MSM_SUBSYSTEM_MAP_KADDR;
+		if (alignment == DDL_KILO_BYTE(128))
+				index = 1;
+		else if (alignment > SZ_4K)
+			flags |= MSM_SUBSYSTEM_ALIGN_IOVA_8K;
+
+		addr->mapped_buffer =
+		msm_subsystem_map_buffer((unsigned long)addr->alloced_phys_addr,
+			alloc_size, flags, &vidc_mmu_subsystem[index],
+			sizeof(vidc_mmu_subsystem[index])/sizeof(unsigned int));
+		if (IS_ERR(addr->mapped_buffer)) {
+			pr_err(" %s() buffer map failed", __func__);
+			goto free_acm_alloc;
+		}
+		mapped_buffer = addr->mapped_buffer;
+		if (!mapped_buffer->vaddr || !mapped_buffer->iova[0]) {
+			pr_err("%s() map buffers failed\n", __func__);
+			goto free_map_buffers;
+		}
+		addr->physical_base_addr = (u8 *)mapped_buffer->iova[0];
+		addr->virtual_base_addr = mapped_buffer->vaddr;
+		addr->align_physical_addr = (u8 *) DDL_ALIGN((u32)
+			addr->physical_base_addr, alignment);
+		offset = (u32)(addr->align_physical_addr -
+				addr->physical_base_addr);
+		addr->align_virtual_addr = addr->virtual_base_addr + offset;
+		addr->buffer_size = sz;
+	}
+	return addr->virtual_base_addr;
+free_map_buffers:
+	msm_subsystem_unmap_buffer(addr->mapped_buffer);
+	addr->mapped_buffer = NULL;
+free_acm_alloc:
+		free_contiguous_memory_by_paddr(
+			(unsigned long)addr->alloced_phys_addr);
+		addr->alloced_phys_addr = (phys_addr_t)NULL;
+		return NULL;
+unmap_ion_alloc:
+	ion_unmap_kernel(ddl_context->video_ion_client,
+		addr->alloc_handle);
+	addr->virtual_base_addr = NULL;
+	addr->alloced_phys_addr = (phys_addr_t)NULL;
+free_ion_alloc:
+	ion_free(ddl_context->video_ion_client,
+		addr->alloc_handle);
+	addr->alloc_handle = NULL;
+bail_out:
+	return NULL;
+}
+
+void ddl_pmem_free(struct ddl_buf_addr *addr)
+{
+	struct ddl_context *ddl_context;
+	ddl_context = ddl_get_context();
+	if (!addr) {
+		pr_err("%s() invalid args\n", __func__);
+		return;
+	}
+	if (ddl_context->video_ion_client) {
+		if (!IS_ERR_OR_NULL(addr->alloc_handle)) {
+			ion_unmap_kernel(ddl_context->video_ion_client,
+					addr->alloc_handle);
+			if (!res_trk_check_for_sec_session()) {
+				ion_unmap_iommu(ddl_context->video_ion_client,
+					addr->alloc_handle,
+					VIDEO_DOMAIN,
+					VIDEO_MAIN_POOL);
+			}
+			ion_free(ddl_context->video_ion_client,
+				addr->alloc_handle);
+			}
+	} else {
+		if (addr->mapped_buffer)
+			msm_subsystem_unmap_buffer(addr->mapped_buffer);
+		if (addr->alloced_phys_addr)
+			free_contiguous_memory_by_paddr(
+				(unsigned long)addr->alloced_phys_addr);
+	}
+	memset(addr, 0, sizeof(struct ddl_buf_addr));
+}
+
+#ifdef DDL_BUF_LOG
+
+static void ddl_print_buffer(struct ddl_context *ddl_context,
+	struct ddl_buf_addr *buf, u32 idx, u8 *str)
+{
+	struct ddl_buf_addr *base_ram;
+	s32  offset;
+	size_t sz, KB = 0;
+
+	base_ram = &ddl_context->dram_base_a;
+	offset = (s32) DDL_ADDR_OFFSET(*base_ram, *buf);
+	sz = buf->buffer_size;
+	if (sz > 0) {
+		if (!(sz % 1024)) {
+			sz /= 1024;
+			KB++;
+			if (!(sz % 1024)) {
+				sz /= 1024;
+				KB++;
+			}
+		}
+	}
+	DDL_MSG_LOW("\n%12s [%2d]:  0x%08x [0x%04x],  0x%08x(%d%s),  %s",
+		str, idx, (u32) buf->align_physical_addr,
+		(offset > 0) ? offset : 0, buf->buffer_size, sz,
+		((2 == KB) ? "MB" : (1 == KB) ? "KB" : ""),
+		(((u32) buf->virtual_base_addr) ? "Alloc" : ""));
+}
+
+static void ddl_print_port(struct ddl_context *ddl_context,
+	struct ddl_buf_addr *buf)
+{
+	struct ddl_buf_addr *a = &ddl_context->dram_base_a;
+	struct ddl_buf_addr *b = &ddl_context->dram_base_b;
+
+	if (!buf->align_physical_addr || !buf->buffer_size)
+		return;
+	if (buf->align_physical_addr >= a->align_physical_addr &&
+		buf->align_physical_addr + buf->buffer_size <=
+		a->align_physical_addr + a->buffer_size)
+		DDL_MSG_LOW(" -A [0x%x]-", DDL_ADDR_OFFSET(*a, *buf));
+	else if (buf->align_physical_addr >= b->align_physical_addr &&
+		buf->align_physical_addr + buf->buffer_size <=
+		b->align_physical_addr + b->buffer_size)
+		DDL_MSG_LOW(" -B [0x%x]-", DDL_ADDR_OFFSET(*b, *buf));
+	else
+		DDL_MSG_LOW(" -?-");
+}
+
+static void ddl_print_buffer_port(struct ddl_context *ddl_context,
+	struct ddl_buf_addr *buf, u32 idx, u8 *str)
+{
+	DDL_MSG_LOW("\n");
+	ddl_print_buffer(ddl_context, buf, idx, str);
+	ddl_print_port(ddl_context, buf);
+}
+
+void ddl_list_buffers(struct ddl_client_context *ddl)
+{
+	struct ddl_context *ddl_context;
+	u32 i;
+
+	ddl_context = ddl->ddl_context;
+	DDL_MSG_LOW("\n\n");
+	DDL_MSG_LOW("\n      Buffer     :     Start    [offs],      Size    \
+	(Size),     Alloc/Port");
+	DDL_MSG_LOW("\n-------------------------------------------------------\
+	-------------------------");
+	ddl_print_buffer(ddl_context, &ddl_context->dram_base_a, 0,
+		"dram_base_a");
+	ddl_print_buffer(ddl_context, &ddl_context->dram_base_b, 0,
+		"dram_base_b");
+	if (ddl->codec_data.hdr.decoding) {
+		struct ddl_dec_buffers  *dec_bufs =
+			&ddl->codec_data.decoder.hw_bufs;
+		for (i = 0; i < 32; i++)
+			ddl_print_buffer_port(ddl_context,
+				&dec_bufs->h264Mv[i], i, "h264Mv");
+		ddl_print_buffer_port(ddl_context,
+			&dec_bufs->h264Vert_nb_mv, 0, "h264Vert_nb_mv");
+		ddl_print_buffer_port(ddl_context,
+			&dec_bufs->h264Nb_ip, 0, "h264Nb_ip");
+		ddl_print_buffer_port(ddl_context,
+			&dec_bufs->nb_dcac, 0, "nb_dcac");
+		ddl_print_buffer_port(ddl_context,
+			&dec_bufs->upnb_mv, 0, "upnb_mv");
+		ddl_print_buffer_port(ddl_context,
+			&dec_bufs->sub_anchor_mv, 0, "sub_anchor_mv");
+		ddl_print_buffer_port(ddl_context,
+			&dec_bufs->overlay_xform, 0, "overlay_xform");
+		ddl_print_buffer_port(ddl_context,
+			&dec_bufs->bit_plane3, 0, "bit_plane3");
+		ddl_print_buffer_port(ddl_context,
+			&dec_bufs->bit_plane2, 0, "bit_plane2");
+		ddl_print_buffer_port(ddl_context,
+			&dec_bufs->bit_plane1, 0, "bit_plane1");
+		ddl_print_buffer_port(ddl_context,
+			dec_bufs->stx_parser, 0, "stx_parser");
+		ddl_print_buffer_port(ddl_context,
+			&dec_bufs->desc, 0, "desc");
+		ddl_print_buffer_port(ddl_context,
+			&dec_bufs->context, 0, "context");
+	} else {
+		struct ddl_enc_buffers  *enc_bufs =
+			&ddl->codec_data.encoder.hw_bufs;
+
+		for (i = 0; i < 4; i++)
+			ddl_print_buffer_port(ddl_context,
+				&enc_bufs->dpb_y[i], i, "dpb_y");
+		for (i = 0; i < 4; i++)
+			ddl_print_buffer_port(ddl_context,
+				&enc_bufs->dpb_c[i], i, "dpb_c");
+		ddl_print_buffer_port(ddl_context, &enc_bufs->mv, 0, "mv");
+		ddl_print_buffer_port(ddl_context,
+			&enc_bufs->col_zero, 0, "col_zero");
+		ddl_print_buffer_port(ddl_context, &enc_bufs->md, 0, "md");
+		ddl_print_buffer_port(ddl_context,
+			&enc_bufs->pred, 0, "pred");
+		ddl_print_buffer_port(ddl_context,
+			&enc_bufs->nbor_info, 0, "nbor_info");
+		ddl_print_buffer_port(ddl_context,
+			&enc_bufs->acdc_coef, 0, "acdc_coef");
+		ddl_print_buffer_port(ddl_context,
+			&enc_bufs->context, 0, "context");
+	}
+}
+#endif
+
+u32 ddl_fw_init(struct ddl_buf_addr *dram_base)
+{
+	u8 *dest_addr;
+	dest_addr = DDL_GET_ALIGNED_VITUAL(*dram_base);
+	DDL_MSG_LOW("FW Addr / FW Size : %x/%d", (u32)vidc_video_codec_fw,
+		vidc_video_codec_fw_size);
+	if (res_trk_check_for_sec_session() && res_trk_is_cp_enabled()) {
+		if (res_trk_enable_footswitch()) {
+			pr_err("Failed to enable footswitch");
+			return false;
+		}
+		if (res_trk_enable_iommu_clocks()) {
+			res_trk_disable_footswitch();
+			pr_err("Failed to enable iommu clocks\n");
+			return false;
+		}
+		dram_base->pil_cookie = pil_get("vidc");
+		if (res_trk_disable_iommu_clocks())
+			pr_err("Failed to disable iommu clocks\n");
+		if (IS_ERR_OR_NULL(dram_base->pil_cookie)) {
+			res_trk_disable_footswitch();
+			pr_err("pil_get failed\n");
+			return false;
+		}
+	} else {
+		if (vidc_video_codec_fw_size > dram_base->buffer_size ||
+				!vidc_video_codec_fw)
+			return false;
+		memcpy(dest_addr, vidc_video_codec_fw,
+				vidc_video_codec_fw_size);
+	}
+	return true;
+}
+
+void ddl_fw_release(struct ddl_buf_addr *dram_base)
+{
+	void *cookie = dram_base->pil_cookie;
+	if (res_trk_is_cp_enabled() &&
+		res_trk_check_for_sec_session()) {
+		res_trk_close_secure_session();
+		if (IS_ERR_OR_NULL(cookie)) {
+			pr_err("Invalid params");
+		return;
+	}
+	if (res_trk_enable_footswitch()) {
+		pr_err("Failed to enable footswitch");
+		return;
+	}
+	if (res_trk_enable_iommu_clocks()) {
+		res_trk_disable_footswitch();
+		pr_err("Failed to enable iommu clocks\n");
+		return;
+	}
+	pil_put(cookie);
+	if (res_trk_disable_iommu_clocks())
+		pr_err("Failed to disable iommu clocks\n");
+	if (res_trk_disable_footswitch())
+		pr_err("Failed to disable footswitch\n");
+	} else {
+	if (res_trk_check_for_sec_session())
+		res_trk_close_secure_session();
+		res_trk_release_fw_addr();
+	}
+}
+
+void ddl_set_core_start_time(const char *func_name, u32 index)
+{
+	u32 act_time;
+	struct timeval ddl_tv;
+	struct time_data *time_data = &proc_time[index];
+	do_gettimeofday(&ddl_tv);
+	act_time = (ddl_tv.tv_sec * 1000) + (ddl_tv.tv_usec / 1000);
+	if (!time_data->ddl_t1) {
+		time_data->ddl_t1 = act_time;
+		DDL_MSG_LOW("\n%s(): Start Time (%u)", func_name, act_time);
+	} else if (vidc_msg_timing) {
+		DDL_MSG_TIME("\n%s(): Timer already started! St(%u) Act(%u)",
+			func_name, time_data->ddl_t1, act_time);
+	}
+}
+
+void ddl_calc_core_proc_time(const char *func_name, u32 index,
+			struct ddl_client_context *ddl)
+{
+	struct time_data *time_data = &proc_time[index];
+	struct ddl_decoder_data *decoder = NULL;
+	if (time_data->ddl_t1) {
+		int ddl_t2;
+		struct timeval ddl_tv;
+		do_gettimeofday(&ddl_tv);
+		ddl_t2 = (ddl_tv.tv_sec * 1000) + (ddl_tv.tv_usec / 1000);
+		time_data->ddl_ttotal += (ddl_t2 - time_data->ddl_t1);
+		time_data->ddl_count++;
+		if (vidc_msg_timing) {
+			DDL_MSG_TIME("\n%s(): cnt(%u) End Time (%u)"
+				"Diff(%u) Avg(%u)",
+				func_name, time_data->ddl_count, ddl_t2,
+				ddl_t2 - time_data->ddl_t1,
+				time_data->ddl_ttotal/time_data->ddl_count);
+		}
+		if ((index == DEC_OP_TIME) && (time_data->ddl_count > 2) &&
+					(time_data->ddl_count < 6)) {
+			decoder = &(ddl->codec_data.decoder);
+			decoder->dec_time_sum = decoder->dec_time_sum +
+				ddl_t2 - time_data->ddl_t1;
+			if (time_data->ddl_count == 5)
+				decoder->avg_dec_time =
+					decoder->dec_time_sum / 3;
+		}
+		time_data->ddl_t1 = 0;
+	}
+}
+
+void ddl_calc_core_proc_time_cnt(const char *func_name, u32 index, u32 count)
+{
+	struct time_data *time_data = &proc_time[index];
+	if (time_data->ddl_t1) {
+		int ddl_t2;
+		struct timeval ddl_tv;
+		do_gettimeofday(&ddl_tv);
+		ddl_t2 = (ddl_tv.tv_sec * 1000) + (ddl_tv.tv_usec / 1000);
+		time_data->ddl_ttotal += (ddl_t2 - time_data->ddl_t1);
+		time_data->ddl_count += count;
+		DDL_MSG_TIME("\n%s(): cnt(%u) End Time (%u) Diff(%u) Avg(%u)",
+			func_name, time_data->ddl_count, ddl_t2,
+			ddl_t2 - time_data->ddl_t1,
+			time_data->ddl_ttotal/time_data->ddl_count);
+		time_data->ddl_t1 = 0;
+	}
+}
+
+void ddl_update_core_start_time(const char *func_name, u32 index)
+{
+	u32 act_time;
+	struct timeval ddl_tv;
+	struct time_data *time_data = &proc_time[index];
+	do_gettimeofday(&ddl_tv);
+	act_time = (ddl_tv.tv_sec * 1000) + (ddl_tv.tv_usec / 1000);
+	time_data->ddl_t1 = act_time;
+	DDL_MSG_LOW("\n%s(): Start time updated Act(%u)",
+				func_name, act_time);
+}
+
+void ddl_reset_core_time_variables(u32 index)
+{
+	proc_time[index].ddl_t1 = 0;
+	proc_time[index].ddl_ttotal = 0;
+	proc_time[index].ddl_count = 0;
+}
+
+int ddl_get_core_decode_proc_time(u32 *ddl_handle)
+{
+	int avg_time = 0;
+	struct ddl_client_context *ddl =
+		(struct ddl_client_context *) ddl_handle;
+	avg_time = ddl_vidc_decode_get_avg_time(ddl);
+	return avg_time;
+}
+
+void ddl_reset_avg_dec_time(u32 *ddl_handle)
+{
+	struct ddl_client_context *ddl =
+		(struct ddl_client_context *) ddl_handle;
+	ddl_vidc_decode_reset_avg_time(ddl);
+}
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_utils.h b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_utils.h
new file mode 100644
index 0000000..bbde7ae
--- /dev/null
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_utils.h
@@ -0,0 +1,78 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VCD_DDL_UTILS_H_
+#define _VCD_DDL_UTILS_H_
+
+#include <linux/delay.h>
+#include <media/msm/vidc_type.h>
+
+extern u32 vidc_msg_pmem;
+extern u32 vidc_msg_timing;
+
+enum timing_data {
+	DEC_OP_TIME,
+	DEC_IP_TIME,
+	ENC_OP_TIME,
+	ENC_SLICE_OP_TIME,
+	MAX_TIME_DATA
+};
+
+#define DBG_PMEM(x...) \
+do { \
+	if (vidc_msg_pmem) \
+		printk(KERN_DEBUG x); \
+} while (0)
+
+#ifdef DDL_MSG_LOG
+#define DDL_MSG_LOW(x...)    printk(KERN_INFO x)
+#define DDL_MSG_MED(x...)    printk(KERN_INFO x)
+#define DDL_MSG_HIGH(x...)   printk(KERN_INFO x)
+#else
+#define DDL_MSG_LOW(x...)
+#define DDL_MSG_MED(x...)
+#define DDL_MSG_HIGH(x...)
+#endif
+
+#define DDL_MSG_ERROR(x...)  printk(KERN_INFO x)
+#define DDL_MSG_FATAL(x...)  printk(KERN_INFO x)
+
+#define DDL_ALIGN_SIZE(sz, guard_bytes, align_mask) \
+	(((u32)(sz) + guard_bytes) & align_mask)
+#define DDL_ADDR_IS_ALIGNED(addr, align_bytes) \
+	(!((u32)(addr) & ((align_bytes) - 1)))
+#define  DDL_ALIGN(val, grid) ((!(grid)) ? (val) : \
+		((((val) + (grid) - 1) / (grid)) * (grid)))
+#define  DDL_ALIGN_FLOOR(val, grid) ((!(grid)) ? (val) : \
+		(((val) / (grid)) * (grid)))
+#define DDL_OFFSET(base, addr) ((!(addr)) ? 0 : (u32)((u8 *) \
+		(addr) - (u8 *) (base)))
+#define DDL_ADDR_OFFSET(base, addr) DDL_OFFSET((base).align_physical_addr, \
+		(addr).align_physical_addr)
+#define DDL_GET_ALIGNED_VITUAL(x)   ((x).align_virtual_addr)
+#define DDL_KILO_BYTE(x)   ((x)*1024)
+#define DDL_MEGA_BYTE(x)   ((x)*1024*1024)
+#define DDL_FRAMERATE_SCALE(x)            ((x) * 1000)
+
+#define DDL_MIN(x, y)  ((x < y) ? x : y)
+#define DDL_MAX(x, y)  ((x > y) ? x : y)
+#define DDL_MEMCPY(dest, src, len)  memcpy((dest), (src), (len))
+#define DDL_MEMSET(src, value, len) memset((src), (value), (len))
+
+void ddl_set_core_start_time(const char *func_name, u32 index);
+void ddl_reset_core_time_variables(u32 index);
+void ddl_calc_core_proc_time_cnt(const char *func_name, u32 index, u32 count);
+void ddl_update_core_start_time(const char *func_name, u32 index);
+int ddl_get_core_decode_proc_time(u32 *ddl_handle);
+void ddl_reset_avg_dec_time(u32 *ddl_handle);
+#endif
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_vidc.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_vidc.c
new file mode 100644
index 0000000..d0cf4e8
--- /dev/null
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_vidc.c
@@ -0,0 +1,1181 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "vcd_ddl.h"
+#include "vcd_ddl_metadata.h"
+#include "vcd_ddl_shared_mem.h"
+#include "vcd_core.h"
+
+#if defined(PIX_CACHE_DISABLE)
+#define DDL_PIX_CACHE_ENABLE  false
+#else
+#define DDL_PIX_CACHE_ENABLE  true
+#endif
+static unsigned int run_cnt;
+
+void ddl_vidc_core_init(struct ddl_context *ddl_context)
+{
+	struct vidc_1080P_pix_cache_config pixel_cache_config;
+
+	vidc_1080p_do_sw_reset(VIDC_1080P_RESET_IN_SEQ_FIRST_STAGE);
+	msleep(DDL_SW_RESET_SLEEP);
+	vidc_1080p_do_sw_reset(VIDC_1080P_RESET_IN_SEQ_SECOND_STAGE);
+	vidc_1080p_init_memory_controller(
+		(u32) ddl_context->dram_base_a.align_physical_addr,
+		(u32) ddl_context->dram_base_b.align_physical_addr);
+	vidc_1080p_clear_returned_channel_inst_id();
+	ddl_context->vidc_decode_seq_start[0] =
+		vidc_1080p_decode_seq_start_ch0;
+	ddl_context->vidc_decode_seq_start[1] =
+		vidc_1080p_decode_seq_start_ch1;
+	ddl_context->vidc_decode_init_buffers[0] =
+		vidc_1080p_decode_init_buffers_ch0;
+	ddl_context->vidc_decode_init_buffers[1] =
+		vidc_1080p_decode_init_buffers_ch1;
+	ddl_context->vidc_decode_frame_start[0] =
+		vidc_1080p_decode_frame_start_ch0;
+	ddl_context->vidc_decode_frame_start[1] =
+		vidc_1080p_decode_frame_start_ch1;
+	ddl_context->vidc_set_dec_resolution[0] =
+		vidc_1080p_set_dec_resolution_ch0;
+	ddl_context->vidc_set_dec_resolution[1] =
+		vidc_1080p_set_dec_resolution_ch1;
+	ddl_context->vidc_encode_seq_start[0] =
+		vidc_1080p_encode_seq_start_ch0;
+	ddl_context->vidc_encode_seq_start[1] =
+		vidc_1080p_encode_seq_start_ch1;
+	ddl_context->vidc_encode_frame_start[0] =
+		vidc_1080p_encode_frame_start_ch0;
+	ddl_context->vidc_encode_frame_start[1] =
+		vidc_1080p_encode_frame_start_ch1;
+	ddl_context->vidc_encode_slice_batch_start[0] =
+		vidc_1080p_encode_slice_batch_start_ch0;
+	ddl_context->vidc_encode_slice_batch_start[1] =
+		vidc_1080p_encode_slice_batch_start_ch1;
+	vidc_1080p_release_sw_reset();
+	ddl_context->pix_cache_enable = DDL_PIX_CACHE_ENABLE;
+	if (ddl_context->pix_cache_enable) {
+		vidc_pix_cache_sw_reset();
+		pixel_cache_config.cache_enable = true;
+		pixel_cache_config.prefetch_en = true;
+		pixel_cache_config.port_select = VIDC_1080P_PIX_CACHE_PORT_B;
+		pixel_cache_config.statistics_off = true;
+		pixel_cache_config.page_size =
+			VIDC_1080P_PIX_CACHE_PAGE_SIZE_1K;
+		vidc_pix_cache_init_config(&pixel_cache_config);
+	}
+}
+
+void ddl_vidc_core_term(struct ddl_context *ddl_context)
+{
+	if (ddl_context->pix_cache_enable) {
+		u32 pix_cache_idle = false;
+		u32 counter = 0;
+
+		vidc_pix_cache_set_halt(true);
+
+		do {
+			msleep(DDL_SW_RESET_SLEEP);
+			vidc_pix_cache_get_status_idle(&pix_cache_idle);
+			counter++;
+		} while (!pix_cache_idle &&
+			counter < DDL_PIXEL_CACHE_STATUS_READ_RETRY);
+
+		if (!pix_cache_idle) {
+			ddl_context->cmd_err_status =
+				DDL_PIXEL_CACHE_NOT_IDLE;
+			ddl_handle_core_errors(ddl_context);
+		}
+	}
+}
+
+void ddl_vidc_channel_set(struct ddl_client_context *ddl)
+{
+	struct ddl_context *ddl_context = ddl->ddl_context;
+	enum vcd_codec *vcd_codec;
+	enum vidc_1080p_codec codec = VIDC_1080P_H264_DECODE;
+	const enum vidc_1080p_decode_p_cache_enable
+		dec_pix_cache = VIDC_1080P_DECODE_PCACHE_DISABLE;
+	const enum vidc_1080p_encode_p_cache_enable
+		enc_pix_cache = VIDC_1080P_ENCODE_PCACHE_ENABLE;
+	u32 pix_cache_ctrl, ctxt_mem_offset, ctxt_mem_size;
+
+	if (ddl->decoding) {
+		ddl_set_core_start_time(__func__, DEC_OP_TIME);
+		vcd_codec = &(ddl->codec_data.decoder.codec.codec);
+		pix_cache_ctrl = (u32)dec_pix_cache;
+		ctxt_mem_offset = DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+		ddl->codec_data.decoder.hw_bufs.context) >> 11;
+		ctxt_mem_size =
+			ddl->codec_data.decoder.hw_bufs.context.buffer_size;
+	} else {
+		vcd_codec = &(ddl->codec_data.encoder.codec.codec);
+		pix_cache_ctrl = (u32)enc_pix_cache;
+		ctxt_mem_offset = DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+			ddl->codec_data.encoder.hw_bufs.context) >> 11;
+		ctxt_mem_size =
+			ddl->codec_data.encoder.hw_bufs.context.buffer_size;
+	}
+	switch (*vcd_codec) {
+	default:
+	case VCD_CODEC_MPEG4:
+		if (ddl->decoding)
+			codec = VIDC_1080P_MPEG4_DECODE;
+		else
+			codec = VIDC_1080P_MPEG4_ENCODE;
+	break;
+	case VCD_CODEC_H264:
+		if (ddl->decoding)
+			codec = VIDC_1080P_H264_DECODE;
+		else
+			codec = VIDC_1080P_H264_ENCODE;
+	break;
+	case VCD_CODEC_DIVX_3:
+		if (ddl->decoding)
+			codec = VIDC_1080P_DIVX311_DECODE;
+	break;
+	case VCD_CODEC_DIVX_4:
+		if (ddl->decoding)
+			codec = VIDC_1080P_DIVX412_DECODE;
+	break;
+	case VCD_CODEC_DIVX_5:
+		if (ddl->decoding)
+			codec = VIDC_1080P_DIVX502_DECODE;
+	break;
+	case VCD_CODEC_DIVX_6:
+		if (ddl->decoding)
+			codec = VIDC_1080P_DIVX503_DECODE;
+	break;
+	case VCD_CODEC_XVID:
+		if (ddl->decoding)
+			codec = VIDC_1080P_MPEG4_DECODE;
+	break;
+	case VCD_CODEC_H263:
+		if (ddl->decoding)
+			codec = VIDC_1080P_H263_DECODE;
+		else
+			codec = VIDC_1080P_H263_ENCODE;
+	break;
+	case VCD_CODEC_MPEG1:
+	case VCD_CODEC_MPEG2:
+		if (ddl->decoding)
+			codec = VIDC_1080P_MPEG2_DECODE;
+	break;
+	case VCD_CODEC_VC1:
+		if (ddl->decoding)
+			codec = VIDC_1080P_VC1_DECODE;
+	break;
+	case VCD_CODEC_VC1_RCV:
+		if (ddl->decoding)
+			codec = VIDC_1080P_VC1_RCV_DECODE;
+	break;
+	}
+	ddl->cmd_state = DDL_CMD_CHANNEL_SET;
+	DDL_MSG_LOW("ddl_state_transition: %s ~~> DDL_CLIENT_WAIT_FOR_CHDONE",
+	ddl_get_state_string(ddl->client_state));
+	ddl->client_state = DDL_CLIENT_WAIT_FOR_CHDONE;
+	vidc_1080p_set_host2risc_cmd(VIDC_1080P_HOST2RISC_CMD_OPEN_CH,
+		(u32)codec, pix_cache_ctrl, ctxt_mem_offset,
+		ctxt_mem_size);
+}
+
+void ddl_vidc_decode_init_codec(struct ddl_client_context *ddl)
+{
+	struct ddl_context  *ddl_context = ddl->ddl_context;
+	struct ddl_decoder_data *decoder = &(ddl->codec_data.decoder);
+	struct vidc_1080p_dec_seq_start_param seq_start_param;
+	u32 seq_size;
+
+	ddl_set_core_start_time(__func__, DEC_OP_TIME);
+	vidc_1080p_set_decode_mpeg4_pp_filter(decoder->post_filter.post_filter);
+	vidc_sm_set_concealment_color(&ddl->shared_mem[ddl->command_channel],
+		DDL_CONCEALMENT_Y_COLOR, DDL_CONCEALMENT_C_COLOR);
+
+	vidc_sm_set_error_concealment_config(
+		&ddl->shared_mem[ddl->command_channel],
+		VIDC_SM_ERR_CONCEALMENT_INTER_SLICE_MB_COPY,
+		VIDC_SM_ERR_CONCEALMENT_INTRA_SLICE_COLOR_CONCEALMENT,
+		VIDC_SM_ERR_CONCEALMENT_ENABLE);
+
+	ddl_vidc_metadata_enable(ddl);
+	vidc_sm_set_metadata_start_address(&ddl->shared_mem
+		[ddl->command_channel],
+		DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+		ddl->codec_data.decoder.meta_data_input));
+
+	vidc_sm_set_idr_decode_only(&ddl->shared_mem[ddl->command_channel],
+			decoder->idr_only_decoding);
+
+	if ((decoder->codec.codec == VCD_CODEC_DIVX_3) ||
+	   (decoder->codec.codec == VCD_CODEC_VC1_RCV ||
+		decoder->codec.codec == VCD_CODEC_VC1))
+		ddl_context->vidc_set_dec_resolution
+		[ddl->command_channel](decoder->client_frame_size.width,
+		decoder->client_frame_size.height);
+	else
+	ddl_context->vidc_set_dec_resolution
+	[ddl->command_channel](0x0, 0x0);
+	DDL_MSG_LOW("HEADER-PARSE-START");
+	DDL_MSG_LOW("ddl_state_transition: %s ~~>"
+	"DDL_CLIENT_WAIT_FOR_INITCODECDONE",
+	ddl_get_state_string(ddl->client_state));
+	ddl->client_state = DDL_CLIENT_WAIT_FOR_INITCODECDONE;
+	ddl->cmd_state = DDL_CMD_HEADER_PARSE;
+	seq_start_param.cmd_seq_num = ++ddl_context->cmd_seq_num;
+	seq_start_param.inst_id = ddl->instance_id;
+	seq_start_param.shared_mem_addr_offset = DDL_ADDR_OFFSET(
+	ddl_context->dram_base_a, ddl->shared_mem
+		[ddl->command_channel]);
+	seq_start_param.stream_buffer_addr_offset =
+	DDL_OFFSET(ddl_context->dram_base_a.align_physical_addr,
+	decoder->decode_config.sequence_header);
+	seq_start_param.stream_buffersize =
+		decoder->client_input_buf_req.sz;
+	seq_size = decoder->decode_config.sequence_header_len +
+		DDL_LINEAR_BUFFER_ALIGN_BYTES + VCD_SEQ_HDR_PADDING_BYTES;
+	if (seq_start_param.stream_buffersize < seq_size)
+		seq_start_param.stream_buffersize = seq_size;
+	seq_start_param.stream_frame_size =
+		decoder->decode_config.sequence_header_len;
+	seq_start_param.descriptor_buffer_addr_offset =
+		DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+		decoder->hw_bufs.desc),
+	seq_start_param.descriptor_buffer_size =
+		decoder->hw_bufs.desc.buffer_size;
+	if ((decoder->codec.codec == VCD_CODEC_MPEG4) ||
+		(decoder->codec.codec == VCD_CODEC_DIVX_4) ||
+		(decoder->codec.codec == VCD_CODEC_DIVX_5) ||
+		(decoder->codec.codec == VCD_CODEC_DIVX_6) ||
+		(decoder->codec.codec == VCD_CODEC_XVID))
+		vidc_sm_set_mpeg4_profile_override(
+			&ddl->shared_mem[ddl->command_channel],
+			VIDC_SM_PROFILE_INFO_ASP);
+	if (VCD_CODEC_H264 == decoder->codec.codec)
+		vidc_sm_set_decoder_sei_enable(
+			&ddl->shared_mem[ddl->command_channel],
+			VIDC_SM_RECOVERY_POINT_SEI);
+	ddl_context->vidc_decode_seq_start[ddl->command_channel](
+		&seq_start_param);
+
+	vidc_sm_set_decoder_stuff_bytes_consumption(
+		&ddl->shared_mem[ddl->command_channel],
+		VIDC_SM_NUM_STUFF_BYTES_CONSUME_NONE);
+}
+
+void ddl_vidc_decode_dynamic_property(struct ddl_client_context *ddl,
+	u32 enable)
+{
+	struct ddl_decoder_data *decoder = &(ddl->codec_data.decoder);
+	struct vcd_frame_data *bit_stream =
+		&(ddl->input_frame.vcd_frm);
+	struct ddl_context *ddl_context = ddl->ddl_context;
+
+	if (!enable) {
+		if (decoder->dynmic_prop_change_req)
+			decoder->dynmic_prop_change_req = false;
+		return;
+	}
+	if ((decoder->dynamic_prop_change & DDL_DEC_REQ_OUTPUT_FLUSH)) {
+		decoder->dynmic_prop_change_req = true;
+		decoder->dynamic_prop_change &= ~(DDL_DEC_REQ_OUTPUT_FLUSH);
+		decoder->dpb_mask.hw_mask = 0;
+		decoder->flush_pending = true;
+	}
+	if (((decoder->meta_data_enable_flag & VCD_METADATA_PASSTHROUGH)) &&
+		((VCD_FRAME_FLAG_EXTRADATA & bit_stream->flags))) {
+		u32 extradata_presence = true;
+		u8* tmp = ((u8 *) bit_stream->physical +
+				bit_stream->offset +
+				bit_stream->data_len + 3);
+		u32 extra_data_start = (u32) ((u32)tmp & ~3);
+
+		extra_data_start = extra_data_start -
+			(u32)ddl_context->dram_base_a.align_physical_addr;
+		decoder->dynmic_prop_change_req = true;
+		vidc_sm_set_extradata_addr(&ddl->shared_mem
+			[ddl->command_channel], extra_data_start);
+		vidc_sm_set_extradata_presence(&ddl->shared_mem
+			[ddl->command_channel], extradata_presence);
+	}
+}
+
+void ddl_vidc_encode_dynamic_property(struct ddl_client_context *ddl,
+	u32 enable)
+{
+	struct ddl_encoder_data *encoder = &(ddl->codec_data.encoder);
+	u32 frame_rate_change = false, bit_rate_change = false;
+	u32 i_period_change = false, reset_req = false;
+
+	if (!enable) {
+		if (encoder->dynmic_prop_change_req) {
+			reset_req = true;
+			encoder->dynmic_prop_change_req = false;
+		}
+	} else {
+		if ((encoder->dynamic_prop_change & DDL_ENC_REQ_IFRAME)) {
+			encoder->intra_frame_insertion = true;
+			encoder->dynamic_prop_change &=
+				~(DDL_ENC_REQ_IFRAME);
+		}
+		if ((encoder->dynamic_prop_change &
+			DDL_ENC_CHANGE_BITRATE)) {
+			bit_rate_change = true;
+			vidc_sm_set_encoder_new_bit_rate(
+				&ddl->shared_mem[ddl->command_channel],
+				encoder->target_bit_rate.target_bitrate);
+			encoder->dynamic_prop_change &=
+				~(DDL_ENC_CHANGE_BITRATE);
+		}
+		if ((encoder->dynamic_prop_change
+			& DDL_ENC_CHANGE_IPERIOD)) {
+			i_period_change = true;
+			vidc_sm_set_encoder_new_i_period(
+				&ddl->shared_mem[ddl->command_channel],
+				encoder->i_period.p_frames);
+			encoder->dynamic_prop_change &=
+				~(DDL_ENC_CHANGE_IPERIOD);
+		}
+		if ((encoder->dynamic_prop_change
+			& DDL_ENC_CHANGE_FRAMERATE)) {
+			frame_rate_change = true;
+			vidc_sm_set_encoder_new_frame_rate(
+				&ddl->shared_mem[ddl->command_channel],
+				(u32)(DDL_FRAMERATE_SCALE(encoder->\
+				frame_rate.fps_numerator) /
+				encoder->frame_rate.fps_denominator));
+			encoder->dynamic_prop_change &=
+				~(DDL_ENC_CHANGE_FRAMERATE);
+		}
+	}
+	if ((enable) || (reset_req)) {
+		vidc_sm_set_encoder_param_change(
+			&ddl->shared_mem[ddl->command_channel],
+			bit_rate_change, frame_rate_change,
+			i_period_change);
+	}
+}
+
+static void ddl_vidc_encode_set_profile_level(
+	struct ddl_client_context *ddl)
+{
+	struct ddl_encoder_data *encoder = &(ddl->codec_data.encoder);
+	u32  encode_profile, level = 0;
+
+	switch (encoder->profile.profile) {
+	default:
+	case VCD_PROFILE_MPEG4_SP:
+		encode_profile = VIDC_1080P_PROFILE_MPEG4_SIMPLE;
+	break;
+	case VCD_PROFILE_MPEG4_ASP:
+		encode_profile = VIDC_1080P_PROFILE_MPEG4_ADV_SIMPLE;
+	break;
+	case VCD_PROFILE_H264_BASELINE:
+		encode_profile = VIDC_1080P_PROFILE_H264_BASELINE;
+	break;
+	case VCD_PROFILE_H264_MAIN:
+		encode_profile = VIDC_1080P_PROFILE_H264_MAIN;
+	break;
+	case VCD_PROFILE_H264_HIGH:
+		encode_profile = VIDC_1080P_PROFILE_H264_HIGH;
+	break;
+	}
+	switch (encoder->level.level) {
+	default:
+	case VCD_LEVEL_MPEG4_0:
+		level = VIDC_1080P_MPEG4_LEVEL0;
+	break;
+	case VCD_LEVEL_MPEG4_0b:
+		level = VIDC_1080P_MPEG4_LEVEL0b;
+	break;
+	case VCD_LEVEL_MPEG4_1:
+		level = VIDC_1080P_MPEG4_LEVEL1;
+	break;
+	case VCD_LEVEL_MPEG4_2:
+		level = VIDC_1080P_MPEG4_LEVEL2;
+	break;
+	case VCD_LEVEL_MPEG4_3:
+		level = VIDC_1080P_MPEG4_LEVEL3;
+	break;
+	case VCD_LEVEL_MPEG4_3b:
+		level = VIDC_1080P_MPEG4_LEVEL3b;
+	break;
+	case VCD_LEVEL_MPEG4_4:
+		level = VIDC_1080P_MPEG4_LEVEL4;
+	break;
+	case VCD_LEVEL_MPEG4_4a:
+		level = VIDC_1080P_MPEG4_LEVEL4a;
+	break;
+	case VCD_LEVEL_MPEG4_5:
+		level = VIDC_1080P_MPEG4_LEVEL5;
+	break;
+	case VCD_LEVEL_MPEG4_6:
+		level = VIDC_1080P_MPEG4_LEVEL6;
+	break;
+	case VCD_LEVEL_MPEG4_7:
+		level = VIDC_1080P_MPEG4_LEVEL7;
+	break;
+	case VCD_LEVEL_H264_1:
+		level = VIDC_1080P_H264_LEVEL1;
+	break;
+	case VCD_LEVEL_H264_1b:
+		level = VIDC_1080P_H264_LEVEL1b;
+	break;
+	case VCD_LEVEL_H264_1p1:
+		level = VIDC_1080P_H264_LEVEL1p1;
+	break;
+	case VCD_LEVEL_H264_1p2:
+		level = VIDC_1080P_H264_LEVEL1p2;
+	break;
+	case VCD_LEVEL_H264_1p3:
+		level = VIDC_1080P_H264_LEVEL1p3;
+	break;
+	case VCD_LEVEL_H264_2:
+		level = VIDC_1080P_H264_LEVEL2;
+	break;
+	case VCD_LEVEL_H264_2p1:
+		level = VIDC_1080P_H264_LEVEL2p1;
+	break;
+	case VCD_LEVEL_H264_2p2:
+		level = VIDC_1080P_H264_LEVEL2p2;
+	break;
+	case VCD_LEVEL_H264_3:
+		level = VIDC_1080P_H264_LEVEL3;
+	break;
+	case VCD_LEVEL_H264_3p1:
+		level = VIDC_1080P_H264_LEVEL3p1;
+	break;
+	case VCD_LEVEL_H264_3p2:
+		level = VIDC_1080P_H264_LEVEL3p2;
+	break;
+	case VCD_LEVEL_H264_4:
+		level = VIDC_1080P_H264_LEVEL4;
+	break;
+	case VCD_LEVEL_H263_10:
+		level = VIDC_1080P_H263_LEVEL10;
+	break;
+	case VCD_LEVEL_H263_20:
+		level = VIDC_1080P_H263_LEVEL20;
+	break;
+	case VCD_LEVEL_H263_30:
+		level = VIDC_1080P_H263_LEVEL30;
+	break;
+	case VCD_LEVEL_H263_40:
+		level = VIDC_1080P_H263_LEVEL40;
+	break;
+	case VCD_LEVEL_H263_45:
+		level = VIDC_1080P_H263_LEVEL45;
+	break;
+	case VCD_LEVEL_H263_50:
+		level = VIDC_1080P_H263_LEVEL50;
+	break;
+	case VCD_LEVEL_H263_60:
+		level = VIDC_1080P_H263_LEVEL60;
+	break;
+	case VCD_LEVEL_H263_70:
+		level = VIDC_1080P_H263_LEVEL70;
+	break;
+	}
+	vidc_1080p_set_encode_profile_level(encode_profile, level);
+}
+
+static void ddl_vidc_encode_set_multi_slice_info(
+	struct ddl_encoder_data *encoder)
+{
+	enum vidc_1080p_MSlice_selection m_slice_sel;
+	u32 i_multi_slice_size = 0, i_multi_slice_byte = 0;
+
+	if (!encoder) {
+		DDL_MSG_ERROR("Invalid Parameter");
+		return;
+	}
+
+	switch (encoder->multi_slice.m_slice_sel) {
+	default:
+	case VCD_MSLICE_OFF:
+		m_slice_sel = VIDC_1080P_MSLICE_DISABLE;
+	break;
+	case VCD_MSLICE_BY_GOB:
+	case VCD_MSLICE_BY_MB_COUNT:
+		m_slice_sel = VIDC_1080P_MSLICE_BY_MB_COUNT;
+		i_multi_slice_size = encoder->multi_slice.m_slice_size;
+	break;
+	case VCD_MSLICE_BY_BYTE_COUNT:
+		m_slice_sel = VIDC_1080P_MSLICE_BY_BYTE_COUNT;
+		i_multi_slice_byte = encoder->multi_slice.m_slice_size;
+	break;
+	}
+	vidc_1080p_set_encode_multi_slice_control(m_slice_sel,
+		i_multi_slice_size, i_multi_slice_byte);
+}
+
+static void ddl_vidc_encode_set_batch_slice_info(
+	struct ddl_client_context *ddl)
+{
+	struct ddl_context *ddl_context = ddl->ddl_context;
+	struct ddl_encoder_data *encoder = &(ddl->codec_data.encoder);
+	DDL_MSG_LOW("%s\n", __func__);
+	encoder->batch_frame.slice_batch_out.buffer_size =
+					encoder->output_buf_req.sz;
+	DDL_MSG_LOW("encoder->batch_frame.slice_batch_out.buffer_size = %d\n",
+			encoder->batch_frame.slice_batch_out.buffer_size);
+	vidc_sm_set_encoder_batch_config(
+			&ddl->shared_mem[ddl->command_channel],
+			1,
+			DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+			encoder->batch_frame.slice_batch_in),
+			DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+			encoder->batch_frame.slice_batch_out),
+			encoder->batch_frame.slice_batch_out.buffer_size);
+	vidc_sm_set_encoder_slice_batch_int_ctrl(
+			&ddl->shared_mem[ddl->command_channel],
+			0);
+}
+
+void ddl_vidc_encode_init_codec(struct ddl_client_context *ddl)
+{
+	struct ddl_context *ddl_context = ddl->ddl_context;
+	struct ddl_encoder_data *encoder = &(ddl->codec_data.encoder);
+	struct ddl_enc_buffers *enc_buffers = &encoder->hw_bufs;
+	struct vidc_1080p_enc_seq_start_param seq_start_param;
+	enum vidc_1080p_memory_access_method mem_access_method;
+	enum vidc_1080p_DBConfig db_config;
+	enum VIDC_SM_frame_skip r_cframe_skip =
+		VIDC_SM_FRAME_SKIP_DISABLE;
+	u32 index, luma[4], chroma[4], hdr_ext_control = false;
+	const u32 recon_bufs = 4;
+	u32 h263_cpfc_enable = false;
+	u32 scaled_frame_rate;
+
+	ddl_vidc_encode_set_profile_level(ddl);
+	vidc_1080p_set_encode_frame_size(encoder->frame_size.width,
+		encoder->frame_size.height);
+	vidc_1080p_encode_set_qp_params(encoder->qp_range.max_qp,
+		encoder->qp_range.min_qp);
+	vidc_1080p_encode_set_rc_config(encoder->rc_level.frame_level_rc,
+		encoder->rc_level.mb_level_rc,
+		encoder->session_qp.i_frame_qp);
+	if (encoder->hdr_ext_control > 0)
+		hdr_ext_control = true;
+	if (encoder->r_cframe_skip > 0)
+		r_cframe_skip = VIDC_SM_FRAME_SKIP_ENABLE_LEVEL;
+	scaled_frame_rate = DDL_FRAMERATE_SCALE(encoder->\
+			frame_rate.fps_numerator) /
+			encoder->frame_rate.fps_denominator;
+	if ((encoder->codec.codec == VCD_CODEC_H263) &&
+		(DDL_FRAMERATE_SCALE(DDL_INITIAL_FRAME_RATE)
+		 != scaled_frame_rate))
+		h263_cpfc_enable = true;
+	vidc_sm_set_extended_encoder_control(&ddl->shared_mem
+		[ddl->command_channel], hdr_ext_control,
+		r_cframe_skip, false, 0,
+		h263_cpfc_enable, encoder->sps_pps.sps_pps_for_idr_enable_flag,
+		encoder->closed_gop);
+	vidc_sm_set_encoder_init_rc_value(&ddl->shared_mem
+		[ddl->command_channel],
+		encoder->target_bit_rate.target_bitrate);
+	vidc_sm_set_encoder_hec_period(&ddl->shared_mem
+		[ddl->command_channel], encoder->hdr_ext_control);
+		vidc_sm_set_encoder_vop_time(&ddl->shared_mem
+			[ddl->command_channel], true,
+			encoder->vop_timing.vop_time_resolution, 0);
+	if (encoder->rc_level.frame_level_rc)
+		vidc_1080p_encode_set_frame_level_rc_params(
+			scaled_frame_rate,
+			encoder->target_bit_rate.target_bitrate,
+			encoder->frame_level_rc.reaction_coeff);
+	if (encoder->rc_level.mb_level_rc)
+		vidc_1080p_encode_set_mb_level_rc_params(
+			encoder->adaptive_rc.disable_dark_region_as_flag,
+			encoder->adaptive_rc.disable_smooth_region_as_flag,
+			encoder->adaptive_rc.disable_static_region_as_flag,
+			encoder->adaptive_rc.disable_activity_region_flag);
+	if ((!encoder->rc_level.frame_level_rc) &&
+		(!encoder->rc_level.mb_level_rc))
+		vidc_sm_set_pand_b_frame_qp(
+			&ddl->shared_mem[ddl->command_channel],
+			encoder->session_qp.b_frame_qp,
+			encoder->session_qp.p_frame_qp);
+	if (encoder->codec.codec == VCD_CODEC_MPEG4) {
+		vidc_1080p_set_mpeg4_encode_quarter_pel_control(false);
+		vidc_1080p_set_encode_field_picture_structure(false);
+	}
+	if (encoder->codec.codec == VCD_CODEC_H264) {
+		enum vidc_1080p_entropy_sel entropy_sel;
+		switch (encoder->entropy_control.entropy_sel) {
+		default:
+		case VCD_ENTROPY_SEL_CAVLC:
+			entropy_sel = VIDC_1080P_ENTROPY_SEL_CAVLC;
+		break;
+		case VCD_ENTROPY_SEL_CABAC:
+			entropy_sel = VIDC_1080P_ENTROPY_SEL_CABAC;
+		break;
+	}
+	vidc_1080p_set_h264_encode_entropy(entropy_sel);
+	switch (encoder->db_control.db_config) {
+	default:
+	case VCD_DB_ALL_BLOCKING_BOUNDARY:
+		db_config = VIDC_1080P_DB_ALL_BLOCKING_BOUNDARY;
+	break;
+	case VCD_DB_DISABLE:
+		db_config = VIDC_1080P_DB_DISABLE;
+	break;
+	case VCD_DB_SKIP_SLICE_BOUNDARY:
+		db_config = VIDC_1080P_DB_SKIP_SLICE_BOUNDARY;
+	break;
+	}
+	vidc_1080p_set_h264_encode_loop_filter(db_config,
+		encoder->db_control.slice_alpha_offset,
+		encoder->db_control.slice_beta_offset);
+	vidc_1080p_set_h264_encoder_p_frame_ref_count(encoder->\
+		num_references_for_p_frame);
+	if (encoder->profile.profile == VCD_PROFILE_H264_HIGH)
+		vidc_1080p_set_h264_encode_8x8transform_control(true);
+	}
+	vidc_1080p_set_encode_picture(encoder->i_period.p_frames,
+		encoder->i_period.b_frames);
+	vidc_1080p_set_encode_circular_intra_refresh(
+		encoder->intra_refresh.cir_mb_number);
+	ddl_vidc_encode_set_multi_slice_info(encoder);
+	ddl_vidc_metadata_enable(ddl);
+	if (encoder->meta_data_enable_flag)
+		vidc_sm_set_metadata_start_address(&ddl->shared_mem
+			[ddl->command_channel], DDL_ADDR_OFFSET(
+			ddl_context->dram_base_a,
+			ddl->codec_data.encoder.meta_data_input));
+	luma[0] = DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+			enc_buffers->dpb_y[0]);
+	luma[1] = DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+			enc_buffers->dpb_y[1]);
+	if (encoder->hw_bufs.dpb_count == DDL_ENC_MAX_DPB_BUFFERS) {
+		luma[2] = DDL_ADDR_OFFSET(ddl_context->dram_base_b,
+			enc_buffers->dpb_y[2]);
+		luma[3] = DDL_ADDR_OFFSET(ddl_context->dram_base_b,
+			enc_buffers->dpb_y[3]);
+	}
+	for (index = 0; index < recon_bufs; index++)
+		chroma[index] = DDL_ADDR_OFFSET(ddl_context->dram_base_b,
+					enc_buffers->dpb_c[index]);
+	vidc_1080p_set_encode_recon_buffers(recon_bufs, luma, chroma);
+	switch (encoder->codec.codec) {
+	case VCD_CODEC_MPEG4:
+		vidc_1080p_set_mpeg4_encode_work_buffers(
+			DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+				enc_buffers->col_zero),
+				DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+				enc_buffers->acdc_coef),
+				DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+				enc_buffers->mv));
+	break;
+	case VCD_CODEC_H263:
+		vidc_1080p_set_h263_encode_work_buffers(
+			DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+				enc_buffers->mv),
+				DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+				enc_buffers->acdc_coef));
+	break;
+	case VCD_CODEC_H264:
+		vidc_1080p_set_h264_encode_work_buffers(
+			DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+				enc_buffers->mv),
+			DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+				enc_buffers->col_zero),
+			DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+				enc_buffers->md),
+			DDL_ADDR_OFFSET(ddl_context->dram_base_b,
+				enc_buffers->pred),
+			DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+				enc_buffers->nbor_info),
+			DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+				enc_buffers->mb_info));
+	break;
+	default:
+	break;
+	}
+	if (encoder->buf_format.buffer_format ==
+		VCD_BUFFER_FORMAT_NV12_16M2KA)
+		mem_access_method = VIDC_1080P_TILE_LINEAR;
+	else
+		mem_access_method = VIDC_1080P_TILE_64x32;
+	vidc_1080p_set_encode_input_frame_format(mem_access_method);
+	vidc_1080p_set_encode_padding_control(0, 0, 0, 0);
+	DDL_MSG_LOW("ddl_state_transition: %s ~~>"
+		"DDL_CLIENT_WAIT_FOR_INITCODECDONE",
+		ddl_get_state_string(ddl->client_state));
+	ddl->client_state = DDL_CLIENT_WAIT_FOR_INITCODECDONE;
+	ddl->cmd_state = DDL_CMD_INIT_CODEC;
+	vidc_1080p_set_encode_field_picture_structure(false);
+	seq_start_param.cmd_seq_num = ++ddl_context->cmd_seq_num;
+	seq_start_param.inst_id = ddl->instance_id;
+	seq_start_param.shared_mem_addr_offset = DDL_ADDR_OFFSET(
+		ddl_context->dram_base_a, ddl->shared_mem
+		[ddl->command_channel]);
+	seq_start_param.stream_buffer_addr_offset = DDL_ADDR_OFFSET(
+		ddl_context->dram_base_a, encoder->seq_header);
+	seq_start_param.stream_buffer_size =
+		encoder->seq_header.buffer_size;
+	encoder->seq_header_length = 0;
+	ddl_context->vidc_encode_seq_start[ddl->command_channel](
+		&seq_start_param);
+}
+
+void ddl_vidc_channel_end(struct ddl_client_context *ddl)
+{
+	DDL_MSG_LOW("ddl_state_transition: %s ~~> DDL_CLIENT_WAIT_FOR_CHEND",
+	ddl_get_state_string(ddl->client_state));
+	ddl->client_state = DDL_CLIENT_WAIT_FOR_CHEND;
+	ddl->cmd_state = DDL_CMD_CHANNEL_END;
+	vidc_1080p_set_host2risc_cmd(VIDC_1080P_HOST2RISC_CMD_CLOSE_CH,
+		ddl->instance_id, 0, 0, 0);
+}
+
+void ddl_vidc_encode_frame_run(struct ddl_client_context *ddl)
+{
+	struct vidc_1080p_enc_frame_start_param enc_param;
+	struct ddl_context *ddl_context = ddl->ddl_context;
+	struct ddl_encoder_data  *encoder = &(ddl->codec_data.encoder);
+	struct ddl_enc_buffers *enc_buffers = &(encoder->hw_bufs);
+	struct vcd_frame_data *stream = &(ddl->output_frame.vcd_frm);
+	struct vcd_frame_data *input_vcd_frm =
+		&(ddl->input_frame.vcd_frm);
+	u32 dpb_addr_y[4], dpb_addr_c[4];
+	u32 index, y_addr, c_addr;
+
+	DDL_MSG_LOW("%s\n", __func__);
+	ddl_vidc_encode_set_metadata_output_buf(ddl);
+
+	encoder->enc_frame_info.meta_data_exists = false;
+
+	y_addr = DDL_OFFSET(ddl_context->dram_base_b.align_physical_addr,
+			input_vcd_frm->physical);
+	c_addr = (y_addr + encoder->input_buf_size.size_y);
+	if (input_vcd_frm->flags & VCD_FRAME_FLAG_EOS) {
+		enc_param.encode = VIDC_1080P_ENC_TYPE_LAST_FRAME_DATA;
+		DDL_MSG_LOW("ddl_state_transition: %s ~~>"
+			"DDL_CLIENT_WAIT_FOR_EOS_DONE",
+			ddl_get_state_string(ddl->client_state));
+		ddl->client_state = DDL_CLIENT_WAIT_FOR_EOS_DONE;
+	} else {
+		enc_param.encode = VIDC_1080P_ENC_TYPE_FRAME_DATA;
+		DDL_MSG_LOW("ddl_state_transition: %s ~~>"
+			"DDL_CLIENT_WAIT_FOR_FRAME_DONE",
+			ddl_get_state_string(ddl->client_state));
+		ddl->client_state = DDL_CLIENT_WAIT_FOR_FRAME_DONE;
+	}
+	ddl->cmd_state = DDL_CMD_ENCODE_FRAME;
+	if (encoder->dynamic_prop_change) {
+		encoder->dynmic_prop_change_req = true;
+		ddl_vidc_encode_dynamic_property(ddl, true);
+	}
+
+	vidc_1080p_set_encode_circular_intra_refresh(
+		encoder->intra_refresh.cir_mb_number);
+	ddl_vidc_encode_set_multi_slice_info(encoder);
+	enc_param.cmd_seq_num = ++ddl_context->cmd_seq_num;
+	enc_param.inst_id = ddl->instance_id;
+	enc_param.shared_mem_addr_offset = DDL_ADDR_OFFSET(
+			ddl_context->dram_base_a,
+			ddl->shared_mem[ddl->command_channel]);
+	enc_param.current_y_addr_offset = y_addr;
+	enc_param.current_c_addr_offset = c_addr;
+	enc_param.stream_buffer_addr_offset = DDL_OFFSET(
+	ddl_context->dram_base_a.align_physical_addr, stream->physical);
+	enc_param.stream_buffer_size =
+		encoder->client_output_buf_req.sz;
+
+	enc_param.intra_frame = encoder->intra_frame_insertion;
+	if (encoder->intra_frame_insertion)
+		encoder->intra_frame_insertion = false;
+	enc_param.input_flush = false;
+	enc_param.slice_enable = false;
+		vidc_sm_set_encoder_vop_time(
+			&ddl->shared_mem[ddl->command_channel], true,
+			encoder->vop_timing.vop_time_resolution,
+			ddl->input_frame.frm_delta);
+	vidc_sm_set_frame_tag(&ddl->shared_mem[ddl->command_channel],
+	ddl->input_frame.vcd_frm.ip_frm_tag);
+	if (ddl_context->pix_cache_enable) {
+		for (index = 0; index < enc_buffers->dpb_count;
+			index++) {
+			dpb_addr_y[index] =
+				(u32) VIDC_1080P_DEC_DPB_RESET_VALUE;
+			dpb_addr_c[index] = (u32) enc_buffers->dpb_c
+				[index].align_physical_addr;
+		}
+
+		dpb_addr_y[index] = (u32) input_vcd_frm->physical;
+		dpb_addr_c[index] = (u32) input_vcd_frm->physical +
+			encoder->input_buf_size.size_y;
+
+		vidc_pix_cache_init_luma_chroma_base_addr(
+			enc_buffers->dpb_count + 1, dpb_addr_y, dpb_addr_c);
+		vidc_pix_cache_set_frame_size(encoder->frame_size.width,
+			encoder->frame_size.height);
+		vidc_pix_cache_set_frame_range(enc_buffers->sz_dpb_y,
+			enc_buffers->sz_dpb_c);
+		vidc_pix_cache_clear_cache_tags();
+	}
+	ddl_context->vidc_encode_frame_start[ddl->command_channel] (
+		&enc_param);
+}
+
+void ddl_vidc_encode_frame_continue(struct ddl_client_context *ddl)
+{
+	struct ddl_context *ddl_context = ddl->ddl_context;
+	struct vcd_frame_data *input_vcd_frm = &(ddl->input_frame.vcd_frm);
+	u32 address_offset;
+	address_offset = (u32)(ddl->output_frame.vcd_frm.physical -
+			ddl_context->dram_base_a.align_physical_addr) >>
+			DDL_VIDC_1080P_BASE_OFFSET_SHIFT;
+	DDL_MSG_LOW("%s\n", __func__);
+	if (VCD_FRAME_FLAG_EOS & input_vcd_frm->flags)
+		ddl->client_state = DDL_CLIENT_WAIT_FOR_EOS_DONE;
+	else
+		ddl->client_state = DDL_CLIENT_WAIT_FOR_FRAME_DONE;
+	ddl->cmd_state = DDL_CMD_ENCODE_CONTINUE;
+	vidc_1080p_set_host2risc_cmd(VIDC_1080P_HOST2RISC_CMD_CONTINUE_ENC,
+		address_offset,
+		0, 0, 0);
+}
+
+void ddl_vidc_encode_slice_batch_run(struct ddl_client_context *ddl)
+{
+	struct vidc_1080p_enc_frame_start_param enc_param;
+	struct ddl_context *ddl_context = ddl->ddl_context;
+	struct ddl_encoder_data  *encoder = &(ddl->codec_data.encoder);
+	struct ddl_enc_buffers *enc_buffers = &(encoder->hw_bufs);
+	struct vcd_frame_data *input_vcd_frm =
+		&(ddl->input_frame.vcd_frm);
+	u32 dpb_addr_y[4], dpb_addr_c[4];
+	u32 index, y_addr, c_addr;
+	u32 bitstream_size;
+	struct vidc_1080p_enc_slice_batch_in_param *slice_batch_in =
+		(struct vidc_1080p_enc_slice_batch_in_param *)
+		encoder->batch_frame.slice_batch_in.align_virtual_addr;
+	DDL_MSG_LOW("%s\n", __func__);
+	DDL_MEMSET(slice_batch_in, 0,
+		sizeof(struct vidc_1080p_enc_slice_batch_in_param));
+	DDL_MEMSET(encoder->batch_frame.slice_batch_in.align_virtual_addr, 0,
+		sizeof(struct vidc_1080p_enc_slice_batch_in_param));
+	encoder->batch_frame.out_frm_next_frmindex = 0;
+	bitstream_size = encoder->batch_frame.output_frame[0].vcd_frm.alloc_len;
+	encoder->output_buf_req.sz = bitstream_size;
+	y_addr = DDL_OFFSET(ddl_context->dram_base_b.align_physical_addr,
+			input_vcd_frm->physical);
+	c_addr = (y_addr + encoder->input_buf_size.size_y);
+	enc_param.encode = VIDC_1080P_ENC_TYPE_SLICE_BATCH_START;
+	DDL_MSG_LOW("ddl_state_transition: %s ~~>"
+		"DDL_CLIENT_WAIT_FOR_FRAME_DONE",
+		ddl_get_state_string(ddl->client_state));
+	slice_batch_in->cmd_type = VIDC_1080P_ENC_TYPE_SLICE_BATCH_START;
+	ddl->client_state = DDL_CLIENT_WAIT_FOR_FRAME_DONE;
+	ddl->cmd_state = DDL_CMD_ENCODE_FRAME;
+	vidc_1080p_set_encode_circular_intra_refresh(
+		encoder->intra_refresh.cir_mb_number);
+	ddl_vidc_encode_set_multi_slice_info(encoder);
+	enc_param.cmd_seq_num = ++ddl_context->cmd_seq_num;
+	enc_param.inst_id = ddl->instance_id;
+	enc_param.shared_mem_addr_offset = DDL_ADDR_OFFSET(
+			ddl_context->dram_base_a,
+			ddl->shared_mem[ddl->command_channel]);
+	enc_param.current_y_addr_offset = y_addr;
+	enc_param.current_c_addr_offset = c_addr;
+	enc_param.stream_buffer_size = bitstream_size;
+	slice_batch_in->num_stream_buffer =
+		encoder->batch_frame.num_output_frames;
+	slice_batch_in->stream_buffer_size = bitstream_size;
+	DDL_MSG_LOW("%s slice_batch_in->num_stream_buffer = %u size = %u\n",
+			 __func__, slice_batch_in->num_stream_buffer,
+			slice_batch_in->stream_buffer_size);
+	for (index = 0; index < encoder->batch_frame.num_output_frames;
+		index++) {
+		slice_batch_in->stream_buffer_addr_offset[index] =
+		((DDL_OFFSET(ddl_context->dram_base_b.align_physical_addr,
+		encoder->batch_frame.output_frame[index].vcd_frm.physical)) >>
+			DDL_VIDC_1080P_BASE_OFFSET_SHIFT);
+	}
+	slice_batch_in->input_size = VIDC_1080P_SLICE_BATCH_IN_SIZE(index);
+	enc_param.intra_frame = encoder->intra_frame_insertion;
+	if (encoder->intra_frame_insertion)
+		encoder->intra_frame_insertion = false;
+	enc_param.input_flush = false;
+	enc_param.slice_enable =
+		encoder->slice_delivery_info.enable;
+	vidc_sm_set_encoder_vop_time(
+			&ddl->shared_mem[ddl->command_channel], true,
+			encoder->vop_timing.vop_time_resolution,
+			ddl->input_frame.frm_delta);
+	vidc_sm_set_frame_tag(&ddl->shared_mem[ddl->command_channel],
+			ddl->input_frame.vcd_frm.ip_frm_tag);
+	DDL_MSG_LOW("%sdpb_count = %d\n", __func__, enc_buffers->dpb_count);
+	if (ddl_context->pix_cache_enable) {
+		for (index = 0; index < enc_buffers->dpb_count;
+			index++) {
+			dpb_addr_y[index] =
+				(u32) VIDC_1080P_DEC_DPB_RESET_VALUE;
+			dpb_addr_c[index] = (u32) enc_buffers->dpb_c
+				[index].align_physical_addr;
+		}
+
+		dpb_addr_y[index] = (u32) input_vcd_frm->physical;
+		dpb_addr_c[index] = (u32) input_vcd_frm->physical +
+				encoder->input_buf_size.size_y;
+
+		vidc_pix_cache_init_luma_chroma_base_addr(
+			enc_buffers->dpb_count + 1, dpb_addr_y, dpb_addr_c);
+		vidc_pix_cache_set_frame_size(encoder->frame_size.width,
+			encoder->frame_size.height);
+		vidc_pix_cache_set_frame_range(enc_buffers->sz_dpb_y,
+			enc_buffers->sz_dpb_c);
+		vidc_pix_cache_clear_cache_tags();
+	}
+	if ((!encoder->rc_level.frame_level_rc) &&
+		(!encoder->rc_level.mb_level_rc)) {
+		encoder->session_qp.p_frame_qp++;
+		if (encoder->session_qp.p_frame_qp > encoder->qp_range.max_qp)
+			encoder->session_qp.p_frame_qp =
+						encoder->qp_range.min_qp;
+		vidc_sm_set_pand_b_frame_qp(
+			&ddl->shared_mem[ddl->command_channel],
+				encoder->session_qp.b_frame_qp,
+				encoder->session_qp.p_frame_qp);
+	}
+
+	if (vidc_msg_timing) {
+		if (run_cnt < 2) {
+			ddl_reset_core_time_variables(ENC_OP_TIME);
+			ddl_reset_core_time_variables(ENC_SLICE_OP_TIME);
+			run_cnt++;
+		 }
+		ddl_update_core_start_time(__func__, ENC_SLICE_OP_TIME);
+		ddl_set_core_start_time(__func__, ENC_OP_TIME);
+	}
+	ddl_vidc_encode_set_batch_slice_info(ddl);
+	ddl_context->vidc_encode_slice_batch_start[ddl->command_channel] (
+			&enc_param);
+}
+
+u32 ddl_vidc_decode_set_buffers(struct ddl_client_context *ddl)
+{
+	struct ddl_context *ddl_context = ddl->ddl_context;
+	struct ddl_decoder_data *decoder = &(ddl->codec_data.decoder);
+	u32 vcd_status = VCD_S_SUCCESS;
+	struct vidc_1080p_dec_init_buffers_param init_buf_param;
+	u32 size_y = 0;
+	u32 size_c = 0;
+
+	if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_DPB)) {
+		DDL_MSG_ERROR("STATE-CRITICAL");
+		return VCD_ERR_FAIL;
+	}
+	ddl_set_vidc_timeout(ddl);
+	ddl_vidc_decode_set_metadata_output(decoder);
+	if (decoder->dp_buf.no_of_dec_pic_buf <
+		decoder->client_output_buf_req.actual_count)
+		return VCD_ERR_BAD_STATE;
+	if (decoder->codec.codec == VCD_CODEC_H264) {
+		vidc_sm_set_allocated_h264_mv_size(
+			&ddl->shared_mem[ddl->command_channel],
+			decoder->hw_bufs.h264_mv[0].buffer_size);
+	}
+	if (vcd_status)
+		return vcd_status;
+#ifdef DDL_BUF_LOG
+	ddl_list_buffers(ddl);
+#endif
+	ddl_set_core_start_time(__func__, DEC_OP_TIME);
+	ddl_decoder_dpb_transact(decoder, NULL, DDL_DPB_OP_INIT);
+	if (ddl_decoder_dpb_init(ddl) == VCD_ERR_FAIL)
+		return VCD_ERR_FAIL;
+	DDL_MSG_LOW("ddl_state_transition: %s ~~> DDL_CLIENT_WAIT_FOR_DPBDONE",
+	ddl_get_state_string(ddl->client_state));
+	ddl->client_state = DDL_CLIENT_WAIT_FOR_DPBDONE;
+	ddl->cmd_state = DDL_CMD_DECODE_SET_DPB;
+	if (decoder->cont_mode) {
+		size_y = ddl_get_yuv_buf_size(decoder->client_frame_size.width,
+				decoder->client_frame_size.height,
+				DDL_YUV_BUF_TYPE_TILE);
+		size_c = ddl_get_yuv_buf_size(decoder->client_frame_size.width,
+				(decoder->client_frame_size.height >> 1),
+				DDL_YUV_BUF_TYPE_TILE);
+		vidc_sm_set_allocated_dpb_size(
+			&ddl->shared_mem[ddl->command_channel],
+			size_y,
+			size_c);
+	} else {
+		vidc_sm_set_allocated_dpb_size(
+			&ddl->shared_mem[ddl->command_channel],
+			decoder->dpb_buf_size.size_y,
+			decoder->dpb_buf_size.size_c);
+	}
+	init_buf_param.cmd_seq_num = ++ddl_context->cmd_seq_num;
+	init_buf_param.inst_id = ddl->instance_id;
+	init_buf_param.shared_mem_addr_offset = DDL_ADDR_OFFSET(
+				ddl_context->dram_base_a, ddl->shared_mem
+				[ddl->command_channel]);
+	init_buf_param.dpb_count = decoder->dp_buf.no_of_dec_pic_buf;
+	init_buf_param.dmx_disable = decoder->dmx_disable;
+	ddl_context->vidc_decode_init_buffers[ddl->command_channel] (
+		&init_buf_param);
+	return VCD_S_SUCCESS;
+}
+
+void ddl_vidc_decode_frame_run(struct ddl_client_context *ddl)
+{
+	struct ddl_context *ddl_context = ddl->ddl_context;
+	struct ddl_decoder_data *decoder = &(ddl->codec_data.decoder);
+	struct vcd_frame_data *bit_stream =
+		&(ddl->input_frame.vcd_frm);
+	struct ddl_dec_buffers *dec_buffers = &decoder->hw_bufs;
+	struct ddl_mask *dpb_mask = &ddl->codec_data.decoder.dpb_mask;
+	struct vidc_1080p_dec_frame_start_param dec_param;
+	u32 dpb_addr_y[32], index;
+	ddl_set_core_start_time(__func__, DEC_OP_TIME);
+	ddl_set_core_start_time(__func__, DEC_IP_TIME);
+	if ((!bit_stream->data_len) || (!bit_stream->physical)) {
+		ddl_vidc_decode_eos_run(ddl);
+		return;
+	}
+	DDL_MSG_LOW("ddl_state_transition: %s ~~"
+		"DDL_CLIENT_WAIT_FOR_FRAME_DONE",
+		ddl_get_state_string(ddl->client_state));
+	ddl->client_state = DDL_CLIENT_WAIT_FOR_FRAME_DONE;
+	ddl_vidc_decode_dynamic_property(ddl, true);
+	ddl_decoder_dpb_transact(decoder, NULL, DDL_DPB_OP_SET_MASK);
+	ddl->cmd_state = DDL_CMD_DECODE_FRAME;
+	dec_param.cmd_seq_num = ++ddl_context->cmd_seq_num;
+	dec_param.inst_id = ddl->instance_id;
+	dec_param.shared_mem_addr_offset = DDL_ADDR_OFFSET(
+				ddl_context->dram_base_a, ddl->shared_mem
+				[ddl->command_channel]);
+	dec_param.stream_buffer_addr_offset = DDL_OFFSET(
+			ddl_context->dram_base_a.align_physical_addr,
+			bit_stream->physical);
+	dec_param.stream_frame_size = bit_stream->data_len;
+	dec_param.stream_buffersize = decoder->client_input_buf_req.sz;
+	dec_param.descriptor_buffer_addr_offset = DDL_ADDR_OFFSET(
+	ddl_context->dram_base_a, dec_buffers->desc);
+	dec_param.descriptor_buffer_size = dec_buffers->desc.buffer_size;
+	dec_param.release_dpb_bit_mask = dpb_mask->hw_mask;
+	dec_param.decode = VIDC_1080P_DEC_TYPE_FRAME_DATA;
+	dec_param.dpb_count = decoder->dp_buf.no_of_dec_pic_buf;
+	dec_param.dmx_disable = decoder->dmx_disable;
+	if (decoder->dmx_disable)
+		ddl_fill_dec_desc_buffer(ddl);
+	if (decoder->flush_pending) {
+		dec_param.dpb_flush = true;
+		decoder->flush_pending = false;
+	} else
+		dec_param.dpb_flush = false;
+	vidc_sm_set_frame_tag(&ddl->shared_mem[ddl->command_channel],
+		bit_stream->ip_frm_tag);
+	if (ddl_context->pix_cache_enable) {
+		for (index = 0; index <
+			decoder->dp_buf.no_of_dec_pic_buf; index++) {
+			dpb_addr_y[index] = (u32)
+			decoder->dp_buf.dec_pic_buffers
+				[index].vcd_frm.physical;
+		}
+		vidc_pix_cache_init_luma_chroma_base_addr(
+			decoder->dp_buf.no_of_dec_pic_buf,
+			dpb_addr_y, NULL);
+		vidc_pix_cache_set_frame_range(decoder->dpb_buf_size.size_y,
+			decoder->dpb_buf_size.size_c);
+		vidc_pix_cache_clear_cache_tags();
+	}
+	ddl_context->vidc_decode_frame_start[ddl->command_channel] (
+		&dec_param);
+}
+
+void ddl_vidc_decode_eos_run(struct ddl_client_context *ddl)
+{
+	struct ddl_context *ddl_context = ddl->ddl_context;
+	struct ddl_decoder_data *decoder = &(ddl->codec_data.decoder);
+	struct vcd_frame_data *bit_stream =
+		&(ddl->input_frame.vcd_frm);
+	struct ddl_dec_buffers *dec_buffers = &(decoder->hw_bufs);
+	struct ddl_mask *dpb_mask =
+		&(ddl->codec_data.decoder.dpb_mask);
+	struct vidc_1080p_dec_frame_start_param dec_param;
+
+	DDL_MSG_LOW("ddl_state_transition: %s ~~> DDL_CLIENT_WAIT_FOR_EOS_DONE",
+	ddl_get_state_string(ddl->client_state));
+	ddl->client_state = DDL_CLIENT_WAIT_FOR_EOS_DONE;
+	if (decoder->output_order == VCD_DEC_ORDER_DECODE)
+		decoder->dynamic_prop_change |= DDL_DEC_REQ_OUTPUT_FLUSH;
+	ddl_vidc_decode_dynamic_property(ddl, true);
+	ddl_decoder_dpb_transact(decoder, NULL, DDL_DPB_OP_SET_MASK);
+	decoder->dynmic_prop_change_req = true;
+	ddl->cmd_state = DDL_CMD_EOS;
+	memset(&dec_param, 0, sizeof(dec_param));
+	dec_param.cmd_seq_num = ++ddl_context->cmd_seq_num;
+	dec_param.inst_id = ddl->instance_id;
+	dec_param.shared_mem_addr_offset = DDL_ADDR_OFFSET(
+			ddl_context->dram_base_a,
+			ddl->shared_mem[ddl->command_channel]);
+	dec_param.descriptor_buffer_addr_offset = DDL_ADDR_OFFSET(
+	ddl_context->dram_base_a, dec_buffers->desc);
+	dec_param.descriptor_buffer_size = dec_buffers->desc.buffer_size;
+	dec_param.release_dpb_bit_mask = dpb_mask->hw_mask;
+	dec_param.decode = VIDC_1080P_DEC_TYPE_LAST_FRAME_DATA;
+	dec_param.dpb_count = decoder->dp_buf.no_of_dec_pic_buf;
+	if (decoder->flush_pending) {
+		dec_param.dpb_flush = true;
+		decoder->flush_pending = false;
+	} else
+		dec_param.dpb_flush = false;
+	vidc_sm_set_frame_tag(&ddl->shared_mem[ddl->command_channel],
+	bit_stream->ip_frm_tag);
+	ddl_context->vidc_decode_frame_start[ddl->command_channel] (
+		&dec_param);
+}
+
+void ddl_vidc_encode_eos_run(struct ddl_client_context *ddl)
+{
+	struct vidc_1080p_enc_frame_start_param enc_param;
+	struct ddl_context *ddl_context = ddl->ddl_context;
+	struct ddl_encoder_data *encoder = &(ddl->codec_data.encoder);
+	DDL_MSG_LOW("%s\n", __func__);
+	ddl->client_state = DDL_CLIENT_WAIT_FOR_EOS_DONE;
+	ddl_vidc_encode_dynamic_property(ddl, true);
+	ddl->client_state = DDL_CMD_EOS;
+	DDL_MEMSET(&enc_param, 0, sizeof(enc_param));
+	enc_param.encode = VIDC_1080P_ENC_TYPE_LAST_FRAME_DATA;
+	enc_param.cmd_seq_num = ++ddl_context->cmd_seq_num;
+	enc_param.inst_id = ddl->instance_id;
+	enc_param.shared_mem_addr_offset =
+		DDL_ADDR_OFFSET(ddl_context->dram_base_a,
+		ddl->shared_mem[ddl->command_channel]);
+	enc_param.current_y_addr_offset = 0;
+	enc_param.current_c_addr_offset = 0;
+	enc_param.stream_buffer_size = 0;
+	enc_param.intra_frame = encoder->intra_frame_insertion;
+	vidc_sm_set_frame_tag(&ddl->shared_mem[ddl->command_channel],
+				ddl->input_frame.vcd_frm.ip_frm_tag);
+	ddl_context->vidc_encode_frame_start[ddl->command_channel](
+						&enc_param);
+}
+
+int ddl_vidc_decode_get_avg_time(struct ddl_client_context *ddl)
+{
+	int avg_time = 0;
+	struct ddl_decoder_data *decoder = &(ddl->codec_data.decoder);
+	avg_time = decoder->avg_dec_time;
+	return avg_time;
+}
+
+void ddl_vidc_decode_reset_avg_time(struct ddl_client_context *ddl)
+{
+	struct ddl_decoder_data *decoder = &(ddl->codec_data.decoder);
+	decoder->avg_dec_time = 0;
+	decoder->dec_time_sum = 0;
+}
diff --git a/drivers/video/msm/vidc/1080p/ddl/vidc.c b/drivers/video/msm/vidc/1080p/ddl/vidc.c
new file mode 100644
index 0000000..d399847
--- /dev/null
+++ b/drivers/video/msm/vidc/1080p/ddl/vidc.c
@@ -0,0 +1,1108 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "vidc.h"
+#include "vidc_hwio.h"
+
+
+#define VIDC_1080P_INIT_CH_INST_ID      0x0000ffff
+#define VIDC_1080P_RESET_VI             0x3f7
+#define VIDC_1080P_RESET_VI_RISC        0x3f6
+#define VIDC_1080P_RESET_VI_VIDC_RISC    0x3f2
+#define VIDC_1080P_RESET_ALL            0
+#define VIDC_1080P_RESET_RISC           0x3fe
+#define VIDC_1080P_RESET_NONE           0x3ff
+#define VIDC_1080P_INTERRUPT_CLEAR      0
+#define VIDC_1080P_MAX_H264DECODER_DPB  32
+#define VIDC_1080P_MAX_DEC_RECON_BUF    32
+
+#define VIDC_1080P_SI_RG7_DISPLAY_STATUS_MASK    0x00000007
+#define VIDC_1080P_SI_RG7_DISPLAY_STATUS_SHIFT   0
+#define VIDC_1080P_SI_RG7_DISPLAY_CODING_MASK    0x00000008
+#define VIDC_1080P_SI_RG7_DISPLAY_CODING_SHIFT   3
+#define VIDC_1080P_SI_RG7_DISPLAY_RES_MASK       0x00000030
+#define VIDC_1080P_SI_RG7_DISPLAY_RES_SHIFT      4
+
+#define VIDC_1080P_SI_RG7_DISPLAY_CROP_MASK      0x00000040
+#define VIDC_1080P_SI_RG7_DISPLAY_CROP_SHIFT     6
+
+#define VIDC_1080P_SI_RG7_DISPLAY_CORRECT_MASK    0x00000180
+#define VIDC_1080P_SI_RG7_DISPLAY_CORRECT_SHIFT   7
+#define VIDC_1080P_SI_RG8_DECODE_FRAMETYPE_MASK  0x00000007
+
+#define VIDC_1080P_SI_RG10_NUM_DPB_BMSK      0x00003fff
+#define VIDC_1080P_SI_RG10_NUM_DPB_SHFT      0
+#define VIDC_1080P_SI_RG10_DPB_FLUSH_BMSK    0x00004000
+#define VIDC_1080P_SI_RG10_DPB_FLUSH_SHFT    14
+#define VIDC_1080P_SI_RG10_DMX_DISABLE_BMSK  0x00008000
+#define VIDC_1080P_SI_RG10_DMX_DISABLE_SHFT  15
+
+#define VIDC_1080P_SI_RG11_DECODE_STATUS_MASK    0x00000007
+#define VIDC_1080P_SI_RG11_DECODE_STATUS_SHIFT   0
+#define VIDC_1080P_SI_RG11_DECODE_CODING_MASK    0x00000008
+#define VIDC_1080P_SI_RG11_DECODE_CODING_SHIFT   3
+#define VIDC_1080P_SI_RG11_DECODE_RES_MASK       0x000000C0
+#define VIDC_1080P_SI_RG11_DECODE_RES_SHIFT      6
+#define VIDC_1080P_SI_RG11_DECODE_CROPP_MASK     0x00000100
+#define VIDC_1080P_SI_RG11_DECODE_CROPP_SHIFT    8
+
+#define VIDC_1080P_SI_RG11_DECODE_CORRECT_MASK    0x00000600
+#define VIDC_1080P_SI_RG11_DECODE_CORRECT_SHIFT   9
+#define VIDC_1080P_BASE_OFFSET_SHIFT         11
+
+
+#define VIDC_1080P_H264DEC_LUMA_ADDR      HWIO_REG_759068_ADDR
+#define VIDC_1080P_H264DEC_CHROMA_ADDR    HWIO_REG_515200_ADDR
+#define VIDC_1080P_H264DEC_MV_PLANE_ADDR  HWIO_REG_466192_ADDR
+
+#define VIDC_1080P_DEC_LUMA_ADDR        HWIO_REG_759068_ADDR
+#define VIDC_1080P_DEC_CHROMA_ADDR      HWIO_REG_515200_ADDR
+
+#define VIDC_1080P_DEC_TYPE_SEQ_HEADER         0x00010000
+#define VIDC_1080P_DEC_TYPE_FRAME_DATA         0x00020000
+#define VIDC_1080P_DEC_TYPE_LAST_FRAME_DATA    0x00030000
+#define VIDC_1080P_DEC_TYPE_INIT_BUFFERS       0x00040000
+
+#define VIDC_1080P_ENC_TYPE_SEQ_HEADER       0x00010000
+#define VIDC_1080P_ENC_TYPE_FRAME_DATA       0x00020000
+#define VIDC_1080P_ENC_TYPE_LAST_FRAME_DATA  0x00030000
+
+#define VIDC_1080P_SI_RG10_ENCODE_INPUT_BUFFER_FLUSH_BMSK   0x00004000
+#define VIDC_1080P_SI_RG10_ENCODE_INPUT_BUFFER_FLUSH_SHFT   14
+#define VIDC_1080P_SI_RG10_ENCODE_SLICE_IF_ENABLE_BMSK      0x80000000
+#define VIDC_1080P_SI_RG10_ENCODE_SLICE_IF_ENABLE_SHFT      31
+#define VIDC_1080P_MAX_INTRA_PERIOD 0xffff
+
+u8 *VIDC_BASE_PTR;
+
+void vidc_1080p_do_sw_reset(enum vidc_1080p_reset init_flag)
+{
+	if (init_flag == VIDC_1080P_RESET_IN_SEQ_FIRST_STAGE) {
+		u32 sw_reset_value = 0;
+
+		VIDC_HWIO_IN(REG_557899, &sw_reset_value);
+		sw_reset_value &= (~HWIO_REG_557899_RSTN_VI_BMSK);
+		VIDC_HWIO_OUT(REG_557899, sw_reset_value);
+		sw_reset_value &= (~HWIO_REG_557899_RSTN_RISC_BMSK);
+		VIDC_HWIO_OUT(REG_557899, sw_reset_value);
+		sw_reset_value &= (~(HWIO_REG_557899_RSTN_VIDCCORE_BMSK |
+					HWIO_REG_557899_RSTN_DMX_BMSK));
+
+		VIDC_HWIO_OUT(REG_557899, sw_reset_value);
+	} else if (init_flag == VIDC_1080P_RESET_IN_SEQ_SECOND_STAGE) {
+		VIDC_HWIO_OUT(REG_557899, VIDC_1080P_RESET_ALL);
+		VIDC_HWIO_OUT(REG_557899, VIDC_1080P_RESET_RISC);
+	}
+}
+
+void vidc_1080p_release_sw_reset(void)
+{
+	u32 nAxiCtl;
+	u32 nAxiStatus;
+	u32 nRdWrBurst;
+	u32 nOut_Order;
+
+	nOut_Order = VIDC_SETFIELD(1, HWIO_REG_5519_AXI_AOOORD_SHFT,
+					HWIO_REG_5519_AXI_AOOORD_BMSK);
+	VIDC_HWIO_OUT(REG_5519, nOut_Order);
+
+	nOut_Order = VIDC_SETFIELD(1, HWIO_REG_606364_AXI_AOOOWR_SHFT,
+					HWIO_REG_606364_AXI_AOOOWR_BMSK);
+	VIDC_HWIO_OUT(REG_606364, nOut_Order);
+
+	nAxiCtl = VIDC_SETFIELD(1, HWIO_REG_471159_AXI_HALT_REQ_SHFT,
+				HWIO_REG_471159_AXI_HALT_REQ_BMSK);
+
+	VIDC_HWIO_OUT(REG_471159, nAxiCtl);
+
+	do {
+		VIDC_HWIO_IN(REG_437878, &nAxiStatus);
+		nAxiStatus = VIDC_GETFIELD(nAxiStatus,
+					 HWIO_REG_437878_AXI_HALT_ACK_BMSK,
+					 HWIO_REG_437878_AXI_HALT_ACK_SHFT);
+	} while (0x3 != nAxiStatus);
+
+	nAxiCtl  =  VIDC_SETFIELD(1,
+				HWIO_REG_471159_AXI_RESET_SHFT,
+				HWIO_REG_471159_AXI_RESET_BMSK);
+
+	VIDC_HWIO_OUT(REG_471159, nAxiCtl);
+	VIDC_HWIO_OUT(REG_471159, 0);
+
+	nRdWrBurst = VIDC_SETFIELD(8,
+				HWIO_REG_922106_XBAR_OUT_MAX_RD_BURST_SHFT,
+				HWIO_REG_922106_XBAR_OUT_MAX_RD_BURST_BMSK) |
+	VIDC_SETFIELD(8, HWIO_REG_922106_XBAR_OUT_MAX_WR_BURST_SHFT,
+				HWIO_REG_922106_XBAR_OUT_MAX_WR_BURST_BMSK);
+
+	VIDC_HWIO_OUT(REG_922106, nRdWrBurst);
+
+	VIDC_HWIO_OUT(REG_666957, VIDC_1080P_INIT_CH_INST_ID);
+	VIDC_HWIO_OUT(REG_313350, VIDC_1080P_INIT_CH_INST_ID);
+	VIDC_HWIO_OUT(REG_695082, VIDC_1080P_RISC2HOST_CMD_EMPTY);
+	VIDC_HWIO_OUT(REG_611794, VIDC_1080P_HOST2RISC_CMD_EMPTY);
+	VIDC_HWIO_OUT(REG_557899, VIDC_1080P_RESET_NONE);
+}
+
+void vidc_1080p_clear_interrupt(void)
+{
+	VIDC_HWIO_OUT(REG_575377, VIDC_1080P_INTERRUPT_CLEAR);
+}
+
+void vidc_1080p_set_host2risc_cmd(enum vidc_1080p_host2risc_cmd
+	host2risc_command, u32 host2risc_arg1, u32 host2risc_arg2,
+	u32 host2risc_arg3, u32 host2risc_arg4)
+{
+	VIDC_HWIO_OUT(REG_611794, VIDC_1080P_HOST2RISC_CMD_EMPTY);
+	VIDC_HWIO_OUT(REG_356340, host2risc_arg1);
+	VIDC_HWIO_OUT(REG_899023, host2risc_arg2);
+	VIDC_HWIO_OUT(REG_987762, host2risc_arg3);
+	VIDC_HWIO_OUT(REG_544000, host2risc_arg4);
+	VIDC_HWIO_OUT(REG_611794, host2risc_command);
+}
+
+void vidc_1080p_get_risc2host_cmd(u32 *pn_risc2host_command,
+	u32 *pn_risc2host_arg1, u32 *pn_risc2host_arg2,
+	u32 *pn_risc2host_arg3, u32 *pn_risc2host_arg4)
+{
+	VIDC_HWIO_IN(REG_695082, pn_risc2host_command);
+	VIDC_HWIO_IN(REG_156596, pn_risc2host_arg1);
+	VIDC_HWIO_IN(REG_222292, pn_risc2host_arg2);
+	VIDC_HWIO_IN(REG_790962, pn_risc2host_arg3);
+	VIDC_HWIO_IN(REG_679882, pn_risc2host_arg4);
+}
+
+void vidc_1080p_get_risc2host_cmd_status(u32 err_status,
+	u32 *dec_err_status, u32 *disp_err_status)
+{
+	*dec_err_status = VIDC_GETFIELD(err_status,
+		VIDC_RISC2HOST_ARG2_VIDC_DEC_ERROR_STATUS_BMSK,
+		VIDC_RISC2HOST_ARG2_VIDC_DEC_ERROR_STATUS_SHFT);
+	*disp_err_status = VIDC_GETFIELD(err_status,
+		VIDC_RISC2HOST_ARG2_VIDC_DISP_ERROR_STATUS_BMSK,
+		VIDC_RISC2HOST_ARG2_VIDC_DISP_ERROR_STATUS_SHFT);
+
+}
+
+void vidc_1080p_clear_risc2host_cmd(void)
+{
+	VIDC_HWIO_OUT(REG_695082, VIDC_1080P_RISC2HOST_CMD_EMPTY);
+}
+
+void vidc_1080p_get_fw_version(u32 *pn_fw_version)
+{
+	VIDC_HWIO_IN(REG_653206, pn_fw_version);
+}
+
+void vidc_1080p_get_fw_status(u32 *pn_fw_status)
+{
+	VIDC_HWIO_IN(REG_350619, pn_fw_status);
+}
+
+void vidc_1080p_init_memory_controller(u32 dram_base_addr_a,
+	u32 dram_base_addr_b)
+{
+	VIDC_HWIO_OUT(REG_64440, dram_base_addr_a);
+	VIDC_HWIO_OUT(REG_675915, dram_base_addr_b);
+}
+
+void vidc_1080p_get_memory_controller_status(u32 *pb_mc_abusy,
+	u32 *pb_mc_bbusy)
+{
+	u32 mc_status = 0;
+
+	VIDC_HWIO_IN(REG_399911, &mc_status);
+	*pb_mc_abusy = (u32) ((mc_status &
+			HWIO_REG_399911_MC_BUSY_A_BMSK) >>
+			HWIO_REG_399911_MC_BUSY_A_SHFT);
+	*pb_mc_bbusy = (u32) ((mc_status &
+			HWIO_REG_399911_MC_BUSY_B_BMSK) >>
+			HWIO_REG_399911_MC_BUSY_B_SHFT);
+}
+
+void vidc_1080p_set_h264_decode_buffers(u32 dpb, u32 dec_vert_nb_mv_offset,
+	u32 dec_nb_ip_offset, u32 *pn_dpb_luma_offset,
+	u32 *pn_dpb_chroma_offset, u32 *pn_mv_buffer_offset)
+{
+	u32 count = 0, num_dpb_used = dpb;
+	u8 *vidc_dpb_luma_reg = (u8 *) VIDC_1080P_H264DEC_LUMA_ADDR;
+	u8 *vidc_dpb_chroma_reg = (u8 *) VIDC_1080P_H264DEC_CHROMA_ADDR;
+	u8 *vidc_mv_buffer_reg = (u8 *) VIDC_1080P_H264DEC_MV_PLANE_ADDR;
+
+	VIDC_HWIO_OUT(REG_931311, (dec_vert_nb_mv_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT));
+	VIDC_HWIO_OUT(REG_16277, (dec_nb_ip_offset >>
+	VIDC_1080P_BASE_OFFSET_SHIFT));
+	if (num_dpb_used > VIDC_1080P_MAX_H264DECODER_DPB)
+		num_dpb_used = VIDC_1080P_MAX_H264DECODER_DPB;
+	for (count = 0; count < num_dpb_used; count++) {
+		VIDC_OUT_DWORD(vidc_dpb_luma_reg,
+			(pn_dpb_luma_offset[count] >>
+			VIDC_1080P_BASE_OFFSET_SHIFT));
+		VIDC_OUT_DWORD(vidc_dpb_chroma_reg,
+			(pn_dpb_chroma_offset[count] >>
+			VIDC_1080P_BASE_OFFSET_SHIFT));
+		VIDC_OUT_DWORD(vidc_mv_buffer_reg,
+			(pn_mv_buffer_offset[count] >>
+			VIDC_1080P_BASE_OFFSET_SHIFT));
+		vidc_dpb_luma_reg += 4;
+		vidc_dpb_chroma_reg += 4;
+		vidc_mv_buffer_reg += 4;
+	}
+}
+
+void vidc_1080p_set_decode_recon_buffers(u32 recon_buffer,
+	u32 *pn_dec_luma, u32 *pn_dec_chroma)
+{
+	u32 count = 0, recon_buf_to_program = recon_buffer;
+	u8 *dec_recon_luma_reg = (u8 *) VIDC_1080P_DEC_LUMA_ADDR;
+	u8 *dec_recon_chroma_reg = (u8 *) VIDC_1080P_DEC_CHROMA_ADDR;
+
+	if (recon_buf_to_program > VIDC_1080P_MAX_DEC_RECON_BUF)
+		recon_buf_to_program = VIDC_1080P_MAX_DEC_RECON_BUF;
+	for (count = 0; count < recon_buf_to_program; count++) {
+		VIDC_OUT_DWORD(dec_recon_luma_reg, (pn_dec_luma[count] >>
+			VIDC_1080P_BASE_OFFSET_SHIFT));
+		VIDC_OUT_DWORD(dec_recon_chroma_reg,
+			(pn_dec_chroma[count] >>
+			VIDC_1080P_BASE_OFFSET_SHIFT));
+		dec_recon_luma_reg += 4;
+		dec_recon_chroma_reg += 4;
+	}
+}
+
+void vidc_1080p_set_mpeg4_divx_decode_work_buffers(u32 nb_dcac_buffer_offset,
+	u32 upnb_mv_buffer_offset, u32 sub_anchor_buffer_offset,
+	u32 overlay_transform_buffer_offset, u32 stx_parser_buffer_offset)
+{
+	VIDC_HWIO_OUT(REG_931311, (nb_dcac_buffer_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT));
+	VIDC_HWIO_OUT(REG_16277, (upnb_mv_buffer_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT));
+	VIDC_HWIO_OUT(REG_654169, (sub_anchor_buffer_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT));
+	VIDC_HWIO_OUT(REG_802794,
+		(overlay_transform_buffer_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT));
+	VIDC_HWIO_OUT(REG_252167, (stx_parser_buffer_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT));
+}
+
+void vidc_1080p_set_h263_decode_work_buffers(u32 nb_dcac_buffer_offset,
+	u32 upnb_mv_buffer_offset, u32 sub_anchor_buffer_offset,
+	u32 overlay_transform_buffer_offset)
+{
+	VIDC_HWIO_OUT(REG_931311, (nb_dcac_buffer_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT));
+	VIDC_HWIO_OUT(REG_16277, (upnb_mv_buffer_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT));
+	VIDC_HWIO_OUT(REG_654169, (sub_anchor_buffer_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT));
+	VIDC_HWIO_OUT(REG_802794,
+		(overlay_transform_buffer_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT));
+}
+
+void vidc_1080p_set_vc1_decode_work_buffers(u32 nb_dcac_buffer_offset,
+	u32 upnb_mv_buffer_offset, u32 sub_anchor_buffer_offset,
+	u32 overlay_transform_buffer_offset, u32 bitplain1Buffer_offset,
+	u32 bitplain2Buffer_offset, u32 bitplain3Buffer_offset)
+{
+	VIDC_HWIO_OUT(REG_931311, (nb_dcac_buffer_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT));
+	VIDC_HWIO_OUT(REG_16277, (upnb_mv_buffer_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT));
+	VIDC_HWIO_OUT(REG_654169, (sub_anchor_buffer_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT));
+	VIDC_HWIO_OUT(REG_802794,
+		(overlay_transform_buffer_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT));
+	VIDC_HWIO_OUT(REG_724376, (bitplain3Buffer_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT));
+	VIDC_HWIO_OUT(REG_551674, (bitplain2Buffer_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT));
+	VIDC_HWIO_OUT(REG_115991, (bitplain1Buffer_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT));
+}
+
+void vidc_1080p_set_encode_recon_buffers(u32 recon_buffer,
+	u32 *pn_enc_luma, u32 *pn_enc_chroma)
+{
+	if (recon_buffer > 0) {
+		VIDC_HWIO_OUT(REG_294579, (pn_enc_luma[0] >>
+			VIDC_1080P_BASE_OFFSET_SHIFT));
+		VIDC_HWIO_OUT(REG_759068, (pn_enc_chroma[0] >>
+			VIDC_1080P_BASE_OFFSET_SHIFT));
+	}
+	if (recon_buffer > 1) {
+		VIDC_HWIO_OUT(REG_616802, (pn_enc_luma[1] >>
+			VIDC_1080P_BASE_OFFSET_SHIFT));
+		VIDC_HWIO_OUT(REG_833502, (pn_enc_chroma[1] >>
+			VIDC_1080P_BASE_OFFSET_SHIFT));
+	}
+	if (recon_buffer > 2) {
+		VIDC_HWIO_OUT(REG_61427, (pn_enc_luma[2] >>
+			VIDC_1080P_BASE_OFFSET_SHIFT));
+		VIDC_HWIO_OUT(REG_68356, (pn_enc_chroma[2] >>
+			VIDC_1080P_BASE_OFFSET_SHIFT));
+	}
+	if (recon_buffer > 3) {
+		VIDC_HWIO_OUT(REG_23318, (pn_enc_luma[3] >>
+			VIDC_1080P_BASE_OFFSET_SHIFT));
+		VIDC_HWIO_OUT(REG_127855, (pn_enc_chroma[3] >>
+			VIDC_1080P_BASE_OFFSET_SHIFT));
+	}
+}
+
+void vidc_1080p_set_h264_encode_work_buffers(u32 up_row_mv_buffer_offset,
+	u32 direct_colzero_flag_buffer_offset,
+	u32 upper_intra_md_buffer_offset,
+	u32 upper_intra_pred_buffer_offset, u32 nbor_infor_buffer_offset,
+	u32 mb_info_offset)
+{
+	VIDC_HWIO_OUT(REG_515200, (up_row_mv_buffer_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT));
+	VIDC_HWIO_OUT(REG_69832,
+		(direct_colzero_flag_buffer_offset>>
+		VIDC_1080P_BASE_OFFSET_SHIFT));
+	VIDC_HWIO_OUT(REG_256132,
+		(upper_intra_md_buffer_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT));
+	VIDC_HWIO_OUT(REG_475648,
+		(upper_intra_pred_buffer_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT));
+	VIDC_HWIO_OUT(REG_29510, (nbor_infor_buffer_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT));
+	VIDC_HWIO_OUT(REG_175929, (mb_info_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT));
+}
+
+void vidc_1080p_set_h263_encode_work_buffers(u32 up_row_mv_buffer_offset,
+	u32 up_row_inv_quanti_coeff_buffer_offset)
+{
+	VIDC_HWIO_OUT(REG_515200, (up_row_mv_buffer_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT));
+	VIDC_HWIO_OUT(REG_29510, (
+		up_row_inv_quanti_coeff_buffer_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT));
+}
+
+void vidc_1080p_set_mpeg4_encode_work_buffers(u32 skip_flag_buffer_offset,
+	u32 up_row_inv_quanti_coeff_buffer_offset, u32 upper_mv_offset)
+{
+	VIDC_HWIO_OUT(REG_69832, (skip_flag_buffer_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT));
+	VIDC_HWIO_OUT(REG_29510, (
+		up_row_inv_quanti_coeff_buffer_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT));
+	VIDC_HWIO_OUT(REG_515200, (upper_mv_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT));
+}
+
+void vidc_1080p_set_encode_frame_size(u32 hori_size, u32 vert_size)
+{
+	VIDC_HWIO_OUT(REG_934655, hori_size);
+	VIDC_HWIO_OUT(REG_179070, vert_size);
+}
+
+void vidc_1080p_set_encode_profile_level(u32 encode_profile, u32 enc_level)
+{
+	u32 profile_level = 0;
+
+	profile_level = VIDC_SETFIELD(enc_level,
+				HWIO_REG_63643_LEVEL_SHFT,
+				HWIO_REG_63643_LEVEL_BMSK) |
+				VIDC_SETFIELD(encode_profile,
+				HWIO_REG_63643_PROFILE_SHFT,
+				HWIO_REG_63643_PROFILE_BMSK);
+	VIDC_HWIO_OUT(REG_63643, profile_level);
+}
+
+void vidc_1080p_set_encode_field_picture_structure(u32 enc_field_picture)
+{
+	VIDC_HWIO_OUT(REG_786024, enc_field_picture);
+}
+
+void vidc_1080p_set_decode_mpeg4_pp_filter(u32 lf_enables)
+{
+	VIDC_HWIO_OUT(REG_152500, lf_enables);
+}
+
+void vidc_1080p_set_decode_qp_save_control(u32 enable_q_pout)
+{
+	VIDC_HWIO_OUT(REG_143629, enable_q_pout);
+}
+
+void vidc_1080p_get_returned_channel_inst_id(u32 *pn_rtn_chid)
+{
+	VIDC_HWIO_IN(REG_607589, pn_rtn_chid);
+}
+
+void vidc_1080p_clear_returned_channel_inst_id(void)
+{
+	VIDC_HWIO_OUT(REG_607589, VIDC_1080P_INIT_CH_INST_ID);
+}
+
+void vidc_1080p_get_decode_seq_start_result(
+	struct vidc_1080p_seq_hdr_info *seq_hdr_info)
+{
+	u32 dec_disp_result;
+	u32 frame = 0;
+	VIDC_HWIO_IN(REG_845544, &seq_hdr_info->img_size_y);
+	VIDC_HWIO_IN(REG_859906, &seq_hdr_info->img_size_x);
+	VIDC_HWIO_IN(REG_490078, &seq_hdr_info->min_num_dpb);
+	VIDC_HWIO_IN(REG_489688, &seq_hdr_info->dec_frm_size);
+	VIDC_HWIO_IN(REG_853667, &dec_disp_result);
+	seq_hdr_info->disp_progressive = VIDC_GETFIELD(dec_disp_result,
+					VIDC_1080P_SI_RG7_DISPLAY_CODING_MASK,
+					VIDC_1080P_SI_RG7_DISPLAY_CODING_SHIFT);
+	seq_hdr_info->disp_crop_exists  = VIDC_GETFIELD(dec_disp_result,
+		VIDC_1080P_SI_RG7_DISPLAY_CROP_MASK,
+		VIDC_1080P_SI_RG7_DISPLAY_CROP_SHIFT);
+	VIDC_HWIO_IN(REG_692991, &dec_disp_result);
+	seq_hdr_info->dec_progressive = VIDC_GETFIELD(dec_disp_result,
+					VIDC_1080P_SI_RG11_DECODE_CODING_MASK,
+					VIDC_1080P_SI_RG11_DECODE_CODING_SHIFT);
+	seq_hdr_info->dec_crop_exists  = VIDC_GETFIELD(dec_disp_result,
+		VIDC_1080P_SI_RG11_DECODE_CROPP_MASK,
+		VIDC_1080P_SI_RG11_DECODE_CROPP_SHIFT);
+	VIDC_HWIO_IN(REG_760102, &frame);
+	seq_hdr_info->data_partition = ((frame & 0x8) >> 3);
+}
+
+void vidc_1080p_get_decoded_frame_size(u32 *pn_decoded_size)
+{
+	VIDC_HWIO_IN(REG_489688, pn_decoded_size);
+}
+
+void vidc_1080p_get_display_frame_result(
+	struct vidc_1080p_dec_disp_info *dec_disp_info)
+{
+	u32 display_result;
+	VIDC_HWIO_IN(REG_640904, &dec_disp_info->display_y_addr);
+	VIDC_HWIO_IN(REG_60114, &dec_disp_info->display_c_addr);
+	VIDC_HWIO_IN(REG_853667, &display_result);
+	VIDC_HWIO_IN(REG_845544, &dec_disp_info->img_size_y);
+	VIDC_HWIO_IN(REG_859906, &dec_disp_info->img_size_x);
+	dec_disp_info->display_status =
+		(enum vidc_1080p_display_status)
+		VIDC_GETFIELD(display_result,
+		VIDC_1080P_SI_RG7_DISPLAY_STATUS_MASK,
+		VIDC_1080P_SI_RG7_DISPLAY_STATUS_SHIFT);
+	dec_disp_info->display_coding =
+		(enum vidc_1080p_display_coding)
+	VIDC_GETFIELD(display_result, VIDC_1080P_SI_RG7_DISPLAY_CODING_MASK,
+		VIDC_1080P_SI_RG7_DISPLAY_CODING_SHIFT);
+	dec_disp_info->disp_resl_change = VIDC_GETFIELD(display_result,
+		VIDC_1080P_SI_RG7_DISPLAY_RES_MASK,
+		VIDC_1080P_SI_RG7_DISPLAY_RES_SHIFT);
+	dec_disp_info->disp_crop_exists = VIDC_GETFIELD(display_result,
+		VIDC_1080P_SI_RG7_DISPLAY_CROP_MASK,
+		VIDC_1080P_SI_RG7_DISPLAY_CROP_SHIFT);
+	dec_disp_info->display_correct = VIDC_GETFIELD(display_result,
+		VIDC_1080P_SI_RG7_DISPLAY_CORRECT_MASK,
+		VIDC_1080P_SI_RG7_DISPLAY_CORRECT_SHIFT);
+}
+
+void vidc_1080p_get_decode_frame(
+	enum vidc_1080p_decode_frame *pe_frame)
+{
+	u32 frame = 0;
+
+	VIDC_HWIO_IN(REG_760102, &frame);
+	*pe_frame = (enum vidc_1080p_decode_frame)
+		(frame & VIDC_1080P_SI_RG8_DECODE_FRAMETYPE_MASK);
+}
+
+void vidc_1080p_get_decode_frame_result(
+	struct vidc_1080p_dec_disp_info *dec_disp_info)
+{
+	u32 decode_result;
+
+	VIDC_HWIO_IN(REG_378318, &dec_disp_info->decode_y_addr);
+	VIDC_HWIO_IN(REG_203487, &dec_disp_info->decode_c_addr);
+	VIDC_HWIO_IN(REG_692991, &decode_result);
+	dec_disp_info->decode_status = (enum vidc_1080p_display_status)
+				VIDC_GETFIELD(decode_result,
+				VIDC_1080P_SI_RG11_DECODE_STATUS_MASK,
+				VIDC_1080P_SI_RG11_DECODE_STATUS_SHIFT);
+	dec_disp_info->decode_coding = (enum vidc_1080p_display_coding)
+				VIDC_GETFIELD(decode_result,
+				VIDC_1080P_SI_RG11_DECODE_CODING_MASK,
+				VIDC_1080P_SI_RG11_DECODE_CODING_SHIFT);
+	dec_disp_info->dec_resl_change = VIDC_GETFIELD(decode_result,
+		VIDC_1080P_SI_RG11_DECODE_RES_MASK,
+		VIDC_1080P_SI_RG11_DECODE_RES_SHIFT);
+	dec_disp_info->dec_crop_exists = VIDC_GETFIELD(decode_result,
+		VIDC_1080P_SI_RG11_DECODE_CROPP_MASK,
+		VIDC_1080P_SI_RG11_DECODE_CROPP_SHIFT);
+	dec_disp_info->decode_correct = VIDC_GETFIELD(decode_result,
+		VIDC_1080P_SI_RG11_DECODE_CORRECT_MASK,
+		VIDC_1080P_SI_RG11_DECODE_CORRECT_SHIFT);
+}
+
+void vidc_1080p_decode_seq_start_ch0(
+	struct vidc_1080p_dec_seq_start_param *param)
+{
+	VIDC_HWIO_OUT(REG_695082, VIDC_1080P_RISC2HOST_CMD_EMPTY);
+	VIDC_HWIO_OUT(REG_666957, VIDC_1080P_INIT_CH_INST_ID);
+	VIDC_HWIO_OUT(REG_117192,
+		param->stream_buffer_addr_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT);
+	VIDC_HWIO_OUT(REG_145068, param->stream_frame_size);
+	VIDC_HWIO_OUT(REG_921356,
+		param->descriptor_buffer_addr_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT);
+	VIDC_HWIO_OUT(REG_190381,  param->stream_buffersize);
+	VIDC_HWIO_OUT(REG_85655,  param->descriptor_buffer_size);
+	VIDC_HWIO_OUT(REG_889944,  param->shared_mem_addr_offset);
+	VIDC_HWIO_OUT(REG_404623, 0);
+	VIDC_HWIO_OUT(REG_397087, param->cmd_seq_num);
+	VIDC_HWIO_OUT(REG_666957, VIDC_1080P_DEC_TYPE_SEQ_HEADER |
+		param->inst_id);
+}
+
+void vidc_1080p_decode_seq_start_ch1(
+	struct vidc_1080p_dec_seq_start_param *param)
+{
+	VIDC_HWIO_OUT(REG_695082, VIDC_1080P_RISC2HOST_CMD_EMPTY);
+	VIDC_HWIO_OUT(REG_313350, VIDC_1080P_INIT_CH_INST_ID);
+	VIDC_HWIO_OUT(REG_980194,
+		param->stream_buffer_addr_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT);
+	VIDC_HWIO_OUT(REG_936704, param->stream_frame_size);
+	VIDC_HWIO_OUT(REG_821977,
+		param->descriptor_buffer_addr_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT);
+	VIDC_HWIO_OUT(REG_887095, param->stream_buffersize);
+	VIDC_HWIO_OUT(REG_576987, param->descriptor_buffer_size);
+	VIDC_HWIO_OUT(REG_652528, param->shared_mem_addr_offset);
+	VIDC_HWIO_OUT(REG_404623, 0);
+	VIDC_HWIO_OUT(REG_254093, param->cmd_seq_num);
+	VIDC_HWIO_OUT(REG_313350, VIDC_1080P_DEC_TYPE_SEQ_HEADER |
+		param->inst_id);
+}
+
+void vidc_1080p_decode_frame_start_ch0(
+	struct vidc_1080p_dec_frame_start_param *param)
+{
+	u32 dpb_config;
+
+	VIDC_HWIO_OUT(REG_695082, VIDC_1080P_RISC2HOST_CMD_EMPTY);
+	VIDC_HWIO_OUT(REG_666957, VIDC_1080P_INIT_CH_INST_ID);
+	if ((param->decode == VIDC_1080P_DEC_TYPE_LAST_FRAME_DATA) &&
+		((!param->stream_buffer_addr_offset) ||
+		(!param->stream_frame_size))) {
+		VIDC_HWIO_OUT(REG_117192, 0);
+		VIDC_HWIO_OUT(REG_145068, 0);
+		VIDC_HWIO_OUT(REG_190381, 0);
+	} else {
+		VIDC_HWIO_OUT(REG_117192,
+			param->stream_buffer_addr_offset >>
+			VIDC_1080P_BASE_OFFSET_SHIFT);
+		VIDC_HWIO_OUT(REG_145068,
+			param->stream_frame_size);
+		VIDC_HWIO_OUT(REG_190381,
+			param->stream_buffersize);
+	}
+	dpb_config = VIDC_SETFIELD(param->dmx_disable,
+					VIDC_1080P_SI_RG10_DMX_DISABLE_SHFT,
+					VIDC_1080P_SI_RG10_DMX_DISABLE_BMSK) |
+				VIDC_SETFIELD(param->dpb_flush,
+					VIDC_1080P_SI_RG10_DPB_FLUSH_SHFT,
+					VIDC_1080P_SI_RG10_DPB_FLUSH_BMSK) |
+				VIDC_SETFIELD(param->dpb_count,
+					VIDC_1080P_SI_RG10_NUM_DPB_SHFT,
+					VIDC_1080P_SI_RG10_NUM_DPB_BMSK);
+	VIDC_HWIO_OUT(REG_921356,
+		param->descriptor_buffer_addr_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT);
+	VIDC_HWIO_OUT(REG_85655, param->descriptor_buffer_size);
+	VIDC_HWIO_OUT(REG_86830, param->release_dpb_bit_mask);
+	VIDC_HWIO_OUT(REG_889944, param->shared_mem_addr_offset);
+	VIDC_HWIO_OUT(REG_404623, dpb_config);
+	VIDC_HWIO_OUT(REG_397087, param->cmd_seq_num);
+	VIDC_HWIO_OUT(REG_666957, (u32)param->decode |
+		param->inst_id);
+}
+
+
+void vidc_1080p_decode_frame_start_ch1(
+	struct vidc_1080p_dec_frame_start_param *param)
+{
+	u32 dpb_config;
+
+	VIDC_HWIO_OUT(REG_695082, VIDC_1080P_RISC2HOST_CMD_EMPTY);
+	VIDC_HWIO_OUT(REG_313350, VIDC_1080P_INIT_CH_INST_ID);
+	if ((param->decode == VIDC_1080P_DEC_TYPE_LAST_FRAME_DATA) &&
+		((!param->stream_buffer_addr_offset) ||
+		(!param->stream_frame_size))) {
+		VIDC_HWIO_OUT(REG_980194, 0);
+		VIDC_HWIO_OUT(REG_936704, 0);
+		VIDC_HWIO_OUT(REG_887095, 0);
+	} else {
+		VIDC_HWIO_OUT(REG_980194,
+			param->stream_buffer_addr_offset >>
+			VIDC_1080P_BASE_OFFSET_SHIFT);
+		VIDC_HWIO_OUT(REG_936704,
+			param->stream_frame_size);
+		VIDC_HWIO_OUT(REG_887095,
+			param->stream_buffersize);
+	}
+	dpb_config = VIDC_SETFIELD(param->dmx_disable,
+					VIDC_1080P_SI_RG10_DMX_DISABLE_SHFT,
+					VIDC_1080P_SI_RG10_DMX_DISABLE_BMSK) |
+				VIDC_SETFIELD(param->dpb_flush,
+					VIDC_1080P_SI_RG10_DPB_FLUSH_SHFT,
+					VIDC_1080P_SI_RG10_DPB_FLUSH_BMSK) |
+				VIDC_SETFIELD(param->dpb_count,
+					VIDC_1080P_SI_RG10_NUM_DPB_SHFT,
+					VIDC_1080P_SI_RG10_NUM_DPB_BMSK);
+	VIDC_HWIO_OUT(REG_821977,
+		param->descriptor_buffer_addr_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT);
+	VIDC_HWIO_OUT(REG_576987, param->descriptor_buffer_size);
+	VIDC_HWIO_OUT(REG_70448, param->release_dpb_bit_mask);
+	VIDC_HWIO_OUT(REG_652528, param->shared_mem_addr_offset);
+	VIDC_HWIO_OUT(REG_220637, dpb_config);
+	VIDC_HWIO_OUT(REG_254093, param->cmd_seq_num);
+	VIDC_HWIO_OUT(REG_313350, (u32)param->decode |
+		param->inst_id);
+}
+
+void vidc_1080p_decode_init_buffers_ch0(
+	struct vidc_1080p_dec_init_buffers_param *param)
+{
+	u32 dpb_config;
+	VIDC_HWIO_OUT(REG_695082, VIDC_1080P_RISC2HOST_CMD_EMPTY);
+	VIDC_HWIO_OUT(REG_666957, VIDC_1080P_INIT_CH_INST_ID);
+	dpb_config = VIDC_SETFIELD(param->dmx_disable,
+					VIDC_1080P_SI_RG10_DMX_DISABLE_SHFT,
+					VIDC_1080P_SI_RG10_DMX_DISABLE_BMSK) |
+				VIDC_SETFIELD(param->dpb_count,
+					VIDC_1080P_SI_RG10_NUM_DPB_SHFT,
+					VIDC_1080P_SI_RG10_NUM_DPB_BMSK);
+	VIDC_HWIO_OUT(REG_889944, param->shared_mem_addr_offset);
+	VIDC_HWIO_OUT(REG_404623, dpb_config);
+	VIDC_HWIO_OUT(REG_397087, param->cmd_seq_num);
+	VIDC_HWIO_OUT(REG_666957, VIDC_1080P_DEC_TYPE_INIT_BUFFERS |
+		param->inst_id);
+}
+
+void vidc_1080p_decode_init_buffers_ch1(
+	struct vidc_1080p_dec_init_buffers_param *param)
+{
+	u32 dpb_config;
+	VIDC_HWIO_OUT(REG_695082, VIDC_1080P_RISC2HOST_CMD_EMPTY);
+	VIDC_HWIO_OUT(REG_313350, VIDC_1080P_INIT_CH_INST_ID);
+	dpb_config = VIDC_SETFIELD(param->dmx_disable,
+					VIDC_1080P_SI_RG10_DMX_DISABLE_SHFT,
+					VIDC_1080P_SI_RG10_DMX_DISABLE_BMSK) |
+				VIDC_SETFIELD(param->dpb_count,
+					VIDC_1080P_SI_RG10_NUM_DPB_SHFT,
+					VIDC_1080P_SI_RG10_NUM_DPB_BMSK);
+	VIDC_HWIO_OUT(REG_652528,  param->shared_mem_addr_offset);
+	VIDC_HWIO_OUT(REG_220637, dpb_config);
+	VIDC_HWIO_OUT(REG_254093, param->cmd_seq_num);
+	VIDC_HWIO_OUT(REG_313350, VIDC_1080P_DEC_TYPE_INIT_BUFFERS |
+		param->inst_id);
+}
+
+void vidc_1080p_set_dec_resolution_ch0(u32 width, u32 height)
+{
+	VIDC_HWIO_OUT(REG_612810, height);
+	VIDC_HWIO_OUT(REG_175608, width);
+}
+
+void vidc_1080p_set_dec_resolution_ch1(u32 width, u32 height)
+{
+	VIDC_HWIO_OUT(REG_655721, height);
+	VIDC_HWIO_OUT(REG_548308, width);
+}
+
+void vidc_1080p_get_encode_frame_info(
+	struct vidc_1080p_enc_frame_info *frame_info)
+{
+	VIDC_HWIO_IN(REG_845544, &(frame_info->enc_frame_size));
+	VIDC_HWIO_IN(REG_859906,
+		&(frame_info->enc_picture_count));
+	VIDC_HWIO_IN(REG_490078,
+		&(frame_info->enc_write_pointer));
+	VIDC_HWIO_IN(REG_640904,
+		(u32 *)(&(frame_info->enc_frame)));
+	VIDC_HWIO_IN(REG_60114,
+		&(frame_info->enc_luma_address));
+	frame_info->enc_luma_address = frame_info->enc_luma_address <<
+		VIDC_1080P_BASE_OFFSET_SHIFT;
+	VIDC_HWIO_IN(REG_489688,
+		&(frame_info->enc_chroma_address));
+	frame_info->enc_chroma_address = frame_info->\
+		enc_chroma_address << VIDC_1080P_BASE_OFFSET_SHIFT;
+}
+
+void vidc_1080p_encode_seq_start_ch0(
+	struct vidc_1080p_enc_seq_start_param *param)
+{
+	VIDC_HWIO_OUT(REG_695082, VIDC_1080P_RISC2HOST_CMD_EMPTY);
+	VIDC_HWIO_OUT(REG_666957, VIDC_1080P_INIT_CH_INST_ID);
+	VIDC_HWIO_OUT(REG_117192,
+		param->stream_buffer_addr_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT);
+	VIDC_HWIO_OUT(REG_921356, param->stream_buffer_size);
+	VIDC_HWIO_OUT(REG_889944, param->shared_mem_addr_offset);
+	VIDC_HWIO_OUT(REG_397087, param->cmd_seq_num);
+	VIDC_HWIO_OUT(REG_666957, VIDC_1080P_ENC_TYPE_SEQ_HEADER |
+		param->inst_id);
+}
+
+void vidc_1080p_encode_seq_start_ch1(
+	struct vidc_1080p_enc_seq_start_param *param)
+{
+	VIDC_HWIO_OUT(REG_695082, VIDC_1080P_RISC2HOST_CMD_EMPTY);
+	VIDC_HWIO_OUT(REG_313350, VIDC_1080P_INIT_CH_INST_ID);
+	VIDC_HWIO_OUT(REG_980194,
+		param->stream_buffer_addr_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT);
+	VIDC_HWIO_OUT(REG_821977, param->stream_buffer_size);
+	VIDC_HWIO_OUT(REG_652528, param->shared_mem_addr_offset);
+	VIDC_HWIO_OUT(REG_254093, param->cmd_seq_num);
+	VIDC_HWIO_OUT(REG_313350, VIDC_1080P_ENC_TYPE_SEQ_HEADER |
+		param->inst_id);
+}
+
+void vidc_1080p_encode_frame_start_ch0(
+	struct vidc_1080p_enc_frame_start_param *param)
+{
+	u32 input_flush;
+	VIDC_HWIO_OUT(REG_695082, VIDC_1080P_RISC2HOST_CMD_EMPTY);
+	VIDC_HWIO_OUT(REG_666957, VIDC_1080P_INIT_CH_INST_ID);
+	VIDC_HWIO_OUT(REG_117192,
+		param->stream_buffer_addr_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT);
+	VIDC_HWIO_OUT(REG_921356, param->stream_buffer_size);
+	VIDC_HWIO_OUT(REG_612810, param->current_y_addr_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT);
+	VIDC_HWIO_OUT(REG_175608, param->current_c_addr_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT);
+	VIDC_HWIO_OUT(REG_190381, param->intra_frame);
+	VIDC_HWIO_OUT(REG_889944, param->shared_mem_addr_offset);
+	input_flush = VIDC_SETFIELD(param->input_flush,
+			VIDC_1080P_SI_RG10_ENCODE_INPUT_BUFFER_FLUSH_SHFT,
+			VIDC_1080P_SI_RG10_ENCODE_INPUT_BUFFER_FLUSH_BMSK);
+	input_flush |= VIDC_SETFIELD(param->slice_enable,
+			VIDC_1080P_SI_RG10_ENCODE_SLICE_IF_ENABLE_SHFT,
+			VIDC_1080P_SI_RG10_ENCODE_SLICE_IF_ENABLE_BMSK);
+	VIDC_HWIO_OUT(REG_404623, input_flush);
+	VIDC_HWIO_OUT(REG_397087, param->cmd_seq_num);
+
+	VIDC_HWIO_OUT(REG_666957, (u32)param->encode |
+		param->inst_id);
+}
+
+void vidc_1080p_encode_frame_start_ch1(
+	struct vidc_1080p_enc_frame_start_param *param)
+{
+	u32 input_flush;
+	VIDC_HWIO_OUT(REG_695082, VIDC_1080P_RISC2HOST_CMD_EMPTY);
+	VIDC_HWIO_OUT(REG_313350, VIDC_1080P_INIT_CH_INST_ID);
+	VIDC_HWIO_OUT(REG_980194,
+		param->stream_buffer_addr_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT);
+	VIDC_HWIO_OUT(REG_821977, param->stream_buffer_size);
+	VIDC_HWIO_OUT(REG_655721, param->current_y_addr_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT);
+	VIDC_HWIO_OUT(REG_548308,  param->current_c_addr_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT);
+	VIDC_HWIO_OUT(REG_887095, param->intra_frame);
+	VIDC_HWIO_OUT(REG_652528, param->shared_mem_addr_offset);
+	input_flush = VIDC_SETFIELD(param->input_flush,
+			VIDC_1080P_SI_RG10_ENCODE_INPUT_BUFFER_FLUSH_SHFT,
+			VIDC_1080P_SI_RG10_ENCODE_INPUT_BUFFER_FLUSH_BMSK);
+	input_flush |= VIDC_SETFIELD(param->slice_enable,
+			VIDC_1080P_SI_RG10_ENCODE_SLICE_IF_ENABLE_SHFT,
+			VIDC_1080P_SI_RG10_ENCODE_SLICE_IF_ENABLE_BMSK);
+
+	VIDC_HWIO_OUT(REG_404623, input_flush);
+	VIDC_HWIO_OUT(REG_254093, param->cmd_seq_num);
+	VIDC_HWIO_OUT(REG_313350, (u32)param->encode |
+		param->inst_id);
+}
+
+void vidc_1080p_encode_slice_batch_start_ch0(
+	struct vidc_1080p_enc_frame_start_param *param)
+{
+	u32 input_flush;
+	VIDC_HWIO_OUT(REG_695082, VIDC_1080P_RISC2HOST_CMD_EMPTY);
+	VIDC_HWIO_OUT(REG_666957, VIDC_1080P_INIT_CH_INST_ID);
+	VIDC_HWIO_OUT(REG_612810, param->current_y_addr_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT);
+	VIDC_HWIO_OUT(REG_175608, param->current_c_addr_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT);
+	VIDC_HWIO_OUT(REG_190381, param->intra_frame);
+	VIDC_HWIO_OUT(REG_889944, param->shared_mem_addr_offset);
+	input_flush = VIDC_SETFIELD(param->input_flush,
+			VIDC_1080P_SI_RG10_ENCODE_INPUT_BUFFER_FLUSH_SHFT,
+			VIDC_1080P_SI_RG10_ENCODE_INPUT_BUFFER_FLUSH_BMSK);
+	input_flush |= VIDC_SETFIELD(param->slice_enable,
+			VIDC_1080P_SI_RG10_ENCODE_SLICE_IF_ENABLE_SHFT,
+			VIDC_1080P_SI_RG10_ENCODE_SLICE_IF_ENABLE_BMSK);
+	VIDC_HWIO_OUT(REG_404623, input_flush);
+	VIDC_HWIO_OUT(REG_397087, param->cmd_seq_num);
+
+	VIDC_HWIO_OUT(REG_666957, (u32)param->encode |
+		param->inst_id);
+
+}
+
+void vidc_1080p_encode_slice_batch_start_ch1(
+	struct vidc_1080p_enc_frame_start_param *param)
+{
+	u32 input_flush;
+	VIDC_HWIO_OUT(REG_695082, VIDC_1080P_RISC2HOST_CMD_EMPTY);
+	VIDC_HWIO_OUT(REG_313350, VIDC_1080P_INIT_CH_INST_ID);
+	VIDC_HWIO_OUT(REG_655721, param->current_y_addr_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT);
+	VIDC_HWIO_OUT(REG_548308,  param->current_c_addr_offset >>
+		VIDC_1080P_BASE_OFFSET_SHIFT);
+	VIDC_HWIO_OUT(REG_887095, param->intra_frame);
+	VIDC_HWIO_OUT(REG_652528, param->shared_mem_addr_offset);
+	input_flush = VIDC_SETFIELD(param->input_flush,
+			VIDC_1080P_SI_RG10_ENCODE_INPUT_BUFFER_FLUSH_SHFT,
+			VIDC_1080P_SI_RG10_ENCODE_INPUT_BUFFER_FLUSH_BMSK);
+	input_flush |= VIDC_SETFIELD(param->slice_enable,
+			VIDC_1080P_SI_RG10_ENCODE_SLICE_IF_ENABLE_SHFT,
+			VIDC_1080P_SI_RG10_ENCODE_SLICE_IF_ENABLE_BMSK);
+
+	VIDC_HWIO_OUT(REG_404623, input_flush);
+	VIDC_HWIO_OUT(REG_254093, param->cmd_seq_num);
+	VIDC_HWIO_OUT(REG_313350, (u32)param->encode |
+		param->inst_id);
+
+}
+
+void vidc_1080p_set_encode_picture(u32 number_p, u32 number_b)
+{
+	u32 picture, ifrm_ctrl;
+	if (number_p >= VIDC_1080P_MAX_INTRA_PERIOD)
+		ifrm_ctrl = 0;
+	else
+		ifrm_ctrl = number_p + 1;
+	picture = VIDC_SETFIELD(1 ,
+				HWIO_REG_783891_ENC_PIC_TYPE_USE_SHFT,
+				HWIO_REG_783891_ENC_PIC_TYPE_USE_BMSK) |
+				VIDC_SETFIELD(ifrm_ctrl,
+					HWIO_REG_783891_I_FRM_CTRL_SHFT,
+					HWIO_REG_783891_I_FRM_CTRL_BMSK)
+				| VIDC_SETFIELD(number_b ,
+				HWIO_REG_783891_B_FRM_CTRL_SHFT ,
+				HWIO_REG_783891_B_FRM_CTRL_BMSK);
+	VIDC_HWIO_OUT(REG_783891, picture);
+}
+
+void vidc_1080p_set_encode_multi_slice_control(
+	enum vidc_1080p_MSlice_selection multiple_slice_selection,
+	u32 mslice_mb, u32 mslice_byte)
+{
+	VIDC_HWIO_OUT(REG_226332, multiple_slice_selection);
+	VIDC_HWIO_OUT(REG_696136, mslice_mb);
+	VIDC_HWIO_OUT(REG_515564, mslice_byte);
+}
+
+void vidc_1080p_set_encode_circular_intra_refresh(u32 cir_num)
+{
+	VIDC_HWIO_OUT(REG_886210, cir_num);
+}
+
+void vidc_1080p_set_encode_input_frame_format(
+	enum vidc_1080p_memory_access_method memory_format)
+{
+	VIDC_HWIO_OUT(REG_645603, memory_format);
+}
+
+void vidc_1080p_set_encode_padding_control(u32 pad_ctrl_on,
+	u32 cr_pad_val, u32 cb_pad_val, u32 luma_pad_val)
+{
+	u32 padding = VIDC_SETFIELD(pad_ctrl_on ,
+				HWIO_REG_811733_PAD_CTRL_ON_SHFT,
+				HWIO_REG_811733_PAD_CTRL_ON_BMSK) |
+			VIDC_SETFIELD(cr_pad_val ,
+				HWIO_REG_811733_CR_PAD_VIDC_SHFT ,
+				HWIO_REG_811733_CR_PAD_VIDC_BMSK) |
+			VIDC_SETFIELD(cb_pad_val ,
+				HWIO_REG_811733_CB_PAD_VIDC_SHFT ,
+				HWIO_REG_811733_CB_PAD_VIDC_BMSK) |
+			VIDC_SETFIELD(luma_pad_val ,
+				HWIO_REG_811733_LUMA_PAD_VIDC_SHFT ,
+				HWIO_REG_811733_LUMA_PAD_VIDC_BMSK) ;
+	VIDC_HWIO_OUT(REG_811733, padding);
+}
+
+void vidc_1080p_encode_set_rc_config(u32 enable_frame_level_rc,
+	u32 enable_mb_level_rc_flag, u32 frame_qp)
+{
+	u32 rc_config = VIDC_SETFIELD(enable_frame_level_rc ,
+					HWIO_REG_559908_FR_RC_EN_SHFT ,
+					HWIO_REG_559908_FR_RC_EN_BMSK) |
+			VIDC_SETFIELD(enable_mb_level_rc_flag ,
+					HWIO_REG_559908_MB_RC_EN_SHFT,
+					HWIO_REG_559908_MB_RC_EN_BMSK) |
+			VIDC_SETFIELD(frame_qp ,
+					HWIO_REG_559908_FRAME_QP_SHFT ,
+					HWIO_REG_559908_FRAME_QP_BMSK);
+	VIDC_HWIO_OUT(REG_559908, rc_config);
+}
+
+void vidc_1080p_encode_set_frame_level_rc_params(u32 rc_frame_rate,
+	u32 target_bitrate, u32 reaction_coeff)
+{
+	VIDC_HWIO_OUT(REG_977937, rc_frame_rate);
+	VIDC_HWIO_OUT(REG_166135, target_bitrate);
+	VIDC_HWIO_OUT(REG_550322, reaction_coeff);
+}
+
+void vidc_1080p_encode_set_qp_params(u32 max_qp, u32 min_qp)
+{
+	u32 qbound = VIDC_SETFIELD(max_qp , HWIO_REG_109072_MAX_QP_SHFT,
+					HWIO_REG_109072_MAX_QP_BMSK) |
+					VIDC_SETFIELD(min_qp,
+					HWIO_REG_109072_MIN_QP_SHFT ,
+					HWIO_REG_109072_MIN_QP_BMSK);
+	VIDC_HWIO_OUT(REG_109072, qbound);
+}
+
+void vidc_1080p_encode_set_mb_level_rc_params(u32 disable_dark_region_as_flag,
+	u32 disable_smooth_region_as_flag , u32 disable_static_region_as_flag,
+	u32 disable_activity_region_flag)
+{
+	u32 rc_active_feature = VIDC_SETFIELD(
+					disable_dark_region_as_flag,
+					HWIO_REG_949086_DARK_DISABLE_SHFT,
+					HWIO_REG_949086_DARK_DISABLE_BMSK) |
+					VIDC_SETFIELD(
+					disable_smooth_region_as_flag,
+					HWIO_REG_949086_SMOOTH_DISABLE_SHFT,
+					HWIO_REG_949086_SMOOTH_DISABLE_BMSK) |
+					VIDC_SETFIELD(
+					disable_static_region_as_flag,
+					HWIO_REG_949086_STATIC_DISABLE_SHFT,
+					HWIO_REG_949086_STATIC_DISABLE_BMSK) |
+					VIDC_SETFIELD(
+					disable_activity_region_flag,
+					HWIO_REG_949086_ACT_DISABLE_SHFT,
+					HWIO_REG_949086_ACT_DISABLE_BMSK);
+	VIDC_HWIO_OUT(REG_949086, rc_active_feature);
+}
+
+void vidc_1080p_set_h264_encode_entropy(
+	enum vidc_1080p_entropy_sel entropy_sel)
+{
+	VIDC_HWIO_OUT(REG_447796, entropy_sel);
+}
+
+void vidc_1080p_set_h264_encode_loop_filter(
+	enum vidc_1080p_DBConfig db_config, u32 slice_alpha_offset,
+	u32 slice_beta_offset)
+{
+	VIDC_HWIO_OUT(REG_152500, db_config);
+	VIDC_HWIO_OUT(REG_266285, slice_alpha_offset);
+	VIDC_HWIO_OUT(REG_964731, slice_beta_offset);
+}
+
+void vidc_1080p_set_h264_encoder_p_frame_ref_count(u32 max_reference)
+{
+	u32 ref_frames;
+	ref_frames = VIDC_SETFIELD(max_reference,
+		HWIO_REG_744348_P_SHFT,
+		HWIO_REG_744348_P_BMSK);
+	VIDC_HWIO_OUT(REG_744348, ref_frames);
+}
+
+void vidc_1080p_set_h264_encode_8x8transform_control(u32 enable_8x8transform)
+{
+	VIDC_HWIO_OUT(REG_672163, enable_8x8transform);
+}
+
+void vidc_1080p_set_mpeg4_encode_quarter_pel_control(
+	u32 enable_mpeg4_quarter_pel)
+{
+	VIDC_HWIO_OUT(REG_330132, enable_mpeg4_quarter_pel);
+}
+
+void vidc_1080p_set_device_base_addr(u8 *mapped_va)
+{
+	VIDC_BASE_PTR = mapped_va;
+}
+
+void vidc_1080p_get_intra_bias(u32 *bias)
+{
+	u32 intra_bias;
+
+	VIDC_HWIO_IN(REG_676866, &intra_bias);
+	*bias = VIDC_GETFIELD(intra_bias,
+					HWIO_REG_676866_RMSK,
+					HWIO_REG_676866_SHFT);
+}
+
+void vidc_1080p_set_intra_bias(u32 bias)
+{
+	u32 intra_bias;
+
+	intra_bias = VIDC_SETFIELD(bias,
+					HWIO_REG_676866_SHFT,
+					HWIO_REG_676866_RMSK);
+	VIDC_HWIO_OUT(REG_676866, intra_bias);
+}
+
+void vidc_1080p_get_bi_directional_bias(u32 *bi_directional_bias)
+{
+	u32 nbi_direct_bias;
+
+	VIDC_HWIO_IN(REG_54267, &nbi_direct_bias);
+	*bi_directional_bias = VIDC_GETFIELD(nbi_direct_bias,
+					HWIO_REG_54267_RMSK,
+					HWIO_REG_54267_SHFT);
+}
+
+void vidc_1080p_set_bi_directional_bias(u32 bi_directional_bias)
+{
+	u32 nbi_direct_bias;
+
+	nbi_direct_bias = VIDC_SETFIELD(bi_directional_bias,
+					HWIO_REG_54267_SHFT,
+					HWIO_REG_54267_RMSK);
+	VIDC_HWIO_OUT(REG_54267, nbi_direct_bias);
+}
+
+void vidc_1080p_get_encoder_sequence_header_size(u32 *seq_header_size)
+{
+	VIDC_HWIO_IN(REG_845544, seq_header_size);
+}
+
+void vidc_1080p_get_intermedia_stage_debug_counter(
+	u32 *intermediate_stage_counter)
+{
+	VIDC_HWIO_IN(REG_805993, intermediate_stage_counter);
+}
+
+void vidc_1080p_get_exception_status(u32 *exception_status)
+{
+	VIDC_HWIO_IN(REG_493355, exception_status);
+}
+
+void vidc_1080p_frame_start_realloc(u32 instance_id)
+{
+	VIDC_HWIO_OUT(REG_695082, VIDC_1080P_RISC2HOST_CMD_EMPTY);
+	VIDC_HWIO_OUT(REG_666957, VIDC_1080P_INIT_CH_INST_ID);
+	VIDC_HWIO_OUT(REG_666957,
+		VIDC_1080P_DEC_TYPE_FRAME_START_REALLOC | instance_id);
+}
diff --git a/drivers/video/msm/vidc/1080p/ddl/vidc.h b/drivers/video/msm/vidc/1080p/ddl/vidc.h
new file mode 100644
index 0000000..7460ef3
--- /dev/null
+++ b/drivers/video/msm/vidc/1080p/ddl/vidc.h
@@ -0,0 +1,586 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VIDC_H_
+#define _VIDC_H_
+
+#include "vidc_hwio_reg.h"
+
+#define VIDC_1080P_RISC2HOST_CMD_EMPTY               0
+#define VIDC_1080P_RISC2HOST_CMD_OPEN_CH_RET         1
+#define VIDC_1080P_RISC2HOST_CMD_CLOSE_CH_RET        2
+#define VIDC_1080P_RISC2HOST_CMD_SEQ_DONE_RET        4
+#define VIDC_1080P_RISC2HOST_CMD_FRAME_DONE_RET      5
+#define VIDC_1080P_RISC2HOST_CMD_SLICE_DONE_RET      6
+#define VIDC_1080P_RISC2HOST_CMD_ENC_COMPLETE_RET    7
+#define VIDC_1080P_RISC2HOST_CMD_SYS_INIT_RET        8
+#define VIDC_1080P_RISC2HOST_CMD_FW_STATUS_RET       9
+#define VIDC_1080P_RISC2HOST_CMD_FLUSH_COMMAND_RET  12
+#define VIDC_1080P_RISC2HOST_CMD_ABORT_RET          13
+#define VIDC_1080P_RISC2HOST_CMD_BATCH_ENC_RET      14
+#define VIDC_1080P_RISC2HOST_CMD_INIT_BUFFERS_RET   15
+#define VIDC_1080P_RISC2HOST_CMD_EDFU_INT_RET       16
+#define VIDC_1080P_RISC2HOST_CMD_ERROR_RET          32
+
+#define VIDC_RISC2HOST_ARG2_VIDC_DISP_ERROR_STATUS_BMSK  0xffff0000
+#define VIDC_RISC2HOST_ARG2_VIDC_DISP_ERROR_STATUS_SHFT  16
+#define VIDC_RISC2HOST_ARG2_VIDC_DEC_ERROR_STATUS_BMSK   0x0000ffff
+#define VIDC_RISC2HOST_ARG2_VIDC_DEC_ERROR_STATUS_SHFT   0
+
+#define VIDC_1080P_ERROR_INVALID_CHANNEL_NUMBER                  1
+#define VIDC_1080P_ERROR_INVALID_COMMAND_ID                      2
+#define VIDC_1080P_ERROR_CHANNEL_ALREADY_IN_USE                  3
+#define VIDC_1080P_ERROR_CHANNEL_NOT_OPEN_BEFORE_CHANNEL_CLOSE   4
+#define VIDC_1080P_ERROR_OPEN_CH_ERROR_SEQ_START                 5
+#define VIDC_1080P_ERROR_SEQ_START_ALREADY_CALLED                6
+#define VIDC_1080P_ERROR_OPEN_CH_ERROR_INIT_BUFFERS              7
+#define VIDC_1080P_ERROR_SEQ_START_ERROR_INIT_BUFFERS            8
+#define VIDC_1080P_ERROR_INIT_BUFFER_ALREADY_CALLED              9
+#define VIDC_1080P_ERROR_OPEN_CH_ERROR_FRAME_START              10
+#define VIDC_1080P_ERROR_SEQ_START_ERROR_FRAME_START            11
+#define VIDC_1080P_ERROR_INIT_BUFFERS_ERROR_FRAME_START         12
+#define VIDC_1080P_ERROR_RESOLUTION_CHANGED                     13
+#define VIDC_1080P_ERROR_INVALID_COMMAND_LAST_FRAME             14
+#define VIDC_1080P_ERROR_INVALID_COMMAND                        15
+#define VIDC_1080P_ERROR_INVALID_CODEC_TYPE                     16
+
+#define VIDC_1080P_ERROR_MEM_ALLOCATION_FAILED                  20
+#define VIDC_1080P_ERROR_INSUFFICIENT_CONTEXT_SIZE              25
+#define VIDC_1080P_ERROR_UNSUPPORTED_FEATURE_IN_PROFILE         27
+#define VIDC_1080P_ERROR_RESOLUTION_NOT_SUPPORTED               28
+
+#define VIDC_1080P_ERROR_HEADER_NOT_FOUND                 52
+#define VIDC_1080P_ERROR_VOS_END_CODE_RECEIVED            53
+#define VIDC_1080P_ERROR_FRAME_RATE_NOT_SUPPORTED         62
+#define VIDC_1080P_ERROR_INVALID_QP_VALUE                 63
+#define VIDC_1080P_ERROR_INVALID_RC_REACTION_COEFFICIENT  64
+#define VIDC_1080P_ERROR_INVALID_CPB_SIZE_AT_GIVEN_LEVEL  65
+#define VIDC_1080P_ERROR_B_FRAME_NOT_SUPPORTED            66
+#define VIDC_1080P_ERROR_ALLOC_DPB_SIZE_NOT_SUFFICIENT    71
+#define VIDC_1080P_ERROR_NUM_DPB_OUT_OF_RANGE             74
+#define VIDC_1080P_ERROR_NULL_METADATA_INPUT_POINTER      77
+#define VIDC_1080P_ERROR_NULL_DPB_POINTER                 78
+#define VIDC_1080P_ERROR_NULL_OTH_EXT_BUFADDR             79
+#define VIDC_1080P_ERROR_NULL_MV_POINTER                  80
+#define VIDC_1080P_ERROR_DIVIDE_BY_ZERO                   81
+#define VIDC_1080P_ERROR_BIT_STREAM_BUF_EXHAUST           82
+#define VIDC_1080P_ERROR_DESCRIPTOR_BUFFER_EMPTY          83
+#define VIDC_1080P_ERROR_DMA_TX_NOT_COMPLETE              84
+#define VIDC_1080P_ERROR_DESCRIPTOR_TABLE_ENTRY_INVALID   85
+#define VIDC_1080P_ERROR_MB_COEFF_NOT_DONE        86
+#define VIDC_1080P_ERROR_CODEC_SLICE_NOT_DONE     87
+#define VIDC_1080P_ERROR_VIDC_CORE_TIME_OUT       88
+#define VIDC_1080P_ERROR_VC1_BITPLANE_DECODE_ERR  89
+#define VIDC_1080P_ERROR_VSP_NOT_READY            90
+#define VIDC_1080P_ERROR_BUFFER_FULL_STATE        91
+
+#define VIDC_1080P_ERROR_RESOLUTION_MISMATCH      112
+#define VIDC_1080P_ERROR_NV_QUANT_ERR             113
+#define VIDC_1080P_ERROR_SYNC_MARKER_ERR          114
+#define VIDC_1080P_ERROR_FEATURE_NOT_SUPPORTED    115
+#define VIDC_1080P_ERROR_MEM_CORRUPTION           116
+#define VIDC_1080P_ERROR_INVALID_REFERENCE_FRAME  117
+#define VIDC_1080P_ERROR_PICTURE_CODING_TYPE_ERR  118
+#define VIDC_1080P_ERROR_MV_RANGE_ERR             119
+#define VIDC_1080P_ERROR_PICTURE_STRUCTURE_ERR    120
+#define VIDC_1080P_ERROR_SLICE_ADDR_INVALID       121
+#define VIDC_1080P_ERROR_NON_PAIRED_FIELD_NOT_SUPPORTED         122
+#define VIDC_1080P_ERROR_NON_FRAME_DATA_RECEIVED                123
+#define VIDC_1080P_ERROR_NO_BUFFER_RELEASED_FROM_HOST           125
+#define VIDC_1080P_ERROR_NULL_FW_DEBUG_INFO_POINTER             126
+#define VIDC_1080P_ERROR_ALLOC_DEBUG_INFO_SIZE_INSUFFICIENT     127
+#define VIDC_1080P_ERROR_NALU_HEADER_ERROR       128
+#define VIDC_1080P_ERROR_SPS_PARSE_ERROR         129
+#define VIDC_1080P_ERROR_PPS_PARSE_ERROR         130
+#define VIDC_1080P_ERROR_SLICE_PARSE_ERROR       131
+#define VIDC_1080P_ERROR_SYNC_POINT_NOT_RECEIVED  171
+
+#define VIDC_1080P_WARN_COMMAND_FLUSHED                  145
+#define VIDC_1080P_WARN_METADATA_NO_SPACE_NUM_CONCEAL_MB 150
+#define VIDC_1080P_WARN_METADATA_NO_SPACE_QP             151
+#define VIDC_1080P_WARN_METADATA_NO_SPACE_CONCEAL_MB     152
+#define VIDC_1080P_WARN_METADATA_NO_SPACE_VC1_PARAM      153
+#define VIDC_1080P_WARN_METADATA_NO_SPACE_SEI            154
+#define VIDC_1080P_WARN_METADATA_NO_SPACE_VUI            155
+#define VIDC_1080P_WARN_METADATA_NO_SPACE_EXTRA          156
+#define VIDC_1080P_WARN_METADATA_NO_SPACE_DATA_NONE      157
+#define VIDC_1080P_WARN_FRAME_RATE_UNKNOWN               158
+#define VIDC_1080P_WARN_ASPECT_RATIO_UNKNOWN             159
+#define VIDC_1080P_WARN_COLOR_PRIMARIES_UNKNOWN          160
+#define VIDC_1080P_WARN_TRANSFER_CHAR_UNKNOWN            161
+#define VIDC_1080P_WARN_MATRIX_COEFF_UNKNOWN             162
+#define VIDC_1080P_WARN_NON_SEQ_SLICE_ADDR               163
+#define VIDC_1080P_WARN_BROKEN_LINK                      164
+#define VIDC_1080P_WARN_FRAME_CONCEALED                  165
+#define VIDC_1080P_WARN_PROFILE_UNKNOWN                  166
+#define VIDC_1080P_WARN_LEVEL_UNKNOWN                    167
+#define VIDC_1080P_WARN_BIT_RATE_NOT_SUPPORTED           168
+#define VIDC_1080P_WARN_COLOR_DIFF_FORMAT_NOT_SUPPORTED  169
+#define VIDC_1080P_WARN_NULL_EXTRA_METADATA_POINTER      170
+#define VIDC_1080P_WARN_DEBLOCKING_NOT_DONE              178
+#define VIDC_1080P_WARN_INCOMPLETE_FRAME                 179
+#define VIDC_1080P_WARN_METADATA_NO_SPACE_MB_INFO        180
+#define VIDC_1080P_WARN_METADATA_NO_SPACE_SLICE_SIZE     181
+#define VIDC_1080P_WARN_RESOLUTION_WARNING               182
+
+#define VIDC_1080P_WARN_NO_LONG_TERM_REFERENCE           183
+#define VIDC_1080P_WARN_NO_SPACE_MPEG2_DATA_DUMP         190
+#define VIDC_1080P_WARN_METADATA_NO_SPACE_MISSING_MB     191
+
+#define VIDC_1080P_H264_ENC_TYPE_P       0
+#define VIDC_1080P_H264_ENC_TYPE_B       1
+#define VIDC_1080P_H264_ENC_TYPE_IDR     2
+#define VIDC_1080P_MP4_H263_ENC_TYPE_I   0
+#define VIDC_1080P_MP4_H263_ENC_TYPE_P   1
+#define VIDC_1080P_MP4_H263_ENC_TYPE_B   2
+
+#define VIDC_1080P_MPEG4_LEVEL0    0
+#define VIDC_1080P_MPEG4_LEVEL0b   9
+#define VIDC_1080P_MPEG4_LEVEL1    1
+#define VIDC_1080P_MPEG4_LEVEL2    2
+#define VIDC_1080P_MPEG4_LEVEL3    3
+#define VIDC_1080P_MPEG4_LEVEL3b   7
+#define VIDC_1080P_MPEG4_LEVEL4    4
+#define VIDC_1080P_MPEG4_LEVEL4a   4
+#define VIDC_1080P_MPEG4_LEVEL5    5
+#define VIDC_1080P_MPEG4_LEVEL6    6
+#define VIDC_1080P_MPEG4_LEVEL7    7
+
+#define VIDC_1080P_H264_LEVEL1     10
+#define VIDC_1080P_H264_LEVEL1b    9
+#define VIDC_1080P_H264_LEVEL1p1   11
+#define VIDC_1080P_H264_LEVEL1p2   12
+#define VIDC_1080P_H264_LEVEL1p3   13
+#define VIDC_1080P_H264_LEVEL2     20
+#define VIDC_1080P_H264_LEVEL2p1   21
+#define VIDC_1080P_H264_LEVEL2p2   22
+#define VIDC_1080P_H264_LEVEL3     30
+#define VIDC_1080P_H264_LEVEL3p1   31
+#define VIDC_1080P_H264_LEVEL3p2   32
+#define VIDC_1080P_H264_LEVEL4     40
+#define VIDC_1080P_H264_LEVEL5p1   51
+#define VIDC_1080P_H264_LEVEL_MAX  VIDC_1080P_H264_LEVEL5p1
+
+#define VIDC_1080P_H263_LEVEL10    10
+#define VIDC_1080P_H263_LEVEL20    20
+#define VIDC_1080P_H263_LEVEL30    30
+#define VIDC_1080P_H263_LEVEL40    40
+#define VIDC_1080P_H263_LEVEL45    45
+#define VIDC_1080P_H263_LEVEL50    50
+#define VIDC_1080P_H263_LEVEL60    60
+#define VIDC_1080P_H263_LEVEL70    70
+
+#define VIDC_1080P_BUS_ERROR_HANDLER                   0x01
+#define VIDC_1080P_ILLEVIDC_INSTRUCTION_HANDLER         0x02
+#define VIDC_1080P_TICK_HANDLER                        0x04
+#define VIDC_1080P_TRAP_HANDLER                        0x10
+#define VIDC_1080P_ALIGN_HANDLER                       0x20
+#define VIDC_1080P_RANGE_HANDLER                       0x40
+#define VIDC_1080P_DTLB_MISS_EXCEPTION_HANDLER         0x80
+#define VIDC_1080P_ITLB_MISS_EXCEPTION_HANDLER        0x100
+#define VIDC_1080P_DATA_PAGE_FAULT_EXCEPTION_HANDLER  0x200
+#define VIDC_1080P_INST_PAGE_FAULT_EXCEPTION_HANDLER  0x400
+#define VIDC_1080P_SLICE_BATCH_MAX_STRM_BFR           8
+#define VIDC_1080P_SLICE_BATCH_IN_SIZE(idx)           (4 * sizeof(u32) + \
+							idx * sizeof(u32))
+enum vidc_1080p_reset{
+	VIDC_1080P_RESET_IN_SEQ_FIRST_STAGE   = 0x0,
+	VIDC_1080P_RESET_IN_SEQ_SECOND_STAGE  = 0x1,
+};
+enum vidc_1080p_memory_access_method{
+	VIDC_1080P_TILE_LINEAR = 0,
+	VIDC_1080P_TILE_16x16  = 2,
+	VIDC_1080P_TILE_64x32  = 3,
+	VIDC_1080P_TILE_32BIT  = 0x7FFFFFFF
+};
+enum vidc_1080p_host2risc_cmd{
+	VIDC_1080P_HOST2RISC_CMD_EMPTY          = 0,
+	VIDC_1080P_HOST2RISC_CMD_OPEN_CH        = 1,
+	VIDC_1080P_HOST2RISC_CMD_CLOSE_CH       = 2,
+	VIDC_1080P_HOST2RISC_CMD_SYS_INIT       = 3,
+	VIDC_1080P_HOST2RISC_CMD_FLUSH_COMMMAND = 4,
+	VIDC_1080P_HOST2RISC_CMD_CONTINUE_ENC   = 7,
+	VIDC_1080P_HOST2RISC_CMD_ABORT_ENC      = 8,
+	VIDC_1080P_HOST2RISC_CMD_32BIT          = 0x7FFFFFFF
+};
+enum vidc_1080p_decode_p_cache_enable{
+	VIDC_1080P_DECODE_PCACHE_ENABLE_P   = 0,
+	VIDC_1080P_DECODE_PCACHE_ENABLE_B   = 1,
+	VIDC_1080P_DECODE_PCACHE_ENABLE_PB  = 2,
+	VIDC_1080P_DECODE_PCACHE_DISABLE    = 3,
+	VIDC_1080P_DECODE_PCACHE_32BIT      = 0x7FFFFFFF
+};
+enum vidc_1080p_encode_p_cache_enable{
+	VIDC_1080P_ENCODE_PCACHE_ENABLE  = 0,
+	VIDC_1080P_ENCODE_PCACHE_DISABLE = 3,
+	VIDC_1080P_ENCODE_PCACHE_32BIT   = 0x7FFFFFFF
+};
+enum vidc_1080p_codec{
+	VIDC_1080P_H264_DECODE     = 0,
+	VIDC_1080P_VC1_DECODE      = 1,
+	VIDC_1080P_MPEG4_DECODE    = 2,
+	VIDC_1080P_MPEG2_DECODE    = 3,
+	VIDC_1080P_H263_DECODE     = 4,
+	VIDC_1080P_VC1_RCV_DECODE  = 5,
+	VIDC_1080P_DIVX311_DECODE  = 6,
+	VIDC_1080P_DIVX412_DECODE  = 7,
+	VIDC_1080P_DIVX502_DECODE  = 8,
+	VIDC_1080P_DIVX503_DECODE  = 9,
+	VIDC_1080P_H264_ENCODE    = 16,
+	VIDC_1080P_MPEG4_ENCODE   = 17,
+	VIDC_1080P_H263_ENCODE    = 18,
+	VIDC_1080P_CODEC_32BIT    = 0x7FFFFFFF
+};
+enum vidc_1080p_entropy_sel{
+	VIDC_1080P_ENTROPY_SEL_CAVLC = 0,
+	VIDC_1080P_ENTROPY_SEL_CABAC = 1,
+	VIDC_1080P_ENTROPY_32BIT     = 0x7FFFFFFF
+};
+enum vidc_1080p_DBConfig{
+	VIDC_1080P_DB_ALL_BLOCKING_BOUNDARY  = 0,
+	VIDC_1080P_DB_DISABLE                = 1,
+	VIDC_1080P_DB_SKIP_SLICE_BOUNDARY    = 2,
+	VIDC_1080P_DB_32BIT                  = 0x7FFFFFFF
+};
+enum vidc_1080p_MSlice_selection{
+	VIDC_1080P_MSLICE_DISABLE        = 0,
+	VIDC_1080P_MSLICE_BY_MB_COUNT    = 1,
+	VIDC_1080P_MSLICE_BY_BYTE_COUNT  = 3,
+	VIDC_1080P_MSLICE_32BIT          = 0x7FFFFFFF
+};
+enum vidc_1080p_display_status{
+	VIDC_1080P_DISPLAY_STATUS_DECODE_ONLY        = 0,
+	VIDC_1080P_DISPLAY_STATUS_DECODE_AND_DISPLAY = 1,
+	VIDC_1080P_DISPLAY_STATUS_DISPLAY_ONLY       = 2,
+	VIDC_1080P_DISPLAY_STATUS_DPB_EMPTY          = 3,
+	VIDC_1080P_DISPLAY_STATUS_NOOP               = 4,
+	VIDC_1080P_DISPLAY_STATUS_32BIT              = 0x7FFFFFFF
+};
+enum vidc_1080p_display_coding{
+	VIDC_1080P_DISPLAY_CODING_PROGRESSIVE_SCAN = 0,
+	VIDC_1080P_DISPLAY_CODING_INTERLACED       = 1,
+	VIDC_1080P_DISPLAY_CODING_32BIT            = 0x7FFFFFFF
+};
+enum vidc_1080p_decode_frame{
+	VIDC_1080P_DECODE_FRAMETYPE_NOT_CODED  = 0,
+	VIDC_1080P_DECODE_FRAMETYPE_I          = 1,
+	VIDC_1080P_DECODE_FRAMETYPE_P          = 2,
+	VIDC_1080P_DECODE_FRAMETYPE_B          = 3,
+	VIDC_1080P_DECODE_FRAMETYPE_OTHERS     = 4,
+	VIDC_1080P_DECODE_FRAMETYPE_32BIT      = 0x7FFFFFFF
+};
+enum vidc_1080P_decode_frame_correct_type {
+	VIDC_1080P_DECODE_NOT_CORRECT = 0,
+	VIDC_1080P_DECODE_CORRECT = 1,
+	VIDC_1080P_DECODE_APPROX_CORRECT = 2,
+	VIDC_1080P_DECODE_CORRECTTYPE_32BIT = 0x7FFFFFFF
+};
+enum vidc_1080p_encode_frame{
+	VIDC_1080P_ENCODE_FRAMETYPE_NOT_CODED  = 0,
+	VIDC_1080P_ENCODE_FRAMETYPE_I          = 1,
+	VIDC_1080P_ENCODE_FRAMETYPE_P          = 2,
+	VIDC_1080P_ENCODE_FRAMETYPE_B          = 3,
+	VIDC_1080P_ENCODE_FRAMETYPE_SKIPPED    = 4,
+	VIDC_1080P_ENCODE_FRAMETYPE_OTHERS     = 5,
+	VIDC_1080P_ENCODE_FRAMETYPE_32BIT      = 0x7FFFFFFF
+
+};
+
+enum vidc_1080p_decode_idc_format {
+	VIDC_1080P_IDCFORMAT_MONOCHROME = 0,
+	VIDC_1080P_IDCFORMAT_420 = 1,
+	VIDC_1080P_IDCFORMAT_422 = 2,
+	VIDC_1080P_IDCFORMAT_444 = 3,
+	VIDC_1080P_IDCFORMAT_OTHERS = 4,
+	VIDC_1080P_IDCFORMAT_32BIT = 0x7FFFFFFF
+};
+
+#define VIDC_1080P_PROFILE_MPEG4_SIMPLE      0x00000000
+#define VIDC_1080P_PROFILE_MPEG4_ADV_SIMPLE  0x00000001
+
+#define VIDC_1080P_PROFILE_H264_MAIN         0x00000000
+#define VIDC_1080P_PROFILE_H264_HIGH         0x00000001
+#define VIDC_1080P_PROFILE_H264_BASELINE     0x00000002
+
+
+enum vidc_1080p_decode{
+	VIDC_1080P_DEC_TYPE_SEQ_HEADER       = 0x00010000,
+	VIDC_1080P_DEC_TYPE_FRAME_DATA       = 0x00020000,
+	VIDC_1080P_DEC_TYPE_LAST_FRAME_DATA  = 0x00030000,
+	VIDC_1080P_DEC_TYPE_INIT_BUFFERS     = 0x00040000,
+	VIDC_1080P_DEC_TYPE_FRAME_START_REALLOC = 0x00050000,
+	VIDC_1080P_DEC_TYPE_32BIT            = 0x7FFFFFFF
+};
+enum vidc_1080p_encode{
+	VIDC_1080P_ENC_TYPE_SEQ_HEADER        = 0x00010000,
+	VIDC_1080P_ENC_TYPE_FRAME_DATA        = 0x00020000,
+	VIDC_1080P_ENC_TYPE_LAST_FRAME_DATA   = 0x00030000,
+	VIDC_1080P_ENC_TYPE_SLICE_BATCH_START = 0x00070000,
+	VIDC_1080P_ENC_TYPE_32BIT             = 0x7FFFFFFF
+};
+struct vidc_1080p_dec_seq_start_param{
+	u32 cmd_seq_num;
+	u32 inst_id;
+	u32 shared_mem_addr_offset;
+	u32 stream_buffer_addr_offset;
+	u32 stream_buffersize;
+	u32 stream_frame_size;
+	u32 descriptor_buffer_addr_offset;
+	u32 descriptor_buffer_size;
+};
+struct vidc_1080p_dec_frame_start_param{
+	u32 cmd_seq_num;
+	u32 inst_id;
+	u32 shared_mem_addr_offset;
+	u32 stream_buffer_addr_offset;
+	u32 stream_buffersize;
+	u32 stream_frame_size;
+	u32 descriptor_buffer_addr_offset;
+	u32 descriptor_buffer_size;
+	u32 release_dpb_bit_mask;
+	u32 dpb_count;
+	u32 dpb_flush;
+	u32 dmx_disable;
+	enum vidc_1080p_decode decode;
+};
+struct vidc_1080p_dec_init_buffers_param{
+	u32 cmd_seq_num;
+	u32 inst_id;
+	u32 shared_mem_addr_offset;
+	u32 dpb_count;
+	u32 dmx_disable;
+};
+struct vidc_1080p_seq_hdr_info{
+	u32 img_size_x;
+	u32 img_size_y;
+	u32 dec_frm_size;
+	u32 min_num_dpb;
+	u32 min_luma_dpb_size;
+	u32 min_chroma_dpb_size;
+	u32 profile;
+	u32 level;
+	u32 disp_progressive;
+	u32 disp_crop_exists;
+	u32 dec_progressive;
+	u32 dec_crop_exists;
+	u32 crop_right_offset;
+	u32 crop_left_offset;
+	u32 crop_bottom_offset;
+	u32 crop_top_offset;
+	u32 data_partition;
+};
+struct vidc_1080p_enc_seq_start_param{
+	u32 cmd_seq_num;
+	u32 inst_id;
+	u32 shared_mem_addr_offset;
+	u32 stream_buffer_addr_offset;
+	u32 stream_buffer_size;
+};
+struct vidc_1080p_enc_frame_start_param{
+	u32 cmd_seq_num;
+	u32 inst_id;
+	u32 shared_mem_addr_offset;
+	u32 current_y_addr_offset;
+	u32 current_c_addr_offset;
+	u32 stream_buffer_addr_offset;
+	u32 stream_buffer_size;
+	u32 intra_frame;
+	u32 input_flush;
+	u32 slice_enable;
+	enum vidc_1080p_encode encode;
+};
+struct vidc_1080p_enc_frame_info{
+	u32 enc_frame_size;
+	u32 enc_picture_count;
+	u32 enc_write_pointer;
+	u32 enc_luma_address;
+	u32 enc_chroma_address;
+	enum vidc_1080p_encode_frame enc_frame;
+	u32 meta_data_exists;
+};
+struct vidc_1080p_enc_slice_batch_in_param {
+	u32 cmd_type;
+	u32 input_size;
+	u32 num_stream_buffer;
+	u32 stream_buffer_size;
+	u32 stream_buffer_addr_offset[VIDC_1080P_SLICE_BATCH_MAX_STRM_BFR];
+};
+struct vidc_1080p_enc_slice_info {
+	u32 stream_buffer_idx;
+	u32 stream_buffer_size;
+};
+struct vidc_1080p_enc_slice_batch_out_param {
+	u32 cmd_type;
+	u32 output_size;
+	struct vidc_1080p_enc_slice_info slice_info
+		[VIDC_1080P_SLICE_BATCH_MAX_STRM_BFR];
+};
+struct vidc_1080p_dec_disp_info{
+	u32 disp_resl_change;
+	u32 dec_resl_change;
+	u32 reconfig_flush_done;
+	u32 img_size_x;
+	u32 img_size_y;
+	u32 display_y_addr;
+	u32 display_c_addr;
+	u32 decode_y_addr;
+	u32 decode_c_addr;
+	u32 tag_top;
+	u32 pic_time_top;
+	u32 tag_bottom;
+	u32 pic_time_bottom;
+	u32 metadata_exists;
+	u32 disp_crop_exists;
+	u32 dec_crop_exists;
+	u32 crop_right_offset;
+	u32 crop_left_offset;
+	u32 crop_bottom_offset;
+	u32 crop_top_offset;
+	u32 input_bytes_consumed;
+	u32 input_is_interlace;
+	u32 input_frame_num;
+	enum vidc_1080p_display_status display_status;
+	enum vidc_1080p_display_status decode_status;
+	enum vidc_1080p_display_coding display_coding;
+	enum vidc_1080p_display_coding decode_coding;
+	enum vidc_1080P_decode_frame_correct_type display_correct;
+	enum vidc_1080P_decode_frame_correct_type decode_correct;
+	enum vidc_1080p_decode_frame input_frame;
+};
+void vidc_1080p_do_sw_reset(enum vidc_1080p_reset init_flag);
+void vidc_1080p_release_sw_reset(void);
+void vidc_1080p_clear_interrupt(void);
+void vidc_1080p_set_host2risc_cmd(
+	enum vidc_1080p_host2risc_cmd host2risc_command,
+	u32 host2risc_arg1, u32 host2risc_arg2,
+	u32 host2risc_arg3, u32 host2risc_arg4);
+void vidc_1080p_get_risc2host_cmd(u32 *pn_risc2host_command,
+	u32 *pn_risc2host_arg1, u32 *pn_risc2host_arg2,
+	u32 *pn_risc2host_arg3, u32 *pn_risc2host_arg4);
+void vidc_1080p_get_risc2host_cmd_status(u32 err_status,
+	u32 *dec_err_status, u32 *disp_err_status);
+void vidc_1080p_clear_risc2host_cmd(void);
+void vidc_1080p_get_fw_version(u32 *pn_fw_version);
+void vidc_1080p_get_fw_status(u32 *pn_fw_status);
+void vidc_1080p_init_memory_controller(u32 dram_base_addr_a,
+	u32 dram_base_addr_b);
+void vidc_1080p_get_memory_controller_status(u32 *pb_mc_abusy,
+	u32 *pb_mc_bbusy);
+void vidc_1080p_set_h264_decode_buffers(u32 dpb, u32 dec_vert_nb_mv_offset,
+	u32 dec_nb_ip_offset, u32 *pn_dpb_luma_offset,
+	u32 *pn_dpb_chroma_offset, u32 *pn_mv_buffer_offset);
+void vidc_1080p_set_decode_recon_buffers(u32 recon_buffer, u32 *pn_dec_luma,
+	u32 *pn_dec_chroma);
+void vidc_1080p_set_mpeg4_divx_decode_work_buffers(u32 nb_dcac_buffer_offset,
+	u32 upnb_mv_buffer_offset, u32 sub_anchor_buffer_offset,
+	u32 overlay_transform_buffer_offset, u32 stx_parser_buffer_offset);
+void vidc_1080p_set_h263_decode_work_buffers(u32 nb_dcac_buffer_offset,
+	u32 upnb_mv_buffer_offset, u32 sub_anchor_buffer_offset,
+	u32 overlay_transform_buffer_offset);
+void vidc_1080p_set_vc1_decode_work_buffers(u32 nb_dcac_buffer_offset,
+	u32 upnb_mv_buffer_offset, u32 sub_anchor_buffer_offset,
+	u32 overlay_transform_buffer_offset, u32 bitplain1Buffer_offset,
+	u32 bitplain2Buffer_offset, u32 bitplain3Buffer_offset);
+void vidc_1080p_set_encode_recon_buffers(u32 recon_buffer, u32 *pn_enc_luma,
+	u32 *pn_enc_chroma);
+void vidc_1080p_set_h264_encode_work_buffers(u32 up_row_mv_buffer_offset,
+	u32 direct_colzero_flag_buffer_offset,
+	u32 upper_intra_md_buffer_offset,
+	u32 upper_intra_pred_buffer_offset, u32 nbor_infor_buffer_offset,
+	u32 mb_info_offset);
+void vidc_1080p_set_h263_encode_work_buffers(u32 up_row_mv_buffer_offset,
+	u32 up_row_inv_quanti_coeff_buffer_offset);
+void vidc_1080p_set_mpeg4_encode_work_buffers(u32 skip_flag_buffer_offset,
+	u32 up_row_inv_quanti_coeff_buffer_offset, u32 upper_mv_offset);
+void vidc_1080p_set_encode_frame_size(u32 hori_size, u32 vert_size);
+void vidc_1080p_set_encode_profile_level(u32 encode_profile, u32 enc_level);
+void vidc_1080p_set_encode_field_picture_structure(u32 enc_field_picture);
+void vidc_1080p_set_decode_mpeg4_pp_filter(u32 lf_enables);
+void vidc_1080p_set_decode_qp_save_control(u32 enable_q_pout);
+void vidc_1080p_get_returned_channel_inst_id(u32 *pn_rtn_chid);
+void vidc_1080p_clear_returned_channel_inst_id(void);
+void vidc_1080p_get_decode_seq_start_result(
+	struct vidc_1080p_seq_hdr_info *seq_hdr_info);
+void vidc_1080p_get_decoded_frame_size(u32 *pn_decoded_size);
+void vidc_1080p_get_display_frame_result(
+	struct vidc_1080p_dec_disp_info *dec_disp_info);
+void vidc_1080p_get_decode_frame(
+	enum vidc_1080p_decode_frame *pe_frame);
+void vidc_1080p_get_decode_frame_result(
+	struct vidc_1080p_dec_disp_info *dec_disp_info);
+void vidc_1080p_decode_seq_start_ch0(
+	struct vidc_1080p_dec_seq_start_param *param);
+void vidc_1080p_decode_seq_start_ch1(
+	struct vidc_1080p_dec_seq_start_param *param);
+void vidc_1080p_decode_init_buffers_ch0
+	(struct vidc_1080p_dec_init_buffers_param *param);
+void vidc_1080p_decode_init_buffers_ch1(
+	struct vidc_1080p_dec_init_buffers_param *param);
+void vidc_1080p_decode_frame_start_ch0(
+	struct vidc_1080p_dec_frame_start_param *param);
+void vidc_1080p_decode_frame_start_ch1(
+	struct vidc_1080p_dec_frame_start_param *param);
+void vidc_1080p_set_dec_resolution_ch0(u32 width, u32 height);
+void vidc_1080p_set_dec_resolution_ch1(u32 width, u32 height);
+void vidc_1080p_get_encode_frame_info(
+	struct vidc_1080p_enc_frame_info *frame_info);
+void vidc_1080p_encode_seq_start_ch0(
+	struct vidc_1080p_enc_seq_start_param *param);
+void vidc_1080p_encode_seq_start_ch1(
+	struct vidc_1080p_enc_seq_start_param *param);
+void vidc_1080p_encode_frame_start_ch0(
+	struct vidc_1080p_enc_frame_start_param *param);
+void vidc_1080p_encode_frame_start_ch1(
+	struct vidc_1080p_enc_frame_start_param *param);
+void vidc_1080p_encode_slice_batch_start_ch0(
+	struct vidc_1080p_enc_frame_start_param *param);
+void vidc_1080p_encode_slice_batch_start_ch1(
+	struct vidc_1080p_enc_frame_start_param *param);
+void vidc_1080p_set_encode_picture(u32 ifrm_ctrl, u32 number_b);
+void vidc_1080p_set_encode_multi_slice_control(
+	enum vidc_1080p_MSlice_selection multiple_slice_selection,
+	u32 mslice_mb, u32 mslice_byte);
+void vidc_1080p_set_encode_circular_intra_refresh(u32 cir_num);
+void vidc_1080p_set_encode_input_frame_format(
+	enum vidc_1080p_memory_access_method memory_format);
+void vidc_1080p_set_encode_padding_control(u32 pad_ctrl_on,
+	u32 cr_pad_val, u32 cb_pad_val, u32 luma_pad_val);
+void vidc_1080p_encode_set_rc_config(u32 enable_frame_level_rc,
+	u32 enable_mb_level_rc_flag, u32 frame_qp);
+void vidc_1080p_encode_set_frame_level_rc_params(u32 rc_frame_rate,
+	u32 target_bitrate, u32 reaction_coeff);
+void vidc_1080p_encode_set_qp_params(u32 max_qp, u32 min_qp);
+void vidc_1080p_encode_set_mb_level_rc_params(u32 disable_dark_region_as_flag,
+	u32 disable_smooth_region_as_flag , u32 disable_static_region_as_flag,
+	u32 disable_activity_region_flag);
+void vidc_1080p_get_qp(u32 *pn_frame_qp);
+void vidc_1080p_set_h264_encode_entropy(
+	enum vidc_1080p_entropy_sel entropy_sel);
+void vidc_1080p_set_h264_encode_loop_filter(
+	enum vidc_1080p_DBConfig db_config, u32 slice_alpha_offset,
+	u32 slice_beta_offset);
+void vidc_1080p_set_h264_encoder_p_frame_ref_count(u32 max_reference);
+void vidc_1080p_set_h264_encode_8x8transform_control(u32 enable_8x8transform);
+void vidc_1080p_set_mpeg4_encode_quarter_pel_control(
+	u32 enable_mpeg4_quarter_pel);
+void vidc_1080p_set_device_base_addr(u8 *mapped_va);
+void vidc_1080p_get_intra_bias(u32 *intra_bias);
+void vidc_1080p_set_intra_bias(u32 intra_bias);
+void vidc_1080p_get_bi_directional_bias(u32 *bi_directional_bias);
+void vidc_1080p_set_bi_directional_bias(u32 bi_directional_bias);
+void vidc_1080p_get_encoder_sequence_header_size(u32 *seq_header_size);
+void vidc_1080p_get_intermedia_stage_debug_counter(
+	u32 *intermediate_stage_counter);
+void vidc_1080p_get_exception_status(u32 *exception_status);
+void vidc_1080p_frame_start_realloc(u32 instance_id);
+#endif
diff --git a/drivers/video/msm/vidc/1080p/ddl/vidc_hwio.h b/drivers/video/msm/vidc/1080p/ddl/vidc_hwio.h
new file mode 100644
index 0000000..a5a8e57
--- /dev/null
+++ b/drivers/video/msm/vidc/1080p/ddl/vidc_hwio.h
@@ -0,0 +1,115 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VIDC_HWIO_H_
+#define _VIDC_HWIO_H_
+
+#include "vidc_hwio_reg.h"
+
+#ifdef VIDC_REGISTER_LOG
+#define VIDC_REG_OUT(x...)  printk(KERN_DEBUG x)
+#define VIDC_REG_IN(x...)   printk(KERN_DEBUG x)
+#else
+#define VIDC_REG_OUT(x...)
+#define VIDC_REG_IN(x...)
+#endif
+
+#define __inpdw(port) __raw_readl(port)
+#define __outpdw(port, val) __raw_writel(val, port)
+
+#define in_dword(addr) (__inpdw(addr))
+#define in_dword_masked(addr, mask) (__inpdw(addr) & (mask))
+#define out_dword(addr, val) __outpdw(addr, val)
+
+#define out_dword_masked(io, mask, val, shadow) \
+do { \
+	shadow = (shadow & (u32)(~(mask))) | ((u32)((val) & (mask))); \
+	out_dword(io, shadow); \
+} while (0)
+#define out_dword_masked_ns(io, mask, val, current_reg_content) \
+	out_dword(io, ((current_reg_content & (u32)(~(mask))) | \
+	((u32)((val) & (mask)))))
+
+#define HWIO_IN(hwiosym)  HWIO_##hwiosym##_IN
+#define HWIO_INI(hwiosym, index)  HWIO_##hwiosym##_INI(index)
+#define HWIO_INM(hwiosym, mask)   HWIO_##hwiosym##_INM(mask)
+#define HWIO_INF(hwiosym, field)  (HWIO_INM(hwiosym, \
+	HWIO_FMSK(hwiosym, field)) >> HWIO_SHFT(hwiosym, field))
+
+#define HWIO_OUT(hwiosym, val)  HWIO_##hwiosym##_OUT(val)
+#define HWIO_OUTI(hwiosym, index, val)  HWIO_##hwiosym##_OUTI(index, val)
+#define HWIO_OUTM(hwiosym, mask, val)  HWIO_##hwiosym##_OUTM(mask, val)
+#define HWIO_OUTF(hwiosym, field, val)  HWIO_OUTM(hwiosym, \
+	HWIO_FMSK(hwiosym, field), (u32)(val) << HWIO_SHFT(hwiosym, field))
+
+#define HWIO_SHFT(hwio_regsym, hwio_fldsym) \
+	HWIO_##hwiosym##_##hwiofldsym##_SHFT
+#define HWIO_FMSK(hwio_regsym, hwio_fldsym) \
+	HWIO_##hwiosym##_##hwiofldsym##_BMSK
+
+#define VIDC_SETFIELD(val, shift, mask) \
+	(((val) << (shift)) & (mask))
+#define VIDC_GETFIELD(val, mask, shift) \
+	(((val) & (mask)) >> (shift))
+
+#define VIDC_HWIO_OUT(hwiosym, val) \
+do { \
+	VIDC_REG_OUT("\n(0x%x:"#hwiosym"=0x%x)", \
+	HWIO_##hwiosym##_ADDR - VIDC_BASE_PTR, val); \
+	mb(); \
+	HWIO_OUT(hwiosym, val); \
+} while (0)
+#define VIDC_HWIO_OUTI(hwiosym, index, val) \
+do { \
+	VIDC_REG_OUT("\n(0x%x:"#hwiosym"(%d)=0x%x)", \
+	HWIO_##hwiosym##_ADDR(index) - VIDC_BASE_PTR, index, val); \
+	mb(); \
+	HWIO_OUTI(hwiosym, index, val); \
+} while (0)
+#define VIDC_HWIO_OUTF(hwiosym, field, val) \
+do { \
+	VIDC_REG_OUT("\n(0x%x:"#hwiosym":0x%x:=0x%x)" , \
+	HWIO_##hwiosym##_ADDR - VIDC_BASE_PTR, \
+	HWIO_##hwiosym##_##field##_BMSK, val) \
+	mb(); \
+	HWIO_OUTF(hwiosym, field, val); \
+} while (0)
+#define VIDC_OUT_DWORD(addr, val) \
+do { \
+	VIDC_REG_OUT("\n(0x%x:"#addr"=0x%x)", \
+	addr - VIDC_BASE_PTR, val); \
+	mb(); \
+	out_dword(addr, val); \
+} while (0)
+#define VIDC_HWIO_IN(hwiosym, pval) \
+do { \
+	mb(); \
+	*pval = (u32) HWIO_IN(hwiosym); \
+	VIDC_REG_IN("\n(0x%x:"#hwiosym"=0x%x)", \
+	HWIO_##hwiosym##_ADDR - VIDC_BASE_PTR, *pval);\
+} while (0)
+#define VIDC_HWIO_INI(hwiosym, index, pval) \
+do { \
+	mb(); \
+	*pval = (u32) HWIO_INI(hwiosym, index); \
+	VIDC_REG_IN("(0x%x:"#hwiosym"(%d)==0x%x)", \
+	HWIO_##hwiosym##_ADDR(index) - VIDC_BASE_PTR, index, *pval); \
+} while (0)
+#define VIDC_HWIO_INF(hwiosym, mask, pval) \
+do { \
+	mb(); \
+	*pval = HWIO_INF(hwiosym, mask); \
+	VIDC_REG_IN("\n(0x%x:"#hwiosym"=0x%x)", \
+	HWIO_##hwiosym##_ADDR - VIDC_BASE_PTR, *pval); \
+} while (0)
+#endif
diff --git a/drivers/video/msm/vidc/1080p/ddl/vidc_hwio_reg.h b/drivers/video/msm/vidc/1080p/ddl/vidc_hwio_reg.h
new file mode 100644
index 0000000..819cd6c
--- /dev/null
+++ b/drivers/video/msm/vidc/1080p/ddl/vidc_hwio_reg.h
@@ -0,0 +1,4544 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VIDC_HWIO_REG_H_
+#define _VIDC_HWIO_REG_H_
+
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <asm/system.h>
+#include "vidc.h"
+
+extern u8 *VIDC_BASE_PTR;
+
+#define VIDC_BASE  VIDC_BASE_PTR
+
+#define VIDC_BLACKBIRD_REG_BASE  (VIDC_BASE + 0x00000000)
+#define VIDC_BLACKBIRD_REG_BASE_PHYS  0x04400000
+
+#define HWIO_REG_557899_ADDR  (VIDC_BLACKBIRD_REG_BASE + 00000000)
+#define HWIO_REG_557899_PHYS  (VIDC_BLACKBIRD_REG_BASE_PHYS + 00000000)
+#define HWIO_REG_557899_RMSK  0x3ff
+#define HWIO_REG_557899_SHFT  0
+#define HWIO_REG_557899_IN  in_dword_masked(HWIO_REG_557899_ADDR,\
+	HWIO_REG_557899_RMSK)
+#define HWIO_REG_557899_INM(m)  in_dword_masked(HWIO_REG_557899_ADDR, m)
+#define HWIO_REG_557899_OUT(v)  out_dword(HWIO_REG_557899_ADDR, v)
+#define HWIO_REG_557899_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_557899_ADDR, m, v, HWIO_REG_557899_IN);
+#define HWIO_REG_557899_RSTN_RG_MPEG2_BMSK     0x200
+#define HWIO_REG_557899_RSTN_RG_MPEG2_SHFT     0x9
+#define HWIO_REG_557899_RSTN_RG_MPEG4_BMSK     0x100
+#define HWIO_REG_557899_RSTN_RG_MPEG4_SHFT     0x8
+#define HWIO_REG_557899_RSTN_RG_VC1_BMSK       0x80
+#define HWIO_REG_557899_RSTN_RG_VC1_SHFT       0x7
+#define HWIO_REG_557899_RSTN_RG_H264_BMSK      0x40
+#define HWIO_REG_557899_RSTN_RG_H264_SHFT      0x6
+#define HWIO_REG_557899_RSTN_RG_COMMON_BMSK    0x20
+#define HWIO_REG_557899_RSTN_RG_COMMON_SHFT    0x5
+#define HWIO_REG_557899_RSTN_DMX_BMSK          0x10
+#define HWIO_REG_557899_RSTN_DMX_SHFT          0x4
+#define HWIO_REG_557899_RSTN_VI_BMSK           0x8
+#define HWIO_REG_557899_RSTN_VI_SHFT           0x3
+#define HWIO_REG_557899_RSTN_VIDCCORE_BMSK     0x4
+#define HWIO_REG_557899_RSTN_VIDCCORE_SHFT     0x2
+#define HWIO_REG_557899_RSTN_MC_BMSK           0x2
+#define HWIO_REG_557899_RSTN_MC_SHFT           0x1
+#define HWIO_REG_557899_RSTN_RISC_BMSK         0x1
+#define HWIO_REG_557899_RSTN_RISC_SHFT         0
+
+#define HWIO_REG_575377_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00000008)
+#define HWIO_REG_575377_PHYS (VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000008)
+#define HWIO_REG_575377_RMSK 0x1
+#define HWIO_REG_575377_SHFT 0
+#define HWIO_REG_575377_IN  in_dword_masked(\
+	HWIO_REG_575377_ADDR, HWIO_REG_575377_RMSK)
+#define HWIO_REG_575377_INM(m) \
+	in_dword_masked(HWIO_REG_575377_ADDR, m)
+#define HWIO_REG_575377_OUT(v) \
+	out_dword(HWIO_REG_575377_ADDR, v)
+#define HWIO_REG_575377_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_575377_ADDR, m, v, HWIO_REG_575377_IN);
+#define HWIO_REG_575377_INTERRUPT_BMSK  0x1
+#define HWIO_REG_575377_INTERRUPT_SHFT  0
+
+#define HWIO_REG_611794_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00000030)
+#define HWIO_REG_611794_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000030)
+#define HWIO_REG_611794_RMSK  0xffffffff
+#define HWIO_REG_611794_SHFT  0
+#define HWIO_REG_611794_IN  in_dword_masked(\
+	HWIO_REG_611794_ADDR, HWIO_REG_611794_RMSK)
+#define HWIO_REG_611794_INM(m) \
+	in_dword_masked(HWIO_REG_611794_ADDR, m)
+#define HWIO_REG_611794_OUT(v) \
+	out_dword(HWIO_REG_611794_ADDR, v)
+#define HWIO_REG_611794_OUTM(m, v) \
+	out_dword_masked_ns(HWIO_REG_611794_ADDR, m, v,\
+	HWIO_REG_611794_IN);
+#define HWIO_REG_611794_HOST2RISC_COMMAND_BMSK 0xffffffff
+#define HWIO_REG_611794_HOST2RISC_COMMAND_SHFT 0
+
+#define HWIO_REG_356340_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00000034)
+#define HWIO_REG_356340_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000034)
+#define HWIO_REG_356340_RMSK  0xffffffff
+#define HWIO_REG_356340_SHFT  0
+#define HWIO_REG_356340_IN  in_dword_masked(\
+	HWIO_REG_356340_ADDR, HWIO_REG_356340_RMSK)
+#define HWIO_REG_356340_INM(m) \
+	in_dword_masked(HWIO_REG_356340_ADDR, m)
+#define HWIO_REG_356340_OUT(v) \
+	out_dword(HWIO_REG_356340_ADDR, v)
+#define HWIO_REG_356340_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_356340_ADDR, m, v, HWIO_REG_356340_IN);
+#define HWIO_REG_356340_HOST2RISC_ARG1_BMSK  0xffffffff
+#define HWIO_REG_356340_HOST2RISC_ARG1_SHFT  0
+
+#define HWIO_REG_899023_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00000038)
+#define HWIO_REG_899023_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000038)
+#define HWIO_REG_899023_RMSK 0xffffffff
+#define HWIO_REG_899023_SHFT 0
+#define HWIO_REG_899023_IN  in_dword_masked(\
+	HWIO_REG_899023_ADDR, HWIO_REG_899023_RMSK)
+#define HWIO_REG_899023_INM(m) \
+	in_dword_masked(HWIO_REG_899023_ADDR, m)
+#define HWIO_REG_899023_OUT(v) \
+	out_dword(HWIO_REG_899023_ADDR, v)
+#define HWIO_REG_899023_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_899023_ADDR, m, v, HWIO_REG_899023_IN);
+#define HWIO_REG_899023_HOST2RISC_ARG2_BMSK  0xffffffff
+#define HWIO_REG_899023_HOST2RISC_ARG2_SHFT  0
+
+#define HWIO_REG_987762_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000003c)
+#define HWIO_REG_987762_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000003c)
+#define HWIO_REG_987762_RMSK  0xffffffff
+#define HWIO_REG_987762_SHFT  0
+#define HWIO_REG_987762_IN  in_dword_masked(\
+	HWIO_REG_987762_ADDR, HWIO_REG_987762_RMSK)
+#define HWIO_REG_987762_INM(m) \
+	in_dword_masked(HWIO_REG_987762_ADDR, m)
+#define HWIO_REG_987762_OUT(v) \
+	out_dword(HWIO_REG_987762_ADDR, v)
+#define HWIO_REG_987762_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_987762_ADDR, m, v, HWIO_REG_987762_IN);
+#define HWIO_REG_987762_HOST2RISC_ARG3_BMSK  0xffffffff
+#define HWIO_REG_987762_HOST2RISC_ARG3_SHFT  0
+
+#define HWIO_REG_544000_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00000040)
+#define HWIO_REG_544000_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000040)
+#define HWIO_REG_544000_RMSK  0xffffffff
+#define HWIO_REG_544000_SHFT  0
+#define HWIO_REG_544000_IN  in_dword_masked(\
+	HWIO_REG_544000_ADDR, HWIO_REG_544000_RMSK)
+#define HWIO_REG_544000_INM(m)  \
+	in_dword_masked(HWIO_REG_544000_ADDR, m)
+#define HWIO_REG_544000_OUT(v)  \
+	out_dword(HWIO_REG_544000_ADDR, v)
+#define HWIO_REG_544000_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_544000_ADDR, m, v, HWIO_REG_544000_IN);
+#define HWIO_REG_544000_HOST2RISC_ARG4_BMSK  0xffffffff
+#define HWIO_REG_544000_HOST2RISC_ARG4_SHFT  0
+
+#define HWIO_REG_695082_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00000044)
+#define HWIO_REG_695082_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000044)
+#define HWIO_REG_695082_RMSK  0xffffffff
+#define HWIO_REG_695082_SHFT  0
+#define HWIO_REG_695082_IN  in_dword_masked(\
+	HWIO_REG_695082_ADDR, HWIO_REG_695082_RMSK)
+#define HWIO_REG_695082_INM(m) \
+	in_dword_masked(HWIO_REG_695082_ADDR, m)
+#define HWIO_REG_695082_OUT(v) \
+	out_dword(HWIO_REG_695082_ADDR, v)
+#define HWIO_REG_695082_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_695082_ADDR, m, v, HWIO_REG_695082_IN);
+#define HWIO_REG_695082_RISC2HOST_COMMAND_BMSK  0xffffffff
+#define HWIO_REG_695082_RISC2HOST_COMMAND_SHFT  0
+
+#define HWIO_REG_156596_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00000048)
+#define HWIO_REG_156596_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000048)
+#define HWIO_REG_156596_RMSK  0xffffffff
+#define HWIO_REG_156596_SHFT  0
+#define HWIO_REG_156596_IN  in_dword_masked(\
+	HWIO_REG_156596_ADDR, HWIO_REG_156596_RMSK)
+#define HWIO_REG_156596_INM(m) \
+	in_dword_masked(HWIO_REG_156596_ADDR, m)
+#define HWIO_REG_156596_OUT(v) \
+	out_dword(HWIO_REG_156596_ADDR, v)
+#define HWIO_REG_156596_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_156596_ADDR, m, v, HWIO_REG_156596_IN);
+#define HWIO_REG_156596_REG_156596_BMSK  0xffffffff
+#define HWIO_REG_156596_REG_156596_SHFT  0
+
+#define HWIO_REG_222292_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000004c)
+#define HWIO_REG_222292_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000004c)
+#define HWIO_REG_222292_RMSK  0xffffffff
+#define HWIO_REG_222292_SHFT  0
+#define HWIO_REG_222292_IN  in_dword_masked(\
+	HWIO_REG_222292_ADDR, HWIO_REG_222292_RMSK)
+#define HWIO_REG_222292_INM(m) \
+	in_dword_masked(HWIO_REG_222292_ADDR, m)
+#define HWIO_REG_222292_OUT(v) \
+	out_dword(HWIO_REG_222292_ADDR, v)
+#define HWIO_REG_222292_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_222292_ADDR, m, v, HWIO_REG_222292_IN);
+#define HWIO_REG_222292_REG_222292_BMSK  0xffffffff
+#define HWIO_REG_222292_REG_222292_SHFT  0
+
+#define HWIO_REG_790962_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00000050)
+#define HWIO_REG_790962_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000050)
+#define HWIO_REG_790962_RMSK  0xffffffff
+#define HWIO_REG_790962_SHFT  0
+#define HWIO_REG_790962_IN  in_dword_masked(\
+	HWIO_REG_790962_ADDR, HWIO_REG_790962_RMSK)
+#define HWIO_REG_790962_INM(m) \
+	in_dword_masked(HWIO_REG_790962_ADDR, m)
+#define HWIO_REG_790962_OUT(v) \
+	out_dword(HWIO_REG_790962_ADDR, v)
+#define HWIO_REG_790962_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_790962_ADDR, m, v, HWIO_REG_790962_IN);
+#define HWIO_REG_790962_REG_790962_BMSK  0xffffffff
+#define HWIO_REG_790962_REG_790962_SHFT  0
+
+#define HWIO_REG_679882_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00000054)
+#define HWIO_REG_679882_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000054)
+#define HWIO_REG_679882_RMSK  0xffffffff
+#define HWIO_REG_679882_SHFT  0
+#define HWIO_REG_679882_IN  in_dword_masked(\
+	HWIO_REG_679882_ADDR, HWIO_REG_679882_RMSK)
+#define HWIO_REG_679882_INM(m) \
+	in_dword_masked(HWIO_REG_679882_ADDR, m)
+#define HWIO_REG_679882_OUT(v) \
+	out_dword(HWIO_REG_679882_ADDR, v)
+#define HWIO_REG_679882_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_679882_ADDR, m, v, HWIO_REG_679882_IN);
+#define HWIO_REG_679882_REG_679882_BMSK  0xffffffff
+#define HWIO_REG_679882_REG_679882_SHFT  0
+
+#define HWIO_REG_653206_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00000058)
+#define HWIO_REG_653206_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000058)
+#define HWIO_REG_653206_RMSK  0xffffff
+#define HWIO_REG_653206_SHFT  0
+#define HWIO_REG_653206_IN  in_dword_masked(\
+	HWIO_REG_653206_ADDR, HWIO_REG_653206_RMSK)
+#define HWIO_REG_653206_INM(m) \
+	in_dword_masked(HWIO_REG_653206_ADDR, m)
+#define HWIO_REG_653206_YEAR_BMSK   0xff0000
+#define HWIO_REG_653206_YEAR_SHFT   0x10
+#define HWIO_REG_653206_MONTH_BMSK  0xff00
+#define HWIO_REG_653206_MONTH_SHFT  0x8
+#define HWIO_REG_653206_DAY_BMSK    0xff
+#define HWIO_REG_653206_DAY_SHFT    0
+
+#define HWIO_REG_805993_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00000064)
+#define HWIO_REG_805993_PHYS (VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000064)
+#define HWIO_REG_805993_RMSK  0xffffffff
+#define HWIO_REG_805993_SHFT  0
+#define HWIO_REG_805993_IN  in_dword_masked(\
+	HWIO_REG_805993_ADDR, HWIO_REG_805993_RMSK)
+#define HWIO_REG_805993_INM(m) \
+	in_dword_masked(HWIO_REG_805993_ADDR, m)
+#define HWIO_REG_805993_INTERMEDIATE_STAGE_COUNTER_BMSK  0xffffffff
+#define HWIO_REG_805993_INTERMEDIATE_STAGE_COUNTER_SHFT  0
+
+#define HWIO_REG_493355_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00000068)
+#define HWIO_REG_493355_PHYS (VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000068)
+#define HWIO_REG_493355_RMSK  0xffffffff
+#define HWIO_REG_493355_SHFT  0
+#define HWIO_REG_493355_IN  in_dword_masked(\
+	HWIO_REG_493355_ADDR, HWIO_REG_493355_RMSK)
+#define HWIO_REG_493355_INM(m) \
+	in_dword_masked(HWIO_REG_493355_ADDR, m)
+#define HWIO_REG_493355_EXCEPTION_STATUS_BMSK  0xffffffff
+#define HWIO_REG_493355_EXCEPTION_STATUS_SHFT  0
+
+#define HWIO_REG_350619_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00000080)
+#define HWIO_REG_350619_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000080)
+#define HWIO_REG_350619_RMSK  0x1
+#define HWIO_REG_350619_SHFT  0
+#define HWIO_REG_350619_IN  in_dword_masked(\
+	HWIO_REG_350619_ADDR, HWIO_REG_350619_RMSK)
+#define HWIO_REG_350619_INM(m) \
+	in_dword_masked(HWIO_REG_350619_ADDR, m)
+#define HWIO_REG_350619_FIRMWARE_STATUS_BMSK  0x1
+#define HWIO_REG_350619_FIRMWARE_STATUS_SHFT  0
+
+#define HWIO_REG_64440_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00000508)
+#define HWIO_REG_64440_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000508)
+#define HWIO_REG_64440_RMSK  0xfffe0000
+#define HWIO_REG_64440_SHFT  0
+#define HWIO_REG_64440_IN  in_dword_masked(\
+	HWIO_REG_64440_ADDR, HWIO_REG_64440_RMSK)
+#define HWIO_REG_64440_INM(m) \
+	in_dword_masked(HWIO_REG_64440_ADDR, m)
+#define HWIO_REG_64440_OUT(v) \
+	out_dword(HWIO_REG_64440_ADDR, v)
+#define HWIO_REG_64440_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_64440_ADDR, m, v,\
+	HWIO_REG_64440_IN);
+#define HWIO_REG_64440_MC_DRAMBASE_ADDR_BMSK  0xfffe0000
+#define HWIO_REG_64440_MC_DRAMBASE_ADDR_SHFT  0x11
+
+#define HWIO_REG_675915_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000050c)
+#define HWIO_REG_675915_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000050c)
+#define HWIO_REG_675915_RMSK  0xfffe0000
+#define HWIO_REG_675915_SHFT  0
+#define HWIO_REG_675915_IN  in_dword_masked(\
+	HWIO_REG_675915_ADDR, HWIO_REG_675915_RMSK)
+#define HWIO_REG_675915_INM(m) \
+	in_dword_masked(HWIO_REG_675915_ADDR, m)
+#define HWIO_REG_675915_OUT(v) \
+	out_dword(HWIO_REG_675915_ADDR, v)
+#define HWIO_REG_675915_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_675915_ADDR, m, v,\
+	HWIO_REG_675915_IN);
+#define HWIO_REG_675915_MC_DRAMBASE_ADDR_BMSK  0xfffe0000
+#define HWIO_REG_675915_MC_DRAMBASE_ADDR_SHFT  0x11
+
+#define HWIO_REG_399911_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00000510)
+#define HWIO_REG_399911_PHYS (VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000510)
+#define HWIO_REG_399911_RMSK  0x3
+#define HWIO_REG_399911_SHFT  0
+#define HWIO_REG_399911_IN  in_dword_masked(\
+	HWIO_REG_399911_ADDR, HWIO_REG_399911_RMSK)
+#define HWIO_REG_399911_INM(m)  in_dword_masked(HWIO_REG_399911_ADDR, m)
+#define HWIO_REG_399911_MC_BUSY_B_BMSK  0x2
+#define HWIO_REG_399911_MC_BUSY_B_SHFT  0x1
+#define HWIO_REG_399911_MC_BUSY_A_BMSK  0x1
+#define HWIO_REG_399911_MC_BUSY_A_SHFT  0
+
+#define HWIO_REG_515200_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00000600)
+#define HWIO_REG_515200_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000600)
+#define HWIO_REG_515200_RMSK  0x1ffff
+#define HWIO_REG_515200_SHFT  0
+#define HWIO_REG_515200_IN  in_dword_masked(\
+	HWIO_REG_515200_ADDR, HWIO_REG_515200_RMSK)
+#define HWIO_REG_515200_INM(m) \
+	in_dword_masked(HWIO_REG_515200_ADDR, m)
+#define HWIO_REG_515200_OUT(v) \
+	out_dword(HWIO_REG_515200_ADDR, v)
+#define HWIO_REG_515200_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_515200_ADDR, m, v,\
+	HWIO_REG_515200_IN);
+#define HWIO_REG_515200_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_515200_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_29510_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00000604)
+#define HWIO_REG_29510_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000604)
+#define HWIO_REG_29510_RMSK  0x1ffff
+#define HWIO_REG_29510_SHFT  0
+#define HWIO_REG_29510_IN  in_dword_masked(\
+	HWIO_REG_29510_ADDR, HWIO_REG_29510_RMSK)
+#define HWIO_REG_29510_INM(m) \
+	in_dword_masked(HWIO_REG_29510_ADDR, m)
+#define HWIO_REG_29510_OUT(v) \
+	out_dword(HWIO_REG_29510_ADDR, v)
+#define HWIO_REG_29510_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_29510_ADDR, m, v,\
+	HWIO_REG_29510_IN);
+#define HWIO_REG_29510_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_29510_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_256132_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00000608)
+#define HWIO_REG_256132_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000608)
+#define HWIO_REG_256132_RMSK  0x1ffff
+#define HWIO_REG_256132_SHFT  0
+#define HWIO_REG_256132_IN  in_dword_masked(\
+	HWIO_REG_256132_ADDR, HWIO_REG_256132_RMSK)
+#define HWIO_REG_256132_INM(m) \
+	in_dword_masked(HWIO_REG_256132_ADDR, m)
+#define HWIO_REG_256132_OUT(v) \
+	out_dword(HWIO_REG_256132_ADDR, v)
+#define HWIO_REG_256132_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_256132_ADDR, m, v,\
+	HWIO_REG_256132_IN);
+#define HWIO_REG_256132_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_256132_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_885152_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000060c)
+#define HWIO_REG_885152_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000060c)
+#define HWIO_REG_885152_RMSK  0x1ffff
+#define HWIO_REG_885152_SHFT  0
+#define HWIO_REG_885152_IN  in_dword_masked(\
+	HWIO_REG_885152_ADDR, HWIO_REG_885152_RMSK)
+#define HWIO_REG_885152_INM(m) \
+	in_dword_masked(HWIO_REG_885152_ADDR, m)
+#define HWIO_REG_885152_OUT(v) \
+	out_dword(HWIO_REG_885152_ADDR, v)
+#define HWIO_REG_885152_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_885152_ADDR, m, v,\
+	HWIO_REG_885152_IN);
+#define HWIO_REG_885152_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_885152_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_69832_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00000610)
+#define HWIO_REG_69832_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000610)
+#define HWIO_REG_69832_RMSK 0x1ffff
+#define HWIO_REG_69832_SHFT 0
+#define HWIO_REG_69832_IN  in_dword_masked(\
+	HWIO_REG_69832_ADDR, HWIO_REG_69832_RMSK)
+#define HWIO_REG_69832_INM(m) \
+	in_dword_masked(HWIO_REG_69832_ADDR, m)
+#define HWIO_REG_69832_OUT(v) \
+	out_dword(HWIO_REG_69832_ADDR, v)
+#define HWIO_REG_69832_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_69832_ADDR, m, v,\
+	HWIO_REG_69832_IN);
+#define HWIO_REG_69832_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_69832_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_686205_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00000614)
+#define HWIO_REG_686205_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000614)
+#define HWIO_REG_686205_RMSK  0x1ffff
+#define HWIO_REG_686205_SHFT  0
+#define HWIO_REG_686205_IN  in_dword_masked(\
+	HWIO_REG_686205_ADDR, HWIO_REG_686205_RMSK)
+#define HWIO_REG_686205_INM(m) \
+	in_dword_masked(HWIO_REG_686205_ADDR, m)
+#define HWIO_REG_686205_OUT(v) \
+	out_dword(HWIO_REG_686205_ADDR, v)
+#define HWIO_REG_686205_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_686205_ADDR, m, v,\
+	HWIO_REG_686205_IN);
+#define HWIO_REG_686205_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_686205_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_728036_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00000618)
+#define HWIO_REG_728036_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000618)
+#define HWIO_REG_728036_RMSK 0x1ffff
+#define HWIO_REG_728036_SHFT 0
+#define HWIO_REG_728036_IN  in_dword_masked(\
+	HWIO_REG_728036_ADDR, HWIO_REG_728036_RMSK)
+#define HWIO_REG_728036_INM(m) \
+	in_dword_masked(HWIO_REG_728036_ADDR, m)
+#define HWIO_REG_728036_OUT(v) \
+	out_dword(HWIO_REG_728036_ADDR, v)
+#define HWIO_REG_728036_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_728036_ADDR, m, v,\
+	HWIO_REG_728036_IN);
+#define HWIO_REG_728036_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_728036_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_294579_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000061c)
+#define HWIO_REG_294579_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000061c)
+#define HWIO_REG_294579_RMSK  0x1ffff
+#define HWIO_REG_294579_SHFT  0
+#define HWIO_REG_294579_IN  in_dword_masked(\
+	HWIO_REG_294579_ADDR, HWIO_REG_294579_RMSK)
+#define HWIO_REG_294579_INM(m) \
+	in_dword_masked(HWIO_REG_294579_ADDR, m)
+#define HWIO_REG_294579_OUT(v) \
+	out_dword(HWIO_REG_294579_ADDR, v)
+#define HWIO_REG_294579_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_294579_ADDR, m, v,\
+	HWIO_REG_294579_IN);
+#define HWIO_REG_294579_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_294579_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_61427_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00000620)
+#define HWIO_REG_61427_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000620)
+#define HWIO_REG_61427_RMSK  0x1ffff
+#define HWIO_REG_61427_SHFT  0
+#define HWIO_REG_61427_IN  in_dword_masked(\
+	HWIO_REG_61427_ADDR, HWIO_REG_61427_RMSK)
+#define HWIO_REG_61427_INM(m) \
+	in_dword_masked(HWIO_REG_61427_ADDR, m)
+#define HWIO_REG_61427_OUT(v) \
+	out_dword(HWIO_REG_61427_ADDR, v)
+#define HWIO_REG_61427_OUTM(m , v)  out_dword_masked_ns(\
+	HWIO_REG_61427_ADDR, m, v,\
+	HWIO_REG_61427_IN);
+#define HWIO_REG_61427_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_61427_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_578196_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00000624)
+#define HWIO_REG_578196_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000624)
+#define HWIO_REG_578196_RMSK  0x1ffff
+#define HWIO_REG_578196_SHFT  0
+#define HWIO_REG_578196_IN  in_dword_masked(\
+	HWIO_REG_578196_ADDR, HWIO_REG_578196_RMSK)
+#define HWIO_REG_578196_INM(m) \
+	in_dword_masked(HWIO_REG_578196_ADDR, m)
+#define HWIO_REG_578196_OUT(v) \
+	out_dword(HWIO_REG_578196_ADDR, v)
+#define HWIO_REG_578196_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_578196_ADDR, m, v,\
+	HWIO_REG_578196_IN);
+#define HWIO_REG_578196_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_578196_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_408588_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000628)
+#define HWIO_REG_408588_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000628)
+#define HWIO_REG_408588_RMSK  0x1ffff
+#define HWIO_REG_408588_SHFT  0
+#define HWIO_REG_408588_IN  in_dword_masked(\
+	HWIO_REG_408588_ADDR, HWIO_REG_408588_RMSK)
+#define HWIO_REG_408588_INM(m) \
+	in_dword_masked(HWIO_REG_408588_ADDR, m)
+#define HWIO_REG_408588_OUT(v) \
+	out_dword(HWIO_REG_408588_ADDR, v)
+#define HWIO_REG_408588_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_408588_ADDR, m, v,\
+	HWIO_REG_408588_IN);
+#define HWIO_REG_408588_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_408588_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_55617_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x0000062c)
+#define HWIO_REG_55617_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000062c)
+#define HWIO_REG_55617_RMSK  0x1ffff
+#define HWIO_REG_55617_SHFT  0
+#define HWIO_REG_55617_IN  in_dword_masked(\
+	HWIO_REG_55617_ADDR, HWIO_REG_55617_RMSK)
+#define HWIO_REG_55617_INM(m) \
+	in_dword_masked(HWIO_REG_55617_ADDR, m)
+#define HWIO_REG_55617_OUT(v) \
+	out_dword(HWIO_REG_55617_ADDR, v)
+#define HWIO_REG_55617_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_55617_ADDR, m, v,\
+	HWIO_REG_55617_IN);
+#define HWIO_REG_55617_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_55617_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_555239_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000630)
+#define HWIO_REG_555239_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000630)
+#define HWIO_REG_555239_RMSK  0x1ffff
+#define HWIO_REG_555239_SHFT  0
+#define HWIO_REG_555239_IN  in_dword_masked(\
+	HWIO_REG_555239_ADDR, HWIO_REG_555239_RMSK)
+#define HWIO_REG_555239_INM(m) \
+	in_dword_masked(HWIO_REG_555239_ADDR, m)
+#define HWIO_REG_555239_OUT(v) \
+	out_dword(HWIO_REG_555239_ADDR, v)
+#define HWIO_REG_555239_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_555239_ADDR, m, v,\
+	HWIO_REG_555239_IN);
+#define HWIO_REG_555239_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_555239_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_515333_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000634)
+#define HWIO_REG_515333_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000634)
+#define HWIO_REG_515333_RMSK  0x1ffff
+#define HWIO_REG_515333_SHFT  0
+#define HWIO_REG_515333_IN  in_dword_masked(\
+	HWIO_REG_515333_ADDR, HWIO_REG_515333_RMSK)
+#define HWIO_REG_515333_INM(m) \
+	in_dword_masked(HWIO_REG_515333_ADDR, m)
+#define HWIO_REG_515333_OUT(v) \
+	out_dword(HWIO_REG_515333_ADDR, v)
+#define HWIO_REG_515333_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_515333_ADDR, m, v,\
+	HWIO_REG_515333_IN);
+#define HWIO_REG_515333_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_515333_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_951675_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE  + 0x00000638)
+#define HWIO_REG_951675_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000638)
+#define HWIO_REG_951675_RMSK  0x1ffff
+#define HWIO_REG_951675_SHFT  0
+#define HWIO_REG_951675_IN  in_dword_masked(\
+	HWIO_REG_951675_ADDR, HWIO_REG_951675_RMSK)
+#define HWIO_REG_951675_INM(m) \
+	in_dword_masked(HWIO_REG_951675_ADDR, m)
+#define HWIO_REG_951675_OUT(v) \
+	out_dword(HWIO_REG_951675_ADDR, v)
+#define HWIO_REG_951675_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_951675_ADDR, m, v,\
+	HWIO_REG_951675_IN);
+#define HWIO_REG_951675_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_951675_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_500775_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE  + 0x0000063c)
+#define HWIO_REG_500775_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000063c)
+#define HWIO_REG_500775_RMSK  0x1ffff
+#define HWIO_REG_500775_SHFT  0
+#define HWIO_REG_500775_IN  in_dword_masked(\
+	HWIO_REG_500775_ADDR, HWIO_REG_500775_RMSK)
+#define HWIO_REG_500775_INM(m) \
+	in_dword_masked(HWIO_REG_500775_ADDR, m)
+#define HWIO_REG_500775_OUT(v) \
+	out_dword(HWIO_REG_500775_ADDR, v)
+#define HWIO_REG_500775_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_500775_ADDR, m, v,\
+	HWIO_REG_500775_IN);
+#define HWIO_REG_500775_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_500775_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_649786_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000640)
+#define HWIO_REG_649786_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000640)
+#define HWIO_REG_649786_RMSK  0x1ffff
+#define HWIO_REG_649786_SHFT  0
+#define HWIO_REG_649786_IN  in_dword_masked(\
+	HWIO_REG_649786_ADDR, HWIO_REG_649786_RMSK)
+#define HWIO_REG_649786_INM(m) \
+	in_dword_masked(HWIO_REG_649786_ADDR, m)
+#define HWIO_REG_649786_OUT(v) \
+	out_dword(HWIO_REG_649786_ADDR, v)
+#define HWIO_REG_649786_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_649786_ADDR, m, v,\
+	HWIO_REG_649786_IN);
+#define HWIO_REG_649786_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_649786_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_233366_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000644)
+#define HWIO_REG_233366_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000644)
+#define HWIO_REG_233366_RMSK  0x1ffff
+#define HWIO_REG_233366_SHFT  0
+#define HWIO_REG_233366_IN  in_dword_masked(\
+	HWIO_REG_233366_ADDR, HWIO_REG_233366_RMSK)
+#define HWIO_REG_233366_INM(m) \
+	in_dword_masked(HWIO_REG_233366_ADDR, m)
+#define HWIO_REG_233366_OUT(v) \
+	out_dword(HWIO_REG_233366_ADDR, v)
+#define HWIO_REG_233366_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_233366_ADDR, m, v,\
+	HWIO_REG_233366_IN);
+#define HWIO_REG_233366_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_233366_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_366750_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000648)
+#define HWIO_REG_366750_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000648)
+#define HWIO_REG_366750_RMSK  0x1ffff
+#define HWIO_REG_366750_SHFT  0
+#define HWIO_REG_366750_IN  in_dword_masked(\
+	HWIO_REG_366750_ADDR, HWIO_REG_366750_RMSK)
+#define HWIO_REG_366750_INM(m) \
+	in_dword_masked(HWIO_REG_366750_ADDR, m)
+#define HWIO_REG_366750_OUT(v) \
+	out_dword(HWIO_REG_366750_ADDR, v)
+#define HWIO_REG_366750_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_366750_ADDR, m, v,\
+	HWIO_REG_366750_IN);
+#define HWIO_REG_366750_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_366750_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_616292_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x0000064c)
+#define HWIO_REG_616292_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000064c)
+#define HWIO_REG_616292_RMSK  0x1ffff
+#define HWIO_REG_616292_SHFT  0
+#define HWIO_REG_616292_IN  in_dword_masked(\
+	HWIO_REG_616292_ADDR, HWIO_REG_616292_RMSK)
+#define HWIO_REG_616292_INM(m) \
+	in_dword_masked(HWIO_REG_616292_ADDR, m)
+#define HWIO_REG_616292_OUT(v) \
+	out_dword(HWIO_REG_616292_ADDR, v)
+#define HWIO_REG_616292_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_616292_ADDR, m, v,\
+	HWIO_REG_616292_IN);
+#define HWIO_REG_616292_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_616292_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_666754_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000650)
+#define HWIO_REG_666754_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000650)
+#define HWIO_REG_666754_RMSK  0x1ffff
+#define HWIO_REG_666754_SHFT  0
+#define HWIO_REG_666754_IN  in_dword_masked(\
+	HWIO_REG_666754_ADDR, HWIO_REG_666754_RMSK)
+#define HWIO_REG_666754_INM(m) \
+	in_dword_masked(HWIO_REG_666754_ADDR, m)
+#define HWIO_REG_666754_OUT(v) \
+	out_dword(HWIO_REG_666754_ADDR, v)
+#define HWIO_REG_666754_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_666754_ADDR, m, v,\
+	HWIO_REG_666754_IN);
+#define HWIO_REG_666754_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_666754_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_650155_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000654)
+#define HWIO_REG_650155_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000654)
+#define HWIO_REG_650155_RMSK  0x1ffff
+#define HWIO_REG_650155_SHFT  0
+#define HWIO_REG_650155_IN  in_dword_masked(\
+	HWIO_REG_650155_ADDR, HWIO_REG_650155_RMSK)
+#define HWIO_REG_650155_INM(m) \
+	in_dword_masked(HWIO_REG_650155_ADDR, m)
+#define HWIO_REG_650155_OUT(v) \
+	out_dword(HWIO_REG_650155_ADDR, v)
+#define HWIO_REG_650155_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_650155_ADDR, m, v,\
+	HWIO_REG_650155_IN);
+#define HWIO_REG_650155_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_650155_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_248198_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000658)
+#define HWIO_REG_248198_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000658)
+#define HWIO_REG_248198_RMSK  0x1ffff
+#define HWIO_REG_248198_SHFT  0
+#define HWIO_REG_248198_IN  in_dword_masked(\
+	HWIO_REG_248198_ADDR, HWIO_REG_248198_RMSK)
+#define HWIO_REG_248198_INM(m) \
+	in_dword_masked(HWIO_REG_248198_ADDR, m)
+#define HWIO_REG_248198_OUT(v) \
+	out_dword(HWIO_REG_248198_ADDR, v)
+#define HWIO_REG_248198_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_248198_ADDR, m, v,\
+	HWIO_REG_248198_IN);
+#define HWIO_REG_248198_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_248198_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_389428_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x0000065c)
+#define HWIO_REG_389428_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000065c)
+#define HWIO_REG_389428_RMSK  0x1ffff
+#define HWIO_REG_389428_SHFT  0
+#define HWIO_REG_389428_IN  in_dword_masked(\
+	HWIO_REG_389428_ADDR, HWIO_REG_389428_RMSK)
+#define HWIO_REG_389428_INM(m) \
+	in_dword_masked(HWIO_REG_389428_ADDR, m)
+#define HWIO_REG_389428_OUT(v) \
+	out_dword(HWIO_REG_389428_ADDR, v)
+#define HWIO_REG_389428_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_389428_ADDR, m, v,\
+	HWIO_REG_389428_IN);
+#define HWIO_REG_389428_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_389428_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_504308_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000660)
+#define HWIO_REG_504308_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000660)
+#define HWIO_REG_504308_RMSK  0x1ffff
+#define HWIO_REG_504308_SHFT  0
+#define HWIO_REG_504308_IN  in_dword_masked(\
+	HWIO_REG_504308_ADDR, HWIO_REG_504308_RMSK)
+#define HWIO_REG_504308_INM(m) \
+	in_dword_masked(HWIO_REG_504308_ADDR, m)
+#define HWIO_REG_504308_OUT(v) \
+	out_dword(HWIO_REG_504308_ADDR, v)
+#define HWIO_REG_504308_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_504308_ADDR, m, v,\
+	HWIO_REG_504308_IN);
+#define HWIO_REG_504308_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_504308_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_280814_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000664)
+#define HWIO_REG_280814_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000664)
+#define HWIO_REG_280814_RMSK  0x1ffff
+#define HWIO_REG_280814_SHFT  0
+#define HWIO_REG_280814_IN  in_dword_masked(\
+	HWIO_REG_280814_ADDR, HWIO_REG_280814_RMSK)
+#define HWIO_REG_280814_INM(m) \
+	in_dword_masked(HWIO_REG_280814_ADDR, m)
+#define HWIO_REG_280814_OUT(v) \
+	out_dword(HWIO_REG_280814_ADDR, v)
+#define HWIO_REG_280814_OUTM(m, v) \
+	out_dword_masked_ns(HWIO_REG_280814_ADDR, m, v,\
+	HWIO_REG_280814_IN);
+#define HWIO_REG_280814_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_280814_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_785484_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000668)
+#define HWIO_REG_785484_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000668)
+#define HWIO_REG_785484_RMSK  0x1ffff
+#define HWIO_REG_785484_SHFT  0
+#define HWIO_REG_785484_IN  in_dword_masked(\
+	HWIO_REG_785484_ADDR, HWIO_REG_785484_RMSK)
+#define HWIO_REG_785484_INM(m) \
+	in_dword_masked(HWIO_REG_785484_ADDR, m)
+#define HWIO_REG_785484_OUT(v) \
+		out_dword(HWIO_REG_785484_ADDR, v)
+#define HWIO_REG_785484_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_785484_ADDR, m, v,\
+	HWIO_REG_785484_IN);
+#define HWIO_REG_785484_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_785484_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_218455_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x0000066c)
+#define HWIO_REG_218455_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000066c)
+#define HWIO_REG_218455_RMSK  0x1ffff
+#define HWIO_REG_218455_SHFT  0
+#define HWIO_REG_218455_IN  in_dword_masked(\
+	HWIO_REG_218455_ADDR, HWIO_REG_218455_RMSK)
+#define HWIO_REG_218455_INM(m) \
+	in_dword_masked(HWIO_REG_218455_ADDR, m)
+#define HWIO_REG_218455_OUT(v) \
+	out_dword(HWIO_REG_218455_ADDR, v)
+#define HWIO_REG_218455_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_218455_ADDR, m, v,\
+	HWIO_REG_218455_IN);
+#define HWIO_REG_218455_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_218455_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_886591_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000670)
+#define HWIO_REG_886591_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000670)
+#define HWIO_REG_886591_RMSK  0x1ffff
+#define HWIO_REG_886591_SHFT  0
+#define HWIO_REG_886591_IN  in_dword_masked(\
+	HWIO_REG_886591_ADDR, HWIO_REG_886591_RMSK)
+#define HWIO_REG_886591_INM(m) \
+	in_dword_masked(HWIO_REG_886591_ADDR, m)
+#define HWIO_REG_886591_OUT(v) \
+	out_dword(HWIO_REG_886591_ADDR, v)
+#define HWIO_REG_886591_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_886591_ADDR, m, v,\
+	HWIO_REG_886591_IN);
+#define HWIO_REG_886591_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_886591_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_912449_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000674)
+#define HWIO_REG_912449_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000674)
+#define HWIO_REG_912449_RMSK  0x1ffff
+#define HWIO_REG_912449_SHFT  0
+#define HWIO_REG_912449_IN  in_dword_masked(\
+	HWIO_REG_912449_ADDR, HWIO_REG_912449_RMSK)
+#define HWIO_REG_912449_INM(m) \
+	in_dword_masked(HWIO_REG_912449_ADDR, m)
+#define HWIO_REG_912449_OUT(v) \
+	out_dword(HWIO_REG_912449_ADDR, v)
+#define HWIO_REG_912449_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_912449_ADDR, m, v,\
+	HWIO_REG_912449_IN);
+#define HWIO_REG_912449_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_912449_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_1065_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000678)
+#define HWIO_REG_1065_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000678)
+#define HWIO_REG_1065_RMSK  0x1ffff
+#define HWIO_REG_1065_SHFT  0
+#define HWIO_REG_1065_IN  in_dword_masked(\
+	HWIO_REG_1065_ADDR, HWIO_REG_1065_RMSK)
+#define HWIO_REG_1065_INM(m) \
+	in_dword_masked(HWIO_REG_1065_ADDR, m)
+#define HWIO_REG_1065_OUT(v) \
+	out_dword(HWIO_REG_1065_ADDR, v)
+#define HWIO_REG_1065_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_1065_ADDR, m, v,\
+	HWIO_REG_1065_IN);
+#define HWIO_REG_1065_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_1065_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_61838_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x0000067c)
+#define HWIO_REG_61838_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000067c)
+#define HWIO_REG_61838_RMSK  0x1ffff
+#define HWIO_REG_61838_SHFT  0
+#define HWIO_REG_61838_IN  in_dword_masked(\
+	HWIO_REG_61838_ADDR, HWIO_REG_61838_RMSK)
+#define HWIO_REG_61838_INM(m) \
+	in_dword_masked(HWIO_REG_61838_ADDR, m)
+#define HWIO_REG_61838_OUT(v) \
+	out_dword(HWIO_REG_61838_ADDR, v)
+#define HWIO_REG_61838_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_61838_ADDR, m, v,\
+	HWIO_REG_61838_IN);
+#define HWIO_REG_61838_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_61838_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_169838_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000680)
+#define HWIO_REG_169838_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000680)
+#define HWIO_REG_169838_RMSK  0x1ffff
+#define HWIO_REG_169838_SHFT  0
+#define HWIO_REG_169838_IN  in_dword_masked(\
+	HWIO_REG_169838_ADDR, HWIO_REG_169838_RMSK)
+#define HWIO_REG_169838_INM(m) \
+	in_dword_masked(HWIO_REG_169838_ADDR, m)
+#define HWIO_REG_169838_OUT(v) \
+	out_dword(HWIO_REG_169838_ADDR, v)
+#define HWIO_REG_169838_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_169838_ADDR, m, v,\
+	HWIO_REG_169838_IN);
+#define HWIO_REG_169838_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_169838_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_986147_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000684)
+#define HWIO_REG_986147_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000684)
+#define HWIO_REG_986147_RMSK  0x1ffff
+#define HWIO_REG_986147_SHFT  0
+#define HWIO_REG_986147_IN  in_dword_masked(\
+	HWIO_REG_986147_ADDR, HWIO_REG_986147_RMSK)
+#define HWIO_REG_986147_INM(m) \
+	in_dword_masked(HWIO_REG_986147_ADDR, m)
+#define HWIO_REG_986147_OUT(v)  \
+	out_dword(HWIO_REG_986147_ADDR, v)
+#define HWIO_REG_986147_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_986147_ADDR, m, v,\
+	HWIO_REG_986147_IN);
+#define HWIO_REG_986147_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_986147_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_678637_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000688)
+#define HWIO_REG_678637_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000688)
+#define HWIO_REG_678637_RMSK  0x1ffff
+#define HWIO_REG_678637_SHFT  0
+#define HWIO_REG_678637_IN  in_dword_masked(\
+	HWIO_REG_678637_ADDR, HWIO_REG_678637_RMSK)
+#define HWIO_REG_678637_INM(m) \
+	in_dword_masked(HWIO_REG_678637_ADDR, m)
+#define HWIO_REG_678637_OUT(v) \
+	out_dword(HWIO_REG_678637_ADDR, v)
+#define HWIO_REG_678637_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_678637_ADDR, m, v,\
+	HWIO_REG_678637_IN);
+#define HWIO_REG_678637_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_678637_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_931311_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x0000068c)
+#define HWIO_REG_931311_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000068c)
+#define HWIO_REG_931311_RMSK  0x1ffff
+#define HWIO_REG_931311_SHFT  0
+#define HWIO_REG_931311_IN  in_dword_masked(\
+	HWIO_REG_931311_ADDR, HWIO_REG_931311_RMSK)
+#define HWIO_REG_931311_INM(m) \
+	in_dword_masked(HWIO_REG_931311_ADDR, m)
+#define HWIO_REG_931311_OUT(v) \
+	out_dword(HWIO_REG_931311_ADDR, v)
+#define HWIO_REG_931311_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_931311_ADDR, m, v,\
+	HWIO_REG_931311_IN);
+#define HWIO_REG_931311_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_931311_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_16277_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000690)
+#define HWIO_REG_16277_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000690)
+#define HWIO_REG_16277_RMSK  0x1ffff
+#define HWIO_REG_16277_SHFT  0
+#define HWIO_REG_16277_IN  in_dword_masked(\
+	HWIO_REG_16277_ADDR, HWIO_REG_16277_RMSK)
+#define HWIO_REG_16277_INM(m) \
+	in_dword_masked(HWIO_REG_16277_ADDR, m)
+#define HWIO_REG_16277_OUT(v) \
+	out_dword(HWIO_REG_16277_ADDR, v)
+#define HWIO_REG_16277_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_16277_ADDR, m, v,\
+	HWIO_REG_16277_IN);
+#define HWIO_REG_16277_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_16277_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_654169_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE  + 0x00000694)
+#define HWIO_REG_654169_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000694)
+#define HWIO_REG_654169_RMSK  0x1ffff
+#define HWIO_REG_654169_SHFT  0
+#define HWIO_REG_654169_IN  in_dword_masked(\
+	HWIO_REG_654169_ADDR, HWIO_REG_654169_RMSK)
+#define HWIO_REG_654169_INM(m) \
+	in_dword_masked(HWIO_REG_654169_ADDR, m)
+#define HWIO_REG_654169_OUT(v) \
+	out_dword(HWIO_REG_654169_ADDR, v)
+#define HWIO_REG_654169_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_654169_ADDR, m, v,\
+	HWIO_REG_654169_IN);
+#define HWIO_REG_654169_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_654169_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_802794_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000698)
+#define HWIO_REG_802794_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000698)
+#define HWIO_REG_802794_RMSK  0x1ffff
+#define HWIO_REG_802794_SHFT  0
+#define HWIO_REG_802794_IN  in_dword_masked(\
+	HWIO_REG_802794_ADDR, HWIO_REG_802794_RMSK)
+#define HWIO_REG_802794_INM(m) \
+	in_dword_masked(HWIO_REG_802794_ADDR, m)
+#define HWIO_REG_802794_OUT(v) \
+	out_dword(HWIO_REG_802794_ADDR, v)
+#define HWIO_REG_802794_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_802794_ADDR, m, v,\
+	HWIO_REG_802794_IN);
+#define HWIO_REG_802794_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_802794_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_724376_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x0000069c)
+#define HWIO_REG_724376_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000069c)
+#define HWIO_REG_724376_RMSK  0x1ffff
+#define HWIO_REG_724376_SHFT  0
+#define HWIO_REG_724376_IN  in_dword_masked(\
+	HWIO_REG_724376_ADDR, HWIO_REG_724376_RMSK)
+#define HWIO_REG_724376_INM(m) \
+	in_dword_masked(HWIO_REG_724376_ADDR, m)
+#define HWIO_REG_724376_OUT(v) \
+	out_dword(HWIO_REG_724376_ADDR, v)
+#define HWIO_REG_724376_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_724376_ADDR, m, v,\
+	HWIO_REG_724376_IN);
+#define HWIO_REG_724376_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_724376_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_551674_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000006a0)
+#define HWIO_REG_551674_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000006a0)
+#define HWIO_REG_551674_RMSK  0x1ffff
+#define HWIO_REG_551674_SHFT  0
+#define HWIO_REG_551674_IN  in_dword_masked(\
+	HWIO_REG_551674_ADDR, HWIO_REG_551674_RMSK)
+#define HWIO_REG_551674_INM(m) \
+	in_dword_masked(HWIO_REG_551674_ADDR, m)
+#define HWIO_REG_551674_OUT(v) \
+	out_dword(HWIO_REG_551674_ADDR, v)
+#define HWIO_REG_551674_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_551674_ADDR, m, v,\
+	HWIO_REG_551674_IN);
+#define HWIO_REG_551674_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_551674_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_115991_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000006a4)
+#define HWIO_REG_115991_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000006a4)
+#define HWIO_REG_115991_RMSK  0x1ffff
+#define HWIO_REG_115991_SHFT  0
+#define HWIO_REG_115991_IN  in_dword_masked(\
+	HWIO_REG_115991_ADDR, HWIO_REG_115991_RMSK)
+#define HWIO_REG_115991_INM(m) \
+	in_dword_masked(HWIO_REG_115991_ADDR, m)
+#define HWIO_REG_115991_OUT(v) \
+	out_dword(HWIO_REG_115991_ADDR, v)
+#define HWIO_REG_115991_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_115991_ADDR, m, v,\
+	HWIO_REG_115991_IN);
+#define HWIO_REG_115991_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_115991_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_252167_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000006a8)
+#define HWIO_REG_252167_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000006a8)
+#define HWIO_REG_252167_RMSK  0x1ffff
+#define HWIO_REG_252167_SHFT  0
+#define HWIO_REG_252167_IN  in_dword_masked(\
+	HWIO_REG_252167_ADDR, HWIO_REG_252167_RMSK)
+#define HWIO_REG_252167_INM(m) \
+	in_dword_masked(HWIO_REG_252167_ADDR, m)
+#define HWIO_REG_252167_OUT(v) \
+	out_dword(HWIO_REG_252167_ADDR, v)
+#define HWIO_REG_252167_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_252167_ADDR, m, v,\
+	HWIO_REG_252167_IN);
+#define HWIO_REG_252167_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_252167_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_695516_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000006ac)
+#define HWIO_REG_695516_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000006ac)
+#define HWIO_REG_695516_RMSK  0x1ffff
+#define HWIO_REG_695516_SHFT  0
+#define HWIO_REG_695516_IN  in_dword_masked(\
+	HWIO_REG_695516_ADDR, HWIO_REG_695516_RMSK)
+#define HWIO_REG_695516_INM(m) \
+	in_dword_masked(HWIO_REG_695516_ADDR, m)
+#define HWIO_REG_695516_OUT(v) \
+	out_dword(HWIO_REG_695516_ADDR, v)
+#define HWIO_REG_695516_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_695516_ADDR, m, v,\
+	HWIO_REG_695516_IN);
+#define HWIO_REG_695516_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_695516_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_152193_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000006b0)
+#define HWIO_REG_152193_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000006b0)
+#define HWIO_REG_152193_RMSK  0x1ffff
+#define HWIO_REG_152193_SHFT  0
+#define HWIO_REG_152193_IN  in_dword_masked(\
+	HWIO_REG_152193_ADDR, HWIO_REG_152193_RMSK)
+#define HWIO_REG_152193_INM(m) \
+	in_dword_masked(HWIO_REG_152193_ADDR, m)
+#define HWIO_REG_152193_OUT(v) \
+	out_dword(HWIO_REG_152193_ADDR, v)
+#define HWIO_REG_152193_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_152193_ADDR, m, v,\
+	HWIO_REG_152193_IN);
+#define HWIO_REG_152193_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_152193_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_358705_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000006b4)
+#define HWIO_REG_358705_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000006b4)
+#define HWIO_REG_358705_RMSK  0x1ffff
+#define HWIO_REG_358705_SHFT  0
+#define HWIO_REG_358705_IN  in_dword_masked(\
+	HWIO_REG_358705_ADDR, HWIO_REG_358705_RMSK)
+#define HWIO_REG_358705_INM(m) \
+	in_dword_masked(HWIO_REG_358705_ADDR, m)
+#define HWIO_REG_358705_OUT(v) \
+	out_dword(HWIO_REG_358705_ADDR, v)
+#define HWIO_REG_358705_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_358705_ADDR, m, v,\
+	HWIO_REG_358705_IN);
+#define HWIO_REG_358705_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_358705_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_457068_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000006b8)
+#define HWIO_REG_457068_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000006b8)
+#define HWIO_REG_457068_RMSK  0x1ffff
+#define HWIO_REG_457068_SHFT  0
+#define HWIO_REG_457068_IN  in_dword_masked(\
+	HWIO_REG_457068_ADDR, HWIO_REG_457068_RMSK)
+#define HWIO_REG_457068_INM(m) \
+	in_dword_masked(HWIO_REG_457068_ADDR, m)
+#define HWIO_REG_457068_OUT(v) \
+	out_dword(HWIO_REG_457068_ADDR, v)
+#define HWIO_REG_457068_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_457068_ADDR, m, v,\
+	HWIO_REG_457068_IN);
+#define HWIO_REG_457068_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_457068_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_485412_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000006bc)
+#define HWIO_REG_485412_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000006bc)
+#define HWIO_REG_485412_RMSK  0x1ffff
+#define HWIO_REG_485412_SHFT  0
+#define HWIO_REG_485412_IN  in_dword_masked(\
+	HWIO_REG_485412_ADDR, HWIO_REG_485412_RMSK)
+#define HWIO_REG_485412_INM(m) \
+	in_dword_masked(HWIO_REG_485412_ADDR, m)
+#define HWIO_REG_485412_OUT(v) \
+	out_dword(HWIO_REG_485412_ADDR, v)
+#define HWIO_REG_485412_OUTM(m, v) \
+	out_dword_masked_ns(HWIO_REG_485412_ADDR, m, v,\
+	HWIO_REG_485412_IN);
+#define HWIO_REG_485412_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_485412_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_223131_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000006c0)
+#define HWIO_REG_223131_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000006c0)
+#define HWIO_REG_223131_RMSK  0x1ffff
+#define HWIO_REG_223131_SHFT  0
+#define HWIO_REG_223131_IN  in_dword_masked(\
+	HWIO_REG_223131_ADDR, HWIO_REG_223131_RMSK)
+#define HWIO_REG_223131_INM(m) \
+		in_dword_masked(HWIO_REG_223131_ADDR, m)
+#define HWIO_REG_223131_OUT(v) \
+		out_dword(HWIO_REG_223131_ADDR, v)
+#define HWIO_REG_223131_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_223131_ADDR, m, v,\
+	HWIO_REG_223131_IN);
+#define HWIO_REG_223131_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_223131_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_683737_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000006c4)
+#define HWIO_REG_683737_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000006c4)
+#define HWIO_REG_683737_RMSK  0x1ffff
+#define HWIO_REG_683737_SHFT  0
+#define HWIO_REG_683737_IN  in_dword_masked(\
+	HWIO_REG_683737_ADDR, HWIO_REG_683737_RMSK)
+#define HWIO_REG_683737_INM(m) \
+	in_dword_masked(HWIO_REG_683737_ADDR, m)
+#define HWIO_REG_683737_OUT(v) \
+	out_dword(HWIO_REG_683737_ADDR, v)
+#define HWIO_REG_683737_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_683737_ADDR, m, v,\
+	HWIO_REG_683737_IN);
+#define HWIO_REG_683737_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_683737_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_750474_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000006c8)
+#define HWIO_REG_750474_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000006c8)
+#define HWIO_REG_750474_RMSK  0x1ffff
+#define HWIO_REG_750474_SHFT  0
+#define HWIO_REG_750474_IN  in_dword_masked(\
+	HWIO_REG_750474_ADDR, HWIO_REG_750474_RMSK)
+#define HWIO_REG_750474_INM(m) \
+	in_dword_masked(HWIO_REG_750474_ADDR, m)
+#define HWIO_REG_750474_OUT(v) \
+	out_dword(HWIO_REG_750474_ADDR, v)
+#define HWIO_REG_750474_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_750474_ADDR, m, v,\
+	HWIO_REG_750474_IN);
+#define HWIO_REG_750474_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_750474_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_170086_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000006cc)
+#define HWIO_REG_170086_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000006cc)
+#define HWIO_REG_170086_RMSK  0x1ffff
+#define HWIO_REG_170086_SHFT  0
+#define HWIO_REG_170086_IN  in_dword_masked(\
+	HWIO_REG_170086_ADDR, HWIO_REG_170086_RMSK)
+#define HWIO_REG_170086_INM(m) \
+	in_dword_masked(HWIO_REG_170086_ADDR, m)
+#define HWIO_REG_170086_OUT(v) \
+	out_dword(HWIO_REG_170086_ADDR, v)
+#define HWIO_REG_170086_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_170086_ADDR, m, v,\
+	HWIO_REG_170086_IN);
+#define HWIO_REG_170086_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_170086_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_838595_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000006d0)
+#define HWIO_REG_838595_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000006d0)
+#define HWIO_REG_838595_RMSK  0x1ffff
+#define HWIO_REG_838595_SHFT  0
+#define HWIO_REG_838595_IN  in_dword_masked(\
+	HWIO_REG_838595_ADDR, HWIO_REG_838595_RMSK)
+#define HWIO_REG_838595_INM(m)  \
+	in_dword_masked(HWIO_REG_838595_ADDR, m)
+#define HWIO_REG_838595_OUT(v)  \
+	out_dword(HWIO_REG_838595_ADDR, v)
+#define HWIO_REG_838595_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_838595_ADDR, m, v,\
+	HWIO_REG_838595_IN);
+#define HWIO_REG_838595_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_838595_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_569788_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000006d4)
+#define HWIO_REG_569788_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000006d4)
+#define HWIO_REG_569788_RMSK  0x1ffff
+#define HWIO_REG_569788_SHFT  0
+#define HWIO_REG_569788_IN  in_dword_masked(\
+	HWIO_REG_569788_ADDR, HWIO_REG_569788_RMSK)
+#define HWIO_REG_569788_INM(m) \
+	in_dword_masked(HWIO_REG_569788_ADDR, m)
+#define HWIO_REG_569788_OUT(v) \
+	out_dword(HWIO_REG_569788_ADDR, v)
+#define HWIO_REG_569788_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_569788_ADDR, m, v,\
+	HWIO_REG_569788_IN);
+#define HWIO_REG_569788_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_569788_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_974527_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000006d8)
+#define HWIO_REG_974527_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000006d8)
+#define HWIO_REG_974527_RMSK  0x1ffff
+#define HWIO_REG_974527_SHFT  0
+#define HWIO_REG_974527_IN  in_dword_masked(\
+	HWIO_REG_974527_ADDR, HWIO_REG_974527_RMSK)
+#define HWIO_REG_974527_INM(m) \
+	in_dword_masked(HWIO_REG_974527_ADDR, m)
+#define HWIO_REG_974527_OUT(v) \
+	out_dword(HWIO_REG_974527_ADDR, v)
+#define HWIO_REG_974527_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_974527_ADDR, m, v,\
+	HWIO_REG_974527_IN);
+#define HWIO_REG_974527_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_974527_BASE_ADDR_SHFT   0
+
+#define HWIO_REG_316806_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000006dc)
+#define HWIO_REG_316806_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000006dc)
+#define HWIO_REG_316806_RMSK  0x1ffff
+#define HWIO_REG_316806_SHFT  0
+#define HWIO_REG_316806_IN  in_dword_masked(\
+	HWIO_REG_316806_ADDR, HWIO_REG_316806_RMSK)
+#define HWIO_REG_316806_INM(m) \
+	in_dword_masked(HWIO_REG_316806_ADDR, m)
+#define HWIO_REG_316806_OUT(v) \
+	out_dword(HWIO_REG_316806_ADDR, v)
+#define HWIO_REG_316806_OUTM(m, v) \
+	out_dword_masked_ns(HWIO_REG_316806_ADDR, m, v,\
+	HWIO_REG_316806_IN);
+#define HWIO_REG_316806_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_316806_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_900472_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000006e0)
+#define HWIO_REG_900472_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000006e0)
+#define HWIO_REG_900472_RMSK  0x1ffff
+#define HWIO_REG_900472_SHFT  0
+#define HWIO_REG_900472_IN  in_dword_masked(\
+	HWIO_REG_900472_ADDR, HWIO_REG_900472_RMSK)
+#define HWIO_REG_900472_INM(m) \
+	in_dword_masked(HWIO_REG_900472_ADDR, m)
+#define HWIO_REG_900472_OUT(v) \
+	out_dword(HWIO_REG_900472_ADDR, v)
+#define HWIO_REG_900472_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_900472_ADDR, m, v,\
+	HWIO_REG_900472_IN);
+#define HWIO_REG_900472_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_900472_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_256156_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000006e4)
+#define HWIO_REG_256156_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000006e4)
+#define HWIO_REG_256156_RMSK  0x1ffff
+#define HWIO_REG_256156_SHFT  0
+#define HWIO_REG_256156_IN  in_dword_masked(\
+	HWIO_REG_256156_ADDR, HWIO_REG_256156_RMSK)
+#define HWIO_REG_256156_INM(m) \
+	in_dword_masked(HWIO_REG_256156_ADDR, m)
+#define HWIO_REG_256156_OUT(v) \
+	out_dword(HWIO_REG_256156_ADDR, v)
+#define HWIO_REG_256156_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_256156_ADDR, m, v,\
+	HWIO_REG_256156_IN);
+#define HWIO_REG_256156_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_256156_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_335729_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000006e8)
+#define HWIO_REG_335729_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000006e8)
+#define HWIO_REG_335729_RMSK  0x1ffff
+#define HWIO_REG_335729_SHFT  0
+#define HWIO_REG_335729_IN  in_dword_masked(\
+	HWIO_REG_335729_ADDR, HWIO_REG_335729_RMSK)
+#define HWIO_REG_335729_INM(m) \
+	in_dword_masked(HWIO_REG_335729_ADDR, m)
+#define HWIO_REG_335729_OUT(v) \
+	out_dword(HWIO_REG_335729_ADDR, v)
+#define HWIO_REG_335729_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_335729_ADDR, m, v,\
+	HWIO_REG_335729_IN);
+#define HWIO_REG_335729_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_335729_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_303383_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000006ec)
+#define HWIO_REG_303383_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000006ec)
+#define HWIO_REG_303383_RMSK  0x1ffff
+#define HWIO_REG_303383_SHFT  0
+#define HWIO_REG_303383_IN  in_dword_masked(\
+	HWIO_REG_303383_ADDR, HWIO_REG_303383_RMSK)
+#define HWIO_REG_303383_INM(m) \
+	in_dword_masked(HWIO_REG_303383_ADDR, m)
+#define HWIO_REG_303383_OUT(v) \
+	out_dword(HWIO_REG_303383_ADDR, v)
+#define HWIO_REG_303383_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_303383_ADDR, m, v,\
+	HWIO_REG_303383_IN);
+#define HWIO_REG_303383_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_303383_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_180871_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000006f0)
+#define HWIO_REG_180871_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000006f0)
+#define HWIO_REG_180871_RMSK  0x1ffff
+#define HWIO_REG_180871_SHFT  0
+#define HWIO_REG_180871_IN  in_dword_masked(\
+	HWIO_REG_180871_ADDR, HWIO_REG_180871_RMSK)
+#define HWIO_REG_180871_INM(m) \
+	in_dword_masked(HWIO_REG_180871_ADDR, m)
+#define HWIO_REG_180871_OUT(v) \
+	out_dword(HWIO_REG_180871_ADDR, v)
+#define HWIO_REG_180871_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_180871_ADDR, m, v,\
+	HWIO_REG_180871_IN);
+#define HWIO_REG_180871_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_180871_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_514148_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000006f4)
+#define HWIO_REG_514148_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000006f4)
+#define HWIO_REG_514148_RMSK  0x1ffff
+#define HWIO_REG_514148_SHFT  0
+#define HWIO_REG_514148_IN  in_dword_masked(\
+	HWIO_REG_514148_ADDR, HWIO_REG_514148_RMSK)
+#define HWIO_REG_514148_INM(m) \
+	in_dword_masked(HWIO_REG_514148_ADDR, m)
+#define HWIO_REG_514148_OUT(v) \
+	out_dword(HWIO_REG_514148_ADDR, v)
+#define HWIO_REG_514148_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_514148_ADDR, m, v,\
+	HWIO_REG_514148_IN);
+#define HWIO_REG_514148_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_514148_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_578636_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000006f8)
+#define HWIO_REG_578636_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000006f8)
+#define HWIO_REG_578636_RMSK  0x1ffff
+#define HWIO_REG_578636_SHFT  0
+#define HWIO_REG_578636_IN  in_dword_masked(\
+	HWIO_REG_578636_ADDR, HWIO_REG_578636_RMSK)
+#define HWIO_REG_578636_INM(m) \
+	in_dword_masked(HWIO_REG_578636_ADDR, m)
+#define HWIO_REG_578636_OUT(v) \
+	out_dword(HWIO_REG_578636_ADDR, v)
+#define HWIO_REG_578636_OUTM(m, v) \
+	out_dword_masked_ns(HWIO_REG_578636_ADDR, m, v,\
+	HWIO_REG_578636_IN);
+#define HWIO_REG_578636_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_578636_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_888116_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000006fc)
+#define HWIO_REG_888116_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000006fc)
+#define HWIO_REG_888116_RMSK  0x1ffff
+#define HWIO_REG_888116_SHFT  0
+#define HWIO_REG_888116_IN  in_dword_masked(\
+	HWIO_REG_888116_ADDR, HWIO_REG_888116_RMSK)
+#define HWIO_REG_888116_INM(m) \
+	in_dword_masked(HWIO_REG_888116_ADDR, m)
+#define HWIO_REG_888116_OUT(v) \
+	out_dword(HWIO_REG_888116_ADDR, v)
+#define HWIO_REG_888116_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_888116_ADDR, m, v,\
+	HWIO_REG_888116_IN);
+#define HWIO_REG_888116_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_888116_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_759068_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000700)
+#define HWIO_REG_759068_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000700)
+#define HWIO_REG_759068_RMSK  0x1ffff
+#define HWIO_REG_759068_SHFT  0
+#define HWIO_REG_759068_IN  in_dword_masked(\
+	HWIO_REG_759068_ADDR, HWIO_REG_759068_RMSK)
+#define HWIO_REG_759068_INM(m) \
+	in_dword_masked(HWIO_REG_759068_ADDR, m)
+#define HWIO_REG_759068_OUT(v) \
+	out_dword(HWIO_REG_759068_ADDR, v)
+#define HWIO_REG_759068_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_759068_ADDR, m, v,\
+	HWIO_REG_759068_IN);
+#define HWIO_REG_759068_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_759068_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_68356_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000704)
+#define HWIO_REG_68356_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000704)
+#define HWIO_REG_68356_RMSK  0x1ffff
+#define HWIO_REG_68356_SHFT  0
+#define HWIO_REG_68356_IN   in_dword_masked(\
+	HWIO_REG_68356_ADDR, HWIO_REG_68356_RMSK)
+#define HWIO_REG_68356_INM(m) \
+	in_dword_masked(HWIO_REG_68356_ADDR, m)
+#define HWIO_REG_68356_OUT(v) \
+	out_dword(HWIO_REG_68356_ADDR, v)
+#define HWIO_REG_68356_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_68356_ADDR, m, v,\
+	HWIO_REG_68356_IN);
+#define HWIO_REG_68356_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_68356_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_833502_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000708)
+#define HWIO_REG_833502_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000708)
+#define HWIO_REG_833502_RMSK  0x1ffff
+#define HWIO_REG_833502_SHFT  0
+#define HWIO_REG_833502_IN  in_dword_masked(\
+	HWIO_REG_833502_ADDR, HWIO_REG_833502_RMSK)
+#define HWIO_REG_833502_INM(m) \
+	in_dword_masked(HWIO_REG_833502_ADDR, m)
+#define HWIO_REG_833502_OUT(v) \
+	out_dword(HWIO_REG_833502_ADDR, v)
+#define HWIO_REG_833502_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_833502_ADDR, m, v,\
+	HWIO_REG_833502_IN);
+#define HWIO_REG_833502_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_833502_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_127855_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x0000070c)
+#define HWIO_REG_127855_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000070c)
+#define HWIO_REG_127855_RMSK  0x1ffff
+#define HWIO_REG_127855_SHFT  0
+#define HWIO_REG_127855_IN  in_dword_masked(\
+	HWIO_REG_127855_ADDR, HWIO_REG_127855_RMSK)
+#define HWIO_REG_127855_INM(m) \
+	in_dword_masked(HWIO_REG_127855_ADDR, m)
+#define HWIO_REG_127855_OUT(v) \
+	out_dword(HWIO_REG_127855_ADDR, v)
+#define HWIO_REG_127855_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_127855_ADDR, m, v,\
+	HWIO_REG_127855_IN);
+#define HWIO_REG_127855_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_127855_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_616802_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000710)
+#define HWIO_REG_616802_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000710)
+#define HWIO_REG_616802_RMSK  0x1ffff
+#define HWIO_REG_616802_SHFT  0
+#define HWIO_REG_616802_IN  in_dword_masked(\
+	HWIO_REG_616802_ADDR, HWIO_REG_616802_RMSK)
+#define HWIO_REG_616802_INM(m) \
+	in_dword_masked(HWIO_REG_616802_ADDR, m)
+#define HWIO_REG_616802_OUT(v) \
+	out_dword(HWIO_REG_616802_ADDR, v)
+#define HWIO_REG_616802_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_616802_ADDR, m, v,\
+	HWIO_REG_616802_IN);
+#define HWIO_REG_616802_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_616802_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_23318_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000714)
+#define HWIO_REG_23318_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000714)
+#define HWIO_REG_23318_RMSK  0x1ffff
+#define HWIO_REG_23318_SHFT  0
+#define HWIO_REG_23318_IN  in_dword_masked(\
+	HWIO_REG_23318_ADDR, HWIO_REG_23318_RMSK)
+#define HWIO_REG_23318_INM(m) \
+	in_dword_masked(HWIO_REG_23318_ADDR, m)
+#define HWIO_REG_23318_OUT(v) \
+	out_dword(HWIO_REG_23318_ADDR, v)
+#define HWIO_REG_23318_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_23318_ADDR, m, v,\
+	HWIO_REG_23318_IN);
+#define HWIO_REG_23318_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_23318_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_317106_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000718)
+#define HWIO_REG_317106_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000718)
+#define HWIO_REG_317106_RMSK  0x1ffff
+#define HWIO_REG_317106_SHFT  0
+#define HWIO_REG_317106_IN  in_dword_masked(\
+	HWIO_REG_317106_ADDR, HWIO_REG_317106_RMSK)
+#define HWIO_REG_317106_INM(m) \
+	in_dword_masked(HWIO_REG_317106_ADDR, m)
+#define HWIO_REG_317106_OUT(v) \
+	out_dword(HWIO_REG_317106_ADDR, v)
+#define HWIO_REG_317106_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_317106_ADDR, m, v,\
+	HWIO_REG_317106_IN);
+#define HWIO_REG_317106_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_317106_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_603772_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x0000071c)
+#define HWIO_REG_603772_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000071c)
+#define HWIO_REG_603772_RMSK  0x1ffff
+#define HWIO_REG_603772_SHFT  0
+#define HWIO_REG_603772_IN  in_dword_masked(\
+	HWIO_REG_603772_ADDR, HWIO_REG_603772_RMSK)
+#define HWIO_REG_603772_INM(m) \
+	in_dword_masked(HWIO_REG_603772_ADDR, m)
+#define HWIO_REG_603772_OUT(v) \
+	out_dword(HWIO_REG_603772_ADDR, v)
+#define HWIO_REG_603772_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_603772_ADDR, m, v,\
+	HWIO_REG_603772_IN);
+#define HWIO_REG_603772_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_603772_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_175929_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000720)
+#define HWIO_REG_175929_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000720)
+#define HWIO_REG_175929_RMSK  0x1ffff
+#define HWIO_REG_175929_SHFT  0
+#define HWIO_REG_175929_IN  in_dword_masked(\
+	HWIO_REG_175929_ADDR, HWIO_REG_175929_RMSK)
+#define HWIO_REG_175929_INM(m) \
+	in_dword_masked(HWIO_REG_175929_ADDR, m)
+#define HWIO_REG_175929_OUT(v) \
+	out_dword(HWIO_REG_175929_ADDR, v)
+#define HWIO_REG_175929_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_175929_ADDR, m, v,\
+	HWIO_REG_175929_IN);
+#define HWIO_REG_175929_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_175929_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_11928_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000724)
+#define HWIO_REG_11928_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000724)
+#define HWIO_REG_11928_RMSK  0x1ffff
+#define HWIO_REG_11928_SHFT  0
+#define HWIO_REG_11928_IN  in_dword_masked(\
+	HWIO_REG_11928_ADDR, HWIO_REG_11928_RMSK)
+#define HWIO_REG_11928_INM(m) \
+	in_dword_masked(HWIO_REG_11928_ADDR, m)
+#define HWIO_REG_11928_OUT(v) \
+	out_dword(HWIO_REG_11928_ADDR, v)
+#define HWIO_REG_11928_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_11928_ADDR, m, v,\
+	HWIO_REG_11928_IN);
+#define HWIO_REG_11928_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_11928_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_772678_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000728)
+#define HWIO_REG_772678_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000728)
+#define HWIO_REG_772678_RMSK  0x1ffff
+#define HWIO_REG_772678_SHFT  0
+#define HWIO_REG_772678_IN  in_dword_masked(\
+	HWIO_REG_772678_ADDR, HWIO_REG_772678_RMSK)
+#define HWIO_REG_772678_INM(m) \
+	in_dword_masked(HWIO_REG_772678_ADDR, m)
+#define HWIO_REG_772678_OUT(v) \
+	out_dword(HWIO_REG_772678_ADDR, v)
+#define HWIO_REG_772678_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_772678_ADDR, m, v,\
+	HWIO_REG_772678_IN);
+#define HWIO_REG_772678_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_772678_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_603389_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x0000072c)
+#define HWIO_REG_603389_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000072c)
+#define HWIO_REG_603389_RMSK  0x1ffff
+#define HWIO_REG_603389_SHFT  0
+#define HWIO_REG_603389_IN  in_dword_masked(\
+	HWIO_REG_603389_ADDR, HWIO_REG_603389_RMSK)
+#define HWIO_REG_603389_INM(m) \
+	in_dword_masked(HWIO_REG_603389_ADDR, m)
+#define HWIO_REG_603389_OUT(v) \
+	out_dword(HWIO_REG_603389_ADDR, v)
+#define HWIO_REG_603389_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_603389_ADDR, m, v,\
+	HWIO_REG_603389_IN);
+#define HWIO_REG_603389_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_603389_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_989918_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000730)
+#define HWIO_REG_989918_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000730)
+#define HWIO_REG_989918_RMSK  0x1ffff
+#define HWIO_REG_989918_SHFT  0
+#define HWIO_REG_989918_IN  in_dword_masked(\
+	HWIO_REG_989918_ADDR, HWIO_REG_989918_RMSK)
+#define HWIO_REG_989918_INM(m) \
+	in_dword_masked(HWIO_REG_989918_ADDR, m)
+#define HWIO_REG_989918_OUT(v) \
+	out_dword(HWIO_REG_989918_ADDR, v)
+#define HWIO_REG_989918_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_989918_ADDR, m, v,\
+	HWIO_REG_989918_IN);
+#define HWIO_REG_989918_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_989918_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_5460_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000734)
+#define HWIO_REG_5460_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000734)
+#define HWIO_REG_5460_RMSK  0x1ffff
+#define HWIO_REG_5460_SHFT  0
+#define HWIO_REG_5460_IN  in_dword_masked(\
+	HWIO_REG_5460_ADDR, HWIO_REG_5460_RMSK)
+#define HWIO_REG_5460_INM(m) \
+	in_dword_masked(HWIO_REG_5460_ADDR, m)
+#define HWIO_REG_5460_OUT(v) \
+	out_dword(HWIO_REG_5460_ADDR, v)
+#define HWIO_REG_5460_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_5460_ADDR, m, v,\
+	HWIO_REG_5460_IN);
+#define HWIO_REG_5460_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_5460_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_734724_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000738)
+#define HWIO_REG_734724_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000738)
+#define HWIO_REG_734724_RMSK  0x1ffff
+#define HWIO_REG_734724_SHFT  0
+#define HWIO_REG_734724_IN  in_dword_masked(\
+	HWIO_REG_734724_ADDR, HWIO_REG_734724_RMSK)
+#define HWIO_REG_734724_INM(m) \
+	in_dword_masked(HWIO_REG_734724_ADDR, m)
+#define HWIO_REG_734724_OUT(v) \
+	out_dword(HWIO_REG_734724_ADDR, v)
+#define HWIO_REG_734724_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_734724_ADDR, m, v,\
+	HWIO_REG_734724_IN);
+#define HWIO_REG_734724_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_734724_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_451742_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x0000073c)
+#define HWIO_REG_451742_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000073c)
+#define HWIO_REG_451742_RMSK  0x1ffff
+#define HWIO_REG_451742_SHFT  0
+#define HWIO_REG_451742_IN  in_dword_masked(\
+	HWIO_REG_451742_ADDR, HWIO_REG_451742_RMSK)
+#define HWIO_REG_451742_INM(m) \
+	in_dword_masked(HWIO_REG_451742_ADDR, m)
+#define HWIO_REG_451742_OUT(v) \
+	out_dword(HWIO_REG_451742_ADDR, v)
+#define HWIO_REG_451742_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_451742_ADDR, m, v,\
+	HWIO_REG_451742_IN);
+#define HWIO_REG_451742_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_451742_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_475648_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000740)
+#define HWIO_REG_475648_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000740)
+#define HWIO_REG_475648_RMSK  0x1ffff
+#define HWIO_REG_475648_SHFT  0
+#define HWIO_REG_475648_IN  in_dword_masked(\
+	HWIO_REG_475648_ADDR, HWIO_REG_475648_RMSK)
+#define HWIO_REG_475648_INM(m) \
+	in_dword_masked(HWIO_REG_475648_ADDR, m)
+#define HWIO_REG_475648_OUT(v) \
+	out_dword(HWIO_REG_475648_ADDR, v)
+#define HWIO_REG_475648_OUTM(m, v) \
+	out_dword_masked_ns(HWIO_REG_475648_ADDR, m, v,\
+	HWIO_REG_475648_IN);
+#define HWIO_REG_475648_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_475648_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_284758_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000744)
+#define HWIO_REG_284758_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000744)
+#define HWIO_REG_284758_RMSK  0x1ffff
+#define HWIO_REG_284758_SHFT  0
+#define HWIO_REG_284758_IN  in_dword_masked(\
+	HWIO_REG_284758_ADDR, HWIO_REG_284758_RMSK)
+#define HWIO_REG_284758_INM(m) \
+	in_dword_masked(HWIO_REG_284758_ADDR, m)
+#define HWIO_REG_284758_OUT(v) \
+	out_dword(HWIO_REG_284758_ADDR, v)
+#define HWIO_REG_284758_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_284758_ADDR, m, v,\
+	HWIO_REG_284758_IN);
+#define HWIO_REG_284758_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_284758_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_523659_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000748)
+#define HWIO_REG_523659_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000748)
+#define HWIO_REG_523659_RMSK  0x1ffff
+#define HWIO_REG_523659_SHFT  0
+#define HWIO_REG_523659_IN  in_dword_masked(\
+	HWIO_REG_523659_ADDR, HWIO_REG_523659_RMSK)
+#define HWIO_REG_523659_INM(m) \
+	in_dword_masked(HWIO_REG_523659_ADDR, m)
+#define HWIO_REG_523659_OUT(v) \
+	out_dword(HWIO_REG_523659_ADDR, v)
+#define HWIO_REG_523659_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_523659_ADDR, m, v,\
+	HWIO_REG_523659_IN);
+#define HWIO_REG_523659_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_523659_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_815580_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x0000074c)
+#define HWIO_REG_815580_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000074c)
+#define HWIO_REG_815580_RMSK  0x1ffff
+#define HWIO_REG_815580_SHFT  0
+#define HWIO_REG_815580_IN  in_dword_masked(\
+	HWIO_REG_815580_ADDR, HWIO_REG_815580_RMSK)
+#define HWIO_REG_815580_INM(m) \
+	in_dword_masked(HWIO_REG_815580_ADDR, m)
+#define HWIO_REG_815580_OUT(v) \
+	out_dword(HWIO_REG_815580_ADDR, v)
+#define HWIO_REG_815580_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_815580_ADDR, m, v,\
+	HWIO_REG_815580_IN);
+#define HWIO_REG_815580_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_815580_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_546551_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000750)
+#define HWIO_REG_546551_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000750)
+#define HWIO_REG_546551_RMSK  0x1ffff
+#define HWIO_REG_546551_SHFT  0
+#define HWIO_REG_546551_IN  in_dword_masked(\
+	HWIO_REG_546551_ADDR, HWIO_REG_546551_RMSK)
+#define HWIO_REG_546551_INM(m) \
+	in_dword_masked(HWIO_REG_546551_ADDR, m)
+#define HWIO_REG_546551_OUT(v) \
+	out_dword(HWIO_REG_546551_ADDR, v)
+#define HWIO_REG_546551_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_546551_ADDR, m, v,\
+	HWIO_REG_546551_IN);
+#define HWIO_REG_546551_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_546551_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_769851_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000754)
+#define HWIO_REG_769851_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000754)
+#define HWIO_REG_769851_RMSK  0x1ffff
+#define HWIO_REG_769851_SHFT  0
+#define HWIO_REG_769851_IN  in_dword_masked(\
+	HWIO_REG_769851_ADDR, HWIO_REG_769851_RMSK)
+#define HWIO_REG_769851_INM(m) \
+	in_dword_masked(HWIO_REG_769851_ADDR, m)
+#define HWIO_REG_769851_OUT(v) \
+	out_dword(HWIO_REG_769851_ADDR, v)
+#define HWIO_REG_769851_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_769851_ADDR, m, v,\
+	HWIO_REG_769851_IN);
+#define HWIO_REG_769851_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_769851_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_205028_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000758)
+#define HWIO_REG_205028_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000758)
+#define HWIO_REG_205028_RMSK  0x1ffff
+#define HWIO_REG_205028_SHFT  0
+#define HWIO_REG_205028_IN  in_dword_masked(\
+	HWIO_REG_205028_ADDR, HWIO_REG_205028_RMSK)
+#define HWIO_REG_205028_INM(m) \
+	in_dword_masked(HWIO_REG_205028_ADDR, m)
+#define HWIO_REG_205028_OUT(v) \
+	out_dword(HWIO_REG_205028_ADDR, v)
+#define HWIO_REG_205028_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_205028_ADDR, m, v,\
+	HWIO_REG_205028_IN);
+#define HWIO_REG_205028_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_205028_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_206835_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x0000075c)
+#define HWIO_REG_206835_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000075c)
+#define HWIO_REG_206835_RMSK  0x1ffff
+#define HWIO_REG_206835_SHFT  0
+#define HWIO_REG_206835_IN  in_dword_masked(\
+	HWIO_REG_206835_ADDR, HWIO_REG_206835_RMSK)
+#define HWIO_REG_206835_INM(m) \
+	in_dword_masked(HWIO_REG_206835_ADDR, m)
+#define HWIO_REG_206835_OUT(v) \
+	out_dword(HWIO_REG_206835_ADDR, v)
+#define HWIO_REG_206835_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_206835_ADDR, m, v,\
+	HWIO_REG_206835_IN);
+#define HWIO_REG_206835_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_206835_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_582575_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000760)
+#define HWIO_REG_582575_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000760)
+#define HWIO_REG_582575_RMSK  0x1ffff
+#define HWIO_REG_582575_SHFT  0
+#define HWIO_REG_582575_IN  in_dword_masked(\
+	HWIO_REG_582575_ADDR, HWIO_REG_582575_RMSK)
+#define HWIO_REG_582575_INM(m) \
+	in_dword_masked(HWIO_REG_582575_ADDR, m)
+#define HWIO_REG_582575_OUT(v) \
+	out_dword(HWIO_REG_582575_ADDR, v)
+#define HWIO_REG_582575_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_582575_ADDR, m, v,\
+	HWIO_REG_582575_IN);
+#define HWIO_REG_582575_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_582575_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_120885_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000764)
+#define HWIO_REG_120885_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000764)
+#define HWIO_REG_120885_RMSK  0x1ffff
+#define HWIO_REG_120885_SHFT  0
+#define HWIO_REG_120885_IN  in_dword_masked(\
+	HWIO_REG_120885_ADDR, HWIO_REG_120885_RMSK)
+#define HWIO_REG_120885_INM(m) \
+	in_dword_masked(HWIO_REG_120885_ADDR, m)
+#define HWIO_REG_120885_OUT(v) \
+	out_dword(HWIO_REG_120885_ADDR, v)
+#define HWIO_REG_120885_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_120885_ADDR, m, v,\
+	HWIO_REG_120885_IN);
+#define HWIO_REG_120885_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_120885_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_496067_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000768)
+#define HWIO_REG_496067_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000768)
+#define HWIO_REG_496067_RMSK  0x1ffff
+#define HWIO_REG_496067_SHFT  0
+#define HWIO_REG_496067_IN  in_dword_masked(\
+	HWIO_REG_496067_ADDR, HWIO_REG_496067_RMSK)
+#define HWIO_REG_496067_INM(m) \
+	in_dword_masked(HWIO_REG_496067_ADDR, m)
+#define HWIO_REG_496067_OUT(v) \
+	out_dword(HWIO_REG_496067_ADDR, v)
+#define HWIO_REG_496067_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_496067_ADDR, m, v,\
+	HWIO_REG_496067_IN);
+#define HWIO_REG_496067_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_496067_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_472919_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x0000076c)
+#define HWIO_REG_472919_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000076c)
+#define HWIO_REG_472919_RMSK  0x1ffff
+#define HWIO_REG_472919_SHFT  0
+#define HWIO_REG_472919_IN  in_dword_masked(\
+	HWIO_REG_472919_ADDR, HWIO_REG_472919_RMSK)
+#define HWIO_REG_472919_INM(m) \
+	in_dword_masked(HWIO_REG_472919_ADDR, m)
+#define HWIO_REG_472919_OUT(v) \
+	out_dword(HWIO_REG_472919_ADDR, v)
+#define HWIO_REG_472919_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_472919_ADDR, m, v,\
+	HWIO_REG_472919_IN);
+#define HWIO_REG_472919_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_472919_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_486985_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000770)
+#define HWIO_REG_486985_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000770)
+#define HWIO_REG_486985_RMSK  0x1ffff
+#define HWIO_REG_486985_SHFT  0
+#define HWIO_REG_486985_IN  in_dword_masked(\
+	HWIO_REG_486985_ADDR, HWIO_REG_486985_RMSK)
+#define HWIO_REG_486985_INM(m) \
+	in_dword_masked(HWIO_REG_486985_ADDR, m)
+#define HWIO_REG_486985_OUT(v) \
+	out_dword(HWIO_REG_486985_ADDR, v)
+#define HWIO_REG_486985_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_486985_ADDR, m, v,\
+	HWIO_REG_486985_IN);
+#define HWIO_REG_486985_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_486985_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_964692_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000774)
+#define HWIO_REG_964692_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000774)
+#define HWIO_REG_964692_RMSK  0x1ffff
+#define HWIO_REG_964692_SHFT  0
+#define HWIO_REG_964692_IN  in_dword_masked(\
+	HWIO_REG_964692_ADDR, HWIO_REG_964692_RMSK)
+#define HWIO_REG_964692_INM(m) \
+	in_dword_masked(HWIO_REG_964692_ADDR, m)
+#define HWIO_REG_964692_OUT(v) \
+	out_dword(HWIO_REG_964692_ADDR, v)
+#define HWIO_REG_964692_OUTM(m, v)   out_dword_masked_ns(\
+	HWIO_REG_964692_ADDR, m, v,\
+	HWIO_REG_964692_IN);
+#define HWIO_REG_964692_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_964692_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_941116_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000778)
+#define HWIO_REG_941116_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000778)
+#define HWIO_REG_941116_RMSK  0x1ffff
+#define HWIO_REG_941116_SHFT  0
+#define HWIO_REG_941116_IN  in_dword_masked(\
+	HWIO_REG_941116_ADDR, HWIO_REG_941116_RMSK)
+#define HWIO_REG_941116_INM(m) \
+	in_dword_masked(HWIO_REG_941116_ADDR, m)
+#define HWIO_REG_941116_OUT(v) \
+	out_dword(HWIO_REG_941116_ADDR, v)
+#define HWIO_REG_941116_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_941116_ADDR, m, v,\
+	HWIO_REG_941116_IN);
+#define HWIO_REG_941116_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_941116_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_122567_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x0000077c)
+#define HWIO_REG_122567_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000077c)
+#define HWIO_REG_122567_RMSK  0x1ffff
+#define HWIO_REG_122567_SHFT  0
+#define HWIO_REG_122567_IN  in_dword_masked(\
+	HWIO_REG_122567_ADDR, HWIO_REG_122567_RMSK)
+#define HWIO_REG_122567_INM(m) \
+	in_dword_masked(HWIO_REG_122567_ADDR, m)
+#define HWIO_REG_122567_OUT(v) \
+	out_dword(HWIO_REG_122567_ADDR, v)
+#define HWIO_REG_122567_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_122567_ADDR, m, v,\
+	HWIO_REG_122567_IN);
+#define HWIO_REG_122567_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_122567_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_466192_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000780)
+#define HWIO_REG_466192_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000780)
+#define HWIO_REG_466192_RMSK  0x1ffff
+#define HWIO_REG_466192_SHFT  0
+#define HWIO_REG_466192_IN  in_dword_masked(\
+	HWIO_REG_466192_ADDR, HWIO_REG_466192_RMSK)
+#define HWIO_REG_466192_INM(m) \
+	in_dword_masked(HWIO_REG_466192_ADDR, m)
+#define HWIO_REG_466192_OUT(v) \
+	out_dword(HWIO_REG_466192_ADDR, v)
+#define HWIO_REG_466192_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_466192_ADDR, m, v,\
+	HWIO_REG_466192_IN);
+#define HWIO_REG_466192_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_466192_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_554890_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000784)
+#define HWIO_REG_554890_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000784)
+#define HWIO_REG_554890_RMSK  0x1ffff
+#define HWIO_REG_554890_SHFT  0
+#define HWIO_REG_554890_IN  in_dword_masked(\
+	HWIO_REG_554890_ADDR, HWIO_REG_554890_RMSK)
+#define HWIO_REG_554890_INM(m) \
+	in_dword_masked(HWIO_REG_554890_ADDR, m)
+#define HWIO_REG_554890_OUT(v)                          \
+	out_dword(HWIO_REG_554890_ADDR, v)
+#define HWIO_REG_554890_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_554890_ADDR, m, v,\
+	HWIO_REG_554890_IN);
+#define HWIO_REG_554890_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_554890_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_295616_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000788)
+#define HWIO_REG_295616_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000788)
+#define HWIO_REG_295616_RMSK  0x1ffff
+#define HWIO_REG_295616_SHFT  0
+#define HWIO_REG_295616_IN  in_dword_masked(\
+	HWIO_REG_295616_ADDR, HWIO_REG_295616_RMSK)
+#define HWIO_REG_295616_INM(m) \
+	in_dword_masked(HWIO_REG_295616_ADDR, m)
+#define HWIO_REG_295616_OUT(v) \
+	out_dword(HWIO_REG_295616_ADDR, v)
+#define HWIO_REG_295616_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_295616_ADDR, m, v,\
+	HWIO_REG_295616_IN);
+#define HWIO_REG_295616_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_295616_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_440836_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x0000078c)
+#define HWIO_REG_440836_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000078c)
+#define HWIO_REG_440836_RMSK  0x1ffff
+#define HWIO_REG_440836_SHFT  0
+#define HWIO_REG_440836_IN  in_dword_masked(\
+	HWIO_REG_440836_ADDR, HWIO_REG_440836_RMSK)
+#define HWIO_REG_440836_INM(m) \
+	in_dword_masked(HWIO_REG_440836_ADDR, m)
+#define HWIO_REG_440836_OUT(v) \
+	out_dword(HWIO_REG_440836_ADDR, v)
+#define HWIO_REG_440836_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_440836_ADDR, m, v,\
+	HWIO_REG_440836_IN);
+#define HWIO_REG_440836_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_440836_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_741154_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000790)
+#define HWIO_REG_741154_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000790)
+#define HWIO_REG_741154_RMSK  0x1ffff
+#define HWIO_REG_741154_SHFT  0
+#define HWIO_REG_741154_IN  in_dword_masked(\
+	HWIO_REG_741154_ADDR,\
+	HWIO_REG_741154_RMSK)
+#define HWIO_REG_741154_INM(m) \
+	in_dword_masked(HWIO_REG_741154_ADDR, m)
+#define HWIO_REG_741154_OUT(v) \
+	out_dword(HWIO_REG_741154_ADDR, v)
+#define HWIO_REG_741154_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_741154_ADDR, m, v,\
+	HWIO_REG_741154_IN);
+#define HWIO_REG_741154_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_741154_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_753139_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000794)
+#define HWIO_REG_753139_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000794)
+#define HWIO_REG_753139_RMSK  0x1ffff
+#define HWIO_REG_753139_SHFT  0
+#define HWIO_REG_753139_IN  in_dword_masked(\
+	HWIO_REG_753139_ADDR,\
+	HWIO_REG_753139_RMSK)
+#define HWIO_REG_753139_INM(m) \
+	in_dword_masked(HWIO_REG_753139_ADDR, m)
+#define HWIO_REG_753139_OUT(v) \
+	out_dword(HWIO_REG_753139_ADDR, v)
+#define HWIO_REG_753139_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_753139_ADDR, m, v,\
+	HWIO_REG_753139_IN);
+#define HWIO_REG_753139_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_753139_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_409994_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x00000798)
+#define HWIO_REG_409994_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000798)
+#define HWIO_REG_409994_RMSK  0x1ffff
+#define HWIO_REG_409994_SHFT  0
+#define HWIO_REG_409994_IN  in_dword_masked(\
+	HWIO_REG_409994_ADDR,\
+	HWIO_REG_409994_RMSK)
+#define HWIO_REG_409994_INM(m) \
+	in_dword_masked(HWIO_REG_409994_ADDR, m)
+#define HWIO_REG_409994_OUT(v) \
+	out_dword(HWIO_REG_409994_ADDR, v)
+#define HWIO_REG_409994_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_409994_ADDR, m, v,\
+	HWIO_REG_409994_IN);
+#define HWIO_REG_409994_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_409994_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_492611_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x0000079c)
+#define HWIO_REG_492611_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000079c)
+#define HWIO_REG_492611_RMSK  0x1ffff
+#define HWIO_REG_492611_SHFT  0
+#define HWIO_REG_492611_IN  in_dword_masked(\
+	HWIO_REG_492611_ADDR,\
+	HWIO_REG_492611_RMSK)
+#define HWIO_REG_492611_INM(m) \
+	in_dword_masked(HWIO_REG_492611_ADDR, m)
+#define HWIO_REG_492611_OUT(v) \
+	out_dword(HWIO_REG_492611_ADDR, v)
+#define HWIO_REG_492611_OUTM(m, v) \
+	out_dword_masked_ns(HWIO_REG_492611_ADDR, m, v,\
+	HWIO_REG_492611_IN);
+#define HWIO_REG_492611_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_492611_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_91427_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000007a0)
+#define HWIO_REG_91427_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000007a0)
+#define HWIO_REG_91427_RMSK  0x1ffff
+#define HWIO_REG_91427_SHFT  0
+#define HWIO_REG_91427_IN  in_dword_masked(\
+	HWIO_REG_91427_ADDR,\
+	HWIO_REG_91427_RMSK)
+#define HWIO_REG_91427_INM(m) \
+	in_dword_masked(HWIO_REG_91427_ADDR, m)
+#define HWIO_REG_91427_OUT(v) \
+	out_dword(HWIO_REG_91427_ADDR, v)
+#define HWIO_REG_91427_OUTM(m, v) \
+	out_dword_masked_ns(HWIO_REG_91427_ADDR, m, v,\
+	HWIO_REG_91427_IN);
+#define HWIO_REG_91427_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_91427_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_617696_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000007a4)
+#define HWIO_REG_617696_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000007a4)
+#define HWIO_REG_617696_RMSK  0x1ffff
+#define HWIO_REG_617696_SHFT  0
+#define HWIO_REG_617696_IN  in_dword_masked(\
+	HWIO_REG_617696_ADDR,\
+	HWIO_REG_617696_RMSK)
+#define HWIO_REG_617696_INM(m) \
+	in_dword_masked(HWIO_REG_617696_ADDR, m)
+#define HWIO_REG_617696_OUT(v) \
+	out_dword(HWIO_REG_617696_ADDR, v)
+#define HWIO_REG_617696_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_617696_ADDR, m, v,\
+	HWIO_REG_617696_IN);
+#define HWIO_REG_617696_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_617696_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_459602_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000007a8)
+#define HWIO_REG_459602_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000007a8)
+#define HWIO_REG_459602_RMSK  0x1ffff
+#define HWIO_REG_459602_SHFT  0
+#define HWIO_REG_459602_IN  in_dword_masked(\
+	HWIO_REG_459602_ADDR,\
+	HWIO_REG_459602_RMSK)
+#define HWIO_REG_459602_INM(m) \
+	in_dword_masked(HWIO_REG_459602_ADDR, m)
+#define HWIO_REG_459602_OUT(v) \
+	out_dword(HWIO_REG_459602_ADDR, v)
+#define HWIO_REG_459602_OUTM(m, v) \
+	out_dword_masked_ns(HWIO_REG_459602_ADDR, m, v,\
+	HWIO_REG_459602_IN);
+#define HWIO_REG_459602_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_459602_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_758_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000007ac)
+#define HWIO_REG_758_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000007ac)
+#define HWIO_REG_758_RMSK  0x1ffff
+#define HWIO_REG_758_SHFT  0
+#define HWIO_REG_758_IN  in_dword_masked(\
+	HWIO_REG_758_ADDR,\
+	HWIO_REG_758_RMSK)
+#define HWIO_REG_758_INM(m) \
+	in_dword_masked(HWIO_REG_758_ADDR, m)
+#define HWIO_REG_758_OUT(v) \
+	out_dword(HWIO_REG_758_ADDR, v)
+#define HWIO_REG_758_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_758_ADDR, m, v,\
+	HWIO_REG_758_IN);
+#define HWIO_REG_758_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_758_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_710606_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000007b0)
+#define HWIO_REG_710606_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000007b0)
+#define HWIO_REG_710606_RMSK  0x1ffff
+#define HWIO_REG_710606_SHFT  0
+#define HWIO_REG_710606_IN  in_dword_masked(\
+	HWIO_REG_710606_ADDR,\
+	HWIO_REG_710606_RMSK)
+#define HWIO_REG_710606_INM(m) \
+	in_dword_masked(HWIO_REG_710606_ADDR, m)
+#define HWIO_REG_710606_OUT(v) \
+	out_dword(HWIO_REG_710606_ADDR, v)
+#define HWIO_REG_710606_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_710606_ADDR, m, v,\
+	HWIO_REG_710606_IN);
+#define HWIO_REG_710606_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_710606_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_122975_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000007b4)
+#define HWIO_REG_122975_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000007b4)
+#define HWIO_REG_122975_RMSK  0x1ffff
+#define HWIO_REG_122975_SHFT  0
+#define HWIO_REG_122975_IN  in_dword_masked(\
+	HWIO_REG_122975_ADDR,\
+	HWIO_REG_122975_RMSK)
+#define HWIO_REG_122975_INM(m)\
+	in_dword_masked(HWIO_REG_122975_ADDR, m)
+#define HWIO_REG_122975_OUT(v)\
+	out_dword(HWIO_REG_122975_ADDR, v)
+#define HWIO_REG_122975_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_122975_ADDR, m, v,\
+	HWIO_REG_122975_IN);
+#define HWIO_REG_122975_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_122975_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_860205_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000007b8)
+#define HWIO_REG_860205_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000007b8)
+#define HWIO_REG_860205_RMSK  0x1ffff
+#define HWIO_REG_860205_SHFT  0
+#define HWIO_REG_860205_IN  in_dword_masked(\
+	HWIO_REG_860205_ADDR,\
+	HWIO_REG_860205_RMSK)
+#define HWIO_REG_860205_INM(m) \
+	in_dword_masked(HWIO_REG_860205_ADDR, m)
+#define HWIO_REG_860205_OUT(v) \
+	out_dword(HWIO_REG_860205_ADDR, v)
+#define HWIO_REG_860205_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_860205_ADDR, m, v,\
+	HWIO_REG_860205_IN);
+#define HWIO_REG_860205_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_860205_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_366154_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000007bc)
+#define HWIO_REG_366154_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000007bc)
+#define HWIO_REG_366154_RMSK  0x1ffff
+#define HWIO_REG_366154_SHFT  0
+#define HWIO_REG_366154_IN  in_dword_masked(\
+	HWIO_REG_366154_ADDR,\
+	HWIO_REG_366154_RMSK)
+#define HWIO_REG_366154_INM(m) \
+	in_dword_masked(HWIO_REG_366154_ADDR, m)
+#define HWIO_REG_366154_OUT(v) \
+	out_dword(HWIO_REG_366154_ADDR, v)
+#define HWIO_REG_366154_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_366154_ADDR, m, v,\
+	HWIO_REG_366154_IN);
+#define HWIO_REG_366154_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_366154_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_632247_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000007c0)
+#define HWIO_REG_632247_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000007c0)
+#define HWIO_REG_632247_RMSK  0x1ffff
+#define HWIO_REG_632247_SHFT  0
+#define HWIO_REG_632247_IN  in_dword_masked(\
+	HWIO_REG_632247_ADDR,\
+	HWIO_REG_632247_RMSK)
+#define HWIO_REG_632247_INM(m) \
+	in_dword_masked(HWIO_REG_632247_ADDR, m)
+#define HWIO_REG_632247_OUT(v) \
+	out_dword(HWIO_REG_632247_ADDR, v)
+#define HWIO_REG_632247_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_632247_ADDR, m, v,\
+	HWIO_REG_632247_IN);
+#define HWIO_REG_632247_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_632247_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_709312_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000007c4)
+#define HWIO_REG_709312_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000007c4)
+#define HWIO_REG_709312_RMSK  0x1ffff
+#define HWIO_REG_709312_SHFT  0
+#define HWIO_REG_709312_IN  in_dword_masked(\
+	HWIO_REG_709312_ADDR,\
+	HWIO_REG_709312_RMSK)
+#define HWIO_REG_709312_INM(m) \
+	in_dword_masked(HWIO_REG_709312_ADDR, m)
+#define HWIO_REG_709312_OUT(v) \
+	out_dword(HWIO_REG_709312_ADDR, v)
+#define HWIO_REG_709312_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_709312_ADDR, m, v,\
+	HWIO_REG_709312_IN);
+#define HWIO_REG_709312_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_709312_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_891367_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000007c8)
+#define HWIO_REG_891367_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000007c8)
+#define HWIO_REG_891367_RMSK  0x1ffff
+#define HWIO_REG_891367_SHFT  0
+#define HWIO_REG_891367_IN  in_dword_masked(\
+	HWIO_REG_891367_ADDR,\
+	HWIO_REG_891367_RMSK)
+#define HWIO_REG_891367_INM(m) \
+	in_dword_masked(HWIO_REG_891367_ADDR, m)
+#define HWIO_REG_891367_OUT(v) \
+	out_dword(HWIO_REG_891367_ADDR, v)
+#define HWIO_REG_891367_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_891367_ADDR, m, v,\
+	HWIO_REG_891367_IN);
+#define HWIO_REG_891367_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_891367_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_628746_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000007cc)
+#define HWIO_REG_628746_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000007cc)
+#define HWIO_REG_628746_RMSK  0x1ffff
+#define HWIO_REG_628746_SHFT  0
+#define HWIO_REG_628746_IN  in_dword_masked(\
+	HWIO_REG_628746_ADDR,\
+	HWIO_REG_628746_RMSK)
+#define HWIO_REG_628746_INM(m) \
+	in_dword_masked(HWIO_REG_628746_ADDR, m)
+#define HWIO_REG_628746_OUT(v) \
+	out_dword(HWIO_REG_628746_ADDR, v)
+#define HWIO_REG_628746_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_628746_ADDR, m, v,\
+	HWIO_REG_628746_IN);
+#define HWIO_REG_628746_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_628746_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_821010_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000007d0)
+#define HWIO_REG_821010_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000007d0)
+#define HWIO_REG_821010_RMSK  0x1ffff
+#define HWIO_REG_821010_SHFT  0
+#define HWIO_REG_821010_IN  in_dword_masked(\
+	HWIO_REG_821010_ADDR,\
+	HWIO_REG_821010_RMSK)
+#define HWIO_REG_821010_INM(m) \
+	in_dword_masked(HWIO_REG_821010_ADDR, m)
+#define HWIO_REG_821010_OUT(v) \
+	out_dword(HWIO_REG_821010_ADDR, v)
+#define HWIO_REG_821010_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_821010_ADDR, m, v,\
+	HWIO_REG_821010_IN);
+#define HWIO_REG_821010_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_821010_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_902098_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000007d4)
+#define HWIO_REG_902098_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000007d4)
+#define HWIO_REG_902098_RMSK  0x1ffff
+#define HWIO_REG_902098_SHFT  0
+#define HWIO_REG_902098_IN   in_dword_masked(\
+	HWIO_REG_902098_ADDR,\
+	HWIO_REG_902098_RMSK)
+#define HWIO_REG_902098_INM(m) \
+	in_dword_masked(HWIO_REG_902098_ADDR, m)
+#define HWIO_REG_902098_OUT(v) \
+	out_dword(HWIO_REG_902098_ADDR, v)
+#define HWIO_REG_902098_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_902098_ADDR, m, v,\
+	HWIO_REG_902098_IN);
+#define HWIO_REG_902098_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_902098_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_939091_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000007d8)
+#define HWIO_REG_939091_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000007d8)
+#define HWIO_REG_939091_RMSK  0x1ffff
+#define HWIO_REG_939091_SHFT  0
+#define HWIO_REG_939091_IN  in_dword_masked(\
+	HWIO_REG_939091_ADDR,\
+	HWIO_REG_939091_RMSK)
+#define HWIO_REG_939091_INM(m) \
+	in_dword_masked(HWIO_REG_939091_ADDR, m)
+#define HWIO_REG_939091_OUT(v) \
+	out_dword(HWIO_REG_939091_ADDR, v)
+#define HWIO_REG_939091_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_939091_ADDR, m, v,\
+	HWIO_REG_939091_IN);
+#define HWIO_REG_939091_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_939091_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_261074_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000007dc)
+#define HWIO_REG_261074_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000007dc)
+#define HWIO_REG_261074_RMSK  0x1ffff
+#define HWIO_REG_261074_SHFT  0
+#define HWIO_REG_261074_IN  in_dword_masked(\
+	HWIO_REG_261074_ADDR,\
+	HWIO_REG_261074_RMSK)
+#define HWIO_REG_261074_INM(m) \
+	in_dword_masked(HWIO_REG_261074_ADDR, m)
+#define HWIO_REG_261074_OUT(v) \
+	out_dword(HWIO_REG_261074_ADDR, v)
+#define HWIO_REG_261074_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_261074_ADDR, m, v,\
+	HWIO_REG_261074_IN);
+#define HWIO_REG_261074_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_261074_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_157718_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000007e0)
+#define HWIO_REG_157718_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000007e0)
+#define HWIO_REG_157718_RMSK  0x1ffff
+#define HWIO_REG_157718_SHFT  0
+#define HWIO_REG_157718_IN  in_dword_masked(\
+	HWIO_REG_157718_ADDR,\
+	HWIO_REG_157718_RMSK)
+#define HWIO_REG_157718_INM(m) \
+	in_dword_masked(HWIO_REG_157718_ADDR, m)
+#define HWIO_REG_157718_OUT(v) \
+	out_dword(HWIO_REG_157718_ADDR, v)
+#define HWIO_REG_157718_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_157718_ADDR, m, v,\
+	HWIO_REG_157718_IN);
+#define HWIO_REG_5552391_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_5552391_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_148889_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000007e8)
+#define HWIO_REG_148889_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000007e8)
+#define HWIO_REG_148889_RMSK  0x1ffff
+#define HWIO_REG_148889_SHFT  0
+#define HWIO_REG_148889_IN  in_dword_masked(\
+	HWIO_REG_148889_ADDR,\
+	HWIO_REG_148889_RMSK)
+#define HWIO_REG_148889_INM(m) \
+	in_dword_masked(HWIO_REG_148889_ADDR, m)
+#define HWIO_REG_148889_OUT(v) \
+	out_dword(HWIO_REG_148889_ADDR, v)
+#define HWIO_REG_148889_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_148889_ADDR, m, v,\
+	HWIO_REG_148889_IN);
+#define HWIO_REG_148889_BASE_ADDR_BMSK 0x1ffff
+#define HWIO_REG_148889_BASE_ADDR_SHFT 0
+
+#define HWIO_REG_396380_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000007ec)
+#define HWIO_REG_396380_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000007ec)
+#define HWIO_REG_396380_RMSK  0x1ffff
+#define HWIO_REG_396380_SHFT  0
+#define HWIO_REG_396380_IN  in_dword_masked(\
+	HWIO_REG_396380_ADDR,\
+	HWIO_REG_396380_RMSK)
+#define HWIO_REG_396380_INM(m) \
+	in_dword_masked(HWIO_REG_396380_ADDR, m)
+#define HWIO_REG_396380_OUT(v) \
+	out_dword(HWIO_REG_396380_ADDR, v)
+#define HWIO_REG_396380_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_396380_ADDR, m, v,\
+	HWIO_REG_396380_IN);
+#define HWIO_REG_396380_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_396380_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_351005_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000007f0)
+#define HWIO_REG_351005_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000007f0)
+#define HWIO_REG_351005_RMSK  0x1ffff
+#define HWIO_REG_351005_SHFT  0
+#define HWIO_REG_351005_IN  in_dword_masked(\
+	HWIO_REG_351005_ADDR,\
+	HWIO_REG_351005_RMSK)
+#define HWIO_REG_351005_INM(m) \
+	in_dword_masked(HWIO_REG_351005_ADDR, m)
+#define HWIO_REG_351005_OUT(v) \
+	out_dword(HWIO_REG_351005_ADDR, v)
+#define HWIO_REG_351005_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_351005_ADDR, m, v,\
+	HWIO_REG_351005_IN);
+#define HWIO_REG_351005_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_351005_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_863263_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000007f4)
+#define HWIO_REG_863263_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000007f4)
+#define HWIO_REG_863263_RMSK  0x1ffff
+#define HWIO_REG_863263_SHFT  0
+#define HWIO_REG_863263_IN  in_dword_masked(\
+	HWIO_REG_863263_ADDR,\
+	HWIO_REG_863263_RMSK)
+#define HWIO_REG_863263_INM(m) \
+	in_dword_masked(HWIO_REG_863263_ADDR, m)
+#define HWIO_REG_863263_OUT(v) \
+	out_dword(HWIO_REG_863263_ADDR, v)
+#define HWIO_REG_863263_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_863263_ADDR, m, v,\
+	HWIO_REG_863263_IN);
+#define HWIO_REG_863263_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_863263_BASE_ADDR_SHFT   0
+
+#define HWIO_REG_135009_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000007f8)
+#define HWIO_REG_135009_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000007f8)
+#define HWIO_REG_135009_RMSK  0x1ffff
+#define HWIO_REG_135009_SHFT  0
+#define HWIO_REG_135009_IN  in_dword_masked(\
+	HWIO_REG_135009_ADDR,\
+	HWIO_REG_135009_RMSK)
+#define HWIO_REG_135009_INM(m) \
+	in_dword_masked(HWIO_REG_135009_ADDR, m)
+#define HWIO_REG_135009_OUT(v) \
+	out_dword(HWIO_REG_135009_ADDR, v)
+#define HWIO_REG_135009_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_135009_ADDR, m, v,\
+	HWIO_REG_135009_IN);
+#define HWIO_REG_135009_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_135009_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_923883_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x000007fc)
+#define HWIO_REG_923883_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000007fc)
+#define HWIO_REG_923883_RMSK  0x1ffff
+#define HWIO_REG_923883_SHFT  0
+#define HWIO_REG_923883_IN  in_dword_masked(\
+	HWIO_REG_923883_ADDR,\
+	HWIO_REG_923883_RMSK)
+#define HWIO_REG_923883_INM(m) \
+	in_dword_masked(HWIO_REG_923883_ADDR, m)
+#define HWIO_REG_923883_OUT(v) \
+	out_dword(HWIO_REG_923883_ADDR, v)
+#define HWIO_REG_923883_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_923883_ADDR, m, v,\
+	HWIO_REG_923883_IN);
+#define HWIO_REG_923883_BASE_ADDR_BMSK  0x1ffff
+#define HWIO_REG_923883_BASE_ADDR_SHFT  0
+
+#define HWIO_REG_934655_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00000818)
+#define HWIO_REG_934655_PHYS (VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000818)
+#define HWIO_REG_934655_RMSK  0x1fff
+#define HWIO_REG_934655_SHFT  0
+#define HWIO_REG_934655_IN \
+	in_dword_masked(HWIO_REG_934655_ADDR, HWIO_REG_934655_RMSK)
+#define HWIO_REG_934655_INM(m) \
+	in_dword_masked(HWIO_REG_934655_ADDR, m)
+#define HWIO_REG_934655_OUT(v) \
+	out_dword(HWIO_REG_934655_ADDR, v)
+#define HWIO_REG_934655_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_934655_ADDR, m, v, HWIO_REG_934655_IN);
+#define HWIO_REG_934655_FRAME_WIDTH_BMSK  0x1fff
+#define HWIO_REG_934655_FRAME_WIDTH_SHFT  0
+
+#define HWIO_REG_179070_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000081c)
+#define HWIO_REG_179070_PHYS (VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000081c)
+#define HWIO_REG_179070_RMSK  0x1fff
+#define HWIO_REG_179070_SHFT  0
+#define HWIO_REG_179070_IN  in_dword_masked(\
+	HWIO_REG_179070_ADDR, HWIO_REG_179070_RMSK)
+#define HWIO_REG_179070_INM(m) \
+	in_dword_masked(HWIO_REG_179070_ADDR, m)
+#define HWIO_REG_179070_OUT(v) \
+	out_dword(HWIO_REG_179070_ADDR, v)
+#define HWIO_REG_179070_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_179070_ADDR, m, v, HWIO_REG_179070_IN);
+#define HWIO_REG_179070_FRAME_HEIGHT_BMSK  0x1fff
+#define HWIO_REG_179070_FRAME_HEIGHT_SHFT  0
+
+#define HWIO_REG_63643_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00000830)
+#define HWIO_REG_63643_PHYS (VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000830)
+#define HWIO_REG_63643_RMSK  0xff3f
+#define HWIO_REG_63643_SHFT  0
+#define HWIO_REG_63643_IN  in_dword_masked(\
+	HWIO_REG_63643_ADDR, HWIO_REG_63643_RMSK)
+#define HWIO_REG_63643_INM(m) \
+	in_dword_masked(HWIO_REG_63643_ADDR, m)
+#define HWIO_REG_63643_OUT(v) \
+	out_dword(HWIO_REG_63643_ADDR, v)
+#define HWIO_REG_63643_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_63643_ADDR, m, v, HWIO_REG_63643_IN);
+#define HWIO_REG_63643_LEVEL_BMSK    0xff00
+#define HWIO_REG_63643_LEVEL_SHFT    0x8
+#define HWIO_REG_63643_PROFILE_BMSK  0x3f
+#define HWIO_REG_63643_PROFILE_SHFT  0
+
+#define HWIO_REG_786024_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000083c)
+#define HWIO_REG_786024_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000083c)
+#define HWIO_REG_786024_RMSK  0x1
+#define HWIO_REG_786024_SHFT  0
+#define HWIO_REG_786024_IN  in_dword_masked(\
+	HWIO_REG_786024_ADDR, HWIO_REG_786024_RMSK)
+#define HWIO_REG_786024_INM(m) \
+	in_dword_masked(HWIO_REG_786024_ADDR, m)
+#define HWIO_REG_786024_OUT(v) \
+	out_dword(HWIO_REG_786024_ADDR, v)
+#define HWIO_REG_786024_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_786024_ADDR, m, v, HWIO_REG_786024_IN);
+#define HWIO_REG_786024_FIELD_BMSK  0x1
+#define HWIO_REG_786024_FIELD_SHFT  0
+
+#define HWIO_REG_152500_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00000848)
+#define HWIO_REG_152500_PHYS (VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000848)
+#define HWIO_REG_152500_RMSK  0x3
+#define HWIO_REG_152500_SHFT  0
+#define HWIO_REG_152500_IN  in_dword_masked(\
+	HWIO_REG_152500_ADDR, HWIO_REG_152500_RMSK)
+#define HWIO_REG_152500_INM(m) \
+	in_dword_masked(HWIO_REG_152500_ADDR, m)
+#define HWIO_REG_152500_OUT(v) \
+	out_dword(HWIO_REG_152500_ADDR, v)
+#define HWIO_REG_152500_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_152500_ADDR, m, v, HWIO_REG_152500_IN);
+#define HWIO_REG_152500_LF_CONTROL_BMSK  0x3
+#define HWIO_REG_152500_LF_CONTROL_SHFT  0
+
+#define HWIO_REG_266285_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000084c)
+#define HWIO_REG_266285_PHYS (VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000084c)
+#define HWIO_REG_266285_RMSK  0x1f
+#define HWIO_REG_266285_SHFT  0
+#define HWIO_REG_266285_IN  in_dword_masked(\
+	HWIO_REG_266285_ADDR, HWIO_REG_266285_RMSK)
+#define HWIO_REG_266285_INM(m) \
+	in_dword_masked(HWIO_REG_266285_ADDR, m)
+#define HWIO_REG_266285_OUT(v) \
+	out_dword(HWIO_REG_266285_ADDR, v)
+#define HWIO_REG_266285_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_266285_ADDR, m, v, HWIO_REG_266285_IN);
+#define HWIO_REG_266285_LF_ALPHAS_OFF_BMSK  0x1f
+#define HWIO_REG_266285_LF_ALPHAS_OFF_SHFT  0
+
+#define HWIO_REG_964731_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00000850)
+#define HWIO_REG_964731_PHYS (VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000850)
+#define HWIO_REG_964731_RMSK  0x1f
+#define HWIO_REG_964731_SHFT  0
+#define HWIO_REG_964731_IN  in_dword_masked(\
+	HWIO_REG_964731_ADDR, HWIO_REG_964731_RMSK)
+#define HWIO_REG_964731_INM(m) \
+	in_dword_masked(HWIO_REG_964731_ADDR, m)
+#define HWIO_REG_964731_OUT(v) \
+	out_dword(HWIO_REG_964731_ADDR, v)
+#define HWIO_REG_964731_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_964731_ADDR, m, v, HWIO_REG_964731_IN);
+#define HWIO_REG_964731_LF_BETA_OFF_BMSK  0x1f
+#define HWIO_REG_964731_LF_BETA_OFF_SHFT  0
+
+#define HWIO_REG_919924_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00000c30)
+#define HWIO_REG_919924_PHYS (VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000c30)
+#define HWIO_REG_919924_RMSK  0xffffffff
+#define HWIO_REG_919924_SHFT  0
+#define HWIO_REG_919924_IN  in_dword_masked(\
+	HWIO_REG_919924_ADDR, HWIO_REG_919924_RMSK)
+#define HWIO_REG_919924_INM(m) \
+	in_dword_masked(HWIO_REG_919924_ADDR, m)
+#define HWIO_REG_919924_OUT(v) \
+	out_dword(HWIO_REG_919924_ADDR, v)
+#define HWIO_REG_919924_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_919924_ADDR, m, v, HWIO_REG_919924_IN);
+#define HWIO_REG_919924_VIDC_QP_OFFSET_BMSK  0xffffffff
+#define HWIO_REG_919924_VIDC_QP_OFFSET_SHFT  0
+
+#define HWIO_REG_143629_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00000c34)
+#define HWIO_REG_143629_PHYS (VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00000c34)
+#define HWIO_REG_143629_RMSK  0x1
+#define HWIO_REG_143629_SHFT  0
+#define HWIO_REG_143629_IN  in_dword_masked(\
+	HWIO_REG_143629_ADDR, HWIO_REG_143629_RMSK)
+#define HWIO_REG_143629_INM(m) \
+	in_dword_masked(HWIO_REG_143629_ADDR, m)
+#define HWIO_REG_143629_OUT(v) \
+	out_dword(HWIO_REG_143629_ADDR, v)
+#define HWIO_REG_143629_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_143629_ADDR, m, v, HWIO_REG_143629_IN);
+#define HWIO_REG_143629_REG_143629_BMSK  0x1
+#define HWIO_REG_143629_REG_143629_SHFT  0
+
+#define HWIO_REG_607589_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00002000)
+#define HWIO_REG_607589_PHYS (VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00002000)
+#define HWIO_REG_607589_RMSK  0xffffffff
+#define HWIO_REG_607589_SHFT  0
+#define HWIO_REG_607589_IN  in_dword_masked(\
+	HWIO_REG_607589_ADDR, HWIO_REG_607589_RMSK)
+#define HWIO_REG_607589_INM(m) \
+	in_dword_masked(HWIO_REG_607589_ADDR, m)
+#define HWIO_REG_607589_OUT(v) \
+	out_dword(HWIO_REG_607589_ADDR, v)
+#define HWIO_REG_607589_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_607589_ADDR, m, v, HWIO_REG_607589_IN);
+#define HWIO_REG_607589_RTN_CHID_BMSK  0xffffffff
+#define HWIO_REG_607589_RTN_CHID_SHFT  0
+
+#define HWIO_REG_845544_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00002004)
+#define HWIO_REG_845544_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00002004)
+#define HWIO_REG_845544_RMSK  0xffffffff
+#define HWIO_REG_845544_SHFT  0
+#define HWIO_REG_845544_IN  in_dword_masked(\
+	HWIO_REG_845544_ADDR, HWIO_REG_845544_RMSK)
+#define HWIO_REG_845544_INM(m) \
+	in_dword_masked(HWIO_REG_845544_ADDR, m)
+#define HWIO_REG_845544_OUT(v) \
+	out_dword(HWIO_REG_845544_ADDR, v)
+#define HWIO_REG_845544_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_845544_ADDR, m, v, HWIO_REG_845544_IN);
+#define HWIO_REG_845544_REG_845544_BMSK  0xffffffff
+#define HWIO_REG_845544_REG_845544_SHFT  0
+
+#define HWIO_REG_859906_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00002008)
+#define HWIO_REG_859906_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00002008)
+#define HWIO_REG_859906_RMSK  0xffffffff
+#define HWIO_REG_859906_SHFT  0
+#define HWIO_REG_859906_IN  in_dword_masked(\
+	HWIO_REG_859906_ADDR, HWIO_REG_859906_RMSK)
+#define HWIO_REG_859906_INM(m) \
+	in_dword_masked(HWIO_REG_859906_ADDR, m)
+#define HWIO_REG_859906_OUT(v) \
+	out_dword(HWIO_REG_859906_ADDR, v)
+#define HWIO_REG_859906_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_859906_ADDR, m, v, HWIO_REG_859906_IN);
+#define HWIO_REG_859906_REG_859906_BMSK  0xffffffff
+#define HWIO_REG_859906_REG_859906_SHFT  0
+
+#define HWIO_REG_490078_ADDR  (VIDC_BLACKBIRD_REG_BASE + 0x0000200c)
+#define HWIO_REG_490078_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000200c)
+#define HWIO_REG_490078_RMSK  0xffffffff
+#define HWIO_REG_490078_SHFT  0
+#define HWIO_REG_490078_IN  in_dword_masked(\
+	HWIO_REG_490078_ADDR, HWIO_REG_490078_RMSK)
+#define HWIO_REG_490078_INM(m) \
+	in_dword_masked(HWIO_REG_490078_ADDR, m)
+#define HWIO_REG_490078_OUT(v) \
+	out_dword(HWIO_REG_490078_ADDR, v)
+#define HWIO_REG_490078_OUTM(m, v) \
+	out_dword_masked_ns(HWIO_REG_490078_ADDR, m, v,\
+	HWIO_REG_490078_IN);
+#define HWIO_REG_490078_REG_490078_BMSK  0xffffffff
+#define HWIO_REG_490078_REG_490078_SHFT  0
+
+#define HWIO_REG_640904_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00002010)
+#define HWIO_REG_640904_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00002010)
+#define HWIO_REG_640904_RMSK  0xffffffff
+#define HWIO_REG_640904_SHFT  0
+#define HWIO_REG_640904_IN  in_dword_masked(\
+	HWIO_REG_640904_ADDR, HWIO_REG_640904_RMSK)
+#define HWIO_REG_640904_INM(m) \
+	in_dword_masked(HWIO_REG_640904_ADDR, m)
+#define HWIO_REG_640904_OUT(v) \
+	out_dword(HWIO_REG_640904_ADDR, v)
+#define HWIO_REG_640904_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_640904_ADDR, m, v, HWIO_REG_640904_IN);
+#define HWIO_REG_640904_REG_640904_BMSK  0xffffffff
+#define HWIO_REG_640904_REG_640904_SHFT  0
+
+#define HWIO_REG_60114_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00002014)
+#define HWIO_REG_60114_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00002014)
+#define HWIO_REG_60114_RMSK  0xffffffff
+#define HWIO_REG_60114_SHFT  0
+#define HWIO_REG_60114_IN  in_dword_masked(\
+	HWIO_REG_60114_ADDR, HWIO_REG_60114_RMSK)
+#define HWIO_REG_60114_INM(m) \
+	in_dword_masked(HWIO_REG_60114_ADDR, m)
+#define HWIO_REG_60114_OUT(v) \
+	out_dword(HWIO_REG_60114_ADDR, v)
+#define HWIO_REG_60114_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_60114_ADDR, m, v, HWIO_REG_60114_IN);
+#define HWIO_REG_60114_REG_60114_BMSK  0xffffffff
+#define HWIO_REG_60114_REG_60114_SHFT  0
+
+#define HWIO_REG_489688_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00002018)
+#define HWIO_REG_489688_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00002018)
+#define HWIO_REG_489688_RMSK  0xffffffff
+#define HWIO_REG_489688_SHFT  0
+#define HWIO_REG_489688_IN  in_dword_masked(\
+	HWIO_REG_489688_ADDR, HWIO_REG_489688_RMSK)
+#define HWIO_REG_489688_INM(m) \
+	in_dword_masked(HWIO_REG_489688_ADDR, m)
+#define HWIO_REG_489688_OUT(v) \
+	out_dword(HWIO_REG_489688_ADDR, v)
+#define HWIO_REG_489688_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_489688_ADDR, m, v, HWIO_REG_489688_IN);
+#define HWIO_REG_489688_REG_489688_BMSK  0xffffffff
+#define HWIO_REG_489688_REG_489688_SHFT  0
+
+#define HWIO_REG_853667_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000201c)
+#define HWIO_REG_853667_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000201c)
+#define HWIO_REG_853667_RMSK  0xffffffff
+#define HWIO_REG_853667_SHFT  0
+#define HWIO_REG_853667_IN  in_dword_masked(\
+	HWIO_REG_853667_ADDR, HWIO_REG_853667_RMSK)
+#define HWIO_REG_853667_INM(m) \
+	in_dword_masked(HWIO_REG_853667_ADDR, m)
+#define HWIO_REG_853667_OUT(v) \
+	out_dword(HWIO_REG_853667_ADDR, v)
+#define HWIO_REG_853667_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_853667_ADDR, m, v, HWIO_REG_853667_IN);
+#define HWIO_REG_853667_REG_853667_BMSK  0xffffffff
+#define HWIO_REG_853667_REG_853667_SHFT  0
+
+#define HWIO_REG_760102_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00002020)
+#define HWIO_REG_760102_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00002020)
+#define HWIO_REG_760102_RMSK  0xffffffff
+#define HWIO_REG_760102_SHFT  0
+#define HWIO_REG_760102_IN  in_dword_masked(\
+	HWIO_REG_760102_ADDR, HWIO_REG_760102_RMSK)
+#define HWIO_REG_760102_INM(m) \
+	in_dword_masked(HWIO_REG_760102_ADDR, m)
+#define HWIO_REG_760102_OUT(v) \
+	out_dword(HWIO_REG_760102_ADDR, v)
+#define HWIO_REG_760102_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_760102_ADDR, m, v, HWIO_REG_760102_IN);
+#define HWIO_REG_760102_REG_760102_BMSK  0xffffffff
+#define HWIO_REG_760102_REG_760102_SHFT  0
+
+#define HWIO_REG_378318_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00002024)
+#define HWIO_REG_378318_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00002024)
+#define HWIO_REG_378318_RMSK  0xffffffff
+#define HWIO_REG_378318_SHFT  0
+#define HWIO_REG_378318_IN  in_dword_masked(\
+	HWIO_REG_378318_ADDR, HWIO_REG_378318_RMSK)
+#define HWIO_REG_378318_INM(m) \
+	in_dword_masked(HWIO_REG_378318_ADDR, m)
+#define HWIO_REG_378318_OUT(v) \
+	out_dword(HWIO_REG_378318_ADDR, v)
+#define HWIO_REG_378318_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_378318_ADDR, m, v, HWIO_REG_378318_IN);
+#define HWIO_REG_378318_REG_378318_BMSK  0xffffffff
+#define HWIO_REG_378318_REG_378318_SHFT  0
+
+#define HWIO_REG_203487_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00002028)
+#define HWIO_REG_203487_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00002028)
+#define HWIO_REG_203487_RMSK  0xffffffff
+#define HWIO_REG_203487_SHFT  0
+#define HWIO_REG_203487_IN  in_dword_masked(\
+	HWIO_REG_203487_ADDR, HWIO_REG_203487_RMSK)
+#define HWIO_REG_203487_INM(m) \
+	in_dword_masked(HWIO_REG_203487_ADDR, m)
+#define HWIO_REG_203487_OUT(v) \
+	out_dword(HWIO_REG_203487_ADDR, v)
+#define HWIO_REG_203487_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_203487_ADDR, m, v, HWIO_REG_203487_IN);
+#define HWIO_REG_203487_REG_203487_BMSK  0xffffffff
+#define HWIO_REG_203487_REG_203487_SHFT  0
+
+#define HWIO_REG_692991_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000202c)
+#define HWIO_REG_692991_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000202c)
+#define HWIO_REG_692991_RMSK  0xffffffff
+#define HWIO_REG_692991_SHFT  0
+#define HWIO_REG_692991_IN  in_dword_masked(\
+	HWIO_REG_692991_ADDR, HWIO_REG_692991_RMSK)
+#define HWIO_REG_692991_INM(m) \
+	in_dword_masked(HWIO_REG_692991_ADDR, m)
+#define HWIO_REG_692991_OUT(v) \
+	out_dword(HWIO_REG_692991_ADDR, v)
+#define HWIO_REG_692991_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_692991_ADDR, m, v, HWIO_REG_692991_IN);
+#define HWIO_REG_692991_REG_692991_BMSK  0xffffffff
+#define HWIO_REG_692991_REG_692991_SHFT  0
+
+#define HWIO_REG_161740_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00002030)
+#define HWIO_REG_161740_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00002030)
+#define HWIO_REG_161740_RMSK  0xffffffff
+#define HWIO_REG_161740_SHFT  0
+#define HWIO_REG_161740_IN  in_dword_masked(\
+	HWIO_REG_161740_ADDR, HWIO_REG_161740_RMSK)
+#define HWIO_REG_161740_INM(m) \
+	in_dword_masked(HWIO_REG_161740_ADDR, m)
+#define HWIO_REG_161740_OUT(v) \
+	out_dword(HWIO_REG_161740_ADDR, v)
+#define HWIO_REG_161740_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_161740_ADDR, m, v, HWIO_REG_161740_IN);
+#define HWIO_REG_161740_REG_161740_BMSK  0xffffffff
+#define HWIO_REG_161740_REG_161740_SHFT  0
+
+#define HWIO_REG_930239_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00002034)
+#define HWIO_REG_930239_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00002034)
+#define HWIO_REG_930239_RMSK  0xffffffff
+#define HWIO_REG_930239_SHFT  0
+#define HWIO_REG_930239_IN  in_dword_masked(\
+	HWIO_REG_930239_ADDR, HWIO_REG_930239_RMSK)
+#define HWIO_REG_930239_INM(m) \
+	in_dword_masked(HWIO_REG_930239_ADDR, m)
+#define HWIO_REG_930239_OUT(v) \
+	out_dword(HWIO_REG_930239_ADDR, v)
+#define HWIO_REG_930239_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_930239_ADDR, m, v, HWIO_REG_930239_IN);
+#define HWIO_REG_930239_REG_930239_BMSK  0xffffffff
+#define HWIO_REG_930239_REG_930239_SHFT  0
+
+#define HWIO_REG_567827_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00002038)
+#define HWIO_REG_567827_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00002038)
+#define HWIO_REG_567827_RMSK  0xffffffff
+#define HWIO_REG_567827_SHFT  0
+#define HWIO_REG_567827_IN  in_dword_masked(\
+	HWIO_REG_567827_ADDR, HWIO_REG_567827_RMSK)
+#define HWIO_REG_567827_INM(m) \
+	in_dword_masked(HWIO_REG_567827_ADDR, m)
+#define HWIO_REG_567827_OUT(v) \
+	out_dword(HWIO_REG_567827_ADDR, v)
+#define HWIO_REG_567827_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_567827_ADDR, m, v, HWIO_REG_567827_IN);
+#define HWIO_REG_567827_REG_567827_BMSK 0xffffffff
+#define HWIO_REG_567827_REG_567827_SHFT 0
+
+#define HWIO_REG_542997_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000203c)
+#define HWIO_REG_542997_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000203c)
+#define HWIO_REG_542997_RMSK  0xffffffff
+#define HWIO_REG_542997_SHFT  0
+#define HWIO_REG_542997_IN  in_dword_masked(\
+	HWIO_REG_542997_ADDR, HWIO_REG_542997_RMSK)
+#define HWIO_REG_542997_INM(m) \
+	in_dword_masked(HWIO_REG_542997_ADDR, m)
+#define HWIO_REG_542997_OUT(v) \
+	out_dword(HWIO_REG_542997_ADDR, v)
+#define HWIO_REG_542997_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_542997_ADDR, m, v, HWIO_REG_542997_IN);
+#define HWIO_REG_542997_REG_542997_BMSK  0xffffffff
+#define HWIO_REG_542997_REG_542997_SHFT  0
+
+#define HWIO_REG_666957_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00002040)
+#define HWIO_REG_666957_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00002040)
+#define HWIO_REG_666957_RMSK  0x7ffff
+#define HWIO_REG_666957_SHFT  0
+#define HWIO_REG_666957_IN  in_dword_masked(\
+	HWIO_REG_666957_ADDR, HWIO_REG_666957_RMSK)
+#define HWIO_REG_666957_INM(m) \
+	in_dword_masked(HWIO_REG_666957_ADDR, m)
+#define HWIO_REG_666957_OUT(v) \
+	out_dword(HWIO_REG_666957_ADDR, v)
+#define HWIO_REG_666957_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_666957_ADDR, m, v, HWIO_REG_666957_IN);
+#define HWIO_REG_666957_CH_DEC_TYPE_BMSK  0x70000
+#define HWIO_REG_666957_CH_DEC_TYPE_SHFT  0x10
+#define HWIO_REG_666957_CH_INST_ID_BMSK   0xffff
+#define HWIO_REG_666957_CH_INST_ID_SHFT   0
+
+#define HWIO_REG_117192_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00002044)
+#define HWIO_REG_117192_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00002044)
+#define HWIO_REG_117192_RMSK  0xffffffff
+#define HWIO_REG_117192_SHFT  0
+#define HWIO_REG_117192_IN  in_dword_masked(\
+	HWIO_REG_117192_ADDR, HWIO_REG_117192_RMSK)
+#define HWIO_REG_117192_INM(m) \
+	in_dword_masked(HWIO_REG_117192_ADDR, m)
+#define HWIO_REG_117192_OUT(v) \
+	out_dword(HWIO_REG_117192_ADDR, v)
+#define HWIO_REG_117192_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_117192_ADDR, m, v, HWIO_REG_117192_IN);
+#define HWIO_REG_117192_REG_117192_BMSK  0xffffffff
+#define HWIO_REG_117192_REG_117192_SHFT  0
+
+#define HWIO_REG_145068_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00002048)
+#define HWIO_REG_145068_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00002048)
+#define HWIO_REG_145068_RMSK  0xffffffff
+#define HWIO_REG_145068_SHFT  0
+#define HWIO_REG_145068_IN  in_dword_masked(\
+	HWIO_REG_145068_ADDR, HWIO_REG_145068_RMSK)
+#define HWIO_REG_145068_INM(m) \
+	in_dword_masked(HWIO_REG_145068_ADDR, m)
+#define HWIO_REG_145068_OUT(v) \
+	out_dword(HWIO_REG_145068_ADDR, v)
+#define HWIO_REG_145068_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_145068_ADDR, m, v, HWIO_REG_145068_IN);
+#define HWIO_REG_145068_REG_145068_BMSK  0xffffffff
+#define HWIO_REG_145068_REG_145068_SHFT  0
+
+#define HWIO_REG_921356_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000204c)
+#define HWIO_REG_921356_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000204c)
+#define HWIO_REG_921356_RMSK  0xffffffff
+#define HWIO_REG_921356_SHFT  0
+#define HWIO_REG_921356_IN  in_dword_masked(\
+	HWIO_REG_921356_ADDR, HWIO_REG_921356_RMSK)
+#define HWIO_REG_921356_INM(m) \
+	in_dword_masked(HWIO_REG_921356_ADDR, m)
+#define HWIO_REG_921356_OUT(v) \
+	out_dword(HWIO_REG_921356_ADDR, v)
+#define HWIO_REG_921356_OUTM(m, v) out_dword_masked_ns(\
+	HWIO_REG_921356_ADDR, m, v, HWIO_REG_921356_IN);
+#define HWIO_REG_921356_REG_921356_BMSK  0xffffffff
+#define HWIO_REG_921356_REG_921356_SHFT  0
+
+#define HWIO_REG_612810_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00002050)
+#define HWIO_REG_612810_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00002050)
+#define HWIO_REG_612810_RMSK  0xffffffff
+#define HWIO_REG_612810_SHFT  0
+#define HWIO_REG_612810_IN  in_dword_masked(\
+	HWIO_REG_612810_ADDR, HWIO_REG_612810_RMSK)
+#define HWIO_REG_612810_INM(m) \
+	in_dword_masked(HWIO_REG_612810_ADDR, m)
+#define HWIO_REG_612810_OUT(v) \
+	out_dword(HWIO_REG_612810_ADDR, v)
+#define HWIO_REG_612810_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_612810_ADDR, m, v, HWIO_REG_612810_IN);
+#define HWIO_REG_612810_REG_612810_BMSK  0xffffffff
+#define HWIO_REG_612810_REG_612810_SHFT  0
+
+#define HWIO_REG_175608_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00002054)
+#define HWIO_REG_175608_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00002054)
+#define HWIO_REG_175608_RMSK  0xffffffff
+#define HWIO_REG_175608_SHFT  0
+#define HWIO_REG_175608_IN  in_dword_masked(\
+	HWIO_REG_175608_ADDR, HWIO_REG_175608_RMSK)
+#define HWIO_REG_175608_INM(m) \
+	in_dword_masked(HWIO_REG_175608_ADDR, m)
+#define HWIO_REG_175608_OUT(v) \
+	out_dword(HWIO_REG_175608_ADDR, v)
+#define HWIO_REG_175608_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_175608_ADDR, m, v, HWIO_REG_175608_IN);
+#define HWIO_REG_175608_REG_175608_BMSK  0xffffffff
+#define HWIO_REG_175608_REG_175608_SHFT  0
+
+#define HWIO_REG_190381_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00002058)
+#define HWIO_REG_190381_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00002058)
+#define HWIO_REG_190381_RMSK  0xffffffff
+#define HWIO_REG_190381_SHFT  0
+#define HWIO_REG_190381_IN  in_dword_masked(\
+	HWIO_REG_190381_ADDR, HWIO_REG_190381_RMSK)
+#define HWIO_REG_190381_INM(m) \
+	in_dword_masked(HWIO_REG_190381_ADDR, m)
+#define HWIO_REG_190381_OUT(v) \
+	out_dword(HWIO_REG_190381_ADDR, v)
+#define HWIO_REG_190381_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_190381_ADDR, m, v, HWIO_REG_190381_IN);
+#define HWIO_REG_190381_REG_190381_BMSK  0xffffffff
+#define HWIO_REG_190381_REG_190381_SHFT  0
+
+#define HWIO_REG_85655_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000205c)
+#define HWIO_REG_85655_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000205c)
+#define HWIO_REG_85655_RMSK  0xffffffff
+#define HWIO_REG_85655_SHFT  0
+#define HWIO_REG_85655_IN  in_dword_masked(\
+	HWIO_REG_85655_ADDR, HWIO_REG_85655_RMSK)
+#define HWIO_REG_85655_INM(m) \
+	in_dword_masked(HWIO_REG_85655_ADDR, m)
+#define HWIO_REG_85655_OUT(v) \
+	out_dword(HWIO_REG_85655_ADDR, v)
+#define HWIO_REG_85655_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_85655_ADDR, m, v, HWIO_REG_85655_IN);
+#define HWIO_REG_85655_REG_85655_BMSK  0xffffffff
+#define HWIO_REG_85655_REG_85655_SHFT  0
+
+#define HWIO_REG_86830_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00002060)
+#define HWIO_REG_86830_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00002060)
+#define HWIO_REG_86830_RMSK  0xffffffff
+#define HWIO_REG_86830_SHFT  0
+#define HWIO_REG_86830_IN  in_dword_masked(\
+	HWIO_REG_86830_ADDR, HWIO_REG_86830_RMSK)
+#define HWIO_REG_86830_INM(m) \
+	in_dword_masked(HWIO_REG_86830_ADDR, m)
+#define HWIO_REG_86830_OUT(v) \
+	out_dword(HWIO_REG_86830_ADDR, v)
+#define HWIO_REG_86830_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_86830_ADDR, m, v, HWIO_REG_86830_IN);
+#define HWIO_REG_86830_REG_86830_BMSK  0xffffffff
+#define HWIO_REG_86830_REG_86830_SHFT  0
+
+#define HWIO_REG_889944_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00002064)
+#define HWIO_REG_889944_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00002064)
+#define HWIO_REG_889944_RMSK  0xffffffff
+#define HWIO_REG_889944_SHFT  0
+#define HWIO_REG_889944_IN  in_dword_masked(\
+	HWIO_REG_889944_ADDR, HWIO_REG_889944_RMSK)
+#define HWIO_REG_889944_INM(m) \
+	in_dword_masked(HWIO_REG_889944_ADDR, m)
+#define HWIO_REG_889944_OUT(v) \
+	out_dword(HWIO_REG_889944_ADDR, v)
+#define HWIO_REG_889944_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_889944_ADDR, m, v, HWIO_REG_889944_IN);
+#define HWIO_REG_889944_HOST_WR_ADDR_BMSK  0xffffffff
+#define HWIO_REG_889944_HOST_WR_ADSR_SHFT  0
+
+#define HWIO_REG_404623_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00002068)
+#define HWIO_REG_404623_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00002068)
+#define HWIO_REG_404623_RMSK  0xffffffff
+#define HWIO_REG_404623_SHFT  0
+#define HWIO_REG_404623_IN  in_dword_masked(\
+	HWIO_REG_404623_ADDR, HWIO_REG_404623_RMSK)
+#define HWIO_REG_404623_INM(m) \
+	in_dword_masked(HWIO_REG_404623_ADDR, m)
+#define HWIO_REG_404623_OUT(v) \
+	out_dword(HWIO_REG_404623_ADDR, v)
+#define HWIO_REG_404623_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_404623_ADDR, m, v, HWIO_REG_404623_IN);
+#define HWIO_REG_404623_REG_404623_BMSK  0xffffffff
+#define HWIO_REG_404623_REG_404623_SHFT  0
+
+#define HWIO_REG_397087_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000206c)
+#define HWIO_REG_397087_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000206c)
+#define HWIO_REG_397087_RMSK  0xffffffff
+#define HWIO_REG_397087_SHFT  0
+#define HWIO_REG_397087_IN  in_dword_masked(\
+	HWIO_REG_397087_ADDR, HWIO_REG_397087_RMSK)
+#define HWIO_REG_397087_INM(m) \
+	in_dword_masked(HWIO_REG_397087_ADDR, m)
+#define HWIO_REG_397087_OUT(v) \
+	out_dword(HWIO_REG_397087_ADDR, v)
+#define HWIO_REG_397087_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_397087_ADDR, m, v, HWIO_REG_397087_IN);
+#define HWIO_REG_397087_CMD_SEQ_NUM_BMSK  0xffffffff
+#define HWIO_REG_397087_CMD_SEQ_NUM_SHFT  0
+
+#define HWIO_REG_212613_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00002070)
+#define HWIO_REG_212613_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00002070)
+#define HWIO_REG_212613_RMSK  0xffffffff
+#define HWIO_REG_212613_SHFT  0
+#define HWIO_REG_212613_IN  in_dword_masked(\
+	HWIO_REG_212613_ADDR, HWIO_REG_212613_RMSK)
+#define HWIO_REG_212613_INM(m) \
+	in_dword_masked(HWIO_REG_212613_ADDR, m)
+#define HWIO_REG_212613_OUT(v) \
+	out_dword(HWIO_REG_212613_ADDR, v)
+#define HWIO_REG_212613_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_212613_ADDR, m, v, HWIO_REG_212613_IN);
+#define HWIO_REG_212613_REG_212613_BMSK  0xffffffff
+#define HWIO_REG_212613_REG_212613_SHFT  0
+
+#define HWIO_REG_840123_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00002074)
+#define HWIO_REG_840123_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00002074)
+#define HWIO_REG_840123_RMSK  0xffffffff
+#define HWIO_REG_840123_SHFT  0
+#define HWIO_REG_840123_IN  in_dword_masked(\
+	HWIO_REG_840123_ADDR, HWIO_REG_840123_RMSK)
+#define HWIO_REG_840123_INM(m) \
+	in_dword_masked(HWIO_REG_840123_ADDR, m)
+#define HWIO_REG_840123_OUT(v) \
+	out_dword(HWIO_REG_840123_ADDR, v)
+#define HWIO_REG_840123_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_840123_ADDR, m, v, HWIO_REG_840123_IN);
+#define HWIO_REG_840123_REG_840123_BMSK  0xffffffff
+#define HWIO_REG_840123_REG_840123_SHFT  0
+
+#define HWIO_REG_520335_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00002078)
+#define HWIO_REG_520335_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00002078)
+#define HWIO_REG_520335_RMSK  0xffffffff
+#define HWIO_REG_520335_SHFT  0
+#define HWIO_REG_520335_IN  in_dword_masked(\
+	HWIO_REG_520335_ADDR, HWIO_REG_520335_RMSK)
+#define HWIO_REG_520335_INM(m) \
+	in_dword_masked(HWIO_REG_520335_ADDR, m)
+#define HWIO_REG_520335_OUT(v) \
+	out_dword(HWIO_REG_520335_ADDR, v)
+#define HWIO_REG_520335_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_520335_ADDR, m, v, HWIO_REG_520335_IN);
+#define HWIO_REG_520335_REG_196943_BMSK  0xffffffff
+#define HWIO_REG_520335_REG_196943_SHFT  0
+
+#define HWIO_REG_196943_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000207c)
+#define HWIO_REG_196943_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000207c)
+#define HWIO_REG_196943_RMSK  0xffffffff
+#define HWIO_REG_196943_SHFT  0
+#define HWIO_REG_196943_IN  in_dword_masked(\
+	HWIO_REG_196943_ADDR, HWIO_REG_196943_RMSK)
+#define HWIO_REG_196943_INM(m) \
+	in_dword_masked(HWIO_REG_196943_ADDR, m)
+#define HWIO_REG_196943_OUT(v) \
+	out_dword(HWIO_REG_196943_ADDR, v)
+#define HWIO_REG_196943_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_196943_ADDR, m, v, HWIO_REG_196943_IN);
+#define HWIO_REG_196943_REG_196943_BMSK  0xffffffff
+#define HWIO_REG_196943_REG_196943_SHFT   0
+
+#define HWIO_REG_313350_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00002080)
+#define HWIO_REG_313350_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00002080)
+#define HWIO_REG_313350_RMSK  0x7ffff
+#define HWIO_REG_313350_SHFT  0
+#define HWIO_REG_313350_IN  in_dword_masked(\
+	HWIO_REG_313350_ADDR, HWIO_REG_313350_RMSK)
+#define HWIO_REG_313350_INM(m) \
+	in_dword_masked(HWIO_REG_313350_ADDR, m)
+#define HWIO_REG_313350_OUT(v) \
+	out_dword(HWIO_REG_313350_ADDR, v)
+#define HWIO_REG_313350_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_313350_ADDR, m, v, HWIO_REG_313350_IN);
+#define HWIO_REG_313350_CH_DEC_TYPE_BMSK  0x70000
+#define HWIO_REG_313350_CH_DEC_TYPE_SHFT  0x10
+#define HWIO_REG_313350_CH_INST_ID_BMSK   0xffff
+#define HWIO_REG_313350_CH_INST_ID_SHFT   0
+
+#define HWIO_REG_980194_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00002084)
+#define HWIO_REG_980194_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00002084)
+#define HWIO_REG_980194_RMSK  0xffffffff
+#define HWIO_REG_980194_SHFT  0
+#define HWIO_REG_980194_IN  in_dword_masked(\
+	HWIO_REG_980194_ADDR, HWIO_REG_980194_RMSK)
+#define HWIO_REG_980194_INM(m) \
+	in_dword_masked(HWIO_REG_980194_ADDR, m)
+#define HWIO_REG_980194_OUT(v) \
+	out_dword(HWIO_REG_980194_ADDR, v)
+#define HWIO_REG_980194_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_980194_ADDR, m, v, HWIO_REG_980194_IN);
+#define HWIO_REG_980194_REG_980194_BMSK  0xffffffff
+#define HWIO_REG_980194_REG_980194_SHFT  0
+
+#define HWIO_REG_936704_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00002088)
+#define HWIO_REG_936704_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00002088)
+#define HWIO_REG_936704_RMSK  0xffffffff
+#define HWIO_REG_936704_SHFT  0
+#define HWIO_REG_936704_IN  in_dword_masked(\
+	HWIO_REG_936704_ADDR, HWIO_REG_936704_RMSK)
+#define HWIO_REG_936704_INM(m) \
+	in_dword_masked(HWIO_REG_936704_ADDR, m)
+#define HWIO_REG_936704_OUT(v) \
+	out_dword(HWIO_REG_936704_ADDR, v)
+#define HWIO_REG_936704_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_936704_ADDR, m, v, HWIO_REG_936704_IN);
+#define HWIO_REG_936704_REG_936704_BMSK  0xffffffff
+#define HWIO_REG_936704_REG_936704_SHFT  0
+
+#define HWIO_REG_821977_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000208c)
+#define HWIO_REG_821977_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000208c)
+#define HWIO_REG_821977_RMSK  0xffffffff
+#define HWIO_REG_821977_SHFT  0
+#define HWIO_REG_821977_IN  in_dword_masked(\
+	HWIO_REG_821977_ADDR, HWIO_REG_821977_RMSK)
+#define HWIO_REG_821977_INM(m) \
+	in_dword_masked(HWIO_REG_821977_ADDR, m)
+#define HWIO_REG_821977_OUT(v) \
+	out_dword(HWIO_REG_821977_ADDR, v)
+#define HWIO_REG_821977_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_821977_ADDR, m, v, HWIO_REG_821977_IN);
+#define HWIO_REG_821977_REG_821977_BMSK  0xffffffff
+#define HWIO_REG_821977_REG_821977_SHFT  0
+
+#define HWIO_REG_655721_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00002090)
+#define HWIO_REG_655721_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00002090)
+#define HWIO_REG_655721_RMSK  0xffffffff
+#define HWIO_REG_655721_SHFT  0
+#define HWIO_REG_655721_IN  in_dword_masked(\
+	HWIO_REG_655721_ADDR, HWIO_REG_655721_RMSK)
+#define HWIO_REG_655721_INM(m) \
+	in_dword_masked(HWIO_REG_655721_ADDR, m)
+#define HWIO_REG_655721_OUT(v) \
+	out_dword(HWIO_REG_655721_ADDR, v)
+#define HWIO_REG_655721_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_655721_ADDR, m, v, HWIO_REG_655721_IN);
+#define HWIO_REG_655721_REG_655721_BMSK  0xffffffff
+#define HWIO_REG_655721_REG_655721_SHFT  0
+
+#define HWIO_REG_548308_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00002094)
+#define HWIO_REG_548308_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00002094)
+#define HWIO_REG_548308_RMSK  0xffffffff
+#define HWIO_REG_548308_SHFT  0
+#define HWIO_REG_548308_IN  in_dword_masked(\
+	HWIO_REG_548308_ADDR, HWIO_REG_548308_RMSK)
+#define HWIO_REG_548308_INM(m) \
+	in_dword_masked(HWIO_REG_548308_ADDR, m)
+#define HWIO_REG_548308_OUT(v) \
+	out_dword(HWIO_REG_548308_ADDR, v)
+#define HWIO_REG_548308_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_548308_ADDR, m, v, HWIO_REG_548308_IN);
+#define HWIO_REG_548308_REG_548308_BMSK  0xffffffff
+#define HWIO_REG_548308_REG_548308_SHFT  0
+
+#define HWIO_REG_887095_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x00002098)
+#define HWIO_REG_887095_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x00002098)
+#define HWIO_REG_887095_RMSK  0xffffffff
+#define HWIO_REG_887095_SHFT  0
+#define HWIO_REG_887095_IN  in_dword_masked(\
+	HWIO_REG_887095_ADDR, HWIO_REG_887095_RMSK)
+#define HWIO_REG_887095_INM(m) \
+	in_dword_masked(HWIO_REG_887095_ADDR, m)
+#define HWIO_REG_887095_OUT(v) \
+	out_dword(HWIO_REG_887095_ADDR, v)
+#define HWIO_REG_887095_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_887095_ADDR, m, v, HWIO_REG_887095_IN);
+#define HWIO_REG_887095_REG_887095_BMSK  0xffffffff
+#define HWIO_REG_887095_REG_887095_SHFT  0
+
+#define HWIO_REG_576987_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000209c)
+#define HWIO_REG_576987_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000209c)
+#define HWIO_REG_576987_RMSK  0xffffffff
+#define HWIO_REG_576987_SHFT 0
+#define HWIO_REG_576987_IN  in_dword_masked(\
+	HWIO_REG_576987_ADDR, HWIO_REG_576987_RMSK)
+#define HWIO_REG_576987_INM(m) \
+	in_dword_masked(HWIO_REG_576987_ADDR, m)
+#define HWIO_REG_576987_OUT(v) \
+	out_dword(HWIO_REG_576987_ADDR, v)
+#define HWIO_REG_576987_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_576987_ADDR, m, v, HWIO_REG_576987_IN);
+#define HWIO_REG_576987_REG_576987_BMSK  0xffffffff
+#define HWIO_REG_576987_REG_576987_SHFT  0
+
+#define HWIO_REG_70448_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x000020a0)
+#define HWIO_REG_70448_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000020a0)
+#define HWIO_REG_70448_RMSK  0xffffffff
+#define HWIO_REG_70448_SHFT  0
+#define HWIO_REG_70448_IN  in_dword_masked(\
+	HWIO_REG_70448_ADDR, HWIO_REG_70448_RMSK)
+#define HWIO_REG_70448_INM(m) \
+	in_dword_masked(HWIO_REG_70448_ADDR, m)
+#define HWIO_REG_70448_OUT(v) \
+	out_dword(HWIO_REG_70448_ADDR, v)
+#define HWIO_REG_70448_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_70448_ADDR, m, v, HWIO_REG_70448_IN);
+#define HWIO_REG_70448_REG_70448_BMSK  0xffffffff
+#define HWIO_REG_70448_REG_70448_SHFT  0
+
+#define HWIO_REG_652528_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x000020a4)
+#define HWIO_REG_652528_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000020a4)
+#define HWIO_REG_652528_RMSK  0xffffffff
+#define HWIO_REG_652528_SHFT  0
+#define HWIO_REG_652528_IN  in_dword_masked(\
+	HWIO_REG_652528_ADDR, HWIO_REG_652528_RMSK)
+#define HWIO_REG_652528_INM(m) \
+	in_dword_masked(HWIO_REG_652528_ADDR, m)
+#define HWIO_REG_652528_OUT(v) \
+	out_dword(HWIO_REG_652528_ADDR, v)
+#define HWIO_REG_652528_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_652528_ADDR, m, v , HWIO_REG_652528_IN);
+#define HWIO_REG_652528_REG_652528_BMSK  0xffffffff
+#define HWIO_REG_652528_REG_652528_SHFT  0
+
+#define HWIO_REG_220637_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x000020a8)
+#define HWIO_REG_220637_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000020a8)
+#define HWIO_REG_220637_RMSK  0xffffffff
+#define HWIO_REG_220637_SHFT  0
+#define HWIO_REG_220637_IN  in_dword_masked(\
+	HWIO_REG_220637_ADDR, HWIO_REG_220637_RMSK)
+#define HWIO_REG_220637_INM(m) \
+	in_dword_masked(HWIO_REG_220637_ADDR, m)
+#define HWIO_REG_220637_OUT(v) \
+	out_dword(HWIO_REG_220637_ADDR, v)
+#define HWIO_REG_220637_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_220637_ADDR, m, v, HWIO_REG_220637_IN);
+#define HWIO_REG_220637_REG_220637_BMSK  0xffffffff
+#define HWIO_REG_220637_REG_220637_SHFT  0
+
+#define HWIO_REG_254093_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x000020ac)
+#define HWIO_REG_254093_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000020ac)
+#define HWIO_REG_254093_RMSK  0xffffffff
+#define HWIO_REG_254093_SHFT  0
+#define HWIO_REG_254093_IN  in_dword_masked(\
+	HWIO_REG_254093_ADDR, HWIO_REG_254093_RMSK)
+#define HWIO_REG_254093_INM(m) \
+	in_dword_masked(HWIO_REG_254093_ADDR, m)
+#define HWIO_REG_254093_OUT(v) \
+	out_dword(HWIO_REG_254093_ADDR, v)
+#define HWIO_REG_254093_OUTM(m, v)  out_dword_masked_ns\
+	(HWIO_REG_254093_ADDR, m, v, HWIO_REG_254093_IN);
+#define HWIO_REG_254093_REG_254093_BMSK  0xffffffff
+#define HWIO_REG_254093_REG_254093_SHFT  0
+
+#define HWIO_REG_160474_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x000020b0)
+#define HWIO_REG_160474_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000020b0)
+#define HWIO_REG_160474_RMSK  0xffffffff
+#define HWIO_REG_160474_SHFT  0
+#define HWIO_REG_160474_IN  in_dword_masked(\
+	HWIO_REG_160474_ADDR, HWIO_REG_160474_RMSK)
+#define HWIO_REG_160474_INM(m) \
+	in_dword_masked(HWIO_REG_160474_ADDR, m)
+#define HWIO_REG_160474_OUT(v) \
+	out_dword(HWIO_REG_160474_ADDR, v)
+#define HWIO_REG_160474_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_160474_ADDR, m, v, HWIO_REG_160474_IN);
+#define HWIO_REG_160474_REG_160474_BMSK  0xffffffff
+#define HWIO_REG_160474_REG_160474_SHFT  0
+
+#define HWIO_REG_39027_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x000020b4)
+#define HWIO_REG_39027_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000020b4)
+#define HWIO_REG_39027_RMSK  0xffffffff
+#define HWIO_REG_39027_SHFT  0
+#define HWIO_REG_39027_IN  in_dword_masked(\
+	HWIO_REG_39027_ADDR, HWIO_REG_39027_RMSK)
+#define HWIO_REG_39027_INM(m) \
+	in_dword_masked(HWIO_REG_39027_ADDR, m)
+#define HWIO_REG_39027_OUT(v) \
+	out_dword(HWIO_REG_39027_ADDR, v)
+#define HWIO_REG_39027_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_39027_ADDR, m, v, HWIO_REG_39027_IN);
+#define HWIO_REG_39027_REG_39027_BMSK  0xffffffff
+#define HWIO_REG_39027_REG_39027_SHFT  0
+
+#define HWIO_REG_74049_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x000020b8)
+#define HWIO_REG_74049_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000020b8)
+#define HWIO_REG_74049_RMSK  0xffffffff
+#define HWIO_REG_74049_SHFT  0
+#define HWIO_REG_74049_IN  in_dword_masked(\
+	HWIO_REG_74049_ADDR, HWIO_REG_74049_RMSK)
+#define HWIO_REG_74049_INM(m) \
+	in_dword_masked(HWIO_REG_74049_ADDR, m)
+#define HWIO_REG_74049_OUT(v) \
+	out_dword(HWIO_REG_74049_ADDR, v)
+#define HWIO_REG_74049_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_74049_ADDR, m, v, HWIO_REG_74049_IN);
+#define HWIO_REG_74049_REG_74049_BMSK  0xffffffff
+#define HWIO_REG_74049_REG_74049_SHFT  0
+
+#define HWIO_REG_697870_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x000020bc)
+#define HWIO_REG_697870_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x000020bc)
+#define HWIO_REG_697870_RMSK  0xffffffff
+#define HWIO_REG_697870_SHFT  0
+#define HWIO_REG_697870_IN  in_dword_masked(\
+	HWIO_REG_697870_ADDR, HWIO_REG_697870_RMSK)
+#define HWIO_REG_697870_INM(m) \
+	in_dword_masked(HWIO_REG_697870_ADDR, m)
+#define HWIO_REG_697870_OUT(v) \
+	out_dword(HWIO_REG_697870_ADDR, v)
+#define HWIO_REG_697870_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_697870_ADDR, m, v, HWIO_REG_697870_IN);
+#define HWIO_REG_697870_REG_697870_BMSK  0xffffffff
+#define HWIO_REG_697870_REG_697870_SHFT  0
+
+#define HWIO_REG_783891_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000c504)
+#define HWIO_REG_783891_PHYS (VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000c504)
+#define HWIO_REG_783891_RMSK  0x7ffff
+#define HWIO_REG_783891_SHFT  0
+#define HWIO_REG_783891_IN  in_dword_masked(\
+	HWIO_REG_783891_ADDR, HWIO_REG_783891_RMSK)
+#define HWIO_REG_783891_INM(m) \
+	in_dword_masked(HWIO_REG_783891_ADDR, m)
+#define HWIO_REG_783891_OUT(v) \
+	out_dword(HWIO_REG_783891_ADDR, v)
+#define HWIO_REG_783891_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_783891_ADDR, m, v, HWIO_REG_783891_IN);
+#define HWIO_REG_783891_ENC_PIC_TYPE_USE_BMSK  0x40000
+#define HWIO_REG_783891_ENC_PIC_TYPE_USE_SHFT  0x12
+#define HWIO_REG_783891_B_FRM_CTRL_BMSK        0x30000
+#define HWIO_REG_783891_B_FRM_CTRL_SHFT        0x10
+#define HWIO_REG_783891_I_FRM_CTRL_BMSK        0xffff
+#define HWIO_REG_783891_I_FRM_CTRL_SHFT        0
+
+#define HWIO_REG_226332_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000c50c)
+#define HWIO_REG_226332_PHYS (VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000c50c)
+#define HWIO_REG_226332_RMSK  0x7
+#define HWIO_REG_226332_SHFT  0
+#define HWIO_REG_226332_IN  in_dword_masked(\
+	HWIO_REG_226332_ADDR, HWIO_REG_226332_RMSK)
+#define HWIO_REG_226332_INM(m)  in_dword_masked(HWIO_REG_226332_ADDR, m)
+#define HWIO_REG_226332_OUT(v)  out_dword(HWIO_REG_226332_ADDR, v)
+#define HWIO_REG_226332_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_226332_ADDR, m, v, HWIO_REG_226332_IN);
+#define HWIO_REG_226332_MSLICE_MODE_BMSK  0x6
+#define HWIO_REG_226332_MSLICE_MODE_SHFT  0x1
+#define HWIO_REG_226332_MSLICE_ENA_BMSK   0x1
+#define HWIO_REG_226332_MSLICE_ENA_SHFT   0
+
+#define HWIO_REG_696136_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000c510)
+#define HWIO_REG_696136_PHYS (VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000c510)
+#define HWIO_REG_696136_RMSK  0xffff
+#define HWIO_REG_696136_SHFT  0
+#define HWIO_REG_696136_IN  in_dword_masked(\
+	HWIO_REG_696136_ADDR, HWIO_REG_696136_RMSK)
+#define HWIO_REG_696136_INM(m)  in_dword_masked(HWIO_REG_696136_ADDR, m)
+#define HWIO_REG_696136_OUT(v)  out_dword(HWIO_REG_696136_ADDR, v)
+#define HWIO_REG_696136_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_696136_ADDR, m, v, HWIO_REG_696136_IN);
+#define HWIO_REG_696136_MSLICE_MB_BMSK  0xffff
+#define HWIO_REG_696136_MSLICE_MB_SHFT  0
+
+#define HWIO_REG_515564_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000c514)
+#define HWIO_REG_515564_PHYS (VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000c514)
+#define HWIO_REG_515564_RMSK  0xffffffff
+#define HWIO_REG_515564_SHFT  0
+#define HWIO_REG_515564_IN  in_dword_masked(\
+	HWIO_REG_515564_ADDR, HWIO_REG_515564_RMSK)
+#define HWIO_REG_515564_INM(m) \
+	in_dword_masked(HWIO_REG_515564_ADDR, m)
+#define HWIO_REG_515564_OUT(v)  out_dword(HWIO_REG_515564_ADDR, v)
+#define HWIO_REG_515564_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_515564_ADDR, m, v, HWIO_REG_515564_IN);
+#define HWIO_REG_515564_MSLICE_BIT_BMSK  0xffffffff
+#define HWIO_REG_515564_MSLICE_BIT_SHFT  0
+
+#define HWIO_REG_886210_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000c518)
+#define HWIO_REG_886210_PHYS (VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000c518)
+#define HWIO_REG_886210_RMSK  0xffff
+#define HWIO_REG_886210_SHFT  0
+#define HWIO_REG_886210_IN  in_dword_masked(\
+	HWIO_REG_886210_ADDR, HWIO_REG_886210_RMSK)
+#define HWIO_REG_886210_INM(m)  in_dword_masked(HWIO_REG_886210_ADDR, m)
+#define HWIO_REG_886210_OUT(v)  out_dword(HWIO_REG_886210_ADDR, v)
+#define HWIO_REG_886210_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_886210_ADDR, m, v, HWIO_REG_886210_IN);
+#define HWIO_REG_886210_CIR_NUM_BMSK  0xffff
+#define HWIO_REG_886210_CIR_NUM_SHFT  0
+
+#define HWIO_REG_645603_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000c51c)
+#define HWIO_REG_645603_PHYS (VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000c51c)
+#define HWIO_REG_645603_RMSK  0x3
+#define HWIO_REG_645603_SHFT  0
+#define HWIO_REG_645603_IN  in_dword_masked(\
+	HWIO_REG_645603_ADDR, HWIO_REG_645603_RMSK)
+#define HWIO_REG_645603_INM(m) \
+	in_dword_masked(HWIO_REG_645603_ADDR, m)
+#define HWIO_REG_645603_OUT(v)  out_dword(HWIO_REG_645603_ADDR, v)
+#define HWIO_REG_645603_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_645603_ADDR, m, v, HWIO_REG_645603_IN);
+#define HWIO_REG_645603_REG_645603_BMSK  0x3
+#define HWIO_REG_645603_REG_645603_SHFT  0
+
+#define HWIO_REG_811733_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000c520)
+#define HWIO_REG_811733_PHYS (VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000c520)
+#define HWIO_REG_811733_RMSK  0x80ffffff
+#define HWIO_REG_811733_SHFT  0
+#define HWIO_REG_811733_IN  in_dword_masked(\
+	HWIO_REG_811733_ADDR, HWIO_REG_811733_RMSK)
+#define HWIO_REG_811733_INM(m) \
+	in_dword_masked(HWIO_REG_811733_ADDR, m)
+#define HWIO_REG_811733_OUT(v)  out_dword(HWIO_REG_811733_ADDR, v)
+#define HWIO_REG_811733_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_811733_ADDR, m, v, HWIO_REG_811733_IN);
+#define HWIO_REG_811733_PAD_CTRL_ON_BMSK    0x80000000
+#define HWIO_REG_811733_PAD_CTRL_ON_SHFT    0x1f
+#define HWIO_REG_811733_CR_PAD_VIDC_BMSK    0xff0000
+#define HWIO_REG_811733_CR_PAD_VIDC_SHFT    0x10
+#define HWIO_REG_811733_CB_PAD_VIDC_BMSK    0xff00
+#define HWIO_REG_811733_CB_PAD_VIDC_SHFT    0x8
+#define HWIO_REG_811733_LUMA_PAD_VIDC_BMSK  0xff
+#define HWIO_REG_811733_LUMA_PAD_VIDC_SHFT  0
+
+#define HWIO_REG_676866_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000c588)
+#define HWIO_REG_676866_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000c588)
+#define HWIO_REG_676866_RMSK  0xffff
+#define HWIO_REG_676866_SHFT  0
+#define HWIO_REG_676866_IN  in_dword_masked(\
+	HWIO_REG_676866_ADDR, HWIO_REG_676866_RMSK)
+#define HWIO_REG_676866_INM(m) \
+	in_dword_masked(HWIO_REG_676866_ADDR, m)
+#define HWIO_REG_676866_OUT(v) \
+	out_dword(HWIO_REG_676866_ADDR, v)
+#define HWIO_REG_676866_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_676866_ADDR, m, v, HWIO_REG_676866_IN);
+#define HWIO_REG_676866_REG_676866_BMSK  0xffff
+#define HWIO_REG_676866_REG_676866_SHFT  0
+
+#define HWIO_REG_54267_ADDR \
+	(VIDC_BLACKBIRD_REG_BASE + 0x0000c58c)
+#define HWIO_REG_54267_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000c58c)
+#define HWIO_REG_54267_RMSK  0xffff
+#define HWIO_REG_54267_SHFT  0
+#define HWIO_REG_54267_IN  in_dword_masked(\
+	HWIO_REG_54267_ADDR,\
+	HWIO_REG_54267_RMSK)
+#define HWIO_REG_54267_INM(m) \
+	in_dword_masked(HWIO_REG_54267_ADDR, m)
+#define HWIO_REG_54267_OUT(v) \
+	out_dword(HWIO_REG_54267_ADDR, v)
+#define HWIO_REG_54267_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_54267_ADDR, m, v,\
+	HWIO_REG_54267_IN);
+#define HWIO_REG_54267_REG_54267_BMSK  0xffff
+#define HWIO_REG_54267_REG_54267_SHFT  0
+
+#define HWIO_REG_559908_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000c5a0)
+#define HWIO_REG_559908_PHYS (VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000c5a0)
+#define HWIO_REG_559908_RMSK  0x33f
+#define HWIO_REG_559908_SHFT  0
+#define HWIO_REG_559908_IN  in_dword_masked(\
+	HWIO_REG_559908_ADDR, HWIO_REG_559908_RMSK)
+#define HWIO_REG_559908_INM(m)  in_dword_masked(HWIO_REG_559908_ADDR, m)
+#define HWIO_REG_559908_OUT(v)  out_dword(HWIO_REG_559908_ADDR, v)
+#define HWIO_REG_559908_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_559908_ADDR, m, v, HWIO_REG_559908_IN);
+#define HWIO_REG_559908_FR_RC_EN_BMSK  0x200
+#define HWIO_REG_559908_FR_RC_EN_SHFT  0x9
+#define HWIO_REG_559908_MB_RC_EN_BMSK  0x100
+#define HWIO_REG_559908_MB_RC_EN_SHFT  0x8
+#define HWIO_REG_559908_FRAME_QP_BMSK  0x3f
+#define HWIO_REG_559908_FRAME_QP_SHFT  0
+
+#define HWIO_REG_977937_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000d0d0)
+#define HWIO_REG_977937_PHYS (VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000d0d0)
+#define HWIO_REG_977937_RMSK  0xff
+#define HWIO_REG_977937_SHFT  0
+#define HWIO_REG_977937_IN  in_dword_masked(\
+	HWIO_REG_977937_ADDR, HWIO_REG_977937_RMSK)
+#define HWIO_REG_977937_INM(m)  in_dword_masked(HWIO_REG_977937_ADDR, m)
+#define HWIO_REG_977937_OUT(v)  out_dword(HWIO_REG_977937_ADDR, v)
+#define HWIO_REG_977937_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_977937_ADDR, m, v, HWIO_REG_977937_IN);
+#define HWIO_REG_977937_FRAME_RATE_BMSK  0xff
+#define HWIO_REG_977937_FRAME_RATE_SHFT  0
+
+#define HWIO_REG_166135_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000c5a8)
+#define HWIO_REG_166135_PHYS (VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000c5a8)
+#define HWIO_REG_166135_RMSK  0xffffffff
+#define HWIO_REG_166135_SHFT  0
+#define HWIO_REG_166135_IN  in_dword_masked(\
+	HWIO_REG_166135_ADDR, HWIO_REG_166135_RMSK)
+#define HWIO_REG_166135_INM(m)  in_dword_masked(HWIO_REG_166135_ADDR, m)
+#define HWIO_REG_166135_OUT(v)  out_dword(HWIO_REG_166135_ADDR, v)
+#define HWIO_REG_166135_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_166135_ADDR, m, v, HWIO_REG_166135_IN);
+#define HWIO_REG_166135_BIT_RATE_BMSK  0xffffffff
+#define HWIO_REG_166135_BIT_RATE_SHFT  0
+
+#define HWIO_REG_109072_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000c5ac)
+#define HWIO_REG_109072_PHYS (VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000c5ac)
+#define HWIO_REG_109072_RMSK  0x3fff
+#define HWIO_REG_109072_SHFT  0
+#define HWIO_REG_109072_IN  in_dword_masked(\
+	HWIO_REG_109072_ADDR, HWIO_REG_109072_RMSK)
+#define HWIO_REG_109072_INM(m)  in_dword_masked(HWIO_REG_109072_ADDR, m)
+#define HWIO_REG_109072_OUT(v)  out_dword(HWIO_REG_109072_ADDR, v)
+#define HWIO_REG_109072_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_109072_ADDR, m, v, HWIO_REG_109072_IN);
+#define HWIO_REG_109072_MAX_QP_BMSK  0x3f00
+#define HWIO_REG_109072_MAX_QP_SHFT  0x8
+#define HWIO_REG_109072_MIN_QP_BMSK  0x3f
+#define HWIO_REG_109072_MIN_QP_SHFT  0
+
+#define HWIO_REG_550322_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000c5b0)
+#define HWIO_REG_550322_PHYS (VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000c5b0)
+#define HWIO_REG_550322_RMSK  0xffff
+#define HWIO_REG_550322_SHFT  0
+#define HWIO_REG_550322_IN  in_dword_masked(\
+	HWIO_REG_550322_ADDR, HWIO_REG_550322_RMSK)
+#define HWIO_REG_550322_INM(m)  in_dword_masked(HWIO_REG_550322_ADDR, m)
+#define HWIO_REG_550322_OUT(v)  out_dword(HWIO_REG_550322_ADDR, v)
+#define HWIO_REG_550322_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_550322_ADDR, m, v, HWIO_REG_550322_IN);
+#define HWIO_REG_550322_REACT_PARA_BMSK  0xffff
+#define HWIO_REG_550322_REACT_PARA_SHFT  0
+
+#define HWIO_REG_949086_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000c5b4)
+#define HWIO_REG_949086_PHYS (VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000c5b4)
+#define HWIO_REG_949086_RMSK  0xf
+#define HWIO_REG_949086_SHFT  0
+#define HWIO_REG_949086_IN  in_dword_masked(\
+	HWIO_REG_949086_ADDR, HWIO_REG_949086_RMSK)
+#define HWIO_REG_949086_INM(m)  in_dword_masked(HWIO_REG_949086_ADDR, m)
+#define HWIO_REG_949086_OUT(v)  out_dword(HWIO_REG_949086_ADDR, v)
+#define HWIO_REG_949086_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_949086_ADDR, m, v, HWIO_REG_949086_IN);
+#define HWIO_REG_949086_DARK_DISABLE_BMSK    0x8
+#define HWIO_REG_949086_DARK_DISABLE_SHFT    0x3
+#define HWIO_REG_949086_SMOOTH_DISABLE_BMSK  0x4
+#define HWIO_REG_949086_SMOOTH_DISABLE_SHFT  0x2
+#define HWIO_REG_949086_STATIC_DISABLE_BMSK  0x2
+#define HWIO_REG_949086_STATIC_DISABLE_SHFT  0x1
+#define HWIO_REG_949086_ACT_DISABLE_BMSK     0x1
+#define HWIO_REG_949086_ACT_DISABLE_SHFT     0
+
+#define HWIO_REG_447796_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000d004)
+#define HWIO_REG_447796_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000d004)
+#define HWIO_REG_447796_RMSK  0x1
+#define HWIO_REG_447796_SHFT  0
+#define HWIO_REG_447796_IN  in_dword_masked(\
+	HWIO_REG_447796_ADDR, HWIO_REG_447796_RMSK)
+#define HWIO_REG_447796_INM(m) \
+	in_dword_masked(HWIO_REG_447796_ADDR, m)
+#define HWIO_REG_447796_OUT(v) \
+	out_dword(HWIO_REG_447796_ADDR, v)
+#define HWIO_REG_447796_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_447796_ADDR, m, v, HWIO_REG_447796_IN);
+#define HWIO_REG_447796_REG_447796_BMSK  0x1
+#define HWIO_REG_447796_REG_447796_SHFT  0
+
+#define HWIO_REG_744348_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000d010)
+#define HWIO_REG_744348_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000d010)
+#define HWIO_REG_744348_RMSK  0x7f
+#define HWIO_REG_744348_SHFT  0
+#define HWIO_REG_744348_IN  in_dword_masked(\
+	HWIO_REG_744348_ADDR, HWIO_REG_744348_RMSK)
+#define HWIO_REG_744348_INM(m) \
+	in_dword_masked(HWIO_REG_744348_ADDR, m)
+#define HWIO_REG_744348_OUT(v)  \
+	out_dword(HWIO_REG_744348_ADDR, v)
+#define HWIO_REG_744348_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_744348_ADDR, m, v, HWIO_REG_744348_IN);
+#define HWIO_REG_744348_P_BMSK  0x60
+#define HWIO_REG_744348_P_SHFT  0x5
+#define HWIO_REG_744348_BMSK  0x1f
+#define HWIO_REG_744348_SHFT  0
+
+#define HWIO_REG_672163_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000d034)
+#define HWIO_REG_672163_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000d034)
+#define HWIO_REG_672163_RMSK  0x1
+#define HWIO_REG_672163_SHFT  0
+#define HWIO_REG_672163_IN  in_dword_masked(\
+	HWIO_REG_672163_ADDR, HWIO_REG_672163_RMSK)
+#define HWIO_REG_672163_INM(m) \
+	in_dword_masked(HWIO_REG_672163_ADDR, m)
+#define HWIO_REG_672163_OUT(v) \
+	out_dword(HWIO_REG_672163_ADDR, v)
+#define HWIO_REG_672163_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_672163_ADDR, m, v,\
+	HWIO_REG_672163_IN);
+#define HWIO_REG_672163_ENC_TRANS_8X8_FLAG_BMSK  0x1
+#define HWIO_REG_672163_ENC_TRANS_8X8_FLAG_SHFT  0
+
+#define HWIO_REG_780908_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000d140)
+#define HWIO_REG_780908_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000d140)
+#define HWIO_REG_780908_RMSK  0x1
+#define HWIO_REG_780908_SHFT  0
+#define HWIO_REG_780908_IN  in_dword_masked(\
+	HWIO_REG_780908_ADDR, HWIO_REG_780908_RMSK)
+#define HWIO_REG_780908_INM(m)  in_dword_masked(\
+	HWIO_REG_780908_ADDR, m)
+#define HWIO_REG_780908_OUT(v)  out_dword(\
+	HWIO_REG_780908_ADDR, v)
+#define HWIO_REG_780908_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_780908_ADDR, m, v,\
+	HWIO_REG_780908_IN)
+#define HWIO_REG_780908_REG_780908_BMSK  0x1
+#define HWIO_REG_780908_REG_780908_SHFT  0
+
+#define HWIO_REG_330132_ADDR (VIDC_BLACKBIRD_REG_BASE + 0x0000e008)
+#define HWIO_REG_330132_PHYS \
+	(VIDC_BLACKBIRD_REG_BASE_PHYS + 0x0000e008)
+#define HWIO_REG_330132_RMSK  0x1
+#define HWIO_REG_330132_SHFT  0
+#define HWIO_REG_330132_IN  in_dword_masked(\
+	HWIO_REG_330132_ADDR, HWIO_REG_330132_RMSK)
+#define HWIO_REG_330132_INM(m) \
+	in_dword_masked(HWIO_REG_330132_ADDR, m)
+#define HWIO_REG_330132_OUT(v) \
+	out_dword(HWIO_REG_330132_ADDR, v)
+#define HWIO_REG_330132_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_330132_ADDR, m, v, HWIO_REG_330132_IN);
+#define HWIO_REG_330132_MPEG4_QUART_PXL_BMSK  0x1
+#define HWIO_REG_330132_MPEG4_QUART_PXL_SHFT  0
+
+
+#define VIDC_MGEN2MAXI_REG_BASE (VIDC_BASE + 0x00080000)
+#define VIDC_MGEN2MAXI_REG_BASE_PHYS 0x04480000
+
+#define HWIO_REG_916352_ADDR (VIDC_MGEN2MAXI_REG_BASE + 00000000)
+#define HWIO_REG_916352_PHYS (VIDC_MGEN2MAXI_REG_BASE_PHYS + 00000000)
+#define HWIO_REG_916352_RMSK  0xff
+#define HWIO_REG_916352_SHFT  0
+#define HWIO_REG_916352_IN  in_dword_masked(\
+	HWIO_REG_916352_ADDR, HWIO_REG_916352_RMSK)
+#define HWIO_REG_916352_INM(m) \
+	in_dword_masked(HWIO_REG_916352_ADDR, m)
+#define HWIO_REG_916352_VERSION_BMSK  0xff
+#define HWIO_REG_916352_VERSION_SHFT  0
+
+#define HWIO_REG_5519_ADDR (VIDC_MGEN2MAXI_REG_BASE + 0x00000004)
+#define HWIO_REG_5519_PHYS \
+	(VIDC_MGEN2MAXI_REG_BASE_PHYS + 0x00000004)
+#define HWIO_REG_5519_RMSK  0x1
+#define HWIO_REG_5519_SHFT  0
+#define HWIO_REG_5519_IN  in_dword_masked(\
+	HWIO_REG_5519_ADDR, HWIO_REG_5519_RMSK)
+#define HWIO_REG_5519_INM(m) \
+	in_dword_masked(HWIO_REG_5519_ADDR, m)
+#define HWIO_REG_5519_OUT(v) \
+	out_dword(HWIO_REG_5519_ADDR, v)
+#define HWIO_REG_5519_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_5519_ADDR, m, v, HWIO_REG_5519_IN);
+#define HWIO_REG_5519_AXI_AOOORD_BMSK  0x1
+#define HWIO_REG_5519_AXI_AOOORD_SHFT  0
+
+#define HWIO_REG_606364_ADDR (VIDC_MGEN2MAXI_REG_BASE + 0x00000008)
+#define HWIO_REG_606364_PHYS \
+	(VIDC_MGEN2MAXI_REG_BASE_PHYS + 0x00000008)
+#define HWIO_REG_606364_RMSK  0x1
+#define HWIO_REG_606364_SHFT  0
+#define HWIO_REG_606364_IN  in_dword_masked(\
+	HWIO_REG_606364_ADDR, HWIO_REG_606364_RMSK)
+#define HWIO_REG_606364_INM(m) \
+	in_dword_masked(HWIO_REG_606364_ADDR, m)
+#define HWIO_REG_606364_OUT(v) \
+	out_dword(HWIO_REG_606364_ADDR, v)
+#define HWIO_REG_606364_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_606364_ADDR, m, v, HWIO_REG_606364_IN);
+#define HWIO_REG_606364_AXI_AOOOWR_BMSK  0x1
+#define HWIO_REG_606364_AXI_AOOOWR_SHFT  0
+
+#define HWIO_REG_821472_ADDR (VIDC_MGEN2MAXI_REG_BASE + 0x0000000c)
+#define HWIO_REG_821472_PHYS \
+	(VIDC_MGEN2MAXI_REG_BASE_PHYS + 0x0000000c)
+#define HWIO_REG_821472_RMSK  0xf
+#define HWIO_REG_821472_SHFT  0
+#define HWIO_REG_821472_IN  in_dword_masked(\
+	HWIO_REG_821472_ADDR, HWIO_REG_821472_RMSK)
+#define HWIO_REG_821472_INM(m) \
+	in_dword_masked(HWIO_REG_821472_ADDR, m)
+#define HWIO_REG_821472_OUT(v) \
+	out_dword(HWIO_REG_821472_ADDR, v)
+#define HWIO_REG_821472_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_821472_ADDR, m, v, HWIO_REG_821472_IN);
+#define HWIO_REG_821472_AXI_TYPE_BMSK  0xf
+#define HWIO_REG_821472_AXI_TYPE_SHFT  0
+
+#define HWIO_REG_988424_ADDR \
+	(VIDC_MGEN2MAXI_REG_BASE  + 0x00000010)
+#define HWIO_REG_988424_PHYS \
+	(VIDC_MGEN2MAXI_REG_BASE_PHYS + 0x00000010)
+#define HWIO_REG_988424_RMSK  0x3
+#define HWIO_REG_988424_SHFT  0
+#define HWIO_REG_988424_IN  in_dword_masked(\
+	HWIO_REG_988424_ADDR,\
+	HWIO_REG_988424_RMSK)
+#define HWIO_REG_988424_INM(m) \
+	in_dword_masked(HWIO_REG_988424_ADDR, m)
+#define HWIO_REG_988424_OUT(v) \
+	out_dword(HWIO_REG_988424_ADDR, v)
+#define HWIO_REG_988424_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_988424_ADDR, m, v,\
+	HWIO_REG_988424_IN);
+#define HWIO_REG_988424_AXI_AREQPRIORITY_BMSK  0x3
+#define HWIO_REG_988424_AXI_AREQPRIORITY_SHFT  0
+
+#define HWIO_REG_471159_ADDR (VIDC_MGEN2MAXI_REG_BASE + 0x00000014)
+#define HWIO_REG_471159_PHYS (VIDC_MGEN2MAXI_REG_BASE_PHYS + 0x00000014)
+#define HWIO_REG_471159_RMSK  0x801f1111
+#define HWIO_REG_471159_SHFT  0
+#define HWIO_REG_471159_IN  in_dword_masked(\
+	HWIO_REG_471159_ADDR, HWIO_REG_471159_RMSK)
+#define HWIO_REG_471159_INM(m) \
+	in_dword_masked(HWIO_REG_471159_ADDR, m)
+#define HWIO_REG_471159_OUT(v) \
+	out_dword(HWIO_REG_471159_ADDR, v)
+#define HWIO_REG_471159_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_471159_ADDR, m, v, HWIO_REG_471159_IN);
+#define HWIO_REG_471159_AXI_INTR_CLR_BMSK           0x80000000
+#define HWIO_REG_471159_AXI_INTR_CLR_SHFT           0x1f
+#define HWIO_REG_471159_AXI_WDTIMEOUT_LOG2_BMSK     0x1e0000
+#define HWIO_REG_471159_AXI_WDTIMEOUT_LOG2_SHFT     0x11
+#define HWIO_REG_471159_AXI_HALT_ON_WDTIMEOUT_BMSK  0x10000
+#define HWIO_REG_471159_AXI_HALT_ON_WDTIMEOUT_SHFT  0x10
+#define HWIO_REG_471159_AXI_HALT_ON_WR_ERR_BMSK     0x1000
+#define HWIO_REG_471159_AXI_HALT_ON_WR_ERR_SHFT     0xc
+#define HWIO_REG_471159_AXI_HALT_ON_RD_ERR_BMSK     0x100
+#define HWIO_REG_471159_AXI_HALT_ON_RD_ERR_SHFT     0x8
+#define HWIO_REG_471159_AXI_RESET_BMSK              0x10
+#define HWIO_REG_471159_AXI_RESET_SHFT              0x4
+#define HWIO_REG_471159_AXI_HALT_REQ_BMSK           0x1
+#define HWIO_REG_471159_AXI_HALT_REQ_SHFT            0
+
+#define HWIO_REG_437878_ADDR (VIDC_MGEN2MAXI_REG_BASE + 0x00000018)
+#define HWIO_REG_437878_PHYS \
+	(VIDC_MGEN2MAXI_REG_BASE_PHYS + 0x00000018)
+#define HWIO_REG_437878_RMSK  0x3333
+#define HWIO_REG_437878_SHFT  0
+#define HWIO_REG_437878_IN  in_dword_masked(\
+	HWIO_REG_437878_ADDR, HWIO_REG_437878_RMSK)
+#define HWIO_REG_437878_INM(m) \
+	in_dword_masked(HWIO_REG_437878_ADDR, m)
+#define HWIO_REG_437878_AXI_WDTIMEOUT_INTR_BMSK  0x3000
+#define HWIO_REG_437878_AXI_WDTIMEOUT_INTR_SHFT  0xc
+#define HWIO_REG_437878_AXI_ERR_INTR_BMSK        0x300
+#define HWIO_REG_437878_AXI_ERR_INTR_SHFT        0x8
+#define HWIO_REG_437878_AXI_IDLE_BMSK            0x30
+#define HWIO_REG_437878_AXI_IDLE_SHFT            0x4
+#define HWIO_REG_437878_AXI_HALT_ACK_BMSK        0x3
+#define HWIO_REG_437878_AXI_HALT_ACK_SHFT        0
+
+#define HWIO_REG_736158_ADDR \
+	(VIDC_MGEN2MAXI_REG_BASE + 0x0000001c)
+#define HWIO_REG_736158_PHYS \
+	(VIDC_MGEN2MAXI_REG_BASE_PHYS + 0x0000001c)
+#define HWIO_REG_736158_RMSK  0x10fff
+#define HWIO_REG_736158_SHFT  0
+#define HWIO_REG_736158_IN  in_dword_masked(\
+	HWIO_REG_736158_ADDR,\
+	HWIO_REG_736158_RMSK)
+#define HWIO_REG_736158_INM(m) \
+	in_dword_masked(HWIO_REG_736158_ADDR, m)
+#define HWIO_REG_736158_AXI_WDTIMEOUT_BMSK  0x10000
+#define HWIO_REG_736158_AXI_WDTIMEOUT_SHFT  0x10
+#define HWIO_REG_736158_AXI_ERR_BMSK        0x800
+#define HWIO_REG_736158_AXI_ERR_SHFT        0xb
+#define HWIO_REG_736158_AXI_ERR_TYPE_BMSK   0x400
+#define HWIO_REG_736158_AXI_ERR_TYPE_SHFT   0xa
+#define HWIO_REG_736158_AXI_RESP_BMSK       0x300
+#define HWIO_REG_736158_AXI_RESP_SHFT       0x8
+#define HWIO_REG_736158_AXI_MID_BMSK        0xf0
+#define HWIO_REG_736158_AXI_MID_SHFT        0x4
+#define HWIO_REG_736158_AXI_TID_BMSK        0xf
+#define HWIO_REG_736158_AXI_TID_SHFT        0
+
+#define HWIO_REG_598415_ADDR \
+	(VIDC_MGEN2MAXI_REG_BASE + 0x00000020)
+#define HWIO_REG_598415_PHYS \
+	(VIDC_MGEN2MAXI_REG_BASE_PHYS + 0x00000020)
+#define HWIO_REG_598415_RMSK  0x10fff
+#define HWIO_REG_598415_SHFT  0
+#define HWIO_REG_598415_IN  in_dword_masked(\
+	HWIO_REG_598415_ADDR,\
+	HWIO_REG_598415_RMSK)
+#define HWIO_REG_598415_INM(m) \
+	in_dword_masked(HWIO_REG_598415_ADDR, m)
+#define HWIO_REG_598415_AXI_WDTIMEOUT_BMSK  0x10000
+#define HWIO_REG_598415_AXI_WDTIMEOUT_SHFT  0x10
+#define HWIO_REG_598415_AXI_ERR_BMSK        0x800
+#define HWIO_REG_598415_AXI_ERR_SHFT        0xb
+#define HWIO_REG_598415_AXI_ERR_TYPE_BMSK   0x400
+#define HWIO_REG_598415_AXI_ERR_TYPE_SHFT   0xa
+#define HWIO_REG_598415_AXI_RESP_BMSK       0x300
+#define HWIO_REG_598415_AXI_RESP_SHFT       0x8
+#define HWIO_REG_598415_AXI_MID_BMSK        0xf0
+#define HWIO_REG_598415_AXI_MID_SHFT        0x4
+#define HWIO_REG_598415_AXI_TID_BMSK        0xf
+#define HWIO_REG_598415_AXI_TID_SHFT        0
+
+#define HWIO_REG_439061_ADDR (VIDC_MGEN2MAXI_REG_BASE + 0x00000024)
+#define HWIO_REG_439061_PHYS \
+	(VIDC_MGEN2MAXI_REG_BASE_PHYS + 0x00000024)
+#define HWIO_REG_439061_RMSK  0x11111ff
+#define HWIO_REG_439061_SHFT  0
+#define HWIO_REG_439061_IN  in_dword_masked(\
+	HWIO_REG_439061_ADDR, HWIO_REG_439061_RMSK)
+#define HWIO_REG_439061_INM(m) \
+	in_dword_masked(HWIO_REG_439061_ADDR, m)
+#define HWIO_REG_439061_OUT(v) \
+	out_dword(HWIO_REG_439061_ADDR, v)
+#define HWIO_REG_439061_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_439061_ADDR, m, v, HWIO_REG_439061_IN);
+#define HWIO_REG_439061_AXI_RD_LAT_REP_EN_BMSK  0x1000000
+#define HWIO_REG_439061_AXI_RD_LAT_REP_EN_SHFT  0x18
+#define HWIO_REG_439061_AXI_LSFR_EN_BMSK        0x100000
+#define HWIO_REG_439061_AXI_LSFR_EN_SHFT        0x14
+#define HWIO_REG_439061_AXI_MISR_RES_BMSK       0x10000
+#define HWIO_REG_439061_AXI_MISR_RES_SHFT       0x10
+#define HWIO_REG_439061_AXI_MISR_EN_BMSK        0x1000
+#define HWIO_REG_439061_AXI_MISR_EN_SHFT        0xc
+#define HWIO_REG_439061_AXI_MISR_WD_BMSK        0x100
+#define HWIO_REG_439061_AXI_MISR_WD_SHFT        0x8
+#define HWIO_REG_439061_AXI_CTR_EN_BMSK         0x80
+#define HWIO_REG_439061_AXI_CTR_EN_SHFT         0x7
+#define HWIO_REG_439061_AXI_CTR_RES_BMSK        0x40
+#define HWIO_REG_439061_AXI_CTR_RES_SHFT        0x6
+#define HWIO_REG_439061_AXI_TEST_ARB_SEL_BMSK   0x30
+#define HWIO_REG_439061_AXI_TEST_ARB_SEL_SHFT   0x4
+#define HWIO_REG_439061_AXI_TEST_OUT_SEL_BMSK   0xf
+#define HWIO_REG_439061_AXI_TEST_OUT_SEL_SHFT   0
+
+#define HWIO_REG_573121_ADDR \
+	(VIDC_MGEN2MAXI_REG_BASE + 0x00000028)
+#define HWIO_REG_573121_PHYS \
+	(VIDC_MGEN2MAXI_REG_BASE_PHYS + 0x00000028)
+#define HWIO_REG_573121_RMSK  0xffffffff
+#define HWIO_REG_573121_SHFT  0
+#define HWIO_REG_573121_IN  in_dword_masked(\
+	HWIO_REG_573121_ADDR,\
+	HWIO_REG_573121_RMSK)
+#define HWIO_REG_573121_INM(m) \
+	in_dword_masked(HWIO_REG_573121_ADDR, m)
+#define HWIO_REG_573121_AXI_TEST_OUT_BMSK  0xffffffff
+#define HWIO_REG_573121_AXI_TEST_OUT_SHFT  0
+
+#define HWIO_REG_806413_ADDR \
+	(VIDC_MGEN2MAXI_REG_BASE + 0x0000002c)
+#define HWIO_REG_806413_PHYS \
+	(VIDC_MGEN2MAXI_REG_BASE_PHYS + 0x0000002c)
+#define HWIO_REG_806413_RMSK  0xffffffff
+#define HWIO_REG_806413_SHFT  0
+#define HWIO_REG_806413_IN  in_dword_masked(\
+	HWIO_REG_806413_ADDR,\
+	HWIO_REG_806413_RMSK)
+#define HWIO_REG_806413_INM(m) \
+	in_dword_masked(HWIO_REG_806413_ADDR, m)
+#define HWIO_REG_806413_AXI_TEST_OUT_BMSK 0xffffffff
+#define HWIO_REG_806413_AXI_TEST_OUT_SHFT 0
+
+#define HWIO_REG_804110_ADDR (VIDC_MGEN2MAXI_REG_BASE + 0x00000030)
+#define HWIO_REG_804110_PHYS (VIDC_MGEN2MAXI_REG_BASE_PHYS + 0x00000030)
+#define HWIO_REG_804110_RMSK  0xc00fffff
+#define HWIO_REG_804110_SHFT  0
+#define HWIO_REG_804110_IN  in_dword_masked(\
+	HWIO_REG_804110_ADDR, HWIO_REG_804110_RMSK)
+#define HWIO_REG_804110_INM(m) \
+	in_dword_masked(HWIO_REG_804110_ADDR, m)
+#define HWIO_REG_804110_OUT(v)  out_dword(HWIO_REG_804110_ADDR, v)
+#define HWIO_REG_804110_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_804110_ADDR, m, v, HWIO_REG_804110_IN);
+#define HWIO_REG_804110_ENABLE_BMSK                 0x80000000
+#define HWIO_REG_804110_ENABLE_SHFT                 0x1f
+#define HWIO_REG_804110_CONST_VIDC_BMSK             0x40000000
+#define HWIO_REG_804110_CONST_VIDC_SHFT             0x1e
+#define HWIO_REG_804110_VIDCV_1080P_VERSION_BMSK    0xff000
+#define HWIO_REG_804110_VIDCV_1080P_VERSION_SHFT    0xc
+#define HWIO_REG_804110_MGEN2MAXI_DATA_SEL_BMSK     0xf00
+#define HWIO_REG_804110_MGEN2MAXI_DATA_SEL_SHFT     0x8
+#define HWIO_REG_804110_MGEN2MAXI_XIN_SEL_BMSK      0x80
+#define HWIO_REG_804110_MGEN2MAXI_XIN_SEL_SHFT      0x7
+#define HWIO_REG_804110_MGEN2MAXI_ARB_SEL_BMSK      0x40
+#define HWIO_REG_804110_MGEN2MAXI_ARB_SEL_SHFT      0x6
+#define HWIO_REG_804110_MGEN2MAXI_TESTBUS_SEL_BMSK  0x30
+#define HWIO_REG_804110_MGEN2MAXI_TESTBUS_SEL_SHFT  0x4
+#define HWIO_REG_804110_AHB2AHB_TESTBUS_SEL_BMSK    0x8
+#define HWIO_REG_804110_AHB2AHB_TESTBUS_SEL_SHFT    0x3
+#define HWIO_REG_804110_MGEN2MAXI_AXI_SEL_BMSK      0x4
+#define HWIO_REG_804110_MGEN2MAXI_AXI_SEL_SHFT      0x2
+#define HWIO_REG_804110_SELECT_BMSK                 0x3
+#define HWIO_REG_804110_SELECT_SHFT                 0
+
+#define HWIO_REG_616440_ADDR \
+	(VIDC_MGEN2MAXI_REG_BASE + 0x00000034)
+#define HWIO_REG_616440_PHYS \
+	(VIDC_MGEN2MAXI_REG_BASE_PHYS + 0x00000034)
+#define HWIO_REG_616440_RMSK  0xffff
+#define HWIO_REG_616440_SHFT  0
+#define HWIO_REG_616440_IN  in_dword_masked(\
+	HWIO_REG_616440_ADDR,\
+	HWIO_REG_616440_RMSK)
+#define HWIO_REG_616440_INM(m) \
+	in_dword_masked(HWIO_REG_616440_ADDR, m)
+#define HWIO_REG_616440_OUT(v) \
+	out_dword(HWIO_REG_616440_ADDR, v)
+#define HWIO_REG_616440_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_616440_ADDR, m, v,\
+	HWIO_REG_616440_IN);
+#define HWIO_REG_616440_XBAR_IN_RD_LIM_BMSK  0xff00
+#define HWIO_REG_616440_XBAR_IN_RD_LIM_SHFT  0x8
+#define HWIO_REG_616440_XBAR_IN_WR_LIM_BMSK  0xff
+#define HWIO_REG_616440_XBAR_IN_WR_LIM_SHFT  0
+
+#define HWIO_REG_527219_ADDR \
+	(VIDC_MGEN2MAXI_REG_BASE + 0x00000038)
+#define HWIO_REG_527219_PHYS \
+	(VIDC_MGEN2MAXI_REG_BASE_PHYS + 0x00000038)
+#define HWIO_REG_527219_RMSK  0xffff
+#define HWIO_REG_527219_SHFT  0
+#define HWIO_REG_527219_IN  in_dword_masked(\
+	HWIO_REG_527219_ADDR,\
+	HWIO_REG_527219_RMSK)
+#define HWIO_REG_527219_INM(m) \
+	in_dword_masked(HWIO_REG_527219_ADDR, m)
+#define HWIO_REG_527219_OUT(v) \
+	out_dword(HWIO_REG_527219_ADDR, v)
+#define HWIO_REG_527219_OUTM(m, v) \out_dword_masked_ns(\
+	HWIO_REG_527219_ADDR, m, v,\
+	HWIO_REG_527219_IN);
+#define HWIO_REG_527219_XBAR_OUT_RD_LIM_BMSK  0xff00
+#define HWIO_REG_527219_XBAR_OUT_RD_LIM_SHFT  0x8
+#define HWIO_REG_527219_XBAR_OUT_WR_LIM_BMSK  0xff
+#define HWIO_REG_527219_XBAR_OUT_WR_LIM_SHFT  0
+
+#define HWIO_REG_922106_ADDR \
+	(VIDC_MGEN2MAXI_REG_BASE + 0x0000003c)
+#define HWIO_REG_922106_PHYS \
+	(VIDC_MGEN2MAXI_REG_BASE_PHYS + 0x0000003c)
+#define HWIO_REG_922106_RMSK  0xffff
+#define HWIO_REG_922106_SHFT  0
+#define HWIO_REG_922106_IN  in_dword_masked(\
+	HWIO_REG_922106_ADDR,\
+	HWIO_REG_922106_RMSK)
+#define HWIO_REG_922106_INM(m) \
+	in_dword_masked(HWIO_REG_922106_ADDR, m)
+#define HWIO_REG_922106_OUT(v) \
+	out_dword(HWIO_REG_922106_ADDR, v)
+#define HWIO_REG_922106_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_922106_ADDR, m, v,\
+	HWIO_REG_922106_IN);
+#define HWIO_REG_922106_XBAR_OUT_MAX_RD_BURST_BMSK  0xff00
+#define HWIO_REG_922106_XBAR_OUT_MAX_RD_BURST_SHFT  0x8
+#define HWIO_REG_922106_XBAR_OUT_MAX_WR_BURST_BMSK  0xff
+#define HWIO_REG_922106_XBAR_OUT_MAX_WR_BURST_SHFT  0
+
+#define VIDC_ENHANCE_REG_BASE (VIDC_BASE + 0x000c0000)
+#define VIDC_ENHANCE_REG_BASE_PHYS  0x044c0000
+
+#define HWIO_REG_261029_ADDR (VIDC_ENHANCE_REG_BASE + 00000000)
+#define HWIO_REG_261029_PHYS (VIDC_ENHANCE_REG_BASE_PHYS + 00000000)
+#define HWIO_REG_261029_RMSK  0x10f
+#define HWIO_REG_261029_SHFT  0
+#define HWIO_REG_261029_IN  in_dword_masked(\
+	HWIO_REG_261029_ADDR, HWIO_REG_261029_RMSK)
+#define HWIO_REG_261029_INM(m) \
+	in_dword_masked(HWIO_REG_261029_ADDR, m)
+#define HWIO_REG_261029_OUT(v) \
+	out_dword(HWIO_REG_261029_ADDR, v)
+#define HWIO_REG_261029_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_261029_ADDR, m, v, HWIO_REG_261029_IN);
+#define HWIO_REG_261029_AUTO_INC_EN_BMSK  0x100
+#define HWIO_REG_261029_AUTO_INC_EN_SHFT  0x8
+#define HWIO_REG_261029_DMI_RAM_SEL_BMSK  0xf
+#define HWIO_REG_261029_DMI_RAM_SEL_SHFT  0
+
+#define HWIO_REG_576200_ADDR (VIDC_ENHANCE_REG_BASE + 0x00000004)
+#define HWIO_REG_576200_PHYS (VIDC_ENHANCE_REG_BASE_PHYS + 0x00000004)
+#define HWIO_REG_576200_RMSK  0x7ff
+#define HWIO_REG_576200_SHFT  0
+#define HWIO_REG_576200_IN  in_dword_masked(\
+	HWIO_REG_576200_ADDR, HWIO_REG_576200_RMSK)
+#define HWIO_REG_576200_INM(m) \
+	in_dword_masked(HWIO_REG_576200_ADDR, m)
+#define HWIO_REG_576200_OUT(v) \
+	out_dword(HWIO_REG_576200_ADDR, v)
+#define HWIO_REG_576200_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_576200_ADDR, m, v, HWIO_REG_576200_IN);
+#define HWIO_REG_576200_DMI_ADDR_BMSK  0x7ff
+#define HWIO_REG_576200_DMI_ADDR_SHFT  0
+
+#define HWIO_REG_917583_ADDR (VIDC_ENHANCE_REG_BASE + 0x00000008)
+#define HWIO_REG_917583_PHYS (VIDC_ENHANCE_REG_BASE_PHYS + 0x00000008)
+#define HWIO_REG_917583_RMSK  0xffffffff
+#define HWIO_REG_917583_SHFT   0
+#define HWIO_REG_917583_IN  in_dword_masked(\
+	HWIO_REG_917583_ADDR, HWIO_REG_917583_RMSK)
+#define HWIO_REG_917583_INM(m) \
+	in_dword_masked(HWIO_REG_917583_ADDR, m)
+#define HWIO_REG_917583_OUT(v) \
+	out_dword(HWIO_REG_917583_ADDR, v)
+#define HWIO_REG_917583_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_917583_ADDR, m, v, HWIO_REG_917583_IN);
+#define HWIO_REG_917583_DMI_DATA_HI_BMSK  0xffffffff
+#define HWIO_REG_917583_DMI_DATA_HI_SHFT  0
+
+#define HWIO_REG_556274_ADDR (VIDC_ENHANCE_REG_BASE + 0x0000000c)
+#define HWIO_REG_556274_PHYS (VIDC_ENHANCE_REG_BASE_PHYS + 0x0000000c)
+#define HWIO_REG_556274_RMSK  0xffffffff
+#define HWIO_REG_556274_SHFT  0
+#define HWIO_REG_556274_IN  in_dword_masked(\
+	HWIO_REG_556274_ADDR, HWIO_REG_556274_RMSK)
+#define HWIO_REG_556274_INM(m) \
+	in_dword_masked(HWIO_REG_556274_ADDR, m)
+#define HWIO_REG_556274_OUT(v) \
+	out_dword(HWIO_REG_556274_ADDR, v)
+#define HWIO_REG_556274_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_556274_ADDR, m, v, HWIO_REG_556274_IN);
+#define HWIO_REG_556274_DMI_DATA_LO_BMSK  0xffffffff
+#define HWIO_REG_556274_DMI_DATA_LO_SHFT  0
+
+#define HWIO_REG_39703_ADDR (VIDC_ENHANCE_REG_BASE + 0x00000010)
+#define HWIO_REG_39703_PHYS \
+	(VIDC_ENHANCE_REG_BASE_PHYS + 0x00000010)
+#define HWIO_REG_39703_RMSK  0x1f
+#define HWIO_REG_39703_SHFT  0
+#define HWIO_REG_39703_IN  in_dword_masked(\
+	HWIO_REG_39703_ADDR, HWIO_REG_39703_RMSK)
+#define HWIO_REG_39703_INM(m) \
+	in_dword_masked(HWIO_REG_39703_ADDR, m)
+#define HWIO_REG_39703_OUT(v) \
+	out_dword(HWIO_REG_39703_ADDR, v)
+#define HWIO_REG_39703_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_39703_ADDR, m, v, HWIO_REG_39703_IN);
+#define HWIO_REG_39703_PIX_CACHE_TB_SEL_BMSK  0x1f
+#define HWIO_REG_39703_PIX_CACHE_TB_SEL_SHFT  0
+
+#define HWIO_REG_169013_ADDR (VIDC_ENHANCE_REG_BASE + 0x00000014)
+#define HWIO_REG_169013_PHYS (VIDC_ENHANCE_REG_BASE_PHYS + 0x00000014)
+#define HWIO_REG_169013_RMSK  0x3
+#define HWIO_REG_169013_SHFT  0
+#define HWIO_REG_169013_IN  in_dword_masked(\
+	HWIO_REG_169013_ADDR, HWIO_REG_169013_RMSK)
+#define HWIO_REG_169013_INM(m) \
+	in_dword_masked(HWIO_REG_169013_ADDR, m)
+#define HWIO_REG_169013_OUT(v) \
+	out_dword(HWIO_REG_169013_ADDR, v)
+#define HWIO_REG_169013_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_169013_ADDR, m, v, HWIO_REG_169013_IN);
+#define HWIO_REG_169013_PIX_CACHE_SW_RESET_BMSK  0x2
+#define HWIO_REG_169013_PIX_CACHE_SW_RESET_SHFT  0x1
+#define HWIO_REG_169013_CRIF_RESET_BMSK          0x1
+#define HWIO_REG_169013_CRIF_RESET_SHFT          0
+
+#define HWIO_REG_22756_ADDR (VIDC_ENHANCE_REG_BASE + 0x00000018)
+#define HWIO_REG_22756_PHYS \
+	(VIDC_ENHANCE_REG_BASE_PHYS + 0x00000018)
+#define HWIO_REG_22756_RMSK  0x133f
+#define HWIO_REG_22756_SHFT  0
+#define HWIO_REG_22756_IN  in_dword_masked(\
+	HWIO_REG_22756_ADDR, HWIO_REG_22756_RMSK)
+#define HWIO_REG_22756_INM(m) \
+	in_dword_masked(HWIO_REG_22756_ADDR, m)
+#define HWIO_REG_22756_OUT(v) \
+	out_dword(HWIO_REG_22756_ADDR, v)
+#define HWIO_REG_22756_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_22756_ADDR, m, v, HWIO_REG_22756_IN);
+#define HWIO_REG_22756_CACHE_HALT_BMSK         0x1000
+#define HWIO_REG_22756_CACHE_HALT_SHFT         0xc
+#define HWIO_REG_22756_PAGE_SIZE_BMSK          0x300
+#define HWIO_REG_22756_PAGE_SIZE_SHFT          0x8
+#define HWIO_REG_22756_STATISTICS_OFF_BMSK     0x20
+#define HWIO_REG_22756_STATISTICS_OFF_SHFT     0x5
+#define HWIO_REG_22756_CACHE_PORT_SELECT_BMSK  0x10
+#define HWIO_REG_22756_CACHE_PORT_SELECT_SHFT  0x4
+#define HWIO_REG_22756_PREFETCH_EN_BMSK        0x8
+#define HWIO_REG_22756_PREFETCH_EN_SHFT        0x3
+#define HWIO_REG_22756_SS_TILE_FORMAT_BMSK     0x4
+#define HWIO_REG_22756_SS_TILE_FORMAT_SHFT     0x2
+#define HWIO_REG_22756_CACHE_EN_BMSK           0x2
+#define HWIO_REG_22756_CACHE_EN_SHFT           0x1
+#define HWIO_REG_22756_CACHE_TAG_CLEAR_BMSK    0x1
+#define HWIO_REG_22756_CACHE_TAG_CLEAR_SHFT    0
+
+#define HWIO_REG_951731_ADDR \
+	(VIDC_ENHANCE_REG_BASE + 0x0000001c)
+#define HWIO_REG_951731_PHYS \
+	(VIDC_ENHANCE_REG_BASE_PHYS + 0x0000001c)
+#define HWIO_REG_951731_RMSK  0x7ff07ff
+#define HWIO_REG_951731_SHFT  0
+#define HWIO_REG_951731_IN  in_dword_masked(\
+	HWIO_REG_951731_ADDR,\
+	HWIO_REG_951731_RMSK)
+#define HWIO_REG_951731_INM(m) \
+	in_dword_masked(HWIO_REG_951731_ADDR, m)
+#define HWIO_REG_951731_OUT(v) \
+	out_dword(HWIO_REG_951731_ADDR, v)
+#define HWIO_REG_951731_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_951731_ADDR, m, v,\
+	HWIO_REG_951731_IN);
+#define HWIO_REG_951731_FRAME_HEIGHT_BMSK  0x7ff0000
+#define HWIO_REG_951731_FRAME_HEIGHT_SHFT  0x10
+#define HWIO_REG_951731_FRAME_WIDTH_BMSK   0x7ff
+#define HWIO_REG_951731_FRAME_WIDTH_SHFT   0
+
+#define HWIO_REG_905239_ADDR \
+	(VIDC_ENHANCE_REG_BASE + 0x00000020)
+#define HWIO_REG_905239_PHYS \
+	(VIDC_ENHANCE_REG_BASE_PHYS + 0x00000020)
+#define HWIO_REG_905239_RMSK  0x3ffff
+#define HWIO_REG_905239_SHFT  0
+#define HWIO_REG_905239_IN  in_dword_masked(\
+	HWIO_REG_905239_ADDR,\
+	HWIO_REG_905239_RMSK)
+#define HWIO_REG_905239_INM(m) \
+	in_dword_masked(HWIO_REG_905239_ADDR, m)
+#define HWIO_REG_905239_OUT(v) \
+	out_dword(HWIO_REG_905239_ADDR, v)
+#define HWIO_REG_905239_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_905239_ADDR, m, v,\
+	HWIO_REG_905239_IN);
+#define HWIO_REG_905239_LINEAR_LUMA_BMSK  0x3ffff
+#define HWIO_REG_905239_LINEAR_LUMA_SHFT  0
+#define HWIO_REG_905239_TILE_LUMA_BMSK    0xff00
+#define HWIO_REG_905239_TILE_LUMA_SHFT    0x8
+#define HWIO_REG_905239_TILE_CHROMA_BMSK  0xff
+#define HWIO_REG_905239_TILE_CHROMA_SHFT  0
+
+#define HWIO_REG_804925_ADDR(n) \
+	(VIDC_ENHANCE_REG_BASE + 0x00000024 + 4 * (n))
+#define HWIO_REG_804925_PHYS(n) \
+	(VIDC_ENHANCE_REG_BASE_PHYS + 0x00000024 + 4 * (n))
+#define HWIO_REG_804925_RMSK  0xfffffff8
+#define HWIO_REG_804925_SHFT  0
+#define HWIO_REG_804925_MAXn  0x12
+#define HWIO_REG_804925_INI(n) \
+	in_dword(HWIO_REG_804925_ADDR(n))
+#define HWIO_REG_804925_INMI(n, mask) \
+	in_dword_masked(HWIO_REG_804925_ADDR(n), mask)
+#define HWIO_REG_804925_OUTI(n, val) \
+	out_dword(HWIO_REG_804925_ADDR(n), val)
+#define HWIO_REG_804925_OUTMI(n, mask, val) \
+	out_dword_masked_ns(HWIO_REG_804925_ADDR(n),\
+	mask, val, HWIO_REG_804925_INI(n));
+#define HWIO_REG_804925_ADDR_BMSK  0xfffffff8
+#define HWIO_REG_804925_ADDR_SHFT  0x3
+
+#define HWIO_REG_41909_ADDR(n) \
+	(VIDC_ENHANCE_REG_BASE + 0x00000070 + 4 * (n))
+#define HWIO_REG_41909_PHYS(n) \
+	(VIDC_ENHANCE_REG_BASE_PHYS + 0x00000070 + 4 * (n))
+#define HWIO_REG_41909_RMSK  0xfffffff8
+#define HWIO_REG_41909_SHFT  0
+#define HWIO_REG_41909_MAXn  0x12
+#define HWIO_REG_41909_INI(n) \
+	in_dword(HWIO_REG_41909_ADDR(n))
+#define HWIO_REG_41909_INMI(n, mask) \
+	in_dword_masked(HWIO_REG_41909_ADDR(n), mask)
+#define HWIO_REG_41909_OUTI(n, val) \
+	out_dword(HWIO_REG_41909_ADDR(n), val)
+#define HWIO_REG_41909_OUTMI(n, mask, val) \
+	out_dword_masked_ns(HWIO_REG_41909_ADDR(n),\
+	mask, val, HWIO_REG_41909_INI(n));
+#define HWIO_REG_41909_ADDR_BMSK  0xfffffff8
+#define HWIO_REG_41909_ADDR_SHFT  0x3
+
+#define HWIO_REG_919904_ADDR (VIDC_ENHANCE_REG_BASE + 0x000000bc)
+#define HWIO_REG_919904_PHYS \
+	(VIDC_ENHANCE_REG_BASE_PHYS + 0x000000bc)
+#define HWIO_REG_919904_RMSK  0x1
+#define HWIO_REG_919904_SHFT  0
+#define HWIO_REG_919904_IN  in_dword_masked(\
+	HWIO_REG_919904_ADDR,\
+	HWIO_REG_919904_RMSK)
+#define HWIO_REG_919904_INM(m) \
+	in_dword_masked(HWIO_REG_919904_ADDR, m)
+#define HWIO_REG_919904_IDLE_BMSK  0x1
+#define HWIO_REG_919904_IDLE_SHFT  0
+
+#define HWIO_REG_278310_ADDR \
+	(VIDC_ENHANCE_REG_BASE + 0x000000c0)
+#define HWIO_REG_278310_PHYS \
+	(VIDC_ENHANCE_REG_BASE_PHYS + 0x000000c0)
+#define HWIO_REG_278310_RMSK  0xffffffff
+#define HWIO_REG_278310_SHFT  0
+#define HWIO_REG_278310_IN  in_dword_masked(\
+	HWIO_REG_278310_ADDR,\
+	HWIO_REG_278310_RMSK)
+#define HWIO_REG_278310_INM(m) \
+	in_dword_masked(HWIO_REG_278310_ADDR, m)
+#define HWIO_REG_278310_MISS_COUNT_BMSK  0xffffffff
+#define HWIO_REG_278310_MISS_COUNT_SHFT  0
+
+#define HWIO_REG_421222_ADDR \
+	(VIDC_ENHANCE_REG_BASE + 0x000000c4)
+#define HWIO_REG_421222_PHYS \
+	(VIDC_ENHANCE_REG_BASE_PHYS + 0x000000c4)
+#define HWIO_REG_421222_RMSK  0xffffffff
+#define HWIO_REG_421222_SHFT  0
+#define HWIO_REG_421222_IN  in_dword_masked(\
+	HWIO_REG_421222_ADDR,\
+	HWIO_REG_421222_RMSK)
+#define HWIO_REG_421222_INM(m) \
+	in_dword_masked(HWIO_REG_421222_ADDR, m)
+#define HWIO_REG_421222_HIT_COUNT_BMSK  0xffffffff
+#define HWIO_REG_421222_HIT_COUNT_SHFT  0
+
+#define HWIO_REG_609607_ADDR \
+	(VIDC_ENHANCE_REG_BASE + 0x000000c8)
+#define HWIO_REG_609607_PHYS \
+	(VIDC_ENHANCE_REG_BASE_PHYS + 0x000000c8)
+#define HWIO_REG_609607_RMSK  0xffffffff
+#define HWIO_REG_609607_SHFT  0
+#define HWIO_REG_609607_IN  in_dword_masked(\
+	HWIO_REG_609607_ADDR,\
+	HWIO_REG_609607_RMSK)
+#define HWIO_REG_609607_INM(m) \
+	in_dword_masked(HWIO_REG_609607_ADDR, m)
+#define HWIO_REG_609607_AXI_REQUEST_COUNT_BMSK  0xffffffff
+#define HWIO_REG_609607_AXI_REQUEST_COUNT_SHFT  0
+
+#define HWIO_REG_395232_ADDR \
+	(VIDC_ENHANCE_REG_BASE + 0x000000cc)
+#define HWIO_REG_395232_PHYS \
+	(VIDC_ENHANCE_REG_BASE_PHYS + 0x000000cc)
+#define HWIO_REG_395232_RMSK  0xffffffff
+#define HWIO_REG_395232_SHFT  0
+#define HWIO_REG_395232_IN  in_dword_masked(\
+	HWIO_REG_395232_ADDR,\
+	HWIO_REG_395232_RMSK)
+#define HWIO_REG_395232_INM(m) \
+	in_dword_masked(HWIO_REG_395232_ADDR, m)
+#define HWIO_REG_395232_CORE_REQUEST_COUNT_BMSK \
+	0xffffffff
+#define HWIO_REG_395232_CORE_REQUEST_COUNT_SHFT  0
+
+#define HWIO_REG_450146_ADDR \
+	(VIDC_ENHANCE_REG_BASE + 0x000000d0)
+#define HWIO_REG_450146_PHYS \
+	(VIDC_ENHANCE_REG_BASE_PHYS + 0x000000d0)
+#define HWIO_REG_450146_RMSK  0xffffffff
+#define HWIO_REG_450146_SHFT  0
+#define HWIO_REG_450146_IN  in_dword_masked(\
+	HWIO_REG_450146_ADDR,\
+	HWIO_REG_450146_RMSK)
+#define HWIO_REG_450146_INM(m) \
+	in_dword_masked(HWIO_REG_450146_ADDR, m)
+#define HWIO_REG_450146_AXI_BEAT_COUNT_BMSK  0xffffffff
+#define HWIO_REG_450146_AXI_BEAT_COUNT_SHFT  0
+
+#define HWIO_REG_610651_ADDR \
+	(VIDC_ENHANCE_REG_BASE + 0x000000d4)
+#define HWIO_REG_610651_PHYS \
+	(VIDC_ENHANCE_REG_BASE_PHYS + 0x000000d4)
+#define HWIO_REG_610651_RMSK  0xffffffff
+#define HWIO_REG_610651_SHFT  0
+#define HWIO_REG_610651_IN  in_dword_masked(\
+	HWIO_REG_610651_ADDR,\
+	HWIO_REG_610651_RMSK)
+#define HWIO_REG_610651_INM(m) \
+	in_dword_masked(HWIO_REG_610651_ADDR, m)
+#define HWIO_REG_610651_CORE_BEAT_COUNT_BMSK  0xffffffff
+#define HWIO_REG_610651_CORE_BEAT_COUNT_SHFT  0
+
+#define HWIO_REG_883784_ADDR \
+	(VIDC_ENHANCE_REG_BASE + 0x000000d8)
+#define HWIO_REG_883784_PHYS \
+	(VIDC_ENHANCE_REG_BASE_PHYS + 0x000000d8)
+#define HWIO_REG_883784_RMSK  0xffffffff
+#define HWIO_REG_883784_SHFT  0
+#define HWIO_REG_883784_IN  in_dword_masked(\
+	HWIO_REG_883784_ADDR,\
+	HWIO_REG_883784_RMSK)
+#define HWIO_REG_883784_INM(m) \
+	in_dword_masked(HWIO_REG_883784_ADDR, m)
+#define HWIO_REG_883784_OUT(v) \
+	out_dword(HWIO_REG_883784_ADDR, v)
+#define HWIO_REG_883784_OUTM(m, v)  out_dword_masked_ns(\
+	HWIO_REG_883784_ADDR, m, v,\
+	HWIO_REG_883784_IN);
+#define HWIO_REG_883784_COUNTER_BMSK    0xffffff00
+#define HWIO_REG_883784_COUNTER_SHFT    0x8
+#define HWIO_REG_883784_ID_BMSK         0xf0
+#define HWIO_REG_883784_ID_SHFT         0x4
+#define HWIO_REG_883784_IGNORE_ID_BMSK  0x8
+#define HWIO_REG_883784_IGNORE_ID_SHFT  0x3
+#define HWIO_REG_883784_INPUT_SEL_BMSK  0x6
+#define HWIO_REG_883784_INPUT_SEL_SHFT  0x1
+#define HWIO_REG_883784_MISR_EN_BMSK    0x1
+#define HWIO_REG_883784_MISR_EN_SHFT    0
+
+#define HWIO_REG_651391_ADDR(n) \
+	(VIDC_ENHANCE_REG_BASE + 0x000000dc + 4 * (n))
+#define HWIO_REG_651391_PHYS(n) \
+	(VIDC_ENHANCE_REG_BASE_PHYS + 0x000000dc + 4 * (n))
+#define HWIO_REG_651391_RMSK  0xffffffff
+#define HWIO_REG_651391_SHFT  0
+#define HWIO_REG_651391_MAXn  0x1
+#define HWIO_REG_651391_INI(n) \
+	in_dword(HWIO_REG_651391_ADDR(n))
+#define HWIO_REG_651391_INMI(n, mask) \
+	in_dword_masked(HWIO_REG_651391_ADDR(n), mask)
+#define HWIO_REG_651391_OUTI(n, val) \
+	out_dword(HWIO_REG_651391_ADDR(n), val)
+#define HWIO_REG_651391_SIGNATURE_BMSK  0xffffffff
+#define HWIO_REG_651391_SIGNATURE_SHFT  0
+
+#endif
+
diff --git a/drivers/video/msm/vidc/1080p/ddl/vidc_pix_cache.c b/drivers/video/msm/vidc/1080p/ddl/vidc_pix_cache.c
new file mode 100644
index 0000000..6870525
--- /dev/null
+++ b/drivers/video/msm/vidc/1080p/ddl/vidc_pix_cache.c
@@ -0,0 +1,349 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "vidc_hwio_reg.h"
+#include "vidc_hwio.h"
+#include "vidc_pix_cache.h"
+
+
+#define VIDC_1080P_MAX_DEC_DPB 19
+#define VIDC_TILE_MULTIPLY_FACTOR 8192
+
+void vidc_pix_cache_sw_reset(void)
+{
+	u32 sw_reset_value = 0;
+
+	VIDC_HWIO_IN(REG_169013, &sw_reset_value);
+	sw_reset_value |= HWIO_REG_169013_PIX_CACHE_SW_RESET_BMSK;
+	VIDC_HWIO_OUT(REG_169013, sw_reset_value);
+	VIDC_HWIO_IN(REG_169013, &sw_reset_value);
+	sw_reset_value &= (~HWIO_REG_169013_PIX_CACHE_SW_RESET_BMSK);
+	VIDC_HWIO_OUT(REG_169013, sw_reset_value);
+}
+
+void vidc_pix_cache_init_luma_chroma_base_addr(u32 dpb,
+	u32 *pn_dpb_luma_offset, u32 *pn_dpb_chroma_offset)
+{
+	u32 count, num_dpb_used = dpb;
+	u32 dpb_reset_value = VIDC_1080P_DEC_DPB_RESET_VALUE;
+
+	if (num_dpb_used > VIDC_1080P_MAX_DEC_DPB)
+		num_dpb_used = VIDC_1080P_MAX_DEC_DPB;
+	for (count = 0; count < VIDC_1080P_MAX_DEC_DPB; count++) {
+		if (count < num_dpb_used) {
+			if (pn_dpb_luma_offset) {
+				VIDC_HWIO_OUTI(
+					REG_804925,
+					count, pn_dpb_luma_offset[count]);
+			} else {
+				VIDC_HWIO_OUTI(
+					REG_804925,
+					count, dpb_reset_value);
+			}
+			if (pn_dpb_chroma_offset) {
+				VIDC_HWIO_OUTI(
+					REG_41909,
+					count, pn_dpb_chroma_offset[count]);
+			} else {
+				VIDC_HWIO_OUTI(
+					REG_41909,
+					count, dpb_reset_value);
+			}
+		} else {
+			VIDC_HWIO_OUTI(REG_804925,
+				count, dpb_reset_value);
+			VIDC_HWIO_OUTI(REG_41909,
+				count, dpb_reset_value);
+		}
+	}
+}
+
+void vidc_pix_cache_set_frame_range(u32 luma_size, u32 chroma_size)
+{
+	u32 frame_range;
+
+	frame_range =
+		(((luma_size / VIDC_TILE_MULTIPLY_FACTOR) & 0xFF) << 8)|
+		((chroma_size / VIDC_TILE_MULTIPLY_FACTOR) & 0xFF);
+	VIDC_HWIO_OUT(REG_905239, frame_range);
+}
+void vidc_pix_cache_set_frame_size(u32 frame_width, u32 frame_height)
+{
+   u32 frame_size;
+   frame_size =  (((u32) (frame_height << HWIO_REG_951731_FRAME_HEIGHT_SHFT) &
+		HWIO_REG_951731_FRAME_HEIGHT_BMSK) |
+		((u32) (frame_width << HWIO_REG_951731_FRAME_WIDTH_SHFT) &
+		 HWIO_REG_951731_FRAME_WIDTH_BMSK));
+   VIDC_HWIO_OUT(REG_951731, frame_size);
+}
+
+void vidc_pix_cache_init_config(
+	struct vidc_1080P_pix_cache_config *config)
+{
+	u32 cfg_reg = 0;
+
+	if (config->cache_enable)
+		cfg_reg |= HWIO_REG_22756_CACHE_EN_BMSK;
+	else
+		cfg_reg &= (~HWIO_REG_22756_CACHE_EN_BMSK);
+	if (config->port_select == VIDC_1080P_PIX_CACHE_PORT_A)
+		cfg_reg &=
+			(~HWIO_REG_22756_CACHE_PORT_SELECT_BMSK);
+	else
+		cfg_reg |= HWIO_REG_22756_CACHE_PORT_SELECT_BMSK;
+	if (!config->statistics_off)
+		cfg_reg |= HWIO_REG_22756_STATISTICS_OFF_BMSK;
+	else
+		cfg_reg &= (~HWIO_REG_22756_STATISTICS_OFF_BMSK);
+	if (config->prefetch_en)
+		cfg_reg |= HWIO_REG_22756_PREFETCH_EN_BMSK;
+	else
+		cfg_reg &= (~HWIO_REG_22756_PREFETCH_EN_BMSK);
+	cfg_reg &= (~HWIO_REG_22756_PAGE_SIZE_BMSK);
+	cfg_reg |= VIDC_SETFIELD(config->page_size,
+			HWIO_REG_22756_PAGE_SIZE_SHFT,
+			HWIO_REG_22756_PAGE_SIZE_BMSK);
+	VIDC_HWIO_OUT(REG_22756, cfg_reg);
+}
+
+void vidc_pix_cache_set_prefetch_page_limit(u32 page_size_limit)
+{
+	u32 cfg_reg = 0;
+
+	VIDC_HWIO_IN(REG_22756, &cfg_reg);
+	cfg_reg &= (~HWIO_REG_22756_PAGE_SIZE_BMSK);
+	cfg_reg |= VIDC_SETFIELD(page_size_limit,
+			HWIO_REG_22756_PAGE_SIZE_SHFT,
+			HWIO_REG_22756_PAGE_SIZE_BMSK);
+	VIDC_HWIO_OUT(REG_22756, cfg_reg);
+}
+
+void vidc_pix_cache_enable_prefetch(u32 prefetch_enable)
+{
+	u32 cfg_reg = 0;
+
+	VIDC_HWIO_IN(REG_22756, &cfg_reg);
+	if (prefetch_enable)
+		cfg_reg |= HWIO_REG_22756_PREFETCH_EN_BMSK;
+	else
+		cfg_reg &= (~HWIO_REG_22756_PREFETCH_EN_BMSK);
+	VIDC_HWIO_OUT(REG_22756, cfg_reg);
+}
+
+void vidc_pix_cache_disable_statistics(u32 statistics_off)
+{
+	u32 cfg_reg = 0;
+
+	VIDC_HWIO_IN(REG_22756, &cfg_reg);
+	if (!statistics_off)
+		cfg_reg |= HWIO_REG_22756_STATISTICS_OFF_BMSK;
+	else
+		cfg_reg &= (~HWIO_REG_22756_STATISTICS_OFF_BMSK);
+	VIDC_HWIO_OUT(REG_22756, cfg_reg);
+}
+
+void vidc_pix_cache_set_port(
+	enum vidc_1080P_pix_cache_port_sel port_select)
+{
+	u32 cfg_reg = 0;
+
+	VIDC_HWIO_IN(REG_22756, &cfg_reg);
+	if (port_select == VIDC_1080P_PIX_CACHE_PORT_A)
+		cfg_reg &=
+			(~HWIO_REG_22756_CACHE_PORT_SELECT_BMSK);
+	else
+		cfg_reg |= HWIO_REG_22756_CACHE_PORT_SELECT_BMSK;
+	VIDC_HWIO_OUT(REG_22756, cfg_reg);
+}
+
+void vidc_pix_cache_enable_cache(u32 cache_enable)
+{
+	u32 cfg_reg = 0;
+
+	VIDC_HWIO_IN(REG_22756, &cfg_reg);
+	if (cache_enable)
+		cfg_reg |= HWIO_REG_22756_CACHE_EN_BMSK;
+	else
+		cfg_reg &= (~HWIO_REG_22756_CACHE_EN_BMSK);
+	VIDC_HWIO_OUT(REG_22756, cfg_reg);
+}
+
+void vidc_pix_cache_clear_cache_tags(void)
+{
+	u32 cfg_reg = 0;
+
+	VIDC_HWIO_IN(REG_22756, &cfg_reg);
+	cfg_reg |= HWIO_REG_22756_CACHE_TAG_CLEAR_BMSK;
+	VIDC_HWIO_OUT(REG_22756, cfg_reg);
+	VIDC_HWIO_IN(REG_22756, &cfg_reg);
+	cfg_reg &= (~HWIO_REG_22756_CACHE_TAG_CLEAR_BMSK);
+	VIDC_HWIO_OUT(REG_22756, cfg_reg);
+}
+
+void vidc_pix_cache_set_halt(u32 halt_enable)
+{
+	u32 cfg_reg = 0;
+
+	VIDC_HWIO_IN(REG_22756, &cfg_reg);
+	if (halt_enable)
+		cfg_reg |= HWIO_REG_22756_CACHE_HALT_BMSK;
+	else
+		cfg_reg &= (~HWIO_REG_22756_CACHE_HALT_BMSK);
+	VIDC_HWIO_OUT(REG_22756, cfg_reg);
+}
+
+void vidc_pix_cache_get_status_idle(u32 *idle_status)
+{
+	VIDC_HWIO_IN(REG_919904, idle_status);
+}
+
+void vidc_pix_cache_set_ram(u32 ram_select)
+{
+	u32 dmi_cfg_reg = 0;
+
+	VIDC_HWIO_IN(REG_261029, &dmi_cfg_reg);
+	dmi_cfg_reg &= (~HWIO_REG_261029_DMI_RAM_SEL_BMSK);
+	dmi_cfg_reg |= VIDC_SETFIELD(ram_select,
+			HWIO_REG_261029_AUTO_INC_EN_SHFT,
+			HWIO_REG_261029_DMI_RAM_SEL_BMSK);
+	VIDC_HWIO_OUT(REG_261029, dmi_cfg_reg);
+}
+
+void vidc_pix_cache_set_auto_inc_ram_addr(u32 auto_inc_enable)
+{
+	u32 dmi_cfg_reg = 0;
+
+	VIDC_HWIO_IN(REG_261029, &dmi_cfg_reg);
+	if (auto_inc_enable)
+		dmi_cfg_reg |= HWIO_REG_261029_AUTO_INC_EN_BMSK;
+	else
+		dmi_cfg_reg &= (~HWIO_REG_261029_AUTO_INC_EN_BMSK);
+	VIDC_HWIO_OUT(REG_261029, dmi_cfg_reg);
+}
+
+
+void vidc_pix_cache_read_ram_data(u32 src_ram_address,
+	u32 ram_size, u32 *dest_address)
+{
+	u32 count, dmi_cfg_reg = 0;
+
+	VIDC_HWIO_IN(REG_261029, &dmi_cfg_reg);
+	VIDC_HWIO_OUT(REG_576200, src_ram_address);
+	vidc_pix_cache_set_auto_inc_ram_addr(1);
+	for (count = 0; count < ram_size; count++) {
+		VIDC_HWIO_IN(REG_556274, dest_address);
+		dest_address++;
+		VIDC_HWIO_IN(REG_917583, dest_address);
+		dest_address++;
+	}
+	VIDC_HWIO_OUT(REG_261029, dmi_cfg_reg);
+}
+
+void vidc_pix_cache_write_ram_data(u32 *src_address,
+	u32 ram_size, u32 dest_ram_address)
+{
+	u32 count, dmi_cfg_reg = 0;
+
+	VIDC_HWIO_IN(REG_261029, &dmi_cfg_reg);
+	VIDC_HWIO_OUT(REG_576200, dest_ram_address);
+	vidc_pix_cache_set_auto_inc_ram_addr(1);
+	for (count = 0; count < ram_size; count++) {
+		VIDC_HWIO_OUT(REG_917583, *src_address);
+		src_address++;
+		VIDC_HWIO_OUT(REG_556274, *src_address);
+		src_address++;
+	}
+	VIDC_HWIO_OUT(REG_261029, dmi_cfg_reg);
+}
+
+void vidc_pix_cache_get_statistics(
+	struct vidc_1080P_pix_cache_statistics *statistics)
+{
+	VIDC_HWIO_IN(REG_278310,
+		&statistics->access_miss);
+	VIDC_HWIO_IN(REG_421222,
+		&statistics->access_hit);
+	VIDC_HWIO_IN(REG_609607,
+		&statistics->axi_req);
+	VIDC_HWIO_IN(REG_395232,
+		&statistics->core_req);
+	VIDC_HWIO_IN(REG_450146,
+		&statistics->axi_bus);
+	VIDC_HWIO_IN(REG_610651,
+		&statistics->core_bus);
+}
+
+void vidc_pix_cache_enable_misr(u32 misr_enable)
+{
+   u32 misr_cfg_reg = 0;
+
+	VIDC_HWIO_IN(REG_883784, &misr_cfg_reg);
+	if (misr_enable)
+		misr_cfg_reg |= HWIO_REG_883784_MISR_EN_BMSK;
+	else
+		misr_cfg_reg &=
+			(~HWIO_REG_883784_MISR_EN_BMSK);
+	VIDC_HWIO_OUT(REG_261029, misr_cfg_reg);
+}
+
+void vidc_pix_cache_set_misr_interface(u32 input_select)
+{
+	u32 misr_cfg_reg = 0;
+
+	VIDC_HWIO_IN(REG_883784, &misr_cfg_reg);
+	misr_cfg_reg &= (~HWIO_REG_883784_INPUT_SEL_BMSK);
+	misr_cfg_reg |= VIDC_SETFIELD(input_select,
+			HWIO_REG_883784_INPUT_SEL_SHFT,
+			HWIO_REG_883784_INPUT_SEL_BMSK);
+	VIDC_HWIO_OUT(REG_261029, misr_cfg_reg);
+}
+
+void vidc_pix_cache_set_misr_id_filtering(
+	struct vidc_1080P_pix_cache_misr_id_filtering *filter_id)
+{
+	u32 misr_cfg_reg = 0;
+
+	VIDC_HWIO_IN(REG_883784, &misr_cfg_reg);
+	if (filter_id->ignore_id)
+		misr_cfg_reg |=
+			HWIO_REG_883784_IGNORE_ID_BMSK;
+	else
+		misr_cfg_reg &=
+			(~HWIO_REG_883784_IGNORE_ID_BMSK);
+	misr_cfg_reg &= (~HWIO_REG_883784_ID_BMSK);
+	misr_cfg_reg |= VIDC_SETFIELD(filter_id->id,
+			HWIO_REG_883784_ID_SHFT,
+			HWIO_REG_883784_ID_BMSK);
+	VIDC_HWIO_OUT(REG_261029, misr_cfg_reg);
+}
+
+void vidc_pix_cache_set_misr_filter_trans(u32 no_of_trans)
+{
+	u32 misr_cfg_reg = 0;
+
+	VIDC_HWIO_IN(REG_883784, &misr_cfg_reg);
+	misr_cfg_reg &= (~HWIO_REG_883784_COUNTER_BMSK);
+	misr_cfg_reg |= VIDC_SETFIELD(no_of_trans,
+			HWIO_REG_883784_COUNTER_SHFT,
+			HWIO_REG_883784_COUNTER_BMSK);
+	VIDC_HWIO_OUT(REG_261029, misr_cfg_reg);
+}
+
+void vidc_pix_cache_get_misr_signatures(
+	struct vidc_1080P_pix_cache_misr_signature *signatures)
+{
+	VIDC_HWIO_INI(REG_651391, 0,
+		&signatures->signature0);
+	VIDC_HWIO_INI(REG_651391, 1,
+		&signatures->signature1);
+}
diff --git a/drivers/video/msm/vidc/1080p/ddl/vidc_pix_cache.h b/drivers/video/msm/vidc/1080p/ddl/vidc_pix_cache.h
new file mode 100644
index 0000000..e8a93a1
--- /dev/null
+++ b/drivers/video/msm/vidc/1080p/ddl/vidc_pix_cache.h
@@ -0,0 +1,87 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VIDEO_CORE_PIXCACHE_
+#define _VIDEO_CORE_PIXCACHE_
+
+
+#include "vidc.h"
+
+#define VIDC_1080P_DEC_DPB_RESET_VALUE 0xFFFFFFF8
+
+enum vidc_1080P_pix_cache_port_sel{
+	VIDC_1080P_PIX_CACHE_PORT_A = 0,
+	VIDC_1080P_PIX_CACHE_PORT_B = 1,
+	VIDC_1080P_PIX_CACHE_PORT_32BIT = 0x7FFFFFFF
+};
+enum vidc_1080P_pix_cache_page_size{
+	VIDC_1080P_PIX_CACHE_PAGE_SIZE_1K = 0,
+	VIDC_1080P_PIX_CACHE_PAGE_SIZE_2K = 1,
+	VIDC_1080P_PIX_CACHE_PAGE_SIZE_4K = 2
+};
+struct vidc_1080P_pix_cache_config{
+	u32 cache_enable;
+	u32 prefetch_en;
+	enum vidc_1080P_pix_cache_port_sel port_select;
+	u32 statistics_off;
+	enum vidc_1080P_pix_cache_page_size page_size;
+};
+struct vidc_1080P_pix_cache_statistics{
+	u32 access_miss;
+	u32 access_hit;
+	u32 axi_req;
+	u32 core_req;
+	u32 axi_bus;
+	u32 core_bus;
+};
+struct vidc_1080P_pix_cache_misr_id_filtering{
+	u32 ignore_id;
+	u32 id;
+};
+struct vidc_1080P_pix_cache_misr_signature{
+	u32 signature0;
+	u32 signature1;
+};
+
+void vidc_pix_cache_sw_reset(void);
+void vidc_pix_cache_init_luma_chroma_base_addr(u32 dpb,
+	u32 *pn_dpb_luma_offset, u32 *pn_dpb_chroma_offset);
+void vidc_pix_cache_set_frame_range(u32 luma_size, u32 chroma_size);
+void vidc_pix_cache_set_frame_size(u32 frame_width, u32 frame_height);
+void vidc_pix_cache_init_config(
+	struct vidc_1080P_pix_cache_config *config);
+void vidc_pix_cache_set_prefetch_page_limit(u32 page_size_limit);
+void vidc_pix_cache_enable_prefetch(u32 prefetch_enable);
+void vidc_pix_cache_disable_statistics(u32 statistics_off);
+void vidc_pix_cache_set_port(
+	enum vidc_1080P_pix_cache_port_sel port_select);
+void vidc_pix_cache_enable_cache(u32 cache_enable);
+void vidc_pix_cache_clear_cache_tags(void);
+void vidc_pix_cache_set_halt(u32 halt_enable);
+void vidc_pix_cache_get_status_idle(u32 *idle_status);
+void vidc_pix_cache_set_ram(u32 ram_select);
+void vidc_pix_cache_set_auto_inc_ram_addr(u32 auto_inc_enable);
+void vidc_pix_cache_read_ram_data(u32 src_ram_address, u32 ram_size,
+	u32 *dest_address);
+void vidc_pix_cache_write_ram_data(u32 *src_address, u32 ram_size,
+	u32 dest_ram_address);
+void vidc_pix_cache_get_statistics(
+	struct vidc_1080P_pix_cache_statistics *statistics);
+void vidc_pix_cache_enable_misr(u32 misr_enable);
+void vidc_pix_cache_set_misr_interface(u32 input_select);
+void vidc_pix_cache_set_misr_id_filtering(
+	struct vidc_1080P_pix_cache_misr_id_filtering *filter_id);
+void vidc_pix_cache_set_misr_filter_trans(u32 no_of_trans);
+void vidc_pix_cache_get_misr_signatures(
+	struct vidc_1080P_pix_cache_misr_signature *signatures);
+#endif
diff --git a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c
new file mode 100644
index 0000000..d90b8ca
--- /dev/null
+++ b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c
@@ -0,0 +1,991 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/firmware.h>
+#include <linux/pm_qos.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
+#include <mach/clk.h>
+#include <mach/msm_memtypes.h>
+#include <linux/interrupt.h>
+#include <linux/memory_alloc.h>
+#include <asm/sizes.h>
+#include <media/msm/vidc_init.h>
+#include "vidc.h"
+#include "vcd_res_tracker.h"
+
+#define PIL_FW_BASE_ADDR 0x9fe00000
+#define PIL_FW_SIZE 0x200000
+
+static unsigned int vidc_clk_table[3] = {
+	48000000, 133330000, 200000000
+};
+static unsigned int restrk_mmu_subsystem[] =	{
+		MSM_SUBSYSTEM_VIDEO, MSM_SUBSYSTEM_VIDEO_FWARE};
+static struct res_trk_context resource_context;
+
+#define VIDC_FW	"vidc_1080p.fw"
+#define VIDC_FW_SIZE SZ_1M
+
+struct res_trk_vidc_mmu_clk {
+	char *mmu_clk_name;
+	struct clk *mmu_clk;
+};
+
+static struct res_trk_vidc_mmu_clk vidc_mmu_clks[] = {
+	{"mdp_iommu_clk"}, {"rot_iommu_clk"},
+	{"vcodec_iommu0_clk"}, {"vcodec_iommu1_clk"},
+	{"smmu_iface_clk"}
+};
+
+unsigned char *vidc_video_codec_fw;
+u32 vidc_video_codec_fw_size;
+static u32 res_trk_get_clk(void);
+static void res_trk_put_clk(void);
+
+static void *res_trk_pmem_map
+	(struct ddl_buf_addr *addr, size_t sz, u32 alignment)
+{
+	u32 offset = 0, flags = 0;
+	u32 index = 0;
+	struct ddl_context *ddl_context;
+	struct msm_mapped_buffer *mapped_buffer = NULL;
+	int ret = 0;
+	unsigned long iova = 0;
+	unsigned long buffer_size  = 0;
+	unsigned long *kernel_vaddr = NULL;
+
+	ddl_context = ddl_get_context();
+	if (res_trk_get_enable_ion() && addr->alloc_handle) {
+		kernel_vaddr = (unsigned long *) ion_map_kernel(
+					ddl_context->video_ion_client,
+					addr->alloc_handle, UNCACHED);
+		if (IS_ERR_OR_NULL(kernel_vaddr)) {
+			DDL_MSG_ERROR("%s():DDL ION client map failed\n",
+						 __func__);
+			goto ion_bail_out;
+		}
+		addr->virtual_base_addr = (u8 *) kernel_vaddr;
+		ret = ion_map_iommu(ddl_context->video_ion_client,
+				addr->alloc_handle,
+				VIDEO_DOMAIN,
+				VIDEO_FIRMWARE_POOL,
+				SZ_4K,
+				0,
+				&iova,
+				&buffer_size,
+				UNCACHED, 0);
+		if (ret) {
+			DDL_MSG_ERROR("%s():DDL ION client iommu map failed\n",
+						 __func__);
+			goto ion_unmap_bail_out;
+		}
+		addr->mapped_buffer = NULL;
+		addr->physical_base_addr = (u8 *)iova;
+		addr->align_physical_addr = (u8 *) DDL_ALIGN((u32)
+		addr->physical_base_addr, alignment);
+		offset = (u32)(addr->align_physical_addr -
+				addr->physical_base_addr);
+		addr->align_virtual_addr = addr->virtual_base_addr + offset;
+		addr->buffer_size = buffer_size;
+	} else {
+		if (!res_trk_check_for_sec_session()) {
+			if (!addr->alloced_phys_addr) {
+				pr_err(" %s() alloced addres NULL", __func__);
+				goto bail_out;
+			}
+			flags = MSM_SUBSYSTEM_MAP_IOVA |
+				MSM_SUBSYSTEM_MAP_KADDR;
+			if (alignment == DDL_KILO_BYTE(128))
+					index = 1;
+			else if (alignment > SZ_4K)
+				flags |= MSM_SUBSYSTEM_ALIGN_IOVA_8K;
+			addr->mapped_buffer =
+			msm_subsystem_map_buffer(
+			(unsigned long)addr->alloced_phys_addr,
+			sz, flags, &restrk_mmu_subsystem[index],
+			sizeof(restrk_mmu_subsystem[index])/
+				sizeof(unsigned int));
+			if (IS_ERR(addr->mapped_buffer)) {
+				pr_err(" %s() buffer map failed", __func__);
+				goto bail_out;
+			}
+			mapped_buffer = addr->mapped_buffer;
+			if (!mapped_buffer->vaddr || !mapped_buffer->iova[0]) {
+				pr_err("%s() map buffers failed\n", __func__);
+				goto bail_out;
+			}
+			addr->physical_base_addr =
+				 (u8 *)mapped_buffer->iova[0];
+			addr->virtual_base_addr =
+					mapped_buffer->vaddr;
+		} else {
+			addr->physical_base_addr =
+				(u8 *) addr->alloced_phys_addr;
+			addr->virtual_base_addr =
+				(u8 *)addr->alloced_phys_addr;
+		}
+		addr->align_physical_addr = (u8 *) DDL_ALIGN((u32)
+		addr->physical_base_addr, alignment);
+		offset = (u32)(addr->align_physical_addr -
+				addr->physical_base_addr);
+		addr->align_virtual_addr = addr->virtual_base_addr + offset;
+		addr->buffer_size = sz;
+	}
+	return addr->virtual_base_addr;
+bail_out:
+	if (IS_ERR(addr->mapped_buffer))
+		msm_subsystem_unmap_buffer(addr->mapped_buffer);
+	return NULL;
+ion_unmap_bail_out:
+	if (!IS_ERR_OR_NULL(addr->alloc_handle)) {
+		ion_unmap_kernel(resource_context.
+			res_ion_client,	addr->alloc_handle);
+	}
+ion_bail_out:
+	return NULL;
+}
+
+static void res_trk_pmem_free(struct ddl_buf_addr *addr)
+{
+	struct ddl_context *ddl_context;
+	ddl_context = ddl_get_context();
+	if (ddl_context->video_ion_client) {
+		if (addr && addr->alloc_handle) {
+			ion_free(ddl_context->video_ion_client,
+			 addr->alloc_handle);
+			addr->alloc_handle = NULL;
+		}
+	} else {
+		if (addr->mapped_buffer)
+			msm_subsystem_unmap_buffer(addr->mapped_buffer);
+		if (addr->alloced_phys_addr)
+			free_contiguous_memory_by_paddr(
+			(unsigned long)addr->alloced_phys_addr);
+	}
+	memset(addr, 0 , sizeof(struct ddl_buf_addr));
+}
+static int res_trk_pmem_alloc
+	(struct ddl_buf_addr *addr, size_t sz, u32 alignment)
+{
+	u32 alloc_size;
+	struct ddl_context *ddl_context;
+	int rc = 0;
+	DBG_PMEM("\n%s() IN: Requested alloc size(%u)", __func__, (u32)sz);
+	if (!addr) {
+		DDL_MSG_ERROR("\n%s() Invalid Parameters", __func__);
+		rc = -EINVAL;
+		goto bail_out;
+	}
+	ddl_context = ddl_get_context();
+	res_trk_set_mem_type(addr->mem_type);
+	alloc_size = (sz + alignment);
+	if (res_trk_get_enable_ion()) {
+		if (!res_trk_is_cp_enabled() ||
+			 !res_trk_check_for_sec_session()) {
+			if (!ddl_context->video_ion_client)
+				ddl_context->video_ion_client =
+					res_trk_get_ion_client();
+			if (!ddl_context->video_ion_client) {
+				DDL_MSG_ERROR(
+				"%s() :DDL ION Client Invalid handle\n",
+						__func__);
+				rc = -ENOMEM;
+				goto bail_out;
+			}
+			alloc_size = (alloc_size+4095) & ~4095;
+			addr->alloc_handle = ion_alloc(
+					ddl_context->video_ion_client,
+					 alloc_size, SZ_4K,
+					res_trk_get_mem_type());
+			if (IS_ERR_OR_NULL(addr->alloc_handle)) {
+				DDL_MSG_ERROR("%s() :DDL ION alloc failed\n",
+						__func__);
+				rc = -ENOMEM;
+				goto bail_out;
+			}
+		} else {
+			addr->alloc_handle = NULL;
+			addr->alloced_phys_addr = PIL_FW_BASE_ADDR;
+			addr->buffer_size = sz;
+		}
+	} else {
+		addr->alloced_phys_addr = (phys_addr_t)
+			allocate_contiguous_memory_nomap(alloc_size,
+					res_trk_get_mem_type(), SZ_4K);
+		if (!addr->alloced_phys_addr) {
+			DDL_MSG_ERROR("%s() : acm alloc failed (%d)\n",
+					__func__, alloc_size);
+			rc = -ENOMEM;
+			goto bail_out;
+		}
+		addr->buffer_size = sz;
+		return rc;
+	}
+bail_out:
+	return rc;
+}
+
+static void res_trk_pmem_unmap(struct ddl_buf_addr *addr)
+{
+	if (!addr) {
+		pr_err("%s() invalid args\n", __func__);
+		return;
+	}
+	if (!IS_ERR_OR_NULL(addr->alloc_handle)) {
+		if (addr->physical_base_addr) {
+			ion_unmap_kernel(resource_context.res_ion_client,
+					addr->alloc_handle);
+			if (!res_trk_check_for_sec_session()) {
+				ion_unmap_iommu(resource_context.res_ion_client,
+				addr->alloc_handle,
+				VIDEO_DOMAIN,
+				VIDEO_FIRMWARE_POOL);
+			}
+			addr->virtual_base_addr = NULL;
+			addr->physical_base_addr = NULL;
+		}
+	} else if (addr->mapped_buffer)
+		msm_subsystem_unmap_buffer(addr->mapped_buffer);
+	addr->mapped_buffer = NULL;
+}
+
+static u32 res_trk_get_clk()
+{
+	if (resource_context.vcodec_clk ||
+		resource_context.vcodec_pclk) {
+		VCDRES_MSG_ERROR("%s() Clock reference exists\n",
+						__func__);
+		goto bail_out;
+	}
+	resource_context.vcodec_clk = clk_get(resource_context.device,
+		"core_clk");
+	if (IS_ERR(resource_context.vcodec_clk)) {
+		VCDRES_MSG_ERROR("%s(): core_clk get failed\n",
+						__func__);
+		goto bail_out;
+	}
+	 resource_context.vcodec_pclk = clk_get(resource_context.device,
+		"iface_clk");
+	if (IS_ERR(resource_context.vcodec_pclk)) {
+		VCDRES_MSG_ERROR("%s(): iface_clk get failed\n",
+						__func__);
+		goto release_vcodec_clk;
+	}
+	if (clk_set_rate(resource_context.vcodec_clk,
+		vidc_clk_table[0])) {
+		VCDRES_MSG_ERROR("%s(): set rate failed in power up\n",
+						__func__);
+		goto release_vcodec_pclk;
+	}
+	return true;
+release_vcodec_pclk:
+	clk_put(resource_context.vcodec_pclk);
+	resource_context.vcodec_pclk = NULL;
+release_vcodec_clk:
+	clk_put(resource_context.vcodec_clk);
+	resource_context.vcodec_clk = NULL;
+bail_out:
+	return false;
+}
+
+static void res_trk_put_clk()
+{
+	if (resource_context.vcodec_clk)
+		clk_put(resource_context.vcodec_clk);
+	if (resource_context.vcodec_pclk)
+		clk_put(resource_context.vcodec_pclk);
+	resource_context.vcodec_clk = NULL;
+	resource_context.vcodec_pclk = NULL;
+}
+
+static u32 res_trk_shutdown_vidc(void)
+{
+	mutex_lock(&resource_context.lock);
+	if (resource_context.clock_enabled) {
+		mutex_unlock(&resource_context.lock);
+		VCDRES_MSG_LOW("\n Calling CLK disable in Power Down\n");
+		res_trk_disable_clocks();
+		mutex_lock(&resource_context.lock);
+	}
+	res_trk_put_clk();
+	if (resource_context.footswitch) {
+		if (regulator_disable(resource_context.footswitch))
+			VCDRES_MSG_ERROR("Regulator disable failed\n");
+		regulator_put(resource_context.footswitch);
+		resource_context.footswitch = NULL;
+	}
+	if (pm_runtime_put(resource_context.device) < 0)
+		VCDRES_MSG_ERROR("Error : pm_runtime_put failed");
+	mutex_unlock(&resource_context.lock);
+	return true;
+}
+
+u32 res_trk_enable_clocks(void)
+{
+	VCDRES_MSG_LOW("\n in res_trk_enable_clocks()");
+	mutex_lock(&resource_context.lock);
+	if (!resource_context.clock_enabled) {
+		VCDRES_MSG_LOW("Enabling IRQ in %s()\n", __func__);
+		enable_irq(resource_context.irq_num);
+		VCDRES_MSG_LOW("%s(): Enabling the clocks\n", __func__);
+		if (resource_context.vcodec_clk &&
+			resource_context.vcodec_pclk) {
+			if (clk_prepare_enable(resource_context.vcodec_pclk)) {
+				VCDRES_MSG_ERROR("vidc pclk Enable fail\n");
+				goto bail_out;
+			}
+			if (clk_prepare_enable(resource_context.vcodec_clk)) {
+				VCDRES_MSG_ERROR("vidc core clk Enable fail\n");
+				goto vidc_disable_pclk;
+			}
+
+			VCDRES_MSG_LOW("%s(): Clocks enabled!\n", __func__);
+		} else {
+		   VCDRES_MSG_ERROR("%s(): Clocks enable failed!\n",
+			__func__);
+		   goto bail_out;
+		}
+	}
+	resource_context.clock_enabled = 1;
+	mutex_unlock(&resource_context.lock);
+	return true;
+vidc_disable_pclk:
+	clk_disable_unprepare(resource_context.vcodec_pclk);
+bail_out:
+	mutex_unlock(&resource_context.lock);
+	return false;
+}
+
+static u32 res_trk_sel_clk_rate(unsigned long hclk_rate)
+{
+	u32 status = true;
+	mutex_lock(&resource_context.lock);
+	if (clk_set_rate(resource_context.vcodec_clk,
+		hclk_rate)) {
+		VCDRES_MSG_ERROR("vidc hclk set rate failed\n");
+		status = false;
+	} else
+		resource_context.vcodec_clk_rate = hclk_rate;
+	mutex_unlock(&resource_context.lock);
+	return status;
+}
+
+u32 res_trk_get_clk_rate(unsigned long *phclk_rate)
+{
+	u32 status = true;
+	mutex_lock(&resource_context.lock);
+	if (phclk_rate) {
+		*phclk_rate = clk_get_rate(resource_context.vcodec_clk);
+		if (!(*phclk_rate)) {
+			VCDRES_MSG_ERROR("vidc hclk get rate failed\n");
+			status = false;
+		}
+	} else
+		status = false;
+	mutex_unlock(&resource_context.lock);
+	return status;
+}
+
+u32 res_trk_disable_clocks(void)
+{
+	u32 status = false;
+	VCDRES_MSG_LOW("in res_trk_disable_clocks()\n");
+	mutex_lock(&resource_context.lock);
+	if (resource_context.clock_enabled) {
+		VCDRES_MSG_LOW("Disabling IRQ in %s()\n", __func__);
+		disable_irq_nosync(resource_context.irq_num);
+		VCDRES_MSG_LOW("%s(): Disabling the clocks ...\n", __func__);
+		resource_context.clock_enabled = 0;
+		if (resource_context.vcodec_clk)
+			clk_disable_unprepare(resource_context.vcodec_clk);
+		if (resource_context.vcodec_pclk)
+			clk_disable_unprepare(resource_context.vcodec_pclk);
+		status = true;
+	}
+	mutex_unlock(&resource_context.lock);
+	return status;
+}
+
+static u32 res_trk_vidc_pwr_up(void)
+{
+	mutex_lock(&resource_context.lock);
+
+	if (pm_runtime_get(resource_context.device) < 0) {
+		VCDRES_MSG_ERROR("Error : pm_runtime_get failed\n");
+		goto bail_out;
+	}
+	if (!resource_context.footswitch)
+		resource_context.footswitch =
+			regulator_get(resource_context.device, "vdd");
+	if (IS_ERR(resource_context.footswitch)) {
+		VCDRES_MSG_ERROR("foot switch get failed\n");
+		resource_context.footswitch = NULL;
+	} else
+		regulator_enable(resource_context.footswitch);
+	if (!res_trk_get_clk())
+		goto rel_vidc_pm_runtime;
+	mutex_unlock(&resource_context.lock);
+	return true;
+
+rel_vidc_pm_runtime:
+	if (pm_runtime_put(resource_context.device) < 0)
+		VCDRES_MSG_ERROR("Error : pm_runtime_put failed");
+bail_out:
+	mutex_unlock(&resource_context.lock);
+	return false;
+}
+
+static struct ion_client *res_trk_create_ion_client(void){
+	struct ion_client *video_client;
+	video_client = msm_ion_client_create(-1, "video_client");
+	return video_client;
+}
+
+int res_trk_enable_footswitch(void)
+{
+	int rc = 0;
+	mutex_lock(&resource_context.lock);
+	if (!resource_context.footswitch)
+		resource_context.footswitch = regulator_get(NULL, "fs_ved");
+	if (IS_ERR(resource_context.footswitch)) {
+		VCDRES_MSG_ERROR("foot switch get failed\n");
+		resource_context.footswitch = NULL;
+		rc = -EINVAL;
+	} else
+		rc = regulator_enable(resource_context.footswitch);
+	mutex_unlock(&resource_context.lock);
+	return rc;
+}
+
+int res_trk_disable_footswitch(void)
+{
+	mutex_lock(&resource_context.lock);
+	if (resource_context.footswitch) {
+		if (regulator_disable(resource_context.footswitch))
+			VCDRES_MSG_ERROR("Regulator disable failed\n");
+		regulator_put(resource_context.footswitch);
+		resource_context.footswitch = NULL;
+	}
+	mutex_unlock(&resource_context.lock);
+	return 0;
+}
+
+u32 res_trk_power_up(void)
+{
+	VCDRES_MSG_LOW("clk_regime_rail_enable");
+	VCDRES_MSG_LOW("clk_regime_sel_rail_control");
+#ifdef CONFIG_MSM_BUS_SCALING
+	resource_context.pcl = 0;
+	if (resource_context.vidc_bus_client_pdata) {
+		resource_context.pcl = msm_bus_scale_register_client(
+			resource_context.vidc_bus_client_pdata);
+		VCDRES_MSG_LOW("%s(), resource_context.pcl = %x", __func__,
+			 resource_context.pcl);
+	}
+	if (resource_context.pcl == 0) {
+		dev_err(resource_context.device,
+			"register bus client returned NULL\n");
+		return false;
+	}
+#endif
+	return res_trk_vidc_pwr_up();
+}
+
+u32 res_trk_power_down(void)
+{
+	VCDRES_MSG_LOW("clk_regime_rail_disable");
+	res_trk_pmem_unmap(&resource_context.firmware_addr);
+	res_trk_pmem_free(&resource_context.firmware_addr);
+#ifdef CONFIG_MSM_BUS_SCALING
+	msm_bus_scale_client_update_request(resource_context.pcl, 0);
+	msm_bus_scale_unregister_client(resource_context.pcl);
+#endif
+	VCDRES_MSG_MED("res_trk_power_down():: Calling "
+		"res_trk_shutdown_vidc()\n");
+	return res_trk_shutdown_vidc();
+}
+
+u32 res_trk_get_max_perf_level(u32 *pn_max_perf_lvl)
+{
+	if (!pn_max_perf_lvl) {
+		VCDRES_MSG_ERROR("%s(): pn_max_perf_lvl is NULL\n",
+			__func__);
+		return false;
+	}
+	*pn_max_perf_lvl = RESTRK_1080P_MAX_PERF_LEVEL;
+	return true;
+}
+
+#ifdef CONFIG_MSM_BUS_SCALING
+int res_trk_update_bus_perf_level(struct vcd_dev_ctxt *dev_ctxt, u32 perf_level)
+{
+	struct vcd_clnt_ctxt *cctxt_itr = NULL;
+	u32 enc_perf_level = 0, dec_perf_level = 0;
+	u32 bus_clk_index, client_type = 0;
+	int rc = 0;
+
+	cctxt_itr = dev_ctxt->cctxt_list_head;
+	while (cctxt_itr) {
+		if (cctxt_itr->decoding)
+			dec_perf_level += cctxt_itr->reqd_perf_lvl;
+		else
+			enc_perf_level += cctxt_itr->reqd_perf_lvl;
+		cctxt_itr = cctxt_itr->next;
+	}
+	if (!enc_perf_level)
+		client_type = 1;
+	if (perf_level <= RESTRK_1080P_VGA_PERF_LEVEL)
+		bus_clk_index = 0;
+	else if (perf_level <= RESTRK_1080P_720P_PERF_LEVEL)
+		bus_clk_index = 1;
+	else
+		bus_clk_index = 2;
+
+	if (dev_ctxt->reqd_perf_lvl + dev_ctxt->curr_perf_lvl == 0)
+		bus_clk_index = 2;
+	bus_clk_index = (bus_clk_index << 1) + (client_type + 1);
+	VCDRES_MSG_LOW("%s(), bus_clk_index = %d", __func__, bus_clk_index);
+	VCDRES_MSG_LOW("%s(),context.pcl = %x", __func__, resource_context.pcl);
+	VCDRES_MSG_LOW("%s(), bus_perf_level = %x", __func__, perf_level);
+	rc = msm_bus_scale_client_update_request(resource_context.pcl,
+		bus_clk_index);
+	return rc;
+}
+#endif
+
+u32 res_trk_set_perf_level(u32 req_perf_lvl, u32 *pn_set_perf_lvl,
+	struct vcd_dev_ctxt *dev_ctxt)
+{
+	u32 vidc_freq = 0;
+	if (!pn_set_perf_lvl || !dev_ctxt) {
+		VCDRES_MSG_ERROR("%s(): NULL pointer! dev_ctxt(%p)\n",
+			__func__, dev_ctxt);
+		return false;
+	}
+	VCDRES_MSG_LOW("%s(), req_perf_lvl = %d", __func__, req_perf_lvl);
+#ifdef CONFIG_MSM_BUS_SCALING
+	if (!res_trk_update_bus_perf_level(dev_ctxt, req_perf_lvl) < 0) {
+		VCDRES_MSG_ERROR("%s(): update buf perf level failed\n",
+			__func__);
+		return false;
+	}
+
+#endif
+	if (dev_ctxt->reqd_perf_lvl + dev_ctxt->curr_perf_lvl == 0)
+		req_perf_lvl = RESTRK_1080P_MAX_PERF_LEVEL;
+
+	if (req_perf_lvl <= RESTRK_1080P_VGA_PERF_LEVEL) {
+		vidc_freq = vidc_clk_table[0];
+		*pn_set_perf_lvl = RESTRK_1080P_VGA_PERF_LEVEL;
+	} else if (req_perf_lvl <= RESTRK_1080P_720P_PERF_LEVEL) {
+		vidc_freq = vidc_clk_table[1];
+		*pn_set_perf_lvl = RESTRK_1080P_720P_PERF_LEVEL;
+	} else {
+		vidc_freq = vidc_clk_table[2];
+		*pn_set_perf_lvl = RESTRK_1080P_MAX_PERF_LEVEL;
+	}
+	resource_context.perf_level = *pn_set_perf_lvl;
+	VCDRES_MSG_MED("VIDC: vidc_freq = %u, req_perf_lvl = %u\n",
+		vidc_freq, req_perf_lvl);
+#ifdef USE_RES_TRACKER
+    if (req_perf_lvl != RESTRK_1080P_MIN_PERF_LEVEL) {
+		VCDRES_MSG_MED("%s(): Setting vidc freq to %u\n",
+			__func__, vidc_freq);
+		if (!res_trk_sel_clk_rate(vidc_freq)) {
+			VCDRES_MSG_ERROR("%s(): res_trk_sel_clk_rate FAILED\n",
+				__func__);
+			*pn_set_perf_lvl = 0;
+			return false;
+		}
+	}
+#endif
+	VCDRES_MSG_MED("%s() set perl level : %d", __func__, *pn_set_perf_lvl);
+	return true;
+}
+
+u32 res_trk_get_curr_perf_level(u32 *pn_perf_lvl)
+{
+	unsigned long freq;
+
+	if (!pn_perf_lvl) {
+		VCDRES_MSG_ERROR("%s(): pn_perf_lvl is NULL\n",
+			__func__);
+		return false;
+	}
+	VCDRES_MSG_LOW("clk_regime_msm_get_clk_freq_hz");
+	if (!res_trk_get_clk_rate(&freq)) {
+		VCDRES_MSG_ERROR("%s(): res_trk_get_clk_rate FAILED\n",
+			__func__);
+		*pn_perf_lvl = 0;
+		return false;
+	}
+	*pn_perf_lvl = resource_context.perf_level;
+	VCDRES_MSG_MED("%s(): freq = %lu, *pn_perf_lvl = %u", __func__,
+		freq, *pn_perf_lvl);
+	return true;
+}
+
+u32 res_trk_download_firmware(void)
+{
+	const struct firmware *fw_video = NULL;
+	int rc = 0;
+	u32 status = true;
+
+	VCDRES_MSG_HIGH("%s(): Request firmware download\n",
+		__func__);
+	mutex_lock(&resource_context.lock);
+	rc = request_firmware(&fw_video, VIDC_FW,
+		resource_context.device);
+	if (rc) {
+		VCDRES_MSG_ERROR("request_firmware for %s error %d\n",
+				VIDC_FW, rc);
+		status = false;
+		goto bail_out;
+	}
+	vidc_video_codec_fw = (unsigned char *)fw_video->data;
+	vidc_video_codec_fw_size = (u32) fw_video->size;
+bail_out:
+	mutex_unlock(&resource_context.lock);
+	return status;
+}
+
+void res_trk_init(struct device *device, u32 irq)
+{
+	if (resource_context.device || resource_context.irq_num ||
+		!device) {
+		VCDRES_MSG_ERROR("%s() Resource Tracker Init error\n",
+			__func__);
+	} else {
+		memset(&resource_context, 0, sizeof(resource_context));
+		mutex_init(&resource_context.lock);
+		mutex_init(&resource_context.secure_lock);
+		resource_context.device = device;
+		resource_context.irq_num = irq;
+		resource_context.vidc_platform_data =
+			(struct msm_vidc_platform_data *) device->platform_data;
+		if (resource_context.vidc_platform_data) {
+			resource_context.memtype =
+			resource_context.vidc_platform_data->memtype;
+			resource_context.fw_mem_type =
+			resource_context.vidc_platform_data->memtype;
+			resource_context.cmd_mem_type =
+			resource_context.vidc_platform_data->memtype;
+			if (resource_context.vidc_platform_data->enable_ion) {
+				resource_context.res_ion_client =
+					res_trk_create_ion_client();
+				if (!(resource_context.res_ion_client)) {
+					VCDRES_MSG_ERROR("%s()ION createfail\n",
+							__func__);
+					return;
+				}
+				resource_context.fw_mem_type =
+				ION_MM_FIRMWARE_HEAP_ID;
+				resource_context.cmd_mem_type =
+				ION_CP_MFC_HEAP_ID;
+			}
+			resource_context.disable_dmx =
+			resource_context.vidc_platform_data->disable_dmx;
+			resource_context.disable_fullhd =
+			resource_context.vidc_platform_data->disable_fullhd;
+#ifdef CONFIG_MSM_BUS_SCALING
+			resource_context.vidc_bus_client_pdata =
+			resource_context.vidc_platform_data->
+				vidc_bus_client_pdata;
+#endif
+		} else {
+			resource_context.memtype = -1;
+			resource_context.disable_dmx = 0;
+		}
+		resource_context.core_type = VCD_CORE_1080P;
+		resource_context.firmware_addr.mem_type = DDL_FW_MEM;
+	}
+}
+
+u32 res_trk_get_core_type(void){
+	return resource_context.core_type;
+}
+
+u32 res_trk_get_firmware_addr(struct ddl_buf_addr *firm_addr)
+{
+	int rc = 0;
+	size_t size = 0;
+	if (!firm_addr || resource_context.firmware_addr.mapped_buffer) {
+		pr_err("%s() invalid params", __func__);
+		return -EINVAL;
+	}
+	if (res_trk_is_cp_enabled() && res_trk_check_for_sec_session())
+		size = PIL_FW_SIZE;
+	else
+		size = VIDC_FW_SIZE;
+
+	if (res_trk_pmem_alloc(&resource_context.firmware_addr,
+				size, DDL_KILO_BYTE(128))) {
+		pr_err("%s() Firmware buffer allocation failed",
+				__func__);
+		memset(&resource_context.firmware_addr, 0,
+				sizeof(resource_context.firmware_addr));
+		rc = -ENOMEM;
+		goto fail_alloc;
+	}
+	if (!res_trk_pmem_map(&resource_context.firmware_addr,
+		resource_context.firmware_addr.buffer_size,
+		DDL_KILO_BYTE(128))) {
+		pr_err("%s() Firmware buffer mapping failed",
+			   __func__);
+		rc = -ENOMEM;
+		goto fail_map;
+	}
+	memcpy(firm_addr, &resource_context.firmware_addr,
+		sizeof(struct ddl_buf_addr));
+	return 0;
+fail_map:
+	res_trk_pmem_free(&resource_context.firmware_addr);
+fail_alloc:
+	return rc;
+}
+
+void res_trk_release_fw_addr(void)
+{
+	res_trk_pmem_unmap(&resource_context.firmware_addr);
+	res_trk_pmem_free(&resource_context.firmware_addr);
+}
+
+int res_trk_check_for_sec_session(void)
+{
+	int rc;
+	mutex_lock(&resource_context.secure_lock);
+	rc = resource_context.secure_session;
+	mutex_unlock(&resource_context.secure_lock);
+	return rc;
+}
+
+int res_trk_get_mem_type(void)
+{
+	int mem_type = -1;
+	switch (resource_context.res_mem_type) {
+	case DDL_FW_MEM:
+		mem_type = ION_HEAP(resource_context.fw_mem_type);
+		return mem_type;
+	case DDL_MM_MEM:
+		mem_type = resource_context.memtype;
+		break;
+	case DDL_CMD_MEM:
+		if (res_trk_check_for_sec_session())
+			mem_type = resource_context.cmd_mem_type;
+		else
+			mem_type = resource_context.memtype;
+		break;
+	default:
+		return mem_type;
+	}
+	if (resource_context.vidc_platform_data->enable_ion) {
+		if (res_trk_check_for_sec_session()) {
+			mem_type = ION_HEAP(mem_type);
+	if (resource_context.res_mem_type != DDL_FW_MEM)
+		mem_type |= ION_SECURE;
+	else if (res_trk_is_cp_enabled())
+		mem_type |= ION_SECURE;
+	} else
+		mem_type = (ION_HEAP(mem_type) |
+			ION_HEAP(ION_IOMMU_HEAP_ID));
+	}
+	return mem_type;
+}
+
+u32 res_trk_is_cp_enabled(void)
+{
+	if (resource_context.vidc_platform_data->cp_enabled)
+		return 1;
+	else
+		return 0;
+}
+
+u32 res_trk_get_enable_ion(void)
+{
+	if (resource_context.vidc_platform_data->enable_ion)
+		return 1;
+	else
+		return 0;
+}
+
+struct ion_client *res_trk_get_ion_client(void)
+{
+	return resource_context.res_ion_client;
+}
+
+u32 res_trk_get_disable_dmx(void){
+	return resource_context.disable_dmx;
+}
+
+u32 res_trk_get_min_dpb_count(void){
+	return resource_context.vidc_platform_data->cont_mode_dpb_count;
+}
+
+void res_trk_set_mem_type(enum ddl_mem_area mem_type)
+{
+	resource_context.res_mem_type = mem_type;
+	return;
+}
+
+u32 res_trk_get_disable_fullhd(void)
+{
+	return resource_context.disable_fullhd;
+}
+
+int res_trk_enable_iommu_clocks(void)
+{
+	int ret = 0, i;
+	if (resource_context.mmu_clks_on) {
+		pr_err(" %s: Clocks are already on", __func__);
+		return -EINVAL;
+	}
+	resource_context.mmu_clks_on = 1;
+	for (i = 0; i < ARRAY_SIZE(vidc_mmu_clks); i++) {
+		vidc_mmu_clks[i].mmu_clk = clk_get(resource_context.device,
+			vidc_mmu_clks[i].mmu_clk_name);
+		if (IS_ERR(vidc_mmu_clks[i].mmu_clk)) {
+			pr_err(" %s: Get failed for clk %s", __func__,
+				   vidc_mmu_clks[i].mmu_clk_name);
+			ret = PTR_ERR(vidc_mmu_clks[i].mmu_clk);
+		}
+		if (!ret) {
+			ret = clk_prepare_enable(vidc_mmu_clks[i].mmu_clk);
+			if (ret) {
+				clk_put(vidc_mmu_clks[i].mmu_clk);
+				vidc_mmu_clks[i].mmu_clk = NULL;
+			}
+		}
+		if (ret) {
+			for (i--; i >= 0; i--) {
+				clk_disable_unprepare(vidc_mmu_clks[i].mmu_clk);
+				clk_put(vidc_mmu_clks[i].mmu_clk);
+				vidc_mmu_clks[i].mmu_clk = NULL;
+			}
+			resource_context.mmu_clks_on = 0;
+			pr_err("%s() clocks enable failed", __func__);
+			break;
+		}
+	}
+	return ret;
+}
+
+int res_trk_disable_iommu_clocks(void)
+{
+	int i;
+	if (!resource_context.mmu_clks_on) {
+		pr_err(" %s: clks are already off", __func__);
+		return -EINVAL;
+	}
+	resource_context.mmu_clks_on = 0;
+	for (i = 0; i < ARRAY_SIZE(vidc_mmu_clks); i++) {
+		clk_disable_unprepare(vidc_mmu_clks[i].mmu_clk);
+		clk_put(vidc_mmu_clks[i].mmu_clk);
+		vidc_mmu_clks[i].mmu_clk = NULL;
+	}
+	return 0;
+}
+
+void res_trk_secure_unset(void)
+{
+	mutex_lock(&resource_context.secure_lock);
+	resource_context.secure_session--;
+	mutex_unlock(&resource_context.secure_lock);
+}
+
+void res_trk_secure_set(void)
+{
+	mutex_lock(&resource_context.secure_lock);
+	resource_context.secure_session++;
+	mutex_unlock(&resource_context.secure_lock);
+}
+
+int res_trk_open_secure_session()
+{
+	int rc;
+
+	if (res_trk_check_for_sec_session() == 1) {
+		mutex_lock(&resource_context.secure_lock);
+		pr_err("Securing...\n");
+		rc = res_trk_enable_iommu_clocks();
+		if (rc) {
+			pr_err("IOMMU clock enabled failed while open");
+			goto error_open;
+		}
+		msm_ion_secure_heap(ION_HEAP(resource_context.memtype));
+		msm_ion_secure_heap(ION_HEAP(resource_context.cmd_mem_type));
+		res_trk_disable_iommu_clocks();
+		mutex_unlock(&resource_context.secure_lock);
+	}
+	return 0;
+error_open:
+	mutex_unlock(&resource_context.secure_lock);
+	return rc;
+}
+
+int res_trk_close_secure_session()
+{
+	int rc;
+	if (res_trk_check_for_sec_session() == 1) {
+		pr_err("Unsecuring....\n");
+		mutex_lock(&resource_context.secure_lock);
+		rc = res_trk_enable_iommu_clocks();
+		if (rc) {
+			pr_err("IOMMU clock enabled failed while close");
+			goto error_close;
+		}
+		msm_ion_unsecure_heap(ION_HEAP(resource_context.cmd_mem_type));
+		msm_ion_unsecure_heap(ION_HEAP(resource_context.memtype));
+		res_trk_disable_iommu_clocks();
+		mutex_unlock(&resource_context.secure_lock);
+	}
+	return 0;
+error_close:
+	mutex_unlock(&resource_context.secure_lock);
+	return rc;
+}
+
+u32 get_res_trk_perf_level(enum vcd_perf_level perf_level)
+{
+	u32 res_trk_perf_level;
+	switch (perf_level) {
+	case VCD_PERF_LEVEL0:
+		res_trk_perf_level = RESTRK_1080P_VGA_PERF_LEVEL;
+		break;
+	case VCD_PERF_LEVEL1:
+		res_trk_perf_level = RESTRK_1080P_720P_PERF_LEVEL;
+		break;
+	case VCD_PERF_LEVEL2:
+		res_trk_perf_level = RESTRK_1080P_MAX_PERF_LEVEL;
+		break;
+	default:
+		VCD_MSG_ERROR("Invalid perf level: %d\n", perf_level);
+		res_trk_perf_level = -EINVAL;
+	}
+	return res_trk_perf_level;
+}
+
+u32 res_trk_estimate_perf_level(u32 pn_perf_lvl)
+{
+	VCDRES_MSG_MED("%s(), req_perf_lvl = %d", __func__, pn_perf_lvl);
+	if ((pn_perf_lvl >= RESTRK_1080P_VGA_PERF_LEVEL) &&
+		(pn_perf_lvl < RESTRK_1080P_720P_PERF_LEVEL)) {
+		return RESTRK_1080P_720P_PERF_LEVEL;
+	} else if ((pn_perf_lvl >= RESTRK_1080P_720P_PERF_LEVEL) &&
+			(pn_perf_lvl < RESTRK_1080P_MAX_PERF_LEVEL)) {
+		return RESTRK_1080P_MAX_PERF_LEVEL;
+	} else {
+		return pn_perf_lvl;
+	}
+}
diff --git a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.h b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.h
new file mode 100644
index 0000000..bf8607d
--- /dev/null
+++ b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.h
@@ -0,0 +1,77 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef _VIDEO_720P_RESOURCE_TRACKER_H_
+#define _VIDEO_720P_RESOURCE_TRACKER_H_
+
+#include <linux/regulator/consumer.h>
+#include <linux/ion.h>
+#include "vcd_res_tracker_api.h"
+#ifdef CONFIG_MSM_BUS_SCALING
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
+#endif
+#include <mach/board.h>
+
+#define RESTRK_1080P_VGA_PERF_LEVEL    VCD_MIN_PERF_LEVEL
+#define RESTRK_1080P_720P_PERF_LEVEL   108000
+#define RESTRK_1080P_1080P_PERF_LEVEL  244800
+
+#define RESTRK_1080P_MIN_PERF_LEVEL RESTRK_1080P_VGA_PERF_LEVEL
+#define RESTRK_1080P_MAX_PERF_LEVEL RESTRK_1080P_1080P_PERF_LEVEL
+
+struct res_trk_context {
+	struct device *device;
+	u32 irq_num;
+	struct mutex lock;
+	struct clk *vcodec_clk;
+	struct clk *vcodec_pclk;
+	unsigned long vcodec_clk_rate;
+	unsigned int clock_enabled;
+	unsigned int perf_level;
+	struct regulator *footswitch;
+	struct msm_vidc_platform_data *vidc_platform_data;
+	int memtype;
+	int fw_mem_type;
+	int cmd_mem_type;
+#ifdef CONFIG_MSM_BUS_SCALING
+	struct msm_bus_scale_pdata *vidc_bus_client_pdata;
+	uint32_t     pcl;
+#endif
+	u32 core_type;
+	struct ddl_buf_addr firmware_addr;
+	struct ion_client *res_ion_client;
+	u32 disable_dmx;
+	u32 disable_fullhd;
+	enum ddl_mem_area res_mem_type;
+	u32 mmu_clks_on;
+	u32 secure_session;
+	struct mutex secure_lock;
+};
+
+#if DEBUG
+
+#define VCDRES_MSG_LOW(xx_fmt...)	printk(KERN_INFO "\n\t* " xx_fmt)
+#define VCDRES_MSG_MED(xx_fmt...)	printk(KERN_INFO "\n  * " xx_fmt)
+
+#else
+
+#define VCDRES_MSG_LOW(xx_fmt...)
+#define VCDRES_MSG_MED(xx_fmt...)
+
+#endif
+
+#define VCDRES_MSG_HIGH(xx_fmt...)	printk(KERN_WARNING "\n" xx_fmt)
+#define VCDRES_MSG_ERROR(xx_fmt...)	printk(KERN_ERR "\n err: " xx_fmt)
+#define VCDRES_MSG_FATAL(xx_fmt...)	printk(KERN_ERR "\n<FATAL> " xx_fmt)
+
+#endif
diff --git a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker_api.h b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker_api.h
new file mode 100644
index 0000000..2ae2512
--- /dev/null
+++ b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker_api.h
@@ -0,0 +1,53 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef _VIDEO_720P_RESOURCE_TRACKER_API_H_
+#define _VIDEO_720P_RESOURCE_TRACKER_API_H_
+
+#include "vcd_core.h"
+#include "vcd_ddl.h"
+#include "vcd_ddl_utils.h"
+
+void res_trk_init(struct device *device, u32 irq);
+u32 res_trk_power_up(void);
+u32 res_trk_power_down(void);
+u32 res_trk_enable_clocks(void);
+u32 res_trk_disable_clocks(void);
+u32 res_trk_get_max_perf_level(u32 *pn_max_perf_lvl);
+u32 res_trk_set_perf_level(u32 req_perf_lvl, u32 *pn_set_perf_lvl,
+	struct vcd_dev_ctxt *dev_ctxt);
+u32 res_trk_get_curr_perf_level(u32 *pn_perf_lvl);
+u32 res_trk_download_firmware(void);
+u32 res_trk_get_core_type(void);
+u32 res_trk_get_firmware_addr(struct ddl_buf_addr *firm_addr);
+int res_trk_get_mem_type(void);
+u32 res_trk_get_enable_ion(void);
+u32 res_trk_is_cp_enabled(void);
+u32 res_trk_get_disable_fullhd(void);
+struct ion_client *res_trk_get_ion_client(void);
+u32 res_trk_get_disable_dmx(void);
+u32 res_trk_get_min_dpb_count(void);
+void res_trk_set_mem_type(enum ddl_mem_area mem_type);
+int res_trk_enable_iommu_clocks(void);
+int res_trk_disable_iommu_clocks(void);
+int res_trk_check_for_sec_session(void);
+int res_trk_open_secure_session(void);
+int res_trk_close_secure_session(void);
+void res_trk_secure_set(void);
+void res_trk_secure_unset(void);
+u32 get_res_trk_perf_level(enum vcd_perf_level);
+int res_trk_enable_footswitch(void);
+int res_trk_disable_footswitch(void);
+void res_trk_release_fw_addr(void);
+u32 res_trk_estimate_perf_level(u32 pn_perf_lvl);
+u32 res_trk_get_clk_rate(unsigned long *phclk_rate);
+#endif
diff --git a/drivers/video/msm/vidc/720p/ddl/vcd_ddl.c b/drivers/video/msm/vidc/720p/ddl/vcd_ddl.c
new file mode 100644
index 0000000..02b2369
--- /dev/null
+++ b/drivers/video/msm/vidc/720p/ddl/vcd_ddl.c
@@ -0,0 +1,628 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <media/msm/vidc_type.h>
+#include "vcd_ddl_utils.h"
+#include "vcd_ddl_metadata.h"
+#include "vcd_res_tracker_api.h"
+
+u32 ddl_device_init(struct ddl_init_config *ddl_init_config,
+		    void *client_data)
+{
+	struct ddl_context *ddl_context;
+	u32 status = VCD_S_SUCCESS;
+
+	if ((!ddl_init_config) ||
+	    (!ddl_init_config->ddl_callback) ||
+	    (!ddl_init_config->core_virtual_base_addr)
+	    ) {
+		VIDC_LOGERR_STRING("ddl_dev_init:Bad_argument");
+		return VCD_ERR_ILLEGAL_PARM;
+	}
+
+	ddl_context = ddl_get_context();
+
+	if (DDL_IS_INITIALIZED(ddl_context)) {
+		VIDC_LOGERR_STRING("ddl_dev_init:Multiple_init");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	if (DDL_IS_BUSY(ddl_context)) {
+		VIDC_LOGERR_STRING("ddl_dev_init:Ddl_busy");
+		return VCD_ERR_BUSY;
+	}
+
+	DDL_MEMSET(ddl_context, 0, sizeof(struct ddl_context));
+
+	DDL_BUSY(ddl_context);
+	ddl_context->memtype = res_trk_get_mem_type();
+	if (ddl_context->memtype == -1) {
+		VIDC_LOGERR_STRING("ddl_dev_init:Invalid Memtype");
+		return VCD_ERR_ILLEGAL_PARM;
+	}
+	ddl_context->ddl_callback = ddl_init_config->ddl_callback;
+	ddl_context->interrupt_clr = ddl_init_config->interrupt_clr;
+	ddl_context->core_virtual_base_addr =
+	    ddl_init_config->core_virtual_base_addr;
+	ddl_context->client_data = client_data;
+
+	vidc_720p_set_device_virtual_base(ddl_context->
+					   core_virtual_base_addr);
+
+	ddl_context->current_ddl = NULL;
+	ddl_move_command_state(ddl_context, DDL_CMD_INVALID);
+
+	ddl_client_transact(DDL_INIT_CLIENTS, NULL);
+
+	ddl_pmem_alloc(&ddl_context->context_buf_addr,
+		       DDL_CONTEXT_MEMORY, DDL_LINEAR_BUFFER_ALIGN_BYTES);
+	if (!ddl_context->context_buf_addr.virtual_base_addr) {
+		VIDC_LOGERR_STRING("ddl_dev_init:Context_alloc_fail");
+		status = VCD_ERR_ALLOC_FAIL;
+	}
+	if (!status) {
+		ddl_pmem_alloc(&ddl_context->db_line_buffer,
+			       DDL_DB_LINE_BUF_SIZE,
+			       DDL_TILE_BUFFER_ALIGN_BYTES);
+		if (!ddl_context->db_line_buffer.virtual_base_addr) {
+			VIDC_LOGERR_STRING("ddl_dev_init:Line_buf_alloc_fail");
+			status = VCD_ERR_ALLOC_FAIL;
+		}
+	}
+
+	if (!status) {
+		ddl_pmem_alloc(&ddl_context->data_partition_tempbuf,
+					   DDL_MPEG4_DATA_PARTITION_BUF_SIZE,
+					   DDL_TILE_BUFFER_ALIGN_BYTES);
+		if (ddl_context->data_partition_tempbuf.virtual_base_addr \
+			== NULL) {
+			VIDC_LOGERR_STRING
+				("ddl_dev_init:Data_partition_buf_alloc_fail");
+			status = VCD_ERR_ALLOC_FAIL;
+		}
+   }
+
+   if (!status) {
+
+		ddl_pmem_alloc(&ddl_context->metadata_shared_input,
+					   DDL_METADATA_TOTAL_INPUTBUFSIZE,
+					   DDL_LINEAR_BUFFER_ALIGN_BYTES);
+		if (!ddl_context->metadata_shared_input.virtual_base_addr) {
+			VIDC_LOGERR_STRING
+			("ddl_dev_init:metadata_shared_input_alloc_fail");
+			status = VCD_ERR_ALLOC_FAIL;
+		}
+	 }
+
+	if (!status) {
+		ddl_pmem_alloc(&ddl_context->dbg_core_dump, \
+					   DDL_DBG_CORE_DUMP_SIZE,  \
+					   DDL_LINEAR_BUFFER_ALIGN_BYTES);
+		if (!ddl_context->dbg_core_dump.virtual_base_addr) {
+			VIDC_LOGERR_STRING
+				("ddl_dev_init:dbg_core_dump_alloc_failed");
+			status = VCD_ERR_ALLOC_FAIL;
+		}
+		ddl_context->enable_dbg_core_dump = 0;
+	}
+
+	if (!status && !vcd_fw_init()) {
+		VIDC_LOGERR_STRING("ddl_dev_init:fw_init_failed");
+		status = VCD_ERR_ALLOC_FAIL;
+	}
+	if (status) {
+		ddl_release_context_buffers(ddl_context);
+		DDL_IDLE(ddl_context);
+		return status;
+	}
+
+	ddl_move_command_state(ddl_context, DDL_CMD_DMA_INIT);
+
+	ddl_core_init(ddl_context);
+
+	return status;
+}
+
+u32 ddl_device_release(void *client_data)
+{
+	struct ddl_context *ddl_context;
+
+	ddl_context = ddl_get_context();
+
+	if (DDL_IS_BUSY(ddl_context)) {
+		VIDC_LOGERR_STRING("ddl_dev_rel:Ddl_busy");
+		return VCD_ERR_BUSY;
+	}
+
+	if (!DDL_IS_INITIALIZED(ddl_context)) {
+		VIDC_LOGERR_STRING("ddl_dev_rel:Not_inited");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+
+	if (!ddl_client_transact(DDL_ACTIVE_CLIENT, NULL)) {
+		VIDC_LOGERR_STRING("ddl_dev_rel:Client_present_err");
+		return VCD_ERR_CLIENT_PRESENT;
+	}
+	DDL_BUSY(ddl_context);
+
+	ddl_context->device_state = DDL_DEVICE_NOTINIT;
+	ddl_context->client_data = client_data;
+	ddl_move_command_state(ddl_context, DDL_CMD_INVALID);
+	vidc_720p_stop_fw();
+
+	VIDC_LOG_STRING("FW_ENDDONE");
+	ddl_release_context_buffers(ddl_context);
+
+	DDL_IDLE(ddl_context);
+
+	return VCD_S_SUCCESS;
+}
+
+u32 ddl_open(u32 **ddl_handle, u32 decoding)
+{
+	struct ddl_context *ddl_context;
+	struct ddl_client_context *ddl;
+	u32 status;
+
+	if (!ddl_handle) {
+		VIDC_LOGERR_STRING("ddl_open:Bad_handle");
+		return VCD_ERR_BAD_HANDLE;
+	}
+
+	ddl_context = ddl_get_context();
+
+	if (!DDL_IS_INITIALIZED(ddl_context)) {
+		VIDC_LOGERR_STRING("ddl_open:Not_inited");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+
+	status = ddl_client_transact(DDL_GET_CLIENT, &ddl);
+
+	if (status) {
+		VIDC_LOGERR_STRING("ddl_open:Client_trasac_failed");
+		return status;
+	}
+
+	ddl_move_client_state(ddl, DDL_CLIENT_OPEN);
+
+	ddl->codec_data.hdr.decoding = decoding;
+	ddl->decoding = decoding;
+
+	ddl_set_default_meta_data_hdr(ddl);
+
+	ddl_set_initial_default_values(ddl);
+
+	*ddl_handle = (u32 *) ddl;
+	return VCD_S_SUCCESS;
+}
+
+u32 ddl_close(u32 **ddl_handle)
+{
+	struct ddl_context *ddl_context;
+	struct ddl_client_context **ddl =
+	    (struct ddl_client_context **)ddl_handle;
+
+	if (!ddl || !*ddl) {
+		VIDC_LOGERR_STRING("ddl_close:Bad_handle");
+		return VCD_ERR_BAD_HANDLE;
+	}
+
+	ddl_context = ddl_get_context();
+
+	if (!DDL_IS_INITIALIZED(ddl_context)) {
+		VIDC_LOGERR_STRING("ddl_close:Not_inited");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+
+	if (!DDLCLIENT_STATE_IS(*ddl, DDL_CLIENT_OPEN)) {
+		VIDC_LOGERR_STRING("ddl_close:Not_in_open_state");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+
+	ddl_move_client_state(*ddl, DDL_CLIENT_INVALID);
+	if ((*ddl)->decoding) {
+		vcd_fw_transact(false, true,
+			(*ddl)->codec_data.decoder.codec.codec);
+	} else {
+		vcd_fw_transact(false, false,
+			(*ddl)->codec_data.encoder.codec.codec);
+	}
+	ddl_client_transact(DDL_FREE_CLIENT, ddl);
+
+	return VCD_S_SUCCESS;
+}
+
+u32 ddl_encode_start(u32 *ddl_handle, void *client_data)
+{
+	struct ddl_client_context *ddl =
+	    (struct ddl_client_context *)ddl_handle;
+	struct ddl_context *ddl_context;
+	struct ddl_encoder_data *encoder;
+	u32 dpb_size;
+
+	ddl_context = ddl_get_context();
+
+	if (!DDL_IS_INITIALIZED(ddl_context)) {
+		VIDC_LOGERR_STRING("ddl_enc_start:Not_inited");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	if (DDL_IS_BUSY(ddl_context)) {
+		VIDC_LOGERR_STRING("ddl_enc_start:Ddl_busy");
+		return VCD_ERR_BUSY;
+	}
+	if (!ddl || ddl->decoding) {
+		VIDC_LOGERR_STRING("ddl_enc_start:Bad_handle");
+		return VCD_ERR_BAD_HANDLE;
+	}
+
+	if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN)) {
+		VIDC_LOGERR_STRING("ddl_enc_start:Not_opened");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+
+	if (!ddl_encoder_ready_to_start(ddl)) {
+		VIDC_LOGERR_STRING("ddl_enc_start:Err_param_settings");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+
+	encoder = &ddl->codec_data.encoder;
+
+	dpb_size = ddl_get_yuv_buffer_size(&encoder->frame_size,
+					&encoder->re_con_buf_format, false,
+					encoder->codec.codec);
+
+	dpb_size *= DDL_ENC_NUM_DPB_BUFFERS;
+	ddl_pmem_alloc(&encoder->enc_dpb_addr,
+		       dpb_size, DDL_TILE_BUFFER_ALIGN_BYTES);
+	if (!encoder->enc_dpb_addr.virtual_base_addr) {
+		VIDC_LOGERR_STRING("ddl_enc_start:Dpb_alloc_failed");
+		return VCD_ERR_ALLOC_FAIL;
+	}
+
+	if ((encoder->codec.codec == VCD_CODEC_MPEG4 &&
+	     !encoder->short_header.short_header) ||
+	    encoder->codec.codec == VCD_CODEC_H264) {
+		ddl_pmem_alloc(&encoder->seq_header,
+			       DDL_ENC_SEQHEADER_SIZE,
+			       DDL_LINEAR_BUFFER_ALIGN_BYTES);
+		if (!encoder->seq_header.virtual_base_addr) {
+			ddl_pmem_free(&encoder->enc_dpb_addr);
+			VIDC_LOGERR_STRING
+			    ("ddl_enc_start:Seq_hdr_alloc_failed");
+			return VCD_ERR_ALLOC_FAIL;
+		}
+	} else {
+		encoder->seq_header.buffer_size = 0;
+		encoder->seq_header.virtual_base_addr = 0;
+	}
+
+	DDL_BUSY(ddl_context);
+
+	ddl_context->current_ddl = ddl;
+	ddl_context->client_data = client_data;
+	ddl_channel_set(ddl);
+	return VCD_S_SUCCESS;
+}
+
+u32 ddl_decode_start(u32 *ddl_handle,
+     struct vcd_sequence_hdr *header, void *client_data)
+{
+	struct ddl_client_context *ddl =
+	    (struct ddl_client_context *)ddl_handle;
+	struct ddl_context *ddl_context;
+	struct ddl_decoder_data *decoder;
+
+	ddl_context = ddl_get_context();
+
+	if (!DDL_IS_INITIALIZED(ddl_context)) {
+		VIDC_LOGERR_STRING("ddl_dec_start:Not_inited");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	if (DDL_IS_BUSY(ddl_context)) {
+		VIDC_LOGERR_STRING("ddl_dec_start:Ddl_busy");
+		return VCD_ERR_BUSY;
+	}
+	if (!ddl || !ddl->decoding) {
+		VIDC_LOGERR_STRING("ddl_dec_start:Bad_handle");
+		return VCD_ERR_BAD_HANDLE;
+	}
+	if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN)) {
+		VIDC_LOGERR_STRING("ddl_dec_start:Not_in_opened_state");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+
+	if ((header) &&
+	    ((!header->sequence_header_len) ||
+	     (!header->sequence_header)
+	    )
+	    ) {
+		VIDC_LOGERR_STRING("ddl_dec_start:Bad_param_seq_header");
+		return VCD_ERR_ILLEGAL_PARM;
+	}
+
+	if (!ddl_decoder_ready_to_start(ddl, header)) {
+		VIDC_LOGERR_STRING("ddl_dec_start:Err_param_settings");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+
+	DDL_BUSY(ddl_context);
+
+	decoder = &ddl->codec_data.decoder;
+	if (header) {
+		decoder->header_in_start = true;
+		decoder->decode_config = *header;
+	} else {
+		decoder->header_in_start = false;
+		decoder->decode_config.sequence_header_len = 0;
+	}
+
+	if (decoder->codec.codec == VCD_CODEC_H264) {
+		ddl_pmem_alloc(&decoder->h264Vsp_temp_buffer,
+			       DDL_DECODE_H264_VSPTEMP_BUFSIZE,
+			       DDL_LINEAR_BUFFER_ALIGN_BYTES);
+		if (!decoder->h264Vsp_temp_buffer.virtual_base_addr) {
+			DDL_IDLE(ddl_context);
+			VIDC_LOGERR_STRING
+			    ("ddl_dec_start:H264Sps_alloc_failed");
+			return VCD_ERR_ALLOC_FAIL;
+		}
+	}
+
+	ddl_context->current_ddl = ddl;
+	ddl_context->client_data = client_data;
+
+	ddl_channel_set(ddl);
+	return VCD_S_SUCCESS;
+}
+
+u32 ddl_decode_frame(u32 *ddl_handle,
+     struct ddl_frame_data_tag *input_bits, void *client_data)
+{
+	u32 vcd_status = VCD_S_SUCCESS;
+	struct ddl_client_context *ddl =
+	    (struct ddl_client_context *)ddl_handle;
+	struct ddl_context *ddl_context = ddl_get_context();
+
+	if (!DDL_IS_INITIALIZED(ddl_context)) {
+		VIDC_LOGERR_STRING("ddl_dec_frame:Not_inited");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	if (DDL_IS_BUSY(ddl_context)) {
+		VIDC_LOGERR_STRING("ddl_dec_frame:Ddl_busy");
+		return VCD_ERR_BUSY;
+	}
+	if (!ddl || !ddl->decoding) {
+		VIDC_LOGERR_STRING("ddl_dec_frame:Bad_handle");
+		return VCD_ERR_BAD_HANDLE;
+	}
+	if (!input_bits ||
+	    ((!input_bits->vcd_frm.physical ||
+	      !input_bits->vcd_frm.data_len) &&
+	     (!(VCD_FRAME_FLAG_EOS & input_bits->vcd_frm.flags))
+	    )
+	    ) {
+		VIDC_LOGERR_STRING("ddl_dec_frame:Bad_input_param");
+		return VCD_ERR_ILLEGAL_PARM;
+	}
+
+	DDL_BUSY(ddl_context);
+
+	ddl_context->current_ddl = ddl;
+	ddl_context->client_data = client_data;
+
+	ddl->input_frame = *input_bits;
+
+	if (DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME)) {
+		ddl_decode_frame_run(ddl);
+	} else {
+		if (!ddl->codec_data.decoder.dp_buf.no_of_dec_pic_buf) {
+			VIDC_LOGERR_STRING("ddl_dec_frame:Dpbs_requied");
+			vcd_status = VCD_ERR_ILLEGAL_OP;
+		} else if (DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_DPB)) {
+			vcd_status = ddl_decode_set_buffers(ddl);
+		} else
+		    if (DDLCLIENT_STATE_IS
+			(ddl, DDL_CLIENT_WAIT_FOR_INITCODEC)) {
+			ddl->codec_data.decoder.decode_config.
+			    sequence_header =
+			    ddl->input_frame.vcd_frm.physical;
+			ddl->codec_data.decoder.decode_config.
+			    sequence_header_len =
+			    ddl->input_frame.vcd_frm.data_len;
+			ddl_decode_init_codec(ddl);
+		} else {
+			VIDC_LOGERR_STRING("Dec_frame:Wrong_state");
+			vcd_status = VCD_ERR_ILLEGAL_OP;
+		}
+		if (vcd_status)
+			DDL_IDLE(ddl_context);
+	}
+	return vcd_status;
+}
+
+u32 ddl_encode_frame(u32 *ddl_handle,
+     struct ddl_frame_data_tag *input_frame,
+     struct ddl_frame_data_tag *output_bit, void *client_data)
+{
+	struct ddl_client_context *ddl =
+	    (struct ddl_client_context *)ddl_handle;
+	struct ddl_context *ddl_context = ddl_get_context();
+
+	if (vidc_msg_timing)
+		ddl_set_core_start_time(__func__, ENC_OP_TIME);
+
+	if (!DDL_IS_INITIALIZED(ddl_context)) {
+		VIDC_LOGERR_STRING("ddl_enc_frame:Not_inited");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	if (DDL_IS_BUSY(ddl_context)) {
+		VIDC_LOGERR_STRING("ddl_enc_frame:Ddl_busy");
+		return VCD_ERR_BUSY;
+	}
+	if (!ddl || ddl->decoding) {
+		VIDC_LOGERR_STRING("ddl_enc_frame:Bad_handle");
+		return VCD_ERR_BAD_HANDLE;
+	}
+	if (!input_frame ||
+	    !input_frame->vcd_frm.physical ||
+	    !input_frame->vcd_frm.data_len) {
+		VIDC_LOGERR_STRING("ddl_enc_frame:Bad_input_params");
+		return VCD_ERR_ILLEGAL_PARM;
+	}
+	if ((((u32) input_frame->vcd_frm.physical +
+		   input_frame->vcd_frm.offset) &
+		  (DDL_STREAMBUF_ALIGN_GUARD_BYTES)
+	    )
+	    ) {
+		VIDC_LOGERR_STRING
+		    ("ddl_enc_frame:Un_aligned_yuv_start_address");
+		return VCD_ERR_ILLEGAL_PARM;
+	}
+	if (!output_bit ||
+	    !output_bit->vcd_frm.physical ||
+	    !output_bit->vcd_frm.alloc_len) {
+		VIDC_LOGERR_STRING("ddl_enc_frame:Bad_output_params");
+		return VCD_ERR_ILLEGAL_PARM;
+	}
+	if ((ddl->codec_data.encoder.output_buf_req.sz +
+	     output_bit->vcd_frm.offset) >
+	    output_bit->vcd_frm.alloc_len) {
+		VIDC_LOGERR_STRING
+		    ("ddl_enc_frame:offset_large, Exceeds_min_buf_size");
+	}
+	if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME)) {
+		VIDC_LOGERR_STRING("ddl_enc_frame:Wrong_state");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+
+	DDL_BUSY(ddl_context);
+
+	ddl_context->current_ddl = ddl;
+	ddl_context->client_data = client_data;
+
+	ddl->input_frame = *input_frame;
+	ddl->output_frame = *output_bit;
+
+	ddl_encode_frame_run(ddl);
+	return VCD_S_SUCCESS;
+}
+
+u32 ddl_decode_end(u32 *ddl_handle, void *client_data)
+{
+	struct ddl_client_context *ddl =
+	    (struct ddl_client_context *)ddl_handle;
+	struct ddl_context *ddl_context;
+
+	ddl_context = ddl_get_context();
+
+	if (vidc_msg_timing) {
+		ddl_reset_core_time_variables(DEC_OP_TIME);
+		ddl_reset_core_time_variables(DEC_IP_TIME);
+	}
+
+	if (!DDL_IS_INITIALIZED(ddl_context)) {
+		VIDC_LOGERR_STRING("ddl_dec_end:Not_inited");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	if (DDL_IS_BUSY(ddl_context)) {
+		VIDC_LOGERR_STRING("ddl_dec_end:Ddl_busy");
+		return VCD_ERR_BUSY;
+	}
+	if (!ddl || !ddl->decoding) {
+		VIDC_LOGERR_STRING("ddl_dec_end:Bad_handle");
+		return VCD_ERR_BAD_HANDLE;
+	}
+	if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME) &&
+		!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_INITCODEC) &&
+		!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_DPB) &&
+		!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_FATAL_ERROR)
+	    ) {
+		VIDC_LOGERR_STRING("ddl_dec_end:Wrong_state");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	DDL_BUSY(ddl_context);
+
+	ddl_context->current_ddl = ddl;
+	ddl_context->client_data = client_data;
+
+	ddl_channel_end(ddl);
+	return VCD_S_SUCCESS;
+}
+
+u32 ddl_encode_end(u32 *ddl_handle, void *client_data)
+{
+	struct ddl_client_context *ddl =
+	    (struct ddl_client_context *)ddl_handle;
+	struct ddl_context *ddl_context;
+
+	ddl_context = ddl_get_context();
+
+	if (vidc_msg_timing)
+		ddl_reset_core_time_variables(ENC_OP_TIME);
+
+	if (!DDL_IS_INITIALIZED(ddl_context)) {
+		VIDC_LOGERR_STRING("ddl_enc_end:Not_inited");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	if (DDL_IS_BUSY(ddl_context)) {
+		VIDC_LOGERR_STRING("ddl_enc_end:Ddl_busy");
+		return VCD_ERR_BUSY;
+	}
+	if (!ddl || ddl->decoding) {
+		VIDC_LOGERR_STRING("ddl_enc_end:Bad_handle");
+		return VCD_ERR_BAD_HANDLE;
+	}
+	if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME) &&
+		!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_INITCODEC) &&
+		!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_FATAL_ERROR)) {
+		VIDC_LOGERR_STRING("ddl_enc_end:Wrong_state");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	DDL_BUSY(ddl_context);
+
+	ddl_context->current_ddl = ddl;
+	ddl_context->client_data = client_data;
+
+	ddl_channel_end(ddl);
+	return VCD_S_SUCCESS;
+}
+
+u32 ddl_reset_hw(u32 mode)
+{
+	struct ddl_context *ddl_context;
+	struct ddl_client_context *ddl;
+	int i_client_num;
+
+	VIDC_LOG_STRING("ddl_reset_hw:called");
+	ddl_context = ddl_get_context();
+	ddl_move_command_state(ddl_context, DDL_CMD_INVALID);
+	DDL_BUSY(ddl_context);
+
+	if (ddl_context->core_virtual_base_addr)
+		vidc_720p_do_sw_reset();
+
+	ddl_context->device_state = DDL_DEVICE_NOTINIT;
+	for (i_client_num = 0; i_client_num < VCD_MAX_NO_CLIENT;
+			++i_client_num) {
+		ddl = ddl_context->ddl_clients[i_client_num];
+		ddl_context->ddl_clients[i_client_num] = NULL;
+		if (ddl) {
+			ddl_release_client_internal_buffers(ddl);
+			ddl_client_transact(DDL_FREE_CLIENT, &ddl);
+		}
+	}
+
+	ddl_release_context_buffers(ddl_context);
+	DDL_MEMSET(ddl_context, 0, sizeof(struct ddl_context));
+
+	return true;
+}
diff --git a/drivers/video/msm/vidc/720p/ddl/vcd_ddl.h b/drivers/video/msm/vidc/720p/ddl/vcd_ddl.h
new file mode 100644
index 0000000..e1407c8
--- /dev/null
+++ b/drivers/video/msm/vidc/720p/ddl/vcd_ddl.h
@@ -0,0 +1,292 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef _VCD_DDL_H_
+#define _VCD_DDL_H_
+#include <mach/msm_subsystem_map.h>
+#include "vcd_ddl_api.h"
+#include "vcd_ddl_utils.h"
+#include "vcd_ddl_firmware.h"
+#include "vidc.h"
+
+#undef DDL_INLINE
+#define DDL_INLINE
+
+#define DDL_BUSY_STATE 1
+#define DDL_IDLE_STATE 0
+#define DDL_ERROR_STATE 2
+#define DDL_IS_BUSY(ddl_context) \
+	(((ddl_context)->ddl_busy != DDL_IDLE_STATE))
+#define DDL_BUSY(ddl_context) \
+	((ddl_context)->ddl_busy = DDL_BUSY_STATE)
+#define DDL_IDLE(ddl_context) \
+	((ddl_context)->ddl_busy = DDL_IDLE_STATE)
+#define DDL_ERROR(ddl_context) \
+	((ddl_context)->ddl_busy = DDL_ERROR_STATE)
+
+#define DDL_DEVICE_NOTINIT  0
+#define DDL_DEVICE_INITED   1
+#define DDL_DEVICE_HWFATAL  2
+#define DDL_IS_INITIALIZED(ddl_context)  \
+(ddl_context->device_state == DDL_DEVICE_INITED)
+
+#define DDLCOMMAND_STATE_IS(ddl_context, command_state) \
+(command_state == (ddl_context)->cmd_state)
+
+#define DDLCLIENT_STATE_IS(ddl, current_state) \
+(current_state == (ddl)->client_state)
+
+#define DDL_DPB_OP_INIT       1
+#define DDL_DPB_OP_MARK_FREE  2
+#define DDL_DPB_OP_MARK_BUSY  3
+#define DDL_DPB_OP_SET_MASK   4
+#define DDL_DPB_OP_RETRIEVE   5
+
+#define DDL_INIT_CLIENTS     0
+#define DDL_GET_CLIENT       1
+#define DDL_FREE_CLIENT      2
+#define DDL_ACTIVE_CLIENT    3
+
+#define DDL_INVALID_CHANNEL_ID  ((u32)~0)
+#define DDL_INVALID_CODEC_TYPE ((u32)~0)
+
+#define DDL_ENC_REQ_IFRAME                      0x01
+#define DDL_ENC_CHANGE_IPERIOD                  0x02
+#define DDL_ENC_CHANGE_BITRATE                  0x04
+#define DDL_ENC_CHANGE_FRAMERATE                0x08
+#define DDL_ENC_CHANGE_CIR                      0x10
+
+#define DDL_DEC_REQ_OUTPUT_FLUSH                0x1
+
+enum ddl_mem_area {
+	DDL_MM_MEM	= 0x0
+};
+
+struct ddl_buf_addr {
+	u32 *physical_base_addr;
+	u32 *virtual_base_addr;
+	u32 *align_physical_addr;
+	u32 *align_virtual_addr;
+	struct msm_mapped_buffer *mapped_buffer;
+	u32 buffer_size;
+	enum ddl_mem_area mem_type;
+};
+
+enum ddl_cmd_state {
+	DDL_CMD_INVALID = 0x0,
+	DDL_CMD_DMA_INIT = 0x1,
+	DDL_CMD_CPU_RESET = 0x2,
+	DDL_CMD_CHANNEL_SET = 0x3,
+	DDL_CMD_INIT_CODEC = 0x4,
+	DDL_CMD_HEADER_PARSE = 0x5,
+	DDL_CMD_DECODE_SET_DPB = 0x6,
+	DDL_CMD_DECODE_FRAME = 0x7,
+	DDL_CMD_ENCODE_FRAME = 0x8,
+	DDL_CMD_EOS = 0x9,
+	DDL_CMD_CHANNEL_END = 0xA,
+	DDL_CMD_32BIT = 0x7FFFFFFF
+};
+
+enum ddl_client_state {
+	DDL_CLIENT_INVALID = 0x0,
+	DDL_CLIENT_OPEN = 0x1,
+	DDL_CLIENT_WAIT_FOR_CHDONE = 0x2,
+	DDL_CLIENT_WAIT_FOR_INITCODEC = 0x3,
+	DDL_CLIENT_WAIT_FOR_INITCODECDONE = 0x4,
+	DDL_CLIENT_WAIT_FOR_DPB = 0x5,
+	DDL_CLIENT_WAIT_FOR_DPBDONE = 0x6,
+	DDL_CLIENT_WAIT_FOR_FRAME = 0x7,
+	DDL_CLIENT_WAIT_FOR_FRAME_DONE = 0x8,
+	DDL_CLIENT_WAIT_FOR_EOS_DONE = 0x9,
+	DDL_CLIENT_WAIT_FOR_CHEND = 0xA,
+	DDL_CLIENT_FATAL_ERROR = 0xB,
+	DDL_CLIENT_32BIT = 0x7FFFFFFF
+};
+
+struct ddl_mask {
+	u32 client_mask;
+	u32 hw_mask;
+};
+
+struct ddl_context;
+
+struct ddl_client_context;
+
+struct ddl_codec_data_hdr {
+	u32 decoding;
+};
+
+struct ddl_encoder_data {
+	struct ddl_codec_data_hdr hdr;
+	struct vcd_property_codec codec;
+	struct vcd_property_frame_size frame_size;
+	struct vcd_property_frame_rate frame_rate;
+	struct vcd_property_target_bitrate target_bit_rate;
+	struct vcd_property_profile profile;
+	struct vcd_property_level level;
+	struct vcd_property_rate_control rc;
+	struct vcd_property_multi_slice multi_slice;
+	u32 meta_data_enable_flag;
+	u32 suffix;
+	struct ddl_buf_addr meta_data_input;
+	u32 meta_data_offset;
+	struct vcd_property_short_header short_header;
+	struct vcd_property_vop_timing vop_timing;
+	u32 hdr_ext_control;
+	struct vcd_property_db_config db_control;
+	struct vcd_property_entropy_control entropy_control;
+	struct vcd_property_i_period i_period;
+	struct vcd_property_session_qp session_qp;
+	struct vcd_property_qp_range qp_range;
+	struct vcd_property_rc_level rc_level;
+	u32 r_cframe_skip;
+	u32 vb_vbuffer_size;
+	struct vcd_property_frame_level_rc_params frame_level_rc;
+	struct vcd_property_adaptive_rc_params adaptive_rc;
+	struct vcd_property_intra_refresh_mb_number intra_refresh;
+	struct vcd_property_buffer_format buf_format;
+	struct vcd_property_buffer_format re_con_buf_format;
+	u32 dynamic_prop_change;
+	u32 dynmic_prop_change_req;
+	u32 ext_enc_control_val;
+	struct vidc_720p_enc_frame_info enc_frame_info;
+	struct ddl_buf_addr enc_dpb_addr;
+	struct ddl_buf_addr seq_header;
+	struct vcd_buffer_requirement input_buf_req;
+	struct vcd_buffer_requirement output_buf_req;
+	struct vcd_buffer_requirement client_input_buf_req;
+	struct vcd_buffer_requirement client_output_buf_req;
+};
+
+struct ddl_decoder_data {
+	struct ddl_codec_data_hdr hdr;
+	struct vcd_property_codec codec;
+	struct vcd_property_buffer_format buf_format;
+	struct vcd_property_frame_size frame_size;
+	struct vcd_property_frame_size client_frame_size;
+	struct vcd_property_profile profile;
+	struct vcd_property_level level;
+	u32 progressive_only;
+	u32 output_order;
+	u32 meta_data_enable_flag;
+	u32 suffix;
+	struct ddl_buf_addr meta_data_input;
+	struct ddl_buf_addr ref_buffer;
+	u32 meta_data_offset;
+	struct vcd_property_post_filter post_filter;
+	struct vcd_sequence_hdr decode_config;
+	u32 header_in_start;
+	u32 min_dpb_num;
+	u32 y_cb_cr_size;
+	struct ddl_property_dec_pic_buffers dp_buf;
+	struct ddl_mask dpb_mask;
+	u32 dynamic_prop_change;
+	u32 dynmic_prop_change_req;
+	struct vidc_720p_dec_disp_info dec_disp_info;
+	struct ddl_buf_addr dpb_comv_buffer;
+	struct ddl_buf_addr h264Vsp_temp_buffer;
+	struct vcd_buffer_requirement actual_input_buf_req;
+	struct vcd_buffer_requirement min_input_buf_req;
+	struct vcd_buffer_requirement client_input_buf_req;
+	struct vcd_buffer_requirement actual_output_buf_req;
+	struct vcd_buffer_requirement min_output_buf_req;
+	struct vcd_buffer_requirement client_output_buf_req;
+	u32 idr_only_decoding;
+};
+
+union ddl_codec_data {
+	struct ddl_codec_data_hdr hdr;
+	struct ddl_decoder_data decoder;
+	struct ddl_encoder_data encoder;
+};
+
+struct ddl_context {
+	int memtype;
+	u8 *core_virtual_base_addr;
+	void (*ddl_callback) (u32 event, u32 status, void *payload, size_t sz,
+			      u32 *ddl_handle, void *const client_data);
+	void *client_data;
+	void (*interrupt_clr) (void);
+	enum ddl_cmd_state cmd_state;
+	struct ddl_client_context *current_ddl;
+	struct ddl_buf_addr context_buf_addr;
+	struct ddl_buf_addr db_line_buffer;
+	struct ddl_buf_addr data_partition_tempbuf;
+	struct ddl_buf_addr metadata_shared_input;
+	struct ddl_buf_addr dbg_core_dump;
+	u32 enable_dbg_core_dump;
+	struct ddl_client_context *ddl_clients[VCD_MAX_NO_CLIENT];
+	u32 device_state;
+	u32 ddl_busy;
+	u32  intr_status;
+	u32 cmd_err_status;
+	u32 disp_pic_err_status;
+	u32 op_failed;
+};
+
+struct ddl_client_context {
+	struct ddl_context *ddl_context;
+	enum ddl_client_state client_state;
+	u32 decoding;
+	u32 channel_id;
+	struct ddl_frame_data_tag input_frame;
+	struct ddl_frame_data_tag output_frame;
+	union ddl_codec_data codec_data;
+};
+
+DDL_INLINE struct ddl_context *ddl_get_context(void);
+DDL_INLINE void ddl_move_command_state(struct ddl_context *ddl_context,
+				       enum ddl_cmd_state command_state);
+DDL_INLINE void ddl_move_client_state(struct ddl_client_context *ddl,
+				      enum ddl_client_state client_state);
+void ddl_core_init(struct ddl_context *);
+void ddl_core_start_cpu(struct ddl_context *);
+void ddl_channel_set(struct ddl_client_context *);
+void ddl_channel_end(struct ddl_client_context *);
+void ddl_encode_init_codec(struct ddl_client_context *);
+void ddl_decode_init_codec(struct ddl_client_context *);
+void ddl_encode_frame_run(struct ddl_client_context *);
+void ddl_decode_frame_run(struct ddl_client_context *);
+void  ddl_decode_eos_run(struct ddl_client_context *);
+void ddl_release_context_buffers(struct ddl_context *);
+void ddl_release_client_internal_buffers(struct ddl_client_context *ddl);
+u32 ddl_decode_set_buffers(struct ddl_client_context *);
+u32 ddl_decoder_dpb_transact(struct ddl_decoder_data *decoder,
+			     struct ddl_frame_data_tag *in_out_frame,
+			     u32 operation);
+u32 ddl_client_transact(u32, struct ddl_client_context **);
+void ddl_set_default_decoder_buffer_req
+    (struct ddl_decoder_data *decoder, u32 estimate);
+void ddl_set_default_encoder_buffer_req
+    (struct ddl_encoder_data *encoder);
+void ddl_set_default_dec_property(struct ddl_client_context *);
+u32 ddl_encoder_ready_to_start(struct ddl_client_context *);
+u32 ddl_decoder_ready_to_start(struct ddl_client_context *,
+			       struct vcd_sequence_hdr *);
+u32 ddl_get_yuv_buffer_size
+    (struct vcd_property_frame_size *frame_size,
+     struct vcd_property_buffer_format *buf_format, u32 inter_lace,
+     enum vcd_codec codec);
+void ddl_calculate_stride(struct vcd_property_frame_size *frame_size,
+	u32 inter_lace, enum vcd_codec codec);
+void ddl_encode_dynamic_property(struct ddl_client_context *ddl,
+				 u32 enable);
+void ddl_decode_dynamic_property(struct ddl_client_context *ddl,
+				 u32 enable);
+void ddl_set_initial_default_values(struct ddl_client_context *ddl);
+u32 ddl_handle_core_errors(struct ddl_context *ddl_context);
+void ddl_client_fatal_cb(struct ddl_context *ddl_context);
+void ddl_hw_fatal_cb(struct ddl_context *ddl_context);
+u32 ddl_hal_engine_reset(struct ddl_context *ddl_context);
+void ddl_pmem_alloc(struct ddl_buf_addr *addr, size_t sz, u32 alignment);
+void ddl_pmem_free(struct ddl_buf_addr *addr);
+#endif
diff --git a/drivers/video/msm/vidc/720p/ddl/vcd_ddl_api.h b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_api.h
new file mode 100644
index 0000000..53cc93e
--- /dev/null
+++ b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_api.h
@@ -0,0 +1,53 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef _VCD_DDL_API_H_
+#define _VCD_DDL_API_H_
+#include "vcd_ddl_internal_property.h"
+
+struct ddl_init_config {
+	int memtype;
+	u8 *core_virtual_base_addr;
+	void (*interrupt_clr) (void);
+	void (*ddl_callback) (u32 event, u32 status, void *payload, size_t sz,
+		u32 *ddl_handle, void *const client_data);
+};
+
+struct ddl_frame_data_tag {
+	struct vcd_frame_data vcd_frm;
+	u32 frm_trans_end;
+	u32 frm_delta;
+};
+
+u32 ddl_device_init(struct ddl_init_config *ddl_init_config,
+					void *client_data);
+u32 ddl_device_release(void *client_data);
+u32 ddl_open(u32 **ddl_handle, u32 decoding);
+u32 ddl_close(u32 **ddl_handle);
+u32 ddl_encode_start(u32 *ddl_handle, void *client_data);
+u32 ddl_encode_frame(u32 *ddl_handle,
+	struct ddl_frame_data_tag *input_frame,
+	struct ddl_frame_data_tag *output_bit, void *client_data);
+u32 ddl_encode_end(u32 *ddl_handle, void *client_data);
+u32 ddl_decode_start(u32 *ddl_handle, struct vcd_sequence_hdr *header,
+					void *client_data);
+u32 ddl_decode_frame(u32 *ddl_handle,
+	struct ddl_frame_data_tag *input_bits, void *client_data);
+u32 ddl_decode_end(u32 *ddl_handle, void *client_data);
+u32 ddl_set_property(u32 *ddl_handle,
+	struct vcd_property_hdr *property_hdr, void *property_value);
+u32 ddl_get_property(u32 *ddl_handle,
+	struct vcd_property_hdr *property_hdr, void *property_value);
+void ddl_read_and_clear_interrupt(void);
+u32 ddl_process_core_response(void);
+u32 ddl_reset_hw(u32 mode);
+#endif
diff --git a/drivers/video/msm/vidc/720p/ddl/vcd_ddl_core.h b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_core.h
new file mode 100644
index 0000000..9fdb668
--- /dev/null
+++ b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_core.h
@@ -0,0 +1,99 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef _VCD_DDL_CORE_H_
+#define _VCD_DDL_CORE_H_
+
+#define DDL_LINEAR_BUF_ALIGN_MASK   0xFFFFFFF8U
+#define DDL_LINEAR_BUF_ALIGN_GUARD_BYTES 0x7
+#define DDL_LINEAR_BUFFER_ALIGN_BYTES  8
+
+#define DDL_TILE_BUF_ALIGN_MASK   0xFFFFE000U
+#define DDL_TILE_BUF_ALIGN_GUARD_BYTES 0x1FFF
+#define DDL_TILE_BUFFER_ALIGN_BYTES  8192
+
+#define DDL_MAX_FRAME_WIDTH   (1280)
+#define DDL_MAX_FRAME_HEIGHT  (720)
+
+#define DDL_MAX_DP_FRAME_WIDTH  352
+#define DDL_MAX_DP_FRAME_HEIGHT 288
+
+#define DDL_MAX_BIT_RATE (14*1000*1000)
+
+#define DDL_SW_RESET_SLEEP 10
+
+#define VCD_MAX_NO_CLIENT  4
+#define VCD_FRAME_COMMAND_DEPTH 1
+#define VCD_GENERAL_COMMAND_DEPTH 1
+#define VCD_COMMAND_EXCLUSIVE true
+
+#define DDL_HW_TIMEOUT_IN_MS  1000
+
+#define DDL_STREAMBUF_ALIGN_GUARD_BYTES 0x7
+
+#define DDL_CONTEXT_MEMORY (1024 * 15 * (VCD_MAX_NO_CLIENT + 1))
+#define DDL_DB_LINE_BUF_SIZE \
+(((((DDL_MAX_FRAME_WIDTH * 4) - 1) / 256) + 1) * 8 * 1024)
+#define DDL_MPEG4_DATA_PARTITION_BUF_SIZE (64 * 1024)
+#define DDL_DECODE_H264_VSPTEMP_BUFSIZE 0x59c00
+#define DDL_ENC_NUM_DPB_BUFFERS 2
+
+#define DDL_DBG_CORE_DUMP_SIZE (10 * 1024)
+
+#define DDL_BUFEND_PAD    256
+#define DDL_ENC_SEQHEADER_SIZE (256+DDL_BUFEND_PAD)
+#define DDL_MAX_BUFFER_COUNT  32
+
+#define DDL_MPEG_REFBUF_COUNT  2
+
+#define DDL_MPEG_COMV_BUF_NO 2
+#define DDL_H263_COMV_BUF_NO 2
+#define DDL_COMV_BUFLINE_NO  128
+#define DDL_VC1_COMV_BUFLINE_NO  32
+#define DDL_MINIMUM_BYTE_PER_SLICE  1920
+
+#define DDL_MAX_H264_QP   51
+#define DDL_MAX_MPEG4_QP  31
+
+#define DDL_PADDING_HACK(addr) \
+ (addr) = (u32)((((u32)(addr) + DDL_STREAMBUF_ALIGN_GUARD_BYTES) & \
+			 ~(DDL_STREAMBUF_ALIGN_GUARD_BYTES)) + DDL_BUFEND_PAD)
+
+#define DDL_QCIF_MBS 99
+#define DDL_CIF_MBS  396
+#define DDL_QVGA_MBS 300
+#define DDL_VGA_MBS  1200
+#define DDL_WVGA_MBS 1500
+#define DDL_720P_MBS 3600
+
+#define DDL_FRAMESIZE_DIV_FACTOR   (0xF)
+
+#define DDL_NO_OF_MB(width, height) \
+	(((width + 15) >> 4) * ((height + 15) >> 4))
+
+#define DDL_ALLOW_ENC_FRAMESIZE(width, height) \
+((DDL_NO_OF_MB(width, height) <= DDL_720P_MBS) \
+ && (((width) <= DDL_MAX_FRAME_WIDTH) &&            \
+     ((height) <= DDL_MAX_FRAME_WIDTH))            \
+ && ((width) >= 32 && (height) >= 32))
+
+#define DDL_VALIDATE_ENC_FRAMESIZE(width, height) \
+	(!((width) & DDL_FRAMESIZE_DIV_FACTOR) &&     \
+     !((height) & DDL_FRAMESIZE_DIV_FACTOR))
+
+#define DDL_TILE_ALIGN_WIDTH     128
+#define DDL_TILE_ALIGN_HEIGHT    32
+#define DDL_TILE_MULTIPLY_FACTOR 8192
+#define DDL_TILE_ALIGN(val, grid) \
+   (((val) + (grid) - 1) / (grid) * (grid))
+
+#endif
diff --git a/drivers/video/msm/vidc/720p/ddl/vcd_ddl_errors.c b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_errors.c
new file mode 100644
index 0000000..ac5bce9
--- /dev/null
+++ b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_errors.c
@@ -0,0 +1,609 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <media/msm/vidc_type.h>
+#include "vcd_ddl_utils.h"
+#include "vcd_ddl.h"
+
+#if DEBUG
+#define DBG(x...) printk(KERN_DEBUG x)
+#else
+#define DBG(x...)
+#endif
+
+#define ERR(x...) printk(KERN_ERR x)
+
+#define INVALID_CHANNEL_NUMBER  1
+#define INVALID_COMMAND_ID 2
+#define CHANNEL_ALREADY_IN_USE 3
+#define CHANNEL_NOT_SET_BEFORE_CHANNEL_CLOSE 4
+#define CHANNEL_SET_ERROR_INIT_CODEC 5
+#define INIT_CODEC_ALREADY_CALLED 6
+#define CHANNEL_SET_ERROR_INIT_BUFFERS 7
+#define INIT_CODEC_ERROR_INIT_BUFFERS 8
+#define INIT_BUFFER_ALREADY_CALLED  9
+#define CHANNEL_SET_ERROR_FRAME_RUN 10
+#define INIT_CODEC_ERROR_FRAME_RUN 11
+#define INIT_BUFFERS_ERROR_FRAME_RUN 12
+#define CODEC_LIMIT_EXCEEDED 13
+#define FIRMWARE_SIZE_ZERO 14
+#define FIRMWARE_ADDRESS_EXT_ZERO 15
+#define CONTEXT_DMA_IN_ERROR 16
+#define CONTEXT_DMA_OUT_ERROR 17
+#define PROGRAM_DMA_ERROR 18
+#define CONTEXT_STORE_EXT_ADD_ZERO 19
+#define MEM_ALLOCATION_FAILED 20
+
+
+#define UNSUPPORTED_FEATURE_IN_PROFILE 27
+#define RESOLUTION_NOT_SUPPORTED 28
+#define HEADER_NOT_FOUND 52
+#define MB_NUM_INVALID 61
+#define FRAME_RATE_NOT_SUPPORTED 62
+#define INVALID_QP_VALUE 63
+#define INVALID_RC_REACTION_COEFFICIENT 64
+#define INVALID_CPB_SIZE_AT_GIVEN_LEVEL 65
+
+#define ALLOC_DPB_SIZE_NOT_SUFFICIENT 71
+#define ALLOC_DB_SIZE_NOT_SUFFICIENT 72
+#define ALLOC_COMV_SIZE_NOT_SUFFICIENT 73
+#define NUM_BUF_OUT_OF_RANGE 74
+#define NULL_CONTEXT_POINTER 75
+#define NULL_COMAMND_CONTROL_COMM_POINTER 76
+#define NULL_METADATA_INPUT_POINTER 77
+#define NULL_DPB_POINTER 78
+#define NULL_DB_POINTER 79
+#define NULL_COMV_POINTER 80
+
+#define DIVIDE_BY_ZERO 81
+#define BIT_STREAM_BUF_EXHAUST 82
+#define DMA_NOT_STOPPED 83
+#define DMA_TX_NOT_COMPLETE 84
+
+#define MB_HEADER_NOT_DONE  85
+#define MB_COEFF_NOT_DONE 86
+#define CODEC_SLICE_NOT_DONE 87
+#define VME_NOT_READY 88
+#define VC1_BITPLANE_DECODE_ERR 89
+
+
+#define VSP_NOT_READY 90
+#define BUFFER_FULL_STATE 91
+
+#define RESOLUTION_MISMATCH 112
+#define NV_QUANT_ERR 113
+#define SYNC_MARKER_ERR 114
+#define FEATURE_NOT_SUPPORTED 115
+#define MEM_CORRUPTION  116
+#define INVALID_REFERENCE_FRAME  117
+#define PICTURE_CODING_TYPE_ERR  118
+#define MV_RANGE_ERR  119
+#define PICTURE_STRUCTURE_ERR 120
+#define SLICE_ADDR_INVALID  121
+#define NON_PAIRED_FIELD_NOT_SUPPORTED  122
+#define NON_FRAME_DATA_RECEIVED 123
+#define INCOMPLETE_FRAME  124
+#define NO_BUFFER_RELEASED_FROM_HOST  125
+#define PICTURE_MANAGEMENT_ERROR  128
+#define INVALID_MMCO  129
+#define INVALID_PIC_REORDERING 130
+#define INVALID_POC_TYPE 131
+#define ACTIVE_SPS_NOT_PRESENT 132
+#define ACTIVE_PPS_NOT_PRESENT 133
+#define INVALID_SPS_ID 134
+#define INVALID_PPS_ID 135
+
+
+#define METADATA_NO_SPACE_QP 151
+#define METADATA_NO_SAPCE_CONCEAL_MB 152
+#define METADATA_NO_SPACE_VC1_PARAM 153
+#define METADATA_NO_SPACE_SEI 154
+#define METADATA_NO_SPACE_VUI 155
+#define METADATA_NO_SPACE_EXTRA 156
+#define METADATA_NO_SPACE_DATA_NONE 157
+#define FRAME_RATE_UNKNOWN 158
+#define ASPECT_RATIO_UNKOWN 159
+#define COLOR_PRIMARIES_UNKNOWN 160
+#define TRANSFER_CHAR_UNKWON 161
+#define MATRIX_COEFF_UNKNOWN 162
+#define NON_SEQ_SLICE_ADDR 163
+#define BROKEN_LINK 164
+#define FRAME_CONCEALED 165
+#define PROFILE_UNKOWN 166
+#define LEVEL_UNKOWN 167
+#define BIT_RATE_NOT_SUPPORTED 168
+#define COLOR_DIFF_FORMAT_NOT_SUPPORTED 169
+#define NULL_EXTRA_METADATA_POINTER  170
+#define SYNC_POINT_NOT_RECEIVED_STARTED_DECODING  171
+#define NULL_FW_DEBUG_INFO_POINTER  172
+#define ALLOC_DEBUG_INFO_SIZE_INSUFFICIENT  173
+#define MAX_STAGE_COUNTER_EXCEEDED 174
+
+#define METADATA_NO_SPACE_MB_INFO 180
+#define METADATA_NO_SPACE_SLICE_SIZE 181
+#define RESOLUTION_WARNING 182
+
+static void ddl_handle_npf_decoding_error(
+	struct ddl_context *ddl_context);
+
+static u32 ddl_handle_seqhdr_fail_error(
+	struct ddl_context *ddl_context);
+
+void ddl_hw_fatal_cb(struct ddl_context *ddl_context)
+{
+	/* Invalidate the command state */
+	ddl_move_command_state(ddl_context, DDL_CMD_INVALID);
+	ddl_context->device_state = DDL_DEVICE_HWFATAL;
+
+	/* callback to the client to indicate hw fatal error */
+	ddl_context->ddl_callback(VCD_EVT_IND_HWERRFATAL,
+					VCD_ERR_HW_FATAL, NULL, 0,
+					(void *)ddl_context->current_ddl,
+					ddl_context->client_data);
+
+	DDL_IDLE(ddl_context);
+}
+
+static u32 ddl_handle_hw_fatal_errors(struct ddl_context
+			*ddl_context)
+{
+	u32 status = false;
+
+	switch (ddl_context->cmd_err_status) {
+
+	case INVALID_CHANNEL_NUMBER:
+	case INVALID_COMMAND_ID:
+	case CHANNEL_ALREADY_IN_USE:
+	case CHANNEL_NOT_SET_BEFORE_CHANNEL_CLOSE:
+	case CHANNEL_SET_ERROR_INIT_CODEC:
+	case INIT_CODEC_ALREADY_CALLED:
+	case CHANNEL_SET_ERROR_INIT_BUFFERS:
+	case INIT_CODEC_ERROR_INIT_BUFFERS:
+	case INIT_BUFFER_ALREADY_CALLED:
+	case CHANNEL_SET_ERROR_FRAME_RUN:
+	case INIT_CODEC_ERROR_FRAME_RUN:
+	case INIT_BUFFERS_ERROR_FRAME_RUN:
+	case CODEC_LIMIT_EXCEEDED:
+	case FIRMWARE_SIZE_ZERO:
+	case FIRMWARE_ADDRESS_EXT_ZERO:
+
+	case CONTEXT_DMA_IN_ERROR:
+	case CONTEXT_DMA_OUT_ERROR:
+	case PROGRAM_DMA_ERROR:
+	case CONTEXT_STORE_EXT_ADD_ZERO:
+	case MEM_ALLOCATION_FAILED:
+
+	case DIVIDE_BY_ZERO:
+	case DMA_NOT_STOPPED:
+	case DMA_TX_NOT_COMPLETE:
+
+	case VSP_NOT_READY:
+	case BUFFER_FULL_STATE:
+	case NULL_DB_POINTER:
+		ERR("HW FATAL ERROR");
+		ddl_hw_fatal_cb(ddl_context);
+		status = true;
+		break;
+	}
+	return status;
+}
+
+void ddl_client_fatal_cb(struct ddl_context *ddl_context)
+{
+	struct ddl_client_context  *ddl =
+		ddl_context->current_ddl;
+
+	if (ddl_context->cmd_state == DDL_CMD_DECODE_FRAME)
+		ddl_decode_dynamic_property(ddl, false);
+	else if (ddl_context->cmd_state == DDL_CMD_ENCODE_FRAME)
+		ddl_encode_dynamic_property(ddl, false);
+
+	ddl_move_command_state(ddl_context, DDL_CMD_INVALID);
+
+	ddl_move_client_state(ddl, DDL_CLIENT_FATAL_ERROR);
+
+	ddl_context->ddl_callback
+	(
+		VCD_EVT_IND_HWERRFATAL,
+		VCD_ERR_CLIENT_FATAL,
+		NULL,
+		0,
+		(void *)ddl,
+		ddl_context->client_data
+	);
+
+	DDL_IDLE(ddl_context);
+}
+
+static u32 ddl_handle_client_fatal_errors(struct ddl_context
+			*ddl_context)
+{
+	u32 status = false;
+
+	switch (ddl_context->cmd_err_status) {
+	case MB_NUM_INVALID:
+	case FRAME_RATE_NOT_SUPPORTED:
+	case INVALID_QP_VALUE:
+	case INVALID_RC_REACTION_COEFFICIENT:
+	case INVALID_CPB_SIZE_AT_GIVEN_LEVEL:
+
+	case ALLOC_DPB_SIZE_NOT_SUFFICIENT:
+	case ALLOC_DB_SIZE_NOT_SUFFICIENT:
+	case ALLOC_COMV_SIZE_NOT_SUFFICIENT:
+	case NUM_BUF_OUT_OF_RANGE:
+	case NULL_CONTEXT_POINTER:
+	case NULL_COMAMND_CONTROL_COMM_POINTER:
+	case NULL_METADATA_INPUT_POINTER:
+	case NULL_DPB_POINTER:
+	case NULL_COMV_POINTER:
+		{
+			status = true;
+			break;
+		}
+	}
+
+	if (!status)
+		ERR("UNKNOWN-OP-FAILED");
+
+	ddl_client_fatal_cb(ddl_context);
+
+	return true;
+}
+
+static void ddl_input_failed_cb(struct ddl_context *ddl_context,
+			u32 vcd_event, u32 vcd_status)
+{
+	struct ddl_client_context  *ddl = ddl_context->current_ddl;
+
+	ddl_move_command_state(ddl_context, DDL_CMD_INVALID);
+
+	if (ddl->decoding)
+		ddl_decode_dynamic_property(ddl, false);
+	else
+		ddl_encode_dynamic_property(ddl, false);
+
+	ddl_context->ddl_callback(vcd_event,
+		vcd_status, &ddl->input_frame,
+		sizeof(struct ddl_frame_data_tag),
+		(void *)ddl, ddl_context->client_data);
+
+	ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_FRAME);
+}
+
+static u32 ddl_handle_core_recoverable_errors(struct ddl_context \
+			*ddl_context)
+{
+	struct ddl_client_context  *ddl = ddl_context->current_ddl;
+	u32   vcd_status = VCD_S_SUCCESS;
+	u32   vcd_event = VCD_EVT_RESP_INPUT_DONE;
+	u32   eos = false, pending_display = 0, release_mask = 0;
+
+	if (ddl->decoding)
+		if (ddl_handle_seqhdr_fail_error(ddl_context))
+			return true;
+
+	if (ddl_context->cmd_state != DDL_CMD_DECODE_FRAME &&
+		ddl_context->cmd_state != DDL_CMD_ENCODE_FRAME) {
+		return false;
+	}
+	switch (ddl_context->cmd_err_status) {
+	case NON_PAIRED_FIELD_NOT_SUPPORTED:
+		{
+			ddl_handle_npf_decoding_error(ddl_context);
+			return true;
+		}
+	case NO_BUFFER_RELEASED_FROM_HOST:
+		{
+			/* lets check sanity of this error */
+			release_mask =
+				ddl->codec_data.decoder.dpb_mask.hw_mask;
+			while (release_mask > 0) {
+				if ((release_mask & 0x1))
+					pending_display += 1;
+				release_mask >>= 1;
+			}
+
+			if (pending_display >=
+				ddl->codec_data.decoder.min_dpb_num) {
+				DBG("FWISSUE-REQBUF!!");
+				/* callback to client for client fatal error */
+				ddl_client_fatal_cb(ddl_context);
+				return true ;
+			}
+		vcd_event = VCD_EVT_RESP_OUTPUT_REQ;
+		break;
+		}
+	case BIT_STREAM_BUF_EXHAUST:
+	case MB_HEADER_NOT_DONE:
+	case MB_COEFF_NOT_DONE:
+	case CODEC_SLICE_NOT_DONE:
+	case VME_NOT_READY:
+	case VC1_BITPLANE_DECODE_ERR:
+		{
+			u32 reset_core;
+			/* need to reset the internal core hw engine */
+			reset_core = ddl_hal_engine_reset(ddl_context);
+			if (!reset_core)
+				return true;
+			/* fall through to process bitstream error handling */
+		}
+	case RESOLUTION_MISMATCH:
+	case NV_QUANT_ERR:
+	case SYNC_MARKER_ERR:
+	case FEATURE_NOT_SUPPORTED:
+	case MEM_CORRUPTION:
+	case INVALID_REFERENCE_FRAME:
+	case PICTURE_CODING_TYPE_ERR:
+	case MV_RANGE_ERR:
+	case PICTURE_STRUCTURE_ERR:
+	case SLICE_ADDR_INVALID:
+	case NON_FRAME_DATA_RECEIVED:
+	case INCOMPLETE_FRAME:
+	case PICTURE_MANAGEMENT_ERROR:
+	case INVALID_MMCO:
+	case INVALID_PIC_REORDERING:
+	case INVALID_POC_TYPE:
+		{
+			vcd_status = VCD_ERR_BITSTREAM_ERR;
+			break;
+		}
+	case ACTIVE_SPS_NOT_PRESENT:
+	case ACTIVE_PPS_NOT_PRESENT:
+		{
+			if (ddl->codec_data.decoder.idr_only_decoding) {
+				DBG("Consider warnings as errors in idr mode");
+				ddl_client_fatal_cb(ddl_context);
+				return true;
+			}
+			vcd_status = VCD_ERR_BITSTREAM_ERR;
+			break;
+		}
+	case PROFILE_UNKOWN:
+		if (ddl->decoding)
+			vcd_status = VCD_ERR_BITSTREAM_ERR;
+		break;
+	}
+
+	if (!vcd_status && vcd_event == VCD_EVT_RESP_INPUT_DONE)
+		return false;
+
+	ddl->input_frame.frm_trans_end = true;
+
+	eos = ((vcd_event == VCD_EVT_RESP_INPUT_DONE) &&
+		((VCD_FRAME_FLAG_EOS & ddl->input_frame.
+				vcd_frm.flags)));
+
+	if ((ddl->decoding && eos) ||
+		(!ddl->decoding))
+		ddl->input_frame.frm_trans_end = false;
+
+	if (vcd_event == VCD_EVT_RESP_INPUT_DONE &&
+		ddl->decoding &&
+		!ddl->codec_data.decoder.header_in_start &&
+		!ddl->codec_data.decoder.dec_disp_info.img_size_x &&
+		!ddl->codec_data.decoder.dec_disp_info.img_size_y
+		) {
+		/* this is first frame seq. header only case */
+		vcd_status = VCD_S_SUCCESS;
+		ddl->input_frame.vcd_frm.flags |=
+			VCD_FRAME_FLAG_CODECCONFIG;
+		ddl->input_frame.frm_trans_end = !eos;
+		/* put just some non - zero value */
+		ddl->codec_data.decoder.dec_disp_info.img_size_x = 0xff;
+	}
+	/* inform client about input failed */
+	ddl_input_failed_cb(ddl_context, vcd_event, vcd_status);
+
+	/* for Encoder case, we need to send output done also */
+	if (!ddl->decoding) {
+		/* transaction is complete after this callback */
+		ddl->output_frame.frm_trans_end = !eos;
+		/* error case: NO data present */
+		ddl->output_frame.vcd_frm.data_len = 0;
+		/* call back to client for output frame done */
+		ddl_context->ddl_callback(VCD_EVT_RESP_OUTPUT_DONE,
+		VCD_ERR_FAIL, &(ddl->output_frame),
+			sizeof(struct ddl_frame_data_tag),
+			(void *)ddl, ddl_context->client_data);
+
+		if (eos) {
+			DBG("ENC-EOS_DONE");
+			/* send client EOS DONE callback */
+			ddl_context->ddl_callback(VCD_EVT_RESP_EOS_DONE,
+				VCD_S_SUCCESS, NULL, 0, (void *)ddl,
+				ddl_context->client_data);
+		}
+	}
+
+	/* if it is decoder EOS case */
+	if (ddl->decoding && eos)
+		ddl_decode_eos_run(ddl);
+	else
+		DDL_IDLE(ddl_context);
+
+	return true;
+}
+
+static u32 ddl_handle_core_warnings(u32 err_status)
+{
+	u32 status = false;
+
+	switch (err_status) {
+	case FRAME_RATE_UNKNOWN:
+	case ASPECT_RATIO_UNKOWN:
+	case COLOR_PRIMARIES_UNKNOWN:
+	case TRANSFER_CHAR_UNKWON:
+	case MATRIX_COEFF_UNKNOWN:
+	case NON_SEQ_SLICE_ADDR:
+	case BROKEN_LINK:
+	case FRAME_CONCEALED:
+	case PROFILE_UNKOWN:
+	case LEVEL_UNKOWN:
+	case BIT_RATE_NOT_SUPPORTED:
+	case COLOR_DIFF_FORMAT_NOT_SUPPORTED:
+	case NULL_EXTRA_METADATA_POINTER:
+	case SYNC_POINT_NOT_RECEIVED_STARTED_DECODING:
+
+	case NULL_FW_DEBUG_INFO_POINTER:
+	case ALLOC_DEBUG_INFO_SIZE_INSUFFICIENT:
+	case MAX_STAGE_COUNTER_EXCEEDED:
+
+	case METADATA_NO_SPACE_MB_INFO:
+	case METADATA_NO_SPACE_SLICE_SIZE:
+	case RESOLUTION_WARNING:
+
+	/* decoder warnings */
+	case METADATA_NO_SPACE_QP:
+	case METADATA_NO_SAPCE_CONCEAL_MB:
+	case METADATA_NO_SPACE_VC1_PARAM:
+	case METADATA_NO_SPACE_SEI:
+	case METADATA_NO_SPACE_VUI:
+	case METADATA_NO_SPACE_EXTRA:
+	case METADATA_NO_SPACE_DATA_NONE:
+		{
+			status = true;
+			DBG("CMD-WARNING-IGNORED!!");
+			break;
+		}
+	}
+	return status;
+}
+
+u32 ddl_handle_core_errors(struct ddl_context *ddl_context)
+{
+	u32 status = false;
+
+	if (!ddl_context->cmd_err_status &&
+		!ddl_context->disp_pic_err_status &&
+		!ddl_context->op_failed)
+		return false;
+
+	if (ddl_context->cmd_state == DDL_CMD_INVALID) {
+		DBG("SPURIOUS_INTERRUPT_ERROR");
+		return true;
+	}
+
+	if (!ddl_context->op_failed) {
+		u32 disp_status;
+		status = ddl_handle_core_warnings(ddl_context->
+			cmd_err_status);
+		disp_status = ddl_handle_core_warnings(
+			ddl_context->disp_pic_err_status);
+		if (!status && !disp_status)
+			DBG("ddl_warning:Unknown");
+
+		return false;
+	}
+
+	ERR("\n %s(): OPFAILED!!", __func__);
+	ERR("\n CMD_ERROR_STATUS = %u, DISP_ERR_STATUS = %u",
+		ddl_context->cmd_err_status,
+		ddl_context->disp_pic_err_status);
+
+	status = ddl_handle_hw_fatal_errors(ddl_context);
+
+	if (!status)
+		status = ddl_handle_core_recoverable_errors(ddl_context);
+
+	if (!status)
+		status = ddl_handle_client_fatal_errors(ddl_context);
+
+	return status;
+}
+
+void ddl_handle_npf_decoding_error(struct ddl_context *ddl_context)
+{
+	struct ddl_client_context *ddl = ddl_context->current_ddl;
+	struct ddl_decoder_data *decoder = &ddl->codec_data.decoder;
+	if (!ddl->decoding) {
+		ERR("FWISSUE-ENC-NPF!!!");
+		ddl_client_fatal_cb(ddl_context);
+		return;
+	}
+	vidc_720p_decode_display_info(&decoder->dec_disp_info);
+	ddl_decode_dynamic_property(ddl, false);
+	ddl->output_frame.vcd_frm.ip_frm_tag =
+		decoder->dec_disp_info.tag_top;
+	ddl->output_frame.vcd_frm.physical = NULL;
+	ddl->output_frame.frm_trans_end = false;
+	ddl->ddl_context->ddl_callback(
+		VCD_EVT_RESP_OUTPUT_DONE,
+		VCD_ERR_INTRLCD_FIELD_DROP,
+		&ddl->output_frame,
+		sizeof(struct ddl_frame_data_tag),
+		(void *)ddl,
+		ddl->ddl_context->client_data);
+	ddl_decode_frame_run(ddl);
+}
+
+u32 ddl_handle_seqhdr_fail_error(struct ddl_context *ddl_context)
+{
+	struct ddl_client_context *ddl = ddl_context->current_ddl;
+	struct ddl_decoder_data *decoder = &ddl->codec_data.decoder;
+	u32 status = false;
+	if (ddl_context->cmd_state == DDL_CMD_HEADER_PARSE &&
+		DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_INITCODECDONE)) {
+		switch (ddl_context->cmd_err_status) {
+		case UNSUPPORTED_FEATURE_IN_PROFILE:
+		case HEADER_NOT_FOUND:
+		case INVALID_SPS_ID:
+		case INVALID_PPS_ID:
+		case RESOLUTION_NOT_SUPPORTED:
+		case PROFILE_UNKOWN:
+			ERR("SEQ-HDR-FAILED!!!");
+			if ((ddl_context->cmd_err_status ==
+				 RESOLUTION_NOT_SUPPORTED) &&
+				(decoder->codec.codec == VCD_CODEC_H264 ||
+				decoder->codec.codec == VCD_CODEC_H263 ||
+				decoder->codec.codec == VCD_CODEC_MPEG4 ||
+				decoder->codec.codec == VCD_CODEC_VC1_RCV ||
+				decoder->codec.codec == VCD_CODEC_VC1)) {
+				ddl_client_fatal_cb(ddl_context);
+				status = true;
+				break;
+			}
+			if (decoder->header_in_start) {
+				decoder->header_in_start = false;
+				ddl_context->ddl_callback(VCD_EVT_RESP_START,
+					VCD_ERR_SEQHDR_PARSE_FAIL,
+					NULL, 0, (void *)ddl,
+					ddl_context->client_data);
+			} else {
+				if (ddl->input_frame.vcd_frm.flags &
+					VCD_FRAME_FLAG_EOS)
+					ddl->input_frame.frm_trans_end = false;
+				else
+					ddl->input_frame.frm_trans_end = true;
+				ddl_decode_dynamic_property(ddl, false);
+				ddl_context->ddl_callback(
+					VCD_EVT_RESP_INPUT_DONE,
+					VCD_ERR_SEQHDR_PARSE_FAIL,
+					&ddl->input_frame,
+					sizeof(struct ddl_frame_data_tag),
+					(void *)ddl, ddl_context->client_data);
+				if (ddl->input_frame.vcd_frm.flags &
+					VCD_FRAME_FLAG_EOS)
+					ddl_context->ddl_callback(
+						VCD_EVT_RESP_EOS_DONE,
+						VCD_S_SUCCESS, NULL,
+						0, (void *)ddl,
+						ddl_context->client_data);
+			}
+			ddl_move_client_state(ddl,
+				DDL_CLIENT_WAIT_FOR_INITCODEC);
+			DDL_IDLE(ddl_context);
+			status = true;
+		}
+	}
+	return status;
+}
diff --git a/drivers/video/msm/vidc/720p/ddl/vcd_ddl_firmware.c b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_firmware.c
new file mode 100644
index 0000000..965c3aa
--- /dev/null
+++ b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_firmware.c
@@ -0,0 +1,352 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <media/msm/vidc_type.h>
+#include "vcd_ddl_firmware.h"
+#include "vcd_ddl_utils.h"
+
+#define VCDFW_TOTALNUM_IMAGE  7
+#define VCDFW_MAX_NO_IMAGE    2
+
+struct vcd_firmware {
+	u32 active_fw_img[VCDFW_TOTALNUM_IMAGE];
+	struct ddl_buf_addr boot_code;
+
+	struct ddl_buf_addr enc_mpeg4;
+	struct ddl_buf_addr encH264;
+
+	struct ddl_buf_addr dec_mpeg4;
+	struct ddl_buf_addr decH264;
+	struct ddl_buf_addr decH263;
+	struct ddl_buf_addr dec_mpeg2;
+	struct ddl_buf_addr dec_vc1;
+};
+
+static struct vcd_firmware vcd_firmware;
+
+
+static void vcd_fw_change_endian(unsigned char *fw, u32 fw_size)
+{
+	u32 i = 0;
+	unsigned char temp;
+	for (i = 0; i < fw_size; i = i + 4) {
+		temp = fw[i];
+		fw[i] = fw[i + 3];
+		fw[i + 3] = temp;
+
+		temp = fw[i + 1];
+		fw[i + 1] = fw[i + 2];
+		fw[i + 2] = temp;
+	}
+	return;
+}
+
+static u32 vcd_fw_prepare(struct ddl_buf_addr *fw_details,
+			 const unsigned char fw_array[],
+			 const unsigned int fw_array_size, u32 change_endian)
+{
+	u32 *buffer;
+
+	ddl_pmem_alloc(fw_details, fw_array_size,
+		       DDL_LINEAR_BUFFER_ALIGN_BYTES);
+	if (!fw_details->virtual_base_addr)
+		return false;
+
+	fw_details->buffer_size = fw_array_size / 4;
+
+	buffer = fw_details->align_virtual_addr;
+
+	memcpy(buffer, fw_array, fw_array_size);
+	if (change_endian)
+		vcd_fw_change_endian((unsigned char *)buffer, fw_array_size);
+	return true;
+}
+
+u32 vcd_fw_init(void)
+{
+	u32 status = false;
+
+	status = vcd_fw_prepare(&vcd_firmware.boot_code,
+				vidc_command_control_fw,
+				vidc_command_control_fw_size, false);
+
+	if (status) {
+		status = vcd_fw_prepare(&vcd_firmware.dec_mpeg4,
+					vidc_mpg4_dec_fw,
+					vidc_mpg4_dec_fw_size, true);
+	}
+
+	if (status) {
+		status = vcd_fw_prepare(&vcd_firmware.decH264,
+					vidc_h264_dec_fw,
+					vidc_h264_dec_fw_size, true);
+	}
+
+	if (status) {
+		status = vcd_fw_prepare(&vcd_firmware.decH263,
+					vidc_h263_dec_fw,
+					vidc_h263_dec_fw_size, true);
+	}
+
+	if (status) {
+		status = vcd_fw_prepare(&vcd_firmware.enc_mpeg4,
+					vidc_mpg4_enc_fw,
+					vidc_mpg4_enc_fw_size, true);
+	}
+
+	if (status) {
+		status = vcd_fw_prepare(&vcd_firmware.encH264,
+					vidc_h264_enc_fw,
+					vidc_h264_enc_fw_size, true);
+	}
+
+	if (status) {
+		status = vcd_fw_prepare(&vcd_firmware.dec_vc1,
+					vidc_vc1_dec_fw,
+					vidc_vc1_dec_fw_size, true);
+	}
+	return status;
+}
+
+
+static u32 get_dec_fw_image(struct vcd_fw_details *fw_details)
+{
+	u32 status = true;
+	switch (fw_details->codec) {
+	case VCD_CODEC_DIVX_4:
+	case VCD_CODEC_DIVX_5:
+	case VCD_CODEC_DIVX_6:
+	case VCD_CODEC_XVID:
+	case VCD_CODEC_MPEG4:
+		{
+			fw_details->fw_buffer_addr =
+			    vcd_firmware.dec_mpeg4.align_physical_addr;
+			fw_details->fw_size =
+			    vcd_firmware.dec_mpeg4.buffer_size;
+			break;
+		}
+	case VCD_CODEC_H264:
+		{
+			fw_details->fw_buffer_addr =
+			    vcd_firmware.decH264.align_physical_addr;
+			fw_details->fw_size =
+			    vcd_firmware.decH264.buffer_size;
+			break;
+		}
+	case VCD_CODEC_VC1:
+	case VCD_CODEC_VC1_RCV:
+		{
+			fw_details->fw_buffer_addr =
+			    vcd_firmware.dec_vc1.align_physical_addr;
+			fw_details->fw_size =
+			    vcd_firmware.dec_vc1.buffer_size;
+			break;
+		}
+	case VCD_CODEC_MPEG2:
+		{
+			fw_details->fw_buffer_addr =
+			    vcd_firmware.dec_mpeg2.align_physical_addr;
+			fw_details->fw_size =
+			    vcd_firmware.dec_mpeg2.buffer_size;
+			break;
+		}
+	case VCD_CODEC_H263:
+		{
+			fw_details->fw_buffer_addr =
+			    vcd_firmware.decH263.align_physical_addr;
+			fw_details->fw_size =
+			    vcd_firmware.decH263.buffer_size;
+			break;
+		}
+	default:
+		{
+			status = false;
+			break;
+		}
+	}
+	return status;
+}
+
+static u32 get_enc_fw_image(struct vcd_fw_details *fw_details)
+{
+	u32 status = true;
+	switch (fw_details->codec) {
+	case VCD_CODEC_H263:
+	case VCD_CODEC_MPEG4:
+		{
+			fw_details->fw_buffer_addr =
+			    vcd_firmware.enc_mpeg4.align_physical_addr;
+			fw_details->fw_size =
+			    vcd_firmware.enc_mpeg4.buffer_size;
+			break;
+		}
+	case VCD_CODEC_H264:
+		{
+			fw_details->fw_buffer_addr =
+			    vcd_firmware.encH264.align_physical_addr;
+			fw_details->fw_size =
+			    vcd_firmware.encH264.buffer_size;
+			break;
+		}
+	default:
+		{
+			status = false;
+			break;
+		}
+	}
+	return status;
+}
+
+u32 vcd_get_fw_property(u32 prop_id, void *prop_details)
+{
+	u32 status = true;
+	struct vcd_fw_details *fw_details;
+	switch (prop_id) {
+	case VCD_FW_ENDIAN:
+		{
+			*(u32 *) prop_details = VCD_FW_BIG_ENDIAN;
+			break;
+		}
+	case VCD_FW_BOOTCODE:
+		{
+			fw_details =
+			    (struct vcd_fw_details *)prop_details;
+			fw_details->fw_buffer_addr =
+			    vcd_firmware.boot_code.align_physical_addr;
+			fw_details->fw_size =
+			    vcd_firmware.boot_code.buffer_size;
+			break;
+		}
+	case VCD_FW_DECODE:
+		{
+			fw_details =
+			    (struct vcd_fw_details *)prop_details;
+			status = get_dec_fw_image(fw_details);
+			break;
+		}
+	case VCD_FW_ENCODE:
+		{
+			fw_details =
+			    (struct vcd_fw_details *)prop_details;
+			status = get_enc_fw_image(fw_details);
+			break;
+		}
+	default:
+		{
+			status = false;
+			break;
+		}
+	}
+	return status;
+}
+
+u32 vcd_fw_transact(u32 add, u32 decoding, enum vcd_codec codec)
+{
+	u32 status = true;
+	u32 index = 0, active_fw = 0, loop_count;
+
+	if (decoding) {
+		switch (codec) {
+		case VCD_CODEC_DIVX_4:
+		case VCD_CODEC_DIVX_5:
+		case VCD_CODEC_DIVX_6:
+		case VCD_CODEC_XVID:
+		case VCD_CODEC_MPEG4:
+			{
+				index = 0;
+				break;
+			}
+		case VCD_CODEC_H264:
+			{
+				index = 1;
+				break;
+			}
+		case VCD_CODEC_H263:
+			{
+				index = 2;
+				break;
+			}
+		case VCD_CODEC_MPEG2:
+			{
+				index = 3;
+				break;
+			}
+		case VCD_CODEC_VC1:
+		case VCD_CODEC_VC1_RCV:
+			{
+				index = 4;
+				break;
+			}
+		default:
+			{
+				status = false;
+				break;
+			}
+		}
+	} else {
+		switch (codec) {
+		case VCD_CODEC_H263:
+		case VCD_CODEC_MPEG4:
+			{
+				index = 5;
+				break;
+			}
+		case VCD_CODEC_H264:
+			{
+				index = 6;
+				break;
+			}
+		default:
+			{
+				status = false;
+				break;
+			}
+		}
+	}
+
+	if (!status)
+		return status;
+
+	if (!add &&
+	    vcd_firmware.active_fw_img[index]
+	    ) {
+		--vcd_firmware.active_fw_img[index];
+		return status;
+	}
+
+	for (loop_count = 0; loop_count < VCDFW_TOTALNUM_IMAGE;
+	     ++loop_count) {
+		if (vcd_firmware.active_fw_img[loop_count])
+			++active_fw;
+	}
+
+	if (active_fw < VCDFW_MAX_NO_IMAGE ||
+	    vcd_firmware.active_fw_img[index] > 0) {
+		++vcd_firmware.active_fw_img[index];
+	} else {
+		status = false;
+	}
+	return status;
+}
+
+void vcd_fw_release(void)
+{
+	ddl_pmem_free(&vcd_firmware.boot_code);
+	ddl_pmem_free(&vcd_firmware.enc_mpeg4);
+	ddl_pmem_free(&vcd_firmware.encH264);
+	ddl_pmem_free(&vcd_firmware.dec_mpeg4);
+	ddl_pmem_free(&vcd_firmware.decH264);
+	ddl_pmem_free(&vcd_firmware.decH263);
+	ddl_pmem_free(&vcd_firmware.dec_mpeg2);
+	ddl_pmem_free(&vcd_firmware.dec_vc1);
+}
diff --git a/drivers/video/msm/vidc/720p/ddl/vcd_ddl_firmware.h b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_firmware.h
new file mode 100644
index 0000000..a136de8
--- /dev/null
+++ b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_firmware.h
@@ -0,0 +1,53 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef _VCD_DDL_FIRMWARE_H_
+#define _VCD_DDL_FIRMWARE_H_
+#include <media/msm/vcd_property.h>
+
+#define VCD_FW_BIG_ENDIAN     0x0
+#define VCD_FW_LITTLE_ENDIAN  0x1
+
+struct vcd_fw_details {
+	enum vcd_codec codec;
+	u32 *fw_buffer_addr;
+	u32 fw_size;
+};
+
+#define VCD_FW_PROP_BASE         0x0
+
+#define VCD_FW_ENDIAN       (VCD_FW_PROP_BASE + 0x1)
+#define VCD_FW_BOOTCODE     (VCD_FW_PROP_BASE + 0x2)
+#define VCD_FW_DECODE     (VCD_FW_PROP_BASE + 0x3)
+#define VCD_FW_ENCODE     (VCD_FW_PROP_BASE + 0x4)
+
+extern unsigned char *vidc_command_control_fw;
+extern u32 vidc_command_control_fw_size;
+extern unsigned char *vidc_mpg4_dec_fw;
+extern u32 vidc_mpg4_dec_fw_size;
+extern unsigned char *vidc_h263_dec_fw;
+extern u32 vidc_h263_dec_fw_size;
+extern unsigned char *vidc_h264_dec_fw;
+extern u32 vidc_h264_dec_fw_size;
+extern unsigned char *vidc_mpg4_enc_fw;
+extern u32 vidc_mpg4_enc_fw_size;
+extern unsigned char *vidc_h264_enc_fw;
+extern u32 vidc_h264_enc_fw_size;
+extern unsigned char *vidc_vc1_dec_fw;
+extern u32 vidc_vc1_dec_fw_size;
+
+u32 vcd_fw_init(void);
+u32 vcd_get_fw_property(u32 prop_id, void *prop_details);
+u32 vcd_fw_transact(u32 add, u32 decoding, enum vcd_codec codec);
+void vcd_fw_release(void);
+
+#endif
diff --git a/drivers/video/msm/vidc/720p/ddl/vcd_ddl_hal.c b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_hal.c
new file mode 100644
index 0000000..6a69955
--- /dev/null
+++ b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_hal.c
@@ -0,0 +1,971 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <media/msm/vidc_type.h>
+
+#include "vcd_ddl_utils.h"
+#include "vcd_ddl_metadata.h"
+
+#if DEBUG
+#define DBG(x...) printk(KERN_DEBUG x)
+#else
+#define DBG(x...)
+#endif
+
+#define DBG_INFO(x...) pr_info(x)
+
+void ddl_core_init(struct ddl_context *ddl_context)
+{
+	char *psz_version;
+	struct vcd_fw_details fw_details;
+	u32 fw_endianness;
+	enum vidc_720p_endian dma_endian;
+	u32 interrupt_off;
+	enum vidc_720p_interrupt_level_selection interrupt_sel;
+	u32 intr_mask = 0x0;
+
+	vcd_get_fw_property(VCD_FW_BOOTCODE, &fw_details);
+	vcd_get_fw_property(VCD_FW_ENDIAN, &fw_endianness);
+	if (fw_endianness == VCD_FW_BIG_ENDIAN)
+		dma_endian = VIDC_720P_BIG_ENDIAN;
+	else
+		dma_endian = VIDC_720P_LITTLE_ENDIAN;
+
+	interrupt_off = false;
+	interrupt_sel = VIDC_720P_INTERRUPT_LEVEL_SEL;
+
+	intr_mask |= VIDC_720P_INTR_BUFFER_FULL;
+	intr_mask |= VIDC_720P_INTR_FW_DONE;
+	intr_mask |= VIDC_720P_INTR_DMA_DONE;
+	intr_mask |= VIDC_720P_INTR_FRAME_DONE;
+
+	vidc_720p_do_sw_reset();
+
+	DBG_INFO("Loading CONTROL_FW of FW_SIZE %u\n",
+		fw_details.fw_size*4);
+
+	vidc_720p_init(&psz_version,
+			fw_details.fw_size,
+			fw_details.fw_buffer_addr,
+			dma_endian,
+			interrupt_off, interrupt_sel, intr_mask);
+	return;
+}
+
+void ddl_core_start_cpu(struct ddl_context *ddl_context)
+{
+	u32 fw_endianness;
+	enum vidc_720p_endian dma_endian;
+	u32 dbg_core_dump_buf_size = 0;
+
+	vcd_get_fw_property(VCD_FW_ENDIAN, &fw_endianness);
+	if (fw_endianness == VCD_FW_BIG_ENDIAN)
+		dma_endian = VIDC_720P_LITTLE_ENDIAN;
+	else
+		dma_endian = VIDC_720P_BIG_ENDIAN;
+
+	ddl_move_command_state(ddl_context, DDL_CMD_CPU_RESET);
+
+	DBG("VSP_BUF_ADDR_SIZE %d",
+		ddl_context->context_buf_addr.buffer_size);
+	if (ddl_context->enable_dbg_core_dump) {
+		dbg_core_dump_buf_size = ddl_context->dbg_core_dump.
+			buffer_size;
+	}
+
+	vidc_720p_start_cpu(dma_endian,
+		ddl_context->context_buf_addr.align_physical_addr,
+		ddl_context->dbg_core_dump.align_physical_addr,
+		dbg_core_dump_buf_size);
+
+	VIDC_DEBUG_REGISTER_LOG;
+}
+
+void ddl_channel_set(struct ddl_client_context *ddl)
+{
+	enum vidc_720p_enc_dec_selection enc_dec_sel;
+	enum vidc_720p_codec codec;
+	enum vcd_codec *vcd_codec;
+	u32 fw_property_id;
+	struct vcd_fw_details fw_details;
+
+	if (ddl->decoding) {
+		if (vidc_msg_timing)
+			ddl_set_core_start_time(__func__, DEC_OP_TIME);
+		enc_dec_sel = VIDC_720P_DECODER;
+		fw_property_id = VCD_FW_DECODE;
+		vcd_codec = &(ddl->codec_data.decoder.codec.codec);
+	} else {
+		enc_dec_sel = VIDC_720P_ENCODER;
+		fw_property_id = VCD_FW_ENCODE;
+		vcd_codec = &(ddl->codec_data.encoder.codec.codec);
+	}
+	switch (*vcd_codec) {
+	default:
+	case VCD_CODEC_MPEG4:
+		{
+			codec = VIDC_720P_MPEG4;
+
+		if (ddl->decoding) {
+			vidc_720p_decode_set_mpeg4_data_partitionbuffer
+				(ddl->ddl_context->data_partition_tempbuf.
+				 align_physical_addr);
+		}
+
+			break;
+		}
+	case VCD_CODEC_H264:
+		{
+			codec = VIDC_720P_H264;
+			break;
+		}
+	case VCD_CODEC_DIVX_4:
+	case VCD_CODEC_DIVX_5:
+	case VCD_CODEC_DIVX_6:
+		{
+			codec = VIDC_720P_DIVX;
+			break;
+		}
+	case VCD_CODEC_XVID:
+		{
+			codec = VIDC_720P_XVID;
+			break;
+		}
+	case VCD_CODEC_H263:
+		{
+			codec = VIDC_720P_H263;
+			break;
+		}
+	case VCD_CODEC_MPEG2:
+		{
+			codec = VIDC_720P_MPEG2;
+			break;
+		}
+	case VCD_CODEC_VC1:
+	case VCD_CODEC_VC1_RCV:
+		{
+			codec = VIDC_720P_VC1;
+			break;
+		}
+	}
+
+	fw_details.codec = *vcd_codec;
+	vcd_get_fw_property(fw_property_id, &fw_details);
+	VIDC_DEBUG_REGISTER_LOG;
+
+	ddl_move_command_state(ddl->ddl_context, DDL_CMD_CHANNEL_SET);
+	ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_CHDONE);
+
+	DBG_INFO("Loading firmware for CODEC:%u of FW_SIZE:%u\n",
+		fw_details.codec, fw_details.fw_size*4);
+
+	vidc_720p_set_channel(ddl->channel_id,
+			       enc_dec_sel,
+			       codec,
+			       fw_details.fw_buffer_addr,
+			       fw_details.fw_size);
+}
+
+void ddl_decode_init_codec(struct ddl_client_context *ddl)
+{
+	u32 seq_h = 0, seq_e = 0, start_byte_num = 0;
+	struct ddl_decoder_data *decoder = &(ddl->codec_data.decoder);
+	struct vcd_sequence_hdr *seq_hdr = &decoder->decode_config;
+	enum vidc_720p_memory_access_method mem_access_method;
+	if (vidc_msg_timing)
+		ddl_set_core_start_time(__func__, DEC_OP_TIME);
+	ddl_metadata_enable(ddl);
+
+	vidc_720p_decode_set_error_control(true);
+
+	vidc_720p_decode_set_mpeg4Post_filter(decoder->post_filter.
+					       post_filter);
+
+	if (decoder->codec.codec == VCD_CODEC_H264) {
+		vidc_720p_decode_setH264VSPBuffer(decoder->
+						   h264Vsp_temp_buffer.
+						   align_physical_addr);
+		VIDC_LOG1("VSP_BUF_ADDR_SIZE",
+			   decoder->h264Vsp_temp_buffer.buffer_size);
+	}
+
+	if (decoder->codec.codec == VCD_CODEC_VC1_RCV ||
+		decoder->codec.codec == VCD_CODEC_VC1) {
+		vidc_720p_set_frame_size(decoder->client_frame_size.width,
+			decoder->client_frame_size.height);
+	} else {
+		vidc_720p_set_frame_size(0x0, 0x0);
+	}
+
+	switch (decoder->buf_format.buffer_format) {
+	default:
+	case VCD_BUFFER_FORMAT_NV12:
+		{
+			mem_access_method = VIDC_720P_TILE_LINEAR;
+			break;
+		}
+	case VCD_BUFFER_FORMAT_TILE_4x2:
+		{
+			mem_access_method = VIDC_720P_TILE_64x32;
+			break;
+		}
+	}
+	VIDC_LOG_STRING("HEADER-PARSE-START");
+	VIDC_DEBUG_REGISTER_LOG;
+	seq_h = (u32) seq_hdr->sequence_header;
+	start_byte_num = 8 - (seq_h & DDL_STREAMBUF_ALIGN_GUARD_BYTES);
+	seq_e = seq_h + seq_hdr->sequence_header_len;
+	seq_h &= ~(DDL_STREAMBUF_ALIGN_GUARD_BYTES);
+	DDL_PADDING_HACK(seq_e);
+
+	ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_INITCODECDONE);
+	ddl_move_command_state(ddl->ddl_context, DDL_CMD_HEADER_PARSE);
+
+	vidc_720p_decode_bitstream_header(ddl->channel_id,
+		   seq_hdr->sequence_header_len,
+		   start_byte_num,
+		   seq_h,
+		   seq_e,
+		   mem_access_method,
+		   decoder->output_order);
+}
+
+void ddl_decode_dynamic_property(struct ddl_client_context *ddl,
+				 u32 enable)
+{
+	uint8_t *temp = NULL;
+	u32 extra_datastart = 0;
+	struct ddl_decoder_data *decoder = &(ddl->codec_data.decoder);
+	struct vcd_frame_data *bit_stream =
+	    &(ddl->input_frame.vcd_frm);
+
+	if (!enable) {
+		if (decoder->dynmic_prop_change_req) {
+			decoder->dynmic_prop_change_req = false;
+			vidc_720p_decode_dynamic_req_reset();
+		}
+		return;
+	}
+	if ((decoder->dynamic_prop_change &
+				DDL_DEC_REQ_OUTPUT_FLUSH)) {
+		decoder->dynmic_prop_change_req = true;
+		decoder->dynamic_prop_change &= ~(DDL_DEC_REQ_OUTPUT_FLUSH);
+		decoder->dpb_mask.hw_mask = 0;
+		vidc_720p_decode_dynamic_req_set(VIDC_720P_FLUSH_REQ);
+	}
+	if (((decoder->meta_data_enable_flag & VCD_METADATA_PASSTHROUGH))
+	    && ((VCD_FRAME_FLAG_EXTRADATA & bit_stream->flags))
+	    ) {
+
+		temp = ((uint8_t *)bit_stream->physical +
+					bit_stream->offset +
+					bit_stream->data_len + 3);
+
+		extra_datastart = (u32) ((u32)temp & ~3);
+		decoder->dynmic_prop_change_req = true;
+
+		vidc_720p_decode_setpassthrough_start(extra_datastart);
+
+		vidc_720p_decode_dynamic_req_set(VIDC_720P_EXTRADATA);
+	}
+}
+
+void ddl_encode_dynamic_property(struct ddl_client_context *ddl,
+				 u32 enable)
+{
+	struct ddl_encoder_data *encoder = &(ddl->codec_data.encoder);
+	u32 enc_param_change = 0;
+
+	if (!enable) {
+		if (encoder->dynmic_prop_change_req) {
+			encoder->dynmic_prop_change_req = false;
+			encoder->ext_enc_control_val &=
+				~(VIDC_720P_ENC_IFRAME_REQ);
+			vidc_720p_encode_set_control_param
+			(encoder->ext_enc_control_val);
+			vidc_720p_encoder_set_param_change(enc_param_change);
+		}
+		return;
+	}
+	if ((encoder->dynamic_prop_change & DDL_ENC_REQ_IFRAME)) {
+		encoder->dynamic_prop_change &= ~(DDL_ENC_REQ_IFRAME);
+		encoder->ext_enc_control_val |= VIDC_720P_ENC_IFRAME_REQ;
+		vidc_720p_encode_set_control_param
+		(encoder->ext_enc_control_val);
+	}
+	if ((encoder->dynamic_prop_change & DDL_ENC_CHANGE_BITRATE)) {
+		vidc_720p_encode_set_bit_rate(
+		encoder->target_bit_rate.target_bitrate);
+		enc_param_change |= VIDC_720P_ENC_BITRATE_CHANGE;
+		encoder->dynamic_prop_change &= ~(DDL_ENC_CHANGE_BITRATE);
+	}
+	if ((encoder->dynamic_prop_change & DDL_ENC_CHANGE_CIR)) {
+		vidc_720p_encode_set_intra_refresh_mb_number(
+			encoder->intra_refresh.cir_mb_number);
+		encoder->dynamic_prop_change &= ~(DDL_ENC_CHANGE_CIR);
+	}
+	if ((encoder->dynamic_prop_change & DDL_ENC_CHANGE_IPERIOD)) {
+		vidc_720p_encode_set_i_period
+			(encoder->i_period.p_frames);
+		enc_param_change |= VIDC_720P_ENC_IPERIOD_CHANGE;
+		encoder->dynamic_prop_change &= ~(DDL_ENC_CHANGE_IPERIOD);
+	}
+	if ((encoder->dynamic_prop_change &
+				DDL_ENC_CHANGE_FRAMERATE)) {
+		vidc_720p_encode_set_fps
+		    ((encoder->frame_rate.fps_numerator * 1000) /
+		     encoder->frame_rate.fps_denominator);
+		enc_param_change |= VIDC_720P_ENC_FRAMERATE_CHANGE;
+		encoder->dynamic_prop_change &= ~(DDL_ENC_CHANGE_FRAMERATE);
+	}
+	if (enc_param_change)
+		vidc_720p_encoder_set_param_change(enc_param_change);
+}
+
+static void ddl_encode_set_profile_level(struct ddl_client_context *ddl)
+{
+	struct ddl_encoder_data *encoder = &(ddl->codec_data.encoder);
+	u32 profile;
+	u32 level;
+
+	switch (encoder->profile.profile) {
+	default:
+	case VCD_PROFILE_MPEG4_SP:
+		{
+			profile = VIDC_720P_PROFILE_MPEG4_SP;
+			break;
+		}
+	case VCD_PROFILE_MPEG4_ASP:
+		{
+			profile = VIDC_720P_PROFILE_MPEG4_ASP;
+			break;
+		}
+	case VCD_PROFILE_H264_BASELINE:
+		{
+			profile = VIDC_720P_PROFILE_H264_CPB;
+			break;
+		}
+	case VCD_PROFILE_H264_MAIN:
+		{
+			profile = VIDC_720P_PROFILE_H264_MAIN;
+			break;
+		}
+	case VCD_PROFILE_H264_HIGH:
+		{
+			profile = VIDC_720P_PROFILE_H264_HIGH;
+			break;
+		}
+	case VCD_PROFILE_H263_BASELINE:
+		{
+			profile = VIDC_720P_PROFILE_H263_BASELINE;
+			break;
+		}
+	}
+	switch (encoder->level.level) {
+	default:
+	case VCD_LEVEL_MPEG4_0:
+		{
+			level = VIDC_720P_MPEG4_LEVEL0;
+			break;
+		}
+	case VCD_LEVEL_MPEG4_0b:
+		{
+			level = VIDC_720P_MPEG4_LEVEL0b;
+			break;
+		}
+	case VCD_LEVEL_MPEG4_1:
+		{
+			level = VIDC_720P_MPEG4_LEVEL1;
+			break;
+		}
+	case VCD_LEVEL_MPEG4_2:
+		{
+			level = VIDC_720P_MPEG4_LEVEL2;
+			break;
+		}
+	case VCD_LEVEL_MPEG4_3:
+		{
+			level = VIDC_720P_MPEG4_LEVEL3;
+			break;
+		}
+	case VCD_LEVEL_MPEG4_3b:
+		{
+			level = VIDC_720P_MPEG4_LEVEL3b;
+			break;
+		}
+
+	case VCD_LEVEL_MPEG4_4:
+	case VCD_LEVEL_MPEG4_4a:
+		{
+			level = VIDC_720P_MPEG4_LEVEL4a;
+			break;
+		}
+	case VCD_LEVEL_MPEG4_5:
+		{
+			level = VIDC_720P_MPEG4_LEVEL5;
+			break;
+		}
+	case VCD_LEVEL_MPEG4_6:
+		{
+			level = VIDC_720P_MPEG4_LEVEL6;
+			break;
+		}
+	case VCD_LEVEL_H264_1:
+		{
+			level = VIDC_720P_H264_LEVEL1;
+			break;
+		}
+	case VCD_LEVEL_H264_1b:
+		{
+			level = VIDC_720P_H264_LEVEL1b;
+			break;
+		}
+	case VCD_LEVEL_H264_1p1:
+		{
+			level = VIDC_720P_H264_LEVEL1p1;
+			break;
+		}
+	case VCD_LEVEL_H264_1p2:
+		{
+			level = VIDC_720P_H264_LEVEL1p2;
+			break;
+		}
+	case VCD_LEVEL_H264_1p3:
+		{
+			level = VIDC_720P_H264_LEVEL1p3;
+			break;
+		}
+	case VCD_LEVEL_H264_2:
+		{
+			level = VIDC_720P_H264_LEVEL2;
+			break;
+		}
+	case VCD_LEVEL_H264_2p1:
+		{
+			level = VIDC_720P_H264_LEVEL2p1;
+			break;
+		}
+	case VCD_LEVEL_H264_2p2:
+		{
+			level = VIDC_720P_H264_LEVEL2p2;
+			break;
+		}
+	case VCD_LEVEL_H264_3:
+		{
+			level = VIDC_720P_H264_LEVEL3;
+			break;
+		}
+	case VCD_LEVEL_H264_3p1:
+		{
+			level = VIDC_720P_H264_LEVEL3p1;
+			break;
+		}
+	case VCD_LEVEL_H263_10:
+		{
+			level = VIDC_720P_H263_LEVEL10;
+			break;
+		}
+	case VCD_LEVEL_H263_20:
+		{
+			level = VIDC_720P_H263_LEVEL20;
+			break;
+		}
+	case VCD_LEVEL_H263_30:
+		{
+			level = VIDC_720P_H263_LEVEL30;
+			break;
+		}
+	case VCD_LEVEL_H263_40:
+		{
+			level = VIDC_720P_H263_LEVEL40;
+			break;
+		}
+	case VCD_LEVEL_H263_45:
+		{
+			level = VIDC_720P_H263_LEVEL45;
+			break;
+		}
+	case VCD_LEVEL_H263_50:
+		{
+			level = VIDC_720P_H263_LEVEL50;
+			break;
+		}
+	case VCD_LEVEL_H263_60:
+		{
+			level = VIDC_720P_H263_LEVEL60;
+			break;
+		}
+	case VCD_LEVEL_H263_70:
+		{
+			level = VIDC_720P_H263_LEVEL70;
+			break;
+		}
+	}
+	vidc_720p_encode_set_profile(profile, level);
+}
+
+void ddl_encode_init_codec(struct ddl_client_context *ddl)
+{
+	struct ddl_encoder_data *encoder = &(ddl->codec_data.encoder);
+	enum vidc_720p_memory_access_method mem_access_method;
+	enum vidc_720p_DBConfig db_config;
+	enum vidc_720p_MSlice_selection m_slice_sel;
+
+	ddl_encode_set_profile_level(ddl);
+
+	vidc_720p_set_frame_size
+	    (encoder->frame_size.width, encoder->frame_size.height);
+	vidc_720p_encode_set_qp_params
+	    (encoder->qp_range.max_qp, encoder->qp_range.min_qp);
+	vidc_720p_encode_set_rc_config
+	    (encoder->rc_level.frame_level_rc,
+	     encoder->rc_level.mb_level_rc,
+	     encoder->session_qp.i_frame_qp,
+	     encoder->session_qp.p_frame_qp);
+
+	if (encoder->r_cframe_skip) {
+		if (encoder->vb_vbuffer_size) {
+			encoder->ext_enc_control_val = (0x2 << 0x2) |
+			(encoder->vb_vbuffer_size << 0x10);
+		} else
+			encoder->ext_enc_control_val = (0x1 << 2);
+	} else
+		encoder->ext_enc_control_val = 0;
+
+	vidc_720p_encode_set_fps
+	    ((encoder->frame_rate.fps_numerator * 1000) /
+	     encoder->frame_rate.fps_denominator);
+
+	vidc_720p_encode_set_vop_time(
+			encoder->vop_timing.vop_time_resolution, 0);
+
+	if (encoder->rc_level.frame_level_rc) {
+		vidc_720p_encode_set_bit_rate
+		    (encoder->target_bit_rate.target_bitrate);
+
+		vidc_720p_encode_set_frame_level_rc_params
+		    (encoder->frame_level_rc.reaction_coeff);
+	}
+	if (encoder->rc_level.mb_level_rc) {
+		vidc_720p_encode_set_mb_level_rc_params
+		    (encoder->adaptive_rc.dark_region_as_flag,
+		     encoder->adaptive_rc.smooth_region_as_flag,
+		     encoder->adaptive_rc.static_region_as_flag,
+		     encoder->adaptive_rc.activity_region_flag);
+	}
+	if (encoder->codec.codec == VCD_CODEC_MPEG4) {
+		vidc_720p_encode_set_short_header
+		    (encoder->short_header.short_header);
+
+		if (encoder->hdr_ext_control) {
+			vidc_720p_encode_set_hec_period
+			(encoder->hdr_ext_control);
+			encoder->ext_enc_control_val |= (0x1 << 0x1);
+		}
+	}
+	/* set extended encoder control settings */
+	vidc_720p_encode_set_control_param
+	(encoder->ext_enc_control_val);
+
+	if (encoder->codec.codec == VCD_CODEC_H264) {
+		enum vidc_720p_entropy_sel entropy_sel;
+		enum vidc_720p_cabac_model cabac_model_number;
+		switch (encoder->entropy_control.entropy_sel) {
+		default:
+		case VCD_ENTROPY_SEL_CAVLC:
+			{
+				entropy_sel = VIDC_720P_ENTROPY_SEL_CAVLC;
+				break;
+			}
+		case VCD_ENTROPY_SEL_CABAC:
+			{
+				entropy_sel = VIDC_720P_ENTROPY_SEL_CABAC;
+				break;
+			}
+		}
+		switch (encoder->entropy_control.cabac_model) {
+		default:
+		case VCD_CABAC_MODEL_NUMBER_0:
+			{
+				cabac_model_number =
+				    VIDC_720P_CABAC_MODEL_NUMBER_0;
+				break;
+			}
+		case VCD_CABAC_MODEL_NUMBER_1:
+			{
+				cabac_model_number =
+				    VIDC_720P_CABAC_MODEL_NUMBER_1;
+				break;
+			}
+		case VCD_CABAC_MODEL_NUMBER_2:
+			{
+				cabac_model_number =
+				    VIDC_720P_CABAC_MODEL_NUMBER_2;
+				break;
+			}
+		}
+		vidc_720p_encode_set_entropy_control
+		    (entropy_sel, cabac_model_number);
+		switch (encoder->db_control.db_config) {
+		default:
+		case VCD_DB_ALL_BLOCKING_BOUNDARY:
+			{
+				db_config =
+				    VIDC_720P_DB_ALL_BLOCKING_BOUNDARY;
+				break;
+			}
+		case VCD_DB_DISABLE:
+			{
+				db_config =
+				    VIDC_720P_DB_DISABLE;
+				break;
+			}
+		case VCD_DB_SKIP_SLICE_BOUNDARY:
+			{
+				db_config =
+				    VIDC_720P_DB_SKIP_SLICE_BOUNDARY;
+				break;
+			}
+		}
+		vidc_720p_encode_set_db_filter_control
+		    (db_config,
+		     encoder->db_control.slice_alpha_offset,
+		     encoder->db_control.slice_beta_offset);
+	}
+
+	vidc_720p_encode_set_intra_refresh_mb_number
+	    (encoder->intra_refresh.cir_mb_number);
+
+	switch (encoder->multi_slice.m_slice_sel) {
+	default:
+	case VCD_MSLICE_OFF:
+		m_slice_sel = VIDC_720P_MSLICE_OFF;
+		break;
+	case VCD_MSLICE_BY_MB_COUNT:
+		{
+			m_slice_sel = VIDC_720P_MSLICE_BY_MB_COUNT;
+			break;
+		}
+	case VCD_MSLICE_BY_BYTE_COUNT:
+		{
+			m_slice_sel = VIDC_720P_MSLICE_BY_BYTE_COUNT;
+			break;
+		}
+	case VCD_MSLICE_BY_GOB:
+		{
+			m_slice_sel = VIDC_720P_MSLICE_BY_GOB;
+			break;
+		}
+	}
+	vidc_720p_encode_set_multi_slice_info
+	    (m_slice_sel, encoder->multi_slice.m_slice_size);
+
+	vidc_720p_encode_set_dpb_buffer
+	    (encoder->enc_dpb_addr.align_physical_addr,
+			 encoder->enc_dpb_addr.buffer_size);
+
+	VIDC_LOG1("ENC_DPB_ADDR_SIZE", encoder->enc_dpb_addr.buffer_size);
+
+	vidc_720p_encode_set_i_period(encoder->i_period.p_frames);
+
+	ddl_metadata_enable(ddl);
+
+	if (encoder->seq_header.virtual_base_addr) {
+		u32 ext_buffer_start, ext_buffer_end, start_byte_num;
+		ext_buffer_start =
+		    (u32) encoder->seq_header.align_physical_addr;
+		ext_buffer_end =
+		    ext_buffer_start + encoder->seq_header.buffer_size;
+		start_byte_num =
+		    (ext_buffer_start & DDL_STREAMBUF_ALIGN_GUARD_BYTES);
+		ext_buffer_start &= ~(DDL_STREAMBUF_ALIGN_GUARD_BYTES);
+		ext_buffer_end &= ~(DDL_STREAMBUF_ALIGN_GUARD_BYTES);
+		VIDC_LOG1("ENC_SEQHDR_ALLOC_SIZE",
+			   encoder->seq_header.buffer_size);
+		vidc_720p_encode_set_seq_header_buffer(ext_buffer_start,
+							ext_buffer_end,
+							start_byte_num);
+	}
+
+	if (encoder->re_con_buf_format.buffer_format ==
+		VCD_BUFFER_FORMAT_NV12)
+		mem_access_method = VIDC_720P_TILE_LINEAR;
+	else
+		mem_access_method = VIDC_720P_TILE_16x16;
+
+	ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_INITCODECDONE);
+	ddl_move_command_state(ddl->ddl_context, DDL_CMD_INIT_CODEC);
+
+	vidc_720p_encode_init_codec(ddl->channel_id, mem_access_method);
+}
+
+void ddl_channel_end(struct ddl_client_context *ddl)
+{
+	VIDC_DEBUG_REGISTER_LOG;
+
+	ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_CHEND);
+	ddl_move_command_state(ddl->ddl_context, DDL_CMD_CHANNEL_END);
+
+	vidc_720p_submit_command(ddl->channel_id, VIDC_720P_CMD_CHEND);
+}
+
+void ddl_encode_frame_run(struct ddl_client_context *ddl)
+{
+	u32 ext_buffer_start, ext_buffer_end;
+	u32 y_addr, c_addr;
+	u32 start_byte_number = 0;
+	struct ddl_encoder_data *encoder = &(ddl->codec_data.encoder);
+	struct vcd_frame_data *stream = &(ddl->output_frame.vcd_frm);
+
+	ext_buffer_start = (u32) stream->physical + stream->offset;
+	ext_buffer_end = ddl_encode_set_metadata_output_buf(ddl);
+	start_byte_number =
+	    (ext_buffer_start & DDL_STREAMBUF_ALIGN_GUARD_BYTES);
+	if (start_byte_number) {
+		u32 upper_data, lower_data;
+		u32 *align_virtual_addr;
+		ext_buffer_start &= ~(DDL_STREAMBUF_ALIGN_GUARD_BYTES);
+		align_virtual_addr = (u32 *) (((u32) stream->virtual +
+						 stream->offset) -
+						start_byte_number);
+		upper_data = *align_virtual_addr;
+		align_virtual_addr++;
+		lower_data = *align_virtual_addr;
+		vidc_720p_encode_unalign_bitstream(upper_data, lower_data);
+	}
+
+	y_addr = (u32) ddl->input_frame.vcd_frm.physical +
+	    ddl->input_frame.vcd_frm.offset;
+	c_addr = (y_addr + (encoder->frame_size.scan_lines *
+				encoder->frame_size.stride));
+	ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_FRAME_DONE);
+	ddl_move_command_state(ddl->ddl_context, DDL_CMD_ENCODE_FRAME);
+
+	if (encoder->dynamic_prop_change) {
+		encoder->dynmic_prop_change_req = true;
+		ddl_encode_dynamic_property(ddl, true);
+	}
+	vidc_720p_encode_set_vop_time(
+			encoder->vop_timing.vop_time_resolution,
+			ddl->input_frame.frm_delta
+	    );
+
+	vidc_720p_encode_frame(ddl->channel_id,
+			ext_buffer_start,
+				ext_buffer_end,
+				start_byte_number, y_addr, c_addr);
+}
+
+u32 ddl_decode_set_buffers(struct ddl_client_context *ddl)
+{
+	struct ddl_decoder_data *decoder = &(ddl->codec_data.decoder);
+	u32 comv_buf_size = DDL_COMV_BUFLINE_NO, comv_buf_no = 0;
+	u32 ref_buf_no = 0;
+	struct ddl_context  *ddl_ctxt = NULL;
+
+	if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_DPB)) {
+		VIDC_LOG_STRING("STATE-CRITICAL");
+		return VCD_ERR_FAIL;
+	}
+	if (vidc_msg_timing)
+		ddl_set_core_start_time(__func__, DEC_OP_TIME);
+	switch (decoder->codec.codec) {
+	default:
+	case VCD_CODEC_DIVX_4:
+	case VCD_CODEC_DIVX_5:
+	case VCD_CODEC_DIVX_6:
+	case VCD_CODEC_XVID:
+	case VCD_CODEC_MPEG2:
+	case VCD_CODEC_MPEG4:
+		{
+			comv_buf_no = DDL_MPEG_COMV_BUF_NO;
+			ref_buf_no = DDL_MPEG_REFBUF_COUNT;
+			break;
+		}
+	case VCD_CODEC_H263:
+		{
+			comv_buf_no = DDL_H263_COMV_BUF_NO;
+			break;
+		}
+	case VCD_CODEC_VC1:
+	case VCD_CODEC_VC1_RCV:
+		{
+			comv_buf_no =
+			    decoder->client_output_buf_req.actual_count + 1;
+			comv_buf_size = DDL_VC1_COMV_BUFLINE_NO;
+			break;
+		}
+	case VCD_CODEC_H264:
+		{
+			if (decoder->idr_only_decoding)
+				comv_buf_no = decoder->min_dpb_num;
+			else
+				comv_buf_no =
+					decoder->
+					client_output_buf_req.
+					actual_count;
+			break;
+		}
+	}
+
+	if (comv_buf_no) {
+		comv_buf_size *= (comv_buf_no *
+			(decoder->client_frame_size.stride >> 4) *
+			((decoder->client_frame_size.scan_lines >> 4) + 1));
+		if (decoder->dpb_comv_buffer.virtual_base_addr)
+			ddl_pmem_free(&decoder->dpb_comv_buffer);
+		ddl_pmem_alloc(&decoder->dpb_comv_buffer, comv_buf_size,
+			       DDL_LINEAR_BUFFER_ALIGN_BYTES);
+		if (!decoder->dpb_comv_buffer.virtual_base_addr) {
+			VIDC_LOGERR_STRING
+			    ("Dec_set_buf:Comv_buf_alloc_failed");
+			return VCD_ERR_ALLOC_FAIL;
+		}
+		vidc_720p_decode_set_comv_buffer(decoder->dpb_comv_buffer.
+						  align_physical_addr,
+						  decoder->dpb_comv_buffer.
+						  buffer_size);
+	}
+	decoder->ref_buffer.align_physical_addr = NULL;
+	if (ref_buf_no) {
+		size_t sz, align_bytes, y_sz, frm_sz;
+		u32 i = 0;
+		sz = decoder->dp_buf.dec_pic_buffers[0].vcd_frm.alloc_len;
+		frm_sz = sz;
+		y_sz = decoder->client_frame_size.height *
+				decoder->client_frame_size.width;
+		sz *= ref_buf_no;
+		align_bytes = decoder->client_output_buf_req.align;
+		if (decoder->ref_buffer.virtual_base_addr)
+			ddl_pmem_free(&decoder->ref_buffer);
+		ddl_pmem_alloc(&decoder->ref_buffer, sz, align_bytes);
+		if (!decoder->ref_buffer.virtual_base_addr) {
+			ddl_pmem_free(&decoder->dpb_comv_buffer);
+			VIDC_LOGERR_STRING
+			    ("Dec_set_buf:mpeg_ref_buf_alloc_failed");
+			return VCD_ERR_ALLOC_FAIL;
+		}
+		memset((u8 *)decoder->ref_buffer.virtual_base_addr,
+			0x80, sz);
+		for (i = 0; i < ref_buf_no; i++)
+			memset((u8 *)decoder->ref_buffer.align_virtual_addr +
+				i*frm_sz, 0x10, y_sz);
+	}
+	ddl_decode_set_metadata_output(decoder);
+	ddl_decoder_dpb_transact(decoder, NULL, DDL_DPB_OP_INIT);
+	ddl_ctxt = ddl_get_context();
+	vidc_720p_set_deblock_line_buffer(
+		ddl_ctxt->db_line_buffer.align_physical_addr,
+		ddl_ctxt->db_line_buffer.buffer_size);
+	ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_DPBDONE);
+	ddl_move_command_state(ddl->ddl_context, DDL_CMD_DECODE_SET_DPB);
+
+	vidc_720p_submit_command(ddl->channel_id,
+		VIDC_720P_CMD_INITBUFFERS);
+	return VCD_S_SUCCESS;
+}
+
+void ddl_decode_frame_run(struct ddl_client_context *ddl)
+{
+	u32 ext_buffer_start = 0, ext_buffer_end = 0;
+	u32 start_byte_num = 8;
+	struct ddl_decoder_data *decoder = &ddl->codec_data.decoder;
+	struct vcd_frame_data *bit_stream =
+	    &(ddl->input_frame.vcd_frm);
+	if (vidc_msg_timing) {
+		ddl_set_core_start_time(__func__, DEC_OP_TIME);
+		ddl_set_core_start_time(__func__, DEC_IP_TIME);
+	}
+	if (!bit_stream->data_len ||
+		!bit_stream->physical) {
+		ddl_decode_eos_run(ddl);
+		return;
+	}
+
+	ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_FRAME_DONE);
+
+	ddl_decode_dynamic_property(ddl, true);
+
+	ddl_decoder_dpb_transact(decoder, NULL, DDL_DPB_OP_SET_MASK);
+
+	ext_buffer_start = (u32)bit_stream->physical +
+		bit_stream->offset;
+	start_byte_num = 8 - (ext_buffer_start &
+		DDL_STREAMBUF_ALIGN_GUARD_BYTES);
+	ext_buffer_end = ext_buffer_start + bit_stream->data_len;
+	ext_buffer_start &= ~(DDL_STREAMBUF_ALIGN_GUARD_BYTES);
+	DDL_PADDING_HACK(ext_buffer_end);
+
+	ddl_move_command_state(ddl->ddl_context, DDL_CMD_DECODE_FRAME);
+
+	vidc_720p_decode_frame(ddl->channel_id,
+			ext_buffer_start,
+			ext_buffer_end,
+			bit_stream->data_len,
+			start_byte_num, bit_stream->ip_frm_tag);
+}
+
+void  ddl_decode_eos_run(struct ddl_client_context *ddl)
+{
+	struct ddl_decoder_data *decoder = &ddl->codec_data.decoder;
+
+	ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_EOS_DONE);
+
+	ddl_decode_dynamic_property(ddl, true);
+
+	ddl_decoder_dpb_transact(decoder, NULL, DDL_DPB_OP_SET_MASK);
+
+	decoder->dynmic_prop_change_req = true;
+
+	ddl_move_command_state(ddl->ddl_context, DDL_CMD_EOS);
+
+	vidc_720p_issue_eos(ddl->channel_id);
+}
+
+u32 ddl_hal_engine_reset(struct ddl_context *ddl_context)
+{
+	u32 eng_reset;
+	u32 channel_id = 0;
+	u32 fw_endianness;
+	enum vidc_720p_endian dma_endian;
+	enum vidc_720p_interrupt_level_selection interrupt_sel;
+	u32 intr_mask = 0x0;
+
+	if (ddl_context->current_ddl)
+		channel_id = ddl_context->current_ddl->channel_id;
+
+	interrupt_sel = VIDC_720P_INTERRUPT_LEVEL_SEL;
+	/* Enable all the supported interrupt */
+	intr_mask |= VIDC_720P_INTR_BUFFER_FULL;
+	intr_mask |= VIDC_720P_INTR_FW_DONE;
+	intr_mask |= VIDC_720P_INTR_DMA_DONE;
+	intr_mask |= VIDC_720P_INTR_FRAME_DONE;
+
+	vcd_get_fw_property(VCD_FW_ENDIAN, &fw_endianness);
+	/* Reverse the endianness settings after boot code download */
+	if (fw_endianness == VCD_FW_BIG_ENDIAN)
+		dma_endian = VIDC_720P_LITTLE_ENDIAN;
+	else
+		dma_endian = VIDC_720P_BIG_ENDIAN;
+
+	/* Need to reset MFC silently */
+	eng_reset = vidc_720p_engine_reset(
+		channel_id,
+		dma_endian, interrupt_sel,
+		intr_mask);
+	if (!eng_reset) {
+		/* call the hw fatal callback if engine reset fails */
+		ddl_hw_fatal_cb(ddl_context);
+	}
+	return eng_reset ;
+}
diff --git a/drivers/video/msm/vidc/720p/ddl/vcd_ddl_helper.c b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_helper.c
new file mode 100644
index 0000000..15adf21
--- /dev/null
+++ b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_helper.c
@@ -0,0 +1,297 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <media/msm/vidc_type.h>
+#include "vcd_ddl_utils.h"
+
+DDL_INLINE struct ddl_context *ddl_get_context(void)
+{
+	static struct ddl_context ddl_context;
+	return &ddl_context;
+}
+
+DDL_INLINE void ddl_move_client_state(struct ddl_client_context *ddl,
+				      enum ddl_client_state client_state)
+{
+	ddl->client_state = client_state;
+}
+
+DDL_INLINE void ddl_move_command_state(struct ddl_context *ddl_context,
+				       enum ddl_cmd_state command_state)
+{
+	ddl_context->cmd_state = command_state;
+}
+
+u32 ddl_client_transact(u32 operation,
+			struct ddl_client_context **pddl_client)
+{
+	u32 ret_status = VCD_ERR_FAIL;
+	u32 counter;
+	struct ddl_context *ddl_context;
+
+	ddl_context = ddl_get_context();
+	switch (operation) {
+	case DDL_FREE_CLIENT:
+		{
+			if (pddl_client && *pddl_client) {
+				u32 channel_id;
+				channel_id = (*pddl_client)->channel_id;
+				if (channel_id < VCD_MAX_NO_CLIENT) {
+					ddl_context->
+					    ddl_clients[channel_id] = NULL;
+				} else {
+					VIDC_LOG_STRING("CHID_CORRUPTION");
+				}
+				DDL_FREE(*pddl_client);
+				ret_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case DDL_GET_CLIENT:
+		{
+			ret_status = VCD_ERR_MAX_CLIENT;
+			for (counter = 0; counter < VCD_MAX_NO_CLIENT &&
+			     ret_status == VCD_ERR_MAX_CLIENT; ++counter) {
+				if (!ddl_context->ddl_clients[counter]) {
+					*pddl_client =
+					    (struct ddl_client_context *)
+					    DDL_MALLOC(sizeof
+					       (struct ddl_client_context)
+					       );
+					if (!*pddl_client) {
+						ret_status = VCD_ERR_ALLOC_FAIL;
+					} else {
+						DDL_MEMSET(*pddl_client, 0,
+						   sizeof(struct
+						   ddl_client_context));
+						ddl_context->
+						    ddl_clients[counter] =
+						    *pddl_client;
+						(*pddl_client)->channel_id =
+						    counter;
+						(*pddl_client)->ddl_context =
+						    ddl_context;
+						ret_status = VCD_S_SUCCESS;
+					}
+				}
+			}
+			break;
+		}
+	case DDL_INIT_CLIENTS:
+		{
+			for (counter = 0; counter < VCD_MAX_NO_CLIENT;
+			     ++counter) {
+				ddl_context->ddl_clients[counter] = NULL;
+			}
+			ret_status = VCD_S_SUCCESS;
+			break;
+		}
+	case DDL_ACTIVE_CLIENT:
+		{
+			for (counter = 0; counter < VCD_MAX_NO_CLIENT;
+			     ++counter) {
+				if (ddl_context->ddl_clients[counter]) {
+					ret_status = VCD_S_SUCCESS;
+					break;
+				}
+			}
+			break;
+		}
+	default:
+		{
+			ret_status = VCD_ERR_ILLEGAL_PARM;
+			break;
+		}
+	}
+	return ret_status;
+}
+
+u32 ddl_decoder_dpb_transact(struct ddl_decoder_data *decoder,
+			     struct ddl_frame_data_tag *in_out_frame,
+			     u32 operation)
+{
+	u32 vcd_status = VCD_S_SUCCESS;
+	u32 loopc;
+	struct ddl_frame_data_tag *found_frame = NULL;
+	struct ddl_mask *dpb_mask = &decoder->dpb_mask;
+	u32 temp_mask;
+
+	switch (operation) {
+	case DDL_DPB_OP_MARK_BUSY:
+	case DDL_DPB_OP_MARK_FREE:
+		{
+			for (loopc = 0; !found_frame &&
+			     loopc < decoder->dp_buf.no_of_dec_pic_buf;
+			     ++loopc) {
+				if (in_out_frame->vcd_frm.physical ==
+				    decoder->dp_buf.
+				    dec_pic_buffers[loopc].vcd_frm.
+				    physical) {
+					found_frame =
+					    &(decoder->dp_buf.
+					      dec_pic_buffers[loopc]);
+					break;
+				}
+			}
+
+			if (found_frame) {
+				if (operation == DDL_DPB_OP_MARK_BUSY) {
+					temp_mask = (~(0x1 << loopc));
+					if (decoder->idr_only_decoding)
+						temp_mask = ~(0xffffffff);
+					dpb_mask->hw_mask &= temp_mask;
+					*in_out_frame = *found_frame;
+				} else if (operation ==
+					DDL_DPB_OP_MARK_FREE) {
+					temp_mask = (0x1 << loopc);
+					if (decoder->idr_only_decoding)
+						temp_mask = 0xffffffff;
+					dpb_mask->client_mask |= temp_mask;
+					*found_frame = *in_out_frame;
+				}
+			} else {
+				in_out_frame->vcd_frm.physical = NULL;
+				in_out_frame->vcd_frm.virtual = NULL;
+				vcd_status = VCD_ERR_BAD_POINTER;
+				VIDC_LOG_STRING("BUF_NOT_FOUND");
+			}
+			break;
+		}
+	case DDL_DPB_OP_SET_MASK:
+		{
+			dpb_mask->hw_mask |= dpb_mask->client_mask;
+			dpb_mask->client_mask = 0;
+			vidc_720p_decode_set_dpb_release_buffer_mask
+			    (dpb_mask->hw_mask);
+			break;
+		}
+	case DDL_DPB_OP_INIT:
+		{
+			u32 dpb_size, index, num_dpb;
+			dpb_size = (!decoder->meta_data_offset) ?
+			    decoder->dp_buf.dec_pic_buffers[0].vcd_frm.
+			    alloc_len : decoder->meta_data_offset;
+			if (decoder->idr_only_decoding)
+				num_dpb = decoder->min_dpb_num;
+			else
+				num_dpb = decoder->dp_buf.no_of_dec_pic_buf;
+			vidc_720p_decode_set_dpb_details(
+						  num_dpb,
+						  dpb_size,
+						  decoder->ref_buffer.
+						  align_physical_addr);
+			for (loopc = 0; loopc < num_dpb; ++loopc) {
+				if (decoder->idr_only_decoding)
+					index = 0;
+				else
+					index = loopc;
+				vidc_720p_decode_set_dpb_buffers(loopc,
+							  (u32 *)
+							  decoder->
+							  dp_buf.
+							  dec_pic_buffers
+							  [index].
+							  vcd_frm.
+							  physical);
+				VIDC_LOG1("DEC_DPB_BUFn_SIZE=%d",
+					   decoder->dp_buf.
+					   dec_pic_buffers[index].vcd_frm.
+					   alloc_len);
+			}
+			break;
+		}
+	case DDL_DPB_OP_RETRIEVE:
+		{
+			u32 position;
+			if (dpb_mask->client_mask) {
+				position = 0x1;
+				for (loopc = 0;
+				     loopc <
+				     decoder->dp_buf.no_of_dec_pic_buf
+				     && !found_frame; ++loopc) {
+					if (dpb_mask->
+					    client_mask & position) {
+						found_frame =
+						    &decoder->dp_buf.
+						    dec_pic_buffers[loopc];
+						dpb_mask->client_mask &=
+						    ~(position);
+					}
+					position <<= 1;
+				}
+			} else if (dpb_mask->hw_mask) {
+				position = 0x1;
+				for (loopc = 0;
+				     loopc <
+				     decoder->dp_buf.no_of_dec_pic_buf
+				     && !found_frame; ++loopc) {
+					if (dpb_mask->hw_mask
+							& position) {
+						found_frame =
+						    &decoder->dp_buf.
+						    dec_pic_buffers[loopc];
+						dpb_mask->hw_mask &=
+						    ~(position);
+					}
+					position <<= 1;
+				}
+			}
+			if (found_frame)
+				*in_out_frame = *found_frame;
+			else {
+				in_out_frame->vcd_frm.physical = NULL;
+				in_out_frame->vcd_frm.virtual = NULL;
+			}
+			break;
+		}
+	}
+	return vcd_status;
+}
+
+void ddl_release_context_buffers(struct ddl_context *ddl_context)
+{
+	ddl_pmem_free(&ddl_context->context_buf_addr);
+	ddl_pmem_free(&ddl_context->db_line_buffer);
+	ddl_pmem_free(&ddl_context->data_partition_tempbuf);
+	ddl_pmem_free(&ddl_context->metadata_shared_input);
+	ddl_pmem_free(&ddl_context->dbg_core_dump);
+
+	vcd_fw_release();
+}
+
+void ddl_release_client_internal_buffers(struct ddl_client_context *ddl)
+{
+	if (ddl->decoding) {
+		struct ddl_decoder_data *decoder =
+		    &(ddl->codec_data.decoder);
+		ddl_pmem_free(&decoder->h264Vsp_temp_buffer);
+		ddl_pmem_free(&decoder->dpb_comv_buffer);
+		ddl_pmem_free(&decoder->ref_buffer);
+		DDL_FREE(decoder->dp_buf.dec_pic_buffers);
+		ddl_decode_dynamic_property(ddl, false);
+		decoder->decode_config.sequence_header_len = 0;
+		decoder->decode_config.sequence_header = NULL;
+		decoder->dpb_mask.client_mask = 0;
+		decoder->dpb_mask.hw_mask = 0;
+		decoder->dp_buf.no_of_dec_pic_buf = 0;
+		decoder->dynamic_prop_change = 0;
+
+	} else {
+		struct ddl_encoder_data *encoder =
+		    &(ddl->codec_data.encoder);
+		ddl_pmem_free(&encoder->enc_dpb_addr);
+		ddl_pmem_free(&encoder->seq_header);
+		ddl_encode_dynamic_property(ddl, false);
+		encoder->dynamic_prop_change = 0;
+	}
+}
diff --git a/drivers/video/msm/vidc/720p/ddl/vcd_ddl_internal_property.h b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_internal_property.h
new file mode 100644
index 0000000..7e201cf
--- /dev/null
+++ b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_internal_property.h
@@ -0,0 +1,81 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef _VCD_DDL_INTERNAL_PROPERTY_H_
+#define _VCD_DDL_INTERNAL_PROPERTY_H_
+#include <media/msm/vcd_api.h>
+
+#define VCD_EVT_RESP_DDL_BASE          0x3000
+#define VCD_EVT_RESP_DEVICE_INIT       (VCD_EVT_RESP_DDL_BASE + 0x1)
+#define VCD_EVT_RESP_OUTPUT_REQ        (VCD_EVT_RESP_DDL_BASE + 0x2)
+#define VCD_EVT_RESP_EOS_DONE          (VCD_EVT_RESP_DDL_BASE + 0x3)
+#define VCD_EVT_RESP_TRANSACTION_PENDING (VCD_EVT_RESP_DDL_BASE + 0x4)
+
+#define VCD_S_DDL_ERR_BASE     0x90000000
+#define VCD_ERR_MAX_NO_CODEC   (VCD_S_DDL_ERR_BASE + 0x1)
+#define VCD_ERR_CLIENT_PRESENT (VCD_S_DDL_ERR_BASE + 0x2)
+#define VCD_ERR_CLIENT_FATAL   (VCD_S_DDL_ERR_BASE + 0x3)
+
+#define VCD_I_CUSTOM_BASE  (VCD_I_RESERVED_BASE)
+#define VCD_I_RC_LEVEL_CONFIG (VCD_I_CUSTOM_BASE + 0x1)
+#define VCD_I_FRAME_LEVEL_RC (VCD_I_CUSTOM_BASE + 0x2)
+#define VCD_I_ADAPTIVE_RC    (VCD_I_CUSTOM_BASE + 0x3)
+#define VCD_I_CUSTOM_DDL_BASE  (VCD_I_RESERVED_BASE + 0x100)
+#define DDL_I_INPUT_BUF_REQ  (VCD_I_CUSTOM_DDL_BASE + 0x1)
+#define DDL_I_OUTPUT_BUF_REQ (VCD_I_CUSTOM_DDL_BASE + 0x2)
+#define DDL_I_DPB       (VCD_I_CUSTOM_DDL_BASE + 0x3)
+#define DDL_I_DPB_RELEASE    (VCD_I_CUSTOM_DDL_BASE + 0x4)
+#define DDL_I_DPB_RETRIEVE  (VCD_I_CUSTOM_DDL_BASE + 0x5)
+#define DDL_I_REQ_OUTPUT_FLUSH   (VCD_I_CUSTOM_DDL_BASE + 0x6)
+#define DDL_I_SEQHDR_ALIGN_BYTES (VCD_I_CUSTOM_DDL_BASE + 0x7)
+#define DDL_I_SEQHDR_PRESENT (VCD_I_CUSTOM_DDL_BASE + 0xb)
+#define DDL_I_CAPABILITY    (VCD_I_CUSTOM_DDL_BASE + 0x8)
+#define DDL_I_FRAME_PROC_UNITS    (VCD_I_CUSTOM_DDL_BASE + 0x9)
+
+struct vcd_property_rc_level {
+	u32 frame_level_rc;
+	u32 mb_level_rc;
+};
+
+struct vcd_property_frame_level_rc_params {
+	u32 reaction_coeff;
+};
+
+struct vcd_property_adaptive_rc_params {
+	u32 dark_region_as_flag;
+	u32 smooth_region_as_flag;
+	u32 static_region_as_flag;
+	u32 activity_region_flag;
+};
+
+struct vcd_property_slice_delivery_info {
+	u32  enable;
+	u32  num_slices;
+	u32  num_slices_enc;
+};
+
+struct ddl_frame_data_tag;
+
+struct ddl_property_dec_pic_buffers {
+	struct ddl_frame_data_tag *dec_pic_buffers;
+	u32 no_of_dec_pic_buf;
+};
+
+struct ddl_property_capability {
+	u32 max_num_client;
+	u32 general_command_depth;
+	u32 frame_command_depth;
+	u32 exclusive;
+	u32   ddl_time_out_in_ms;
+};
+
+#endif
diff --git a/drivers/video/msm/vidc/720p/ddl/vcd_ddl_interrupt_handler.c b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_interrupt_handler.c
new file mode 100644
index 0000000..5fa9b09
--- /dev/null
+++ b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_interrupt_handler.c
@@ -0,0 +1,1130 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <media/msm/vidc_type.h>
+#include "vidc.h"
+#include "vcd_ddl_utils.h"
+#include "vcd_ddl_metadata.h"
+
+#if DEBUG
+#define DBG(x...) printk(KERN_DEBUG x)
+#else
+#define DBG(x...)
+#endif
+
+static void ddl_decoder_input_done_callback(
+	struct	ddl_client_context *ddl, u32 frame_transact_end);
+static u32 ddl_decoder_output_done_callback(
+	struct ddl_client_context *ddl, u32 frame_transact_end);
+
+static u32 ddl_get_frame
+    (struct vcd_frame_data *frame, u32 frame_type);
+
+static void ddl_getdec_profilelevel
+(struct ddl_decoder_data   *decoder, u32 profile, u32 level);
+
+static void ddl_dma_done_callback(struct ddl_context *ddl_context)
+{
+	if (!DDLCOMMAND_STATE_IS(ddl_context, DDL_CMD_DMA_INIT)) {
+		VIDC_LOGERR_STRING("UNKWN_DMADONE");
+		return;
+	}
+	ddl_move_command_state(ddl_context, DDL_CMD_INVALID);
+	VIDC_LOG_STRING("DMA_DONE");
+	ddl_core_start_cpu(ddl_context);
+}
+
+static void ddl_cpu_started_callback(struct ddl_context *ddl_context)
+{
+	ddl_move_command_state(ddl_context, DDL_CMD_INVALID);
+	VIDC_LOG_STRING("CPU-STARTED");
+
+	if (!vidc_720p_cpu_start()) {
+		ddl_hw_fatal_cb(ddl_context);
+		return;
+	}
+
+	vidc_720p_set_deblock_line_buffer(
+			ddl_context->db_line_buffer.align_physical_addr,
+			ddl_context->db_line_buffer.buffer_size);
+	ddl_context->device_state = DDL_DEVICE_INITED;
+	ddl_context->ddl_callback(VCD_EVT_RESP_DEVICE_INIT, VCD_S_SUCCESS,
+			NULL, 0, NULL, ddl_context->client_data);
+	DDL_IDLE(ddl_context);
+}
+
+
+static u32 ddl_eos_done_callback(struct ddl_context *ddl_context)
+{
+	struct ddl_client_context *ddl = ddl_context->current_ddl;
+	u32 displaystatus, resl_change;
+
+	if (!DDLCOMMAND_STATE_IS(ddl_context, DDL_CMD_EOS)) {
+		VIDC_LOGERR_STRING("UNKWN_EOSDONE");
+		ddl_client_fatal_cb(ddl_context);
+		return true;
+	}
+
+	if (!ddl ||
+		!ddl->decoding ||
+		!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_EOS_DONE)
+		) {
+		VIDC_LOG_STRING("STATE-CRITICAL-EOSDONE");
+		ddl_client_fatal_cb(ddl_context);
+		return true;
+	}
+	ddl_move_command_state(ddl_context, DDL_CMD_INVALID);
+
+	vidc_720p_eos_info(&displaystatus, &resl_change);
+	if ((enum vidc_720p_display_status)displaystatus
+		!= VIDC_720P_EMPTY_BUFFER) {
+		VIDC_LOG_STRING("EOSDONE-EMPTYBUF-ISSUE");
+	}
+
+	ddl_decode_dynamic_property(ddl, false);
+	if (resl_change == 0x1) {
+		ddl->codec_data.decoder.header_in_start = false;
+		ddl->codec_data.decoder.decode_config.sequence_header =
+			ddl->input_frame.vcd_frm.physical;
+		ddl->codec_data.decoder.decode_config.sequence_header_len =
+			ddl->input_frame.vcd_frm.data_len;
+		ddl_decode_init_codec(ddl);
+		return false;
+	}
+	ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_FRAME);
+	VIDC_LOG_STRING("EOS_DONE");
+	ddl_context->ddl_callback(VCD_EVT_RESP_EOS_DONE, VCD_S_SUCCESS,
+		NULL, 0, (u32 *) ddl, ddl_context->client_data);
+	DDL_IDLE(ddl_context);
+
+	return true;
+}
+
+static u32 ddl_channel_set_callback(struct ddl_context *ddl_context)
+{
+	struct ddl_client_context *ddl = ddl_context->current_ddl;
+	u32 return_status = false;
+
+	ddl_move_command_state(ddl_context, DDL_CMD_INVALID);
+	VIDC_DEBUG_REGISTER_LOG;
+
+	if (!ddl ||
+		!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_CHDONE)
+		) {
+		VIDC_LOG_STRING("STATE-CRITICAL-CHSET");
+		DDL_IDLE(ddl_context);
+		return return_status;
+	}
+	VIDC_LOG_STRING("Channel-set");
+	ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_INITCODEC);
+
+	if (ddl->decoding) {
+		if (vidc_msg_timing)
+			ddl_calc_core_proc_time(__func__, DEC_OP_TIME);
+		if (ddl->codec_data.decoder.header_in_start) {
+			ddl_decode_init_codec(ddl);
+		} else {
+			ddl_context->ddl_callback(VCD_EVT_RESP_START,
+				VCD_S_SUCCESS, NULL,
+				0, (u32 *) ddl,
+				ddl_context->client_data);
+
+			DDL_IDLE(ddl_context);
+			return_status = true;
+		}
+	} else {
+		ddl_encode_init_codec(ddl);
+	}
+	return return_status;
+}
+
+static void ddl_init_codec_done_callback(struct ddl_context *ddl_context)
+{
+	struct ddl_client_context *ddl = ddl_context->current_ddl;
+	struct ddl_encoder_data *encoder;
+
+	if (!ddl ||
+		ddl->decoding ||
+		!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_INITCODECDONE)
+		) {
+		VIDC_LOG_STRING("STATE-CRITICAL-INITCODEC");
+		ddl_client_fatal_cb(ddl_context);
+		return;
+	}
+	ddl_move_command_state(ddl_context, DDL_CMD_INVALID);
+	ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_FRAME);
+	VIDC_LOG_STRING("INIT_CODEC_DONE");
+
+	encoder = &ddl->codec_data.encoder;
+	if (encoder->seq_header.virtual_base_addr) {
+		vidc_720p_encode_get_header(&encoder->seq_header.
+			buffer_size);
+	}
+
+	ddl_context->ddl_callback(VCD_EVT_RESP_START, VCD_S_SUCCESS, NULL,
+		0, (u32 *) ddl, ddl_context->client_data);
+
+	DDL_IDLE(ddl_context);
+}
+
+static u32 ddl_header_done_callback(struct ddl_context *ddl_context)
+{
+	struct ddl_client_context *ddl = ddl_context->current_ddl;
+	struct ddl_decoder_data *decoder;
+	struct vidc_720p_seq_hdr_info seq_hdr_info;
+
+	u32 process_further = true;
+	u32 seq_hdr_only_frame = false;
+	u32 need_reconfig = true;
+	struct vcd_frame_data *input_vcd_frm;
+	struct ddl_frame_data_tag *reconfig_payload = NULL;
+	u32 reconfig_payload_size = 0;
+
+	if (!ddl ||
+		!ddl->decoding ||
+		!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_INITCODECDONE)
+		) {
+		VIDC_LOG_STRING("STATE-CRITICAL-HDDONE");
+		ddl_client_fatal_cb(ddl_context);
+		return true;
+	}
+	if (vidc_msg_timing)
+		ddl_calc_core_proc_time(__func__, DEC_OP_TIME);
+	ddl_move_command_state(ddl_context, DDL_CMD_INVALID);
+	ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_DPB);
+	VIDC_LOG_STRING("HEADER_DONE");
+	VIDC_DEBUG_REGISTER_LOG;
+
+	vidc_720p_decode_get_seq_hdr_info(&seq_hdr_info);
+
+	decoder = &(ddl->codec_data.decoder);
+	decoder->frame_size.width = seq_hdr_info.img_size_x;
+	decoder->frame_size.height = seq_hdr_info.img_size_y;
+	decoder->min_dpb_num = seq_hdr_info.min_num_dpb;
+	decoder->y_cb_cr_size = seq_hdr_info.min_dpb_size;
+	decoder->progressive_only = 1 - seq_hdr_info.progressive;
+	if (!seq_hdr_info.img_size_x || !seq_hdr_info.img_size_y) {
+		VIDC_LOGERR_STRING("FATAL: ZeroImageSize");
+		ddl_client_fatal_cb(ddl_context);
+		return process_further;
+	}
+	if (seq_hdr_info.data_partitioned == 0x1 &&
+		decoder->codec.codec == VCD_CODEC_MPEG4 &&
+		seq_hdr_info.img_size_x > DDL_MAX_DP_FRAME_WIDTH &&
+		seq_hdr_info.img_size_y > DDL_MAX_DP_FRAME_HEIGHT)	{
+		ddl_client_fatal_cb(ddl_context);
+		return process_further;
+	}
+	ddl_getdec_profilelevel(decoder, seq_hdr_info.profile,
+		seq_hdr_info.level);
+	ddl_calculate_stride(&decoder->frame_size,
+			!decoder->progressive_only,
+			decoder->codec.codec);
+	if (decoder->buf_format.buffer_format == VCD_BUFFER_FORMAT_TILE_4x2) {
+		decoder->frame_size.stride =
+		DDL_TILE_ALIGN(decoder->frame_size.width,
+					DDL_TILE_ALIGN_WIDTH);
+		decoder->frame_size.scan_lines =
+			DDL_TILE_ALIGN(decoder->frame_size.height,
+						 DDL_TILE_ALIGN_HEIGHT);
+	}
+	if (seq_hdr_info.crop_exists)	{
+		decoder->frame_size.width -=
+		(seq_hdr_info.crop_right_offset
+		+ seq_hdr_info.crop_left_offset);
+		decoder->frame_size.height -=
+		(seq_hdr_info.crop_top_offset +
+		seq_hdr_info.crop_bottom_offset);
+	}
+	ddl_set_default_decoder_buffer_req(decoder, false);
+
+	if (decoder->header_in_start) {
+		decoder->client_frame_size = decoder->frame_size;
+		decoder->client_output_buf_req =
+			decoder->actual_output_buf_req;
+		decoder->client_input_buf_req =
+			decoder->actual_input_buf_req;
+		ddl_context->ddl_callback(VCD_EVT_RESP_START, VCD_S_SUCCESS,
+			NULL, 0, (u32 *) ddl,	ddl_context->client_data);
+		DDL_IDLE(ddl_context);
+	} else {
+		DBG("%s(): Client data: WxH(%u x %u) SxSL(%u x %u) Sz(%u)\n",
+			__func__, decoder->client_frame_size.width,
+			decoder->client_frame_size.height,
+			decoder->client_frame_size.stride,
+			decoder->client_frame_size.scan_lines,
+			decoder->client_output_buf_req.sz);
+		DBG("%s(): DDL data: WxH(%u x %u) SxSL(%u x %u) Sz(%u)\n",
+			__func__, decoder->frame_size.width,
+			decoder->frame_size.height,
+			decoder->frame_size.stride,
+			decoder->frame_size.scan_lines,
+			decoder->actual_output_buf_req.sz);
+		DBG("%s(): min_dpb_num = %d actual_count = %d\n", __func__,
+			decoder->min_dpb_num,
+			decoder->client_output_buf_req.actual_count);
+
+		input_vcd_frm = &(ddl->input_frame.vcd_frm);
+
+		if (decoder->frame_size.width ==
+			decoder->client_frame_size.width
+			&& decoder->frame_size.height ==
+			decoder->client_frame_size.height
+			&& decoder->frame_size.stride ==
+			decoder->client_frame_size.stride
+			&& decoder->frame_size.scan_lines ==
+			decoder->client_frame_size.scan_lines
+			&& decoder->actual_output_buf_req.sz <=
+			decoder->client_output_buf_req.sz
+			&& decoder->actual_output_buf_req.actual_count <=
+			decoder->client_output_buf_req.actual_count
+			&& decoder->progressive_only)
+			need_reconfig = false;
+		if (input_vcd_frm->flags & VCD_FRAME_FLAG_EOS)
+			need_reconfig = false;
+		if ((input_vcd_frm->data_len <= seq_hdr_info.dec_frm_size ||
+			 (input_vcd_frm->flags & VCD_FRAME_FLAG_CODECCONFIG)) &&
+			(!need_reconfig ||
+			 !(input_vcd_frm->flags & VCD_FRAME_FLAG_EOS))) {
+			input_vcd_frm->flags |=
+				VCD_FRAME_FLAG_CODECCONFIG;
+			seq_hdr_only_frame = true;
+			input_vcd_frm->data_len = 0;
+			ddl->input_frame.frm_trans_end = !need_reconfig;
+			ddl_context->ddl_callback(
+				VCD_EVT_RESP_INPUT_DONE,
+				VCD_S_SUCCESS, &ddl->input_frame,
+				sizeof(struct ddl_frame_data_tag),
+				(u32 *) ddl,
+				ddl->ddl_context->client_data);
+		} else if (decoder->codec.codec != VCD_CODEC_H263) {
+			input_vcd_frm->offset += seq_hdr_info.dec_frm_size;
+			input_vcd_frm->data_len -= seq_hdr_info.dec_frm_size;
+		}
+		if (need_reconfig) {
+			decoder->client_frame_size = decoder->frame_size;
+			decoder->client_output_buf_req =
+				decoder->actual_output_buf_req;
+			decoder->client_input_buf_req =
+				decoder->actual_input_buf_req;
+			if (!seq_hdr_only_frame) {
+				reconfig_payload = &ddl->input_frame;
+				reconfig_payload_size =
+					sizeof(struct ddl_frame_data_tag);
+			}
+			ddl_context->ddl_callback(VCD_EVT_IND_OUTPUT_RECONFIG,
+					VCD_S_SUCCESS, reconfig_payload,
+					reconfig_payload_size,
+					(u32 *) ddl,
+					ddl_context->client_data);
+		}
+		if (!need_reconfig && !seq_hdr_only_frame) {
+			if (ddl_decode_set_buffers(ddl) == VCD_S_SUCCESS)
+				process_further = false;
+			else
+				ddl_client_fatal_cb(ddl_context);
+		} else
+			DDL_IDLE(ddl_context);
+	}
+	return process_further;
+}
+
+static u32 ddl_dpb_buffers_set_done_callback(struct ddl_context
+						  *ddl_context)
+{
+	struct ddl_client_context *ddl = ddl_context->current_ddl;
+
+	ddl_move_command_state(ddl_context, DDL_CMD_INVALID);
+	if (!ddl ||
+		!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_DPBDONE)
+		) {
+		VIDC_LOG_STRING("STATE-CRITICAL-DPBDONE");
+		ddl_client_fatal_cb(ddl_context);
+		return true;
+	}
+	if (vidc_msg_timing) {
+		ddl_calc_core_proc_time(__func__, DEC_OP_TIME);
+		ddl_reset_core_time_variables(DEC_OP_TIME);
+	}
+	VIDC_LOG_STRING("INTR_DPBDONE");
+	ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_FRAME);
+	ddl->codec_data.decoder.dec_disp_info.img_size_x = 0;
+	ddl->codec_data.decoder.dec_disp_info.img_size_y = 0;
+	ddl_decode_frame_run(ddl);
+	return false;
+}
+
+static void ddl_encoder_frame_run_callback(struct ddl_context
+					   *ddl_context)
+{
+	struct ddl_client_context *ddl = ddl_context->current_ddl;
+	struct ddl_encoder_data *encoder = &(ddl->codec_data.encoder);
+	u32 eos_present = false;
+
+	if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME_DONE)
+		) {
+		VIDC_LOG_STRING("STATE-CRITICAL-ENCFRMRUN");
+		ddl_client_fatal_cb(ddl_context);
+		return;
+	}
+
+	VIDC_LOG_STRING("ENC_FRM_RUN_DONE");
+
+	ddl_move_command_state(ddl_context, DDL_CMD_INVALID);
+	vidc_720p_enc_frame_info(&encoder->enc_frame_info);
+
+	ddl->output_frame.vcd_frm.ip_frm_tag =
+		ddl->input_frame.vcd_frm.ip_frm_tag;
+	ddl->output_frame.vcd_frm.data_len =
+		encoder->enc_frame_info.enc_size;
+	ddl->output_frame.vcd_frm.flags |= VCD_FRAME_FLAG_ENDOFFRAME;
+	ddl_get_frame
+		(&(ddl->output_frame.vcd_frm),
+		 encoder->enc_frame_info.frame);
+	ddl_process_encoder_metadata(ddl);
+
+	ddl_encode_dynamic_property(ddl, false);
+
+	ddl->input_frame.frm_trans_end = false;
+	ddl_context->ddl_callback(VCD_EVT_RESP_INPUT_DONE, VCD_S_SUCCESS,
+		&(ddl->input_frame), sizeof(struct ddl_frame_data_tag),
+		(u32 *) ddl, ddl_context->client_data);
+
+	if (vidc_msg_timing)
+		ddl_calc_core_proc_time(__func__, ENC_OP_TIME);
+
+	/* check the presence of EOS */
+   eos_present =
+	((VCD_FRAME_FLAG_EOS & ddl->input_frame.vcd_frm.flags));
+
+	ddl->output_frame.frm_trans_end = !eos_present;
+	ddl_context->ddl_callback(VCD_EVT_RESP_OUTPUT_DONE, VCD_S_SUCCESS,
+		&(ddl->output_frame),	sizeof(struct ddl_frame_data_tag),
+		(u32 *) ddl, ddl_context->client_data);
+
+	if (eos_present) {
+		VIDC_LOG_STRING("ENC-EOS_DONE");
+		ddl_context->ddl_callback(VCD_EVT_RESP_EOS_DONE,
+				VCD_S_SUCCESS, NULL, 0,	(u32 *)ddl,
+				ddl_context->client_data);
+	}
+
+	ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_FRAME);
+	DDL_IDLE(ddl_context);
+}
+
+static u32 ddl_decoder_frame_run_callback(struct ddl_context
+					   *ddl_context)
+{
+	struct ddl_client_context *ddl = ddl_context->current_ddl;
+	struct vidc_720p_dec_disp_info *dec_disp_info =
+	    &(ddl->codec_data.decoder.dec_disp_info);
+	u32 callback_end = false;
+	u32 status = true, eos_present = false;;
+
+	if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME_DONE)) {
+		VIDC_LOG_STRING("STATE-CRITICAL-DECFRMRUN");
+		ddl_client_fatal_cb(ddl_context);
+		return true;
+	}
+
+	VIDC_LOG_STRING("DEC_FRM_RUN_DONE");
+
+	ddl_move_command_state(ddl_context, DDL_CMD_INVALID);
+
+	vidc_720p_decode_display_info(dec_disp_info);
+
+	ddl_decode_dynamic_property(ddl, false);
+
+	if (dec_disp_info->resl_change) {
+		VIDC_LOG_STRING
+			("DEC_FRM_RUN_DONE: RECONFIG");
+		ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_EOS_DONE);
+		ddl_move_command_state(ddl_context, DDL_CMD_EOS);
+		vidc_720p_submit_command(ddl->channel_id,
+			VIDC_720P_CMD_FRAMERUN_REALLOCATE);
+		return false;
+	}
+
+	if ((VCD_FRAME_FLAG_EOS & ddl->input_frame.vcd_frm.flags)) {
+		callback_end = false;
+		eos_present = true;
+	}
+
+
+	if (dec_disp_info->disp_status == VIDC_720P_DECODE_ONLY ||
+		dec_disp_info->disp_status
+			== VIDC_720P_DECODE_AND_DISPLAY) {
+		if (!eos_present)
+			callback_end = (dec_disp_info->disp_status
+					== VIDC_720P_DECODE_ONLY);
+
+	  ddl_decoder_input_done_callback(ddl, callback_end);
+	}
+
+	if (dec_disp_info->disp_status == VIDC_720P_DECODE_AND_DISPLAY
+		|| dec_disp_info->disp_status == VIDC_720P_DISPLAY_ONLY) {
+		if (!eos_present)
+			callback_end =
+			(dec_disp_info->disp_status
+				== VIDC_720P_DECODE_AND_DISPLAY);
+
+		if (ddl_decoder_output_done_callback(ddl, callback_end)
+			!= VCD_S_SUCCESS)
+			return true;
+	}
+
+	if (dec_disp_info->disp_status ==  VIDC_720P_DISPLAY_ONLY ||
+		dec_disp_info->disp_status ==  VIDC_720P_EMPTY_BUFFER) {
+		/* send the same input once again for decoding */
+		ddl_decode_frame_run(ddl);
+		/* client need to ignore the interrupt */
+		status = false;
+	} else if (eos_present) {
+		/* send EOS command to HW */
+		ddl_decode_eos_run(ddl);
+		/* client need to ignore the interrupt */
+		status = false;
+	} else {
+		ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_FRAME);
+		/* move to Idle */
+		DDL_IDLE(ddl_context);
+	}
+	return status;
+}
+
+static u32 ddl_eos_frame_done_callback(struct ddl_context *ddl_context)
+{
+	struct ddl_client_context *ddl = ddl_context->current_ddl;
+	struct ddl_decoder_data *decoder = &(ddl->codec_data.decoder);
+	struct vidc_720p_dec_disp_info *dec_disp_info =
+		&(decoder->dec_disp_info);
+
+	if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_EOS_DONE)) {
+		VIDC_LOGERR_STRING("STATE-CRITICAL-EOSFRMRUN");
+		ddl_client_fatal_cb(ddl_context);
+		return true;
+	}
+	VIDC_LOG_STRING("EOS_FRM_RUN_DONE");
+
+	ddl_move_command_state(ddl_context, DDL_CMD_INVALID);
+
+	vidc_720p_decode_display_info(dec_disp_info);
+
+	ddl_decode_dynamic_property(ddl, false);
+
+	if (dec_disp_info->disp_status == VIDC_720P_DISPLAY_ONLY) {
+		if (ddl_decoder_output_done_callback(ddl, false)
+			!= VCD_S_SUCCESS)
+			return true;
+	} else
+		VIDC_LOG_STRING("STATE-CRITICAL-WRONG-DISP-STATUS");
+
+	ddl_decoder_dpb_transact(decoder, NULL, DDL_DPB_OP_SET_MASK);
+	ddl_move_command_state(ddl_context, DDL_CMD_EOS);
+	vidc_720p_submit_command(ddl->channel_id,
+		VIDC_720P_CMD_FRAMERUN);
+	return false;
+}
+
+static void ddl_channel_end_callback(struct ddl_context *ddl_context)
+{
+	struct ddl_client_context *ddl;
+
+	ddl_move_command_state(ddl_context, DDL_CMD_INVALID);
+	VIDC_LOG_STRING("CH_END_DONE");
+
+	ddl = ddl_context->current_ddl;
+	if (!ddl ||
+		!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_CHEND)
+		) {
+		VIDC_LOG_STRING("STATE-CRITICAL-CHEND");
+		DDL_IDLE(ddl_context);
+		return;
+	}
+
+	ddl_release_client_internal_buffers(ddl);
+	ddl_context->ddl_callback(VCD_EVT_RESP_STOP, VCD_S_SUCCESS,
+		NULL, 0, (u32 *) ddl,	ddl_context->client_data);
+	ddl_move_client_state(ddl, DDL_CLIENT_OPEN);
+	DDL_IDLE(ddl_context);
+}
+
+static u32 ddl_operation_done_callback(struct ddl_context *ddl_context)
+{
+	u32 return_status = true;
+
+	switch (ddl_context->cmd_state) {
+	case DDL_CMD_DECODE_FRAME:
+		{
+			return_status = ddl_decoder_frame_run_callback(
+				ddl_context);
+			break;
+		}
+	case DDL_CMD_ENCODE_FRAME:
+		{
+			ddl_encoder_frame_run_callback(ddl_context);
+			break;
+		}
+	case DDL_CMD_CHANNEL_SET:
+		{
+			return_status = ddl_channel_set_callback(
+				ddl_context);
+			break;
+		}
+	case DDL_CMD_INIT_CODEC:
+		{
+			ddl_init_codec_done_callback(ddl_context);
+			break;
+		}
+	case DDL_CMD_HEADER_PARSE:
+		{
+			return_status = ddl_header_done_callback(
+				ddl_context);
+			break;
+		}
+	case DDL_CMD_DECODE_SET_DPB:
+		{
+			return_status = ddl_dpb_buffers_set_done_callback(
+				ddl_context);
+			break;
+		}
+	case DDL_CMD_CHANNEL_END:
+		{
+			ddl_channel_end_callback(ddl_context);
+			break;
+		}
+	case DDL_CMD_EOS:
+		{
+			return_status = ddl_eos_frame_done_callback(
+				ddl_context);
+			break;
+		}
+	case DDL_CMD_CPU_RESET:
+		{
+			ddl_cpu_started_callback(ddl_context);
+			break;
+		}
+	default:
+		{
+			VIDC_LOG_STRING("UNKWN_OPDONE");
+			return_status = false;
+			break;
+		}
+	}
+	return return_status;
+}
+
+static u32 ddl_process_intr_status(struct ddl_context *ddl_context,
+			u32 int_status)
+{
+	u32 status = true;
+	switch (int_status) {
+	case VIDC_720P_INTR_FRAME_DONE:
+		 {
+			status = ddl_operation_done_callback(ddl_context);
+			break;
+		 }
+	case VIDC_720P_INTR_DMA_DONE:
+		 {
+			ddl_dma_done_callback(ddl_context);
+			status = false;
+			break;
+		 }
+	case VIDC_720P_INTR_FW_DONE:
+		 {
+			status = ddl_eos_done_callback(ddl_context);
+			break;
+		 }
+	case VIDC_720P_INTR_BUFFER_FULL:
+		 {
+			VIDC_LOGERR_STRING("BUF_FULL_INTR");
+			ddl_hw_fatal_cb(ddl_context);
+			break;
+		 }
+	default:
+		 {
+			VIDC_LOGERR_STRING("UNKWN_INTR");
+			break;
+		 }
+	}
+	return status;
+}
+
+void ddl_read_and_clear_interrupt(void)
+{
+	struct ddl_context *ddl_context;
+
+	ddl_context = ddl_get_context();
+	if (!ddl_context->core_virtual_base_addr) {
+		VIDC_LOGERR_STRING("SPURIOUS_INTERRUPT");
+		return;
+	}
+	vidc_720p_get_interrupt_status(&ddl_context->intr_status,
+		&ddl_context->cmd_err_status,
+		&ddl_context->disp_pic_err_status,
+		&ddl_context->op_failed
+	);
+
+	vidc_720p_interrupt_done_clear();
+
+}
+
+u32 ddl_process_core_response(void)
+{
+	struct ddl_context *ddl_context;
+	u32 return_status = true;
+
+	ddl_context = ddl_get_context();
+	if (!ddl_context->core_virtual_base_addr) {
+		VIDC_LOGERR_STRING("UNKWN_INTR");
+		return false;
+	}
+
+	if (!ddl_handle_core_errors(ddl_context)) {
+		return_status = ddl_process_intr_status(ddl_context,
+			ddl_context->intr_status);
+	}
+
+	if (ddl_context->interrupt_clr)
+		(*ddl_context->interrupt_clr)();
+
+	return return_status;
+}
+
+static void ddl_decoder_input_done_callback(
+	struct	ddl_client_context *ddl, u32 frame_transact_end)
+{
+	struct vidc_720p_dec_disp_info *dec_disp_info =
+		&(ddl->codec_data.decoder.dec_disp_info);
+	struct vcd_frame_data *input_vcd_frm =
+		&(ddl->input_frame.vcd_frm);
+	ddl_get_frame(input_vcd_frm, dec_disp_info->
+		input_frame);
+
+	input_vcd_frm->interlaced = (dec_disp_info->
+		input_is_interlace);
+
+	input_vcd_frm->offset += dec_disp_info->input_bytes_consumed;
+	input_vcd_frm->data_len -= dec_disp_info->input_bytes_consumed;
+
+	ddl->input_frame.frm_trans_end = frame_transact_end;
+	if (vidc_msg_timing)
+		ddl_calc_core_proc_time(__func__, DEC_IP_TIME);
+	ddl->ddl_context->ddl_callback(
+		VCD_EVT_RESP_INPUT_DONE,
+		VCD_S_SUCCESS,
+		&ddl->input_frame,
+		sizeof(struct ddl_frame_data_tag),
+		(void *)ddl,
+		ddl->ddl_context->client_data);
+}
+
+static u32 ddl_decoder_output_done_callback(
+	struct ddl_client_context *ddl,
+	u32 frame_transact_end)
+{
+	struct ddl_decoder_data *decoder = &(ddl->codec_data.decoder);
+	struct vidc_720p_dec_disp_info *dec_disp_info =
+		&(decoder->dec_disp_info);
+	struct ddl_frame_data_tag *output_frame =
+		&ddl->output_frame;
+	struct vcd_frame_data *output_vcd_frm =
+		&(output_frame->vcd_frm);
+	u32 vcd_status;
+	u32 free_luma_dpb = 0;
+
+	output_vcd_frm->physical = (u8 *)dec_disp_info->y_addr;
+
+	if (decoder->codec.codec == VCD_CODEC_MPEG4 ||
+		decoder->codec.codec == VCD_CODEC_VC1 ||
+		decoder->codec.codec == VCD_CODEC_VC1_RCV ||
+		(decoder->codec.codec >= VCD_CODEC_DIVX_3 &&
+		 decoder->codec.codec <= VCD_CODEC_XVID)){
+		vidc_720p_decode_skip_frm_details(&free_luma_dpb);
+		if (free_luma_dpb)
+			output_vcd_frm->physical = (u8 *) free_luma_dpb;
+	}
+
+
+	vcd_status = ddl_decoder_dpb_transact(
+			decoder,
+			output_frame,
+			DDL_DPB_OP_MARK_BUSY);
+
+	if (vcd_status != VCD_S_SUCCESS) {
+		VIDC_LOGERR_STRING("CorruptedOutputBufferAddress");
+		ddl_hw_fatal_cb(ddl->ddl_context);
+		return vcd_status;
+	}
+
+	output_vcd_frm->ip_frm_tag =  dec_disp_info->tag_top;
+	if (dec_disp_info->crop_exists == 0x1) {
+		output_vcd_frm->dec_op_prop.disp_frm.left =
+			dec_disp_info->crop_left_offset;
+		output_vcd_frm->dec_op_prop.disp_frm.top =
+			dec_disp_info->crop_top_offset;
+		output_vcd_frm->dec_op_prop.disp_frm.right =
+			dec_disp_info->img_size_x -
+			dec_disp_info->crop_right_offset;
+		output_vcd_frm->dec_op_prop.disp_frm.bottom =
+			dec_disp_info->img_size_y -
+			dec_disp_info->crop_bottom_offset;
+	} else {
+		output_vcd_frm->dec_op_prop.disp_frm.left = 0;
+		output_vcd_frm->dec_op_prop.disp_frm.top = 0;
+		output_vcd_frm->dec_op_prop.disp_frm.right =
+			dec_disp_info->img_size_x;
+		output_vcd_frm->dec_op_prop.disp_frm.bottom =
+			dec_disp_info->img_size_y;
+	}
+	if (!dec_disp_info->disp_is_interlace) {
+		output_vcd_frm->interlaced = false;
+		output_vcd_frm->intrlcd_ip_frm_tag = VCD_FRAMETAG_INVALID;
+	} else {
+		output_vcd_frm->interlaced = true;
+		output_vcd_frm->intrlcd_ip_frm_tag =
+			dec_disp_info->tag_bottom;
+	}
+
+	output_vcd_frm->offset = 0;
+	output_vcd_frm->data_len = decoder->y_cb_cr_size;
+	if (free_luma_dpb) {
+		output_vcd_frm->data_len = 0;
+		output_vcd_frm->flags |= VCD_FRAME_FLAG_DECODEONLY;
+	}
+	output_vcd_frm->flags |= VCD_FRAME_FLAG_ENDOFFRAME;
+	ddl_process_decoder_metadata(ddl);
+	output_frame->frm_trans_end = frame_transact_end;
+
+	if (vidc_msg_timing)
+		ddl_calc_core_proc_time(__func__, DEC_OP_TIME);
+
+	ddl->ddl_context->ddl_callback(
+		VCD_EVT_RESP_OUTPUT_DONE,
+		vcd_status,
+		output_frame,
+		sizeof(struct ddl_frame_data_tag),
+		(void *)ddl,
+		ddl->ddl_context->client_data);
+	return vcd_status;
+}
+
+static u32 ddl_get_frame
+	(struct vcd_frame_data *frame, u32 frametype) {
+	enum vidc_720p_frame vidc_frame =
+		(enum vidc_720p_frame)frametype;
+	u32 status = true;
+
+	switch (vidc_frame) {
+	case VIDC_720P_IFRAME:
+		{
+			frame->flags |= VCD_FRAME_FLAG_SYNCFRAME;
+			frame->frame = VCD_FRAME_I;
+			break;
+		}
+	case VIDC_720P_PFRAME:
+		{
+			frame->frame = VCD_FRAME_P;
+			break;
+		}
+	case VIDC_720P_BFRAME:
+		{
+			frame->frame = VCD_FRAME_B;
+			break;
+		}
+	case VIDC_720P_NOTCODED:
+		{
+			frame->frame = VCD_FRAME_NOTCODED;
+			frame->data_len = 0;
+			break;
+		}
+	case VIDC_720P_IDRFRAME:
+		{
+			frame->flags |= VCD_FRAME_FLAG_SYNCFRAME;
+			frame->frame = VCD_FRAME_IDR;
+			break;
+		}
+	default:
+		{
+			VIDC_LOG_STRING("CRITICAL-FRAMETYPE");
+			status = false;
+			break;
+		}
+	}
+	return status;
+}
+
+static void ddl_getmpeg4_declevel(enum vcd_codec_level *codec_level,
+	u32 level)
+{
+	switch (level) {
+	case VIDC_720P_MPEG4_LEVEL0:
+		{
+			*codec_level = VCD_LEVEL_MPEG4_0;
+			break;
+		}
+	case VIDC_720P_MPEG4_LEVEL0b:
+		{
+			*codec_level = VCD_LEVEL_MPEG4_0b;
+			break;
+		}
+	case VIDC_720P_MPEG4_LEVEL1:
+		{
+			*codec_level = VCD_LEVEL_MPEG4_1;
+			break;
+		}
+	case VIDC_720P_MPEG4_LEVEL2:
+		{
+			*codec_level = VCD_LEVEL_MPEG4_2;
+			break;
+		}
+	case VIDC_720P_MPEG4_LEVEL3:
+		{
+			*codec_level = VCD_LEVEL_MPEG4_3;
+			break;
+		}
+	case VIDC_720P_MPEG4_LEVEL3b:
+		{
+			*codec_level = VCD_LEVEL_MPEG4_3b;
+			break;
+		}
+	case VIDC_720P_MPEG4_LEVEL4a:
+		{
+			*codec_level = VCD_LEVEL_MPEG4_4a;
+			break;
+		}
+	case VIDC_720P_MPEG4_LEVEL5:
+		{
+			*codec_level = VCD_LEVEL_MPEG4_5;
+			break;
+		}
+	case VIDC_720P_MPEG4_LEVEL6:
+		{
+			*codec_level = VCD_LEVEL_MPEG4_6;
+			break;
+		}
+	}
+}
+
+static void ddl_geth264_declevel(enum vcd_codec_level *codec_level,
+	u32 level)
+{
+	switch (level) {
+	case VIDC_720P_H264_LEVEL1:
+		{
+			*codec_level = VCD_LEVEL_H264_1;
+			break;
+		}
+	case VIDC_720P_H264_LEVEL1b:
+		{
+			*codec_level = VCD_LEVEL_H264_1b;
+			break;
+		}
+	case VIDC_720P_H264_LEVEL1p1:
+		{
+			*codec_level = VCD_LEVEL_H264_1p1;
+			break;
+		}
+	case VIDC_720P_H264_LEVEL1p2:
+		{
+			*codec_level = VCD_LEVEL_H264_1p2;
+			break;
+		}
+	case VIDC_720P_H264_LEVEL1p3:
+		{
+			*codec_level = VCD_LEVEL_H264_1p3;
+			break;
+		}
+	case VIDC_720P_H264_LEVEL2:
+		{
+			*codec_level = VCD_LEVEL_H264_2;
+			break;
+		}
+	case VIDC_720P_H264_LEVEL2p1:
+		{
+			*codec_level = VCD_LEVEL_H264_2p1;
+			break;
+		}
+	case VIDC_720P_H264_LEVEL2p2:
+		{
+			*codec_level = VCD_LEVEL_H264_2p2;
+			break;
+		}
+	case VIDC_720P_H264_LEVEL3:
+		{
+			*codec_level = VCD_LEVEL_H264_3;
+			break;
+		}
+	case VIDC_720P_H264_LEVEL3p1:
+		{
+			*codec_level = VCD_LEVEL_H264_3p1;
+			break;
+		}
+	case VIDC_720P_H264_LEVEL3p2:
+	{
+		*codec_level = VCD_LEVEL_H264_3p2;
+		break;
+	}
+
+	}
+}
+
+static void ddl_get_vc1_dec_level(
+	enum vcd_codec_level *codec_level, u32 level,
+	enum vcd_codec_profile vc1_profile)
+{
+	if (vc1_profile == VCD_PROFILE_VC1_ADVANCE)	{
+		switch (level) {
+		case VIDC_720P_VC1_LEVEL0:
+			{
+				*codec_level = VCD_LEVEL_VC1_A_0;
+				break;
+			}
+		case VIDC_720P_VC1_LEVEL1:
+			{
+				*codec_level = VCD_LEVEL_VC1_A_1;
+				break;
+			}
+		case VIDC_720P_VC1_LEVEL2:
+			{
+				*codec_level = VCD_LEVEL_VC1_A_2;
+				break;
+			}
+		case VIDC_720P_VC1_LEVEL3:
+			{
+				*codec_level = VCD_LEVEL_VC1_A_3;
+				break;
+			}
+		case VIDC_720P_VC1_LEVEL4:
+			{
+				*codec_level = VCD_LEVEL_VC1_A_4;
+				break;
+			}
+		}
+		return;
+	} else if (vc1_profile == VCD_PROFILE_VC1_MAIN) {
+		switch (level) {
+		case VIDC_720P_VC1_LEVEL_LOW:
+			{
+				*codec_level = VCD_LEVEL_VC1_M_LOW;
+				break;
+			}
+		case VIDC_720P_VC1_LEVEL_MED:
+			{
+				*codec_level = VCD_LEVEL_VC1_M_MEDIUM;
+				break;
+			}
+		case VIDC_720P_VC1_LEVEL_HIGH:
+			{
+				*codec_level = VCD_LEVEL_VC1_M_HIGH;
+				break;
+			}
+		}
+	} else if (vc1_profile == VCD_PROFILE_VC1_SIMPLE) {
+		switch (level) {
+		case VIDC_720P_VC1_LEVEL_LOW:
+			{
+				*codec_level = VCD_LEVEL_VC1_S_LOW;
+				break;
+			}
+		case VIDC_720P_VC1_LEVEL_MED:
+			{
+				*codec_level = VCD_LEVEL_VC1_S_MEDIUM;
+				break;
+			}
+		}
+	}
+}
+
+static void ddl_get_mpeg2_dec_level(enum vcd_codec_level *codec_level,
+								 u32 level)
+{
+	switch (level) {
+	case VIDCL_720P_MPEG2_LEVEL_LOW:
+		{
+			*codec_level = VCD_LEVEL_MPEG2_LOW;
+			break;
+		}
+	case VIDCL_720P_MPEG2_LEVEL_MAIN:
+		{
+			*codec_level = VCD_LEVEL_MPEG2_MAIN;
+			break;
+		}
+	case VIDCL_720P_MPEG2_LEVEL_HIGH14:
+		{
+			*codec_level = VCD_LEVEL_MPEG2_HIGH_14;
+			break;
+		}
+	}
+}
+
+static void ddl_getdec_profilelevel(struct ddl_decoder_data *decoder,
+		u32 profile, u32 level)
+{
+	enum vcd_codec_profile codec_profile = VCD_PROFILE_UNKNOWN;
+	enum vcd_codec_level codec_level = VCD_LEVEL_UNKNOWN;
+
+	switch (decoder->codec.codec) {
+	case VCD_CODEC_MPEG4:
+		{
+			if (profile == VIDC_720P_PROFILE_MPEG4_SP)
+				codec_profile = VCD_PROFILE_MPEG4_SP;
+			else if (profile == VIDC_720P_PROFILE_MPEG4_ASP)
+				codec_profile = VCD_PROFILE_MPEG4_ASP;
+
+			ddl_getmpeg4_declevel(&codec_level, level);
+			break;
+		}
+	case VCD_CODEC_H264:
+		{
+			if (profile == VIDC_720P_PROFILE_H264_BASELINE)
+				codec_profile = VCD_PROFILE_H264_BASELINE;
+			else if (profile == VIDC_720P_PROFILE_H264_MAIN)
+				codec_profile = VCD_PROFILE_H264_MAIN;
+			else if (profile == VIDC_720P_PROFILE_H264_HIGH)
+				codec_profile = VCD_PROFILE_H264_HIGH;
+			ddl_geth264_declevel(&codec_level, level);
+			break;
+		}
+	default:
+	case VCD_CODEC_H263:
+		{
+			break;
+		}
+	case VCD_CODEC_VC1:
+	case VCD_CODEC_VC1_RCV:
+		{
+			if (profile == VIDC_720P_PROFILE_VC1_SP)
+				codec_profile = VCD_PROFILE_VC1_SIMPLE;
+			else if (profile == VIDC_720P_PROFILE_VC1_MAIN)
+				codec_profile = VCD_PROFILE_VC1_MAIN;
+			else if (profile == VIDC_720P_PROFILE_VC1_ADV)
+				codec_profile = VCD_PROFILE_VC1_ADVANCE;
+			ddl_get_vc1_dec_level(&codec_level, level, profile);
+			break;
+		}
+	case VCD_CODEC_MPEG2:
+		{
+			if (profile == VIDC_720P_PROFILE_MPEG2_MAIN)
+				codec_profile = VCD_PROFILE_MPEG2_MAIN;
+			else if (profile == VIDC_720P_PROFILE_MPEG2_SP)
+				codec_profile = VCD_PROFILE_MPEG2_SIMPLE;
+			ddl_get_mpeg2_dec_level(&codec_level, level);
+			break;
+		}
+	}
+
+	decoder->profile.profile = codec_profile;
+	decoder->level.level = codec_level;
+}
diff --git a/drivers/video/msm/vidc/720p/ddl/vcd_ddl_metadata.c b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_metadata.c
new file mode 100644
index 0000000..2a74da8
--- /dev/null
+++ b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_metadata.c
@@ -0,0 +1,580 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <media/msm/vidc_type.h>
+#include "vcd_ddl_utils.h"
+#include "vcd_ddl_metadata.h"
+
+static u32 *ddl_metadata_hdr_entry(struct ddl_client_context *ddl,
+				   u32 meta_data)
+{
+	u32 skip_words = 0;
+	u32 *buffer;
+
+	if (ddl->decoding) {
+		buffer = (u32 *)
+		    ddl->codec_data.decoder.meta_data_input.
+		    align_virtual_addr;
+		skip_words = 32 + 1;
+		buffer += skip_words;
+
+		switch (meta_data) {
+		default:
+		case VCD_METADATA_DATANONE:
+			{
+				skip_words = 0;
+				break;
+			}
+		case VCD_METADATA_QPARRAY:
+			{
+				skip_words = 3;
+				break;
+			}
+		case VCD_METADATA_CONCEALMB:
+			{
+				skip_words = 6;
+				break;
+			}
+		case VCD_METADATA_VC1:
+			{
+				skip_words = 9;
+				break;
+			}
+		case VCD_METADATA_SEI:
+			{
+				skip_words = 12;
+				break;
+			}
+		case VCD_METADATA_VUI:
+			{
+				skip_words = 15;
+				break;
+			}
+		case VCD_METADATA_PASSTHROUGH:
+			{
+				skip_words = 18;
+				break;
+			}
+		case VCD_METADATA_QCOMFILLER:
+			{
+				skip_words = 21;
+				break;
+			}
+		}
+	} else {
+		buffer = (u32 *)
+		    ddl->codec_data.encoder.meta_data_input.
+		    align_virtual_addr;
+		skip_words = 2;
+		buffer += skip_words;
+
+		switch (meta_data) {
+		default:
+		case VCD_METADATA_DATANONE:
+			{
+				skip_words = 0;
+				break;
+			}
+		case VCD_METADATA_ENC_SLICE:
+			{
+				skip_words = 3;
+				break;
+			}
+		case VCD_METADATA_QCOMFILLER:
+			{
+				skip_words = 6;
+				break;
+			}
+		}
+
+	}
+
+	buffer += skip_words;
+	return buffer;
+}
+
+void ddl_set_default_meta_data_hdr(struct ddl_client_context *ddl)
+{
+	struct ddl_buf_addr *main_buffer =
+	    &ddl->ddl_context->metadata_shared_input;
+	struct ddl_buf_addr *client_buffer;
+	u32 *hdr_entry;
+
+	if (ddl->decoding)
+		client_buffer = &(ddl->codec_data.decoder.meta_data_input);
+	else
+		client_buffer = &(ddl->codec_data.encoder.meta_data_input);
+
+	DDL_METADATA_CLIENT_INPUTBUF(main_buffer, client_buffer,
+				     ddl->channel_id);
+
+	hdr_entry = ddl_metadata_hdr_entry(ddl, VCD_METADATA_QCOMFILLER);
+	hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 0x00000101;
+	hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 1;
+	hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_QCOMFILLER;
+
+	hdr_entry = ddl_metadata_hdr_entry(ddl, VCD_METADATA_DATANONE);
+	hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 0x00000101;
+	hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 1;
+	hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_DATANONE;
+
+	if (ddl->decoding) {
+		hdr_entry =
+		    ddl_metadata_hdr_entry(ddl, VCD_METADATA_QPARRAY);
+		hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 0x00000101;
+		hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 1;
+		hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_QPARRAY;
+
+		hdr_entry =
+		    ddl_metadata_hdr_entry(ddl, VCD_METADATA_CONCEALMB);
+		hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 0x00000101;
+		hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 1;
+		hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_CONCEALMB;
+
+		hdr_entry = ddl_metadata_hdr_entry(ddl, VCD_METADATA_SEI);
+		hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 0x00000101;
+		hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 1;
+		hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_SEI;
+
+		hdr_entry = ddl_metadata_hdr_entry(ddl, VCD_METADATA_VUI);
+		hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 0x00000101;
+		hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 1;
+		hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_VUI;
+
+		hdr_entry = ddl_metadata_hdr_entry(ddl, VCD_METADATA_VC1);
+		hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 0x00000101;
+		hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 1;
+		hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_VC1;
+
+		hdr_entry =
+		    ddl_metadata_hdr_entry(ddl, VCD_METADATA_PASSTHROUGH);
+		hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 0x00000101;
+		hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 1;
+		hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] =
+		    VCD_METADATA_PASSTHROUGH;
+
+	} else {
+		hdr_entry =
+		    ddl_metadata_hdr_entry(ddl, VCD_METADATA_ENC_SLICE);
+		hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 0x00000101;
+		hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 1;
+		hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] =
+		    VCD_METADATA_ENC_SLICE;
+	}
+}
+
+static u32 ddl_supported_metadata_flag(struct ddl_client_context *ddl)
+{
+	u32 flag = 0;
+
+	if (ddl->decoding) {
+		enum vcd_codec codec =
+		    ddl->codec_data.decoder.codec.codec;
+
+		flag |= (VCD_METADATA_CONCEALMB |
+			   VCD_METADATA_PASSTHROUGH | VCD_METADATA_QPARRAY);
+		if (codec == VCD_CODEC_H264) {
+			flag |= (VCD_METADATA_SEI | VCD_METADATA_VUI);
+		} else if (codec == VCD_CODEC_VC1 ||
+			   codec == VCD_CODEC_VC1_RCV) {
+			flag |= VCD_METADATA_VC1;
+		}
+	} else {
+		flag |= VCD_METADATA_ENC_SLICE;
+	}
+
+	return flag;
+}
+
+void ddl_set_default_metadata_flag(struct ddl_client_context *ddl)
+{
+	if (ddl->decoding)
+		ddl->codec_data.decoder.meta_data_enable_flag = 0;
+	else
+		ddl->codec_data.encoder.meta_data_enable_flag = 0;
+}
+
+void ddl_set_default_decoder_metadata_buffer_size(
+	struct ddl_decoder_data *decoder,
+	struct vcd_property_frame_size *frame_size,
+	struct vcd_buffer_requirement *output_buf_req)
+{
+	u32 flag = decoder->meta_data_enable_flag;
+	u32 suffix = 0;
+	size_t sz = 0;
+
+	if (!flag) {
+		decoder->suffix = 0;
+		return;
+	}
+
+	if (flag & VCD_METADATA_QPARRAY) {
+		u32 num_of_mb =
+		    ((frame_size->width * frame_size->height) >> 8);
+		sz = DDL_METADATA_HDR_SIZE;
+		sz += num_of_mb;
+		DDL_METADATA_ALIGNSIZE(sz);
+		suffix += sz;
+	}
+	if (flag & VCD_METADATA_CONCEALMB) {
+		u32 num_of_mb =
+		    ((frame_size->width * frame_size->height) >> 8);
+		sz = DDL_METADATA_HDR_SIZE + (num_of_mb >> 3);
+		DDL_METADATA_ALIGNSIZE(sz);
+		suffix += sz;
+	}
+	if (flag & VCD_METADATA_VC1) {
+		sz = DDL_METADATA_HDR_SIZE;
+		sz += DDL_METADATA_VC1_PAYLOAD_SIZE;
+		DDL_METADATA_ALIGNSIZE(sz);
+		suffix += sz;
+	}
+	if (flag & VCD_METADATA_SEI) {
+		sz = DDL_METADATA_HDR_SIZE;
+		sz += DDL_METADATA_SEI_PAYLOAD_SIZE;
+		DDL_METADATA_ALIGNSIZE(sz);
+		suffix += (sz * DDL_METADATA_SEI_MAX);
+	}
+	if (flag & VCD_METADATA_VUI) {
+		sz = DDL_METADATA_HDR_SIZE;
+		sz += DDL_METADATA_VUI_PAYLOAD_SIZE;
+		DDL_METADATA_ALIGNSIZE(sz);
+		suffix += (sz);
+	}
+	if (flag & VCD_METADATA_PASSTHROUGH) {
+		sz = DDL_METADATA_HDR_SIZE;
+		sz += DDL_METADATA_PASSTHROUGH_PAYLOAD_SIZE;
+		DDL_METADATA_ALIGNSIZE(sz);
+		suffix += (sz);
+	}
+	sz = DDL_METADATA_EXTRADATANONE_SIZE;
+	DDL_METADATA_ALIGNSIZE(sz);
+	suffix += (sz);
+
+	suffix += DDL_METADATA_EXTRAPAD_SIZE;
+	DDL_METADATA_ALIGNSIZE(suffix);
+
+	decoder->suffix = suffix;
+	output_buf_req->sz += suffix;
+	return;
+}
+
+void ddl_set_default_encoder_metadata_buffer_size(struct ddl_encoder_data
+						  *encoder)
+{
+	u32 flag = encoder->meta_data_enable_flag;
+	u32 suffix = 0;
+	size_t sz = 0;
+
+	if (!flag) {
+		encoder->suffix = 0;
+		return;
+	}
+
+	if (flag & VCD_METADATA_ENC_SLICE) {
+		u32 num_of_mb = (encoder->frame_size.width *
+				   encoder->frame_size.height / 16 / 16);
+		sz = DDL_METADATA_HDR_SIZE;
+
+		sz += 4;
+
+		sz += (8 * num_of_mb);
+		DDL_METADATA_ALIGNSIZE(sz);
+		suffix += sz;
+	}
+
+	sz = DDL_METADATA_EXTRADATANONE_SIZE;
+	DDL_METADATA_ALIGNSIZE(sz);
+	suffix += (sz);
+
+	suffix += DDL_METADATA_EXTRAPAD_SIZE;
+	DDL_METADATA_ALIGNSIZE(suffix);
+
+	encoder->suffix = suffix;
+	encoder->output_buf_req.sz += suffix;
+}
+
+u32 ddl_set_metadata_params(struct ddl_client_context *ddl,
+			    struct vcd_property_hdr *property_hdr,
+			    void *property_value)
+{
+	u32 vcd_status = VCD_ERR_ILLEGAL_PARM;
+	if (property_hdr->prop_id == VCD_I_METADATA_ENABLE) {
+		struct vcd_property_meta_data_enable *meta_data_enable =
+		    (struct vcd_property_meta_data_enable *)
+		    property_value;
+		u32 *meta_data_enable_flag;
+		enum vcd_codec codec;
+		if (ddl->decoding) {
+			meta_data_enable_flag =
+			    &(ddl->codec_data.decoder.
+			      meta_data_enable_flag);
+			codec = ddl->codec_data.decoder.codec.codec;
+		} else {
+			meta_data_enable_flag =
+			    &(ddl->codec_data.encoder.
+			      meta_data_enable_flag);
+			codec = ddl->codec_data.encoder.codec.codec;
+		}
+		if (sizeof(struct vcd_property_meta_data_enable) ==
+		    property_hdr->sz &&
+		    DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN) &&
+					codec) {
+			u32 flag = ddl_supported_metadata_flag(ddl);
+			flag &= (meta_data_enable->meta_data_enable_flag);
+			if (flag)
+				flag |= DDL_METADATA_MANDATORY;
+			if (flag != *meta_data_enable_flag) {
+				*meta_data_enable_flag = flag;
+				if (ddl->decoding) {
+					ddl_set_default_decoder_buffer_req
+						(&ddl->codec_data.decoder,
+						 true);
+				} else {
+					ddl_set_default_encoder_buffer_req
+						(&ddl->codec_data.encoder);
+				}
+			}
+			vcd_status = VCD_S_SUCCESS;
+		}
+	} else if (property_hdr->prop_id == VCD_I_METADATA_HEADER) {
+		struct vcd_property_metadata_hdr *hdr =
+		    (struct vcd_property_metadata_hdr *)property_value;
+		if (sizeof(struct vcd_property_metadata_hdr) ==
+		    property_hdr->sz) {
+			u32 flag = ddl_supported_metadata_flag(ddl);
+			flag |= DDL_METADATA_MANDATORY;
+			flag &= hdr->meta_data_id;
+			if (!(flag & (flag - 1))) {
+				u32 *hdr_entry =
+				    ddl_metadata_hdr_entry(ddl, flag);
+				hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] =
+				    hdr->version;
+				hdr_entry[DDL_METADATA_HDR_PORT_INDEX] =
+				    hdr->port_index;
+				hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] =
+				    hdr->type;
+				vcd_status = VCD_S_SUCCESS;
+			}
+		}
+	}
+	return vcd_status;
+}
+
+u32 ddl_get_metadata_params(struct ddl_client_context *ddl,
+	struct vcd_property_hdr *property_hdr,
+	void	*property_value)
+{
+	u32 vcd_status = VCD_ERR_ILLEGAL_PARM ;
+	if (property_hdr->prop_id == VCD_I_METADATA_ENABLE &&
+		sizeof(struct vcd_property_meta_data_enable)
+		== property_hdr->sz) {
+		struct vcd_property_meta_data_enable *meta_data_enable =
+			(struct vcd_property_meta_data_enable *)
+			property_value;
+		meta_data_enable->meta_data_enable_flag =
+			((ddl->decoding) ?
+			(ddl->codec_data.decoder.meta_data_enable_flag)
+			: (ddl->codec_data.encoder.meta_data_enable_flag));
+		vcd_status = VCD_S_SUCCESS;
+	} else if (property_hdr->prop_id == VCD_I_METADATA_HEADER &&
+		sizeof(struct vcd_property_metadata_hdr) ==
+		property_hdr->sz) {
+		struct vcd_property_metadata_hdr *hdr =
+			(struct vcd_property_metadata_hdr *)
+			property_value;
+		u32 flag = ddl_supported_metadata_flag(ddl);
+		flag |= DDL_METADATA_MANDATORY;
+		flag &= hdr->meta_data_id;
+		if (!(flag & (flag - 1))) {
+			u32 *hdr_entry = ddl_metadata_hdr_entry(ddl,
+				flag);
+			hdr->version =
+			hdr_entry[DDL_METADATA_HDR_VERSION_INDEX];
+			hdr->port_index =
+			hdr_entry[DDL_METADATA_HDR_PORT_INDEX];
+			hdr->type =
+				hdr_entry[DDL_METADATA_HDR_TYPE_INDEX];
+			vcd_status = VCD_S_SUCCESS;
+		}
+	}
+	return vcd_status;
+}
+
+void ddl_metadata_enable(struct ddl_client_context *ddl)
+{
+	u32 flag, hal_flag = 0;
+	u32 *metadata_input;
+	if (ddl->decoding) {
+		flag = ddl->codec_data.decoder.meta_data_enable_flag;
+		metadata_input =
+		    ddl->codec_data.decoder.meta_data_input.
+		    align_physical_addr;
+	} else {
+		flag = ddl->codec_data.encoder.meta_data_enable_flag;
+		metadata_input =
+		    ddl->codec_data.encoder.meta_data_input.
+		    align_physical_addr;
+	}
+	if (flag) {
+		if (flag & VCD_METADATA_QPARRAY)
+			hal_flag |= VIDC_720P_METADATA_ENABLE_QP;
+		if (flag & VCD_METADATA_CONCEALMB)
+			hal_flag |= VIDC_720P_METADATA_ENABLE_CONCEALMB;
+		if (flag & VCD_METADATA_VC1)
+			hal_flag |= VIDC_720P_METADATA_ENABLE_VC1;
+		if (flag & VCD_METADATA_SEI)
+			hal_flag |= VIDC_720P_METADATA_ENABLE_SEI;
+		if (flag & VCD_METADATA_VUI)
+			hal_flag |= VIDC_720P_METADATA_ENABLE_VUI;
+		if (flag & VCD_METADATA_ENC_SLICE)
+			hal_flag |= VIDC_720P_METADATA_ENABLE_ENCSLICE;
+		if (flag & VCD_METADATA_PASSTHROUGH)
+			hal_flag |= VIDC_720P_METADATA_ENABLE_PASSTHROUGH;
+	} else {
+		metadata_input = 0;
+	}
+	vidc_720p_metadata_enable(hal_flag, metadata_input);
+}
+
+u32 ddl_encode_set_metadata_output_buf(struct ddl_client_context *ddl)
+{
+	struct ddl_encoder_data *encoder = &ddl->codec_data.encoder;
+	u32 *buffer;
+	struct vcd_frame_data *stream = &(ddl->output_frame.vcd_frm);
+	u32 ext_buffer_end, hw_metadata_start;
+
+	ext_buffer_end = (u32) stream->physical + stream->alloc_len;
+	if (!encoder->meta_data_enable_flag) {
+		ext_buffer_end &= ~(DDL_STREAMBUF_ALIGN_GUARD_BYTES);
+		return ext_buffer_end;
+	}
+	hw_metadata_start = (ext_buffer_end - encoder->suffix) &
+	    ~(DDL_STREAMBUF_ALIGN_GUARD_BYTES);
+
+	ext_buffer_end = (hw_metadata_start - 1) &
+	    ~(DDL_STREAMBUF_ALIGN_GUARD_BYTES);
+
+	buffer = encoder->meta_data_input.align_virtual_addr;
+
+	*buffer++ = encoder->suffix;
+
+	*buffer = hw_metadata_start;
+
+	encoder->meta_data_offset =
+	    hw_metadata_start - (u32) stream->physical;
+
+	return ext_buffer_end;
+}
+
+void ddl_decode_set_metadata_output(struct ddl_decoder_data *decoder)
+{
+	u32 *buffer;
+	u32 loopc;
+
+	if (!decoder->meta_data_enable_flag) {
+		decoder->meta_data_offset = 0;
+		return;
+	}
+
+	decoder->meta_data_offset = ddl_get_yuv_buffer_size(
+		&decoder->client_frame_size, &decoder->buf_format,
+		(!decoder->progressive_only), decoder->codec.codec);
+
+	buffer = decoder->meta_data_input.align_virtual_addr;
+
+	*buffer++ = decoder->suffix;
+
+	for (loopc = 0; loopc < decoder->dp_buf.no_of_dec_pic_buf;
+	     ++loopc) {
+		*buffer++ = (u32) (decoder->meta_data_offset + (u8 *)
+				     decoder->dp_buf.
+				     dec_pic_buffers[loopc].vcd_frm.
+				     physical);
+	}
+}
+
+void ddl_process_encoder_metadata(struct ddl_client_context *ddl)
+{
+	struct ddl_encoder_data *encoder = &(ddl->codec_data.encoder);
+	struct vcd_frame_data *out_frame =
+	    &(ddl->output_frame.vcd_frm);
+	u32 *qfiller_hdr, *qfiller, start_addr;
+	u32 qfiller_size;
+
+	if (!encoder->meta_data_enable_flag) {
+		out_frame->flags &= ~(VCD_FRAME_FLAG_EXTRADATA);
+		return;
+	}
+
+	if (!encoder->enc_frame_info.metadata_exists) {
+		out_frame->flags &= ~(VCD_FRAME_FLAG_EXTRADATA);
+		return;
+	}
+	out_frame->flags |= VCD_FRAME_FLAG_EXTRADATA;
+
+	start_addr = (u32) ((u8 *) out_frame->virtual +
+			      out_frame->offset);
+	qfiller = (u32 *) ((out_frame->data_len + start_addr + 3) & ~3);
+
+	qfiller_size = (u32) ((encoder->meta_data_offset +
+				 (u8 *) out_frame->virtual) -
+				(u8 *) qfiller);
+
+	qfiller_hdr = ddl_metadata_hdr_entry(ddl, VCD_METADATA_QCOMFILLER);
+
+	*qfiller++ = qfiller_size;
+	*qfiller++ = qfiller_hdr[DDL_METADATA_HDR_VERSION_INDEX];
+	*qfiller++ = qfiller_hdr[DDL_METADATA_HDR_PORT_INDEX];
+	*qfiller++ = qfiller_hdr[DDL_METADATA_HDR_TYPE_INDEX];
+	*qfiller = (u32) (qfiller_size - DDL_METADATA_HDR_SIZE);
+}
+
+void ddl_process_decoder_metadata(struct ddl_client_context *ddl)
+{
+	struct ddl_decoder_data *decoder = &(ddl->codec_data.decoder);
+	struct vcd_frame_data *output_frame =
+	    &(ddl->output_frame.vcd_frm);
+	u32 *qfiller_hdr, *qfiller;
+	u32 qfiller_size;
+
+	if (!decoder->meta_data_enable_flag) {
+		output_frame->flags &= ~(VCD_FRAME_FLAG_EXTRADATA);
+		return;
+	}
+
+	if (!decoder->dec_disp_info.metadata_exists) {
+		output_frame->flags &= ~(VCD_FRAME_FLAG_EXTRADATA);
+		return;
+	}
+	output_frame->flags |= VCD_FRAME_FLAG_EXTRADATA;
+
+	if (output_frame->data_len != decoder->meta_data_offset) {
+		qfiller = (u32 *) ((u32) ((output_frame->data_len +
+					     output_frame->offset +
+					     (u8 *) output_frame->virtual) +
+					    3) & ~3);
+
+		qfiller_size = (u32) ((decoder->meta_data_offset +
+					 (u8 *) output_frame->virtual) -
+					(u8 *) qfiller);
+
+		qfiller_hdr =
+		    ddl_metadata_hdr_entry(ddl, VCD_METADATA_QCOMFILLER);
+		*qfiller++ = qfiller_size;
+		*qfiller++ = qfiller_hdr[DDL_METADATA_HDR_VERSION_INDEX];
+		*qfiller++ = qfiller_hdr[DDL_METADATA_HDR_PORT_INDEX];
+		*qfiller++ = qfiller_hdr[DDL_METADATA_HDR_TYPE_INDEX];
+		*qfiller = (u32) (qfiller_size - DDL_METADATA_HDR_SIZE);
+	}
+}
diff --git a/drivers/video/msm/vidc/720p/ddl/vcd_ddl_metadata.h b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_metadata.h
new file mode 100644
index 0000000..ed43861
--- /dev/null
+++ b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_metadata.h
@@ -0,0 +1,79 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef _VCD_DDL_METADATA_H_
+#define _VCD_DDL_METADATA_H_
+
+#define DDL_MAX_DEC_METADATATYPE  (8)
+#define DDL_MAX_ENC_METADATATYPE  (3)
+
+#define DDL_METADATA_EXTRAPAD_SIZE (256)
+#define DDL_METADATA_HDR_SIZE (20)
+
+#define DDL_METADATA_EXTRADATANONE_SIZE (24)
+
+#define DDL_METADATA_ALIGNSIZE(x) ((x) = (((x) + 0x7) & ~0x7))
+
+#define DDL_METADATA_MANDATORY (VCD_METADATA_DATANONE | \
+				VCD_METADATA_QCOMFILLER)
+
+#define DDL_METADATA_VC1_PAYLOAD_SIZE (38*4)
+
+#define DDL_METADATA_SEI_PAYLOAD_SIZE (100)
+#define DDL_METADATA_SEI_MAX (5)
+
+#define DDL_METADATA_VUI_PAYLOAD_SIZE (256)
+
+#define DDL_METADATA_PASSTHROUGH_PAYLOAD_SIZE  (68)
+
+#define DDL_METADATA_CLIENT_INPUTBUFSIZE  (256)
+#define DDL_METADATA_TOTAL_INPUTBUFSIZE \
+	(DDL_METADATA_CLIENT_INPUTBUFSIZE * VCD_MAX_NO_CLIENT)
+
+#define DDL_METADATA_CLIENT_INPUTBUF(main_buffer, client_buffer, \
+		channel_id) \
+{ \
+  (client_buffer)->align_physical_addr = (u32 *)\
+	((u8 *)(main_buffer)->align_physical_addr + \
+	(DDL_METADATA_CLIENT_INPUTBUFSIZE * (channel_id)) \
+	); \
+  (client_buffer)->align_virtual_addr = (u32 *)\
+	((u8 *)(main_buffer)->align_virtual_addr + \
+	(DDL_METADATA_CLIENT_INPUTBUFSIZE * (channel_id)) \
+	); \
+  (client_buffer)->virtual_base_addr = 0; \
+}
+
+#define DDL_METADATA_HDR_VERSION_INDEX 0
+#define DDL_METADATA_HDR_PORT_INDEX    1
+#define DDL_METADATA_HDR_TYPE_INDEX    2
+
+
+void ddl_set_default_meta_data_hdr(struct ddl_client_context *ddl);
+u32 ddl_get_metadata_params(struct ddl_client_context	*ddl,
+	struct vcd_property_hdr *property_hdr, void *property_value);
+u32 ddl_set_metadata_params(struct ddl_client_context *ddl,
+			    struct vcd_property_hdr *property_hdr,
+			    void *property_value);
+void ddl_set_default_metadata_flag(struct ddl_client_context *ddl);
+void ddl_set_default_decoder_metadata_buffer_size
+    (struct ddl_decoder_data *decoder,
+	struct vcd_property_frame_size *frame_size,
+	struct vcd_buffer_requirement *output_buf_req);
+void ddl_set_default_encoder_metadata_buffer_size(struct ddl_encoder_data
+						  *encoder);
+void ddl_metadata_enable(struct ddl_client_context *ddl);
+u32 ddl_encode_set_metadata_output_buf(struct ddl_client_context *ddl);
+void ddl_decode_set_metadata_output(struct ddl_decoder_data *decoder);
+void ddl_process_encoder_metadata(struct ddl_client_context *ddl);
+void ddl_process_decoder_metadata(struct ddl_client_context *ddl);
+#endif
diff --git a/drivers/video/msm/vidc/720p/ddl/vcd_ddl_properties.c b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_properties.c
new file mode 100644
index 0000000..3aebdaf
--- /dev/null
+++ b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_properties.c
@@ -0,0 +1,1943 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <media/msm/vidc_type.h>
+#include "vcd_ddl_utils.h"
+#include "vcd_ddl_metadata.h"
+
+static u32 ddl_set_dec_property(struct ddl_client_context *pddl,
+				struct vcd_property_hdr *property_hdr,
+				void *property_value);
+static u32 ddl_set_enc_property(struct ddl_client_context *pddl,
+				struct vcd_property_hdr *property_hdr,
+				void *property_value);
+static u32 ddl_get_dec_property(struct ddl_client_context *pddl,
+				struct vcd_property_hdr *property_hdr,
+				void *property_value);
+static u32 ddl_get_enc_property(struct ddl_client_context *pddl,
+				struct vcd_property_hdr *property_hdr,
+				void *property_value);
+static u32 ddl_set_enc_dynamic_property(struct ddl_client_context *ddl,
+				struct vcd_property_hdr *property_hdr,
+				void *property_value);
+static void ddl_set_default_enc_property(struct ddl_client_context *ddl);
+static void ddl_set_default_enc_profile(struct ddl_encoder_data
+					*encoder);
+static void ddl_set_default_enc_level(struct ddl_encoder_data *encoder);
+static void ddl_set_default_enc_vop_timing(struct ddl_encoder_data
+					   *encoder);
+static void ddl_set_default_enc_intra_period(struct ddl_encoder_data
+					     *encoder);
+static void ddl_set_default_enc_rc_params(struct ddl_encoder_data
+					  *encoder);
+static u32 ddl_valid_buffer_requirement(struct vcd_buffer_requirement
+					*original_buf_req,
+					struct vcd_buffer_requirement
+					*req_buf_req);
+static u32 ddl_decoder_min_num_dpb(struct ddl_decoder_data *decoder);
+static u32 ddl_set_dec_buffers
+    (struct ddl_decoder_data *decoder,
+     struct ddl_property_dec_pic_buffers *dpb);
+
+u32 ddl_set_property(u32 *ddl_handle,
+     struct vcd_property_hdr *property_hdr, void *property_value)
+{
+	u32 vcd_status;
+	struct ddl_context *ddl_context;
+	struct ddl_client_context *ddl =
+	    (struct ddl_client_context *)ddl_handle;
+
+	if (!property_hdr || !property_value) {
+		VIDC_LOGERR_STRING("ddl_set_prop:Bad_argument");
+		return VCD_ERR_ILLEGAL_PARM;
+	}
+	ddl_context = ddl_get_context();
+
+	if (!DDL_IS_INITIALIZED(ddl_context)) {
+		VIDC_LOGERR_STRING("ddl_set_prop:Not_inited");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+
+	if (!ddl) {
+		VIDC_LOGERR_STRING("ddl_set_prop:Bad_handle");
+		return VCD_ERR_BAD_HANDLE;
+	}
+	if (ddl->decoding) {
+		vcd_status =
+		    ddl_set_dec_property(ddl, property_hdr,
+					 property_value);
+	} else {
+		vcd_status =
+		    ddl_set_enc_property(ddl, property_hdr,
+					 property_value);
+	}
+	if (vcd_status)
+		VIDC_LOGERR_STRING("ddl_set_prop:FAILED");
+
+	return vcd_status;
+}
+
+u32 ddl_get_property(u32 *ddl_handle,
+     struct vcd_property_hdr *property_hdr, void *property_value)
+{
+
+	u32 vcd_status = VCD_ERR_ILLEGAL_PARM;
+	struct ddl_context *ddl_context;
+	struct ddl_client_context *ddl =
+	    (struct ddl_client_context *)ddl_handle;
+
+	if (!property_hdr || !property_value)
+		return VCD_ERR_ILLEGAL_PARM;
+
+	if (property_hdr->prop_id == DDL_I_CAPABILITY) {
+		if (sizeof(struct ddl_property_capability) ==
+		    property_hdr->sz) {
+			struct ddl_property_capability *ddl_capability =
+			    (struct ddl_property_capability *)
+			    property_value;
+			ddl_capability->max_num_client = VCD_MAX_NO_CLIENT;
+			ddl_capability->exclusive =
+				VCD_COMMAND_EXCLUSIVE;
+			ddl_capability->frame_command_depth =
+				VCD_FRAME_COMMAND_DEPTH;
+			ddl_capability->general_command_depth =
+				VCD_GENERAL_COMMAND_DEPTH;
+			ddl_capability->ddl_time_out_in_ms =
+				DDL_HW_TIMEOUT_IN_MS;
+			vcd_status = VCD_S_SUCCESS;
+		}
+		return vcd_status;
+	}
+	ddl_context = ddl_get_context();
+	if (!DDL_IS_INITIALIZED(ddl_context))
+		return VCD_ERR_ILLEGAL_OP;
+
+	if (!ddl)
+		return VCD_ERR_BAD_HANDLE;
+
+	if (ddl->decoding) {
+		vcd_status =
+		    ddl_get_dec_property(ddl, property_hdr,
+					 property_value);
+	} else {
+		vcd_status =
+		    ddl_get_enc_property(ddl, property_hdr,
+					 property_value);
+	}
+	if (vcd_status)
+		VIDC_LOGERR_STRING("ddl_get_prop:FAILED");
+
+	return vcd_status;
+}
+
+u32 ddl_decoder_ready_to_start(struct ddl_client_context *ddl,
+     struct vcd_sequence_hdr *header)
+{
+	struct ddl_decoder_data *decoder = &(ddl->codec_data.decoder);
+	if (!decoder->codec.codec) {
+		VIDC_LOGERR_STRING("ddl_dec_start_check:Codec_not_set");
+		return false;
+	}
+	if ((!header) &&
+	    (!decoder->client_frame_size.height ||
+	     !decoder->client_frame_size.width)
+	    ) {
+		VIDC_LOGERR_STRING
+		    ("ddl_dec_start_check:Client_height_width_default");
+		return false;
+	}
+	return true;
+}
+
+u32 ddl_encoder_ready_to_start(struct ddl_client_context *ddl)
+{
+	struct ddl_encoder_data *encoder = &(ddl->codec_data.encoder);
+
+	if (!encoder->codec.codec ||
+	    !encoder->frame_size.height ||
+	    !encoder->frame_size.width ||
+	    !encoder->frame_rate.fps_denominator ||
+	    !encoder->frame_rate.fps_numerator ||
+	    !encoder->target_bit_rate.target_bitrate) {
+		return false;
+	}
+	return true;
+}
+
+static u32 ddl_set_dec_property
+    (struct ddl_client_context *ddl,
+     struct vcd_property_hdr *property_hdr, void *property_value) {
+	u32 vcd_status = VCD_ERR_ILLEGAL_PARM;
+	struct ddl_decoder_data *decoder = &(ddl->codec_data.decoder);
+	switch (property_hdr->prop_id) {
+	case DDL_I_DPB_RELEASE:
+		{
+			if (sizeof(struct ddl_frame_data_tag) ==
+			    property_hdr->sz
+			    && decoder->dp_buf.no_of_dec_pic_buf) {
+				vcd_status =
+				    ddl_decoder_dpb_transact(decoder,
+					     (struct ddl_frame_data_tag *)
+					     property_value,
+					     DDL_DPB_OP_MARK_FREE);
+			}
+			break;
+		}
+	case DDL_I_DPB:
+		{
+			struct ddl_property_dec_pic_buffers *dpb =
+			    (struct ddl_property_dec_pic_buffers *)
+			    property_value;
+
+			if (sizeof(struct ddl_property_dec_pic_buffers) ==
+			    property_hdr->sz &&
+			    (DDLCLIENT_STATE_IS
+			     (ddl, DDL_CLIENT_WAIT_FOR_INITCODEC)
+			     || DDLCLIENT_STATE_IS(ddl,
+						   DDL_CLIENT_WAIT_FOR_DPB)
+			    ) &&
+			    dpb->no_of_dec_pic_buf >=
+			    decoder->client_output_buf_req.actual_count) {
+				vcd_status =
+				    ddl_set_dec_buffers(decoder, dpb);
+			}
+			break;
+		}
+	case DDL_I_REQ_OUTPUT_FLUSH:
+		{
+			if (sizeof(u32) == property_hdr->sz) {
+				decoder->dynamic_prop_change |=
+				    DDL_DEC_REQ_OUTPUT_FLUSH;
+				decoder->dpb_mask.client_mask = 0;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case DDL_I_INPUT_BUF_REQ:
+		{
+			struct vcd_buffer_requirement *buffer_req =
+			    (struct vcd_buffer_requirement *)
+			    property_value;
+			if (sizeof(struct vcd_buffer_requirement) ==
+			    property_hdr->sz &&
+			    (ddl_valid_buffer_requirement(
+						&decoder->min_input_buf_req,
+						buffer_req))) {
+				decoder->client_input_buf_req = *buffer_req;
+				decoder->client_input_buf_req.min_count =
+					decoder->min_input_buf_req.min_count;
+				decoder->client_input_buf_req.max_count =
+					decoder->min_input_buf_req.max_count;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case DDL_I_OUTPUT_BUF_REQ:
+		{
+			struct vcd_buffer_requirement *buffer_req =
+			    (struct vcd_buffer_requirement *)
+			    property_value;
+			if (sizeof(struct vcd_buffer_requirement) ==
+			    property_hdr->sz &&
+			    (ddl_valid_buffer_requirement(
+						&decoder->min_output_buf_req,
+						buffer_req))) {
+				decoder->client_output_buf_req =
+				    *buffer_req;
+				decoder->client_output_buf_req.min_count =
+					decoder->min_output_buf_req.min_count;
+				decoder->client_output_buf_req.max_count =
+					decoder->min_output_buf_req.max_count;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+
+	case VCD_I_CODEC:
+		{
+			struct vcd_property_codec *codec =
+			    (struct vcd_property_codec *)property_value;
+			if (sizeof(struct vcd_property_codec) ==
+			    property_hdr->sz
+			    && DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN)
+			    ) {
+				u32 status;
+				vcd_fw_transact(false, true,
+					decoder->codec.codec);
+				status = vcd_fw_transact(true, true,
+					codec->codec);
+				if (status) {
+					decoder->codec = *codec;
+					ddl_set_default_dec_property(ddl);
+					vcd_status = VCD_S_SUCCESS;
+				} else {
+					status = vcd_fw_transact(true, true,
+						decoder->codec.codec);
+					vcd_status = VCD_ERR_NOT_SUPPORTED;
+				}
+			}
+			break;
+		}
+	case VCD_I_POST_FILTER:
+		{
+			if (sizeof(struct vcd_property_post_filter) ==
+			    property_hdr->sz
+			    && DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN) &&
+			    (decoder->codec.codec == VCD_CODEC_MPEG4 ||
+			     decoder->codec.codec == VCD_CODEC_MPEG2)
+			    ) {
+				decoder->post_filter =
+				    *(struct vcd_property_post_filter *)
+				    property_value;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_FRAME_SIZE:
+		{
+			struct vcd_property_frame_size *frame_size =
+			    (struct vcd_property_frame_size *)
+			    property_value;
+
+			if ((sizeof(struct vcd_property_frame_size) ==
+					property_hdr->sz) &&
+				(DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN))) {
+				if (decoder->client_frame_size.height !=
+				    frame_size->height
+				    || decoder->client_frame_size.width !=
+				    frame_size->width) {
+					decoder->client_frame_size =
+					    *frame_size;
+					ddl_set_default_decoder_buffer_req
+					    (decoder, true);
+				}
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_BUFFER_FORMAT:
+		{
+			struct vcd_property_buffer_format *tile =
+			    (struct vcd_property_buffer_format *)
+			    property_value;
+			if (sizeof(struct vcd_property_buffer_format) ==
+			    property_hdr->sz &&
+			    DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN) &&
+			    (tile->buffer_format == VCD_BUFFER_FORMAT_NV12
+			     || tile->buffer_format ==
+			     VCD_BUFFER_FORMAT_TILE_4x2)
+			    ) {
+				if (tile->buffer_format !=
+				    decoder->buf_format.buffer_format) {
+					decoder->buf_format = *tile;
+					ddl_set_default_decoder_buffer_req
+					    (decoder, true);
+				}
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_METADATA_ENABLE:
+	case VCD_I_METADATA_HEADER:
+		{
+			vcd_status = ddl_set_metadata_params(ddl,
+							     property_hdr,
+							     property_value);
+			break;
+		}
+	case VCD_I_OUTPUT_ORDER:
+		{
+			if (sizeof(u32) == property_hdr->sz &&
+				DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN)) {
+					decoder->output_order =
+						*(u32 *)property_value;
+					vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_DEC_PICTYPE:
+		{
+			if ((sizeof(u32) == property_hdr->sz) &&
+				DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN)) {
+				decoder->idr_only_decoding =
+					*(u32 *)property_value;
+				ddl_set_default_decoder_buffer_req(
+						decoder, true);
+				vcd_status = VCD_S_SUCCESS;
+			}
+		}
+		break;
+	case VCD_I_FRAME_RATE:
+		{
+			vcd_status = VCD_S_SUCCESS;
+			break;
+		}
+	default:
+		{
+			vcd_status = VCD_ERR_ILLEGAL_OP;
+			break;
+		}
+	}
+	return vcd_status;
+}
+
+static u32 ddl_set_enc_property(struct ddl_client_context *ddl,
+	struct vcd_property_hdr *property_hdr, void *property_value)
+{
+	u32 vcd_status = VCD_ERR_ILLEGAL_PARM;
+	struct ddl_encoder_data *encoder = &(ddl->codec_data.encoder);
+
+	if (DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME) ||
+	   (DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME_DONE) ||
+		DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN)))
+		vcd_status = ddl_set_enc_dynamic_property(ddl,
+			property_hdr, property_value);
+	if (vcd_status == VCD_S_SUCCESS)
+		return vcd_status;
+
+	if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN) ||
+		vcd_status != VCD_ERR_ILLEGAL_OP) {
+		VIDC_LOGERR_STRING
+			("ddl_set_enc_property:Fails_as_not_in_open_state");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+
+	switch (property_hdr->prop_id) {
+	case VCD_I_FRAME_SIZE:
+		{
+			struct vcd_property_frame_size *framesize =
+				(struct vcd_property_frame_size *)
+				property_value;
+
+			if (sizeof(struct vcd_property_frame_size)
+				== property_hdr->sz &&
+				DDL_ALLOW_ENC_FRAMESIZE(framesize->width,
+				framesize->height) &&
+				(encoder->codec.codec == VCD_CODEC_H264 ||
+				 DDL_VALIDATE_ENC_FRAMESIZE(framesize->width,
+				 framesize->height))
+				) {
+				encoder->frame_size = *framesize;
+				ddl_calculate_stride(&encoder->frame_size,
+					false, encoder->codec.codec);
+				ddl_set_default_encoder_buffer_req(encoder);
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_CODEC:
+		{
+			struct vcd_property_codec *codec =
+				(struct vcd_property_codec *)
+				property_value;
+			if (sizeof(struct vcd_property_codec) ==
+				property_hdr->sz) {
+				u32 status;
+
+				vcd_fw_transact(false, false,
+					encoder->codec.codec);
+
+				status = vcd_fw_transact(true, false,
+					codec->codec);
+				if (status) {
+					encoder->codec = *codec;
+					ddl_set_default_enc_property(ddl);
+					vcd_status = VCD_S_SUCCESS;
+				} else {
+					status = vcd_fw_transact(true, false,
+						encoder->codec.codec);
+					vcd_status = VCD_ERR_NOT_SUPPORTED;
+				}
+			}
+			break;
+		}
+	case VCD_I_PROFILE:
+		{
+			struct vcd_property_profile *profile =
+				(struct vcd_property_profile *)
+				property_value;
+			if ((sizeof(struct vcd_property_profile) ==
+				property_hdr->sz) &&
+				((encoder->codec.codec ==
+					VCD_CODEC_MPEG4 &&
+				  (profile->profile ==
+					VCD_PROFILE_MPEG4_SP ||
+					profile->profile ==
+					VCD_PROFILE_MPEG4_ASP)) ||
+				 (encoder->codec.codec ==
+					VCD_CODEC_H264 &&
+				 (profile->profile >=
+					VCD_PROFILE_H264_BASELINE ||
+				  profile->profile <=
+					VCD_PROFILE_H264_HIGH)) ||
+				 (encoder->codec.codec ==
+					VCD_CODEC_H263 &&
+				  profile->profile ==
+					VCD_PROFILE_H263_BASELINE))
+				) {
+				encoder->profile = *profile;
+				vcd_status = VCD_S_SUCCESS;
+
+				if (profile->profile ==
+					VCD_PROFILE_H264_BASELINE)
+					encoder->entropy_control.entropy_sel
+						= VCD_ENTROPY_SEL_CAVLC;
+				else
+					encoder->entropy_control.entropy_sel
+						= VCD_ENTROPY_SEL_CABAC;
+			}
+			break;
+		}
+	case VCD_I_LEVEL:
+		{
+			struct vcd_property_level *level =
+				(struct vcd_property_level *)
+				property_value;
+			if (
+				(sizeof(struct vcd_property_level) ==
+				 property_hdr->sz
+				) &&
+				(
+				(
+				(encoder->codec.
+				 codec == VCD_CODEC_MPEG4) &&
+				(level->level >= VCD_LEVEL_MPEG4_0) &&
+				(level->level <= VCD_LEVEL_MPEG4_6)
+				) ||
+				(
+				(encoder->codec.
+				 codec == VCD_CODEC_H264) &&
+				(level->level >= VCD_LEVEL_H264_1) &&
+				(level->level <= VCD_LEVEL_H264_3p1)
+				) ||
+				(
+				(encoder->codec.
+				 codec == VCD_CODEC_H263) &&
+				(level->level >= VCD_LEVEL_H263_10) &&
+				(level->level <= VCD_LEVEL_H263_70)
+				)
+				)
+				) {
+				encoder->level = *level;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_MULTI_SLICE:
+		{
+			struct vcd_property_multi_slice *multislice =
+				(struct vcd_property_multi_slice *)
+				property_value;
+			switch (multislice->m_slice_sel) {
+			case VCD_MSLICE_OFF:
+				{
+					vcd_status = VCD_S_SUCCESS;
+					break;
+				}
+			case VCD_MSLICE_BY_GOB:
+				{
+					if (encoder->codec.codec ==
+						VCD_CODEC_H263)
+						vcd_status = VCD_S_SUCCESS;
+					 break;
+				}
+			case VCD_MSLICE_BY_MB_COUNT:
+				{
+					if (multislice->m_slice_size
+						>= 1 && (multislice->
+						m_slice_size <=
+						(encoder->frame_size.height
+						* encoder->frame_size.width
+						/ 16 / 16))
+						) {
+						vcd_status = VCD_S_SUCCESS;
+					}
+					break;
+				  }
+			case VCD_MSLICE_BY_BYTE_COUNT:
+				{
+					if (multislice->m_slice_size > 0)
+						vcd_status = VCD_S_SUCCESS;
+					break;
+				}
+			default:
+				{
+					break;
+				}
+			}
+			if (sizeof(struct vcd_property_multi_slice) ==
+				property_hdr->sz &&
+				!vcd_status) {
+				encoder->multi_slice = *multislice;
+				if (multislice->m_slice_sel ==
+						VCD_MSLICE_OFF)
+					encoder->multi_slice.m_slice_size = 0;
+			}
+			break;
+		}
+	case VCD_I_RATE_CONTROL:
+		{
+			struct vcd_property_rate_control
+				*ratecontrol =
+				(struct vcd_property_rate_control *)
+				property_value;
+			if (sizeof(struct vcd_property_rate_control) ==
+				property_hdr->sz &&
+				ratecontrol->
+				rate_control >= VCD_RATE_CONTROL_OFF &&
+				ratecontrol->
+				rate_control <= VCD_RATE_CONTROL_CBR_CFR
+				) {
+				encoder->rc = *ratecontrol;
+				ddl_set_default_enc_rc_params(encoder);
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_SHORT_HEADER:
+		{
+
+		if (sizeof(struct vcd_property_short_header) ==
+			property_hdr->sz &&
+			encoder->codec.codec == VCD_CODEC_MPEG4) {
+			encoder->short_header =
+				*(struct vcd_property_short_header *)
+				property_value;
+			vcd_status = VCD_S_SUCCESS;
+		}
+
+			break;
+		}
+	case VCD_I_VOP_TIMING:
+		{
+			struct vcd_property_vop_timing *voptime =
+				(struct vcd_property_vop_timing *)
+				property_value;
+			if (
+				(sizeof(struct vcd_property_vop_timing) ==
+					  property_hdr->sz
+				) &&
+				(encoder->frame_rate.fps_numerator <=
+					voptime->vop_time_resolution)
+				) {
+				encoder->vop_timing = *voptime;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_HEADER_EXTENSION:
+		{
+			if (sizeof(u32) == property_hdr->sz &&
+				encoder->codec.codec == VCD_CODEC_MPEG4
+				) {
+				encoder->hdr_ext_control = *(u32 *)
+					property_value;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_ENTROPY_CTRL:
+		{
+			struct vcd_property_entropy_control
+				*entropy_control =
+				(struct vcd_property_entropy_control *)
+				property_value;
+			if (sizeof(struct vcd_property_entropy_control) ==
+				property_hdr->sz &&
+				encoder->codec.codec == VCD_CODEC_H264
+				&& entropy_control->
+				entropy_sel >= VCD_ENTROPY_SEL_CAVLC &&
+				entropy_control->entropy_sel <=
+				VCD_ENTROPY_SEL_CABAC) {
+				encoder->entropy_control = *entropy_control;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_DEBLOCKING:
+		{
+			struct vcd_property_db_config *dbconfig =
+				(struct vcd_property_db_config *)
+				property_value;
+			if (sizeof(struct vcd_property_db_config) ==
+				property_hdr->sz &&
+				encoder->codec.codec == VCD_CODEC_H264
+				&& dbconfig->db_config >=
+				VCD_DB_ALL_BLOCKING_BOUNDARY
+				&& dbconfig->db_config <=
+				VCD_DB_SKIP_SLICE_BOUNDARY
+				) {
+				encoder->db_control = *dbconfig;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_QP_RANGE:
+		{
+			struct vcd_property_qp_range *qp =
+				(struct vcd_property_qp_range *)
+				property_value;
+			if ((sizeof(struct vcd_property_qp_range) ==
+				property_hdr->sz) &&
+				(qp->min_qp <= qp->max_qp) &&
+				(
+				(encoder->codec.codec == VCD_CODEC_H264
+				&& qp->max_qp <= DDL_MAX_H264_QP) ||
+				(qp->max_qp <= DDL_MAX_MPEG4_QP)
+				)
+				) {
+				encoder->qp_range = *qp;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_SESSION_QP:
+		{
+			struct vcd_property_session_qp *qp =
+				(struct vcd_property_session_qp *)
+				property_value;
+
+		if ((sizeof(struct vcd_property_session_qp) ==
+			property_hdr->sz) &&
+			(qp->i_frame_qp >= encoder->qp_range.min_qp) &&
+			(qp->i_frame_qp <= encoder->qp_range.max_qp) &&
+			(qp->p_frame_qp >= encoder->qp_range.min_qp) &&
+			(qp->p_frame_qp <= encoder->qp_range.max_qp)
+			) {
+			encoder->session_qp = *qp;
+			vcd_status = VCD_S_SUCCESS;
+		}
+
+			break;
+		}
+	case VCD_I_RC_LEVEL_CONFIG:
+		{
+			struct vcd_property_rc_level *rc_level =
+				(struct vcd_property_rc_level *)
+				property_value;
+			if (sizeof(struct vcd_property_rc_level) ==
+				property_hdr->sz &&
+				(
+				encoder->rc.
+				rate_control >= VCD_RATE_CONTROL_VBR_VFR ||
+				encoder->rc.
+				rate_control <= VCD_RATE_CONTROL_CBR_VFR
+				) &&
+				(!rc_level->mb_level_rc ||
+				encoder->codec.codec == VCD_CODEC_H264
+				)
+				) {
+				encoder->rc_level = *rc_level;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_FRAME_LEVEL_RC:
+		{
+
+		struct vcd_property_frame_level_rc_params
+			*frame_levelrc =
+			(struct vcd_property_frame_level_rc_params *)
+			property_value;
+
+			if ((sizeof(struct
+				vcd_property_frame_level_rc_params)
+				== property_hdr->sz) &&
+				(frame_levelrc->reaction_coeff) &&
+				(encoder->rc_level.frame_level_rc)
+				) {
+				encoder->frame_level_rc = *frame_levelrc;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_ADAPTIVE_RC:
+		{
+
+		if ((sizeof(struct
+			vcd_property_adaptive_rc_params)
+			== property_hdr->sz) &&
+			(encoder->codec.
+			codec == VCD_CODEC_H264) &&
+			(encoder->rc_level.mb_level_rc)) {
+
+			encoder->adaptive_rc =
+				*(struct vcd_property_adaptive_rc_params *)
+				property_value;
+
+			vcd_status = VCD_S_SUCCESS;
+		}
+
+			break;
+		}
+	case VCD_I_BUFFER_FORMAT:
+		{
+			struct vcd_property_buffer_format *tile =
+				(struct vcd_property_buffer_format *)
+				property_value;
+			if (sizeof(struct vcd_property_buffer_format) ==
+				property_hdr->sz &&
+				tile->buffer_format ==
+				VCD_BUFFER_FORMAT_NV12) {
+				encoder->buf_format = *tile;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case DDL_I_INPUT_BUF_REQ:
+		{
+			struct vcd_buffer_requirement *buffer_req =
+				(struct vcd_buffer_requirement *)
+				property_value;
+			if (sizeof(struct vcd_buffer_requirement) ==
+				property_hdr->sz &&
+				(ddl_valid_buffer_requirement(
+				&encoder->input_buf_req, buffer_req))
+				) {
+				encoder->client_input_buf_req = *buffer_req;
+				encoder->client_input_buf_req.min_count =
+					encoder->input_buf_req.min_count;
+				encoder->client_input_buf_req.max_count =
+					encoder->input_buf_req.max_count;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case DDL_I_OUTPUT_BUF_REQ:
+		{
+			struct vcd_buffer_requirement *buffer_req =
+				(struct vcd_buffer_requirement *)
+				property_value;
+			if (sizeof(struct vcd_buffer_requirement) ==
+				property_hdr->sz &&
+				(ddl_valid_buffer_requirement(
+				&encoder->output_buf_req, buffer_req))
+				) {
+				encoder->client_output_buf_req =
+					*buffer_req;
+				encoder->client_output_buf_req.min_count =
+					encoder->output_buf_req.min_count;
+				encoder->client_output_buf_req.max_count =
+					encoder->output_buf_req.max_count;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_METADATA_ENABLE:
+	case VCD_I_METADATA_HEADER:
+		{
+			vcd_status = ddl_set_metadata_params(
+				ddl, property_hdr, property_value);
+			break;
+		}
+	case VCD_I_META_BUFFER_MODE:
+		{
+			vcd_status = VCD_S_SUCCESS;
+			break;
+		}
+	default:
+		{
+			vcd_status = VCD_ERR_ILLEGAL_OP;
+			break;
+		}
+	}
+	return vcd_status;
+}
+
+static u32 ddl_get_dec_property
+    (struct ddl_client_context *ddl,
+     struct vcd_property_hdr *property_hdr, void *property_value) {
+	u32 vcd_status = VCD_ERR_ILLEGAL_PARM;
+	struct ddl_decoder_data *decoder = &ddl->codec_data.decoder;
+
+	switch (property_hdr->prop_id) {
+	case VCD_I_FRAME_SIZE:
+		{
+			struct vcd_property_frame_size *fz_size;
+			if (sizeof(struct vcd_property_frame_size) ==
+			    property_hdr->sz) {
+					ddl_calculate_stride(
+					&decoder->client_frame_size,
+					!decoder->progressive_only,
+					decoder->codec.codec);
+					if (decoder->buf_format.buffer_format
+						== VCD_BUFFER_FORMAT_TILE_4x2) {
+						fz_size =
+						&decoder->client_frame_size;
+						fz_size->stride =
+						DDL_TILE_ALIGN(fz_size->width,
+							DDL_TILE_ALIGN_WIDTH);
+						fz_size->scan_lines =
+						DDL_TILE_ALIGN(fz_size->height,
+							DDL_TILE_ALIGN_HEIGHT);
+					}
+					*(struct vcd_property_frame_size *)
+						property_value =
+						decoder->client_frame_size;
+					vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_PROFILE:
+		{
+			if (sizeof(struct vcd_property_profile) ==
+			    property_hdr->sz) {
+				*(struct vcd_property_profile *)
+				    property_value = decoder->profile;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_LEVEL:
+		{
+			if (sizeof(struct vcd_property_level) ==
+			    property_hdr->sz) {
+				*(struct vcd_property_level *)
+				    property_value = decoder->level;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_PROGRESSIVE_ONLY:
+		{
+			if (sizeof(u32) == property_hdr->sz) {
+				*(u32 *) property_value =
+				    decoder->progressive_only;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case DDL_I_INPUT_BUF_REQ:
+		{
+			if (sizeof(struct vcd_buffer_requirement) ==
+			    property_hdr->sz) {
+				*(struct vcd_buffer_requirement *)
+				    property_value =
+						decoder->client_input_buf_req;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case DDL_I_OUTPUT_BUF_REQ:
+		{
+			if (sizeof(struct vcd_buffer_requirement) ==
+			    property_hdr->sz) {
+				*(struct vcd_buffer_requirement *)
+				    property_value =
+						decoder->client_output_buf_req;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_CODEC:
+		{
+			if (sizeof(struct vcd_property_codec) ==
+			    property_hdr->sz) {
+				*(struct vcd_property_codec *)
+				    property_value = decoder->codec;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_BUFFER_FORMAT:
+		{
+			if (sizeof(struct vcd_property_buffer_format) ==
+			    property_hdr->sz) {
+				*(struct vcd_property_buffer_format *)
+				    property_value = decoder->buf_format;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_POST_FILTER:
+		{
+			if (sizeof(struct vcd_property_post_filter) ==
+			    property_hdr->sz) {
+				*(struct vcd_property_post_filter *)
+				    property_value = decoder->post_filter;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case DDL_I_SEQHDR_ALIGN_BYTES:
+		{
+			if (sizeof(u32) == property_hdr->sz) {
+				*(u32 *) property_value =
+				    DDL_LINEAR_BUFFER_ALIGN_BYTES;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case DDL_I_FRAME_PROC_UNITS:
+		{
+			if (sizeof(u32) == property_hdr->sz) {
+				struct vcd_property_frame_size frame_sz =
+					decoder->client_frame_size;
+				ddl_calculate_stride(&frame_sz,
+					!decoder->progressive_only,
+					decoder->codec.codec);
+				*(u32 *) property_value =
+				    ((frame_sz.stride >> 4) *
+				     (frame_sz.scan_lines >> 4));
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case DDL_I_DPB_RETRIEVE:
+		{
+			if (sizeof(struct ddl_frame_data_tag) ==
+			    property_hdr->sz) {
+				vcd_status =
+				    ddl_decoder_dpb_transact(decoder,
+					 (struct ddl_frame_data_tag *)
+					     property_value,
+					     DDL_DPB_OP_RETRIEVE);
+			}
+			break;
+		}
+	case VCD_I_OUTPUT_ORDER:
+		{
+			if (sizeof(u32) == property_hdr->sz) {
+				*(u32 *)property_value = decoder->output_order;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_METADATA_ENABLE:
+	case VCD_I_METADATA_HEADER:
+		{
+			vcd_status = ddl_get_metadata_params(
+						   ddl,
+						   property_hdr,
+						   property_value);
+			break;
+		}
+	default:
+		{
+			vcd_status = VCD_ERR_ILLEGAL_OP;
+			break;
+		}
+	}
+	return vcd_status;
+}
+
+static u32 ddl_get_enc_property
+    (struct ddl_client_context *ddl,
+     struct vcd_property_hdr *property_hdr, void *property_value) {
+	u32 vcd_status = VCD_ERR_ILLEGAL_PARM;
+	struct ddl_encoder_data *encoder = &ddl->codec_data.encoder;
+
+	struct vcd_property_entropy_control *entropy_control;
+	struct vcd_property_intra_refresh_mb_number *intra_refresh;
+
+	switch (property_hdr->prop_id) {
+	case VCD_I_CODEC:
+		{
+			if (sizeof(struct vcd_property_codec) ==
+			    property_hdr->sz) {
+				*(struct vcd_property_codec *)
+					property_value =
+					encoder->codec;
+		    vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_FRAME_SIZE:
+		{
+			if (sizeof(struct vcd_property_frame_size) ==
+			    property_hdr->sz) {
+				*(struct vcd_property_frame_size *)
+					property_value =
+					encoder->frame_size;
+
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_FRAME_RATE:
+		{
+			if (sizeof(struct vcd_property_frame_rate) ==
+				property_hdr->sz) {
+
+				*(struct vcd_property_frame_rate *)
+					property_value =
+					encoder->frame_rate;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_TARGET_BITRATE:
+		{
+
+			if (sizeof(struct vcd_property_target_bitrate) ==
+			    property_hdr->sz) {
+				*(struct vcd_property_target_bitrate *)
+					property_value =
+					encoder->target_bit_rate;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_RATE_CONTROL:
+		{
+			if (sizeof(struct vcd_property_rate_control) ==
+			    property_hdr->sz) {
+				*(struct vcd_property_rate_control *)
+				    property_value = encoder->rc;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_PROFILE:
+		{
+			if (sizeof(struct vcd_property_profile) ==
+			    property_hdr->sz) {
+				*(struct vcd_property_profile *)
+				    property_value = encoder->profile;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_LEVEL:
+		{
+			if (sizeof(struct vcd_property_level) ==
+			    property_hdr->sz) {
+				*(struct vcd_property_level *)
+				    property_value = encoder->level;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_MULTI_SLICE:
+		{
+			if (sizeof(struct vcd_property_multi_slice) ==
+			    property_hdr->sz) {
+				*(struct vcd_property_multi_slice *)
+				    property_value = encoder->multi_slice;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_SEQ_HEADER:
+		{
+			struct vcd_sequence_hdr *seq_hdr =
+			    (struct vcd_sequence_hdr *)property_value;
+			if (encoder->seq_header.buffer_size &&
+			    sizeof(struct vcd_sequence_hdr) ==
+			    property_hdr->sz
+			    && encoder->seq_header.buffer_size <=
+			    seq_hdr->sequence_header_len) {
+				DDL_MEMCPY(seq_hdr->sequence_header,
+					   encoder->seq_header.
+					   align_virtual_addr,
+					   encoder->seq_header.buffer_size);
+				seq_hdr->sequence_header_len =
+				    encoder->seq_header.buffer_size;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case DDL_I_SEQHDR_PRESENT:
+		{
+			if (sizeof(u32) == property_hdr->sz) {
+				if ((encoder->codec.
+					codec == VCD_CODEC_MPEG4 &&
+					!encoder->short_header.short_header)
+					|| encoder->codec.codec ==
+					VCD_CODEC_H264) {
+					*(u32 *)property_value = 0x1;
+				} else {
+					*(u32 *)property_value = 0x0;
+				}
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_VOP_TIMING:
+		{
+			if (sizeof(struct vcd_property_vop_timing) ==
+			    property_hdr->sz) {
+				*(struct vcd_property_vop_timing *)
+				    property_value = encoder->vop_timing;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_SHORT_HEADER:
+		{
+			if (sizeof(struct vcd_property_short_header) ==
+			    property_hdr->sz) {
+				if (encoder->codec.codec ==
+					VCD_CODEC_MPEG4) {
+					*(struct vcd_property_short_header
+					  *)property_value =
+						encoder->short_header;
+					vcd_status = VCD_S_SUCCESS;
+				} else {
+					vcd_status = VCD_ERR_ILLEGAL_OP;
+				}
+			}
+			break;
+		}
+	case VCD_I_ENTROPY_CTRL:
+		{
+			entropy_control = property_value;
+			if (sizeof(struct vcd_property_entropy_control) ==
+			    property_hdr->sz) {
+				if (encoder->codec.codec ==
+					VCD_CODEC_H264) {
+					*entropy_control =
+				     encoder->entropy_control;
+					vcd_status = VCD_S_SUCCESS;
+				} else {
+					vcd_status = VCD_ERR_ILLEGAL_OP;
+				}
+			}
+			break;
+		}
+	case VCD_I_DEBLOCKING:
+		{
+			if (sizeof(struct vcd_property_db_config) ==
+			    property_hdr->sz) {
+				if (encoder->codec.codec ==
+					VCD_CODEC_H264) {
+					*(struct vcd_property_db_config *)
+					    property_value =
+					    encoder->db_control;
+					vcd_status = VCD_S_SUCCESS;
+				} else {
+					vcd_status = VCD_ERR_ILLEGAL_OP;
+				}
+			}
+			break;
+		}
+	case VCD_I_INTRA_PERIOD:
+		{
+			if (sizeof(struct vcd_property_i_period) ==
+			    property_hdr->sz) {
+				*(struct vcd_property_i_period *)
+				    property_value = encoder->i_period;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_QP_RANGE:
+		{
+			if (sizeof(struct vcd_property_qp_range) ==
+			    property_hdr->sz) {
+				*(struct vcd_property_qp_range *)
+				    property_value = encoder->qp_range;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_SESSION_QP:
+		{
+			if (sizeof(struct vcd_property_session_qp) ==
+			    property_hdr->sz) {
+				*(struct vcd_property_session_qp *)
+				    property_value = encoder->session_qp;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_RC_LEVEL_CONFIG:
+		{
+			if (sizeof(struct vcd_property_rc_level) ==
+			    property_hdr->sz) {
+				*(struct vcd_property_rc_level *)
+				    property_value = encoder->rc_level;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_FRAME_LEVEL_RC:
+		{
+			if (sizeof
+			    (struct vcd_property_frame_level_rc_params) ==
+			    property_hdr->sz) {
+				*(struct vcd_property_frame_level_rc_params
+				 *)property_value =
+				 encoder->frame_level_rc;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_ADAPTIVE_RC:
+		{
+			if (sizeof(struct vcd_property_adaptive_rc_params)
+			    == property_hdr->sz) {
+				*(struct vcd_property_adaptive_rc_params *)
+				    property_value = encoder->adaptive_rc;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_INTRA_REFRESH:
+		{
+			intra_refresh = property_value;
+			if (sizeof
+			    (struct vcd_property_intra_refresh_mb_number)
+			    == property_hdr->sz) {
+				*intra_refresh = encoder->intra_refresh;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case DDL_I_INPUT_BUF_REQ:
+		{
+			if (sizeof(struct vcd_buffer_requirement) ==
+			    property_hdr->sz) {
+				*(struct vcd_buffer_requirement *)
+				    property_value =
+						encoder->client_input_buf_req;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case DDL_I_OUTPUT_BUF_REQ:
+		{
+			if (sizeof(struct vcd_buffer_requirement) ==
+			    property_hdr->sz) {
+				*(struct vcd_buffer_requirement *)
+				    property_value =
+						encoder->client_output_buf_req;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_BUFFER_FORMAT:
+		{
+			if (sizeof(struct vcd_property_buffer_format) ==
+			    property_hdr->sz) {
+				*(struct vcd_property_buffer_format *)
+				    property_value = encoder->buf_format;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case DDL_I_FRAME_PROC_UNITS:
+		{
+			if (sizeof(u32) == property_hdr->sz) {
+				*(u32 *) property_value =
+				    ((encoder->frame_size.width >> 4) *
+				     (encoder->frame_size.height >> 4)
+				    );
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_HEADER_EXTENSION:
+		{
+			if (sizeof(u32) == property_hdr->sz &&
+			    encoder->codec.codec == VCD_CODEC_MPEG4) {
+				*(u32 *) property_value =
+				    encoder->hdr_ext_control;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_METADATA_ENABLE:
+	case VCD_I_METADATA_HEADER:
+		{
+			vcd_status = ddl_get_metadata_params(
+						   ddl,
+						   property_hdr,
+						   property_value);
+			break;
+		}
+	default:
+		{
+			vcd_status = VCD_ERR_ILLEGAL_OP;
+			break;
+		}
+	}
+	return vcd_status;
+}
+
+static u32 ddl_set_enc_dynamic_property
+    (struct ddl_client_context *ddl,
+     struct vcd_property_hdr *property_hdr, void *property_value) {
+	struct ddl_encoder_data *encoder = &(ddl->codec_data.encoder);
+	u32 vcd_status = VCD_ERR_ILLEGAL_PARM, dynamic_prop_change = 0x0;
+	switch (property_hdr->prop_id) {
+	case VCD_I_REQ_IFRAME:
+		{
+			if (sizeof(struct vcd_property_req_i_frame) ==
+			    property_hdr->sz) {
+				dynamic_prop_change = DDL_ENC_REQ_IFRAME;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_TARGET_BITRATE:
+		{
+		    struct vcd_property_target_bitrate *bitrate =
+				(struct vcd_property_target_bitrate *)
+				property_value;
+			if (sizeof(struct vcd_property_target_bitrate) ==
+			 property_hdr->sz && bitrate->target_bitrate > 0
+			 && bitrate->target_bitrate <= DDL_MAX_BIT_RATE) {
+				encoder->target_bit_rate = *bitrate;
+				dynamic_prop_change = DDL_ENC_CHANGE_BITRATE;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_INTRA_PERIOD:
+		{
+			struct vcd_property_i_period *iperiod =
+				(struct vcd_property_i_period *)
+				property_value;
+			if (sizeof(struct vcd_property_i_period) ==
+				property_hdr->sz &&
+				!iperiod->b_frames) {
+				encoder->i_period = *iperiod;
+				dynamic_prop_change = DDL_ENC_CHANGE_IPERIOD;
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_FRAME_RATE:
+		{
+			struct vcd_property_frame_rate *frame_rate =
+			    (struct vcd_property_frame_rate *)
+			    property_value;
+			if (sizeof(struct vcd_property_frame_rate)
+			    == property_hdr->sz &&
+			    frame_rate->fps_denominator &&
+			    frame_rate->fps_numerator &&
+			    frame_rate->fps_denominator <=
+			    frame_rate->fps_numerator) {
+				encoder->frame_rate = *frame_rate;
+				dynamic_prop_change = DDL_ENC_CHANGE_FRAMERATE;
+				if (DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN) &&
+					(encoder->codec.codec != VCD_CODEC_MPEG4
+					 || encoder->short_header.short_header))
+					ddl_set_default_enc_vop_timing(encoder);
+				vcd_status = VCD_S_SUCCESS;
+			}
+			break;
+		}
+	case VCD_I_INTRA_REFRESH:
+		{
+			struct vcd_property_intra_refresh_mb_number
+				*intra_refresh_mbnum = (
+				struct vcd_property_intra_refresh_mb_number *)
+					property_value;
+			u32 frame_mbnum =
+				(encoder->frame_size.width >> 4) *
+				(encoder->frame_size.height >> 4);
+			if (sizeof(struct
+				vcd_property_intra_refresh_mb_number)
+				== property_hdr->sz &&
+				intra_refresh_mbnum->cir_mb_number <=
+				frame_mbnum) {
+				encoder->intra_refresh =
+					*intra_refresh_mbnum;
+				dynamic_prop_change = DDL_ENC_CHANGE_CIR;
+				vcd_status = VCD_S_SUCCESS;
+			}
+
+			break;
+		}
+	default:
+		{
+			vcd_status = VCD_ERR_ILLEGAL_OP;
+			break;
+		}
+	}
+	if (vcd_status == VCD_S_SUCCESS &&
+	(DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME) ||
+	DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME_DONE)))
+		encoder->dynamic_prop_change |= dynamic_prop_change;
+	return vcd_status;
+}
+
+void ddl_set_default_dec_property(struct ddl_client_context *ddl)
+{
+	struct ddl_decoder_data *decoder = &(ddl->codec_data.decoder);
+
+	if (decoder->codec.codec >= VCD_CODEC_MPEG2 &&
+		decoder->codec.codec <=  VCD_CODEC_XVID)
+		decoder->post_filter.post_filter = true;
+	else
+		decoder->post_filter.post_filter = false;
+	decoder->buf_format.buffer_format = VCD_BUFFER_FORMAT_NV12;
+	decoder->client_frame_size.height = 144;
+	decoder->client_frame_size.width = 176;
+	decoder->client_frame_size.stride = 176;
+	decoder->client_frame_size.scan_lines = 144;
+	decoder->progressive_only = 1;
+	decoder->idr_only_decoding = 0;
+	decoder->profile.profile = VCD_PROFILE_UNKNOWN;
+	decoder->level.level = VCD_LEVEL_UNKNOWN;
+	decoder->output_order = VCD_DEC_ORDER_DISPLAY;
+	ddl_set_default_metadata_flag(ddl);
+	ddl_set_default_decoder_buffer_req(decoder, true);
+}
+
+static void ddl_set_default_enc_property(struct ddl_client_context *ddl)
+{
+	struct ddl_encoder_data *encoder = &(ddl->codec_data.encoder);
+
+	ddl_set_default_enc_profile(encoder);
+	ddl_set_default_enc_level(encoder);
+
+	encoder->rc.rate_control = VCD_RATE_CONTROL_VBR_VFR;
+	ddl_set_default_enc_rc_params(encoder);
+
+	ddl_set_default_enc_intra_period(encoder);
+
+	encoder->intra_refresh.cir_mb_number = 0;
+	ddl_set_default_enc_vop_timing(encoder);
+
+	encoder->multi_slice.m_slice_sel = VCD_MSLICE_OFF;
+	encoder->multi_slice.m_slice_size = 0;
+	encoder->short_header.short_header = false;
+
+	encoder->entropy_control.entropy_sel = VCD_ENTROPY_SEL_CAVLC;
+	encoder->entropy_control.cabac_model = VCD_CABAC_MODEL_NUMBER_0;
+	encoder->db_control.db_config = VCD_DB_ALL_BLOCKING_BOUNDARY;
+	encoder->db_control.slice_alpha_offset = 0;
+	encoder->db_control.slice_beta_offset = 0;
+
+	encoder->re_con_buf_format.buffer_format =
+		VCD_BUFFER_FORMAT_TILE_4x2;
+
+	encoder->buf_format.buffer_format = VCD_BUFFER_FORMAT_NV12;
+
+	encoder->hdr_ext_control = 0;
+
+	ddl_set_default_metadata_flag(ddl);
+
+	ddl_set_default_encoder_buffer_req(encoder);
+}
+
+static void ddl_set_default_enc_profile(struct ddl_encoder_data *encoder)
+{
+	enum vcd_codec codec = encoder->codec.codec;
+	if (codec == VCD_CODEC_MPEG4)
+		encoder->profile.profile = VCD_PROFILE_MPEG4_SP;
+	else if (codec == VCD_CODEC_H264)
+		encoder->profile.profile = VCD_PROFILE_H264_BASELINE;
+	else
+		encoder->profile.profile = VCD_PROFILE_H263_BASELINE;
+}
+
+static void ddl_set_default_enc_level(struct ddl_encoder_data *encoder)
+{
+	enum vcd_codec codec = encoder->codec.codec;
+	if (codec == VCD_CODEC_MPEG4)
+		encoder->level.level = VCD_LEVEL_MPEG4_1;
+	else if (codec == VCD_CODEC_H264)
+		encoder->level.level = VCD_LEVEL_H264_1;
+	else
+		encoder->level.level = VCD_LEVEL_H263_10;
+}
+
+static void ddl_set_default_enc_vop_timing
+    (struct ddl_encoder_data *encoder)
+{
+	if (encoder->codec.codec == VCD_CODEC_MPEG4)
+		encoder->vop_timing.vop_time_resolution =
+		    (2 * encoder->frame_rate.fps_numerator) /
+		    encoder->frame_rate.fps_denominator;
+	else
+		encoder->vop_timing.vop_time_resolution = 0x7530;
+}
+
+static void ddl_set_default_enc_intra_period(
+		struct ddl_encoder_data *encoder)
+{
+	switch (encoder->rc.rate_control) {
+	default:
+	case VCD_RATE_CONTROL_VBR_VFR:
+	case VCD_RATE_CONTROL_VBR_CFR:
+	case VCD_RATE_CONTROL_CBR_VFR:
+	case VCD_RATE_CONTROL_OFF:
+		{
+			encoder->i_period.p_frames =
+			    ((encoder->frame_rate.fps_numerator << 1) /
+			     encoder->frame_rate.fps_denominator) - 1;
+			break;
+		}
+	case VCD_RATE_CONTROL_CBR_CFR:
+		{
+			encoder->i_period.p_frames =
+			    ((encoder->frame_rate.fps_numerator >> 1) /
+			     encoder->frame_rate.fps_denominator) - 1;
+			break;
+		}
+	}
+	encoder->i_period.b_frames = 0;
+}
+
+static void ddl_set_default_enc_rc_params(
+		struct ddl_encoder_data *encoder)
+{
+	enum vcd_codec codec = encoder->codec.codec;
+
+	encoder->rc_level.frame_level_rc = true;
+	encoder->qp_range.min_qp = 0x1;
+
+	if (codec == VCD_CODEC_H264) {
+		encoder->qp_range.max_qp = 0x33;
+		encoder->session_qp.i_frame_qp = 0x14;
+		encoder->session_qp.p_frame_qp = 0x14;
+
+		encoder->rc_level.mb_level_rc = true;
+		encoder->adaptive_rc.activity_region_flag = true;
+		encoder->adaptive_rc.dark_region_as_flag = true;
+		encoder->adaptive_rc.smooth_region_as_flag = true;
+		encoder->adaptive_rc.static_region_as_flag = true;
+	} else {
+		encoder->qp_range.max_qp = 0x1f;
+		encoder->session_qp.i_frame_qp = 0xd;
+		encoder->session_qp.p_frame_qp = 0xd;
+		encoder->rc_level.mb_level_rc = false;
+	}
+
+	switch (encoder->rc.rate_control) {
+	default:
+	case VCD_RATE_CONTROL_VBR_VFR:
+		{
+			encoder->r_cframe_skip = 1;
+			encoder->frame_level_rc.reaction_coeff = 0x1f4;
+			break;
+		}
+	case VCD_RATE_CONTROL_VBR_CFR:
+		{
+			encoder->r_cframe_skip = 0;
+			encoder->frame_level_rc.reaction_coeff = 0x1f4;
+			break;
+		}
+	case VCD_RATE_CONTROL_CBR_VFR:
+		{
+			encoder->r_cframe_skip = 1;
+			if (codec != VCD_CODEC_H264) {
+				encoder->session_qp.i_frame_qp = 0xf;
+				encoder->session_qp.p_frame_qp = 0xf;
+			}
+
+			encoder->frame_level_rc.reaction_coeff = 0x14;
+			break;
+		}
+	case VCD_RATE_CONTROL_CBR_CFR:
+		{
+			encoder->r_cframe_skip = 0;
+			encoder->frame_level_rc.reaction_coeff = 0x6;
+			break;
+		}
+	case VCD_RATE_CONTROL_OFF:
+		{
+			encoder->r_cframe_skip = 0;
+			encoder->rc_level.frame_level_rc = false;
+			encoder->rc_level.mb_level_rc = false;
+			break;
+		}
+	}
+}
+
+void ddl_set_default_encoder_buffer_req(struct ddl_encoder_data *encoder)
+{
+	u32 y_cb_cr_size;
+
+	y_cb_cr_size = ddl_get_yuv_buffer_size(&encoder->frame_size,
+		&encoder->buf_format, false, encoder->codec.codec);
+
+	memset(&encoder->input_buf_req, 0,
+	       sizeof(struct vcd_buffer_requirement));
+
+	encoder->input_buf_req.min_count = 1;
+	encoder->input_buf_req.actual_count =
+	    encoder->input_buf_req.min_count + 8;
+	encoder->input_buf_req.max_count = DDL_MAX_BUFFER_COUNT;
+	encoder->input_buf_req.sz = y_cb_cr_size;
+	encoder->input_buf_req.align = DDL_LINEAR_BUFFER_ALIGN_BYTES;
+
+	encoder->client_input_buf_req = encoder->input_buf_req;
+
+	memset(&encoder->output_buf_req, 0,
+	       sizeof(struct vcd_buffer_requirement));
+
+	encoder->output_buf_req.min_count = 2;
+	encoder->output_buf_req.actual_count =
+	    encoder->output_buf_req.min_count + 3;
+	encoder->output_buf_req.max_count = DDL_MAX_BUFFER_COUNT;
+	encoder->output_buf_req.align = DDL_LINEAR_BUFFER_ALIGN_BYTES;
+	encoder->output_buf_req.sz = y_cb_cr_size;
+	ddl_set_default_encoder_metadata_buffer_size(encoder);
+	encoder->client_output_buf_req = encoder->output_buf_req;
+}
+
+void ddl_set_default_decoder_buffer_req(struct ddl_decoder_data *decoder,
+		u32 estimate)
+{
+	u32 y_cb_cr_size, min_dpb, num_mb;
+	struct vcd_property_frame_size  *frame_size;
+	struct vcd_buffer_requirement *output_buf_req, *input_buf_req;
+
+	if (!decoder->codec.codec)
+		return;
+
+	if (estimate) {
+		frame_size = &decoder->client_frame_size;
+		output_buf_req = &decoder->client_output_buf_req;
+		input_buf_req = &decoder->client_input_buf_req;
+		min_dpb = ddl_decoder_min_num_dpb(decoder);
+		 y_cb_cr_size = ddl_get_yuv_buffer_size(frame_size,
+			&decoder->buf_format, (!decoder->progressive_only),
+			decoder->codec.codec);
+	} else {
+		frame_size = &decoder->frame_size;
+		output_buf_req = &decoder->actual_output_buf_req;
+		input_buf_req = &decoder->actual_input_buf_req;
+		y_cb_cr_size = decoder->y_cb_cr_size;
+		min_dpb = decoder->min_dpb_num;
+	}
+
+	if (decoder->idr_only_decoding)
+		min_dpb = 1;
+
+	memset(output_buf_req, 0, sizeof(struct vcd_buffer_requirement));
+
+	output_buf_req->min_count = min_dpb;
+
+	num_mb = DDL_NO_OF_MB(frame_size->width, frame_size->height);
+	if (decoder->idr_only_decoding) {
+		output_buf_req->actual_count = output_buf_req->min_count;
+	} else {
+		if (num_mb >= DDL_WVGA_MBS) {
+			output_buf_req->actual_count = min_dpb + 2;
+			if (output_buf_req->actual_count < 10)
+				output_buf_req->actual_count = 10;
+		} else
+			output_buf_req->actual_count = min_dpb + 5;
+	}
+	output_buf_req->max_count = DDL_MAX_BUFFER_COUNT;
+	output_buf_req->sz = y_cb_cr_size;
+	if (decoder->buf_format.buffer_format != VCD_BUFFER_FORMAT_NV12)
+		output_buf_req->align = DDL_TILE_BUFFER_ALIGN_BYTES;
+	else
+		output_buf_req->align = DDL_LINEAR_BUFFER_ALIGN_BYTES;
+
+	ddl_set_default_decoder_metadata_buffer_size(decoder,
+		frame_size, output_buf_req);
+
+	decoder->min_output_buf_req = *output_buf_req;
+
+	memset(input_buf_req, 0, sizeof(struct vcd_buffer_requirement));
+
+	input_buf_req->min_count = 1;
+	input_buf_req->actual_count = input_buf_req->min_count + 3;
+	input_buf_req->max_count = DDL_MAX_BUFFER_COUNT;
+	input_buf_req->sz = (1280*720*3*3) >> 3;
+	input_buf_req->align = DDL_LINEAR_BUFFER_ALIGN_BYTES;
+
+	decoder->min_input_buf_req = *input_buf_req;
+
+}
+
+u32 ddl_get_yuv_buffer_size(struct vcd_property_frame_size *frame_size,
+     struct vcd_property_buffer_format *buf_format, u32 inter_lace,
+     enum vcd_codec codec)
+{
+	struct vcd_property_frame_size frame_sz = *frame_size;
+	u32 total_memory_size;
+	ddl_calculate_stride(&frame_sz, inter_lace, codec);
+
+	if (buf_format->buffer_format != VCD_BUFFER_FORMAT_NV12) {
+		u32 component_mem_size;
+		u32 width_round_up;
+		u32 height_round_up;
+		u32 height_chroma = (frame_sz.scan_lines >> 1);
+
+		width_round_up =
+		    DDL_TILE_ALIGN(frame_sz.stride, DDL_TILE_ALIGN_WIDTH);
+		height_round_up =
+		    DDL_TILE_ALIGN(frame_sz.scan_lines, DDL_TILE_ALIGN_HEIGHT);
+
+		component_mem_size = width_round_up * height_round_up;
+		component_mem_size = DDL_TILE_ALIGN(component_mem_size,
+						      DDL_TILE_MULTIPLY_FACTOR);
+
+		total_memory_size = ((component_mem_size +
+					 DDL_TILE_BUF_ALIGN_GUARD_BYTES) &
+					DDL_TILE_BUF_ALIGN_MASK);
+
+		height_round_up =
+		    DDL_TILE_ALIGN(height_chroma, DDL_TILE_ALIGN_HEIGHT);
+		component_mem_size = width_round_up * height_round_up;
+		component_mem_size = DDL_TILE_ALIGN(component_mem_size,
+						      DDL_TILE_MULTIPLY_FACTOR);
+		total_memory_size += component_mem_size;
+	} else {
+		total_memory_size = frame_sz.scan_lines * frame_sz.stride;
+		total_memory_size += (total_memory_size >> 1);
+	}
+	return total_memory_size;
+}
+
+void ddl_calculate_stride(struct vcd_property_frame_size *frame_size,
+	u32 interlace, enum vcd_codec codec)
+{
+	frame_size->stride = ((frame_size->width + 15) >> 4) << 4;
+	if (!interlace || codec == VCD_CODEC_MPEG4 ||
+		codec == VCD_CODEC_DIVX_4 ||
+		codec == VCD_CODEC_DIVX_5 ||
+		codec == VCD_CODEC_DIVX_6 ||
+		codec == VCD_CODEC_XVID) {
+		frame_size->scan_lines =
+			((frame_size->height + 15) >> 4) << 4;
+	} else {
+		frame_size->scan_lines =
+			((frame_size->height + 31) >> 5) << 5;
+	}
+
+}
+
+static u32 ddl_valid_buffer_requirement
+	(struct vcd_buffer_requirement *original_buf_req,
+	struct vcd_buffer_requirement *req_buf_req)
+{
+	u32 status = false;
+	if (original_buf_req->max_count >= req_buf_req->actual_count &&
+		original_buf_req->min_count <= req_buf_req->actual_count &&
+		original_buf_req->align <= req_buf_req->align &&
+		original_buf_req->sz <= req_buf_req->sz) {
+		status = true;
+	} else {
+		VIDC_LOGERR_STRING("ddl_valid_buf_req:Failed");
+	}
+	return status;
+}
+
+static u32 ddl_decoder_min_num_dpb(struct ddl_decoder_data *decoder)
+{
+	u32 min_dpb = 0, yuv_size = 0;
+	struct vcd_property_frame_size frame_sz = decoder->client_frame_size;
+	switch (decoder->codec.codec) {
+	default:
+	case VCD_CODEC_MPEG4:
+	case VCD_CODEC_MPEG2:
+	case VCD_CODEC_DIVX_4:
+	case VCD_CODEC_DIVX_5:
+	case VCD_CODEC_DIVX_6:
+	case VCD_CODEC_XVID:
+		{
+			min_dpb = 3;
+			break;
+		}
+	case VCD_CODEC_H263:
+		{
+			min_dpb = 2;
+			break;
+		}
+	case VCD_CODEC_VC1:
+	case VCD_CODEC_VC1_RCV:
+		{
+			min_dpb = 4;
+			break;
+		}
+	case VCD_CODEC_H264:
+		{
+			ddl_calculate_stride(&frame_sz,
+				!decoder->progressive_only,
+				decoder->codec.codec);
+			yuv_size =
+			    ((frame_sz.scan_lines *
+			      frame_sz.stride * 3) >> 1);
+			min_dpb = 6912000 / yuv_size;
+			if (min_dpb > 16)
+				min_dpb = 16;
+
+			min_dpb += 2;
+			break;
+		}
+	}
+	return min_dpb;
+}
+
+static u32 ddl_set_dec_buffers
+    (struct ddl_decoder_data *decoder,
+     struct ddl_property_dec_pic_buffers *dpb) {
+	u32 vcd_status = VCD_S_SUCCESS;
+	u32 loopc;
+	for (loopc = 0; !vcd_status &&
+	     loopc < dpb->no_of_dec_pic_buf; ++loopc) {
+		if ((!DDL_ADDR_IS_ALIGNED
+		     (dpb->dec_pic_buffers[loopc].vcd_frm.physical,
+		      decoder->client_output_buf_req.align)
+		    )
+		    || (dpb->dec_pic_buffers[loopc].vcd_frm.alloc_len <
+			decoder->client_output_buf_req.sz)
+		    ) {
+			vcd_status = VCD_ERR_ILLEGAL_PARM;
+		}
+	}
+	if (vcd_status) {
+		VIDC_LOGERR_STRING
+		    ("ddl_set_prop:Dpb_align_fail_or_alloc_size_small");
+		return vcd_status;
+	}
+	if (decoder->dp_buf.no_of_dec_pic_buf) {
+		DDL_FREE(decoder->dp_buf.dec_pic_buffers);
+		decoder->dp_buf.no_of_dec_pic_buf = 0;
+	}
+	decoder->dp_buf.dec_pic_buffers =
+	    DDL_MALLOC(dpb->no_of_dec_pic_buf *
+		       sizeof(struct ddl_frame_data_tag));
+
+	if (!decoder->dp_buf.dec_pic_buffers) {
+		VIDC_LOGERR_STRING
+		    ("ddl_dec_set_prop:Dpb_container_alloc_failed");
+		return VCD_ERR_ALLOC_FAIL;
+	}
+	decoder->dp_buf.no_of_dec_pic_buf = dpb->no_of_dec_pic_buf;
+	for (loopc = 0; loopc < dpb->no_of_dec_pic_buf; ++loopc) {
+		decoder->dp_buf.dec_pic_buffers[loopc] =
+		    dpb->dec_pic_buffers[loopc];
+	}
+	decoder->dpb_mask.client_mask = 0;
+	decoder->dpb_mask.hw_mask = 0;
+	decoder->dynamic_prop_change = 0;
+	return VCD_S_SUCCESS;
+}
+
+void ddl_set_initial_default_values(struct ddl_client_context *ddl)
+{
+	if (ddl->decoding) {
+		ddl->codec_data.decoder.codec.codec = VCD_CODEC_MPEG4;
+		vcd_fw_transact(true, true,
+			ddl->codec_data.decoder.codec.codec);
+		ddl_set_default_dec_property(ddl);
+	} else {
+		struct ddl_encoder_data *encoder =
+		    &(ddl->codec_data.encoder);
+		encoder->codec.codec = VCD_CODEC_MPEG4;
+		vcd_fw_transact(true, false,
+			encoder->codec.codec);
+
+		encoder->target_bit_rate.target_bitrate = 64000;
+		encoder->frame_size.width = 176;
+		encoder->frame_size.height = 144;
+		encoder->frame_size.stride = 176;
+		encoder->frame_size.scan_lines = 144;
+		encoder->frame_rate.fps_numerator = 30;
+		encoder->frame_rate.fps_denominator = 1;
+		ddl_set_default_enc_property(ddl);
+	}
+
+	return;
+}
diff --git a/drivers/video/msm/vidc/720p/ddl/vcd_ddl_utils.c b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_utils.c
new file mode 100644
index 0000000..aa0d4b8
--- /dev/null
+++ b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_utils.c
@@ -0,0 +1,242 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/memory_alloc.h>
+#include <media/msm/vidc_type.h>
+#include "vcd_ddl_utils.h"
+
+#if DEBUG
+#define DBG(x...) printk(KERN_DEBUG x)
+#else
+#define DBG(x...)
+#endif
+
+#define DBG_TIME(x...) printk(KERN_DEBUG x)
+#define ERR(x...) printk(KERN_ERR x)
+
+struct time_data {
+	unsigned int ddl_t1;
+	unsigned int ddl_ttotal;
+	unsigned int ddl_count;
+};
+
+static struct time_data proc_time[MAX_TIME_DATA];
+
+#ifdef NO_IN_KERNEL_PMEM
+
+void ddl_pmem_alloc(struct ddl_buf_addr *buff_addr, size_t sz, u32 align)
+{
+	u32 guard_bytes, align_mask;
+	u32 physical_addr, align_offset;
+	dma_addr_t phy_addr;
+
+	if (align == DDL_LINEAR_BUFFER_ALIGN_BYTES) {
+
+		guard_bytes = 31;
+		align_mask = 0xFFFFFFE0U;
+
+	} else {
+
+		guard_bytes = DDL_TILE_BUF_ALIGN_GUARD_BYTES;
+		align_mask = DDL_TILE_BUF_ALIGN_MASK;
+	}
+
+	buff_addr->virtual_base_addr =
+		kmalloc((sz + guard_bytes), GFP_KERNEL);
+
+	if (!buff_addr->virtual_base_addr) {
+		ERR("\n ERROR %s:%u kamlloc fails to allocate"
+			" sz + guard_bytes = %u\n", __func__, __LINE__,
+			(sz + guard_bytes));
+		return;
+	}
+
+	phy_addr = dma_map_single(NULL, buff_addr->virtual_base_addr,
+				  sz + guard_bytes, DMA_TO_DEVICE);
+
+	buff_addr->buffer_size = sz;
+	physical_addr = (u32) phy_addr;
+	buff_addr->align_physical_addr =
+	    (u32 *) ((physical_addr + guard_bytes) & align_mask);
+	align_offset =
+	    (u32) (buff_addr->align_physical_addr) - physical_addr;
+	buff_addr->align_virtual_addr =
+	    (u32 *) ((u32) (buff_addr->virtual_base_addr)
+		     + align_offset);
+}
+
+void ddl_pmem_free(struct ddl_buf_addr *buff_addr)
+{
+	kfree(buff_addr->virtual_base_addr);
+	buff_addr->buffer_size = 0;
+	buff_addr->virtual_base_addr = NULL;
+}
+
+#else
+
+void ddl_pmem_alloc(struct ddl_buf_addr *buff_addr, size_t sz, u32 align)
+{
+	u32 guard_bytes, align_mask;
+	u32 physical_addr;
+	u32 align_offset;
+	u32 alloc_size, flags = 0;
+	struct ddl_context *ddl_context;
+	struct msm_mapped_buffer *mapped_buffer = NULL;
+
+	if (!buff_addr) {
+		ERR("\n%s() Invalid Parameters", __func__);
+		return;
+	}
+
+	DBG_PMEM("\n%s() IN: Requested alloc size(%u)", __func__, (u32)sz);
+
+	if (align == DDL_LINEAR_BUFFER_ALIGN_BYTES) {
+
+		guard_bytes = 31;
+		align_mask = 0xFFFFFFE0U;
+
+	} else {
+
+		guard_bytes = DDL_TILE_BUF_ALIGN_GUARD_BYTES;
+		align_mask = DDL_TILE_BUF_ALIGN_MASK;
+	}
+	ddl_context = ddl_get_context();
+	alloc_size = sz + guard_bytes;
+
+	physical_addr = (u32)
+		allocate_contiguous_memory_nomap(alloc_size,
+					ddl_context->memtype, SZ_4K);
+
+	if (!physical_addr) {
+		pr_err("%s(): could not allocate kernel pmem buffers\n",
+		       __func__);
+		goto bailout;
+	}
+	buff_addr->physical_base_addr = (u32 *) physical_addr;
+	flags = MSM_SUBSYSTEM_MAP_KADDR;
+	buff_addr->mapped_buffer =
+	msm_subsystem_map_buffer((unsigned long)physical_addr,
+	alloc_size, flags, NULL, 0);
+	if (IS_ERR(buff_addr->mapped_buffer)) {
+		pr_err(" %s() buffer map failed", __func__);
+		goto free_acm_alloc;
+	}
+	mapped_buffer = buff_addr->mapped_buffer;
+	if (!mapped_buffer->vaddr) {
+		pr_err("%s() mapped virtual address is NULL", __func__);
+		goto free_map_buffers;
+	}
+	buff_addr->virtual_base_addr = mapped_buffer->vaddr;
+	memset(buff_addr->virtual_base_addr, 0 , sz + guard_bytes);
+	buff_addr->buffer_size = sz;
+
+	buff_addr->align_physical_addr =
+	    (u32 *) ((physical_addr + guard_bytes) & align_mask);
+
+	align_offset =
+	    (u32) (buff_addr->align_physical_addr) - physical_addr;
+
+	buff_addr->align_virtual_addr =
+	    (u32 *) ((u32) (buff_addr->virtual_base_addr)
+		     + align_offset);
+
+	DBG_PMEM("\n%s() OUT: phy_addr(%p) ker_addr(%p) size(%u)", __func__,
+		buff_addr->physical_base_addr, buff_addr->virtual_base_addr,
+		buff_addr->buffer_size);
+
+	return;
+free_map_buffers:
+	msm_subsystem_unmap_buffer(buff_addr->mapped_buffer);
+free_acm_alloc:
+	free_contiguous_memory_by_paddr(
+		(unsigned long) physical_addr);
+bailout:
+	buff_addr->physical_base_addr = NULL;
+	buff_addr->virtual_base_addr = NULL;
+	buff_addr->buffer_size = 0;
+	buff_addr->mapped_buffer = NULL;
+}
+
+void ddl_pmem_free(struct ddl_buf_addr *buff_addr)
+{
+	if (!buff_addr) {
+		ERR("\n %s() invalid arguments %p", __func__, buff_addr);
+		return;
+	}
+	DBG_PMEM("\n%s() IN: phy_addr(%p) ker_addr(%p) size(%u)", __func__,
+		buff_addr->physical_base_addr, buff_addr->virtual_base_addr,
+		buff_addr->buffer_size);
+
+	if (buff_addr->mapped_buffer)
+		msm_subsystem_unmap_buffer(buff_addr->mapped_buffer);
+	if (buff_addr->physical_base_addr)
+		free_contiguous_memory_by_paddr(
+			(unsigned long) buff_addr->physical_base_addr);
+	DBG_PMEM("\n%s() OUT: phy_addr(%p) ker_addr(%p) size(%u)", __func__,
+		buff_addr->physical_base_addr, buff_addr->virtual_base_addr,
+		buff_addr->buffer_size);
+	buff_addr->buffer_size = 0;
+	buff_addr->physical_base_addr = NULL;
+	buff_addr->virtual_base_addr = NULL;
+	buff_addr->mapped_buffer = NULL;
+}
+#endif
+
+void ddl_set_core_start_time(const char *func_name, u32 index)
+{
+	u32 act_time;
+	struct timeval ddl_tv;
+	struct time_data *time_data = &proc_time[index];
+	do_gettimeofday(&ddl_tv);
+	act_time = (ddl_tv.tv_sec * 1000) + (ddl_tv.tv_usec / 1000);
+	if (!time_data->ddl_t1) {
+		time_data->ddl_t1 = act_time;
+		DBG("\n%s(): Start Time (%u)", func_name, act_time);
+	} else {
+		DBG_TIME("\n%s(): Timer already started! St(%u) Act(%u)",
+			func_name, time_data->ddl_t1, act_time);
+	}
+}
+
+void ddl_calc_core_proc_time(const char *func_name, u32 index)
+{
+	struct time_data *time_data = &proc_time[index];
+	if (time_data->ddl_t1) {
+		int ddl_t2;
+		struct timeval ddl_tv;
+		do_gettimeofday(&ddl_tv);
+		ddl_t2 = (ddl_tv.tv_sec * 1000) + (ddl_tv.tv_usec / 1000);
+		time_data->ddl_ttotal += (ddl_t2 - time_data->ddl_t1);
+		time_data->ddl_count++;
+		DBG_TIME("\n%s(): cnt(%u) Diff(%u) Avg(%u)",
+			func_name, time_data->ddl_count,
+			ddl_t2 - time_data->ddl_t1,
+			time_data->ddl_ttotal/time_data->ddl_count);
+		time_data->ddl_t1 = 0;
+	}
+}
+
+void ddl_reset_core_time_variables(u32 index)
+{
+	proc_time[index].ddl_t1 = 0;
+	proc_time[index].ddl_ttotal = 0;
+	proc_time[index].ddl_count = 0;
+}
+int ddl_get_core_decode_proc_time(u32 *ddl_handle)
+{
+	return 0;
+}
+
+void ddl_reset_avg_dec_time(u32 *ddl_handle)
+{
+	return;
+}
diff --git a/drivers/video/msm/vidc/720p/ddl/vcd_ddl_utils.h b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_utils.h
new file mode 100644
index 0000000..59bb620
--- /dev/null
+++ b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_utils.h
@@ -0,0 +1,60 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef _VCD_DDL_UTILS_H_
+#define _VCD_DDL_UTILS_H_
+
+#include "vcd_ddl_core.h"
+#include "vcd_ddl.h"
+
+extern u32 vidc_msg_pmem;
+extern u32 vidc_msg_timing;
+
+enum timing_data {
+	DEC_OP_TIME,
+	DEC_IP_TIME,
+	ENC_OP_TIME,
+	MAX_TIME_DATA
+};
+
+#define DDL_INLINE
+
+#define DDL_ALIGN_SIZE(sz, guard_bytes, align_mask) \
+  (((u32)(sz) + guard_bytes) & align_mask)
+
+#define DDL_MALLOC(x)  kmalloc(x, GFP_KERNEL)
+#define DDL_FREE(x)   { if ((x)) kfree((x)); (x) = NULL; }
+
+#define DBG_PMEM(x...) \
+do { \
+	if (vidc_msg_pmem) \
+		printk(KERN_DEBUG x); \
+} while (0)
+
+void ddl_set_core_start_time(const char *func_name, u32 index);
+
+void ddl_calc_core_proc_time(const char *func_name, u32 index);
+
+void ddl_reset_core_time_variables(u32 index);
+
+int ddl_get_core_decode_proc_time(u32 *ddl_handle);
+
+void ddl_reset_avg_dec_time(u32 *ddl_handle);
+
+#define DDL_ASSERT(x)
+#define DDL_MEMSET(src, value, len) memset((src), (value), (len))
+#define DDL_MEMCPY(dest, src, len)  memcpy((dest), (src), (len))
+
+#define DDL_ADDR_IS_ALIGNED(addr, align_bytes) \
+(!((u32)(addr) & ((align_bytes) - 1)))
+
+#endif
diff --git a/drivers/video/msm/vidc/720p/ddl/vidc.c b/drivers/video/msm/vidc/720p/ddl/vidc.c
new file mode 100644
index 0000000..de6cbbb
--- /dev/null
+++ b/drivers/video/msm/vidc/720p/ddl/vidc.c
@@ -0,0 +1,804 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/unistd.h>
+#include <media/msm/vidc_type.h>
+#include "vidc.h"
+
+#if DEBUG
+#define DBG(x...) printk(KERN_DEBUG x)
+#else
+#define DBG(x...)
+#endif
+
+#define VIDC_720P_VERSION_STRING "VIDC_V1.0"
+u8 *vidc_base_addr;
+
+#ifdef VIDC_REGISTER_LOG_INTO_BUFFER
+char vidclog[VIDC_REGLOG_BUFSIZE];
+unsigned int vidclog_index;
+#endif
+
+void vidc_720p_set_device_virtual_base(u8 *core_virtual_base_addr)
+{
+	vidc_base_addr = core_virtual_base_addr;
+}
+
+void vidc_720p_init(char **ppsz_version, u32 i_firmware_size,
+		     u32 *pi_firmware_address,
+		     enum vidc_720p_endian dma_endian,
+		     u32 interrupt_off,
+		     enum vidc_720p_interrupt_level_selection
+		     interrupt_sel, u32 interrupt_mask)
+{
+	if (ppsz_version)
+		*ppsz_version = VIDC_720P_VERSION_STRING;
+
+	if (interrupt_sel == VIDC_720P_INTERRUPT_LEVEL_SEL)
+		VIDC_IO_OUT(REG_491082, 0);
+	else
+		VIDC_IO_OUT(REG_491082, 1);
+
+	if (interrupt_off)
+		VIDC_IO_OUT(REG_609676, 1);
+	else
+		VIDC_IO_OUT(REG_609676, 0);
+
+	VIDC_IO_OUT(REG_614776, 1);
+
+	VIDC_IO_OUT(REG_418173, 0);
+
+	VIDC_IO_OUT(REG_418173, interrupt_mask);
+
+	VIDC_IO_OUT(REG_736316, dma_endian);
+
+	VIDC_IO_OUT(REG_215724, 0);
+
+	VIDC_IO_OUT(REG_361582, 1);
+
+	VIDC_IO_OUT(REG_591577, i_firmware_size);
+
+	VIDC_IO_OUT(REG_203921, pi_firmware_address);
+
+	VIDC_IO_OUT(REG_531515_ADDR, 0);
+
+	VIDC_IO_OUT(REG_614413, 1);
+}
+
+u32 vidc_720p_do_sw_reset(void)
+{
+
+	u32 fw_start = 0;
+	VIDC_BUSY_WAIT(5);
+	VIDC_IO_OUT(REG_224135, 0);
+	VIDC_BUSY_WAIT(5);
+	VIDC_IO_OUT(REG_193553, 0);
+	VIDC_BUSY_WAIT(5);
+	VIDC_IO_OUT(REG_141269, 1);
+	VIDC_BUSY_WAIT(15);
+	VIDC_IO_OUT(REG_141269, 0);
+	VIDC_BUSY_WAIT(5);
+	VIDC_IO_IN(REG_193553, &fw_start);
+
+	if (!fw_start) {
+		DBG("\n VIDC-SW-RESET-FAILS!");
+		return false;
+	}
+	return true;
+}
+
+u32 vidc_720p_reset_is_success()
+{
+	u32 stagecounter = 0;
+	VIDC_IO_IN(REG_352831, &stagecounter);
+	stagecounter &= 0xff;
+	if (stagecounter != 0xe5) {
+		DBG("\n VIDC-CPU_RESET-FAILS!");
+		VIDC_IO_OUT(REG_224135, 0);
+		msleep(10);
+		return false;
+	}
+	return true;
+}
+
+void vidc_720p_start_cpu(enum vidc_720p_endian dma_endian,
+						  u32 *icontext_bufferstart,
+						  u32 *debug_core_dump_addr,
+						  u32  debug_buffer_size)
+{
+	u32 dbg_info_input0_reg = 0x1;
+	VIDC_IO_OUT(REG_361582, 0);
+	VIDC_IO_OUT(REG_958768, icontext_bufferstart);
+	VIDC_IO_OUT(REG_736316, dma_endian);
+	if (debug_buffer_size) {
+		dbg_info_input0_reg = (debug_buffer_size << 0x10)
+			| (0x2 << 1) | 0x1;
+		VIDC_IO_OUT(REG_166247, debug_core_dump_addr);
+	}
+	VIDC_IO_OUT(REG_699747, dbg_info_input0_reg);
+	VIDC_IO_OUT(REG_224135, 1);
+}
+
+u32 vidc_720p_cpu_start()
+{
+	u32 fw_status = 0x0;
+	VIDC_IO_IN(REG_381535, &fw_status);
+	if (fw_status != 0x02)
+		return false;
+	return true;
+}
+
+
+void vidc_720p_stop_fw(void)
+{
+   VIDC_IO_OUT(REG_193553, 0);
+   VIDC_IO_OUT(REG_224135, 0);
+}
+
+void vidc_720p_get_interrupt_status(u32 *interrupt_status,
+	u32 *cmd_err_status, u32 *disp_pic_err_status, u32 *op_failed)
+{
+	u32 err_status;
+	VIDC_IO_IN(REG_512143, interrupt_status);
+	VIDC_IO_IN(REG_300310, &err_status);
+	*cmd_err_status = err_status & 0xffff;
+	*disp_pic_err_status = (err_status & 0xffff0000) >> 16;
+	VIDC_IO_INF(REG_724381, OPERATION_FAILED, \
+				 op_failed);
+}
+
+void vidc_720p_interrupt_done_clear(void)
+{
+	VIDC_IO_OUT(REG_614776, 1);
+	VIDC_IO_OUT(REG_97293, 4);
+}
+
+void vidc_720p_submit_command(u32 ch_id, u32 cmd_id)
+{
+	u32 fw_status;
+	VIDC_IO_OUT(REG_97293, ch_id);
+	VIDC_IO_OUT(REG_62325, cmd_id);
+	VIDC_DEBUG_REGISTER_LOG;
+	VIDC_IO_IN(REG_381535, &fw_status);
+	VIDC_IO_OUT(REG_926519, fw_status);
+}
+
+u32 vidc_720p_engine_reset(u32 ch_id,
+	enum vidc_720p_endian dma_endian,
+	enum vidc_720p_interrupt_level_selection interrupt_sel,
+	u32 interrupt_mask
+)
+{
+	u32 op_done = 0;
+	u32 counter = 0;
+
+	VIDC_LOGERR_STRING("ENG-RESET!!");
+	/* issue the engine reset command */
+	vidc_720p_submit_command(ch_id, VIDC_720P_CMD_MFC_ENGINE_RESET);
+
+	do {
+		VIDC_BUSY_WAIT(20);
+		VIDC_IO_IN(REG_982553, &op_done);
+		counter++;
+	} while (!op_done && counter < 10);
+
+	if (!op_done) {
+		/* Reset fails */
+		return  false ;
+	}
+
+	/* write invalid channel id */
+	VIDC_IO_OUT(REG_97293, 4);
+
+	/* Set INT_PULSE_SEL */
+	if (interrupt_sel == VIDC_720P_INTERRUPT_LEVEL_SEL)
+		VIDC_IO_OUT(REG_491082, 0);
+	else
+		VIDC_IO_OUT(REG_491082, 1);
+
+	if (!interrupt_mask) {
+		/* Disable interrupt */
+		VIDC_IO_OUT(REG_609676, 1);
+	} else {
+	  /* Enable interrupt */
+		VIDC_IO_OUT(REG_609676, 0);
+	}
+
+	/* Clear any pending interrupt */
+	VIDC_IO_OUT(REG_614776, 1);
+
+	/* Set INT_ENABLE_REG */
+	VIDC_IO_OUT(REG_418173, interrupt_mask);
+
+	/*Sets the DMA endianness */
+	VIDC_IO_OUT(REG_736316, dma_endian);
+
+	/*Restore ARM endianness */
+	VIDC_IO_OUT(REG_215724, 0);
+
+	/* retun engine reset success */
+	return true ;
+}
+
+void vidc_720p_set_channel(u32 i_ch_id,
+			    enum vidc_720p_enc_dec_selection
+			    enc_dec_sel, enum vidc_720p_codec codec,
+			    u32 *pi_fw, u32 i_firmware_size)
+{
+	u32 std_sel = 0;
+	VIDC_IO_OUT(REG_661565, 0);
+
+	if (enc_dec_sel)
+		std_sel = VIDC_REG_713080_ENC_ON_BMSK;
+
+	std_sel |= (u32) codec;
+
+	VIDC_IO_OUT(REG_713080, std_sel);
+
+	switch (codec) {
+	default:
+	case VIDC_720P_DIVX:
+	case VIDC_720P_XVID:
+	case VIDC_720P_MPEG4:
+		{
+			if (enc_dec_sel == VIDC_720P_ENCODER)
+				VIDC_IO_OUT(REG_765787, pi_fw);
+			else
+				VIDC_IO_OUT(REG_225040, pi_fw);
+			break;
+		}
+	case VIDC_720P_H264:
+		{
+			if (enc_dec_sel == VIDC_720P_ENCODER)
+				VIDC_IO_OUT(REG_942456, pi_fw);
+			else
+				VIDC_IO_OUT(REG_942170_ADDR_3, pi_fw);
+			break;
+		}
+	case VIDC_720P_H263:
+		{
+			if (enc_dec_sel == VIDC_720P_ENCODER)
+				VIDC_IO_OUT(REG_765787, pi_fw);
+			else
+				VIDC_IO_OUT(REG_942170_ADDR_6, pi_fw);
+			break;
+		}
+	case VIDC_720P_VC1:
+		{
+			VIDC_IO_OUT(REG_880188, pi_fw);
+			break;
+		}
+	case VIDC_720P_MPEG2:
+		{
+			VIDC_IO_OUT(REG_40293, pi_fw);
+			break;
+		}
+	}
+	VIDC_IO_OUT(REG_591577, i_firmware_size);
+
+	vidc_720p_submit_command(i_ch_id, VIDC_720P_CMD_CHSET);
+}
+
+void vidc_720p_encode_set_profile(u32 i_profile, u32 i_level)
+{
+	u32 profile_level = i_profile|(i_level << 0x8);
+	VIDC_IO_OUT(REG_839021, profile_level);
+}
+
+void vidc_720p_set_frame_size(u32 i_size_x, u32 i_size_y)
+{
+	VIDC_IO_OUT(REG_999267, i_size_x);
+
+	VIDC_IO_OUT(REG_345712, i_size_y);
+}
+
+void vidc_720p_encode_set_fps(u32 i_rc_frame_rate)
+{
+	VIDC_IO_OUT(REG_625444, i_rc_frame_rate);
+}
+
+void vidc_720p_encode_set_short_header(u32 i_short_header)
+{
+	VIDC_IO_OUT(REG_314290, i_short_header);
+}
+
+void vidc_720p_encode_set_vop_time(u32 vop_time_resolution,
+				    u32 vop_time_increment)
+{
+	u32 enable_vop, vop_timing_reg;
+	if (!vop_time_resolution)
+		VIDC_IO_OUT(REG_64895, 0x0);
+	else {
+		enable_vop = 0x1;
+		vop_timing_reg = (enable_vop << 0x1f) |
+		(vop_time_resolution << 0x10) | vop_time_increment;
+		VIDC_IO_OUT(REG_64895, vop_timing_reg);
+	}
+}
+
+void vidc_720p_encode_set_hec_period(u32 hec_period)
+{
+	VIDC_IO_OUT(REG_407718, hec_period);
+}
+
+void vidc_720p_encode_set_qp_params(u32 i_max_qp, u32 i_min_qp)
+{
+	u32 qp = i_min_qp | (i_max_qp << 0x8);
+	VIDC_IO_OUT(REG_734318, qp);
+}
+
+void vidc_720p_encode_set_rc_config(u32 enable_frame_level_rc,
+				     u32 enable_mb_level_rc_flag,
+				     u32 i_frame_qp, u32 pframe_qp)
+{
+   u32 rc_config = i_frame_qp;
+
+	if (enable_frame_level_rc)
+		rc_config |= (0x1 << 0x9);
+
+	if (enable_mb_level_rc_flag)
+		rc_config |= (0x1 << 0x8);
+
+	VIDC_IO_OUT(REG_58211, rc_config);
+	VIDC_IO_OUT(REG_548359, pframe_qp);
+}
+
+void vidc_720p_encode_set_bit_rate(u32 i_target_bitrate)
+{
+	VIDC_IO_OUT(REG_174150, i_target_bitrate);
+}
+
+void vidc_720p_encoder_set_param_change(u32 enc_param_change)
+{
+	VIDC_IO_OUT(REG_804959, enc_param_change);
+}
+
+void vidc_720p_encode_set_control_param(u32 param_val)
+{
+	VIDC_IO_OUT(REG_128234, param_val);
+}
+
+void vidc_720p_encode_set_frame_level_rc_params(u32 i_reaction_coeff)
+{
+	VIDC_IO_OUT(REG_677784, i_reaction_coeff);
+}
+
+void vidc_720p_encode_set_mb_level_rc_params(u32 dark_region_as_flag,
+					      u32 smooth_region_as_flag,
+					      u32 static_region_as_flag,
+					      u32 activity_region_flag)
+{
+	u32 mb_level_rc = 0x0;
+	if (activity_region_flag)
+		mb_level_rc |= 0x1;
+	if (static_region_as_flag)
+		mb_level_rc |= (0x1 << 0x1);
+	if (smooth_region_as_flag)
+		mb_level_rc |= (0x1 << 0x2);
+	if (dark_region_as_flag)
+		mb_level_rc |= (0x1 << 0x3);
+	/* Write MB level rate control */
+	VIDC_IO_OUT(REG_995041, mb_level_rc);
+}
+
+void vidc_720p_encode_set_entropy_control(enum vidc_720p_entropy_sel
+					   entropy_sel,
+					   enum vidc_720p_cabac_model
+					   cabac_model_number)
+{
+	u32 num;
+	u32 entropy_params = (u32)entropy_sel;
+	/* Set Model Number */
+	if (entropy_sel == VIDC_720P_ENTROPY_SEL_CABAC) {
+		num = (u32)cabac_model_number;
+		entropy_params |= (num << 0x2);
+	}
+	/* Set Entropy parameters */
+	VIDC_IO_OUT(REG_504878, entropy_params);
+}
+
+void vidc_720p_encode_set_db_filter_control(enum vidc_720p_DBConfig
+					     db_config,
+					     u32 i_slice_alpha_offset,
+					     u32 i_slice_beta_offset)
+{
+	u32 deblock_params;
+	deblock_params = (u32)db_config;
+	deblock_params |=
+		((i_slice_beta_offset << 0x2) | (i_slice_alpha_offset << 0x7));
+
+	/* Write deblocking control settings */
+	VIDC_IO_OUT(REG_458130, deblock_params);
+}
+
+void vidc_720p_encode_set_intra_refresh_mb_number(u32 i_cir_mb_number)
+{
+	VIDC_IO_OUT(REG_857491, i_cir_mb_number);
+}
+
+void vidc_720p_encode_set_multi_slice_info(enum
+					    vidc_720p_MSlice_selection
+					    m_slice_sel,
+					    u32 multi_slice_size)
+{
+	switch (m_slice_sel) {
+	case VIDC_720P_MSLICE_BY_MB_COUNT:
+		{
+			VIDC_IO_OUT(REG_588301, 0x1);
+			VIDC_IO_OUT(REG_1517, m_slice_sel);
+			VIDC_IO_OUT(REG_105335, multi_slice_size);
+			break;
+		}
+	case VIDC_720P_MSLICE_BY_BYTE_COUNT:
+		{
+			VIDC_IO_OUT(REG_588301, 0x1);
+			VIDC_IO_OUT(REG_1517, m_slice_sel);
+			VIDC_IO_OUT(REG_561679, multi_slice_size);
+			break;
+		}
+	case VIDC_720P_MSLICE_BY_GOB:
+		{
+			VIDC_IO_OUT(REG_588301, 0x1);
+			break;
+		}
+	default:
+	case VIDC_720P_MSLICE_OFF:
+		{
+			VIDC_IO_OUT(REG_588301, 0x0);
+			break;
+		}
+	}
+}
+
+void vidc_720p_encode_set_dpb_buffer(u32 *pi_enc_dpb_addr, u32 alloc_len)
+{
+	VIDC_IO_OUT(REG_341928_ADDR, pi_enc_dpb_addr);
+	VIDC_IO_OUT(REG_319934, alloc_len);
+}
+
+void vidc_720p_encode_set_i_period(u32 i_i_period)
+{
+	VIDC_IO_OUT(REG_950374, i_i_period);
+}
+
+void vidc_720p_encode_init_codec(u32 i_ch_id,
+				  enum vidc_720p_memory_access_method
+				  memory_access_model)
+{
+
+	VIDC_IO_OUT(REG_841539, memory_access_model);
+	vidc_720p_submit_command(i_ch_id, VIDC_720P_CMD_INITCODEC);
+}
+
+void vidc_720p_encode_unalign_bitstream(u32 upper_unalign_word,
+					 u32 lower_unalign_word)
+{
+	VIDC_IO_OUT(REG_792026, upper_unalign_word);
+	VIDC_IO_OUT(REG_844152, lower_unalign_word);
+}
+
+void vidc_720p_encode_set_seq_header_buffer(u32 ext_buffer_start,
+					     u32 ext_buffer_end,
+					     u32 start_byte_num)
+{
+	VIDC_IO_OUT(REG_275113_ADDR, ext_buffer_start);
+
+	VIDC_IO_OUT(REG_87912, ext_buffer_start);
+
+	VIDC_IO_OUT(REG_988007_ADDR, ext_buffer_end);
+
+	VIDC_IO_OUT(REG_66693, start_byte_num);
+}
+
+void vidc_720p_encode_frame(u32 ch_id,
+			     u32 ext_buffer_start,
+			     u32 ext_buffer_end,
+			     u32 start_byte_number, u32 y_addr,
+			     u32 c_addr)
+{
+	VIDC_IO_OUT(REG_275113_ADDR, ext_buffer_start);
+
+	VIDC_IO_OUT(REG_988007_ADDR, ext_buffer_end);
+
+	VIDC_IO_OUT(REG_87912, ext_buffer_start);
+
+	VIDC_IO_OUT(REG_66693, start_byte_number);
+
+	VIDC_IO_OUT(REG_99105, y_addr);
+
+	VIDC_IO_OUT(REG_777113_ADDR, c_addr);
+
+	vidc_720p_submit_command(ch_id, VIDC_720P_CMD_FRAMERUN);
+}
+
+void vidc_720p_encode_get_header(u32 *pi_enc_header_size)
+{
+	VIDC_IO_IN(REG_114286, pi_enc_header_size);
+}
+
+void vidc_720p_enc_frame_info(struct vidc_720p_enc_frame_info
+			       *enc_frame_info)
+{
+	VIDC_IO_IN(REG_782249, &enc_frame_info->enc_size);
+
+	VIDC_IO_IN(REG_441270, &enc_frame_info->frame);
+
+	enc_frame_info->frame &= 0x03;
+
+	VIDC_IO_IN(REG_613254,
+		    &enc_frame_info->metadata_exists);
+}
+
+void vidc_720p_decode_bitstream_header(u32 ch_id,
+					u32 dec_unit_size,
+					u32 start_byte_num,
+					u32 ext_buffer_start,
+					u32 ext_buffer_end,
+					enum
+					vidc_720p_memory_access_method
+					memory_access_model,
+					u32 decode_order)
+{
+	VIDC_IO_OUT(REG_965480, decode_order);
+
+	VIDC_IO_OUT(REG_639999, 0x8080);
+
+	VIDC_IO_OUT(REG_275113_ADDR, ext_buffer_start);
+
+	VIDC_IO_OUT(REG_988007_ADDR, ext_buffer_end);
+
+	VIDC_IO_OUT(REG_87912, ext_buffer_end);
+
+	VIDC_IO_OUT(REG_761892, dec_unit_size);
+
+	VIDC_IO_OUT(REG_66693, start_byte_num);
+
+	VIDC_IO_OUT(REG_841539, memory_access_model);
+
+	vidc_720p_submit_command(ch_id, VIDC_720P_CMD_INITCODEC);
+}
+
+void vidc_720p_decode_get_seq_hdr_info(struct vidc_720p_seq_hdr_info
+					*seq_hdr_info)
+{
+	u32 display_status;
+	VIDC_IO_IN(REG_999267, &seq_hdr_info->img_size_x);
+
+	VIDC_IO_IN(REG_345712, &seq_hdr_info->img_size_y);
+
+	VIDC_IO_IN(REG_257463, &seq_hdr_info->min_num_dpb);
+
+	VIDC_IO_IN(REG_854281, &seq_hdr_info->min_dpb_size);
+
+	VIDC_IO_IN(REG_580603, &seq_hdr_info->dec_frm_size);
+
+	VIDC_IO_INF(REG_606447, DISP_PIC_PROFILE,
+				 &seq_hdr_info->profile);
+
+	VIDC_IO_INF(REG_606447, DIS_PIC_LEVEL,
+				 &seq_hdr_info->level);
+
+	VIDC_IO_INF(REG_612715, DISPLAY_STATUS,
+				&display_status);
+	seq_hdr_info->progressive =
+			((display_status & 0x4) >> 2);
+	/* bit 3 is for crop existence */
+	seq_hdr_info->crop_exists = ((display_status & 0x8) >> 3);
+
+	if (seq_hdr_info->crop_exists) {
+		/* read the cropping information */
+		VIDC_IO_INF(REG_881638, CROP_RIGHT_OFFSET, \
+			&seq_hdr_info->crop_right_offset);
+		VIDC_IO_INF(REG_881638, CROP_LEFT_OFFSET, \
+			&seq_hdr_info->crop_left_offset);
+		VIDC_IO_INF(REG_161486, CROP_BOTTOM_OFFSET, \
+			&seq_hdr_info->crop_bottom_offset);
+		VIDC_IO_INF(REG_161486, CROP_TOP_OFFSET, \
+			&seq_hdr_info->crop_top_offset);
+	}
+	/* Read the MPEG4 data partitioning indication */
+	VIDC_IO_INF(REG_441270, DATA_PARTITIONED, \
+				&seq_hdr_info->data_partitioned);
+
+}
+
+void vidc_720p_decode_set_dpb_release_buffer_mask(u32
+						   i_dpb_release_buffer_mask)
+{
+	VIDC_IO_OUT(REG_603032, i_dpb_release_buffer_mask);
+}
+
+void vidc_720p_decode_set_dpb_buffers(u32 i_buf_index, u32 *pi_dpb_buffer)
+{
+	VIDC_IO_OUTI(REG_615716, i_buf_index, pi_dpb_buffer);
+}
+
+void vidc_720p_decode_set_comv_buffer(u32 *pi_dpb_comv_buffer,
+				       u32 alloc_len)
+{
+	VIDC_IO_OUT(REG_456376_ADDR, pi_dpb_comv_buffer);
+
+	VIDC_IO_OUT(REG_490443, alloc_len);
+}
+
+void vidc_720p_decode_set_dpb_details(u32 num_dpb, u32 alloc_len,
+				       u32 *ref_buffer)
+{
+	VIDC_IO_OUT(REG_518133, ref_buffer);
+
+	VIDC_IO_OUT(REG_267567, 0);
+
+	VIDC_IO_OUT(REG_883500, num_dpb);
+
+	VIDC_IO_OUT(REG_319934, alloc_len);
+}
+
+void vidc_720p_decode_set_mpeg4Post_filter(u32 enable_post_filter)
+{
+	if (enable_post_filter)
+		VIDC_IO_OUT(REG_443811, 0x1);
+	else
+		VIDC_IO_OUT(REG_443811, 0x0);
+}
+
+void vidc_720p_decode_set_error_control(u32 enable_error_control)
+{
+	if (enable_error_control)
+		VIDC_IO_OUT(REG_846346, 0);
+	else
+		VIDC_IO_OUT(REG_846346, 1);
+}
+
+void vidc_720p_set_deblock_line_buffer(u32 *pi_deblock_line_buffer_start,
+					u32 alloc_len)
+{
+	VIDC_IO_OUT(REG_979942, pi_deblock_line_buffer_start);
+
+	VIDC_IO_OUT(REG_101184, alloc_len);
+}
+
+void vidc_720p_decode_set_mpeg4_data_partitionbuffer(u32 *vsp_buf_start)
+{
+    VIDC_IO_OUT(REG_958768, vsp_buf_start);
+}
+
+void vidc_720p_decode_setH264VSPBuffer(u32 *pi_vsp_temp_buffer_start)
+{
+	VIDC_IO_OUT(REG_958768, pi_vsp_temp_buffer_start);
+}
+
+void vidc_720p_decode_frame(u32 ch_id, u32 ext_buffer_start,
+			     u32 ext_buffer_end, u32 dec_unit_size,
+			     u32 start_byte_num, u32 input_frame_tag)
+{
+	VIDC_IO_OUT(REG_275113_ADDR, ext_buffer_start);
+
+	VIDC_IO_OUT(REG_988007_ADDR, ext_buffer_end);
+
+	VIDC_IO_OUT(REG_87912, ext_buffer_end);
+
+	VIDC_IO_OUT(REG_66693, start_byte_num);
+
+	VIDC_IO_OUT(REG_94750, input_frame_tag);
+
+	VIDC_IO_OUT(REG_761892, dec_unit_size);
+
+	vidc_720p_submit_command(ch_id, VIDC_720P_CMD_FRAMERUN);
+}
+
+void vidc_720p_issue_eos(u32 i_ch_id)
+{
+    VIDC_IO_OUT(REG_896825, 0x1);
+
+    VIDC_IO_OUT(REG_761892, 0);
+
+    vidc_720p_submit_command(i_ch_id, VIDC_720P_CMD_FRAMERUN);
+}
+
+void vidc_720p_eos_info(u32 *disp_status, u32 *resl_change)
+{
+   VIDC_IO_INF(REG_612715, DISPLAY_STATUS, disp_status);
+   (*disp_status) = (*disp_status) & 0x3;
+   VIDC_IO_INF(REG_724381, RESOLUTION_CHANGE, resl_change);
+}
+
+void vidc_720p_decode_display_info(struct vidc_720p_dec_disp_info
+				    *disp_info)
+{
+	u32 display_status = 0;
+	VIDC_IO_INF(REG_612715, DISPLAY_STATUS, &display_status);
+
+	disp_info->disp_status =
+	    (enum vidc_720p_display_status)((display_status & 0x3));
+
+	disp_info->disp_is_interlace = ((display_status & 0x4) >> 2);
+	disp_info->crop_exists = ((display_status & 0x8) >> 3);
+
+	disp_info->resl_change = ((display_status & 0x30) >> 4);
+
+	VIDC_IO_INF(REG_724381, RESOLUTION_CHANGE,
+		     &disp_info->reconfig_flush_done);
+
+	VIDC_IO_IN(REG_999267, &disp_info->img_size_x);
+
+	VIDC_IO_IN(REG_345712, &disp_info->img_size_y);
+	VIDC_IO_IN(REG_151345, &disp_info->y_addr);
+	VIDC_IO_IN(REG_293983, &disp_info->c_addr);
+	VIDC_IO_IN(REG_370409, &disp_info->tag_top);
+	VIDC_IO_IN(REG_438677, &disp_info->tag_bottom);
+	VIDC_IO_IN(REG_679165, &disp_info->pic_time_top);
+	VIDC_IO_IN(REG_374150, &disp_info->pic_time_bottom);
+
+	if (disp_info->crop_exists) {
+		VIDC_IO_INF(REG_881638, CROP_RIGHT_OFFSET,
+			&disp_info->crop_right_offset);
+		VIDC_IO_INF(REG_881638, CROP_LEFT_OFFSET,
+			&disp_info->crop_left_offset);
+		VIDC_IO_INF(REG_161486, CROP_BOTTOM_OFFSET,
+			&disp_info->crop_bottom_offset);
+		VIDC_IO_INF(REG_161486, CROP_TOP_OFFSET,
+			&disp_info->crop_top_offset);
+	}
+	VIDC_IO_IN(REG_613254, &disp_info->metadata_exists);
+
+	VIDC_IO_IN(REG_580603,
+		    &disp_info->input_bytes_consumed);
+
+	VIDC_IO_IN(REG_757835, &disp_info->input_frame_num);
+
+	VIDC_IO_INF(REG_441270, FRAME_TYPE,
+			   &disp_info->input_frame);
+
+	disp_info->input_is_interlace =
+	    ((disp_info->input_frame & 0x4) >> 2);
+
+	if (disp_info->input_frame & 0x10)
+		disp_info->input_frame = VIDC_720P_IDRFRAME;
+	else
+		disp_info->input_frame &= 0x3;
+}
+
+void vidc_720p_decode_skip_frm_details(u32 *free_luma_dpb)
+{
+	u32 disp_frm;
+	VIDC_IO_IN(REG_697961, &disp_frm);
+
+	if (disp_frm == VIDC_720P_NOTCODED)
+		VIDC_IO_IN(REG_347105, free_luma_dpb);
+}
+
+void vidc_720p_metadata_enable(u32 flag, u32 *input_buffer)
+{
+	VIDC_IO_OUT(REG_854681, flag);
+	VIDC_IO_OUT(REG_988552, input_buffer);
+}
+
+void vidc_720p_decode_dynamic_req_reset(void)
+{
+	VIDC_IO_OUT(REG_76706, 0x0);
+	VIDC_IO_OUT(REG_147682, 0x0);
+	VIDC_IO_OUT(REG_896825, 0x0);
+}
+
+void vidc_720p_decode_dynamic_req_set(u32 property)
+{
+	if (property == VIDC_720P_FLUSH_REQ)
+		VIDC_IO_OUT(REG_76706, 0x1);
+	else if (property == VIDC_720P_EXTRADATA)
+		VIDC_IO_OUT(REG_147682, 0x1);
+}
+
+void vidc_720p_decode_setpassthrough_start(u32 pass_startaddr)
+{
+	VIDC_IO_OUT(REG_486169, pass_startaddr);
+}
diff --git a/drivers/video/msm/vidc/720p/ddl/vidc.h b/drivers/video/msm/vidc/720p/ddl/vidc.h
new file mode 100644
index 0000000..509482b
--- /dev/null
+++ b/drivers/video/msm/vidc/720p/ddl/vidc.h
@@ -0,0 +1,2705 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef VIDC_H
+#define VIDC_H
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <asm/system.h>
+
+#define VIDC_720P_IN(reg)                       VIDC_##reg##_IN
+#define VIDC_720P_INM(reg,  mask)                VIDC_##reg##_INM(mask)
+#define VIDC_720P_OUT(reg,  val)                 VIDC_##reg##_OUT(val)
+#define VIDC_720P_OUTI(reg,  index,  val)         VIDC_##reg##_OUTI(index, val)
+#define VIDC_720P_OUTM(reg,  mask,  val)          VIDC_##reg##_OUTM(mask,  val)
+#define VIDC_720P_SHFT(reg,  field)              VIDC_##reg##_##field##_SHFT
+#define VIDC_720P_FMSK(reg,  field)              VIDC_##reg##_##field##_BMSK
+
+#define VIDC_720P_INF(io, field) (VIDC_720P_INM(io, VIDC_720P_FMSK(io, field)) \
+		>> VIDC_720P_SHFT(io,  field))
+#define VIDC_720P_OUTF(io, field, val) \
+		VIDC_720P_OUTM(io, VIDC_720P_FMSK(io, field), \
+		val << VIDC_720P_SHFT(io,  field))
+
+#define __inpdw(port)	ioread32(port)
+#define __outpdw(port,  val) iowrite32(val, port)
+
+#define in_dword_masked(addr,  mask) (__inpdw(addr) & (mask))
+
+#define out_dword(addr,  val)        __outpdw(addr, val)
+
+#define out_dword_masked(io,  mask,  val,  shadow)  \
+do { \
+	shadow = (shadow & (u32)(~(mask))) | ((u32)((val) & (mask))); \
+	(void) out_dword(io,  shadow); \
+} while (0)
+
+#define out_dword_masked_ns(io,  mask,  val,  current_reg_content) \
+	(void) out_dword(io,  ((current_reg_content & (u32)(~(mask))) | \
+				((u32)((val) & (mask)))))
+
+extern u8 *vidc_base_addr;
+
+#define VIDC720P_BASE  vidc_base_addr
+#define VIDC_720P_WRAPPER_REG_BASE               (VIDC720P_BASE + \
+		0x00000000)
+#define VIDC_720P_WRAPPER_REG_BASE_PHYS          VIDC_720P_BASE_PHYS
+
+#define VIDC_REG_614413_ADDR                     \
+	(VIDC_720P_WRAPPER_REG_BASE      + 00000000)
+#define VIDC_REG_614413_PHYS                     \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 00000000)
+#define VIDC_REG_614413_RMSK                            0x1
+#define VIDC_REG_614413_SHFT                              0
+#define VIDC_REG_614413_IN                       \
+	in_dword_masked(VIDC_REG_614413_ADDR,        \
+		VIDC_REG_614413_RMSK)
+#define VIDC_REG_614413_INM(m)                   \
+	in_dword_masked(VIDC_REG_614413_ADDR,  m)
+#define VIDC_REG_614413_OUT(v)                   \
+	out_dword(VIDC_REG_614413_ADDR, v)
+#define VIDC_REG_614413_OUTM(m, v)                \
+do { \
+	out_dword_masked_ns(VIDC_REG_614413_ADDR, m, v, \
+			VIDC_REG_614413_IN); \
+} while (0)
+#define VIDC_REG_614413_DMA_START_BMSK                  0x1
+#define VIDC_REG_614413_DMA_START_SHFT                    0
+
+#define VIDC_REG_591577_ADDR                 \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x0000000c)
+#define VIDC_REG_591577_PHYS                 \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000000c)
+#define VIDC_REG_591577_RMSK                 0xffffffff
+#define VIDC_REG_591577_SHFT                          0
+#define VIDC_REG_591577_IN                   \
+	in_dword_masked(VIDC_REG_591577_ADDR,  \
+			VIDC_REG_591577_RMSK)
+#define VIDC_REG_591577_INM(m)               \
+	in_dword_masked(VIDC_REG_591577_ADDR,  m)
+#define VIDC_REG_591577_OUT(v)               \
+	out_dword(VIDC_REG_591577_ADDR, v)
+#define VIDC_REG_591577_OUTM(m, v)            \
+do { \
+	out_dword_masked_ns(VIDC_REG_591577_ADDR, m, v, \
+			VIDC_REG_591577_IN); \
+} while (0)
+#define VIDC_REG_591577_BOOTCODE_SIZE_BMSK   0xffffffff
+#define VIDC_REG_591577_BOOTCODE_SIZE_SHFT            0
+
+#define VIDC_REG_203921_ADDR                   \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000014)
+#define VIDC_REG_203921_PHYS                   \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000014)
+#define VIDC_REG_203921_RMSK                   0xffffffff
+#define VIDC_REG_203921_SHFT                            0
+#define VIDC_REG_203921_IN                     \
+	in_dword_masked(VIDC_REG_203921_ADDR,  \
+			VIDC_REG_203921_RMSK)
+#define VIDC_REG_203921_INM(m)                 \
+	in_dword_masked(VIDC_REG_203921_ADDR,  m)
+#define VIDC_REG_203921_OUT(v)                 \
+	out_dword(VIDC_REG_203921_ADDR, v)
+#define VIDC_REG_203921_OUTM(m, v)              \
+do { \
+	out_dword_masked_ns(VIDC_REG_203921_ADDR, m, v, \
+			VIDC_REG_203921_IN); \
+} while (0)
+#define VIDC_REG_203921_DMA_EXTADDR_BMSK       0xffffffff
+#define VIDC_REG_203921_DMA_EXTADDR_SHFT                0
+
+#define VIDC_REG_275113_ADDR_ADDR            \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000018)
+#define VIDC_REG_275113_ADDR_PHYS            \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000018)
+#define VIDC_REG_275113_ADDR_RMSK            0xffffffff
+#define VIDC_REG_275113_ADDR_SHFT                     0
+#define VIDC_REG_275113_ADDR_IN              \
+	in_dword_masked(VIDC_REG_275113_ADDR_ADDR,  \
+			VIDC_REG_275113_ADDR_RMSK)
+#define VIDC_REG_275113_ADDR_INM(m)          \
+	in_dword_masked(VIDC_REG_275113_ADDR_ADDR,  m)
+#define VIDC_REG_275113_ADDR_OUT(v)          \
+	out_dword(VIDC_REG_275113_ADDR_ADDR, v)
+#define VIDC_REG_275113_ADDR_OUTM(m, v)       \
+do { \
+	out_dword_masked_ns(VIDC_REG_275113_ADDR_ADDR, m, v, \
+			VIDC_REG_275113_ADDR_IN); \
+} while (0)
+#define VIDC_REG_742076_ADDR_BMSK 0xffffffff
+#define VIDC_REG_742076_ADDR_SHFT          0
+
+#define VIDC_REG_988007_ADDR_ADDR              \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x0000001c)
+#define VIDC_REG_988007_ADDR_PHYS              \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000001c)
+#define VIDC_REG_988007_ADDR_RMSK              0xffffffff
+#define VIDC_REG_988007_ADDR_SHFT                       0
+#define VIDC_REG_988007_ADDR_IN                \
+	in_dword_masked(VIDC_REG_988007_ADDR_ADDR,  \
+			VIDC_REG_988007_ADDR_RMSK)
+#define VIDC_REG_988007_ADDR_INM(m)            \
+	in_dword_masked(VIDC_REG_988007_ADDR_ADDR,  m)
+#define VIDC_REG_988007_ADDR_OUT(v)            \
+	out_dword(VIDC_REG_988007_ADDR_ADDR, v)
+#define VIDC_REG_988007_ADDR_OUTM(m, v)         \
+do { \
+	out_dword_masked_ns(VIDC_REG_988007_ADDR_ADDR, m, v, \
+			VIDC_REG_988007_ADDR_IN); \
+} while (0)
+#define VIDC_REG_988007_ADDR_EXT_BUF_END_ADDR_BMSK 0xffffffff
+#define VIDC_REG_988007_ADDR_EXT_BUF_END_ADDR_SHFT          0
+
+#define VIDC_REG_531515_ADDR_ADDR                  \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000020)
+#define VIDC_REG_531515_ADDR_PHYS                  \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000020)
+#define VIDC_REG_531515_ADDR_RMSK                  0xffffffff
+#define VIDC_REG_531515_ADDR_SHFT                           0
+#define VIDC_REG_531515_ADDR_IN                    \
+	in_dword_masked(VIDC_REG_531515_ADDR_ADDR,  \
+			VIDC_REG_531515_ADDR_RMSK)
+#define VIDC_REG_531515_ADDR_INM(m)                \
+	in_dword_masked(VIDC_REG_531515_ADDR_ADDR,  m)
+#define VIDC_REG_531515_ADDR_OUT(v)                \
+	out_dword(VIDC_REG_531515_ADDR_ADDR, v)
+#define VIDC_REG_531515_ADDR_OUTM(m, v)             \
+do { \
+	out_dword_masked_ns(VIDC_REG_531515_ADDR_ADDR, m, v, \
+			VIDC_REG_531515_ADDR_IN); \
+} while (0)
+#define VIDC_REG_531515_ADDR_DMA_INT_ADDR_BMSK     0xffffffff
+#define VIDC_REG_531515_ADDR_DMA_INT_ADDR_SHFT              0
+
+#define VIDC_REG_87912_ADDR                 \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000024)
+#define VIDC_REG_87912_PHYS                 \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000024)
+#define VIDC_REG_87912_RMSK                 0xffffffff
+#define VIDC_REG_87912_SHFT                          0
+#define VIDC_REG_87912_IN                   \
+	in_dword_masked(VIDC_REG_87912_ADDR,  \
+			VIDC_REG_87912_RMSK)
+#define VIDC_REG_87912_INM(m)               \
+	in_dword_masked(VIDC_REG_87912_ADDR,  m)
+#define VIDC_REG_87912_OUT(v)               \
+	out_dword(VIDC_REG_87912_ADDR, v)
+#define VIDC_REG_87912_OUTM(m, v)            \
+do { \
+	out_dword_masked_ns(VIDC_REG_87912_ADDR, m, v, \
+			VIDC_REG_87912_IN); \
+} while (0)
+#define VIDC_REG_87912_HOST_PTR_ADDR_BMSK   0xffffffff
+#define VIDC_REG_87912_HOST_PTR_ADDR_SHFT            0
+
+#define VIDC_REG_896825_ADDR                      \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000028)
+#define VIDC_REG_896825_PHYS                      \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000028)
+#define VIDC_REG_896825_RMSK                             0x1
+#define VIDC_REG_896825_SHFT                               0
+#define VIDC_REG_896825_IN                        \
+	in_dword_masked(VIDC_REG_896825_ADDR,         \
+	VIDC_REG_896825_RMSK)
+#define VIDC_REG_896825_INM(m)                    \
+	in_dword_masked(VIDC_REG_896825_ADDR,  m)
+#define VIDC_REG_896825_OUT(v)                    \
+	out_dword(VIDC_REG_896825_ADDR, v)
+#define VIDC_REG_896825_OUTM(m, v)                 \
+do { \
+	out_dword_masked_ns(VIDC_REG_896825_ADDR, m, v, \
+			VIDC_REG_896825_IN); \
+} while (0)
+#define VIDC_REG_896825_LAST_DEC_BMSK                    0x1
+#define VIDC_REG_896825_LAST_DEC_SHFT                      0
+
+#define VIDC_REG_174526_ADDR                        \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x0000002c)
+#define VIDC_REG_174526_PHYS                        \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000002c)
+#define VIDC_REG_174526_RMSK                               0x1
+#define VIDC_REG_174526_SHFT                                 0
+#define VIDC_REG_174526_IN                          \
+	in_dword_masked(VIDC_REG_174526_ADDR,  VIDC_REG_174526_RMSK)
+#define VIDC_REG_174526_INM(m)                      \
+	in_dword_masked(VIDC_REG_174526_ADDR,  m)
+#define VIDC_REG_174526_DONE_M_BMSK                        0x1
+#define VIDC_REG_174526_DONE_M_SHFT                          0
+
+#define VIDC_REG_736316_ADDR                   \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000044)
+#define VIDC_REG_736316_PHYS                   \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000044)
+#define VIDC_REG_736316_RMSK                          0x1
+#define VIDC_REG_736316_SHFT                            0
+#define VIDC_REG_736316_IN                     \
+	in_dword_masked(VIDC_REG_736316_ADDR,  \
+			VIDC_REG_736316_RMSK)
+#define VIDC_REG_736316_INM(m)                 \
+	in_dword_masked(VIDC_REG_736316_ADDR,  m)
+#define VIDC_REG_736316_OUT(v)                 \
+	out_dword(VIDC_REG_736316_ADDR, v)
+#define VIDC_REG_736316_OUTM(m, v)              \
+do { \
+	out_dword_masked_ns(VIDC_REG_736316_ADDR, m, v, \
+			VIDC_REG_736316_IN); \
+} while (0)
+#define VIDC_REG_736316_BITS_ENDIAN_BMSK              0x1
+#define VIDC_REG_736316_BITS_ENDIAN_SHFT                0
+
+#define VIDC_REG_761892_ADDR                 \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000054)
+#define VIDC_REG_761892_PHYS                 \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000054)
+#define VIDC_REG_761892_RMSK                 0xffffffff
+#define VIDC_REG_761892_SHFT                          0
+#define VIDC_REG_761892_IN                   \
+	in_dword_masked(VIDC_REG_761892_ADDR,  \
+			VIDC_REG_761892_RMSK)
+#define VIDC_REG_761892_INM(m)               \
+	in_dword_masked(VIDC_REG_761892_ADDR,  m)
+#define VIDC_REG_761892_OUT(v)               \
+	out_dword(VIDC_REG_761892_ADDR, v)
+#define VIDC_REG_761892_OUTM(m, v)            \
+do { \
+	out_dword_masked_ns(VIDC_REG_761892_ADDR, m, v, \
+			VIDC_REG_761892_IN); \
+} while (0)
+#define VIDC_REG_761892_DEC_UNIT_SIZE_BMSK   0xffffffff
+#define VIDC_REG_761892_DEC_UNIT_SIZE_SHFT            0
+
+#define VIDC_REG_782249_ADDR                 \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000058)
+#define VIDC_REG_782249_PHYS                 \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000058)
+#define VIDC_REG_782249_RMSK                 0xffffffff
+#define VIDC_REG_782249_SHFT                          0
+#define VIDC_REG_782249_IN                   \
+	in_dword_masked(VIDC_REG_782249_ADDR,  \
+			VIDC_REG_782249_RMSK)
+#define VIDC_REG_782249_INM(m)               \
+	in_dword_masked(VIDC_REG_782249_ADDR,  m)
+#define VIDC_REG_782249_ENC_UNIT_SIZE_BMSK   0xffffffff
+#define VIDC_REG_782249_ENC_UNIT_SIZE_SHFT            0
+
+#define VIDC_REG_66693_ADDR                \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x0000005c)
+#define VIDC_REG_66693_PHYS                \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000005c)
+#define VIDC_REG_66693_RMSK                       0xf
+#define VIDC_REG_66693_SHFT                         0
+#define VIDC_REG_66693_IN                  \
+	in_dword_masked(VIDC_REG_66693_ADDR,  \
+			VIDC_REG_66693_RMSK)
+#define VIDC_REG_66693_INM(m)              \
+	in_dword_masked(VIDC_REG_66693_ADDR,  m)
+#define VIDC_REG_66693_OUT(v)              \
+	out_dword(VIDC_REG_66693_ADDR, v)
+#define VIDC_REG_66693_OUTM(m, v)           \
+do { \
+	out_dword_masked_ns(VIDC_REG_66693_ADDR, m, v, \
+			VIDC_REG_66693_IN); \
+} while (0)
+#define VIDC_REG_66693_START_BYTE_NUM_BMSK        0xf
+#define VIDC_REG_66693_START_BYTE_NUM_SHFT          0
+
+#define VIDC_REG_114286_ADDR               \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000060)
+#define VIDC_REG_114286_PHYS               \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000060)
+#define VIDC_REG_114286_RMSK               0xffffffff
+#define VIDC_REG_114286_SHFT                        0
+#define VIDC_REG_114286_IN                 \
+	in_dword_masked(VIDC_REG_114286_ADDR,  \
+			VIDC_REG_114286_RMSK)
+#define VIDC_REG_114286_INM(m)             \
+	in_dword_masked(VIDC_REG_114286_ADDR,  m)
+#define VIDC_REG_114286_ENC_HEADER_SIZE_BMSK 0xffffffff
+#define VIDC_REG_114286_ENC_HEADER_SIZE_SHFT          0
+
+#define VIDC_REG_713080_ADDR                   \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000100)
+#define VIDC_REG_713080_PHYS                   \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000100)
+#define VIDC_REG_713080_RMSK                         0x1f
+#define VIDC_REG_713080_SHFT                            0
+#define VIDC_REG_713080_IN                     \
+	in_dword_masked(VIDC_REG_713080_ADDR,  \
+			VIDC_REG_713080_RMSK)
+#define VIDC_REG_713080_INM(m)                 \
+	in_dword_masked(VIDC_REG_713080_ADDR,  m)
+#define VIDC_REG_713080_OUT(v)                 \
+	out_dword(VIDC_REG_713080_ADDR, v)
+#define VIDC_REG_713080_OUTM(m, v)              \
+do { \
+	out_dword_masked_ns(VIDC_REG_713080_ADDR, m, v, \
+			VIDC_REG_713080_IN); \
+} while (0)
+#define VIDC_REG_713080_ENC_ON_BMSK                  0x10
+#define VIDC_REG_713080_ENC_ON_SHFT                   0x4
+#define VIDC_REG_713080_STANDARD_SEL_BMSK             0xf
+#define VIDC_REG_713080_STANDARD_SEL_SHFT               0
+
+#define VIDC_REG_97293_ADDR                         \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000104)
+#define VIDC_REG_97293_PHYS                         \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000104)
+#define VIDC_REG_97293_RMSK                               0x1f
+#define VIDC_REG_97293_SHFT                                  0
+#define VIDC_REG_97293_IN                           \
+	in_dword_masked(VIDC_REG_97293_ADDR,  VIDC_REG_97293_RMSK)
+#define VIDC_REG_97293_INM(m)                       \
+	in_dword_masked(VIDC_REG_97293_ADDR,  m)
+#define VIDC_REG_97293_OUT(v)                       \
+	out_dword(VIDC_REG_97293_ADDR, v)
+#define VIDC_REG_97293_OUTM(m, v)                    \
+do { \
+	out_dword_masked_ns(VIDC_REG_97293_ADDR, m, v, \
+			VIDC_REG_97293_IN); \
+} while (0)
+#define VIDC_REG_97293_CH_ID_BMSK                         0x1f
+#define VIDC_REG_97293_CH_ID_SHFT                            0
+
+#define VIDC_REG_224135_ADDR                     \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000108)
+#define VIDC_REG_224135_PHYS                     \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000108)
+#define VIDC_REG_224135_RMSK                            0x1
+#define VIDC_REG_224135_SHFT                              0
+#define VIDC_REG_224135_IN                       \
+	in_dword_masked(VIDC_REG_224135_ADDR,        \
+	VIDC_REG_224135_RMSK)
+#define VIDC_REG_224135_INM(m)                   \
+	in_dword_masked(VIDC_REG_224135_ADDR,  m)
+#define VIDC_REG_224135_OUT(v)                   \
+	out_dword(VIDC_REG_224135_ADDR, v)
+#define VIDC_REG_224135_OUTM(m, v)                \
+do { \
+	out_dword_masked_ns(VIDC_REG_224135_ADDR, m, v, \
+			VIDC_REG_224135_IN); \
+} while (0)
+#define VIDC_REG_224135_CPU_RESET_BMSK                  0x1
+#define VIDC_REG_224135_CPU_RESET_SHFT                    0
+
+#define VIDC_REG_832522_ADDR                        \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x0000010c)
+#define VIDC_REG_832522_PHYS                        \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000010c)
+#define VIDC_REG_832522_RMSK                               0x1
+#define VIDC_REG_832522_SHFT                                 0
+#define VIDC_REG_832522_IN                          \
+	in_dword_masked(VIDC_REG_832522_ADDR,  VIDC_REG_832522_RMSK)
+#define VIDC_REG_832522_INM(m)                      \
+	in_dword_masked(VIDC_REG_832522_ADDR,  m)
+#define VIDC_REG_832522_OUT(v)                      \
+	out_dword(VIDC_REG_832522_ADDR, v)
+#define VIDC_REG_832522_OUTM(m, v)                   \
+do { \
+	out_dword_masked_ns(VIDC_REG_832522_ADDR, m, v, \
+			VIDC_REG_832522_IN); \
+} while (0)
+#define VIDC_REG_832522_FW_END_BMSK                        0x1
+#define VIDC_REG_832522_FW_END_SHFT                          0
+
+#define VIDC_REG_361582_ADDR                    \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000110)
+#define VIDC_REG_361582_PHYS                    \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000110)
+#define VIDC_REG_361582_RMSK                           0x1
+#define VIDC_REG_361582_SHFT                             0
+#define VIDC_REG_361582_IN                      \
+	in_dword_masked(VIDC_REG_361582_ADDR,  \
+			VIDC_REG_361582_RMSK)
+#define VIDC_REG_361582_INM(m)                  \
+	in_dword_masked(VIDC_REG_361582_ADDR,  m)
+#define VIDC_REG_361582_OUT(v)                  \
+	out_dword(VIDC_REG_361582_ADDR, v)
+#define VIDC_REG_361582_OUTM(m, v)               \
+do { \
+	out_dword_masked_ns(VIDC_REG_361582_ADDR, m, v, \
+			VIDC_REG_361582_IN); \
+} while (0)
+#define VIDC_REG_361582_BUS_MASTER_BMSK                0x1
+#define VIDC_REG_361582_BUS_MASTER_SHFT                  0
+
+#define VIDC_REG_314435_ADDR                   \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000114)
+#define VIDC_REG_314435_PHYS                   \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000114)
+#define VIDC_REG_314435_RMSK                          0x1
+#define VIDC_REG_314435_SHFT                            0
+#define VIDC_REG_314435_IN                     \
+	in_dword_masked(VIDC_REG_314435_ADDR,  \
+			VIDC_REG_314435_RMSK)
+#define VIDC_REG_314435_INM(m)                 \
+	in_dword_masked(VIDC_REG_314435_ADDR,  m)
+#define VIDC_REG_314435_OUT(v)                 \
+	out_dword(VIDC_REG_314435_ADDR, v)
+#define VIDC_REG_314435_OUTM(m, v)              \
+do { \
+	out_dword_masked_ns(VIDC_REG_314435_ADDR, m, v, \
+			VIDC_REG_314435_IN); \
+} while (0)
+#define VIDC_REG_314435_FRAME_START_BMSK              0x1
+#define VIDC_REG_314435_FRAME_START_SHFT                0
+
+#define VIDC_REG_999267_ADDR                    \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000118)
+#define VIDC_REG_999267_PHYS                    \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000118)
+#define VIDC_REG_999267_RMSK                        0xffff
+#define VIDC_REG_999267_SHFT                             0
+#define VIDC_REG_999267_IN                      \
+	in_dword_masked(VIDC_REG_999267_ADDR,  \
+			VIDC_REG_999267_RMSK)
+#define VIDC_REG_999267_INM(m)                  \
+	in_dword_masked(VIDC_REG_999267_ADDR,  m)
+#define VIDC_REG_999267_OUT(v)                  \
+	out_dword(VIDC_REG_999267_ADDR, v)
+#define VIDC_REG_999267_OUTM(m, v)               \
+do { \
+	out_dword_masked_ns(VIDC_REG_999267_ADDR, m, v, \
+			VIDC_REG_999267_IN); \
+} while (0)
+#define VIDC_REG_999267_IMG_SIZE_X_BMSK             0xffff
+#define VIDC_REG_999267_IMG_SIZE_X_SHFT                  0
+
+#define VIDC_REG_345712_ADDR                    \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x0000011c)
+#define VIDC_REG_345712_PHYS                    \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000011c)
+#define VIDC_REG_345712_RMSK                        0xffff
+#define VIDC_REG_345712_SHFT                             0
+#define VIDC_REG_345712_IN                      \
+	in_dword_masked(VIDC_REG_345712_ADDR,  \
+			VIDC_REG_345712_RMSK)
+#define VIDC_REG_345712_INM(m)                  \
+	in_dword_masked(VIDC_REG_345712_ADDR,  m)
+#define VIDC_REG_345712_OUT(v)                  \
+	out_dword(VIDC_REG_345712_ADDR, v)
+#define VIDC_REG_345712_OUTM(m, v)               \
+do { \
+	out_dword_masked_ns(VIDC_REG_345712_ADDR, m, v, \
+			VIDC_REG_345712_IN); \
+} while (0)
+#define VIDC_REG_345712_IMG_SIZE_Y_BMSK             0xffff
+#define VIDC_REG_345712_IMG_SIZE_Y_SHFT                  0
+
+#define VIDC_REG_443811_ADDR                       \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000124)
+#define VIDC_REG_443811_PHYS                       \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000124)
+#define VIDC_REG_443811_RMSK                              0x1
+#define VIDC_REG_443811_SHFT                                0
+#define VIDC_REG_443811_IN                         \
+	in_dword_masked(VIDC_REG_443811_ADDR,  VIDC_REG_443811_RMSK)
+#define VIDC_REG_443811_INM(m)                     \
+	in_dword_masked(VIDC_REG_443811_ADDR,  m)
+#define VIDC_REG_443811_OUT(v)                     \
+	out_dword(VIDC_REG_443811_ADDR, v)
+#define VIDC_REG_443811_OUTM(m, v)                  \
+do { \
+	out_dword_masked_ns(VIDC_REG_443811_ADDR, m, v, \
+			VIDC_REG_443811_IN); \
+} while (0)
+#define VIDC_REG_443811_POST_ON_BMSK                      0x1
+#define VIDC_REG_443811_POST_ON_SHFT                        0
+
+#define VIDC_REG_538267_ADDR                    \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000128)
+#define VIDC_REG_538267_PHYS                    \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000128)
+#define VIDC_REG_538267_RMSK                    0xffffffff
+#define VIDC_REG_538267_SHFT                             0
+#define VIDC_REG_538267_IN                      \
+	in_dword_masked(VIDC_REG_538267_ADDR,  \
+			VIDC_REG_538267_RMSK)
+#define VIDC_REG_538267_INM(m)                  \
+	in_dword_masked(VIDC_REG_538267_ADDR,  m)
+#define VIDC_REG_538267_OUT(v)                  \
+	out_dword(VIDC_REG_538267_ADDR, v)
+#define VIDC_REG_538267_OUTM(m, v)               \
+do { \
+	out_dword_masked_ns(VIDC_REG_538267_ADDR, m, v, \
+			VIDC_REG_538267_IN); \
+} while (0)
+#define VIDC_REG_538267_QUOTIENT_VAL_BMSK       0xffff0000
+#define VIDC_REG_538267_QUOTIENT_VAL_SHFT             0x10
+#define VIDC_REG_538267_REMAINDER_VAL_BMSK          0xffff
+#define VIDC_REG_538267_REMAINDER_VAL_SHFT               0
+
+#define VIDC_REG_661565_ADDR                \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x0000012c)
+#define VIDC_REG_661565_PHYS                \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000012c)
+#define VIDC_REG_661565_RMSK                       0x1
+#define VIDC_REG_661565_SHFT                         0
+#define VIDC_REG_661565_IN                  \
+	in_dword_masked(VIDC_REG_661565_ADDR,  \
+			VIDC_REG_661565_RMSK)
+#define VIDC_REG_661565_INM(m)              \
+	in_dword_masked(VIDC_REG_661565_ADDR,  m)
+#define VIDC_REG_661565_OUT(v)              \
+	out_dword(VIDC_REG_661565_ADDR, v)
+#define VIDC_REG_661565_OUTM(m, v)           \
+do { \
+	out_dword_masked_ns(VIDC_REG_661565_ADDR, m, v, \
+			VIDC_REG_661565_IN); \
+} while (0)
+#define VIDC_REG_661565_SEQUENCE_START_BMSK        0x1
+#define VIDC_REG_661565_SEQUENCE_START_SHFT          0
+
+#define VIDC_REG_141269_ADDR                      \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000130)
+#define VIDC_REG_141269_PHYS                      \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000130)
+#define VIDC_REG_141269_RMSK                             0x1
+#define VIDC_REG_141269_SHFT                               0
+#define VIDC_REG_141269_IN                        \
+	in_dword_masked(VIDC_REG_141269_ADDR,         \
+	VIDC_REG_141269_RMSK)
+#define VIDC_REG_141269_INM(m)                    \
+	in_dword_masked(VIDC_REG_141269_ADDR,  m)
+#define VIDC_REG_141269_OUT(v)                    \
+	out_dword(VIDC_REG_141269_ADDR, v)
+#define VIDC_REG_141269_OUTM(m, v)                 \
+do { \
+	out_dword_masked_ns(VIDC_REG_141269_ADDR, m, v, \
+			VIDC_REG_141269_IN); \
+} while (0)
+#define VIDC_REG_141269_SW_RESET_BMSK                    0x1
+#define VIDC_REG_141269_SW_RESET_SHFT                      0
+
+#define VIDC_REG_193553_ADDR                      \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000134)
+#define VIDC_REG_193553_PHYS                      \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000134)
+#define VIDC_REG_193553_RMSK                             0x1
+#define VIDC_REG_193553_SHFT                               0
+#define VIDC_REG_193553_IN                        \
+	in_dword_masked(VIDC_REG_193553_ADDR,         \
+	VIDC_REG_193553_RMSK)
+#define VIDC_REG_193553_INM(m)                    \
+	in_dword_masked(VIDC_REG_193553_ADDR,  m)
+#define VIDC_REG_193553_OUT(v)                    \
+	out_dword(VIDC_REG_193553_ADDR, v)
+#define VIDC_REG_193553_OUTM(m, v)                 \
+do { \
+	out_dword_masked_ns(VIDC_REG_193553_ADDR, m, v, \
+			VIDC_REG_193553_IN); \
+} while (0)
+#define VIDC_REG_193553_FW_START_BMSK                    0x1
+#define VIDC_REG_193553_FW_START_SHFT                      0
+
+#define VIDC_REG_215724_ADDR                    \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000138)
+#define VIDC_REG_215724_PHYS                    \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000138)
+#define VIDC_REG_215724_RMSK                           0x1
+#define VIDC_REG_215724_SHFT                             0
+#define VIDC_REG_215724_IN                      \
+	in_dword_masked(VIDC_REG_215724_ADDR,  \
+			VIDC_REG_215724_RMSK)
+#define VIDC_REG_215724_INM(m)                  \
+	in_dword_masked(VIDC_REG_215724_ADDR,  m)
+#define VIDC_REG_215724_OUT(v)                  \
+	out_dword(VIDC_REG_215724_ADDR, v)
+#define VIDC_REG_215724_OUTM(m, v)               \
+do { \
+	out_dword_masked_ns(VIDC_REG_215724_ADDR, m, v, \
+			VIDC_REG_215724_IN); \
+} while (0)
+#define VIDC_REG_215724_ARM_ENDIAN_BMSK                0x1
+#define VIDC_REG_215724_ARM_ENDIAN_SHFT                  0
+
+#define VIDC_REG_846346_ADDR                      \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x0000013c)
+#define VIDC_REG_846346_PHYS                      \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000013c)
+#define VIDC_REG_846346_RMSK                             0x1
+#define VIDC_REG_846346_SHFT                               0
+#define VIDC_REG_846346_IN                        \
+	in_dword_masked(VIDC_REG_846346_ADDR,         \
+	VIDC_REG_846346_RMSK)
+#define VIDC_REG_846346_INM(m)                    \
+	in_dword_masked(VIDC_REG_846346_ADDR,  m)
+#define VIDC_REG_846346_OUT(v)                    \
+	out_dword(VIDC_REG_846346_ADDR, v)
+#define VIDC_REG_846346_OUTM(m, v)                 \
+do { \
+	out_dword_masked_ns(VIDC_REG_846346_ADDR, m, v, \
+			VIDC_REG_846346_IN); \
+} while (0)
+#define VIDC_REG_846346_ERR_CTRL_BMSK                    0x1
+#define VIDC_REG_846346_ERR_CTRL_SHFT                      0
+
+#define VIDC_REG_765787_ADDR                 \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000200)
+#define VIDC_REG_765787_PHYS                 \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000200)
+#define VIDC_REG_765787_RMSK                 0xffffffff
+#define VIDC_REG_765787_SHFT                          0
+#define VIDC_REG_765787_IN                   \
+	in_dword_masked(VIDC_REG_765787_ADDR,  \
+			VIDC_REG_765787_RMSK)
+#define VIDC_REG_765787_INM(m)               \
+	in_dword_masked(VIDC_REG_765787_ADDR,  m)
+#define VIDC_REG_765787_OUT(v)               \
+	out_dword(VIDC_REG_765787_ADDR, v)
+#define VIDC_REG_765787_OUTM(m, v)            \
+do { \
+	out_dword_masked_ns(VIDC_REG_765787_ADDR, m, v, \
+			VIDC_REG_765787_IN); \
+} while (0)
+#define VIDC_REG_765787_FW_STT_ADDR_0_BMSK   0xffffffff
+#define VIDC_REG_765787_FW_STT_ADDR_0_SHFT            0
+
+#define VIDC_REG_225040_ADDR                 \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000204)
+#define VIDC_REG_225040_PHYS                 \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000204)
+#define VIDC_REG_225040_RMSK                 0xffffffff
+#define VIDC_REG_225040_SHFT                          0
+#define VIDC_REG_225040_IN                   \
+	in_dword_masked(VIDC_REG_225040_ADDR,  \
+			VIDC_REG_225040_RMSK)
+#define VIDC_REG_225040_INM(m)               \
+	in_dword_masked(VIDC_REG_225040_ADDR,  m)
+#define VIDC_REG_225040_OUT(v)               \
+	out_dword(VIDC_REG_225040_ADDR, v)
+#define VIDC_REG_225040_OUTM(m, v)            \
+do { \
+	out_dword_masked_ns(VIDC_REG_225040_ADDR, m, v, \
+			VIDC_REG_225040_IN); \
+} while (0)
+#define VIDC_REG_225040_FW_STT_ADDR_1_BMSK   0xffffffff
+#define VIDC_REG_225040_FW_STT_ADDR_1_SHFT            0
+
+#define VIDC_REG_942456_ADDR                 \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000208)
+#define VIDC_REG_942456_PHYS                 \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000208)
+#define VIDC_REG_942456_RMSK                 0xffffffff
+#define VIDC_REG_942456_SHFT                          0
+#define VIDC_REG_942456_IN                   \
+	in_dword_masked(VIDC_REG_942456_ADDR,  \
+			VIDC_REG_942456_RMSK)
+#define VIDC_REG_942456_INM(m)               \
+	in_dword_masked(VIDC_REG_942456_ADDR,  m)
+#define VIDC_REG_942456_OUT(v)               \
+	out_dword(VIDC_REG_942456_ADDR, v)
+#define VIDC_REG_942456_OUTM(m, v)            \
+do { \
+	out_dword_masked_ns(VIDC_REG_942456_ADDR, m, v, \
+			VIDC_REG_942456_IN); \
+} while (0)
+#define VIDC_REG_942456_FW_STT_ADDR_2_BMSK   0xffffffff
+#define VIDC_REG_942456_FW_STT_ADDR_2_SHFT            0
+
+#define VIDC_REG_942170_ADDR_3_ADDR                 \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x0000020c)
+#define VIDC_REG_942170_ADDR_3_PHYS                 \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000020c)
+#define VIDC_REG_942170_ADDR_3_RMSK                 0xffffffff
+#define VIDC_REG_942170_ADDR_3_SHFT                          0
+#define VIDC_REG_942170_ADDR_3_IN                   \
+	in_dword_masked(VIDC_REG_942170_ADDR_3_ADDR,  \
+			VIDC_REG_942170_ADDR_3_RMSK)
+#define VIDC_REG_942170_ADDR_3_INM(m)               \
+	in_dword_masked(VIDC_REG_942170_ADDR_3_ADDR,  m)
+#define VIDC_REG_942170_ADDR_3_OUT(v)               \
+	out_dword(VIDC_REG_942170_ADDR_3_ADDR, v)
+#define VIDC_REG_942170_ADDR_3_OUTM(m, v)            \
+do { \
+	out_dword_masked_ns(VIDC_REG_942170_ADDR_3_ADDR, m, v, \
+			VIDC_REG_942170_ADDR_3_IN); \
+} while (0)
+#define VIDC_REG_942170_ADDR_3_FW_STT_ADDR_3_BMSK   0xffffffff
+#define VIDC_REG_942170_ADDR_3_FW_STT_ADDR_3_SHFT            0
+
+#define VIDC_REG_880188_ADDR                 \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000210)
+#define VIDC_REG_880188_PHYS                 \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000210)
+#define VIDC_REG_880188_RMSK                 0xffffffff
+#define VIDC_REG_880188_SHFT                          0
+#define VIDC_REG_880188_IN                   \
+	in_dword_masked(VIDC_REG_880188_ADDR,  \
+			VIDC_REG_880188_RMSK)
+#define VIDC_REG_880188_INM(m)               \
+	in_dword_masked(VIDC_REG_880188_ADDR,  m)
+#define VIDC_REG_880188_OUT(v)               \
+	out_dword(VIDC_REG_880188_ADDR, v)
+#define VIDC_REG_880188_OUTM(m, v)            \
+do { \
+	out_dword_masked_ns(VIDC_REG_880188_ADDR, m, v, \
+			VIDC_REG_880188_IN); \
+} while (0)
+#define VIDC_REG_880188_FW_STT_ADDR_4_BMSK   0xffffffff
+#define VIDC_REG_880188_FW_STT_ADDR_4_SHFT            0
+
+#define VIDC_REG_40293_ADDR                 \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000214)
+#define VIDC_REG_40293_PHYS                 \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000214)
+#define VIDC_REG_40293_RMSK                 0xffffffff
+#define VIDC_REG_40293_SHFT                          0
+#define VIDC_REG_40293_IN                   \
+	in_dword_masked(VIDC_REG_40293_ADDR,  \
+			VIDC_REG_40293_RMSK)
+#define VIDC_REG_40293_INM(m)               \
+	in_dword_masked(VIDC_REG_40293_ADDR,  m)
+#define VIDC_REG_40293_OUT(v)               \
+	out_dword(VIDC_REG_40293_ADDR, v)
+#define VIDC_REG_40293_OUTM(m, v)            \
+do { \
+	out_dword_masked_ns(VIDC_REG_40293_ADDR, m, v, \
+			VIDC_REG_40293_IN); \
+} while (0)
+#define VIDC_REG_40293_FW_STT_ADDR_5_BMSK   0xffffffff
+#define VIDC_REG_40293_FW_STT_ADDR_5_SHFT            0
+
+#define VIDC_REG_942170_ADDR_6_ADDR                 \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000218)
+#define VIDC_REG_942170_ADDR_6_PHYS                 \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000218)
+#define VIDC_REG_942170_ADDR_6_RMSK                 0xffffffff
+#define VIDC_REG_942170_ADDR_6_SHFT                          0
+#define VIDC_REG_942170_ADDR_6_IN                   \
+	in_dword_masked(VIDC_REG_942170_ADDR_6_ADDR,  \
+			VIDC_REG_942170_ADDR_6_RMSK)
+#define VIDC_REG_942170_ADDR_6_INM(m)               \
+	in_dword_masked(VIDC_REG_942170_ADDR_6_ADDR,  m)
+#define VIDC_REG_942170_ADDR_6_OUT(v)               \
+	out_dword(VIDC_REG_942170_ADDR_6_ADDR, v)
+#define VIDC_REG_942170_ADDR_6_OUTM(m, v)            \
+do { \
+	out_dword_masked_ns(VIDC_REG_942170_ADDR_6_ADDR, m, v, \
+			VIDC_REG_942170_ADDR_6_IN); \
+} while (0)
+#define VIDC_REG_942170_ADDR_6_FW_STT_ADDR_6_BMSK   0xffffffff
+#define VIDC_REG_942170_ADDR_6_FW_STT_ADDR_6_SHFT            0
+
+#define VIDC_REG_958768_ADDR                  \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000230)
+#define VIDC_REG_958768_PHYS                  \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000230)
+#define VIDC_REG_958768_RMSK                  0xffffffff
+#define VIDC_REG_958768_SHFT                           0
+#define VIDC_REG_958768_IN                    \
+	in_dword_masked(VIDC_REG_958768_ADDR,  \
+			VIDC_REG_958768_RMSK)
+#define VIDC_REG_958768_INM(m)                \
+	in_dword_masked(VIDC_REG_958768_ADDR,  m)
+#define VIDC_REG_958768_OUT(v)                \
+	out_dword(VIDC_REG_958768_ADDR, v)
+#define VIDC_REG_958768_OUTM(m, v)             \
+do { \
+	out_dword_masked_ns(VIDC_REG_958768_ADDR, m, v, \
+			VIDC_REG_958768_IN); \
+} while (0)
+#define VIDC_REG_699384_ADDR_BMSK     0xffffffff
+#define VIDC_REG_699384_ADDR_SHFT              0
+
+#define VIDC_REG_979942_ADDR                   \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000234)
+#define VIDC_REG_979942_PHYS                   \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000234)
+#define VIDC_REG_979942_RMSK                   0xffffffff
+#define VIDC_REG_979942_SHFT                            0
+#define VIDC_REG_979942_IN                     \
+	in_dword_masked(VIDC_REG_979942_ADDR,  \
+			VIDC_REG_979942_RMSK)
+#define VIDC_REG_979942_INM(m)                 \
+	in_dword_masked(VIDC_REG_979942_ADDR,  m)
+#define VIDC_REG_979942_OUT(v)                 \
+	out_dword(VIDC_REG_979942_ADDR, v)
+#define VIDC_REG_979942_OUTM(m, v)              \
+do { \
+	out_dword_masked_ns(VIDC_REG_979942_ADDR, m, v, \
+			VIDC_REG_979942_IN); \
+} while (0)
+#define VIDC_REG_979942_DB_STT_ADDR_BMSK       0xffffffff
+#define VIDC_REG_979942_DB_STT_ADDR_SHFT                0
+
+#define VIDC_REG_839021_ADDR                       \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000300)
+#define VIDC_REG_839021_PHYS                       \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000300)
+#define VIDC_REG_839021_RMSK                           0xff1f
+#define VIDC_REG_839021_SHFT                                0
+#define VIDC_REG_839021_IN                         \
+	in_dword_masked(VIDC_REG_839021_ADDR,  VIDC_REG_839021_RMSK)
+#define VIDC_REG_839021_INM(m)                     \
+	in_dword_masked(VIDC_REG_839021_ADDR,  m)
+#define VIDC_REG_839021_OUT(v)                     \
+	out_dword(VIDC_REG_839021_ADDR, v)
+#define VIDC_REG_839021_OUTM(m, v)                  \
+do { \
+	out_dword_masked_ns(VIDC_REG_839021_ADDR, m, v, \
+			VIDC_REG_839021_IN); \
+} while (0)
+#define VIDC_REG_839021_LEVEL_BMSK                     0xff00
+#define VIDC_REG_839021_LEVEL_SHFT                        0x8
+#define VIDC_REG_839021_PROFILE_BMSK                     0x1f
+#define VIDC_REG_839021_PROFILE_SHFT                        0
+
+#define VIDC_REG_950374_ADDR                      \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000308)
+#define VIDC_REG_950374_PHYS                      \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000308)
+#define VIDC_REG_950374_RMSK                          0xffff
+#define VIDC_REG_950374_SHFT                               0
+#define VIDC_REG_950374_IN                        \
+	in_dword_masked(VIDC_REG_950374_ADDR,         \
+	VIDC_REG_950374_RMSK)
+#define VIDC_REG_950374_INM(m)                    \
+	in_dword_masked(VIDC_REG_950374_ADDR,  m)
+#define VIDC_REG_950374_OUT(v)                    \
+	out_dword(VIDC_REG_950374_ADDR, v)
+#define VIDC_REG_950374_OUTM(m, v)                 \
+do { \
+	out_dword_masked_ns(VIDC_REG_950374_ADDR, m, v, \
+			VIDC_REG_950374_IN); \
+} while (0)
+#define VIDC_REG_950374_I_PERIOD_BMSK                 0xffff
+#define VIDC_REG_950374_I_PERIOD_SHFT                      0
+
+#define VIDC_REG_504878_ADDR               \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000310)
+#define VIDC_REG_504878_PHYS               \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000310)
+#define VIDC_REG_504878_RMSK                      0xd
+#define VIDC_REG_504878_SHFT                        0
+#define VIDC_REG_504878_IN                 \
+	in_dword_masked(VIDC_REG_504878_ADDR,  \
+			VIDC_REG_504878_RMSK)
+#define VIDC_REG_504878_INM(m)             \
+	in_dword_masked(VIDC_REG_504878_ADDR,  m)
+#define VIDC_REG_504878_OUT(v)             \
+	out_dword(VIDC_REG_504878_ADDR, v)
+#define VIDC_REG_504878_OUTM(m, v)          \
+do { \
+	out_dword_masked_ns(VIDC_REG_504878_ADDR, m, v, \
+			VIDC_REG_504878_IN); \
+} while (0)
+#define VIDC_REG_504878_FIXED_NUMBER_BMSK         0xc
+#define VIDC_REG_504878_FIXED_NUMBER_SHFT         0x2
+#define VIDC_REG_504878_ENTROPY_SEL_BMSK          0x1
+#define VIDC_REG_504878_ENTROPY_SEL_SHFT            0
+
+#define VIDC_REG_458130_ADDR            \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000314)
+#define VIDC_REG_458130_PHYS            \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000314)
+#define VIDC_REG_458130_RMSK                 0xfff
+#define VIDC_REG_458130_SHFT                     0
+#define VIDC_REG_458130_IN              \
+	in_dword_masked(VIDC_REG_458130_ADDR,  \
+			VIDC_REG_458130_RMSK)
+#define VIDC_REG_458130_INM(m)          \
+	in_dword_masked(VIDC_REG_458130_ADDR,  m)
+#define VIDC_REG_458130_OUT(v)          \
+	out_dword(VIDC_REG_458130_ADDR, v)
+#define VIDC_REG_458130_OUTM(m, v)       \
+do { \
+	out_dword_masked_ns(VIDC_REG_458130_ADDR, m, v, \
+			VIDC_REG_458130_IN); \
+} while (0)
+#define VIDC_REG_458130_SLICE_ALPHA_C0_OFFSET_DIV2_BMSK      \
+	0xf80
+#define VIDC_REG_458130_SLICE_ALPHA_C0_OFFSET_DIV2_SHFT      \
+	0x7
+#define VIDC_REG_458130_SLICE_BETA_OFFSET_DIV2_BMSK       0x7c
+#define VIDC_REG_458130_SLICE_BETA_OFFSET_DIV2_SHFT        0x2
+#define \
+	\
+VIDC_REG_458130_DISABLE_DEBLOCKING_FILTER_IDC_BMSK        0x3
+#define \
+	\
+VIDC_REG_458130_DISABLE_DEBLOCKING_FILTER_IDC_SHFT          0
+
+#define VIDC_REG_314290_ADDR                   \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000318)
+#define VIDC_REG_314290_PHYS                   \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000318)
+#define VIDC_REG_314290_RMSK                          0x1
+#define VIDC_REG_314290_SHFT                            0
+#define VIDC_REG_314290_IN                     \
+	in_dword_masked(VIDC_REG_314290_ADDR,  \
+			VIDC_REG_314290_RMSK)
+#define VIDC_REG_314290_INM(m)                 \
+	in_dword_masked(VIDC_REG_314290_ADDR,  m)
+#define VIDC_REG_314290_OUT(v)                 \
+	out_dword(VIDC_REG_314290_ADDR, v)
+#define VIDC_REG_314290_OUTM(m, v)              \
+do { \
+	out_dword_masked_ns(VIDC_REG_314290_ADDR, m, v, \
+			VIDC_REG_314290_IN); \
+} while (0)
+#define VIDC_REG_314290_SHORT_HD_ON_BMSK              0x1
+#define VIDC_REG_314290_SHORT_HD_ON_SHFT                0
+
+#define VIDC_REG_588301_ADDR                    \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x0000031c)
+#define VIDC_REG_588301_PHYS                    \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000031c)
+#define VIDC_REG_588301_RMSK                           0x1
+#define VIDC_REG_588301_SHFT                             0
+#define VIDC_REG_588301_IN                      \
+	in_dword_masked(VIDC_REG_588301_ADDR,  \
+			VIDC_REG_588301_RMSK)
+#define VIDC_REG_588301_INM(m)                  \
+	in_dword_masked(VIDC_REG_588301_ADDR,  m)
+#define VIDC_REG_588301_OUT(v)                  \
+	out_dword(VIDC_REG_588301_ADDR, v)
+#define VIDC_REG_588301_OUTM(m, v)               \
+do { \
+	out_dword_masked_ns(VIDC_REG_588301_ADDR, m, v, \
+			VIDC_REG_588301_IN); \
+} while (0)
+#define VIDC_REG_588301_MSLICE_ENA_BMSK                0x1
+#define VIDC_REG_588301_MSLICE_ENA_SHFT                  0
+
+#define VIDC_REG_1517_ADDR                    \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000320)
+#define VIDC_REG_1517_PHYS                    \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000320)
+#define VIDC_REG_1517_RMSK                           0x3
+#define VIDC_REG_1517_SHFT                             0
+#define VIDC_REG_1517_IN                      \
+	in_dword_masked(VIDC_REG_1517_ADDR,  \
+			VIDC_REG_1517_RMSK)
+#define VIDC_REG_1517_INM(m)                  \
+	in_dword_masked(VIDC_REG_1517_ADDR,  m)
+#define VIDC_REG_1517_OUT(v)                  \
+	out_dword(VIDC_REG_1517_ADDR, v)
+#define VIDC_REG_1517_OUTM(m, v)               \
+do { \
+	out_dword_masked_ns(VIDC_REG_1517_ADDR, m, v, \
+			VIDC_REG_1517_IN); \
+} while (0)
+#define VIDC_REG_1517_MSLICE_SEL_BMSK                0x3
+#define VIDC_REG_1517_MSLICE_SEL_SHFT                  0
+
+#define VIDC_REG_105335_ADDR                     \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000324)
+#define VIDC_REG_105335_PHYS                     \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000324)
+#define VIDC_REG_105335_RMSK                     0xffffffff
+#define VIDC_REG_105335_SHFT                              0
+#define VIDC_REG_105335_IN                       \
+	in_dword_masked(VIDC_REG_105335_ADDR,        \
+	VIDC_REG_105335_RMSK)
+#define VIDC_REG_105335_INM(m)                   \
+	in_dword_masked(VIDC_REG_105335_ADDR,  m)
+#define VIDC_REG_105335_OUT(v)                   \
+	out_dword(VIDC_REG_105335_ADDR, v)
+#define VIDC_REG_105335_OUTM(m, v)                \
+do { \
+	out_dword_masked_ns(VIDC_REG_105335_ADDR, m, v, \
+			VIDC_REG_105335_IN); \
+} while (0)
+#define VIDC_REG_105335_MSLICE_MB_BMSK           0xffffffff
+#define VIDC_REG_105335_MSLICE_MB_SHFT                    0
+
+#define VIDC_REG_561679_ADDR                   \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000328)
+#define VIDC_REG_561679_PHYS                   \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000328)
+#define VIDC_REG_561679_RMSK                   0xffffffff
+#define VIDC_REG_561679_SHFT                            0
+#define VIDC_REG_561679_IN                     \
+	in_dword_masked(VIDC_REG_561679_ADDR,  \
+			VIDC_REG_561679_RMSK)
+#define VIDC_REG_561679_INM(m)                 \
+	in_dword_masked(VIDC_REG_561679_ADDR,  m)
+#define VIDC_REG_561679_OUT(v)                 \
+	out_dword(VIDC_REG_561679_ADDR, v)
+#define VIDC_REG_561679_OUTM(m, v)              \
+do { \
+	out_dword_masked_ns(VIDC_REG_561679_ADDR, m, v, \
+			VIDC_REG_561679_IN); \
+} while (0)
+#define VIDC_REG_561679_MSLICE_BYTE_BMSK       0xffffffff
+#define VIDC_REG_561679_MSLICE_BYTE_SHFT                0
+
+#define VIDC_REG_151345_ADDR                 \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000400)
+#define VIDC_REG_151345_PHYS                 \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000400)
+#define VIDC_REG_151345_RMSK                 0xffffffff
+#define VIDC_REG_151345_SHFT                          0
+#define VIDC_REG_151345_IN                   \
+	in_dword_masked(VIDC_REG_151345_ADDR,  \
+			VIDC_REG_151345_RMSK)
+#define VIDC_REG_151345_INM(m)               \
+	in_dword_masked(VIDC_REG_151345_ADDR,  m)
+#define VIDC_REG_151345_DISPLAY_Y_ADR_BMSK   0xffffffff
+#define VIDC_REG_151345_DISPLAY_Y_ADR_SHFT            0
+
+#define VIDC_REG_293983_ADDR                 \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000404)
+#define VIDC_REG_293983_PHYS                 \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000404)
+#define VIDC_REG_293983_RMSK                 0xffffffff
+#define VIDC_REG_293983_SHFT                          0
+#define VIDC_REG_293983_IN                   \
+	in_dword_masked(VIDC_REG_293983_ADDR,  \
+			VIDC_REG_293983_RMSK)
+#define VIDC_REG_293983_INM(m)               \
+	in_dword_masked(VIDC_REG_293983_ADDR,  m)
+#define VIDC_REG_293983_DISPLAY_C_ADR_BMSK   0xffffffff
+#define VIDC_REG_293983_DISPLAY_C_ADR_SHFT            0
+
+#define VIDC_REG_612715_ADDR                \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000408)
+#define VIDC_REG_612715_PHYS                \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000408)
+#define VIDC_REG_612715_RMSK                      0x3f
+#define VIDC_REG_612715_SHFT                         0
+#define VIDC_REG_612715_IN                  \
+	in_dword_masked(VIDC_REG_612715_ADDR,  \
+			VIDC_REG_612715_RMSK)
+#define VIDC_REG_612715_INM(m)              \
+	in_dword_masked(VIDC_REG_612715_ADDR,  m)
+#define VIDC_REG_612715_DISPLAY_STATUS_BMSK       0x3f
+#define VIDC_REG_612715_DISPLAY_STATUS_SHFT          0
+
+#define VIDC_REG_209364_ADDR                   \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x0000040c)
+#define VIDC_REG_209364_PHYS                   \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000040c)
+#define VIDC_REG_209364_RMSK                          0x1
+#define VIDC_REG_209364_SHFT                            0
+#define VIDC_REG_209364_IN                     \
+	in_dword_masked(VIDC_REG_209364_ADDR,  \
+			VIDC_REG_209364_RMSK)
+#define VIDC_REG_209364_INM(m)                 \
+	in_dword_masked(VIDC_REG_209364_ADDR,  m)
+#define VIDC_REG_209364_HEADER_DONE_BMSK              0x1
+#define VIDC_REG_209364_HEADER_DONE_SHFT                0
+
+#define VIDC_REG_757835_ADDR                     \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000410)
+#define VIDC_REG_757835_PHYS                     \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000410)
+#define VIDC_REG_757835_RMSK                     0xffffffff
+#define VIDC_REG_757835_SHFT                              0
+#define VIDC_REG_757835_IN                       \
+	in_dword_masked(VIDC_REG_757835_ADDR,        \
+	VIDC_REG_757835_RMSK)
+#define VIDC_REG_757835_INM(m)                   \
+	in_dword_masked(VIDC_REG_757835_ADDR,  m)
+#define VIDC_REG_757835_FRAME_NUM_BMSK           0xffffffff
+#define VIDC_REG_757835_FRAME_NUM_SHFT                    0
+
+#define VIDC_REG_352831_ADDR              \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000414)
+#define VIDC_REG_352831_PHYS              \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000414)
+#define VIDC_REG_352831_RMSK              0xffffffff
+#define VIDC_REG_352831_SHFT                       0
+#define VIDC_REG_352831_IN                \
+	in_dword_masked(VIDC_REG_352831_ADDR,  \
+			VIDC_REG_352831_RMSK)
+#define VIDC_REG_352831_INM(m)            \
+	in_dword_masked(VIDC_REG_352831_ADDR,  m)
+#define VIDC_REG_352831_DBG_INFO_OUTPUT0_BMSK 0xffffffff
+#define VIDC_REG_352831_DBG_INFO_OUTPUT0_SHFT          0
+
+#define VIDC_REG_668634_ADDR              \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000418)
+#define VIDC_REG_668634_PHYS              \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000418)
+#define VIDC_REG_668634_RMSK              0xffffffff
+#define VIDC_REG_668634_SHFT                       0
+#define VIDC_REG_668634_IN                \
+	in_dword_masked(VIDC_REG_668634_ADDR,  \
+			VIDC_REG_668634_RMSK)
+#define VIDC_REG_668634_INM(m)            \
+	in_dword_masked(VIDC_REG_668634_ADDR,  m)
+#define VIDC_REG_668634_DBG_INFO_OUTPUT1_BMSK 0xffffffff
+#define VIDC_REG_668634_DBG_INFO_OUTPUT1_SHFT          0
+
+#define VIDC_REG_609676_ADDR                       \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000500)
+#define VIDC_REG_609676_PHYS                       \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000500)
+#define VIDC_REG_609676_RMSK                              0x1
+#define VIDC_REG_609676_SHFT                                0
+#define VIDC_REG_609676_IN                         \
+	in_dword_masked(VIDC_REG_609676_ADDR,  VIDC_REG_609676_RMSK)
+#define VIDC_REG_609676_INM(m)                     \
+	in_dword_masked(VIDC_REG_609676_ADDR,  m)
+#define VIDC_REG_609676_OUT(v)                     \
+	out_dword(VIDC_REG_609676_ADDR, v)
+#define VIDC_REG_609676_OUTM(m, v)                  \
+do { \
+	out_dword_masked_ns(VIDC_REG_609676_ADDR, m, v, \
+			VIDC_REG_609676_IN); \
+} while (0)
+#define VIDC_REG_609676_INT_OFF_BMSK                      0x1
+#define VIDC_REG_609676_INT_OFF_SHFT                        0
+
+#define VIDC_REG_491082_ADDR                 \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000504)
+#define VIDC_REG_491082_PHYS                 \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000504)
+#define VIDC_REG_491082_RMSK                        0x1
+#define VIDC_REG_491082_SHFT                          0
+#define VIDC_REG_491082_IN                   \
+	in_dword_masked(VIDC_REG_491082_ADDR,  \
+			VIDC_REG_491082_RMSK)
+#define VIDC_REG_491082_INM(m)               \
+	in_dword_masked(VIDC_REG_491082_ADDR,  m)
+#define VIDC_REG_491082_OUT(v)               \
+	out_dword(VIDC_REG_491082_ADDR, v)
+#define VIDC_REG_491082_OUTM(m, v)            \
+do { \
+	out_dword_masked_ns(VIDC_REG_491082_ADDR, m, v, \
+			VIDC_REG_491082_IN); \
+} while (0)
+#define VIDC_REG_491082_INT_PULSE_SEL_BMSK          0x1
+#define VIDC_REG_491082_INT_PULSE_SEL_SHFT            0
+
+#define VIDC_REG_614776_ADDR                \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000508)
+#define VIDC_REG_614776_PHYS                \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000508)
+#define VIDC_REG_614776_RMSK                       0x1
+#define VIDC_REG_614776_SHFT                         0
+#define VIDC_REG_614776_IN                  \
+	in_dword_masked(VIDC_REG_614776_ADDR,  \
+			VIDC_REG_614776_RMSK)
+#define VIDC_REG_614776_INM(m)              \
+	in_dword_masked(VIDC_REG_614776_ADDR,  m)
+#define VIDC_REG_614776_OUT(v)              \
+	out_dword(VIDC_REG_614776_ADDR, v)
+#define VIDC_REG_614776_OUTM(m, v)           \
+do { \
+	out_dword_masked_ns(VIDC_REG_614776_ADDR, m, v, \
+			VIDC_REG_614776_IN); \
+} while (0)
+#define VIDC_REG_614776_INT_DONE_CLEAR_BMSK        0x1
+#define VIDC_REG_614776_INT_DONE_CLEAR_SHFT          0
+
+#define VIDC_REG_982553_ADDR                \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x0000050c)
+#define VIDC_REG_982553_PHYS                \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000050c)
+#define VIDC_REG_982553_RMSK                       0x1
+#define VIDC_REG_982553_SHFT                         0
+#define VIDC_REG_982553_IN                  \
+	in_dword_masked(VIDC_REG_982553_ADDR,  \
+			VIDC_REG_982553_RMSK)
+#define VIDC_REG_982553_INM(m)              \
+	in_dword_masked(VIDC_REG_982553_ADDR,  m)
+#define VIDC_REG_982553_OPERATION_DONE_BMSK        0x1
+#define VIDC_REG_982553_OPERATION_DONE_SHFT          0
+
+#define VIDC_REG_259967_ADDR                       \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000510)
+#define VIDC_REG_259967_PHYS                       \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000510)
+#define VIDC_REG_259967_RMSK                              0x1
+#define VIDC_REG_259967_SHFT                                0
+#define VIDC_REG_259967_IN                         \
+	in_dword_masked(VIDC_REG_259967_ADDR,  VIDC_REG_259967_RMSK)
+#define VIDC_REG_259967_INM(m)                     \
+	in_dword_masked(VIDC_REG_259967_ADDR,  m)
+#define VIDC_REG_259967_FW_DONE_BMSK                      0x1
+#define VIDC_REG_259967_FW_DONE_SHFT                        0
+
+#define VIDC_REG_512143_ADDR                    \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000514)
+#define VIDC_REG_512143_PHYS                    \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000514)
+#define VIDC_REG_512143_RMSK                         0x1f8
+#define VIDC_REG_512143_SHFT                             0
+#define VIDC_REG_512143_IN                      \
+	in_dword_masked(VIDC_REG_512143_ADDR,  \
+			VIDC_REG_512143_RMSK)
+#define VIDC_REG_512143_INM(m)                  \
+	in_dword_masked(VIDC_REG_512143_ADDR,  m)
+#define VIDC_REG_512143_FRAME_DONE_STAT_BMSK         0x100
+#define VIDC_REG_512143_FRAME_DONE_STAT_SHFT           0x8
+#define VIDC_REG_512143_DMA_DONE_STAT_BMSK            0x80
+#define VIDC_REG_512143_DMA_DONE_STAT_SHFT             0x7
+#define VIDC_REG_512143_HEADER_DONE_STAT_BMSK         0x40
+#define VIDC_REG_512143_HEADER_DONE_STAT_SHFT          0x6
+#define VIDC_REG_512143_FW_DONE_STAT_BMSK             0x20
+#define VIDC_REG_512143_FW_DONE_STAT_SHFT              0x5
+#define VIDC_REG_512143_OPERATION_FAILED_BMSK         0x10
+#define VIDC_REG_512143_OPERATION_FAILED_SHFT          0x4
+#define VIDC_REG_512143_STREAM_HDR_CHANGED_BMSK        0x8
+#define VIDC_REG_512143_STREAM_HDR_CHANGED_SHFT        0x3
+
+#define VIDC_REG_418173_ADDR                \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000518)
+#define VIDC_REG_418173_PHYS                \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000518)
+#define VIDC_REG_418173_RMSK                     0x1fa
+#define VIDC_REG_418173_SHFT                         0
+#define VIDC_REG_418173_IN                  \
+	in_dword_masked(VIDC_REG_418173_ADDR,  \
+			VIDC_REG_418173_RMSK)
+#define VIDC_REG_418173_INM(m)              \
+	in_dword_masked(VIDC_REG_418173_ADDR,  m)
+#define VIDC_REG_418173_OUT(v)              \
+	out_dword(VIDC_REG_418173_ADDR, v)
+#define VIDC_REG_418173_OUTM(m, v)           \
+do { \
+	out_dword_masked_ns(VIDC_REG_418173_ADDR, m, v, \
+			VIDC_REG_418173_IN); \
+} while (0)
+#define VIDC_REG_418173_FRAME_DONE_ENABLE_BMSK      0x100
+#define VIDC_REG_418173_FRAME_DONE_ENABLE_SHFT        0x8
+#define VIDC_REG_418173_DMA_DONE_ENABLE_BMSK       0x80
+#define VIDC_REG_418173_DMA_DONE_ENABLE_SHFT        0x7
+#define VIDC_REG_418173_HEADER_DONE_ENABLE_BMSK       0x40
+#define VIDC_REG_418173_HEADER_DONE_ENABLE_SHFT        0x6
+#define VIDC_REG_418173_FW_DONE_ENABLE_BMSK       0x20
+#define VIDC_REG_418173_FW_DONE_ENABLE_SHFT        0x5
+#define VIDC_REG_418173_OPERATION_FAILED_ENABLE_BMSK       0x10
+#define VIDC_REG_418173_OPERATION_FAILED_ENABLE_SHFT        0x4
+#define VIDC_REG_418173_STREAM_HDR_CHANGED_ENABLE_BMSK        0x8
+#define VIDC_REG_418173_STREAM_HDR_CHANGED_ENABLE_SHFT        0x3
+#define VIDC_REG_418173_BUFFER_FULL_ENABLE_BMSK        0x2
+#define VIDC_REG_418173_BUFFER_FULL_ENABLE_SHFT        0x1
+
+#define VIDC_REG_841539_ADDR                \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000600)
+#define VIDC_REG_841539_PHYS                \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000600)
+#define VIDC_REG_841539_RMSK                       0x3
+#define VIDC_REG_841539_SHFT                         0
+#define VIDC_REG_841539_IN                  \
+	in_dword_masked(VIDC_REG_841539_ADDR,  \
+			VIDC_REG_841539_RMSK)
+#define VIDC_REG_841539_INM(m)              \
+	in_dword_masked(VIDC_REG_841539_ADDR,  m)
+#define VIDC_REG_841539_OUT(v)              \
+	out_dword(VIDC_REG_841539_ADDR, v)
+#define VIDC_REG_841539_OUTM(m, v)           \
+do { \
+	out_dword_masked_ns(VIDC_REG_841539_ADDR, m, v, \
+			VIDC_REG_841539_IN); \
+} while (0)
+#define VIDC_REG_841539_TILE_MODE_BMSK             0x3
+#define VIDC_REG_841539_TILE_MODE_SHFT               0
+
+#define VIDC_REG_99105_ADDR                \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000800)
+#define VIDC_REG_99105_PHYS                \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000800)
+#define VIDC_REG_99105_RMSK                0xffffffff
+#define VIDC_REG_99105_SHFT                         0
+#define VIDC_REG_99105_IN                  \
+	in_dword_masked(VIDC_REG_99105_ADDR,  \
+			VIDC_REG_99105_RMSK)
+#define VIDC_REG_99105_INM(m)              \
+	in_dword_masked(VIDC_REG_99105_ADDR,  m)
+#define VIDC_REG_99105_OUT(v)              \
+	out_dword(VIDC_REG_99105_ADDR, v)
+#define VIDC_REG_99105_OUTM(m, v)           \
+do { \
+	out_dword_masked_ns(VIDC_REG_99105_ADDR, m, v, \
+			VIDC_REG_99105_IN); \
+} while (0)
+#define VIDC_REG_99105_ENC_CUR_Y_ADDR_BMSK 0xffffffff
+#define VIDC_REG_99105_ENC_CUR_Y_ADDR_SHFT          0
+
+#define VIDC_REG_777113_ADDR_ADDR                \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000804)
+#define VIDC_REG_777113_ADDR_PHYS                \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000804)
+#define VIDC_REG_777113_ADDR_RMSK                0xffffffff
+#define VIDC_REG_777113_ADDR_SHFT                         0
+#define VIDC_REG_777113_ADDR_IN                  \
+	in_dword_masked(VIDC_REG_777113_ADDR_ADDR,  \
+			VIDC_REG_777113_ADDR_RMSK)
+#define VIDC_REG_777113_ADDR_INM(m)              \
+	in_dword_masked(VIDC_REG_777113_ADDR_ADDR,  m)
+#define VIDC_REG_777113_ADDR_OUT(v)              \
+	out_dword(VIDC_REG_777113_ADDR_ADDR, v)
+#define VIDC_REG_777113_ADDR_OUTM(m, v)           \
+do { \
+	out_dword_masked_ns(VIDC_REG_777113_ADDR_ADDR, m, v, \
+			VIDC_REG_777113_ADDR_IN); \
+} while (0)
+#define VIDC_REG_777113_ADDR_ENC_CUR_C_ADDR_BMSK 0xffffffff
+#define VIDC_REG_777113_ADDR_ENC_CUR_C_ADDR_SHFT          0
+
+#define VIDC_REG_341928_ADDR_ADDR                  \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x0000080c)
+#define VIDC_REG_341928_ADDR_PHYS                  \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000080c)
+#define VIDC_REG_341928_ADDR_RMSK                  0xffffffff
+#define VIDC_REG_341928_ADDR_SHFT                           0
+#define VIDC_REG_341928_ADDR_IN                    \
+	in_dword_masked(VIDC_REG_341928_ADDR_ADDR,  \
+			VIDC_REG_341928_ADDR_RMSK)
+#define VIDC_REG_341928_ADDR_INM(m)                \
+	in_dword_masked(VIDC_REG_341928_ADDR_ADDR,  m)
+#define VIDC_REG_341928_ADDR_OUT(v)                \
+	out_dword(VIDC_REG_341928_ADDR_ADDR, v)
+#define VIDC_REG_341928_ADDR_OUTM(m, v)             \
+do { \
+	out_dword_masked_ns(VIDC_REG_341928_ADDR_ADDR, m, v, \
+			VIDC_REG_341928_ADDR_IN); \
+} while (0)
+#define VIDC_REG_341928_ADDR_ENC_DPB_ADR_BMSK      0xffffffff
+#define VIDC_REG_341928_ADDR_ENC_DPB_ADR_SHFT               0
+
+#define VIDC_REG_857491_ADDR                    \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000810)
+#define VIDC_REG_857491_PHYS                    \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000810)
+#define VIDC_REG_857491_RMSK                         0xfff
+#define VIDC_REG_857491_SHFT                             0
+#define VIDC_REG_857491_IN                      \
+	in_dword_masked(VIDC_REG_857491_ADDR,  \
+			VIDC_REG_857491_RMSK)
+#define VIDC_REG_857491_INM(m)                  \
+	in_dword_masked(VIDC_REG_857491_ADDR,  m)
+#define VIDC_REG_857491_OUT(v)                  \
+	out_dword(VIDC_REG_857491_ADDR, v)
+#define VIDC_REG_857491_OUTM(m, v)               \
+do { \
+	out_dword_masked_ns(VIDC_REG_857491_ADDR, m, v, \
+			VIDC_REG_857491_IN); \
+} while (0)
+#define VIDC_REG_857491_CIR_MB_NUM_BMSK              0xfff
+#define VIDC_REG_857491_CIR_MB_NUM_SHFT                  0
+
+#define VIDC_REG_518133_ADDR                  \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000900)
+#define VIDC_REG_518133_PHYS                  \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000900)
+#define VIDC_REG_518133_RMSK                  0xffffffff
+#define VIDC_REG_518133_SHFT                           0
+#define VIDC_REG_518133_IN                    \
+	in_dword_masked(VIDC_REG_518133_ADDR,  \
+			VIDC_REG_518133_RMSK)
+#define VIDC_REG_518133_INM(m)                \
+	in_dword_masked(VIDC_REG_518133_ADDR,  m)
+#define VIDC_REG_518133_OUT(v)                \
+	out_dword(VIDC_REG_518133_ADDR, v)
+#define VIDC_REG_518133_OUTM(m, v)             \
+do { \
+	out_dword_masked_ns(VIDC_REG_518133_ADDR, m, v, \
+			VIDC_REG_518133_IN); \
+} while (0)
+#define VIDC_REG_518133_DEC_DPB_ADDR_BMSK     0xffffffff
+#define VIDC_REG_518133_DEC_DPB_ADDR_SHFT              0
+
+#define VIDC_REG_456376_ADDR_ADDR                 \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000904)
+#define VIDC_REG_456376_ADDR_PHYS                 \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000904)
+#define VIDC_REG_456376_ADDR_RMSK                 0xffffffff
+#define VIDC_REG_456376_ADDR_SHFT                          0
+#define VIDC_REG_456376_ADDR_IN                   \
+	in_dword_masked(VIDC_REG_456376_ADDR_ADDR,  \
+			VIDC_REG_456376_ADDR_RMSK)
+#define VIDC_REG_456376_ADDR_INM(m)               \
+	in_dword_masked(VIDC_REG_456376_ADDR_ADDR,  m)
+#define VIDC_REG_456376_ADDR_OUT(v)               \
+	out_dword(VIDC_REG_456376_ADDR_ADDR, v)
+#define VIDC_REG_456376_ADDR_OUTM(m, v)            \
+do { \
+	out_dword_masked_ns(VIDC_REG_456376_ADDR_ADDR, m, v, \
+			VIDC_REG_456376_ADDR_IN); \
+} while (0)
+#define VIDC_REG_456376_ADDR_DPB_COMV_ADDR_BMSK   0xffffffff
+#define VIDC_REG_456376_ADDR_DPB_COMV_ADDR_SHFT            0
+
+#define VIDC_REG_267567_ADDR                 \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000908)
+#define VIDC_REG_267567_PHYS                 \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000908)
+#define VIDC_REG_267567_RMSK                 0xffffffff
+#define VIDC_REG_267567_SHFT                          0
+#define VIDC_REG_267567_IN                   \
+	in_dword_masked(VIDC_REG_267567_ADDR,  \
+			VIDC_REG_267567_RMSK)
+#define VIDC_REG_267567_INM(m)               \
+	in_dword_masked(VIDC_REG_267567_ADDR,  m)
+#define VIDC_REG_267567_OUT(v)               \
+	out_dword(VIDC_REG_267567_ADDR, v)
+#define VIDC_REG_267567_OUTM(m, v)            \
+do { \
+	out_dword_masked_ns(VIDC_REG_267567_ADDR, m, v, \
+			VIDC_REG_267567_IN); \
+} while (0)
+#define VIDC_REG_798486_ADDR_BMSK   0xffffffff
+#define VIDC_REG_798486_ADDR_SHFT            0
+
+#define VIDC_REG_105770_ADDR                      \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x0000090c)
+#define VIDC_REG_105770_PHYS                      \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000090c)
+#define VIDC_REG_105770_RMSK                            0xff
+#define VIDC_REG_105770_SHFT                               0
+#define VIDC_REG_105770_IN                        \
+	in_dword_masked(VIDC_REG_105770_ADDR,         \
+	VIDC_REG_105770_RMSK)
+#define VIDC_REG_105770_INM(m)                    \
+	in_dword_masked(VIDC_REG_105770_ADDR,  m)
+#define VIDC_REG_105770_DPB_SIZE_BMSK                   0xff
+#define VIDC_REG_105770_DPB_SIZE_SHFT                      0
+
+#define VIDC_REG_58211_ADDR                 \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000a00)
+#define VIDC_REG_58211_PHYS                 \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000a00)
+#define VIDC_REG_58211_RMSK                      0x33f
+#define VIDC_REG_58211_SHFT                          0
+#define VIDC_REG_58211_IN                   \
+	in_dword_masked(VIDC_REG_58211_ADDR,  \
+			VIDC_REG_58211_RMSK)
+#define VIDC_REG_58211_INM(m)               \
+	in_dword_masked(VIDC_REG_58211_ADDR,  m)
+#define VIDC_REG_58211_OUT(v)               \
+	out_dword(VIDC_REG_58211_ADDR, v)
+#define VIDC_REG_58211_OUTM(m, v)            \
+do { \
+	out_dword_masked_ns(VIDC_REG_58211_ADDR, m, v, \
+			VIDC_REG_58211_IN); \
+} while (0)
+#define VIDC_REG_58211_FR_RC_EN_BMSK             0x200
+#define VIDC_REG_58211_FR_RC_EN_SHFT               0x9
+#define VIDC_REG_58211_MB_RC_EN_BMSK             0x100
+#define VIDC_REG_58211_MB_RC_EN_SHFT               0x8
+#define VIDC_REG_58211_FRAME_QP_BMSK              0x3f
+#define VIDC_REG_58211_FRAME_QP_SHFT                 0
+
+#define VIDC_REG_548359_ADDR                    \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000a04)
+#define VIDC_REG_548359_PHYS                    \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000a04)
+#define VIDC_REG_548359_RMSK                          0x3f
+#define VIDC_REG_548359_SHFT                             0
+#define VIDC_REG_548359_IN                      \
+	in_dword_masked(VIDC_REG_548359_ADDR,  \
+			VIDC_REG_548359_RMSK)
+#define VIDC_REG_548359_INM(m)                  \
+	in_dword_masked(VIDC_REG_548359_ADDR,  m)
+#define VIDC_REG_548359_OUT(v)                  \
+	out_dword(VIDC_REG_548359_ADDR, v)
+#define VIDC_REG_548359_OUTM(m, v)               \
+do { \
+	out_dword_masked_ns(VIDC_REG_548359_ADDR, m, v, \
+			VIDC_REG_548359_IN); \
+} while (0)
+#define VIDC_REG_548359_P_FRAME_QP_BMSK               0x3f
+#define VIDC_REG_548359_P_FRAME_QP_SHFT                  0
+
+#define VIDC_REG_174150_ADDR                   \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000a08)
+#define VIDC_REG_174150_PHYS                   \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000a08)
+#define VIDC_REG_174150_RMSK                   0xffffffff
+#define VIDC_REG_174150_SHFT                            0
+#define VIDC_REG_174150_IN                     \
+	in_dword_masked(VIDC_REG_174150_ADDR,  \
+			VIDC_REG_174150_RMSK)
+#define VIDC_REG_174150_INM(m)                 \
+	in_dword_masked(VIDC_REG_174150_ADDR,  m)
+#define VIDC_REG_174150_OUT(v)                 \
+	out_dword(VIDC_REG_174150_ADDR, v)
+#define VIDC_REG_174150_OUTM(m, v)              \
+do { \
+	out_dword_masked_ns(VIDC_REG_174150_ADDR, m, v, \
+			VIDC_REG_174150_IN); \
+} while (0)
+#define VIDC_REG_174150_BIT_RATE_BMSK          0xffffffff
+#define VIDC_REG_174150_BIT_RATE_SHFT                   0
+
+#define VIDC_REG_734318_ADDR                     \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000a0c)
+#define VIDC_REG_734318_PHYS                     \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000a0c)
+#define VIDC_REG_734318_RMSK                         0x3f3f
+#define VIDC_REG_734318_SHFT                              0
+#define VIDC_REG_734318_IN                       \
+	in_dword_masked(VIDC_REG_734318_ADDR,        \
+	VIDC_REG_734318_RMSK)
+#define VIDC_REG_734318_INM(m)                   \
+	in_dword_masked(VIDC_REG_734318_ADDR,  m)
+#define VIDC_REG_734318_OUT(v)                   \
+	out_dword(VIDC_REG_734318_ADDR, v)
+#define VIDC_REG_734318_OUTM(m, v)                \
+do { \
+	out_dword_masked_ns(VIDC_REG_734318_ADDR, m, v, \
+			VIDC_REG_734318_IN); \
+} while (0)
+#define VIDC_REG_734318_MAX_QP_BMSK                  0x3f00
+#define VIDC_REG_734318_MAX_QP_SHFT                     0x8
+#define VIDC_REG_734318_MIN_QP_BMSK                    0x3f
+#define VIDC_REG_734318_MIN_QP_SHFT                       0
+
+#define VIDC_REG_677784_ADDR                      \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000a10)
+#define VIDC_REG_677784_PHYS                      \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000a10)
+#define VIDC_REG_677784_RMSK                          0xffff
+#define VIDC_REG_677784_SHFT                               0
+#define VIDC_REG_677784_IN                        \
+	in_dword_masked(VIDC_REG_677784_ADDR,         \
+	VIDC_REG_677784_RMSK)
+#define VIDC_REG_677784_INM(m)                    \
+	in_dword_masked(VIDC_REG_677784_ADDR,  m)
+#define VIDC_REG_677784_OUT(v)                    \
+	out_dword(VIDC_REG_677784_ADDR, v)
+#define VIDC_REG_677784_OUTM(m, v)                 \
+do { \
+	out_dword_masked_ns(VIDC_REG_677784_ADDR, m, v, \
+			VIDC_REG_677784_IN); \
+} while (0)
+#define VIDC_REG_677784_REACT_PARA_BMSK               0xffff
+#define VIDC_REG_677784_REACT_PARA_SHFT                    0
+
+#define VIDC_REG_995041_ADDR                    \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000a14)
+#define VIDC_REG_995041_PHYS                    \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000a14)
+#define VIDC_REG_995041_RMSK                           0xf
+#define VIDC_REG_995041_SHFT                             0
+#define VIDC_REG_995041_IN                      \
+	in_dword_masked(VIDC_REG_995041_ADDR,  \
+			VIDC_REG_995041_RMSK)
+#define VIDC_REG_995041_INM(m)                  \
+	in_dword_masked(VIDC_REG_995041_ADDR,  m)
+#define VIDC_REG_995041_OUT(v)                  \
+	out_dword(VIDC_REG_995041_ADDR, v)
+#define VIDC_REG_995041_OUTM(m, v)               \
+do { \
+	out_dword_masked_ns(VIDC_REG_995041_ADDR, m, v, \
+			VIDC_REG_995041_IN); \
+} while (0)
+#define VIDC_REG_995041_DARK_DISABLE_BMSK              0x8
+#define VIDC_REG_995041_DARK_DISABLE_SHFT              0x3
+#define VIDC_REG_995041_SMOOTH_DISABLE_BMSK            0x4
+#define VIDC_REG_995041_SMOOTH_DISABLE_SHFT            0x2
+#define VIDC_REG_995041_STATIC_DISABLE_BMSK            0x2
+#define VIDC_REG_995041_STATIC_DISABLE_SHFT            0x1
+#define VIDC_REG_995041_ACT_DISABLE_BMSK               0x1
+#define VIDC_REG_995041_ACT_DISABLE_SHFT                 0
+
+#define VIDC_REG_273649_ADDR                       \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000a18)
+#define VIDC_REG_273649_PHYS                       \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000a18)
+#define VIDC_REG_273649_RMSK                             0x3f
+#define VIDC_REG_273649_SHFT                                0
+#define VIDC_REG_273649_IN                         \
+	in_dword_masked(VIDC_REG_273649_ADDR,  VIDC_REG_273649_RMSK)
+#define VIDC_REG_273649_INM(m)                     \
+	in_dword_masked(VIDC_REG_273649_ADDR,  m)
+#define VIDC_REG_273649_QP_OUT_BMSK                      0x3f
+#define VIDC_REG_273649_QP_OUT_SHFT                         0
+
+#define VIDC_REG_548823_ADDR                   \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000b00)
+#define VIDC_REG_548823_PHYS                   \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000b00)
+#define VIDC_REG_548823_RMSK                   0xffffffff
+#define VIDC_REG_548823_SHFT                            0
+#define VIDC_REG_548823_IN                     \
+	in_dword_masked(VIDC_REG_548823_ADDR,  \
+			VIDC_REG_548823_RMSK)
+#define VIDC_REG_548823_INM(m)                 \
+	in_dword_masked(VIDC_REG_548823_ADDR,  m)
+#define VIDC_REG_548823_720P_VERSION_BMSK       0xffffffff
+#define VIDC_REG_548823_720P_VERSION_SHFT                0
+
+#define VIDC_REG_881638_ADDR                     \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000c00)
+#define VIDC_REG_881638_PHYS                     \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000c00)
+#define VIDC_REG_881638_RMSK                     0xffffffff
+#define VIDC_REG_881638_SHFT                              0
+#define VIDC_REG_881638_IN                       \
+	in_dword_masked(VIDC_REG_881638_ADDR,        \
+	VIDC_REG_881638_RMSK)
+#define VIDC_REG_881638_INM(m)                   \
+	in_dword_masked(VIDC_REG_881638_ADDR,  m)
+#define VIDC_REG_881638_CROP_RIGHT_OFFSET_BMSK   0xffff0000
+#define VIDC_REG_881638_CROP_RIGHT_OFFSET_SHFT         0x10
+#define VIDC_REG_881638_CROP_LEFT_OFFSET_BMSK        0xffff
+#define VIDC_REG_881638_CROP_LEFT_OFFSET_SHFT             0
+
+#define VIDC_REG_161486_ADDR                     \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000c04)
+#define VIDC_REG_161486_PHYS                     \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000c04)
+#define VIDC_REG_161486_RMSK                     0xffffffff
+#define VIDC_REG_161486_SHFT                              0
+#define VIDC_REG_161486_IN                       \
+	in_dword_masked(VIDC_REG_161486_ADDR,        \
+	VIDC_REG_161486_RMSK)
+#define VIDC_REG_161486_INM(m)                   \
+	in_dword_masked(VIDC_REG_161486_ADDR,  m)
+#define VIDC_REG_161486_CROP_BOTTOM_OFFSET_BMSK  0xffff0000
+#define VIDC_REG_161486_CROP_BOTTOM_OFFSET_SHFT        0x10
+#define VIDC_REG_161486_CROP_TOP_OFFSET_BMSK         0xffff
+#define VIDC_REG_161486_CROP_TOP_OFFSET_SHFT              0
+
+#define VIDC_REG_580603_ADDR              \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000c08)
+#define VIDC_REG_580603_PHYS              \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000c08)
+#define VIDC_REG_580603_RMSK              0xffffffff
+#define VIDC_REG_580603_SHFT                       0
+#define VIDC_REG_580603_IN                \
+	in_dword_masked(VIDC_REG_580603_ADDR,  \
+			VIDC_REG_580603_RMSK)
+#define VIDC_REG_580603_INM(m)            \
+	in_dword_masked(VIDC_REG_580603_ADDR,  m)
+#define VIDC_REG_580603_720P_DEC_FRM_SIZE_BMSK 0xffffffff
+#define VIDC_REG_580603_720P_DEC_FRM_SIZE_SHFT          0
+
+
+#define VIDC_REG_606447_ADDR \
+		(VIDC_720P_WRAPPER_REG_BASE + 0x00000c0c)
+#define VIDC_REG_606447_PHYS \
+		(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000c0c)
+#define VIDC_REG_606447_RMSK  0xff1f
+#define VIDC_REG_606447_SHFT  0
+#define VIDC_REG_606447_IN                         \
+		in_dword_masked(VIDC_REG_606447_ADDR, \
+		VIDC_REG_606447_RMSK)
+#define VIDC_REG_606447_INM(m)                     \
+		in_dword_masked(VIDC_REG_606447_ADDR, m)
+#define VIDC_REG_606447_OUT(v)                     \
+		out_dword(VIDC_REG_606447_ADDR, v)
+#define VIDC_REG_606447_OUTM(m, v)                  \
+		out_dword_masked_ns(VIDC_REG_606447_ADDR, \
+		m, v, VIDC_REG_606447_IN); \
+
+#define VIDC_REG_606447_DIS_PIC_LEVEL_BMSK 0xff00
+#define VIDC_REG_606447_DIS_PIC_LEVEL_SHFT 0x8
+#define VIDC_REG_606447_DISP_PIC_PROFILE_BMSK 0x1f
+#define VIDC_REG_606447_DISP_PIC_PROFILE_SHFT 0
+
+#define VIDC_REG_854281_ADDR \
+		(VIDC_720P_WRAPPER_REG_BASE      + 0x00000c10)
+#define VIDC_REG_854281_PHYS \
+		(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000c10)
+#define VIDC_REG_854281_RMSK 0xffffffff
+#define VIDC_REG_854281_SHFT 0
+#define VIDC_REG_854281_IN \
+		in_dword_masked(VIDC_REG_854281_ADDR, \
+		VIDC_REG_854281_RMSK)
+#define VIDC_REG_854281_INM(m) \
+		in_dword_masked(VIDC_REG_854281_ADDR, m)
+#define VIDC_REG_854281_MIN_DPB_SIZE_BMSK 0xffffffff
+#define VIDC_REG_854281_MIN_DPB_SIZE_SHFT 0
+
+
+#define VIDC_REG_381535_ADDR \
+		(VIDC_720P_WRAPPER_REG_BASE + 0x00000c14)
+#define VIDC_REG_381535_PHYS \
+		(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000c14)
+#define VIDC_REG_381535_RMSK 0xffffffff
+#define VIDC_REG_381535_SHFT 0
+#define VIDC_REG_381535_IN \
+		in_dword_masked(VIDC_REG_381535_ADDR, \
+		VIDC_REG_381535_RMSK)
+#define VIDC_REG_381535_INM(m) \
+		in_dword_masked(VIDC_REG_381535_ADDR, m)
+#define VIDC_REG_381535_720P_FW_STATUS_BMSK 0xffffffff
+#define VIDC_REG_381535_720P_FW_STATUS_SHFT 0
+
+
+#define VIDC_REG_347105_ADDR \
+		(VIDC_720P_WRAPPER_REG_BASE + 0x00000c18)
+#define VIDC_REG_347105_PHYS \
+		(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000c18)
+#define VIDC_REG_347105_RMSK 0xffffffff
+#define VIDC_REG_347105_SHFT 0
+#define VIDC_REG_347105_IN \
+		in_dword_masked(VIDC_REG_347105_ADDR, \
+		VIDC_REG_347105_RMSK)
+#define VIDC_REG_347105_INM(m) \
+		in_dword_masked(VIDC_REG_347105_ADDR, m)
+#define VIDC_REG_347105_FREE_LUMA_DPB_BMSK 0xffffffff
+#define VIDC_REG_347105_FREE_LUMA_DPB_SHFT 0
+
+
+#define VIDC_REG_62325_ADDR              \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000d00)
+#define VIDC_REG_62325_PHYS              \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000d00)
+#define VIDC_REG_62325_RMSK                     0xf
+#define VIDC_REG_62325_SHFT                       0
+#define VIDC_REG_62325_IN                \
+		in_dword_masked(VIDC_REG_62325_ADDR,  \
+		VIDC_REG_62325_RMSK)
+#define VIDC_REG_62325_INM(m)            \
+	in_dword_masked(VIDC_REG_62325_ADDR,  m)
+#define VIDC_REG_62325_OUT(v)            \
+	out_dword(VIDC_REG_62325_ADDR, v)
+#define VIDC_REG_62325_OUTM(m, v)         \
+do { \
+	out_dword_masked_ns(VIDC_REG_62325_ADDR, m, v, \
+			VIDC_REG_62325_IN); \
+} while (0)
+#define VIDC_REG_62325_COMMAND_TYPE_BMSK        0xf
+#define VIDC_REG_62325_COMMAND_TYPE_SHFT          0
+
+#define VIDC_REG_101184_ADDR  \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000d04)
+#define VIDC_REG_101184_PHYS  \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000d04)
+#define VIDC_REG_101184_RMSK                0xffffffff
+#define VIDC_REG_101184_SHFT                0
+#define VIDC_REG_101184_OUT(v)                     \
+	out_dword(VIDC_REG_101184_ADDR, v)
+
+#define VIDC_REG_490443_ADDR  \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000d08)
+#define VIDC_REG_490443_PHYS  \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000d08)
+#define VIDC_REG_490443_RMSK                       \
+	0xffffffff
+#define \
+	\
+VIDC_REG_490443_SHFT                                0
+#define VIDC_REG_490443_OUT(v)                     \
+	out_dword(VIDC_REG_490443_ADDR, v)
+
+#define VIDC_REG_625444_ADDR                 \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000d14)
+#define VIDC_REG_625444_PHYS                 \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000d14)
+#define VIDC_REG_625444_RMSK                 0xffffffff
+#define VIDC_REG_625444_SHFT                          0
+#define VIDC_REG_625444_IN                   \
+	in_dword_masked(VIDC_REG_625444_ADDR,  \
+			VIDC_REG_625444_RMSK)
+#define VIDC_REG_625444_INM(m)               \
+	in_dword_masked(VIDC_REG_625444_ADDR,  m)
+#define VIDC_REG_625444_OUT(v)               \
+	out_dword(VIDC_REG_625444_ADDR, v)
+#define VIDC_REG_625444_OUTM(m, v)            \
+do { \
+	out_dword_masked_ns(VIDC_REG_625444_ADDR, m, v, \
+			VIDC_REG_625444_IN); \
+} while (0)
+#define VIDC_REG_625444_FRAME_RATE_BMSK      0xffffffff
+#define VIDC_REG_625444_FRAME_RATE_SHFT               0
+
+#define VIDC_REG_639999_ADDR                    \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000d20)
+#define VIDC_REG_639999_PHYS                    \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000d20)
+#define VIDC_REG_639999_RMSK                    0xffff
+#define VIDC_REG_639999_SHFT                         0
+#define VIDC_REG_639999_OUT(v)                  \
+	out_dword(VIDC_REG_639999_ADDR, v)
+
+#define VIDC_REG_64895_ADDR                    \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000e00)
+#define VIDC_REG_64895_PHYS                    \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000e00)
+#define VIDC_REG_64895_RMSK                    0xffffffff
+#define VIDC_REG_64895_SHFT                             0
+#define VIDC_REG_64895_OUT(v)                  \
+	out_dword(VIDC_REG_64895_ADDR, v)
+
+#define VIDC_REG_965480_ADDR \
+		(VIDC_720P_WRAPPER_REG_BASE + 0x00000e04)
+#define VIDC_REG_965480_PHYS \
+		(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000e04)
+#define VIDC_REG_965480_RMSK 0x1
+#define VIDC_REG_965480_SHFT 0
+#define VIDC_REG_965480_OUT(v) \
+		out_dword(VIDC_REG_965480_ADDR, v)
+
+#define VIDC_REG_804959_ADDR              \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000e08)
+#define VIDC_REG_804959_PHYS              \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000e08)
+#define VIDC_REG_804959_RMSK                     0x7
+#define VIDC_REG_804959_SHFT                       0
+#define VIDC_REG_804959_OUT(v)            \
+	out_dword(VIDC_REG_804959_ADDR, v)
+
+#define VIDC_REG_257463_ADDR                   \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000e10)
+#define VIDC_REG_257463_PHYS                   \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000e10)
+#define VIDC_REG_257463_RMSK                   0xffffffff
+#define VIDC_REG_257463_SHFT                            0
+#define VIDC_REG_257463_IN                     \
+	in_dword_masked(VIDC_REG_257463_ADDR,  \
+			VIDC_REG_257463_RMSK)
+#define VIDC_REG_257463_INM(m)                 \
+	in_dword_masked(VIDC_REG_257463_ADDR,  m)
+#define VIDC_REG_257463_MIN_NUM_DPB_BMSK       0xffffffff
+#define VIDC_REG_257463_MIN_NUM_DPB_SHFT                0
+
+#define VIDC_REG_883500_ADDR                       \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000e14)
+#define VIDC_REG_883500_PHYS                       \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000e14)
+#define VIDC_REG_883500_RMSK                       0xffffffff
+#define VIDC_REG_883500_SHFT                                0
+#define VIDC_REG_883500_OUT(v)                     \
+	out_dword(VIDC_REG_883500_ADDR, v)
+
+#define VIDC_REG_615716_ADDR(n)               \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000e18 + 4 * (n))
+#define VIDC_REG_615716_PHYS(n)               \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000e18 + 4 * (n))
+#define VIDC_REG_615716_RMSK                  0xffffffff
+#define VIDC_REG_615716_SHFT                           0
+#define VIDC_REG_615716_OUTI(n, v) \
+	out_dword(VIDC_REG_615716_ADDR(n), v)
+
+#define VIDC_REG_603032_ADDR                \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000e98)
+#define VIDC_REG_603032_PHYS                \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000e98)
+#define VIDC_REG_603032_RMSK                0xffffffff
+#define VIDC_REG_603032_SHFT                         0
+#define VIDC_REG_603032_OUT(v)              \
+	out_dword(VIDC_REG_603032_ADDR, v)
+
+#define VIDC_REG_300310_ADDR                  \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000e9c)
+#define VIDC_REG_300310_PHYS                  \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000e9c)
+#define VIDC_REG_300310_RMSK                  0xffffffff
+#define VIDC_REG_300310_SHFT                           0
+#define VIDC_REG_300310_IN                    \
+	in_dword_masked(VIDC_REG_300310_ADDR,  \
+			VIDC_REG_300310_RMSK)
+#define VIDC_REG_300310_INM(m)                \
+	in_dword_masked(VIDC_REG_300310_ADDR,  m)
+#define VIDC_REG_300310_ERROR_STATUS_BMSK     0xffffffff
+#define VIDC_REG_300310_ERROR_STATUS_SHFT              0
+
+#define VIDC_REG_792026_ADDR        \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000ea0)
+#define VIDC_REG_792026_PHYS        \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ea0)
+#define VIDC_REG_792026_RMSK        0xffffffff
+#define VIDC_REG_792026_SHFT                 0
+#define VIDC_REG_792026_OUT(v)      \
+	out_dword(VIDC_REG_792026_ADDR, v)
+
+#define VIDC_REG_844152_ADDR        \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000ea4)
+#define VIDC_REG_844152_PHYS        \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ea4)
+#define VIDC_REG_844152_RMSK        0xffffffff
+#define VIDC_REG_844152_SHFT                 0
+#define VIDC_REG_844152_OUT(v)      \
+	out_dword(VIDC_REG_844152_ADDR, v)
+
+#define VIDC_REG_370409_ADDR            \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000ea8)
+#define VIDC_REG_370409_PHYS                \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ea8)
+#define VIDC_REG_370409_RMSK                0xffffffff
+#define VIDC_REG_370409_SHFT                         0
+#define VIDC_REG_370409_IN                  \
+	in_dword_masked(VIDC_REG_370409_ADDR,  \
+			VIDC_REG_370409_RMSK)
+#define VIDC_REG_370409_INM(m)              \
+	in_dword_masked(VIDC_REG_370409_ADDR,  m)
+#define VIDC_REG_370409_GET_FRAME_TAG_TOP_BMSK 0xffffffff
+#define VIDC_REG_370409_GET_FRAME_TAG_TOP_SHFT          0
+
+#define VIDC_REG_147682_ADDR               \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000eac)
+#define VIDC_REG_147682_PHYS               \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000eac)
+#define VIDC_REG_147682_RMSK                        0x1
+#define VIDC_REG_147682_SHFT                        0
+#define VIDC_REG_147682_OUT(v)             \
+	out_dword(VIDC_REG_147682_ADDR, v)
+
+#define VIDC_REG_407718_ADDR                    \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000eb0)
+#define VIDC_REG_407718_PHYS                    \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000eb0)
+#define VIDC_REG_407718_RMSK                    0xffffffff
+#define VIDC_REG_407718_SHFT                             0
+#define VIDC_REG_407718_OUT(v)                  \
+	out_dword(VIDC_REG_407718_ADDR, v)
+
+#define VIDC_REG_697961_ADDR \
+		(VIDC_720P_WRAPPER_REG_BASE + 0x00000eb4)
+#define VIDC_REG_697961_PHYS \
+		(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000eb4)
+#define VIDC_REG_697961_RMSK 0x3
+#define VIDC_REG_697961_SHFT 0
+#define VIDC_REG_697961_IN \
+		in_dword_masked(VIDC_REG_697961_ADDR, \
+		VIDC_REG_697961_RMSK)
+#define VIDC_REG_697961_INM(m) \
+		in_dword_masked(VIDC_REG_697961_ADDR, m)
+#define VIDC_REG_697961_FRAME_TYPE_BMSK 0x3
+#define VIDC_REG_697961_FRAME_TYPE_SHFT 0
+
+
+#define VIDC_REG_613254_ADDR               \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000eb8)
+#define VIDC_REG_613254_PHYS               \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000eb8)
+#define VIDC_REG_613254_RMSK                      0x1
+#define VIDC_REG_613254_SHFT                        0
+#define VIDC_REG_613254_IN                 \
+	in_dword_masked(VIDC_REG_613254_ADDR,  \
+			VIDC_REG_613254_RMSK)
+#define VIDC_REG_613254_INM(m)             \
+	in_dword_masked(VIDC_REG_613254_ADDR,  m)
+#define VIDC_REG_613254_METADATA_STATUS_BMSK        0x1
+#define VIDC_REG_613254_METADATA_STATUS_SHFT          0
+#define VIDC_REG_441270_ADDR                    \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000ebc)
+#define VIDC_REG_441270_PHYS                    \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ebc)
+#define VIDC_REG_441270_RMSK                          0x1f
+#define VIDC_REG_441270_SHFT                             0
+#define VIDC_REG_441270_IN                      \
+	in_dword_masked(VIDC_REG_441270_ADDR,  \
+			VIDC_REG_441270_RMSK)
+#define VIDC_REG_441270_INM(m)                  \
+	in_dword_masked(VIDC_REG_441270_ADDR,  m)
+#define VIDC_REG_441270_DATA_PARTITIONED_BMSK 0x8
+#define VIDC_REG_441270_DATA_PARTITIONED_SHFT 0x3
+
+#define VIDC_REG_441270_FRAME_TYPE_BMSK               0x17
+#define VIDC_REG_441270_FRAME_TYPE_SHFT                  0
+
+#define VIDC_REG_724381_ADDR        \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000ec0)
+#define VIDC_REG_724381_PHYS        \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ec0)
+#define VIDC_REG_724381_RMSK               0x3
+#define VIDC_REG_724381_SHFT                 0
+#define VIDC_REG_724381_IN          \
+	in_dword_masked(VIDC_REG_724381_ADDR,  \
+			VIDC_REG_724381_RMSK)
+#define VIDC_REG_724381_INM(m)      \
+	in_dword_masked(VIDC_REG_724381_ADDR,  m)
+#define VIDC_REG_724381_MORE_FIELD_NEEDED_BMSK       0x4
+#define VIDC_REG_724381_MORE_FIELD_NEEDED_SHFT       0x2
+#define VIDC_REG_724381_OPERATION_FAILED_BMSK        0x2
+#define VIDC_REG_724381_OPERATION_FAILED_SHFT        0x1
+#define VIDC_REG_724381_RESOLUTION_CHANGE_BMSK       0x1
+#define VIDC_REG_724381_RESOLUTION_CHANGE_SHFT         0
+
+#define VIDC_REG_854681_ADDR               \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000ec4)
+#define VIDC_REG_854681_PHYS               \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ec4)
+#define VIDC_REG_854681_RMSK                     0x7f
+#define VIDC_REG_854681_SHFT                        0
+#define VIDC_REG_854681_OUT(v)             \
+	out_dword(VIDC_REG_854681_ADDR, v)
+
+#define VIDC_REG_128234_ADDR               \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000ec8)
+#define VIDC_REG_128234_PHYS               \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ec8)
+#define VIDC_REG_128234_RMSK               0xffff000f
+#define VIDC_REG_128234_SHFT                        0
+#define VIDC_REG_128234_OUT(v)             \
+	out_dword(VIDC_REG_128234_ADDR, v)
+
+#define VIDC_REG_1137_ADDR        \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000ecc)
+#define VIDC_REG_1137_PHYS        \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ecc)
+#define VIDC_REG_1137_RMSK        0xffffffff
+#define VIDC_REG_1137_SHFT                 0
+#define VIDC_REG_1137_IN          \
+	in_dword_masked(VIDC_REG_1137_ADDR,  \
+			VIDC_REG_1137_RMSK)
+#define VIDC_REG_1137_INM(m)      \
+	in_dword_masked(VIDC_REG_1137_ADDR,  m)
+#define VIDC_REG_1137_METADATA_DISPLAY_INDEX_BMSK \
+	0xffffffff
+#define \
+	\
+VIDC_REG_1137_METADATA_DISPLAY_INDEX_SHFT          0
+
+#define VIDC_REG_988552_ADDR       \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000ed0)
+#define VIDC_REG_988552_PHYS       \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ed0)
+#define VIDC_REG_988552_RMSK       0xffffffff
+#define VIDC_REG_988552_SHFT                0
+#define VIDC_REG_988552_OUT(v)     \
+	out_dword(VIDC_REG_988552_ADDR, v)
+
+#define VIDC_REG_319934_ADDR  \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000ed4)
+#define VIDC_REG_319934_PHYS  \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ed4)
+#define VIDC_REG_319934_RMSK                       0xffffffff
+#define VIDC_REG_319934_SHFT                   0
+#define VIDC_REG_319934_OUT(v)                     \
+	out_dword(VIDC_REG_319934_ADDR, v)
+
+#define VIDC_REG_679165_ADDR                   \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000ed8)
+#define VIDC_REG_679165_PHYS                   \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ed8)
+#define VIDC_REG_679165_RMSK                   0xffffffff
+#define VIDC_REG_679165_SHFT                            0
+#define VIDC_REG_679165_IN                     \
+	in_dword_masked(VIDC_REG_679165_ADDR,  \
+			VIDC_REG_679165_RMSK)
+#define VIDC_REG_679165_INM(m)                 \
+	in_dword_masked(VIDC_REG_679165_ADDR,  m)
+#define VIDC_REG_679165_PIC_TIME_TOP_BMSK       0xffffffff
+#define VIDC_REG_679165_PIC_TIME_TOP_SHFT                0
+
+#define VIDC_REG_374150_ADDR                     \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000edc)
+#define VIDC_REG_374150_PHYS                     \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000edc)
+#define VIDC_REG_374150_RMSK                     0xffffffff
+#define VIDC_REG_374150_SHFT                              0
+#define VIDC_REG_374150_IN                       \
+	in_dword_masked(VIDC_REG_374150_ADDR,  \
+			VIDC_REG_374150_RMSK)
+#define VIDC_REG_374150_INM(m)                   \
+	in_dword_masked(VIDC_REG_374150_ADDR,  m)
+#define VIDC_REG_374150_PIC_TIME_BOTTOM_BMSK           0xffffffff
+#define VIDC_REG_374150_PIC_TIME_BOTTOM_SHFT                    0
+
+#define VIDC_REG_94750_ADDR                 \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000ee0)
+#define VIDC_REG_94750_PHYS                 \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ee0)
+#define VIDC_REG_94750_RMSK                 0xffffffff
+#define VIDC_REG_94750_SHFT                          0
+#define VIDC_REG_94750_OUT(v)               \
+	out_dword(VIDC_REG_94750_ADDR, v)
+
+#define VIDC_REG_438677_ADDR          \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000ee4)
+#define VIDC_REG_438677_PHYS                \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ee4)
+#define VIDC_REG_438677_RMSK                0xffffffff
+#define VIDC_REG_438677_SHFT                         0
+#define VIDC_REG_438677_IN                  \
+	in_dword_masked(VIDC_REG_438677_ADDR,  \
+			VIDC_REG_438677_RMSK)
+#define VIDC_REG_438677_INM(m)              \
+	in_dword_masked(VIDC_REG_438677_ADDR,  m)
+#define VIDC_REG_438677_GET_FRAME_TAG_BOTTOM_BMSK 0xffffffff
+#define VIDC_REG_438677_GET_FRAME_TAG_BOTTOM_SHFT          0
+
+#define VIDC_REG_76706_ADDR               \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00000ee8)
+#define VIDC_REG_76706_PHYS               \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ee8)
+#define VIDC_REG_76706_RMSK                      0x1
+#define VIDC_REG_76706_SHFT                        0
+#define VIDC_REG_76706_OUT(v)             \
+	out_dword(VIDC_REG_76706_ADDR, v)
+
+#define VIDC_REG_809984_ADDR                       \
+	(VIDC_720P_WRAPPER_REG_BASE      + 0x00001000)
+#define VIDC_REG_809984_PHYS                       \
+	(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00001000)
+#define VIDC_REG_809984_RMSK                       0xffff0007
+#define VIDC_REG_809984_SHFT                                0
+#define VIDC_REG_809984_IN                         \
+	in_dword_masked(VIDC_REG_809984_ADDR,  VIDC_REG_809984_RMSK)
+#define VIDC_REG_809984_INM(m)                     \
+	in_dword_masked(VIDC_REG_809984_ADDR,  m)
+#define VIDC_REG_809984_720PV_720P_WRAPPER_VERSION_BMSK 0xffff0000
+#define VIDC_REG_809984_720PV_720P_WRAPPER_VERSION_SHFT       0x10
+#define VIDC_REG_809984_TEST_MUX_SEL_BMSK                 0x7
+#define VIDC_REG_809984_TEST_MUX_SEL_SHFT                   0
+
+
+#define VIDC_REG_699747_ADDR \
+       (VIDC_720P_WRAPPER_REG_BASE + 0x00000d0c)
+#define VIDC_REG_699747_PHYS \
+       (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000d0c)
+#define VIDC_REG_699747_RMSK 0xffffffff
+#define VIDC_REG_699747_SHFT 0
+#define VIDC_REG_699747_OUT(v)                  \
+		out_dword(VIDC_REG_699747_ADDR, v)
+
+#define VIDC_REG_166247_ADDR \
+       (VIDC_720P_WRAPPER_REG_BASE + 0x00000d10)
+#define VIDC_REG_166247_PHYS \
+       (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000d10)
+#define VIDC_REG_166247_RMSK 0xffffffff
+#define VIDC_REG_166247_SHFT 0
+#define VIDC_REG_166247_OUT(v)               \
+		out_dword(VIDC_REG_166247_ADDR, v)
+
+#define VIDC_REG_486169_ADDR \
+		(VIDC_720P_WRAPPER_REG_BASE + 0x00000d18)
+#define VIDC_REG_486169_PHYS \
+		(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000d18)
+#define VIDC_REG_486169_RMSK 0xffffffff
+#define VIDC_REG_486169_SHFT 0
+#define VIDC_REG_486169_OUT(v) \
+		out_dword(VIDC_REG_486169_ADDR, v)
+
+#define VIDC_REG_926519_ADDR \
+		(VIDC_720P_WRAPPER_REG_BASE + 0x00000d1c)
+#define VIDC_REG_926519_PHYS \
+		(VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000d1c)
+#define VIDC_REG_926519_RMSK 0xffffffff
+#define VIDC_REG_926519_SHFT 0
+#define VIDC_REG_926519_OUT(v) \
+		out_dword(VIDC_REG_926519_ADDR, v)
+
+/** List all the levels and their register valus */
+
+#define VIDC_720P_PROFILE_MPEG4_SP      0
+#define VIDC_720P_PROFILE_MPEG4_ASP     1
+#define VIDC_720P_PROFILE_H264_BASELINE 0
+#define VIDC_720P_PROFILE_H264_MAIN     1
+#define VIDC_720P_PROFILE_H264_HIGH     2
+#define VIDC_720P_PROFILE_H264_CPB      3
+#define VIDC_720P_PROFILE_H263_BASELINE 0
+
+#define VIDC_720P_PROFILE_VC1_SP        0
+#define VIDC_720P_PROFILE_VC1_MAIN      1
+#define VIDC_720P_PROFILE_VC1_ADV       2
+#define VIDC_720P_PROFILE_MPEG2_MAIN    4
+#define VIDC_720P_PROFILE_MPEG2_SP      5
+
+#define VIDC_720P_MPEG4_LEVEL0  0
+#define VIDC_720P_MPEG4_LEVEL0b 9
+#define VIDC_720P_MPEG4_LEVEL1  1
+#define VIDC_720P_MPEG4_LEVEL2  2
+#define VIDC_720P_MPEG4_LEVEL3  3
+#define VIDC_720P_MPEG4_LEVEL3b 7
+#define VIDC_720P_MPEG4_LEVEL4a 4
+#define VIDC_720P_MPEG4_LEVEL5  5
+#define VIDC_720P_MPEG4_LEVEL6  6
+
+#define VIDC_720P_H264_LEVEL1     10
+#define VIDC_720P_H264_LEVEL1b    9
+#define VIDC_720P_H264_LEVEL1p1   11
+#define VIDC_720P_H264_LEVEL1p2   12
+#define VIDC_720P_H264_LEVEL1p3   13
+#define VIDC_720P_H264_LEVEL2     20
+#define VIDC_720P_H264_LEVEL2p1   21
+#define VIDC_720P_H264_LEVEL2p2   22
+#define VIDC_720P_H264_LEVEL3     30
+#define VIDC_720P_H264_LEVEL3p1   31
+#define VIDC_720P_H264_LEVEL3p2   32
+
+#define VIDC_720P_H263_LEVEL10    10
+#define VIDC_720P_H263_LEVEL20    20
+#define VIDC_720P_H263_LEVEL30    30
+#define VIDC_720P_H263_LEVEL40    40
+#define VIDC_720P_H263_LEVEL45    45
+#define VIDC_720P_H263_LEVEL50    50
+#define VIDC_720P_H263_LEVEL60    60
+#define VIDC_720P_H263_LEVEL70    70
+
+#define VIDC_720P_VC1_LEVEL_LOW    0
+#define VIDC_720P_VC1_LEVEL_MED    2
+#define VIDC_720P_VC1_LEVEL_HIGH   4
+#define VIDC_720P_VC1_LEVEL0       0
+#define VIDC_720P_VC1_LEVEL1       1
+#define VIDC_720P_VC1_LEVEL2       2
+#define VIDC_720P_VC1_LEVEL3       3
+#define VIDC_720P_VC1_LEVEL4       4
+
+#define VIDCL_720P_MPEG2_LEVEL_LOW 10
+#define VIDCL_720P_MPEG2_LEVEL_MAIN 8
+#define VIDCL_720P_MPEG2_LEVEL_HIGH14 6
+
+#define VIDC_720P_CMD_CHSET               0x0
+#define VIDC_720P_CMD_CHEND               0x2
+#define VIDC_720P_CMD_INITCODEC           0x3
+#define VIDC_720P_CMD_FRAMERUN            0x4
+#define VIDC_720P_CMD_INITBUFFERS         0x5
+#define VIDC_720P_CMD_FRAMERUN_REALLOCATE 0x6
+#define VIDC_720P_CMD_MFC_ENGINE_RESET 0x7
+
+enum vidc_720p_endian {
+	VIDC_720P_BIG_ENDIAN = 0x0,
+	VIDC_720P_LITTLE_ENDIAN = 0x1
+};
+
+enum vidc_720p_memory_access_method {
+	VIDC_720P_TILE_LINEAR = 0,
+	VIDC_720P_TILE_16x16 = 2,
+	VIDC_720P_TILE_64x32 = 3
+};
+
+enum vidc_720p_interrupt_control_mode {
+	VIDC_720P_INTERRUPT_MODE = 0,
+	VIDC_720P_POLL_MODE = 1
+};
+
+enum vidc_720p_interrupt_level_selection {
+	VIDC_720P_INTERRUPT_LEVEL_SEL = 0,
+	VIDC_720P_INTERRUPT_PULSE_SEL = 1
+};
+
+#define VIDC_720P_INTR_BUFFER_FULL             0x002
+#define VIDC_720P_INTR_FW_DONE                 0x020
+#define VIDC_720P_INTR_HEADER_DONE             0x040
+#define VIDC_720P_INTR_DMA_DONE                0x080
+#define VIDC_720P_INTR_FRAME_DONE              0x100
+
+enum vidc_720p_enc_dec_selection {
+	VIDC_720P_DECODER = 0,
+	VIDC_720P_ENCODER = 1
+};
+
+enum vidc_720p_codec {
+	VIDC_720P_MPEG4 = 0,
+	VIDC_720P_H264 = 1,
+	VIDC_720P_DIVX = 2,
+	VIDC_720P_XVID = 3,
+	VIDC_720P_H263 = 4,
+	VIDC_720P_MPEG2 = 5,
+	VIDC_720P_VC1 = 6
+};
+
+enum vidc_720p_frame {
+	VIDC_720P_NOTCODED = 0,
+	VIDC_720P_IFRAME = 1,
+	VIDC_720P_PFRAME = 2,
+	VIDC_720P_BFRAME = 3,
+	VIDC_720P_IDRFRAME = 4
+};
+
+enum vidc_720p_entropy_sel {
+	VIDC_720P_ENTROPY_SEL_CAVLC = 0,
+	VIDC_720P_ENTROPY_SEL_CABAC = 1
+};
+
+enum vidc_720p_cabac_model {
+	VIDC_720P_CABAC_MODEL_NUMBER_0 = 0,
+	VIDC_720P_CABAC_MODEL_NUMBER_1 = 1,
+	VIDC_720P_CABAC_MODEL_NUMBER_2 = 2
+};
+
+enum vidc_720p_DBConfig {
+	VIDC_720P_DB_ALL_BLOCKING_BOUNDARY = 0,
+	VIDC_720P_DB_DISABLE = 1,
+	VIDC_720P_DB_SKIP_SLICE_BOUNDARY = 2
+};
+
+enum vidc_720p_MSlice_selection {
+	VIDC_720P_MSLICE_BY_MB_COUNT = 0,
+	VIDC_720P_MSLICE_BY_BYTE_COUNT = 1,
+	VIDC_720P_MSLICE_BY_GOB = 2,
+	VIDC_720P_MSLICE_OFF = 3
+};
+
+enum vidc_720p_display_status {
+	VIDC_720P_DECODE_ONLY = 0,
+	VIDC_720P_DECODE_AND_DISPLAY = 1,
+	VIDC_720P_DISPLAY_ONLY = 2,
+	VIDC_720P_EMPTY_BUFFER = 3
+};
+
+#define VIDC_720P_ENC_IFRAME_REQ       0x1
+#define VIDC_720P_ENC_IPERIOD_CHANGE   0x1
+#define VIDC_720P_ENC_FRAMERATE_CHANGE 0x2
+#define VIDC_720P_ENC_BITRATE_CHANGE   0x4
+
+#define VIDC_720P_FLUSH_REQ     0x1
+#define VIDC_720P_EXTRADATA     0x2
+
+#define VIDC_720P_METADATA_ENABLE_QP           0x01
+#define VIDC_720P_METADATA_ENABLE_CONCEALMB    0x02
+#define VIDC_720P_METADATA_ENABLE_VC1          0x04
+#define VIDC_720P_METADATA_ENABLE_SEI          0x08
+#define VIDC_720P_METADATA_ENABLE_VUI          0x10
+#define VIDC_720P_METADATA_ENABLE_ENCSLICE     0x20
+#define VIDC_720P_METADATA_ENABLE_PASSTHROUGH  0x40
+
+struct vidc_720p_dec_disp_info {
+	enum vidc_720p_display_status disp_status;
+	u32 resl_change;
+	u32 reconfig_flush_done;
+	u32 img_size_x;
+	u32 img_size_y;
+	u32 y_addr;
+	u32 c_addr;
+	u32 tag_top;
+	u32 pic_time_top;
+	u32 disp_is_interlace;
+	u32 tag_bottom;
+	u32 pic_time_bottom;
+	u32 metadata_exists;
+	u32 crop_exists;
+	u32 crop_right_offset;
+	u32 crop_left_offset;
+	u32 crop_bottom_offset;
+	u32 crop_top_offset;
+	u32 input_frame;
+	u32 input_bytes_consumed;
+	u32 input_is_interlace;
+	u32 input_frame_num;
+};
+
+struct vidc_720p_seq_hdr_info {
+	u32 img_size_x;
+	u32 img_size_y;
+	u32 dec_frm_size;
+	u32 min_num_dpb;
+	u32 min_dpb_size;
+	u32 profile;
+	u32 level;
+	u32 progressive;
+	u32 data_partitioned;
+	u32  crop_exists;
+	u32  crop_right_offset;
+	u32  crop_left_offset;
+	u32  crop_bottom_offset;
+	u32  crop_top_offset;
+};
+
+struct vidc_720p_enc_frame_info {
+	u32 enc_size;
+	u32 frame;
+	u32 metadata_exists;
+};
+
+void vidc_720p_set_device_virtual_base(u8 *core_virtual_base_addr);
+
+void vidc_720p_init(char **ppsz_version, u32 i_firmware_size,
+	u32 *pi_firmware_address, enum vidc_720p_endian dma_endian,
+	u32 interrupt_off,
+	enum vidc_720p_interrupt_level_selection	interrupt_sel,
+	u32 interrupt_mask);
+
+u32 vidc_720p_do_sw_reset(void);
+
+u32 vidc_720p_reset_is_success(void);
+
+void vidc_720p_start_cpu(enum vidc_720p_endian dma_endian,
+		u32 *icontext_bufferstart, u32 *debug_core_dump_addr,
+		u32  debug_buffer_size);
+
+u32 vidc_720p_cpu_start(void);
+
+void vidc_720p_stop_fw(void);
+
+void vidc_720p_get_interrupt_status(u32 *interrupt_status,
+		u32 *cmd_err_status, u32 *disp_pic_err_status,
+		u32 *op_failed);
+
+void vidc_720p_interrupt_done_clear(void);
+
+void vidc_720p_submit_command(u32 ch_id, u32 cmd_id);
+
+
+void vidc_720p_set_channel(u32 i_ch_id,
+	enum vidc_720p_enc_dec_selection enc_dec_sel,
+	enum vidc_720p_codec codec, u32 *pi_fw, u32 i_firmware_size);
+
+u32 vidc_720p_engine_reset(u32 ch_id,
+   enum vidc_720p_endian dma_endian,
+   enum vidc_720p_interrupt_level_selection interrupt_sel,
+   u32 interrupt_mask
+);
+
+void vidc_720p_encode_set_profile(u32 i_profile, u32 i_level);
+
+void vidc_720p_set_frame_size(u32 i_size_x, u32 i_size_y);
+
+void vidc_720p_encode_set_fps(u32 i_rc_frame_rate);
+
+void vidc_720p_encode_set_vop_time(u32 vop_time_resolution,
+		u32 vop_time_increment);
+
+void vidc_720p_encode_set_hec_period(u32 hec_period);
+
+void vidc_720p_encode_set_short_header(u32 i_short_header);
+
+void vidc_720p_encode_set_qp_params(u32 i_max_qp, u32 i_min_qp);
+
+void vidc_720p_encode_set_rc_config(u32 enable_frame_level_rc,
+		u32 enable_mb_level_rc_flag, u32 i_frame_qp, u32 pframe_qp);
+
+void vidc_720p_encode_set_bit_rate(u32 i_target_bitrate);
+
+void vidc_720p_encoder_set_param_change(u32 enc_param_change);
+
+void vidc_720p_encode_set_control_param(u32 param_val);
+
+void vidc_720p_encode_set_frame_level_rc_params(u32 i_reaction_coeff);
+
+void vidc_720p_encode_set_mb_level_rc_params(u32 dark_region_as_flag,
+	u32 smooth_region_as_flag, u32 static_region_as_flag,
+	u32 activity_region_flag);
+
+void vidc_720p_encode_set_entropy_control(enum vidc_720p_entropy_sel \
+		entropy_sel,
+		enum vidc_720p_cabac_model cabac_model_number);
+
+void vidc_720p_encode_set_db_filter_control(enum vidc_720p_DBConfig
+		db_config, u32 i_slice_alpha_offset, u32 i_slice_beta_offset);
+
+void vidc_720p_encode_set_intra_refresh_mb_number(u32 i_cir_mb_number);
+
+void vidc_720p_encode_set_multi_slice_info(
+		enum vidc_720p_MSlice_selection m_slice_sel,
+		u32 multi_slice_size);
+
+void vidc_720p_encode_set_dpb_buffer(u32 *pi_enc_dpb_addr, u32 alloc_len);
+
+void vidc_720p_set_deblock_line_buffer(u32 *pi_deblock_line_buffer_start,
+		u32 alloc_len);
+
+void vidc_720p_encode_set_i_period(u32 i_i_period);
+
+void vidc_720p_encode_init_codec(u32 i_ch_id,
+	enum vidc_720p_memory_access_method memory_access_model);
+
+void vidc_720p_encode_unalign_bitstream(u32 upper_unalign_word,
+	u32 lower_unalign_word);
+
+void vidc_720p_encode_set_seq_header_buffer(u32 ext_buffer_start,
+	u32 ext_buffer_end, u32 start_byte_num);
+
+void vidc_720p_encode_frame(u32 ch_id, u32 ext_buffer_start,
+	u32 ext_buffer_end, u32 start_byte_number,
+	u32 y_addr, u32 c_addr);
+
+void vidc_720p_encode_get_header(u32 *pi_enc_header_size);
+
+void vidc_720p_enc_frame_info
+	(struct vidc_720p_enc_frame_info *enc_frame_info);
+
+void vidc_720p_decode_bitstream_header(u32 ch_id, u32 dec_unit_size,
+	u32 start_byte_num, u32 ext_buffer_start, u32 ext_buffer_end,
+	enum vidc_720p_memory_access_method memory_access_model,
+	u32 decode_order);
+
+void vidc_720p_decode_get_seq_hdr_info
+    (struct vidc_720p_seq_hdr_info *seq_hdr_info);
+
+void vidc_720p_decode_set_dpb_release_buffer_mask
+    (u32 i_dpb_release_buffer_mask);
+
+void vidc_720p_decode_set_dpb_buffers(u32 i_buf_index, u32 *pi_dpb_buffer);
+
+void vidc_720p_decode_set_comv_buffer
+    (u32 *pi_dpb_comv_buffer, u32 alloc_len);
+
+void vidc_720p_decode_set_dpb_details
+    (u32 num_dpb, u32 alloc_len, u32 *ref_buffer);
+
+void vidc_720p_decode_set_mpeg4Post_filter(u32 enable_post_filter);
+
+void vidc_720p_decode_set_error_control(u32 enable_error_control);
+
+void vidc_720p_decode_set_mpeg4_data_partitionbuffer(u32 *vsp_buf_start);
+
+void vidc_720p_decode_setH264VSPBuffer(u32 *pi_vsp_temp_buffer_start);
+
+void vidc_720p_decode_frame(u32 ch_id, u32 ext_buffer_start,
+		u32 ext_buffer_end, u32 dec_unit_size,
+		u32 start_byte_num, u32 input_frame_tag);
+
+void vidc_720p_issue_eos(u32 i_ch_id);
+void vidc_720p_eos_info(u32 *disp_status, u32 *resl_change);
+
+void vidc_720p_decode_display_info
+    (struct vidc_720p_dec_disp_info *disp_info);
+
+void vidc_720p_decode_skip_frm_details(u32 *free_luma_dpb);
+
+void vidc_720p_metadata_enable(u32 flag, u32 *input_buffer);
+
+void vidc_720p_decode_dynamic_req_reset(void);
+
+void vidc_720p_decode_dynamic_req_set(u32 property);
+
+void vidc_720p_decode_setpassthrough_start(u32 pass_startaddr);
+
+
+
+#define DDL_720P_REG_BASE VIDC_720P_WRAPPER_REG_BASE
+#define VIDC_BUSY_WAIT(n) udelay(n)
+
+#undef VIDC_REGISTER_LOG_MSG
+#undef VIDC_REGISTER_LOG_INTO_BUFFER
+
+#ifdef VIDC_REGISTER_LOG_MSG
+#define VIDC_MSG1(msg_format, a) printk(KERN_INFO msg_format, a)
+#define VIDC_MSG2(msg_format, a, b) printk(KERN_INFO msg_format, a, b)
+#define VIDC_MSG3(msg_format, a, b, c) printk(KERN_INFO msg_format, a, b, c)
+#else
+#define VIDC_MSG1(msg_format, a)
+#define VIDC_MSG2(msg_format, a, b)
+#define VIDC_MSG3(msg_format, a, b, c)
+#endif
+
+#ifdef VIDC_REGISTER_LOG_INTO_BUFFER
+
+#define VIDC_REGLOG_BUFSIZE 200000
+#define VIDC_REGLOG_MAX_PRINT_SIZE 100
+extern char vidclog[VIDC_REGLOG_BUFSIZE];
+extern unsigned int vidclog_index;
+
+#define VIDC_LOG_BUFFER_INIT \
+{if (vidclog_index) \
+  memset(vidclog, 0, vidclog_index+1); \
+  vidclog_index = 0; }
+
+#define VIDC_REGLOG_CHECK_BUFINDEX(req_size) \
+  vidclog_index = \
+  (vidclog_index+(req_size) < VIDC_REGLOG_BUFSIZE) ? vidclog_index : 0;
+
+#define VIDC_LOG_WRITE(reg, val) \
+{unsigned int len; \
+	VIDC_REGLOG_CHECK_BUFINDEX(VIDC_REGLOG_MAX_PRINT_SIZE); \
+	len = snprintf(&vidclog[vidclog_index], VIDC_REGLOG_MAX_PRINT_SIZE, \
+	"(0x%x:"#reg"=0x%x)" , VIDC_##reg##_ADDR - DDL_720P_REG_BASE, val);\
+	vidclog_index += len; }
+
+#define VIDC_LOG_WRITEI(reg, index, val) \
+{unsigned int len; \
+	VIDC_REGLOG_CHECK_BUFINDEX(VIDC_REGLOG_MAX_PRINT_SIZE); \
+	len = snprintf(&vidclog[vidclog_index], VIDC_REGLOG_MAX_PRINT_SIZE, \
+	"(0x%x:"#reg"=0x%x)" , VIDC_##reg##_ADDR(index)-DDL_720P_REG_BASE,  \
+	val); vidclog_index += len; }
+
+#define VIDC_LOG_WRITEF(reg, field, val) \
+{unsigned int len; \
+	VIDC_REGLOG_CHECK_BUFINDEX(VIDC_REGLOG_MAX_PRINT_SIZE); \
+	len = snprintf(&vidclog[vidclog_index], VIDC_REGLOG_MAX_PRINT_SIZE, \
+	"(0x%x:"#reg":0x%x:=0x%x)" , VIDC_##reg##_ADDR - DDL_720P_REG_BASE,  \
+	VIDC_##reg##_##field##_BMSK,  val);\
+	vidclog_index += len; }
+
+#define VIDC_LOG_READ(reg, pval) \
+{ unsigned int len; \
+	VIDC_REGLOG_CHECK_BUFINDEX(VIDC_REGLOG_MAX_PRINT_SIZE); \
+	len = snprintf(&vidclog[vidclog_index], VIDC_REGLOG_MAX_PRINT_SIZE, \
+	"(0x%x:"#reg"==0x%x)" , VIDC_##reg##_ADDR - DDL_720P_REG_BASE,  \
+	(u32)*pval); \
+	vidclog_index += len; }
+
+#define VIDC_STR_LOGBUFFER(str) \
+{ unsigned int len; \
+	VIDC_REGLOG_CHECK_BUFINDEX(VIDC_REGLOG_MAX_PRINT_SIZE); \
+	len = snprintf(&vidclog[vidclog_index], VIDC_REGLOG_MAX_PRINT_SIZE, \
+	"<%s>" , str); vidclog_index += len; }
+
+#define VIDC_LONG_LOGBUFFER(str, arg1) \
+{ unsigned int len; \
+	VIDC_REGLOG_CHECK_BUFINDEX(VIDC_REGLOG_MAX_PRINT_SIZE); \
+	len = snprintf(&vidclog[vidclog_index], VIDC_REGLOG_MAX_PRINT_SIZE, \
+	"<%s=0x%x>" , str, arg1); vidclog_index += len; }
+
+#define VIDC_DEBUG_REGISTER_LOG \
+{ u32 val; unsigned int len; \
+	val = VIDC_720P_IN(REG_881638); \
+	VIDC_REGLOG_CHECK_BUFINDEX(VIDC_REGLOG_MAX_PRINT_SIZE); \
+	len = snprintf(&vidclog[vidclog_index], 50,  "[dbg1=%x]" , val); \
+	vidclog_index += len; \
+	val = VIDC_720P_IN(REG_161486); \
+	VIDC_REGLOG_CHECK_BUFINDEX(VIDC_REGLOG_MAX_PRINT_SIZE); \
+	len = snprintf(&vidclog[vidclog_index], 50,  "[dbg2=%x]" , val); \
+	vidclog_index += len; }
+
+#else
+#define VIDC_LOG_WRITE(reg, val)
+#define VIDC_LOG_WRITEI(reg, index, val)
+#define VIDC_LOG_WRITEF(reg, field, val)
+#define VIDC_LOG_READ(reg, pval)
+#define VIDC_LOG_BUFFER_INIT
+#define VIDC_STR_LOGBUFFER(str)
+#define VIDC_LONG_LOGBUFFER(str, arg1)
+#define VIDC_DEBUG_REGISTER_LOG
+#endif
+
+void vidcputlog(char *str);
+void vidcput_debug_reglog(void);
+
+#define VIDC_LOGERR_STRING(str) \
+do { \
+	VIDC_STR_LOGBUFFER(str); \
+	VIDC_MSG1("\n<%s>", str); \
+} while (0)
+
+#define VIDC_LOG_STRING(str) \
+do { \
+	VIDC_STR_LOGBUFFER(str); \
+	VIDC_MSG1("\n<%s>", str); \
+} while (0)
+
+#define VIDC_LOG1(str, arg1) \
+do { \
+	VIDC_LONG_LOGBUFFER(str, arg1); \
+	VIDC_MSG2("\n<%s=0x%08x>", str, arg1); \
+} while (0)
+
+#define VIDC_IO_OUT(reg,  val) \
+do { \
+	VIDC_LOG_WRITE(reg, (u32)val);  \
+	VIDC_MSG2("\n(0x%08x:"#reg"=0x%08x)",  \
+	(u32)(VIDC_##reg##_ADDR - DDL_720P_REG_BASE),  (u32)val); \
+	mb(); \
+	VIDC_720P_OUT(reg, val);  \
+} while (0)
+
+#define VIDC_IO_OUTI(reg,  index,  val) \
+do { \
+	VIDC_LOG_WRITEI(reg, index, (u32)val); \
+	VIDC_MSG2("\n(0x%08x:"#reg"=0x%08x)",  \
+	(u32)(VIDC_##reg##_ADDR(index)-DDL_720P_REG_BASE),  (u32)val); \
+	mb(); \
+	VIDC_720P_OUTI(reg, index, val);  \
+} while (0)
+
+#define VIDC_IO_OUTF(reg,  field,  val) \
+do { \
+	VIDC_LOG_WRITEF(reg, field, val); \
+	VIDC_MSG3("\n(0x%08x:"#reg":0x%x:=0x%08x)",  \
+	(u32)(VIDC_##reg##_ADDR - DDL_720P_REG_BASE),  \
+	VIDC_##reg##_##field##_BMSK,  (u32)val); \
+	mb(); \
+	VIDC_720P_OUTF(reg, field, val);  \
+} while (0)
+
+#define VIDC_IO_IN(reg, pval) \
+do { \
+	mb(); \
+	*pval = (u32) VIDC_720P_IN(reg); \
+	VIDC_LOG_READ(reg, pval); \
+	VIDC_MSG2("\n(0x%08x:"#reg"==0x%08x)",  \
+	(u32)(VIDC_##reg##_ADDR - DDL_720P_REG_BASE), (u32) *pval);  \
+} while (0)
+
+#define VIDC_IO_INF(reg, mask, pval) \
+do { \
+	mb(); \
+	*pval = VIDC_720P_INF(reg, mask); \
+	VIDC_LOG_READ(reg, pval); \
+	VIDC_MSG2("\n(0x%08x:"#reg"==0x%08x)",  \
+	(u32)(VIDC_##reg##_ADDR - DDL_720P_REG_BASE),  *pval); \
+} while (0)
+
+#endif
diff --git a/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker.c b/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker.c
new file mode 100644
index 0000000..e51bf45
--- /dev/null
+++ b/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker.c
@@ -0,0 +1,771 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/firmware.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <mach/clk.h>
+#include <linux/interrupt.h>
+#include <media/msm/vidc_init.h>
+#include <media/msm/vidc_type.h>
+#include "vcd_res_tracker.h"
+
+#define MSM_AXI_QOS_NAME "msm_vidc_reg"
+#define AXI_CLK_SCALING
+
+#define QVGA_PERF_LEVEL (300 * 30)
+#define VGA_PERF_LEVEL (1200 * 30)
+#define WVGA_PERF_LEVEL (1500 * 30)
+
+static unsigned int mfc_clk_freq_table[3] = {
+	61440000, 122880000, 170667000
+};
+
+static unsigned int axi_clk_freq_table_enc[2] = {
+	122880, 192000
+};
+static unsigned int axi_clk_freq_table_dec[2] = {
+	122880, 192000
+};
+
+static struct res_trk_context resource_context;
+
+#define VIDC_BOOT_FW			"vidc_720p_command_control.fw"
+#define VIDC_MPG4_DEC_FW		"vidc_720p_mp4_dec_mc.fw"
+#define VIDC_H263_DEC_FW		"vidc_720p_h263_dec_mc.fw"
+#define VIDC_H264_DEC_FW		"vidc_720p_h264_dec_mc.fw"
+#define VIDC_MPG4_ENC_FW		"vidc_720p_mp4_enc_mc.fw"
+#define VIDC_H264_ENC_FW		"vidc_720p_h264_enc_mc.fw"
+#define VIDC_VC1_DEC_FW		"vidc_720p_vc1_dec_mc.fw"
+
+unsigned char *vidc_command_control_fw;
+u32 vidc_command_control_fw_size;
+
+unsigned char *vidc_mpg4_dec_fw;
+u32 vidc_mpg4_dec_fw_size;
+
+unsigned char *vidc_h263_dec_fw;
+u32 vidc_h263_dec_fw_size;
+
+unsigned char *vidc_h264_dec_fw;
+u32 vidc_h264_dec_fw_size;
+
+unsigned char *vidc_mpg4_enc_fw;
+u32 vidc_mpg4_enc_fw_size;
+
+unsigned char *vidc_h264_enc_fw;
+u32 vidc_h264_enc_fw_size;
+
+unsigned char *vidc_vc1_dec_fw;
+u32 vidc_vc1_dec_fw_size;
+
+static u32 res_trk_disable_videocore(void)
+{
+	int rc = -1;
+	mutex_lock(&resource_context.lock);
+
+	if (!resource_context.rail_enabled) {
+		mutex_unlock(&resource_context.lock);
+		return false;
+	}
+
+	if (!resource_context.clock_enabled &&
+		resource_context.pclk &&
+		resource_context.hclk &&
+		resource_context.hclk_div2) {
+
+		VCDRES_MSG_LOW("\nEnabling clk before disabling pwr rail\n");
+		if (clk_set_rate(resource_context.hclk,
+			mfc_clk_freq_table[0])) {
+			VCDRES_MSG_ERROR("\n pwr_rail_disable:"
+				 " set clk rate failed\n");
+			goto bail_out;
+		}
+
+		if (clk_prepare_enable(resource_context.pclk)) {
+			VCDRES_MSG_ERROR("vidc pclk Enable failed\n");
+			goto bail_out;
+		}
+
+		if (clk_prepare_enable(resource_context.hclk)) {
+			VCDRES_MSG_ERROR("vidc hclk Enable failed\n");
+			goto disable_pclk;
+		}
+
+		if (clk_prepare_enable(resource_context.hclk_div2)) {
+			VCDRES_MSG_ERROR("vidc hclk_div2 Enable failed\n");
+			goto disable_hclk;
+		}
+	} else {
+		VCDRES_MSG_ERROR("\ndisabling pwr rail: Enabling clk failed\n");
+		goto bail_out;
+	}
+
+	resource_context.rail_enabled = 0;
+	rc = clk_reset(resource_context.pclk, CLK_RESET_ASSERT);
+	if (rc) {
+		VCDRES_MSG_ERROR("\n clk_reset failed %d\n", rc);
+		mutex_unlock(&resource_context.lock);
+		return false;
+	}
+	msleep(20);
+
+	clk_disable_unprepare(resource_context.pclk);
+	clk_disable_unprepare(resource_context.hclk);
+	clk_disable_unprepare(resource_context.hclk_div2);
+
+	clk_put(resource_context.hclk_div2);
+	clk_put(resource_context.hclk);
+	clk_put(resource_context.pclk);
+
+	rc = regulator_disable(resource_context.regulator);
+	if (rc) {
+		VCDRES_MSG_ERROR("\n regulator disable failed %d\n", rc);
+		mutex_unlock(&resource_context.lock);
+		return false;
+	}
+
+	resource_context.hclk_div2 = NULL;
+	resource_context.hclk = NULL;
+	resource_context.pclk = NULL;
+
+	mutex_unlock(&resource_context.lock);
+
+	return true;
+
+disable_hclk:
+	clk_disable_unprepare(resource_context.hclk);
+disable_pclk:
+	clk_disable_unprepare(resource_context.pclk);
+bail_out:
+	if (resource_context.pclk) {
+		clk_put(resource_context.pclk);
+		resource_context.pclk = NULL;
+	}
+	if (resource_context.hclk) {
+		clk_put(resource_context.hclk);
+		resource_context.hclk = NULL;
+	}
+	if (resource_context.hclk_div2) {
+		clk_put(resource_context.hclk_div2);
+		resource_context.hclk_div2 = NULL;
+	}
+	mutex_unlock(&resource_context.lock);
+	return false;
+}
+
+u32 res_trk_enable_clocks(void)
+{
+	VCDRES_MSG_LOW("\n in res_trk_enable_clocks()");
+
+	mutex_lock(&resource_context.lock);
+	if (!resource_context.clock_enabled) {
+		VCDRES_MSG_LOW("Enabling IRQ in %s()\n", __func__);
+		enable_irq(resource_context.irq_num);
+
+		VCDRES_MSG_LOW("%s(): Enabling the clocks ...\n", __func__);
+
+		if (clk_prepare_enable(resource_context.pclk)) {
+			VCDRES_MSG_ERROR("vidc pclk Enable failed\n");
+
+			clk_put(resource_context.hclk);
+			clk_put(resource_context.hclk_div2);
+			mutex_unlock(&resource_context.lock);
+			return false;
+		}
+
+		if (clk_prepare_enable(resource_context.hclk)) {
+			VCDRES_MSG_ERROR("vidc  hclk Enable failed\n");
+			clk_put(resource_context.pclk);
+			clk_put(resource_context.hclk_div2);
+			mutex_unlock(&resource_context.lock);
+			return false;
+		}
+
+		if (clk_prepare_enable(resource_context.hclk_div2)) {
+			VCDRES_MSG_ERROR("vidc  hclk Enable failed\n");
+			clk_put(resource_context.hclk);
+			clk_put(resource_context.pclk);
+			mutex_unlock(&resource_context.lock);
+			return false;
+		}
+	}
+
+	resource_context.clock_enabled = 1;
+	mutex_unlock(&resource_context.lock);
+	return true;
+}
+
+static u32 res_trk_sel_clk_rate(unsigned long hclk_rate)
+{
+	mutex_lock(&resource_context.lock);
+	if (clk_set_rate(resource_context.hclk,
+		hclk_rate)) {
+		VCDRES_MSG_ERROR("vidc hclk set rate failed\n");
+		mutex_unlock(&resource_context.lock);
+		return false;
+	}
+	resource_context.hclk_rate = hclk_rate;
+	mutex_unlock(&resource_context.lock);
+	return true;
+}
+
+static u32 res_trk_get_clk_rate(unsigned long *phclk_rate)
+{
+	if (!phclk_rate) {
+		VCDRES_MSG_ERROR("%s(): phclk_rate is NULL\n", __func__);
+		return false;
+	}
+	mutex_lock(&resource_context.lock);
+	*phclk_rate = clk_get_rate(resource_context.hclk);
+	if (!(*phclk_rate)) {
+		VCDRES_MSG_ERROR("vidc hclk get rate failed\n");
+		mutex_unlock(&resource_context.lock);
+		return false;
+	}
+	mutex_unlock(&resource_context.lock);
+	return true;
+}
+
+u32 res_trk_disable_clocks(void)
+{
+	VCDRES_MSG_LOW("in res_trk_disable_clocks()\n");
+
+	mutex_lock(&resource_context.lock);
+
+	if (!resource_context.clock_enabled) {
+		mutex_unlock(&resource_context.lock);
+		return false;
+	}
+
+	VCDRES_MSG_LOW("Disabling IRQ in %s()\n", __func__);
+	disable_irq_nosync(resource_context.irq_num);
+	VCDRES_MSG_LOW("%s(): Disabling the clocks ...\n", __func__);
+
+	resource_context.clock_enabled = 0;
+	clk_disable_unprepare(resource_context.hclk);
+	clk_disable_unprepare(resource_context.hclk_div2);
+	clk_disable_unprepare(resource_context.pclk);
+	mutex_unlock(&resource_context.lock);
+
+	return true;
+}
+
+static u32 res_trk_enable_videocore(void)
+{
+	mutex_lock(&resource_context.lock);
+	if (!resource_context.rail_enabled) {
+		int rc = -1;
+
+		rc = regulator_enable(resource_context.regulator);
+		if (rc) {
+			VCDRES_MSG_ERROR("%s(): regulator_enable failed %d\n",
+							 __func__, rc);
+			goto bail_out;
+		}
+		VCDRES_MSG_LOW("%s(): regulator enable Success %d\n",
+							__func__, rc);
+
+		resource_context.pclk = clk_get(resource_context.device,
+			"iface_clk");
+
+		if (IS_ERR(resource_context.pclk)) {
+			VCDRES_MSG_ERROR("%s(): iface_clk get failed\n"
+							 , __func__);
+			goto disable_regulator;
+		}
+
+		resource_context.hclk = clk_get(resource_context.device,
+			"core_clk");
+
+		if (IS_ERR(resource_context.hclk)) {
+			VCDRES_MSG_ERROR("%s(): core_clk get failed\n"
+							 , __func__);
+
+			goto release_pclk;
+		}
+
+		resource_context.hclk_div2 =
+			clk_get(resource_context.device, "core_div2_clk");
+
+		if (IS_ERR(resource_context.hclk_div2)) {
+			VCDRES_MSG_ERROR("%s(): core_div2_clk get failed\n"
+							 , __func__);
+			goto release_hclk_pclk;
+		}
+
+		if (clk_set_rate(resource_context.hclk,
+			mfc_clk_freq_table[0])) {
+			VCDRES_MSG_ERROR("\n pwr_rail_enable:"
+				 " set clk rate failed\n");
+			goto release_all_clks;
+		}
+
+		if (clk_prepare_enable(resource_context.pclk)) {
+			VCDRES_MSG_ERROR("vidc pclk Enable failed\n");
+			goto release_all_clks;
+		}
+
+		if (clk_prepare_enable(resource_context.hclk)) {
+			VCDRES_MSG_ERROR("vidc hclk Enable failed\n");
+			goto disable_pclk;
+		}
+
+		if (clk_prepare_enable(resource_context.hclk_div2)) {
+			VCDRES_MSG_ERROR("vidc hclk_div2 Enable failed\n");
+			goto disable_hclk_pclk;
+		}
+
+		rc = clk_reset(resource_context.pclk, CLK_RESET_DEASSERT);
+		if (rc) {
+			VCDRES_MSG_ERROR("\n clk_reset failed %d\n", rc);
+			goto disable_and_release_all_clks;
+		}
+		msleep(20);
+
+		clk_disable_unprepare(resource_context.pclk);
+		clk_disable_unprepare(resource_context.hclk);
+		clk_disable_unprepare(resource_context.hclk_div2);
+
+	}
+	resource_context.rail_enabled = 1;
+	mutex_unlock(&resource_context.lock);
+	return true;
+
+disable_and_release_all_clks:
+	clk_disable_unprepare(resource_context.hclk_div2);
+disable_hclk_pclk:
+	clk_disable_unprepare(resource_context.hclk);
+disable_pclk:
+	clk_disable_unprepare(resource_context.pclk);
+release_all_clks:
+	clk_put(resource_context.hclk_div2);
+	resource_context.hclk_div2 = NULL;
+release_hclk_pclk:
+	clk_put(resource_context.hclk);
+	resource_context.hclk = NULL;
+release_pclk:
+	clk_put(resource_context.pclk);
+	resource_context.pclk = NULL;
+disable_regulator:
+	regulator_disable(resource_context.regulator);
+bail_out:
+	mutex_unlock(&resource_context.lock);
+	return false;
+}
+
+static u32 res_trk_convert_freq_to_perf_lvl(u64 freq)
+{
+	u64 perf_lvl;
+	u64 temp;
+
+	VCDRES_MSG_MED("\n %s():: freq = %u\n", __func__, (u32)freq);
+
+	if (!freq)
+		return 0;
+
+	temp = freq * 1000;
+	do_div(temp, VCD_RESTRK_HZ_PER_1000_PERFLVL);
+	perf_lvl = (u32)temp;
+	VCDRES_MSG_MED("\n %s(): perf_lvl = %u\n", __func__,
+		(u32)perf_lvl);
+
+	return (u32)perf_lvl;
+}
+
+static u32 res_trk_convert_perf_lvl_to_freq(u64 perf_lvl)
+{
+	u64 freq, temp;
+
+	VCDRES_MSG_MED("\n %s():: perf_lvl = %u\n", __func__,
+		(u32)perf_lvl);
+	temp = (perf_lvl * VCD_RESTRK_HZ_PER_1000_PERFLVL) + 999;
+	do_div(temp, 1000);
+	freq = (u32)temp;
+	VCDRES_MSG_MED("\n %s(): freq = %u\n", __func__, (u32)freq);
+
+	return (u32)freq;
+}
+
+static struct clk *ebi1_clk;
+
+u32 res_trk_power_up(void)
+{
+	VCDRES_MSG_LOW("clk_regime_rail_enable");
+	VCDRES_MSG_LOW("clk_regime_sel_rail_control");
+#ifdef AXI_CLK_SCALING
+{
+	VCDRES_MSG_MED("\n res_trk_power_up():: "
+		"Calling AXI add requirement\n");
+	ebi1_clk = clk_get(resource_context.device, "mem_clk");
+	if (IS_ERR(ebi1_clk)) {
+		VCDRES_MSG_ERROR("Request AXI bus QOS fails.");
+		return false;
+	}
+	clk_prepare_enable(ebi1_clk);
+}
+#endif
+
+	VCDRES_MSG_MED("\n res_trk_power_up():: Calling "
+		"vidc_enable_pwr_rail()\n");
+	return res_trk_enable_videocore();
+}
+
+u32 res_trk_power_down(void)
+{
+	VCDRES_MSG_LOW("clk_regime_rail_disable");
+#ifdef AXI_CLK_SCALING
+	VCDRES_MSG_MED("\n res_trk_power_down()::"
+		"Calling AXI remove requirement\n");
+	clk_disable_unprepare(ebi1_clk);
+	clk_put(ebi1_clk);
+#endif
+	VCDRES_MSG_MED("\n res_trk_power_down():: Calling "
+		"res_trk_disable_videocore()\n");
+	return res_trk_disable_videocore();
+}
+
+u32 res_trk_get_max_perf_level(u32 *pn_max_perf_lvl)
+{
+	if (!pn_max_perf_lvl) {
+		VCDRES_MSG_ERROR("%s(): pn_max_perf_lvl is NULL\n",
+			__func__);
+		return false;
+	}
+
+	*pn_max_perf_lvl = VCD_RESTRK_MAX_PERF_LEVEL;
+	return true;
+}
+
+u32 res_trk_set_perf_level(u32 req_perf_lvl, u32 *pn_set_perf_lvl,
+	struct vcd_dev_ctxt *dev_ctxt)
+{
+	struct vcd_clnt_ctxt *cctxt_itr = NULL;
+	u32 axi_freq = 0, mfc_freq = 0, calc_mfc_freq = 0;
+	u8 enc_clnt_present = false;
+
+	if (!pn_set_perf_lvl || !dev_ctxt) {
+		VCDRES_MSG_ERROR("%s(): NULL pointer! dev_ctxt(%p)\n",
+			__func__, dev_ctxt);
+		return false;
+	}
+
+	VCDRES_MSG_LOW("%s(), req_perf_lvl = %d", __func__, req_perf_lvl);
+	calc_mfc_freq = res_trk_convert_perf_lvl_to_freq(
+		(u64)req_perf_lvl);
+
+	if (calc_mfc_freq < VCD_RESTRK_MIN_FREQ_POINT)
+		calc_mfc_freq = VCD_RESTRK_MIN_FREQ_POINT;
+	else if (calc_mfc_freq > VCD_RESTRK_MAX_FREQ_POINT)
+		calc_mfc_freq = VCD_RESTRK_MAX_FREQ_POINT;
+
+	cctxt_itr = dev_ctxt->cctxt_list_head;
+	while (cctxt_itr) {
+		VCDRES_MSG_LOW("\n cctxt_itr = %p", cctxt_itr);
+		if (!cctxt_itr->decoding) {
+				VCDRES_MSG_LOW("\n Encoder client");
+				enc_clnt_present = true;
+				break;
+		} else {
+				VCDRES_MSG_LOW("\n Decoder client");
+		}
+		cctxt_itr = cctxt_itr->next;
+	}
+
+	if (enc_clnt_present) {
+		if (req_perf_lvl >= VGA_PERF_LEVEL) {
+			mfc_freq = mfc_clk_freq_table[2];
+			axi_freq = axi_clk_freq_table_enc[1];
+		} else {
+			mfc_freq = mfc_clk_freq_table[0];
+			axi_freq = axi_clk_freq_table_enc[0];
+		}
+		VCDRES_MSG_MED("\n ENCODER: axi_freq = %u"
+			", mfc_freq = %u, calc_mfc_freq = %u,"
+			" req_perf_lvl = %u", axi_freq,
+			mfc_freq, calc_mfc_freq,
+			req_perf_lvl);
+	} else {
+		if (req_perf_lvl <= QVGA_PERF_LEVEL) {
+			mfc_freq = mfc_clk_freq_table[0];
+			axi_freq = axi_clk_freq_table_dec[0];
+		} else {
+			axi_freq = axi_clk_freq_table_dec[0];
+			if (req_perf_lvl <= VGA_PERF_LEVEL)
+				mfc_freq = mfc_clk_freq_table[0];
+			else if (req_perf_lvl <= WVGA_PERF_LEVEL)
+				mfc_freq = mfc_clk_freq_table[1];
+			else {
+				mfc_freq = mfc_clk_freq_table[2];
+				axi_freq = axi_clk_freq_table_dec[1];
+			}
+		}
+		VCDRES_MSG_MED("\n DECODER: axi_freq = %u"
+			", mfc_freq = %u, calc_mfc_freq = %u,"
+			" req_perf_lvl = %u", axi_freq,
+			mfc_freq, calc_mfc_freq,
+			req_perf_lvl);
+	}
+
+#ifdef AXI_CLK_SCALING
+    if (req_perf_lvl != VCD_RESTRK_MIN_PERF_LEVEL) {
+		VCDRES_MSG_MED("\n %s(): Setting AXI freq to %u",
+			__func__, axi_freq);
+		clk_set_rate(ebi1_clk, axi_freq * 1000);
+	}
+#endif
+
+#ifdef USE_RES_TRACKER
+    if (req_perf_lvl != VCD_RESTRK_MIN_PERF_LEVEL) {
+		VCDRES_MSG_MED("\n %s(): Setting MFC freq to %u",
+			__func__, mfc_freq);
+		if (!res_trk_sel_clk_rate(mfc_freq)) {
+			VCDRES_MSG_ERROR("%s(): res_trk_sel_clk_rate FAILED\n",
+				__func__);
+			*pn_set_perf_lvl = 0;
+			return false;
+		}
+	}
+#endif
+
+	*pn_set_perf_lvl =
+	    res_trk_convert_freq_to_perf_lvl((u64) mfc_freq);
+	return true;
+}
+
+u32 res_trk_get_curr_perf_level(u32 *pn_perf_lvl)
+{
+	unsigned long freq;
+
+	if (!pn_perf_lvl) {
+		VCDRES_MSG_ERROR("%s(): pn_perf_lvl is NULL\n",
+			__func__);
+		return false;
+	}
+	VCDRES_MSG_LOW("clk_regime_msm_get_clk_freq_hz");
+	if (!res_trk_get_clk_rate(&freq)) {
+		VCDRES_MSG_ERROR("%s(): res_trk_get_clk_rate FAILED\n",
+			__func__);
+		*pn_perf_lvl = 0;
+		return false;
+	}
+
+	*pn_perf_lvl = res_trk_convert_freq_to_perf_lvl((u64) freq);
+	VCDRES_MSG_MED("%s(): freq = %lu, *pn_perf_lvl = %u", __func__,
+		freq, *pn_perf_lvl);
+	return true;
+}
+
+u32 res_trk_download_firmware(void)
+{
+	const struct firmware *fw_boot = NULL;
+	const struct firmware *fw_mpg4_dec = NULL;
+	const struct firmware *fw_h263_dec = NULL;
+	const struct firmware *fw_h264_dec = NULL;
+	const struct firmware *fw_mpg4_enc = NULL;
+	const struct firmware *fw_h264_enc = NULL;
+	const struct firmware *fw_vc1_dec = NULL;
+	int rc = 0;
+	u32 status = true;
+
+	VCDRES_MSG_HIGH("%s(): Request firmware download\n",
+		__func__);
+	mutex_lock(&resource_context.lock);
+	rc = request_firmware(&fw_boot, VIDC_BOOT_FW,
+						  resource_context.device);
+	if (rc) {
+		VCDRES_MSG_ERROR("request_firmware for %s error %d\n",
+				VIDC_BOOT_FW, rc);
+		mutex_unlock(&resource_context.lock);
+		return false;
+	}
+	vidc_command_control_fw = (unsigned char *)fw_boot->data;
+	vidc_command_control_fw_size = (u32) fw_boot->size;
+
+	rc = request_firmware(&fw_mpg4_dec, VIDC_MPG4_DEC_FW,
+						  resource_context.device);
+	if (rc) {
+		VCDRES_MSG_ERROR("request_firmware for %s error %d\n",
+				VIDC_MPG4_DEC_FW, rc);
+		status = false;
+		goto boot_fw_free;
+	}
+	vidc_mpg4_dec_fw = (unsigned char *)fw_mpg4_dec->data;
+	vidc_mpg4_dec_fw_size = (u32) fw_mpg4_dec->size;
+
+
+	rc = request_firmware(&fw_h263_dec, VIDC_H263_DEC_FW,
+						  resource_context.device);
+	if (rc) {
+		VCDRES_MSG_ERROR("request_firmware for %s error %d\n",
+				VIDC_H263_DEC_FW, rc);
+		status = false;
+		goto mp4dec_fw_free;
+	}
+	vidc_h263_dec_fw = (unsigned char *)fw_h263_dec->data;
+	vidc_h263_dec_fw_size = (u32) fw_h263_dec->size;
+
+	rc = request_firmware(&fw_h264_dec, VIDC_H264_DEC_FW,
+						  resource_context.device);
+	if (rc) {
+		VCDRES_MSG_ERROR("request_firmware for %s error %d\n",
+				VIDC_H264_DEC_FW, rc);
+		status = false;
+		goto h263dec_fw_free;
+	}
+	vidc_h264_dec_fw = (unsigned char *)fw_h264_dec->data;
+	vidc_h264_dec_fw_size = (u32) fw_h264_dec->size;
+
+	rc = request_firmware(&fw_mpg4_enc, VIDC_MPG4_ENC_FW,
+						  resource_context.device);
+	if (rc) {
+		VCDRES_MSG_ERROR("request_firmware for %s error %d\n",
+				VIDC_MPG4_ENC_FW, rc);
+		status = false;
+		goto h264dec_fw_free;
+	}
+	vidc_mpg4_enc_fw = (unsigned char *)fw_mpg4_enc->data;
+	vidc_mpg4_enc_fw_size = (u32) fw_mpg4_enc->size;
+
+	rc = request_firmware(&fw_h264_enc, VIDC_H264_ENC_FW,
+						  resource_context.device);
+	if (rc) {
+		VCDRES_MSG_ERROR("request_firmware for %s error %d\n",
+				VIDC_H264_ENC_FW, rc);
+		status = false;
+		goto mp4enc_fw_free;
+	}
+	vidc_h264_enc_fw = (unsigned char *)fw_h264_enc->data;
+	vidc_h264_enc_fw_size = (u32) fw_h264_enc->size;
+
+	rc = request_firmware(&fw_vc1_dec, VIDC_VC1_DEC_FW,
+						  resource_context.device);
+	if (rc) {
+		VCDRES_MSG_ERROR("request_firmware for %s error %d\n",
+				VIDC_VC1_DEC_FW, rc);
+		status = false;
+		goto h264enc_fw_free;
+	}
+	vidc_vc1_dec_fw = (unsigned char *)fw_vc1_dec->data;
+	vidc_vc1_dec_fw_size = (u32) fw_vc1_dec->size;
+	mutex_unlock(&resource_context.lock);
+	return status;
+
+h264enc_fw_free:
+	release_firmware(fw_h264_enc);
+mp4enc_fw_free:
+	release_firmware(fw_mpg4_enc);
+h264dec_fw_free:
+	release_firmware(fw_h264_dec);
+h263dec_fw_free:
+	release_firmware(fw_h263_dec);
+mp4dec_fw_free:
+	release_firmware(fw_mpg4_dec);
+boot_fw_free:
+	release_firmware(fw_boot);
+	mutex_unlock(&resource_context.lock);
+	return false;
+}
+
+void res_trk_init(struct device *device, u32 irq)
+{
+	if (resource_context.device || resource_context.irq_num ||
+		!device) {
+		VCDRES_MSG_ERROR("%s() Resource Tracker Init error\n",
+				__func__);
+		return;
+	}
+	memset(&resource_context, 0, sizeof(resource_context));
+	mutex_init(&resource_context.lock);
+	resource_context.device = device;
+	resource_context.irq_num = irq;
+	resource_context.core_type = VCD_CORE_720P;
+	resource_context.regulator = regulator_get(NULL, "fs_mfc");
+	resource_context.vidc_platform_data =
+		(struct msm_vidc_platform_data *) device->platform_data;
+	if (resource_context.vidc_platform_data) {
+		resource_context.memtype =
+		resource_context.vidc_platform_data->memtype;
+	} else {
+		resource_context.memtype = -1;
+	}
+}
+
+u32 res_trk_get_core_type(void){
+	return resource_context.core_type;
+}
+
+u32 res_trk_get_mem_type(void){
+	return resource_context.memtype;
+}
+
+u32 res_trk_get_enable_ion(void)
+{
+	return 0;
+}
+
+struct ion_client *res_trk_get_ion_client(void)
+{
+	return NULL;
+}
+
+void res_trk_set_mem_type(enum ddl_mem_area mem_type)
+{
+	return;
+}
+
+u32 res_trk_get_disable_fullhd(void)
+{
+	return 0;
+}
+
+int res_trk_check_for_sec_session()
+{
+	return 0;
+}
+
+void res_trk_secure_unset(void)
+{
+	return;
+}
+
+void res_trk_secure_set(void)
+{
+	return;
+}
+
+int res_trk_open_secure_session()
+{
+	return -EINVAL;
+}
+
+int res_trk_close_secure_session()
+{
+	return 0;
+}
+u32 get_res_trk_perf_level(enum vcd_perf_level perf_level)
+{
+	return -ENOTSUPP;
+}
+u32 res_trk_is_cp_enabled(void)
+{
+	if (resource_context.vidc_platform_data->cp_enabled)
+		return 1;
+	else
+		return 0;
+}
+u32 res_trk_estimate_perf_level(u32 pn_perf_lvl)
+{
+	return 0;
+}
+
diff --git a/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker.h b/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker.h
new file mode 100644
index 0000000..2b92a42
--- /dev/null
+++ b/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker.h
@@ -0,0 +1,57 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef _VIDEO_720P_RESOURCE_TRACKER_H_
+#define _VIDEO_720P_RESOURCE_TRACKER_H_
+#include <mach/board.h>
+#include "vcd_res_tracker_api.h"
+
+#define VCD_RESTRK_MIN_PERF_LEVEL 37900
+#define VCD_RESTRK_MAX_PERF_LEVEL 108000
+#define VCD_RESTRK_MIN_FREQ_POINT 61440000
+#define VCD_RESTRK_MAX_FREQ_POINT 170667000
+#define VCD_RESTRK_HZ_PER_1000_PERFLVL 1580250
+
+struct res_trk_context {
+	struct device *device;
+	u32 irq_num;
+	struct mutex lock;
+	struct clk *hclk;
+	struct clk *hclk_div2;
+	struct clk *pclk;
+	unsigned long hclk_rate;
+	unsigned int clock_enabled;
+	unsigned int rail_enabled;
+	struct regulator *regulator;
+	struct msm_vidc_platform_data *vidc_platform_data;
+	u32 core_type;
+	int memtype;
+	u32 secure_session;
+};
+
+#if DEBUG
+
+#define VCDRES_MSG_LOW(xx_fmt...)	printk(KERN_INFO "\n\t* " xx_fmt)
+#define VCDRES_MSG_MED(xx_fmt...)	printk(KERN_INFO "\n  * " xx_fmt)
+
+#else
+
+#define VCDRES_MSG_LOW(xx_fmt...)
+#define VCDRES_MSG_MED(xx_fmt...)
+
+#endif
+
+#define VCDRES_MSG_HIGH(xx_fmt...)	printk(KERN_WARNING "\n" xx_fmt)
+#define VCDRES_MSG_ERROR(xx_fmt...)	printk(KERN_ERR "\n err: " xx_fmt)
+#define VCDRES_MSG_FATAL(xx_fmt...)	printk(KERN_ERR "\n<FATAL> " xx_fmt)
+
+#endif
diff --git a/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker_api.h b/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker_api.h
new file mode 100644
index 0000000..75fdb3e
--- /dev/null
+++ b/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker_api.h
@@ -0,0 +1,43 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef _VIDEO_720P_RESOURCE_TRACKER_API_H_
+#define _VIDEO_720P_RESOURCE_TRACKER_API_H_
+
+#include "vcd_core.h"
+#include "vcd_ddl.h"
+
+void res_trk_init(struct device *device, u32 irq);
+u32 res_trk_power_up(void);
+u32 res_trk_power_down(void);
+u32 res_trk_enable_clocks(void);
+u32 res_trk_disable_clocks(void);
+u32 res_trk_get_max_perf_level(u32 *pn_max_perf_lvl);
+u32 res_trk_set_perf_level(u32 req_perf_lvl, u32 *pn_set_perf_lvl,
+	struct vcd_dev_ctxt *dev_ctxt);
+u32 res_trk_get_curr_perf_level(u32 *pn_perf_lvl);
+u32 res_trk_download_firmware(void);
+u32 res_trk_get_core_type(void);
+u32 res_trk_get_mem_type(void);
+u32 res_trk_get_disable_fullhd(void);
+u32 res_trk_get_enable_ion(void);
+u32 res_trk_is_cp_enabled(void);
+struct ion_client *res_trk_get_ion_client(void);
+void res_trk_set_mem_type(enum ddl_mem_area mem_type);
+int res_trk_check_for_sec_session(void);
+int res_trk_open_secure_session(void);
+int res_trk_close_secure_session(void);
+void res_trk_secure_set(void);
+void res_trk_secure_unset(void);
+u32 get_res_trk_perf_level(enum vcd_perf_level perf_level);
+u32 res_trk_estimate_perf_level(u32 pn_perf_lvl);
+#endif
diff --git a/drivers/video/msm/vidc/Kconfig b/drivers/video/msm/vidc/Kconfig
new file mode 100644
index 0000000..9ffcb15
--- /dev/null
+++ b/drivers/video/msm/vidc/Kconfig
@@ -0,0 +1,39 @@
+#
+# VIDEO CORE
+#
+menuconfig MSM_VIDC
+	bool "Video Core Driver"
+	depends on ARCH_MSM8X60 || ARCH_MSM7X30 || ARCH_MSM8960
+	default y
+	---help---
+	Say Y here to see options for video device drivers.
+	If you say N, all options in this submenu will be skipped and disabled.
+
+config MSM_VIDC_720P
+	bool "720P Video Core"
+	depends on MSM_VIDC && ARCH_MSM7X30
+	default y
+	help
+	This option enables support for Video core.
+
+config MSM_VIDC_1080P
+	bool "1080P Video Core"
+	depends on MSM_VIDC && (ARCH_MSM8X60 || ARCH_MSM8960)
+	default y
+	help
+	This option enables support for Video core.
+
+config MSM_VIDC_VENC
+	tristate "Video encoder"
+	depends on MSM_VIDC
+	default y
+	help
+	This option enables support for Video encoder.
+
+config MSM_VIDC_VDEC
+	tristate "Video decoder"
+	depends on MSM_VIDC
+	default y
+	help
+	This option enables support for Video decoder.
+
diff --git a/drivers/video/msm/vidc/Makefile b/drivers/video/msm/vidc/Makefile
new file mode 100644
index 0000000..af41f18
--- /dev/null
+++ b/drivers/video/msm/vidc/Makefile
@@ -0,0 +1,62 @@
+ifdef CONFIG_MSM_VIDC_720P
+EXTRA_CFLAGS += -Idrivers/video/msm/vidc/720p/ddl
+EXTRA_CFLAGS += -Idrivers/video/msm/vidc/720p/resource_tracker
+endif
+
+ifdef CONFIG_MSM_VIDC_1080P
+EXTRA_CFLAGS  += -Idrivers/video/msm/vidc/1080p/ddl
+EXTRA_CFLAGS  += -Idrivers/video/msm/vidc/1080p/resource_tracker
+endif
+
+EXTRA_CFLAGS  += -Idrivers/video/msm/vidc/common/dec
+EXTRA_CFLAGS  += -Idrivers/video/msm/vidc/common/enc
+EXTRA_CFLAGS  += -Idrivers/video/msm/vidc/common/vcd
+EXTRA_CFLAGS  += -Idrivers/video/msm/vidc/common/init
+
+obj-$(CONFIG_MSM_VIDC) += vidc.o
+
+vidc-objs :=	common/init/vidc_init.o \
+		common/vcd/vcd_api.o \
+		common/vcd/vcd_power_sm.o \
+		common/vcd/vcd_client_sm.o \
+		common/vcd/vcd_device_sm.o \
+		common/vcd/vcd_scheduler.o \
+		common/vcd/vcd_sub.o \
+
+ifdef CONFIG_MSM_VIDC_720P
+vidc-objs +=	720p/ddl/vcd_ddl_firmware.o \
+		720p/ddl/vcd_ddl_metadata.o \
+		720p/ddl/vidc.o \
+		720p/ddl/vcd_ddl_utils.o \
+		720p/ddl/vcd_ddl.o \
+		720p/ddl/vcd_ddl_helper.o \
+		720p/ddl/vcd_ddl_interrupt_handler.o \
+		720p/ddl/vcd_ddl_hal.o \
+		720p/ddl/vcd_ddl_properties.o \
+		720p/resource_tracker/vcd_res_tracker.o \
+		720p/ddl/vcd_ddl_errors.o
+endif
+
+ifdef CONFIG_MSM_VIDC_1080P
+vidc-objs +=	1080p/ddl/vcd_ddl_helper.o \
+		1080p/ddl/vcd_ddl_utils.o \
+		1080p/ddl/vcd_ddl_interrupt_handler.o \
+		1080p/ddl/vcd_ddl_properties.o \
+		1080p/ddl/vcd_ddl_errors.o \
+		1080p/ddl/vcd_ddl_shared_mem.o \
+		1080p/ddl/vidc.o \
+		1080p/ddl/vidc_pix_cache.o \
+		1080p/ddl/vcd_ddl_vidc.o \
+		1080p/ddl/vcd_ddl.o \
+		1080p/ddl/vcd_ddl_metadata.o \
+		1080p/resource_tracker/vcd_res_tracker.o
+endif
+
+obj-$(CONFIG_MSM_VIDC_VDEC) += vidc_vdec.o
+
+vidc_vdec-objs :=	common/dec/vdec.o
+
+obj-$(CONFIG_MSM_VIDC_VENC) += vidc_venc.o
+
+vidc_venc-objs :=	common/enc/venc.o \
+			common/enc/venc_internal.o
diff --git a/drivers/video/msm/vidc/common/dec/vdec.c b/drivers/video/msm/vidc/common/dec/vdec.c
new file mode 100644
index 0000000..11177b8
--- /dev/null
+++ b/drivers/video/msm/vidc/common/dec/vdec.c
@@ -0,0 +1,2380 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <linux/android_pmem.h>
+#include <linux/clk.h>
+#include <linux/timer.h>
+#include <mach/msm_subsystem_map.h>
+#include <media/msm/vidc_type.h>
+#include <media/msm/vcd_api.h>
+#include <media/msm/vidc_init.h>
+#include "vcd_res_tracker_api.h"
+#include "vdec_internal.h"
+
+
+
+#define DBG(x...) pr_debug(x)
+#define INFO(x...) pr_info(x)
+#define ERR(x...) pr_err(x)
+
+#define VID_DEC_NAME "msm_vidc_dec"
+
+static char *node_name[2] = {"", "_sec"};
+static struct vid_dec_dev *vid_dec_device_p;
+static dev_t vid_dec_dev_num;
+static struct class *vid_dec_class;
+
+static unsigned int vidc_mmu_subsystem[] = {
+	MSM_SUBSYSTEM_VIDEO};
+static s32 vid_dec_get_empty_client_index(void)
+{
+	u32 i, found = false;
+
+	for (i = 0; i < VIDC_MAX_NUM_CLIENTS; i++) {
+		if (!vid_dec_device_p->vdec_clients[i].vcd_handle) {
+			found = true;
+			break;
+		}
+	}
+	if (!found) {
+		ERR("%s():ERROR No space for new client\n", __func__);
+		return -ENOMEM;
+	} else {
+		DBG("%s(): available client index = %u\n", __func__, i);
+		return i;
+	}
+}
+
+u32 vid_dec_get_status(u32 status)
+{
+	u32 vdec_status;
+
+	switch (status) {
+	case VCD_ERR_SEQHDR_PARSE_FAIL:
+	case VCD_ERR_BITSTREAM_ERR:
+		vdec_status = VDEC_S_INPUT_BITSTREAM_ERR;
+		break;
+	case VCD_S_SUCCESS:
+		vdec_status = VDEC_S_SUCCESS;
+		break;
+	case VCD_ERR_FAIL:
+		vdec_status = VDEC_S_EFAIL;
+		break;
+	case VCD_ERR_ALLOC_FAIL:
+		vdec_status = VDEC_S_ENOSWRES;
+		break;
+	case VCD_ERR_ILLEGAL_OP:
+		vdec_status = VDEC_S_EINVALCMD;
+		break;
+	case VCD_ERR_ILLEGAL_PARM:
+		vdec_status = VDEC_S_EBADPARAM;
+		break;
+	case VCD_ERR_BAD_POINTER:
+	case VCD_ERR_BAD_HANDLE:
+		vdec_status = VDEC_S_EFATAL;
+		break;
+	case VCD_ERR_NOT_SUPPORTED:
+		vdec_status = VDEC_S_ENOTSUPP;
+		break;
+	case VCD_ERR_BAD_STATE:
+		vdec_status = VDEC_S_EINVALSTATE;
+		break;
+	case VCD_ERR_BUSY:
+		vdec_status = VDEC_S_BUSY;
+		break;
+	case VCD_ERR_MAX_CLIENT:
+		vdec_status = VDEC_S_ENOHWRES;
+		break;
+	default:
+		vdec_status = VDEC_S_EFAIL;
+		break;
+	}
+
+	return vdec_status;
+}
+
+static void vid_dec_notify_client(struct video_client_ctx *client_ctx)
+{
+	if (client_ctx)
+		complete(&client_ctx->event);
+}
+
+void vid_dec_vcd_open_done(struct video_client_ctx *client_ctx,
+			   struct vcd_handle_container *handle_container)
+{
+	DBG("vid_dec_vcd_open_done\n");
+
+	if (client_ctx) {
+		if (handle_container)
+			client_ctx->vcd_handle = handle_container->handle;
+		else
+			ERR("%s(): ERROR. handle_container is NULL\n",
+			    __func__);
+
+		vid_dec_notify_client(client_ctx);
+	} else
+		ERR("%s(): ERROR. client_ctx is NULL\n", __func__);
+}
+
+static void vid_dec_handle_field_drop(struct video_client_ctx *client_ctx,
+	u32 event, u32 status, int64_t time_stamp)
+{
+	struct vid_dec_msg *vdec_msg;
+
+	if (!client_ctx) {
+		ERR("%s() NULL pointer\n", __func__);
+		return;
+	}
+
+	vdec_msg = kzalloc(sizeof(struct vid_dec_msg), GFP_KERNEL);
+	if (!vdec_msg) {
+		ERR("%s(): cannot allocate vid_dec_msg "
+			" buffer\n", __func__);
+		return;
+	}
+	vdec_msg->vdec_msg_info.status_code = vid_dec_get_status(status);
+	if (event == VCD_EVT_IND_INFO_FIELD_DROPPED) {
+		vdec_msg->vdec_msg_info.msgcode =
+			VDEC_MSG_EVT_INFO_FIELD_DROPPED;
+		vdec_msg->vdec_msg_info.msgdata.output_frame.time_stamp
+		= time_stamp;
+		DBG("Send FIELD_DROPPED message to client = %p\n", client_ctx);
+	} else {
+		ERR("vid_dec_input_frame_done(): invalid event type: "
+			"%d\n", event);
+		vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_INVALID;
+	}
+	vdec_msg->vdec_msg_info.msgdatasize =
+		sizeof(struct vdec_output_frameinfo);
+	mutex_lock(&client_ctx->msg_queue_lock);
+	list_add_tail(&vdec_msg->list, &client_ctx->msg_queue);
+	mutex_unlock(&client_ctx->msg_queue_lock);
+	wake_up(&client_ctx->msg_wait);
+}
+
+static void vid_dec_input_frame_done(struct video_client_ctx *client_ctx,
+				     u32 event, u32 status,
+				     struct vcd_frame_data *vcd_frame_data)
+{
+	struct vid_dec_msg *vdec_msg;
+
+	if (!client_ctx || !vcd_frame_data) {
+		ERR("vid_dec_input_frame_done() NULL pointer\n");
+		return;
+	}
+
+	kfree(vcd_frame_data->desc_buf);
+	vcd_frame_data->desc_buf = NULL;
+	vcd_frame_data->desc_size = 0;
+
+	vdec_msg = kzalloc(sizeof(struct vid_dec_msg), GFP_KERNEL);
+	if (!vdec_msg) {
+		ERR("vid_dec_input_frame_done(): cannot allocate vid_dec_msg "
+		    " buffer\n");
+		return;
+	}
+
+	vdec_msg->vdec_msg_info.status_code = vid_dec_get_status(status);
+
+	if (event == VCD_EVT_RESP_INPUT_DONE) {
+		vdec_msg->vdec_msg_info.msgcode =
+		    VDEC_MSG_RESP_INPUT_BUFFER_DONE;
+		DBG("Send INPUT_DON message to client = %p\n", client_ctx);
+
+	} else if (event == VCD_EVT_RESP_INPUT_FLUSHED) {
+		vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_RESP_INPUT_FLUSHED;
+		DBG("Send INPUT_FLUSHED message to client = %p\n", client_ctx);
+	} else {
+		ERR("vid_dec_input_frame_done(): invalid event type: "
+			"%d\n", event);
+		vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_INVALID;
+	}
+
+	vdec_msg->vdec_msg_info.msgdata.input_frame_clientdata =
+	    (void *)vcd_frame_data->frm_clnt_data;
+	vdec_msg->vdec_msg_info.msgdatasize = sizeof(void *);
+
+	mutex_lock(&client_ctx->msg_queue_lock);
+	list_add_tail(&vdec_msg->list, &client_ctx->msg_queue);
+	mutex_unlock(&client_ctx->msg_queue_lock);
+	wake_up(&client_ctx->msg_wait);
+}
+
+static void vid_dec_output_frame_done(struct video_client_ctx *client_ctx,
+			u32 event, u32 status,
+			struct vcd_frame_data *vcd_frame_data)
+{
+	struct vid_dec_msg *vdec_msg;
+
+	unsigned long kernel_vaddr = 0, phy_addr = 0, user_vaddr = 0;
+	int pmem_fd;
+	struct file *file;
+	s32 buffer_index = -1;
+	enum vdec_picture pic_type;
+	u32 ion_flag = 0;
+	struct ion_handle *buff_handle = NULL;
+	struct vdec_output_frameinfo  *output_frame;
+
+	if (!client_ctx || !vcd_frame_data) {
+		ERR("vid_dec_input_frame_done() NULL pointer\n");
+		return;
+	}
+
+	vdec_msg = kzalloc(sizeof(struct vid_dec_msg), GFP_KERNEL);
+	if (!vdec_msg) {
+		ERR("vid_dec_input_frame_done(): cannot allocate vid_dec_msg "
+		    " buffer\n");
+		return;
+	}
+
+	vdec_msg->vdec_msg_info.status_code = vid_dec_get_status(status);
+
+	if (event == VCD_EVT_RESP_OUTPUT_DONE)
+		vdec_msg->vdec_msg_info.msgcode =
+		    VDEC_MSG_RESP_OUTPUT_BUFFER_DONE;
+	else if (event == VCD_EVT_RESP_OUTPUT_FLUSHED)
+		vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_RESP_OUTPUT_FLUSHED;
+	else {
+		ERR("QVD: vid_dec_output_frame_done invalid cmd type: "
+			"%d\n", event);
+		vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_INVALID;
+	}
+
+	kernel_vaddr = (unsigned long)vcd_frame_data->virtual;
+
+	if (vidc_lookup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT,
+				      false, &user_vaddr, &kernel_vaddr,
+				      &phy_addr, &pmem_fd, &file,
+				      &buffer_index) ||
+		(vcd_frame_data->flags & VCD_FRAME_FLAG_EOS)) {
+		/* Buffer address in user space */
+		vdec_msg->vdec_msg_info.msgdata.output_frame.bufferaddr =
+		    (u8 *) user_vaddr;
+		/* Data length */
+		vdec_msg->vdec_msg_info.msgdata.output_frame.len =
+		    vcd_frame_data->data_len;
+		vdec_msg->vdec_msg_info.msgdata.output_frame.flags =
+		    vcd_frame_data->flags;
+		/* Timestamp pass-through from input frame */
+		vdec_msg->vdec_msg_info.msgdata.output_frame.time_stamp =
+		    vcd_frame_data->time_stamp;
+		/* Output frame client data */
+		vdec_msg->vdec_msg_info.msgdata.output_frame.client_data =
+		    (void *)vcd_frame_data->frm_clnt_data;
+		/* Associated input frame client data */
+		vdec_msg->vdec_msg_info.msgdata.output_frame.
+		    input_frame_clientdata =
+		    (void *)vcd_frame_data->ip_frm_tag;
+		/* Decoded picture width and height */
+		vdec_msg->vdec_msg_info.msgdata.output_frame.framesize.
+		bottom =
+		    vcd_frame_data->dec_op_prop.disp_frm.bottom;
+		vdec_msg->vdec_msg_info.msgdata.output_frame.framesize.left =
+		    vcd_frame_data->dec_op_prop.disp_frm.left;
+		vdec_msg->vdec_msg_info.msgdata.output_frame.framesize.right =
+			vcd_frame_data->dec_op_prop.disp_frm.right;
+		vdec_msg->vdec_msg_info.msgdata.output_frame.framesize.top =
+			vcd_frame_data->dec_op_prop.disp_frm.top;
+		if (vcd_frame_data->interlaced) {
+			vdec_msg->vdec_msg_info.msgdata.
+				output_frame.interlaced_format =
+				VDEC_InterlaceInterleaveFrameTopFieldFirst;
+		} else {
+			vdec_msg->vdec_msg_info.msgdata.
+				output_frame.interlaced_format =
+				VDEC_InterlaceFrameProgressive;
+		}
+		/* Decoded picture type */
+		switch (vcd_frame_data->frame) {
+		case VCD_FRAME_I:
+			pic_type = PICTURE_TYPE_I;
+			break;
+		case VCD_FRAME_P:
+			pic_type = PICTURE_TYPE_P;
+			break;
+		case VCD_FRAME_B:
+			pic_type = PICTURE_TYPE_B;
+			break;
+		case VCD_FRAME_NOTCODED:
+			pic_type = PICTURE_TYPE_SKIP;
+			break;
+		case VCD_FRAME_IDR:
+			pic_type = PICTURE_TYPE_IDR;
+			break;
+		default:
+			pic_type = PICTURE_TYPE_UNKNOWN;
+		}
+		vdec_msg->vdec_msg_info.msgdata.output_frame.pic_type =
+			pic_type;
+		output_frame = &vdec_msg->vdec_msg_info.msgdata.output_frame;
+		output_frame->aspect_ratio_info.aspect_ratio =
+			vcd_frame_data->aspect_ratio_info.aspect_ratio;
+		output_frame->aspect_ratio_info.par_width =
+			vcd_frame_data->aspect_ratio_info.extended_par_width;
+		output_frame->aspect_ratio_info.par_height =
+			vcd_frame_data->aspect_ratio_info.extended_par_height;
+		vdec_msg->vdec_msg_info.msgdatasize =
+		    sizeof(struct vdec_output_frameinfo);
+	} else {
+		ERR("vid_dec_output_frame_done UVA can not be found\n");
+		vdec_msg->vdec_msg_info.status_code = VDEC_S_EFATAL;
+	}
+	if (vcd_frame_data->data_len > 0) {
+		ion_flag = vidc_get_fd_info(client_ctx, BUFFER_TYPE_OUTPUT,
+				pmem_fd, kernel_vaddr, buffer_index,
+				&buff_handle);
+		if (ion_flag == CACHED && buff_handle) {
+			msm_ion_do_cache_op(client_ctx->user_ion_client,
+					buff_handle,
+					(unsigned long *) kernel_vaddr,
+					(unsigned long)vcd_frame_data->data_len,
+					ION_IOC_INV_CACHES);
+		}
+	}
+	mutex_lock(&client_ctx->msg_queue_lock);
+	list_add_tail(&vdec_msg->list, &client_ctx->msg_queue);
+	mutex_unlock(&client_ctx->msg_queue_lock);
+	wake_up(&client_ctx->msg_wait);
+}
+
+static void vid_dec_lean_event(struct video_client_ctx *client_ctx,
+			       u32 event, u32 status)
+{
+	struct vid_dec_msg *vdec_msg;
+
+	if (!client_ctx) {
+		ERR("%s(): !client_ctx pointer\n", __func__);
+		return;
+	}
+
+	vdec_msg = kzalloc(sizeof(struct vid_dec_msg), GFP_KERNEL);
+	if (!vdec_msg) {
+		ERR("%s(): cannot allocate vid_dec_msg buffer\n", __func__);
+		return;
+	}
+
+	vdec_msg->vdec_msg_info.status_code = vid_dec_get_status(status);
+
+	switch (event) {
+	case VCD_EVT_IND_OUTPUT_RECONFIG:
+		DBG("msm_vidc_dec: Sending VDEC_MSG_EVT_CONFIG_CHANGED"
+			 " to client");
+		vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_EVT_CONFIG_CHANGED;
+		break;
+	case VCD_EVT_IND_RESOURCES_LOST:
+		DBG("msm_vidc_dec: Sending VDEC_EVT_RESOURCES_LOST"
+			 " to client");
+		vdec_msg->vdec_msg_info.msgcode = VDEC_EVT_RESOURCES_LOST;
+		break;
+	case VCD_EVT_RESP_FLUSH_INPUT_DONE:
+		DBG("msm_vidc_dec: Sending VDEC_MSG_RESP_FLUSH_INPUT_DONE"
+			 " to client");
+		vdec_msg->vdec_msg_info.msgcode =
+		    VDEC_MSG_RESP_FLUSH_INPUT_DONE;
+		break;
+	case VCD_EVT_RESP_FLUSH_OUTPUT_DONE:
+		DBG("msm_vidc_dec: Sending VDEC_MSG_RESP_FLUSH_OUTPUT_DONE"
+			 " to client");
+		vdec_msg->vdec_msg_info.msgcode =
+		    VDEC_MSG_RESP_FLUSH_OUTPUT_DONE;
+		break;
+	case VCD_EVT_IND_HWERRFATAL:
+		DBG("msm_vidc_dec: Sending VDEC_MSG_EVT_HW_ERROR"
+			 " to client");
+		vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_EVT_HW_ERROR;
+		break;
+	case VCD_EVT_RESP_START:
+		DBG("msm_vidc_dec: Sending VDEC_MSG_RESP_START_DONE"
+			 " to client");
+		vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_RESP_START_DONE;
+		break;
+	case VCD_EVT_RESP_STOP:
+		DBG("msm_vidc_dec: Sending VDEC_MSG_RESP_STOP_DONE"
+			 " to client");
+		vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_RESP_STOP_DONE;
+		break;
+	case VCD_EVT_RESP_PAUSE:
+		DBG("msm_vidc_dec: Sending VDEC_MSG_RESP_PAUSE_DONE"
+			 " to client");
+		vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_RESP_PAUSE_DONE;
+		break;
+	case VCD_EVT_IND_INFO_OUTPUT_RECONFIG:
+		DBG("msm_vidc_dec: Sending VDEC_MSG_EVT_INFO_CONFIG_CHANGED"
+			 " to client");
+		vdec_msg->vdec_msg_info.msgcode =
+			 VDEC_MSG_EVT_INFO_CONFIG_CHANGED;
+		break;
+	default:
+		ERR("%s() : unknown event type\n", __func__);
+		break;
+	}
+
+	vdec_msg->vdec_msg_info.msgdatasize = 0;
+	if (client_ctx->stop_sync_cb &&
+	   (event == VCD_EVT_RESP_STOP || event == VCD_EVT_IND_HWERRFATAL)) {
+		client_ctx->stop_sync_cb = false;
+		complete(&client_ctx->event);
+		kfree(vdec_msg);
+		return;
+	}
+	mutex_lock(&client_ctx->msg_queue_lock);
+	list_add_tail(&vdec_msg->list, &client_ctx->msg_queue);
+	mutex_unlock(&client_ctx->msg_queue_lock);
+	wake_up(&client_ctx->msg_wait);
+}
+
+
+void vid_dec_vcd_cb(u32 event, u32 status,
+		   void *info, size_t sz, void *handle, void *const client_data)
+{
+	struct video_client_ctx *client_ctx =
+	    (struct video_client_ctx *)client_data;
+
+	DBG("Entering %s()\n", __func__);
+
+	if (!client_ctx) {
+		ERR("%s(): client_ctx is NULL\n", __func__);
+		return;
+	}
+
+	client_ctx->event_status = status;
+
+	switch (event) {
+	case VCD_EVT_RESP_OPEN:
+		vid_dec_vcd_open_done(client_ctx,
+				      (struct vcd_handle_container *)
+				      info);
+		break;
+	case VCD_EVT_RESP_INPUT_DONE:
+	case VCD_EVT_RESP_INPUT_FLUSHED:
+		vid_dec_input_frame_done(client_ctx, event, status,
+					 (struct vcd_frame_data *)info);
+		break;
+	case VCD_EVT_IND_INFO_FIELD_DROPPED:
+		if (info)
+			vid_dec_handle_field_drop(client_ctx, event,
+			status,	*((int64_t *)info));
+		else
+			pr_err("Wrong Payload for Field dropped\n");
+		break;
+	case VCD_EVT_RESP_OUTPUT_DONE:
+	case VCD_EVT_RESP_OUTPUT_FLUSHED:
+		vid_dec_output_frame_done(client_ctx, event, status,
+					  (struct vcd_frame_data *)info);
+		break;
+	case VCD_EVT_RESP_PAUSE:
+	case VCD_EVT_RESP_STOP:
+	case VCD_EVT_RESP_FLUSH_INPUT_DONE:
+	case VCD_EVT_RESP_FLUSH_OUTPUT_DONE:
+	case VCD_EVT_IND_OUTPUT_RECONFIG:
+	case VCD_EVT_IND_HWERRFATAL:
+	case VCD_EVT_IND_RESOURCES_LOST:
+	case VCD_EVT_IND_INFO_OUTPUT_RECONFIG:
+		vid_dec_lean_event(client_ctx, event, status);
+		break;
+	case VCD_EVT_RESP_START:
+		if (!client_ctx->seq_header_set)
+			vid_dec_lean_event(client_ctx, event, status);
+		else
+			vid_dec_notify_client(client_ctx);
+		break;
+	default:
+		ERR("%s() :  Error - Invalid event type =%u\n", __func__,
+		    event);
+		break;
+	}
+}
+
+static u32 vid_dec_set_codec(struct video_client_ctx *client_ctx,
+			     enum vdec_codec *vdec_codec)
+{
+	u32 result = true;
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_codec codec;
+	u32 vcd_status = VCD_ERR_FAIL;
+
+	if (!client_ctx || !vdec_codec)
+		return false;
+
+	vcd_property_hdr.prop_id = VCD_I_CODEC;
+	vcd_property_hdr.sz = sizeof(struct vcd_property_codec);
+
+	switch (*vdec_codec) {
+	case VDEC_CODECTYPE_MPEG4:
+		codec.codec = VCD_CODEC_MPEG4;
+		break;
+	case VDEC_CODECTYPE_H264:
+		codec.codec = VCD_CODEC_H264;
+		break;
+	case VDEC_CODECTYPE_DIVX_3:
+		codec.codec = VCD_CODEC_DIVX_3;
+		break;
+	case VDEC_CODECTYPE_DIVX_4:
+		codec.codec = VCD_CODEC_DIVX_4;
+		break;
+	case VDEC_CODECTYPE_DIVX_5:
+		codec.codec = VCD_CODEC_DIVX_5;
+		break;
+	case VDEC_CODECTYPE_DIVX_6:
+		codec.codec = VCD_CODEC_DIVX_6;
+		break;
+	case VDEC_CODECTYPE_XVID:
+		codec.codec = VCD_CODEC_XVID;
+		break;
+	case VDEC_CODECTYPE_H263:
+		codec.codec = VCD_CODEC_H263;
+		break;
+	case VDEC_CODECTYPE_MPEG2:
+		codec.codec = VCD_CODEC_MPEG2;
+		break;
+	case VDEC_CODECTYPE_VC1:
+		codec.codec = VCD_CODEC_VC1;
+		break;
+	case VDEC_CODECTYPE_VC1_RCV:
+		codec.codec = VCD_CODEC_VC1_RCV;
+		break;
+	default:
+		result = false;
+		break;
+	}
+
+	if (result) {
+		vcd_status = vcd_set_property(client_ctx->vcd_handle,
+					      &vcd_property_hdr, &codec);
+		if (vcd_status)
+			result = false;
+	}
+	return result;
+}
+
+static u32 vid_dec_set_output_format(struct video_client_ctx *client_ctx,
+				     enum vdec_output_fromat *output_format)
+{
+	u32 result = true;
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_buffer_format vcd_prop_buffer_format;
+	u32 vcd_status = VCD_ERR_FAIL;
+
+	if (!client_ctx || !output_format)
+		return false;
+
+	vcd_property_hdr.prop_id = VCD_I_BUFFER_FORMAT;;
+	vcd_property_hdr.sz =
+	    sizeof(struct vcd_property_buffer_format);
+
+	switch (*output_format) {
+	case VDEC_YUV_FORMAT_NV12:
+		vcd_prop_buffer_format.buffer_format = VCD_BUFFER_FORMAT_NV12;
+		break;
+	case VDEC_YUV_FORMAT_TILE_4x2:
+		vcd_prop_buffer_format.buffer_format =
+		    VCD_BUFFER_FORMAT_TILE_4x2;
+		break;
+	default:
+		result = false;
+		break;
+	}
+
+	if (result)
+		vcd_status = vcd_set_property(client_ctx->vcd_handle,
+					      &vcd_property_hdr,
+					      &vcd_prop_buffer_format);
+
+	if (vcd_status)
+		return false;
+	else
+		return true;
+}
+
+static u32 vid_dec_set_frame_resolution(struct video_client_ctx *client_ctx,
+					struct vdec_picsize *video_resoultion)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_frame_size frame_resolution;
+	u32 vcd_status = VCD_ERR_FAIL;
+
+	if (!client_ctx || !video_resoultion)
+		return false;
+
+	vcd_property_hdr.prop_id = VCD_I_FRAME_SIZE;
+	vcd_property_hdr.sz = sizeof(struct vcd_property_frame_size);
+	frame_resolution.width = video_resoultion->frame_width;
+	frame_resolution.height = video_resoultion->frame_height;
+	frame_resolution.stride = video_resoultion->stride;
+	frame_resolution.scan_lines = video_resoultion->scan_lines;
+
+	vcd_status = vcd_set_property(client_ctx->vcd_handle,
+				      &vcd_property_hdr, &frame_resolution);
+
+	if (vcd_status)
+		return false;
+	else
+		return true;
+}
+
+static u32 vid_dec_get_frame_resolution(struct video_client_ctx *client_ctx,
+					struct vdec_picsize *video_resoultion)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_frame_size frame_resolution;
+	u32 vcd_status = VCD_ERR_FAIL;
+
+	if (!client_ctx || !video_resoultion)
+		return false;
+
+	vcd_property_hdr.prop_id = VCD_I_FRAME_SIZE;
+	vcd_property_hdr.sz = sizeof(struct vcd_property_frame_size);
+
+	vcd_status = vcd_get_property(client_ctx->vcd_handle, &vcd_property_hdr,
+					  &frame_resolution);
+
+	video_resoultion->frame_width = frame_resolution.width;
+	video_resoultion->frame_height = frame_resolution.height;
+	video_resoultion->scan_lines = frame_resolution.scan_lines;
+	video_resoultion->stride = frame_resolution.stride;
+
+	if (vcd_status)
+		return false;
+	else
+		return true;
+}
+
+static u32 vid_dec_get_progressive_only(struct video_client_ctx *client_ctx,
+					u32 *progressive_only)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	if (!client_ctx || !progressive_only)
+		return false;
+	vcd_property_hdr.prop_id = VCD_I_PROGRESSIVE_ONLY;
+	vcd_property_hdr.sz = sizeof(u32);
+	if (vcd_get_property(client_ctx->vcd_handle, &vcd_property_hdr,
+						 progressive_only))
+		return false;
+	else
+		return true;
+}
+
+static u32 vid_dec_get_disable_dmx_support(struct video_client_ctx *client_ctx,
+					   u32 *disable_dmx)
+{
+
+	struct vcd_property_hdr vcd_property_hdr;
+	if (!client_ctx || !disable_dmx)
+		return false;
+	vcd_property_hdr.prop_id = VCD_I_DISABLE_DMX_SUPPORT;
+	vcd_property_hdr.sz = sizeof(u32);
+	if (vcd_get_property(client_ctx->vcd_handle, &vcd_property_hdr,
+						 disable_dmx))
+		return false;
+	else
+		return true;
+}
+static u32 vid_dec_get_disable_dmx(struct video_client_ctx *client_ctx,
+					   u32 *disable_dmx)
+{
+
+	struct vcd_property_hdr vcd_property_hdr;
+	if (!client_ctx || !disable_dmx)
+		return false;
+	vcd_property_hdr.prop_id = VCD_I_DISABLE_DMX;
+	vcd_property_hdr.sz = sizeof(u32);
+	if (vcd_get_property(client_ctx->vcd_handle, &vcd_property_hdr,
+						 disable_dmx))
+		return false;
+	else
+		return true;
+}
+
+static u32 vid_dec_set_disable_dmx(struct video_client_ctx *client_ctx)
+{
+
+	struct vcd_property_hdr vcd_property_hdr;
+	u32 vcd_disable_dmx;
+	if (!client_ctx)
+		return false;
+	vcd_property_hdr.prop_id = VCD_I_DISABLE_DMX;
+	vcd_property_hdr.sz = sizeof(u32);
+	vcd_disable_dmx = true;
+	DBG("%s() : Setting Disable DMX: %d\n",
+		__func__, vcd_disable_dmx);
+
+	if (vcd_set_property(client_ctx->vcd_handle, &vcd_property_hdr,
+						 &vcd_disable_dmx))
+		return false;
+	else
+		return true;
+}
+
+static u32 vid_dec_set_picture_order(struct video_client_ctx *client_ctx,
+					u32 *picture_order)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	u32 vcd_status = VCD_ERR_FAIL, vcd_picture_order, ret = true;
+	if (!client_ctx || !picture_order)
+		return false;
+	vcd_property_hdr.prop_id = VCD_I_OUTPUT_ORDER;
+	vcd_property_hdr.sz = sizeof(u32);
+	if (*picture_order == VDEC_ORDER_DISPLAY)
+		vcd_picture_order = VCD_DEC_ORDER_DISPLAY;
+	else if (*picture_order == VDEC_ORDER_DECODE)
+		vcd_picture_order = VCD_DEC_ORDER_DECODE;
+	else
+		ret = false;
+	if (ret) {
+		DBG("%s() : Setting output picture order: %d\n",
+		    __func__, vcd_picture_order);
+		vcd_status = vcd_set_property(client_ctx->vcd_handle,
+				      &vcd_property_hdr, &vcd_picture_order);
+		if (vcd_status != VCD_S_SUCCESS)
+			ret = false;
+	}
+	return ret;
+}
+
+static u32 vid_dec_set_frame_rate(struct video_client_ctx *client_ctx,
+					struct vdec_framerate *frame_rate)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_frame_rate vcd_frame_rate;
+	u32 vcd_status = VCD_ERR_FAIL;
+
+	if (!client_ctx || !frame_rate)
+		return false;
+
+	vcd_property_hdr.prop_id = VCD_I_FRAME_RATE;
+	vcd_property_hdr.sz = sizeof(struct vcd_property_frame_rate);
+	vcd_frame_rate.fps_numerator = frame_rate->fps_numerator;
+	vcd_frame_rate.fps_denominator = frame_rate->fps_denominator;
+
+	vcd_status = vcd_set_property(client_ctx->vcd_handle,
+				      &vcd_property_hdr, &vcd_frame_rate);
+
+	if (vcd_status)
+		return false;
+	else
+		return true;
+}
+
+static u32 vid_dec_set_extradata(struct video_client_ctx *client_ctx,
+					u32 *extradata_flag)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_meta_data_enable vcd_meta_data;
+	u32 vcd_status = VCD_ERR_FAIL;
+	if (!client_ctx || !extradata_flag)
+		return false;
+	vcd_property_hdr.prop_id = VCD_I_METADATA_ENABLE;
+	vcd_property_hdr.sz = sizeof(struct vcd_property_meta_data_enable);
+	vcd_meta_data.meta_data_enable_flag = *extradata_flag;
+	vcd_status = vcd_set_property(client_ctx->vcd_handle,
+				      &vcd_property_hdr, &vcd_meta_data);
+	if (vcd_status)
+		return false;
+	else
+		return true;
+}
+
+static u32 vid_dec_set_idr_only_decoding(struct video_client_ctx *client_ctx)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	u32 vcd_status = VCD_ERR_FAIL;
+	u32 enable = true;
+	if (!client_ctx)
+		return false;
+	vcd_property_hdr.prop_id = VCD_I_DEC_PICTYPE;
+	vcd_property_hdr.sz = sizeof(u32);
+	vcd_status = vcd_set_property(client_ctx->vcd_handle,
+			&vcd_property_hdr, &enable);
+	if (vcd_status)
+		return false;
+	return true;
+}
+
+static u32 vid_dec_set_h264_mv_buffers(struct video_client_ctx *client_ctx,
+					struct vdec_h264_mv *mv_data)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_h264_mv_buffer *vcd_h264_mv_buffer = NULL;
+	struct msm_mapped_buffer *mapped_buffer = NULL;
+	u32 vcd_status = VCD_ERR_FAIL;
+	u32 len = 0, flags = 0;
+	struct file *file;
+	int rc = 0;
+	unsigned long ionflag = 0;
+	unsigned long buffer_size = 0;
+	unsigned long iova = 0;
+
+	if (!client_ctx || !mv_data)
+		return false;
+
+	vcd_property_hdr.prop_id = VCD_I_H264_MV_BUFFER;
+	vcd_property_hdr.sz = sizeof(struct vcd_property_h264_mv_buffer);
+	vcd_h264_mv_buffer = &client_ctx->vcd_h264_mv_buffer;
+
+	memset(&client_ctx->vcd_h264_mv_buffer, 0,
+		   sizeof(struct vcd_property_h264_mv_buffer));
+	vcd_h264_mv_buffer->size = mv_data->size;
+	vcd_h264_mv_buffer->count = mv_data->count;
+	vcd_h264_mv_buffer->pmem_fd = mv_data->pmem_fd;
+	vcd_h264_mv_buffer->offset = mv_data->offset;
+
+	if (!vcd_get_ion_status()) {
+		if (get_pmem_file(vcd_h264_mv_buffer->pmem_fd,
+			(unsigned long *) (&(vcd_h264_mv_buffer->
+			physical_addr)),
+			(unsigned long *) (&vcd_h264_mv_buffer->
+						kernel_virtual_addr),
+			(unsigned long *) (&len), &file)) {
+			ERR("%s(): get_pmem_file failed\n", __func__);
+			return false;
+		}
+		put_pmem_file(file);
+		flags = MSM_SUBSYSTEM_MAP_IOVA;
+		mapped_buffer = msm_subsystem_map_buffer(
+			(unsigned long)vcd_h264_mv_buffer->physical_addr, len,
+				flags, vidc_mmu_subsystem,
+				sizeof(vidc_mmu_subsystem)/
+				sizeof(unsigned int));
+		if (IS_ERR(mapped_buffer)) {
+			pr_err("buffer map failed");
+			return false;
+		}
+		vcd_h264_mv_buffer->client_data = (void *) mapped_buffer;
+		vcd_h264_mv_buffer->dev_addr = (u8 *)mapped_buffer->iova[0];
+	} else {
+		client_ctx->h264_mv_ion_handle = ion_import_fd(
+					client_ctx->user_ion_client,
+					vcd_h264_mv_buffer->pmem_fd);
+		if (IS_ERR_OR_NULL(client_ctx->h264_mv_ion_handle)) {
+			ERR("%s(): get_ION_handle failed\n", __func__);
+			goto import_ion_error;
+		}
+		rc = ion_handle_get_flags(client_ctx->user_ion_client,
+					client_ctx->h264_mv_ion_handle,
+					&ionflag);
+		if (rc) {
+			ERR("%s():get_ION_flags fail\n",
+					 __func__);
+			goto import_ion_error;
+		}
+		vcd_h264_mv_buffer->kernel_virtual_addr = (u8 *) ion_map_kernel(
+			client_ctx->user_ion_client,
+			client_ctx->h264_mv_ion_handle,
+			ionflag);
+		if (!vcd_h264_mv_buffer->kernel_virtual_addr) {
+			ERR("%s(): get_ION_kernel virtual addr failed\n",
+				 __func__);
+			goto import_ion_error;
+		}
+		if (res_trk_check_for_sec_session()) {
+			rc = ion_phys(client_ctx->user_ion_client,
+				client_ctx->h264_mv_ion_handle,
+				(unsigned long *) (&(vcd_h264_mv_buffer->
+				physical_addr)), &len);
+			if (rc) {
+				ERR("%s():get_ION_kernel physical addr fail\n",
+					__func__);
+				goto ion_map_error;
+			}
+			vcd_h264_mv_buffer->client_data = NULL;
+			vcd_h264_mv_buffer->dev_addr = (u8 *)
+				vcd_h264_mv_buffer->physical_addr;
+		} else {
+			rc = ion_map_iommu(client_ctx->user_ion_client,
+					client_ctx->h264_mv_ion_handle,
+					VIDEO_DOMAIN, VIDEO_MAIN_POOL,
+					SZ_4K, 0, (unsigned long *)&iova,
+					(unsigned long *)&buffer_size,
+					UNCACHED, 0);
+			if (rc) {
+				ERR("%s():get_ION_kernel physical addr fail\n",
+						 __func__);
+				goto ion_map_error;
+			}
+			vcd_h264_mv_buffer->physical_addr = (u8 *) iova;
+			vcd_h264_mv_buffer->client_data = NULL;
+			vcd_h264_mv_buffer->dev_addr = (u8 *) iova;
+		}
+	}
+	DBG("Virt: %p, Phys %p, fd: %d", vcd_h264_mv_buffer->
+		kernel_virtual_addr, vcd_h264_mv_buffer->physical_addr,
+		vcd_h264_mv_buffer->pmem_fd);
+	DBG("Dev addr %p", vcd_h264_mv_buffer->dev_addr);
+	vcd_status = vcd_set_property(client_ctx->vcd_handle,
+				      &vcd_property_hdr, vcd_h264_mv_buffer);
+
+	if (vcd_status)
+		return false;
+	else
+		return true;
+ion_map_error:
+	if (vcd_h264_mv_buffer->kernel_virtual_addr) {
+		ion_unmap_kernel(client_ctx->user_ion_client,
+				client_ctx->h264_mv_ion_handle);
+		vcd_h264_mv_buffer->kernel_virtual_addr = NULL;
+	}
+	if (!IS_ERR_OR_NULL(client_ctx->h264_mv_ion_handle)) {
+		ion_free(client_ctx->user_ion_client,
+			client_ctx->h264_mv_ion_handle);
+		 client_ctx->h264_mv_ion_handle = NULL;
+	}
+import_ion_error:
+	return false;
+}
+
+static u32 vid_dec_set_cont_on_reconfig(struct video_client_ctx *client_ctx)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	u32 vcd_status = VCD_ERR_FAIL;
+	u32 enable = true;
+	if (!client_ctx)
+		return false;
+	vcd_property_hdr.prop_id = VCD_I_CONT_ON_RECONFIG;
+	vcd_property_hdr.sz = sizeof(u32);
+	vcd_status = vcd_set_property(client_ctx->vcd_handle,
+		&vcd_property_hdr, &enable);
+	if (vcd_status)
+		return false;
+	return true;
+}
+
+static u32 vid_dec_get_h264_mv_buffer_size(struct video_client_ctx *client_ctx,
+					struct vdec_mv_buff_size *mv_buff)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_buffer_size h264_mv_buffer_size;
+	u32 vcd_status = VCD_ERR_FAIL;
+
+	if (!client_ctx || !mv_buff)
+		return false;
+
+	vcd_property_hdr.prop_id = VCD_I_GET_H264_MV_SIZE;
+	vcd_property_hdr.sz = sizeof(struct vcd_property_buffer_size);
+
+	h264_mv_buffer_size.width = mv_buff->width;
+	h264_mv_buffer_size.height = mv_buff->height;
+
+	vcd_status = vcd_get_property(client_ctx->vcd_handle,
+				      &vcd_property_hdr, &h264_mv_buffer_size);
+
+	mv_buff->width = h264_mv_buffer_size.width;
+	mv_buff->height = h264_mv_buffer_size.height;
+	mv_buff->size = h264_mv_buffer_size.size;
+	mv_buff->alignment = h264_mv_buffer_size.alignment;
+
+	if (vcd_status)
+		return false;
+	else
+		return true;
+}
+
+static u32 vid_dec_free_h264_mv_buffers(struct video_client_ctx *client_ctx)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_buffer_size h264_mv_buffer_size;
+	u32 vcd_status = VCD_ERR_FAIL;
+
+	if (!client_ctx)
+		return false;
+	if (client_ctx->vcd_h264_mv_buffer.client_data)
+		msm_subsystem_unmap_buffer((struct msm_mapped_buffer *)
+		client_ctx->vcd_h264_mv_buffer.client_data);
+
+	vcd_property_hdr.prop_id = VCD_I_FREE_H264_MV_BUFFER;
+	vcd_property_hdr.sz = sizeof(struct vcd_property_buffer_size);
+
+	vcd_status = vcd_set_property(client_ctx->vcd_handle,
+				      &vcd_property_hdr, &h264_mv_buffer_size);
+
+	if (!IS_ERR_OR_NULL(client_ctx->h264_mv_ion_handle)) {
+		ion_unmap_kernel(client_ctx->user_ion_client,
+					client_ctx->h264_mv_ion_handle);
+		if (!res_trk_check_for_sec_session()) {
+			ion_unmap_iommu(client_ctx->user_ion_client,
+				client_ctx->h264_mv_ion_handle,
+				VIDEO_DOMAIN,
+				VIDEO_MAIN_POOL);
+		}
+		ion_free(client_ctx->user_ion_client,
+					client_ctx->h264_mv_ion_handle);
+		 client_ctx->h264_mv_ion_handle = NULL;
+	}
+
+	if (vcd_status)
+		return false;
+	else
+		return true;
+}
+
+static u32 vid_dec_get_buffer_req(struct video_client_ctx *client_ctx,
+				  struct vdec_allocatorproperty *vdec_buf_req)
+{
+	u32 vcd_status = VCD_ERR_FAIL;
+	struct vcd_buffer_requirement vcd_buf_req;
+
+	if (!client_ctx || !vdec_buf_req)
+		return false;
+
+	if (vdec_buf_req->buffer_type == VDEC_BUFFER_TYPE_INPUT) {
+		vcd_status = vcd_get_buffer_requirements(client_ctx->vcd_handle,
+							 VCD_BUFFER_INPUT,
+							 &vcd_buf_req);
+	} else {
+		vcd_status = vcd_get_buffer_requirements(client_ctx->vcd_handle,
+							 VCD_BUFFER_OUTPUT,
+							 &vcd_buf_req);
+	}
+
+	if (vcd_status) {
+		return false;
+	} else {
+		vdec_buf_req->mincount = vcd_buf_req.min_count;
+		vdec_buf_req->maxcount = vcd_buf_req.max_count;
+		vdec_buf_req->actualcount = vcd_buf_req.actual_count;
+		vdec_buf_req->buffer_size = vcd_buf_req.sz;
+		vdec_buf_req->alignment = vcd_buf_req.align;
+		vdec_buf_req->buf_poolid = vcd_buf_req.buf_pool_id;
+
+		return true;
+	}
+}
+
+static u32 vid_dec_set_buffer(struct video_client_ctx *client_ctx,
+			      struct vdec_setbuffer_cmd *buffer_info)
+{
+	enum vcd_buffer_type buffer = VCD_BUFFER_INPUT;
+	enum buffer_dir dir_buffer = BUFFER_TYPE_INPUT;
+	u32 vcd_status = VCD_ERR_FAIL;
+	unsigned long kernel_vaddr, buf_adr_offset = 0, length;
+
+	if (!client_ctx || !buffer_info)
+		return false;
+
+	if (buffer_info->buffer_type == VDEC_BUFFER_TYPE_OUTPUT) {
+		dir_buffer = BUFFER_TYPE_OUTPUT;
+		buffer = VCD_BUFFER_OUTPUT;
+		buf_adr_offset = (unsigned long)buffer_info->buffer.offset;
+	}
+	length = buffer_info->buffer.buffer_len;
+	/*If buffer cannot be set, ignore */
+	if (!vidc_insert_addr_table(client_ctx, dir_buffer,
+		(unsigned long)buffer_info->buffer.bufferaddr,
+		&kernel_vaddr, buffer_info->buffer.pmem_fd,
+		buf_adr_offset, MAX_VIDEO_NUM_OF_BUFF, length)) {
+		DBG("%s() : user_virt_addr = %p cannot be set.",
+		    __func__, buffer_info->buffer.bufferaddr);
+		return false;
+	}
+	vcd_status = vcd_set_buffer(client_ctx->vcd_handle,
+		buffer, (u8 *) kernel_vaddr,
+		buffer_info->buffer.buffer_len);
+
+	if (!vcd_status)
+		return true;
+	else
+		return false;
+}
+
+
+static u32 vid_dec_free_buffer(struct video_client_ctx *client_ctx,
+			      struct vdec_setbuffer_cmd *buffer_info)
+{
+	enum vcd_buffer_type buffer = VCD_BUFFER_INPUT;
+	enum buffer_dir dir_buffer = BUFFER_TYPE_INPUT;
+	u32 vcd_status = VCD_ERR_FAIL;
+	unsigned long kernel_vaddr;
+
+	if (!client_ctx || !buffer_info)
+		return false;
+
+	if (buffer_info->buffer_type == VDEC_BUFFER_TYPE_OUTPUT) {
+		dir_buffer = BUFFER_TYPE_OUTPUT;
+		buffer = VCD_BUFFER_OUTPUT;
+	}
+
+	/*If buffer NOT set, ignore */
+	if (!vidc_delete_addr_table(client_ctx, dir_buffer,
+				(unsigned long)buffer_info->buffer.bufferaddr,
+				&kernel_vaddr)) {
+		DBG("%s() : user_virt_addr = %p has not been set.",
+		    __func__, buffer_info->buffer.bufferaddr);
+		return true;
+	}
+	vcd_status = vcd_free_buffer(client_ctx->vcd_handle, buffer,
+					 (u8 *)kernel_vaddr);
+
+	if (!vcd_status)
+		return true;
+	else
+		return false;
+}
+
+static u32 vid_dec_pause_resume(struct video_client_ctx *client_ctx, u32 pause)
+{
+  u32 vcd_status;
+
+	if (!client_ctx) {
+		ERR("\n %s(): Invalid client_ctx", __func__);
+		return false;
+	}
+
+	if (pause) {
+		DBG("msm_vidc_dec: PAUSE command from client = %p\n",
+			 client_ctx);
+		vcd_status = vcd_pause(client_ctx->vcd_handle);
+	} else{
+		DBG("msm_vidc_dec: RESUME command from client = %p\n",
+			 client_ctx);
+		vcd_status = vcd_resume(client_ctx->vcd_handle);
+	}
+
+	if (vcd_status)
+		return false;
+
+	return true;
+
+}
+
+static u32 vid_dec_start_stop(struct video_client_ctx *client_ctx, u32 start)
+{
+	struct vid_dec_msg *vdec_msg = NULL;
+	u32 vcd_status;
+
+	DBG("msm_vidc_dec: Inside %s()", __func__);
+	if (!client_ctx) {
+		ERR("\n Invalid client_ctx");
+		return false;
+	}
+
+	if (start) {
+		if (client_ctx->seq_header_set) {
+			DBG("%s(): Seq Hdr set: Send START_DONE to client",
+				 __func__);
+			vdec_msg = kzalloc(sizeof(*vdec_msg), GFP_KERNEL);
+			if (!vdec_msg) {
+				ERR("vid_dec_start_stop: cannot allocate"
+				    "buffer\n");
+				return false;
+			}
+			vdec_msg->vdec_msg_info.msgcode =
+			    VDEC_MSG_RESP_START_DONE;
+			vdec_msg->vdec_msg_info.status_code = VDEC_S_SUCCESS;
+			vdec_msg->vdec_msg_info.msgdatasize = 0;
+			mutex_lock(&client_ctx->msg_queue_lock);
+			list_add_tail(&vdec_msg->list, &client_ctx->msg_queue);
+			mutex_unlock(&client_ctx->msg_queue_lock);
+
+			wake_up(&client_ctx->msg_wait);
+
+			DBG("Send START_DONE message to client = %p\n",
+			    client_ctx);
+
+		} else {
+			DBG("%s(): Calling decode_start()", __func__);
+			vcd_status =
+			    vcd_decode_start(client_ctx->vcd_handle, NULL);
+
+			if (vcd_status) {
+				ERR("%s(): vcd_decode_start failed."
+				    " vcd_status = %u\n", __func__, vcd_status);
+				return false;
+			}
+		}
+	} else {
+		DBG("%s(): Calling vcd_stop()", __func__);
+		mutex_lock(&vid_dec_device_p->lock);
+		vcd_status = VCD_ERR_FAIL;
+		if (!client_ctx->stop_called) {
+			client_ctx->stop_called = true;
+			vcd_status = vcd_stop(client_ctx->vcd_handle);
+		}
+		if (vcd_status) {
+			ERR("%s(): vcd_stop failed.  vcd_status = %u\n",
+				__func__, vcd_status);
+			mutex_unlock(&vid_dec_device_p->lock);
+			return false;
+		}
+		DBG("Send STOP_DONE message to client = %p\n", client_ctx);
+		mutex_unlock(&vid_dec_device_p->lock);
+	}
+	return true;
+}
+
+static u32 vid_dec_decode_frame(struct video_client_ctx *client_ctx,
+				struct vdec_input_frameinfo *input_frame_info,
+				u8 *desc_buf, u32 desc_size)
+{
+	struct vcd_frame_data vcd_input_buffer;
+	unsigned long kernel_vaddr, phy_addr, user_vaddr;
+	int pmem_fd;
+	struct file *file;
+	s32 buffer_index = -1;
+	u32 vcd_status = VCD_ERR_FAIL;
+	u32 ion_flag = 0;
+	struct ion_handle *buff_handle = NULL;
+
+	if (!client_ctx || !input_frame_info)
+		return false;
+
+	user_vaddr = (unsigned long)input_frame_info->bufferaddr;
+
+	if (vidc_lookup_addr_table(client_ctx, BUFFER_TYPE_INPUT,
+				      true, &user_vaddr, &kernel_vaddr,
+				      &phy_addr, &pmem_fd, &file,
+				      &buffer_index)) {
+
+		/* kernel_vaddr  is found. send the frame to VCD */
+		memset((void *)&vcd_input_buffer, 0,
+		       sizeof(struct vcd_frame_data));
+		vcd_input_buffer.virtual =
+		    (u8 *) (kernel_vaddr + input_frame_info->pmem_offset);
+		vcd_input_buffer.offset = input_frame_info->offset;
+		vcd_input_buffer.frm_clnt_data =
+		    (u32) input_frame_info->client_data;
+		vcd_input_buffer.ip_frm_tag =
+		    (u32) input_frame_info->client_data;
+		vcd_input_buffer.data_len = input_frame_info->datalen;
+		vcd_input_buffer.time_stamp = input_frame_info->timestamp;
+		/* Rely on VCD using the same flags as OMX */
+		vcd_input_buffer.flags = input_frame_info->flags;
+		vcd_input_buffer.desc_buf = desc_buf;
+		vcd_input_buffer.desc_size = desc_size;
+		if (vcd_input_buffer.data_len > 0) {
+			ion_flag = vidc_get_fd_info(client_ctx,
+						BUFFER_TYPE_INPUT,
+						pmem_fd,
+						kernel_vaddr,
+						buffer_index,
+						&buff_handle);
+			if (ion_flag == CACHED && buff_handle) {
+				msm_ion_do_cache_op(client_ctx->user_ion_client,
+				buff_handle,
+				(unsigned long *)kernel_vaddr,
+				(unsigned long) vcd_input_buffer.data_len,
+				ION_IOC_CLEAN_CACHES);
+			}
+		}
+		vcd_status = vcd_decode_frame(client_ctx->vcd_handle,
+					      &vcd_input_buffer);
+		if (!vcd_status)
+			return true;
+		else {
+			ERR("%s(): vcd_decode_frame failed = %u\n", __func__,
+			    vcd_status);
+			return false;
+		}
+
+	} else {
+		ERR("%s(): kernel_vaddr not found\n", __func__);
+		return false;
+	}
+}
+
+static u32 vid_dec_fill_output_buffer(struct video_client_ctx *client_ctx,
+		struct vdec_fillbuffer_cmd *fill_buffer_cmd)
+{
+	unsigned long kernel_vaddr, phy_addr, user_vaddr;
+	int pmem_fd;
+	struct file *file;
+	s32 buffer_index = -1;
+	u32 vcd_status = VCD_ERR_FAIL;
+	struct ion_handle *buff_handle = NULL;
+
+	struct vcd_frame_data vcd_frame;
+
+	if (!client_ctx || !fill_buffer_cmd)
+		return false;
+
+	user_vaddr = (unsigned long)fill_buffer_cmd->buffer.bufferaddr;
+
+	if (vidc_lookup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT,
+				      true, &user_vaddr, &kernel_vaddr,
+				      &phy_addr, &pmem_fd, &file,
+				      &buffer_index)) {
+
+		memset((void *)&vcd_frame, 0,
+		       sizeof(struct vcd_frame_data));
+		vcd_frame.virtual = (u8 *) kernel_vaddr;
+		vcd_frame.frm_clnt_data = (u32) fill_buffer_cmd->client_data;
+		vcd_frame.alloc_len = fill_buffer_cmd->buffer.buffer_len;
+		vcd_frame.ion_flag = vidc_get_fd_info(client_ctx,
+						 BUFFER_TYPE_OUTPUT,
+						pmem_fd, kernel_vaddr,
+						buffer_index,
+						&buff_handle);
+		vcd_frame.buff_ion_handle = buff_handle;
+		vcd_status = vcd_fill_output_buffer(client_ctx->vcd_handle,
+						    &vcd_frame);
+		if (!vcd_status)
+			return true;
+		else {
+			ERR("%s(): vcd_fill_output_buffer failed = %u\n",
+			    __func__, vcd_status);
+			return false;
+		}
+	} else {
+		ERR("%s(): kernel_vaddr not found\n", __func__);
+		return false;
+	}
+}
+
+
+static u32 vid_dec_flush(struct video_client_ctx *client_ctx,
+			 enum vdec_bufferflush flush_dir)
+{
+	u32 vcd_status = VCD_ERR_FAIL;
+
+	DBG("msm_vidc_dec: %s() called with dir = %u", __func__,
+		 flush_dir);
+	if (!client_ctx) {
+		ERR("\n Invalid client_ctx");
+		return false;
+	}
+
+	switch (flush_dir) {
+	case VDEC_FLUSH_TYPE_INPUT:
+		vcd_status = vcd_flush(client_ctx->vcd_handle, VCD_FLUSH_INPUT);
+		break;
+	case VDEC_FLUSH_TYPE_OUTPUT:
+		vcd_status = vcd_flush(client_ctx->vcd_handle,
+				       VCD_FLUSH_OUTPUT);
+		break;
+	case VDEC_FLUSH_TYPE_ALL:
+		vcd_status = vcd_flush(client_ctx->vcd_handle, VCD_FLUSH_ALL);
+		break;
+	default:
+		ERR("%s(): Inavlid flush cmd. flush_dir = %u\n", __func__,
+		    flush_dir);
+		return false;
+		break;
+	}
+
+	if (!vcd_status)
+		return true;
+	else {
+		ERR("%s(): vcd_flush failed. vcd_status = %u "
+		    " flush_dir = %u\n", __func__, vcd_status, flush_dir);
+		return false;
+	}
+}
+
+static u32 vid_dec_msg_pending(struct video_client_ctx *client_ctx)
+{
+	u32 islist_empty = 0;
+	mutex_lock(&client_ctx->msg_queue_lock);
+	islist_empty = list_empty(&client_ctx->msg_queue);
+	mutex_unlock(&client_ctx->msg_queue_lock);
+
+	if (islist_empty) {
+		DBG("%s(): vid_dec msg queue empty\n", __func__);
+		if (client_ctx->stop_msg) {
+			DBG("%s(): List empty and Stop Msg set\n",
+				__func__);
+			return client_ctx->stop_msg;
+		}
+	} else
+		DBG("%s(): vid_dec msg queue Not empty\n", __func__);
+
+	return !islist_empty;
+}
+
+static int vid_dec_get_next_msg(struct video_client_ctx *client_ctx,
+				struct vdec_msginfo *vdec_msg_info)
+{
+	int rc;
+	struct vid_dec_msg *vid_dec_msg = NULL;
+
+	if (!client_ctx)
+		return false;
+
+	rc = wait_event_interruptible(client_ctx->msg_wait,
+				      vid_dec_msg_pending(client_ctx));
+	if (rc < 0) {
+		DBG("rc = %d, stop_msg = %u\n", rc, client_ctx->stop_msg);
+		return rc;
+	} else if (client_ctx->stop_msg) {
+		DBG("rc = %d, stop_msg = %u\n", rc, client_ctx->stop_msg);
+		return -EIO;
+	}
+
+	mutex_lock(&client_ctx->msg_queue_lock);
+	if (!list_empty(&client_ctx->msg_queue)) {
+		DBG("%s(): After Wait\n", __func__);
+		vid_dec_msg = list_first_entry(&client_ctx->msg_queue,
+					       struct vid_dec_msg, list);
+		list_del(&vid_dec_msg->list);
+		memcpy(vdec_msg_info, &vid_dec_msg->vdec_msg_info,
+		       sizeof(struct vdec_msginfo));
+		kfree(vid_dec_msg);
+	}
+	mutex_unlock(&client_ctx->msg_queue_lock);
+	return 0;
+}
+
+static long vid_dec_ioctl(struct file *file,
+			 unsigned cmd, unsigned long u_arg)
+{
+	struct video_client_ctx *client_ctx = NULL;
+	struct vdec_ioctl_msg vdec_msg;
+	u32 vcd_status;
+	unsigned long kernel_vaddr, phy_addr, len;
+	unsigned long ker_vaddr;
+	struct file *pmem_file;
+	u32 result = true;
+	void __user *arg = (void __user *)u_arg;
+	int rc = 0;
+	size_t ion_len;
+
+	DBG("%s\n", __func__);
+	if (_IOC_TYPE(cmd) != VDEC_IOCTL_MAGIC)
+		return -ENOTTY;
+
+	client_ctx = (struct video_client_ctx *)file->private_data;
+	if (!client_ctx) {
+		ERR("!client_ctx. Cannot attach to device handle\n");
+		return -ENODEV;
+	}
+
+	switch (cmd) {
+	case VDEC_IOCTL_SET_CODEC:
+	{
+		enum vdec_codec vdec_codec;
+		DBG("VDEC_IOCTL_SET_CODEC\n");
+		if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
+			return -EFAULT;
+		if (copy_from_user(&vdec_codec,	vdec_msg.in,
+						   sizeof(vdec_codec)))
+			return -EFAULT;
+		DBG("setting code type = %u\n", vdec_codec);
+		result = vid_dec_set_codec(client_ctx, &vdec_codec);
+		if (!result)
+			return -EIO;
+		break;
+	}
+	case VDEC_IOCTL_SET_OUTPUT_FORMAT:
+	{
+		enum vdec_output_fromat output_format;
+		DBG("VDEC_IOCTL_SET_OUTPUT_FORMAT\n");
+		if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
+			return -EFAULT;
+		if (copy_from_user(&output_format, vdec_msg.in,
+						   sizeof(output_format)))
+			return -EFAULT;
+
+		result = vid_dec_set_output_format(client_ctx, &output_format);
+
+		if (!result)
+			return -EIO;
+		break;
+	}
+	case VDEC_IOCTL_SET_PICRES:
+	{
+		struct vdec_picsize video_resoultion;
+		DBG("VDEC_IOCTL_SET_PICRES\n");
+		if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
+			return -EFAULT;
+		if (copy_from_user(&video_resoultion, vdec_msg.in,
+						   sizeof(video_resoultion)))
+			return -EFAULT;
+		result =
+		vid_dec_set_frame_resolution(client_ctx, &video_resoultion);
+		if (!result)
+			return -EIO;
+		break;
+	}
+	case VDEC_IOCTL_GET_PICRES:
+	{
+		struct vdec_picsize video_resoultion;
+		DBG("VDEC_IOCTL_GET_PICRES\n");
+		if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
+			return -EFAULT;
+		if (copy_from_user(&video_resoultion, vdec_msg.out,
+						   sizeof(video_resoultion)))
+			return -EFAULT;
+
+		result = vid_dec_get_frame_resolution(client_ctx,
+					&video_resoultion);
+
+		if (result) {
+			if (copy_to_user(vdec_msg.out, &video_resoultion,
+					sizeof(video_resoultion)))
+				return -EFAULT;
+		} else
+			return -EIO;
+		break;
+	}
+	case VDEC_IOCTL_SET_BUFFER_REQ:
+	{
+		struct vdec_allocatorproperty vdec_buf_req;
+		struct vcd_buffer_requirement buffer_req;
+		DBG("VDEC_IOCTL_SET_BUFFER_REQ\n");
+		if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
+			return -EFAULT;
+
+		if (copy_from_user(&vdec_buf_req, vdec_msg.in,
+				   sizeof(vdec_buf_req)))
+			return -EFAULT;
+
+		buffer_req.actual_count = vdec_buf_req.actualcount;
+		buffer_req.align = vdec_buf_req.alignment;
+		buffer_req.max_count = vdec_buf_req.maxcount;
+		buffer_req.min_count = vdec_buf_req.mincount;
+		buffer_req.sz = vdec_buf_req.buffer_size;
+
+		switch (vdec_buf_req.buffer_type) {
+		case VDEC_BUFFER_TYPE_INPUT:
+			vcd_status =
+			vcd_set_buffer_requirements(client_ctx->vcd_handle,
+				VCD_BUFFER_INPUT, &buffer_req);
+			break;
+		case VDEC_BUFFER_TYPE_OUTPUT:
+			vcd_status =
+			vcd_set_buffer_requirements(client_ctx->vcd_handle,
+				VCD_BUFFER_OUTPUT, &buffer_req);
+			break;
+		default:
+			vcd_status = VCD_ERR_BAD_POINTER;
+			break;
+		}
+
+		if (vcd_status)
+			return -EFAULT;
+		break;
+	}
+	case VDEC_IOCTL_GET_BUFFER_REQ:
+	{
+		struct vdec_allocatorproperty vdec_buf_req;
+		DBG("VDEC_IOCTL_GET_BUFFER_REQ\n");
+		if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
+			return -EFAULT;
+		if (copy_from_user(&vdec_buf_req, vdec_msg.out,
+				   sizeof(vdec_buf_req)))
+			return -EFAULT;
+
+		result = vid_dec_get_buffer_req(client_ctx, &vdec_buf_req);
+
+		if (result) {
+			if (copy_to_user(vdec_msg.out, &vdec_buf_req,
+					sizeof(vdec_buf_req)))
+				return -EFAULT;
+		} else
+			return -EIO;
+		break;
+	}
+	case VDEC_IOCTL_SET_BUFFER:
+	{
+		struct vdec_setbuffer_cmd setbuffer;
+		DBG("VDEC_IOCTL_SET_BUFFER\n");
+		if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
+			return -EFAULT;
+		if (copy_from_user(&setbuffer, vdec_msg.in,
+				sizeof(setbuffer)))
+			return -EFAULT;
+		result = vid_dec_set_buffer(client_ctx, &setbuffer);
+		if (!result)
+			return -EIO;
+		break;
+	}
+	case VDEC_IOCTL_FREE_BUFFER:
+	{
+		struct vdec_setbuffer_cmd setbuffer;
+		DBG("VDEC_IOCTL_FREE_BUFFER\n");
+		if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
+			return -EFAULT;
+		if (copy_from_user(&setbuffer, vdec_msg.in,
+				sizeof(setbuffer)))
+			return -EFAULT;
+		result = vid_dec_free_buffer(client_ctx, &setbuffer);
+		if (!result)
+			return -EIO;
+		break;
+	}
+	case VDEC_IOCTL_CMD_START:
+	{
+		DBG(" VDEC_IOCTL_CMD_START\n");
+		result = vid_dec_start_stop(client_ctx, true);
+		if (!result)
+			return -EIO;
+		break;
+	}
+	case VDEC_IOCTL_CMD_STOP:
+	{
+		DBG("VDEC_IOCTL_CMD_STOP\n");
+		result = vid_dec_start_stop(client_ctx, false);
+		if (!result)
+			return -EIO;
+		break;
+	}
+	case VDEC_IOCTL_CMD_PAUSE:
+	{
+		result = vid_dec_pause_resume(client_ctx, true);
+		if (!result)
+			return -EIO;
+		break;
+	}
+	case VDEC_IOCTL_CMD_RESUME:
+	{
+		DBG("VDEC_IOCTL_CMD_PAUSE\n");
+		result = vid_dec_pause_resume(client_ctx, false);
+
+		if (!result)
+			return -EIO;
+		break;
+	}
+	case VDEC_IOCTL_DECODE_FRAME:
+	{
+		struct vdec_input_frameinfo input_frame_info;
+		u8 *desc_buf = NULL;
+		u32 desc_size = 0;
+		DBG("VDEC_IOCTL_DECODE_FRAME\n");
+		if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
+			return -EFAULT;
+		if (copy_from_user(&input_frame_info, vdec_msg.in,
+				   sizeof(input_frame_info)))
+			return -EFAULT;
+		if (client_ctx->dmx_disable) {
+			if (input_frame_info.desc_addr) {
+				desc_size = input_frame_info.desc_size;
+				desc_buf = kzalloc(desc_size, GFP_KERNEL);
+				if (desc_buf) {
+					if (copy_from_user(desc_buf,
+						input_frame_info.desc_addr,
+							desc_size)) {
+						kfree(desc_buf);
+						desc_buf = NULL;
+						return -EFAULT;
+					}
+				}
+			} else
+				return -EINVAL;
+		}
+		result = vid_dec_decode_frame(client_ctx, &input_frame_info,
+					desc_buf, desc_size);
+
+		if (!result) {
+			kfree(desc_buf);
+			desc_buf = NULL;
+			return -EIO;
+		}
+		break;
+	}
+	case VDEC_IOCTL_FILL_OUTPUT_BUFFER:
+	{
+		struct vdec_fillbuffer_cmd fill_buffer_cmd;
+		DBG("VDEC_IOCTL_FILL_OUTPUT_BUFFER\n");
+		if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
+			return -EFAULT;
+		if (copy_from_user(&fill_buffer_cmd, vdec_msg.in,
+				   sizeof(fill_buffer_cmd)))
+			return -EFAULT;
+		result = vid_dec_fill_output_buffer(client_ctx,
+							&fill_buffer_cmd);
+		if (!result)
+			return -EIO;
+		break;
+	}
+	case VDEC_IOCTL_CMD_FLUSH:
+	{
+		enum vdec_bufferflush flush_dir;
+		DBG("VDEC_IOCTL_CMD_FLUSH\n");
+		if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
+			return -EFAULT;
+		if (copy_from_user(&flush_dir, vdec_msg.in,
+				   sizeof(flush_dir)))
+			return -EFAULT;
+		result = vid_dec_flush(client_ctx, flush_dir);
+		if (!result)
+			return -EIO;
+		break;
+	}
+	case VDEC_IOCTL_GET_NEXT_MSG:
+	{
+		struct vdec_msginfo vdec_msg_info;
+		DBG("VDEC_IOCTL_GET_NEXT_MSG\n");
+		if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
+			return -EFAULT;
+		result = vid_dec_get_next_msg(client_ctx, &vdec_msg_info);
+		if (result)
+			return result;
+		if (copy_to_user(vdec_msg.out, &vdec_msg_info,
+					sizeof(vdec_msg_info)))
+			return -EFAULT;
+		break;
+	}
+	case VDEC_IOCTL_STOP_NEXT_MSG:
+	{
+		DBG("VDEC_IOCTL_STOP_NEXT_MSG\n");
+		client_ctx->stop_msg = 1;
+		wake_up(&client_ctx->msg_wait);
+		break;
+	}
+	case VDEC_IOCTL_SET_SEQUENCE_HEADER:
+	{
+		struct vdec_seqheader seq_header;
+		struct vcd_sequence_hdr vcd_seq_hdr;
+		unsigned long ionflag;
+		DBG("VDEC_IOCTL_SET_SEQUENCE_HEADER\n");
+		if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg))) {
+			ERR("Copy from user vdec_msg failed\n");
+			return -EFAULT;
+		}
+		if (copy_from_user(&seq_header,	vdec_msg.in,
+				   sizeof(seq_header))) {
+			ERR("Copy from user seq_header failed\n");
+			return -EFAULT;
+		}
+		if (!seq_header.seq_header_len) {
+			ERR("Seq Len is Zero\n");
+			return -EFAULT;
+		}
+
+		if (!vcd_get_ion_status()) {
+			if (get_pmem_file(seq_header.pmem_fd,
+				  &phy_addr, &kernel_vaddr, &len, &pmem_file)) {
+				ERR("%s(): get_pmem_file failed\n", __func__);
+				return false;
+			}
+			put_pmem_file(pmem_file);
+		} else {
+			client_ctx->seq_hdr_ion_handle = ion_import_fd(
+				client_ctx->user_ion_client,
+				seq_header.pmem_fd);
+			if (!client_ctx->seq_hdr_ion_handle) {
+				ERR("%s(): get_ION_handle failed\n", __func__);
+				return false;
+			}
+			rc = ion_handle_get_flags(client_ctx->user_ion_client,
+						client_ctx->seq_hdr_ion_handle,
+						&ionflag);
+			if (rc) {
+				ERR("%s():get_ION_flags fail\n",
+							 __func__);
+				ion_free(client_ctx->user_ion_client,
+					client_ctx->seq_hdr_ion_handle);
+				return false;
+			}
+			ker_vaddr = (unsigned long) ion_map_kernel(
+				client_ctx->user_ion_client,
+				client_ctx->seq_hdr_ion_handle, ionflag);
+			if (!ker_vaddr) {
+				ERR("%s():get_ION_kernel virtual addr fail\n",
+							 __func__);
+				ion_free(client_ctx->user_ion_client,
+					client_ctx->seq_hdr_ion_handle);
+				return false;
+			}
+			kernel_vaddr = ker_vaddr;
+			rc = ion_phys(client_ctx->user_ion_client,
+					client_ctx->seq_hdr_ion_handle,
+					&phy_addr, &ion_len);
+			if (rc) {
+				ERR("%s():get_ION_kernel physical addr fail\n",
+						 __func__);
+				ion_unmap_kernel(client_ctx->user_ion_client,
+					client_ctx->seq_hdr_ion_handle);
+				ion_free(client_ctx->user_ion_client,
+					client_ctx->seq_hdr_ion_handle);
+				return false;
+			}
+			len = ion_len;
+		}
+		vcd_seq_hdr.sequence_header_len = seq_header.seq_header_len;
+		kernel_vaddr += (unsigned long)seq_header.pmem_offset;
+		vcd_seq_hdr.sequence_header = (u8 *)kernel_vaddr;
+		if (!vcd_seq_hdr.sequence_header) {
+			ERR("Sequence Header pointer failed\n");
+			return -EFAULT;
+		}
+		client_ctx->seq_header_set = true;
+		if (vcd_decode_start(client_ctx->vcd_handle, &vcd_seq_hdr)) {
+			ERR("Decode start Failed\n");
+			client_ctx->seq_header_set = false;
+			return -EFAULT;
+		}
+		DBG("Wait Client completion Sequence Header\n");
+		wait_for_completion(&client_ctx->event);
+		vcd_seq_hdr.sequence_header = NULL;
+		if (client_ctx->event_status) {
+			ERR("Set Seq Header status is failed");
+			return -EFAULT;
+		}
+		if (vcd_get_ion_status()) {
+			if (client_ctx->seq_hdr_ion_handle) {
+				ion_unmap_kernel(client_ctx->user_ion_client,
+						client_ctx->seq_hdr_ion_handle);
+				ion_free(client_ctx->user_ion_client,
+					client_ctx->seq_hdr_ion_handle);
+			}
+		}
+		break;
+	}
+	case VDEC_IOCTL_GET_NUMBER_INSTANCES:
+	{
+		DBG("VDEC_IOCTL_GET_NUMBER_INSTANCES\n");
+		if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
+			return -EFAULT;
+		if (copy_to_user(vdec_msg.out,
+			&vid_dec_device_p->num_clients, sizeof(u32)))
+			return -EFAULT;
+		break;
+	}
+	case VDEC_IOCTL_GET_INTERLACE_FORMAT:
+	{
+		u32 progressive_only, interlace_format;
+		DBG("VDEC_IOCTL_GET_INTERLACE_FORMAT\n");
+		if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
+			return -EFAULT;
+		result = vid_dec_get_progressive_only(client_ctx,
+					&progressive_only);
+		if (result) {
+			interlace_format = progressive_only ?
+				VDEC_InterlaceFrameProgressive :
+				VDEC_InterlaceInterleaveFrameTopFieldFirst;
+			if (copy_to_user(vdec_msg.out, &interlace_format,
+					sizeof(u32)))
+				return -EFAULT;
+		} else
+			return -EIO;
+		break;
+	}
+
+	case VDEC_IOCTL_GET_DISABLE_DMX_SUPPORT:
+	{
+		u32 disable_dmx;
+		DBG("VDEC_IOCTL_GET_DISABLE_DMX_SUPPORT\n");
+		if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
+			return -EFAULT;
+		result = vid_dec_get_disable_dmx_support(client_ctx,
+					&disable_dmx);
+		if (result) {
+			if (copy_to_user(vdec_msg.out, &disable_dmx,
+					sizeof(u32)))
+				return -EFAULT;
+		} else
+			return -EIO;
+		break;
+	}
+	case VDEC_IOCTL_GET_DISABLE_DMX:
+	{
+		u32 disable_dmx;
+		DBG("VDEC_IOCTL_GET_DISABLE_DMX\n");
+		if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
+			return -EFAULT;
+		result = vid_dec_get_disable_dmx(client_ctx,
+					&disable_dmx);
+		if (result) {
+			if (copy_to_user(vdec_msg.out, &disable_dmx,
+					sizeof(u32)))
+				return -EFAULT;
+		} else
+			return -EIO;
+		break;
+	}
+	case VDEC_IOCTL_SET_DISABLE_DMX:
+	{
+		DBG("VDEC_IOCTL_SET_DISABLE_DMX\n");
+		result =  vid_dec_set_disable_dmx(client_ctx);
+		if (!result)
+			return -EIO;
+		client_ctx->dmx_disable = 1;
+		break;
+	}
+	case VDEC_IOCTL_SET_PICTURE_ORDER:
+	{
+		u32 picture_order;
+		DBG("VDEC_IOCTL_SET_PICTURE_ORDER\n");
+		if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
+			return -EFAULT;
+		if (copy_from_user(&picture_order, vdec_msg.in,
+						   sizeof(u32)))
+			return -EFAULT;
+		result =  vid_dec_set_picture_order(client_ctx, &picture_order);
+		if (!result)
+			return -EIO;
+		break;
+	}
+	case VDEC_IOCTL_SET_FRAME_RATE:
+	{
+		struct vdec_framerate frame_rate;
+		DBG("VDEC_IOCTL_SET_FRAME_RATE\n");
+		if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
+			return -EFAULT;
+		if (copy_from_user(&frame_rate, vdec_msg.in,
+						   sizeof(frame_rate)))
+			return -EFAULT;
+		result = vid_dec_set_frame_rate(client_ctx, &frame_rate);
+		if (!result)
+			return -EIO;
+		break;
+	}
+	case VDEC_IOCTL_SET_EXTRADATA:
+	{
+		u32 extradata_flag;
+		DBG("VDEC_IOCTL_SET_EXTRADATA\n");
+		if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
+			return -EFAULT;
+		if (copy_from_user(&extradata_flag, vdec_msg.in,
+						   sizeof(u32)))
+			return -EFAULT;
+		result = vid_dec_set_extradata(client_ctx, &extradata_flag);
+		if (!result)
+			return -EIO;
+		break;
+	}
+	case VDEC_IOCTL_SET_H264_MV_BUFFER:
+	{
+		struct vdec_h264_mv mv_data;
+		DBG("VDEC_IOCTL_SET_H264_MV_BUFFER\n");
+		if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
+			return -EFAULT;
+		if (copy_from_user(&mv_data, vdec_msg.in,
+						   sizeof(mv_data)))
+			return -EFAULT;
+		result = vid_dec_set_h264_mv_buffers(client_ctx, &mv_data);
+
+		if (!result)
+			return -EIO;
+		break;
+	}
+	case VDEC_IOCTL_FREE_H264_MV_BUFFER:
+	{
+		DBG("VDEC_IOCTL_FREE_H264_MV_BUFFER\n");
+		result = vid_dec_free_h264_mv_buffers(client_ctx);
+		if (!result)
+			return -EIO;
+		break;
+	}
+	case VDEC_IOCTL_GET_MV_BUFFER_SIZE:
+	{
+		struct vdec_mv_buff_size mv_buff;
+		DBG("VDEC_IOCTL_GET_MV_BUFFER_SIZE\n");
+		if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
+			return -EFAULT;
+		if (copy_from_user(&mv_buff, vdec_msg.out,
+						   sizeof(mv_buff)))
+			return -EFAULT;
+		result = vid_dec_get_h264_mv_buffer_size(client_ctx, &mv_buff);
+		if (result) {
+			DBG(" Returning W: %d, H: %d, S: %d, A: %d",
+				mv_buff.width, mv_buff.height,
+				mv_buff.size, mv_buff.alignment);
+			if (copy_to_user(vdec_msg.out, &mv_buff,
+					sizeof(mv_buff)))
+				return -EFAULT;
+		} else
+			return -EIO;
+		break;
+	}
+	case VDEC_IOCTL_SET_IDR_ONLY_DECODING:
+	{
+		result = vid_dec_set_idr_only_decoding(client_ctx);
+		if (!result)
+			return -EIO;
+		break;
+	}
+	case VDEC_IOCTL_SET_CONT_ON_RECONFIG:
+	{
+		result = vid_dec_set_cont_on_reconfig(client_ctx);
+		if (!result)
+			return -EIO;
+		break;
+	}
+	default:
+		ERR("%s(): Unsupported ioctl\n", __func__);
+		return -ENOTTY;
+		break;
+	}
+
+	return 0;
+}
+
+static u32 vid_dec_close_client(struct video_client_ctx *client_ctx)
+{
+	struct vid_dec_msg *vdec_msg;
+	u32 vcd_status;
+
+	DBG("msm_vidc_dec: Inside %s()", __func__);
+	if (!client_ctx || (!client_ctx->vcd_handle)) {
+		ERR("\n Invalid client_ctx");
+		return false;
+	}
+
+	mutex_lock(&vid_dec_device_p->lock);
+	if (!client_ctx->stop_called) {
+		client_ctx->stop_called = true;
+		client_ctx->stop_sync_cb = true;
+		vcd_status = vcd_stop(client_ctx->vcd_handle);
+		DBG("\n Stuck at the stop call");
+		if (!vcd_status)
+			wait_for_completion(&client_ctx->event);
+		DBG("\n Came out of wait event");
+	}
+	mutex_lock(&client_ctx->msg_queue_lock);
+	while (!list_empty(&client_ctx->msg_queue)) {
+		DBG("%s(): Delete remaining entries\n", __func__);
+		vdec_msg = list_first_entry(&client_ctx->msg_queue,
+						   struct vid_dec_msg, list);
+		if (vdec_msg) {
+			list_del(&vdec_msg->list);
+			kfree(vdec_msg);
+		}
+	}
+	mutex_unlock(&client_ctx->msg_queue_lock);
+	vcd_status = vcd_close(client_ctx->vcd_handle);
+
+	if (vcd_status) {
+		mutex_unlock(&vid_dec_device_p->lock);
+		return false;
+	}
+	client_ctx->user_ion_client = NULL;
+	memset((void *)client_ctx, 0, sizeof(struct video_client_ctx));
+	vid_dec_device_p->num_clients--;
+	mutex_unlock(&vid_dec_device_p->lock);
+	return true;
+}
+
+int vid_dec_open_client(struct video_client_ctx **vid_clnt_ctx, int flags)
+{
+	int rc = 0;
+	s32 client_index;
+	struct video_client_ctx *client_ctx = NULL;
+	u8 client_count;
+
+	if (!vid_clnt_ctx) {
+		ERR("Invalid input\n");
+		return -EINVAL;
+	}
+	*vid_clnt_ctx = NULL;
+	client_count = vcd_get_num_of_clients();
+	if (client_count == VIDC_MAX_NUM_CLIENTS) {
+		ERR("ERROR : vid_dec_open() max number of clients"
+			"limit reached\n");
+		rc = -ENOMEM;
+		goto client_failure;
+	}
+
+	DBG(" Virtual Address of ioremap is %p\n", vid_dec_device_p->virt_base);
+	if (!vid_dec_device_p->num_clients) {
+		if (!vidc_load_firmware()) {
+			rc = -ENOMEM;
+			goto client_failure;
+		}
+	}
+
+	client_index = vid_dec_get_empty_client_index();
+	if (client_index == -1) {
+		ERR("%s() : No free clients client_index == -1\n", __func__);
+		rc = -ENOMEM;
+		goto client_failure;
+	}
+	client_ctx = &vid_dec_device_p->vdec_clients[client_index];
+	vid_dec_device_p->num_clients++;
+	init_completion(&client_ctx->event);
+	mutex_init(&client_ctx->msg_queue_lock);
+	mutex_init(&client_ctx->enrty_queue_lock);
+	INIT_LIST_HEAD(&client_ctx->msg_queue);
+	init_waitqueue_head(&client_ctx->msg_wait);
+	client_ctx->stop_msg = 0;
+	client_ctx->stop_called = false;
+	client_ctx->stop_sync_cb = false;
+	client_ctx->dmx_disable = 0;
+	if (vcd_get_ion_status()) {
+		client_ctx->user_ion_client = vcd_get_ion_client();
+		if (!client_ctx->user_ion_client) {
+			ERR("vcd_open ion client get failed");
+			rc = -ENOMEM;
+			goto client_failure;
+		}
+	}
+	rc = vcd_open(vid_dec_device_p->device_handle, true,
+				  vid_dec_vcd_cb, client_ctx, flags);
+	if (!rc) {
+		wait_for_completion(&client_ctx->event);
+		if (client_ctx->event_status) {
+			ERR("callback for vcd_open returned error: %u",
+				client_ctx->event_status);
+			rc = -ENODEV;
+			goto client_failure;
+		}
+	} else {
+		ERR("vcd_open returned error: %u", rc);
+		goto client_failure;
+	}
+	client_ctx->seq_header_set = false;
+	*vid_clnt_ctx = client_ctx;
+client_failure:
+	return rc;
+}
+
+static int vid_dec_open_secure(struct inode *inode, struct file *file)
+{
+	int rc = 0;
+	struct video_client_ctx *client_ctx;
+	mutex_lock(&vid_dec_device_p->lock);
+	rc = vid_dec_open_client(&client_ctx, VCD_CP_SESSION);
+	if (rc)
+		goto error;
+	if (!client_ctx) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	file->private_data = client_ctx;
+	if (res_trk_open_secure_session()) {
+		ERR("Secure session operation failure\n");
+		rc = -EACCES;
+		goto error;
+	}
+	mutex_unlock(&vid_dec_device_p->lock);
+	return 0;
+error:
+	mutex_unlock(&vid_dec_device_p->lock);
+	return rc;
+}
+
+static int vid_dec_open(struct inode *inode, struct file *file)
+{
+	int rc = 0;
+	struct video_client_ctx *client_ctx;
+	INFO("msm_vidc_dec: Inside %s()", __func__);
+	mutex_lock(&vid_dec_device_p->lock);
+	rc = vid_dec_open_client(&client_ctx, 0);
+	if (rc) {
+		mutex_unlock(&vid_dec_device_p->lock);
+		return rc;
+	}
+	if (!client_ctx) {
+		mutex_unlock(&vid_dec_device_p->lock);
+		return -ENOMEM;
+	}
+
+	file->private_data = client_ctx;
+	mutex_unlock(&vid_dec_device_p->lock);
+	return rc;
+}
+
+static int vid_dec_release_secure(struct inode *inode, struct file *file)
+{
+	struct video_client_ctx *client_ctx = file->private_data;
+
+	INFO("msm_vidc_dec: Inside %s()", __func__);
+	vidc_cleanup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT);
+	vidc_cleanup_addr_table(client_ctx, BUFFER_TYPE_INPUT);
+	vid_dec_close_client(client_ctx);
+	vidc_release_firmware();
+#ifndef USE_RES_TRACKER
+	vidc_disable_clk();
+#endif
+	INFO("msm_vidc_dec: Return from %s()", __func__);
+	return 0;
+}
+
+static int vid_dec_release(struct inode *inode, struct file *file)
+{
+	struct video_client_ctx *client_ctx = file->private_data;
+
+	INFO("msm_vidc_dec: Inside %s()", __func__);
+	vidc_cleanup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT);
+	vidc_cleanup_addr_table(client_ctx, BUFFER_TYPE_INPUT);
+	vid_dec_close_client(client_ctx);
+	vidc_release_firmware();
+#ifndef USE_RES_TRACKER
+	vidc_disable_clk();
+#endif
+	INFO("msm_vidc_dec: Return from %s()", __func__);
+	return 0;
+}
+
+static const struct file_operations vid_dec_fops[2] = {
+	{
+		.owner = THIS_MODULE,
+		.open = vid_dec_open,
+		.release = vid_dec_release,
+		.unlocked_ioctl = vid_dec_ioctl,
+	},
+	{
+		.owner = THIS_MODULE,
+		.open = vid_dec_open_secure,
+		.release = vid_dec_release_secure,
+		.unlocked_ioctl = vid_dec_ioctl,
+	},
+
+};
+
+void vid_dec_interrupt_deregister(void)
+{
+}
+
+void vid_dec_interrupt_register(void *device_name)
+{
+}
+
+void vid_dec_interrupt_clear(void)
+{
+}
+
+void *vid_dec_map_dev_base_addr(void *device_name)
+{
+	return vid_dec_device_p->virt_base;
+}
+
+static int vid_dec_vcd_init(void)
+{
+	int rc;
+	struct vcd_init_config vcd_init_config;
+	u32 i;
+
+	/* init_timer(&hw_timer); */
+	DBG("msm_vidc_dec: Inside %s()", __func__);
+	vid_dec_device_p->num_clients = 0;
+
+	for (i = 0; i < VIDC_MAX_NUM_CLIENTS; i++) {
+		memset((void *)&vid_dec_device_p->vdec_clients[i], 0,
+		       sizeof(vid_dec_device_p->vdec_clients[i]));
+	}
+
+	mutex_init(&vid_dec_device_p->lock);
+	vid_dec_device_p->virt_base = vidc_get_ioaddr();
+	DBG("%s() : base address for VIDC core %u\n", __func__, \
+		(int)vid_dec_device_p->virt_base);
+
+	if (!vid_dec_device_p->virt_base) {
+		ERR("%s() : ioremap failed\n", __func__);
+		return -ENOMEM;
+	}
+
+	vcd_init_config.device_name = "VIDC";
+	vcd_init_config.map_dev_base_addr = vid_dec_map_dev_base_addr;
+	vcd_init_config.interrupt_clr = vid_dec_interrupt_clear;
+	vcd_init_config.register_isr = vid_dec_interrupt_register;
+	vcd_init_config.deregister_isr = vid_dec_interrupt_deregister;
+	vcd_init_config.timer_create = vidc_timer_create;
+	vcd_init_config.timer_release = vidc_timer_release;
+	vcd_init_config.timer_start = vidc_timer_start;
+	vcd_init_config.timer_stop = vidc_timer_stop;
+
+	rc = vcd_init(&vcd_init_config, &vid_dec_device_p->device_handle);
+
+	if (rc) {
+		ERR("%s() : vcd_init failed\n", __func__);
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static int __init vid_dec_init(void)
+{
+	int rc = 0, i = 0, j = 0;
+	struct device *class_devp;
+
+	DBG("msm_vidc_dec: Inside %s()", __func__);
+	vid_dec_device_p = kzalloc(sizeof(struct vid_dec_dev), GFP_KERNEL);
+	if (!vid_dec_device_p) {
+		ERR("%s Unable to allocate memory for vid_dec_dev\n",
+		       __func__);
+		return -ENOMEM;
+	}
+
+	rc = alloc_chrdev_region(&vid_dec_dev_num, 0, NUM_OF_DRIVER_NODES,
+		VID_DEC_NAME);
+	if (rc < 0) {
+		ERR("%s: alloc_chrdev_region Failed rc = %d\n",
+		       __func__, rc);
+		goto error_vid_dec_alloc_chrdev_region;
+	}
+
+	vid_dec_class = class_create(THIS_MODULE, VID_DEC_NAME);
+	if (IS_ERR(vid_dec_class)) {
+		rc = PTR_ERR(vid_dec_class);
+		ERR("%s: couldn't create vid_dec_class rc = %d\n",
+		       __func__, rc);
+
+		goto error_vid_dec_class_create;
+	}
+	for (i = 0; i < NUM_OF_DRIVER_NODES; i++) {
+		class_devp = device_create(vid_dec_class, NULL,
+						(vid_dec_dev_num + i),
+						NULL, VID_DEC_NAME "%s",
+						node_name[i]);
+
+		if (IS_ERR(class_devp)) {
+			rc = PTR_ERR(class_devp);
+			ERR("%s: class device_create failed %d\n",
+				   __func__, rc);
+			if (!i)
+				goto error_vid_dec_class_device_create;
+			else
+				goto error_vid_dec_cdev_add;
+		}
+
+	  vid_dec_device_p->device[i] = class_devp;
+
+		cdev_init(&vid_dec_device_p->cdev[i], &vid_dec_fops[i]);
+		vid_dec_device_p->cdev[i].owner = THIS_MODULE;
+		rc = cdev_add(&(vid_dec_device_p->cdev[i]),
+				(vid_dec_dev_num+i), 1);
+
+		if (rc < 0) {
+			ERR("%s: cdev_add failed %d\n", __func__, rc);
+			goto error_vid_dec_cdev_add;
+		}
+	}
+	vid_dec_vcd_init();
+	return 0;
+
+error_vid_dec_cdev_add:
+	for (j = i-1; j >= 0; j--)
+		cdev_del(&(vid_dec_device_p->cdev[j]));
+	device_destroy(vid_dec_class, vid_dec_dev_num);
+error_vid_dec_class_device_create:
+	class_destroy(vid_dec_class);
+error_vid_dec_class_create:
+	unregister_chrdev_region(vid_dec_dev_num, NUM_OF_DRIVER_NODES);
+error_vid_dec_alloc_chrdev_region:
+	kfree(vid_dec_device_p);
+	return rc;
+}
+
+static void __exit vid_dec_exit(void)
+{
+	int i = 0;
+	INFO("msm_vidc_dec: Inside %s()", __func__);
+	for (i = 0; i < NUM_OF_DRIVER_NODES; i++)
+		cdev_del(&(vid_dec_device_p->cdev[i]));
+	device_destroy(vid_dec_class, vid_dec_dev_num);
+	class_destroy(vid_dec_class);
+	unregister_chrdev_region(vid_dec_dev_num, NUM_OF_DRIVER_NODES);
+	kfree(vid_dec_device_p);
+	DBG("msm_vidc_dec: Return from %s()", __func__);
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Video decoder driver");
+MODULE_VERSION("1.0");
+
+module_init(vid_dec_init);
+module_exit(vid_dec_exit);
diff --git a/drivers/video/msm/vidc/common/dec/vdec_internal.h b/drivers/video/msm/vidc/common/dec/vdec_internal.h
new file mode 100644
index 0000000..89da9a2
--- /dev/null
+++ b/drivers/video/msm/vidc/common/dec/vdec_internal.h
@@ -0,0 +1,45 @@
+/* Copyright (c) 2010, 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef VDEC_INTERNAL_H
+#define VDEC_INTERNAL_H
+
+#include <linux/msm_vidc_dec.h>
+#include <linux/cdev.h>
+#include <media/msm/vidc_init.h>
+
+#define NUM_OF_DRIVER_NODES 2
+
+struct vid_dec_msg {
+	struct list_head list;
+	struct vdec_msginfo vdec_msg_info;
+};
+
+struct vid_dec_dev {
+	struct cdev cdev[NUM_OF_DRIVER_NODES];
+	struct device *device[NUM_OF_DRIVER_NODES];
+	resource_size_t phys_base;
+	void __iomem *virt_base;
+	unsigned int irq;
+	struct clk *hclk;
+	struct clk *hclk_div2;
+	struct clk *pclk;
+	unsigned long hclk_rate;
+	struct mutex lock;
+	s32 device_handle;
+	struct video_client_ctx vdec_clients[VIDC_MAX_NUM_CLIENTS];
+	u32 num_clients;
+	void(*timer_handler)(void *);
+};
+
+#endif
diff --git a/drivers/video/msm/vidc/common/enc/venc.c b/drivers/video/msm/vidc/common/enc/venc.c
new file mode 100644
index 0000000..1b77b67
--- /dev/null
+++ b/drivers/video/msm/vidc/common/enc/venc.c
@@ -0,0 +1,1648 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <linux/android_pmem.h>
+#include <linux/clk.h>
+#include <media/msm/vidc_type.h>
+#include <media/msm/vcd_api.h>
+#include <media/msm/vidc_init.h>
+
+#include "venc_internal.h"
+#include "vcd_res_tracker_api.h"
+
+#define VID_ENC_NAME	"msm_vidc_enc"
+
+#if DEBUG
+#define DBG(x...) printk(KERN_DEBUG x)
+#else
+#define DBG(x...)
+#endif
+
+#define INFO(x...) printk(KERN_INFO x)
+#define ERR(x...) printk(KERN_ERR x)
+
+static struct vid_enc_dev *vid_enc_device_p;
+static dev_t vid_enc_dev_num;
+static struct class *vid_enc_class;
+static long vid_enc_ioctl(struct file *file,
+	unsigned cmd, unsigned long arg);
+static int stop_cmd;
+
+static s32 vid_enc_get_empty_client_index(void)
+{
+	u32 i;
+	u32 found = false;
+
+	for (i = 0; i < VIDC_MAX_NUM_CLIENTS; i++) {
+		if (!vid_enc_device_p->venc_clients[i].vcd_handle) {
+			found = true;
+			break;
+		}
+	}
+	if (!found) {
+		ERR("%s():ERROR No space for new client\n",
+			__func__);
+		return -ENOMEM;
+	} else {
+		DBG("%s(): available client index = %u\n",
+			__func__, i);
+		return i;
+	}
+}
+
+
+u32 vid_enc_get_status(u32 status)
+{
+	u32 venc_status;
+
+	switch (status) {
+	case VCD_S_SUCCESS:
+		venc_status = VEN_S_SUCCESS;
+		break;
+	case VCD_ERR_FAIL:
+		venc_status = VEN_S_EFAIL;
+		break;
+	case VCD_ERR_ALLOC_FAIL:
+		venc_status = VEN_S_ENOSWRES;
+		break;
+	case VCD_ERR_ILLEGAL_OP:
+		venc_status = VEN_S_EINVALCMD;
+		break;
+	case VCD_ERR_ILLEGAL_PARM:
+		venc_status = VEN_S_EBADPARAM;
+		break;
+	case VCD_ERR_BAD_POINTER:
+	case VCD_ERR_BAD_HANDLE:
+		venc_status = VEN_S_EFATAL;
+		break;
+	case VCD_ERR_NOT_SUPPORTED:
+		venc_status = VEN_S_ENOTSUPP;
+		break;
+	case VCD_ERR_BAD_STATE:
+		venc_status = VEN_S_EINVALSTATE;
+		break;
+	case VCD_ERR_MAX_CLIENT:
+		venc_status = VEN_S_ENOHWRES;
+		break;
+	default:
+		venc_status = VEN_S_EFAIL;
+		break;
+	}
+	return venc_status;
+}
+
+static void vid_enc_notify_client(struct video_client_ctx *client_ctx)
+{
+	if (client_ctx)
+		complete(&client_ctx->event);
+}
+
+void vid_enc_vcd_open_done(struct video_client_ctx *client_ctx,
+	struct vcd_handle_container *handle_container)
+{
+	DBG("vid_enc_vcd_open_done\n");
+
+	if (client_ctx) {
+		if (handle_container)
+			client_ctx->vcd_handle = handle_container->handle;
+		else
+		ERR("%s(): ERROR. handle_container is NULL\n",
+		__func__);
+		vid_enc_notify_client(client_ctx);
+	} else
+		ERR("%s(): ERROR. client_ctx is NULL\n",
+			__func__);
+}
+
+static void vid_enc_input_frame_done(struct video_client_ctx *client_ctx,
+		u32 event, u32 status,
+		struct vcd_frame_data *vcd_frame_data)
+{
+	struct vid_enc_msg *venc_msg;
+
+	if (!client_ctx || !vcd_frame_data) {
+		ERR("vid_enc_input_frame_done() NULL pointer\n");
+		return;
+	}
+
+	venc_msg = kzalloc(sizeof(struct vid_enc_msg),
+					    GFP_KERNEL);
+	if (!venc_msg) {
+		ERR("vid_enc_input_frame_done(): cannot allocate vid_enc_msg "
+		" buffer\n");
+		return;
+	}
+
+	venc_msg->venc_msg_info.statuscode = vid_enc_get_status(status);
+
+	venc_msg->venc_msg_info.msgcode = VEN_MSG_INPUT_BUFFER_DONE;
+
+	switch (event) {
+	case VCD_EVT_RESP_INPUT_DONE:
+	   DBG("Send INPUT_DON message to client = %p\n",
+			client_ctx);
+	   break;
+	case VCD_EVT_RESP_INPUT_FLUSHED:
+		DBG("Send INPUT_FLUSHED message to client = %p\n",
+			client_ctx);
+	   break;
+	default:
+		ERR("vid_enc_input_frame_done(): invalid event type: "
+			"%d\n", event);
+		venc_msg->venc_msg_info.statuscode = VEN_S_EFATAL;
+	   break;
+	}
+
+	venc_msg->venc_msg_info.buf.clientdata =
+		(void *)vcd_frame_data->frm_clnt_data;
+	venc_msg->venc_msg_info.msgdata_size =
+		sizeof(struct vid_enc_msg);
+
+	mutex_lock(&client_ctx->msg_queue_lock);
+	list_add_tail(&venc_msg->list, &client_ctx->msg_queue);
+	mutex_unlock(&client_ctx->msg_queue_lock);
+	wake_up(&client_ctx->msg_wait);
+}
+
+static void vid_enc_output_frame_done(struct video_client_ctx *client_ctx,
+		u32 event, u32 status,
+		struct vcd_frame_data *vcd_frame_data)
+{
+	struct vid_enc_msg *venc_msg;
+	unsigned long kernel_vaddr, phy_addr, user_vaddr;
+	int pmem_fd;
+	struct file *file;
+	s32 buffer_index = -1;
+	u32 ion_flag = 0;
+	struct ion_handle *buff_handle = NULL;
+
+	if (!client_ctx || !vcd_frame_data) {
+		ERR("vid_enc_input_frame_done() NULL pointer\n");
+		return;
+	}
+
+	venc_msg = kzalloc(sizeof(struct vid_enc_msg),
+					   GFP_KERNEL);
+	if (!venc_msg) {
+		ERR("vid_enc_input_frame_done(): cannot allocate vid_enc_msg "
+		" buffer\n");
+		return;
+	}
+
+	venc_msg->venc_msg_info.statuscode = vid_enc_get_status(status);
+	venc_msg->venc_msg_info.msgcode = VEN_MSG_OUTPUT_BUFFER_DONE;
+
+	switch (event) {
+	case VCD_EVT_RESP_OUTPUT_DONE:
+	   DBG("Send INPUT_DON message to client = %p\n",
+			client_ctx);
+	   break;
+	case VCD_EVT_RESP_OUTPUT_FLUSHED:
+	   DBG("Send INPUT_FLUSHED message to client = %p\n",
+		   client_ctx);
+	   break;
+	default:
+	   ERR("QVD: vid_enc_output_frame_done invalid cmd type: %d\n", event);
+	   venc_msg->venc_msg_info.statuscode = VEN_S_EFATAL;
+	   break;
+	}
+
+	kernel_vaddr =
+		(unsigned long)vcd_frame_data->virtual;
+
+	if (vidc_lookup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT,
+		false, &user_vaddr, &kernel_vaddr,
+		&phy_addr, &pmem_fd, &file,
+		&buffer_index)) {
+
+		/* Buffer address in user space */
+		venc_msg->venc_msg_info.buf.ptrbuffer =	(u8 *) user_vaddr;
+		/* Buffer address in user space */
+		venc_msg->venc_msg_info.buf.clientdata = (void *)
+		vcd_frame_data->frm_clnt_data;
+		/* Data length */
+		venc_msg->venc_msg_info.buf.len =
+			vcd_frame_data->data_len;
+		venc_msg->venc_msg_info.buf.flags =
+			vcd_frame_data->flags;
+		/* Timestamp pass-through from input frame */
+		venc_msg->venc_msg_info.buf.timestamp =
+			vcd_frame_data->time_stamp;
+
+		/* Decoded picture width and height */
+		venc_msg->venc_msg_info.msgdata_size =
+			sizeof(struct venc_buffer);
+	} else {
+		ERR("vid_enc_output_frame_done UVA can not be found\n");
+		venc_msg->venc_msg_info.statuscode =
+			VEN_S_EFATAL;
+	}
+	if (venc_msg->venc_msg_info.buf.len > 0) {
+		ion_flag = vidc_get_fd_info(client_ctx, BUFFER_TYPE_OUTPUT,
+					pmem_fd, kernel_vaddr, buffer_index,
+					&buff_handle);
+		if (ion_flag == CACHED && buff_handle) {
+			msm_ion_do_cache_op(client_ctx->user_ion_client,
+				buff_handle,
+				(unsigned long *) kernel_vaddr,
+				(unsigned long)venc_msg->venc_msg_info.buf.len,
+				ION_IOC_CLEAN_INV_CACHES);
+		}
+	}
+	mutex_lock(&client_ctx->msg_queue_lock);
+	list_add_tail(&venc_msg->list, &client_ctx->msg_queue);
+	mutex_unlock(&client_ctx->msg_queue_lock);
+	wake_up(&client_ctx->msg_wait);
+}
+
+static void vid_enc_lean_event(struct video_client_ctx *client_ctx,
+	u32 event, u32 status)
+{
+	struct vid_enc_msg *venc_msg;
+	if (!client_ctx) {
+		ERR("%s(): !client_ctx pointer\n",
+			__func__);
+		return;
+	}
+
+	venc_msg = kzalloc(sizeof(struct vid_enc_msg),
+			GFP_KERNEL);
+	if (!venc_msg) {
+		ERR("%s(): cannot allocate vid_enc_msg buffer\n",
+			__func__);
+		return;
+	}
+
+	venc_msg->venc_msg_info.statuscode =
+		vid_enc_get_status(status);
+
+	switch (event) {
+	case VCD_EVT_RESP_FLUSH_INPUT_DONE:
+		INFO("\n msm_vidc_enc: Sending VCD_EVT_RESP_FLUSH_INPUT_DONE"
+			 " to client");
+		venc_msg->venc_msg_info.msgcode =
+			VEN_MSG_FLUSH_INPUT_DONE;
+		break;
+	case VCD_EVT_RESP_FLUSH_OUTPUT_DONE:
+		INFO("\n msm_vidc_enc: Sending VCD_EVT_RESP_FLUSH_OUTPUT_DONE"
+			 " to client");
+		venc_msg->venc_msg_info.msgcode =
+			VEN_MSG_FLUSH_OUPUT_DONE;
+		break;
+
+	case VCD_EVT_RESP_START:
+		INFO("\n msm_vidc_enc: Sending VCD_EVT_RESP_START"
+			 " to client");
+		venc_msg->venc_msg_info.msgcode =
+			VEN_MSG_START;
+		break;
+
+	case VCD_EVT_RESP_STOP:
+		INFO("\n msm_vidc_enc: Sending VCD_EVT_RESP_STOP"
+			 " to client");
+		venc_msg->venc_msg_info.msgcode =
+			VEN_MSG_STOP;
+		break;
+
+	case VCD_EVT_RESP_PAUSE:
+		INFO("\n msm_vidc_enc: Sending VCD_EVT_RESP_PAUSE"
+			 " to client");
+		venc_msg->venc_msg_info.msgcode =
+			VEN_MSG_PAUSE;
+		break;
+
+	default:
+		ERR("%s() : unknown event type %u\n",
+			__func__, event);
+		break;
+	}
+
+	venc_msg->venc_msg_info.msgdata_size = 0;
+
+	mutex_lock(&client_ctx->msg_queue_lock);
+	list_add_tail(&venc_msg->list, &client_ctx->msg_queue);
+	mutex_unlock(&client_ctx->msg_queue_lock);
+	wake_up(&client_ctx->msg_wait);
+}
+
+
+void vid_enc_vcd_cb(u32 event, u32 status,
+	void *info, size_t sz, void *handle,
+	void *const client_data)
+{
+	struct video_client_ctx *client_ctx =
+		(struct video_client_ctx *)client_data;
+
+	DBG("Entering %s()\n", __func__);
+
+	if (!client_ctx) {
+		ERR("%s(): client_ctx is NULL\n", __func__);
+		return;
+	}
+
+	client_ctx->event_status = status;
+
+	switch (event) {
+	case VCD_EVT_RESP_OPEN:
+		vid_enc_vcd_open_done(client_ctx,
+		(struct vcd_handle_container *)info);
+		break;
+
+	case VCD_EVT_RESP_INPUT_DONE:
+	case VCD_EVT_RESP_INPUT_FLUSHED:
+		vid_enc_input_frame_done(client_ctx, event,
+		status, (struct vcd_frame_data *)info);
+		break;
+
+	case VCD_EVT_RESP_OUTPUT_DONE:
+	case VCD_EVT_RESP_OUTPUT_FLUSHED:
+		vid_enc_output_frame_done(client_ctx, event, status,
+		(struct vcd_frame_data *)info);
+		break;
+
+	case VCD_EVT_RESP_PAUSE:
+	case VCD_EVT_RESP_START:
+	case VCD_EVT_RESP_STOP:
+	case VCD_EVT_RESP_FLUSH_INPUT_DONE:
+	case VCD_EVT_RESP_FLUSH_OUTPUT_DONE:
+	case VCD_EVT_IND_OUTPUT_RECONFIG:
+	case VCD_EVT_IND_HWERRFATAL:
+	case VCD_EVT_IND_RESOURCES_LOST:
+		vid_enc_lean_event(client_ctx, event, status);
+		break;
+
+	default:
+		ERR("%s() :  Error - Invalid event type =%u\n",
+		__func__, event);
+		break;
+	}
+}
+
+static u32 vid_enc_msg_pending(struct video_client_ctx *client_ctx)
+{
+	u32 islist_empty = 0;
+
+	mutex_lock(&client_ctx->msg_queue_lock);
+	islist_empty = list_empty(&client_ctx->msg_queue);
+	mutex_unlock(&client_ctx->msg_queue_lock);
+
+	if (islist_empty) {
+		DBG("%s(): vid_enc msg queue empty\n",
+			__func__);
+		if (client_ctx->stop_msg) {
+			DBG("%s(): List empty and Stop Msg set\n",
+				__func__);
+			return client_ctx->stop_msg;
+		}
+	} else
+		DBG("%s(): vid_enc msg queue Not empty\n",
+			__func__);
+
+	return !islist_empty;
+}
+
+static int vid_enc_get_next_msg(struct video_client_ctx *client_ctx,
+		struct venc_msg *venc_msg_info)
+{
+	int rc;
+	struct vid_enc_msg *vid_enc_msg = NULL;
+
+	if (!client_ctx)
+		return -EIO;
+
+	rc = wait_event_interruptible(client_ctx->msg_wait,
+		vid_enc_msg_pending(client_ctx));
+
+	if (rc < 0) {
+		DBG("rc = %d,stop_msg= %u\n", rc, client_ctx->stop_msg);
+		return rc;
+	} else if (client_ctx->stop_msg) {
+		DBG("stopped stop_msg = %u\n", client_ctx->stop_msg);
+		return -EIO;
+	}
+
+	mutex_lock(&client_ctx->msg_queue_lock);
+
+	if (!list_empty(&client_ctx->msg_queue)) {
+		DBG("%s(): After Wait\n", __func__);
+		vid_enc_msg = list_first_entry(&client_ctx->msg_queue,
+					struct vid_enc_msg, list);
+		list_del(&vid_enc_msg->list);
+		memcpy(venc_msg_info, &vid_enc_msg->venc_msg_info,
+		sizeof(struct venc_msg));
+		kfree(vid_enc_msg);
+	}
+	mutex_unlock(&client_ctx->msg_queue_lock);
+	return 0;
+}
+
+static u32 vid_enc_close_client(struct video_client_ctx *client_ctx)
+{
+	struct vid_enc_msg *vid_enc_msg = NULL;
+	u32 vcd_status;
+	int rc;
+
+	INFO("\n msm_vidc_enc: Inside %s()", __func__);
+	if (!client_ctx || (!client_ctx->vcd_handle)) {
+		ERR("\n %s(): Invalid client_ctx", __func__);
+		return false;
+	}
+
+	mutex_lock(&vid_enc_device_p->lock);
+
+	if (!stop_cmd) {
+		vcd_status = vcd_stop(client_ctx->vcd_handle);
+		DBG("Waiting for VCD_STOP: Before Timeout\n");
+		if (!vcd_status) {
+			rc = wait_for_completion_timeout(&client_ctx->event,
+				5 * HZ);
+			if (!rc) {
+				ERR("%s:ERROR vcd_stop time out"
+				"rc = %d\n", __func__, rc);
+			}
+
+			if (client_ctx->event_status) {
+				ERR("%s:ERROR "
+				"vcd_stop Not successs\n", __func__);
+			}
+		}
+	}
+	DBG("VCD_STOPPED: After Timeout, calling VCD_CLOSE\n");
+	mutex_lock(&client_ctx->msg_queue_lock);
+	while (!list_empty(&client_ctx->msg_queue)) {
+		DBG("%s(): Delete remaining entries\n", __func__);
+		vid_enc_msg = list_first_entry(&client_ctx->msg_queue,
+					struct vid_enc_msg, list);
+		list_del(&vid_enc_msg->list);
+		kfree(vid_enc_msg);
+	}
+	mutex_unlock(&client_ctx->msg_queue_lock);
+	vcd_status = vcd_close(client_ctx->vcd_handle);
+
+	if (vcd_status) {
+		mutex_unlock(&vid_enc_device_p->lock);
+		return false;
+	}
+	memset((void *)client_ctx, 0,
+		sizeof(struct video_client_ctx));
+
+	vid_enc_device_p->num_clients--;
+	stop_cmd = 0;
+	mutex_unlock(&vid_enc_device_p->lock);
+	return true;
+}
+
+
+static int vid_enc_open(struct inode *inode, struct file *file)
+{
+	s32 client_index;
+	struct video_client_ctx *client_ctx;
+	int rc = 0;
+	u8 client_count = 0;
+
+	INFO("\n msm_vidc_enc: Inside %s()", __func__);
+
+	mutex_lock(&vid_enc_device_p->lock);
+
+	stop_cmd = 0;
+	client_count = vcd_get_num_of_clients();
+	if (client_count == VIDC_MAX_NUM_CLIENTS) {
+		ERR("ERROR : vid_enc_open() max number of clients"
+		    "limit reached\n");
+		mutex_unlock(&vid_enc_device_p->lock);
+		return -ENODEV;
+	}
+
+	DBG(" Virtual Address of ioremap is %p\n", vid_enc_device_p->virt_base);
+	if (!vid_enc_device_p->num_clients) {
+		if (!vidc_load_firmware())
+			return -ENODEV;
+	}
+
+	client_index = vid_enc_get_empty_client_index();
+
+	if (client_index == -1) {
+		ERR("%s() : No free clients client_index == -1\n",
+			__func__);
+		return -ENODEV;
+	}
+
+	client_ctx =
+		&vid_enc_device_p->venc_clients[client_index];
+	vid_enc_device_p->num_clients++;
+
+	init_completion(&client_ctx->event);
+	mutex_init(&client_ctx->msg_queue_lock);
+	mutex_init(&client_ctx->enrty_queue_lock);
+	INIT_LIST_HEAD(&client_ctx->msg_queue);
+	init_waitqueue_head(&client_ctx->msg_wait);
+	if (vcd_get_ion_status()) {
+		client_ctx->user_ion_client = vcd_get_ion_client();
+		if (!client_ctx->user_ion_client) {
+			ERR("vcd_open ion get client failed");
+			return -EFAULT;
+		}
+	}
+	rc = vcd_open(vid_enc_device_p->device_handle, false,
+		vid_enc_vcd_cb, client_ctx, 0);
+	client_ctx->stop_msg = 0;
+
+	if (!rc) {
+		wait_for_completion(&client_ctx->event);
+		if (client_ctx->event_status) {
+			ERR("callback for vcd_open returned error: %u",
+				client_ctx->event_status);
+			mutex_unlock(&vid_enc_device_p->lock);
+			return -EFAULT;
+		}
+	} else {
+		ERR("vcd_open returned error: %u", rc);
+		mutex_unlock(&vid_enc_device_p->lock);
+		return rc;
+	}
+	file->private_data = client_ctx;
+	mutex_unlock(&vid_enc_device_p->lock);
+	return rc;
+}
+
+static int vid_enc_release(struct inode *inode, struct file *file)
+{
+	struct video_client_ctx *client_ctx = file->private_data;
+	INFO("\n msm_vidc_enc: Inside %s()", __func__);
+	vid_enc_close_client(client_ctx);
+	vidc_release_firmware();
+#ifndef USE_RES_TRACKER
+	vidc_disable_clk();
+#endif
+	INFO("\n msm_vidc_enc: Return from %s()", __func__);
+	return 0;
+}
+
+static const struct file_operations vid_enc_fops = {
+	.owner = THIS_MODULE,
+	.open = vid_enc_open,
+	.release = vid_enc_release,
+	.unlocked_ioctl = vid_enc_ioctl,
+};
+
+void vid_enc_interrupt_deregister(void)
+{
+}
+
+void vid_enc_interrupt_register(void *device_name)
+{
+}
+
+void vid_enc_interrupt_clear(void)
+{
+}
+
+void *vid_enc_map_dev_base_addr(void *device_name)
+{
+	return vid_enc_device_p->virt_base;
+}
+
+static int vid_enc_vcd_init(void)
+{
+	int rc;
+	struct vcd_init_config vcd_init_config;
+	u32 i;
+
+	INFO("\n msm_vidc_enc: Inside %s()", __func__);
+	vid_enc_device_p->num_clients = 0;
+
+	for (i = 0; i < VIDC_MAX_NUM_CLIENTS; i++) {
+		memset((void *)&vid_enc_device_p->venc_clients[i], 0,
+		sizeof(vid_enc_device_p->venc_clients[i]));
+	}
+
+	mutex_init(&vid_enc_device_p->lock);
+	vid_enc_device_p->virt_base = vidc_get_ioaddr();
+
+	if (!vid_enc_device_p->virt_base) {
+		ERR("%s() : ioremap failed\n", __func__);
+		return -ENOMEM;
+	}
+
+	vcd_init_config.device_name = "VIDC";
+	vcd_init_config.map_dev_base_addr =
+		vid_enc_map_dev_base_addr;
+	vcd_init_config.interrupt_clr =
+		vid_enc_interrupt_clear;
+	vcd_init_config.register_isr =
+		vid_enc_interrupt_register;
+	vcd_init_config.deregister_isr =
+		vid_enc_interrupt_deregister;
+
+	rc = vcd_init(&vcd_init_config,
+		&vid_enc_device_p->device_handle);
+
+	if (rc) {
+		ERR("%s() : vcd_init failed\n",
+			__func__);
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static int __init vid_enc_init(void)
+{
+	int rc = 0;
+	struct device *class_devp;
+
+	INFO("\n msm_vidc_enc: Inside %s()", __func__);
+	vid_enc_device_p = kzalloc(sizeof(struct vid_enc_dev),
+					 GFP_KERNEL);
+	if (!vid_enc_device_p) {
+		ERR("%s Unable to allocate memory for vid_enc_dev\n",
+			__func__);
+		return -ENOMEM;
+	}
+
+	rc = alloc_chrdev_region(&vid_enc_dev_num, 0, 1, VID_ENC_NAME);
+	if (rc < 0) {
+		ERR("%s: alloc_chrdev_region Failed rc = %d\n",
+			__func__, rc);
+		goto error_vid_enc_alloc_chrdev_region;
+	}
+
+	vid_enc_class = class_create(THIS_MODULE, VID_ENC_NAME);
+	if (IS_ERR(vid_enc_class)) {
+		rc = PTR_ERR(vid_enc_class);
+		ERR("%s: couldn't create vid_enc_class rc = %d\n",
+			__func__, rc);
+		goto error_vid_enc_class_create;
+	}
+
+	class_devp = device_create(vid_enc_class, NULL,
+				vid_enc_dev_num, NULL, VID_ENC_NAME);
+
+	if (IS_ERR(class_devp)) {
+		rc = PTR_ERR(class_devp);
+		ERR("%s: class device_create failed %d\n",
+		__func__, rc);
+		goto error_vid_enc_class_device_create;
+	}
+
+	vid_enc_device_p->device = class_devp;
+
+	cdev_init(&vid_enc_device_p->cdev, &vid_enc_fops);
+	vid_enc_device_p->cdev.owner = THIS_MODULE;
+	rc = cdev_add(&(vid_enc_device_p->cdev), vid_enc_dev_num, 1);
+
+	if (rc < 0) {
+		ERR("%s: cdev_add failed %d\n",
+		__func__, rc);
+		goto error_vid_enc_cdev_add;
+	}
+	vid_enc_vcd_init();
+	return 0;
+
+error_vid_enc_cdev_add:
+	device_destroy(vid_enc_class, vid_enc_dev_num);
+error_vid_enc_class_device_create:
+	class_destroy(vid_enc_class);
+error_vid_enc_class_create:
+	unregister_chrdev_region(vid_enc_dev_num, 1);
+error_vid_enc_alloc_chrdev_region:
+	kfree(vid_enc_device_p);
+
+	return rc;
+}
+
+static void __exit vid_enc_exit(void)
+{
+	INFO("\n msm_vidc_enc: Inside %s()", __func__);
+	cdev_del(&(vid_enc_device_p->cdev));
+	device_destroy(vid_enc_class, vid_enc_dev_num);
+	class_destroy(vid_enc_class);
+	unregister_chrdev_region(vid_enc_dev_num, 1);
+	kfree(vid_enc_device_p);
+	INFO("\n msm_vidc_enc: Return from %s()", __func__);
+}
+static long vid_enc_ioctl(struct file *file,
+		unsigned cmd, unsigned long u_arg)
+{
+	struct video_client_ctx *client_ctx = NULL;
+	struct venc_ioctl_msg venc_msg;
+	void __user *arg = (void __user *)u_arg;
+	u32 result = true;
+	int result_read = -1;
+
+	DBG("%s\n", __func__);
+
+	client_ctx = (struct video_client_ctx *)file->private_data;
+	if (!client_ctx) {
+		ERR("!client_ctx. Cannot attach to device handle\n");
+		return -ENODEV;
+	}
+
+	switch (cmd) {
+	case VEN_IOCTL_CMD_READ_NEXT_MSG:
+	{
+		struct venc_msg cb_msg;
+		if (copy_from_user(&venc_msg, arg, sizeof(venc_msg)))
+			return -EFAULT;
+		DBG("VEN_IOCTL_CMD_READ_NEXT_MSG\n");
+		result_read = vid_enc_get_next_msg(client_ctx, &cb_msg);
+		if (result_read < 0)
+			return result_read;
+		if (copy_to_user(venc_msg.out, &cb_msg, sizeof(cb_msg)))
+			return -EFAULT;
+		break;
+	}
+	case VEN_IOCTL_CMD_STOP_READ_MSG:
+	{
+		DBG("VEN_IOCTL_CMD_STOP_READ_MSG\n");
+		client_ctx->stop_msg = 1;
+		wake_up(&client_ctx->msg_wait);
+		break;
+	}
+	case VEN_IOCTL_CMD_ENCODE_FRAME:
+	case VEN_IOCTL_CMD_FILL_OUTPUT_BUFFER:
+	{
+		struct venc_buffer enc_buffer;
+		if (copy_from_user(&venc_msg, arg, sizeof(venc_msg)))
+			return -EFAULT;
+		DBG("VEN_IOCTL_CMD_ENCODE_FRAME"
+			"/VEN_IOCTL_CMD_FILL_OUTPUT_BUFFER\n");
+		if (copy_from_user(&enc_buffer, venc_msg.in,
+						   sizeof(enc_buffer)))
+			return -EFAULT;
+		if (cmd == VEN_IOCTL_CMD_ENCODE_FRAME)
+			result = vid_enc_encode_frame(client_ctx,
+					&enc_buffer);
+		else
+			result = vid_enc_fill_output_buffer(client_ctx,
+					&enc_buffer);
+		if (!result) {
+			DBG("\n VEN_IOCTL_CMD_ENCODE_FRAME/"
+				"VEN_IOCTL_CMD_FILL_OUTPUT_BUFFER failed");
+			return -EIO;
+		}
+		break;
+	}
+	case VEN_IOCTL_SET_INPUT_BUFFER:
+	case VEN_IOCTL_SET_OUTPUT_BUFFER:
+	{
+		enum venc_buffer_dir buffer_dir;
+		struct venc_bufferpayload buffer_info;
+		if (copy_from_user(&venc_msg, arg, sizeof(venc_msg)))
+			return -EFAULT;
+		DBG("VEN_IOCTL_SET_INPUT_BUFFER/VEN_IOCTL_SET_OUTPUT_BUFFER\n");
+		if (copy_from_user(&buffer_info, venc_msg.in,
+			sizeof(buffer_info)))
+			return -EFAULT;
+		buffer_dir = VEN_BUFFER_TYPE_INPUT;
+		if (cmd == VEN_IOCTL_SET_OUTPUT_BUFFER)
+			buffer_dir = VEN_BUFFER_TYPE_OUTPUT;
+		result = vid_enc_set_buffer(client_ctx, &buffer_info,
+				buffer_dir);
+		if (!result) {
+			DBG("\n VEN_IOCTL_SET_INPUT_BUFFER"
+				"/VEN_IOCTL_SET_OUTPUT_BUFFER failed");
+			return -EIO;
+		}
+		break;
+	}
+	case VEN_IOCTL_CMD_FREE_INPUT_BUFFER:
+	case VEN_IOCTL_CMD_FREE_OUTPUT_BUFFER:
+	{
+		enum venc_buffer_dir buffer_dir;
+		struct venc_bufferpayload buffer_info;
+		if (copy_from_user(&venc_msg, arg, sizeof(venc_msg)))
+			return -EFAULT;
+
+		DBG("VEN_IOCTL_CMD_FREE_INPUT_BUFFER/"
+			"VEN_IOCTL_CMD_FREE_OUTPUT_BUFFER\n");
+
+		if (copy_from_user(&buffer_info, venc_msg.in,
+			sizeof(buffer_info)))
+			return -EFAULT;
+
+		buffer_dir = VEN_BUFFER_TYPE_INPUT;
+		if (cmd == VEN_IOCTL_CMD_FREE_OUTPUT_BUFFER)
+			buffer_dir = VEN_BUFFER_TYPE_OUTPUT;
+
+		result = vid_enc_free_buffer(client_ctx, &buffer_info,
+				buffer_dir);
+		if (!result) {
+			DBG("\n VEN_IOCTL_CMD_FREE_OUTPUT_BUFFER"
+				"/VEN_IOCTL_CMD_FREE_OUTPUT_BUFFER failed");
+			return -EIO;
+		}
+		break;
+	}
+	case VEN_IOCTL_SET_INPUT_BUFFER_REQ:
+	case VEN_IOCTL_SET_OUTPUT_BUFFER_REQ:
+	{
+		struct venc_allocatorproperty allocatorproperty;
+		if (copy_from_user(&venc_msg, arg, sizeof(venc_msg)))
+			return -EFAULT;
+
+		DBG("VEN_IOCTL_SET_INPUT_BUFFER_REQ"
+			"/VEN_IOCTL_SET_OUTPUT_BUFFER_REQ\n");
+
+		if (copy_from_user(&allocatorproperty, venc_msg.in,
+			sizeof(allocatorproperty)))
+			return -EFAULT;
+
+		if (cmd == VEN_IOCTL_SET_OUTPUT_BUFFER_REQ)
+				result = vid_enc_set_buffer_req(client_ctx,
+						&allocatorproperty, false);
+		else
+			result = vid_enc_set_buffer_req(client_ctx,
+					&allocatorproperty, true);
+		if (!result) {
+			DBG("setting VEN_IOCTL_SET_OUTPUT_BUFFER_REQ/"
+			"VEN_IOCTL_SET_INPUT_BUFFER_REQ failed\n");
+			return -EIO;
+		}
+		break;
+	}
+	case VEN_IOCTL_GET_INPUT_BUFFER_REQ:
+	case VEN_IOCTL_GET_OUTPUT_BUFFER_REQ:
+	{
+		struct venc_allocatorproperty allocatorproperty;
+		if (copy_from_user(&venc_msg, arg, sizeof(venc_msg)))
+			return -EFAULT;
+
+		DBG("VEN_IOCTL_GET_INPUT_BUFFER_REQ/"
+			"VEN_IOCTL_GET_OUTPUT_BUFFER_REQ\n");
+
+		if (cmd == VEN_IOCTL_GET_OUTPUT_BUFFER_REQ)
+			result = vid_enc_get_buffer_req(client_ctx,
+					&allocatorproperty, false);
+		else
+			result = vid_enc_get_buffer_req(client_ctx,
+					&allocatorproperty, true);
+		if (!result)
+			return -EIO;
+		if (copy_to_user(venc_msg.out, &allocatorproperty,
+				sizeof(allocatorproperty)))
+			return -EFAULT;
+		break;
+	}
+	case VEN_IOCTL_CMD_FLUSH:
+	{
+		struct venc_bufferflush bufferflush;
+		if (copy_from_user(&venc_msg, arg, sizeof(venc_msg)))
+			return -EFAULT;
+
+		DBG("VEN_IOCTL_CMD_FLUSH\n");
+		if (copy_from_user(&bufferflush, venc_msg.in,
+			sizeof(bufferflush)))
+			return -EFAULT;
+		INFO("\n %s(): Calling vid_enc_flush with mode = %lu",
+			 __func__, bufferflush.flush_mode);
+		result = vid_enc_flush(client_ctx, &bufferflush);
+
+		if (!result) {
+			ERR("setting VEN_IOCTL_CMD_FLUSH failed\n");
+			return -EIO;
+		}
+		break;
+	}
+	case VEN_IOCTL_CMD_START:
+	{
+		INFO("\n %s(): Executing VEN_IOCTL_CMD_START", __func__);
+		result = vid_enc_start_stop(client_ctx, true);
+		if (!result) {
+			ERR("setting VEN_IOCTL_CMD_START failed\n");
+			return -EIO;
+		}
+		break;
+	}
+	case VEN_IOCTL_CMD_STOP:
+	{
+		INFO("\n %s(): Executing VEN_IOCTL_CMD_STOP", __func__);
+		result = vid_enc_start_stop(client_ctx, false);
+		if (!result) {
+			ERR("setting VEN_IOCTL_CMD_STOP failed\n");
+			return -EIO;
+		}
+		stop_cmd = 1;
+		break;
+	}
+	case VEN_IOCTL_CMD_PAUSE:
+	{
+		INFO("\n %s(): Executing VEN_IOCTL_CMD_PAUSE", __func__);
+		result = vid_enc_pause_resume(client_ctx, true);
+		if (!result) {
+			ERR("setting VEN_IOCTL_CMD_PAUSE failed\n");
+			return -EIO;
+		}
+		break;
+	}
+	case VEN_IOCTL_CMD_RESUME:
+	{
+		INFO("\n %s(): Executing VEN_IOCTL_CMD_RESUME", __func__);
+		result = vid_enc_pause_resume(client_ctx, false);
+		if (!result) {
+			ERR("setting VEN_IOCTL_CMD_RESUME failed\n");
+			return -EIO;
+		}
+		break;
+	}
+	case VEN_IOCTL_SET_RECON_BUFFER:
+	{
+		struct venc_recon_addr venc_recon;
+		if (copy_from_user(&venc_msg, arg, sizeof(venc_msg)))
+			return -EFAULT;
+		DBG("VEN_IOCTL_SET_RECON_BUFFER\n");
+		if (copy_from_user(&venc_recon, venc_msg.in,
+				sizeof(venc_recon)))
+				return -EFAULT;
+		result = vid_enc_set_recon_buffers(client_ctx,
+					&venc_recon);
+		if (!result) {
+			ERR("setting VEN_IOCTL_SET_RECON_BUFFER failed\n");
+			return -EIO;
+		}
+		break;
+	}
+	case VEN_IOCTL_FREE_RECON_BUFFER:
+	{
+		struct venc_recon_addr venc_recon;
+		DBG("VEN_IOCTL_FREE_RECON_BUFFER\n");
+		if (copy_from_user(&venc_msg, arg, sizeof(venc_msg)))
+			return -EFAULT;
+		if (copy_from_user(&venc_recon, venc_msg.in,
+				sizeof(venc_recon)))
+				return -EFAULT;
+		result = vid_enc_free_recon_buffers(client_ctx,
+				&venc_recon);
+		if (!result) {
+			ERR("VEN_IOCTL_FREE_RECON_BUFFER failed\n");
+			return -EIO;
+		}
+		break;
+	}
+	case VEN_IOCTL_GET_RECON_BUFFER_SIZE:
+	{
+		struct venc_recon_buff_size venc_recon_size;
+		if (copy_from_user(&venc_msg, arg, sizeof(venc_msg)))
+			return -EFAULT;
+		DBG("VEN_IOCTL_GET_RECON_BUFFER_SIZE\n");
+		if (copy_from_user(&venc_recon_size, venc_msg.out,
+						   sizeof(venc_recon_size)))
+				return -EFAULT;
+		result = vid_enc_get_recon_buffer_size(client_ctx,
+					&venc_recon_size);
+		if (result) {
+				if (copy_to_user(venc_msg.out, &venc_recon_size,
+					sizeof(venc_recon_size)))
+					return -EFAULT;
+			} else {
+				ERR("setting VEN_IOCTL_GET_RECON_BUFFER_SIZE"
+					"failed\n");
+				return -EIO;
+			}
+		break;
+	}
+	case VEN_IOCTL_SET_QP_RANGE:
+	case VEN_IOCTL_GET_QP_RANGE:
+	{
+		struct venc_qprange qprange;
+		if (copy_from_user(&venc_msg, arg, sizeof(venc_msg)))
+			return -EFAULT;
+		DBG("VEN_IOCTL_G(S)ET_QP_RANGE\n");
+		if (cmd == VEN_IOCTL_SET_QP_RANGE) {
+			if (copy_from_user(&qprange, venc_msg.in,
+				sizeof(qprange)))
+				return -EFAULT;
+			result = vid_enc_set_get_qprange(client_ctx,
+					&qprange, true);
+		} else {
+			result = vid_enc_set_get_qprange(client_ctx,
+					&qprange, false);
+			if (result) {
+				if (copy_to_user(venc_msg.out, &qprange,
+					sizeof(qprange)))
+					return -EFAULT;
+			}
+		}
+		if (!result) {
+			ERR("setting VEN_IOCTL_G(S)ET_QP_RANGE failed\n");
+			return -EIO;
+		}
+		break;
+	}
+	case VEN_IOCTL_SET_HEC:
+	case VEN_IOCTL_GET_HEC:
+	{
+		struct venc_headerextension headerextension;
+		if (copy_from_user(&venc_msg, arg, sizeof(venc_msg)))
+			return -EFAULT;
+		DBG("VEN_IOCTL_(G)SET_HEC\n");
+		if (cmd == VEN_IOCTL_SET_HEC) {
+			if (copy_from_user(&headerextension, venc_msg.in,
+				sizeof(headerextension)))
+				return -EFAULT;
+
+			result = vid_enc_set_get_headerextension(client_ctx,
+					&headerextension, true);
+		} else {
+			result = vid_enc_set_get_headerextension(client_ctx,
+					&headerextension, false);
+			if (result) {
+				if (copy_to_user(venc_msg.out, &headerextension,
+				sizeof(headerextension)))
+					return -EFAULT;
+			}
+		}
+
+		if (!result) {
+			ERR("setting VEN_IOCTL_(G)SET_HEC failed\n");
+			return -EIO;
+		}
+		break;
+	}
+	case VEN_IOCTL_SET_TARGET_BITRATE:
+	case VEN_IOCTL_GET_TARGET_BITRATE:
+	{
+		struct venc_targetbitrate targetbitrate;
+		if (copy_from_user(&venc_msg, arg, sizeof(venc_msg)))
+			return -EFAULT;
+		DBG("VEN_IOCTL_(G)SET_TARGET_BITRATE\n");
+		if (cmd == VEN_IOCTL_SET_TARGET_BITRATE) {
+			if (copy_from_user(&targetbitrate, venc_msg.in,
+				sizeof(targetbitrate)))
+				return -EFAULT;
+
+			result = vid_enc_set_get_bitrate(client_ctx,
+					&targetbitrate, true);
+		} else {
+			result = vid_enc_set_get_bitrate(client_ctx,
+					&targetbitrate, false);
+			if (result) {
+				if (copy_to_user(venc_msg.out, &targetbitrate,
+					sizeof(targetbitrate)))
+					return -EFAULT;
+			}
+		}
+		if (!result) {
+			ERR("setting VEN_IOCTL_(G)SET_TARGET_BITRATE failed\n");
+			return -EIO;
+		}
+		break;
+	}
+	case VEN_IOCTL_SET_FRAME_RATE:
+	case VEN_IOCTL_GET_FRAME_RATE:
+	{
+		struct venc_framerate framerate;
+		if (copy_from_user(&venc_msg, arg, sizeof(venc_msg)))
+			return -EFAULT;
+		DBG("VEN_IOCTL_(G)SET_FRAME_RATE\n");
+		if (cmd == VEN_IOCTL_SET_FRAME_RATE) {
+			if (copy_from_user(&framerate, venc_msg.in,
+				sizeof(framerate)))
+				return -EFAULT;
+			result = vid_enc_set_get_framerate(client_ctx,
+					&framerate, true);
+		} else {
+			result = vid_enc_set_get_framerate(client_ctx,
+					&framerate,	false);
+			if (result) {
+				if (copy_to_user(venc_msg.out, &framerate,
+					sizeof(framerate)))
+					return -EFAULT;
+			}
+		}
+
+		if (!result) {
+			ERR("VEN_IOCTL_(G)SET_FRAME_RATE failed\n");
+			return -EIO;
+		}
+		break;
+	}
+	case VEN_IOCTL_SET_VOP_TIMING_CFG:
+	case VEN_IOCTL_GET_VOP_TIMING_CFG:
+	{
+		struct venc_voptimingcfg voptimingcfg;
+		if (copy_from_user(&venc_msg, arg, sizeof(venc_msg)))
+			return -EFAULT;
+
+		DBG("VEN_IOCTL_(G)SET_VOP_TIMING_CFG\n");
+		if (cmd == VEN_IOCTL_SET_VOP_TIMING_CFG) {
+			if (copy_from_user(&voptimingcfg, venc_msg.in,
+				sizeof(voptimingcfg)))
+				return -EFAULT;
+			result = vid_enc_set_get_voptimingcfg(client_ctx,
+					&voptimingcfg, true);
+		} else {
+			result = vid_enc_set_get_voptimingcfg(client_ctx,
+					&voptimingcfg, false);
+			if (result) {
+				if (copy_to_user(venc_msg.out, &voptimingcfg,
+					sizeof(voptimingcfg)))
+					return -EFAULT;
+			}
+		}
+		if (!result) {
+			ERR("VEN_IOCTL_(G)SET_VOP_TIMING_CFG failed\n");
+			return -EIO;
+		}
+		break;
+	}
+	case VEN_IOCTL_SET_RATE_CTRL_CFG:
+	case VEN_IOCTL_GET_RATE_CTRL_CFG:
+	{
+		struct venc_ratectrlcfg ratectrlcfg;
+		if (copy_from_user(&venc_msg, arg, sizeof(venc_msg)))
+			return -EFAULT;
+		DBG("VEN_IOCTL_(G)SET_RATE_CTRL_CFG\n");
+		if (cmd == VEN_IOCTL_SET_RATE_CTRL_CFG) {
+			if (copy_from_user(&ratectrlcfg, venc_msg.in,
+				sizeof(ratectrlcfg)))
+				return -EFAULT;
+
+			result = vid_enc_set_get_ratectrlcfg(client_ctx,
+					&ratectrlcfg, true);
+		} else {
+			result = vid_enc_set_get_ratectrlcfg(client_ctx,
+					&ratectrlcfg, false);
+			if (result) {
+				if (copy_to_user(venc_msg.out, &ratectrlcfg,
+					sizeof(ratectrlcfg)))
+					return -EFAULT;
+			}
+		}
+		if (!result) {
+			ERR("setting VEN_IOCTL_(G)SET_RATE_CTRL_CFG failed\n");
+			return -EIO;
+		}
+		break;
+	}
+	case VEN_IOCTL_SET_MULTI_SLICE_CFG:
+	case VEN_IOCTL_GET_MULTI_SLICE_CFG:
+	{
+		struct venc_multiclicecfg multiclicecfg;
+		if (copy_from_user(&venc_msg, arg, sizeof(venc_msg)))
+			return -EFAULT;
+		DBG("VEN_IOCTL_(G)SET_MULTI_SLICE_CFG\n");
+		if (cmd == VEN_IOCTL_SET_MULTI_SLICE_CFG) {
+			if (copy_from_user(&multiclicecfg, venc_msg.in,
+				sizeof(multiclicecfg)))
+				return -EFAULT;
+
+			result = vid_enc_set_get_multiclicecfg(client_ctx,
+					&multiclicecfg, true);
+		} else {
+			result = vid_enc_set_get_multiclicecfg(client_ctx,
+					&multiclicecfg, false);
+			if (result) {
+				if (copy_to_user(venc_msg.out, &multiclicecfg,
+					sizeof(multiclicecfg)))
+					return -EFAULT;
+			}
+		}
+		if (!result) {
+			ERR("VEN_IOCTL_(G)SET_MULTI_SLICE_CFG failed\n");
+			return -EIO;
+		}
+		break;
+	}
+	case VEN_IOCTL_SET_INTRA_REFRESH:
+	case VEN_IOCTL_GET_INTRA_REFRESH:
+	{
+		struct venc_intrarefresh intrarefresh;
+		if (copy_from_user(&venc_msg, arg, sizeof(venc_msg)))
+			return -EFAULT;
+		DBG("VEN_IOCTL_(G)SET_INTRA_REFRESH\n");
+		if (cmd == VEN_IOCTL_SET_INTRA_REFRESH) {
+			if (copy_from_user(&intrarefresh, venc_msg.in,
+				sizeof(intrarefresh)))
+				return -EFAULT;
+			result = vid_enc_set_get_intrarefresh(client_ctx,
+					&intrarefresh, true);
+		} else {
+			result = vid_enc_set_get_intrarefresh(client_ctx,
+					&intrarefresh, false);
+			if (result) {
+				if (copy_to_user(venc_msg.out, &intrarefresh,
+					sizeof(intrarefresh)))
+					return -EFAULT;
+			}
+		}
+		if (!result) {
+			ERR("setting VEN_IOCTL_SET_INTRA_REFRESH failed\n");
+			return -EIO;
+		}
+		break;
+	}
+	case VEN_IOCTL_SET_DEBLOCKING_CFG:
+	case VEN_IOCTL_GET_DEBLOCKING_CFG:
+	{
+		struct venc_dbcfg dbcfg;
+		if (copy_from_user(&venc_msg, arg, sizeof(venc_msg)))
+			return -EFAULT;
+
+		DBG("VEN_IOCTL_(G)SET_DEBLOCKING_CFG\n");
+		if (cmd == VEN_IOCTL_SET_DEBLOCKING_CFG) {
+			if (copy_from_user(&dbcfg, venc_msg.in,
+				sizeof(dbcfg)))
+				return -EFAULT;
+			result = vid_enc_set_get_dbcfg(client_ctx,
+					&dbcfg, true);
+		} else {
+			result = vid_enc_set_get_dbcfg(client_ctx,
+					&dbcfg, false);
+			if (result) {
+				if (copy_to_user(venc_msg.out, &dbcfg,
+				sizeof(dbcfg)))
+					return -EFAULT;
+			}
+		}
+		if (!result) {
+			ERR("setting VEN_IOCTL_SET_DEBLOCKING_CFG failed\n");
+			return -EIO;
+		}
+		break;
+	}
+	case VEN_IOCTL_SET_ENTROPY_CFG:
+	case VEN_IOCTL_GET_ENTROPY_CFG:
+	{
+		struct venc_entropycfg entropy_cfg;
+		if (copy_from_user(&venc_msg, arg, sizeof(venc_msg)))
+			return -EFAULT;
+		DBG("VEN_IOCTL_(G)SET_ENTROPY_CFG\n");
+		if (cmd == VEN_IOCTL_SET_ENTROPY_CFG) {
+			if (copy_from_user(&entropy_cfg, venc_msg.in,
+				sizeof(entropy_cfg)))
+				return -EFAULT;
+			result = vid_enc_set_get_entropy_cfg(client_ctx,
+					&entropy_cfg, true);
+		} else {
+			result = vid_enc_set_get_entropy_cfg(client_ctx,
+					&entropy_cfg, false);
+			if (result) {
+				if (copy_to_user(venc_msg.out, &entropy_cfg,
+				sizeof(entropy_cfg)))
+					return -EFAULT;
+			}
+		}
+		if (!result) {
+			ERR("setting VEN_IOCTL_(G)SET_ENTROPY_CFG failed\n");
+			return -EIO;
+		}
+		break;
+	}
+	case VEN_IOCTL_GET_SEQUENCE_HDR:
+	{
+		struct venc_seqheader seq_header;
+		if (copy_from_user(&venc_msg, arg, sizeof(venc_msg)))
+			return -EFAULT;
+
+		if (copy_from_user(&seq_header, venc_msg.in,
+			sizeof(seq_header)))
+			return -EFAULT;
+
+		DBG("VEN_IOCTL_GET_SEQUENCE_HDR\n");
+		result = vid_enc_get_sequence_header(client_ctx,
+				&seq_header);
+		if (!result) {
+			ERR("get sequence header failed\n");
+			return -EIO;
+		}
+		DBG("seq_header: buf=%x, sz=%d, hdrlen=%d\n",
+			(int)seq_header.hdrbufptr,
+			(int)seq_header.bufsize,
+			(int)seq_header.hdrlen);
+		if (copy_to_user(venc_msg.out, &seq_header,
+			sizeof(seq_header)))
+			return -EFAULT;
+		break;
+	}
+	case VEN_IOCTL_CMD_REQUEST_IFRAME:
+	{
+		result = vid_enc_request_iframe(client_ctx);
+		if (!result) {
+			ERR("setting VEN_IOCTL_CMD_REQUEST_IFRAME failed\n");
+			return -EIO;
+		}
+		break;
+	}
+	case VEN_IOCTL_SET_INTRA_PERIOD:
+	case VEN_IOCTL_GET_INTRA_PERIOD:
+	{
+		struct venc_intraperiod intraperiod;
+		if (copy_from_user(&venc_msg, arg, sizeof(venc_msg)))
+			return -EFAULT;
+		DBG("VEN_IOCTL_(G)SET_INTRA_PERIOD\n");
+		if (cmd == VEN_IOCTL_SET_INTRA_PERIOD) {
+			if (copy_from_user(&intraperiod, venc_msg.in,
+				sizeof(intraperiod)))
+				return -EFAULT;
+			result = vid_enc_set_get_intraperiod(client_ctx,
+					&intraperiod, true);
+		} else {
+			result = vid_enc_set_get_intraperiod(client_ctx,
+					&intraperiod, false);
+			if (result) {
+				if (copy_to_user(venc_msg.out, &intraperiod,
+					sizeof(intraperiod)))
+					return -EFAULT;
+			}
+		}
+		if (!result) {
+			ERR("setting VEN_IOCTL_(G)SET_INTRA_PERIOD failed\n");
+			return -EIO;
+		}
+		break;
+	}
+	case VEN_IOCTL_SET_SESSION_QP:
+	case VEN_IOCTL_GET_SESSION_QP:
+	{
+		struct venc_sessionqp session_qp;
+		if (copy_from_user(&venc_msg, arg, sizeof(venc_msg)))
+			return -EFAULT;
+		DBG("VEN_IOCTL_(G)SET_SESSION_QP\n");
+		if (cmd == VEN_IOCTL_SET_SESSION_QP) {
+			if (copy_from_user(&session_qp,	venc_msg.in,
+				sizeof(session_qp)))
+				return -EFAULT;
+			result = vid_enc_set_get_session_qp(client_ctx,
+					&session_qp, true);
+		} else {
+			result = vid_enc_set_get_session_qp(client_ctx,
+					&session_qp, false);
+			if (result) {
+				if (copy_to_user(venc_msg.out, &session_qp,
+					sizeof(session_qp)))
+					return -EFAULT;
+			}
+		}
+		if (!result) {
+			ERR("setting VEN_IOCTL_(G)SET_SESSION_QP failed\n");
+			return -EIO;
+		}
+		break;
+	}
+	case VEN_IOCTL_SET_PROFILE_LEVEL:
+	case VEN_IOCTL_GET_PROFILE_LEVEL:
+	{
+		struct ven_profilelevel profile_level;
+		if (copy_from_user(&venc_msg, arg, sizeof(venc_msg)))
+			return -EFAULT;
+
+		DBG("VEN_IOCTL_(G)SET_PROFILE_LEVEL\n");
+		if (cmd == VEN_IOCTL_SET_PROFILE_LEVEL) {
+			if (copy_from_user(&profile_level, venc_msg.in,
+				sizeof(profile_level)))
+				return -EFAULT;
+			result = vid_enc_set_get_profile_level(client_ctx,
+					&profile_level, true);
+		} else {
+			result = vid_enc_set_get_profile_level(client_ctx,
+					&profile_level, false);
+			if (result) {
+				if (copy_to_user(venc_msg.out,
+				&profile_level,	sizeof(profile_level)))
+					return -EFAULT;
+			}
+		}
+		if (!result) {
+			ERR("setting VEN_IOCTL_SET_PROFILE_LEVEL failed\n");
+			return -EIO;
+		}
+		break;
+	}
+	case VEN_IOCTL_SET_CODEC_PROFILE:
+	case VEN_IOCTL_GET_CODEC_PROFILE:
+	{
+		struct venc_profile profile;
+		if (copy_from_user(&venc_msg, arg, sizeof(venc_msg)))
+			return -EFAULT;
+
+		DBG("VEN_IOCTL_(G)SET_CODEC_PROFILE\n");
+		if (cmd == VEN_IOCTL_SET_CODEC_PROFILE) {
+			if (copy_from_user(&profile, venc_msg.in,
+					sizeof(profile)))
+				return -EFAULT;
+			result = vid_enc_set_get_profile(client_ctx,
+					&profile, true);
+		} else {
+			result = vid_enc_set_get_profile(client_ctx,
+					&profile, false);
+			if (result) {
+				if (copy_to_user(venc_msg.out, &profile,
+						sizeof(profile)))
+					return -EFAULT;
+			}
+		}
+		if (!result) {
+			ERR("setting VEN_IOCTL_SET_CODEC_PROFILE failed\n");
+			return -EIO;
+		}
+		break;
+	}
+	case VEN_IOCTL_SET_SHORT_HDR:
+	case VEN_IOCTL_GET_SHORT_HDR:
+	{
+		struct venc_switch encoder_switch;
+		if (copy_from_user(&venc_msg, arg, sizeof(venc_msg)))
+			return -EFAULT;
+		DBG("Getting VEN_IOCTL_(G)SET_SHORT_HDR\n");
+		if (cmd == VEN_IOCTL_SET_SHORT_HDR) {
+			if (copy_from_user(&encoder_switch,	venc_msg.in,
+				sizeof(encoder_switch)))
+				return -EFAULT;
+
+			result = vid_enc_set_get_short_header(client_ctx,
+					&encoder_switch, true);
+		} else {
+			result = vid_enc_set_get_short_header(client_ctx,
+					&encoder_switch, false);
+			if (result) {
+				if (copy_to_user(venc_msg.out, &encoder_switch,
+					sizeof(encoder_switch)))
+					return -EFAULT;
+			}
+		}
+		if (!result) {
+			ERR("setting VEN_IOCTL_(G)SET_SHORT_HDR failed\n");
+			return -EIO;
+		}
+		break;
+	}
+	case VEN_IOCTL_SET_BASE_CFG:
+	case VEN_IOCTL_GET_BASE_CFG:
+	{
+		struct venc_basecfg base_config;
+		DBG("VEN_IOCTL_SET_BASE_CFG\n");
+		if (copy_from_user(&venc_msg, arg, sizeof(venc_msg)))
+			return -EFAULT;
+		if (cmd == VEN_IOCTL_SET_BASE_CFG) {
+			if (copy_from_user(&base_config, venc_msg.in,
+				sizeof(base_config)))
+				return -EFAULT;
+			result = vid_enc_set_get_base_cfg(client_ctx,
+					&base_config, true);
+		} else {
+			result = vid_enc_set_get_base_cfg(client_ctx,
+					&base_config, false);
+			if (result) {
+				if (copy_to_user(venc_msg.out, &base_config,
+					sizeof(base_config)))
+					return -EFAULT;
+			}
+		}
+		if (!result) {
+			ERR("setting VEN_IOCTL_SET_BASE_CFG failed\n");
+			return -EIO;
+		}
+		break;
+	}
+	case VEN_IOCTL_SET_LIVE_MODE:
+	case VEN_IOCTL_GET_LIVE_MODE:
+	{
+		struct venc_switch encoder_switch;
+		if (copy_from_user(&venc_msg, arg, sizeof(venc_msg)))
+			return -EFAULT;
+
+		DBG("Getting VEN_IOCTL_(G)SET_LIVE_MODE\n");
+		if (cmd == VEN_IOCTL_SET_LIVE_MODE) {
+			if (copy_from_user(&encoder_switch,	venc_msg.in,
+				sizeof(encoder_switch)))
+				return -EFAULT;
+			result = vid_enc_set_get_live_mode(client_ctx,
+					&encoder_switch, true);
+		} else {
+			result = vid_enc_set_get_live_mode(client_ctx,
+					&encoder_switch, false);
+			if (result) {
+				if (copy_to_user(venc_msg.out, &encoder_switch,
+					sizeof(encoder_switch)))
+					return -EFAULT;
+			}
+		}
+		if (!result) {
+			ERR("setting VEN_IOCTL_(G)SET_LIVE_MODE failed\n");
+			return -EIO;
+		}
+		break;
+	}
+	case VEN_IOCTL_GET_NUMBER_INSTANCES:
+	{
+		DBG("VEN_IOCTL_GET_NUMBER_INSTANCES\n");
+		if (copy_from_user(&venc_msg, arg, sizeof(venc_msg)))
+			return -EFAULT;
+		if (copy_to_user(venc_msg.out,
+			&vid_enc_device_p->num_clients, sizeof(u32)))
+			return -EFAULT;
+		break;
+	}
+	case VEN_IOCTL_SET_METABUFFER_MODE:
+	{
+		u32 metabuffer_mode, vcd_status;
+		struct vcd_property_hdr vcd_property_hdr;
+		struct vcd_property_live live_mode;
+
+		if (copy_from_user(&venc_msg, arg, sizeof(venc_msg)))
+			return -EFAULT;
+		if (copy_from_user(&metabuffer_mode, venc_msg.in,
+			sizeof(metabuffer_mode)))
+			return -EFAULT;
+		vcd_property_hdr.prop_id = VCD_I_META_BUFFER_MODE;
+		vcd_property_hdr.sz =
+			sizeof(struct vcd_property_live);
+		live_mode.live = metabuffer_mode;
+		vcd_status = vcd_set_property(client_ctx->vcd_handle,
+					&vcd_property_hdr, &live_mode);
+		if (vcd_status) {
+			pr_err(" Setting metabuffer mode failed");
+			return -EIO;
+		}
+		break;
+	}
+	case VEN_IOCTL_SET_EXTRADATA:
+	case VEN_IOCTL_GET_EXTRADATA:
+	{
+		u32 extradata_flag;
+		DBG("VEN_IOCTL_(G)SET_EXTRADATA\n");
+		if (copy_from_user(&venc_msg, arg, sizeof(venc_msg)))
+			return -EFAULT;
+		if (cmd == VEN_IOCTL_SET_EXTRADATA) {
+			if (copy_from_user(&extradata_flag, venc_msg.in,
+					sizeof(u32)))
+				return -EFAULT;
+			result = vid_enc_set_get_extradata(client_ctx,
+					&extradata_flag, true);
+		} else {
+			result = vid_enc_set_get_extradata(client_ctx,
+					&extradata_flag, false);
+			if (result) {
+				if (copy_to_user(venc_msg.out, &extradata_flag,
+						sizeof(u32)))
+					return -EFAULT;
+			}
+		}
+		if (!result) {
+			ERR("setting VEN_IOCTL_(G)SET_LIVE_MODE failed\n");
+		}
+		break;
+	}
+	case VEN_IOCTL_SET_SLICE_DELIVERY_MODE:
+	{
+		struct vcd_property_hdr vcd_property_hdr;
+		u32 vcd_status = VCD_ERR_FAIL;
+		u32 enable = true;
+		vcd_property_hdr.prop_id = VCD_I_SLICE_DELIVERY_MODE;
+		vcd_property_hdr.sz = sizeof(u32);
+		vcd_status = vcd_set_property(client_ctx->vcd_handle,
+						&vcd_property_hdr, &enable);
+		if (vcd_status) {
+			pr_err(" Setting slice delivery mode failed");
+			return -EIO;
+		}
+		break;
+	}
+	case VEN_IOCTL_SET_AC_PREDICTION:
+	case VEN_IOCTL_GET_AC_PREDICTION:
+	case VEN_IOCTL_SET_RVLC:
+	case VEN_IOCTL_GET_RVLC:
+	case VEN_IOCTL_SET_ROTATION:
+	case VEN_IOCTL_GET_ROTATION:
+	case VEN_IOCTL_SET_DATA_PARTITION:
+	case VEN_IOCTL_GET_DATA_PARTITION:
+	case VEN_IOCTL_GET_CAPABILITY:
+	default:
+		ERR("%s(): Unsupported ioctl %d\n", __func__, cmd);
+		return -ENOTTY;
+
+		break;
+	}
+	return 0;
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Video encoder driver");
+MODULE_VERSION("1.0");
+
+module_init(vid_enc_init);
+module_exit(vid_enc_exit);
diff --git a/drivers/video/msm/vidc/common/enc/venc_internal.c b/drivers/video/msm/vidc/common/enc/venc_internal.c
new file mode 100644
index 0000000..bbbe0cf
--- /dev/null
+++ b/drivers/video/msm/vidc/common/enc/venc_internal.c
@@ -0,0 +1,1990 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <linux/android_pmem.h>
+#include <linux/clk.h>
+#include <mach/msm_subsystem_map.h>
+#include <media/msm/vidc_type.h>
+#include <media/msm/vcd_api.h>
+#include <media/msm/vidc_init.h>
+#include "vcd_res_tracker_api.h"
+#include "venc_internal.h"
+
+#if DEBUG
+#define DBG(x...) printk(KERN_DEBUG x)
+#else
+#define DBG(x...)
+#endif
+
+#define ERR(x...) printk(KERN_ERR x)
+static unsigned int vidc_mmu_subsystem[] = {
+	MSM_SUBSYSTEM_VIDEO};
+
+
+u32 vid_enc_set_get_base_cfg(struct video_client_ctx *client_ctx,
+		struct venc_basecfg *base_config, u32 set_flag)
+{
+	struct venc_targetbitrate venc_bitrate;
+	struct venc_framerate frame_rate;
+	u32 current_codec;
+
+	if (!client_ctx || !base_config)
+		return false;
+
+	if (!vid_enc_set_get_codec(client_ctx, &current_codec, false))
+			return false;
+
+	DBG("%s(): Current Codec Type = %u\n", __func__, current_codec);
+	if (current_codec != base_config->codectype) {
+		if (!vid_enc_set_get_codec(client_ctx,
+				(u32 *)&base_config->codectype, set_flag))
+			return false;
+	}
+
+	if (!vid_enc_set_get_inputformat(client_ctx,
+			(u32 *)&base_config->inputformat, set_flag))
+		return false;
+
+	if (!vid_enc_set_get_framesize(client_ctx,
+			(u32 *)&base_config->input_height,
+			(u32 *)&base_config->input_width, set_flag))
+		return false;
+
+	if (set_flag)
+		venc_bitrate.target_bitrate = base_config->targetbitrate;
+
+	if (!vid_enc_set_get_bitrate(client_ctx, &venc_bitrate, set_flag))
+		return false;
+
+	if (!set_flag)
+		base_config->targetbitrate = venc_bitrate.target_bitrate;
+
+	if (set_flag) {
+		frame_rate.fps_denominator = base_config->fps_den;
+		frame_rate.fps_numerator = base_config->fps_num;
+	}
+
+	if (!vid_enc_set_get_framerate(client_ctx, &frame_rate, set_flag))
+		return false;
+
+	if (!set_flag) {
+		base_config->fps_den = frame_rate.fps_denominator;
+		base_config->fps_num = frame_rate.fps_numerator;
+	}
+
+	return true;
+}
+
+u32 vid_enc_set_get_inputformat(struct video_client_ctx *client_ctx,
+		u32 *input_format, u32 set_flag)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_buffer_format format;
+	u32 vcd_status = VCD_ERR_FAIL;
+	u32 status = true;
+
+	if (!client_ctx || !input_format)
+		return false;
+
+	vcd_property_hdr.prop_id = VCD_I_BUFFER_FORMAT;
+	vcd_property_hdr.sz =
+		sizeof(struct vcd_property_buffer_format);
+
+	if (set_flag) {
+		switch (*input_format) {
+		case VEN_INPUTFMT_NV12:
+			format.buffer_format = VCD_BUFFER_FORMAT_NV12;
+			break;
+		case VEN_INPUTFMT_NV12_16M2KA:
+			format.buffer_format =
+				VCD_BUFFER_FORMAT_NV12_16M2KA;
+			break;
+		default:
+			status = false;
+			break;
+		}
+
+		if (status) {
+			vcd_status = vcd_set_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &format);
+			if (vcd_status) {
+				status = false;
+				ERR("%s(): Set VCD_I_BUFFER_FORMAT Failed\n",
+						 __func__);
+			}
+		}
+	} else {
+		vcd_status = vcd_get_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &format);
+
+		if (vcd_status) {
+			status = false;
+			ERR("%s(): Get VCD_I_BUFFER_FORMAT Failed\n", __func__);
+		} else {
+			switch (format.buffer_format) {
+			case VCD_BUFFER_FORMAT_NV12:
+				*input_format = VEN_INPUTFMT_NV12;
+				break;
+			case VCD_BUFFER_FORMAT_TILE_4x2:
+				*input_format = VEN_INPUTFMT_NV21;
+				break;
+			default:
+				status = false;
+				break;
+			}
+		}
+	}
+	return status;
+}
+
+u32 vid_enc_set_get_codec(struct video_client_ctx *client_ctx, u32 *codec,
+		u32 set_flag)
+{
+	struct vcd_property_codec vcd_property_codec;
+	struct vcd_property_hdr vcd_property_hdr;
+	u32 vcd_status = VCD_ERR_FAIL;
+	u32 status = true;
+
+	if (!client_ctx || !codec)
+		return false;
+
+	vcd_property_hdr.prop_id = VCD_I_CODEC;
+	vcd_property_hdr.sz = sizeof(struct vcd_property_codec);
+
+	if (set_flag) {
+		switch (*codec) {
+		case VEN_CODEC_MPEG4:
+			vcd_property_codec.codec = VCD_CODEC_MPEG4;
+			break;
+		case VEN_CODEC_H263:
+			vcd_property_codec.codec = VCD_CODEC_H263;
+			break;
+		case VEN_CODEC_H264:
+			vcd_property_codec.codec = VCD_CODEC_H264;
+			break;
+		default:
+			status = false;
+			break;
+		}
+
+		if (status) {
+			vcd_status = vcd_set_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &vcd_property_codec);
+			if (vcd_status) {
+				status = false;
+				ERR("%s(): Set VCD_I_CODEC Failed\n", __func__);
+			}
+		}
+	}	else {
+		vcd_status = vcd_get_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &vcd_property_codec);
+
+		if (vcd_status) {
+			status = false;
+			ERR("%s(): Get VCD_I_CODEC Failed\n",
+					 __func__);
+		} else {
+			switch (vcd_property_codec.codec) {
+			case VCD_CODEC_H263:
+				*codec = VEN_CODEC_H263;
+				break;
+			case VCD_CODEC_H264:
+				*codec = VEN_CODEC_H264;
+				break;
+			case VCD_CODEC_MPEG4:
+				*codec = VEN_CODEC_MPEG4;
+				break;
+			case VCD_CODEC_DIVX_3:
+			case VCD_CODEC_DIVX_4:
+			case VCD_CODEC_DIVX_5:
+			case VCD_CODEC_DIVX_6:
+			case VCD_CODEC_MPEG1:
+			case VCD_CODEC_MPEG2:
+			case VCD_CODEC_VC1:
+			case VCD_CODEC_VC1_RCV:
+			case VCD_CODEC_XVID:
+			default:
+				status = false;
+				break;
+			}
+		}
+	}
+	return status;
+}
+
+u32 vid_enc_set_get_framesize(struct video_client_ctx *client_ctx,
+		u32 *height, u32 *width, u32 set_flag)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_frame_size frame_size;
+	u32 vcd_status = VCD_ERR_FAIL;
+
+	if (!client_ctx || !height || !width)
+		return false;
+
+	vcd_property_hdr.prop_id = VCD_I_FRAME_SIZE;
+	vcd_property_hdr.sz =
+		sizeof(struct vcd_property_frame_size);
+
+	vcd_status = vcd_get_property(client_ctx->vcd_handle,
+					&vcd_property_hdr, &frame_size);
+
+	if (vcd_status) {
+		ERR("%s(): Get VCD_I_FRAME_SIZE Failed\n",
+				__func__);
+		return false;
+	}
+	if (set_flag) {
+		if (frame_size.height != *height ||
+			frame_size.width != *width) {
+			DBG("%s(): ENC Set Size (%d x %d)\n",
+				__func__, *height, *width);
+			frame_size.height = *height;
+			frame_size.width = *width;
+			vcd_status = vcd_set_property(client_ctx->vcd_handle,
+					&vcd_property_hdr, &frame_size);
+			if (vcd_status) {
+				ERR("%s(): Set VCD_I_FRAME_SIZE Failed\n",
+						__func__);
+				return false;
+			}
+		}
+	} else {
+		*height = frame_size.height;
+		*width = frame_size.width;
+	}
+	return true;
+}
+
+u32 vid_enc_set_get_bitrate(struct video_client_ctx *client_ctx,
+		struct venc_targetbitrate *venc_bitrate, u32 set_flag)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_target_bitrate bit_rate;
+	u32 vcd_status = VCD_ERR_FAIL;
+
+	if (!client_ctx || !venc_bitrate)
+		return false;
+
+	vcd_property_hdr.prop_id = VCD_I_TARGET_BITRATE;
+	vcd_property_hdr.sz =
+		sizeof(struct vcd_property_target_bitrate);
+	if (set_flag) {
+		bit_rate.target_bitrate = venc_bitrate->target_bitrate;
+		vcd_status = vcd_set_property(client_ctx->vcd_handle,
+					&vcd_property_hdr, &bit_rate);
+
+		if (vcd_status) {
+			ERR("%s(): Set VCD_I_TARGET_BITRATE Failed\n",
+					__func__);
+			return false;
+		}
+	} else {
+		vcd_status = vcd_get_property(client_ctx->vcd_handle,
+					&vcd_property_hdr, &bit_rate);
+
+		if (vcd_status) {
+			ERR("%s(): Get VCD_I_TARGET_BITRATE Failed\n",
+					__func__);
+			return false;
+		}
+		venc_bitrate->target_bitrate = bit_rate.target_bitrate;
+	}
+	return true;
+}
+
+u32 vid_enc_set_get_extradata(struct video_client_ctx *client_ctx,
+		u32 *extradata_flag, u32 set_flag)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_meta_data_enable vcd_meta_data;
+	u32 vcd_status = VCD_ERR_FAIL;
+	if (!client_ctx || !extradata_flag)
+		return false;
+	vcd_property_hdr.prop_id = VCD_I_METADATA_ENABLE;
+	vcd_property_hdr.sz = sizeof(struct vcd_property_meta_data_enable);
+	if (set_flag) {
+		DBG("vcd_set_property: VCD_I_METADATA_ENABLE = %d\n",
+				*extradata_flag);
+		vcd_meta_data.meta_data_enable_flag = *extradata_flag;
+		vcd_status = vcd_set_property(client_ctx->vcd_handle,
+					&vcd_property_hdr, &vcd_meta_data);
+		if (vcd_status) {
+			ERR("%s(): Set VCD_I_METADATA_ENABLE Failed\n",
+				__func__);
+			return false;
+		}
+	} else {
+		vcd_status = vcd_get_property(client_ctx->vcd_handle,
+					&vcd_property_hdr, &vcd_meta_data);
+		if (vcd_status) {
+			ERR("%s(): Get VCD_I_METADATA_ENABLE Failed\n",
+				__func__);
+			return false;
+		}
+		*extradata_flag = vcd_meta_data.meta_data_enable_flag;
+		DBG("vcd_get_property: VCD_I_METADATA_ENABLE = %d\n",
+				*extradata_flag);
+	}
+	return true;
+}
+
+u32 vid_enc_set_get_framerate(struct video_client_ctx *client_ctx,
+		struct venc_framerate *frame_rate, u32 set_flag)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_frame_rate vcd_frame_rate;
+	u32 vcd_status = VCD_ERR_FAIL;
+
+	if (!client_ctx || !frame_rate)
+		return false;
+
+	vcd_property_hdr.prop_id = VCD_I_FRAME_RATE;
+	vcd_property_hdr.sz =
+				sizeof(struct vcd_property_frame_rate);
+
+	if (set_flag) {
+		vcd_frame_rate.fps_denominator = frame_rate->fps_denominator;
+		vcd_frame_rate.fps_numerator = frame_rate->fps_numerator;
+		vcd_status = vcd_set_property(client_ctx->vcd_handle,
+					&vcd_property_hdr, &vcd_frame_rate);
+
+		if (vcd_status) {
+			ERR("%s(): Set VCD_I_FRAME_RATE Failed\n",
+					__func__);
+			return false;
+		}
+	} else {
+		vcd_status = vcd_get_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &vcd_frame_rate);
+
+		if (vcd_status) {
+			ERR("%s(): Get VCD_I_FRAME_RATE Failed\n",
+					__func__);
+			return false;
+		}
+		frame_rate->fps_denominator = vcd_frame_rate.fps_denominator;
+		frame_rate->fps_numerator = vcd_frame_rate.fps_numerator;
+	}
+	return true;
+}
+
+u32 vid_enc_set_get_live_mode(struct video_client_ctx *client_ctx,
+		struct venc_switch *encoder_switch, u32 set_flag)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_live live_mode;
+	u32 vcd_status = VCD_ERR_FAIL;
+
+	if (!client_ctx)
+		return false;
+
+	vcd_property_hdr.prop_id = VCD_I_LIVE;
+	vcd_property_hdr.sz =
+				sizeof(struct vcd_property_live);
+
+	if (set_flag) {
+		live_mode.live = 1;
+		if (!encoder_switch->status)
+			live_mode.live = 0;
+
+		vcd_status = vcd_set_property(client_ctx->vcd_handle,
+					&vcd_property_hdr, &live_mode);
+		if (vcd_status) {
+			ERR("%s(): Set VCD_I_LIVE Failed\n",
+					__func__);
+			return false;
+		}
+	} else {
+		vcd_status = vcd_get_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &live_mode);
+
+		if (vcd_status) {
+			ERR("%s(): Get VCD_I_LIVE Failed\n",
+					__func__);
+			return false;
+		}	else {
+			encoder_switch->status = 1;
+			if (!live_mode.live)
+				encoder_switch->status = 0;
+		}
+	}
+	return true;
+}
+
+u32 vid_enc_set_get_short_header(struct video_client_ctx *client_ctx,
+		struct venc_switch *encoder_switch,	u32 set_flag)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_short_header short_header;
+	u32 vcd_status = VCD_ERR_FAIL;
+
+	if (!client_ctx || !encoder_switch)
+		return false;
+
+	vcd_property_hdr.prop_id = VCD_I_SHORT_HEADER;
+	vcd_property_hdr.sz =
+				sizeof(struct vcd_property_short_header);
+
+	if (set_flag) {
+		short_header.short_header = (u32) encoder_switch->status;
+		vcd_status = vcd_set_property(client_ctx->vcd_handle,
+			&vcd_property_hdr, &short_header);
+
+		if (vcd_status) {
+			ERR("%s(): Set VCD_I_SHORT_HEADER Failed\n",
+					__func__);
+			return false;
+		}
+	} else {
+		vcd_status = vcd_get_property(client_ctx->vcd_handle,
+					&vcd_property_hdr, &short_header);
+
+		if (vcd_status) {
+			ERR("%s(): Get VCD_I_SHORT_HEADER Failed\n",
+					__func__);
+			return false;
+		}	else {
+			encoder_switch->status =
+				(u8) short_header.short_header;
+		}
+	}
+	return true;
+}
+
+u32 vid_enc_set_get_profile(struct video_client_ctx *client_ctx,
+		struct venc_profile *profile, u32 set_flag)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_profile profile_type;
+	u32 vcd_status = VCD_ERR_FAIL;
+	u32 status = true;
+
+	if (!client_ctx || !profile)
+		return false;
+
+	vcd_property_hdr.prop_id = VCD_I_PROFILE;
+	vcd_property_hdr.sz =
+		sizeof(struct vcd_property_profile);
+
+	if (set_flag) {
+		switch (profile->profile) {
+		case VEN_PROFILE_MPEG4_SP:
+			profile_type.profile = VCD_PROFILE_MPEG4_SP;
+			break;
+		case VEN_PROFILE_MPEG4_ASP:
+			profile_type.profile = VCD_PROFILE_MPEG4_ASP;
+			break;
+		case VEN_PROFILE_H264_BASELINE:
+			profile_type.profile = VCD_PROFILE_H264_BASELINE;
+			break;
+		case VEN_PROFILE_H264_MAIN:
+			profile_type.profile = VCD_PROFILE_H264_MAIN;
+			break;
+		case VEN_PROFILE_H264_HIGH:
+			profile_type.profile = VCD_PROFILE_H264_HIGH;
+			break;
+		case VEN_PROFILE_H263_BASELINE:
+			profile_type.profile = VCD_PROFILE_H263_BASELINE;
+			break;
+		default:
+			status = false;
+			break;
+		}
+
+		if (status) {
+			vcd_status = vcd_set_property(client_ctx->vcd_handle,
+			&vcd_property_hdr, &profile_type);
+
+			if (vcd_status) {
+				ERR("%s(): Set VCD_I_PROFILE Failed\n",
+						__func__);
+				return false;
+			}
+		}
+	}	else {
+		vcd_status = vcd_get_property(client_ctx->vcd_handle,
+					&vcd_property_hdr, &profile_type);
+
+		if (vcd_status) {
+			ERR("%s(): Get VCD_I_PROFILE Failed\n",
+					__func__);
+			return false;
+		} else {
+			switch (profile_type.profile) {
+			case VCD_PROFILE_H263_BASELINE:
+				profile->profile = VEN_PROFILE_H263_BASELINE;
+				break;
+			case VCD_PROFILE_H264_BASELINE:
+				profile->profile = VEN_PROFILE_H264_BASELINE;
+				break;
+			case VCD_PROFILE_H264_HIGH:
+				profile->profile = VEN_PROFILE_H264_HIGH;
+				break;
+			case VCD_PROFILE_H264_MAIN:
+				profile->profile = VEN_PROFILE_H264_MAIN;
+				break;
+			case VCD_PROFILE_MPEG4_ASP:
+				profile->profile = VEN_PROFILE_MPEG4_ASP;
+				break;
+			case VCD_PROFILE_MPEG4_SP:
+				profile->profile = VEN_PROFILE_MPEG4_SP;
+				break;
+			default:
+				status = false;
+				break;
+			}
+		}
+	}
+	return status;
+}
+
+u32 vid_enc_set_get_profile_level(struct video_client_ctx *client_ctx,
+		struct ven_profilelevel *profile_level,	u32 set_flag)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_level level;
+	u32 vcd_status = VCD_ERR_FAIL;
+	u32 status = true;
+
+	if (!client_ctx || !profile_level)
+		return false;
+
+	vcd_property_hdr.prop_id = VCD_I_LEVEL;
+	vcd_property_hdr.sz =
+			sizeof(struct vcd_property_level);
+
+	if (set_flag) {
+		switch (profile_level->level) {
+		case VEN_LEVEL_MPEG4_0:
+			level.level = VCD_LEVEL_MPEG4_0;
+			break;
+		case VEN_LEVEL_MPEG4_1:
+			level.level = VCD_LEVEL_MPEG4_1;
+			break;
+		case VEN_LEVEL_MPEG4_2:
+			level.level = VCD_LEVEL_MPEG4_2;
+			break;
+		case VEN_LEVEL_MPEG4_3:
+			level.level = VCD_LEVEL_MPEG4_3;
+			break;
+		case VEN_LEVEL_MPEG4_4:
+			level.level = VCD_LEVEL_MPEG4_4;
+			break;
+		case VEN_LEVEL_MPEG4_5:
+			level.level = VCD_LEVEL_MPEG4_5;
+			break;
+		case VEN_LEVEL_MPEG4_3b:
+			level.level = VCD_LEVEL_MPEG4_3b;
+			break;
+		case VEN_LEVEL_MPEG4_6:
+			level.level = VCD_LEVEL_MPEG4_6;
+			break;
+		case VEN_LEVEL_H264_1:
+			level.level = VCD_LEVEL_H264_1;
+			break;
+		case VEN_LEVEL_H264_1b:
+			level.level = VCD_LEVEL_H264_1b;
+			break;
+		case VEN_LEVEL_H264_1p1:
+			level.level = VCD_LEVEL_H264_1p1;
+			break;
+		case VEN_LEVEL_H264_1p2:
+			level.level = VCD_LEVEL_H264_1p2;
+			break;
+		case VEN_LEVEL_H264_1p3:
+			level.level = VCD_LEVEL_H264_1p3;
+			break;
+		case VEN_LEVEL_H264_2:
+			level.level = VCD_LEVEL_H264_2;
+			break;
+		case VEN_LEVEL_H264_2p1:
+			level.level = VCD_LEVEL_H264_2p1;
+			break;
+		case VEN_LEVEL_H264_2p2:
+			level.level = VCD_LEVEL_H264_2p2;
+			break;
+		case VEN_LEVEL_H264_3:
+			level.level = VCD_LEVEL_H264_3;
+			break;
+		case VEN_LEVEL_H264_3p1:
+			level.level = VCD_LEVEL_H264_3p1;
+			break;
+		case VEN_LEVEL_H264_3p2:
+			level.level = VCD_LEVEL_H264_3p2;
+			break;
+		case VEN_LEVEL_H264_4:
+			level.level = VCD_LEVEL_H264_4;
+			break;
+		case VEN_LEVEL_H263_10:
+			level.level = VCD_LEVEL_H263_10;
+			break;
+		case VEN_LEVEL_H263_20:
+			level.level = VCD_LEVEL_H263_20;
+			break;
+		case VEN_LEVEL_H263_30:
+			level.level = VCD_LEVEL_H263_30;
+			break;
+		case VEN_LEVEL_H263_40:
+			level.level = VCD_LEVEL_H263_40;
+			break;
+		case VEN_LEVEL_H263_45:
+			level.level = VCD_LEVEL_H263_45;
+			break;
+		case VEN_LEVEL_H263_50:
+			level.level = VCD_LEVEL_H263_50;
+			break;
+		case VEN_LEVEL_H263_60:
+			level.level = VCD_LEVEL_H263_60;
+			break;
+		case VEN_LEVEL_H263_70:
+			level.level = VCD_LEVEL_H263_70;
+			break;
+		default:
+			status = false;
+			break;
+		}
+		if (status) {
+			vcd_status = vcd_set_property(client_ctx->vcd_handle,
+						&vcd_property_hdr, &level);
+
+			if (vcd_status) {
+				ERR("%s(): Set VCD_I_LEVEL Failed\n",
+						__func__);
+				return false;
+			}
+		}
+	} else {
+		vcd_status = vcd_get_property(client_ctx->vcd_handle,
+					&vcd_property_hdr, &level);
+
+		if (vcd_status) {
+			ERR("%s(): Get VCD_I_LEVEL Failed\n",
+					__func__);
+			return false;
+		} else {
+			switch (level.level) {
+			case VCD_LEVEL_MPEG4_0:
+				profile_level->level = VEN_LEVEL_MPEG4_0;
+				break;
+			case VCD_LEVEL_MPEG4_1:
+				profile_level->level = VEN_LEVEL_MPEG4_1;
+				break;
+			case VCD_LEVEL_MPEG4_2:
+				profile_level->level = VEN_LEVEL_MPEG4_2;
+				break;
+			case VCD_LEVEL_MPEG4_3:
+				profile_level->level = VEN_LEVEL_MPEG4_3;
+				break;
+			case VCD_LEVEL_MPEG4_4:
+				profile_level->level = VEN_LEVEL_MPEG4_4;
+				break;
+			case VCD_LEVEL_MPEG4_5:
+				profile_level->level = VEN_LEVEL_MPEG4_5;
+				break;
+			case VCD_LEVEL_MPEG4_3b:
+				profile_level->level = VEN_LEVEL_MPEG4_3b;
+				break;
+			case VCD_LEVEL_H264_1:
+				profile_level->level = VEN_LEVEL_H264_1;
+				break;
+			case VCD_LEVEL_H264_1b:
+				profile_level->level = VEN_LEVEL_H264_1b;
+				break;
+			case VCD_LEVEL_H264_1p1:
+				profile_level->level = VEN_LEVEL_H264_1p1;
+				break;
+			case VCD_LEVEL_H264_1p2:
+				profile_level->level = VEN_LEVEL_H264_1p2;
+				break;
+			case VCD_LEVEL_H264_1p3:
+				profile_level->level = VEN_LEVEL_H264_1p3;
+				break;
+			case VCD_LEVEL_H264_2:
+				profile_level->level = VEN_LEVEL_H264_2;
+				break;
+			case VCD_LEVEL_H264_2p1:
+				profile_level->level = VEN_LEVEL_H264_2p1;
+				break;
+			case VCD_LEVEL_H264_2p2:
+				profile_level->level = VEN_LEVEL_H264_2p2;
+				break;
+			case VCD_LEVEL_H264_3:
+				profile_level->level = VEN_LEVEL_H264_3;
+				break;
+			case VCD_LEVEL_H264_3p1:
+				profile_level->level = VEN_LEVEL_H264_3p1;
+				break;
+			case VCD_LEVEL_H264_3p2:
+				profile_level->level = VEN_LEVEL_H264_3p2;
+				break;
+			case VCD_LEVEL_H264_4:
+				profile_level->level = VEN_LEVEL_H264_4;
+				break;
+			case VCD_LEVEL_H263_10:
+				profile_level->level = VEN_LEVEL_H263_10;
+				break;
+			case VCD_LEVEL_H263_20:
+				profile_level->level = VEN_LEVEL_H263_20;
+				break;
+			case VCD_LEVEL_H263_30:
+				profile_level->level = VEN_LEVEL_H263_30;
+				break;
+			case VCD_LEVEL_H263_40:
+				profile_level->level = VEN_LEVEL_H263_40;
+				break;
+			case VCD_LEVEL_H263_45:
+				profile_level->level = VEN_LEVEL_H263_45;
+				break;
+			case VCD_LEVEL_H263_50:
+				profile_level->level = VEN_LEVEL_H263_50;
+				break;
+			case VCD_LEVEL_H263_60:
+				profile_level->level = VEN_LEVEL_H263_60;
+				break;
+			case VCD_LEVEL_H263_70:
+				status = false;
+				break;
+			default:
+				status = false;
+				break;
+			}
+		}
+	}
+	return status;
+}
+
+u32 vid_enc_set_get_session_qp(struct video_client_ctx *client_ctx,
+		struct venc_sessionqp *session_qp, u32 set_flag)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_session_qp qp;
+	u32 vcd_status = VCD_ERR_FAIL;
+
+	if (!client_ctx || !session_qp)
+		return false;
+
+	vcd_property_hdr.prop_id = VCD_I_SESSION_QP;
+	vcd_property_hdr.sz =
+			sizeof(struct vcd_property_session_qp);
+
+	if (set_flag) {
+		qp.i_frame_qp = session_qp->iframeqp;
+		qp.p_frame_qp = session_qp->pframqp;
+
+		vcd_status = vcd_set_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &qp);
+
+		if (vcd_status) {
+			ERR("%s(): Set VCD_I_SESSION_QP Failed\n",
+					__func__);
+			return false;
+		}
+	} else {
+		vcd_status = vcd_get_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &qp);
+
+		if (vcd_status) {
+			ERR("%s(): Set VCD_I_SESSION_QP Failed\n",
+					__func__);
+			return false;
+		} else {
+			session_qp->iframeqp = qp.i_frame_qp;
+			session_qp->pframqp = qp.p_frame_qp;
+		}
+	}
+	return true;
+}
+
+u32 vid_enc_set_get_intraperiod(struct video_client_ctx *client_ctx,
+		struct venc_intraperiod *intraperiod,	u32 set_flag)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_i_period period;
+	u32 vcd_status = VCD_ERR_FAIL;
+
+	if (!client_ctx || !intraperiod)
+		return false;
+
+	vcd_property_hdr.prop_id = VCD_I_INTRA_PERIOD;
+	vcd_property_hdr.sz =
+		sizeof(struct vcd_property_i_period);
+
+	if (set_flag) {
+		period.p_frames = intraperiod->num_pframes;
+		period.b_frames = intraperiod->num_bframes;
+		vcd_status = vcd_set_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &period);
+
+		if (vcd_status) {
+			ERR("%s(): Set VCD_I_INTRA_PERIOD Failed\n",
+					__func__);
+			return false;
+		}
+	} else {
+		vcd_status = vcd_get_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &period);
+
+		if (vcd_status) {
+			ERR("%s(): Get VCD_I_INTRA_PERIOD Failed\n",
+					__func__);
+			return false;
+		} else
+			intraperiod->num_pframes = period.p_frames;
+	}
+	return true;
+}
+
+u32 vid_enc_request_iframe(struct video_client_ctx *client_ctx)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_req_i_frame request;
+	u32 vcd_status = VCD_ERR_FAIL;
+	u32 status = true;
+
+	if (!client_ctx)
+		return false;
+
+	vcd_property_hdr.prop_id = VCD_I_REQ_IFRAME;
+	vcd_property_hdr.sz =
+				sizeof(struct vcd_property_req_i_frame);
+	request.req_i_frame = 1;
+
+	vcd_status = vcd_set_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &request);
+
+	if (vcd_status) {
+		ERR("%s(): Set VCD_I_REQ_IFRAME Failed\n",
+				__func__);
+		return false;
+	}
+	return status;
+}
+
+u32 vid_enc_get_sequence_header(struct video_client_ctx *client_ctx,
+		struct venc_seqheader	*seq_header)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_sequence_hdr hdr;
+	u32 vcd_status = VCD_ERR_FAIL;
+	u32 status = true;
+
+	if (!client_ctx || !seq_header || !seq_header->bufsize)
+		return false;
+
+	vcd_property_hdr.prop_id = VCD_I_SEQ_HEADER;
+	vcd_property_hdr.sz = sizeof(struct vcd_sequence_hdr);
+
+	hdr.sequence_header = seq_header->hdrbufptr;
+	hdr.sequence_header_len = seq_header->bufsize;
+	vcd_status = vcd_get_property(client_ctx->vcd_handle,
+			&vcd_property_hdr, &hdr);
+	seq_header->hdrlen = hdr.sequence_header_len;
+
+	if (vcd_status) {
+		ERR("%s(): Get VCD_I_SEQ_HEADER Failed\n",
+				__func__);
+		status = false;
+	}
+	return true;
+}
+
+u32 vid_enc_set_get_entropy_cfg(struct video_client_ctx *client_ctx,
+		struct venc_entropycfg *entropy_cfg, u32 set_flag)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_entropy_control control;
+	u32 vcd_status = VCD_ERR_FAIL;
+	u32 status = true;
+
+	if (!client_ctx || !entropy_cfg)
+		return false;
+
+	vcd_property_hdr.prop_id = VCD_I_ENTROPY_CTRL;
+	vcd_property_hdr.sz =
+		sizeof(struct vcd_property_entropy_control);
+	if (set_flag) {
+		switch (entropy_cfg->longentropysel) {
+		case VEN_ENTROPY_MODEL_CAVLC:
+			control.entropy_sel = VCD_ENTROPY_SEL_CAVLC;
+			break;
+		case VEN_ENTROPY_MODEL_CABAC:
+			control.entropy_sel = VCD_ENTROPY_SEL_CABAC;
+			break;
+		default:
+			status = false;
+			break;
+		}
+
+		if (status && entropy_cfg->cabacmodel ==
+				VCD_ENTROPY_SEL_CABAC) {
+			switch (entropy_cfg->cabacmodel) {
+			case VEN_CABAC_MODEL_0:
+				control.cabac_model =
+					VCD_CABAC_MODEL_NUMBER_0;
+				break;
+			case VEN_CABAC_MODEL_1:
+				control.cabac_model =
+					VCD_CABAC_MODEL_NUMBER_1;
+				break;
+			case VEN_CABAC_MODEL_2:
+				control.cabac_model =
+					VCD_CABAC_MODEL_NUMBER_2;
+				break;
+			default:
+				status = false;
+				break;
+			}
+		}
+		if (status) {
+			vcd_status = vcd_set_property(client_ctx->vcd_handle,
+					&vcd_property_hdr, &control);
+
+			if (vcd_status) {
+				ERR("%s(): Set VCD_I_ENTROPY_CTRL Failed\n",
+						__func__);
+				status = false;
+			}
+		}
+	} else {
+		vcd_status = vcd_get_property(client_ctx->vcd_handle,
+					&vcd_property_hdr, &control);
+
+		if (vcd_status) {
+			ERR("%s(): Get VCD_I_ENTROPY_CTRL Failed\n",
+					__func__);
+			status = false;
+		} else {
+			switch (control.entropy_sel) {
+			case VCD_ENTROPY_SEL_CABAC:
+				entropy_cfg->cabacmodel =
+					VEN_ENTROPY_MODEL_CABAC;
+				break;
+			case VCD_ENTROPY_SEL_CAVLC:
+				entropy_cfg->cabacmodel =
+					VEN_ENTROPY_MODEL_CAVLC;
+				break;
+			default:
+				status = false;
+				break;
+			}
+
+			if (status && control.entropy_sel ==
+					VCD_ENTROPY_SEL_CABAC) {
+				switch (control.cabac_model) {
+				case VCD_CABAC_MODEL_NUMBER_0:
+					entropy_cfg->cabacmodel =
+						VEN_CABAC_MODEL_0;
+					break;
+				case VCD_CABAC_MODEL_NUMBER_1:
+					entropy_cfg->cabacmodel =
+						VEN_CABAC_MODEL_1;
+					break;
+				case VCD_CABAC_MODEL_NUMBER_2:
+					entropy_cfg->cabacmodel =
+						VEN_CABAC_MODEL_2;
+					break;
+				default:
+					status = false;
+					break;
+				}
+			}
+		}
+	}
+	return status;
+}
+
+u32 vid_enc_set_get_dbcfg(struct video_client_ctx *client_ctx,
+		struct venc_dbcfg *dbcfg, u32 set_flag)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_db_config control;
+	u32 vcd_status = VCD_ERR_FAIL;
+	u32 status = true;
+
+	if (!client_ctx || !dbcfg)
+		return false;
+
+	vcd_property_hdr.prop_id = VCD_I_DEBLOCKING;
+	vcd_property_hdr.sz =
+		sizeof(struct vcd_property_db_config);
+
+	if (set_flag) {
+		switch (dbcfg->db_mode) {
+		case VEN_DB_DISABLE:
+			control.db_config = VCD_DB_DISABLE;
+			break;
+		case VEN_DB_ALL_BLKG_BNDRY:
+			control.db_config = VCD_DB_ALL_BLOCKING_BOUNDARY;
+			break;
+		case VEN_DB_SKIP_SLICE_BNDRY:
+			control.db_config = VCD_DB_SKIP_SLICE_BOUNDARY;
+			break;
+		default:
+			status = false;
+			break;
+		}
+
+		if (status) {
+			control.slice_alpha_offset =
+				dbcfg->slicealpha_offset;
+			control.slice_beta_offset =
+				dbcfg->slicebeta_offset;
+			vcd_status = vcd_set_property(client_ctx->vcd_handle,
+			&vcd_property_hdr, &control);
+			if (vcd_status) {
+				ERR("%s(): Set VCD_I_DEBLOCKING Failed\n",
+						__func__);
+				status = false;
+			}
+		}
+	} else {
+		vcd_status = vcd_get_property(client_ctx->vcd_handle,
+					&vcd_property_hdr, &control);
+		if (vcd_status) {
+			ERR("%s(): Get VCD_I_DEBLOCKING Failed\n",
+					__func__);
+			status = false;
+		} else {
+			switch (control.db_config) {
+			case VCD_DB_ALL_BLOCKING_BOUNDARY:
+				dbcfg->db_mode = VEN_DB_ALL_BLKG_BNDRY;
+				break;
+			case VCD_DB_DISABLE:
+				dbcfg->db_mode = VEN_DB_DISABLE;
+				break;
+			case VCD_DB_SKIP_SLICE_BOUNDARY:
+				dbcfg->db_mode = VEN_DB_SKIP_SLICE_BNDRY;
+				break;
+			default:
+				status = false;
+				break;
+			}
+			dbcfg->slicealpha_offset =
+				control.slice_alpha_offset;
+			dbcfg->slicebeta_offset =
+				control.slice_beta_offset;
+		}
+	}
+	return status;
+}
+
+u32 vid_enc_set_get_intrarefresh(struct video_client_ctx *client_ctx,
+		struct venc_intrarefresh *intrarefresh, u32 set_flag)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_intra_refresh_mb_number control;
+	u32 vcd_status = VCD_ERR_FAIL;
+
+	if (!client_ctx || !intrarefresh)
+		return false;
+
+	vcd_property_hdr.prop_id = VCD_I_INTRA_REFRESH;
+	vcd_property_hdr.sz =
+		sizeof(struct vcd_property_intra_refresh_mb_number);
+
+	if (set_flag) {
+		control.cir_mb_number = intrarefresh->mbcount;
+		vcd_status = vcd_set_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &control);
+
+		if (vcd_status) {
+			ERR("%s(): Set VCD_I_INTRA_REFRESH Failed\n",
+					__func__);
+			return false;
+		}
+	} else {
+		vcd_status = vcd_get_property(client_ctx->vcd_handle,
+					&vcd_property_hdr, &control);
+
+		if (vcd_status) {
+			ERR("%s(): Set VCD_I_INTRA_REFRESH Failed\n",
+					__func__);
+			return false;
+		} else
+			intrarefresh->mbcount = control.cir_mb_number;
+	}
+	return true;
+}
+
+u32 vid_enc_set_get_multiclicecfg(struct video_client_ctx *client_ctx,
+		struct venc_multiclicecfg *multiclicecfg,	u32 set_flag)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_multi_slice control;
+	u32 vcd_status = VCD_ERR_FAIL;
+	u32 status = true;
+
+	if (!client_ctx || !multiclicecfg)
+		return false;
+
+	vcd_property_hdr.prop_id = VCD_I_MULTI_SLICE;
+	vcd_property_hdr.sz =
+		sizeof(struct vcd_property_multi_slice);
+
+	if (set_flag) {
+		switch (multiclicecfg->mslice_mode) {
+		case VEN_MSLICE_OFF:
+			control.m_slice_sel =
+				VCD_MSLICE_OFF;
+			break;
+		case VEN_MSLICE_CNT_MB:
+			control.m_slice_sel =
+				VCD_MSLICE_BY_MB_COUNT;
+			break;
+		case VEN_MSLICE_CNT_BYTE:
+			control.m_slice_sel =
+				VCD_MSLICE_BY_BYTE_COUNT;
+			break;
+		case VEN_MSLICE_GOB:
+			control.m_slice_sel =
+				VCD_MSLICE_BY_GOB;
+			break;
+		default:
+			status = false;
+			break;
+		}
+
+		if (status) {
+			control.m_slice_size =
+				multiclicecfg->mslice_size;
+			vcd_status = vcd_set_property(client_ctx->vcd_handle,
+			&vcd_property_hdr, &control);
+
+			if (vcd_status) {
+				ERR("%s(): Set VCD_I_MULTI_SLICE Failed\n",
+						__func__);
+				status = false;
+			}
+		}
+	} else {
+		vcd_status = vcd_get_property(client_ctx->vcd_handle,
+			&vcd_property_hdr, &control);
+
+		if (vcd_status) {
+			ERR("%s(): Get VCD_I_MULTI_SLICE Failed\n",
+					__func__);
+			status = false;
+		} else {
+			multiclicecfg->mslice_size =
+				control.m_slice_size;
+			switch (control.m_slice_sel) {
+			case VCD_MSLICE_OFF:
+				multiclicecfg->mslice_mode = VEN_MSLICE_OFF;
+				break;
+			case VCD_MSLICE_BY_MB_COUNT:
+				multiclicecfg->mslice_mode = VEN_MSLICE_CNT_MB;
+				break;
+			case VCD_MSLICE_BY_BYTE_COUNT:
+				multiclicecfg->mslice_mode =
+					VEN_MSLICE_CNT_BYTE;
+				break;
+			case VCD_MSLICE_BY_GOB:
+				multiclicecfg->mslice_mode =
+					VEN_MSLICE_GOB;
+				break;
+			default:
+				status = false;
+				break;
+			}
+		}
+	}
+	return status;
+}
+
+u32 vid_enc_set_get_ratectrlcfg(struct video_client_ctx *client_ctx,
+		struct venc_ratectrlcfg *ratectrlcfg,	u32 set_flag)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_rate_control control;
+	u32 vcd_status = VCD_ERR_FAIL;
+	u32 status = true;
+
+	if (!client_ctx || !ratectrlcfg)
+		return false;
+
+	vcd_property_hdr.prop_id = VCD_I_RATE_CONTROL;
+	vcd_property_hdr.sz =
+		sizeof(struct vcd_property_rate_control);
+
+	if (set_flag) {
+		switch (ratectrlcfg->rcmode) {
+		case VEN_RC_OFF:
+			control.rate_control = VCD_RATE_CONTROL_OFF;
+			break;
+		case VEN_RC_CBR_VFR:
+			control.rate_control = VCD_RATE_CONTROL_CBR_VFR;
+			break;
+		case VEN_RC_VBR_CFR:
+			control.rate_control = VCD_RATE_CONTROL_VBR_CFR;
+			break;
+		case VEN_RC_VBR_VFR:
+			control.rate_control = VCD_RATE_CONTROL_VBR_VFR;
+			break;
+		case VEN_RC_CBR_CFR:
+			control.rate_control = VCD_RATE_CONTROL_CBR_CFR;
+			break;
+		default:
+			status = false;
+			break;
+		}
+
+		if (status) {
+			vcd_status = vcd_set_property(client_ctx->vcd_handle,
+			&vcd_property_hdr, &control);
+			if (vcd_status) {
+				ERR("%s(): Set VCD_I_RATE_CONTROL Failed\n",
+						__func__);
+				status = false;
+			}
+		}
+	} else {
+		vcd_status = vcd_get_property(client_ctx->vcd_handle,
+		&vcd_property_hdr, &control);
+
+		if (vcd_status) {
+			ERR("%s(): Get VCD_I_RATE_CONTROL Failed\n",
+					__func__);
+			status = false;
+		} else {
+			switch (control.rate_control) {
+			case VCD_RATE_CONTROL_OFF:
+				ratectrlcfg->rcmode = VEN_RC_OFF;
+				break;
+			case VCD_RATE_CONTROL_CBR_VFR:
+				ratectrlcfg->rcmode = VEN_RC_CBR_VFR;
+				break;
+			case VCD_RATE_CONTROL_VBR_CFR:
+				ratectrlcfg->rcmode = VEN_RC_VBR_CFR;
+				break;
+			case VCD_RATE_CONTROL_VBR_VFR:
+				ratectrlcfg->rcmode = VEN_RC_VBR_VFR;
+				break;
+			case VCD_RATE_CONTROL_CBR_CFR:
+				ratectrlcfg->rcmode = VEN_RC_CBR_CFR;
+				break;
+			default:
+				status = false;
+				break;
+			}
+		}
+	}
+	return status;
+}
+
+u32 vid_enc_set_get_voptimingcfg(struct video_client_ctx *client_ctx,
+		struct	venc_voptimingcfg *voptimingcfg, u32 set_flag)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_vop_timing control;
+	u32 vcd_status = VCD_ERR_FAIL;
+	u32 status = true;
+
+	if (!client_ctx || !voptimingcfg)
+		return false;
+
+	vcd_property_hdr.prop_id = VCD_I_VOP_TIMING;
+	vcd_property_hdr.sz =
+		sizeof(struct vcd_property_vop_timing);
+
+	if (set_flag) {
+		control.vop_time_resolution =
+		voptimingcfg->voptime_resolution;
+		vcd_status = vcd_set_property(client_ctx->vcd_handle,
+		&vcd_property_hdr, &control);
+
+		if (vcd_status) {
+			ERR("%s(): Set VCD_I_VOP_TIMING Failed\n",
+					__func__);
+			status = false;
+		}
+	} else {
+		vcd_status = vcd_get_property(client_ctx->vcd_handle,
+		&vcd_property_hdr, &control);
+		if (vcd_status) {
+			ERR("%s(): Get VCD_I_VOP_TIMING Failed\n",
+					__func__);
+			status = false;
+		} else
+			voptimingcfg->voptime_resolution =
+			control.vop_time_resolution;
+	}
+	return status;
+}
+
+u32 vid_enc_set_get_headerextension(struct video_client_ctx *client_ctx,
+		struct venc_headerextension *headerextension, u32 set_flag)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	u32 control;
+	u32 vcd_status = VCD_ERR_FAIL;
+	u32 status = true;
+
+	if (!client_ctx || !headerextension)
+		return false;
+
+	vcd_property_hdr.prop_id = VCD_I_HEADER_EXTENSION;
+	vcd_property_hdr.sz = sizeof(u32);
+
+	if (set_flag) {
+		control = headerextension->header_extension;
+		vcd_status = vcd_set_property(client_ctx->vcd_handle,
+		&vcd_property_hdr, &control);
+		if (vcd_status) {
+			ERR("%s(): Set VCD_I_HEADER_EXTENSION Failed\n",
+					__func__);
+			status = false;
+		}
+	} else {
+		vcd_status = vcd_get_property(client_ctx->vcd_handle,
+		&vcd_property_hdr, &control);
+		if (vcd_status) {
+			ERR("%s(): Get VCD_I_HEADER_EXTENSION Failed\n",
+					__func__);
+			status = false;
+		} else {
+			headerextension->header_extension = control;
+		}
+	}
+	return status;
+}
+
+u32 vid_enc_set_get_qprange(struct video_client_ctx *client_ctx,
+		struct venc_qprange *qprange, u32 set_flag)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_qp_range control;
+	u32 vcd_status = VCD_ERR_FAIL;
+	u32 status = true;
+
+	if (!client_ctx || !qprange)
+		return false;
+
+	vcd_property_hdr.prop_id = VCD_I_QP_RANGE;
+	vcd_property_hdr.sz =
+		sizeof(struct vcd_property_qp_range);
+
+	if (set_flag) {
+		control.max_qp = qprange->maxqp;
+		control.min_qp = qprange->minqp;
+		vcd_status = vcd_set_property(client_ctx->vcd_handle,
+		&vcd_property_hdr, &control);
+
+		if (vcd_status) {
+			ERR("%s(): Set VCD_I_QP_RANGE Failed\n",
+					__func__);
+			status = false;
+		}
+	} else {
+		vcd_status = vcd_get_property(client_ctx->vcd_handle,
+					&vcd_property_hdr, &control);
+		if (vcd_status) {
+			ERR("%s(): Get VCD_I_QP_RANGE Failed\n",
+					__func__);
+			status = false;
+		} else {
+			qprange->maxqp = control.max_qp;
+			qprange->minqp = control.min_qp;
+		}
+	}
+	return status;
+}
+
+u32 vid_enc_start_stop(struct video_client_ctx *client_ctx, u32 start)
+{
+	u32 vcd_status;
+
+	if (!client_ctx)
+		return false;
+
+	if (start) {
+			vcd_status = vcd_encode_start(client_ctx->vcd_handle);
+
+			if (vcd_status) {
+				ERR("%s(): vcd_encode_start failed."
+				" vcd_status = %u\n", __func__, vcd_status);
+				return false;
+			}
+	} else {
+		vcd_status = vcd_stop(client_ctx->vcd_handle);
+		if (vcd_status) {
+			ERR("%s(): vcd_stop failed.  vcd_status = %u\n",
+		__func__, vcd_status);
+			return false;
+		}
+		DBG("Send STOP_DONE message to client = %p\n",
+				client_ctx);
+	}
+	return true;
+}
+
+u32 vid_enc_pause_resume(struct video_client_ctx *client_ctx, u32 pause)
+{
+	u32 vcd_status;
+
+	if (!client_ctx)
+		return false;
+
+	if (pause) {
+		DBG("PAUSE command from client = %p\n",
+				client_ctx);
+		vcd_status = vcd_pause(client_ctx->vcd_handle);
+	} else {
+		DBG("Resume command from client = %p\n",
+				client_ctx);
+		vcd_status = vcd_resume(client_ctx->vcd_handle);
+	}
+
+	if (vcd_status)
+		return false;
+
+	return true;
+}
+
+u32 vid_enc_flush(struct video_client_ctx *client_ctx,
+		struct venc_bufferflush *bufferflush)
+{
+	u32 status = true, mode, vcd_status;
+
+	if (!client_ctx || !bufferflush)
+		return false;
+
+	switch (bufferflush->flush_mode) {
+	case VEN_FLUSH_INPUT:
+		mode = VCD_FLUSH_INPUT;
+		break;
+	case VEN_FLUSH_OUTPUT:
+		mode = VCD_FLUSH_OUTPUT;
+		break;
+	case VEN_FLUSH_ALL:
+		mode = VCD_FLUSH_ALL;
+		break;
+	default:
+		status = false;
+		break;
+	}
+	if (status) {
+		vcd_status = vcd_flush(client_ctx->vcd_handle, mode);
+		if (vcd_status)
+			status = false;
+	}
+	return status;
+}
+
+u32 vid_enc_get_buffer_req(struct video_client_ctx *client_ctx,
+		struct venc_allocatorproperty *venc_buf_req, u32 input_dir)
+{
+	enum vcd_buffer_type buffer;
+	struct vcd_buffer_requirement buffer_req;
+	u32 status = true;
+	u32 vcd_status;
+
+	if (!client_ctx || !venc_buf_req)
+		return false;
+
+	buffer = VCD_BUFFER_OUTPUT;
+	if (input_dir)
+		buffer = VCD_BUFFER_INPUT;
+
+	vcd_status = vcd_get_buffer_requirements(client_ctx->vcd_handle,
+							buffer, &buffer_req);
+
+	if (vcd_status)
+		status = false;
+
+	if (status) {
+		venc_buf_req->actualcount = buffer_req.actual_count;
+		venc_buf_req->alignment = buffer_req.align;
+		venc_buf_req->datasize = buffer_req.sz;
+		venc_buf_req->mincount = buffer_req.min_count;
+		venc_buf_req->maxcount = buffer_req.max_count;
+		venc_buf_req->alignment = buffer_req.align;
+		venc_buf_req->bufpoolid = buffer_req.buf_pool_id;
+		venc_buf_req->suffixsize = 0;
+		DBG("%s: actual_count=%d, align=%d, sz=%d, min_count=%d, "
+			"max_count=%d, buf_pool_id=%d\n", __func__,
+			buffer_req.actual_count, buffer_req.align,
+			buffer_req.sz, buffer_req.min_count,
+			buffer_req.max_count, buffer_req.buf_pool_id);
+	}
+	return status;
+}
+
+u32 vid_enc_set_buffer_req(struct video_client_ctx *client_ctx,
+		struct venc_allocatorproperty *venc_buf_req, u32 input_dir)
+{
+	enum vcd_buffer_type buffer;
+	struct vcd_buffer_requirement buffer_req;
+	u32 status = true;
+	u32 vcd_status;
+
+	if (!client_ctx || !venc_buf_req)
+		return false;
+
+	buffer = VCD_BUFFER_OUTPUT;
+	if (input_dir)
+		buffer = VCD_BUFFER_INPUT;
+
+	buffer_req.actual_count = venc_buf_req->actualcount;
+	buffer_req.align = venc_buf_req->alignment;
+	buffer_req.sz = venc_buf_req->datasize;
+	buffer_req.min_count = venc_buf_req->mincount;
+	buffer_req.max_count = venc_buf_req->maxcount;
+	buffer_req.align = venc_buf_req->alignment;
+	buffer_req.buf_pool_id = 0;
+
+	DBG("%s: actual_count=%d, align=%d, sz=%d, min_count=%d, "
+		"max_count=%d, buf_pool_id=%d\n", __func__,
+		buffer_req.actual_count, buffer_req.align, buffer_req.sz,
+		buffer_req.min_count, buffer_req.max_count,
+		buffer_req.buf_pool_id);
+	vcd_status = vcd_set_buffer_requirements(client_ctx->vcd_handle,
+				buffer, &buffer_req);
+
+	if (vcd_status)
+		status = false;
+	return status;
+}
+
+u32 vid_enc_set_buffer(struct video_client_ctx *client_ctx,
+		struct venc_bufferpayload *buffer_info,
+		enum venc_buffer_dir buffer)
+{
+	enum vcd_buffer_type vcd_buffer_t = VCD_BUFFER_INPUT;
+	enum buffer_dir dir_buffer = BUFFER_TYPE_INPUT;
+	u32 vcd_status = VCD_ERR_FAIL;
+	unsigned long kernel_vaddr, length = 0;
+
+	if (!client_ctx || !buffer_info)
+		return false;
+
+	if (buffer == VEN_BUFFER_TYPE_OUTPUT) {
+		dir_buffer = BUFFER_TYPE_OUTPUT;
+		vcd_buffer_t = VCD_BUFFER_OUTPUT;
+	}
+	length = buffer_info->sz;
+	/*If buffer cannot be set, ignore */
+	if (!vidc_insert_addr_table(client_ctx, dir_buffer,
+					(unsigned long)buffer_info->pbuffer,
+					&kernel_vaddr,
+					buffer_info->fd,
+					(unsigned long)buffer_info->offset,
+					VID_ENC_MAX_NUM_OF_BUFF, length)) {
+		DBG("%s() : user_virt_addr = %p cannot be set.",
+		    __func__, buffer_info->pbuffer);
+		return false;
+	}
+
+	vcd_status = vcd_set_buffer(client_ctx->vcd_handle,
+				    vcd_buffer_t, (u8 *) kernel_vaddr,
+				    buffer_info->sz);
+
+	if (!vcd_status)
+		return true;
+	else
+		return false;
+}
+
+u32 vid_enc_free_buffer(struct video_client_ctx *client_ctx,
+		struct venc_bufferpayload *buffer_info,
+		enum venc_buffer_dir buffer)
+{
+	enum vcd_buffer_type buffer_vcd = VCD_BUFFER_INPUT;
+	enum buffer_dir dir_buffer = BUFFER_TYPE_INPUT;
+	u32 vcd_status = VCD_ERR_FAIL;
+	unsigned long kernel_vaddr;
+
+	if (!client_ctx || !buffer_info)
+		return false;
+
+	if (buffer == VEN_BUFFER_TYPE_OUTPUT) {
+		dir_buffer = BUFFER_TYPE_OUTPUT;
+		buffer_vcd = VCD_BUFFER_OUTPUT;
+	}
+	/*If buffer NOT set, ignore */
+	if (!vidc_delete_addr_table(client_ctx, dir_buffer,
+				(unsigned long)buffer_info->pbuffer,
+				&kernel_vaddr)) {
+		DBG("%s() : user_virt_addr = %p has not been set.",
+		    __func__, buffer_info->pbuffer);
+		return true;
+	}
+	vcd_status = vcd_free_buffer(client_ctx->vcd_handle, buffer_vcd,
+					 (u8 *)kernel_vaddr);
+
+	if (!vcd_status)
+		return true;
+	else
+		return false;
+}
+
+u32 vid_enc_encode_frame(struct video_client_ctx *client_ctx,
+		struct venc_buffer *input_frame_info)
+{
+	struct vcd_frame_data vcd_input_buffer;
+	unsigned long kernel_vaddr, phy_addr, user_vaddr;
+	int pmem_fd;
+	struct file *file;
+	s32 buffer_index = -1;
+	u32 ion_flag = 0;
+	struct ion_handle *buff_handle = NULL;
+
+	u32 vcd_status = VCD_ERR_FAIL;
+
+	if (!client_ctx || !input_frame_info)
+		return false;
+
+	user_vaddr = (unsigned long)input_frame_info->ptrbuffer;
+
+	if (vidc_lookup_addr_table(client_ctx, BUFFER_TYPE_INPUT,
+			true, &user_vaddr, &kernel_vaddr,
+			&phy_addr, &pmem_fd, &file,
+			&buffer_index)) {
+
+		/* kernel_vaddr  is found. send the frame to VCD */
+		memset((void *)&vcd_input_buffer, 0,
+					sizeof(struct vcd_frame_data));
+
+		vcd_input_buffer.virtual =
+		(u8 *) (kernel_vaddr + input_frame_info->offset);
+
+		vcd_input_buffer.offset = input_frame_info->offset;
+		vcd_input_buffer.frm_clnt_data =
+				(u32) input_frame_info->clientdata;
+		vcd_input_buffer.ip_frm_tag =
+				(u32) input_frame_info->clientdata;
+		vcd_input_buffer.data_len = input_frame_info->len;
+		vcd_input_buffer.time_stamp = input_frame_info->timestamp;
+
+		/* Rely on VCD using the same flags as OMX */
+		vcd_input_buffer.flags = input_frame_info->flags;
+
+		ion_flag = vidc_get_fd_info(client_ctx, BUFFER_TYPE_INPUT,
+				pmem_fd, kernel_vaddr, buffer_index,
+				&buff_handle);
+
+		if (vcd_input_buffer.data_len > 0) {
+			if (ion_flag == CACHED && buff_handle) {
+				msm_ion_do_cache_op(
+				client_ctx->user_ion_client,
+				buff_handle,
+				(unsigned long *) vcd_input_buffer.virtual,
+				(unsigned long) vcd_input_buffer.data_len,
+				ION_IOC_CLEAN_CACHES);
+			}
+		}
+
+		vcd_status = vcd_encode_frame(client_ctx->vcd_handle,
+		&vcd_input_buffer);
+		if (!vcd_status)
+			return true;
+		else {
+			ERR("%s(): vcd_encode_frame failed = %u\n",
+			__func__, vcd_status);
+			return false;
+		}
+
+	} else {
+		ERR("%s(): kernel_vaddr not found\n",
+				__func__);
+		return false;
+	}
+}
+
+u32 vid_enc_fill_output_buffer(struct video_client_ctx *client_ctx,
+		struct venc_buffer *output_frame_info)
+{
+	unsigned long kernel_vaddr, phy_addr, user_vaddr;
+	int pmem_fd;
+	struct file *file;
+	s32 buffer_index = -1;
+	u32 vcd_status = VCD_ERR_FAIL;
+
+	struct vcd_frame_data vcd_frame;
+
+	if (!client_ctx || !output_frame_info)
+		return false;
+
+	user_vaddr = (unsigned long)output_frame_info->ptrbuffer;
+
+	if (vidc_lookup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT,
+			true, &user_vaddr, &kernel_vaddr,
+			&phy_addr, &pmem_fd, &file,
+			&buffer_index)) {
+
+		memset((void *)&vcd_frame, 0,
+					 sizeof(struct vcd_frame_data));
+		vcd_frame.virtual = (u8 *) kernel_vaddr;
+		vcd_frame.frm_clnt_data = (u32) output_frame_info->clientdata;
+		vcd_frame.alloc_len = output_frame_info->sz;
+
+		vcd_status = vcd_fill_output_buffer(client_ctx->vcd_handle,
+								&vcd_frame);
+		if (!vcd_status)
+			return true;
+		else {
+			ERR("%s(): vcd_fill_output_buffer failed = %u\n",
+					__func__, vcd_status);
+			return false;
+		}
+	} else {
+		ERR("%s(): kernel_vaddr not found\n", __func__);
+		return false;
+	}
+}
+u32 vid_enc_set_recon_buffers(struct video_client_ctx *client_ctx,
+		struct venc_recon_addr *venc_recon)
+{
+	u32 vcd_status = VCD_ERR_FAIL;
+	u32 len, i, flags = 0;
+	struct file *file;
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_enc_recon_buffer *control = NULL;
+	struct msm_mapped_buffer *mapped_buffer = NULL;
+	int rc = -1;
+	unsigned long ionflag = 0;
+	unsigned long iova = 0;
+	unsigned long buffer_size = 0;
+	size_t ion_len = -1;
+	unsigned long phy_addr;
+
+	if (!client_ctx || !venc_recon) {
+		pr_err("%s() Invalid params", __func__);
+		return false;
+	}
+	len = sizeof(client_ctx->recon_buffer)/
+		sizeof(struct vcd_property_enc_recon_buffer);
+	for (i = 0; i < len; i++) {
+		if (!client_ctx->recon_buffer[i].kernel_virtual_addr) {
+			control = &client_ctx->recon_buffer[i];
+			break;
+		}
+	}
+	if (!control) {
+		pr_err("Exceeded max recon buffer setting");
+		return false;
+	}
+	control->buffer_size = venc_recon->buffer_size;
+	control->kernel_virtual_addr = NULL;
+	control->physical_addr = NULL;
+	control->pmem_fd = venc_recon->pmem_fd;
+	control->offset = venc_recon->offset;
+	control->user_virtual_addr = venc_recon->pbuffer;
+
+	if (!vcd_get_ion_status()) {
+		if (get_pmem_file(control->pmem_fd, (unsigned long *)
+			(&(control->physical_addr)), (unsigned long *)
+			(&control->kernel_virtual_addr),
+			(unsigned long *) (&len), &file)) {
+				ERR("%s(): get_pmem_file failed\n", __func__);
+				return false;
+			}
+			put_pmem_file(file);
+			flags = MSM_SUBSYSTEM_MAP_IOVA;
+			mapped_buffer = msm_subsystem_map_buffer(
+			(unsigned long)control->physical_addr, len,
+			flags, vidc_mmu_subsystem,
+			sizeof(vidc_mmu_subsystem)/sizeof(unsigned int));
+			if (IS_ERR(mapped_buffer)) {
+				pr_err("buffer map failed");
+				return false;
+			}
+			control->client_data = (void *) mapped_buffer;
+			control->dev_addr = (u8 *)mapped_buffer->iova[0];
+	} else {
+		client_ctx->recon_buffer_ion_handle[i] = ion_import_fd(
+				client_ctx->user_ion_client, control->pmem_fd);
+		if (IS_ERR_OR_NULL(client_ctx->recon_buffer_ion_handle[i])) {
+			ERR("%s(): get_ION_handle failed\n", __func__);
+			goto import_ion_error;
+		}
+		rc = ion_handle_get_flags(client_ctx->user_ion_client,
+					client_ctx->recon_buffer_ion_handle[i],
+					&ionflag);
+		if (rc) {
+			ERR("%s():get_ION_flags fail\n",
+				 __func__);
+			goto import_ion_error;
+		}
+		control->kernel_virtual_addr = (u8 *) ion_map_kernel(
+			client_ctx->user_ion_client,
+			client_ctx->recon_buffer_ion_handle[i],
+			ionflag);
+		if (!control->kernel_virtual_addr) {
+			ERR("%s(): get_ION_kernel virtual addr fail\n",
+				 __func__);
+			goto import_ion_error;
+		}
+		if (res_trk_check_for_sec_session()) {
+			rc = ion_phys(client_ctx->user_ion_client,
+				client_ctx->recon_buffer_ion_handle[i],
+				&phy_addr, &ion_len);
+			if (rc) {
+				ERR("%s():get_ION_kernel physical addr fail\n",
+					__func__);
+				goto map_ion_error;
+			}
+			control->physical_addr =  (u8 *) phy_addr;
+			len = (unsigned long) ion_len;
+			control->client_data = NULL;
+			control->dev_addr = (u8 *)control->physical_addr;
+		} else {
+			rc = ion_map_iommu(client_ctx->user_ion_client,
+					client_ctx->recon_buffer_ion_handle[i],
+					VIDEO_DOMAIN,
+					VIDEO_MAIN_POOL,
+					SZ_4K,
+					0,
+					(unsigned long *)&iova,
+					(unsigned long *)&buffer_size,
+					UNCACHED, 0);
+			if (rc) {
+				ERR("%s():ION map iommu addr fail\n",
+					 __func__);
+				goto map_ion_error;
+			}
+			control->physical_addr =  (u8 *) iova;
+			len = buffer_size;
+			control->client_data = NULL;
+			control->dev_addr = (u8 *)iova;
+		}
+	}
+
+	vcd_property_hdr.prop_id = VCD_I_RECON_BUFFERS;
+	vcd_property_hdr.sz =
+		sizeof(struct vcd_property_enc_recon_buffer);
+
+	vcd_status = vcd_set_property(client_ctx->vcd_handle,
+					&vcd_property_hdr, control);
+	if (!vcd_status) {
+		DBG("vcd_set_property returned success\n");
+		return true;
+	} else {
+		ERR("%s(): vid_enc_set_recon_buffers failed = %u\n",
+				__func__, vcd_status);
+		return false;
+	}
+map_ion_error:
+	if (control->kernel_virtual_addr)
+		ion_unmap_kernel(client_ctx->user_ion_client,
+			client_ctx->recon_buffer_ion_handle[i]);
+	if (client_ctx->recon_buffer_ion_handle[i])
+		ion_free(client_ctx->user_ion_client,
+			client_ctx->recon_buffer_ion_handle[i]);
+		client_ctx->recon_buffer_ion_handle[i] = NULL;
+import_ion_error:
+	return false;
+}
+
+u32 vid_enc_free_recon_buffers(struct video_client_ctx *client_ctx,
+			struct venc_recon_addr *venc_recon)
+{
+	u32 vcd_status = VCD_ERR_FAIL;
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_enc_recon_buffer *control = NULL;
+	u32 len = 0, i;
+
+	if (!client_ctx || !venc_recon) {
+		pr_err("%s() Invalid params", __func__);
+		return false;
+	}
+	len = sizeof(client_ctx->recon_buffer)/
+		sizeof(struct vcd_property_enc_recon_buffer);
+	pr_err(" %s() address  %p", __func__,
+	venc_recon->pbuffer);
+	for (i = 0; i < len; i++) {
+		if (client_ctx->recon_buffer[i].user_virtual_addr
+			== venc_recon->pbuffer) {
+			control = &client_ctx->recon_buffer[i];
+			break;
+		}
+	}
+	if (!control) {
+		pr_err(" %s() address not found %p", __func__,
+			venc_recon->pbuffer);
+		return false;
+	}
+	if (control->client_data)
+		msm_subsystem_unmap_buffer((struct msm_mapped_buffer *)
+		control->client_data);
+
+	vcd_property_hdr.prop_id = VCD_I_FREE_RECON_BUFFERS;
+	vcd_property_hdr.sz = sizeof(struct vcd_property_buffer_size);
+	vcd_status = vcd_set_property(client_ctx->vcd_handle,
+						&vcd_property_hdr, control);
+	if (vcd_get_ion_status()) {
+		if (client_ctx->recon_buffer_ion_handle[i]) {
+			ion_unmap_kernel(client_ctx->user_ion_client,
+				client_ctx->recon_buffer_ion_handle[i]);
+			if (!res_trk_check_for_sec_session()) {
+				ion_unmap_iommu(client_ctx->user_ion_client,
+				client_ctx->recon_buffer_ion_handle[i],
+				VIDEO_DOMAIN,
+				VIDEO_MAIN_POOL);
+			}
+			ion_free(client_ctx->user_ion_client,
+				client_ctx->recon_buffer_ion_handle[i]);
+			client_ctx->recon_buffer_ion_handle[i] = NULL;
+		}
+	}
+	memset(control, 0, sizeof(struct vcd_property_enc_recon_buffer));
+	return true;
+}
+
+u32 vid_enc_get_recon_buffer_size(struct video_client_ctx *client_ctx,
+		struct venc_recon_buff_size *venc_recon_size)
+{
+	u32 vcd_status = VCD_ERR_FAIL;
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_buffer_size control;
+
+	control.width = venc_recon_size->width;
+	control.height = venc_recon_size->height;
+
+	vcd_property_hdr.prop_id = VCD_I_GET_RECON_BUFFER_SIZE;
+	vcd_property_hdr.sz = sizeof(struct vcd_property_buffer_size);
+
+	vcd_status = vcd_get_property(client_ctx->vcd_handle,
+					&vcd_property_hdr, &control);
+
+	venc_recon_size->width = control.width;
+	venc_recon_size->height = control.height;
+	venc_recon_size->size = control.size;
+	venc_recon_size->alignment = control.alignment;
+	DBG("W: %d, H: %d, S: %d, A: %d", venc_recon_size->width,
+			venc_recon_size->height, venc_recon_size->size,
+			venc_recon_size->alignment);
+
+	if (!vcd_status) {
+		DBG("vcd_set_property returned success\n");
+		return true;
+		} else {
+			ERR("%s(): vid_enc_get_recon_buffer_size failed = %u\n",
+				__func__, vcd_status);
+			return false;
+		}
+}
diff --git a/drivers/video/msm/vidc/common/enc/venc_internal.h b/drivers/video/msm/vidc/common/enc/venc_internal.h
new file mode 100644
index 0000000..8a07fdb
--- /dev/null
+++ b/drivers/video/msm/vidc/common/enc/venc_internal.h
@@ -0,0 +1,154 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef VENC_INTERNAL_H
+#define VENC_INTERNAL_H
+
+#include <linux/msm_vidc_enc.h>
+#include <linux/cdev.h>
+#include <media/msm/vidc_init.h>
+
+#define VID_ENC_MAX_NUM_OF_BUFF 100
+
+enum venc_buffer_dir{
+  VEN_BUFFER_TYPE_INPUT,
+  VEN_BUFFER_TYPE_OUTPUT
+};
+
+struct vid_enc_msg {
+	struct list_head list;
+	struct venc_msg venc_msg_info;
+};
+
+struct vid_enc_dev {
+
+	struct cdev cdev;
+	struct device *device;
+	resource_size_t phys_base;
+	void __iomem *virt_base;
+	unsigned int irq;
+	struct clk *hclk;
+	struct clk *hclk_div2;
+	struct clk *pclk;
+	unsigned long hclk_rate;
+	struct mutex lock;
+	s32 device_handle;
+	struct video_client_ctx venc_clients[VIDC_MAX_NUM_CLIENTS];
+	u32 num_clients;
+};
+
+u32 vid_enc_set_get_base_cfg(struct video_client_ctx *client_ctx,
+		struct venc_basecfg *base_config, u32 set_flag);
+
+u32 vid_enc_set_get_inputformat(struct video_client_ctx *client_ctx,
+		u32 *input_format, u32 set_flag);
+
+u32 vid_enc_set_get_codec(struct video_client_ctx *client_ctx, u32 *codec,
+		u32 set_flag);
+
+u32 vid_enc_set_get_framesize(struct video_client_ctx *client_ctx,
+		u32 *height, u32 *width, u32 set_flag);
+
+u32 vid_enc_set_get_bitrate(struct video_client_ctx *client_ctx,
+		struct venc_targetbitrate *venc_bitrate, u32 set_flag);
+
+u32 vid_enc_set_get_framerate(struct video_client_ctx *client_ctx,
+		struct venc_framerate *frame_rate, u32 set_flag);
+
+u32 vid_enc_set_get_live_mode(struct video_client_ctx *client_ctx,
+		struct venc_switch *encoder_switch, u32 set_flag);
+
+u32 vid_enc_set_get_extradata(struct video_client_ctx *client_ctx,
+		u32 *extradata_flag, u32 set_flag);
+
+u32 vid_enc_set_get_short_header(struct video_client_ctx *client_ctx,
+		struct venc_switch *encoder_switch, u32 set_flag);
+
+u32 vid_enc_set_get_profile(struct video_client_ctx *client_ctx,
+		struct venc_profile *profile, u32 set_flag);
+
+u32 vid_enc_set_get_profile_level(struct video_client_ctx *client_ctx,
+		struct ven_profilelevel *profile_level, u32 set_flag);
+
+u32 vid_enc_set_get_session_qp(struct video_client_ctx *client_ctx,
+		struct venc_sessionqp *session_qp, u32 set_flag);
+
+u32 vid_enc_set_get_intraperiod(struct video_client_ctx *client_ctx,
+		struct venc_intraperiod *intraperiod, u32 set_flag);
+
+u32 vid_enc_request_iframe(struct video_client_ctx *client_ctx);
+
+u32 vid_enc_get_sequence_header(struct video_client_ctx *client_ctx,
+		struct venc_seqheader *seq_header);
+
+u32 vid_enc_set_get_entropy_cfg(struct video_client_ctx *client_ctx,
+		struct venc_entropycfg *entropy_cfg, u32 set_flag);
+
+u32 vid_enc_set_get_dbcfg(struct video_client_ctx *client_ctx,
+		struct venc_dbcfg *dbcfg, u32 set_flag);
+
+u32 vid_enc_set_get_intrarefresh(struct video_client_ctx *client_ctx,
+		struct venc_intrarefresh *intrarefresh,	u32 set_flag);
+
+u32 vid_enc_set_get_multiclicecfg(struct video_client_ctx *client_ctx,
+		struct venc_multiclicecfg *multiclicecfg, u32 set_flag);
+
+u32 vid_enc_set_get_ratectrlcfg(struct video_client_ctx *client_ctx,
+		struct venc_ratectrlcfg *ratectrlcfg, u32 set_flag);
+
+u32 vid_enc_set_get_voptimingcfg(struct video_client_ctx *client_ctx,
+		struct  venc_voptimingcfg *voptimingcfg, u32 set_flag);
+
+u32 vid_enc_set_get_headerextension(struct video_client_ctx *client_ctx,
+		struct venc_headerextension *headerextension, u32 set_flag);
+
+u32 vid_enc_set_get_qprange(struct video_client_ctx *client_ctx,
+		struct venc_qprange *qprange, u32 set_flag);
+
+u32 vid_enc_start_stop(struct video_client_ctx *client_ctx, u32 start);
+
+u32 vid_enc_pause_resume(struct video_client_ctx *client_ctx, u32 pause);
+
+u32 vid_enc_flush(struct video_client_ctx *client_ctx,
+		struct venc_bufferflush *bufferflush);
+
+u32 vid_enc_get_buffer_req(struct video_client_ctx *client_ctx,
+		struct venc_allocatorproperty *venc_buf_req, u32 input_dir);
+
+u32 vid_enc_set_buffer_req(struct video_client_ctx *client_ctx,
+		struct venc_allocatorproperty *venc_buf_req, u32 input_dir);
+
+u32 vid_enc_set_buffer(struct video_client_ctx *client_ctx,
+		struct venc_bufferpayload *buffer_info,
+		enum venc_buffer_dir buffer);
+
+u32 vid_enc_free_buffer(struct video_client_ctx *client_ctx,
+		struct venc_bufferpayload *buffer_info,
+		enum venc_buffer_dir buffer);
+
+u32 vid_enc_encode_frame(struct video_client_ctx *client_ctx,
+		struct venc_buffer *input_frame_info);
+
+u32 vid_enc_fill_output_buffer(struct video_client_ctx *client_ctx,
+		struct venc_buffer *output_frame_info);
+
+u32 vid_enc_set_recon_buffers(struct video_client_ctx *client_ctx,
+		struct venc_recon_addr *venc_recon);
+
+u32 vid_enc_free_recon_buffers(struct video_client_ctx *client_ctx,
+		struct venc_recon_addr *venc_recon);
+
+u32 vid_enc_get_recon_buffer_size(struct video_client_ctx *client_ctx,
+		struct venc_recon_buff_size *venc_recon_size);
+
+#endif
diff --git a/drivers/video/msm/vidc/common/init/vidc_init.c b/drivers/video/msm/vidc/common/init/vidc_init.c
new file mode 100644
index 0000000..23c990a
--- /dev/null
+++ b/drivers/video/msm/vidc/common/init/vidc_init.c
@@ -0,0 +1,929 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <linux/android_pmem.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <mach/clk.h>
+#include <linux/pm_runtime.h>
+#include <mach/msm_subsystem_map.h>
+#include <media/msm/vcd_api.h>
+#include <media/msm/vidc_init.h>
+#include "vidc_init_internal.h"
+#include "vcd_res_tracker_api.h"
+
+#if DEBUG
+#define DBG(x...) printk(KERN_DEBUG x)
+#else
+#define DBG(x...)
+#endif
+
+#define VIDC_NAME "msm_vidc_reg"
+
+#define ERR(x...) printk(KERN_ERR x)
+
+static struct vidc_dev *vidc_device_p;
+static dev_t vidc_dev_num;
+static struct class *vidc_class;
+static unsigned int vidc_mmu_subsystem[] = {MSM_SUBSYSTEM_VIDEO};
+
+static const struct file_operations vidc_fops = {
+	.owner = THIS_MODULE,
+	.open = NULL,
+	.release = NULL,
+	.unlocked_ioctl = NULL,
+};
+
+struct workqueue_struct *vidc_wq;
+struct workqueue_struct *vidc_timer_wq;
+static irqreturn_t vidc_isr(int irq, void *dev);
+static spinlock_t vidc_spin_lock;
+
+u32 vidc_msg_timing, vidc_msg_pmem, vidc_msg_register;
+
+#ifdef VIDC_ENABLE_DBGFS
+struct dentry *vidc_debugfs_root;
+
+struct dentry *vidc_get_debugfs_root(void)
+{
+	if (vidc_debugfs_root == NULL)
+		vidc_debugfs_root = debugfs_create_dir("vidc", NULL);
+	return vidc_debugfs_root;
+}
+
+void vidc_debugfs_file_create(struct dentry *root, const char *name,
+				u32 *var)
+{
+	struct dentry *vidc_debugfs_file =
+		debugfs_create_u32(name, S_IRUGO | S_IWUSR, root, var);
+	if (!vidc_debugfs_file)
+		ERR("%s(): Error creating/opening file %s\n", __func__, name);
+}
+#endif
+
+static void vidc_timer_fn(unsigned long data)
+{
+	unsigned long flag;
+	struct vidc_timer *hw_timer = NULL;
+	ERR("%s() Timer expired\n", __func__);
+	spin_lock_irqsave(&vidc_spin_lock, flag);
+	hw_timer = (struct vidc_timer *)data;
+	list_add_tail(&hw_timer->list, &vidc_device_p->vidc_timer_queue);
+	spin_unlock_irqrestore(&vidc_spin_lock, flag);
+	DBG("Queue the work for timer\n");
+	queue_work(vidc_timer_wq, &vidc_device_p->vidc_timer_worker);
+}
+
+static void vidc_timer_handler(struct work_struct *work)
+{
+	unsigned long flag = 0;
+	u32 islist_empty = 0;
+	struct vidc_timer *hw_timer = NULL;
+
+	ERR("%s() Timer expired\n", __func__);
+	do {
+		spin_lock_irqsave(&vidc_spin_lock, flag);
+		islist_empty = list_empty(&vidc_device_p->vidc_timer_queue);
+		if (!islist_empty) {
+			hw_timer = list_first_entry(
+				&vidc_device_p->vidc_timer_queue,
+				struct vidc_timer, list);
+			list_del(&hw_timer->list);
+		}
+		spin_unlock_irqrestore(&vidc_spin_lock, flag);
+		if (!islist_empty && hw_timer && hw_timer->cb_func)
+			hw_timer->cb_func(hw_timer->userdata);
+	} while (!islist_empty);
+}
+
+static void vidc_work_handler(struct work_struct *work)
+{
+	DBG("vidc_work_handler()");
+	vcd_read_and_clear_interrupt();
+	vcd_response_handler();
+	enable_irq(vidc_device_p->irq);
+	DBG("vidc_work_handler() done");
+}
+
+static DECLARE_WORK(vidc_work, vidc_work_handler);
+
+static int __devinit vidc_720p_probe(struct platform_device *pdev)
+{
+	struct resource *resource;
+	DBG("Enter %s()\n", __func__);
+
+	if (pdev->id) {
+		ERR("Invalid plaform device ID = %d\n", pdev->id);
+		return -EINVAL;
+	}
+	vidc_device_p->irq = platform_get_irq(pdev, 0);
+	if (unlikely(vidc_device_p->irq < 0)) {
+		ERR("%s(): Invalid irq = %d\n", __func__,
+					 vidc_device_p->irq);
+		return -ENXIO;
+	}
+
+	resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (unlikely(!resource)) {
+		ERR("%s(): Invalid resource\n", __func__);
+		return -ENXIO;
+	}
+
+	vidc_device_p->phys_base = resource->start;
+	vidc_device_p->virt_base = ioremap(resource->start,
+	resource->end - resource->start + 1);
+
+	if (!vidc_device_p->virt_base) {
+		ERR("%s() : ioremap failed\n", __func__);
+		return -ENOMEM;
+	}
+	vidc_device_p->device = &pdev->dev;
+	mutex_init(&vidc_device_p->lock);
+
+	vidc_wq = create_singlethread_workqueue("vidc_worker_queue");
+	if (!vidc_wq) {
+		ERR("%s: create workque failed\n", __func__);
+		return -ENOMEM;
+	}
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+	return 0;
+}
+
+static int __devexit vidc_720p_remove(struct platform_device *pdev)
+{
+	if (pdev->id) {
+		ERR("Invalid plaform device ID = %d\n", pdev->id);
+		return -EINVAL;
+	}
+	pm_runtime_disable(&pdev->dev);
+
+	return 0;
+}
+
+static int vidc_runtime_suspend(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: suspending...\n");
+	return 0;
+}
+
+static int vidc_runtime_resume(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: resuming...\n");
+	return 0;
+}
+
+static const struct dev_pm_ops vidc_dev_pm_ops = {
+	.runtime_suspend = vidc_runtime_suspend,
+	.runtime_resume = vidc_runtime_resume,
+};
+
+static struct platform_driver msm_vidc_720p_platform_driver = {
+	.probe = vidc_720p_probe,
+	.remove = vidc_720p_remove,
+	.driver = {
+		.name = "msm_vidc",
+		.pm   = &vidc_dev_pm_ops,
+	},
+};
+
+static void __exit vidc_exit(void)
+{
+	platform_driver_unregister(&msm_vidc_720p_platform_driver);
+}
+
+static irqreturn_t vidc_isr(int irq, void *dev)
+{
+	DBG("\n vidc_isr() %d ", irq);
+	disable_irq_nosync(irq);
+	queue_work(vidc_wq, &vidc_work);
+	return IRQ_HANDLED;
+}
+
+static int __init vidc_init(void)
+{
+	int rc = 0;
+	struct device *class_devp;
+#ifdef VIDC_ENABLE_DBGFS
+	struct dentry *root = NULL;
+#endif
+
+	vidc_device_p = kzalloc(sizeof(struct vidc_dev), GFP_KERNEL);
+	if (!vidc_device_p) {
+		ERR("%s Unable to allocate memory for vidc_dev\n",
+			__func__);
+		return -ENOMEM;
+	}
+
+	rc = alloc_chrdev_region(&vidc_dev_num, 0, 1, VIDC_NAME);
+	if (rc < 0) {
+		ERR("%s: alloc_chrdev_region Failed rc = %d\n",
+			__func__, rc);
+		goto error_vidc_alloc_chrdev_region;
+	}
+
+	vidc_class = class_create(THIS_MODULE, VIDC_NAME);
+	if (IS_ERR(vidc_class)) {
+		rc = PTR_ERR(vidc_class);
+		ERR("%s: couldn't create vidc_class rc = %d\n",
+		__func__, rc);
+
+		goto error_vidc_class_create;
+	}
+
+	class_devp = device_create(vidc_class, NULL, vidc_dev_num, NULL,
+					VIDC_NAME);
+
+	if (IS_ERR(class_devp)) {
+		rc = PTR_ERR(class_devp);
+		ERR("%s: class device_create failed %d\n",
+			__func__, rc);
+		goto error_vidc_class_device_create;
+	}
+
+	cdev_init(&vidc_device_p->cdev, &vidc_fops);
+	vidc_device_p->cdev.owner = THIS_MODULE;
+	rc = cdev_add(&(vidc_device_p->cdev), vidc_dev_num, 1);
+
+	if (rc < 0) {
+		ERR("%s: cdev_add failed %d\n", __func__, rc);
+		goto error_vidc_cdev_add;
+	}
+
+	rc = platform_driver_register(&msm_vidc_720p_platform_driver);
+	if (rc) {
+		ERR("%s failed to load\n", __func__);
+		goto error_vidc_platfom_register;
+	}
+
+	rc = request_irq(vidc_device_p->irq, vidc_isr, IRQF_TRIGGER_HIGH,
+			 "vidc", vidc_device_p->device);
+
+	if (unlikely(rc)) {
+		ERR("%s() :request_irq failed\n", __func__);
+		goto error_vidc_request_irq;
+	}
+	res_trk_init(vidc_device_p->device, vidc_device_p->irq);
+	vidc_timer_wq = create_singlethread_workqueue("vidc_timer_wq");
+	if (!vidc_timer_wq) {
+		ERR("%s: create workque failed\n", __func__);
+		rc = -ENOMEM;
+		goto error_vidc_create_workqueue;
+	}
+	DBG("Disabling IRQ in %s()\n", __func__);
+	disable_irq_nosync(vidc_device_p->irq);
+	INIT_WORK(&vidc_device_p->vidc_timer_worker,
+			  vidc_timer_handler);
+	spin_lock_init(&vidc_spin_lock);
+	INIT_LIST_HEAD(&vidc_device_p->vidc_timer_queue);
+
+	vidc_device_p->ref_count = 0;
+	vidc_device_p->firmware_refcount = 0;
+	vidc_device_p->get_firmware = 0;
+#ifdef VIDC_ENABLE_DBGFS
+	root = vidc_get_debugfs_root();
+	if (root) {
+		vidc_debugfs_file_create(root, "vidc_msg_timing",
+				(u32 *) &vidc_msg_timing);
+		vidc_debugfs_file_create(root, "vidc_msg_pmem",
+				(u32 *) &vidc_msg_pmem);
+		vidc_debugfs_file_create(root, "vidc_msg_register",
+				(u32 *) &vidc_msg_register);
+	}
+#endif
+	return 0;
+
+error_vidc_create_workqueue:
+	free_irq(vidc_device_p->irq, vidc_device_p->device);
+error_vidc_request_irq:
+	platform_driver_unregister(&msm_vidc_720p_platform_driver);
+error_vidc_platfom_register:
+	cdev_del(&(vidc_device_p->cdev));
+error_vidc_cdev_add:
+	device_destroy(vidc_class, vidc_dev_num);
+error_vidc_class_device_create:
+	class_destroy(vidc_class);
+error_vidc_class_create:
+	unregister_chrdev_region(vidc_dev_num, 1);
+error_vidc_alloc_chrdev_region:
+	kfree(vidc_device_p);
+
+	return rc;
+}
+
+void __iomem *vidc_get_ioaddr(void)
+{
+	return (u8 *)vidc_device_p->virt_base;
+}
+EXPORT_SYMBOL(vidc_get_ioaddr);
+
+int vidc_load_firmware(void)
+{
+	u32 status = true;
+
+	if (!res_trk_check_for_sec_session()) {
+		mutex_lock(&vidc_device_p->lock);
+		if (!vidc_device_p->get_firmware) {
+			status = res_trk_download_firmware();
+			if (!status)
+				goto error;
+			vidc_device_p->get_firmware = 1;
+		}
+		vidc_device_p->firmware_refcount++;
+error:
+		mutex_unlock(&vidc_device_p->lock);
+	}
+	return status;
+}
+EXPORT_SYMBOL(vidc_load_firmware);
+
+void vidc_release_firmware(void)
+{
+	if (!res_trk_check_for_sec_session()) {
+		mutex_lock(&vidc_device_p->lock);
+		if (vidc_device_p->firmware_refcount > 0)
+			vidc_device_p->firmware_refcount--;
+		else
+			vidc_device_p->firmware_refcount = 0;
+		mutex_unlock(&vidc_device_p->lock);
+	}
+}
+EXPORT_SYMBOL(vidc_release_firmware);
+
+u32 vidc_get_fd_info(struct video_client_ctx *client_ctx,
+		enum buffer_dir buffer, int pmem_fd,
+		unsigned long kvaddr, int index,
+		struct ion_handle **buff_handle)
+{
+	struct buf_addr_table *buf_addr_table;
+	u32 rc = 0;
+	if (!client_ctx)
+		return false;
+	if (buffer == BUFFER_TYPE_INPUT)
+		buf_addr_table = client_ctx->input_buf_addr_table;
+	else
+		buf_addr_table = client_ctx->output_buf_addr_table;
+	if (buf_addr_table[index].pmem_fd == pmem_fd) {
+		if (buf_addr_table[index].kernel_vaddr == kvaddr) {
+			rc = buf_addr_table[index].buff_ion_flag;
+			*buff_handle = buf_addr_table[index].buff_ion_handle;
+		} else
+			*buff_handle = NULL;
+	} else
+		*buff_handle = NULL;
+	return rc;
+}
+EXPORT_SYMBOL(vidc_get_fd_info);
+
+void vidc_cleanup_addr_table(struct video_client_ctx *client_ctx,
+				enum buffer_dir buffer)
+{
+	u32 *num_of_buffers = NULL;
+	u32 i = 0;
+	struct buf_addr_table *buf_addr_table;
+	if (buffer == BUFFER_TYPE_INPUT) {
+		buf_addr_table = client_ctx->input_buf_addr_table;
+		num_of_buffers = &client_ctx->num_of_input_buffers;
+		DBG("%s(): buffer = INPUT\n", __func__);
+
+	} else {
+		buf_addr_table = client_ctx->output_buf_addr_table;
+		num_of_buffers = &client_ctx->num_of_output_buffers;
+		DBG("%s(): buffer = OUTPUT\n", __func__);
+	}
+
+	if (!*num_of_buffers)
+		goto bail_out_cleanup;
+	if (!client_ctx->user_ion_client)
+		goto bail_out_cleanup;
+	for (i = 0; i < *num_of_buffers; ++i) {
+		if (buf_addr_table[i].client_data) {
+			msm_subsystem_unmap_buffer(
+			(struct msm_mapped_buffer *)
+			buf_addr_table[i].client_data);
+			buf_addr_table[i].client_data = NULL;
+		}
+		if (!IS_ERR_OR_NULL(buf_addr_table[i].buff_ion_handle)) {
+			if (!IS_ERR_OR_NULL(client_ctx->user_ion_client)) {
+				ion_unmap_kernel(client_ctx->user_ion_client,
+						buf_addr_table[i].
+						buff_ion_handle);
+				if (!res_trk_check_for_sec_session()) {
+					ion_unmap_iommu(
+						client_ctx->user_ion_client,
+						buf_addr_table[i].
+						buff_ion_handle,
+						VIDEO_DOMAIN,
+						VIDEO_MAIN_POOL);
+				}
+				ion_free(client_ctx->user_ion_client,
+						buf_addr_table[i].
+						buff_ion_handle);
+				buf_addr_table[i].buff_ion_handle = NULL;
+			}
+		}
+	}
+	if (client_ctx->vcd_h264_mv_buffer.client_data) {
+		msm_subsystem_unmap_buffer((struct msm_mapped_buffer *)
+		client_ctx->vcd_h264_mv_buffer.client_data);
+		client_ctx->vcd_h264_mv_buffer.client_data = NULL;
+	}
+	if (!IS_ERR_OR_NULL(client_ctx->h264_mv_ion_handle)) {
+		if (!IS_ERR_OR_NULL(client_ctx->user_ion_client)) {
+			ion_unmap_kernel(client_ctx->user_ion_client,
+					client_ctx->h264_mv_ion_handle);
+			if (!res_trk_check_for_sec_session()) {
+				ion_unmap_iommu(client_ctx->user_ion_client,
+					client_ctx->h264_mv_ion_handle,
+					VIDEO_DOMAIN,
+					VIDEO_MAIN_POOL);
+			}
+			ion_free(client_ctx->user_ion_client,
+					client_ctx->h264_mv_ion_handle);
+			client_ctx->h264_mv_ion_handle = NULL;
+		}
+	}
+bail_out_cleanup:
+	return;
+}
+EXPORT_SYMBOL(vidc_cleanup_addr_table);
+
+u32 vidc_lookup_addr_table(struct video_client_ctx *client_ctx,
+	enum buffer_dir buffer,
+	u32 search_with_user_vaddr,
+	unsigned long *user_vaddr,
+	unsigned long *kernel_vaddr,
+	unsigned long *phy_addr, int *pmem_fd,
+	struct file **file, s32 *buffer_index)
+{
+	u32 num_of_buffers;
+	u32 i;
+	struct buf_addr_table *buf_addr_table;
+	u32 found = false;
+
+	if (!client_ctx)
+		return false;
+	mutex_lock(&client_ctx->enrty_queue_lock);
+	if (buffer == BUFFER_TYPE_INPUT) {
+		buf_addr_table = client_ctx->input_buf_addr_table;
+		num_of_buffers = client_ctx->num_of_input_buffers;
+		DBG("%s(): buffer = INPUT\n", __func__);
+
+	} else {
+		buf_addr_table = client_ctx->output_buf_addr_table;
+		num_of_buffers = client_ctx->num_of_output_buffers;
+		DBG("%s(): buffer = OUTPUT\n", __func__);
+	}
+
+	for (i = 0; i < num_of_buffers; ++i) {
+		if (search_with_user_vaddr) {
+			if (*user_vaddr == buf_addr_table[i].user_vaddr) {
+				*kernel_vaddr = buf_addr_table[i].kernel_vaddr;
+				found = true;
+				DBG("%s() : client_ctx = %p."
+				" user_virt_addr = 0x%08lx is found",
+				__func__, client_ctx, *user_vaddr);
+				break;
+			}
+		} else {
+			if (*kernel_vaddr == buf_addr_table[i].kernel_vaddr) {
+				*user_vaddr = buf_addr_table[i].user_vaddr;
+				found = true;
+				DBG("%s() : client_ctx = %p."
+				" kernel_virt_addr = 0x%08lx is found",
+				__func__, client_ctx, *kernel_vaddr);
+				break;
+			}
+		}
+	}
+
+	if (found) {
+		*phy_addr = buf_addr_table[i].dev_addr;
+		*pmem_fd = buf_addr_table[i].pmem_fd;
+		*file = buf_addr_table[i].file;
+		*buffer_index = i;
+
+		if (search_with_user_vaddr)
+			DBG("kernel_vaddr = 0x%08lx, phy_addr = 0x%08lx "
+			" pmem_fd = %d, struct *file	= %p "
+			"buffer_index = %d\n", *kernel_vaddr,
+			*phy_addr, *pmem_fd, *file, *buffer_index);
+		else
+			DBG("user_vaddr = 0x%08lx, phy_addr = 0x%08lx "
+			" pmem_fd = %d, struct *file	= %p "
+			"buffer_index = %d\n", *user_vaddr, *phy_addr,
+			*pmem_fd, *file, *buffer_index);
+		mutex_unlock(&client_ctx->enrty_queue_lock);
+		return true;
+	} else {
+		if (search_with_user_vaddr)
+			DBG("%s() : client_ctx = %p user_virt_addr = 0x%08lx"
+			" Not Found.\n", __func__, client_ctx, *user_vaddr);
+		else
+			DBG("%s() : client_ctx = %p kernel_virt_addr = 0x%08lx"
+			" Not Found.\n", __func__, client_ctx,
+			*kernel_vaddr);
+		mutex_unlock(&client_ctx->enrty_queue_lock);
+		return false;
+	}
+}
+EXPORT_SYMBOL(vidc_lookup_addr_table);
+
+u32 vidc_insert_addr_table(struct video_client_ctx *client_ctx,
+	enum buffer_dir buffer, unsigned long user_vaddr,
+	unsigned long *kernel_vaddr, int pmem_fd,
+	unsigned long buffer_addr_offset, unsigned int max_num_buffers,
+	unsigned long length)
+{
+	unsigned long len, phys_addr;
+	struct file *file = NULL;
+	u32 *num_of_buffers = NULL;
+	u32 i, flags;
+	struct buf_addr_table *buf_addr_table;
+	struct msm_mapped_buffer *mapped_buffer = NULL;
+	struct ion_handle *buff_ion_handle = NULL;
+	unsigned long ionflag = 0;
+	unsigned long iova = 0;
+	int ret = 0;
+	unsigned long buffer_size  = 0;
+	size_t ion_len;
+
+	if (!client_ctx || !length)
+		return false;
+	mutex_lock(&client_ctx->enrty_queue_lock);
+	if (buffer == BUFFER_TYPE_INPUT) {
+		buf_addr_table = client_ctx->input_buf_addr_table;
+		num_of_buffers = &client_ctx->num_of_input_buffers;
+		DBG("%s(): buffer = INPUT #Buf = %d\n",
+			__func__, *num_of_buffers);
+
+	} else {
+		buf_addr_table = client_ctx->output_buf_addr_table;
+		num_of_buffers = &client_ctx->num_of_output_buffers;
+		DBG("%s(): buffer = OUTPUT #Buf = %d\n",
+			__func__, *num_of_buffers);
+		length = length * 2; /* workaround for iommu video h/w bug */
+	}
+
+	if (*num_of_buffers == max_num_buffers) {
+		ERR("%s(): Num of buffers reached max value : %d",
+			__func__, max_num_buffers);
+		goto bail_out_add;
+	}
+
+	i = 0;
+	while (i < *num_of_buffers &&
+		user_vaddr != buf_addr_table[i].user_vaddr)
+		i++;
+	if (i < *num_of_buffers) {
+		DBG("%s() : client_ctx = %p."
+			" user_virt_addr = 0x%08lx already set",
+			__func__, client_ctx, user_vaddr);
+		goto bail_out_add;
+	} else {
+		if (!vcd_get_ion_status()) {
+			if (get_pmem_file(pmem_fd, &phys_addr,
+					kernel_vaddr, &len, &file)) {
+				ERR("%s(): get_pmem_file failed\n", __func__);
+				goto bail_out_add;
+			}
+			put_pmem_file(file);
+			flags = (buffer == BUFFER_TYPE_INPUT)
+			? MSM_SUBSYSTEM_MAP_IOVA :
+			MSM_SUBSYSTEM_MAP_IOVA|MSM_SUBSYSTEM_ALIGN_IOVA_8K;
+			mapped_buffer = msm_subsystem_map_buffer(phys_addr,
+			length, flags, vidc_mmu_subsystem,
+			sizeof(vidc_mmu_subsystem)/sizeof(unsigned int));
+			if (IS_ERR(mapped_buffer)) {
+				pr_err("buffer map failed");
+				goto bail_out_add;
+			}
+			buf_addr_table[*num_of_buffers].client_data = (void *)
+				mapped_buffer;
+			buf_addr_table[*num_of_buffers].dev_addr =
+				mapped_buffer->iova[0];
+		} else {
+			buff_ion_handle = ion_import_fd(
+				client_ctx->user_ion_client, pmem_fd);
+			if (IS_ERR_OR_NULL(buff_ion_handle)) {
+				ERR("%s(): get_ION_handle failed\n",
+				 __func__);
+				goto bail_out_add;
+			}
+			if (ion_handle_get_flags(client_ctx->user_ion_client,
+						buff_ion_handle,
+						&ionflag)) {
+				ERR("%s():ION flags fail\n",
+				 __func__);
+				goto bail_out_add;
+			}
+			*kernel_vaddr = (unsigned long)
+				ion_map_kernel(
+				client_ctx->user_ion_client,
+				buff_ion_handle,
+				ionflag);
+			if (IS_ERR_OR_NULL((void *)*kernel_vaddr)) {
+				ERR("%s():ION virtual addr fail\n",
+				 __func__);
+				*kernel_vaddr = (unsigned long)NULL;
+				goto ion_free_error;
+			}
+			if (res_trk_check_for_sec_session()) {
+				if (ion_phys(client_ctx->user_ion_client,
+					buff_ion_handle,
+					&phys_addr, &ion_len)) {
+					ERR("%s():ION physical addr fail\n",
+					__func__);
+					goto ion_map_error;
+				}
+				len = (unsigned long) ion_len;
+				buf_addr_table[*num_of_buffers].client_data =
+					 NULL;
+				buf_addr_table[*num_of_buffers].dev_addr =
+					 phys_addr;
+			} else {
+				ret = ion_map_iommu(client_ctx->user_ion_client,
+						buff_ion_handle,
+						VIDEO_DOMAIN,
+						VIDEO_MAIN_POOL,
+						SZ_8K,
+						length,
+						(unsigned long *) &iova,
+						(unsigned long *) &buffer_size,
+						UNCACHED,
+						ION_IOMMU_UNMAP_DELAYED);
+				if (ret) {
+					ERR("%s():ION iommu map fail\n",
+					 __func__);
+					goto ion_map_error;
+				}
+				phys_addr = iova;
+				buf_addr_table[*num_of_buffers].client_data =
+						 NULL;
+				buf_addr_table[*num_of_buffers].dev_addr =
+						 iova;
+			}
+		}
+		phys_addr += buffer_addr_offset;
+		(*kernel_vaddr) += buffer_addr_offset;
+		buf_addr_table[*num_of_buffers].user_vaddr = user_vaddr;
+		buf_addr_table[*num_of_buffers].kernel_vaddr = *kernel_vaddr;
+		buf_addr_table[*num_of_buffers].pmem_fd = pmem_fd;
+		buf_addr_table[*num_of_buffers].file = file;
+		buf_addr_table[*num_of_buffers].phy_addr = phys_addr;
+		buf_addr_table[*num_of_buffers].buff_ion_handle =
+						buff_ion_handle;
+		buf_addr_table[*num_of_buffers].buff_ion_flag =
+						ionflag;
+		*num_of_buffers = *num_of_buffers + 1;
+		DBG("%s() : client_ctx = %p, user_virt_addr = 0x%08lx, "
+			"kernel_vaddr = 0x%08lx phys_addr=%lu inserted!",
+			__func__, client_ctx, user_vaddr, *kernel_vaddr,
+			phys_addr);
+	}
+	mutex_unlock(&client_ctx->enrty_queue_lock);
+	return true;
+ion_map_error:
+	if (*kernel_vaddr && buff_ion_handle)
+		ion_unmap_kernel(client_ctx->user_ion_client, buff_ion_handle);
+ion_free_error:
+	if (!IS_ERR_OR_NULL(buff_ion_handle))
+		ion_free(client_ctx->user_ion_client, buff_ion_handle);
+bail_out_add:
+	mutex_unlock(&client_ctx->enrty_queue_lock);
+	return false;
+}
+EXPORT_SYMBOL(vidc_insert_addr_table);
+
+/*
+ * Similar to vidc_insert_addr_table except intended for in-kernel
+ * use where buffers have already been alloced and mapped properly
+ */
+u32 vidc_insert_addr_table_kernel(struct video_client_ctx *client_ctx,
+	enum buffer_dir buffer, unsigned long user_vaddr,
+	unsigned long kernel_vaddr, unsigned long phys_addr,
+	unsigned int max_num_buffers,
+	unsigned long length)
+{
+	u32 *num_of_buffers = NULL;
+	u32 i;
+	struct buf_addr_table *buf_addr_table;
+	struct msm_mapped_buffer *mapped_buffer = NULL;
+
+	if (!client_ctx || !length || !kernel_vaddr || !phys_addr)
+		return false;
+	mutex_lock(&client_ctx->enrty_queue_lock);
+	if (buffer == BUFFER_TYPE_INPUT) {
+		buf_addr_table = client_ctx->input_buf_addr_table;
+		num_of_buffers = &client_ctx->num_of_input_buffers;
+		DBG("%s(): buffer = INPUT #Buf = %d\n",
+			__func__, *num_of_buffers);
+
+	} else {
+		buf_addr_table = client_ctx->output_buf_addr_table;
+		num_of_buffers = &client_ctx->num_of_output_buffers;
+		DBG("%s(): buffer = OUTPUT #Buf = %d\n",
+			__func__, *num_of_buffers);
+	}
+
+	if (*num_of_buffers == max_num_buffers) {
+		ERR("%s(): Num of buffers reached max value : %d",
+			__func__, max_num_buffers);
+		goto bail_out_add;
+	}
+
+	i = 0;
+	while (i < *num_of_buffers &&
+		user_vaddr != buf_addr_table[i].user_vaddr) {
+		i++;
+	}
+	if (i < *num_of_buffers) {
+		DBG("%s() : client_ctx = %p."
+			" user_virt_addr = 0x%08lx already set",
+			__func__, client_ctx, user_vaddr);
+		goto bail_out_add;
+	} else {
+		mapped_buffer = NULL;
+		buf_addr_table[*num_of_buffers].client_data = (void *)
+			mapped_buffer;
+		buf_addr_table[*num_of_buffers].dev_addr = phys_addr;
+		buf_addr_table[*num_of_buffers].user_vaddr = user_vaddr;
+		buf_addr_table[*num_of_buffers].kernel_vaddr = kernel_vaddr;
+		buf_addr_table[*num_of_buffers].pmem_fd = -1;
+		buf_addr_table[*num_of_buffers].file = NULL;
+		buf_addr_table[*num_of_buffers].phy_addr = phys_addr;
+		buf_addr_table[*num_of_buffers].buff_ion_handle = NULL;
+		*num_of_buffers = *num_of_buffers + 1;
+		DBG("%s() : client_ctx = %p, user_virt_addr = 0x%08lx, "
+			"kernel_vaddr = 0x%08lx inserted!", __func__,
+			client_ctx, user_vaddr, *kernel_vaddr);
+	}
+	mutex_unlock(&client_ctx->enrty_queue_lock);
+	return true;
+bail_out_add:
+	mutex_unlock(&client_ctx->enrty_queue_lock);
+	return false;
+}
+EXPORT_SYMBOL(vidc_insert_addr_table_kernel);
+
+u32 vidc_delete_addr_table(struct video_client_ctx *client_ctx,
+	enum buffer_dir buffer,
+	unsigned long user_vaddr,
+	unsigned long *kernel_vaddr)
+{
+	u32 *num_of_buffers = NULL;
+	u32 i;
+	struct buf_addr_table *buf_addr_table;
+
+	if (!client_ctx)
+		return false;
+	mutex_lock(&client_ctx->enrty_queue_lock);
+	if (buffer == BUFFER_TYPE_INPUT) {
+		buf_addr_table = client_ctx->input_buf_addr_table;
+		num_of_buffers = &client_ctx->num_of_input_buffers;
+
+	} else {
+		buf_addr_table = client_ctx->output_buf_addr_table;
+		num_of_buffers = &client_ctx->num_of_output_buffers;
+	}
+
+	if (!*num_of_buffers)
+		goto bail_out_del;
+
+	i = 0;
+	while (i < *num_of_buffers &&
+		user_vaddr != buf_addr_table[i].user_vaddr)
+		i++;
+	if (i == *num_of_buffers) {
+		pr_err("%s() : client_ctx = %p."
+			" user_virt_addr = 0x%08lx NOT found",
+			__func__, client_ctx, user_vaddr);
+		goto bail_out_del;
+	}
+	if (buf_addr_table[i].client_data) {
+		msm_subsystem_unmap_buffer(
+		(struct msm_mapped_buffer *)buf_addr_table[i].client_data);
+		buf_addr_table[i].client_data = NULL;
+	}
+	*kernel_vaddr = buf_addr_table[i].kernel_vaddr;
+	if (buf_addr_table[i].buff_ion_handle) {
+		ion_unmap_kernel(client_ctx->user_ion_client,
+				buf_addr_table[i].buff_ion_handle);
+		if (!res_trk_check_for_sec_session()) {
+			ion_unmap_iommu(client_ctx->user_ion_client,
+				buf_addr_table[i].buff_ion_handle,
+				VIDEO_DOMAIN,
+				VIDEO_MAIN_POOL);
+		}
+		ion_free(client_ctx->user_ion_client,
+				buf_addr_table[i].buff_ion_handle);
+		buf_addr_table[i].buff_ion_handle = NULL;
+	}
+	if (i < (*num_of_buffers - 1)) {
+		buf_addr_table[i].client_data =
+			buf_addr_table[*num_of_buffers - 1].client_data;
+		buf_addr_table[i].dev_addr =
+			buf_addr_table[*num_of_buffers - 1].dev_addr;
+		buf_addr_table[i].user_vaddr =
+			buf_addr_table[*num_of_buffers - 1].user_vaddr;
+		buf_addr_table[i].kernel_vaddr =
+			buf_addr_table[*num_of_buffers - 1].kernel_vaddr;
+		buf_addr_table[i].phy_addr =
+			buf_addr_table[*num_of_buffers - 1].phy_addr;
+		buf_addr_table[i].pmem_fd =
+			buf_addr_table[*num_of_buffers - 1].pmem_fd;
+		buf_addr_table[i].file =
+			buf_addr_table[*num_of_buffers - 1].file;
+		buf_addr_table[i].buff_ion_handle =
+			buf_addr_table[*num_of_buffers - 1].buff_ion_handle;
+	}
+	*num_of_buffers = *num_of_buffers - 1;
+	DBG("%s() : client_ctx = %p."
+		" user_virt_addr = 0x%08lx is found and deleted",
+		__func__, client_ctx, user_vaddr);
+	mutex_unlock(&client_ctx->enrty_queue_lock);
+	return true;
+bail_out_del:
+	mutex_unlock(&client_ctx->enrty_queue_lock);
+	return false;
+}
+EXPORT_SYMBOL(vidc_delete_addr_table);
+
+u32 vidc_timer_create(void (*timer_handler)(void *),
+	void *user_data, void **timer_handle)
+{
+	struct vidc_timer *hw_timer = NULL;
+	if (!timer_handler || !timer_handle) {
+		DBG("%s(): timer creation failed\n ", __func__);
+		return false;
+	}
+	hw_timer = kzalloc(sizeof(struct vidc_timer), GFP_KERNEL);
+	if (!hw_timer) {
+		DBG("%s(): timer creation failed in allocation\n ", __func__);
+		return false;
+	}
+	init_timer(&hw_timer->hw_timeout);
+	hw_timer->hw_timeout.data = (unsigned long)hw_timer;
+	hw_timer->hw_timeout.function = vidc_timer_fn;
+	hw_timer->cb_func = timer_handler;
+	hw_timer->userdata = user_data;
+	*timer_handle = hw_timer;
+	return true;
+}
+EXPORT_SYMBOL(vidc_timer_create);
+
+void  vidc_timer_release(void *timer_handle)
+{
+	kfree(timer_handle);
+}
+EXPORT_SYMBOL(vidc_timer_release);
+
+void  vidc_timer_start(void *timer_handle, u32 time_out)
+{
+	struct vidc_timer *hw_timer = (struct vidc_timer *)timer_handle;
+	DBG("%s(): start timer\n ", __func__);
+	if (hw_timer) {
+		hw_timer->hw_timeout.expires = jiffies + 1*HZ;
+		add_timer(&hw_timer->hw_timeout);
+	}
+}
+EXPORT_SYMBOL(vidc_timer_start);
+
+void  vidc_timer_stop(void *timer_handle)
+{
+	struct vidc_timer *hw_timer = (struct vidc_timer *)timer_handle;
+	DBG("%s(): stop timer\n ", __func__);
+	if (hw_timer)
+		del_timer(&hw_timer->hw_timeout);
+}
+EXPORT_SYMBOL(vidc_timer_stop);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Video decoder/encoder driver Init Module");
+MODULE_VERSION("1.0");
+module_init(vidc_init);
+module_exit(vidc_exit);
diff --git a/drivers/video/msm/vidc/common/init/vidc_init_internal.h b/drivers/video/msm/vidc/common/init/vidc_init_internal.h
new file mode 100644
index 0000000..1d903ad
--- /dev/null
+++ b/drivers/video/msm/vidc/common/init/vidc_init_internal.h
@@ -0,0 +1,41 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef VIDC_INIT_INTERNAL_H
+#define VIDC_INIT_INTERNAL_H
+
+#include <linux/cdev.h>
+
+struct vidc_timer {
+	struct list_head list;
+	struct timer_list hw_timeout;
+	void (*cb_func)(void *);
+	void *userdata;
+};
+
+struct vidc_dev {
+	struct cdev cdev;
+	struct device *device;
+	resource_size_t phys_base;
+	void __iomem *virt_base;
+	unsigned int irq;
+	unsigned int ref_count;
+	unsigned int firmware_refcount;
+	unsigned int get_firmware;
+	struct mutex lock;
+	s32 device_handle;
+	struct list_head vidc_timer_queue;
+	struct work_struct vidc_timer_worker;
+};
+
+#endif
diff --git a/drivers/video/msm/vidc/common/vcd/vcd.h b/drivers/video/msm/vidc/common/vcd/vcd.h
new file mode 100644
index 0000000..3e02030
--- /dev/null
+++ b/drivers/video/msm/vidc/common/vcd/vcd.h
@@ -0,0 +1,400 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef _VCD_H_
+#define _VCD_H_
+
+#include <media/msm/vcd_api.h>
+#include "vcd_util.h"
+#include "vcd_ddl_api.h"
+#include "vcd_res_tracker_api.h"
+#include "vcd_client_sm.h"
+#include "vcd_core.h"
+#include "vcd_device_sm.h"
+
+void vcd_reset_device_channels(struct vcd_dev_ctxt *dev_ctxt);
+
+u32 vcd_get_command_channel
+    (struct vcd_dev_ctxt *dev_ctxt, struct vcd_transc **transc);
+
+u32 vcd_get_command_channel_in_loop
+    (struct vcd_dev_ctxt *dev_ctxt, struct vcd_transc **transc);
+
+void vcd_mark_command_channel
+    (struct vcd_dev_ctxt *dev_ctxt, struct vcd_transc *transc);
+
+void vcd_release_command_channel
+    (struct vcd_dev_ctxt *dev_ctxt, struct vcd_transc *transc);
+
+void vcd_release_multiple_command_channels(struct vcd_dev_ctxt *dev_ctxt,
+		u32 channels);
+
+void vcd_release_interim_command_channels(struct vcd_dev_ctxt *dev_ctxt);
+
+u32 vcd_get_frame_channel
+    (struct vcd_dev_ctxt *dev_ctxt, struct vcd_transc **transc);
+
+u32 vcd_get_frame_channel_in_loop
+    (struct vcd_dev_ctxt *dev_ctxt, struct vcd_transc **transc);
+
+void vcd_mark_frame_channel(struct vcd_dev_ctxt *dev_ctxt);
+
+void vcd_release_frame_channel
+    (struct vcd_dev_ctxt *dev_ctxt, struct vcd_transc *transc);
+
+void vcd_release_multiple_frame_channels(struct vcd_dev_ctxt *dev_ctxt,
+		u32 channels);
+
+void vcd_release_interim_frame_channels(struct vcd_dev_ctxt *dev_ctxt);
+u32 vcd_core_is_busy(struct vcd_dev_ctxt *dev_ctxt);
+
+void vcd_device_timer_start(struct vcd_dev_ctxt *dev_ctxt);
+void vcd_device_timer_stop(struct vcd_dev_ctxt *dev_ctxt);
+
+
+u32 vcd_init_device_context
+    (struct vcd_drv_ctxt *drv_ctxt, u32 ev_code);
+
+u32 vcd_deinit_device_context
+    (struct vcd_drv_ctxt *drv_ctxt, u32 ev_code);
+
+u32 vcd_init_client_context(struct vcd_clnt_ctxt *cctxt);
+
+void vcd_destroy_client_context(struct vcd_clnt_ctxt *cctxt);
+
+u32 vcd_check_for_client_context
+    (struct vcd_dev_ctxt *dev_ctxt, s32 driver_id);
+
+u32 vcd_validate_driver_handle
+    (struct vcd_dev_ctxt *dev_ctxt, s32 driver_handle);
+
+void vcd_handle_for_last_clnt_close
+	(struct vcd_dev_ctxt *dev_ctxt, u32 send_deinit);
+
+u32 vcd_common_allocate_set_buffer
+    (struct vcd_clnt_ctxt *cctxt,
+     enum vcd_buffer_type buffer,
+     u32 buf_size, struct vcd_buffer_pool **buf_pool);
+
+u32 vcd_set_buffer_internal
+    (struct vcd_clnt_ctxt *cctxt,
+     struct vcd_buffer_pool *buf_pool, u8 *buffer, u32 buf_size);
+
+u32 vcd_allocate_buffer_internal
+    (struct vcd_clnt_ctxt *cctxt,
+     struct vcd_buffer_pool *buf_pool,
+     u32 buf_size, u8 **vir_buf_addr, u8 **phy_buf_addr);
+
+u32 vcd_free_one_buffer_internal
+    (struct vcd_clnt_ctxt *cctxt,
+     enum vcd_buffer_type buffer_type, u8 *buffer);
+
+u32 vcd_free_buffers_internal
+    (struct vcd_clnt_ctxt *cctxt,
+     struct vcd_buffer_pool *buf_pool);
+
+u32 vcd_alloc_buffer_pool_entries
+    (struct vcd_buffer_pool *buf_pool,
+     struct vcd_buffer_requirement *buf_req);
+
+void vcd_free_buffer_pool_entries(struct vcd_buffer_pool *buf_pool);
+
+void vcd_flush_in_use_buffer_pool_entries(struct vcd_clnt_ctxt *cctxt,
+	struct vcd_buffer_pool *buf_pool, u32 event);
+
+void vcd_reset_buffer_pool_for_reuse(struct vcd_buffer_pool *buf_pool);
+
+struct vcd_buffer_entry *vcd_get_free_buffer_pool_entry
+    (struct vcd_buffer_pool *pool);
+
+struct vcd_buffer_entry *vcd_find_buffer_pool_entry
+    (struct vcd_buffer_pool *pool, u8 *v_addr);
+
+struct vcd_buffer_entry *vcd_buffer_pool_entry_de_q
+    (struct vcd_buffer_pool *pool);
+
+u32 vcd_buffer_pool_entry_en_q
+    (struct vcd_buffer_pool *pool,
+     struct vcd_buffer_entry *entry);
+
+u32 vcd_check_if_buffer_req_met(struct vcd_clnt_ctxt *cctxt,
+	enum vcd_buffer_type buffer_type);
+
+u32 vcd_client_cmd_en_q
+    (struct vcd_clnt_ctxt *cctxt, enum vcd_command command);
+
+void vcd_client_cmd_flush_and_en_q
+    (struct vcd_clnt_ctxt *cctxt, enum vcd_command command);
+
+u32 vcd_client_cmd_de_q
+    (struct vcd_clnt_ctxt *cctxt, enum vcd_command *command);
+
+u32 vcd_handle_recvd_eos
+    (struct vcd_clnt_ctxt *cctxt,
+     struct vcd_frame_data *input_frame, u32 * pb_eos_handled);
+
+u32 vcd_handle_first_decode_frame(struct vcd_clnt_ctxt *cctxt);
+
+u32 vcd_handle_input_frame
+    (struct vcd_clnt_ctxt *cctxt,
+     struct vcd_frame_data *input_frame);
+
+u32 vcd_store_seq_hdr
+    (struct vcd_clnt_ctxt *cctxt,
+     struct vcd_sequence_hdr *seq_hdr);
+
+u32 vcd_set_frame_size
+    (struct vcd_clnt_ctxt *cctxt,
+     struct vcd_property_frame_size *frm_size);
+
+u32 vcd_set_frame_rate
+    (struct vcd_clnt_ctxt *cctxt,
+     struct vcd_property_frame_rate *fps);
+
+u32 vcd_calculate_frame_delta
+    (struct vcd_clnt_ctxt *cctxt, struct vcd_frame_data *frame);
+
+struct vcd_buffer_entry *vcd_check_fill_output_buffer
+    (struct vcd_clnt_ctxt *cctxt,
+     struct vcd_frame_data *buffer);
+
+u32 vcd_handle_first_fill_output_buffer
+    (struct vcd_clnt_ctxt *cctxt,
+     struct vcd_frame_data *buffer, u32 *b_handled);
+
+u32 vcd_handle_first_fill_output_buffer_for_enc
+    (struct vcd_clnt_ctxt *cctxt,
+     struct vcd_frame_data *frm_entry, u32 *b_handled);
+
+u32 vcd_handle_first_fill_output_buffer_for_dec
+    (struct vcd_clnt_ctxt *cctxt,
+     struct vcd_frame_data *frm_entry, u32 *b_handled);
+
+u32 vcd_schedule_frame(struct vcd_dev_ctxt *dev_ctxt,
+	struct vcd_clnt_ctxt **cctxt, struct vcd_buffer_entry
+	**ip_buf_entry);
+
+u32 vcd_submit_command_in_continue
+    (struct vcd_dev_ctxt *dev_ctxt, struct vcd_transc *transc);
+
+u32 vcd_submit_cmd_sess_start(struct vcd_transc *transc);
+
+u32 vcd_submit_cmd_sess_end(struct vcd_transc *transc);
+
+void vcd_submit_cmd_client_close(struct vcd_clnt_ctxt *cctxt);
+
+u32 vcd_submit_frame
+    (struct vcd_dev_ctxt *dev_ctxt, struct vcd_transc *transc);
+
+u32 vcd_try_submit_frame_in_continue(struct vcd_dev_ctxt *dev_ctxt,
+	struct vcd_transc *transc);
+
+u32 vcd_process_cmd_sess_start(struct vcd_clnt_ctxt *cctxt);
+
+void vcd_try_submit_frame(struct vcd_dev_ctxt *dev_ctxt);
+
+u32 vcd_setup_with_ddl_capabilities(struct vcd_dev_ctxt *dev_ctxt);
+void vcd_handle_submit_frame_failed(struct vcd_dev_ctxt *dev_ctxt,
+	struct vcd_transc *transc);
+
+struct vcd_transc *vcd_get_free_trans_tbl_entry
+    (struct vcd_dev_ctxt *dev_ctxt);
+
+void vcd_release_trans_tbl_entry(struct vcd_transc *trans_entry);
+
+void vcd_release_all_clnt_frm_transc(struct vcd_clnt_ctxt *cctxt);
+void vcd_release_all_clnt_transc(struct vcd_clnt_ctxt *cctxt);
+
+u32 vcd_handle_input_done
+    (struct vcd_clnt_ctxt *cctxt,
+     void *payload, u32 event, u32 status);
+
+u32 vcd_handle_input_done_in_eos
+    (struct vcd_clnt_ctxt *cctxt, void *payload, u32 status);
+
+void vcd_handle_input_done_failed
+    (struct vcd_clnt_ctxt *cctxt, struct vcd_transc *transc);
+
+void vcd_handle_input_done_with_codec_config
+	(struct vcd_clnt_ctxt *cctxt,
+	struct vcd_transc *transc,
+	struct ddl_frame_data_tag *frm);
+
+void vcd_handle_input_done_for_interlacing
+    (struct vcd_clnt_ctxt *cctxt);
+
+void vcd_handle_input_done_with_trans_end
+    (struct vcd_clnt_ctxt *cctxt);
+
+u32 vcd_handle_frame_done
+    (struct vcd_clnt_ctxt *cctxt,
+     void *payload, u32 event, u32 status);
+
+void vcd_handle_frame_done_for_interlacing
+    (struct vcd_clnt_ctxt *cctxt,
+     struct vcd_transc *transc_ip1,
+     struct ddl_frame_data_tag *op_frm, u32 status);
+
+
+u32 vcd_handle_frame_done_in_eos
+    (struct vcd_clnt_ctxt *cctxt, void *payload, u32 status);
+
+u32 vcd_handle_output_required(struct vcd_clnt_ctxt *cctxt,
+	void *payload, u32 status);
+
+u32 vcd_handle_output_required_in_flushing(struct vcd_clnt_ctxt *cctxt,
+	void *payload);
+
+u32 vcd_handle_output_req_tran_end_in_eos(struct vcd_clnt_ctxt *cctxt);
+
+u32 vcd_validate_io_done_pyld
+	(struct vcd_clnt_ctxt *cctxt, void *payload, u32 status);
+
+void vcd_handle_eos_trans_end(struct vcd_clnt_ctxt *cctxt);
+
+
+void vcd_handle_eos_done
+    (struct vcd_clnt_ctxt *cctxt,
+     struct vcd_transc *transc, u32 status);
+
+void vcd_send_frame_done_in_eos
+    (struct vcd_clnt_ctxt *cctxt,
+     struct vcd_frame_data *input_frame, u32 valid_opbuf);
+
+void vcd_send_frame_done_in_eos_for_dec
+    (struct vcd_clnt_ctxt *cctxt,
+     struct vcd_frame_data *input_frame);
+
+void vcd_send_frame_done_in_eos_for_enc
+    (struct vcd_clnt_ctxt *cctxt,
+     struct vcd_frame_data *input_frame);
+
+void vcd_handle_start_done(struct vcd_clnt_ctxt *cctxt,
+	struct vcd_transc *transc, u32 status);
+
+void vcd_handle_stop_done(struct vcd_clnt_ctxt *cctxt,
+	struct vcd_transc *transc, u32 status);
+
+void vcd_handle_stop_done_in_starting(struct vcd_clnt_ctxt *cctxt,
+	struct vcd_transc *transc, u32 status);
+
+void vcd_handle_stop_done_in_invalid(struct vcd_clnt_ctxt *cctxt,
+	struct vcd_transc *transc, u32 status);
+
+void vcd_send_flush_done(struct vcd_clnt_ctxt *cctxt, u32 status);
+
+void vcd_process_pending_flush_in_eos(struct vcd_clnt_ctxt *cctxt);
+
+void vcd_process_pending_stop_in_eos(struct vcd_clnt_ctxt *cctxt);
+
+void vcd_handle_trans_pending(struct vcd_clnt_ctxt *cctxt);
+
+u32 vcd_handle_ind_output_reconfig
+    (struct vcd_clnt_ctxt *cctxt, void* payload, u32 status);
+
+u32 vcd_handle_ind_output_reconfig_in_flushing
+    (struct vcd_clnt_ctxt *cctxt, void* payload, u32 status);
+
+void vcd_flush_output_buffers(struct vcd_clnt_ctxt *cctxt);
+
+void vcd_flush_bframe_buffers(struct vcd_clnt_ctxt *cctxt, u32 mode);
+
+u32 vcd_flush_buffers(struct vcd_clnt_ctxt *cctxt, u32 mode);
+void vcd_flush_buffers_in_err_fatal(struct vcd_clnt_ctxt *cctxt);
+
+u32 vcd_power_event
+    (struct vcd_dev_ctxt *dev_ctxt,
+     struct vcd_clnt_ctxt *cctxt, u32 event);
+
+u32 vcd_device_power_event(struct vcd_dev_ctxt *dev_ctxt, u32 event,
+	struct vcd_clnt_ctxt *cctxt);
+
+u32 vcd_client_power_event
+    (struct vcd_dev_ctxt *dev_ctxt,
+     struct vcd_clnt_ctxt *cctxt, u32 event);
+
+u32 vcd_enable_clock(struct vcd_dev_ctxt *dev_ctxt,
+	struct vcd_clnt_ctxt *cctxt);
+
+u32 vcd_disable_clock(struct vcd_dev_ctxt *dev_ctxt);
+
+u32 vcd_set_perf_level(struct vcd_dev_ctxt *dev_ctxt, u32 perf_lvl);
+
+u32 vcd_update_clnt_perf_lvl
+    (struct vcd_clnt_ctxt *cctxt,
+     struct vcd_property_frame_rate *fps, u32 frm_p_units);
+
+u32 vcd_gate_clock(struct vcd_dev_ctxt *dev_ctxt);
+
+u32 vcd_un_gate_clock(struct vcd_dev_ctxt *dev_ctxt);
+
+void vcd_handle_err_fatal(struct vcd_clnt_ctxt *cctxt,
+		u32 event, u32 status);
+
+void vcd_handle_device_err_fatal(struct vcd_dev_ctxt *dev_ctxt,
+		struct vcd_clnt_ctxt *cctxt);
+
+void vcd_clnt_handle_device_err_fatal(struct vcd_clnt_ctxt *cctxt,
+		u32 event);
+
+void vcd_handle_err_in_starting(struct vcd_clnt_ctxt *cctxt,
+		u32 status);
+
+void vcd_handle_ind_hw_err_fatal(struct vcd_clnt_ctxt *cctxt,
+		u32 event, u32 status);
+
+u32 vcd_return_op_buffer_to_hw(struct vcd_clnt_ctxt *cctxt,
+	struct vcd_buffer_entry *buf_entry);
+
+u32 vcd_sched_create(struct list_head *sched_list);
+
+void vcd_sched_destroy(struct list_head *sched_clnt_list);
+
+u32 vcd_sched_add_client(struct vcd_clnt_ctxt *cctxt);
+
+u32 vcd_sched_remove_client(struct vcd_sched_clnt_ctx *sched_cctxt);
+
+u32 vcd_sched_update_config(struct vcd_clnt_ctxt *cctxt);
+
+u32 vcd_sched_queue_buffer(
+	struct vcd_sched_clnt_ctx *sched_cctxt,
+	struct vcd_buffer_entry *buffer, u32 b_tail);
+
+u32 vcd_sched_dequeue_buffer(
+	struct vcd_sched_clnt_ctx *sched_cctxt,
+	struct vcd_buffer_entry **buffer);
+
+u32 vcd_sched_mark_client_eof(struct vcd_sched_clnt_ctx *sched_cctxt);
+
+u32 vcd_sched_suspend_resume_clnt(
+	struct vcd_clnt_ctxt *cctxt, u32 b_state);
+
+u32 vcd_sched_get_client_frame(struct list_head *sched_clnt_list,
+	struct vcd_clnt_ctxt **cctxt,
+	struct vcd_buffer_entry **buffer);
+
+void vcd_handle_clnt_fatal(struct vcd_clnt_ctxt *cctxt, u32 trans_end);
+
+void vcd_handle_clnt_fatal_input_done(struct vcd_clnt_ctxt *cctxt,
+	u32 trans_end);
+
+void vcd_handle_ind_info_output_reconfig
+	(struct vcd_clnt_ctxt *cctxt, u32 status);
+
+u32 vcd_req_perf_level(struct vcd_clnt_ctxt *cctxt,
+	struct vcd_property_perf_level *);
+
+u32 vcd_set_num_slices(struct vcd_clnt_ctxt *cctxt);
+
+u32 vcd_update_decoder_perf_level(struct vcd_dev_ctxt *dev_ctxt, u32 perf_lvl);
+
+#endif
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_api.c b/drivers/video/msm/vidc/common/vcd/vcd_api.c
new file mode 100644
index 0000000..c66c2b7
--- /dev/null
+++ b/drivers/video/msm/vidc/common/vcd/vcd_api.c
@@ -0,0 +1,986 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/export.h>
+#include <media/msm/vidc_type.h>
+#include "vcd.h"
+
+u32 vcd_init(struct vcd_init_config *config, s32 *driver_handle)
+{
+	u32 rc = VCD_S_SUCCESS;
+	struct vcd_drv_ctxt *drv_ctxt;
+
+	VCD_MSG_MED("vcd_init:");
+
+	if (!config ||
+	    !driver_handle || !config->map_dev_base_addr) {
+		VCD_MSG_ERROR("Bad parameters");
+
+		return VCD_ERR_ILLEGAL_PARM;
+	}
+
+	drv_ctxt = vcd_get_drv_context();
+	mutex_init(&drv_ctxt->dev_mutex);
+	mutex_lock(&drv_ctxt->dev_mutex);
+
+	if (drv_ctxt->dev_state.state_table->ev_hdlr.init) {
+		rc = drv_ctxt->dev_state.state_table->ev_hdlr.
+		    init(drv_ctxt, config, driver_handle);
+	} else {
+		VCD_MSG_ERROR("Unsupported API in device state %d",
+			      drv_ctxt->dev_state.state);
+
+		rc = VCD_ERR_BAD_STATE;
+	}
+
+	mutex_unlock(&drv_ctxt->dev_mutex);
+
+	return rc;
+
+}
+EXPORT_SYMBOL(vcd_init);
+
+u32 vcd_term(s32 driver_handle)
+{
+	u32 rc = VCD_S_SUCCESS;
+	struct vcd_drv_ctxt *drv_ctxt;
+
+	VCD_MSG_MED("vcd_term:");
+
+	drv_ctxt = vcd_get_drv_context();
+	mutex_lock(&drv_ctxt->dev_mutex);
+
+	if (drv_ctxt->dev_state.state_table->ev_hdlr.term) {
+		rc = drv_ctxt->dev_state.state_table->ev_hdlr.
+		    term(drv_ctxt, driver_handle);
+	} else {
+		VCD_MSG_ERROR("Unsupported API in device state %d",
+			      drv_ctxt->dev_state.state);
+
+		rc = VCD_ERR_BAD_STATE;
+	}
+	mutex_unlock(&drv_ctxt->dev_mutex);
+	return rc;
+
+}
+EXPORT_SYMBOL(vcd_term);
+
+struct client_security_info {
+	int secure_enc;
+	int secure_dec;
+	int non_secure_enc;
+	int non_secure_dec;
+};
+
+static int vcd_get_clients_security_info(struct client_security_info *sec_info)
+{
+	struct vcd_drv_ctxt *drv_ctxt;
+	struct vcd_clnt_ctxt *cctxt;
+	int count = 0;
+	if (!sec_info) {
+		VCD_MSG_ERROR("Invalid argument\n");
+		return -EINVAL;
+	}
+	memset(sec_info, 0 , sizeof(*sec_info));
+	drv_ctxt = vcd_get_drv_context();
+	mutex_lock(&drv_ctxt->dev_mutex);
+	cctxt = drv_ctxt->dev_ctxt.cctxt_list_head;
+	while (cctxt) {
+		if (cctxt->secure && cctxt->decoding)
+			sec_info->secure_dec++;
+		else if (cctxt->secure && !cctxt->decoding)
+			sec_info->secure_enc++;
+		else if (!cctxt->secure && cctxt->decoding)
+			sec_info->non_secure_dec++;
+		else
+			sec_info->non_secure_enc++;
+		count++;
+		cctxt = cctxt->next;
+	}
+	mutex_unlock(&drv_ctxt->dev_mutex);
+	return count;
+}
+
+static int is_session_invalid(u32 decoding, u32 flags)
+{
+	int is_secure;
+	struct client_security_info sec_info;
+	int client_count = 0;
+	int secure_session_running = 0;
+	is_secure = (flags & VCD_CP_SESSION) ? 1 : 0;
+	client_count = vcd_get_clients_security_info(&sec_info);
+	secure_session_running = (sec_info.secure_enc > 0) ||
+			(sec_info.secure_dec > 0);
+	if (!decoding && is_secure) {
+		if ((sec_info.secure_dec == 1))
+			VCD_MSG_LOW("SE-SD: SUCCESS\n");
+		else {
+			VCD_MSG_LOW("SE is permitted only with SD: FAILURE\n");
+			return -EACCES;
+		}
+	} else if (!decoding && !is_secure) {
+		if (secure_session_running) {
+			VCD_MSG_LOW("SD-NSE: FAILURE\n");
+			VCD_MSG_LOW("SE-NSE: FAILURE\n");
+			return -EACCES;
+		}
+	} else if (decoding && is_secure) {
+		if (client_count > 0) {
+			VCD_MSG_LOW("S/NS-SD: FAILURE\n");
+			if (sec_info.secure_enc > 0 ||
+				sec_info.non_secure_enc > 0) {
+				return -EAGAIN;
+			}
+			return -EACCES;
+		}
+	} else {
+		if (sec_info.secure_dec > 0) {
+			VCD_MSG_LOW("SD-NSD: FAILURE\n");
+			return -EACCES;
+		}
+	}
+	return 0;
+}
+
+u32 vcd_open(s32 driver_handle, u32 decoding,
+	void (*callback) (u32 event, u32 status, void *info, size_t sz,
+		       void *handle, void *const client_data),
+	void *client_data, int flags)
+{
+	u32 rc = 0;
+	struct vcd_drv_ctxt *drv_ctxt;
+	struct vcd_clnt_ctxt *cctxt;
+	int is_secure = (flags & VCD_CP_SESSION) ? 1 : 0;
+	VCD_MSG_MED("vcd_open:");
+
+	if (!callback) {
+		VCD_MSG_ERROR("Bad parameters");
+		return -EINVAL;
+	}
+	rc = is_session_invalid(decoding, flags);
+	if (rc) {
+		VCD_MSG_ERROR("Invalid Session: is_decoder: %d, secure: %d\n",
+				decoding, flags);
+		return rc;
+	}
+	if (is_secure)
+		res_trk_secure_set();
+	drv_ctxt = vcd_get_drv_context();
+	mutex_lock(&drv_ctxt->dev_mutex);
+
+	if (drv_ctxt->dev_state.state_table->ev_hdlr.open) {
+		rc = drv_ctxt->dev_state.state_table->ev_hdlr.
+		    open(drv_ctxt, driver_handle, decoding, callback,
+			    client_data);
+		if (rc) {
+			rc = -ENODEV;
+		}
+	} else {
+		VCD_MSG_ERROR("Unsupported API in device state %d",
+			      drv_ctxt->dev_state.state);
+		rc = -EPERM;
+	}
+	if (!rc) {
+		cctxt = drv_ctxt->dev_ctxt.cctxt_list_head;
+		cctxt->secure = is_secure;
+	} else if (is_secure)
+		res_trk_secure_unset();
+
+	mutex_unlock(&drv_ctxt->dev_mutex);
+	return rc;
+}
+EXPORT_SYMBOL(vcd_open);
+
+u32 vcd_close(void *handle)
+{
+	struct vcd_clnt_ctxt *cctxt =
+	    (struct vcd_clnt_ctxt *)handle;
+	struct vcd_drv_ctxt *drv_ctxt;
+	u32 rc;
+	int is_secure = 0;
+	VCD_MSG_MED("vcd_close:");
+
+	if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+		VCD_MSG_ERROR("Bad client handle");
+
+		return VCD_ERR_BAD_HANDLE;
+	}
+
+	is_secure = cctxt->secure;
+	drv_ctxt = vcd_get_drv_context();
+	mutex_lock(&drv_ctxt->dev_mutex);
+	if (drv_ctxt->dev_state.state_table->ev_hdlr.close) {
+		rc = drv_ctxt->dev_state.state_table->ev_hdlr.
+		    close(drv_ctxt, cctxt);
+	} else {
+		VCD_MSG_ERROR("Unsupported API in device state %d",
+			      drv_ctxt->dev_state.state);
+
+		rc = VCD_ERR_BAD_STATE;
+	}
+	mutex_unlock(&drv_ctxt->dev_mutex);
+	if (is_secure)
+		res_trk_secure_unset();
+	return rc;
+
+}
+EXPORT_SYMBOL(vcd_close);
+
+u32 vcd_encode_start(void *handle)
+{
+	struct vcd_clnt_ctxt *cctxt =
+	    (struct vcd_clnt_ctxt *)handle;
+	struct vcd_drv_ctxt *drv_ctxt;
+	u32 rc;
+
+	VCD_MSG_MED("vcd_encode_start:");
+
+	if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+		VCD_MSG_ERROR("Bad client handle");
+
+		return VCD_ERR_BAD_HANDLE;
+	}
+
+	drv_ctxt = vcd_get_drv_context();
+
+	mutex_lock(&drv_ctxt->dev_mutex);
+
+	if (cctxt->clnt_state.state_table->ev_hdlr.encode_start &&
+	    drv_ctxt->dev_ctxt.pwr_state != VCD_PWR_STATE_SLEEP) {
+		rc = cctxt->clnt_state.state_table->ev_hdlr.
+		    encode_start(cctxt);
+	} else {
+		VCD_MSG_ERROR
+		    ("Unsupported API in dev power state %d OR client state %d",
+		     drv_ctxt->dev_ctxt.pwr_state,
+		     cctxt->clnt_state.state);
+
+		rc = VCD_ERR_BAD_STATE;
+	}
+
+	mutex_unlock(&drv_ctxt->dev_mutex);
+
+	return rc;
+
+}
+EXPORT_SYMBOL(vcd_encode_start);
+
+u32 vcd_encode_frame(void *handle, struct vcd_frame_data *input_frame)
+{
+	struct vcd_clnt_ctxt *cctxt =
+	    (struct vcd_clnt_ctxt *)handle;
+	struct vcd_drv_ctxt *drv_ctxt;
+	u32 rc;
+
+	VCD_MSG_MED("vcd_encode_frame:");
+
+	if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+		VCD_MSG_ERROR("Bad client handle");
+
+		return VCD_ERR_BAD_HANDLE;
+	}
+
+	if (!input_frame) {
+		VCD_MSG_ERROR("Bad parameters");
+
+		return VCD_ERR_BAD_POINTER;
+	}
+
+	drv_ctxt = vcd_get_drv_context();
+
+	mutex_lock(&drv_ctxt->dev_mutex);
+
+	if (cctxt->clnt_state.state_table->ev_hdlr.encode_frame) {
+		rc = cctxt->clnt_state.state_table->ev_hdlr.
+		    encode_frame(cctxt, input_frame);
+	} else {
+		VCD_MSG_ERROR("Unsupported API in client state %d",
+			      cctxt->clnt_state.state);
+
+		rc = VCD_ERR_BAD_STATE;
+	}
+
+	mutex_unlock(&drv_ctxt->dev_mutex);
+
+	return rc;
+
+}
+EXPORT_SYMBOL(vcd_encode_frame);
+
+u32 vcd_decode_start(void *handle, struct vcd_sequence_hdr *seq_hdr)
+{
+	struct vcd_clnt_ctxt *cctxt =
+	    (struct vcd_clnt_ctxt *)handle;
+	struct vcd_drv_ctxt *drv_ctxt;
+	u32 rc;
+
+	VCD_MSG_MED("vcd_decode_start:");
+
+	if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+		VCD_MSG_ERROR("Bad client handle");
+
+		return VCD_ERR_BAD_HANDLE;
+	}
+
+	drv_ctxt = vcd_get_drv_context();
+
+	mutex_lock(&drv_ctxt->dev_mutex);
+
+	if (cctxt->clnt_state.state_table->ev_hdlr.decode_start &&
+	    drv_ctxt->dev_ctxt.pwr_state != VCD_PWR_STATE_SLEEP) {
+		rc = cctxt->clnt_state.state_table->ev_hdlr.
+		    decode_start(cctxt, seq_hdr);
+	} else {
+		VCD_MSG_ERROR
+		    ("Unsupported API in dev power state %d OR client state %d",
+		     drv_ctxt->dev_ctxt.pwr_state,
+		     cctxt->clnt_state.state);
+
+		rc = VCD_ERR_BAD_STATE;
+	}
+
+	mutex_unlock(&drv_ctxt->dev_mutex);
+
+	return rc;
+
+}
+EXPORT_SYMBOL(vcd_decode_start);
+
+u32 vcd_decode_frame(void *handle, struct vcd_frame_data *input_frame)
+{
+	struct vcd_clnt_ctxt *cctxt =
+	    (struct vcd_clnt_ctxt *)handle;
+	struct vcd_drv_ctxt *drv_ctxt;
+	u32 rc;
+
+	VCD_MSG_MED("vcd_decode_frame:");
+
+	if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+		VCD_MSG_ERROR("Bad client handle");
+
+		return VCD_ERR_BAD_HANDLE;
+	}
+
+	if (!input_frame) {
+		VCD_MSG_ERROR("Bad parameters");
+
+		return VCD_ERR_BAD_POINTER;
+	}
+
+	drv_ctxt = vcd_get_drv_context();
+
+	mutex_lock(&drv_ctxt->dev_mutex);
+
+	if (cctxt->clnt_state.state_table->ev_hdlr.decode_frame) {
+		rc = cctxt->clnt_state.state_table->ev_hdlr.
+		    decode_frame(cctxt, input_frame);
+	} else {
+		VCD_MSG_ERROR("Unsupported API in client state %d",
+			      cctxt->clnt_state.state);
+
+		rc = VCD_ERR_BAD_STATE;
+	}
+
+	mutex_unlock(&drv_ctxt->dev_mutex);
+
+	return rc;
+
+}
+EXPORT_SYMBOL(vcd_decode_frame);
+
+u32 vcd_pause(void *handle)
+{
+	struct vcd_drv_ctxt *drv_ctxt;
+	struct vcd_clnt_ctxt *cctxt =
+	    (struct vcd_clnt_ctxt *)handle;
+	u32 rc;
+
+	VCD_MSG_MED("vcd_pause:");
+
+	if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+		VCD_MSG_ERROR("Bad client handle");
+
+		return VCD_ERR_BAD_HANDLE;
+	}
+
+	drv_ctxt = vcd_get_drv_context();
+
+	mutex_lock(&drv_ctxt->dev_mutex);
+
+	if (cctxt->clnt_state.state_table->ev_hdlr.pause) {
+		rc = cctxt->clnt_state.state_table->ev_hdlr.
+		    pause(cctxt);
+	} else {
+		VCD_MSG_ERROR("Unsupported API in client state %d",
+			      cctxt->clnt_state.state);
+
+		rc = VCD_ERR_BAD_STATE;
+	}
+
+	mutex_unlock(&drv_ctxt->dev_mutex);
+
+	return rc;
+
+}
+EXPORT_SYMBOL(vcd_pause);
+
+u32 vcd_resume(void *handle)
+{
+	struct vcd_drv_ctxt *drv_ctxt;
+	struct vcd_clnt_ctxt *cctxt =
+	    (struct vcd_clnt_ctxt *)handle;
+	u32 rc;
+
+	VCD_MSG_MED("vcd_resume:");
+
+	if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+		VCD_MSG_ERROR("Bad client handle");
+
+		return VCD_ERR_BAD_HANDLE;
+	}
+
+	drv_ctxt = vcd_get_drv_context();
+
+	mutex_lock(&drv_ctxt->dev_mutex);
+
+	if (drv_ctxt->dev_state.state_table->ev_hdlr.resume &&
+	    drv_ctxt->dev_ctxt.pwr_state != VCD_PWR_STATE_SLEEP) {
+		rc = drv_ctxt->dev_state.state_table->ev_hdlr.
+		    resume(drv_ctxt, cctxt);
+	} else {
+		VCD_MSG_ERROR
+		    ("Unsupported API in dev power state %d OR client state %d",
+		     drv_ctxt->dev_ctxt.pwr_state,
+		     cctxt->clnt_state.state);
+
+		rc = VCD_ERR_BAD_STATE;
+	}
+
+	mutex_unlock(&drv_ctxt->dev_mutex);
+
+	return rc;
+
+}
+EXPORT_SYMBOL(vcd_resume);
+
+u32 vcd_flush(void *handle, u32 mode)
+{
+	struct vcd_clnt_ctxt *cctxt =
+	    (struct vcd_clnt_ctxt *)handle;
+	struct vcd_drv_ctxt *drv_ctxt;
+	u32 rc;
+
+	VCD_MSG_MED("vcd_flush:");
+
+	if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+		VCD_MSG_ERROR("Bad client handle");
+
+		return VCD_ERR_BAD_HANDLE;
+	}
+
+	drv_ctxt = vcd_get_drv_context();
+
+	mutex_lock(&drv_ctxt->dev_mutex);
+
+	if (cctxt->clnt_state.state_table->ev_hdlr.flush) {
+		rc = cctxt->clnt_state.state_table->ev_hdlr.
+		    flush(cctxt, mode);
+	} else {
+		VCD_MSG_ERROR("Unsupported API in client state %d",
+			      cctxt->clnt_state.state);
+
+		rc = VCD_ERR_BAD_STATE;
+	}
+
+	mutex_unlock(&drv_ctxt->dev_mutex);
+
+	return rc;
+
+}
+EXPORT_SYMBOL(vcd_flush);
+
+u32 vcd_stop(void *handle)
+{
+	struct vcd_clnt_ctxt *cctxt =
+	    (struct vcd_clnt_ctxt *)handle;
+	struct vcd_drv_ctxt *drv_ctxt;
+	u32 rc;
+
+	VCD_MSG_MED("vcd_stop:");
+
+	if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+		VCD_MSG_ERROR("Bad client handle");
+
+		return VCD_ERR_BAD_HANDLE;
+	}
+
+	drv_ctxt = vcd_get_drv_context();
+
+	mutex_lock(&drv_ctxt->dev_mutex);
+
+	if (cctxt->clnt_state.state_table->ev_hdlr.stop &&
+	    drv_ctxt->dev_ctxt.pwr_state != VCD_PWR_STATE_SLEEP) {
+		rc = cctxt->clnt_state.state_table->ev_hdlr.
+		    stop(cctxt);
+	} else {
+		VCD_MSG_ERROR
+		    ("Unsupported API in dev power state %d OR client state %d",
+		     drv_ctxt->dev_ctxt.pwr_state,
+		     cctxt->clnt_state.state);
+
+		rc = VCD_ERR_BAD_STATE;
+	}
+
+	mutex_unlock(&drv_ctxt->dev_mutex);
+
+	return rc;
+
+}
+EXPORT_SYMBOL(vcd_stop);
+
+u32 vcd_set_property(void *handle,
+     struct vcd_property_hdr *prop_hdr, void *prop_val)
+{
+	struct vcd_clnt_ctxt *cctxt =
+	    (struct vcd_clnt_ctxt *)handle;
+	struct vcd_drv_ctxt *drv_ctxt;
+	u32 rc;
+
+	VCD_MSG_MED("vcd_set_property:");
+
+	if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+		VCD_MSG_ERROR("Bad client handle");
+
+		return VCD_ERR_BAD_HANDLE;
+	}
+
+	if (!prop_hdr || !prop_val) {
+		VCD_MSG_ERROR("Bad parameters");
+
+		return VCD_ERR_BAD_POINTER;
+	}
+
+	drv_ctxt = vcd_get_drv_context();
+
+	mutex_lock(&drv_ctxt->dev_mutex);
+
+	if (cctxt->clnt_state.state_table->ev_hdlr.set_property) {
+		rc = cctxt->clnt_state.state_table->ev_hdlr.
+		    set_property(cctxt, prop_hdr, prop_val);
+	} else {
+		VCD_MSG_ERROR("Unsupported API in client state %d",
+			      cctxt->clnt_state.state);
+
+		rc = VCD_ERR_BAD_STATE;
+	}
+
+	mutex_unlock(&drv_ctxt->dev_mutex);
+
+	return rc;
+
+}
+EXPORT_SYMBOL(vcd_set_property);
+
+u32 vcd_get_property(void *handle,
+     struct vcd_property_hdr *prop_hdr, void *prop_val)
+{
+	struct vcd_clnt_ctxt *cctxt =
+	    (struct vcd_clnt_ctxt *)handle;
+	struct vcd_drv_ctxt *drv_ctxt;
+	u32 rc;
+
+	VCD_MSG_MED("vcd_get_property:");
+
+	if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+		VCD_MSG_ERROR("Bad client handle");
+
+		return VCD_ERR_BAD_HANDLE;
+	}
+
+	if (!prop_hdr || !prop_val) {
+		VCD_MSG_ERROR("Bad parameters");
+
+		return VCD_ERR_BAD_POINTER;
+	}
+
+	drv_ctxt = vcd_get_drv_context();
+
+	mutex_lock(&drv_ctxt->dev_mutex);
+
+	if (cctxt->clnt_state.state_table->ev_hdlr.get_property) {
+		rc = cctxt->clnt_state.state_table->ev_hdlr.
+		    get_property(cctxt, prop_hdr, prop_val);
+	} else {
+		VCD_MSG_ERROR("Unsupported API in client state %d",
+			      cctxt->clnt_state.state);
+
+		rc = VCD_ERR_BAD_STATE;
+	}
+
+	mutex_unlock(&drv_ctxt->dev_mutex);
+
+	return rc;
+
+}
+EXPORT_SYMBOL(vcd_get_property);
+
+u32 vcd_set_buffer_requirements(void *handle,
+     enum vcd_buffer_type buffer,
+     struct vcd_buffer_requirement *buffer_req)
+{
+	struct vcd_clnt_ctxt *cctxt =
+	    (struct vcd_clnt_ctxt *)handle;
+	struct vcd_drv_ctxt *drv_ctxt;
+	u32 rc;
+
+	VCD_MSG_MED("vcd_set_buffer_requirements:");
+
+	if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+		VCD_MSG_ERROR("Bad client handle");
+
+		return VCD_ERR_BAD_HANDLE;
+	}
+
+	if (!buffer_req) {
+		VCD_MSG_ERROR("Bad parameters");
+
+		return VCD_ERR_BAD_POINTER;
+	}
+
+	drv_ctxt = vcd_get_drv_context();
+
+	mutex_lock(&drv_ctxt->dev_mutex);
+
+	if (cctxt->clnt_state.state_table->ev_hdlr.
+	    set_buffer_requirements) {
+		rc = cctxt->clnt_state.state_table->ev_hdlr.
+		    set_buffer_requirements(cctxt, buffer, buffer_req);
+	} else {
+		VCD_MSG_ERROR("Unsupported API in client state %d",
+			      cctxt->clnt_state.state);
+
+		rc = VCD_ERR_BAD_STATE;
+	}
+
+	mutex_unlock(&drv_ctxt->dev_mutex);
+
+	return rc;
+
+}
+EXPORT_SYMBOL(vcd_set_buffer_requirements);
+
+u32 vcd_get_buffer_requirements(void *handle,
+     enum vcd_buffer_type buffer,
+     struct vcd_buffer_requirement *buffer_req)
+{
+	struct vcd_clnt_ctxt *cctxt =
+	    (struct vcd_clnt_ctxt *)handle;
+	struct vcd_drv_ctxt *drv_ctxt;
+	u32 rc;
+
+	VCD_MSG_MED("vcd_get_buffer_requirements:");
+
+	if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+		VCD_MSG_ERROR("Bad client handle");
+
+		return VCD_ERR_BAD_HANDLE;
+	}
+
+	if (!buffer_req) {
+		VCD_MSG_ERROR("Bad parameters");
+
+		return VCD_ERR_BAD_POINTER;
+	}
+
+	drv_ctxt = vcd_get_drv_context();
+
+	mutex_lock(&drv_ctxt->dev_mutex);
+
+	if (cctxt->clnt_state.state_table->ev_hdlr.
+	    get_buffer_requirements) {
+		rc = cctxt->clnt_state.state_table->ev_hdlr.
+		    get_buffer_requirements(cctxt, buffer, buffer_req);
+	} else {
+		VCD_MSG_ERROR("Unsupported API in client state %d",
+			      cctxt->clnt_state.state);
+
+		rc = VCD_ERR_BAD_STATE;
+	}
+
+	mutex_unlock(&drv_ctxt->dev_mutex);
+
+	return rc;
+
+}
+EXPORT_SYMBOL(vcd_get_buffer_requirements);
+
+u32 vcd_set_buffer(void *handle,
+     enum vcd_buffer_type buffer_type, u8 *buffer, u32 buf_size)
+{
+	struct vcd_clnt_ctxt *cctxt =
+	    (struct vcd_clnt_ctxt *)handle;
+	struct vcd_drv_ctxt *drv_ctxt;
+	u32 rc;
+
+	VCD_MSG_MED("vcd_set_buffer:");
+
+	if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+		VCD_MSG_ERROR("Bad client handle");
+
+		return VCD_ERR_BAD_HANDLE;
+	}
+
+	if (!buffer || !buf_size) {
+		VCD_MSG_ERROR("Bad parameters");
+
+		return VCD_ERR_BAD_POINTER;
+	}
+
+	drv_ctxt = vcd_get_drv_context();
+
+	mutex_lock(&drv_ctxt->dev_mutex);
+
+	if (cctxt->clnt_state.state_table->ev_hdlr.set_buffer) {
+		rc = cctxt->clnt_state.state_table->ev_hdlr.
+		    set_buffer(cctxt, buffer_type, buffer, buf_size);
+	} else {
+		VCD_MSG_ERROR("Unsupported API in client state %d",
+			      cctxt->clnt_state.state);
+
+		rc = VCD_ERR_BAD_STATE;
+	}
+
+	mutex_unlock(&drv_ctxt->dev_mutex);
+
+	return rc;
+
+}
+EXPORT_SYMBOL(vcd_set_buffer);
+
+u32 vcd_allocate_buffer(void *handle,
+     enum vcd_buffer_type buffer,
+     u32 buf_size, u8 **vir_buf_addr, u8 **phy_buf_addr)
+{
+	struct vcd_clnt_ctxt *cctxt =
+	    (struct vcd_clnt_ctxt *)handle;
+	struct vcd_drv_ctxt *drv_ctxt;
+	u32 rc;
+
+	VCD_MSG_MED("vcd_allocate_buffer:");
+
+	if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+		VCD_MSG_ERROR("Bad client handle");
+
+		return VCD_ERR_BAD_HANDLE;
+	}
+
+	if (!vir_buf_addr || !phy_buf_addr
+	    || !buf_size) {
+		VCD_MSG_ERROR("Bad parameters");
+
+		return VCD_ERR_BAD_POINTER;
+	}
+
+	drv_ctxt = vcd_get_drv_context();
+
+	mutex_lock(&drv_ctxt->dev_mutex);
+
+	if (cctxt->clnt_state.state_table->ev_hdlr.allocate_buffer) {
+		rc = cctxt->clnt_state.state_table->ev_hdlr.
+		    allocate_buffer(cctxt, buffer, buf_size,
+				       vir_buf_addr, phy_buf_addr);
+	} else {
+		VCD_MSG_ERROR("Unsupported API in client state %d",
+			      cctxt->clnt_state.state);
+
+		rc = VCD_ERR_BAD_STATE;
+	}
+
+	mutex_unlock(&drv_ctxt->dev_mutex);
+
+	return rc;
+
+}
+EXPORT_SYMBOL(vcd_allocate_buffer);
+
+u32 vcd_free_buffer(void *handle, enum vcd_buffer_type buffer_type, u8 *buffer)
+{
+	struct vcd_clnt_ctxt *cctxt =
+	    (struct vcd_clnt_ctxt *)handle;
+	struct vcd_drv_ctxt *drv_ctxt;
+	u32 rc;
+
+	VCD_MSG_MED("vcd_free_buffer:");
+
+	if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+		VCD_MSG_ERROR("Bad client handle");
+
+		return VCD_ERR_BAD_HANDLE;
+	}
+
+	drv_ctxt = vcd_get_drv_context();
+
+	mutex_lock(&drv_ctxt->dev_mutex);
+
+	if (cctxt->clnt_state.state_table->ev_hdlr.free_buffer) {
+		rc = cctxt->clnt_state.state_table->ev_hdlr.
+		    free_buffer(cctxt, buffer_type, buffer);
+	} else {
+		VCD_MSG_ERROR("Unsupported API in client state %d",
+			      cctxt->clnt_state.state);
+
+		rc = VCD_ERR_BAD_STATE;
+	}
+
+	mutex_unlock(&drv_ctxt->dev_mutex);
+
+	return rc;
+
+}
+EXPORT_SYMBOL(vcd_free_buffer);
+
+u32 vcd_fill_output_buffer(void *handle, struct vcd_frame_data *buffer)
+{
+	struct vcd_clnt_ctxt *cctxt =
+	    (struct vcd_clnt_ctxt *)handle;
+	struct vcd_drv_ctxt *drv_ctxt;
+	u32 rc;
+
+	VCD_MSG_MED("vcd_fill_output_buffer:");
+
+	if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+		VCD_MSG_ERROR("Bad client handle");
+
+		return VCD_ERR_BAD_HANDLE;
+	}
+
+	if (!buffer) {
+		VCD_MSG_ERROR("Bad parameters");
+
+		return VCD_ERR_BAD_POINTER;
+	}
+
+	drv_ctxt = vcd_get_drv_context();
+
+	mutex_lock(&drv_ctxt->dev_mutex);
+
+	if (cctxt->clnt_state.state_table->ev_hdlr.fill_output_buffer) {
+		rc = cctxt->clnt_state.state_table->ev_hdlr.
+		    fill_output_buffer(cctxt, buffer);
+	} else {
+		VCD_MSG_ERROR("Unsupported API in client state %d",
+			      cctxt->clnt_state.state);
+
+		rc = VCD_ERR_BAD_STATE;
+	}
+
+	mutex_unlock(&drv_ctxt->dev_mutex);
+
+	return rc;
+
+}
+EXPORT_SYMBOL(vcd_fill_output_buffer);
+
+u32 vcd_set_device_power(s32 driver_handle,
+		enum vcd_power_state pwr_state)
+{
+	u32 rc = VCD_S_SUCCESS;
+	struct vcd_drv_ctxt *drv_ctxt;
+
+	VCD_MSG_MED("vcd_set_device_power:");
+
+	drv_ctxt = vcd_get_drv_context();
+	mutex_lock(&drv_ctxt->dev_mutex);
+
+	if (drv_ctxt->dev_state.state_table->ev_hdlr.set_dev_pwr) {
+		rc = drv_ctxt->dev_state.state_table->ev_hdlr.
+		    set_dev_pwr(drv_ctxt, pwr_state);
+	} else {
+		VCD_MSG_ERROR("Unsupported API in device state %d",
+			      drv_ctxt->dev_state.state);
+
+		rc = VCD_ERR_BAD_STATE;
+	}
+
+	mutex_unlock(&drv_ctxt->dev_mutex);
+
+	return rc;
+
+}
+EXPORT_SYMBOL(vcd_set_device_power);
+
+void vcd_read_and_clear_interrupt(void)
+{
+   VCD_MSG_LOW("vcd_read_and_clear_interrupt:");
+   ddl_read_and_clear_interrupt();
+}
+
+
+void vcd_response_handler(void)
+{
+	struct vcd_drv_ctxt *drv_ctxt;
+
+	VCD_MSG_LOW("vcd_response_handler:");
+  drv_ctxt = vcd_get_drv_context();
+
+  mutex_lock(&drv_ctxt->dev_mutex);
+
+	if (!ddl_process_core_response()) {
+		VCD_MSG_HIGH
+		    ("ddl_process_core_response indicated no further"
+		     "processing");
+    mutex_unlock(&drv_ctxt->dev_mutex);
+		return;
+	}
+
+	if (drv_ctxt->dev_ctxt.command_continue)
+		vcd_continue();
+	mutex_unlock(&drv_ctxt->dev_mutex);
+}
+EXPORT_SYMBOL(vcd_response_handler);
+
+u8 vcd_get_num_of_clients(void)
+{
+	struct vcd_drv_ctxt *drv_ctxt;
+	struct vcd_clnt_ctxt *cctxt;
+	u8 count = 0;
+
+	VCD_MSG_LOW("vcd_get_num_of_clients:");
+	drv_ctxt = vcd_get_drv_context();
+
+	mutex_lock(&drv_ctxt->dev_mutex);
+	cctxt = drv_ctxt->dev_ctxt.cctxt_list_head;
+	while (cctxt) {
+		count++;
+		cctxt = cctxt->next;
+	}
+	mutex_unlock(&drv_ctxt->dev_mutex);
+	return count;
+}
+EXPORT_SYMBOL(vcd_get_num_of_clients);
+
+u32 vcd_get_ion_status(void)
+{
+	return res_trk_get_enable_ion();
+}
+EXPORT_SYMBOL(vcd_get_ion_status);
+
+struct ion_client *vcd_get_ion_client(void)
+{
+	return res_trk_get_ion_client();
+}
+EXPORT_SYMBOL(vcd_get_ion_client);
+
+
+
+
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_client_sm.c b/drivers/video/msm/vidc/common/vcd/vcd_client_sm.c
new file mode 100644
index 0000000..5019d31
--- /dev/null
+++ b/drivers/video/msm/vidc/common/vcd/vcd_client_sm.c
@@ -0,0 +1,1884 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <media/msm/vidc_type.h>
+#include "vcd.h"
+
+static const struct vcd_clnt_state_table *vcd_clnt_state_table[];
+
+void vcd_clnt_handle_device_err_fatal(struct vcd_clnt_ctxt *cctxt,
+								  u32 event)
+{
+	if (cctxt->clnt_state.state == VCD_CLIENT_STATE_NULL) {
+		cctxt->callback(VCD_EVT_RESP_OPEN, VCD_ERR_HW_FATAL, NULL, 0,
+			cctxt, cctxt->client_data);
+		vcd_destroy_client_context(cctxt);
+		return;
+	}
+	if (event == VCD_EVT_RESP_BASE)
+		event = VCD_EVT_IND_HWERRFATAL;
+	if (cctxt->clnt_state.state != VCD_CLIENT_STATE_INVALID) {
+		cctxt->callback(event, VCD_ERR_HW_FATAL, NULL, 0,
+			cctxt, cctxt->client_data);
+		vcd_flush_buffers_in_err_fatal(cctxt);
+		vcd_do_client_state_transition(cctxt,
+			VCD_CLIENT_STATE_INVALID,
+			CLIENT_STATE_EVENT_NUMBER(clnt_cb));
+	}
+}
+
+static u32 vcd_close_in_open(struct vcd_clnt_ctxt *cctxt)
+{
+	u32 rc = VCD_S_SUCCESS;
+
+	VCD_MSG_LOW("vcd_close_in_open:");
+	if (cctxt->in_buf_pool.allocated ||
+		 cctxt->out_buf_pool.allocated) {
+		VCD_MSG_ERROR("\n Allocated buffers are not freed yet");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+	vcd_destroy_client_context(cctxt);
+	return rc;
+}
+
+static u32  vcd_close_in_invalid(struct vcd_clnt_ctxt *cctxt)
+{
+	VCD_MSG_LOW("vcd_close_in_invalid:");
+	if (cctxt->in_buf_pool.allocated ||
+		cctxt->out_buf_pool.allocated){
+		VCD_MSG_ERROR("Allocated buffers are not freed yet");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+
+	if (cctxt->status.mask & VCD_CLEANING_UP)
+		cctxt->status.mask |= VCD_CLOSE_PENDING;
+	else
+		vcd_destroy_client_context(cctxt);
+	return VCD_S_SUCCESS;
+}
+
+static u32 vcd_start_in_run_cmn(struct vcd_clnt_ctxt *cctxt)
+{
+	VCD_MSG_LOW("vcd_start_in_run_cmn:");
+	cctxt->callback(VCD_EVT_RESP_START, VCD_S_SUCCESS, NULL, 0,
+					  cctxt, cctxt->client_data);
+	return VCD_S_SUCCESS;
+
+}
+
+static u32 vcd_encode_start_in_open(struct vcd_clnt_ctxt *cctxt)
+{
+	u32 rc = VCD_S_SUCCESS;
+	struct vcd_property_hdr prop_hdr;
+	struct vcd_property_vop_timing timing;
+
+	VCD_MSG_LOW("vcd_encode_start_in_open:");
+
+	if (cctxt->decoding) {
+		VCD_MSG_ERROR("vcd_encode_init for decoder client");
+
+		return VCD_ERR_ILLEGAL_OP;
+	}
+
+	if ((!cctxt->meta_mode && !cctxt->in_buf_pool.entries) ||
+	    !cctxt->out_buf_pool.entries ||
+	    (!cctxt->meta_mode &&
+		 cctxt->in_buf_pool.validated != cctxt->in_buf_pool.count) ||
+	    cctxt->out_buf_pool.validated !=
+	    cctxt->out_buf_pool.count) {
+		VCD_MSG_HIGH("%s: Buffer pool is not completely setup yet",
+			__func__);
+	}
+
+	rc = vcd_sched_add_client(cctxt);
+	VCD_FAILED_RETURN(rc, "Failed: vcd_sched_add_client");
+
+	prop_hdr.prop_id = VCD_I_VOP_TIMING;
+	prop_hdr.sz = sizeof(struct vcd_property_vop_timing);
+	rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr, &timing);
+
+	VCD_FAILED_RETURN(rc, "Failed: Get VCD_I_VOP_TIMING");
+	if (!timing.vop_time_resolution) {
+		VCD_MSG_ERROR("Vop_time_resolution value is zero");
+		return VCD_ERR_FAIL;
+	}
+	cctxt->time_resoln = timing.vop_time_resolution;
+
+	rc = vcd_process_cmd_sess_start(cctxt);
+
+	if (!VCD_FAILED(rc)) {
+		vcd_do_client_state_transition(cctxt,
+					       VCD_CLIENT_STATE_STARTING,
+					       CLIENT_STATE_EVENT_NUMBER
+					       (encode_start));
+	}
+
+	return rc;
+}
+
+static u32  vcd_encode_start_in_run(struct vcd_clnt_ctxt
+	*cctxt)
+{
+	VCD_MSG_LOW("vcd_encode_start_in_run:");
+	(void) vcd_start_in_run_cmn(cctxt);
+	return VCD_S_SUCCESS;
+}
+
+
+static u32 vcd_encode_frame_cmn(struct vcd_clnt_ctxt *cctxt,
+     struct vcd_frame_data *input_frame)
+{
+	VCD_MSG_LOW("vcd_encode_frame_cmn in %d:", cctxt->clnt_state.state);
+
+	if (cctxt->decoding) {
+		VCD_MSG_ERROR("vcd_encode_frame for decoder client");
+
+		return VCD_ERR_ILLEGAL_OP;
+	}
+
+	return vcd_handle_input_frame(cctxt, input_frame);
+}
+
+static u32 vcd_decode_start_in_open
+    (struct vcd_clnt_ctxt *cctxt,
+     struct vcd_sequence_hdr *seq_hdr)
+{
+	u32 rc = VCD_S_SUCCESS;
+
+	VCD_MSG_LOW("vcd_decode_start_in_open:");
+
+	if (!cctxt->decoding) {
+		VCD_MSG_ERROR("vcd_decode_init for encoder client");
+
+		return VCD_ERR_ILLEGAL_OP;
+	}
+
+	if (seq_hdr) {
+		VCD_MSG_HIGH("Seq hdr supplied. len = %d",
+			     seq_hdr->sequence_header_len);
+
+		rc = vcd_store_seq_hdr(cctxt, seq_hdr);
+
+	} else {
+		VCD_MSG_HIGH("Seq hdr not supplied");
+
+		cctxt->seq_hdr.sequence_header_len = 0;
+		cctxt->seq_hdr.sequence_header = NULL;
+	}
+
+	VCD_FAILED_RETURN(rc, "Err processing seq hdr");
+
+	rc = vcd_process_cmd_sess_start(cctxt);
+
+	if (!VCD_FAILED(rc)) {
+		vcd_do_client_state_transition(cctxt,
+					       VCD_CLIENT_STATE_STARTING,
+					       CLIENT_STATE_EVENT_NUMBER
+					       (decode_start));
+	}
+
+	return rc;
+}
+
+static u32 vcd_decode_start_in_run(struct vcd_clnt_ctxt *cctxt,
+	struct vcd_sequence_hdr *seqhdr)
+{
+   VCD_MSG_LOW("vcd_decode_start_in_run:");
+   (void) vcd_start_in_run_cmn(cctxt);
+   return VCD_S_SUCCESS;
+}
+
+static u32 vcd_decode_frame_cmn
+    (struct vcd_clnt_ctxt *cctxt,
+     struct vcd_frame_data *input_frame)
+{
+	VCD_MSG_LOW("vcd_decode_frame_cmn in %d:", cctxt->clnt_state.state);
+
+	if (!cctxt->decoding) {
+		VCD_MSG_ERROR("Decode_frame api called for Encoder client");
+
+		return VCD_ERR_ILLEGAL_OP;
+	}
+
+	return vcd_handle_input_frame(cctxt, input_frame);
+}
+
+static u32 vcd_pause_in_run(struct vcd_clnt_ctxt *cctxt)
+{
+	u32 rc = VCD_S_SUCCESS;
+
+	VCD_MSG_LOW("vcd_pause_in_run:");
+
+	if (cctxt->sched_clnt_hdl) {
+		rc = vcd_sched_suspend_resume_clnt(cctxt, false);
+		VCD_FAILED_RETURN(rc, "Failed: vcd_sched_suspend_resume_clnt");
+	}
+
+	if (cctxt->status.frame_submitted > 0) {
+		vcd_do_client_state_transition(cctxt,
+					       VCD_CLIENT_STATE_PAUSING,
+					       CLIENT_STATE_EVENT_NUMBER
+					       (pause));
+
+	} else {
+		VCD_MSG_HIGH("No client frames are currently being processed");
+
+		vcd_do_client_state_transition(cctxt,
+					       VCD_CLIENT_STATE_PAUSED,
+					       CLIENT_STATE_EVENT_NUMBER
+					       (pause));
+
+		cctxt->callback(VCD_EVT_RESP_PAUSE,
+				  VCD_S_SUCCESS,
+				  NULL, 0, cctxt, cctxt->client_data);
+
+		rc = vcd_power_event(cctxt->dev_ctxt, cctxt,
+				     VCD_EVT_PWR_CLNT_PAUSE);
+
+		if (VCD_FAILED(rc))
+			VCD_MSG_ERROR("VCD_EVT_PWR_CLNT_PAUSE_END failed");
+
+	}
+
+	return VCD_S_SUCCESS;
+}
+
+static u32 vcd_resume_in_paused(struct vcd_clnt_ctxt *cctxt)
+{
+	struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt;
+	u32 rc = VCD_S_SUCCESS;
+
+	VCD_MSG_LOW("vcd_resume_in_paused:");
+
+
+	if (cctxt->sched_clnt_hdl) {
+		rc = vcd_power_event(cctxt->dev_ctxt,
+				     cctxt, VCD_EVT_PWR_CLNT_RESUME);
+
+		if (VCD_FAILED(rc)) {
+			VCD_MSG_ERROR("VCD_EVT_PWR_CLNT_RESUME failed");
+		} else {
+			rc = vcd_sched_suspend_resume_clnt(cctxt, true);
+			if (VCD_FAILED(rc)) {
+				VCD_MSG_ERROR
+				    ("rc = 0x%x. Failed: "
+				     "vcd_sched_suspend_resume_clnt",
+				     rc);
+			}
+
+		}
+		if (!VCD_FAILED(rc)) {
+			vcd_do_client_state_transition(cctxt,
+						       VCD_CLIENT_STATE_RUN,
+						       CLIENT_STATE_EVENT_NUMBER
+						       (resume));
+			vcd_try_submit_frame(dev_ctxt);
+		}
+	} else {
+		vcd_do_client_state_transition(cctxt,
+					       VCD_CLIENT_STATE_RUN,
+					       CLIENT_STATE_EVENT_NUMBER
+					       (resume));
+	}
+
+	return rc;
+}
+
+static u32 vcd_flush_cmn(struct vcd_clnt_ctxt *cctxt, u32 mode)
+{
+	u32 rc = VCD_S_SUCCESS;
+
+	VCD_MSG_LOW("vcd_flush_cmn in %d:", cctxt->clnt_state.state);
+
+	rc = vcd_flush_buffers(cctxt, mode);
+
+	VCD_FAILED_RETURN(rc, "Failed: vcd_flush_buffers");
+
+	if (cctxt->status.frame_submitted > 0) {
+		vcd_do_client_state_transition(cctxt,
+					       VCD_CLIENT_STATE_FLUSHING,
+					       CLIENT_STATE_EVENT_NUMBER
+					       (flush));
+	} else {
+		VCD_MSG_HIGH("All buffers are flushed");
+		cctxt->status.mask |= (mode & VCD_FLUSH_ALL);
+		vcd_send_flush_done(cctxt, VCD_S_SUCCESS);
+	}
+
+	return rc;
+}
+
+static u32  vcd_flush_inopen(struct vcd_clnt_ctxt *cctxt,
+	u32 mode)
+{
+   VCD_MSG_LOW("vcd_flush_inopen:");
+   cctxt->status.mask |= (mode & VCD_FLUSH_ALL);
+   vcd_send_flush_done(cctxt, VCD_S_SUCCESS);
+   return VCD_S_SUCCESS;
+}
+
+static u32 vcd_flush_in_flushing
+    (struct vcd_clnt_ctxt *cctxt, u32 mode)
+{
+	u32 rc = VCD_S_SUCCESS;
+
+	VCD_MSG_LOW("vcd_flush_in_flushing:");
+
+	rc = vcd_flush_buffers(cctxt, mode);
+
+	return rc;
+}
+
+static u32 vcd_flush_in_eos(struct vcd_clnt_ctxt *cctxt,
+	u32 mode)
+{
+	u32 rc = VCD_S_SUCCESS;
+	VCD_MSG_LOW("vcd_flush_in_eos:");
+
+	if (mode > VCD_FLUSH_ALL || !mode) {
+		VCD_MSG_ERROR("Invalid flush mode %d", mode);
+
+		return VCD_ERR_ILLEGAL_PARM;
+	}
+
+	VCD_MSG_MED("Flush mode requested %d", mode);
+	if (!(cctxt->status.frame_submitted) &&
+		(!cctxt->decoding)) {
+		rc = vcd_flush_buffers(cctxt, mode);
+		if (!VCD_FAILED(rc)) {
+			VCD_MSG_HIGH("All buffers are flushed");
+			cctxt->status.mask |= (mode & VCD_FLUSH_ALL);
+			vcd_send_flush_done(cctxt, VCD_S_SUCCESS);
+		}
+	} else
+		cctxt->status.mask |= (mode & VCD_FLUSH_ALL);
+
+	return rc;
+}
+
+static u32 vcd_flush_in_invalid(struct vcd_clnt_ctxt *cctxt,
+	u32 mode)
+{
+	u32 rc = VCD_S_SUCCESS;
+	VCD_MSG_LOW("vcd_flush_in_invalid:");
+	if (!(cctxt->status.mask & VCD_CLEANING_UP)) {
+		rc = vcd_flush_buffers(cctxt, mode);
+		if (!VCD_FAILED(rc)) {
+			VCD_MSG_HIGH("All buffers are flushed");
+			cctxt->status.mask |= (mode & VCD_FLUSH_ALL);
+			vcd_send_flush_done(cctxt, VCD_S_SUCCESS);
+		}
+	} else
+		cctxt->status.mask |= (mode & VCD_FLUSH_ALL);
+	return rc;
+}
+
+static u32 vcd_stop_cmn(struct vcd_clnt_ctxt *cctxt)
+{
+	struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt;
+	u32 rc = VCD_S_SUCCESS;
+	struct vcd_transc *transc;
+
+	VCD_MSG_LOW("vcd_stop_cmn in %d:", cctxt->clnt_state.state);
+
+	rc = vcd_flush_buffers(cctxt, VCD_FLUSH_ALL);
+
+	VCD_FAILED_RETURN(rc, "Failed: vcd_flush_buffers");
+
+	if (!cctxt->status.frame_submitted) {
+
+		if (vcd_get_command_channel(dev_ctxt, &transc)) {
+			rc = vcd_power_event(dev_ctxt, cctxt,
+				VCD_EVT_PWR_CLNT_CMD_BEGIN);
+
+			if (!VCD_FAILED(rc)) {
+				transc->type = VCD_CMD_CODEC_STOP;
+				transc->cctxt = cctxt;
+
+				rc = vcd_submit_cmd_sess_end(transc);
+			} else {
+				VCD_MSG_ERROR("Failed:"
+					" VCD_EVT_PWR_CLNT_CMD_BEGIN");
+			}
+
+			if (VCD_FAILED(rc)) {
+				vcd_release_command_channel(dev_ctxt,
+							    transc);
+			}
+
+		} else {
+			vcd_client_cmd_flush_and_en_q(cctxt,
+						      VCD_CMD_CODEC_STOP);
+		}
+	}
+
+	if (VCD_FAILED(rc)) {
+		(void)vcd_power_event(dev_ctxt, cctxt,
+				      VCD_EVT_PWR_CLNT_CMD_FAIL);
+	} else {
+		vcd_do_client_state_transition(cctxt,
+					       VCD_CLIENT_STATE_STOPPING,
+					       CLIENT_STATE_EVENT_NUMBER
+					       (stop));
+	}
+
+	return rc;
+}
+
+
+static u32  vcd_stop_inopen(struct vcd_clnt_ctxt *cctxt)
+{
+	VCD_MSG_LOW("vcd_stop_inopen:");
+
+	cctxt->callback(VCD_EVT_RESP_STOP, VCD_S_SUCCESS,
+					  NULL, 0, cctxt,
+					  cctxt->client_data);
+
+	return VCD_S_SUCCESS;
+}
+
+static u32 vcd_stop_in_run(struct vcd_clnt_ctxt *cctxt)
+{
+	u32 rc = VCD_S_SUCCESS;
+	VCD_MSG_LOW("vcd_stop_in_run:");
+	rc = vcd_stop_cmn(cctxt);
+	if (!VCD_FAILED(rc) &&
+		(cctxt->status.mask & VCD_FIRST_IP_RCVD)) {
+		rc = vcd_power_event(cctxt->dev_ctxt,
+				     cctxt, VCD_EVT_PWR_CLNT_LAST_FRAME);
+	}
+	return rc;
+}
+
+static u32 vcd_stop_in_eos(struct vcd_clnt_ctxt *cctxt)
+{
+	u32 rc = VCD_S_SUCCESS;
+	VCD_MSG_LOW("vcd_stop_in_eos:");
+	if (cctxt->status.mask & VCD_EOS_WAIT_OP_BUF) {
+		rc = vcd_stop_cmn(cctxt);
+		if (!VCD_FAILED(rc)) {
+			rc = vcd_power_event(cctxt->dev_ctxt,
+				cctxt, VCD_EVT_PWR_CLNT_LAST_FRAME);
+			cctxt->status.mask &= ~VCD_EOS_WAIT_OP_BUF;
+		}
+	} else
+		cctxt->status.mask |= VCD_STOP_PENDING;
+	return rc;
+}
+
+static u32  vcd_stop_in_invalid(struct vcd_clnt_ctxt *cctxt)
+{
+	VCD_MSG_LOW("vcd_stop_in_invalid:");
+	if (cctxt->status.mask & VCD_CLEANING_UP) {
+		cctxt->status.mask |= VCD_STOP_PENDING;
+	} else {
+		(void) vcd_flush_buffers(cctxt, VCD_FLUSH_ALL);
+		cctxt->callback(VCD_EVT_RESP_STOP, VCD_S_SUCCESS, NULL,
+			0, cctxt,	cctxt->client_data);
+	}
+	return VCD_S_SUCCESS;
+}
+
+static u32 vcd_set_property_cmn
+    (struct vcd_clnt_ctxt *cctxt,
+     struct vcd_property_hdr *prop_hdr, void *prop_val)
+{
+	u32 rc;
+	VCD_MSG_LOW("vcd_set_property_cmn in %d:", cctxt->clnt_state.state);
+	VCD_MSG_LOW("property Id = %d", prop_hdr->prop_id);
+	if (!prop_hdr->sz || !prop_hdr->prop_id) {
+		VCD_MSG_MED("Bad parameters");
+		return VCD_ERR_ILLEGAL_PARM;
+	}
+
+	rc = ddl_set_property(cctxt->ddl_handle, prop_hdr, prop_val);
+	if (rc) {
+		/* Some properties aren't known to ddl that we can handle */
+		if (prop_hdr->prop_id != VCD_I_VOP_TIMING_CONSTANT_DELTA)
+			VCD_FAILED_RETURN(rc, "Failed: ddl_set_property");
+	}
+
+	switch (prop_hdr->prop_id) {
+	case VCD_I_META_BUFFER_MODE:
+		{
+			struct vcd_property_live *live =
+			    (struct vcd_property_live *)prop_val;
+			cctxt->meta_mode = live->live;
+			break;
+		}
+	case VCD_I_LIVE:
+		{
+			struct vcd_property_live *live =
+			    (struct vcd_property_live *)prop_val;
+			cctxt->live = live->live;
+			break;
+		}
+	case VCD_I_FRAME_RATE:
+		{
+			if (cctxt->sched_clnt_hdl) {
+				rc = vcd_set_frame_rate(cctxt,
+					(struct vcd_property_frame_rate *)
+					prop_val);
+			}
+			break;
+		}
+	case VCD_I_FRAME_SIZE:
+		{
+			if (cctxt->sched_clnt_hdl) {
+				rc = vcd_set_frame_size(cctxt,
+					(struct vcd_property_frame_size *)
+					prop_val);
+			}
+			break;
+		}
+	case VCD_I_INTRA_PERIOD:
+		{
+			struct vcd_property_i_period *iperiod =
+				(struct vcd_property_i_period *)prop_val;
+			cctxt->bframe = iperiod->b_frames;
+			break;
+		}
+	case VCD_REQ_PERF_LEVEL:
+		rc = vcd_req_perf_level(cctxt,
+				(struct vcd_property_perf_level *)prop_val);
+		break;
+	case VCD_I_VOP_TIMING_CONSTANT_DELTA:
+		{
+			struct vcd_property_vop_timing_constant_delta *delta =
+				prop_val;
+
+			if (delta->constant_delta > 0) {
+				cctxt->time_frame_delta = delta->constant_delta;
+				rc = VCD_S_SUCCESS;
+			} else {
+				VCD_MSG_ERROR("Frame delta must be positive");
+				rc = VCD_ERR_ILLEGAL_PARM;
+			}
+			break;
+		}
+	default:
+		{
+			break;
+		}
+	}
+	return rc;
+}
+
+static u32 vcd_get_property_cmn
+    (struct vcd_clnt_ctxt *cctxt,
+     struct vcd_property_hdr *prop_hdr, void *prop_val)
+{
+	int rc;
+	VCD_MSG_LOW("vcd_get_property_cmn in %d:", cctxt->clnt_state.state);
+	VCD_MSG_LOW("property Id = %d", prop_hdr->prop_id);
+	if (!prop_hdr->sz || !prop_hdr->prop_id) {
+		VCD_MSG_MED("Bad parameters");
+
+		return VCD_ERR_ILLEGAL_PARM;
+	}
+	rc = ddl_get_property(cctxt->ddl_handle, prop_hdr, prop_val);
+	if (rc) {
+		/* Some properties aren't known to ddl that we can handle */
+		if (prop_hdr->prop_id != VCD_I_VOP_TIMING_CONSTANT_DELTA)
+			VCD_FAILED_RETURN(rc, "Failed: ddl_set_property");
+	}
+
+	switch (prop_hdr->prop_id) {
+	case VCD_I_VOP_TIMING_CONSTANT_DELTA:
+	{
+		struct vcd_property_vop_timing_constant_delta *delta =
+			(struct vcd_property_vop_timing_constant_delta *)
+			prop_val;
+		delta->constant_delta = cctxt->time_frame_delta;
+		rc = VCD_S_SUCCESS;
+	}
+	}
+	return rc;
+}
+
+static u32 vcd_set_buffer_requirements_cmn
+    (struct vcd_clnt_ctxt *cctxt,
+     enum vcd_buffer_type buffer,
+     struct vcd_buffer_requirement *buffer_req)
+{
+	struct vcd_property_hdr Prop_hdr;
+	u32 rc = VCD_S_SUCCESS;
+	struct vcd_buffer_pool *buf_pool;
+	u32 first_frm_recvd = 0;
+
+	VCD_MSG_LOW("vcd_set_buffer_requirements_cmn in %d:",
+		    cctxt->clnt_state.state);
+
+	if (!cctxt->decoding &&
+	    cctxt->clnt_state.state != VCD_CLIENT_STATE_OPEN) {
+		VCD_MSG_ERROR("Bad state (%d) for encoder",
+					cctxt->clnt_state.state);
+
+		return VCD_ERR_BAD_STATE;
+	}
+
+	VCD_MSG_MED("Buffer type = %d", buffer);
+
+	if (buffer == VCD_BUFFER_INPUT) {
+		Prop_hdr.prop_id = DDL_I_INPUT_BUF_REQ;
+		buf_pool = &cctxt->in_buf_pool;
+		first_frm_recvd = VCD_FIRST_IP_RCVD;
+	} else if (buffer == VCD_BUFFER_OUTPUT) {
+		Prop_hdr.prop_id = DDL_I_OUTPUT_BUF_REQ;
+		buf_pool = &cctxt->out_buf_pool;
+		first_frm_recvd = VCD_FIRST_OP_RCVD;
+	} else {
+		rc = VCD_ERR_ILLEGAL_PARM;
+	}
+
+	VCD_FAILED_RETURN(rc, "Invalid buffer type provided");
+
+	if (buf_pool->validated > 0) {
+		VCD_MSG_ERROR("Need to free allocated buffers");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+
+	first_frm_recvd &= cctxt->status.mask;
+	if (first_frm_recvd) {
+		VCD_MSG_ERROR("VCD SetBufReq called when data path is active");
+		return VCD_ERR_BAD_STATE;
+	}
+	Prop_hdr.sz = sizeof(*buffer_req);
+	rc = ddl_set_property(cctxt->ddl_handle, &Prop_hdr, buffer_req);
+	VCD_FAILED_RETURN(rc, "Failed: ddl_set_property");
+	if (buf_pool->entries) {
+		VCD_MSG_MED("Resetting buffer requirements");
+		vcd_free_buffer_pool_entries(buf_pool);
+	}
+	return rc;
+}
+
+static u32 vcd_get_buffer_requirements_cmn
+    (struct vcd_clnt_ctxt *cctxt,
+     enum vcd_buffer_type buffer,
+     struct vcd_buffer_requirement *buffer_req)
+{
+	struct vcd_property_hdr Prop_hdr;
+	u32 rc = VCD_S_SUCCESS;
+
+	VCD_MSG_LOW("vcd_get_buffer_requirements_cmn in %d:",
+		    cctxt->clnt_state.state);
+
+	VCD_MSG_MED("Buffer type = %d", buffer);
+
+	if (buffer == VCD_BUFFER_INPUT)
+		Prop_hdr.prop_id = DDL_I_INPUT_BUF_REQ;
+	else if (buffer == VCD_BUFFER_OUTPUT)
+		Prop_hdr.prop_id = DDL_I_OUTPUT_BUF_REQ;
+	else
+		rc = VCD_ERR_ILLEGAL_PARM;
+
+	VCD_FAILED_RETURN(rc, "Invalid buffer type provided");
+
+	Prop_hdr.sz = sizeof(*buffer_req);
+
+	return ddl_get_property(cctxt->ddl_handle, &Prop_hdr, buffer_req);
+
+}
+
+static u32 vcd_set_buffer_cmn
+    (struct vcd_clnt_ctxt *cctxt,
+     enum vcd_buffer_type buffer_type, u8 *buffer, u32 buf_size)
+{
+	u32 rc;
+	struct vcd_buffer_pool *buf_pool;
+
+	VCD_MSG_LOW("vcd_set_buffer_cmn in %d:", cctxt->clnt_state.state);
+
+	rc = vcd_common_allocate_set_buffer(cctxt, buffer_type, buf_size,
+					    &buf_pool);
+
+	if (!VCD_FAILED(rc)) {
+		rc = vcd_set_buffer_internal(cctxt, buf_pool, buffer,
+					     buf_size);
+	}
+
+	return rc;
+}
+
+static u32 vcd_allocate_buffer_cmn
+    (struct vcd_clnt_ctxt *cctxt,
+     enum vcd_buffer_type buffer,
+     u32 buf_size, u8 **vir_buf_addr, u8 **phy_buf_addr)
+{
+	u32 rc;
+	struct vcd_buffer_pool *buf_pool;
+
+	VCD_MSG_LOW("vcd_allocate_buffer_cmn in %d:",
+		    cctxt->clnt_state.state);
+
+	rc = vcd_common_allocate_set_buffer(cctxt, buffer, buf_size,
+					    &buf_pool);
+
+	if (!VCD_FAILED(rc)) {
+		rc = vcd_allocate_buffer_internal(cctxt,
+						  buf_pool,
+						  buf_size,
+						  vir_buf_addr,
+						  phy_buf_addr);
+	}
+
+	return rc;
+}
+
+static u32 vcd_free_buffer_cmn
+    (struct vcd_clnt_ctxt *cctxt,
+     enum vcd_buffer_type buffer_type, u8 *buffer)
+{
+
+	VCD_MSG_LOW("vcd_free_buffer_cmn in %d:", cctxt->clnt_state.state);
+
+	return vcd_free_one_buffer_internal(cctxt, buffer_type, buffer);
+}
+
+static u32 vcd_fill_output_buffer_cmn
+    (struct vcd_clnt_ctxt *cctxt,
+     struct vcd_frame_data *buffer)
+{
+	u32 rc = VCD_S_SUCCESS;
+	struct vcd_buffer_entry *buf_entry;
+	u32 result = true;
+	u32 handled = true;
+	if (!cctxt || !buffer) {
+		VCD_MSG_ERROR("%s(): Inavlid params cctxt %p buffer %p",
+					__func__, cctxt, buffer);
+		return VCD_ERR_BAD_POINTER;
+	}
+	VCD_MSG_LOW("vcd_fill_output_buffer_cmn in %d:",
+		    cctxt->clnt_state.state);
+	if (cctxt->status.mask & VCD_IN_RECONFIG) {
+		buffer->time_stamp = 0;
+		buffer->data_len = 0;
+		VCD_MSG_LOW("In reconfig: Return output buffer");
+		cctxt->callback(VCD_EVT_RESP_OUTPUT_DONE,
+			VCD_S_SUCCESS,
+			buffer,
+			sizeof(struct vcd_frame_data),
+			cctxt, cctxt->client_data);
+		return rc;
+	}
+	buf_entry = vcd_check_fill_output_buffer(cctxt, buffer);
+	if (!buf_entry)
+		return VCD_ERR_BAD_POINTER;
+
+	if (!(cctxt->status.mask & VCD_FIRST_OP_RCVD)) {
+		rc = vcd_handle_first_fill_output_buffer(cctxt, buffer,
+			&handled);
+		VCD_FAILED_RETURN(rc,
+			"Failed: vcd_handle_first_fill_output_buffer");
+		if (handled)
+			return rc ;
+	}
+
+	result =
+	    vcd_buffer_pool_entry_en_q(&cctxt->out_buf_pool, buf_entry);
+
+	if (!result && !cctxt->decoding) {
+		VCD_MSG_ERROR("Failed: vcd_buffer_pool_entry_en_q");
+
+		return VCD_ERR_FAIL;
+	}
+
+	buf_entry->frame = *buffer;
+	rc = vcd_return_op_buffer_to_hw(cctxt, buf_entry);
+	if (!VCD_FAILED(rc) && cctxt->sched_clnt_hdl) {
+		cctxt->sched_clnt_hdl->tkns++;
+		vcd_try_submit_frame(cctxt->dev_ctxt);
+	}
+	return rc;
+}
+
+static u32 vcd_fill_output_buffer_in_eos
+    (struct vcd_clnt_ctxt *cctxt,
+     struct vcd_frame_data *buffer)
+{
+	u32 rc = VCD_S_SUCCESS;
+	struct vcd_buffer_entry *buf_entry;
+
+	VCD_MSG_LOW("vcd_fill_output_buffer_in_eos:");
+
+	buf_entry = vcd_check_fill_output_buffer(cctxt, buffer);
+	if (!buf_entry)
+		return VCD_ERR_BAD_POINTER;
+
+	if (cctxt->status.mask & VCD_EOS_WAIT_OP_BUF) {
+		VCD_MSG_HIGH("Got an output buffer we were waiting for");
+
+		buf_entry->frame = *buffer;
+
+		buf_entry->frame.data_len = 0;
+		buf_entry->frame.flags |= VCD_FRAME_FLAG_EOS;
+		buf_entry->frame.ip_frm_tag =
+		    cctxt->status.eos_trig_ip_frm.ip_frm_tag;
+		buf_entry->frame.time_stamp =
+		    cctxt->status.eos_trig_ip_frm.time_stamp;
+
+		cctxt->callback(VCD_EVT_RESP_OUTPUT_DONE,
+				  VCD_S_SUCCESS,
+				  &buf_entry->frame,
+				  sizeof(struct vcd_frame_data),
+				  cctxt, cctxt->client_data);
+
+		cctxt->status.mask &= ~VCD_EOS_WAIT_OP_BUF;
+
+		vcd_do_client_state_transition(cctxt,
+					       VCD_CLIENT_STATE_RUN,
+					       CLIENT_STATE_EVENT_NUMBER
+					       (fill_output_buffer));
+
+	} else {
+		rc = vcd_fill_output_buffer_cmn(cctxt, buffer);
+	}
+
+	return rc;
+}
+
+static void vcd_clnt_cb_in_starting
+    (struct vcd_clnt_ctxt *cctxt,
+     u32 event, u32 status, void *payload, size_t sz,
+	 u32 *ddl_handle, void *const client_data)
+{
+	struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt;
+	struct vcd_transc *transc =
+		(struct vcd_transc *)client_data;
+	VCD_MSG_LOW("vcd_clnt_cb_in_starting:");
+	if (cctxt->ddl_handle != ddl_handle) {
+		VCD_MSG_ERROR("vcd_clnt_cb_in_initing: Wrong DDL handle %p",
+			ddl_handle);
+		return;
+	}
+
+	switch (event) {
+	case VCD_EVT_RESP_START:
+		{
+			vcd_handle_start_done(cctxt,
+				(struct vcd_transc *)client_data,
+				status);
+			break;
+		}
+	case VCD_EVT_RESP_STOP:
+		{
+			vcd_handle_stop_done_in_starting(cctxt,
+				(struct vcd_transc *)client_data,
+				status);
+			break;
+		}
+	case VCD_EVT_IND_HWERRFATAL:
+		{
+			cctxt->status.cmd_submitted--;
+			vcd_mark_command_channel(cctxt->dev_ctxt, transc);
+			vcd_handle_err_fatal(cctxt, VCD_EVT_RESP_START,
+				status);
+			break;
+		}
+	default:
+		{
+			VCD_MSG_ERROR("Unexpected callback event=%d status=%d "
+				"from DDL",	event, status);
+			dev_ctxt->command_continue = false;
+			break;
+		}
+	}
+}
+
+static void vcd_clnt_cb_in_run
+    (struct vcd_clnt_ctxt *cctxt,
+     u32 event,
+     u32 status,
+     void *payload, size_t sz, u32 *ddl_handle, void *const client_data)
+{
+	struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt;
+	u32 rc = VCD_S_SUCCESS;
+
+	if (cctxt->ddl_handle != ddl_handle) {
+		VCD_MSG_ERROR("ddl_handle mismatch");
+
+		return;
+	}
+
+	switch (event) {
+	case VCD_EVT_RESP_INPUT_DONE:
+		{
+			rc = vcd_handle_input_done(cctxt, payload, event,
+						   status);
+
+			break;
+		}
+
+	case VCD_EVT_RESP_OUTPUT_DONE:
+		{
+
+			rc = vcd_handle_frame_done(cctxt, payload, event,
+						   status);
+
+			break;
+		}
+	case VCD_EVT_RESP_OUTPUT_REQ:
+		{
+			rc = vcd_handle_output_required(cctxt, payload,
+				status);
+			break;
+		}
+
+	case VCD_EVT_IND_OUTPUT_RECONFIG:
+		{
+			rc = vcd_handle_ind_output_reconfig(cctxt, payload,
+				status);
+      break;
+		}
+	case VCD_EVT_RESP_TRANSACTION_PENDING:
+		{
+			 vcd_handle_trans_pending(cctxt);
+			 break;
+		}
+
+	case VCD_EVT_IND_HWERRFATAL:
+		{
+			 vcd_handle_ind_hw_err_fatal(cctxt,
+				VCD_EVT_IND_HWERRFATAL, status);
+			 break;
+		}
+	case VCD_EVT_IND_INFO_OUTPUT_RECONFIG:
+		{
+			vcd_handle_ind_info_output_reconfig(cctxt, status);
+			break;
+		}
+	default:
+		{
+			VCD_MSG_ERROR
+			    ("Unexpected callback event=%d status=%d from DDL",
+			     event, status);
+			dev_ctxt->command_continue = false;
+
+			break;
+		}
+	}
+
+	if (!VCD_FAILED(rc) &&
+	    (event == VCD_EVT_RESP_INPUT_DONE ||
+	     event == VCD_EVT_RESP_OUTPUT_DONE ||
+	     event == VCD_EVT_RESP_OUTPUT_REQ)) {
+
+		if (((struct ddl_frame_data_tag *)
+					payload)->frm_trans_end)
+			vcd_mark_frame_channel(cctxt->dev_ctxt);
+	}
+}
+
+static void vcd_clnt_cb_in_eos
+    (struct vcd_clnt_ctxt *cctxt,
+     u32 event,
+     u32 status,
+     void *payload, size_t sz, u32 *ddl_handle, void *const client_data) {
+	struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt;
+	struct vcd_transc *transc = NULL;
+	u32 frm_trans_end = false, rc = VCD_S_SUCCESS;
+
+	if (cctxt->ddl_handle != ddl_handle) {
+		VCD_MSG_ERROR("ddl_handle mismatch");
+
+		return;
+	}
+
+	switch (event) {
+	case VCD_EVT_RESP_INPUT_DONE:
+		{
+			rc = vcd_handle_input_done_in_eos(cctxt, payload,
+						     status);
+
+			break;
+		}
+
+	case VCD_EVT_RESP_OUTPUT_DONE:
+		{
+			rc = vcd_handle_frame_done_in_eos(cctxt, payload,
+						     status);
+
+			break;
+		}
+	case VCD_EVT_RESP_OUTPUT_REQ:
+		{
+			rc = vcd_handle_output_required(cctxt, payload,
+					status);
+			break;
+		}
+	case VCD_EVT_RESP_EOS_DONE:
+		{
+			transc = (struct vcd_transc *)client_data;
+			vcd_handle_eos_done(cctxt, transc, status);
+			vcd_mark_frame_channel(cctxt->dev_ctxt);
+			break;
+		}
+	case VCD_EVT_IND_OUTPUT_RECONFIG:
+		{
+			rc = vcd_handle_ind_output_reconfig(cctxt,
+				payload, status);
+			if (!VCD_FAILED(rc)) {
+				frm_trans_end = true;
+				payload = NULL;
+				vcd_do_client_state_transition(cctxt,
+					VCD_CLIENT_STATE_RUN,
+					CLIENT_STATE_EVENT_NUMBER
+					(clnt_cb));
+				VCD_MSG_LOW
+					("RECONFIGinEOS:Suspending Client");
+				rc = vcd_sched_suspend_resume_clnt(cctxt,
+						false);
+				if (VCD_FAILED(rc)) {
+					VCD_MSG_ERROR
+					("Failed: suspend_resume_clnt. rc=0x%x",
+						rc);
+				}
+			}
+			break;
+		}
+	case VCD_EVT_IND_HWERRFATAL:
+		{
+			vcd_handle_ind_hw_err_fatal(cctxt,
+				VCD_EVT_IND_HWERRFATAL,	status);
+			break;
+		}
+	case VCD_EVT_IND_INFO_OUTPUT_RECONFIG:
+		{
+			vcd_handle_ind_info_output_reconfig(cctxt, status);
+			break;
+		}
+	default:
+		{
+			VCD_MSG_ERROR
+			    ("Unexpected callback event=%d status=%d from DDL",
+			     event, status);
+
+			dev_ctxt->command_continue = false;
+
+			break;
+		}
+
+	}
+	if (!VCD_FAILED(rc) &&
+		(event == VCD_EVT_RESP_INPUT_DONE ||
+		event == VCD_EVT_RESP_OUTPUT_DONE ||
+		event == VCD_EVT_RESP_OUTPUT_REQ ||
+		event == VCD_EVT_IND_OUTPUT_RECONFIG)) {
+		if (payload && ((struct ddl_frame_data_tag *)
+			payload)->frm_trans_end) {
+			vcd_mark_frame_channel(cctxt->dev_ctxt);
+			frm_trans_end = true;
+		}
+		if (frm_trans_end && !cctxt->status.frame_submitted)
+			vcd_handle_eos_trans_end(cctxt);
+	}
+}
+
+static void vcd_clnt_cb_in_flushing
+    (struct vcd_clnt_ctxt *cctxt,
+     u32 event,
+     u32 status,
+     void *payload, size_t sz, u32 *ddl_handle, void *const client_data) {
+	struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt;
+	u32 rc = VCD_S_SUCCESS;
+	u32 frm_trans_end = false;
+
+	VCD_MSG_LOW("vcd_clnt_cb_in_flushing:");
+
+	if (cctxt->ddl_handle != ddl_handle) {
+		VCD_MSG_ERROR("ddl_handle mismatch");
+
+		return;
+	}
+
+	switch (event) {
+	case VCD_EVT_RESP_INPUT_DONE:
+		{
+			rc = vcd_handle_input_done(cctxt,
+						   payload,
+						   VCD_EVT_RESP_INPUT_FLUSHED,
+						   status);
+
+			break;
+		}
+
+	case VCD_EVT_RESP_OUTPUT_DONE:
+		{
+
+			rc = vcd_handle_frame_done(cctxt,
+						   payload,
+						   VCD_EVT_RESP_OUTPUT_FLUSHED,
+						   status);
+
+			break;
+		}
+	case VCD_EVT_RESP_OUTPUT_REQ:
+		{
+			rc = vcd_handle_output_required_in_flushing(cctxt,
+				payload);
+			break;
+		}
+	case VCD_EVT_IND_OUTPUT_RECONFIG:
+		{
+			rc = vcd_handle_ind_output_reconfig(cctxt,
+				payload, status);
+			if (!VCD_FAILED(rc)) {
+				frm_trans_end = true;
+				payload = NULL;
+			}
+			break;
+		}
+	case VCD_EVT_IND_HWERRFATAL:
+		{
+			vcd_handle_ind_hw_err_fatal(cctxt,
+				VCD_EVT_IND_HWERRFATAL,	status);
+			break;
+		}
+	default:
+		{
+			VCD_MSG_ERROR
+			    ("Unexpected callback event=%d status=%d from DDL",
+			     event, status);
+
+			dev_ctxt->command_continue = false;
+
+			break;
+		}
+	}
+	if (!VCD_FAILED(rc) && ((event == VCD_EVT_RESP_INPUT_DONE ||
+		event == VCD_EVT_RESP_OUTPUT_DONE ||
+		event == VCD_EVT_RESP_OUTPUT_REQ ||
+		event == VCD_EVT_IND_OUTPUT_RECONFIG))) {
+		if (payload &&
+			((struct ddl_frame_data_tag *)\
+			payload)->frm_trans_end) {
+
+			vcd_mark_frame_channel(cctxt->dev_ctxt);
+			frm_trans_end = true;
+		}
+		if (frm_trans_end && !cctxt->status.frame_submitted) {
+			VCD_MSG_HIGH
+			    ("All pending frames recvd from DDL");
+			if (cctxt->status.mask & VCD_FLUSH_INPUT)
+				vcd_flush_bframe_buffers(cctxt,
+							VCD_FLUSH_INPUT);
+			if (cctxt->status.mask & VCD_FLUSH_OUTPUT)
+				vcd_flush_output_buffers(cctxt);
+			vcd_send_flush_done(cctxt, VCD_S_SUCCESS);
+			vcd_release_interim_frame_channels(dev_ctxt);
+			VCD_MSG_HIGH("Flush complete");
+			vcd_do_client_state_transition(cctxt,
+				VCD_CLIENT_STATE_RUN,
+				CLIENT_STATE_EVENT_NUMBER
+				(clnt_cb));
+		}
+	}
+}
+
+static void vcd_clnt_cb_in_stopping
+    (struct vcd_clnt_ctxt *cctxt,
+     u32 event,
+     u32 status,
+     void *payload, size_t sz, u32 *ddl_handle, void *const client_data) {
+	struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt;
+	u32 rc = VCD_S_SUCCESS;
+	u32 frm_trans_end = false;
+
+	VCD_MSG_LOW("vcd_clnt_cb_in_stopping:");
+
+	if (cctxt->ddl_handle != ddl_handle) {
+		VCD_MSG_ERROR("ddl_handle mismatch");
+
+		return;
+	}
+
+	switch (event) {
+
+	case VCD_EVT_RESP_INPUT_DONE:
+		{
+			rc = vcd_handle_input_done(cctxt,
+						   payload,
+						   VCD_EVT_RESP_INPUT_FLUSHED,
+						   status);
+
+			break;
+		}
+
+	case VCD_EVT_RESP_OUTPUT_DONE:
+		{
+
+			rc = vcd_handle_frame_done(cctxt,
+						   payload,
+						   VCD_EVT_RESP_OUTPUT_FLUSHED,
+						   status);
+
+			break;
+		}
+	case VCD_EVT_RESP_OUTPUT_REQ:
+		{
+			rc = vcd_handle_output_required_in_flushing(cctxt,
+				payload);
+			break;
+		}
+	case VCD_EVT_RESP_STOP:
+		{
+			vcd_handle_stop_done(cctxt,
+					     (struct vcd_transc *)
+					     client_data, status);
+
+			break;
+		}
+	case VCD_EVT_IND_OUTPUT_RECONFIG:
+		{
+			(void) vcd_handle_ind_output_reconfig(cctxt,
+				payload, status);
+
+			frm_trans_end = true;
+			payload = NULL;
+
+			break;
+		}
+	case VCD_EVT_IND_HWERRFATAL:
+		{
+			vcd_handle_ind_hw_err_fatal(cctxt, VCD_EVT_RESP_STOP,
+				status);
+			break;
+		}
+
+	default:
+		{
+			VCD_MSG_ERROR
+			    ("Unexpected callback event=%d status=%d from DDL",
+			     event, status);
+
+			dev_ctxt->command_continue = false;
+
+			break;
+		}
+	}
+
+	if (!VCD_FAILED(rc) && ((event == VCD_EVT_RESP_INPUT_DONE ||
+		event == VCD_EVT_RESP_OUTPUT_DONE) ||
+		event == VCD_EVT_RESP_OUTPUT_REQ ||
+		event == VCD_EVT_IND_OUTPUT_RECONFIG)) {
+
+		if (payload &&
+			((struct ddl_frame_data_tag *)\
+			payload)->frm_trans_end) {
+
+			vcd_mark_frame_channel(cctxt->dev_ctxt);
+			frm_trans_end = true;
+		}
+		if (frm_trans_end && !cctxt->status.frame_submitted) {
+				VCD_MSG_HIGH
+					("All pending frames recvd from DDL");
+				vcd_flush_bframe_buffers(cctxt,
+							VCD_FLUSH_INPUT);
+				vcd_flush_output_buffers(cctxt);
+				cctxt->status.mask &= ~VCD_FLUSH_ALL;
+				vcd_release_all_clnt_frm_transc(cctxt);
+				VCD_MSG_HIGH
+				("All buffers flushed. Enqueuing stop cmd");
+				vcd_client_cmd_flush_and_en_q(cctxt,
+						VCD_CMD_CODEC_STOP);
+		}
+	}
+}
+
+static void vcd_clnt_cb_in_pausing
+    (struct vcd_clnt_ctxt *cctxt,
+     u32 event,
+     u32 status,
+     void *payload, size_t sz, u32 *ddl_handle, void *const client_data)
+{
+	struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt;
+	u32 rc = VCD_S_SUCCESS;
+	u32 frm_trans_end = false;
+
+	VCD_MSG_LOW("vcd_clnt_cb_in_pausing:");
+
+	if (cctxt->ddl_handle != ddl_handle) {
+		VCD_MSG_ERROR("ddl_handle mismatch");
+
+		return;
+	}
+
+	switch (event) {
+	case VCD_EVT_RESP_INPUT_DONE:
+		{
+			rc = vcd_handle_input_done(cctxt, payload, event,
+						   status);
+
+			break;
+		}
+
+	case VCD_EVT_RESP_OUTPUT_DONE:
+		{
+			rc = vcd_handle_frame_done(cctxt, payload, event,
+						   status);
+			break;
+		}
+	case VCD_EVT_RESP_OUTPUT_REQ:
+		{
+			rc = vcd_handle_output_required(cctxt, payload,
+				status);
+			break;
+		}
+	case VCD_EVT_IND_OUTPUT_RECONFIG:
+		{
+			rc = vcd_handle_ind_output_reconfig(cctxt,
+				payload, status);
+			if (!VCD_FAILED(rc)) {
+				frm_trans_end = true;
+				payload = NULL;
+			}
+			break;
+		}
+	case VCD_EVT_IND_HWERRFATAL:
+		{
+			vcd_handle_ind_hw_err_fatal(cctxt,
+				VCD_EVT_RESP_PAUSE,	status);
+			rc = VCD_ERR_FAIL;
+			break;
+		}
+	default:
+		{
+			VCD_MSG_ERROR
+			    ("Unexpected callback event=%d status=%d from DDL",
+			     event, status);
+
+			dev_ctxt->command_continue = false;
+
+			break;
+		}
+
+	}
+
+	if (!VCD_FAILED(rc)) {
+
+		if (payload &&
+			((struct ddl_frame_data_tag *)\
+			payload)->frm_trans_end) {
+
+			vcd_mark_frame_channel(cctxt->dev_ctxt);
+			frm_trans_end = true;
+		}
+		if (frm_trans_end && !cctxt->status.frame_submitted) {
+			VCD_MSG_HIGH
+			    ("All pending frames recvd from DDL");
+
+			cctxt->callback(VCD_EVT_RESP_PAUSE,
+					  VCD_S_SUCCESS,
+					  NULL,
+					  0,
+					  cctxt,
+					  cctxt->client_data);
+
+			vcd_do_client_state_transition(cctxt,
+					VCD_CLIENT_STATE_PAUSED,
+					CLIENT_STATE_EVENT_NUMBER
+						       (clnt_cb));
+
+			rc = vcd_power_event(cctxt->dev_ctxt,
+					     cctxt,
+					     VCD_EVT_PWR_CLNT_PAUSE);
+
+			if (VCD_FAILED(rc)) {
+				VCD_MSG_ERROR
+				    ("VCD_EVT_PWR_CLNT_PAUSE_END"
+				     "failed");
+			}
+		}
+	}
+}
+
+static void  vcd_clnt_cb_in_invalid(
+   struct vcd_clnt_ctxt *cctxt, u32 event, u32 status,
+   void *payload, size_t sz, u32 *ddl_handle,
+   void *const client_data
+)
+{
+	struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt;
+	VCD_MSG_LOW("vcd_clnt_cb_in_invalid:");
+	if (cctxt->ddl_handle != ddl_handle) {
+		VCD_MSG_ERROR("ddl_handle mismatch");
+		return;
+	}
+	switch (event) {
+	case VCD_EVT_RESP_STOP:
+		{
+			vcd_handle_stop_done_in_invalid(cctxt,
+				(struct vcd_transc *)client_data,
+				status);
+			break;
+		}
+	case VCD_EVT_RESP_INPUT_DONE:
+	case VCD_EVT_RESP_OUTPUT_REQ:
+		{
+			if (cctxt->status.frame_submitted)
+				cctxt->status.frame_submitted--;
+			if (payload && ((struct ddl_frame_data_tag *)
+							payload)->frm_trans_end)
+				vcd_mark_frame_channel(cctxt->dev_ctxt);
+			break;
+		}
+	case VCD_EVT_RESP_OUTPUT_DONE:
+		{
+			if (payload && ((struct ddl_frame_data_tag *)
+							payload)->frm_trans_end)
+				vcd_mark_frame_channel(cctxt->dev_ctxt);
+			break;
+		}
+	case VCD_EVT_RESP_TRANSACTION_PENDING:
+		{
+			if (cctxt->status.frame_submitted)
+				cctxt->status.frame_submitted--;
+			vcd_mark_frame_channel(cctxt->dev_ctxt);
+			break;
+		}
+	case VCD_EVT_IND_HWERRFATAL:
+		{
+			if (status == VCD_ERR_HW_FATAL)
+				vcd_handle_stop_done_in_invalid(cctxt,
+					(struct vcd_transc *)client_data,
+					status);
+
+			break;
+		}
+	case VCD_EVT_RESP_EOS_DONE:
+		{
+			vcd_mark_frame_channel(cctxt->dev_ctxt);
+			break;
+		}
+	case VCD_EVT_IND_OUTPUT_RECONFIG:
+		{
+			if (cctxt->status.frame_submitted > 0)
+				cctxt->status.frame_submitted--;
+			else
+				cctxt->status.frame_delayed--;
+			vcd_mark_frame_channel(cctxt->dev_ctxt);
+			break;
+		}
+	default:
+		{
+			VCD_MSG_ERROR("Unexpected callback event=%d status=%d"
+				"from DDL",	event, status);
+			dev_ctxt->command_continue = false;
+			break;
+		}
+	}
+}
+
+static void vcd_clnt_enter_open
+    (struct vcd_clnt_ctxt *cctxt, s32 state_event) {
+	VCD_MSG_MED("Entering CLIENT_STATE_OPEN on api %d", state_event);
+}
+
+static void vcd_clnt_enter_starting
+    (struct vcd_clnt_ctxt *cctxt, s32 state_event) {
+	VCD_MSG_MED("Entering CLIENT_STATE_STARTING on api %d",
+		    state_event);
+	cctxt->status.last_evt = VCD_EVT_RESP_START;
+}
+
+static void vcd_clnt_enter_run
+    (struct vcd_clnt_ctxt *cctxt, s32 state_event) {
+	VCD_MSG_MED("Entering CLIENT_STATE_RUN on api %d", state_event);
+}
+
+static void vcd_clnt_enter_flushing
+    (struct vcd_clnt_ctxt *cctxt, s32 state_event) {
+	VCD_MSG_MED("Entering CLIENT_STATE_FLUSHING on api %d",
+		    state_event);
+}
+
+static void vcd_clnt_enter_stopping
+    (struct vcd_clnt_ctxt *cctxt, s32 state_event) {
+	VCD_MSG_MED("Entering CLIENT_STATE_STOPPING on api %d",
+		    state_event);
+	cctxt->status.last_evt = VCD_EVT_RESP_STOP;
+}
+
+static void vcd_clnt_enter_eos(struct vcd_clnt_ctxt *cctxt,
+	s32 state_event)
+{
+   u32     rc;
+   VCD_MSG_MED("Entering CLIENT_STATE_EOS on api %d", state_event);
+	rc = vcd_sched_suspend_resume_clnt(cctxt, false);
+	if (VCD_FAILED(rc))
+		VCD_MSG_ERROR("Failed: vcd_sched_suspend_resume_clnt."
+					  "rc=0x%x", rc);
+}
+
+static void vcd_clnt_enter_pausing
+    (struct vcd_clnt_ctxt *cctxt, s32 state_event) {
+	VCD_MSG_MED("Entering CLIENT_STATE_PAUSING on api %d",
+		    state_event);
+	cctxt->status.last_evt = VCD_EVT_RESP_PAUSE;
+}
+
+static void vcd_clnt_enter_paused
+    (struct vcd_clnt_ctxt *cctxt, s32 state_event)
+{
+	VCD_MSG_MED("Entering CLIENT_STATE_PAUSED on api %d",
+		state_event);
+}
+
+static void  vcd_clnt_enter_invalid(struct vcd_clnt_ctxt *cctxt,
+	s32 state_event)
+{
+	VCD_MSG_MED("Entering CLIENT_STATE_INVALID on api %d",
+		state_event);
+	cctxt->ddl_hdl_valid = false;
+	cctxt->status.mask &= ~(VCD_FIRST_IP_RCVD | VCD_FIRST_OP_RCVD);
+	if (cctxt->sched_clnt_hdl)
+		vcd_sched_suspend_resume_clnt(cctxt, false);
+}
+
+static void vcd_clnt_exit_open
+    (struct vcd_clnt_ctxt *cctxt, s32 state_event)
+{
+	VCD_MSG_MED("Exiting CLIENT_STATE_OPEN on api %d", state_event);
+}
+
+static void vcd_clnt_exit_starting
+    (struct vcd_clnt_ctxt *cctxt, s32 state_event) {
+	VCD_MSG_MED("Exiting CLIENT_STATE_STARTING on api %d",
+		    state_event);
+	cctxt->status.last_evt = VCD_EVT_RESP_BASE;
+}
+
+static void vcd_clnt_exit_run
+    (struct vcd_clnt_ctxt *cctxt, s32 state_event) {
+	VCD_MSG_MED("Exiting CLIENT_STATE_RUN on api %d", state_event);
+}
+
+static void vcd_clnt_exit_flushing
+    (struct vcd_clnt_ctxt *cctxt, s32 state_event) {
+	VCD_MSG_MED("Exiting CLIENT_STATE_FLUSHING on api %d",
+		    state_event);
+}
+
+static void vcd_clnt_exit_stopping
+    (struct vcd_clnt_ctxt *cctxt, s32 state_event) {
+	VCD_MSG_MED("Exiting CLIENT_STATE_STOPPING on api %d",
+		    state_event);
+	cctxt->status.last_evt = VCD_EVT_RESP_BASE;
+}
+
+static void vcd_clnt_exit_eos
+    (struct vcd_clnt_ctxt *cctxt, s32 state_event)
+{
+	u32 rc;
+	VCD_MSG_MED("Exiting CLIENT_STATE_EOS on api %d", state_event);
+	rc = vcd_sched_suspend_resume_clnt(cctxt, true);
+	if (VCD_FAILED(rc))
+		VCD_MSG_ERROR("Failed: vcd_sched_suspend_resume_clnt. rc=0x%x",
+			rc);
+}
+
+static void vcd_clnt_exit_pausing
+    (struct vcd_clnt_ctxt *cctxt, s32 state_event) {
+	VCD_MSG_MED("Exiting CLIENT_STATE_PAUSING on api %d",
+		    state_event);
+	cctxt->status.last_evt = VCD_EVT_RESP_BASE;
+}
+
+static void vcd_clnt_exit_paused
+    (struct vcd_clnt_ctxt *cctxt, s32 state_event) {
+	VCD_MSG_MED("Exiting CLIENT_STATE_PAUSED on api %d",
+		    state_event);
+}
+
+static void  vcd_clnt_exit_invalid(struct vcd_clnt_ctxt *cctxt,
+	s32 state_event)
+{
+	VCD_MSG_MED("Exiting CLIENT_STATE_INVALID on api %d",
+		state_event);
+}
+
+void vcd_do_client_state_transition(struct vcd_clnt_ctxt *cctxt,
+     enum vcd_clnt_state_enum to_state, u32 ev_code)
+{
+	struct vcd_clnt_state_ctxt *state_ctxt;
+
+	if (!cctxt || to_state >= VCD_CLIENT_STATE_MAX) {
+		VCD_MSG_ERROR("Bad parameters. cctxt=%p, to_state=%d",
+			      cctxt, to_state);
+	}
+
+	state_ctxt = &cctxt->clnt_state;
+
+	if (state_ctxt->state == to_state) {
+		VCD_MSG_HIGH("Client already in requested to_state=%d",
+			     to_state);
+
+		return;
+	}
+
+	VCD_MSG_MED("vcd_do_client_state_transition: C%d -> C%d, for api %d",
+		    (int)state_ctxt->state, (int)to_state, ev_code);
+
+	if (state_ctxt->state_table->exit)
+		state_ctxt->state_table->exit(cctxt, ev_code);
+
+
+	state_ctxt->state = to_state;
+	state_ctxt->state_table = vcd_clnt_state_table[to_state];
+
+	if (state_ctxt->state_table->entry)
+		state_ctxt->state_table->entry(cctxt, ev_code);
+}
+
+const struct vcd_clnt_state_table *vcd_get_client_state_table
+    (enum vcd_clnt_state_enum state) {
+	return vcd_clnt_state_table[state];
+}
+
+static const struct vcd_clnt_state_table vcd_clnt_table_open = {
+	{
+	 vcd_close_in_open,
+	 vcd_encode_start_in_open,
+	 NULL,
+	 vcd_decode_start_in_open,
+	 NULL,
+	 NULL,
+	 NULL,
+	 vcd_flush_inopen,
+	 vcd_stop_inopen,
+	 vcd_set_property_cmn,
+	 vcd_get_property_cmn,
+	 vcd_set_buffer_requirements_cmn,
+	 vcd_get_buffer_requirements_cmn,
+	 vcd_set_buffer_cmn,
+	 vcd_allocate_buffer_cmn,
+	 vcd_free_buffer_cmn,
+	 vcd_fill_output_buffer_cmn,
+	 NULL,
+	 },
+	vcd_clnt_enter_open,
+	vcd_clnt_exit_open
+};
+
+static const struct vcd_clnt_state_table vcd_clnt_table_starting = {
+	{
+	 NULL,
+	 NULL,
+	 NULL,
+	 NULL,
+	 NULL,
+	 NULL,
+	 NULL,
+	 NULL,
+	 NULL,
+	 NULL,
+	 vcd_get_property_cmn,
+	 NULL,
+	 vcd_get_buffer_requirements_cmn,
+	 NULL,
+	 NULL,
+	 NULL,
+	 NULL,
+	 vcd_clnt_cb_in_starting,
+	 },
+	vcd_clnt_enter_starting,
+	vcd_clnt_exit_starting
+};
+
+static const struct vcd_clnt_state_table vcd_clnt_table_run = {
+	{
+	 NULL,
+	 vcd_encode_start_in_run,
+	 vcd_encode_frame_cmn,
+	 vcd_decode_start_in_run,
+	 vcd_decode_frame_cmn,
+	 vcd_pause_in_run,
+	 NULL,
+	 vcd_flush_cmn,
+	 vcd_stop_in_run,
+	 vcd_set_property_cmn,
+	 vcd_get_property_cmn,
+	 vcd_set_buffer_requirements_cmn,
+	 vcd_get_buffer_requirements_cmn,
+	 vcd_set_buffer_cmn,
+	 vcd_allocate_buffer_cmn,
+	 vcd_free_buffer_cmn,
+	 vcd_fill_output_buffer_cmn,
+	 vcd_clnt_cb_in_run,
+	 },
+	vcd_clnt_enter_run,
+	vcd_clnt_exit_run
+};
+
+static const struct vcd_clnt_state_table vcd_clnt_table_flushing = {
+	{
+	 NULL,
+	 NULL,
+	 NULL,
+	 NULL,
+	 NULL,
+	 NULL,
+	 NULL,
+	 vcd_flush_in_flushing,
+	 NULL,
+	 vcd_set_property_cmn,
+	 vcd_get_property_cmn,
+	 NULL,
+	 vcd_get_buffer_requirements_cmn,
+	 NULL,
+	 NULL,
+	 NULL,
+	 vcd_fill_output_buffer_cmn,
+	 vcd_clnt_cb_in_flushing,
+	 },
+	vcd_clnt_enter_flushing,
+	vcd_clnt_exit_flushing
+};
+
+static const struct vcd_clnt_state_table vcd_clnt_table_stopping = {
+	{
+	 NULL,
+	 NULL,
+	 NULL,
+	 NULL,
+	 NULL,
+	 NULL,
+	 NULL,
+	 NULL,
+	 NULL,
+	 NULL,
+	 vcd_get_property_cmn,
+	 NULL,
+	 vcd_get_buffer_requirements_cmn,
+	 NULL,
+	 NULL,
+	 NULL,
+	 NULL,
+	 vcd_clnt_cb_in_stopping,
+	 },
+	vcd_clnt_enter_stopping,
+	vcd_clnt_exit_stopping
+};
+
+static const struct vcd_clnt_state_table vcd_clnt_table_eos = {
+	{
+	 NULL,
+	 NULL,
+	 vcd_encode_frame_cmn,
+	 NULL,
+	 vcd_decode_frame_cmn,
+	 NULL,
+	 NULL,
+	 vcd_flush_in_eos,
+	 vcd_stop_in_eos,
+	 NULL,
+	 vcd_get_property_cmn,
+	 NULL,
+	 vcd_get_buffer_requirements_cmn,
+	 NULL,
+	 NULL,
+	 NULL,
+	 vcd_fill_output_buffer_in_eos,
+	 vcd_clnt_cb_in_eos,
+	 },
+	vcd_clnt_enter_eos,
+	vcd_clnt_exit_eos
+};
+
+static const struct vcd_clnt_state_table vcd_clnt_table_pausing = {
+	{
+	 NULL,
+	 NULL,
+	 vcd_encode_frame_cmn,
+	 NULL,
+	 vcd_decode_frame_cmn,
+	 NULL,
+	 NULL,
+	 NULL,
+	 NULL,
+	 vcd_set_property_cmn,
+	 vcd_get_property_cmn,
+	 NULL,
+	 vcd_get_buffer_requirements_cmn,
+	 NULL,
+	 NULL,
+	 NULL,
+	 vcd_fill_output_buffer_cmn,
+	 vcd_clnt_cb_in_pausing,
+	 },
+	vcd_clnt_enter_pausing,
+	vcd_clnt_exit_pausing
+};
+
+static const struct vcd_clnt_state_table vcd_clnt_table_paused = {
+	{
+	 NULL,
+	 NULL,
+	 vcd_encode_frame_cmn,
+	 NULL,
+	 vcd_decode_frame_cmn,
+	 NULL,
+	 vcd_resume_in_paused,
+	 vcd_flush_cmn,
+	 vcd_stop_cmn,
+	 vcd_set_property_cmn,
+	 vcd_get_property_cmn,
+	 vcd_set_buffer_requirements_cmn,
+	 vcd_get_buffer_requirements_cmn,
+	 vcd_set_buffer_cmn,
+	 vcd_allocate_buffer_cmn,
+	 NULL,
+	 vcd_fill_output_buffer_cmn,
+	 NULL,
+	 },
+	vcd_clnt_enter_paused,
+	vcd_clnt_exit_paused
+};
+static const struct vcd_clnt_state_table vcd_clnt_table_invalid = {
+   {
+      vcd_close_in_invalid,
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      vcd_flush_in_invalid,
+      vcd_stop_in_invalid,
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      vcd_free_buffer_cmn,
+      NULL,
+      vcd_clnt_cb_in_invalid,
+   },
+   vcd_clnt_enter_invalid,
+   vcd_clnt_exit_invalid
+};
+
+static const struct vcd_clnt_state_table *vcd_clnt_state_table[] = {
+	NULL,
+	&vcd_clnt_table_open,
+	&vcd_clnt_table_starting,
+	&vcd_clnt_table_run,
+	&vcd_clnt_table_flushing,
+	&vcd_clnt_table_pausing,
+	&vcd_clnt_table_paused,
+	&vcd_clnt_table_stopping,
+	&vcd_clnt_table_eos,
+   &vcd_clnt_table_invalid
+};
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_client_sm.h b/drivers/video/msm/vidc/common/vcd/vcd_client_sm.h
new file mode 100644
index 0000000..9f2d63d
--- /dev/null
+++ b/drivers/video/msm/vidc/common/vcd/vcd_client_sm.h
@@ -0,0 +1,110 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef _VCD_CLIENT_SM_H_
+#define _VCD_CLIENT_SM_H_
+#include <media/msm/vcd_api.h>
+#include "vcd_ddl_api.h"
+
+struct vcd_clnt_state_table;
+struct vcd_clnt_state_ctxt;
+struct vcd_clnt_ctxt;
+
+enum vcd_clnt_state_enum {
+	VCD_CLIENT_STATE_NULL = 0,
+	VCD_CLIENT_STATE_OPEN,
+	VCD_CLIENT_STATE_STARTING,
+	VCD_CLIENT_STATE_RUN,
+	VCD_CLIENT_STATE_FLUSHING,
+	VCD_CLIENT_STATE_PAUSING,
+	VCD_CLIENT_STATE_PAUSED,
+	VCD_CLIENT_STATE_STOPPING,
+	VCD_CLIENT_STATE_EOS,
+	VCD_CLIENT_STATE_INVALID,
+	VCD_CLIENT_STATE_MAX,
+	VCD_CLIENT_STATE_32BIT = 0x7FFFFFFF
+};
+
+#define   CLIENT_STATE_EVENT_NUMBER(ppf) \
+    ((u32 *) (&(((struct vcd_clnt_state_table*)0)->ev_hdlr.ppf)) -  \
+    (u32 *) (&(((struct vcd_clnt_state_table*)0)->ev_hdlr.close)) \
+	+ 1)
+
+struct vcd_clnt_state_table {
+	struct {
+		u32(*close) (struct vcd_clnt_ctxt *cctxt);
+		u32(*encode_start) (struct vcd_clnt_ctxt *cctxt);
+		u32(*encode_frame) (struct vcd_clnt_ctxt *cctxt,
+				struct vcd_frame_data *input_frame);
+		u32(*decode_start) (struct vcd_clnt_ctxt *cctxt,
+				struct vcd_sequence_hdr *seq_hdr);
+		u32(*decode_frame) (struct vcd_clnt_ctxt *cctxt,
+				struct vcd_frame_data *input_frame);
+		u32(*pause) (struct vcd_clnt_ctxt *cctxt);
+		u32(*resume) (struct vcd_clnt_ctxt *cctxt);
+		u32(*flush) (struct vcd_clnt_ctxt *cctxt,
+				u32 mode);
+		u32(*stop) (struct vcd_clnt_ctxt *cctxt);
+		u32(*set_property) (struct vcd_clnt_ctxt *cctxt,
+				struct vcd_property_hdr *prop_hdr,
+				void *prop);
+		u32(*get_property) (struct vcd_clnt_ctxt *cctxt,
+				struct vcd_property_hdr *prop_hdr,
+				void *prop);
+		u32(*set_buffer_requirements) (struct vcd_clnt_ctxt *
+						  cctxt,
+						  enum vcd_buffer_type buffer,
+						  struct
+						  vcd_buffer_requirement *
+						  buffer_req);
+		u32(*get_buffer_requirements) (struct vcd_clnt_ctxt *
+						  cctxt,
+						  enum vcd_buffer_type buffer,
+						  struct
+						  vcd_buffer_requirement *
+						  buffer_req);
+		u32(*set_buffer) (struct vcd_clnt_ctxt *cctxt,
+				enum vcd_buffer_type buffer_type, u8 *buffer,
+				u32 buf_size);
+		u32(*allocate_buffer) (struct vcd_clnt_ctxt *cctxt,
+				enum vcd_buffer_type buffer, u32 buf_size,
+				u8 **vir_buf_addr, u8 **phy_buf_addr);
+		u32(*free_buffer) (struct vcd_clnt_ctxt *cctxt,
+				enum vcd_buffer_type buffer_type, u8 *buffer);
+		u32(*fill_output_buffer) (
+				struct vcd_clnt_ctxt *cctxt,
+				struct vcd_frame_data *buffer);
+		void (*clnt_cb) (struct vcd_clnt_ctxt *cctxt,
+				u32 event, u32 status, void *payload,
+				size_t sz, u32 *ddl_handle,
+				void *const client_data);
+	} ev_hdlr;
+
+	void (*entry) (struct vcd_clnt_ctxt *cctxt,
+			s32 state_event);
+	void (*exit) (struct vcd_clnt_ctxt *cctxt,
+			s32 state_event);
+};
+
+struct vcd_clnt_state_ctxt {
+	const struct vcd_clnt_state_table *state_table;
+	enum vcd_clnt_state_enum state;
+};
+
+extern void vcd_do_client_state_transition
+    (struct vcd_clnt_ctxt *cctxt,
+     enum vcd_clnt_state_enum to_state, u32 ev_code);
+
+extern const struct vcd_clnt_state_table *vcd_get_client_state_table(
+		enum vcd_clnt_state_enum state);
+
+#endif
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_core.h b/drivers/video/msm/vidc/common/vcd/vcd_core.h
new file mode 100644
index 0000000..7ae4f45
--- /dev/null
+++ b/drivers/video/msm/vidc/common/vcd/vcd_core.h
@@ -0,0 +1,228 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef _VCD_CORE_H_
+#define _VCD_CORE_H_
+
+#include <linux/ion.h>
+#include <media/msm/vcd_api.h>
+#include "vcd_ddl_api.h"
+
+#include "vcd_util.h"
+#include "vcd_client_sm.h"
+#include "vcd_power_sm.h"
+
+#define VCD_SIGNATURE                        0x75017591U
+
+#define VCD_MIN_PERF_LEVEL                   37900
+
+#define VCD_DRIVER_INSTANCE_MAX              4
+
+#define VCD_MAX_CLIENT_TRANSACTIONS          32
+
+#define VCD_MAX_BUFFER_ENTRIES               32
+
+#define VCD_SEQ_HDR_PADDING_BYTES            256
+
+#define VCD_DEC_NUM_INTERLACED_FIELDS        2
+
+#define VCD_TIMESTAMP_RESOLUTION             1000000
+#define VCD_DEC_INITIAL_FRAME_RATE           30
+
+#define VCD_FIRST_IP_RCVD                    0x00000004
+#define VCD_FIRST_OP_RCVD                    0x00000008
+#define VCD_EOS_PREV_VALID                   0x00000010
+#define VCD_EOS_WAIT_OP_BUF                  0x00000020
+#define VCD_CLEANING_UP                      0x00000040
+#define VCD_STOP_PENDING                     0x00000080
+#define VCD_CLOSE_PENDING                    0x00000100
+#define VCD_IN_RECONFIG                      0x00000200
+#define VCD_FIRST_IP_DONE                    0x00000400
+
+enum vcd_command {
+	VCD_CMD_NONE,
+	VCD_CMD_DEVICE_INIT,
+	VCD_CMD_DEVICE_TERM,
+	VCD_CMD_DEVICE_RESET,
+	VCD_CMD_CODEC_START,
+	VCD_CMD_CODEC_STOP,
+	VCD_CMD_CODE_FRAME,
+	VCD_CMD_OUTPUT_FLUSH,
+	VCD_CMD_CLIENT_CLOSE
+};
+
+enum vcd_core_type {
+    VCD_CORE_1080P,
+    VCD_CORE_720P
+};
+
+struct vcd_cmd_q_element {
+	enum vcd_command pending_cmd;
+};
+
+struct vcd_buffer_entry {
+	struct list_head sched_list;
+	struct list_head list;
+	u32 valid;
+	u8 *alloc;
+	u8 *virtual;
+	u8 *physical;
+	size_t sz;
+	u32 allocated;
+	u32 in_use;
+	struct vcd_frame_data frame;
+
+};
+
+struct vcd_buffer_pool {
+	struct vcd_buffer_entry *entries;
+	u32 count;
+	struct vcd_buffer_requirement buf_req;
+	u32 validated;
+	u32 allocated;
+	u32 in_use;
+	struct list_head queue;
+	u16 q_len;
+};
+
+struct vcd_transc {
+	u32 in_use;
+	enum vcd_command type;
+	struct vcd_clnt_ctxt *cctxt;
+
+	struct vcd_buffer_entry *ip_buf_entry;
+
+	s64 time_stamp;
+	u32 flags;
+	u32 ip_frm_tag;
+	enum vcd_frame frame;
+
+	struct vcd_buffer_entry *op_buf_entry;
+
+	u32 input_done;
+	u32 frame_done;
+};
+
+struct vcd_dev_ctxt {
+	u32 ddl_cmd_concurrency;
+	u32 ddl_frame_ch_depth;
+	u32 ddl_cmd_ch_depth;
+	u32 ddl_frame_ch_interim;
+	u32 ddl_cmd_ch_interim;
+	u32 ddl_frame_ch_free;
+	u32 ddl_cmd_ch_free;
+
+	struct list_head sched_clnt_list;
+
+	struct vcd_init_config config;
+
+	u32 driver_ids[VCD_DRIVER_INSTANCE_MAX];
+	u32 refs;
+	u8 *device_base_addr;
+	void *hw_timer_handle;
+	u32               hw_time_out;
+	struct vcd_clnt_ctxt *cctxt_list_head;
+
+	enum vcd_command pending_cmd;
+
+	u32 command_continue;
+
+	struct vcd_transc *trans_tbl;
+	u32 trans_tbl_size;
+
+	enum vcd_power_state pwr_state;
+	enum vcd_pwr_clk_state pwr_clk_state;
+	u32 active_clnts;
+	u32 max_perf_lvl;
+	u32 reqd_perf_lvl;
+	u32 curr_perf_lvl;
+	u32 set_perf_lvl_pending;
+
+};
+
+struct vcd_clnt_status {
+	u32 req_perf_lvl;
+	u32 frame_submitted;
+	u32 frame_delayed;
+	u32 cmd_submitted;
+	u32 int_field_cnt;
+	s64 first_ts;
+	s64 prev_ts;
+	u64 time_elapsed;
+	struct vcd_frame_data eos_trig_ip_frm;
+	struct ddl_frame_data_tag eos_prev_op_frm;
+	u32 eos_prev_op_frm_status;
+	u32	last_err;
+	u32	last_evt;
+	u32 mask;
+};
+
+struct vcd_sched_clnt_ctx {
+	struct list_head list;
+	u32 clnt_active;
+	void *clnt_data;
+	u32 tkns;
+	u32 round_perfrm;
+	u32 rounds;
+	struct list_head ip_frm_list;
+};
+
+struct vcd_clnt_ctxt {
+	u32 signature;
+	struct vcd_clnt_state_ctxt clnt_state;
+
+	s32 driver_id;
+
+	u32 live;
+	u32 decoding;
+	u32 bframe;
+	u32 num_slices;
+
+	struct vcd_property_frame_rate frm_rate;
+	u32 frm_p_units;
+	u32 reqd_perf_lvl;
+	u32 time_resoln;
+	u32 time_frame_delta;
+
+	struct vcd_buffer_pool in_buf_pool;
+	struct vcd_buffer_pool out_buf_pool;
+
+	void (*callback) (u32 event, u32 status, void *info, size_t sz,
+			  void *handle, void *const client_data);
+	void *client_data;
+	struct vcd_sched_clnt_ctx *sched_clnt_hdl;
+	u32	ddl_hdl_valid;
+	u32 *ddl_handle;
+	struct vcd_dev_ctxt *dev_ctxt;
+	struct vcd_cmd_q_element cmd_q;
+	struct vcd_sequence_hdr seq_hdr;
+	u8 *seq_hdr_phy_addr;
+	struct vcd_clnt_status status;
+	struct ion_client *vcd_ion_client;
+	u32 vcd_enable_ion;
+	struct vcd_clnt_ctxt *next;
+	u32 meta_mode;
+	int perf_set_by_client;
+	int secure;
+};
+
+#define VCD_BUFFERPOOL_INUSE_DECREMENT(val) \
+do { \
+	if ((val) > 0) \
+		val--; \
+	else { \
+		VCD_MSG_ERROR("%s(): Inconsistent val given in " \
+			" VCD_BUFFERPOOL_INUSE_DECREMENT\n", __func__); \
+	} \
+} while (0)
+
+#endif
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_device_sm.c b/drivers/video/msm/vidc/common/vcd/vcd_device_sm.c
new file mode 100644
index 0000000..d517028
--- /dev/null
+++ b/drivers/video/msm/vidc/common/vcd/vcd_device_sm.c
@@ -0,0 +1,1218 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <media/msm/vidc_type.h>
+#include "vcd.h"
+
+static const struct vcd_dev_state_table *vcd_dev_state_table[];
+static const struct vcd_dev_state_table vcd_dev_table_null;
+
+struct vcd_drv_ctxt *vcd_get_drv_context(void)
+{
+	static struct vcd_drv_ctxt drv_context = {
+		{&vcd_dev_table_null, VCD_DEVICE_STATE_NULL},
+		{0},
+	};
+
+	return &drv_context;
+
+}
+
+void vcd_do_device_state_transition(struct vcd_drv_ctxt *drv_ctxt,
+	 enum vcd_dev_state_enum to_state, u32 ev_code)
+{
+	struct vcd_dev_state_ctxt *state_ctxt;
+
+	if (!drv_ctxt || to_state >= VCD_DEVICE_STATE_MAX) {
+		VCD_MSG_ERROR("Bad parameters. drv_ctxt=%p, to_state=%d",
+				  drv_ctxt, to_state);
+	}
+
+	state_ctxt = &drv_ctxt->dev_state;
+
+	if (state_ctxt->state == to_state) {
+		VCD_MSG_HIGH("Device already in requested to_state=%d",
+				 to_state);
+
+		return;
+	}
+
+	VCD_MSG_MED("vcd_do_device_state_transition: D%d -> D%d, for api %d",
+			(int)state_ctxt->state, (int)to_state, ev_code);
+
+	if (state_ctxt->state_table->exit)
+		state_ctxt->state_table->exit(drv_ctxt, ev_code);
+
+
+	state_ctxt->state = to_state;
+	state_ctxt->state_table = vcd_dev_state_table[to_state];
+
+	if (state_ctxt->state_table->entry)
+		state_ctxt->state_table->entry(drv_ctxt, ev_code);
+}
+
+void vcd_hw_timeout_handler(void *user_data)
+{
+	struct vcd_drv_ctxt *drv_ctxt;
+
+	VCD_MSG_HIGH("vcd_hw_timeout_handler:");
+	user_data = NULL;
+	drv_ctxt = vcd_get_drv_context();
+	mutex_lock(&drv_ctxt->dev_mutex);
+	if (drv_ctxt->dev_state.state_table->ev_hdlr.timeout)
+		drv_ctxt->dev_state.state_table->ev_hdlr.
+			timeout(drv_ctxt, user_data);
+	else
+		VCD_MSG_ERROR("hw_timeout unsupported in device state %d",
+			drv_ctxt->dev_state.state);
+	mutex_unlock(&drv_ctxt->dev_mutex);
+}
+
+void vcd_ddl_callback(u32 event, u32 status, void *payload,
+	size_t sz, u32 *ddl_handle, void *const client_data)
+{
+	struct vcd_drv_ctxt *drv_ctxt;
+	struct vcd_dev_ctxt *dev_ctxt;
+	struct vcd_dev_state_ctxt *dev_state;
+	struct vcd_clnt_ctxt *cctxt;
+	struct vcd_transc *transc;
+
+	VCD_MSG_LOW("vcd_ddl_callback:");
+
+	VCD_MSG_LOW("event=0x%x status=0x%x", event, status);
+
+	drv_ctxt = vcd_get_drv_context();
+	dev_ctxt = &drv_ctxt->dev_ctxt;
+	dev_state = &drv_ctxt->dev_state;
+
+	dev_ctxt->command_continue = true;
+	vcd_device_timer_stop(dev_ctxt);
+
+	switch (dev_state->state) {
+	case VCD_DEVICE_STATE_NULL:
+		{
+			VCD_MSG_HIGH("Callback unexpected in NULL state");
+			break;
+		}
+
+	case VCD_DEVICE_STATE_NOT_INIT:
+		{
+			VCD_MSG_HIGH("Callback unexpected in NOT_INIT state");
+			break;
+		}
+
+	case VCD_DEVICE_STATE_INITING:
+		{
+			if (dev_state->state_table->ev_hdlr.dev_cb) {
+				dev_state->state_table->ev_hdlr.
+					dev_cb(drv_ctxt, event, status,
+						  payload, sz, ddl_handle,
+						  client_data);
+			} else {
+				VCD_MSG_HIGH("No device handler in %d state",
+						 dev_state->state);
+			}
+			break;
+		}
+
+	case VCD_DEVICE_STATE_READY:
+		{
+			transc = (struct vcd_transc *)client_data;
+
+			if (!transc || !transc->in_use || !transc->cctxt) {
+				VCD_MSG_ERROR("Invalid clientdata "
+					"received from DDL, transc = 0x%x\n",
+					(u32)transc);
+				if (transc) {
+					VCD_MSG_ERROR("transc->in_use = %u, "
+						"transc->cctxt = 0x%x\n",
+						transc->in_use,
+						(u32)transc->cctxt);
+				}
+			} else {
+				cctxt = transc->cctxt;
+
+				if (cctxt->clnt_state.state_table->ev_hdlr.
+					clnt_cb) {
+					cctxt->clnt_state.state_table->
+						ev_hdlr.clnt_cb(cctxt,
+						event, status, payload,
+						sz,	ddl_handle,
+						client_data);
+				} else {
+					VCD_MSG_HIGH
+					("No client handler in"
+					" (dsm:READY, csm:%d) state",
+					(int)cctxt->clnt_state.state);
+
+					if (VCD_FAILED(status)) {
+						VCD_MSG_FATAL("DDL callback"
+						" returned failure 0x%x",
+						status);
+					}
+				}
+			}
+			break;
+		}
+
+	default:
+		{
+			VCD_MSG_ERROR("Unknown state");
+			break;
+		}
+
+	}
+
+}
+
+u32 vcd_init_device_context(struct vcd_drv_ctxt *drv_ctxt,
+		u32 ev_code)
+{
+	struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt;
+	u32 rc;
+	struct ddl_init_config ddl_init;
+
+	VCD_MSG_LOW("vcd_init_device_context:");
+
+	dev_ctxt->pending_cmd = VCD_CMD_NONE;
+
+	rc = vcd_power_event(dev_ctxt, NULL, VCD_EVT_PWR_DEV_INIT_BEGIN);
+	VCD_FAILED_RETURN(rc, "VCD_EVT_PWR_DEV_INIT_BEGIN failed");
+
+	VCD_MSG_HIGH("Device powered ON and clocked");
+	rc = vcd_sched_create(&dev_ctxt->sched_clnt_list);
+	if (VCD_FAILED(rc)) {
+		VCD_MSG_ERROR("rc = 0x%x. Failed: vcd_sched_create", rc);
+
+		(void)vcd_power_event(dev_ctxt, NULL,
+					  VCD_EVT_PWR_DEV_INIT_FAIL);
+
+		return rc;
+	}
+
+	VCD_MSG_HIGH("Created scheduler instance.");
+
+	ddl_init.core_virtual_base_addr = dev_ctxt->device_base_addr;
+	ddl_init.interrupt_clr = dev_ctxt->config.interrupt_clr;
+	ddl_init.ddl_callback = vcd_ddl_callback;
+
+	rc = ddl_device_init(&ddl_init, NULL);
+
+	if (VCD_FAILED(rc)) {
+		VCD_MSG_ERROR("rc = 0x%x. Failed: ddl_device_init", rc);
+		vcd_sched_destroy(&dev_ctxt->sched_clnt_list);
+		(void)vcd_power_event(dev_ctxt, NULL,
+					  VCD_EVT_PWR_DEV_INIT_FAIL);
+	} else {
+		vcd_device_timer_start(dev_ctxt);
+		vcd_do_device_state_transition(drv_ctxt,
+						   VCD_DEVICE_STATE_INITING,
+						   ev_code);
+	}
+
+	return rc;
+}
+
+void vcd_handle_device_init_failed(struct vcd_drv_ctxt *drv_ctxt,
+		u32 status)
+{
+	struct vcd_clnt_ctxt *client;
+	struct vcd_clnt_ctxt *tmp_client;
+
+	VCD_MSG_ERROR("Device init failed. status = %d", status);
+
+	client = drv_ctxt->dev_ctxt.cctxt_list_head;
+	while (client) {
+		client->callback(VCD_EVT_RESP_OPEN,
+				   status, NULL, 0, 0, client->client_data);
+
+		tmp_client = client;
+		client = client->next;
+
+		vcd_destroy_client_context(tmp_client);
+	}
+	if (ddl_device_release(NULL))
+		VCD_MSG_ERROR("Failed: ddl_device_release");
+
+	vcd_sched_destroy(&drv_ctxt->dev_ctxt.sched_clnt_list);
+	if (vcd_power_event(&drv_ctxt->dev_ctxt,
+		NULL, VCD_EVT_PWR_DEV_INIT_FAIL))
+		VCD_MSG_ERROR("VCD_EVT_PWR_DEV_INIT_FAIL failed");
+
+	vcd_do_device_state_transition(drv_ctxt,
+		VCD_DEVICE_STATE_NOT_INIT,
+		DEVICE_STATE_EVENT_NUMBER(dev_cb));
+}
+
+u32 vcd_deinit_device_context(struct vcd_drv_ctxt *drv_ctxt,
+		u32 ev_code)
+{
+	struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt;
+	u32 rc = VCD_S_SUCCESS;
+
+	VCD_MSG_LOW("vcd_deinit_device_context:");
+
+	rc = vcd_power_event(&drv_ctxt->dev_ctxt, NULL,
+				 VCD_EVT_PWR_DEV_TERM_BEGIN);
+
+	VCD_FAILED_RETURN(rc, "VCD_EVT_PWR_DEV_TERM_BEGIN failed");
+
+	rc = ddl_device_release(NULL);
+
+	if (VCD_FAILED(rc)) {
+		VCD_MSG_ERROR("rc = 0x%x. Failed: ddl_device_release", rc);
+
+		(void)vcd_power_event(dev_ctxt, NULL,
+					  VCD_EVT_PWR_DEV_TERM_FAIL);
+	} else {
+		vcd_sched_destroy(&dev_ctxt->sched_clnt_list);
+		(void) vcd_power_event(dev_ctxt, NULL,
+			VCD_EVT_PWR_DEV_TERM_END);
+
+		vcd_do_device_state_transition(drv_ctxt,
+			VCD_DEVICE_STATE_NOT_INIT, ev_code);
+	}
+	return rc;
+}
+
+void vcd_term_driver_context(struct vcd_drv_ctxt *drv_ctxt)
+{
+	struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt;
+
+	VCD_MSG_HIGH("All driver instances terminated");
+
+	if (dev_ctxt->config.deregister_isr)
+		dev_ctxt->config.deregister_isr();
+
+	if (dev_ctxt->config.un_map_dev_base_addr)
+		dev_ctxt->config.un_map_dev_base_addr();
+
+	if (dev_ctxt->config.timer_release)
+		dev_ctxt->config.timer_release(
+			dev_ctxt->hw_timer_handle);
+
+	kfree(dev_ctxt->trans_tbl);
+
+	memset(dev_ctxt, 0, sizeof(struct vcd_dev_ctxt));
+
+	vcd_do_device_state_transition(drv_ctxt,
+					   VCD_DEVICE_STATE_NULL,
+					   DEVICE_STATE_EVENT_NUMBER(term));
+
+}
+
+u32 vcd_reset_device_context(struct vcd_drv_ctxt *drv_ctxt,
+	u32 ev_code)
+{
+	struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt;
+	u32 rc = VCD_S_SUCCESS;
+
+	VCD_MSG_LOW("vcd_reset_device_context:");
+	vcd_reset_device_channels(dev_ctxt);
+	rc = vcd_power_event(&drv_ctxt->dev_ctxt, NULL,
+						 VCD_EVT_PWR_DEV_TERM_BEGIN);
+	VCD_FAILED_RETURN(rc, "VCD_EVT_PWR_DEV_TERM_BEGIN failed");
+	if (ddl_reset_hw(0))
+		VCD_MSG_HIGH("HW Reset done");
+	else
+		VCD_MSG_FATAL("HW Reset failed");
+
+	(void)vcd_power_event(dev_ctxt, NULL, VCD_EVT_PWR_DEV_TERM_END);
+
+	return VCD_S_SUCCESS;
+}
+
+void vcd_handle_device_err_fatal(struct vcd_dev_ctxt *dev_ctxt,
+	struct vcd_clnt_ctxt *trig_clnt)
+{
+	struct vcd_clnt_ctxt *cctxt = dev_ctxt->cctxt_list_head;
+	struct vcd_clnt_ctxt *tmp_clnt = NULL;
+	VCD_MSG_LOW("vcd_handle_device_err_fatal:");
+	while (cctxt) {
+		tmp_clnt = cctxt;
+		cctxt = cctxt->next;
+		if (tmp_clnt != trig_clnt)
+			vcd_clnt_handle_device_err_fatal(tmp_clnt,
+				tmp_clnt->status.last_evt);
+	}
+	dev_ctxt->pending_cmd = VCD_CMD_DEVICE_RESET;
+	if (!dev_ctxt->cctxt_list_head)
+		vcd_do_device_state_transition(vcd_get_drv_context(),
+			VCD_DEVICE_STATE_NOT_INIT,
+			DEVICE_STATE_EVENT_NUMBER(timeout));
+	else
+		vcd_do_device_state_transition(vcd_get_drv_context(),
+			VCD_DEVICE_STATE_INVALID,
+			DEVICE_STATE_EVENT_NUMBER(dev_cb));
+}
+
+void vcd_handle_for_last_clnt_close(
+	struct vcd_dev_ctxt *dev_ctxt, u32 send_deinit)
+{
+	if (!dev_ctxt->cctxt_list_head) {
+		VCD_MSG_HIGH("All clients are closed");
+		if (send_deinit)
+			(void) vcd_deinit_device_context(
+				vcd_get_drv_context(),
+				DEVICE_STATE_EVENT_NUMBER(close));
+		else
+			dev_ctxt->pending_cmd =
+			VCD_CMD_DEVICE_TERM;
+	}
+}
+void vcd_continue(void)
+{
+	struct vcd_drv_ctxt *drv_ctxt;
+	struct vcd_dev_ctxt *dev_ctxt;
+	u32 command_continue;
+	struct vcd_transc *transc;
+	u32 rc;
+	VCD_MSG_LOW("vcd_continue:");
+
+	drv_ctxt = vcd_get_drv_context();
+	dev_ctxt = &drv_ctxt->dev_ctxt;
+
+	dev_ctxt->command_continue = false;
+
+	if (dev_ctxt->pending_cmd == VCD_CMD_DEVICE_INIT) {
+		VCD_MSG_HIGH("VCD_CMD_DEVICE_INIT is pending");
+
+		dev_ctxt->pending_cmd = VCD_CMD_NONE;
+
+		(void)vcd_init_device_context(drv_ctxt,
+			DEVICE_STATE_EVENT_NUMBER(open));
+	} else if (dev_ctxt->pending_cmd == VCD_CMD_DEVICE_TERM) {
+		VCD_MSG_HIGH("VCD_CMD_DEVICE_TERM is pending");
+
+		dev_ctxt->pending_cmd = VCD_CMD_NONE;
+
+		(void)vcd_deinit_device_context(drv_ctxt,
+			DEVICE_STATE_EVENT_NUMBER(close));
+	} else if (dev_ctxt->pending_cmd == VCD_CMD_DEVICE_RESET) {
+		VCD_MSG_HIGH("VCD_CMD_DEVICE_RESET is pending");
+		dev_ctxt->pending_cmd = VCD_CMD_NONE;
+		(void)vcd_reset_device_context(drv_ctxt,
+			DEVICE_STATE_EVENT_NUMBER(dev_cb));
+	} else {
+		if (dev_ctxt->set_perf_lvl_pending) {
+			rc = vcd_power_event(dev_ctxt, NULL,
+						 VCD_EVT_PWR_DEV_SET_PERFLVL);
+
+			if (VCD_FAILED(rc)) {
+				VCD_MSG_ERROR
+					("VCD_EVT_PWR_CLNT_SET_PERFLVL failed");
+				VCD_MSG_HIGH
+					("Not running at desired perf level."
+					 "curr=%d, reqd=%d",
+					 dev_ctxt->curr_perf_lvl,
+					 dev_ctxt->reqd_perf_lvl);
+			} else {
+				dev_ctxt->set_perf_lvl_pending = false;
+			}
+		}
+
+		do {
+			command_continue = false;
+
+			if (vcd_get_command_channel_in_loop
+				(dev_ctxt, &transc)) {
+				if (vcd_submit_command_in_continue(dev_ctxt,
+					transc))
+					command_continue = true;
+				else {
+					VCD_MSG_MED
+						("No more commands to submit");
+
+					vcd_release_command_channel(dev_ctxt,
+						transc);
+
+					vcd_release_interim_command_channels
+						(dev_ctxt);
+				}
+			}
+		} while (command_continue);
+
+		do {
+			command_continue = false;
+
+			if (vcd_get_frame_channel_in_loop
+				(dev_ctxt, &transc)) {
+				if (vcd_try_submit_frame_in_continue(dev_ctxt,
+					transc)) {
+					command_continue = true;
+				} else {
+					VCD_MSG_MED("No more frames to submit");
+
+					vcd_release_frame_channel(dev_ctxt,
+								  transc);
+
+					vcd_release_interim_frame_channels
+						(dev_ctxt);
+				}
+			}
+
+		} while (command_continue);
+
+		if (!vcd_core_is_busy(dev_ctxt)) {
+			rc = vcd_power_event(dev_ctxt, NULL,
+				VCD_EVT_PWR_CLNT_CMD_END);
+
+			if (VCD_FAILED(rc))
+				VCD_MSG_ERROR("Failed:"
+					"VCD_EVT_PWR_CLNT_CMD_END");
+		}
+	}
+}
+
+static void vcd_pause_all_sessions(struct vcd_dev_ctxt *dev_ctxt)
+{
+	struct vcd_clnt_ctxt *cctxt = dev_ctxt->cctxt_list_head;
+	u32 rc;
+
+	while (cctxt) {
+		if (cctxt->clnt_state.state_table->ev_hdlr.pause) {
+			rc = cctxt->clnt_state.state_table->ev_hdlr.
+				pause(cctxt);
+
+			if (VCD_FAILED(rc))
+				VCD_MSG_ERROR("Client pause failed");
+
+		}
+
+		cctxt = cctxt->next;
+	}
+}
+
+static void vcd_resume_all_sessions(struct vcd_dev_ctxt *dev_ctxt)
+{
+	struct vcd_clnt_ctxt *cctxt = dev_ctxt->cctxt_list_head;
+	u32 rc;
+
+	while (cctxt) {
+		if (cctxt->clnt_state.state_table->ev_hdlr.resume) {
+			rc = cctxt->clnt_state.state_table->ev_hdlr.
+				resume(cctxt);
+
+			if (VCD_FAILED(rc))
+				VCD_MSG_ERROR("Client resume failed");
+
+		}
+
+		cctxt = cctxt->next;
+	}
+}
+
+static u32 vcd_init_cmn
+	(struct vcd_drv_ctxt *drv_ctxt,
+	 struct vcd_init_config *config, s32 *driver_handle)
+{
+	struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt;
+	s32 driver_id;
+
+	if (dev_ctxt->config.interrupt_clr !=
+		config->interrupt_clr
+		|| dev_ctxt->config.register_isr !=
+		config->register_isr
+		|| dev_ctxt->config.deregister_isr !=
+		config->deregister_isr
+		|| dev_ctxt->config.map_dev_base_addr !=
+		config->map_dev_base_addr
+		|| dev_ctxt->config.un_map_dev_base_addr !=
+		config->un_map_dev_base_addr) {
+		VCD_MSG_HIGH("Device config mismatch. "
+			"VCD will be using config from 1st vcd_init");
+	}
+
+	*driver_handle = 0;
+
+	driver_id = 0;
+	while (driver_id < VCD_DRIVER_INSTANCE_MAX &&
+		   dev_ctxt->driver_ids[driver_id]) {
+		++driver_id;
+	}
+
+	if (driver_id == VCD_DRIVER_INSTANCE_MAX) {
+		VCD_MSG_ERROR("Max driver instances reached");
+
+		return VCD_ERR_FAIL;
+	}
+
+	++dev_ctxt->refs;
+	dev_ctxt->driver_ids[driver_id] = true;
+	*driver_handle = driver_id + 1;
+
+	VCD_MSG_HIGH("Driver_id = %d. No of driver instances = %d",
+			 driver_id, dev_ctxt->refs);
+
+	return VCD_S_SUCCESS;
+
+}
+
+static u32 vcd_init_in_null
+	(struct vcd_drv_ctxt *drv_ctxt,
+	 struct vcd_init_config *config, s32 *driver_handle) {
+	u32 rc = VCD_S_SUCCESS;
+	struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt;
+	u32 done_create_timer = false;
+	VCD_MSG_LOW("vcd_init_in_dev_null:");
+
+
+	dev_ctxt->config = *config;
+
+	dev_ctxt->device_base_addr =
+		(u8 *)config->map_dev_base_addr(
+			dev_ctxt->config.device_name);
+
+	if (!dev_ctxt->device_base_addr) {
+		VCD_MSG_ERROR("NULL Device_base_addr");
+
+		return VCD_ERR_FAIL;
+	}
+
+	if (config->register_isr) {
+		config->register_isr(dev_ctxt->config.
+			device_name);
+	}
+
+	if (config->timer_create) {
+		if (config->timer_create(vcd_hw_timeout_handler,
+			NULL, &dev_ctxt->hw_timer_handle))
+			done_create_timer = true;
+		else {
+			VCD_MSG_ERROR("timercreate failed");
+			return VCD_ERR_FAIL;
+		}
+	}
+
+
+	rc = vcd_init_cmn(drv_ctxt, config, driver_handle);
+
+	if (!VCD_FAILED(rc)) {
+		vcd_do_device_state_transition(drv_ctxt,
+						   VCD_DEVICE_STATE_NOT_INIT,
+						   DEVICE_STATE_EVENT_NUMBER
+						   (init));
+	} else {
+		if (dev_ctxt->config.un_map_dev_base_addr)
+			dev_ctxt->config.un_map_dev_base_addr();
+
+		if (dev_ctxt->config.deregister_isr)
+			dev_ctxt->config.deregister_isr();
+
+		if (done_create_timer && dev_ctxt->config.timer_release)
+			dev_ctxt->config.timer_release(dev_ctxt->
+				hw_timer_handle);
+
+	}
+
+	return rc;
+
+}
+
+static u32 vcd_init_in_not_init
+	(struct vcd_drv_ctxt *drv_ctxt,
+	 struct vcd_init_config *config, s32 *driver_handle)
+{
+
+	VCD_MSG_LOW("vcd_init_in_dev_not_init:");
+
+	return vcd_init_cmn(drv_ctxt, config, driver_handle);
+
+}
+
+static u32 vcd_init_in_initing
+	(struct vcd_drv_ctxt *drv_ctxt,
+	 struct vcd_init_config *config, s32 *driver_handle) {
+
+	VCD_MSG_LOW("vcd_init_in_dev_initing:");
+
+	return vcd_init_cmn(drv_ctxt, config, driver_handle);
+
+}
+
+static u32 vcd_init_in_ready
+	(struct vcd_drv_ctxt *drv_ctxt,
+	 struct vcd_init_config *config, s32 *driver_handle)
+{
+	VCD_MSG_LOW("vcd_init_in_dev_ready:");
+
+	return vcd_init_cmn(drv_ctxt, config, driver_handle);
+}
+
+static u32 vcd_term_cmn
+	(struct vcd_drv_ctxt *drv_ctxt, s32 driver_handle)
+{
+	struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt;
+
+	if (!vcd_validate_driver_handle(dev_ctxt, driver_handle)) {
+		VCD_MSG_ERROR("Invalid driver handle = %d", driver_handle);
+
+		return VCD_ERR_BAD_HANDLE;
+	}
+
+	if (vcd_check_for_client_context(dev_ctxt,
+				driver_handle - 1)) {
+		VCD_MSG_ERROR("Driver has active client");
+
+		return VCD_ERR_BAD_STATE;
+	}
+
+	--dev_ctxt->refs;
+	dev_ctxt->driver_ids[driver_handle - 1] = false;
+
+	VCD_MSG_HIGH("Driver_id %d terminated. No of driver instances = %d",
+			 driver_handle - 1, dev_ctxt->refs);
+
+	return VCD_S_SUCCESS;
+}
+
+static u32 vcd_term_in_not_init
+	(struct vcd_drv_ctxt *drv_ctxt, s32 driver_handle)
+{
+	struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt;
+	u32 rc;
+
+	VCD_MSG_LOW("vcd_term_in_dev_not_init:");
+
+	rc = vcd_term_cmn(drv_ctxt, driver_handle);
+
+	if (!VCD_FAILED(rc) && !dev_ctxt->refs)
+		vcd_term_driver_context(drv_ctxt);
+
+	return rc;
+}
+
+static u32 vcd_term_in_initing
+	(struct vcd_drv_ctxt *drv_ctxt, s32 driver_handle)
+{
+	VCD_MSG_LOW("vcd_term_in_dev_initing:");
+
+	return vcd_term_cmn(drv_ctxt, driver_handle);
+}
+
+static u32 vcd_term_in_ready
+	(struct vcd_drv_ctxt *drv_ctxt, s32 driver_handle)
+{
+	VCD_MSG_LOW("vcd_term_in_dev_ready:");
+
+	return vcd_term_cmn(drv_ctxt, driver_handle);
+}
+
+static u32  vcd_term_in_invalid(struct vcd_drv_ctxt *drv_ctxt,
+							 s32  driver_handle)
+{
+	u32 rc;
+	VCD_MSG_LOW("vcd_term_in_invalid:");
+	rc = vcd_term_cmn(drv_ctxt, driver_handle);
+	if (!VCD_FAILED(rc) && !drv_ctxt->dev_ctxt.refs)
+		vcd_term_driver_context(drv_ctxt);
+
+	return rc;
+}
+
+static u32 vcd_open_cmn
+	(struct vcd_drv_ctxt *drv_ctxt,
+	 s32 driver_handle,
+	 u32 decoding,
+	 void (*callback) (u32 event, u32 status, void *info, size_t sz,
+			   void *handle, void *const client_data),
+	 void *client_data, struct vcd_clnt_ctxt ** clnt_cctxt)
+{
+	struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt;
+	struct vcd_clnt_ctxt *cctxt;
+	struct vcd_clnt_ctxt *client;
+
+	if (!vcd_validate_driver_handle(dev_ctxt, driver_handle)) {
+		VCD_MSG_ERROR("Invalid driver handle = %d", driver_handle);
+
+		return VCD_ERR_BAD_HANDLE;
+	}
+
+	cctxt =	(struct vcd_clnt_ctxt *)
+		kmalloc(sizeof(struct vcd_clnt_ctxt), GFP_KERNEL);
+	if (!cctxt) {
+		VCD_MSG_ERROR("No memory for client ctxt");
+
+		return VCD_ERR_ALLOC_FAIL;
+	}
+
+	memset(cctxt, 0, sizeof(struct vcd_clnt_ctxt));
+	cctxt->dev_ctxt = dev_ctxt;
+	cctxt->driver_id = driver_handle - 1;
+	cctxt->decoding = decoding;
+	cctxt->callback = callback;
+	cctxt->client_data = client_data;
+	cctxt->status.last_evt = VCD_EVT_RESP_OPEN;
+	INIT_LIST_HEAD(&cctxt->in_buf_pool.queue);
+	INIT_LIST_HEAD(&cctxt->out_buf_pool.queue);
+	client = dev_ctxt->cctxt_list_head;
+	dev_ctxt->cctxt_list_head = cctxt;
+	cctxt->next = client;
+
+	*clnt_cctxt = cctxt;
+
+	return VCD_S_SUCCESS;
+
+}
+
+static u32 vcd_open_in_not_init
+	(struct vcd_drv_ctxt *drv_ctxt,
+	 s32 driver_handle,
+	 u32 decoding,
+	 void (*callback) (u32 event, u32 status, void *info, size_t sz,
+			   void *handle, void *const client_data),
+	 void *client_data)
+{
+	struct vcd_clnt_ctxt *cctxt;
+	u32 rc;
+
+	VCD_MSG_LOW("vcd_open_in_dev_not_init:");
+
+	rc = vcd_open_cmn(drv_ctxt, driver_handle, decoding, callback,
+			  client_data, &cctxt);
+
+	VCD_FAILED_RETURN(rc, "Failed: vcd_open_cmn");
+
+	rc = vcd_init_device_context(drv_ctxt,
+					 DEVICE_STATE_EVENT_NUMBER(open));
+
+	if (VCD_FAILED(rc))
+		vcd_destroy_client_context(cctxt);
+
+	return rc;
+}
+
+static u32 vcd_open_in_initing(struct vcd_drv_ctxt *drv_ctxt,
+	 s32 driver_handle, u32 decoding,
+	 void (*callback) (u32 event, u32 status, void *info, size_t sz,
+			   void *handle, void *const client_data),
+	 void *client_data)
+{
+	struct vcd_clnt_ctxt *cctxt;
+
+	VCD_MSG_LOW("vcd_open_in_dev_initing:");
+
+	return vcd_open_cmn(drv_ctxt, driver_handle, decoding, callback,
+				 client_data, &cctxt);
+}
+
+static u32 vcd_open_in_ready
+	(struct vcd_drv_ctxt *drv_ctxt,
+	 s32 driver_handle,
+	 u32 decoding,
+	 void (*callback) (u32 event, u32 status, void *info, size_t sz,
+			   void *handle, void *const client_data),
+	 void *client_data)
+{
+	struct vcd_clnt_ctxt *cctxt;
+	struct vcd_handle_container container;
+	u32 rc;
+
+	VCD_MSG_LOW("vcd_open_in_dev_ready:");
+
+	rc = vcd_open_cmn(drv_ctxt, driver_handle, decoding, callback,
+			  client_data, &cctxt);
+
+	VCD_FAILED_RETURN(rc, "Failed: vcd_open_cmn");
+
+	rc = vcd_init_client_context(cctxt);
+
+	if (!VCD_FAILED(rc)) {
+		container.handle = (void *)cctxt;
+
+		callback(VCD_EVT_RESP_OPEN,
+			 VCD_S_SUCCESS,
+			 &container,
+			 sizeof(container), container.handle, client_data);
+	} else {
+		VCD_MSG_ERROR("rc = 0x%x. Failed: vcd_init_client_context", rc);
+
+		vcd_destroy_client_context(cctxt);
+	}
+
+	return rc;
+}
+
+static u32 vcd_close_in_ready
+	(struct vcd_drv_ctxt *drv_ctxt,
+	 struct vcd_clnt_ctxt *cctxt) {
+	u32 rc;
+
+	VCD_MSG_LOW("vcd_close_in_dev_ready:");
+
+	if (cctxt->clnt_state.state_table->ev_hdlr.close) {
+		rc = cctxt->clnt_state.state_table->ev_hdlr.
+			close(cctxt);
+	} else {
+		VCD_MSG_ERROR("Unsupported API in client state %d",
+				  cctxt->clnt_state.state);
+
+		rc = VCD_ERR_BAD_STATE;
+	}
+
+	if (!VCD_FAILED(rc))
+		vcd_handle_for_last_clnt_close(&drv_ctxt->dev_ctxt, true);
+
+	return rc;
+}
+
+static u32  vcd_close_in_dev_invalid(struct vcd_drv_ctxt *drv_ctxt,
+	struct vcd_clnt_ctxt *cctxt)
+{
+	u32 rc;
+	VCD_MSG_LOW("vcd_close_in_dev_invalid:");
+	if (cctxt->clnt_state.state_table->ev_hdlr.close) {
+		rc = cctxt->clnt_state.state_table->
+			ev_hdlr.close(cctxt);
+	} else {
+		VCD_MSG_ERROR("Unsupported API in client state %d",
+					  cctxt->clnt_state.state);
+		rc = VCD_ERR_BAD_STATE;
+	}
+	if (!VCD_FAILED(rc) && !drv_ctxt->dev_ctxt.
+		cctxt_list_head) {
+		VCD_MSG_HIGH("All INVALID clients are closed");
+		vcd_do_device_state_transition(drv_ctxt,
+			VCD_DEVICE_STATE_NOT_INIT,
+			DEVICE_STATE_EVENT_NUMBER(close));
+	}
+	return rc;
+}
+
+static u32 vcd_resume_in_ready
+	(struct vcd_drv_ctxt *drv_ctxt,
+	 struct vcd_clnt_ctxt *cctxt) {
+	u32 rc = VCD_S_SUCCESS;
+
+	VCD_MSG_LOW("vcd_resume_in_ready:");
+
+	if (cctxt->clnt_state.state_table->ev_hdlr.resume) {
+		rc = cctxt->clnt_state.state_table->ev_hdlr.
+			resume(cctxt);
+	} else {
+		VCD_MSG_ERROR("Unsupported API in client state %d",
+				  cctxt->clnt_state.state);
+
+		rc = VCD_ERR_BAD_STATE;
+	}
+
+	return rc;
+}
+
+static u32 vcd_set_dev_pwr_in_ready
+	(struct vcd_drv_ctxt *drv_ctxt,
+	 enum vcd_power_state pwr_state)
+{
+	u32 rc = VCD_S_SUCCESS;
+	struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt;
+
+	VCD_MSG_LOW("vcd_set_dev_pwr_in_ready:");
+
+	switch (pwr_state) {
+	case VCD_PWR_STATE_SLEEP:
+		{
+			if (dev_ctxt->pwr_state == VCD_PWR_STATE_ON)
+				vcd_pause_all_sessions(dev_ctxt);
+			dev_ctxt->pwr_state = VCD_PWR_STATE_SLEEP;
+			break;
+		}
+
+	case VCD_PWR_STATE_ON:
+		{
+			if (dev_ctxt->pwr_state == VCD_PWR_STATE_SLEEP)
+				vcd_resume_all_sessions(dev_ctxt);
+			dev_ctxt->pwr_state = VCD_PWR_STATE_ON;
+			break;
+		}
+
+	default:
+		{
+			VCD_MSG_ERROR("Invalid power state requested %d",
+					  pwr_state);
+			break;
+		}
+
+	}
+
+	return rc;
+}
+
+static void vcd_dev_cb_in_initing
+	(struct vcd_drv_ctxt *drv_ctxt,
+	 u32 event,
+	 u32 status,
+	 void *payload, size_t sz, u32 *ddl_handle, void *const client_data)
+{
+	struct vcd_dev_ctxt *dev_ctxt;
+	struct vcd_clnt_ctxt *client;
+	struct vcd_clnt_ctxt *tmp_client;
+	struct vcd_handle_container container;
+	u32 rc = VCD_S_SUCCESS;
+	u32 client_inited = false;
+	u32 fail_all_open = false;
+	struct ddl_context *ddl_context;
+
+	ddl_context = ddl_get_context();
+
+	VCD_MSG_LOW("vcd_dev_cb_in_initing:");
+
+	if (event != VCD_EVT_RESP_DEVICE_INIT) {
+		VCD_MSG_ERROR("vcd_dev_cb_in_initing: Unexpected event %d",
+				  (int)event);
+		return;
+	}
+
+	dev_ctxt = &drv_ctxt->dev_ctxt;
+
+	dev_ctxt->command_continue = false;
+
+	if (VCD_FAILED(status)) {
+		vcd_handle_device_init_failed(drv_ctxt, status);
+
+		return;
+	}
+
+	vcd_do_device_state_transition(drv_ctxt,
+					   VCD_DEVICE_STATE_READY,
+					   DEVICE_STATE_EVENT_NUMBER(open));
+
+	if (!dev_ctxt->cctxt_list_head) {
+		VCD_MSG_HIGH("All clients are closed");
+
+		dev_ctxt->pending_cmd = VCD_CMD_DEVICE_TERM;
+
+		return;
+	}
+
+	if (!dev_ctxt->ddl_cmd_ch_depth
+		|| !dev_ctxt->trans_tbl)
+		rc = vcd_setup_with_ddl_capabilities(dev_ctxt);
+
+
+	if (VCD_FAILED(rc)) {
+		VCD_MSG_ERROR
+			("rc = 0x%x: Failed vcd_setup_with_ddl_capabilities",
+			 rc);
+
+		fail_all_open = true;
+	}
+
+	client = dev_ctxt->cctxt_list_head;
+	while (client) {
+		if (!fail_all_open)
+			rc = vcd_init_client_context(client);
+
+
+		if (!VCD_FAILED(rc)) {
+			container.handle = (void *)client;
+			client->callback(VCD_EVT_RESP_OPEN,
+					   VCD_S_SUCCESS,
+					   &container,
+					   sizeof(container),
+					   container.handle,
+					   client->client_data);
+
+			client = client->next;
+
+			client_inited = true;
+		} else {
+			VCD_MSG_ERROR
+				("rc = 0x%x, Failed: vcd_init_client_context",
+				 rc);
+
+			client->callback(VCD_EVT_RESP_OPEN,
+					   rc,
+					   NULL, 0, 0, client->client_data);
+
+			tmp_client = client;
+			client = client->next;
+			if (tmp_client == dev_ctxt->cctxt_list_head)
+				fail_all_open = true;
+
+			vcd_destroy_client_context(tmp_client);
+		}
+	}
+
+	if (!client_inited || fail_all_open) {
+		VCD_MSG_ERROR("All client open requests failed");
+
+		DDL_IDLE(ddl_context);
+
+		vcd_handle_device_init_failed(drv_ctxt,
+			DEVICE_STATE_EVENT_NUMBER(close));
+		dev_ctxt->pending_cmd = VCD_CMD_DEVICE_TERM;
+	} else {
+		if (vcd_power_event(dev_ctxt, NULL,
+					 VCD_EVT_PWR_DEV_INIT_END)) {
+			VCD_MSG_ERROR("VCD_EVT_PWR_DEV_INIT_END failed");
+		}
+	}
+}
+
+static void  vcd_hw_timeout_cmn(struct vcd_drv_ctxt *drv_ctxt,
+							  void *user_data)
+{
+	struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt;
+	VCD_MSG_LOW("vcd_hw_timeout_cmn:");
+	vcd_device_timer_stop(dev_ctxt);
+
+	vcd_handle_device_err_fatal(dev_ctxt, NULL);
+
+	/* Reset HW. */
+	(void) vcd_reset_device_context(drv_ctxt,
+		DEVICE_STATE_EVENT_NUMBER(timeout));
+}
+
+static void vcd_dev_enter_null
+	(struct vcd_drv_ctxt *drv_ctxt, s32 state_event) {
+	VCD_MSG_MED("Entering DEVICE_STATE_NULL on api %d", state_event);
+
+}
+
+static void vcd_dev_enter_not_init
+	(struct vcd_drv_ctxt *drv_ctxt, s32 state_event) {
+	VCD_MSG_MED("Entering DEVICE_STATE_NOT_INIT on api %d",
+			state_event);
+
+}
+
+static void vcd_dev_enter_initing
+	(struct vcd_drv_ctxt *drv_ctxt, s32 state_event) {
+	VCD_MSG_MED("Entering DEVICE_STATE_INITING on api %d",
+			state_event);
+
+}
+
+static void vcd_dev_enter_ready
+	(struct vcd_drv_ctxt *drv_ctxt, s32 state_event) {
+	VCD_MSG_MED("Entering DEVICE_STATE_READY on api %d",
+			state_event);
+}
+
+static void vcd_dev_enter_invalid(struct vcd_drv_ctxt *drv_ctxt,
+							   s32 state_event)
+{
+   VCD_MSG_MED("Entering DEVICE_STATE_INVALID on api %d", state_event);
+}
+
+static void vcd_dev_exit_null
+	(struct vcd_drv_ctxt *drv_ctxt, s32 state_event) {
+	VCD_MSG_MED("Exiting DEVICE_STATE_NULL on api %d", state_event);
+}
+
+static void vcd_dev_exit_not_init
+	(struct vcd_drv_ctxt *drv_ctxt, s32 state_event) {
+	VCD_MSG_MED("Exiting DEVICE_STATE_NOT_INIT on api %d",
+			state_event);
+
+}
+
+static void vcd_dev_exit_initing
+	(struct vcd_drv_ctxt *drv_ctxt, s32 state_event) {
+	VCD_MSG_MED("Exiting DEVICE_STATE_INITING on api %d",
+			state_event);
+}
+
+static void vcd_dev_exit_ready
+	(struct vcd_drv_ctxt *drv_ctxt, s32 state_event) {
+	VCD_MSG_MED("Exiting DEVICE_STATE_READY on api %d", state_event);
+}
+
+static void vcd_dev_exit_invalid(struct vcd_drv_ctxt *drv_ctxt,
+							  s32 state_event)
+{
+   VCD_MSG_MED("Exiting DEVICE_STATE_INVALID on api %d", state_event);
+}
+
+static const struct vcd_dev_state_table vcd_dev_table_null = {
+	{
+	 vcd_init_in_null,
+	 NULL,
+	 NULL,
+	 NULL,
+	 NULL,
+	 NULL,
+	 NULL,
+	 NULL,
+	 },
+	vcd_dev_enter_null,
+	vcd_dev_exit_null
+};
+
+static const struct vcd_dev_state_table vcd_dev_table_not_init = {
+	{
+	 vcd_init_in_not_init,
+	 vcd_term_in_not_init,
+	 vcd_open_in_not_init,
+	 NULL,
+	 NULL,
+	 NULL,
+	 NULL,
+	 NULL,
+	 },
+	vcd_dev_enter_not_init,
+	vcd_dev_exit_not_init
+};
+
+static const struct vcd_dev_state_table vcd_dev_table_initing = {
+	{
+	 vcd_init_in_initing,
+	 vcd_term_in_initing,
+	 vcd_open_in_initing,
+	 NULL,
+	 NULL,
+	 NULL,
+	 vcd_dev_cb_in_initing,
+	 vcd_hw_timeout_cmn,
+	 },
+	vcd_dev_enter_initing,
+	vcd_dev_exit_initing
+};
+
+static const struct vcd_dev_state_table vcd_dev_table_ready = {
+	{
+	 vcd_init_in_ready,
+	 vcd_term_in_ready,
+	 vcd_open_in_ready,
+	 vcd_close_in_ready,
+	 vcd_resume_in_ready,
+	 vcd_set_dev_pwr_in_ready,
+	 NULL,
+	 vcd_hw_timeout_cmn,
+	 },
+	vcd_dev_enter_ready,
+	vcd_dev_exit_ready
+};
+
+static const struct vcd_dev_state_table vcd_dev_table_in_invalid = {
+	{
+		NULL,
+		vcd_term_in_invalid,
+		NULL,
+		vcd_close_in_dev_invalid,
+		NULL,
+		NULL,
+		NULL,
+		NULL,
+	},
+	vcd_dev_enter_invalid,
+	vcd_dev_exit_invalid
+};
+
+static const struct vcd_dev_state_table *vcd_dev_state_table[] = {
+	&vcd_dev_table_null,
+	&vcd_dev_table_not_init,
+	&vcd_dev_table_initing,
+	&vcd_dev_table_ready,
+	&vcd_dev_table_in_invalid
+};
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_device_sm.h b/drivers/video/msm/vidc/common/vcd/vcd_device_sm.h
new file mode 100644
index 0000000..2443c33
--- /dev/null
+++ b/drivers/video/msm/vidc/common/vcd/vcd_device_sm.h
@@ -0,0 +1,96 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef _VCD_DEVICE_SM_H_
+#define _VCD_DEVICE_SM_H_
+
+#include <media/msm/vcd_api.h>
+#include "vcd_ddl_api.h"
+#include "vcd_core.h"
+
+struct vcd_dev_state_table;
+struct vcd_dev_state_ctxt;
+struct vcd_drv_ctxt;
+
+enum vcd_dev_state_enum {
+	VCD_DEVICE_STATE_NULL = 0,
+	VCD_DEVICE_STATE_NOT_INIT,
+	VCD_DEVICE_STATE_INITING,
+	VCD_DEVICE_STATE_READY,
+	VCD_DEVICE_STATE_INVALID,
+	VCD_DEVICE_STATE_MAX,
+	VCD_DEVICE_STATE_32BIT = 0x7FFFFFFF
+};
+
+struct vcd_dev_state_table {
+	struct {
+		u32(*init) (struct vcd_drv_ctxt *drv_ctxt,
+				struct vcd_init_config *config,
+				s32 *driver_handle);
+
+		u32(*term) (struct vcd_drv_ctxt *drv_ctxt,
+				s32 driver_handle);
+
+		u32(*open) (struct vcd_drv_ctxt *drv_ctxt,
+				s32 driver_handle, u32 decoding,
+				void (*callback) (u32 event, u32 status,
+					void *info, size_t sz, void *handle,
+					void *const client_data),
+				void *client_data);
+
+		u32(*close) (struct vcd_drv_ctxt *drv_ctxt,
+				struct vcd_clnt_ctxt *cctxt);
+
+		u32(*resume) (struct vcd_drv_ctxt *drv_ctxt,
+				struct vcd_clnt_ctxt *cctxt);
+
+		u32(*set_dev_pwr) (struct vcd_drv_ctxt *drv_ctxt,
+				enum vcd_power_state pwr_state);
+
+		void (*dev_cb) (struct vcd_drv_ctxt *drv_ctxt,
+				u32 event, u32 status, void *payload,
+				size_t sz, u32 *ddl_handle,
+				void *const client_data);
+
+		void (*timeout) (struct vcd_drv_ctxt *drv_ctxt,
+							void *user_data);
+	} ev_hdlr;
+
+	void (*entry) (struct vcd_drv_ctxt *drv_ctxt,
+			s32 state_event);
+	void (*exit) (struct vcd_drv_ctxt *drv_ctxt,
+			s32 state_event);
+};
+
+#define   DEVICE_STATE_EVENT_NUMBER(ppf) \
+	((u32 *) (&(((struct vcd_dev_state_table*)0)->ev_hdlr.ppf)) - \
+	(u32 *) (&(((struct vcd_dev_state_table*)0)->ev_hdlr.init)) \
+	+ 1)
+
+struct vcd_dev_state_ctxt {
+	const struct vcd_dev_state_table *state_table;
+
+	enum vcd_dev_state_enum state;
+};
+
+struct vcd_drv_ctxt {
+	struct vcd_dev_state_ctxt dev_state;
+	struct vcd_dev_ctxt dev_ctxt;
+	struct mutex dev_mutex;
+};
+
+
+extern struct vcd_drv_ctxt *vcd_get_drv_context(void);
+
+void vcd_continue(void);
+
+#endif
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_power_sm.c b/drivers/video/msm/vidc/common/vcd/vcd_power_sm.c
new file mode 100644
index 0000000..beaa872
--- /dev/null
+++ b/drivers/video/msm/vidc/common/vcd/vcd_power_sm.c
@@ -0,0 +1,366 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <media/msm/vidc_type.h>
+#include "vcd_power_sm.h"
+#include "vcd_core.h"
+#include "vcd.h"
+
+u32 vcd_power_event(
+	struct vcd_dev_ctxt *dev_ctxt,
+     struct vcd_clnt_ctxt *cctxt, u32 event)
+{
+	u32 rc = VCD_S_SUCCESS;
+
+	VCD_MSG_MED("Device power state = %d", dev_ctxt->pwr_clk_state);
+	VCD_MSG_MED("event = 0x%x", event);
+	switch (event) {
+
+	case VCD_EVT_PWR_DEV_INIT_BEGIN:
+	case VCD_EVT_PWR_DEV_INIT_END:
+	case VCD_EVT_PWR_DEV_INIT_FAIL:
+	case VCD_EVT_PWR_DEV_TERM_BEGIN:
+	case VCD_EVT_PWR_DEV_TERM_END:
+	case VCD_EVT_PWR_DEV_TERM_FAIL:
+	case VCD_EVT_PWR_DEV_SLEEP_BEGIN:
+	case VCD_EVT_PWR_DEV_SLEEP_END:
+	case VCD_EVT_PWR_DEV_SET_PERFLVL:
+	case VCD_EVT_PWR_DEV_HWTIMEOUT:
+		{
+			rc = vcd_device_power_event(dev_ctxt, event,
+				cctxt);
+			break;
+		}
+
+	case VCD_EVT_PWR_CLNT_CMD_BEGIN:
+	case VCD_EVT_PWR_CLNT_CMD_END:
+	case VCD_EVT_PWR_CLNT_CMD_FAIL:
+	case VCD_EVT_PWR_CLNT_PAUSE:
+	case VCD_EVT_PWR_CLNT_RESUME:
+	case VCD_EVT_PWR_CLNT_FIRST_FRAME:
+	case VCD_EVT_PWR_CLNT_LAST_FRAME:
+	case VCD_EVT_PWR_CLNT_ERRFATAL:
+		{
+			rc = vcd_client_power_event(dev_ctxt, cctxt, event);
+			break;
+		}
+
+	}
+
+	if (VCD_FAILED(rc))
+		VCD_MSG_ERROR("vcd_power_event: event 0x%x failed", event);
+
+
+	return rc;
+
+}
+
+u32 vcd_device_power_event(struct vcd_dev_ctxt *dev_ctxt, u32 event,
+	struct vcd_clnt_ctxt *cctxt)
+{
+	u32 rc = VCD_ERR_FAIL;
+	u32 set_perf_lvl;
+
+	switch (event) {
+
+	case VCD_EVT_PWR_DEV_INIT_BEGIN:
+	{
+		if (dev_ctxt->pwr_clk_state ==
+			VCD_PWRCLK_STATE_OFF) {
+			if (res_trk_get_max_perf_level(&dev_ctxt->
+				max_perf_lvl)) {
+				if (res_trk_power_up()) {
+					dev_ctxt->pwr_clk_state =
+					VCD_PWRCLK_STATE_ON_NOTCLOCKED;
+					dev_ctxt->curr_perf_lvl = 0;
+					dev_ctxt->reqd_perf_lvl = 0;
+					dev_ctxt->active_clnts = 0;
+					dev_ctxt->
+						set_perf_lvl_pending = false;
+					rc = vcd_enable_clock(dev_ctxt,
+						cctxt);
+					if (VCD_FAILED(rc)) {
+						(void)res_trk_power_down();
+						dev_ctxt->pwr_clk_state =
+							VCD_PWRCLK_STATE_OFF;
+					}
+				}
+			}
+		}
+
+		break;
+	}
+
+	case VCD_EVT_PWR_DEV_INIT_END:
+	case VCD_EVT_PWR_DEV_TERM_FAIL:
+	case VCD_EVT_PWR_DEV_SLEEP_BEGIN:
+	case VCD_EVT_PWR_DEV_HWTIMEOUT:
+		{
+			rc = vcd_gate_clock(dev_ctxt);
+
+			break;
+		}
+
+	case VCD_EVT_PWR_DEV_INIT_FAIL:
+	case VCD_EVT_PWR_DEV_TERM_END:
+		{
+			if (dev_ctxt->pwr_clk_state !=
+				VCD_PWRCLK_STATE_OFF) {
+				(void)vcd_disable_clock(dev_ctxt);
+				(void)res_trk_power_down();
+
+				dev_ctxt->pwr_clk_state =
+				    VCD_PWRCLK_STATE_OFF;
+				dev_ctxt->curr_perf_lvl = 0;
+				dev_ctxt->reqd_perf_lvl = 0;
+				dev_ctxt->active_clnts = 0;
+				dev_ctxt->set_perf_lvl_pending = false;
+				rc = VCD_S_SUCCESS;
+			}
+
+			break;
+		}
+
+	case VCD_EVT_PWR_DEV_TERM_BEGIN:
+	case VCD_EVT_PWR_DEV_SLEEP_END:
+		{
+			rc = vcd_un_gate_clock(dev_ctxt);
+
+			break;
+		}
+
+	case VCD_EVT_PWR_DEV_SET_PERFLVL:
+		{
+			set_perf_lvl =
+			    dev_ctxt->reqd_perf_lvl >
+			    0 ? dev_ctxt->
+			    reqd_perf_lvl : VCD_MIN_PERF_LEVEL;
+			rc = vcd_set_perf_level(dev_ctxt, set_perf_lvl);
+			break;
+		}
+	}
+	return rc;
+}
+
+u32 vcd_client_power_event(
+	struct vcd_dev_ctxt *dev_ctxt,
+    struct vcd_clnt_ctxt *cctxt, u32 event)
+{
+	u32 rc = VCD_ERR_FAIL;
+
+	switch (event) {
+
+	case VCD_EVT_PWR_CLNT_CMD_BEGIN:
+		{
+			rc = vcd_un_gate_clock(dev_ctxt);
+			break;
+		}
+
+	case VCD_EVT_PWR_CLNT_CMD_END:
+		{
+			rc = vcd_gate_clock(dev_ctxt);
+			break;
+		}
+
+	case VCD_EVT_PWR_CLNT_CMD_FAIL:
+		{
+			if (!vcd_core_is_busy(dev_ctxt))
+				rc = vcd_gate_clock(dev_ctxt);
+
+			break;
+		}
+
+	case VCD_EVT_PWR_CLNT_PAUSE:
+	case VCD_EVT_PWR_CLNT_LAST_FRAME:
+	case VCD_EVT_PWR_CLNT_ERRFATAL:
+		{
+			if (cctxt) {
+				rc = VCD_S_SUCCESS;
+				if (cctxt->status.req_perf_lvl) {
+					dev_ctxt->reqd_perf_lvl -=
+						cctxt->reqd_perf_lvl;
+					cctxt->status.req_perf_lvl = false;
+					rc = vcd_set_perf_level(dev_ctxt,
+						dev_ctxt->reqd_perf_lvl);
+				}
+			}
+
+			break;
+		}
+
+	case VCD_EVT_PWR_CLNT_RESUME:
+	case VCD_EVT_PWR_CLNT_FIRST_FRAME:
+		{
+			if (cctxt) {
+				rc = VCD_S_SUCCESS;
+				if (!cctxt->status.req_perf_lvl) {
+					dev_ctxt->reqd_perf_lvl +=
+						cctxt->reqd_perf_lvl;
+					cctxt->status.req_perf_lvl = true;
+
+					rc = vcd_set_perf_level(dev_ctxt,
+						dev_ctxt->reqd_perf_lvl);
+				}
+			}
+			break;
+		}
+	}
+
+	return rc;
+}
+
+u32 vcd_enable_clock(struct vcd_dev_ctxt *dev_ctxt,
+	struct vcd_clnt_ctxt *cctxt)
+{
+	u32 rc = VCD_S_SUCCESS;
+	u32 set_perf_lvl;
+
+	if (dev_ctxt->pwr_clk_state == VCD_PWRCLK_STATE_OFF) {
+		VCD_MSG_ERROR("vcd_enable_clock(): Already in state "
+			"VCD_PWRCLK_STATE_OFF\n");
+		rc = VCD_ERR_FAIL;
+	} else if (dev_ctxt->pwr_clk_state ==
+		VCD_PWRCLK_STATE_ON_NOTCLOCKED) {
+		set_perf_lvl =
+				dev_ctxt->reqd_perf_lvl >
+				0 ? dev_ctxt->
+				reqd_perf_lvl : VCD_MIN_PERF_LEVEL;
+		rc = vcd_set_perf_level(dev_ctxt, set_perf_lvl);
+		if (!VCD_FAILED(rc)) {
+			if (res_trk_enable_clocks()) {
+				dev_ctxt->pwr_clk_state =
+					VCD_PWRCLK_STATE_ON_CLOCKED;
+			}
+		} else {
+			rc = VCD_ERR_FAIL;
+		}
+
+	}
+
+	if (!VCD_FAILED(rc))
+		dev_ctxt->active_clnts++;
+
+	return rc;
+}
+
+u32 vcd_disable_clock(struct vcd_dev_ctxt *dev_ctxt)
+{
+	u32 rc = VCD_S_SUCCESS;
+
+	if (dev_ctxt->pwr_clk_state == VCD_PWRCLK_STATE_OFF) {
+		VCD_MSG_ERROR("vcd_disable_clock(): Already in state "
+			"VCD_PWRCLK_STATE_OFF\n");
+		rc = VCD_ERR_FAIL;
+	} else if (dev_ctxt->pwr_clk_state == VCD_PWRCLK_STATE_ON_CLOCKED ||
+		dev_ctxt->pwr_clk_state == VCD_PWRCLK_STATE_ON_CLOCKGATED) {
+		dev_ctxt->active_clnts--;
+
+		if (!dev_ctxt->active_clnts) {
+			if (!res_trk_disable_clocks())
+				rc = VCD_ERR_FAIL;
+
+			dev_ctxt->pwr_clk_state =
+			    VCD_PWRCLK_STATE_ON_NOTCLOCKED;
+			dev_ctxt->curr_perf_lvl = 0;
+		}
+	}
+
+	return rc;
+}
+
+u32 vcd_set_perf_level(struct vcd_dev_ctxt *dev_ctxt, u32 perf_lvl)
+{
+	u32 rc = VCD_S_SUCCESS;
+	if (!vcd_core_is_busy(dev_ctxt)) {
+		if (res_trk_set_perf_level(perf_lvl,
+			&dev_ctxt->curr_perf_lvl, dev_ctxt)) {
+			dev_ctxt->set_perf_lvl_pending = false;
+		} else {
+			rc = VCD_ERR_FAIL;
+			dev_ctxt->set_perf_lvl_pending = true;
+		}
+
+	} else {
+		dev_ctxt->set_perf_lvl_pending = true;
+	}
+
+	return rc;
+}
+
+u32 vcd_update_decoder_perf_level(struct vcd_dev_ctxt *dev_ctxt, u32 perf_lvl)
+{
+	u32 rc = VCD_S_SUCCESS;
+
+	if (res_trk_set_perf_level(perf_lvl,
+		&dev_ctxt->curr_perf_lvl, dev_ctxt)) {
+		dev_ctxt->set_perf_lvl_pending = false;
+	} else {
+		rc = VCD_ERR_FAIL;
+		dev_ctxt->set_perf_lvl_pending = true;
+	}
+
+	return rc;
+}
+
+u32 vcd_update_clnt_perf_lvl(
+	struct vcd_clnt_ctxt *cctxt,
+     struct vcd_property_frame_rate *fps, u32 frm_p_units)
+{
+	u32 rc = VCD_S_SUCCESS;
+	struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt;
+	u32 new_perf_lvl;
+	new_perf_lvl = frm_p_units *\
+		(fps->fps_numerator / fps->fps_denominator);
+	if (cctxt->status.req_perf_lvl) {
+		dev_ctxt->reqd_perf_lvl =
+		    dev_ctxt->reqd_perf_lvl - cctxt->reqd_perf_lvl +
+		    new_perf_lvl;
+		rc = vcd_set_perf_level(cctxt->dev_ctxt,
+			dev_ctxt->reqd_perf_lvl);
+	}
+	cctxt->reqd_perf_lvl = new_perf_lvl;
+	return rc;
+}
+
+u32 vcd_gate_clock(struct vcd_dev_ctxt *dev_ctxt)
+{
+	u32 rc = VCD_S_SUCCESS;
+	if (dev_ctxt->pwr_clk_state == VCD_PWRCLK_STATE_OFF ||
+		dev_ctxt->pwr_clk_state == VCD_PWRCLK_STATE_ON_NOTCLOCKED) {
+		VCD_MSG_ERROR("%s(): Clk is Off or Not Clked yet\n", __func__);
+		rc = VCD_ERR_FAIL;
+	} else if (dev_ctxt->pwr_clk_state == VCD_PWRCLK_STATE_ON_CLOCKGATED)
+		rc = VCD_S_SUCCESS;
+	else if (res_trk_disable_clocks())
+		dev_ctxt->pwr_clk_state = VCD_PWRCLK_STATE_ON_CLOCKGATED;
+	else
+		rc = VCD_ERR_FAIL;
+	return rc;
+}
+
+u32 vcd_un_gate_clock(struct vcd_dev_ctxt *dev_ctxt)
+{
+	u32 rc = VCD_S_SUCCESS;
+	if (dev_ctxt->pwr_clk_state == VCD_PWRCLK_STATE_OFF ||
+		dev_ctxt->pwr_clk_state == VCD_PWRCLK_STATE_ON_NOTCLOCKED) {
+		VCD_MSG_ERROR("%s(): Clk is Off or Not Clked yet\n", __func__);
+		rc = VCD_ERR_FAIL;
+	} else if (dev_ctxt->pwr_clk_state == VCD_PWRCLK_STATE_ON_CLOCKED)
+		rc = VCD_S_SUCCESS;
+	else if (res_trk_enable_clocks())
+		dev_ctxt->pwr_clk_state = VCD_PWRCLK_STATE_ON_CLOCKED;
+	else
+		rc = VCD_ERR_FAIL;
+	return rc;
+}
+
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_power_sm.h b/drivers/video/msm/vidc/common/vcd/vcd_power_sm.h
new file mode 100644
index 0000000..26ce019
--- /dev/null
+++ b/drivers/video/msm/vidc/common/vcd/vcd_power_sm.h
@@ -0,0 +1,43 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef _VCD_POWERSM_H_
+#define _VCD_POWERSM_H_
+
+#define VCD_EVT_PWR_BASE                0x5000
+#define VCD_EVT_PWR_DEV_INIT_BEGIN      (VCD_EVT_PWR_BASE + 0x1)
+#define VCD_EVT_PWR_DEV_INIT_END        (VCD_EVT_PWR_BASE + 0x2)
+#define VCD_EVT_PWR_DEV_INIT_FAIL       (VCD_EVT_PWR_BASE + 0x3)
+#define VCD_EVT_PWR_DEV_TERM_BEGIN      (VCD_EVT_PWR_BASE + 0x4)
+#define VCD_EVT_PWR_DEV_TERM_END        (VCD_EVT_PWR_BASE + 0x5)
+#define VCD_EVT_PWR_DEV_TERM_FAIL       (VCD_EVT_PWR_BASE + 0x6)
+#define VCD_EVT_PWR_DEV_SLEEP_BEGIN     (VCD_EVT_PWR_BASE + 0x7)
+#define VCD_EVT_PWR_DEV_SLEEP_END       (VCD_EVT_PWR_BASE + 0x8)
+#define VCD_EVT_PWR_DEV_SET_PERFLVL     (VCD_EVT_PWR_BASE + 0x9)
+#define VCD_EVT_PWR_DEV_HWTIMEOUT       (VCD_EVT_PWR_BASE + 0xa)
+#define VCD_EVT_PWR_CLNT_CMD_BEGIN      (VCD_EVT_PWR_BASE + 0xb)
+#define VCD_EVT_PWR_CLNT_CMD_END        (VCD_EVT_PWR_BASE + 0xc)
+#define VCD_EVT_PWR_CLNT_CMD_FAIL       (VCD_EVT_PWR_BASE + 0xd)
+#define VCD_EVT_PWR_CLNT_PAUSE          (VCD_EVT_PWR_BASE + 0xe)
+#define VCD_EVT_PWR_CLNT_RESUME         (VCD_EVT_PWR_BASE + 0xf)
+#define VCD_EVT_PWR_CLNT_FIRST_FRAME    (VCD_EVT_PWR_BASE + 0x10)
+#define VCD_EVT_PWR_CLNT_LAST_FRAME     (VCD_EVT_PWR_BASE + 0x11)
+#define VCD_EVT_PWR_CLNT_ERRFATAL       (VCD_EVT_PWR_BASE + 0x12)
+
+enum vcd_pwr_clk_state {
+	VCD_PWRCLK_STATE_OFF = 0,
+	VCD_PWRCLK_STATE_ON_NOTCLOCKED,
+	VCD_PWRCLK_STATE_ON_CLOCKED,
+	VCD_PWRCLK_STATE_ON_CLOCKGATED
+};
+
+#endif
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_scheduler.c b/drivers/video/msm/vidc/common/vcd/vcd_scheduler.c
new file mode 100644
index 0000000..ab21bac
--- /dev/null
+++ b/drivers/video/msm/vidc/common/vcd/vcd_scheduler.c
@@ -0,0 +1,287 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <media/msm/vidc_type.h>
+#include "vcd.h"
+
+#define NORMALIZATION_FACTOR 3600
+#define ADJUST_CLIENT_ROUNDS(client, round_adjustment) \
+do {\
+	if ((client)->rounds < round_adjustment) {\
+		(client)->rounds = 0;\
+		VCD_MSG_HIGH("%s(): WARNING: Scheduler list unsorted",\
+			__func__);\
+	} else\
+		(client)->rounds -= round_adjustment;\
+} while (0)
+
+u32 vcd_sched_create(struct list_head *sched_list)
+{
+	u32 rc = VCD_S_SUCCESS;
+	if (!sched_list) {
+		VCD_MSG_ERROR("%s(): Invalid parameter", __func__);
+		rc = VCD_ERR_ILLEGAL_PARM;
+	} else
+		INIT_LIST_HEAD(sched_list);
+	return rc;
+}
+
+void vcd_sched_destroy(struct list_head *sched_clnt_list)
+{
+	struct vcd_sched_clnt_ctx *sched_clnt, *sched_clnt_next;
+	if (sched_clnt_list)
+		list_for_each_entry_safe(sched_clnt,
+			sched_clnt_next, sched_clnt_list, list) {
+			list_del_init(&sched_clnt->list);
+			sched_clnt->clnt_active = false;
+		}
+}
+
+void insert_client_in_list(struct list_head *sched_clnt_list,
+	struct vcd_sched_clnt_ctx *sched_new_clnt, bool tail)
+{
+	struct vcd_sched_clnt_ctx *sched_clnt;
+	if (!list_empty(sched_clnt_list)) {
+		if (tail)
+			sched_clnt = list_entry(sched_clnt_list->prev,
+				struct vcd_sched_clnt_ctx, list);
+		else
+			sched_clnt = list_first_entry(sched_clnt_list,
+				struct vcd_sched_clnt_ctx, list);
+		sched_new_clnt->rounds = sched_clnt->rounds;
+	} else
+		sched_new_clnt->rounds = 0;
+	if (tail)
+		list_add_tail(&sched_new_clnt->list, sched_clnt_list);
+	else
+		list_add(&sched_new_clnt->list, sched_clnt_list);
+}
+
+u32 vcd_sched_add_client(struct vcd_clnt_ctxt *cctxt)
+{
+	struct vcd_property_hdr prop_hdr;
+	struct vcd_sched_clnt_ctx *sched_cctxt;
+	u32 rc = VCD_S_SUCCESS;
+	if (!cctxt) {
+		VCD_MSG_ERROR("%s(): Invalid parameter", __func__);
+		rc = VCD_ERR_ILLEGAL_PARM;
+	} else if (cctxt->sched_clnt_hdl)
+		VCD_MSG_HIGH(
+			"%s(): Scheduler client already exists!", __func__);
+	else {
+		sched_cctxt = (struct vcd_sched_clnt_ctx *)
+			kmalloc(sizeof(struct vcd_sched_clnt_ctx),
+					GFP_KERNEL);
+		if (sched_cctxt) {
+
+			prop_hdr.prop_id = DDL_I_FRAME_PROC_UNITS;
+			prop_hdr.sz = sizeof(cctxt->frm_p_units);
+			rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr,
+						  &cctxt->frm_p_units);
+			VCD_FAILED_RETURN(rc,
+				"Failed: Get DDL_I_FRAME_PROC_UNITS");
+			if (cctxt->decoding) {
+				cctxt->frm_rate.fps_numerator =
+					VCD_DEC_INITIAL_FRAME_RATE;
+				cctxt->frm_rate.fps_denominator = 1;
+			} else {
+				prop_hdr.prop_id = VCD_I_FRAME_RATE;
+				prop_hdr.sz = sizeof(cctxt->frm_rate);
+				rc = ddl_get_property(cctxt->ddl_handle,
+						&prop_hdr, &cctxt->frm_rate);
+				VCD_FAILED_RETURN(rc,
+					"Failed: Get VCD_I_FRAME_RATE");
+			}
+			if (!cctxt->perf_set_by_client)
+				cctxt->reqd_perf_lvl = cctxt->frm_p_units *
+					cctxt->frm_rate.fps_numerator /
+					cctxt->frm_rate.fps_denominator;
+
+			cctxt->sched_clnt_hdl = sched_cctxt;
+			memset(sched_cctxt, 0,
+				sizeof(struct vcd_sched_clnt_ctx));
+			sched_cctxt->tkns = 0;
+			sched_cctxt->round_perfrm = NORMALIZATION_FACTOR *
+				cctxt->frm_rate.fps_denominator /
+				cctxt->frm_rate.fps_numerator;
+			sched_cctxt->clnt_active = true;
+			sched_cctxt->clnt_data = cctxt;
+			INIT_LIST_HEAD(&sched_cctxt->ip_frm_list);
+
+			insert_client_in_list(
+				&cctxt->dev_ctxt->sched_clnt_list,
+				sched_cctxt, false);
+		}
+	}
+	return rc;
+}
+
+u32 vcd_sched_remove_client(struct vcd_sched_clnt_ctx *sched_cctxt)
+{
+	u32 rc = VCD_S_SUCCESS;
+	struct vcd_clnt_ctxt *cctxt;
+	if (!sched_cctxt) {
+		VCD_MSG_ERROR("%s(): Invalid handle ptr", __func__);
+		rc = VCD_ERR_ILLEGAL_PARM;
+	} else if (!list_empty(&sched_cctxt->ip_frm_list)) {
+		VCD_MSG_ERROR(
+			"%s(): Cannot remove client, queue no empty", __func__);
+		rc = VCD_ERR_ILLEGAL_OP;
+	} else {
+		cctxt = sched_cctxt->clnt_data;
+		list_del(&sched_cctxt->list);
+		memset(sched_cctxt, 0,
+			sizeof(struct vcd_sched_clnt_ctx));
+		kfree(sched_cctxt);
+	}
+	return rc;
+}
+
+u32 vcd_sched_update_config(struct vcd_clnt_ctxt *cctxt)
+{
+	u32 rc = VCD_S_SUCCESS;
+	if (!cctxt || !cctxt->sched_clnt_hdl) {
+		VCD_MSG_ERROR("%s(): Invalid parameter", __func__);
+		rc = VCD_ERR_ILLEGAL_PARM;
+	} else {
+		cctxt->sched_clnt_hdl->rounds /=
+			cctxt->sched_clnt_hdl->round_perfrm;
+		cctxt->sched_clnt_hdl->round_perfrm =
+			NORMALIZATION_FACTOR *
+			cctxt->frm_rate.fps_denominator /
+			cctxt->frm_rate.fps_numerator;
+		cctxt->sched_clnt_hdl->rounds *=
+			cctxt->sched_clnt_hdl->round_perfrm;
+	}
+	return rc;
+}
+
+u32 vcd_sched_queue_buffer(
+	struct vcd_sched_clnt_ctx *sched_cctxt,
+	struct vcd_buffer_entry *buffer, u32 tail)
+{
+	u32 rc = VCD_S_SUCCESS;
+	if (!sched_cctxt || !buffer) {
+		VCD_MSG_ERROR("%s(): Invalid parameter", __func__);
+		rc = VCD_ERR_ILLEGAL_PARM;
+	} else if (tail)
+		list_add_tail(&buffer->sched_list,
+				&sched_cctxt->ip_frm_list);
+	else
+		list_add(&buffer->sched_list, &sched_cctxt->ip_frm_list);
+	return rc;
+}
+
+u32 vcd_sched_dequeue_buffer(
+	struct vcd_sched_clnt_ctx *sched_cctxt,
+	struct vcd_buffer_entry **buffer)
+{
+	u32 rc = VCD_ERR_QEMPTY;
+	if (!sched_cctxt || !buffer) {
+		VCD_MSG_ERROR("%s(): Invalid parameter", __func__);
+		rc = VCD_ERR_ILLEGAL_PARM;
+	} else {
+		*buffer = NULL;
+		if (!list_empty(&sched_cctxt->ip_frm_list)) {
+			*buffer = list_first_entry(
+					&sched_cctxt->ip_frm_list,
+					struct vcd_buffer_entry,
+					sched_list);
+			list_del(&(*buffer)->sched_list);
+			rc = VCD_S_SUCCESS;
+		}
+	}
+	return rc;
+}
+
+u32 vcd_sched_mark_client_eof(struct vcd_sched_clnt_ctx *sched_cctxt)
+{
+	u32 rc = VCD_S_SUCCESS;
+	struct vcd_buffer_entry *buffer = NULL;
+	if (!sched_cctxt) {
+		VCD_MSG_ERROR("%s(): Invalid parameter", __func__);
+		rc = VCD_ERR_ILLEGAL_PARM;
+	} else if (!list_empty(&sched_cctxt->ip_frm_list)) {
+		buffer = list_entry(sched_cctxt->ip_frm_list.prev,
+			struct vcd_buffer_entry, sched_list);
+		buffer->frame.flags |= VCD_FRAME_FLAG_EOS;
+	} else
+		rc = VCD_ERR_QEMPTY;
+	return rc;
+}
+
+u32 vcd_sched_suspend_resume_clnt(
+	struct vcd_clnt_ctxt *cctxt, u32 state)
+{
+	u32 rc = VCD_S_SUCCESS;
+	struct vcd_sched_clnt_ctx *sched_cctxt;
+	if (!cctxt || !cctxt->sched_clnt_hdl) {
+		VCD_MSG_ERROR("%s(): Invalid parameter", __func__);
+		rc = VCD_ERR_ILLEGAL_PARM;
+	} else {
+		sched_cctxt = cctxt->sched_clnt_hdl;
+		if (state != sched_cctxt->clnt_active) {
+			sched_cctxt->clnt_active = state;
+			if (state)
+				insert_client_in_list(&cctxt->dev_ctxt->\
+					sched_clnt_list, sched_cctxt, false);
+			else
+				list_del_init(&sched_cctxt->list);
+		}
+	}
+	return rc;
+}
+
+u32 vcd_sched_get_client_frame(struct list_head *sched_clnt_list,
+	struct vcd_clnt_ctxt **cctxt,
+	struct vcd_buffer_entry **buffer)
+{
+	u32 rc = VCD_ERR_QEMPTY, round_adjustment = 0;
+	struct vcd_sched_clnt_ctx *sched_clnt, *clnt_nxt;
+	if (!sched_clnt_list || !cctxt || !buffer) {
+		VCD_MSG_ERROR("%s(): Invalid parameter", __func__);
+		rc = VCD_ERR_ILLEGAL_PARM;
+	} else if (!list_empty(sched_clnt_list)) {
+		*cctxt = NULL;
+		*buffer = NULL;
+		list_for_each_entry_safe(sched_clnt,
+			clnt_nxt, sched_clnt_list, list) {
+			if (&sched_clnt->list == sched_clnt_list->next)
+				round_adjustment = sched_clnt->rounds;
+			if (*cctxt) {
+				if ((*cctxt)->sched_clnt_hdl->rounds >=
+					sched_clnt->rounds)
+					list_move(&(*cctxt)->sched_clnt_hdl\
+						->list, &sched_clnt->list);
+				ADJUST_CLIENT_ROUNDS(sched_clnt,
+					round_adjustment);
+			} else if (sched_clnt->tkns &&
+				!list_empty(&sched_clnt->ip_frm_list)) {
+				*cctxt = sched_clnt->clnt_data;
+				sched_clnt->rounds += sched_clnt->round_perfrm;
+			} else
+				ADJUST_CLIENT_ROUNDS(sched_clnt,
+						round_adjustment);
+		}
+		if (*cctxt) {
+			rc = vcd_sched_dequeue_buffer(
+				(*cctxt)->sched_clnt_hdl, buffer);
+			if (rc == VCD_S_SUCCESS) {
+				(*cctxt)->sched_clnt_hdl->tkns--;
+				ADJUST_CLIENT_ROUNDS((*cctxt)->\
+					sched_clnt_hdl, round_adjustment);
+			}
+		}
+	}
+	return rc;
+}
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_sub.c b/drivers/video/msm/vidc/common/vcd/vcd_sub.c
new file mode 100644
index 0000000..6ca4dbe
--- /dev/null
+++ b/drivers/video/msm/vidc/common/vcd/vcd_sub.c
@@ -0,0 +1,3433 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/memory_alloc.h>
+#include <mach/msm_subsystem_map.h>
+#include <asm/div64.h>
+#include <media/msm/vidc_type.h>
+#include "vcd.h"
+#include "vdec_internal.h"
+
+#define MAX(x, y) (((x) > (y)) ? (x) : (y))
+#define MAP_TABLE_SZ 64
+#define VCD_ENC_MAX_OUTBFRS_PER_FRAME 8
+#define MAX_DEC_TIME 33
+
+struct vcd_msm_map_buffer {
+	phys_addr_t phy_addr;
+	struct msm_mapped_buffer *mapped_buffer;
+	struct ion_handle *alloc_handle;
+	u32 in_use;
+};
+static struct vcd_msm_map_buffer msm_mapped_buffer_table[MAP_TABLE_SZ];
+static unsigned int vidc_mmu_subsystem[] = {MSM_SUBSYSTEM_VIDEO};
+
+static int vcd_pmem_alloc(size_t sz, u8 **kernel_vaddr, u8 **phy_addr,
+			 struct vcd_clnt_ctxt *cctxt)
+{
+	u32 memtype, i = 0, flags = 0;
+	struct vcd_msm_map_buffer *map_buffer = NULL;
+	struct msm_mapped_buffer *mapped_buffer = NULL;
+	unsigned long iova = 0;
+	unsigned long buffer_size = 0;
+	int ret = 0;
+	unsigned long ionflag = 0;
+
+	if (!kernel_vaddr || !phy_addr || !cctxt) {
+		pr_err("\n%s: Invalid parameters", __func__);
+		goto bailout;
+	}
+	*phy_addr = NULL;
+	*kernel_vaddr = NULL;
+	for (i = 0; i  < MAP_TABLE_SZ; i++) {
+		if (!msm_mapped_buffer_table[i].in_use) {
+			map_buffer = &msm_mapped_buffer_table[i];
+			map_buffer->in_use = 1;
+			break;
+		}
+	}
+	if (!map_buffer) {
+		pr_err("%s() map table is full", __func__);
+		goto bailout;
+	}
+	res_trk_set_mem_type(DDL_MM_MEM);
+	memtype = res_trk_get_mem_type();
+	if (!cctxt->vcd_enable_ion) {
+		map_buffer->phy_addr = (phys_addr_t)
+		allocate_contiguous_memory_nomap(sz, memtype, SZ_4K);
+		if (!map_buffer->phy_addr) {
+			pr_err("%s() acm alloc failed", __func__);
+			goto free_map_table;
+		}
+		flags = MSM_SUBSYSTEM_MAP_IOVA | MSM_SUBSYSTEM_MAP_KADDR;
+		map_buffer->mapped_buffer =
+		msm_subsystem_map_buffer((unsigned long)map_buffer->phy_addr,
+		sz, flags, vidc_mmu_subsystem,
+		sizeof(vidc_mmu_subsystem)/sizeof(unsigned int));
+		if (IS_ERR(map_buffer->mapped_buffer)) {
+			pr_err(" %s() buffer map failed", __func__);
+			goto free_acm_alloc;
+		}
+		mapped_buffer = map_buffer->mapped_buffer;
+		if (!mapped_buffer->vaddr || !mapped_buffer->iova[0]) {
+			pr_err("%s() map buffers failed", __func__);
+			goto free_map_buffers;
+		}
+		*phy_addr = (u8 *) mapped_buffer->iova[0];
+		*kernel_vaddr = (u8 *) mapped_buffer->vaddr;
+	} else {
+		map_buffer->alloc_handle = ion_alloc(
+			    cctxt->vcd_ion_client, sz, SZ_4K,
+			    memtype);
+		if (!map_buffer->alloc_handle) {
+			pr_err("%s() ION alloc failed", __func__);
+			goto bailout;
+		}
+		if (ion_handle_get_flags(cctxt->vcd_ion_client,
+				map_buffer->alloc_handle,
+				&ionflag)) {
+			pr_err("%s() ION get flag failed", __func__);
+			goto bailout;
+		}
+		*kernel_vaddr = (u8 *) ion_map_kernel(
+				cctxt->vcd_ion_client,
+				map_buffer->alloc_handle,
+				ionflag);
+		if (!(*kernel_vaddr)) {
+			pr_err("%s() ION map failed", __func__);
+			goto ion_free_bailout;
+		}
+		ret = ion_map_iommu(cctxt->vcd_ion_client,
+				map_buffer->alloc_handle,
+				VIDEO_DOMAIN,
+				VIDEO_MAIN_POOL,
+				SZ_4K,
+				0,
+				(unsigned long *)&iova,
+				(unsigned long *)&buffer_size,
+				UNCACHED, 0);
+		if (ret) {
+			pr_err("%s() ION iommu map failed", __func__);
+			goto ion_map_bailout;
+		}
+		map_buffer->phy_addr = iova;
+		if (!map_buffer->phy_addr) {
+			pr_err("%s() acm alloc failed", __func__);
+			goto free_map_table;
+		}
+		*phy_addr = (u8 *)iova;
+		mapped_buffer = NULL;
+		map_buffer->mapped_buffer = NULL;
+	}
+
+	return 0;
+
+free_map_buffers:
+	if (map_buffer->mapped_buffer)
+		msm_subsystem_unmap_buffer(map_buffer->mapped_buffer);
+free_acm_alloc:
+	if (!cctxt->vcd_enable_ion) {
+		free_contiguous_memory_by_paddr(
+		(unsigned long)map_buffer->phy_addr);
+	}
+	return -ENOMEM;
+ion_map_bailout:
+	ion_unmap_kernel(cctxt->vcd_ion_client, map_buffer->alloc_handle);
+ion_free_bailout:
+	ion_free(cctxt->vcd_ion_client, map_buffer->alloc_handle);
+free_map_table:
+	map_buffer->in_use = 0;
+bailout:
+	return -ENOMEM;
+}
+
+static int vcd_pmem_free(u8 *kernel_vaddr, u8 *phy_addr,
+			 struct vcd_clnt_ctxt *cctxt)
+{
+	u32 i = 0;
+	struct vcd_msm_map_buffer *map_buffer = NULL;
+
+	if (!kernel_vaddr || !phy_addr || !cctxt) {
+		pr_err("\n%s: Invalid parameters", __func__);
+		goto bailout;
+	}
+	for (i = 0; i  < MAP_TABLE_SZ; i++) {
+		if (msm_mapped_buffer_table[i].in_use &&
+			(msm_mapped_buffer_table[i]
+			.mapped_buffer->vaddr == kernel_vaddr)) {
+			map_buffer = &msm_mapped_buffer_table[i];
+			map_buffer->in_use = 0;
+			break;
+		}
+	}
+	if (!map_buffer) {
+		pr_err("%s() Entry not found", __func__);
+		goto bailout;
+	}
+	if (map_buffer->mapped_buffer)
+		msm_subsystem_unmap_buffer(map_buffer->mapped_buffer);
+	if (cctxt->vcd_enable_ion) {
+		if (map_buffer->alloc_handle) {
+			ion_unmap_kernel(cctxt->vcd_ion_client,
+					map_buffer->alloc_handle);
+			ion_unmap_iommu(cctxt->vcd_ion_client,
+					map_buffer->alloc_handle,
+					VIDEO_DOMAIN,
+					VIDEO_MAIN_POOL);
+			ion_free(cctxt->vcd_ion_client,
+			map_buffer->alloc_handle);
+		}
+	} else {
+		free_contiguous_memory_by_paddr(
+			(unsigned long)map_buffer->phy_addr);
+	}
+bailout:
+	kernel_vaddr = NULL;
+	phy_addr = NULL;
+	return 0;
+}
+
+
+u8 *vcd_pmem_get_physical(struct video_client_ctx *client_ctx,
+			  unsigned long kernel_vaddr)
+{
+	unsigned long phy_addr, user_vaddr;
+	int pmem_fd;
+	struct file *file;
+	s32 buffer_index = -1;
+
+	if (vidc_lookup_addr_table(client_ctx, BUFFER_TYPE_INPUT,
+					  false, &user_vaddr, &kernel_vaddr,
+					  &phy_addr, &pmem_fd, &file,
+					  &buffer_index)) {
+
+		return (u8 *) phy_addr;
+	} else if (vidc_lookup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT,
+		false, &user_vaddr, &kernel_vaddr, &phy_addr, &pmem_fd, &file,
+		&buffer_index)) {
+		return (u8 *) phy_addr;
+	} else {
+		VCD_MSG_ERROR("Couldn't get physical address");
+
+		return NULL;
+	}
+
+}
+
+u32 vcd_get_ion_flag(struct video_client_ctx *client_ctx,
+			  unsigned long kernel_vaddr,
+			struct ion_handle **buff_ion_handle)
+{
+	unsigned long phy_addr, user_vaddr;
+	int pmem_fd;
+	struct file *file;
+	s32 buffer_index = -1;
+	u32 ion_flag = 0;
+	struct ion_handle *buff_handle = NULL;
+
+	if (vidc_lookup_addr_table(client_ctx, BUFFER_TYPE_INPUT,
+					  false, &user_vaddr, &kernel_vaddr,
+					  &phy_addr, &pmem_fd, &file,
+					  &buffer_index)) {
+
+		ion_flag = vidc_get_fd_info(client_ctx, BUFFER_TYPE_INPUT,
+				pmem_fd, kernel_vaddr, buffer_index,
+				&buff_handle);
+		*buff_ion_handle = buff_handle;
+		return ion_flag;
+	} else if (vidc_lookup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT,
+		false, &user_vaddr, &kernel_vaddr, &phy_addr, &pmem_fd, &file,
+		&buffer_index)) {
+		ion_flag = vidc_get_fd_info(client_ctx, BUFFER_TYPE_OUTPUT,
+				pmem_fd, kernel_vaddr, buffer_index,
+				&buff_handle);
+		*buff_ion_handle = buff_handle;
+		return ion_flag;
+	} else {
+		VCD_MSG_ERROR("Couldn't get ion flag");
+		return 0;
+	}
+
+}
+
+void vcd_reset_device_channels(struct vcd_dev_ctxt *dev_ctxt)
+{
+	dev_ctxt->ddl_frame_ch_free = dev_ctxt->ddl_frame_ch_depth;
+	dev_ctxt->ddl_cmd_ch_free   = dev_ctxt->ddl_cmd_ch_depth;
+	dev_ctxt->ddl_frame_ch_interim = 0;
+	dev_ctxt->ddl_cmd_ch_interim = 0;
+}
+
+u32 vcd_get_command_channel(
+	struct vcd_dev_ctxt *dev_ctxt,
+	 struct vcd_transc **transc)
+{
+	u32 result = false;
+
+	*transc = NULL;
+
+	if (dev_ctxt->ddl_cmd_ch_free > 0) {
+		if (dev_ctxt->ddl_cmd_concurrency) {
+			--dev_ctxt->ddl_cmd_ch_free;
+			result = true;
+		} else if ((dev_ctxt->ddl_frame_ch_free +
+			 dev_ctxt->ddl_frame_ch_interim)
+			== dev_ctxt->ddl_frame_ch_depth) {
+				--dev_ctxt->ddl_cmd_ch_free;
+				result = true;
+		}
+	}
+
+	if (result) {
+		*transc = vcd_get_free_trans_tbl_entry(dev_ctxt);
+
+		if (!*transc) {
+			result = false;
+
+			vcd_release_command_channel(dev_ctxt, *transc);
+		}
+
+	}
+	return result;
+}
+
+u32 vcd_get_command_channel_in_loop(
+	struct vcd_dev_ctxt *dev_ctxt,
+	 struct vcd_transc **transc)
+{
+	u32 result = false;
+
+	*transc = NULL;
+
+	if (dev_ctxt->ddl_cmd_ch_interim > 0) {
+		if (dev_ctxt->ddl_cmd_concurrency) {
+			--dev_ctxt->ddl_cmd_ch_interim;
+			result = true;
+		} else if ((dev_ctxt->ddl_frame_ch_free +
+				dev_ctxt->ddl_frame_ch_interim)
+				== dev_ctxt->ddl_frame_ch_depth) {
+				--dev_ctxt->ddl_cmd_ch_interim;
+				result = true;
+		}
+	} else {
+		result = vcd_get_command_channel(dev_ctxt, transc);
+	}
+
+	if (result && !*transc) {
+		*transc = vcd_get_free_trans_tbl_entry(dev_ctxt);
+
+		if (!*transc) {
+			result = false;
+
+			++dev_ctxt->ddl_cmd_ch_interim;
+		}
+
+	}
+
+	return result;
+}
+
+void vcd_mark_command_channel(struct vcd_dev_ctxt *dev_ctxt,
+	struct vcd_transc *transc)
+{
+	++dev_ctxt->ddl_cmd_ch_interim;
+
+	vcd_release_trans_tbl_entry(transc);
+	if (dev_ctxt->ddl_cmd_ch_interim +
+		dev_ctxt->ddl_cmd_ch_free >
+		dev_ctxt->ddl_cmd_ch_depth) {
+		VCD_MSG_ERROR("\n Command channel access counters messed up");
+	}
+}
+
+void vcd_release_command_channel(
+	struct vcd_dev_ctxt *dev_ctxt, struct vcd_transc *transc)
+{
+	++dev_ctxt->ddl_cmd_ch_free;
+
+	vcd_release_trans_tbl_entry(transc);
+	if (dev_ctxt->ddl_cmd_ch_interim + dev_ctxt->ddl_cmd_ch_free >
+		dev_ctxt->ddl_cmd_ch_depth) {
+		VCD_MSG_ERROR("\n Command channel access counters messed up");
+	}
+}
+
+void vcd_release_multiple_command_channels(struct vcd_dev_ctxt
+	*dev_ctxt, u32 channels)
+{
+	dev_ctxt->ddl_cmd_ch_free += channels;
+
+	if (dev_ctxt->ddl_cmd_ch_interim +
+		dev_ctxt->ddl_cmd_ch_free >
+		dev_ctxt->ddl_cmd_ch_depth) {
+		VCD_MSG_ERROR("\n Command channel access counters messed up");
+	}
+}
+
+void vcd_release_interim_command_channels(struct vcd_dev_ctxt *dev_ctxt)
+{
+	dev_ctxt->ddl_cmd_ch_free += dev_ctxt->ddl_cmd_ch_interim;
+	dev_ctxt->ddl_cmd_ch_interim = 0;
+
+	if (dev_ctxt->ddl_cmd_ch_interim + dev_ctxt->ddl_cmd_ch_free >
+		dev_ctxt->ddl_cmd_ch_depth) {
+		VCD_MSG_ERROR("\n Command channel access counters messed up");
+	}
+}
+
+u32 vcd_get_frame_channel(struct vcd_dev_ctxt *dev_ctxt,
+	struct vcd_transc **transc)
+{
+	u32 result = false;
+
+	if (dev_ctxt->ddl_frame_ch_free > 0) {
+		if (dev_ctxt->ddl_cmd_concurrency) {
+			--dev_ctxt->ddl_frame_ch_free;
+			result = true;
+		} else if ((dev_ctxt->ddl_cmd_ch_free +
+			 dev_ctxt->ddl_cmd_ch_interim)
+			== dev_ctxt->ddl_cmd_ch_depth) {
+			--dev_ctxt->ddl_frame_ch_free;
+			result = true;
+		}
+	}
+
+	if (result) {
+		*transc = vcd_get_free_trans_tbl_entry(dev_ctxt);
+
+		if (!*transc) {
+			result = false;
+
+			vcd_release_frame_channel(dev_ctxt, *transc);
+		} else {
+			(*transc)->type = VCD_CMD_CODE_FRAME;
+		}
+
+	}
+
+	return result;
+}
+
+u32 vcd_get_frame_channel_in_loop(
+	struct vcd_dev_ctxt *dev_ctxt,
+	 struct vcd_transc **transc)
+{
+	u32 result = false;
+
+	*transc = NULL;
+
+	if (dev_ctxt->ddl_frame_ch_interim > 0) {
+		if (dev_ctxt->ddl_cmd_concurrency) {
+			--dev_ctxt->ddl_frame_ch_interim;
+			result = true;
+		} else if ((dev_ctxt->ddl_cmd_ch_free +
+			 dev_ctxt->ddl_cmd_ch_interim)
+			== dev_ctxt->ddl_cmd_ch_depth) {
+			--dev_ctxt->ddl_frame_ch_interim;
+			result = true;
+		}
+	} else {
+		result = vcd_get_frame_channel(dev_ctxt, transc);
+	}
+
+	if (result && !*transc) {
+		*transc = vcd_get_free_trans_tbl_entry(dev_ctxt);
+
+		if (!*transc) {
+			result = false;
+			VCD_MSG_FATAL("\n%s: All transactions are busy;"
+				"Couldnt find free one\n", __func__);
+			++dev_ctxt->ddl_frame_ch_interim;
+		} else
+			(*transc)->type = VCD_CMD_CODE_FRAME;
+	}
+
+	return result;
+}
+
+void vcd_mark_frame_channel(struct vcd_dev_ctxt *dev_ctxt)
+{
+	++dev_ctxt->ddl_frame_ch_interim;
+
+	if (dev_ctxt->ddl_frame_ch_interim +
+		dev_ctxt->ddl_frame_ch_free >
+		dev_ctxt->ddl_cmd_ch_depth) {
+		VCD_MSG_FATAL("Frame channel access counters messed up");
+	}
+}
+
+void vcd_release_frame_channel(struct vcd_dev_ctxt *dev_ctxt,
+	struct vcd_transc *transc)
+{
+	++dev_ctxt->ddl_frame_ch_free;
+
+	vcd_release_trans_tbl_entry(transc);
+
+	if (dev_ctxt->ddl_frame_ch_interim +
+		dev_ctxt->ddl_frame_ch_free >
+		dev_ctxt->ddl_cmd_ch_depth) {
+		VCD_MSG_FATAL("Frame channel access counters messed up");
+	}
+}
+
+void vcd_release_multiple_frame_channels(struct vcd_dev_ctxt
+	*dev_ctxt, u32 channels)
+{
+	dev_ctxt->ddl_frame_ch_free += channels;
+
+	if (dev_ctxt->ddl_frame_ch_interim +
+		dev_ctxt->ddl_frame_ch_free >
+		dev_ctxt->ddl_frame_ch_depth) {
+		VCD_MSG_FATAL("Frame channel access counters messed up");
+	}
+}
+
+void vcd_release_interim_frame_channels(struct vcd_dev_ctxt
+	*dev_ctxt)
+{
+	dev_ctxt->ddl_frame_ch_free +=
+		dev_ctxt->ddl_frame_ch_interim;
+	dev_ctxt->ddl_frame_ch_interim = 0;
+
+	if (dev_ctxt->ddl_frame_ch_free >
+		dev_ctxt->ddl_cmd_ch_depth) {
+		VCD_MSG_FATAL("Frame channel access counters messed up");
+	}
+}
+
+u32 vcd_core_is_busy(struct vcd_dev_ctxt *dev_ctxt)
+{
+	if (((dev_ctxt->ddl_cmd_ch_free +
+		  dev_ctxt->ddl_cmd_ch_interim) !=
+		 dev_ctxt->ddl_cmd_ch_depth)
+		||
+		((dev_ctxt->ddl_frame_ch_free +
+		  dev_ctxt->ddl_frame_ch_interim) !=
+		 dev_ctxt->ddl_frame_ch_depth)
+	  ) {
+		return true;
+	} else {
+		return false;
+	}
+}
+
+void vcd_device_timer_start(struct vcd_dev_ctxt *dev_ctxt)
+{
+	if (dev_ctxt->config.timer_start)
+		dev_ctxt->config.timer_start(dev_ctxt->hw_timer_handle,
+			dev_ctxt->hw_time_out);
+}
+
+void vcd_device_timer_stop(struct vcd_dev_ctxt *dev_ctxt)
+{
+	if (dev_ctxt->config.timer_stop)
+		dev_ctxt->config.timer_stop(dev_ctxt->hw_timer_handle);
+}
+
+
+u32 vcd_common_allocate_set_buffer(
+	struct vcd_clnt_ctxt *cctxt,
+	 enum vcd_buffer_type buffer,
+	 u32 buf_size, struct vcd_buffer_pool **buffer_pool)
+{
+	u32 rc = VCD_S_SUCCESS;
+	struct vcd_buffer_requirement Buf_req;
+	struct vcd_property_hdr Prop_hdr;
+	struct vcd_buffer_pool *buf_pool;
+
+	if (buffer == VCD_BUFFER_INPUT) {
+		Prop_hdr.prop_id = DDL_I_INPUT_BUF_REQ;
+		buf_pool = &cctxt->in_buf_pool;
+	} else if (buffer == VCD_BUFFER_OUTPUT) {
+		Prop_hdr.prop_id = DDL_I_OUTPUT_BUF_REQ;
+		buf_pool = &cctxt->out_buf_pool;
+	} else {
+		rc = VCD_ERR_ILLEGAL_PARM;
+	}
+	VCD_FAILED_RETURN(rc, "Invalid buffer type provided");
+
+	*buffer_pool = buf_pool;
+
+	if (buf_pool->count > 0 &&
+		buf_pool->validated == buf_pool->count) {
+		VCD_MSG_ERROR("Buffer pool is full");
+		return VCD_ERR_FAIL;
+	}
+
+	if (!buf_pool->entries) {
+		Prop_hdr.sz = sizeof(Buf_req);
+		rc = ddl_get_property(cctxt->ddl_handle, &Prop_hdr, &Buf_req);
+		if (!VCD_FAILED(rc)) {
+			rc = vcd_alloc_buffer_pool_entries(buf_pool,
+							   &Buf_req);
+		} else {
+			VCD_MSG_ERROR("rc = 0x%x. Failed: ddl_get_property",
+					  rc);
+		}
+	}
+
+	if (!VCD_FAILED(rc)) {
+		if (buf_pool->buf_req.sz > buf_size) {
+			VCD_MSG_ERROR("\n required buffer sz %u "
+				"allocated sz %u", buf_pool->buf_req.
+				sz, buf_size);
+
+			rc = VCD_ERR_ILLEGAL_PARM;
+		}
+	}
+
+	return rc;
+}
+
+u32 vcd_set_buffer_internal(
+	struct vcd_clnt_ctxt *cctxt,
+	 struct vcd_buffer_pool *buf_pool, u8 *buffer, u32 buf_size)
+{
+	struct vcd_buffer_entry *buf_entry;
+	u8 *physical;
+	u32 ion_flag = 0;
+	struct ion_handle *buff_handle = NULL;
+
+	buf_entry = vcd_find_buffer_pool_entry(buf_pool, buffer);
+	if (buf_entry) {
+		VCD_MSG_ERROR("This buffer address already exists");
+
+		return VCD_ERR_ILLEGAL_OP;
+	}
+
+	physical = (u8 *) vcd_pmem_get_physical(
+		cctxt->client_data, (unsigned long)buffer);
+
+	ion_flag = vcd_get_ion_flag(cctxt->client_data,
+				(unsigned long)buffer,
+				&buff_handle);
+	if (!physical) {
+		VCD_MSG_ERROR("Couldn't get physical address");
+		return VCD_ERR_BAD_POINTER;
+	}
+	if (((u32) physical % buf_pool->buf_req.align)) {
+		VCD_MSG_ERROR("Physical addr is not aligned");
+		return VCD_ERR_BAD_POINTER;
+	}
+
+	buf_entry = vcd_get_free_buffer_pool_entry(buf_pool);
+	if (!buf_entry) {
+		VCD_MSG_ERROR("Can't allocate buffer pool is full");
+		return VCD_ERR_FAIL;
+	}
+	buf_entry->virtual = buffer;
+	buf_entry->physical = physical;
+	buf_entry->sz = buf_size;
+	buf_entry->frame.alloc_len = buf_size;
+	buf_entry->allocated = false;
+
+	buf_entry->frame.virtual = buf_entry->virtual;
+	buf_entry->frame.physical = buf_entry->physical;
+	buf_entry->frame.ion_flag = ion_flag;
+	buf_entry->frame.buff_ion_handle = buff_handle;
+
+	buf_pool->validated++;
+
+	return VCD_S_SUCCESS;
+
+}
+
+u32 vcd_allocate_buffer_internal(
+	struct vcd_clnt_ctxt *cctxt,
+	 struct vcd_buffer_pool *buf_pool,
+	 u32 buf_size, u8 **vir_buf_addr, u8 **phy_buf_addr)
+{
+	struct vcd_buffer_entry *buf_entry;
+	struct vcd_buffer_requirement *buf_req;
+	u32 addr;
+	int rc = 0;
+
+	buf_entry = vcd_get_free_buffer_pool_entry(buf_pool);
+	if (!buf_entry) {
+		VCD_MSG_ERROR("Can't allocate buffer pool is full");
+
+		return VCD_ERR_FAIL;
+	}
+
+	buf_req = &buf_pool->buf_req;
+
+	buf_size += buf_req->align;
+
+	rc = vcd_pmem_alloc(buf_size, &buf_entry->alloc,
+				&buf_entry->physical, cctxt);
+
+	if (rc < 0) {
+		VCD_MSG_ERROR("Buffer allocation failed");
+
+		return VCD_ERR_ALLOC_FAIL;
+	}
+
+	buf_entry->sz = buf_size;
+	buf_entry->frame.alloc_len = buf_size;
+
+	if (!buf_entry->physical) {
+		VCD_MSG_ERROR("Couldn't get physical address");
+
+		return VCD_ERR_BAD_POINTER;
+	}
+
+	buf_entry->allocated = true;
+
+	if (buf_req->align > 0) {
+
+		addr = (u32) buf_entry->physical;
+		addr += buf_req->align;
+		addr -= (addr % buf_req->align);
+		buf_entry->virtual = buf_entry->alloc;
+		buf_entry->virtual += (u32) (addr - (u32)
+			buf_entry->physical);
+		buf_entry->physical = (u8 *) addr;
+	} else {
+		VCD_MSG_LOW("No buffer alignment required");
+
+		buf_entry->virtual = buf_entry->alloc;
+
+	}
+
+	buf_entry->frame.virtual = buf_entry->virtual;
+	buf_entry->frame.physical = buf_entry->physical;
+
+	*vir_buf_addr = buf_entry->virtual;
+	*phy_buf_addr = buf_entry->physical;
+
+	buf_pool->allocated++;
+	buf_pool->validated++;
+
+	return VCD_S_SUCCESS;
+}
+
+u32 vcd_free_one_buffer_internal(
+	struct vcd_clnt_ctxt *cctxt,
+	 enum vcd_buffer_type buffer_type, u8 *buffer)
+{
+	struct vcd_buffer_pool *buf_pool;
+	u32 rc = VCD_S_SUCCESS;
+	struct vcd_buffer_entry *buf_entry;
+	u32 first_frm_recvd = 0;
+
+	if (buffer_type == VCD_BUFFER_INPUT) {
+		buf_pool = &cctxt->in_buf_pool;
+		first_frm_recvd = VCD_FIRST_IP_RCVD;
+	} else if (buffer_type == VCD_BUFFER_OUTPUT) {
+		buf_pool = &cctxt->out_buf_pool;
+		first_frm_recvd = VCD_FIRST_OP_RCVD;
+	} else
+		rc = VCD_ERR_ILLEGAL_PARM;
+
+	VCD_FAILED_RETURN(rc, "Invalid buffer type provided");
+
+	first_frm_recvd &= cctxt->status.mask;
+	if (first_frm_recvd && !cctxt->meta_mode) {
+		VCD_MSG_ERROR(
+			"VCD free buffer called when data path is active");
+		return VCD_ERR_BAD_STATE;
+	}
+
+	buf_entry = vcd_find_buffer_pool_entry(buf_pool, buffer);
+	if (!buf_entry) {
+		VCD_MSG_ERROR("Buffer addr %p not found. Can't free buffer",
+				  buffer);
+
+		return VCD_ERR_ILLEGAL_PARM;
+	}
+	if (buf_entry->in_use) {
+		VCD_MSG_ERROR("\n Buffer is in use and is not flushed");
+		return VCD_ERR_ILLEGAL_OP;
+	}
+
+	VCD_MSG_LOW("Freeing buffer %p. Allocated %d",
+			buf_entry->virtual, buf_entry->allocated);
+
+	if (buf_entry->allocated) {
+		vcd_pmem_free(buf_entry->alloc, buf_entry->physical, cctxt);
+		buf_pool->allocated--;
+	}
+
+	memset(buf_entry, 0, sizeof(struct vcd_buffer_entry));
+	buf_pool->validated--;
+	if (buf_pool->validated == 0)
+		vcd_free_buffer_pool_entries(buf_pool);
+
+	return VCD_S_SUCCESS;
+}
+
+u32 vcd_free_buffers_internal(
+	struct vcd_clnt_ctxt *cctxt,
+	 struct vcd_buffer_pool *buf_pool)
+{
+	u32 rc = VCD_S_SUCCESS;
+	u32 i;
+
+	VCD_MSG_LOW("vcd_free_buffers_internal:");
+
+	if (buf_pool->entries) {
+		for (i = 1; i <= buf_pool->count; i++) {
+			if (buf_pool->entries[i].valid &&
+				buf_pool->entries[i].allocated) {
+				vcd_pmem_free(buf_pool->entries[i].alloc,
+						  buf_pool->entries[i].
+						  physical, cctxt);
+			}
+		}
+
+	}
+
+	vcd_reset_buffer_pool_for_reuse(buf_pool);
+
+	return rc;
+}
+
+u32 vcd_alloc_buffer_pool_entries(
+	struct vcd_buffer_pool *buf_pool,
+	 struct vcd_buffer_requirement *buf_req)
+{
+
+	VCD_MSG_LOW("vcd_alloc_buffer_pool_entries:");
+
+	buf_pool->buf_req = *buf_req;
+
+	buf_pool->count = buf_req->actual_count;
+	buf_pool->entries = (struct vcd_buffer_entry *)
+		kzalloc((sizeof(struct vcd_buffer_entry) *
+			   (VCD_MAX_BUFFER_ENTRIES + 1)), GFP_KERNEL);
+
+	if (!buf_pool->entries) {
+		VCD_MSG_ERROR("Buf_pool entries alloc failed");
+		return VCD_ERR_ALLOC_FAIL;
+	}
+
+	INIT_LIST_HEAD(&buf_pool->queue);
+	buf_pool->entries[0].valid = true;
+	buf_pool->q_len = 0;
+
+	buf_pool->validated = 0;
+	buf_pool->allocated = 0;
+	buf_pool->in_use = 0;
+
+	return VCD_S_SUCCESS;
+}
+
+void vcd_free_buffer_pool_entries(struct vcd_buffer_pool *buf_pool)
+{
+	VCD_MSG_LOW("vcd_free_buffer_pool_entries:");
+	kfree(buf_pool->entries);
+	memset(buf_pool, 0, sizeof(struct vcd_buffer_pool));
+	INIT_LIST_HEAD(&buf_pool->queue);
+}
+
+void vcd_flush_in_use_buffer_pool_entries(struct vcd_clnt_ctxt *cctxt,
+	struct vcd_buffer_pool *buf_pool, u32 event)
+{
+	u32 i;
+	VCD_MSG_LOW("vcd_flush_buffer_pool_entries: event=0x%x", event);
+
+	if (buf_pool->entries) {
+		for (i = 0; i <= buf_pool->count; i++) {
+			if (buf_pool->entries[i].virtual &&
+				buf_pool->entries[i].in_use) {
+				cctxt->callback(event, VCD_S_SUCCESS,
+					&buf_pool->entries[i].frame,
+					sizeof(struct vcd_frame_data),
+					cctxt, cctxt->client_data);
+				buf_pool->entries[i].in_use = false;
+				VCD_BUFFERPOOL_INUSE_DECREMENT(
+					buf_pool->in_use);
+		 }
+		}
+	}
+}
+
+
+void vcd_reset_buffer_pool_for_reuse(struct vcd_buffer_pool *buf_pool)
+{
+	VCD_MSG_LOW("vcd_reset_buffer_pool_for_reuse:");
+
+	if (buf_pool->entries) {
+		memset(&buf_pool->entries[1], 0,
+			sizeof(struct vcd_buffer_entry) *
+			VCD_MAX_BUFFER_ENTRIES);
+	}
+	buf_pool->q_len = 0;
+
+	buf_pool->validated = 0;
+	buf_pool->allocated = 0;
+	buf_pool->in_use = 0;
+	INIT_LIST_HEAD(&buf_pool->queue);
+}
+
+struct vcd_buffer_entry *vcd_get_free_buffer_pool_entry
+	(struct vcd_buffer_pool *pool) {
+	u32 i;
+
+	i = 1;
+	while (i <= pool->count && pool->entries[i].valid)
+		i++;
+
+
+	if (i <= pool->count) {
+		pool->entries[i].valid = true;
+
+		return &pool->entries[i];
+	} else {
+		return NULL;
+	}
+}
+
+struct vcd_buffer_entry *vcd_find_buffer_pool_entry
+	(struct vcd_buffer_pool *pool, u8 *addr)
+{
+	u32 i;
+	u32 found = false;
+
+	for (i = 0; i <= pool->count && !found; i++) {
+		if (pool->entries[i].virtual == addr)
+			found = true;
+
+	}
+
+	if (found)
+		return &pool->entries[i - 1];
+	else
+		return NULL;
+
+}
+
+u32 vcd_buffer_pool_entry_en_q(
+	struct vcd_buffer_pool *pool,
+	 struct vcd_buffer_entry *entry)
+{
+	struct vcd_buffer_entry *list_itr;
+
+	if (pool->q_len == pool->count)
+		return false;
+
+	list_for_each_entry(list_itr, &pool->queue, list)
+	if (list_itr == entry) {
+		VCD_MSG_HIGH("\n this output buffer is already present"
+			" in queue");
+		VCD_MSG_HIGH("\n Vir Addr %p Phys Addr %p",
+			entry->virtual, entry->physical);
+		return false;
+	}
+
+	list_add_tail(&entry->list, &pool->queue);
+	pool->q_len++;
+
+	return true;
+}
+
+struct vcd_buffer_entry *vcd_buffer_pool_entry_de_q
+	(struct vcd_buffer_pool *pool) {
+	struct vcd_buffer_entry *entry;
+
+	if (!pool || !pool->q_len)
+		return NULL;
+
+	entry = list_first_entry(&pool->queue,
+		struct vcd_buffer_entry, list);
+
+	if (entry) {
+		list_del(&entry->list);
+		pool->q_len--;
+	}
+	return entry;
+}
+
+void vcd_flush_bframe_buffers(struct vcd_clnt_ctxt *cctxt, u32 mode)
+{
+	int i;
+	struct vcd_buffer_pool *buf_pool;
+
+	if (!cctxt->decoding && cctxt->bframe) {
+		buf_pool = (mode == VCD_FLUSH_INPUT) ?
+			&cctxt->in_buf_pool : &cctxt->out_buf_pool;
+		if (buf_pool->entries != NULL) {
+			for (i = 1; i <= buf_pool->count; i++) {
+				if ((buf_pool->entries[i].in_use) &&
+					(buf_pool->entries[i].frame.virtual
+					 != NULL)) {
+					if (mode == VCD_FLUSH_INPUT) {
+						cctxt->callback(
+						VCD_EVT_RESP_INPUT_FLUSHED,
+						VCD_S_SUCCESS,
+						&(buf_pool->entries[i].frame),
+						sizeof(struct vcd_frame_data),
+						cctxt, cctxt->client_data);
+					} else {
+						buf_pool->entries[i].
+							frame.data_len = 0;
+						cctxt->callback(
+						VCD_EVT_RESP_OUTPUT_FLUSHED,
+						VCD_S_SUCCESS,
+						&(buf_pool->entries[i].frame),
+						sizeof(struct vcd_frame_data),
+						cctxt,
+						cctxt->client_data);
+					}
+				VCD_BUFFERPOOL_INUSE_DECREMENT(
+					buf_pool->in_use);
+				buf_pool->entries[i].in_use = false;
+				}
+			}
+		}
+	}
+}
+
+void vcd_flush_output_buffers(struct vcd_clnt_ctxt *cctxt)
+{
+	struct vcd_buffer_pool *buf_pool;
+	struct vcd_buffer_entry *buf_entry;
+	u32 count = 0;
+	struct vcd_property_hdr prop_hdr;
+
+	VCD_MSG_LOW("vcd_flush_output_buffers:");
+	buf_pool = &cctxt->out_buf_pool;
+	buf_entry = vcd_buffer_pool_entry_de_q(buf_pool);
+	while (buf_entry) {
+		if (!cctxt->decoding || buf_entry->in_use) {
+			buf_entry->frame.data_len = 0;
+			cctxt->callback(VCD_EVT_RESP_OUTPUT_FLUSHED,
+					VCD_S_SUCCESS,
+					&buf_entry->frame,
+					sizeof(struct vcd_frame_data),
+					cctxt, cctxt->client_data);
+			if (buf_entry->in_use) {
+				VCD_BUFFERPOOL_INUSE_DECREMENT(
+					buf_pool->in_use);
+				buf_entry->in_use = false;
+			}
+			count++;
+		}
+		buf_entry = vcd_buffer_pool_entry_de_q(buf_pool);
+	}
+	vcd_flush_bframe_buffers(cctxt, VCD_FLUSH_OUTPUT);
+	if (buf_pool->in_use || buf_pool->q_len) {
+		VCD_MSG_ERROR("%s(): WARNING in_use(%u) or q_len(%u) not zero!",
+			__func__, buf_pool->in_use, buf_pool->q_len);
+		buf_pool->in_use = buf_pool->q_len = 0;
+		}
+	if (cctxt->sched_clnt_hdl) {
+		if (count > cctxt->sched_clnt_hdl->tkns)
+			cctxt->sched_clnt_hdl->tkns = 0;
+		else
+			cctxt->sched_clnt_hdl->tkns -= count;
+	}
+
+	if (cctxt->ddl_hdl_valid && cctxt->decoding) {
+		prop_hdr.prop_id = DDL_I_REQ_OUTPUT_FLUSH;
+		prop_hdr.sz = sizeof(u32);
+		count = 0x1;
+
+		(void)ddl_set_property(cctxt->ddl_handle, &prop_hdr,
+					&count);
+	}
+	vcd_release_all_clnt_frm_transc(cctxt);
+	cctxt->status.mask &= ~VCD_IN_RECONFIG;
+}
+
+u32 vcd_flush_buffers(struct vcd_clnt_ctxt *cctxt, u32 mode)
+{
+	u32 rc = VCD_S_SUCCESS;
+	struct vcd_buffer_entry *buf_entry;
+
+	VCD_MSG_LOW("vcd_flush_buffers:");
+
+	if (mode > VCD_FLUSH_ALL || !(mode & VCD_FLUSH_ALL)) {
+		VCD_MSG_ERROR("Invalid flush mode %d", mode);
+		return VCD_ERR_ILLEGAL_PARM;
+	}
+
+	VCD_MSG_MED("Flush mode %d requested", mode);
+	if ((mode & VCD_FLUSH_INPUT) &&
+		cctxt->sched_clnt_hdl) {
+
+		rc = vcd_sched_dequeue_buffer(
+			cctxt->sched_clnt_hdl, &buf_entry);
+		while (!VCD_FAILED(rc) && buf_entry) {
+			if (buf_entry->virtual) {
+				cctxt->callback(VCD_EVT_RESP_INPUT_FLUSHED,
+						VCD_S_SUCCESS,
+						&buf_entry->frame,
+						sizeof(struct
+							 vcd_frame_data),
+						cctxt,
+						cctxt->client_data);
+				}
+
+			buf_entry->in_use = false;
+			VCD_BUFFERPOOL_INUSE_DECREMENT(
+				cctxt->in_buf_pool.in_use);
+			buf_entry = NULL;
+			rc = vcd_sched_dequeue_buffer(
+				cctxt->sched_clnt_hdl, &buf_entry);
+		}
+	}
+	if (rc != VCD_ERR_QEMPTY)
+		VCD_FAILED_RETURN(rc, "Failed: vcd_sched_dequeue_buffer");
+	if (cctxt->status.frame_submitted > 0)
+		cctxt->status.mask |= mode;
+	else {
+		if (mode & VCD_FLUSH_INPUT)
+			vcd_flush_bframe_buffers(cctxt, VCD_FLUSH_INPUT);
+		if (mode & VCD_FLUSH_OUTPUT)
+			vcd_flush_output_buffers(cctxt);
+	}
+	return VCD_S_SUCCESS;
+}
+
+void vcd_flush_buffers_in_err_fatal(struct vcd_clnt_ctxt *cctxt)
+{
+	VCD_MSG_LOW("\n vcd_flush_buffers_in_err_fatal:");
+	(void) vcd_flush_buffers(cctxt, VCD_FLUSH_ALL);
+	vcd_flush_in_use_buffer_pool_entries(cctxt,
+		&cctxt->in_buf_pool, VCD_EVT_RESP_INPUT_FLUSHED);
+	vcd_flush_in_use_buffer_pool_entries(cctxt,
+		&cctxt->out_buf_pool, VCD_EVT_RESP_OUTPUT_FLUSHED);
+	vcd_send_flush_done(cctxt, VCD_S_SUCCESS);
+}
+
+u32 vcd_init_client_context(struct vcd_clnt_ctxt *cctxt)
+{
+	u32 rc;
+	VCD_MSG_LOW("vcd_init_client_context:");
+	rc = ddl_open(&cctxt->ddl_handle, cctxt->decoding);
+	VCD_FAILED_RETURN(rc, "Failed: ddl_open");
+	cctxt->vcd_enable_ion = res_trk_get_enable_ion();
+	if (cctxt->vcd_enable_ion) {
+		cctxt->vcd_ion_client = res_trk_get_ion_client();
+		if (!cctxt->vcd_ion_client) {
+			VCD_MSG_LOW("vcd_init_ion_get_client_failed:");
+			return -EINVAL;
+		}
+	}
+	cctxt->ddl_hdl_valid = true;
+	cctxt->clnt_state.state = VCD_CLIENT_STATE_OPEN;
+	cctxt->clnt_state.state_table =
+		vcd_get_client_state_table(VCD_CLIENT_STATE_OPEN);
+	cctxt->signature = VCD_SIGNATURE;
+	cctxt->live = true;
+	cctxt->bframe = 0;
+	cctxt->cmd_q.pending_cmd = VCD_CMD_NONE;
+	cctxt->status.last_evt = VCD_EVT_RESP_BASE;
+	cctxt->num_slices = 1;
+	return rc;
+}
+
+void vcd_destroy_client_context(struct vcd_clnt_ctxt *cctxt)
+{
+	struct vcd_dev_ctxt *dev_ctxt;
+	struct vcd_clnt_ctxt *client;
+	struct vcd_buffer_entry *buf_entry;
+	u32 rc = VCD_S_SUCCESS;
+	VCD_MSG_LOW("vcd_destroy_client_context:");
+
+	dev_ctxt = cctxt->dev_ctxt;
+
+	if (cctxt == dev_ctxt->cctxt_list_head) {
+		VCD_MSG_MED("Clnt list head clnt being removed");
+
+		dev_ctxt->cctxt_list_head = cctxt->next;
+	} else {
+		client = dev_ctxt->cctxt_list_head;
+		while (client && cctxt != client->next)
+			client = client->next;
+		if (client)
+			client->next = cctxt->next;
+		if (!client) {
+			rc = VCD_ERR_FAIL;
+			VCD_MSG_ERROR("Client not found in client list");
+		}
+	}
+
+	if (VCD_FAILED(rc))
+		return;
+
+	if (cctxt->sched_clnt_hdl) {
+		rc = VCD_S_SUCCESS;
+		while (!VCD_FAILED(rc)) {
+			rc = vcd_sched_dequeue_buffer(
+				cctxt->sched_clnt_hdl, &buf_entry);
+			if (rc != VCD_ERR_QEMPTY && VCD_FAILED(rc))
+				VCD_MSG_ERROR("\n Failed: "
+					"vcd_sched_de_queue_buffer");
+		}
+		rc = vcd_sched_remove_client(cctxt->sched_clnt_hdl);
+		if (VCD_FAILED(rc))
+			VCD_MSG_ERROR("\n Failed: sched_remove_client");
+		cctxt->sched_clnt_hdl = NULL;
+	}
+
+	if (cctxt->seq_hdr.sequence_header) {
+		vcd_pmem_free(cctxt->seq_hdr.sequence_header,
+				  cctxt->seq_hdr_phy_addr, cctxt);
+		cctxt->seq_hdr.sequence_header = NULL;
+	}
+
+	vcd_free_buffers_internal(cctxt, &cctxt->in_buf_pool);
+	vcd_free_buffers_internal(cctxt, &cctxt->out_buf_pool);
+	vcd_free_buffer_pool_entries(&cctxt->in_buf_pool);
+	vcd_free_buffer_pool_entries(&cctxt->out_buf_pool);
+	vcd_release_all_clnt_transc(cctxt);
+
+	if (cctxt->ddl_hdl_valid) {
+		(void)ddl_close(&cctxt->ddl_handle);
+		cctxt->ddl_hdl_valid = false;
+	}
+
+	cctxt->signature = 0;
+	cctxt->clnt_state.state = VCD_CLIENT_STATE_NULL;
+	cctxt->clnt_state.state_table = NULL;
+	cctxt->vcd_ion_client = NULL;
+	kfree(cctxt);
+}
+
+u32 vcd_check_for_client_context(
+	struct vcd_dev_ctxt *dev_ctxt, s32 driver_id)
+{
+	struct vcd_clnt_ctxt *client;
+
+	client = dev_ctxt->cctxt_list_head;
+	while (client && client->driver_id != driver_id)
+		client = client->next;
+
+	if (!client)
+		return false;
+	else
+		return true;
+}
+
+u32 vcd_validate_driver_handle(
+	struct vcd_dev_ctxt *dev_ctxt, s32 driver_handle)
+{
+	driver_handle--;
+
+	if (driver_handle < 0 ||
+		driver_handle >= VCD_DRIVER_INSTANCE_MAX ||
+		!dev_ctxt->driver_ids[driver_handle]) {
+		return false;
+	} else {
+		return true;
+	}
+}
+
+u32 vcd_client_cmd_en_q(
+	struct vcd_clnt_ctxt *cctxt, enum vcd_command command)
+{
+	u32 result;
+
+	if (cctxt->cmd_q.pending_cmd == VCD_CMD_NONE) {
+		cctxt->cmd_q.pending_cmd = command;
+		result = true;
+	} else {
+		result = false;
+	}
+
+	return result;
+}
+
+void vcd_client_cmd_flush_and_en_q(
+	struct vcd_clnt_ctxt *cctxt, enum vcd_command command)
+{
+	cctxt->cmd_q.pending_cmd = command;
+}
+
+u32 vcd_client_cmd_de_q(struct vcd_clnt_ctxt *cctxt,
+	enum vcd_command *command)
+{
+	if (cctxt->cmd_q.pending_cmd == VCD_CMD_NONE)
+		return false;
+
+	*command = cctxt->cmd_q.pending_cmd;
+	cctxt->cmd_q.pending_cmd = VCD_CMD_NONE;
+
+	return true;
+}
+
+u32 vcd_get_next_queued_client_cmd(struct vcd_dev_ctxt *dev_ctxt,
+	struct vcd_clnt_ctxt **cctxt, enum vcd_command *command)
+{
+	struct vcd_clnt_ctxt *client = dev_ctxt->cctxt_list_head;
+	u32 result = false;
+
+	while (client && !result) {
+		*cctxt = client;
+		result = vcd_client_cmd_de_q(client, command);
+		client = client->next;
+	}
+	return result;
+}
+
+u32 vcd_submit_cmd_sess_start(struct vcd_transc *transc)
+{
+	u32 rc;
+	struct vcd_sequence_hdr Seq_hdr;
+
+	VCD_MSG_LOW("vcd_submit_cmd_sess_start:");
+
+	if (transc->cctxt->decoding) {
+
+		if (transc->cctxt->seq_hdr.sequence_header) {
+			Seq_hdr.sequence_header_len =
+				transc->cctxt->seq_hdr.
+				sequence_header_len;
+			Seq_hdr.sequence_header =
+				transc->cctxt->seq_hdr_phy_addr;
+
+			rc = ddl_decode_start(transc->cctxt->ddl_handle,
+						  &Seq_hdr, (void *)transc);
+		} else {
+			rc = ddl_decode_start(transc->cctxt->ddl_handle,
+						  NULL, (void *)transc);
+		}
+
+	} else {
+		vcd_set_num_slices(transc->cctxt);
+		rc = ddl_encode_start(transc->cctxt->ddl_handle,
+					  (void *)transc);
+	}
+	if (!VCD_FAILED(rc)) {
+		transc->cctxt->status.cmd_submitted++;
+		vcd_device_timer_start(transc->cctxt->dev_ctxt);
+	} else
+		VCD_MSG_ERROR("rc = 0x%x. Failed: ddl start", rc);
+
+	return rc;
+}
+
+u32 vcd_submit_cmd_sess_end(struct vcd_transc *transc)
+{
+	u32 rc;
+
+	VCD_MSG_LOW("vcd_submit_cmd_sess_end:");
+
+	if (transc->cctxt->decoding) {
+		rc = ddl_decode_end(transc->cctxt->ddl_handle,
+					(void *)transc);
+	} else {
+		rc = ddl_encode_end(transc->cctxt->ddl_handle,
+					(void *)transc);
+	}
+	if (!VCD_FAILED(rc)) {
+		transc->cctxt->status.cmd_submitted++;
+		vcd_device_timer_start(transc->cctxt->dev_ctxt);
+	} else
+		VCD_MSG_ERROR("rc = 0x%x. Failed: ddl end", rc);
+
+	return rc;
+}
+
+void vcd_submit_cmd_client_close(struct vcd_clnt_ctxt *cctxt)
+{
+	(void) ddl_close(&cctxt->ddl_handle);
+	cctxt->ddl_hdl_valid = false;
+	cctxt->status.mask &= ~VCD_CLEANING_UP;
+	if (cctxt->status.mask & VCD_CLOSE_PENDING) {
+		vcd_destroy_client_context(cctxt);
+		vcd_handle_for_last_clnt_close(cctxt->dev_ctxt, true);
+	}
+}
+
+u32 vcd_submit_command_in_continue(struct vcd_dev_ctxt
+	*dev_ctxt, struct vcd_transc *transc)
+{
+	struct vcd_property_hdr   prop_hdr;
+	struct vcd_clnt_ctxt *client = NULL;
+	enum vcd_command cmd = VCD_CMD_NONE;
+	u32 rc = VCD_ERR_FAIL;
+	u32 result = false, flush = 0, event = 0;
+	u32 command_break = false;
+
+	VCD_MSG_LOW("\n vcd_submit_command_in_continue:");
+
+	while (!command_break) {
+		result = vcd_get_next_queued_client_cmd(dev_ctxt,
+			&client, &cmd);
+
+		if (!result)
+			command_break = true;
+		else {
+			transc->type = cmd;
+			transc->cctxt = client;
+
+		 switch (cmd) {
+		 case VCD_CMD_CODEC_START:
+			{
+				rc = vcd_submit_cmd_sess_start(transc);
+				event = VCD_EVT_RESP_START;
+				break;
+			}
+		 case VCD_CMD_CODEC_STOP:
+			{
+				rc = vcd_submit_cmd_sess_end(transc);
+				event = VCD_EVT_RESP_STOP;
+				break;
+			}
+		 case VCD_CMD_OUTPUT_FLUSH:
+			{
+				prop_hdr.prop_id = DDL_I_REQ_OUTPUT_FLUSH;
+				prop_hdr.sz = sizeof(u32);
+				flush = 0x1;
+				(void) ddl_set_property(client->ddl_handle,
+						 &prop_hdr, &flush);
+				vcd_release_command_channel(dev_ctxt,
+					transc);
+				rc = VCD_S_SUCCESS;
+				break;
+			}
+		 case VCD_CMD_CLIENT_CLOSE:
+			{
+				vcd_submit_cmd_client_close(client);
+				vcd_release_command_channel(dev_ctxt,
+					transc);
+				rc = VCD_S_SUCCESS;
+				break;
+			}
+		 default:
+			{
+				VCD_MSG_ERROR("\n vcd_submit_command: Unknown"
+					"command %d", (int)cmd);
+				break;
+			}
+		 }
+
+		 if (!VCD_FAILED(rc)) {
+			command_break = true;
+		 } else	{
+			VCD_MSG_ERROR("vcd_submit_command %d: failed 0x%x",
+				cmd, rc);
+			client->callback(event, rc, NULL, 0, client,
+				client->client_data);
+		 }
+	  }
+	}
+	return result;
+}
+
+u32 vcd_schedule_frame(struct vcd_dev_ctxt *dev_ctxt,
+	struct vcd_clnt_ctxt **cctxt, struct vcd_buffer_entry
+	**ip_buf_entry)
+{
+	u32 rc = VCD_S_SUCCESS;
+	VCD_MSG_LOW("vcd_schedule_frame:");
+
+	if (!dev_ctxt->cctxt_list_head) {
+		VCD_MSG_HIGH("Client list empty");
+		return false;
+	}
+	rc = vcd_sched_get_client_frame(&dev_ctxt->sched_clnt_list,
+		cctxt, ip_buf_entry);
+	if (rc == VCD_ERR_QEMPTY) {
+		VCD_MSG_HIGH("No frame available. Sched queues are empty");
+		return false;
+	}
+	if (VCD_FAILED(rc)) {
+		VCD_MSG_FATAL("vcd_submit_frame: sched_de_queue_frame"
+			"failed 0x%x", rc);
+	  return false;
+	}
+	if (!*cctxt || !*ip_buf_entry) {
+		VCD_MSG_FATAL("Sched returned invalid values. ctxt=%p,"
+			"ipbuf=%p",	*cctxt, *ip_buf_entry);
+		return false;
+	}
+	return true;
+}
+
+void vcd_try_submit_frame(struct vcd_dev_ctxt *dev_ctxt)
+{
+	struct vcd_transc *transc;
+	u32 rc = VCD_S_SUCCESS;
+	struct vcd_clnt_ctxt *cctxt = NULL;
+	struct vcd_buffer_entry *ip_buf_entry = NULL;
+	u32 result = false;
+
+	VCD_MSG_LOW("vcd_try_submit_frame:");
+
+	if (!vcd_get_frame_channel(dev_ctxt, &transc))
+		return;
+
+	if (!vcd_schedule_frame(dev_ctxt, &cctxt, &ip_buf_entry)) {
+		vcd_release_frame_channel(dev_ctxt, transc);
+		return;
+	}
+
+	rc = vcd_power_event(dev_ctxt, cctxt, VCD_EVT_PWR_CLNT_CMD_BEGIN);
+
+	if (!VCD_FAILED(rc)) {
+		transc->cctxt = cctxt;
+		transc->ip_buf_entry = ip_buf_entry;
+
+		result = vcd_submit_frame(dev_ctxt, transc);
+	} else {
+		VCD_MSG_ERROR("Failed: VCD_EVT_PWR_CLNT_CMD_BEGIN");
+		(void) vcd_sched_queue_buffer(
+			cctxt->sched_clnt_hdl, ip_buf_entry, false);
+		cctxt->sched_clnt_hdl->tkns++;
+	}
+
+	if (!result) {
+		vcd_release_frame_channel(dev_ctxt, transc);
+		(void) vcd_power_event(dev_ctxt, cctxt,
+				VCD_EVT_PWR_CLNT_CMD_FAIL);
+	}
+}
+
+u32 vcd_submit_frame(struct vcd_dev_ctxt *dev_ctxt,
+					 struct vcd_transc *transc)
+{
+	struct vcd_clnt_ctxt *cctxt = NULL;
+	struct vcd_frame_data *ip_frm_entry;
+	struct vcd_buffer_entry *op_buf_entry = NULL;
+	u32 rc = VCD_S_SUCCESS;
+	u32 evcode = 0;
+	u32 perf_level = 0;
+	int decodeTime = 0;
+	struct ddl_frame_data_tag ddl_ip_frm;
+	struct ddl_frame_data_tag *ddl_op_frm;
+	u32 out_buf_cnt = 0;
+
+	VCD_MSG_LOW("vcd_submit_frame:");
+	cctxt = transc->cctxt;
+	ip_frm_entry = &transc->ip_buf_entry->frame;
+
+	transc->op_buf_entry = op_buf_entry;
+	transc->ip_frm_tag = ip_frm_entry->ip_frm_tag;
+	transc->time_stamp = ip_frm_entry->time_stamp;
+	transc->flags = ip_frm_entry->flags;
+	ip_frm_entry->ip_frm_tag = (u32) transc;
+	memset(&ddl_ip_frm, 0, sizeof(ddl_ip_frm));
+	if (cctxt->decoding) {
+		decodeTime = ddl_get_core_decode_proc_time(cctxt->ddl_handle);
+		if (decodeTime > MAX_DEC_TIME) {
+			if (res_trk_get_curr_perf_level(&perf_level)) {
+				vcd_update_decoder_perf_level(dev_ctxt,
+				   res_trk_estimate_perf_level(perf_level));
+				ddl_reset_avg_dec_time(cctxt->ddl_handle);
+			} else
+				VCD_MSG_ERROR("%s(): retrieve curr_perf_level"
+						"returned FALSE\n", __func__);
+		}
+		evcode = CLIENT_STATE_EVENT_NUMBER(decode_frame);
+		ddl_ip_frm.vcd_frm = *ip_frm_entry;
+		rc = ddl_decode_frame(cctxt->ddl_handle, &ddl_ip_frm,
+							(void *) transc);
+	} else {
+		ddl_op_frm = (struct ddl_frame_data_tag *)
+			kmalloc((sizeof(struct ddl_frame_data_tag) *
+			VCD_ENC_MAX_OUTBFRS_PER_FRAME), GFP_KERNEL);
+		if (!ddl_op_frm) {
+			VCD_MSG_ERROR("Memory allocation failure");
+			return VCD_ERR_ALLOC_FAIL;
+		}
+		memset(ddl_op_frm, 0, (sizeof(struct ddl_frame_data_tag) *
+			VCD_ENC_MAX_OUTBFRS_PER_FRAME));
+		for (out_buf_cnt = 0; out_buf_cnt < cctxt->num_slices ;
+				out_buf_cnt++) {
+			op_buf_entry = vcd_buffer_pool_entry_de_q(
+				&cctxt->out_buf_pool);
+			if (!op_buf_entry) {
+				VCD_MSG_ERROR("Sched provided frame when no"
+					"op buffer was present");
+				rc = VCD_ERR_FAIL;
+				break;
+			}
+			op_buf_entry->in_use = true;
+			cctxt->out_buf_pool.in_use++;
+			ddl_op_frm[out_buf_cnt].vcd_frm = op_buf_entry->frame;
+			VCD_MSG_LOW("%s : buffer_cnt = %d framebfr(virtual)"
+				" 0x%p", __func__, out_buf_cnt,
+				op_buf_entry->frame.virtual);
+			VCD_MSG_LOW("framebfr(physical) 0x%p bfrlength %d",
+				op_buf_entry->frame.physical,
+				op_buf_entry->frame.alloc_len);
+		}
+		ddl_ip_frm.vcd_frm = *ip_frm_entry;
+		ddl_ip_frm.frm_delta =
+			vcd_calculate_frame_delta(cctxt,
+				ip_frm_entry);
+		evcode = CLIENT_STATE_EVENT_NUMBER(encode_frame);
+
+		if (!VCD_FAILED(rc)) {
+			rc = ddl_encode_frame(cctxt->ddl_handle,
+				&ddl_ip_frm, &ddl_op_frm[0], (void *) transc);
+		}
+		kfree(ddl_op_frm);
+	}
+	ip_frm_entry->ip_frm_tag = transc->ip_frm_tag;
+	if (!VCD_FAILED(rc)) {
+		vcd_device_timer_start(dev_ctxt);
+		cctxt->status.frame_submitted++;
+		if (ip_frm_entry->flags & VCD_FRAME_FLAG_EOS)
+			vcd_do_client_state_transition(cctxt,
+				VCD_CLIENT_STATE_EOS, evcode);
+	} else {
+		VCD_MSG_ERROR("Frame submission failed. rc = 0x%x", rc);
+		vcd_handle_submit_frame_failed(dev_ctxt, transc);
+	}
+	return true;
+}
+
+u32 vcd_try_submit_frame_in_continue(struct vcd_dev_ctxt *dev_ctxt,
+	struct vcd_transc *transc)
+{
+	struct vcd_clnt_ctxt *cctxt = NULL;
+	struct vcd_buffer_entry *ip_buf_entry = NULL;
+
+	VCD_MSG_LOW("vcd_try_submit_frame_in_continue:");
+
+	if (!vcd_schedule_frame(dev_ctxt, &cctxt, &ip_buf_entry))
+		return false;
+
+	transc->cctxt = cctxt;
+	transc->ip_buf_entry = ip_buf_entry;
+
+	return vcd_submit_frame(dev_ctxt, transc);
+}
+
+u32 vcd_process_cmd_sess_start(struct vcd_clnt_ctxt *cctxt)
+{
+	struct vcd_transc *transc;
+	u32 rc = VCD_S_SUCCESS;
+
+	VCD_MSG_LOW("vcd_process_cmd_sess_start:");
+	if (vcd_get_command_channel(cctxt->dev_ctxt, &transc)) {
+		rc = vcd_power_event(cctxt->dev_ctxt,
+					 cctxt, VCD_EVT_PWR_CLNT_CMD_BEGIN);
+
+		if (!VCD_FAILED(rc)) {
+			transc->type = VCD_CMD_CODEC_START;
+			transc->cctxt = cctxt;
+			rc = vcd_submit_cmd_sess_start(transc);
+		} else {
+			VCD_MSG_ERROR("Failed: VCD_EVT_PWR_CLNT_CMD_BEGIN");
+		}
+
+		if (VCD_FAILED(rc)) {
+			vcd_release_command_channel(cctxt->dev_ctxt,
+							transc);
+		}
+	} else {
+		u32 result;
+
+		result = vcd_client_cmd_en_q(cctxt, VCD_CMD_CODEC_START);
+		if (!result) {
+			rc = VCD_ERR_BUSY;
+			VCD_MSG_ERROR("%s(): vcd_client_cmd_en_q() "
+				"failed\n", __func__);
+		}
+	}
+
+	if (VCD_FAILED(rc)) {
+		(void)vcd_power_event(cctxt->dev_ctxt,
+					  cctxt, VCD_EVT_PWR_CLNT_CMD_FAIL);
+	}
+
+	return rc;
+}
+
+void vcd_send_frame_done_in_eos(struct vcd_clnt_ctxt *cctxt,
+	 struct vcd_frame_data *input_frame, u32 valid_opbuf)
+{
+	VCD_MSG_LOW("vcd_send_frame_done_in_eos:");
+
+	if (!input_frame->virtual && !valid_opbuf) {
+		VCD_MSG_MED("Sending NULL output with EOS");
+
+		cctxt->out_buf_pool.entries[0].frame.flags =
+			VCD_FRAME_FLAG_EOS;
+		cctxt->out_buf_pool.entries[0].frame.data_len = 0;
+		cctxt->out_buf_pool.entries[0].frame.time_stamp =
+			input_frame->time_stamp;
+		cctxt->out_buf_pool.entries[0].frame.ip_frm_tag =
+			input_frame->ip_frm_tag;
+
+		cctxt->callback(VCD_EVT_RESP_OUTPUT_DONE,
+				  VCD_S_SUCCESS,
+				  &cctxt->out_buf_pool.entries[0].frame,
+				  sizeof(struct vcd_frame_data),
+				  cctxt, cctxt->client_data);
+
+		memset(&cctxt->out_buf_pool.entries[0].frame,
+			   0, sizeof(struct vcd_frame_data));
+	} else if (!input_frame->data_len) {
+		if (cctxt->decoding) {
+			vcd_send_frame_done_in_eos_for_dec(cctxt,
+							   input_frame);
+		} else {
+			vcd_send_frame_done_in_eos_for_enc(cctxt,
+							   input_frame);
+		}
+
+	}
+}
+
+void vcd_send_frame_done_in_eos_for_dec(
+	struct vcd_clnt_ctxt *cctxt,
+	 struct vcd_frame_data *input_frame)
+{
+	struct vcd_buffer_entry *buf_entry;
+	struct vcd_property_hdr prop_hdr;
+	u32 rc;
+	struct ddl_frame_data_tag ddl_frm;
+
+	prop_hdr.prop_id = DDL_I_DPB_RETRIEVE;
+	prop_hdr.sz = sizeof(struct ddl_frame_data_tag);
+	memset(&ddl_frm, 0, sizeof(ddl_frm));
+	rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr, &ddl_frm);
+
+	if (VCD_FAILED(rc) || !ddl_frm.vcd_frm.virtual) {
+		cctxt->status.eos_trig_ip_frm = *input_frame;
+		cctxt->status.mask |= VCD_EOS_WAIT_OP_BUF;
+		return;
+	}
+
+	buf_entry = vcd_find_buffer_pool_entry(&cctxt->out_buf_pool,
+		ddl_frm.vcd_frm.virtual);
+	if (!buf_entry) {
+		VCD_MSG_ERROR("Unrecognized buffer address provided = %p",
+				  ddl_frm.vcd_frm.virtual);
+		return;
+	} else {
+		if (cctxt->sched_clnt_hdl->tkns)
+			cctxt->sched_clnt_hdl->tkns--;
+
+		VCD_MSG_MED("Sending non-NULL output with EOS");
+
+		buf_entry->frame.data_len = 0;
+		buf_entry->frame.offset = 0;
+		buf_entry->frame.flags |= VCD_FRAME_FLAG_EOS;
+		buf_entry->frame.ip_frm_tag = input_frame->ip_frm_tag;
+		buf_entry->frame.time_stamp = input_frame->time_stamp;
+
+		cctxt->callback(VCD_EVT_RESP_OUTPUT_DONE,
+				  VCD_S_SUCCESS,
+				  &buf_entry->frame,
+				  sizeof(struct vcd_frame_data),
+				  cctxt, cctxt->client_data);
+
+		buf_entry->in_use = false;
+		VCD_BUFFERPOOL_INUSE_DECREMENT(cctxt->out_buf_pool.in_use);
+	}
+}
+
+void vcd_send_frame_done_in_eos_for_enc(
+	struct vcd_clnt_ctxt *cctxt,
+	 struct vcd_frame_data *input_frame)
+{
+	struct vcd_buffer_entry *op_buf_entry;
+
+	if (!cctxt->out_buf_pool.q_len) {
+		cctxt->status.eos_trig_ip_frm = *input_frame;
+
+		cctxt->status.mask |= VCD_EOS_WAIT_OP_BUF;
+
+		return;
+	}
+
+	op_buf_entry = vcd_buffer_pool_entry_de_q(&cctxt->out_buf_pool);
+	if (!op_buf_entry) {
+		VCD_MSG_ERROR("%s(): vcd_buffer_pool_entry_de_q() "
+			"failed\n", __func__);
+	} else {
+		if (cctxt->sched_clnt_hdl->tkns)
+			cctxt->sched_clnt_hdl->tkns--;
+
+		VCD_MSG_MED("Sending non-NULL output with EOS");
+
+		op_buf_entry->frame.data_len = 0;
+		op_buf_entry->frame.flags |= VCD_FRAME_FLAG_EOS;
+		op_buf_entry->frame.ip_frm_tag =
+			input_frame->ip_frm_tag;
+		op_buf_entry->frame.time_stamp = input_frame->time_stamp;
+
+		cctxt->callback(VCD_EVT_RESP_OUTPUT_DONE,
+				  VCD_S_SUCCESS,
+				  &op_buf_entry->frame,
+				  sizeof(struct vcd_frame_data),
+				  cctxt, cctxt->client_data);
+	}
+}
+
+u32 vcd_handle_recvd_eos(
+	struct vcd_clnt_ctxt *cctxt,
+	 struct vcd_frame_data *input_frame, u32 *pb_eos_handled)
+{
+	u32 rc;
+
+	VCD_MSG_LOW("vcd_handle_recvd_eos:");
+
+	*pb_eos_handled = false;
+
+	if (input_frame->virtual &&
+			input_frame->data_len)
+		return VCD_S_SUCCESS;
+
+	input_frame->data_len = 0;
+	rc = vcd_sched_mark_client_eof(cctxt->sched_clnt_hdl);
+	if (VCD_FAILED(rc) && rc != VCD_ERR_QEMPTY)
+		return rc;
+
+	if (rc == VCD_S_SUCCESS)
+		*pb_eos_handled = true;
+	else if (cctxt->decoding && !input_frame->virtual)
+		cctxt->sched_clnt_hdl->tkns++;
+	else if (!cctxt->decoding) {
+		vcd_send_frame_done_in_eos(cctxt, input_frame, false);
+		if (cctxt->status.mask & VCD_EOS_WAIT_OP_BUF) {
+			vcd_do_client_state_transition(cctxt,
+				VCD_CLIENT_STATE_EOS,
+				CLIENT_STATE_EVENT_NUMBER
+				(encode_frame));
+		}
+		*pb_eos_handled = true;
+	}
+
+	if (*pb_eos_handled &&
+		input_frame->virtual &&
+		!input_frame->data_len) {
+		cctxt->callback(VCD_EVT_RESP_INPUT_DONE,
+				  VCD_S_SUCCESS,
+				  input_frame,
+				  sizeof(struct vcd_frame_data),
+				  cctxt, cctxt->client_data);
+	}
+	return VCD_S_SUCCESS;
+}
+
+u32 vcd_handle_first_decode_frame(struct vcd_clnt_ctxt *cctxt)
+{
+	u32 rc =  VCD_ERR_BAD_STATE;
+
+	VCD_MSG_LOW("vcd_handle_first_decode_frame:");
+	if (!cctxt->in_buf_pool.entries ||
+		!cctxt->out_buf_pool.entries ||
+		cctxt->in_buf_pool.validated !=
+		cctxt->in_buf_pool.count ||
+		cctxt->out_buf_pool.validated !=
+		cctxt->out_buf_pool.count)
+		VCD_MSG_ERROR("Buffer pool is not completely setup yet");
+	else if (!cctxt->sched_clnt_hdl) {
+		rc = vcd_sched_add_client(cctxt);
+		VCD_FAILED_RETURN(rc, "Failed: vcd_add_client_to_sched");
+		cctxt->sched_clnt_hdl->tkns =
+			cctxt->out_buf_pool.q_len;
+	} else
+		rc = vcd_sched_suspend_resume_clnt(cctxt, true);
+	return rc;
+}
+
+u32 vcd_setup_with_ddl_capabilities(struct vcd_dev_ctxt *dev_ctxt)
+{
+	struct vcd_property_hdr Prop_hdr;
+	struct ddl_property_capability capability;
+	u32 rc = VCD_S_SUCCESS;
+
+	VCD_MSG_LOW("vcd_setup_with_ddl_capabilities:");
+
+	if (!dev_ctxt->ddl_cmd_ch_depth) {
+		Prop_hdr.prop_id = DDL_I_CAPABILITY;
+		Prop_hdr.sz = sizeof(capability);
+
+		/*
+		** Since this is underlying core's property we don't need a
+		** ddl client handle.
+		*/
+		rc = ddl_get_property(NULL, &Prop_hdr, &capability);
+
+		if (!VCD_FAILED(rc)) {
+			/*
+			** Allocate the transaction table.
+			*/
+			dev_ctxt->trans_tbl_size =
+				(VCD_MAX_CLIENT_TRANSACTIONS *
+				capability.max_num_client) +
+				capability.general_command_depth;
+
+			dev_ctxt->trans_tbl = (struct vcd_transc *)
+				kzalloc((sizeof(struct vcd_transc) *
+				dev_ctxt->trans_tbl_size), GFP_KERNEL);
+
+			if (!dev_ctxt->trans_tbl) {
+				VCD_MSG_ERROR("Transaction table alloc failed");
+				rc = VCD_ERR_ALLOC_FAIL;
+			} else	{
+				dev_ctxt->ddl_cmd_concurrency =
+					!capability.exclusive;
+				dev_ctxt->ddl_frame_ch_depth =
+					capability.frame_command_depth;
+				dev_ctxt->ddl_cmd_ch_depth =
+					capability.general_command_depth;
+
+				vcd_reset_device_channels(dev_ctxt);
+
+				dev_ctxt->hw_time_out =
+					capability.ddl_time_out_in_ms;
+
+			}
+		}
+	}
+	return rc;
+}
+
+struct vcd_transc *vcd_get_free_trans_tbl_entry
+	(struct vcd_dev_ctxt *dev_ctxt) {
+	u32 i;
+
+	if (!dev_ctxt->trans_tbl)
+		return NULL;
+
+	i = 0;
+	while (i < dev_ctxt->trans_tbl_size &&
+		   dev_ctxt->trans_tbl[i].in_use)
+		i++;
+
+	if (i == dev_ctxt->trans_tbl_size) {
+		return NULL;
+	} else {
+		memset(&dev_ctxt->trans_tbl[i], 0,
+			   sizeof(struct vcd_transc));
+		dev_ctxt->trans_tbl[i].in_use = true;
+		VCD_MSG_LOW("%s: Get transc = 0x%x, in_use = %u\n",
+			__func__, (u32)(&dev_ctxt->trans_tbl[i]),
+			dev_ctxt->trans_tbl[i].in_use);
+		return &dev_ctxt->trans_tbl[i];
+	}
+}
+
+void vcd_release_trans_tbl_entry(struct vcd_transc *trans_entry)
+{
+	if (trans_entry) {
+		trans_entry->in_use = false;
+		VCD_MSG_LOW("%s: Free transc = 0x%x, in_use = %u\n",
+			__func__, (u32)trans_entry, trans_entry->in_use);
+	}
+}
+
+u32 vcd_handle_input_done(
+	struct vcd_clnt_ctxt *cctxt,
+	 void *payload, u32 event, u32 status)
+{
+	struct vcd_transc *transc;
+	struct ddl_frame_data_tag *frame =
+		(struct ddl_frame_data_tag *) payload;
+	struct vcd_buffer_entry *orig_frame = NULL;
+	u32 rc;
+	VCD_MSG_LOW("%s\n", __func__);
+
+	if (!cctxt->status.frame_submitted &&
+		!cctxt->status.frame_delayed) {
+		VCD_MSG_ERROR("Input done was not expected");
+		return VCD_ERR_BAD_STATE;
+	}
+
+	rc = vcd_validate_io_done_pyld(cctxt, payload, status);
+	if (rc == VCD_ERR_CLIENT_FATAL)
+		vcd_handle_clnt_fatal_input_done(cctxt, frame->frm_trans_end);
+	VCD_FAILED_RETURN(rc, "Bad input done payload");
+
+	transc = (struct vcd_transc *)frame->vcd_frm.ip_frm_tag;
+	orig_frame = vcd_find_buffer_pool_entry(&cctxt->in_buf_pool,
+					 transc->ip_buf_entry->virtual);
+
+	if ((transc->ip_buf_entry->frame.virtual !=
+		 frame->vcd_frm.virtual)
+		|| !transc->ip_buf_entry->in_use) {
+		VCD_MSG_ERROR("Bad frm transaction state");
+		vcd_handle_clnt_fatal_input_done(cctxt, frame->frm_trans_end);
+		return VCD_ERR_BAD_POINTER;
+	}
+
+	frame->vcd_frm.ip_frm_tag = transc->ip_frm_tag;
+	transc->frame = frame->vcd_frm.frame;
+
+	cctxt->callback(event,
+			status,
+			&frame->vcd_frm,
+			sizeof(struct vcd_frame_data),
+			cctxt, cctxt->client_data);
+
+	orig_frame->in_use--;
+	VCD_BUFFERPOOL_INUSE_DECREMENT(cctxt->in_buf_pool.in_use);
+
+	if (cctxt->decoding && orig_frame->in_use) {
+		VCD_MSG_ERROR("When decoding same input buffer not "
+				"supposed to be queued multiple times");
+		return VCD_ERR_FAIL;
+	}
+
+	if (orig_frame != transc->ip_buf_entry)
+		kfree(transc->ip_buf_entry);
+	transc->ip_buf_entry = NULL;
+	transc->input_done = true;
+
+	if (transc->input_done && transc->frame_done) {
+		VCD_MSG_LOW("%s Calling vcd_release_trans_tbl_entry\n",
+		__func__);
+		vcd_release_trans_tbl_entry(transc);
+	}
+
+	if (VCD_FAILED(status)) {
+		VCD_MSG_ERROR("INPUT_DONE returned err = 0x%x", status);
+		vcd_handle_input_done_failed(cctxt, transc);
+	} else
+		cctxt->status.mask |= VCD_FIRST_IP_DONE;
+
+	if (cctxt->status.frame_submitted > 0)
+		cctxt->status.frame_submitted--;
+	else
+		cctxt->status.frame_delayed--;
+
+	if (!VCD_FAILED(status) &&
+		cctxt->decoding) {
+		if (frame->vcd_frm.flags & VCD_FRAME_FLAG_CODECCONFIG) {
+			VCD_MSG_HIGH(
+				"INPUT_DONE with VCD_FRAME_FLAG_CODECCONFIG");
+			vcd_handle_input_done_with_codec_config(cctxt,
+				transc, frame);
+			frame->vcd_frm.flags &= ~VCD_FRAME_FLAG_CODECCONFIG;
+		}
+		if (frame->vcd_frm.interlaced)
+			vcd_handle_input_done_for_interlacing(cctxt);
+		if (frame->frm_trans_end)
+			vcd_handle_input_done_with_trans_end(cctxt);
+	}
+
+	return VCD_S_SUCCESS;
+}
+
+u32 vcd_handle_input_done_in_eos(
+	struct vcd_clnt_ctxt *cctxt, void *payload, u32 status)
+{
+	struct vcd_transc *transc;
+	struct ddl_frame_data_tag *frame =
+		(struct ddl_frame_data_tag *) payload;
+	u32 rc = VCD_ERR_FAIL, codec_config = false;
+	u32 core_type = res_trk_get_core_type();
+	VCD_MSG_LOW("%s\n", __func__);
+	rc = vcd_validate_io_done_pyld(cctxt, payload, status);
+	if (rc == VCD_ERR_CLIENT_FATAL)
+		vcd_handle_clnt_fatal_input_done(cctxt, frame->frm_trans_end);
+	VCD_FAILED_RETURN(rc, "Failed: vcd_validate_io_done_pyld");
+	transc = (struct vcd_transc *)frame->vcd_frm.ip_frm_tag;
+	codec_config = frame->vcd_frm.flags & VCD_FRAME_FLAG_CODECCONFIG;
+	rc = vcd_handle_input_done(cctxt,
+		payload, VCD_EVT_RESP_INPUT_DONE, status);
+	VCD_FAILED_RETURN(rc, "Failed: vcd_handle_input_done");
+	if (frame->vcd_frm.flags & VCD_FRAME_FLAG_EOS) {
+		VCD_MSG_HIGH("Got input done for EOS initiator");
+		transc->input_done = false;
+		transc->in_use = true;
+		if ((codec_config &&
+			 (status != VCD_ERR_BITSTREAM_ERR)) ||
+			((status == VCD_ERR_BITSTREAM_ERR) &&
+			 !(cctxt->status.mask & VCD_FIRST_IP_DONE) &&
+			 (core_type == VCD_CORE_720P)))
+			vcd_handle_eos_done(cctxt, transc, VCD_S_SUCCESS);
+	}
+	return rc;
+}
+
+u32 vcd_validate_io_done_pyld(
+	struct vcd_clnt_ctxt *cctxt, void *payload, u32 status)
+{
+	struct ddl_frame_data_tag *frame =
+		(struct ddl_frame_data_tag *) payload;
+	struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt;
+	struct vcd_transc *transc = NULL;
+	u32 rc = VCD_S_SUCCESS, i = 0;
+
+	if (!frame) {
+		VCD_MSG_ERROR("Bad payload from DDL");
+		return VCD_ERR_BAD_POINTER;
+	}
+
+	transc = (struct vcd_transc *)frame->vcd_frm.ip_frm_tag;
+	if (dev_ctxt->trans_tbl) {
+		while (i < dev_ctxt->trans_tbl_size &&
+			transc != &dev_ctxt->trans_tbl[i])
+			i++;
+		if (i == dev_ctxt->trans_tbl_size ||
+			!dev_ctxt->trans_tbl[i].in_use)
+			rc = VCD_ERR_CLIENT_FATAL;
+	} else
+		rc = VCD_ERR_CLIENT_FATAL;
+
+	if (VCD_FAILED(rc)) {
+		VCD_MSG_FATAL(
+			"vcd_validate_io_done_pyld: invalid transaction");
+	} else if (!frame->vcd_frm.virtual &&
+		status != VCD_ERR_INTRLCD_FIELD_DROP)
+		rc = VCD_ERR_BAD_POINTER;
+
+	return rc;
+}
+
+void vcd_handle_input_done_failed(
+	struct vcd_clnt_ctxt *cctxt, struct vcd_transc *transc)
+{
+	if (cctxt->decoding) {
+		cctxt->sched_clnt_hdl->tkns++;
+		vcd_release_trans_tbl_entry(transc);
+	}
+}
+
+void vcd_handle_input_done_with_codec_config(
+	struct vcd_clnt_ctxt *cctxt, struct vcd_transc *transc,
+	struct ddl_frame_data_tag *frm)
+{
+	cctxt->sched_clnt_hdl->tkns++;
+	if (frm->frm_trans_end)
+		vcd_release_trans_tbl_entry(transc);
+}
+
+void vcd_handle_input_done_for_interlacing(struct vcd_clnt_ctxt *cctxt)
+{
+	cctxt->status.int_field_cnt++;
+	if (cctxt->status.int_field_cnt == 1)
+		cctxt->sched_clnt_hdl->tkns++;
+	else if (cctxt->status.int_field_cnt ==
+		VCD_DEC_NUM_INTERLACED_FIELDS)
+		cctxt->status.int_field_cnt = 0;
+}
+
+void vcd_handle_input_done_with_trans_end(
+	struct vcd_clnt_ctxt *cctxt)
+{
+	if (!cctxt->decoding)
+		return;
+	if (cctxt->out_buf_pool.in_use <
+		cctxt->out_buf_pool.buf_req.min_count)
+		return;
+	if (!cctxt->sched_clnt_hdl->tkns)
+		cctxt->sched_clnt_hdl->tkns++;
+}
+
+u32 vcd_handle_output_required(struct vcd_clnt_ctxt
+	*cctxt, void *payload, u32 status)
+{
+	struct vcd_transc *transc;
+	struct ddl_frame_data_tag *frame =
+		(struct ddl_frame_data_tag *)payload;
+	u32 rc = VCD_S_SUCCESS;
+	VCD_MSG_LOW("%s\n", __func__);
+
+	if (!cctxt->status.frame_submitted &&
+		!cctxt->status.frame_delayed) {
+		VCD_MSG_ERROR("\n Input done was not expected");
+		return VCD_ERR_BAD_STATE;
+	}
+
+	rc = vcd_validate_io_done_pyld(cctxt, payload, status);
+	if (rc == VCD_ERR_CLIENT_FATAL)
+		vcd_handle_clnt_fatal_input_done(cctxt, frame->frm_trans_end);
+	VCD_FAILED_RETURN(rc, "\n Bad input done payload");
+
+	transc = (struct vcd_transc *)frame->
+		vcd_frm.ip_frm_tag;
+
+	if ((transc->ip_buf_entry->frame.virtual !=
+		 frame->vcd_frm.virtual) ||
+		!transc->ip_buf_entry->in_use) {
+		VCD_MSG_ERROR("\n Bad frm transaction state");
+		vcd_handle_clnt_fatal_input_done(cctxt, frame->frm_trans_end);
+		return VCD_ERR_BAD_STATE;
+	}
+	rc = vcd_sched_queue_buffer(cctxt->sched_clnt_hdl,
+			transc->ip_buf_entry, false);
+	VCD_FAILED_RETURN(rc, "Failed: vcd_sched_queue_buffer");
+
+	transc->ip_buf_entry = NULL;
+	vcd_release_trans_tbl_entry(transc);
+	frame->frm_trans_end = true;
+
+	if (VCD_FAILED(status))
+		VCD_MSG_ERROR("\n OUTPUT_REQ returned err = 0x%x",
+			status);
+
+	if (cctxt->status.frame_submitted > 0)
+		cctxt->status.frame_submitted--;
+	else
+		cctxt->status.frame_delayed--;
+
+
+	if (!VCD_FAILED(status) &&
+		cctxt->decoding &&
+		frame->vcd_frm.interlaced) {
+		if (cctxt->status.int_field_cnt > 0) {
+			VCD_MSG_ERROR("\n Not expected: OUTPUT_REQ"
+				"for 2nd interlace field");
+			rc = VCD_ERR_FAIL;
+		}
+	}
+
+	return rc;
+}
+
+u32 vcd_handle_output_required_in_flushing(
+struct vcd_clnt_ctxt *cctxt, void *payload)
+{
+	u32 rc;
+	struct vcd_transc *transc;
+	struct ddl_frame_data_tag *frame =
+		(struct ddl_frame_data_tag *)payload;
+	VCD_MSG_LOW("%s\n", __func__);
+
+	rc = vcd_validate_io_done_pyld(cctxt, payload, VCD_S_SUCCESS);
+	if (rc == VCD_ERR_CLIENT_FATAL)
+		vcd_handle_clnt_fatal_input_done(cctxt, frame->frm_trans_end);
+	VCD_FAILED_RETURN(rc, "Bad input done payload");
+
+	transc = (struct vcd_transc *)
+		(((struct ddl_frame_data_tag *)payload)->
+		 vcd_frm.ip_frm_tag);
+
+	((struct ddl_frame_data_tag *)payload)->
+		vcd_frm.interlaced = false;
+
+	rc = vcd_handle_input_done(cctxt, payload,
+			VCD_EVT_RESP_INPUT_FLUSHED, VCD_S_SUCCESS);
+	VCD_FAILED_RETURN(rc, "Failed: vcd_handle_input_done");
+
+	vcd_release_trans_tbl_entry(transc);
+	((struct ddl_frame_data_tag *)payload)->frm_trans_end = true;
+
+	return rc;
+}
+
+u32 vcd_handle_frame_done(
+	struct vcd_clnt_ctxt *cctxt,
+	 void *payload, u32 event, u32 status)
+{
+	struct vcd_buffer_entry *op_buf_entry = NULL;
+	struct ddl_frame_data_tag *op_frm =
+		(struct ddl_frame_data_tag *) payload;
+	struct vcd_transc *transc;
+	u32 rc;
+	s64 time_stamp;
+	VCD_MSG_LOW("%s\n", __func__);
+
+	rc = vcd_validate_io_done_pyld(cctxt, payload, status);
+	if (rc == VCD_ERR_CLIENT_FATAL)
+		vcd_handle_clnt_fatal(cctxt, op_frm->frm_trans_end);
+	VCD_FAILED_RETURN(rc, "Bad payload recvd");
+
+	transc = (struct vcd_transc *)op_frm->vcd_frm.ip_frm_tag;
+
+	if (op_frm->vcd_frm.virtual) {
+
+		if (!transc->op_buf_entry) {
+			op_buf_entry =
+				vcd_find_buffer_pool_entry(
+					&cctxt->out_buf_pool,
+					op_frm->vcd_frm.
+					virtual);
+		} else {
+			op_buf_entry = transc->op_buf_entry;
+		}
+
+		if (!op_buf_entry) {
+			VCD_MSG_ERROR("Invalid output buffer returned"
+				"from DDL");
+			vcd_handle_clnt_fatal(cctxt, op_frm->frm_trans_end);
+			rc = VCD_ERR_BAD_POINTER;
+		} else if (!op_buf_entry->in_use) {
+			VCD_MSG_ERROR("Bad output buffer 0x%p recvd from DDL",
+					  op_buf_entry->frame.virtual);
+			vcd_handle_clnt_fatal(cctxt, op_frm->frm_trans_end);
+			rc = VCD_ERR_BAD_POINTER;
+		} else {
+			op_buf_entry->in_use = false;
+			VCD_BUFFERPOOL_INUSE_DECREMENT(
+				cctxt->out_buf_pool.in_use);
+			VCD_MSG_LOW("outBufPool.InUse = %d",
+						cctxt->out_buf_pool.in_use);
+		}
+	}
+	VCD_FAILED_RETURN(rc, "Bad output buffer pointer");
+	op_frm->vcd_frm.time_stamp = transc->time_stamp;
+	op_frm->vcd_frm.ip_frm_tag = transc->ip_frm_tag;
+
+	if (transc->flags & VCD_FRAME_FLAG_EOSEQ)
+		op_frm->vcd_frm.flags |= VCD_FRAME_FLAG_EOSEQ;
+	else
+		op_frm->vcd_frm.flags &= ~VCD_FRAME_FLAG_EOSEQ;
+
+	if (cctxt->decoding)
+		op_frm->vcd_frm.frame = transc->frame;
+	else
+		transc->frame = op_frm->vcd_frm.frame;
+	transc->frame_done = true;
+
+	if (transc->input_done && transc->frame_done) {
+		time_stamp = transc->time_stamp;
+		vcd_release_trans_tbl_entry(transc);
+	}
+
+	if (status == VCD_ERR_INTRLCD_FIELD_DROP ||
+		(op_frm->vcd_frm.intrlcd_ip_frm_tag !=
+		VCD_FRAMETAG_INVALID &&
+		op_frm->vcd_frm.intrlcd_ip_frm_tag)) {
+		vcd_handle_frame_done_for_interlacing(cctxt, transc,
+							  op_frm, status);
+		if (status == VCD_ERR_INTRLCD_FIELD_DROP) {
+			cctxt->callback(VCD_EVT_IND_INFO_FIELD_DROPPED,
+				VCD_S_SUCCESS,
+				&time_stamp,
+				sizeof(time_stamp),
+				cctxt, cctxt->client_data);
+		}
+	}
+
+	if (status != VCD_ERR_INTRLCD_FIELD_DROP) {
+		cctxt->callback(event,
+			status,
+			&op_frm->vcd_frm,
+			sizeof(struct vcd_frame_data),
+			cctxt, cctxt->client_data);
+	}
+	return rc;
+}
+
+u32 vcd_handle_frame_done_in_eos(
+	struct vcd_clnt_ctxt *cctxt, void *payload, u32 status)
+{
+	struct ddl_frame_data_tag *frame =
+		(struct ddl_frame_data_tag *) payload;
+	u32 rc = VCD_S_SUCCESS;
+	VCD_MSG_LOW("%s\n", __func__);
+	rc = vcd_validate_io_done_pyld(cctxt, payload, status);
+	if (rc == VCD_ERR_CLIENT_FATAL)
+		vcd_handle_clnt_fatal(cctxt, frame->frm_trans_end);
+	VCD_FAILED_RETURN(rc, "Bad payload received");
+
+	if (cctxt->status.mask & VCD_EOS_PREV_VALID) {
+		rc = vcd_handle_frame_done(cctxt,
+			(void *)&cctxt->status.
+			eos_prev_op_frm,
+			VCD_EVT_RESP_OUTPUT_DONE,
+			cctxt->status.eos_prev_op_frm_status);
+		VCD_FAILED_RETURN(rc, "Failed: vcd_handle_frame_done");
+	}
+
+	cctxt->status.eos_prev_op_frm = *frame;
+	cctxt->status.eos_prev_op_frm_status = status;
+	cctxt->status.mask |= VCD_EOS_PREV_VALID;
+	return rc;
+}
+
+void vcd_handle_frame_done_for_interlacing(
+	struct vcd_clnt_ctxt *cctxt,
+	 struct vcd_transc *transc_ip1,
+	 struct ddl_frame_data_tag *op_frm, u32 status)
+{
+	struct vcd_transc *transc_ip2 =
+		(struct vcd_transc *)op_frm->\
+		vcd_frm.intrlcd_ip_frm_tag;
+
+	if (status == VCD_ERR_INTRLCD_FIELD_DROP) {
+		cctxt->status.int_field_cnt = 0;
+		return;
+	}
+
+	op_frm->vcd_frm.intrlcd_ip_frm_tag = transc_ip2->ip_frm_tag;
+
+	transc_ip2->frame_done = true;
+
+	if (transc_ip2->input_done && transc_ip2->frame_done)
+		vcd_release_trans_tbl_entry(transc_ip2);
+
+	if (!transc_ip1->frame || !transc_ip2->frame) {
+		VCD_MSG_ERROR("DDL didn't provided frame type");
+		return;
+	}
+}
+
+u32 vcd_handle_first_fill_output_buffer(
+	struct vcd_clnt_ctxt *cctxt,
+	struct vcd_frame_data *buffer,
+	u32 *handled)
+{
+	u32 rc = VCD_S_SUCCESS;
+	rc = vcd_check_if_buffer_req_met(cctxt, VCD_BUFFER_OUTPUT);
+	VCD_FAILED_RETURN(rc, "Output buffer requirements not met");
+	if (cctxt->out_buf_pool.q_len > 0) {
+		VCD_MSG_ERROR("Old output buffers were not flushed out");
+		return VCD_ERR_BAD_STATE;
+	}
+	cctxt->status.mask |= VCD_FIRST_OP_RCVD;
+	if (cctxt->sched_clnt_hdl)
+		rc = vcd_sched_suspend_resume_clnt(cctxt, true);
+	VCD_FAILED_RETURN(rc, "Failed: vcd_sched_suspend_resume_clnt");
+	if (cctxt->decoding)
+		rc = vcd_handle_first_fill_output_buffer_for_dec(
+			cctxt, buffer, handled);
+	else
+		rc = vcd_handle_first_fill_output_buffer_for_enc(
+			cctxt, buffer, handled);
+	return rc;
+}
+
+u32 vcd_handle_first_fill_output_buffer_for_enc(
+	struct vcd_clnt_ctxt *cctxt,
+	struct vcd_frame_data *frm_entry,
+	u32 *handled)
+{
+	u32 rc, seqhdr_present = 0;
+	struct vcd_property_hdr prop_hdr;
+	struct vcd_sequence_hdr seq_hdr;
+	struct vcd_property_sps_pps_for_idr_enable idr_enable;
+	struct vcd_property_codec codec;
+	*handled = true;
+	prop_hdr.prop_id = DDL_I_SEQHDR_PRESENT;
+	prop_hdr.sz = sizeof(seqhdr_present);
+	rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr, &seqhdr_present);
+	VCD_FAILED_RETURN(rc, "Failed: DDL_I_SEQHDR_PRESENT");
+	if (!seqhdr_present) {
+		*handled = false;
+		return VCD_S_SUCCESS;
+	}
+
+	prop_hdr.prop_id = VCD_I_CODEC;
+	prop_hdr.sz = sizeof(struct vcd_property_codec);
+	rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr, &codec);
+	if (!VCD_FAILED(rc)) {
+		if (codec.codec != VCD_CODEC_H263) {
+			/*
+			 * The seq. header is stored in a seperate internal
+			 * buffer and is memcopied into the output buffer
+			 * that we provide.  In secure sessions, we aren't
+			 * allowed to touch these buffers.  In these cases
+			 * seq. headers are returned to client as part of
+			 * I-frames. So for secure session, just return
+			 * empty buffer.
+			 */
+			if (!cctxt->secure) {
+				prop_hdr.prop_id = VCD_I_SEQ_HEADER;
+				prop_hdr.sz = sizeof(struct vcd_sequence_hdr);
+				seq_hdr.sequence_header = frm_entry->virtual;
+				seq_hdr.sequence_header_len =
+					frm_entry->alloc_len;
+				rc = ddl_get_property(cctxt->ddl_handle,
+						&prop_hdr, &seq_hdr);
+				if (!VCD_FAILED(rc)) {
+					frm_entry->data_len =
+						seq_hdr.sequence_header_len;
+					frm_entry->time_stamp = 0;
+					frm_entry->flags |=
+						VCD_FRAME_FLAG_CODECCONFIG;
+				} else
+					VCD_MSG_ERROR("rc = 0x%x. Failed:"
+							"ddl_get_property: VCD_I_SEQ_HEADER",
+							rc);
+			} else {
+				/*
+				 * First check that the proper props are enabled
+				 * so  client can get the proper info eventually
+				 */
+				prop_hdr.prop_id = VCD_I_ENABLE_SPS_PPS_FOR_IDR;
+				prop_hdr.sz = sizeof(idr_enable);
+				rc = ddl_get_property(cctxt->ddl_handle,
+						&prop_hdr, &idr_enable);
+				if (!VCD_FAILED(rc)) {
+					if (!idr_enable.
+						sps_pps_for_idr_enable_flag) {
+						VCD_MSG_ERROR("SPS/PPS per IDR "
+							"needs to be enabled");
+						rc = VCD_ERR_BAD_STATE;
+					} else {
+						/* Send zero len frame */
+						frm_entry->data_len = 0;
+						frm_entry->time_stamp = 0;
+						frm_entry->flags = 0;
+					}
+				}
+
+			}
+
+			if (!VCD_FAILED(rc))
+				cctxt->callback(VCD_EVT_RESP_OUTPUT_DONE,
+						VCD_S_SUCCESS, frm_entry,
+						sizeof(struct vcd_frame_data),
+						cctxt,
+						cctxt->client_data);
+		} else
+			VCD_MSG_LOW("Codec Type is H.263\n");
+	} else
+		VCD_MSG_ERROR(
+			"rc = 0x%x. Failed: ddl_get_property:VCD_I_CODEC",
+			rc);
+	return rc;
+}
+
+u32 vcd_handle_first_fill_output_buffer_for_dec(
+	struct vcd_clnt_ctxt *cctxt,
+	struct vcd_frame_data *frm_entry,
+	u32 *handled)
+{
+	u32 rc;
+	struct vcd_property_hdr prop_hdr;
+	struct vcd_buffer_pool *out_buf_pool;
+	struct ddl_property_dec_pic_buffers dpb;
+	struct ddl_frame_data_tag *dpb_list;
+	u8 i;
+
+	(void)frm_entry;
+	*handled = true;
+	prop_hdr.prop_id = DDL_I_DPB;
+	prop_hdr.sz = sizeof(dpb);
+	out_buf_pool = &cctxt->out_buf_pool;
+
+	dpb_list = (struct ddl_frame_data_tag *)
+		kmalloc((sizeof(struct ddl_frame_data_tag) *
+		out_buf_pool->count), GFP_KERNEL);
+
+	if (!dpb_list) {
+		VCD_MSG_ERROR("Memory allocation failure");
+		return VCD_ERR_ALLOC_FAIL;
+	}
+
+	for (i = 1; i <= out_buf_pool->count; i++)
+		dpb_list[i - 1].vcd_frm = out_buf_pool->entries[i].frame;
+
+	dpb.dec_pic_buffers = dpb_list;
+	dpb.no_of_dec_pic_buf = out_buf_pool->count;
+	rc = ddl_set_property(cctxt->ddl_handle, &prop_hdr, &dpb);
+
+	kfree(dpb_list);
+	*handled = false;
+
+	return VCD_S_SUCCESS;
+}
+
+void vcd_handle_eos_trans_end(struct vcd_clnt_ctxt *cctxt)
+{
+	u32 rc = VCD_S_SUCCESS;
+	if (cctxt->status.mask & VCD_EOS_PREV_VALID) {
+		rc = vcd_handle_frame_done(cctxt,
+			(void *)&cctxt->status.eos_prev_op_frm,
+			VCD_EVT_RESP_OUTPUT_DONE,
+			cctxt->status.eos_prev_op_frm_status);
+		cctxt->status.mask &= ~VCD_EOS_PREV_VALID;
+	}
+	if (VCD_FAILED(rc))
+		return;
+
+	if (cctxt->status.mask & VCD_FLUSH_ALL)
+		vcd_process_pending_flush_in_eos(cctxt);
+
+	if (cctxt->status.mask & VCD_STOP_PENDING)
+		vcd_process_pending_stop_in_eos(cctxt);
+	else {
+		vcd_do_client_state_transition(cctxt,
+			VCD_CLIENT_STATE_RUN,
+			CLIENT_STATE_EVENT_NUMBER(clnt_cb));
+	}
+}
+
+void vcd_handle_eos_done(struct vcd_clnt_ctxt *cctxt,
+	 struct vcd_transc *transc, u32 status)
+{
+	struct vcd_frame_data  vcd_frm;
+	u32 rc = VCD_S_SUCCESS, sent_eos_frm = false;
+	VCD_MSG_LOW("vcd_handle_eos_done:");
+
+	if (VCD_FAILED(status))
+		VCD_MSG_ERROR("EOS DONE returned error = 0x%x", status);
+
+	if (cctxt->status.mask & VCD_EOS_PREV_VALID) {
+		cctxt->status.eos_prev_op_frm.vcd_frm.flags |=
+			VCD_FRAME_FLAG_EOS;
+
+		rc = vcd_handle_frame_done(cctxt,
+						(void *)&cctxt->status.
+						eos_prev_op_frm,
+						VCD_EVT_RESP_OUTPUT_DONE,
+						cctxt->status.
+							eos_prev_op_frm_status);
+		cctxt->status.mask &= ~VCD_EOS_PREV_VALID;
+		if (!VCD_FAILED(rc) &&
+			cctxt->status.eos_prev_op_frm_status !=
+				VCD_ERR_INTRLCD_FIELD_DROP)
+			sent_eos_frm = true;
+	}
+	if (!sent_eos_frm) {
+		if (transc->ip_buf_entry) {
+			transc->ip_buf_entry->frame.ip_frm_tag =
+				transc->ip_frm_tag;
+
+			vcd_send_frame_done_in_eos(cctxt,
+				&transc->ip_buf_entry->frame, false);
+		} else {
+			memset(&vcd_frm, 0, sizeof(struct vcd_frame_data));
+			vcd_frm.ip_frm_tag = transc->ip_frm_tag;
+			vcd_frm.time_stamp = transc->time_stamp;
+			vcd_frm.flags = VCD_FRAME_FLAG_EOS;
+			vcd_send_frame_done_in_eos(cctxt, &vcd_frm, true);
+		}
+	}
+	if (VCD_FAILED(rc))
+		return;
+	if (transc->ip_buf_entry) {
+		if (transc->ip_buf_entry->frame.virtual) {
+			transc->ip_buf_entry->frame.ip_frm_tag =
+				transc->ip_frm_tag;
+			cctxt->callback(VCD_EVT_RESP_INPUT_DONE,
+					  VCD_S_SUCCESS,
+					  &transc->ip_buf_entry->frame,
+					  sizeof(struct vcd_frame_data),
+					  cctxt, cctxt->client_data);
+		}
+		transc->ip_buf_entry->in_use = false;
+		VCD_BUFFERPOOL_INUSE_DECREMENT(cctxt->in_buf_pool.in_use);
+		transc->ip_buf_entry = NULL;
+		if (cctxt->status.frame_submitted)
+			cctxt->status.frame_submitted--;
+		else
+			cctxt->status.frame_delayed--;
+	}
+
+	vcd_release_trans_tbl_entry(transc);
+	if (cctxt->status.mask & VCD_FLUSH_ALL)
+		vcd_process_pending_flush_in_eos(cctxt);
+
+	if (cctxt->status.mask & VCD_STOP_PENDING) {
+		vcd_process_pending_stop_in_eos(cctxt);
+	} else if (!(cctxt->status.mask & VCD_EOS_WAIT_OP_BUF)) {
+		vcd_do_client_state_transition(cctxt,
+						   VCD_CLIENT_STATE_RUN,
+						   CLIENT_STATE_EVENT_NUMBER
+						   (clnt_cb));
+	}
+}
+
+void vcd_handle_start_done(struct vcd_clnt_ctxt *cctxt,
+	struct vcd_transc *transc, u32 status)
+{
+	cctxt->status.cmd_submitted--;
+	vcd_mark_command_channel(cctxt->dev_ctxt, transc);
+
+	if (!VCD_FAILED(status)) {
+		cctxt->callback(VCD_EVT_RESP_START, status, NULL,
+			0, cctxt,	cctxt->client_data);
+
+		vcd_do_client_state_transition(cctxt,
+			VCD_CLIENT_STATE_RUN,
+			CLIENT_STATE_EVENT_NUMBER(clnt_cb));
+	} else {
+		VCD_MSG_ERROR("ddl callback returned failure."
+			"status = 0x%x", status);
+		vcd_handle_err_in_starting(cctxt, status);
+	}
+}
+
+void vcd_handle_stop_done(struct vcd_clnt_ctxt *cctxt,
+	struct vcd_transc *transc, u32 status)
+{
+
+	VCD_MSG_LOW("vcd_handle_stop_done:");
+	cctxt->status.cmd_submitted--;
+	vcd_mark_command_channel(cctxt->dev_ctxt, transc);
+
+	if (!VCD_FAILED(status)) {
+		vcd_do_client_state_transition(cctxt,
+			VCD_CLIENT_STATE_OPEN,
+			CLIENT_STATE_EVENT_NUMBER(clnt_cb));
+	} else {
+		VCD_MSG_FATAL("STOP_DONE returned error = 0x%x", status);
+		status = VCD_ERR_HW_FATAL;
+		vcd_handle_device_err_fatal(cctxt->dev_ctxt, cctxt);
+		vcd_do_client_state_transition(cctxt,
+			VCD_CLIENT_STATE_INVALID,
+			CLIENT_STATE_EVENT_NUMBER(clnt_cb));
+	}
+
+	cctxt->callback(VCD_EVT_RESP_STOP, status, NULL, 0, cctxt,
+					  cctxt->client_data);
+
+	memset(&cctxt->status, 0, sizeof(struct vcd_clnt_status));
+}
+
+void vcd_handle_stop_done_in_starting(struct vcd_clnt_ctxt
+	*cctxt, struct vcd_transc *transc, u32 status)
+{
+	VCD_MSG_LOW("vcd_handle_stop_done_in_starting:");
+	cctxt->status.cmd_submitted--;
+	vcd_mark_command_channel(cctxt->dev_ctxt, transc);
+	if (!VCD_FAILED(status)) {
+		cctxt->callback(VCD_EVT_RESP_START, cctxt->status.
+			last_err, NULL, 0, cctxt, cctxt->client_data);
+		vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_OPEN,
+			   CLIENT_STATE_EVENT_NUMBER(clnt_cb));
+	} else {
+		VCD_MSG_FATAL("VCD Cleanup: STOP_DONE returned error "
+			"= 0x%x", status);
+		vcd_handle_err_fatal(cctxt, VCD_EVT_RESP_START,
+			VCD_ERR_HW_FATAL);
+	}
+}
+
+void vcd_handle_stop_done_in_invalid(struct vcd_clnt_ctxt *cctxt,
+	struct vcd_transc *transc, u32 status)
+{
+	u32 rc;
+	VCD_MSG_LOW("vcd_handle_stop_done_in_invalid:");
+
+	cctxt->status.cmd_submitted--;
+	vcd_mark_command_channel(cctxt->dev_ctxt, transc);
+
+	if (!VCD_FAILED(status)) {
+		vcd_client_cmd_flush_and_en_q(cctxt, VCD_CMD_CLIENT_CLOSE);
+		if (cctxt->status.frame_submitted) {
+			vcd_release_multiple_frame_channels(cctxt->dev_ctxt,
+			cctxt->status.frame_submitted);
+
+			cctxt->status.frame_submitted = 0;
+			cctxt->status.frame_delayed = 0;
+		}
+		if (cctxt->status.cmd_submitted) {
+			vcd_release_multiple_command_channels(
+				cctxt->dev_ctxt,
+				cctxt->status.cmd_submitted);
+			cctxt->status.cmd_submitted = 0;
+		}
+	} else {
+		VCD_MSG_FATAL("VCD Cleanup: STOP_DONE returned error "
+			"= 0x%x", status);
+		vcd_handle_device_err_fatal(cctxt->dev_ctxt, cctxt);
+		cctxt->status.mask &= ~VCD_CLEANING_UP;
+	}
+	vcd_flush_buffers_in_err_fatal(cctxt);
+	VCD_MSG_HIGH("VCD cleanup: All buffers are returned");
+	if (cctxt->status.mask & VCD_STOP_PENDING) {
+		cctxt->callback(VCD_EVT_RESP_STOP, VCD_S_SUCCESS, NULL, 0,
+			cctxt, cctxt->client_data);
+		cctxt->status.mask &= ~VCD_STOP_PENDING;
+	}
+	rc = vcd_power_event(cctxt->dev_ctxt, cctxt,
+						  VCD_EVT_PWR_CLNT_ERRFATAL);
+	if (VCD_FAILED(rc))
+		VCD_MSG_ERROR("VCD_EVT_PWR_CLNT_ERRFATAL failed");
+	if (!(cctxt->status.mask & VCD_CLEANING_UP) &&
+		cctxt->status.mask & VCD_CLOSE_PENDING) {
+		vcd_destroy_client_context(cctxt);
+		vcd_handle_for_last_clnt_close(cctxt->dev_ctxt, false);
+	}
+}
+
+u32 vcd_handle_input_frame(
+	struct vcd_clnt_ctxt *cctxt,
+	 struct vcd_frame_data *input_frame)
+{
+	struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt;
+	struct vcd_buffer_entry *buf_entry, *orig_frame;
+	struct vcd_frame_data *frm_entry;
+	u32 rc = VCD_S_SUCCESS;
+	u32 eos_handled = false;
+
+	VCD_MSG_LOW("vcd_handle_input_frame:");
+
+	VCD_MSG_LOW("input buffer: addr=(0x%p), sz=(%d), len=(%d)",
+			input_frame->virtual, input_frame->alloc_len,
+			input_frame->data_len);
+
+	if (!input_frame->virtual &&
+		!(input_frame->flags & VCD_FRAME_FLAG_EOS)) {
+		VCD_MSG_ERROR("Bad frame ptr/len/EOS combination");
+		return VCD_ERR_ILLEGAL_PARM;
+	}
+
+
+	if (!input_frame->data_len &&
+		!(input_frame->flags & VCD_FRAME_FLAG_EOS)) {
+		VCD_MSG_MED("data_len = 0, returning INPUT DONE");
+		cctxt->callback(VCD_EVT_RESP_INPUT_DONE,
+				  VCD_ERR_INPUT_NOT_PROCESSED,
+				  input_frame,
+				  sizeof(struct vcd_frame_data),
+				  cctxt, cctxt->client_data);
+		return VCD_S_SUCCESS;
+	}
+
+	if (!(cctxt->status.mask & VCD_FIRST_IP_RCVD)) {
+		if (cctxt->decoding)
+			rc = vcd_handle_first_decode_frame(cctxt);
+
+		if (!VCD_FAILED(rc)) {
+			cctxt->status.first_ts = input_frame->time_stamp;
+			cctxt->status.prev_ts = cctxt->status.first_ts;
+
+			cctxt->status.mask |= VCD_FIRST_IP_RCVD;
+
+			(void)vcd_power_event(cctxt->dev_ctxt,
+						  cctxt,
+						  VCD_EVT_PWR_CLNT_FIRST_FRAME);
+		}
+	}
+	VCD_FAILED_RETURN(rc, "Failed: First frame handling");
+
+	orig_frame = vcd_find_buffer_pool_entry(&cctxt->in_buf_pool,
+						 input_frame->virtual);
+	if (!orig_frame) {
+		VCD_MSG_ERROR("Bad buffer addr: %p", input_frame->virtual);
+		return VCD_ERR_FAIL;
+	}
+
+	if (orig_frame->in_use) {
+		/*
+		 * This path only allowed for enc., dec. not allowed
+		 * to queue same buffer multiple times
+		 */
+		if (cctxt->decoding) {
+			VCD_MSG_ERROR("An inuse input frame is being "
+					"re-queued to scheduler");
+			return VCD_ERR_FAIL;
+		}
+
+		buf_entry = kzalloc(sizeof(*buf_entry), GFP_KERNEL);
+		if (!buf_entry) {
+			VCD_MSG_ERROR("Unable to alloc memory");
+			return VCD_ERR_FAIL;
+		}
+
+		INIT_LIST_HEAD(&buf_entry->sched_list);
+		/*
+		 * Pre-emptively poisoning this, as these dupe entries
+		 * shouldn't get added to any list
+		 */
+		INIT_LIST_HEAD(&buf_entry->list);
+		buf_entry->list.next = LIST_POISON1;
+		buf_entry->list.prev = LIST_POISON2;
+
+		buf_entry->valid = orig_frame->valid;
+		buf_entry->alloc = orig_frame->alloc;
+		buf_entry->virtual = orig_frame->virtual;
+		buf_entry->physical = orig_frame->physical;
+		buf_entry->sz = orig_frame->sz;
+		buf_entry->allocated = orig_frame->allocated;
+		buf_entry->in_use = 1; /* meaningless for the dupe buffers */
+		buf_entry->frame = orig_frame->frame;
+	} else
+		buf_entry = orig_frame;
+
+	if (input_frame->alloc_len > buf_entry->sz) {
+		VCD_MSG_ERROR("Bad buffer Alloc_len %d, Actual sz=%d",
+			input_frame->alloc_len, buf_entry->sz);
+
+		return VCD_ERR_ILLEGAL_PARM;
+	}
+
+	frm_entry = &buf_entry->frame;
+
+	*frm_entry = *input_frame;
+	frm_entry->physical = buf_entry->physical;
+
+	if (input_frame->flags & VCD_FRAME_FLAG_EOS) {
+		rc = vcd_handle_recvd_eos(cctxt, input_frame,
+					  &eos_handled);
+	}
+
+	if (VCD_FAILED(rc) || eos_handled) {
+		VCD_MSG_HIGH("rc = 0x%x, eos_handled = %d", rc,
+				 eos_handled);
+
+		return rc;
+	}
+	rc = vcd_sched_queue_buffer(
+		cctxt->sched_clnt_hdl, buf_entry, true);
+	VCD_FAILED_RETURN(rc, "Failed: vcd_sched_queue_buffer");
+
+	orig_frame->in_use++;
+	cctxt->in_buf_pool.in_use++;
+	vcd_try_submit_frame(dev_ctxt);
+	return rc;
+}
+
+void vcd_release_all_clnt_frm_transc(struct vcd_clnt_ctxt *cctxt)
+{
+	struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt;
+	u32 i, cntr = 0;
+	VCD_MSG_LOW("vcd_release_all_clnt_frm_transc:");
+	for (i = 0; i < dev_ctxt->trans_tbl_size; i++) {
+		if (dev_ctxt->trans_tbl[i].in_use &&
+			cctxt == dev_ctxt->trans_tbl[i].cctxt) {
+			if (dev_ctxt->trans_tbl[i].
+				type == VCD_CMD_CODE_FRAME ||
+				dev_ctxt->trans_tbl[i].
+				type == VCD_CMD_NONE) {
+				vcd_release_trans_tbl_entry(&dev_ctxt->
+								trans_tbl[i]);
+			} else {
+				VCD_MSG_LOW("vcd_transaction in use type(%u)",
+					dev_ctxt->trans_tbl[i].type);
+				cntr++;
+			}
+		}
+	}
+	if (cntr)
+		VCD_MSG_ERROR("vcd_transactions still in use: (%d)", cntr);
+}
+
+void vcd_release_all_clnt_transc(struct vcd_clnt_ctxt *cctxt)
+{
+	struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt;
+	u32 i;
+
+	VCD_MSG_LOW("vcd_release_all_clnt_transc:");
+
+	for (i = 0; i < dev_ctxt->trans_tbl_size; i++) {
+		if (dev_ctxt->trans_tbl[i].in_use &&
+			cctxt == dev_ctxt->trans_tbl[i].cctxt) {
+				vcd_release_trans_tbl_entry(
+					&dev_ctxt->trans_tbl[i]);
+		}
+	}
+}
+
+void vcd_send_flush_done(struct vcd_clnt_ctxt *cctxt, u32 status)
+{
+	VCD_MSG_LOW("vcd_send_flush_done:");
+
+	if (cctxt->status.mask & VCD_FLUSH_INPUT) {
+		cctxt->callback(VCD_EVT_RESP_FLUSH_INPUT_DONE,
+			status, NULL, 0, cctxt, cctxt->client_data);
+		cctxt->status.mask &= ~VCD_FLUSH_INPUT;
+	}
+
+	if (cctxt->status.mask & VCD_FLUSH_OUTPUT) {
+		cctxt->callback(VCD_EVT_RESP_FLUSH_OUTPUT_DONE,
+			status, NULL, 0, cctxt, cctxt->client_data);
+		cctxt->status.mask &= ~VCD_FLUSH_OUTPUT;
+	}
+}
+
+u32 vcd_store_seq_hdr(
+	struct vcd_clnt_ctxt *cctxt,
+	 struct vcd_sequence_hdr *seq_hdr)
+{
+	u32 rc;
+	struct vcd_property_hdr prop_hdr;
+	u32 align;
+	u8 *virtual_aligned;
+	u32 addr;
+	int ret = 0;
+
+	if (!seq_hdr->sequence_header_len
+		|| !seq_hdr->sequence_header) {
+		VCD_MSG_ERROR("Bad seq hdr");
+
+		return VCD_ERR_BAD_POINTER;
+	}
+
+	if (cctxt->seq_hdr.sequence_header) {
+		VCD_MSG_HIGH("Old seq hdr detected");
+
+		vcd_pmem_free(cctxt->seq_hdr.sequence_header,
+				  cctxt->seq_hdr_phy_addr, cctxt);
+		cctxt->seq_hdr.sequence_header = NULL;
+	}
+
+	cctxt->seq_hdr.sequence_header_len =
+		seq_hdr->sequence_header_len;
+
+	prop_hdr.prop_id = DDL_I_SEQHDR_ALIGN_BYTES;
+	prop_hdr.sz = sizeof(u32);
+
+	rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr, &align);
+
+	VCD_FAILED_RETURN(rc,
+			  "Failed: ddl_get_property DDL_I_SEQHDR_ALIGN_BYTES");
+
+	VCD_MSG_MED("Seq hdr alignment bytes = %d", align);
+
+	ret = vcd_pmem_alloc(cctxt->seq_hdr.sequence_header_len + align +
+				 VCD_SEQ_HDR_PADDING_BYTES,
+				 &(cctxt->seq_hdr.sequence_header),
+				 &(cctxt->seq_hdr_phy_addr), cctxt);
+
+	if (ret < 0) {
+		VCD_MSG_ERROR("Seq hdr allocation failed");
+
+		return VCD_ERR_ALLOC_FAIL;
+	}
+
+	if (!cctxt->seq_hdr_phy_addr) {
+		VCD_MSG_ERROR("Couldn't get physical address");
+
+		return VCD_ERR_BAD_POINTER;
+	}
+
+	if (align > 0) {
+		addr = (u32) cctxt->seq_hdr_phy_addr;
+		addr += align;
+		addr -= (addr % align);
+		virtual_aligned = cctxt->seq_hdr.sequence_header;
+		virtual_aligned += (u32) (addr -
+			(u32) cctxt->seq_hdr_phy_addr);
+		cctxt->seq_hdr_phy_addr = (u8 *) addr;
+	} else {
+		virtual_aligned = cctxt->seq_hdr.sequence_header;
+	}
+
+	memcpy(virtual_aligned, seq_hdr->sequence_header,
+		seq_hdr->sequence_header_len);
+
+	return VCD_S_SUCCESS;
+}
+
+u32 vcd_set_frame_rate(
+	struct vcd_clnt_ctxt *cctxt,
+	 struct vcd_property_frame_rate *fps)
+{
+	u32 rc;
+	cctxt->frm_rate = *fps;
+	rc = vcd_update_clnt_perf_lvl(cctxt, &cctxt->frm_rate,
+					  cctxt->frm_p_units);
+	if (VCD_FAILED(rc)) {
+		VCD_MSG_ERROR("rc = 0x%x. Failed: vcd_update_clnt_perf_lvl",
+				  rc);
+	}
+	rc = vcd_sched_update_config(cctxt);
+	return rc;
+}
+
+u32 vcd_req_perf_level(
+	struct vcd_clnt_ctxt *cctxt,
+	 struct vcd_property_perf_level *perf_level)
+{
+	u32 rc;
+	u32 res_trk_perf_level;
+	if (!perf_level) {
+		VCD_MSG_ERROR("Invalid parameters\n");
+		return -EINVAL;
+	}
+	res_trk_perf_level = get_res_trk_perf_level(perf_level->level);
+	if (res_trk_perf_level < 0) {
+		rc = -ENOTSUPP;
+		goto perf_level_not_supp;
+	}
+	rc = vcd_set_perf_level(cctxt->dev_ctxt, res_trk_perf_level);
+	if (!rc) {
+		cctxt->reqd_perf_lvl = res_trk_perf_level;
+		cctxt->perf_set_by_client = 1;
+	}
+perf_level_not_supp:
+	return rc;
+}
+
+u32 vcd_set_frame_size(
+	struct vcd_clnt_ctxt *cctxt,
+	 struct vcd_property_frame_size *frm_size)
+{
+	struct vcd_property_hdr prop_hdr;
+	u32 rc;
+	u32 frm_p_units;
+	(void)frm_size;
+	if (res_trk_get_disable_fullhd() && frm_size &&
+		(frm_size->width * frm_size->height > 1280 * 720)) {
+		VCD_MSG_ERROR("Frame size = %dX%d greater than 1280X720 not"
+			"supported", frm_size->width, frm_size->height);
+		return VCD_ERR_FAIL;
+	}
+	prop_hdr.prop_id = DDL_I_FRAME_PROC_UNITS;
+	prop_hdr.sz = sizeof(frm_p_units);
+	rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr, &frm_p_units);
+	VCD_FAILED_RETURN(rc, "Failed: Get DDL_I_FRAME_PROC_UNITS");
+
+	cctxt->frm_p_units = frm_p_units;
+
+	rc = vcd_update_clnt_perf_lvl(cctxt, &cctxt->frm_rate,
+					  frm_p_units);
+	if (VCD_FAILED(rc)) {
+		VCD_MSG_ERROR("rc = 0x%x. Failed: vcd_update_clnt_perf_lvl",
+				  rc);
+	}
+	return rc;
+}
+
+void vcd_process_pending_flush_in_eos(struct vcd_clnt_ctxt *cctxt)
+{
+	u32 rc = VCD_S_SUCCESS;
+	VCD_MSG_HIGH("Buffer flush is pending");
+	rc = vcd_flush_buffers(cctxt, cctxt->status.mask & VCD_FLUSH_ALL);
+	if (VCD_FAILED(rc))
+		VCD_MSG_ERROR("rc = 0x%x. Failed: vcd_flush_buffers", rc);
+	cctxt->status.mask &= ~VCD_EOS_WAIT_OP_BUF;
+	vcd_send_flush_done(cctxt, VCD_S_SUCCESS);
+}
+
+void vcd_process_pending_stop_in_eos(struct vcd_clnt_ctxt *cctxt)
+{
+	u32 rc = VCD_S_SUCCESS;
+	rc = vcd_flush_buffers(cctxt, VCD_FLUSH_ALL);
+	if (VCD_FAILED(rc))
+		VCD_MSG_ERROR("rc = 0x%x. Failed: vcd_flush_buffers", rc);
+	VCD_MSG_HIGH("All buffers are returned. Enqueuing stop cmd");
+	vcd_client_cmd_flush_and_en_q(cctxt, VCD_CMD_CODEC_STOP);
+	cctxt->status.mask &= ~VCD_STOP_PENDING;
+	vcd_do_client_state_transition(cctxt,
+					   VCD_CLIENT_STATE_STOPPING,
+					   CLIENT_STATE_EVENT_NUMBER(stop));
+}
+
+u32 vcd_calculate_frame_delta(
+	struct vcd_clnt_ctxt *cctxt,
+	 struct vcd_frame_data *frame)
+{
+	u32 frm_delta;
+	u64 temp, max = ~((u64)0);
+
+	if (cctxt->time_frame_delta)
+		temp = cctxt->time_frame_delta;
+	else if (frame->time_stamp >= cctxt->status.prev_ts)
+		temp = frame->time_stamp - cctxt->status.prev_ts;
+	else
+		temp = (max - cctxt->status.prev_ts) +
+			frame->time_stamp;
+
+	VCD_MSG_LOW("Curr_ts=%lld  Prev_ts=%lld Diff=%llu\n",
+			frame->time_stamp, cctxt->status.prev_ts, temp);
+
+	temp *= cctxt->time_resoln;
+	(void)do_div(temp, VCD_TIMESTAMP_RESOLUTION);
+	frm_delta = temp;
+	cctxt->status.time_elapsed += frm_delta;
+
+	temp = (cctxt->status.time_elapsed * VCD_TIMESTAMP_RESOLUTION);
+	(void)do_div(temp, cctxt->time_resoln);
+	cctxt->status.prev_ts = cctxt->status.first_ts + temp;
+
+	VCD_MSG_LOW("Time_elapsed=%llu, Drift=%llu, new Prev_ts=%lld",
+			cctxt->status.time_elapsed, temp,
+			cctxt->status.prev_ts);
+
+	return frm_delta;
+}
+
+struct vcd_buffer_entry *vcd_check_fill_output_buffer
+	(struct vcd_clnt_ctxt *cctxt,
+	 struct vcd_frame_data *buffer) {
+	struct vcd_buffer_pool *buf_pool = &cctxt->out_buf_pool;
+	struct vcd_buffer_entry *buf_entry;
+
+	if (!buf_pool->entries) {
+		VCD_MSG_ERROR("Buffers not set or allocated yet");
+
+		return NULL;
+	}
+
+	if (!buffer->virtual) {
+		VCD_MSG_ERROR("NULL buffer address provided");
+		return NULL;
+	}
+
+	buf_entry =
+		vcd_find_buffer_pool_entry(buf_pool, buffer->virtual);
+	if (!buf_entry) {
+		VCD_MSG_ERROR("Unrecognized buffer address provided = %p",
+				  buffer->virtual);
+		return NULL;
+	}
+
+	if (buf_entry->in_use) {
+		VCD_MSG_ERROR
+			("An inuse output frame is being provided for reuse");
+		return NULL;
+	}
+
+	if ((buffer->alloc_len < buf_pool->buf_req.sz ||
+		 buffer->alloc_len > buf_entry->sz) &&
+		 !(cctxt->status.mask & VCD_IN_RECONFIG)) {
+		VCD_MSG_ERROR
+			("Bad buffer Alloc_len = %d, Actual sz = %d, "
+			 " Min sz = %u",
+			 buffer->alloc_len, buf_entry->sz,
+			 buf_pool->buf_req.sz);
+		return NULL;
+	}
+
+	return buf_entry;
+}
+
+void vcd_handle_ind_hw_err_fatal(struct vcd_clnt_ctxt *cctxt,
+	u32 event, u32 status)
+{
+	if (cctxt->status.frame_submitted) {
+		cctxt->status.frame_submitted--;
+		vcd_mark_frame_channel(cctxt->dev_ctxt);
+	}
+	vcd_handle_err_fatal(cctxt, event, status);
+}
+
+void vcd_handle_err_fatal(struct vcd_clnt_ctxt *cctxt, u32 event,
+						  u32 status)
+{
+	VCD_MSG_LOW("vcd_handle_err_fatal: event=%x, err=%x", event, status);
+	if (!VCD_FAILED_FATAL(status))
+		return;
+
+	if (VCD_FAILED_DEVICE_FATAL(status)) {
+		vcd_clnt_handle_device_err_fatal(cctxt, event);
+		vcd_handle_device_err_fatal(cctxt->dev_ctxt, cctxt);
+	} else if (VCD_FAILED_CLIENT_FATAL(status)) {
+		cctxt->status.last_evt = event;
+		cctxt->callback(event, VCD_ERR_HW_FATAL, NULL, 0, cctxt,
+						   cctxt->client_data);
+		cctxt->status.mask |= VCD_CLEANING_UP;
+		vcd_client_cmd_flush_and_en_q(cctxt, VCD_CMD_CODEC_STOP);
+		vcd_do_client_state_transition(cctxt,
+			VCD_CLIENT_STATE_INVALID,
+			CLIENT_STATE_EVENT_NUMBER(clnt_cb));
+	}
+}
+
+void vcd_handle_err_in_starting(struct vcd_clnt_ctxt *cctxt,
+								u32 status)
+{
+	VCD_MSG_LOW("\n vcd_handle_err_in_starting:");
+	if (VCD_FAILED_FATAL(status)) {
+		vcd_handle_err_fatal(cctxt, VCD_EVT_RESP_START, status);
+	} else {
+		cctxt->status.last_err = status;
+		VCD_MSG_HIGH("\n VCD cleanup: Enqueuing stop cmd");
+		vcd_client_cmd_flush_and_en_q(cctxt, VCD_CMD_CODEC_STOP);
+	}
+}
+
+void vcd_handle_trans_pending(struct vcd_clnt_ctxt *cctxt)
+{
+	if (!cctxt->status.frame_submitted) {
+		VCD_MSG_ERROR("Transaction pending response was not expected");
+		return;
+	}
+	cctxt->status.frame_submitted--;
+	cctxt->status.frame_delayed++;
+	vcd_mark_frame_channel(cctxt->dev_ctxt);
+}
+void vcd_handle_submit_frame_failed(struct vcd_dev_ctxt
+	*dev_ctxt, struct vcd_transc *transc)
+{
+	struct vcd_clnt_ctxt *cctxt = transc->cctxt;
+	u32 rc;
+
+	vcd_mark_frame_channel(dev_ctxt);
+	vcd_release_trans_tbl_entry(transc);
+
+	vcd_handle_err_fatal(cctxt, VCD_EVT_IND_HWERRFATAL,
+		VCD_ERR_CLIENT_FATAL);
+
+	if (vcd_get_command_channel(dev_ctxt, &transc)) {
+		transc->type = VCD_CMD_CODEC_STOP;
+		transc->cctxt = cctxt;
+		rc = vcd_submit_cmd_sess_end(transc);
+		if (VCD_FAILED(rc))	{
+			vcd_release_command_channel(dev_ctxt, transc);
+			VCD_MSG_ERROR("rc = 0x%x. Failed: VCD_SubmitCmdSessEnd",
+				rc);
+		}
+	}
+}
+
+u32 vcd_check_if_buffer_req_met(struct vcd_clnt_ctxt *cctxt,
+	enum vcd_buffer_type buffer)
+{
+	struct vcd_property_hdr prop_hdr;
+	struct vcd_buffer_pool *buf_pool;
+	struct vcd_buffer_requirement buf_req;
+	u32 rc;
+	u8 i;
+
+	if (buffer == VCD_BUFFER_INPUT) {
+		prop_hdr.prop_id = DDL_I_INPUT_BUF_REQ;
+		buf_pool = &cctxt->in_buf_pool;
+	} else {
+		prop_hdr.prop_id = DDL_I_OUTPUT_BUF_REQ;
+		buf_pool = &cctxt->out_buf_pool;
+	}
+
+	prop_hdr.sz = sizeof(buf_req);
+	rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr, &buf_req);
+	VCD_FAILED_RETURN(rc, "Failed: ddl_GetProperty");
+
+	buf_pool->buf_req = buf_req;
+	if (buf_pool->count < buf_req.actual_count) {
+		VCD_MSG_ERROR("Buf requirement count not met");
+		return VCD_ERR_FAIL;
+	}
+
+	if (buf_pool->count > buf_req.actual_count)
+		buf_pool->count = buf_req.actual_count;
+
+	if (!buf_pool->entries ||
+	buf_pool->validated != buf_pool->count) {
+		VCD_MSG_ERROR("Buffer pool is not completely setup yet");
+		return VCD_ERR_BAD_STATE;
+	}
+	for (i = 1; (rc == VCD_S_SUCCESS && i <= buf_pool->count); i++) {
+		if (buf_pool->entries[i].sz <
+			buf_pool->buf_req.sz) {
+			VCD_MSG_ERROR(
+				"BufReq sz not met:\
+					addr=(0x%p) sz=%d ReqSize=%d",
+				buf_pool->entries[i].virtual,
+				buf_pool->entries[i].sz,
+				buf_pool->buf_req.sz);
+			rc = VCD_ERR_FAIL;
+		}
+	}
+	return rc;
+}
+
+u32 vcd_handle_ind_output_reconfig(
+	struct vcd_clnt_ctxt *cctxt, void* payload, u32 status)
+{
+	struct ddl_frame_data_tag *frame =
+		(struct ddl_frame_data_tag *)payload;
+	struct vcd_property_hdr prop_hdr;
+	u32 rc = VCD_S_SUCCESS;
+	struct vcd_buffer_pool *out_buf_pool;
+	struct vcd_buffer_requirement buf_req;
+
+	if (frame)
+		rc = vcd_handle_output_required(cctxt, payload, status);
+	VCD_FAILED_RETURN(rc, "Failed: vcd_handle_output_required in reconfig");
+	vcd_mark_frame_channel(cctxt->dev_ctxt);
+
+	rc = vcd_sched_suspend_resume_clnt(cctxt, false);
+	VCD_FAILED_RETURN(rc, "Failed: vcd_sched_suspend_resume_clnt");
+	out_buf_pool = &cctxt->out_buf_pool;
+	prop_hdr.prop_id = DDL_I_OUTPUT_BUF_REQ;
+	prop_hdr.sz = sizeof(buf_req);
+	rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr, &buf_req);
+	VCD_FAILED_RETURN(rc, "Failed: ddl_GetProperty");
+
+	out_buf_pool->buf_req = buf_req;
+
+	if (out_buf_pool->count < buf_req.actual_count) {
+		VCD_MSG_HIGH("Output buf requirement count increased");
+		out_buf_pool->count = buf_req.actual_count;
+	}
+
+	if (buf_req.actual_count > VCD_MAX_BUFFER_ENTRIES) {
+		VCD_MSG_ERROR("\n New act count exceeds Max count(32)");
+		return VCD_ERR_FAIL;
+	}
+
+	if (!VCD_FAILED(rc)) {
+		rc = vcd_set_frame_size(cctxt, NULL);
+		VCD_FAILED_RETURN(rc, "Failed: set_frame_size in reconfig");
+		cctxt->status.mask &= ~VCD_FIRST_OP_RCVD;
+		cctxt->status.mask |= VCD_IN_RECONFIG;
+		cctxt->callback(VCD_EVT_IND_OUTPUT_RECONFIG,
+			status, NULL, 0, cctxt,
+			cctxt->client_data);
+	}
+	return rc;
+}
+
+u32 vcd_handle_ind_output_reconfig_in_flushing(
+	struct vcd_clnt_ctxt *cctxt, void* payload, u32 status)
+{
+	u32 rc = VCD_S_SUCCESS;
+	if (cctxt->status.mask & VCD_FLUSH_INPUT && payload) {
+		(void)vcd_handle_input_done(cctxt, payload,
+		VCD_EVT_RESP_INPUT_FLUSHED, status);
+		payload = NULL;
+	}
+	rc = vcd_handle_ind_output_reconfig(cctxt, payload, status);
+	return rc;
+}
+
+u32 vcd_return_op_buffer_to_hw(struct vcd_clnt_ctxt *cctxt,
+	struct vcd_buffer_entry *buf_entry)
+{
+	u32 rc = VCD_S_SUCCESS;
+	struct vcd_frame_data *frm_entry = &buf_entry->frame;
+
+	VCD_MSG_LOW("vcd_return_op_buffer_to_hw in %d:",
+		    cctxt->clnt_state.state);
+	frm_entry->physical = buf_entry->physical;
+	frm_entry->ip_frm_tag = VCD_FRAMETAG_INVALID;
+	frm_entry->intrlcd_ip_frm_tag = VCD_FRAMETAG_INVALID;
+	frm_entry->data_len = 0;
+
+	if (cctxt->decoding) {
+		struct vcd_property_hdr Prop_hdr;
+		struct ddl_frame_data_tag ddl_frm;
+		Prop_hdr.prop_id = DDL_I_DPB_RELEASE;
+		Prop_hdr.sz =
+			sizeof(struct ddl_frame_data_tag);
+		memset(&ddl_frm, 0, sizeof(ddl_frm));
+		ddl_frm.vcd_frm = *frm_entry;
+		rc = ddl_set_property(cctxt->ddl_handle, &Prop_hdr,
+				      &ddl_frm);
+		if (VCD_FAILED(rc)) {
+			VCD_MSG_ERROR("Error returning output buffer to"
+					" HW. rc = 0x%x", rc);
+			buf_entry->in_use = false;
+		} else {
+			cctxt->out_buf_pool.in_use++;
+			buf_entry->in_use = true;
+		}
+	}
+	return rc;
+}
+
+void vcd_handle_clnt_fatal(struct vcd_clnt_ctxt *cctxt, u32 trans_end)
+{
+	if (trans_end)
+		vcd_mark_frame_channel(cctxt->dev_ctxt);
+	vcd_handle_err_fatal(cctxt,
+		VCD_EVT_IND_HWERRFATAL, VCD_ERR_CLIENT_FATAL);
+}
+
+void vcd_handle_clnt_fatal_input_done(struct vcd_clnt_ctxt *cctxt,
+	u32 trans_end)
+{
+	if (cctxt->status.frame_submitted > 0)
+		cctxt->status.frame_submitted--;
+	vcd_handle_clnt_fatal(cctxt, trans_end);
+}
+
+void vcd_handle_ind_info_output_reconfig(
+	struct vcd_clnt_ctxt *cctxt, u32 status)
+{
+	if (cctxt) {
+		cctxt->callback(VCD_EVT_IND_INFO_OUTPUT_RECONFIG, status, NULL,
+		 0, cctxt, cctxt->client_data);
+	}
+}
+
+u32 vcd_set_num_slices(struct vcd_clnt_ctxt *cctxt)
+{
+	struct vcd_property_hdr prop_hdr;
+	struct vcd_property_slice_delivery_info slice_delivery_info;
+	u32 rc = VCD_S_SUCCESS;
+	prop_hdr.prop_id = VCD_I_SLICE_DELIVERY_MODE;
+	prop_hdr.sz = prop_hdr.sz =
+		sizeof(struct vcd_property_slice_delivery_info);
+	rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr,
+				&slice_delivery_info);
+	VCD_FAILED_RETURN(rc, "Failed: Get VCD_I_SLICE_DELIVERY_MODE");
+	if (slice_delivery_info.enable) {
+		cctxt->num_slices = slice_delivery_info.num_slices;
+		VCD_MSG_LOW("%s slice delivery mode num_slices = %u\n",
+					__func__, cctxt->num_slices);
+	} else {
+		cctxt->num_slices = 1;
+	}
+	return rc;
+}
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_util.c b/drivers/video/msm/vidc/common/vcd/vcd_util.c
new file mode 100644
index 0000000..ba991f1
--- /dev/null
+++ b/drivers/video/msm/vidc/common/vcd/vcd_util.c
@@ -0,0 +1,106 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "vidc_type.h"
+#include "vcd_util.h"
+
+u32 vcd_critical_section_create(u32 **p_cs)
+{
+	struct mutex *lock;
+	if (!p_cs) {
+		VCD_MSG_ERROR("Bad critical section ptr");
+		return VCD_ERR_BAD_POINTER;
+	} else {
+		lock = kmalloc(sizeof(struct mutex), GFP_KERNEL);
+		if (!lock) {
+			VCD_MSG_ERROR("Failed: vcd_critical_section_create");
+			return VCD_ERR_ALLOC_FAIL;
+		}
+		mutex_init(lock);
+		*p_cs = (u32 *) lock;
+		return VCD_S_SUCCESS;
+	}
+}
+
+u32 vcd_critical_section_release(u32 *cs)
+{
+	struct mutex *lock = (struct mutex *)cs;
+	if (!lock) {
+		VCD_MSG_ERROR("Bad critical section object");
+		return VCD_ERR_BAD_POINTER;
+	}
+
+	mutex_destroy(lock);
+	kfree(cs);
+	return VCD_S_SUCCESS;
+}
+
+u32 vcd_critical_section_enter(u32 *cs)
+{
+	struct mutex *lock = (struct mutex *)cs;
+	if (!lock) {
+		VCD_MSG_ERROR("Bad critical section object");
+		return VCD_ERR_BAD_POINTER;
+	} else
+		mutex_lock(lock);
+
+	return VCD_S_SUCCESS;
+}
+
+u32 vcd_critical_section_leave(u32 *cs)
+{
+	struct mutex *lock = (struct mutex *)cs;
+
+	if (!lock) {
+		VCD_MSG_ERROR("Bad critical section object");
+
+		return VCD_ERR_BAD_POINTER;
+	} else
+		mutex_unlock(lock);
+
+	return VCD_S_SUCCESS;
+}
+
+int vcd_pmem_alloc(u32 size, u8 **kernel_vaddr, u8 **phy_addr)
+{
+	*phy_addr =
+	    (u8 *) pmem_kalloc(size, PMEM_MEMTYPE | PMEM_ALIGNMENT_4K);
+
+	if (!IS_ERR((void *)*phy_addr)) {
+
+		*kernel_vaddr = ioremap((unsigned long)*phy_addr, size);
+
+		if (!*kernel_vaddr) {
+			pr_err("%s: could not ioremap in kernel pmem buffers\n",
+			       __func__);
+			pmem_kfree((s32) *phy_addr);
+			return -ENOMEM;
+		}
+		pr_debug("write buf: phy addr 0x%08x kernel addr 0x%08x\n",
+			 (u32) *phy_addr, (u32) *kernel_vaddr);
+		return 0;
+	} else {
+		pr_err("%s: could not allocte in kernel pmem buffers\n",
+		       __func__);
+		return -ENOMEM;
+	}
+
+}
+
+int vcd_pmem_free(u8 *kernel_vaddr, u8 *phy_addr)
+{
+	iounmap((void *)kernel_vaddr);
+	pmem_kfree((s32) phy_addr);
+
+	return 0;
+}
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_util.h b/drivers/video/msm/vidc/common/vcd/vcd_util.h
new file mode 100644
index 0000000..7164029
--- /dev/null
+++ b/drivers/video/msm/vidc/common/vcd/vcd_util.h
@@ -0,0 +1,52 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef _VCD_UTIL_H_
+#define _VCD_UTIL_H_
+#include <media/msm/vidc_type.h>
+#include <media/msm/vcd_api.h>
+
+#if DEBUG
+
+#define VCD_MSG_LOW(xx_fmt...)		printk(KERN_INFO "\n\t* " xx_fmt)
+#define VCD_MSG_MED(xx_fmt...)		printk(KERN_INFO "\n  * " xx_fmt)
+#define VCD_MSG_HIGH(xx_fmt...)		printk(KERN_WARNING "\n" xx_fmt)
+
+#else
+
+#define VCD_MSG_LOW(xx_fmt...)
+#define VCD_MSG_MED(xx_fmt...)
+#define VCD_MSG_HIGH(xx_fmt...)
+
+#endif
+
+#define VCD_MSG_ERROR(xx_fmt...)	printk(KERN_ERR "\n err: " xx_fmt)
+#define VCD_MSG_FATAL(xx_fmt...)	printk(KERN_ERR "\n<FATAL> " xx_fmt)
+
+#define VCD_FAILED_RETURN(rc, xx_fmt...)		\
+	do {						\
+		if (VCD_FAILED(rc)) {			\
+			printk(KERN_ERR  xx_fmt);	\
+			return rc;			\
+		}					\
+	} while	(0)
+
+#define VCD_FAILED_DEVICE_FATAL(rc) \
+	(rc == VCD_ERR_HW_FATAL ? true : false)
+#define VCD_FAILED_CLIENT_FATAL(rc) \
+	(rc == VCD_ERR_CLIENT_FATAL ? true : false)
+
+#define VCD_FAILED_FATAL(rc)  \
+	((VCD_FAILED_DEVICE_FATAL(rc) || VCD_FAILED_CLIENT_FATAL(rc)) \
+	? true : false)
+
+#endif
diff --git a/drivers/video/vfb.c b/drivers/video/vfb.c
index 501a922..2428997 100644
--- a/drivers/video/vfb.c
+++ b/drivers/video/vfb.c
@@ -35,6 +35,13 @@
 static void *videomemory;
 static u_long videomemorysize = VIDEOMEMSIZE;
 module_param(videomemorysize, ulong, 0);
+static char *mode_option __devinitdata;
+static int bpp __devinitdata = 8;
+
+module_param(mode_option, charp, 0);
+MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
+module_param(bpp, int, 0);
+
 
 /**********************************************************************
  *
@@ -469,6 +476,14 @@
 		/* Test disable for backwards compatibility */
 		if (!strcmp(this_opt, "disable"))
 			vfb_enable = 0;
+		else if (!strncmp(this_opt, "bpp=", 4)) {
+			if (kstrtoint(this_opt + 4, 0, &bpp) < 0)
+				bpp = 8;
+		} else if (!strncmp(this_opt, "memsize=", 8)) {
+			if (kstrtoul(this_opt + 8, 0, &videomemorysize) < 0)
+				videomemorysize = VIDEOMEMSIZE;
+		} else
+			mode_option = this_opt;
 	}
 	return 1;
 }
@@ -504,8 +519,8 @@
 	info->screen_base = (char __iomem *)videomemory;
 	info->fbops = &vfb_ops;
 
-	retval = fb_find_mode(&info->var, info, NULL,
-			      NULL, 0, NULL, 8);
+	retval = fb_find_mode(&info->var, info, mode_option,
+			      NULL, 0, NULL, bpp);
 
 	if (!retval || (retval == 4))
 		info->var = vfb_default;